474 lines
15 KiB
Dart
474 lines
15 KiB
Dart
import 'package:json_annotation/json_annotation.dart';
|
|
|
|
part 'face_discern_models.g.dart';
|
|
|
|
@JsonSerializable()
|
|
class FaceStream {
|
|
// 相似度
|
|
double similar;
|
|
|
|
// 识别时间
|
|
int cost;
|
|
|
|
// 人脸id
|
|
int? faceId;
|
|
|
|
// 人脸样本id
|
|
int? sampleId;
|
|
|
|
// 样本id
|
|
String? userId;
|
|
|
|
// 用户名
|
|
String? userName;
|
|
|
|
FaceStream({
|
|
required this.similar,
|
|
required this.cost,
|
|
this.faceId,
|
|
this.sampleId,
|
|
this.userId,
|
|
this.userName,
|
|
});
|
|
|
|
factory FaceStream.fromJson(Map<String, dynamic> json) =>
|
|
_$FaceStreamFromJson(json);
|
|
|
|
Map<String, dynamic> toJson() => _$FaceStreamToJson(this);
|
|
}
|
|
|
|
@JsonSerializable()
|
|
class FaceConfig {
|
|
String? appId;
|
|
|
|
String? sdkKey;
|
|
|
|
String? activeKey;
|
|
|
|
// 是否激活
|
|
bool isActive = false;
|
|
|
|
FaceConfig({
|
|
this.appId,
|
|
this.sdkKey,
|
|
this.activeKey,
|
|
this.isActive = false,
|
|
});
|
|
|
|
factory FaceConfig.fromJson(Map<String, dynamic> json) =>
|
|
_$FaceConfigFromJson(json);
|
|
|
|
Map<String, dynamic> toJson() => _$FaceConfigToJson(this);
|
|
}
|
|
|
|
/// 终端本机的人脸识别策略,对应 Android `RecognizeConfiguration` 的可调白名单。
|
|
class FaceRecognitionConfiguration {
|
|
const FaceRecognitionConfiguration({
|
|
required this.enableLiveness,
|
|
required this.similarThreshold,
|
|
required this.rgbLivenessThreshold,
|
|
required this.enableImageQuality,
|
|
required this.imageQualityNoMaskRecognizeThreshold,
|
|
required this.imageQualityMaskRecognizeThreshold,
|
|
required this.keepMaxFace,
|
|
required this.maxDetectFaces,
|
|
required this.extractRetryCount,
|
|
required this.livenessRetryCount,
|
|
required this.recognizeFailedRetryInterval,
|
|
required this.livenessFailedRetryInterval,
|
|
required this.livenessType,
|
|
});
|
|
|
|
const FaceRecognitionConfiguration.defaults()
|
|
: enableLiveness = false,
|
|
similarThreshold = 0.8,
|
|
rgbLivenessThreshold = 0.5,
|
|
enableImageQuality = true,
|
|
imageQualityNoMaskRecognizeThreshold = 0.49,
|
|
imageQualityMaskRecognizeThreshold = 0.29,
|
|
keepMaxFace = true,
|
|
maxDetectFaces = 1,
|
|
extractRetryCount = 3,
|
|
livenessRetryCount = 3,
|
|
recognizeFailedRetryInterval = 0,
|
|
livenessFailedRetryInterval = 0,
|
|
livenessType = 'RGB';
|
|
|
|
final bool enableLiveness;
|
|
final double similarThreshold;
|
|
final double rgbLivenessThreshold;
|
|
final bool enableImageQuality;
|
|
final double imageQualityNoMaskRecognizeThreshold;
|
|
final double imageQualityMaskRecognizeThreshold;
|
|
final bool keepMaxFace;
|
|
final int maxDetectFaces;
|
|
final int extractRetryCount;
|
|
final int livenessRetryCount;
|
|
final int recognizeFailedRetryInterval;
|
|
final int livenessFailedRetryInterval;
|
|
final String livenessType;
|
|
|
|
factory FaceRecognitionConfiguration.fromMap(Map<dynamic, dynamic> map) {
|
|
const defaults = FaceRecognitionConfiguration.defaults();
|
|
return FaceRecognitionConfiguration(
|
|
enableLiveness: _asBoolOr(map['enableLiveness'], defaults.enableLiveness),
|
|
similarThreshold:
|
|
_asDouble(map['similarThreshold'], defaults.similarThreshold),
|
|
rgbLivenessThreshold: _asDouble(
|
|
map['rgbLivenessThreshold'],
|
|
defaults.rgbLivenessThreshold,
|
|
),
|
|
enableImageQuality:
|
|
_asBoolOr(map['enableImageQuality'], defaults.enableImageQuality),
|
|
imageQualityNoMaskRecognizeThreshold: _asDouble(
|
|
map['imageQualityNoMaskRecognizeThreshold'],
|
|
defaults.imageQualityNoMaskRecognizeThreshold,
|
|
),
|
|
imageQualityMaskRecognizeThreshold: _asDouble(
|
|
map['imageQualityMaskRecognizeThreshold'],
|
|
defaults.imageQualityMaskRecognizeThreshold,
|
|
),
|
|
keepMaxFace: _asBoolOr(map['keepMaxFace'], defaults.keepMaxFace),
|
|
maxDetectFaces: _asIntOr(map['maxDetectFaces'], defaults.maxDetectFaces),
|
|
extractRetryCount:
|
|
_asIntOr(map['extractRetryCount'], defaults.extractRetryCount),
|
|
livenessRetryCount:
|
|
_asIntOr(map['livenessRetryCount'], defaults.livenessRetryCount),
|
|
recognizeFailedRetryInterval: _asIntOr(
|
|
map['recognizeFailedRetryInterval'],
|
|
defaults.recognizeFailedRetryInterval,
|
|
),
|
|
livenessFailedRetryInterval: _asIntOr(
|
|
map['livenessFailedRetryInterval'],
|
|
defaults.livenessFailedRetryInterval,
|
|
),
|
|
livenessType: map['livenessType']?.toString() ?? defaults.livenessType,
|
|
);
|
|
}
|
|
|
|
Map<String, dynamic> toMap() => {
|
|
'enableLiveness': enableLiveness,
|
|
'similarThreshold': similarThreshold,
|
|
'rgbLivenessThreshold': rgbLivenessThreshold,
|
|
'enableImageQuality': enableImageQuality,
|
|
'imageQualityNoMaskRecognizeThreshold':
|
|
imageQualityNoMaskRecognizeThreshold,
|
|
'imageQualityMaskRecognizeThreshold':
|
|
imageQualityMaskRecognizeThreshold,
|
|
'keepMaxFace': keepMaxFace,
|
|
'maxDetectFaces': maxDetectFaces,
|
|
'extractRetryCount': extractRetryCount,
|
|
'livenessRetryCount': livenessRetryCount,
|
|
'recognizeFailedRetryInterval': recognizeFailedRetryInterval,
|
|
'livenessFailedRetryInterval': livenessFailedRetryInterval,
|
|
};
|
|
|
|
bool get usesDefaults =>
|
|
this == const FaceRecognitionConfiguration.defaults();
|
|
|
|
FaceRecognitionConfiguration copyWith({
|
|
bool? enableLiveness,
|
|
double? similarThreshold,
|
|
double? rgbLivenessThreshold,
|
|
bool? enableImageQuality,
|
|
double? imageQualityNoMaskRecognizeThreshold,
|
|
double? imageQualityMaskRecognizeThreshold,
|
|
bool? keepMaxFace,
|
|
int? maxDetectFaces,
|
|
int? extractRetryCount,
|
|
int? livenessRetryCount,
|
|
int? recognizeFailedRetryInterval,
|
|
int? livenessFailedRetryInterval,
|
|
}) {
|
|
return FaceRecognitionConfiguration(
|
|
enableLiveness: enableLiveness ?? this.enableLiveness,
|
|
similarThreshold: similarThreshold ?? this.similarThreshold,
|
|
rgbLivenessThreshold: rgbLivenessThreshold ?? this.rgbLivenessThreshold,
|
|
enableImageQuality: enableImageQuality ?? this.enableImageQuality,
|
|
imageQualityNoMaskRecognizeThreshold:
|
|
imageQualityNoMaskRecognizeThreshold ??
|
|
this.imageQualityNoMaskRecognizeThreshold,
|
|
imageQualityMaskRecognizeThreshold: imageQualityMaskRecognizeThreshold ??
|
|
this.imageQualityMaskRecognizeThreshold,
|
|
keepMaxFace: keepMaxFace ?? this.keepMaxFace,
|
|
maxDetectFaces: maxDetectFaces ?? this.maxDetectFaces,
|
|
extractRetryCount: extractRetryCount ?? this.extractRetryCount,
|
|
livenessRetryCount: livenessRetryCount ?? this.livenessRetryCount,
|
|
recognizeFailedRetryInterval:
|
|
recognizeFailedRetryInterval ?? this.recognizeFailedRetryInterval,
|
|
livenessFailedRetryInterval:
|
|
livenessFailedRetryInterval ?? this.livenessFailedRetryInterval,
|
|
livenessType: livenessType,
|
|
);
|
|
}
|
|
|
|
@override
|
|
bool operator ==(Object other) =>
|
|
identical(this, other) ||
|
|
other is FaceRecognitionConfiguration &&
|
|
enableLiveness == other.enableLiveness &&
|
|
similarThreshold == other.similarThreshold &&
|
|
rgbLivenessThreshold == other.rgbLivenessThreshold &&
|
|
enableImageQuality == other.enableImageQuality &&
|
|
imageQualityNoMaskRecognizeThreshold ==
|
|
other.imageQualityNoMaskRecognizeThreshold &&
|
|
imageQualityMaskRecognizeThreshold ==
|
|
other.imageQualityMaskRecognizeThreshold &&
|
|
keepMaxFace == other.keepMaxFace &&
|
|
maxDetectFaces == other.maxDetectFaces &&
|
|
extractRetryCount == other.extractRetryCount &&
|
|
livenessRetryCount == other.livenessRetryCount &&
|
|
recognizeFailedRetryInterval == other.recognizeFailedRetryInterval &&
|
|
livenessFailedRetryInterval == other.livenessFailedRetryInterval &&
|
|
livenessType == other.livenessType;
|
|
|
|
@override
|
|
int get hashCode => Object.hash(
|
|
enableLiveness,
|
|
similarThreshold,
|
|
rgbLivenessThreshold,
|
|
enableImageQuality,
|
|
imageQualityNoMaskRecognizeThreshold,
|
|
imageQualityMaskRecognizeThreshold,
|
|
keepMaxFace,
|
|
maxDetectFaces,
|
|
extractRetryCount,
|
|
livenessRetryCount,
|
|
recognizeFailedRetryInterval,
|
|
livenessFailedRetryInterval,
|
|
livenessType,
|
|
);
|
|
}
|
|
|
|
@JsonSerializable()
|
|
class RegisterAllStorageFaceRes {
|
|
// 注册成功数量
|
|
int successCount;
|
|
|
|
// 注册失败数量
|
|
int failedCount;
|
|
|
|
// 注册数量
|
|
int registerCount;
|
|
|
|
// 本地文件数量
|
|
int fileCount;
|
|
|
|
RegisterAllStorageFaceRes({
|
|
required this.successCount,
|
|
required this.failedCount,
|
|
required this.registerCount,
|
|
required this.fileCount,
|
|
});
|
|
|
|
factory RegisterAllStorageFaceRes.fromJson(Map<String, dynamic> json) =>
|
|
_$RegisterAllStorageFaceResFromJson(json);
|
|
|
|
Map<String, dynamic> toJson() => _$RegisterAllStorageFaceResToJson(this);
|
|
}
|
|
|
|
@JsonSerializable()
|
|
class FaceRes {
|
|
int searchId;
|
|
|
|
List<int> featureData;
|
|
|
|
String faceTag;
|
|
|
|
FaceRes({
|
|
required this.searchId,
|
|
required this.featureData,
|
|
required this.faceTag,
|
|
});
|
|
|
|
factory FaceRes.fromJson(Map<String, dynamic> json) =>
|
|
_$FaceResFromJson(json);
|
|
|
|
Map<String, dynamic> toJson() => _$FaceResToJson(this);
|
|
}
|
|
|
|
/// 原生人脸插件、授权文件和内存人脸库的实时状态。
|
|
class FacePluginStatus {
|
|
const FacePluginStatus({
|
|
required this.activated,
|
|
required this.activationCode,
|
|
required this.parametersComplete,
|
|
required this.engineReady,
|
|
required this.engineCode,
|
|
required this.databaseCount,
|
|
required this.memoryCount,
|
|
required this.registrationInProgress,
|
|
required this.libraryConsistent,
|
|
this.activationStartTime,
|
|
this.activationEndTime,
|
|
this.registrationProgress,
|
|
this.lastRegistrationResult,
|
|
});
|
|
|
|
final bool activated;
|
|
final int activationCode;
|
|
final bool parametersComplete;
|
|
final bool engineReady;
|
|
final int engineCode;
|
|
final int databaseCount;
|
|
final int memoryCount;
|
|
final bool registrationInProgress;
|
|
final bool libraryConsistent;
|
|
final String? activationStartTime;
|
|
final String? activationEndTime;
|
|
final FaceRegistrationProgress? registrationProgress;
|
|
final FaceRegistrationResult? lastRegistrationResult;
|
|
|
|
bool get ready =>
|
|
activated && engineReady && !registrationInProgress && libraryConsistent;
|
|
|
|
factory FacePluginStatus.fromMap(Map<dynamic, dynamic> map) {
|
|
return FacePluginStatus(
|
|
activated: _asBool(map['activated']),
|
|
activationCode: _asInt(map['activationCode']),
|
|
parametersComplete: _asBool(map['parametersComplete']),
|
|
engineReady: _asBool(map['engineReady']),
|
|
engineCode: _asInt(map['engineCode']),
|
|
databaseCount: _asInt(map['databaseCount']),
|
|
memoryCount: _asInt(map['memoryCount']),
|
|
registrationInProgress: _asBool(map['registrationInProgress']),
|
|
libraryConsistent: _asBool(map['libraryConsistent']),
|
|
activationStartTime: _asNullableString(map['activationStartTime']),
|
|
activationEndTime: _asNullableString(map['activationEndTime']),
|
|
registrationProgress: map['registrationProgress'] is Map
|
|
? FaceRegistrationProgress.fromMap(
|
|
map['registrationProgress'] as Map,
|
|
)
|
|
: null,
|
|
lastRegistrationResult: map['lastRegistrationResult'] is Map
|
|
? FaceRegistrationResult.fromMap(
|
|
map['lastRegistrationResult'] as Map,
|
|
)
|
|
: null,
|
|
);
|
|
}
|
|
}
|
|
|
|
class FaceActivationResult {
|
|
const FaceActivationResult({
|
|
required this.success,
|
|
required this.code,
|
|
required this.message,
|
|
required this.requiresPermission,
|
|
required this.canOpenSettings,
|
|
});
|
|
|
|
final bool success;
|
|
final int code;
|
|
final String message;
|
|
final bool requiresPermission;
|
|
final bool canOpenSettings;
|
|
|
|
factory FaceActivationResult.fromMap(Map<dynamic, dynamic> map) {
|
|
return FaceActivationResult(
|
|
success: _asBool(map['success']),
|
|
code: _asInt(map['code']),
|
|
message: _asString(map['message'], '人脸插件激活失败'),
|
|
requiresPermission: _asBool(map['requiresPermission']),
|
|
canOpenSettings: _asBool(map['canOpenSettings']),
|
|
);
|
|
}
|
|
}
|
|
|
|
class FaceRegistrationProgress {
|
|
const FaceRegistrationProgress({
|
|
required this.phase,
|
|
required this.message,
|
|
required this.processed,
|
|
required this.total,
|
|
required this.successCount,
|
|
required this.failedCount,
|
|
required this.inProgress,
|
|
this.currentSampleId,
|
|
});
|
|
|
|
final String phase;
|
|
final String message;
|
|
final int processed;
|
|
final int total;
|
|
final int successCount;
|
|
final int failedCount;
|
|
final bool inProgress;
|
|
final int? currentSampleId;
|
|
|
|
double get fraction => total <= 0 ? 0 : (processed / total).clamp(0, 1);
|
|
|
|
factory FaceRegistrationProgress.fromMap(Map<dynamic, dynamic> map) {
|
|
return FaceRegistrationProgress(
|
|
phase: _asString(map['phase'], 'idle'),
|
|
message: _asString(map['message'], '尚未执行人脸库注册'),
|
|
processed: _asInt(map['processed']),
|
|
total: _asInt(map['total']),
|
|
successCount: _asInt(map['successCount']),
|
|
failedCount: _asInt(map['failedCount']),
|
|
inProgress: _asBool(map['inProgress']),
|
|
currentSampleId: map['currentSampleId'] == null
|
|
? null
|
|
: _asInt(map['currentSampleId']),
|
|
);
|
|
}
|
|
}
|
|
|
|
class FaceRegistrationResult {
|
|
const FaceRegistrationResult({
|
|
required this.success,
|
|
required this.message,
|
|
required this.total,
|
|
required this.successCount,
|
|
required this.failedCount,
|
|
required this.failedSampleIds,
|
|
required this.durationMs,
|
|
});
|
|
|
|
final bool success;
|
|
final String message;
|
|
final int total;
|
|
final int successCount;
|
|
final int failedCount;
|
|
final List<int> failedSampleIds;
|
|
final int durationMs;
|
|
|
|
factory FaceRegistrationResult.fromMap(Map<dynamic, dynamic> map) {
|
|
final failedSampleIds = map['failedSampleIds'];
|
|
return FaceRegistrationResult(
|
|
success: _asBool(map['success']),
|
|
message: _asString(map['message'], '人脸库注册失败'),
|
|
total: _asInt(map['total']),
|
|
successCount: _asInt(map['successCount']),
|
|
failedCount: _asInt(map['failedCount']),
|
|
failedSampleIds: failedSampleIds is List
|
|
? failedSampleIds.map(_asInt).toList(growable: false)
|
|
: const [],
|
|
durationMs: _asInt(map['durationMs']),
|
|
);
|
|
}
|
|
}
|
|
|
|
bool _asBool(dynamic value) => value == true;
|
|
|
|
int _asInt(dynamic value) => value is num ? value.toInt() : 0;
|
|
|
|
int _asIntOr(dynamic value, int fallback) =>
|
|
value is num ? value.toInt() : fallback;
|
|
|
|
double _asDouble(dynamic value, double fallback) =>
|
|
value is num ? value.toDouble() : fallback;
|
|
|
|
bool _asBoolOr(dynamic value, bool fallback) =>
|
|
value is bool ? value : fallback;
|
|
|
|
String _asString(dynamic value, String fallback) {
|
|
final result = value?.toString();
|
|
return result == null || result.isEmpty ? fallback : result;
|
|
}
|
|
|
|
String? _asNullableString(dynamic value) {
|
|
final result = value?.toString();
|
|
return result == null || result.isEmpty ? null : result;
|
|
}
|