# Arc Flutter 插件使用指南 ## 简介 Arc 是一个封装虹软 (ArcSoft) Face SDK 的 Flutter 插件,提供 Android 平台的人脸检测、人脸识别、活体检测功能。本插件支持 1:1 人脸比对和 1:N 人脸搜索两种模式。 ## 安装 ### 1. 添加依赖 在您的 Flutter 项目 `pubspec.yaml` 中添加依赖: ```yaml dependencies: arc: path: ../arc # 本地引用,或使用 git/path 方式 ``` ### 2. Android 配置 在 `android/app/build.gradle` 中确保最小 SDK 版本: ```gradle android { defaultConfig { minSdkVersion 21 // 虹软 SDK 要求最低 Android 5.0 } } ``` ### 3. 获取虹软 SDK 密钥 访问 [虹软开放平台](https://ai.arcsoft.com.cn/) 注册账号,创建应用获取: - `appId`: 应用 ID - `sdkKey`: SDK 密钥 - `activeKey`: 激活密钥 ## 快速开始 ### 基础流程 ``` 激活 SDK → 初始化引擎 → 人脸检测 → 特征提取 → 特征比对/注册 ``` ### 示例代码 ```dart import 'package:arc/arc.dart'; final _arc = Arc(); // 1. 激活 SDK(必须首先执行) await _arc.activeOnline( appId: '您的AppId', sdkKey: '您的SdkKey', activeKey: '您的ActiveKey', ); // 2. 初始化引擎 await _arc.init( detectMode: 0, // 0=视频流模式, 1=图像模式 orient: 0, // 检测角度 maxFaceNum: 1, // 最大检测人脸数 combinedMask: 0x9D, // 功能组合掩码 ); // 3. 人脸检测(需要摄像头 NV21 数据) final result = await _arc.detectFaces( data: nv21Data, width: width, height: height, ); // 4. 提取特征 final featureResult = await _arc.extractFaceFeature( data: nv21Data, width: width, height: height, rectLeft: faceInfo['rectLeft'], rectTop: faceInfo['rectTop'], rectRight: faceInfo['rectRight'], rectBottom: faceInfo['rectBottom'], faceData: faceInfo['faceData'], // 重要!必须传递 ); // 5. 比对特征 final compareResult = await _arc.compareFaceFeature( featureData1: feature1, featureData2: feature2, ); ``` ## API 详细说明 ### activeOnline - SDK 激活 在线激活虹软 SDK,必须在使用其他功能前完成。 ```dart Future?> activeOnline({ required String appId, // 虹软应用 ID required String sdkKey, // SDK 密钥 required String activeKey, // 激活密钥 }) ``` **返回值:** | 字段 | 类型 | 说明 | |------|------|------| | `success` | bool | 是否成功 | | `errorCode` | int | 错误码(0=成功) | | `message` | String | 结果描述 | --- ### init - 初始化引擎 初始化人脸识别引擎,配置检测模式和功能。 ```dart Future?> init({ int? detectMode, // 检测模式 int? orient, // 检测角度 int? maxFaceNum, // 最大人脸数 int? combinedMask, // 功能掩码组合 }) ``` **参数说明:** | 参数 | 值 | 说明 | |------|------|------| | detectMode | 0 | VIDEO 模式(视频流,适合实时检测) | | detectMode | 1 | IMAGE 模式(单张图片) | | orient | 0/90/180/270/360 | 检测角度优先级 | | maxFaceNum | 1-50 | 同时检测的最大人脸数量 | **功能掩码组合:** | 功能 | 值 | 说明 | |------|------|------| | ASF_FACE_DETECT | 0x00000001 | 人脸检测 | | ASF_FACE_RECOGNITION | 0x00000004 | 人脸识别 | | ASF_AGE | 0x00000008 | 年龄检测 | | ASF_GENDER | 0x00000010 | 性别检测 | | ASF_LIVENESS | 0x00000080 | RGB 活体检测 | **推荐组合:** ```dart combinedMask: 0x9D // 人脸检测 + 识别 + 年龄 + 性别 + 活体 // 计算方式: 0x01 | 0x04 | 0x08 | 0x10 | 0x80 = 0x9D ``` --- ### detectFaces - 人脸检测 检测图像中的人脸,同时进行 RGB 活体检测。 ```dart Future?> detectFaces({ required Uint8List data, // NV21 图像数据 required int width, // 图像宽度 required int height, // 图像高度 int format = 2050, // 图像格式(默认 NV21) }) ``` **返回值:** | 字段 | 类型 | 说明 | |------|------|------| | `success` | bool | 是否检测成功 | | `errorCode` | int | 错误码 | | `faceList` | List | 检测到的人脸列表 | | `rgbLiveness` | int | RGB 活体结果(-1=未知, 0=非真人, 1=真人) | | `isRgbAlive` | bool | 是否真人 | **faceList 每项包含:** | 字段 | 类型 | 说明 | |------|------|------| | `rectLeft` | int | 人脸框左边界 | | `rectTop` | int | 人脸框上边界 | | `rectRight` | int | 人脸框右边界 | | `rectBottom` | int | 人脸框下边界 | | `faceOrientation` | int | 人脸角度 | | `faceId` | int | 人脸 ID | | `faceData` | Uint8List | **重要!特征提取必需的数据** | --- ### extractFaceFeature - 特征提取 从检测到的人脸中提取 512 字节的特征数据。 ```dart Future?> extractFaceFeature({ required Uint8List data, // NV21 图像数据 required int width, // 图像宽度 required int height, // 图像高度 required int rectLeft, // 人脸框左边界(从 detectFaces 获取) required int rectTop, // 人脸框上边界 required int rectRight, // 人脸框右边界 required int rectBottom, // 人脸框下边界 int format = 2050, // 图像格式 int faceOrientation = 0, // 人脸角度(从 detectFaces 获取) int faceId = -1, // 人脸 ID Uint8List? faceData, // **关键:人脸数据(从 detectFaces 获取)** int extractType = 1, // 0=注册模式, 1=识别模式 int mask = 0, // 口罩状态 }) ``` **参数说明:** | 参数 | 值 | 说明 | |------|------|------| | extractType | 0 | REGISTER 模式(用于注册到人脸库) | | extractType | 1 | RECOGNIZE 模式(用于比对验证) | | mask | 0 | 未佩戴口罩 | | mask | 1 | 已佩戴口罩 | **返回值:** | 字段 | 类型 | 说明 | |------|------|------| | `success` | bool | 是否成功 | | `errorCode` | int | 错误码 | | `featureData` | Uint8List | 512 字节特征数据 | --- ### compareFaceFeature - 特征比对 (1:1) 比对两个人脸特征的相似度。 ```dart Future?> compareFaceFeature({ required Uint8List featureData1, // 第一个特征 required Uint8List featureData2, // 第二个特征 int compareModel = 0, // 比对模型 }) ``` **compareModel 参数:** | 值 | 说明 | 推荐阈值 | |------|------|------| | 0 | 生活照模型 | 0.8 | | 1 | 证件照模型 | 0.82 | **返回值:** | 字段 | 类型 | 说明 | |------|------|------| | `success` | bool | 是否成功 | | `similarity` | double | 相似度(0-1) | --- ### registerFaceFeature - 注册到人脸库 (1:N) 将人脸特征注册到本地人脸库,用于后续 1:N 搜索。 ```dart Future?> registerFaceFeature({ required int searchId, // 唯一标识符(建议使用用户 ID) required Uint8List featureData, // 特征数据 String? faceTag, // 附属信息(可选) }) ``` --- ### registerFaceFeatureBatch - 批量注册 批量注册多张人脸特征。 ```dart Future?> registerFaceFeatureBatch({ required List> faceList, }) // 示例: await _arc.registerFaceFeatureBatch( faceList: [ {'searchId': 1, 'featureData': feature1, 'faceTag': '张三'}, {'searchId': 2, 'featureData': feature2, 'faceTag': '李四'}, ], ); ``` ## 图像格式要求 ### NV21 格式 - 虹软 SDK 使用 NV21 格式(Android 摄像头默认格式) - 格式代码:`2050` - 宽度必须是 **4 的倍数** - 高度必须是 **2 的倍数** ### 从 Camera 提取 NV21 使用 `camera` 插件获取 NV21 数据: ```dart // 配置摄像头使用 NV21 格式 _cameraController = CameraController( camera, ResolutionPreset.medium, enableAudio: false, imageFormatGroup: ImageFormatGroup.nv21, // 关键配置 ); // 处理图像流 void _onImageAvailable(CameraImage image) { final nv21Data = _extractNV21(image, image.width, image.height); // 人脸检测 final result = await _arc.detectFaces( data: nv21Data, width: image.width, height: image.height, ); } // NV21 数据提取函数 Uint8List _extractNV21(CameraImage image, int width, int height) { final ySize = width * height; final nv21Size = ySize * 3 ~/ 2; // ... 根据 planes 结构提取数据 // 参考 example/lib/main.dart 中的完整实现 } ``` ## 推荐阈值 | 功能 | 阈值 | 说明 | |------|------|------| | 人脸比对(生活照) | 0.8 | 相似度 >= 0.8 认为匹配 | | 人脸比对(证件照) | 0.82 | 相似度 >= 0.82 认为匹配 | | RGB 活体检测 | 0.5 | 活体分数 >= 0.5 认为真人 | | IR 活体检测 | 0.5 | 活体分数 >= 0.5 认为真人 | ## 完整使用流程示例 ### 1:1 人脸验证 ```dart // 1. 激活 SDK final activeResult = await _arc.activeOnline( appId: appId, sdkKey: sdkKey, activeKey: activeKey, ); if (activeResult?['success'] != true) { print('激活失败'); return; } // 2. 初始化引擎 final initResult = await _arc.init( detectMode: 0, orient: 0, maxFaceNum: 1, combinedMask: 0x9D, ); // 3. 摄像头获取图像 // 使用 camera 插件获取 NV21 数据... // 4. 人脸检测 final detectResult = await _arc.detectFaces( data: nv21Data, width: width, height: height, ); if (detectResult?['success'] == true) { final faceList = detectResult!['faceList'] as List; if (faceList.isNotEmpty) { final faceInfo = faceList[0] as Map; final rgbLiveness = detectResult['rgbLiveness'] as int; // 检查活体 if (rgbLiveness != 1) { print('非真人'); return; } // 5. 提取特征 final featureResult = await _arc.extractFaceFeature( data: nv21Data, width: width, height: height, rectLeft: faceInfo['rectLeft'], rectTop: faceInfo['rectTop'], rectRight: faceInfo['rectRight'], rectBottom: faceInfo['rectBottom'], faceData: faceInfo['faceData'], // 重要! extractType: 1, // 识别模式 ); if (featureResult?['success'] == true) { final currentFeature = featureResult!['featureData'] as Uint8List; // 6. 比对(与已存储的特征) final compareResult = await _arc.compareFaceFeature( featureData1: currentFeature, featureData2: storedFeature, compareModel: 0, ); final similarity = compareResult?['similarity'] as double ?? 0.0; if (similarity >= 0.8) { print('验证通过,相似度: ${similarity}'); } else { print('验证失败,相似度: ${similarity}'); } } } } ``` ### 1:N 人脸搜索 ```dart // 注册阶段 // 提取特征(注册模式) final registerFeature = await _arc.extractFaceFeature( data: nv21Data, width: width, height: height, rectLeft: faceInfo['rectLeft'], rectTop: faceInfo['rectTop'], rectRight: faceInfo['rectRight'], rectBottom: faceInfo['rectBottom'], faceData: faceInfo['faceData'], extractType: 0, // 注册模式 ); // 注册到人脸库 await _arc.registerFaceFeature( searchId: userId, // 如: 10001 featureData: registerFeature!['featureData'], faceTag: '张三', ); // 搜索阶段(SDK 会自动搜索匹配) // 使用 extractType=1 提取特征后,SDK 会自动进行 1:N 搜索 ``` ## 错误处理 所有 API 返回统一的错误格式: ```dart { 'success': false, 'errorCode': 81929, // 具体错误码 'message': '特征提取失败', } ``` 常见错误码: - `0`: 成功 - `81929`: 特征提取失败(通常是因为未传递 faceData) - `其他`: 参考 `android/src/.../FaceErrorCode.java` 中的 586 个错误码定义 ## 注意事项 1. **必须先激活再初始化**:使用顺序为 `activeOnline` → `init` → 其他功能 2. **faceData 必须传递**:`extractFaceFeature` 的 `faceData` 参数是从 `detectFaces` 获取的关键数据,不传递会导致特征提取失败 3. **图像尺寸限制**:宽度必须为 4 的倍数,高度必须为 2 的倍数 4. **单设备激活**:虹软 SDK 激活与设备绑定,同一 AppId 在不同设备上需要重新激活 5. **特征存储**:特征数据为 512 字节,建议使用 Base64 编码后存储 ## 依赖插件推荐 实现完整人脸识别功能通常需要配合: ```yaml dependencies: arc: ^0.0.1 # 人脸识别 camera: ^0.11.0 # 摄像头访问 shared_preferences: ^2.0.0 # 特征存储 ``` ## 参考资源 - [虹软开放平台](https://ai.arcsoft.com.cn/) - [虹软人脸识别 SDK 文档](https://ai.arcsoft.com.cn/manual/) - 本插件示例代码:`example/lib/main.dart` - 虹软接口文档:`docs/虹软人脸识别接口文档.md`