环境:VS2012+Qt5+Opencv2.4.10,window10系统
功能:打开电脑摄像头进行人脸显示,可以拍照存储到本地,最重要的是能够实现人脸的检测与识别。
先讨论人脸的识别过程:
1、收集训练库,这里采用的是ORL人脸数据集,总共有40个人,每人10张,为了能够识别自己,需要采集自己不同角度以及光照的10张脸部分的照片,加入ORL数据集存储在s41文件夹内(PS:这里面主要是要制作一个CSV文件,即txt文件,把dataset中的所以图片的路径存储在txt文件中,然后可以通过Python的一个文件批处理的程序给每个路径的图片进行加上label,中间用“;”分号隔开;目的是为了接下来的训练数据读取图片准备的)。
2、训练。Opencv提供了三种方式来训练并产生xml分类文件。分类模型有EigenFaceRecognizer()、FisherFaceRecognizer()、LBPHFaceRecognizer()这三种。训练的代码也十分简洁。只需要三行代码即可,以LBP(局部二值化模式)为例!代码如下:
Ptr<FaceRecognizer> model2 = createLBPHFaceRecognizer();
model2->train(images, labels);
model2->save("My_Face_LBPHModel.xml");
其他两种都是这样的格式,是不是十分简单粗暴。
3、检测。这里面需要用到Opencv自带的人脸检测的级联分类器。haarcascade_frontalface_alt.xml,这在Opencv中data文件夹下的haarcascade中,这里面官方训练好了许多分类器模型,有兴趣的可以自己研究Haar特征LBP特征提取原理,这里不作详解;如果有需要交流的可以私信。这里给出检测的核心部分。
void dectectAndDisplay(Mat& frame)
{
vector faces;
Mat frame_gray;
cvtColor(frame,frame_gray,CV_BGR2GRAY);
equalizeHist(frame_gray,frame_gray);
face_cascade.detectMultiScale(frame_gray,faces,1.1,2,0|CV_HAAR_SCALE_IMAGE,Size(30,30));
for(int i = 0;i0.5,faces[i].y+faces[i].height*0.5);
ellipse(frame,center,Size(faces[i].width*0.5,faces[i].height*0.5),0,0,360,Scalar(255,0,255),4,8,0);
}
imshow(window_face,frame);
}
通过一个vector faces;将检测到的人脸部分以Rec类型存储在容器中,然后通过绘图函数直接绘出,以圆形圈出或者矩形框出都可以。检测的重点在
face_cascade.detectMultiScale(frame_gray,faces,1.1,2,0|CV_HAAR_SCALE_IMAGE,Size(30,30));
这个代码部分,这是实现多尺度检测人脸的,如果想详细了解分类型是如何做检测的可以参考这篇博文。
4、识别
识别部分就特别简单了,就一句
predictPCA = modelPCA->predict(face_test);
即可这里面传进来的face_test人脸图片需要进行预处理,归一化直方图(减小光照的影响),大小resize到(92,112)大小等操作,目的是需要跟数据集中的图片大小一致。最后再将返回检测的predictPCA的值和我的41(因为算在开始训练的时候加上了我自己共41个人,从1开始标签数据。同一个人标签一样,我标为41)进行比较,如果一致则putText进行显示我的名字,这个还是很简单的。
大致人脸识别的过程就是以上的说明。这仅仅是在VS中实现了这个功能,还没有结合Qt做界面,所以接下来的部分将介绍如何将人脸识别嵌入Qt中实现友好的人机交互界面的设计。人脸识别代码如下:
#include
#include
using namespace std;
using namespace cv;
//变量声明区
Mat frame;
Mat edges;
Mat gray;
CascadeClassifier cascade;
bool stop = false;
//函数声明区
static void HelpText();
int main()
{
HelpText();
VideoCapture cap(0); //打开默认摄像头
if (!cap.isOpened())
{
return -1;
}
//训练好的文件名称,放置在可执行文件同目录下
cascade.load("haarcascade_frontalface_alt.xml");
Ptr modelPCA = createEigenFaceRecognizer();
modelPCA->load("MyFacePCAModel.xml");
while (!stop)
{
cap >> frame;
//建立用于存放人脸的向量容器
vector faces(0);
cvtColor(frame, gray, CV_BGR2GRAY);
//改变图像大小,使用双线性差值
//resize(gray, smallImg, smallImg.size(), 0, 0, INTER_LINEAR);
//变换后的图像进行直方图均值化处理
equalizeHist(gray, gray);
double timeA = (double)cvGetTickCount();
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));
double timeB = ((double)cvGetTickCount() - timeA)/(double)cvGetTickFrequency()*1000000;
cout<<"DetcetMultiscale is :"<char AA[20];
for (size_t i = 0; i < faces.size(); i++)
{
if (faces[i].height > 0 && faces[i].width > 0)
{
face = gray(faces[i]);
text_lbd = Point(faces[i].x+faces[i].height*0.5, faces[i].y+faces[i].width*0.5);
text_lb = Point(faces[i].x, faces[i].y);
cout<<"x:"<0.5<<" "<<"y:"<<" "<0.5<sprintf(AA,"%d %d",faces[i].x+faces[i].height*0.5,faces[i].y+faces[i].width*0.5);
cout<<"AA is :"<2,1.0,Scalar(255,122,134),2,8);
rectangle(frame, faces[i], Scalar(255, 0, 0), 1, 8, 0);
}
}
Mat face_test;
int predictPCA = 0;
if (face.rows >= 70)
{
resize(face, face_test, Size(92, 112));
//resize(face,);
}
//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 == 41)
{
string name = "W.Chaolong";
putText(frame, name, text_lb, FONT_HERSHEY_COMPLEX, 1, Scalar(0, 0, 255));
}
imshow("face", frame);
if (waitKey(10) >= 0)
stop = true;
}
return 0;
}
static void HelpText()
{
system("color 5F");
cout<<"********************************************\n"<cout<<"** 应用AT&T数据集ORL **\n"<cout<<"** 实现识别自己 **\n"<cout<<"********************************************"<
本部分即将介绍Qt+Opencv实现一个简单的界面环境。如果有Qt不熟悉的可以参考如下[《Qt学习之路2》](https://www.devbean.net/2012/08/qt-study-road-2-catelog/)系列教程。如果不会配置在VS中配置Qt的可以参照这篇VS+Qt+Opencv博客。如果还有不懂的可以私信还有主要就是通过互联网解决问题。
在这一部分如果只是刚开始接触Qt的话,可以在网上先搜索在Qt中打照片以及通过按钮打开视频来学习,C++比较好的话很容易上手。 这个主要就是实现了人脸的检测,因为没有采集检测这个人的图片集,所以只能检测出来不能进行识别
在实验我自己的时候是可以检测并识别出来,而且可以putText在人脸左上方。后期具体的过程再补!
先贴上代码如下:
1、Ctest_Qt_VideoCapture_0607.hpp(因为我的文件名字就是这个)
#ifndef CTEST_QT_VIDEOCAPTURE_0607_H
#define CTEST_QT_VIDEOCAPTURE_0607_H
#include
#include "ui_ctest_qt_videocapture_0607.h"
#include
#include
#include
#include
//#include<>
using namespace cv;
using namespace std;
class Ctest_Qt_VideoCapture_0607 : public QMainWindow
{
Q_OBJECT
public:
Ctest_Qt_VideoCapture_0607(QWidget *parent = 0);
~Ctest_Qt_VideoCapture_0607();
QImage Mat2Qimage(const Mat& mat);
// Mat BackFrame();
private:
Ui::Ctest_Qt_VideoCapture_0607Class ui;
Mat frame;
Mat CannyImg;
QImage TakeP;
bool start;
char file[20];
//人脸识别
Mat edges;
Mat gray;
QString tempStr;
VideoCapture cap;
//CvCapture *cap;
QTimer *timer;
int AA;
private slots:
void OpenVideoCap();
void CloseVideoCap();
void TakePhoto();
//人脸识别
void FaceRecognition();
//初始化
void Start();
void Quit();
void Slider(int);
//void checkBox();
};
#endif // CTEST_QT_VIDEOCAPTURE_0607_H
2、Ctest_Qt_VideoCapture_0607.cpp
#include "ctest_qt_videocapture_0607.h"
Ctest_Qt_VideoCapture_0607::Ctest_Qt_VideoCapture_0607(QWidget *parent)
: QMainWindow(parent)
{
ui.setupUi(this);
ui.pushButton_3->setEnabled(false);
ui.pushButton->setEnabled(false);
ui.pushButton_2->setEnabled(false);
ui.pushButton_6->setEnabled(false);
timer = new QTimer(this);
timer->start(50);
}
Ctest_Qt_VideoCapture_0607::~Ctest_Qt_VideoCapture_0607()
{
}
void Ctest_Qt_VideoCapture_0607::OpenVideoCap()
{
//VideoCapture cap(0);
start = true;
while(start)
{
cap>>frame;
imshow("Camera",frame);
waitKey(10);
timer->start(50);
QImage img = Mat2Qimage(frame);
//Canny(frame,CannyImg,100,255);
//QImage img1 = Mat2Qimage(CannyImg);
TakeP = img;
ui.label->setPixmap(QPixmap::fromImage(img));
//ui.label1->setPixMap(QPixmap::fromImage(img1));
//ui.label_1->setPixmap(QPixmap::fromImage(img1));
//ui.label->setScaledContents(true);
int c = waitKey(30);
if(c>=0)
{
waitKey(0);
}
}
}
QImage Ctest_Qt_VideoCapture_0607::Mat2Qimage(const Mat& mat)
{
if(mat.type()==CV_8UC1)
{
QVector colorTable;
for(int i=0;i<256;i++)
{
colorTable.push_back(qRgb(i,i,i));
}
const uchar* qImageBuff = (const uchar*)mat.data;
QImage img(qImageBuff,mat.cols,mat.rows,mat.step,QImage::Format_Indexed8);
img.setColorTable(colorTable);
return img;
}
if(mat.type()==CV_8UC3)
{
const uchar* qImageBuffer = (const uchar*)mat.data;
QImage img(qImageBuffer,mat.cols,mat.rows,mat.step,QImage::Format_RGB888);
return img.rgbSwapped();
}
else
{
return QImage();
}
}
void Ctest_Qt_VideoCapture_0607::CloseVideoCap()
{
start = false;
}
void Ctest_Qt_VideoCapture_0607::TakePhoto()
{
static int i =1;
ui.pushButton_3->setEnabled(false);
ui.label_1->setPixmap(QPixmap::fromImage(TakeP));
sprintf(file,"../Images/Pic_%d.jpg",i);
i++;
imwrite(file,frame);
waitKey(10);
ui.pushButton_3->setEnabled(true);
//cout<<"jjjjj"<
}
//人脸识别
void Ctest_Qt_VideoCapture_0607::FaceRecognition()
{
CascadeClassifier cascade;
//bool stop = false;
//while中
vector faces(0);
Mat face;
Point text_lb;
Point text_lbd;
char AA[20];
Mat face_test;
int predictPCA = 0;
//训练好的文件名称,放置在可执行文件同目录下
cascade.load("haarcascade_frontalface_alt2.xml");
Ptr modelPCA = createEigenFaceRecognizer();
modelPCA->load("MyFacePCAModel.xml");
while (start)
{
cap >> frame;
//建立用于存放人脸的向量容器
QImage img4 = Mat2Qimage(frame);
ui.label->setPixmap(QPixmap::fromImage(img4));
cvtColor(frame, gray, CV_BGR2GRAY);
//改变图像大小,使用双线性差值
//resize(gray, smallImg, smallImg.size(), 0, 0, INTER_LINEAR);
//变换后的图像进行直方图均值化处理
equalizeHist(gray, gray);
double timeA = (double)cvGetTickCount();
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));
double timeB = ((double)cvGetTickCount() - timeA)/(double)cvGetTickFrequency()*1000;
ui.label_3->setText(tempStr.setNum(timeB));
cout<<"DetcetMultiscale is :"<for (size_t i = 0; i < faces.size(); i++)
{
if (faces[i].height > 0 && faces[i].width > 0)
{
face = gray(faces[i]);
text_lbd = Point(faces[i].x+faces[i].height*0.5, faces[i].y+faces[i].width*0.5);
text_lb = Point(faces[i].x, faces[i].y);
ui.label_4->setText(tempStr.setNum(faces[i].area()));
//cout<<"x:"<
//sprintf(AA,"%d %d",faces[i].x+faces[i].height*0.5,faces[i].y+faces[i].width*0.5);
//cout<<"AA is :"<
//ui.label_2->setText(tempStr.setNum(faces[i].x));
//ui.label_3->setText(tempStr.setNum(faces[i].y));
//putText(frame,AA,text_lbd,2,1.0,Scalar(255,122,134),2,8);
rectangle(frame, faces[i], Scalar(255, 0, 0), 1, 8, 0);
}
}
if (face.rows >= 70)
{
//cvResize(face, face_test, Size(92, 112));
//resize(face,face_test,Size(92,112));
cv::resize(face,face_test,Size(92,112));
//resize(face,);
}
//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 == 41)
{
string name = "W.Chaolong";
putText(frame, name, text_lb, FONT_HERSHEY_COMPLEX, 1, Scalar(0, 0, 255));
}
ui.label_2->setText(tempStr.setNum(predictPCA));
imshow("face", frame);
QImage img2 = Mat2Qimage(frame);
ui.label_1->setPixmap(QPixmap::fromImage(img2));
waitKey(5);
//if (waitKey(10) >= 0)
// stop = true;
}
}
//返回Cap拍摄的frame
void Ctest_Qt_VideoCapture_0607::Start()
{
//VideoCapture cap(0);
//while(true)
//{
// cap>>frame;
// waitKey(1);
//}
cap.open(0);
ui.pushButton_3->setEnabled(true);
ui.pushButton->setEnabled(true);
ui.pushButton_2->setEnabled(true);
ui.pushButton_6->setEnabled(true);
//ui.pushButton_7->setText("关闭");
ui.pushButton_7->setEnabled(false);
}
void Ctest_Qt_VideoCapture_0607::Quit()
{
qApp->quit();
}
void Ctest_Qt_VideoCapture_0607::Slider(int value)
{
//ui.Slider->getValue
AA = ui.Slider->value();
QString BB = QString("%1").arg(AA);
ui.progressBar->setValue(AA);
ui.label_5->setText(BB);
}
3、main.cpp(主函数可以不动)
#include "ctest_qt_videocapture_0607.h"
#include
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Ctest_Qt_VideoCapture_0607 w;
w.show();
return a.exec();
}
结果真是太感人,仅仅用Opencv中自带的分类器效率还有待提高。这个还需要后期优化代码或者CUDA编程利用GPU加速处理,不然完全不能用到实际工程中去。本实验仅仅是实现了一个结合Qt的界面话的小实验。还有许多地方需要完善,如有不当之处,还望批评指正!共同学习进步!
这里贴一张界面图: