资讯动态

YOLOv8行人检测系统实战:从数据处理到PyQt界面开发

发布时间:2026/10/2 2:37:53 来源:尧图企业网站定制
简介YOLOV8行人检测系统是一套完整的目标检测方案面向开发者和研究者适用于街道监控、交通流量分析、自动驾驶辅助等场景。压缩包共2000个文件、456.56MB以txt标注与训练结果文件为主另含Python脚本含PyQt界面、PDF环境配置教程及Markdown说明文档。资源附带数千张已用LabelImg标注的JPG行人图片类别为person并给出基于该数据训练好的YOLOV8权重mAP达90%以上可省去采集与标注工作直接推理或微调。配有的PyQt图形界面能加载图片或视频流实时展示检测效果多份教程PDF覆盖YOLO系列环境配置与运行步骤便于从零搭建环境并跑通训练、预测流程。目前已有817人学习适合需要高质量行人数据集和完整工程代码的入门及进阶用户。1. 先拆一句大实话这个标题到底在说什么YOLOV8行人检测数据集pyqt界面拆开看就是三件事用YOLOv8训练一个能框住行人的模型给它准备一份能用的行人数据再套一个PyQt写的界面让人能点鼠标操作。很多第一次接触这个组合的人以为难点在YOLOv8本身实际跑一遍就会发现模型训练反而是最省心的环节——真正把项目拖死的往往是数据集标注错位和PyQt界面卡顿这两件事。这类项目最常见的真实场景是毕设、课设、实验室的室内监控demo或者公司里“先做个原型看看效果”的小需求。目标不是刷COCO榜单而是让模型在特定场景下能稳定框住人同时界面能开摄像头、能拖图片、能显示检测结果不崩不卡。适合的人群也很明确已经会用Python跑过至少一个深度学习demo但没完整走过“数据→训练→界面”全流程的开发者。我下面按自己做过的一个类似项目的顺序来讲选型与装环境、数据集处理、PyQt界面集成、踩坑记录、最后收在验证和部署上。你跟着走完得到的不是一堆零散代码而是一套能跑通的最小闭环。2. 选型与环境YOLOv8为什么是行人检测的默认选项以及Ubuntu/Windows下把环境装到能用2.1 选型理由从yolov8网络结构图看为什么它天然适合行人检测YOLOv8是Ultralytics在2023年初放出的目标检测框架继承了YOLOv5的工程化思路又把anchor-free和动态标签分配这两件事做了进去。它的网络结构图在网上随处可见但大部分人只看图不看门道主干C2f模块把梯度分流做得更细Neck部分用PAN-FPN做多尺度融合Head部分直接预测物体中心和宽高不再需要预先设定anchor框。这对行人检测意味着什么行人是典型的“高瘦目标”在不同镜头下尺度差异极大——监控画面里远处的人可能只有20像素高近处的人能撑满半个画面。YOLOv8的多尺度融合把浅层细节和深层语义拼在一起小目标漏检比YOLOv5明显少。另外anchor-free设计让模型不再受预设anchor尺寸的束缚遇到特殊拍摄角度的行人比如俯视监控也不会因为anchor匹配不上而学不动。你不需要改任何结构直接用默认配置就能在行人数据上拿到可用结果。选型时还有个容易被忽略的细节Ultralytics的包和ultralytics/yolov8这个老仓库是两个东西。现在推荐直接装ultralytics这个Python包模型权重也统一走这个入口加载。网上搜“yolov8哪里下载”“yolov8下载”会翻出一堆过时链接其实官方权重只需要一行代码就能拉下来根本不用手动下载。2.2 Ubuntu 20.04 CPU环境搭建最低成本的起步方式如果你的机器没有NVIDIA显卡或者显卡显存不够跑训练不要直接放弃。用CPU版PyTorch先跑通推理和界面联调是成本最低的起步方式。网上搜“ubuntu20.04搭建yolov8环境cpu版本”能找到很多教程但不少是让新手装CUDA、装显卡驱动那对CPU环境毫无意义。干净的最小流程如下# 安装miniconda如果已装conda可跳过 wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh bash Miniconda3-latest-Linux-x86_64.sh -b -p $HOME/miniconda3 eval $($HOME/miniconda3/bin/conda shell.bash hook) # 创建独立环境避免把系统Python搞乱 conda create -n yolo python3.10 -y conda activate yolo # 安装CPU版PyTorch pip install torch torchvision --index-url https://download.pytorch.org/whl/cpu # 安装ultralytics和opencv pip install ultralytics opencv-python代码里的关键参数说明python3.10YOLOv8官方支持3.8到3.113.10是最稳的选择。别用3.12有些旧版onnxruntime和pyqt5的依赖在3.12上会编译失败。--index-url https://download.pytorch.org/whl/cpu这是CPU版PyTorch的官方下载源不加这个参数默认装CUDA版装了也用不了。opencv-python后面PyQt界面读摄像头和图片预处理全靠它ultralytics的依赖里虽然带opencv但版本可能偏旧显式装一遍更放心。装完后用一行命令验证环境可用python -c from ultralytics import YOLO; model YOLO(yolov8n.pt); results model(https://ultralytics.com/images/bus.jpg); print(len(results))能输出结果说明环境通了一半。另一半是确认PyQt能跑起来pip install pyqt5 python -c from PyQt5.QtWidgets import QApplication; app QApplication([]); print(Qt OK)2.3 Windows环境的差异有NVIDIA显卡时怎么省时间Windows上装环境和Ubuntu大同小异但如果你的电脑是NVIDIA显卡比如网上常搜到的GTX 1660Ti建议直接装CUDA版PyTorch训练速度能快一个量级。GTX 1660Ti显存6GB跑yolov8n或yolov8s完全没问题yolov8m会比较吃力yolov8l基本不用想。conda create -n yolo python3.10 -y conda activate yolo pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 pip install ultralytics opencv-python pyqt5cu118对应CUDA 11.8和配套的cuDNN驱动版本只要不低于452.39就能跑。这里要提醒一个血泪经验别去项目里折腾系统的CUDA环境。PyTorch是自带CUDA runtime的只要显卡驱动装上pip装的torch直接就能用不需要你手动装任何CUDA toolkit。很多人翻车在手动装了CUDA之后又改环境变量把系统搞坏最后重装系统才救回来。环境装完先跑一次推理确认GPU真的在工作而不是静默走了CPUpython -c from ultralytics import YOLO import torch print(CUDA available:, torch.cuda.is_available()) model YOLO(yolov8n.pt) results model(https://ultralytics.com/images/bus.jpg, device0) 如果CUDA available显示False先查显卡驱动版本再看是不是装了CPU版的torch。这条命令能帮你省掉后面至少一个小时的困惑时间。3. 数据集处理把行人检测数据从标注格式到训练文件一段能直接跑的清洗与转换流程3.1 行人检测数据集怎么选网上那些“数据集下载”哪个能用网上搜“行人检测数据集”出来一堆结果但真的能直接用于YOLOv8训练的并不多。原因很简单YOLOv8训练需要的是YOLO格式的txt标注文件而很多公开数据集给的是VOC的xml或者COCO的json必须自己做格式转换而且转换过程中坑特别多。主流选择就三个COCO的person类别、WiderPerson、以及CityPersons。COCO的person类别最干净但背景复杂适合练通用能力WiderPerson是专门的行人检测数据集密集场景多但标注质量参差不齐有些框的边界偏松CityPersons是街拍场景行人尺度变化大适合监控场景。如果你的目标是实验室室内或园区监控我建议从COCO里抽person类别起步数据量够大标注质量也最稳定——用“coco2017person”作为筛选条件能抽出大约26万张含行人的图片。真正的坑在标注格式转换。网上经常有人直接说“把xml转成txt就行”但voc格式里的bndbox坐标是左上角和右下角而YOLO格式需要的是归一化后的中心点坐标和宽高。这个转换反直觉的地方在于新手容易忘了归一化或者归一化时除以了错误的图像尺寸。图像尺寸必须以标注文件对应的原始图片为准不能随便取一个固定值。更隐蔽的坑是类别编号。YOLOv8的标签文件第一列是类别id如果你的数据集只有行人这一类那所有行都应该是0。但很多公开数据集的xml里person类别的name字段是person如果直接套用现成脚本没有把name映射成id生成的txt第一列就会是字符串person训练时直接报错。3.2 手写VOC格式转YOLO格式的脚本转换脚本与四个边界坑下面的脚本是我自己项目里在用的处理VOC格式的xml标注并生成YOLO格式的txt文件。它把这个领域里几乎所有的坑都踩了一遍import os import xml.etree.ElementTree as ET from pathlib import Path def voc_to_yolo(xml_dir, img_dir, out_dir, class_names): xml_dir: VOC标注文件目录 img_dir: 对应图片目录用于读取真实宽高 out_dir: 输出txt标签目录 class_names: 类别名称列表[person]表示只保留person类 os.makedirs(out_dir, exist_okTrue) class_to_id {name: idx for idx, name in enumerate(class_names)} for xml_file in Path(xml_dir).glob(*.xml): tree ET.parse(str(xml_file)) root tree.getroot() # 读取图片真实尺寸避免归一化出错 img_file xml_file.stem .jpg img_path os.path.join(img_dir, img_file) if not os.path.exists(img_path): print(f[警告] 图片不存在: {img_path}跳过 {xml_file.name}) continue # 从标注文件里读宽高大多数情况下和图片实际尺寸一致 size root.find(size) img_w int(size.find(width).text) img_h int(size.find(height).text) lines [] for obj in root.iter(object): name obj.find(name).text if name not in class_to_id: continue # 只保留指定类别 # 解析bndbox注意VOC坐标可能越界 box obj.find(bndbox) xmin max(float(box.find(xmin).text), 0) ymin max(float(box.find(ymin).text), 0) xmax min(float(box.find(xmax).text), img_w) ymax min(float(box.find(ymax).text), img_h) # 过滤掉无效框宽高为0或坐标颠倒 if xmax xmin or ymax ymin: print(f[警告] 无效框: {xml_file.name} 中 {name} 坐标 ({xmin},{ymin},{xmax},{ymax})) continue # VOC是xyxy格式YOLO是归一化xywh格式 x_center (xmin xmax) / 2 / img_w y_center (ymin ymax) / 2 / img_h box_w (xmax - xmin) / img_w box_h (ymax - ymin) / img_h # 限制在(0,1)范围内防止训练时越界 x_center min(max(x_center, 0), 1) y_center min(max(y_center, 0), 1) box_w min(max(box_w, 0), 1) box_h min(max(box_h, 0), 1) lines.append(f{class_to_id[name]} {x_center:.6f} {y_center:.6f} {box_w:.6f} {box_h:.6f}) if not lines: print(f[跳过] {xml_file.name} 没有目标类别) continue out_file os.path.join(out_dir, xml_file.stem .txt) with open(out_file, w) as f: f.write(\n.join(lines)) print(转换完成) voc_to_yolo(path/to/xml, path/to/images, path/to/labels, [person])代码里四个必须注意的边界情况坐标越界VOC标注里偶尔会出现xmax比图片宽度还大的情况必须clamp回图片范围内否则训练时YOLOv8计算loss会出NaN。宽高为0的框有些标注工具会生成退化的框比如xminxmax。这类框在计算IoU时直接崩溃要么跳过要么修成最小尺寸。类别过滤如果数据集中混了person和rider而你的任务只需要person脚本里if name not in class_to_id这行会直接丢掉rider。很多现成脚本不区分这两个类导致模型把骑摩托的人也当行人误检率飙升。图片缺失标注文件存在但图片被删这种情况经常发生在从网盘下载数据集时。训练时YOLOv8会直接跳过缺失图片的标签文件但这个跳过是静默的你根本不知道。脚本里打印警告能帮你提前发现数据不完整。3.3 数据检查训练前必须做的几步验证数据转换完不代表就能直接训练。我见过太多人把标注转完就往train目录一丢就开始训练结果训练两天发现模型什么都学不会回头查数据才发现标签和图片对不上。训练前花20分钟做数据检查能省掉两天的返工。第一件事是统计标签分布确认每张图平均有多少个目标、有没有空的标签文件。全空的标签文件会让YOLOv8在训练时报错或者在loss计算时产生NaN。from pathlib import Path import collections label_dir Path(datasets/person/labels) file_count 0 empty_count 0 box_counts [] for txt_file in label_dir.glob(*.txt): file_count 1 lines txt_file.read_text().strip().split(\n) if lines [] or not lines: empty_count 1 print(f[空标签] {txt_file.name}) continue box_counts.append(len(lines)) print(f标签文件总数: {file_count}) print(f空标签文件数: {empty_count}) print(f平均每图目标数: {sum(box_counts) / len(box_counts):.2f}) print(f目标数分布: 最少{min(box_counts)}个, 最多{max(box_counts)}个)第二件事是抽查图片和标签的匹配情况。代码不好写但有一个直观的方法用YOLOv8自带的plot功能画出来看它会把标注框直接绘制在图片上。from ultralytics import YOLO model YOLO(yolov8n.yaml) # 只加载结构不加载权重 results model.predict( sourcedatasets/person/images/train, projectdebug_vis, namecheck_labels, saveTrue, conf0.0, # 显示所有预测不看置信度 devicecpu )这里的conf0.0是唯一的参数技巧。默认conf是0.25会过滤掉低置信度框但没训练过的模型预测是随机的过滤后什么都看不到。设成0.0之后依赖模型结构自带的anchor解码会把标注框的位置画出来帮你肉眼检查标签坐标是否对准了人。如果你发现画的框偏左或偏右半个人身说明转换脚本里坐标系搞错了这是最常见的标注错误。4. PyQt界面集成把YOLOv8模型嵌进Qt界面线程分离与QImage转换是全部关键4.1 PyQt界面为什么经常卡死检测放主线程是最大的坑PyQt界面卡顿这个问题网上搜“ui界面卡顿”“pyqt界面卡死”能出来一堆帖子但结论高度一致不要在GUI线程里跑耗时操作。YOLOv8前向推理一次即使是最小的yolov8n在CPU上也要100到300毫秒。如果在主线程里跑检测界面就会在这个时间段内完全无响应用户拖窗口、点按钮都没反应。更隐蔽的问题是视频流检测。如果用QTimer每30毫秒触发一次帧读取然后在回调里同步执行推理帧率会直接降到3到5帧而且界面越来越卡。原因不是推理本身慢而是QTimer的回调在拖累整个事件循环。我的做法是标准的生产级方案把检测放进QThread用信号和槽把结果传回主线程。这不算什么高级技巧但很多人第一次写PyQt的线程模型时会对信号槽的线程归属产生误解信号槽的连接方式决定了回调在哪个线程执行。默认的AutoConnection如果发送者和接收者不在同一线程会自动变成QueuedConnection也就是消息队列异步传递。这意味着你在子线程里emit一个信号主线程的槽函数会在下一次事件循环时执行不会阻塞子线程。子线程负责读帧和推理主线程只负责把推理结果画到QLabel上两边通过信号交换数据互不阻塞。4.2 用QThread实现实时行人检测界面完整可跑的最小代码下面这个代码是我项目里界面模块的简化版但所有关键点都在。它实现了摄像头或视频文件的实时行人检测并用信号把结果帧传回主线程显示import sys import cv2 import numpy as np from PyQt5.QtCore import QThread, pyqtSignal, Qt from PyQt5.QtGui import QImage, QPixmap from PyQt5.QtWidgets import QApplication, QLabel, QPushButton, QVBoxLayout, QWidget from ultralytics import YOLO class DetectThread(QThread): 检测线程负责读视频帧、YOLOv8推理、把结果帧通过信号传回主线程 change_pixmap pyqtSignal(QImage) fps_signal pyqtSignal(float) def __init__(self, model_pathyolov8n.pt, source0, parentNone): super().__init__(parent) self.model YOLO(model_path) self.source source self.running True def run(self): cap cv2.VideoCapture(self.source) # 参数1跳帧读取避免处理重复帧 frame_interval 2 frame_count 0 import time while self.running: ret, frame cap.read() if not ret: break if frame_count % frame_interval ! 0: frame_count 1 continue frame_count 1 start_time time.time() results self.model.predict(frame, imgsz640, conf0.4, verboseFalse) # 参数2在numpy数组上绘制结果 annotated results[0].plot() elapsed time.time() - start_time fps 1.0 / max(elapsed, 1e-6) self.fps_signal.emit(fps) # 将BGR numpy数组转成RGB QImage rgb_image cv2.cvtColor(annotated, 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) qt_image qt_image.copy() # 参数3强制拷贝防止data指针失效 self.change_pixmap.emit(qt_image) cap.release() def stop(self): self.running False self.wait() class MainWindow(QWidget): def __init__(self): super().__init__() self.label QLabel(摄像头画面显示区) self.label.setAlignment(Qt.AlignCenter) self.btn_start QPushButton(开始检测) self.btn_stop QPushButton(停止) layout QVBoxLayout() layout.addWidget(self.label) layout.addWidget(self.btn_start) layout.addWidget(self.btn_stop) self.setLayout(layout) self.thread None self.btn_start.clicked.connect(self.start_detect) self.btn_stop.clicked.connect(self.stop_detect) def start_detect(self): if self.thread is None: self.thread DetectThread(model_pathyolov8n.pt, source0) self.thread.change_pixmap.connect(self.update_frame) self.thread.fps_signal.connect(self.update_fps) self.thread.start() def stop_detect(self): if self.thread is not None: self.thread.stop() self.thread None def update_frame(self, qt_image): self.label.setPixmap(QPixmap.fromImage(qt_image).scaled( self.label.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation)) def update_fps(self, fps): self.setWindowTitle(fYOLOv8行人检测 - {fps:.1f} FPS) def closeEvent(self, event): self.stop_detect() event.accept() if __name__ __main__: app QApplication(sys.argv) win MainWindow() win.resize(960, 640) win.show() sys.exit(app.exec_())代码里的关键决策和参数说明frame_interval2跳帧策略处理奇数帧跳过偶数帧。CPU上推理一次200毫秒跳帧能让画面看起来连续同时FPS显示更稳定。显卡强的电脑可以改成1。conf0.4置信度阈值。行人检测场景里0.4是我调试下来的折中值。太低了会把柱子、雕塑当人误检框一堆太高了会漏掉远处的小行人。qt_image.copy()这行是很多PyQtOpenCV教程漏掉的。QImage直接用rgb_image.data构造时引用的是numpy数组的内存。如果numpy数组在下一次循环被重新分配这里的QImage就成了野指针界面显示的花屏或崩溃全都由这一行引起。self.thread.wait()停止线程时等它完全结束避免直接关闭窗口时线程还在跑Qt会报QThread: Destroyed while thread is still running。4.3 静态图片检测与界面扩展把界面从“能用”做到“好用”视频检测是演示的核心但很多毕设或课设的验收场景反而更看重静态图片检测——老师在现场拖一张测试图片进界面点一下“检测”框立刻出来。这个功能实现起来非常轻量不需要额外开线程from PyQt5.QtWidgets import QFileDialog, QMessageBox from ultralytics import YOLO class MainWindow(QWidget): def __init__(self): # ... 省略与上节重复的部分 self.btn_open QPushButton(打开图片) self.btn_detect QPushButton(检测行人) self.model YOLO(yolov8n.pt) self.current_image_path None def open_image(self): path, _ QFileDialog.getOpenFileName(self, 选择图片, , 图片文件 (*.jpg *.png *.bmp)) if path: self.current_image_path path pixmap QPixmap(path) self.label.setPixmap(pixmap.scaled(self.label.size(), Qt.KeepAspectRatio)) def detect_image(self): if not self.current_image_path: QMessageBox.warning(self, 提示, 请先打开一张图片) return results self.model(self.current_image_path, conf0.4) annotated results[0].plot() rgb_image cv2.cvtColor(annotated, cv2.COLOR_BGR2RGB) h, w, ch rgb_image.shape qt_image QImage(rgb_image.data, w, h, ch * w, QImage.Format_RGB888).copy() self.label.setPixmap(QPixmap.fromImage(qt_image).scaled( self.label.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation))这两个方法加到前面的MainWindow里再绑定按钮就行。注意这里没有用predict()而是直接调用了model()这是ultralytics的快捷方式效果一样。静态检测还有个容易被忽略的点results[0].plot()返回的数组是RGB格式不是OpenCV传统的BGR。如果直接QImage不转cvtColor画面颜色会偏蓝偏红。这个坑在视频检测里也存在不过摄像头画面颜色偏一点不容易察觉静态图片对比原图就很明显。5. 避坑记录YOLOv8行人检测PyQt的5条高频翻车经验5.1 CPU推理速度慢到无法用界面卡成一帧一帧现象笔记本上跑yolov8s模型界面FPS不到2拖窗口都跟着卡。原因默认模型是yolov8s比yolov8n参数多一倍多。CPU上yolov8n推理在100到150毫秒yolov8s直接翻倍到300毫秒以上。PyQt界面没有用线程分离的话这个延迟会全部体现在事件循环里。解决第一步换yolov8n.pt权重先跑通再看效果第二步用我前面代码里的QThread方案把推理移出主线程第三步开启跳帧。如果这还卡考虑用ONNX Runtime替换PyTorch推理在CPU上通常能快30%到50%转换方法我放最后一章。5.2 训练完的模型什么都检测不到但训练loss确实下降了现象训练了100个epochloss曲线看起来很平滑但用训练好的权重测试一张图都检测不到行人。原因大概率是数据集标签文件有问题。最常见的原因是VOC转YOLO格式时坐标归一化出错——如果你看了图片尺寸而不是标注里的size字段或者坐标除以了错误的宽高生成的txt里所有框都是错的。训练loss下降是模型在拟合错误的坐标自然什么都学不会。解决按第3.3节的做法用conf0.0的可视化脚本画出标注框和图片叠加的效果。如果框是歪的、偏的、甚至跑到图外面去了转换脚本一定有bug。另一个快速验证方法是随便打开一个txt文件看里面的坐标是否在0到1之间任何超出这个范围的数值都说明转换有问题。5.3 摄像头画面颜色偏蓝检测框画上去后颜色像负片现象PyQt界面里显示的视频画面颜色整体偏蓝尤其红色物体变成紫色。原因OpenCV的cv2.VideoCapture读出来的帧是BGR顺序QImage默认是RGB顺序中间缺少cv2.COLOR_BGR2RGB转换。很多人只对结果帧做了转换但忘了对原始帧或标注后的帧做同样的转换。解决在创建QImage之前始终先cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)。注意YOLOv8的plot()方法返回的已经是RGB格式不需要再转一次BGR再转回RGB那种二次转换会让颜色偏移更严重。5.4 关闭窗口时程序崩溃报QThread destroyed错误现象点击窗口关闭按钮控制台报QThread: Destroyed while thread is still running然后程序直接崩溃退出。原因界面关闭时主窗口的析构函数把QThread对象销毁了但线程还在运行循环。PyQt不会自动等你线程结束这个错误是零容忍的只要你碰到过就忘不掉。解决接管closeEvent在事件里先置线程的running标志为False然后调用self.thread.wait()等线程完全退出再accept()允许窗口关闭。关闭窗口的行为从“立刻销毁”变成“先停止线程再销毁窗口”这是Qt线程最好的收尾方式。5.5 训练时显存溢出换成小模型后精度掉得不能看现象显卡是GTX 1660Ti 6GB显存跑yolov8m直接OOM显存溢出换成yolov8n能跑但检测精度明显下降小目标全漏。原因yolov8n在COCO上的mAP比yolov8m低十几个点这种差距在行人这类小目标场景上被放大因为行人属于小目标对特征提取能力要求更高。解决不是硬上大模型而是减少训练资源的浪费。用yolov8s配合batch-size8、imgsz640通常能在6GB显存上跑精度比yolov8n好很多。另一个思路是开启混合精度训练ampTrueultralytics默认开启能省将近一半显存。如果仍然OOM调低imgsz到512但测试时用640这种训练和测试尺寸不一致的做法是YOLO系列常用的提精度技巧。6. 进阶做法验证训练效果再谈向前一步的部署优化6.1 怎么确认训练真的有效损失曲线与PR曲线的配合使用训练结束后ultralytics会在训练目录下生成results.png和confusion_matrix.png大多数人只看一眼loss曲线降没降就结束了但这远远不够。我在项目里实际用的验证方法是三件套第一看results.png里val/box_loss是否在最后10个epoch还在下降如果已经平台化说明模型收敛了第二看confusion_matrix.png的FN假阴性格子行人检测最怕漏检FN占比超过20%就要考虑加大数据量或调低置信度阈值第三拿几张完全没有参与训练的场景图做手工测试看模型在新环境下的泛化能力。更细的验证方式是看precision-recall曲线。ultralytics会在验证阶段打印每个类别的mAP0.5和mAP0.5:0.95前者是你的模型在宽松IoU阈值下的精度后者是严格IoU下的综合精度。行人检测场景里mAP0.5能到0.85就算不错mAP0.5:0.95到0.6就很好了。如果mAP0.5高但mAP0.5:0.95很低说明框的位置不够准常见原因是验证图片里行人尺度太小需要提高imgsz到960再测一次有时能涨两三个点。6.2 部署优化把模型转成ONNX或量化到rk3588这类板端如果你的目标不是只在电脑上跑demo而是要把行人检测部署到开发板网上常搜到“rk3588部署yolov8”这类需求第一步一定是导出ONNX。YOLOv8官方支持一行命令导出yolo export modelyolov8n.pt formatonnx imgsz640导出后可以用onnxruntime跑推理在CPU上通常比PyTorch快30%到50%import cv2 import numpy as np import onnxruntime as ort session ort.InferenceSession(yolov8n.onnx) input_name session.get_inputs()[0].name img cv2.imread(test.jpg) img cv2.resize(img, (640, 640)) img img[:, :, ::-1].transpose(2, 0, 1) # BGR转RGB并调整维度到CHW img np.ascontiguousarray(img, dtypenp.float32) img / 255.0 img np.expand_dims(img, axis0) outputs session.run(None, {input_name: img})导出和转换有几个实际参数要考虑imgsz必须和训练时一致否则精度掉得厉害如果目标设备是rk3588优先导出formatonnx再借助rknn-toolkit转成rknn格式不要直接把pt文件丢给板端推理框架。量化时优先用INT8量化行人检测这种单类别任务精度损失可以控制在2到3个点以内换来的是推理速度翻倍。这个取舍值不值取决于你的场景是放在园区门口还是放在室内研究——前者对误检容忍度高后者对漏检容忍度低建议先跑一版量化模型对比PR曲线用数据说话而不是拍脑袋。6.3 给界面加一个“布防区域”是验收时的加分项最后说一个小技巧把界面从“能看到人”升级成“能划区域布防”。做法不复杂在DetectThread的推理结果里加一帧像素级的ROI判断——如果行人的中心点落在用户画的矩形框内就在框上方用红色字提示“告警”否则用绿色框标“观察”。这个改动对验收效果提升非常明显因为它的确解决了真实需求园区监控不关心所有行人只关心闯入特定区域的人。实现上也不需要重写界面在MainWindow里用mousePressEvent记录两个坐标点把roi传给DetectThread即可。这个功能在毕设答辩里几乎是万能加分项因为你不只是在演示“模型能检测”而是在演示“技术能解决具体问题”。如果你要在这个方向上继续深入我建议先去尝试做小目标增强用SAHI切片辅助推理把大图切成小图分别检测对行人这类小目标密集场景的效果提升立竿见影。我自己的教训是一开始总想用更大的模型榨取精度后来发现把数据清洗干净、调整推理策略收益比换大模型高得多。希望帮到你。本文还有配套的精品资源点击获取

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

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

免费获取报价 →
↑