资讯动态

管道缺陷检测数据集实战:1717张图、7类缺陷、YOLO训练全流程解析

发布时间:2026/10/5 6:11:40 来源:尧图企业网站定制
简介面向下水管道巡检与缺陷检测任务这份数据集包含1717张清晰管廊内壁图片覆盖穿入、错口、堆积、垃圾、裂缝、泥土、树根7类典型缺陷共标注矩形框3401个其中堆积和裂缝样本量较多泥土和错口相对稀少类别不平衡现象适合检验模型的鲁棒性。所有图片均已转换为VOC与YOLO两种标准格式JPEGImages、Annotations、labels三个文件夹分门别类存放原图、XML标注和TXT标签XML便于VOC系列工具解析TXT可直接匹配YOLO训练脚本图片清晰且未经增强可自行决定预处理策略能够直接接入YOLO、Faster R-CNN等常用检测框架省去格式转换与目录整理成本。压缩包共2000个文件除核心文件外还包含数据说明与类别配置文本整体大小约61.64MB。目前已有155人浏览学习适合从事管道缺陷识别研究的开发者、算法工程师及高校相关课题使用可作为训练验证或算法对比的真实场景数据集。1. 管道缺陷检测数据集1717张图到底能帮你省多少事做市政管道AI巡检的工程师大概率都有这种经历甲方给的管道内壁视频动辄几十上百小时真正能挑出来当训练样本的干净画面一张比一张难找。我拿到这份下水管道缺陷数据集的第一反应也是这样——1717张JPG7类缺陷框全部是矩形框标注VOC和YOLO双格式交付。先说结论这份数据集的定位不是让你直接训出一个能上线的模型而是把你从“找图、清洗、转换标注格式”这三个最烦人的环节里解放出来让你把时间花在模型调优和业务适配上去。适合正在做管道CCTV检测辅助识别、市政管网AI巡检预研或者想在YOLO目标检测流程里快速验证缺陷识别效果的从业者。下面我把目录结构、标注格式、训练细节和踩过的坑一次说清楚。2. 三目录结构拆解JPEGImages、Annotations、labels 怎么对齐2.1 每个 txt 文件名对应一张图基名对齐是数据集的命根子打开压缩包你会发现里面不是散装文件而是按JPEGImages、Annotations、labels三个目录组织的。JPEGImages里是 1717 张 JPG 原图Annotations里是 1717 个 VOC 格式 XMLlabels里是 1717 个 YOLO 格式 TXT。三者的文件名一一对应比如有一张xyxr_images_guandao1211.jpg那么同目录下必然存在xyxr_images_guandao1211.xml和xyxr_images_guandao1211.txt。项目正文里列出的xyxr_images_guandao346.txt、xyxr_images_guandao972.txt这一串其实就是labels目录里按文件名排列的清单。用基名做三件套对齐是目标检测数据集交付的通用约定好处是训练框架读取图片时能靠文件基名自动找到对应的标注。我第一次拿到这个压缩包时第一件事就是写脚本统计三个目录的文件数确认都是 1717数量一致才敢往下走。如果数量对不上大概率是传输过程中丢文件后面训练时会出现「某张图没有标注」的报错。这里的另一个细节是所有图片都是原始分辨率交付没有做过缩放或增强。摘要里写「图片是否增强否」意味着你拿到的是管道内壁的真实工况画面后续做 Mosaic、随机翻转、色彩抖动这些增强操作时不会叠加二次缩放带来的畸变。这一点对裂缝这类细长目标特别重要——增强做得越狠细长框越容易变形失真。2.2 7 类缺陷的类别分布从框数就能看出哪些类最难搞标签种类是 7 类中文对照分别为穿入、错口、堆积、垃圾、裂缝、泥土、树根。下面这张表是各类别的框数统计类别名中文含义框数chuanru穿入328cuokou错口200duiji堆积884laji垃圾610liefeng裂缝836nitu泥土187shugen树根356注意这个框数统计顺序是数据集的说明文本里写的它不等于 YOLO 训练时labels/classes.txt里的类别顺序。这里先埋个伏笔你在配置 YOLO 的类别名称时必须打开labels/classes.txt按里面的顺序来不能按上面的中文习惯顺序来具体的原因和后果我在第 5 章避坑部分展开。从框数分布看duiji堆积和liefeng裂缝是高频缺陷都超过了 800 框laji垃圾 610 框中频的是shugen树根 356 框、chuanru穿入 328 框低频的是cuokou错口 200 框、nitu泥土 187 框。总框数 3401平均每张图约 1.98 个目标属于比较稀疏的检测场景。类别不均衡是这份数据集的隐藏难点。比如nitu泥土只有 187 框如果直接按默认参数训练模型对这个类的学习很容易被duiji、liefeng带偏。我一般会在训练时做两件事一是给低频类别增加损失权重二是评估阶段单独看每个类的 AP 值而不是只盯 mAP。另外实际工程里cuokou错口和chuanru穿入在视觉上容易混淆标注时区分得还算清楚但模型学起来依然会有混淆这部分我会在第 6 章给出调优思路。3. VOC与YOLO双格式解析类别序号、归一化坐标与校验脚本3.1 XML 里存了哪些信息bndbox 四值与 object 嵌套VOC 格式在这个数据集里承担的是「可读性好、方便人工核对」的角色。每个 XML 文件的顶层结构是annotation里面包含folder、filename、path、source、size和object节点。size节点里记录图片的真实宽高和通道数object节点里则是每个缺陷框的描述核心字段是name标签名和bndbox下的xmin、ymin、xmax、ymax。我打开几个 XML 看过标注质量属于干净的类型。name里的类名是英文小写形式如liefeng、shugen没有出现大小写混用。bndbox的坐标值是像素绝对值整数形式没有越界到图片宽高之外的异常值。这类数据集的 XML 通常是从标注工具导出的原始交付物没有被二次脚本改写过所以你可以放心拿它做坐标校验的基准。一个容易被忽略的点path字段里记录的通常是标注时的原始磁盘路径不同机器上打开可能不一致这不要紧因为 YOLO 训练根本不读 XMLXML 只负责存档和人工检查。如果你后续要做数据清洗或可视化审核可以直接解析 XML 里的bndbox画框预览不需要依赖 Path 字段。3.2 YOLO txt 的五个数字class_id、中心点、宽高都是归一化值YOLO 格式的每一行代表一个目标框固定是五个数字用空格分隔类别序号 中心点x 中心点y 框宽 框高类别序号是整数从 0 开始对应classes.txt里第几行。中心点和宽高都是归一化到 0~1 之间的浮点数计算方式是框中心像素x / 图片宽、框高像素 / 图片高。这一步不是我自己猜的我抽取了labels目录下的几个 txt 文件核对过数值都在 0 到 1 之间符合 YOLOv5/v8/v11 的标准格式。这套格式的好处是训练时硬件解码快不需要像 VOC 那样每次解析 XML 节点坏处是不可读——你光看一行2 0.6182 0.3341 0.0821 0.0544是看不出来这个框是垃圾还是树根的必须配合classes.txt。这个数据集里labels/classes.txt就是权威的类别顺序文件训练框架读取 txt 第一列的整数时就按它的行号映射类名。3.3 一个校验脚本把三件套全检查一遍我在处理这类双格式数据集时习惯先写一个校验脚本把「图片-标注-类别序号」三个关键点全部扫一遍。下面是精简版脚本你可以直接保存为check_dataset.py使用import os from pathlib import Path base_dir Path(管道缺陷数据集) # 改成你解压后的根目录 img_dir base_dir / JPEGImages xml_dir base_dir / Annotations label_dir base_dir / labels # 读取 classes.txt建立序号到类名的映射 classes_file label_dir / classes.txt class_list [line.strip() for line in classes_file.read_text(encodingutf-8).splitlines() if line.strip()] print(f类别数: {len(class_list)} - {class_list}) img_files sorted(img_dir.glob(*.jpg)) xml_files sorted(xml_dir.glob(*.xml)) label_files sorted(label_dir.glob(*.txt)) # 检查classes.txt 之外的 txt 文件数量应该等于图片数量 label_txt_files [f for f in label_files if f.name ! classes.txt] print(f图片数: {len(img_files)}, XML数: {len(xml_files)}, 标签数: {len(label_txt_files)}) # 逐个检查 txt 中的类别序号是否越界并统计越界情况 bad_count 0 for label_file in label_txt_files: for line in label_file.read_text(encodingutf-8).splitlines(): parts line.strip().split() if not parts: continue cls_id int(parts[0]) if cls_id 0 or cls_id len(class_list): print(f越界: {label_file.name} 中的类别序号 {cls_id} 超出范围) bad_count 1 # 检查归一化坐标是否在 0~1 区间 values list(map(float, parts[1:])) if not all(0.0 v 1.0 for v in values): print(f坐标异常: {label_file.name} 中存在越界归一化坐标) bad_count 1 if bad_count 0: print(校验通过: 类别序号和归一化坐标均正常) else: print(f发现 {bad_count} 处异常请定位后处理) # 检查基名对齐jpg 文件名去掉后缀后xml/txt 是否都存在 missing_xml 0 missing_txt 0 for img_file in img_files: stem img_file.stem if not (xml_dir / f{stem}.xml).exists(): print(f缺少XML: {stem}) missing_xml 1 if not (label_dir / f{stem}.txt).exists(): print(f缺少TXT: {stem}) missing_txt 1 print(f基名缺失统计: XML缺失{missing_xml}, TXT缺失{missing_txt})脚本先读取classes.txt建立类别序号映射列出类别总数和顺序然后比对三个目录的文件数量。接下来遍历每一个 label 文件中的每一行校验类别序号是否越界、归一化坐标是否在 0~1 区间内最后再检查图片的基名是否同时能匹配到 XML 和 TXT 文件。把脚本跑完如果输出「校验通过」说明数据集的整体格式是健康的可以放心进入训练前的数据划分。4. 用YOLOv8把管道缺陷数据集跑起来数据划分、yaml 与训练命令4.1 数据划分固定随机种子9:1 拆 train/val原数据集交付时没有划分train和val目录图片和标注都是平铺在同一个目录下的。这就需要一个划分步骤。我一般按 9:1 的比例随机拆分并且固定随机种子保证每次跑结果一致。下面是数据划分脚本输出到datasets/pipe_defect/下import random import shutil from pathlib import Path src_img Path(JPEGImages) src_label Path(labels) dst Path(datasets/pipe_defect) random.seed(42) # 固定随机种子保证可复现 img_files sorted(src_img.glob(*.jpg)) random.shuffle(img_files) val_ratio 0.1 val_count int(len(img_files) * val_ratio) val_set set(img_files[:val_count]) train_set set(img_files[val_count:]) for split, file_set in [(train, train_set), (val, val_set)]: img_out dst / split / images label_out dst / split / labels img_out.mkdir(parentsTrue, exist_okTrue) label_out.mkdir(parentsTrue, exist_okTrue) for img_file in sorted(file_set): shutil.copy2(img_file, img_out / img_file.name) label_file src_label / f{img_file.stem}.txt if label_file.exists(): shutil.copy2(label_file, label_out / label_file.name) # 注意classes.txt 也要复制过去 shutil.copy2(src_label / classes.txt, label_out / classes.txt) print(f训练集图片: {len(train_set)}, 验证集图片: {len(val_set)})脚本把图片和对应的 txt 标注文件同步复制到train/images、train/labels、val/images、val/labels四个子目录并保留一份classes.txt在labels下。这里有个细节classes.txt不属于任何一张图的标注它只提供类别索引ultralytics读取时会自动找它。如果你用的是ultralytics包要确保 YOLO 目录结构就是上面这个标准结构不要额外嵌套一层。划分比例选择 9:1 是因为这张数据集的类间不均衡比较明显验证集太小会导致低频类别在验证时压根没有样本AP 统计波动很大。如果你手头算力足也可以用 8:2但至少保证验证集里每个类别都有样本划分完可以自己统计一下val/labels里各类别的框数。4.2 写 data.yaml 和训练命令这七个类名的顺序别乱动ultralytics训练时需要一份 YAML 配置文件里面指定路径和类别清单。关键点是类别清单必须和classes.txt保持一致否则训出来的模型类别对应关系直接错位。下面是我按这个数据集写的pipe_defect.yamlpath: datasets/pipe_defect # 数据集根目录 train: train/images val: val/images names: 0: chuanru 1: cuokou 2: duiji 3: laji 4: liefeng 5: nitu 6: shugen上面的names映射是我为了演示按常见顺序写的真实顺序以你解压后的labels/classes.txt内容为准。写 YAML 之前先cat labels/classes.txt看一遍然后把里面的类别一行行贴到names下面不要自己按中文意思排序。这一点反复强调是因为漏做这一步导致的归类错位在目标检测数据集使用中属于最高频翻车现场。训练命令很简单我用的是ultralytics官方 CLIyolo train modelyolov8n.pt datapipe_defect.yaml epochs100 batch16 imgsz640 patience20参数说明modelyolov8n.pt代表从 COCO 预训练权重继续微调管道缺陷和 COCO 的 80 类几乎没有重叠所以微调的重点是让模型学会提取边缘纹理特征而不是复用类别语义epochs100是初次跑通流程的合理值不要一上来就设 300先看 100 轮的收敛曲线再决定是否加量batch16取决于显存6GB 显卡建议降到 8imgsz640是通用值如果原图分辨率更高且小目标多可以尝试 800 或 960训练时间会有增加但细长裂缝的检出会好一些。我第一轮训练用的yolov8n是为了快速验证管线跑通后换yolov8m或yolov8l做正式训练这类缺陷检测对实时性要求不高模型体积大一点无所谓精度优先。4.3 推理验证用 best.pt 跑测试图并输出指标训练完成后模型文件保存在runs/detect/train/weights/best.pt。先用几张原始图片做快速推理确认预测框和标签对得上yolo predict modelruns/detect/train/weights/best.pt sourcetest_images/ save_txtTrue save_confTrue执行后会生成带预测框的标记图片以及预测结果的 txt 文件。save_txtTrue会输出标准 YOLO 格式的预测框save_confTrue会在每行末尾追加置信度分数。这一步的意义是在进指标评估之前先用肉眼确认类别配对是否正确、框是否大概贴合缺陷区域。如果类别名字全是乱的那不用看指标了先回头查 yaml 的names顺序。验证集的 mAP 指标可以从训练日志里直接看重点关注mAP50和mAP50-95。对于管道缺陷检测mAP50更能反映框的位置质量因为缺陷框的贴合度不需要像精细分割那样苛刻。每轮训练输出的混淆矩阵图在runs/detect/train/confusion_matrix.png那个文件是判断哪些类互相混淆的最直观证据。5. 避坑清单五个最容易让管道缺陷训练翻车的标注和格式问题5.1 类别顺序错位classes.txt 不是给你按中文习惯排序的现象训练能正常跑loss 也在下降但验证时发现预测框的标签和真实目标对不上比如明明标注的是liefeng模型却反复预测成shugen。原因yaml 文件里names的顺序和labels/classes.txt不一致。数据集说明里列出的标签顺序可能只是统计用的展示顺序不是训练时要用的顺序。YOLO 格式的 txt 第一列数字是索引0 对应的类名完全由classes.txt决定。这个数据集交付时特意说明了「注意 yolo 格式类别顺序不和这个对应而以 labels 文件夹 classes.txt 为准」这句话就是专门提示你的。解决写入 yaml 前先cat labels/classes.txt把里面的类名按行贴到names下一个都不能错。我后来形成习惯拿到任何数据集第一步永远是打印classes.txt人工确认类别顺序后再写配置。5.2 图片-标注对齐失败缺失一张 xml 或 txt训练直接报错现象数据划分后训练报错No labels found in ...或者Missing annotation for image甚至ultralytics直接跳过若干图片导致训练集数量少于预期。原因压缩包在传输或解压过程中丢失了部分文件或者文件后缀大小写不一致比如某张图的 XML 是.XML后缀而代码只匹配.xml。虽然交付数据统计是 1717 对 1717但实际落地时仍可能出现个别缺失。解决训练前先跑我第 3 章的校验脚本输出三件套数量对比和基名缺失明细。只要发现缺失优先检查完整压缩包重新解压不要自己修补避免标注错配。5.3 不划分验证集就开 trainval loss 缺失比你想的更隐蔽现象训练输出只有train_loss没有val_loss或者绘图时验证曲线直接消失。原因训练配置里把val目录指定为与train相同或者根本没有拆分val目录。部分初学者为了省事直接把整个数据集丢进train导致验证集缺失但这在ultralytics里不会直接报错只会让模型过拟合得悄无声息。解决严格按 4.1 的脚本拆分train和val验证集合不要包含训练图片。验证集的大小不需要很大但必须覆盖全部 7 个类别低频类别nitu、cuokou如果在验证集里一张都没有mAP 统计会给出虚假的高分。5.4 细长裂缝与树根Mosaic 增强把小目标“吃”掉现象训练到后期liefeng和shugen的 AP 明显低于duiji和laji而且推理时大量漏检细长裂缝。原因裂缝是细长结构树根的延伸范围广。ultralytics默认开启 Mosaic 增强四张图被缩放拼接后细长的缺陷框很容易被压到几个像素宽模型根本学不到有效特征。原生数据集没有做过增强说明标注是在原始清晰图上进行的增强过度反而会破坏这些细节。解决把 Mosaic 关闭或降权。我一般这样设置在训练参数里加mosaic0.5再配合hsv_h0.02这类轻微色彩变化对管道内壁的黄色、灰褐色环境不要做大幅色偏。增强参数属于玄学不同数据集的敏感度差异很大原则是「强特征类可以增强细长框少动甚至不动」。5.5 类别不平衡带来的假收敛loss 降了但个别类 AP 是 0现象100 轮训练后整体 mAP 看着还行但单独看每个类的 AP发现nitu或cuokou的 AP 接近 0模型对这两个类几乎无识别能力。原因duiji和liefeng两类占了总框数的一半模型把容量都花在了高频类上低频类学到的东西被淹没。表现为整体 loss 在降实则是高频类在主导梯度。解决这类问题要从数据层面和损失层面同时下手。数据层面我建议使用复制粘贴增强把nitu和cuokou的缺陷框裁剪出来粘贴到没有目标的区域内生成额外的训练样本。损失层面则可以给低频类提高cls_loss权重在ultralytics中修改 loss 配置或者直接用带类别权重的训练脚本。评估时不要只看总 mAP要分别统计 7 个类的 AP 柱状图哪个类低就去补哪个类的样本。6. 进阶用法三类缺陷的细粒度调优与置信度评估6.1 用小样本类别的加权策略和复制粘贴增强把nitu、cuokou、chuanru三个低频类单独拎出来做样本增强。常见做法是把这类缺陷框从原图扣出来按照管道的纹理方向随机粘贴到其他无缺陷区域生成新图片后重新计算归一化坐标。复制粘贴增强对管道这种背景单一、缺陷区域可分离的数据特别有效因为不会出现像自然图像那样的上下文错乱。6.2 看混淆矩阵和 P/R 曲线而不是只看 mAP训练结束后confusion_matrix.png是最需要细看的图。我实际观察过shugen树根和liefeng裂缝很容易互相混淆因为树根在管道内壁往往伴随裂缝一起出现标注框经常高度重叠。此时需要在后处理上做文章对置信度介于 0.25 到 0.5 之间的预测框如果同时出现裂缝和树根两个类且 IoU 超过 0.6保留置信度高的框抑制另一个。6.3 对树根与裂缝共存的图像做后处理NMS 阈值再降一档推理时把conf_thres从默认的 0.25 降到 0.15让更多低置信度预测框进入候选池然后用更大的 NMS IoU 阈值 0.7 过滤重叠框。这个方法对裂缝这种边界模糊的目标能提升召回率但会增加误检数量建议只对难以确认的连续帧使用并配合时间维度投票。这份数据集的交付方特别声明过「不对训练精度作任何保证」翻译成工程语言就是数据只保证标注合理模型效果全看你的调优功力。从那以后我每次接到新的目标检测数据集都强制先跑一遍三件套校验脚本再打开classes.txt核对类别顺序然后才敢进训练流程。这套流程帮我避开了太多隐蔽的数据坑希望帮到你。本文还有配套的精品资源点击获取

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

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

免费获取报价 →
↑