简体   繁体   English

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

[英]How to use python OpenCV to find largest connected component in a single channel image that matches a specific value?

So I have a single channel image that is mostly 0s (background), and some values for foreground pixels like 20, 21, 22. The nonzero foreground pixels are mostly clustered together with other foreground pixels with the same value. 所以我有一个主要是0s(背景)的单通道图像,以及前景像素的一些值,如20,21,22。非零前景像素主要与其他具有相同值的前景像素聚集在一起。 However, there is some noise in the image. 但是,图像中存在一些噪音。 To get rid of the noise, I want to use connected components analysis, and for each value (in this case 20, 21, 22), zero out everything but the largest connected component. 为了消除噪声,我想使用连通分量分析,并且对于每个值(在这种情况下为20,21,22),除了最大的连通分量之外,将所有内容归零。 So in the end, I will have 3 large connected components and no noise. 所以最后,我将有3个大型连接组件,没有噪音。 How would I use cv2.connectedComponentsWithStats to accomplish this? 我如何使用cv2.connectedComponentsWithStats来完成此任务? It seems poorly documented and even after looking at this post , I don't fully understand how to parse the return value of the function. 它看起来很难记录,甚至在查看这篇文章之后 ,我还不完全了解如何解析函数的返回值。 Is there a way to specify to the function that I only want connected components matching a specific greyscale value? 有没有办法指定函数我只想要连接的组件匹配特定的灰度值?

Here's the general approach: 这是一般方法:

  1. Create a new blank image to add the components into 创建一个新的空白图像以添加组件
  2. Loop through each distinct non-zero value in your image 循环遍历图像中每个不同的非零值
  3. Create a mask for each value (giving the multiple blobs per value) 为每个值创建一个掩码(为每个值提供多个blob)
  4. Run connectedComponentsWithStats() on the mask 在掩码上运行connectedComponentsWithStats()
  5. Find the non-zero label corresponding to the largest area 找到与最大区域对应的非零标签
  6. Create a mask with the largest label and insert the value into the new image at the masked positions 创建具有最大标签的蒙版,并将值插入到蒙版位置的新图像中

The annoying thing here is step 5, because the value of 0 will usually, but not always be the largest component. 这里讨厌的事情是第5步,因为0的值通常是,但并不总是最大的组件。 So we need to get the largest non-zero component by area. 因此,我们需要按区域获得最大的非零组件。

Here's some code which I think achieves everything (some sample images would be nice to be sure): 这里有一些我认为可以实现的代码(一些示例图像可以确定):

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)

Showing the desired output: 显示所需的输出:

[[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]]

To go through the code, first we create the new labeled image, unimaginatively called new_img , filled with zeros to be populated later by the correct label. 要完成代码,首先我们创建一个新的标记图像,无法想象地称为new_img ,填充零,以便稍后由正确的标签填充。 Then, np.unique() finds the unique values in the image, and I'm taking everything except the first value; 然后, np.unique()在图像中找到唯一的值,并且除了第一个值之外我正在处理所有内容; note that np.unique() returns a sorted array, so 0 will be the first value and we don't need to find components of zero. 请注意, np.unique()返回一个已排序的数组,因此0将是第一个值,我们不需要查找零组件。 For each unique val, create a mask populated with 0s and 1s, and run connected components on this mask. 对于每个唯一的val,创建一个填充0和1的掩码,并在此掩码上运行连接的组件。 This will label each distinct region with a different label. 这将使用不同的标签标记每个不同的区域。 Then we can grab the largest non-zero labeled component**, create a mask for it, and add that val into the new image at that place. 然后我们可以抓取最大的非零标记组件**,为它创建一个掩码,并将该val添加到该位置的新图像中。

** This is the annoying bit that looks weird in the code. **这是令人烦恼的一点,在代码中看起来很奇怪。

largest_label = 1 + np.argmax(stats[1:, cv2.CC_STAT_AREA])

First, you can check out the answer you linked for the shape of the stats array, but each row corresponds to a label (so the label 0 will correspond to the first row, etc), and the column is defined by the integer cv2.CC_STAT_AREA (which is just 4). 首先,您可以查看您为stats数组的形状链接的答案,但每行对应一个标签(因此标签0将对应于第一行等),并且该列由整数cv2定义。 CC_STAT_AREA(仅为4)。 We'll need to make sure we're looking at the largest non-zero label, so I'm only looking at rows past the first one. 我们需要确保我们正在查看最大的非零标签,所以我只查看第一个之后的行。 Then, grab the index corresponding to the largest area. 然后,抓取对应于最大区域的索引。 Since we shaved the zero row off, the index now corresponds to label-1 , so add 1 to get the correct label. 由于我们将零行关闭,因此索引现在对应于label-1 ,因此添加1以获取正确的标签。 Then we can mask as usual and insert the value. 然后我们可以像往常一样屏蔽并插入值。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM