YOLOv8行人检测实战:从数据集转换到PyQt5界面部署
简介本资源面向计算机视觉入门与进阶学习者提供一套完整的YOLOV8行人检测实战方案覆盖从数据标注、环境配置到模型训练与图形化部署的全流程。包内包含约5000张已标注行人检测数据集以及可训练与验证的代码、训练好的YOLO系列模型和一份图形化界面并附有作者联系方式便于调试交流。资源共1642个文件以md说明文档、py源码、pyc编译文件、yaml配置、jpg与png图像、pt权重、ipynb笔记等为主压缩包约847.6MB目录结构清晰便于按模块检索。教程不仅支持YOLOv8训练也兼容v3、v5、v9、v10等系列模型适合希望快速上手目标检测、完成课程设计或项目落地的读者。目前已有4296人学习下载可帮助读者掌握数据准备、模型训练、界面调用与常见问题排查的完整思路。1. 从一份 YOLOv8 行人检测压缩包说起它到底能省你多少事如果你最近在找行人检测的落地方案大概率会刷到「YOLOv8 行人检测代码数据集训练好的模型图形化系统」这类打包资源。它解决的不是「YOLOv8 是什么」这种科普问题而是一个更现实的痛点从零搭一套能跑、能看、能演示的行人检测系统中间要跨过环境配置、数据集标注格式转换、训练调参、推理封装、界面开发五道坎任何一道卡住都可能耗掉两三天。这个压缩包的价值就在于把这五步压缩成一条可复现的流水线让你把精力放在「改哪里、调什么参数、怎么接自己的数据」上而不是重复造轮子。它适合三类人一是刚接触 YOLOv8、想拿一个完整项目练手的学生和转行者二是需要快速做出行人检测 demo 给客户或领导看的工程师三是已经会训练模型、但懒得写图形界面的算法同学。下面我按「数据怎么准备 → 模型怎么训 → 界面怎么接 → 坑在哪」的顺序把这条链路拆开讲清楚参数和命令都给到能直接抄的程度。2. 行人检测数据集从原始标注到 YOLOv8 可训练格式2.1 为什么行人数据集不能直接丢给 YOLOv8 训练YOLOv8 只认一种标注格式每张图片对应一个同名.txt文件每行是类别 中心x 中心y 宽 高且坐标全部归一化到 0~1 之间。而市面上常见的行人数据集标注格式五花八门——VOC 是 XMLCOCO 是 JSONLabelMe 是 JSON还有的直接是 CSV。你拿到的压缩包里如果已经带数据集先别急着开训用下面这段脚本检查一遍格式是否合规。import os import glob def check_yolo_labels(label_dir, img_dir): 检查 YOLO 格式标注是否合规 issues [] label_files glob.glob(os.path.join(label_dir, *.txt)) if not label_files: print(未找到任何 .txt 标注文件) return for lf in label_files: base os.path.splitext(os.path.basename(lf))[0] # 检查是否有对应图片 img_exists any( os.path.exists(os.path.join(img_dir, base ext)) for ext in [.jpg, .jpeg, .png, .bmp] ) if not img_exists: issues.append(f标注无对应图片: {lf}) with open(lf, r) as f: for i, line in enumerate(f): parts line.strip().split() if len(parts) ! 5: issues.append(f{lf} 第{i1}行字段数不对: {len(parts)}) continue cls, cx, cy, w, h parts vals [float(cx), float(cy), float(w), float(h)] if any(v 0 or v 1 for v in vals): issues.append(f{lf} 第{i1}行坐标越界: {vals}) if issues: for msg in issues[:20]: print(msg) print(f共发现 {len(issues)} 处问题) else: print(标注格式检查通过) check_yolo_labels(./labels/train, ./images/train)这段脚本做三件事确认每个标注文件都有对应图片、确认每行是 5 个字段、确认坐标在 0~1 范围内。参数上label_dir和img_dir分别指向标注和图片目录训练集和验证集要各跑一次。如果检查出问题最常见的是坐标没归一化——有些转换脚本忘了除以图片宽高导致坐标全是几百的整数YOLOv8 会直接报错或训出垃圾模型。2.2 把 VOC 和 LabelMe 标注转成 YOLO 格式的两个脚本如果你手头是 VOC 格式的 XML用下面这个脚本批量转换。核心逻辑是读 XML 里的bndbox算出中心点和宽高再除以图片尺寸归一化。import xml.etree.ElementTree as ET import os from PIL import Image def voc_to_yolo(xml_dir, img_dir, out_dir, class_map): VOC XML 转 YOLO txt class_map: {person: 0} 类别名到索引的映射 os.makedirs(out_dir, exist_okTrue) for xml_file in os.listdir(xml_dir): if not xml_file.endswith(.xml): continue tree ET.parse(os.path.join(xml_dir, xml_file)) root tree.getroot() # 找对应图片拿尺寸 img_name os.path.splitext(xml_file)[0] img_path None for ext in [.jpg, .jpeg, .png]: p os.path.join(img_dir, img_name ext) if os.path.exists(p): img_path p break if img_path is None: print(f跳过无对应图片: {xml_file}) continue w, h Image.open(img_path).size lines [] for obj in root.findall(object): cls_name obj.find(name).text if cls_name not in class_map: continue bbox obj.find(bndbox) xmin float(bbox.find(xmin).text) ymin float(bbox.find(ymin).text) xmax float(bbox.find(xmax).text) ymax float(bbox.find(ymax).text) cx (xmin xmax) / 2.0 / w cy (ymin ymax) / 2.0 / h bw (xmax - xmin) / w bh (ymax - ymin) / h lines.append(f{class_map[cls_name]} {cx:.6f} {cy:.6f} {bw:.6f} {bh:.6f}) with open(os.path.join(out_dir, img_name .txt), w) as f: f.write(\n.join(lines)) voc_to_yolo(./VOC/Annotations, ./VOC/JPEGImages, ./labels/train, {person: 0})参数说明class_map是类别映射行人检测通常只有一类所以写{person: 0}即可如果你的数据集里行人有多个子类比如pedestrian、people要么合并成 0要么按需扩展。out_dir输出的 txt 文件名必须和图片名一致YOLOv8 靠文件名配对差一个字符都找不到。LabelMe 的 JSON 转换思路类似区别在于坐标存在shapes数组里且 LabelMe 默认存的是绝对坐标同样需要除以图片宽高。转换完记得用 2.1 的检查脚本过一遍别跳过这步。2.3 数据集划分与 data.yaml 的四个必填字段YOLOv8 训练靠一个data.yaml描述数据集结构四个字段缺一不可字段含义示例path数据集根目录./datasets/persontrain训练集图片路径相对 pathimages/trainval验证集图片路径images/valnames类别索引到名称的映射{0: person}常见做法是按 8:1:1 划分训练、验证、测试集。划分脚本用random.shuffle打乱后切片即可注意图片和标注要同步移动别只移了图片忘了 txt。names字段的索引必须从 0 开始连续如果你有多个类别写成{0: person, 1: car}这种字典形式YOLOv8 会自动解析。提示验证集不能和训练集有重叠图片否则 mAP 会虚高看起来训得很好实际一上真实场景就翻车。3. 训练 YOLOv8 行人检测模型参数怎么设、曲线怎么看3.1 环境配置与最小训练命令环境这块CPU 版本和 GPU 版本差别很大。如果你只是跑通流程、数据量不大CPU 也能训只是慢。常见做法是用 conda 建一个干净环境conda create -n yolov8 python3.10 -y conda activate yolov8 pip install ultralytics # GPU 版本需要先装对应 CUDA 的 torch再装 ultralytics装完后用yolo checks确认环境它会打印 torch 版本、CUDA 是否可用。如果显示CPU说明没装 GPU 版 torch训练会走 CPU速度差几十倍。训练命令最小化到一行yolo detect train data./data.yaml modelyolov8n.pt epochs100 imgsz640 batch16这里modelyolov8n.pt是官方预训练权重n 是最小模型速度快但精度低行人检测如果场景复杂建议从yolov8s.pt或yolov8m.pt起步。imgsz640是输入分辨率行人目标通常不大640 够用如果小目标多可以提到 960但显存和速度会明显变化。batch16在 8G 显存上比较稳显存小就降到 8 或 4。3.2 行人检测必调的五个训练参数YOLOv8 默认参数是通用目标检测的配置行人检测有几个参数需要针对性调整epochs默认 100行人数据集如果只有几千张100 轮够收敛数据上万可以加到 200~300配合早停。patience早停耐心值默认 50。如果验证集 mAP 连续 50 轮不涨就停省时间。小数据集建议设 20~30。lr0初始学习率默认 0.01。行人检测微调预训练模型时0.001~0.01 都行太大容易震荡。close_mosaic默认最后 10 轮关闭 mosaic 增强。行人检测里 mosaic 会把四张图拼一起可能让行人变得过小如果发现小目标漏检多可以提前关闭比如设close_mosaic20。classes如果你只想检测行人而数据集里还有其他类别用classes0限定只训行人这一类避免其他类别干扰。训练启动后终端会实时打印 loss 和 mAP。重点看三个指标box_loss是否稳定下降、mAP50是否上升、mAP50-95是否跟着涨。如果box_loss下降但mAP不涨多半是过拟合或者验证集分布和训练集差太多。3.3 用损失曲线和混淆矩阵判断模型好坏训练完在runs/detect/train/下会生成一堆图其中results.png是损失和 mAP 曲线confusion_matrix.png是混淆矩阵。看曲线有个血泪经验如果训练 loss 一直降、验证 loss 先降后升说明过拟合了要么加数据要么加增强要么减模型复杂度。混淆矩阵里如果行人被大量判成背景说明漏检严重可能是标注框太小、或者imgsz不够。from ultralytics import YOLO import matplotlib.pyplot as plt # 加载训练好的模型做一次验证 model YOLO(runs/detect/train/weights/best.pt) metrics model.val(data./data.yaml, imgsz640, batch16) print(fmAP50: {metrics.box.map50:.4f}) print(fmAP50-95: {metrics.box.map:.4f}) print(f精确率: {metrics.box.mp:.4f}) print(f召回率: {metrics.box.mr:.4f})这段代码加载best.pt跑验证集输出四个核心指标。mAP50是 IoU 阈值 0.5 时的平均精度行人检测一般能到 0.85 以上算不错mAP50-95更严格0.5~0.95 多个阈值平均通常比 mAP50 低 0.15~0.25。如果召回率明显低于精确率说明漏检多优先查标注质量和conf阈值。4. 图形化系统把训练好的模型接上界面4.1 用 PyQt5 搭一个最小可用的检测界面图形化系统这块常见做法是 PyQt5 或 Gradio。PyQt5 适合做桌面端Gradio 适合快速出网页 demo。这里给一个 PyQt5 的最小骨架能选图片、能显示检测结果、能调置信度阈值。import sys from PyQt5.QtWidgets import (QApplication, QMainWindow, QPushButton, QLabel, QVBoxLayout, QWidget, QFileDialog, QSlider) from PyQt5.QtGui import QPixmap, QImage from PyQt5.QtCore import Qt from ultralytics import YOLO import cv2 class DetectApp(QMainWindow): def __init__(self): super().__init__() self.model YOLO(runs/detect/train/weights/best.pt) self.conf 0.25 self.init_ui() def init_ui(self): self.setWindowTitle(YOLOv8 行人检测系统) central QWidget() layout QVBoxLayout() self.img_label QLabel(请选择图片) self.img_label.setAlignment(Qt.AlignCenter) btn QPushButton(选择图片) btn.clicked.connect(self.load_image) self.slider QSlider(Qt.Horizontal) self.slider.setRange(5, 95) self.slider.setValue(25) self.slider.valueChanged.connect(self.update_conf) layout.addWidget(self.img_label) layout.addWidget(btn) layout.addWidget(self.slider) central.setLayout(layout) self.setCentralWidget(central) def update_conf(self, val): self.conf val / 100.0 def load_image(self): path, _ QFileDialog.getOpenFileName(self, 选图片, , Images (*.jpg *.png)) if not path: return results self.model.predict(path, confself.conf, imgsz640) annotated results[0].plot() # 返回带框的 numpy 数组 rgb cv2.cvtColor(annotated, cv2.COLOR_BGR2RGB) h, w, ch rgb.shape qimg QImage(rgb.data, w, h, ch * w, QImage.Format_RGB888) self.img_label.setPixmap(QPixmap.fromImage(qimg).scaled( 800, 600, Qt.KeepAspectRatio)) app QApplication(sys.argv) win DetectApp() win.show() sys.exit(app.exec_())逻辑说明YOLO(...)加载训练好的权重predict做推理results[0].plot()直接返回画好框的图省去手动画框。滑块控制conf阈值值越小框越多但误检也越多。参数上imgsz640要和训练时一致否则精度会掉conf0.25是常用起点行人检测如果误检多就提到 0.4~0.5漏检多就降到 0.15。4.2 视频流检测与帧率控制图片检测跑通后接视频或摄像头是自然延伸。核心改动是把predict换成循环读帧每帧推理一次。但这里有个性能坑YOLOv8 单帧推理在 CPU 上可能 200ms 以上视频会卡成幻灯片。常见做法是跳帧处理或者用streamTrue让 ultralytics 内部做批处理。cap cv2.VideoCapture(0) # 0 是摄像头也可以传视频路径 while cap.isOpened(): ret, frame cap.read() if not ret: break results model.predict(frame, conf0.3, imgsz640, verboseFalse) annotated results[0].plot() cv2.imshow(Person Detection, annotated) if cv2.waitKey(1) 0xFF ord(q): break cap.release() cv2.destroyAllWindows()verboseFalse关掉每帧的日志打印否则终端会刷屏。如果帧率还是低把imgsz降到 416 或 320精度会掉一些但速度明显提升。GPU 环境下这个循环能跑到 30FPS 以上CPU 环境建议只做图片检测视频流体验很差。4.3 界面里加一个「导出结果」按钮的完整逻辑演示系统通常需要把检测结果存下来。加一个导出按钮逻辑是把当前带框的图存到本地同时把检测框坐标写进 CSV。import csv from datetime import datetime def export_result(self, annotated, boxes): 保存带框图片和检测框坐标 ts datetime.now().strftime(%Y%m%d_%H%M%S) cv2.imwrite(foutput/{ts}.jpg, annotated) with open(foutput/{ts}.csv, w, newline) as f: writer csv.writer(f) writer.writerow([x1, y1, x2, y2, conf, cls]) for box in boxes: x1, y1, x2, y2 box.xyxy[0].tolist() conf float(box.conf[0]) cls int(box.cls[0]) writer.writerow([x1, y1, x2, y2, conf, cls])boxes从results[0].boxes拿每个 box 有xyxy、conf、cls三个属性。导出 CSV 的好处是后续可以做统计比如统计某段视频里行人出现次数、平均置信度这些数据拿去做汇报比截图有说服力。5. 避坑与排查行人检测从训练到部署的五个翻车现场5.1 训练 loss 正常但 mAP 一直是 0现象终端里box_loss在降但mAP50始终是 0 或者极低。原因通常是data.yaml里的names和标注文件里的类别索引对不上。比如标注里写的是0但names写成了{1: person}YOLOv8 找不到类别 0 的映射所有预测都被判为错误。解决打开一个标注 txt看第一列数字再对照data.yaml的names键确保完全一致。另一个可能是验证集路径写错YOLOv8 找不到验证图片mAP 自然算不出来。5.2 显存爆了CUDA out of memory 的三种降级方案现象训练启动几秒后报CUDA out of memory。原因无非三个batch太大、imgsz太大、模型太大。解决按优先级来先把batch减半比如 16 降到 8还不行就把imgsz从 640 降到 416再不行换更小的模型yolov8s换yolov8n。另外workers设太大会导致数据加载占用显存设成 4 或 2 试试。如果用的是 1660Ti 这类 6G 显存卡yolov8n batch8 imgsz416是比较稳的组合。5.3 推理时框太多或太少conf 和 iou 阈值的联动现象检测结果要么满屏框要么一个行人都框不出来。原因conf阈值和iou阈值没配合好。conf控制置信度低于多少的框被丢弃iou控制重叠框合并。行人密集场景iou设太高比如 0.7会导致重叠的行人被合并成一个框设太低比如 0.3又会把一个人的多个框都留下。常见做法是conf0.3, iou0.5起步然后根据实际效果微调。如果漏检多先降conf到 0.15 看能不能框出来能框出来说明是阈值问题框不出来就是模型没学好。5.4 图形界面卡死主线程做推理的后果现象点「选择图片」后界面无响应几秒后才出结果视频流直接卡死。原因PyQt5 的主线程负责界面刷新推理是耗时操作放在主线程里界面就冻结了。解决把推理放到QThread子线程里通过信号槽把结果传回主线程更新界面。如果只是图片检测、能接受一两秒等待可以不改但视频流必须用子线程否则体验没法看。5.5 换自己的数据集后效果暴跌现象用压缩包自带数据集训出来效果不错换成自己拍的行人数据后 mAP 掉到 0.3。原因通常是分布差异自带数据集可能是白天、正面、大目标你自己的数据是夜间、侧面、小目标。解决先检查标注质量用 2.1 的脚本跑一遍然后在自己的数据上微调不要从零训加载预训练权重lr0调小到 0.001epochs加到 150如果夜间场景多训练时开hsv_v增强把亮度扰动范围调大。6. 把行人检测做到能交付三个进阶技巧和我的习惯第一个技巧是用 TensorRT 或 ONNX 加速推理。PyTorch 权重直接推理在 GPU 上已经能用但如果要部署到边缘设备或者追求极致帧率导出 ONNX 再转 TensorRT 能提速 2~3 倍。导出命令一行yolo export modelbest.pt formatonnx opset12 simplifyTruesimplifyTrue会做图优化去掉冗余算子。导出后在推理时用onnxruntime加载CPU 上也能比原生 PyTorch 快一些。注意导出时的imgsz要和推理时一致否则精度会掉。第二个技巧是用跟踪代替逐帧检测。视频流里如果每帧都独立检测行人 ID 会跳来跳去统计人数时重复计数。加一个 ByteTrack 或 BoT-SORT 跟踪器YOLOv8 内置支持只要在predict里加trackerbytetrack.yaml就能给每个行人分配稳定 ID。这样统计「画面里有多少人」才准确做客流统计、区域停留时长都靠这个。第三个技巧是建立自己的验证集。压缩包自带的验证集只能说明模型在它的数据上表现如何不能说明在你的场景里表现如何。我一般会从实际场景里抽 100~200 张图人工标一遍作为「金标准」验证集。每次改完模型或参数都在这套验证集上跑一次看 mAP 和召回率的变化。这套验证集不用大但必须代表真实场景否则调参就是玄学。最后一个习惯训练前先跑 1 个 epoch 看流程通不通。很多人一上来就设 300 epochs结果跑了半小时才发现标注格式错了白等。我一般先epochs1跑一遍确认数据加载、loss 计算、验证、保存权重全流程没问题再改成正式轮数。这个习惯帮我省过很多次后悔药。希望帮到你。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联
返回资讯列表 →