You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 00:10:16