如何统计给定二维数组中背景为0、主体有值的轮廓像素数量
实现方案
有两种成熟的稳定实现,都适配任意形状的二值主体,支持批量处理:
方案1:基于二值形态学运算(依赖scipy,逻辑更直观)
原理:二值图像的边缘像素,等价于「原1值区域」减去「对原1值区域做腐蚀操作后保留的核心1值区域」,剩余的像素就是所有边缘像素,直接求和就能得到总数。
支持自由切换边缘判定规则:
- 4邻域:仅上下左右相邻存在0像素就算边缘
- 8邻域:上下左右+斜向相邻存在0像素就算边缘
代码示例:
import numpy as np from scipy.ndimage import binary_erosion # 单图处理,img为shape=(H,W)的二值numpy数组,值为0/1 def count_edge_pixels_single(img, connectivity=4): # 生成对应邻域的腐蚀核 if connectivity == 4: kernel = np.array([[0,1,0], [1,1,1], [0,1,0]], dtype=np.uint8) else: # 8邻域 kernel = np.ones((3,3), dtype=np.uint8) eroded = binary_erosion(img, structure=kernel) # 边缘掩码 = 原图 - 腐蚀后图,求和就是边缘像素总数 edge_count = np.sum(img - eroded) return edge_count # 批量处理,batch_imgs为shape=(N, H, W)的批量二值数组,N为图像数量 def count_edge_pixels_batch(batch_imgs, connectivity=4): if connectivity == 4: kernel = np.array([[0,1,0], [1,1,1], [0,1,0]], dtype=np.uint8) else: kernel = np.ones((3,3), dtype=np.uint8) # binary_erosion原生支持对高维数组的最后两维做运算,无需逐图循环 eroded_batch = binary_erosion(batch_imgs, structure=kernel) # 逐张求和,返回shape=(N,)的数组,每个元素对应一张图的边缘像素总数 edge_counts = np.sum(batch_imgs - eroded_batch, axis=(1,2)) return edge_counts
方案2:基于OpenCV的轮廓检测(依赖opencv-python,适合需要同时获取轮廓坐标的场景)
如果后续需要用到轮廓的坐标信息,可以直接用OpenCV的findContours接口,统计所有轮廓点的数量即可:
import numpy as np import cv2 def count_edge_pixels_single_cv(img): # img为shape=(H,W)的二值数组,0/1或者0/255格式都支持 # RETR_TREE表示取所有层级的轮廓,CHAIN_APPROX_NONE表示保留所有轮廓点不压缩 contours, _ = cv2.findContours(img.astype(np.uint8), cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE) edge_count = sum([len(cnt) for cnt in contours]) return edge_count # 批量处理 def count_edge_pixels_batch_cv(batch_imgs): return np.array([count_edge_pixels_single_cv(img) for img in batch_imgs])
注意事项
- 两种方案的结果差异仅来自邻域判定规则,scipy方案可以自由切换4/8邻域,OpenCV的findContours默认是4邻域边缘判定
- 批量处理时如果图像数量极大,可以把batch拆分成小批次处理,避免内存溢出
- 输入的二值数组不需要提前转成0/255格式,0/1的浮点或整数数组都可以直接处理
内容的提问来源于stack exchange,提问作者Igor Kolesnikov
相关产品推荐
相关产品推荐

