资讯详情

恶劣天气道路目标检测:YOLO标注数据使用与避坑指南

📅 2026/10/19 10:28:51 | 华诺云谱 👁 阅读
恶劣天气道路目标检测:YOLO标注数据使用与避坑指南
简介面向计算机视觉目标检测研究者和开发者提供一份恶劣天气下道路行人车辆的已标注数据集。数据覆盖雨雪、雾天、沙尘等复杂场景共约1000张图像包含car、bus、person、truck等7类目标并已按YOLO格式完成标注同时划分好训练集与测试集附带show.py脚本便于可视化检查标注效果。压缩包内共2000个文件包括1027个txt标注文件、972个jpg图像文件及1个Python可视化脚本整体大小约133.76MB目录结构清晰可直接用于模型训练和验证。目前已有157人学习下载适合行人车辆检测、恶劣天气感知及YOLOv5改进实战等方向的研究与实验结合作者相关专栏可进一步开展算法优化与结果对比。1. 恶劣天气下的道路目标检测这份 YOLO 标注数据到底怎么用做目标检测的人最怕两件事一是数据集干净得像实验室训练出来一上真实场景就翻车二是公开数据集全是晴天白天模型根本没见过雨雪沙尘。这份恶劣天气下的道路行人、车辆图像检测数据约 1000 张已经按 YOLO 格式做好标注类别覆盖 car、bus、person、truck 等 7 类训练集和测试集都已划分好还带一个 show 脚本可以直接可视化标注结果。适合正在做 YOLOv5/YOLOv8 实战、自动驾驶感知、或者在恶劣天气场景下做模型鲁棒性验证的人省去从零采集和标注的重复劳动直接进入算法调试环节。2. 数据组织与标注格式先读懂目录和 classes 文件再动手2.1 文件命名暗含天气场景标签把数据解压之后第一件事不是急着跑训练而是把文件名扫一遍。这批数据的文件名直接暴露了场景类型比如snow_storm-347.jpg、mist-101.jpg、sand_storm-184.jpg、foggy-040.jpg、sand_storm_g2-992.jpg一眼就能看出覆盖了雪天、雾天、沙尘天、阴霾天。g2可能表示第二批采集或第二个子场景这个在训练时可以作为 group 维度去分析后面我会讲怎么利用这个信息。文件名的前缀不仅是命名习惯它其实给了你一个免费的 domain label。很多人在恶劣天气检测上翻车原因不是模型结构不行而是训练集和测试集的天气分布不一致。如果你要做域泛化分析完全可以依据文件名把数据重新划分成「已知天气」和「未知天气」两组来做验证。2.2 目录结构与划分方式约 1000 张图的数据量不大但胜在已经做了训练集和测试集划分。通常这类资源的目录结构是dataset/ ├── images/ │ ├── train/ # 训练图片约 800 张 │ └── test/ # 测试图片约 200 张 ├── labels/ │ ├── train/ # 对应训练图片的 YOLO 标注 txt │ └── test/ # 对应测试图片的 YOLO 标注 txt ├── classes.txt # 类别名列表一行一个类 └── show.py # 可视化标注的脚本拿到手后建议先用tree命令确认实际结构因为有些打包方式会把images和labels合并到一个目录或者把划分写在一个 txt 清单里。如果结构和我上面写的不完全一样不影响使用核心只有三点图片和标签文件名必须一一对应、classes.txt必须存在且顺序正确、train和test的划分是明确的。2.3 读 classes 文件并统计各类别样本量类别一共 7 个除了 car、bus、person、truck 之外还有哪些以classes.txt为准。拿到数据后我一般会先跑一个统计脚本确认每个类别的实例数量这直接关系到后面训练时要不要做类别重加权import os from collections import Counter labels_dir labels/train class_counts Counter() for fname in os.listdir(labels_dir): if not fname.endswith(.txt): continue with open(os.path.join(labels_dir, fname), r) as f: for line in f: cls_id line.strip().split()[0] class_counts[int(cls_id)] 1 # 读取 classes.txt 映射类别 ID 到名称 with open(classes.txt, r) as f: names [line.strip() for line in f.readlines()] for cls_id, cnt in sorted(class_counts.items()): print(f类别 {cls_id} ({names[cls_id]}): {cnt} 个标注实例)这段脚本做的事情很简单遍历训练标签目录里的每个 txt读到每一行标注把第一个字段当作类别 ID 统计出现次数。YOLO 格式的标注每行固定是class_id x_center y_center width height前 5 个字段以空格分隔其中坐标是相对图片宽高的归一化值算实例数时不需要解析坐标只看第一个字段即可。如果你的业务场景对某个类别特别敏感比如卡车翻车事故检测而统计结果显示 truck 只有寥寥几十个实例那训练时就要考虑对 truck 类别提高 loss 权重或者在数据增强时做针对性复制。这一步在动手训练前花五分钟做完后面能省好几轮试错。2.4 YOLO 标注格式的坐标陷阱YOLO 格式的标注是归一化的中心点坐标和宽高即(cx, cy, w, h)都除以了图片的宽和高。这里最常见的坑是有人拿到标注后想转成 COCO 或 VOC 格式做对比实验结果忘了反归一化直接把 0.5 当像素坐标画框画出来全挤在图片左上角。转换时要注意一个细节cx, cy是中心点坐标转 VOC 格式时需要换算成xmin cx - w/2ymin cy - h/2而 COCO 格式恰好相反存的是左上角坐标和宽高。我曾经帮人调一个模型精度一直上不去排查到最后发现是标注转换时把xmin算成了cx - w框全部偏移了半个框位。这类错误在数据资源里不会出现但你自己二次加工时最容易踩。3. 可视化验证与数据划分训练前必须做的一次「体检」3.1 用 show 脚本确认标注质量数据集自带的show.py用来可视化标注它的核心逻辑一般是用 OpenCV 读取图片和对应 txt画出边界框后输出到output/目录。但我不建议直接跑完就完事会先把脚本读一遍确认它用的是cv2.rectangle还是PIL.ImageDraw画框颜色是否区分了类别坐标有没有做反归一化。如果 show 脚本缺失或输出异常下面这段代码可以替代逻辑同样适用import cv2 import os image_dir images/train label_dir labels/train output_dir visualize/train os.makedirs(output_dir, exist_okTrue) with open(classes.txt, r) as f: class_names [line.strip() for line in f.readlines()] colors [(0, 255, 0), (0, 0, 255), (255, 0, 0), (0, 255, 255), (255, 255, 0), (255, 0, 255), (128, 128, 0)] for img_name in os.listdir(image_dir): if not img_name.endswith(.jpg): continue img_path os.path.join(image_dir, img_name) label_path os.path.join(label_dir, img_name.replace(.jpg, .txt)) img cv2.imread(img_path) h, w img.shape[:2] if not os.path.exists(label_path): print(f警告: {img_name} 没有对应标签) continue with open(label_path, r) as f: for line in f.readlines(): parts line.strip().split() if len(parts) 5: continue cls_id int(parts[0]) cx, cy, bw, bh map(float, parts[1:5]) x1 int((cx - bw / 2) * w) y1 int((cy - bh / 2) * h) x2 int((cx bw / 2) * w) y2 int((cy bh / 2) * h) cv2.rectangle(img, (x1, y1), (x2, y2), colors[cls_id % len(colors)], 2) cv2.putText(img, class_names[cls_id], (x1, max(0, y1 - 5)), cv2.FONT_HERSHEY_SIMPLEX, 0.5, colors[cls_id % len(colors)], 1) cv2.imwrite(os.path.join(output_dir, img_name), img) print(f已保存 {img_name} 的可视化结果)这段代码把 YOLO 归一化坐标还原成像素坐标核心公式是x1 (cx - bw/2) * w先还原中心点坐标再按宽高的一半向左上角偏移得到左上角点。之所以强调这个公式是因为它用到了两套坐标系如果忘记乘宽高所有框都会缩在 1×1 的像素范围内看起来就像标注全部失效。跑完可视化后抽样检查三件事大目标比如 bus 和 truck框是否完整覆盖车身而不是只框了一半person 在雾天和沙尘环境下是否漏标边界框是否超出图片边缘。恶劣天气下的人车目标对比度低标注时漏标和框偏移的概率比晴天数据高不少这笔账先算清楚免得训练完才发现测试集标注本身就带病。3.2 训练集和测试集的分布一致性检验资源已经做了 train/test 划分但「划分」不等于「合理」。我会做两个检查一是确认测试集里有没有和训练集重复的文件二是统计两张划分下各类别的实例占比是否接近。import os train_imgs set(os.listdir(images/train)) test_imgs set(os.listdir(images/test)) duplicates train_imgs test_imgs print(f重复图片数量: {len(duplicates)}) if duplicates: for name in list(duplicates)[:10]: print(f {name})第二个检查更关键。如果训练集里 car 占 60%而测试集里 car 只占 30%那模型在测试集上的 mAP 会被少数几个类别带偏训练时的 loss 曲线也会失真。跑完第 2.3 节的统计脚本后分别对 train 和 test 执行一遍对比两个分布。如果发现 bus 或 truck 在测试集里压根没出现那测试指标就说明不了这类目标在恶劣天气下的表现需要自己重新划分或补充验证集。3.3 天气维度上的划分策略文件名里的天气前缀虽然简单但可以组合出多种实验设置。如果目标只是「把模型训出来」直接用现成的划分即可如果要做学术向的鲁棒性验证可以按天气重新划分成源域snow_storm foggy mist训练、目标域sand_storm测试专门检验模型在没见过的恶劣天气上的泛化能力。这是这份数据很有价值的用法毕竟一个模型在相似天气分布上表现好不意味着换一种天气还能稳。从实践角度我通常会把原划分复制一份做扰动实验——给测试集图片加高斯噪声或模拟雨水条纹观察 mAP 掉多少。这类韧性实验在自动驾驶感知上经常要做这份数据覆盖了多种天气刚好可以串起来做。4. 避坑指南恶劣天气检测数据集的典型问题和排查方法4.1 现象训练 loss 降得很快但 mAP 极低这类问题在目标检测里非常典型很多人上来就以为是模型结构有问题调了半天网络最后发现是数据配置的问题。我在第一次拿到这类带恶劣天气的数据集时也犯过同样的错当时是一份自动驾驶相关数据loss 从 8 掉到 2 只用了 10 个 epoch开心得不行结果验证集 mAP 不到 5%人直接傻了。原因排查之后发现classes.txt的类别顺序和我写进数据配置文件里的顺序不一致。数据集里的person在classes.txt的第 3 行我却在加载时把它写成了第 0 个类等价于把原本标注为person的框全部当成car来训练。模型学到了错误的映射关系训练 loss 当然可以降因为它在自洽地学习「把那个形状的东西识别成 car」但这和真实目标完全对不上。解决方法很简单数据加载时用classes.txt动态生成类别映射不要手写死列表硬编码类别顺序是绝对不要做的事情。用 YOLOv5 时在 data yaml 里直接把 classes 列表和classes.txt比对一遍再跑可视化确认框的类别标签是否正确。4.2 现象沙尘天和雾天的漏检率明显偏高低对比度、边缘模糊、目标与背景融为一体这是恶劣天气下检测的核心难点模型往往在雪天表现尚可到了沙尘天就崩掉。原因不止是标注层面的漏标还有模型本身的特征提取能力在这些极端条件下失效。解决思路分两路一路在数据上做文章对雾天和沙尘天的图片做去雾增强或多尺度增强另一路在模型上做结构改进比如在 backbone 里加入注意力模块强化模型对低对比度区域的响应。对于这份数据资源最直接的做法是先统计各类天气的样本数量再决定是给沙尘天增加训练权重还是把它们单独挑出来做二次验证。4.3 现象训练集和测试集的 mAP 差了一倍这是训练数据分布和测试数据分布不一致的经典症状。具体到这份数据上我遇到过的情况是训练集以foggy和mist为主测试集却混了不少snow_storm的图。模型在训练过程中已经「过拟合」到了雾天的对比度和亮度分布换一种天气自然掉点。解决方法是回到第 3.3 节的思路重新审视划分逻辑。如果测试集的天气类型在训练集里完全不存在那模型跨天气泛化的失败是预期内的不叫 bug。如果混合了多种天气就按天气名做分层抽样确保每种天气在训练集和测试集里的占比一致用 pandas 先统计分布再划分避免随机抽样带来的偏差。4.4 现象标注文件里有空标签的图片所谓空标签就是一张图片对应的 txt 文件是 0 字节或者只有换行符没有任何有效行。这种情况在人工标注过程中很常见特别是雨雾天气下看不清、标不出来的时候标注员可能就丢一个空文件占位。这类空标签在训练时不会直接报错但会影响 loss 的累计逻辑——如果数据加载器直接返回空标签有的框架会把它当作背景样本处理有的会报 「IndexError」。我习惯先跑一遍全量检查把空标签对应的图片单独拎出来看看如果确实是没有可标注目标就放到测试集而不是训练集如果是有目标但漏标了就得补标不能留在训练集里影响模型对背景的判断。5. 把这份数据真正用出效果的几个进阶操作数据本身是 YOLO 格式意味着主流的 YOLOv5、YOLOv8、YOLOv11 都能直接消费不需要额外转换。下面这套是我自己验证过、在恶劣天气行人车辆检测上稳定有效的组合拳从训练到评估一次走完。先看训练侧。天气场景多样但数据量只有约 1000 张我的配置思路是用小模型跑通基线再用大模型吃数据。具体来说用 YOLOv8s 配合 640 输入尺寸做第一轮yolo detect train \ dataweather.yaml \ modelyolov8s.pt \ epochs100 \ imgsz640 \ batch16 \ workers4 \ pretrainedTrue \ mosaic1.0 \ hsv_h0.015 hsv_s0.7 hsv_v0.4 \ fliplr0.5几个关键参数的考虑pretrainedTrue是必须开的1000 张图从头训练很容易过拟合用 COCO 预训练权重做迁移学习才能学到泛化的特征表达mosaic1.0在数据量不大时是性价比最高的增强方式它能在一张图里拼 4 张图相当于扩充了样本组合空间hsv_h/s/v适当加大一点可以模拟不同天气下的色偏和亮度变化这对雾天和沙尘天的泛化有直接帮助。训练完跑测试集指标yolo detect val \ dataweather.yaml \ modelruns/detect/train/weights/best.pt \ imgsz640 \ conf0.25 \ iou0.5注意conf和iou的取值直接决定 mAP 数字分享结果时一定要注明这两个参数。比如conf0.25 iou0.5下 mAP0.5 是 78%换成conf0.5 iou0.75可能直接掉到 62%这些指标不是模型绝对能力的体现而是评估配置下的表现。最后是二次验证。我会选几张典型场景图用同一套权重跑推理对比可视化输出。对沙尘暴和雪天各选一张有密集行人的图用conf0.1看模型在低置信度下能不能召回更多目标同时观察有没有误检。这一步的意义在于确认模型「真的能在这个天气下干活」而不只是验证集上的数字好看。有一次我帮朋友调一个恶劣天气检测模型前面训练和评估都正常就是实拍视频里频繁漏检。后来发现他的测试图片是从监控视频抽帧截的分辨率只有 640×480而我训练时统一 resize 到 640×640输入尺寸变了小目标直接扛不住。从那以后我每次用天气数据集前都会强制走一遍「统计类别分布 → 可视化标注 → 验证划分 → 检查空标签 → 确认评估参数」五步下来再调模型基本没有在数据层面翻过车。这套流程对你手头这份资源同样适用先把数据吃透再让模型去发挥希望帮到你。本文还有配套的精品资源点击获取
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑