| | |
| | | |
| | | private static BDFaceImageInstance rgbInstance =null; |
| | | private LivenessModel livenessModel; |
| | | private String groupId; |
| | | |
| | | /** |
| | | * 0:管理员,1:用户 |
| | | * @param groupId |
| | | */ |
| | | public void setGroupId(String groupId){ |
| | | if(livenessModel!=null){ |
| | | livenessModel.setGroupId(groupId); |
| | | } |
| | | this.groupId = groupId; |
| | | } |
| | | /** |
| | | * 检测-活体-特征-人脸检索流程 |
| | |
| | | |
| | | @Override |
| | | public void run() { |
| | | try { |
| | | // 获取BDFaceCheckConfig配置信息 |
| | | if (bdFaceCheckConfig == null) { |
| | | |
| | | // 获取BDFaceCheckConfig配置信息 |
| | | if (bdFaceCheckConfig == null) { |
| | | return; |
| | | } |
| | | onDetect( |
| | | bdFaceCheckConfig, |
| | | rgbInstance, |
| | | fastFaceInfos, |
| | | livenessModel, |
| | | new DetectListener() { |
| | | |
| | | return; |
| | | } |
| | | onDetect( |
| | | bdFaceCheckConfig, |
| | | rgbInstance, |
| | | fastFaceInfos, |
| | | livenessModel, |
| | | new DetectListener() { |
| | | @Override |
| | | public void onDetectSuccess(FaceInfo[] faceInfos, BDFaceImageInstance rgbInstance) { |
| | | |
| | | @Override |
| | | public void onDetectSuccess(FaceInfo[] faceInfos, BDFaceImageInstance rgbInstance) { |
| | | try { |
| | | |
| | | // 人脸id赋值 |
| | | if (mLastFaceId != fastFaceInfos[0].faceID) { |
| | | mLastFaceId = fastFaceInfos[0].faceID; |
| | | mRgbLiveList.clear(); |
| | | mNirLiveList.clear(); |
| | | } |
| | | // 人脸id赋值 |
| | | if (mLastFaceId != fastFaceInfos[0].faceID) { |
| | | mLastFaceId = fastFaceInfos[0].faceID; |
| | | mRgbLiveList.clear(); |
| | | mNirLiveList.clear(); |
| | | } |
| | | |
| | | if (bdFaceCheckConfig == null) { |
| | | if (bdFaceCheckConfig == null) { |
| | | |
| | | livenessModel.clearIdentifyResults(); |
| | | if (faceDetectCallBack != null) { |
| | | faceDetectCallBack.onFaceDetectCallback(livenessModel); |
| | | } |
| | | return; |
| | | } |
| | | livenessModel.clearIdentifyResults(); |
| | | if (faceDetectCallBack != null) { |
| | | faceDetectCallBack.onFaceDetectCallback(livenessModel); |
| | | } |
| | | return; |
| | | } |
| | | |
| | | // 最优人脸控制 |
| | | if (!onBestImageCheck(livenessModel, bdFaceCheckConfig, faceDetectCallBack)) { |
| | | livenessModel.setQualityCheck(true); |
| | | livenessModel.clearIdentifyResults(); |
| | | // 最优人脸控制 |
| | | if (!onBestImageCheck(livenessModel, bdFaceCheckConfig, faceDetectCallBack)) { |
| | | livenessModel.setQualityCheck(true); |
| | | livenessModel.clearIdentifyResults(); |
| | | |
| | | if (faceDetectCallBack != null) { |
| | | faceDetectCallBack.onFaceDetectCallback(livenessModel); |
| | | } |
| | | return; |
| | | } |
| | | if (faceDetectCallBack != null) { |
| | | faceDetectCallBack.onFaceDetectCallback(livenessModel); |
| | | } |
| | | return; |
| | | } |
| | | |
| | | // 质量检测未通过,销毁BDFaceImageInstance,结束函数 |
| | | // 质量检测未通过,销毁BDFaceImageInstance,结束函数 |
| | | |
| | | if (!onQualityCheck( |
| | | faceInfos, bdFaceCheckConfig.bdQualityConfig, faceDetectCallBack)) { |
| | | livenessModel.setQualityCheck(true); |
| | | livenessModel.clearIdentifyResults(); |
| | | if (!onQualityCheck( |
| | | faceInfos, bdFaceCheckConfig.bdQualityConfig, faceDetectCallBack)) { |
| | | livenessModel.setQualityCheck(true); |
| | | livenessModel.clearIdentifyResults(); |
| | | |
| | | if (faceDetectCallBack != null) { |
| | | faceDetectCallBack.onFaceDetectCallback(livenessModel); |
| | | } |
| | | return; |
| | | } |
| | | 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; |
| | | 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 (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); |
| | |
| | | } |
| | | } */ |
| | | |
| | | 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 (faceInfos.length == 1) { |
| | | livenessModel.setRgbLivenessScore(rgbScores[0]); |
| | | } |
| | | |
| | | 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.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); |
| | | } |
| | | }catch (RuntimeException e){ |
| | | faceDetectCallBack.onTip(1,e.getMessage()); |
| | | }catch (Exception e){ |
| | | faceDetectCallBack.onTip(1,e.getMessage()); |
| | | } |
| | | } |
| | | } |
| | | |
| | | // 流程结束,记录最终时间 |
| | | livenessModel.setAllDetectDuration(System.currentTimeMillis() - startTime); |
| | | // LogUtils.e(TIME_TAG, "all process time = " + livenessModel.getAllDetectDuration()); |
| | | // 流程结束销毁图片,开始下一帧图片检测,否着内存泄露 |
| | | @Override |
| | | public void onDetectFail() { |
| | | |
| | | if (nirInstance != null) { |
| | | nirInstance.destory(); |
| | | } |
| | | // 显示最终结果提示 |
| | | if (faceDetectCallBack != null) { |
| | | faceDetectCallBack.onFaceDetectCallback(livenessModel); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public void onDetectFail() { |
| | | |
| | | if (faceDetectCallBack != null) { |
| | | faceDetectCallBack.onFaceDetectCallback(livenessModel); |
| | | } |
| | | } |
| | | }); |
| | | if (faceDetectCallBack != null) { |
| | | faceDetectCallBack.onFaceDetectCallback(livenessModel); |
| | | } |
| | | } |
| | | }); |
| | | }catch (RuntimeException e){ |
| | | faceDetectCallBack.onTip(1,e.getMessage()); |
| | | }catch (Exception e){ |
| | | faceDetectCallBack.onTip(1,e.getMessage()); |
| | | } |
| | | } |
| | | }); |
| | | } |
| | |
| | | } |
| | | } |
| | | 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); |
| | | } |
| | | } |
| | | |
| | | /** |
| | |
| | | //System.out.println("==isOk==>匹配到数量"+featureResult.size()); |
| | | User user = null; |
| | | Feature topFeature = null; |
| | | if(TextUtils.isEmpty(livenessModel.getGroupId())){ |
| | | if(TextUtils.isEmpty(groupId)){ |
| | | //为空,需要排序,优先取会员 |
| | | for(Feature feat:featureResult) { |
| | | threholdScore = bdFaceCheckConfig.scoreThreshold; |
| | |
| | | //System.out.println("==isOk==>50"); |
| | | // 当前featureEntity 只有id+feature 索引,在数据库中查到完整信息 |
| | | User userOld = FaceApi.getInstance().getUserListById(feat.getId()); |
| | | if(livenessModel.getGroupId().equals(userOld.getGroupId())){ |
| | | if(groupId.equals(userOld.getGroupId())){ |
| | | user = userOld; |
| | | topFeature = feat; |
| | | break; |
| | |
| | | |
| | | @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); |
| | | try { |
| | | onDetect( |
| | | bdFaceCheckConfig, |
| | | rgbInstance, |
| | | fastFaceInfos, |
| | | livenessModel, |
| | | new DetectListener() { |
| | | @Override |
| | | public void onDetectSuccess(FaceInfo[] faceInfos, BDFaceImageInstance rgbInstance) { |
| | | try { |
| | | // 人脸id赋值 |
| | | if (mLastFaceId != fastFaceInfos[0].faceID) { |
| | | mLastFaceId = fastFaceInfos[0].faceID; |
| | | mRgbLiveList.clear(); |
| | | mNirLiveList.clear(); |
| | | } |
| | | // TODO nir活体检测 |
| | | BDFaceImageInstance nirInstance = null; |
| | | boolean isHaveNirImage = nirBDFaceImageConfig != null && isLiveCheck; |
| | | if (isHaveNirImage) { |
| | | if (bdFaceCheckConfig == null) { |
| | | |
| | | // 创建检测对象,如果原始数据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); |
| | | if (faceDetectCallBack != null) { |
| | | faceDetectCallBack.onFaceDetectCallback(livenessModel); |
| | | } |
| | | |
| | | nirInstance.destory(); |
| | | return; |
| | | } |
| | | // 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; |
| | | // 最优人脸控制 |
| | | if (!onBestImageCheck(livenessModel, bdFaceCheckConfig, faceDetectCallBack)) { |
| | | livenessModel.setQualityCheck(true); |
| | | |
| | | if (faceDetectCallBack != null) { |
| | | faceDetectCallBack.onFaceDetectCallback(livenessModel); |
| | | } |
| | | |
| | | 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(); |
| | | return; |
| | | } |
| | | // 流程结束,记录最终时间 |
| | | livenessModel.setAllDetectDuration( |
| | | System.currentTimeMillis() - startTime); |
| | | // LogUtils.e(TIME_TAG, "all process time = " + livenessModel.getAllDetectDuration()); |
| | | // 流程结束销毁图片,开始下一帧图片检测,否着内存泄露 |
| | | 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) { |
| | | |
| | | // 显示最终结果提示 |
| | | if (faceDetectCallBack != null) { |
| | | faceDetectCallBack.onFaceDetectCallback(livenessModel); |
| | | } |
| | | // 创建检测对象,如果原始数据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); |
| | | } |
| | | } |
| | | }); |
| | | }catch (RuntimeException e){ |
| | | faceDetectCallBack.onTip(1,e.getMessage()); |
| | | }catch (Exception e){ |
| | | faceDetectCallBack.onTip(1,e.getMessage()); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public void onQualityFail(String detectFail, String occlusionFail) { |
| | | livenessModel.setQualityOcclusion(occlusionFail); |
| | | livenessModel.setQualityDetect(detectFail); |
| | | livenessModel.setQualityCheck(true); |
| | | @Override |
| | | public void onDetectFail() { |
| | | |
| | | if (faceDetectCallBack != null) { |
| | | faceDetectCallBack.onFaceDetectCallback(livenessModel); |
| | | } |
| | | if (faceDetectCallBack != null) { |
| | | faceDetectCallBack.onFaceDetectCallback(livenessModel); |
| | | } |
| | | }); |
| | | } |
| | | |
| | | @Override |
| | | public void onDetectFail() { |
| | | |
| | | if (faceDetectCallBack != null) { |
| | | faceDetectCallBack.onFaceDetectCallback(livenessModel); |
| | | } |
| | | } |
| | | }); |
| | | } |
| | | }); |
| | | }catch (RuntimeException e){ |
| | | faceDetectCallBack.onTip(1,e.getMessage()); |
| | | }catch (Exception e){ |
| | | faceDetectCallBack.onTip(1,e.getMessage()); |
| | | } |
| | | } |
| | | }); |
| | | } |