如何使用OpenCV去除图像中的内部轮廓与边界轮廓?
解决OpenCV findContours去除边界与内部轮廓的问题
我明白你现在的需求:用findContours提取轮廓后,想要剔除掉图像的边界大轮廓,以及目标内部的孔洞轮廓,只保留中间有效的轮廓。下面我来一步步帮你实现这个需求。
核心思路:利用轮廓层级(Hierarchy)筛选
当你使用cv.RETR_TREE模式提取轮廓时,findContours会返回一个hierarchy数组,每个轮廓对应的层级信息是[next, prev, first_child, parent],我们可以通过这个信息区分不同类型的轮廓:
- 边界轮廓:通常是面积接近图像大小的最外层轮廓(对应层级中
parent=-1,且面积极大) - 内部孔洞轮廓:属于某个外层轮廓的子轮廓(
parent!=-1),且自身没有子轮廓(child=-1)
修改后的完整代码
import numpy as np import cv2 as cv # 读取图像并预处理 im = cv.imread('0.jpg') imgray = cv.cvtColor(im, cv.COLOR_BGR2GRAY) blur = cv.GaussianBlur(imgray,(5,5),0) ret3,thresh = cv.threshold(blur,0,255,cv.THRESH_BINARY+cv.THRESH_OTSU) # 提取轮廓与层级信息 contours, hierarchy = cv.findContours(thresh, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE) filtered_contours = [] img_area = im.shape[0] * im.shape[1] # 计算图像总面积 for cnt, hier in zip(contours, hierarchy[0]): cnt_area = cv.contourArea(cnt) # 1. 排除图像边界的大轮廓(面积超过图像90%的基本就是边界) if cnt_area > 0.9 * img_area: continue # 2. 排除内部孔洞轮廓:有父轮廓但没有子轮廓的轮廓 parent_idx = hier[3] child_idx = hier[2] if parent_idx != -1 and child_idx == -1: continue # 保留符合要求的轮廓 filtered_contours.append(cnt) # 绘制并保存筛选后的轮廓 cv.drawContours(im, filtered_contours, -1 ,(0,0,255), 1) cv.imwrite('image.jpg',im)
代码解释
- 预处理部分:保留了你原来的高斯模糊+OTSU阈值分割逻辑,这部分没问题,能很好地提取目标轮廓。
- 轮廓筛选:
- 用面积判断排除边界轮廓:因为图像边界的轮廓面积几乎等于图像本身的面积,用90%的阈值可以有效过滤掉它,同时不会误删正常的外层目标轮廓。
- 用层级信息排除内部孔洞:内部孔洞是外层轮廓的子轮廓,且自身没有下一级子轮廓,通过
parent_idx != -1和child_idx == -1可以精准识别这类轮廓。
- 绘制轮廓:只绘制筛选后的
filtered_contours,就得到了你想要的结果。
如果你对轮廓的层级关系还有疑问,可以打印hierarchy数组看看每个轮廓的层级值,这样能更直观地理解不同轮廓的结构~
内容的提问来源于stack exchange,提问作者Giri Kishore
相关产品推荐
相关产品推荐

