资讯动态

YOLOv5口罩检测系统:PyQt5本地化部署与工程化实践

发布时间:2026/9/11 20:56:49 来源:尧图企业网站定制
简介本资源是一个基于YOLOv5与PyQt5开发的完整口罩检测系统面向计算机视觉初学者、深度学习实践者及防疫类智能应用开发者解决图像、视频及摄像头实时场景下的佩戴口罩识别问题适用于课堂演示、课程设计、小型安防监控等实际场景。压缩包共150个文件含44个配置与模型定义用的yaml文件、40个核心逻辑与GUI实现的py脚本、3个训练好的.pt模型权重以及jpg/jpeg/png格式的测试图像和logo等界面素材另有sh部署脚本、md说明文档及ipynb实验笔记整体大小为139.86MB。已有1501人学习下载。用户可直接运行主程序启动图形界面支持图片上传、视频导入与USB/网络摄像头实时检测并获得带边界框与置信度标注的可视化结果项目结构清晰包含数据预处理、模型微调、GUI封装、多源输入适配等完整流程附带Dockerfile与setup.cfg便于环境复现与二次开发。1. 这不是又一个YOLOV5 demo它把口罩检测从命令行拽进真实工作流你刚在终端里跑通detect.py看到控制台刷出一堆mask: 0.92但下一秒就卡在「怎么让非技术人员也用得上」——这才是口罩检测落地真正的断点。这个基于YOLOV5的系统核心价值不在模型精度本身而在于用PyQt5把检测能力封装成可即点即用的本地应用支持拖入任意图片、加载本地视频文件、调用笔记本摄像头实时推理所有操作都在一个无黑窗、无报错弹窗、结果带框置信度标注的界面里完成。它不依赖Web服务或云API全部计算在本地GPU/CPU完成也不要求用户懂--weights参数含义而是把conf_thres0.5、iou_thres0.45这些超参数藏进「高级设置」折叠面板里。适合防疫管理人员快速部署到办公电脑也适合作为计算机视觉课程中「模型工程化封装」的完整范例——从.pt模型加载、OpenCV帧处理、PyQt5信号槽调度到多线程防界面冻结每一步都暴露在源码里。2. YOLOV5模型轻量化与口罩数据集微调实战2.1 为什么选YOLOV5s而非YOLOV5x精度与延迟的硬平衡口罩检测场景有其特殊性目标尺度小人脸区域仅占画面5%~15%、遮挡频繁头发/眼镜/口罩边缘模糊、背景干扰强办公室/地铁站/医院走廊。直接使用COCO预训练权重会导致漏检率高。我们实测发现YOLOV5s在640×480输入下单帧推理耗时约32msRTX3060mAP0.5达0.87而YOLOV5x虽提升至0.91但耗时翻倍至78ms且显存占用超3.2GB无法在4GB显存设备上稳定运行。因此项目默认采用yolov5s.pt作为基础权重通过以下三步微调提示不要跳过--img 640参数。口罩目标尺寸集中于120×80像素左右输入分辨率过低如320会丢失鼻梁关键特征过高如1280则增加冗余计算且易受光照噪声影响。2.1.1 数据集构建标注规范决定模型上限本项目使用的口罩数据集包含3276张图像按train:val:test 7:2:1划分。关键标注规则必须严格执行人脸框必须紧贴面部轮廓上边界对齐发际线下边界覆盖下巴尖左右边界卡住耳前点非耳朵本身口罩标签分两类mask正确佩戴覆盖口鼻、no_mask未佩戴或佩戴不规范如仅遮口或下滑露鼻强制排除模糊样本运动模糊导致边缘不可辨识的图像直接剔除不打标签# 使用labelImg生成PASCAL VOC格式后转换为YOLOV5要求的txt格式 python datasets/convert_voc_to_yolo.py \ --voc-root ./datasets/mask_voc \ --yolo-root ./datasets/mask_yolo \ --classes mask,no_mask该脚本将VOC的XML标注转为每图一行的class_id center_x center_y width height归一化坐标并自动创建train.txt/val.txt路径列表。注意center_x和width需除以图像原始宽度center_y和height除以原始高度——这是YOLOV5训练器唯一接受的格式。2.1.2 超参数调优针对小目标的Anchor重聚类YOLOV5默认Anchor基于COCO数据集聚类生成对口罩这类小目标泛化差。我们使用utils/autoanchor.py重新聚类# 在train.py同级目录执行 from utils.autoanchor import check_anchors from models.yolo import Model model Model(models/yolov5s.yaml, ch3, nc2) # nc2表示mask/no_mask两类 check_anchors(dataset./datasets/mask_yolo/train.txt, modelmodel, thr0.95, # IoU阈值越高越严格 imgsz640)输出新Anchor[10,13, 16,30, 33,23, 30,61, 62,45, 59,119, 116,90, 156,198, 373,326]将其写入models/yolov5s.yaml的anchors:字段并在训练命令中指定python train.py \ --data ./datasets/mask_yolo/data.yaml \ --cfg ./models/yolov5s.yaml \ --weights ./weights/yolov5s.pt \ --epochs 150 \ --batch-size 32 \ --img 640 \ --name mask_yolov5s_v2 \ --cache # 启用缓存加速读取注意--cache参数在首次运行时会将所有训练图像预处理并存入内存映射文件后续训练启动快3倍但会占用额外12GB磁盘空间。若显存不足可改用--cache disk存到SSD。3. PyQt5界面架构设计与多线程防阻塞实现3.1 主窗口布局QStackedWidget实现功能模块隔离整个GUI采用QStackedWidget管理三个核心视图图片检测页、视频检测页、摄像头页。每个页面继承自QWidget并独立实现setup_ui()方法避免逻辑耦合。关键设计点图片页使用QGraphicsView替代QLabel显示原图与检测结果。原因QLabel缩放时失真严重而QGraphicsView支持平滑缩放、滚轮缩放、拖拽移动且能叠加多个QGraphicsRectItem绘制检测框。视频页QTimer定时触发update_frame()但绝不直接在主线程调用模型推理。否则视频播放会卡顿甚至崩溃。摄像头页使用cv2.VideoCapture(0)获取帧但通过QThread子类CameraWorker在后台线程持续采集主线程只负责接收信号更新UI。3.1.1 多线程安全机制QThread Signal/Slot通信模型推理是CPU/GPU密集型任务必须剥离出主线程。我们定义DetectionWorker类# workers/detection_worker.py from PyQt5.QtCore import QThread, pyqtSignal import torch from models.experimental import attempt_load from utils.general import non_max_suppression class DetectionWorker(QThread): result_ready pyqtSignal(object, list) # (original_img, detections) def __init__(self, weights_path, conf_thres0.5, iou_thres0.45): super().__init__() self.weights_path weights_path self.conf_thres conf_thres self.iou_thres iou_thres self.model None self.device torch.device(cuda if torch.cuda.is_available() else cpu) def run(self): # 模型加载放在run()中确保在子线程执行 self.model attempt_load(self.weights_path, map_locationself.device) self.model.half() if self.device.type cuda else None # 推理逻辑简化版 img_tensor preprocess_image(self.current_img) # 自定义预处理函数 pred self.model(img_tensor.half() if self.device.type cuda else img_tensor)[0] detections non_max_suppression(pred, self.conf_thres, self.iou_thres) self.result_ready.emit(self.current_img, detections.tolist())在主窗口中连接信号# main_window.py self.detector DetectionWorker(./weights/best_mask.pt) self.detector.result_ready.connect(self.on_detection_finished) self.detector.start() # 当需要检测时 self.detector.current_img cv2.imread(input.jpg) # 注意不能直接调用self.detector.run()必须用start()提示QThread子类中禁止在__init__里加载模型。因为__init__在主线程执行模型加载会阻塞UI。必须在run()中加载且run()由start()触发在新线程中运行。3.1.2 实时性能优化帧率控制与结果缓存摄像头检测时若每帧都送入模型RTX3060下实际帧率仅8fps远低于30fps采集帧率导致画面卡顿。解决方案是动态跳帧# camera_worker.py def run(self): cap cv2.VideoCapture(0) frame_count 0 while self.running: ret, frame cap.read() if not ret: break frame_count 1 # 每3帧处理1帧保证UI流畅 if frame_count % 3 0: # 发送帧给检测线程此处用队列或信号 self.frame_ready.emit(frame.copy()) # .copy()避免内存冲突 cap.release()同时在检测结果绘制时启用QPainter.setRenderHint(QPainter.Antialiasing)消除矩形框锯齿并用QFont.setPointSize(10)统一标注字体大小避免不同分辨率下文字溢出。4. 图片/视频/摄像头三模式检测的参数配置与结果解析4.1 统一检测入口DetectEngine类封装核心逻辑为避免重复代码我们抽象出DetectEngine类统一处理三种输入源的预处理与后处理# core/detect_engine.py class DetectEngine: def __init__(self, weights_path, devicecuda): self.model attempt_load(weights_path, map_locationdevice) self.device torch.device(device) self.names self.model.module.names if hasattr(self.model, module) else self.model.names def detect_image(self, img_path, conf_thres0.5): img cv2.imread(img_path) img_rgb cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # 预处理resize normalize add batch dim img_tensor torch.from_numpy(img_rgb).permute(2,0,1).float().div(255.0).unsqueeze(0) img_tensor img_tensor.to(self.device) pred self.model(img_tensor)[0] detections non_max_suppression(pred, conf_thres, 0.45)[0].cpu().numpy() return img, detections # 返回原图和[N,6]数组x1,y1,x2,y2,conf,class_id def detect_video(self, video_path, conf_thres0.5, skip_frames2): cap cv2.VideoCapture(video_path) results [] frame_idx 0 while cap.isOpened(): ret, frame cap.read() if not ret: break if frame_idx % skip_frames 0: _, det self.detect_image_from_array(frame, conf_thres) results.append((frame_idx, det)) frame_idx 1 cap.release() return results4.1.1 参数配置表三模式下的关键参数差异检测模式推荐conf_thresskip_frames输入尺寸输出要求典型耗时RTX3060图片检测0.50-640×480单次输出高精度框42ms/frame视频检测0.451每2帧处理1帧640×480逐帧结果保存带框视频38ms/frame平均摄像头检测0.402每3帧处理1帧640×480实时渲染低延迟优先35ms/frame平均注意conf_thres越低召回率越高但误检增多。摄像头模式设为0.40是因为环境光线变化大需容忍部分低置信度检测图片模式设为0.50因输入质量高可严控误报。4.1.2 结果解析从[N,6]数组到可视化标注YOLOV5输出的detections是(N,6)数组列顺序为[x1,y1,x2,y2,confidence,class_id]。绘制时需注意坐标是归一化后的绝对像素值非相对坐标可直接用于cv2.rectangleclass_id0对应maskclass_id1对应no_mask颜色需区分如绿色/红色置信度文本位置在框左上角外侧2像素处避免遮挡def draw_detections(img, detections, names[mask,no_mask], colors[(0,255,0),(0,0,255)]): for *xyxy, conf, cls in detections: x1, y1, x2, y2 map(int, xyxy) label f{names[int(cls)]} {conf:.2f} color colors[int(cls)] cv2.rectangle(img, (x1,y1), (x2,y2), color, 2) cv2.putText(img, label, (x1, y1-5), cv2.FONT_HERSHEY_SIMPLEX, 0.6, color, 2) return img该函数返回BGR格式图像供PyQt5的QImage转换使用。特别注意cv2.putText的字体大小0.6和线宽2在640×480分辨率下最清晰放大到全屏时需按比例缩放。5. 模型部署验证与常见故障排查技巧5.1 三步验证法确认模型真正可用而非仅加载成功很多用户反馈「界面能打开但检测框永远不出现」问题往往出在模型与数据预处理不匹配。按此顺序逐项验证权重文件完整性检查运行python models/yolo.py --weights ./weights/best_mask.pt --data ./datasets/mask_yolo/data.yaml观察是否报错KeyError: nc。若报错说明权重文件的nc类别数与data.yaml中nc: 2不一致需重新训练或修改权重头信息。输入通道验证在DetectEngine.detect_image()中插入调试代码print(fInput shape: {img_tensor.shape}) # 应为 [1,3,480,640] print(fPixel range: {img_tensor.min().item():.3f} ~ {img_tensor.max().item():.3f}) # 应为 0.0 ~ 1.0若范围异常如-1.0~1.0说明归一化参数错误需检查img_tensor.div(255.0)是否遗漏。后处理阈值穿透测试临时将conf_thres设为0.01运行一张已知含口罩的图片。若仍无框说明模型根本没输出若有大量杂乱小框则是NMS参数iou_thres过低应≥0.3。5.1.1 典型报错与修复方案速查表报错信息根本原因修复命令/操作ModuleNotFoundError: No module named torchPyTorch未安装或CUDA版本不匹配pip3 install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118根据显卡驱动选cu118/cu121cv2.error: OpenCV(4.8.0) ... error: (-215:Assertion failed) ... size.width0 size.height0图片路径含中文或空格OpenCV无法读取将图片移至纯英文路径或改用PIL.Image.open()读取后转cv2.cvtColor(np.array(img), cv2.COLOR_RGB2BGR)QPixmap: Must construct a QGuiApplication before a QPixmapPyQt5在无GUI环境如SSH下运行添加import os; os.environ[QT_QPA_PLATFORM] offscreen到main.py顶部RuntimeError: CUDA out of memory批处理尺寸过大或显存被其他进程占用在train.py中添加--batch-size 16或任务管理器结束python.exe进程释放显存5.2 摄像头权限失效的静默处理Windows下USB摄像头常因权限问题返回cap.isOpened()False但OpenCV不抛异常。我们在CameraWorker.run()中加入主动探测def run(self): for dev_id in range(10): # 尝试0~9号设备 cap cv2.VideoCapture(dev_id) if cap.isOpened(): ret, _ cap.read() if ret: # 能读到帧才认为有效 self.active_device dev_id break cap.release() if not hasattr(self, active_device): self.error.emit(未检测到可用摄像头请检查设备连接与权限) return # 后续正常采集...此逻辑绕过Windows的「假设备」陷阱如虚拟摄像头驱动残留直接用cap.read()验证硬件有效性。提示Linux用户需将当前用户加入video组sudo usermod -aG video $USER然后重启生效。否则cv2.VideoCapture(0)始终返回False。最后当检测框在摄像头画面中抖动时不要急着调conf_thres——先检查cv2.VideoCapture的set(cv2.CAP_PROP_FPS, 30)是否生效用cap.get(cv2.CAP_PROP_FPS)打印实际帧率。多数笔记本摄像头硬件限制为15fps强行设30会导致驱动丢帧引发检测结果跳变。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价