一、背景數(shù)據(jù)集
本次用的數(shù)據(jù)集是opencv給出的教程里面的第一個(gè)數(shù)據(jù)集:??The AT&T Facedatabase??,又稱為ORL人臉數(shù)據(jù)庫(kù),40個(gè)人,每人10張照片。照片在不同時(shí)間、不同光照、不同表情(睜眼閉眼、笑或者不笑)、不同人臉細(xì)節(jié)(戴眼鏡或者不戴眼鏡)下采集。所有的圖像都在一個(gè)黑暗均勻的背景下采集的,正面豎直人臉(有些有輕微旋轉(zhuǎn))。?
?
二、自己的人臉數(shù)據(jù)集
想要識(shí)別自己,單有別人的數(shù)據(jù)集還是不行的,還需要自己人臉的照片才行。這就需要我們收集自己的照片,然后和上面的那個(gè)數(shù)據(jù)集一起來(lái)訓(xùn)練模型。
Qt開(kāi)發(fā)學(xué)習(xí)基地(免費(fèi)報(bào)名學(xué)習(xí)):C/C++項(xiàng)目實(shí)戰(zhàn)/Qt5/C語(yǔ)言/c++/數(shù)據(jù)庫(kù)/OpenCV/MFC/QT項(xiàng)目-學(xué)習(xí)視頻教程-騰訊課堂
2.1、人臉數(shù)據(jù)采集
下列程序調(diào)用openCV來(lái)拍照,按下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()<<"打開(kāi)攝像頭成功!";
else
qDebug()<<"打開(kāi)攝像頭失??!";
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();
}
注意變換角度,變換表情,拍完之后,精選十張帥照,用于自己的人臉數(shù)據(jù)集
?文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-729908.html
2.2、預(yù)處理
在得到自己的人臉照片之后,還需要對(duì)這些照片進(jìn)行一些預(yù)處理才能拿去訓(xùn)練模型。所謂預(yù)處理,其實(shí)就是檢測(cè)并分割出人臉,并改變?nèi)四槇D片的大小,需與下載的數(shù)據(jù)集中圖片大?。?2 x 112)一致。使用下列程序可以自動(dòng)檢測(cè)人臉、分割人臉、調(diào)整大小和存儲(chǔ)。
注意:調(diào)用opencv訓(xùn)練好的分類器和自帶的檢測(cè)函數(shù)檢測(cè)人臉,需要將OpenCV源代碼中分類器事先放到自己自己的工程目錄中去;
分類器位置:???D:\Qt\opencv-3.4.5\opencv-3.4.5\data\haarcascades\haarcascade_frontalface_default.xml?? 移動(dòng)到:自己工程的build目錄下(必須這個(gè)目錄)
?
#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; //定義人臉?lè)诸惼?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人臉數(shù)據(jù)庫(kù)人臉大小一致的自己的人臉數(shù)據(jù)集。然后我們把自己的作為第41個(gè)人,在我們下載的人臉文件夾下建立一個(gè)s41的子文件夾,把自己的人臉數(shù)據(jù)放進(jìn)去。就成了這樣下面這樣,最后一個(gè)文件夾里面是我自己的頭像照片:
?
三、csv文件生成
當(dāng)我們寫(xiě)人臉模型的訓(xùn)練程序的時(shí)候,我們需要讀取人臉和人臉對(duì)應(yīng)的標(biāo)簽。直接在數(shù)據(jù)庫(kù)中讀取顯然是低效的,所以我們用csv文件讀取。csv文件中包含兩方面的內(nèi)容:一是每一張圖片的位置所在;二是每一個(gè)人臉對(duì)應(yīng)的標(biāo)簽,就是為每一個(gè)人編號(hào)。如下圖示:
?
這個(gè)工作可以自己純手動(dòng)去完成,但是400多張圖片,也是很費(fèi)時(shí)間的,珍惜生命,走下面這條路吧!
打開(kāi)命令行,切換到人臉數(shù)據(jù)集目錄下,輸入如下指令:
dir /b/s *bmp > at.txt
?
?本文福利,莬費(fèi)領(lǐng)取Qt開(kāi)發(fā)學(xué)習(xí)資料包、技術(shù)視頻,內(nèi)容包括(C++語(yǔ)言基礎(chǔ),Qt編程入門,QT信號(hào)與槽機(jī)制,QT界面開(kāi)發(fā)-圖像繪制,QT網(wǎng)絡(luò),QT數(shù)據(jù)庫(kù)編程,QT項(xiàng)目實(shí)戰(zhàn),QT嵌入式開(kāi)發(fā),Quick模塊等等)↓↓↓↓↓↓見(jiàn)下面↓↓文章底部點(diǎn)擊莬費(fèi)領(lǐng)取↓↓文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-729908.html
?
?
這個(gè)at.txt就是我們需要的csv文件,但是現(xiàn)在是只有路徑?jīng)]有標(biāo)簽的。使用下列程序,會(huì)生成一個(gè)at_temp.txt,這個(gè)既有路徑又有標(biāo)簽,正是我們想要的,將之前的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();
}
?
?
四、模型訓(xùn)練
#include <QApplication>
#include <opencv2\opencv.hpp>
#include <iostream>
#include <opencv2/face.hpp>
#include <QDebug>
using namespace cv;
using namespace std;
using namespace face;
//使用CSV文件去讀圖像和標(biāo)簽,主要使用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個(gè)容器來(lái)存放圖像數(shù)據(jù)和對(duì)應(yīng)的標(biāo)簽
vector<Mat> images;
vector<int> labels;
// 讀取數(shù)據(jù). 如果文件不合法就會(huì)出錯(cuò)
// 輸入的文件名已經(jīng)有了.
try
{
read_csv(fn_csv, images, labels);
}
catch (cv::Exception& e)
{
cerr << "Error opening file \"" << fn_csv << "\". Reason: " << e.msg << endl;
// 文件有問(wèn)題,我們啥也做不了了,退出了
exit(1);
}
// 如果沒(méi)有讀取到足夠圖片,也退出.
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);
}
// 下面的幾行代碼僅僅是從你的數(shù)據(jù)集中移除最后一張圖片
//[gm:自然這里需要根據(jù)自己的需要修改,他這里簡(jiǎn)化了很多問(wèn)題]
Mat testSample = images[images.size() - 1];
int testLabel = labels[labels.size() - 1];
images.pop_back();
labels.pop_back();
// 下面幾行創(chuàng)建了一個(gè)特征臉模型用于人臉識(shí)別,
// 通過(guò)CSV文件讀取的圖像和標(biāo)簽訓(xùn)練它。
// T這里是一個(gè)完整的PCA變換
//如果你只想保留10個(gè)主成分,使用如下代碼
// cv::createEigenFaceRecognizer(10);
//
// 如果你還希望使用置信度閾值來(lái)初始化,使用以下語(yǔ)句:
// cv::createEigenFaceRecognizer(10, 123.0);
//
// 如果你使用所有特征并且使用一個(gè)閾值,使用以下語(yǔ)句:
// 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");
// 下面對(duì)測(cè)試圖像進(jìn)行預(yù)測(cè),predictedLabel是預(yù)測(cè)標(biāo)簽結(jié)果
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();
}
?
上述程序從人臉數(shù)據(jù)集中取了最后一張照片用于測(cè)試訓(xùn)練的模型。人臉數(shù)據(jù)集總共40+1個(gè)人,但是標(biāo)簽是從0開(kāi)始的,所以最后的人臉標(biāo)簽是40,從上述程序運(yùn)行結(jié)果可以看出,實(shí)際是40,預(yù)測(cè)也是40,說(shuō)明模型訓(xùn)練成功,三個(gè)訓(xùn)練好的模型如下圖:
?
五、人臉識(shí)別
#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); //打開(kāi)默認(rèn)攝像頭
if (!cap.isOpened())
{
return -1;
}
Mat frame;
Mat edges;
Mat gray;
CascadeClassifier cascade;
bool stop = false;
//訓(xùn)練好的文件名稱,放置在可執(zhí)行文件同目錄下
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");//訓(xùn)練的模型
while(!stop)
{
cap >> frame;
//建立用于存放人臉的向量容器
vector<Rect> faces(0);
cvtColor(frame, gray, CV_BGR2GRAY);
//改變圖像大小,使用雙線性差值
//resize(gray, smallImg, smallImg.size(), 0, 0, INTER_LINEAR);
//變換后的圖像進(jìn)行直方圖均值化處理
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())
{
//測(cè)試圖像應(yīng)該是灰度圖
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();
}
從上圖可知,自己的人臉標(biāo)簽是35,當(dāng)識(shí)別的人臉為35時(shí),識(shí)別成功為自己,標(biāo)記人臉,貼上名字。?
?
?
C++ Qt是一個(gè)基于C++語(yǔ)言的跨平臺(tái)應(yīng)用程序開(kāi)發(fā)框架,具有豐富的UI組件和API庫(kù)。Qt包括了許多模塊,如Qt Core、Qt GUI、Qt Network等,每個(gè)模塊都提供了一系列的類和函數(shù)。Qt還提供了集成開(kāi)發(fā)環(huán)境(IDE)Qt Creator,使得開(kāi)發(fā)人員可以通過(guò)它來(lái)創(chuàng)建、編譯、調(diào)試和部署Qt應(yīng)用程序。?
學(xué)習(xí)Qt開(kāi)發(fā)不知道做什么?我為大家整理的這些領(lǐng)域都涉及到Qt開(kāi)發(fā):嵌入式領(lǐng)域、桌面端開(kāi)發(fā)、移動(dòng)端、微控制器MCU、客戶端(游戲、直播等等)、汽車領(lǐng)域行業(yè)、 消費(fèi)類電子設(shè)備、醫(yī)療領(lǐng)域行業(yè)、工業(yè)自動(dòng)化領(lǐng)域等等
Qt框架,GUI應(yīng)用程序,跨平臺(tái)開(kāi)發(fā),信號(hào)與槽機(jī)制,QML語(yǔ)言,模型視圖編程,多線程編程,數(shù)據(jù)庫(kù)編程,網(wǎng)絡(luò)編程,XML解析,JSON解析,圖形圖像處理,用戶界面設(shè)計(jì),動(dòng)畫(huà)效果,OpenGL,WebKit,嵌入式開(kāi)發(fā),客戶端/服務(wù)器應(yīng)用程序.自定義控件QT6,C++語(yǔ)言基礎(chǔ),qt基礎(chǔ)編程,qt軟件開(kāi)發(fā),Qt架構(gòu)設(shè)計(jì),qt布局管理器,qt嵌入式開(kāi)發(fā),qt編程入門,qt數(shù)據(jù)庫(kù)編程,qt跨平臺(tái)框架,QT項(xiàng)目實(shí)戰(zhàn),Quick模塊,OpenCV,Qt實(shí)戰(zhàn),OpenCV教程,QT界面開(kāi)發(fā),Qt框架,C++數(shù)據(jù)結(jié)構(gòu),Qt線程,桌面應(yīng)用開(kāi)發(fā),qt桌面應(yīng)用開(kāi)發(fā),Socket網(wǎng)絡(luò)編程,qt開(kāi)發(fā)工程師,qt開(kāi)發(fā),應(yīng)用程序開(kāi)發(fā)框架,圖形視圖框架,數(shù)據(jù)庫(kù)編程,Qt開(kāi)發(fā)編程,Qt開(kāi)發(fā)控件,Qt開(kāi)發(fā)工程師,QT開(kāi)發(fā)必備技能棧,qt編碼,qt網(wǎng)絡(luò)編程,qt網(wǎng)絡(luò)通信,Qt信號(hào),Qt槽機(jī)制,qt字符串,qt數(shù)據(jù)類型,qt容器,qt客戶端開(kāi)發(fā),qt軟件工程師,qt頁(yè)面繪制
?
?本文福利,莬費(fèi)領(lǐng)取Qt開(kāi)發(fā)學(xué)習(xí)資料包、技術(shù)視頻,內(nèi)容包括(C++語(yǔ)言基礎(chǔ),Qt編程入門,QT信號(hào)與槽機(jī)制,QT界面開(kāi)發(fā)-圖像繪制,QT網(wǎng)絡(luò),QT數(shù)據(jù)庫(kù)編程,QT項(xiàng)目實(shí)戰(zhàn),QT嵌入式開(kāi)發(fā),Quick模塊等等)↓↓↓↓↓↓見(jiàn)下面↓↓文章底部點(diǎn)擊莬費(fèi)領(lǐng)取↓↓
到了這里,關(guān)于OpenCV--人臉識(shí)別入門的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!