简介:这份摩托车与行人目标检测数据集面向交通监控、自动驾驶感知及智慧城市方向的算法开发者与研究人员,用于解决道路场景下两类关键目标的识别与定位问题。数据来源于实际道路监控,包含训练集937张、验证集158张,共1095张JPEG图片,覆盖多种光照与视角条件,标注采用YOLO格式的归一化边界框与类别标签,可直接兼容YOLOv5/v7/v8等主流框架。压缩包共2000个文件,以txt标注文件、jpg图像为主,另附yaml配置文件与docx说明文档,整体约62.91MB,目录结构清晰便于快速接入训练流程。目前已有124人学习下载。读者可借助该数据集完成交通流量统计、危险行为预警、行人聚集识别等模型的训练与验证,也可用于摩托车与行人互动行为的学术研究,为道路安全策略提供数据支撑。
1. 摩托车与行人目标检测数据集:从标注格式到 YOLO 训练,这条路值不值得走
城市道路场景里,摩托车和行人是两类最容易被模型混淆的目标。行人姿态多变、遮挡频繁,摩托车则存在细长结构、骑手与车身粘连的问题,两者在拥挤路段经常互相遮挡。如果你手头正好有一个「摩托车与行人目标检测数据集.zip」,想把它用起来训练一个能落地的检测模型,那这篇就是按一线实操顺序写的:先搞清楚这类数据集通常长什么样、标注格式怎么选,再一步步走通格式转换、划分、训练、评估,最后把踩过的坑摊开讲。适合刚拿到数据集的新手照着复现,也适合做过 YOLO 系列、想换到摩托车+行人这个细分场景的熟手看边界。目标检测这个方向不缺通用教程,缺的是针对具体数据集把参数和坑讲透的记录,下面按这个思路展开。
2. 摩托车与行人数据集的结构、标注格式与选型判断
2.1 拿到 zip 先别急着解压训练,先看清目录结构
一个目标检测数据集压缩包,解压后常见的组织方式无非几种:images/和labels/平行存放,或者JPEGImages/配Annotations/,再或者按train/val/test已经分好。摩托车与行人这类场景,图片多半来自道路监控或行车记录仪,分辨率不统一,长宽比偏宽。先跑一遍统计,比盲目开训省事得多。
# 解压后先看目录层级,别直接丢进训练脚本 unzip motorcycle_pedestrian.zip -d moto_ped cd moto_ped find . -maxdepth 2 -type d | head -30 # 统计图片数量和格式分布 find . -type f \( -iname "*.jpg" -o -iname "*.png" -o -iname "*.jpeg" \) | wc -l find . -type f -iname "*.jpg" | wc -l find . -type f -iname "*.png" | wc -l这段命令做三件事:确认目录层级、统计图片总数、看格式是否混杂。参数上没什么可调的,重点是-maxdepth 2别设太大,否则深层目录刷屏。如果发现图片格式既有 jpg 又有 png,后面训练前要统一,否则某些数据加载器会因为解码路径不一致报错。图片数量在几千到几万之间都正常,几百张的话要警惕过拟合。
2.2 标注格式决定你后面所有工具链的选择
目标检测标注格式主流就三种:Pascal VOC 的 XML、COCO 的 JSON、YOLO 的 txt。摩托车与行人数据集如果是从标注工具导出的,大概率是 VOC XML 或 COCO JSON。选哪种不是喜好问题,而是看你用哪套训练框架。用 Ultralytics 系(YOLOv5/v8/v11 这一脉)就必须转成 YOLO txt;用 MMDetection 或 Detectron2 则 COCO JSON 更顺。
| 格式 | 单文件结构 | 坐标表示 | 适配框架 |
|---|---|---|---|
| VOC XML | 每图一个 xml | 左上右下绝对像素 | 老版 TF、部分转换脚本 |
| COCO JSON | 全数据集一个 json | 左上宽高绝对像素 | MMDetection、Detectron2 |
| YOLO txt | 每图一个 txt | 归一化中心宽高 | Ultralytics 全系 |
判断依据很简单:打开一个标注文件看内容。XML 里有<bndbox>和<xmin>就是 VOC;JSON 里有annotations数组和bbox字段就是 COCO;txt 里每行五个数、后四个都在 0 到 1 之间就是 YOLO。摩托车与行人两类,类别名常见是motorcycle/motorbike和person/pedestrian,转换前务必把类别名统一,否则会出现同一类被拆成两个 ID 的玄学问题。
2.3 类别不平衡和骑手重叠,是这类数据集的先天特征
摩托车与行人数据集有个绕不开的特点:行人样本通常远多于摩托车,而且大量图片里骑手同时属于「人」和「车」两个框,两个框高度重叠。这不是标注错误,是场景本身如此。训练时如果直接按默认配置跑,模型会偏向行人这一类,摩托车的召回率上不去。常见做法是在损失里给摩托车类更高权重,或者在采样阶段对含摩托车的图片做上采样。这一点在选型阶段就要想清楚,别等训完发现摩托车漏检一片再回头改。
3. 把 VOC/COCO 标注转成 YOLO 格式:脚本、参数与四个边界坑
3.1 VOC XML 转 YOLO txt 的完整脚本
假设你的数据集是 VOC 格式,Annotations/放 xml,JPEGImages/放图。转换的核心是把绝对像素的左上右下坐标,换算成归一化的中心点加宽高。下面这个脚本我一般直接用,改路径就能跑。
import os import xml.etree.ElementTree as ET from PIL import Image # 类别映射:把数据集里的类别名统一成 0/1,顺序要和训练配置一致 CLASS_MAP = {"person": 0, "pedestrian": 0, "motorcycle": 1, "motorbike": 1} def convert_voc_to_yolo(xml_dir, img_dir, out_dir): os.makedirs(out_dir, exist_ok=True) 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() # 用图片真实尺寸做归一化,不能拿标注里的 size 字段凑合 img_name = root.find("filename").text img_path = os.path.join(img_dir, img_name) w, h = Image.open(img_path).size lines = [] for obj in root.findall("object"): cls_name = obj.find("name").text.strip().lower() if cls_name not in CLASS_MAP: continue # 不在目标类别里的直接跳过,别硬塞 cls_id = CLASS_MAP[cls_name] 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) # 裁剪到图像边界,防止标注越界导致归一化后出现负数 xmin, ymin = max(0, xmin), max(0, ymin) xmax, ymax = min(w, xmax), min(h, ymax) cx = (xmin + xmax) / 2.0 / w cy = (ymin + ymax) / 2.0 / h bw = (xmax - xmin) / w bh = (ymax - ymin) / h lines.append(f"{cls_id} {cx:.6f} {cy:.6f} {bw:.6f} {bh:.6f}") out_name = os.path.splitext(xml_file)[0] + ".txt" with open(os.path.join(out_dir, out_name), "w") as f: f.write("\n".join(lines)) convert_voc_to_yolo("Annotations", "JPEGImages", "labels")逻辑说明:先读 xml 拿到每个目标框,用PIL读图片真实宽高做归一化,而不是信 xml 里的<size>字段——那个字段经常和实际图片对不上。CLASS_MAP把同义类别名合并,这是处理摩托车与行人数据集的关键一步。参数上,:.6f保留六位小数足够,YOLO 对精度不敏感。max(0, xmin)这类裁剪是后悔药,防止个别越界标注把归一化坐标搞成负数,训练时直接报错。
3.2 COCO JSON 转 YOLO 的差异点
COCO 格式的bbox是[x, y, width, height]绝对像素,转换时不用算 xmax/ymax,直接拿宽高归一化即可。但 COCO 的category_id往往不是从 0 连续开始的,比如行人可能是 1、摩托车是 3,中间空着。YOLO 要求类别 ID 从 0 连续,所以必须建一张category_id到0..N-1的映射表,不能直接拿原 ID 用。
import json with open("annotations.json") as f: coco = json.load(f) # 建立原始 category_id 到连续 id 的映射 cat_ids = sorted(c["id"] for c in coco["categories"]) id_map = {old: new for new, old in enumerate(cat_ids)} # 之后遍历 annotations,用 id_map[ann["category_id"]] 取新 id这段不完整展开,重点在id_map这一步。跳过它,训练时会出现「类别 3 超出 nc 范围」的报错,或者更隐蔽地把摩托车当成背景丢掉。
3.3 转换后必须做的三项校验
转完不是就完事了,至少校验三样:一是每个 txt 的行数是否和原标注目标数一致;二是所有坐标是否都落在 0 到 1 之间;三是类别 ID 是否只出现你预期的几个值。
# 检查有没有坐标越界(出现负数或大于1) awk '{if($2<0||$2>1||$3<0||$3>1||$4<0||$4>1||$5<0||$5>1) print FILENAME": "$0}' labels/*.txt | head # 统计每个类别出现的次数,确认没有意外类别 awk '{print $1}' labels/*.txt | sort | uniq -c第一条命令扫出越界坐标,第二条给出类别分布。如果uniq -c里冒出一个你没定义过的 ID,说明CLASS_MAP漏了某个类别名,回去补。这两条命令花不了十秒,能省掉一次几小时的无效训练。
4. 数据集划分、训练配置与摩托车行人场景的参数调法
4.1 训练验证集划分:别用随机划分糊弄
摩托车与行人数据集如果来自连续视频抽帧,相邻帧高度相似。这时候用纯随机划分,验证集里会出现和训练集几乎一样的图,指标虚高,实际部署翻车。正确做法是按视频片段或时间划分,同一段视频的帧要么全进训练,要么全进验证。
import os, random, shutil random.seed(42) # 固定种子,保证可复现 imgs = sorted(os.listdir("images")) random.shuffle(imgs) split = int(len(imgs) * 0.8) for i, img in enumerate(imgs): subset = "train" if i < split else "val" shutil.copy(f"images/{img}", f"dataset/images/{subset}/{img}") txt = os.path.splitext(img)[0] + ".txt" shutil.copy(f"labels/{txt}", f"dataset/labels/{subset}/{txt}")参数上0.8是常见比例,数据量少于两千张时可以调到0.85。seed一定要固定,否则每次划分不同,实验没法对比。如果数据集自带 train/val 目录,直接用自带的,别自己再分。
4.2 YOLO 训练配置里针对本场景要动的几个参数
以 Ultralytics 系为例,配置文件里和摩托车行人场景最相关的是这几项。类别数nc设成 2,names按你的映射顺序写。输入尺寸imgsz建议 640 起步,摩托车细长结构在 640 下小目标容易丢,显存够就上 960。
# data.yaml path: ./dataset train: images/train val: images/val nc: 2 names: 0: person 1: motorcycle训练命令:
yolo detect train data=data.yaml model=yolov8n.pt epochs=100 imgsz=640 batch=16参数说明:model选 n 还是 s 看你的算力,摩托车行人两类任务不算复杂,n 版通常够用,追求召回再上 s。epochs=100配合早停,实际往往几十轮就收敛。batch按显存调,爆显存就减半。如果摩托车召回明显偏低,在损失配置里给类别 1 加权,或者对含摩托车的图做重复采样。
4.3 评估指标怎么看才算数
训练完看mAP50和mAP50-95,但别只看总数。摩托车与行人两类要分开看每一类的 AP。常见情况是行人 AP 0.85、摩托车 AP 0.6,总数被行人拉高,看着还行,实际摩托车漏检严重。这时候要回到数据层面查:是不是摩托车样本太少,是不是骑手框和车身框重叠导致 NMS 把摩托车框压掉了。后者可以调 NMS 的 IoU 阈值,默认 0.7 对高度重叠的框偏激进,适当放宽到 0.75 试试。
5. 摩托车与行人检测的避坑与排查清单
5.1 训练 loss 正常但 mAP 一直是 0
现象:训练日志里 box_loss、cls_loss 都在降,但验证 mAP 始终为 0。原因九成是类别 ID 对不上,或者验证集的 label 路径没配对。YOLO 找不到对应 txt 时会静默当成背景图,loss 照降,指标全废。解决:检查data.yaml里val路径下 images 和 labels 是否一一对应,用ls labels/val | wc -l和ls images/val | wc -l对比数量,再抽查一个 txt 的类别 ID 是否在names范围内。
5.2 骑手被同时标成人和摩托车,NMS 后摩托车框消失
现象:推理图上骑手位置只剩行人框,摩托车框被抑制。原因:人和摩托车的框 IoU 很高,默认 NMS 阈值下摩托车框被当冗余删掉。解决:把 NMS 的 IoU 阈值从 0.7 提到 0.75 到 0.8,或者改用 soft-NMS。如果框架支持按类别做 NMS,把人和摩托车分开抑制,效果更稳。
5.3 图片格式混杂导致训练中途报解码错误
现象:训练跑了几百步突然报image decode failed或cannot identify image file。原因:数据集里混了 png、jpg,甚至个别损坏文件或 webp。解决:训练前统一转成 jpg,并过滤掉打不开的图。
from PIL import Image import os for f in os.listdir("images"): p = os.path.join("images", f) try: Image.open(p).convert("RGB").save(p.replace(os.path.splitext(f)[1], ".jpg"), "JPEG") except Exception as e: print("坏图:", p, e) # 打不开的直接记录并删除5.4 验证集指标虚高,部署后一塌糊涂
现象:验证 mAP 0.9,实际视频里漏检严重。原因:随机划分导致训练验证集高度相似,或者验证集图片和训练集来自同一段视频。解决:按视频片段划分,验证集用完全没见过的场景。另外检查验证集里摩托车样本占比,如果验证集几乎全是行人,摩托车指标没有参考意义。
5.5 小目标摩托车在 640 输入下大量漏检
现象:远处摩托车检测不到,近处正常。原因:摩托车在远景下像素面积极小,640 输入下特征图分辨率不够。解决:把imgsz提到 960 或 1280,或者在数据增强里开启 mosaic 和 copy-paste 增加小目标样本。代价是显存和训练时间上升,按算力权衡。
6. 让摩托车与行人检测真正可用的两个进阶技巧
第一个技巧是难例回灌。模型训完第一版后,拿它去跑一批没标注的实际场景视频,把漏检和误检的帧挑出来,人工补标后加进训练集再训一轮。这个循环做两到三次,摩托车召回率通常能涨十几个点。我一般会写个脚本把置信度在 0.1 到 0.4 之间的预测框对应的图单独存出来,这批就是模型最不确定的难例,优先标它们性价比最高。
# 伪代码:把低置信度预测的图挑出来做难例 for result in model.predict(source="unlabeled_videos", conf=0.1, stream=True): low_conf = [b for b in result.boxes if 0.1 < b.conf < 0.4] if low_conf: result.save(filename=f"hard_examples/{result.path}")第二个技巧是导出时选对格式。摩托车与行人检测多半要落到边缘设备或监控盒子上,导出 ONNX 或 TensorRT 时注意输入尺寸和训练时保持一致,动态轴别乱开。我吃过一次亏:训练用 640,导出 ONNX 时图省事开了动态尺寸,部署端喂 1280 的图,坐标全偏,排查了大半天才定位到是预处理没跟着缩放。现在我的习惯是导出后一定拿同一张图分别跑 PyTorch 和 ONNX,比对输出框的坐标差,差值超过一个像素就说明预处理链路有问题。这个比对花五分钟,能挡掉部署阶段最烦人的一类 bug。数据集本身的质量决定上限,训练技巧只是逼近上限,把标注校验和难例回灌做扎实,比反复调学习率有用得多。希望帮到你。
本文还有配套的精品资源,点击获取