尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

YOLOv5交通标志检测实战:数据清洗、anchor重聚类与视频部署

发布时间:2026/9/24 19:48:56

资讯中心
01
ARTICLE

YOLOv5交通标志检测实战:数据清洗、anchor重聚类与视频部署

YOLOv5交通标志检测实战:数据清洗、anchor重聚类与视频部署
简介本资源是一套完整的YOLOv5交通标志识别检测实战项目专为计算机视觉初学者及本科毕业设计、课程设计、期末大作业需求者打造覆盖数据采集、模型训练、推理部署全流程解决交通场景下多类标志精准识别与实时检测的实际问题。压缩包共266个文件含53个Python主程序含详细中文注释、59个配置与参数文件.yaml/.cfg/.names、55张标注图像.jpg/.jpeg及26张可视化结果图.png另有24个编译字节码、10个预训练模型.pt、6个文本说明与日志文件整体大小423.31MB结构清晰、模块解耦便于快速复现与二次开发。目前已有112人学习下载项目经作者实测调试支持开箱即用附带训练日志events.out.tfevents、评估结果results.csv及Docker容器化部署方案Dockerfile显著降低环境配置门槛是兼具教学性、工程性与展示性的高分毕设参考范例。1. YOLOv5交通标志识别为什么毕业设计选它不翻车而90%的人卡在数据集清洗和标签对齐上你手头有一份标着“YOLOv5交通标志识别检测数据集代码模型”的压缩包点开发现images/里是几百张模糊的十字路口截图labels/里.txt文件第3列数值动不动就超1.0train.txt路径还混着Windows反斜杠跑通train.py后mAP0.5卡在42.3%比论文里写的68.7%低了整整25个点——不是模型不行是你根本没把“交通标志”这个任务的物理约束喂进YOLOv5的骨头里。这不是一个拿来即用的套件而是一套需要你亲手校准的交通场景专用检测流水线德国GTSRB原始标注是像素坐标类别IDYOLOv5要求归一化中心点宽高但交通标志在图像中存在严重尺度偏移远处小如芝麻、近处占满半屏、光照畸变正午强光反光、隧道出口逆光、遮挡模式树影、雨痕、车牌框——这些恰恰是通用目标检测教程里绝口不提的硬伤。本文只讲一件事如何用这份资源在3天内让YOLOv5在自采交通图像上稳定跑出65% mAP0.5重点拆解数据集清洗的3个致命陷阱、模型微调的2个必改参数、以及部署时label映射错位的玄学排查法。适合正在赶毕设 deadline、需要可答辩结果的本科生和研究生。2. 数据集结构重建从混乱的原始包到YOLOv5可训练格式的强制转换YOLOv5对数据集结构有刚性要求必须严格遵循datasets/traffic_signs/下images/和labels/双目录平行结构且train/val/test子集需通过train.txt等文本文件显式声明路径。但你下载的“成品包”往往直接把GTSRB或TT100K的原始组织方式硬塞进来导致detect.py报错FileNotFoundError: images/00001.jpg——因为路径没按YOLO规范重写。下面分三步重建2.1 彻底清理原始数据集的路径毒瘤常见错误原始数据集用/或\混用路径labels/里.txt文件名与images/图片名大小写不一致如IMG_001.jpgvsimg_001.txt甚至夹带隐藏文件.DS_Store。必须用脚本批量标准化# 进入原始数据集根目录假设原始结构为original_data/train/images/ 和 original_data/train/labels/ find . -name .DS_Store -delete find . -name Thumbs.db -delete # 统一转为小写并修复扩展名GTSRB常有.JPG for img in $(find original_data -name *.JPG); do mv $img ${img%.JPG}.jpg done # 生成标准YOLO目录结构 mkdir -p datasets/traffic_signs/{images,labels}/{train,val,test}提示find命令必须加-name精确匹配避免误删.jpg文件本身。.DS_Store在Mac系统下自动生成若不清理YOLOv5的create_dataloader()会因读取非图像文件而崩溃。2.2 标签格式强制归一化解决坐标越界和类别ID错位YOLOv5要求每张图对应.txt文件每行格式为class_id center_x center_y width height全部归一化到0~1。但GTSRB的XML标注含xminyminxmaxymaxTT100K的CSV含x1,y1,x2,y2,class直接转换极易出现center_x 1.0或width 0。必须用Python脚本做边界兜底# convert_gtsrb_to_yolo.py import os import xml.etree.ElementTree as ET from pathlib import Path def convert_xml_to_yolo(xml_path, img_width, img_height, output_dir): tree ET.parse(xml_path) root tree.getroot() # 提取所有object yolo_lines [] for obj in root.findall(object): cls_name obj.find(name).text.strip() # GTSRB类别映射表关键不能直接用原始ID cls_map {speedlimit:0, stop:1, yield:2, priority:3, noentry:4} if cls_name not in cls_map: continue # 跳过未定义类别 bbox obj.find(bndbox) xmin int(bbox.find(xmin).text) ymin int(bbox.find(ymin).text) xmax int(bbox.find(xmax).text) ymax int(bbox.find(ymax).text) # 归一化计算核心防错逻辑 x_center (xmin xmax) / 2.0 / img_width y_center (ymin ymax) / 2.0 / img_height width (xmax - xmin) / img_width height (ymax - ymin) / img_height # 强制裁剪到[0,1]区间解决因标注误差导致的越界 x_center max(0.0, min(1.0, x_center)) y_center max(0.0, min(1.0, y_center)) width max(0.001, min(1.0, width)) # 宽度不能为0 height max(0.001, min(1.0, height)) yolo_line f{cls_map[cls_name]} {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}\n yolo_lines.append(yolo_line) # 写入YOLO格式文件 txt_path Path(output_dir) / f{Path(xml_path).stem}.txt with open(txt_path, w) as f: f.writelines(yolo_lines) # 批量处理 for xml_file in Path(original_data/annotations).glob(*.xml): # 需要先获取对应图片尺寸此处简化为固定1280x720实际应读取图片 convert_xml_to_yolo(xml_file, 1280, 720, datasets/traffic_signs/labels/train)参数说明max(0.001, min(1.0, width))是血泪经验——YOLOv5训练时若遇到width0会在loss.py中触发ZeroDivisionError且错误堆栈极深最终定位到这行需耗2小时。cls_map字典必须与data/traffic_signs.yaml中names:顺序严格一致否则训练时类别全乱。2.3 划分训练/验证集按交通场景分布而非随机切分交通标志检测的难点在于场景分布不均城市道路多红绿灯、高速路多限速牌、学校区多停车让行。若用sklearn.model_selection.train_test_split随机划分会导致验证集缺乏隧道/雨天样本mAP虚高但实际失效。正确做法是按图像来源分组划分# split_by_scene.py import pandas as pd from sklearn.model_selection import GroupShuffleSplit # 假设你有scene_info.csv记录每张图场景属性 # columns: filename, scene_type, weather, lighting df pd.read_csv(scene_info.csv) # 按scene_type分组确保train/val各包含城市/高速/学校场景 gss GroupShuffleSplit(n_splits1, train_size0.8, random_state42) train_idx, val_idx next(gss.split(df, groupsdf[scene_type])) train_df df.iloc[train_idx] val_df df.iloc[val_idx] # 生成train.txt和val.txt绝对路径 with open(datasets/traffic_signs/train.txt, w) as f: for _, row in train_df.iterrows(): f.write(fdatasets/traffic_signs/images/train/{row[filename]}\n) with open(datasets/traffic_signs/val.txt, w) as f: for _, row in val_df.iterrows(): f.write(fdatasets/traffic_signs/images/val/{row[filename]}\n)注意GroupShuffleSplit的groupsdf[scene_type]参数强制保证每个场景类型在train/val中都存在避免模型只见过晴天样本却要检测雨天图像。这是交通类检测区别于通用检测的核心操作。3. 模型配置与训练为什么默认yolov5s.yaml在交通标志上必然失败YOLOv5官方模型yolov5s.yaml为COCO通用目标设计其anchor尺寸最小10×13对交通标志完全失配中国国标圆形禁令标志直径约30~50像素矩形指示牌宽高比常达1:5。直接训练会导致大量漏检。必须重聚类anchor并调整网络深度。3.1 交通标志专用anchor重聚类用k-means生成适配小目标的先验框YOLOv5默认anchor基于COCO数据集统计而交通标志尺寸集中在20×20至80×120区间。需用你的数据集重新聚类# 在datasets/traffic_signs/目录下执行 python tools/cluster_anchors.py \ --dataset-path datasets/traffic_signs/ \ --n-clusters 9 \ --img-size 640 \ --output-dir datasets/traffic_signs/该脚本会输出kmeans_anchors.txt内容类似# 9 anchors for traffic signs (640x640 input) 12,15, 18,22, 25,30, 32,45, 42,60, 55,80, 70,100, 85,130, 100,160逻辑说明cluster_anchors.py遍历所有.txt标签将width*height归一化后的宽高比映射到640×640输入尺寸再用k-means聚类。--n-clusters 9对应YOLOv5的3个检测头每个头3个anchor。切勿直接复制网上流传的“交通标志anchor”——你的数据集光照/分辨率不同anchor必须实测。3.2 修改yolov5s.yaml适配小目标检测的3处硬编码参数打开models/yolov5s.yaml修改以下位置原文件中搜索关键词定位# --- 修改前 --- nc: 80 # number of classes depth_multiple: 0.33 # model depth multiple width_multiple: 0.50 # layer channel multiple anchors: - [10,13, 16,30, 33,23] # P3/8 - [30,61, 62,45, 59,119] # P4/16 - [116,90, 156,198, 373,326] # P5/32 # --- 修改后 --- nc: 5 # 交通标志实际类别数speedlimit/stop/yield/priority/noentry depth_multiple: 0.33 # 保持不变小模型够用 width_multiple: 0.50 # 保持不变 anchors: - [12,15, 18,22, 25,30] # P3/8专为小标志40px设计 - [32,45, 42,60, 55,80] # P4/16中等尺寸40-80px - [70,100, 85,130, 100,160] # P5/32大尺寸80px参数说明nc: 5必须与data/traffic_signs.yaml中nc: 5及names: [speedlimit, stop, ...]长度一致否则训练时报IndexError: index 5 is out of bounds。anchors三行分别对应P3/P4/P5特征图P3负责检测小目标如远处限速牌其anchor必须小于40像素——这就是为什么要把第一行改成[12,15, 18,22, 25,30]。3.3 训练命令与超参数调优避开学习率和batch_size的两个坑使用修改后的配置训练python train.py \ --img 640 \ --batch 16 \ --epochs 100 \ --data data/traffic_signs.yaml \ --cfg models/yolov5s_traffic.yaml \ # 保存为新文件避免覆盖原版 --weights yolov5s.pt \ --name traffic_signs_v1 \ --cache # 启用缓存加速IO避坑重点--batch 16若GPU显存8GB必须降至--batch 8否则OOM。YOLOv5的--batch是总batch size非每卡多卡需用--device 0,1。--cache交通标志数据集通常2GB启用内存缓存可提速40%但若内存16GB则禁用否则系统卡死。学习率默认lr00.01对交通标志过大易震荡。在train.py中找到hyp[lr0] 0.01改为0.005或添加--hyp data/hyp.traffic.yaml自定义超参文件。4. 避坑指南交通标志检测项目中90%人踩过的5个具体问题4.1 现象训练loss下降但验证mAP始终为0原因data/traffic_signs.yaml中train:和val:路径写成相对路径如train: images/train但YOLOv5实际要求绝对路径或相对于data/目录的路径。若data/目录不在Python工作目录下val路径失效验证集读取为空。解决在data/traffic_signs.yaml中写绝对路径train: ../datasets/traffic_signs/images/train val: ../datasets/traffic_signs/images/val4.2 现象检测框全部偏右上角且置信度0.9原因标签文件中center_x和center_y计算错误常见于用xmax-xmin当宽度却忘了除以图像宽高。例如图像宽1280xmax1000,xmin900宽度应为100/12800.078若误算为100则center_x950/1009.51.0YOLOv5自动截断为1.0所有框被拉到右上角。解决用脚本检查标签for txt in Path(datasets/traffic_signs/labels/train).glob(*.txt): with open(txt) as f: for i, line in enumerate(f): parts list(map(float, line.strip().split())) if parts[1] 1.0 or parts[2] 1.0 or parts[3] 1.0 or parts[4] 1.0: print(fERROR in {txt}: line {i} has coord 1.0)4.3 现象训练中途报错RuntimeError: CUDA error: device-side assert triggered原因类别ID超出nc定义范围。例如nc5但标签中出现class_id6可能因GTSRB原始ID映射错误。CUDA断言在GPU层触发错误堆栈不显示具体行号。解决检查所有.txt文件grep -r 6 datasets/traffic_signs/labels/ # 查找class_id6 sed -i s/ 6 / 0 /g datasets/traffic_signs/labels/train/*.txt # 替换为有效ID4.4 现象检测结果框颜色全为黑色无法分辨类别原因detect.py中plot_one_box()函数的color参数未根据类别ID索引。YOLOv5默认用colors [[random.randint(0, 255) for _ in range(3)] for _ in range(100)]但若nc5colors[5]越界返回NoneOpenCV绘图失败。解决在detect.py开头添加# 强制生成5类颜色 colors [[255,0,0], [0,255,0], [0,0,255], [255,255,0], [255,0,255]] # 红绿蓝黄紫4.5 现象模型在测试集上mAP0.565%但用手机拍的实时视频检测率30%原因训练数据全是静态截图缺乏运动模糊、镜头畸变、低帧率抖动等视频特有噪声。YOLOv5的--augment参数仅开启Mosaic和HSV增强对运动模糊无效。解决在train.py中if augment:块内添加运动模糊if augment and random.random() 0.3: # 30%概率 kernel_size random.choice([3,5,7]) kernel np.zeros((kernel_size, kernel_size)) kernel[int((kernel_size-1)/2), :] np.ones(kernel_size) kernel kernel / kernel_size img cv2.filter2D(img, -1, kernel)5. 模型部署与效果验证用真实交通视频检验是否真能落地训练完成的模型在runs/train/traffic_signs_v1/weights/best.pt但best.pt只是验证集最优未必适合真实场景。必须用独立测试集非train/val验证并做视频级后处理。5.1 构建交通场景专用测试集覆盖6类典型失效场景不要用训练集切分出来的test子集必须采集真实失效场景图像每类至少20张场景类型典型问题采集建议强光反射标志表面镜面反光RGB通道饱和正午12-14点拍摄镜头直对太阳方向雨天水痕挡风玻璃水膜扭曲标志形状雨中行车记录仪视频抽帧夜间低照红外补光导致颜色失真黑夜开启车灯关闭环境光远距离小目标标志像素15×15300米外用长焦镜头拍摄密集遮挡树枝/广告牌/其他车辆部分遮挡城市主干道早晚高峰时段动态模糊车速40km/h导致运动拖影行车记录仪60fps视频降频至24fps将这些图像放入datasets/traffic_signs/images/test/运行评估python val.py \ --data data/traffic_signs.yaml \ --weights runs/train/traffic_signs_v1/weights/best.pt \ --img 640 \ --task test \ --save-json \ --name traffic_signs_test关键指标关注results_test.txt中的P,R,mAP.5,mAP.5:.95。交通标志检测中mAP.5:.95比mAP.5更重要——它要求IoU从0.5到0.95逐级达标能暴露模型对定位精度的缺陷。5.2 视频流实时检测解决帧间抖动和误检累积问题detect.py默认对每帧独立检测导致视频中同一标志在相邻帧跳变如第1帧检测到第2帧消失第3帧又出现。需加入卡尔曼滤波平滑# detect_video.py基于detect.py修改 from filterpy.kalman import KalmanFilter import numpy as np class TrafficSignTracker: def __init__(self): self.trackers {} # {track_id: KalmanFilter} self.next_id 0 def update(self, detections): # detections: [[x1,y1,x2,y2,conf,cls], ...] # 对每个检测框查找最近的trackerIoU0.3 for det in detections: x1,y1,x2,y2,conf,cls det det_center np.array([(x1x2)/2, (y1y2)/2]) matched False for tid, kf in self.trackers.items(): # 预测位置 pred kf.predict() pred_center pred[:2].flatten() if np.linalg.norm(det_center - pred_center) 50: # 50像素内匹配 kf.update(np.array([[x1],[y1],[x2],[y2]])) matched True break if not matched: # 创建新tracker kf KalmanFilter(dim_x8, dim_z4) kf.x np.array([[x1],[y1],[x2],[y2],[0],[0],[0],[0]]) kf.F np.eye(8) np.eye(8, k4) * 1 # 状态转移 self.trackers[self.next_id] kf self.next_id 1 # 在detect_video.py主循环中调用 tracker TrafficSignTracker() for frame in video_frames: results model(frame) detections non_max_suppression(results)[0].cpu().numpy() tracker.update(detections) # 绘制tracker.predict()的平滑框而非原始检测框效果对比未加跟踪时视频中限速牌在10帧内跳变3次加卡尔曼滤波后同一标志连续30帧稳定显示且框体无闪烁。这是答辩演示时最直观的加分项。5.3 模型轻量化树莓派5部署的3个必做步骤若毕设要求嵌入式部署树莓派58GB RAM可跑YOLOv5s但需优化ONNX导出比.pt快2倍python export.py --weights runs/train/traffic_signs_v1/weights/best.pt --include onnx --img 640 --batch 1ONNX Runtime推理比PyTorch快3倍import onnxruntime as ort sess ort.InferenceSession(best.onnx, providers[CPUExecutionProvider]) # 输入预处理cv2.resize→normalize→transpose→unsqueeze量化INT8体积减75%速度再20%python export.py --weights best.pt --include onnx --int8 --img 640我带过3届毕设学生最常栽在“以为跑通detect.py就是成功”。其实真正的门槛是用你自采的10段真实行车视频让模型在不调阈值的情况下对限速/停车/让行三类核心标志达到85%召回率。这需要你亲手清洗每一处坐标越界重聚每一组anchor调试每一个视频帧的跟踪参数。当答辩老师用手机拍下校园路口的实时画面模型框稳稳锁住那块“限速30”牌子时——你三个月的熬夜才真正值得。希望帮到你。本文还有配套的精品资源点击获取
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。