在像素数组中查找连通组件

25

我有一个像下面一样的像素数组,并希望从中区分出两个“组”的1。计划是在大量类似的像素数组中执行此操作,因此我需要找到一种有效的方法。

也许我可以将所有的1位置添加到一个单独的数组中,并进行一些搜索以找到连接的1,但应该有更好的方法。是否有任何算法可以找到这样的连通组件?

[
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
]

1
根据您的目标和背景,它有各种名称,例如“图像分割”、“连通组件分析”、“标记”和“Blob分析”。 - Mark Setchell
您可以将每个矩阵编码为图形并搜索连接的组件。只要“组”不“接触”,这种方法就有效。或者,您可以运行某种K-means聚类算法,只要您事先知道组件的数量即可。还有许多其他选项,但正如您可能已经注意到的那样,我们需要更多关于您的数据的详细信息,即可以做出什么样的假设:您是否知道组数,它们是否线性可分,它们是否可以“接触”... - Eli Korvigo
你想做的听起来很像泛洪填充,因此查看执行此操作的算法可能会很有成效(尽管纯Python实现可能太慢)。 - martineau
纯Python在这个任务中非常缓慢,考虑使用scipy或OpenCV或类似工具来进行标记/连通组件。它们非常快速。我曾经在纯Python中实现过连通组件,速度非常非常慢。 - alkasm
谢谢回复,我会研究一下并看看是否能找到适合我的东西。@EliKorvigo它们不能接触,并且每个图像中始终有两个,但不幸的是它们不是线性可分的。 - gugge
显示剩余2条评论
1个回答

51

考虑到组之间不应该相互接触,您可以使用 scipy.ndimage.measurements.label 来对这些组进行标记:

In [1]: import numpy as np

In [2]: from scipy.ndimage.measurements import label

In [3]: array = np.array(...)  # your example

In [4]: structure = np.ones((3, 3), dtype=np.int)  # this defines the connection filter

In [5]: structure  # in this case we allow any kind of connection
Out[5]: 
array([[1, 1, 1],
       [1, 1, 1],
       [1, 1, 1]])

In [6]: labeled, ncomponents = label(array, structure)

In [7]: labeled
Out[7]: 
array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
       [0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
       [0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
       [0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
       [0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
       [0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
       [0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
       [0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], dtype=int32)

In [7]: ncomponents
Out[7]: 2

虽然我没有阅读过具体的实现,但是SciPy倾向于使用在C中实现的高效算法,因此性能应该比较高。然后,您可以使用NumPy提取每个组的索引:

In [8]: indices = np.indices(array.shape).T[:,:,[1, 0]]

In [9]: indices[labeled == 1]
Out[9]: 
array([[ 1,  6],
       [ 1,  7],
       [ 2,  6],
       [ 2,  7],
       [ 2,  8],
       [ 2,  9],
       [ 2, 10],
       [ 2, 11],
       [ 2, 12],
       [ 2, 13],
       [ 3, 11],
       [ 3, 12],
       [ 3, 13]])

In [10]: indices[labeled == 2]
Out[10]: 
array([[ 5,  1],
       [ 6,  1],
       [ 7,  1],
       [ 7,  2],
       [ 8,  1],
       [ 8,  2],
       [ 9,  2],
       [10,  2],
       [10,  3],
       [11,  2],
       [11,  3],
       [12,  3],
       [13,  3]])

我有一个问题,如果想按范围值进行分组怎么办?例如,我想将值为1-3的数据分为一组,而不是只将值为1的数据分为一组。我的意思是,给定一个值范围的公差,进行分组。 - Meyer Buaharon
DeprecationWarning: Please use labelfrom thescipy.ndimagenamespace, thescipy.ndimage.measurements namespace is deprecated. - Tomasz Gandor

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