这是一般的方法:
- 创建一个新的空白图像以添加组件
- 循环遍历图像中每个不同的非零值
- 为每个值创建一个掩模(给出每个值的多个 blob)
- 在掩模上运行
connectedComponentsWithStats()
- 找到与最大面积相对应的非零标签
- 创建一个具有最大标签的掩模,并将该值插入到掩膜位置处的新图像中
这里讨厌的是第5步,因为值为0通常(但不总是)是最大的组件。因此,我们需要通过面积获取最大的非零组件。
以下是我认为实现了所有内容的代码(一些示例图像会很好地确保结果正确):
import cv2
import numpy as np
img = np.array([
[1, 0, 1, 1, 2],
[1, 0, 1, 1, 2],
[1, 0, 1, 1, 2],
[1, 0, 1, 1, 2],
[1, 0, 1, 1, 2]], dtype=np.uint8)
new_img = np.zeros_like(img)
for val in np.unique(img)[1:]:
mask = np.uint8(img == val)
labels, stats = cv2.connectedComponentsWithStats(mask, 4)[1:3]
largest_label = 1 + np.argmax(stats[1:, cv2.CC_STAT_AREA])
new_img[labels == largest_label] = val
print(new_img)
展示所需的输出:
[[0 0 1 1 2]
[0 0 1 1 2]
[0 0 1 1 2]
[0 0 1 1 2]
[0 0 1 1 2]]
为了查看代码,首先我们创建一个新的标记图像,无聊地称为new_img
,填充零以后再用正确的标签填充。然后,np.unique()
在图像中找到唯一值,我取除第一个值之外的所有值;请注意,np.unique()
返回一个排序数组,因此0将是第一个值,我们不需要找到零的组件。对于每个唯一的值,创建一个由0和1填充的掩码,并在该掩码上运行连接组件。这将使用不同的标签标记每个不同的区域。然后,我们可以抓取最大的非零标记组件**,为其创建一个掩码,并将该值添加到该位置的新图像中。
** 这就是在代码中看起来奇怪的烦人部分。
largest_label = 1 + np.argmax(stats[1:, cv2.CC_STAT_AREA])
首先,您可以查看链接中的答案以了解stats
数组的形状,但每行对应一个标签(因此标签0将对应于第一行等),列由整数cv2.CC_STAT_AREA(只是4)定义。我们需要确保我们正在查看最大的非零标签,所以我只看超过第一行的行。然后,获取相应于最大区域的索引。由于我们去掉了零行,因此索引现在对应于label-1
,因此添加1以获取正确的标签。然后我们可以像往常一样进行掩码并插入值。