MrShi
2026-01-13 3a154bdb0a5aaa2c0ac3eac95a6ba747068bd454
keyCabinet-android/datalibrary/src/main/java/com/example/datalibrary/manager/FaceSDKManager.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,1994 @@
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<Boolean> mRgbLiveList = new ArrayList<>();
    private List<Boolean> 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<User> 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;
    private LivenessModel livenessModel;
    private String groupId;
    /**
     * 0:管理员,1:用户
     * @param groupId
     */
    public void setGroupId(String groupId){
        this.groupId = groupId;
    }
    /**
     * æ£€æµ‹-活体-特征-人脸检索流程
     *
     * @param bdFaceImageConfig      å¯è§å…‰YUV æ•°æ®æµ
     * @param bdNirFaceImageConfig   çº¢å¤–YUV æ•°æ®æµ
     * @param bdDepthFaceImageConfig æ·±åº¦depth æ•°æ®æµ
     * @param bdFaceCheckConfig      è¯†åˆ«å‚æ•°
     * @param faceDetectCallBack
     */
    public void onDetectCheck(
        final BDFaceImageConfig bdFaceImageConfig,
        final BDFaceImageConfig bdNirFaceImageConfig,
        final BDFaceImageConfig bdDepthFaceImageConfig,
        final BDFaceCheckConfig bdFaceCheckConfig,
        final FaceDetectCallBack faceDetectCallBack) {
        if (!FaceSDKManager.initModelSuccess) {
            return;
        }
        long startTime = System.currentTimeMillis();
        // åˆ›å»ºæ£€æµ‹ç»“果存储数据
        livenessModel = new LivenessModel();
        // åˆ›å»ºæ£€æµ‹å¯¹è±¡ï¼Œå¦‚果原始数据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<Boolean> 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<Boolean> 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<? extends Feature> 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<? extends Feature> 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(groupId)){
                        //为空,需要排序,优先取会员
                        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("1".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(groupId.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;
    }
}