前言
本人從事機械設(shè)計12年,業(yè)余時間自學(xué)編程。
2022年4月6日,開始學(xué)習(xí)C#,
2022年9月7日,開始學(xué)習(xí)c++和Qt,
2022年10月28日,開始學(xué)習(xí)OpenCV,
今天終于搞定了傳說中的 人臉識別?,在此,做個記錄。
人臉檢測,是基于Haar特征的cascade分類器,
人臉識別,是基于LDA理論的Fisherface算法。
話不多說,上視頻?。–SDN上傳的視頻,太清晰?。?/p>
人臉識別測試程序
測試代碼
FaceRecognition.pro
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
CONFIG += c++11
# You can make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
SOURCES += \
main.cpp \
sm.cpp \
widget.cpp
HEADERS += \
sm.h \
widget.h
FORMS += \
widget.ui
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
unix|win32: LIBS += -L$$PWD/../../../../../opencv/install/x64/mingw/lib/ -llibopencv_world460.dll
INCLUDEPATH += $$PWD/../../../../../opencv/install/include
DEPENDPATH += $$PWD/../../../../../opencv/install/include
sm.h
#ifndef SM_H
#define SM_H
#include <iostream>
#include "opencv2/core.hpp"
class sm
{
public:
sm();
//讀取文件
static void read_csv(const std::string& filename, std::vector<cv::Mat>& images, std::vector<int>& labels, char separator);
//圖像預(yù)處理:檢測人臉、裁剪、縮放、保存、生成列表
static void pretreatment(std::vector<cv::Mat> images, std::vector<int> labels,std::string path,int width,int height);
};
#endif // SM_H
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
private slots:
void on_pushButton_clicked();
void on_pushButton_7_clicked();
void on_pushButton_2_clicked();
void on_pushButton_3_clicked();
void on_pushButton_5_clicked();
void on_pushButton_6_clicked();
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
main.cpp
#include "widget.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
sm.cpp
#include "sm.h"
//引用依賴
#include "opencv2/core.hpp"
#include "opencv2/face.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"
#include <iostream>
#include <fstream>
#include <sstream>
#include <QDebug>
sm::sm()
{
}
void sm::pretreatment(std::vector<cv::Mat> images, std::vector<int> labels, std::string path, int width, int height)
{
cv::Mat dst_shear;
cv::Mat dst_resize;
//創(chuàng)建級聯(lián)分類器
cv::CascadeClassifier cascade;
//載入Haar特征分類器
cascade.load("C:/opencv/date/haarcascade_frontalface_default.xml");
//創(chuàng)建矩形容器
std::vector<cv::Rect> rects;
//遍歷
int flag=1;
for(uint i=0;i<images.size();i++)
{
//人臉檢測
cascade.detectMultiScale(images[i],rects);
//裁剪
dst_shear = images[i](rects[0]).clone();
//縮放
resize(dst_shear,dst_resize,cv::Size(width,height),0,0,cv::INTER_AREA);
//保存路徑拼接
std::string im_path1 = std::to_string(labels[i]);
std::string im_path2 = std::to_string(flag);
flag++;
if(labels[i+1]!=labels[i])
{
flag=1;
}
std::string im_path3 = ".png";
std::string im_path = path+im_path1+"-"+im_path2+im_path3;
//調(diào)試
QString qstr_im = QString::fromStdString(im_path);
qDebug()<<qstr_im;
//保存
imwrite(im_path,dst_resize);
//顯示
//std::string str = std::to_string(i);
//imshow(str,dst_resize);
//生成列表文件
std::string list_name = "list.txt";
std::string list_path = path + list_name;
//調(diào)試
QString qstr_list = QString::fromStdString(list_path);
qDebug()<<qstr_list;
std::ofstream list(list_path, std::ios::app);
if (list.fail())
{
qDebug()<<"list文件打開失敗,請檢查文件路徑!";
}
else
{
list<<im_path;
list<<";";
list<<im_path1;
list<<"\n";
}
}
}
void sm::read_csv(const std::string &filename, std::vector<cv::Mat> &images, std::vector<int> &labels, char separator)
{
//以只讀方式讀取文件
std::ifstream file(filename, std::ios::in);
if (!file)
{
qDebug()<<"文件打開失敗,請檢查文件路徑!";
}
else
{
//逐行讀取文本,分離路徑和標簽
std::string line, path, classlabel;
//逐行讀取
while (getline(file, line))
{
//將讀取到的文本轉(zhuǎn)為字符串流
std::stringstream stream(line);
//分離路徑
getline(stream, path, separator);
//分離標簽
getline(stream, classlabel);
//若分離成功,則按照路徑載入圖像,設(shè)置標簽
if(!path.empty() && !classlabel.empty())
{
images.push_back(cv::imread(path,0));
labels.push_back(atoi(classlabel.c_str()));
}
}
}
}
widget.cpp
#include "widget.h"
#include "ui_widget.h"
//引用
#include "sm.h"
#include "opencv2/core.hpp"
#include "opencv2/face.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"
#include <iostream>
#include <fstream>
#include <sstream>
#include <QFileDialog>
#include <QDebug>
#include <QMessageBox>
#include <qdatetime.h>
//進行人臉識別的路徑
QString face_path;
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
}
Widget::~Widget()
{
delete ui;
}
//選擇文件
void Widget::on_pushButton_clicked()
{
QString filename = QFileDialog::getOpenFileName(this,"請選擇列表文件",".","*.txt");
if(!filename.isEmpty())
{
ui->lineEdit->setText(filename);
}
}
//選擇保存目錄
void Widget::on_pushButton_7_clicked()
{
QString dir = QFileDialog::getExistingDirectory(this,"請選擇保存目錄",".");
if(!dir.isEmpty())
{
QString str = dir + "/";
ui->lineEdit_3->setText(str);
}
}
//訓(xùn)練模型
void Widget::on_pushButton_2_clicked()
{
//獲取文件路徑
std::string src_filename = ui->lineEdit->text().toStdString();
if(src_filename.empty())
{
QMessageBox::warning(this,"警告","圖像載入失敗,請檢查文件路徑!");
return;
}
//圖像集合
std::vector<cv::Mat> src_images;
//標簽集合
std::vector<int> src_labels;
//加載文件
sm::read_csv(src_filename,src_images,src_labels,';');
//判斷讀取是否成功
if(src_images.size()<=1||src_labels.size()<=1)
{
QMessageBox::warning(this,"警告","數(shù)據(jù)量不足,請檢查數(shù)據(jù)列表!");
return;
}
//調(diào)試
qDebug()<<src_images.size();
qDebug()<<src_labels.size();
//獲取圖像保存路徑
std::string dst_path = ui->lineEdit_3->text().toStdString();
if(dst_path.empty())
{
QMessageBox::warning(this,"警告","請檢查文件保存路徑!");
return;
}
//圖像預(yù)處理,生成新文件
sm::pretreatment(src_images,src_labels,dst_path,100,100);
//獲取新文件路徑
std::string dst_filename = dst_path+"list.txt";
//新圖像集合
std::vector<cv::Mat> dst_images;
//新標簽集合
std::vector<int> dst_labels;
//重新加載文件
sm::read_csv(dst_filename,dst_images,dst_labels,';');
// 創(chuàng)建模型
cv::Ptr<cv::face::FisherFaceRecognizer> model = cv::face::FisherFaceRecognizer::create();
// 訓(xùn)練模型
model->train(dst_images, dst_labels);
//保存模型
model->write(dst_path+"model.xml");
//提示
QMessageBox::information(this,"消息","模型訓(xùn)練完成!");
}
//選擇模型路徑
void Widget::on_pushButton_3_clicked()
{
QString filename = QFileDialog::getOpenFileName(this,"請選擇模型",".","*.xml");
if(!filename.isEmpty())
{
ui->lineEdit_2->setText(filename);
}
}
//選擇需要識別的圖像,縮放,保持比例,顯示
void Widget::on_pushButton_5_clicked()
{
face_path = QFileDialog::getOpenFileName(this,"選擇一個圖片",".","*.jpg *.png *.bmp");
if(!face_path.isEmpty())
{
//加載圖像
QPixmap* pix= new QPixmap;
pix->load(face_path);
//圖像縮放
QPixmap* npix= new QPixmap;
*npix = pix->scaled(ui->label_4->size(),Qt::KeepAspectRatio);
//顯示
ui->label_4->setPixmap(*npix);
}
}
//人臉識別
void Widget::on_pushButton_6_clicked()
{
cv::Mat src,
dst_shear,
dst_resize;
//創(chuàng)建級聯(lián)分類器
cv::CascadeClassifier cascade;
//載入Haar特征分類器
cascade.load("C:/opencv/date/haarcascade_frontalface_default.xml");
//加載圖像
if(face_path.isEmpty())
{
QMessageBox::warning(this,"警告","請先選擇一個圖像!");
return;
}
else
{
src = cv::imread(face_path.QString::toStdString(),0);
}
//創(chuàng)建矩形容器
std::vector<cv::Rect> rects;
//識別人臉
cascade.detectMultiScale(src,rects);
//裁剪圖像
dst_shear = src(rects[0]).clone();
//縮放
cv::resize(dst_shear,dst_resize,cv::Size(100,100),0,0,cv::INTER_AREA);
if(ui->lineEdit_2->text().isEmpty())
{
QMessageBox::warning(this,"警告","請檢查模型加載路徑!");
}
else
{
// 創(chuàng)建模型
cv::Ptr<cv::face::FisherFaceRecognizer> model = cv::face::FisherFaceRecognizer::create();
//載入訓(xùn)練好的模型
model->read(ui->lineEdit_2->text().QString::toStdString());
//進行識別
int predictedLabel;
double confidence;
model->predict(dst_resize,predictedLabel,confidence);
//打印結(jié)果
QDateTime cur = QDateTime::currentDateTime();
QString str;
switch (predictedLabel)
{
case 1:
str = "周敏慧";
break;
case 2:
str = "林志玲";
break;
case 3:
str = "黃渤";
break;
case 4:
str = "單大偉";
break;
default:
str = "這個人我不認識!";
}
ui->textBrowser->append(cur.toString("yyyy-MM-dd hh:mm:ss"));
ui->textBrowser->append(str);
}
}
widget.ui
測試結(jié)果
? ? ? ? 綜上,將導(dǎo)入的圖像進行裁剪和縮放,僅保存人臉部分,用于訓(xùn)練模型;然后加載訓(xùn)練好的模型,進行人臉識別,最后將識別的信息予以顯示。文章來源:http://www.zghlxwxcb.cn/news/detail-484898.html
? ? ? ? 代碼經(jīng)過修改,可以用于 門禁系統(tǒng) 或者 人臉打卡 。文章來源地址http://www.zghlxwxcb.cn/news/detail-484898.html
到了這里,關(guān)于Qt-OpenCV學(xué)習(xí)筆記--人臉識別的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!