DNN 人脸识别

  • 使用facenet模型基于Torch,对每张图片进行多层卷积处理,计算出128个向量
  • 使用样本空间中的每张图的128个向量与采样的128个向量进行余弦相似度比较,0度为1,表示方向相同,90度为0,表示垂直正交。值越小,相似度越高,通过label存储的名字信息,可以把人检索出来,但是应该设置一定的阈值,否则会出现误检测

代码实现过程

  • 模型加载
  • 人脸检测与识别模型特征向量输出
  • 相似度比较,阈值控制,检索匹配
  • 显示结果

模型输入输出

  • 输入 Size:96*96,缩放:1/255.0,rgb:true
  • 输出:111*128 一张单通道 一行 128列的浮点数据

代码

#include <opencv2/opencv.hpp>
#include<opencv2/dnn.hpp>
#include <iostream>
#include <fstream>

using namespace cv;
using namespace cv::dnn;
using namespace std;
void recognize_face(Mat& face, Net net, vector<float> &fv);
float compare(vector<float> &fv1, vector<float> &fv2);
int main(int argc, char** argv) {
	string facenet_model = "/work/opencv_dnn/face_detector/openface.nn4.small2.v1.t7";
    string pb_model = "/work/opencv_dnn/face_detector/opencv_face_detector_uint8.pb";
    string pb_txt = "/work/opencv_dnn/face_detector/opencv_face_detector.pbtxt";
    // 载入模型
    Net net = readNetFromTensorflow(pb_model, pb_txt);   //人脸检测模型
    Net face_net = readNetFromTorch(facenet_model);      //人脸识别模型
    // 设置计算后台
    net.setPreferableBackend(DNN_BACKEND_OPENCV);
    net.setPreferableTarget(DNN_TARGET_CPU);

    face_net.setPreferableBackend(DNN_BACKEND_OPENCV);
    face_net.setPreferableTarget(DNN_TARGET_CPU);

    // 载入采集的样本数据
    vector<vector<float>> face_data;   //每张人脸的特征向量信息
    vector<string> labels;     //人脸对应名字
    vector<string> faces;      //采集的人脸图片名称 (含有路径)
    glob("/work/li_face", faces);    //遍历文件夹下所有的采集的人脸照片 存储其路径

    //分析每张照片  计算每张人脸照片的特征向量 为每张照片对应名称
    for (auto fn : faces) {
        vector<float> fv;
        Mat sample = imread(fn);
        recognize_face(sample, face_net, fv);
        face_data.push_back(fv);
        labels.push_back("Jing Jing");
    }

    faces.clear();   //清空容器 进行下一个人的人脸样本计算
    glob("/work/face_pic", faces);
    for (auto fn : faces) {
        vector<float> fv;
        Mat sample = imread(fn);
        recognize_face(sample, face_net, fv);
        face_data.push_back(fv);
        labels.push_back("Zhang Chao");
    }
    VideoCapture capture(0);     //打开摄像头
    Mat frame;
    while (true) {
        bool ret = capture.read(frame);
        if (!ret) break;
        flip(frame, frame, 1);   //画面镜像

        // 构建输入 (人脸检测模型)
        Mat blob = blobFromImage(frame, 1.0, Size(300, 300), Scalar(104, 177, 123), false, false);
        net.setInput(blob, "data");
        // 执行推理
        Mat detection = net.forward("detection_out");
        //将推理数据 进行格式化
        Mat detectionMat(detection.size[2], detection.size[3], CV_32F, detection.ptr<float>());
        float confidence_threshold = 0.5;
        float* curr_row;
        // 解析输出数据
        for (int i = 0; i < detectionMat.rows; i++) {
            curr_row = detectionMat.ptr<float>(i);
            curr_row+=2;    //获取图片id信息 用不到 只是为了让指针指向下一个数据
            float score = *curr_row++;                //获取可信度
            if (score > confidence_threshold) {    //可信度大于一定范围才进行处理
                //获取人脸矩形框的左上角 右下角坐标
                float tl_x = (*curr_row++) * frame.cols;
                float tl_y = (*curr_row++) * frame.rows;
                float br_x = (*curr_row++) * frame.cols;
                float br_y = (*curr_row++) * frame.rows;

                //根据坐标生成 矩形
                Rect box((int)tl_x, (int)tl_y, (int)(br_x - tl_x), (int)(br_y - tl_y));
                // 获取人脸ROI
                Mat face = frame(box);    //截取人脸部分
                // 人脸比对与识别
                vector<float> curr_fv;      //计算当前人脸的特征向量
                recognize_face(face, face_net, curr_fv);
                // 遍历计算与采样图片余弦相似度最小的人脸照片 并给出索引
                float minDist = 10;
                int index = 0;
                for (size_t i = 0; i < face_data.size(); i++) {
                    float dist = compare(face_data[i], curr_fv);
                    if (minDist > dist) {
                        minDist = dist;
                        index = i;
                    }
                }
                // 显示比较结果
                printf("index : %d, min distance : %.2f \n", index, minDist);
                if (minDist < 0.19 && index >= 0)   //阈值限定  显示人名
                {
                    putText(frame, format("%s", labels[index].c_str()), Point(box.x-10,box.y-10), FONT_HERSHEY_SIMPLEX, 1.5, Scalar(0, 0, 255), 2, 8);
                }
                rectangle(frame, box, Scalar(255, 0, 255), 1, 8, 0);
            }
        }
        imshow("face-detection-demo", frame);
        char c = waitKey(1);
        if (c == 27) { // ESC
            break;
        }
    }
    // 释放资源
    capture.release();
    destroyAllWindows();
    waitKey(0);
    return 0;
}

void recognize_face(Mat& face, Net net, vector<float> &fv) {
	Mat blob = blobFromImage(face, 1 / 255.0, Size(96, 96), Scalar(0, 0, 0), true, false);
	net.setInput(blob);
	Mat probMat = net.forward();
	Mat vec = probMat.reshape(1, 1);
	// printf("vec rows : %d, vec cols: %d \n", vec.rows, vec.cols);
	for (int i = 0; i < vec.cols; i++) {
		fv.push_back(vec.at<float>(0, i));
	}
}

float compare(vector<float> &fv1, vector<float> &fv2) {
	float dot = 0;
	float sum2 = 0;
	float sum3 = 0;
	for (int i = 0; i < fv1.size(); i++) {
		dot += fv1[i] * fv2[i];
		sum2 += pow(fv1[i], 2);
		sum3 += pow(fv2[i], 2);
	}
	float norm = sqrt(sum2)*sqrt(sum3);
	float similary = dot / norm;
	float dist = acos(similary) / CV_PI;
	return dist;
}

效果

训练图1

dlib opencv python 人脸检测 opencv dnn 人脸识别_#include


训练图2

dlib opencv python 人脸检测 opencv dnn 人脸识别_DNN_02

检测图

dlib opencv python 人脸检测 opencv dnn 人脸识别_#include_03