基于YOLOv8的摩托车与行人目标检测数据集实战解析
2026/9/15 3:05:06 网站建设 项目流程

简介:摩托车与行人目标检测数据集是一份面向道路监控与智能交通场景的工业级标注数据,专为需要开展目标检测算法训练与验证的开发者、研究人员设计。压缩包共2000个文件,核心包含1095个txt标签文件、903张jpg道路场景图片、1个yaml类别配置及1个docx说明文档,整体大小约62.91MB。资源聚焦摩托车和行人两类关键目标,采用YOLO格式的归一化边界框标注,可支撑交通流量统计、危险行为预警、自动驾驶感知增强、智慧城市安防及人车互动行为研究等任务,兼容YOLOv5/v7/v8等主流检测框架,开箱即用。数据集具有目标定位精准、场景针对性强等优势,标签与图像一一对应,yaml配置便于直接修改类别,docx文档说明了数据组织方式,能够大幅降低上手门槛。目前已有124人学习下载,数据覆盖多种光照和视角条件,可帮助使用者在真实道路场景上快速构建检测模型、评估算法泛化能力,从而节省大量数据采集与标注时间。

1. 摩托车与行人目标检测数据集:从路测监控到YOLO训练样本的拆解

如果你拿到一个.zip压缩包,里面是1095张道路监控JPEG图和对应的YOLO格式txt标签,类别只有motorcycle和pedestrian两个,第一反应是什么?我通常会先看标注文件和图片是否对得上,再决定能不能直接丢进YOLOv8训练。这个摩托车与行人目标检测数据集就是这样的结构:训练集937张、验证集158张,全部来自实际道路监控场景,覆盖不同光照和视角。对于做交通监控、自动驾驶感知验证或者智慧城市安防的团队来说,它解决了冷启动时“没有现成标注数据”的问题。更重要的是,因为格式标准,它可以和COCO2017数据集结构里的类别映射做对照,快速迁移到自己的任务上。下面从数据格式、训练配置到推理验证,完整过一遍。

2. 数据与标注:YOLO格式下的摩托车/行人样本构成

这个数据集从文件命名就能看出是Roboflow导出的格式,image-572-_jpg.rf.49ecbc...jpg,每张图对应一个同名.txt标签。解压后第一件事是用tree命令确认目录层级,常见做法是不管zip包里面怎么嵌套,先平铺到一个datasets根目录下,让图片和标签一一对应。zip包本身不需要额外处理,用unzip解压后就能直接看到训练集和验证集的图片、标签目录。

2.1 训练/验证划分与场景覆盖观察

数据总量是1095张,训练集937张、验证集158张,分割比例大约85.5%和14.5%。这个比例对两类目标检测任务来说够用,但要注意验证集图片和训练集如果来自同一段监控视频的不同帧,会存在一定程度的相似性,导致验证精度虚高。我一般会抽查验证集里是否有过分接近的连续帧,方法很简单:把验证集图片的感知哈希算出来,两两对比汉明距离。

import os import imagehash from PIL import Image import itertools val_dir = "datasets/val/images" hashes = {} for fname in os.listdir(val_dir): if fname.endswith(".jpg"): hashes[fname] = imagehash.phash(Image.open(os.path.join(val_dir, fname))) for (f1, h1), (f2, h2) in itertools.combinations(hashes.items(), 2): if h1 - h2 < 5: print(f"疑似重复: {f1} <-> {f2}, 汉明距离 {h1-h2}")

这段代码用imagehash库计算感知哈希,汉明距离小于5通常意味着两张图视觉上非常接近。跑完后,如果发现大量重复帧,建议重新划分验证集,否则后面跑出的mAP参考价值要打折扣。场景覆盖方面,图片来自道路监控,视角偏高位,车身和行人存在不同程度的遮挡,这对训练前的数据清洗提出了要求。

从图片尺寸来看,实际监控画面通常是1920x1080或1280x720,但YOLO标签的坐标已经归一化,所以无论原始分辨率是多少,读取时都能统一缩放到训练尺寸。这个数据集里没有提供exif信息,不过从JPEG文件头可以读取宽高。我建议用下面的命令统计一下所有图片分辨率,确认是否存在混合分辨率。

python -c " from PIL import Image import os, collections sizes = collections.Counter() for split in ['train','val']: for f in os.listdir(f'datasets/{split}/images'): with Image.open(f'datasets/{split}/images/{f}') as im: sizes[im.size] += 1 print(sizes) "

如果发现大部分图片是同一分辨率,说明数据来源一致,训练时不容易出现尺度跳动。如果混合了多个分辨率,需要设置合理的imgsz训练参数,否则宽幅画面中的摩托车会被压缩成小目标。

2.2 边界框标签格式与统计方法

标签是标准的YOLO格式,每行内容为class x_center y_center width height,所有坐标均归一化到0~1。在目标检测算法里,这种格式是绕不开的基础。类别0对应motorcycle,类别1对应pedestrian。我打开其中一个标签文件,内容类似:

0 0.421875 0.639815 0.104167 0.162500 1 0.710938 0.653241 0.089583 0.225000

第一行表示一个摩托车目标,中心点在图片的相对位置(0.42, 0.64),宽高占图幅的10.4%和16.3%。第二行是一个行人。注意,这里没有单独的类别文件,类别映射通常是数据集描述里给出的。如果你还要复用到其他框架,建议先写一个classes.txt,内容是两行类别名,后面转COCO或VOC格式会更快。

为了验证所有标签是否合法,我习惯跑一段快速检查脚本:

python -c " import os for split in ['train','val']: for f in os.listdir(f'datasets/{split}/labels'): with open(f'datasets/{split}/labels/{f}') as fp: for line in fp: parts = line.strip().split() assert len(parts) == 5, f'非法标签行: {f} {line}' x, y, w, h = map(float, parts[1:]) assert 0 <= x <= 1 and 0 <= y <= 1 and w > 0 and h > 0, f'坐标越界: {f} {line}' print('标签格式全部通过') "

这个逻辑就是把每行拆成5个字段,检查类别ID是否在0或1,中心坐标是否在[0,1]区间,宽高是否为正。如果出现wh为0,多半是标注工具的退化框,训练时会被当成背景,直接跳过或删除该行。

进一步统计框的尺寸和类别分布,有助于判断模型容量和分辨率选择。下面这段代码统计每个类别框的面积占整图比例:

import os import numpy as np areas = {"motorcycle": [], "pedestrian": []} counts = {"motorcycle": 0, "pedestrian": 0} for split in ["train", "val"]: label_dir = f"datasets/{split}/labels" for f in os.listdir(label_dir): with open(os.path.join(label_dir, f)) as fp: for line in fp: c, x, y, w, h = line.split() name = "motorcycle" if int(c) == 0 else "pedestrian" counts[name] += 1 areas[name].append(float(w) * float(h)) for name in counts: arr = np.array(areas[name]) print(f"{name}: 样本数 {counts[name]}, 面积中位数 {np.median(arr):.4f}, 90分位 {np.percentile(arr, 90):.4f}")

通过面积分布可以看出小目标占比。如果大量框的面积小于0.01,也就是框的宽高各不到原图的10%,那在640分辨率下可能只有30x30像素左右,需要适当提高输入分辨率或使用小目标检测模块。

2.3 标注可视化与质量问题处理

机器检查只能发现格式错误,标注偏移和类别错标还需要人工抽查。我一般会写一个OpenCV可视化脚本,把标注框画到图上,生成一张拼图快速扫一遍。脚本代码如下:

import cv2 import os def draw_boxes(image_path, label_path, out_path): img = cv2.imread(image_path) h, w = img.shape[:2] with open(label_path) as f: for line in f: c, x, y, bw, bh = map(float, line.split()) x1, y1, x2, y2 = ( int((x - bw / 2) * w), int((y - bh / 2) * h), int((x + bw / 2) * w), int((y + bh / 2) * h), ) color = (0, 255, 0) if int(c) == 0 else (0, 0, 255) cv2.rectangle(img, (x1, y1), (x2, y2), color, 2) cv2.putText(img, "motorcycle" if int(c)==0 else "pedestrian", (x1, max(0, y1 - 5)), cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 1) cv2.imwrite(out_path, img) for split in ["train", "val"]: img_dir = f"datasets/{split}/images" lbl_dir = f"datasets/{split}/labels" os.makedirs(f"visual_{split}", exist_ok=True) for f in os.listdir(img_dir): if f.endswith(".jpg"): stem = f.replace(".jpg", "") if os.path.exists(os.path.join(lbl_dir, stem + ".txt")): draw_boxes(os.path.join(img_dir, f), os.path.join(lbl_dir, stem + ".txt"), f"visual_{split}/{stem}.jpg")

这段代码会把每个框按类别画成绿色或红色,保存到visual_trainvisual_val目录。肉眼扫一遍,重点看两类问题:一是标注框是否刚好框住目标,有没有把背景大片包含进来;二是摩托车和行人同时出现时,标注框是否重叠得不符合逻辑。如果发现部分边界框明显偏移,可以用LabelImg或Roboflow重新修正,但考虑到数据集来自实际道路监控,标注整体质量在线,少量偏差在可接受范围内。

下面表格汇总了我在校验过程中遇到过的问题和处理方式:

问题类型症状处理方式
标签缺失图片存在但txt不存在丢弃该图,或重跑标注
文件重名不同目录下同名统一重命名,前缀加split
类别ID错误行人标成0逐个检查类别文件
框退化width/height为0删除对应行
框严重偏移框中心不在目标上人工修正,或作为困难样本保留

这一层校验做完,才进入训练环节。不同框架对目录要求略有差别,但YOLOv8的trainval目录结构是固定的,下一章就按这个结构配置。

3. 基于YOLOv8的训练配置与参数调优

拿到标注数据后,选型不是越新的模型越好。摩托车和行人两类目标在道路监控里尺度差异大,行人不时被摩托车遮挡,所以需要兼顾小目标召回率和推理速度。YOLOv8系列中,我一般用yolov8syolov8m,既能满足实时性,又能通过数据增强弥补样本量不足。如果你的目标是快速验证,yolov8n也行,但后面调参时容易碰到容量瓶颈。

3.1 数据集目录组织与data.yaml

把zip解压后重排成标准目录:

datasets/ ├── train/ │ ├── images/ │ └── labels/ ├── val/ │ ├── images/ │ └── labels/ └── data.yaml

data.yaml内容如下:

path: /absolute/path/to/datasets train: train/images val: val/images nc: 2 names: 0: motorcycle 1: pedestrian

注意,path字段在YOLOv8里必须是绝对路径或相对ultralytics运行目录的路径。踩过坑是写成相对路径后训练时找不到图片,最后统一改成绝对路径才正常。nc必须等于2,和names中的类别数一致。如果你准备把该数据集与COCO2017数据集结构里的其他类混合训练,需要把names扩充并重新映射类别ID,但那样要重新生成标签,不建议在初期做。

3.2 训练命令和模型选型

命令行启动训练:

yolo detect train \ data=data.yaml \ model=yolov8s.pt \ epochs=100 \ imgsz=640 \ batch=16 \ lr0=0.01 \ augment=True \ patience=20

参数含义如下:

参数作用建议值
model预训练权重yolov8s.pt
epochs训练轮数100,样本少可以早停
imgsz输入分辨率640,兼顾监控场景
batch批量大小按显存调整,16或32
lr0初始学习率0.01,小数据集可降到0.005
patience早停轮数20,防止过拟合

我一般会先跑50轮,观察损失曲线。如果val/box_loss在30轮后不再下降,就把patience调小,节省时间。显存不足时,最有效的手段是降低batch到8,而不是盲目缩小imgsz,因为输入分辨率对监控小目标检测影响很大。如果batch降到4还不够,可以使用梯度累积,YOLO命令行没有直接暴露该参数,但可以在Python训练脚本中设置accumulate参数。

YOLOv8不同尺寸模型的选择参考:

模型推理速度小目标检出能力适用场景
yolov8n最快较弱边缘盒子,实时监控
yolov8s中等主流服务器,通用项目
yolov8m中等较强精度优先,延迟容忍

如果你后面的任务是交通流统计,需要长时间跑视频流,yolov8s是性价比最高的。yolov8n在牌照级远处行人上容易漏检,yolov8m在1080p视频上仍能跑到30fps以上,但对显卡要求更高。

3.3 数据增强与类别不平衡处理

这个数据集里摩托车和行人的数量并不完全平衡,监控场景中行人出现的频率可能更高。YOLOv8自带增强策略(马赛克、翻转、HSV扰动等),对小数据集通常有正面效果。我建议开启augment=True,但如果发现训练集和验证集分布差异大,马赛克增强会在最后10轮关闭,因为马赛克会拼接多张图,导致目标尺度失真。YOLOv8内部已做这种调度,不需要手动干预。

如果类别不平衡严重,例如行人样本是摩托车的两倍以上,需要调整损失权重。YOLOv8没有直接暴露类别权重参数,常见做法是复制少样本的标签行,或者用cls损失系数。我用过最简单的方法是先训练一版,统计验证集上各类别的recall,再针对低召回类别补充裁剪样本。数据集本身标注质量不错,一般不需要过度处理。

对这类小数据集,冻结预训练backbone的前几层可以加速收敛,也能防止过拟合。命令里加一个freeze=10即可:

yolo detect train \ data=data.yaml \ model=yolov8s.pt \ epochs=100 \ imgsz=640 \ batch=16 \ freeze=10

freeze=10表示冻结模型前10层,这些层提取的纹理、边缘特征在ImageNet和COCO上已经学得很充分,不需要在少量交通数据上重新学习。后面层仍然会更新,用于学习摩托车和行人的组合特征。经验上,冻结前10层能缩短约20%的训练时间,对最终精度影响很小。

训练结束后,runs/detect/train/目录下会生成weights/best.ptlast.pt,以及results.png曲线。下一步就是加载best.pt做实际推理验证。这里特别提醒,验证集表现不等于监控视频表现,必须拿一段没见过的视频来测试。下一章专门讲推理与评估。

4. 实战:在自有监控视频上验证模型效果

训练完只看mAP是不够的。交通监控场景里,摩托车和行人的姿态变化大,摄像头角度固定但光照变化剧烈。我习惯的做法是用模型跑一段真实监控视频,逐帧保存结果,然后人工扫一遍,重点看漏检和误检。

4.1 加载best.pt进行推理

先用best.pt跑单张图片验证:

from ultralytics import YOLO model = YOLO("runs/detect/train/weights/best.pt") results = model.predict( source="test_images/", conf=0.25, iou=0.5, save=True, save_txt=True, )

source可以是图片目录,也可以直接给视频文件路径。conf是置信度阈值,监控场景建议设0.3以上,否则误检会增多;iou是NMS的IoU阈值,默认0.5。输出会保存在runs/detect/predict/下,save_txt会生成YOLO格式的检测结果,方便后续做统计。

对于视频文件,model.predict会自动逐帧推理并输出注释后的视频,但实际项目里我更常直接用cap = cv2.VideoCapture读帧并处理,这样能把检测结果和业务逻辑直接接起来。示例代码:

import cv2 from ultralytics import YOLO model = YOLO("runs/detect/train/weights/best.pt") cap = cv2.VideoCapture("traffic.mp4") fps = cap.get(cv2.CAP_PROP_FPS) width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) writer = cv2.VideoWriter("output.mp4", cv2.VideoWriter_fourcc(*"mp4v"), fps, (width, height)) while cap.isOpened(): ret, frame = cap.read() if not ret: break results = model.predict(frame, conf=0.3, verbose=False) annotated = results[0].plot() writer.write(annotated) cap.release() writer.release()

这里results[0].plot()会把检测框、类别和置信度直接画在帧上,省去手工绘制的麻烦。如果要在低成本设备上运行,可以关闭verbose=False减少终端打印开销。

4.2 评估指标、混淆矩阵与失败样本

YOLOv8训练结束后,可以用val模式直接评估:

yolo detect val data=data.yaml model=runs/detect/train/weights/best.pt

这一步会输出每个类别的mAP50mAP50-95precisionrecall。如果是二类数据集,重点看行人类别的recall,因为行人是弱势交通参与者,漏检的后果比误检严重。尤其要注意小目标,监控画面中远处的行人往往低于32x32像素,在YOLOv8的检测头中可能被判为背景。对于这种场景,建议把imgsz从640提到960,或者使用SAHI等切片推理库。常见做法是把原图切成若干重叠图块,分别推理后合并。

混淆矩阵是分析错误来源的关键。训练结束后在runs/detect/train/confusion_matrix.png中可以看到类别间的混叠情况。我根据实际经验列举一个典型的混淆矩阵:

真实\预测motorcyclepedestrian背景
motorcycle9046
pedestrian88012
背景350

从这个表里可以看到,摩托车的召回率约90%,行人约80%。行人被漏检到背景的12%,通常是远处小目标或被摩托车遮挡的瞬间。摩托车被误检成行人的4%,多数是骑车人下车推行时的姿态。这种混淆信息比单一mAP值更有用,它直接告诉你该补哪些数据。

4.3 小目标、夜间与遮挡场景的针对性优化

摩托车和行人互相遮挡是监控场景的常态。我遇到过的问题是摩托车驾驶人被判定为行人,原因是标注时把人车合并在一个框里。此时需要回到标注层面检查:如果边界框同时覆盖了摩托车和骑车人,模型学到的特征就会混淆。这个数据集在标注时对摩托车和行人分别画框,但监控视角下,骑车人身体和摩托车高度重叠,模型容易只检测出摩托车。解决方法是训练后对检测结果做后处理,用包围框面积比例和历史帧速度来辅助分类,或者把骑车人单列第三类。但单列第三类需要额外标注,数据量不够时效果反而差。

夜间场景的曝光不足会显著降低召回。YOLOv8不支持自动对比度增强,需要在外接的预处理里做CLAHE。下面这段代码用于推理前增强:

import cv2 def preprocess(frame): lab = cv2.cvtColor(frame, cv2.COLOR_BGR2LAB) l, a, b = cv2.split(lab) l = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8)).apply(l) return cv2.cvtColor(cv2.merge([l, a, b]), cv2.COLOR_LAB2BGR)

preprocess应用在每一帧后再送入模型,可以提升夜间小目标召回。代价是推理帧率下降约15%。如果部署在边缘设备上,建议只对低照度帧启用。判断标准很简单:计算灰度图均值,如果小于80就启用增强。

对于更小目标,还可以把输入图切成四块分别推理,再合并结果。这种切片方式会显著增加计算量,但漏检率下降明显。实际项目中,我会优先保证视频流实时性,切片只在报警区域中使用,比如对道路远端区域做局部放大检测。

5. 进阶:把摩托车/行人检测接到交通流统计与预警

模型能出检测框只完成了第一步,实际项目里还要做目标追踪、事件判定和性能优化。这里我给出一个可以落地的组合方案:YOLOv8 + ByteTrack + 规则引擎。

5.1 目标追踪与跨帧ID关联

监控视频中摩托车和行人移动会出现短暂遮挡,ByteTrack相比DeepSORT不需要外观特征,在小目标场景下更稳定。用ultralytics内置的track方法:

results = model.track(source="traffic.mp4", tracker="bytetrack.yaml", persist=True)

这会输出每个目标的track_id,同一目标在不同帧中保持相同ID。有了ID,就能统计进入画面的目标数量。注意,固定摄像头场景中背景静止,可以使用帧差法过滤掉静止目标,只统计运动的摩托车和行人。ByteTrack的默认配置中,track_buffer是30帧,match_thresh是0.8。如果视频帧率是25fps,那么目标被遮挡1.2秒以内仍然能保持ID,超过就会重新分配。对交通监控来说这个参数够用,如果路口拥堵导致目标停滞,可以适当增大track_buffer到50。

5.2 越线与聚集事件的规则实现

例如判断摩托车是否越过停止线,可以采用逻辑:取摩托车检测框底边中点坐标,当该点跨越预设的ROI线时触发事件。落地代码片段:

prev_y = {} line_y = 500 for frame_id, result in enumerate(frame_results): for box in result.boxes: x1, y1, x2, y2 = map(int, box.xyxy[0]) track_id = int(box.id) category = int(box.cls) if category == 0: current_y = (y1 + y2) // 2 if track_id in prev_y and prev_y[track_id] < line_y <= current_y: print(f"摩托车 {track_id} 在帧 {frame_id} 越过停止线") prev_y[track_id] = current_y

这里line_y是ROI线的纵坐标,prev_y保存每个目标上一帧的底边中点。当上一帧在线上方、当前帧在线下方时,判定为一次越线。注意要加一个去重逻辑,防止同一目标连续多帧触发。

行人聚集检测可以通过统计单位面积内行人框中心点密度实现。当密度超过阈值且持续时间超过5秒,触发预警。具体做法是维护一个滑动窗口队列,每帧统计人数,如果连续超过10帧且框中心点两两距离小于100像素的数量超过5个,就发出预警。实际调参时要结合摄像头安装高度和视角。

5.3 部署优化:TensorRT与半精度推理

最后是性能。TensorRT加速是最常用方案,YOLOv8导出:

yolo export model=best.pt format=engine device=0

导出的engine文件在Jetson等嵌入式设备上可以用。如果对延迟敏感,开启半精度FP16,通常在保证精度前提下提速1.5~2倍。另一个优化点是降低推理分辨率,但监控小目标多,不宜降到480以下。在公开数据集上可能看不出差异,但在实际道路上,小目标漏检率会直线上升。

验证部署效果时,建议准备一份包含白天、夜晚、雨天三个场景的测试视频,统计每类目标的precision和recall。这个摩托车与行人目标检测数据集覆盖了多种光照和视角,作为训练起点够用,但要让自己模型的泛化能力真正上线,还要在实际环境中持续补充困难样本。比如把夜间误检的灯影、雨天地面反光这些false positive单独收集起来,定期融入到训练集里重训,模型才会越用越稳。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询