资讯动态

车轮识别检测数据集实战:VOC/YOLO/JSON三格式转换与YOLO训练避坑指南

发布时间:2026/10/27 17:00:24 来源:尧图企业网站定制
简介这份车轮识别检测数据集面向从事车辆违规检测、智能交通分析的算法工程师与深度学习学习者可用于压线、不礼让行人、违规车辆计数等实际项目中的车轮目标定位与训练。数据集共3016张图片包含6000余个车轮目标背景多样部分来自道路监控实拍数据分布较为均匀标签同时提供vocxml与yolotxt两种格式主流目标检测算法可直接接入使用。压缩包共9049个文件以3016个xml、3016个txt、3016个jpg及1个py脚本为主整体约481.63MB图片、标注与格式转换脚本配套齐全省去自行整理与转换的环节。所有标注均为纯手工完成框选精准算法拟合效果良好数据质量可靠。目前已有860人学习下载适合需要快速搭建车轮检测基线、验证模型效果或扩充交通场景样本的读者参考使用。1. 车轮识别检测数据集3016 张图、三套标签到底能省下多少标注工时做车辆检测相关项目的工程师绕不开一个现实车轮这个目标说大不大说小不小。它不像整车那样有清晰轮廓也不像车牌那样有固定纹理轮毂辐条、轮胎侧壁、遮挡、夜间反光每一个变量都能让模型在验证集上翻车。我最近拆了一份车轮识别检测数据集3016 张图同时提供 VOC、YOLO、JSON 三种格式标签第一反应是——这正好卡在「自己标太慢、网上找的不敢用」这个尴尬区间里。这份资源解决的核心问题不是「有没有数据」而是「数据能不能直接进训练管线」。VOC 的 XML 适合做数据审查和格式转换的中间态YOLO 的 txt 可以直接喂给 ultralytics 系训练脚本JSON 则方便做可视化校验和跨框架迁移。三种格式并存意味着你不需要在标注工具里来回导出省掉的是格式转换脚本的调试时间以及转换过程中坐标对不齐的玄学问题。适合谁做车辆检测、自动驾驶感知、智慧交通分析、甚至停车场管理系统的从业者只要你的任务里涉及车轮定位或车轮计数这份数据都能当起点用。2. 三种标签格式怎么选VOC、YOLO、JSON 的坐标系差异与转换逻辑2.1 为什么同一份数据要存三种格式先说清楚一个容易混淆的点VOC、YOLO、JSON 不是三种「不同的标注」而是同一批标注框的三种「表达方式」。VOC 用 XML 存绝对像素坐标YOLO 用 txt 存归一化后的中心点加宽高JSON 则看具体实现常见的是 COCO 风格的[x_min, y_min, width, height]绝对坐标数组。坐标系原点都在左上角但数值含义完全不同。我一般会先打开 VOC 的 XML 确认标注质量因为 XML 里保留了图像尺寸、目标类别名、以及每个框的xmin/ymin/xmax/ymax肉眼可读性最好。确认没问题后再用脚本转成 YOLO 格式进训练。JSON 我主要用来做可视化抽检比如用 Python 读出来画框快速看有没有漏标或框偏。提示不要假设 JSON 一定是 COCO 格式。这份数据里的 JSON 具体字段名需要你打开一个文件确认常见的有image_id、bbox、category_id这套 COCO 结构也有直接存points或box的简化结构。先看再写解析代码。2.2 VOC 转 YOLO归一化公式与边界处理VOC 转 YOLO 的核心就一个公式但坑在边界。YOLO 要求中心点坐标和宽高都除以图像宽高得到 0 到 1 之间的浮点数。公式如下# VOC XML 转 YOLO txt 的核心逻辑 import xml.etree.ElementTree as ET from PIL import Image def voc_to_yolo(xml_path, img_path, class_map): tree ET.parse(xml_path) root tree.getroot() # 从 XML 里读图像尺寸不要用 PIL 重新读避免尺寸不一致 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 if cls_name not in class_map: continue # 跳过未定义类别避免训练时索引越界 cls_id class_map[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) # 边界裁剪有些标注会超出图像范围不裁会导致归一化后越界 xmin max(0, min(xmin, img_w)) ymin max(0, min(ymin, img_h)) xmax max(0, min(xmax, img_w)) ymax max(0, min(ymax, img_h)) # 跳过无效框宽或高为 0 的框在 YOLO 里会引发 NaN loss if xmax xmin or ymax ymin: continue cx (xmin xmax) / 2.0 / img_w cy (ymin ymax) / 2.0 / img_h w (xmax - xmin) / img_w h (ymax - ymin) / img_h lines.append(f{cls_id} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}) return lines这段代码里有两个参数需要你根据实际情况改class_map是类别名到索引的映射这份数据如果只有「车轮」一个类那就是{wheel: 0}img_w和img_h我从 XML 的size节点读而不是用 PIL 重新打开图像原因是标注时的尺寸和实际文件尺寸偶尔会不一致以 XML 为准更安全。逻辑说明先读 XML 里的尺寸再遍历每个object做边界裁剪和无效框过滤最后按 YOLO 格式拼接字符串。边界裁剪这一步很多人会省结果训练时出现 loss 突然变 NaN查半天查不出来血泪经验。2.3 JSON 标签的解析与可视化校验JSON 格式的用途主要是抽检。我一般会写一个短脚本随机抽 20 张图把 JSON 里的框画出来和原图对比。这样能快速判断标注是否准确比一张张打开 XML 快得多。# JSON 标签可视化抽检 import json import cv2 import random import os def visualize_json_sample(json_path, img_dir, num_samples20): with open(json_path, r, encodingutf-8) as f: data json.load(f) # 兼容两种常见结构列表形式或字典带 annotations 键 if isinstance(data, dict): annotations data.get(annotations, data.get(images, [])) else: annotations data samples random.sample(annotations, min(num_samples, len(annotations))) for ann in samples: # 字段名需要根据实际 JSON 调整这里假设是 file_name 和 bbox img_name ann.get(file_name) or ann.get(image_id) img_path os.path.join(img_dir, img_name) img cv2.imread(img_path) if img is None: continue bbox ann.get(bbox) or ann.get(box) if bbox: x, y, w, h bbox cv2.rectangle(img, (int(x), int(y)), (int(xw), int(yh)), (0, 255, 0), 2) cv2.imshow(check, img) key cv2.waitKey(0) if key 27: # ESC 退出 break cv2.destroyAllWindows()参数说明json_path指向 JSON 标签文件img_dir是图像目录。num_samples控制抽检数量我一般抽 20 张就够判断整体质量。字段名file_name、bbox需要你打开 JSON 文件确认不同标注工具导出的键名不一样常见的有image_id、bbox、category_id这套 COCO 结构。注意如果 JSON 里存的是归一化坐标而不是绝对坐标画框前需要乘以图像宽高还原。判断方法很简单看数值是否都在 0 到 1 之间。3. 把数据集接进 YOLO 训练管线目录结构、data.yaml 与首轮跑通3.1 目录结构怎么摆才不报错YOLO 系训练脚本对目录结构有固定预期摆错了就是FileNotFoundError或者No labels found。我一般按下面这样组织wheel_dataset/ ├── images/ │ ├── train/ │ │ ├── 000001.jpg │ │ └── ... │ └── val/ │ ├── 000201.jpg │ └── ... ├── labels/ │ ├── train/ │ │ ├── 000001.txt │ │ └── ... │ └── val/ │ ├── 000201.txt │ └── ... └── data.yaml关键点images和labels是同级目录train和val子目录名必须一致。YOLO 在找标签时会把images替换成labels再把.jpg换成.txt。如果你把标签放在别的地方就得改源码里的路径拼接逻辑不推荐。3.2 data.yaml 的最小配置data.yaml是 YOLO 训练的入口配置文件内容不多但每个字段都不能错# data.yaml path: /home/user/wheel_dataset # 数据集根目录绝对路径最稳 train: images/train val: images/val nc: 1 # 类别数这份数据如果只标了车轮就是 1 names: 0: wheel # 类别名顺序必须和 YOLO txt 里的 class_id 对应参数说明path写绝对路径相对路径在不同工作目录下跑容易翻车。nc是类别数量names是索引到类别名的映射。如果你把车轮细分成前轮、后轮那nc改成 2names里加一行。改完names记得同步改 YOLO txt 里的 class_id否则模型学出来的类别是错的。3.3 首轮训练命令与关键参数目录和配置就绪后用 ultralytics 的 YOLO 脚本跑首轮yolo detect train \ data/home/user/wheel_dataset/data.yaml \ modelyolov8n.pt \ epochs100 \ imgsz640 \ batch16 \ lr00.01 \ patience20 \ projectwheel_runs \ nameexp1参数逐个说modelyolov8n.pt用 nano 版先跑通确认数据没问题再换更大的模型。imgsz640是输入分辨率车轮目标通常不大640 够用如果小目标漏检多可以提到 1280但显存要跟上。batch16根据显存调8G 显存跑 640 分辨率一般能到 16。lr00.01是初始学习率默认值数据量 3000 张左右这个值比较稳。patience20是早停耐心值20 轮验证集指标不升就停省时间。首轮跑通后看results.png里的mAP50曲线如果 50 轮以内就平了说明数据或参数有问题先别急着加轮数。4. 避坑与排查坐标越界、类别错位、空标签这些坑我替你踩过了4.1 现象训练 loss 正常下降但 mAP 一直是 0原因YOLO txt 里的 class_id 和data.yaml里的names顺序对不上。比如 txt 里写的是0但names里0对应的是别的类模型学出来的类别索引和验证时对不上mAP 自然为 0。解决打开一个 YOLO txt 文件看第一个数字是几然后确认data.yaml里names对应索引的类别名是不是车轮。不一致就改names或批量改 txt 里的 class_id。4.2 现象训练报错NaN loss或lossnan原因标注框坐标越界或宽高为 0。VOC 转 YOLO 时如果没做边界裁剪归一化后中心点可能大于 1 或小于 0YOLO 计算 loss 时直接出 NaN。解决在转换脚本里加边界裁剪和无效框过滤就是第 2 章那段代码里的max(0, min(x, img_w))和if xmax xmin判断。已经转好的 txt 可以写个脚本扫一遍把越界的行删掉或修正。4.3 现象No labels found警告训练时提示跳过大量图像原因图像和标签文件名不匹配。YOLO 要求images/train/000001.jpg对应labels/train/000001.txt文件名必须完全一致包括大小写。有些数据集图像是.JPG大写标签是.txtYOLO 替换后缀时按.jpg找找不到就跳过。解决写个脚本统一文件名把图像和标签都改成小写后缀或者批量重命名成纯数字编号。改完再跑一次警告消失说明对了。4.4 现象验证集 mAP 很高但实际推理时漏检严重原因训练集和验证集分布不一致。如果这份数据的 3016 张图里某些场景夜间、雨天、遮挡只出现在训练集验证集全是白天清晰图mAP 会虚高。解决重新划分训练集和验证集按场景分层抽样。我一般会先统计图像亮度、目标数量、遮挡比例再按比例分。YOLO 默认按 8:2 分但如果你手动指定了val目录就按你的来。4.5 现象JSON 可视化时框画歪了或画到图像外面原因JSON 里存的是归一化坐标但可视化脚本按绝对坐标画。或者 JSON 里的bbox格式是[x, y, w, h]但脚本按[x_min, y_min, x_max, y_max]解析。解决先打印一个 JSON 样本的bbox值看数值范围。如果都在 0 到 1 之间就是归一化坐标画框前乘以图像宽高。如果bbox是四个数但第三个比第一个小说明格式是[x, y, w, h]需要自己算x_max x w。5. 进阶用法用这份数据做车轮计数与跨格式一致性校验5.1 车轮计数从检测框到计数的后处理车轮识别的一个常见下游任务是计数。YOLO 输出的是框要变成数字需要加一层后处理。我一般用置信度阈值加 NMS 后的框数量作为计数结果但有两个细节要注意。# 车轮计数后处理 from ultralytics import YOLO import cv2 model YOLO(wheel_runs/exp1/weights/best.pt) img cv2.imread(test.jpg) results model(img, conf0.5, iou0.45) # results[0].boxes 里是 NMS 后的框 count len(results[0].boxes) print(f检测到 {count} 个车轮) # 如果同一辆车有多个车轮可以按框的中心点 x 坐标排序区分左右轮 boxes results[0].boxes.xyxy.cpu().numpy() centers [( (b[0]b[2])/2, (b[1]b[3])/2 ) for b in boxes] centers_sorted sorted(centers, keylambda c: c[0])参数说明conf0.5是置信度阈值低于这个值的框不输出。车轮目标如果被遮挡置信度会偏低可以降到 0.3 试试但误检会增多。iou0.45是 NMS 的 IoU 阈值两个框重叠超过 45% 就只保留置信度高的那个。如果同一车轮被重复检测调低iou如果相邻车轮被误合并调高iou。5.2 跨格式一致性校验确保 VOC、YOLO、JSON 说的是同一件事三种格式并存的最大风险是「你以为它们一致其实不一致」。我习惯在训练前做一次交叉校验从 VOC 读一个框转成 YOLO 格式再从 JSON 里读同一个图的框对比数值是否在误差范围内。# 跨格式一致性校验 import xml.etree.ElementTree as ET import json def check_consistency(xml_path, json_path, img_w, img_h, tolerance2.0): # 读 VOC tree ET.parse(xml_path) root tree.getroot() voc_boxes [] for obj in root.findall(object): 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) voc_boxes.append((xmin, ymin, xmax, ymax)) # 读 JSON with open(json_path, r, encodingutf-8) as f: data json.load(f) json_boxes [] anns data if isinstance(data, list) else data.get(annotations, []) for ann in anns: bbox ann.get(bbox) or ann.get(box) if bbox: x, y, w, h bbox json_boxes.append((x, y, xw, yh)) # 对比 if len(voc_boxes) ! len(json_boxes): print(f框数量不一致: VOC{len(voc_boxes)}, JSON{len(json_boxes)}) return False for i, (vb, jb) in enumerate(zip(voc_boxes, json_boxes)): for a, b in zip(vb, jb): if abs(a - b) tolerance: print(f第 {i} 个框偏差过大: VOC{vb}, JSON{jb}) return False print(一致性校验通过) return True参数说明tolerance2.0是允许的像素偏差因为不同格式转换时可能有浮点舍入。如果偏差超过 2 像素说明某一种格式的标注有问题需要人工复查。这个校验我一般抽 10 到 20 张图做全量跑太慢抽检足够发现问题。5.3 一个具体技巧用 JSON 快速生成数据分布报告JSON 格式最适合做统计分析因为json.load之后就是 Python 字典或列表遍历统计很方便。我一般会统计每张图的框数量分布、框的宽高比分布、以及目标在图像中的位置分布。这三个指标能快速判断数据集是否有偏。# 数据分布快速统计 import json import numpy as np with open(annotations.json, r, encodingutf-8) as f: data json.load(f) anns data if isinstance(data, list) else data.get(annotations, []) box_counts [] aspect_ratios [] centers_x [] centers_y [] for ann in anns: bbox ann.get(bbox) or ann.get(box) if not bbox: continue x, y, w, h bbox box_counts.append(1) # 每张图一个框的情况多框需要按 image_id 聚合 aspect_ratios.append(w / h if h 0 else 0) centers_x.append(x w/2) centers_y.append(y h/2) print(f框数量: {len(box_counts)}) print(f宽高比均值: {np.mean(aspect_ratios):.2f}, 标准差: {np.std(aspect_ratios):.2f}) print(f中心点 x 范围: {min(centers_x):.0f} - {max(centers_x):.0f}) print(f中心点 y 范围: {min(centers_y):.0f} - {max(centers_y):.0f})如果宽高比标准差很大说明车轮形态差异大训练时可以考虑多尺度增强。如果中心点分布集中在图像上半部分说明拍摄角度单一验证集可能无法覆盖真实场景。这些判断不需要复杂工具一个 JSON 文件加几行 numpy 就能看出来。从那以后我每次拿到新数据集都强制走一遍「VOC 抽检 → YOLO 转换 → JSON 校验 → 首轮训练」这个流程不跳过任何一步。看起来多花半小时但比训练到一半发现标签有问题再回头查要省得多。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑