【问题标题】:How to select all locations of unique elements in numpy 2d array with bounding box around them?如何选择numpy 2d数组中唯一元素的所有位置,周围有边界框?
【发布时间】:2011-10-10 18:57:39
【问题描述】:

我有一个 2D numpy 数组,我想找到所有唯一元素的“每个”位置。我们可以使用numpy.unique(numpyarray.) 找到独特的元素。棘手的部分来了。现在我必须知道每个独特元素的所有位置。让我们考虑以下示例。

array([[1, 1, 2, 2],\
       [1, 1, 2, 2],\
       [3, 3, 4, 4],\
       [3, 3, 4, 4]])

结果应该是

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

如何做以及什么是存储和迭代值的合适方法。

请注意,所有唯一值将彼此相邻。它们之间唯一的差距只能是零。让我们考虑另一种变体

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

结果应该是

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

边界上的 zeoros 将被忽略。

非常感谢

【问题讨论】:

    标签: python numpy


    【解决方案1】:

    简单而暴力的方法就是使用numpy.where

    例如,如果您只想要边界框

    import numpy as np
    
    x = np.array([[1,1,2,2],
                  [1,1,2,2],
                  [3,3,4,4],
                  [3,3,4,4]])
    
    for val in np.unique(x):
        rows, cols = np.where(x == val)
        rowstart, rowstop = np.min(rows), np.max(rows)
        colstart, colstop = np.min(cols), np.max(cols)
        print val, (rowstart, colstart), (rowstop, colstop) 
    

    这也适用于带有零的示例。

    如果数组很大,并且您已经有scipy,您可以考虑使用scipy.ndimage.find_objects,正如@unutbu 建议的那样。

    在您的示例的特定情况下,您的唯一值是连续整数,您可以直接使用find_objects。它需要一个数组,其中每个非 0 的连续整数代表一个需要返回其边界框的对象。 (完全按照您的意愿忽略 0。)但是,通常,您需要进行一些预处理以将任意唯一值转换为连续整数。

    find_objects 返回slice 对象的元组列表。老实说,如果您想要边界框,这些可能正是您想要的。但是,打印出开始和停止标记看起来会有点混乱。

    import numpy as np
    import scipy.ndimage as ndimage
    
    x = np.array([[1, 0, 1, 2, 2],
                  [1, 0, 1, 2, 2],
                  [3, 0, 3, 4, 4],
                  [3, 0, 3, 4, 4]])
    
    for i, item in enumerate(ndimage.find_objects(x), start=1):
        print i, item
    

    这看起来与您预期的略有不同。这些是slice 对象,因此“max”值将始终比前面示例中的“max”值高一个。这样您就可以简单地使用给定的元组进行切片以获取相关数据。

    例如

    for i, item in enumerate(ndimage.find_objects(x), start=1):
        print i, ':'
        print x[item], '\n'
    

    如果您真的想要开始和停止,只需执行以下操作:

        for i, (rowslice, colslice) in enumerate(ndimage.find_objects(x), start=1):
            print i, 
            print (rowslice.start, rowslice.stop - 1),
            print (colslice.start, colslice.stop - 1)
    

    如果您的唯一值不是连续整数,您需要进行一些预处理,正如我之前提到的。你可以这样做:

    import numpy as np
    import scipy.ndimage as ndimage
    
    x = np.array([[1.1, 0.0, 1.1, 0.9, 0.9],
                  [1.1, 0.0, 1.1, 0.9, 0.9],
                  [3.3, 0.0, 3.3, 4.4, 4.4],
                  [3.3, 0.0, 3.3, 4.4, 4.4]])
    ignored_val = 0.0
    labels = np.zeros(data.shape, dtype=np.int)
    
    i = 1
    for val in np.unique(x):
        if val != ignored_val:
            labels[x == val] = i
            i += 1
    
    # Now we can use the "labels" array as input to find_objects
    for i, item in enumerate(ndimage.find_objects(labels), start=1):
        print i, ':'
        print x[item], '\n'
    

    【讨论】:

    • 暂时忽略零,有没有办法使用ndimage.find_objects来做到这一点?
    • 绝对!我应该把它放在第一位。谢谢提醒。
    • 感谢您演示如何操作。你让它看起来如此简单。顺便说一句,我认为新版本的 numpy 有 np.unique(x, return_inverse=True) judging from this 应该允许您在没有 for 循环的情况下生成 labels
    • find_objects 返回的对象的“修剪”边界是否容易获得?例如,我不一定想要“边界框”。我想要对象的真实坐标,即每个对象的轮廓。
    • @durden2.0 - 有几个不同的库实现它,但没有直接内置到 scipy 中。看看skimage.measure.find_contours,例如scikit-image.org/docs/dev/auto_examples/plot_contours.html(请注意,它们被绘制在那里,类似于matplotlib.pyplot.contour,但该函数只是返回一个坐标数组。)GDAL 也有这方面的方法,如果您正在使用 GIS 数据,它会很方便。但是,如果您不这样做,那就太过分了。我认为mahotas 也可能实现类似的功能。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-08-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多