☰
基于深度学习的口罩检测系统:YOLOv3源码与VOC数据集实战解析
2026/9/28 2:30:31 网站建设 项目流程

简介:这份资源是基于深度学习的口罩检测系统完整工程包,适用于毕业设计、期末大作业及课程设计等场景。项目以YOLO目标检测算法为核心,提供YOLOv3、Darknet53、YOLOv3-tiny三种模型配置,可快速实现图像中人员是否佩戴口罩的识别与定位。压缩包共45个文件,涵盖Python脚本、模型配置文件、文本标注数据及示例图片等,其中脚本负责模型构建、训练、转换与批量检测,配置文件保存网络结构参数,整体仅2.64MB,轻量易用。目前已有41人学习下载。资源内含完整训练流程,包含模型训练脚本、格式转换工具、候选框聚类优化以及数据预处理脚本,便于准备数据集。同时附带说明文档与测试图片,帮助快速上手。对想入门YOLO检测或完成相关课程设计的学生而言,是一份结构清晰、可直接运行的参考资料。

1. 基于深度学习的口罩检测系统:别急着跑代码,先看懂这个结构

一个“基于深度学习的口罩检测系统.zip”摆在你面前,如果你是冲着毕设、期末大作业或课程设计去的,我最想提醒你的是:这个包能不能跑通,和你会不会用里面的文件是两码事。它不是你想象的“安装即用”的成品软件,而是一套完整的YOLOv3源码加VOC格式数据集的组合。换句话说,这是一个可以直接拿来训练、验证、推理的深度学习目标检测工程,适合做课程设计和毕业设计,但前提是你得知道哪份文件负责什么、哪份文件是怎么串起来的。

很多拿这套代码的人,第一反应是解压后直接打开train.py开始训练。以我的经验,这样翻车概率极高——因为在这之前,你要先弄明白voc_annotation.py生成的txt文件是什么、yolo.py里的默认anchors到底适不适合口罩目标、模型配置里的classes数量改成2了没有。这一篇我会按实际落地顺序把这套包的构成、每个关键脚本的开箱用法和踩坑点讲清楚。不绕弯子,直接进入正题。

2. 从原始图片到训练标注:VOC格式是这套系统的地基

2.1 为什么是VOC格式而不是COCO或YOLO格式

打开压缩包,你会看到VOC_data和VOC_mask两个目录,还有voc_annotation.py这个脚本。这一堆东西的背后只有一件事:构建YOLOv3可以消费的数据集。在目标检测任务里,数据格式决定了后续训练能不能直接跑。YOLOv3原生训练脚本读取的是txt标注文件——每行一条样本,格式是“图片路径 x1,y1,x2,y2,class_id”。而VOC数据集用的是XML文件标注,每个目标的坐标和类别都写在XML里。

这套系统的设计逻辑就是:先用LabelImg等标注工具把图片标成VOC格式的XML,再用voc_annotation.py把XML转成YOLO训练用的txt。你解压后看到的_mask_train.txt、_mask_val.txt、_mask_test.txt,就是转换后的成品,分别对应训练集、验证集、测试集的样本索引。voc_annotation.py的作用是在你新增或修改图片标注后,重新生成这三个txt文件。

2.2 转换脚本的三种运行方式

voc_annotation.py这个脚本在源码里有多套变体,取决于你的数据目录结构。但最常用的调用方式是在项目根目录直接执行:

python voc_annotation.py

执行之后,脚本会扫描VOC_mask目录下的JPEGImages、Annotations两个子文件夹,把图片和XML一一配对。如果每张图至少有一个标注目标,就将它的路径和归一化坐标写入对应txt。要知道,这里有几个参数是你必须根据自己的目录实际去改的,核心是base_dir和image_dir的路径配置,还有一个很关键的参数:classes列表,它决定脚本把你XML里的哪一个“name”字段识别成一个类别。口罩检测就两类:with_mask和without_mask,如果你标注文件里写的是mask和no_mask,那就要同步改掉,不然后续训练时类别索引会错位。

2.3 数据集怎么划分才靠谱

很多做课程设计的同学拿到这个包,直接就把VOC_mask文件夹里的图片全部拿去训练了,这是一个特别常见的翻车点。voc_annotation.py会按代码里设定的比例把数据分成三份,但默认划分不一定合理。你需要注意两个细节:一是训练集要占绝大多数,通常按7:2:1来切,也就是train占70%,val占20%,test占10%;二是划分最好按文件夹来,不要把同一个视频帧序列里的连续图片一部分进训练集一部分进测试集,否则模型会“记题”。

打开生成后的_mask_train.txt,每行长这样:

VOC_mask/JPEGImages/000001.jpg 0.352,0.486,0.583,0.697,0

这里的四个小数是归一化后的x1,y1,x2,y2坐标,对应到图片宽高的比例;行尾的0就是类别索引,with_mask为0,without_mask为1。如果你改过类别顺序,这条数据的内容也要重新生成,不能直接复用旧文件。

提示:改任何标注或类别定义后,建议把三个txt都删除重新生成。手工去改txt里的坐标和类别索引容易出错,你很难验证每一行是不是对的。

3. 锚点计算:为什么直接用默认anchors会让口罩检测结果变差

3.1 YOLOv3的anchors机制与口罩目标的尺度特征

YOLOv3不是直接在原始分辨率上做检测,而是把图片划分成网格,每个网格预测若干边界框。这些边界框的初始尺寸由anchors(锚点)决定。源码里给的yolo_anchors.txt是COCO数据集聚类出来的9组宽高,例如(10,13)、(16,30)、(33,23)这组小锚点,对应的是COCO数据集里的小物体分布。但口罩在人脸上的物理大小和COCO里的盘子、书本、猫不一样,锚点不匹配,模型训练初期会很挣扎,表现为loss降得慢,或者训练完后小尺寸口罩漏检。

这里不涉及玄学,是纯数字上的不适配。你拿到的这个包里有kmeans.py,它专门解决这个问题。kmeans.py会用K-means算法对你自己的训练集标注框做聚类,重新算出9组适合当前数据集的anchors。这是从COCO预训练权重往自己数据集迁移时最容易被跳过的一步。

3.2 kmeans.py 的使用与anchors替换步骤

执行kmeans.py的方法很直接:

python kmeans.py

但这个脚本默认读取的标注格式可能是VOC的XML,也可能是YOLO的txt,不同版本不一样。我习惯性的做法是:先确认脚本里load_dataset函数读的是哪个文件,如果是txt,就指向_mask_train.txt;如果是XML,就指向VOC_mask的Annotations目录。运行完成后,脚本会输出9组新anchors,例如:

new anchors: 35,28 46,52 68,43 82,77 108,96 139,129 196,159 235,217 296,240

拿到这个输出之后,你需要做两件事。第一件,打开yolo.py,找到anchors的定义处,把这9组数字替换进去;第二件,打开model_data目录下的yolo_anchors.txt,把旧的COCO锚点改成这9组新值。注意顺序和逗号格式不要弄错,YOLOv3对anchors的顺序有要求——按面积从小到大排列,小锚点在前,大锚点在后,因为检测头输出的特征图是从细到粗,依次对应小中大目标。

3.3 聚类结果异常时的判断方法

kmeans.py跑完以后,如果输出的9组anchors里出现明显偏大的值,比如宽高达到600以上,这说明你的标注可能有错误框,坐标没归一化或者标注框超出了图片边界。这时候不要急着用新anchors,先回去查标注数据。另一种情况是聚出来的9组anchors中,最大的那两组相差不大,比如(240,180)和(252,188),说明数据里的大目标样本不够,模型对大目标的分辨能力会被浪费。这种情况下我会选择只保留6个或8个anchors,同时把yolo.py里mask参数对应的锚点索引同步修改,避免维度不匹配报错。

4. 正式训练:train.py与train_bottleneck.py的差异化使用

4.1 两种训练脚本分别解决什么问题

这个压缩包里同时存在train.py和train_bottleneck.py,它们对应两种训练策略。train.py是端到端训练,即从零开始或者加载预训练权重直接训练整个网络,更新所有层的参数。train_bottleneck.py则是一种两步训练法的第二步:它先冻结主干网络Darknet53的大部分层,只训练检测头(也就是YOLO层附近的卷积参数),等loss下降到一定程度后再解冻所有层做微调。在GPU资源紧张或者数据集只有几千张图的场景下,train_bottleneck.py的收敛速度明显更快,也不太容易过拟合。

4.2 train.py 的关键参数与一次可落地的训练命令

我把train.py里最常见的训练配置拆开说说,你不用全懂,按这个模板改成你的路径就行:

python train.py \ --model_path model_data/yolo_weights.h5 \ --anchors_path model_data/yolo_anchors.txt \ --classes_path model_data/voc_classes.txt \ --batch_size 8 \ --epochs 50 \ --input_shape 416,416

其中model_path指的是预训练权重或当前训练状态的文件路径。如果你没有预训练权重,第一次跑可以让这个参数指向一个空的h5路径,但效果会差很多。anchors_path和classes_path必须对应你的新锚点和“with_mask、without_mask”类别文件。input_shape决定模型输入分辨率,416,416是速度和精度的均衡点,如果显存够大,换成608,608精度会高一点,但训练时间差不多增加一倍。batch_size的设置要看显存,8是一个不高的起点,如果你在训练时报ResourceExhausted错误,就降到4或者2。

4.3 如何监控loss并判断训练是否正常

训练正常启动后,终端每隔几个batch会打印一次loss,类似这样:

Epoch 1/50: loss 12.7342 - val_loss 11.8934 Epoch 2/50: loss 10.1023 - val_loss 9.8456

关注两个指标。第一,loss有没有持续下降——如果前5个epoch loss几乎不动,大概率是anchors和classes配置不匹配,或者学习率设太低了。第二,val_loss是否比loss高很多——val_loss连续10个epoch比loss高2以上,说明过拟合开始出现,应该提前停止或加大数据增强。这套代码没有内置早停机制,所以你要自己在训练到大概70%轮次时观察val_loss的走势,如果已经开始反弹,就果断Ctrl+C终止训练,再调低epoch数重跑,不要让它跑完全部50轮浪费时间。

4.4 模型保存与推理前置操作

训练结束后,工作目录下会出现训练好的权重文件。在推理之前,你还得用yolo.py做一次模型加载测试。yolo.py的底部有一个默认的检测入口,直接运行:

python yolo.py

程序会加载模型并打开摄像头或读取一张默认图片进行检测。第一次跑通时,屏幕上会打印检测结果,包含每个目标的类别、置信度和边界框坐标。这一步验证的不只是模型,更是验证你的整个环境和路径配置是否正确。很多人在这一步卡住,原因是yolo.py里的model_path指向的是旧路径,或者权重文件保存时用的回调函数没设对,导致路径不存在。

5. 训练和推理中的避坑指南:五条真实踩坑记录

5.1 训练loss降低但检测结果全无

现象:训练过程看起来正常,loss从十几降到一两,但用yolo.py检测时,图片上一张口罩都框不出来,或者置信度全部低于0.1。

原因:这类问题90%出在anchors和类别序号错位上——训练时用的类别顺序是with_mask=0、without_mask=1,但voc_classes.txt里写的顺序反了;或者kmeans.py聚类出的是新anchors,但yolo.py和yolo_anchors.txt里还在用COCO默认值。

解决:把model_data/voc_classes.txt打开,确认里面的类别名称与voc_annotation.py里classes顺序完全一致;再把yolo.py与yolo_anchors.txt里的anchors和训练时用的新锚点逐项核对。两个文件必须完全一致,任何一边不对都会让检测头输出的解码结果错位。

5.2 训练时显存溢出,OOM中断

现象:train.py刚启动没几个batch就报ResourceExhaustedError,提示显存不够。

原因:最常见的不是batch_size太大,而是input_shape设得偏高。有人直接把分辨率拉到608甚至更高,小显存显卡根本扛不住。另一个容易被忽视的是,train_bottleneck.py在冻结主干时,仍然会把整张计算图保留在显存里。

解决:先把batch_size降为2,input_shape降到416,416试跑。如果还爆,看你的显卡是不是只支持半精度,在train.py里开启混合精度训练可以省下近一半显存。代码里如果有model.compile时加载了Adam优化器,把学习率从默认的1e-3稍微调低到5e-4,也有助于稳住前几轮的loss波动。

5.3 模型训练完了,但检测慢到无法用于视频

现象:单张图片检测要200-400毫秒,跑视频基本一帧一卡。

原因:yolo.py默认加载的是完整版YOLOv3,它的推理速度就是比tiny版本慢许多。这个包里有yolov3-tiny.cfg和对应的tiny_yolo_anchors.txt,就是给这种情况准备的。

解决:改用tiny模型做推理——在yolo.py里把模型结构切换为tiny,anchors换成tiny_yolo_anchors.txt里的值。代价是精度会有一定下降,但对口罩这种较大目标,tiny模型完全够用。实测在GTX 1060上能把速度推到30毫秒左右,做实时视频检测没有压力。

5.4 训练时所有图片都报“No labels found”警告

现象:voc_annotation.py执行完,终端提示很多图片没有对应的标注,或者生成txt后空行一堆。

原因:一是XML文件名和图片文件名对不上——VOC_mask/JPEGImages里图片是000123.jpg,但Annotations里XML是000124.xml,或者文件名大小写不一致;二是你标注的XML里,所有的目标object类别都不在脚本允许的classes白名单内。

解决:用下面这段脚本快速检查两个目录的文件名是否一一对应,比肉眼省力得多:

import os img_dir = "VOC_mask/JPEGImages" xml_dir = "VOC_mask/Annotations" img_names = {f.split(".")[0] for f in os.listdir(img_dir)} xml_names = {f.split(".")[0] for f in os.listdir(xml_dir)} print("仅在图片目录出现的:", len(img_names - xml_names)) print("仅在XML目录出现的:", len(xml_names - img_names))

如果输出非0,就去把文件名改成一致。另外打开任一XML确认object的name字段是with_mask或without_mask,再检查voc_annotation.py里的classes列表是否包含了这两个值,标签属性不区分大小写的问题经常让人找半天。

5.5 视频检测崩溃或画面全黑

现象:直接用detect_batch.py或者摄像头检测时,程序运行到某帧突然报错退出,或者在某一帧之后输出画面变黑。

原因:视频流中出现了异常帧——可能是画面全黑、分辨率突变或编码损坏的帧,检测代码没有做空帧保护。另一个原因是画面色彩空间处理出错,默认读取BGR帧但模型训练时用的是RGB,颜色通道翻转了,模型看到的是“反色”图像,输出自然全是噪声。

解决:在检测循环中加一行空帧判断,视频帧读取进来后先检查是否存在,再做通道转换。代码如下:

ret, frame = cap.read() if not ret: break frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) # 送入模型检测...

6. 批量检测与效果验证:detect_batch.py的高效用法与结果自查

6.1 对一组测试图片跑批量检测的实际操作

走到这一步,你已经有了训练好的模型权重,接下来最值得做的事是拿它跑一批没见过的测试图,而不是只在一两张图上碰运气。detect_batch.py就是干这个用的。它通常有两种用法:一种是指定一个图片文件夹,把里面所有图片检测完并把结果图输出到指定目录;另一种是读取一个txt文件列表,逐个处理对应路径的图片。

我一般这样执行:

python detect_batch.py \ --model_path model_data/ep050-loss2.315-val_loss2.021.h5 \ --classes_path model_data/voc_classes.txt \ --anchors_path model_data/yolo_anchors.txt \ --image_dir test_img \ --output_dir output_detected

程序会遍历test_img文件夹下所有JPG图片,然后生成带检测框的结果图。这里最核心的参数是model_path,很多人会忘记把训练完的最终权重文件路径写进去,导致程序还在用预训练权重跑检测。另一个细节是output_dir不存在时,脚本一般不会自己创建目录,你要提前mkdir,否则会在写文件时报错。

6.2 批量检测完之后如何快速判断模型行不行

批量检测不是跑完就结束了,还要有一套快速自查的方法。我会在output_detected里随机挑20张图,检查三个事情:检测框有没有贴合口罩边缘而不是一个很大的方框;同时出现多个人的图片里是漏检了还是正确检出了;有没有把背景物体误报成口罩。不要只挑检测好的图看,那样会给你一种“模型很棒”的错觉。如果发现个别有框但置信度很低的,可以在detect_batch.py里把score阈值下调到0.3再跑一次,对比下是阈值问题还是模型本身没学到。这套模型输出默认的置信度阈值是0.5,对口罩这种目标,0.3到0.5之间通常有一个比较舒适的平衡区。

6.3 一张图验证你的检测效果是否真的可用

挑一张包含多个行人、部分遮挡的实拍图,执行下面的单张图调用。yolo.py和detect_batch.py都支持单图模式,但更直接的方式是用convert.py把训练好的keras权重转成推理模式后跑:

python convert.py \ --model model_data/ep050-loss2.315-val_loss2.021.h5 \ --classes_path model_data/voc_classes.txt \ --anchors_path model_data/yolo_anchors.txt \ --output_model model_data/converted_yolo.h5

转换完成后,再用yolo.py加载converted_yolo.h5进行单图检测。这一步的意义是绕开训练状态自带的权重结构,得到一个更干净的推理模型。如果转换后的模型在你测试图上表现和训练时一致,说明权重没有损坏,可以正常部署。从那以后我每次拿到新的检测需求,都强制自己走一遍“格式检查→锚点聚类→训练→批量推理→自查输出”这个流程,不跳步,不靠感觉。

希望这套流程对你有用。

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

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

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

立即咨询