恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
YOLOV5口罩检测实战:从数据集标注到树莓派RK3568部署全流程
首页
资讯中心
/
YOLOV5口罩检测实战:从数据集标注到树莓派RK3568部署全流程
YOLOV5口罩检测实战:从数据集标注到树莓派RK3568部署全流程
发布时间:2026/10/10 13:45:52
简介这份资源是面向计算机相关专业学生与项目实战学习者的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_widthwidth (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_okTrue) 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 $21 || $31 || $41 || $51 {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指定第一块 GPUCPU 训练会慢到无法接受。--epochs 100对口罩这种简单任务通常 50 轮就收敛100 轮是保险值。3.2 三个必调超参数lr0、lrf、anchorYOLOV5 默认超参数在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和mAP0.5。口罩检测正常收敛的标志是前 10 轮box_loss快速下降cls_loss在 20 轮左右降到 0.05 以下mAP0.5在 50 轮达到 0.9 以上。如果obj_loss一直不降说明 anchor 和你的目标尺寸不匹配需要重新聚类 anchorpython 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。导出 ONNXpython 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 FPSCPU 温度 10 分钟内到 75 度不加散热片会降频到 5 FPS 以下。建议加装风扇并把--img降到 416帧率能到 12 FPSmAP 只掉 2 个点左右。如果要用 USB 摄像头实时检测用 OpenCV 的VideoCapture(0)时把CAP_PROP_BUFFERSIZE设为 1否则延迟会累积到 2 秒以上。5. 避坑与排查口罩检测训练部署的 5 个真实翻车记录现象训练 loss 正常下降但推理时所有框都偏向图片左上角。原因标注转换时用了 XML 里的size字段而不是真实图片尺寸而size是错的。解决用 PIL 重新读取图片尺寸重跑转换脚本并抽查 10 张图的.txt可视化确认。现象mAP0.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 张自己的数据比在脏数据上调参一周更划算。希望帮到你。本文还有配套的精品资源点击获取