559张三轮车数据集:VOC转YOLO格式与labelImg手工标注实战
简介三轮车数据集包含559张真实场景图片聚焦“tricycle”单类别目标检测适合计算机视觉初学者与研究者用于YOLO、SSD等模型的训练与效果验证。所有图片均通过labelImg手工标注画框准确累计659个标注框并提供Pascal VOC和YOLO两种格式的标签文件可无缝对接主流检测框架省去格式转换的繁琐步骤。资源包整体约25.22MB内含1679个文件包括559个jpg图片、559个xml标注、559个yolo格式txt标注及少量附加说明文本目录结构简洁按需取用即可。目前已有641人浏览学习数据集标注质量可靠可直接用于目标检测实战项目或毕业设计帮助学习者专注模型调优而非数据整理。1. 559张三轮车数据集VOCYOLO双格式与labelImg手工标注的组合价值城市道路检测里三轮车是典型的小众类别公开数据集基本不覆盖自己采集又要承担标注成本。这套559张三轮车数据集把VOC与YOLO格式一次给齐全部由labelImg人工逐框标注拿到手可以直接进yolo训练流程。双格式意味着少踩转换坑VOC的XML喂给经典框架YOLO的txt归一化坐标直接进yolo目标检测流程。手工标注相比自动标注边界框更干净漏框与偏移这类批量污染少得多。下面的路径适合做三轮车、快递车等非机动车检测的工程师也适合刚入门yolo训练、需要干净样本练手的新手先拆格式再复现标注然后划分与校验最后反向排查漏标。2. VOC与YOLO双格式拆解三轮车标注的XML结构、txt归一化坐标与互转公式拿到559张数据的第一件事不是开训而是确认两种格式的目录与字段对得上。VOC和YOLO描述的是同一个边界框坐标系定义却完全不同这个差异会在训练阶段以各种隐蔽方式暴露出来。2.1 先看VOC的XMLannotation、size与bndbox三个关键节点VOC格式源自PASCAL VOC竞赛每张jpg对应一个同名xml。以一张1280x720的三轮车样本为例结构固定为annotation根节点下挂filename、size和若干个object块。annotation foldertricycle_images/folder filenameimg_0042.jpg/filename size width1280/width height720/height depth3/depth /size object nametricycle/name truncated0/truncated difficult0/difficult bndbox xmin112/xmin ymin240/ymin xmax486/xmax ymax612/ymax /bndbox /object /annotation逻辑说明size里的width与height是整个文件的坐标基准bndbox的四个值都是像素绝对坐标原点在图像左上角xmin/ymin是边框左上角xmax/ymax是右下角。图像里出现几辆三轮车就有几个object块一图多框时块与块之间没有强制排序labelImg按画框先后生成顺序。参数说明truncated表示目标是否被画面边缘截断手工标注时三轮车只有半边进图这个值应置1difficult表示目标因遮挡或模糊难以辨认训练流程通常会忽略difficult1的框。检查这套数据时重点看这两个字段是否与图像内容一致如果所有图都写0但画面里明显有截断目标说明标注时没处理边界语义训练出的框会整体偏大或偏小。2.2 YOLO的txt归一化坐标class_id、cx/cy与宽高的存储约定YOLO格式下每张jpg对应一个同名txt每行一个目标内容是五个数字0 0.2336 0.5917 0.2922 0.5167 0 0.7250 0.3819 0.1805 0.3333逻辑说明第一个数字是类别编号class_id从0开始。后面四个是边界框中心点x、y以及宽w、高h全部除以图像宽高做了归一化。换算关系是cx(xminxmax)/(2*width)w(xmax-xmin)/widthy与h同理。这份数据如果只标了tricycle一个类别所有行class_id都是0多类别时class_id范围在0到nc-1之间越界属于标注事故。参数说明归一化坐标必须落在0到1区间。训练时YOLO直接读txt进损失函数w或h出现负值会触发NaN某个坐标大于1会让anchor匹配错乱。拿到数据集先全量扫一遍数值范围比训练到一半报错再查快得多。2.3 VOC转YOLO的换算公式与边界情况处理已知图像宽W、高HVOC的坐标换算关系为cx(xminxmax)/(2W)cy(yminymax)/(2H)w(xmax-xmin)/Wh(ymax-ymin)/H。反向转换则xmin(cx-w/2)*Wxmax(cxw/2)*Wymin与ymax同理。换算里最容易忽略的是浮点精度float64转成6位小数的txt再转回像素坐标会产生1到2像素偏差对imgsz640的训练影响可忽略但做像素级评测时需保留更多小数位。import xml.etree.ElementTree as ET import os def voc_to_yolo(xml_path, out_dir, class_map): # 解析XML先拿size作为归一化基准 tree ET.parse(xml_path) root tree.getroot() width int(root.find(size/width).text) height int(root.find(size/height).text) lines [] for obj in root.iter(object): name obj.find(name).text if name not in class_map: continue 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) # 中心点与宽高全部除以图宽高保留6位小数 cx (xmin xmax) / 2 / width cy (ymin ymax) / 2 / height bw (xmax - xmin) / width bh (ymax - ymin) / height lines.append(f{class_map[name]} {cx:.6f} {cy:.6f} {bw:.6f} {bh:.6f}) base os.path.basename(xml_path).replace(.xml, .txt) with open(os.path.join(out_dir, base), w) as f: f.write(\n.join(lines))逻辑说明脚本先解析size得到基准宽高再遍历每个object把bndbox的像素坐标换算成归一化值按class_id cx cy w h的顺序写成txt。class_map参数把类别名映射成数字单类别三轮车数据集传{tricycle: 0}即可。参数说明保留6位小数是兼顾精度与文件体积的惯例。如果xml里有difficult1的框通常先过滤不写入txt。反向转换YOLO到VOC时用round而不是int直接int截断小数会产生系统性偏移。三种常见标注格式的坐标语义差异用一张对照表收敛格式文件后缀坐标基准每行/块结构典型消费方VOCxml像素左上角原点object块内含xmin/ymin/xmax/ymaxFaster R-CNN、SSD、labelImg默认YOLOtxt归一化class_id cx cy w hYOLOv5到v11、UltralyticsKITTItxt像素字段带语义标签class truncated occluded alpha 加bbox自动驾驶需字段级映射常见的坑是KITTI标注转YOLOKITTI的2D框虽然是像素坐标但每行开头是类别、截断程度、遮挡程度直接按YOLO的行序解析会把类别当成坐标。转换脚本必须逐字段映射不能只按空格切分后取后四位。任何数据集到手先确认坐标系定义再谈训练。3. labelImg手工标注559张三轮车的实战流程环境配置、快捷键与bbox判定标准标题里写的labellmg是LabelImg的常见手误拼法实际工具名是labelImg目标检测领域最常用的bbox标注器。559张这个量级完全适合手工标注按单张平均一个目标算熟练后一小时能标30到40张关键是把流程、类别文件和判定标准先定死。3.1 labelImg的安装与启动参数预定义类别文件怎么传yolo环境配置里最轻的一环就是labelImg安装Python环境直接pip装装完用两条命令启动pip install labelImg # 第一个参数是图片目录第二个是预定义类别文件 labelImg tricycle_images tricycle_classes.txt逻辑说明第一个位置参数是图片目录第二个是预定义类别文件每行一个类名。对559张三轮车数据classes.txt里写一行tricycle就够了标注时拉框后直接确认默认类别不用每次弹窗选。如果不传类别文件labelImg会进入自由输入模式同一个类别名大小写写法不一致时后面转YOLO会得到两个class_id数据集类别数直接翻倍这是手工标注最常见的事故之一。参数说明labelImg还有几个常用参数比如-d指定xml/txt输出目录、-l指定预标注导入目录。更关键的是保存格式界面里的PascalVOC与YOLO两种模式输出文件后缀不同一个存xml一个存txt。我一般全程用VOC模式标注标完后统一跑一次第2.3节的转换脚本生成YOLO格式避免两种格式文件混在同一个目录里后续校验逻辑翻倍。提示多人协作标注时所有人必须共用同一份tricycle_classes.txt并传给labelImg而不是各自新建能根除类别名不一致问题。3.2 提高手工标注效率的快捷键组合与自动保存labelImg的标注节奏是W键拉框、松手确认类别、D键翻页、自动保存落盘核心快捷键如下快捷键功能使用场景W进入拉框模式每张图开始的第一次操作A / D上一张 / 下一张翻图浏览配合自动保存CtrlS手动保存关闭自动保存时使用Delete删除选中框误拉背景框时直接删Ctrl滚轮缩放画布小目标放大后再框精度更高效率提升的常见做法是勾选View菜单里的Auto Save mode每次翻页自动落盘上一张的标注。手工标注最怕标了十几张程序崩溃自动保存配合A/D翻页能把丢标注的概率降到接近零。另一个习惯是每标完50张把标注目录复制一份做增量备份样本量到559张时备份目录也就几十MB成本很低。参数说明Ctrl滚轮缩放之后拉框坐标仍然是图像原始像素坐标不受画布缩放影响可以放心放大到局部再拖框。对有遮挡的三轮车场景放大后贴轮廓画框比整图直接拉更准xmin这类数值能控制在1到2像素波动内。3.3 三轮车bbox的边界判定标准框车身还是连车斗一起框手工标注一致性问题最常出现在边界框语义模糊。同一辆三轮车一个人标成只框驾驶位另一个人标成连车斗全框训练出来的预测框就会忽大忽小。推荐做法是把整车语义定成最小外接矩形车斗、车轮、骑行者的主体躯干全部算进框内伸出去的手或腿不强行纳入。遮挡场景按可见轮廓画框两辆三轮车重叠时允许框相交被完全挡死的位置不做猜测不在图像里的部分就地截断。三种常见误标要尽量避免。一是把单独行人误当骑行者骑行者与三轮车的分界在躯干与车把、座垫的接触关系只盯人没看车就会把路人拉进框。二是框沿卡在车轮外沿内侧三轮车的框应包含车轮触地点车轮被压线等于框整体偏小。三是重复框同一目标一辆车被画了两个相交框训练时产生两个高置信度预测NMS后保留哪一个完全随机。判定标准定完后做一次快速抽检随机抽50张看同一目标被不同人画的框IoU是否都在0.8以上低于0.8说明标准没执行到位需要回炉。4. 用559张三轮车数据训练YOLO前的数据准备yolo数据集划分、格式校验与训练参数对照进入yolo训练自己的数据集的标准准备阶段。559张不能全量灌进训练要先做yolo数据集划分再校验标注与图像的对应关系最后把data.yaml和训练参数按小样本量调整。漏掉任何一步排查loss曲线异常的时间都会加倍。4.1 按8:1:1划分train/val/test的Python脚本Ultralytics的目录约定是images与labels各分train、val、test三个子目录txt与jpg同名配对tricycle/ images/train/*.jpg images/val/*.jpg images/test/*.jpg labels/train/*.txt labels/val/*.txt labels/test/*.txt data.yaml划分脚本最省事的写法是shuffle后切片而不是按文件名顺序取前80%import os, random, shutil from glob import glob random.seed(42) # 固定随机种子保证划分可复现 imgs sorted(glob(tricycle/images/*.jpg)) random.shuffle(imgs) n len(imgs) parts { train: imgs[:int(n * 0.8)], val: imgs[int(n * 0.8):int(n * 0.9)], test: imgs[int(n * 0.9):], } for split, paths in parts.items(): img_out ftricycle/{split}/images lbl_out ftricycle/{split}/labels os.makedirs(img_out, exist_okTrue) os.makedirs(lbl_out, exist_okTrue) for p in paths: base os.path.splitext(os.path.basename(p))[0] shutil.copy(p, f{img_out}/{base}.jpg) lbl ftricycle/labels/{base}.txt if os.path.exists(lbl): shutil.copy(lbl, f{lbl_out}/{base}.txt)逻辑说明先shuffle再切片避免同一路段连续拍摄的三轮车图片全部落进train导致val与train场景过度相似mAP虚高。固定seed42保证每次重跑得到的划分完全一致实验结果可以复现。参数说明559张按8:1:1得到447张train、56张val与56张test。test集划分出来后就固定不动后续做增强消融只动val。单类别三轮车数据集用不到分层抽样如果以后扩到多类别且数量不均衡再把shuffle改成按类别分层。4.2 校验txt标注与图像对应关系的快速脚本标注文件与图片不匹配是手工数据集的常见病写一个全量校验脚本跑一次把所有问题一次性列出来from glob import glob imgs {p.split(/)[-1][:-4] for p in glob(tricycle/images/*.jpg)} lbls {p.split(/)[-1][:-4] for p in glob(tricycle/labels/*.txt)} print(缺标注图片数:, len(imgs - lbls)) print(孤儿标注数:, len(lbls - imgs)) for f in glob(tricycle/labels/*.txt): for line in open(f): parts line.split() if len(parts) ! 5: print(字段数错误:, f, line) break cls, cx, cy, w, h map(float, parts) # 单类别class_id只允许0坐标必须在0~1之间 if not (0 cls 1) or not (0 cx 1) or not (0 cy 1): print(坐标越界:, f, line)逻辑说明集合差集分别找出没有txt的图片和没有图片的txt逐行检查字段数与数值范围字段数不等于5说明YOLO行被污染坐标大于1说明归一化用了错误的分辨率基准。参数说明单类别数据class_id只有0所以判断范围是0到1多类别时把上界改成nc-1。孤儿标注主要是删图时忘了删txtUltralytics训练遇到孤儿txt会报missing image提前清理能省一次训练中断。VOC格式的XML还要额外检查每个object是否含完整四值bndbox和合法类名。4.3 data.yaml配置与三轮车数据集的训练参数建议目录就绪后写data.yaml表明类别数量与类别名路径用相对path指到data.yaml所在目录path: /data/tricycle # 数据集根目录 train: images/train # 训练图片相对路径 val: images/val # 验证图片相对路径 test: images/test # 测试图片相对路径 nc: 1 # 类别数 names: 0: tricycle # 类别编号与名称训练命令与参数按小样本场景调整yolo detect train modelyolo11n.pt data/data/tricycle/data.yaml \ epochs300 imgsz640 batch16 patience50 workers8参数说明imgsz640与常见标注分辨率接近缩放倍数最小边界框坐标误差也最小lr0从默认的0.01降到0.005小数据集初期不容易震荡patience50让验证指标连续50轮不升时提前停省算力同时避免过拟合。559张里如果平均每张不到2个目标正样本比例偏低损失函数里背景占绝大多数模型容易学成保守预测所以用yolo11n这种轻量权重先跑通pipeline确认val曲线正常后再换yolo11s提精度。关键参数推荐值与依据整理成表参数三轮车数据集建议值依据imgsz640缩放倍率小手工标注的1到2像素误差不被放大batch168到12G显存的安全值30系以上可提至32epochs300小样本需要较长训练配合早停patience50防止val过拟合后继续空转lr00.005目标稀疏初始学习率过大会发散cacheTrue559张可全量缓存进内存省掉IO等待逻辑说明cacheTrue对559张这种量级几乎不占内存却能把每个epoch的数据读取时间从秒级压到毫秒级一次完整训练能省下可观的墙钟时间。如果手上只有VOC格式的XML先跑第2.3节的转换脚本转成txt再训练Ultralytics主流程按txt读取绕开转换直接把XML目录喂进去不是常规做法。5. 漏标反向排查用50轮快速训练定位559张三轮车数据里的人工疏漏手工标注559张图漏标几乎是必然事件。与其逐张瞪眼复查不如让模型自己把可疑位置指出来这套反向排查思路能显著压缩质检时间。5.1 用短训练加高置信度预测找出没有标注的图片先用第4章的data.yaml训一个只跑50轮的yolo11n权重不追求精度只要模型对三轮车有基本响应。用这个半成品预测train集重点看那些ground truth为空、但模型置信度很高的图片from ultralytics import YOLO import os model YOLO(runs/detect/train/weights/last.pt) results model.predict(sourcetricycle/images/train, conf0.25, save_txtTrue, save_confTrue) for r in results: if r.boxes is None or len(r.boxes) 0: continue lbl r.path.replace(images, labels).replace(.jpg, .txt) gt_n 0 if os.path.exists(lbl): gt_n sum(1 for _ in open(lbl)) # 真实标注为0且模型置信度超过0.4标记为疑似漏标 if gt_n 0 and r.boxes.conf.max() 0.4: print(疑似漏标:, r.path, round(float(r.boxes.conf.max()), 3))逻辑说明模型在train集上见过这些图像如果在labelImg明确没画框的位置给出持续高置信度预测最可能的解释是人工漏标。只取置信度大于阈值的框相当于在完整yolo目标检测流程里提前做了一轮后处理过滤。输出聚焦gt_n0的图片这类图片即使漏一辆三轮车也会让训练时该图的损失少算一个正样本。参数说明conf0.25先过滤低质量预测判断漏标用0.4而不是0.5给阈值留余量。如果输出里大面积图片命中说明标注整体质量有问题先检查标注文件是否配对别急着补框如果命中集中在某几张图且置信度超0.6基本可以定位到真实漏标。5.2 SAM2做语义确认后再回labelImg补框对怀疑漏标的图片下一步用SAM2对预测框做语义确认。把可疑框作为box prompt输入SAM2分割出的mask边缘贴合三轮车轮廓说明该位置确有目标mask发散到背景多半是模型幻觉直接忽略。SAM2在这里只承担确认角色不直接产生检测框。三轮车密集重叠时SAM2可能把两辆车并成一个mask直接采用会引入新的边框误差。确认后的图片回到labelImg打开手动补一个符合第3.3节判定标准的框比自动生成框再修更省时间。补框完成后重新跑一次第4.1节的划分脚本把新增标注同步进train/val/test再回到第4.3节启动正式训练。按这个流程跑完500张级别的数据通常能抓出5到15个漏标框补完再重跑划分脚本后续正式训练前就不需要人工逐张复查了。本文还有配套的精品资源点击获取