车辆数据集YOLO标签格式转换与yolov8训练实战
发布时间:2026/10/2 8:37:30来源:尧图网络
简介面向目标检测学习者与算法开发者的YOLO车辆检测数据集含汽车、自行车、公共汽车三类共969张带标签图像已经完成训练/验证测试划分并内置data.yaml配置兼容YOLOv5至YOLO11等多版算法下载后即可直接训练、验证与测试。包内共2000个文件包括969个XML与969个TXT标签文件分别对应VOC与YOLO两种标准格式两类标签分文件夹存放配合61张JPG原始图像和1个YAML配置文件可满足完整的目标检测实验流程。YOLO标签采用归一化坐标表示记录了类别索引、中心点位置和宽高比例便于理解数据组织方式也方便在不同框架或转换工具间复用。资源压缩包约57.08MB目录结构清晰省去自行采集、标注和划分数据的环节可快速用于课程设计、论文实验或实际场景中的车辆检测项目。已有164人学习下载适合想专注算法调优而非数据准备的目标检测入门及进阶用户。1. yolo 车辆数据集969 张标注图能帮你跑通哪一步做目标检测的同行大多有过这种经历模型选好、环境装好手里却缺一份能直接喂给 yolo 算法的车辆数据集。自己爬图标注几小时才凑出百来张标完还得操心格式对不对。这份 969 张图像的车辆数据集包含汽车、自行车、公共汽车三个类别标签同时提供 yolo 格式txt和 voc 格式xml附带 data.yamltrain/val/test 已划分完毕。它的价值不在量大而在开箱即用解压后改个路径就能进 yolov5、yolov7、yolov8、yolov9、yolov10、yolo11 训练。适合刚想跑通第一个检测模型的新手也适合拿它做基准验证的熟手。2. 双格式标签的坐标换算逻辑YOLO txt 与 VOC xml 到底差在哪拿到压缩包先别急着开训把两种标签格式的坐标换算逻辑搞清楚后面训练报错时你才知道去哪查。这份数据集把 yolo 格式和 voc 格式的标签分文件夹存放本质上同一批标注目标用两种坐标系各写了一遍。YOLO 用的是归一化比例值VOC 用的是绝对像素坐标两者之间就靠图片的实际宽高做桥梁。2.1 YOLO 归一化坐标0~1 比例值的计算与解读yolo 格式的每个 txt 文件里每一行对应一个目标结构是五个字段class x_center y_center width height其中class是类别索引从 0 开始本数据集里 0 对应 car1 对应 bicycle2 对应 bus。后面四个值全部是相对图片宽高的比例范围在 0 到 1 之间。也就是说x_center不是目标框中心点的像素 x 坐标而是中心点 x 除以图片宽度得到的商。同理width是目标框像素宽度除以图片宽度的比例。举例图片宽度为 1280 像素某个汽车框中心点 x 坐标为 640框宽为 320那 txt 里记录的就是x_center 640 / 1280 0.5width 320 / 1280 0.25。换一张宽度为 640 的图同样位置和比例的框yolo 标签写出来的还是0.5和0.25分子变了但比例不变。这个设计的直接好处是模型训练时不受输入图片分辨率影响。yolov8 训练时会把图像缩放到 imgsz比如 640x640如果标签里存的是绝对像素缩放后所有框都得跟着重算存成比例值标注天然适配任意输入尺寸。代价是解析时你必须知道图片的真实宽高否则没法把比例还原成像素坐标去做可视化或评估 IoU。提示训练前抽查一个 txt 文件如果发现某个值大于 1 或者小于 0说明标签的归一化过程出了岔子这种情况后面第 5 章会专门讲怎么排查。2.2 VOC xml 绝对坐标bndbox 结构与像素坐标读取voc 格式则完全不同它是 Pascal VOC 的标注规范用 xml 描述每个目标的类别和像素级边界框。一段典型的 voc 标签长这样annotation filenameimg_0689_348.jpg/filename size width1280/width height720/height depth3/depth /size object namecar/name bndbox xmin480/xmin ymin300/ymin xmax800/xmax ymax620/ymax /bndbox /object /annotationsize节点记录图片宽高object节点里name是类别名bndbox里四个值就是左上角和右下角的绝对像素坐标。标注工具 labelImg 默认就输出这种格式很多老项目的可视化脚本也只认 voc所以虽然 yolo 训练不需要 xml但当你需要把数据喂给非 yolo 框架或者用视觉工具做二次检查时xml 就派上用场了。两种格式的换算关系可以整理成一张对照表含义yolo txt 字段voc xml 节点换算公式类别第一列索引object/name索引转名称靠 names 列表框中心 xx_center比例(xmin xmax) / 2像素值除以图片宽度框中心 yy_center比例(ymin ymax) / 2像素值除以图片高度框宽width比例xmax - xmin像素宽除以图片宽度框高height比例ymax - ymin像素高除以图片高度注意size里的宽高不一定可信有些标注工具在调整图片后没同步更新 size 节点。我一般直接用 OpenCV 或 PIL 读图片真实尺寸而不是信任 xml 里写的值这个习惯在转换脚本里尤为重要。2.3 data.yaml 与目录结构数据集划分的约定yolov5 之后的大多数 yolo 版本都用 data.yaml 描述数据集这份压缩包里已经带好了。核心内容就是三个路径加类别信息train: D:/vehicle_969/train/images val: D:/vehicle_969/val/images test: D:/vehicle_969/test/images nc: 3 names: [car, bicycle, bus]train、val、test分别指向训练、验证、测试集的图片目录nc是类别总数 3names按索引顺序列出类别名。yolov8 训练时读取标签的规则是图片在train/images标签就在同级的train/labels目录文件名与图片同名只是扩展名换成.txt。解压后先跑一条命令确认结构是否符合这个约定cd /path/to/vehicle_969 find . -maxdepth 2 -type d | sort常见的目标结构长这样vehicle_969/ ├── data.yaml ├── train/ │ ├── images/ │ │ └── img_0689_348.jpg │ └── labels/ │ └── img_0689_348.txt ├── val/ │ ├── images/ │ └── labels/ └── test/ ├── images/ └── labels/如果解压后看到的是单独的 images 文件夹加两个标签文件夹yolo 目录和 voc 目录说明划分信息可能在 data.yaml 里以别的字段体现或者压缩包内另有划分清单。无论哪种组织方式核心原则不变训练前必须确认「图片、txt 标签、xml 标签三方文件名能对上」这是后面一切操作的地基。3. 把数据集跑进 yolov8data.yaml 修改、训练启动与推理验证格式逻辑清楚之后下一步就是让数据真正转起来。yolov8 是目前用起来最省事的版本一条命令就能完成训练但「省事」的前提是环境干净、路径正确、参数合理。这章按我自己的操作顺序来讲你照着走一遍就能出权重。3.1 环境准备与目录核验先装 ultralytics 包它会连带装好 torch 和 torchvisionpip install ultralytics装完确认版本和显卡状态python -c import ultralytics; print(ultralytics.__version__) nvidia-smi有显卡就放心用 GPU 训练显存低于 6G 的话建议用yolov8n或yolov8s这类轻量权重起步。没有显卡也可以跑 CPU把epochs调小、imgsz调到 416 就行。接着做标签体检我习惯用一段极简脚本扫描整个数据集确认标签文件数量和图片数量能对上格式没有明显异常import os from pathlib import Path base Path(D:/vehicle_969) for split in [train, val, test]: img_dir base / split / images lbl_dir base / split / labels if not img_dir.exists(): print(f{split}: images dir not found) continue imgs {p.stem for p in img_dir.glob(*.jpg)} lbls {p.stem for p in lbl_dir.glob(*.txt)} missing imgs - lbls extra lbls - imgs print(f{split}: images{len(imgs)}, labels{len(lbls)}, fmissing{len(missing)}, extra{len(extra)})这段脚本把三个划分下的图片和标签按文件名去掉扩展名做集合差。missing表示有图但没标签extra表示有标签但没图。正常情况两个数都应该是 0出现任何一个都说明文件配对有问题直接进训练必然浪费时间。3.2 data.yaml 配置与训练命令打开 data.yaml把路径改成你机器上的实际绝对路径。注意 yaml 文件里路径不要带中文Windows 用户尤其要避开C:\用户\张三\桌面这种路径OpenCV 对非 ASCII 路径支持很差训练时图片会一批批地读不出来。改完后启动训练yolo detect train \ dataD:/vehicle_969/data.yaml \ modelyolov8n.pt \ epochs100 \ imgsz640 \ batch16 \ device0几个关键参数的含义和调整思路modelyolov8n.pt预训练权重n 是 nano 最轻量版。第一次跑建议用 n验证流程没问题再换 s 或 m 提精度。epochs100训练轮数。969 张图的数据量不大100 轮足够收敛跑完看曲线如果还在下降可以续到 150。imgsz640训练时统一缩放到 640x640。本数据集图片如果本身是 1280x720 之类的分辨率640 是稳妥的默认值想保留小目标细节可以设 768但显存占用会明显上升。batch16批大小按显存调整。6G 显存用 16 配 640 可能会爆报CUDA out of memory就降到 8 或 4。device0使用第一块 GPU。CPU 训练就改成devicecpu同时把 epochs 降到 30 先验证流程。如果你用的是 yolov5 仓库命令会稍不同python train.py --data data.yaml --weights yolov5s.pt --img 640 --batch 16 --epochs 100标签格式两者通用txt 文件和 data.yaml 的结构一样所以本数据集在 yolov5 和 yolov7 上也都能直接用不用做额外转换。3.3 权重验证图片推理与指标读取训练结束后最优权重在runs/detect/train/weights/best.pt。用它对测试集做一次推理既验证模型效果也顺手确认标签格式从头到尾都没问题yolo detect predict \ modelruns/detect/train/weights/best.pt \ sourceD:/vehicle_969/test/images \ saveTrue \ save_txtTruesaveTrue保存画了检测框的图片save_txtTrue把预测结果导出成 yolo 格式文本方便后续和真实标签做对比。预测完输出目录里会有一个labels子文件夹每个 txt 的格式和训练标签一模一样第一列是类别索引后面是归一化的框坐标。训练过程的指标在runs/detect/train/results.csv里用 pandas 读一下就能看到每个 epoch 的mAP50和mAP50-95。本数据集类别少、目标大100 轮正常结果 mAP50 应该在 0.8 以上如果远低于这个数别急着调模型先回头查标签问题大概率在数据侧。4. 标签互转实战txt 与 xml 双向转换脚本及边界处理这份数据集同时给了 txt 和 xml多数情况下你不需要自己转换。但换个角度想正因为两种格式都有你完全可以拿它当「标准答案」来校验自己的转换脚本。以后拿到只有单格式的公开数据集时这套脚本就是你的后悔药。4.1 xml 转 txt读取 bndbox 并做归一化从 voc 转 yolo 的核心就一句话把 bndbox 的四个像素值换算成 0~1 比例。但真正写代码时有两个坑图片宽高从哪读、越界框怎么处理。import xml.etree.ElementTree as ET from pathlib import Path import cv2 CLASSES [car, bicycle, bus] def xml_to_yolo(xml_path: str) - list: tree ET.parse(xml_path) root tree.getroot() # 图片宽高以实际文件为准不信任 xml 里的 size 节点 img_path xml_path.replace(.xml, .jpg) img cv2.imread(img_path) img_h, img_w img.shape[:2] lines [] for obj in root.iter(object): name obj.find(name).text.strip() if name not in CLASSES: print(fskip unknown class: {name}) continue cls_id CLASSES.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) # 越界修正某些标注工具会画出图像边界外的框 xmin max(0, min(xmin, img_w)) xmax max(0, min(xmax, img_w)) ymin max(0, min(ymin, img_h)) ymax max(0, min(ymax, img_h)) 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 if w 0 or h 0: continue lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) return lines参数说明CLASSES列表的顺序必须和 data.yaml 里的 names 完全一致否则类别索引会整体错位。img_w、img_h我用 cv2 从图片文件读而不是读 xml 的 size 节点因为实际项目里经常遇到 xml 的 size 是旧值的情况。越界修正用min(max(...))把坐标夹取回图像范围内这是转换脚本里最容易被忽略的一步——标注框画出去一点在 labelImg 里显示不出来但训练时会让模型学到错误的边界信息。批量转换时对每个 xml 调一次这个函数写出的 txt 存到目标 labels 目录就行。4.2 txt 转 xml反算像素坐标与越界修正反向转换是把 0~1 比例还原成像素值公式是像素 比例 × 图片宽高。注意还原后要用int()取整因为 xml 的 bndbox 规定是整数像素坐标。import xml.etree.ElementTree as ET import cv2 CLASSES [car, bicycle, bus] def yolo_to_xml(txt_path: str, out_xml_path: str) - None: img_path txt_path.replace(.txt, .jpg) img cv2.imread(img_path) img_h, img_w img.shape[:2] root ET.Element(annotation) filename ET.SubElement(root, filename) filename.text str(Path(img_path).name) size ET.SubElement(root, size) ET.SubElement(size, width).text str(img_w) ET.SubElement(size, height).text str(img_h) ET.SubElement(size, depth).text 3 for line in Path(txt_path).read_text().strip().splitlines(): if not line.strip(): continue parts line.split() cls_id int(parts[0]) x_center float(parts[1]) y_center float(parts[2]) w float(parts[3]) h float(parts[4]) # 反算像素坐标并夹到图像边界内 xmin int(max(0, (x_center - w / 2) * img_w)) ymin int(max(0, (y_center - h / 2) * img_h)) xmax int(min(img_w, (x_center w / 2) * img_w)) ymax int(min(img_h, (y_center h / 2) * img_h)) if xmax xmin or ymax ymin: continue obj ET.SubElement(root, object) ET.SubElement(obj, name).text CLASSES[cls_id] bndbox ET.SubElement(obj, bndbox) ET.SubElement(bndbox, xmin).text str(xmin) ET.SubElement(bndbox, ymin).text str(ymin) ET.SubElement(bndbox, xmax).text str(xmax) ET.SubElement(bndbox, ymax).text str(ymax) tree ET.ElementTree(root) tree.write(out_xml_path, encodingutf-8, xml_declarationTrue)这里最容易犯的错是反算后不取整直接写浮点数voc 规范里 bndbox 是整数labelImg 打开时可能解析异常。另外反算后会遇到 xmax 小于 xmin 的退化情况必须跳过否则生成的 xml 打开就是错的。4.3 转换过程里的两类坑空标签与文件名错位转换脚本写对了批量跑的时候还有两个隐蔽问题。第一个是空标签。有的图片确实没有目标对应 txt 或 xml 文件是零字节的。转换时如果直接read_text()会得到空字符串循环自然跳过没问题。但如果你在转换前用「有文件就算有标签」的逻辑去统计空文件会被算成有效标签导致后面训练日志里这张图显示 no labels found。我一般会在转换脚本里额外输出空文件清单确认哪些图真的没目标而不是文件损坏。第二个是文件名错位。批量转换时如果用xml_path.replace(.xml, .jpg)去关联图片前提是文件名一一对应。但有些数据集里图片扩展名混用 jpg 和 jpeg或者 xml 文件名与图片文件名本身就不一致。稳妥的做法是先构造{stem: img_path}的映射再遍历 xml 去查查不到就报错而不是静默跳过。本数据集文件名是规范的img_0689_348.jpg这类编号一般不会出问题但写成防御式总没坏处。5. 避坑实录车辆数据集从解压到出模型的五个翻车现场这部分是我在类似数据集上反复踩过的坑每一条都是真金白银换来的。按「现象 → 原因 → 解决」写你对照自己的报错信息就能定位。5.1 解压后路径带中文导致训练失败现象Windows 上把 zip 解压到桌面或D:\数据集\车辆969这类中文路径改好 data.yaml 启动训练报FileNotFoundError或者某些图片加载失败而且失败是零星的不是全部图片都读不出来。原因OpenCV 的imread在 Windows 上对非 ASCII 路径支持不稳定中文字符在处理时可能乱码导致文件找不到。yolov8 的 dataloader 对不同图片可能走不同读取分支所以表现是随机失败特别迷惑人。解决解压到纯英文路径比如D:\datasets\vehicle_969data.yaml 里所有路径用绝对路径且全英文。zip 解压后我习惯先看一眼目录树确认没有乱码目录名再跑训练。5.2 类别索引越界class index 3 is out of range现象训练刚启动数据集加载阶段报错提示标签里的类别索引超出了nc定义的范围比如报出索引 3但 data.yaml 里nc: 3、names只有 0、1、2 三个类别。原因txt 标签文件第一列写的是 3说明标注时出现过第 4 类或者某个 voc 转 txt 的脚本用了不同的类别顺序把 bus 映射成了 3 而不是 2。解决扫描所有 txt 标签找出第一列的最大值跟nc-1对比from pathlib import Path label_dir Path(D:/vehicle_969) max_cls -1 for txt in label_dir.rglob(*.txt): for line in txt.read_text(encodingutf-8).strip().splitlines(): if not line.strip(): continue cls int(line.split()[0]) if cls max_cls: max_cls cls print(max class index found:, max_cls)如果最大值等于nc或更大用脚本把那行改掉或者回头检查 voc 标签里的类别名是否超出了[car, bicycle, bus]这个集合。5.3 可视化看不到标注框文件名错位与空标签现象训练进行中日志里显示no labels found或者标签数量为 0用可视化工具打开图片也看不到任何检测框但标签文件确实存在。原因文件名对不上。比如图片是img_0689_348.jpg标签却是img_0689_349.txtyolo 的 dataloader 按同名字典匹配图片和标签匹配不上就默认这张图没有标签。另一种原因是标签文件是零字节空文件内容为空自然没框。解决用 3.1 节那段集合差脚本重新检查一遍missing和extra的数量。空文件就看文件大小find D:/vehicle_969 -name *.txt -size 0c -print这条命令列出所有零字节的标签文件确认它们是「真的没有目标」还是「转换时写空了」。5.4 验证集指标虚高类别分布不均造成的假象现象训练完看 results.csvval 的 mAP50 高达 0.92很漂亮。但随手拿几张测试图推理自行车和公交车的检测效果明显拉胯误检、漏检不少。原因数据集划分时没有按类别分层。如果验证集里 80% 的框都是汽车模型只要把汽车学好mAP 就能被拉得很高而自行车、公交车的真实水平被稀释了。解决拿到任何数据集先统计每个划分下的类别实例数。对 yolo 标签来说直接数每个 txt 的行首数字from collections import Counter from pathlib import Path def count_classes(label_dir: str) - Counter: counter Counter() for txt in Path(label_dir).glob(*.txt): for line in txt.read_text().strip().splitlines(): if line.strip(): counter[int(line.split()[0])] 1 return counter for split in [train, val, test]: c count_classes(fD:/vehicle_969/{split}/labels) print(split, dict(sorted(c.items())))如果验证集里某类只有几个实例训练时要么别用随机划分按类别比例分层抽样要么把该类样本补到验证集里。本数据集已经划分好正常来说分布是均匀的但拿到手自己确认一遍总归放心。5.5 训练不收敛标签坐标越界与 imgsz 过高的叠加效应现象loss 前几十个 epoch 不降反升mAP 全程为 0训练结束后 best.pt 和 last.pt 几乎没有区别。原因常见是两个因素叠加。一是标签坐标越界比如width或height为 0、中心点超出 0~1模型在 loss 计算时拿到无效的目标框二是imgsz设得比图片实际分辨率还大导致图片被暴力放大目标被插值糊掉。解决先扫标签越界再调imgsz。越界扫描脚本from pathlib import Path for txt in Path(D:/vehicle_969).rglob(*.txt): for i, line in enumerate(txt.read_text().strip().splitlines(), 1): if not line.strip(): continue parts line.split() if len(parts) ! 5: print(f{txt}:{i} bad field count: {line}) continue _, xc, yc, w, h map(float, parts) if not (0 xc 1 and 0 yc 1 and 0 w 1 and 0 h 1): print(f{txt}:{i} out of range: {line})运行后如果有输出把对应行修正或删除没有输出说明标签本身干净那就把imgsz调小一档比如从 640 降到 512或者把学习率从默认值往下调。yolov8 默认 lr 是 0.01小数据集上如果扩增太猛可以加--lr0 0.005试试。6. 让 969 张图发挥更大价值数据增强与混淆矩阵验证模型跑通只是第一步。969 张图不算多想让最终效果更扎实重点放在两个地方训练时的数据增强策略和训练后的错误分析。6.1 按场景开关 mosaic 增强yolov8 默认开启 mosaic 增强把四张图拼成一张训练对小目标检测和类别不平衡很有帮助。但对本数据集要留意车辆是典型的大目标mosaic 拼图后目标被缩小的概率不低如果训练时发现 mAP50 上去了但 mAP50-95 上不去很可能是 mosaic 把大目标变成了小目标模型对小尺寸目标的泛化没跟上。我一般在大目标数据集上把 mosaic 概率降到 0.5 而不是关掉留一半 epoch 用完整图片训练。迁移到别的框架时对应参数通常是mosaic或augment配置。这个值该怎么调直接看训练曲线loss 前 20 轮降得快是好事但如果 mAP 到 60 轮还在原地抖动就把增强降一档试试。6.2 用混淆矩阵定位类别混淆焦点训练完成后runs/detect/train目录下有一个confusion_matrix.png这是我最先看的图。它展示真实类别与预测类别的对应关系对角线越亮越好。对车辆数据集来说常见混淆点是公交车和汽车——两者外形接近如果该位置有亮斑说明特征没学够优先补充的是这两类的样本而不是盲目加 epochs。另一个实用习惯是把测试集预测结果导出来挑几类典型错误做人工复盘。比如把save_txtTrue的结果和 voc 标签的 xml 放到同一个可视化脚本里比对找出漏检的图片看是目标太小、遮挡严重、还是类别本身模糊。这类错误分析比调参更能指引下一步方向比如针对性地对误检样本做复制粘贴增强。从那以后我每次拿到新数据集都会先跑一遍标签体检脚本再扫一遍越界和空文件最后看一眼混淆矩阵才决定要不要调参。这套流程在 969 张的小数据集上能省下大半天的返工时间希望帮到你。本文还有配套的精品资源点击获取
网站建设高端定制企业官网