如何仅用Numpy提取二值图像外轮廓并填充内部区域?
仅用Numpy实现二值图像孔洞填充方案
要实现填充车轮内部孔洞的效果,核心思路是泛洪填充法:从图像边缘的背景区域出发,标记所有能连通到边缘的背景,剩下未被标记的区域(包括原前景和内部孔洞)就是需要保留的前景区域。以下是具体实现步骤和代码:
实现步骤
- 准备二值图像数组:假设输入图像已转为Numpy数组
img,其中0代表背景(黑色),255代表前景(白色)。 - 创建掩码数组:初始化一个与图像同形状的全0掩码
mask,用于标记连通到边缘的背景区域。 - 标记边缘背景点:提取图像四周边缘中属于背景的点,作为泛洪填充的起始点。
- 泛洪填充遍历:通过队列遍历所有连通的背景点,将其在掩码中标记为1。
- 生成填充后图像:将掩码取反,得到包含原前景和填充孔洞的区域,再映射为二值图像。
完整代码
import numpy as np def fill_holes_with_numpy(img): # 确保图像是二值单通道数组 img = img.astype(np.uint8) h, w = img.shape mask = np.zeros_like(img, dtype=np.uint8) # 找到边缘的背景点(值为0的点) edge_points = [] # 第一行和最后一行 for x in range(w): if img[0, x] == 0: edge_points.append((0, x)) if img[h-1, x] == 0: edge_points.append((h-1, x)) # 第一列和最后一列(排除已加入的行端点) for y in range(1, h-1): if img[y, 0] == 0: edge_points.append((y, 0)) if img[y, w-1] == 0: edge_points.append((y, w-1)) # 泛洪填充:用队列遍历连通背景点 queue = edge_points while queue: y, x = queue.pop(0) if mask[y, x] == 1: continue mask[y, x] = 1 # 检查上下左右四个邻居 for dy, dx in [(-1,0), (1,0), (0,-1), (0,1)]: ny, nx = y + dy, x + dx if 0 <= ny < h and 0 <= nx < w: if img[ny, nx] == 0 and mask[ny, nx] == 0: queue.append((ny, nx)) # 生成填充后的图像:掩码取反,背景为0,前景为255 filled_img = (1 - mask) * 255 return filled_img.astype(np.uint8) # 示例使用(假设已读取图像为Numpy数组) # from PIL import Image # img = np.array(Image.open("原始图像路径").convert("L")) # img = np.where(img < 128, 0, 255) # 转为二值图像 # filled_img = fill_holes_with_numpy(img) # Image.fromarray(filled_img).save("填充后图像路径")
代码说明
- 泛洪填充部分用队列实现广度优先遍历,确保所有连通到边缘的背景都被标记。
- 最终通过掩码取反,将未被标记的区域(原前景+内部孔洞)全部转为白色,完美匹配目标图像效果。
- 全程仅依赖Numpy,无需其他第三方图像处理库。
内容的提问来源于stack exchange,提问作者Qimin Chen
相关产品推荐
相关产品推荐

