资讯动态

积水图像分割检测数据集制作与YOLOv8/YOLO11训练实战

发布时间:2026/10/18 13:44:08 来源:尧图企业网站定制
简介这是一份面向计算机视觉开发者与深度学习研究者的积水图像分割检测数据集专为使用YOLOv8、YOLO11等框架开展图像分割与目标检测训练而整理。压缩包内为纯数据集不包含训练代码共1071个文件含428张积水场景原图jpg、428个YOLO格式分割标注txt、214个JSON格式标注文件及1个yaml配置文件整体大小约389.4MB。JSON标注适合需要精细化区域信息的场景YOLO格式可直接接入主流检测分割流程方便快速实验数据按img、json、seg分目录组织便于对照查看与按需选用。内容覆盖积水区域识别、分割训练等实际任务可为道路积水监测、防汛预警等应用提供数据支撑目前已有523人学习/下载。整体目录结构清晰开发者可在此基础上快速搭建分割训练管线。1. 积水图像分割检测数据集为什么积水分割比目标框检测难一截雨后路面上的积水用检测框去圈会非常尴尬水洼是不规则形状反光、倒影、路灯影子全都混在里面框出来一大块区域少说三成像素不是水可要是漏掉一块行人一脚踩进深水坑才是真问题。这个积水图像分割检测数据集要解决的就是把「到底是水还是只是亮的柏油路」做成逐像素判断用分割模型给每个积水区域画出精确掩码再交给下游统计水淹面积、发预警。它特别适合做道路积水监测、汛期巡查、厂区低洼地巡检以及自动驾驶对积水路面的感知这四类场景核心价值是把识别结果从「有个框」升级成「这块水面可信」。这个数据集覆盖不同光照条件下的积水样本配合yolo8、yolo11这类分割代码能直接跑通标注、格式转换、训练、验证全链路。新手可以照着后面步骤把第一版模型跑起来熟手则能在边界处理和训练调参里找到对自己场景有用的部分。下面直接从数据怎么拍、怎么标讲起。2. 积水数据采集与标注把现场照片变成可训练的分割样本分割模型的精度上限由标注质量决定这一章先解决数据从哪来、怎么标、标完怎么检查。2.1 拍什么样的积水采集清单与标注原则先说采集。积水的语义严重依赖环境不能只拍晴天大太阳下的一滩水。我一般会把采集分成几个维度时段上白天、傍晚、夜间各占一部分夜里要专门拍路灯下的积水、车灯扫过的水洼天气上雨后半小时内是黄金窗口这时积水量大、边界清晰小雨中的路面因为整体湿润标注难度会骤增建议和晴天后积水分开存放角度上平视、俯视、高位摄像头视角都收一些因为很多部署位是路侧立杆俯视画面占大头。分辨率方面训练图像建议长边不低于640像素太小的图在缩放后水洼边缘细节会丢。采集时优先保存原始JPEG不要低于80%质量分割对边缘锐度比检测更敏感。数量上先按500到1000张起步这个量级足够让模型学到积水的基本外观后续再根据夜间、雨天这类难点补充难例。如果现场有条件同一片水洼正对拍一张、斜向拍一张顺便记录一下积水的大致水深等级后期想扩展成多级预警数据集时就不用重新返工。标注原则要定死默认只标一个类别「积水」水花、水痕、深色柏油路上的水渍不算标注时多边形紧贴水面可见边缘不要把背景包进来积水被树叶或栏杆遮挡的部分如果看不到水面就不标宁可漏不要错同一张图里多个水洼都逐一标出哪怕很小。类别名在标注工具里就统一写成water不要这张图写water、那张写puddle否则转换脚本里映射关系会乱。另外建议维护一份采集日志记录每张图的拍摄地点、时段和天气这会在后面划分训练集和验证集时起到关键作用。2.2 用标注工具打分割掩码LabelMe的标注路径与批量检查常见做法是先用LabelMe这类开源标注工具画多边形。操作路径不复杂打开工具加载图像用多边形工具沿积水边缘点下去点得越密边缘越贴合但后面训练成本越高一般几十个点就够闭合后给形状命名water保存JSON。多边形的起始点无所谓但方向尽量保持统一虽然训练代码对方向不敏感人工复查时统一的绕行方向能少看漏几个。标注完后别急着转格式先写个脚本批量检查JSON里有没有空标注、类别名是否统一、坐标是否越界。下面这段脚本放在导出JSON之后跑一遍import json from pathlib import Path def check_labelme(jsons_dir, category_names): issues [] for fp in Path(jsons_dir).glob(*.json): with open(fp, encodingutf-8) as f: data json.load(f) label_map {s[label] for s in data[shapes]} unknown label_map - set(category_names) if unknown: issues.append(f{fp.name}: 未注册类别 {unknown}) for i, s in enumerate(data[shapes]): if len(s[points]) 3: issues.append(f{fp.name}: shape[{i}] 点数小于3无法组成多边形) w, h data[imageWidth], data[imageHeight] xs, ys zip(*s[points]) if min(xs) 0 or max(xs) w or min(ys) 0 or max(ys) h: issues.append(f{fp.name}: shape[{i}] 坐标越界) return issues known [water] for msg in check_labelme(path/to/labelme_jsons, known): print(msg)这段逻辑很直接先检查有没有没登记过的类别名避免「转换时类别映射找不到」的连锁翻车再检查每个多边形至少3个点、坐标不超出图像宽高。我实际用时发现标注工具偶尔会存出2个点的废多边形不查的话后面转出来是非法线段。参数里category_names是项目允许的类别集合改成[water, splash]这类即可。跑完后把issues逐条看一遍特别是坐标越界这种通常是用图像旋转预览后没刷新原图尺寸导致的。2.3 数据增强的边界哪些增强会破坏积水的语义标注完直接开训通常效果一般夜间、逆光样本少时还得靠增强。但积水分割对增强有很强的约束下面是常见增强手段的对照增强手段是否推荐原因水平翻转推荐地面语义左右对称能稳定增加样本量垂直翻转不推荐积水和天空倒置模型学到错误的重力方向亮度/对比度调整适度能模拟夜间和阴影但别拉重度到水也看不清旋转90/270度不推荐积水一般水平附着地面旋转后语义失真随机裁剪/缩放推荐且必需让模型适应不同水洼尺度但缩放别低于0.5倍HSV色相扰动不推荐会改变水面反射的固有色调容易引入假特征重要提示跑yolo8、yolo11自带增强时旋转项调低甚至关掉mosaic可以保留因为小水洼在mosaic混合里更容易被模型注意到。所谓亮度适度是说把图像变暗到真实夜间水平即可不要做那种把整个画面压成剪影的极端增强那会让水洼和路面彻底混在一起。3. 把标注结果转成yolo8、yolo11能吃的格式转换脚本与四个边界坑标注完只是画完图训练代码认的是自己的标签格式这一章负责把LabelMe的JSON转成yolo8、yolo11分割训练的txt。3.1 YOLO分割格式的坐标规律归一化多边形怎么写才不出框yolo8、yolo11这套分割训练代码用的标签格式是每行一个目标类别id 归一化的多边形坐标序列。拿一个小水洼举例一行文本是0 0.55 0.42 0.58 0.44 0.56 0.47 0.53 0.45第一个数字是类别id后面每两个数字是一个归一化坐标点x,y。坐标必须除以图像宽和高后落在0到1之间不能出现负值和大于1的数多边形如果跨图像边缘要先把越界点裁剪进边界再归一化不要直接保留超出边界的坐标。还有一个细节标准格式要求多边形闭合自洽连续重复点或自相交会导致mask生成异常。坐标小数精度保留几位值得较真保留太少比如两位小数小水洼边缘会被磨平保留太多比如八位文件变大且对精度无实质帮助。我一般保留六位小数这个精度在归一化后足够表征亚像素边缘不会因为四舍五入在边界处产生争议。如果一批图里既有1920的大图又有640的小图归一化后坐标范围一致无需额外处理这也是YOLO标签格式最大的优势。3.2 LabelMe JSON转YOLO TXT的转换脚本与参数解释把上一章的JSON转成txt我写的核心转换脚本大致如下import json import shutil from pathlib import Path label_to_id {water: 0} # 类别名映射到id val_names {IMG_0382, IMG_0401} # 验证集文件名黑名单 src_root Path(alldata/labelme) # 标注JSON目录 img_root Path(alldata/images) # 原始图像目录 out_root Path(dataset) # 输出根目录 for split in [train, val]: (out_root / images / split).mkdir(parentsTrue, exist_okTrue) (out_root / labels / split).mkdir(parentsTrue, exist_okTrue) for json_path in sorted(src_root.glob(*.json)): with open(json_path, encodingutf-8) as f: data json.load(f) image_w, image_h data[imageWidth], data[imageHeight] base json_path.stem src_img img_root / data[imagePath] if not src_img.exists(): print(f跳过 {base}: 原始图像缺失) continue target_split val if json_path.name in val_names else train shutil.copy(src_img, out_root / images / target_split / src_img.name) lines [] for shape in data[shapes]: if shape[label] not in label_to_id: continue cid label_to_id[shape[label]] norm_pts [] for x, y in shape[points]: nx min(max(x / image_w, 0.0), 1.0) ny min(max(y / image_h, 0.0), 1.0) norm_pts.extend([f{nx:.6f}, f{ny:.6f}]) lines.append(f{cid} .join(norm_pts)) (out_root / labels / target_split / f{base}.txt).write_text( \n.join(lines), encodingutf-8) print(f{base} - {target_split})参数要点label_to_id是类别名与模型类别数量对齐的唯一依据单类积水写成{water: 0}多类按顺序加就行但一定要和后面data.yaml里的names顺序一致。val_names用文件名黑名单来划分验证集这是我习惯的做法比随机切分更可控目的是防止同一地点的多张图一部分进train、一部分进val造成验证集虚高。图像缺失时脚本会print并跳过不跳过的话训练时因为找不到图会直接报错。复制原图而不是移动保留一份原始JSON和原始图是分割项目的基本保底习惯删错了没有后悔药。3.3 四个边界坑空txt、类别串位、自交多边形、目录缺层第一个坑是空txt。一个JSON里的多边形全部被过滤掉时labels里会写出0字节的txt。训练时这个文件会被当作无目标的负样本如果负样本比例高模型会倾向一律不输出积水。解决转换后统计一下小于几个字节的txt数量对照原图人工抽查确认是不是漏标。第二个坑是类别id串位。多类场景里类别映射顺序写错或者标注时用了两个不同名字会导致同一个id对应两种语义。解决转换脚本开头加一条断言遍历所有JSON检查类别集合是否严格等于映射key集合不相等直接抛异常别让下游去猜。第三个坑是自交多边形。手动画点时偶尔交叉了一下面积计算和掩码生成都会出错。解决转换时对每个多边形做valid校验无效的多边形自动跳过并打印文件名让标注员补标比带着坏数据训练强得多。第四个坑是目录层级缺层。yolo8、yolo11训练时找的是dataset/images/train和dataset/labels/train少一层训练直接起不来。解决脚本里先用mkdir建全目录再开始复制不要依赖别人给的zip目录。跑完转换后顺手打印一下train和val的图片数、标签数两边加起来能和源目录对上再进下一步。4. 用yolo8、yolo11跑积水分割训练命令、超参数与效果验证转换完就能进训练阶段。这一章从目录结构、训练命令、关键参数到指标解读逐层补齐。4.1 最小训练命令与目录结构检查先把数据集目录摆好结构是固定套路dataset/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/ └── data.yamlyaml内容决定一切我写的是path: D:/projects/water_dataset/dataset train: images/train val: images/val nc: 1 names: [water]path建议写绝对路径相对路径在换机器跑或者后台进程启动时会踩到工作目录不一致的坑。names顺序必须和转换脚本里的label_to_id完全一致第0类叫water就写water这叫对齐否则训练出的模型推理时第0类画出来根本不是水。然后是最小训练命令yolo train modelyolo11n-seg.pt dataD:/projects/water_dataset/dataset/data.yaml \ epochs100 batch16 imgsz640 cacheTruemodel参数可以直接写一个预训练分割权重文件名建议先拿最小的n模型跑通流程确认数据集没问题再换大模型data指向yaml路径epochs先给100轮积水数据量少100轮足够看到趋势batch大小取决于显存16G显存跑640分辨率分割batch16比较合适8G就降到8。cacheTrue把图像缓存进内存训练前只读一次磁盘之后全走内存速度提升非常明显代价是占内存。图像总量几十GB时建议关掉cache或只开部分缓存并量力而行。4.2 四个必调参数imgsz、batch、cache、augmentimgsz分割模型的多尺度能力都靠它。640是性价比基准官方预训练权重就在这个尺度上磨出来的如果现场采集的大多是高位俯拍水洼可以试着提到960小目标分割的边缘更清晰代价是显存和训练时长直接翻倍。白天和夜里的图像分辨率差异大时宁可保持640并用增强补不要强行拉高。batch不只是显存问题还影响BatchNorm的统计。积水样本里同一条路的图可能高度相似batch太大会让模型把「这段路」当成特征。我常用的区间是16到32低于8时分割掩码收敛明显变慢且训练曲线会抖得厉害。cache第一次训练必须开后续复训也要开着。如果机器内存紧张关掉cache改成调高加载线程数是另一种做法但在Windows上多进程加载容易碰壁用cache规避更省事。注意cache开起来后如果数据集后来又加了新图先删掉缓存重新构建否则新图不会生效。augmentyolo8、yolo11里影响分割的主要是旋转、尺度、翻折、HSV扰动。积水场景里旋转项我一般压到10以内因为小角度旋转还好大角度旋转会破坏水洼和地面的贴合关系水平翻转保持默认0.5这是积水分割里最安全的增强HSV色相扰动适当收一点到10上下因为水面反射的色调是真实特征扰动太大会让倒影被学成真水。其他增强可以用默认值先跑通一轮再逐个加。参数设置汇总如下参数推荐值说明imgsz640必要时960高分俯拍可拉高显存占用会翻倍batch168到32区间优先保证BatchNorm统计可靠cacheTrue/ram小数据集可开内存不足就关degrees0~10积水语义不宜大角度旋转hsv_h10~15保留水面反射色相特征fliplr0.5积水平面翻转安全默认即可4.3 验证指标怎么读mask_mAP与单类精确度训练日志里会同时打box和mask两类指标积水这类不规则物体必须盯mask。训练跑完看最后几行通常长这样Epoch 100/100 mask_mAP_50 0.872 mask_mAP_50-95 0.511 box_mAP_50 0.908只看box会和真实体验脱节。框误差小不代表掩码贴合积水边缘如果错得离谱mask_mAP_50-95会立刻给个低分那才是分割的真正瓶颈。单类项目再打印一下每个类别的precision、recall看看验证集里夜间样本多不多precision高但recall偏低说明模型在「少报」这对积水监测来说比误报更危险漏掉一个水坑比多报一个更让甲方头疼。训练完顺手跑一次预测看看掩码可视化对不对比任何指标都直观yolo predict modelD:/projects/water_dataset/runs/segment/train/weights/best.pt \ sourceD:/val/sample_images saveTrue输出目录里逐张看掩码重点看反光高亮区有没有把整个光斑标成水。如果光斑被划进去回去补一批「车灯下路面积水」的负样本标注再训一轮通常比调参管用。5. 积水分割训练的高频排查5个典型翻车现象与对症解法这一章直接列排查清单每条都是实际跑积水项目时会遇到的。5.1 模型把水面倒影和真水一起标出来现象白天的小水洼周围树影、楼影也被画成积水掩码比实际水面积大一圈。原因积水区域里有倒影低层特征把「暗色纹理块」误当成积水边缘本质是缺少边缘约束样本。解决把倒影单独标成一类不现实更可靠的是补标一批「水洼边缘清晰、背景复杂」的实例并在增强里把对比度扰动打开让模型学到倒影只在水面范围内才成立范围外不算水。5.2 验证集mAP高但现场新图全崩现象本地验证集mask_mAP到0.85拿园区摄像头新拍的画面去测漏检率三成。原因数据集里同一个积水位置重复拍了很多张随机划分后验证集会泄漏场景特征模型记的是背景而不是积水。解决按拍摄地点而不是按文件名随机切train/val一个地点的全部图进同一侧。这个划分逻辑改动不大但涉及重新训练多数项目都在这一步翻车。这也是第二章让大家记采集日志的原因。5.3 标注点太密训练速度断崖式下降现象一张水洼边缘标了几百个点训练时数据加载就卡很久batch耗时长到没法看。原因分割训练需要对每个多边形的点列参与loss计算点数越多计算量越大而水洼形状根本不需要几百个点。解决转换脚本里加多边形抽稀把点数压到80以内保留主要拐点去掉冗余点。训练速度能降下来边缘精度也不会受影响。5.4 夜间积水全漏检白天指标还不错现象白天mAP正常夜间肉眼可见的水坑输出空掩码。原因夜间样本数不足且曝光不足模型白天特征学得太强一到低照度场景整个失效。解决采集时专门补夜间路灯、车灯场景同时把亮度阶梯增强加到训练集让像素值分布更贴真实夜间。必要时把imgsz降到480试跑提升夜间小水洼的感知力。5.5 推理时掩码出现在错误位置类别错位现象模型标注的名字是water但掩码总出现在另一类的位置上两张图破绽百出。原因yolo8、yolo11用的类别id从0开始而外部标注转了一圈后类别计数从1开始两边错位。解决一切以data.yaml里的顺序为准转换脚本里写死从0开始每次训练前抽查一个txt确认类id最大值小于nc。养成这个检查习惯后类别错位基本绝迹。6. 掩码后处理与连续帧抖动让积水检测从「能跑」到「能用」模型输出的是概率掩码直接拿它统计面积和报警还不够差在后处理这最后一步。6.1 把原始mask变成干净的积水块二值化、开运算与面积过滤推理代码吐出的掩码是单通道概率图要转成可用的二值图。我会用一段OpenCV脚本做后处理import cv2 import numpy as np def refine_mask(mask, min_area50): _, binary cv2.threshold(mask, 127, 255, cv2.THRESH_BINARY) kernel cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5)) opened cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel) contours, _ cv2.findContours(opened, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) out np.zeros_like(binary) for c in contours: if cv2.contourArea(c) min_area: cv2.drawContours(out, [c], -1, 255, -1) return outthreshold把概率图切开成前景MORPH_OPEN先把细小噪声点消掉再用面积阈值过滤掉水花和反光碎块。min_area按图像分辨率调640下50像素起步低于这个值的基本是误检。这样处理后水洼边缘保持得干净又不会因为一个小点触发报警。6.2 连续帧掩码不连贯一帧有水、下一帧消失的处理习惯单帧推理时车灯光斑、水面的细微变化都可能导致掩码闪烁报警系统最怕这种抖动。我在做现场巡检项目时发现按帧直接处理会连续触发误报场景很难看。常见做法是在检测逻辑外再套一个轻量的帧间滤波对每个积水块记录连续出现的帧数只有连续几帧都有掩码且中心点位移小于阈值时才认定是可靠目标一旦消失就缓删而不是立刻清除。阈值取3到5帧配合单帧的分数阈值能把误报磨平。这个滤波不伤掩码精度但一定要放在面积统计之前做否则报警数据很难看。更省事的做法是直接对二值图做时间维度的滑动平均当前帧结果取上一帧结果的一部分加当前帧的一部分再重新做一次阈值。这样处理后延时小内存占用极低适合摄像头端的轻量部署。需要注意积水是慢变化物体这个均值不要衰减得太快否则夜间闪烁依旧会漏过。积水分割从数据集到可用的动作链路最后一步就是这样的轻量工程化。我自己的习惯是每批新数据进模型前先跑一遍转换脚本和掩码后处理验证新图能否被正确过滤出积水块再决定要不要追加训练集。做完这层防护现场误报率明显下降希望这套排查思路能帮到你。本文还有配套的精品资源点击获取

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

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

免费获取报价 →
↑