下水管道缺陷检测数据集:1717张图7类缺陷,VOC与YOLO双格式标注

发布时间:2026/9/23 22:54:03
下水管道缺陷检测数据集:1717张图7类缺陷,VOC与YOLO双格式标注 简介面向下水道管道缺陷检测任务的目标检测数据集包含1717张清晰管道巡检图片采用VOC与YOLO双格式存储覆盖穿入、错口、堆积、垃圾、裂缝、泥土、树根共7类常见缺陷矩形框标注总计3401个可满足YOLO系、Faster R-CNN、SSD等主流目标检测模型的训练与验证需求。压缩包共2000个文件以xml标注文件和txt标签文件为主jpg原图与xml一一对应目录结构按JPEGImages、Annotations、labels三层划分便于直接接入现有训练流程整体大小约61.64MB。数据未经增强矩形框标注准确且合理标签文件内含类别配置可避免自行修改类别映射的麻烦。图片分辨率清晰保留了管道原始纹理特征适合刚入门目标检测的开发者练习标注格式转换也适合需要真实管道缺陷样本的算法工程师做预训练或微调。目前已有154人学习/下载可用于教学实验或课题研究。1. 下水管道缺陷检测数据集7类1717张图的成色与用途下水管道缺陷检测这几年在市政检测圈子里需求量很大但真正能直接拿来训练的开源数据很少多数团队卡在标注这一关。这份数据集提供1717张真实管道内窥图片包含穿入、错口、堆积、垃圾、裂缝、泥土、树根七类缺陷总标注框数3401同时给出VOC格式XML与YOLO格式TXT两种标注覆盖目标检测任务从训练到验证的完整需要。适合正在做CCTV管道检测视觉模块、市政管网缺陷自动识别或者需要一份标注质量可靠的检测数据来做算法验证的工程师和研究者。拿到手之后直接划分数据集就能开训不用再从零标注图片。2. 双格式标注解析VOC与YOLO的目录结构和标签对应关系2.1 三个文件夹JPEGImages、Annotations、labels解压之后数据集根目录下的三个文件夹是目标检测任务里最经典的组织方式JPEGImages存放1717张jpg原始图片Annotations存放1717个xml标注文件labels存放1717个txt标注文件。三个文件夹里的文件名一一对应例如xyxr_images_guandao1211.jpg对应xyxr_images_guandao1211.xml和xyxr_images_guandao1211.txt。这个命名约定看似简单却在后面数据划分和训练时非常关键YOLO系列框架默认按照图片名去寻找同名的标签文件一旦文件对不上号训练就会报出大量No labels found或者直接跳过这些图片你甚至不会发现图片被静默丢弃了。labels文件夹里的txt是YOLO格式的归一化坐标Annotations里的xml是VOC格式的绝对像素坐标。两份标注描述的是同一批目标框只是坐标系和表达方式不同。YOLOv5、YOLOv8训练时直接读取labels文件夹即可如果换用Faster R-CNN、SSD或者MMDetection这类框架从Annotations解析xml是更通用的做法。双格式冗余设计的价值就在这里切换框架时不需要拿着标注文件到处找转换脚本。刚拿到数据时我建议先做一次清单对比确认三边文件数量一致都是1717ls JPEGImages | wc -l ls Annotations | wc -l ls labels | wc -l这个命令分别输出三个文件夹里的文件数量正常情况应显示1717、1717、1717。如果数量不一致说明压缩包内文件缺失或命名有误先排查这个再往下走。文件数量对不上后面所有训练步骤都会在不经意间出问题而且这类问题往往要到跑完整个训练流程才发现返工成本很高。2.2 VOC格式XML从annotation到bndbox字段打开任意一个xml文件看到的是标准VOC标注结构annotation作为根节点里面包含folder、filename、size和多个object节点。每个object节点对应一个缺陷目标核心字段就两个——name类别名和bndbox矩形框坐标。一个典型的xml长这样annotation folderJPEGImages/folder filenamexyxr_images_guandao1211.jpg/filename size width1280/width height720/height depth3/depth /size object namelaji/name bndbox xmin261/xmin ymin144/ymin xmax782/xmax ymax655/ymax /bndbox /object /annotationbndbox里的xmin、ymin、xmax、ymax是像素绝对坐标取值不会超过图片宽高。size节点保存了原始图片的分辨率这个信息在预处理阶段格外重要。比如你想把图片统一缩放到640x640同时保持目标比例不变正确做法是先读取size计算等比缩放系数再同步更新所有标注坐标直接resize而不改标注训练出来的模型预测框位置全偏。xml是整个数据集的标注权威来源当txt文件出现坐标越界、数值异常时反查xml就能确认原始标注到底是什么样。我个人有个习惯拿到任何带xml标注的数据集先抽查20到30个文件看object数量、bndbox坐标值是否有超出图片边界的异常情况。这一步花不了几分钟但能避免后面训练时出现NaN loss时手足无措。2.3 YOLO格式txt归一化坐标与类别索引labels文件夹里的txt文件每行对应一个目标框格式为五个空格分隔的数值class_index x_center y_center width height第一个数字是类别索引后四个是归一化坐标。举个例子某一行是2 0.4132 0.5431 0.2863 0.3184含义是类别索引2在classes.txt里对应duiji堆积目标框中心点位于图片宽度41.32%、高度54.31%的位置框宽为图片宽度的28.63%框高为图片高度的31.84%。所有归一化数值的取值范围都在0到1之间方便不同分辨率输入时保持一致。VOC的像素坐标抓换成YOLO归一化坐标公式看起来简单中心点坐标等于(xminxmax)/(2*图片宽度)宽度等于(xmax-xmin)/图片宽度高度和y坐标类推。但实际写脚本时最容易踩坑的是类别顺序映射下面这段转换脚本是常见做法import xml.etree.ElementTree as ET def voc_to_yolo(xml_path, out_txt_path, class_names): tree ET.parse(xml_path) root tree.getroot() size root.find(size) img_w int(size.find(width).text) img_h int(size.find(height).text) lines [] for obj in root.iter(object): name obj.find(name).text if name not in class_names: continue class_id class_names.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) x_center (xmin xmax) / 2.0 / img_w y_center (ymin ymax) / 2.0 / img_h w (xmax - xmin) / img_w h (ymax - ymin) / img_h lines.append(f{class_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) with open(out_txt_path, w) as f: f.write(\n.join(lines))这个脚本的关键逻辑是先解析xml里的像素坐标再除以图片宽高完成归一化最后按YOLO格式写出txt。注意class_names列表的传入顺序它决定了每个类别名称对应的数字索引列表里第几个元素就代表类别几。这份数据集的labels文件夹里自带一个classes.txt文件里面按顺序排列了七个类别名称转换脚本参照的就是这个顺序。实际项目中这份数据集已经同时提供VOC和YOLO两种格式不需要自己转换。但理解转换关系仍然有用因为划分数据集后要重新生成路径文件做数据增强时要同步更新标注坐标迁移到其他检测框架时也要用到同样的转换逻辑。3. 七类管道缺陷的分布统计与实际检测难点3.1 标注框数统计与类别不均衡这份数据集七个类别的标注框数是这样的类别名中文含义标注框数占比chuanru穿入3289.6%cuokou错口2005.9%duiji堆积88426.0%laji垃圾61017.9%liefeng裂缝83624.6%nitu泥土1875.5%shugen树根35610.5%总框数3401平均到1717张图上差不多每张图两个目标框。从分布来看duiji堆积和liefeng裂缝占比最高两者加起来超过一半nitu泥土和cuokou错口最少均低于6%。这种不均衡在管道缺陷数据里很典型因为管道内部最常见的问题就是沉积物堆积和结构性裂缝而泥土渗入、接口错位出现的频率本身就要低一些。类别不均衡对YOLO训练的影响是实打实的。默认设置下模型会对样本量大的类别过拟合对样本量小的类别欠拟合。尤其是nitu这种只有187框的类别训练时模型见到的机会少提取到的特征就不充分推理时误检漏检都容易发生。处理方式通常是三选一或组合使用给少数类提高分类损失权重、对包含少数类的图片做重复采样、或者用复制粘贴的增强方式扩充少数类目标。这一点后面第5章会具体展开。另外要留意数据集说明里专门提到YOLO格式的类别顺序和上面这张统计表的顺序不对应要以labels文件夹里的classes.txt为准。这个细节非常容易忽略但一旦忽略训练出的模型类别预测就是错乱的而且错得很隐蔽loss曲线看着正常画出来才知道全对不上。3.2 各类缺陷的形态特征与检测难易度七类缺陷在真实管道内窥图像里的视觉形态差异很大检测难度也完全不同这直接影响训练参数的选择。chuanru穿入指管道外物体穿入管道内部常见的是支管接入、异物贯穿。这类目标在图像里常有透视变形边界不规则有时还会被其他缺陷遮挡检测难度偏高。特别是穿入物与背景对比度低时模型很容易漏检这是需要重点关注的类别之一。cuokou错口是两段管道接口处的错位在图像中表现为断面台阶。错口特征本身比较清晰但判定依赖上下文——必须看到相邻两段管道的结构关系才能确认如果视野只覆盖了局部模型很难做出正确判断。这类目标需要模型具备较大的感受野输入分辨率低的时候特征会被削弱。duiji堆积和laji垃圾在图像中通常占据较大面积目标形态不规则但边界相对清楚。这两类检测精度容易提升难点在于堆积和垃圾的边界常常过渡模糊标注员之间的框定差异大模型学到的边界也会不稳定。换一句话说这类目标的框天然存在主观性不必强求预测框和人工标注完全重合。liefeng裂缝是最难处理的一类。裂缝在图像里往往只有几个像素宽属于典型的细小目标输入分辨率降到640x640之后裂缝特征基本被压缩没了。实测中对裂缝这类小目标训练时把imgsz提到960或1024能带来肉眼可见的改善。nitu泥土和shugen树根同样棘手。泥土在管道底部呈不规则滩涂状边缘与背景的界限模糊树根呈放射状蔓延细密的根须遍布图像矩形框只能框住主干大量根须溢出框外。这两类的标注本身就有挑战训练时注意不要因为框不准而限制了模型的学习上限。3.3 这份数据适合什么场景适合的场景很明确市政排水管道CCTV检测视频的关键帧自动识别、管网缺陷初筛、管道健康评估前的数据预处理。你可以用这份数据训练一个检测模型对视频帧先做自动筛查把包含缺陷的帧挑出来交给人工复核而不是让人逐帧盯着屏幕看。这种方式能显著降低巡检人员的工作强度在很多城市的管道检测项目中已经是实际落地路径。不适合的场景同样要说清楚。1717张静态图像不是连续视频帧序列用来做时序建模或者视频级别的缺陷识别数据量完全不够标注形式是矩形框检测不做像素级分割如果要输出缺陷的精确轮廓需要额外标注mask单一场图像来源意味着泛化边界存在直接部署到夜间光纤照明条件完全不同的新管道上效果会打折。这份数据适合作为预训练基础或者验证基准。先用它训练一个baseline模型再在自己的场景数据上做finetune比从ImageNet或COCO预训练权重起步收敛更快。数据集的特别声明里也提到不保证训练出的模型达到某个精度它只保证标注准确合理——这意味着不要神化数据本身把它当作一个可靠的标注起点就好。4. 避坑/常见问题/排查标注边界、类别对应与训练中的五个坑4.1 类别索引错位训练完才发现预测全乱现象训练时loss正常下降验证集mAP数值也不错但把预测结果画到图片上一看模型把垃圾识别成裂缝把泥土识别成堆积类别预测整体错位。原因YOLO训练读取txt时每行第一个数字代表类别索引模型按照data.yaml里names列表的下标顺序把索引映射到类别名字上。如果自己新建的data.yaml里names顺序和数据集labels文件夹里的classes.txt顺序不一致模型接受的监督信号就是错位的学习到的分类边界自然全部错乱。这个错误很隐蔽因为loss曲线看起来一切正常。解决统一以labels/classes.txt的顺序为准。训练前跑一段核对脚本把data.yaml里的names顺序和classes.txt逐行对比一个字符都不能差。classes_file labels/classes.txt data_yaml_names [chuanru, cuokou, duiji, laji, liefeng, nitu, shugen] with open(classes_file) as f: classes_txt [line.strip() for line in f.readlines()] assert classes_txt data_yaml_names, f类别顺序不一致请以classes.txt为准: {classes_txt} print(类别顺序校验通过)这个脚本做的事情很直接读取classes.txt每一行得到标准顺序与自己准备的data.yaml里的names列表做精确匹配。一旦不一致立即报错。从那以后我每次拿到新的检测数据集第一件事就是跑这个对比脚本避免在错误配置下浪费几十个小时的训练时间。4.2 多缺陷重叠区域矩形框互相覆盖现象一张图里同时存在堆积和垃圾两个缺陷区域交叠模型训练时对重叠区域预测不稳定有时候只输出其中一个框另一个被吞掉。原因标注时对重叠目标的处理方式不一致是主要原因。有人把重叠区域框给了堆积有人给了垃圾还有人用标注工具修正时把后标注的框直接覆盖了先前的框导致某个目标整体丢失。模型接收到的监督信号互相矛盾自然学不好重叠区域的归属。解决推荐做法是允许两个框同时存在各框各的区域交叠部分不做特殊处理交给模型自己学习。这类目标的置信度天然偏低这是正常现象不要因此误判为训练异常。标注完成后跑一遍按类别统计框数的脚本核对每个类的框数与数据集说明里的数字是否对应。如果某个类别的框数明显偏少大概率是有标注被覆盖了需要回到xml里检查。4.3 图像增强后标注失配训练loss异常波动现象自己做离线数据增强后开始训练loss曲线波动剧烈验证时预测框和物体位置对不上看起来模型完全没学到东西。原因对图片做了水平翻转、随机裁剪、旋转等操作但txt里的坐标没有同步更新。YOLO的txt坐标是归一化的水平翻转后x_center要变成1减去原来的值随机裁剪后整个坐标都要重新计算这一步很容易遗漏。很多人只用增强后的图片训练却忘了标注已经被无效化了。解决建议优先使用YOLO训练流程内置的数据增强mosaic、hsv变化、随机翻转等操作由框架自动同步处理标注不存在失配问题。YOLOv5和YOLOv8的hyp参数里配置了这些增强的开启和强度。如果确实需要离线增强扩充样本量推荐使用albumentations库它的BboxParams可以自动同步变换标注框比自己手写增强逻辑可靠得多。无论用哪种方式增强后至少抽样20张图把框画在图上人工确认标注和内容对应这一步不能省。4.4 验证集随机划分导致同源图泄漏现象训练集mAP在0.85以上验证集mAP只有0.6左右差距越拉越大换一组验证集结果又不一样稳定性很差。原因随机划分数据集时没有考虑同源问题。管道CCTV图像往往来自一段一段连续拍摄的视频同一段视频的相邻帧在光照、角度、场景内容上高度相似。如果相邻帧被随机分到了训练集和验证集两边模型在训练时已经见过类似画面验证指标虚高部署到新的管道视频上效果大跌。解决按文件名排序后间隔取样不要纯随机划分。这份数据集的图像命名是xyxr_images_guandao加数字编号虽然无法直接从文件名还原视频分组但按排序间隔抽样至少能让验证集的图片在时间顺序上不会和训练集靠得太近。更稳妥的做法是按源视频段分组划分一段视频的帧要么全部进训练集要么全部进验证集这样才能真实反映模型在未知场景上的泛化能力。4.5 数据完整性检查坐标越界与缺失文件现象训练过程中出现NaN loss或者某些图片没有任何预测框输出排查时发现部分txt文件缺失或者txt里的坐标值大于1、类别索引超出范围。原因数据在打包、传输过程中可能出现文件缺失标注工具在导出时也可能产生极少数的异常行。数量匹配关系在文件夹层面看起来是对的但个别txt的内容可能已经损坏。解决训练前跑一遍完整性检查脚本逐行解析每个txt文件确认每张图都有对应标注、每行五个字段、坐标在0到1之间、类别索引在0到6之间。import os img_dir JPEGImages txt_dir labels errors [] for img_name in os.listdir(img_dir): img_id os.path.splitext(img_name)[0] txt_path os.path.join(txt_dir, img_id .txt) if not os.path.exists(txt_path): errors.append(f{img_id}.txt 缺失) continue with open(txt_path) as f: for line in f: parts line.strip().split() if len(parts) ! 5: errors.append(f{img_id}.txt 行格式错误: {line.strip()}) continue cls_id int(parts[0]) coords list(map(float, parts[1:])) if cls_id 0 or cls_id 6: errors.append(f{img_id}.txt 类别索引越界: {cls_id}) if any(c 0 or c 1 for c in coords): errors.append(f{img_id}.txt 坐标越界: {line.strip()}) if errors: for e in errors[:20]: print(e) print(f共发现 {len(errors)} 个问题) else: print(数据集完整性检查通过)这段脚本检查三类问题txt文件是否存在、每行字段数是否等于5、坐标和类别编号是否越界。跑完脚本之后还缺一道工序——脚本检查不出的问题坐标数值正常但框的位置与图上目标不匹配。这种只能抽样画框人工对比。我一般至少抽30张图片把txt坐标还原成矩形框画在图上与原始标注对比这能避免绝大多数标注质量问题。5. 用这份数据训练YOLO模型划分与参数设置5.1 数据集划分按文件名排序间隔取样拿到数据后不要直接训练先划分train和val目录。划分方式直接决定验证指标的可信度。推荐的做法是先把所有图片按文件名排序再每隔固定数量抽一张作为验证集。这样做既保证了验证集与整体数据分布一致又能在一定程度上避免相邻帧泄漏。import os import shutil random_seed 42 img_dir JPEGImages label_dir labels train_img_dir train/images val_img_dir val/images train_label_dir train/labels val_label_dir val/labels for d in [train_img_dir, val_img_dir, train_label_dir, val_label_dir]: os.makedirs(d, exist_okTrue) images sorted(os.listdir(img_dir)) val_indices set(range(0, len(images), 10)) # 每10张抽1张进验证集 for idx, img_name in enumerate(images): img_id os.path.splitext(img_name)[0] label_name img_id .txt if idx in val_indices: shutil.copy(os.path.join(img_dir, img_name), os.path.join(val_img_dir, img_name)) shutil.copy(os.path.join(label_dir, label_name), os.path.join(val_label_dir, label_name)) else: shutil.copy(os.path.join(img_dir, img_name), os.path.join(train_img_dir, img_name)) shutil.copy(os.path.join(label_dir, label_name), os.path.join(train_label_dir, label_name)) print(f训练集图片数: {len(os.listdir(train_img_dir))}) print(f验证集图片数: {len(os.listdir(val_img_dir))})这段代码里的关键逻辑是val_indices set(range(0, len(images), 10))意思是按下标每隔10张取一张最终约10%的图片进入验证集。排序使用sorted(os.listdir(img_dir))确保文件名数字部分按字典序排列从而让验证集尽可能均匀分布在整体序列里。如果不排序直接shuffle相邻帧散布到两边的风险会大大增加。10%的验证集比例对这个数据规模是合理的171张验证图足够反映模型水平。如果想做更严格的测试集评估可以再划分出一部分test目录比如每15张取一张进test剩下再按9比1分train和val。注意这里的random_seed变量在实际项目中应该传给数据划分脚本使用保证每次划分结果一致后续复现实验结果才有对比价值。5.2 YOLOv8训练参数设置以YOLOv8为例基础训练命令长这样yolo train \ modelyolov8s.pt \ datapipe.yaml \ epochs150 \ imgsz1024 \ batch16 \ lr00.01 \ lrf0.01 \ optimizerSGD \ pretrainedTrue参数含义逐个解释。modelyolov8s.pt表示加载s尺寸的预训练权重s模型在速度和精度之间比较均衡这个小规模数据集不需要上yolov8l或yolov8x收敛慢且容易过拟合。pretrainedTrue是关键使用COCO预训练权重做迁移学习初始化模型一开始就具备通用视觉特征提取能力用管道数据微调后收敛速度远快于从零训练。epochs150看起来偏多但实际训练中一般50到80个epoch就能收敛多设置一些可以让模型跑完学习率衰减周期。要留意loss曲线验证集mAP不再上升甚至开始下降的时间点就是早停位置。imgsz1024推荐优先使用前文已经说过裂缝、树根这类细小目标对输入分辨率敏感1024比默认的640有肉眼可见的提升。代价是显存占用上升batch16对应单卡12GB显存如果显存只有8GBimgsz保持1024的情况下batch要降到8或者把imgsz降到768。datapipe.yaml文件内容如下训练脚本根据这个文件定位数据目录和类别名称train: train/images val: val/images nc: 7 names: [chuanru, cuokou, duiji, laji, liefeng, nitu, shugen]注意names列表的顺序必须与labels/classes.txt完全一致这是整个训练配置里最容易出错的地方第4章已经踩过这个坑。训练完成后权重会自动保存在runs/detect目录下用验证集做评估时框架会自动输出mAP50、mAP50-95、precision、recall这些指标。5.3 类别不平衡处理损失权重、重复采样与Copy-Paste上一章提过duiji有884框nitu只有187框类别差距接近5倍。YOLO默认按均匀权重训练模型自然偏向多数类少样本类别的召回率会明显偏低。对管道缺陷检测来说少样本类别往往是实际重点——比如错口和泥土这类结构性缺陷漏检代价很大所以需要主动干预。个别损失权重是影响最小的干预方式yolo train \ modelyolov8s.pt \ datapipe.yaml \ epochs150 \ imgsz1024 \ batch16 \ cls0.7cls参数控制分类损失在整个损失函数中的占比调高它对类别不平衡有约束效果但幅度有限它更多影响的是模型对难分类样本的关注程度。真正有效的是重复采样训练时把包含nitu或cuokou目标的图片复制若干份加入训练集模型和这两类打交道的机会多了特征学习自然更充分。具体做法是在划分数据集时对包含指定类别的图片做多次保留副本实现起来不复杂但注意不要把所有训练集翻倍那样训练时间线性增长却不一定带来对多数类的提升。Copy-Paste增强适合极端不平衡场景把一个包含树根或泥土目标的小图块裁出来粘贴到不含该目标的训练图上同时把标注框也复制过去。对背景简单、目标形态独立的情况效果不错。不过要限制粘贴目标的大小和位置不要把一个巨大的目标硬塞进完全不相关的场景里否则模型学到的上下文关系是错的反而损害检测精度。我一般优先做重复采样Copy-Paste在少数类样本极少的最后一公里才用。6. 从1717张到可部署模型置信度阈值与验证习惯训练结束后最后一步是调整推理时的置信度阈值。YOLO默认conf0.25这个值对duiji和liefeng这类大样本类别很友好但对nitu、cuokou这些少数类来说模型输出的置信度普遍偏低0.25的阈值会砍掉大量正确预测。管道缺陷场景里漏检代价远高于误检一个被漏掉的裂缝可能意味着后续维护计划被延误。我的做法是在验证集上按类别分别统计precision和recall随置信度变化的曲线取每个类别recall开始明显下降之前的置信度作为该类的部署阈值。具体操作时可以把验证集图片批量跑一遍推理输出结果保存为json再用脚本按类别分组统计。如果嫌烦先按类别设两档阈值大样本类别维持0.25小样本类别降到0.1部署后再根据现场反馈逐步调整。不要指望一套阈值通吃所有类别那是偷懒的做法。还有一个习惯非常重要把模型的预测结果落到图片上不要只看mAP数字。mAP是统计指标很多时候数字好看但实际效果差尤其对小目标密集场景要人眼去判断才有实感。yolo predict \ modelbest.pt \ sourceval/images/xyxr_images_guandao1211.jpg \ conf0.1 \ saveTrue跑完生成带预测框的图片重点检查三个地方重叠缺陷区域是否两个框都在、细小裂缝是否检出、泥土和堆积的边界框是否合理。这三个点直接从视觉上判断比任何指标都直观。用一句话复盘整体流程解压后先核对三个文件夹数量一致然后确认类别顺序按排序间隔划分数据集写data.yaml用imgsz1024训练最后在验证集上按类别调整置信度。这套流程走完之后模型在真实管道场景里才算真正能用。我接手这份数据的时候第一版训练出来mAP数字挺漂亮一部署到新管道视频上就露馅裂缝漏检严重。后来排查发现是验证集划分时随机分配导致同源帧泄漏指标虚高掩盖了真实泛化能力。从那以后我每次处理检测数据集都强制先走完整性检查和类别顺序核对再动手训练最后一定把预测框画出来目检一遍。数据集的标注质量再好也不如自己亲手验证一遍来得安心。希望帮到你。本文还有配套的精品资源点击获取