一百二十一、基于QT的OpenCV库实现人脸识别功能
121.1 UI 界面
登录按钮现在没啥实际作用,因为没加功能,可以添加在识别成功后运行的功能代码
121.2 思路
- 显示人脸: 通过 VideoCapture 这个类下面的 open() 方法打开摄像头,对摄像头读取到的图像帧进行处理。调整人脸图像尺寸,将人脸图像放到矩形框容器中,再将这个图像显示到UI界面上
- 录入人脸: 加载或者创建级联分类器文件(有就是加载,没有就是创建)。对当前图形依次进行灰度处理、均衡化处理,然后放到容器里面,每张人脸对应的标签也放进去(虽然都是1),可以自己设定往容器里面放多少张图像帧。当放完之后就可以转化为人脸模型了,存完人脸模型就可以选择进行验证(即人脸检测识别)。顺手将人脸容器和标签容器清空。
- 人脸识别: 打开人脸模型文件,打开成功后将当前视频帧依次进行灰度处理和均衡化处理,然后根据设置的人脸可信度进行识别,识别成功后就提示识别成功。
121.3 代码
main.cpp
#include "faceidentification.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
faceIdentification w;
w.show();
return a.exec();
}
faceidentification.h
#ifndef FACEIDENTIFICATION_H
#define FACEIDENTIFICATION_H
#include <QWidget>
#include <opencv2/opencv.hpp>
#include <iostream>
#include <math.h>
#include <opencv2/face.hpp>
#include <vector>
#include <map>
#include <QMessageBox>
#include <QDebug>
#include <QFile>
#include <QTextStream>
#include <QDateTime>
#include <QTimerEvent>
#include <QtSerialPort/QtSerialPort>
#include <QtSerialPort/QSerialPortInfo>
using namespace cv;
using namespace cv::face;
using namespace std;
namespace Ui {
class faceIdentification;
}
class faceIdentification : public QWidget
{
Q_OBJECT
public:
explicit faceIdentification(QWidget *parent = 0);
~faceIdentification();
signals:
// 加载配置文件与保存人脸模型文件 的信号
void load_CascadeClassifier_and_create_face_file_SIGNAL();
public slots:
// 加载配置文件与人脸模型文件 的槽函数声明
void load_CascadeClassifier_and_create_face_file_SLOT();
private slots:
// 打开摄像头对应的槽函数声明
void on_openBtn_clicked();
// 关闭摄像头对应的槽函数声明
void on_closeBtn_clicked();
// 开始录入对应的槽函数声明
void on_inputBtn_clicked();
private:
Ui::faceIdentification *ui;
// 摄像头相关成员
// 视频对象
VideoCapture v;
// 图像容器,存放图片帧
Mat src; // 摄像头直接摄入的图像
Mat gray; // 转换成的灰度图
Mat dst; // 灰度图转换成的灰度直方图
Mat rgb; // 摄像头摄入的BGR转换的RGB图像
// 级联分类器对象
CascadeClassifier c;
// 人脸矩形框容器
vector<Rect> faces;
// 重写的定时器超时事件处理函数
void timerEvent(QTimerEvent *event) override;
// 打开摄像头的定时器
int camer_timer_id;
/*** 人脸录入的相关成员 ***/
// 人脸录入的定时器
int study_timer_id;
// 人脸识别器指针
Ptr<LBPHFaceRecognizer> recognizer;
// 存储人脸图像帧的容器
vector<Mat> study_faces;
// 存放人脸图像帧对应的标签
vector<int> study_labs;
// 保存人脸图像帧的次数
int count = 0;
// 用于控制是人脸录入还是人脸检测
bool input_flag = false;
bool check_flag = false;
// 人脸检测的定时器
int check_timer_id;
};
#endif // FACEIDENTIFICATION_H
faceidentification.cpp
#include "faceidentification.h"
#include "ui_faceidentification.h"
faceIdentification::faceIdentification(QWidget *parent) :
QWidget(parent),
ui(new Ui::faceIdentification)
{
ui->setupUi(this);
// 将自定义的 加载配置文件与保存人脸模型文件 的信号与槽函数进行连接
connect(this, &faceIdentification::load_CascadeClassifier_and_create_face_file_SIGNAL,
this,&faceIdentification::load_CascadeClassifier_and_create_face_file_SLOT);
// 初始化UI界面
ui->closeBtn->setEnabled(false);
ui->inputBtn->setEnabled(false);
ui->loginBtn->setEnabled(false);
}
faceIdentification::~faceIdentification()
{
delete ui;
}
// 加载配置文件与人脸模型文件 的槽函数实现
void faceIdentification::load_CascadeClassifier_and_create_face_file_SLOT()
{
// 加载级联分类器配置文件
if(!c.load("D:\\opencv\\heads\\haarcascade_frontalface_alt.xml")){
QMessageBox::warning(this,"警告","级联分类器加载失败");
return ;
}
// 人脸模型文件
QFile faceFile("D:\\opencv\\heads\\my_face.xml");
// 如果人脸模型存在,则加载;若不存在,则创建
if(faceFile.exists()){
recognizer = LBPHFaceRecognizer::load<LBPHFaceRecognizer>("D:\\opencv\\heads\\my_face.xml");
}else{
recognizer = LBPHFaceRecognizer::create();
}
}
// 打开摄像头对应的槽函数实现
void faceIdentification::on_openBtn_clicked()
{
// 打开摄像头
if(!v.open(0)){
QMessageBox::warning(this,"警告","摄像头打开失败");
return ;
}
// 发射 加载配置文件与保存人脸模型文件 的信号
emit load_CascadeClassifier_and_create_face_file_SIGNAL();
// 启动计时器,用于在UI界面上显示人脸
camer_timer_id = startTimer(20);
// 更改UI按钮可用性
ui->closeBtn->setEnabled(true);
ui->inputBtn->setEnabled(true);
ui->openBtn->setEnabled(false);
// 启动人脸检测定时器
check_timer_id = startTimer(1000);
// 表示只进行人脸检测
check_flag = true;
// 设置人脸检测可信度,低于100表示检测成功
recognizer->setThreshold(100);
}
// 关闭摄像头对应的槽函数实现
void faceIdentification::on_closeBtn_clicked()
{
// 关闭摄像头
v.release();
// 关闭定时器
killTimer(camer_timer_id);
// 更改UI按钮可用性
ui->closeBtn->setEnabled(false);
ui->inputBtn->setEnabled(false);
ui->openBtn->setEnabled(true);
ui->label->clear();
}
// 人脸录入按钮对应的槽函数实现
void faceIdentification::on_inputBtn_clicked()
{
// 进行人脸录入,而不是识别
input_flag = true;
check_flag = false;
// 初始化人脸录入次数
count = 0;
// 启动人脸录入定时器
study_timer_id = startTimer(50);
// 将人脸录入按钮设置为不可用状态
ui->inputBtn->setEnabled(false);
}
// 定时器超时事件处理函数
void faceIdentification::timerEvent(QTimerEvent *event)
{
// 打开摄像头的定时器超时
if(camer_timer_id == event->timerId()){
// 从摄像头中读取图像放到src中
v.read(src);
// 将读取进来的镜像图像进行翻转
flip(src, src, 1);
// 将opencv的 BGR 图像转换为 RGB 图像
cvtColor(src, rgb, CV_BGR2RGB);
// 调整 RGB 图像的尺寸,使其能够正好放进UI界面的图像框中
cv::resize(rgb, rgb, Size(ui->label->width(), ui->label->height()));
// 将 RGB 图像转换为灰度图,再转换为灰度直方图(均衡化处理)
cvtColor(rgb, gray, CV_BGR2GRAY);
equalizeHist(gray, dst);
// 将图像上的人脸放到矩形框容器中
c.detectMultiScale(dst, faces);
// 将矩形框绘制到人脸上
for(int i=0; i<faces.size(); i++){
rectangle(rgb, faces[i], Scalar(255, 0, 0), 2);
}
// 定义一个QImage对象,用于将图像放到UI界面上
QImage img(rgb.data, rgb.cols, rgb.rows, rgb.cols*rgb.channels(), QImage::Format_RGB888);
ui->label->setPixmap(QPixmap::fromImage(img));
}
// 人脸录入定时器超时
if(study_timer_id == event->timerId()){
qDebug() << "正在录入,请正对摄像头";
if(input_flag){
// 存储当前图像
Mat face;
// 将从视频中读取来的图像存起来
face = src(faces[0]);
// 灰度处理
cvtColor(face, face, CV_BGR2GRAY);
// 灰度直方图(均衡化处理)
equalizeHist(face, face);
// 保存起来,放到容器中
study_faces.push_back(face);
study_labs.push_back(1);
count++;
// 存了五十张
if(50 == count){
// 将五十张人脸转换为数据模型存起来
recognizer->update(study_faces, study_labs);
recognizer->save("D:\\opencv\\heads\\my_face.xml");
QMessageBox::information(this, "提示", "人脸录入成功");
// 可以进行人脸检测,而不是录入了
check_flag = true;
input_flag = false;
// 清空容器及相关变量,以待下次使用
study_faces.clear();
study_labs.clear();
count = 0;
// 关闭人脸录入定时器
killTimer(study_timer_id);
}
}
}
// 人脸检测识别定时器超时
if(check_timer_id == event->timerId()){
qDebug() << "正在检测";
if(check_flag){
QFile faceFile("D:\\opencv\\heads\\my_face.xml");
if(faceFile.exists()){
// 将视频中的一帧人脸图像放到face中
Mat face;
face = src(faces[0]);
// 灰度处理
cvtColor(face, face, CV_BGR2GRAY);
// 均衡化处理
equalizeHist(face, face);
// 假设匹配后的结果
int lab = -1;
double conf = 0.0;
// 如果匹配成功,则 lab 不再是 -1;若匹配失败,则 lab 还是 -1
recognizer->predict(face, lab, conf);
if(lab != -1){
// 匹配成功,给出提示,并关闭人脸检测定时器
QMessageBox::information(this, "提示", "欢迎回来");
ui->loginBtn->setEnabled(true);
killTimer(check_timer_id);
}
}
}
}
}