android 两个人脸对比 mlkit

一、先厘清核心概念

1.ML Kit 人脸比对的本质 :不是直接对比两张人脸照片,而是先提取每张人脸的人脸特征向量(128 维浮点数组),再通过计算两个特征向量的欧氏距离来判断是否为同一人(距离越小,相似度越高,通常阈值≤0.8 可判定为同一人)。
2.所需依赖 :ML Kit 提供了「人脸检测」和「人脸特征提取」两个相关 API,我们需要用到「人脸特征提取(Face Recognition)」,它支持在线(依赖 Google 服务,精度更高)和离线(本地模型,无网络依赖)两种模式,优先推荐离线模式(适配更多设备)。
3.前置条件:

最低 Android 版本:API 21(Android 5.0)

项目集成 AndroidX(主流项目已默认支持)

人脸照片要求:正面清晰、光线充足、无大面积遮挡(口罩、墨镜等)

二、实操步骤(分步落地)

步骤 1:集成 ML Kit 依赖

在项目的 build.gradle 中添加依赖(Module 级别的 build.gradle,通常是 app/build.gradle):

java 复制代码
<!-- 读取相册权限 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<!-- 相机权限(若需拍摄人脸) -->
<uses-permission android:name="android.permission.CAMERA" />
<!-- 声明应用使用相机功能(可选) -->
<uses-feature android:name="android.hardware.camera" android:required="false" />

同步 Gradle,确保依赖下载完成。
步骤 2:配置权限(访问照片 / 相机,可选)

如果需要从相册选择照片或相机拍摄获取人脸,在 AndroidManifest.xml 中添加权限:

java 复制代码
<!-- 读取相册权限 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<!-- 相机权限(若需拍摄人脸) -->
<uses-permission android:name="android.permission.CAMERA" />
<!-- 声明应用使用相机功能(可选) -->
<uses-feature android:name="android.hardware.camera" android:required="false" />

注意:Android 6.0(API 23)及以上需要动态申请权限,这里暂不展开,重点聚焦人脸比对核心逻辑。
步骤 3:核心代码实现(人脸特征提取 + 相似度计算)

完整代码包含 3 个核心方法:「提取人脸特征向量」、「计算欧氏距离」、「判断是否为同一人」,可直接复制到 Activity 或工具类中。

java 复制代码
import android.graphics.Bitmap
import com.google.mlkit.vision.common.InputImage
import com.google.mlkit.vision.face.Face
import com.google.mlkit.vision.face.FaceDetection
import com.google.mlkit.vision.face.FaceDetectorOptions
import com.google.mlkit.vision.face.recognition.FaceRecognition
import com.google.mlkit.vision.face.recognition.FaceRecognitionModel
import com.google.mlkit.vision.face.recognition.FaceRecognizer
import kotlin.math.sqrt

class FaceCompareManager {
    // 1. 初始化人脸识别器(离线模式,优先推荐)
    private val faceRecognizer: FaceRecognizer by lazy {
        FaceRecognition.getClient(
            FaceRecognitionModel.FACE_RECOGNITION_MODEL_V1 // 离线模型版本
        )
    }

    // 2. 初始化人脸检测器(筛选有效人脸,避免非人脸图片干扰)
    private val faceDetector by lazy {
        val options = FaceDetectorOptions.Builder()
            .setPerformanceMode(FaceDetectorOptions.PERFORMANCE_MODE_ACCURATE) // 高精度模式
            .setLandmarkMode(FaceDetectorOptions.LANDMARK_MODE_NONE)
            .setContourMode(FaceDetectorOptions.CONTOUR_MODE_NONE)
            .build()
        FaceDetection.getClient(options)
    }

    // 核心方法 1:提取人脸特征向量(返回 128 维浮点数组,提取失败返回 null)
    suspend fun extractFaceFeature(bitmap: Bitmap): FloatArray? {
        return try {
            // 将 Bitmap 转换为 ML Kit 可处理的 InputImage
            val inputImage = InputImage.fromBitmap(bitmap, 0)
            
            // 第一步:先检测图片中是否有人脸
            val faces = faceDetector.process(inputImage).await()
            if (faces.isEmpty() || faces.size > 1) {
                // 无人脸或多个人脸,返回 null
                return null
            }
            val face = faces[0]
            
            // 第二步:提取该人脸的特征向量
            val faceResult = faceRecognizer.process(inputImage, face).await()
            faceResult.faceEmbedding // 返回 128 维特征向量
        } catch (e: Exception) {
            e.printStackTrace()
            null
        }
    }

    // 核心方法 2:计算两个特征向量的欧氏距离(距离越小,相似度越高)
    fun calculateEuclideanDistance(feature1: FloatArray, feature2: FloatArray): Double {
        require(feature1.size == 128 && feature2.size == 128) { "人脸特征向量必须为 128 维" }
        var sum = 0.0
        for (i in 0 until 128) {
            val diff = feature1[i] - feature2[i]
            sum += diff * diff
        }
        return sqrt(sum)
    }

    // 核心方法 3:判断是否为同一人(阈值 ≤ 0.8 判定为同一人,可根据实际场景调整)
    fun isSamePerson(distance: Double): Boolean {
        return distance <= 0.8
    }

    // 释放资源(在 Activity 销毁时调用)
    fun release() {
        faceRecognizer.close()
        faceDetector.close()
    }
}

步骤 4:调用示例(在 Activity 中使用)

java 复制代码
 import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.graphics.BitmapFactory
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext

class MainActivity : AppCompatActivity() {
    private lateinit var faceCompareManager: FaceCompareManager

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        
        faceCompareManager = FaceCompareManager()
        
        // 模拟:从本地资源读取两张人脸照片进行比对
        GlobalScope.launch(Dispatchers.Main) {
            val bitmap1 = BitmapFactory.decodeResource(resources, R.drawable.face1)
            val bitmap2 = BitmapFactory.decodeResource(resources, R.drawable.face2)
            
            val result = compareTwoFaces(bitmap1, bitmap2)
            if (result.first) {
                // 同一人
                println("人脸比对成功:是同一人,欧氏距离:${result.second}")
            } else {
                // 非同一人
                println("人脸比对失败:非同一人,欧氏距离:${result.second}")
            }
        }
    }

    // 封装比对逻辑
    private suspend fun compareTwoFaces(bitmap1: Bitmap, bitmap2: Bitmap): Pair<Boolean, Double> {
        return withContext(Dispatchers.IO) {
            // 提取两张人脸的特征向量
            val feature1 = faceCompareManager.extractFaceFeature(bitmap1)
            val feature2 = faceCompareManager.extractFaceFeature(bitmap2)
            
            if (feature1 == null || feature2 == null) {
                // 特征提取失败(无人脸、多人脸或异常)
                Pair(false, Double.MAX_VALUE)
            } else {
                // 计算欧氏距离
                val distance = faceCompareManager.calculateEuclideanDistance(feature1, feature2)
                // 判断是否为同一人
                Pair(faceCompareManager.isSamePerson(distance), distance)
            }
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        // 释放资源
        faceCompareManager.release()
    }
}

三、关键说明与注意事项

**协程使用:**上述代码使用了 Kotlin 协程(await())处理 ML Kit 的异步任务,避免主线程阻塞,若你使用 Java,可通过 addOnSuccessListener 和 addOnFailureListener 处理回调。

**阈值调整:**默认阈值 0.8 是通用参考值,实际项目中可根据测试数据调整(如对精度要求高可设为 0.6,对容错率要求高可设为 1.0)。
图片预处理:

建议将图片缩放到合适尺寸(如 640×480),减少计算耗时。

确保人脸正面朝向,光线均匀,避免侧脸、逆光、大面积遮挡,否则会导致特征提取失败或相似度判断偏差。
性能优化:

离线模型首次使用时会下载本地模型(约几十 MB),建议在 Wi-Fi 环境下完成。

人脸特征提取是耗时操作,必须在子线程执行,不可在主线程调用。

错误处理:需捕获 Exception(如模型下载失败、图片格式不支持、人脸检测失败等),提升应用健壮性。

四、总结

Android ML Kit 人脸比对的核心是「提取 128 维特征向量 + 计算欧氏距离」,而非直接比对图片。

落地核心步骤:集成离线依赖 → 初始化识别器 / 检测器 → 提取特征 → 计算距离 → 阈值判断。

关键优化点:图片预处理、子线程执行、合理调整阈值、及时释放资源,同时注意权限申请。

相关推荐
千里马学框架4 天前
一起学 Android 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
美狐美颜SDK开放平台4 天前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
AFinalStone4 天前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
致远ccc4 天前
Google Play 上架前如何测试 App?多国家 Android 环境测试
android·app测试·googleplay·多国家应用测试
ttyyttemo4 天前
Kotlin 协程中的 Job 结构化并发与取消
android
sun0077004 天前
tbox 4g/5g切换,导致wan ip 改变,导致车机旧网络不可用。需要重启车机才行
android
其实防守也摸鱼4 天前
内网穿透与反向代理:原理、工具与实战指南
android·大数据·运维·安全·网络安全·自动化·渗透
AFinalStone4 天前
Android7 SystemUI 源码解析(四)NavigationBar 导航栏与 SystemBars
android·systemui
JMchen4 天前
属性动画原理与高级动画实现
android·kotlin·canvas
AFinalStone4 天前
Android7 SystemUI 源码解析(二)启动流程深度解析
android·systemui