feat(face): 升级虹软SDK到5.0及完善人脸识别逻辑
- FaceHelper中集成独立口罩检测引擎maskEngine并调用口罩检测接口 - FaceEngine初始化中增加口罩检测引擎初始化及状态管理 - FaceServer注册人脸时打印特征信息日志,便于调试 - CompareResult新增similarPass字段,标记识别是否通过 - FaceApi新增针对人脸数据的增删查接口,方便管理 - FaceDatabase数据库版本升级至2,支持人脸特征库重建迁移 - FaceDao新增多用户查询、人脸计数及临时用户数据删除接口 - FaceEntity新增userId、userFaceId、member标记及更新时间字段及相关方法 - 优化人脸识别失败重试机制,连续失败后回调识别失败结果 - 优化识别回调逻辑,成功与失败结果均及时通知观察者 - 调整配置获取逻辑,禁用人脸活体检测时关闭相关初始化 - 精简并修正部分冗余代码与日志输出,提升代码清晰度与可维护性
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -88,14 +88,8 @@ public class ConfigUtil {
|
|||||||
* 默认相机分辨率
|
* 默认相机分辨率
|
||||||
*/
|
*/
|
||||||
// private static final String DEFAULT_PREVIEW_SIZE = "1280x720";
|
// private static final String DEFAULT_PREVIEW_SIZE = "1280x720";
|
||||||
// private static final String DEFAULT_PREVIEW_SIZE = "1080x720";
|
|
||||||
// private static final String DEFAULT_PREVIEW_SIZE = "720x1080";
|
|
||||||
private static final String DEFAULT_PREVIEW_SIZE = "720x1280";
|
private static final String DEFAULT_PREVIEW_SIZE = "720x1280";
|
||||||
// private static final String DEFAULT_PREVIEW_SIZE = "608x456";
|
// private static final String DEFAULT_PREVIEW_SIZE = "400x640";
|
||||||
// private static final String DEFAULT_PREVIEW_SIZE = "1024x768";
|
|
||||||
// private static final String DEFAULT_PREVIEW_SIZE = "800x600";
|
|
||||||
// private static final String DEFAULT_PREVIEW_SIZE = "640x480";
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取String类型的preference
|
* 获取String类型的preference
|
||||||
@@ -244,7 +238,7 @@ public class ConfigUtil {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO: 该Demo基于单人脸识别实现,若想使用多人脸识别,请将 return true 改成 return getBoolean,并修改相关配置项的preference.xml和业务代码
|
* TODO: 该Demo基于单人脸识别实现,若想使用多人脸识别,请将 return true 改成 return getBoolean,并修改相关配置项的preference.xml和业务代码
|
||||||
* <p>
|
*
|
||||||
* 获取识别界面是否保留最大人脸
|
* 获取识别界面是否保留最大人脸
|
||||||
*
|
*
|
||||||
* @param context 上下文
|
* @param context 上下文
|
||||||
@@ -349,7 +343,7 @@ public class ConfigUtil {
|
|||||||
return Float.parseFloat(getString(context, R.string.preference_ir_liveness_threshold, String.valueOf(RECOMMEND_IR_LIVENESS_THRESHOLD)));
|
return Float.parseFloat(getString(context, R.string.preference_ir_liveness_threshold, String.valueOf(RECOMMEND_IR_LIVENESS_THRESHOLD)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static float getLivenessFqThreshold(Context context) {
|
public static float getLivenessFqThreshold(Context context){
|
||||||
return Float.parseFloat(getString(context, R.string.preference_liveness_fq_threshold, String.valueOf(RECOMMEND_LIVENESS_FQ_THRESHOLD)));
|
return Float.parseFloat(getString(context, R.string.preference_liveness_fq_threshold, String.valueOf(RECOMMEND_LIVENESS_FQ_THRESHOLD)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ public class ErrorCodeUtil {
|
|||||||
}
|
}
|
||||||
return "unknown error";
|
return "unknown error";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 将ArcSoftImageUtil错误码转换为对应的错误码常量名,便于理解
|
* 将ArcSoftImageUtil错误码转换为对应的错误码常量名,便于理解
|
||||||
* TODO:目前每次都遍历,如果使用频繁,建议将Field缓存处理,避免每次都反射
|
* TODO:目前每次都遍历,如果使用频繁,建议将Field缓存处理,避免每次都反射
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.sw.plate.utils.arcface;
|
package com.sw.plate.utils.arcface;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.text.TextUtils;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import com.arcsoft.face.FaceEngine;
|
import com.arcsoft.face.FaceEngine;
|
||||||
@@ -31,25 +32,21 @@ public class FaceApi {
|
|||||||
*/
|
*/
|
||||||
public void updateFaceData(int index, List<FaceEntity> list) {
|
public void updateFaceData(int index, List<FaceEntity> list) {
|
||||||
Log.d(TAG, "updateFaceData: index = " + index + ", listSize = " + list.size());
|
Log.d(TAG, "updateFaceData: index = " + index + ", listSize = " + list.size());
|
||||||
FaceDao faceDao = FaceDatabase.getInstance(App.getContext()).faceDao();
|
FaceDao faceDao = getFaceDao();
|
||||||
|
// 档口机业务:首页(index==1)全量同步前清空旧特征库,避免特征重复堆积
|
||||||
if (index == 1) {
|
if (index == 1) {
|
||||||
faceDao.deleteAll();
|
faceDao.deleteAll();
|
||||||
faceDao.resetId();
|
faceDao.resetId();
|
||||||
}
|
}
|
||||||
List<Long> insertIdList = faceDao.insert(list);
|
faceDao.insert(list);
|
||||||
Log.d(TAG, "updateFaceData: count = " + insertIdList.size());
|
|
||||||
List<FaceEntity> queryList = faceDao.getAllFaces();
|
List<FaceEntity> queryList = faceDao.getAllFaces();
|
||||||
Log.d(TAG, "updateFaceData: queryList.size = " + queryList.size());
|
Log.d(TAG, "updateFaceData: queryList.size = " + queryList.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateFaceData2(int index, List<FaceEntity> list) {
|
public void clearFaceData() {
|
||||||
Log.d(TAG, "updateFaceData: index = " + index + ", listSize = " + list.size());
|
FaceDao faceDao = getFaceDao();
|
||||||
FaceDao faceDao = FaceDatabase.getInstance(App.getContext()).faceDao();
|
faceDao.deleteAll();
|
||||||
if (index == 0) {
|
faceDao.resetId();
|
||||||
faceDao.deleteAll();
|
|
||||||
faceDao.resetId();
|
|
||||||
}
|
|
||||||
faceDao.insert(list);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -92,4 +89,35 @@ public class FaceApi {
|
|||||||
// List<FacePreviewInfo> facePreviewInfoList = recognizeViewModel.onPreviewFrame(nv21, true);
|
// List<FacePreviewInfo> facePreviewInfoList = recognizeViewModel.onPreviewFrame(nv21, true);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void deleteByUserName(String userName) {
|
||||||
|
getFaceDao().deleteFaceById(userName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long insert(FaceEntity entity) {
|
||||||
|
if (entity == null) {
|
||||||
|
return 0L;
|
||||||
|
}
|
||||||
|
return getFaceDao().insert(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
public FaceEntity queryByUserName(String userName) {
|
||||||
|
if (TextUtils.isEmpty(userName)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return getFaceDao().queryByUserName(userName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int queryFaceCount() {
|
||||||
|
return getFaceDao().getFaceCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<FaceEntity> queryAllByUserName(String userName) {
|
||||||
|
return getFaceDao().queryAllByUserName(userName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public FaceDao getFaceDao() {
|
||||||
|
return FaceDatabase.getInstance(App.getContext()).faceDao();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ public class FaceRectTransformer {
|
|||||||
rect.bottom *= verticalRatio;
|
rect.bottom *= verticalRatio;
|
||||||
|
|
||||||
Rect newRect = new Rect();
|
Rect newRect = new Rect();
|
||||||
// L.e("cameraDisplayOrientation " + cameraDisplayOrientation + " === " + cameraId);
|
L.e("cameraDisplayOrientation " + cameraDisplayOrientation + " === " + cameraId);
|
||||||
switch (cameraDisplayOrientation) {
|
switch (cameraDisplayOrientation) {
|
||||||
case 0:
|
case 0:
|
||||||
if (cameraId == Camera.CameraInfo.CAMERA_FACING_FRONT) {
|
if (cameraId == Camera.CameraInfo.CAMERA_FACING_FRONT) {
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package com.sw.plate.utils.arcface;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.graphics.Canvas;
|
import android.graphics.Canvas;
|
||||||
import android.graphics.Color;
|
|
||||||
import android.graphics.Paint;
|
import android.graphics.Paint;
|
||||||
import android.graphics.Path;
|
import android.graphics.Path;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
@@ -47,6 +46,10 @@ public class FaceRectView extends View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getRectColor() {
|
||||||
|
return paint.getColor();
|
||||||
|
}
|
||||||
|
|
||||||
public void clearFaceInfo() {
|
public void clearFaceInfo() {
|
||||||
drawInfoList.clear();
|
drawInfoList.clear();
|
||||||
postInvalidate();
|
postInvalidate();
|
||||||
@@ -216,7 +219,6 @@ public class FaceRectView extends View {
|
|||||||
paint.setStyle(Paint.Style.STROKE);
|
paint.setStyle(Paint.Style.STROKE);
|
||||||
paint.setStrokeWidth(faceRectThickness);
|
paint.setStrokeWidth(faceRectThickness);
|
||||||
paint.setColor(drawInfo.getColor());
|
paint.setColor(drawInfo.getColor());
|
||||||
// paint.setColor(Color.parseColor("#FF0000"));
|
|
||||||
paint.setAntiAlias(true);
|
paint.setAntiAlias(true);
|
||||||
|
|
||||||
Path mPath = new Path();
|
Path mPath = new Path();
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ public class FileUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static boolean saveDataToFile(byte[] data, File file, boolean append) {
|
public static boolean saveDataToFile(byte[] data, File file, boolean append) {
|
||||||
if (data == null) {
|
if (data == null){
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
File parentFile = file.getParentFile();
|
File parentFile = file.getParentFile();
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import android.hardware.Camera;
|
|||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
import androidx.annotation.IntDef;
|
import androidx.annotation.IntDef;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
@@ -18,6 +19,7 @@ import com.arcsoft.face.ImageQualitySimilar;
|
|||||||
import com.arcsoft.face.LivenessInfo;
|
import com.arcsoft.face.LivenessInfo;
|
||||||
import com.arcsoft.face.MaskInfo;
|
import com.arcsoft.face.MaskInfo;
|
||||||
import com.arcsoft.face.enums.ExtractType;
|
import com.arcsoft.face.enums.ExtractType;
|
||||||
|
import com.sw.plate.App;
|
||||||
import com.sw.plate.utils.L;
|
import com.sw.plate.utils.L;
|
||||||
import com.sw.plate.utils.arcface.FaceRectTransformer;
|
import com.sw.plate.utils.arcface.FaceRectTransformer;
|
||||||
import com.sw.plate.utils.arcface.face.constants.LivenessType;
|
import com.sw.plate.utils.arcface.face.constants.LivenessType;
|
||||||
@@ -37,7 +39,6 @@ import java.lang.annotation.Retention;
|
|||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Enumeration;
|
import java.util.Enumeration;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -102,6 +103,10 @@ public class FaceHelper implements FaceListener {
|
|||||||
* 活体检测引擎
|
* 活体检测引擎
|
||||||
*/
|
*/
|
||||||
private FaceEngine flEngine;
|
private FaceEngine flEngine;
|
||||||
|
/**
|
||||||
|
* 口罩检测引擎(5.0 独立引擎)
|
||||||
|
*/
|
||||||
|
private FaceEngine maskEngine;
|
||||||
|
|
||||||
private Camera.Size previewSize;
|
private Camera.Size previewSize;
|
||||||
|
|
||||||
@@ -146,11 +151,6 @@ public class FaceHelper implements FaceListener {
|
|||||||
*/
|
*/
|
||||||
private boolean onlyDetectLiveness;
|
private boolean onlyDetectLiveness;
|
||||||
|
|
||||||
/**
|
|
||||||
* 是否需要更新faceData
|
|
||||||
*/
|
|
||||||
private boolean needUpdateFaceData;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 识别的配置项
|
* 识别的配置项
|
||||||
*/
|
*/
|
||||||
@@ -180,9 +180,9 @@ public class FaceHelper implements FaceListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private FaceHelper(Builder builder) {
|
private FaceHelper(Builder builder) {
|
||||||
needUpdateFaceData = builder.needUpdateFaceData;
|
|
||||||
onlyDetectLiveness = builder.onlyDetectLiveness;
|
onlyDetectLiveness = builder.onlyDetectLiveness;
|
||||||
ftEngine = builder.ftEngine;
|
ftEngine = builder.ftEngine;
|
||||||
|
maskEngine = builder.maskEngine;
|
||||||
trackedFaceCount = builder.trackedFaceCount;
|
trackedFaceCount = builder.trackedFaceCount;
|
||||||
previewSize = builder.previewSize;
|
previewSize = builder.previewSize;
|
||||||
frEngine = builder.frEngine;
|
frEngine = builder.frEngine;
|
||||||
@@ -350,11 +350,11 @@ public class FaceHelper implements FaceListener {
|
|||||||
return facePreviewInfoList;
|
return facePreviewInfoList;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!onlyDetectLiveness) {
|
if (!onlyDetectLiveness && maskEngine != null) {
|
||||||
code = ftEngine.process(rgbNv21, previewSize.width, previewSize.height, FaceEngine.CP_PAF_NV21, faceInfoList,
|
code = maskEngine.process(rgbNv21, previewSize.width, previewSize.height, FaceEngine.CP_PAF_NV21, faceInfoList,
|
||||||
FaceEngine.ASF_MASK_DETECT);
|
FaceEngine.ASF_MASK_DETECT);
|
||||||
if (code == ErrorInfo.MOK) {
|
if (code == ErrorInfo.MOK) {
|
||||||
code = ftEngine.getMask(maskInfoList);
|
code = maskEngine.getMask(maskInfoList);
|
||||||
if (code != ErrorInfo.MOK) {
|
if (code != ErrorInfo.MOK) {
|
||||||
onFail(new Exception("process getMask failed,code is " + code));
|
onFail(new Exception("process getMask failed,code is " + code));
|
||||||
return facePreviewInfoList;
|
return facePreviewInfoList;
|
||||||
@@ -430,7 +430,7 @@ public class FaceHelper implements FaceListener {
|
|||||||
if (!contained) {
|
if (!contained) {
|
||||||
RecognizeInfo recognizeInfo = recognizeInfoMap.remove(key);
|
RecognizeInfo recognizeInfo = recognizeInfoMap.remove(key);
|
||||||
if (recognizeInfo != null) {
|
if (recognizeInfo != null) {
|
||||||
recognizeCallback.onNoticeChanged("leave");
|
recognizeCallback.onNoticeChanged("");
|
||||||
// 人脸离开时,通知特征提取线程,避免一直等待活体结果
|
// 人脸离开时,通知特征提取线程,避免一直等待活体结果
|
||||||
synchronized (recognizeInfo.getWaitLock()) {
|
synchronized (recognizeInfo.getWaitLock()) {
|
||||||
recognizeInfo.getWaitLock().notifyAll();
|
recognizeInfo.getWaitLock().notifyAll();
|
||||||
@@ -660,21 +660,52 @@ public class FaceHelper implements FaceListener {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int failCount = 0;
|
||||||
|
private long startTime = 0;
|
||||||
private void searchFace(final FaceFeature faceFeature, final Integer trackId) {
|
private void searchFace(final FaceFeature faceFeature, final Integer trackId) {
|
||||||
CompareResult compareResult = FaceServer.getInstance().searchFaceFeature(faceFeature, frEngine);
|
CompareResult compareResult = FaceServer.getInstance().searchFaceFeature(faceFeature, frEngine);
|
||||||
if (compareResult == null || compareResult.getFaceEntity() == null) {
|
if (compareResult == null || compareResult.getFaceEntity() == null) {
|
||||||
|
if (startTime == 0) {
|
||||||
|
failCount = 0;
|
||||||
|
startTime = System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
if (System.currentTimeMillis() - startTime > 10*1000) {
|
||||||
|
failCount = 0;
|
||||||
|
startTime = System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
failCount++;
|
||||||
|
float similar;
|
||||||
|
if (compareResult != null) {
|
||||||
|
similar = compareResult.getSimilar();
|
||||||
|
} else {
|
||||||
|
similar = 0f;
|
||||||
|
}
|
||||||
|
// new Handler(Looper.getMainLooper()).post(()-> {
|
||||||
|
// try {
|
||||||
|
// Toast.makeText(App.getContext(),"识别失败"+failCount+"次,similar="+similar, Toast.LENGTH_SHORT).show();
|
||||||
|
// } catch (Exception e) {
|
||||||
|
// e.printStackTrace();
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
Log.d(TAG, "collectFace,searchFace查询失败"+failCount+"次,similar="+similar);
|
||||||
|
if (failCount >= 2) {
|
||||||
|
recognizeCallback.onRecognized(null, LivenessInfo.UNKNOWN, false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
retryRecognizeDelayed(trackId);
|
retryRecognizeDelayed(trackId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
compareResult.setTrackId(trackId);
|
compareResult.setTrackId(trackId);
|
||||||
boolean pass = compareResult.getSimilar() > recognizeConfiguration.getSimilarThreshold();
|
boolean pass = compareResult.getSimilar() > recognizeConfiguration.getSimilarThreshold();
|
||||||
|
compareResult.setSimilarPass(pass);
|
||||||
|
Log.d(TAG, "collectFace,searchFace: pass="+pass+",similar="+compareResult.getSimilar()+",threshold="+recognizeConfiguration.getSimilarThreshold());
|
||||||
recognizeCallback.onRecognized(compareResult, getRecognizeInfo(recognizeInfoMap, trackId).getLiveness(), pass);
|
recognizeCallback.onRecognized(compareResult, getRecognizeInfo(recognizeInfoMap, trackId).getLiveness(), pass);
|
||||||
if (pass) {
|
if (pass) {
|
||||||
setName(trackId, "识别通过");
|
setName(trackId, "识别通过");
|
||||||
noticeCurrentStatus("识别通过");
|
noticeCurrentStatus("识别通过");
|
||||||
changeRecognizeStatus(trackId, RequestFeatureStatus.SUCCEED);
|
changeRecognizeStatus(trackId, RequestFeatureStatus.SUCCEED);
|
||||||
} else {
|
} else {
|
||||||
noticeCurrentStatus("未通过:NOT_REGISTERED");
|
noticeCurrentStatus("未通过:NOT_REGISTERED,"+compareResult.getSimilar());
|
||||||
retryRecognizeDelayed(trackId);
|
retryRecognizeDelayed(trackId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -847,18 +878,7 @@ public class FaceHelper implements FaceListener {
|
|||||||
int fdCode = flEngine.detectFaces(nv21Data, width, height, format, faceInfoList);
|
int fdCode = flEngine.detectFaces(nv21Data, width, height, format, faceInfoList);
|
||||||
boolean isFaceExists = isFaceExists(faceInfoList, faceInfo);
|
boolean isFaceExists = isFaceExists(faceInfoList, faceInfo);
|
||||||
if (fdCode == ErrorInfo.MOK && isFaceExists) {
|
if (fdCode == ErrorInfo.MOK && isFaceExists) {
|
||||||
if (needUpdateFaceData) {
|
flCode = flEngine.processIr(nv21Data, width, height, format, Arrays.asList(faceInfo), FaceEngine.ASF_IR_LIVENESS);
|
||||||
/*
|
|
||||||
* 若IR人脸框有偏移,则需要对IR的人脸数据进行updateFaceData处理,再将处理后的FaceInfo信息传输给活体检测接口
|
|
||||||
*/
|
|
||||||
flCode = flEngine.updateFaceData(nv21Data, previewSize.width, previewSize.height, FaceEngine.CP_PAF_NV21,
|
|
||||||
new ArrayList<>(Collections.singletonList(faceInfo)));
|
|
||||||
if (flCode == ErrorInfo.MOK) {
|
|
||||||
flCode = flEngine.processIr(nv21Data, width, height, format, Arrays.asList(faceInfo), FaceEngine.ASF_IR_LIVENESS);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
flCode = flEngine.processIr(nv21Data, width, height, format, Arrays.asList(faceInfo), FaceEngine.ASF_IR_LIVENESS);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
onFail(new Exception("ir detectFaces failed fdCode:" + fdCode + ",isFaceExists:" + isFaceExists));
|
onFail(new Exception("ir detectFaces failed fdCode:" + fdCode + ",isFaceExists:" + isFaceExists));
|
||||||
}
|
}
|
||||||
@@ -1065,9 +1085,9 @@ public class FaceHelper implements FaceListener {
|
|||||||
private FaceEngine ftEngine;
|
private FaceEngine ftEngine;
|
||||||
private FaceEngine frEngine;
|
private FaceEngine frEngine;
|
||||||
private FaceEngine flEngine;
|
private FaceEngine flEngine;
|
||||||
|
private FaceEngine maskEngine;
|
||||||
private Camera.Size previewSize;
|
private Camera.Size previewSize;
|
||||||
private boolean onlyDetectLiveness;
|
private boolean onlyDetectLiveness;
|
||||||
private boolean needUpdateFaceData;
|
|
||||||
private RecognizeConfiguration recognizeConfiguration;
|
private RecognizeConfiguration recognizeConfiguration;
|
||||||
private RecognizeCallback recognizeCallback;
|
private RecognizeCallback recognizeCallback;
|
||||||
private IDualCameraFaceInfoTransformer dualCameraFaceInfoTransformer;
|
private IDualCameraFaceInfoTransformer dualCameraFaceInfoTransformer;
|
||||||
@@ -1108,6 +1128,11 @@ public class FaceHelper implements FaceListener {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Builder maskEngine(FaceEngine val) {
|
||||||
|
maskEngine = val;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public Builder previewSize(Camera.Size val) {
|
public Builder previewSize(Camera.Size val) {
|
||||||
previewSize = val;
|
previewSize = val;
|
||||||
return this;
|
return this;
|
||||||
@@ -1133,11 +1158,6 @@ public class FaceHelper implements FaceListener {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder needUpdateFaceData(boolean val) {
|
|
||||||
needUpdateFaceData = val;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public FaceHelper build() {
|
public FaceHelper build() {
|
||||||
return new FaceHelper(this);
|
return new FaceHelper(this);
|
||||||
}
|
}
|
||||||
|
|||||||
-1
@@ -2,7 +2,6 @@ package com.sw.plate.utils.arcface.face.constants;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 人脸识别中可能出现的状态
|
* 人脸识别中可能出现的状态
|
||||||
*
|
|
||||||
* @author
|
* @author
|
||||||
*/
|
*/
|
||||||
public @interface RequestFeatureStatus {
|
public @interface RequestFeatureStatus {
|
||||||
|
|||||||
@@ -10,6 +10,16 @@ public class CompareResult {
|
|||||||
private int compareCode;
|
private int compareCode;
|
||||||
private long cost;
|
private long cost;
|
||||||
|
|
||||||
|
private boolean similarPass;
|
||||||
|
|
||||||
|
public void setSimilarPass(boolean similarPass) {
|
||||||
|
this.similarPass = similarPass;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSimilarPass() {
|
||||||
|
return similarPass;
|
||||||
|
}
|
||||||
|
|
||||||
public CompareResult(FaceEntity faceEntity, float similar) {
|
public CompareResult(FaceEntity faceEntity, float similar) {
|
||||||
this.faceEntity = faceEntity;
|
this.faceEntity = faceEntity;
|
||||||
this.similar = similar;
|
this.similar = similar;
|
||||||
|
|||||||
-1
@@ -169,7 +169,6 @@ public class RecognizeConfiguration {
|
|||||||
this.enableImageQuality = val;
|
this.enableImageQuality = val;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder enableFaceAreaLimit(boolean val) {
|
public Builder enableFaceAreaLimit(boolean val) {
|
||||||
this.enableFaceAreaLimit = val;
|
this.enableFaceAreaLimit = val;
|
||||||
return this;
|
return this;
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import androidx.room.RoomDatabase;
|
|||||||
import com.sw.plate.utils.arcface.facedb.dao.FaceDao;
|
import com.sw.plate.utils.arcface.facedb.dao.FaceDao;
|
||||||
import com.sw.plate.utils.arcface.facedb.entity.FaceEntity;
|
import com.sw.plate.utils.arcface.facedb.entity.FaceEntity;
|
||||||
|
|
||||||
@Database(entities = {FaceEntity.class}, version = 1, exportSchema = false)
|
@Database(entities = {FaceEntity.class}, version = 2, exportSchema = false)
|
||||||
public abstract class FaceDatabase extends RoomDatabase {
|
public abstract class FaceDatabase extends RoomDatabase {
|
||||||
public abstract FaceDao faceDao();
|
public abstract FaceDao faceDao();
|
||||||
|
|
||||||
@@ -22,7 +22,10 @@ public abstract class FaceDatabase extends RoomDatabase {
|
|||||||
faceDatabase = Room.databaseBuilder(context, FaceDatabase.class,
|
faceDatabase = Room.databaseBuilder(context, FaceDatabase.class,
|
||||||
context.getDatabasePath("faceDB.db").getPath()
|
context.getDatabasePath("faceDB.db").getPath()
|
||||||
// context.getExternalFilesDir("database") + File.separator + "faceDB.db"
|
// context.getExternalFilesDir("database") + File.separator + "faceDB.db"
|
||||||
).build();
|
)
|
||||||
|
// 5.0 特征模型不兼容,升级时直接删除旧表重建
|
||||||
|
.fallbackToDestructiveMigration()
|
||||||
|
.build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ public interface FaceDao {
|
|||||||
Long insert(FaceEntity faceEntity);
|
Long insert(FaceEntity faceEntity);
|
||||||
|
|
||||||
@Insert(onConflict = OnConflictStrategy.IGNORE)
|
@Insert(onConflict = OnConflictStrategy.IGNORE)
|
||||||
List<Long> insert(List<FaceEntity> items);
|
void insert(List<FaceEntity> items);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取已注册的人脸数
|
* 获取已注册的人脸数
|
||||||
@@ -91,4 +91,26 @@ public interface FaceDao {
|
|||||||
|
|
||||||
@Query("SELECT * FROM face WHERE user_name = :userName limit 1")
|
@Query("SELECT * FROM face WHERE user_name = :userName limit 1")
|
||||||
FaceEntity queryByUserName(String userName);
|
FaceEntity queryByUserName(String userName);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询指定用户的所有人脸记录(同一用户可能存有多条特征数据)
|
||||||
|
*
|
||||||
|
* @param userName 用户ID
|
||||||
|
* @return 该用户所有人脸记录列表
|
||||||
|
*/
|
||||||
|
@Query("SELECT * FROM face WHERE user_name = :userName")
|
||||||
|
List<FaceEntity> queryAllByUserName(String userName);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return 删除临时用户人脸
|
||||||
|
*/
|
||||||
|
@Query("DELETE from face WHERE user_type = :userType")
|
||||||
|
int deleteUserFaceData(int userType);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询用户人脸数
|
||||||
|
* @param userType 1-会员,2-临时用户
|
||||||
|
*/
|
||||||
|
@Query("SELECT COUNT(1) FROM face WHERE user_type = :userType")
|
||||||
|
int getFaceCountByUserType(int userType);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,12 +52,39 @@ public class FaceEntity implements Parcelable {
|
|||||||
/**
|
/**
|
||||||
* 会员编号
|
* 会员编号
|
||||||
*/
|
*/
|
||||||
@ColumnInfo(name = "cardNo")
|
@ColumnInfo(name = "card_no")
|
||||||
private String cardNo;
|
private String cardNo;
|
||||||
|
|
||||||
@Ignore
|
@Ignore
|
||||||
private int trackId;//人脸追踪ID
|
private int trackId;//人脸追踪ID
|
||||||
|
|
||||||
|
@ColumnInfo(name = "user_idd")
|
||||||
|
private String userId;
|
||||||
|
@ColumnInfo(name = "user_face_id")
|
||||||
|
private String userFaceId;
|
||||||
|
@ColumnInfo(name = "member")
|
||||||
|
private boolean member;
|
||||||
|
@ColumnInfo(name = "face_update_timestamp")
|
||||||
|
private long faceUpdateTimestamp;
|
||||||
|
|
||||||
|
public FaceEntity() {
|
||||||
|
registerTime = System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Ignore
|
||||||
|
public FaceEntity(String userName, byte[] featureData, String userType, String cardNo, String userId, String userFaceId, boolean member, long faceUpdateTimestamp) {
|
||||||
|
this.userName = userName;
|
||||||
|
this.featureData = featureData;
|
||||||
|
this.userType = userType;
|
||||||
|
this.cardNo = cardNo;
|
||||||
|
this.userId = userId;
|
||||||
|
this.userFaceId = userFaceId;
|
||||||
|
this.member = member;
|
||||||
|
this.faceUpdateTimestamp = faceUpdateTimestamp;
|
||||||
|
registerTime = System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Ignore
|
||||||
public FaceEntity(String userName, String imagePath, byte[] featureData) {
|
public FaceEntity(String userName, String imagePath, byte[] featureData) {
|
||||||
this.userName = userName;
|
this.userName = userName;
|
||||||
this.imagePath = imagePath;
|
this.imagePath = imagePath;
|
||||||
@@ -65,6 +92,7 @@ public class FaceEntity implements Parcelable {
|
|||||||
registerTime = System.currentTimeMillis();
|
registerTime = System.currentTimeMillis();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Ignore
|
||||||
public FaceEntity(FaceEntity faceEntity) {
|
public FaceEntity(FaceEntity faceEntity) {
|
||||||
this.faceId = faceEntity.faceId;
|
this.faceId = faceEntity.faceId;
|
||||||
this.userName = faceEntity.userName;
|
this.userName = faceEntity.userName;
|
||||||
@@ -73,9 +101,14 @@ public class FaceEntity implements Parcelable {
|
|||||||
this.registerTime = faceEntity.registerTime;
|
this.registerTime = faceEntity.registerTime;
|
||||||
this.userType = faceEntity.getUserType();
|
this.userType = faceEntity.getUserType();
|
||||||
this.cardNo = faceEntity.getCardNo();
|
this.cardNo = faceEntity.getCardNo();
|
||||||
|
this.userId = faceEntity.getUserId();
|
||||||
|
this.userFaceId = faceEntity.getUserFaceId();
|
||||||
|
this.member = faceEntity.isMember();
|
||||||
|
this.faceUpdateTimestamp = faceEntity.getFaceUpdateTimestamp();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Ignore
|
||||||
protected FaceEntity(Parcel in) {
|
protected FaceEntity(Parcel in) {
|
||||||
faceId = in.readLong();
|
faceId = in.readLong();
|
||||||
registerTime = in.readLong();
|
registerTime = in.readLong();
|
||||||
@@ -84,6 +117,10 @@ public class FaceEntity implements Parcelable {
|
|||||||
featureData = in.createByteArray();
|
featureData = in.createByteArray();
|
||||||
userType = in.readString();
|
userType = in.readString();
|
||||||
cardNo = in.readString();
|
cardNo = in.readString();
|
||||||
|
userId = in.readString();
|
||||||
|
userFaceId = in.readString();
|
||||||
|
member = in.readByte() != 0;
|
||||||
|
faceUpdateTimestamp = in.readLong();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final Creator<FaceEntity> CREATOR = new Creator<FaceEntity>() {
|
public static final Creator<FaceEntity> CREATOR = new Creator<FaceEntity>() {
|
||||||
@@ -162,7 +199,37 @@ public class FaceEntity implements Parcelable {
|
|||||||
this.cardNo = cardNo;
|
this.cardNo = cardNo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getUserId() {
|
||||||
|
return userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUserId(String userId) {
|
||||||
|
this.userId = userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUserFaceId() {
|
||||||
|
return userFaceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUserFaceId(String userFaceId) {
|
||||||
|
this.userFaceId = userFaceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isMember() {
|
||||||
|
return member;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMember(boolean member) {
|
||||||
|
this.member = member;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getFaceUpdateTimestamp() {
|
||||||
|
return faceUpdateTimestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFaceUpdateTimestamp(long faceUpdateTimestamp) {
|
||||||
|
this.faceUpdateTimestamp = faceUpdateTimestamp;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int describeContents() {
|
public int describeContents() {
|
||||||
@@ -178,6 +245,10 @@ public class FaceEntity implements Parcelable {
|
|||||||
dest.writeByteArray(featureData);
|
dest.writeByteArray(featureData);
|
||||||
dest.writeString(userType);
|
dest.writeString(userType);
|
||||||
dest.writeString(cardNo);
|
dest.writeString(cardNo);
|
||||||
|
dest.writeString(userId);
|
||||||
|
dest.writeString(userFaceId);
|
||||||
|
dest.writeByte((byte) (member ? 1 : 0));
|
||||||
|
dest.writeLong(faceUpdateTimestamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -195,12 +266,16 @@ public class FaceEntity implements Parcelable {
|
|||||||
TextUtils.equals(this.imagePath, that.imagePath) &&
|
TextUtils.equals(this.imagePath, that.imagePath) &&
|
||||||
Arrays.equals(featureData, that.featureData) &&
|
Arrays.equals(featureData, that.featureData) &&
|
||||||
TextUtils.equals(this.userType, that.userType) &&
|
TextUtils.equals(this.userType, that.userType) &&
|
||||||
TextUtils.equals(this.cardNo, that.cardNo);
|
TextUtils.equals(this.cardNo, that.cardNo) &&
|
||||||
|
TextUtils.equals(this.userId, that.userId) &&
|
||||||
|
TextUtils.equals(this.userFaceId, that.userFaceId) &&
|
||||||
|
this.member == that.member &&
|
||||||
|
this.faceUpdateTimestamp == that.faceUpdateTimestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
int result = Objects.hash(faceId, registerTime, userName, imagePath, userType, cardNo);
|
int result = Objects.hash(faceId, registerTime, userName, imagePath, userType, cardNo, userId, userFaceId, member, faceUpdateTimestamp);
|
||||||
result = 31 * result + Arrays.hashCode(featureData);
|
result = 31 * result + Arrays.hashCode(featureData);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ import java.io.File;
|
|||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -60,6 +61,10 @@ public class FaceServer {
|
|||||||
faceRegisterInfoList = new ArrayList<>();
|
faceRegisterInfoList = new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public FaceEngine getFaceEngine() {
|
||||||
|
return faceEngine;
|
||||||
|
}
|
||||||
|
|
||||||
public static FaceServer getInstance() {
|
public static FaceServer getInstance() {
|
||||||
if (faceServer == null) {
|
if (faceServer == null) {
|
||||||
synchronized (FaceServer.class) {
|
synchronized (FaceServer.class) {
|
||||||
@@ -123,16 +128,16 @@ public class FaceServer {
|
|||||||
*/
|
*/
|
||||||
public void initFaceList(final Context context, FaceEngine faceEngine, final OnInitFinishedCallback onInitFinishedCallback, boolean recognize) {
|
public void initFaceList(final Context context, FaceEngine faceEngine, final OnInitFinishedCallback onInitFinishedCallback, boolean recognize) {
|
||||||
Disposable disposable = Observable.create((ObservableOnSubscribe<Integer>) emitter -> {
|
Disposable disposable = Observable.create((ObservableOnSubscribe<Integer>) emitter -> {
|
||||||
if (recognize) {
|
if (recognize) {
|
||||||
List<FaceEntity> faceEntityList = FaceDatabase.getInstance(context).faceDao().getAllFaces();
|
List<FaceEntity> faceEntityList = FaceDatabase.getInstance(context).faceDao().getAllFaces();
|
||||||
registerFaceFeatureInfoListFromDb(faceEngine, faceEntityList);
|
registerFaceFeatureInfoListFromDb(faceEngine, faceEntityList);
|
||||||
emitter.onNext(faceEntityList.size());
|
emitter.onNext(faceEntityList.size());
|
||||||
} else {
|
} else {
|
||||||
faceRegisterInfoList = FaceDatabase.getInstance(context).faceDao().getAllFaces();
|
faceRegisterInfoList = FaceDatabase.getInstance(context).faceDao().getAllFaces();
|
||||||
emitter.onNext(faceRegisterInfoList == null ? 0 : faceRegisterInfoList.size());
|
emitter.onNext(faceRegisterInfoList == null ? 0 : faceRegisterInfoList.size());
|
||||||
}
|
}
|
||||||
emitter.onComplete();
|
emitter.onComplete();
|
||||||
}).subscribeOn(Schedulers.io())
|
}).subscribeOn(Schedulers.io())
|
||||||
.unsubscribeOn(Schedulers.io())
|
.unsubscribeOn(Schedulers.io())
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.subscribe(size -> {
|
.subscribe(size -> {
|
||||||
@@ -164,6 +169,7 @@ public class FaceServer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@SuppressLint("CheckResult")
|
@SuppressLint("CheckResult")
|
||||||
public synchronized int clearAllFaces() {
|
public synchronized int clearAllFaces() {
|
||||||
if (faceRegisterInfoList != null) {
|
if (faceRegisterInfoList != null) {
|
||||||
@@ -340,6 +346,7 @@ public class FaceServer {
|
|||||||
*/
|
*/
|
||||||
public void registerFaceFeatureInfoFromDb(FaceEntity faceEntity, FaceEngine faceEngine) {
|
public void registerFaceFeatureInfoFromDb(FaceEntity faceEntity, FaceEngine faceEngine) {
|
||||||
if (faceEntity != null && faceEngine != null) {
|
if (faceEntity != null && faceEngine != null) {
|
||||||
|
Log.i(TAG, "registerFaceFeature:" + faceEntity.getFaceId()+"==="+ Arrays.toString(faceEntity.getFeatureData()));
|
||||||
FaceFeatureInfo faceFeatureInfo = new FaceFeatureInfo((int) faceEntity.getFaceId(), faceEntity.getFeatureData());
|
FaceFeatureInfo faceFeatureInfo = new FaceFeatureInfo((int) faceEntity.getFaceId(), faceEntity.getFeatureData());
|
||||||
int res = faceEngine.registerFaceFeature(faceFeatureInfo);
|
int res = faceEngine.registerFaceFeature(faceFeatureInfo);
|
||||||
Log.i(TAG, "registerFaceFeature:" + res);
|
Log.i(TAG, "registerFaceFeature:" + res);
|
||||||
|
|||||||
+50
-37
@@ -12,7 +12,6 @@ import androidx.lifecycle.ViewModel;
|
|||||||
|
|
||||||
import com.arcsoft.face.AgeInfo;
|
import com.arcsoft.face.AgeInfo;
|
||||||
import com.arcsoft.face.ErrorInfo;
|
import com.arcsoft.face.ErrorInfo;
|
||||||
import com.arcsoft.face.FaceAttributeParam;
|
|
||||||
import com.arcsoft.face.FaceEngine;
|
import com.arcsoft.face.FaceEngine;
|
||||||
import com.arcsoft.face.FaceInfo;
|
import com.arcsoft.face.FaceInfo;
|
||||||
import com.arcsoft.face.GenderInfo;
|
import com.arcsoft.face.GenderInfo;
|
||||||
@@ -132,13 +131,14 @@ public class RecognizeViewModel extends ViewModel implements RecognizeCallback {
|
|||||||
private MutableLiveData<Integer> ftInitCode = new MutableLiveData<>();
|
private MutableLiveData<Integer> ftInitCode = new MutableLiveData<>();
|
||||||
private MutableLiveData<Integer> frInitCode = new MutableLiveData<>();
|
private MutableLiveData<Integer> frInitCode = new MutableLiveData<>();
|
||||||
private MutableLiveData<Integer> flInitCode = new MutableLiveData<>();
|
private MutableLiveData<Integer> flInitCode = new MutableLiveData<>();
|
||||||
|
private MutableLiveData<Integer> maskInitCode = new MutableLiveData<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 人脸操作辅助类,推帧即可,内部会进行特征提取、识别
|
* 人脸操作辅助类,推帧即可,内部会进行特征提取、识别
|
||||||
*/
|
*/
|
||||||
private FaceHelper faceHelper;
|
private FaceHelper faceHelper;
|
||||||
/**
|
/**
|
||||||
* VIDEO模式人脸检测引擎,用于预览帧人脸追踪及图像质量检测
|
* VIDEO模式人脸检测引擎,用于预览帧人脸追踪
|
||||||
*/
|
*/
|
||||||
private FaceEngine ftEngine;
|
private FaceEngine ftEngine;
|
||||||
/**
|
/**
|
||||||
@@ -149,6 +149,10 @@ public class RecognizeViewModel extends ViewModel implements RecognizeCallback {
|
|||||||
* IMAGE模式活体检测引擎,用于预览帧人脸活体检测
|
* IMAGE模式活体检测引擎,用于预览帧人脸活体检测
|
||||||
*/
|
*/
|
||||||
private FaceEngine flEngine;
|
private FaceEngine flEngine;
|
||||||
|
/**
|
||||||
|
* IMAGE模式口罩检测引擎(5.0 独立引擎)
|
||||||
|
*/
|
||||||
|
private FaceEngine maskEngine;
|
||||||
|
|
||||||
private PreviewConfig previewConfig;
|
private PreviewConfig previewConfig;
|
||||||
|
|
||||||
@@ -160,10 +164,6 @@ public class RecognizeViewModel extends ViewModel implements RecognizeCallback {
|
|||||||
|
|
||||||
private MutableLiveData<CompareResult> recognizeUserId = new MutableLiveData<>();
|
private MutableLiveData<CompareResult> recognizeUserId = new MutableLiveData<>();
|
||||||
|
|
||||||
/**
|
|
||||||
* 检测ir活体前,是否需要更新faceData
|
|
||||||
*/
|
|
||||||
private boolean needUpdateFaceData;
|
|
||||||
/**
|
/**
|
||||||
* 当前活体检测的检测类型
|
* 当前活体检测的检测类型
|
||||||
*/
|
*/
|
||||||
@@ -270,6 +270,7 @@ public class RecognizeViewModel extends ViewModel implements RecognizeCallback {
|
|||||||
*/
|
*/
|
||||||
public void init(PreviewConfig previewConfig1) {
|
public void init(PreviewConfig previewConfig1) {
|
||||||
Context context = App.getContext();
|
Context context = App.getContext();
|
||||||
|
// 档口机业务:支持外部传入 PreviewConfig(指定 RGB/IR 摄像头 id 及旋转角度),未传时走默认配置
|
||||||
if (previewConfig1 != null) {
|
if (previewConfig1 != null) {
|
||||||
previewConfig = previewConfig1;
|
previewConfig = previewConfig1;
|
||||||
} else {
|
} else {
|
||||||
@@ -281,9 +282,11 @@ public class RecognizeViewModel extends ViewModel implements RecognizeCallback {
|
|||||||
Integer.parseInt(ConfigUtil.getIrCameraAdditionalRotation(context))
|
Integer.parseInt(ConfigUtil.getIrCameraAdditionalRotation(context))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 填入在设置界面设置好的配置信息
|
// 填入在设置界面设置好的配置信息
|
||||||
boolean enableLive = !ConfigUtil.getLivenessDetectType(context).equals(context.getString(R.string.value_liveness_type_disable));
|
boolean enableLive = !ConfigUtil.getLivenessDetectType(context).equals(context.getString(R.string.value_liveness_type_disable));
|
||||||
enableLive = false;
|
enableLive = false;
|
||||||
|
|
||||||
boolean enableFaceQualityDetect = ConfigUtil.isEnableImageQualityDetect(context);
|
boolean enableFaceQualityDetect = ConfigUtil.isEnableImageQualityDetect(context);
|
||||||
boolean enableFaceMoveLimit = ConfigUtil.isEnableFaceMoveLimit(context);
|
boolean enableFaceMoveLimit = ConfigUtil.isEnableFaceMoveLimit(context);
|
||||||
boolean enableFaceSizeLimit = ConfigUtil.isEnableFaceSizeLimit(context);
|
boolean enableFaceSizeLimit = ConfigUtil.isEnableFaceSizeLimit(context);
|
||||||
@@ -299,22 +302,22 @@ public class RecognizeViewModel extends ViewModel implements RecognizeCallback {
|
|||||||
.similarThreshold(ConfigUtil.getRecognizeThreshold(context))
|
.similarThreshold(ConfigUtil.getRecognizeThreshold(context))
|
||||||
.imageQualityNoMaskRecognizeThreshold(ConfigUtil.getImageQualityNoMaskRecognizeThreshold(context))
|
.imageQualityNoMaskRecognizeThreshold(ConfigUtil.getImageQualityNoMaskRecognizeThreshold(context))
|
||||||
.imageQualityMaskRecognizeThreshold(ConfigUtil.getImageQualityMaskRecognizeThreshold(context))
|
.imageQualityMaskRecognizeThreshold(ConfigUtil.getImageQualityMaskRecognizeThreshold(context))
|
||||||
.livenessParam(new LivenessParam(ConfigUtil.getRgbLivenessThreshold(context), ConfigUtil.getIrLivenessThreshold(context),
|
.livenessParam(new LivenessParam(ConfigUtil.getRgbLivenessThreshold(context), ConfigUtil.getIrLivenessThreshold(context)))
|
||||||
ConfigUtil.getLivenessFqThreshold(context)))
|
|
||||||
.build();
|
.build();
|
||||||
int cameraOffsetX = ConfigUtil.getDualCameraHorizontalOffset(context);
|
|
||||||
int cameraOffsetY = ConfigUtil.getDualCameraVerticalOffset(context);
|
|
||||||
needUpdateFaceData = (livenessType == LivenessType.IR && (cameraOffsetX != 0 || cameraOffsetY != 0));
|
|
||||||
|
|
||||||
|
// 人脸追踪引擎(VIDEO模式,仅检测)
|
||||||
ftEngine = new FaceEngine();
|
ftEngine = new FaceEngine();
|
||||||
int ftEngineMask = FaceEngine.ASF_FACE_DETECT | FaceEngine.ASF_MASK_DETECT;
|
int ftEngineMask = FaceEngine.ASF_FACE_DETECT;
|
||||||
ftInitCode.postValue(ftEngine.init(context, DetectMode.ASF_DETECT_MODE_VIDEO, ConfigUtil.getFtOrient(context),
|
ftInitCode.postValue(ftEngine.init(context, DetectMode.ASF_DETECT_MODE_VIDEO, ConfigUtil.getFtOrient(context),
|
||||||
ConfigUtil.getRecognizeMaxDetectFaceNum(context), ftEngineMask));
|
ConfigUtil.getRecognizeMaxDetectFaceNum(context), ftEngineMask));
|
||||||
FaceAttributeParam attributeParam = new FaceAttributeParam(
|
|
||||||
ConfigUtil.getRecognizeEyeOpenThreshold(context), ConfigUtil.getRecognizeMouthCloseThreshold(context),
|
|
||||||
ConfigUtil.getRecognizeWearGlassesThreshold(context));
|
|
||||||
ftEngine.setFaceAttributeParam(attributeParam);
|
|
||||||
|
|
||||||
|
// 口罩检测引擎(5.0 独立引擎)
|
||||||
|
maskEngine = new FaceEngine();
|
||||||
|
maskInitCode.postValue(maskEngine.init(context, DetectMode.ASF_DETECT_MODE_IMAGE,
|
||||||
|
DetectFaceOrientPriority.ASF_OP_ALL_OUT,
|
||||||
|
ConfigUtil.getRecognizeMaxDetectFaceNum(context), FaceEngine.ASF_MASK_DETECT));
|
||||||
|
|
||||||
|
// 特征提取引擎
|
||||||
frEngine = new FaceEngine();
|
frEngine = new FaceEngine();
|
||||||
int frEngineMask = FaceEngine.ASF_FACE_RECOGNITION;
|
int frEngineMask = FaceEngine.ASF_FACE_RECOGNITION;
|
||||||
if (enableFaceQualityDetect) {
|
if (enableFaceQualityDetect) {
|
||||||
@@ -324,16 +327,13 @@ public class RecognizeViewModel extends ViewModel implements RecognizeCallback {
|
|||||||
10, frEngineMask));
|
10, frEngineMask));
|
||||||
FaceServer.getInstance().initFaceList(context, frEngine, faceCount -> loadFaceList = true, true);
|
FaceServer.getInstance().initFaceList(context, frEngine, faceCount -> loadFaceList = true, true);
|
||||||
|
|
||||||
//启用活体检测时,才初始化活体引擎
|
// 启用活体检测时,才初始化活体引擎
|
||||||
if (enableLive) {
|
if (enableLive) {
|
||||||
flEngine = new FaceEngine();
|
flEngine = new FaceEngine();
|
||||||
int flEngineMask = (livenessType == LivenessType.RGB ? FaceEngine.ASF_LIVENESS : (FaceEngine.ASF_IR_LIVENESS | FaceEngine.ASF_FACE_DETECT));
|
int flEngineMask = (livenessType == LivenessType.RGB ? FaceEngine.ASF_LIVENESS : (FaceEngine.ASF_IR_LIVENESS | FaceEngine.ASF_FACE_DETECT));
|
||||||
if (needUpdateFaceData) {
|
|
||||||
flEngineMask |= FaceEngine.ASF_UPDATE_FACEDATA;
|
|
||||||
}
|
|
||||||
flInitCode.postValue(flEngine.init(context, DetectMode.ASF_DETECT_MODE_IMAGE,
|
flInitCode.postValue(flEngine.init(context, DetectMode.ASF_DETECT_MODE_IMAGE,
|
||||||
DetectFaceOrientPriority.ASF_OP_ALL_OUT, 10, flEngineMask));
|
DetectFaceOrientPriority.ASF_OP_ALL_OUT, 10, flEngineMask));
|
||||||
LivenessParam livenessParam = new LivenessParam(ConfigUtil.getRgbLivenessThreshold(context), ConfigUtil.getIrLivenessThreshold(context), ConfigUtil.getLivenessFqThreshold(context));
|
LivenessParam livenessParam = new LivenessParam(ConfigUtil.getRgbLivenessThreshold(context), ConfigUtil.getIrLivenessThreshold(context));
|
||||||
flEngine.setLivenessParam(livenessParam);
|
flEngine.setLivenessParam(livenessParam);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -341,8 +341,10 @@ public class RecognizeViewModel extends ViewModel implements RecognizeCallback {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void addFace(FaceEntity faceEntity) {
|
public void addFace(FaceEntity faceEntity) {
|
||||||
if (frEngine != null)
|
if (frEngine != null) {
|
||||||
|
Log.e("performSync", "addFace=" + faceEntity.getRegisterTime());
|
||||||
FaceServer.getInstance().registerFaceFeatureInfoFromDb(faceEntity, frEngine);
|
FaceServer.getInstance().registerFaceFeatureInfoFromDb(faceEntity, frEngine);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void refreshFaceList() {
|
public void refreshFaceList() {
|
||||||
@@ -356,19 +358,25 @@ public class RecognizeViewModel extends ViewModel implements RecognizeCallback {
|
|||||||
if (ftEngine != null) {
|
if (ftEngine != null) {
|
||||||
synchronized (ftEngine) {
|
synchronized (ftEngine) {
|
||||||
int ftUnInitCode = ftEngine.unInit();
|
int ftUnInitCode = ftEngine.unInit();
|
||||||
Log.i(TAG, "unInitEngine: " + ftUnInitCode);
|
Log.i(TAG, "unInitEngine ft: " + ftUnInitCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (maskEngine != null) {
|
||||||
|
synchronized (maskEngine) {
|
||||||
|
int maskUnInitCode = maskEngine.unInit();
|
||||||
|
Log.i(TAG, "unInitEngine mask: " + maskUnInitCode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (frEngine != null) {
|
if (frEngine != null) {
|
||||||
synchronized (frEngine) {
|
synchronized (frEngine) {
|
||||||
int frUnInitCode = frEngine.unInit();
|
int frUnInitCode = frEngine.unInit();
|
||||||
Log.i(TAG, "unInitEngine: " + frUnInitCode);
|
Log.i(TAG, "unInitEngine fr: " + frUnInitCode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (flEngine != null) {
|
if (flEngine != null) {
|
||||||
synchronized (flEngine) {
|
synchronized (flEngine) {
|
||||||
int flUnInitCode = flEngine.unInit();
|
int flUnInitCode = flEngine.unInit();
|
||||||
Log.i(TAG, "unInitEngine: " + flUnInitCode);
|
Log.i(TAG, "unInitEngine fl: " + flUnInitCode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -400,6 +408,7 @@ public class RecognizeViewModel extends ViewModel implements RecognizeCallback {
|
|||||||
/**
|
/**
|
||||||
* 重置人脸识别状态:清空上次识别结果及 FaceHelper 内部的 recognizeInfoMap,
|
* 重置人脸识别状态:清空上次识别结果及 FaceHelper 内部的 recognizeInfoMap,
|
||||||
* 使下一帧进入时能重新触发识别流程。
|
* 使下一帧进入时能重新触发识别流程。
|
||||||
|
* 适用场景:短时间内再次识别、点击重试按钮等需要重新开始识别的时机。
|
||||||
*/
|
*/
|
||||||
public void resetFaceState() {
|
public void resetFaceState() {
|
||||||
// 清空粘性 LiveData,防止旧结果被重新投递给 observer
|
// 清空粘性 LiveData,防止旧结果被重新投递给 observer
|
||||||
@@ -468,7 +477,7 @@ public class RecognizeViewModel extends ViewModel implements RecognizeCallback {
|
|||||||
.ftEngine(ftEngine)
|
.ftEngine(ftEngine)
|
||||||
.frEngine(frEngine)
|
.frEngine(frEngine)
|
||||||
.flEngine(flEngine)
|
.flEngine(flEngine)
|
||||||
.needUpdateFaceData(needUpdateFaceData)
|
.maskEngine(maskEngine)
|
||||||
.frQueueSize(maxDetectFaceNum)
|
.frQueueSize(maxDetectFaceNum)
|
||||||
.flQueueSize(maxDetectFaceNum)
|
.flQueueSize(maxDetectFaceNum)
|
||||||
.previewSize(previewSize)
|
.previewSize(previewSize)
|
||||||
@@ -484,13 +493,20 @@ public class RecognizeViewModel extends ViewModel implements RecognizeCallback {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String getUserId(CompareResult result) {
|
||||||
|
if (result != null && result.getFaceEntity() != null) {
|
||||||
|
return result.getFaceEntity().getUserName();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onRecognized(CompareResult compareResult, Integer live, boolean similarPass) {
|
public void onRecognized(CompareResult compareResult, Integer live, boolean similarPass) {
|
||||||
Disposable disposable = Observable.just(true).observeOn(AndroidSchedulers.mainThread()).subscribe(aBoolean -> {
|
// TODO: 2026/1/21 测试使用 Observable.just(similarPass)代替Observable.just(true)---
|
||||||
|
Observable.just(similarPass).observeOn(AndroidSchedulers.mainThread()).subscribe(aBoolean -> {
|
||||||
|
Log.d(TAG, "collectFace,onRecognized: similarPass=" + similarPass + ",live=" + live + ",userId=" + getUserId(compareResult));
|
||||||
if (similarPass) {
|
if (similarPass) {
|
||||||
if (recognizeUserId != null) {
|
recognizeUserId.postValue(compareResult);
|
||||||
recognizeUserId.postValue(compareResult);
|
|
||||||
}
|
|
||||||
boolean isAdded = false;
|
boolean isAdded = false;
|
||||||
List<CompareResult> compareResults = compareResultList.getValue();
|
List<CompareResult> compareResults = compareResultList.getValue();
|
||||||
if (compareResults != null && !compareResults.isEmpty()) {
|
if (compareResults != null && !compareResults.isEmpty()) {
|
||||||
@@ -512,6 +528,8 @@ public class RecognizeViewModel extends ViewModel implements RecognizeCallback {
|
|||||||
getFaceItemEventMutableLiveData().postValue(new FaceItemEvent(compareResults.size() - 1, EventType.INSERTED));
|
getFaceItemEventMutableLiveData().postValue(new FaceItemEvent(compareResults.size() - 1, EventType.INSERTED));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
recognizeUserId.postValue(compareResult);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -599,14 +617,9 @@ public class RecognizeViewModel extends ViewModel implements RecognizeCallback {
|
|||||||
if (recognizeStatus != null) {
|
if (recognizeStatus != null) {
|
||||||
if (recognizeStatus == RequestFeatureStatus.FAILED) {
|
if (recognizeStatus == RequestFeatureStatus.FAILED) {
|
||||||
color = RecognizeColor.COLOR_FAILED;
|
color = RecognizeColor.COLOR_FAILED;
|
||||||
}else if (recognizeStatus == RequestFeatureStatus.SUCCEED) {
|
}
|
||||||
|
if (recognizeStatus == RequestFeatureStatus.SUCCEED) {
|
||||||
color = RecognizeColor.COLOR_SUCCESS;
|
color = RecognizeColor.COLOR_SUCCESS;
|
||||||
} else if (recognizeStatus == RequestFeatureStatus.TO_RETRY) {
|
|
||||||
color = RecognizeColor.COLOR_UNKNOWN;
|
|
||||||
//需要重试
|
|
||||||
// FaceEntity faceEntity = new FaceEntity("2",null, null);
|
|
||||||
// CompareResult result = new CompareResult(faceEntity, 0.0f);
|
|
||||||
// recognizeUserId.postValue(result);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (liveness != null && liveness == LivenessInfo.NOT_ALIVE) {
|
if (liveness != null && liveness == LivenessInfo.NOT_ALIVE) {
|
||||||
|
|||||||
Reference in New Issue
Block a user