fix: 修复特征提取失败(错误码81929)
问题: extractFaceFeature 返回错误码 81929 (MERR_FSDK_FACEFEATURE_FACEDATA) 根本原因: detectFaces 返回的人脸信息缺少 faceData 字段, 而虹软 SDK 的 extractFaceFeature 必须要有这个字段才能提取特征 修复: - FaceEngineManager.convertFaceInfoToList: 添加返回 faceData - ArcPlugin.handleExtractFaceFeature: 接收并传递 faceData 参数 - Dart API: extractFaceFeature 添加 faceData 参数 - example: 传递 faceData 到特征提取调用 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
174
lib/arc.dart
Normal file
174
lib/arc.dart
Normal file
@@ -0,0 +1,174 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'arc_platform_interface.dart';
|
||||
|
||||
class Arc {
|
||||
/// 获取平台版本
|
||||
Future<String?> getPlatformVersion() {
|
||||
return ArcPlatform.instance.getPlatformVersion();
|
||||
}
|
||||
|
||||
/// 激活 SDK(在线激活)
|
||||
/// [appId] 应用 ID(从虹软控制台获取)
|
||||
/// [sdkKey] SDK 密钥(从虹软控制台获取)
|
||||
/// [activeKey] 激活密钥
|
||||
/// 返回包含 success, errorCode, message 的 Map
|
||||
Future<Map<String, dynamic>?> activeOnline({
|
||||
required String appId,
|
||||
required String sdkKey,
|
||||
required String activeKey,
|
||||
}) {
|
||||
return ArcPlatform.instance.activeOnline(
|
||||
appId: appId,
|
||||
sdkKey: sdkKey,
|
||||
activeKey: activeKey,
|
||||
);
|
||||
}
|
||||
|
||||
/// 初始化人脸识别引擎
|
||||
/// [detectMode] 检测模式(0=VIDEO 视频流模式,1=IMAGE 图像模式)
|
||||
/// [orient] 检测角度(0/90/180/270/360)
|
||||
/// [maxFaceNum] 最大可检测人脸数量
|
||||
/// [combinedMask] 功能组合掩码
|
||||
/// 返回包含 success, errorCode, message 的 Map
|
||||
Future<Map<String, dynamic>?> init({
|
||||
int? detectMode,
|
||||
int? orient,
|
||||
int? maxFaceNum,
|
||||
int? combinedMask,
|
||||
}) {
|
||||
return ArcPlatform.instance.init(
|
||||
detectMode: detectMode,
|
||||
orient: orient,
|
||||
maxFaceNum: maxFaceNum,
|
||||
combinedMask: combinedMask,
|
||||
);
|
||||
}
|
||||
|
||||
/// 检测人脸(同时进行 RGB 活体检测)
|
||||
/// [data] NV21 格式的图像数据
|
||||
/// [width] 图像宽度
|
||||
/// [height] 图像高度
|
||||
/// [format] 图像格式(2050=NV21,默认)
|
||||
/// 返回包含 success, errorCode, message, faceList, rgbLiveness, isRgbAlive 的 Map
|
||||
/// rgbLiveness: -1=未知, 0=非真人, 1=真人
|
||||
Future<Map<String, dynamic>?> detectFaces({
|
||||
required Uint8List data,
|
||||
required int width,
|
||||
required int height,
|
||||
int format = 2050,
|
||||
}) {
|
||||
return ArcPlatform.instance.detectFaces(
|
||||
data: data,
|
||||
width: width,
|
||||
height: height,
|
||||
format: format,
|
||||
);
|
||||
}
|
||||
|
||||
/// 提取人脸特征
|
||||
/// [data] NV21 格式的图像数据
|
||||
/// [width] 图像宽度
|
||||
/// [height] 图像高度
|
||||
/// [rectLeft] 人脸框左边界(从detectFaces获取)
|
||||
/// [rectTop] 人脸框上边界(从detectFaces获取)
|
||||
/// [rectRight] 人脸框右边界(从detectFaces获取)
|
||||
/// [rectBottom] 人脸框下边界(从detectFaces获取)
|
||||
/// [format] 图像格式(2050=NV21,默认)
|
||||
/// [faceOrientation] 人脸角度(从detectFaces获取)
|
||||
/// [faceId] 人脸ID(从detectFaces获取)
|
||||
/// [faceData] 人脸数据(从detectFaces获取,必需!这是虹软SDK进行特征提取的关键数据)
|
||||
/// [extractType] 特征提取类型(0=注册, 1=识别,默认为识别)
|
||||
/// [mask] 口罩状态(0=未佩戴, 1=已佩戴,默认为未佩戴)
|
||||
/// 返回包含 success, errorCode, message, featureData 的 Map
|
||||
Future<Map<String, dynamic>?> extractFaceFeature({
|
||||
required Uint8List data,
|
||||
required int width,
|
||||
required int height,
|
||||
required int rectLeft,
|
||||
required int rectTop,
|
||||
required int rectRight,
|
||||
required int rectBottom,
|
||||
int format = 2050,
|
||||
int faceOrientation = 0,
|
||||
int faceId = -1,
|
||||
Uint8List? faceData,
|
||||
int extractType = 1,
|
||||
int mask = 0,
|
||||
}) {
|
||||
return ArcPlatform.instance.extractFaceFeature(
|
||||
data: data,
|
||||
width: width,
|
||||
height: height,
|
||||
rectLeft: rectLeft,
|
||||
rectTop: rectTop,
|
||||
rectRight: rectRight,
|
||||
rectBottom: rectBottom,
|
||||
format: format,
|
||||
faceOrientation: faceOrientation,
|
||||
faceId: faceId,
|
||||
faceData: faceData,
|
||||
extractType: extractType,
|
||||
mask: mask,
|
||||
);
|
||||
}
|
||||
|
||||
/// 比对人脸特征
|
||||
/// [featureData1] 第一个人脸特征数据(从extractFaceFeature获取)
|
||||
/// [featureData2] 第二个人脸特征数据(从extractFaceFeature获取)
|
||||
/// [compareModel] 比对模型(0=生活照, 1=证件照,默认为生活照)
|
||||
/// 返回包含 success, errorCode, message, similarity 的 Map
|
||||
/// similarity: 相似度分数(0-1之间,推荐阈值:生活照0.8,证件照0.82)
|
||||
Future<Map<String, dynamic>?> compareFaceFeature({
|
||||
required Uint8List featureData1,
|
||||
required Uint8List featureData2,
|
||||
int compareModel = 0,
|
||||
}) {
|
||||
return ArcPlatform.instance.compareFaceFeature(
|
||||
featureData1: featureData1,
|
||||
featureData2: featureData2,
|
||||
compareModel: compareModel,
|
||||
);
|
||||
}
|
||||
|
||||
/// 注册单张人脸特征到人脸库(用于1:N搜索)
|
||||
/// [searchId] 唯一标识符(用于后续搜索匹配,建议使用用户ID)
|
||||
/// [featureData] 人脸特征数据(从extractFaceFeature获取,建议使用extractType=0注册模式)
|
||||
/// [faceTag] 附属信息(可选,如用户名、员工ID等)
|
||||
/// 返回包含 success, errorCode, message 的 Map
|
||||
/// 注意:如果底库中已存在相同searchId,SDK会忽略此次注册
|
||||
Future<Map<String, dynamic>?> registerFaceFeature({
|
||||
required int searchId,
|
||||
required Uint8List featureData,
|
||||
String? faceTag,
|
||||
}) {
|
||||
return ArcPlatform.instance.registerFaceFeature(
|
||||
searchId: searchId,
|
||||
featureData: featureData,
|
||||
faceTag: faceTag,
|
||||
);
|
||||
}
|
||||
|
||||
/// 批量注册人脸特征到人脸库(用于1:N搜索)
|
||||
/// [faceList] 人脸列表,每项需包含:
|
||||
/// - searchId: 唯一标识符(int,必填)
|
||||
/// - featureData: 人脸特征数据(Uint8List,必填)
|
||||
/// - faceTag: 附属信息(String,可选)
|
||||
/// 返回包含 success, errorCode, message 的 Map
|
||||
/// 示例:
|
||||
/// ```dart
|
||||
/// await arc.registerFaceFeatureBatch(
|
||||
/// faceList: [
|
||||
/// {'searchId': 1, 'featureData': feature1, 'faceTag': '张三'},
|
||||
/// {'searchId': 2, 'featureData': feature2, 'faceTag': '李四'},
|
||||
/// ],
|
||||
/// );
|
||||
/// ```
|
||||
Future<Map<String, dynamic>?> registerFaceFeatureBatch({
|
||||
required List<Map<String, dynamic>> faceList,
|
||||
}) {
|
||||
return ArcPlatform.instance.registerFaceFeatureBatch(
|
||||
faceList: faceList,
|
||||
);
|
||||
}
|
||||
}
|
||||
135
lib/arc_method_channel.dart
Normal file
135
lib/arc_method_channel.dart
Normal file
@@ -0,0 +1,135 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
import 'arc_platform_interface.dart';
|
||||
|
||||
/// An implementation of [ArcPlatform] that uses method channels.
|
||||
class MethodChannelArc extends ArcPlatform {
|
||||
/// The method channel used to interact with the native platform.
|
||||
@visibleForTesting
|
||||
final methodChannel = const MethodChannel('arc');
|
||||
|
||||
@override
|
||||
Future<String?> getPlatformVersion() async {
|
||||
final version = await methodChannel.invokeMethod<String>('getPlatformVersion');
|
||||
return version;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Map<String, dynamic>?> activeOnline({
|
||||
required String appId,
|
||||
required String sdkKey,
|
||||
required String activeKey,
|
||||
}) async {
|
||||
final result = await methodChannel.invokeMethod<Map<dynamic, dynamic>>('activeOnline', {
|
||||
'appId': appId,
|
||||
'sdkKey': sdkKey,
|
||||
'activeKey': activeKey,
|
||||
});
|
||||
return result?.cast<String, dynamic>();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Map<String, dynamic>?> init({
|
||||
int? detectMode,
|
||||
int? orient,
|
||||
int? maxFaceNum,
|
||||
int? combinedMask,
|
||||
}) async {
|
||||
final result = await methodChannel.invokeMethod<Map<dynamic, dynamic>>('init', {
|
||||
'detectMode': detectMode,
|
||||
'orient': orient,
|
||||
'maxFaceNum': maxFaceNum,
|
||||
'combinedMask': combinedMask,
|
||||
});
|
||||
return result?.cast<String, dynamic>();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Map<String, dynamic>?> detectFaces({
|
||||
required Uint8List data,
|
||||
required int width,
|
||||
required int height,
|
||||
int format = 2050,
|
||||
}) async {
|
||||
final result = await methodChannel.invokeMethod<Map<dynamic, dynamic>>('detectFaces', {
|
||||
'data': data,
|
||||
'width': width,
|
||||
'height': height,
|
||||
'format': format,
|
||||
});
|
||||
return result?.cast<String, dynamic>();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Map<String, dynamic>?> extractFaceFeature({
|
||||
required Uint8List data,
|
||||
required int width,
|
||||
required int height,
|
||||
required int rectLeft,
|
||||
required int rectTop,
|
||||
required int rectRight,
|
||||
required int rectBottom,
|
||||
int format = 2050,
|
||||
int faceOrientation = 0,
|
||||
int faceId = -1,
|
||||
Uint8List? faceData,
|
||||
int extractType = 1,
|
||||
int mask = 0,
|
||||
}) async {
|
||||
final result = await methodChannel.invokeMethod<Map<dynamic, dynamic>>('extractFaceFeature', {
|
||||
'data': data,
|
||||
'width': width,
|
||||
'height': height,
|
||||
'format': format,
|
||||
'rectLeft': rectLeft,
|
||||
'rectTop': rectTop,
|
||||
'rectRight': rectRight,
|
||||
'rectBottom': rectBottom,
|
||||
'faceOrientation': faceOrientation,
|
||||
'faceId': faceId,
|
||||
'faceData': faceData, // 关键:传递 faceData,这是虹软 SDK 特征提取必需的数据
|
||||
'extractType': extractType,
|
||||
'mask': mask,
|
||||
});
|
||||
return result?.cast<String, dynamic>();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Map<String, dynamic>?> compareFaceFeature({
|
||||
required Uint8List featureData1,
|
||||
required Uint8List featureData2,
|
||||
int compareModel = 0,
|
||||
}) async {
|
||||
final result = await methodChannel.invokeMethod<Map<dynamic, dynamic>>('compareFaceFeature', {
|
||||
'featureData1': featureData1,
|
||||
'featureData2': featureData2,
|
||||
'compareModel': compareModel,
|
||||
});
|
||||
return result?.cast<String, dynamic>();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Map<String, dynamic>?> registerFaceFeature({
|
||||
required int searchId,
|
||||
required Uint8List featureData,
|
||||
String? faceTag,
|
||||
}) async {
|
||||
final result = await methodChannel.invokeMethod<Map<dynamic, dynamic>>('registerFaceFeature', {
|
||||
'searchId': searchId,
|
||||
'featureData': featureData,
|
||||
'faceTag': faceTag,
|
||||
});
|
||||
return result?.cast<String, dynamic>();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Map<String, dynamic>?> registerFaceFeatureBatch({
|
||||
required List<Map<String, dynamic>> faceList,
|
||||
}) async {
|
||||
final result = await methodChannel.invokeMethod<Map<dynamic, dynamic>>('registerFaceFeatureBatch', {
|
||||
'faceList': faceList,
|
||||
});
|
||||
return result?.cast<String, dynamic>();
|
||||
}
|
||||
}
|
||||
147
lib/arc_platform_interface.dart
Normal file
147
lib/arc_platform_interface.dart
Normal file
@@ -0,0 +1,147 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
|
||||
|
||||
import 'arc_method_channel.dart';
|
||||
|
||||
abstract class ArcPlatform extends PlatformInterface {
|
||||
/// Constructs a ArcPlatform.
|
||||
ArcPlatform() : super(token: _token);
|
||||
|
||||
static final Object _token = Object();
|
||||
|
||||
static ArcPlatform _instance = MethodChannelArc();
|
||||
|
||||
/// The default instance of [ArcPlatform] to use.
|
||||
///
|
||||
/// Defaults to [MethodChannelArc].
|
||||
static ArcPlatform get instance => _instance;
|
||||
|
||||
/// Platform-specific implementations should set this with their own
|
||||
/// platform-specific class that extends [ArcPlatform] when
|
||||
/// they register themselves.
|
||||
static set instance(ArcPlatform instance) {
|
||||
PlatformInterface.verifyToken(instance, _token);
|
||||
_instance = instance;
|
||||
}
|
||||
|
||||
/// 获取平台版本
|
||||
Future<String?> getPlatformVersion() {
|
||||
throw UnimplementedError('platformVersion() has not been implemented.');
|
||||
}
|
||||
|
||||
/// 激活 SDK(在线激活)
|
||||
/// [appId] 应用 ID(从虹软控制台获取)
|
||||
/// [sdkKey] SDK 密钥(从虹软控制台获取)
|
||||
/// [activeKey] 激活密钥
|
||||
/// 返回包含 success, errorCode, message 的 Map
|
||||
Future<Map<String, dynamic>?> activeOnline({
|
||||
required String appId,
|
||||
required String sdkKey,
|
||||
required String activeKey,
|
||||
}) {
|
||||
throw UnimplementedError('activeOnline() has not been implemented.');
|
||||
}
|
||||
|
||||
/// 初始化人脸识别引擎
|
||||
/// [detectMode] 检测模式(0=VIDEO 视频流模式,1=IMAGE 图像模式)
|
||||
/// [orient] 检测角度(0/90/180/270/360)
|
||||
/// [maxFaceNum] 最大可检测人脸数量
|
||||
/// [combinedMask] 功能组合掩码
|
||||
/// 返回包含 success, errorCode, message 的 Map
|
||||
Future<Map<String, dynamic>?> init({
|
||||
int? detectMode,
|
||||
int? orient,
|
||||
int? maxFaceNum,
|
||||
int? combinedMask,
|
||||
}) {
|
||||
throw UnimplementedError('init() has not been implemented.');
|
||||
}
|
||||
|
||||
/// 检测人脸(同时进行 RGB 活体检测)
|
||||
/// [data] NV21 格式的图像数据
|
||||
/// [width] 图像宽度
|
||||
/// [height] 图像高度
|
||||
/// [format] 图像格式(2050=NV21)
|
||||
/// 返回包含 success, errorCode, message, faceList, rgbLiveness, isRgbAlive 的 Map
|
||||
/// rgbLiveness: -1=未知, 0=非真人, 1=真人
|
||||
Future<Map<String, dynamic>?> detectFaces({
|
||||
required Uint8List data,
|
||||
required int width,
|
||||
required int height,
|
||||
int format = 2050,
|
||||
}) {
|
||||
throw UnimplementedError('detectFaces() has not been implemented.');
|
||||
}
|
||||
|
||||
/// 提取人脸特征
|
||||
/// [data] NV21 格式的图像数据
|
||||
/// [width] 图像宽度
|
||||
/// [height] 图像高度
|
||||
/// [format] 图像格式(2050=NV21)
|
||||
/// [rectLeft] 人脸框左边界
|
||||
/// [rectTop] 人脸框上边界
|
||||
/// [rectRight] 人脸框右边界
|
||||
/// [rectBottom] 人脸框下边界
|
||||
/// [faceOrientation] 人脸角度
|
||||
/// [faceId] 人脸ID(从detectFaces获取)
|
||||
/// [faceData] 人脸数据(从detectFaces获取,必需!这是虹软SDK进行特征提取的关键数据)
|
||||
/// [extractType] 特征提取类型(0=注册, 1=识别)
|
||||
/// [mask] 口罩状态(0=未佩戴, 1=已佩戴)
|
||||
/// 返回包含 success, errorCode, message, featureData 的 Map
|
||||
Future<Map<String, dynamic>?> extractFaceFeature({
|
||||
required Uint8List data,
|
||||
required int width,
|
||||
required int height,
|
||||
required int rectLeft,
|
||||
required int rectTop,
|
||||
required int rectRight,
|
||||
required int rectBottom,
|
||||
int format = 2050,
|
||||
int faceOrientation = 0,
|
||||
int faceId = -1,
|
||||
Uint8List? faceData,
|
||||
int extractType = 1,
|
||||
int mask = 0,
|
||||
}) {
|
||||
throw UnimplementedError('extractFaceFeature() has not been implemented.');
|
||||
}
|
||||
|
||||
/// 比对人脸特征
|
||||
/// [featureData1] 第一个人脸特征数据
|
||||
/// [featureData2] 第二个人脸特征数据
|
||||
/// [compareModel] 比对模型(0=生活照, 1=证件照)
|
||||
/// 返回包含 success, errorCode, message, similarity 的 Map
|
||||
Future<Map<String, dynamic>?> compareFaceFeature({
|
||||
required Uint8List featureData1,
|
||||
required Uint8List featureData2,
|
||||
int compareModel = 0,
|
||||
}) {
|
||||
throw UnimplementedError('compareFaceFeature() has not been implemented.');
|
||||
}
|
||||
|
||||
/// 注册单张人脸特征到人脸库(用于1:N搜索)
|
||||
/// [searchId] 唯一标识符(用于后续搜索匹配,建议使用用户ID)
|
||||
/// [featureData] 人脸特征数据(从extractFaceFeature获取,建议使用extractType=0注册模式)
|
||||
/// [faceTag] 附属信息(可选,如用户名、员工ID等)
|
||||
/// 返回包含 success, errorCode, message 的 Map
|
||||
Future<Map<String, dynamic>?> registerFaceFeature({
|
||||
required int searchId,
|
||||
required Uint8List featureData,
|
||||
String? faceTag,
|
||||
}) {
|
||||
throw UnimplementedError('registerFaceFeature() has not been implemented.');
|
||||
}
|
||||
|
||||
/// 批量注册人脸特征到人脸库(用于1:N搜索)
|
||||
/// [faceList] 人脸列表,每项需包含:
|
||||
/// - searchId: 唯一标识符
|
||||
/// - featureData: 人脸特征数据
|
||||
/// - faceTag: 附属信息(可选)
|
||||
/// 返回包含 success, errorCode, message 的 Map
|
||||
Future<Map<String, dynamic>?> registerFaceFeatureBatch({
|
||||
required List<Map<String, dynamic>> faceList,
|
||||
}) {
|
||||
throw UnimplementedError('registerFaceFeatureBatch() has not been implemented.');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user