当前位置:   article > 正文

探索MediaPipe检测人脸关键点

mediapipe

MediaPipe是Google开源的计算机视觉处理框架,基于TensorFlow来训练模型,支持人脸识别、人脸关键点、目标检测追踪、图像分类、人像分割、手势识别、文本分类、语音分类等。我们可以使用CPU来推理,也可以选择GPU加速推理。在滤镜特效场景,经常需要用到人脸关键点。

 

目录

一、配置参数与模型

1、配置参数

2、检测模型 

二、工程配置

三、初始化工作

1、初始化模型

2、初始化Camera

四、检测实时流

1、检测人脸关键点

2、绘制人脸关键点

五、检测结果​​​​​​​

一、配置参数与模型

1、配置参数

检测人脸关键点的配置参数有运行模式、人脸数、最小的检测人脸置信度、最小的显示人脸置信度、最小的追踪人脸置信度、结果回调,具体如下表所示:

选项描述取值范围默认值
running_mode

IMAGE: 单个图像 

VIDEO: 视频帧

LIVE_STREAM: 实时流

{IMAGE,VIDEO,

LIVE_STREAM}

IMAGE
num_faces最多检测的人脸数大于01

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人脸模型)Booleanfalse

output_facial_transformation

_matrixes

是否输出变换矩阵(用于滤镜特效)Booleanfalse
result_callback异步回调结果(LIVE_STREAM模式)ResultListener   /

2、检测模型 

检测人脸关键点分为三步:首先检测人脸,然后定位关键点,最后识别面部特征。使用到的模型如下:

  • 人脸检测模型:根据人脸关键点特征来检测人脸;
  • 人脸网格模型:包含478个坐标点的3D人脸标识;
  • 混合形状模型:预测52个混合形状的分数,表示不同表情的系数; 

二、工程配置

以Android平台为例,在gradle导入MediaPipe相关包:

implementation 'com.google.mediapipe:tasks-vision:0.10.0'

然后运行下载模型的task,并且指定模型保存路径:

  1. project.ext.ASSET_DIR = projectDir.toString() + '/src/main/assets'
  2. apply from: 'download_tasks.gradle'

这里用到的模型是face_landmarker,设置src和dest:

  1. task downloadTaskFile(type: Download) {
  2. src 'https://storage.googleapis.com/mediapipe-models/face_landmarker/face_landmarker/float16/1/face_landmarker.task'
  3. dest project.ext.ASSET_DIR + '/face_landmarker.task'
  4. overwrite false
  5. }
  6. preBuild.dependsOn downloadTaskFile

三、初始化工作

1、初始化模型

模型的初始化包括:设置运行模式、模型路径、检测人脸数、回调结果等,示例代码如下:

  1. fun setupFaceLandmark() {
  2. val baseOptionBuilder = BaseOptions.builder()
  3. // 设置运行模式,默认CPU
  4. when (currentDelegate) {
  5. DELEGATE_CPU -> {
  6. baseOptionBuilder.setDelegate(Delegate.CPU)
  7. }
  8. DELEGATE_GPU -> {
  9. baseOptionBuilder.setDelegate(Delegate.GPU)
  10. }
  11. }
  12. // 设置模型路径
  13. baseOptionBuilder.setModelAssetPath(MP_FACE_LANDMARKER_TASK)
  14. try {
  15. val baseOptions = baseOptionBuilder.build()
  16. // 设置检测的人脸数、最小的检测人脸置信度
  17. val optionsBuilder =
  18. FaceLandmarker.FaceLandmarkerOptions.builder()
  19. .setBaseOptions(baseOptions)
  20. .setMinFaceDetectionConfidence(minFaceDetectionConfidence)
  21. .setMinTrackingConfidence(minFaceTrackingConfidence)
  22. .setMinFacePresenceConfidence(minFacePresenceConfidence)
  23. .setNumFaces(maxNumFaces)
  24. .setRunningMode(runningMode)
  25. // LIVE_STREAM模式:设置回调结果
  26. if (runningMode == RunningMode.LIVE_STREAM) {
  27. optionsBuilder
  28. .setResultListener(this::returnLivestreamResult)
  29. .setErrorListener(this::returnLivestreamError)
  30. }
  31. val options = optionsBuilder.build()
  32. faceLandmarker =
  33. FaceLandmarker.createFromOptions(context, options)
  34. } catch (e: IllegalStateException) {
  35. faceLandmarkerHelperListener?.onError(
  36. "Face Landmark failed to initialize, error: " + e.message)
  37. } catch (e: RuntimeException) {
  38. faceLandmarkerHelperListener?.onError(
  39. "Face Landmark failed to initialize. See error logs for details", GPU_ERROR)
  40. }
  41. }

2、初始化Camera

这里以LIVE_STREAM模式为例,Camera的初始化包括:设置像素格式、预览宽高比、绑定生命周期、关联SurfaceProvider。示例代码如下:

  1. private fun bindCameraUseCases() {
  2. val cameraProvider = cameraProvider ?: throw IllegalStateException("Camera init failed.")
  3. val cameraSelector =
  4. CameraSelector.Builder().requireLensFacing(cameraFacing).build()
  5. // 预览的宽高比为4:3
  6. preview = Preview.Builder().setTargetAspectRatio(AspectRatio.RATIO_4_3)
  7. .setTargetRotation(fragmentCameraBinding.viewFinder.display.rotation)
  8. .build()
  9. // 设置像素格式为RGBA_8888,预览的旋转角度
  10. imageAnalyzer =
  11. ImageAnalysis.Builder().setTargetAspectRatio(AspectRatio.RATIO_4_3)
  12. .setTargetRotation(fragmentCameraBinding.viewFinder.display.rotation)
  13. .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
  14. .setOutputImageFormat(ImageAnalysis.OUTPUT_IMAGE_FORMAT_RGBA_8888)
  15. .build()
  16. .also {
  17. it.setAnalyzer(backgroundExecutor) { image ->
  18. // 执行检测人脸关键点
  19. faceLandmarkerHelper.detectLiveStream(image, cameraFacing == CameraSelector.LENS_FACING_FRONT)
  20. }
  21. }
  22. // 绑定之前,先解除绑定
  23. cameraProvider.unbindAll()
  24. try {
  25. // 绑定Lifecycle
  26. camera = cameraProvider.bindToLifecycle(
  27. this, cameraSelector, preview, imageAnalyzer)
  28. // 关联SurfaceProvider
  29. preview?.setSurfaceProvider(fragmentCameraBinding.viewFinder.surfaceProvider)
  30. } catch (exc: Exception) {
  31. Log.e(TAG, "bind lifecycle failed", exc)
  32. }
  33. }

四、检测实时流

1、检测人脸关键点

在检测之前,先拷贝数据,图像帧预处理,然后执行检测:

  1. fun detectLiveStream(
  2. imageProxy: ImageProxy,
  3. isFrontCamera: Boolean) {
  4. val frameTime = SystemClock.uptimeMillis()
  5. // 拷贝RGB数据到缓冲区
  6. val bitmapBuffer =
  7. Bitmap.createBitmap(
  8. imageProxy.width,
  9. imageProxy.height,
  10. Bitmap.Config.ARGB_8888
  11. )
  12. imageProxy.use { bitmapBuffer.copyPixelsFromBuffer(imageProxy.planes[0].buffer) }
  13. imageProxy.close()
  14. val matrix = Matrix().apply {
  15. // 图像旋转
  16. postRotate(imageProxy.imageInfo.rotationDegrees.toFloat())
  17. // 如果是前置摄像头,需要左右镜像
  18. if (isFrontCamera) {
  19. postScale(-1f, 1f, imageProxy.width.toFloat(), imageProxy.height.toFloat())
  20. }
  21. }
  22. val rotatedBitmap = Bitmap.createBitmap(
  23. bitmapBuffer, 0, 0, bitmapBuffer.width, bitmapBuffer.height,
  24. matrix, true)
  25. // 转换Bitmap为MPImage
  26. val mpImage = BitmapImageBuilder(rotatedBitmap).build()
  27. // 异步检测人脸关键点
  28. faceLandmarker?.detectAsync(mpImage, frameTime)
  29. }

2、绘制人脸关键点

检测到人脸关键点结果后,然后回调到主线程:

  1. override fun onResults(resultBundle: FaceLandmarkerHelper.ResultBundle) {
  2. activity?.runOnUiThread {
  3. if (_fragmentCameraBinding != null) {
  4. // 显示推理时长
  5. fragmentCameraBinding.bottomSheetLayout.inferenceTimeVal.text =
  6. String.format("%d ms", resultBundle.inferenceTime)
  7. // 传递结果给OverlayView
  8. fragmentCameraBinding.overlay.setResults(
  9. resultBundle.result,
  10. resultBundle.inputImageHeight,
  11. resultBundle.inputImageWidth,
  12. RunningMode.LIVE_STREAM
  13. )
  14. // 主动触发渲染
  15. fragmentCameraBinding.overlay.invalidate()
  16. }
  17. }
  18. }

最后绘制人脸关键点,包括面部表情、轮廓:

  1. override fun draw(canvas: Canvas) {
  2. super.draw(canvas)
  3. if(results == null || results!!.faceLandmarks().isEmpty()) {
  4. clear()
  5. return
  6. }
  7. results?.let { faceLandmarkResult ->
  8. // 绘制关键点
  9. for(landmark in faceLandmarkResult.faceLandmarks()) {
  10. for(normalizedLandmark in landmark) {
  11. canvas.drawPoint(normalizedLandmark.x() * imageWidth * scaleFactor,
  12. normalizedLandmark.y() * imageHeight * scaleFactor, pointPaint)
  13. }
  14. }
  15. // 绘制线条
  16. FaceLandmarker.FACE_LANDMARKS_CONNECTORS.forEach {
  17. canvas.drawLine(
  18. faceLandmarkResult.faceLandmarks()[0][it!!.start()].x() * imageWidth * scaleFactor,
  19. faceLandmarkResult.faceLandmarks()[0][it.start()].y() * imageHeight * scaleFactor,
  20. faceLandmarkResult.faceLandmarks()[0][it.end()].x() * imageWidth * scaleFactor,
  21. faceLandmarkResult.faceLandmarks()[0][it.end()].y() * imageHeight * scaleFactor,
  22. linePaint)
  23. }
  24. }
  25. }

五、检测结果

输入数据可以是静态图像、实时视频流、文件视频帧。输出数据有人脸边界框、人脸网格、关键点坐标。其中,人脸关键点包括:脸部轮廓、嘴巴、鼻子、眼睛、眉毛、脸颊等,属于3D的landmark模型。如下图所示:

 在人脸识别、人脸关键点基础上,还支持换脸,变成可爱的卡通效果。眨眼睛、摇头、张嘴这些表情动作,都会有实时的卡通头像变化。如下图所示:

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家小花儿/article/detail/906097
推荐阅读
相关标签
  

闽ICP备14008679号