排水管道缺陷检测数据集:770张YOLO+VOC双格式实战指南

发布时间:2026/10/4 1:38:49
排水管道缺陷检测数据集:770张YOLO+VOC双格式实战指南 简介这份排水管道缺陷检测数据集面向计算机视觉学习者与目标检测工程实践者用于训练和验证管道破损识别模型覆盖裂缝、破洞、障碍、干净四类典型状态。资源同时提供VOC与YOLO两种标注格式方便直接接入主流检测框架省去格式转换环节。压缩包共约2000个文件以772个txt标签、770个xml标注和458张jpg图片为主图片与标注一一对应整体约14.53MB目录按图片、xml、txt分文件夹存放结构清晰便于检索。标注框总计1812个其中crack 1010、obstacle 539、hole 153、clean 110类别分布真实反映管道场景中的长尾特征适合做类别不平衡处理与数据增强实验。图片分辨率清晰且未做增强可作为干净基线数据使用。目前已有1012人学习下载适合需要快速搭建管道缺陷检测流程、验证模型效果或开展课程设计的读者参考。1. 排水管道缺陷检测数据集770 张 YOLOVOC 双格式到底能跑出什么手上只有几百张工业缺陷图却要快速验证一个检测模型能不能落地这是很多做管道巡检、市政排水、地下管廊视觉项目的工程师都会遇到的局面。这份排水管道缺陷检测数据集规模是 770 张同时提供 YOLO 和 VOC 两套标注格式覆盖的是管道内壁常见的结构性缺陷与功能性缺陷目标。它的价值不在于数据量有多大而在于省掉了你自己从巡检视频里抽帧、清洗、画框、转格式的那一整套脏活。适合两类人一类是想拿真实工业缺陷场景练手 YOLO 训练流程的算法工程师另一类是手上有管道检测业务、需要快速搭一个缺陷识别 demo 去验证可行性的视觉从业者。770 张不算多但双格式意味着你不用被某一个框架绑死YOLO 系直接吃 txtFaster R-CNN、SSD 这类走 VOC 的也能直接读 xml选型空间是留出来的。2. 双格式数据集的结构拆解YOLO 的 txt 和 VOC 的 xml 差在哪2.1 两种标注格式的本质区别YOLO 格式和 VOC 格式最核心的差异是坐标的表达方式。YOLO 的每张图对应一个同名 txt 文件每行是一个目标格式为类别索引 中心x 中心y 宽 高后四个值全部是相对整图宽高的归一化数值范围 0 到 1。VOC 的每张图对应一个同名 xml 文件里面用bndbox记录xmin ymin xmax ymax是绝对像素坐标同时类别名以字符串形式写在name标签里。这个差异直接决定了你换框架时要改什么。YOLO 训练时读的是归一化中心点VOC 训练时读的是左上右下角点两者之间转换必须经过「绝对坐标 ↔ 归一化」和「角点 ↔ 中心点」两次换算。很多人在转换后 mAP 掉得莫名其妙八成是宽高算反了或者忘了归一化。2.2 目录组织与文件对应关系一份规范的 YOLOVOC 双格式数据集常见目录结构是这样的dataset/ ├── images/ │ ├── 001.jpg │ ├── 002.jpg │ └── ... ├── labels_yolo/ │ ├── 001.txt │ ├── 002.txt │ └── ... ├── annotations_voc/ │ ├── 001.xml │ ├── 002.xml │ └── ... └── classes.txtclasses.txt里按行写类别名行号就是 YOLO 的类别索引。这个文件是整份数据集的「字典」训练配置里的nc类别数必须和它的行数一致否则训练时类别会错位模型学出来的东西全是乱的。2.3 用脚本核对标注一致性拿到数据集第一件事不是急着训练而是核对图片、txt、xml 三者是否一一对应。缺文件、空标注、坐标越界这三类问题在中小规模数据集里非常常见。import os from pathlib import Path img_dir Path(dataset/images) yolo_dir Path(dataset/labels_yolo) voc_dir Path(dataset/annotations_voc) imgs {p.stem for p in img_dir.glob(*.jpg)} yolos {p.stem for p in yolo_dir.glob(*.txt)} vocs {p.stem for p in voc_dir.glob(*.xml)} # 找出三者的差集定位缺失文件 print(图片有但YOLO缺:, imgs - yolos) print(图片有但VOC缺:, imgs - vocs) print(YOLO多出的:, yolos - imgs) print(VOC多出的:, vocs - imgs) # 检查YOLO标注是否有空文件或坐标越界 for txt in yolo_dir.glob(*.txt): lines txt.read_text().strip().splitlines() if not lines: print(空标注:, txt.name) continue for ln in lines: parts ln.split() if len(parts) ! 5: print(字段数异常:, txt.name, ln) continue _, cx, cy, w, h map(float, parts) if not all(0 v 1 for v in (cx, cy, w, h)): print(坐标越界:, txt.name, ln)这段脚本做了三件事用集合差集找出图片与两种标注之间的缺失对应遍历每个 YOLO txt 检查是否为空文件逐行校验字段数和归一化坐标是否落在 0 到 1 之间。跑完一遍数据集里藏着的脏数据基本就暴露出来了。参数上唯一要注意的是图片扩展名如果你的图是 png把glob(*.jpg)改掉即可。3. 从 VOC 转 YOLO 与反向转换坐标换算的完整实现3.1 VOC 转 YOLO 的换算逻辑VOC 的xmin ymin xmax ymax转 YOLO 需要四步先算中心点cx (xmin xmax) / 2、cy (ymin ymax) / 2再算宽高w xmax - xmin、h ymax - ymin然后分别除以图片宽高做归一化最后按类别索引 cx cy w h拼成一行。类别索引要从classes.txt里查不能想当然按字母序。import xml.etree.ElementTree as ET from pathlib import Path from PIL import Image classes Path(dataset/classes.txt).read_text().strip().splitlines() cls2idx {c: i for i, c in enumerate(classes)} voc_dir Path(dataset/annotations_voc) img_dir Path(dataset/images) out_dir Path(dataset/labels_from_voc) out_dir.mkdir(exist_okTrue) for xml_path in voc_dir.glob(*.xml): tree ET.parse(xml_path) root tree.getroot() # 从xml里读图片尺寸避免再开一次图 size root.find(size) iw int(size.find(width).text) ih int(size.find(height).text) lines [] for obj in root.findall(object): name obj.find(name).text if name not in cls2idx: print(未知类别:, name, xml_path.name) continue 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) cx (xmin xmax) / 2 / iw cy (ymin ymax) / 2 / ih w (xmax - xmin) / iw h (ymax - ymin) / ih lines.append(f{cls2idx[name]} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}) (out_dir / (xml_path.stem .txt)).write_text(\n.join(lines))逻辑说明用ElementTree解析 xml从size节点直接读图片宽高省去用 PIL 重新打开图片的开销。类别名通过cls2idx映射成索引遇到不在classes.txt里的类别会打印警告并跳过避免索引错位。坐标统一保留 6 位小数精度足够且不会让文件膨胀。参数上如果你的 xml 里size缺失就得改成用 PIL 打开对应图片取img.size。3.2 YOLO 转 VOC 的还原反向转换就是把上面的公式倒过来xmin (cx - w/2) * iwymin (cy - h/2) * ihxmax (cx w/2) * iwymax (cy h/2) * ih。这里有个坑算出来的坐标必须裁剪到[0, iw]和[0, ih]范围内因为浮点误差可能让xmin变成负数写进 xml 后某些框架会直接报错。from PIL import Image yolo_dir Path(dataset/labels_yolo) out_voc Path(dataset/annotations_from_yolo) out_voc.mkdir(exist_okTrue) for txt_path in yolo_dir.glob(*.txt): img_path img_dir / (txt_path.stem .jpg) iw, ih Image.open(img_path).size xml_lines [annotation, f filename{img_path.name}/filename, size, f width{iw}/width, f height{ih}/height, /size] for ln in txt_path.read_text().strip().splitlines(): idx, cx, cy, w, h ln.split() cx, cy, w, h map(float, (cx, cy, w, h)) xmin max(0, int((cx - w / 2) * iw)) ymin max(0, int((cy - h / 2) * ih)) xmax min(iw, int((cx w / 2) * iw)) ymax min(ih, int((cy h / 2) * ih)) xml_lines [ object, f name{classes[int(idx)]}/name, bndbox, f xmin{xmin}/xmin, f ymin{ymin}/ymin, f xmax{xmax}/xmax, f ymax{ymax}/ymax, /bndbox, /object] xml_lines.append(/annotation) (out_voc / (txt_path.stem .xml)).write_text(\n.join(xml_lines))关键点是max(0, ...)和min(iw, ...)这两层裁剪把浮点误差导致的越界坐标拉回合法范围。类别名用classes[int(idx)]反查所以classes.txt的顺序必须和当初生成 YOLO 标注时一致顺序变了类别就全错。3.3 转换后必须做的验证转换完不要直接开训先抽几张图把框画出来肉眼比对。用 OpenCV 把 YOLO 标注画到原图上和 VOC 标注画出来的结果叠一起看框重合就说明转换正确。import cv2 def draw_yolo(img, txt_path, color(0, 255, 0)): ih, iw img.shape[:2] for ln in Path(txt_path).read_text().strip().splitlines(): _, cx, cy, w, h map(float, ln.split()) x1 int((cx - w / 2) * iw); y1 int((cy - h / 2) * ih) x2 int((cx w / 2) * iw); y2 int((cy h / 2) * ih) cv2.rectangle(img, (x1, y1), (x2, y2), color, 2) return img img cv2.imread(dataset/images/001.jpg) img draw_yolo(img, dataset/labels_yolo/001.txt) cv2.imwrite(check_001.jpg, img)把生成的check_001.jpg打开框应该紧贴缺陷区域。如果框整体偏移或大小不对回头查归一化时用的宽高是不是搞反了——这是最高频的翻车点。4. 用这份数据集训练 YOLO配置、划分与首轮跑通4.1 训练集验证集划分770 张按 8:2 划分训练集 616 张验证集 154 张。划分要随机但可复现固定随机种子否则每次跑结果都不一样调参时根本分不清是改动生效还是划分变了。import random from pathlib import Path import shutil random.seed(42) stems [p.stem for p in Path(dataset/images).glob(*.jpg)] random.shuffle(stems) split int(len(stems) * 0.8) train, val stems[:split], stems[split:] for subset, names in [(train, train), (val, val)]: for sub in (images, labels): (Path(dataset) / subset / sub).mkdir(parentsTrue, exist_okTrue) for s in names: shutil.copy(fdataset/images/{s}.jpg, fdataset/{subset}/images/{s}.jpg) shutil.copy(fdataset/labels_yolo/{s}.txt, fdataset/{subset}/labels/{s}.txt)random.seed(42)保证每次划分一致shutil.copy保留原文件方便你后面换划分比例重跑。划分完检查两个子集的图片数加起来是不是 770少一张都说明有文件没对上。4.2 data.yaml 配置YOLO 训练靠一个 yaml 描述数据位置和类别path: ./dataset train: train/images val: val/images nc: 4 names: [deformation, crack, blockage, deposit]nc必须等于classes.txt的行数names的顺序必须和classes.txt完全一致。这里的类别名是管道缺陷场景的常见分类实际以你数据集里的classes.txt为准不要照抄。path用相对路径时训练命令要在项目根目录执行否则找不到数据。4.3 启动训练与关键参数yolo detect train \ datadataset/data.yaml \ modelyolov8n.pt \ epochs100 \ imgsz640 \ batch16 \ lr00.01 \ patience20 \ projectruns/pipe \ nameexp1参数说明modelyolov8n.pt用 nano 版起步770 张数据量下大模型极易过拟合imgsz640是通用起点管道缺陷目标通常不大可以试 640 和 800 对比batch16在 8G 显存上比较稳爆显存就降到 8lr00.01是默认初始学习率小数据集可以降到 0.005 更稳patience20表示 20 轮验证指标不升就早停省时间。首轮目标不是刷高 mAP而是确认整条链路能跑通、loss 正常下降、验证集能出框。4.4 看训练日志判断是否正常正常训练时box_loss、cls_loss、dfl_loss三条都应该下降。如果cls_loss一直不降多半是类别索引和names对不上如果box_loss震荡剧烈检查标注里有没有宽高为 0 的框。验证阶段看mAP50770 张数据首轮能到 0.5 以上就算链路健康剩下的靠调参和数据增强往上推。5. 避坑与排查这份数据集最容易踩的五个坑5.1 类别索引错位导致全盘学错现象训练 loss 能降但验证时框的位置对、类别全错或者 mAP 低得离谱。原因classes.txt的顺序和data.yaml里names的顺序不一致YOLO 按索引读类别顺序一乱全错。解决把两个文件并排打开逐行核对或者写脚本断言len(classes) nc且逐项相等训练前强制过一遍。5.2 坐标越界引发训练中断现象训练跑到某张图直接报错退出提示坐标超出范围。原因VOC 转 YOLO 时浮点误差让xmax略微超过图片宽度归一化后大于 1。解决转换脚本里对归一化结果做min(1.0, max(0.0, v))裁剪或者训练前用第 2 章的校验脚本扫一遍把越界框修掉。5.3 图片与标注文件名不匹配现象训练时提示找不到某张图的标注或者标注数量远少于图片数。原因图片是001.jpg标注却是001.JPG.txt或img_001.txtstem 对不上。解决统一用Path.stem做匹配转换和划分脚本里全部基于 stem 操作不要手写文件名拼接。5.4 小目标漏检严重现象大缺陷能检出细长裂缝、小面积沉积基本漏掉。原因770 张里小目标占比高640 分辨率下目标缩得太小特征丢失。解决把imgsz提到 800 或 1024开启 mosaic 和 copy-paste 增强或者用带 P2 小目标检测头的模型结构。这是数据规模有限时的常见取舍分辨率上去显存也跟着上去要平衡。5.5 验证集指标虚高现象验证集 mAP 很漂亮换一批真实巡检图就崩。原因770 张如果来自少数几段视频抽帧训练集和验证集高度相似等于变相泄漏。解决划分时按视频源或拍摄批次分组同一批次的图要么全进训练要么全进验证别随机打散。这个坑最隐蔽指标好看但模型没泛化能力。6. 把 770 张用出 7700 张的效果增强策略与迁移技巧数据量小是这份数据集的天花板但天花板不是不能顶。我的习惯是先把基线跑通再上增强最后用预训练权重兜底。增强这块YOLO 内置的 mosaic、mixup、随机翻转、HSV 抖动对小数据集提升明显但管道缺陷有个特殊性缺陷的形态和方向有物理意义垂直翻转可能造出真实场景里不存在的样本。所以我的做法是只开水平翻转和轻度旋转关掉垂直翻转mosaic 概率从默认 1.0 降到 0.5避免过度拼接让目标语义失真。# 在 data.yaml 同级加一个增强配置训练时通过参数覆盖 hsv_h: 0.015 hsv_s: 0.7 hsv_v: 0.4 degrees: 10.0 translate: 0.1 scale: 0.5 flipud: 0.0 # 关掉垂直翻转 fliplr: 0.5 mosaic: 0.5 mixup: 0.1参数上degrees: 10.0限制旋转角度管道缺陷旋转太大会失真flipud: 0.0是关键垂直翻转对这类场景是负增强mosaic: 0.5比默认温和mixup: 0.1轻度混合。这套配置在几百张量级的数据上通常比默认增强稳。迁移学习是另一张牌。直接用 COCO 预训练的yolov8n.pt起步比从头训收敛快得多770 张数据下这是必选项。如果 nano 版精度不够换yolov8s.pt但要注意小数据集上模型越大过拟合越快配合早停和更强的增强一起用。还有个技巧是两阶段训练先用较大学习率快速收敛再降学习率精调lr00.01跑 80 轮后接lr00.001跑 20 轮验证集指标往往能再抬一截。验证方法上别只看 mAP 一个数。把验证集的预测结果导出来按置信度排序人工看前 50 个高分框和随机 50 个低分框高分框里有没有误检、低分框里有没有漏检比看指标更能发现问题。我一般会写个脚本把预测框和真值框画在同一张图上绿色真值、红色预测重合度一眼就能看出来。从那以后我每次拿到新数据集都强制先跑一遍文件对应校验和坐标范围检查再画几张标注可视化图确认最后才动训练命令。这三步花不了十分钟但能省掉后面几小时的无效训练。希望这份 770 张的管道缺陷数据集能帮你把第一版缺陷检测模型快速立起来。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询