package com.example.datalibrary.manager; import static com.example.datalibrary.model.GlobalSet.FEATURE_SIZE; import android.content.Context; import android.graphics.Bitmap; import android.os.Debug; import android.text.TextUtils; import android.util.Log; import com.baidu.idl.main.facesdk.FaceAuth; import com.baidu.idl.main.facesdk.FaceCrop; import com.baidu.idl.main.facesdk.FaceDetect; import com.baidu.idl.main.facesdk.FaceFeature; import com.baidu.idl.main.facesdk.FaceInfo; import com.baidu.idl.main.facesdk.FaceMouthMask; import com.baidu.idl.main.facesdk.ImageIllum; import com.baidu.idl.main.facesdk.model.BDFaceDetectListConf; import com.baidu.idl.main.facesdk.model.BDFaceImageInstance; import com.baidu.idl.main.facesdk.model.BDFaceOcclusion; import com.baidu.idl.main.facesdk.model.BDFaceSDKCommon; import com.baidu.idl.main.facesdk.model.BDFaceSDKConfig; import com.baidu.idl.main.facesdk.model.Feature; import com.example.datalibrary.api.FaceApi; import com.example.datalibrary.callback.FaceDetectCallBack; import com.example.datalibrary.callback.FaceQualityBack; import com.example.datalibrary.db.DBManager; import com.example.datalibrary.listener.DetectListener; import com.example.datalibrary.listener.QualityListener; import com.example.datalibrary.listener.SdkInitListener; import com.example.datalibrary.model.BDFaceCheckConfig; import com.example.datalibrary.model.BDFaceImageConfig; import com.example.datalibrary.model.BDLiveConfig; import com.example.datalibrary.model.BDQualityConfig; import com.example.datalibrary.model.LivenessModel; import com.example.datalibrary.model.User; import com.example.datalibrary.utils.ToastUtils; import java.util.ArrayList; import java.util.List; import java.util.Random; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicInteger; public class FaceSDKManager { public static final int SDK_MODEL_LOAD_SUCCESS = 0; public static final int SDK_UNACTIVATION = 1; private static final String TAG = "FaceSDKManager"; private List mRgbLiveList = new ArrayList<>(); private List mNirLiveList = new ArrayList<>(); private int mLastFaceId; private float threholdScore; public static volatile int initStatus = SDK_UNACTIVATION; public static volatile boolean initModelSuccess = false; private FaceAuth faceAuth; private ExecutorService es = Executors.newSingleThreadExecutor(); private Future future; private ExecutorService es2 = Executors.newSingleThreadExecutor(); private Future future2; private ExecutorService es3 = Executors.newSingleThreadExecutor(); private Future future3; private float[] scores; private ImageIllum imageIllum; private long startInitModelTime; private static int failNumber = 0; private static int faceId = 0; private static int lastFaceId = 0; private static LivenessModel faceAdoptModel; private boolean isFail = false; private long trackTime; private FaceModel faceModel; private boolean checkMouthMask = false; private boolean isMultiIdentify = false; private FaceSDKManager() { faceAuth = new FaceAuth(); faceAuth.setCoreConfigure(BDFaceSDKCommon.BDFaceCoreRunMode.BDFACE_LITE_POWER_NO_BIND, 2); } public void setActiveLog(boolean isLog) { if (faceAuth != null) { if (isLog) { faceAuth.setActiveLog(BDFaceSDKCommon.BDFaceLogInfo.BDFACE_LOG_TYPE_ALL, 1); } else { faceAuth.setActiveLog(BDFaceSDKCommon.BDFaceLogInfo.BDFACE_LOG_TYPE_ALL, 0); } } } public void setCheckMouthMask(boolean checkMouthMask) { this.checkMouthMask = checkMouthMask; } public void setMultiIdentify(boolean isMultiFaceIdentify) { this.isMultiIdentify = isMultiFaceIdentify; } private static class HolderClass { private static final FaceSDKManager INSTANCE = new FaceSDKManager(); } public static FaceSDKManager getInstance() { return HolderClass.INSTANCE; } public ImageIllum getImageIllum() { return imageIllum; } public void initModel( final Context context, BDFaceSDKConfig config, boolean isLog, final SdkInitListener listener) { setActiveLog(isLog); initModel(context, config, listener); } /** * 初始化模型,目前包含检查,活体,识别模型;因为初始化是顺序执行,可以在最好初始化回掉中返回状态结果 * * @param context */ public void initModel(final Context context, BDFaceSDKConfig config, final SdkInitListener listener) { // 曝光 if (imageIllum == null) { imageIllum = new ImageIllum(); } // 其他模型初始化 if (faceModel == null) { faceModel = new FaceModel(checkMouthMask); } faceModel.setListener(listener); faceModel.init(config, context); startInitModelTime = System.currentTimeMillis(); } public void destroy(){ if(rgbInstance!=null){ rgbInstance = null; } } public FaceCrop getFaceCrop() { return faceModel.getFaceCrop(); } public FaceDetect getFaceDetectPerson() { return faceModel.getFaceDetectPerson(); } public FaceFeature getFacePersonFeature() { return faceModel.getFacePersonFeature(); } public FaceMouthMask getFaceMouthMask() { return faceModel.getFaceMoutMask(); } public void initDataBases(Context context) { if (FaceApi.getInstance().getmUserNum() != 0) { //ToastUtils.toast(context, "人脸库加载中"); } emptyFrame(); // 初始化数据库 DBManager.getInstance().init(context); // 数据变化,更新内存 initPush(context); } /** * 数据库发现变化时候,重新把数据库中的人脸信息添加到内存中,id+feature */ public void initPush(final Context context) { if (future3 != null && !future3.isDone()) { future3.cancel(true); } future3 = es3.submit( new Runnable() { @Override public void run() { faceModel.getFaceSearch().featureClear(); synchronized (faceModel.getFaceSearch()) { List users = FaceApi.getInstance().getAllUserList(); for (int i = 0; i < users.size(); i++) { User user = users.get(i); faceModel.getFaceSearch().pushPersonById(user.getId(), user.getFeature()); } if (FaceApi.getInstance().getmUserNum() != 0) { //ToastUtils.toast(context, "人脸库加载成功"); } } } }); } private void setFail(LivenessModel livenessModel) { Log.e("faceId", livenessModel.getFaceInfo().faceID + ""); if (failNumber >= 2) { faceId = livenessModel.getFaceInfo().faceID; faceAdoptModel = livenessModel; trackTime = System.currentTimeMillis(); isFail = false; faceAdoptModel.setMultiFrame(true); } else { failNumber += 1; faceId = 0; faceAdoptModel = null; isFail = true; livenessModel.setMultiFrame(true); } } public void emptyFrame() { failNumber = 0; faceId = 0; isFail = false; trackTime = 0; faceAdoptModel = null; } private FaceInfo[] getTrackCheck(BDFaceImageInstance rgbInstance) { // 快速检测获取人脸信息,仅用于绘制人脸框,详细人脸数据后续获取 FaceInfo[] faceInfos = faceModel .getFaceTrack() .track( BDFaceSDKCommon.DetectType.DETECT_VIS, BDFaceSDKCommon.AlignType.BDFACE_ALIGN_TYPE_RGB_FAST, rgbInstance); return faceInfos; } private FaceInfo[] getDetectCheck(BDFaceImageInstance rgbInstance) { // 快速检测获取人脸信息,仅用于绘制人脸框,详细人脸数据后续获取 FaceInfo[] faceInfos = faceModel.getFaceDetect().detect(BDFaceSDKCommon.DetectType.DETECT_VIS, rgbInstance); return faceInfos; } private BDFaceImageInstance getBdImage(BDFaceImageConfig bdFaceImageConfig, boolean darkEnhance) { BDFaceImageInstance rgbInstance = new BDFaceImageInstance( bdFaceImageConfig.data, bdFaceImageConfig.srcHeight, bdFaceImageConfig.srcWidth, bdFaceImageConfig.bdFaceImageType, bdFaceImageConfig.direction, bdFaceImageConfig.mirror); BDFaceImageInstance rgbInstanceOne; // 判断暗光恢复 if (darkEnhance) { rgbInstanceOne = faceModel.getDark().faceDarkEnhance(rgbInstance); } else { rgbInstanceOne = rgbInstance; } return rgbInstanceOne; } private boolean frameSelect(FaceInfo faceInfo) { if (lastFaceId != faceInfo.faceID) { lastFaceId = faceInfo.faceID; } if (System.currentTimeMillis() - trackTime < 0 && faceId == faceInfo.faceID) { faceAdoptModel.setMultiFrame(true); return false; } if (faceAdoptModel != null) { faceAdoptModel.setMultiFrame(false); } faceId = 0; faceAdoptModel = null; if (!isFail /*&& failNumber != 0*/) { failNumber = 0; } return true; } public BDFaceImageInstance getCopeFace(Bitmap bitmap, float[] landmarks, int initialValue) { if (faceModel == null || faceModel.getFaceCrop() == null) { return null; } BDFaceImageInstance imageInstance = new BDFaceImageInstance(bitmap); if (!bitmap.isRecycled()) { bitmap.recycle(); } AtomicInteger isOutoBoundary = new AtomicInteger(); BDFaceImageInstance cropInstance = faceModel.getFaceCrop().cropFaceByLandmark(imageInstance, landmarks, 2.0f, false, isOutoBoundary); return cropInstance; } private static BDFaceImageInstance rgbInstance =null; /** * 检测-活体-特征-人脸检索流程 * * @param bdFaceImageConfig 可见光YUV 数据流 * @param bdNirFaceImageConfig 红外YUV 数据流 * @param bdDepthFaceImageConfig 深度depth 数据流 * @param bdFaceCheckConfig 识别参数 * @param faceDetectCallBack */ public void onDetectCheck( String groupId, final BDFaceImageConfig bdFaceImageConfig, final BDFaceImageConfig bdNirFaceImageConfig, final BDFaceImageConfig bdDepthFaceImageConfig, final BDFaceCheckConfig bdFaceCheckConfig, final FaceDetectCallBack faceDetectCallBack) { if (!FaceSDKManager.initModelSuccess) { return; } long startTime = System.currentTimeMillis(); // 创建检测结果存储数据 LivenessModel livenessModel = new LivenessModel(); livenessModel.setGroupId(groupId); // 创建检测对象,如果原始数据YUV,转为算法检测的图片BGR // TODO: 用户调整旋转角度和是否镜像,手机和开发版需要动态适配 //System.out.println("==rgbInstance==>初始化"); if(rgbInstance!=null){ rgbInstance.destory(); } rgbInstance = getBdImage(bdFaceImageConfig, bdFaceCheckConfig.darkEnhance); livenessModel.setTestBDFaceImageInstanceDuration(System.currentTimeMillis() - startTime); onTrack( rgbInstance, livenessModel, new DetectListener() { @Override public void onDetectSuccess(FaceInfo[] faceInfos, BDFaceImageInstance rgbInstance) { // 多帧判断 if (!frameSelect(faceInfos[0])) { livenessModel.setBdFaceImageInstance(rgbInstance.getImage()); if (faceDetectCallBack != null && faceAdoptModel != null) { //System.out.println("==isOk==>多帧判断"); faceDetectCallBack.onFaceDetectDarwCallback(livenessModel); faceDetectCallBack.onFaceDetectCallback(faceAdoptModel); } return; } // 保存人脸特征点 livenessModel.setLandmarks(faceInfos[0].landmarks); // 保存人脸图片 livenessModel.setBdFaceImageInstance(rgbInstance.getImage()); // 口罩检测数据 if (checkMouthMask) { FaceMouthMask mouthMask = getFaceMouthMask(); if (mouthMask != null) { float[] maskScores = mouthMask.checkMask(rgbInstance, faceInfos); if (maskScores != null && maskScores.length > 0) { float maskResult = maskScores[0]; Log.d("mouth_mask", "mask_score:" + maskResult); if (livenessModel != null) { livenessModel.setMouthMaskArray(maskScores); } } } } // 调用绘制人脸框接口 if (faceDetectCallBack != null) { faceDetectCallBack.onFaceDetectDarwCallback(livenessModel); } // 送检识别 onLivenessCheck( rgbInstance, bdNirFaceImageConfig, bdDepthFaceImageConfig, bdFaceCheckConfig, livenessModel, startTime, faceDetectCallBack, faceInfos); } @Override public void onDetectFail() { emptyFrame(); // 流程结束销毁图片,开始下一帧图片检测,否着内存泄露 if (faceDetectCallBack != null) { faceDetectCallBack.onFaceDetectCallback(null); livenessModel.setBdFaceImageInstance(rgbInstance.getImage()); //System.out.println("==isOk==>流程结束"); // SaveImageManager.getInstance().saveImage(livenessModel, bdFaceCheckConfig.bdLiveConfig); faceDetectCallBack.onFaceDetectDarwCallback(livenessModel); faceDetectCallBack.onTip(0, "未检测到人脸"); } } }); } private float silentLive( BDFaceImageInstance rgbInstance, BDFaceSDKCommon.LiveType type, float[] landmarks, List list, float liveScore) { float score = 0; if (landmarks != null) { synchronized (faceModel.getFaceLive()) { Log.e("test_camera", rgbInstance.getImage().data.length + "开始"); score = faceModel.getFaceLive().silentLive(type, rgbInstance, landmarks, liveScore); Log.e("test_camera", "活体结束"); } list.add(score > liveScore); } while (list.size() > 6) { list.remove(0); } if (list.size() > 2) { int rgbSum = 0; for (Boolean b : list) { if (b) { rgbSum++; } } if (1.0 * rgbSum / list.size() > 0.6) { if (score < liveScore) { score = liveScore + (1 - liveScore) * new Random().nextFloat(); } } else { if (score > liveScore) { score = new Random().nextFloat() * liveScore; } } } return score; } /** * 活体检测 * @param rgbInstance * @param type * @param faceInfos * @param list * @param liveScore * @return */ private float[] silentLives( BDFaceImageInstance rgbInstance, BDFaceSDKCommon.LiveType type, FaceInfo[] faceInfos, List list, float liveScore) { float[] scores = {0, 0, 0}; // 最多检测3个人脸 if (faceInfos != null) { synchronized (faceModel.getFaceLive()) { Log.e("test_camera", rgbInstance.getImage().data.length + "开始"); int size = faceInfos.length; for (int i = 0; i < size; i++) { scores[i] = faceModel.getFaceLive().silentLive(type, rgbInstance, faceInfos[i].landmarks, liveScore); } } } return scores; } private void onDetect( BDFaceCheckConfig bdFaceCheckConfig, BDFaceImageInstance rgbInstance, FaceInfo[] fastFaceInfos, LivenessModel livenessModel, DetectListener detectListener) { long accurateTime = System.currentTimeMillis(); FaceInfo[] faceInfos; if (bdFaceCheckConfig != null) { bdFaceCheckConfig.bdFaceDetectListConfig.usingQuality = true; faceInfos = faceModel .getFaceDetect() .detect( BDFaceSDKCommon.DetectType.DETECT_VIS, BDFaceSDKCommon.AlignType.BDFACE_ALIGN_TYPE_RGB_ACCURATE, rgbInstance, fastFaceInfos, bdFaceCheckConfig.bdFaceDetectListConfig); } else { faceInfos = faceModel.getFaceDetect().detect(BDFaceSDKCommon.DetectType.DETECT_VIS, rgbInstance); } livenessModel.setAccurateTime(System.currentTimeMillis() - accurateTime); if (faceInfos == null || faceInfos.length <= 0) { detectListener.onDetectFail(); return; } // 重新赋予详细人脸信息 if (livenessModel.getFaceInfo() != null) { faceInfos[0].faceID = livenessModel.getFaceInfo().faceID; } livenessModel.setFaceInfos(faceInfos); livenessModel.setFaceInfo(faceInfos[0]); livenessModel.setTrackStatus(2); // 保存人脸关键点 livenessModel.setLandmarks(faceInfos[0].landmarks); detectListener.onDetectSuccess(faceInfos, rgbInstance); } /** * 活体-特征-人脸检索全流程 * * @param rgbInstance 可见光底层送检对象 * @param nirBDFaceImageConfig 红外YUV 数据流 * @param depthBDFaceImageConfig 深度depth 数据流 * @param livenessModel 检测结果数据集合 * @param startTime 开始检测时间 * @param faceDetectCallBack */ public void onLivenessCheck( final BDFaceImageInstance rgbInstance, final BDFaceImageConfig nirBDFaceImageConfig, final BDFaceImageConfig depthBDFaceImageConfig, final BDFaceCheckConfig bdFaceCheckConfig, final LivenessModel livenessModel, final long startTime, final FaceDetectCallBack faceDetectCallBack, final FaceInfo[] fastFaceInfos) { if (future2 != null && !future2.isDone()) { // 流程结束销毁图片,开始下一帧图片检测,否着内存泄露 //future2.cancel(true); //System.out.println("==isOk==>之前没结束"); return; } future2 = es2.submit( new Runnable() { @Override public void run() { // 获取BDFaceCheckConfig配置信息 if (bdFaceCheckConfig == null) { return; } onDetect( bdFaceCheckConfig, rgbInstance, fastFaceInfos, livenessModel, new DetectListener() { @Override public void onDetectSuccess(FaceInfo[] faceInfos, BDFaceImageInstance rgbInstance) { // 人脸id赋值 if (mLastFaceId != fastFaceInfos[0].faceID) { mLastFaceId = fastFaceInfos[0].faceID; mRgbLiveList.clear(); mNirLiveList.clear(); } if (bdFaceCheckConfig == null) { livenessModel.clearIdentifyResults(); if (faceDetectCallBack != null) { faceDetectCallBack.onFaceDetectCallback(livenessModel); } return; } // 最优人脸控制 if (!onBestImageCheck(livenessModel, bdFaceCheckConfig, faceDetectCallBack)) { livenessModel.setQualityCheck(true); livenessModel.clearIdentifyResults(); if (faceDetectCallBack != null) { faceDetectCallBack.onFaceDetectCallback(livenessModel); } return; } // 质量检测未通过,销毁BDFaceImageInstance,结束函数 if (!onQualityCheck( faceInfos, bdFaceCheckConfig.bdQualityConfig, faceDetectCallBack)) { livenessModel.setQualityCheck(true); livenessModel.clearIdentifyResults(); if (faceDetectCallBack != null) { faceDetectCallBack.onFaceDetectCallback(livenessModel); } return; } livenessModel.setQualityCheck(false); // 获取LivenessConfig liveCheckMode 配置选项:【不使用活体:0】;【RGB活体:1】;【RGB+NIR活体:2】;【RGB+Depth活体:3】;【RGB+NIR+Depth活体:4】 // TODO 活体检测 float[] rgbScores = {-1}; BDLiveConfig bdLiveConfig = bdFaceCheckConfig.bdLiveConfig; boolean isLiveCheck = bdFaceCheckConfig.bdLiveConfig != null; if (isLiveCheck) { long startRgbTime = System.currentTimeMillis(); rgbScores = silentLives( rgbInstance, BDFaceSDKCommon.LiveType.BDFACE_SILENT_LIVE_TYPE_RGB, faceInfos, mRgbLiveList, bdLiveConfig.rgbLiveScore); /* if (rgbScores != null){ int size = rgbScores.length; Log.d("Attend", "score size:" + size); for (int i = 0; i < size; i++) { Log.d("Attend", "score:" + rgbScores[i]); } } */ if (faceInfos.length == 1) { livenessModel.setRgbLivenessScore(rgbScores[0]); } livenessModel.setRgbLivenessScores(rgbScores); livenessModel.setRgbLivenessDuration(System.currentTimeMillis() - startRgbTime); } // TODO nir活体检测 float nirScore = -1; FaceInfo[] faceInfosIr = null; BDFaceImageInstance nirInstance = null; boolean isHaveNirImage = nirBDFaceImageConfig != null && isLiveCheck; if (isHaveNirImage) { // 创建检测对象,如果原始数据YUV-IR,转为算法检测的图片BGR // TODO: 用户调整旋转角度和是否镜像,手机和开发版需要动态适配 long nirInstanceTime = System.currentTimeMillis(); nirInstance = getBdImage(nirBDFaceImageConfig, false); livenessModel.setBdNirFaceImageInstance(nirInstance.getImage()); livenessModel.setNirInstanceTime(System.currentTimeMillis() - nirInstanceTime); // 避免RGB检测关键点在IR对齐活体稳定,增加红外检测 long startIrDetectTime = System.currentTimeMillis(); BDFaceDetectListConf bdFaceDetectListConf = new BDFaceDetectListConf(); bdFaceDetectListConf.usingDetect = true; faceInfosIr = faceModel .getFaceNirDetect() .detect( BDFaceSDKCommon.DetectType.DETECT_NIR, BDFaceSDKCommon.AlignType.BDFACE_ALIGN_TYPE_NIR_ACCURATE, nirInstance, null, bdFaceDetectListConf); livenessModel.setIrLivenessDuration( System.currentTimeMillis() - startIrDetectTime); // LogUtils.e(TIME_TAG, "detect ir time = " + livenessModel.getIrLivenessDuration()); if (faceInfosIr != null && faceInfosIr.length > 0) { FaceInfo faceInfoIr = faceInfosIr[0]; nirScore = silentLive( nirInstance, BDFaceSDKCommon.LiveType.BDFACE_SILENT_LIVE_TYPE_NIR, faceInfoIr.landmarks, mNirLiveList, bdLiveConfig.nirLiveScore); livenessModel.setIrLivenessScore(nirScore); // LogUtils.e(TIME_TAG, "live ir time = " + livenessModel.getIrLivenessDuration()); } } // TODO depth活体检测 float depthScore = -1; boolean isHaveDepthImage = depthBDFaceImageConfig != null && isLiveCheck; if (depthBDFaceImageConfig != null) { // TODO: 用户调整旋转角度和是否镜像,适配Atlas 镜头,目前宽和高400*640,其他摄像头需要动态调整,人脸72 个关键点x 坐标向左移动80个像素点 float[] depthLandmark = new float[faceInfos[0].landmarks.length]; BDFaceImageInstance depthInstance; if (bdFaceCheckConfig.cameraType == 1) { System.arraycopy( faceInfos[0].landmarks, 0, depthLandmark, 0, faceInfos[0].landmarks.length); for (int i = 0; i < 144; i = i + 2) { depthLandmark[i] -= 80; } } else { depthLandmark = faceInfos[0].landmarks; } depthInstance = getBdImage(depthBDFaceImageConfig, false); livenessModel.setBdDepthFaceImageInstance(depthInstance.getImage()); // 创建检测对象,如果原始数据Depth long startDepthTime = System.currentTimeMillis(); depthScore = faceModel .getFaceLive() .silentLive( BDFaceSDKCommon.LiveType.BDFACE_SILENT_LIVE_TYPE_DEPTH, depthInstance, depthLandmark); livenessModel.setDepthLivenessScore(depthScore); livenessModel.setDepthtLivenessDuration( System.currentTimeMillis() - startDepthTime); // LogUtils.e(TIME_TAG, "live depth time = " + livenessModel.getDepthtLivenessDuration()); depthInstance.destory(); } boolean isRgbScoreCheck = false; boolean isNirScoreCheck = false; boolean isDepthScoreCheck = false; if (isLiveCheck) { int size = rgbScores.length; for (int i = 0; i < size; i++) { if (rgbScores[i] > bdLiveConfig.rgbLiveScore) { isRgbScoreCheck = true; } } // isRgbScoreCheck = true; // liujinhui for test // isRgbScoreCheck = (rgbScores[0] > bdLiveConfig.rgbLiveScore); isNirScoreCheck = (isHaveNirImage ? nirScore > bdLiveConfig.nirLiveScore : true); isDepthScoreCheck = (isHaveDepthImage ? depthScore > bdLiveConfig.depthLiveScore : true); } // 如果设置为不进行活体检测 int liveCheckMode = livenessModel.getLiveType(); if (liveCheckMode == 0){ isRgbScoreCheck = true; } // TODO 特征提取+人脸检索 if (!isLiveCheck || (isRgbScoreCheck && isNirScoreCheck && isDepthScoreCheck)) { if (livenessModel != null) { livenessModel.clearIdentifyResults(); livenessModel.setUser(null); } synchronized (faceModel.getFaceSearch()) { if (faceInfos != null) { int size = faceInfos.length; for (int i = 0; i < size; i++) { // 特征提取 // 模糊结果过滤,戴口罩时候,不进行过滤 if (!checkMouthMask) { float blur = faceInfos[i].bluriness; BDFaceOcclusion occlusion = faceInfos[i].occlusion; float leftEye = occlusion.leftEye; // "左眼遮挡" float rightEye = occlusion.rightEye; // "右眼遮挡" float nose = occlusion.nose; // "鼻子遮挡置信度" float mouth = occlusion.mouth; // "嘴巴遮挡置信度" float leftCheek = occlusion.leftCheek; // "左脸遮挡" float rightCheek = occlusion.rightCheek; // "右脸遮挡" float chin = occlusion.chin; // 动态底库限制 faceModel .getFaceSearch() .setNeedJoinDB( selectQuality( blur, leftEye, rightEye, nose, mouth, leftCheek, rightCheek, chin)); } if (bdLiveConfig != null){ onFeatureChecks( i, rgbInstance, bdFaceCheckConfig, faceInfos, faceInfosIr, nirInstance, livenessModel, bdFaceCheckConfig.secondFeature, bdFaceCheckConfig.featureCheckMode, bdFaceCheckConfig.activeModel, rgbScores, bdLiveConfig.rgbLiveScore); } else{ onFeatureChecks( i, rgbInstance, bdFaceCheckConfig, faceInfos, faceInfosIr, nirInstance, livenessModel, bdFaceCheckConfig.secondFeature, bdFaceCheckConfig.featureCheckMode, bdFaceCheckConfig.activeModel, rgbScores, -1); } } } } } // 流程结束,记录最终时间 livenessModel.setAllDetectDuration(System.currentTimeMillis() - startTime); // LogUtils.e(TIME_TAG, "all process time = " + livenessModel.getAllDetectDuration()); // 流程结束销毁图片,开始下一帧图片检测,否着内存泄露 if (nirInstance != null) { nirInstance.destory(); } // 显示最终结果提示 if (faceDetectCallBack != null) { faceDetectCallBack.onFaceDetectCallback(livenessModel); } } @Override public void onDetectFail() { if (faceDetectCallBack != null) { faceDetectCallBack.onFaceDetectCallback(livenessModel); } } }); } }); } /** * 最优人脸控制 * * @param livenessModel * @param faceDetectCallBack * @return */ public boolean onBestImageCheck( LivenessModel livenessModel, BDFaceCheckConfig bdFaceCheckConfig, FaceDetectCallBack faceDetectCallBack) { boolean isBestImageCheck = false; if (livenessModel != null && livenessModel.getFaceInfos() != null) { FaceInfo[] faceInfos = livenessModel.getFaceInfos(); int size = faceInfos.length; for (int i = 0; i < size; i++) { float bestImageScore = faceInfos[i].bestImageScore; if (bestImageScore > 0.5) { isBestImageCheck = true; } } } return isBestImageCheck; } /** * 特征提取-人脸识别比对 * * @param rgbInstance 可见光底层送检对象 * @param landmark 检测眼睛,嘴巴,鼻子,72个关键点 * @param faceInfos nir人脸数据 * @param nirInstance nir 图像句柄 * @param livenessModel 检测结果数据集合 * @param featureCheckMode 特征抽取模式【不提取特征:1】;【提取特征:2】;【提取特征+1:N检索:3】; * @param featureType 特征抽取模态执行 【生活照:1】;【证件照:2】;【混合模态:3】; */ private void onFeatureCheck( BDFaceImageInstance rgbInstance, BDFaceCheckConfig bdFaceCheckConfig, float[] landmark, FaceInfo[] faceInfos, BDFaceImageInstance nirInstance, LivenessModel livenessModel, byte[] secondFeature, final int featureCheckMode, final int featureType) { // 如果不抽取特征,直接返回 if (featureCheckMode == 1) { return; } byte[] feature = new byte[512]; if (featureType == 3) { // todo: 混合模态使用方式是根据图片的曝光来选择需要使用的type,光照的取值范围为:0~1之间 AtomicInteger atomicInteger = new AtomicInteger(); FaceSDKManager.getInstance().getImageIllum().imageIllum(rgbInstance, atomicInteger); int illumScore = atomicInteger.get(); BDQualityConfig bdQualityConfig = bdFaceCheckConfig.bdQualityConfig; boolean isIllum = bdQualityConfig != null ? illumScore < bdQualityConfig.illum : false; BDFaceSDKCommon.FeatureType type = isIllum ? BDFaceSDKCommon.FeatureType.BDFACE_FEATURE_TYPE_NIR : BDFaceSDKCommon.FeatureType.BDFACE_FEATURE_TYPE_LIVE_PHOTO; BDFaceImageInstance bdFaceImageInstance = isIllum ? nirInstance : rgbInstance; float[] landmarks = isIllum ? faceInfos[0].landmarks : landmark; long startFeatureTime = System.currentTimeMillis(); float featureSize = faceModel.getFaceFeature().feature(type, bdFaceImageInstance, landmarks, feature); livenessModel.setFeatureDuration(System.currentTimeMillis() - startFeatureTime); livenessModel.setFeature(feature); // 人脸检索 featureSearch( featureCheckMode, livenessModel, bdFaceCheckConfig, feature, secondFeature, featureSize, BDFaceSDKCommon.FeatureType.BDFACE_FEATURE_TYPE_LIVE_PHOTO); } else { // 生活照检索 long startFeatureTime = System.currentTimeMillis(); float featureSize = faceModel .getFaceFeature() .feature( BDFaceSDKCommon.FeatureType.BDFACE_FEATURE_TYPE_LIVE_PHOTO, rgbInstance, landmark, feature); livenessModel.setFeatureDuration(System.currentTimeMillis() - startFeatureTime); livenessModel.setFeature(feature); livenessModel.setFeatureDuration(System.currentTimeMillis() - startFeatureTime); // 人脸检索 featureSearch( featureCheckMode, livenessModel, bdFaceCheckConfig, feature, secondFeature, featureSize, BDFaceSDKCommon.FeatureType.BDFACE_FEATURE_TYPE_LIVE_PHOTO); } } /** * 特征提取-人脸识别比对 * * @param rgbInstance 可见光底层送检对象 * @param rgbFaceInfos rgb人脸数据 * @param faceInfos nir人脸数据 * @param nirInstance nir 图像句柄 * @param livenessModel 检测结果数据集合 * @param featureCheckMode 特征抽取模式【不提取特征:1】;【提取特征:2】;【提取特征+1:N检索:3】; * @param featureType 特征抽取模态执行 【生活照:1】;【证件照:2】;【混合模态:3】; * @param rgbScores 可见光得分 * @param liveScore 可见光活体阈值 */ private void onFeatureChecks( int index, BDFaceImageInstance rgbInstance, BDFaceCheckConfig bdFaceCheckConfig, FaceInfo[] rgbFaceInfos, FaceInfo[] faceInfos, BDFaceImageInstance nirInstance, LivenessModel livenessModel, byte[] secondFeature, final int featureCheckMode, final int featureType, float[] rgbScores, float liveScore) { // 如果不抽取特征,直接返回 if (featureCheckMode == 1) { return; } byte[] feature = new byte[512]; if (featureType == 3) { // todo: 混合模态使用方式是根据图片的曝光来选择需要使用的type,光照的取值范围为:0~1之间 AtomicInteger atomicInteger = new AtomicInteger(); FaceSDKManager.getInstance().getImageIllum().imageIllum(rgbInstance, atomicInteger); int illumScore = atomicInteger.get(); BDQualityConfig bdQualityConfig = bdFaceCheckConfig.bdQualityConfig; boolean isIllum = bdQualityConfig != null ? illumScore < bdQualityConfig.illum : false; BDFaceSDKCommon.FeatureType type = isIllum ? BDFaceSDKCommon.FeatureType.BDFACE_FEATURE_TYPE_NIR : BDFaceSDKCommon.FeatureType.BDFACE_FEATURE_TYPE_LIVE_PHOTO; BDFaceImageInstance bdFaceImageInstance = isIllum ? nirInstance : rgbInstance; if (rgbFaceInfos == null) { return; } int size = faceInfos.length; if (rgbFaceInfos[index].landmarks == null) { return; } if (rgbScores != null) { float score = rgbScores[index]; if (score < liveScore) { return; } } float[] landmarks = rgbFaceInfos[index].landmarks; long startFeatureTime = System.currentTimeMillis(); float featureSize = faceModel.getFaceFeature().feature(type, bdFaceImageInstance, landmarks, feature); livenessModel.setFeatureDuration(System.currentTimeMillis() - startFeatureTime); livenessModel.setFeature(feature); // 人脸检索 featureSearchs( index, featureCheckMode, livenessModel, bdFaceCheckConfig, feature, secondFeature, featureSize, BDFaceSDKCommon.FeatureType.BDFACE_FEATURE_TYPE_LIVE_PHOTO); } else { // 生活照检索 long startFeatureTime = System.currentTimeMillis(); if (rgbFaceInfos == null) { return; } int size = rgbFaceInfos.length; if (rgbFaceInfos[index].landmarks == null) { return; } if (rgbScores != null) { float score = rgbScores[index]; if (score < liveScore) { return; } } float featureSize = faceModel .getFaceFeature() .feature( BDFaceSDKCommon.FeatureType.BDFACE_FEATURE_TYPE_LIVE_PHOTO, rgbInstance, rgbFaceInfos[index].landmarks, feature); livenessModel.setFeatureDuration(System.currentTimeMillis() - startFeatureTime); livenessModel.setFeature(feature); livenessModel.setFeatureDuration(System.currentTimeMillis() - startFeatureTime); // 人脸检索 featureSearchs( index, featureCheckMode, livenessModel, bdFaceCheckConfig, feature, secondFeature, featureSize, BDFaceSDKCommon.FeatureType.BDFACE_FEATURE_TYPE_LIVE_PHOTO); } } /** * 人脸库检索 * * @param featureCheckMode 特征抽取模式【不提取特征:1】;【提取特征:2】;【提取特征+1:N检索:3】; * @param livenessModel 检测结果数据集合 * @param feature 特征点 * @param secondFeature 1:1 特征点 * @param featureSize 特征点的size * @param type 特征提取类型 */ private void featureSearch( final int featureCheckMode, LivenessModel livenessModel, BDFaceCheckConfig bdFaceCheckConfig, byte[] feature, byte[] secondFeature, float featureSize, BDFaceSDKCommon.FeatureType type) { // 如果只提去特征,不做检索,此处返回 if (featureCheckMode == 2) { livenessModel.setFeatureCode(featureSize); return; } // 如果提取特征+检索,调用search 方法 if (featureSize == FEATURE_SIZE / 4) { long startFeature = System.currentTimeMillis(); // 特征提取成功 // TODO 阈值可以根据不同模型调整 if (featureCheckMode == 3) { List featureResult = faceModel.getFaceSearch().search(type, bdFaceCheckConfig.scoreThreshold, 1, feature, false); // TODO 返回top num = 1 个数据集合,此处可以任意设置,会返回比对从大到小排序的num 个数据集合 if (featureResult != null && featureResult.size() > 0) { // 获取第一个数据 Feature topFeature = featureResult.get(0); // 判断第一个阈值是否大于设定阈值,如果大于,检索成功 threholdScore = bdFaceCheckConfig.scoreThreshold; if (topFeature != null && topFeature.getScore() > threholdScore) { // 当前featureEntity 只有id+feature 索引,在数据库中查到完整信息 User user = FaceApi.getInstance().getUserListById(topFeature.getId()); if (user != null) { livenessModel.setUser(user); livenessModel.setFeatureScore(topFeature.getScore()); /*faceId = livenessModel.getFaceInfo().faceID; trackTime = System.currentTimeMillis(); faceAdoptModel = livenessModel; failNumber = 0; isFail = false;*/ setFail(livenessModel); } else { setFail(livenessModel); } } else { setFail(livenessModel); } } else { setFail(livenessModel); } } else if (featureCheckMode == 4) { // 目前仅支持 float score = faceModel .getFaceSearch() .compare( BDFaceSDKCommon.FeatureType.BDFACE_FEATURE_TYPE_ID_PHOTO, livenessModel.getFeature(), secondFeature, true); livenessModel.setScore(score); if (score > threholdScore) { /*faceId = livenessModel.getFaceInfo().faceID; trackTime = System.currentTimeMillis(); faceAdoptModel = livenessModel; failNumber = 0; isFail = false;*/ setFail(livenessModel); } else { setFail(livenessModel); } } livenessModel.setCheckDuration(System.currentTimeMillis() - startFeature); } } /** * 人脸库检索 * * @param featureCheckMode 特征抽取模式【不提取特征:1】;【提取特征:2】;【提取特征+1:N检索:3】; * @param livenessModel 检测结果数据集合 * @param feature 特征点 * @param secondFeature 1:1 特征点 * @param featureSize 特征点的size * @param type 特征提取类型 */ private void featureSearchs( final int index, final int featureCheckMode, LivenessModel livenessModel, BDFaceCheckConfig bdFaceCheckConfig, byte[] feature, byte[] secondFeature, float featureSize, BDFaceSDKCommon.FeatureType type) { // 如果只提去特征,不做检索,此处返回 if (featureCheckMode == 2) { livenessModel.setFeatureCode(featureSize); return; } // 如果提取特征+检索,调用search 方法 if (featureSize == FEATURE_SIZE / 4) { long startFeature = System.currentTimeMillis(); // 特征提取成功 // TODO 阈值可以根据不同模型调整 if (featureCheckMode == 3) { //System.out.println("==isOk==>44"); List featureResult = faceModel.getFaceSearch().search(type, bdFaceCheckConfig.scoreThreshold, 5, feature, false); //System.out.println("==isOk==>45"); // TODO 返回top num = 1 个数据集合,此处可以任意设置,会返回比对从大到小排序的num 个数据集合 if (featureResult != null && featureResult.size() > 0) { //System.out.println("==isOk==>匹配到数量"+featureResult.size()); User user = null; Feature topFeature = null; if(TextUtils.isEmpty(livenessModel.getGroupId())){ //为空,需要排序,优先取会员 for(Feature feat:featureResult) { threholdScore = bdFaceCheckConfig.scoreThreshold; if (feat != null && feat.getScore() > threholdScore) { User userOld = FaceApi.getInstance().getUserListById(feat.getId()); //System.out.println("==isOk==>匹配到类型:"+userOld.getUserName()); if("0".equals(userOld.getGroupId())){ user = userOld; topFeature = feat; break; }else if(user==null){ user = userOld; topFeature = feat; } } } }else { //只取该类型用户 for(Feature feat:featureResult) { // 获取数据 // 判断阈值是否大于设定阈值,如果大于,检索成功 threholdScore = bdFaceCheckConfig.scoreThreshold; if (feat != null && feat.getScore() > threholdScore) { //System.out.println("==isOk==>50"); // 当前featureEntity 只有id+feature 索引,在数据库中查到完整信息 User userOld = FaceApi.getInstance().getUserListById(feat.getId()); if(livenessModel.getGroupId().equals(userOld.getGroupId())){ user = userOld; topFeature = feat; break; } } } } if (user != null) { //System.out.println("==isOk==>匹配到"); IdentifyResult idResult = new IdentifyResult(user, index, topFeature.getScore()); livenessModel.addIdentifyResult(idResult); livenessModel.setUser(user); livenessModel.setFeatureScore(topFeature.getScore()); setFail(livenessModel); } else { //IdentifyResult idResult = new IdentifyResult(user, index, topFeature.getScore()); setFail(livenessModel); } } else { setFail(livenessModel); } } else if (featureCheckMode == 4) { // 目前仅支持 float score = faceModel .getFaceSearch() .compare( BDFaceSDKCommon.FeatureType.BDFACE_FEATURE_TYPE_ID_PHOTO, livenessModel.getFeature(), secondFeature, true); livenessModel.setScore(score); if (score > threholdScore) { /*faceId = livenessModel.getFaceInfo().faceID; trackTime = System.currentTimeMillis(); faceAdoptModel = livenessModel; failNumber = 0; isFail = false;*/ setFail(livenessModel); } else { setFail(livenessModel); } } livenessModel.setCheckDuration(System.currentTimeMillis() - startFeature); } } /** * 金融活检-检测-活体 * * @param bdFaceImageConfig 可见光YUV 数据流 * @param bdNirFaceImageConfig 红外YUV 数据流 * @param bdDepthFaceImageConfig 深度depth 数据流 * @param bdFaceCheckConfig 识别参数 * @param faceDetectCallBack */ public void onDetectSilentLiveCheck( final BDFaceImageConfig bdFaceImageConfig, final BDFaceImageConfig bdNirFaceImageConfig, final BDFaceImageConfig bdDepthFaceImageConfig, final BDFaceCheckConfig bdFaceCheckConfig, final FaceDetectCallBack faceDetectCallBack) { long startTime = System.currentTimeMillis(); // 创建检测结果存储数据 LivenessModel livenessModel = new LivenessModel(); // 创建检测对象,如果原始数据YUV,转为算法检测的图片BGR // TODO: 用户调整旋转角度和是否镜像,手机和开发版需要动态适配 BDFaceImageInstance rgbInstance = getBdImage(bdFaceImageConfig, bdFaceCheckConfig.darkEnhance); livenessModel.setTestBDFaceImageInstanceDuration(System.currentTimeMillis() - startTime); onTrack( rgbInstance, livenessModel, new DetectListener() { @Override public void onDetectSuccess(FaceInfo[] faceInfos, BDFaceImageInstance rgbInstance) { // 保存人脸特征点 livenessModel.setLandmarks(faceInfos[0].landmarks); // 保存人脸图片 livenessModel.setBdFaceImageInstance(rgbInstance.getImage()); // 调用绘制人脸框接口 if (faceDetectCallBack != null) { faceDetectCallBack.onFaceDetectDarwCallback(livenessModel); } // 送检识别 onSilentLivenessCheck( rgbInstance, bdNirFaceImageConfig, bdDepthFaceImageConfig, bdFaceCheckConfig, livenessModel, startTime, faceDetectCallBack, faceInfos); } @Override public void onDetectFail() { // 流程结束销毁图片,开始下一帧图片检测,否着内存泄露 if (faceDetectCallBack != null) { faceDetectCallBack.onFaceDetectCallback(null); livenessModel.setBdFaceImageInstance(rgbInstance.getImage()); faceDetectCallBack.onFaceDetectDarwCallback(livenessModel); faceDetectCallBack.onTip(0, "未检测到人脸"); } } }); } /** * 金融活检-活体 * * @param rgbInstance 可见光底层送检对象 * @param nirBDFaceImageConfig 红外YUV 数据流 * @param depthBDFaceImageConfig 深度depth 数据流 * @param livenessModel 检测结果数据集合 * @param startTime 开始检测时间 * @param faceDetectCallBack */ public void onSilentLivenessCheck( final BDFaceImageInstance rgbInstance, final BDFaceImageConfig nirBDFaceImageConfig, final BDFaceImageConfig depthBDFaceImageConfig, final BDFaceCheckConfig bdFaceCheckConfig, final LivenessModel livenessModel, final long startTime, final FaceDetectCallBack faceDetectCallBack, final FaceInfo[] fastFaceInfos) { if (future2 != null && !future2.isDone()) { // 流程结束销毁图片,开始下一帧图片检测,否着内存泄露 return; } future2 = es2.submit( new Runnable() { @Override public void run() { onDetect( bdFaceCheckConfig, rgbInstance, fastFaceInfos, livenessModel, new DetectListener() { @Override public void onDetectSuccess(FaceInfo[] faceInfos, BDFaceImageInstance rgbInstance) { // 人脸id赋值 if (mLastFaceId != fastFaceInfos[0].faceID) { mLastFaceId = fastFaceInfos[0].faceID; mRgbLiveList.clear(); mNirLiveList.clear(); } if (bdFaceCheckConfig == null) { if (faceDetectCallBack != null) { faceDetectCallBack.onFaceDetectCallback(livenessModel); } return; } // 最优人脸控制 if (!onBestImageCheck(livenessModel, bdFaceCheckConfig, faceDetectCallBack)) { livenessModel.setQualityCheck(true); if (faceDetectCallBack != null) { faceDetectCallBack.onFaceDetectCallback(livenessModel); } return; } onQualityCheck( faceInfos, bdFaceCheckConfig.bdQualityConfig, faceDetectCallBack, new QualityListener() { @Override public void onQualitySuccess() { livenessModel.setQualityCheck(false); // 获取LivenessConfig liveCheckMode 配置选项:【不使用活体:0】;【RGB活体:1】;【RGB+NIR活体:2】;【RGB+Depth活体:3】;【RGB+NIR+Depth活体:4】 // TODO 活体检测 BDLiveConfig bdLiveConfig = bdFaceCheckConfig.bdLiveConfig; boolean isLiveCheck = bdFaceCheckConfig.bdLiveConfig != null; if (isLiveCheck) { long startRgbTime = System.currentTimeMillis(); boolean rgbLiveStatus = faceModel .getFaceLive() .strategySilentLive( BDFaceSDKCommon.LiveType.BDFACE_SILENT_LIVE_TYPE_RGB, rgbInstance, faceInfos[0], bdLiveConfig.framesThreshold, bdLiveConfig.rgbLiveScore); livenessModel.setRGBLiveStatus(rgbLiveStatus); livenessModel.setRgbLivenessDuration( System.currentTimeMillis() - startRgbTime); } // TODO nir活体检测 BDFaceImageInstance nirInstance = null; boolean isHaveNirImage = nirBDFaceImageConfig != null && isLiveCheck; if (isHaveNirImage) { // 创建检测对象,如果原始数据YUV-IR,转为算法检测的图片BGR // TODO: 用户调整旋转角度和是否镜像,手机和开发版需要动态适配 nirInstance = getBdImage(nirBDFaceImageConfig, false); livenessModel.setBdNirFaceImageInstance(nirInstance.getImage()); // 避免RGB检测关键点在IR对齐活体稳定,增加红外检测 long startIrDetectTime = System.currentTimeMillis(); BDFaceDetectListConf bdFaceDetectListConf = new BDFaceDetectListConf(); bdFaceDetectListConf.usingDetect = true; FaceInfo[] faceInfosIr = faceModel .getFaceNirDetect() .detect( BDFaceSDKCommon.DetectType.DETECT_NIR, BDFaceSDKCommon.AlignType .BDFACE_ALIGN_TYPE_NIR_ACCURATE, nirInstance, null, bdFaceDetectListConf); bdFaceDetectListConf.usingDetect = false; livenessModel.setIrLivenessDuration( System.currentTimeMillis() - startIrDetectTime); // LogUtils.e(TIME_TAG, "detect ir time = " + livenessModel.getIrLivenessDuration()); if (faceInfosIr != null && faceInfosIr.length > 0) { FaceInfo faceInfoIr = faceInfosIr[0]; long startNirTime = System.currentTimeMillis(); boolean nirLiveStatus = faceModel .getFaceLive() .strategySilentLive( BDFaceSDKCommon.LiveType .BDFACE_SILENT_LIVE_TYPE_NIR, nirInstance, faceInfoIr, bdLiveConfig.framesThreshold, bdLiveConfig.nirLiveScore); livenessModel.setNIRLiveStatus(nirLiveStatus); livenessModel.setIrLivenessDuration( System.currentTimeMillis() - startNirTime); } nirInstance.destory(); } // TODO depth活体检测 if (depthBDFaceImageConfig != null) { fastFaceInfos[0].landmarks = faceInfos[0].landmarks; // TODO: 用户调整旋转角度和是否镜像,适配Atlas 镜头,目前宽和高400*640,其他摄像头需要动态调整,人脸72 个关键点x 坐标向左移动80个像素点 float[] depthLandmark = new float[faceInfos[0].landmarks.length]; BDFaceImageInstance depthInstance; if (bdFaceCheckConfig.cameraType == 1) { System.arraycopy( faceInfos[0].landmarks, 0, depthLandmark, 0, faceInfos[0].landmarks.length); for (int i = 0; i < 144; i = i + 2) { depthLandmark[i] -= 80; } fastFaceInfos[0].landmarks = depthLandmark; } depthInstance = getBdImage(depthBDFaceImageConfig, false); livenessModel.setBdDepthFaceImageInstance(depthInstance.getImage()); // 创建检测对象,如果原始数据Depth long startDepthTime = System.currentTimeMillis(); boolean depthLiveStatus = faceModel .getFaceLive() .strategySilentLive( BDFaceSDKCommon.LiveType.BDFACE_SILENT_LIVE_TYPE_DEPTH, depthInstance, fastFaceInfos[0], bdLiveConfig.framesThreshold, bdLiveConfig.nirLiveScore); livenessModel.setDepthLiveStatus(depthLiveStatus); livenessModel.setDepthtLivenessDuration( System.currentTimeMillis() - startDepthTime); depthInstance.destory(); } // 流程结束,记录最终时间 livenessModel.setAllDetectDuration( System.currentTimeMillis() - startTime); // LogUtils.e(TIME_TAG, "all process time = " + livenessModel.getAllDetectDuration()); // 流程结束销毁图片,开始下一帧图片检测,否着内存泄露 // 显示最终结果提示 if (faceDetectCallBack != null) { faceDetectCallBack.onFaceDetectCallback(livenessModel); } } @Override public void onQualityFail(String detectFail, String occlusionFail) { livenessModel.setQualityOcclusion(occlusionFail); livenessModel.setQualityDetect(detectFail); livenessModel.setQualityCheck(true); if (faceDetectCallBack != null) { faceDetectCallBack.onFaceDetectCallback(livenessModel); } } }); } @Override public void onDetectFail() { if (faceDetectCallBack != null) { faceDetectCallBack.onFaceDetectCallback(livenessModel); } } }); } }); } private void onTrack(BDFaceImageInstance rgbInstance, LivenessModel livenessModel, DetectListener detectListener) { long startDetectTime = System.currentTimeMillis(); livenessModel.setRgbDetectDuration(System.currentTimeMillis() - startDetectTime); // track FaceInfo[] faceInfos = null; // 多人采用检测,不能跟踪 if (isMultiIdentify){ faceInfos = getDetectCheck(rgbInstance); } else{ faceInfos = getTrackCheck(rgbInstance); } // 检测结果判断 if (faceInfos == null || faceInfos.length == 0) { detectListener.onDetectFail(); return; } livenessModel.setTrackFaceInfo(faceInfos); // livenessModel.setFaceInfo(faceInfos[0]); // 修改为返回多人脸框 // livenessModel.setFaceInfos(faceInfos); livenessModel.setFaceInfos(faceInfos); livenessModel.setTrackLandmarks(faceInfos[0].landmarks); livenessModel.setTrackStatus(1); // detectListener.onDetectSuccess(faceInfos, rgbInstance); detectListener.onDetectSuccess(faceInfos, rgbInstance); /* if (size > 0) { byte[] featureArr1 = new byte[512]; byte[] featureArr2 = new byte[512]; float featureSize = faceModel.getFaceFeature().feature( BDFaceSDKCommon.FeatureType.BDFACE_FEATURE_TYPE_LIVE_PHOTO, rgbInstance, new_faceInfos[0].landmarks, featureArr1); Log.d("Attend", "compare_score1111:" + featureSize); featureSize = faceModel.getFaceFeature().feature( BDFaceSDKCommon.FeatureType.BDFACE_FEATURE_TYPE_LIVE_PHOTO, rgbInstance, new_faceInfos[0].landmarks, featureArr1); Log.d("Attend", "compare_score2222:" + featureSize); FaceSearch faceSearch = new FaceSearch(); float featureScore = faceSearch.compare( BDFaceSDKCommon.FeatureType.BDFACE_FEATURE_TYPE_LIVE_PHOTO, featureArr1, featureArr2, true); Log.d("Attend", "compare_score:" + featureScore); } */ } /** * 质量检测结果过滤,如果需要质量检测, * 需要调用 SingleBaseConfig.getBaseConfig().setQualityControl(true);设置为true, * 再调用 FaceSDKManager.getInstance().initConfig() 加载到底层配置项中 * * @param faceInfos * @param faceInfos * @param faceDetectCallBack * @return */ public void onQualityCheck( final FaceInfo[] faceInfos, final BDQualityConfig bdQualityConfig, final FaceDetectCallBack faceDetectCallBack, final QualityListener qualityListener) { if (bdQualityConfig == null) { qualityListener.onQualitySuccess(); return; } // 检测结果过滤 // 角度过滤 boolean checkQualityOk = true; String detectFail = ""; String occlusionFail = ""; if (faceInfos != null) { int size = faceInfos.length; for (int i = 0; i < size; i++) { StringBuffer stringBufferDetected = new StringBuffer(); StringBuffer stringBufferOcclusion = new StringBuffer(); FaceInfo faceInfo = faceInfos[i]; // 角度过滤 if (Math.abs(faceInfo.yaw) > bdQualityConfig.gesture) { faceDetectCallBack.onTip(-1, "人脸左右偏转角超出限制"); stringBufferDetected.append("人脸左右偏转角超出限制"); } else if (Math.abs(faceInfo.roll) > bdQualityConfig.gesture) { faceDetectCallBack.onTip(-1, "人脸平行平面内的头部旋转角超出限制"); stringBufferDetected.append("人脸平行平面内的头部旋转角超出限制"); } else if (Math.abs(faceInfo.pitch) > bdQualityConfig.gesture) { faceDetectCallBack.onTip(-1, "人脸上下偏转角超出限制"); stringBufferDetected.append("人脸上下偏转角超出限制"); } // 模糊结果过滤 float blur = faceInfo.bluriness; if (blur > bdQualityConfig.blur) { faceDetectCallBack.onTip(-1, "图片模糊"); stringBufferDetected.append("图片模糊"); } // 光照结果过滤 float illum = faceInfo.illum; Log.e("illum", "illum = " + illum); if (illum < bdQualityConfig.illum) { faceDetectCallBack.onTip(-1, "图片光照不通过"); stringBufferDetected.append("图片光照不通过"); } if (!checkMouthMask) { // 遮挡结果过滤 if (faceInfo.occlusion != null) { BDFaceOcclusion occlusion = faceInfo.occlusion; if (occlusion.leftEye > bdQualityConfig.leftEye) { // 左眼遮挡置信度 faceDetectCallBack.onTip(-1, "左眼遮挡"); stringBufferOcclusion.append("左眼遮挡"); } else if (occlusion.rightEye > bdQualityConfig.rightEye) { // 右眼遮挡置信度 faceDetectCallBack.onTip(-1, "右眼遮挡"); stringBufferOcclusion.append("右眼遮挡"); } else if (occlusion.nose > bdQualityConfig.nose) { // 鼻子遮挡置信度 faceDetectCallBack.onTip(-1, "鼻子遮挡"); stringBufferOcclusion.append("鼻子遮挡"); } else if (occlusion.mouth > bdQualityConfig.mouth) { // 嘴巴遮挡置信度 faceDetectCallBack.onTip(-1, "嘴巴遮挡"); stringBufferOcclusion.append("嘴巴遮挡"); } else if (occlusion.leftCheek > bdQualityConfig.leftCheek) { // 左脸遮挡置信度 faceDetectCallBack.onTip(-1, "左脸遮挡"); stringBufferOcclusion.append("左脸遮挡"); } else if (occlusion.rightCheek > bdQualityConfig.rightCheek) { // 右脸遮挡置信度 faceDetectCallBack.onTip(-1, "右脸遮挡"); stringBufferOcclusion.append("右脸遮挡"); } else if (occlusion.chin > bdQualityConfig.chinContour) { // 下巴遮挡置信度 faceDetectCallBack.onTip(-1, "下巴遮挡"); stringBufferOcclusion.append("下巴遮挡"); } } } if (!TextUtils.isEmpty(stringBufferDetected.toString()) || !TextUtils.isEmpty(stringBufferOcclusion.toString())) { if (!TextUtils.isEmpty(stringBufferDetected.toString())) { detectFail = stringBufferDetected.toString(); } if (!TextUtils.isEmpty(stringBufferOcclusion.toString())) { occlusionFail = stringBufferOcclusion.toString(); } checkQualityOk = false; } } } if (checkQualityOk) { qualityListener.onQualitySuccess(); return; } qualityListener.onQualityFail(detectFail, occlusionFail); } /** * 质量检测结果过滤,如果需要质量检测, * 需要调用 SingleBaseConfig.getBaseConfig().setQualityControl(true);设置为true, * 再调用 FaceSDKManager.getInstance().initConfig() 加载到底层配置项中 * * @param faceInfos * @param faceInfos * @param faceDetectCallBack * @return */ public boolean onQualityCheck( final FaceInfo[] faceInfos, final BDQualityConfig bdQualityConfig, final FaceDetectCallBack faceDetectCallBack) { if (bdQualityConfig == null) { return true; } boolean qualityCheck = false; if (faceInfos != null) { int size = faceInfos.length; for (int i = 0; i < size; ++i) { FaceInfo faceInfo = faceInfos[i]; boolean checkItem = true; // 角度过滤 if (Math.abs(faceInfo.yaw) > bdQualityConfig.gesture) { faceDetectCallBack.onTip(-1, "人脸左右偏转角超出限制"); checkItem = false; } else if (Math.abs(faceInfo.roll) > bdQualityConfig.gesture) { faceDetectCallBack.onTip(-1, "人脸平行平面内的头部旋转角超出限制"); checkItem = false; } else if (Math.abs(faceInfo.pitch) > bdQualityConfig.gesture) { faceDetectCallBack.onTip(-1, "人脸上下偏转角超出限制"); checkItem = false; } // 模糊结果过滤 float blur = faceInfo.bluriness; if (blur > bdQualityConfig.blur) { faceDetectCallBack.onTip(-1, "图片模糊"); checkItem = false; } // 光照结果过滤 float illum = faceInfo.illum; Log.e("illum", "illum = " + illum); if (illum < bdQualityConfig.illum) { faceDetectCallBack.onTip(-1, "图片光照不通过"); checkItem = false; } // 口罩识别不进行质量判断 if (!checkMouthMask) { // 遮挡结果过滤 if (faceInfo.occlusion != null) { BDFaceOcclusion occlusion = faceInfo.occlusion; if (occlusion.leftEye > bdQualityConfig.leftEye) { // 左眼遮挡置信度 faceDetectCallBack.onTip(-1, "左眼遮挡"); checkItem = false; } else if (occlusion.rightEye > bdQualityConfig.rightEye) { // 右眼遮挡置信度 faceDetectCallBack.onTip(-1, "右眼遮挡"); checkItem = false; } else if (occlusion.nose > bdQualityConfig.nose) { // 鼻子遮挡置信度 faceDetectCallBack.onTip(-1, "鼻子遮挡"); checkItem = false; } else if (occlusion.mouth > bdQualityConfig.mouth) { // 嘴巴遮挡置信度 faceDetectCallBack.onTip(-1, "嘴巴遮挡"); checkItem = false; } else if (occlusion.leftCheek > bdQualityConfig.leftCheek) { // 左脸遮挡置信度 faceDetectCallBack.onTip(-1, "左脸遮挡"); checkItem = false; } else if (occlusion.rightCheek > bdQualityConfig.rightCheek) { // 右脸遮挡置信度 faceDetectCallBack.onTip(-1, "右脸遮挡"); checkItem = false; } else if (occlusion.chin > bdQualityConfig.chinContour) { // 下巴遮挡置信度 faceDetectCallBack.onTip(-1, "下巴遮挡"); } } } else{ return true; } if (checkItem) { qualityCheck = true; return qualityCheck; } } } return qualityCheck; } /** * 质量检测结果过滤,如果需要质量检测, * 需要调用 SingleBaseConfig.getBaseConfig().setQualityControl(true);设置为true, * 再调用 FaceSDKManager.getInstance().initConfig() 加载到底层配置项中 * * @param faceInfo * @param faceInfo * @param faceDetectCallBack * @return */ public boolean onPersonQualityCheck( final FaceInfo faceInfo, final BDQualityConfig bdQualityConfig, final FaceDetectCallBack faceDetectCallBack) { if (bdQualityConfig == null) { return true; } if (faceInfo != null) { // 角度过滤 if (Math.abs(faceInfo.yaw) > bdQualityConfig.gesture) { faceDetectCallBack.onTip(-1, "人脸左右偏转角超出限制"); return false; } else if (Math.abs(faceInfo.roll) > bdQualityConfig.gesture) { faceDetectCallBack.onTip(-1, "人脸平行平面内的头部旋转角超出限制"); return false; } else if (Math.abs(faceInfo.pitch) > bdQualityConfig.gesture) { faceDetectCallBack.onTip(-1, "人脸上下偏转角超出限制"); return false; } // 模糊结果过滤 float blur = faceInfo.bluriness; if (blur > bdQualityConfig.blur) { faceDetectCallBack.onTip(-1, "图片模糊"); return false; } // 光照结果过滤 float illum = faceInfo.illum; Log.e("illum", "illum = " + illum); if (illum < bdQualityConfig.illum) { faceDetectCallBack.onTip(-1, "图片光照不通过"); return false; } // 口罩识别不进行质量判断 if (!checkMouthMask) { // 遮挡结果过滤 if (faceInfo.occlusion != null) { BDFaceOcclusion occlusion = faceInfo.occlusion; if (occlusion.leftEye > bdQualityConfig.leftEye) { // 左眼遮挡置信度 faceDetectCallBack.onTip(-1, "左眼遮挡"); } else if (occlusion.rightEye > bdQualityConfig.rightEye) { // 右眼遮挡置信度 faceDetectCallBack.onTip(-1, "右眼遮挡"); } else if (occlusion.nose > bdQualityConfig.nose) { // 鼻子遮挡置信度 faceDetectCallBack.onTip(-1, "鼻子遮挡"); } else if (occlusion.mouth > bdQualityConfig.mouth) { // 嘴巴遮挡置信度 faceDetectCallBack.onTip(-1, "嘴巴遮挡"); } else if (occlusion.leftCheek > bdQualityConfig.leftCheek) { // 左脸遮挡置信度 faceDetectCallBack.onTip(-1, "左脸遮挡"); } else if (occlusion.rightCheek > bdQualityConfig.rightCheek) { // 右脸遮挡置信度 faceDetectCallBack.onTip(-1, "右脸遮挡"); } else if (occlusion.chin > bdQualityConfig.chinContour) { // 下巴遮挡置信度 faceDetectCallBack.onTip(-1, "下巴遮挡"); } else { return true; } } } } return false; } /** * 检测-活体-特征- 全流程 * bluriness 模糊得分 * leftEye 左眼遮擋得分 * rightEye 右眼遮擋得分 * nose 鼻子遮擋得分 * mouth 嘴巴遮擋得分 * leftCheek 左臉眼遮擋得分 * rightCheek 右臉遮擋得分 */ private boolean selectQuality( float bluriness, float leftEye, float rightEye, float nose, float mouth, float leftCheek, float rightCheek, float chin) { return bluriness < 0.5 && leftEye < 0.75 && rightEye < 0.75 && nose < 0.75 && mouth < 0.75 && leftCheek < 0.75 && rightCheek < 0.75 && chin < 0.7; } // 人证核验特征提取 public float personDetect( final Bitmap bitmap, final byte[] feature, final BDFaceCheckConfig bdFaceCheckConfig, Context context) { if(bitmap==null||bitmap.isRecycled()){ return -1; } BDFaceImageInstance rgbInstance = new BDFaceImageInstance(bitmap); float ret = -1; FaceInfo[] faceInfos; BDQualityConfig bdQualityConfig = bdFaceCheckConfig == null ? null : bdFaceCheckConfig.bdQualityConfig; if (bdFaceCheckConfig != null) { bdFaceCheckConfig.bdFaceDetectListConfig.usingDetect = true; faceInfos = faceModel .getFaceDetectPerson() .detect( BDFaceSDKCommon.DetectType.DETECT_VIS, BDFaceSDKCommon.AlignType.BDFACE_ALIGN_TYPE_RGB_ACCURATE, rgbInstance, null, bdFaceCheckConfig.bdFaceDetectListConfig); } else { faceInfos = faceModel.getFaceDetectPerson().detect(BDFaceSDKCommon.DetectType.DETECT_VIS, rgbInstance); } if (faceInfos != null && faceInfos.length > 0) { // 判断质量检测,针对模糊度、遮挡、角度 if (onPersonQualityCheck(faceInfos[0], bdQualityConfig, new FaceQualityBack(context))) { ret = faceModel .getFacePersonFeature() .feature( BDFaceSDKCommon.FeatureType.BDFACE_FEATURE_TYPE_LIVE_PHOTO, rgbInstance, faceInfos[0].landmarks, feature); } } else { return -1; } return ret; } }