首页 / 资讯中心 / 文章详情

交通信号灯数据集:COCO格式红绿灯检测与YOLO训练指南

交通信号灯数据集:COCO格式红绿灯检测与YOLO训练指南 ★ FEATURED ARTICLE
简介面向计算机视觉开发者的交通信号灯目标检测数据集提供红、绿、黄三种颜色信号灯的实景图片已按COCO格式完成标注可直接用于主流目标检测框架的训练与验证适用于自动驾驶、智慧交通等场景。压缩包内共2000个文件其中jpg图片1995张配套3个json标注文件和2个txt说明文件整体大小约223.94MB标注信息与图片一一对应便于快速上手。图片素材源自真实拍摄场景涵盖不同光线、角度与姿态下的信号灯实例有助于提升模型的泛化能力json标注文件符合COCO规范可无缝接入YOLO、MMDetection等常见工具链。同时附带的txt说明文件对数据组织结构进行了补充说明降低了使用门槛。目前已有594人学习下载适合需要高质量红绿灯数据集的算法工程师、科研人员及竞赛团队可有效减少数据采集和标注的时间成本。1. 交通信号灯数据集红绿黄三色检测为什么值得用COCO格式做车路协同或辅助驾驶 Demo 的人多半有同感公开数据集里行人、车辆一大把但交通信号灯这种小目标、强光照干扰的品类反而最不好找。这个交通信号灯数据集把红绿黄三种颜色的识别和 COCO 格式标注一起打包了你不用再自己对着图片画框解压后直接进训练管线就能跑。从文件名来看图片编号到 620 左右覆盖了不同角度、不同距离的灯组实例对做目标检测入门或者验证检测框架来说是少见的“标注完整、格式现代”的资源。它适合谁想快速跑通 YOLO 或 mmdetection 训练流程的开发者以及需要在信号灯识别方向上做预研的算法工程师。配好环境、解压、检查 JSON、转成自己需要的格式一个晚上就能把训练跑起来。下面我按自己拆这类数据集的经验从标注结构讲到训练避坑最后给一个能直接复用的验证脚本。2. 数据集结构与 COCO 标注先看懂 JSON 再动手训练这个数据集的标注是标准 COCO 格式。很多新手拿到压缩包后第一件事就是把图片扔进训练脚本结果报错一堆原因基本都是没搞懂 COCO 的 JSON 里字段的从属关系。这一章先花十分钟把结构看明白后面所有操作都会顺很多。2.1 文件构成与 Roboflow 导出特征压缩包解压后你会看到很多类似traffic-light-534-_jpg.rf.f4725a2994734785934e0c77592c2e40.jpg的文件名中间那段.rf.是 Roboflow 平台导出时加的哈希标识目的是避免不同来源的图片重名。这不是脏数据处理时不用删但要注意如果你自己写脚本按文件名前缀去匹配图片和标注得先把这段 hash 去掉或统一用image_id关联。常见做法是Roboflow 导出 COCO 格式时会同时生成一个train/valid/test的目录划分以及每个划分对应的_annotations.coco.json标注文件。所以解压后优先找这三个目录和三个 JSON而不是自己重新划分。目录结构大致如下traffic-light-dataset/ ├── train/ │ ├── images/ # 训练图片 │ └── _annotations.coco.json ├── valid/ │ ├── images/ │ └── _annotations.coco.json └── test/ ├── images/ └── _annotations.coco.json这里images/和 JSON 文件是否在同级目录取决于你解压工具和原始打包方式建议先用find命令确认布局再写路径避免训练时路径写死导致文件找不到。find . -type f | head -20这一步的意义是看清文件根目录结构。很多训练框架报FileNotFoundError并不是图片真的丢了而是脚本里的相对路径和实际目录层级对不上。我一般会先把整个目录树打印出来贴到笔记里再写数据加载配置。2.2 标准 COCO 标注的四个核心字段COCO 格式的标注文件是一个 JSON顶层包含images、annotations、categories三个关键数组。对于检测任务你真正要关注的就这三个。字段类型说明images数组每张图的id、file_name、width、heightannotations数组每个框的id、image_id、category_id、bbox、areacategories数组类别列表包含id和name其中bbox是[x, y, width, height]单位是像素坐标原点在图片左上角。这里最容易出问题的是category_id的起点COCO 官方约定类别id从 1 开始而 YOLO 的类别下标是从 0 开始。如果直接拿 COCO 的category_id当 YOLO 的 class 编号训练时类别就整体错位了。import json with open(train/_annotations.coco.json, encodingutf-8) as f: data json.load(f) print(图片数:, len(data[images])) print(标注框数:, len(data[annotations])) print(类别表:, [(c[id], c[name]) for c in data[categories]])这段代码是快速读取标注统计的起点。categories打印出来通常类似[(1, green), (2, red), (3, yellow)]顺序是按类别名字母排的。记住这个顺序后面转 YOLO 格式时不要再重新排序否则标签会张冠李戴。2.3 用 Python 快速校验标注合法性拿到 JSON 后我建议先跑一段校验脚本确认标注没有低级错误再进训练。否则训练到一半才发现某个框的宽高是负数或超出图片边界排查起来非常耗时。import json from pathlib import Path ann_path Path(train/_annotations.coco.json) data json.loads(ann_path.read_text(encodingutf-8)) img_map {img[id]: img for img in data[images]} cat_map {cat[id]: cat[name] for cat in data[categories]} print(图片数:, len(img_map), 标注数:, len(data[annotations])) print(类别:, cat_map) bad_boxes 0 out_of_bounds 0 for ann in data[annotations]: x, y, w, h ann[bbox] if w 0 or h 0 or x 0 or y 0: bad_boxes 1 img_info img_map.get(ann[image_id]) if img_info: if x w img_info[width] or y h img_info[height]: out_of_bounds 1 print(非法bbox数:, bad_boxes) print(越界bbox数:, out_of_bounds)这段校验做了两件事一是检查宽高是否为非正数二是检查框是否超出图片边界。img_map是用image_id建立索引方便快速查找图片的宽高。越界框如果数量少可以训练前手动剔除如果数量多说明标注和图片尺寸不匹配考虑是图片被统一缩放过而 JSON 没有同步更新。3. 把 COCO 格式跑进训练管线划分、转换与参数建议COCO 格式本身不能直接喂给 YOLO 系列训练脚本也不适合直接丢进所有 mmdetection 配置。你需要根据自己的框架做一步转换或配置适配。这章按常见流程讲先定划分再转格式最后聊训练参数。3.1 数据划分train/valid/test 比例怎么设如果压缩包内已经按 Roboflow 默认的train/valid/test划分好了直接沿用即可如果没有划分按目标检测惯例来分。场景trainvalidtest快速验证训练流程70%20%10%数据量少500 张80%10%10%上线前最终评估90%含valid合并5%5%对于几百张的信号灯数据集我建议用 80/10/10因为信号灯是小目标验证集太小会导致 mAP 波动很大。划分时还要保证每个子集里三类灯都有分布不能出现 valid 里全是绿灯的情况。写个简单脚本统计即可import json import random from pathlib import Path all_images list(Path(images).glob(*.jpg)) random.seed(42) random.shuffle(all_images) train_ratio 0.8 valid_ratio 0.1 train_imgs all_images[:int(len(all_images) * train_ratio)] valid_imgs all_images[int(len(all_images) * train_ratio): int(len(all_images) * (train_ratio valid_ratio))] test_imgs all_images[int(len(all_images) * (train_ratio valid_ratio)):] print(ftrain{len(train_imgs)} valid{len(valid_imgs)} test{len(test_imgs)})random.seed(42)保证每次运行划分结果一致这对复现实验很重要。如果你发现某个子集里某类灯一个都没有需要把对应图片手动调剂过去不要依赖纯随机。3.2 COCO 转 YOLO转换脚本与坐标归一化YOLOv5、YOLOv8 用的是每张图一个 txt 的格式每行是class x_center y_center width height坐标基于图片宽高归一化到 0~1。转换脚本我直接给出可用的版本import json from pathlib import Path LABEL_DIR Path(labels) LABEL_DIR.mkdir(exist_okTrue) ann_path train/_annotations.coco.json data json.loads(Path(ann_path).read_text(encodingutf-8)) # 按原 category_id 排序保证类别映射稳定 categories sorted(data[categories], keylambda c: c[id]) cat_to_id {cat[id]: idx for idx, cat in enumerate(categories)} img_id_to_info {img[id]: img for img in data[images]} for ann in data[annotations]: img_info img_id_to_info.get(ann[image_id]) if img_info is None: continue img_w img_info[width] img_h img_info[height] x, y, w, h ann[bbox] cx (x w / 2) / img_w cy (y h / 2) / img_h nw w / img_w nh h / img_h class_idx cat_to_id[ann[category_id]] line f{class_idx} {cx:.6f} {cy:.6f} {nw:.6f} {nh:.6f} label_path LABEL_DIR / (Path(img_info[file_name]).stem .txt) with label_path.open(a) as f: f.write(line \n)这段脚本关键是cat_to_id的构建方式强制按 JSON 里categories数组的id升序排列再映射到从 0 开始的索引。比如原 JSON 里green1、red2、yellow3转换后就是green0、red1、yellow2。如果后面你手动改了类别名或重新排了数组顺序训练结果会完全对不上。另外注意坐标归一化用的是该图真实的宽高不是全数据集统一尺寸。3.3 训练参数建议与颜色空间处理信号灯和普通目标不太一样灯本身是自发光的红黄两色在夜间会过曝绿色在背光时又容易发暗。所以训练参数里要特别关注数据增强环节。常见做法是训练时不要用太大的hsv_h色相增强。YOLO 默认hsv_h0.015如果调大到 0.1红灯会被增成橙色甚至黄色模型学到的是“颜色不稳定的灯”而不是“红灯长什么样”。我建议hsv_h保持在 0.01 以内hsv_s和hsv_v可以适当放开到 0.3 和 0.4模拟白天到黄昏的光照变化。输入尺寸方面640 是通用选择。信号灯在画面里通常只有几十个像素太小属于典型的小目标有条件的话用 640 以上分辨率训练推理时也保持同尺寸不要训练 640 推理 320mAP 会明显掉。以 YOLOv5 为例python train.py --img 640 --batch 16 --epochs 100 \ --data traffic_light.yaml --weights yolov5s.pt--img 640是训练和推理的输入尺寸--batch 16在单卡 8GB 显存下比较稳妥100 个 epoch 对于几百张的数据集足够收敛。如果你的显存只有 6GB把 batch 降到 8 或改用 yolov5n。注意信号灯数据集很小训练时务必开--cache选项否则每轮 epoch 都在重复读盘IO 会成为瓶颈。4. 避坑指南交通信号灯数据集的五个常见问题这部分是我自己拆这类数据集时踩过的坑每条都按“现象 → 原因 → 解决”来写全是血泪经验。4.1 现象训练 loss 正常下降但 mAP 一直起不来且类别错乱训练完成后看results.png发现mAP0.5只有 0.1 左右甚至预测的框类别和真实颜色对不上红灯被标成绿色。原因是 COCO 的category_id从 1 开始YOLO 类别从 0 开始直接拿原始category_id当 YOLO class 用所有类别都整体偏移了一位。解决方法是转换脚本中必须重新建映射按sorted(categories, keylambda c: c[id])生成新索引并用cat_to_id[ann[category_id]]转换。4.2 现象红绿灯框重叠严重黄灯检成红灯信号灯三色灯组是竖直排列的如果标注框画得偏大红色的框下沿会盖住黄色的框训练时黄灯样本的有效特征区域被红灯边界干扰。这类问题常见于标注时把整个灯组外框当成了单个灯的框。如果数据集里已经存在这种框强行改标注成本很高我一般会保留训练但在后处理里对黄灯使用更低的置信度阈值召回同时用 NMS 的类别感知模式防止红灯框把黄灯框压掉。如果用的是 YOLOv8直接在 NMS 参数里把红黄两类的agnostic_nms关掉让两类可以同时保留。4.3 现象白天效果不错夜间几乎全废信号灯是自发光目标夜间红灯过曝后中心变成白色模型白天学到的是“圆形的红色区域”夜间看到“白色圆核红色光晕”就认不出来。原因就是训练数据里白天样本占了绝大多数夜间和背光样本太少。解决思路是扩充数据把这个数据集的图片做亮度降低、对比度提升的增强副本或者单独补一批夜间信号灯图混入训练集。如果不想扩充数据至少把训练时的hsv_v增强幅度调大一点让模型见过更暗的版本。4.4 现象Roboflow 导出的文件名带.rf.训练脚本解析异常部分老的训练脚本按文件名提取图片 id 时默认按点号分割取第一个字段结果traffic-light-534-_jpg.rf.f4725a...被截断成traffic-light-534-_jpg导致图片和标注匹配不上。原因是脚本对文件名格式有固定假设。解决方法是转换阶段就用Path(img_info[file_name]).stem生成 txt这样标注文件会严格跟着图片文件名走如果脚本已经报错批量重命名图片为纯数字 id 是最省力的方式。for f in *.jpg; do mv $f $(echo $f | cut -d. -f1).jpg done这个命令把文件名里的.rf.那段去掉只保留前缀部分。注意执行前先备份因为cut -d. -f1会把所有点号后面的内容删掉。4.5 现象验证集图片尺寸不一致batch 处理报错数据集中有些图片是长条形或竖构图尺寸差异大。深度学习框架要求一个 batch 内图片尺寸统一框架内部一般会做 letterbox 填充但如果你自定义数据加载器没做这一步就会报Expected size错误。原因是加载器缺少尺寸归一化逻辑。解决方法是统一用框架自带的 dataloader或者在自己加载图片时强制cv2.resize到固定宽高但注意等比缩放要配合 letterbox否则灯的形状会被拉伸变形影响检测精度。5. 标注可视化与数据体检训练前先让数据自己说话很多人拿到数据集直接开训训练完才发现标注有错位、缺漏或类别不平衡。我现在的习惯是训练前先做一轮可视化检查和分布统计让数据自己把问题说出来。5.1 标注可视化把 bbox 画回原图检查写一个脚本把annotations里的框画到原图上肉眼扫一遍比任何统计都直观。信号灯是小目标框稍微偏一点都能看出来。import json import cv2 from pathlib import Path ann_path Path(valid/_annotations.coco.json) data json.loads(ann_path.read_text(encodingutf-8)) color_map {1: (0, 0, 255), 2: (0, 255, 0), 3: (0, 255, 255)} # BGR 顺序 name_map {c[id]: c[name] for c in data[categories]} img_map {img[id]: img for img in data[images]} for img_info in list(img_map.values())[:20]: img cv2.imread(str(Path(valid/images) / img_info[file_name])) if img is None: print(读取失败:, img_info[file_name]) continue for ann in data[annotations]: if ann[image_id] ! img_info[id]: continue x, y, w, h map(int, ann[bbox]) color color_map.get(ann[category_id], (255, 255, 255)) cv2.rectangle(img, (x, y), (x w, y h), color, 2) cv2.putText(img, name_map[ann[category_id]], (x, y - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 1) cv2.imshow(check, img) cv2.waitKey(0) cv2.destroyAllWindows()这个脚本用color_map把红绿黄三类框分别画成对应颜色方便一眼识别类别是否标错。img_info[file_name]必须和图片实际路径拼接正确如果读图失败优先检查 images 目录的路径层级。建议抽看 20 到 50 张重点看远处的灯有没有漏标、框有没有把整个灯组包进去。5.2 类别分布与样本质量检查统计每个类别的实例数量能提前判断训练是否会出现类别不平衡。信号灯数据里黄灯数量常常远少于红灯和绿灯因为路口的黄灯亮灯时间最短采集时天然难遇到。import json from collections import Counter from pathlib import Path ann_path Path(train/_annotations.coco.json) data json.loads(ann_path.read_text(encodingutf-8)) name_map {c[id]: c[name] for c in data[categories]} counters Counter(name_map[ann[category_id]] for ann in data[annotations]) for name, cnt in counters.most_common(): print(f{name}: {cnt})看输出结果时关注两点数量最少的类别是不是黄灯以及三类之间的比例是否超过 1:3。如果黄灯只有几十个训练时给它单独设一个更高的采样权重或者复制粘贴增强黄灯样本。这个操作直接影响最终对黄灯的召回率特别是你要做红黄绿三色精确识别而不是只做“有灯”检测时这一步不能省。6. 进阶用类别独立阈值和 IoU 匹配改善密集路口检测模型训练完在真实路口画面里跑会发现一个典型问题红灯和黄灯同时出现时红灯置信度很高黄灯置信度偏低默认阈值下黄灯直接被过滤掉。解决思路是按类别设定不同的置信度阈值而不是全局一刀切。import torch model torch.hub.load(ultralytics/yolov5, custom, pathbest.pt, force_reloadTrue) conf_thr {green: 0.25, red: 0.25, yellow: 0.15} results model(intersection.jpg, size640) for det in results.xyxy[0]: cls int(det[5]) name results.names[cls] conf float(det[4]) if conf conf_thr.get(name, 0.25): print(name, round(conf, 3), [round(v, 1) for v in det[:4].tolist()])conf_thr里黄灯的阈值设到 0.15是因为黄灯样本少、模型对其置信度天然偏低。代价是误检会增加一些但安全方向宁可多检不可漏检。这个脚本还能做一件事对比conf和框坐标确认红灯和黄灯两个框是否同时出现在同一个灯组位置如果红黄同时出现且 IoU 大于 0.1多半是漏了黄灯或类别混淆需要回到数据增强环节补样本。从那以后我每次拿到新的标注数据集都强制自己先走一遍“校验 JSON → 转格式 → 可视化抽检 → 按类别调阈值”这个流程短则半小时长则两小时但换来的是训练结果的稳定可复现而不是玄学调参。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站