kwarray.util_slider

Module Contents

Classes

SlidingWindow

Slide a window of a certain shape over an array with a larger shape.

Stitcher

Stitches multiple possibly overlapping slices into a larger array.

Functions

_slices1d(margin, stop, step=None, start=0, keepbound=False, check=True)

Helper to generates slices in a single dimension.

Attributes

torch

kwarray.util_slider.torch
class kwarray.util_slider.SlidingWindow(shape, window, overlap=None, stride=None, keepbound=False, allow_overshoot=False)

Bases: ubelt.NiceRepr

Slide a window of a certain shape over an array with a larger shape.

This can be used for iterating over a grid of sub-regions of 2d-images, 3d-volumes, or any n-dimensional array.

Yields slices of shape window that can be used to index into an array with shape shape via numpy / torch fancy indexing. This allows for fast fast iteration over subregions of a larger image. Because we generate a grid-basis using only shapes, the larger image does not need to be in memory as long as its width/height/depth/etc…

Parameters
  • shape (Tuple[int, …]) – shape of source array to slide across.

  • window (Tuple[int, …]) – shape of window that will be slid over the larger image.

  • overlap (float, default=0) – a number between 0 and 1 indicating the fraction of overlap that parts will have. Specifying this is mutually exclusive with stride. Must be 0 <= overlap < 1.

  • stride (int, default=None) – the number of cells (pixels) moved on each step of the window. Mutually exclusive with overlap.

  • keepbound (bool, default=False) – if True, a non-uniform stride will be taken to ensure that the right / bottom of the image is returned as a slice if needed. Such a slice will not obey the overlap constraints. (Defaults to False)

  • allow_overshoot (bool, default=False) – if False, we will raise an error if the window doesn’t slide perfectly over the input shape.

Variables
  • strides (basis_shape - shape of the grid corresponding to the number of) – the sliding window will take.

  • dimension (basis_slices - slices that will be taken in every) –

Yields

Tuple[slice, …]

slices used for numpy indexing, the number of slices

in the tuple

Notes

For each dimension, we generate a basis (which defines a grid), and we slide over that basis.

Example

>>> from kwarray.util_slider import *  # NOQA
>>> shape = (10, 10)
>>> window = (5, 5)
>>> self = SlidingWindow(shape, window)
>>> for i, index in enumerate(self):
>>>     print('i={}, index={}'.format(i, index))
i=0, index=(slice(0, 5, None), slice(0, 5, None))
i=1, index=(slice(0, 5, None), slice(5, 10, None))
i=2, index=(slice(5, 10, None), slice(0, 5, None))
i=3, index=(slice(5, 10, None), slice(5, 10, None))

Example

>>> from kwarray.util_slider import *  # NOQA
>>> shape = (16, 16)
>>> window = (4, 4)
>>> self = SlidingWindow(shape, window, overlap=(.5, .25))
>>> print('self.stride = {!r}'.format(self.stride))
self.stride = [2, 3]
>>> list(ub.chunks(self.grid, 5))
[[(0, 0), (0, 1), (0, 2), (0, 3), (0, 4)],
 [(1, 0), (1, 1), (1, 2), (1, 3), (1, 4)],
 [(2, 0), (2, 1), (2, 2), (2, 3), (2, 4)],
 [(3, 0), (3, 1), (3, 2), (3, 3), (3, 4)],
 [(4, 0), (4, 1), (4, 2), (4, 3), (4, 4)],
 [(5, 0), (5, 1), (5, 2), (5, 3), (5, 4)],
 [(6, 0), (6, 1), (6, 2), (6, 3), (6, 4)]]

Example

>>> # Test shapes that dont fit
>>> # When the window is bigger than the shape, the left-aligned slices
>>> # are returend.
>>> self = SlidingWindow((3, 3), (12, 12), allow_overshoot=True, keepbound=True)
>>> print(list(self))
[(slice(0, 12, None), slice(0, 12, None))]
>>> print(list(SlidingWindow((3, 3), None, allow_overshoot=True, keepbound=True)))
[(slice(0, 3, None), slice(0, 3, None))]
>>> print(list(SlidingWindow((3, 3), (None, 2), allow_overshoot=True, keepbound=True)))
[(slice(0, 3, None), slice(0, 2, None)), (slice(0, 3, None), slice(1, 3, None))]
__nice__(self)
_compute_stride(self, overlap, stride, shape, window)

Ensures that stride hasoverlap the correct shape. If stride is not provided, compute stride from desired overlap.

__len__(self)
_iter_basis_frac(self)
__iter__(self)
__getitem__(self, index)

Get a specific item by its flat (raveled) index

Example

>>> from kwarray.util_slider import *  # NOQA
>>> window = (10, 10)
>>> shape = (20, 20)
>>> self = SlidingWindow(shape, window, stride=5)
>>> itered_items = list(self)
>>> assert len(itered_items) == len(self)
>>> indexed_items = [self[i] for i in range(len(self))]
>>> assert itered_items[0] == self[0]
>>> assert itered_items[-1] == self[-1]
>>> assert itered_items == indexed_items
property grid(self)

Generate indices into the “basis” slice for each dimension. This enumerates the nd indices of the grid.

Yields

Tuple[int, …]

property slices(self)

Generate slices for each window (equivalent to iter(self))

Example

>>> shape = (220, 220)
>>> window = (10, 10)
>>> self = SlidingWindow(shape, window, stride=5)
>>> list(self)[41:45]
[(slice(0, 10, None), slice(205, 215, None)),
 (slice(0, 10, None), slice(210, 220, None)),
 (slice(5, 15, None), slice(0, 10, None)),
 (slice(5, 15, None), slice(5, 15, None))]
>>> print('self.overlap = {!r}'.format(self.overlap))
self.overlap = [0.5, 0.5]
property centers(self)

Generate centers of each window

Yields

Tuple[float, …] – the center coordinate of the slice

Example

>>> shape = (4, 4)
>>> window = (3, 3)
>>> self = SlidingWindow(shape, window, stride=1)
>>> list(zip(self.centers, self.slices))
[((1.0, 1.0), (slice(0, 3, None), slice(0, 3, None))),
 ((1.0, 2.0), (slice(0, 3, None), slice(1, 4, None))),
 ((2.0, 1.0), (slice(1, 4, None), slice(0, 3, None))),
 ((2.0, 2.0), (slice(1, 4, None), slice(1, 4, None)))]
>>> shape = (3, 3)
>>> window = (2, 2)
>>> self = SlidingWindow(shape, window, stride=1)
>>> list(zip(self.centers, self.slices))
[((0.5, 0.5), (slice(0, 2, None), slice(0, 2, None))),
 ((0.5, 1.5), (slice(0, 2, None), slice(1, 3, None))),
 ((1.5, 0.5), (slice(1, 3, None), slice(0, 2, None))),
 ((1.5, 1.5), (slice(1, 3, None), slice(1, 3, None)))]
class kwarray.util_slider.Stitcher(stitcher, shape, device='numpy')

Bases: ubelt.NiceRepr

Stitches multiple possibly overlapping slices into a larger array.

This is used to invert the SlidingWindow. For semenatic segmentation the patches are probability chips. Overlapping chips are averaged together.

Parameters

shape (tuple) – dimensions of the large image that will be created from the smaller pixels or patches.

Todo

  • [ ] Look at the old “add_fast” code in the netharn version and see if

    it is worth porting. This code is kept in the dev folder in ../dev/_dev_slider.py

Example

>>> from kwarray.util_slider import *  # NOQA
>>> import sys
>>> # Build a high resolution image and slice it into chips
>>> highres = np.random.rand(5, 200, 200).astype(np.float32)
>>> target_shape = (1, 50, 50)
>>> slider = SlidingWindow(highres.shape, target_shape, overlap=(0, .5, .5))
>>> # Show how Sticher can be used to reconstruct the original image
>>> stitcher = Stitcher(slider.input_shape)
>>> for sl in list(slider):
...     chip = highres[sl]
...     stitcher.add(sl, chip)
>>> assert stitcher.weights.max() == 4, 'some parts should be processed 4 times'
>>> recon = stitcher.finalize()
__nice__(stitcher)
add(stitcher, indices, patch, weight=None)

Incorporate a new (possibly overlapping) patch or pixel using a weighted sum.

Parameters
  • indices (slice or tuple) – typically a Tuple[slice] of pixels or a single pixel, but this can be any numpy fancy index.

  • patch (ndarray) – data to patch into the bigger image.

  • weight (float or ndarray) – weight of this patch (default to 1.0)

average(stitcher)

Averages out contributions from overlapping adds using weighted average

Returns

ndarray: the stitched image

Return type

out

finalize(stitcher, indices=None)

Averages out contributions from overlapping adds

Parameters

indices (None | slice | tuple) – if None, finalize the entire block, otherwise only finalize a subregion.

Returns

ndarray: the stitched image

Return type

final

kwarray.util_slider._slices1d(margin, stop, step=None, start=0, keepbound=False, check=True)

Helper to generates slices in a single dimension.

Parameters
  • margin (int) – the length of the slice (window)

  • stop (int) – the length of the image dimension

  • step (int, default=None) – the length of each step / distance between slices

  • start (int, default=0) – starting point (in most cases set this to 0)

  • keepbound (bool) – if True, a non-uniform step will be taken to ensure that the right / bottom of the image is returned as a slice if needed. Such a slice will not obey the overlap constraints. (Defaults to False)

  • check (bool) – if True an error will be raised if the window does not cover the entire extent from start to stop, even if keepbound is True.

Yields

slice – slice in one dimension of size (margin)

Example

>>> stop, margin, step = 2000, 360, 360
>>> keepbound = True
>>> strides = list(_slices1d(margin, stop, step, keepbound, check=False))
>>> assert all([(s.stop - s.start) == margin for s in strides])

Example

>>> stop, margin, step = 200, 46, 7
>>> keepbound = True
>>> strides = list(_slices1d(margin, stop, step, keepbound=False, check=True))
>>> starts = np.array([s.start for s in strides])
>>> stops = np.array([s.stop for s in strides])
>>> widths = stops - starts
>>> assert np.all(np.diff(starts) == step)
>>> assert np.all(widths == margin)

Example

>>> import pytest
>>> stop, margin, step = 200, 36, 7
>>> with pytest.raises(ValueError):
...     list(_slices1d(margin, stop, step))