斑马线行人交通灯数据集构建与YOLOv8训练实战

发布时间:2026/9/1 17:45:31
斑马线行人交通灯数据集构建与YOLOv8训练实战 简介一套面向智能交通与自动驾驶场景的斑马线、行人与交通灯目标检测数据集及配套源码适合目标检测方向的算法研究者、深度学习开发者及计算机视觉学习者使用。数据源自真实行车记录仪涵盖城市道路与郊区环境并包含阴晴雨雾等光照变化共12554张图像标注83546个实例其中交通灯13826个、斑马线10706个、行人59014个标注精度较高。基于YoLoV5 m6权重训练300轮后mAP0.5达到0.956mAP0.5~0.95为0.7299可快速迁移至不同YoLo系列模型开展训练与验证。资源包为zip格式共含6个文件主要包括数据集说明文档、下载说明txt、项目配置及License文件等整体体积仅9KB轻量便于分发。压缩包内附源码级别的实现细节与目录结构便于直接对照实验适合用于算法训练、模型调优与评测。目前已有53人学习下载。 斑马线行人交通灯数据集听起来是个很小的方向但做下来你会发现它比想象中复杂得多——红绿灯目标小、环境光照变化大、夜间过曝、倒计时数字识别和灯色识别其实是两种任务。我花了一个多月从采集、标注、格式转换到模型训练和部署把整条链路都走了一遍。这篇文章就把这套数据集从零到能跑通源码的完整过程拆开讲透包括采集点位怎么选、标注类别怎么定、VOC/YOLO/COCO三种格式怎么互转、YOLOv8训练参数怎么调以及我在实际过程中踩过的那些坑。适合正在做智慧交通、辅助驾驶、信号灯识别相关项目的同学参考。1. 为什么需要一套斑马线交通灯专属数据集1.1 现有公开数据集的三个痛点做目标检测的人应该都有体会公开数据集和真实项目需求之间始终隔着一层。LISA、Bosch Small Traffic Lights这些老牌交通灯数据集确实经典但它们大多是从驾驶视角拍的画面里信号灯通常挂在远处、比较小目标尺寸集中在几十乘几十像素。而斑马线行人交通灯这个场景有个明显区别灯安装在行人过街通道两侧相机离灯的距离近拍出来的灯体更大、更清晰但同时背景也复杂——斑马线纹理、行人、非机动车、广告牌灯箱都会进来干扰。另一个痛点是状态覆盖不均。老数据集里红灯样本泛滥黄灯和倒计时状态占比很低我在整理时统计过公开数据集中红灯和绿灯的比例往往超过10比1。这种直接拿来做训练模型自然会对少数类视而不见黄灯检测的recall掉到50%以下很常见。第三个痛点是城市风格差异。国内不同城市的行人灯样式五花八门有倒计时数字的、有动态人形动画的、有单灯双色切换的还有带蜂鸣器的。公开数据集以欧美场景为主直接迁移到国内场景误检率会明显上升。所以做项目前一定要先确认你是不是要解决自家门口的问题如果是那自建数据集基本绕不开。1.2 这个项目的场景边界与目标定义做数据集之前最怕边界模糊。我这个项目一开始就定死了四条规则只检测斑马线两侧的行人信号灯目标状态划分为三类加一个辅助类即红灯、绿灯、黄灯外加倒计时数字区域拍摄视角固定在行人过街等待区附近模拟行人视角分辨率要求是单灯目标在1080p画面下不小于40×40像素。这个定义很关键。它决定了后面采集、标注、训练整个链路的一切选择。如果定位是驾驶员视角那相机要装车顶目标会很小模型要抗抖动如果定位是行人视角那相机高度大约在1.5米到1.7米角度略微仰视灯体更大但容易出现侧光和逆光。实际做的时候我建议先把这个问题想清楚再动手否则数据采回来发现视角不对返工成本非常高。2. 数据采集从拍摄场景到原始素材2.1 采集设备与部署位置怎么选这套数据集用的是GoPro Hero 9和一台普通手机分别固定在行人等待区的护栏和自拍杆上。为什么不用工业相机因为最终部署场景也不一定是工业设备消费级相机的传感器尺寸、动态范围更接近真实监控或手机端的应用环境。用GoPro主要是看中它的广角和防抖能力能在一段视频里同时拍到灯体、斑马线和行人上下文。采集位置需要特别注意相机要放在行人停止线后1到2米高度1.5米左右正对信号灯方向角度允许有10度到20度的偏差。这个偏差是故意留的太正会导致模型对视角变化过于敏感太斜又会让灯体失真。每个点位持续录制90分钟以上确保能覆盖完整的红绿黄状态切换周期。一天下来我一般能录3到4个点位的原始素材总计大概6小时的视频。2.2 场景覆盖矩阵别让模型只学会晴天做过视觉项目的都知道泛化能力全靠数据多样性撑。我给自己定了一个覆盖矩阵必须满足这几个维度天气至少覆盖晴天、阴天、雨天三种时间段覆盖白天、黄昏、夜间光照覆盖顺光、逆光、侧光场景覆盖城市主干道、学校周边、商业区人行道。每类至少2000帧有效样本。实际操作起来最麻烦的是夜间和雨天。夜间信号灯会过曝红色和绿色光晕连成一片灯的小目标检测难度直线上升。雨天则是镜头上全是水珠反光和模糊并存。我建议采集时多用几个角度同时拍同一场景这样后期可以互补而不是指望一条视频素材全能用上的概率。另外黄昏时段往往被忽略但这个时段色温变化极大5点钟拍的和6点半拍的画面色调完全不一样建议黄昏时段单独建一个子集。原始视频抽帧也有讲究。按15秒间隔抽一帧既不会让相邻帧太相似导致数据冗余也不会漏掉状态切换瞬间。最终我拿到约12000张原始图经过清晰度筛选和内容去重后剩下10600张有效样本。3. 标注规范与格式转换3.1 类别体系怎么设计我最终的类别体系是这样的red、green、yellow三种灯色加上countdown_digit倒计时数字区域。有人会问为什么不把倒计时直接作为第四类状态因为倒计时数字和灯色在物理上是两个独立区域合在一起标注会导致目标框特别扁或者特别宽对检测器不友好。我的做法是灯色框标注发光灯体的外接矩形倒计时框单独标数字显示区域两者可能在同一个灯杆上但互不重叠。还有一类目标必须考虑行人。斑马线场景里行人是天然的上下文虽然不参与最终检测但我把行人区域也标注了用的是VOC格式的xml文件。后面如果做跟踪或者行为分析这套数据的扩展性就体现出来了。标注工具我用的是X-AnyLabeling支持自动追踪辅助标注对视频帧序列特别省力比纯手工一张张画快了一倍不止。标注规范里有几条硬性规则要提前定好不然10个人标出来是10个样子灯体发光区域和黑色外壳的边界以发光区域为主灯壳只标到外缘即可被遮挡面积超过50%的目标不标倒计时数字区域只包含数字本身不包含周围的圆环或文字说明同一帧内同一信号灯组只能有一个灯色状态。3.2 从标注文件到 VOC / COCO / YOLO 三格式互通标注完了之后格式转换几乎是所有后续工作的前提。我的源码里提供了三个脚本xml2yolo.py、xml2coco.py、yolo2coco.py。这里我强烈建议以VOC格式作为中转格式因为它结构简单一个文件夹放图片一个文件夹放xml没有额外的json依赖人工检查也方便。YOLO格式转换的核心代码逻辑很简单就是读取xml里的每个object的bndbox坐标做归一化import xml.etree.ElementTree as ET def xml_to_yolo(xml_file, img_w, img_h, class_map): tree ET.parse(xml_file) root tree.getroot() yolo_lines [] for obj in root.findall(object): cls_name obj.find(name).text if cls_name not in class_map: continue cls_id class_map[cls_name] bbox obj.find(bndbox) x_min float(bbox.find(xmin).text) y_min float(bbox.find(ymin).text) x_max float(bbox.find(xmax).text) y_max float(bbox.find(ymax).text) x_center (x_min x_max) / 2 / img_w y_center (y_min y_max) / 2 / img_h width (x_max - x_min) / img_w height (y_max - y_min) / img_h yolo_lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}) return yolo_lines转换脚本本身不难难的是转换前要统一类别名称和坐标归一化的基准。我踩过一个坑有些图片是旋转过的Exif信息里带了orientation直接用PIL读像素再配xml坐标就会错位。后来所有图片统一在标注前用ImageOps.exif_transpose做了预处理这个问题才彻底解决。COCO格式则是把xml信息汇总到一个大的instances.json里注意categories的id要从1开始且要和训练配置里的nc参数对得上。3.3 标注质量抽检方法标注质量直接决定模型上限。别以为标注完就万事大吉了我在第一轮训练后专门做了抽检从每个类别里随机抽100张图人工检查标签和实际内容是否匹配。结果发现最大的问题不是框歪了而是red和green标签互换——在夜间红色通道过曝时人眼都容易看错。为了解决这个我在源码里加了一个基于HSV色相角的自动校验脚本对每张图的标注框区域计算平均色相红色类应该在165-180或0-10度区间绿色类应该在40-85度区间。相差明显的图自动标记出来让人复核。这招很有效把标签错误率从肉眼抽检发现的1.8%降到了0.4%以内。数据集的干净程度很多时候比加了多牛的模型结构都重要这一点在后续训练结果上也得到了验证。4. 模型训练与源码使用4.1 环境与依赖训练部分我选了YOLOv8作为主模型。原因很简单生态成熟、文档多、调参资料丰富对单类小目标检测的默认配置也比较友好。环境依赖主要是下面这些ultralytics8.0.0 torch1.8.0 torchvision0.9.0 opencv-python4.5.0 numpy1.21.0 tqdm4.60.0 pyyaml5.3.1硬件方面我用了一张RTX 4090 24GB。如果显存只有16GB可以把batch size从24降到12同时把yaml里的imgsz从1280降到960效果差别不大但速度会快很多。有一句实话放在这里交通灯检测对输入分辨率非常敏感同样的模型640输入和1280输入的mAP50:95差距肉眼可见通常后者能高出8到10个点。所以预算允许的话尽量在推理时也保持1280输入。4.2 训练配置与参数建议数据准备的文件格式按ultralytics的要求来。在images/下放图片labels/下放同名txt文件然后用一个yaml指定路径和类别数train: /path/to/dataset/images/train val: /path/to/dataset/images/val nc: 4 names: [red, green, yellow, countdown_digit]训练脚本的核心参数我这样设置yolo detect train \ modelyolov8n.pt \ datatraffic_light.yaml \ imgsz1280 \ epochs160 \ batch24 \ optimizerAdamW \ lr00.001 \ lrf0.01 \ hsv_h0.015 \ hsv_s0.7 \ hsv_v0.4这里两个点需要解释一下。一是预训练权重我坚持用yolov8n.pt而不是从零训练。信号灯虽然是小目标但预训练模型提供的底层特征边缘、纹理、颜色区域是可以复用的从零训练会多花至少3倍的时间才达到同样的精度。二是增强参数mosaic和hsv增强默认是开的但我在最后30个epoch把mosaic关掉了具体做法是训练两个阶段前130轮开mosaic后30轮用mosaic0.0的配置继续跑。这么做是为了避免最后一阶段还在用马赛克拼接图做BN统计导致验证时对真实分布的单图效果打折扣。4.3 源码目录结构与关键逻辑这套项目的源码目录结构我按模块化思路组织保证别人拿到手上不会迷路traffic_light_dataset/ ├── data/ │ ├── images/ │ │ ├── train/ │ │ └── val/ │ ├── labels/ │ ├── annotations_voc/ │ ├── annotations_coco/ │ ├── train.txt │ └── val.txt ├── tools/ │ ├── xml2yolo.py │ ├── xml2coco.py │ ├── check_label_color.py │ └── split_dataset.py ├── configs/ │ └── traffic_light.yaml ├── scripts/ │ ├── train.sh │ ├── evaluate.py │ └── inference_demo.py ├── requirements.txt └── README.mdsplit_dataset.py这块要特别提醒一句划分训练集和验证集时必须按视频片段划分不能按单张图片随机划分。因为同一段视频里相邻帧太相似如果一部分分到训练集一部分分到验证集验证指标会虚高看起来有0.98的mAP实际上换个视频场景直接拉胯。我按片段划分后验证集上的mAP从虚高的0.96降到了真实的0.91但这个0.91才是真正能反映泛化能力的数据。inference_demo.py里我加了两段值得参考的逻辑。一是对检测框做帧间平滑利用信号灯状态不会瞬间跳变这个先验使用滑动窗口投票连续5帧中至少3帧判定为同一状态才输出该状态这招能滤掉大部分偶发误检。二是对倒计时区域检测框稳定后裁剪出数字区域再交给一个轻量分类模型识别0到9或两位数这一步不是检测任务但如果项目需要读秒建议单独做一个MobileNet分类器不要尝试用检测器去直接识别数字内容。训练完成后我在验证集上的最终指标是mAP50接近0.93mAP50:95约0.71四类中yellow的AP最低大概0.84符合预期——黄灯本来就比较少见而且和红灯在某些曝光条件下确实容易混淆。模型加上NMS后单帧推理速度约11毫秒这里没有算预处理和后处理完全够实时用。5. 常见问题与排查技巧实录5.1 红灯绿灯相互误检这是我在实际测试中遇到最多的一个问题。特别是夜间红色发光管在传感器上会溢出形成一片光晕绿光在过曝时也会发白两者特征变得非常相似。排查手段是用上面提到的HSV校验脚本先在标注层面扫一遍疑似错误标签然后训练时加大颜色增强幅值hsv_h从默认的0.015调高到0.02同时增加夜间样本的权重。另外在推理后处理阶段对每个检测框内区域计算平均色相做二次确认如果检测器的类别结果和色相冲突以后者为准这个方法简单粗暴但确实能挡住不少低级错误。5.2 小目标漏检怎么办斑马线场景中如果相机离灯距离超过30米灯体在1080p画面里可能只有25×25像素这种小目标非常容易被特征提取阶段的下采样操作弄丢。我的经验是两个方向一是输入分辨率尽量用1280而不是640YOLOv8在训练时会对小目标更友好二是推理时使用SAHI切片辅助推理把原图切成带重叠的patch分别检测再合并结果。切片的代价是速度慢3到4倍但小目标检出的提升非常显著。也可以直接换YOLOv8的P6模型结构增加的检测层专门服务小目标不过模型体积和推理耗时都要跟着涨。5.3 训练不收敛或loss乱跳我在第一次训练时用默认的SGD优化器和lr00.01结果到第120个epochmAP50一直停在0.8以下上不去loss在后期还出现了小幅震荡。后来改成AdamW并将初始学习率降到0.001问题立刻缓解。原因在于信号灯目标很小梯度中存在大量高方差贡献SGD对这类噪声梯度的鲁棒性不如AdamW。另外在最后阶段可以用linear的lr schedule代替默认的cosine有时候能让收敛更干脆只是需要多试几次才能确定哪种更好。5.4 关键排查速查表现象可能原因排查方案验证集mAP高但实测差数据划分方式不对视频帧泄漏按视频片段重新划分数据集red/green互检严重夜间过曝、标签错误HSV校验标签调整增强参数yellow几乎检不出样本不平衡补采黄灯样本或做复制粘贴增强小目标漏检多输入分辨率不足imgsz1280考虑SAHI切片推理loss后期震荡学习率过高、mosaic未关闭换AdamW最后30轮关mosaic部署端速度慢后处理代码没优化用TensorRT或OpenVINO做加速这套数据集和源码说白了是一次自给自足的完整实践从打开录像机到端上跑通推理每个环节都留下了记录。我自己最大的体会是数据集的构建占了这个项目七成的时间但也是回报率最高的投入。标注规范定得细致一点、校验脚本写早一点、划分方式想清楚一点后面训练和调优阶段能省下大把时间。如果你也在做类似的红绿灯识别、智慧路口、行人过街辅助等项目拿这套流程去套先立规则再动工数据攒够再把模型训练起来整个节奏会顺畅很多。最后再分享一个小技巧采集素材时多存一份原始视频不要删后面想加新类别或者做视频级验证时这些都是最宝贵的资产。本文还有配套的精品资源点击获取