赞
踩
MediaPipe是Google开源的计算机视觉处理框架,基于TensorFlow来训练模型,支持人脸识别、人脸关键点、目标检测追踪、图像分类、人像分割、手势识别、文本分类、语音分类等。我们可以使用CPU来推理,也可以选择GPU加速推理。在滤镜特效场景,经常需要用到人脸关键点。
目录
五、检测结果
检测人脸关键点的配置参数有运行模式、人脸数、最小的检测人脸置信度、最小的显示人脸置信度、最小的追踪人脸置信度、结果回调,具体如下表所示:
选项 | 描述 | 取值范围 | 默认值 |
running_mode | IMAGE: 单个图像 VIDEO: 视频帧 LIVE_STREAM: 实时流 | {IMAGE,VIDEO, LIVE_STREAM} | IMAGE |
num_faces | 最多检测的人脸数 | 大于0 | 1 |
min_face_detection _confidence | 人脸检测最小置信度 | [0.0, 1.0] | 0.5 |
min_face_presence _confidence | 人脸显示最小置信度 | [0.0, 1.0] | 0.5 |
min_tracking_confidence | 人脸追踪最小置信度 | [0.0, 1.0] | 0.5 |
output_face_blendshapes | 是否输出混合形状(用于3D人脸模型) | Boolean | false |
output_facial_transformation _matrixes | 是否输出变换矩阵(用于滤镜特效) | Boolean | false |
result_callback | 异步回调结果(LIVE_STREAM模式) | ResultListener | / |
检测人脸关键点分为三步:首先检测人脸,然后定位关键点,最后识别面部特征。使用到的模型如下:
以Android平台为例,在gradle导入MediaPipe相关包:
implementation 'com.google.mediapipe:tasks-vision:0.10.0'
然后运行下载模型的task,并且指定模型保存路径:
- project.ext.ASSET_DIR = projectDir.toString() + '/src/main/assets'
- apply from: 'download_tasks.gradle'
这里用到的模型是face_landmarker,设置src和dest:
- task downloadTaskFile(type: Download) {
- src 'https://storage.googleapis.com/mediapipe-models/face_landmarker/face_landmarker/float16/1/face_landmarker.task'
- dest project.ext.ASSET_DIR + '/face_landmarker.task'
- overwrite false
- }
-
- preBuild.dependsOn downloadTaskFile
模型的初始化包括:设置运行模式、模型路径、检测人脸数、回调结果等,示例代码如下:
- fun setupFaceLandmark() {
- val baseOptionBuilder = BaseOptions.builder()
-
- // 设置运行模式,默认CPU
- when (currentDelegate) {
- DELEGATE_CPU -> {
- baseOptionBuilder.setDelegate(Delegate.CPU)
- }
- DELEGATE_GPU -> {
- baseOptionBuilder.setDelegate(Delegate.GPU)
- }
- }
- // 设置模型路径
- baseOptionBuilder.setModelAssetPath(MP_FACE_LANDMARKER_TASK)
-
- try {
- val baseOptions = baseOptionBuilder.build()
- // 设置检测的人脸数、最小的检测人脸置信度
- val optionsBuilder =
- FaceLandmarker.FaceLandmarkerOptions.builder()
- .setBaseOptions(baseOptions)
- .setMinFaceDetectionConfidence(minFaceDetectionConfidence)
- .setMinTrackingConfidence(minFaceTrackingConfidence)
- .setMinFacePresenceConfidence(minFacePresenceConfidence)
- .setNumFaces(maxNumFaces)
- .setRunningMode(runningMode)
-
- // LIVE_STREAM模式:设置回调结果
- if (runningMode == RunningMode.LIVE_STREAM) {
- optionsBuilder
- .setResultListener(this::returnLivestreamResult)
- .setErrorListener(this::returnLivestreamError)
- }
-
- val options = optionsBuilder.build()
- faceLandmarker =
- FaceLandmarker.createFromOptions(context, options)
- } catch (e: IllegalStateException) {
- faceLandmarkerHelperListener?.onError(
- "Face Landmark failed to initialize, error: " + e.message)
- } catch (e: RuntimeException) {
- faceLandmarkerHelperListener?.onError(
- "Face Landmark failed to initialize. See error logs for details", GPU_ERROR)
- }
- }
这里以LIVE_STREAM模式为例,Camera的初始化包括:设置像素格式、预览宽高比、绑定生命周期、关联SurfaceProvider。示例代码如下:
- private fun bindCameraUseCases() {
- val cameraProvider = cameraProvider ?: throw IllegalStateException("Camera init failed.")
-
- val cameraSelector =
- CameraSelector.Builder().requireLensFacing(cameraFacing).build()
-
- // 预览的宽高比为4:3
- preview = Preview.Builder().setTargetAspectRatio(AspectRatio.RATIO_4_3)
- .setTargetRotation(fragmentCameraBinding.viewFinder.display.rotation)
- .build()
-
- // 设置像素格式为RGBA_8888,预览的旋转角度
- imageAnalyzer =
- ImageAnalysis.Builder().setTargetAspectRatio(AspectRatio.RATIO_4_3)
- .setTargetRotation(fragmentCameraBinding.viewFinder.display.rotation)
- .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
- .setOutputImageFormat(ImageAnalysis.OUTPUT_IMAGE_FORMAT_RGBA_8888)
- .build()
- .also {
- it.setAnalyzer(backgroundExecutor) { image ->
- // 执行检测人脸关键点
- faceLandmarkerHelper.detectLiveStream(image, cameraFacing == CameraSelector.LENS_FACING_FRONT)
- }
- }
-
- // 绑定之前,先解除绑定
- cameraProvider.unbindAll()
-
- try {
- // 绑定Lifecycle
- camera = cameraProvider.bindToLifecycle(
- this, cameraSelector, preview, imageAnalyzer)
- // 关联SurfaceProvider
- preview?.setSurfaceProvider(fragmentCameraBinding.viewFinder.surfaceProvider)
- } catch (exc: Exception) {
- Log.e(TAG, "bind lifecycle failed", exc)
- }
- }
在检测之前,先拷贝数据,图像帧预处理,然后执行检测:
- fun detectLiveStream(
- imageProxy: ImageProxy,
- isFrontCamera: Boolean) {
- val frameTime = SystemClock.uptimeMillis()
-
- // 拷贝RGB数据到缓冲区
- val bitmapBuffer =
- Bitmap.createBitmap(
- imageProxy.width,
- imageProxy.height,
- Bitmap.Config.ARGB_8888
- )
- imageProxy.use { bitmapBuffer.copyPixelsFromBuffer(imageProxy.planes[0].buffer) }
- imageProxy.close()
-
- val matrix = Matrix().apply {
- // 图像旋转
- postRotate(imageProxy.imageInfo.rotationDegrees.toFloat())
-
- // 如果是前置摄像头,需要左右镜像
- if (isFrontCamera) {
- postScale(-1f, 1f, imageProxy.width.toFloat(), imageProxy.height.toFloat())
- }
- }
- val rotatedBitmap = Bitmap.createBitmap(
- bitmapBuffer, 0, 0, bitmapBuffer.width, bitmapBuffer.height,
- matrix, true)
-
- // 转换Bitmap为MPImage
- val mpImage = BitmapImageBuilder(rotatedBitmap).build()
- // 异步检测人脸关键点
- faceLandmarker?.detectAsync(mpImage, frameTime)
- }
检测到人脸关键点结果后,然后回调到主线程:
- override fun onResults(resultBundle: FaceLandmarkerHelper.ResultBundle) {
- activity?.runOnUiThread {
- if (_fragmentCameraBinding != null) {
- // 显示推理时长
- fragmentCameraBinding.bottomSheetLayout.inferenceTimeVal.text =
- String.format("%d ms", resultBundle.inferenceTime)
-
- // 传递结果给OverlayView
- fragmentCameraBinding.overlay.setResults(
- resultBundle.result,
- resultBundle.inputImageHeight,
- resultBundle.inputImageWidth,
- RunningMode.LIVE_STREAM
- )
-
- // 主动触发渲染
- fragmentCameraBinding.overlay.invalidate()
- }
- }
- }
最后绘制人脸关键点,包括面部表情、轮廓:
- override fun draw(canvas: Canvas) {
- super.draw(canvas)
- if(results == null || results!!.faceLandmarks().isEmpty()) {
- clear()
- return
- }
-
- results?.let { faceLandmarkResult ->
- // 绘制关键点
- for(landmark in faceLandmarkResult.faceLandmarks()) {
- for(normalizedLandmark in landmark) {
- canvas.drawPoint(normalizedLandmark.x() * imageWidth * scaleFactor,
- normalizedLandmark.y() * imageHeight * scaleFactor, pointPaint)
- }
- }
- // 绘制线条
- FaceLandmarker.FACE_LANDMARKS_CONNECTORS.forEach {
- canvas.drawLine(
- faceLandmarkResult.faceLandmarks()[0][it!!.start()].x() * imageWidth * scaleFactor,
- faceLandmarkResult.faceLandmarks()[0][it.start()].y() * imageHeight * scaleFactor,
- faceLandmarkResult.faceLandmarks()[0][it.end()].x() * imageWidth * scaleFactor,
- faceLandmarkResult.faceLandmarks()[0][it.end()].y() * imageHeight * scaleFactor,
- linePaint)
- }
- }
- }
输入数据可以是静态图像、实时视频流、文件视频帧。输出数据有人脸边界框、人脸网格、关键点坐标。其中,人脸关键点包括:脸部轮廓、嘴巴、鼻子、眼睛、眉毛、脸颊等,属于3D的landmark模型。如下图所示:
在人脸识别、人脸关键点基础上,还支持换脸,变成可爱的卡通效果。眨眼睛、摇头、张嘴这些表情动作,都会有实时的卡通头像变化。如下图所示:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。