ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

YOLO交通标志与信号灯检测:从数据集标注到模型训练部署全指南

YOLO交通标志与信号灯检测:从数据集标注到模型训练部署全指南 简介面向目标检测实验的YOLO交通标志与交通信号灯数据集内含877张高清道路场景图片标注对象包括限速牌、交通警告牌、红灯、绿灯、黄灯等常见类别。标注过程使用labelimg工具完成既保留了包含目标四角坐标的XML文件也提供转换后的YOLO格式TXT标签含类别编号与坐标信息可无缝接入YOLO系列模型的训练与验证流程。整个资源包共1641个文件以PNG图片和XML标注文件为主体另附2个TXT标签文件和1个Python辅助脚本压缩包大小约218MB。目前已有475人学习下载。借助该数据集使用者可省去自行采集道路图像和手动标注的繁琐步骤直接开展交通标志识别实验适合目标检测入门者、高校项目以及交通场景算法研究。1. YOLO 交通标志与信号灯检测数据集878 张图能撑起一个完整的检测实验做目标检测实验最消耗精力的环节往往不是训练而是数据准备采集、清洗、标注、格式转换每一步都在烧时间。YOLO 交通标志与信号灯检测数据集把这段路直接铺好了——878 张高清道路图片labelimg 手工标注同时交付 xml 和 txt 双格式覆盖限速牌、交通警告牌、红灯、绿灯、黄灯五类目标。刚跑通 YOLO 流程的学生可以拿它当第一份完整数据集做工程验证的从业者也能用它测试模型在小目标和光照变化场景下的表现。省掉数据环节把精力留给训练、调参和部署。2. 数据集构成与格式转换从 XML 到 YOLO txt 的字段对应关系拿到任何标注数据集第一件事不是直接开训而是弄清楚标注到底存在哪、训练脚本怎么消费它。这份数据同时给了 xml 和 txt 两份标注前者是 labelimg 直接产出的 Pascal VOC 风格 XML后者是 YOLO 训练真正读取的 txt。两者之间的字段对应和坐标换算是整个实验的地基值得先花十几分钟过一遍后面所有脚本和调试都会基于这层理解。2.1 场景构成与五类目标从图片命名road7.png、road22.png、road31.png、road51.png、road110.png 等可以推断采集对象是城市道路与路口。这类场景的典型特征是信号灯小而密集限速牌和警告牌分布在车道两侧天然存在明显尺度差异。摘要列出的五类目标分别是限速牌、交通警告牌、红灯、绿灯、黄灯。这五类的检测难度差别很大。红绿灯属于典型小目标在整张高清图里可能只占二三十像素见方缩放输入后信息进一步压缩限速牌和警告牌尺度稍大但伴随反光、倾斜、部分遮挡。如果你的实验目标就是交通标志/信号灯检测这五类构成一个最小但有代表性的验证集训练结论能直接对照真实场景解释。想快速统计各类别有多少框不需要解析 XML统计 txt 第一列就行for f in labels/train/*.txt; do awk {print $1} $f; done | sort | uniq -c这个命令输出每个类别编号的框数量。注意这是框数而不是图数一张图可以同时出现红灯和绿灯。878 张图对 5 个类来说属于够跑通实验、不够刷极限精度的量级后面要靠在训练策略和预训练权重上找补。2.2 XML 标注结构与坐标换算labelimg 保存的 XML 遵循 Pascal VOC 格式核心结构如下annotation folderJPEGImages/folder filenameroad51.png/filename size width1920/width height1080/height depth3/depth /size object namered_light/name bndbox xmin520/xmin ymin310/ymin xmax558/xmax ymax348/ymax /bndbox /object /annotationbndbox 里就是摘要提到的四个角的坐标xmin、ymin 是框左上角xmax、ymax 是右下角。YOLO txt 每行存的是类别编号加归一化后的中心点和宽高class_id center_x center_y width height。换算关系是中心点由四角坐标求平均再除以图宽高宽高由四角坐标相减再归一化。所有值都在 0~1 之间类别编号从 0 开始。为什么 YOLO 要归一化而不是直接存像素坐标因为训练时图片会被随机缩放到输入尺寸归一化坐标在缩放中保持比例不变训练脚本不需要关心每张图的原始分辨率。这也是 YOLO 能统一处理不同来源图片的前提。转换时务必从 XML 的 size 字段取分母不能写死数值否则换一批图片就全错位。2.3 转换与自查脚本从 labelimg 产物到 YOLO txt数据集虽然已经转好 txt我仍建议你亲手跑一遍转换逻辑既验证标注完整性也留一份自己的工具。下面这段是标准的 Pascal VOC 转 YOLO 脚本import xml.etree.ElementTree as ET import os CLASSES [speed_limit, warning_sign, red_light, green_light, yellow_light] def voc_to_yolo(xml_path, txt_path): tree ET.parse(xml_path) root tree.getroot() img_w int(root.find(size/width).text) img_h int(root.find(size/height).text) lines [] for obj in root.iter(object): name obj.find(name).text.strip() if name not in CLASSES: 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) 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}) with open(txt_path, w, encodingutf-8) as f: f.write(\n.join(lines))逻辑说明先解析 XML 拿图片宽高和每个 object 的类别名、bndbox 四角坐标类别名映射成索引四角坐标换算成归一化 cx/cy/w/h最后写入 txt。写入时保留 6 位小数小目标宽高很小精度不足会变成 0训练时会被当无效框丢弃。参数说明CLASSES 的顺序决定类别编号必须和后续 data.yaml 的 names 完全一致xml_path 建议用绝对路径避免执行目录不同找不到文件。再封装一层批量处理遍历整个标注目录def batch_convert(xml_dir, label_dir): os.makedirs(label_dir, exist_okTrue) for xml_file in os.listdir(xml_dir): if not xml_file.endswith(.xml): continue stem os.path.splitext(xml_file)[0] voc_to_yolo( os.path.join(xml_dir, xml_file), os.path.join(label_dir, stem .txt) )转换完成后抽 5~10 个 txt 对照原图确认框的位置贴合目标。这一步几分钟能避免后面训练白跑几十轮。3. 把数据集喂给 YOLO 训练目录结构、data.yaml 与关键参数标注格式理顺之后数据集要从文件集合变成可训练资源关键在目录组织和 data.yaml。很多训练翻车案例都出在这一步目录对不上、类别顺序不一致、train/val 划分不干净报错信息还不直观。3.1 标准目录结构与 train/val 划分YOLO 系训练流程默认按 images 和 labels 两套目录读取数据推荐组织如下traffic_dataset/ ├── images/ │ ├── train/ │ │ ├── road51.png │ │ └── ... │ └── val/ │ ├── road22.png │ └── ... ├── labels/ │ ├── train/ │ │ ├── road51.txt │ │ └── ... │ └── val/ │ ├── road22.txt │ └── ... ├── traffic.yaml └── classes.txtimages 与 labels 必须同名配对images/train/road51.png 对应 labels/train/road51.txt。训练脚本靠同名扩展名配对找标签任何一边缺文件都会在读取时报错或警告。878 张图按 8:2 或 9:1 划分划分前按文件名单排序再做分层采样避免某些场景全部掉进 val 导致训练集类别缺失。一个简单的 shell 划分方法mkdir -p images/val labels/val find images/train -name *.png | shuf -n 176 | while read f; do mv $f images/val/ mv labels/train/$(basename $f .png).txt labels/val/ done3.2 data.yaml 与类别顺序data.yaml 是训练脚本信任的数据描述文件train: ./images/train val: ./images/val nc: 5 names: 0: speed_limit 1: warning_sign 2: red_light 3: green_light 4: yellow_lighttrain 和 val 建议写绝对路径或相对 data.yaml 所在目录的相对路径不要依赖终端当前工作目录换机器就迷路。nc 必须和 names 长度一致。最关键的是 names 的索引顺序不能动txt 里 class_id 在转换阶段已经固化改显示名可以改顺序就类别错位。注意data.yaml 的 names 顺序一旦和 txt 的 class_id 错位模型会安静地学坏训练过程不报错val mAP 却一直上不去。3.3 训练命令与关键超参数以 YOLOv8 为例yolo detect train \ datatraffic.yaml \ modelyolov8s.pt \ imgsz640 \ batch16 \ epochs150 \ patience30 \ projectruns/traffic \ nameexp1参数说明imgsz640 是输入分辨率这份数据红绿灯小目标多显存有余建议上 960小目标召回会有可见提升代价是训练时间翻倍batch 按显存调8GB 以下用 8~1624GB 可以到 32epochs 设 150 配合 patience30 早停878 张图通常几十轮就收敛。学习率保持默认batch 翻倍时学习率可以线性放大但小数据上乱调学习率反而容易震荡。前 3 个 epoch 是 warmuploss 波动大是正常的不要看到前面 loss 升高就立刻 kill 掉。YOLOv5 的等价命令python train.py \ --data traffic.yaml \ --weights yolov5s.pt \ --img 640 \ --batch 16 \ --epochs 150 \ --patience 30两份框架对数据的要求一致这份数据可以无缝切换。我的习惯是先跑 s 或 n 把流程走通再决定要不要升级模型。3.4 训练日志怎么读训练结束后项目目录会生成 results.csv、confusion_matrix.png、F1_curve.png、labels.jpg。重点看 val/cls_loss 是否持续下降、mAP0.5 是否进入平台期。results.csv 里每一行对应一个 epoch字段包含 train/box_loss、train/cls_loss、val/box_loss、val/cls_loss、mAP0.5、mAP0.5:0.95。labels.jpg 里每张小图是归一化到单位框后的目标分布如果框集中在图像中心且尺寸普遍偏小说明小目标占比高后面调 imgsz 和锚框时心里就有数。如果 train/cls_loss 走低而 val/cls_loss 先降后升就是过拟合信号。878 张图、5 类目标模型容量天花板低大模型在这份数据上几乎必过拟合。再用 20 张 val 图跑 predict 肉眼确认边界框和类别这一步能发现 loss 曲线看不出的标注质量问题。4. 交通标志检测的避坑清单标注、转换与训练中的四个坑下面四条都是同类交通数据上反复出现的问题按现象、原因、解决展开可直接对照排查。4.1 信号灯灯色与框选范围不一致现象训练后红灯和绿灯的 mAP 差距明显预测图里有的框套住整个信号灯组有的只套一个灯珠。原因标注口径没统一。有人按灯组整体框有人按发光灯珠框两种 bbox 混在一起模型学到的是不确定的目标定义同类目标的 IoU 分布被拉宽。定位这种问题有个笨办法把 val 预测结果里所有红灯的框按面积排序输出如果面积分布出现两个明显的峰基本就是口径混用。解决标注规范约定只框发光灯体已经混的标注按 bbox 面积做统计聚类把面积接近整个灯组的大框筛掉或转成最小单灯框必要时人工复核。878 张图规模不大手工过一遍可能比反复调参更快。4.2 小目标漏检640 输入下信号灯只剩十几个像素现象原始图清晰缩放成 640 后远处信号灯只有 10×10 像素左右模型直接漏检mAP0.5 上不去。原因YOLO 默认锚框按通用目标分布设计对密集小目标召回弱下采样倍数也限制了小目标特征保留。判断锚框是否对齐看训练日志里 autoanchor 输出的最优召回率如果低于 0.9说明锚框分布和目标尺寸不匹配。解决先试低成本方案——imgsz 提到 960同时让自动锚框按当前数据重新统计目标尺寸分布YOLOv5 用 --autoanchorYOLOv8 默认开启。还不够就换带 P2 小目标检测头的结构小目标 mAP 通常能提 5 个点以上。4.3 XML 与 txt 不配对导致训练报错现象训练开场报 FileNotFoundError或日志提示 cant find labels核对 labels 目录发现部分 txt 缺失。原因labelimg 保存的 XML 里 filename 字段可能是绝对路径或旧版本残留转换脚本没对缺失文件做校验漏转的图混进了训练列表。另外 labelimg 偶尔会存出空 object 的 XML转换后 txt 是 0 字节训练脚本不报错但会静默减少有效样本。解决训练前跑自查脚本逐图检查 txt 是否存在、内容是否为 5 列合法数值、坐标是否在 0~1 内import os img_dir images/train label_dir labels/train for img in sorted(os.listdir(img_dir)): stem os.path.splitext(img)[0] txt os.path.join(label_dir, stem .txt) if not os.path.exists(txt): print(fMISSING: {img}) continue for line in open(txt): parts line.strip().split() if len(parts) ! 5: print(fBAD LINE: {txt}: {line.strip()}) for v in map(float, parts[1:]): if not 0.0 v 1.0: print(fOUT OF RANGE: {txt}: {line.strip()})这段脚本输出所有缺标签和格式异常的文件路径几分钟定位数据质量问题。我每换一份数据集都会先跑它。4.4 夜间与逆光样本不足导致误检现象白天场景 mAP 不错一到黄昏、夜间或逆光测试信号灯漏检、限速牌误检成警告牌。原因图片以白天为主模型学到的是光照充足下的纹理而不是形状 颜色 位置的组合先验对光照变化不鲁棒。解决标注阶段补夜间红灯绿灯样本训练时用曝光、对比度类增强模拟不同光照推理端做自适应光照归一化。单独抽一个夜间序列做验证集和白天 mAP 对比如果差距超过 20 个点基本可以断定是训练分布偏差而不是模型结构问题。5. 数据增强与模型轻量化把 mAP 顶上去再压到边缘设备训练跑通只是第一步。交通检测的真实落点是路口设备和车载盒子这类边缘硬件精度和推理性能要同时兼顾。这一章讲怎么把精度顶上去再把模型压到能部署。5.1 针对交通场景的增强策略YOLO 自带增强里Mosaic 和 MixUp 对提升小目标鲁棒性帮助最大。Mosaic 把四张图拼成一张等于让模型在单次前向里看到更多小目标MixUp 让模型对遮挡和目标重叠更鲁棒。但交通场景有两个例外。第一水平翻转要慎用。限速牌的50翻转变05警告牌箭头方向翻转后语义相反。我一般把 horizontal_flip 概率从默认的 0.5 降到 0.1 或直接关掉。第二颜色增强别关。信号灯靠颜色区分HSV 扰动能模拟不同天气的色偏但幅度要克制hsv_h 保持默认 0.015 附近saturation 和 value 可以加到 0.7 左右模拟夜间逆光。提示训练时把增强后的样本图输出到项目目录亲眼确认增强效果。Mosaic 过强会把小目标裁掉反而伤害召回。除了在线增强还可以对 878 张图做离线增广比如小幅旋转 15 度以内、加高斯噪声、局部裁剪生成 1.5~2 倍样本。我一般只对数量少的类别比如黄灯单独做总量控制住避免训练集膨胀后照样过拟合。5.2 模型选型n / s / m 怎么挑对这份数据集常见选择如下模型参数量参考小目标表现适合场景YOLOv8n约 3.2M一般树莓派、低算力盒子YOLOv8s约 11M均衡实验验证与常规部署YOLOv8m约 26M更好但易过拟合大输入分辨率、数据充足时878 张图撑不起 m 以上模型的训练收益s 是甜点位。目标设备是 rk3588、Jetson Nano 这类盒子时可以用 s 训练后蒸馏到 n或者直接用 n 配 960 输入效果往往比 s 配 640 更好——小模型的瓶颈在容量高分辨率直接提供更多小目标像素。5.3 导出 ONNX 与 TensorRT 加速验证完精度后进入部署。YOLOv8 导出 ONNXyolo export modelruns/traffic/exp1/weights/best.pt formatonnx imgsz640 simplifyTrueimgsz 必须和训练一致否则推理精度下降。导出后用 onnxruntime 做对齐测试确认输出与 PyTorch 推理差异在合理范围。TensorRT 引擎转换trtexec --onnxbest.onnx --saveEnginebest.trt --fp16 \ --minShapesimages:1x3x640x640 \ --optShapesimages:1x3x640x640 \ --maxShapesimages:1x3x640x640我的习惯是先做静态 FP16 导出跑通后再考虑动态 batch。FP16 在交通检测场景下精度损失通常不到 0.5 个 mAP 点延迟能降一半左右性价比很高。6. 交付前的验证方法mAP、混淆矩阵与场景重放模型训练完不能直接交付验证环节最容易被跳过但它恰恰决定实验结论靠不靠谱。第一步看 mAP 曲线。val 集 mAP0.5 到 0.85 说明基础能打mAP0.5:0.95 更严格反映定位和分类的综合水平交通检测建议至少 0.6。两个指标差距过大问题大概率在标签质量而非模型结构。第二步看混淆矩阵。训练输出里的 confusion_matrix.png重点看红灯、绿灯是否互相混淆。红绿之间误检严重先查标注是否把灭掉的灯壳也框了进来。这一步最容易暴露数据问题。第三步做场景重放把 val 图片按难易排序取最难的后 20% 单独预测一遍记录漏检和误检的类别再用真实路口视频做连续帧测试观察单帧稳定性和误触发频率。最后用 predict 导出可视化结果yolo detect predict modelruns/traffic/exp1/weights/best.pt \ sourcetest_video.mp4 conf0.25 saveTrueconf 设 0.25 左右压太低会看到一堆置信度 0.05 的误检框干扰判断。从那以后我每交付一个检测模型都强制自己走这三步看曲线、看混淆矩阵、回放最难样本。看上去多花半小时实际每次都能揪出几个单看 loss 根本发现不了的标注问题。希望帮到你。本文还有配套的精品资源点击获取
返回列表