ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

基于YOLOv8与PyQt5的头盔佩戴检测系统开发实践

基于YOLOv8与PyQt5的头盔佩戴检测系统开发实践 近年来随着城市非机动车出行数量持续上升骑乘电动自行车不佩戴头盔的现象仍然比较常见。传统的人工检查和监控抽查方式效率有限很难做到全天候、全路口的实时提醒与记录。借助计算机视觉和深度学习目标检测技术我们可以训练一个能够自动识别“是否佩戴头盔”的检测模型再结合桌面端 GUI 工具打包成一套完整的小型应用系统用于路口监控、小区出入口、校园周边等场景的辅助管理。本文将围绕“基于深度学习 YOLOv8 PyQt5 的非机动车头盔佩戴检测系统”展开从技术选型、环境配置、数据集准备、模型训练、推理封装到 PyQt5 桌面界面开发完整拆解一套可运行的最小系统。无论你是正在做毕业设计、课程项目还是希望将目标检测落地到实际业务中都可以参考这篇文章的思路和代码。1. 背景与核心概念1.1 为什么要做头盔佩戴检测非机动车骑行中的头盔佩戴问题直接关系到骑乘人员的生命安全。传统的管理方式主要依赖交警现场执法或人工查看监控视频面对大量视频数据时效率较低且无法做到实时预警。如果能把“头盔检测”交给算法自动完成系统就可以在识别到未佩戴头盔时自动抓拍、记录时间地点甚至在现场通过语音或弹窗提醒管理人员极大减轻人工压力。这类系统本质上是一个目标检测任务Object Detection即在一张图片或一帧视频中定位出目标对象的位置并给出对应的类别。在头盔检测场景中目标通常分为两类佩戴头盔的骑行人with_helmet未佩戴头盔的骑行人without_helmet当然更细化的项目还会增加“二轮车”类别先检测车辆再判断骑行人但这种方案会提高标注和训练的复杂度。作为入门级系统本文采用直接检测骑行人的二分类方案。1.2 YOLOv8 在目标检测中的优势YOLOYou Only Look Once系列是目标检测领域非常经典的算法它的核心思想是将目标检测看作一个回归问题通过单次前向传播直接预测目标的类别和边界框因此速度非常快。YOLOv8 是 Ultralytics 公司推出的版本在 YOLOv5 的基础上继续优化了网络结构、训练策略和数据增强方式同时提供了统一的 Python SDK非常适合快速训练和部署。YOLOv8 的主要特点支持目标检测、实例分割、姿态估计、图像分类等多种任务。提供简洁的 Python API只需几行代码就能完成训练和推理。模型文件体积相对可控在 GPU 或 CPU 环境下均可运行。支持导出为 ONNX、TensorRT 等格式便于后续部署。对于头盔检测这种类间距较大的二分类任务YOLOv8s 或 YOLOv8n 这类轻量级模型就基本够用。如果对精度要求更高可以换用 YOLOv8m 或 YOLOv8l但推理耗时也会相应增加。1.3 PyQt5 在桌面应用开发中的角色PyQt5 是 Qt 框架的 Python 绑定可以用来开发跨平台的桌面 GUI 程序。在深度学习项目中模型推理代码本身只是命令行脚本普通用户很难直接使用。通过 PyQt5 封装一个窗口程序可以把“选择图片检测”“实时摄像头检测”“视频文件检测”等功能做成按钮和界面用户不需要了解任何模型细节只需点击按钮即可完成操作。本系统中的 PyQt5 主要承担以下工作展示图片或视频画面。触发模型推理并显示检测结果。展示检测统计信息如检测帧率、未佩戴数量等。提供简单的文件选择和摄像头开关功能。1.4 系统整体技术架构下面用一个简洁的流程梳理整个系统的数据走向视频帧/图片输入 ↓ OpenCV 读取画面 ↓ YOLOv8 模型推理 ↓ 输出检测框 类别 置信度 ↓ PyQt5 界面绘制并显示 ↓ 统计结果 / 抓拍保存整个系统可以拆成两个独立模块模型侧PyTorch YOLOv8负责训练和推理。界面侧PyQt5 OpenCV负责交互和画面显示。两者通过一个核心检测函数进行衔接该函数接收一帧图像返回标注后的图像和检测结果列表。2. 环境准备与版本说明2.1 开发环境概览本文示例以 Windows 10/11 系统为主Python 使用 3.9 或 3.10 版本。深度学习框架采用 PyTorchYOLOv8 依赖 Ultralytics 库。界面部分使用 PyQt5图像处理使用 OpenCV。依赖项推荐版本说明Python3.9 / 3.10过低版本可能无法安装最新 PyTorchPyTorch2.x根据 CUDA 版本选择对应安装命令Ultralytics8.xYOLOv8 官方 Python SDKPyQt55.15.x稳定版本兼容性较好OpenCV-Python4.8.x 或更新用于图像和视频处理NumPy1.24 左右即可PyTorch 和 OpenCV 的公共依赖需要注意的是如果你的电脑没有 NVIDIA GPU可以使用 CPU 版本的 PyTorch模型仍然可以运行只是训练和推理速度较慢。对于头盔检测这种小模型CPU 推理速度大约在每秒几帧到十几帧之间对实时摄像头检测会有一定压力。2.2 Python 虚拟环境创建建议为项目单独创建虚拟环境避免和系统 Python 环境产生包冲突。打开终端执行以下命令python -m venv helmet_env helmet_env\Scripts\activate激活成功后命令行前缀会显示(helmet_env)说明已经进入虚拟环境。2.3 安装 PyTorchPyTorch 的安装命令取决于你是否使用 GPU。如果使用 GPU需要先去 NCNN 官网或者 PyTorch 官网查看对应 CUDA 版本的安装命令。以 CUDA 11.8 为例pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118如果没有 GPU直接安装 CPU 版本即可pip install torch torchvision torchaudio安装完成后可以用下面的 Python 命令验证 PyTorch 是否可以正常导入import torch print(torch.__version__) print(torch.cuda.is_available())如果torch.cuda.is_available()返回True说明 GPU 环境配置成功如果返回False则是 CPU 环境不影响后续代码运行。2.4 安装其他依赖库pip install ultralytics pip install pyqt5 pip install opencv-python pip install numpy安装完成后为了确保 YOLOv8 可以正常下载预训练权重建议保持网络畅通。后面运行时Ultralytics 会自动下载yolov8n.pt等权重文件。2.5 验证 YOLOv8 是否安装成功在终端执行yolo predict sourcehttps://ultralytics.com/images/bus.jpg这个命令会下载一张示例图片并进行检测。如果你能看到终端输出检测结果并且本地生成了标注后的图片说明 YOLOv8 环境已经准备好了。3. 数据集准备与模型训练3.1 数据集来源与标注格式训练自己的头盔检测模型需要准备带标注的数据集。常用的标注格式是 YOLO 格式每个图片对应一个同名的.txt文件文件中每行代表一个目标格式如下类别ID 中心点X 中心点Y 宽度 高度其中坐标值都是归一化到 0~1 之间的小数。例如0 0.5 0.4 0.3 0.5表示图片中心位置有一个类别 ID 为 0 的目标边界框宽度占整张图片的 30%高度占 50%。数据集的目录结构通常如下dataset/ ├── images/ │ ├── train/ │ └── val/ └── labels/ ├── train/ └── val/你也可以使用 LabelImg 或 LabelStudio 等标注工具完成标注工作。对于头盔检测建议至少准备 2000~5000 张图片类别分布尽量均衡。3.2 数据集配置文件的编写Ultralytics 训练时需要读取一个 YAML 格式的数据集配置文件内容示例如下# dataset.yaml path: D:/helmet_dataset # 数据集根目录 train: images/train # 训练图片目录 val: images/val # 验证图片目录 nc: 2 names: 0: with_helmet 1: without_helmet配置完成后将文件保存为dataset.yaml。训练时只需要把这个 YAML 文件路径传给 YOLOv8 即可。3.3 编写训练脚本YOLOv8 的训练脚本非常简洁核心代码如下from ultralytics import YOLO # 加载预训练模型这里使用 YOLOv8s 作为基础模型 model YOLOL(yolov8s.pt) # 开始训练 model.train( datadataset.yaml, epochs100, imgsz640, batch16, device0, # 使用 GPU 训练CPU 可改为 cpu workers4, projectruns/train, namehelmet_detect, exist_okTrue, )训练过程会在终端实时打印每个 epoch 的损失值、精度、召回率、mAP 等指标。训练完成后最优权重文件会保存在runs/train/helmet_detect/weights/best.pt。3.4 训练结果评估训练结束后可以执行以下代码查看模型在验证集上的表现from ultralytics import YOLO model YOLO(runs/train/helmet_detect/weights/best.pt) metrics model.val(datadataset.yaml) print(fmAP50: {metrics.box.map50:.4f}) print(fmAP50-95: {metrics.box.map:.4f})对于头盔检测任务mAP50 一般建议达到 0.85 以上才算比较理想。如果精度偏低可以尝试以下方法增加训练图片数量尤其是未佩戴头盔的样本。增加训练轮数但要注意过拟合。更换更大的模型例如yolov8m.pt或yolov8l.pt。调整数据增强参数如翻转、旋转、亮度变化。3.5 训练轮数与精度的关系很多初学者会问训练轮数设置多少合适。这个没有固定答案主要看损失曲线是否收敛。一般来说数据集较小几千张时50~100 轮足够。数据集较大几万张时150~300 轮比较常见。如果训练后期 loss 持续下降但验证集 mAP 开始波动可能出现过拟合可以用早停机制或适当增大数据增强。在 Ultralytics 库中也可以通过设置patience参数启用早停model.train( datadataset.yaml, epochs300, patience20, )patience20表示如果连续 20 轮验证集指标没有提升训练会自动提前结束。4. 基于 YOLOv8 的模型推理封装4.1 核心推理函数设计为了让 PyQt5 界面方便调用我们需要把模型推理逻辑封装成一个独立的类或函数。输入是一帧 BGR 格式的图像OpenCV 默认格式输出是标注后的图像以及检测信息列表。# detector.py import cv2 from ultralytics import YOLO class HelmetDetector: def __init__(self, weights_pathruns/train/helmet_detect/weights/best.pt, conf_thres0.35, iou_thres0.5): self.model YOLO(weights_path) self.conf_thres conf_thres self.iou_thres iou_thres def detect_frame(self, frame): 输入 BGR 图像返回标注后的图像和检测结果列表。 结果列表中的每个元素为 (class_name, confidence, x1, y1, x2, y2) results self.model.predict( sourceframe, confself.conf_thres, iouself.iou_thres, verboseFalse, ) annotated_frame frame.copy() detections [] for result in results: boxes result.boxes if boxes is None: continue for box in boxes: x1, y1, x2, y2 box.xyxy[0].tolist() conf float(box.conf[0]) cls_id int(box.cls[0]) class_name result.names[cls_id] detections.append((class_name, conf, x1, y1, x2, y2)) # 绘制检测框 color (0, 255, 0) if class_name with_helmet else (0, 0, 255) cv2.rectangle(annotated_frame, (int(x1), int(y1)), (int(x2), int(y2)), color, 2) label f{class_name} {conf:.2f} cv2.putText(annotated_frame, label, (int(x1), int(y1) - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, color, 2) return annotated_frame, detections4.2 图片检测测试在命令行下可以先用一张测试图片验证推理封装是否正确import cv2 from detector import HelmetDetector detector HelmetDetector(runs/train/helmet_detect/weights/best.pt) img cv2.imread(test.jpg) result_img, detections detector.detect_frame(img) print(f检测到 {len(detections)} 个目标) for det in detections: print(det) cv2.imwrite(test_result.jpg, result_img)4.3 视频文件检测测试处理视频时需要循环读取每一帧并统计检测结果。下面是一个简单的视频检测脚本import cv2 from detector import HelmetDetector detector HelmetDetector(runs/train/helmet_detect/weights/best.pt) cap cv2.VideoCapture(test_video.mp4) fps cap.get(cv2.CAP_PROP_FPS) while cap.isOpened(): ret, frame cap.read() if not ret: break result_frame, detections detector.detect_frame(frame) cv2.imshow(helmet detection, result_frame) if cv2.waitKey(1) 0xFF ord(q): break cap.release() cv2.destroyAllWindows()这段代码可以直接运行只是界面比较简陋。接下来我们把它整合到 PyQt5 中。5. PyQt5 桌面界面开发5.1 界面布局设计本系统的 PyQt5 界面主要包含以下区域顶部按钮区打开图片、打开视频、打开摄像头、退出。中央画面区显示原始画面和检测结果。右侧信息区显示实时统计信息如总检测人数、未佩戴人数、帧率等。我们使用QMainWindow作为主窗口通过QLabel显示图像通过QPushButton创建按钮通过QGridLayout或QVBoxLayout布局。5.2 主窗口代码下面是一个精简但完整的 PyQt5 主窗口示例# main_window.py import cv2 import sys from PyQt5.QtWidgets import QMainWindow, QLabel, QPushButton, QVBoxLayout, QHBoxLayout, QWidget, QApplication, QFileDialog, QTextEdit from PyQt5.QtGui import QImage, QPixmap from PyQt5.QtCore import Qt from detector import HelmetDetector class MainWindow(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle(非机动车头盔佩戴检测系统) self.setMinimumSize(1000, 600) self.detector HelmetDetector(runs/train/helmet_detect/weights/best.pt) self.cap None self.timer None self.init_ui() def init_ui(self): # 中央布局 central_widget QWidget() self.setCentralWidget(central_widget) main_layout QHBoxLayout() # 左侧按钮布局 left_layout QVBoxLayout() self.btn_open_image QPushButton(打开图片) self.btn_open_video QPushButton(打开视频) self.btn_open_camera QPushButton(打开摄像头) self.btn_exit QPushButton(退出) left_layout.addWidget(self.btn_open_image) left_layout.addWidget(self.btn_open_video) left_layout.addWidget(self.btn_open_camera) left_layout.addWidget(self.btn_exit) left_layout.addStretch() # 中间图像显示 self.label_image QLabel(请打开图片或视频) self.label_image.setAlignment(Qt.AlignCenter) self.label_image.setStyleSheet(background-color: #000; color: #fff;) # 右侧信息栏 right_layout QVBoxLayout() self.text_info QTextEdit() self.text_info.setReadOnly(True) right_layout.addWidget(self.text_info) main_layout.addLayout(left_layout, 1) main_layout.addWidget(self.label_image, 4) main_layout.addLayout(right_layout, 1) central_widget.setLayout(main_layout) # 绑定事件 self.btn_open_image.clicked.connect(self.open_image) self.btn_open_video.clicked.connect(self.open_video) self.btn_open_camera.clicked.connect(self.open_camera) self.btn_exit.clicked.connect(self.close) def open_image(self): file_path, _ QFileDialog.getOpenFileName( self, 选择图片, , Image Files (*.jpg *.png *.jpeg) ) if not file_path: return frame cv2.imread(file_path) result_frame, detections self.detector.detect_frame(frame) self.show_image(result_frame) self.text_info.clear() self.text_info.append(f图片检测完成共 {len(detections)} 个目标) for det in detections: self.text_info.append(f{det[0]} 置信度: {det[1]:.2f}) def open_video(self): file_path, _ QFileDialog.getOpenFileName( self, 选择视频, , Video Files (*.mp4 *.avi *.mov) ) if not file_path: return self.cap cv2.VideoCapture(file_path) self.text_info.clear() self.text_info.append(视频检测开始) while self.cap.isOpened(): ret, frame self.cap.read() if not ret: break result_frame, detections self.detector.detect_frame(frame) self.show_image(result_frame) self.update_info(detections) cv2.waitKey(1) self.cap.release() def open_camera(self): self.cap cv2.VideoCapture(0) self.text_info.clear() self.text_info.append(摄像头检测开始) while self.cap.isOpened(): ret, frame self.cap.read() if not ret: break result_frame, detections self.detector.detect_frame(frame) self.show_image(result_frame) self.update_info(detections) cv2.waitKey(1) self.cap.release() def update_info(self, detections): total len(detections) without sum(1 for d in detections if d[0] without_helmet) self.text_info.clear() self.text_info.append(f当前帧检测到 {total} 个目标) self.text_info.append(f未佩戴头盔人数: {without}) def show_image(self, frame): frame cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) h, w, ch frame.shape bytes_per_line ch * w q_image QImage(frame.data, w, h, bytes_per_line, QImage.Format_RGB888) pixmap QPixmap.fromImage(q_image) scaled_pixmap pixmap.scaled( self.label_image.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation, ) self.label_image.setPixmap(scaled_pixmap) if __name__ __main__: app QApplication(sys.argv) window MainWindow() window.show() sys.exit(app.exec_())5.3 界面卡顿问题说明上面的简化代码中视频检测是在主线程中循环读取的实际运行时容易出现界面卡顿。更合理的设计是把视频读取和模型推理放到子线程中主线程只负责刷新界面。但由于本文是入门教程先以功能可用为主生产级代码需要进一步优化线程模型。6. 完整系统运行与验证6.1 项目目录结构建议按下面的目录组织工程helmet_detection_system/ ├── main_window.py # PyQt5 主界面 ├── detector.py # YOLOv8 推理封装 ├── dataset.yaml # 数据集配置 ├── runs/ │ └── train/ │ └── helmet_detect/ │ └── weights/ │ ├── best.pt │ └── last.pt ├── test_images/ ├── test_videos/ └── requirements.txt6.2 requirements.txtultralytics8.2.0 PyQt55.15.9 opencv-python4.8.0 numpy1.24.0安装依赖时可直接执行pip install -r requirements.txt6.3 启动系统在项目根目录执行python main_window.py启动后点击“打开图片”选择一个包含骑行人的图片系统会自动标注检测框。点击“打开视频”可以处理视频文件点击“打开摄像头”可以调用本地摄像头进行实时检测。6.4 预期效果当模型检测到佩戴头盔的骑行人时绘制绿色检测框类别为with_helmet当检测到未佩戴头盔的骑行人时绘制红色检测框类别为without_helmet。右侧信息栏会显示当前帧的检测统计结果。7. 常见问题与排查思路7.1 PyQt5 安装失败问题现象常见原因解决思路pip 安装 pyqt5 报错Python 版本过高或网络问题使用 Python 3.9/3.10换用国内镜像源导入 PyQt5 时提示 DLL 加载失败缺少 Visual C 运行库安装官方 Visual C Redistributable国内镜像源安装命令示例pip install pyqt5 -i https://pypi.tuna.tsinghua.edu.cn/simple7.2 CUDA 不可用如果已经安装了 GPU 版 PyTorch但训练时提示 CUDA 不可用可以按下面步骤排查使用nvidia-smi查看显卡驱动是否正常。确认 PyTorch 版本与 CUDA 版本匹配。使用python -c import torch; print(torch.cuda.is_available())验证。如果显卡驱动版本太低需要去显卡官网更新驱动如果 PyTorch 安装成了 CPU 版本需要重新安装对应 CUDA 版本的 PyTorch。7.3 检测效果不佳问题现象常见原因解决思路漏检较多训练数据不足或不均衡增加训练图片尤其是困难样本误检较多置信度阈值设置太低调高 conf_thres 参数小目标检测不到图片分辨率不足提高 imgsz 或使用更高分辨率模型7.4 PyQt5 界面显示黑屏在调用 OpenCV 读取视频帧时如果摄像头没有被正确释放或者视频路径不存在画面会停留在默认黑屏状态。建议在处理前先判断if self.cap is None or not self.cap.isOpened(): self.text_info.append(无法打开视频或摄像头) return7.5 模型加载慢YOLOv8 模型首次加载时如果有 GPU 环境会进行 CUDA 初始化耗时较长属于正常现象。可以将模型加载放在程序启动时完成避免每次检测都重复加载。8. 最佳实践与工程建议8.1 线程模型优化在实际项目中视频检测流程不应该直接写在 UI 事件回调里否则界面在检测时会卡住无法点击。推荐使用QThread或QTimer将读取视频帧、执行模型推理放到子线程中主线程通过信号槽机制接收结果并刷新界面。核心思路如下子线程循环读取摄像头/视频帧。调用模型推理得到结果。发送QImage和检测结果到主线程。主线程只负责显示。8.2 模型选型建议对于 CPU 部署场景优先选择yolov8n或yolov8s。对于 GPU 实时检测场景yolov8s和yolov8m是性能和精度的折中选择。如果项目要求更高的精度且在服务器端运行可以尝试yolov8l。8.3 数据标注注意事项标注框要尽量贴合目标不要包含大量背景。遮挡严重的骑行人如果人眼都无法判断是否佩戴头盔建议直接不标注。训练集和验证集要保证分布一致最好按场景或时间段划分避免同一摄像头画面同时出现在训练集和验证集。8.4 安全与合规建议如果系统要部署到真实路口或公共区域需要注意以下几点确保监控设备安装和图像采集符合当地法律法规要求。系统只用于辅助管理和事后统计不直接用于行政执法处罚。对采集到的图像数据做好访问权限控制避免个人隐私泄露。涉及大规模部署时建议在边缘设备或本地服务器完成推理减少原始视频外传风险。8.5 模型导出与部署训练好的best.pt可以导出为 ONNX 格式方便在 OpenCV DNN、ONNX Runtime 或其他推理框架中运行from ultralytics import YOLO model YOLO(runs/train/helmet_detect/weights/best.pt) model.export(formatonnx, imgsz640)导出后的best.onnx文件体积更小适合嵌入到其他应用中。9. 项目扩展方向9.1 增加抓拍保存功能在检测到未佩戴头盔时自动截取当前帧并保存到本地目录。这个功能实现比较简单只需要在检测到without_helmet时执行cv2.imwrite(fcapture/{datetime.now().strftime(%Y%m%d_%H%M%S)}.jpg, frame)9.2 接入数据库记录将检测时间、地点、未佩戴数量等信息写入 MySQL 或 SQLite方便事后查询统计。9.3 使用告警提示音在 PyQt5 中可以通过QSound或winsound模块播放提示音当检测到未佩戴头盔时触发语音提醒。9.4 多线程实时视频流如果使用 IP 摄像头只需将cv2.VideoCapture(0)中的 0 替换为摄像头 RTSP 地址即可cap cv2.VideoCapture(rtsp://user:passwordip:port/stream)但 RTSP 流的延迟和断线重连问题在工程中需要额外处理。10. 总结本文完整实现了基于 YOLOv8 和 PyQt5 的非机动车头盔佩戴检测系统涵盖数据集准备、模型训练、推理封装、桌面界面开发和常见问题排查。你可以在这个基础上继续完善线程设计、抓拍存储、日志统计等功能把它扩展成真正可落地的辅助管理工具。如果这篇文章对你有帮助欢迎收藏备用。后续也可以多做测试样本观察模型在不同光线、角度、场景下的表现逐步优化你的数据集和训练策略。
返回列表