简介:面向管道漏水、破损检测任务的目标检测数据集,可支撑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张VOC+YOLO双格式,开箱就能喂给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.jpg,VOC的标注长这样:
<annotation> <filename>pipe_001.jpg</filename> <size> <width>1280</width> <height>720</height> <depth>3</depth> </size> <object> <name>leak</name> <bndbox> <xmin>156</xmin> <ymin>233</ymin> <xmax>452</xmax> <ymax>418</ymax> </bndbox> </object> </annotation>换成YOLO格式后,同一标注变成一行:
0 0.2375 0.4521 0.2313 0.2569第一列的0是类别索引,对应classes列表里的第0个类;后面四个数是中心点x、中心点y、框宽、框高,全部除以图片宽高做了归一化。两类格式的核心差异可以压成一张表:
| 对比项 | VOC XML | YOLO txt |
|---|---|---|
| 坐标形式 | 像素绝对坐标(xmin/ymin/xmax/ymax) | 归一化中心坐标(x_center/y_center/w/h) |
| 类别表示 | 字符串name | 整数索引,从0开始 |
| 存储位置 | Annotations/xxx.xml | labels/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 2>&1 &解压完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 -mcp936=CP936指定代码页,或者解压后用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_ok=True) 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就会大于1,YOLO训练直接报坐标越界错误。
转换完顺手做个坐标范围检查,确保所有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(f'images/{split}') label_dir = Path(f'labels/{split}') img_dir.mkdir(parents=True, exist_ok=True) label_dir.mkdir(parents=True, exist_ok=True) 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.yaml:
path: /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 data=pipeline_leak.yaml model=yolov8s.pt epochs=100 imgsz=640 batch=16 device=0参数说明:model=yolov8s.pt会先从官方下载预训练权重,如果服务器没外网,提前把权重文件放到当前目录再指定路径;imgsz=640是训练分辨率,管道缺陷普遍偏小,有条件可以试imgsz=768,但显存占用会明显上升;batch=16在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 data=pipeline_leak.yaml model=runs/detect/train/weights/best.pt跑完会在runs/detect/val目录下生成混淆矩阵图。很多新手看到混淆矩阵每一行的数字加起来不是100%就开始慌,其实这个矩阵是按行归一化的,每一行代表“该类所有真实样本中,有多少被分到了每一列”,所以每行之和才是1,整张图的元素和当然不等于1。看混淆矩阵的正确姿势是盯对角线:对角线数值越高说明该类分得越准,某个类频繁分到另一个类去,就得回去看这两个类别的标注有没有混淆。
6.2 抽检管道场景的硬样本:光照不均、锈蚀背景、小目标
验证集mAP只能说明整体水平,管道检测真正考验模型的是三类硬样本:光照不足的暗部区域、锈蚀背景下的小破损、以及远处的小目标漏水点。我的做法是单独建一个hard_samples目录,把原始图片里这三类挑出来,一张张过推理:
yolo predict model=runs/detect/train/weights/best.pt source=hard_samples save_txt=True save_conf=True然后重点看两个东西:漏检(该框没框出来)和误检(把正常管壁框成了缺陷)。漏检多的类别,优先回数据集补样本或做数据增强;误检多的类别,多半是背景和缺陷的纹理太像,考虑提高NMS阈值或加一层分类器。
从那以后我每次训练完都强制走一遍这个流程:先跑val出混淆矩阵,再抽50张硬样本手动看推理结果,确认没有系统性漏检才敢交付。这套习惯帮我挡住了好几次模型翻车,数据集的格式坑、类别坑、坐标坑,也都在前期核对中提前排掉了。希望帮到你。
本文还有配套的精品资源,点击获取