如何将 numpy 二维数组裁剪为非零值?

问题描述 投票:0回答:4

假设我有一个像这样的 2d 布尔 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)
arrays numpy crop
4个回答
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)

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])

这不是最快的,但也还好。


0
投票

这是 N 维数组的通用解决方案。 我发现在我的应用程序中使用

np.any
np.argwhere 快 15 倍。
def crop_over_axis(vec:np.ndarray, axis:Tuple[int]) -> slice:
    """ Returns a pair of indices that contains non-zero pixels. """
    found = np.any(vec, axis)
    index = np.where(found)[0]
    return slice(index[0], index[-1] + 1)

def crop_array(arr: np.ndarray) -> Tuple[slice]:
    """Returns the tuple of slices that select the non-zero data.
    If all zeros, return None.
    """
    n = arr.ndim
    r = list(range(n))
    rr = tuple(r + r)
    try:
        return tuple(crop_over_axis(arr, rr[i:i+n-1]) for i in range(1,n+1))
    except:
        return None

您可以像这样将切片元组传递到方括号

arr = np.zeros((10,10)) arr[2:5,3:6] = 42 sel = crop_array(arr) print(arr[sel])

输出是...

(slice(2, 5, None), slice(3, 6, None)) [[42. 42. 42.] [42. 42. 42.] [42. 42. 42.]]

	
© www.soinside.com 2019 - 2024. All rights reserved.