ndsampler.delayed

DEPRECATD. THIS IS BEING MOVED TO KWCOCO FOR DEVELOPMENT AND EVENTUALLY WILL LIVE IN KWIMAGE.

The classes in this file represent a tree of delayed operations.

Proof of concept for delayed chainable transforms in Python.

There are several optimizations that could be applied.

This is similar to GDAL’s virtual raster table, but it works in memory and I think it is easier to chain operations.

SeeAlso:

../dev/symbolic_delayed.py

Concepts:

Each class should be a layer that adds a new transformation on top of underlying nested layers. Adding new layers should be quick, and there should always be the option to “finalize” a stack of layers, chaining the transforms / operations and then applying one final efficient transform at the end.

Conventions:

  • dsize = (always in width / height), no channels are present

  • shape for images is always (height, width, channels)

  • channels are always the last dimension of each image, if no channel dim is specified, finalize will add it.

  • Videos must be the last process in the stack, and add a leading

    time dimension to the shape. dsize is still width, height, but shape is now: (time, height, width, chan)

Example

>>> # Example demonstrating the modivating use case
>>> # We have multiple aligned frames for a video, but each of
>>> # those frames is in a different resolution. Furthermore,
>>> # each of the frames consists of channels in different resolutions.
>>> from ndsampler.delayed import *  # NOQA
>>> # Create raw channels in some "native" resolution for frame 1
>>> f1_chan1 = DelayedIdentity.demo('astro', chan=0, dsize=(300, 300))
>>> f1_chan2 = DelayedIdentity.demo('astro', chan=1, dsize=(200, 200))
>>> f1_chan3 = DelayedIdentity.demo('astro', chan=2, dsize=(10, 10))
>>> # Create raw channels in some "native" resolution for frame 2
>>> f2_chan1 = DelayedIdentity.demo('carl', dsize=(64, 64), chan=0)
>>> f2_chan2 = DelayedIdentity.demo('carl', dsize=(260, 260), chan=1)
>>> f2_chan3 = DelayedIdentity.demo('carl', dsize=(10, 10), chan=2)
>>> #
>>> # Delayed warp each channel into its "image" space
>>> # Note: the images never actually enter this space we transform through it
>>> f1_dsize = np.array((3, 3))
>>> f2_dsize = np.array((2, 2))
>>> f1_img = DelayedChannelConcat([
>>>     f1_chan1.delayed_warp(Affine.scale(f1_dsize / f1_chan1.dsize), dsize=f1_dsize),
>>>     f1_chan2.delayed_warp(Affine.scale(f1_dsize / f1_chan2.dsize), dsize=f1_dsize),
>>>     f1_chan3.delayed_warp(Affine.scale(f1_dsize / f1_chan3.dsize), dsize=f1_dsize),
>>> ])
>>> f2_img = DelayedChannelConcat([
>>>     f2_chan1.delayed_warp(Affine.scale(f2_dsize / f2_chan1.dsize), dsize=f2_dsize),
>>>     f2_chan2.delayed_warp(Affine.scale(f2_dsize / f2_chan2.dsize), dsize=f2_dsize),
>>>     f2_chan3.delayed_warp(Affine.scale(f2_dsize / f2_chan3.dsize), dsize=f2_dsize),
>>> ])
>>> # Combine frames into a video
>>> vid_dsize = np.array((280, 280))
>>> vid = DelayedFrameConcat([
>>>     f1_img.delayed_warp(Affine.scale(vid_dsize / f1_img.dsize), dsize=vid_dsize),
>>>     f2_img.delayed_warp(Affine.scale(vid_dsize / f2_img.dsize), dsize=vid_dsize),
>>> ])
>>> vid.nesting
>>> print('vid.nesting = {}'.format(ub.repr2(vid.nesting(), nl=-1)))
>>> final = vid.finalize(interpolation='nearest')
>>> # xdoctest: +REQUIRES(--show)
>>> import kwplot
>>> kwplot.autompl()
>>> kwplot.imshow(final[0], pnum=(1, 2, 1), fnum=1)
>>> kwplot.imshow(final[1], pnum=(1, 2, 2), fnum=1)

Module Contents

Classes

DelayedOperation

Base class for nodes in a tree of delayed operations

DelayedVideoOperation

Base class for nodes in a tree of delayed operations

DelayedImageOperation

Operations that pertain only to images

DelayedIdentity

Noop leaf that does nothing. Mostly used in tests atm

DelayedLoad

Example

DelayedFrameConcat

Represents multiple frames in a video

DelayedChannelConcat

Represents multiple channels in an image that could be concatenated

DelayedWarp

POC for chainable transforms

DelayedCrop

Represent a delayed crop operation

Functions

_compute_leaf_subcrop(root_region_bounds, tf_leaf_to_root)

Given a region in a "root" image and a trasnform between that "root" and

_largest_shape(shapes)

Finds maximum over all shapes

_devcheck_corner()

class ndsampler.delayed.DelayedOperation

Bases: ubelt.NiceRepr

Base class for nodes in a tree of delayed operations

__nice__(self)
abstract finalize(self)
abstract children(self)

Abstract method, which should generate all of the direct children of a node in the operation tree.

_optimize_paths(self, **kwargs)

Iterate through the leaf nodes, which are virtually transformed into the root space.

This returns some sort of hueristically optimized leaf repr wrt warps.

nesting(self)
class ndsampler.delayed.DelayedVideoOperation

Bases: DelayedOperation

Base class for nodes in a tree of delayed operations

class ndsampler.delayed.DelayedImageOperation

Bases: DelayedOperation

Operations that pertain only to images

delayed_crop(self, region_slices)

Create a new delayed image that performs a crop in the transformed “self” space.

Parameters

region_slices (Tuple[slice, slice]) – y-slice and x-slice.

Notes

Returns a heuristically “simplified” tree. In the current implementation there are only 3 operations, cat, warp, and crop. All cats go at the top, all crops go at the bottom, all warps are in the middle.

Returns

lazy executed delayed transform

Return type

DelayedWarp

Example

>>> from ndsampler.delayed import *  # NOQA
>>> dsize = (100, 100)
>>> tf2 = Affine.affine(scale=3).matrix
>>> self = DelayedWarp(np.random.rand(33, 33), tf2, dsize)
>>> region_slices = (slice(5, 10), slice(1, 12))
>>> delayed_crop = self.delayed_crop(region_slices)
>>> print(ub.repr2(delayed_crop.nesting(), nl=-1, sort=0))
>>> delayed_crop.finalize()

Example

>>> chan1 = DelayedLoad.demo('astro')
>>> chan2 = DelayedLoad.demo('carl')
>>> warped1a = chan1.delayed_warp(Affine.scale(1.2).matrix)
>>> warped2a = chan2.delayed_warp(Affine.scale(1.5))
>>> warped1b = warped1a.delayed_warp(Affine.scale(1.2).matrix)
>>> warped2b = warped2a.delayed_warp(Affine.scale(1.5))
>>> #
>>> region_slices = (slice(97, 677), slice(5, 691))
>>> self = warped2b
>>> #
>>> crop1 = warped1b.delayed_crop(region_slices)
>>> crop2 = warped2b.delayed_crop(region_slices)
>>> print(ub.repr2(warped1b.nesting(), nl=-1, sort=0))
>>> print(ub.repr2(warped2b.nesting(), nl=-1, sort=0))
>>> # Notice how the crop merges the two nesting layers
>>> # (via the hueristic optimize step)
>>> print(ub.repr2(crop1.nesting(), nl=-1, sort=0))
>>> print(ub.repr2(crop2.nesting(), nl=-1, sort=0))
>>> frame1 = crop1.finalize(dsize=(500, 500))
>>> frame2 = crop2.finalize(dsize=(500, 500))
>>> # xdoctest: +REQUIRES(--show)
>>> import kwplot
>>> kwplot.autompl()
>>> kwplot.imshow(frame1, pnum=(1, 2, 1), fnum=1)
>>> kwplot.imshow(frame2, pnum=(1, 2, 2), fnum=1)
delayed_warp(self, transform, dsize=None)

Delayedly transform the underlying data.

Note

this deviates from kwimage warp functions because instead of “output_dims” (specified in c-style shape) we specify dsize (w, h).

Returns

new delayed transform a chained transform

Return type

DelayedWarp

class ndsampler.delayed.DelayedIdentity(sub_data)

Bases: DelayedImageOperation

Noop leaf that does nothing. Mostly used in tests atm

DelayedIdentity.demo(‘astro’, chan=0, dsize=(32, 32))

__hack_dont_optimize__ = True
classmethod demo(cls, key='astro', chan=None, dsize=None)
children(self)

Abstract method, which should generate all of the direct children of a node in the operation tree.

finalize(self)
class ndsampler.delayed.DelayedLoad(fpath, dsize=None, channels=None)

Bases: DelayedImageOperation

Example

>>> fpath = kwimage.grab_test_image_fpath()
>>> self = DelayedLoad(fpath)
>>> print('self = {!r}'.format(self))
>>> self.load_shape()
>>> print('self = {!r}'.format(self))
>>> f1_img = DelayedLoad.demo('astro', dsize=(300, 300))
>>> f2_img = DelayedLoad.demo('carl', dsize=(256, 320))
>>> print('f1_img = {!r}'.format(f1_img))
>>> print('f2_img = {!r}'.format(f2_img))
>>> print(f2_img.finalize().shape)
>>> print(f1_img.finalize().shape)
__hack_dont_optimize__ = True
classmethod demo(DelayedLoad, key='astro', dsize=None)
abstract classmethod coerce(cls, data)
children(self)

Abstract method, which should generate all of the direct children of a node in the operation tree.

_optimize_paths(self, **kwargs)

Iterate through the leaf nodes, which are virtually transformed into the root space.

This returns some sort of hueristically optimized leaf repr wrt warps.

load_shape(self)
property shape(self)
property num_bands(self)
property dsize(self)
property channels(self)
property fpath(self)
finalize(self, **kwargs)
class ndsampler.delayed.DelayedFrameConcat(frames, dsize=None)

Bases: DelayedVideoOperation

Represents multiple frames in a video

Notes

Video[0]:
Frame[0]:

Chan[0]: (32) +——————————–+ Chan[1]: (16) +—————-+ Chan[2]: ( 8) +——–+

Frame[1]:

Chan[0]: (30) +——————————+ Chan[1]: (14) +————–+ Chan[2]: ( 6) +——+

Todo

  • [ ] Support computing the transforms when none of the data is loaded

Example

>>> # Simpler case with fewer nesting levels
>>> from ndsampler.delayed import *  # NOQA
>>> rng = kwarray.ensure_rng(None)
>>> # Delayed warp each channel into its "image" space
>>> # Note: the images never enter the space we transform through
>>> f1_img = DelayedLoad.demo('astro', (300, 300))
>>> f2_img = DelayedLoad.demo('carl', (256, 256))
>>> # Combine frames into a video
>>> vid_dsize = np.array((100, 100))
>>> self = vid = DelayedFrameConcat([
>>>     f1_img.delayed_warp(Affine.scale(vid_dsize / f1_img.dsize)),
>>>     f2_img.delayed_warp(Affine.scale(vid_dsize / f2_img.dsize)),
>>> ], dsize=vid_dsize)
>>> print(ub.repr2(vid.nesting(), nl=-1, sort=0))
>>> final = vid.finalize(interpolation='nearest', dsize=(32, 32))
>>> # xdoctest: +REQUIRES(--show)
>>> import kwplot
>>> kwplot.autompl()
>>> kwplot.imshow(final[0], pnum=(1, 2, 1), fnum=1)
>>> kwplot.imshow(final[1], pnum=(1, 2, 2), fnum=1)
>>> region_slices = (slice(0, 90), slice(30, 60))
children(self)

Abstract method, which should generate all of the direct children of a node in the operation tree.

property shape(self)
finalize(self, **kwargs)

Execute the final transform

delayed_crop(self, region_slices)

Example

>>> from ndsampler.delayed import *  # NOQA
>>> # Create raw channels in some "native" resolution for frame 1
>>> f1_chan1 = DelayedIdentity.demo('astro', chan=(1, 0), dsize=(300, 300))
>>> f1_chan2 = DelayedIdentity.demo('astro', chan=2, dsize=(10, 10))
>>> # Create raw channels in some "native" resolution for frame 2
>>> f2_chan1 = DelayedIdentity.demo('carl', dsize=(64, 64), chan=(1, 0))
>>> f2_chan2 = DelayedIdentity.demo('carl', dsize=(10, 10), chan=2)
>>> #
>>> f1_dsize = np.array(f1_chan1.dsize)
>>> f2_dsize = np.array(f2_chan1.dsize)
>>> f1_img = DelayedChannelConcat([
>>>     f1_chan1.delayed_warp(Affine.scale(f1_dsize / f1_chan1.dsize), dsize=f1_dsize),
>>>     f1_chan2.delayed_warp(Affine.scale(f1_dsize / f1_chan2.dsize), dsize=f1_dsize),
>>> ])
>>> f2_img = DelayedChannelConcat([
>>>     f2_chan1.delayed_warp(Affine.scale(f2_dsize / f2_chan1.dsize), dsize=f2_dsize),
>>>     f2_chan2.delayed_warp(Affine.scale(f2_dsize / f2_chan2.dsize), dsize=f2_dsize),
>>> ])
>>> vid_dsize = np.array((280, 280))
>>> full_vid = DelayedFrameConcat([
>>>     f1_img.delayed_warp(Affine.scale(vid_dsize / f1_img.dsize), dsize=vid_dsize),
>>>     f2_img.delayed_warp(Affine.scale(vid_dsize / f2_img.dsize), dsize=vid_dsize),
>>> ])
>>> region_slices = (slice(80, 200), slice(80, 200))
>>> crop_vid = full_vid.delayed_crop(region_slices)
>>> print(ub.repr2(full_vid.nesting(), nl=-1, sort=0))
>>> final_full = full_vid.finalize(interpolation='nearest')
>>> final_crop = crop_vid.finalize(interpolation='nearest')
>>> import pytest
>>> with pytest.raises(ValueError):
>>>     # should not be able to crop a crop yet
>>>     crop_vid.delayed_crop(region_slices)
>>> # xdoctest: +REQUIRES(--show)
>>> import kwplot
>>> kwplot.autompl()
>>> kwplot.imshow(final_full[0], pnum=(2, 2, 1), fnum=1)
>>> kwplot.imshow(final_full[1], pnum=(2, 2, 2), fnum=1)
>>> kwplot.imshow(final_crop[0], pnum=(2, 2, 3), fnum=1)
>>> kwplot.imshow(final_crop[1], pnum=(2, 2, 4), fnum=1)
class ndsampler.delayed.DelayedChannelConcat(components, dsize=None)

Bases: DelayedImageOperation

Represents multiple channels in an image that could be concatenated

Variables

components (List[DelayedWarp]) – a list of stackable channels. Each component may be comprised of multiple channels.

Todo

  • [ ] can this be generalized into a delayed concat?

  • [ ] can all concats be delayed until the very end?

Example

>>> comp1 = DelayedWarp(np.random.rand(11, 7))
>>> comp2 = DelayedWarp(np.random.rand(11, 7, 3))
>>> comp3 = DelayedWarp(
>>>     np.random.rand(3, 5, 2),
>>>     transform=Affine.affine(scale=(7/5, 11/3)).matrix,
>>>     dsize=(7, 11)
>>> )
>>> components = [comp1, comp2, comp3]
>>> chans = DelayedChannelConcat(components)
>>> final = chans.finalize()
>>> assert final.shape == chans.shape
>>> assert final.shape == (11, 7, 6)
>>> # We should be able to nest DelayedChannelConcat inside virutal images
>>> frame1 = DelayedWarp(
>>>     chans, transform=Affine.affine(scale=2.2).matrix,
>>>     dsize=(20, 26))
>>> frame2 = DelayedWarp(
>>>     np.random.rand(3, 3, 6), dsize=(20, 26))
>>> frame3 = DelayedWarp(
>>>     np.random.rand(3, 3, 6), dsize=(20, 26))
>>> print(ub.repr2(frame1.nesting(), nl=-1, sort=False))
>>> frame1.finalize()
>>> vid = DelayedFrameConcat([frame1, frame2, frame3])
>>> print(ub.repr2(vid.nesting(), nl=-1, sort=False))
children(self)

Abstract method, which should generate all of the direct children of a node in the operation tree.

classmethod random(cls, num_parts=3, rng=None)
CommandLine:

xdoctest -m ndsampler.delayed DelayedWarp.random

Example

>>> self = DelayedChannelConcat.random()
>>> print('self = {!r}'.format(self))
>>> print(ub.repr2(self.nesting(), nl=-1, sort=0))
property shape(self)
finalize(self, **kwargs)

Execute the final transform

class ndsampler.delayed.DelayedWarp(sub_data, transform=None, dsize=None)

Bases: DelayedImageOperation

POC for chainable transforms

Notes

“sub” is used to refer to the underlying data in its native coordinates and resolution.

“self” is used to refer to the data in the transformed coordinates that are exposed by this class.

Variables
  • sub_data (DelayedWarp | ArrayLike) – array-like image data at a naitive resolution

  • transform (Transform) – transforms data from native “sub”-image-space to “self”-image-space.

Example

>>> from ndsampler.delayed import *  # NOQA
>>> dsize = (12, 12)
>>> tf1 = np.array([[2, 0, 0], [0, 2, 0], [0, 0, 1]])
>>> tf2 = np.array([[3, 0, 0], [0, 3, 0], [0, 0, 1]])
>>> tf3 = np.array([[4, 0, 0], [0, 4, 0], [0, 0, 1]])
>>> band1 = DelayedWarp(np.random.rand(6, 6), tf1, dsize)
>>> band2 = DelayedWarp(np.random.rand(4, 4), tf2, dsize)
>>> band3 = DelayedWarp(np.random.rand(3, 3), tf3, dsize)
>>> #
>>> # Execute a crop in a one-level transformed space
>>> region_slices = (slice(5, 10), slice(0, 12))
>>> delayed_crop = band2.delayed_crop(region_slices)
>>> final_crop = delayed_crop.finalize()
>>> #
>>> # Execute a crop in a nested transformed space
>>> tf4 = np.array([[1.5, 0, 0], [0, 1.5, 0], [0, 0, 1]])
>>> chained = DelayedWarp(band2, tf4, (18, 18))
>>> delayed_crop = chained.delayed_crop(region_slices)
>>> final_crop = delayed_crop.finalize()
>>> #
>>> tf4 = np.array([[.5, 0, 0], [0, .5, 0], [0, 0, 1]])
>>> chained = DelayedWarp(band2, tf4, (6, 6))
>>> delayed_crop = chained.delayed_crop(region_slices)
>>> final_crop = delayed_crop.finalize()
>>> #
>>> region_slices = (slice(1, 5), slice(2, 4))
>>> delayed_crop = chained.delayed_crop(region_slices)
>>> final_crop = delayed_crop.finalize()

Example

>>> dsize = (17, 12)
>>> tf = np.array([[5.2, 0, 1.1], [0, 3.1, 2.2], [0, 0, 1]])
>>> self = DelayedWarp(np.random.rand(3, 5, 13), tf, dsize=dsize)
>>> self.finalize().shape
property channels(self)
classmethod random(cls, nesting=(2, 5), rng=None)
CommandLine:

xdoctest -m ndsampler.delayed DelayedWarp.random

Example

>>> from ndsampler.delayed import *  # NOQA
>>> self = DelayedWarp.random(nesting=(4, 7))
>>> print('self = {!r}'.format(self))
>>> print(ub.repr2(self.nesting(), nl=-1, sort=0))
children(self)

Abstract method, which should generate all of the direct children of a node in the operation tree.

property shape(self)
_optimize_paths(self, **kwargs)

Example

>>> from ndsampler.delayed import *  # NOQA
>>> self = DelayedWarp.random()
>>> leafs = list(self._optimize_paths())
>>> print('leafs = {!r}'.format(leafs))
finalize(self, transform=None, dsize=None, interpolation='linear', **kwargs)

Execute the final transform

Can pass a parent transform to augment this underlying transform.

Parameters
  • transform (Transform) – an additional transform to perform

  • dsize (Tuple[int, int]) – overrides destination canvas size

Example

>>> from ndsampler.delayed import *  # NOQA
>>> tf = np.array([[0.9, 0, 3.9], [0, 1.1, -.5], [0, 0, 1]])
>>> raw = kwimage.grab_test_image(dsize=(54, 65))
>>> raw = kwimage.ensure_float01(raw)
>>> # Test nested finalize
>>> layer1 = raw
>>> num = 10
>>> for _ in range(num):
...     layer1  = DelayedWarp(layer1, tf, dsize='auto')
>>> final1 = layer1.finalize()
>>> # Test non-nested finalize
>>> layer2 = list(layer1._optimize_paths())[0]
>>> final2 = layer2.finalize()
>>> #
>>> print(ub.repr2(layer1.nesting(), nl=-1, sort=0))
>>> print(ub.repr2(layer2.nesting(), nl=-1, sort=0))
>>> print('final1 = {!r}'.format(final1))
>>> print('final2 = {!r}'.format(final2))
>>> print('final1.shape = {!r}'.format(final1.shape))
>>> print('final2.shape = {!r}'.format(final2.shape))
>>> assert np.allclose(final1, final2)
>>> #
>>> # xdoctest: +REQUIRES(--show)
>>> import kwplot
>>> kwplot.autompl()
>>> kwplot.imshow(raw, pnum=(1, 3, 1), fnum=1)
>>> kwplot.imshow(final1, pnum=(1, 3, 2), fnum=1)
>>> kwplot.imshow(final2, pnum=(1, 3, 3), fnum=1)
>>> kwplot.show_if_requested()

Example

>>> # Test aliasing
>>> from ndsampler.delayed import *  # NOQA
>>> s = DelayedIdentity.demo()
>>> s = DelayedIdentity.demo('checkerboard')
>>> a = s.delayed_warp(Affine.scale(0.05), dsize='auto')
>>> b = s.delayed_warp(Affine.scale(3), dsize='auto')
>>> # xdoctest: +REQUIRES(--show)
>>> import kwplot
>>> kwplot.autompl()
>>> # It looks like downsampling linear and area is the same
>>> # Does warpAffine have no alias handling?
>>> pnum_ = kwplot.PlotNums(nRows=2, nCols=4)
>>> kwplot.imshow(a.finalize(interpolation='area'), pnum=pnum_(), title='warpAffine area')
>>> kwplot.imshow(a.finalize(interpolation='linear'), pnum=pnum_(), title='warpAffine linear')
>>> kwplot.imshow(a.finalize(interpolation='nearest'), pnum=pnum_(), title='warpAffine nearest')
>>> kwplot.imshow(a.finalize(interpolation='nearest', antialias=False), pnum=pnum_(), title='warpAffine nearest AA=0')
>>> kwplot.imshow(kwimage.imresize(s.finalize(), dsize=a.dsize, interpolation='area'), pnum=pnum_(), title='resize area')
>>> kwplot.imshow(kwimage.imresize(s.finalize(), dsize=a.dsize, interpolation='linear'), pnum=pnum_(), title='resize linear')
>>> kwplot.imshow(kwimage.imresize(s.finalize(), dsize=a.dsize, interpolation='nearest'), pnum=pnum_(), title='resize nearest')
>>> kwplot.imshow(kwimage.imresize(s.finalize(), dsize=a.dsize, interpolation='cubic'), pnum=pnum_(), title='resize cubic')
class ndsampler.delayed.DelayedCrop(sub_data, sub_slices)

Bases: DelayedImageOperation

Represent a delayed crop operation

Example

>>> from ndsampler.delayed import *  # NOQA
>>> sub_data = DelayedLoad.demo()
>>> sub_slices = (slice(5, 10), slice(1, 12))
>>> self = DelayedCrop(sub_data, sub_slices)
>>> print(ub.repr2(self.nesting(), nl=-1, sort=0))
>>> final = self.finalize()
>>> print('final.shape = {!r}'.format(final.shape))

Example

>>> from ndsampler.delayed import *  # NOQA
>>> sub_data = DelayedLoad.demo()
>>> sub_slices = (slice(5, 10), slice(1, 12))
>>> crop1 = DelayedCrop(sub_data, sub_slices)
>>> import pytest
>>> # Should only error while huristics are in use.
>>> with pytest.raises(ValueError):
>>>     crop2 = DelayedCrop(crop1, sub_slices)
__hack_dont_optimize__ = True
property channels(self)
children(self)

Abstract method, which should generate all of the direct children of a node in the operation tree.

finalize(self, **kwargs)
abstract _optimize_paths(self, **kwargs)

Iterate through the leaf nodes, which are virtually transformed into the root space.

This returns some sort of hueristically optimized leaf repr wrt warps.

ndsampler.delayed._compute_leaf_subcrop(root_region_bounds, tf_leaf_to_root)

Given a region in a “root” image and a trasnform between that “root” and some “leaf” image, compute the appropriate quantized region in the “leaf” image and the adjusted transformation between that root and leaf.

Example

>>> region_slices = (slice(33, 100), slice(22, 62))
>>> region_shape = (100, 100, 1)
>>> root_region_box = kwimage.Boxes.from_slice(region_slices, shape=region_shape)
>>> root_region_bounds = root_region_box.to_polygons()[0]
>>> tf_leaf_to_root = Affine.affine(scale=7).matrix
>>> slices, tf_new = _compute_leaf_subcrop(root_region_bounds, tf_leaf_to_root)
>>> print('tf_new =\n{!r}'.format(tf_new))
>>> print('slices = {!r}'.format(slices))

Ignore:

root_region_bounds = kwimage.Coords.random(4) tf_leaf_to_root = np.eye(3) tf_leaf_to_root[0, 2] = -1e-11

ndsampler.delayed._largest_shape(shapes)

Finds maximum over all shapes

Example

>>> shapes = [
>>>     (10, 20), None, (None, 30), (40, 50, 60, None), (100,)
>>> ]
>>> largest = _largest_shape(shapes)
>>> print('largest = {!r}'.format(largest))
>>> assert largest == (100, 50, 60, None)
ndsampler.delayed._devcheck_corner()