人脸识别示例解析(三)——微笑识别解析

上一节我们分析了人脸检测的原理,以及简化了示例代码。在检测过程中,我们主要使用了opencv的级联分类器CascadeClassifier,而它提供的detectMultiScale方法能够检测出一张图片中的一个或多个人脸,并用矩形标识出人脸位置。结果如下图所示

在这里插入图片描述

在检测过程当中,我们并没有训练模型,而是使用opencv提供给我们训练好的模型。事实上,opencv提供给我们很多模型。我们可以根据这些模型做相应的检测。

在这里插入图片描述

我们可以根据这些模型,套用上次代码做相应不同的检测,比如根据名字,上图中红色框框起的模型应该是用来检测眼睛部位的。但是我们不能直接套用上次代码,原因是双眼检测是在人脸检测的基础上进行的,也就是说,检测某人双眼位置之前,我们需要先检测出该人的人脸部位,继而在人脸范围内寻找双眼部位,这样比较合理。

代码应该如下所示;

// face1.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#define _CRT_SECURE_NO_WARNINGS

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

using namespace std;
using namespace cv;

void detectAndDraw(Mat& img, CascadeClassifier& cascade,
    CascadeClassifier& nestedCascade,
    double scale, bool tryflip);

string cascadeName;
string nestedCascadeName;

int main(int argc, const char** argv)
{

    string inputName;
    bool tryflip;
    double scale;

 CommandLineParser parser(argc, argv,
        "{help h||}"
        "{cascade|data/haarcascades/haarcascade_frontalface_alt2.xml|}"
        //双眼检测模型
        "{nested-cascade|data/haarcascades/haarcascade_eye.xml|}"
        "{scale|1|}{try-flip||}{@filename|D:/试验/test/smile.jpg|}"
    );
    if (parser.has("help"))
    {
        return 0;
    }
    //解析参数得到人脸检测模型路径
    cascadeName = parser.get<string>("cascade");
    //解析参数得到眼检测模型路径
    nestedCascadeName = parser.get<string>("nested-cascade");

    //开始解析其他参数
    scale = parser.get<double>("scale");
    if (scale < 1)
        scale = 1;
    tryflip = parser.has("try-flip");

    //解析输入文件名(就是我们想要检测的图像路径)
    inputName = parser.get<string>("@filename");
    if (!parser.check())
    {
        parser.printErrors();
        return 0;
    }

    //构造分类器用于检测脸部
    cv::CascadeClassifier cascade(cascadeName);

    //根据参数inputName加载被检测图像
    Mat image = imread(inputName);
    Mat gray;
    //转灰度图便于检测
    cvtColor(image, gray, cv::COLOR_BGRA2GRAY);
    vector<Rect> faces;
    //检测人脸
    cascade.detectMultiScale(gray,faces);

    //构造“内层”的分类器,该分类器用于检测双眼
    cv::CascadeClassifier nestedCascade(nestedCascadeName);
    for (int i = 0; i < faces.size(); i++)
    {
        //得到本人人脸检测范围,我们需要在这个范围内进行内层检测(双眼定位)
        Rect r = faces[i];
        //绘制人脸矩形
        cv::rectangle(image,r,Scalar(255, 0, 0));
        //定义一个矩形数组,用于标识检测到的双眼
        vector<Rect> faces2;
        //检测双眼矩形
        nestedCascade.detectMultiScale(image(r), faces2);
        for (int j = 0; j < faces2.size(); j++)
        {
            Mat image2 = image(r);
            cv::rectangle(image2, faces2[j], Scalar(0, 255, 0));
        }

    }
    //显示标记好的图像
    imshow("检测人脸", image);
    waitKey();
    return 0;
}

结果如下图所示

在这里插入图片描述

让我们看看还有什么模型

在这里插入图片描述

上面红框标记的好像是检测笑脸。笑脸跟双眼一样也是在脸部检测的基础上,再来看微笑有无出现,所以可以套用上述代码。事实上我们可以利用这个模型,来检测出人脸微笑的程度。但是这个检测是个动态的概念,就是说,对人脸笑颜动态展开,会比较敏感。

接下来我们有一个视频,人再逐渐展开微笑时,视频两边红色条块随着男人微笑程度而逐渐明亮

效果视频

在这里插入图片描述

代码如下

#define _CRT_SECURE_NO_WARNINGS

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

using namespace std;
using namespace cv;


void detectAndDraw(Mat& img, CascadeClassifier& cascade,
    CascadeClassifier& nestedCascade);

string cascadeName;
string nestedCascadeName;
VideoWriter writer("output.mp4", CV_FOURCC('m', 'p', '4', 'v'), 10, Size(700, 350));
int main(int argc, const char** argv)
{
    VideoCapture capture;
    Mat frame, image;
    string inputName;
    bool tryflip;


    CascadeClassifier cascade, nestedCascade;
    double scale;
    cv::CommandLineParser parser(argc, argv,
        "{help h||}{scale|1|}"
        "{cascade|data/haarcascades/haarcascade_frontalface_alt.xml|}"
        "{smile-cascade|data/haarcascades/haarcascade_smile.xml|}"
        "{try-flip||}{@input|D:/试验/test/smile.mp4|}");

    //以下参数解析
    if (parser.has("help"))
    {
        return 0;
    }
    cascadeName = samples::findFile(parser.get<string>("cascade"));
    nestedCascadeName = samples::findFile(parser.get<string>("smile-cascade"));
    inputName = parser.get<string>("@input");
    if (!parser.check())
    {
        return 1;
    }
    if (!cascade.load(cascadeName))
    {
        cerr << "错误:不能加载人脸检测模型" << endl; 
        return  -1;
    }
    if (!nestedCascade.load(nestedCascadeName)) 
    {
        cerr << "错误:不能加载内层检测模型" << endl;
        return -1;
    }
    if (inputName.empty() )
    {
        cerr << "错误:被检测图像路径为空" << endl;
        return -1;
    }
 
    //跟据参数“inputName”打开相应视频文件,并读取一帧图像
    capture.open(inputName);
    capture.read(frame);

    //循环读取视频每帧图像,并进行检测处理
    while (!frame.empty())
    {
        //检测处理以及微笑程度显示
        detectAndDraw(frame, cascade, nestedCascade);

        capture.read(frame);
    }
    writer.release();
    return 0;
}

//检测处理以及微笑程度显示
void detectAndDraw(Mat& img, CascadeClassifier& cascade,
    CascadeClassifier& nestedCascade)
{
    //定义矩形数组存放人脸检测结果
    vector<Rect> faces;
    Mat gray;

    //将原图转换为灰度图方便进一步处理
    cvtColor(img, gray, COLOR_BGR2GRAY);

    //检测人脸
    cascade.detectMultiScale(img, faces,
        1.1, 2, 0|CASCADE_SCALE_IMAGE);
   
    //对每个人脸检测结果进行处理(进一步检测微笑程度)
    Mat imgOut;
    for (size_t i = 0; i < faces.size(); i++)
    {
        //得到本人人脸检测范围,我们需要在这个范围内进行内层检测(微笑密度)
        Rect r = faces[i];
        Mat imgROI = img(r);

        //用于存储内层检测结果
        vector<Rect> nestedObjects;
        
        //绘制人脸矩形
        rectangle(img,r,Scalar(255,0,0));
        //在矩形范围内检测微笑密度
        nestedCascade.detectMultiScale(imgROI, nestedObjects,
            1.1, 0, 0 | CASCADE_SCALE_IMAGE);

        // 微笑密度和内层检测的结果大小有关(成正比)
        const int smile_neighbors = (int)nestedObjects.size();

        //结合前帧图像数据,经过换算得到真正的微笑密度
        //注意:以下两个变量为静态变量,循环调用此函数,它们的值不会重新初始化。
        static int max_neighbors = -1;
        static int min_neighbors = -1;
        if (min_neighbors == -1) min_neighbors = smile_neighbors;
        max_neighbors = MAX(max_neighbors, smile_neighbors);
        float intensityZeroOne = ((float)smile_neighbors - min_neighbors) / (max_neighbors - min_neighbors + 1);

        //根据微笑密度换算“红色”深度
        Scalar color = Scalar(0,0,(float)255 * intensityZeroOne);
        //绘制左右两边矩形,显示微笑程度
        rectangle(img, Point(0, 0), Point(img.cols / 10, img.rows), color, -1);
        rectangle(img, Point(img.cols*9/10, 0), Point(img.cols , img.rows), color, -1);
       
    }
    resize(img, imgOut,Size(700, 350));
    writer.write(imgOut);
}

事实你还可以依此原理玩出其他花样,比如用微笑触发烟花,植入手机,用微笑触发拍照,植入电子锁,用微笑打开日记,等等
在这里插入图片描述
最后值得一提的是,实际上opencv只是实现了经典的算法,但很多时候并不精确,还需要我们根据工作需要改进。

上一篇-----人脸检测解析
下一篇,人脸融合

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个基于Python OpenCV和face_recognition库的人脸识别示例: 1. 安装OpenCV和face_recognition库 在命令行中输入以下命令: ``` pip install opencv-python pip install face_recognition ``` 2. 下载示例图片和模型文件 下载示例图片和模型文件,保存在同一个文件夹中。示例图片包括两张人脸照片,分别为“Obama.jpg”和“Trump.jpg”。模型文件为“face_recognition_model.dat”。 3. 编写代码 ```python import cv2 import face_recognition # 加载示例图片和模型文件 obama_image = face_recognition.load_image_file("Obama.jpg") trump_image = face_recognition.load_image_file("Trump.jpg") face_encodings = [] face_encodings.append(face_recognition.face_encodings(obama_image)[0]) face_encodings.append(face_recognition.face_encodings(trump_image)[0]) # 打开摄像头 video_capture = cv2.VideoCapture(0) while True: # 读取一帧图像 ret, frame = video_capture.read() # 将图像从BGR转换为RGB rgb_frame = frame[:, :, ::-1] # 检测人脸 face_locations = face_recognition.face_locations(rgb_frame) # 对于每个检测到的人脸,进行人脸识别 for face_location in face_locations: # 提取人脸特征 face_encoding = face_recognition.face_encodings(rgb_frame, [face_location])[0] # 将人脸特征与已知人脸进行比对 matches = face_recognition.compare_faces(face_encodings, face_encoding) # 找到最佳匹配的人脸 best_match_index = matches.index(True) # 标注人脸 top, right, bottom, left = face_location cv2.rectangle(frame, (left, top), (right, bottom), (0, 0, 255), 2) cv2.putText(frame, "Unknown" if best_match_index == -1 else ["Obama", "Trump"][best_match_index], (left, top - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 2) # 显示图像 cv2.imshow('Video', frame) # 按下q键退出循环 if cv2.waitKey(1) & 0xFF == ord('q'): break # 释放摄像头 video_capture.release() # 关闭窗口 cv2.destroyAllWindows() ``` 4. 运行程序 在命令行中输入以下命令: ``` python face_recognition_example.py ``` 程序将打开摄像头,检测人脸并进行人脸识别。在图像中,已知的人脸将被标注为“Obama”或“Trump”,未知的人脸将被标注为“Unknown”。按下q键退出程序。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值