
简介这是一份水下管道泄漏破损检测数据集面向计算机视觉目标检测开发者和水下设施维护场景提供Pascal VOC与YOLO两种主流标注格式可直接用于训练和评测目标检测模型。压缩包内文件总数为2000以xml标注文件为主另含使用前必读.txt说明文件整体大小约15.86MB数据对应2069张水下管道图像标注覆盖kebocoran泄漏、keretakan破裂两类缺陷分别有1406框和1192框总标注框2598个全部由labelImg以矩形框完成。已有1269人学习下载。数据集标注规则清晰、格式完整可帮助研究者免去繁琐的格式转换与标注整理工作也适合作为水下缺陷检测、VOC/YOLO格式互通及模型训练调参的实战素材。1. 水下管道泄漏破损检测数据集把“VOC转YOLO”这一步从项目里删掉水下管道泄漏破损检测数据集VOCYOLO格式2069张2类别.7z这个压缩包解决的是检测项目里最容易被低估的一环数据格式对齐。做水下管网巡检、管道机器人视觉或市政排水管道状态评估的人通常不缺模型代码缺的是能直接喂进YOLO训练脚本的标注。VOC的xml是绝对坐标YOLO的txt是归一化中心点坐标差一步换算训练出来的框就会整体偏移。这个数据集把两边都备齐了2069张图、泄漏和破损两个类别解压后直接进yolov8训练自己的数据集或者继续用VOC做迁移学习都不需要再写转换脚本。适合正在找现成水下泄漏样本、又不确定标注边界是否可靠的工程师和学生。2. 拆开压缩包VOC与YOLO的字段映射和坐标换算拿到这类数据集第一件事不是急着训练而是先确认压缩包里的标注是否真的和图片一一对应。水下图片噪声大、光照分布乱转换脚本里任何一个想当然的字段都会在下游变成框偏移或漏检。这一章把解压、VOC字段读法、VOC转YOLO三件事一次讲清。2.1 先解压Linux下7z命令、目录预期与完整性校验.7z格式在数据集分发里比zip更常见因为水下原始图像纹理细节多7z的压缩率通常比zip高一截2049张图压下来能省不少流量。在Linux服务器上我一般直接用命令行解压先测试再释放7z t pipe_leak_breakage_2069_2cls.7z 7z x pipe_leak_breakage_2069_2cls.7z -o./pipe_data7z t是很多人会跳过的一步但数据集的坑往往就在这下载过程丢字节解压到一半报CRC错误你以为是密码或工具问题实际上是文件本身坏了。7z x后面跟-o指定输出目录不写-o时默认解压到当前目录会把xml、txt、jpg全部摊在工作区里后面训练时的路径要来回改非常被动。如果是分卷压缩命令写成7z x xxx.7z.0017-Zip会自动读取后续分卷。Windows下解压则尽量用官方7-ZipWindows系统自带的“压缩文件夹”功能对7z的加密头和长文件名支持不完整双击报错时先别怀疑密码。解压完成后目录预期大致是两类标注并存VOC/下是xml文件YOLO/下是txt文件两份标注描述同一批图片。如果只有其中一种说明发布方把另一种格式当“赠送”选项这时才需要跑转换脚本后面2.3节会给出完整实现。提示解压后先执行find . -name *.xml | wc -l和find . -name *.txt | wc -l两个数字应当相等且等于图片数量。这一步能在30秒内发现文件缺失或重复命名的严重问题。2.2 VOC标注里最容易读错的三个字段size、bndbox与objectVOC格式的核心是每个xml对应一张图片。解析xml时新手最容易踩的坑是直接用bndbox里的xmin/xmax/ymin/ymax去做训练却忘了这四个绝对坐标依赖size节点里的图像宽高。同一个标注框在1920x1080和640x360下语义完全不同YOLO训练前又要把图缩放到imgsz转换脚本一旦忽略size输出的框就是百分比级的偏移。xml里的关键结构大致长这样annotation filenamepipe_00042.jpg/filename size width1280/width height720/height /size object nameleak/name bndbox xmin312/xmin ymin240/ymin xmax518/xmax ymax331/ymax /bndbox /object /annotation每张图有几个object节点就表示标注了几个目标。类别字段是name这个数据集的2类一般命名为leak和break但不同发布版本可能写成leakage或crack。训练前先用一条命令统计所有xml里出现过的类别名不要拿着README里的类别说明想当然grep -h name VOC/*.xml | sort | uniq -c这条命令同时能暴露出类别不平衡问题。如果leak占了90%以上后面训练就需要针对break单独做增强或调loss权重否则模型会对泄漏敏感、对破损迟钝。另一个常被忽略的点是filename字段某些数据集的xml里写的图片文件名和实际文件名后缀不一致比如xml写的是.jpg盘里实际是.png转换脚本按后缀去拼标签路径时会找不到对应文件。2.3 VOC转YOLO的Python脚本坐标归一化和类别映射的完整实现YOLO的txt每行是class_id x_center y_center width height五个值都必须归一化到0-1。转换逻辑本身不复杂但两个细节很容易出错一是bndbox四个值要先除以图片宽高二是x_center是(xminxmax)/2再除以width不是xmin除以width。下面这个脚本我用来处理多套VOC数据集改一下CLASS_MAP就能复用import xml.etree.ElementTree as ET import os from pathlib import Path # 类别映射顺序决定class_id必须和训练时data.yaml的names一致 CLASS_MAP {leak: 0, break: 1} def convert_voc_xml(xml_path, output_dir, img_width, img_height): tree ET.parse(xml_path) root tree.getroot() txt_name Path(xml_path).stem .txt lines [] for obj in root.findall(object): name obj.find(name).text if name not in CLASS_MAP: print(f警告: {xml_path.name} 含未定义类别 {name}已跳过) continue bndbox obj.find(bndbox) xmin float(bndbox.find(xmin).text) ymin float(bndbox.find(ymin).text) xmax float(bndbox.find(xmax).text) ymax float(bndbox.find(ymax).text) # 归一化到[0,1]YOLO要求框坐标相对图片宽高 x_center ((xmin xmax) / 2) / img_width y_center ((ymin ymax) / 2) / img_height box_w (xmax - xmin) / img_width box_h (ymax - ymin) / img_height # 防止标注越界导致训练时警告做一次夹紧 x_center min(max(x_center, 0.0), 1.0) y_center min(max(y_center, 0.0), 1.0) box_w min(box_w, 1.0 - x_center) box_h min(box_h, 1.0 - y_center) lines.append(f{CLASS_MAP[name]} {x_center:.6f} {y_center:.6f} {box_w:.6f} {box_h:.6f}) if not lines: return 0 out_path os.path.join(output_dir, txt_name) with open(out_path, w) as f: f.write(\n.join(lines) \n) return len(lines) xml_root VOC label_root YOLO/labels os.makedirs(label_root, exist_okTrue) for xml_file in sorted(Path(xml_root).glob(*.xml)): tree ET.parse(str(xml_file)) root tree.getroot() # 推荐从xml的size节点取宽高不用PIL读图 size_node root.find(size) w int(size_node.find(width).text) h int(size_node.find(height).text) n convert_voc_xml(str(xml_file), label_root, w, h) if n 0: print(f提示: {xml_file.name} 没有有效目标检查类别名是否在CLASS_MAP中)这段脚本的逻辑可以拆成三层看外层遍历xml文件中层遍历object节点内层做坐标换算。最关键的参数是CLASS_MAP它的字典顺序直接决定YOLO的class_id训练时data.yaml里的names必须按相同顺序写否则class_id1会对应到错误的类别名。用(xminxmax)/2而不是xmin box_w/2数学上等价但前者少一次计算对于几千张图来说更稳。还要说明的是标题说“VOCYOLO格式”意味着压缩包里大概率两套标注都已给出。这种情况下这个脚本的价值不是必须跑一遍而是用来做校验——随机抽5张图把转换出的txt里的框画回原图再和xml里的框比较IOU但凡有一个框对不上就要检查size字段和filename字段。水下图像经常被发布方做过去雾或裁剪任何预处理都会改变原始尺寸而xml里的size可能还停留在预处理前这是唯一需要手工介入的地方。3. 用YOLOv8训练自己的水下泄漏破损数据集目录组织与参数修正数据集准备到位后接下来的问题就是怎么让训练不翻车。yolov8训练自己的数据集这件事网上教程很多但大多没讲清目录和数据yaml之间的配对规则。水下数据又有特殊性目标小、对比度低、背景纹理杂参数照搬COCO那套不一定合适。3.1 目录组织images和labels必须按train/val分好data.yaml写对namesYOLOv8训练时要求的目录结构固定常见做法是pipe_data/ ├── images/ │ ├── train/ │ ├── val/ ├── labels/ │ ├── train/ │ ├── val/ └── data.yaml关键约束是同一张图片的标注文件必须放在labels下的同名子目录里后缀从.jpg换成.txt主文件名不能变。如果一张图在images/train下它的标签就得在labels/train下目录层级必须一致。我一般用脚本划分而不是手动拖文件夹方便复现import random import shutil from pathlib import Path random.seed(42) src_imgs Path(pipe_data/images/all) src_labels Path(pipe_data/labels/all) all_imgs sorted(src_imgs.glob(*.jpg)) random.shuffle(all_imgs) val_num int(len(all_imgs) * 0.2) # 8:2划分 def organize(img_path, split): dst_img_dir Path(fpipe_data/images/{split}) dst_lbl_dir Path(fpipe_data/labels/{split}) dst_img_dir.mkdir(parentsTrue, exist_okTrue) dst_lbl_dir.mkdir(parentsTrue, exist_okTrue) shutil.copy(img_path, dst_img_dir / img_path.name) # 标签文件和图片的主名必须一致 label_src src_labels / (img_path.stem .txt) if label_src.exists(): shutil.copy(label_src, dst_lbl_dir / label_src.name) else: print(f警告: {img_path.name} 找不到对应标签) for img in all_imgs[val_num:]: organize(img, train) for img in all_imgs[:val_num]: organize(img, val)这段脚本固定的随机种子保证了每次划分结果一致方便对比实验。脚本最后打印的警告非常关键——如果图片和标签文件主名对不上训练时不会报错只会导致标签参与率下降比如200张图只找到150个标签训练照样跑但模型学到的类别分布是残缺的。data.yaml的内容更直接train: pipe_data/images/train val: pipe_data/images/val nc: 2 names: [leak, break]nc必须与names列表长度一致names的顺序就是类别索引的映射顺序。如果你在第2章的转换脚本里把leak设成class_id0那么这里的names列表也必须把leak放在第0位否则模型训练过程中会把类别A的数据当成类别B去拟合预测结果全乱。可以用一个简单方式验证训练完随意挑两张val图片做预测如果所有框都显示成同一个类别大概率是names排序错了而不是模型没收敛。3.2 训练命令和三个必调的参数batch、epochs、imgsz数据组织好训练命令本身很短。我通常用yolov8s.pt预训练权重起步不用nano是因为水下小目标检测对特征表达能力有要求s版本在精度和显存占用之间更平衡yolo detect train \ datapipe_data/data.yaml \ modelyolov8s.pt \ epochs200 \ batch16 \ imgsz640 \ project./runs \ namepipe_leak_v8s第一次运行这条命令时会自动下载yolov8s.pt预训练权重如果离线环境需要手动下载权重放到当前目录并把model改成model./yolov8s.pt。三个参数需要按实际情况调batch直接受显存限制。12G显存跑yolov8s和640分辨率batch16是安全的如果显存只有8Gbatch降到4会明显影响BatchNorm稳定性这时优先把imgsz降到480而不是硬扛batch4。水下目标视野通常较窄480分辨率并不会造成严重的精度损失。epochs我先给200但配合早停观察loss曲线一般110到150轮就收敛了跑满200轮多半开始过拟合。imgsz默认640够用原始图如果普遍是1920x1080的高分辨率可以试试960但显存占用接近翻倍训练时间也明显拉长。3.3 损失曲线怎么读box_loss、cls_loss、dfl_loss哪一条先收敛训练开始后打开runs/pipe_leak_v8s/results.csv或直接看results.png。YOLOv8有三条核心lossbox_loss是边界框回归损失cls_loss是分类损失dfl_loss是分布焦点损失它负责让框边缘更贴合目标。判断训练是否健康的标准是三条loss在前20个epoch同步下降只是下降幅度不同如果某一条横盘超过30个epoch就要回头检查数据。box_loss高而cls_loss正常通常意味着标注框本身有偏差——比如水下图片边缘弯曲导致真实管道轮廓和标注框不完全重合模型怎么拟合都压不住回归误差。这时不要加训练轮数而是检查标注框是否准确。cls_loss高而box_loss正常则大概率是类别不平衡或两类目标外观太接近早期能明显看到漏检集中在频次少的那一类。dfl_loss单独偏高时我一般先检查是不是有大量小目标——dfl对小目标的边缘预测更敏感如果数据集里泄漏点只有十几个像素dfl_loss不降是正常的可以尝试提升imgsz到960再看曲线。提示如果训练日志里出现NaN优先怀疑batch太小和学习率太高而不是网络结构。把batch调到16以上、学习率降到0.0005这个数据集基本不会触发数值问题。4. 水下数据集训练常见问题避坑指南5条能省一周的排错记录做数据集项目最浪费时间的就是排错。下面5条都是我在类似的水下检测任务里实际踩过的坑按“现象、原因、解决”的方式记录你大概率会遇到其中的一两条。4.1 7z解压报错密码正确却提示错误问题多半不在密码现象执行7z x时报密码错误或者Windows下双击压缩包解压到一半提示文件损坏。 原因下载文件不完整或者解压工具对7z加密头的兼容性有问题。还有一个常见现象是输出路径中包含空格或中文目录部分终端环境下7z解析路径出错给出误导性的密码错误信息。 解决先执行7z t测试完整性返回CRC Error就重新下载不要反复试密码路径全部改成纯英文数字Windows下安装官方7-Zip后右键解压不要用第三方压缩软件。这个问题和数据本身没关系但会卡住你半天。4.2 yolo训练中bn崩溃loss突然变nan问题定位到BatchNorm现象训练到某个epoch后loss变成nan重启训练又在相近位置崩掉。 原因batch过小时BatchNorm的均值和方差估计不稳加上默认学习率对它来说偏高BN统计量发散后loss直接爆炸。显存不足导致的batch2、batch4是这个问题的高发区。 解决先把batch调到16若显存不够就把imgsz降为480再把学习率从默认的1e-2降到0.0005如果还是nan临时加上ampFalse排除混合精度问题。用这个2069张的数据集batch16配合yolov8s在一个12G显卡上是可以跑完的没有理由用batch4硬撑。4.3 类别不平衡leak占绝大多数break老被漏检现象训练完成后val集上break的recall明显低于leak尤其在小目标上几乎检不到。 原因水下破损磨损样本天然少于泄漏样本统计标签分布经常能看到leak占了80%以上break和它的差距会误导模型的分类边界。 解决先统计两类频次然后对break做离线增强——把break样本做亮度扰动、顺时针旋转15度、加高斯噪声增强后的新图片同步生成对应txt标签。这一步不要去动leak的样本量保持原始分布验证增强效果。如果离线增强后仍不均衡评估指标改用macro mAP看少数类的平均表现。4.4 yolo混淆矩阵总合不唯一指标读法比数值更重要现象训练后生成的混淆矩阵行求和和列求和都不等于100%看起来“不对劲”。 原因yolo的混淆矩阵不是单一归一化矩阵。按行归一化时每一行代表某个真实类别的召回率分布按列归一化时每一列代表某个预测类别的精确率分布两者无法同时满足行列和为1。此外background单独占一行一列也会让数字看着不规整。 解决读图前先确认坐标轴含义——行是Ground Truth、列是Predicted时按行看召回率想查某个类别的误检率就按列看。不要用“所有数字加起来是不是100%”来判断代码有没有bug这本来就不是同一个归一化基准。4.5 标注边界错位xml里的size和真实图片不一致现象val集上mAP不低但把模型放到实际水下视频里检测框整体偏移误差方向一致。 原因VOC的xml里记录了图片尺寸如果数据集发布方做过裁剪、去雾或缩放但xml的size没有同步更新转换出的YOLO坐标就带着系统性偏差。 解决随机抽5张图把xml里的bndbox画回原图看框是否贴合目标。如果发现框全部偏右或偏上直接用2.3节的脚本重新处理并从预处理后的图片读取新尺寸。这个检查必须在训练前做训练后再发现只能重新标注代价最大。5. 验证与进阶从混淆矩阵到视频流实时检测训练完别急着看mAP先把val集的指标拆细。用yolo val拿到per-class AP后我习惯额外盯漏检率而不是只盯mAP——水下泄漏检测漏掉一个点可能比误报十个更严重。把混淆矩阵按行归一化看recall找出漏检集中在哪类目标的哪个尺寸区间。如果漏检集中在小目标可以训练时开scale数据增强或者提高imgsz如果集中在模糊帧说明训练集缺少模糊样本需要在数据层面补图而不是调参。模型验证通过后下一步是接实时视频流。水下管道机器人或固定点摄像头通常走RTSP协议用YOLO的Python接口做流式推理非常直接from ultralytics import YOLO model YOLO(runs/pipe_leak_v8s/weights/best.pt) source rtsp://user:pass192.168.1.64:554/stream results model.predict( sourcesource, imgsz640, conf0.30, streamTrue, max_det50, ) for r in results: boxes r.boxes.xyxy.cpu().numpy() # 检测框坐标 clss r.boxes.cls.cpu().numpy() # 类别ID scores r.boxes.conf.cpu().numpy() # 置信度 # 实际项目里在这里做连续帧逻辑同一位置连续5帧出现才触发报警streamTrue表示开启生成器模式逐帧处理而不是一次性加载整段视频这对长时间值守很重要conf0.30是经验值水下场景的置信度往往比陆地场景低一截设太高会把真目标滤掉。每帧拿到boxes和clss后连续帧逻辑要自己做——同一位置连续出现才报警能过滤掉鱼和悬浮物在镜头前短暂遮挡造成的误报。我自己在项目里的习惯是任何数据集拿到手先花10分钟把标签画回图上目检20张再谈训练。这一步帮我躲过了至少三次格式错位和类别顺序颠倒的坑比调任何超参都值得。这次这个水下管道泄漏破损检测数据集VOCYOLO格式2069张2类别.7z只要解压后你愿意先做一遍标签抽检后面整个训练流程会很顺。希望帮到你。本文还有配套的精品资源点击获取