OpenCV--人脸识别入门

一、背景数据集

本次用的数据集是opencv给出的教程里面的第一个数据集:The AT&T Facedatabase,又称为ORL人脸数据库,40个人,每人10张照片。照片在不同时间、不同光照、不同表情(睁眼闭眼、笑或者不笑)、不同人脸细节(戴眼镜或者不戴眼镜)下采集。所有的图像都在一个黑暗均匀的背景下采集的,正面竖直人脸(有些有轻微旋转)。

下载链接ORL人脸数据库
提取码:jqco

下载下来如下图示:
在这里插入图片描述
在这里插入图片描述


二、自己的人脸数据集

想要识别自己,单有别人的数据集还是不行的,还需要自己人脸的照片才行。这就需要我们收集自己的照片,然后和上面的那个数据集一起来训练模型。

2.1、人脸数据采集

下列程序调用openCV来拍照,按下P键拍照,按下Esc退出。

/** 拍照程序 **/
#include "mainwindow.h"
#include <QApplication>
#include <QDebug>

#include <opencv2/opencv.hpp>
using namespace cv;

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    namedWindow("photo",WINDOW_AUTOSIZE);

    VideoCapture cap(0);
    if(cap.isOpened())
        qDebug()<<"打开摄像头成功!";
    else
        qDebug()<<"打开摄像头失败!";

    Mat frame;
    int i=1;
    while (1)
    {
        char key = waitKey(100);
        cap >> frame;
        imshow("photo", frame);
        QString filename = QString("D:/Qt/Project/OpenCV/ORL_92x112/s41/s41_%1.bmp").arg(i);

        switch (key)
        {
            case 'p':
                imwrite(filename.toStdString(), frame);
                waitKey(500);
                i++;
                break;
            default:
                break;
        }

		int c = waitKey(0);
    	if ((char)c == 27)
        	return 0}

    return a.exec();
}

注意变换角度,变换表情,拍完之后,精选十张帅照,用于自己的人脸数据集
在这里插入图片描述

2.2、预处理

在得到自己的人脸照片之后,还需要对这些照片进行一些预处理才能拿去训练模型。所谓预处理,其实就是检测并分割出人脸,并改变人脸图片的大小,需与下载的数据集中图片大小(92 x 112)一致。使用下列程序可以自动检测人脸、分割人脸、调整大小和存储。

注意:调用opencv训练好的分类器和自带的检测函数检测人脸,需要将OpenCV源代码中分类器事先放到自己自己的工程目录中去;
分类器位置:D:\Qt\opencv-3.4.5\opencv-3.4.5\data\haarcascades\haarcascade_frontalface_default.xml
移动到:自己工程的build目录下(必须这个目录)

在这里插入图片描述

#include <QApplication>
#include <QDebug>

#include <opencv2/opencv.hpp>

using namespace cv;

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    QString face_cascade_name = "haarcascade_frontalface_default.xml";
    CascadeClassifier face_cascade;   //定义人脸分类器
    QString window_name = "Capture - Face detection";

    namedWindow(window_name.toStdString(),WINDOW_AUTOSIZE);

    //-- 1. Load the cascades
    if (!face_cascade.load(face_cascade_name.toStdString()))
    {
        qDebug()<<"--(!)Error loading face cascade";
        return -1;
    }

    for(int i=1; i<=10; i++)
    {
        Mat img = imread(QString("D:/Qt/Project/OpenCV/ORL_92x112/s41/sources/s41_%1.bmp").arg(i).toStdString());

        std::vector<Rect> faces;
        Mat img_gray;

        cvtColor(img, img_gray, COLOR_BGR2GRAY);
        equalizeHist(img_gray, img_gray);

        //-- Detect faces
        face_cascade.detectMultiScale(img_gray, faces, 1.1, 3, CV_HAAR_DO_ROUGH_SEARCH, Size(50, 50));

        for (size_t j = 0; j < faces.size(); j++)
        {
            Mat faceROI = img_gray(faces[j]);
            Mat MyFace;
            if (faceROI.cols > 100)
            {
                resize(faceROI, MyFace, Size(92, 112));
                QString  str = QString("D:/Qt/Project/OpenCV/ORL_92x112/s41/s41_%1.bmp").arg(i);
                imwrite(str.toStdString(), MyFace);
                imshow(window_name.toStdString(), MyFace);
            }
            waitKey(10);
        }
    }

    int c = waitKey(0);
    if ((char)c == 27)
        return 0;

    return a.exec();
}

在这里插入图片描述

至此,我们就得到和ORL人脸数据库人脸大小一致的自己的人脸数据集。然后我们把自己的作为第41个人,在我们下载的人脸文件夹下建立一个s41的子文件夹,把自己的人脸数据放进去。就成了这样下面这样,最后一个文件夹里面是我自己的头像照片:
在这里插入图片描述


三、csv文件生成

当我们写人脸模型的训练程序的时候,我们需要读取人脸和人脸对应的标签。直接在数据库中读取显然是低效的,所以我们用csv文件读取。csv文件中包含两方面的内容:一是每一张图片的位置所在;二是每一个人脸对应的标签,就是为每一个人编号。如下图示:
在这里插入图片描述

这个工作可以自己纯手动去完成,但是400多张图片,也是很费时间的,珍惜生命,走下面这条路吧!

打开命令行,切换到人脸数据集目录下,输入如下指令:

dir /b/s *bmp > at.txt

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
这个at.txt就是我们需要的csv文件,但是现在是只有路径没有标签的。使用下列程序,会生成一个at_temp.txt,这个既有路径又有标签,正是我们想要的,将之前的at.txt删除,将生成at_temp.txt改名为at.txt。

#include <QApplication>
#include <QDebug>
#include <QFile>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    QString fileName1 = "D:/Qt/Project/OpenCV/ORL_92x112/at.txt";
    QString fileName2 = "D:/Qt/Project/OpenCV/ORL_92x112/at_temp.txt";
    QFile file1(fileName1);
    QFile file2(fileName2);
    file1.open(QIODevice::ReadOnly | QIODevice::Text);
    file2.open(QIODevice::WriteOnly | QIODevice::Text);

    int i=0;
    int count = 0;
    while (!file1.atEnd())
    {
        count++;
        QTextStream stream(&file2);

        QByteArray line = file1.readLine();
        QString str(line);
        str.replace('\\','/');
        str.replace('\n',';');

        stream << str<<QString("%1").arg(i)<<"\n";

        if(count%10 == 0)
        {
            i++;
            count=0;
        }
    }

    file1.close();
    file2.close();

    qDebug()<<"Done!!!";

    return a.exec();
}

在这里插入图片描述


四、模型训练

#include <QApplication>

#include <opencv2\opencv.hpp>
#include <iostream>
#include <opencv2/face.hpp>
#include <QDebug>

using namespace cv;
using namespace std;
using namespace face;

//使用CSV文件去读图像和标签,主要使用stringstream和getline方法
static void read_csv(const string& filename, vector<Mat>& images, vector<int>& labels, char separator = ';') {
    std::ifstream file(filename.c_str(), ifstream::in);
    if (!file)
    {
        string error_message = "No valid input file was given, please check the given filename.";
        CV_Error(CV_StsBadArg, error_message);
    }
    string line, path, classlabel;
    while (getline(file, line))
    {
        stringstream liness(line);
        getline(liness, path, separator);
        getline(liness, classlabel);
        if (!path.empty() && !classlabel.empty())
        {
            images.push_back(imread(path, 0));
            labels.push_back(atoi(classlabel.c_str()));
        }
    }
}

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    //读取你的CSV文件路径.
    string fn_csv = "D:/Qt/Project/OpenCV/ORL_92x112/at.txt";

    // 2个容器来存放图像数据和对应的标签
    vector<Mat> images;
    vector<int> labels;
    // 读取数据. 如果文件不合法就会出错
    // 输入的文件名已经有了.
    try
    {
        read_csv(fn_csv, images, labels);
    }
    catch (cv::Exception& e)
    {
        cerr << "Error opening file \"" << fn_csv << "\". Reason: " << e.msg << endl;
        // 文件有问题,我们啥也做不了了,退出了
        exit(1);
    }
    // 如果没有读取到足够图片,也退出.
    if (images.size() <= 1)
    {
        string error_message = "This demo needs at least 2 images to work. Please add more images to your data set!";
        CV_Error(CV_StsError, error_message);
    }

    // 下面的几行代码仅仅是从你的数据集中移除最后一张图片
    //[gm:自然这里需要根据自己的需要修改,他这里简化了很多问题]
    Mat testSample = images[images.size() - 1];
    int testLabel = labels[labels.size() - 1];
    images.pop_back();
    labels.pop_back();
    // 下面几行创建了一个特征脸模型用于人脸识别,
    // 通过CSV文件读取的图像和标签训练它。
    // T这里是一个完整的PCA变换
    //如果你只想保留10个主成分,使用如下代码
    //      cv::createEigenFaceRecognizer(10);
    //
    // 如果你还希望使用置信度阈值来初始化,使用以下语句:
    //      cv::createEigenFaceRecognizer(10, 123.0);
    //
    // 如果你使用所有特征并且使用一个阈值,使用以下语句:
    //      cv::createEigenFaceRecognizer(0, 123.0);

    Ptr<FaceRecognizer> model = EigenFaceRecognizer::create();
    model->train(images, labels);
    model->save("MyFacePCAModel.xml");

    Ptr<FaceRecognizer> model1 = FisherFaceRecognizer::create();
    model1->train(images, labels);
    model1->save("MyFaceFisherModel.xml");

    Ptr<FaceRecognizer> model2 = LBPHFaceRecognizer::create();
    model2->train(images, labels);
    model2->save("MyFaceLBPHModel.xml");

    // 下面对测试图像进行预测,predictedLabel是预测标签结果
    int predictedLabel = model->predict(testSample);
    int predictedLabel1 = model1->predict(testSample);
    int predictedLabel2 = model2->predict(testSample);

    QString result_message = QString("Predicted class = %1 | Actual class = %2").arg(predictedLabel).arg(testLabel);
    QString result_message1 = QString("Predicted class = %1 | Actual class = %2").arg(predictedLabel1).arg(testLabel);
    QString result_message2 = QString("Predicted class = %1 | Actual class = %2").arg(predictedLabel2).arg(testLabel);
    qDebug() << result_message << endl;
    qDebug() << result_message1 << endl;
    qDebug() << result_message2 << endl;

    waitKey(0);

    return a.exec();
}

在这里插入图片描述
上述程序从人脸数据集中取了最后一张照片用于测试训练的模型。人脸数据集总共40+1个人,但是标签是从0开始的,所以最后的人脸标签是40,从上述程序运行结果可以看出,实际是40,预测也是40,说明模型训练成功,三个训练好的模型如下图:
在这里插入图片描述


五、人脸识别

#include <QApplication>

#include <opencv2\opencv.hpp>
#include <iostream>
#include <opencv2/face.hpp>
#include <QDebug>

using namespace cv;
using namespace std;
using namespace face;


int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    VideoCapture cap(0);    //打开默认摄像头
    if (!cap.isOpened())
    {
        return -1;
    }
    Mat frame;
    Mat edges;
    Mat gray;

    CascadeClassifier cascade;
    bool stop = false;
    //训练好的文件名称,放置在可执行文件同目录下
    cascade.load("D:/Qt/opencv-3.4.5/opencv-3.4.5/data/haarcascades/haarcascade_frontalface_default.xml");

    //Ptr<FaceRecognizer> modelPCA = createEigenFaceRecognizer();
    //modelPCA->load("MyFacePCAModel.xml");
    Ptr<FaceRecognizer> modelPCA = EigenFaceRecognizer::create();
    modelPCA->read("D:/Qt/Project/build-OpenCV-Desktop_Qt_5_13_2_MinGW_64_bit-Debug/MyFacePCAModel.xml");//训练的模型

    while(!stop)
    {
        cap >> frame;

        //建立用于存放人脸的向量容器
        vector<Rect> faces(0);

        cvtColor(frame, gray, CV_BGR2GRAY);
        //改变图像大小,使用双线性差值
        //resize(gray, smallImg, smallImg.size(), 0, 0, INTER_LINEAR);
        //变换后的图像进行直方图均值化处理
        equalizeHist(gray, gray);

        cascade.detectMultiScale(gray, faces,
            1.1, 2, 0
            //|CV_HAAR_FIND_BIGGEST_OBJECT
            //|CV_HAAR_DO_ROUGH_SEARCH
            | CV_HAAR_SCALE_IMAGE,
            Size(30, 30));

        Mat face;
        Point text_lb;

        for (size_t i = 0; i < faces.size(); i++)
        {
            if (faces[i].height > 0 && faces[i].width > 0)
            {
                face = gray(faces[i]);
                text_lb = Point(faces[i].x, faces[i].y);

                rectangle(frame, faces[i], Scalar(255, 0, 0), 1, 8, 0);
            }
        }

        Mat face_test;

        int predictPCA = 0;
        if (face.rows >= 120)
        {
            resize(face, face_test, Size(92, 112));
            imshow("缩放",face_test);

        }
        //Mat face_test_gray;
        //cvtColor(face_test, face_test_gray, CV_BGR2GRAY);

        if (!face_test.empty())
        {
            //测试图像应该是灰度图
            predictPCA = modelPCA->predict(face_test);
        }

        cout << predictPCA << endl;
        if (predictPCA == 40)
        {
            string name = "WangJiChuan";
            putText(frame, name, text_lb, FONT_HERSHEY_COMPLEX, 1, Scalar(255, 0, 0));
        }

        imshow("face", frame);
        if (waitKey(1) >= 0)
            stop = true;
    }

    return a.exec();
}

在这里插入图片描述
从上图可知,自己的人脸标签是35,当识别的人脸为35时,识别成功为自己,标记人脸,贴上名字。
在这里插入图片描述

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

贝勒里恩

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值