:py:mod:`kwarray.util_slices` ============================= .. py:module:: kwarray.util_slices .. autoapi-nested-parse:: Utilities related to slicing .. rubric:: References https://stackoverflow.com/questions/41153803/zero-padding-slice-past-end-of-array-in-numpy .. todo:: - [ ] Could have a kwarray function to expose this inverse slice functionality. Also having a top-level call to apply an embedded slice would be good. Module Contents --------------- Functions ~~~~~~~~~ .. autoapisummary:: kwarray.util_slices.padded_slice kwarray.util_slices.apply_embedded_slice kwarray.util_slices._apply_padding kwarray.util_slices.embed_slice Attributes ~~~~~~~~~~ .. autoapisummary:: kwarray.util_slices.__TODO__ .. py:function:: padded_slice(data, slices, pad=None, padkw=None, return_info=False) Allows slices with out-of-bound coordinates. Any out of bounds coordinate will be sampled via padding. :Parameters: * **data** (*Sliceable[T]*) -- data to slice into. Any channels must be the last dimension. * **slices** (*slice | Tuple[slice, ...]*) -- slice for each dimensions * **ndim** (*int*) -- number of spatial dimensions * **pad** (*List[int|Tuple]*) -- additional padding of the slice * **padkw** (*Dict*) -- if unspecified defaults to ``{'mode': 'constant'}`` * **return_info** (*bool, default=False*) -- if True, return extra information about the transform. .. note:: Negative slices have a different meaning here then they usually do. Normally, they indicate a wrap-around or a reversed stride, but here they index into out-of-bounds space (which depends on the pad mode). For example a slice of -2:1 literally samples two pixels to the left of the data and one pixel from the data, so you get two padded values and one data value. SeeAlso: embed_slice - finds the embedded slice and padding :returns: data_sliced: subregion of the input data (possibly with padding, depending on if the original slice went out of bounds) Tuple[Sliceable, Dict] : data_sliced : as above transform : information on how to return to the original coordinates Currently a dict containing: st_dims: a list indicating the low and high space-time coordinate values of the returned data slice. The structure of this dictionary mach change in the future :rtype: Sliceable .. rubric:: Example >>> data = np.arange(5) >>> slices = [slice(-2, 7)] >>> data_sliced = padded_slice(data, slices) >>> print(ub.repr2(data_sliced, with_dtype=False)) np.array([0, 0, 0, 1, 2, 3, 4, 0, 0]) >>> data_sliced = padded_slice(data, slices, pad=(3, 3)) >>> print(ub.repr2(data_sliced, with_dtype=False)) np.array([0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 0, 0, 0, 0, 0]) >>> data_sliced = padded_slice(data, slice(3, 4), pad=[(1, 0)]) >>> print(ub.repr2(data_sliced, with_dtype=False)) np.array([2, 3]) .. py:data:: __TODO__ :annotation: = Multiline-String .. raw:: html
Show Value .. code-block:: text :linenos: - [ ] Could have a kwarray function to expose this inverse slice functionality. Also having a top-level call to apply an embedded slice would be good. chip_index = tuple([slice(tl_y, br_y), slice(tl_x, br_x)]) data_slice, padding = kwarray.embed_slice(chip_index, imdata.shape) # TODO: could have a kwarray function to expose this inverse slice # functionality. Also having a top-level call to apply an embedded # slice would be good inverse_slice = ( slice(padding[0][0], imdata.shape[0] - padding[0][1]), slice(padding[1][0], imdata.shape[1] - padding[1][1]), ) chip = kwarray.padded_slice(imdata, chip_index) chip = imdata[chip_index] fgdata = function(chip) # Apply just the data part back to the original imdata[tl_y:br_y, tl_x:br_x, :] = fgdata[inverse_slice] .. raw:: html
.. py:function:: apply_embedded_slice(data, data_slice, extra_padding, **padkw) Apply a precomputed embedded slice. This is used as a subroutine in padded_slice. :Parameters: * **data** (*ndarray*) -- data to slice * **data_slice** (*Tuple[slice]*) * **extra_padding** (*Tuple[slice]*) :returns: ndarray .. py:function:: _apply_padding(array, pad_width, **padkw) Alternative to numpy pad with different short-cut semantics for the "pad_width" argument. Unlike numpy pad, you must specify a (start, stop) tuple for each dimension. The shortcut is that you only need to specify this for the leading dimensions. Any unspecified trailing dimension will get an implicit (0, 0) padding. TODO: does this get exposed as a public function? .. py:function:: embed_slice(slices, data_dims, pad=None) Embeds a "padded-slice" inside known data dimension. Returns the valid data portion of the slice with extra padding for regions outside of the available dimension. Given a slices for each dimension, image dimensions, and a padding get the corresponding slice from the image and any extra padding needed to achieve the requested window size. .. todo:: - [ ] Add the option to return the inverse slice :Parameters: * **slices** (*Tuple[slice, ...]*) -- a tuple of slices for to apply to data data dimension. * **data_dims** (*Tuple[int, ...]*) -- n-dimension data sizes (e.g. 2d height, width) * **pad** (*List[int|Tuple]*) -- extra pad applied to (left and right) / (both) sides of each slice dim :returns: data_slice - Tuple[slice] a slice that can be applied to an array with with shape `data_dims`. This slice will not correspond to the full window size if the requested slice is out of bounds. extra_padding - extra padding needed after slicing to achieve the requested window size. :rtype: Tuple .. rubric:: Example >>> # Case where slice is inside the data dims on left edge >>> from kwarray.util_slices import * # NOQA >>> slices = (slice(0, 10), slice(0, 10)) >>> data_dims = [300, 300] >>> pad = [10, 5] >>> a, b = embed_slice(slices, data_dims, pad) >>> print('data_slice = {!r}'.format(a)) >>> print('extra_padding = {!r}'.format(b)) data_slice = (slice(0, 20, None), slice(0, 15, None)) extra_padding = [(10, 0), (5, 0)] .. rubric:: Example >>> # Case where slice is bigger than the image >>> slices = (slice(-10, 400), slice(-10, 400)) >>> data_dims = [300, 300] >>> pad = [10, 5] >>> a, b = embed_slice(slices, data_dims, pad) >>> print('data_slice = {!r}'.format(a)) >>> print('extra_padding = {!r}'.format(b)) data_slice = (slice(0, 300, None), slice(0, 300, None)) extra_padding = [(20, 110), (15, 105)] .. rubric:: Example >>> # Case where slice is inside than the image >>> slices = (slice(10, 40), slice(10, 40)) >>> data_dims = [300, 300] >>> pad = None >>> a, b = embed_slice(slices, data_dims, pad) >>> print('data_slice = {!r}'.format(a)) >>> print('extra_padding = {!r}'.format(b)) data_slice = (slice(10, 40, None), slice(10, 40, None)) extra_padding = [(0, 0), (0, 0)]