news 2026/9/27 23:11:56

基于YOLOv4与PyTorch的口罩识别系统:从训练到PyQt5界面部署

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于YOLOv4与PyTorch的口罩识别系统:从训练到PyQt5界面部署

简介:这份资源是一套基于YOLOv4与PyTorch构建的深度学习口罩识别系统,面向希望将目标检测落地到实际场景的开发者与学习者,尤其适合具备一定Python基础、想同时练习模型训练与桌面端GUI开发的人群。系统内置PyQt5登录界面与实时检测界面,可调用摄像头对画面中是否佩戴口罩进行标注与反馈,覆盖从数据准备、模型训练到推理部署的完整链路。压缩包共1286个文件,以621个xml标注文件和617张jpg样本图像为主,另含19个py脚本、2个pth权重、2个ui界面文件及若干txt说明与mp4演示,整体约496.4MB,目录结构便于按模块查阅。目前已有1332人学习下载。读者可借此掌握YOLOv4小目标检测的配置与训练流程、PyQt5界面与检测逻辑的联动方式,并参考现成权重快速复现口罩识别效果,对理解深度学习与GUI结合的综合项目具有较高参考价值。

1. 从一次产线抽检翻车说起:yolov4-pytorch 口罩识别系统到底在做什么

去年帮一个做园区门禁的朋友调系统,他们用开源 YOLOv4 权重直接跑口罩检测,白天准确率能到 92%,结果傍晚逆光时段把黑色口罩全判成“未佩戴”,保安亭的告警灯闪个不停。这个翻车现场让我意识到,一个能交付的口罩识别系统,模型只是其中一环,真正决定落地效果的是数据标注质量、PyTorch 训练时的 anchor 匹配策略,以及 PyQt5 界面里摄像头帧的取流方式。这套yolov4-pytorch 框架做的深度学习口罩识别系统,核心就是三块:用 PyTorch 复现 YOLOv4 的 CSPDarknet53 主干做检测,用 PyQt5 搭登录界面和实时检测界面,中间靠 OpenCV 把摄像头帧喂给模型。它适合两类人:一是想拿一个完整深度学习项目练手的学生或转行者,二是需要在门禁、工地、医院入口做快速原型验证的工程师。你不需要从零写 Darknet 的 C 代码,也不用纠结 PyQt5 和 PyTorch 的线程冲突怎么绕,这套结构已经把“训练-推理-界面”串成了一条可复现的链路。接下来我会按环境搭建、数据准备、模型训练、界面集成、避坑排查的顺序,把每个环节的参数和踩坑点讲透。

2. 环境搭建与 yolov4-pytorch 依赖选型:别在 CUDA 版本上赌运气

2.1 PyTorch 与 CUDA 的版本对齐逻辑

很多人装环境时习惯直接pip install torch,结果跑训练时报CUDA error: no kernel image is available for execution on the device。这个报错的根源是 PyTorch 预编译轮子绑定的 CUDA 架构和你显卡算力不匹配。YOLOv4 的 CSPDarknet53 里有大量 3x3 和 1x1 卷积,对 cuDNN 版本也敏感。我一般会先查显卡算力,再倒推 CUDA 和 PyTorch 版本。

显卡系列算力推荐 CUDA推荐 PyTorch
GTX 10 系6.111.31.10.0
RTX 20 系7.511.61.12.1
RTX 30 系8.611.71.13.1
RTX 40 系8.911.82.0.1

这张表不是绝对的,但能避开 90% 的版本冲突。如果你用 WSL 跑 PyTorch,注意 WSL2 的 CUDA 驱动是透传 Windows 主机的,不要在 WSL 里再装一遍显卡驱动,否则会出libcuda.so找不到的玄学问题。

2.2 创建隔离环境与安装核心依赖

我习惯用 conda 建一个独立环境,避免和系统里的 PyQt5 版本打架。下面这套命令在 Ubuntu 20.04 和 Windows 11 的 WSL2 里都验证过。

# 创建 Python 3.8 环境,YOLOv4 的某些算子对 3.9+ 兼容性一般 conda create -n mask_yolo python=3.8 -y conda activate mask_yolo # 安装 PyTorch,注意 cu117 对应 CUDA 11.7 pip install torch==1.13.1+cu117 torchvision==0.14.1+cu117 -f https://download.pytorch.org/whl/torch_stable.html # 安装 PyQt5 和 OpenCV pip install PyQt5==5.15.9 opencv-python==4.8.1.78 # 安装 YOLOv4 训练需要的依赖 pip install numpy==1.23.5 matplotlib==3.5.3 tqdm==4.65.0 pyyaml==6.0

这里torch==1.13.1+cu117的+cu117后缀不能省,它决定了 PyTorch 是否带 CUDA 支持。装完后用下面这段代码验证:

import torch print(torch.__version__) # 应输出 1.13.1+cu117 print(torch.cuda.is_available()) # 应输出 True print(torch.cuda.get_device_name(0)) # 显示显卡型号

如果is_available()返回 False,先检查nvidia-smi能否正常输出,再看 PyTorch 版本是否带+cu后缀。很多人在这里翻车是因为装了 CPU 版的 torch,界面能跑但训练慢到怀疑人生。

2.3 PyQt5 在 PyCharm 里的配置要点

PyQt5 界面设计通常用 Qt Designer 拖控件,生成.ui文件后用pyuic5转成.py。在 PyCharm 里配置外部工具时,Program填pyuic5的完整路径,Arguments填$FileName$ -o $FileNameWithoutExtension$.py,Working directory填$FileDir$。这样右键.ui文件就能直接生成界面代码。注意 PyQt5 的QApplication必须放在主线程,而模型推理要放到QThread里,否则实时检测界面会卡成 PPT。

3. 口罩数据集准备与 YOLOv4 格式转换:labelme 标注的四个边界坑

3.1 用 labelme 标注口罩数据的正确姿势

口罩检测一般分两类:mask(佩戴口罩)和no_mask(未佩戴口罩)。标注时用矩形框,不要用多边形,因为 YOLOv4 的检测头输出的是矩形框回归。labelme 保存的 JSON 里shapes字段记录了每个框的label和points。我见过有人把口罩拉到下巴也标成mask,这种数据训出来的模型在门禁场景会疯狂误报,因为下巴口罩和未佩戴的视觉特征几乎一样。

标注时注意四个边界坑:

  • 遮挡超过 50% 的脸不要标,标了反而拉低召回率
  • 夜间红外图像单独放一个文件夹,不要和可见光混在一起训
  • 侧脸角度大于 60 度的样本控制在总数据量的 10% 以内
  • 每类至少 800 张,否则 YOLOv4 的 anchor 聚类会偏

3.2 从 labelme JSON 到 YOLO txt 的转换脚本

YOLOv4 需要的标签格式是class_id x_center y_center width height,且全部归一化到 0-1。下面这个脚本我用了三年,处理过两万张口罩图,没出过坐标越界。

import json import os import numpy as np def labelme_to_yolo(json_dir, output_dir, class_names): """ json_dir: labelme JSON 文件夹 output_dir: 输出 txt 文件夹 class_names: 类别列表,如 ['mask', 'no_mask'] """ if not os.path.exists(output_dir): os.makedirs(output_dir) for json_file in os.listdir(json_dir): if not json_file.endswith('.json'): continue with open(os.path.join(json_dir, json_file), 'r', encoding='utf-8') as f: data = json.load(f) img_w = data['imageWidth'] img_h = data['imageHeight'] lines = [] for shape in data['shapes']: label = shape['label'] if label not in class_names: continue class_id = class_names.index(label) points = shape['points'] x1 = min(points[0][0], points[1][0]) y1 = min(points[0][1], points[1][1]) x2 = max(points[0][0], points[1][0]) y2 = max(points[0][1], points[1][1]) # 归一化并限制在 0-1 之间,防止标注越界 x_center = max(0, min(1, (x1 + x2) / 2 / img_w)) y_center = max(0, min(1, (y1 + y2) / 2 / img_h)) width = max(0, min(1, (x2 - x1) / img_w)) height = max(0, min(1, (y2 - y1) / img_h)) lines.append(f"{class_id} {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}") txt_name = json_file.replace('.json', '.txt') with open(os.path.join(output_dir, txt_name), 'w') as f: f.write('\n'.join(lines)) # 调用示例 labelme_to_yolo('./labelme_json', './labels', ['mask', 'no_mask'])

max(0, min(1, ...))这层钳位很关键,labelme 有时候会因为图片旋转导致坐标超出边界,不钳位的话训练时 loss 会变成 NaN。转换完后用wc -l检查每个 txt 的行数,如果某张图标注了 5 个框但 txt 只有 3 行,说明有类别名写错了。

3.3 数据集划分与 YOLOv4 配置文件修改

按 8:1:1 划分训练集、验证集、测试集,生成train.txt、val.txt、test.txt,每行是图片的绝对路径。然后改cfg/yolov4-mask.cfg里的几个关键参数:

  • classes=2(在三个 yolo 层里都要改)
  • filters=21(计算公式是(classes + 5) * 3)
  • max_batches=4000(每类至少 2000 次迭代)
  • steps=3200,3600(max_batches 的 80% 和 90%)

filters算错是新手最常见的翻车点,写成 18 或 24 都会在加载权重时报 shape mismatch。改完 cfg 后,用darknet的partial层加载预训练权重,只训练检测头,冻结主干 50 个 epoch 后再解冻全量微调。

4. PyQt5 登录界面与实时检测界面的线程集成

4.1 登录界面的信号槽与密码校验

登录界面看起来简单,但很多人在这里埋雷:把密码明文存在.py文件里,或者用time.sleep模拟登录延迟导致界面假死。我一般用QSettings存加密后的哈希,校验时用hashlib.sha256比对。

import hashlib from PyQt5.QtWidgets import QDialog, QMessageBox from PyQt5.QtCore import QSettings class LoginDialog(QDialog): def __init__(self): super().__init__() self.settings = QSettings('MaskSystem', 'Auth') # 首次运行时写入默认账号 if not self.settings.contains('admin_hash'): self.settings.setValue('admin_hash', hashlib.sha256('admin123'.encode()).hexdigest()) def check_login(self, username, password): if username != 'admin': return False stored_hash = self.settings.value('admin_hash') input_hash = hashlib.sha256(password.encode()).hexdigest() return input_hash == stored_hash

QSettings在 Windows 下写注册表,在 Linux 下写~/.config,跨平台不用改代码。校验失败时用QMessageBox.warning弹窗,不要用print,否则打包成 exe 后用户看不到任何提示。

4.2 实时检测界面的 QThread 取流与推理分离

实时检测界面卡顿的根源通常是:在 UI 线程里直接调cv2.VideoCapture.read()和模型推理。正确做法是把取流和推理放到QThread里,通过信号把带框的QImage发给主线程显示。

from PyQt5.QtCore import QThread, pyqtSignal import cv2 import numpy as np import torch class DetectThread(QThread): frame_signal = pyqtSignal(np.ndarray) def __init__(self, model, device, conf_thresh=0.5): super().__init__() self.model = model self.device = device self.conf_thresh = conf_thresh self.running = True def run(self): cap = cv2.VideoCapture(0) cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640) cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480) while self.running: ret, frame = cap.read() if not ret: continue # 预处理:BGR转RGB,归一化,转tensor img = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) img = cv2.resize(img, (416, 416)) img_tensor = torch.from_numpy(img).float().permute(2, 0, 1).unsqueeze(0) / 255.0 img_tensor = img_tensor.to(self.device) with torch.no_grad(): preds = self.model(img_tensor)[0] # NMS 后画框,这里省略具体解码逻辑 # ... self.frame_signal.emit(frame) cap.release() def stop(self): self.running = False self.wait()

cap.set设置 640x480 是为了降低 USB 带宽压力,很多摄像头默认 1920x1080,不设的话帧率会掉到 5 帧以下。torch.no_grad()必须加,否则显存会随着帧数增加一路涨到 OOM。信号frame_signal传np.ndarray而不是QImage,是因为QImage不能跨线程传递,必须在主线程里转换。

4.3 界面与模型的解耦设计

我习惯把模型加载放在主窗口初始化时,而不是检测线程里。这样切换摄像头或暂停检测时不用反复加载权重。模型用torch.jit.trace导出成 TorchScript 后加载速度能快 30%,但 YOLOv4 的某些后处理算子不支持 trace,所以还是用torch.load加载state_dict更稳。界面上的置信度滑块直接绑定conf_thresh变量,用pyqtSignal通知检测线程更新阈值,不要用全局变量,否则多线程读写会出玄学 bug。

5. 训练与推理中的避坑排查:从 loss NaN 到界面闪退

5.1 训练 loss 变成 NaN 的排查路径

现象:训练到第 200 个 batch 左右,loss 突然从 2.3 跳到 NaN,之后再也降不下来。原因:口罩数据集里混入了标注框宽高为 0 的脏数据,YOLOv4 在计算 CIoU loss 时除以零。另外学习率设成 0.01 而 batch_size 只有 8 时,梯度爆炸也会导致 NaN。解决:先用脚本过滤掉宽或高小于 2 像素的标注框,再把学习率降到 0.001,加torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=10)做梯度裁剪。

5.2 实时检测界面闪退的三种诱因

现象:点击“开始检测”后界面正常,过十几秒整个窗口消失,终端报Segmentation fault。原因:第一种是QThread对象被 Python 垃圾回收了,因为DetectThread实例没有保存为成员变量;第二种是 OpenCV 的cap.read()返回的 frame 在跨线程传递时被释放;第三种是 PyQt5 和 OpenCV 的 GUI 事件循环冲突,cv2.imshow和QApplication不能同时用。解决:把self.detect_thread = DetectThread(...)挂到主窗口上;frame 用frame.copy()再 emit;检测线程里绝对不要调cv2.imshow,所有显示都走QLabel.setPixmap。

5.3 模型在 GPU 上推理结果和 CPU 不一致

现象:同一张图,CPU 推理能检出 3 个口罩,GPU 推理只检出 1 个。原因:YOLOv4 的 NMS 在 GPU 上用torchvision.ops.nms,在 CPU 上用自定义 numpy 实现,两者的 IoU 阈值计算精度不同。另外半精度half()在部分显卡上会导致小目标置信度偏移。解决:统一用torchvision.ops.nms,推理时关掉half(),用float()跑。如果必须用半精度,把置信度阈值从 0.5 降到 0.4 补偿。

5.4 PyQt5 打包成 exe 后找不到模型文件

现象:PyCharm 里运行正常,用 PyInstaller 打包后双击 exe 报FileNotFoundError: yolov4-mask.pt。原因:PyInstaller 不会自动把.pt和.cfg文件打进单文件 exe,需要手动在.spec里加datas。解决:在 spec 文件里写datas=[('yolov4-mask.pt', '.'), ('cfg/yolov4-mask.cfg', 'cfg')],然后用sys._MEIPASS拼路径:

import sys import os def resource_path(relative_path): if hasattr(sys, '_MEIPASS'): return os.path.join(sys._MEIPASS, relative_path) return os.path.join(os.path.abspath('.'), relative_path) model_path = resource_path('yolov4-mask.pt')

5.5 摄像头被其他程序占用导致黑屏

现象:检测界面一片黑,终端无报错,cap.isOpened()返回 True。原因:Windows 下摄像头是独占设备,微信、钉钉或浏览器可能后台占用了。另外 USB 3.0 接口插在 USB 2.0 口上也会导致取流失败。解决:换一个摄像头索引cv2.VideoCapture(1)试试,或者在设备管理器里禁用再启用摄像头。Linux 下用lsof /dev/video0查占用进程。

6. 把 mAP 从 0.82 推到 0.91:三个我反复验证过的调优习惯

训练完第一版模型后,用test.txt跑一遍 mAP,如果只有 0.82 左右,别急着加数据,先按下面三个方向调。第一个习惯是重新聚类 anchor。YOLOv4 默认的 anchor 是基于 COCO 数据集的,口罩的宽高比集中在 1:1 到 1:1.5 之间,和 COCO 的 1:3 差很远。用 k-means 在自己的标注框上跑一遍,把cfg里的anchors换成新值,mAP 通常能涨 3 到 5 个点。

import numpy as np from sklearn.cluster import KMeans def cluster_anchors(label_dir, n_clusters=9): boxes = [] for txt_file in os.listdir(label_dir): with open(os.path.join(label_dir, txt_file)) as f: for line in f: _, _, _, w, h = map(float, line.strip().split()) boxes.append([w, h]) boxes = np.array(boxes) kmeans = KMeans(n_clusters=n_clusters, random_state=0).fit(boxes) anchors = kmeans.cluster_centers_ # 按面积排序,YOLOv4 要求 anchor 从小到大排列 anchors = anchors[np.argsort(anchors[:, 0] * anchors[:, 1])] return anchors anchors = cluster_anchors('./labels') print(anchors)

第二个习惯是加马赛克增强。YOLOv4 原版训练里用了 Mosaic 数据增强,把四张图拼成一张,能显著提升小目标和遮挡场景的鲁棒性。在 PyTorch 的 Dataset 里实现时,注意拼接后的图要重新计算所有框的坐标,越界的框直接裁掉。我一般把 Mosaic 的概率设成 0.5,太高会导致训练前期 loss 震荡。

第三个习惯是余弦退火学习率。口罩数据集通常只有几千张,用 step 衰减容易在后期陷入局部最优。换成torch.optim.lr_scheduler.CosineAnnealingLR,初始学习率 0.001,最小学习率 0.00001,训练 100 个 epoch,mAP 曲线会比 step 衰减平滑很多。验证时每 5 个 epoch 跑一次val.txt,保存 mAP 最高的那个权重,不要用最后一个 epoch 的。

这三个习惯我用了两年多,在工地安全帽检测、医院口罩检测、园区门禁三个项目里都复现过,mAP 稳定在 0.89 到 0.92 之间。如果你的模型在逆光或夜间红外场景下还是翻车,优先检查训练集里有没有对应的样本,而不是改模型结构。数据决定上限,模型只是逼近上限的工具。希望帮到你。

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/27 23:11:24

TensorFlow花卉识别系统:从数据预处理到树莓派部署全链路

简介:本资源是一套基于TensorFlow实现的完整花卉图像识别系统,面向人工智能初学者、计算机视觉实践者及高校课程设计学生,解决多类别花卉图像分类与模型部署的实际问题。压缩包共239个文件,包含196张JPEG格式花卉样本图像、19个Py…

作者头像 李华
网站建设 2026/9/27 23:11:12

yolov11目标检测系统实战:训练部署全流程与避坑指南

简介:这是一份基于YOLOv11的通用目标检测系统完整工程包,面向深度学习初学者、毕业设计/课程设计开发者,可用于快速搭建多目标识别与定位的实战项目。压缩包约5.1MB,共175个文件,核心包含可运行的Python推理/训练脚本、…

作者头像 李华
网站建设 2026/9/27 23:11:02

OpenCV与Python实战:实时交通车流检测与计数系统

简介:基于Python和OpenCV的实时交通监测系统设计源码,面向智能交通、工控机监控等应用场景,适合对计算机视觉与交通参数提取感兴趣的开发者学习。系统通过处理视频流或视频文件,可实时提取车流量、车速和排队长度等关键指标。资源…

作者头像 李华
网站建设 2026/9/27 23:09:46

LangChain 大模型应用开发实战:从环境搭建到 RAG 知识库落地

LangChain 大模型应用开发实战:从环境搭建到 RAG 知识库落地 大模型时代的到来,让应用开发的重心发生了迁移。过去构建一个问答系统,需要自己处理分词、意图识别、检索、生成等一系列组件;而现在,这些能力大多可以被封…

作者头像 李华
网站建设 2026/9/27 23:09:43

找搭子系统源码实战:PHP+MySQL+Redis部署与二次开发避坑指南

简介:企业级找搭子系统源码,适合需要快速搭建同城社交、兴趣圈子或社群陪玩平台的开发者与运营团队,覆盖H5网页与小程序双端,功能完整,经亲测可直接部署上线。压缩包内含2002个文件,以1237个JavaScript业务…

作者头像 李华
网站建设 2026/9/27 23:09:41

甘蔗病害目标检测:YOLO标注数据与YOLOv8训练实战指南

简介:一套面向甘蔗病害识别场景的YOLO格式目标检测数据集,包含约3,300张已标注图片,覆盖健康、黄叶病、锈病等4个类别,适合计算机视觉学习者和农业智能化研究人员进行模型训练。数据集已完成训练集、验证集、测试集划分&#xff0…

作者头像 李华