管道漏水检测数据集:VOC转YOLO格式与YOLOv8训练实战

发布时间:2026/9/27 23:47:02
管道漏水检测数据集:VOC转YOLO格式与YOLOv8训练实战 简介面向管道漏水、破损检测任务的目标检测数据集可支撑YOLO、Faster R-CNN等常见模型训练与评估也适合用于数据标注质量检验与模型泛化验证。内容覆盖crack、leak、no leak、water四个类别共2614张图片每张均配套VOC格式xml与YOLO格式txt标注文件全量标注框2690个其中leak框1187个、water框851个、no leak框465个、crack框187个类别分布清晰透明。压缩包共2000个文件以xml标注文件为主并含txt标签清单整体约101.44MB目录结构简明便于直接构建训练、验证与测试集。数据集内含部分增强图片且不附带训练权重或推理脚本仅提供准确合理标注使用前建议先查看预览按实际场景决定是否保留避免影响评估口径。已有1743人学习下载适合视觉算法工程师、科研人员与管道巡检项目开发者用于漏水定位、破损识别等方向的专项实验和模型迭代。1. 管道漏水检测数据集2614张VOCYOLO双格式开箱就能喂给YOLOv8做管道巡检的同行都知道漏水、破损这类缺陷样本有多难凑现场拍不够拍回来标注格式又五花八门。这份管道漏水泄漏破损检测数据集一共2614张图、4类目标VOC和YOLO两种格式都给你标好了解压之后基本不用预处理直接划分训练集就能喂给YOLOv5/YOLOv8。对做燃气管道、供水管网视觉检测的工程师来说它是现成的训练底料对刚学YOLO目标检测的新手来说它又是一份绝佳的练手数据XML和txt对照着看格式转换的坑能少踩一半。下面我从格式底细、解压校验、转训练集到避坑完整过一遍。2. 摸清数据集底细4类目标长什么样VOC与YOLO双格式对照2.1 四类标注对象与两种格式的差异这份数据集的4类目标集中在管道表面的典型缺陷上解压后建议先看根目录有没有classes.txt如果压缩包里没放就按2.2的脚本从XML里把类别名提取出来。常见的四类是漏水leak、破裂crack、锈蚀rust、污损stain这类肉眼可辨的缺陷具体以你拿到的这份为准但它一定是字符串形式的类别名而不是数字这点很重要。VOC和YOLO两种格式的本质区别在于坐标体系。VOC的XML用绝对像素坐标YOLO的txt用归一化的相对坐标。同一张pipe_001.jpgVOC的标注长这样annotation filenamepipe_001.jpg/filename size width1280/width height720/height depth3/depth /size object nameleak/name bndbox xmin156/xmin ymin233/ymin xmax452/xmax ymax418/ymax /bndbox /object /annotation换成YOLO格式后同一标注变成一行0 0.2375 0.4521 0.2313 0.2569第一列的0是类别索引对应classes列表里的第0个类后面四个数是中心点x、中心点y、框宽、框高全部除以图片宽高做了归一化。两类格式的核心差异可以压成一张表对比项VOC XMLYOLO txt坐标形式像素绝对坐标xmin/ymin/xmax/ymax归一化中心坐标x_center/y_center/w/h类别表示字符串name整数索引从0开始存储位置Annotations/xxx.xmllabels/xxx.txt适用工具LabelImg默认导出YOLO系列原生训练格式新手最常见的误区是把VOC里的xmin直接当成YOLO的x_center用这两者差了十万八千里。VOC的框是左上角和右下角两个点YOLO的框是中心点和宽高换算公式就四个x_center (xmin xmax) / 2 / img_width y_center (ymin ymax) / 2 / img_height width (xmax - xmin) / img_width height (ymax - ymin) / img_height这四个公式就是整个VOC转YOLO流程的核心后面第四章的转换脚本也是基于它。2.2 目录结构与类别名提取训练前先核对一遍解压后的目录骨架大致如下不同打包者可能略有出入但核心目录不会缺pipeline_leak/ ├── JPEGImages/ # 原图JPG格式 ├── Annotations/ # VOC标注XML文件 ├── labels/ # YOLO标注txt文件有的包不带需要自己转 └── classes.txt # 类别清单也可能没有需要从XML提取如果解压后没找到classes.txt别急着猜类别写个几行脚本遍历一遍XML把所有的name字段收集成集合这是最稳妥的做法import xml.etree.ElementTree as ET from pathlib import Path xml_dir Path(Annotations) classes set() for xml_file in xml_dir.glob(*.xml): root ET.parse(xml_file).getroot() for obj in root.findall(object): classes.add(obj.find(name).text.strip()) print(sorted(classes))这段脚本的逻辑很简单xml_dir.glob(*.xml)列出Annotations目录下所有XML文件逐个解析后把每个object里的name字段塞进集合集合天然去重。跑完输出的是字符串列表这就是这份数据集的真实类别。注意这里不要自作聪明给类别排序排序方式会直接影响后续YOLO的索引映射建议直接用打印出来的自然顺序。核对完类别后再抽查3到5个XML和同名txt看坐标值是否落在合理区间。像素坐标不会超过图片宽高归一化坐标不会超过1这两个检查花不了两分钟能帮你省掉后面训练时排查数据问题的一大把时间。3. 解压与校验7z在Windows和Linux下的正确打开姿势3.1 Windows下用7-Zip解压从安装到校验Windows用户拿到.7z文件后第一反应可能是用WinRAR或系统自带解压WinRAR新版能解部分7z但遇到大压缩包或加密头时经常翻车。我的习惯是老老实实装一个7-Zip开源免费官网下载安装包后一路下一步就行。安装完右键点击7z文件菜单里会出现“7-Zip”选项选择“Extract to”指定解压目录即可。这里有一个血泪经验解压之前先做完整性校验。打开7-Zip File Manager选中压缩包点击工具栏上的“测试”按钮它会跑一遍CRC校验。如果压缩包下载不完整这一步就会报错这时候就别浪费时间反复解压了直接重新下载源文件。另一个常见坑是文件名乱码。如果压缩包里包含中文或特殊符号文件名解压后可能出现乱码这是因为7z在Windows下默认按系统编码解压。解决办法是右键点7z文件选“7-Zip”里的“Extract to”在弹出的窗口中把“Codepage”切到UTF-8一般就能正常显示。3.2 Linux服务器解压p7zip-full命令行与三个常见报错训练YOLO基本离不开Linux服务器数据集通常也在服务器上解压。Debian/Ubuntu系安装p7zip-full一行命令sudo apt update sudo apt install -y p7zip-full装完先测完整性再解压这两步分开做便于定位问题# 第一步测试压缩包完整性 7z t pipeline_leak.7z # 第二步解压到指定目录注意-o后面没有空格 7z x pipeline_leak.7z -o/data/pipeline_leak参数说明t是test模式只校验不解压x是eXtract模式-o指定输出目录-o和路径之间不能有空格写成-o /data会报错。如果数据集文件很大建议用nohup挂后台执行防止SSH断连导致解压中断nohup 7z x pipeline_leak.7z -o/data/pipeline_leak unzip.log 21 解压完tail -f unzip.log看进度和结果。Linux下解压最常见的三个报错按现象、原因、解决拆开说现象一解压中途报“CRC Failed”或“Unexpected end of data”。原因九成是压缩包下载不完整服务器上下载大文件断流很常见。解决方法是先7z t检测如果在某个百分比位置固定报错基本可以断定源文件坏了重新下载时用wget -c断点续传。现象二密码输入正确但解压一直报错。网络上有不少同学遇到“7z压缩文件密码是正确的但一直报错”这通常不是密码问题而是压缩包本身损坏或命令行shell对特殊字符做了转义。解决方法是把密码用单引号包起来比如7z x -p你的密码或者干脆交互式输入密码避免shell转义干扰。现象三解压出来文件名全是乱码。7z在Linux下对非UTF-8编码的文件名处理不好常见做法是用7z x -mcp936CP936指定代码页或者解压后用convmv做文件名编码转换。如果只是个别文件乱码手动改名也不亏毕竟训练时只认jpg和txt文件名改了记得同步改XML里的filename引用。提示无论Windows还是Linux解压完成后都建议用du -sh看下目录大小再find . -name *.jpg | wc -l数一下图片数量核对是否为2614张防止解压中断导致图片缺失。4. VOC转YOLO训练集转换脚本、数据划分与YOLOv8配置4.1 VOC转YOLO一个直接能跑的Python脚本如果你的压缩包里已经带了labels目录跳到4.2直接划分就行如果只带了Annotations那这一步绕不开。网上类似的转换脚本很多但很多没处理边界情况我用下来这个版本最稳包含了坐标范围裁剪和跳过空标注的逻辑import xml.etree.ElementTree as ET from pathlib import Path # 配置区按自己实际路径改 XML_DIR Path(Annotations) # VOC标注目录 LABEL_DIR Path(labels) # 转换后的txt输出目录 CLASSES [leak, crack, rust, stain] # 必须与2.2步提取的类别一致顺序决定索引 def convert_one(xml_path: Path, out_dir: Path, classes: list): root ET.parse(xml_path).getroot() img_w int(root.find(size/width).text) img_h int(root.find(size/height).text) lines [] for obj in root.findall(object): name obj.find(name).text.strip() if name not in classes: continue cls_id classes.index(name) box obj.find(bndbox) xmin float(box.find(xmin).text) ymin float(box.find(ymin).text) xmax float(box.find(xmax).text) ymax float(box.find(ymax).text) # 边界裁剪防止标注坐标超出图片范围导致归一化后越界 xmin max(0, min(xmin, img_w)) xmax max(0, min(xmax, img_w)) ymin max(0, min(ymin, img_h)) ymax max(0, min(ymax, img_h)) x_center (xmin xmax) / 2 / img_w y_center (ymin ymax) / 2 / img_h w (xmax - xmin) / img_w h (ymax - ymin) / img_h lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) out_path out_dir / (xml_path.stem .txt) out_path.write_text(\n.join(lines) \n) LABEL_DIR.mkdir(exist_okTrue) for xml_file in XML_DIR.glob(*.xml): convert_one(xml_file, LABEL_DIR, CLASSES) print(f转换完成共处理 {len(list(XML_DIR.glob(*.xml)))} 个XML文件)这段脚本的关键点在两个地方一是classes.index(name)把字符串类别名映射成整数索引这个索引是训练时YOLO读到的类别编号所以CLASSES的顺序一旦定下来就不要改二是边界裁剪那句很多转换脚本漏了它如果某个XML里的xmax比图片宽度还大转换出来的w就会大于1YOLO训练直接报坐标越界错误。转换完顺手做个坐标范围检查确保所有txt里的归一化坐标都在0~1之间max_val 0 bad_files [] for txt in LABEL_DIR.glob(*.txt): for line in txt.read_text().splitlines(): vals [float(v) for v in line.split()] cur_max max(vals[1:]) if cur_max 1: bad_files.append((txt.name, line, cur_max)) max_val max(max_val, cur_max) print(f坐标最大值: {max_val:.4f}) if bad_files: print(f发现 {len(bad_files)} 个越界标注样例: {bad_files[:3]}) else: print(所有标注坐标都在合法范围内)这个检查脚本同样是必需的跑一遍能用数字确认数据质量比肉眼抽查靠谱得多。4.2 数据集划分与data.yaml参数这样配才不会跑飞转完格式后下一步是划分训练集和验证集。2614张图不算多按8:2划分足够问题是要保证划分的随机性避免同一管道的相似图片全部挤进训练集或验证集。用shutil.copy保留原始文件方便后续重新划分import random import shutil from pathlib import Path random.seed(42) # 固定种子保证结果可复现 images sorted(Path(JPEGImages).glob(*.jpg)) random.shuffle(images) val_ratio 0.2 val_count int(len(images) * val_ratio) val_set set(images[:val_count]) train_set set(images[val_count:]) for split, files in [(train, train_set), (val, val_set)]: img_dir Path(fimages/{split}) label_dir Path(flabels/{split}) img_dir.mkdir(parentsTrue, exist_okTrue) label_dir.mkdir(parentsTrue, exist_okTrue) for img in files: shutil.copy2(img, img_dir / img.name) label Path(labels) / (img.stem .txt) if label.exists(): shutil.copy2(label, label_dir / label.name) else: print(f警告: {img.name} 缺少标签文件)random.seed(42)这行别删固定种子后每次跑划分脚本得到的结果完全一样排除数据划分带来的变量。copy2会保留文件原始修改时间等元信息比直接rename安全万一划分错了还能基于原始目录重来。划分完成后在数据集根目录建一个pipeline_leak.yamlpath: /data/pipeline_leak train: images/train val: images/val nc: 4 names: [leak, crack, rust, stain]path建议用绝对路径YOLOv8对相对路径的解析偶尔出幺蛾子nc必须和4类对上names的顺序就是训练时类别索引的顺序必须和转换脚本里的CLASSES完全一致这是最容易翻车的地方后面避坑章会专门讲。训练命令按YOLOv8的标准写法来yolo detect train datapipeline_leak.yaml modelyolov8s.pt epochs100 imgsz640 batch16 device0参数说明modelyolov8s.pt会先从官方下载预训练权重如果服务器没外网提前把权重文件放到当前目录再指定路径imgsz640是训练分辨率管道缺陷普遍偏小有条件可以试imgsz768但显存占用会明显上升batch16在12G显存上跑yolov8s基本是极限再大就OOM了。注意如果解压出来的目录里已经有labels可以直接用4.2的划分脚本跳过4.1的转换别重复转导致标签错乱。5. 避坑记录7z报错、坐标越界、类别错位与BN崩溃5.1 7z密码正确但一直报错现象网络上很多人下载数据集会遇到这种情况——输入的密码明明是对的但解压到一半就报CRC Failed或者Data Error in packed file。原因排除人为输错密码后最常见的原因有两个压缩包在下载过程中损坏或者磁盘文件系统不支持大文件比如FAT32单文件不能超过4G。7z工具本身很少误报它报CRC错误基本就是数据对不上校验值。解决先用7z t跑完整性测试定位是不是压缩包问题如果是FAT32分区把压缩包拷贝到NTFS或exFAT分区再解压确认是下载损坏就用wget -c续传重下别用浏览器下载浏览器断点续传对大文件支持很差。5.2 标签txt里出现大于1的坐标现象训练启动后很快就报invalid box或box must be in [0, 1]检查labels目录发现某些txt里的w或h超过1。原因两个可能一是原始XML标注的坐标超出图片边界二是转换脚本没做裁剪直接把越界值归一化。这类问题在人工标注的数据里尤其容易出现标注员手滑把框拖出画布是常事。解决按4.1的脚本做边界裁剪把xmin/xmax/ymin/ymax都限制在图片尺寸范围内再算归一化。如果裁剪后出现宽高为零的框直接把这条标注删掉YOLO对空框容忍度很低。5.3 类别顺序错位导致模型学成四不像现象训练时loss下降很正常但val集的mAP一直上不去看混淆矩阵发现对角线全乱——比如漏水被识别成锈蚀破损被识别成泄漏。原因转换脚本里的CLASSES顺序和训练配置里data.yaml的names顺序不一致。比如转换时[leak, crack, rust, stain]txt里索引0代表leak但yaml里names写成了[crack, rust, leak, stain]训练时索引0就变成了crack。全套标签全部错位模型当然学不对。解决定死一套顺序转换时用哪套顺序训练就用哪套中途不要改。我的习惯是把类别顺序写进一个classes.txt放在数据集根目录每次训练前核对一遍。5.4 没分验证集mAP高得离谱现象训练完在train集上mAP能到0.97一跑私有的真实场景测试集就跌到0.4差距大到怀疑人生。原因数据划分出了问题最常见的是直接用全部2614张图训练没有留val或者划分时不打乱顺序同一管道的连续帧全部进了训练集网络把背景记住了而不是缺陷。解决至少要留20%做验证集划分时固定random.seed(42)保证可复现更严格的做法是按拍摄场景或管道编号做分层采样保证同一场景不会同时出现在训练集和验证集里。数据量不够时宁可少训练几十张也要保证验证集的独立性。5.5 YOLO训练中BN崩溃现象训练到十几轮时loss突然变成NaN控制台输出BatchNorm相关warning之后mAP一路归零。原因BN崩溃本质是梯度爆炸常见诱因是学习率太大、batch size太小、或输入图像里有纯黑纯白这类方差为0的极端样本。管道内壁图像光照不均很容易出现大面积纯色区域。解决先降学习率从默认的0.01降到0.0005试一轮batch size别低于8预处理里检查图像像素方差把方差过低的纯色图剔除。如果这三步都做了还崩回头查标签坐标越界的归一化坐标会输出异常梯度也是BN崩溃的常见源头。6. 验证模型的一个实操习惯先看混淆矩阵再抽检三类难样本6.1 用val命令出混淆矩阵总合不唯一先别慌训练完不要急着部署先跑一遍验证集yolo detect val datapipeline_leak.yaml modelruns/detect/train/weights/best.pt跑完会在runs/detect/val目录下生成混淆矩阵图。很多新手看到混淆矩阵每一行的数字加起来不是100%就开始慌其实这个矩阵是按行归一化的每一行代表“该类所有真实样本中有多少被分到了每一列”所以每行之和才是1整张图的元素和当然不等于1。看混淆矩阵的正确姿势是盯对角线对角线数值越高说明该类分得越准某个类频繁分到另一个类去就得回去看这两个类别的标注有没有混淆。6.2 抽检管道场景的硬样本光照不均、锈蚀背景、小目标验证集mAP只能说明整体水平管道检测真正考验模型的是三类硬样本光照不足的暗部区域、锈蚀背景下的小破损、以及远处的小目标漏水点。我的做法是单独建一个hard_samples目录把原始图片里这三类挑出来一张张过推理yolo predict modelruns/detect/train/weights/best.pt sourcehard_samples save_txtTrue save_confTrue然后重点看两个东西漏检该框没框出来和误检把正常管壁框成了缺陷。漏检多的类别优先回数据集补样本或做数据增强误检多的类别多半是背景和缺陷的纹理太像考虑提高NMS阈值或加一层分类器。从那以后我每次训练完都强制走一遍这个流程先跑val出混淆矩阵再抽50张硬样本手动看推理结果确认没有系统性漏检才敢交付。这套习惯帮我挡住了好几次模型翻车数据集的格式坑、类别坑、坐标坑也都在前期核对中提前排掉了。希望帮到你。本文还有配套的精品资源点击获取