如何使用Python OpenCV在单通道图像中查找与特定值匹配的最大连通组件?

4
所以我有一个单通道图像,大部分是0(背景),一些前景像素的值如20、21、22。非零前景像素大多聚集在一起,具有相同的值。然而,图像中存在一些噪点。为了去除噪点,我想使用连通组件分析,并对于每个值(在这种情况下是20、21、22),将除最大连通组件之外的所有内容都清零。因此,最终我将拥有3个大的连通组件和没有噪点。我如何使用cv2.connectedComponentsWithStats来完成这个任务?它似乎文档不够完整,即使在查看这篇文章后,我仍然不完全理解如何解析函数的返回值。有没有一种方法可以指定函数只返回特定灰度值的连通组件?

那么只需屏蔽给定的强度并在其上运行分析呢? - Dan Mašek
所以你的意思是可能有许多值为20的不同区域,但是你只想要每个值中最大的那个? - alkasm
请问您能否添加一些示例输入图片? - Dan Mašek
1个回答

5
这是一般的方法:
  1. 创建一个新的空白图像以添加组件
  2. 循环遍历图像中每个不同的非零值
  3. 为每个值创建一个掩模(给出每个值的多个 blob)
  4. 在掩模上运行 connectedComponentsWithStats()
  5. 找到与最大面积相对应的非零标签
  6. 创建一个具有最大标签的掩模,并将该值插入到掩膜位置处的新图像中
这里讨厌的是第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)                                        # step 1
for val in np.unique(img)[1:]:                                      # step 2
    mask = np.uint8(img == val)                                     # step 3
    labels, stats = cv2.connectedComponentsWithStats(mask, 4)[1:3]  # step 4
    largest_label = 1 + np.argmax(stats[1:, cv2.CC_STAT_AREA])      # step 5
    new_img[labels == largest_label] = val                          # step 6

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以获取正确的标签。然后我们可以像往常一样进行掩码并插入值。


感谢您的详细解释,我非常感激。我还有一个问题。在这种情况下,由于掩码只有两个标签(0和val),stats是否应该始终只有2行?在这种情况下,直接使用[1]访问第1行是否有问题,而不是使用[1:]?或者我对于连通组件统计中“标签”一词的使用有误解? - Terry Martin
@Terry Martin 你误解了标签。连通组件会给每个组件都贴上一个标签。如果blob是分开的,那它们就是不同的组件。所以即使是一个3x3的图像,在左右两侧有1,在中间是0,该图像的连通组件也会在左侧有1,在中间是0,在右侧有2。每个连通组件都有一个新标签,这就是区分相同颜色组件的方法。 - alkasm

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接