【问题标题】:How to crop a numpy 2d array to non-zero values?如何将 numpy 二维数组裁剪为非零值?
【发布时间】:2018-08-05 20:47:35
【问题描述】:

假设我有一个像这样的二维布尔 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)

【问题讨论】:

  • 是否必须像示例中那样为 3x3,或者大小可以变化?应该是 n x n 还是可以是 n x m?
  • 对不起,n x m...我将修改示例

标签: arrays numpy crop


【解决方案1】:

经过更多的摆弄,我自己找到了一个解决方案:

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”作为第一个索引的约定。
【解决方案2】:

这是一个带有切片和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

【讨论】:

    【解决方案3】:
    a = np.transpose(a[np.sum(a,1) != 0])
    a = np.transpose(a[np.sum(a,1) != 0])
    

    这不是最快的,但没关系。

    【讨论】:

    • 如果水平或垂直有一个空行,它也会删除它。不过,这个问题并不是 100% 清楚,如果这就是 OP 想要的还是其他的。
    • @Divakar 哎哟,真的!我会把我的庆祝饼干放回罐子里(
    猜你喜欢
    • 2022-12-02
    • 1970-01-01
    • 2012-11-02
    • 2017-01-20
    • 1970-01-01
    • 1970-01-01
    • 2020-07-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多