基于Python和OpenCV实现通用形状填充及面积计算
通用形状内部空白填充方案
你的问题核心是固定尺寸的形态学闭运算无法适配不同大小的形状/空洞,导致填充失效。以下是两种不依赖固定kernel、自适应不同形状的通用方案:
方案一:基于轮廓检测与填充
通过检测形状的外层轮廓,直接填充轮廓内部区域,完全适配任意形状的大小和空洞:
import cv2 import numpy as np # 读取二值图像(假设输入为「白色轮廓+黑色内部/背景」,可根据实际调整) image = cv2.imread('binary_image.png', cv2.IMREAD_GRAYSCALE) # 可选:先做开运算去除小噪声,避免误检测小轮廓 noise_kernel = np.ones((3, 3), np.uint8) clean_image = cv2.morphologyEx(image, cv2.MORPH_OPEN, noise_kernel) # 反转图像(让轮廓为白色,背景为黑色,符合OpenCV轮廓检测的默认逻辑) inv_image = cv2.bitwise_not(clean_image) # 只检测最外层轮廓,忽略内部小轮廓(RETR_EXTERNAL参数) contours, _ = cv2.findContours(inv_image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # 创建空白画布,填充轮廓内部为白色 filled_image = np.zeros_like(image) cv2.drawContours(filled_image, contours, -1, (255), thickness=cv2.FILLED) # 统计白色像素数(即形状面积) total_area = cv2.countNonZero(filled_image) print(f"形状总面积:{total_area}")
关键说明:
RETR_EXTERNAL参数确保只提取每个形状的最外层轮廓,不会把内部空洞当成独立轮廓;- 开运算去噪是可选步骤,如果你的二值图像已经很干净可以跳过;
- 如果你的原始二值图像是「黑色轮廓+白色背景」,可以去掉反转图像的步骤,直接检测轮廓。
方案二:漫水填充法
通过填充图像的背景区域,再反转得到填充后的形状,适合背景区域明确的场景:
import cv2 import numpy as np image = cv2.imread('binary_image.png', cv2.IMREAD_GRAYSCALE) h, w = image.shape # 复制图像作为填充画布 flood_canvas = image.copy() # 创建比原图大2的掩膜,避免漫水填充时边界溢出 mask = np.zeros((h + 2, w + 2), np.uint8) # 从左上角(假设是背景区域)开始漫水填充,将背景改为白色 cv2.floodFill(flood_canvas, mask, (0, 0), 255) # 反转图像,此时原来的形状内部空洞会被填充为白色 filled_image = cv2.bitwise_not(flood_canvas) # 统计面积 total_area = cv2.countNonZero(filled_image) print(f"形状总面积:{total_area}")
关键说明:
- 确保漫水填充的起始点(示例中是
(0,0))属于背景区域,如果你的图像背景不在角落,可以换其他背景点; - 如果背景颜色不是0(黑色),需要调整
cv2.floodFill的参数匹配背景色。
为什么原方案失效?
形态学闭运算依赖固定大小的kernel:
- 当形状内部空洞比kernel大时,无法完全填充;
- 当形状之间的间距比kernel小时,会把相邻形状合并成一个;
- 无法自适应不同大小、不同复杂度的形状。
内容的提问来源于stack exchange,提问作者Daniel Agam
相关产品推荐
相关产品推荐

