简介:这份资源面向计算机视觉与智慧城市基础设施方向的开发者、学生及算法工程师,聚焦下水道管道堵塞与缺陷的自动检测问题。传统人工巡检效率低且存在安全风险,而该方案借助摄像头采集管道内部图像,结合图像预处理、模式识别与深度学习(如CNN)完成堵塞、裂缝、渗漏等缺陷的识别与严重程度评估,并涉及3D视觉、无人机等扩展思路。压缩包共9个文件,约910KB,包含6个Python脚本、2张示例图片和1份README说明文档,脚本覆盖图像归一化、圆形掩膜、时间转换、轮廓高亮与弧线检测等模块,图片可用于快速验证算法效果,文档则帮助理解项目结构与运行方式。目前已有107人学习下载,适合希望快速上手管道缺陷检测、复用图像处理与检测脚本、或作为课程设计与项目原型参考的读者。
1. 下水道管道缺陷检测这套源码,到底能跑出什么结果
城市地下管网排查最头疼的不是没相机,而是拍回来一堆模糊、反光、带水渍的管内视频,人工逐帧看一天也标不出几处堵塞。这套「缺陷检测-基于图像视觉的下水道管道堵塞+缺陷检测」资源包,走的是传统图像处理路线,不依赖深度学习训练,拿到就能在普通笔记本上跑通。它用圆形掩膜裁掉管道图像四周的无效区域,再做 RGB 归一化压掉光照不均,接着用轮廓检测和弧线检测定位堵塞物与结构性缺陷。适合做市政管网巡检、工业管道内窥检测的从业者,也适合想入门图像视觉缺陷检测的学生拿来做第一个可复现的项目。包里带了 Sample.jpg 和 Sample2.jpg 两张样例图,README.md 说明运行入口,Main.py 是主流程,其余脚本各管一个环节。下面按实际拆包顺序讲清楚每个文件干什么、参数怎么调、哪里容易翻车。
2. 拆开压缩包先看文件:六个脚本的分工与调用链
2.1 从 Main.py 反推执行顺序
拿到一个没文档的视觉项目,我习惯先看主入口调了谁。Main.py 里通常按「读图 → 归一化 → 圆形掩膜 → 缺陷检测 → 结果叠加」串起来。打开后重点找 import 了哪些本地模块,就能还原调用链。常见写法是:
# Main.py 典型结构(按资源包内脚本名推断) import cv2 import numpy as np from nomralizeRGB import normalize_rgb # 注意文件名拼写是 nomralize from circularMask import apply_circular_mask from arcDetect import detect_arc_defects from contorsHighlight import highlight_contours img = cv2.imread("Sample.jpg") img = normalize_rgb(img) # 第一步:压光照 img = apply_circular_mask(img) # 第二步:裁掉管壁外区域 defects = detect_arc_defects(img) # 第三步:弧线/裂缝检测 result = highlight_contours(img, defects) # 第四步:把缺陷画回原图 cv2.imwrite("output.jpg", result)逻辑说明:normalize_rgb 负责把三通道亮度拉平,避免管道一侧过曝一侧死黑;apply_circular_mask 用圆形掩膜把画面裁成管道内壁的圆环区域,减少管壁外噪声干扰;detect_arc_defects 针对裂缝这类弧形结构做检测;highlight_contours 把检出的轮廓用高亮色叠回原图,方便人工复核。
参数说明:cv2.imread 默认读 BGR,如果你的样例图是灰度图,后续归一化函数里要判断通道数,否则会报 shape 错误。掩膜半径一般取图像短边的 0.45~0.48 倍,留一点边缘余量,取满 0.5 会把管壁反光带进来。
2.2 文件名拼写与模块导入的坑
资源包里归一化脚本叫nomralizeRGB.py,不是normalizeRGB.py,这是原作者的拼写。你 import 时必须照抄,否则 ModuleNotFoundError。我一般会在项目根目录先跑一遍导入测试:
python -c "import nomralizeRGB, circularMask, arcDetect, contorsHighlight, timeConvert; print('all ok')"如果某个模块报缺 cv2 或 numpy,先装依赖:
pip install opencv-python numpytimeConvert.py 从名字看是处理视频时间戳或帧号转换的,如果你只跑单张图可以暂时不调它;要做视频逐帧检测时,用它把帧序号换算成实际时间,方便定位缺陷出现在第几秒。
2.3 样例图先跑通再换自己的数据
别急着拿现场视频往里灌。先用 Sample.jpg 跑一遍,确认输出图能正常生成、缺陷框位置合理,再换 Sample2.jpg 验证第二张。两张都过了,说明环境没问题。换自己数据时注意:样例图大概率是已经裁好的管道内壁正视图,如果你直接丢一张带法兰、带支架的现场图,圆形掩膜会把有效区域也裁掉。常见做法是先用 circularMask 里的调试开关把掩膜边界画出来看一眼,确认圆心和半径对得上再关掉调试。
3. 图像预处理两个核心脚本:归一化与圆形掩膜怎么调
3.1 nomralizeRGB.py 的归一化逻辑与参数
管道内窥图像最大的问题是光源偏心,一侧亮到发白,另一侧暗到看不清纹理。归一化的目的不是增强对比度,而是把整体亮度分布拉到一个稳定区间,让后续阈值分割不因光照漂移而失效。常见实现是逐通道做线性拉伸或 CLAHE。假设脚本里用的是简单 min-max 归一化:
# nomralizeRGB.py 归一化核心逻辑 def normalize_rgb(img): img = img.astype(np.float32) for c in range(3): ch = img[:, :, c] ch = (ch - ch.min()) / (ch.max() - ch.min() + 1e-6) * 255 img[:, :, c] = ch return img.astype(np.uint8)逻辑说明:逐通道减最小值除极差,把每个通道拉到 0~255。加 1e-6 是防止全黑图除零。这样处理后三通道动态范围一致,后续转灰度或做颜色阈值时不会因为某通道偏暗而漏检。
参数说明:如果你发现归一化后噪点被放大,说明原图暗部噪声重,应该先做一次高斯滤波再归一化,核大小取 3 或 5。如果管道图像本身对比度就够,可以跳过归一化直接进掩膜,避免过度拉伸把水渍反光变成假边缘。
3.2 circularMask.py 的圆心半径怎么定
圆形掩膜是这套流程里最影响检出率的环节。管道内壁在图像里通常呈一个圆或椭圆,掩膜的作用是把圆外的管壁、支架、电缆全部置黑,只保留内壁区域。核心就三行:
# circularMask.py 圆形掩膜 def apply_circular_mask(img, radius_ratio=0.46): h, w = img.shape[:2] mask = np.zeros((h, w), dtype=np.uint8) center = (w // 2, h // 2) radius = int(min(h, w) * radius_ratio) cv2.circle(mask, center, radius, 255, -1) return cv2.bitwise_and(img, img, mask=mask)逻辑说明:先建全黑掩膜,在中心画白色实心圆,再和原图做按位与,圆外变黑。center 默认取图像几何中心,适用于相机正对管道轴心的拍摄方式。
参数说明:radius_ratio 默认 0.46,意思是取短边的 46% 作半径。如果你发现掩膜后边缘还有一圈亮环,说明半径偏大,降到 0.42 试试;如果内壁有效区域被裁掉一块,说明相机没对准轴心,需要手动改 center 坐标。我一般会加一个调试分支,把 mask 边界用红色画在原图上存一张 debug 图,确认无误再跑正式流程。
3.3 预处理顺序为什么不能反
有人图省事先掩膜再归一化,结果圆外全黑区域参与 min-max 计算,把最小值拉到 0,归一化后内壁整体偏亮,阈值全乱。正确顺序一定是先归一化再掩膜,或者掩膜后只在掩膜区域内做归一化。这个顺序问题在血泪经验里排前三,换数据后检出率突然掉一半,八成是顺序反了。
4. 缺陷检测与轮廓高亮:arcDetect 和 contorsHighlight 实战
4.1 arcDetect.py 的弧线检测思路
管道裂缝在图像里表现为细长弧线,堵塞物则是不规则团块。arcDetect 从名字看侧重弧线,常见做法是 Canny 边缘 + 霍夫变换或轮廓拟合。一个可跑的简化版:
# arcDetect.py 弧线/裂缝检测 def detect_arc_defects(img, min_area=80): gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) edges = cv2.Canny(gray, 50, 150) # 双阈值边缘 contours, _ = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) defects = [] for cnt in contours: area = cv2.contourArea(cnt) if area < min_area: continue # 滤掉噪点小轮廓 perimeter = cv2.arcLength(cnt, True) circularity = 4 * np.pi * area / (perimeter * perimeter + 1e-6) if circularity < 0.3: # 越接近 0 越像细长弧线 defects.append(cnt) return defects逻辑说明:Canny 提取边缘后找外轮廓,用面积过滤噪点,再用圆度公式区分团块和细长弧线。圆度接近 1 是圆,接近 0 是细长形状,裂缝和刮痕通常圆度很低。
参数说明:Canny 的 50/150 是低/高阈值,光照均匀的图可以提到 80/200 减少假边缘;min_area 默认 80 像素,图像分辨率高时要按比例放大,比如 1080P 图可以设到 300。圆度阈值 0.3 是经验值,想只抓裂缝可以降到 0.2,想连堵塞团块一起抓就提到 0.6。
4.2 contorsHighlight.py 把缺陷画回原图
检测出轮廓后要让人能一眼看到缺陷在哪。contorsHighlight 通常做两件事:用醒目颜色描边,加编号或面积标注。
# contorsHighlight.py 轮廓高亮 def highlight_contours(img, defects, color=(0, 0, 255), thickness=2): out = img.copy() for i, cnt in enumerate(defects): cv2.drawContours(out, [cnt], -1, color, thickness) x, y, w, h = cv2.boundingRect(cnt) cv2.putText(out, f"D{i+1}", (x, y-5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 1) return out逻辑说明:drawContours 把每个缺陷轮廓描红,boundingRect 取外接框左上角,putText 在框上方标 D1、D2 编号,方便和检测报告对应。
参数说明:color 是 BGR 顺序,(0,0,255) 是红色;thickness 在 1080P 图上建议 2~3,太小看不清,太大盖住缺陷本身。编号字号 0.5 适合 720P,1080P 可以提到 0.7。
4.3 检出结果怎么判断靠不靠谱
跑完先看输出图,重点看三类误检:管壁接缝被当成裂缝、水渍反光被当成堵塞、掩膜边缘被当成缺陷。接缝误检通常是 Canny 阈值太低,水渍误检是归一化过度拉伸,掩膜边缘误检是半径偏大把边界带进来了。我一般会拿 Sample.jpg 和 Sample2.jpg 各跑三组参数,记录检出数量和误检位置,选一组在两张图上都稳定的参数作为默认值,再拿去跑现场数据。
5. 避坑与排查:这套源码最容易翻车的五个地方
5.1 现象:ModuleNotFoundError: No module named 'normalizeRGB'
原因:资源包内文件名是nomralizeRGB.py,拼写为 nomralize,import 时按正确拼写写会找不到。
解决:import 语句照抄文件名from nomralizeRGB import ...,或者把文件重命名为 normalizeRGB.py 并同步改所有引用。重命名更规范,但要注意 Main.py 里可能也写了原拼写,两处一起改。
5.2 现象:掩膜后图像全黑或只剩一个小圆
原因:radius_ratio 传了 0.05 这类过小值,或者图像短边本身很小,乘完半径只剩几像素。
解决:打印min(h, w)确认图像尺寸,radius_ratio 保持在 0.4~0.48。如果原图分辨率低于 200px,先放大再处理,否则掩膜和轮廓检测都没有足够像素支撑。
5.3 现象:Canny 边缘图全是噪点,轮廓数量几百个
原因:管道图像噪声重,或者归一化把暗部噪声放大了,Canny 低阈值 50 太低。
解决:在 Canny 前加高斯滤波cv2.GaussianBlur(gray, (5,5), 0),低阈值提到 80~100,高阈值提到 200。如果还多,用形态学开运算去掉细碎边缘。
5.4 现象:换自己的图后一个缺陷都检不出
原因:样例图是正对管道轴心的圆形视野,现场图可能是斜视、椭圆视野,圆形掩膜把有效区域裁掉了,或者缺陷在掩膜外。
解决:先关掉掩膜跑一遍,看原始检测能不能出结果。能出说明是掩膜问题,需要改圆心坐标或改用椭圆掩膜;不能出说明是阈值问题,按 5.3 调 Canny 参数。
5.5 现象:输出图缺陷框位置偏移
原因:highlight_contours 在掩膜后的图上画框,但保存时又和原图尺寸不一致,或者中途做了 resize 没同步坐标。
解决:确保从读图到保存全程尺寸不变。如果必须 resize,把缩放比例记下来,画框前把轮廓坐标乘回去。我一般会在 Main.py 开头固定img = cv2.resize(img, (640, 640)),后续所有操作都基于这个尺寸,避免坐标错乱。
6. 从单张图到批量视频:timeConvert 的用法与参数固化技巧
单张图跑通只是第一步,实际巡检拿到的是几十分钟的管内视频。timeConvert.py 就是干这个衔接的。常见实现是把帧号换算成时间戳,或者把视频按固定间隔抽帧存成图片序列。我一般这样用:
# 批量抽帧 + 时间戳记录 import cv2 from timeConvert import frame_to_timestamp # 按资源包内函数名推断 cap = cv2.VideoCapture("pipe.mp4") fps = cap.get(cv2.CAP_PROP_FPS) interval = int(fps * 2) # 每 2 秒抽一帧 idx = 0 while True: ret, frame = cap.read() if not ret: break if idx % interval == 0: ts = frame_to_timestamp(idx, fps) # 换算成 00:01:23 形式 cv2.imwrite(f"frames/{ts}.jpg", frame) idx += 1 cap.release()逻辑说明:按固定帧间隔抽帧,避免逐帧处理几万张图。frame_to_timestamp 把帧号除以 fps 得到秒数,再格式化成时分秒,文件名直接带时间戳,后面检出缺陷能对应到视频第几秒。
参数说明:interval 取 fps 的 1~3 倍,2 秒一帧适合巡检视频,缺陷不会只出现一瞬。如果管道移动快,改成 0.5 秒一帧。抽帧后批量跑 Main.py,把每张图的检出结果汇总成 CSV,字段包括文件名、缺陷编号、面积、圆度,方便排序看哪些位置缺陷最密集。
参数固化有个习惯:一旦在样例图上定好 Canny 阈值、掩膜半径、min_area 这三个值,就写进一个 config.py,所有脚本从 config 读,不要散落在各个文件里。换项目时只改 config,不动算法代码。这套源码包没带 config,我建议你自己加一个,后面维护省很多事。
从那以后我每次拿到这类图像检测包,都先跑样例图、再跑一张自己的图、最后跑一段视频抽帧,三步都过了才敢往正式数据上铺。希望帮到你。
本文还有配套的精品资源,点击获取