ARTICLE DETAIL

资讯详情

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

YOLOv8行人检测实战:PyQt界面+监控适配数据集+小目标优化

YOLOv8行人检测实战:PyQt界面+监控适配数据集+小目标优化 简介本资源是一套开箱即用的YOLOv8行人检测完整实现方案面向计算机视觉初学者、AI项目开发者及智能交通领域实践者解决街道与交通场景下高精度、实时行人识别与定位问题。压缩包含2000个文件总大小456.56MB主体为1991个标注用txt文件对应jpg图像的person类别坐标、3个PDF环境配置教程、2个核心Python脚本win.py为主程序入口apprcc_rc.py支撑PyQt界面及4个说明性md文件其中PDF文档详述YOLOv3-v8全系列环境搭建与运行步骤PyQt界面模块支持可视化加载视频/图片并实时显示检测框与置信度。已有814人学习下载用户可直接调用预训练权重mAP超90%复现训练流程或基于标注数据集微调模型代码结构清晰含train_dataset、utils、dialog等模块便于二次开发与教学拓展。1. 用YOLOv8做行人检测配PyQt界面跑通监控场景——不靠现成Demo从数据集到可交互程序全链路实操你手头有一段监控视频想实时框出画面里所有行走的人还要能点开界面选视频、调置信度、保存检测结果。网上搜“YOLOv8行人检测”一堆截图但点进去不是缺数据集路径就是PyQt界面一运行就报QApplication: No such file or directory更别说GPU显存不足时怎么降分辨率、怎么跳过空帧加速推理。这篇不是复述官方文档而是按一线部署逻辑重走一遍先确认YOLOv8在行人检测任务上的结构优势比如C2f模块对小尺度人体特征的保留能力再选真正适配监控视角的公开数据集避开COCO里大量侧身/遮挡少的样本最后用PyQt5搭一个带状态栏、进度条和结果导出按钮的轻量界面——所有代码本地可验证参数值标清物理含义报错位置直接对应到.ui文件第几行。2. 为什么选YOLOv8而非YOLOv5或YOLOv7做行人检测看C2f与Anchor-Free设计对监控场景的实际影响2.1 行人检测的特殊性小目标多、密集遮挡、低帧率视频输入监控场景下行人常以远距离小目标出现如1920×1080画面中人体框不足30×60像素且存在大量并排行走、前后遮挡、光照突变。YOLOv5默认使用固定Anchor尺寸如64×64、128×128在密集人群区域易漏检重叠框YOLOv7虽引入E-ELAN结构但其跨阶段特征融合仍依赖Anchor匹配。而YOLOv8采用完全Anchor-Free设计通过关键点回归中心点偏移直接预测边界框对尺度变化鲁棒性更强。更重要的是其Backbone中的C2f模块Cross Stage Partial with 2 convolutions feature fusion在保持计算量不变前提下将浅层特征图通道数提升至256显著增强对小目标纹理如衣着细节、肢体轮廓的提取能力——这正是监控画面中区分“人”与“移动广告牌”的关键。提示C2f不是简单堆叠卷积它把输入特征图拆分为两路一路经两个标准ConvBNSiLU后与另一路原始特征拼接再通过1×1卷积压缩通道。这种设计让网络在低层就能捕获更多空间细节避免YOLOv5中P3层因下采样过快丢失小目标信息。2.2 数据集选择避开COCO陷阱用CrowdHumanVisDrone构建监控适配训练集COCO数据集虽标注质量高但其图像多为单人特写或中景构图行人平均框面积占画面比例达12%而典型监控视频中该值常低于1.5%。直接迁移会导致模型对小目标召回率骤降。我们采用双数据源混合策略CrowdHuman专为密集人群设计含15000张图像标注包含完整人体框头部点不可见区域掩码特别适合处理遮挡VisDrone2019无人机俯拍视角模拟高空监控包含大量远距离、小尺度行人最小框仅8×16像素且提供夜间/雨雾天气子集。实际操作中我们下载CrowdHuman的train部分约10GB和VisDrone2019的trainval约8GB用以下脚本统一转换为YOLOv8要求的labels/目录结构每个.txt文件含class_id center_x center_y width height归一化坐标# convert_to_yolo.py import os import json from pathlib import Path def crowdhuman_to_yolo(json_path, img_dir, out_label_dir): with open(json_path) as f: data json.load(f) for ann in data[images]: img_name ann[file_name] img_path Path(img_dir) / img_name if not img_path.exists(): continue h, w ann[height], ann[width] label_path out_label_dir / f{img_name.split(.)[0]}.txt with open(label_path, w) as lf: for obj in ann.get(objects, []): if obj[category] ! person: # 只取行人 continue x1, y1, w_box, h_box obj[bbox] cx, cy x1 w_box/2, y1 h_box/2 lf.write(f0 {cx/w:.6f} {cy/h:.6f} {w_box/w:.6f} {h_box/h:.6f}\n) # 执行命令 # python convert_to_yolo.py crowdhuman/annotation_train.json crowdhuman/Images train/labels/注意VisDrone的标注格式为.txt每行class_id x1 y1 w h score category需用pandas.read_csv(..., sep , headerNone)读取后过滤class_id0行人再归一化坐标。混合后总训练图像约2.3万张验证集单独留VisDrone的test子集647张确保评估视角与部署场景一致。3. 用PyQt5搭建可交互检测界面从.ui设计到实时推理线程管理3.1 界面布局设计用Qt Designer定义核心控件与信号槽我们不手写QWidget代码而是用Qt DesignerPyQt5自带拖拽生成.ui文件关键控件包括QGraphicsView显示原图与检测框QPushButton“加载视频”、“开始检测”、“暂停”、“导出日志”QSlider置信度阈值调节范围0.1–0.9默认0.5QLabel状态栏显示当前帧数/检测耗时/FPSQTableWidget检测日志表列时间戳、行人ID、框坐标、置信度保存为main_window.ui后用pyside2-uic或pyside6-uic转为Python类注意PyQt5需用uic.loadUi()加载# main_window.py from PyQt5 import QtWidgets, QtCore, QtGui from PyQt5.QtCore import Qt, QThread, pyqtSignal from PyQt5.uic import loadUi class DetectionThread(QThread): update_frame pyqtSignal(object, list) # 发射 (frame, detections) def __init__(self, model_path, conf_thres0.5): super().__init__() from ultralytics import YOLO self.model YOLO(model_path) self.conf_thres conf_thres self.running False def run(self): cap cv2.VideoCapture(self.video_path) while self.running: ret, frame cap.read() if not ret: break # YOLOv8推理自动启用GPU if available results self.model.predict(frame, confself.conf_thres, verboseFalse) boxes results[0].boxes.xyxy.cpu().numpy() # [x1,y1,x2,y2] confs results[0].boxes.conf.cpu().numpy() classes results[0].boxes.cls.cpu().numpy() # 过滤行人class_id0 mask (classes 0) detections list(zip(boxes[mask], confs[mask])) self.update_frame.emit(frame, detections) cap.release() class MainWindow(QtWidgets.QMainWindow): def __init__(self): super().__init__() loadUi(main_window.ui, self) # 加载UI self.video_path self.thread None # 绑定按钮点击事件 self.load_btn.clicked.connect(self.load_video) self.start_btn.clicked.connect(self.start_detection) self.pause_btn.clicked.connect(self.pause_detection) # 滑块值改变时更新置信度 self.conf_slider.valueChanged.connect( lambda v: self.conf_label.setText(f置信度: {v/10:.1f}) )3.2 多线程安全绘图避免GUI卡顿与OpenCV-BGR/Qt-RGB色彩空间冲突PyQt的QGraphicsView不能直接显示OpenCV的BGR图像且主线程绘图会阻塞UI响应。解决方案是在DetectionThread中完成推理只发射frame和detections主线程接收信号后用QImage转换色彩空间并绘制# 在MainWindow.__init__中连接信号 self.thread.update_frame.connect(self.display_frame) def display_frame(self, frame, detections): # BGR to RGB rgb_frame cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) h, w rgb_frame.shape[:2] qt_image QtGui.QImage(rgb_frame.data, w, h, w * 3, QtGui.QImage.Format_RGB888) # 绘制检测框在QPixmap上 pixmap QtGui.QPixmap.fromImage(qt_image) painter QtGui.QPainter(pixmap) pen QtGui.QPen(Qt.red, 2) painter.setPen(pen) for box, conf in detections: x1, y1, x2, y2 map(int, box) painter.drawRect(x1, y1, x2 - x1, y2 - y1) # 标注置信度 painter.drawText(x1, y1 - 10, f{conf:.2f}) painter.end() # 更新QGraphicsView scene QtWidgets.QGraphicsScene() scene.addPixmap(pixmap) self.graphics_view.setScene(scene) self.status_label.setText(fFPS: {1/(time.time()-self.last_time):.1f}) self.last_time time.time()提示QPainter必须在QPixmap创建后立即调用且painter.end()前不能有return否则绘图失效。若检测框闪烁检查是否重复调用setScene()——应每次新建scene而非复用。4. YOLOv8行人检测模型微调从预训练权重到监控场景精度提升的关键参数4.1 训练配置文件解析data.yaml与train.py中必须修改的5个参数YOLOv8默认配置针对通用目标需针对性调整。在data.yaml中train: ../crowdhuman_visdrone/train/images val: ../visdrone/test/images # 用VisDrone test作验证贴近部署环境 nc: 1 # 类别数行人只有1类 names: [person] # 类别名必须与标签文件class_id一致在训练脚本中关键参数如下表基于RTX 3060 12GB显存实测参数推荐值物理含义调整依据imgsz1280输入图像长边尺寸监控画面常为1920×1080缩放至1280保持宽高比避免小目标进一步失真batch16每批图像数显存占用≈batch×imgsz²×3×4bytes1280²×16≈1.05GB留余量给梯度计算epochs100训练轮数CrowdHumanVisDrone混合数据量大100轮足够收敛早停需设patience10lr00.01初始学习率YOLOv8默认0.01但行人检测易过拟合建议降至0.005并启用cosine衰减mosaic0.5马赛克增强概率设0.5而非默认1.0避免过度扭曲行人姿态保留真实遮挡关系执行训练命令yolo detect train datadata.yaml modelyolov8s.pt \ imgsz1280 batch16 epochs100 lr00.005 \ namepedestrian_v8s mosaic0.54.2 损失函数曲线诊断用TensorBoard定位过拟合与收敛异常训练后runs/detect/pedestrian_v8s目录下生成results.csv可用以下代码快速绘图import pandas as pd import matplotlib.pyplot as plt df pd.read_csv(runs/detect/pedestrian_v8s/results.csv) plt.figure(figsize(12,4)) plt.subplot(1,3,1) plt.plot(df[epoch], df[train/box_loss], labelBox Loss) plt.title(Bounding Box Loss) plt.subplot(1,3,2) plt.plot(df[epoch], df[val/box_loss], labelVal Box Loss) plt.title(Validation Box Loss) plt.subplot(1,3,3) plt.plot(df[epoch], df[metrics/mAP50-95(B)], labelmAP50-95) plt.title(mAP50-95) plt.tight_layout() plt.savefig(loss_curve.png) plt.show()注意若验证集val/box_loss在第60轮后持续上升而训练损失继续下降说明过拟合。此时应降低mosaic至0.3或增加degrees10随机旋转增强泛化性。mAP50-95若卡在0.45以下检查data.yaml中val路径是否指向VisDrone test而非train避免数据泄露。5. 实战技巧解决监控视频检测中的3类高频问题——帧率抖动、重复检测、GPU显存溢出5.1 帧率稳定控制用cv2.CAP_PROP_POS_FRAMES跳过空闲帧监控视频常含大量静止画面如走廊无人时段逐帧推理浪费资源。我们在DetectionThread.run()中加入运动检测逻辑# 在cap.read()后插入 gray cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) gray cv2.GaussianBlur(gray, (21, 21), 0) if hasattr(self, prev_gray) and self.prev_gray is not None: frame_diff cv2.absdiff(self.prev_gray, gray) thresh cv2.threshold(frame_diff, 25, 255, cv2.THRESH_BINARY)[1] motion_pixels cv2.countNonZero(thresh) if motion_pixels 500: # 少于500像素变化视为静止 self.prev_gray gray continue # 跳过此帧推理 self.prev_gray gray提示阈值500需根据摄像头分辨率调整1080p画面建议设300–800。此方法比time.sleep()更精准避免错过突发运动。5.2 行人ID去重用IoU阈值合并相邻帧的同一目标同一行人连续多帧被检测导致日志表重复记录。我们在display_frame中添加跟踪逻辑def merge_detections(self, current_dets, prev_dets, iou_thresh0.6): 合并当前帧与上一帧检测抑制重复 if not prev_dets: return current_dets merged [] used_prev [False] * len(prev_dets) for curr_box, curr_conf in current_dets: best_iou, best_idx 0, -1 for i, (prev_box, _) in enumerate(prev_dets): iou self.bbox_iou(curr_box, prev_box) if iou iou_thresh and iou best_iou: best_iou, best_idx iou, i if best_idx 0 and not used_prev[best_idx]: # 合并置信度取高者 merged.append((curr_box, max(curr_conf, prev_dets[best_idx][1]))) used_prev[best_idx] True else: merged.append((curr_box, curr_conf)) return merged def bbox_iou(self, box1, box2): x1, y1, x2, y2 box1 x1_p, y1_p, x2_p, y2_p box2 inter_x1 max(x1, x1_p) inter_y1 max(y1, y1_p) inter_x2 min(x2, x2_p) inter_y2 min(y2, y2_p) if inter_x2 inter_x1 or inter_y2 inter_y1: return 0 inter_area (inter_x2 - inter_x1) * (inter_y2 - inter_y1) area1 (x2 - x1) * (y2 - y1) area2 (x2_p - x1_p) * (y2_p - y1_p) return inter_area / (area1 area2 - inter_area)5.3 GPU显存优化用torch.cuda.empty_cache()释放中间变量GTX 1660 Ti等入门级显卡6GB显存运行yolov8s.pt易OOM。除降低batch外需手动清理# 在DetectionThread.run()的每次推理后 results self.model.predict(frame, confself.conf_thres, verboseFalse) # ... 处理results del results # 删除引用 torch.cuda.empty_cache() # 立即释放显存同时在model.predict()中禁用halfTrueFP16推理因小目标检测中FP16易导致边界框坐标精度损失。实测GTX 1660 Ti下imgsz960batch8empty_cache()可稳定运行30FPS。本文还有配套的精品资源点击获取
返回列表