ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

YOLOv11+PyQt5实现工地安全帽检测系统,从训练到部署全流程

YOLOv11+PyQt5实现工地安全帽检测系统,从训练到部署全流程 说实话工地安全帽检测这个需求我接触过不少类似的项目。一个施工现场几十路摄像头值班室往往只有一个人盯着看两个小时注意力就崩了漏看是必然的。所以不管是用在智慧工地还是安监巡检一套能自动识别工人是否佩戴安全帽的系统都是刚需。这篇文章把我自己做的一套方案完整拆开YOLOv11负责图像里的目标识别PyQt5负责桌面端界面把整个检测流程串成一套可以直接挂在值班室电脑上跑的程序。文章会从环境配置讲起包括PyQt5安装慢、OpenGL黑屏这类高频问题再到数据集整理、模型训练、图形界面开发、检测结果保存最后聊实测效果和部署边界。每一位想做类似项目、又不想反复踩坑的读者都可以按这个顺序复现。1. 为什么是YOLOv11PyQt5从选型到落地场景1.1 安全帽检测为什么选YOLO系模型YOLO系模型进化到现在已经是非常成熟的工程方案了。从v5开始Ultralytics就把训练、验证、导出的接口统一起来了后面出v8、v11老用户基本可以无缝切换。v11在2024年9月发布相比v8在相同精度下推理更快backbone里用了C3k2模块高层特征部分加入了C2PSA结构对细小目标的特征提取更有优势。安全帽在视频流里往往只占很小一块像素区域这些改进对靶场来说是有实际意义的。我并不是说v5不能用安全帽检测这种类别少、目标体积小、实时性要求高的场景从零开始做新项目直接上v11没毛病。Ultralytics的接口从v5到v11基本一致训练命令换一下权重名就行。双阶段检测器像Faster R-CNN在这种场景下精度上限可能稍微高一点但推理速度完全跟不上视频流需求做桌面客户端很容易卡顿所以在实际项目里我都是直接推单阶段方案。1.2 PyQt5在桌面端方案里的定位界面层我选PyQt5而不是Web方案也不是Tkinter或者PySide6主要基于以下几点考虑。工地值班室这类环境网络不一定稳定本地桌面程序要比浏览器方案更稳妥。PyQt5的控件体系成熟QLabel显示视频帧、QThread做并发处理都是稳定路径而且Qt生态里能搜到的踩坑资料远比PySide6多。PySide6作为Qt官方Python绑定在协议上更友好但教程资料量明显少一截。Tkinter做小工具还行做视频监控这种需要复杂布局和信息刷新的界面开发效率很低。对比项PyQt5PySide6Tkinter授权协议GPL/商用授权LGPL开源教程资料量多中等多视频类界面开发效率高高低常见的踩坑资料丰富较少相对少如果你只是给自己做个内部工具PyQt5的GPL协议问题不大要发布商业闭源软件用PySide6更省心。但对于安全帽检测这类项目绝大多数场景是内部部署PyQt5完全够用。2. 环境配置最踩坑的一段PyQt5安装与OpenGL黑屏排查2.1 安装时间太长的问题出在哪很多人第一次执行pip install pyqt5的时候会觉得这也太慢了。其实不是网络差而是PyQt5的wheel包本身就很大因为它把Qt运行库整个打包进来了。你装的是PyQt5实际下载的是一整套Qt运行时体积轻松超过几十MB。解决办法很简单pip install pyqt5 -i https://pypi.tuna.tsinghua.edu.cn/simple如果用了uv速度会更快uv pip install pyqt55.15.10Python版本建议用3.10或者3.11PyQt5 5.15系列兼容性最好。版本也不用追新5.15.x足够稳定很多网上教程和外卖资料也都是基于这个版本写的。2.2 OpenGL黑屏问题完整排查链路我在自己调试的时候遇到过程序启动完全正常日志没有任何报错窗口也弹出来了但内容区一片黑的情况。网上搜opengl导致pyqt5界面无显示能搜出大量同类问题说明这不是个例。先说一下问题本质。Qt 5.15在Windows上默认走OpenGL渲染如果你的机器显卡驱动不完整或者是远程桌面、虚拟机环境硬件OpenGL支持会被翻车窗口里本该渲染的内容就会黑屏。排查链路如下。第一步写一个最简单的Demo验证渲染后端是否有问题而不是怀疑自己的业务代码import sys from PyQt5.QtWidgets import QApplication, QLabel app QApplication(sys.argv) label QLabel(hello) label.show() sys.exit(app.exec_())如果这个纯文本QLabel窗口也黑屏基本可以确定是Qt渲染后端的问题不是业务逻辑问题。第二步在启动程序前设置环境变量强制走软件渲染export QT_OPENGLsoftware或者在代码里在创建QApplication之前加上这一句from PyQt5.QtCore import Qt from PyQt5.QtWidgets import QApplication QApplication.setAttribute(Qt.AA_UseSoftwareOpenGL, True) app QApplication(sys.argv)注意这个属性必须在QApplication创建之前设置否则不生效。第三步如果黑屏来自QOpenGLWidget可以改成QWidget paintEvent自己绘图。视频监控界面根本用不着OpenGL硬件加速软件渲染完全够用。第四步如果在无头服务器上跑还可以用offscreen平台插件但这属于极端情况一般值班室电脑都用不到。我自己实测下来设置AA_UseSoftwareOpenGL是最省事的方案不影响视频画面的显示质量Frame Rate会有一点点下降但对监控场景完全够用。3. 安全帽数据集从标注规范到小目标增强3.1 数据来源与目录结构做安全帽检测数据集是整个项目的底座。公开数据集SHWDSafety Helmet Wearing Dataset是很多人的起点里面包含七千多张图像标注了佩戴安全帽的人头和未佩戴安全帽的人头。但只靠公开数据还不够因为公开数据里大量是平视视角拍摄的画面工地现场还有俯视、侧逆光、远处小目标等复杂情况。我实际整理数据时会从工地实拍视频里抽帧补充。抽帧不是逐帧抽而是每隔十几帧抽一张避免相邻画面过于重复导致过拟合。整理后的目录结构如下dataset/ ├── images/ │ ├── train/ # 5000张 │ └── val/ # 800张 └── labels/ ├── train/ └── val/每张图像对应一个同名的txt标注文件每行格式是class_id x_center y_center width height坐标值都已经归一化到0到1之间。这是YOLO训练的标准输入格式。如果你拿到的是COCO格式或VOC格式的数据需要先用脚本做一次格式转换。coco2017数据集结构里标注信息都集中在一个json文件里转成YOLO格式时要按图片id把标注拆散到每个txt里去。3.2 类别设计与标注边界类别设计直接影响最终报警逻辑的复杂度。我最开始只标了两类helmet和no_helmet结果发现模型经常把戴帽子的人头判错。后来改成三类person、helmet、head在界面端通过业务逻辑判断——person框内没有helmet也没有head才判定为未佩戴安全帽。三类标注的好处是把人的存在和帽子的存在分开建模模型不需要强行学习有人没戴帽子这个复合概念报警逻辑也更可控。标注时有几个实操小技巧两只眼睛都被遮挡的侧面人头边界往往不清晰框宁大勿小把整个头部区域框进去。安全帽遮挡超过三分之一就不标否则模型会把遮阳帽、头巾误判成安全帽。尽量保证正样本和负样本比例接近不要出现helmet是head数量三倍以上的情况。3.3 小目标检测的数据侧准备安全帽在1920x1080的现场画面里经常只有15x15像素左右这对于目标检测来说是典型的小目标。数据层面的准备有两个方向。第一个方向是训练时用更大的输入分辨率。imgsz640是默认值但安全帽这种小目标场景我会建议把输入分辨率提到1280。相当于把细节放大两倍模型能看到的特征更多。第二个方向是数据增强策略要克制。mosaic、mixup、随机色差这些增强手段在小目标场景里有效但随机旋转和强透视变换慎用。安全帽的形状特征很单一转过15度以上棱线就模糊了模型反而学不到稳定的边缘特征。我自己跑了对比旋转增强控制在10度以内mAP指标更稳定。把数据集整理好之后顺带提一句你以后做占道经营识别、桥墩病害检测这类项目完全可以直接复用这套数据构建思路只是类别数量和目标尺寸略有差异。4. YOLOv11训练参数配置与结构改进的真实收益4.1 训练环境与迁移学习训练环境建议用CUDA 11.8以上的版本显存至少8GB。Ultralytics包一条命令就能跑起来pip install ultralytics预训练权重使用yolov11n.pt或yolov11s.pt。这两个权重都是在COCO数据集上预训练的迁移到安全帽场景后收敛速度会快很多。数据量够大、训练时间充足的话从零开始训练也可以但绝大多数场景下用预训练权重收益更高。自定义数据集对应的yaml配置path: /path/to/dataset train: images/train val: images/val names: 0: person 1: helmet 2: head4.2 关键训练参数与实际调整命令示例yolo detect train \ datasafety_helmet.yaml \ modelyolov11n.pt \ epochs150 \ imgsz1280 \ batch16 \ device0 \ optimizerAdamW \ lr00.001 \ close_mosaic10各个参数的具体作用参数推荐值说明modelyolov11n.pt / yolov11s.ptn更轻s精度更高imgsz1280小目标多时用1280速度优先用640epochs100-200配合早停注意观察过拟合batch8-168G显存/ 32大显存降低batch显存不够时会OOMoptimizerAdamW收敛更稳对新手友好lr00.001AdamW学习率太大会损失爆炸close_mosaic10最后10个epoch关闭mosaic训练过程中要关注两个东西。第一个是训练日志里的loss曲线train_loss持续下降、val_loss先降后升说明已经开始过拟合早停就行。第二个是验证集的mAP50安全帽场景下做到80%以上基本就可以投入使用了。mAP50-95可以看但不用太苛求小目标本来就多50-95这个指标天然就会偏低。4.3 加注意力机制等改进的收益到底有多大热词里很多人问yolov11添加自注意力机制yolov11改进carafe。我拿真实对比数据说话在yolov11n的backbone末端加一个轻量SE注意力模块mAP50大约提升0.5到1.5个百分点但推理速度下降5%到10%。这个收益和数据增强相比非常有限。我的实际建议是动手改结构之前先把以下三件事做完检查数据里小目标样本的占比把imgsz提到1280把训练epoch拉长到150以上观察是否有持续收益这三步做完90%的精度提升空间都已经被榨干了。结构改进是最后一公里的事不是第一站。如果项目对实时性要求高改结构带来的推理速度损失会让你很被动。5. 推理引擎与PyQt5界面线程模型、实时显示与结果保存5.1 把YOLOv11封装成可复用的Detector模型加载不能放在每次推理的函数里那会让推理请求每次都花好几秒重新加载权重。正确做法是封装成类初始化时只加载一次import cv2 from ultralytics import YOLO class Detector: def __init__(self, weights_pathbest.pt, conf0.5, iou0.45, imgsz1280, halfTrue): self.model YOLO(weights_path) self.conf conf self.iou iou self.imgsz imgsz self.half half def infer(self, frame): results self.model.predict( sourceframe, confself.conf, iouself.iou, imgszself.imgsz, halfself.half, verboseFalse ) return results[0]halfTrue是在GPU上开启半精度推理能显著提升每秒能处理的帧数。CPU环境不支持half需要置为False。5.2 Qt界面布局与OpenCV帧显示界面我采用的是左视频、右信息的布局。左侧用QLabel显示检测后的视频画面右侧放统计信息、报警文字和操作按钮。顶部三个按钮打开视频文件、打开摄像头、开始检测。OpenCV的帧是BGR格式Qt显示的格式是RGB中间需要一次转换def cv2_to_qpixmap(frame): rgb_image cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) h, w, ch rgb_image.shape bytes_per_line ch * w qt_image QImage(rgb_image.data, w, h, bytes_per_line, QImage.Format_RGB888) pixmap QPixmap.fromImage(qt_image) return pixmap.scaled(label.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation)界面适配分辨率这个问题也是老生常谈核心原则是布局全部用Qt的layout体系绝对不要用固定坐标定位控件。视频显示区域的尺寸变化由布局自动控制画面缩放时用KeepAspectRatio保持比例就能在1080p和4K屏上正常显示。5.3 多线程模型不卡界面的关键YOLOv11在CPU上推理一帧可能需要几百毫秒如果直接在UI线程跑推理窗口拖动会卡死按钮点了没反应。正确做法是把推理放到单独的QThread里。一个简化版本from PyQt5.QtCore import QThread, pyqtSignal, pyqtSlot class InferenceWorker(QThread): result_ready pyqtSignal(object) def __init__(self, detector, parentNone): super().__init__(parent) self.detector detector self.frame_queue [] self.running True pyqtSlot(object) def process_frame(self, frame): if not self.running: return results self.detector.infer(frame) self.result_ready.emit(results) def stop(self): self.running False self.wait()通过信号槽把推理结果传回主线程主线程只负责刷新UI这样界面就能保持流畅。需要提醒的是process_frame的入参用object类型这样numpy数组可以直接通过信号传递不需要做复杂的序列化转换。5.4 图片、视频和日志的保存方案推理结果的保存是实际工程里很容易被忽略的后半段。很多人问yolov11预测后保存其实要保存的核心内容有三个。保存带检测框的图片cv2.imwrite(fcapture_{timestamp}.jpg, annotated_frame)保存一段检测后的视频用cv2.VideoWriterwriter cv2.VideoWriter( fdetect_{timestamp}.avi, cv2.VideoWriter_fourcc(*XVID), fps, (frame_width, frame_height) ) writer.write(annotated_frame)保存结构化的检测日志用CSV记录每一帧的时间、戴帽人数、未戴帽人数、是否触发告警。日志文件非常有用后续统计某个时间段内的违规趋势直接读CSV就能算。命名规范建议统一用时间戳避免覆盖之前的记录。检测告警图片单独放到告警目录里方便值班人员事后回看。6. 现场实测与调优从指标到边界处理6.1 指标与运行速度参考在我自己整理的安全帽数据集上不同配置的实测结果如下模型imgszmAP50mAP50-95GPU FPSRTX 3060CPU FPSyolov11n6400.8320.50312012-15yolov11n12800.8580.52660-804-6yolov11s12800.8710.55240-502-3这个数据仅供参考具体指标会随现场数据分布浮动。如果在现场部署的摄像头画面和训练集差距较大mAP下降是正常现象不需要慌。6.2 实际部署中的边界情况值班室部署时几个高频问题需要提前处理。误报问题。现场画面里可能有安全帽以外的帽子、头巾、甚至安全帽样式的临时遮挡物。我的处理方式是在报警逻辑里加连续帧确认机制连续三帧检测到未戴帽子的person才触发告警单帧误判不会导致报警。光线问题。逆光、傍晚光线不足时未戴帽子的人头和小安全帽的特征都会被削弱。可以在送入模型前对图像做一次自适应直方图均衡化或者针对暗部区域增强对比度。这个处理在推理线程里做不要放在UI主线程。夜间监控问题。红外摄像头下安全帽颜色信息基本丢失只能靠形状和纹理判断精度会明显下降。有条件的话优先选择带白光补光的相机没有的话就要接受夜间精度低于白天的现实。摄像头断流问题。现场摄像头偶尔会掉线PyQt5界面要捕获读取失败异常在界面上显示断流提示而不是让程序直接崩溃。整个项目做下来最大的体会是安全帽检测真正的难点不在模型本身而在工程整合。数据、训练、界面、线程、保存每一个环节都有各自的坑。这也是我为什么花了大篇幅写环境坑和线程模型的原因。如果你刚开始做类似的桌面检测系统建议先用一个内嵌的视频文件跑通全部流程再切换到实时摄像头画面这样排查问题时能省很多时间。
返回列表