检查相邻值是否在Numpy矩阵中

时间:2017-01-01 20:24:32

标签: python arrays numpy

因此,我目前正在尝试找出一种更优化的解决方案来确定图像中的连接组件。目前,我有一个坐标具有特定值的数组。我想基于它们是否触摸来创建这些坐标的组。我正在使用一个numpy数组,目前我必须检查每个值(左上,上中,右上,中左,中右,左下,中下,右下)是否在该数组中。我通过这段代码这样做:

for x in range (0, groupCoords.shape[0]):
            global tgroup
            xCoord = groupCoords.item((x,0))
            yCoord = groupCoords.item((x,1))
            new = np.array([[xCoord, yCoord]])
            if np.equal(Arr,[xCoord, yCoord+1]).all(1).any():
                tgroup = np.append(tgroup, [[xCoord,yCoord+1]], axis=0)
                new = np.append(new, [[xCoord,yCoord+1]], axis=0)
                index = np.argwhere((Arr == [xCoord,yCoord+1]).all(1))
                Arr = np.delete(Arr, (index), axis=0)

            if np.equal(Arr,[xCoord, yCoord-1]).all(1).any():
                tgroup = np.append(tgroup, [[xCoord, yCoord-1]],axis=0)
                new = np.append(new, [[xCoord,yCoord-1]], axis=0)
                index = np.argwhere((Arr == [xCoord,yCoord-1]).all(1))
                Arr = np.delete(Arr, (index), axis=0)

            if np.equal(Arr,[xCoord+1, yCoord]).all(1).any():
                tgroup = np.append(tgroup, [[xCoord+1,yCoord]],axis=0)
                new = np.append(new, [[xCoord+1,yCoord]], axis=0)
                index = np.argwhere((Arr == [xCoord+1,yCoord]).all(1))
                Arr = np.delete(Arr, (index), axis=0)

            if np.equal(Arr,[xCoord+1, yCoord+1]).all(1).any():
                tgroup = np.append(tgroup, [[xCoord+1,yCoord+1]],axis=0)
                new = np.append(new, [[xCoord+1,yCoord+1]], axis=0)
                index = np.argwhere((Arr == [xCoord+1,yCoord+1]).all(1))
                Arr = np.delete(Arr, (index), axis=0)

            if np.equal(Arr,[xCoord+1, yCoord-1]).all(1).any():
                tgroup = np.append(tgroup, [[xCoord+1,yCoord-1]],axis=0)
                new = np.append(new, [[xCoord+1,yCoord-1]], axis=0)
                index = np.argwhere((Arr == [xCoord+1,yCoord-1]).all(1))
                Arr = np.delete(Arr, (index), axis=0)

            if np.equal(Arr,[xCoord-1, yCoord]).all(1).any():
                tgroup = np.append(tgroup, [[xCoord-1,yCoord]],axis=0)
                new = np.append(new, [[xCoord-1,yCoord]], axis=0)
                index = np.argwhere((Arr == [xCoord-1,yCoord]).all(1))
                Arr = np.delete(Arr, (index), axis=0)

            if np.equal(Arr,[xCoord-1, yCoord+1]).all(1).any():
                tgroup = np.append(tgroup, [[xCoord-1,yCoord+1]],axis=0)
                new = np.append(new, [[xCoord-1,yCoord+1]], axis=0)
                index = np.argwhere((Arr == [xCoord-1,yCoord+1]).all(1))
                Arr = np.delete(Arr, (index), axis=0)

            if np.equal(Arr,[xCoord-1, yCoord-1]).all(1).any():
                tgroup = np.append(tgroup, [[xCoord-1,yCoord-1]],axis=0)
                new = np.append(new, [[xCoord-1,yCoord-1]], axis=0)
                index = np.argwhere((Arr == [xCoord-1,yCoord-1]).all(1))
                Arr = np.delete(Arr, (index), axis=0)

然而,如果图像很大,这显然需要很长时间。我有想法创建一个尺寸为图像宽度和高度的布尔矩阵,然后将值“true”赋给矩阵中与图像中像素对应的值(图像为黑白)。

我想知道,是否有可能,而不是必须检查每个值,确定它们是否是直接围绕另一个“真实”值的“真实”元素?

这就是输入数组的样子:

[
 [0 0]
 [0 1]
 [0 2]
 [10 2]

]

输出看起来像

[
 [0 0]
 [0 1]
 [0 2]
]

我希望改进的功能将检查“真实”值是否触及,并创建一个触及的所有值的“网络”(它将继续运行所找到的新值)。

2 个答案:

答案 0 :(得分:1)

方法#1

我们可以得到欧几里德距离,看看是否有任何距离在sqrt(2)范围内,up-down覆盖distance = 1,对角线覆盖distance = sqrt(2)。这将为我们提供一个掩码,当索引到组坐标数组时,它将为我们提供连接的掩码。

因此,使用Scipy's cdist获取欧几里德距离的实现将是 -

from scipy.spatial.distance import cdist

out = groupCoords[(cdist(groupCoords,Arr)<1.5).any(1)]

示例运行 -

In [401]: Arr
Out[401]: 
array([[ 5,  4],
       [11, 12],
       [ 5,  3],
       [ 1,  3],
       [15,  8],
       [55, 21]])

In [402]: groupCoords
Out[402]: 
array([[2, 3],  # In neighbourhood of (1,3)
       [5, 6],
       [6, 2],  # In neighbourhood of (5,3)
       [5, 3],  # In neighbourhood of (5,4)
       [5, 8]])

In [403]: groupCoords[(cdist(groupCoords,Arr)<1.5).any(1)]
Out[403]: 
array([[2, 3],
       [6, 2],
       [5, 3]])

方法#2

另一种方法是检查两个数组的第一列之间的绝对元素差异,对第二列类似。最后,从这两个蒙版中获取一个联合蒙版,并检查任何匹配并再次索引到已过滤坐标的组数组中。

因此,这种方法的实现将是 -

col0_mask = (np.abs(groupCoords[:,0,None] - Arr[:,0])<=1)
col1_mask = (np.abs(groupCoords[:,1,None] - Arr[:,1])<=1)
out = groupCoords[(col0_mask & col1_mask).any(1)]

方法#3

另一种方法,如果你将Arr作为布尔数组而不是2列坐标数组,可能会更好。我的想法是Arr的{​​{3}},然后看看来自groupCoords的哪些坐标也会出现在这张扩张的图像中。对于扩张,我们将使用所有那些的3 x 3核来覆盖所有这些邻域。为了检测这些公共点,我们需要绘制带有groupCoords的图像。

因此,代码将是 -

from scipy.ndimage.morphology import binary_dilation

img = np.zeros(Arr.shape,dtype=bool)
img[groupCoords[:,0],groupCoords[:,1]] = 1
out = np.argwhere(binary_dilation(Arr,np.ones((3,3))) & img)

示例运行 -

In [444]: # Inputs : groupCoords and let's create a sample array for Arr
     ...: groupCoords = np.array([[2,3],[5,6],[6,2],[5,3],[5,8]])
     ...: 
     ...: Arr_Coords = np.array([[5,4],[11,12],[5,3],[1,3],[15,8],[55,21]])
     ...: Arr = np.zeros(Arr_Coords.max(0)+1,dtype=bool)
     ...: Arr[Arr_Coords[:,0], Arr_Coords[:,1]] = 1
     ...: 

In [445]: img = np.zeros(Arr.shape,dtype=bool)
     ...: img[groupCoords[:,0],groupCoords[:,1]] = 1
     ...: out = np.argwhere(binary_dilation(Arr,np.ones((3,3))) & img)
     ...: 

In [446]: out
Out[446]: 
array([[2, 3],
       [5, 3],
       [6, 2]])

答案 1 :(得分:1)

根据代码的最终目标,您可能会发现scipy.ndimage.label及其亲属有用。

例如,

In [44]: from scipy.ndimage import label

In [45]: x
Out[45]: 
array([[ True,  True, False, False,  True],
       [False, False, False,  True,  True],
       [False,  True, False,  True, False],
       [ True,  True, False, False, False]], dtype=bool)

In [46]: x.astype(int)  # More concise, easier to read
Out[46]: 
array([[1, 1, 0, 0, 1],
       [0, 0, 0, 1, 1],
       [0, 1, 0, 1, 0],
       [1, 1, 0, 0, 0]])

label返回两个值。第一个是与输入数组大小相同的数组。输入中的每个不同的连通组件都被赋予一个整数值,从1开始。背景为0.第二个返回值是找到的组件数。

In [47]: labeled_arr, nlabels = label(x)

In [48]: nlabels
Out[48]: 3

In [49]: labeled_arr
Out[49]: 
array([[1, 1, 0, 0, 2],
       [0, 0, 0, 2, 2],
       [0, 3, 0, 2, 0],
       [3, 3, 0, 0, 0]], dtype=int32)

在下文中,where(labeled_array = i)返回包含两个数组的元组。这些数组是连接组件的行索引和列索引:

In [50]: for i in range(1, nlabels+1):
    ...:     print(where(labeled_arr == i))
    ...:     
(array([0, 0]), array([0, 1]))
(array([0, 1, 1, 2]), array([4, 3, 4, 3]))
(array([2, 3, 3]), array([1, 0, 1]))

您可以将它们压缩在一起,将它们转换为(row,col)对列表:

In [52]: for i in range(1, nlabels+1):
    ...:     print(list(zip(*where(labeled_arr == i))))
    ...:     
[(0, 0), (0, 1)]
[(0, 4), (1, 3), (1, 4), (2, 3)]
[(2, 1), (3, 0), (3, 1)]