佩戴口罩是阻断呼吸道病毒传播的方式之一,但难免会有些人不会自觉佩戴口罩。当人与人最舒适的距离是“你离我远点”的情况下,AI正以一种温暖的方式靠近人们,带来特殊的安全感。如今,在AI的加持下,口罩作为外出必备的保护伞的同时,也不再是“刷脸”通行的障碍物,能让你在公司、校园、小区等场景中畅通无阻……
前段时间在Paddle Lite的QQ群里面看到小伙伴们对在树莓派上部署实时的口罩识别很感兴趣,想着目前这个时期,能用低成本部署口罩识别系统对很多场景的帮助还是很高的。可以应用在学校、公司等等,节约了人力,也能督促人们出门一定要带好口罩。正好手里有个树莓派4B,可以搭建一个简易版本的口罩识别系统,下面将我的实验过程一步步与大家分享。
PyramidBox-Lite是基于2018年百度发表于计算机视觉顶级会议ECCV 2018的论文PyramidBox而研发的轻量级模型,模型基于主干网络FaceBoxes,对于光照、口罩遮挡、表情变化、尺度变化等常见问题具有很强的鲁棒性。该PaddleHub Module是针对于移动端优化过的模型,适合部署于移动端或者边缘检测等算力受限的设备上,并基于WIDER FACE数据集和百度自采人脸数据集进行训练,支持预测。而针对此次疫情,百度宣布免费开源业内首个口罩人脸检测及分类模型。该模型可以有效检测在密集人流区域中携带和未携戴口罩的所有人脸,同时判断该人员是否佩戴口罩。目前已通过飞桨PaddleHub开源出来,广大开发者用几行代码即可快速上手,免费调用。
关于pyramidbox_lite_mobile_mask模型链接:
https://www.paddlepaddle.org.cn/hubdetail?name=pyramidbox_lite_mobile_mask&en_category=ObjectDetection
通过python执行以下代码,即可通过PaddleHub获取并保存pyramidbox_lite_mobile_mask口罩识别模型。
import paddlehub as hub
pyramidbox_lite_mobile_mask = hub.Module(name="pyramidbox_lite_mobile_mask")
# 将模型保存在test_program文件夹之中
pyramidbox_lite_mobile_mask.processor.save_inference_model(dirname="test_program")
预测库编译
ArmLinux目前官网未提供编译好的预测库,所以需要自行编译。这里使用的预测库是Paddle-Lite最新版本v2.3.0:
wget https://github.com/PaddlePaddle/Paddle-Lite/archive/v2.3.0.zip
ArmLinux编译预测库有两种方式,交叉编译和本地编译(直接在树莓派上编译)
交叉编译速度更快,本地编译更加方便,两种方式任选其一即可。详细的编译方法可参考Lite的官方文档:
https://paddle-lite.readthedocs.io/zh/latest/user_guides/source_compile.html
模型转换
使用opt工具将模型转为Paddle-Lite的模型
./opt
--model_file=./__model__
--param_file=./__params__
--optimize_out_type=naive_buffer
--optimize_out=model
Paddle Lite预测接口说明:
C++代码调用Paddle-Lite执行预测库仅需以下五步:
(1)引用头文件和命名空间
#include "paddle_api.h"
using namespace paddle::lite_api;
(2)指定模型文件,创建Predictor
MobileConfig config;
config.set_model_from_file(model_file_path);
std::shared_ptr predictor =
CreatePaddlePredictor(config);
(3)设置模型输入 (下面以全一输入为例)
std::unique_ptr input_tensor(std::move(predictor->GetInput(0)));
input_tensor->Resize({1, 3, 224, 224});
auto* data = input_tensor->mutable_data();
for (int i = 0; i < ShapeProduction(input_tensor->shape()); ++i) {
data[i] = 1;
}
(4)执行预测
predictor->Run();
(5)获得预测结果
std::unique_ptr output_tensor(
std::move(predictor->GetOutput(0)));
auto output_data=output_tensor->data();
1.硬件环境
首先是硬件准备,我们需要一块嵌入式开发板以及摄像头。这里我使用的是树莓派4B和500W像素的CSI摄像头。
图1 树莓派4B 图2 500W CSI摄像头
2.软件环境
操作系统我选用的是Debian-Pi-Aarch64,注意这是一个64位的系统(官方的Raspberry Pi系统是32位的)。
附上链接:
https://gitee.com/openfans-community/Debian-Pi-Aarch64/
进入系统后首先我们给摄像头enable一下,这个系统开启摄像头的方式与Raspberry Pi是不一样的。
打开终端sudo gedit /boot/config.txt文件中将start_x=1的注释去掉后重启即可开启CSI摄像头。
接下来安装一下必要的软件:
sudo apt-get update
sudo apt-get install gcc g++ make wget unzip libopencv-dev pkg-config
wget https://www.cmake.org/files/v3.10/cmake-3.10.3.tar.gz && tar -zxvf cmake-3.10.3.tar.gz
cd cmake-3.10.3
./configure && make -j4 && sudo make install
如图3所示,软件系统的整体流程是先获取视频流,然后转成图像,基于图像,做人脸检测,如果检测到人脸,再基于口罩识别模型判断人脸是否佩戴口罩;根据识别结果,画框并显示口罩是否佩戴。核心的思想是先进行人脸检测,再去分类是否佩戴口罩。
图3 软件流程图
1.捕捉视频流
首先准备要传入的视频数据,这里我们需要用到OpenCV。
创建一个VideoCapture对象。
cv:: VideoCapture cap(-1);
设置一下分辨率,为了保证性能可以不要设置太高。
cap.set(CV_CAP_PROP_FRAME_WIDTH, 640);
cap.set(CV_CAP_PROP_FRAME_HEIGHT, 480);
这里使用了RunTime()函数,将检测模型,分类模型和cap作为参数传入。
RunTime(detect_model_dir, classify_model_dir,cap);
2. 将输入的视频流取帧
cv::Mat img;
cap >> img;
3.人脸检测
使用Lite API,基于人脸模型进行人脸检测,生成人脸检测框。
MobileConfig config;
config.set_model_dir(det_model_dir);
std::shared_ptr predictor =
CreatePaddlePredictor(config);
std::unique_ptr input_tensor0(std::move(predictor->GetInput(0)));
input_tensor0->Resize({1, 3, s_height, s_width});
auto* data = input_tensor0->mutable_data();
predictor->Run();
std::unique_ptr output_tensor0(std::move(predictor->GetOutput(0)));
auto* outptr = output_tensor0->data();
auto shape_out = output_tensor0->shape();
int64_t out_len = ShapeProduction(shape_out);
4.口罩分类
对上述过程生成的每个人脸检测框,进行预处理并执行口罩分类模型的预测,对检测框分类是否佩戴口罩。
config.set_model_dir(class_model_dir);
predictor = CreatePaddlePredictor(config);
std::unique_ptr input_tensor1(std::move(predictor->GetInput(0)));
input_tensor1->Resize({1, 3, classify_h, classify_w});
auto* input_data = input_tensor1->mutable_data();
5. 结果展示
使用cv::Scala框选结果,红色的框选中人脸,黄色的label(wear mask、no mask)标记是否佩戴口罩
cv::rectangle(img, rec_clip, cv::Scalar(0, 0, 255), 2, cv::LINE_AA);
std::string text = outptr[1] > classify_threshold ? "wear mask" : "no mask";
int font_face = cv::FONT_HERSHEY_COMPLEX_SMALL;
double font_scale = 1.f;
int thickness = 1;
cv::Size text_size =cv::getTextSize(text, font_face, font_scale, thickness, nullptr);
float new_font_scale = rec_clip.width * 0.7 * font_scale / text_size.width;
text_size =cv::getTextSize(text, font_face, new_font_scale, thickness, nullptr);
cv::Point origin;
origin.x = rec_clip.x + 5;
origin.y = rec_clip.y + text_size.height + 5;
cv::putText(img,text,origin,font_face,new_font_scale,cv::Scalar(0, 255, 255),
thickness,cv::LINE_AA);
图4 未佩戴口罩
图5 佩戴口罩
图6 手遮挡口罩测试
最后,代码仅做部分演示,完整项目公开在AI Studio上,欢迎大家Fork一下 ~
https://aistudio.baidu.com/aistudio/projectdetail/315730
如果您加入官方QQ群,您将遇上大批志同道合的深度学习同学。官方QQ群:703252161。
如果您想详细了解更多飞桨的相关内容,请参阅以下文档。
官网地址:https://www.paddlepaddle.org.cn
飞桨开源框架项目地址:
GitHub: https://github.com/PaddlePaddle/Paddle
Gitee: https://gitee.com/paddlepaddle/Paddle
END