【问题标题】:Pairwise Distances Between Two "islands"/"connected components" in Numpy ArrayNumpy数组中两个“岛”/“连接组件”之间的成对距离
【发布时间】:2020-10-29 19:30:55
【问题描述】:

考虑下图,存储为 numpy 数组:

a = [[0,0,0,0,0,1,1,0,0,0],
     [0,0,0,0,1,1,1,1,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,2,0,0,0,0],
     [0,0,0,0,0,2,2,0,0,0],
     [0,0,0,0,0,2,0,0,0,0],
     [0,0,0,0,3,3,3,0,0,0],
     [4,0,0,0,0,0,0,0,0,0],
     [4,4,0,0,0,0,0,0,0,0],
     [4,4,4,0,0,0,0,0,0,0]]

a = np.array(a)

0 代表背景像素,1,2,3 和 4 代表属于对象的像素。您可以看到对象总是在图像中形成连续的岛屿或区域。我想知道每对物体之间的距离。作为距离测量,我希望在对象的那些像素之间具有最短的直线距离,这些像素彼此最接近。示例:Distance(2,3) = 1,因为它们正在接触。 Distance(1,2) = 2,因为只有一个背景像素将两个区域隔开,或者换句话说,物体最近的像素相隔两个像素。

谁能告诉我如何在 Python 中解决这个问题?或者给我链接一些资源?

【问题讨论】:

  • 这能回答你的问题吗? Fastest pairwise distance metric in python
  • 不,很遗憾没有。您发布的链接处理 1D 案例,完全是一个稍微不同的问题。
  • 我认为你的问题的解决方案基本上是一样的。我会开始尝试调整其中的内容,看看你是否卡在某个地方。
  • 我认为如果这些岛屿已经被“标记”了,那么下面的方法应该可以正常工作。如果不一定要标记,则 connectec 组件会更多
  • @r0f1 3 号岛和 4 号岛之间的直线是多少?我不确定在这种情况下距离定义是否清晰。请详细说明。谢谢。

标签: python arrays numpy scipy image-segmentation


【解决方案1】:

对于许多 blob 或更大的 blob,或者如果性能/内存效率是一个标准,您可能希望使用这些岛屿的轮廓。考虑到这一点,我们将使用OpenCV's findContours 来获取轮廓,然后执行成对距离计算并获得min 作为最终输出。实现看起来像这样,可以获得所有可能的成对距离 -

from scipy.spatial.distance import cdist
import cv2

ids = np.arange(1, a.max()+1) #np.unique(a)[1:] if not in ranged sequence

idxs = []
for id_ in ids:
    im = (a == id_).astype(np.uint8)
    contours,_ = cv2.findContours(im, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
    idx = contours[0][:, 0]
    idxs.append(idx)

# Get pairwise indices and then distances
r,c = np.triu_indices(len(ids), 1)
pdists = {(ids[i],ids[j]):cdist(idxs[i], idxs[j]).min() for (i, j) in zip(r, c)}

给定样本的输出字典 -

In [225]: pdists
Out[225]: 
{(1, 2): 2.0,
 (1, 3): 5.0,
 (1, 4): 7.810249675906654,
 (2, 3): 1.0,
 (2, 4): 5.0,
 (3, 4): 3.605551275463989}

默认情况下,cdist 使用欧式距离作为metric。根据您对岛屿之间直线的定义,您可能需要尝试其他指标,即分别针对MinkowskiManhattan 距离的'minkowski''cityblock'

因此,cdist(idxs[i], idxs[j]) 将更改为 cdist(idxs[i], idxs[j], metric=...)

【讨论】:

  • 注意:这假设每个岛(连接的组件)都有一个唯一的值。如果多个连通分量具有相同的值,idx = [c[0] for contour in contours for c in contour] 会更准确。
【解决方案2】:

这是你需要的:

from scipy.spatial.distance import cdist
def Distance(a, m, n):
  return cdist(np.argwhere(a==m),np.argwhere(a==n),'minkowski',p=1.).min()

或类似@MaxPowers 评论(声称:cityblock 更快):

  return cdist(np.argwhere(a==m),np.argwhere(a==n),'cityblock').min()

找到岛屿的位置并计算位置的成对距离并获得最小值。我不能 100% 确定您想要的距离,但我认为您正在寻找 l1 norm。如果没有,您可以将 cdist 度量更改为您想要的指标。

输出:

Distance(a,2,3)
1.0
Distance(a,2,1)
2.0
Distance(a,3,1)
5.0
Distance(a,4,3)
5.0

【讨论】:

  • 这回答了这个问题。可以直接使用metric='cityblock',速度更快,不需要p参数。
  • @MaxPowers 谢谢。不知道它有自己的指标。您能否详细说明为什么那个性能更快?它们不应该几乎相似吗?
  • L1 范数计算为向量元素的差值之和。 Minkowski 使用np.norm 计算为广义 Lp 范数。因此,梯形图获取每个向量元素的幂并计算它们总差的根。
  • @MaxPowers 很有趣。谢谢你。对于p=1 的情况,我期待它会更聪明
  • 仅供参考,@Ehsan:根据pull request 12375 minkowski 对于p=1p=2 的情况会更聪明。这将在 1.16.0 版中提供。
猜你喜欢
  • 2015-02-25
  • 1970-01-01
  • 1970-01-01
  • 2017-04-21
  • 1970-01-01
  • 1970-01-01
  • 2020-11-18
  • 2013-07-29
  • 2020-09-13
相关资源
最近更新 更多