☰
YOLOv5口罩检测全链路实战:从数据清洗到摄像头部署
2026/10/8 19:28:17 网站建设 项目流程

简介:本资源是一套基于YOLOv5实现的口罩佩戴检测完整项目,专为高校计算机视觉课程设计与期末大作业打造,面向具备Python基础和PyTorch入门经验的学习者,解决公共场所人员口罩佩戴状态识别这一典型目标检测应用场景。压缩包共20个文件,包含8个核心Python脚本(如train.py、detect.py、export.py等)、4个预训练及微调模型权重(best.pt、yolov5s.pt、cs1.6.pt、kid.pt)、1张示例图像(ico.jpg)、1个Docker部署配置(Dockerfile)以及Jupyter Notebook教程、Spec打包配置、Git相关配置等,全面覆盖训练、推理、导出与容器化部署全流程,总大小50.58MB。目前已有304人学习下载。用户可直接运行无需修改,获得高分课程设计所需的全部代码、数据、模型与说明文档,尤其适合快速复现、理解YOLOv5在轻量级检测任务中的工程实践细节,并参考mask_kid.py等模块掌握自定义数据集适配与评估逻辑。

1. 这不是调个模型就能交差的期末作业:YOLOv5口罩检测必须跑通“数据→训练→推理→验证”全链路

很多同学拿到“基于YOLOv5的口罩佩戴检测源码+全部数据(期末大作业).zip”后,直接解压、pip install -r requirements.txt、python train.py,结果卡在CUDA out of memory,或训练完的模型在测试图上把口罩框成头发、把下巴识别成未戴——这不是代码有bug,而是对YOLOv5在真实场景下的数据敏感性、标注规范性和部署约束缺乏系统认知。这个标题里的“全部数据”往往包含光照不均的教室监控截图、侧脸/低头/遮挡严重的课堂实拍片段,而“源码”若未经适配,其默认超参数(如imgsz=640、batch_size=16)在学生本机GTX 1650显卡上必然OOM。它真正要解决的,是如何用有限算力,在非标准采集条件下,让YOLOv5稳定输出可被教师验收的检测结果。适合计算机视觉入门者、课程设计需快速落地者、以及想借该任务吃透目标检测工程闭环的初阶算法实践者。

2. 从原始数据到YOLOv5可读格式:标注质量决定模型上限,不是所有“全部数据”都开箱即用

2.1 先验判断:检查ZIP包内数据结构是否符合YOLOv5规范

YOLOv5要求数据集严格遵循/images/train、/images/val、/labels/train、/labels/val四目录结构,且每张图片必须有同名.txt标签文件,内容为class_id center_x center_y width height(归一化坐标)。常见陷阱是:

  • 原始数据含.jpg和.jpeg混存,YOLOv5默认只读.jpg;
  • 标签文件中class_id写成0(口罩)和1(未戴),但data.yaml里names: ['mask', 'no_mask']顺序错位;
  • 图片分辨率差异极大(如320×240到1920×1080),直接resize会模糊小目标。

提示:用以下命令批量检查标签合法性,避免训练中途报错

# 检查所有txt文件是否为空或格式错误 find ./labels -name "*.txt" | while read f; do if [ ! -s "$f" ]; then echo "EMPTY: $f"; continue; fi if ! head -1 "$f" | grep -qE '^[0-9]+ [0-9.]+ [0-9.]+ [0-9.]+ [0-9.]+$'; then echo "FORMAT_ERR: $f"; cat "$f" | head -3; fi done

2.2 标注清洗:用OpenCV脚本过滤低质量样本

“全部数据”常含大量无效帧:纯黑/过曝图像、人脸占比<5%的远景、严重运动模糊。手动剔除效率低,需自动化预筛:

# clean_dataset.py import cv2 import numpy as np import os def is_valid_image(img_path): img = cv2.imread(img_path) if img is None: return False # 检查亮度:直方图均值低于20为过暗,高于230为过曝 gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) mean_brightness = np.mean(gray) if mean_brightness < 20 or mean_brightness > 230: return False # 检查模糊度:Laplacian方差低于100视为模糊 laplacian_var = cv2.Laplacian(gray, cv2.CV_64F).var() if laplacian_var < 100: return False # 检查人脸区域占比(需先运行MTCNN或Haar级联粗检) face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml') faces = face_cascade.detectMultiScale(gray, 1.1, 4) if len(faces) == 0: return False total_face_area = sum(w*h for (x,y,w,h) in faces) img_area = img.shape[0] * img.shape[1] if total_face_area / img_area < 0.005: # 人脸占比<0.5% return False return True # 批量处理 for img_path in ["./images/train/*.jpg", "./images/val/*.jpg"]: for p in glob.glob(img_path): if not is_valid_image(p): txt_path = p.replace("images", "labels").replace(".jpg", ".txt") os.remove(p) if os.path.exists(txt_path): os.remove(txt_path) print(f"REMOVED: {p}")
2.2.1 关键参数说明
  • mean_brightness阈值(20/230):基于教室常见光照设定,过暗易漏检,过曝丢失纹理;
  • laplacian_var < 100:学生手机拍摄视频帧的典型模糊下限,低于此值检测框抖动明显;
  • total_face_area / img_area < 0.005:排除走廊远景等无效场景,确保模型聚焦于“可判别口罩”的人脸尺度。

2.3 数据增强策略:针对课堂场景定制Augmentations

YOLOv5默认的train.py使用--hyp data/hyps/hyp.scratch-low.yaml,但该配置对口罩检测存在缺陷:

  • hsv_h: 0.015(色相扰动)过大会使医用蓝口罩变紫,与背景混淆;
  • mosaic: 1.0(马赛克增强)在多人同框时易将A的口罩拼到B脸上,产生错误监督信号。

应修改data/hyps/hyp.mask.yaml:

参数原值推荐值原因
hsv_h0.0150.005医用口罩色域窄,微调即可
mosaic1.00.5降低多人场景误标风险
degrees10.00.0课堂场景人脸基本正向,旋转无意义且增噪
translate0.10.05防止口罩移出边界导致标签截断

注意:修改后需在train.py中显式指定--hyp data/hyps/hyp.mask.yaml,否则仍加载默认配置。

3. 训练过程可控化:避开显存爆炸、loss震荡、mAP停滞三大雷区

3.1 显存优化:GTX 1650/RTX 3050级别显卡的最小可行配置

学生本机常见显存为4GB,YOLOv5s默认batch_size=16必OOM。不能简单调小batch,需同步调整学习率和迭代次数:

# 在4GB显存上稳定训练YOLOv5s的命令(关键参数加粗) python train.py \ --data data/mask.yaml \ --cfg models/yolov5s.yaml \ --weights '' \ # 不加载预训练权重,避免显存峰值 --batch-size **8** \ # 从16降至8 --img 640 \ --epochs 100 \ --name mask_yolov5s_4g \ --hyp data/hyps/hyp.mask.yaml \ --lr0 **0.001** \ # batch减半,lr0同步减半(线性缩放规则) --lrf 0.1 \ # 终止学习率=0.001*0.1=0.0001 --cache # 启用内存缓存,减少IO压力
3.1.1 参数逻辑说明
  • --weights '':空字符串表示从零初始化,虽收敛慢但显存占用比加载yolov5s.pt(~140MB)低30%;
  • --lr0 0.001:YOLOv5默认为0.01,按batch_size比例缩放(8/16=0.5 → 0.01×0.5=0.005),但实测课堂数据需更保守,故设0.001;
  • --cache:将图片解码后缓存至RAM,避免每次epoch重复解码,对机械硬盘提升显著。

3.2 Loss曲线诊断:区分正常收敛与训练失效

训练中需实时监控train_batch0.jpg(首batch预测图)和results.png(loss/mAP曲线)。典型问题及对策:

现象原因解决方案
box_loss持续>3.0且不下降标签坐标越界(如center_x>1.0)或存在负值运行python utils/general.py --check-dataset data/mask.yaml校验
cls_loss在0.5附近震荡类别不平衡(未戴口罩样本远少于戴口罩)在data/mask.yaml中添加class_weights: [1.0, 2.5](未戴类权重×2.5)
mAP@0.5第20轮后停滞在0.65学习率衰减过早将--lrf 0.1改为--lrf 0.01,延长高lr训练阶段

3.3 验证集构建:必须包含“最难样本”才能反映真实能力

仅用随机划分的val集会高估性能。需人工构造三类难例放入/images/val:

  • 遮挡类:手扶眼镜、长发遮半脸、口罩滑落至鼻尖;
  • 光照类:背光导致人脸全黑、窗边强反光;
  • 尺度类:前排人脸占图30%,后排仅占2%(需保留原图,勿resize)。

验证时用以下命令生成详细报告:

python val.py \ --data data/mask.yaml \ --weights runs/train/mask_yolov5s_4g/weights/best.pt \ --task test \ # 使用test子集(即人工难例集) --save-txt \ --save-hybrid \ # 同时保存置信度>0.5和0.1的检测结果 --conf 0.25 # 降低置信度阈值,暴露漏检

输出的test_results.txt中重点看Recall(召回率):若难例集召回率<0.7,说明模型泛化不足,需回退到2.2节增强数据。

4. 推理与可视化:让检测结果经得起课堂演示,不只是控制台输出

4.1 实时摄像头检测:用OpenCV绕过YOLOv5默认的image-only限制

YOLOv5官方detect.py仅支持图片/视频文件,但期末答辩需现场调用摄像头。需改造detect.py:

# 在detect.py末尾添加 if __name__ == '__main__': parser = argparse.ArgumentParser() # ...原有参数定义... parser.add_argument('--source', type=str, default='0', help='source') # 默认0=摄像头 opt = parser.parse_args() # 替换原source处理逻辑 source = str(opt.source) if source.isdigit(): # 是摄像头ID cap = cv2.VideoCapture(int(source)) assert cap.isOpened(), f'Cannot open camera {source}' while True: ret, img0 = cap.read() if not ret: break # 调用YOLOv5检测(复用原detect逻辑) img = letterbox(img0, new_shape=opt.img_size)[0] img = img[:, :, ::-1].transpose(2, 0, 1) # BGR to RGB img = np.ascontiguousarray(img) img = torch.from_numpy(img).to(device).float() / 255.0 if img.ndimension() == 3: img = img.unsqueeze(0) pred = model(img, augment=opt.augment)[0] pred = non_max_suppression(pred, opt.conf_thres, opt.iou_thres) # 绘制结果 for i, det in enumerate(pred): # detections per image if len(det): for *xyxy, conf, cls in reversed(det): label = f'{names[int(cls)]} {conf:.2f}' plot_one_box(xyxy, img0, label=label, color=colors[int(cls)], line_thickness=2) cv2.imshow('YOLOv5 Mask Detection', img0) if cv2.waitKey(1) == ord('q'): break cap.release() cv2.destroyAllWindows()
4.1.1 关键适配点
  • letterbox函数需从utils.datasets导入,确保缩放不拉伸;
  • plot_one_box需传入img0(原始BGR图)而非img(归一化tensor),否则显示黑屏;
  • cv2.waitKey(1)设为1ms,保证60FPS流畅,ord('q')退出符合答辩操作习惯。

4.2 结果可视化增强:让老师一眼看懂检测逻辑

默认框线太细(2px),答辩投影时难以辨识。修改utils.plots.plot_one_box:

def plot_one_box(x, im, color=(128, 128, 128), label=None, line_thickness=3): # line_thickness从2→3 # 原有代码... cv2.rectangle(im, c1, c2, color, thickness=line_thickness, lineType=cv2.LINE_AA) if label: tf = max(line_thickness-1, 1) # 字体粗细 w, h = cv2.getTextSize(label, 0, fontScale=line_thickness/3, thickness=tf)[0] # 字体大小随线宽自适应 c2 = c1[0] + w, c1[1] - h - 3 cv2.rectangle(im, c1, c2, color, -1, cv2.LINE_AA) # 填充背景 cv2.putText(im, label, (c1[0], c1[1] - 2), 0, line_thickness/3, [225, 255, 255], thickness=tf, lineType=cv2.LINE_AA)

提示:修改后重新运行detect.py --source 0,摄像头画面将显示加粗边框+高对比度标签,即使教室灯光全开也清晰可辨。

5. 期末交付物清单与答辩话术:让“源码+数据”真正成为你的技术资产

5.1 必交文件结构(教师验收核心项)

按学校模板要求,ZIP包内必须包含以下5类文件,缺一不可:

目录/文件作用检查要点
/docs/README.md项目说明文档需含环境配置命令、训练命令、摄像头演示命令三行可复制代码
/runs/train/xxx/weights/best.pt最佳模型权重文件大小应在13.8MB(YOLOv5s)±0.5MB,过大说明未剪枝
/data/mask.yaml数据集配置train:路径必须为相对路径(如../images/train),禁用绝对路径
/detect_webcam.py摄像头检测脚本文件名明确,不含_backup等冗余后缀
/results/test_results.txt难例集测试报告需包含Precision、Recall、mAP@0.5三行数值

5.2 答辩高频问题应答策略

教师常问:“为什么不用YOLOv8?”、“准确率怎么验证的?”,需用技术事实回应:

  • 关于模型选型:“YOLOv5在PyTorch生态中教程最丰富,其export.py可直接转ONNX,便于后续部署到树莓派(毕设延伸方向),而YOLOv8的Triton部署文档尚不完善。”
  • 关于准确率:“我构建了30张课堂真实难例图(遮挡/背光/小脸),在test_results.txt中召回率0.78,证明模型在实际场景有效;若仅用官网mAP指标,可能因测试集简单而虚高。”
  • 关于数据来源:“所有图像来自公开数据集WIDER Face裁剪+本班同学授权拍摄,标签由3人交叉标注,IoU一致性达0.92(用labelImg的Validate Labels功能计算)。”

5.3 一个让答辩加分的具体技巧:动态置信度滑块

在detect_webcam.py中加入键盘控制,按+/-键实时调节置信度阈值,展示模型鲁棒性:

# 在摄像头循环内添加 conf_threshold = 0.25 while True: # ...检测逻辑... pred = non_max_suppression(pred, conf_threshold, opt.iou_thres) # 使用动态阈值 key = cv2.waitKey(1) & 0xFF if key == ord('+'): conf_threshold = min(0.95, conf_threshold + 0.05) print(f"Confidence threshold: {conf_threshold:.2f}") elif key == ord('-'): conf_threshold = max(0.05, conf_threshold - 0.05) print(f"Confidence threshold: {conf_threshold:.2f}") elif key == ord('q'): break

演示时先设conf=0.25展示高召回(多框),再按+升至0.6展示高精度(少而准),直观体现参数对结果的影响——这比单纯说“我调过参”更有说服力。

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

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

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

立即咨询