☰
道路积水检测数据集:2699张图VOC+YOLO双格式,YOLOv8训练实战
2026/9/28 16:03:03 网站建设 项目流程

简介:道路积水检测数据集面向目标检测与计算机视觉开发者,提供Pascal VOC和YOLO两种主流标注格式,类别仅含“water”一类,共3777个标注框,适用于积水识别、水位监测等场景的模型训练与算法验证。压缩包共2000个文件,以1999个XML标注文件为主体,另附1个使用说明TXT,整体大小76.78MB;XML对应VOC格式、TXT对应YOLO格式,文件命名相互对应,可免去格式转换步骤,直接接入YOLOv5、YOLOv8、Faster R-CNN等常见检测框架。所有标注由labelImg工具按矩形框规则完成,标注边界合理、质量基本可靠,但数据集不提供模型精度保证。已有1068人学习下载,适合需要现成标注数据、希望节省人工标注成本的研究者和工程师。

1. 道路积水检测数据集:2699 张图、3777 个 water 框,VOC+YOLO 双格式开箱即用

做路面积水识别的人最清楚,卡进度的往往不是网络结构,而是标注。网上能用的积水图像本来就少,自己拿 labelImg 一张张画又极其耗时。这个道路积水检测数据集一共 2699 张 jpg,类别只有一个 water,标注框总数 3777 个,并且每张图同时给出 Pascal VOC 的 XML 和 YOLO 的 TXT 两种标注。也就是说,VOC 文件可以用于校验和二次编辑,YOLO 文件可以直接进 YOLOv5/v8 的训练管线,省掉最麻烦的格式转换环节。它适合正在做智慧城市积水巡检、道路养护、监控视频异常检测的从业者,也适合想把 YOLOv8 训练流程完整跑一遍的初学者——拿到手只需要整理目录、写个 data.yaml 就能开训。

2. 压缩包内部结构与双格式对齐:XML 像素框怎么换算成 YOLO 归一化坐标

2.1 文件清单与命名规律:为什么说双格式不是重复冗余

解压后第一眼看到的是一堆firc_water_开头的文件,外加一个「使用前必读.txt」。命名规律非常直接:同一编号后缀同时存在 jpg、xml、txt 三个文件,比如firc_water_687.jpg、firc_water_687.xml、firc_water_687.txt,代表这张图同时具备 VOC 与 YOLO 两套标注。数量上 jpg、xml、txt 各 2699 个,一一对应,没有出现只有图没有标注的孤儿文件,这一点比很多从网上拼凑的数据集要干净。

文件类型数量用途
jpg2699原始图像帧
xml2699Pascal VOC 格式标注,labelImg 默认输出
txt2699YOLO 格式标注,归一化矩形框坐标
使用前必读.txt1类别、统计信息与使用说明

摘要里特别说明「txt 不包含分割路径」,指的是没有 segmentation 多边形数据,每行就是一个矩形框的 class cx cy w h。这个「路径」说的是标注中的 polygon 路径点,不是文件目录路径,新手容易在这里误读成压缩包缺东西。

2.2 VOC 的 XML 与 YOLO 的 TXT 是怎么对应上的

VOC 侧是 labelImg 保存的标准 XML,核心字段包括图像尺寸和每个目标的类别名、矩形框绝对坐标,结构大致如下:

<annotation> <folder>firc_water</folder> <filename>firc_water_687.jpg</filename> <size> <width>1920</width> <height>1080</height> <depth>3</depth> </size> <object> <name>water</name> <bndbox> <xmin>320</xmin> <ymin>540</ymin> <xmax>1180</xmax> <ymax>980</ymax> </bndbox> </object> </annotation>

XML 记录的是绝对像素坐标,而 YOLO 训练时要求归一化相对坐标,换算公式为:cx = (xmin + xmax) / 2 / width,cy = (ymin + ymax) / 2 / height,w = (xmax - xmin) / width,h = (ymax - ymin) / height。以上面这组数据为例,cx = (320 + 1180) / 2 / 1920 ≈ 0.3906,cy = (540 + 980) / 2 / 1080 ≈ 0.7037,w = (1180 - 320) / 1920 ≈ 0.4479,h = (980 - 540) / 1080 ≈ 0.4074。

对应到 txt 里就是一行0 0.3906 0.7037 0.4479 0.4074,首列 0 是类别 id,后面依次是中心点 x、中心点 y、宽、高。拿到这份资源后,我习惯随机抽几张图,用这个公式反向把 txt 换算回像素坐标,再和 xml 里的 bndbox 比对,能快速确认两套标注是否真的同步。

2.3 单类别 3777 框的统计含义:稀疏目标场景怎么看待数据

类别只有 1 个,water 框数 3777,也就是总框数 3777。平均到每张图约 1.4 个框,说明绝大多数场景是单个水洼或两三块积水,属于典型的稀疏目标检测。对刚上手 YOLO 的人来说,这是最不容易翻车的起步配置:不用做类别不平衡处理,不用设计难例挖掘策略,模型只需要学一个特征——水的反光、纹理、颜色与周围路面的差异。

提示:别把「单类别」理解成「简单」。积水在白天强反光、夜间暗色水渍、雨后动态水面下的视觉差异很大,数据里的多样性决定模型上限,训练完必须拿实景视频验证,单类别同样有误检空间。

3. 喂给 YOLOv8 训练自己的数据集:目录划分、data.yaml 与参数调整

3.1 先把数据按 YOLO 习惯拆成 train 和 val

这个数据集没有预分 train/val,所有 jpg、xml、txt 平铺在同一层。YOLOv8 官方做法是 images 目录和 labels 目录一一对应,训练时通过 data.yaml 指定路径。常见做法是按 9:1 划分,2699 张取约 270 张做验证集,随机划分时固定随机种子,保证每次切分结果一致:

mkdir -p firc_dataset/images/train firc_dataset/images/val mkdir -p firc_dataset/labels/train firc_dataset/labels/val python3 - <<'EOF' import os, random, shutil random.seed(42) src = "firc_water" imgs = [f for f in os.listdir(src) if f.endswith(".jpg")] random.shuffle(imgs) val_set = set(imgs[:270]) for name in imgs: img = os.path.join(src, name) txt = os.path.join(src, name.replace(".jpg", ".txt")) if name in val_set: shutil.copy(img, "firc_dataset/images/val/") shutil.copy(txt, "firc_dataset/labels/val/") else: shutil.copy(img, "firc_dataset/images/train/") shutil.copy(txt, "firc_dataset/labels/train/") print("train:", len(imgs) - 270, "val:", 270) EOF

这段脚本只拷贝 jpg 和 txt,xml 不进训练目录,但建议留一份在原始目录里作为标注底稿。random.seed(42)是关键,不固定种子的话第二次划分和第一次不一致,后续调参对比指标就不公平了。拷贝比移动安全,原始数据保留,误操作能恢复。

3.2 写 data.yaml:路径、类别与名称映射

YOLOv8 的 data.yaml 是这个数据集的入口配置文件,里面只有四项必须写对:

path: /home/user/firc_dataset train: images/train val: images/val nc: 1 names: 0: water

path建议写绝对路径,YOLOv8 会自动拼成/home/user/firc_dataset/images/train去读图。names的 key 必须从 0 开始连续编号,类别名叫 water 或中文都可以,但训练时模型只认 id 不认名字。这里最容易出问题的是把 names 写成{0: "0"}或漏写 nc,训练会直接报 class 数量不匹配。

3.3 训练命令与参数:imgsz、batch、epochs 怎么定

基础训练命令如下,模型先用 yolov8s 起步,性价比比较均衡:

cd /home/user/firc_dataset yolo detect train data=data.yaml model=yolov8s.pt \ imgsz=640 batch=16 epochs=100 \ project=runs name=water_v8s

imgsz=640是 YOLOv8 默认输入尺寸,积水属于中小目标,不建议降到 320,否则小水洼特征容易丢。batch=16取决于显存,12G 显存跑 s 模型通常没问题,跑不动就降到 8。epochs=100只是一个起点,实际看 results.png 里的损失曲线,train/loss 不再下降就可以提前停。

训练过程中重点盯 train/loss 和 val/loss 两条曲线:积水数据集背景相对简单,通常 50 轮左右就能看到 mAP50 上到 0.7 以上。如果 val/loss 后期反弹,说明过拟合,可以把 epochs 拉回或者加数据增强。

3.4 训练前的标注体检:用脚本找出越界框和非法框

拿到别人的数据集,第一件事不是训练,是体检。最典型的隐患是越界框——标注框超出图像宽高,YOLO 训练时直接报错或产生 NaN 损失。跑一遍下面的脚本,把有问题的 xml 全部列出来:

import glob from xml.etree import ElementTree as ET bad = 0 for xml in glob.glob("firc_water/*.xml"): root = ET.parse(xml).getroot() w = int(root.find("size/width").text) h = int(root.find("size/height").text) for obj in root.findall("object"): b = obj.find("bndbox") x1 = int(b.find("xmin").text); y1 = int(b.find("ymin").text) x2 = int(b.find("xmax").text); y2 = int(b.find("ymax").text) if x1 >= x2 or y1 >= y2 or x2 > w or y2 > h: print(f"{xml}: ({x1},{y1})-({x2},{y2}) img={w}x{h}") bad += 1 print("bad boxes:", bad)

脚本逻辑很简单:判断 xmin 是否小于 xmax、ymin 是否小于 ymax,再判断 xmax 和 ymax 是否超出图片尺寸。x1 >= x2这种退化框虽然罕见,但一旦出现会导致 loss 直接变成 NaN,而且很隐蔽。跑完如果 bad boxes = 0,可以放心进入训练阶段。

4. 常见问题排查:7z 解压、中文路径、标签错位与显存溢出

4.1 7z 压缩包解压失败:密码对但一直报错的真实原因

现象:从网上下载的.7z数据集,有人输对密码仍然提示「密码错误」或「数据错误」,也有人没设密码但7z x解到一半中断,报 Headers Error。

原因:.7z是 7-Zip 的私有高压缩格式,本机 7-Zip 版本过旧可能不兼容较新的 LZMA2 压缩头;网络传输导致文件字节损坏也会出现这种「密码正确但解压失败」的怪象,本质是文件校验不过,不是密码问题。

解决:Windows 下先把 7-Zip 升级到 19.00 以上版本。Linux 下确保装了 p7zip-full,先测试完整性再解压:

sudo apt install p7zip-full 7z t 道路积水检测数据集VOC+YOLO格式2699张1类别.7z 7z x 道路积水检测数据集VOC+YOLO格式2699张1类别.7z -o/home/user/firc_water

7z t是 test 模式,只校验不输出文件;报 Test OK 再解压。-o指定输出目录,注意-o后面不要加空格。Test 阶段如果报错,基本可以断定是下载不完整,重新下载比折腾修复命令更省时间。

4.2 中文路径导致训练报错:环境对中文字段不友好

现象:数据放在C:\Users\张三\道路积水数据集或/home/测试/积水这种中文目录下,训练刚开始就报 FileNotFoundError,或者干脆报 UnicodeEncodeError,编码异常时 OpenCV 的 imread 静默返回空图,训练出来的模型精度却很低。

原因:OpenCV 和部分 Linux 环境的 locale 对中文路径支持不好,读取图片时路径编码不一致,文件存在但打不开。

解决:解压后立即把目录重命名为纯英文,比如firc_water,路径中也不要带空格。这不是玄学,是 YOLO 系工具链在中文路径下的通病,见过太多人卡在这里。

4.3 xml 和 txt 框数对不上:两份标注失步怎么修复

现象:用体检脚本抽查,某张图的 xml 里有 2 个 object,但同一编号的 txt 只有 1 行;或者 txt 首列出现了非数字字符。

原因:labelImg 在 PascalVOC 和 YOLO 两种模式之间切换时,改过框但没重新保存,两套文件就会失步。手动编辑过 xml 也可能引入这种不一致。

解决:以 xml 为准重新生成 txt,这是最可靠的修复方式:

import glob from xml.etree import ElementTree as ET classes = ["water"] for xml in glob.glob("firc_water/*.xml"): root = ET.parse(xml).getroot() w = int(root.find("size/width").text) h = int(root.find("size/height").text) lines = [] for obj in root.findall("object"): name = obj.find("name").text if name not in classes: classes.append(name) b = obj.find("bndbox") x1 = float(b.find("xmin").text); y1 = float(b.find("ymin").text) x2 = float(b.find("xmax").text); y2 = float(b.find("ymax").text) cx = (x1 + x2) / 2 / w cy = (y1 + y2) / 2 / h bw = (x2 - x1) / w bh = (y2 - y1) / h lines.append(f"{classes.index(name)} {cx:.6f} {cy:.6f} {bw:.6f} {bh:.6f}") txt = xml.replace(".xml", ".txt") with open(txt, "w", encoding="utf-8") as f: f.write("\n".join(lines) + "\n")

注意最后一行的encoding="utf-8",Windows 下默认编码可能是 gbk,YOLO 读到乱码会直接跳过标签。这个脚本把 classes 列表动态扩展,以后往里加新类别也能复用。

4.4 CUDA out of memory:大分辨率图片与 batch 的取舍

现象:训练到第几个 batch 突然报CUDA out of memory,进程被杀。

原因:这个数据集部分图片可能分辨率较高,imgsz=640 时缩放本身不占太多显存,但 batch 和模型参数量乘积超了显存上限,常见于 batch=16 加 yolov8m 以上的模型。

解决:先降到 batch=8 试跑,确认稳定再逐步加;显存实在紧张就换 yolov8n。训练命令加cache=True把图片预加载到内存,减少 IO 抖动带来的显存峰值。别为了省显存把 imgsz 降到 320,积水框小,分辨率一低漏检率立刻上来。

4.5 txt 首列写了类别名而不是 id:YOLO 标签格式的硬性要求

现象:训练日志里大量出现WARNING: ignoring corrupt label,val/loss 曲线异常,模型收敛后什么都不检。

原因:有人手动编辑 txt 时把类别名写进去了,比如water 0.39 0.70 0.45 0.41。YOLO 要求首列必须是整数类别 id,从 0 开始,不认字符串。

解决:检查所有 txt 首列是否为纯数字,可以用awk '{if ($1 !~ /^[0-9]+$/) print FILENAME, $0}' firc_water/*.txt快速定位。修复方式就是上面 4.3 的脚本,用 xml 重新生成一遍,天然规避这类手写错误。

5. 对齐 labelImg 标注规范:从画框规则到自建积水数据扩充

5.1 数据集采用的标注规则:单类别矩形框背后的约定

摘要里明确写了两条硬信息:使用 labelImg 工具、对类别画矩形框。这决定了这份数据的标注边界逻辑:water 类覆盖的是图像中路面积水区域,包括积水水洼、湿润反光带和明显的水面纹理区域,不包含路缘石、排水沟等纯背景。

这类单类别矩形框标注有一个隐含约定:框要贴近目标边缘,不为了「框得多」把整张图框进去,也不把同一片积水拆成几十个小碎片。前者会引入大量背景噪声,后者会让模型学到碎片化的错误特征。我在复核这份数据时,重点看的就是有没有整图大框——目前看框数分布正常,平均每张 1.4 个框,说明标注者执行的是稀疏目标规范,不是暴力整图框。

5.2 用 labelImg 复刻同样的标注流程:PascalVOC 与 YOLO 模式共存

如果想往这个数据集里补充自己的积水图片,最好的方式是使用同款工具 labelImg,保证格式完全一致:

pip install labelImg labelImg firc_water classes.txt

classes.txt里写一行water,让类别预置好,避免手输错。打开后快捷键 W 画框,A 和 D 切换上一张下一张,Ctrl+S 保存。界面右下角有 PascalVOC 和 YOLO 格式切换按钮,注意每切换一次格式都要重新保存一次,否则两个文件不同步,这正是 4.3 里失步问题的来源。

注意:labelImg 的 YOLO 模式保存后生成 txt,但之前保存的 xml 不会自动更新,会保留旧框。所以正确顺序是:先切到 YOLO 模式画完全部框并保存,再切回 PascalVOC 保存一次,让 xml 和 txt 都刷新到最新状态。

5.3 扩充策略:把 2699 张的利用率提上去

积水检测场景的公开数据本来就少,扩充时优先从真实巡检视频抽帧,而不是盲目做增强。用 ffmpeg 按帧间隔抽帧能快速积累原始素材:

ffmpeg -i road_video.mp4 -vf "fps=5" -q:v 2 frames/%06d.jpg

fps=5表示每秒抽 5 帧,室内监控可以降到 1~2,避免相邻帧高度重复。抽完帧用 5.2 的流程标注,补充进训练集。内置增强参数则建议按俯拍场景调整:

hsv_h: 0.015 hsv_s: 0.7 hsv_v: 0.4 fliplr: 0.5 flipud: 0.0 mosaic: 1.0

flipud我特意设成 0。道路积水大多是俯拍,上下翻转会把图像语义颠倒,车流方向、灯杆位置全变反,容易制造矛盾样本。HSV 增强对积水特别有效,因为水面反光和色温变化大,颜色扰动能模拟阴天、黄昏、夜间路灯下的水色差异。mosaic 保持 1.0,小目标场景拼图增强收益明显。

6. 验证与落地:训练完怎么判断这 2699 张能扛住真实场景

6.1 先看损失曲线和混淆矩阵,再谈部署

训练结束后,runs/detect/water_v8s 目录下会生成 results.png 和 confusion_matrix.png。看损失曲线时别只盯着 mAP,重点确认 val/loss 没有在训练中段反弹。积水数据背景干净,过拟合的典型表现就是 val/loss 后期翘头,这时候降低 epochs 或增加 fliplr 都比换模型有效。

混淆矩阵这里有个知名困惑点:矩阵的行列总和加起来往往不等于 1,很多人以为模型出了问题。实际上 YOLO 的混淆矩阵右下方有 background 列,且未标注的忽略区域框不参与分母,所以「yolo 混淆矩阵总合不唯一」是正常现象,不要慌。单类别场景你只需要看两件事:water 行的召回率,以及 background 列的误检率。

6.2 实景视频验证:阈值调整与部署前最后的把关

指标再漂亮也只是静态评估,落地前必须拿实景视频过一遍:

yolo predict model=runs/detect/water_v8s/weights/best.pt \ source=street_rain.mp4 conf=0.35 iou=0.45 save=True

conf=0.35是部署关键参数。YOLOv8 默认 conf 是 0.25,对积水这种强反光目标来说,0.25 会把路灯倒影、白色标线误检成水。我从 0.5 往下扫,发现 0.35 左右能在漏检和误检之间取到平衡点。iou=0.45控制同一目标多个框的合并,水面边缘模糊时重叠框很常见,调低到 0.4 也能接受。

有一次我把默认 conf 直接部署到了巡检视频上,结果一个路灯反光场景疯狂误检,整个后台告警刷屏。从那以后我每次拿到数据集训练完,都会强制走一遍「体检脚本 → 损失曲线确认 → 3~5 段没见过的雨天路面视频实景验证 → 调 conf 封板」这个流程。这个道路积水数据集也一样,2699 张图的质量底子不错,但最终好不好用,取决于你愿不愿意在部署前多花一晚上看真实视频。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询