资讯详情

资讯详情

交通标志识别数据集:VOC、COCO、YOLO标签格式转换与YOLO训练全流程

简介本资源面向计算机视觉初学者与目标检测开发者提供一套真实场景下的交通标志识别数据集可直接用于YOLO系列模型的训练与验证。数据经labelimg精细标注标注框质量高场景覆盖丰富并同步提供voc(xml)、coco(json)与yolo(txt)三种格式标签分别存放于不同文件夹方便直接接入不同检测框架。压缩包共约2000个文件以1985个xml标注文件为主另含少量txt、html与py脚本整体约807.7MB。资源附赠YOLO环境搭建教程、训练案例教程以及训练集、验证集、测试集划分脚本可按需自行划分数据快速复现训练流程。目前已有801人学习下载适合需要快速上手交通标志检测、验证模型效果或搭建课程实验的读者参考使用。1. 从一份 3000 张图的交通标志数据集说起三种标签格式到底怎么选手上拿到一份交通标志识别数据集3000 张实拍图附带 VOC、COCO、YOLO 三种格式标签还有划分脚本和训练教程——这种组合在真实项目里其实挺常见但很多人第一步就卡住了三种格式到底用哪个能不能只留一种转换的时候坐标对不上怎么办交通标志识别是目标检测里比较典型的场景类别不多通常几十类、目标尺寸偏小、背景干扰大广告牌、路灯、相似形状的圆形标志。3000 张图不算多但配合合理的增强和迁移学习跑出一个能用的 baseline 完全够。关键在于标签格式选对、划分脚本用对、训练参数调对这三步任何一步翻车后面 loss 不降或者 mAP 虚高都会让你怀疑人生。这篇笔记面向两类人刚入门 YOLO 想找个完整数据集练手的以及手头有类似数据但被格式转换和划分逻辑卡住的。我会把三种格式的取舍、转换脚本的写法、划分时避免数据泄漏的细节、以及训练时几个必调参数讲清楚尽量让你照着就能复现。2. VOC、COCO、YOLO 三种标签格式的取舍与转换2.1 三种格式的本质差异VOC 格式的核心是 XML 文件每张图对应一个 XML里面用object标签逐个记录类别名和边界框的xmin/ymin/xmax/ymax。它的坐标是绝对像素值原点在左上角。优点是可读性强用文本编辑器就能改缺点是文件多、解析慢类别名直接写字符串没有统一的类别索引。COCO 格式是单个 JSON 文件管全部结构分images、annotations、categories三块。边界框用[x, y, width, height]同样是绝对像素值但注意它是左上角坐标加宽高不是右下角。COCO 的类别 id 通常从 1 开始0 一般留给背景这个细节在转换时特别容易搞错。YOLO 格式最简每张图一个.txt每行class_id x_center y_center width height全部是归一化到 0~1 的相对值。class_id 从 0 开始连续编号。它没有冗余信息解析最快但可读性差改一个框得自己算归一化坐标。选哪个训练 YOLO 系列直接用 YOLO 格式别绕弯。VOC 和 COCO 的价值在于VOC 方便人工校验和用 LabelImg 二次编辑COCO 方便接一些现成的评估工具比如 pycocotools 算 mAP。所以常见做法是三种都留着训练用 YOLO校验用 VOC评估用 COCO。2.2 VOC 转 YOLO 的转换脚本与四个边界坑下面这个脚本是我常用的 VOC 转 YOLO 写法处理单目录下所有 XMLimport os import xml.etree.ElementTree as ET # 类别列表顺序决定 class_id必须和训练时的 data.yaml 一致 CLASSES [speed_limit, stop, yield, no_entry, pedestrian] CLASS_TO_ID {name: i for i, name in enumerate(CLASSES)} def voc_to_yolo(xml_dir, img_dir, out_dir): os.makedirs(out_dir, exist_okTrue) 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() # 用图片实际尺寸做归一化不要用 XML 里的 size有时不准 img_name root.find(filename).text img_path os.path.join(img_dir, img_name) from PIL import Image with Image.open(img_path) as im: w, h im.size lines [] for obj in root.findall(object): name obj.find(name).text if name not in CLASS_TO_ID: continue # 跳过未定义类别避免 class_id 错位 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, w - 1)) ymin max(0, min(ymin, h - 1)) xmax max(0, min(xmax, w - 1)) ymax max(0, min(ymax, h - 1)) x_center (xmin xmax) / 2.0 / w y_center (ymin ymax) / 2.0 / h bw (xmax - xmin) / w bh (ymax - ymin) / h # 过滤掉宽高为 0 的无效框 if bw 0 or bh 0: continue lines.append(f{CLASS_TO_ID[name]} {x_center:.6f} {y_center:.6f} {bw:.6f} {bh:.6f}) out_path os.path.join(out_dir, xml_file.replace(.xml, .txt)) with open(out_path, w) as f: f.write(\n.join(lines))逻辑说明先读图片真实尺寸而不是 XML 里的size因为有些标注工具写进去的尺寸和实际图片不一致用错会导致所有框偏移。类别过滤那一步很关键如果 XML 里出现了 CLASSES 里没有的类别直接跳过而不是报错否则整个数据集转换会中断。边界裁剪是防止标注框超出图片范围归一化后出现大于 1 的值YOLO 训练时虽然不报错但会引入噪声。参数说明CLASSES的顺序必须和后续data.yaml里的names完全一致这是血泪经验——顺序错了模型学到的类别全是乱的但 loss 看起来正常排查起来很痛苦。x_center保留 6 位小数足够再多没必要。四个边界坑一是 XML 里filename和实际文件名大小写不一致Linux 下会找不到图二是有些 XML 的bndbox坐标是浮点数直接 int 转换会丢精度三是类别名有空格或特殊字符建议统一替换成下划线四是空 XML没有 object也要生成空 txt否则 YOLO 训练时找不到对应标签文件会报错。2.3 COCO 转 YOLO 与划分脚本的配合COCO 转 YOLO 的核心是从 JSON 里按image_id聚合 annotationsimport json import os from PIL import Image def coco_to_yolo(json_path, img_dir, out_dir): os.makedirs(out_dir, exist_okTrue) with open(json_path) as f: data json.load(f) # COCO 的 category_id 不一定连续建立映射到 0 起始 cat_ids sorted([c[id] for c in data[categories]]) cat_map {cid: i for i, cid in enumerate(cat_ids)} img_info {img[id]: img for img in data[images]} # 按 image_id 聚合标注 from collections import defaultdict anns defaultdict(list) for ann in data[annotations]: anns[ann[image_id]].append(ann) for img_id, img in img_info.items(): w, h img[width], img[height] lines [] for ann in anns.get(img_id, []): x, y, bw, bh ann[bbox] # COCO 是左上角 宽高 x_center (x bw / 2) / w y_center (y bh / 2) / h lines.append(f{cat_map[ann[category_id]]} {x_center:.6f} {y_center:.6f} {bw/w:.6f} {bh/h:.6f}) out_name os.path.splitext(img[file_name])[0] .txt with open(os.path.join(out_dir, out_name), w) as f: f.write(\n.join(lines))这里最容易翻车的是 COCO 的bbox格式它是[x, y, width, height]不是[xmin, ymin, xmax, ymax]。我见过有人直接拿后两个值当 xmax/ymax 用结果框全部缩小到左上角区域训练出来的模型只能检测到图像左上角的目标。划分脚本的逻辑要保证训练集、验证集、测试集之间没有图片重叠同时每个集合内部的类别分布尽量均衡。常见做法是按 8:1:1 随机划分但如果某些类别样本极少随机划分可能导致验证集里没有该类。更稳的做法是按类别分层抽样import random from collections import defaultdict def split_dataset(label_dir, train_ratio0.8, val_ratio0.1): files [f for f in os.listdir(label_dir) if f.endswith(.txt)] # 按主类别分组主类别取该图中出现次数最多的 class_id groups defaultdict(list) for f in files: with open(os.path.join(label_dir, f)) as fp: ids [line.split()[0] for line in fp if line.strip()] main_cls max(set(ids), keyids.count) if ids else empty groups[main_cls].append(f) train, val, test [], [], [] for cls, items in groups.items(): random.shuffle(items) n len(items) n_train int(n * train_ratio) n_val int(n * val_ratio) train items[:n_train] val items[n_train:n_train n_val] test items[n_train n_val:] return train, val, test分层抽样的好处是小类别不会被随机划分吞掉。参数上train_ratio和val_ratio按数据集规模调3000 张图用 8:1:1 比较稳如果类别特别不均衡可以调到 7:1.5:1.5。3. 用 YOLO 训练交通标志模型的完整流程3.1 环境搭建与 data.yaml 配置环境这块PyCharm 里建虚拟环境然后 pip 装 ultralytics 是最省事的路径。Python 版本建议 3.8 到 3.10太高有些依赖轮子还没跟上。GPU 的话 CUDA 11.8 配 PyTorch 2.x 比较稳AGX Orin 上搭环境也是类似逻辑先确认 JetPack 版本对应的 PyTorch 轮子。pip install ultralytics # 验证安装 yolo checksdata.yaml是训练入口格式如下path: /data/traffic_sign train: images/train val: images/val test: images/test nc: 5 names: [speed_limit, stop, yield, no_entry, pedestrian]nc是类别数names顺序必须和转换脚本里的 CLASSES 一致。path用绝对路径最稳相对路径在不同工作目录下跑容易找不到文件。图片和标签的目录结构要对应images/train/xxx.jpg对应labels/train/xxx.txtYOLO 会自动把images替换成labels去找标签。3.2 训练命令与必调参数最小训练命令yolo detect train datadata.yaml modelyolov8n.pt epochs100 imgsz640 batch16几个必调参数imgsz交通标志目标偏小640 是底线有条件上 1280 对小目标召回提升明显batch看显存8G 显存跑 640 大概能到 16epochs3000 张图 100 到 200 轮通常够收敛配合patience20早停防止过拟合。学习率方面默认lr00.01对从头训练合适但如果用预训练权重微调建议降到0.001。交通标志类别少、特征明显微调比从头训练收敛快得多。损失函数这块 YOLOv8 用的是分类 BCE 加回归 CIoU 的组合不需要手动改但理解它有助于看 loss 曲线box_loss降得慢说明框回归有问题检查标注质量cls_loss震荡说明类别不平衡考虑加权重采样。3.3 训练过程监控与指标解读训练时重点看三个指标mAP50、mAP50-95、precision/recall。交通标志场景下 mAP50 到 0.85 以上算可用0.9 以上算不错。如果 mAP50 高但 mAP50-95 低说明框的位置不够准可能是标注框偏大或偏小。验证集 loss 开始上升而训练集 loss 还在降就是过拟合信号早停或者加数据增强。YOLO 默认开了 mosaic、mixup、HSV 增强交通标志场景建议把hsv_h调低一点0.015 左右因为标志颜色是重要特征色相扰动太大会让红色禁令标志和蓝色指示标志混淆。4. 训练交通标志模型时最容易踩的五个坑4.1 类别顺序不一致导致模型学乱现象训练 loss 正常下降但推理时所有标志都被识别成同一个类别或者类别完全对不上。原因转换脚本里的 CLASSES 顺序和 data.yaml 的 names 顺序不一致class_id 映射错位。解决把两个列表打印出来逐行对比或者写个断言脚本在训练前校验。4.2 图片和标签文件名不匹配现象训练启动后报No labels found或者大量图片被跳过。原因图片是.jpg但标签是.JPG.txt或者文件名里有空格、中文。解决统一转成小写英文加下划线用脚本批量重命名确保xxx.jpg对应xxx.txt。4.3 验证集里出现训练集图片现象验证集 mAP 异常高但实际测试效果差。原因划分脚本按文件随机分没有去重同一张图的不同增强版本或者重复文件被分到了不同集合。解决划分前先对图片做哈希去重按哈希值分组再划分。4.4 小目标漏检严重现象大标志能检测到远处的小标志全部漏掉。原因imgsz太小或者 anchor 尺寸不匹配。解决提高imgsz到 1280或者在 data.yaml 里加anchors参数自定义小目标 anchor。另外检查标注有些小目标的框可能只有几个像素这种样本建议直接过滤掉。4.5 训练到一半显存溢出现象前几十轮正常突然报 CUDA out of memory。原因mosaic 增强在后期关闭时 batch 内图片尺寸变化或者验证阶段 batch 设置过大。解决把batch降一档或者设置cacheFalse关闭缓存workers也别设太高4 到 8 比较稳。5. 从训练到部署导出 ONNX 与推理验证的一个实用技巧训练完拿到best.pt之后别急着直接上生产。我一般会先做一步导出 ONNX 并用 onnxruntime 跑一遍验证确认导出前后结果一致再考虑部署到边缘设备或者服务端。yolo export modelbest.pt formatonnx imgsz640 opset12 simplifyTrueopset12兼容性比较好simplifyTrue会做图优化去掉冗余节点。导出后用下面这段代码验证import onnxruntime as ort import numpy as np import cv2 sess ort.InferenceSession(best.onnx) img cv2.imread(test.jpg) img cv2.resize(img, (640, 640)) img img[:, :, ::-1].transpose(2, 0, 1) # BGR 转 RGB 加 HWC 转 CHW img np.expand_dims(img, 0).astype(np.float32) / 255.0 outputs sess.run(None, {sess.get_inputs()[0].name: img}) # outputs[0] 形状通常是 [1, 4nc, 8400]需要做 NMS 后处理 print(outputs[0].shape)关键点预处理必须和训练时一致YOLO 训练时做了归一化和 RGB 转换推理时漏掉任何一步都会导致结果偏差。ONNX 输出的形状是[1, 4nc, 8400]前 4 个是框的 xywh后面是类别分数需要自己写 NMS。如果不想手写可以直接用 ultralytics 的YOLO(best.onnx)加载它会自动处理后处理。一个实用技巧导出 ONNX 后用同一批验证集图片分别跑 PyTorch 和 ONNX 推理对比两者的检测框 IoU如果平均 IoU 低于 0.95说明导出过程有问题重点检查opset版本和simplify参数。这个对比花不了几分钟但能避免部署后才发现精度掉点的后悔药。部署到 AGX Orin 这类边缘设备时ONNX 可以进一步转 TensorRT推理速度能提升两三倍。但 TensorRT 对动态 shape 支持有限建议固定imgsz再转。另外 Orin 上注意功耗模式设置默认模式可能跑不满算力。我自己踩过最深的坑是导出时忘了加simplifyTrueONNX 图里多了一堆恒等节点在服务端跑没问题一到边缘设备就报算子不支持。从那以后我养成了一个习惯任何模型导出后先在目标设备上跑通一张图再批量部署。希望帮到你。本文还有配套的精品资源点击获取
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →