介绍

在本节中,我们将介绍用于人脸检测的 cv::FaceDetectorYN 类和用于人脸识别的 cv::FaceRecognizerSF 类。

模型

本模块需要预训练两个模型(ONNX 格式):

  • 人脸检测
    • 大小: 338KB
    • WIDER Face Val 集的结果:0.830(简单)、0.824(中)、0.708(硬)
  • 人脸识别
    • 大小: 36.9MB
    • 结果:
数据库 准确性 阈值 (normL2) 阈值(余弦)
LFW(英语:LFW) 99.60% 1.128 0.363
小牛 93.95% 1.149 0.340
CPLFW系列 91.05% 1.204 0.275
年龄DB-30 94.90% 1.202 0.277
CFP-FP型 94.80% 1.253 0.212

法典

C++蟒

  • 可下载代码: 点击这里
  • 代码一目了然:
    #include < opencv2/dnn.hpp>
    #include < opencv2/imgproc.hpp>
    #include < opencv2/highgui.hpp>
    #include < opencv2/objdetect.hpp>
    #include < iostream>
    使用命名空间 CV;
    使用命名空间 std;
    静态的
    void visualize(Mat& input, int frame, Mat& faces, double fps, int thickness = 2)
    {
    std::string fpsString = cv::format(“FPS : %.2f”, (float)fps);
    if(帧 >= 0)
    cout << “帧 ” << << “, ”;
    cout << “FPS: ” << fpsString << endl;
    forint i = 0; i <面。;i++)
    {
    打印结果
    cout << “Face ” << i
    << “,左上角坐标:(” <<面。at<float>(i, 0) << “, ” <<面。at<float>(i, 1) << “), ”
    << “框宽度:”<<面。at<float>(i, 2) << “,箱高:” <<面。at<float>(i, 3) << “, ”
    << “score: ” << cv::format“%.2f”, faces.at<float>(i, 14))
    << endl;
    绘制边界框
    rectangle(input, Rect2iint(faces.at<float>(i, 0)), int(faces.at<float>(i, 1)), int(faces.at<float>(i, 2)), int(faces.at<float>(i, 3))), 标量(0, 255, 0), 厚度);
    绘制地标
    circle(input, Point2iint(faces.at<float>(i, 4)), int(faces.at<float>(i, 5))), 2, 标量(255, 0, 0), 厚度);
    circle(input, Point2iint(faces.at<float>(i, 6)), int(faces.at<float>(i, 7))), 2, 标量(0, 0, 255), 厚度);
    circle(input, Point2iint(faces.at<float>(i, 8)), int(faces.at<float>(i, 9))), 2, 标量(0, 255, 0), 厚度);
    circle(input, Point2iint(faces.at<float>(i, 10)), int(faces.at<float>(i, 11))), 2, 标量(255, 0, 255), 厚度);
    circle(input, Point2iint(faces.at<float>(i, 12)), int(faces.at<float>(i, 13))), 2, 标量(0, 255, 255), 厚度);
    }
    putText(input, fpsString, Point(0, 15), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(0, 255, 0), 2);
    }
    int main(int argc, char** argv)
    {
    CommandLineParser 解析器(argc, argv,
    “{帮助 h | |打印此消息}”
    “{image1 i1 | |输入 image1 的路径。省略通过VideoCapture进行检测}”
    “{image2 i2 | |输入图像的路径2。当给出 image1 和 image2 参数时,程序会尝试在两张图像上找到一张人脸并运行人脸识别算法}”
    “{视频 v |0 |输入视频路径}”
    “{scale sc |1.0 |用于调整输入视频帧大小的比例因子}”
    “{fd_model fd |face_detection_yunet_2021dec.onnx|模型的路径。下载 yunet.onnx 在 https://github.com/opencv/opencv_zoo/tree/master/models/face_detection_yunet}”
    “{fr_model fr |face_recognition_sface_2021dec.onnx |人脸识别模型的路径。在 https://github.com/opencv/opencv_zoo/tree/master/models/face_recognition_sface}下载模型”
    “{score_threshold |0.9 |过滤掉分数< score_threshold}”
    “{nms_threshold |0.3 |禁止 iou >= nms_threshold}“ 的边界框
    “{top_k |5000 |在 NMS 之前保留top_k边界框}”
    “{保存 |假 |设置为 true 以保存结果。使用相机时,此标志无效}”
    );
    if (parser.has(“帮助”))
    {
    解析器.printMessage();
    返回 0;
    }
    字符串 fd_modelPath = parser.get<String>(“fd_model”);
    字符串 fr_modelPath = parser.get<String>(“fr_model”);
    float scoreThreshold = 解析器.get<float>(“score_threshold”);
    浮点 nmsThreshold = parser.get<float>(“nms_threshold”);
    int topK = 解析器.get<int>(“top_k”);
    bool save = parser.get<bool>(“保存”);
    浮点比例 = parser.get<float>(“scale”);
    双cosine_similar_thresh = 0.363;
    双l2norm_similar_thresh = 1.128;
    初始化 FaceDetectorYN
    Ptr<FaceDetectorYN> detector = FaceDetectorYN::create(fd_modelPath, “”Size(320, 320), scoreThreshold, nmsThreshold, topK);
    滴答计 tm;
    如果输入是图像
    if (parser.has(“image1”))
    {
    字符串 input1 = parser.get<String>(“image1”);
    子 image1 = imreadsamples::findFile(input1));
    如果 (image1.empty())
    {
    std::cerr << “无法读取图像:” << input1 << std::endl;
    返回 2;
    }
    int imageWidth = int(image1.cols * 比例);
    int imageHeight = int(image1.rows * 比例);
    resize(image1, image1, Size(imageWidth, imageHeight));
    tm。开始();
    在推理之前设置输入大小
    探测器->setInputSize(image1.size());
    面1;
    检测器->detect(image1, faces1);
    如果(faces1. < 1)
    {
    std::cerr << “在 ” << input1 << std::endl;
    返回 1;
    }
    tm。();
    在输入图像上绘制结果
    可视化(image1, -1, faces1, tm.获取FPS());
    如果 save 为 true,则保存结果
    if(保存)
    {
    cout << “保存结果.jpg...\n”;
    imwrite“结果.jpg”, image1);
    }
    可视化结果
    imshow“图像1”, 图像1);
    pollKey();处理 UI 事件以显示内容
    if (parser.has(“image2”))
    {
    字符串 input2 = parser.get<String>(“image2”);
    子 image2 = imreadsamples::findFile(input2));
    如果 (image2.empty())
    {
    std::cerr << “无法读取 image2:” << input2 << std::endl;
    返回 2;
    }
    tm。重置();
    tm。开始();
    探测器->setInputSize(image2.size());
    面2;
    检测器->检测(图像2,人脸2);
    如果 (faces2. < 1)
    {
    std::cerr << “在 ” << input2 << std::endl;
    返回 1;
    }
    tm。();
    visualize(image2, -1, faces2, tm.获取FPS());
    if(保存)
    {
    cout << “保存结果 2.jpg...\n”;
    imwrite“结果2.jpg”, image2);
    }
    imshow“图像2”, 图像2);
    初始化 FaceRecognizerSF
    Ptr<FaceRecognizerSF> faceRecognizer = FaceRecognizerSF::create(fr_modelPath,"");
    通过检测到的第一张面孔对齐和裁剪面部图像。
    Mat aligned_face1,aligned_face2;
    faceRecognizer->alignCrop(image1, faces1.row(0), aligned_face1);
    faceRecognizer->alignCrop(image2, faces2.row(0), aligned_face2);
    使用给定aligned_face运行特征提取
    垫子特征1,特征2;
    faceRecognizer->feature(aligned_face1, feature1);
    feature1 = feature1。克隆();
    faceRecognizer->feature(aligned_face2, feature2);
    特征 2 = 特征 2。克隆();
    double cos_score = faceRecognizer->match(feature1, feature2, FaceRecognizerSF::D isType::FR_COSINE);
    L2_score = faceRecognizer->match(feature1, feature2, FaceRecognizerSF::D isType::FR_NORM_L2);
    如果 (cos_score >= cosine_similar_thresh)
    {
    std::cout << “他们具有相同的身份;”;
    }
    {
    std::cout << “他们有不同的身份;”;
    }
    std::cout << “余弦相似度:”<< cos_score <<“,阈值:”<< cosine_similar_thresh <<”。(值越高表示相似度越高,最大值为 1.0)\n”;
    如果 (L2_score <= l2norm_similar_thresh)
    {
    std::cout << “他们具有相同的身份;”;
    }
    {
    std::cout << “他们有不同的身份。”;
    }
    std::cout << “ NormL2 距离:” << L2_score << “,阈值:” << l2norm_similar_thresh << ”。(值越低表示相似度越高,最小值为0.0)\n”;
    }
    cout << “按任意键退出...” << endl;
    waitKey(0);
    }
    {
    int frameWidth, frameHeight;
    VideoCapture 捕获;
    std::string video = 解析器。get<string>(“视频”);
    if (video.size() == 1 && isdigit(video[0]))
    捕获。open(parser.get<int>(“视频”));
    捕获。opensamples::findFileOrKeep(video));保留 GStreamer 流水线
    if(捕获。is已打开())
    {
    frameWidth = int(capture.getCAP_PROP_FRAME_WIDTH) * 比例);
    frameHeight = int(capture.getCAP_PROP_FRAME_HEIGHT) * 缩放);
    cout << “Video ” << video
    << “: width=” << frameWidth
    << “, height=” << frameHeight
    << endl;
    }
    {
    cout << “无法初始化视频捕获:”<<视频<<“\n”;
    返回 1;
    }
    detector->setInputSize(Size(frameWidth, frameHeight));
    cout << “按'空格'保存帧,任何其他键退出......” << endl;
    int nFrame = 0;
    (;;)
    {
    获取框架
    子框架;
    if (!capture.读取(帧))
    {
    cerr << “抓不住框架!停止\n”;
    ;
    }
    resize(frame, frame, Size(frameWidth, frameHeight));
    推理
    面;
    tm。开始();
    detector->detect(帧,人脸);
    tm。();
    垫子结果 = 帧。克隆();
    在输入图像上绘制结果
    visualize(result, nFrame, faces, tm.获取FPS());
    可视化结果
    imshow“Live”, 结果);
    int 键 = waitKey(1);
    bool saveFrame = 保存;
    如果(键 ==' ')
    {
    saveFrame = ;
    键 = 0;//处理
    }
    如果 (saveFrame)
    {
    std::string frame_name = cv::format“frame_%05d.png”, nFrame);
    std::string result_name = cv::format“result_%05d.jpg”, nFrame);
    cout << “保存 '” << frame_name << “' 和 '” << result_name << “' ...\n” ;
    imwrite(frame_name, 帧);
    imwrite(result_name, 结果);
    }
    ++n框架;
    if(密钥> 0)
    ;
    }
    cout << “已处理 ” << nFrame << “frames” << endl;
    }
    cout << “完成。” << endl;
    返回 0;
    }

解释

初始化 FaceDetectorYN
Ptr<FaceDetectorYN> detector = FaceDetectorYN::create(fd_modelPath, “”Size(320, 320), scoreThreshold, nmsThreshold, topK);
在推理之前设置输入大小
探测器->setInputSize(image1.size());
垫面1;
检测器->detect(image1, faces1);
如果 (faces1.rows < 1)
{
std::cerr << “在 ” << input1 << std::endl;
返回 1;
}

检测输出是 CV_32F 类型的二维数组,其行是检测到的人脸实例,列是人脸的位置和 5 个人脸特征点。每行的格式如下:faces

x1、y1、宽、高、x_re、y_re、x_le、y_le、x_nt、y_nt、x_rcm、y_rcm、x_lcm、y_lcm

,其中是左上角坐标,人脸边界框的宽度和高度,分别代表右眼、左眼、鼻尖、右嘴角和左嘴角的坐标。x1, y1, w, h{x, y}_{re, le, nt, rcm, lcm}

人脸识别

C++蟒

在人脸检测之后,运行下面的代码以从人脸图像中提取人脸特征。

初始化 FaceRecognizerSF
Ptr<FaceRecognizerSF> faceRecognizer = FaceRecognizerSF::create(fr_modelPath,"");
通过检测到的第一张面孔对齐和裁剪面部图像。
Mat aligned_face1,aligned_face2;
faceRecognizer->alignCrop(image1, faces1.row(0), aligned_face1);
faceRecognizer->alignCrop(image2, faces2.row(0), aligned_face2);
使用给定aligned_face运行特征提取
垫子特征1,特征2;
faceRecognizer->feature(aligned_face1, feature1);
feature1 = feature1。克隆();
faceRecognizer->feature(aligned_face2, feature2);
特征 2 = 特征 2。克隆();

获取两张人脸图像的人脸特征特征 1 和特征 2 后,运行下面的代码来计算两人脸之间的同一性差异。

double cos_score = faceRecognizer->match(feature1, feature2, FaceRecognizerSF::D isType::FR_COSINE);
L2_score = faceRecognizer->match(feature1, feature2, FaceRecognizerSF::D isType::FR_NORM_L2);

例如,如果余弦距离大于或等于 0.363,或者 normL2 距离小于或等于 1.128,则两个面具有相同的标识。

   在线教程

有需要的小伙伴,可以点击下方链接免费领取或者V扫描下方二维码免费领取🆓

请添加图片描述

人工智能书籍

第一阶段:零基础入门(3-6个月)

新手应首先通过少而精的学习,看到全景图,建立大局观。 通过完成小实验,建立信心,才能避免“从入门到放弃”的尴尬。因此,第一阶段只推荐4本最必要的书(而且这些书到了第二、三阶段也能继续用),入门以后,在后续学习中再“哪里不会补哪里”即可。

第二阶段:基础进阶(3-6个月)

熟读《机器学习算法的数学解析与Python实现》并动手实践后,你已经对机器学习有了基本的了解,不再是小白了。这时可以开始触类旁通,学习热门技术,加强实践水平。在深入学习的同时,也可以探索自己感兴趣的方向,为求职面试打好基础。

第三阶段:工作应用

这一阶段你已经不再需要引导,只需要一些推荐书目。如果你从入门时就确认了未来的工作方向,可以在第二阶段就提前阅读相关入门书籍(对应“商业落地五大方向”中的前两本),然后再“哪里不会补哪里”。

 有需要的小伙伴,可以点击下方链接免费领取或者V扫描下方二维码免费领取🆓

在这里插入图片描述

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐