简介:一份面向目标检测与YOLO系列模型训练的三轮车专用图像数据集,适合入门级与进阶开发者用于行人/车辆识别、物流场景感知等方向的数据准备与模型验证。资源共1679个文件,主要包括559张JPG图片、559个Pascal VOC格式的XML标注文件,以及配套的YOLO格式TXT标注文件,压缩包整体约25.22MB。数据集由labelImg工具手工画框标注,标注类别仅“tricycle”一类,总计659个真实框,定位准确、类别清晰,可直接用于YOLOv5、YOLOv8等框架的训练与评估。已有642人学习下载,对于需要快速获取规范标注数据、省去自行采集与标注环节的CV学习者来说,是一份省时且便于上手的实战资源。
1. 三轮车数据集为什么值得手工做:559张VOC+YOLO能解决什么
做目标检测的同行应该都有体会,公开数据集里汽车、行人、自行车一抓一大把,但三轮车这种“非标目标”反而很难找。共享电单车、快递车、农用三轮车,姿态和外形差异极大,公开的车辆检测模型拿到真实场景里经常漏检。于是自己攒数据就成了绕不开的路,而标题里的这套三轮车数据集走的是最扎实也最费时间的一条路:人工用 labellmg 逐张框选,同时保留 VOC 和 YOLO 两种格式。
559 张听起来不多,但手工标注的 559 张往往比自动扒下来的 5000 张噪声数据更可用。VOC 格式方便你随时改标注、看边界,YOLO 格式则直接喂给训练脚本。这套组合拳特别适合刚上手 YOLO 训练自己数据集的人:先拿它跑通“标注 → 转换 → 训练 → 验证”全流程,再决定要不要在这个基础上扩充数据。下面从格式差异讲起,把从 labellmg 到模型训练这条链路上的关键步骤和坑一次性说清楚。
2. 先搞清楚格式:VOC 与 YOLO 标注的差异和手工转换脚本
2.1 两种格式在存储和解析上的本质差异
VOC 格式源于 PASCAL VOC 竞赛,一个图片对应一个 XML 文件,里面记录了文件名、图片尺寸(width、height、depth)以及每个目标的类别和边界框坐标。边界框用 xmin、ymin、xmax、ymax 四个绝对像素值表示,坐标原点是图片左上角。这种格式的优点是可读性强,修改一个框的位置或类别直接编辑 XML 就行,也方便人在标注阶段排查错误。
YOLO 格式走的是另一条路:每个图片对应一个同名 TXT 文件,每一行代表一个目标,格式是“类别ID 中心点x 中心点y 宽度 高度”。这里的五个数值全部被归一化到 0~1 区间,用的不是像素绝对值,而是相对图片宽度和高度的比例。类别 ID 从 0 开始计数,具体哪个数字对应哪个类别,完全由训练时的 data.yaml 文件决定。
这种设计让 YOLO 训练时读取标注特别快:不用解析嵌套的 XML,每行五个浮点数直接进张量。但代价是肉眼几乎无法直接判断标注对不对,一个只有 0.4812 0.5322 0.2154 0.1876 的 TXT 文件,不还原成像素你根本不知道框在哪。所以我的习惯是标注阶段全部用 VOC 格式存,训练前再做格式转换,这样保留了后悔药。正式训练前我会写一个可视化脚本,把 YOLO 格式的标注画回图片上,确认转换没出错再开工。
2.2 用 Python 脚本把 VOC 转成 YOLO:坐标系换算与边界检查
转换的核心是把“左上角+右下角”的绝对坐标换算成“中心点+宽高”的归一化坐标。公式不复杂,但有几个坑:图片尺寸必须从 XML 的 size 节点读取,不能自己猜;转换后的宽高除以图片宽高后,结果必须落在 0 到 1 之间;类别名必须手动映射成整数 ID。
import os import xml.etree.ElementTree as ET def convert_voc_to_yolo(xml_path, output_dir, class_names): tree = ET.parse(xml_path) root = tree.getroot() # 图片尺寸必须从 xml 里读,不能假设固定值 size = root.find('size') img_w = int(size.find('width').text) img_h = int(size.find('height').text) txt_path = os.path.join(output_dir, os.path.splitext( os.path.basename(xml_path))[0] + '.txt') lines = [] for obj in root.iter('object'): name = obj.find('name').text if name not in class_names: continue # 遇到没在类别表里的目标,跳过并打印警告 class_id = class_names.index(name) box = obj.find('bndbox') xmin = float(box.find('xmin').text) ymin = float(box.find('ymin').text) xmax = float(box.find('xmax').text) ymax = float(box.find('ymax').text) # 坐标换算:绝对像素 -> 归一化中心点 + 宽高 x_center = (xmin + xmax) / 2.0 / img_w y_center = (ymin + ymax) / 2.0 / img_h w = (xmax - xmin) / img_w h = (ymax - ymin) / img_h # 裁剪越界值,防止训练时报 label out of bounds x_center = max(0.0, min(x_center, 1.0)) y_center = max(0.0, min(y_center, 1.0)) w = max(0.0, min(w, 1.0)) h = max(0.0, min(h, 1.0)) lines.append(f"{class_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}") with open(txt_path, 'w') as f: f.write('\n'.join(lines))这段脚本的逻辑很直白:解析 XML 里的 size 节点拿到真实宽高,遍历所有 object 节点提取类别和边框,做坐标换算后写入 TXT。参数里 class_names 的列表顺序就是训练时的类别 ID 顺序,必须和 data.yaml 保持一致,比如 ["tricycle"] 表示 ID 0 对应三轮车。
转换后我会再跑一段检查代码,统计有没有 TXT 文件为空、有没有类别 ID 超出范围、有没有文件数量对不上。因为 labellmg 可能漏存、误删,这类低级错误往往比模型本身更折磨人。
3. 用 labellmg 打 559 张三轮车图:工作流与关键设置
3.1 labellmg 安装与环境配置
labellmg 是目标检测标注里最常用的图形工具,PyQt5 界面,支持同时输出 VOC 和 YOLO 格式。安装有两种常见做法,我推荐在 Anaconda 环境里装,因为后面训练 YOLO 大概率也用同一个环境,省得来回切换环境配置。
首先创建一个干净的 conda 环境,然后装依赖和 labellmg 本体:
conda create -n label python=3.8 -y conda activate label pip install pyqt5==5.15.7 lxml pip install labelImg如果 pip 直接装失败,常见原因是 PyQt5 版本和系统图形库不兼容,这时候改用系统包管理器装 PyQt5 再跑源码方式。labellmg 的启动命令很简单,在终端里输入labelImg回车就会弹窗。打开后的默认界面是英文,在 View 菜单里可以切换 Chinese,不切换也不影响使用,核心快捷键就那么几个。
启动后的第一件事是设置自动保存模式。快捷键 Ctrl+Shift+S 打开“自动保存”开关,这样每切到下一张图时上一张的标注会自动落盘,极大降低忘保存导致标注丢失的概率。另一个必改的设置是默认标注目录,否则每次打开图片都要重新选一次输出目录。
3.2 三轮车这类细长目标的标注策略
三轮车和普通轿车不一样,它在外观上存在“车头+货斗”的明显分段,侧面看尤其长。新手常见的错误是把车头和货斗拆成两个框,或者反过来把后面跟着的拖挂也算进去。我的建议是一个目标一辆车,整车的几何外接框作为一个标注框,原因是你做检测的最终目的是判断“这里有一辆三轮车”,而不是拆零件。
然后是边界框的贴合度问题。labellmg 里用鼠标拖出的框经常比目标大一圈,如果训练数据里所有框都偏大,模型学到的就是“宽松的框”,推理时定位精度会变差。手动调整时把框的四边贴住车身的最外侧像素,轮子下沿要覆盖到,车顶篷最高点也要包含进去,宁可略紧不要过松。对于被遮挡的三轮车,如果遮挡面积超过一半,这个目标可以不标;如果只被电线杆挡了一小块,按可见部分正常标注,这对模型学习遮挡鲁棒性有帮助。
类别定义上,559 张图如果全部只有一类“tricycle”,训练会简单很多,但如果你在真实场景里还要区分“载人三轮车”和“载货三轮车”,那就必须在标注阶段就分开两个类,否则后期重新标注的工作量是灾难级的。手工标注特别费时,我一般建议第一版只做单类,跑通流程后再考虑细分。
3.3 标注质量的抽查与修正
559 张图全部标注完,你会发现人的注意力是会衰减的。标到 200 张以后,鼠标框选的精度明显下降,偶尔还会漏掉角落里的目标。所以每标完一批,我会用随机抽样画框检查的方式做质量抽检,把标注结果可视化出来人工过目。
import cv2 def draw_yolo_boxes(image_path, label_path, class_names, output_path): img = cv2.imread(image_path) h, w = img.shape[:2] with open(label_path, 'r') as f: for line in f.readlines(): parts = line.strip().split() cls_id, xc, yc, bw, bh = int(parts[0]), float(parts[1]), \ float(parts[2]), float(parts[3]), float(parts[4]) # 反向从归一化坐标还原成像素坐标 x1 = int((xc - bw / 2) * w) y1 = int((yc - bh / 2) * h) x2 = int((xc + bw / 2) * w) y2 = int((yc + bh / 2) * h) color = (0, 255, 0) cv2.rectangle(img, (x1, y1), (x2, y2), color, 2) cv2.putText(img, class_names[cls_id], (x1, y1 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.6, color, 2) cv2.imwrite(output_path, img)这个脚本做的事情是把 YOLO 标注画回图片上,参数里 class_names 必须和转换脚本用同一份列表。跑完抽样后一张张看图,重点看框有没有偏移、有没有漏掉小目标、类别有没有标错。抽样比例我一般取 15%~20%,全部 559 张里抽查 80 到 100 张。
抽查发现的问题如果是成片出现的,比如某几十张图的框都整体偏左,那就是标注时手抖了。这时候不要一张张手动改,回到 labellmg 里按批次修正更高效,因为 XML 格式下框的位置一目了然,比对着 TXT 猜坐标靠谱得多。
4. 训练自己的三轮车检测模型:目录结构与超参数设置
4.1 数据集目录结构和 data.yaml 配置
把 VOC 转成 YOLO 并完成质量抽检后,下一步是组织训练集目录。YOLO 官方仓库对目录结构有明确约定:图片和标签分开存放,并各自拆分 train 和 val 两个子目录,标签文件和图片必须同名,只是扩展名不同。
这是一个最小可用的数据集目录结构:
tricycle_dataset/ ├── images/ │ ├── train/ │ │ ├── 0001.jpg │ │ └── ... │ └── val/ │ ├── 0500.jpg │ └── ... ├── labels/ │ ├── train/ │ │ ├── 0001.txt │ │ └── ... │ └── val/ │ └── 0500.txt └── data.yaml划分比例方面,559 张图我一般取 8:2,也就是约 447 张训练、112 张验证。如果图片来源是同一段视频连续抽帧,直接随机划分会导致训练集和验证集高度相似,验证指标虚高。正确做法是先把图片按场景分组,再按组划分。有了目录结构还要写 data.yaml,这是告诉模型“去哪里找数据、有几个类别、类别叫什么”的配置文件。
# data.yaml 内容 train: tricycle_dataset/images/train val: tricycle_dataset/images/val nc: 1 names: ['tricycle']data.yaml 里的 path 如果填的是相对路径,就要保证运行训练命令时的工作目录和这个相对路径对得上。
4.2 训练命令与必调参数:batch、epoch、imgsz
目录和配置文件就绪后开始训练。YOLOv5 和 YOLOv8 是现在用得最多的两个版本,命令略有差异但核心参数名基本一致。我以 YOLOv5 为例,先做一次短训练验证流程通不通,再跑完整训练。
# 安装依赖(第一次需要) pip install -r requirements.txt # 先跑 10 个 epoch 验证流程 python train.py --img 640 --batch 16 --epochs 10 \ --data tricycle_dataset/data.yaml \ --weights yolov5s.pt --name tricycle_test参数从功能上可以分成三类。第一类是输入控制:--img 指定训练分辨率,640 是基础值,如果图片里三轮车只占很小一块,建议先把它切块或改成分辨率更高一档,但显存占用会随之上涨。第二类是训练强度:--batch 是批大小,16 是 8GB 显存下的稳妥值,--epochs 是训练轮数,三轮车这类单类别任务 100 轮基本够用。第三类是模型结构:--weights 指定预训练权重,yolov5s.pt 是参数量较小的版本,559 张的数据量根本喂不饱大模型,用 s 甚至 n 型号才是合理的,直接上 xl 很容易过拟合。
用 YOLOv8 的话命令改成如下形式:
yolo detect train data=tricycle_dataset/data.yaml \ model=yolov8s.pt epochs=100 imgsz=640 batch=16这里 model 参数既可以用官方预训练权重,也可以直接指定模型架构配置文件,训练中途停止后还能用resume=True接着跑。YOLOv8 的这套命令行风格我个人感觉比 v5 更清新,但底层训练逻辑差别不大。
4.3 训练日志怎么看:loss 和 mAP 对应什么问题
训练启动后终端会滚动输出每一轮的指标,很多人盯着数字看半天不知道哪些该关心。我一般按优先级分成三块:第一是 box_loss、cls_loss、dfl_loss 三个损失值,它们只要在持续下降就是正常;第二是验证集上的 precision、recall 和 mAP,这三项才是模型真实水平的反映;第三是训练速度,它只提示系统性能,不影响模型质量。
关于 yolo 损失函数,box_loss 负责回归边界框的坐标,cls_loss 负责分类是否准确,dfl_loss 是分布焦点损失,主要优化框的边沿精度。三轮车数据量小,常见的翻车现场是训练 loss 一直降但 mAP 上不去,这多半是过拟合了。判断方法很简单:看 box_loss 在训练集和验证集上的差距,如果验证集 loss 在某个 epoch 后开始反弹而上训练集还在降,说明模型开始死记硬背训练图片,这时候早停就够了,继续跑只会让结果更差。
训练结束后会在 runs/detect/train 目录下生成 weights/best.pt 和 weights/last.pt,测试和部署时用 best.pt。
5. 三轮车数据集避坑清单:从标注到训练阶段的常见问题与排查
5.1 现象:labellmg 标注完了但训练时报“Can't find label for image”
训练启动后提示找不到某张图的标签文件,打开 labels 目录一看,确实缺了几个 TXT。原因通常是 labellmg 在标注过程中自动保存没有打开,切换图片时部分图跳过了保存,或者图片文件名里有空格和特殊字符,转换脚本处理时同步失败。解决方法是转换前先把所有文件名统一成纯数字前缀,比如 0001.jpg、0002.jpg 这种格式,再重新执行转换脚本,并在转换后对比图片和标签的文件数量。
5.2 现象:loss 训练正常,但预测时所有框都偏向图片左上角
这是坐标系换算翻车了。YOLO 格式要求的是中心点坐标加宽高,如果转换脚本里算的是左上角坐标除以图片宽高,模型学到的就是错误映射。遇到这种情况不要猜,返回到 2.2 节的转换脚本,找一个已知样本手算一遍坐标再对照 TXT 里的数值,确认是 x_center 用的(xmin+xmax)/2而不是xmin,y 轴同理。坐标系这种错位,代码 review 很难看出来,跑一次反向可视化立刻暴露。
5.3 现象:验证集 mAP 很高,但实际图片上三轮车就是检测不到
这是最迷惑人的场景,常见于数据划分不当。如果 559 张图里同一场景的连续帧被同时分进了训练集和验证集,验证指标会“虚假繁荣”,但换个角度、换个背景的真实图片模型立刻不行。解决方法是在划分数据前先按拍摄场景聚类,让同一场景的图片全部进训练或全部进验证。另一个可能原因是标注框过紧或过松导致特征学习不充分,检查第 3.2 节的标注策略,遮挡严重的目标漏标也会让模型误以为“三轮车必须完整可见才算目标”。
5.4 现象:训练时报“image not found”或“label out of bounds”
如果训练日志里出现这种报错,十有八九是图片路径里带了中文或空格,YOLO 的读取逻辑对这类路径支持不好。解决方法是把数据集整体挪到纯英文路径下,图片文件名也改成纯数字。另一个常见诱因是第 2.2 节提到的边界框越界问题,检查转换脚本里是否做了归一化后的裁剪,如果某个框的左边界超出了图片宽度,训练时就会抛异常。我在写转换函数时把越界值裁剪到 0~1 区间,这个处理不要省。
5.5 现象:epoch 已经跑满但 mAP 还在缓慢爬升,要加轮数吗
559 张的小数据集上,mAP 在 80 轮之后还缓慢上升是可能的,但收益极低。这时候该做的是调数据而不是加算力:检查是不是所有三轮车都在白天场景,有没有夜间和雨天样本;用 Mosaic 数据增强扩大有效数据量;或者用已训练好的模型做半自动预标注,人工修正后扩充到 1000 张以上。数据扩充的优先级永远高于堆 epoch,这是我的核心经验。
6. 用验证脚本和测试图片做最后把关:置信度阈值与漏检权衡
训练结束后不要急着部署,先用 test 图片做一轮感知验证。用训练时预留的 112 张验证图跑val.py拿到指标,再额外找一些训练集里绝对没出现过的三轮车图片做鲁棒性测试。小数据集的通病是“见过才认得”,这个阶段你会发现白天场景 mAP 0.93,但同一辆车换个黑暗背景就检测不到了,这很正常,559 张的底子决定了上限。
一个很容易被忽略但影响极大的参数是置信度阈值。YOLO 默认的 conf 阈值在 0.25 左右,如果调低到 0.1,漏检会减少但误检增多;调高到 0.5 则反过来。我的习惯是用验证集做一次阈值扫描,把 precision 和 recall 的曲线画出来,找两者交叉点的阈值作为部署默认值。三轮车检测通常偏重少漏检,宁可多报一个不可放过一个,所以阈值我会设在 0.15~0.2 之间。
最后一公里的建议是保留完整的标注原始文件。559 张的 VOC XML 和 YOLO TXT 都别删,后续要扩充类别、修正边界、做数据蒸馏都靠它们。我自己就吃过亏:一次整理磁盘时清掉了 XML 只留了 TXT,后来想细化“载货三轮车”类别,只能重新打开 labellmg 一张张补标,那种感觉确实不好受。这套数据集的方向是靠谱的,关键是别把原始资产搞丢了。希望帮到你。
本文还有配套的精品资源,点击获取