feat:人脸机器实时增量更新
This commit is contained in:
1 parent
8cfbbd768e
commit
3770173604
29 files changed
+1584
-359
No files matched your search
+92
@@ -186,6 +186,10 @@ public class FlutterFaceDiscernPlugin implements FlutterPlugin, MethodChannel.Me
|
||||
break;
|
||||
case "startRecognition":
|
||||
try {
|
||||
if (FlutterTool.isRegistrationInProgress()) {
|
||||
result.error("REGISTRATION_IN_PROGRESS", "人脸库更新中,请稍后重试", null);
|
||||
break;
|
||||
}
|
||||
result.success(sessionManager.startRecognition(stringArgument(call, "sessionId"), stringArgument(call, "scene"), this));
|
||||
} catch (FaceRecognitionSessionManager.SessionBusyException e) {
|
||||
result.error("SESSION_BUSY", e.getMessage(), e.getActiveSessionId());
|
||||
@@ -197,6 +201,10 @@ public class FlutterFaceDiscernPlugin implements FlutterPlugin, MethodChannel.Me
|
||||
break;
|
||||
case "retryRecognition":
|
||||
try {
|
||||
if (FlutterTool.isRegistrationInProgress()) {
|
||||
result.error("REGISTRATION_IN_PROGRESS", "人脸库更新中,请稍后重试", null);
|
||||
break;
|
||||
}
|
||||
result.success(sessionManager.retryRecognition(stringArgument(call, "sessionId")));
|
||||
} catch (Exception e) {
|
||||
result.error("RECOGNITION_RETRY_FAILED", e.getMessage(), null);
|
||||
@@ -268,6 +276,12 @@ public class FlutterFaceDiscernPlugin implements FlutterPlugin, MethodChannel.Me
|
||||
case "rebuildFaceLibrary":
|
||||
startRegistrationTask(call.argument("userId"), result, false);
|
||||
break;
|
||||
case "extractFaceFeature":
|
||||
startFaceFeatureExtraction(stringArgument(call, "filePath"), result);
|
||||
break;
|
||||
case "applyFaceDelta":
|
||||
startFaceDelta(call, result);
|
||||
break;
|
||||
case "getFace":
|
||||
String sampleId = Objects.requireNonNull(call.argument("sampleId")).toString();
|
||||
Map<String, Object> face = FlutterTool.getFace(context, Integer.parseInt(sampleId));
|
||||
@@ -312,6 +326,84 @@ public class FlutterFaceDiscernPlugin implements FlutterPlugin, MethodChannel.Me
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 在统一人脸注册占用标记和后台执行器中提取临时图片特征。
|
||||
*
|
||||
* @param filePath 临时图片绝对路径
|
||||
* @param result Flutter MethodChannel 异步结果
|
||||
*/
|
||||
private void startFaceFeatureExtraction(String filePath, MethodChannel.Result result) {
|
||||
if (!FlutterTool.tryStartRegistration()) {
|
||||
result.error("REGISTRATION_IN_PROGRESS", "人脸库正在更新,请稍候", null);
|
||||
return;
|
||||
}
|
||||
FACE_EXECUTOR.execute(() -> {
|
||||
try {
|
||||
Map<String, Object> extraction = FlutterTool.extractFaceFeature(context, filePath);
|
||||
MAIN_HANDLER.post(() -> result.success(extraction));
|
||||
} catch (Exception exception) {
|
||||
Log.e(TAG, "提取人脸特征失败:" + exception.getMessage(), exception);
|
||||
MAIN_HANDLER.post(() -> result.error("EXTRACT_FACE_FEATURE_FAILED", "人脸特征提取失败", null));
|
||||
} finally {
|
||||
FlutterTool.finishRegistration();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 在统一人脸注册占用标记和后台执行器中应用单样本增删。
|
||||
*
|
||||
* @param call 包含 operation、sampleId、用户元数据和可选特征字节的调用
|
||||
* @param result Flutter MethodChannel 异步结果
|
||||
*/
|
||||
private void startFaceDelta(MethodCall call, MethodChannel.Result result) {
|
||||
if (!FlutterTool.tryStartRegistration()) {
|
||||
result.error("REGISTRATION_IN_PROGRESS", "人脸库正在更新,请稍候", null);
|
||||
return;
|
||||
}
|
||||
FACE_EXECUTOR.execute(() -> {
|
||||
try {
|
||||
String operation = stringArgument(call, "operation");
|
||||
Number sampleIdValue = call.argument("sampleId");
|
||||
if (sampleIdValue == null) throw new IllegalArgumentException("sampleId不能为空");
|
||||
byte[] featureData = byteArrayArgument(call.argument("featureData"));
|
||||
Map<String, Object> delta = FlutterTool.applyFaceDelta(
|
||||
context,
|
||||
operation,
|
||||
sampleIdValue.intValue(),
|
||||
stringArgument(call, "userId"),
|
||||
stringArgument(call, "username"),
|
||||
featureData
|
||||
);
|
||||
MAIN_HANDLER.post(() -> result.success(delta));
|
||||
} catch (Exception exception) {
|
||||
Log.e(TAG, "应用人脸增量失败:" + exception.getMessage(), exception);
|
||||
MAIN_HANDLER.post(() -> result.error("APPLY_FACE_DELTA_FAILED", "人脸库增量更新失败", null));
|
||||
} finally {
|
||||
FlutterTool.finishRegistration();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 Flutter StandardMessageCodec 的数字列表转换为虹软特征字节。
|
||||
*
|
||||
* @param value 可为空的 byte[] 或数字列表
|
||||
* @return 特征字节;参数为空时返回空数组
|
||||
*/
|
||||
private byte[] byteArrayArgument(Object value) {
|
||||
if (value instanceof byte[]) return (byte[]) value;
|
||||
if (!(value instanceof List)) return new byte[0];
|
||||
List<?> values = (List<?>) value;
|
||||
byte[] result = new byte[values.size()];
|
||||
for (int index = 0; index < values.size(); index++) {
|
||||
Object item = values.get(index);
|
||||
if (!(item instanceof Number)) throw new IllegalArgumentException("featureData包含非数字值");
|
||||
result[index] = ((Number) item).byteValue();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void emitRegistrationProgress(Map<String, Object> progress) {
|
||||
MAIN_HANDLER.post(() -> {
|
||||
EventChannel.EventSink sink = registrationEventSink;
|
||||
|
||||
+113
-15
@@ -24,6 +24,7 @@ import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -107,9 +108,17 @@ public class FlutterTool {
|
||||
|
||||
FaceServer faceServer = FaceServer.getInstance();
|
||||
if (activated) faceServer.init(context);
|
||||
int databaseCount = getUserFace(context, null).size();
|
||||
List<FaceEntity> databaseFaces = getUserFace(context, null);
|
||||
int databaseCount = databaseFaces.size();
|
||||
int memoryCount = activated && faceServer.isEngineReady() ? Math.max(faceServer.getTotalFaceCount(), 0) : 0;
|
||||
boolean registrationInProgress = REGISTRATION_IN_PROGRESS.get();
|
||||
Set<Integer> databaseSampleIds = new HashSet<>();
|
||||
for (FaceEntity face : databaseFaces) {
|
||||
if (face.getSampleId() != null) databaseSampleIds.add(face.getSampleId());
|
||||
}
|
||||
Set<Integer> memorySampleIds = activated && faceServer.isEngineReady()
|
||||
? faceServer.getRegisteredFaceIds()
|
||||
: new HashSet<>();
|
||||
|
||||
status.put("activated", activated);
|
||||
status.put("activationCode", activeCode);
|
||||
@@ -121,7 +130,7 @@ public class FlutterTool {
|
||||
status.put("databaseCount", databaseCount);
|
||||
status.put("memoryCount", memoryCount);
|
||||
status.put("registrationInProgress", registrationInProgress);
|
||||
status.put("libraryConsistent", !registrationInProgress && databaseCount == memoryCount);
|
||||
status.put("libraryConsistent", !registrationInProgress && databaseSampleIds.equals(memorySampleIds));
|
||||
status.put("registrationProgress", new HashMap<>(latestRegistrationProgress));
|
||||
status.put("lastRegistrationResult", latestRegistrationResult == null ? null : new HashMap<>(latestRegistrationResult));
|
||||
return status;
|
||||
@@ -284,6 +293,68 @@ public class FlutterTool {
|
||||
return i == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从临时图片提取虹软注册特征,不修改 SQLite 或虹软内存人脸库。
|
||||
*
|
||||
* @param context Android 应用上下文
|
||||
* @param filePath 同步服务写入的临时图片绝对路径
|
||||
* @return 包含成功标记、安全摘要和特征字节的 MethodChannel 结果
|
||||
*/
|
||||
static public Map<String, Object> extractFaceFeature(Context context, String filePath) {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
try {
|
||||
byte[] featureData = extractFaceModelFromFile(context, filePath);
|
||||
boolean success = featureData != null && featureData.length > 0;
|
||||
result.put("success", success);
|
||||
result.put("message", success ? "人脸特征提取成功" : "图片中未提取到可注册人脸");
|
||||
result.put("featureData", success ? featureData : new byte[0]);
|
||||
return result;
|
||||
} catch (Exception exception) {
|
||||
Log.e(TAG, "提取人脸特征异常error:" + exception.getMessage(), exception);
|
||||
result.put("success", false);
|
||||
result.put("message", "人脸特征提取失败");
|
||||
result.put("featureData", new byte[0]);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 在虹软内存库中应用单样本增删;替换失败由 FaceServer 恢复旧特征。
|
||||
*
|
||||
* @param context Android 应用上下文
|
||||
* @param operation upsert 或 delete
|
||||
* @param sampleId 虹软 32 位搜索标识
|
||||
* @param userId 用户主键,仅 upsert 使用
|
||||
* @param username 用户姓名,仅作为内存识别元数据
|
||||
* @param featureData 已通过提取校验的特征字节,仅 upsert 使用
|
||||
* @return 包含成功标记、SDK 返回码和安全摘要的结果
|
||||
*/
|
||||
static public Map<String, Object> applyFaceDelta(Context context, String operation, int sampleId,
|
||||
String userId, String username, byte[] featureData) {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
FaceServer faceServer = FaceServer.getInstance();
|
||||
faceServer.init(context);
|
||||
int code;
|
||||
if ("delete".equals(operation)) {
|
||||
code = faceServer.removeFace(sampleId);
|
||||
} else if ("upsert".equals(operation) && featureData != null && featureData.length > 0) {
|
||||
FaceEntity entity = new FaceEntity();
|
||||
entity.setFaceId(sampleId);
|
||||
entity.setSampleId(sampleId);
|
||||
entity.setUserId(userId);
|
||||
entity.setUserName(username);
|
||||
entity.setFaceModel(featureData);
|
||||
code = faceServer.replaceFaceFeature(entity);
|
||||
} else {
|
||||
code = ErrorInfo.MERR_INVALID_PARAM;
|
||||
}
|
||||
boolean success = code == ErrorInfo.MOK;
|
||||
result.put("success", success);
|
||||
result.put("code", code);
|
||||
result.put("message", success ? "人脸库增量更新成功" : "人脸库增量更新失败");
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// 旧通道保留同步包装;新页面通过 rebuildFaceLibrary 获取完整结果和进度。
|
||||
static public List<Integer> registerFaceByDatabase(Context context, String userId) {
|
||||
@@ -341,7 +412,13 @@ public class FlutterTool {
|
||||
for (int start = 0; start < validFaces.size(); start += REGISTER_BATCH_SIZE) {
|
||||
int end = Math.min(start + REGISTER_BATCH_SIZE, validFaces.size());
|
||||
List<FaceEntity> batch = new ArrayList<>(validFaces.subList(start, end));
|
||||
int code = faceServer.registerFaceFeatureInfoBatchFromDb(batch);
|
||||
int code;
|
||||
try {
|
||||
code = faceServer.registerFaceFeatureInfoBatchFromDb(batch);
|
||||
} catch (Exception exception) {
|
||||
code = -99;
|
||||
Log.e(TAG, "批量注册抛出异常,改为逐条处理error:" + exception.getMessage(), exception);
|
||||
}
|
||||
if (code == ErrorInfo.MOK) {
|
||||
success += batch.size();
|
||||
processed += batch.size();
|
||||
@@ -352,7 +429,13 @@ public class FlutterTool {
|
||||
|
||||
Log.w(TAG, "批量注册失败code:" + code + ";改为逐条定位失败样本");
|
||||
for (FaceEntity face : batch) {
|
||||
int singleCode = faceServer.registerFaceFeatureInfoFromDb(face);
|
||||
int singleCode;
|
||||
try {
|
||||
singleCode = faceServer.registerFaceFeatureInfoFromDb(face);
|
||||
} catch (Exception exception) {
|
||||
singleCode = -99;
|
||||
Log.e(TAG, "单条注册异常sampleId:" + maskSampleId(face.getSampleId()) + ";error:" + exception.getMessage(), exception);
|
||||
}
|
||||
if (singleCode == ErrorInfo.MOK) {
|
||||
success++;
|
||||
} else if (face.getSampleId() != null) {
|
||||
@@ -377,17 +460,7 @@ public class FlutterTool {
|
||||
private static boolean extractAndPersistFaceModel(Context context, FaceEntity face) {
|
||||
if (face == null || face.getFaceFilePath() == null) return false;
|
||||
try {
|
||||
byte[] bytes = FileUtil.fileToData(new File(face.getFaceFilePath()));
|
||||
if (bytes == null || bytes.length == 0) return false;
|
||||
FaceServer faceServer = FaceServer.getInstance();
|
||||
faceServer.init(context);
|
||||
Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
|
||||
if (bitmap == null) return false;
|
||||
Bitmap alignedBitmap = ArcSoftImageUtil.getAlignedBitmap(bitmap, true);
|
||||
byte[] bgr24Data = ArcSoftImageUtil.createImageData(alignedBitmap.getWidth(), alignedBitmap.getHeight(), ArcSoftImageFormat.BGR24);
|
||||
int transformCode = ArcSoftImageUtil.bitmapToImageData(alignedBitmap, bgr24Data, ArcSoftImageFormat.BGR24);
|
||||
if (transformCode != ArcSoftImageUtilError.CODE_SUCCESS) return false;
|
||||
byte[] faceBytes = faceServer.registerBgr24(context, bgr24Data, alignedBitmap.getWidth(), alignedBitmap.getHeight(), String.valueOf(System.currentTimeMillis()));
|
||||
byte[] faceBytes = extractFaceModelFromFile(context, face.getFaceFilePath());
|
||||
if (faceBytes == null || faceBytes.length == 0) return false;
|
||||
face.setFaceModel(faceBytes);
|
||||
return updateFaceModel(context, face);
|
||||
@@ -397,6 +470,31 @@ public class FlutterTool {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解码图片并在统一 FaceServer 引擎锁内提取注册特征,不写入业务数据库。
|
||||
*
|
||||
* @param context Android 应用上下文
|
||||
* @param filePath 待校验图片绝对路径
|
||||
* @return 虹软特征字节,图片无效或未检测到合格人脸时返回空
|
||||
*/
|
||||
private static byte[] extractFaceModelFromFile(Context context, String filePath) {
|
||||
if (filePath == null || filePath.isEmpty()) return null;
|
||||
byte[] bytes = FileUtil.fileToData(new File(filePath));
|
||||
if (bytes == null || bytes.length == 0) return null;
|
||||
Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
|
||||
if (bitmap == null) return null;
|
||||
Bitmap alignedBitmap = ArcSoftImageUtil.getAlignedBitmap(bitmap, true);
|
||||
if (alignedBitmap == null) return null;
|
||||
byte[] bgr24Data = ArcSoftImageUtil.createImageData(
|
||||
alignedBitmap.getWidth(), alignedBitmap.getHeight(), ArcSoftImageFormat.BGR24);
|
||||
int transformCode = ArcSoftImageUtil.bitmapToImageData(alignedBitmap, bgr24Data, ArcSoftImageFormat.BGR24);
|
||||
if (transformCode != ArcSoftImageUtilError.CODE_SUCCESS) return null;
|
||||
FaceServer faceServer = FaceServer.getInstance();
|
||||
faceServer.init(context);
|
||||
return faceServer.registerBgr24(context, bgr24Data, alignedBitmap.getWidth(),
|
||||
alignedBitmap.getHeight(), String.valueOf(System.currentTimeMillis()));
|
||||
}
|
||||
|
||||
private static boolean hasFaceModel(FaceEntity face) {
|
||||
return face != null && face.getFaceModel() != null && face.getFaceModel().length > 0;
|
||||
}
|
||||
|
||||
+98
-19
@@ -26,9 +26,12 @@ import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.HashSet;
|
||||
|
||||
import io.flutter.plugins.facediscern.tool.face.model.CompareResult;
|
||||
import io.flutter.plugins.facediscern.tool.face.model.FaceEntity;
|
||||
@@ -129,25 +132,96 @@ public class FaceServer {
|
||||
return code;
|
||||
}
|
||||
|
||||
/**
|
||||
* 在同一虹软引擎互斥边界内原子替换单个搜索标识的人脸特征。
|
||||
*
|
||||
* <p>替换前复制旧特征和业务元数据;新特征注册失败或 SDK 抛异常时恢复旧特征,
|
||||
* 避免坏图导致原本可识别的人脸永久丢失。</p>
|
||||
*
|
||||
* @param faceEntity 包含搜索标识、用户元数据和新特征的目标人脸
|
||||
* @return 虹软返回码,插件异常返回内部错误码 {@code -99}
|
||||
*/
|
||||
public synchronized int replaceFaceFeature(FaceEntity faceEntity) {
|
||||
if (faceEntity == null || faceEngine == null || faceEntity.getFaceId() == null || faceEntity.getFaceModel() == null) {
|
||||
return -99;
|
||||
}
|
||||
int searchId = faceEntity.getFaceId();
|
||||
FaceEntity oldEntity = copyFaceEntity(faceRegisterInfoMap.get(searchId));
|
||||
try {
|
||||
FaceFeatureInfo oldFeature = getFaceFeature(searchId);
|
||||
if (oldEntity == null && oldFeature != null) {
|
||||
oldEntity = new FaceEntity(searchId, oldFeature.getFeatureData());
|
||||
}
|
||||
faceEngine.removeFaceFeature(searchId);
|
||||
faceRegisterInfoMap.remove(searchId);
|
||||
FaceFeatureInfo newFeature = new FaceFeatureInfo(searchId, faceEntity.getFaceModel());
|
||||
int code = faceEngine.registerFaceFeature(Collections.singletonList(newFeature));
|
||||
if (code == ErrorInfo.MOK && getFaceFeature(searchId) != null) {
|
||||
faceRegisterInfoMap.put(searchId, faceEntity);
|
||||
return code;
|
||||
}
|
||||
restoreFaceFeature(searchId, oldEntity);
|
||||
return code == ErrorInfo.MOK ? -99 : code;
|
||||
} catch (Exception exception) {
|
||||
Log.e(TAG, "替换人脸特征异常searchId:" + searchId + ";error:" + exception.getMessage(), exception);
|
||||
restoreFaceFeature(searchId, oldEntity);
|
||||
return -99;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回当前虹软内存库已登记的搜索标识快照,用于集合一致性判断。
|
||||
*
|
||||
* @return 与内部映射解耦的搜索标识集合
|
||||
*/
|
||||
public synchronized Set<Integer> getRegisteredFaceIds() {
|
||||
return new HashSet<>(faceRegisterInfoMap.keySet());
|
||||
}
|
||||
|
||||
/**
|
||||
* 恢复单样本替换前的人脸特征;没有旧特征时保持目标搜索标识为空。
|
||||
*
|
||||
* @param searchId 需要恢复的虹软搜索标识
|
||||
* @param oldEntity 替换前的特征和元数据,新增场景为空
|
||||
*/
|
||||
private void restoreFaceFeature(int searchId, FaceEntity oldEntity) {
|
||||
try {
|
||||
faceEngine.removeFaceFeature(searchId);
|
||||
faceRegisterInfoMap.remove(searchId);
|
||||
if (oldEntity == null || oldEntity.getFaceModel() == null) return;
|
||||
FaceFeatureInfo oldFeature = new FaceFeatureInfo(searchId, oldEntity.getFaceModel());
|
||||
if (faceEngine.registerFaceFeature(Collections.singletonList(oldFeature)) == ErrorInfo.MOK) {
|
||||
faceRegisterInfoMap.put(searchId, oldEntity);
|
||||
} else {
|
||||
Log.e(TAG, "恢复旧人脸特征失败searchId:" + searchId);
|
||||
}
|
||||
} catch (Exception exception) {
|
||||
Log.e(TAG, "恢复旧人脸特征异常searchId:" + searchId + ";error:" + exception.getMessage(), exception);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 深拷贝内存库中的人脸业务元数据和特征,避免回滚快照被后续修改。
|
||||
*
|
||||
* @param source 待复制的人脸记录
|
||||
* @return 独立副本,源记录为空时返回空
|
||||
*/
|
||||
private FaceEntity copyFaceEntity(FaceEntity source) {
|
||||
if (source == null) return null;
|
||||
FaceEntity copy = new FaceEntity();
|
||||
copy.setFaceId(source.getFaceId());
|
||||
copy.setSampleId(source.getSampleId());
|
||||
copy.setUserId(source.getUserId());
|
||||
copy.setUserName(source.getUserName());
|
||||
copy.setFaceFilePath(source.getFaceFilePath());
|
||||
copy.setFaceModel(source.getFaceModel() == null ? null : source.getFaceModel().clone());
|
||||
return copy;
|
||||
}
|
||||
|
||||
// 注册单个人脸数据
|
||||
public synchronized int registerFaceFeatureInfoFromDb(FaceEntity faceEntity) {
|
||||
if (faceEntity != null && faceEngine != null) {
|
||||
// ArcSoft 不允许同一个 searchId 重复注册,增量更新前只移除当前用户的旧记录。
|
||||
faceEngine.removeFaceFeature(faceEntity.getFaceId());
|
||||
faceRegisterInfoMap.remove(faceEntity.getFaceId());
|
||||
FaceFeatureInfo faceFeatureInfo = new FaceFeatureInfo(faceEntity.getFaceId(), faceEntity.getFaceModel());
|
||||
int code = faceEngine.registerFaceFeature(faceFeatureInfo);
|
||||
if (code == ErrorInfo.MOK) {
|
||||
faceEntity.setFaceModel(faceFeatureInfo.getFeatureData());
|
||||
// 查询一下确保入库成功
|
||||
FaceFeatureInfo afterDetail = getFaceFeature(faceEntity.getFaceId());
|
||||
if (Objects.nonNull(afterDetail)) {
|
||||
faceRegisterInfoMap.put(faceEntity.getFaceId(), faceEntity);
|
||||
return code;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -99;
|
||||
// 历史单条入口也复用带旧特征回滚的替换逻辑,避免 SDK 异常后丢失原可用样本。
|
||||
return replaceFaceFeature(faceEntity);
|
||||
}
|
||||
|
||||
// 从人脸数据库中搜索人脸
|
||||
@@ -170,6 +244,11 @@ public class FaceServer {
|
||||
// 删除人脸-1删除全部
|
||||
public synchronized int removeFace(int searchId) {
|
||||
if (faceEngine == null) return -99;
|
||||
// MQ 和每日快照都可能重复删除同一样本;内存库已不存在时直接按幂等成功处理。
|
||||
if (searchId != -1 && getFaceFeature(searchId) == null) {
|
||||
faceRegisterInfoMap.remove(searchId);
|
||||
return ErrorInfo.MOK;
|
||||
}
|
||||
int code = faceEngine.removeFaceFeature(searchId);
|
||||
if (code == ErrorInfo.MOK) {
|
||||
if (searchId == -1) {
|
||||
@@ -199,7 +278,7 @@ public class FaceServer {
|
||||
}
|
||||
|
||||
// 在特征库中搜索
|
||||
public CompareResult searchFaceFeature(FaceFeature faceFeature) {
|
||||
public synchronized CompareResult searchFaceFeature(FaceFeature faceFeature) {
|
||||
if (faceFeature == null) return null;
|
||||
if (faceEngine == null) return null;
|
||||
SearchResult searchResult = null;
|
||||
@@ -223,7 +302,7 @@ public class FaceServer {
|
||||
}
|
||||
|
||||
|
||||
public byte[] registerBgr24(Context context, byte[] bgr24, int width, int height, String name) {
|
||||
public synchronized byte[] registerBgr24(Context context, byte[] bgr24, int width, int height, String name) {
|
||||
if (faceEngine == null || context == null || bgr24 == null || width % 4 != 0 || bgr24.length != width * height * 3) {
|
||||
Log.e(TAG, "registerBgr24: invalid params");
|
||||
return null;
|
||||
|
||||
Reference in new issue
Block a user