☰
三轮车目标检测数据集:559张VOC+YOLO双格式标注资源
2026/10/18 12:08:20 网站建设 项目流程

简介:面向目标检测与计算机视觉学习者及开发者,这份三轮车数据集提供559张实景图片,由labelImg逐张手工标注,类别统一为tricycle,共包含659个矩形标注框。数据采用Pascal VOC与YOLO双格式,便于直接接入主流检测框架,无需额外转换。包内文件总数1679个,以jpg图片、xml标注及txt标注为主,压缩包整体25.22MB,轻量易下载。目前已有646人学习浏览,可用于三轮车检测模型训练、算法效果对比或课程设计实践。标注规则规范统一,图片场景多样,适合YOLOv5、YOLOv8、Faster R-CNN等常见模型的训练与评估;数据集未附带任何权重或精度保证,仅提供准确合理的标注,能够显著减少人工标注时间,也是理解VOC/YOLO标注格式的实用样例,可直接作为入门到进阶的目标检测实战数据。

1. 三轮车数据集:559张VOC+YOLO双格式标注包到底能拿来做什么

做目标检测训练的人应该都有过这种经历:满世界找数据集,要么是 COCO 那种通用大而全但就是不贴自己场景的,要么是某宝上拼凑的标注质量参差不齐的图包,下载下来光清洗标注就要耗掉一整天。这份三轮车数据集最直接的价值,就是省掉你自己开 labelImg 一张张画框的时间——559张真实场景图片,手工标注,同时给了 Pascal VOC 的 xml 和 YOLO 的 txt 两套格式,类别只有一个 tricycle,框数总共 659 个。你拿到的不是模型权重,而是干净、可复现、能直接丢进训练脚本的标注源。适合谁?做小车识别、园区巡检、物流分拣视觉、或者只想在自建数据集上跑通 YOLOv5/v8 训练流程的开发者。

2. 先看懂这份数据集的内部结构:VOC和YOLO格式的对应关系与标注规则

2.1 文件组织方式:jpg、xml、txt 三方对应

解压之后目录结构非常朴素,没有嵌套一堆子文件夹,jpg 图片、xml 标注、txt 标注全部平铺在同一层。

tricycle_dataset/ ├── tricycle_xyxr_341.jpg ├── tricycle_xyxr_341.xml ├── tricycle_xyxr_341.txt ├── tricycle_xyxr_179.jpg ├── tricycle_xyxr_179.xml ├── tricycle_xyxr_179.txt ├── ...(共559组)

要注意的是,这份资源里没有单独划分 train/val/test 子目录,也没有包含分割路径的 txt 文件。也就是说,你需要自己在训练前完成数据划分。每组文件都以同名的 .jpg、.xml、.txt 三件套存在,前缀统一是tricycle_xyxr_,后面跟三位序号。我一般拿到手第一件事就是写个脚本校验三件套是否一一对应,防止中间有缺失文件在训练时报「No labels found」之类的错。这种平铺结构对新手最友好,但你需要自己补一个划分脚本,后面我会给出可直接用的版本。

2.2 VOC 标注内容:xml 里到底记录了什么

用任意文本编辑器打开一个 xml 文件,结构是标准的 Pascal VOC layout:<annotation>根节点下包含folder、filename、source、size、object五类信息。

<annotation> <folder>tricycle_xyxr</folder> <filename>tricycle_xyxr_341.jpg</filename> <source> <database>Unknown</database> </source> <size> <width>640</width> <height>480</height> <depth>3</depth> </size> <object> <name>tricycle</name> <pose>Unspecified</pose> <truncated>0</truncated> <difficult>0</difficult> <bndbox> <xmin>126</xmin> <ymin>88</ymin> <xmax>531</xmax> <ymax>402</ymax> </bndbox> </object> </annotation>

关键字段里,name就是类别名 tricycle,全数据集只有这一个类,不会再有大类套小类的情况。difficult和truncated都是 0,说明标注者没有标记遮挡或困难样本——这对训练来说是一个好消息,因为不需要在 loss 计算时做额外过滤。bndbox里的四个值就是矩形框的像素坐标,左上角和右下角。

需要注意的是,这个 xml 里没有segmented字段,也没有多边形分割点,所以它不能直接用于 Mask R-CNN 这类实例分割任务,它就是纯粹的检测框标注。

2.3 YOLO 标注格式:归一化坐标的计算与类 ID 约定

YOLO 格式的 txt 和 xml 描述的是同一个框,只是换了一种表达方式。txt 里每一行代表一个目标框,五个值依次是:类别 ID、中心点 x 归一化坐标、中心点 y 归一化坐标、框宽 w 归一化值、框高 h 归一化值。

拿前面那个 xml 对应的图片为例,图片尺寸是 640x480,框的像素坐标是 xmin=126, ymin=88, xmax=531, ymax=402,转换逻辑很简单:

# 将VOC的xmin/ymin/xmax/ymax转换为YOLO的归一化中心点与宽高 xmin, ymin, xmax, ymax = 126, 88, 531, 402 img_w, img_h = 640, 480 # 计算中心点与宽高(像素单位) center_x = (xmin + xmax) / 2.0 # 328.5 center_y = (ymin + ymax) / 2.0 # 245.0 box_w = xmax - xmin # 405 box_h = ymax - ymin # 314 # 归一化到 [0,1] 区间 norm_cx = center_x / img_w # 0.5133 norm_cy = center_y / img_h # 0.5104 norm_w = box_w / img_w # 0.6328 norm_h = box_h / img_h # 0.6542 print(f"0 {norm_cx:.4f} {norm_cy:.4f} {norm_w:.4f} {norm_h:.4f}")

这段转换代码的核心逻辑就是先算中心点,再做归一化。因为只有一个类别,类别 ID 固定是 0,也就是classes: ['tricycle']里的第一个元素。如果你的训练脚本里 classes 顺序变了,比如把 tricycle 放在第二个位置,那 txt 里的 0 就要对应改成 1,否则会错位——这是最常见的翻车点之一。

2.4 标注质量与统计:659 个框意味着什么

整个数据集 559 张图、659 个框,算下来平均每张图只有 1.18 个目标。这意味着大部分图片都是单目标场景,少数图里有两到三辆三轮车。标注工具是 labelImg,类别只有 tricycle,框的类型全是矩形框。

从训练角度看,这类单类别、低密度目标的数据集,比较适合跑通检测流程和做迁移学习 demo,但不适合挑战高难度密集场景。如果你后续要做的业务场景里一辆图可能同时出现十几辆车,那这个数据集的分布和你的真实场景偏差就比较大,需要额外补充数据。另外,659 个框都是手工标出来的,框边界是否紧贴目标边缘直接决定训练出来的 mAP 上限,建议抽几张贴在 labelImg 里看一眼框的贴合程度再决定要不要做微调。

3. 把数据集跑进训练脚本:划分、训练配置与数据增强

3.1 三步完成训练集的 train/val 划分

拿到手的数据集没有划分目录,第一步必须先做随机划分。我习惯用脚本按 8:2 的比例切分 train 和 val,同时按 YOLO 训练的目录约定把图片和标签整理成 images 和 labels 两个平级文件夹。

import os import random import shutil from pathlib import Path # 源目录与目标目录配置 src_dir = "tricycle_dataset" train_dir = "dataset/images/train" val_dir = "dataset/images/val" train_label_dir = "dataset/labels/train" val_label_dir = "dataset/labels/val" # 创建目录结构 for d in [train_dir, val_dir, train_label_dir, val_label_dir]: Path(d).mkdir(parents=True, exist_ok=True) # 收集所有jpg文件 all_images = [f for f in os.listdir(src_dir) if f.endswith(".jpg")] random.seed(42) random.shuffle(all_images) # 8:2划分 split_idx = int(len(all_images) * 0.8) train_images = all_images[:split_idx] val_images = all_images[split_idx:] def move_files(image_list, img_dest, label_dest): for img_name in image_list: base = img_name.replace(".jpg", "") # 复制图片和同名txt shutil.copy(os.path.join(src_dir, img_name), os.path.join(img_dest, img_name)) shutil.copy(os.path.join(src_dir, base + ".txt"), os.path.join(label_dest, base + ".txt")) move_files(train_images, train_dir, train_label_dir) move_files(val_images, val_dir, val_label_dir) print(f"Train: {len(train_images)} images, Val: {len(val_images)} images")

这段脚本的核心是random.seed(42)固定随机种子,保证每次划分结果一致,方便后续复现实验。shutil.copy是复制而不是移动,保留原始数据集不变,万一训练过程中发现标注有问题还能回到原始文件重新操作。划分比例 8:2 是最通用的做法,如果你的样本量小想更稳一点,可以改成 9:1,效果差别不大;如果是验证调参阶段,也可以抽 20 张固定的当 debug 集,跑通流程后再换全量。

3.2 写 YOLOv5/v8 的 dataset.yaml

YOLO 系列训练脚本要求一个 yaml 文件来声明数据集路径和类别。因为这份数据只有一个类,配置非常简单:

# dataset.yaml path: ./dataset # 数据集根目录,相对于训练脚本的运行位置 train: images/train val: images/val nc: 1 names: ['tricycle']

这里的names列表顺序必须和 txt 里的类别 ID 0 对应,也就是说names[0]必须是你数据集中 ID 为 0 的类别。nc: 1表示只有一个类别,train和val是相对路径,yaml 文件放在哪都不影响,脚本会按path拼接。如果你以后往这个数据集里加第二类,比如行人,就要新增 txt 里类别 ID 为 1 的标注行,同时把 nc 改成 2,names 追加。

3.3 训练参数选择与数据增强的边界

单类 659 框的数据量不算大,训练参数设置要注意两个问题。一是 batch size,如果显卡显存不够,YOLOv5 会自动做 accumulate 策略来模拟大 batch,但实际上数据集小,batch 设 16 就够。二是 epoch 数,不要一上来就 300 epoch,先跑 50 epoch 看 loss 和验证集的 mAP 曲线是否收敛,再决定是否加大。

数据增强方面,YOLOv5 默认开启 mosaic、flip、hsv 等增强,对这个数据集来说增强太多反而有害——单目标场景用 mosaic 会把三轮车拼到奇怪的背景上,模型容易学到错误的上下文。我是直接把 mosaic 概率从默认 1.0 降到 0.5,或者干脆关掉,让模型专注学三轮车本身的形状特征。此外,hsv_h、hsv_s这些颜色增强是安全的,因为三轮车颜色本身就多样。

4. 避坑与排查:这份数据集使用中的五个典型翻车现场

4.1 训练时报「Label class 1 missing」但明明只有一个类

现象:YOLOv5 启动训练后 log 里提示 class 1 缺失,但数据集只有一个 tricycle 类别。

原因:某个 txt 文件里混入了脏数据。比如有人用格式转换工具批量处理时,把别的数据集的标注也复制了一部分过来,或者手动编辑 txt 的时候误写了一个1 0.5 0.5 0.2 0.2。

解决:全量扫描 txt 文件里每行的第一个数字,找出大于 0 的行。

grep -rn "^1" ./labels/ | head -20

如果找到这类行,先确认对应图片里是否真的有两个类,没有就直接删掉该行或修正 ID。我每次拿到新数据集,都会先用这个命令做一次全量体检,避免训练到一半才发现问题。

4.2 剪裁图片后用原标注训练,导致 mAP 暴跌

现象:训练集和验证集是同一个数据集跑出来的,mAP 却只有 0.5 左右,怎么看都不正常。

原因:图片被提前缩放或裁剪过,但标注坐标没有同步更新。有的数据集下载解压后图片尺寸和 xml 里记录的 width/height 不一致,VOC 转 YOLO 时用的是 xml 里的尺寸,而实际图片尺寸已经被压缩过。

解决:写脚本统一核对所有图片的真实分辨率,再将 xml 的 size 字段和框坐标按比例修正。这个数据集本身是 jpg 和标注同步打包的,正常情况下不会出现这个问题,但如果你手里同时还有别家数据,一定要提前做这个校验。

4.3 使用 labelImg 打开新标注图后,xml 中文路径保存失败

现象:labelImg 可以正常打开图片并画框,但保存 xml 时弹出报错,文件没写进去。

原因:labelImg 默认按 UTF-8 处理路径,如果你的整个数据集放在带中文的目录下,某些版本会直接抛异常。

解决:把整个数据集挪到纯英文路径下,比如D:/projects/tricycle_dataset而不是D:/数据集/三轮车。这份资源里 jpg、xml、txt 都是按英文文件名打包的,你只需要保证自己使用时的外层路径是英文即可。

4.4 验证集精度很高,测试集上对未见过的三轮车表现很差

现象:在划分好的 val 集上 mAP 有 0.85,但拿到网上随便找的三轮车照片一测,框要么漏检要么偏大。

原因:这个数据集拍摄场景的分布比较集中,背景、角度、光照都相对单一,模型学到的更像「这种照片里的三轮车」而不是「三轮车」本身。

解决:训练完先把这 559 张图里你的 val 子集拿出来逐张看预测结果,再用十张外部图片做泛化测试。如果泛化差距大,说明数据集本身覆盖度不够,需要补充不同场景、不同角度、不同远近的样本。更保险的做法是训练时做更重的随机增强,比如 random_perspective 和 scale 0.5,让模型见过更多变体。

4.5 训练时 loss 正常下降但 mAP 恒为 0

现象:loss 曲线正常收敛,但验证集 mAP_0.5 一直接近 0,完全不上升。

原因:验证集的标签没有正确加载。极有可能是划分脚本复制图片时,图片文件复制到了 val 的 images 目录,但标签复制到了 val 的 labels 目录之外的地方,或者 txt 文件名和 jpg 文件名不匹配。

解决:验证标签一致性,跑下面这段短脚本:

import os img_dir = "dataset/images/val" label_dir = "dataset/labels/val" img_names = {f.replace(".jpg", "") for f in os.listdir(img_dir) if f.endswith(".jpg")} label_names = {f.replace(".txt", "") for f in os.listdir(label_dir) if f.endswith(".txt")} missing = img_names - label_names extra = label_names - img_names print("Missing labels:", missing) print("Extra labels:", extra)

输出为空,说明标签与图片一一对应,问题就不在数据侧,要继续查训练脚本里的val路径是否写对。

5. 更进一步:利用这份数据集做迁移学习与模型轻量化

5.1 选预训练权重还是从零训练

对 559 张图的数据量,我强烈建议不要从零训练。YOLOv5s 从零训练在 COCO 上都要跑几百个 epoch 才像样,你这点数据量很难收敛出可用的特征提取器。正确做法是直接用yolov5s.pt作为预训练权重,只改最后输出层的类别数为 1。

python train.py \ --data dataset.yaml \ --weights yolov5s.pt \ --img 640 \ --batch 16 \ --epochs 100 \ --freeze 10

--freeze 10表示冻结前 10 层骨干网络的权重,让模型在初期只训练检测头,这样既不会破坏预训练提取到的底层通用特征,也能加速收敛。等到 loss 不再明显下降,再把 freeze 去掉微调整个网络。这种做法在这个量级的数据集上是比直接全量训练更稳的方案。

5.2 验证当前模型:用 val 集跑出 mAP 报告

训练完之后,建议先在验证集上做一次完整评估,不要一边训练一边等最终结果。YOLOv5 的 val 脚本会输出每个类别的 P、R、mAP 三个指标,还能生成 PR 曲线图。这里要看的不只是 mAP 数值,还要重点看 P 和 R 的差值——如果 R 明显低于 P,说明漏检比误检多,需要降低 confidence 阈值或增加训练数据;如果 P 低于 R,说明框打得太宽泛。

python val.py \ --data dataset.yaml \ --weights runs/train/exp/weights/best.pt \ --img 640 \ --conf 0.25 \ --iou 0.45

--iou 0.45是 NMS 的 IoU 阈值,值越小框越不会重叠,适合单类别且目标间距较大的场景。如果发现三轮车并排停靠时被合并成一个框,就把这个值往大了调,比如 0.65,让模型保留更多候选框。

5.3 导出到 ONNX 或 TensorRT:从实验到落地的最后一公里

训练出 best.pt 之后,如果目标是跑在边缘设备上,建议先导出 ONNX 再转 TensorRT 引擎。YOLOv5 的 export 脚本一条命令就能完成格式转换,但有个坑:导出时默认的--opset版本要和部署环境的 onnxruntime 兼容,否则推理时直接报错。

python export.py \ --weights runs/train/exp/weights/best.pt \ --include onnx \ --opset 12 \ --simplify

--simplify会调用 onnx-simplifier 对计算图做精简,能去掉不少冗余算子,推理速度快一些。转 TensorRT 时,如果目标设备是 Jetson,建议把精度模式设为 FP16,检测精度损失很小但速度能提升近一倍。我通常在导出后先写一个 10 行的推理脚本确认输出维度对不对,再进 TensorRT,不然排错要浪费很久。

这个数据集虽然小,但胜在格式干净、类别清晰,用来跑通整个训练、验证、导出、部署链路非常合适。而我自己的习惯是:无论数据集多简单,拿到手第一件事永远是跑一遍文件完整性校验和标签格式体检,从那以后几乎没再因为数据问题在训练中途翻过车。希望这份数据集和这篇文章里的坑位清单,能帮你把时间花在调模型而不是调数据上。

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

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

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

立即咨询