Using the numpy `as_strided` function to create patches, tiles, sliding or sliding windows of arbitrary size

Some morning passed this morning, looking for a generalized question to point out duplicates of questions about as_strided and / or how to make generalized window functions . There seem to be many questions about how to (safely) create patches, sliding windows, rolling windows, tiles or array views for machine learning, convolution, image processing and / or numerical integration.

I am looking for a generic function that can take window , step and axis parameters and return an as_strided for arbitrary dimensions. I will give my answer below, but I am wondering if anyone can make a more efficient method, since I am not sure that using np.squeeze() is the best method, I am not sure that my assert make the function safe enough to write the resulting view, and I'm not sure how to handle the boundary case of the axis not in ascending order.

DUE DILIGENCE

The most generalized function I can find is sklearn.feature_extraction.image.extract_patches written by @eickenberg (and also apparently equivalent to skimage.util.view_as_windows ), but they are not well documented on the network and cannot make windows with less the number of axes than in the original array (for example, this question asks for a window of a certain size along only one axis). Also often questions require a numpy answer.

@Divakar created a generic numpy function for 1-d inputs here , but higher level inputs require a bit more caution. I made a bare-bone 2D window on top of the three-dimensional input method , but it is not very extensible.

+3
source share
1 answer

Here is the recipe that I still have:

 def window_nd(a, window, steps = None, axis = None, outlist = 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 outlist : boolean If output should be as list of windows. If False, it will be an array with `a.nidim + 1 <= a_view.ndim <= a.ndim *2`. If True, output is a list of arrays with `a_view[0].ndim = a.ndim` Warning: this is a memory-intensive copy and not a view Returns ------- a_view : ndarray A windowed view on the input array `a`, or copied list of windows """ 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 outlist: return list(a_view.reshape((-1,) + tuple(wshp))) else: return a_view 

Some test cases:

 a = np.arange(1000).reshape(10,10,10) window_nd(a, 4).shape # sliding (4x4x4) window Out: (7, 7, 7, 4, 4, 4) window_nd(a, 2, 2).shape # (2x2x2) blocks Out: (5, 5, 5, 2, 2, 2) window_nd(a, 2, 1, 0).shape # sliding window of width 2 over axis 0 Out: (9, 2, 10, 10) window_nd(a, 2, 2, (0,1)).shape # tiled (2x2) windows over first and second axes Out: (5, 5, 2, 2, 10) window_nd(a,(4,3,2)).shape # arbitrary sliding window Out: (7, 8, 9, 4, 3, 2) window_nd(a,(4,3,2),(1,5,2),(0,2,1)).shape #arbitrary windows, steps and axis Out: (7, 5, 2, 4, 2, 3) # note shape[-3:] != window as axes are out of order 
+2
source

Source: https://habr.com/ru/post/1274883/


All Articles