【问题标题】:python contour for binary 2D matrix二进制二维矩阵的python轮廓
【发布时间】:2009-11-12 19:41:43
【问题描述】:

我想在二进制 NxM 矩阵中计算一个形状周围的凸包。凸包算法需要一个坐标列表,所以我使用 numpy.argwhere(im) 来获得所有形状点坐标。然而,这些点中的大多数对凸包没有贡献(它们位于形状的内部)。因为凸包计算时间至少与它作为输入的点数成正比,所以我设计了一个想法来预先过滤过多的无用点,只通过那些跨越轮廓的点。这个想法很简单,对于二进制 NxM 矩阵中的每一行,我只取最小和最大索引。比如:

im = np.array([[1,1,1,0],
              [1,0,1,1],
              [1,1,0,1],
              [0,0,0,0],
              [0,1,1,1]], dtype=np.bool)
outline = somefunc(im)

然后大纲应该读取(元组或作为 5x2 numpy 数组,我不介意):

[(0,0),(0,2),(1,0),(1,3),(2,0),(2,3),(4,1),(4,3)]

任何紧紧围绕这个形状(im)的凸包,必须是这些点的子集(轮廓)。换句话说,如果“somefunc()”在过滤内部点方面是有效的,那么它可以节省凸包计算的时间。

我有执行上述技巧的代码,但我希望有人有更聪明(阅读速度更快)的方法,因为我需要多次运行它。我的代码是:

# I have a 2D binary field. random for the purpose of demonstration.
import numpy as np
im = np.random.random((320,360)) > 0.9

# This is my algorithm so far. Notice that coords is sorted.
coords = np.argwhere(im)
left = np.roll(coords[:,0], 1, axis=0) != coords[:,0]
outline = np.vstack([coords[left], coords[left[1:]], coords[-1]])

我的另一个想法是使用 Python 的 reduce(),所以我只需要遍历坐标列表一次。但是我很难找到一个好的归约函数。

任何帮助将不胜感激!

编辑

与此同时,我找到了从im 直接转到outline 的更快方法。至少对于大图像,这要快得多。在明显没有外部解决方案的情况下,我将其作为该问题的解决方案。

不过,如果有人知道更快的方法,请说出来:)

【问题讨论】:

  • fast 不是很有帮助的标签
  • sigh 我正在寻找答案,聪明人。
  • @Paul:可能是抱怨的好方法,因为您的部分问题不清楚且无益。如果我们无法得到您的问题(包括您的标签),我们几乎无法提供帮助。
  • 对于到达这里的人,请参阅此答案:*.com/a/14223101/1128223

标签: python algorithm numpy contour


【解决方案1】:

在没有可接受的答案的情况下,我发布了我最好的工作代码作为解决方案。

def outline(im):
    ''' Input binary 2D (NxM) image. Ouput array (2xK) of K (y,x) coordinates
        where 0 <= K <= 2*M.
    '''
    topbottom = np.empty((1,2*im.shape[1]), dtype=np.uint16)
    topbottom[0,0:im.shape[1]] = np.argmax(im, axis=0)
    topbottom[0,im.shape[1]:] = (im.shape[0]-1)-np.argmax(np.flipud(im), axis=0)
    mask      = np.tile(np.any(im, axis=0), (2,))
    xvalues   = np.tile(np.arange(im.shape[1]), (1,2))
    return np.vstack([topbottom,xvalues])[:,mask].T

【讨论】:

    【解决方案2】:

    这个任务似乎和你最后两个步骤完成了同样的事情:

    outline = np.array(dict(reversed(coords)).items() + dict(coords).items())
    

    不知道是不是更快。

    【讨论】:

    • 太糟糕了,它慢了 10 倍(!)。
    【解决方案3】:

    对于更通用的解决方案,您可以使用某种边缘检测方法来仅找到边缘点。我相信(Google..)NumPy 有内置的 sobel 过滤器,它会做到这一点。

    【讨论】:

    • 是的,我知道 sobel 过滤器,它很棒。事实上,这就是我得到这些二进制图像的方式。但是过滤器没有给出索引。
    • 过滤器将为您提供位图/矩阵,您可以在其中找到所有索引,就像您在代码中所做的那样。
    • 哦,等等,你得到了带有 sobel 的示例图像,它的点太多了?
    • 你是对的。我想在(阈值)sobel 周围拟合一个多边形。因此,首先,我想减少点数。最后一部分是我试图找到快速代码的地方。
    • 在机器视觉应用程序中,我一直在使用blob工具中的轮廓输出,但不幸的是我在OpenCV中没有看到这样的功能。那将是一个优雅的解决方案..