使用np.lib.stride_tricks.as_strided。此解决方案不需要跨步来划分输入堆栈的相应维度。它甚至允许重叠补丁(只是在这种情况下不要写入结果,或者制作副本。)。因此,它比其他方法更灵活:
import numpy as np
from numpy.lib import stride_tricks
def cutup(data, blck, strd):
sh = np.array(data.shape)
blck = np.asanyarray(blck)
strd = np.asanyarray(strd)
nbl = (sh - blck) // strd + 1
strides = np.r_[data.strides * strd, data.strides]
dims = np.r_[nbl, blck]
data6 = stride_tricks.as_strided(data, strides=strides, shape=dims)
return data6#.reshape(-1, *blck)
#demo
x = np.zeros((5, 6, 12), int)
y = cutup(x, (2, 2, 3), (3, 3, 5))
y[...] = 1
print(x[..., 0], '\n')
print(x[:, 0, :], '\n')
print(x[0, ...], '\n')
输出:
[[1 1 0 1 1 0]
[1 1 0 1 1 0]
[0 0 0 0 0 0]
[1 1 0 1 1 0]
[1 1 0 1 1 0]]
[[1 1 1 0 0 1 1 1 0 0 0 0]
[1 1 1 0 0 1 1 1 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 0]
[1 1 1 0 0 1 1 1 0 0 0 0]
[1 1 1 0 0 1 1 1 0 0 0 0]]
[[1 1 1 0 0 1 1 1 0 0 0 0]
[1 1 1 0 0 1 1 1 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 0]
[1 1 1 0 0 1 1 1 0 0 0 0]
[1 1 1 0 0 1 1 1 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0 0 0]]
解释。 Numpy 数组按步长组织,每个维度一个,数据点 [x,y,z] 位于内存地址 base + stridex * x + stridey * y + stridez * z 处。
stride_tricks.as_strided 工厂允许直接操作与给定数组共享其内存的新数组的步长和形状。仅当您知道自己在做什么时才尝试此操作,因为不执行任何检查,这意味着您可以通过寻址越界内存来开枪。
代码使用此函数将三个现有维度中的每一个拆分为两个新维度,一个用于对应的块内坐标(这将与原始维度具有相同的步幅,因为块中的相邻点对应于相邻的整个堆栈中的点)和沿该轴的块索引的一维;这将具有步幅 = 原始步幅 x 块步幅。
代码所做的只是计算正确的步幅和尺寸(= 沿三个轴的块尺寸和块数)。
由于数据与原始数组共享,当我们将6d数组的所有点设置为1时,它们也被设置在原始数组中,暴露了演示中的块结构。请注意,函数最后一行中注释掉的 reshape 会破坏此链接,因为它会强制复制。