一、先厘清核心概念
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 维特征向量 + 计算欧氏距离」,而非直接比对图片。
落地核心步骤:集成离线依赖 → 初始化识别器 / 检测器 → 提取特征 → 计算距离 → 阈值判断。
关键优化点:图片预处理、子线程执行、合理调整阈值、及时释放资源,同时注意权限申请。