资讯动态

雨雪天气路面状况数据集:四类路面COCO标注与训练实战

发布时间:2026/10/18 4:58:58 来源:尧图企业网站定制
简介这份雨雪天气路面状况数据集面向自动驾驶感知、道路安全监测与计算机视觉方向的学习者和开发者用于训练和评估结冰路面、雪地、下雨湿滑、干燥路面等典型场景下的路面状态识别模型。资源共收录651个文件以646张原始jpg图像为主体另附3个json标注文件与2个txt说明文件压缩包整体约26.92MB图像均保留原始分辨率并采用COCO格式完成标注可直接对接主流目标检测与分类框架的数据加载流程。目前已有1011人学习下载具备一定的社区验证基础。借助该数据集读者能够快速构建多类别路面状态识别任务省去自行采集与标注的成本同时可结合COCO标注开展模型微调、类别平衡分析与跨天气泛化能力测试为恶劣天气下的感知算法研究提供可复用的数据支撑。1. 雨雪天气路面状况数据集从四类路面到可训练标注的落地路径做路面状况识别最怕的不是模型选不对而是数据这一环就翻车。我见过太多团队拿着公开的晴天数据集训出一个“晴天王者”一上雨雪天就集体失灵——结冰路面被识别成湿滑积雪被当成干燥误报率高到没法用。这个标题指向的正是一个专门解决这类问题的资源一个覆盖结冰、雪地、下雨湿滑、干燥四类路面的图像数据集图片采用原始分辨率标注格式为 COCO。它适合做自动驾驶感知、道路养护巡检、气象联动预警这类需要判断路面状态的从业者。核心价值在于类别定义直接对应真实决策场景原始图片保留了纹理和反光细节COCO 标注则让主流检测和分割框架能直接吃进去。接下来我会把从拿到数据到跑通第一版模型的完整路径拆开讲包括类别边界怎么定、COCO 标注怎么读、训练参数怎么设以及那些只有踩过才知道的坑。2. 四类路面标签的边界定义与选型理由2.1 为什么是这四类而不是更细的分级拿到数据集第一件事不是急着写 dataloader而是把类别定义吃透。结冰、雪地、下雨湿滑、干燥这四类看起来粗糙实际上对应的是轮胎附着系数发生阶跃变化的几个关键区间。干燥路面附着系数通常在 0.8 以上下雨湿滑会掉到 0.4 到 0.6积雪压实后大概在 0.2 到 0.3结冰则可能低于 0.1。也就是说这四类不是随便切的而是按“车辆还能不能正常制动”这个工程阈值来分的。如果你试图把“小雨湿滑”和“大雨积水”拆成两类标注一致性会立刻崩掉——同一张图A 标成湿滑B 标成积水模型学到的就是噪声。我一般建议在项目初期就锁死这四类把细分需求留给后续的回归任务比如在湿滑类里再预测一个附着系数连续值而不是硬拆标签。2.2 原始图片相比压缩图的三个实际优势标题里特意强调“图片均采用原始图片”这不是凑字数。路面状态识别极度依赖纹理和反光特征结冰路面的镜面反射、积雪的颗粒感、湿滑路面的水膜高光这些在 JPEG 高压缩后会被抹掉。我做过对比同一批图压缩到 30% 质量后结冰和湿滑的类间距离缩小了将近四成模型很容易把两者混淆。原始图片的第二个优势是保留了大动态范围。雪地场景里路面亮度和阴影区亮度差可能超过 12 档压缩图的高光溢出会直接吃掉冰面的细节。第三个优势是标注精度COCO 的多边形标注在原始分辨率下才能贴合路面区域的真实边界缩放后再标边缘误差会被放大。2.3 COCO 标注格式的字段含义与读取方式COCO 格式的核心是三个 JSON 文件annotations/instances_train.json、instances_val.json以及图片目录。每个 JSON 里有images、annotations、categories三个顶层字段。images记录文件名、宽高、idannotations记录image_id、category_id、bbox[x, y, width, height]或segmentation多边形点集categories则是类别 id 和名称的映射。下面这段代码是我常用的读取和校验脚本能一次性把类别分布和标注完整性查清楚import json from collections import Counter # 加载 COCO 标注文件路径按实际目录调整 with open(annotations/instances_train.json, r, encodingutf-8) as f: coco json.load(f) # 建立 image_id 到文件名的映射方便后续排查 img_id_to_name {img[id]: img[file_name] for img in coco[images]} # 统计每个类别的标注数量 cat_id_to_name {cat[id]: cat[name] for cat in coco[categories]} cat_counter Counter(ann[category_id] for ann in coco[annotations]) for cid, name in cat_id_to_name.items(): print(f类别 {name} (id{cid}): {cat_counter.get(cid, 0)} 个标注) # 检查是否有标注引用了不存在的图片 orphan [ann[id] for ann in coco[annotations] if ann[image_id] not in img_id_to_name] print(f孤立标注数量: {len(orphan)}) # 检查 bbox 是否越界这是原始图片标注常见的低级错误 w, h coco[images][0][width], coco[images][0][height] bad_bbox [] for ann in coco[annotations]: x, y, bw, bh ann[bbox] if x 0 or y 0 or x bw w or y bh h: bad_bbox.append(ann[id]) print(f越界 bbox 数量: {len(bad_bbox)})这段脚本的逻辑很直白先看类别是否均衡再看标注有没有指向不存在的图片最后查 bbox 有没有超出图像边界。参数上唯一需要注意的是bbox格式COCO 用的是左上角坐标加宽高不是右下角坐标转换时别搞反。如果越界 bbox 超过总量的 1%我建议直接联系数据提供方不要自己裁剪否则会引入系统性偏差。3. 用 COCO 标注跑通第一版路面分类模型3.1 环境准备与数据目录组织在动手训练之前先把目录结构固定下来。我习惯用下面这种布局后面换数据集也不用改代码dataset/ ├── annotations/ │ ├── instances_train.json │ └── instances_val.json ├── train/ │ ├── xxx.jpg │ └── ... └── val/ ├── yyy.jpg └── ...环境上PyTorch 加 torchvision 就够了不需要额外装 COCO API因为我们要做的是分类而不是检测直接读 JSON 拿类别标签即可。如果你要做分割那就得装pycocotools但第一版验证建议从分类切入收敛快、排错简单。3.2 把 COCO 标注转成分类训练用的 DatasetCOCO 标注里一张图可能对应多个标注但路面状态分类一张图只需要一个主标签。我的处理方式是取该图所有标注中面积最大的那个类别作为主标签因为路面状态通常占据画面主要区域。import os import json from PIL import Image from torch.utils.data import Dataset import torchvision.transforms as T class RoadSurfaceDataset(Dataset): def __init__(self, json_path, img_root, transformNone): with open(json_path, r, encodingutf-8) as f: coco json.load(f) self.img_root img_root self.transform transform # 建立 image_id 到主类别的映射 self.img_to_cat {} # 先按 image_id 聚合标注取面积最大的类别 from collections import defaultdict ann_by_img defaultdict(list) for ann in coco[annotations]: ann_by_img[ann[image_id]].append(ann) for img_id, anns in ann_by_img.items(): # bbox 面积 宽 * 高 main_ann max(anns, keylambda a: a[bbox][2] * a[bbox][3]) self.img_to_cat[img_id] main_ann[category_id] # 只保留有标注的图片 self.images [img for img in coco[images] if img[id] in self.img_to_cat] self.cat_id_to_name {c[id]: c[name] for c in coco[categories]} def __len__(self): return len(self.images) def __getitem__(self, idx): img_info self.images[idx] img_path os.path.join(self.img_root, img_info[file_name]) image Image.open(img_path).convert(RGB) label self.img_to_cat[img_info[id]] if self.transform: image self.transform(image) return image, label # 训练集变换随机裁剪加翻转增强雨雪场景的泛化 train_tf T.Compose([ T.Resize((512, 512)), T.RandomCrop(448), T.RandomHorizontalFlip(), T.ColorJitter(brightness0.2, contrast0.2), T.ToTensor(), T.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]) ])这里的关键参数是Resize到 512 再RandomCrop到 448目的是让模型看到不同尺度的路面纹理同时保留原始图片的细节。ColorJitter的亮度对比度扰动不要开太大否则会把结冰的高光特征抹掉我一般控制在 0.2 以内。Normalize用的是 ImageNet 统计量如果你从零训可以换成这个数据集自己的均值和方差但迁移学习场景下 ImageNet 统计量更稳。3.3 训练参数设置与首轮收敛判断第一版模型我建议用 ResNet-50 做迁移学习冻结前几层只训后面的 stage。优化器用 AdamW学习率 3e-4权重衰减 1e-4batch size 根据显存尽量拉大16 或 32 都行。训练轮次先跑 20 个 epoch观察验证集准确率和混淆矩阵。import torch import torch.nn as nn from torch.utils.data import DataLoader from torchvision.models import resnet50, ResNet50_Weights # 加载预训练权重num_classes 设为 4 对应四类路面 model resnet50(weightsResNet50_Weights.IMAGENET1K_V2) model.fc nn.Linear(model.fc.in_features, 4) # 冻结前两个 stage只训后面部分 for name, param in model.named_parameters(): if name.startswith(layer1) or name.startswith(layer2): param.requires_grad False device torch.device(cuda if torch.cuda.is_available() else cpu) model model.to(device) train_ds RoadSurfaceDataset(annotations/instances_train.json, train, train_tf) train_loader DataLoader(train_ds, batch_size16, shuffleTrue, num_workers4) criterion nn.CrossEntropyLoss() optimizer torch.optim.AdamW(filter(lambda p: p.requires_grad, model.parameters()), lr3e-4, weight_decay1e-4) for epoch in range(20): model.train() total_loss 0 for imgs, labels in train_loader: imgs, labels imgs.to(device), labels.to(device) optimizer.zero_grad() outputs model(imgs) loss criterion(outputs, labels) loss.backward() optimizer.step() total_loss loss.item() print(fEpoch {epoch1}, Loss: {total_loss/len(train_loader):.4f})首轮收敛的判断标准不是 loss 降到多低而是看验证集上结冰和湿滑这两类的混淆比例。如果这两个类互相误判超过 20%说明特征区分度不够要么加数据增强里的随机擦除要么把输入分辨率提到 640。我一般会在第 5 个 epoch 后跑一次验证如果准确率卡在 60% 以下基本就是标签有问题回去查标注一致性。4. 雨雪场景训练的避坑与排查清单4.1 类别不均衡导致模型偏向干燥路面现象训练 loss 正常下降但验证集上干燥类准确率 95%结冰类只有 40%。原因干燥路面在数据集里占比过高模型学会了“猜干燥”就能拿到大部分正确率。解决用加权采样或 focal loss把结冰和雪地的权重调高。我一般用WeightedRandomSampler权重取类别频率的倒数。4.2 原始图片尺寸过大导致显存爆炸现象dataloader 报 CUDA out of memory但 batch size 已经降到 4。原因原始图片可能达到 4000x3000直接 resize 到 512 之前PIL 解码就已经吃掉大量内存。解决在 Dataset 的__getitem__里先做一次缩略图读取用Image.open后立刻thumbnail到长边 1024再做后续变换。注意thumbnail是原地操作不会返回新对象。4.3 标注里的多边形和 bbox 混用导致解析失败现象读取 JSON 时某些 annotation 没有bbox字段只有segmentation。原因COCO 允许同一数据集里混用检测和分割标注。解决在解析时做兼容如果bbox不存在就从segmentation多边形计算外接矩形。下面这段是兼容写法def get_bbox(ann): if bbox in ann: return ann[bbox] # 从 segmentation 多边形计算外接矩形 seg ann[segmentation][0] # 取第一个多边形 xs seg[0::2] ys seg[1::2] x, y min(xs), min(ys) w, h max(xs) - x, max(ys) - y return [x, y, w, h]4.4 雨雪颗粒被误认为标注噪声现象模型在雪地场景把雪花颗粒识别成独立目标输出一堆小框。原因如果做检测任务雪花的纹理和某些小目标特征相似。解决在数据增强里加高斯模糊或者在后处理里过滤掉面积小于 32x32 的框。我一般会在训练前先可视化一批标注确认没有把雪花标成目标。4.5 验证集和训练集同场景泄漏现象验证集准确率虚高但换一批新数据就崩。原因同一段路、同一时间拍的图被随机分到了训练和验证集模型记住了场景而不是路面状态。解决按拍摄批次或路段划分确保验证集的路段在训练集里没出现过。这个坑最隐蔽但杀伤力最大。5. 从分类到分割用 COCO 多边形标注做路面区域提取第一版分类模型跑通后如果你需要精确的路面区域而不是整图标签就可以用 COCO 的多边形标注做分割。这里我分享一个具体技巧把segmentation转成二值掩码时不要直接用pycocotools的annToMask因为它默认按iscrowd处理边缘会有锯齿。我一般用 OpenCV 的fillPoly自己画抗锯齿效果更好。import numpy as np import cv2 def poly_to_mask(segmentation, height, width): # segmentation 是列表的列表每个子列表是一个多边形的点集 mask np.zeros((height, width), dtypenp.uint8) for poly in segmentation: pts np.array(poly, dtypenp.int32).reshape(-1, 2) cv2.fillPoly(mask, [pts], 1) return mask参数上注意fillPoly的坐标必须是整数且顺序是 (x, y)。如果你要做多类分割就把每个类别的 mask 叠成通道维。验证分割质量的方法很简单把 mask 叠加回原图看边缘是否贴合路面的物理边界尤其是积水区和干燥区的交界线。最后一个习惯每次换数据集或改标注版本先跑一遍第 2 章那个校验脚本再看一眼类别分布。我吃过亏有一次直接开训跑到一半才发现结冰类只有 30 张图白白浪费了两天显卡时间。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑