基于树莓派4B实现的口罩识别检测设计
硬件环境:
树莓派4B开发板
树莓派摄像头,淘宝有卖,30rmb以内的盗版亲测可用。。
有线音箱,用于播报检测到异常时的语音播报使用
软件环境:
Debian-Pi-Aarch64:树莓派安装的操作系统,git链接:https://gitee.com/openfans-community/Debian-Pi-Aarch64/
vnc:实现远程连接树莓派
emq:一个mqtt服务器,用于将口罩识别结果上传到服务器,远程查看,链接:https://www.emqx.cn/products/broker
qt:远程查看结果的界面程序设计语言
python:口罩识别结果本地检测,语音播放控制,树莓派mqtt数据上传
opencv:摄像头人脸数据采集,人脸数据显示等
paddle-lite:百度的深度学习平台,用于口罩检测算法,链接:https://aistudio.baidu.com/aistudio/index
实现功能:
1.树莓派口罩检测结果实时显示
2.通过检测结果实现语音播报自动控制
3.数据上传服务器远程监测结果
前期准备:
树莓派操作系统安装烧写,vnc远程连接等
opencv等环境配置
口罩识别方案测试:本设计核心功能完全参考https://aistudio.baidu.com/aistudio/projectdetail/2077064
最终效果展示
口罩识别部分代码:
1 void RunTime(std::string det_model_dir, 2 std::string class_model_dir, 3 cv::VideoCapture cap) { 4 cv::Mat img; 5 cap >> img; 6 // Prepare 7 float shrink = 0.2; 8 int width = img.cols; 9 int height = img.rows; 10 int s_width = static_cast<int>(width * shrink); 11 int s_height = static_cast<int>(height * shrink); 12 13 // Detection 14 MobileConfig config; 15 config.set_model_dir(det_model_dir); 16 17 // Create Predictor For Detction Model 18 std::shared_ptr<PaddlePredictor> predictor = 19 CreatePaddlePredictor<MobileConfig>(config); 20 21 // Get Input Tensor 22 std::unique_ptr<Tensor> input_tensor0(std::move(predictor->GetInput(0))); 23 input_tensor0->Resize({1, 3, s_height, s_width}); 24 auto* data = input_tensor0->mutable_data<float>(); 25 26 // Do PreProcess 27 std::vector<float> detect_mean = {104.f, 117.f, 123.f}; 28 std::vector<float> detect_scale = {0.007843, 0.007843, 0.007843}; 29 pre_process(img, s_width, s_height, detect_mean, detect_scale, data, false); 30 31 // Detection Model Run 32 predictor->Run(); 33 34 // Get Output Tensor 35 std::unique_ptr<const Tensor> output_tensor0( 36 std::move(predictor->GetOutput(0))); 37 auto* outptr = output_tensor0->data<float>(); 38 auto shape_out = output_tensor0->shape(); 39 int64_t out_len = ShapeProduction(shape_out); 40 41 // Filter Out Detection Box 42 float detect_threshold = 0.3; 43 std::vector<Object> detect_result; 44 for (int i = 0; i < out_len / 6; ++i) { 45 if (outptr[1] >= detect_threshold) { 46 Object obj; 47 int xmin = static_cast<int>(width * outptr[2]); 48 int ymin = static_cast<int>(height * outptr[3]); 49 int xmax = static_cast<int>(width * outptr[4]); 50 int ymax = static_cast<int>(height * outptr[5]); 51 int w = xmax - xmin; 52 int h = ymax - ymin; 53 cv::Rect rec_clip = 54 cv::Rect(xmin, ymin, w, h) & cv::Rect(0, 0, width, height); 55 obj.rec = rec_clip; 56 detect_result.push_back(obj); 57 } 58 outptr += 6; 59 } 60 61 // Classification 62 config.set_model_dir(class_model_dir); 63 64 // Create Predictor For Classification Model 65 predictor = CreatePaddlePredictor<MobileConfig>(config); 66 67 // Get Input Tensor 68 std::unique_ptr<Tensor> input_tensor1(std::move(predictor->GetInput(0))); 69 int classify_w = 128; 70 int classify_h = 128; 71 input_tensor1->Resize({1, 3, classify_h, classify_w}); 72 auto* input_data = input_tensor1->mutable_data<float>(); 73 int detect_num = detect_result.size(); 74 std::vector<float> classify_mean = {0.5f, 0.5f, 0.5f}; 75 std::vector<float> classify_scale = {1.f, 1.f, 1.f}; 76 float classify_threshold = 0.5; 77 for (int i = 0; i < detect_num; ++i) { 78 cv::Rect rec_clip = detect_result[i].rec; 79 cv::Mat roi = img(rec_clip); 80 81 // Do PreProcess 82 pre_process(roi, 83 classify_w, 84 classify_h, 85 classify_mean, 86 classify_scale, 87 input_data, 88 true); 89 90 // Classification Model Run 91 predictor->Run(); 92 93 // Get Output Tensor 94 std::unique_ptr<const Tensor> output_tensor1( 95 std::move(predictor->GetOutput(1))); 96 auto* outptr = output_tensor1->data<float>(); 97 98 // Draw Detection and Classification Results 99 cv::rectangle(img, rec_clip, cv::Scalar(0, 0, 255), 2, cv::LINE_AA); 100 std::string text = outptr[1] > classify_threshold ? "wear mask" : "no mask"; 101 std::cout << outptr[1] << std::endl; 102 103 104 if(outptr[1] < 0.5) 105 { 106 ofstream outfile; 107 outfile.open("../result.txt"); 108 outfile << "000000000" << endl; 109 outfile.close(); 110 } 111 else 112 { 113 ofstream outfile; 114 outfile.open("../result.txt"); 115 outfile << "111111111" << endl; 116 outfile.close(); 117 } 118 119 120 int font_face = cv::FONT_HERSHEY_COMPLEX_SMALL; 121 double font_scale = 1.f; 122 int thickness = 1; 123 cv::Size text_size = 124 cv::getTextSize(text, font_face, font_scale, thickness, nullptr); 125 float new_font_scale = rec_clip.width * 0.7 * font_scale / text_size.width; 126 text_size = 127 cv::getTextSize(text, font_face, new_font_scale, thickness, nullptr); 128 cv::Point origin; 129 origin.x = rec_clip.x + 5; 130 origin.y = rec_clip.y + text_size.height + 5; 131 cv::putText(img, 132 text, 133 origin, 134 font_face, 135 new_font_scale, 136 cv::Scalar(0, 255, 255), 137 thickness, 138 cv::LINE_AA); 139 } 140 cv::imshow("Mask Detection Demo", img); 141 142 143 144 }
基本流程:获取人脸数据->获取检测结果->将检测结果存储在文件中->结果视频展示
python或者检测结果、播放音频、数据上传到mqtt服务器代码
#!usr/bin/python # coding=utf-8 import pyttsx3 import time #import pygame import paho.mqtt.client as mqtt import os audio_file = '../audio/tips.mp3' def on_connect(client, userdata, flags, rc): print("Connected with result code: " + str(rc)) def on_message(client, userdata, msg): print(msg.topic + " " + str(msg.payload)) client = mqtt.Client() client.on_connect = on_connect client.on_message = on_message client.connect('192.168.31.149', 1883, 600) # 600\u4e3akeepalive\u7684\u65f6\u95f4\u95f4\u9694 #pygame.mixer.init() #pygame.mixer.music.load('abc.wav') #pygame.mixer.music.set_volume(5) while(1): time.sleep(2) try: f = open('../mask/mask_detection_demo/code/result.txt', 'rb') a = f.read() if len(a)>6: print(a[5]) if a[5] == 48: flag = 0 print("no mask") client.publish('topic', payload='000000', qos=0) # pygame.mixer.music.play() os.system('mpv '+audio_file) elif a[5] == 49: flag = 1 print("wear mask") client.publish('topic', payload='11111', qos=0) else: flag =2 print("error code") else: print("len code == 0") finally: if f: f.close() # if flag == 0: # playsound('./audio.mp3') # voice.say(text) # voice.runAndWait() # print("1")
qt界面代码:检测结果监控,需要安装在qt下安装配置mqtt库:参考https://www.cnblogs.com/bliss-/p/12378226.html
#include "QMqttClientTool.h" #include "QDebug" #include <QtWidgets/QMessageBox> #include <QByteArray> #include <unistd.h> QMqttClientTool::QMqttClientTool(QWidget *parent) : QWidget(parent) { // this->setMaximumSize(600,400); // this->setMinimumSize(600,400); this->resize(600,600); this->setWindowTitle("口罩检测系统"); m_client = new QMqttClient(this); m_client ->setClientId("mqtt_ClientId"); btnConnect = new QPushButton(this); btnPublish = new QPushButton(this); btnSubscribe = new QPushButton(this); clean = new QPushButton(this); qlbHostNameTag = new QLabel(this); qlbHostPortTag = new QLabel(this); qlbPubLishTopicTag = new QLabel(this); qlbSubscribeTopicTag = new QLabel(this); qlbPublishMessageTag = new QLabel(this); qleHostName = new QLineEdit(this); // qleHostName->setText("localhost"); qleHostPort = new QLineEdit(this); // qleHostPort->setText("1883"); qlePublishTopic = new QLineEdit(this); // qlePublishTopic->setText("/World"); qleSubscribeTopic = new QLineEdit(this); // qleSubscribeTopic->setText("/World"); qlePublishMessage = new QLineEdit(this); // qlePublishMessage->setText("hello"); brower = new QTextBrowser(this); // label_ver = new QLabel("<u>wangnimei_1216@163.com<u>",this); label_ver = new QLabel("<u>基于树莓派4B的口罩检测系统--黑龙江大学乘风破疫队<u>",this); QFont ft; ft.setPointSize(15); ft.setBold(1); qlbHostNameTag->move(6,6); qlbHostNameTag->setText("Host"); qlbHostNameTag->setFont(ft); qlbHostNameTag->setMaximumSize(65,35); qlbHostNameTag->setMinimumSize(65,35); qleHostName->move(60,6); qleHostName->setFont(ft); //qleHostName->setText("a1aDaWbRIge.iot-as-mqtt.cn-shanghai.aliyuncs.com"); qleHostName->setText("127.0.0.1"); qleHostName->setMaximumSize(200,35); qleHostName->setMinimumSize(200,35); qlbHostPortTag->move(270,6); qlbHostPortTag->setText("Port"); qlbHostPortTag->setFont(ft); qlbHostPortTag->setMaximumSize(65,35); qlbHostPortTag->setMinimumSize(65,35); qleHostPort->move(324,6); qleHostPort->setFont(ft); qleHostPort->setText("1883"); qleHostPort->setMaximumSize(60,35); qleHostPort->setMinimumSize(60,35); btnConnect->move(390,6); btnConnect->setText("Connect"); btnConnect->setFont(ft); btnConnect->setMaximumSize(120,35); btnConnect->setMinimumSize(120,35); qlbPubLishTopicTag->move(6,50); qlbPubLishTopicTag->setText("PublishTopic"); qlbPubLishTopicTag->setFont(ft); qlbPubLishTopicTag->setMaximumSize(130,35); qlbPubLishTopicTag->setMinimumSize(130,35); qlbPubLishTopicTag->setHidden(1); qlePublishTopic->move(145,50); qlePublishTopic->setFont(ft); qlePublishTopic->setText("/a1aDaWbRIge/bliss/user/update"); qlePublishTopic->setMaximumSize(240,35); qlePublishTopic->setMinimumSize(240,35); qlePublishTopic->setHidden(1); btnPublish->move(390,50); btnPublish->setText("Publish"); btnPublish->setFont(ft); btnPublish->setMaximumSize(120,35); btnPublish->setMinimumSize(120,35); btnPublish->setHidden(1); qlbPublishMessageTag->move(6,94); qlbPublishMessageTag->setText("PublishMessage"); qlbPublishMessageTag->setFont(ft); qlbPublishMessageTag->setMaximumSize(160,35); qlbPublishMessageTag->setMinimumSize(160,35); qlbPublishMessageTag->setHidden(1); qlePublishMessage->move(170,94); qlePublishMessage->setFont(ft); qlePublishMessage->setText("{\"led\",\"on\"}"); qlePublishMessage->setMaximumSize(400,35); qlePublishMessage->setMinimumSize(400,35); qlePublishMessage->setHidden(1); btnSubscribe->move(390,94+50); btnSubscribe->setText("Subscribe"); btnSubscribe->setFont(ft); btnSubscribe->setMaximumSize(120,35); btnSubscribe->setMinimumSize(120,35); qlbSubscribeTopicTag->move(6,94+50); qlbSubscribeTopicTag->setText("PublishTopic"); qlbSubscribeTopicTag->setFont(ft); qlbSubscribeTopicTag->setMaximumSize(130,35); qlbSubscribeTopicTag->setMinimumSize(130,35); qleSubscribeTopic->move(145,94+50); qleSubscribeTopic->setFont(ft); qleSubscribeTopic->setText("topic/#"); qleSubscribeTopic->setMaximumSize(240,35); qleSubscribeTopic->setMinimumSize(240,35); QFont font; font.setPointSize(10); // font.setBold(1); brower->move(6,94+50+50); brower->resize(500,360); brower->setFont(font); clean->move(500+10,94+100); clean->setText("Clean"); clean->setFont(ft); clean->setMaximumSize(80,35); clean->setMinimumSize(80,35); label_ver->move(6,560); /*username*/ username_label=new QLabel(this); username_edit=new QLineEdit(this); username_label->move(6,390); username_label->setText("username:"); username_label->setFont(ft); username_label->setMaximumSize(100,35); username_label->setMinimumSize(100,35); username_label->setHidden(1); username_edit->move(110,390); username_edit->setFont(ft); username_edit->setText("bliss&a1aDaWbRIge"); username_edit->setMaximumSize(300,35); username_edit->setMinimumSize(300,35); username_edit->setHidden(1); /*password*/ password_label=new QLabel(this); password_edit=new QLineEdit(this); password_label->move(6,440); password_label->setText("password:"); password_label->setFont(ft); password_label->setMaximumSize(100,35); password_label->setMinimumSize(100,35); password_label->setHidden(1); password_edit->move(110,440); password_edit->setFont(ft); password_edit->setText("252E42329C0440AFA9C372ED9F8505C7C80F9A03EF20FC6E0FDAAC7299A38774"); password_edit->setMaximumSize(300,35); password_edit->setMinimumSize(300,35); password_edit->setHidden(1); /*clientid*/ clientid_label=new QLabel(this); clientid_edit=new QLineEdit(this); clientid_label->move(6,490); clientid_label->setText("clientId:"); clientid_label->setFont(ft); clientid_label->setMaximumSize(100,35); clientid_label->setMinimumSize(100,35); clientid_label->setHidden(1); clientid_edit->move(110,490); clientid_edit->setFont(ft); clientid_edit->setText("bliss&a1aDaWbRIge|timestamp=123456,securemode=3,signmethod=hmacsha256,lan=C|"); clientid_edit->setMaximumSize(600,35); clientid_edit->setMinimumSize(600,35); clientid_edit->setHidden(1); connect(btnConnect,SIGNAL(clicked()),this,SLOT(on_btnConnect_clicked())); connect(btnPublish,SIGNAL(clicked()),this,SLOT(on_btnPublish_clicked())); connect(btnSubscribe,SIGNAL(clicked()),this,SLOT(on_btnSubcribe_clicked())); connect(clean,SIGNAL(clicked()),this,SLOT(on_btnClean_clicked())); connect(m_client,SIGNAL(messageReceived(const QByteArray , const QMqttTopicName)), this,SLOT(on_recived_msg( const QByteArray , const QMqttTopicName))); } QMqttClientTool::~QMqttClientTool() { this->m_client=NULL; } void QMqttClientTool::on_btnConnect_clicked() { //未连接服务器则连接 if (m_client->state() == QMqttClient::Disconnected) { if((qleHostName->text()==NULL) || (qleHostPort->text()==NULL)) return; m_client->setHostname(qleHostName->text()); m_client->setPort(qleHostPort->text().toInt()); m_client->setUsername(username_edit->text()); m_client->setPassword(password_edit->text()); m_client->setClientId(clientid_edit->text()); m_client->connectToHost(); usleep(1000*10); if(m_client->state() != QMqttClient::Disconnected){ btnConnect->setText(tr("Disconnect")); qleHostName->setEnabled(false); qleHostPort->setEnabled(false); } } else {//断开连接 btnConnect->setText(tr("Connect")); qleHostName->setEnabled(true); qleHostPort->setEnabled(true); m_client->disconnectFromHost(); } } void QMqttClientTool::on_btnPublish_clicked() { if(m_client->state()== QMqttClient::Disconnected) { QMessageBox::critical(this, QLatin1String("Error"), QLatin1String("mqtt is disconnect")); return; } int res=m_client->publish(qlePublishTopic->text(),qlePublishMessage->text().toUtf8(),0,false); qDebug()<<qlePublishMessage->text().toUtf8(); qDebug()<<qlePublishTopic->text(); qDebug()<<res; //QMessageBox::critical(this, QLatin1String("Error"), QLatin1String("publish message Error")); } void QMqttClientTool::on_btnSubcribe_clicked() { if(m_client->state()== QMqttClient::Disconnected){ QMessageBox::critical(this, QLatin1String("Error"), QLatin1String("mqtt is disconnect")); return; } m_client->subscribe(qleSubscribeTopic->text(),0); const QString content ="["+QDateTime::currentDateTime().toString("hh:mm:ss.zzz")+"]" +"Subcribe:" + QLatin1String("Topic:") + qleSubscribeTopic->text()+ QLatin1Char('\n'); //this->brower->insertPlainText(content); qDebug()<<qleSubscribeTopic->text(); } void QMqttClientTool::on_recived_msg( const QByteArray &message, const QMqttTopicName &topic) { if(message[2]=='0') { const QString content ="["+QDateTime::currentDateTime().toString("hh:mm:ss.zzz")+"]" + QLatin1String(":")+ "未佩戴口罩" + QLatin1Char('\n'); this->brower->insertPlainText(content); } else { const QString content ="["+QDateTime::currentDateTime().toString("hh:mm:ss.zzz")+"]" + QLatin1String(":")+ "已佩戴口罩" + QLatin1Char('\n'); this->brower->insertPlainText(content); } } //清理bower数据 void QMqttClientTool::on_btnClean_clicked() { this->brower->clear(); }
以上代码在修改一部分配置信息后基本上都是可以直接运行测试的
运行流程
1.运行emq
1.1 在emq安装路径下bin文件中打开cmd:"dos"
1.2 运行 emq console
2.在mask目录下运行 sudo ./run.sh
3.运行python代码
3.1 sudo gedit test.py打开py文件
3.2 修改192.168.31.149 为 服务器地址 保存退出
3.2 运行 python3.7 test.py
4.运行桌面程序,依次
4.1 点击Connect 连接服务器
4.2 点击Subscribe 订阅消息
0.0点关注不迷路QAQ