当前位置:网站首页>基于STM32单片机的测温仪(带人脸检测)
基于STM32单片机的测温仪(带人脸检测)
2022-07-05 09:11:00 【华为云】
基于STM32单片机的测温仪(带人脸检测)
由于医学发展的需要,在很多情况下,一般的温度计己经满足不了快速而又准确的测温要求,例如:车站、地铁、机场等人口密度较大的地方进行人体温度测量。
当前设计的这款测温仪由测温硬件+上位机软件组合而成,主要用在地铁、车站入口等地方,可以准确识别人脸进行测温,如果有人温度超标会进行语音提示并且保存当前人脸照片。
1、 硬件选型与设计思路
(1). 设备端
主控单片机采用STM32F103C8T6,人体测温功能采用非接触式红外测温模块。
(2). 上位机设计思路
上位机采用Qt5设计,Qt5是一套基于C++语言的跨平台软件库,性能非常强大,目前桌面端很多主流的软件都是采用QT开发。比如: 金山办公旗下的-WPS,字节跳动旗下的-剪映,暴雪娱乐公司旗下-多款游戏登录器等等。Qt在车联网领域用的也非常多,比如,哈佛,特斯拉,比亚迪等等很多车的中控屏整个系统都是采用Qt设计。
在测温项目里,上位机与STM32之间采用串口协议进行通信,上位机可以打开笔记本电脑默认的摄像头,进行人脸检测;当检测到人脸时,控制STM32测量当前人体的实时温度实时,再将温度传递到上位机显示;当温度正常时,上位机上显示绿色的提示字样“温度正常”,并有语音播报,语音播报的声音使用笔记本自带的声卡发出。如果温度过高,上位机显示红色提示字样“温度异常,请重新测量”,并有语音播报提示。温度过高时,会自动将当前人脸拍照留存,照片存放在当前软件目录下的“face”目录里,文件的命名规则是“38.8_2022-01-05-22-12-34.jpg”,其中38.8表示温度值,后面是日期(年月日时分秒)。
(3) 上位机运行效果
上位机需要连接STM32设备之后才可以获取温度数据,点击软件上的打开摄像头按钮,开启摄像头,让检测到人脸时,下面会显示当前测量的温度。如果没有连接STM32设备,那么默认会显示一个正常的固定温度值。界面上右边红色的字,表示当前处理一帧图像的耗时时间,电脑性能越好,检测速度越快。
(4) 拿到可执行文件之后如何运行?
先解压压缩包,进入“测温仪上位机-可执行文件”目录,将“haarcascade_frontalface_alt2.xml”拷贝到C盘根目录。
然后双击“FaceTemperatureCheck.exe”运行程序。
未连接设备,也可以打开摄像头检测人脸,只不过温度值是一个固定的正常温度值范围。
二、上位机设计
2.1 安装编译环境
如果需要自己编译运行源代码,需要先安装Qt5开发环境。
下载地址:https://download.qt.io/official_releases/qt/5.12/5.12.0/
下载之后,先将电脑网络断掉(不然会提示要求输入QT的账号),然后双击安装包进行安装。
安装可以只需要选择一个MinGW 32位编译器就够用了,详情看下面截图,选择“MinGW 7.3.0 32-bit”后,就点击下一步,然后等待安装完成即可。
2.2 软件代码整体效果
打开工程后(工程文件的后缀是xxx.pro),点击左下角的绿色三角形按钮就可以编译运行程序。
2.3 UI设计界面
2.4 人脸检测核心代码
//人脸检测代码 bool ImageHandle::opencv_face(QImage qImage) { bool check_flag=0; QTime time; time.start(); static CvMemStorage* storage = nullptr; static CvHaarClassifierCascade* cascade = nullptr; //模型文件路径 QString face_model_file =QCoreApplication::applicationDirPath()+"/"+FACE_MODEL_FILE; //加载分类器:正面脸检测 cascade =(CvHaarClassifierCascade*)cvLoad(face_model_file.toUtf8().data(),0,0,0); if(!cascade) { qDebug()<<"分类器加载错误.\n"; return check_flag; } //创建内存空间 storage =cvCreateMemStorage(0); //加载需要检测的图片 IplImage* img =QImageToIplImage(&qImage); if(img ==nullptr ) { qDebug()<<"图片加载错误.\n"; return check_flag; } double scale=1.2; //创建图像首地址,并分配存储空间 IplImage* gray =cvCreateImage(cvSize(img->width,img->height),8,1); //创建图像首地址,并分配存储空间 IplImage* small_img=cvCreateImage(cvSize(cvRound(img->width/scale),cvRound(img->height/scale)),8,1); cvCvtColor(img,gray, CV_BGR2GRAY); cvResize(gray, small_img, CV_INTER_LINEAR); cvEqualizeHist(small_img,small_img);//直方图均衡 /* * 指定相应的人脸特征检测分类器,就可以检测出图片中所有的人脸,并将检测到的人脸通过矩形的方式返回。 * 总共有8个参数,函数说明: 参数1:表示输入图像,尽量使用灰度图以加快检测速度。 参数2:表示Haar特征分类器,可以用cvLoad()函数来从磁盘中加载xml文件作为Haar特征分类器。 参数3:用来存储检测到的候选目标的内存缓存区域。 参数4:表示在前后两次相继的扫描中,搜索窗口的比例系数。默认为1.1即每次搜索窗口依次扩大10% 参数5:表示构成检测目标的相邻矩形的最小个数(默认为3个)。如果组成检测目标的小矩形的个数和小于 min_neighbors - 1 都会被排除。如果min_neighbors 为 0, 则函数不做任何操作就返回所有的被检候选矩形框,这种设定值一般用在用户自定义对检测结果的组合程序上。 参数6:要么使用默认值,要么使用CV_HAAR_DO_CANNY_PRUNING,如果设置为CV_HAAR_DO_CANNY_PRUNING,那么函数将会使用Canny边缘检测来排除边缘过多或过少的区域,因此这些区域通常不会是人脸所在区域。 参数7:表示检测窗口的最小值,一般设置为默认即可。 参数8:表示检测窗口的最大值,一般设置为默认即可。 函数返回值:函数将返回CvSeq对象,该对象包含一系列CvRect表示检测到的人脸矩形。 */ CvSeq* objects =cvHaarDetectObjects(small_img, cascade, storage, 1.1, 3, 0/*CV_HAAR_DO_CANNY_PRUNING*/, cvSize(50,50)/*大小决定了检测时消耗的时间多少*/); qDebug()<<"人脸数量:"<<objects->total; //遍历找到对象和周围画盒 QPainter painter(&qImage);//构建 QPainter 绘图对象 QPen pen; pen.setColor(Qt::blue);//画笔颜色 pen.setWidth(5);//画笔宽度 painter.setPen(pen);//设置画笔 CvRect *max=nullptr; for(int i=0;i<(objects->total);++i) { //得到人脸的坐标位置和宽度高度信息 CvRect* r=(CvRect*)cvGetSeqElem(objects,i); if(max==nullptr)max=r; else { if(r->width>max->width||r->height>max->height) { max=r; } } } //如果找到最大的目标脸 if(max!=nullptr) { check_flag=true; //将人脸区域绘制矩形圈起来 painter.drawRect(max->x*scale,max->y*scale,max->width*scale,max->height*scale); } cvReleaseImage(&gray); //释放图片内存 cvReleaseImage(&small_img); //释放图片内存 cvReleaseHaarClassifierCascade(&cascade);//释放内存-->分类器 cvReleaseMemStorage(&objects->storage);//释放内存-->检测出图片中所有的人脸 //释放图片 cvReleaseImage(&img); qint32 time_ms=0; time_ms=time.elapsed(); //耗时时间 emit ss_log_text(QString("%1").arg(time_ms)); //保存结果 m_image=qImage.copy(); return check_flag; } |
2.5 配置文件(修改参数-很重要)
参数说明:
如果电脑上有多个摄像头,可以修改配置文件里的摄像头编号,具体的数量在程序启动时会自动查询,通过打印代码输出到终端。
如果自己第一次编译运行源码,运行之后,
(1)需要将软件源码目录下的“haarcascade_frontalface_alt2.xml” 文件拷贝到C盘根目录,或者其他非中文目录下,具体路径可以在配置文件里修改,默认就是C盘根目录。
(2)需要将软件源码目录下的“OpenCV-MinGW-Build-OpenCV-3.4.7\x86\mingw\bin”目录里所有文件拷贝到,生成的程序执行文件同级目录下。
这样才能保证程序可以正常运行。
报警温度的阀值范围,也可以自行更改,在配置文件里有说明。
2.6 语音提示文件与背景图
语音提示文件,背景图是通过资源文件加载的。
源文件存放在,源代码的“FaceTemperatureCheck\res”目录下。
自己也可以自行替换,重新编译程序即可生效。
2.7 语音播报与图像显示处理代码
//图像处理的结果 void Widget::slot_HandleImage(boolflag,QImage image) { bool temp_state=0; //检测到人脸 if(flag) { //判断温度是否正常 if(current_temp<MAX_TEMP && current_temp>MIN_TEMP) { temp_state=true; //显示温度正常 ui->label_temp->setStyleSheet("color: rgb(0, 255, 127);font: 20pt \"Arial\";"); ui->label_temp->setText(QString("%1℃").arg(current_temp)); } else//语音播报,温度异常 { temp_state=false; //显示温度异常 ui->label_temp->setStyleSheet("color: rgb(255, 0, 0);font: 20pt \"Arial\";"); ui->label_temp->setText(QString("%1℃").arg(current_temp)); } //获取当前ms时间 longlong currentTime =QDateTime::currentDateTime().toMSecsSinceEpoch(); //判断时间间隔是否到达 if(currentTime-old_currentTime>AUDIO_RATE_MS) { //更改当前时间 old_currentTime=currentTime; //温度正常 if(temp_state) { //语音播报,温度正常 QSound::play(":/res/ok.wav"); } //温度异常 else { //语音播报,温度异常 QSound::play(":/res/error.wav"); //拍照留存 QString dir_str =QCoreApplication::applicationDirPath()+"/face"; //检查目录是否存在,若不存在则新建 QDir dir; if(!dir.exists(dir_str)) { bool res =dir.mkpath(dir_str); qDebug()<<"新建目录状态:"<< res; } //目录存在就保存图片 QDir dir2; if(dir2.exists(dir_str)) { //拼接名称 QDateTime dateTime(QDateTime::currentDateTime()); //时间效果: 2020-03-05 16:25::04 周一 QString qStr=QString("%1/%2_").arg(dir_str).arg(current_temp); qStr+=dateTime.toString("yyyy-MM-dd-hh-mm-ss-ddd"); image.save(qStr+".jpg"); } } } } else //不显示温度 { ui->label_temp->setStyleSheet("color: rgb(0, 255, 127);font: 20pt \"Arial\";"); ui->label_temp->setText("----"); } //处理图像的结果画面 ui->widget_player->slotGetOneFrame(image); } |
2.8 STM32的温度接收处理代码
如果需要完整的工程,可以在这里去下载:https://download.csdn.net/download/xiaolong1126626497/85892490
//刷新串口端口 void Widget::on_pushButton_flush_uart_clicked() { QList<QSerialPortInfo> UartInfoList=QSerialPortInfo::availablePorts();//获取可用串口端口信息 ui->comboBox_ComSelect->clear(); if(UartInfoList.count()>0) { for(int i=0;i<UartInfoList.count();i++) { if(UartInfoList.at(i).isBusy())//如果当前串口 COM 口忙就返回真,否则返回假 { QString info=UartInfoList.at(i).portName(); info+="(占用)"; ui->comboBox_ComSelect->addItem(info);//添加新的条目选项 } else { ui->comboBox_ComSelect->addItem(UartInfoList.at(i).portName());//添加新的条目选项 } } } else { ui->comboBox_ComSelect->addItem("无可用COM口");//添加新的条目选项 } } //连接测温设备 void Widget::on_pushButton_OpenUart_clicked() { if(ui->pushButton_OpenUart->text()=="连接测温设备") //打开串口 { ui->pushButton_OpenUart->setText("断开连接"); /*配置串口的信息*/ UART_Config->setPortName(ui->comboBox_ComSelect->currentText()); //COM的名称 if(!(UART_Config->open(QIODevice::ReadWrite))) //打开的属性权限 { QMessageBox::warning(this,tr("状态提示"), tr("设备连接失败!\n请选择正确的COM口"), QMessageBox::Ok); ui->pushButton_OpenUart->setText("连接测温设备"); return; } } else//关闭串口 { ui->pushButton_OpenUart->setText("连接测温设备"); /*关闭串口-*/ UART_Config->clear(QSerialPort::AllDirections); UART_Config->close(); } } //读信号 void Widget::ReadUasrtData() { /*返回可读的字节数*/ if(UART_Config->bytesAvailable()<=0) { return; } /*定义字节数组*/ QByteArray rx_data; /*读取串口缓冲区所有的数据*/ rx_data=UART_Config->readAll(); //转换温度 current_temp=rx_data.toDouble(); } |
边栏推荐
- Jenkins Pipeline 方法(函数)定义及调用
- Multiple solutions to one problem, asp Net core application startup initialization n schemes [Part 1]
- Ministry of transport and Ministry of Education: widely carry out water traffic safety publicity and drowning prevention safety reminders
- It's too difficult to use. Long articles plus pictures and texts will only be written in short articles in the future
- Explain NN in pytorch in simple terms CrossEntropyLoss
- Causes and appropriate analysis of possible errors in seq2seq code of "hands on learning in depth"
- 牛顿迭代法(解非线性方程)
- 混淆矩阵(Confusion Matrix)
- 阿里云发送短信验证码
- [beauty of algebra] singular value decomposition (SVD) and its application to linear least squares solution ax=b
猜你喜欢
C#【必备技能篇】ConfigurationManager 类的使用(文件App.config的使用)
Solution to the problems of the 17th Zhejiang University City College Program Design Competition (synchronized competition)
Summary of "reversal" problem in challenge Programming Competition
2020 "Lenovo Cup" National College programming online Invitational Competition and the third Shanghai University of technology programming competition
TF coordinate transformation of common components of ros-9 ROS
Rebuild my 3D world [open source] [serialization-2]
生成对抗网络
C [essential skills] use of configurationmanager class (use of file app.config)
Global configuration tabbar
Introduction Guide to stereo vision (3): Zhang calibration method of camera calibration [ultra detailed and worthy of collection]
随机推荐
Oracle advanced (III) detailed explanation of data dictionary
Introduction Guide to stereo vision (1): coordinate system and camera parameters
scipy.misc.imread()
Kubedm series-00-overview
2011-11-21 training record personal training (III)
Mengxin summary of LIS (longest ascending subsequence) topics
太不好用了,长文章加图文,今后只写小短文
Use and programming method of ros-8 parameters
Codeworks round 638 (Div. 2) cute new problem solution
Jenkins Pipeline 方法(函数)定义及调用
图解网络:什么是网关负载均衡协议GLBP?
Uni app implements global variables
生成对抗网络
My life
Confusion matrix
Information and entropy, all you want to know is here
My experience from technology to product manager
AdaBoost use
ECMAScript6介绍及环境搭建
C # image difference comparison: image subtraction (pointer method, high speed)