NumPy中是否有一个“bounding box”函数(切片非零值)适用于ndarray?

22
我正在处理由numpy.array()创建的数组,并需要在模拟图像的画布上绘制点。由于中心部分包含有意义的数据,周围有很多零值,因此我想要“修剪”数组,擦除只包含零的列和行。
因此,我想知道是否有任何本地numpy函数或甚至是代码片段可以“修剪”或查找“边界框”,以仅切片包含数据的部分数组。
(由于这是一个概念性问题,我没有放任何代码,请原谅,如果需要的话,我非常新手,所以在SO上发帖。)
感谢阅读

请查看bbox2函数...如果有许多行/列完全填充了零,而只有少量聚集的数据,则速度要快得多。 - Benjamin
3个回答

24

这应该可以解决问题:

from numpy import array, argwhere

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

B = argwhere(A)
(ystart, xstart), (ystop, xstop) = B.min(0), B.max(0) + 1 
Atrim = A[ystart:ystop, xstart:xstop]

1
不错!只是就可读性而言,你可以使用 (ystart, xstart), (ystop, xstop) = B.min(0), B.max(0) + 1,然后只需使用 A 进行索引操作 Atrim = a[ystart:ystop, xstart:xstop]。当然,这完全等效,但我认为它更易读,在任何情况下都是如此。 - Joe Kington
这个没问题,你使用的例子正是我通常会使用的典型数组(只是更大而已)。我不知道 argwhere 函数,现在会去了解一下。谢谢! - heltonbiker
@Paul ..谢谢,你帮了我 :-) - Necromancer
有没有一种方法可以适用于任何数组维度? - Naomi Fridman
1
@Naomi 当然可以。只需在此示例中添加zstart,放在ystartxstart之后,以处理三维数据,并且对于更高维度的数据,可以继续添加更多。 - Paul

17

以下代码来自这个答案,在我的测试中运行速度最快:

def bbox2(img):
    rows = np.any(img, axis=1)
    cols = np.any(img, axis=0)
    ymin, ymax = np.where(rows)[0][[0, -1]]
    xmin, xmax = np.where(cols)[0][[0, -1]]
    return img[ymin:ymax+1, xmin:xmax+1]

使用argwhere的被接受答案虽然可行但速度较慢。我猜测是因为argwhere会分配一个巨大的索引输出数组。我在一个大的2D数组上进行了测试(一个1024 x 1024的图像,大约有一个50x100的非零区域)。


我发现这个答案更加符合Python的风格!谢谢! - heltonbiker
1
注意,这段代码在完全黑色图像的边缘情况下可能会生成错误。您必须验证两个np.where()调用都不返回空数组。 - Delgan
太好了!有没有想过如何使用周期边界条件来扩展它? - Tropilio
@Tropilio 我不确定您所说的周期边界条件是什么意思。但是,如果您想要找到多个连续True值的“blob”,那么像这个答案一样的方法可能行不通。相反,为了找到任意的blob,我会使用OpenCV connectedComponents()函数:https://docs.opencv.org/3.4/d3/dc0/group__imgproc__shape.html#gaedef8c7340499ca391d459122e51bef5 - Luke

0

类似这样:

empty_cols = sp.all(array == 0, axis=0)
empty_rows = sp.all(array == 0, axis=1)

得到的数组将是一维布尔数组。从两端循环它们以找到“边界框”。


1
避免循环numpy数组。 - Paul
循环只是一维的,所以是n阶而不是n的平方。这不是很大的问题。 - kiyo
1
你说得对,而且你甚至不需要循环整个数组宽度,但是Python循环包含各种额外的步骤,比如类型检查。在这个一维示例中:http://www.scipy.org/Getting_Started#head-9aed725bd569d40f625240b2b6ec710550ff14b9 Python循环运行速度慢了25倍才能完成相同的任务!不知道图像的大小或数量以及算法的应用(计算机视觉?),我无法确定这种加速有多大作用。 - Paul

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接