如何将 numpy 二维数组裁剪为非零值?
How to crop a numpy 2d array to non-zero values?
假设我有一个像这样的二维布尔 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)
我通常如何将其裁剪为包含所有真值的最小框(矩形、内核)?
所以在上面的例子中:
b = np.array([
[1,0,1],
[1,1,0],
], dtype=bool)
经过一番折腾,我居然自己找到了解决办法:
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)
这是一个带有切片的 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
a = np.transpose(a[np.sum(a,1) != 0])
a = np.transpose(a[np.sum(a,1) != 0])
这不是最快的,但还可以。
假设我有一个像这样的二维布尔 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)
我通常如何将其裁剪为包含所有真值的最小框(矩形、内核)?
所以在上面的例子中:
b = np.array([
[1,0,1],
[1,1,0],
], dtype=bool)
经过一番折腾,我居然自己找到了解决办法:
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)
这是一个带有切片的 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
a = np.transpose(a[np.sum(a,1) != 0])
a = np.transpose(a[np.sum(a,1) != 0])
这不是最快的,但还可以。