一过程

1生成csv文件(在编译下目录里的1 、2、。。。里面每个用户一个)

1.1拍照 ,生成temp文件夹放入拍照的图片
1.2加载人脸识别特征器,裁剪生成样本(50*60),生成1文件夹
1.3生成CSV文件给人脸编号
1.4生成user文件(前面编号,后面人名)

2采样图片灰度化处理

3直方图均衡

4训练: 图片给模型训练器生成facemodel.xml文件

1
2
3
Ptr<FaceRecognizer> model = createEigenFaceRecognizer();
model->train(images, labels);
model->save("MyFacePCAModel.xml");

5人脸识别

5.1打开摄像头采集数据
5.2使用人脸特征级联器—检测到人脸(特征提取,圈主人脸)
5.3加载刚才训练的模型,进行人脸识别
通过csv\user.txt—显示用户名

二实现函数

修改文件地址

1.mainwidows.cpp 134行/home/lixiao/opencv/opencv-3.4.5需要改成自己装opencv的位置

1
std::string face_Classifier_name = "/home/lixiao/opencv/opencv-3.4.5/data/haarcascades_cuda/haarcascade_frontalface_alt.xml";

2.mainwidows.cpp 26行登录界面的背景图片地址
1
QPixmap logo("/home/lixiao/face/opencv.jpg");

修改圈住人脸的地方 172行

mainwidows.cpp 172行

1
2
3
4
5
6
7
//imshow("face", face[i]);
if (faces[i].height > 0 && faces[i].width > 0)
{
face = gray(faces[i]);
cv::rectangle(frame, faces[i], cv::Scalar(0, 0, 255), 1, 8, 0);
emit getQimage(frame);
}

打印名字(显示识别结果)

mainwidows.cpp 217行

1
2
QStringList list = line.split(";");
ui->lineEdit_user->setText(list[1]);//显示识别结果的名字

图片显示名字
mainwidows.cpp 217行下加入putText()
参数point—文字显示的位置
Scalar—颜色
2—线的粗细
1
putText(frame,list[1].toStdString(),Point(10,50),FONT_HERSHEY_SIMPLEX,2,Scalar(128,0,255),2,LINE_4);

绘制圆形

方法
circle(img, center, radius, color, thickness, lineType, shift)
参数:
1、img:图像;
2、center:圆的中心。
3、radius:圆半径。
4、color:圆颜色或亮度(灰度图像)。
5、thickness:轮廓厚度。负值,填充。
6、lineType:线型。
7、shift:点坐标中小数位数的参数移位数。

1
2
3
4
5
6
7
newimage = (500, 500, 3)
image = np.zeros(newimage, np.uint8)
cv2.circle(image, (150,150), 100, (255, 255, 255), 2)
cv2.circle(image, (300,150), 50, (255, 255, 255), -1)
cv2.imshow('circle', image)
cv2.waitKey(0)
cv2.destroyAllWindows()

代码(节选)

mainwindow.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
#include "mainwindow.h"
#include "ui_mainwindow.h"

#include <opencv2/opencv.hpp>
#include "opencv2/core.hpp"
#include "opencv2/face.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"
#include <iostream>
#include "opencv2/imgproc/types_c.h"
#include "opencv2/face/facerec.hpp"
//#include "opencv2/contrib/contrib.hpp"
//#include<face/face.hpp>
#include <vector>

using namespace cv;
using namespace std;
using namespace cv::face;
//using namespace cv;
// SVM;
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
QPixmap logo("/home/lixiao/face/opencv1.jpg");
ui->label_video->setPixmap(logo);
connect(this, SIGNAL(getQimage(cv::Mat&)), this, SLOT(showimage(cv::Mat&)));
}

MainWindow::~MainWindow()
{
delete ui;
}

void MainWindow::Sleep(int msec)
{
QTime dieTime = QTime::currentTime().addMSecs(msec);
while( QTime::currentTime() < dieTime )
QCoreApplication::processEvents(QEventLoop::AllEvents, 100);

}

void MainWindow::deldir(QString &path)
{
if (path.isEmpty())
return ;

QDir dir(path);
if(!dir.exists())
return ;

dir.setFilter(QDir::AllEntries | QDir::NoDotAndDotDot);
QFileInfoList fileList = dir.entryInfoList();
foreach (QFileInfo fi, fileList)
{
if (fi.isFile())
fi.dir().remove(fi.fileName());
//else
// ; //DeleteDirectory(fi.absoluteFilePath());
}
return ;

}
//样本获取
void MainWindow::on_pushButton_photo_sample_clicked()
{
PhotoIn = new Dialogin(this);
PhotoIn->setMaximumSize(QSize(670,610));
PhotoIn->setMinimumSize(QSize(670,610));
PhotoIn->show();
}
//模型训练开始识别
void MainWindow::on_pushButton_train_clicked()
{
QMessageBox *startbox = new QMessageBox(QMessageBox::Warning ,"信息","开始训练请稍后!!!",QMessageBox::NoButton,this);//界面对话框
startbox->show();
Sleep(1000);
QFile csv(QDir::currentPath() + "/csv.txt");//读取csv.txt文件
if(!csv.open(QIODevice::ReadOnly | QIODevice::Text))
{
qDebug() << csv.errorString() <<endl;
startbox->close();
QMessageBox::warning(this,tr("错误"),tr("csv文件未找到!!!"),QMessageBox::Ok);
return;
}
std::vector<cv::Mat>images;
std::vector<int>labels;
while(!csv.atEnd())
{
QString p = csv.readLine(128);
QStringList qlist = p.split(";");
if(qlist.size() == 2)
{
QString a =qlist[0];
QByteArray b = a.toLatin1();
char *path = b.data();
// char *path = (qlist[0].toLatin1()).data();
int tag = qlist[1].mid(0, 1).toInt();

std::string filepath =cv::format(path);
images.push_back(cv::imread(path, 0));
labels.push_back(tag);
qDebug()<< qlist.size() <<endl;
}
}
if(images.size() < 1)
{
QMessageBox::warning(this,tr("错误"),tr("加载图片失败!!!"),QMessageBox::Ok);
return;
}
cv::Ptr<face::FaceRecognizer> model = EigenFaceRecognizer::create();
//cv::Ptr<FaceRecognizer> model = EigenFaceRecognizer::create();
model->train(images, labels);
model->save("facemodel.xml");
startbox->close();
QMessageBox::warning(this,tr("信息"),tr("恭喜模型训练完成!!!"),QMessageBox::Ok);
/**********************************/
double confidence=0.0;
int predictedLabel=-1;
CvCapture* capture = cvCaptureFromCAM(0); //打开默认摄像头
cvSetCaptureProperty(capture, CV_CAP_PROP_FRAME_WIDTH, 640);
cvSetCaptureProperty(capture, CV_CAP_PROP_FRAME_HEIGHT, 480);
if (capture == NULL)
{
QMessageBox::warning(this,tr("错误"),tr("摄像头打开失败"),QMessageBox::Ok);
return;
}
IplImage* frames;
cv::Mat frame;
cv::Mat gray;
cv::CascadeClassifier faceClassifier;
//训练好的文件名称,放置在可执行文件同目录下
bool flag =faceClassifier.load("/home/lixiao/opencv/opencv-3.4.5/data/haarcascades_cuda/haarcascade_frontalface_alt.xml");
if(flag == false)
{
QMessageBox::warning(this,tr("错误"),tr("不能加载haarcascade_frontalface_alt2.xml文件!!"),QMessageBox::Ok);
return;
}
QFile facemodel(QDir::currentPath() + "/facemodel.xml");//寻找facemodel.xml
if(!facemodel.open(QIODevice::ReadOnly | QIODevice::Text))
{
qDebug() << facemodel.errorString() <<endl;
cvReleaseCapture(&capture);
QMessageBox::warning(this,tr("错误"),tr("facemodel.xml文件未找到!!!"),QMessageBox::Ok);
return;
}
//model->predict(labels,predictedLabel,confidence);
stopflag = true;
while(stopflag == true)
{
Sleep(100);
frames = cvQueryFrame( capture );
frame=cvarrToMat(frames);

std::vector<cv::Rect> faces;
//RGB转BGR格式
cv::cvtColor(frame, gray, CV_BGR2GRAY);
//变换后的图像进行直方图均值化处理
cv::equalizeHist(gray, gray);
//通过人脸分类器识别有多少人脸
faceClassifier.detectMultiScale(gray, faces, 1.1, 2, cv::CASCADE_FIND_BIGGEST_OBJECT|cv::CASCADE_DO_ROUGH_SEARCH, cv::Size(50, 50));
//qDebug() << "faces.size =" << faces.size() << endl;

cv::Mat face;
for (size_t i = 0; i < faces.size(); i++)
{
//imshow("face", face[i]);
if (faces[i].height > 0 && faces[i].width > 0)
{
face = gray(faces[i]);
// cv::rectangle(frame, faces[i], cv::Scalar(0, 0, 255), 1, 8, 0);
// emit getQimage(frame);
Point center(faces[i].x + faces[i].width*0.5, faces[i].y + faces[i].height*0.5);
cv::ellipse(frame, center,Size(faces[i].width*0.5, faces[i].height*0.5), 0, 0, 360, Scalar(0, 0, 255), 1, 8, 0);

emit getQimage(frame);
}

}
if(faces.size() == 0)
{
ui->lineEdit_confidence->clear();
ui->lineEdit_user->clear();
emit getQimage(frame);
}
cv::Mat face_test;
if((face.rows > 112) || (face.cols > 92))
{
cv::resize(face, face_test, cv::Size(92,112));
}
//int predictPCA = 0;
double confidence=0.0;
if (!face_test.empty())
{
//测试图像应该是灰度图
int predictedLabel=-1;
//predictPCA = model->predict(face_test);
model->predict(face_test,predictedLabel,confidence);
qDebug() << "============================="<<endl;
ui->lineEdit_confidence->setText(QString::number(confidence));//显示置信度
// qDebug() << "predictPCA =" << predictPCA << endl;
// qDebug()<<"predictedLabel:"<<predictedLabel;
// qDebug()<<"confidence:"<<confidence;

QFile user(QDir::currentPath() + "/usr.txt");
if(!user.open(QIODevice::ReadWrite | QIODevice::Text))
{
qDebug() << user.errorString() <<endl;
}
qint64 usrlinenum =0;
//usrlinenum=1;
QString line;
QTextStream in(&user);
while((!in.atEnd()) && (usrlinenum < predictedLabel) )
{
line = in.readLine();
usrlinenum++;

}
QStringList list = line.split(";");
ui->lineEdit_user->setText(list[1]);//显示识别结果的名字
ui->label_3->setText(list[1]);
}
}
cvReleaseCapture(&capture);
QPixmap logo(QDir::currentPath() + "/opencv.jpg");
ui->label_video->setPixmap(logo);
//startbox->close();
}

void MainWindow::showimage(cv::Mat &image)
{
cv::Mat src = image ;
cvtColor( src, src, CV_BGR2RGB );
QImage img=QImage((const unsigned char*)(src.data), src.cols, src.rows, QImage::Format_RGB888);
QPixmap pix = QPixmap::fromImage(img);
ui->label_video->setPixmap(pix);
}
//停止识别
void MainWindow::on_pushButton_face_reco_stop_clicked()
{
stopflag = false;

}
//清除数据
void MainWindow::on_pushButton_clear_data_clicked()
{
QString path = QDir::currentPath();
QFile facemodel(path + "/facemodel.xml");
if(facemodel.exists())
{
facemodel.remove();
}
QFile csv(path + "/csv.txt");
if(csv.exists())
{
csv.remove();
}
}


dialogin.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
#include "dialogin.h"
#include "ui_dialogin.h"
#include <opencv2/opencv.hpp>
#include "opencv2/core.hpp"
#include "opencv2/face.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"
#include <iostream>
#include "opencv2/imgproc/types_c.h"
using namespace cv;
using namespace std;
using namespace cv::face;
using namespace cv;
Dialogin::Dialogin(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialogin)
{

ui->setupUi(this);
i=0;
capture = NULL;
capture = cvCaptureFromCAM(0);//打开摄像头
//设置视频获取属性
cvSetCaptureProperty(capture, CV_CAP_PROP_FRAME_WIDTH, 640);// 视频流的帧宽度(只对摄像头有效)
cvSetCaptureProperty(capture, CV_CAP_PROP_FRAME_HEIGHT, 480);// 视频流的帧高度(只对摄像头有效)
setWindowFlags(windowFlags()&~ Qt::WindowCloseButtonHint);//窗口只有一个关闭按钮
//gimage =NULL;
// connect(this, SIGNAL(getimage(const QImage &image)), this, SLOT(showimage(const QImage &image)));
timer = new QTimer(this);
timer->setInterval(50);
connect(timer, SIGNAL(timeout()), this, SLOT(showimage()));
timer->start();
}

Dialogin::~Dialogin()
{
delete ui;
}

void Dialogin::Sleep(int msec)
{
QTime dieTime = QTime::currentTime().addMSecs(msec);
while( QTime::currentTime() < dieTime )
QCoreApplication::processEvents(QEventLoop::AllEvents, 100);
}
//开始拍照
void Dialogin::on_pushButton_photo_clicked()
{

// qDebug() << ui->pushButton_photo->text() << endl;

if(ui->lineEdit_name->text() == NULL)
{
QMessageBox::warning(this,tr("错误"),tr("请输入姓名"),QMessageBox::Ok);
}
else
{
if(capture == NULL)
{
QMessageBox::warning(this, tr("警告!!!"), tr("摄像头打不开!!!!"), QMessageBox::Abort);
return;
}
QDir *path = new QDir;
bool exist = path->exists(QDir::currentPath() + "/TEMP");//把拍完照的图片保存到TEMP目录下


if(!exist)
{
bool ok = path->mkdir(QDir::currentPath() + "/TEMP");
if(ok)
{
QMessageBox::warning(this,tr("创建文件夹"),tr("文件夹创建成功!"));
}
}
QString temp = QDir::currentPath() + "/TEMP/%d.jpg";//照片的数量
char * path_src;
QByteArray ba= temp.toLatin1();
path_src = ba.data();




frames = cvQueryFrame( capture );//函数cvQueryFrame从摄像头或者文件中抓取一帧,然后解压并返回这一帧
frame=cvarrToMat(frames); //格式转换否则无法正常显示图片
std::string filename = cv::format(path_src, i);
ui->pushButton_photo->setText(QString::number(i+1,10));
imwrite(filename, frame);
i++;

}
}
//开始取样
void Dialogin::on_pushButton_start_photo_clicked()
{
QMessageBox *startbox = new QMessageBox(QMessageBox::Warning ,"信息","开始取样请稍后!!!",QMessageBox::NoButton,this);//对话框的交互
startbox->show();
Sleep(1000);
QFile user(QDir::currentPath() + "/usr.txt");//保存照片的名字
if(!user.open(QIODevice::ReadWrite | QIODevice::Text))
{
startbox->close();
qDebug() << user.errorString() <<endl;
}
qint64 usrlinenum =0;
usrlinenum=1;
while(!user.atEnd())
{
char buf[128];
qint64 c = user.readLine(buf, sizeof(buf));

if(c > 0)
{
usrlinenum++;
qDebug() << buf <<endl;
}
if(c <0 )
{
startbox->close();
QMessageBox::warning(this,tr("文件读取"),tr("文件读取失败!!!"));
return;
}
}
// qDebug() << QString::number(i, 10) <<endl;
QString usrline = QString::number(usrlinenum, 10) + ";" + ui->lineEdit_name->text() + "\r\n";
qDebug() << usrline <<endl;


char * p;
QByteArray buf = usrline.toLatin1();
p = buf.data();
user.write(p);
user.close();

QDir *pathdata = new QDir;
bool exist = pathdata->exists(QDir::currentPath() + "/" +QString::number(usrlinenum, 10) );
if(!exist)
{
bool ok = pathdata->mkdir(QDir::currentPath() + "/" +QString::number(usrlinenum, 10));
if(!ok)
{
QMessageBox::warning(this,tr("创建文件夹失败"),tr("文件夹创建失败!"));
startbox->close();
return;
}

}
QString pathdst = QDir::currentPath() + "/" +QString::number(usrlinenum, 10) + "/";
/*********************************************************************************************/

cv::CascadeClassifier face_Classifier; //定义人脸分类器
std::string face_Classifier_name = "/home/lixiao/opencv/opencv-3.4.5/data/haarcascades_cuda/haarcascade_frontalface_alt.xml";


std::vector<cv::Rect> face_rect;
cv::Mat dst_gray;

if (!face_Classifier.load(face_Classifier_name))//加载文件
{
startbox->close();
QMessageBox::warning(this,tr("错误"),tr("haarcascade_frontalface_alt.xml加载失败"),QMessageBox::Ok);
return;
}


QString dst_path = pathdst + "%d.jpg";


QDir dir(QDir::currentPath() + "/TEMP/");
QStringList dst_file_name;
dst_file_name << "*.jpg" ;

QFileInfoList list = dir.entryInfoList(dst_file_name, QDir::Files|QDir::Readable, QDir::Name);

qDebug() << list.size() << endl;

char * path_dst;
QByteArray dsttemp= dst_path.toLatin1();
path_dst = dsttemp.data();
for(int z=0;z< list.size();z++)
{

char * path_list_src ;

QByteArray dsttemp= list[z].absoluteFilePath().toLatin1();
path_list_src = dsttemp.data();

std::string srcfile = cv::format(path_list_src);
std::string dstfile = cv::format( path_dst, z);
cv::Mat srcframe = cv::imread(srcfile);
cv::cvtColor(srcframe, dst_gray, cv::COLOR_BGR2GRAY);
cv::equalizeHist(dst_gray, dst_gray);
//人脸检测
face_Classifier.detectMultiScale(dst_gray, face_rect, 1.1, 3, CV_HAAR_DO_ROUGH_SEARCH, cv::Size(30, 30));
for (int j = 0; j < (int)face_rect.size(); j++)
{
cv::Mat faceROI = srcframe(face_rect[j]);
cv::Mat MyFace;
cv::Mat gray_MyFace;
if (faceROI.cols > 100)
{
cv::resize(faceROI, MyFace, cv::Size(92, 112));
cv::cvtColor(MyFace, gray_MyFace, CV_BGR2GRAY);
imwrite(dstfile, gray_MyFace);
}
}


}
/***********************************************************************************************************/
/*当我们写人脸模型的训练程序的时候,我们需要读取人脸和人脸对应的标签。直接在数据库中读取显然是低效的。所以我们用csv文件读取*/
QFile csv(QDir::currentPath() + "/csv.txt");//写入csv.txt文件
if(!csv.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text))
{
qDebug() << csv.errorString() << endl;
startbox->close();
QMessageBox::warning(this,tr("错误"),tr("无法创建csv文件!!!"),QMessageBox::Ok);
return;
}
QDir dst_file_dir(pathdst);
QStringList dst_file_filter;
dst_file_filter << "*.jpg" ;
QFileInfoList dst_file_list_path = dst_file_dir.entryInfoList(dst_file_filter, QDir::Files | QDir::Readable, QDir::Name);
for(int x=0; x<dst_file_list_path.size(); x++)
{
csv.write(QString(dst_file_list_path[x].absoluteFilePath() + ";" + QString::number(usrlinenum, 10) + "\r\n").toLatin1());
}
startbox->close();
QMessageBox::warning(this,tr("信息"),tr("取样已经完成、csv文件完创建成!!!"),QMessageBox::Ok);
}
//关闭采样界面
void Dialogin::on_pushButton_close_clicked()
{

ui->lineEdit_name->setEnabled(true);
ui->pushButton_start_photo->setEnabled(true);
ui->lineEdit_name->clear();
if(capture != NULL)
{
cvReleaseCapture(&capture);
}
this->close();
}

void Dialogin::showimage()
{
if(capture != NULL)
{
IplImage* pBinary;
cv::Mat src ;
pBinary = cvQueryFrame( capture );//函数cvQueryFrame从摄像头或者文件中抓取一帧,然后解压并返回这一帧
src=cvarrToMat(pBinary);
//格式转换否则无法正常显示图片
cvtColor( src, src, CV_BGR2RGB );//将图像从一个颜色空间转换到另一个颜色空间的转换

QImage img=QImage((const unsigned char*)(src.data), src.cols, src.rows, QImage::Format_RGB888);

QPixmap pix = QPixmap::fromImage(img);//用来对图片进行预处理
ui->label_video->setPixmap(pix);
}

}




源码资料
链接:https://pan.baidu.com/s/1Rol3vv-ERg4fulwcshF1Yg?pwd=k7xx
提取码:k7xx