@@ -1991,6 +1991,107 @@ def _array_perimeter(arr):
19911991 ))
19921992
19931993
1994+ def _unfold (arr , axis , size , step ):
1995+ """
1996+ Append an extra dimension containing sliding windows along *axis*.
1997+
1998+ All windows are of size *size* and begin with every *step* elements.
1999+
2000+ Parameters
2001+ ----------
2002+ arr : ndarray, shape (N_1, ..., N_k)
2003+ The input array
2004+ axis : int
2005+ Axis along which the windows are extracted
2006+ size : int
2007+ Size of the windows
2008+ step : int
2009+ Stride between first elements of subsequent windows.
2010+
2011+ Returns
2012+ -------
2013+ windows : ndarray, shape (N_1, ..., 1 + (N_axis-size)/step, ..., N_k, size)
2014+
2015+ Examples
2016+ --------
2017+ >>> i, j = np.ogrid[:3,:7]
2018+ >>> a = i*10 + j
2019+ >>> a
2020+ array([[ 0, 1, 2, 3, 4, 5, 6],
2021+ [10, 11, 12, 13, 14, 15, 16],
2022+ [20, 21, 22, 23, 24, 25, 26]])
2023+ >>> _unfold(a, axis=1, size=3, step=2)
2024+ array([[[ 0, 1, 2],
2025+ [ 2, 3, 4],
2026+ [ 4, 5, 6]],
2027+
2028+ [[10, 11, 12],
2029+ [12, 13, 14],
2030+ [14, 15, 16]],
2031+
2032+ [[20, 21, 22],
2033+ [22, 23, 24],
2034+ [24, 25, 26]]])
2035+ """
2036+ new_shape = [* arr .shape , size ]
2037+ new_strides = [* arr .strides , arr .strides [axis ]]
2038+ new_shape [axis ] = (new_shape [axis ] - size ) // step + 1
2039+ new_strides [axis ] = new_strides [axis ] * step
2040+ return np .lib .stride_tricks .as_strided (arr ,
2041+ shape = new_shape ,
2042+ strides = new_strides ,
2043+ writeable = False )
2044+
2045+
2046+ def _array_patch_perimeters (x , rstride , cstride ):
2047+ """
2048+ Extract perimeters of patches from *arr*.
2049+
2050+ Extracted patches are of size (*rstride* + 1) x (*cstride* + 1) and
2051+ share perimeters with their neighbors. The ordering of the vertices matches
2052+ that returned by ``_array_perimeter``.
2053+
2054+ Parameters
2055+ ----------
2056+ x : ndarray, shape (N, M)
2057+ Input array
2058+ rstride : int
2059+ Vertical (row) stride between corresponding elements of each patch
2060+ cstride : int
2061+ Horizontal (column) stride between corresponding elements of each patch
2062+
2063+ Returns
2064+ -------
2065+ patches : ndarray, shape (N/rstride * M/cstride, 2 * (rstride + cstride))
2066+ """
2067+ assert rstride > 0 and cstride > 0
2068+ assert (x .shape [0 ] - 1 ) % rstride == 0
2069+ assert (x .shape [1 ] - 1 ) % cstride == 0
2070+ # We build up each perimeter from four half-open intervals. Here is an
2071+ # illustrated explanation for rstride == cstride == 3
2072+ #
2073+ # T T T R
2074+ # L R
2075+ # L R
2076+ # L B B B
2077+ #
2078+ # where T means that this element will be in the top array, R for right,
2079+ # B for bottom and L for left. Each of the arrays below has a shape of:
2080+ #
2081+ # (number of perimeters that can be extracted vertically,
2082+ # number of perimeters that can be extracted horizontally,
2083+ # cstride for top and bottom and rstride for left and right)
2084+ #
2085+ # Note that _unfold doesn't incur any memory copies, so the only costly
2086+ # operation here is the np.concatenate.
2087+ top = _unfold (x [:- 1 :rstride , :- 1 ], 1 , cstride , cstride )
2088+ bottom = _unfold (x [rstride ::rstride , 1 :], 1 , cstride , cstride )[..., ::- 1 ]
2089+ right = _unfold (x [:- 1 , cstride ::cstride ], 0 , rstride , rstride )
2090+ left = _unfold (x [1 :, :- 1 :cstride ], 0 , rstride , rstride )[..., ::- 1 ]
2091+ return (np .concatenate ((top , right , bottom , left ), axis = 2 )
2092+ .reshape (- 1 , 2 * (rstride + cstride )))
2093+
2094+
19942095@contextlib .contextmanager
19952096def _setattr_cm (obj , ** kwargs ):
19962097 """Temporarily set some attributes; restore original state at context exit.
0 commit comments