沿着第三维度从Numpy ndarray中移除并提取最小值



我有一个形状为(3, 4, 3)的数组,它表示一个3x4像素的图像,具有3个颜色通道(最后一个索引(。我的目标是留下一个(3, 4, 2)阵列,其中每个像素都删除了最低的颜色通道。我可以进行逐像素迭代,但这将非常耗时。使用np.argmin,我可以很容易地提取最小值的索引,这样我就知道哪个颜色通道包含每个像素的最小值。然而,我找不到一种巧妙的索引方法来删除这些值,这样我就只剩下一个(3, 4, 2)数组了。

此外,我还试图通过使用类似array[:, :, indexMin)]的东西来选择最小值,但无法获得包含每个像素的最小通道值的形状为(3, 4)的所需阵列。我知道有一个函数np.amin,但它会让我更好地理解Numpy数组。下面提供了我的代码结构的一个最小示例:

import numpy as np
arr = [[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12]]
array = np.zeros((3, 4, 3))
array[:,:,0] = arr
array[:,:,1] = np.fliplr(arr)
array[:,:,2] = np.flipud(arr)
indexMin = np.argmin(array, axis=2)

您需要能够正确广播到所需输出形状的数组。您可以使用np.expand_dims:将缺失的维度添加回

index = np.expand_dims(np.argmin(array, axis=2), axis=2)

这使得设置或提取想要删除的元素变得容易:

index = list(np.indices(array.shape, sparse=True))
index[-1] = np.expand_dims(np.argmin(array, axis=2), axis=2)
minima = array[tuple(index)]

CCD_ 9与CCD_。一个更好的选择是使用np.take_along_axis:

index = np.expand_dims(np.argmin(array, axis=2), axis=2)
minima = np.take_along_axis(array, index, axis=2)

您可以使用这些结果创建掩码,例如使用np.put_along_axis:

mask = np.ones(array.shape, dtype=bool)
np.put_along_axis(mask, index, 0, axis=2)

用掩码索引数组可以得到:

result = array[mask].reshape(*array.shape[:2], -1)

整形之所以有效,是因为像素存储在最后一个维度中,该维度在内存中应该是连续的。这意味着掩模可以正确地移除三个元素中的一个,从而在内存中正确排序。这在掩蔽操作中并不常见。

另一种选择是将np.delete与散列阵列和np.ravel_multi_index:一起使用

i = np.indices(array.shape[:2], sparse=True)
index = np.ravel_multi_index((*i, np.argmin(array, axis=2)), array.shape)
result = np.delete(array.ravel(), index).reshape(*array.shape[:2], -1)

为了好玩,您可以使用每个像素只有三个元素的事实来创建要保留的元素的完整索引。其思想是所有三个指数的总和为CCD_ 15。因此,3 - np.argmin(array, axis=2) - np.argmax(array, axis=2)是中值元素。如果你把中位数和最大值叠加起来,你会得到一个类似于sort给你的指数:

amax = np.argmax(array, axis=2)
amin = np.argmin(array, axis=2)
index = np.stack((np.clip(3 - (amin + amax), 0, 2), amax), axis=2)
result = np.take_along_axis(array, index, axis=2)

np.clip的调用对于处理所有元素相等的情况是必要的,在这种情况下,argmaxargmin都返回零。

定时

比较方法:

def remove_min_indices(array):
index = list(np.indices(array.shape, sparse=True))
index[-1] = np.expand_dims(np.argmin(array, axis=2), axis=2)
mask = np.ones(array.shape, dtype=bool)
mask[tuple(index)] = False
return array[mask].reshape(*array.shape[:2], -1)
def remove_min_put(array):
mask = np.ones(array.shape, dtype=bool)
np.put_along_axis(mask, np.expand_dims(np.argmin(array, axis=2), axis=2), 0, axis=2)
return array[mask].reshape(*array.shape[:2], -1)
def remove_min_delete(array):
i = np.indices(array.shape[:2], sparse=True)
index = np.ravel_multi_index((*i, np.argmin(array, axis=2)), array.shape)
return np.delete(array.ravel(), index).reshape(*array.shape[:2], -1)
def remove_min_sort_c(array):
return np.sort(array, axis=2)[..., 1:]
def remove_min_sort_i(array):
array.sort(axis=2)
return array[..., 1:]
def remove_min_median(array):
amax = np.argmax(array, axis=2)
amin = np.argmin(array, axis=2)
index = np.stack((np.clip(3 - (amin + amax), 0, 2), amax), axis=2)
return np.take_along_axis(array, index, axis=2)

{100, 1K, 10K, 100K, 1M}:中为N测试类似array = np.random.randint(10, size=(N, N, 3), dtype=np.uint8)的阵列

N  |   IND   |   PUT   |   DEL   |   S_C   |   S_I   |   MED   |
-----+---------+---------+---------+---------+---------+---------+
100  | 648. µs | 658. µs | 765. µs | 497. µs | 246. µs | 905. µs |
1K | 67.9 ms | 68.1 ms | 85.7 ms | 51.7 ms | 24.0 ms | 123. ms |
10K | 6.86 s  | 6.86 s  | 8.72 s  | 5.17 s  | 2.39 s  | 13.2 s  |
-----+---------+---------+---------+---------+---------+---------+

N^2的时间刻度,如预期。排序返回的结果与其他方法不同,但显然是最有效的。对于较大的数组,使用put_along_axis屏蔽似乎是更有效的方法,而对于较小的数组,原始索引似乎更有效。

如果您不关心输出顺序,那么array.partition(1)array.sort()np.sort(array)是最快的。在10K随机矩阵上,array.sort()以微弱优势获胜。array[..., 1:]将包含结果。这是有趣的,因为人们可能期望partition()sort()快一些。

Python 3.7, Numpy 1.16.5, with MKL
----------------------------------
array.partition : 5.57 s
np.sort         : 5.15 s
array.sort      : 5.02 s

如果顺序很重要,请转到疯狂物理学家的numpy.put_along_axis解决方案。我之前建议的解决方案的速度大约是这个的两倍:

ind = array.argpartition(1)[..., 1:]
ind.sort()
np.take_along_axis(array, ind, -1)

最新更新