找到与边缘的距离'一个numpy数组

时间:2016-11-08 16:41:28

标签: numpy scipy

我有一个带有1 s&的numpy数组。 0 s(如果更容易,则为bools)

我想找到每个1距离最近的边缘的距离' (边缘是10相遇的地方)。

玩具示例:

原始阵列:

array([[0, 0, 0, 0],
       [0, 1, 1, 1],
       [0, 1, 1, 1],
       [0, 1, 1, 1]])

结果:

array([[0, 0, 0, 0],
       [0, 1, 1, 1],
       [0, 1, 2, 1],
       [0, 1, 1, 1]])

如果可能,我想使用' cityblock'距离,但优先级较低

谢谢!

2 个答案:

答案 0 :(得分:3)

以下是使用scipy.ndimage.distance_transform_cdt(或scipy.ndimage.distance_transform_bf)执行此操作的一种方法:

import numpy as np
from scipy.ndimage import distance_transform_cdt


def distance_from_edge(x):
    x = np.pad(x, 1, mode='constant')
    dist = distance_transform_cdt(x, metric='taxicab')
    return dist[1:-1, 1:-1]

例如:

In [327]: a
Out[327]: 
array([[0, 0, 0, 0],
       [0, 1, 1, 1],
       [0, 1, 1, 1],
       [0, 1, 1, 1]])

In [328]: distance_from_edge(a)
Out[328]: 
array([[0, 0, 0, 0],
       [0, 1, 1, 1],
       [0, 1, 2, 1],
       [0, 1, 1, 1]], dtype=int32)

In [329]: x
Out[329]: 
array([[1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0],
       [1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0],
       [1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0],
       [1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0],
       [1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0],
       [1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0],
       [0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1],
       [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1]])

In [330]: distance_from_edge(x)
Out[330]: 
array([[1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0],
       [1, 2, 2, 2, 2, 1, 0, 0, 0, 1, 0, 0],
       [1, 2, 3, 3, 2, 1, 0, 0, 1, 2, 1, 0],
       [1, 2, 3, 3, 2, 1, 0, 0, 0, 1, 0, 0],
       [1, 2, 3, 3, 2, 1, 0, 0, 0, 0, 0, 0],
       [1, 1, 2, 2, 2, 1, 0, 0, 0, 1, 1, 0],
       [0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 2, 1],
       [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1]], dtype=int32)

如果你没有用零填充数组,你得到距离数组中最近的0 的距离:

In [335]: distance_transform_cdt(a, metric='taxicab')
Out[335]: 
array([[0, 0, 0, 0],
       [0, 1, 1, 1],
       [0, 1, 2, 2],
       [0, 1, 2, 3]], dtype=int32)

In [336]: distance_transform_cdt(x, metric='taxicab')
Out[336]: 
array([[6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0],
       [5, 5, 4, 3, 2, 1, 0, 0, 0, 1, 0, 0],
       [4, 4, 4, 3, 2, 1, 0, 0, 1, 2, 1, 0],
       [3, 3, 4, 3, 2, 1, 0, 0, 0, 1, 0, 0],
       [2, 2, 3, 3, 2, 1, 0, 0, 0, 0, 0, 0],
       [1, 1, 2, 2, 2, 1, 0, 0, 0, 1, 1, 0],
       [0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 2, 1],
       [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2]], dtype=int32)

这是一种使用scipy.ndimage.binary_erosion的不同方法。在我发现距离变换功能之前我写了这个。我确信有更有效的方法,但对于不太大的图像,这应该可以很好地工作。

import numpy as np
from scipy.ndimage import binary_erosion


def distance_from_edge(x):
    dist = np.zeros_like(x, dtype=int)
    while np.count_nonzero(x) > 0:
        dist += x  # Assumes x is an array of 0s and 1s, or bools.
        x = binary_erosion(x)
    return dist

例如,

In [291]: a
Out[291]: 
array([[0, 0, 0, 0],
       [0, 1, 1, 1],
       [0, 1, 1, 1],
       [0, 1, 1, 1]])

In [292]: distance_from_edge(a)
Out[292]: 
array([[0, 0, 0, 0],
       [0, 1, 1, 1],
       [0, 1, 2, 1],
       [0, 1, 1, 1]])

In [293]: x
Out[293]: 
array([[1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0],
       [1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0],
       [1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0],
       [1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0],
       [1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0],
       [1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0],
       [0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1],
       [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1]])

In [294]: distance_from_edge(x)
Out[294]: 
array([[1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0],
       [1, 2, 2, 2, 2, 1, 0, 0, 0, 1, 0, 0],
       [1, 2, 3, 3, 2, 1, 0, 0, 1, 2, 1, 0],
       [1, 2, 3, 3, 2, 1, 0, 0, 0, 1, 0, 0],
       [1, 2, 3, 3, 2, 1, 0, 0, 0, 0, 0, 0],
       [1, 1, 2, 2, 2, 1, 0, 0, 0, 1, 1, 0],
       [0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 2, 1],
       [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1]])

答案 1 :(得分:3)

这是使用binary_erosion&的矢量化方法。 cdist(..'cityblock') -

from scipy.ndimage.morphology import binary_erosion
from scipy.spatial.distance import cdist

def dist_from_edge(img):
    I = binary_erosion(img) # Interior mask
    C = img - I             # Contour mask
    out = C.astype(int)     # Setup o/p and assign cityblock distances
    out[I] = cdist(np.argwhere(C), np.argwhere(I), 'cityblock').min(0) + 1
    return out

示例运行 -

In [188]: img.astype(int)
Out[188]: 
array([[0, 0, 0, 0, 1, 0, 0],
       [0, 1, 1, 1, 1, 1, 0],
       [0, 1, 1, 1, 1, 1, 1],
       [0, 1, 1, 1, 1, 1, 1],
       [0, 0, 1, 1, 1, 1, 1],
       [0, 0, 0, 1, 0, 0, 0]])

In [189]: dist_from_edge(img)
Out[189]: 
array([[0, 0, 0, 0, 1, 0, 0],
       [0, 1, 1, 1, 2, 1, 0],
       [0, 1, 2, 2, 3, 2, 1],
       [0, 1, 2, 3, 2, 2, 1],
       [0, 0, 1, 2, 1, 1, 1],
       [0, 0, 0, 1, 0, 0, 0]])

这是人类blob的输入输出 -

enter image description here enter image description here