☰
YOLOV5口罩检测实战:从数据集标注到树莓派RK3568部署全流程
2026/10/10 13:45:33 网站建设 项目流程

简介:这份资源是面向计算机相关专业学生与项目实战学习者的YOLOV5口罩佩戴检测完整方案,可直接用于毕业设计、课程设计或期末大作业。内容涵盖数据集、项目源码、训练好的模型权重以及标注好的数据,帮助读者跳过环境搭建与数据准备的繁琐环节,快速跑通检测流程并理解目标检测项目的整体结构。压缩包共149个文件,约139.76MB,以yaml配置文件、Python源码、编译缓存、jpg与jpeg样本图片、pt模型权重、sh脚本及md说明文档为主,兼顾训练、推理与部署所需材料,目录组织清晰,便于按模块查阅与二次修改。目前已有238人学习下载,适合需要完整毕设方案、想复现口罩检测效果或进行课程实践的学习者参考,也能为后续更换数据集、调整模型结构提供可复用的工程基础。

1. 从一份口罩检测数据集说起:YOLOV5 落地到底卡在哪

很多人第一次拿到「基于 YOLOV5 口罩佩戴检测数据集系统+代码+训练好的模型+标注好的数据.zip」这类资源包时,第一反应是解压、装环境、跑detect.py,然后发现要么权重加载报错,要么检测框全是乱的,要么自己拍几张照片进去一个口罩都框不出来。问题不在 YOLOV5 本身,而在于大多数人把「数据集 + 代码 + 权重」当成一个开箱即用的黑匣子,忽略了这三者之间必须对齐的类别定义、标注格式和输入尺寸。

口罩佩戴检测这个任务,表面上是二分类(戴口罩 / 不戴口罩),实际落地时会遇到三类真实需求:一是公共场所出入口的合规提醒,二是工地、车间等特定场景的安全帽+口罩联合检测,三是视频流里的实时统计。不同需求对应的数据集标注粒度完全不同——有的只标「face_mask」和「no_face_mask」,有的还会细分「mask_weared_incorrect」(口罩拉到下巴)。如果你拿到的标注数据里只有两个类,却想检测「佩戴不规范」,那训练再久也没用。

这篇文章面向的是手里已经有这份资源包、或者准备自己从零构建一套口罩检测系统的工程师。我会把 YOLOV5 训练自己的数据集这条链路拆开:先讲清楚数据集里那些.xml、.txt、data.yaml到底在说什么,再给出从标注校验到训练、推理、量化部署的完整命令和参数,最后把我在树莓派 4B 和 RK3568 上部署时踩过的坑一条条列出来。读完你至少能判断:这份资源包值不值得投入时间,以及如果要改成自己的场景,该动哪几个文件。

2. 拆开口罩检测数据集:标注格式、类别定义与校验脚本

2.1 VOC XML 与 YOLO TXT 的转换逻辑

大多数公开口罩数据集原始标注是 PASCAL VOC 格式,每张图对应一个.xml,里面记录了xmin, ymin, xmax, ymax和类别名。YOLOV5 训练时只认归一化后的class_id x_center y_center width height,所以第一步永远是格式转换。转换的核心不是写代码,而是理解归一化基准:x_center = (xmin + xmax) / 2 / img_width,width = (xmax - xmin) / img_width。这里最容易翻车的是图片实际尺寸和 XML 里<size>字段不一致——有些数据集经过二次裁剪但 XML 没更新,转换出来的框会整体偏移。

下面这个脚本我用了很多次,加了尺寸校验和越界裁剪:

import os import xml.etree.ElementTree as ET from PIL import Image # 类别映射,必须和 data.yaml 里的 names 顺序完全一致 CLASS_MAP = {"face_mask": 0, "no_face_mask": 1, "mask_weared_incorrect": 2} def convert_voc_to_yolo(xml_dir, img_dir, out_dir): os.makedirs(out_dir, exist_ok=True) for xml_file in os.listdir(xml_dir): if not xml_file.endswith(".xml"): continue tree = ET.parse(os.path.join(xml_dir, xml_file)) root = tree.getroot() img_name = root.find("filename").text img_path = os.path.join(img_dir, img_name) # 用真实图片尺寸,不信任 XML 里的 size with Image.open(img_path) as im: w, h = im.size lines = [] for obj in root.findall("object"): cls_name = obj.find("name").text.strip() if cls_name not in CLASS_MAP: continue bbox = obj.find("bndbox") xmin = float(bbox.find("xmin").text) ymin = float(bbox.find("ymin").text) xmax = float(bbox.find("xmax").text) ymax = float(bbox.find("ymax").text) # 越界裁剪,防止归一化后出现负值或大于1 xmin, xmax = max(0, xmin), min(w, xmax) ymin, ymax = max(0, ymin), min(h, ymax) if xmax <= xmin or ymax <= ymin: continue xc = (xmin + xmax) / 2 / w yc = (ymin + ymax) / 2 / h bw = (xmax - xmin) / w bh = (ymax - ymin) / h lines.append(f"{CLASS_MAP[cls_name]} {xc:.6f} {yc:.6f} {bw:.6f} {bh:.6f}") with open(os.path.join(out_dir, xml_file.replace(".xml", ".txt")), "w") as f: f.write("\n".join(lines))

逻辑说明:CLASS_MAP的键必须和 XML 里的<name>文本完全匹配,大小写敏感。参数上,xc/yc/bw/bh保留 6 位小数足够,YOLOV5 内部会再处理。如果转换后某个类别样本数为 0,训练时该类的 loss 会一直是 0,最终模型对该类完全无响应——这是最常见的「训练成功但检测不到」原因。

2.2 data.yaml 的四个必填字段与路径陷阱

YOLOV5 的data.yaml看起来简单,但路径写错是新手第一道坎。标准结构如下:

path: ../datasets/mask # 数据集根目录 train: images/train # 相对 path 的训练图片目录 val: images/val # 相对 path 的验证图片目录 nc: 3 # 类别数 names: ['face_mask', 'no_face_mask', 'mask_weared_incorrect']

注意train和val写的是图片目录,YOLOV5 会自动把images替换成labels去找同名.txt。如果你的目录结构是train/images和train/labels,那train应该写成train/images。我见过有人把path写成绝对路径但用了~,在 Docker 里~不展开,直接报FileNotFoundError。另外nc必须等于names长度,多一个少一个都会在训练启动时抛断言错误。

2.3 用一条命令统计类别分布和标注质量

在训练前,我习惯先跑一遍统计,确认没有空标注文件、没有类别严重失衡:

# 统计每个类别的框数量 for f in labels/train/*.txt; do cat "$f"; done | awk '{print $1}' | sort | uniq -c # 找出空标注文件(图片里没有目标) find labels/train -name "*.txt" -empty | head -20 # 检查是否有坐标大于1的异常值 awk '$2>1 || $3>1 || $4>1 || $5>1 {print FILENAME}' labels/train/*.txt | head

如果no_face_mask的框数量只有face_mask的十分之一,训练时模型会倾向于全部预测为face_mask。常见做法是用--weights yolov5s.pt做迁移学习,并在data.yaml里加cls_weights或者用过采样补齐少数类。空标注文件要删掉,否则 YOLOV5 会把它当作背景图参与训练,少量可以,大量会拉低召回。

3. 训练自己的口罩数据集:从 yolov5s 到超参数调优

3.1 环境准备与最小训练命令

假设你已经 clone 了 YOLOV5 官方仓库(版本 v6.0 以上),依赖安装用pip install -r requirements.txt。训练口罩检测我一般从yolov5s.pt开始,因为它在精度和速度之间平衡最好,树莓派 4B 也能跑到 5 FPS 左右。最小命令:

python train.py \ --data data/mask.yaml \ --weights yolov5s.pt \ --img 640 \ --batch 16 \ --epochs 100 \ --device 0 \ --project runs/train \ --name mask_exp1

参数说明:--img 640是输入分辨率,口罩目标通常占画面比例不大,640 够用;如果图片里人脸很小,可以提到 1280,但显存翻倍。--batch 16在 8G 显存上跑 640 分辨率刚好,显存不够就降到 8 并加--accumulate 2模拟大 batch。--device 0指定第一块 GPU,CPU 训练会慢到无法接受。--epochs 100对口罩这种简单任务通常 50 轮就收敛,100 轮是保险值。

3.2 三个必调超参数:lr0、lrf、anchor

YOLOV5 默认超参数在data/hyp.scratch.yaml里,口罩检测我只会动三个:

参数默认值建议值作用
lr00.010.005初始学习率,小数据集降低防震荡
lrf0.010.05最终学习率系数,太小后期学不动
anchor_t4.03.5anchor 匹配阈值,小目标多时降低

lr0从 0.01 降到 0.005 是因为口罩数据集通常只有几千张,大学习率容易在前期把预训练权重带偏。lrf是余弦退火的终点系数,默认 0.01 意味着最终 lr 是 0.0001,对 100 轮来说后期几乎不更新,调到 0.05 让模型在最后 20 轮还能微调。anchor_t控制正样本匹配的宽高比容忍度,口罩的宽高比集中在 1:1 到 1:1.5,默认 4.0 会引入太多低质量正样本,3.5 更稳。

3.3 训练过程看什么:loss 曲线与 mAP 的解读

启动训练后,终端会打印box_loss、obj_loss、cls_loss和mAP@0.5。口罩检测正常收敛的标志是:前 10 轮box_loss快速下降,cls_loss在 20 轮左右降到 0.05 以下,mAP@0.5在 50 轮达到 0.9 以上。如果obj_loss一直不降,说明 anchor 和你的目标尺寸不匹配,需要重新聚类 anchor:

python utils/autanchor.py --data data/mask.yaml --img 640 --thr 4.0 --n 9

把输出的 9 个 anchor 替换到模型配置文件里。如果cls_loss震荡严重,检查data.yaml的names顺序是否和标注转换时的CLASS_MAP一致——顺序错位会导致模型学到的类别完全混乱,这是血泪教训里最常见的一条。

4. 推理、验证与量化:让模型在树莓派和 RK3568 上跑起来

4.1 用 detect.py 做批量验证与置信度阈值选择

训练完先别急着部署,用detect.py在验证集上跑一遍,确认没有系统性错误:

python detect.py \ --weights runs/train/mask_exp1/weights/best.pt \ --source datasets/mask/images/val \ --img 640 \ --conf 0.25 \ --iou 0.45 \ --save-txt \ --project runs/detect

--conf 0.25是置信度阈值,口罩检测建议从 0.25 开始,漏检多就降到 0.15,误检多就提到 0.4。--iou 0.45是 NMS 的 IoU 阈值,人群密集场景可以降到 0.4 减少框重叠。--save-txt会把检测结果存成 YOLO 格式,方便和真值对比算 mAP。如果发现「不戴口罩」被大量误检成「戴口罩」,优先检查训练集里这两类的样本是否均衡,而不是调阈值。

4.2 导出 ONNX 与 RKNN 量化:精度掉了怎么排查

树莓派 4B 上直接跑 PyTorch 权重只有 2-3 FPS,通常导出 ONNX 再用 ONNXRuntime 能到 8 FPS。RK3568 则需要转成 RKNN 格式走 NPU。导出 ONNX:

python export.py \ --weights runs/train/mask_exp1/weights/best.pt \ --include onnx \ --img 640 \ --batch 1 \ --opset 12

--opset 12兼容性最好,--batch 1是边缘设备推理的常规设置。转 RKNN 时,量化校准集要选 200 张以上覆盖各种光照的图片,否则量化后 mAP 可能掉 10 个点。如果量化后「不戴口罩」类别几乎失效,原因是该类样本在校准集里太少,量化 scale 被「戴口罩」主导。解决办法是把校准集按类别比例采样,或者对少数类做额外增强后再量化。

4.3 树莓派 4B 部署的实测帧率与散热注意

树莓派 4B 4G 版本跑 ONNX 640 输入,实测 7-9 FPS,CPU 温度 10 分钟内到 75 度,不加散热片会降频到 5 FPS 以下。建议加装风扇并把--img降到 416,帧率能到 12 FPS,mAP 只掉 2 个点左右。如果要用 USB 摄像头实时检测,用 OpenCV 的VideoCapture(0)时把CAP_PROP_BUFFERSIZE设为 1,否则延迟会累积到 2 秒以上。

5. 避坑与排查:口罩检测训练部署的 5 个真实翻车记录

现象:训练 loss 正常下降,但推理时所有框都偏向图片左上角。原因:标注转换时用了 XML 里的<size>字段而不是真实图片尺寸,而<size>是错的。解决:用 PIL 重新读取图片尺寸,重跑转换脚本,并抽查 10 张图的.txt可视化确认。

现象:mAP@0.5 到 0.85 就上不去,验证集预测框大量重叠。原因:NMS 的 IoU 阈值设太高(默认 0.45 对密集人脸偏大),或者 anchor 尺寸和实际目标不匹配。解决:先用autoanchor重新聚类,再把--iou降到 0.4,密集场景可到 0.35。

现象:RKNN 量化后模型对「不戴口罩」完全无响应。原因:量化校准集里「不戴口罩」样本占比低于 5%,量化 scale 被主导类挤压。解决:按类别均匀采样校准集,每类至少 50 张,重新量化后逐类验证。

现象:树莓派上跑 detect.py 报Illegal instruction。原因:PyTorch 轮子是在支持 AVX 的机器上编译的,树莓派 ARM 架构不支持。解决:改用 ONNXRuntime 或安装 ARM 专用轮子,不要直接 pip install torch。

现象:自己拍的照片检测效果极差,但验证集 mAP 很高。原因:训练集和实际场景存在域偏移,比如训练集都是室内白光,实际是室外逆光。解决:用实际场景图片做 50 张标注,加入训练集做 10 轮微调,学习率设 0.001,效果提升明显。

6. 把口罩检测做成可复用的验证流程:一个技巧和我的习惯

最后一章说一个我反复用的技巧:不要只信 mAP,要建立「分场景验证集」。具体做法是把验证集按光照(室内/室外)、遮挡程度(无遮挡/半遮挡/重度遮挡)、口罩类型(医用/N95/布口罩)分成 6 个子集,每个子集至少 50 张,训练完分别跑detect.py统计各类的召回和误检。这样你能清楚知道模型在哪个场景下会翻车,而不是被一个总体 mAP 蒙蔽。

# 分场景验证脚本示例 for scene in indoor outdoor occlusion_heavy; do python detect.py \ --weights runs/train/mask_exp1/weights/best.pt \ --source datasets/mask/val_$scene \ --conf 0.25 \ --save-txt \ --project runs/val_scene \ --name $scene echo "=== $scene 完成 ===" done

跑完后对比每个子集的precision和recall。我的经验是:室外逆光场景的召回通常比室内低 15-20 个点,重度遮挡场景的误检率会翻倍。针对这两个短板,分别补充 100 张对应场景的训练图,再做 20 轮微调,比盲目加数据有效得多。

另一个习惯是每次训练完把best.pt、data.yaml、hyp.yaml和results.csv一起打包存档,命名带上日期和场景标签。我吃过亏——三个月后想复现某个版本,发现超参数忘了改了什么,只能从头再训。现在我的存档目录里每个实验一个文件夹,里面放一个README.md记录改了哪几个参数、为什么改、最终 mAP 是多少。这个习惯看起来笨,但省下的时间远超写 README 的几分钟。

口罩检测这个方向本身不复杂,难的是数据质量和场景覆盖。如果你手里那份资源包的标注质量过关、类别定义符合你的需求,直接拿来微调是最快的路径;如果标注混乱或者类别不对,花两天重新标 500 张自己的数据,比在脏数据上调参一周更划算。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询