基于Android与OpenCV470的人脸检测、人脸采集、人脸识别系统

前言

与常规的Python + OpenCV方式不一样,本文为了使系统运行于手机中,
采用了Android版本的OpenCV,过程十分坎坷,但最终还是成功了,
特放出来,大家一起学习,参考。

结合OpenCV470版本,
使用其人脸检测模型,
采集并存储人脸特征,
检测人脸对比特征,
最终得出结果。

效果展示

在这里插入图片描述

核心代码

初始化

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_register_face);

    //创建本地模型目录
    FilesUtils.createFile(getExternalFilesDir(null).getAbsolutePath() + "/onnx");
    FilesUtils.createFile(getExternalFilesDir(null).getAbsolutePath() + "/faceImg");

    Log.e("【开始复制】", System.currentTimeMillis()+"");
    AssetsUtils.copyFolderFromAssetsToSD(this,"onnx", getExternalFilesDir(null).getAbsolutePath() + "/onnx");
    Log.e("【结束复制】", System.currentTimeMillis()+"");

    //核心线程1, 最大线程数1, 存活60秒,数组队列(确保至少有一个待执行任务), 默认线程工厂
    //丢弃队列中最旧的一个任务(即将被执行的一个任务),并执行最新的任务,如果执行器被关闭则任务被丢弃。–喜新弃旧型
    threadPool=new ThreadPoolExecutor(1, 1,
            60L, TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(1), Executors.defaultThreadFactory(), new ThreadPoolExecutor.DiscardOldestPolicy());

    appDatabase = FaceRoomDatabase.getInstance(this.getApplication());
    faceDao = appDatabase.userDao();

    initView();
}

人脸检测

faceDetectorYN.detect(rgba, faces);
ArrayList<Detection> detections = new ArrayList<>();
if(FaceConstant.IS_SHOW_CHECK_BOX) {
    // 检测输出人脸是一个 CV_32F 类型的二维数组,其行是检测到的人脸实例,列是人脸的位置和 5 个人脸地标。 每行的格式如下:
    // x1, y1, w, h, x_re, y_re, x_le, y_le, x_nt, y_nt, x_rcm, y_rcm, x_lcm, y_lcm
    for (int i = 0; i < faces.rows(); i++) {
        int x1 = (int) Math.round(faces.get(i, 0)[0]);
        int y1 = (int) Math.round(faces.get(i, 1)[0]);
        int w = (int) Math.round(faces.get(i, 2)[0]);
        int h = (int) Math.round(faces.get(i, 3)[0]);
        int x_re = (int) Math.round(faces.get(i, 4)[0]);
        int y_re = (int) Math.round(faces.get(i, 5)[0]);
        int x_le = (int) Math.round(faces.get(i, 6)[0]);
        int y_le = (int) Math.round(faces.get(i, 7)[0]);
        int x_nt = (int) Math.round(faces.get(i, 8)[0]);
        int y_nt = (int) Math.round(faces.get(i, 9)[0]);
        int x_rcm = (int) Math.round(faces.get(i, 10)[0]);
        int y_rcm = (int) Math.round(faces.get(i, 11)[0]);
        int x_lcm = (int) Math.round(faces.get(i, 12)[0]);
        int y_lcm = (int) Math.round(faces.get(i, 13)[0]);

        Detection detection = new Detection();

        // 画框框
        detection.setBoundingBox(new RectF(x1, y1, x1 + w, y1 + h));

        // 画文字
        Category category = new Category();
        category.setLabel("id:" + i);
        detection.setCategory(category);

        ArrayList<PointF> points = new ArrayList<>();
        // 画右眼点
        PointF point1 = new PointF(x_re, y_re);
        points.add(point1);
        // 画左眼点
        PointF point2 = new PointF(x_le, y_le);
        points.add(point2);
        // 画特鼻子点
        PointF point3 = new PointF(x_nt, y_nt);
        points.add(point3);
        // 画右嘴点
        PointF point4 = new PointF(x_rcm, y_rcm);
        points.add(point4);
        // 画左嘴点
        PointF point5 = new PointF(x_lcm, y_lcm);
        points.add(point5);

        detection.setPoint(points);

        detections.add(detection);
    }
}

人脸注册

//首次开始要定好本次刷脸编号(即记录同一个人)
if(successGetCnt == 0) {
    mFaceNo = UUID.randomUUID().toString();
}
successGetCnt++;
if(successGetCnt < FaceConstant.FACE_INFO_GET_CNT) {
    runOnUiThread(new Runnable() {
        @Override
        public void run() {
            imgFaceTipsBg.setVisibility(View.VISIBLE);
            tvFaceTips.setText("采集中..." + Math.round(successGetCnt*1f/FaceConstant.FACE_INFO_GET_CNT*100f) + "%");
        }
    });
    //存储数据
    if(successGetCnt >= (FaceConstant.FACE_INFO_GET_CNT - FaceConstant.FACE_INFO_SAVE_CNT))
        saveFaceInfo(faces, rgba, mFaceNo);
} else if(successGetCnt >= FaceConstant.FACE_INFO_GET_CNT){
    runOnUiThread(new Runnable() {
        @Override
        public void run() {
            imgFaceTipsBg.setVisibility(View.VISIBLE);
            imgFaceTipsBg.setImageResource(R.mipmap.ic_tips_gate_success);
            tvFaceTips.setText("采集完成");
        }
    });
}

人脸识别

private FaceEntity faceRecognizer(Mat faces, Mat img) {
    // 人脸识别(对比)
    // 人脸对齐
    Mat aligned_face1 = new Mat();
    faceRecognizerSF.alignCrop(img, faces.row(0), aligned_face1);
    // 人脸特征提取
    Mat feature1 = new Mat();
    faceRecognizerSF.feature(aligned_face1, feature1);
    //todo 不清楚为啥要克隆一下,如果不克隆,2个人脸会一直显示同一个人
    feature1 = feature1.clone();

    double maxScore_FR_COSINE = -10.0;
    double minScore_FR_NORM_L2 = 10.0;
    int maxScore_FR_COSINE_id = -1;
    int minScore_FR_NORM_L2_id = -1;

    Log.d(Thread.currentThread().getId()+ "【faceRecognizer2】", System.currentTimeMillis()+"");
    Gson gosn = new Gson();
    FaceEntity faceEntitys = null;
    double topScore = -10.0;
    for(int i=0; i<mFaceInfos.size(); i++) {
        Mat feature2 = new Mat(1, 128, CvType.CV_32FC1);
        double[] faceFeature2 = gosn.fromJson(mFaceInfos.get(i).getFaceFeature(), double[].class);
        feature2.put(0,0, faceFeature2);
        // 人脸对比
        double score_FR_COSINE = faceRecognizerSF.match(feature1, feature2, FaceRecognizerSF.FR_COSINE);
        double score_FR_NORM_L2 = faceRecognizerSF.match(feature1, feature2, FaceRecognizerSF.FR_NORM_L2);

        if(score_FR_COSINE > 0.363 && score_FR_COSINE > maxScore_FR_COSINE) {
            maxScore_FR_COSINE = score_FR_COSINE;
            maxScore_FR_COSINE_id = i;
        }

        if(score_FR_NORM_L2 < 1.128 && score_FR_COSINE < minScore_FR_NORM_L2) {
            minScore_FR_NORM_L2 = score_FR_NORM_L2;
            minScore_FR_NORM_L2_id = i;
        }
    }
    feature1.release();


    if(maxScore_FR_COSINE_id > -1 || maxScore_FR_COSINE_id > -1) {
        int id = maxScore_FR_COSINE_id > -1 ? maxScore_FR_COSINE_id : minScore_FR_NORM_L2_id;
        return mFaceInfos.get(id);
    } else {
        return null;
    }
}

完整代码可以联系我获取!!!

如果能帮到您,您又刚好方便,又刚好打开了微信,那就鼓励下咔嚓小豪哥我吖

在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值