245 lines
7.7 KiB
Markdown
245 lines
7.7 KiB
Markdown
人脸识别 Android SDK 集成指南 (V1.0)
|
||
本文档为人脸识别 C++ 核心库 (libface_sdk_jni.so) 提供了详尽的 Android 集成步骤和 API 使用说明。
|
||
|
||
1. 概述
|
||
本 SDK 提供了在 Android 设备上本地运行的高性能人脸识别功能。SDK 核心基于 C++ 实现,1:1 转译自 Python 端的 7 模型推理管线,确保了特征向量的完全兼容。
|
||
|
||
SDK 提供的主要 Java API 接口包括:
|
||
|
||
特征提取: extractFeature(Bitmap bitmap)
|
||
|
||
特征比较: compare(float[] feat1, float[] feat2)
|
||
|
||
关键特性
|
||
C++ 核心: 所有计算均在 C++ 层完成,性能高,内存可控。
|
||
|
||
7 模型管线: 完整复现了 Python 端的 7 模型管线(旋转、检测、姿态、关键点、对齐、识别),保证了高识别精度。
|
||
|
||
质量过滤: 内置姿态角(Yaw/Pitch)过滤,自动拒绝低质量人脸。
|
||
|
||
手动捆绑: SDK 手动捆绑了所有必要的依赖库 (ONNX Runtime, OpenCV, C++ STL),确保了在不同设备上的一致性,避免了因 Gradle 依赖版本不匹配导致的崩溃。
|
||
|
||
2. SDK 包内容
|
||
分发的 SDK 包含三个部分:
|
||
|
||
1. Java 接口
|
||
SDK_Wrapper/
|
||
└── com/
|
||
└── facesdk/
|
||
└── wrapper/
|
||
└── FaceSDKWrapper.java (SDK 公开的 Java API)
|
||
2. Native 库 (C++)
|
||
SDK_Libs/
|
||
└── arm64-v8a/
|
||
├── libface_sdk_jni.so (✅ 您的核心 SDK 库)
|
||
├── libonnxruntime.so (依赖: ONNX 1.23.2 完整版)
|
||
├── libopencv_java4.so (依赖: OpenCV 4.12.0)
|
||
└── libc++_shared.so (依赖: C++ 标准库)
|
||
注意: 本 SDK 目前仅支持 arm64-v8a 架构。这覆盖了市面上 99% 以上的现代 Android 设备。
|
||
|
||
3. AI 模型
|
||
SDK_Models/
|
||
├── faceboxesv2-640x640.onnx
|
||
├── face_landmarker_pts5_net1.onnx
|
||
├── face_landmarker_pts5_net2.onnx
|
||
├── face_recognizer.onnx
|
||
├── fsanet-conv.onnx
|
||
├── fsanet-var.onnx
|
||
└── model_gray_mobilenetv2_rotcls.onnx
|
||
3. 集成指南
|
||
请按照以下步骤将 SDK 集成到您的 Android Studio 项目中。
|
||
|
||
步骤 1: 复制 Java 接口
|
||
在 Android Studio 的 "Project" 视图中,导航到 app/src/main/java/。
|
||
|
||
将 FaceSDKWrapper.java 文件复制到您项目的 Java 源码目录中(例如,com.yourcompany.yourapp.sdk/)。
|
||
|
||
重要: 打开 FaceSDKWrapper.java 文件,将其顶部的 package com.facesdk.wrapper; 声明修改为您自己的包名,例如 package com.yourcompany.yourapp.sdk;。
|
||
|
||
步骤 2: 复制 Native 库 (.so)
|
||
在 Android Studio 的 "Project" 视图中,导航到 app/src/main/。
|
||
|
||
右键点击 main -> New -> Directory。
|
||
|
||
创建 jniLibs 文件夹。
|
||
|
||
右键点击 jniLibs -> New -> Directory。
|
||
|
||
创建 arm64-v8a 文件夹。
|
||
|
||
将 SDK_Libs/arm64-v8a/ 目录下的所有 4 个 .so 文件复制到您刚创建的 app/src/main/jniLibs/arm64-v8a/ 目录中。
|
||
|
||
步骤 3: 复制 AI 模型 (.onnx)
|
||
在 Android Studio 的 "Project" 视图中,导航到 app/src/main/。
|
||
|
||
右键点击 main -> New -> Directory。
|
||
|
||
创建 assets 文件夹。
|
||
|
||
将 SDK_Models/ 目录下的所有 7 个 .onnx 文件复制到您刚创建的 app/src/main/assets/ 目录中。
|
||
|
||
步骤 4: 配置 build.gradle.kts (或 build.gradle)
|
||
这是最关键的一步。我们需要告诉 Gradle 在打包 App 时包含我们的手动库。
|
||
|
||
打开您的 app/build.gradle.kts (或 build.gradle) 文件。
|
||
|
||
移除 (或不要添加) onnxruntime 和 opencv 的 implementation 依赖。我们已经手动提供了它们。
|
||
|
||
在 android { ... } 代码块中,添加 sourceSets 和 ndk 配置:
|
||
|
||
如果是 build.gradle.kts (Kotlin 脚本):
|
||
|
||
Kotlin
|
||
```
|
||
android {
|
||
// ... (namespace, compileSdk, 等)
|
||
|
||
defaultConfig {
|
||
// ... (applicationId, minSdk, targetSdk, 等)
|
||
}
|
||
|
||
// (保持这个 sourceSets 块不变,即使它看起来是空的)
|
||
sourceSets {
|
||
getByName("main") {
|
||
// 明确告知 Gradle 我们的 jniLibs 目录在哪里
|
||
jniLibs.srcDirs("src/main/jniLibs")
|
||
}
|
||
}
|
||
|
||
// 强制 Gradle 只打包和使用 arm64-v8a 架构的库
|
||
// 这可以防止在 x86 模拟器上发生库不匹配的崩溃
|
||
ndk {
|
||
abiFilters.add("arm64-v8a")
|
||
}
|
||
|
||
// ... (buildTypes, compileOptions, 等)
|
||
}
|
||
```
|
||
如果是 build.gradle (Groovy 脚本):
|
||
|
||
Groovy
|
||
```
|
||
android {
|
||
// ... (namespace, compileSdk, 等)
|
||
|
||
defaultConfig {
|
||
// ... (applicationId, minSdk, targetSdk, 等)
|
||
}
|
||
|
||
sourceSets {
|
||
main {
|
||
// 明确告知 Gradle 我们的 jniLibs 目录在哪里
|
||
jniLibs.srcDirs = ['src/main/jniLibs']
|
||
}
|
||
}
|
||
|
||
// 强制 Gradle 只打包和使用 arm64-v8a 架构的库
|
||
ndk {
|
||
abiFilters 'arm64-v8a'
|
||
}
|
||
|
||
// ... (buildTypes, compileOptions, 等)
|
||
}
|
||
```
|
||
点击 "Sync Now" 同步您的项目。
|
||
|
||
至此,集成已全部完成!
|
||
|
||
4. API 使用指南 (Java)
|
||
FaceSDKWrapper.java 会自动处理模型复制和 C++ 库加载。
|
||
|
||
1. 初始化 SDK
|
||
您必须在后台线程中初始化 SDK,因为它包含耗时的模型复制操作(仅限首次启动)。
|
||
|
||
Java
|
||
```
|
||
import com.facesdk.wrapper.FaceSDKWrapper; // 确保导入您修改了包名的类
|
||
|
||
public class MyApplication extends Application {
|
||
|
||
private FaceSDKWrapper sdkWrapper;
|
||
private volatile boolean isSdkInitialized = false;
|
||
|
||
@Override
|
||
public void onCreate() {
|
||
super.onCreate();
|
||
|
||
// 在后台线程初始化 SDK
|
||
new Thread(() -> {
|
||
sdkWrapper = new FaceSDKWrapper();
|
||
boolean success = sdkWrapper.init(getApplicationContext());
|
||
if (success) {
|
||
isSdkInitialized = true;
|
||
Log.i("MyApplication", "人脸识别 SDK 初始化成功!");
|
||
} else {
|
||
Log.e("MyApplication", "人脸识别 SDK 初始化失败!");
|
||
}
|
||
}).start();
|
||
}
|
||
|
||
// 提供一个全局获取 SDK 实例的方法
|
||
public FaceSDKWrapper getSdkWrapper() {
|
||
return (isSdkInitialized) ? sdkWrapper : null;
|
||
}
|
||
}
|
||
```
|
||
(您也可以不在 Application 中初始化,而是在 Activity 中,如我们的测试项目所示)
|
||
|
||
2. 提取特征 (必须在后台线程)
|
||
特征提取是一个CPU 密集型操作(运行 7 个模型)。严禁在 UI 主线程上调用它,否则会导致 App 冻结。
|
||
|
||
Java
|
||
```
|
||
// (在您的 Activity 或 ViewModel 中)
|
||
// 假设您已从 Application 中获取了 sdkWrapper 实例
|
||
|
||
public void runExtraction(Bitmap faceBitmap) {
|
||
if (sdkWrapper == null) {
|
||
Log.e("MyActivity", "SDK 尚未初始化。");
|
||
return;
|
||
}
|
||
|
||
// 必须在后台线程中运行
|
||
new Thread(() -> {
|
||
// 1. 调用 C++ 核心库
|
||
final float[] features = sdkWrapper.extractFeature(faceBitmap);
|
||
|
||
// 2. 在 UI 线程上处理结果
|
||
runOnUiThread(() -> {
|
||
if (features != null) {
|
||
Log.i("MyActivity", "特征提取成功!维度: " + features.length);
|
||
// TODO: 使用特征向量 (例如,与数据库比对)
|
||
} else {
|
||
Log.w("MyActivity", "特征提取失败 (未检测到合格人脸或出错)");
|
||
// TODO: 提示用户
|
||
}
|
||
});
|
||
}).start();
|
||
}
|
||
```
|
||
3. 比较特征
|
||
这是一个非常快速的操作,可以在任何线程上调用。
|
||
|
||
Java
|
||
```
|
||
float[] featureA = ... // (来自 extractFeature)
|
||
float[] featureB = ... // (来自数据库)
|
||
|
||
float similarity = sdkWrapper.compare(featureA, featureB);
|
||
|
||
// 相似度是一个 -1.0 到 1.0 之间的浮点数 (余弦相似度)
|
||
Log.i("MyActivity", "人脸相似度: " + similarity);
|
||
```
|
||
4. 释放 SDK
|
||
在您的主 Activity (或 Application) 退出时,调用 release() 来释放 C++ 占用的内存。
|
||
|
||
Java
|
||
```
|
||
// (在您的 MainActivity 中)
|
||
@Override
|
||
protected void onDestroy() {
|
||
super.onDestroy();
|
||
if (sdkWrapper != null) {
|
||
sdkWrapper.release();
|
||
}
|
||
}``` |