简介:本资源为车轮识别检测数据集,面向从事车辆违规检测(如压线、不礼让行人、违规车辆计数等)的算法工程师与深度学习学习者,可用于目标检测模型的训练与验证。数据集共3016张图片,包含6000多个车轮目标,背景多样,部分来自道路监控拍摄,数据分布均匀,标注精准,算法拟合效果良好。压缩包内共9049个文件,涵盖3016个xml(VOC格式)、3016个txt(YOLO格式)、3016张jpg图像及1个py脚本,整体约481.63MB,可直接用于多种目标检测算法。目前已有860人学习下载。纯手工标注保证质量可靠,标签格式齐全,便于快速接入现有训练流程,适合需要真实场景车轮检测数据的开发者参考使用。
1. 车轮识别检测数据集3016张:三种标签格式到底怎么选、怎么用
拿到一个车轮识别检测数据集,3016张图,同时给了 VOC、YOLO、JSON 三种格式标签,第一反应往往是“全都要”。但真跑起来就会发现,三种格式各有各的脾气:VOC 的 XML 适合做数据审查和跨框架迁移,YOLO 的 txt 直接喂给 ultralytics 系列最省事,JSON 则在 COCO 系训练和可视化标注里更顺手。选错格式不会让模型不收敛,但会在转换、校验、增强、评估这几个环节反复消耗时间。这篇笔记面向已经拿到或准备找车轮检测数据的人:如果你要快速跑通一个车轮检测 baseline,或者要把车轮检测嵌进交通场景分析、车辆部件识别、自动驾驶感知预研里,下面这套从格式选择到训练验证的路径可以直接抄。3016 张这个量级不算大,属于“够跑通、不够卷精度”的规模,所以数据质量、标签一致性、增强策略比堆模型结构更值得花时间。
2. 三种标签格式的差异与选型:VOC、YOLO、JSON 各自适合什么活
2.1 VOC XML 的结构与适用场景
Pascal VOC 格式是目标检测里最老牌、最通用的标注格式之一。每张图对应一个同名 XML 文件,核心节点包括filename、size(宽高通道)、object(每个目标一个节点,含name、bndbox的xmin/ymin/xmax/ymax)。车轮检测里name通常就是wheel或wheel_rim这类类别名。
VOC 的优势在于可读性强,用文本编辑器打开就能核对框的位置,适合做人工抽检。很多标注工具(LabelImg、CVAT 导出)默认就是 VOC,所以拿到手的原始标注大概率是 XML。它的缺点是训练框架原生支持在减弱,ultralytics 虽然还能读 VOC,但官方更推荐转成 YOLO 格式;而且 XML 解析比纯文本慢,3016 张做多轮增强时解析开销会累积。
选型建议:把 VOC 当作“母格式”保存,所有人工修正、类别合并、框越界检查都在 XML 层面做,确认无误后再转出 YOLO 和 JSON。这样原始标注永远可回溯,不会因为一次转换错误丢掉真值。
2.2 YOLO txt 的归一化坐标与训练友好性
YOLO 格式每张图一个 txt,每行一个目标:class_id x_center y_center width height,后四个值都是相对图像宽高的归一化浮点数(0~1)。class_id 从 0 开始,对应一个classes.txt或data.yaml里的names列表。
它最大的好处是训练时零解析负担,ultralytics 的 dataloader 直接读文本、拼 batch,速度快。归一化坐标也让图像 resize、letterbox 增强不会破坏框的对应关系。坑在于:归一化一旦算错(比如用了绝对坐标没除宽高),训练不会报错,只是 loss 降不下去、mAP 极低,属于典型的“玄学翻车”。另外 YOLO txt 不保存图像尺寸,如果原图和标签分离、尺寸被改过,框就会错位。
选型建议:只要用 YOLOv5/v8/v11 或类似框架,训练集一律用 YOLO txt。转换后必须做一次可视化回贴,把框画回原图看是否对齐,这一步能拦掉八成坐标错误。
2.3 JSON(COCO 风格)的聚合结构与多任务扩展
JSON 标签常见两种:一种是 COCO 风格的单个大文件,包含images、annotations、categories三个主键,所有图的框聚合在一起;另一种是每图一个 JSON。COCO 风格更适合做实例分割、关键点等多任务扩展,也方便用 pycocotools 做标准评估。
车轮检测如果只做检测框,JSON 的优势不明显,反而因为单文件聚合,增删图片时容易和images列表不同步。但如果后续要加车轮分割掩码、或者要接入多模态分析(比如结合车轮状态判断事故),COCO JSON 的扩展性更好。注意 COCO 的bbox是[x_min, y_min, width, height]绝对坐标,和 YOLO 的[x_center, y_center, w, h]归一化完全不同,转换时最容易在这里写反。
选型建议:JSON 作为“交换格式”,用于跨团队交付、接入评估工具、或未来扩展分割任务。日常训练不直接读 JSON,而是从 VOC 或 YOLO 转过去。
2.4 三种格式的字段对照与转换关系
| 维度 | VOC XML | YOLO txt | COCO JSON |
|---|---|---|---|
| 存储方式 | 每图一个 XML | 每图一个 txt | 通常单文件聚合 |
| 框坐标 | 绝对 xmin/ymin/xmax/ymax | 归一化 xc/yc/w/h | 绝对 x/y/w/h |
| 类别表示 | 字符串 name | 整数 class_id | category_id 整数 |
| 图像尺寸 | 写在 size 节点 | 不保存 | 写在 images 节点 |
| 训练原生支持 | 弱 | 强(ultralytics) | 中(需 pycocotools 或转换) |
| 适合环节 | 人工审查、母格式 | 训练 | 评估、扩展任务 |
这张表建议存下来,每次转换前对一遍,尤其是坐标顺序和是否归一化这两列,是转换脚本里最高频的错误来源。
3. 从 VOC 到 YOLO 再到 JSON:3016 张数据的转换与校验流程
3.1 目录结构约定与文件清点
动手前先把目录定死,后面所有脚本都基于这个结构,避免路径写死导致换机器就崩。推荐:
wheel_dataset/ ├── images/ # 3016 张原图,jpg/png 混放 ├── annotations_voc/ # 3016 个 XML ├── labels_yolo/ # 转换生成的 txt ├── annotations_json/ # 转换生成的 COCO json └── data.yaml # YOLO 训练配置先清点数量,确认图、XML 一一对应,缺一个都会在训练时静默跳过:
# 统计图片和 XML 数量,找出不匹配的文件 ls images/ | sed 's/\.[^.]*$//' | sort > /tmp/img_names.txt ls annotations_voc/ | sed 's/\.xml$//' | sort > /tmp/xml_names.txt diff /tmp/img_names.txt /tmp/xml_names.txtsed去掉扩展名后排序对比,diff有输出就说明有图没标注或有标注没图。3016 张里哪怕缺 3 张,也建议补上或删掉对应图,不要留着让 dataloader 去处理边界情况。
3.2 VOC 转 YOLO 的脚本与坐标归一化
import os import xml.etree.ElementTree as ET # 类别映射,车轮检测通常单类,多类时按实际顺序改 classes = ["wheel"] img_dir = "images" xml_dir = "annotations_voc" out_dir = "labels_yolo" os.makedirs(out_dir, exist_ok=True) for xml_file in os.listdir(xml_dir): if not xml_file.endswith(".xml"): continue tree = ET.parse(os.path.join(xml_dir, xml_file)) root = tree.getroot() size = root.find("size") img_w = int(size.find("width").text) img_h = int(size.find("height").text) lines = [] for obj in root.findall("object"): cls_name = obj.find("name").text.strip() if cls_name not in classes: continue # 跳过不在类别表里的目标 cls_id = classes.index(cls_name) bbox = obj.find("bndbox") xmin = float(bbox.find("xmin").text) ymin = float(bbox.find("ymin").text) xmax = float(bbox.find("xmax").text) ymax = float(bbox.find("ymax").text) # 裁剪到图像边界,防止标注越界导致归一化后超出 0~1 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)) xc = (xmin + xmax) / 2.0 / img_w yc = (ymin + ymax) / 2.0 / img_h w = (xmax - xmin) / img_w h = (ymax - ymin) / img_h lines.append(f"{cls_id} {xc:.6f} {yc:.6f} {w:.6f} {h:.6f}") out_name = xml_file.replace(".xml", ".txt") with open(os.path.join(out_dir, out_name), "w") as f: f.write("\n".join(lines))逻辑说明:先读 XML 的size拿到图像宽高,这是归一化的分母,不能用固定值。每个 object 取 bndbox 后先做边界裁剪,因为标注工具偶尔会画出超过图像范围的框,不裁剪会导致归一化值大于 1,训练时框跑到图外。类别不在classes里的直接跳过,避免 class_id 越界。
参数说明:classes列表顺序决定 class_id,训练时的data.yaml里names必须和它完全一致,顺序错了模型学到的类别就是错的。{:.6f}保留六位小数,足够精度又不会让文件过大。如果数据集是多类(比如 wheel、tire、rim),把classes改成对应列表,并确认 XML 里的name拼写一致。
3.3 转换后的可视化回贴校验
转换完不能直接开训,先抽 20~30 张把 YOLO 框画回原图,肉眼确认对齐:
import cv2 import os import random img_dir = "images" label_dir = "labels_yolo" img_w, img_h = None, None samples = random.sample(os.listdir(img_dir), 30) for img_name in samples: img_path = os.path.join(img_dir, img_name) img = cv2.imread(img_path) h, w = img.shape[:2] label_path = os.path.join(label_dir, os.path.splitext(img_name)[0] + ".txt") if not os.path.exists(label_path): print("缺标签:", img_name) continue with open(label_path) as f: for line in f: parts = line.strip().split() if len(parts) != 5: continue _, xc, yc, bw, bh = map(float, parts) x1 = int((xc - bw / 2) * w) y1 = int((yc - bh / 2) * h) x2 = int((xc + bw / 2) * w) y2 = int((yc + bh / 2) * h) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.imwrite(f"check_{img_name}", img)逻辑说明:把归一化坐标反算回像素坐标,画绿框。重点看框是否套住车轮、有没有整体偏移、有没有框大小明显异常。参数上random.sample抽 30 张,覆盖不同光照和角度更好,可以手动挑几张夜间或遮挡的图单独看。
3.4 VOC 转 COCO JSON 的聚合脚本
import os import json import xml.etree.ElementTree as ET xml_dir = "annotations_voc" img_dir = "images" out_json = "annotations_json/wheel_coco.json" coco = {"images": [], "annotations": [], "categories": [{"id": 1, "name": "wheel"}]} ann_id = 1 for img_id, xml_file in enumerate(sorted(os.listdir(xml_dir)), start=1): if not xml_file.endswith(".xml"): continue tree = ET.parse(os.path.join(xml_dir, xml_file)) root = tree.getroot() size = root.find("size") w = int(size.find("width").text) h = int(size.find("height").text) file_name = root.find("filename").text coco["images"].append({"id": img_id, "file_name": file_name, "width": w, "height": h}) for obj in root.findall("object"): if obj.find("name").text.strip() != "wheel": continue bbox = obj.find("bndbox") xmin = float(bbox.find("xmin").text) ymin = float(bbox.find("ymin").text) xmax = float(bbox.find("xmax").text) ymax = float(bbox.find("ymax").text) # COCO bbox 是 [x, y, width, height] 绝对坐标 coco["annotations"].append({ "id": ann_id, "image_id": img_id, "category_id": 1, "bbox": [xmin, ymin, xmax - xmin, ymax - ymin], "area": (xmax - xmin) * (ymax - ymin), "iscrowd": 0 }) ann_id += 1 with open(out_json, "w") as f: json.dump(coco, f)逻辑说明:COCO 的bbox是左上角坐标加宽高,不是右下角,这里xmax - xmin就是宽。area用于评估时区分大小目标,iscrowd单类检测一般填 0。image_id和annotations里的image_id必须对应,否则 pycocotools 评估会报找不到图。
参数说明:categories的id从 1 开始(COCO 惯例,0 常留给背景),和 YOLO 的 class_id 从 0 开始不同,跨格式对比结果时要注意这个偏移。如果后续要加分割,在 annotation 里补segmentation字段即可。
4. 用 YOLO 格式跑通车轮检测训练:配置、参数与首轮验证
4.1 data.yaml 的写法与路径陷阱
path: /abs/path/to/wheel_dataset train: images val: images names: 0: wheelpath用绝对路径最稳,相对路径在不同工作目录下启动训练时容易找不到。train和val这里都指向images,是因为 3016 张规模小,先全量过拟合验证流程通不通,正式实验再按 8:1:1 切分并分别建train.txt/val.txt列表文件。names的键必须和 YOLO txt 里的 class_id 对应,单类就是 0: wheel。
4.2 训练命令与关键参数
yolo detect train \ data=wheel_dataset/data.yaml \ model=yolov8n.pt \ epochs=100 \ imgsz=640 \ batch=16 \ lr0=0.01 \ patience=20 \ project=runs/wheel \ name=baseline参数说明:model=yolov8n.pt用 nano 版先跑通,3016 张数据量下大模型容易过拟合,nano 收敛快、显存友好。imgsz=640是通用起点,车轮在图中占比小的话可以提到 960 或 1280,但显存和速度要权衡。batch=16按显存调,爆显存就降到 8 或 4。patience=20表示 20 轮无提升就早停,小数据集上能省时间。lr0=0.01是 SGD 的常见起点,换 AdamW 时可以降到 0.001。
4.3 首轮结果怎么看:mAP、loss 曲线与误检样例
训练完先看runs/wheel/baseline/results.csv里的metrics/mAP50和metrics/mAP50-95。车轮检测单类,mAP50 到 0.85 以上算正常,低于 0.6 基本是标签或配置有问题。loss 曲线重点看train/box_loss和val/box_loss是否同步下降,val loss 早早抬头就是过拟合,加增强或减模型。
然后必须看误检样例,runs/wheel/baseline/val_batch0_pred.jpg会画出预测框。常见问题是把轮毂和轮胎分成两个框、或者把圆形装饰件误检成车轮。这些不是靠调参能解决的,要回到标注层面统一标准:一个车轮一个框,还是轮毂轮胎分开标,必须在标注阶段定死。
5. 车轮检测数据集的避坑与排查:标签、坐标、类别的 5 个高频翻车点
5.1 现象:训练 loss 不降,mAP 长期在 0.01 附近
原因:YOLO txt 里坐标没归一化,或者 class_id 超出了names的范围。归一化错误时模型学的是图外框,loss 会卡住。class_id 越界则部分目标被忽略。
解决:随机抽一个 txt,确认后四个值都在 0~1 之间;确认最大 class_id 小于len(names)。用 3.3 的可视化脚本回贴,框如果全挤在左上角或跑到图外,就是归一化问题。
5.2 现象:可视化回贴时框整体偏移或大小不对
原因:XML 里的size宽高和实际图片尺寸不一致。标注工具可能读的是缩略图尺寸,或者图片被后期裁剪过但 XML 没更新。
解决:用 OpenCV 读实际图片宽高,和 XML 的size对比,不一致的以实际图片为准重新计算归一化。批量检查:
import cv2, os import xml.etree.ElementTree as ET for xml_file in os.listdir("annotations_voc"): root = ET.parse(os.path.join("annotations_voc", xml_file)).getroot() w = int(root.find("size/width").text) h = int(root.find("size/height").text) img_name = root.find("filename").text img = cv2.imread(os.path.join("images", img_name)) if img is None: print("图缺失:", img_name); continue if img.shape[1] != w or img.shape[0] != h: print("尺寸不符:", xml_file, (w, h), img.shape[:2])5.3 现象:COCO 评估时报 “annotations not found” 或 mAP 为 0
原因:JSON 里images的id和annotations的image_id没对上,或者file_name和实际文件名不一致(比如带了路径前缀)。
解决:检查file_name是否只是文件名不带目录;用脚本校验每个image_id都能在images里找到。COCO 的id必须是整数且唯一,用enumerate生成时注意起始值。
5.4 现象:同一张图里车轮被重复标注,模型学出重叠框
原因:标注时多人协作或工具自动保存产生重复 object 节点,XML 里同一位置有两个几乎相同的框。
解决:在 VOC 转 YOLO 前做去重,计算框的 IoU,超过 0.9 的视为重复只保留一个。这个清洗放在母格式 VOC 上做,转出的 YOLO 和 JSON 都干净。
5.5 现象:验证集 mAP 高但实际推理漏检严重
原因:训练集和验证集来自同一批连续视频帧,相邻帧高度相似,验证集没有真正体现分布差异。3016 张如果是从视频抽帧来的,这个问题很常见。
解决:按视频来源或时间段切分,而不是随机切分。同一段视频的帧要么全在训练集,要么全在验证集。切分后重新跑一轮,mAP 会下降但更接近真实场景表现。
6. 把 3016 张用到极致:增强策略、格式互转的自动化与验收习惯
数据量固定时,提升空间主要在增强和验收流程上。车轮检测的增强有几个针对性选择:随机旋转要慎用,车轮本身是圆形,旋转不会改变外观但会改变和车身的相对位置,如果模型依赖上下文,大角度旋转反而有害;Mosaic 和 MixUp 对提升小目标召回有效,但会引入不真实的拼接边缘,车轮检测里建议 Mosaic 概率设 0.5 左右,别拉满。HSV 色调扰动对夜间和阴影场景有帮助,因为车轮在暗光下纹理弱,颜色扰动能让模型更关注形状。
格式互转建议写成一个入口脚本,VOC 作为唯一真值源,YOLO 和 JSON 都是派生产物,每次改完标注只重跑转换,不手工改派生文件。验收习惯上,我一般固定三步:转换后抽 30 张回贴、训练前用yolo detect train的 dry-run 或 1 epoch 确认 dataloader 不报错、训练后必看val_batch0_pred.jpg。这三步花不了十分钟,但能拦住绝大多数“训了半天发现标签是错的”这种血泪情况。
| 增强手段 | 建议概率 | 适用场景 | 注意 |
|---|---|---|---|
| Mosaic | 0.5 | 小目标、遮挡 | 过高会引入不真实边缘 |
| HSV 扰动 | 0.5~0.7 | 夜间、阴影 | 色相范围别太大 |
| 随机旋转 | 0~0.2 | 俯视视角 | 车轮圆形,大角度收益低 |
| MixUp | 0.1~0.2 | 过拟合时 | 小数据集慎用 |
| 随机缩放 | 0.5 | 车轮大小不一 | 配合 letterbox |
最后说个习惯:每次拿到新数据集,先花半小时把三种格式各转一遍、各可视化一次,比直接开训省下的时间多得多。3016 张不大,但标签一致性决定上限,模型结构只决定你能不能摸到那个上限。希望帮到你。
本文还有配套的精品资源,点击获取