【基于OpenCV、Qt的人脸识别及硬件控制】

前言

本篇文章主要介绍使用Qt编程进行串口调试,并且基于 OpenCV实现人脸识别、以及模型训练及预测。我也是一个初学者,借此文章记录学习过程,并且进行自我总结,在写文章的过程中可以使得自己的思路更加清晰,也可以大家互相学习。

项目框架

整体的项目框架可以分为(1)串口调试:创建串口、设置相应的参数信息,并打开串口;(2)人脸识别:打开摄像头、读取图片并对图片进行处理、进行人脸识别,并将图片转为Qt可识别类型;(3)模型训练:读取识别到的脸的位置图、进行灰度处理、将face存到数组、进行模型训练,并将训练结果保存;(4)检测是否有训练模型(训练完才预测)、读取识别到的脸的位置图并进行灰度处理、进行机器预测;

串口调试

首先运行keil5中的串口程序,再打开硬件仿真,最后打开虚拟串口驱动。用Qt实现com1口。这里我们要添加核心库,在**.pro文件中添加如下代码:

QT += core gui serialport

在mainwindow.h中声明开串口函数,并定义指向串口的指针变量;

void openUart();
QSerialPort *port;

接下来就是创建一个串口,定义串口名,设置串口的相应参数,并打开该串口(串口的设置可参考串口调试助手,如下图:)
在这里插入图片描述
相关代码如下:

void MainWindow::openUart()
 {
 	QString uartName = "com1";//定义要使用的串口的名字
	port = new QSerialPort(uartName);//创建com1串口的实例指针变量
	port->setBaudRate(QSerialPort::Baud9600);//设置串口通信速度 9600bit/s
	port->setDataBits(QSerialPort::Data8);//设置串口通信的数据位
	port->setStopBits(QSerialPort::OneStop);//设置串口通信的停止位
	port->setParity(QSerialPort::NoParity);//设置串口通信的校验 无校验
	port->setFlowControl(QSerialPort::NoFlowControl);//设置串口通信的流控制:无(不存在串口和设备通信数据不同步)
//打开串口
if(port->open(QSerialPort::ReadWrite))
	QMessageBox::warning(this,"serival test","serial open OK!");
else
	QMessageBox::warning(this,"seival test","serial open ERROR!");
}

添加按钮,编写按钮的槽函数。UI布局如下:
在这里插入图片描述

部分代码如下:

void MainWindow::on_open_LED_clicked()
 {
 qDebug()<<"open_LED";
 char cmd = '0';
 port->write(&cmd,1);
 }
 void MainWindow::on_close_LED_clicked()
 {
 qDebug()<<"close_LED";
 char cmd = '1';
 port->write(&cmd,1);
 }
 void MainWindow::on_open_RELAY_clicked()
 {
 qDebug()<<"open_RELAY";
 char cmd = '2';
 port->write(&cmd,1);
 }
 void MainWindow::on_close_RELAY_clicked()
 {
 qDebug()<<"close_RELAY";
 char cmd = '3';
 port->write(&cmd,1);
 }
 void MainWindow::on_open_FAN_clicked()
 {
 qDebug()<<"open_FAN";
 char cmd = '4';
 port->write(&cmd,1);
 }
 void MainWindow::on_close_FAN_clicked()
 {
 qDebug()<<"close_FAN";
 char cmd = '5';
 port->write(&cmd,1);
 }
 void MainWindow::on_open_BUZZER_clicked()
 {
 qDebug()<<"open_BUZZER";
 char cmd = '6';
 port->write(&cmd,1);
 }
 void MainWindow::on_close_BUZZER_clicked()
 {
 qDebug()<<"close_BUZZER";
 char cmd = '7';
 port->write(&cmd,1);
 }
 void MainWindow::on_open_camera_clicked()
 {
 timer_ID = startTimer(30);
 ui->open_camera->setEnabled(false);
 }
 void MainWindow::on_end_camera_clicked()
 {
 killTimer(timer_ID);
 timer_ID = 0;
 ui->open_camera->setEnabled(true);
 }
 void MainWindow::on_face_train_clicked()
 {
 cout<<"开始录入人脸"<<endl;
 flag = 0;
 study_timerID = startTimer(50);

}

人脸识别

首先打开摄像头、读取图片并对图片进行处理、进行人脸识别,将图片转成QImage对象,将QPixmap图像显示到label中(QPixmap主要是用于绘图,针对屏幕显示而最佳化设计),部分代码如下:

void MainWindow::timerEvent(QTimerEvent *event)//定时器处理函数
 //若timer_ID、study_timerID、check_timerID定的时间到时,则会进入此函数
 {
 /***人脸识别显示到label中***/
 if(timer_ID == event->timerId())
 {
	 v.read(src);//把读到的摄像头图片存储到原图src中
	 flip(src,src,1);//将图片进行翻转,否则label中呈现出来的是和实际动作相反的
	 cvtColor(src,rgb,CV_BGR2RGB);//要在label中显示图片需要将bgr图片转换为rgb图片
 	 cvtColor(rgb,gray,CV_BGR2GRAY);//将后续用到的rgb图片转换为gray灰度图为了处理人脸识别时数据是单通道的,数据量小
	 c.detectMultiScale(gray,faces,1.1,3,0,Size(24,24));//人脸识别
	 for(uint i=0;i<faces.size();i++)//循环找屏幕中的多个脸
	 rectangle(rgb,faces[i],Scalar(255,0,0),2,LINE_8,0);//并给检测到的人脸画红框
 	 cv::resize(rgb,rgb,Size(400,300));//更改rgb图片的大小
	 ui->label_show->resize(QSize(400,300));//更改label的大小
 QImage img(rgb.data,rgb.cols,rgb.rows,rgb.cols*rgb.channels(),QImage::Format_RGB888);

 //要在label中显示图片需把Mat类型的rgb图片转换为QImage类型图片
	 ui->label_show->setPixmap(QPixmap::fromImage(img));//要在label中显示图片需把QImage图片转换为QPixmap类型
 }

模型训练

根据提供的图片模型通过算法生成数据模型,从而在其它图片中查找相关的目标。在判断之前,我们要先进行学习,生成人脸的模型以便后续识别使用。这里需要用到FaceRecognizer类,FaceRecognizer类是opencv提供的人脸识别器基类,LBPHFaceRecognizer是根据LBPH算法实现的识别器类,其中LBPHFaceRecognizer识别器支持在原有模型基础上继续学习(模型数据可以累计)。
创建LBPHFaceRecognizer识别器对象:所需的头文件:#include <opencv2/face.hpp>、using namespace cv::face;
创建空的人脸识别器对象:Ptr recognizer =LBPHFaceRecognizer::create();
根据已有的模型创建人脸识别器对象,在创建人脸识别器的时候,需要一个已经学习好的模型文件:Ptr recognizer = FaceRecognizer::load(“模型文件.xml”);然后进行机器学习并保存模型;相关代码如下:

 if(study_timerID == event->timerId())
 {
 	qDebug()<<"start model_train";
	Mat face;
	if(faces.empty())return;//如果vector<Rect> faces中是空的直接返回
		 face = src(faces[0]);//把src中人脸位置的部分图给face
 	cv::resize(face,face,Size(100,100));//把人脸位置的部分图的大小设置为100*100
 	cvtColor(face,face,CV_BGR2GRAY);//将face图进行灰度转换
 	train_faces.push_back(face);//把face图存储到vector<Mat> train_faces中
 	train_labels.push_back(1);//把label标签存储到vector<int> train_labels中
 	count++;//每进一次中断训练的图个数+1
 	if(count==100)//训练到100张为止
	 {
		count = 0;
 		recognizer->update(train_faces,train_labels);//图片模型训练
 		recognizer->save("D:/opencv/heads/myface.xml");//保存训练的模型
 		killTimer(study_timerID);//关闭定时器
 		QMessageBox::warning(this,"train","train OK");//提示
 		flag = 1;//训练完之后可以进行预测置1
 		train_faces.clear();//清空图片数组
 		train_labels.clear();//图片标签数组
	 }
 }

机器预测

如果成功则把按钮打开
进行机器预测:判断这个人脸到底是谁,用到的函数如下:

void predict(InputArray src, int &label, double &confidence)

int &label:预测后的标签,学习时对应的标签,标签为-1时匹配不到训练的图片标签;
double &confidence:预测出结果的可信度,数值越小可信度越高;
设置可信度极值:功能函数为:

void setThreshold(double val);

double val:预测可信度极值,预测可信度超出极值则预测失败。
预测相关代码如下:

if(check_timerID == event->timerId())//将人脸进行预测
 {
 	if(flag == 1)//训练完了的话可以进行预测了
 	{
 		qDebug()<<"start pridict";
 		QFile file("D:/opencv/heads/myface.xml");//判断是否有训练的模型
 		if(file.exists())//如果训练模型的文件存在
 		{
 			if(faces.empty() || recognizer->empty())return;
 			Mat face = src(faces[0]);//将src文件中的人脸位置图片存储到face中
 			cvtColor(face,face,CV_BGR2GRAY);//人脸位置图片进行灰度处理
 			cv::resize(face,face,Size(100,100));//重新设置图片大小
 			int label = -1;//标签为-1时匹配不到训练的图片标签
 			double confidence = 0.0;
 			recognizer->predict(face,label,confidence);//机器预测
 			qDebug()<<"label:"<<label;
 			qDebug()<<"confidence:"<<confidence;
 			if(label != -1)
			 {
 				ui->open_LED->setEnabled(true);
 				ui->open_RELAY->setEnabled(true);
 				ui->open_FAN->setEnabled(true);
 				ui->open_BUZZER->setEnabled(true);
 				ui->close_LED->setEnabled(true);
 				ui->close_RELAY->setEnabled(true);
 				ui->close_FAN->setEnabled(true);
 				ui->close_BUZZER->setEnabled(true);
 			}
 			else qDebug()<<"myface.xml NOT exits";
 		}
 	}
 }

关键技术

通过级联分类器实现人脸识别

opencv级联分类器工具类 : CascadeClassifier //struct Student
2>加载级联分类器配置文件 : bool load( const String& filename )
参数1:分类器数据文件的名字
返回值:成功true,失败false
3>更改色彩区间位灰度图
void cvtColor( InputArray src, OutputArray dst, int code, int dstCn = 0 );
参数1:输入图像
参数2:输出图像
参数3:色彩空间转换码

槽函数

信号和槽机制是Qt的核心机制当一个对象的状态发生变化时,通过信号的方式通知其他对象,其他对象通过执行相应的槽函数来响应该信号。
槽:其实就是一个处理函数,是在对象中声明为slots:之下的函数及其实现。槽是一个对象对他感兴趣的对象的某个事件做出处理。其信号槽工作的过程是:当一个对象发射一个信号的时候,则和其连接的对象的槽函数进行处理,等槽函数处理完成之后退出并执行接下来的内容。
槽函数:本质 就是类的成员函数,我们可以调用类的成员函数一样来调用槽函数,槽函数可以跟信号建立起关联,而普通的成员函数不可以。

定时器事件

需要包含头文件:#include< QTimerEvent>
(1)定时器事件处理函数:void timerEvent(QTimerEvent* event);
当定时器发生(或超时)时,会调用timerEvent(QTimerEvent* event),如果定时器在运行,可用通
过event->timerId()来区分当前是哪一个定时器被触发了。
(2)启动定时器:int startTimer(int interval,Qt::TimerType timerType = Qt::CoarseTime);
调用startTimer启动一个定时器,并返回定时器id,如果启动失败则返回0。
参数1:定时器事件,默认为毫秒,定时器每隔interval毫秒就会启动一次,直到调用killTimer()。
参数2:定时器精度
(3)关闭定时器:void killTimer(int id);
参数:关闭id定时器
注意:当一个定时器被关闭后使用的id标识变量要清零。

项目演示

人脸识别项目演示

  • 6
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Holy meat

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值