此代码是交换的 RBG 图像的第一个和最后一个通道,该图像已加载到Numpy数组中:
imap
虽然我了解将 Ellipsis 用于在Numpy数组中进行切片的方法,但我在这里不了解Ellipsis的用法。有人可以解释这里到底发生了什么吗?
答案 0 :(得分:1)
img[..., [2, 1, 0]]
产生的结果与对索引数组img[:, :, i]
中的每个i
提取切片[2, 1, 0]
并沿着{{ 1}}。换句话说:
img
将产生与以下相同的输出:
img[..., [2,1,0]]
省略号np.stack([img[:,:,2], img[:,:,1], img[:,:,0]], axis=2)
是一个占位符,它告诉numpy将索引数组应用于哪个轴。如果没有...
,则索引数组将应用于...
的第一个轴,而不是最后一个轴。因此,在没有img
的情况下,索引语句为:
...
将产生与以下相同的输出:
img[[2,1,0]]
这是docs call "Combining advanced and basic indexing"的示例:
如果索引中至少有一个切片(:),省略号(...)或np.newaxis(或者数组的维数多于高级索引),则行为可能会更加复杂。就像将每个高级索引元素的索引结果连接起来一样。
在此继续描述
在情况下,高级索引操作的维度[在您的示例
np.stack([img[2,:,:], img[1,:,:], img[0,:,:]], axis=0)
中)被插入到结果数组中,其位置与初始数组中的位置相同(后一种逻辑使简单的高级索引行为得以实现)就像切片一样。
文档不是最容易理解的,但是在这种情况下,将其拆分并不难。从更简单的2D案例开始:
[2, 1, 0]
使用具有单个索引值的相同类型的高级索引会产生:
arr = np.arange(12).reshape(4,3)
array([[ 0, 1, 2],
[ 3, 4, 5],
[ 6, 7, 8],
[ 9, 10, 11]])
是arr[:, [1]]
array([[ 1],
[ 4],
[ 7],
[10]])
的第一列。换句话说,就像您在固定最后一个轴的索引的同时,从arr
产生了所有可能的值。就像@hpaulj在他的评论中说的那样,省略号可以充当占位符。它有效地告诉numpy在除应用索引数组的最后一个轴之外的所有轴上自由迭代。
您也可以根据需要使用此索引语法对arr
的列进行混洗:
arr
这与示例中的操作基本相同,但是在2D数组而不是3D数组上。
您可以将arr[..., [1,0,2]]
array([[ 1, 0, 2],
[ 4, 3, 5],
[ 7, 6, 8],
[10, 9, 11]])
分解为更简单的索引操作,以说明发生了什么。就像您首先获取arr[..., [1,0,2]]
的返回值一样:
arr[..., [1]]
然后返回array([[ 1],
[ 4],
[ 7],
[10]])
的返回值:
arr[..., [0]]
然后返回array([[0],
[3],
[6],
[9]])
的返回值:
arr[..., [1]]
,然后将所有这些结果最终连接到形状为array([[ 2],
[ 5],
[ 8],
[11]])
的单个数组中,其中(*arr.shape[:-1], len(ix))
是索引数组。沿最后一个轴的数据按照ix = [2, 0, 1]
中的顺序排序。
一种精确理解省略号的好方法是在不使用省略号的情况下执行相同的操作:
ix
在这种情况下,索引数组应用于arr[[1,0,2]]
array([[6, 7, 8],
[0, 1, 2],
[3, 4, 5]])
的第一条轴,因此输出是一个包含arr
的{{1}}行的数组。在索引数组之前添加[1,0,2]
会通知numpy将索引数组应用于arr
的最后一个轴。
您询问的情况是上述2D ...
示例的3D等效项。假设arr
是arr[..., [1,0,2]]
。您可以考虑将img.shape
遍历(480, 640, 3)
中的每个值img[..., [2, 1, 0]]
。对于每个i
,分度操作将收集形状为ix=[2, 1, 0]
的平板,该平板沿着i
的最后一个轴的第(480, 640, 1)
个索引。一旦收集了所有三个平板,最终结果将相当于沿着它们的最后一个轴并置(并按照找到的顺序)。
i
和img
之间的唯一区别是arr[..., [1]]
保留了原始数组中数据的形状。
对于2D数组,arr[:,1]
等效于arr[..., [1]]
。 arr[:, [1]]
就像arr[..., [1]]
一样充当占位符,但仅用于一个维度。