ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

水下管道泄漏检测:VOC+YOLO数据集与YOLOv8训练实战

水下管道泄漏检测:VOC+YOLO数据集与YOLOv8训练实战 简介面向水下管道缺陷检测数据集包含2类目标——泄漏kebocoran与裂缝keretakan共2069张图像、2598个标注框。标注格式采用VOC与YOLO两套规范压缩包以1999个XML标注文件为主体另附说明TXT共2000个文件整体约15.86MB。适合从事工业巡检、水下机器人视觉识别的开发者与研究人员可用于目标检测模型训练、漏点定位、裂缝识别与算法调优。标注由labelImg工具按矩形框规则生成类别均衡度良好可省去人工标注环节直接作为YOLO、Faster-RCNN等模型的训练或验证数据。目前已有1269人学习下载是开展水下管道检测算法研究的实用基础数据集。1. 为什么水下管道检测要先买数据集2069张图背后的三类刚需水下管道泄漏破损检测数据集VOCYOLO格式2069张2类别第一眼看上去就是那种能让项目立刻转起来的数据资源。水下管道巡检的算法方案里最贵的从来不是显卡和模型而是标注成本和格式对齐时间。VOC格式方便人工核对、二次标注和合并YOLO格式解压就能喂给训练脚本中间省掉的格式转换和格式排查往往比训练本身还耗工时。这个数据集解决的是水下机器人视觉检测里最卡人的两件事有没有可用的数据标注格式是否标准。适合刚接手水下管道检测任务的算法工程师也在做完整性检测方案选型的人还有需要拿真实数据跑通毕业设计的同学。2069张图对深度学习来说不算大但相对水下管道这种垂直场景足以撑起一个像样的baseline再通过清洗和增强把模型推到能上船的状态。拿到这个压缩包第一个建议是按数据工程的老规矩来先确认格式和标注完整度再谈训练。水下环境里的泄漏点和破损区域往往对比度低、目标小标注质量直接决定后面所有工作的上限。把这套数据跑通一次你收获的不仅是一个能检测的模型还有一整套处理VOC和YOLO双格式数据的标准流程。这套流程放到燃气管道、化工储罐、水下结构物检测上套路一模一样只是类别名不同。2. 拆开这份数据集VOC与YOLO两种标注格式、目录结构与7z解压2.1 两种标注格式并存是好事VOC管标注、YOLO管训练VOC格式和YOLO格式本质是同一个标注事实的两种表达。VOC格式用XML文件描述每个目标文件名、图像尺寸、目标类别、边界框坐标坐标是像素级的整数人眼看得懂也方便在图像上直接画框检查。YOLO格式则用同名的txt文件每行一条目标记录类别序号、中心点x、中心点y、宽度、高度全部是相对图像宽高的归一化小数范围在0到1之间。模型训练时读txt效率高不用解析XML但人眼直接看txt几乎看不出对错。这个数据集两种格式都给意味着你既能拿VOC格式做可视化检查和清洗又能直接拿YOLO格式启动训练不必先写转换脚本。一个典型的VOC数据集目录结构是dataset/ ├── JPEGImages/ # 原始图像jpg格式 ├── Annotations/ # VOC格式标注xml文件 ├── ImageSets/ │ └── Main/ │ ├── train.txt # 训练集图像文件名列表 │ └── val.txt # 验证集图像文件名列表 └── labels/ # YOLO格式标注txt文件 ├── leak_001.txt └── damage_002.txt对应的YOLO格式目录一般是images和labels平级dataset/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/ └── classes.txt # 类别列表一行一个类名两类别的数据集classes.txt通常就两行比如leak和damage顺序决定了txt里每行的第一个数字是0还是1。这里有个容易忽略的细节VOC格式里不存在显式的背景类没有目标就写空XMLYOLO格式里背景也是隐式的类别索引只统计实际目标类别。所以“2类别”指的是两个检测目标类别而不是“目标背景”各算一个。2.2 解压.7z先看这些校验与常用命令7z格式比zip压缩率更高数据集从几十MB压到十几MB很常见但代价是解压工具不通用。Windows上双击解压偶尔会碰到“没有权限”或“CRC校验失败”Linux服务器上更是默认没装p7zip。我一般会先把压缩包放在纯英文路径下再执行解压避免中文路径在工具链里出幺蛾子。常见做法是# Linux / macOS 环境下解压 sudo apt install p7zip-full # Debian系先装工具 7z x 水下管道泄漏破损检测数据集VOCYOLO格式2069张2类别.7z -odataset # 如果压缩包带解压密码 7z x xxx.7z -odataset -p你的密码 # 只测试压缩包完整性不实际解压 7z t xxx.7z参数说明-o后面紧跟输出目录注意-o和目录名之间不能有空格写-odataset而不是-o dataset。-p指定密码如果密码正确却反复报错问题通常出在编码或文件损坏上这在后面避坑章节展开。7z t是测试模式会逐个文件校验CRC任何文件损坏都会明确指出来。解压完成后我建议立刻做一次文件和目录计数确认解压结果是不是2069张图配齐了对应标注。Windows下如果不习惯命令行用7-Zip图形界面右键解压也可以但要注意解压设置里的“路径名称”选项如果压缩包内根目录带中文名解压出来偶尔出现文件名乱码。这种情况通常是编码表不一致导致的不是文件损坏重命名目录就能解决。2.3 读懂文件命名与类别文件先跑通一个label可视化拿到数据集后第一件正经事是把标注可视化出来而不是直接开训。找一个标注相对密集的图像文件用OpenCV把VOC的XML标注画在图上肉眼检查框的位置、大小和类别名是否合理。这一步能暴露非常多问题框是不是框住了整个目标、类别名是不是统一、有没有目标太小、有没有框明显标错。一个简单的可视化脚本import cv2 import xml.etree.ElementTree as ET import os img_dir dataset/JPEGImages ann_dir dataset/Annotations img_file damage_0001.jpg # 取一个样本 xml_file os.path.join(ann_dir, img_file.replace(.jpg, .xml)) tree ET.parse(xml_file) root tree.getroot() img cv2.imread(os.path.join(img_dir, img_file)) for obj in root.findall(object): name obj.find(name).text bndbox obj.find(bndbox) xmin int(float(bndbox.find(xmin).text)) ymin int(float(bndbox.find(ymin).text)) xmax int(float(bndbox.find(xmax).text)) ymax int(float(bndbox.find(ymax).text)) cv2.rectangle(img, (xmin, ymin), (xmax, ymax), (0, 0, 255), 2) cv2.putText(img, name, (xmin, ymin - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2) cv2.imshow(annotation check, img) cv2.waitKey(0)这段代码先把jpg对应同名XML解析出来遍历所有object节点拿到类别名和四个坐标值。坐标转int时先过一层float是因为有些标注工具会在XML里写小数直接int()会报错。可视化这一步没什么技术含量但值得对全部2069张做一遍抽样检查闭上眼随机抽30张看框和类别是否都对得上。抽样不是玄学是最低成本的标注质量保障花二十分钟看的图能给后面省下几天返工时间。VOC和YOLO两种格式在同一个数据集里通常是VOC为主、YOLO为派生所以文件的对应关系是一一对应的每张jpg对应一个xml对应一个txt。如果发现任何一对对不上先记录文件名再统一处理不要零散地改。3. 把VOC转成YOLO转换脚本与四个边界坑3.1 为什么必须转YOLO要归一化txt虽然这份数据集自带YOLO格式但真实项目中你拿到的VOC数据多半没转好所以转换脚本是必须掌握的基本功。VOC的XML里坐标是像素值依赖图像宽高才有意义YOLO的txt里坐标是归一化小数模型推理时不管输入多大都按比例映射回原图。转换的核心是除以图像宽高但这里藏着最容易翻车的地方。很多新手直接写x_center (xmin xmax) / 2 / width看起来是对的但精度和数据类型一不注意输出就会越界或产生奇怪的框。真实标注XML里可能出现三种情况坐标是整数、坐标是浮点、坐标缺了某一项。专门的清洗脚本可以一次性处理常见问题但前提是逻辑写得够稳。转换时还要注意XML里width和height节点可能与实际图片尺寸不一致所以稳妥做法是从图片本身读取宽高而不是相信XML里的size节点。3.2 转换脚本一次性处理2069张的完整代码我常用的VOC转YOLO脚本不长但每一步都带防御逻辑。把它放在数据集根目录运行后会在labels目录下生成对应的txt文件并同时输出train/val划分import os import cv2 import random import xml.etree.ElementTree as ET # 配置区 IMG_DIR JPEGImages # 原图目录 ANN_DIR Annotations # VOC xml目录 OUT_DIR labels # 输出yolo标签目录 CLASSES [leak, damage] # 类别顺序与classes.txt保持一致 os.makedirs(OUT_DIR, exist_okTrue) def voc2yolo(xml_path, img_width, img_height): tree ET.parse(xml_path) root tree.getroot() lines [] for obj in root.findall(object): name obj.find(name).text.strip() if name not in CLASSES: print(f跳过未知类别: {name} in {xml_path}) continue cls_id CLASSES.index(name) 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或超过图像边界 xmin max(0, min(xmin, img_width - 1)) xmax max(0, min(xmax, img_width - 1)) ymin max(0, min(ymin, img_height - 1)) ymax max(0, min(ymax, img_height - 1)) # 过滤掉退化框宽或高为0 if xmax xmin or ymax ymin: print(f跳过退化框: {name} in {xml_path}) continue x_center (xmin xmax) / 2.0 / img_width y_center (ymin ymax) / 2.0 / img_height box_w (xmax - xmin) / img_width box_h (ymax - ymin) / img_height # 归一化后理论上在[0,1]但浮点误差可能到1.000001做一次钳位 x_center min(max(x_center, 0.0), 1.0) y_center min(max(y_center, 0.0), 1.0) box_w min(max(box_w, 0.0), 1.0) box_h min(max(box_h, 0.0), 1.0) lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {box_w:.6f} {box_h:.6f}) return lines # 遍历所有xml生成同名的yolo标签 files [f for f in os.listdir(ANN_DIR) if f.endswith(.xml)] for i, xml_name in enumerate(files): img_name xml_name.replace(.xml, .jpg) img_path os.path.join(IMG_DIR, img_name) if not os.path.exists(img_path): print(f图像不存在: {img_name}) continue img cv2.imread(img_path) if img is None: print(f图像读取失败: {img_path}) continue h, w img.shape[:2] lines voc2yolo(os.path.join(ANN_DIR, xml_name), w, h) if lines: label_name xml_name.replace(.xml, .txt) with open(os.path.join(OUT_DIR, label_name), w) as f: f.write(\n.join(lines)) # 按8:2划分训练和验证集 random.seed(42) random.shuffle(files) train_cnt int(len(files) * 0.8) train_files files[:train_cnt] val_files files[train_cnt:] with open(train.txt, w) as f: for name in train_files: f.write(name.replace(.xml, .jpg) \n) with open(val.txt, w) as f: for name in val_files: f.write(name.replace(.xml, .jpg) \n) print(f完成: {len(files)}张, 训练{train_cnt}张, 验证{len(val_files)}张)脚本逻辑分四段解析XML取类别和坐标、钳位并过滤退化框、写txt、划分train/val。需要重点关注的是钳位和退化框过滤这能挡掉绝大多数脏标注。CLASSES列表的顺序必须与后续训练时data.yaml里的names顺序完全一致这是最常见的标签错位来源。random.seed(42)固定划分结果保证每次重跑脚本train/val一致方便复现实验。3.3 转换后立即做的三件事可视化、越界检查、类别均衡转完别急着训先做三件检查。第一件挑10张图叠加画框确认转换后的坐标和原VOC框位置一致。第二件扫描所有txt检查有没有坐标值大于1.0或小于0.0的有就说明某些坐标出了问题回到脚本找原因。第三件统计两个类别各自的样本数看看是不是严重不均衡。# 检查txt文件里是否有越界值 grep -E ^[0-1] (1\.[0-9]|[0-9]\.[0-9]{2,})$ labels/*.txt | head -20 # 统计每个类别的目标数量 cat labels/*.txt | awk {print $1} | sort | uniq -c第一行grep匹配到以1开头的越界坐标值正常范围是不大于1.0的小数出现1.2345这种基本可以确定转换过程有坐标被错误放大。第二行用awk统计txt第一列的类别编号频次如果0和1的比例超过5:1训练时就要考虑类别权重否则模型会倾向学样本多的那个类。后面的训练配置里会处理这个问题。四个边界坑里越界是最常见也是最隐蔽的。有种情况是XML里width节点写的是0脚本读到了0除以0直接报错另一种是jpg分辨率是1920x1080但XML里size节点写的是1280x720按XML的宽高归一化后框位置整体偏移。所以脚本里必须从实际图片取宽高这是统一处理的核心原则。4. 用YOLOv8训练自己的数据集目录结构、配置文件、训练命令4.1 data.yaml怎么写路径、类别、名称YOLOv8是当前处理这类数据集最顺手的训练框架理由很实际Ultralytics把数据加载、增强、训练、评估串成了一套命令对项目落地来说是黑匣子但够稳。训练前要把数据组织成YOLOv8期望的目录结构并写一份data.yaml。# data.yaml path: /home/user/dataset # 数据集根目录绝对路径最稳 train: images/train # 训练图像目录 val: images/val # 验证图像目录 nc: 2 # 类别数 names: 0: leak 1: damagepath字段可以写成相对路径但训练时当前工作目录一变就容易找不到数据我习惯写绝对路径。train和val路径是相对于path的不一定非要叫images但目录结构保持这个习惯最省事。names的索引顺序与前面转换脚本里的CLASSES一一对应顺序错一个整个模型就白训。每张图的标签路径由ultralytics自动推导images/train下的leak_001.jpg会去找labels/train下的leak_001.txt所以jpg和txt必须同名同前缀。4.2 训练命令与参数imgsz、batch、epochs、patience数据集准备完毕训练命令就一行。我用的是小模型起步因为水下管道检测场景对推理速度有要求而且2069张图不够大模型太大容易过拟合。yolo detect train \ data/home/user/dataset/data.yaml \ modelyolov8s.pt \ epochs100 \ imgsz640 \ batch16 \ patience15 \ projectruns/train \ namepipe_leak_v1modelyolov8s.pt会从官方权重继续微调这里的s是小规模再往上还有m和l。水下泄漏点通常是小目标建议先用s跑通之后如果mAP不够再换m。imgsz640是官方预训练的标准输入尺寸水下图像里的泄漏区域往往比较小用640起步合适如果原图分辨率本身很高比如2000x1500以上可以考虑imgsz768或960但显存占用会明显上升batch要相应调小。batch16是v100级别的显卡能轻松跑的数值显存小的机器改成8batch过小会影响BatchNorm的稳定性这是yolo训练中bn崩溃的一个常见诱因。patience15表示连续15个epoch验证损失没改善就提前停省时间。第一次训练建议只跑20个epoch先确认流程能走通再放开跑到100。核对一下runs/train/pipe_leak_v1目录下有没有生成confusion_matrix.png、results.png和weights/best.pt这三个文件说明训练管线正常产出。后面所有实验都基于best.pt继续不要用last.pt做验证。4.3 第一次训练的预期损失曲线怎么读results.png里有三条曲线值得盯box_loss、cls_loss、dfl_loss。正常的训练过程是这三条线在前10个epoch快速下降之后进入缓慢下降的平台期val曲线偶尔小波动属正常。如果box_loss从第5个epoch开始不再下降甚至回升多半是学习率设置不对或数据里有噪声标注。如果cls_loss下降但box_loss纹丝不动优先怀疑边界框坐标标注有系统性偏差比如所有框整体偏左或偏上。yolo损失函数在v8里已经拆成box、cls、dfl三个部分不要盯着总损失看分项看才能定位问题。另外一个常见陷阱是训练集metrics一路飙升但验证集曲线平得像一条直线这基本就是过拟合症状结合2069张的规模数据增强和早停就是为这个准备的。验证集mAP50第一轮跑到0.7以上不稀奇因为预训练权重本身见过大量通用目标真正要看的是mAP50-95那个指标更苛刻能上0.4就算这个数据集训得不错。5. 避坑解压报错、标签错位、小目标漏检的排错记录5.1 7z密码正确但解压一直报错压缩包CRC校验失败现象输入正确的密码后7z解压到一半弹出“ERROR: CRC Failed”或者Windows解压时提示“数据错误”。用7z t测试压缩包能跑但中途报CRC错误。原因最常见的是压缩包在网盘下载过程中被截断或者压缩时用的是带中文文件名的Windows环境解压到Linux时文件名编码转换出问题密码本身没错错在文件字节完整性。解决先7z t定位坏文件输出里会列出每个文件的CRC校验结果如果是单个文件坏用7z e只抽取剩余文件坏的那张图丢弃或重新下载如果是整体CRC失败基本可以确定下载不完整换下载方式重来。文件完整性这种问题解决起来麻烦但原因很朴素校验这一步永远不会白做。5.2 VOC转YOLO后框坐标大面积越界yolo训练直接警告现象训练日志出现大量WARNING ⚠️ Box coordinates out of bounds且验证集mAP低到离谱。原因XML里的width/height节点是1280x720但实际jpg是1920x1080直接按XML宽高归一化导致所有坐标被放大超过1.0或者是bndbox坐标有小数值脚本里按int解析小数点被切断框偏移。解决转换脚本统一从cv2读取的图片shape拿宽高不使用XML里的size节点所有坐标先转float再处理最后统一钳位到[0,1]。做完之后跑一遍grep越界检查确认日志干净再训练。5.3 classes.txt顺序与转换脚本不一致模型把两个类别全认成一个现象训练能跑完但混淆矩阵显示一个类别的recall几乎为0另一个类别recall很高推理时所有框都甩到第一个类别上。原因转换脚本里CLASSES列表的顺序是[leak,damage]而data.yaml里names写成了0:damage,1:leak索引0在训练时对应damage但标签txt里0代表leak整体错位。解决统一以classes.txt内容为准转换脚本和data.yaml都从同一个来源读取类别列表每次改类别后重跑转换别手动改txt。这种错误不报任何警告只能通过混淆矩阵的类别分布发现。5.4 背景区域被标成目标或泄漏点太小导致模型漏检现象训练曲线正常但推理时把管道支架、浮游生物、水下杂物都框出来真泄漏点反而漏掉。原因水下管道场景里泄漏点可能只占图像的0.5%面积而2069张图里这类小目标占比低模型在特征金字塔的高层几乎丢掉了小目标响应。解决第一把imgsz从640提到768或960让泄漏区域映射到更多像素第二用mosaic1.0和copy_paste0.3这类增强增加小目标出现频次第三统计类别数量如果两个类比例差距大在训练配置里添加cls1.2这类损失权重。如果这些做完还漏再回去看标注把那些明显被背景干扰的标注样本清理掉。5.5 水下低对比度导致泛化失败测试集mAP远低于验证集现象训练集和验证集mAP都正常但拿到真实水下环境测试检测率骤降尤其是浑浊水体和蓝色偏色场景。原因数据集里的图像光照条件相对单一模型学到了特定亮度分布下的特征遇到低对比度场景就失效水下图像的对比度和色偏变化极大数据分布差异比陆上场景大得多。解决训练前对图像做预处理增强常见做法是CLAHE对比度限制自适应直方图均衡化把对比度拉伸到统一区间训练时加入HSV增强、亮度扰动和随机仿射变换扩大模型对光照变化的容忍度。我一般会在数据管线里把CLAHE作为固定预处理而不是随机增强让每个batch的输入亮度都在相似范围内。6. 进阶用混淆矩阵和PR曲线判断模型能不能下水6.1 混淆矩阵怎么看两个类别之间的误检逻辑训练完成后runs/train/pipe_leak_v1目录里的confusion_matrix.png是整个实验最有信息量的一张图。矩阵横轴是真实类别纵轴是预测类别对角线的值越高越好。两个类别的数据集里重点看两个格子leak被预测成damage的比例、damage被预测成leak的比例。水下泄漏点和破损区域在图像上经常长得像都是暗色斑块混淆矩阵能直接告诉你模型有没有学会区分这两个类。如果这两个交叉格子的数值超过20%说明类别间的视觉差异不够明显光调参数救不回来得回到数据层面补样本或检查标注一致性。背景列也很关键背景被误检成目标的比例高说明模型产生了系统性的误报部署时会有大量虚警这个问题比漏检更难处理。6.2 用测试集跑一次严格验证看PR曲线下面的面积验证集不能代表真实分布训练完要用独立测试集跑一次确认。我用yolo detect val把测试集完整过一遍关注每个类别的AP值和PR曲线。mAP50是宽松指标目标检测领域人见人爱但参考价值有限mAP50-95更严格它要求预测框和真实框高度重合水下管道检测项目里我更看重后者。yolo detect val \ modelruns/train/pipe_leak_v1/weights/best.pt \ data/home/user/dataset/data.yaml \ splitval \ conf0.25 \ iou0.5conf0.25是默认置信度阈值水下场景误报多我会把conf调到0.35到0.4之间用recall换precisioniou0.5是NMS的IoU阈值多目标重叠场景可以调到0.45减少相邻框被吞掉的情况。输出文件里的P_curve.png和R_curve.png能看出模型在哪个置信度区间最稳如果PR曲线在置信度0.8附近突然掉头说明模型对目标不够自信部署时阈值设置在0.3到0.4比0.5更合适。最后说个我自己的习惯拿到任何数据集第一件事永远是可视化标注和格式校验模型训练反而排第二。2069张图听起来不多但格式坑和数据坑比模型坑多十倍花一小时跑通校验流程省下的是几天的返工时间。这套流程我重复了无数次现在不管是VOC还是YOLO、水下还是燃气管道都是同一个套路先看数据再谈参数。希望帮到你。本文还有配套的精品资源点击获取
返回列表