基于YOLOv8的路面积水识别:数据集构建与工程部署实践
简介:本资源是面向智能交通与城市内涝预警场景的路面积水识别专用数据集,专为深度学习目标检测任务设计,适用于YOLO系列(v5至v10)、Faster R-CNN、SSD等主流模型训练与验证,助力初学者快速上手、研究人员开展泛化性实验。压缩包共2000个文件,含1999个YOLO格式txt标签文件(每图一标,标注积水区域坐标及类别)和1个预配置yaml文件(明确定义类别名、训练/验证/测试路径及图像尺寸),结构规范、开箱即用。资源大小281.43MB,已包含全部4524张实拍图像及其对应VOC格式XML标签,训练集、验证集与测试集划分完备,无需额外处理即可投入模型训练。目前已有1009人学习下载,配套博文详细说明数据采集来源、标注规范、类别分布及典型样本可视化,显著降低复现实验门槛,是开展道路环境感知研究与落地应用的重要基础支撑。 “这个路面是湿的还是有积水?”——就这一个问题,我在给某地市做智慧交通巡检项目时,被交警大队的负责人反复问过好几遍。肉眼一看就明白的事,让摄像头识别却没那么简单:积水的形态会随着光照、角度、路面色差变化,一个不小心就把黑色沥青裂缝当成水,或者把大片反光误判成深水区。当时市面上根本没有现成的、标注干净的路面积水识别数据集,国内外的公开数据集大多是自动驾驶场景的整体路面分割,几乎没有专门针对“目标检测级别”的积水标注。于是就有了这个项目:从零搭建一套路面积水识别数据集,并把目标检测模型跑通,直接落地到巡检视频流里。
这篇博文就把整个过程拆开讲清楚——从场景分析、数据采集、标注规范,到基于YOLOv8的训练流程、效果调优和部署踩坑,全程无保留。如果你也要做类似的城市积水监测、道路巡检、车路协同场景,或者只是想把一个冷门细分目标检测项目跑通,这篇内容值得你从头看完。
1. 路面积水识别的场景分析与数据采集方案
1.1 为什么积水识别不能靠“图像分割”或“人工巡检”硬凑
很多人看到路面积水识别,第一反应是拿语义分割模型做像素级分类,或者靠人工盯监控。这两个思路我都试过,各有各的别扭:
- 语义分割模型的标注成本极高,一张路况图要逐像素抠出水面区域,边缘处还得反复调。而且积水是半透明、带反光的,分割模型容易把湿润路面和真正的积水混在一起,结果出来之后还需要大量人工复核。
- 人工巡检就更不用说了,暴雨时段几十上百路监控,一个人盯三四个屏幕到后来眼睛都花了。到了夜间,积水区域在屏幕上的对比度极低,漏报率可以到一半以上。
目标检测方案是这两者之间的平衡点:标注成本比分割低一个量级,检测框的粒度又足够支撑实际预警需求。你不需要知道积水精确到像素的边缘,只需要知道“在哪个车道、大概多大范围”,就能驱动路侧情报板、手机App推送或者联动排水泵站。
从这个场景反推数据需求,就非常明确了:积水不是一个“均质物体”,它没有固定纹理、没有固定形状、没有固定颜色,识别它主要靠的是“路面区域的颜色突跳”和“周围环境的反射关系”。所以数据里必须覆盖足够多样的路面材质和光线环境,否则模型很容易过拟合到某一种特定的“积水长相”上。
1.2 数据采集的四个关键维度
我做的第一版数据集共采集了约12000张图像,其中带积水目标的约8600张,纯负样本约3400张。采集来源分为三块:自建车载摄像头采集、固定点位监控录像提取、以及少量从开源视频平台按合规要求下载后筛选的雨天素材。这里重点说说我在采集维度上的四个核心考量:
维度一:天气状态
雨天是最自然的积水来源,但光有雨天还不够。雨停之后路面残留的水膜、雪后融水、洒水车刚作业过的路面,都是积水的常见形态。我特意把这些“非降雨时段”的积水也纳入了数据集,避免模型只会在大雨场景下工作。
维度二:时段与光照
积水在正午顺光下是亮白色,在傍晚逆光下是暗黑色,在夜间路灯下是碎金一样的点状反光。同一个水坑,三种光环境下的特征可以说完全不同。所以我在采集时按“白天顺光”“白天逆光”“阴天散射光”“夜间路灯”“夜间无路灯”五个光照档位做了配额,每个档位尽量不少于总样本的15%。
维度三:拍摄视角与高度
车载摄像头的视角和立杆监控的视角完全不一样。车载摄像头离地约1.2米到1.5米,以俯冲视角看前下方路面;立杆监控离地6到8米,以接近垂直的大俯角覆盖一片区域。同一个水坑,在这两种视角下呈现的几何形变差异很大,模型如果只在一种视角下训练,换一个安装位置效果就会明显下降。我的做法是两者都采集,并按6:4的比例混合,保证模型对视角变化有一定容忍度。
维度四:路面材质
沥青路面、水泥路面、砖石铺装路面,这几种路面的吸水性、反光特性差异很大。沥青湿了之后是深黑色,水泥湿了之后是灰亮色,砖面湿了之后容易出现斑驳状。如果数据集里90%都是沥青路面,模型在水泥路面上几乎必挂。我最终把沥青路面控制在50%左右,水泥和砖面各占25%。
采集完成之后,还有一个非常容易被忽略的步骤:数据清洗。我砍掉了三类图像:严重运动模糊帧、摄像头被雨滴完全遮挡的帧、以及积水占比小于1%的远景帧。这类图像即使标了标注,对模型训练也没有正向帮助,反而会增加收敛难度。
2. 数据集标注规范与标签体系设计
2.1 标注类别的“少即是多”原则
第一次设计标签体系时,我其实想过拆多个类:大面积积水、小面积水洼、湿滑反光区域。实际标了500张后我发现,这个拆分方法行不通——标注员在判断“多大算大面积”时会极度主观,同一个水坑,上午标成“大面积”,下午标成“小面积”,标签噪声大到直接干扰模型收敛。
最后我把标签体系收敛成了两类:water_accumulation(积水)和wet_road(湿滑路面)。积水的定义是:有明显的水面边界,且能清晰看到周围物体在水中的倒影或水面自身的反光纹理。湿滑路面则是:路面颜色明显变深但还没有形成连续水面,或者只是水膜状态。这两个类别在视觉特征上有明显差异,标注员容易达成一致。
这里有个经验之谈:如果你的项目只有“要不要预警”这一个诉求,甚至可以只保留一个water_accumulation类别,把wet_road全部归入背景。多一个类别就多一份标注成本和训练难度,尤其对冷启动阶段的数据集,宁缺毋滥。
2.2 标注框的边界怎么打才“算对”
目标检测的标注看似简单——拉个矩形框就行。但积水不是人、车这类刚性物体,它的边界天然就是模糊的,这就需要在标注规范里写清楚边界条件。
我在标注规范中定了四条硬性规则:
- 标注框必须紧贴积水的视觉边界,允许包含少量周边湿润路面,但不允许为了省事把一大片干路面包进框内。
- 如果一片积水被路灯杆、护栏等物体遮挡,按被遮挡后的可见部分标注,不要凭想象补全遮挡区域。
- 如果连续水面被道路标线分割开,但水面在视觉上仍然连通(例如标线已经磨损),按一个目标标注;如果标线清晰且水面两侧的视觉反射不连贯,则标成两个目标。
- 夜间图像中,如果水面反光强烈导致边界无法确认,标注员有权放弃该目标并记录为“不可标”,强行标注只会给模型喂噪声。
这套规则看起来简单,实际执行时我花了很大的精力盯标注员的产出。每批标注回来我都会抽样复核,发现边界超出实际水域超过15%的框,直接退回重标。数据集的标注质量,决定了你后续所有算法工作的上限,这个环节省下来的时间,都会在模型调优阶段加倍还回去。
2.3 数据集规模与划分策略
最终版本的数据集构成大致如下:
| 项目 | 数值 |
|---|---|
| 总图像数 | 12000张 |
| 含积水目标的图像 | 8600张 |
| 纯负样本(无积水) | 3400张 |
| 标注目标总数 | 约23000个 |
| 单图最多目标数 | 12个 |
| 类别数 | 2类(water_accumulation, wet_road) |
划分比例上,我用了火车:验证:测试 = 7:2:1。值得强调的是,划分时必须按“图像来源”而不是按“图像文件”随机分。如果同一个视频里连续抽帧的图像同时出现在训练集和测试集,验证结果会虚高很多,因为模型见过同一场景的“前几帧”了。我按采集批次和视频源做分组,保证同一个场景的视频帧只会出现在一个集合里。
3. 基于YOLOv8的训练实操与调优记录
3.1 为什么选YOLOv8而不是其他目标检测框架
积水识别属于典型的端侧/边缘侧实时检测需求,框架选型时我对比过几个主流方案:
- Faster R-CNN:精度确实高,但推理速度在嵌入式设备上撑不住,单帧推理动不动100毫秒往上,直接排除。
- SSD:速度尚可,但小目标检测能力偏弱,而远处的小面积积水恰恰是巡检场景里最常见的漏报目标。
- YOLOv5:成熟稳定,社区资源多,但YOLOv8在训练收敛速度和最终精度上都有小幅优势,而且Ultralytics的API设计对数据工程更友好。
- RT-DETR:精度不错,但部署生态还不够成熟,文档也相对少,投入产出比不合适。
最终选了YOLOv8,原因很朴素:它在精度、速度和部署生态之间取得了最好的平衡。对我这种偏应用的工程人员来说,能快速跑通、好调参、好部署,比“理论上限最高”重要得多。
3.2 环境准备与目录结构
GPU环境是Ultralytics官方仓库,直接用pip安装即可:
pip install ultralytics如果是Linux服务器,建议配合conda建一个独立环境:
conda create -n yolov8 python=3.10 -y conda activate yolov8 pip install ultralytics然后准备标准的数据集目录结构,YOLOv8用的是YOLO格式的txt标注,每一行是“类别ID 中心点x 中心点y 框宽 框高”,坐标值归一化到0到1。目录结构如下:
datasets/ ├── water_dataset/ │ ├── images/ │ │ ├── train/ │ │ └── val/ │ └── labels/ │ ├── train/ │ └── val/如果你的原始标注是LabelImg输出的VOC XML格式,需要写脚本转换。这个转换步骤我反而建议做好留档,后续加数据的时候可以自动走流程。转换脚本的核心逻辑是把XML里的xmin, ymin, xmax, ymax读出来,除以图像宽高做归一化,再写入txt文件。网上这类脚本一抓一大把,但要注意图像宽高必须从图像文件本身读取,不能从XML里拿,否则某些标注工具写错尺寸信息时你会被带偏。
3.3 配置文件与训练命令详解
数据集准备完成后,写一个数据配置文件,YOLOv8用YAML格式描述数据集路径和类别信息:
# water_data.yaml path: /path/to/water_dataset train: images/train val: images/val nc: 2 names: 0: water_accumulation 1: wet_road训练命令如下:
yolo detect train \ model=yolov8s.pt \ data=water_data.yaml \ epochs=100 \ imgsz=1280 \ batch=16 \ lr0=0.01 \ patience=15 \ project=runs \ name=water_exp1几个关键参数我单独说明一下:
imgsz=1280是这组实验里我觉得最重要的一个选择。YOLOv8默认的推理尺寸是640,但路面积水在画面里往往只占一小块,尤其是立杆监控视角下,一个水坑可能只有40×40像素。在640分辨率下训练,这类小目标很容易被下采样到特征图上的一个点,根本学不到有效特征。把输入分辨率提到1280之后,mAP50有肉眼可见的提升,代价是显存占用增加。如果你显卡显存不够,可以退一步用960,但尽量别低于800。
batch=16按你显卡的显存来定。我用的是一张RTX 4090,16的batch跑1280分辨率没有压力,如果你的卡是24G以下的,建议降到8,或者用batch=-1让YOLOv8自动检测最佳batch大小。
patience=15是早停参数,意思是连续15个epoch验证集指标没有提升就自动停止。这个参数我建议保留,因为训练到后期损失曲线会进入平台期,继续硬跑纯属浪费GPU时间。
多尺度训练参数我开了,但只在最后50个epoch才开。YOLOv8默认在训练中会做一定程度的尺度扰动,但对小目标数据集来说,过强的多尺度反而会降低模型对小尺寸积水的响应能力。所以我前50个epoch关闭多尺度,让模型充分学习原始尺度下的特征,最后50个epoch再开,增强泛化能力。
3.4 训练过程监控与结果判定
训练启动后,不要只盯着loss曲线看。loss下降不代表模型好用,必须同时关注验证集上的mAP50、mAP50-95、precision和recall这四个指标。
我这轮实验的最终结果大致如下:
| 指标 | 数值 |
|---|---|
| Precision | 0.87 |
| Recall | 0.82 |
| mAP50 | 0.88 |
| mAP50-95 | 0.61 |
说实话,mAP50-95的0.61并不算特别高,但这个指标对积水这种弱纹理目标本来就不友好,因为预测框和真实框的IoU稍微偏差0.05,mAP50-95就会往下掉。实际使用中我更看重mAP50和recall,因为巡检场景漏掉一个积水点的代价比多报几个误检大得多。
训练完之后,模型权重会保存在runs/water_exp1/weights/目录下,里面有两个文件:best.pt和last.pt。best.pt是验证集指标最优权重,last.pt是最后一个epoch的权重。默认用best.pt。
4. 数据清洗、增强与模型鲁棒性提升
4.1 训练集里的“脏数据”怎么筛
这一步是我整个项目里踩坑最多的地方。YOLOv8本身有内置的自动清洗机制,但强度远远不够,尤其是对积水这类边界模糊的目标,部分标注框的质量差到足以拖累模型。
我先做了一轮半自动清洗:模型训练完第一版后,把所有训练集图像重新推理一遍,找出置信度很高但标注框对不上的样本。这类样本通常是两种情况:一种是标注时把两个相邻水坑框成了一个目标,另一种是模型看到了标注员遗漏的积水目标。对前者,修正标注;对后者,反而补充了训练集的覆盖面。
第二轮清洗是纯人工抽查,我从每个采集批次里随机抽5%的图像,用图像可视化脚本把标注框画出来,逐张看。这个工作量大概用了两天时间,但收益巨大。我清掉了约300张标注质量不达标的图像,替换成了同等数量的新标注图像。
这里想提醒大家一个细节:很多数据集工具会自动跳过没有标注对象的图像,这在训练时没问题,但你在划分训练集和验证集时,一定要刻意保留一部分“纯负样本”。否则模型的误检率会失控,因为它在训练中从未见过“没有积水但看起来像积水”的场景。
4.2 针对积水场景的专属增强策略
YOLOv8自带Mosaic、随机翻转、色域扭曲等增强手段,但这些通用增强对积水识别还不够,我额外加了三组实验对比:
第一组:亮度扰动增强。积水目标对光照特别敏感,所以我用程序把每张图像的亮度随机调暗20%到调亮20%,模拟从清晨到正午再到黄昏的光线变化。加了这组增强之后,模型在夜间低照度场景下的recall提升了大约3个百分点。
第二组:高斯模糊增强。模拟雨滴在镜头上造成的散焦效果,对每张图像随机叠加小半径高斯模糊。这组增强的作用是让模型不要过度依赖图像的锐利边缘特征,因为下雨天摄像头镜头上的雨滴会让画面整体发糊。
第三组:随机透视变换。轻度模拟摄像头安装角度变化。这个增强要慎用,强度过大会产生畸变,反而干扰模型的几何特征学习。我的经验是变换角度控制在正负5度以内。
增强不是越多越好。每一项增强都相当于人为给模型增加了训练数据的“难度”,但如果增强后的样本失真严重,模型反而会把不存在的特征模式学进去。我的建议是每次只加一种增强,在验证集上观察指标变化,有提升就保留,没有就回滚。
5. 推理部署与实战问题排查
5.1 从单张检测到连续视频流的工程改造
模型训练好之后,真正的挑战才开始。训练时的输入是静态图像,而实际部署面对的是连续的视频流,中间隔着一大堆工程问题。
视频流推理最常见的坑是“单帧检测抖得厉害”。同一个水坑,上一帧检测到了,下一帧漏掉,再下一帧又出现。直接把检测结果推给应用层,甲方大概率会认为你的系统抽风了。我的解决方案是加了一个“时序投票缓冲”:
维护一个长度为5的滑动窗口 每一帧检测结果进入窗口 只有当一个目标在窗口内至少出现3帧,才对外输出“确认”这个策略显著降低了误报和抖动,代价是引入约2帧的延迟,对积水预警场景完全可接受。
由于项目部署在Jetson Orin NX这类边缘设备上,我导出模型时用了TensorRT:
yolo export model=best.pt format=engine device=0TensorRT引擎的推理速度明显优于原始PyTorch模型的,FP16精度下延迟大约在10毫秒以内。这个优化对需要同时跑路况识别、车牌识别等多个模型的边缘盒子来说,省出来的算力非常可观。
5.2 常见误检与漏检案例复盘
我在实景测试中整理了三个最典型的失败案例,花了很大力气逐步排查。
案例一:井盖和深色沥青被误报为积水。这是误检里最常见的类型。深色圆形井盖、补过路面的深色沥青块,在画面中和积水的颜色分布高度相似。排查发现模型主要靠颜色特征判断,对纹理和反射信息不敏感。思路上我试过给训练集增加更多“深色负样本”,同时把wet_road类别从检测目标中暂时移除,强迫模型聚焦在真正的水面反射特征上。经过这两项处理后,误检率大约下降了20%。
案例二:夜间路灯反光导致大面积漏检。夜间积水在路灯下呈现破碎的、点状高光,整个水面区域看起来是暗色背景上撒了一把碎光。模型在白天数据上训练出来的特征,到夜间几乎完全失效。这个问题的根源是训练集中夜间样本占比不足。我专门补了一批夜间采集的数据,把夜间占比提到20%左右,并调整了增强策略,使夜间场景的recall明显回升。
案例三:隔音屏和玻璃幕墙反射导致误检。道路两侧的隔音屏、高架旁边写字楼的玻璃幕墙,在雨天会产生大面积的条状反光区域,模型经常把这一类反光当成积水。这类误检很难完全消除,因为反光的视觉特征和积水确实有很大重叠。我的处理是在应用层加了一个简单的“ROI区域约束”,在固定点位监控场景下,提前标定出道路区域,把检测范围限制在路面范围内,直接从根源上规避了隔音屏区域的反光干扰。
5.3 模型精度和速度的平衡策略
部署完成后,我还做了一轮精度和速度的调优测试,核心目标是在不显著掉点的情况下把推理帧率提上去。
- 输入分辨率从1280降到960,mAP50约下降1.5个百分点,帧率提升约30%。对于近景车载摄像头,960已经够用。
- 推理时的置信度阈值从默认的0.25调到0.35,误检明显下降,召回略有损失。如果场景更看重召回,可以反向调到0.15。
- 用了NMS的
agnostic模式,因为积水目标之间经常紧挨着,类别间的NMS竞争会导致部分相邻目标被吞并。
最终部署参数为:imgsz=960、conf=0.3、iou=0.5,在Jetson Orin NX上实测稳定跑32FPS,满足实时性要求。
6. 数据集扩展的方向与实际建议
6.1 从目标检测向积水深度估计延伸
当前数据集只支持“有没有积水”的二值检测。但我实际对接的几家客户里,有好几个都问到了“积水有多深”的问题。这是从目标检测到更复杂视觉任务的自然延伸。
如果你也想做深度估计,建议在标注阶段升级:给每个积水目标增加一个depth等级字段,按“无积水、浅水膜、水深<5cm、水深5cm-15cm、水深>15cm”分档,然后用YOLOv8的分类头改造或者单独训练一个分类模型。但这里有个现实困难:这个标注需要现场实测水深才能标准,工作量不是一般的大。
我的建议是:如果预算有限,先不要碰深度估计,优先把数据集的地理位置多样性做得更足。同一个城市不同路段的积水形态都有差异,更别提不同地区的排水系统、道路材质和气候条件下积水的视觉特征差异了。
6.2 开放数据集的合规发布准备
数据集做到后期,很多人会考虑开放出来共享,但这里面有几个坑要提前规避:
- 数据版权:如果是自己采集的图像,问题不大;但如果混入了视频监控平台的录像,一定要确认授权协议,否则发布就是侵权风险。
- 隐私问题:车载摄像头采集的街景图像里必然包含车牌和行人,发布前要用检测模型给这类敏感信息做脱敏处理,或者直接裁剪掉包含清晰人脸和完整车牌的区域。
- 标注质量:开源数据集一旦发出去,标注质量就会接受全行业的检验。建议发布前引入至少两位独立的复核员对全部标注做一致性检查,提前识别出有歧义的标注框。
我目前还在整理这批数据集的清洗和授权文档,后续如果真开放出来,会在合规资料上下游再仔细一遍。
6.3 几个现在就能上手的扩展思路
除了深度估计和开放发布,还有三个方向是我觉得短期投入产出比最高的:
第一,把雨天场景进一步细分,例如区分“小雨微湿”“中雨积水”“暴雨内涝”三个等级,训练一个级联分类器。这样系统不仅能告诉你哪里有水,还能告诉你现在的积水风险等级。
第二,引入多视角信息。一个水坑同时被相邻两个摄像头拍到,可以做一个简单的交叉验证,如果一个摄像头检测到而另一个没有,系统自动降低该检测的置信度,降低单视角遮挡带来的误检。
第三,把检测结果和气象数据打通。结合当地气象台的实时降雨量和道路排水能力数据,建立简单的积水预测模型,从“发现积水”升级到“预测积水”。这一步做出来,项目的技术含量和客户价值就完全不一样了。
我做这个数据集和模型最大的体会是:一个看似简单的视觉任务,真正落地时要翻越的山丘比想象中多得多。数据分布、标注规范、训练资源配置、部署环境适配,每一环都可能成为短板。好在这些问题都是已经被解决过的“常规困难”,只要你愿意沉下心一关一关过,路面积水识别这个项目是完全可复现、可交付的。
最后说个最实在的小技巧:无论你的数据集多大、模型多先进,先跑通一个最小的可用版本,哪怕只用了300张图片、训练20个epoch,先让整个链路跑起来,再去补数据、调参数。工程项目的推进节奏,永远比模型的绝对精度更影响成败。这个道理放在路面积水识别上适用,放在任何目标检测项目上也都适用。
本文还有配套的精品资源,点击获取
