如何将NumPy的2D数组裁剪为非零值?

9

假设我有一个像这样的二维布尔numpy数组:

import numpy as np
a = np.array([
    [0,0,0,0,0,0],
    [0,1,0,1,0,0],
    [0,1,1,0,0,0],
    [0,0,0,0,0,0],
], dtype=bool)

我该如何将其裁剪为包含所有True值的最小框(矩形,核)?

因此,在上面的示例中:

b = np.array([
    [1,0,1],
    [1,1,0],
], dtype=bool)

1
它必须像示例中的3x3那样吗?还是大小可以变化?它应该是n x n还是可以是n x m? - Sqoshu
抱歉,n x m... 我会修改示例。 - Jörn Hees
3个回答

14

在进一步调试之后,我自己找到了解决办法:

coords = np.argwhere(a)
x_min, y_min = coords.min(axis=0)
x_max, y_max = coords.max(axis=0)
b = cropped = a[x_min:x_max+1, y_min:y_max+1]

以上内容适用于布尔数组。如果您有其他条件,例如阈值t并且想要裁剪大于t的值,则只需修改第一行:

coords = np.argwhere(a > t)

很好的回答。我只是会将变量名中的“x”替换为“col”,将“y”替换为“row”。在处理图像时,通常需要使用“y”作为第一个索引的约定。 - PiRK

7

这里有一个使用切片和 argmax 来获取边界的例子 -

def smallestbox(a):
    r = a.any(1)
    if r.any():
        m,n = a.shape
        c = a.any(0)
        out = a[r.argmax():m-r[::-1].argmax(), c.argmax():n-c[::-1].argmax()]
    else:
        out = np.empty((0,0),dtype=bool)
    return out

范例运行 -


In [142]: a
Out[142]: 
array([[False, False, False, False, False, False],
       [False,  True, False,  True, False, False],
       [False,  True,  True, False, False, False],
       [False, False, False, False, False, False]])

In [143]: smallestbox(a)
Out[143]: 
array([[ True, False,  True],
       [ True,  True, False]])

In [144]: a[:] = 0

In [145]: smallestbox(a)
Out[145]: array([], shape=(0, 0), dtype=bool)

In [146]: a[2,2] = 1

In [147]: smallestbox(a)
Out[147]: array([[ True]])

基准测试

其他方法 -

def argwhere_app(a): # @Jörn Hees's soln
    coords = np.argwhere(a)
    x_min, y_min = coords.min(axis=0)
    x_max, y_max = coords.max(axis=0)
    return a[x_min:x_max+1, y_min:y_max+1]

稀疏度不同程度的时间(大约10%,50%和90%) -

In [370]: np.random.seed(0)
     ...: a = np.random.rand(5000,5000)>0.1

In [371]: %timeit argwhere_app(a)
     ...: %timeit smallestbox(a)
1 loop, best of 3: 310 ms per loop
100 loops, best of 3: 3.19 ms per loop

In [372]: np.random.seed(0)
     ...: a = np.random.rand(5000,5000)>0.5

In [373]: %timeit argwhere_app(a)
     ...: %timeit smallestbox(a)
1 loop, best of 3: 324 ms per loop
100 loops, best of 3: 3.21 ms per loop

In [374]: np.random.seed(0)
     ...: a = np.random.rand(5000,5000)>0.9

In [375]: %timeit argwhere_app(a)
     ...: %timeit smallestbox(a)
10 loops, best of 3: 106 ms per loop
100 loops, best of 3: 3.19 ms per loop

0
a = np.transpose(a[np.sum(a,1) != 0])
a = np.transpose(a[np.sum(a,1) != 0])

它不是最快的,但还可以。


1
如果水平或垂直方向上有一个空行,它也将删除该行。不过,对于这个问题并不是100%清楚,不确定OP是否想要这样做。 - Divakar
@Divakar 哎呀,没错!我会把庆祝用的饼干放回罐子里。 - IcedLance

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