从零部署:基于OpenMV与Edge Impulse的轻量化口罩检测系统实战
1. 项目背景与硬件准备
最近两年,基于嵌入式设备的轻量化AI应用越来越火。作为一个玩了多年OpenMV的老玩家,我发现用OpenMV配合Edge Impulse平台做口罩检测特别适合新手入门。这个组合最大的优势就是不需要深厚的机器学习基础,从数据采集到模型部署都能在一个下午搞定。
先说说硬件选择。OpenMV Cam H7 Plus是我最推荐的型号,它搭载的STM32H743II芯片有480MHz主频,足够跑轻量级神经网络。摄像头部分支持30万像素,对于口罩检测这种任务完全够用。我实测过,在室内光线充足的环境下,1米距离内的人脸识别准确率能达到95%以上。
配件方面建议准备:
- 1个OpenMV开发板(建议官方版本)
- 1条Micro USB数据线(传输数据用)
- 1个三脚架(固定拍摄角度)
- 若干张不同颜色背景纸(用于数据采集)
第一次使用OpenMV需要先刷固件。打开OpenMV IDE后,连接设备,点击"工具"-"运行引导程序",选择最新固件版本。这个过程大概需要2分钟,完成后设备会自动重启。有个小技巧:如果遇到驱动问题,可以尝试换USB接口或者重新插拔设备。
2. 数据采集实战技巧
数据质量直接决定模型效果。我建议采集至少200张图片,mask和no_mask两类各占一半。实际操作中发现几个关键点:
光线控制:最好在自然光下采集,避免强光直射造成过曝。我习惯在窗边架设设备,上午10点左右的散射光效果最佳。
角度多样性:不仅要拍正面,还要采集15°、30°侧脸。可以让人坐着转椅子,每转15°拍一张。记住要包括戴不同款式口罩的情况(医用外科、N95、布口罩等)。
背景复杂度:简单背景(如白墙)和复杂背景(如书架)都要有。我在数据集中加入了约20%的复杂背景样本,这对提升模型鲁棒性很有帮助。
具体操作步骤:
# OpenMV数据采集示例代码 import sensor, image, time sensor.reset() sensor.set_pixformat(sensor.RGB565) sensor.set_framesize(sensor.QVGA) sensor.skip_frames(time = 2000) img_count = 0 while(True): img = sensor.snapshot() # 按字母's'保存图片 if (img.find_keypoints()): img.save("mask_%d.jpg" % img_count) img_count += 1 print("Saved image:", img_count)这个脚本会检测到人脸后自动保存图片。建议每类采集100-150张后,用20%做测试集。数据命名要规范,比如"mask_001.jpg"、"no_mask_045.jpg"这种格式,后期处理更方便。
3. Edge Impulse平台使用详解
Edge Impulse的网页端操作有几个容易踩坑的地方需要特别注意:
项目创建阶段:
- 选择"Images"项目类型
- 输入分辨率要匹配OpenMV的设置(通常320x240)
- 勾选"Object detection"而不是"Classification"(实测效果更好)
数据上传技巧:
- 不要用API上传,网页直传更稳定
- 批量上传时建议每次不超过50张
- 标签命名用下划线代替空格(如"with_mask")
特征提取设置:
- 图像尺寸保持原始分辨率
- 色彩空间选RGB(不要用灰度)
- 旋转增强建议开10°范围
- 缩放增强建议0.9-1.1倍
这是我调整多次后找到的黄金参数组合:
{ "dsp": { "imageWidth": 320, "imageHeight": 240, "resizeMode": "squash", "colorDepth": 3 }, "augmentation": { "rotation": 10, "zoom": 0.2 } }4. 模型训练与优化
迁移学习模型选择有讲究。经过对比测试,MobileNetV2 96x96 0.35x版最适合OpenMV:
- 推理速度:58ms/帧
- Flash占用:450KB
- RAM占用:120KB
训练参数设置建议:
- 学习率:0.0005
- 训练周期:30
- 批量大小:16
- 数据增强:全部开启
训练完成后要重点看三个指标:
- 混淆矩阵:检查是否有类别偏斜
- F1-score:综合准确率要>0.9
- ROC曲线:AUC值应接近1
如果发现过拟合(训练集准确率高但测试集低),可以:
- 增加数据量
- 调低学习率
- 添加Dropout层
- 减少训练周期
5. 模型部署与性能调优
模型导出时要注意几个关键选项:
- 量化方式:选择int8(精度损失约2%,速度提升3倍)
- 优化级别:选O3
- 内存布局:选择默认
部署到OpenMV后,这个优化版的推理脚本效果更好:
import sensor, image, tf, time sensor.reset() sensor.set_pixformat(sensor.RGB565) sensor.set_framesize(sensor.QVGA) sensor.skip_frames(time=2000) net = tf.load("mask_model.tflite") labels = ['mask', 'no_mask'] clock = time.clock() while(True): clock.tick() img = sensor.snapshot() # 使用ROI提升效率 for r in img.find_features(net, threshold=0.7): img.draw_rectangle(r) predictions = tf.classify(net, img, roi=r) # 输出置信度最高的结果 max_idx = predictions[0].index(max(predictions[0])) img.draw_string(r[0], r[1]-20, labels[max_idx], color=(255,0,0), scale=2) print("FPS:", clock.fps())实测这个版本在OpenMV H7上能跑到17FPS,内存占用稳定在85KB以下。如果遇到帧率下降,可以尝试:
- 减小ROI区域
- 降低分类阈值
- 关闭调试输出
6. 实际应用中的问题排查
在商场部署时遇到过几个典型问题:
光线干扰:
- 现象:傍晚时段误报率升高
- 解决方案:添加自动曝光补偿
sensor.set_auto_exposure(True, exposure_us=10000) # 10ms基准多人场景:
- 现象:画面超过3人时漏检
- 改进:改用滑动窗口检测
for x in range(0, img.width(), 32): for y in range(0, img.height(), 32): roi = (x, y, 64, 64) predictions = tf.classify(net, img, roi=roi)距离变化:
- 现象:1.5米外检测失效
- 对策:动态调整输入分辨率
def adjust_resolution(dist): if dist > 1.2: sensor.set_framesize(sensor.QQVGA) else: sensor.set_framesize(sensor.QVGA)7. 进阶优化方向
想让系统更实用可以尝试这些方法:
模型蒸馏:
- 用大模型生成伪标签
- 训练更小的专用模型
- 实测能将模型体积缩小40%
多任务学习:
- 同时检测口罩和体温
- 共享底层特征提取层
- 需要修改网络输出层
边缘-云端协同:
graph LR A[OpenMV] -->|低置信度样本| B(云端模型) B --> C[最终结果]这种架构能兼顾实时性和准确性,不过需要网络支持。
最后分享一个调试技巧:用OpenMV IDE的直方图工具实时观察图像特征分布,如果发现mask和no_mask的色度值重叠严重,就需要重新采集数据。我一般会准备一个色卡放在拍摄区域旁边作为参考。
