简介:这是一份基于YOLOv3的口罩检测系统毕业设计源码包,面向计算机视觉与深度学习方向的本科毕业生及初学者,可帮助快速搭建口罩识别实验环境,掌握目标检测模型加载、图像推理与结果展示的完整流程。整个项目以Python为主要语言,算法与工程实现结合紧密,适合作为课题研究或课程设计的起点。包体共21个文件,大小约11.04MB,其中8个Python脚本承担核心检测逻辑与工具函数,另有6个pyc编译模块、2张jpg样例图片、hdf5模型权重、JSON配置、markdown说明文档及mp4演示视频,目录划分明确,便于按需查阅与局部修改。目前已有1002人学习下载,具有较好的参考价值,学习者可获得完整项目源码、预训练权重、演示视频与说明文档,既能直接运行复现检测效果,也可在此基础上调整模型参数、扩充数据集或改进后处理流程,进一步适配不同应用场景,为毕业论文写作或答辩演示提供完整的方案支撑。
1. 毕业设计选 YOLOv3 做口罩检测:这套源码到底在解决什么问题
标题里的“基于 YOLOv3 的口罩检测系统源码.zip”,是这几年本科毕业设计里最常出现的一类题目:拿一个人脸检测模型,把类别改成“戴口罩/没戴口罩”,再套一个摄像头界面,就是一个完整系统。很多人的第一反应是“YOLOv3 太老了,为什么不直接上 YOLOv8”,但真把它做进论文和答辩的都知道,这个选择恰恰是最稳的——硬件要求低、代码结构直白、创新点好找,踩坑资料也多。这篇笔记就按我实际调这个课题的经验,把从数据集准备、配置文件修改、训练参数到答辩验证的完整链路拆开讲。
2. YOLOv3 口罩检测的技术构成:从网络结构到数据集准备
2.1 为什么 YOLOv3 对口罩这类小目标够用
口罩检测本质上是一个单类别目标检测任务:正样本是“戴了口罩的人脸区域”,负样本是“没戴口罩的人脸区域”,难点集中在遮挡、尺度变化和正负样本不平衡。YOLOv3 用 Darknet-53 做骨干网络,在输入尺寸 416×416 时输出三个尺度的特征图,分别是 13×13、26×26、52×52,后两者专门负责中小目标。监控画面里一张脸往往只占画面面积的百分之几,口罩区域更小,所以 52×52 分支的预测质量直接决定了召回率。
很多同学把这个课题做成“所有口罩都检测不出来”,原因不是模型太差,而是 anchors 和输入分辨率根本不匹配。YOLOv3 默认的 anchors 是按 COCO 的 80 类目标统计出来的,里面有大目标框,而口罩人脸框整体偏小、宽高比接近 1:1.2 左右。照搬默认配置时,小目标分支的 anchor 数量不够,正样本匹配率低,训练的 loss 自然降不下去。
从论文写作的角度看,YOLOv3 的结构也适合做“改造”。Darknet-53 每层卷积的参数、输出尺寸都有公开资料,画结构图、写前向传播流程,比用一个封装好的 YOLOv8 要好写得多。毕设答辩里老师常问“你改了什么”,最常见、最好回答的就是:加了一层注意力模块,或者修改 anchors 生成策略,基于 YOLOv3 做这些改动,代码改动量小且效果容易对比。
2.2 口罩数据集的目录组织与标注文件格式
做这个项目,数据集通常分两部分:公开的口罩人脸数据集,加上自己用标注工具补充的场景图。公开数据集拿来做训练主集,自标注数据用来覆盖“侧面脸、低头、口罩戴到眼睛下面”等让模型翻车的边缘场景。常见做法是统一转成 Darknet 的格式,目录结构如下:
mask_dataset/ ├── images/ │ ├── train/ # 训练图片,jpg/png 均可 │ └── val/ # 验证图片,和训练集分开 ├── labels/ │ ├── train/ # 与 images/train 同名同前缀的 txt │ └── val/ ├── train.txt # 每行一个训练图片的绝对路径 ├── val.txt └── mask.names # 类别名文件labels 里的 txt 和图片同名,每一行代表一个目标框:class_id center_x center_y width height,全部是归一化后的 0~1 之间的浮点数。举个例子,一张 1920×1080 的图里有一个口罩框,左上角是 (500, 300),右下角是 (700, 600),转换成 YOLO 格式就是:
0 0.3125 0.4167 0.1042 0.2778计算方式是:中心点 x = (500+700)/2 / 1920 = 0.3125,宽 = (700-500)/1920 = 0.1042。容易出错的地方是坐标归一化必须按图片原始尺寸,而不是按模型输入的 416 去算。很多标注工具导出时会把宽高搞反,训练前抽几张图验证一下标注文件是否是“框住人脸”的,比直接开训练省时间得多。
2.3 Darknet 还是 PyTorch:毕设场景这样选
拿到源码包之前,先想清楚要跑哪条技术路线。Darknet 是 YOLOv3 的原生框架,纯 C 写的,命令直接、性能好,适合“最小改动跑通”的场景;PyTorch 复现版则适合需要改代码、加界面、写训练细节的毕设。我一般建议从源码包装的是哪个框架入手,不要中途换框架。
| 对比项 | Darknet(原版) | PyTorch 复现版 |
|---|---|---|
| 安装难度 | 需编译,依赖 OpenCV、CUDA | pip 安装,依赖少 |
| 改模型结构 | 改 cfg 文件即可,不用动 C 代码 | 需要改 Python 模型类 |
| 调试便利性 | 日志直接,定位问题快 | 可以断点调试,适合创新点 |
| 接 GUI/摄像头 | 需要写 C 接口或调命令行 | 直接 import,开发效率高 |
| 答辩友好度 | 结构清晰,但创新点实现难度高 | 容易嵌入注意力、特征融合等改动 |
如果你下载到的 zip 是带 GUI 界面的,大概率是 PyTorch 路线;如果源码包只有可执行文件和 cfg,那基本是 Darknet 编译版。无论哪条路,训练数据格式都是上面的 YOLO 格式,这一步通用。
3. 跑通源码包:环境、配置文件与训练命令
3.1 拿到 zip 后先做的事:梳理目录和权重文件
这类毕业设计源码包来源五花八门,但目录结构大同小异。先别急着配环境,打开压缩包扫一遍,重点看三个东西:有没有训练好的权重文件、有没有 cfg 配置文件、有没有标注好的数据。常见结构是这样的:
yolov3-mask/ ├── cfg/ │ ├── yolov3.cfg │ └── mask.data ├── data/ │ ├── mask/ # 数据集放这里 │ ├── mask.names │ └── train.txt ├── weights/ │ ├── yolov3.weights # 预训练权重 │ └── mask_final.weights # 训练好的权重 ├── detect.py # 推理脚本 ├── train.py 或 Makefile └── requirements.txt先做两件事:第一,训练好的权重有没有。如果有,先拿一张戴口罩的图跑推理,确认模型能出框,再谈训练;如果没有,找预训练权重。YOLOv3 官方发布的 darknet53.conv.74 是分类预训练权重,用它做迁移学习的起点,比从零训练收敛快得多。第二,检查 train.txt 里的路径是不是你本机的路径,压缩包从别人机器上拷过来,绝对路径基本都失效了,这是跑不通的第一大原因。
3.2 修改 cfg 文件:类别数、filters 和 anchors
YOLOv3 的 cfg 文件里有三个 YOLO 层,对应三个尺度。每个 YOLO 层往上数第 6 层(通常是 route 或 conv)有一个关键参数 filters,它的计算公式是3 * (classes + 5)。对于单类别口罩检测,classes = 1,所以 filters = 3 * 6 = 18。也就是说,cfg 里所有“yolo 层之前的最后一个卷积层”的 filters 都要改成 18,一共有三处。
# 以 Darknet 的 cfg 为例,逐层检查 # 找到形如以下的三处卷积层,把 filters=255 改成 18 [convolutional] batch_normalize=1 filters=18 # 原来是 255,对应 80 类,现在单类必须改 size=1 stride=1 pad=1 activation=linear # 在它后面的 [yolo] 层 [yolo] mask = 0,1,2 # 使用当前尺度下的前 3 个 anchors classes=1 # 改成 1 num=9这里最容易翻车的地方是 filters 只改一处,或者 classes 没改。运行时如果报mismatch、Assertion failed: l.outputs == params.inputs,十有八九就是这三处 filters 不一致。建议直接搜索filters=,把数值不等于 18 的卷积层逐个检查,别只改离 yolo 层最近的那一个。
anchors 方面,如果自建数据集里的人脸尺寸和 COCO 差异很大,可以用 k-means 在训练集上重算 anchors。YOLOv3 默认 anchors 是(10,13), (16,30), (33,23), (30,61), (62,45), (59,119), (116,90), (156,198), (373,326)。对于口罩人脸这种目标,前三组小框通常够用,很多毕设项目不改也能收敛;如果发现小目标召回率低,再重算不迟。重算方法是:读取所有标注框的宽高,按 IoU 距离做聚类,聚类数取 9,然后把结果按面积从小到大填进 cfg 的三个 yolo 层。
3.3 训练命令与关键参数
Darknet 路线训练命令是:
./darknet detector train cfg/mask.data cfg/yolov3.cfg weights/darknet53.conv.74 -dont_show -map各参数含义:mask.data里指定了 train.txt、val.txt、类别数、names 路径;darknet53.conv.74是预训练权重,只加载骨干网络部分,不加载检测头;-dont_show表示不弹训练窗口,适合远程服务器;-map表示每个 epoch 后计算验证集 mAP,这个参数建议一开始就加上。
mask.data 内容如下:
classes=1 train=data/train.txt valid=data/val.txt names=data/mask.names backup=backup/PyTorch 路线则通常是:
python train.py --data data/mask.yaml --cfg cfg/yolov3.cfg --epochs 100 --batch-size 16核心训练参数里,batch size 和 learning rate 是绑定的。Darknet 默认为 batch=64、subdivisions=8,意思是一个 batch 分成 8 次前向,显存不够就把 subdivisions 调大,比如 16,不要直接降低 batch,因为 batch 变了学习率也要变。初始学习率 0.001,burn_in=1000 步 warmup,policy 用 steps,在迭代到 max_batches 的 80% 和 90% 时分别乘以 0.1。对于口罩检测,max_batches 一般设置在 6000~12000 之间,数据集小就取小值,数据集大可以往上加,不要照搬 COCO 的 500200。
4. 训练日志、模型评估与摄像头接入
4.1 从训练日志里看 loss:normalizer 与 avg 的关系
训练过程中终端会每隔 100 轮打印一次 loss 信息,类似:
4000: 0.864321, 0.721532 avg, 0.000100 rate, 3.222000 seconds, 5312 images第一个数0.864321是当前 batch 的 loss,第二个0.721532 avg是平滑后的平均 loss。判断训练是否正常,看 avg 而不是第一个数。口罩单类别项目的 loss 在 0.5 左右就能有不错的检出效果,不要盲目追求 loss 降到 0.05,那往往是过拟合的开始。如果 loss 在 6 附近长时间不动,多半是 anchors 不合适或者数据标注错误,不是训练轮数不够。
如果你用的是 PyTorch 版本,训练脚本会输出更丰富的指标,包括 GIoU loss、obj loss、cls loss。重点盯 obj loss,因为它反映“该有目标的位置有没有预测出来”。obj loss 降不下去,说明正负样本分配有问题,优先检查数据里有没有大量空标注图——一张图没有任何目标框,训练时它会作为纯负样本参与计算,如果这种图太多,模型会倾向把所有区域都预测为背景。
4.2 用测试集算 mAP:命令与输出解读
Darknet 训练结束后,用单独的 val 集计算 mAP:
./darknet detector map cfg/mask.data cfg/yolov3.cfg backup/mask_final.weights输出里会有一个表格,每行对应一个类别,最后一行是mean average precision (mAP) = 0.8854。对口罩检测来说,mAP 在 0.85 以上就算合格,0.9 以上就是优秀。但 mAP 只说明“框得准不准”,不能说明“有没有漏掉”——漏检率要看 recall。命令输出里同样有Recall: 0.91之类的指标,答辩时两个数字都报,比只报 mAP 更有说服力。
如果发现 mAP 高但 recall 低,典型的处理办法是调低检测阈值。Darknet 里用
./darknet detector test cfg/mask.data cfg/yolov3.cfg backup/mask_final.weights -thresh 0.3 data/demo.jpg-thresh 0.3表示置信度阈值设置为 0.3,默认是 0.25。实际部署时阈值设 0.4~0.5 可以减少误检,但会把低置信度的真目标过滤掉。阈值的选择要结合你的应用场景:门禁场景宁可误报不可漏报,阈值调低;统计场景则相反。
4.3 最小可用的摄像头检测代码
毕设要求里十有八九带着“实时检测”四个字。这里给出一个不依赖具体框架的 Python 伪代码,思路是 OpenCV 读摄像头帧,调用检测函数,画框,用imshow显示:
import cv2 def detect_frame(frame, model): # 将 BGR 帧转为模型输入,resize 到 416x416,归一化 # 模型前向,得到 boxes, scores, class_ids return boxes, scores, class_ids cap = cv2.VideoCapture(0) # 0 代表默认摄像头 if not cap.isOpened(): print("摄像头打开失败,检查权限或被占用") exit() while True: ret, frame = cap.read() if not ret: break boxes, scores, class_ids = detect_frame(frame, model) for box, score in zip(boxes, scores): x, y, w, h = box cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2) cv2.putText(frame, f"mask {score:.2f}", (x, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2) cv2.imshow("mask detect", frame) if cv2.waitKey(1) & 0xFF == ord('q'): break cap.release() cv2.destroyAllWindows()这段代码里最容易忽视的是输入预处理必须和训练时一致:训练用的是 416×416、RGB 顺序、归一化到 0~1,推理时如果直接喂 BGR 原图,检测效果会明显衰减。另一个细节是VideoCapture(0)在笔记本上偶尔会打开失败,换成cv2.CAP_DSHOW参数在 Windows 上更稳。实时性方面,CPU 跑 YOLOv3 大约只有 1~3 FPS,想流畅得用 CUDA 版本的 Darknet 或 PyTorch 的 GPU 推理。
5. 避坑清单:最容易翻车的 5 个现场
5.1 现象:训练 loss 卡在 6 左右,几百轮都不降
原因:最典型的是 anchors 未修改,默认 anchors 针对 COCO 大目标,80 个类共享 9 组框,口罩人脸这种小目标匹配不到合适的正样本,模型学不到东西。
解决:先在训练集上跑一次 k-means 聚类,算出小目标专用 anchors。如果懒得重算,手动把 cfg 里三组 anchor 统一缩小到原来的 0.5~0.7 倍,通常是第一组直接用(5,7), (10,14), (15,25),效果会立刻改善。
5.2 现象:口罩戴得歪、只遮住下巴的样本几乎全漏检
原因:训练集里“标准戴法”的图太多,模型学到的特征是“脸下半部分被覆盖”的平滑区域,一旦遮挡形态变化,特征对不上。
解决:去网上找“口罩戴歪”“口罩挂在下巴上”的图,补充几百张作为训练集。另一个技巧是数据增强里把随机裁剪和旋转的比例调高,让模型见过更多姿态变化。做过一次对比实验,补充 300 张歪戴图后,这类漏检从 30% 降到 8% 左右。
5.3 现象:没戴口罩的检测正确,戴口罩的反而被误报成“未佩戴”
原因:正负样本比例失衡。如果一个批次里“未佩戴”样本占 90%,模型就偏向预测“未佩戴”这个类别,这是单类别检测里典型的类别不平衡问题。
解决:控制训练集里两类样本的比例在 1:1 到 1:1.5 之间,不要盲目堆数据。另一个有效做法是提高 obj loss 的权重,让模型更重视“有没有目标”的判断,再让类别判断层去区分。
5.4 现象:显存不够,把 batch 从 64 改成 8,之后训练效果明显变差
原因:batch 和学习率是配套的。Darknet 默认策略是 batch=64 时 lr=0.001,改成 batch=8 后学习率仍然 0.001,梯度噪声过大,loss 震荡。
解决:按比例降低学习率,batch 缩小到原来的 1/8,学习率也降到原来的 1/8 左右,也就是 0.000125,然后观察 loss 是否稳定下降。或者保持 batch 不变,把 subdivisions 调大,让单个 batch 的内存占用摊开,这是更推荐的做法。
5.5 现象:摄像头推理只有 1 FPS,卡顿到没法演示
原因:CPU 推理 YOLOv3 本身就是这个性能水平,不是代码问题。用 OpenCV 的imshow每次调用都会阻塞刷新,会进一步拖慢。
解决:答辩演示时不要实时跑摄像头,用预先录制的视频作为输入源,视频可以按帧读取,检测结果离线渲染后展示。如果想保留实时感,把模型输入尺寸从 416 降到 320,或者换用 Tiny YOLOv3 结构(在 cfg 里删掉一个尺度),FPS 能提升一倍以上,代价是精度略降。
6. 一个答辩实用的验证技巧:按遮挡难度分组建测试集
mAP 是一个数字,老师听完就忘。真正留下印象的,是你拿出几张“口罩戴歪了但模型依然检出来”的图,和一张“口罩戴得好却被漏掉”的图。我在帮某开发者收尾这个毕设时,发现最有效的验证方式不是跑测试集,而是按难度分层抽样。
做法很简单:从摄像头实际场景里截 30~50 张图,分成三组——A 组是标准戴法、正面、光线正常;B 组是口罩拉到下巴、侧面、低头;C 组是多人远距离、小目标、口罩颜色和肤色相近。每组 10 张,统计每组的检出率和误检数,做成一个表格。
这个表的威力在于它能直接回答答辩最常问的两个问题:“你的系统在什么情况下会失效?”和“你的创新点解决了什么问题?”如果你的创新点是改进了小目标检测,C 组从 50% 提到 80%,这就是最直观的证据。这张表还能反向指导训练:哪一组不行,就去补哪一类的数据或者调哪一组 anchors。
我自己做这个项目时咬过一次教训:当时只追求 mAP 刷到 0.92,觉得自己离完美不远了,直到拿了 20 张走廊监控截图一测,发现侧面口罩漏检率奇高,才意识到 mAP 高是因为测试集里都是“老实图”。从那以后,每次训练完第一件事不是看曲线,而是跑一遍那几组难例图。这套验证流程也成了我后来做检测类项目的习惯,希望帮到你。
本文还有配套的精品资源,点击获取