diff --git a/lib_face/libs/arcsoft_face.jar b/lib_face/libs/arcsoft_face.jar
index b2e827c..c95cd9d 100644
Binary files a/lib_face/libs/arcsoft_face.jar and b/lib_face/libs/arcsoft_face.jar differ
diff --git a/lib_face/libs/armeabi-v7a/libarcsoft_face.so b/lib_face/libs/armeabi-v7a/libarcsoft_face.so
index e37f157..4adb029 100644
Binary files a/lib_face/libs/armeabi-v7a/libarcsoft_face.so and b/lib_face/libs/armeabi-v7a/libarcsoft_face.so differ
diff --git a/lib_face/libs/armeabi-v7a/libarcsoft_face_engine.so b/lib_face/libs/armeabi-v7a/libarcsoft_face_engine.so
index 9a6e74c..4a0a59d 100644
Binary files a/lib_face/libs/armeabi-v7a/libarcsoft_face_engine.so and b/lib_face/libs/armeabi-v7a/libarcsoft_face_engine.so differ
diff --git a/lib_face/src/main/java/com/sw/plate/utils/arcface/ConfigUtil.java b/lib_face/src/main/java/com/sw/plate/utils/arcface/ConfigUtil.java
index a874581..df12861 100644
--- a/lib_face/src/main/java/com/sw/plate/utils/arcface/ConfigUtil.java
+++ b/lib_face/src/main/java/com/sw/plate/utils/arcface/ConfigUtil.java
@@ -88,14 +88,8 @@ public class ConfigUtil {
* 默认相机分辨率
*/
// 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 = "608x456";
-// private static final String DEFAULT_PREVIEW_SIZE = "1024x768";
-// private static final String DEFAULT_PREVIEW_SIZE = "800x600";
-// private static final String DEFAULT_PREVIEW_SIZE = "640x480";
-
+// private static final String DEFAULT_PREVIEW_SIZE = "400x640";
/**
* 获取String类型的preference
@@ -244,7 +238,7 @@ public class ConfigUtil {
/**
* TODO: 该Demo基于单人脸识别实现,若想使用多人脸识别,请将 return true 改成 return getBoolean,并修改相关配置项的preference.xml和业务代码
- *
+ *
* 获取识别界面是否保留最大人脸
*
* @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)));
}
- 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)));
}
diff --git a/lib_face/src/main/java/com/sw/plate/utils/arcface/ErrorCodeUtil.java b/lib_face/src/main/java/com/sw/plate/utils/arcface/ErrorCodeUtil.java
index c8ba88a..591d4cb 100644
--- a/lib_face/src/main/java/com/sw/plate/utils/arcface/ErrorCodeUtil.java
+++ b/lib_face/src/main/java/com/sw/plate/utils/arcface/ErrorCodeUtil.java
@@ -27,7 +27,6 @@ public class ErrorCodeUtil {
}
return "unknown error";
}
-
/**
* 将ArcSoftImageUtil错误码转换为对应的错误码常量名,便于理解
* TODO:目前每次都遍历,如果使用频繁,建议将Field缓存处理,避免每次都反射
diff --git a/lib_face/src/main/java/com/sw/plate/utils/arcface/FaceApi.java b/lib_face/src/main/java/com/sw/plate/utils/arcface/FaceApi.java
index 4b3753e..bb2351a 100644
--- a/lib_face/src/main/java/com/sw/plate/utils/arcface/FaceApi.java
+++ b/lib_face/src/main/java/com/sw/plate/utils/arcface/FaceApi.java
@@ -1,6 +1,7 @@
package com.sw.plate.utils.arcface;
import android.content.Context;
+import android.text.TextUtils;
import android.util.Log;
import com.arcsoft.face.FaceEngine;
@@ -31,25 +32,21 @@ public class FaceApi {
*/
public void updateFaceData(int index, List list) {
Log.d(TAG, "updateFaceData: index = " + index + ", listSize = " + list.size());
- FaceDao faceDao = FaceDatabase.getInstance(App.getContext()).faceDao();
+ FaceDao faceDao = getFaceDao();
+ // 档口机业务:首页(index==1)全量同步前清空旧特征库,避免特征重复堆积
if (index == 1) {
faceDao.deleteAll();
faceDao.resetId();
}
- List insertIdList = faceDao.insert(list);
- Log.d(TAG, "updateFaceData: count = " + insertIdList.size());
+ faceDao.insert(list);
List queryList = faceDao.getAllFaces();
Log.d(TAG, "updateFaceData: queryList.size = " + queryList.size());
}
- public void updateFaceData2(int index, List list) {
- Log.d(TAG, "updateFaceData: index = " + index + ", listSize = " + list.size());
- FaceDao faceDao = FaceDatabase.getInstance(App.getContext()).faceDao();
- if (index == 0) {
- faceDao.deleteAll();
- faceDao.resetId();
- }
- faceDao.insert(list);
+ public void clearFaceData() {
+ FaceDao faceDao = getFaceDao();
+ faceDao.deleteAll();
+ faceDao.resetId();
}
/**
@@ -92,4 +89,35 @@ public class FaceApi {
// List facePreviewInfoList = recognizeViewModel.onPreviewFrame(nv21, true);
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 queryAllByUserName(String userName) {
+ return getFaceDao().queryAllByUserName(userName);
+ }
+
+ public FaceDao getFaceDao() {
+ return FaceDatabase.getInstance(App.getContext()).faceDao();
+ }
+
}
diff --git a/lib_face/src/main/java/com/sw/plate/utils/arcface/FaceRectTransformer.java b/lib_face/src/main/java/com/sw/plate/utils/arcface/FaceRectTransformer.java
index 7b34137..58c9d5d 100644
--- a/lib_face/src/main/java/com/sw/plate/utils/arcface/FaceRectTransformer.java
+++ b/lib_face/src/main/java/com/sw/plate/utils/arcface/FaceRectTransformer.java
@@ -77,7 +77,7 @@ public class FaceRectTransformer {
rect.bottom *= verticalRatio;
Rect newRect = new Rect();
-// L.e("cameraDisplayOrientation " + cameraDisplayOrientation + " === " + cameraId);
+ L.e("cameraDisplayOrientation " + cameraDisplayOrientation + " === " + cameraId);
switch (cameraDisplayOrientation) {
case 0:
if (cameraId == Camera.CameraInfo.CAMERA_FACING_FRONT) {
diff --git a/lib_face/src/main/java/com/sw/plate/utils/arcface/FaceRectView.java b/lib_face/src/main/java/com/sw/plate/utils/arcface/FaceRectView.java
index c58ba6e..15c37d3 100644
--- a/lib_face/src/main/java/com/sw/plate/utils/arcface/FaceRectView.java
+++ b/lib_face/src/main/java/com/sw/plate/utils/arcface/FaceRectView.java
@@ -2,7 +2,6 @@ package com.sw.plate.utils.arcface;
import android.content.Context;
import android.graphics.Canvas;
-import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
@@ -47,6 +46,10 @@ public class FaceRectView extends View {
}
}
+ public int getRectColor() {
+ return paint.getColor();
+ }
+
public void clearFaceInfo() {
drawInfoList.clear();
postInvalidate();
@@ -216,7 +219,6 @@ public class FaceRectView extends View {
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(faceRectThickness);
paint.setColor(drawInfo.getColor());
-// paint.setColor(Color.parseColor("#FF0000"));
paint.setAntiAlias(true);
Path mPath = new Path();
diff --git a/lib_face/src/main/java/com/sw/plate/utils/arcface/FileUtil.java b/lib_face/src/main/java/com/sw/plate/utils/arcface/FileUtil.java
index e0efc7e..977389a 100644
--- a/lib_face/src/main/java/com/sw/plate/utils/arcface/FileUtil.java
+++ b/lib_face/src/main/java/com/sw/plate/utils/arcface/FileUtil.java
@@ -27,7 +27,7 @@ public class FileUtil {
}
public static boolean saveDataToFile(byte[] data, File file, boolean append) {
- if (data == null) {
+ if (data == null){
return false;
}
File parentFile = file.getParentFile();
diff --git a/lib_face/src/main/java/com/sw/plate/utils/arcface/face/FaceHelper.java b/lib_face/src/main/java/com/sw/plate/utils/arcface/face/FaceHelper.java
index 59f64eb..f23c24d 100644
--- a/lib_face/src/main/java/com/sw/plate/utils/arcface/face/FaceHelper.java
+++ b/lib_face/src/main/java/com/sw/plate/utils/arcface/face/FaceHelper.java
@@ -5,6 +5,7 @@ import android.hardware.Camera;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
+import android.widget.Toast;
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
@@ -18,6 +19,7 @@ import com.arcsoft.face.ImageQualitySimilar;
import com.arcsoft.face.LivenessInfo;
import com.arcsoft.face.MaskInfo;
import com.arcsoft.face.enums.ExtractType;
+import com.sw.plate.App;
import com.sw.plate.utils.L;
import com.sw.plate.utils.arcface.FaceRectTransformer;
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.util.ArrayList;
import java.util.Arrays;
-import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
@@ -102,6 +103,10 @@ public class FaceHelper implements FaceListener {
* 活体检测引擎
*/
private FaceEngine flEngine;
+ /**
+ * 口罩检测引擎(5.0 独立引擎)
+ */
+ private FaceEngine maskEngine;
private Camera.Size previewSize;
@@ -146,11 +151,6 @@ public class FaceHelper implements FaceListener {
*/
private boolean onlyDetectLiveness;
- /**
- * 是否需要更新faceData
- */
- private boolean needUpdateFaceData;
-
/**
* 识别的配置项
*/
@@ -180,9 +180,9 @@ public class FaceHelper implements FaceListener {
}
private FaceHelper(Builder builder) {
- needUpdateFaceData = builder.needUpdateFaceData;
onlyDetectLiveness = builder.onlyDetectLiveness;
ftEngine = builder.ftEngine;
+ maskEngine = builder.maskEngine;
trackedFaceCount = builder.trackedFaceCount;
previewSize = builder.previewSize;
frEngine = builder.frEngine;
@@ -350,11 +350,11 @@ public class FaceHelper implements FaceListener {
return facePreviewInfoList;
}
}
- if (!onlyDetectLiveness) {
- code = ftEngine.process(rgbNv21, previewSize.width, previewSize.height, FaceEngine.CP_PAF_NV21, faceInfoList,
+ if (!onlyDetectLiveness && maskEngine != null) {
+ code = maskEngine.process(rgbNv21, previewSize.width, previewSize.height, FaceEngine.CP_PAF_NV21, faceInfoList,
FaceEngine.ASF_MASK_DETECT);
if (code == ErrorInfo.MOK) {
- code = ftEngine.getMask(maskInfoList);
+ code = maskEngine.getMask(maskInfoList);
if (code != ErrorInfo.MOK) {
onFail(new Exception("process getMask failed,code is " + code));
return facePreviewInfoList;
@@ -430,7 +430,7 @@ public class FaceHelper implements FaceListener {
if (!contained) {
RecognizeInfo recognizeInfo = recognizeInfoMap.remove(key);
if (recognizeInfo != null) {
- recognizeCallback.onNoticeChanged("leave");
+ recognizeCallback.onNoticeChanged("");
// 人脸离开时,通知特征提取线程,避免一直等待活体结果
synchronized (recognizeInfo.getWaitLock()) {
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) {
CompareResult compareResult = FaceServer.getInstance().searchFaceFeature(faceFeature, frEngine);
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);
return;
}
compareResult.setTrackId(trackId);
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);
if (pass) {
setName(trackId, "识别通过");
noticeCurrentStatus("识别通过");
changeRecognizeStatus(trackId, RequestFeatureStatus.SUCCEED);
} else {
- noticeCurrentStatus("未通过:NOT_REGISTERED");
+ noticeCurrentStatus("未通过:NOT_REGISTERED,"+compareResult.getSimilar());
retryRecognizeDelayed(trackId);
}
}
@@ -847,18 +878,7 @@ public class FaceHelper implements FaceListener {
int fdCode = flEngine.detectFaces(nv21Data, width, height, format, faceInfoList);
boolean isFaceExists = isFaceExists(faceInfoList, faceInfo);
if (fdCode == ErrorInfo.MOK && isFaceExists) {
- if (needUpdateFaceData) {
- /*
- * 若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);
- }
+ flCode = flEngine.processIr(nv21Data, width, height, format, Arrays.asList(faceInfo), FaceEngine.ASF_IR_LIVENESS);
} else {
onFail(new Exception("ir detectFaces failed fdCode:" + fdCode + ",isFaceExists:" + isFaceExists));
}
@@ -1065,9 +1085,9 @@ public class FaceHelper implements FaceListener {
private FaceEngine ftEngine;
private FaceEngine frEngine;
private FaceEngine flEngine;
+ private FaceEngine maskEngine;
private Camera.Size previewSize;
private boolean onlyDetectLiveness;
- private boolean needUpdateFaceData;
private RecognizeConfiguration recognizeConfiguration;
private RecognizeCallback recognizeCallback;
private IDualCameraFaceInfoTransformer dualCameraFaceInfoTransformer;
@@ -1108,6 +1128,11 @@ public class FaceHelper implements FaceListener {
return this;
}
+ public Builder maskEngine(FaceEngine val) {
+ maskEngine = val;
+ return this;
+ }
+
public Builder previewSize(Camera.Size val) {
previewSize = val;
return this;
@@ -1133,11 +1158,6 @@ public class FaceHelper implements FaceListener {
return this;
}
- public Builder needUpdateFaceData(boolean val) {
- needUpdateFaceData = val;
- return this;
- }
-
public FaceHelper build() {
return new FaceHelper(this);
}
diff --git a/lib_face/src/main/java/com/sw/plate/utils/arcface/face/constants/RequestFeatureStatus.java b/lib_face/src/main/java/com/sw/plate/utils/arcface/face/constants/RequestFeatureStatus.java
index 08137d4..a63dc48 100644
--- a/lib_face/src/main/java/com/sw/plate/utils/arcface/face/constants/RequestFeatureStatus.java
+++ b/lib_face/src/main/java/com/sw/plate/utils/arcface/face/constants/RequestFeatureStatus.java
@@ -2,7 +2,6 @@ package com.sw.plate.utils.arcface.face.constants;
/**
* 人脸识别中可能出现的状态
- *
* @author
*/
public @interface RequestFeatureStatus {
diff --git a/lib_face/src/main/java/com/sw/plate/utils/arcface/face/model/CompareResult.java b/lib_face/src/main/java/com/sw/plate/utils/arcface/face/model/CompareResult.java
index 2d33f8b..f78d7c2 100644
--- a/lib_face/src/main/java/com/sw/plate/utils/arcface/face/model/CompareResult.java
+++ b/lib_face/src/main/java/com/sw/plate/utils/arcface/face/model/CompareResult.java
@@ -10,6 +10,16 @@ public class CompareResult {
private int compareCode;
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) {
this.faceEntity = faceEntity;
this.similar = similar;
diff --git a/lib_face/src/main/java/com/sw/plate/utils/arcface/face/model/RecognizeConfiguration.java b/lib_face/src/main/java/com/sw/plate/utils/arcface/face/model/RecognizeConfiguration.java
index ee22a9b..4382eca 100644
--- a/lib_face/src/main/java/com/sw/plate/utils/arcface/face/model/RecognizeConfiguration.java
+++ b/lib_face/src/main/java/com/sw/plate/utils/arcface/face/model/RecognizeConfiguration.java
@@ -169,7 +169,6 @@ public class RecognizeConfiguration {
this.enableImageQuality = val;
return this;
}
-
public Builder enableFaceAreaLimit(boolean val) {
this.enableFaceAreaLimit = val;
return this;
diff --git a/lib_face/src/main/java/com/sw/plate/utils/arcface/facedb/FaceDatabase.java b/lib_face/src/main/java/com/sw/plate/utils/arcface/facedb/FaceDatabase.java
index ddb985f..f8b5948 100644
--- a/lib_face/src/main/java/com/sw/plate/utils/arcface/facedb/FaceDatabase.java
+++ b/lib_face/src/main/java/com/sw/plate/utils/arcface/facedb/FaceDatabase.java
@@ -9,7 +9,7 @@ import androidx.room.RoomDatabase;
import com.sw.plate.utils.arcface.facedb.dao.FaceDao;
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 FaceDao faceDao();
@@ -22,7 +22,10 @@ public abstract class FaceDatabase extends RoomDatabase {
faceDatabase = Room.databaseBuilder(context, FaceDatabase.class,
context.getDatabasePath("faceDB.db").getPath()
// context.getExternalFilesDir("database") + File.separator + "faceDB.db"
- ).build();
+ )
+ // 5.0 特征模型不兼容,升级时直接删除旧表重建
+ .fallbackToDestructiveMigration()
+ .build();
}
}
}
diff --git a/lib_face/src/main/java/com/sw/plate/utils/arcface/facedb/dao/FaceDao.java b/lib_face/src/main/java/com/sw/plate/utils/arcface/facedb/dao/FaceDao.java
index 50091bf..2926276 100644
--- a/lib_face/src/main/java/com/sw/plate/utils/arcface/facedb/dao/FaceDao.java
+++ b/lib_face/src/main/java/com/sw/plate/utils/arcface/facedb/dao/FaceDao.java
@@ -73,7 +73,7 @@ public interface FaceDao {
Long insert(FaceEntity faceEntity);
@Insert(onConflict = OnConflictStrategy.IGNORE)
- List insert(List items);
+ void insert(List items);
/**
* 获取已注册的人脸数
@@ -91,4 +91,26 @@ public interface FaceDao {
@Query("SELECT * FROM face WHERE user_name = :userName limit 1")
FaceEntity queryByUserName(String userName);
+
+ /**
+ * 查询指定用户的所有人脸记录(同一用户可能存有多条特征数据)
+ *
+ * @param userName 用户ID
+ * @return 该用户所有人脸记录列表
+ */
+ @Query("SELECT * FROM face WHERE user_name = :userName")
+ List 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);
}
diff --git a/lib_face/src/main/java/com/sw/plate/utils/arcface/facedb/entity/FaceEntity.java b/lib_face/src/main/java/com/sw/plate/utils/arcface/facedb/entity/FaceEntity.java
index e0e4387..3dc95f5 100644
--- a/lib_face/src/main/java/com/sw/plate/utils/arcface/facedb/entity/FaceEntity.java
+++ b/lib_face/src/main/java/com/sw/plate/utils/arcface/facedb/entity/FaceEntity.java
@@ -52,12 +52,39 @@ public class FaceEntity implements Parcelable {
/**
* 会员编号
*/
- @ColumnInfo(name = "cardNo")
+ @ColumnInfo(name = "card_no")
private String cardNo;
@Ignore
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) {
this.userName = userName;
this.imagePath = imagePath;
@@ -65,6 +92,7 @@ public class FaceEntity implements Parcelable {
registerTime = System.currentTimeMillis();
}
+ @Ignore
public FaceEntity(FaceEntity faceEntity) {
this.faceId = faceEntity.faceId;
this.userName = faceEntity.userName;
@@ -73,9 +101,14 @@ public class FaceEntity implements Parcelable {
this.registerTime = faceEntity.registerTime;
this.userType = faceEntity.getUserType();
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) {
faceId = in.readLong();
registerTime = in.readLong();
@@ -84,6 +117,10 @@ public class FaceEntity implements Parcelable {
featureData = in.createByteArray();
userType = in.readString();
cardNo = in.readString();
+ userId = in.readString();
+ userFaceId = in.readString();
+ member = in.readByte() != 0;
+ faceUpdateTimestamp = in.readLong();
}
public static final Creator CREATOR = new Creator() {
@@ -162,7 +199,37 @@ public class FaceEntity implements Parcelable {
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
public int describeContents() {
@@ -178,6 +245,10 @@ public class FaceEntity implements Parcelable {
dest.writeByteArray(featureData);
dest.writeString(userType);
dest.writeString(cardNo);
+ dest.writeString(userId);
+ dest.writeString(userFaceId);
+ dest.writeByte((byte) (member ? 1 : 0));
+ dest.writeLong(faceUpdateTimestamp);
}
@Override
@@ -195,12 +266,16 @@ public class FaceEntity implements Parcelable {
TextUtils.equals(this.imagePath, that.imagePath) &&
Arrays.equals(featureData, that.featureData) &&
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
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);
return result;
}
diff --git a/lib_face/src/main/java/com/sw/plate/utils/arcface/faceserver/FaceServer.java b/lib_face/src/main/java/com/sw/plate/utils/arcface/faceserver/FaceServer.java
index da36a0e..c6fdfa1 100644
--- a/lib_face/src/main/java/com/sw/plate/utils/arcface/faceserver/FaceServer.java
+++ b/lib_face/src/main/java/com/sw/plate/utils/arcface/faceserver/FaceServer.java
@@ -33,6 +33,7 @@ import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
@@ -60,6 +61,10 @@ public class FaceServer {
faceRegisterInfoList = new ArrayList<>();
}
+ public FaceEngine getFaceEngine() {
+ return faceEngine;
+ }
+
public static FaceServer getInstance() {
if (faceServer == null) {
synchronized (FaceServer.class) {
@@ -123,16 +128,16 @@ public class FaceServer {
*/
public void initFaceList(final Context context, FaceEngine faceEngine, final OnInitFinishedCallback onInitFinishedCallback, boolean recognize) {
Disposable disposable = Observable.create((ObservableOnSubscribe) emitter -> {
- if (recognize) {
- List faceEntityList = FaceDatabase.getInstance(context).faceDao().getAllFaces();
- registerFaceFeatureInfoListFromDb(faceEngine, faceEntityList);
- emitter.onNext(faceEntityList.size());
- } else {
- faceRegisterInfoList = FaceDatabase.getInstance(context).faceDao().getAllFaces();
- emitter.onNext(faceRegisterInfoList == null ? 0 : faceRegisterInfoList.size());
- }
- emitter.onComplete();
- }).subscribeOn(Schedulers.io())
+ if (recognize) {
+ List faceEntityList = FaceDatabase.getInstance(context).faceDao().getAllFaces();
+ registerFaceFeatureInfoListFromDb(faceEngine, faceEntityList);
+ emitter.onNext(faceEntityList.size());
+ } else {
+ faceRegisterInfoList = FaceDatabase.getInstance(context).faceDao().getAllFaces();
+ emitter.onNext(faceRegisterInfoList == null ? 0 : faceRegisterInfoList.size());
+ }
+ emitter.onComplete();
+ }).subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(size -> {
@@ -164,6 +169,7 @@ public class FaceServer {
}
+
@SuppressLint("CheckResult")
public synchronized int clearAllFaces() {
if (faceRegisterInfoList != null) {
@@ -340,6 +346,7 @@ public class FaceServer {
*/
public void registerFaceFeatureInfoFromDb(FaceEntity faceEntity, FaceEngine faceEngine) {
if (faceEntity != null && faceEngine != null) {
+ Log.i(TAG, "registerFaceFeature:" + faceEntity.getFaceId()+"==="+ Arrays.toString(faceEntity.getFeatureData()));
FaceFeatureInfo faceFeatureInfo = new FaceFeatureInfo((int) faceEntity.getFaceId(), faceEntity.getFeatureData());
int res = faceEngine.registerFaceFeature(faceFeatureInfo);
Log.i(TAG, "registerFaceFeature:" + res);
diff --git a/lib_face/src/main/java/com/sw/plate/utils/arcface/viewmodel/RecognizeViewModel.java b/lib_face/src/main/java/com/sw/plate/utils/arcface/viewmodel/RecognizeViewModel.java
index ac55550..1317ddf 100644
--- a/lib_face/src/main/java/com/sw/plate/utils/arcface/viewmodel/RecognizeViewModel.java
+++ b/lib_face/src/main/java/com/sw/plate/utils/arcface/viewmodel/RecognizeViewModel.java
@@ -12,7 +12,6 @@ import androidx.lifecycle.ViewModel;
import com.arcsoft.face.AgeInfo;
import com.arcsoft.face.ErrorInfo;
-import com.arcsoft.face.FaceAttributeParam;
import com.arcsoft.face.FaceEngine;
import com.arcsoft.face.FaceInfo;
import com.arcsoft.face.GenderInfo;
@@ -132,13 +131,14 @@ public class RecognizeViewModel extends ViewModel implements RecognizeCallback {
private MutableLiveData ftInitCode = new MutableLiveData<>();
private MutableLiveData frInitCode = new MutableLiveData<>();
private MutableLiveData flInitCode = new MutableLiveData<>();
+ private MutableLiveData maskInitCode = new MutableLiveData<>();
/**
* 人脸操作辅助类,推帧即可,内部会进行特征提取、识别
*/
private FaceHelper faceHelper;
/**
- * VIDEO模式人脸检测引擎,用于预览帧人脸追踪及图像质量检测
+ * VIDEO模式人脸检测引擎,用于预览帧人脸追踪
*/
private FaceEngine ftEngine;
/**
@@ -149,6 +149,10 @@ public class RecognizeViewModel extends ViewModel implements RecognizeCallback {
* IMAGE模式活体检测引擎,用于预览帧人脸活体检测
*/
private FaceEngine flEngine;
+ /**
+ * IMAGE模式口罩检测引擎(5.0 独立引擎)
+ */
+ private FaceEngine maskEngine;
private PreviewConfig previewConfig;
@@ -160,10 +164,6 @@ public class RecognizeViewModel extends ViewModel implements RecognizeCallback {
private MutableLiveData recognizeUserId = new MutableLiveData<>();
- /**
- * 检测ir活体前,是否需要更新faceData
- */
- private boolean needUpdateFaceData;
/**
* 当前活体检测的检测类型
*/
@@ -270,6 +270,7 @@ public class RecognizeViewModel extends ViewModel implements RecognizeCallback {
*/
public void init(PreviewConfig previewConfig1) {
Context context = App.getContext();
+ // 档口机业务:支持外部传入 PreviewConfig(指定 RGB/IR 摄像头 id 及旋转角度),未传时走默认配置
if (previewConfig1 != null) {
previewConfig = previewConfig1;
} else {
@@ -281,9 +282,11 @@ public class RecognizeViewModel extends ViewModel implements RecognizeCallback {
Integer.parseInt(ConfigUtil.getIrCameraAdditionalRotation(context))
);
}
+
// 填入在设置界面设置好的配置信息
boolean enableLive = !ConfigUtil.getLivenessDetectType(context).equals(context.getString(R.string.value_liveness_type_disable));
enableLive = false;
+
boolean enableFaceQualityDetect = ConfigUtil.isEnableImageQualityDetect(context);
boolean enableFaceMoveLimit = ConfigUtil.isEnableFaceMoveLimit(context);
boolean enableFaceSizeLimit = ConfigUtil.isEnableFaceSizeLimit(context);
@@ -299,22 +302,22 @@ public class RecognizeViewModel extends ViewModel implements RecognizeCallback {
.similarThreshold(ConfigUtil.getRecognizeThreshold(context))
.imageQualityNoMaskRecognizeThreshold(ConfigUtil.getImageQualityNoMaskRecognizeThreshold(context))
.imageQualityMaskRecognizeThreshold(ConfigUtil.getImageQualityMaskRecognizeThreshold(context))
- .livenessParam(new LivenessParam(ConfigUtil.getRgbLivenessThreshold(context), ConfigUtil.getIrLivenessThreshold(context),
- ConfigUtil.getLivenessFqThreshold(context)))
+ .livenessParam(new LivenessParam(ConfigUtil.getRgbLivenessThreshold(context), ConfigUtil.getIrLivenessThreshold(context)))
.build();
- int cameraOffsetX = ConfigUtil.getDualCameraHorizontalOffset(context);
- int cameraOffsetY = ConfigUtil.getDualCameraVerticalOffset(context);
- needUpdateFaceData = (livenessType == LivenessType.IR && (cameraOffsetX != 0 || cameraOffsetY != 0));
+ // 人脸追踪引擎(VIDEO模式,仅检测)
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),
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();
int frEngineMask = FaceEngine.ASF_FACE_RECOGNITION;
if (enableFaceQualityDetect) {
@@ -324,16 +327,13 @@ public class RecognizeViewModel extends ViewModel implements RecognizeCallback {
10, frEngineMask));
FaceServer.getInstance().initFaceList(context, frEngine, faceCount -> loadFaceList = true, true);
- //启用活体检测时,才初始化活体引擎
+ // 启用活体检测时,才初始化活体引擎
if (enableLive) {
flEngine = new FaceEngine();
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,
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);
}
@@ -341,8 +341,10 @@ public class RecognizeViewModel extends ViewModel implements RecognizeCallback {
}
public void addFace(FaceEntity faceEntity) {
- if (frEngine != null)
+ if (frEngine != null) {
+ Log.e("performSync", "addFace=" + faceEntity.getRegisterTime());
FaceServer.getInstance().registerFaceFeatureInfoFromDb(faceEntity, frEngine);
+ }
}
public void refreshFaceList() {
@@ -356,19 +358,25 @@ public class RecognizeViewModel extends ViewModel implements RecognizeCallback {
if (ftEngine != null) {
synchronized (ftEngine) {
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) {
synchronized (frEngine) {
int frUnInitCode = frEngine.unInit();
- Log.i(TAG, "unInitEngine: " + frUnInitCode);
+ Log.i(TAG, "unInitEngine fr: " + frUnInitCode);
}
}
if (flEngine != null) {
synchronized (flEngine) {
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,
* 使下一帧进入时能重新触发识别流程。
+ * 适用场景:短时间内再次识别、点击重试按钮等需要重新开始识别的时机。
*/
public void resetFaceState() {
// 清空粘性 LiveData,防止旧结果被重新投递给 observer
@@ -468,7 +477,7 @@ public class RecognizeViewModel extends ViewModel implements RecognizeCallback {
.ftEngine(ftEngine)
.frEngine(frEngine)
.flEngine(flEngine)
- .needUpdateFaceData(needUpdateFaceData)
+ .maskEngine(maskEngine)
.frQueueSize(maxDetectFaceNum)
.flQueueSize(maxDetectFaceNum)
.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
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 (recognizeUserId != null) {
- recognizeUserId.postValue(compareResult);
- }
+ recognizeUserId.postValue(compareResult);
boolean isAdded = false;
List compareResults = compareResultList.getValue();
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));
}
}
+ } else {
+ recognizeUserId.postValue(compareResult);
}
});
}
@@ -599,14 +617,9 @@ public class RecognizeViewModel extends ViewModel implements RecognizeCallback {
if (recognizeStatus != null) {
if (recognizeStatus == RequestFeatureStatus.FAILED) {
color = RecognizeColor.COLOR_FAILED;
- }else if (recognizeStatus == RequestFeatureStatus.SUCCEED) {
+ }
+ if (recognizeStatus == RequestFeatureStatus.SUCCEED) {
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) {