2020年1月修订:将可迭代返回值从列表更改为生成器以节省内存。
2020年10月修订:将生成器放在单独的函数中,因为混合使用生成器和return
语句不直观。
以下是我目前拥有的配方:
def window_nd(a, window, steps = None, axis = None, gen_data = False):
"""
Create a windowed view over `n`-dimensional input that uses an
`m`-dimensional window, with `m <= n`
Parameters
-------------
a : Array-like
The array to create the view on
window : tuple or int
If int, the size of the window in `axis`, or in all dimensions if
`axis == None`
If tuple, the shape of the desired window. `window.size` must be:
equal to `len(axis)` if `axis != None`, else
equal to `len(a.shape)`, or
1
steps : tuple, int or None
The offset between consecutive windows in desired dimension
If None, offset is one in all dimensions
If int, the offset for all windows over `axis`
If tuple, the steps along each `axis`.
`len(steps)` must me equal to `len(axis)`
axis : tuple, int or None
The axes over which to apply the window
If None, apply over all dimensions
if tuple or int, the dimensions over which to apply the window
gen_data : boolean
returns data needed for a generator
Returns
-------
a_view : ndarray
A windowed view on the input array `a`, or `a, wshp`, where `whsp` is the window shape needed for creating the generator
"""
ashp = np.array(a.shape)
if axis != None:
axs = np.array(axis, ndmin = 1)
assert np.all(np.in1d(axs, np.arange(ashp.size))), "Axes out of range"
else:
axs = np.arange(ashp.size)
window = np.array(window, ndmin = 1)
assert (window.size == axs.size) | (window.size == 1), "Window dims and axes don't match"
wshp = ashp.copy()
wshp[axs] = window
assert np.all(wshp <= ashp), "Window is bigger than input array in axes"
stp = np.ones_like(ashp)
if steps:
steps = np.array(steps, ndmin = 1)
assert np.all(steps > 0), "Only positive steps allowed"
assert (steps.size == axs.size) | (steps.size == 1), "Steps and axes don't match"
stp[axs] = steps
astr = np.array(a.strides)
shape = tuple((ashp - wshp) // stp + 1) + tuple(wshp)
strides = tuple(astr * stp) + tuple(astr)
as_strided = np.lib.stride_tricks.as_strided
a_view = np.squeeze(as_strided(a,
shape = shape,
strides = strides))
if gen_data :
return a_view, shape[:-wshp.size]
else:
return a_view
def window_gen(a, window, **kwargs):
_ = kwargs.pop(gen_data, False)
a_view, shp = window_nd(a, window, gen_data = True, **kwargs)
for idx in np.ndindex(shp):
yield a_view[idx]
一些测试用例:
a = np.arange(1000).reshape(10,10,10)
window_nd(a, 4).shape
Out: (7, 7, 7, 4, 4, 4)
window_nd(a, 2, 2).shape
Out: (5, 5, 5, 2, 2, 2)
window_nd(a, 2, 1, 0).shape
Out: (9, 2, 10, 10)
window_nd(a, 2, 2, (0,1)).shape
Out: (5, 5, 2, 2, 10)
window_nd(a,(4,3,2)).shape
Out: (7, 8, 9, 4, 3, 2)
window_nd(a,(4,3,2),(1,5,2),(0,2,1)).shape
Out: (7, 5, 2, 4, 2, 3)
skimage.util.view_as_windows()
。自己编写并不值得。 - Nils Wernerskimage.util.view_as_windows()
已经被升级到nD版本了,自从我写这个东西之后。无论如何,对于商业API来说,numpy
比skimage
更常见(当我最初编写时,因需处理非可扩展的abaqus
API而陷入困境),因此它仍然对某些人有用。 - Daniel Fskimage
即可。 - Nils Werner