基于Camera X与ML Kit的Android人脸检测与追踪实现
基于CameraX与ML Kit实现人脸检测及快门控制方案
需求概述
需实现以下核心功能:
- 依托CameraX完成图像/视频捕获
- 通过ML Kit实现实时人脸检测
- 使用自定义Overlay展示带圆形取景框的遮罩层
- 仅当人脸完全处于取景框范围内时,启用快门按钮
现有实现基础
已完成CameraX的图像与视频捕获功能,并编写了自定义OverlayView代码用于显示半透明遮罩及圆形取景框。
自定义OverlayView代码
class OverlayPosition(var x: Float, var y: Float, var r: Float) class OverlayView @JvmOverloads constructor( context: Context?, attrs: AttributeSet? = null, defStyleAttr: Int = 0 ) : View(context, attrs, defStyleAttr) { private val paint: Paint = Paint() private var holePaint: Paint = Paint() private var bitmap: Bitmap? = null private var layer: Canvas? = null private var border: Paint = Paint() // 取景框位置参数 var holePosition: OverlayPosition = OverlayPosition(0.0f, 0.0f, 0.0f) set(value) { field = value // 触发视图重绘 this.invalidate() } override fun onDraw(canvas: Canvas) { super.onDraw(canvas) if (bitmap == null) { configureBitmap() } // 绘制半透明背景遮罩 layer?.drawRect(0.0f, 0.0f, width.toFloat(), height.toFloat(), paint) // 绘制取景框白色边框与镂空区域 val centerX = (width / 2).toFloat() val centerY = (height / 4).toFloat() val radius = 400f layer?.drawCircle(centerX, centerY, radius, border) layer?.drawCircle(centerX, centerY, radius, holePaint) // 将绘制好的遮罩层渲染到画布 canvas.drawBitmap(bitmap!!, 0.0f, 0.0f, paint) } private fun configureBitmap() { // 创建用于绘制遮罩的Bitmap与Canvas实例 bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888) layer = Canvas(bitmap!!) } init { // 配置背景遮罩颜色(半透明) val backgroundAlpha = 0.8 paint.color = ColorUtils.setAlphaComponent( ContextCompat.getColor(context!!, R.color.overlay), (255 * backgroundAlpha).toInt() ) // 配置取景框边框样式 border.color = Color.parseColor("#FFFFFF") border.strokeWidth = 30F border.style = Paint.Style.STROKE border.isAntiAlias = true border.isDither = true // 配置取景框镂空区域的绘制模式 holePaint.color = ContextCompat.getColor(context, android.R.color.transparent) holePaint.xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR) } }
集成ML Kit人脸检测与快门控制步骤
1. 添加ML Kit依赖
在Module级别build.gradle中引入人脸检测库:
dependencies { // ML Kit人脸检测依赖 implementation 'com.google.mlkit:face-detection:16.1.5' }
2. 配置人脸检测器
创建高性能的人脸检测器实例,设置基础检测参数:
val faceDetectorOptions = FaceDetectorOptions.Builder() .setPerformanceMode(FaceDetectorOptions.PERFORMANCE_MODE_FAST) .setMinFaceSize(0.15f) // 最小可检测人脸占预览画面比例 .enableTracking() // 启用人脸跟踪,提升连续帧检测效率 .build() val faceDetector = FaceDetection.getClient(faceDetectorOptions)
3. 实现CameraX图像分析用例
通过ImageAnalysis获取相机帧数据,传入ML Kit进行实时检测:
val imageAnalysis = ImageAnalysis.Builder() .setTargetResolution(Size(1280, 720)) .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST) .build() imageAnalysis.setAnalyzer(ExecutorService.newSingleThreadExecutor()) { imageProxy -> val mediaImage = imageProxy.image ?: run { imageProxy.close() return@setAnalyzer } // 将相机帧转换为ML Kit可处理的InputImage val inputImage = InputImage.fromMediaImage(mediaImage, imageProxy.imageInfo.rotationDegrees) // 执行人脸检测 faceDetector.process(inputImage) .addOnSuccessListener { faces -> handleFaceDetectionResult(faces, imageProxy.width, imageProxy.height) imageProxy.close() } .addOnFailureListener { e -> e.printStackTrace() imageProxy.close() } } // 将ImageAnalysis绑定到CameraX生命周期 CameraX.bindToLifecycle(this, imageAnalysis, preview, imageCapture)
4. 判断人脸是否在取景框内
编写检测结果处理逻辑,对比人脸边界框与取景框位置,控制快门按钮状态:
private fun handleFaceDetectionResult(faces: List<Face>, imageWidth: Int, imageHeight: Int) { // 获取取景框在Overlay视图中的坐标范围 val overlayCenterX = overlayView.width / 2f val overlayCenterY = overlayView.height / 4f val overlayRadius = 400f val overlayLeft = overlayCenterX - overlayRadius val overlayTop = overlayCenterY - overlayRadius val overlayRight = overlayCenterX + overlayRadius val overlayBottom = overlayCenterY + overlayRadius var isFaceInFrame = false // 遍历检测到的人脸 for (face in faces) { val faceRect = face.boundingBox // 将相机帧坐标系下的人脸坐标转换为Overlay视图坐标系 val scaleX = overlayView.width.toFloat() / imageWidth.toFloat() val scaleY = overlayView.height.toFloat() / imageHeight.toFloat() val faceLeft = faceRect.left * scaleX val faceTop = faceRect.top * scaleY val faceRight = faceRect.right * scaleX val faceBottom = faceRect.bottom * scaleY // 判断人脸是否完全处于取景框内 if (faceLeft >= overlayLeft && faceTop >= overlayTop && faceRight <= overlayRight && faceBottom <= overlayBottom) { isFaceInFrame = true break } } // 在主线程更新快门按钮状态 runOnUiThread { captureButton.isEnabled = isFaceInFrame } }
5. 关键注意事项
- 坐标系转换:需根据相机预览的缩放比例,将人脸检测结果的坐标转换为Overlay视图坐标系
- 性能优化:使用快速检测模式,避免复杂特征检测,保证预览流畅度
- 线程安全:人脸检测回调在后台线程,更新UI必须切换到主线程
内容的提问来源于stack exchange,提问作者Sharan
相关产品推荐
相关产品推荐

