ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

交通信号灯数据集:COCO格式目标检测实战与YOLOv8训练指南

交通信号灯数据集:COCO格式目标检测实战与YOLOv8训练指南 简介交通信号灯数据集专注于红绿黄三种灯色的目标检测采用COCO格式标注适合计算机视觉学习者、自动驾驶及智能交通项目开发者使用。整个压缩包共两千个文件其中一千九百九十五张JPG图片覆盖不同光照、天气、角度与距离下的真实城市交通场景有助于提升模型在复杂环境中的泛化能力三个JSON文件按COCO标准记录类别与边界框信息可直接加载进MMDetection、YOLOv8等常见训练框架另有二个TXT文件用于类别映射或数据划分说明。压缩包整体约二百二十四MB结构清晰便于快速下载与部署。目前已有五百九十四人学习下载数据量适中既可用于完整模型训练也可作为目标检测入门与算法验证的基准数据。借助这套数据使用者能快速构造红绿灯识别模型减少自行采集、清洗与标注的时间成本专注算法调优与落地验证。1. 交通信号灯数据集一个直接能用的三分类 COCO 目标检测原料包做辅助驾驶、车路协同或者城市交通感知红绿灯检测是绕不开的一环。我之前找数据时翻过车GitHub 上要么是视频流没标注要么是国外路口风格和国内差异太大整理半天心情全无。这个交通信号灯数据集的吸引力在于它直接给了 COCO 格式的标记图片按 traffic-light-编号 这种规律命名目标就是红、绿、黄三种颜色省掉了从零标注的体力活。对刚接触目标检测的人来说它可以用来完整走一遍“数据→训练→验证”流程对老手来说它适合做预训练底材或者用来验证检测算法的改动。下面我从标注格式、训练参数到踩坑记录按实际落地顺序过一遍顺便给一些能直接抄的脚本。2. COCO 格式盘一盘标注 JSON 结构与三个关键字段2.1 为什么选 COCO 而不是 YOLO txt 或 VOC XMLCOCO 格式是目前目标检测领域最通用的标注载体做检测的人总会在某个环节碰到 COCO JSON、Labelme 输出或者 Roboflow 导出包。这个数据集直接用 COCO 格式封装意味着你拿到手不用再写 VOC XML 转 COCO 的转换器少一道人工检查。COCO 的核心结构是三个顶层数组images、annotations 和 categories。images 里存每张图的 id、宽度、高度、文件名annotations 里存每个目标框的 image_id、category_id、bbox 和 areacategories 里存类别 id 与类别名。红绿灯检测的类别就是 red、green、yellow对应的 category_id 常见是 1、2、3但有些工具会从 0 开始这是需要确认的第一个坑。下面是我常用的一张对比表方便你决定要不要转格式。格式标注结构主要使用场景转换成本COCO JSONimages annotations categoriesbbox 为像素坐标Detectron2、MMDetection、Roboflow低YOLO txt每张图一个 txt类别 归一化中心点和宽高YOLOv5/YOLOv8低VOC XML每张图一个 xmlbbox 为像素坐标早期 PASCAL VOC、SSD中虽然 YOLOv8 官方训练支持读取 COCO 格式但我在实际项目里更倾向于先把它转成 YOLO 的 txt。原因很简单YOLO 的归一化坐标在训练时少一步求解缩放batch 读取更快而且 YOLO 的 data.yaml 配置起来也直观。如果你用 MMDetection直接喂 COCO JSON 反而更顺因为 MMDetection 的 CocoDataset 就是为这个设计的。所以不要急着转先想清楚你后面要跑哪个框架。2.2 从文件名到标注先看清目录和类别编号压缩包打开后别急着开训练脚本先花两分钟确认目录结构。文件名类似 traffic-light-534-_jpg.rf.f4725a2994734785934e0c77592c2e40.jpg其中 rf 是 Roboflow 导出时常见的后缀编号 534、546、549 是原始样本编号。这种命名有一个好处样本编号是连续的方便你按区间切训练集、验证集和测试集而不是靠随机 shuffle。Roboflow 导出的 COCO 包通常包含一个 _annotations.json 文件里面就是全部标注数据。拿到手后我第一件事是写个脚本读一遍 categories 和 images 总数确认类别名称和 id 的对应关系。下面这段代码只做信息检查不画图。import json with open(annotations/_annotations.json, r) as f: coco json.load(f) print(categories:) for c in coco[categories]: print(f id{c[id]}, name{c[name]}) print(image count:, len(coco[images])) # 粗略统计每个类别的标注数量 from collections import Counter cat_counter Counter() for a in coco[annotations]: cat_counter[a[category_id]] 1 print(annotation count per category:, dict(cat_counter))逻辑说明这段代码先读 JSON 顶层 categories列出类别的 id 和名称再统计 annotations 里每个 category_id 出现的次数。用途有两个一是确认是不是红绿黄三类的 id 顺序二是看类别是否均衡。红绿灯数据里红色和绿色的数量通常偏高黄色偏少这是后面训练时要重点处理的类别不平衡问题。参数说明_annotations.json 的路径要根据你解压的实际目录调整有些包会把 JSON 放在根目录有些放在 annotations 子目录。如果统计出来的类目 id 是 0、1、2那么后面转 YOLO 格式时不用改如果 id 是 1、2、3转换为 YOLO 时记得减一因为 YOLO 类别 id 默认从 0 开始。2.3 先画 bbox 再动手训练把标注可视化出来检查完数量和类别先别直接开训练建议做一次可视化抽查。COCO bbox 的格式是 [x, y, width, height]x、y 是框左上角坐标width、height 是宽高。常见翻车点是有人把中心点坐标当成了左上角或者把归一化坐标当成了像素坐标导致画出来的框偏移。还有一类问题是标注框超出了图片边界图片本身是 640×640但框的 x width 超过了 640这在训练时轻则裁切掉信息重则数据加载报错。下面这段代码将标注框叠加到原图上抽查前 5 张方便你肉眼确认。import json import cv2 import matplotlib.pyplot as plt ann_path annotations/_annotations.json img_dir images/ with open(ann_path, r) as f: coco json.load(f) cats {c[id]: c[name] for c in coco[categories]} # 假设 1red, 2green, 3yellow可按实际输出调整 color_map {1: (0, 0, 255), 2: (0, 255, 0), 3: (0, 255, 255)} for img in coco[images]: img_path img_dir img[file_name] image cv2.imread(img_path) if image is None: print(fmissing: {img_path}) continue annos [a for a in coco[annotations] if a[image_id] img[id]] for a in annos: x, y, w, h [int(v) for v in a[bbox]] color color_map.get(a[category_id], (255, 255, 255)) cv2.rectangle(image, (x, y), (x w, y h), color, 2) cv2.putText(image, cats.get(a[category_id], ?), (x, y - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 1) plt.imshow(cv2.cvtColor(image, cv2.COLOR_BGR2RGB)) plt.show() # 先抽查 5 张别全量弹窗 if len(coco[images]) 5: break这段代码首先按 image_id 筛选出当前图片的 annotations然后用 cv2.rectangle 画框。需要特别注意的是 bbox 里的值是 Python 数字cv2.rectangle 要求 int所以先做一次 int() 强转。color_map 里默认按 category_id 1 红、2 绿、3 黄分配颜色如果你的类别 id 是 0 开头需要同步修改。如果你跑的机器没有显示器把 plt.show() 换成 cv2.imwrite(viz.jpg, image)把结果输出到文件检查这更适合服务器环境。我一般会在这个环节顺手统计一下每张图标注框的宽高比。红绿灯的形状大多数是竖长条宽高比明显小于 1。如果统计结果里出现了大量接近正方形的框那说明标注可能把灯和灯杆、背景框一起圈进去了后续训练会让模型学到错误特征。3. 拿它训练 YOLOv8从 COCO 转 YOLO 格式到出模型3.1 转换脚本把 COCO bbox 转成 YOLO 归一化坐标我平时跑得最多的是 YOLOv8因为它开箱即用对小目标的检测调参相对省事。红绿灯在画面里往往是小目标所以我会先转成 YOLO txt。转换逻辑很直接COCO 的 bbox 是 [x, y, w, h]YOLO 需要的是 [class_id, center_x, center_y, norm_w, norm_h]其中 center_x 是目标中心相对于图片宽度的比例norm_w 是目标宽度除以图片宽度。类别 id 需要从 COCO 的 category_id 减一因为 COCO 通常从 1 开始YOLO 从 0 开始。下面是我常用的转换脚本处理整个目录。import json import os ann_path annotations/_annotations.json img_dir images out_dir labels os.makedirs(out_dir, exist_okTrue) with open(ann_path, r) as f: coco json.load(f) img_id_to_info {img[id]: img for img in coco[images]} # 按 image_id 聚合标注 from collections import defaultdict annos_by_img defaultdict(list) for a in coco[annotations]: annos_by_img[a[image_id]].append(a) for img_id, annos in annos_by_img.items(): img_info img_id_to_info[img_id] width img_info[width] height img_info[height] img_name img_info[file_name] txt_name os.path.splitext(img_name)[0] .txt with open(os.path.join(out_dir, txt_name), w) as f: for a in annos: x, y, w, h a[bbox] cx x w / 2 cy y h / 2 norm_cx cx / width norm_cy cy / height norm_w w / width norm_h h / height cls_id a[category_id] - 1 # 若 COCO id 从 0 开始则不要减 f.write(f{cls_id} {norm_cx:.6f} {norm_cy:.6f} {norm_w:.6f} {norm_h:.6f}\n)逻辑说明脚本先把 COCO 的 images 按 id 建立索引再把 annotations 按 image_id 聚合这样每个图片只写一个 txt。宽度和高度直接取自 JSON 的 images 字段如果这里的数据和真实图片不一致后面训练会出现标签错位。写入 txt 时每一行是一个目标五个值分别是类别 id、中心点 x 百分比、中心点 y 百分比、宽百分比、高百分比。参数说明norm_cx 等归一化数值保留 6 位小数就够了。cls_id category_id - 1 这行很关键如果你的 JSON 是从 Labelme 转出来的category_id 本身可能已经从 0 开始那就不要减。判断方法很简单回到 2.2 的检查脚本看 categories 里第一个 id 是 1 还是 0。转换完以后labels 目录下每个 txt 和 images 目录下的图片一一对应没有标注的图片是不生成 txt 的这一点要特别留意。3.2 配置 dataset.yaml 并启动训练YOLOv8 训练时需要一份 data.yaml指明训练集、验证集路径和类别名称。这里需要把之前切片好的 train 子目录和 val 子目录路径写正确。常见目录结构是traffic-light-dataset/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/ └── data.yamldata.yaml 内容如下path: /absolute/path/to/traffic-light-dataset train: images/train val: images/val names: 0: red 1: green 2: yellow逻辑说明path 是数据集根目录的绝对路径train 和 val 是相对 path 的图片目录路径。YOLOv8 在训练时会自动去 labels 目录下找对应的 txt也就是把 images/train 替换成 labels/train。names 列表的索引必须和 txt 里的类别 id 对应顺序不能变。参数说明如果训练和验证时提示找不到 labels多半是 txt 文件没有放进 labels/train 和 labels/val 这两个目录。注意 YOLOv8 要求 txt 文件名必须和对应图片文件名完全一致包括后缀前的那一段。比如 traffic-light-534-_jpg.rf....jpg 对应的 txt 名就是 traffic-light-534-_jpg.rf....txt一个字节都不能差。启动训练命令我一般这么写yolo detect train \ modelyolov8s.pt \ datadata.yaml \ epochs80 \ imgsz640 \ batch16 \ projecttraffic_light_run \ nameexp1参数说明model 用 yolov8s.pt 起步比 nano 精度高比 medium 训练快imgsz 选 640 是因为原图大多接近这个尺寸batch 要按显卡显存调整8G 显存建议 batch16如果报 CUDA out of memory减半。epochs 我一般先跑 80 到 100红绿灯类别少收敛不会太慢。3.3 训练完成以后看什么mAP50 和类别分布训练结束后不要只看 total lossYOLOv8 会输出 results.png 和混淆矩阵。我习惯先看三个指标mAP50、mAP50-95、per-class AP。红绿灯这种小目标场景mAP50 能到 0.9 以上算是可用的模型mAP50-95 低于 0.5 也不稀奇因为红绿灯太小真实框的 IoU 稍微偏移一点就掉分。重点看 yellow 类别的 AP如果明显低于 red 和 green那基本可以断定是样本数量太少或者标注框太紧。如果你用的是 MMDetection训练命令则变成这样python tools/train.py configs/yolox/yolox_s_8x8_300e_coco.py这里不做深讲因为 YOLO 路线更轻量。关键是不管用哪个框架都要把 validation 的结果单独保存成图片。YOLOv8 会在 run/detect/val 目录里生成 val_batch0_pred.jpg我一般会再写个脚本把它单独挑出来看。如果发现绿色灯被误标成红色先别急着加训练集回头检查标注框是不是把相邻的灯也圈进去了。4. 避坑与常见问题红绿灯数据训练中的常见翻车点4.1 训练阶段两个高频报错现象一训练时立刻报错AssertionError: Label class ... does not exist。原因是 txt 里的类别 id 超出了 data.yaml 里定义的类别数量。比如 data.yaml 只定义了 3 类但 txt 里出现了 id3而 id 编号是从 0 到 2 的。解决方法是重新跑 2.2 的类别统计脚本看原始 JSON 里 categories id 是否包括 4 或 5如果有就把多余的类过滤掉。我也遇到过类别 id 为 3 但三个类别其实是 0、1、2这一步纯粹是 COCO 转 YOLO 时减一减错了。现象二训练能跑但 loss 不降验证集 mAP 一直很低。常见原因是训练集和验证集划分时出现了同源图片串扰。Roboflow 导出包的图片文件名带哈希后缀如果你按文件名前缀切分比如取前五个字符作为划分条件很容易让同一个场景的不同帧同时进出训练集和验证集造成“看起来指标高、实际泛化差”的假象。解决方法是按文件名中的原始编号进行分层采样比如编号 mod 10 小于 8 的进训练集其余进验证集保证编号交叉分布。4.2 数据标注里的三个隐蔽坑坑一bbox 坐标单位不对。有的标注工具导出的是归一化坐标比如 0.5、0.5 这种但 COCO JSON 里写的是像素坐标。直接用它去画框会发现框全部挤在图片中心区域。验证方法很简单把 bbox 的值乘以图片宽高再看是否落在真实灯体附近。我一般会在 2.3 的可视化脚本里加一行打印抽查几个框的像素范围是否都在图片尺寸之内。坑二黄色灯和红色灯分类边界模糊。红绿灯数据里 yellow 往往最少而且部分图片中黄色灯在强光下发白标注人员容易把它标成 red 或 green。解决方法是做一次类别统计如果 yellow 数量不到 red 的 20%我会在训练时给 yellow 类别加权重或者在数据增强时提高亮度扰动。YOLOv8 里可以直接在损失权重层面做调整但更省事的办法是手动复制黄色灯样本做轻微翻转和旋转做成简单过采样。坑三夜间和逆光样本不足。红绿灯检测在白天容易跑出好指标一到晚上就翻车。这个资源里如果夜间图占比低需要额外补充。常见做法是用本地视频抽帧再用这个模型做半自动标注先让模型给一个置信度较高的预测框人工只纠正错框这样能把标注工作量压到最低。记住不要用模型预测结果直接当标注入库否则错误会自我强化。5. 最后的动手技巧用测试视频验证模型并快速扩充黄灯样本训练完模型后我习惯先用图片测试再跑一段视频因为红绿灯是动态场景前一帧和后一帧的状态变化能帮你发现漏检。你可以用下面这段 OpenCV 脚本对一段路口视频跑 YOLOv8 的检测结果并输出到新视频。import cv2 from ultralytics import YOLO model YOLO(runs/detect/exp1/weights/best.pt) cap cv2.VideoCapture(intersection.mp4) writer cv2.VideoWriter(out.mp4, cv2.VideoWriter_fourcc(*mp4v), 25, (1280, 720)) while True: ret, frame cap.read() if not ret: break results model(frame, conf0.35, imgsz640, verboseFalse) annotated results[0].plot() writer.write(annotated) cap.release() writer.release()逻辑说明模型加载 best.pt 后逐帧检测conf0.35 表示只有置信度大于 0.35 的框才保留。红绿灯背景复杂conf 设太高容易漏掉小目标设太低会有误检我一般从 0.35 开始调。imgsz640 保持和训练一致视频帧可能被缩放。输出文件我用 mp4v 编码兼容性问题最少。参数说明如果你的测试视频是 1920×1080但写入分辨率固定 1280×720画面会被拉伸。建议先用 cap.get(cv2.CAP_PROP_FRAME_WIDTH) 读取原始宽高再传给 VideoWriter。还有一点不要把检测结果直接输出到屏幕如果要看实时效果用 cv2.imshow 加 break 处理按键退出。从这以后我每次拿到新的信号灯数据集都会强制先做三件事统计类别分布、可视化抽查 bbox、确认 id 偏移。不做这三步后面任何训练脚本都可能被同样的问题卡住。这个交通信号灯数据集本身是三分类 COCO 格式底子干净省了最耗时的标注环节但你仍然需要把格式细节和自己用的框架做对齐。希望我的这些踩坑记录和脚本能帮你在红绿灯检测这条路上少走一次弯路。本文还有配套的精品资源点击获取
返回列表