简介:基于深度学习的口罩检测系统是一套面向深度学习初学者与高校学生的完整目标检测实践项目,适合作为毕业设计、期末大作业或课程设计的参考方案。系统采用YOLO算法,能够对静态图像或视频流中的人员口罩佩戴情况进行快速识别,并给出检测结果。压缩包共45个文件,大小约2.64MB,以15个脚本文件、12个文本文件、6个编译文件以及图片和配置资源为主,包含YOLO系列模型配置、训练、格式转换、锚点聚类、批量检测等工具脚本。资源附带数据集标注列表、字体文件和说明文档,便于使用者快速理解目录结构并复现实验。已有41人浏览学习。对于需要快速搭建口罩识别系统的学习者,这套资料提供了直接运行的代码框架和配套文件,既能减少重复搭建工作,也能深入理解YOLO在实际场景中的部署与应用。
1. 基于深度学习的口罩检测系统:先摸清这个 zip 里的完整链路再动手
拿到“基于深度学习的口罩检测系统.zip”这种压缩包,不管是毕设课题、课程设计,还是从网上找的实战项目案例,我见过太多人第一件事就是找 train.py 然后盲目运行,跑出一条 Loss 曲线就以为交了差。口罩检测系统在深度学习 CV 方向里是一个非常典型的目标检测工程,完整链路是数据准备、模型选型、训练调参、推理部署,任何一个环节偷懒,最终效果都会在真实场景的测试图片上现形。我按自己跑通过的路径,把选型理由、训练命令、参数依据和最容易翻车的地方讲清楚,新手照步骤能跑通,熟手可以直接抄参数。如果你正打算把口罩检测做成毕设或用在门禁考勤上,这条路值得完整走一遍。
2. 选型与数据准备:YOLO 还是 SSD,公开数据集怎么筛才不翻车
2.1 模型路线选型:为什么 YOLO 成为深度学习目标检测的默认答案
口罩检测本质上是目标检测,输入图片或视频帧,输出每个人脸的位置和佩戴状态。选型时我先排除两类方案:Faster R-CNN 这类两阶段检测器精度上限高,但训练一轮在消费级显卡上要等很久,在线推理也慢;传统 Haar 级联加肤色检测,不戴口罩时还能靠人脸关键点判断,一旦人脸被口罩遮住,关键点直接丢失,整套方案失效。剩下值得认真比较的,基本就是 YOLO 系列、SSD 系列,还有 Halcon 这类工业视觉软件自带的深度学习目标检测模块。
YOLO 系列能成为深度学习实战项目案例里的默认答案,不是靠单一精度指标,而是工程闭环完整。以 YOLOv5 和 YOLOv8 为例,自带数据增强、锚框计算、损失函数、日志可视化、断点续训和导出脚本,从标注数据到 ONNX 权重一条命令走完。SSD 的优势是轻量,MobileNet-SSD 在 CPU 上也能跑到实时,但小目标检测偏弱。口罩在画面里的尺度非常关键:摄像头距离人 3 米以上时,640px 图像里的人脸可能只占 30px,SSD 在这种尺度下的漏检率明显比 YOLO 高。Halcon 的深度学习模块我也试过,训练界面友好,但模型导出和自定义后处理偏封闭,不适合需要自研逻辑的毕设项目。
如果你不是一定要跑在树莓派或 ARM 板子上,我的建议是第一条路线选 YOLOv5s 或 YOLOv8s。YOLOv5s 权重约 14MB,训练速度略快;YOLOv8s 换了 C2f 特征提取结构,遮挡情况下表现稍好。嵌入式场景再降档到 YOLOv5n 或 MobileNet-SSD,预训练好找,精度损失也还能接受。选型结论可以看这张表:
| 方案 | 精度 | 推理速度 | 训练成本 | 适合场景 |
|---|---|---|---|---|
| YOLOv5s / YOLOv8s | 高 | 快 | 低 | 服务器或 PC 实时检测 |
| MobileNet-SSD | 中 | 快(CPU 可跑) | 低 | 嵌入式或低算力设备 |
| Faster R-CNN | 最高 | 慢 | 高 | 离线高精度分析 |
| Halcon 深度学习模块 | 高 | 中 | 中 | 工业视觉封闭部署 |
2.2 公开数据集怎么筛:VOC 格式转 YOLO label 的清洗脚本
数据集是口罩检测系统里最容易被低估的一环。公开的口罩人脸数据集有 RMFD、MAFA、Kaggle 的 Face Mask Detection 等,很多人下载完直接训练,mAP 卡在 0.5 上下,第一反应是模型不行,实际上问题大多在数据。
我拿到数据集后先做三件事。第一,按类别抽 50 张图肉眼检查,确认标注框贴着人脸边缘,没有漏标半个脸。第二,统计每张图的框数,正常在 1 到 5 个,如果出现大量空标注图片,训练时会被当成背景负样本,干扰收敛。第三,统一类别定义,我只用 with_mask 和 without_mask 两类,不引入 mask_weared_incorrect 这个第三类,因为“戴歪”的标注标准在不同数据集里不一致,混着训练会让模型困惑。
公开数据集多数是 VOC 格式,也就是 XML 标注,而 YOLO 需要每张图对应一个 txt 文件,每行是“类别 中心x 中心y 宽 高”,全部归一化到 0 到 1。核心转换脚本如下:
import xml.etree.ElementTree as ET from pathlib import Path # 类别映射:只保留两类,其它类直接跳过 CLASS_MAP = {"with_mask": 0, "without_mask": 1} def convert_voc_annotation(xml_file, output_txt): tree = ET.parse(xml_file) root = tree.getroot() img_w = int(root.find("size/width").text) img_h = int(root.find("size/height").text) lines = [] for obj in root.iter("object"): name = obj.find("name").text if name not in CLASS_MAP: continue box = obj.find("bndbox") x_min = float(box.find("xmin").text) y_min = float(box.find("ymin").text) x_max = float(box.find("xmax").text) y_max = float(box.find("ymax").text) # 坐标转 YOLO 归一化格式,越界值裁剪到 0~1 x_center = ((x_min + x_max) / 2) / img_w y_center = ((y_min + y_max) / 2) / img_h width = (x_max - x_min) / img_w height = (y_max - y_min) / img_h x_center = min(max(x_center, 0.0), 1.0) y_center = min(max(y_center, 0.0), 1.0) width = min(max(width, 0.0), 1.0) height = min(max(height, 0.0), 1.0) lines.append(f"{CLASS_MAP[name]} {x_center:.6f} " f"{y_center:.6f} {width:.6f} {height:.6f}") with open(output_txt, "w") as f: f.write("\n".join(lines))这段脚本有两个细节直接影响训练效果。第一,坐标除以的是 size 节点里的原始图像宽高,不是读取 bndbox 后自己算的像素值,有的数据集图片含 EXIF 旋转信息,宽高和实际显示不一致,用错了整体坐标偏移。第二,归一化后的中心点和宽高必须裁剪到 0 到 1,否则训练时 anchor 匹配和 loss 计算都可能出现 NaN。
转换完成后要做一次验证:随机抽几张图,把 txt 里的框用 OpenCV 画回原图,人工确认没有坐标错位。然后按 9:1 把图片和 txt 划分到 train、val 目录,注意按文件名随机打散,不要按目录顺序切,否则同一场景的连拍帧会全进训练集,验证结果虚高。
2.3 数据增强参数:小样本下怎么让模型不靠玄学
口罩检测数据规模通常不大,几千张是常态,直接训练很容易过拟合。YOLO 内置的 mosaic、随机仿射、HSV 扰动对通用目标已经够用,但口罩检测有特殊性:口罩颜色纹理相对统一,人脸肤色和光照变化却很大。
我调整增强参数时重点看三个。第一,HSV 饱和度扰动范围开到 0.5 到 1.0,室内灯光偏黄、室外日光偏白,高饱和度扰动让模型不至于依赖某个固定色温。第二,随机仿射的旋转角度开到 15 度左右,超过这个值后标注框里的人脸语义会变得奇怪。第三,上下翻转不要开,左右翻转更不要开,文字、logo 在镜面翻转后会出现反字,引入本不该存在的特征。
如果筛选完可用数据不到 500 张,我会用复制粘贴增强:从训练图里裁出戴口罩的人脸,随机透明度融合到背景图上生成新样本。新增样本不要超过原始样本的 30%,否则模型会学到“口罩只出现在合成背景上”的假特征,真实场景直接漏检。
3. 从环境配置到训练落盘:PyTorch 版本匹配和核心超参数怎么定
3.1 深度学习环境配置:Miniconda 建环境与 CUDA 匹配的那点玄学
口罩检测的训练环境,常见做法是 PyTorch 全家桶。我自己用 Miniconda 管理,避免多个项目之间 torch 版本互相污染。深度学习环境配置的玄学在于 CUDA、cuDNN、PyTorch 三者必须匹配,不匹配时会看到 NaN Loss 或显存分配失败,表面像代码问题,实际是底层库冲突。
conda create -n maskdet python=3.10 -y conda activate maskdet pip install torch torchvision --index-url https://download.pytorch.org/whl/cu121第一条命令创建 python 3.10 的独立环境,第二条激活。第三条从 PyTorch 官方 wheel 源安装 CUDA 12.1 版构建的 torch 和 torchvision。这里有个高频坑:pip 默认安装的 torch 是 CPU 版,不会自动带上 CUDA 依赖,必须用 --index-url 指定 cuda 版 wheel 源。装完务必先跑下面这段检测代码,确认训练环境真的能用 GPU。
import torch print(torch.__version__) print(torch.cuda.is_available()) print(torch.cuda.get_device_name(0))如果第三行输出你的显卡型号,环境基本就位。如果 is_available() 返回 False,先查 nvidia-smi 的驱动版本和驱动支持的 CUDA 版本,不要急着重装。驱动版本和 CUDA Toolkit 版本是两个概念,驱动足够新时,cu121、cu118 都能跑,不需要严格对齐 nvidia-smi 里显示的版本。本地没有可用 GPU 时,租一台带 RTX 显卡的云服务器跑训练,比用 CPU 硬磨省下大半天时间。
3.2 训练命令与核心超参数:batch、lr、epoch 的合理起点
环境就位后,先准备数据配置 yaml,写明 train 和 val 路径、类别数、类别名。路径建议写绝对路径,换机器训练时相对路径问题会浪费一小时排查。一个可用的 mask.yaml 长这样:
# 口罩检测数据配置 path: /data/mask train: train/images val: val/images nc: 2 names: ['with_mask', 'without_mask']path 指数据集根目录,train 和 val 是相对根目录的图片文件夹路径,nc 是类别数,names 与转换脚本里的 CLASS_MAP 一一对应。然后以 YOLOv5 官方仓库的 train.py 为例,启动训练:
python train.py --img 640 --batch 16 --epochs 100 \ --data mask.yaml --weights yolov5s.pt --device 0这个命令里最值得调的是 img、batch、epochs。img 是输入分辨率,640 是省内存和保精度的平衡点;摄像头画面里人脸占比大就降到 480 提速,人脸普遍小就升到 768 提精度,但显存占用接近翻倍。batch 一般取 16 到 32,显存不够先减 batch,不要为了凑 batch 降分辨率。epochs 对口罩这种几千张的数据集,100 轮足够收敛,再多就开始过拟合。
学习率我没放在命令行,而是在 YOLOv5 的 hyp 配置 yaml 里调 lr0。默认 0.01 配 batch 16 比较稳,前 20 轮 Loss 震荡就降到 0.005;Loss 降得慢,先查类别不平衡,而不是急着拉大学习率。训练日志里每一轮的 mAP@0.5 都要记下来,这是我判断模型有没有翻车的核心依据,比盯 Loss 曲线直观。
3.3 断点续训与 early stopping:给训练过程留后悔药
训练时间一长,断电、显存溢出、误触终止都会打断进程。脚本默认每轮保存 last.pt,并按验证集 mAP 保存 best.pt。中断恢复用 last.pt,追求精度用 best.pt,这两个文件千万别搞反。续训命令如下:
python train.py --resume runs/train/exp/weights/last.pt--resume 会自动读原配置,包括学习率调度器的当前状态,不需要重新指定 epoch 和数据配置。续训后要盯学习率:如果从 0.01 直接接着 80 轮的状态继续,余弦退火已经把 lr 降得很低,Loss 不再下降是正常现象,不要手动调高 lr。早停参数 patience 默认 100,意思是验证指标连续 100 轮不提升就停。口罩数据集小,我会调成 50,省出来的时间够再跑一轮消融实验。
4. 口罩检测踩坑排查:5 个高频问题的现象、原因与解决
4.1 训练 Loss 前 20 轮出现 NaN
现象:训练日志里 Loss 突然变成 nan,mAP 归零,后续所有 epoch 全是 nan,训练白跑。
原因:最常见的是学习率过大加上 batch 太小导致梯度爆炸;其次是标注文件里有越界坐标,比如 width 或 height 为 0,YOLO 计算损失时除零产生 NaN;还有一个隐蔽原因是 PyTorch 与 CUDA 版本不匹配导致浮点计算异常。
解决:先把所有 txt 标注里值为 0 或超过 1 的坐标筛出来删除,这是最容易被忽略的黑匣子。然后把 lr0 降到 0.002 重启训练。如果仍出现 NaN,用 CPU 模式跑 10 轮验证是不是 CUDA 相关。我碰到过一次 cu118 的 torch 在 RTX 4090 上产生 NaN,换成 cu121 后问题直接消失。
4.2 戴口罩的人脸漏检严重
现象:不戴口罩样本 mAP 在 0.85 以上,戴口罩只有 0.6,验证集里连续出现整张图只有一个戴口罩的人但模型没检出来。
原因:两个因素叠加。一是数据集中戴口罩的框数远少于不戴口罩的,类别不平衡直接拉低了正类召回;二是口罩区域颜色与背景纹理相近时,特征提取器难以区分,属于特征层面的误判。
解决:先把不戴口罩的框数通过随机下采样控制到戴口罩框数的 1.5 倍以内。再去掉遮挡超过 50% 的极端框,这类框标注本身就不准。最后在训练时开启 --multi-scale 随机变换输入尺寸,增强对小脸和部分遮挡的适应能力。
4.3 把衣服图案、海报误检成口罩
现象:验证集检测精度很高,部署到现场后,有人穿印字母的 T 恤,模型把衣领上的图案框出来当成口罩。
原因:训练框内包含额头、眼睛等区域,模型学到了“脸部附近有一块低纹理区域”的弱特征,而不是口罩本身的形状。加上背景负样本太少,模型分不清目标和背景。
解决:在数据集中加至少 10% 的负样本图片,就是完全没有人脸、但包含口罩形状物体的场景,比如口罩模型、印有口罩图案的海报。同时把推理置信度阈值从默认 0.25 提到 0.4,牺牲少量召回,显著压掉现场假正例。如果误检仍多,做难例挖掘:把误检图片单独存下来,截取误检区域作为背景负样本加入训练集。
4.4 CPU 推理只有 0.5 FPS,视频流卡成 PPT
现象:在服务器上训练好的模型,部署到只有 CPU 的工控机上,跑一帧要 2 秒,视频流完全不能用。
原因:选型阶段为了精度选了 YOLOv8s,但部署端没有 GPU,推理瓶颈在卷积特征提取,而不是检测头。
解决:三个层次优化。第一,换轻量模型,YOLOv5n 在 CPU 上能到 5 到 10 FPS。第二,把输入分辨率从 640 降到 416,推理时间大约减半。第三,导出 ONNX 并用 ONNX Runtime 的 CPU executor 跑,比 PyTorch 原生推理快约 30%。如果业务要求 15 FPS 以上,就得加一张低成本显卡或改用 NPU 设备,这不是纯软件能解决的。
4.5 打包 exe 后闪退:模型文件和资源路径丢失
现象:用 PyInstaller 打包成 exe 后双击启动,窗口一闪而过,后台日志显示找不到模型权重文件。
原因:PyInstaller 默认只打包 Python 源码和依赖库,不打包模型、配置文件这类外部资源。而且打包后程序运行时的工作目录是临时解压目录,代码里用相对路径读取 best.pt 自然失败。
解决:把模型文件作为数据文件打进 exe,并用 sys._MEIPASS 动态拼接资源路径。
import sys from pathlib import Path def resource_path(relative_path): base_path = getattr(sys, "_MEIPASS", Path(__file__).resolve().parent) return str(Path(base_path) / relative_path) model_path = resource_path("weights/mask_best.pt")打包命令里加 --add-data 参数:
pyinstaller -F --add-data "weights/mask_best.pt;weights" app.py注意 Windows 上用分号分隔源路径和目标路径,Linux 或 macOS 上用冒号。这个细节不知道的人会在打包后反复踩坑。加载模型后把路径打印出来,确认资源确实存在于临时目录里,再继续处理视频流。
5. 部署验证与进阶:ONNX 导出、阈值微调与 FPS 体检
5.1 ONNX 导出与最小推理脚本
模型训练完,mAP 看起来不错,但离“能给别人用”还差一步。我建议导出 ONNX,用 ONNX Runtime 做推理,部署端不用装 PyTorch,打包体积也小一圈。YOLOv5 的导出命令如下:
python export.py --weights runs/train/exp/weights/best.pt --include onnx --img-size 640导出后写推理脚本时,预处理必须保持训练时的 letterbox 尺寸一致,推理后的归一化坐标再映射回原图。这一步最容易出的问题是在 resize 时直接拉伸,导致框的位置偏移。验证 ONNX 是否正确的办法很简单:用同一张图分别跑 PyTorch 原生模型和 ONNX 模型,对比输出框坐标,误差在几个像素以内就说明导出成功。
5.2 置信度阈值与距离边界:上线前的三组测试
现场误检多的另一个原因是默认置信度阈值太低。YOLOv5 默认 conf=0.25 偏激进,我会在推理脚本里根据场景分开调:门禁通道要求少误报,conf 设 0.5;考勤抓拍要求少漏报,conf 设 0.35。NMS 的 iou 阈值一般保持 0.45 不动。
上线前我会跑三组测试:公开验证集确认 mAP@0.5 在 0.85 以上;现场采集的白天和夜间图片各 200 张,看误检漏检;视频流 FPS 测试看实时性。三组里现场真实图片的权重最高,公开验证集数字只作参考。还要测一个距离边界:5 米外的人脸能不能被框出来。门禁场景如果检测距离要求超过 3 米,就需要从摄像头安装位置和输入分辨率上重新设计,这属于方案层的问题,不是单纯训练能解决的。
这些年做检测项目我养成一个习惯:每个项目保留一组难样本集,专门收集误检和漏检图片,每次调参后都用同一组图回归一遍,避免模型修好一个问题的同时带回另一个问题。对刚接触深度学习、准备拿口罩检测当第一个完整项目的同学,我的建议是别把训练当成终点,把部署和回归测试也走完。希望帮到你。
本文还有配套的精品资源,点击获取