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

Android ML Kit人脸检测无法获取眨眼、微笑概率的问题求助

解决Google ML Kit人脸检测中眼睛开合/微笑概率为null的问题及活体检测替代方案

一、为什么rightEyeOpenProbability等始终为null?

核心原因是FaceDetector未启用分类特征检测,ML Kit的FaceDetector默认不计算眼睛开合、微笑这类概率值,必须显式配置开启。

1. 检查FaceDetector初始化配置

构建FaceDetectorOptions时,必须开启CLASSIFICATION_MODE_ALL,同时配合合适的性能模式才能正常获取分类特征:

val options = FaceDetectorOptions.Builder()
    .setPerformanceMode(FaceDetectorOptions.PERFORMANCE_MODE_ACCURATE) // 高精度模式确保特征检测精度
    .setClassificationMode(FaceDetectorOptions.CLASSIFICATION_MODE_ALL) // 关键:开启眼睛开合、微笑概率计算
    .setMinFaceSize(0.15f) // 根据业务需求调整最小人脸尺寸
    .build()

val detector = FaceDetection.getClient(options)

如果未设置CLASSIFICATION_MODE_ALL,Face对象的rightEyeOpenProbability、leftEyeOpenProbability、smilingProbability都会返回null。

2. 确认图像输入的旋转角度正确

ML Kit需要准确的图像旋转角度才能正确识别面部特征,从相机获取图像时必须传入对应旋转参数:

// 示例:根据设备方向和相机朝向计算旋转角度
val rotation = calculateCameraRotation(activity, cameraFacing)
val inputImage = InputImage.fromBitmap(bitmap, rotation)

detector.process(inputImage)
    .addOnSuccessListener { faces ->
        // 处理检测到的Face对象
    }

旋转角度错误会导致ML Kit无法正确识别面部结构,进而无法生成分类概率值。

二、活体检测的其他实现方式

如果ML Kit的分类概率不够稳定,或需要更可靠的活体验证,可结合以下方案:

1. 基于帧序列的动作验证

通过连续帧检测眨眼、微笑的状态变化判定活体,比如:

  • 记录连续帧的眼睛开合概率,当概率从高(睁眼,>0.7)降到低(闭眼,<0.3)再回到高,判定为一次有效眨眼
  • 检测微笑概率的从无到有再到无的完整变化,判定为一次有效动作

示例逻辑:

private var lastEyeProb: Float? = null
private var isBlinking = false
private var validBlinkCount = 0

fun checkValidBlink(face: Face) {
    face.rightEyeOpenProbability?.let { currentProb ->
        lastEyeProb?.let { lastProb ->
            when {
                lastProb > 0.7 && currentProb < 0.3 -> isBlinking = true
                isBlinking && currentProb > 0.7 -> {
                    validBlinkCount++
                    isBlinking = false
                    // 检测到有效眨眼,可标记为活体
                }
            }
        }
        lastEyeProb = currentProb
    }
}

2. 结合Face Mesh Detection获取精细眼部特征

使用ML Kit的人脸网格检测(Face Mesh Detection),获取468个面部关键点,通过计算**眼长宽比(EAR)**判断眨眼,比概率值更精准:

// 初始化Face Mesh Detector
val meshOptions = FaceMeshDetectorOptions.Builder()
    .setPerformanceMode(FaceMeshDetectorOptions.PERFORMANCE_MODE_ACCURATE)
    .build()
val meshDetector = FaceMeshDetection.getClient(meshOptions)

// 处理图像
meshDetector.process(inputImage)
    .addOnSuccessListener { faceMeshes ->
        faceMeshes.forEach { mesh ->
            // 提取左右眼关键点索引
            val leftEyePoints = mesh.allPoints.subList(33, 133)
            val rightEyePoints = mesh.allPoints.subList(362, 462)
            // 计算EAR值
            val leftEAR = calculateEAR(leftEyePoints)
            val rightEAR = calculateEAR(rightEyePoints)
            val avgEAR = (leftEAR + rightEAR) / 2
            // EAR阈值通常在0.2-0.3之间,低于阈值判定为闭眼
            if (avgEAR < 0.25) {
                // 检测到闭眼动作
            }
        }
    }

// 计算EAR的辅助函数
private fun calculateEAR(eyePoints: List<FaceMeshPoint>): Float {
    val p1 = eyePoints[1].position
    val p2 = eyePoints[5].position
    val p3 = eyePoints[2].position
    val p4 = eyePoints[4].position
    val p5 = eyePoints[0].position
    val p6 = eyePoints[3].position

    val d1 = sqrt((p2.x - p6.x).pow(2) + (p2.y - p6.y).pow(2))
    val d2 = sqrt((p3.x - p5.x).pow(2) + (p3.y - p5.y).pow(2))
    val d3 = sqrt((p1.x - p4.x).pow(2) + (p1.y - p4.y).pow(2))

    return (d1 + d2) / (2.0f * d3)
}

3. 结合ONNX模型优化活体验证

将ML Kit的动作检测(眨眼、微笑)与你的ONNX模型分数结合,比如要求用户完成一次有效眨眼,同时ONNX返回的活体分数达到设定阈值,才判定为活体,提升验证安全性。

内容的提问来源于stack exchange,提问作者Jaimin Modi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 09:07:56