基于树莓派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 订阅消息

posted @ 2021-06-22 10:25  HuDaXia  阅读(1515)  评论(1编辑  收藏  举报