Android开发Android开发经验谈Android技术知识

NDK 开发实战 - 实时人脸检测和识别

2019-04-01  本文已影响186人  红橙Darren

关于人脸检测和识别,应用的范围是非常广的,其实之前的《NDK开发前奏 - 实现支付宝人脸识别功能》 也有提到,只是那时并未具体的去分析算法和实现原理,这里笔者打算一步一步来分析和实现人脸识别,首先我们得要明确人脸检测和人脸识别是两个不同的概念,人脸检测是检测有人脸,人脸识别是匹配你是你,他们所采用的算法也是不一样的,这篇文章是基于人脸检测来实现人脸识别。我们先来看下已经实现了的人脸检测效果:

实时人脸检测

人脸马赛克大家可以忽略,并不是这里要关注的内容,是因为长得太帅了怕大家嫉妒。马赛克效果实现大家可以参考《图形图像处理 - 手写 QQ 说说图片处理效果》。首先我们不妨来思考一下,要实现像支付宝的人脸识别和员工刷脸签到等等,这样的一些应用功能,我们需要用到哪些知识?需要经过哪些步骤呢?其实这里分为两步,第一步是样本数据采集,第二步是检测和匹配。不过我们得先来看几个概念:均值,标准差,协方差矩阵,特征值,特征向量,PCA降维。

1. 均值,标准差,协方差矩阵

这几个都是概率论中的概念,我们随便举一个例子来算下即可,假设我的 Mat 数据如下:

Mat src = (Mat_<int>(3, 3) << 50, 50, 50, 60, 60, 60, 70, 70, 70);

均值:[60]

均值

标准差:[8.164965809277252]

标准差

协方差矩阵:[200, 200, 200; 200, 200, 200; 200, 200, 200]

协方差矩阵

2. 特征值,特征向量

关于特征值与特征向量这是线性代数的概念,还是老套路拿个例子过来算下,能算出来就可以了,同时大家也可以参考这篇文章:
https://en.wikipedia.org/wiki/Eigenvalues_and_eigenvectors

图片来源于百度经验 图片来源于百度经验 图片来源于百度经验

3. PCA降维

人脸识别肯定需要采集人脸样本,也就是相机采集过来的 Mat 数据,那么大量的数据怎么处理呢?这里就需要 PCA 降维了:

4. 样本训练

接下来就是代码层面的东西了,代码是很简单的就那么几句话,但关键其实还是在于理解里面的原理:

FaceDetection_trainingPattern(JNIEnv *env, jobject instance) {
    // 训练样本,这一步是在数据采集做的
    // train it
    vector<Mat> faces;
    vector<int> labels;

    // 样本比较少
    for (int i = 1; i <= 5; ++i) {
        for (int j = 1; j <= 5; ++j) {
            Mat face = imread(format("/storage/emulated/0/s%d/%d.pgm", i, j), 0);
            if (face.empty()) {
                LOGE("face mat is empty");
                continue;
            }
            // 确保大小一致
            resize(face, face, Size(128, 128));
            faces.push_back(face);
            labels.push_back(i);
        }
    }

    for (int i = 1; i <= 8; ++i) {
        Mat face = imread(format("/storage/emulated/0/face_%d.png", i), 0);
        if (face.empty()) {
            LOGE("face mat is empty");
            continue;
        }
        resize(face, face, Size(128, 128));
        faces.push_back(face);
        labels.push_back(11);
    }

    // 训练方法
    Ptr<BasicFaceRecognizer> model = EigenFaceRecognizer::create();
    // 采集了八张,同一个人 label 一样
    model->train(faces, labels);
    // 训练样本是 xml ,本地
    model->save("/storage/emulated/0/face_darren_pattern.xml");// 存的是处理的特征数据
    LOGE("樣本訓練成功");
}

4. 匹配识别

FaceDetection_faceDetection(JNIEnv *env, jobject instance,
                                                         jlong nativeObj) {
    Mat *src = reinterpret_cast<Mat *>(nativeObj);

    int width = src->rows;
    int height = src->cols;

    Mat grayMat;
    // 2. 转成灰度图,提升运算速度,灰度图所对应的 CV_8UC1 单颜色通道,信息量少 0-255 1u
    cvtColor(*src, grayMat, COLOR_BGRA2GRAY);

    // 4. 检测人脸,这是个大问题
    // 参数 1.1 会采取上采样和降采样 ,缩放比例
    // 参数 3 检测多少次
    // 参数 Size(width / 2, height / 2) 最小脸的大小
    std::vector<Rect> faces;
    cascadeClassifier.detectMultiScale(grayMat, faces, 1.1, 3, 0, Size(width / 2, height / 2));

    if (faces.size() != 1) {
        mosaicFace(*src);
        return;
    }

    // 把脸框出来
    Rect faceRect = faces[0];
    rectangle(*src, faceRect, Scalar(255, 0, 0, 255), 4, LINE_AA);

    // 不断检测,录入 10 张,张张嘴巴,眨眨眼睛 ,保证准确率
    // 还需要注意一点,确保人脸大小一致,reSize(128,128) ,确保收集到的人脸眼睛尽量在一条线上
    // 与服务端进行比对,是不是我

    // 用一个计数器,这里我们做及时的
    Mat face = (*src)(faceRect).clone();
    resize(face, face, Size(128, 128));
    cvtColor(face, face, COLOR_BGRA2GRAY);
    // 直方均衡,harr 检测人脸
    int label = model->predict(face);
    // 训练的时候存的是 11
    if (label == 11) {
        // 识别到了自己
        LOGE("识别到了自己");
        putText(*src, "Darren", Point(faceRect.x + 20, faceRect.y - 20),
                HersheyFonts::FONT_HERSHEY_COMPLEX, 1, Scalar(255, 0, 0, 255),2,LINE_AA);
    } else {
        // 不是自己
        LOGE("不是自己");
        putText(*src, "UnKnow", Point(faceRect.x + 20, faceRect.y - 20),
                HersheyFonts::FONT_HERSHEY_COMPLEX, 1, Scalar(255, 0, 0, 255),2,LINE_AA);
    }
    // 速度, 准确率, 人脸尽量正常
    mosaicFace((*src)(faceRect));
}
实时人脸识别

视频地址:https://pan.baidu.com/s/1lF92ev7_SqVNRNMih9eHkQ
视频密码:jc4k

上一篇下一篇

猜你喜欢

热点阅读