• Qt-OpenCV学习笔记--人脸识别


    前言

    本人从事机械设计12年,业余时间自学编程。

    2022年4月6日,开始学习C#,

    2022年9月7日,开始学习c++和Qt,

    2022年10月28日,开始学习OpenCV

    今天终于搞定了传说中的 人脸识别 ,在此,做个记录。

    人脸检测,是基于Haar特征的cascade分类器,

    人脸识别,是基于LDA理论的Fisherface算法。

    话不多说,上视频!(CSDN上传的视频,太清晰!)

    人脸识别测试程序

    测试代码

    FaceRecognition.pro

    1. QT += core gui
    2. greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
    3. CONFIG += c++11
    4. # You can make your code fail to compile if it uses deprecated APIs.
    5. # In order to do so, uncomment the following line.
    6. #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
    7. SOURCES += \
    8. main.cpp \
    9. sm.cpp \
    10. widget.cpp
    11. HEADERS += \
    12. sm.h \
    13. widget.h
    14. FORMS += \
    15. widget.ui
    16. # Default rules for deployment.
    17. qnx: target.path = /tmp/$${TARGET}/bin
    18. else: unix:!android: target.path = /opt/$${TARGET}/bin
    19. !isEmpty(target.path): INSTALLS += target
    20. unix|win32: LIBS += -L$$PWD/../../../../../opencv/install/x64/mingw/lib/ -llibopencv_world460.dll
    21. INCLUDEPATH += $$PWD/../../../../../opencv/install/include
    22. DEPENDPATH += $$PWD/../../../../../opencv/install/include

    sm.h

    1. #ifndef SM_H
    2. #define SM_H
    3. #include
    4. #include "opencv2/core.hpp"
    5. class sm
    6. {
    7. public:
    8. sm();
    9. //读取文件
    10. static void read_csv(const std::string& filename, std::vector& images, std::vector<int>& labels, char separator);
    11. //图像预处理:检测人脸、裁剪、缩放、保存、生成列表
    12. static void pretreatment(std::vector images, std::vector<int> labels,std::string path,int width,int height);
    13. };
    14. #endif // SM_H

    widget.h

    1. #ifndef WIDGET_H
    2. #define WIDGET_H
    3. #include
    4. QT_BEGIN_NAMESPACE
    5. namespace Ui { class Widget; }
    6. QT_END_NAMESPACE
    7. class Widget : public QWidget
    8. {
    9. Q_OBJECT
    10. public:
    11. Widget(QWidget *parent = nullptr);
    12. ~Widget();
    13. private slots:
    14. void on_pushButton_clicked();
    15. void on_pushButton_7_clicked();
    16. void on_pushButton_2_clicked();
    17. void on_pushButton_3_clicked();
    18. void on_pushButton_5_clicked();
    19. void on_pushButton_6_clicked();
    20. private:
    21. Ui::Widget *ui;
    22. };
    23. #endif // WIDGET_H

    main.cpp

    1. #include "widget.h"
    2. #include
    3. int main(int argc, char *argv[])
    4. {
    5. QApplication a(argc, argv);
    6. Widget w;
    7. w.show();
    8. return a.exec();
    9. }

    sm.cpp

    1. #include "sm.h"
    2. //引用依赖
    3. #include "opencv2/core.hpp"
    4. #include "opencv2/face.hpp"
    5. #include "opencv2/highgui.hpp"
    6. #include "opencv2/imgproc.hpp"
    7. #include <iostream>
    8. #include <fstream>
    9. #include <sstream>
    10. #include <QDebug>
    11. sm::sm()
    12. {
    13. }
    14. void sm::pretreatment(std::vector<cv::Mat> images, std::vector<int> labels, std::string path, int width, int height)
    15. {
    16. cv::Mat dst_shear;
    17. cv::Mat dst_resize;
    18. //创建级联分类器
    19. cv::CascadeClassifier cascade;
    20. //载入Haar特征分类器
    21. cascade.load("C:/opencv/date/haarcascade_frontalface_default.xml");
    22. //创建矩形容器
    23. std::vector<cv::Rect> rects;
    24. //遍历
    25. int flag=1;
    26. for(uint i=0;i<images.size();i++)
    27. {
    28. //人脸检测
    29. cascade.detectMultiScale(images[i],rects);
    30. //裁剪
    31. dst_shear = images[i](rects[0]).clone();
    32. //缩放
    33. resize(dst_shear,dst_resize,cv::Size(width,height),0,0,cv::INTER_AREA);
    34. //保存路径拼接
    35. std::string im_path1 = std::to_string(labels[i]);
    36. std::string im_path2 = std::to_string(flag);
    37. flag++;
    38. if(labels[i+1]!=labels[i])
    39. {
    40. flag=1;
    41. }
    42. std::string im_path3 = ".png";
    43. std::string im_path = path+im_path1+"-"+im_path2+im_path3;
    44. //调试
    45. QString qstr_im = QString::fromStdString(im_path);
    46. qDebug()<<qstr_im;
    47. //保存
    48. imwrite(im_path,dst_resize);
    49. //显示
    50. //std::string str = std::to_string(i);
    51. //imshow(str,dst_resize);
    52. //生成列表文件
    53. std::string list_name = "list.txt";
    54. std::string list_path = path + list_name;
    55. //调试
    56. QString qstr_list = QString::fromStdString(list_path);
    57. qDebug()<<qstr_list;
    58. std::ofstream list(list_path, std::ios::app);
    59. if (list.fail())
    60. {
    61. qDebug()<<"list文件打开失败,请检查文件路径!";
    62. }
    63. else
    64. {
    65. list<<im_path;
    66. list<<";";
    67. list<<im_path1;
    68. list<<"\n";
    69. }
    70. }
    71. }
    72. void sm::read_csv(const std::string &filename, std::vector<cv::Mat> &images, std::vector<int> &labels, char separator)
    73. {
    74. //以只读方式读取文件
    75. std::ifstream file(filename, std::ios::in);
    76. if (!file)
    77. {
    78. qDebug()<<"文件打开失败,请检查文件路径!";
    79. }
    80. else
    81. {
    82. //逐行读取文本,分离路径和标签
    83. std::string line, path, classlabel;
    84. //逐行读取
    85. while (getline(file, line))
    86. {
    87. //将读取到的文本转为字符串流
    88. std::stringstream stream(line);
    89. //分离路径
    90. getline(stream, path, separator);
    91. //分离标签
    92. getline(stream, classlabel);
    93. //若分离成功,则按照路径载入图像,设置标签
    94. if(!path.empty() && !classlabel.empty())
    95. {
    96. images.push_back(cv::imread(path,0));
    97. labels.push_back(atoi(classlabel.c_str()));
    98. }
    99. }
    100. }
    101. }

    widget.cpp

    1. #include "widget.h"
    2. #include "ui_widget.h"
    3. //引用
    4. #include "sm.h"
    5. #include "opencv2/core.hpp"
    6. #include "opencv2/face.hpp"
    7. #include "opencv2/highgui.hpp"
    8. #include "opencv2/imgproc.hpp"
    9. #include <iostream>
    10. #include <fstream>
    11. #include <sstream>
    12. #include <QFileDialog>
    13. #include <QDebug>
    14. #include <QMessageBox>
    15. #include <qdatetime.h>
    16. //进行人脸识别的路径
    17. QString face_path;
    18. Widget::Widget(QWidget *parent)
    19. : QWidget(parent)
    20. , ui(new Ui::Widget)
    21. {
    22. ui->setupUi(this);
    23. }
    24. Widget::~Widget()
    25. {
    26. delete ui;
    27. }
    28. //选择文件
    29. void Widget::on_pushButton_clicked()
    30. {
    31. QString filename = QFileDialog::getOpenFileName(this,"请选择列表文件",".","*.txt");
    32. if(!filename.isEmpty())
    33. {
    34. ui->lineEdit->setText(filename);
    35. }
    36. }
    37. //选择保存目录
    38. void Widget::on_pushButton_7_clicked()
    39. {
    40. QString dir = QFileDialog::getExistingDirectory(this,"请选择保存目录",".");
    41. if(!dir.isEmpty())
    42. {
    43. QString str = dir + "/";
    44. ui->lineEdit_3->setText(str);
    45. }
    46. }
    47. //训练模型
    48. void Widget::on_pushButton_2_clicked()
    49. {
    50. //获取文件路径
    51. std::string src_filename = ui->lineEdit->text().toStdString();
    52. if(src_filename.empty())
    53. {
    54. QMessageBox::warning(this,"警告","图像载入失败,请检查文件路径!");
    55. return;
    56. }
    57. //图像集合
    58. std::vector<cv::Mat> src_images;
    59. //标签集合
    60. std::vector<int> src_labels;
    61. //加载文件
    62. sm::read_csv(src_filename,src_images,src_labels,';');
    63. //判断读取是否成功
    64. if(src_images.size()<=1||src_labels.size()<=1)
    65. {
    66. QMessageBox::warning(this,"警告","数据量不足,请检查数据列表!");
    67. return;
    68. }
    69. //调试
    70. qDebug()<<src_images.size();
    71. qDebug()<<src_labels.size();
    72. //获取图像保存路径
    73. std::string dst_path = ui->lineEdit_3->text().toStdString();
    74. if(dst_path.empty())
    75. {
    76. QMessageBox::warning(this,"警告","请检查文件保存路径!");
    77. return;
    78. }
    79. //图像预处理,生成新文件
    80. sm::pretreatment(src_images,src_labels,dst_path,100,100);
    81. //获取新文件路径
    82. std::string dst_filename = dst_path+"list.txt";
    83. //新图像集合
    84. std::vector<cv::Mat> dst_images;
    85. //新标签集合
    86. std::vector<int> dst_labels;
    87. //重新加载文件
    88. sm::read_csv(dst_filename,dst_images,dst_labels,';');
    89. // 创建模型
    90. cv::Ptr<cv::face::FisherFaceRecognizer> model = cv::face::FisherFaceRecognizer::create();
    91. // 训练模型
    92. model->train(dst_images, dst_labels);
    93. //保存模型
    94. model->write(dst_path+"model.xml");
    95. //提示
    96. QMessageBox::information(this,"消息","模型训练完成!");
    97. }
    98. //选择模型路径
    99. void Widget::on_pushButton_3_clicked()
    100. {
    101. QString filename = QFileDialog::getOpenFileName(this,"请选择模型",".","*.xml");
    102. if(!filename.isEmpty())
    103. {
    104. ui->lineEdit_2->setText(filename);
    105. }
    106. }
    107. //选择需要识别的图像,缩放,保持比例,显示
    108. void Widget::on_pushButton_5_clicked()
    109. {
    110. face_path = QFileDialog::getOpenFileName(this,"选择一个图片",".","*.jpg *.png *.bmp");
    111. if(!face_path.isEmpty())
    112. {
    113. //加载图像
    114. QPixmap* pix= new QPixmap;
    115. pix->load(face_path);
    116. //图像缩放
    117. QPixmap* npix= new QPixmap;
    118. *npix = pix->scaled(ui->label_4->size(),Qt::KeepAspectRatio);
    119. //显示
    120. ui->label_4->setPixmap(*npix);
    121. }
    122. }
    123. //人脸识别
    124. void Widget::on_pushButton_6_clicked()
    125. {
    126. cv::Mat src,
    127. dst_shear,
    128. dst_resize;
    129. //创建级联分类器
    130. cv::CascadeClassifier cascade;
    131. //载入Haar特征分类器
    132. cascade.load("C:/opencv/date/haarcascade_frontalface_default.xml");
    133. //加载图像
    134. if(face_path.isEmpty())
    135. {
    136. QMessageBox::warning(this,"警告","请先选择一个图像!");
    137. return;
    138. }
    139. else
    140. {
    141. src = cv::imread(face_path.QString::toStdString(),0);
    142. }
    143. //创建矩形容器
    144. std::vector<cv::Rect> rects;
    145. //识别人脸
    146. cascade.detectMultiScale(src,rects);
    147. //裁剪图像
    148. dst_shear = src(rects[0]).clone();
    149. //缩放
    150. cv::resize(dst_shear,dst_resize,cv::Size(100,100),0,0,cv::INTER_AREA);
    151. if(ui->lineEdit_2->text().isEmpty())
    152. {
    153. QMessageBox::warning(this,"警告","请检查模型加载路径!");
    154. }
    155. else
    156. {
    157. // 创建模型
    158. cv::Ptr<cv::face::FisherFaceRecognizer> model = cv::face::FisherFaceRecognizer::create();
    159. //载入训练好的模型
    160. model->read(ui->lineEdit_2->text().QString::toStdString());
    161. //进行识别
    162. int predictedLabel;
    163. double confidence;
    164. model->predict(dst_resize,predictedLabel,confidence);
    165. //打印结果
    166. QDateTime cur = QDateTime::currentDateTime();
    167. QString str;
    168. switch (predictedLabel)
    169. {
    170. case 1:
    171. str = "周敏慧";
    172. break;
    173. case 2:
    174. str = "林志玲";
    175. break;
    176. case 3:
    177. str = "黄渤";
    178. break;
    179. case 4:
    180. str = "单大伟";
    181. break;
    182. default:
    183. str = "这个人我不认识!";
    184. }
    185. ui->textBrowser->append(cur.toString("yyyy-MM-dd hh:mm:ss"));
    186. ui->textBrowser->append(str);
    187. }
    188. }

    widget.ui

    测试结果

            综上,将导入的图像进行裁剪和缩放,仅保存人脸部分,用于训练模型;然后加载训练好的模型,进行人脸识别,最后将识别的信息予以显示。

            代码经过修改,可以用于 门禁系统 或者 人脸打卡

  • 相关阅读:
    【指针内功修炼】函数指针 + 函数指针数组 + 回调函数(二)
    7.15 SpringBoot项目实战 【学生入驻】(上):从API接口定义 到 Mybatis查询 串讲
    基于springboot接口的编写
    科技云报道:全球勒索攻击创历史新高,如何建立网络安全的防线?
    链路负载均衡之策略路由
    bash shell 初体验-尚文网络xUP楠哥
    I2C的硬件实现
    Node.js 模块化及npm概念介绍
    微信小程序底部安全区域高度获取
    解决electron设置透明背景后,引入element-plus样式问题
  • 原文地址:https://blog.csdn.net/ssismm/article/details/128209030