COCO格式交通信号灯数据集处理与红绿灯YOLO训练实战指南

📅 发布时间:2026/10/8 13:50:18
COCO格式交通信号灯数据集处理与红绿灯YOLO训练实战指南
简介这份数据集面向计算机视觉与自动驾驶感知方向的学习者聚焦交通信号灯目标检测任务覆盖红、绿、黄三种灯色可支撑模型训练、算法验证与课程设计等场景。包体共2000个文件以1995张JPG图片为主体并配套3个JSON标注文件与2个TXT说明文件整体大小约223.94MB其中JSON标注采用COCO格式方便直接接入主流检测框架。已有594人下载学习资源经过分类整理图片场景包含不同拍摄角度与光线条件可用于提升模型在真实道路环境中的泛化能力。对需要交通灯数据集进行YOLO、Faster R-CNN等模型训练或竞赛实践的用户这套数据能节省不少人工标注时间。1. 交通信号灯数据集不难找但拿COCO格式落地训练先要过这三道坎这个“交通信号灯数据集”压缩包主打红绿黄三类标注和COCO格式标记。很多做车路协同、辅助驾驶和红绿灯识别算法比赛的人拿到手第一反应都是直接拿它去训练检测模型但这里有个语义要拧清楚它不是训练好的识别模型而是一批已经把红、黄、绿三种灯用框标出来的数据能不能“识别”取决于你后面的训练和评估。它的价值在于省掉从采图、抽帧到人工标注的整个前期周期适合拿来验证模型结构、跑通训练流程和做数据增强实验。新手拿到先别急着跑训练把标注质量核一遍能省下后面几倍的返工时间。2. 拆开一个COCO格式的交通信号灯数据集目录、JSON与标注字段这类打包好的交通信号灯数据集目录结构通常很规整图片归图片标注归标注。大部分压缩包会按 image 和 annotation 两大块组织也有偷懒的打包方式比如把图片全部平铺在一个目录只给一份 json。我拿到压缩包的第一件事永远是先解压看目录而不是先看代码因为目录结构决定了我后面所有脚本的路径假设假设错了后面全白跑。2.1 常见目录布局images 与 annotations 两级划分文件怎么放常见的组织方式是这样路径内容是否必需images/train/训练图片jpg 或 png必需images/val/验证图片通常有annotations/instances_train.json训练集标注COCO 格式必需annotations/instances_val.json验证集标注通常有README.md采集场景、类别顺序、标注规范尽力保留labels/转换后的 YOLO txt通常需要自己生成不随包提供train / val 的划分比大多数人想象的重要。有些数据集只给一份 json需要自己按比例划分这时候一定要在图片层面划分最好按视频片段或拍摄路口为单位不要直接随机分到单张图片。否则同一个路口的连续帧会同时出现在训练集和验证集验证时 mAP 虚高部署到没见过的路口立刻露馅。先跑两条命令把结构看清楚unzip traffic_light_coco.zip -d ./traffic_light find ./traffic_light -maxdepth 2 -type d | sort du -sh ./traffic_light/images/*逻辑很简单第一条解压第二条列出所有目录层级第三条看图片目录体积。如果du显示某个文件夹只有几 MB说明图片还没解压完整或者压缩包里本身就是外部图片链接的占位文件这种数据集可以直接丢掉别浪费时间。我一般还会顺手看一眼 json 文件体积标注文件只有几 KB 的数据集大概率只有几十张图训练出一个能用的红绿灯模型几乎不可能。2.2 annotation 字段逐项说明categories、images、annotations 与 bbox 坐标COCO 格式的信号灯标注json 里最核心的三段是 images、annotations 和 categories。下面是我从这类数据集里最常见到的结构我精简掉了不影响理解的 info 和 license 字段{ images: [ {id: 1, file_name: img_001.jpg, width: 1920, height: 1080} ], annotations: [ { id: 1, image_id: 1, category_id: 1, bbox: [830.5, 412.2, 28.3, 42.6], area: 1205.6, iscrowd: 0 } ], categories: [ {id: 1, name: red}, {id: 2, name: yellow}, {id: 3, name: green} ] }categories 的 id 和 name 是第一个要核对的点。多数交通信号灯数据集会把 id 从 1 开始编red1、yellow2、green3但也有直接从 0 开始的。后面转 YOLO 标签时如果按 1、2、3 直接写标签范围会在 1 到 3 而不是 0 到 2训练启动直接报错。images 里每条记录至少要给出 id、file_name、width、height这是后面归一化坐标的分母。annotations 里最常用的是 bbox 字段注意它是左上角 x、左上角 y、框宽 w、框高 h不是中心点。area 是面积检测模型训练时基本用不到。iscrowd 一般全为 0表示这是独立的单个灯而不是密集群组。有些数据集还会带 segmentation但信号灯这种小目标标注segmentation 经常退化成 bbox 的四个角点甚至完全没有这没关系目标检测任务只需要读 bbox。这些字段的理解直接影响你后面写转换脚本时的每个加减乘除错一个就全错。2.3 拿到压缩包先做的三步核验解压、统计、画框我不会直接拿这份数据开训先做一个纯 json 的统计脚本看分布再决定下一步import json from collections import Counter with open(annotations/instances_train.json, r, encodingutf-8) as f: coco json.load(f) cat_map {c[id]: c[name] for c in coco[categories]} counts Counter({name: 0 for name in cat_map.values()}) tiny, bad [], [] for ann in coco[annotations]: cat cat_map.get(ann[category_id], unknown) counts[cat] 1 x, y, w, h ann[bbox] if w 0 or h 0 or x 0 or y 0: bad.append(ann[id]) if w 32 and h 32: tiny.append(ann[id]) print(图片数:, len(coco[images])) print(标注数:, sum(counts.values())) print(每类数量:, dict(counts)) print(异常框数量:, len(bad)) print(小目标框(宽高都小于32px)数量:, len(tiny))脚本逻辑分三步先从 categories 里构建 id 到名称的映射保证后面打印出来的是可读类名而不是数字然后遍历所有标注做三件事——统计类别数量、检查 bbox 的坐标合法性、统计小目标占比。信号灯是典型的小目标全高清图片里一个灯头通常只有几十个像素宽如果小目标框占比超过一半说明训练输入尺寸至少要用 960 而不是 640。如果 bad 里出现负数坐标或宽高为零的框说明标注工具和 json 之间存在坐标系不一致或者是有人手工编辑 json 时把左上角坐标填成了中心点这种数据不洗掉会让训练时 loss 出现莫名其妙的尖峰。如果统计结果正常再做一步画框核验import json, cv2 from pathlib import Path with open(annotations/instances_val.json, r, encodingutf-8) as f: coco json.load(f) imgs {i[id]: i for i in coco[images]} anns {} for a in coco[annotations]: anns.setdefault(a[image_id], []).append(a) for img_id, ann_list in list(anns.items())[:10]: info imgs[img_id] img cv2.imread(str(Path(images/val) / info[file_name])) for a in ann_list: x, y, w, h map(int, a[bbox]) if a[category_id] 1: color (0, 0, 255) elif a[category_id] 2: color (0, 255, 255) else: color (0, 255, 0) cv2.rectangle(img, (x, y), (x w, y h), color, 2) cv2.putText(img, str(a[category_id]), (x, y - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.6, color, 1) cv2.imwrite(fcheck_{img_id}.jpg, img)这个脚本把验证集前 10 张图叠加标注框输出红框是 red黄框是 yellow绿框是 green。画框这一步是给肉眼看的框有没有包住整个灯头、有没有把相邻两个灯圈在一起、有没有明显偏移都能直接看出来。画框如果偏了半个灯头后面所有训练都是白做。统计脚本只要几秒画框脚本也就一分钟这两步是拿到交通信号灯数据集压缩包后最值得做的投资。3. 把 COCO 标注转成 YOLO 标签并训红绿黄检测模型转换脚本与训练参数COCO json 格式适合评估但主流检测训练工具链里YOLO 生态对 txt 标签的支持最省心。我一般会把 COCO 转成 YOLO txt 再训训练完再把预测结果转回 COCO json用 pycocotools 算指标。这一章给出一套我常用的转换脚本和训练参数直接抄就能用。3.1 为什么要转换COCO 负责评估YOLO 负责训练COCO 和 YOLO 的标签格式差异说到底就是坐标表达方式不同对比项COCO 标注YOLO 标签文件组织一个 json 集中管理每张图一个 txt坐标含义bbox [x, y, w, h]左上角 宽高一行 class cx cy w h中心点 宽高坐标范围像素绝对值0 到 1 归一化类别 id可以从 1 开始必须从 0 开始适用场景评估、跨平台交换训练主流检测器这个转换只是坐标表达方式的迁移不损失信息。有人会说“直接用 COCO 也能训”技术上确实可以但调试时打开 txt 文件扫一眼就知道某张图有哪几个灯比解析 json 直观得多。而且后续做按类别重采样、清洗异常框、拆分整组灯这类操作txt 文件也更方便处理。3.2 COCO 转 YOLO可复制的转换脚本与三个关键参数转换脚本我固定用一套基本适配所有这类交通信号灯数据集import json from collections import defaultdict from pathlib import Path def coco_to_yolo(json_path, img_root, out_dir, cat_map): with open(json_path, r, encodingutf-8) as f: coco json.load(f) img_info {img[id]: img for img in coco[images]} anns_by_img defaultdict(list) for ann in coco[annotations]: new_id cat_map.get(ann[category_id]) if new_id is None: continue anns_by_img[ann[image_id]].append((ann, new_id)) missing [] for img in coco[images]: if not (Path(img_root) / img[file_name]).exists(): missing.append(img[file_name]) if missing: print(警告缺失图片数量, len(missing), 例如, missing[:3]) out_dir Path(out_dir) out_dir.mkdir(parentsTrue, exist_okTrue) for img_id, items in anns_by_img.items(): img img_info[img_id] w, h img[width], img[height] txt_path out_dir / (Path(img[file_name]).stem .txt) with open(txt_path, w) as f: for ann, new_id in items: x, y, bw, bh ann[bbox] cx (x bw / 2) / w cy (y bh / 2) / h cx max(0.0, min(1.0, cx)) cy max(0.0, min(1.0, cy)) bw_n max(0.0, min(1.0, bw / w)) bh_n max(0.0, min(1.0, bh / h)) f.write(f{new_id} {cx:.6f} {cy:.6f} {bw_n:.6f} {bh_n:.6f}\n) if __name__ __main__: coco_to_yolo( json_pathannotations/instances_train.json, img_rootimages/train, out_dirlabels/train, cat_map{1: 0, 2: 1, 3: 2}, )这里有几个参数是经常出错的点。第一是cat_map它把 COCO json 里的原始 id 映射到 YOLO 要求的 0、1、2。大多数 COCO 格式数据集的 categories.id 从 1 开始所以写{1: 0, 2: 1, 3: 2}。如果你拿到的 json 里 id 本来就是 0、1、2就传{0: 0, 1: 1, 2: 2}或者干脆去掉映射直接用原始 id。第二是坐标换算bbox 的 x、y 是左上角转成中心点要加 bw/2、bh/2再除以原始图片宽高。这里除以的是 json 里记录的图片宽高不是 bbox 自己的宽高这个方向很多人会搞反。第三是写入方式我用的是按图片聚合后一次性写文件千万不要用追加模式否则脚本跑两遍txt 里出现两行重复标签训练 loss 看似正常mAP 却会神秘下降。脚本里还加了一个图片存在性检查因为 zip 解压不全的情况太常见。json 里写了 1000 张图图片实际只有 800 张不检查直接转就会出现大量空 txt训练时这些样本被跳过你还在纳闷为什么验证集指标上不去。3.3 训练配置模型选择、输入尺寸与增强参数转换完成后先写一个数据集 yamlpath: ./traffic_light train: images/train val: images/val names: 0: red 1: yellow 2: green然后开始训练yolo detect train \ datatraffic_light.yaml \ modelyolov8s.pt \ imgsz960 \ epochs200 \ batch16 \ lr00.01 \ hsv_h0.015 \ hsv_s0.7 \ hsv_v0.4 \ patience30参数选择上有几条经验。模型从 yolov8n 起步也可以但信号灯是小目标n 模型的特征提取能力在中低分辨率下偏弱用 s 或 m 对 recall 更友好我建议先用 s 复现。imgsz960是我对这类数据集最常用的输入尺寸灯头宽高常常小于 32px640 输入会把特征压缩掉960 能明显改善小目标显存不够就降 batch。hsv_s0.7和hsv_v0.4用于模拟不同天气下的饱和度和亮度差异缓解偏色问题。hsv_h保持默认的 0.015 左右对红绿灯不要再加大放大到 0.03 以上会把红灯增强成黄灯反而破坏类别语义。还有两个容易被默认值坑到的点。第一个是 mosaic默认 1.0 在普通数据集好用但信号灯图片里背景占比大mosaic 拼图经常把灯头裁到边界等于训练时大量小目标变成了半截框val 的 mAP 会忽高忽低。我一般会关小到mosaic0.5。第二个是早停 patience信号灯数据集普遍不大很多模型到 100 个 epoch 就在过拟合边缘patience30能少走弯路。这些经验在损失曲线里看不出来但在验证集的每类 AP 上非常明显。4. 红绿黄三类识别最容易踩的坑颜色偏色、标注错位与类别不均这一章是整个方案里最值钱的部分。格式转换和训练参数都能快速搞定真正让红绿灯检测翻车的往往是标注分布和颜色语义。我把自己踩过的坑按现象、原因、解决三件事写出来每条都对应实际训练里能看到的具体症状。4.1 类别分布与颜色混淆黄灯为什么总被红灯吃掉现象训练 loss 掉得很正常mAP0.5 能到 0.85但把 per-class AP 打出来yellow 只有 0.3 左右red 却到 0.9 以上。打开标注统计red 占 70%green 占 25%yellow 不到 5%。原因红灯停车时间长视频抽帧天然密集绿灯时间适中黄灯一般只有 3 到 5 秒一帧就过去了。黄灯的样本数量被数量级碾压检测器在这个少数类身上学不到稳定的“颜色 形状”联合特征于是遇到黄灯时模型倾向输出置信度更高的红灯。解决先做分层抽帧黄灯帧全部保留红、绿按比例抽到约为黄灯的 1.5 倍再重新生成 json。不要简单复制黄灯样本复制会让模型过拟合到特定几个路口的灯型。抽帧时按视频片段划分避免同一条视频既进 train 又进 val否则验证指标虚高实车继续漏检。现象混淆矩阵里 yellow 被识别成 red白天强光下尤其严重。打开预测图黄色箭头灯在过曝后发白部分灯罩喷涂偏橙肉眼和模型都容易往红色靠。原因RGB 空间里黄色与红色在 R 通道上非常接近JPEG 压缩和高光又让橙色进一步趋近红色。解决训练时把hsv_s调到 0.8、hsv_v调到 0.5 左右模拟不同天气下的饱和度差异对黄色箭头的样本做轻微锐化增强让灯芯轮廓更清晰。不要单独加大hsv_h那会让红黄类别语义互换。提示hsv 增强参数调得好不好要用验证集的混淆矩阵来判断。如果红黄误检没有变化先把目标锁定在数据分布上而不是继续调增强参数。4.2 标注粒度与边界整组灯一个框、夜间光晕偏小现象训练时 mAP 很高模型在单灯图片上表现很好但部署后同一组红绿黄灯同时亮时模型只在整组灯的位置输出一个类别。下游逻辑想知道“现在是不是黄闪状态”模型给不出独立信号。原因标注者把一整排或一竖排的三个灯头打成一个 bbox。训练样本看起来没毛病框确实框住了灯但框内同时包含三种颜色检测器被迫在这个位置选一个类于是每次输出都偏向历史样本量最大的红灯。解决先按宽高比排查。竖排三灯的框宽高比通常大于 1.5横排的宽高比小于 0.6。对这类框做拆分比如竖排把原始框高度三等分生成三个新 bbox每个框的类名按灯芯实际颜色重新确认。拆分后要把 area 字段同步更新segmentation 如果存在就直接清空。自动拆分前先抽 20 张图人工检查拆错率超过 10% 就不要再全自动回到标注工具里重标。现象夜间验证 mAP 比白天还高但白天部署漏检变多或者夜间检测框总是套在光晕边缘而不是灯芯上。原因夜间灯芯周围光晕强有的标注框只包住灯芯有的把整个光晕包进去模型学到的是高亮光斑纹理。白天没有光晕特征对不上白天部署自然漏检。解决我一般会把夜间框按灯芯半径外扩 15% 到 20%让框包含一部分柔光区域这样模型学到的是“灯芯 光晕”的组合白天依然有灯芯可用夜间也不会只盯光晕。同时把白天和夜间的图片混合训练不要单独训夜间模型单独训的模型在跨时段时会被亮度分布坑得很惨。4.3 文件与坐标不一致EXIF 旋转和宽高不符现象按 json 画框框和灯的位置完全对不上或者框转了 90 度训练 loss 不降。开始怀疑是格式转换脚本写错了但在图片查看器里直接看图又是正的。原因手机和工业相机拍出的 JPEG 可能带 EXIF 旋转标记。标注工具读图时会自动把图转正json 里记录的是转正后的宽高和坐标但训练脚本如果直接用原始 JPEG 解码读进来的实际宽高和 json 里的宽高不一致坐标就全偏了。解决先用ImageOps.exif_transpose(img)把图片转正并重新存成无 EXIF 的副本再用新副本的宽高重新构建 json。如果这个数据集是你自己采集的建议采集时就把图片统一转成 PNG 或去掉旋转标记后面能少非常多的麻烦。已经转好的 json不要再手工去改 width 和 height改一处错一处。5. 别只盯 loss用 COCO API 分层看红绿黄 AP 与混淆矩阵我见过不少人训练结束只看 val loss 和一张总 mAP 表。红绿灯检测的问题恰恰不是总体指标不行而是某个颜色类别悄悄垮掉。要量化它最直接的办法是把预测结果转回 COCO json用 pycocotools 把每类 AP 打出来。5.1 用 pycocotools 跑出 per-class APimport numpy as np from pycocotools.coco import COCO from pycocotools.cocoeval import COCOeval gt COCO(annotations/instances_val.json) pred gt.loadRes(runs/val/predictions.json) ev COCOeval(gt, pred, bbox) ev.evaluate() ev.accumulate() ev.summarize() for i, cat_id in enumerate(ev.params.catIds): ap50_95 np.nanmean(ev.eval[precision][0, :, i, 0, 2]) ap50 np.nanmean(ev.eval[precision][0, :, i, 0, 0]) print(fcat_id{cat_id}: AP[0.5:0.95]{ap50_95:.3f} AP0.5{ap50:.3f})predictions.json 是检测器输出的 COCO 预测格式每条包含 image_id、category_id、bbox 和 score。这里 precision 数组的维度是[IoU阈值, 召回阈值, 类别, 面积范围, maxDets]所以[0, :, i, 0, 2]取的是第 i 个类别、所有召回点、面积 all、maxDets100 的那一组再对召回维度取平均就是该类别的 AP。用 np.nanmean 是因为该类别在某个召回点没有任何匹配时会出现 NaN直接 mean 会把结果拉低。5.2 用混淆矩阵定位跨颜色误检per-class AP 能告诉你哪个类弱但不能告诉你它被误检成了哪个类。我用训练自带的混淆矩阵图重点看 red→yellow、yellow→red 两个格子。如果 red→yellow 误检高说明标注里黄色箭头被标红回到第 4 章的偏色处理如果 yellow→red 误检高说明黄色样本太少回到分层采样。颜色混淆和类别不平衡在指标上表现不一样但都能在混淆矩阵里一眼看出来。平时我还会按白天、夜间分别跑一次验证两种环境下的 AP 分开记因为这两个场景的失败原因完全不同混在一起算会把问题掩盖掉。我早期训练一个信号灯模型整体 mAP0.5 到了 0.86 就上线结果在早高峰路口连续漏掉三次黄灯车里的决策逻辑只能靠红灯倒计时硬抗。那次之后我把 per-class AP 打印放进了每次训练后的固定动作黄灯 AP 低于 0.5 不放手。希望帮到你。本文还有配套的精品资源点击获取