如何从cv2.findContours返回结果重建包含孔洞的原始图像
OpenCV轮廓还原带孔洞二值图解决方案
核心问题
你遇到的错误主要来自两点:
- 轮廓是在带1像素padding的12x12输入图上检测的,绘制到10x10原始尺寸图时没有做坐标偏移校正
- 没有利用轮廓层级区分外轮廓和孔洞,填充规则不符合需求
完整实现方案
如果你需要单独获取外轮廓、孔洞的坐标,使用分层绘制的方案:
import cv2 import numpy as np # 测试数据生成 def a_small_hole_with_diagonal_border() -> np.ndarray: bitmask = np.zeros((10, 10), dtype=np.uint8) + 255 indices = [(2, 2), (2, 3), (2, 4), (2, 5), (2, 6), (3, 4), (3, 5), (3, 6), (4, 4), (4, 5), (4, 6)] row_indices, col_indices = zip(*indices) bitmask[row_indices, col_indices] = 0 bitmask[5:, 7:] = 0 return bitmask bitmask = a_small_hole_with_diagonal_border() padded_bitmask = np.zeros((bitmask.shape[0] + 2, bitmask.shape[1] + 2), dtype=bitmask.dtype) padded_bitmask[1:-1, 1:-1] = bitmask # 检测轮廓 contours, hierarchy = cv2.findContours( image=padded_bitmask, mode=cv2.RETR_TREE, method=cv2.CHAIN_APPROX_SIMPLE ) # 初始化还原画布 restored = np.zeros_like(bitmask) for idx, cnt in enumerate(contours): # 校正坐标:减去padding的1像素偏移,匹配原始10x10图像坐标 cnt_corrected = cnt - 1 # hierarchy[0][idx][3]为-1表示当前是最外层外轮廓 if hierarchy[0][idx][3] == -1: cv2.fillPoly(restored, [cnt_corrected], 255) # 有父轮廓的为孔洞,填充为黑色挖空 else: cv2.fillPoly(restored, [cnt_corrected], 0) # 验证还原结果和原图完全一致 print(np.array_equal(restored, bitmask)) # 输出为True
其中校正后的cnt_corrected就是你需要的孔洞最外侧边界坐标,直接使用即可。
最简实现
如果你不需要单独提取轮廓坐标,仅需还原二值图,可以直接调用cv2.drawContours自带的层级绘制能力,无需手动处理轮廓:
padded_restored = np.zeros_like(padded_bitmask) # maxLevel=1表示绘制最外层轮廓和它的直接子级(孔洞),OpenCV自动处理填充规则 cv2.drawContours( padded_restored, contours, -1, 255, thickness=cv2.FILLED, hierarchy=hierarchy, maxLevel=1 ) # 裁剪掉padding得到最终结果 restored = padded_restored[1:-1, 1:-1]
内容的提问来源于stack exchange,提问作者RunOrVeith
相关产品推荐
相关产品推荐

