Metadata-Version: 2.1
Name: delayed-image
Version: 0.2.5
Summary: The delayed_image module
Author: Jon Crall
Author-email: jon.crall@kitware.com
License: Apache 2
Classifier: Development Status :: 1 - Planning
Classifier: Intended Audience :: Developers
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Utilities
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Programming Language :: Python :: 3.6
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Requires-Python: >=3.6
Description-Content-Type: text/x-rst
Requires-Dist: kwarray
Requires-Dist: kwimage
Requires-Dist: ubelt
Requires-Dist: affine
Requires-Dist: numpy ; python_version < "3.10" and python_version >= "3.9"
Requires-Dist: numpy ; python_version < "3.11" and python_version >= "3.10"
Requires-Dist: numpy ; python_version < "3.7" and python_version >= "3.6"
Requires-Dist: networkx ; python_version < "3.7.0" and python_version >= "3.6.0"
Requires-Dist: networkx ; python_version < "3.8" and python_version >= "3.7"
Requires-Dist: numpy ; python_version < "3.8" and python_version >= "3.7"
Requires-Dist: numpy ; python_version < "3.9" and python_version >= "3.8"
Requires-Dist: numpy ; python_version >= "3.11"
Requires-Dist: networkx ; python_version >= "3.8"
Provides-Extra: all
Requires-Dist: kwarray ; extra == 'all'
Requires-Dist: kwimage ; extra == 'all'
Requires-Dist: ubelt ; extra == 'all'
Requires-Dist: affine ; extra == 'all'
Requires-Dist: xdoctest ; extra == 'all'
Requires-Dist: pytest-timeout ; extra == 'all'
Requires-Dist: codecov ; extra == 'all'
Requires-Dist: lark-cython ; extra == 'all'
Requires-Dist: lark ; extra == 'all'
Requires-Dist: xarray ; extra == 'all'
Provides-Extra: all-strict
Requires-Dist: kwarray (==0.6.0) ; extra == 'all-strict'
Requires-Dist: kwimage (==0.9.7) ; extra == 'all-strict'
Requires-Dist: ubelt (==1.2.1) ; extra == 'all-strict'
Requires-Dist: affine (==2.3.1) ; extra == 'all-strict'
Requires-Dist: xdoctest (==0.14.0) ; extra == 'all-strict'
Requires-Dist: pytest-timeout (==1.4.2) ; extra == 'all-strict'
Requires-Dist: codecov (==2.0.15) ; extra == 'all-strict'
Requires-Dist: lark-cython (==0.0.12) ; extra == 'all-strict'
Requires-Dist: lark (==1.1.2) ; extra == 'all-strict'
Requires-Dist: xarray (==0.16.0) ; extra == 'all-strict'
Requires-Dist: coverage (==4.5) ; (python_version < "2.7" and python_version >= "2.6") and extra == 'all-strict'
Requires-Dist: pytest (<=4.6.11,==4.6.0) ; (python_version < "2.8.0" and python_version >= "2.7.0") and extra == 'all-strict'
Requires-Dist: pytest-cov (==2.8.1) ; (python_version < "2.8.0" and python_version >= "2.7.0") and extra == 'all-strict'
Requires-Dist: numpy (==1.19.3) ; (python_version < "3.10" and python_version >= "3.9") and extra == 'all-strict'
Requires-Dist: coverage (==5.3.1) ; (python_version < "3.10" and python_version >= "3.9") and extra == 'all-strict'
Requires-Dist: pytest (==4.6.0) ; (python_version < "3.10.0" and python_version >= "3.7.0") and extra == 'all-strict'
Requires-Dist: numpy (==1.21.6) ; (python_version < "3.11" and python_version >= "3.10") and extra == 'all-strict'
Requires-Dist: coverage (==5.3.1) ; (python_version < "3.4" and python_version >= "2.7") and extra == 'all-strict'
Requires-Dist: coverage (==4.3.4) ; (python_version < "3.5" and python_version >= "3.4") and extra == 'all-strict'
Requires-Dist: pytest (<=4.6.11,==4.6.0) ; (python_version < "3.5.0" and python_version >= "3.4.0") and extra == 'all-strict'
Requires-Dist: pytest-cov (==2.8.1) ; (python_version < "3.5.0" and python_version >= "3.4.0") and extra == 'all-strict'
Requires-Dist: coverage (==5.3.1) ; (python_version < "3.6" and python_version >= "3.5") and extra == 'all-strict'
Requires-Dist: pytest (<=6.1.2,==4.6.0) ; (python_version < "3.6.0" and python_version >= "3.5.0") and extra == 'all-strict'
Requires-Dist: pytest-cov (==2.9.0) ; (python_version < "3.6.0" and python_version >= "3.5.0") and extra == 'all-strict'
Requires-Dist: numpy (==1.19.2) ; (python_version < "3.7" and python_version >= "3.6") and extra == 'all-strict'
Requires-Dist: coverage (==6.1.1) ; (python_version < "3.7" and python_version >= "3.6") and extra == 'all-strict'
Requires-Dist: networkx (<=2.5.1,==2.2.0) ; (python_version < "3.7.0" and python_version >= "3.6.0") and extra == 'all-strict'
Requires-Dist: pytest (==4.6.0) ; (python_version < "3.7.0" and python_version >= "3.6.0") and extra == 'all-strict'
Requires-Dist: networkx (==2.6.2) ; (python_version < "3.8" and python_version >= "3.7") and extra == 'all-strict'
Requires-Dist: numpy (==1.19.2) ; (python_version < "3.8" and python_version >= "3.7") and extra == 'all-strict'
Requires-Dist: coverage (==6.1.1) ; (python_version < "3.8" and python_version >= "3.7") and extra == 'all-strict'
Requires-Dist: numpy (==1.19.2) ; (python_version < "3.9" and python_version >= "3.8") and extra == 'all-strict'
Requires-Dist: coverage (==6.1.1) ; (python_version < "3.9" and python_version >= "3.8") and extra == 'all-strict'
Requires-Dist: coverage (==6.1.1) ; (python_version >= "3.10") and extra == 'all-strict'
Requires-Dist: pytest (==6.2.5) ; (python_version >= "3.10.0") and extra == 'all-strict'
Requires-Dist: numpy (==1.23.2) ; (python_version >= "3.11") and extra == 'all-strict'
Requires-Dist: pytest-cov (==3.0.0) ; (python_version >= "3.6.0") and extra == 'all-strict'
Requires-Dist: networkx (==2.7) ; (python_version >= "3.8") and extra == 'all-strict'
Requires-Dist: coverage ; (python_version < "2.7" and python_version >= "2.6") and extra == 'all'
Requires-Dist: pytest ; (python_version < "2.8.0" and python_version >= "2.7.0") and extra == 'all'
Requires-Dist: pytest-cov ; (python_version < "2.8.0" and python_version >= "2.7.0") and extra == 'all'
Requires-Dist: numpy ; (python_version < "3.10" and python_version >= "3.9") and extra == 'all'
Requires-Dist: coverage ; (python_version < "3.10" and python_version >= "3.9") and extra == 'all'
Requires-Dist: pytest ; (python_version < "3.10.0" and python_version >= "3.7.0") and extra == 'all'
Requires-Dist: numpy ; (python_version < "3.11" and python_version >= "3.10") and extra == 'all'
Requires-Dist: coverage ; (python_version < "3.4" and python_version >= "2.7") and extra == 'all'
Requires-Dist: coverage ; (python_version < "3.5" and python_version >= "3.4") and extra == 'all'
Requires-Dist: pytest ; (python_version < "3.5.0" and python_version >= "3.4.0") and extra == 'all'
Requires-Dist: pytest-cov ; (python_version < "3.5.0" and python_version >= "3.4.0") and extra == 'all'
Requires-Dist: coverage ; (python_version < "3.6" and python_version >= "3.5") and extra == 'all'
Requires-Dist: pytest ; (python_version < "3.6.0" and python_version >= "3.5.0") and extra == 'all'
Requires-Dist: pytest-cov ; (python_version < "3.6.0" and python_version >= "3.5.0") and extra == 'all'
Requires-Dist: numpy ; (python_version < "3.7" and python_version >= "3.6") and extra == 'all'
Requires-Dist: coverage ; (python_version < "3.7" and python_version >= "3.6") and extra == 'all'
Requires-Dist: networkx ; (python_version < "3.7.0" and python_version >= "3.6.0") and extra == 'all'
Requires-Dist: pytest ; (python_version < "3.7.0" and python_version >= "3.6.0") and extra == 'all'
Requires-Dist: networkx ; (python_version < "3.8" and python_version >= "3.7") and extra == 'all'
Requires-Dist: numpy ; (python_version < "3.8" and python_version >= "3.7") and extra == 'all'
Requires-Dist: coverage ; (python_version < "3.8" and python_version >= "3.7") and extra == 'all'
Requires-Dist: numpy ; (python_version < "3.9" and python_version >= "3.8") and extra == 'all'
Requires-Dist: coverage ; (python_version < "3.9" and python_version >= "3.8") and extra == 'all'
Requires-Dist: coverage ; (python_version >= "3.10") and extra == 'all'
Requires-Dist: pytest ; (python_version >= "3.10.0") and extra == 'all'
Requires-Dist: numpy ; (python_version >= "3.11") and extra == 'all'
Requires-Dist: pytest-cov ; (python_version >= "3.6.0") and extra == 'all'
Requires-Dist: networkx ; (python_version >= "3.8") and extra == 'all'
Provides-Extra: graphics
Provides-Extra: graphics-strict
Requires-Dist: opencv-python (==3.4.15.55) ; (python_version < "3.10" and python_version >= "3.9") and extra == 'graphics-strict'
Requires-Dist: opencv-python (==3.1.0.0) ; (python_version < "3.4" and python_version >= "2.7") and extra == 'graphics-strict'
Requires-Dist: opencv-python (==3.1.0.5) ; (python_version < "3.5" and python_version >= "3.4") and extra == 'graphics-strict'
Requires-Dist: opencv-python (==3.1.0.2) ; (python_version < "3.6" and python_version >= "3.5") and extra == 'graphics-strict'
Requires-Dist: opencv-python (==3.4.13.47) ; (python_version < "3.7" and python_version >= "3.6") and extra == 'graphics-strict'
Requires-Dist: opencv-python (==3.4.15.55) ; (python_version < "3.8" and python_version >= "3.7") and extra == 'graphics-strict'
Requires-Dist: opencv-python (==3.4.15.55) ; (python_version < "3.9" and python_version >= "3.8") and extra == 'graphics-strict'
Requires-Dist: opencv-python (==4.5.4.58) ; (python_version >= "3.10") and extra == 'graphics-strict'
Requires-Dist: opencv-python ; (python_version < "3.10" and python_version >= "3.9") and extra == 'graphics'
Requires-Dist: opencv-python ; (python_version < "3.4" and python_version >= "2.7") and extra == 'graphics'
Requires-Dist: opencv-python ; (python_version < "3.5" and python_version >= "3.4") and extra == 'graphics'
Requires-Dist: opencv-python ; (python_version < "3.6" and python_version >= "3.5") and extra == 'graphics'
Requires-Dist: opencv-python ; (python_version < "3.7" and python_version >= "3.6") and extra == 'graphics'
Requires-Dist: opencv-python ; (python_version < "3.8" and python_version >= "3.7") and extra == 'graphics'
Requires-Dist: opencv-python ; (python_version < "3.9" and python_version >= "3.8") and extra == 'graphics'
Requires-Dist: opencv-python ; (python_version >= "3.10") and extra == 'graphics'
Provides-Extra: headless
Provides-Extra: headless-strict
Requires-Dist: opencv-python-headless (==3.4.13.47) ; (python_version < "3.10" and python_version >= "3.9") and extra == 'headless-strict'
Requires-Dist: opencv-python-headless (==3.4.2.16) ; (python_version < "3.4" and python_version >= "2.7") and extra == 'headless-strict'
Requires-Dist: opencv-python-headless (==3.4.2.16) ; (python_version < "3.5" and python_version >= "3.4") and extra == 'headless-strict'
Requires-Dist: opencv-python-headless (==3.4.11.39) ; (python_version < "3.6" and python_version >= "3.5") and extra == 'headless-strict'
Requires-Dist: opencv-python-headless (==3.4.13.47) ; (python_version < "3.7" and python_version >= "3.6") and extra == 'headless-strict'
Requires-Dist: opencv-python-headless (==3.4.13.47) ; (python_version < "3.8" and python_version >= "3.7") and extra == 'headless-strict'
Requires-Dist: opencv-python-headless (==3.4.13.47) ; (python_version < "3.9" and python_version >= "3.8") and extra == 'headless-strict'
Requires-Dist: opencv-python-headless (==4.5.4.58) ; (python_version >= "3.10") and extra == 'headless-strict'
Requires-Dist: opencv-python-headless ; (python_version < "3.10" and python_version >= "3.9") and extra == 'headless'
Requires-Dist: opencv-python-headless ; (python_version < "3.4" and python_version >= "2.7") and extra == 'headless'
Requires-Dist: opencv-python-headless ; (python_version < "3.5" and python_version >= "3.4") and extra == 'headless'
Requires-Dist: opencv-python-headless ; (python_version < "3.6" and python_version >= "3.5") and extra == 'headless'
Requires-Dist: opencv-python-headless ; (python_version < "3.7" and python_version >= "3.6") and extra == 'headless'
Requires-Dist: opencv-python-headless ; (python_version < "3.8" and python_version >= "3.7") and extra == 'headless'
Requires-Dist: opencv-python-headless ; (python_version < "3.9" and python_version >= "3.8") and extra == 'headless'
Requires-Dist: opencv-python-headless ; (python_version >= "3.10") and extra == 'headless'
Provides-Extra: optional
Requires-Dist: lark-cython ; extra == 'optional'
Requires-Dist: lark ; extra == 'optional'
Requires-Dist: xarray ; extra == 'optional'
Provides-Extra: optional-strict
Requires-Dist: lark-cython (==0.0.12) ; extra == 'optional-strict'
Requires-Dist: lark (==1.1.2) ; extra == 'optional-strict'
Requires-Dist: xarray (==0.16.0) ; extra == 'optional-strict'
Provides-Extra: runtime-strict
Requires-Dist: kwarray (==0.6.0) ; extra == 'runtime-strict'
Requires-Dist: kwimage (==0.9.7) ; extra == 'runtime-strict'
Requires-Dist: ubelt (==1.2.1) ; extra == 'runtime-strict'
Requires-Dist: affine (==2.3.1) ; extra == 'runtime-strict'
Requires-Dist: numpy (==1.19.3) ; (python_version < "3.10" and python_version >= "3.9") and extra == 'runtime-strict'
Requires-Dist: numpy (==1.21.6) ; (python_version < "3.11" and python_version >= "3.10") and extra == 'runtime-strict'
Requires-Dist: numpy (==1.19.2) ; (python_version < "3.7" and python_version >= "3.6") and extra == 'runtime-strict'
Requires-Dist: networkx (<=2.5.1,==2.2.0) ; (python_version < "3.7.0" and python_version >= "3.6.0") and extra == 'runtime-strict'
Requires-Dist: networkx (==2.6.2) ; (python_version < "3.8" and python_version >= "3.7") and extra == 'runtime-strict'
Requires-Dist: numpy (==1.19.2) ; (python_version < "3.8" and python_version >= "3.7") and extra == 'runtime-strict'
Requires-Dist: numpy (==1.19.2) ; (python_version < "3.9" and python_version >= "3.8") and extra == 'runtime-strict'
Requires-Dist: numpy (==1.23.2) ; (python_version >= "3.11") and extra == 'runtime-strict'
Requires-Dist: networkx (==2.7) ; (python_version >= "3.8") and extra == 'runtime-strict'
Provides-Extra: tests
Requires-Dist: xdoctest ; extra == 'tests'
Requires-Dist: pytest-timeout ; extra == 'tests'
Requires-Dist: codecov ; extra == 'tests'
Provides-Extra: tests-strict
Requires-Dist: xdoctest (==0.14.0) ; extra == 'tests-strict'
Requires-Dist: pytest-timeout (==1.4.2) ; extra == 'tests-strict'
Requires-Dist: codecov (==2.0.15) ; extra == 'tests-strict'
Requires-Dist: coverage (==4.5) ; (python_version < "2.7" and python_version >= "2.6") and extra == 'tests-strict'
Requires-Dist: pytest (<=4.6.11,==4.6.0) ; (python_version < "2.8.0" and python_version >= "2.7.0") and extra == 'tests-strict'
Requires-Dist: pytest-cov (==2.8.1) ; (python_version < "2.8.0" and python_version >= "2.7.0") and extra == 'tests-strict'
Requires-Dist: coverage (==5.3.1) ; (python_version < "3.10" and python_version >= "3.9") and extra == 'tests-strict'
Requires-Dist: pytest (==4.6.0) ; (python_version < "3.10.0" and python_version >= "3.7.0") and extra == 'tests-strict'
Requires-Dist: coverage (==5.3.1) ; (python_version < "3.4" and python_version >= "2.7") and extra == 'tests-strict'
Requires-Dist: coverage (==4.3.4) ; (python_version < "3.5" and python_version >= "3.4") and extra == 'tests-strict'
Requires-Dist: pytest (<=4.6.11,==4.6.0) ; (python_version < "3.5.0" and python_version >= "3.4.0") and extra == 'tests-strict'
Requires-Dist: pytest-cov (==2.8.1) ; (python_version < "3.5.0" and python_version >= "3.4.0") and extra == 'tests-strict'
Requires-Dist: coverage (==5.3.1) ; (python_version < "3.6" and python_version >= "3.5") and extra == 'tests-strict'
Requires-Dist: pytest (<=6.1.2,==4.6.0) ; (python_version < "3.6.0" and python_version >= "3.5.0") and extra == 'tests-strict'
Requires-Dist: pytest-cov (==2.9.0) ; (python_version < "3.6.0" and python_version >= "3.5.0") and extra == 'tests-strict'
Requires-Dist: coverage (==6.1.1) ; (python_version < "3.7" and python_version >= "3.6") and extra == 'tests-strict'
Requires-Dist: pytest (==4.6.0) ; (python_version < "3.7.0" and python_version >= "3.6.0") and extra == 'tests-strict'
Requires-Dist: coverage (==6.1.1) ; (python_version < "3.8" and python_version >= "3.7") and extra == 'tests-strict'
Requires-Dist: coverage (==6.1.1) ; (python_version < "3.9" and python_version >= "3.8") and extra == 'tests-strict'
Requires-Dist: coverage (==6.1.1) ; (python_version >= "3.10") and extra == 'tests-strict'
Requires-Dist: pytest (==6.2.5) ; (python_version >= "3.10.0") and extra == 'tests-strict'
Requires-Dist: pytest-cov (==3.0.0) ; (python_version >= "3.6.0") and extra == 'tests-strict'
Requires-Dist: coverage ; (python_version < "2.7" and python_version >= "2.6") and extra == 'tests'
Requires-Dist: pytest ; (python_version < "2.8.0" and python_version >= "2.7.0") and extra == 'tests'
Requires-Dist: pytest-cov ; (python_version < "2.8.0" and python_version >= "2.7.0") and extra == 'tests'
Requires-Dist: coverage ; (python_version < "3.10" and python_version >= "3.9") and extra == 'tests'
Requires-Dist: pytest ; (python_version < "3.10.0" and python_version >= "3.7.0") and extra == 'tests'
Requires-Dist: coverage ; (python_version < "3.4" and python_version >= "2.7") and extra == 'tests'
Requires-Dist: coverage ; (python_version < "3.5" and python_version >= "3.4") and extra == 'tests'
Requires-Dist: pytest ; (python_version < "3.5.0" and python_version >= "3.4.0") and extra == 'tests'
Requires-Dist: pytest-cov ; (python_version < "3.5.0" and python_version >= "3.4.0") and extra == 'tests'
Requires-Dist: coverage ; (python_version < "3.6" and python_version >= "3.5") and extra == 'tests'
Requires-Dist: pytest ; (python_version < "3.6.0" and python_version >= "3.5.0") and extra == 'tests'
Requires-Dist: pytest-cov ; (python_version < "3.6.0" and python_version >= "3.5.0") and extra == 'tests'
Requires-Dist: coverage ; (python_version < "3.7" and python_version >= "3.6") and extra == 'tests'
Requires-Dist: pytest ; (python_version < "3.7.0" and python_version >= "3.6.0") and extra == 'tests'
Requires-Dist: coverage ; (python_version < "3.8" and python_version >= "3.7") and extra == 'tests'
Requires-Dist: coverage ; (python_version < "3.9" and python_version >= "3.8") and extra == 'tests'
Requires-Dist: coverage ; (python_version >= "3.10") and extra == 'tests'
Requires-Dist: pytest ; (python_version >= "3.10.0") and extra == 'tests'
Requires-Dist: pytest-cov ; (python_version >= "3.6.0") and extra == 'tests'

The delayed_image Module
========================

|Pypi| |PypiDownloads| |ReadTheDocs|

The delayed image module lets you describe (a tree of) image operations, but
waits to execute them until the user calls ``finalize``. This allows for a
sequence of operations to be optimized before it is executed, which means
faster execution and fewer quantization artifacts.

The optimization logic can also leverage image formats that contain builtin
tiling or overviews using the GDAL library. Formats that contain tiles allow
delayed image to read only a subset of the image, if only a small part is
cropped to it.  Overviews allow delayed image to load pre-downscaled images if
it detects a scaling operation. This is **much** faster than the naive way of
accomplishing these operations, and **much** easier than having to remember to
do everything in the right order yourself.

Note: GDAL is optional, but recommended. Precompiled GDAL wheels are available
on Kitware's `large image wheel repository <https://girder.github.io/large_image_wheels/>`__.
Use ``pip install gdal -f https://girder.github.io/large_image_wheels/`` 
to install gdal from this server. Track status of official gdal wheels `here
<https://github.com/OSGeo/gdal/issues/3060>`__.


History
-------

This module is still in its early days of development and is a port the
following code from kwcoco:

* ChannelSpec
* SensorChannelSpec
* DelayedLoad & other delayed operations


Quick Start
-----------

.. code:: python

    # Given a path to some image
    import kwimage
    fpath = kwimage.grab_test_image_fpath('amazon')

    # Demo how to load, scale, and crop a part of an image.
    import delayed_image
    delayed = delayed_image.DelayedLoad(fpath)
    delayed = delayed.prepare()
    delayed = delayed.scale(0.1)
    delayed = delayed[128:256, 128:256]

    import kwplot
    kwplot.autompl()
    kwplot.imshow(delayed.finalize())
    kwimage.imwrite('foo.png', delayed.finalize())
    
.. image:: https://i.imgur.com/lsWLkPx.png

See `the quickstart jupyter notebook <examples/quickstart.ipynb/>`__ for more details.

Delayed Loading
---------------

Example of delayed loading:

.. code:: python

    >>> from delayed_image import DelayedLoad
    >>> import kwimage
    >>> fpath = kwimage.grab_test_image_fpath(overviews=3)
    >>> dimg = DelayedLoad(fpath, channels='r|g|b').prepare()
    >>> quantization = {'quant_max': 255, 'nodata': 0}
    >>> #
    >>> # Make a complex chain of operations
    >>> dimg = dimg.dequantize(quantization)
    >>> dimg = dimg.warp({'scale': 1.1})
    >>> dimg = dimg.warp({'scale': 1.1})
    >>> dimg = dimg[0:400, 1:400]
    >>> dimg = dimg.warp({'scale': 0.5})
    >>> dimg = dimg[0:800, 1:800]
    >>> dimg = dimg.warp({'scale': 0.5})
    >>> dimg = dimg[0:800, 1:800]
    >>> dimg = dimg.warp({'scale': 0.5})
    >>> dimg = dimg.warp({'scale': 1.1})
    >>> dimg = dimg.warp({'scale': 1.1})
    >>> dimg = dimg.warp({'scale': 2.1})
    >>> dimg = dimg[0:200, 1:200]
    >>> dimg = dimg[1:200, 2:200]
    >>> dimg.write_network_text()
    ╙── Crop dsize=(128,130),space_slice=(slice(1,131,None),slice(2,130,None))
        └─╼ Crop dsize=(130,131),space_slice=(slice(0,131,None),slice(1,131,None))
            └─╼ Warp dsize=(131,131),transform={scale=2.1000}
                └─╼ Warp dsize=(62,62),transform={scale=1.1000}
                    └─╼ Warp dsize=(56,56),transform={scale=1.1000}
                        └─╼ Warp dsize=(50,50),transform={scale=0.5000}
                            └─╼ Crop dsize=(99,100),space_slice=(slice(0,100,None),slice(1,100,None))
                                └─╼ Warp dsize=(100,100),transform={scale=0.5000}
                                    └─╼ Crop dsize=(199,200),space_slice=(slice(0,200,None),slice(1,200,None))
                                        └─╼ Warp dsize=(200,200),transform={scale=0.5000}
                                            └─╼ Crop dsize=(399,400),space_slice=(slice(0,400,None),slice(1,400,None))
                                                └─╼ Warp dsize=(621,621),transform={scale=1.1000}
                                                    └─╼ Warp dsize=(564,564),transform={scale=1.1000}
                                                        └─╼ Dequantize dsize=(512,512),quantization={quant_max=255,nodata=0}
                                                            └─╼ Load channels=r|g|b,dsize=(512,512),num_overviews=3,fname=astro_overviews=3.tif

    >>> # Optimize the chain
    >>> dopt = dimg.optimize()
    >>> dopt.write_network_text()
    ╙── Warp dsize=(128,130),transform={offset=(-0.6...,-1.0...),scale=1.5373}
        └─╼ Dequantize dsize=(80,83),quantization={quant_max=255,nodata=0}
            └─╼ Crop dsize=(80,83),space_slice=(slice(0,83,None),slice(3,83,None))
                └─╼ Overview dsize=(128,128),overview=2
                    └─╼ Load channels=r|g|b,dsize=(512,512),num_overviews=3,fname=astro_overviews=3.tif

    #
    >>> final0 = dimg.finalize(optimize=False)
    >>> final1 = dopt.finalize()
    >>> assert final0.shape == final1.shape
    >>> # xdoctest: +REQUIRES(--show)
    >>> import kwplot
    >>> kwplot.autompl()
    >>> kwplot.imshow(final0, pnum=(1, 2, 1), fnum=1, title='raw')
    >>> kwplot.imshow(final1, pnum=(1, 2, 2), fnum=1, title='optimized')

 
.. image:: https://i.imgur.com/3SGvxtC.png


SensorChanSpec
--------------

Includes the SensorChan spec, which makes handling channels from different
sensing sources easier.

It has a simple grammar:

 .. code:: 

    // SENSOR_CHAN_GRAMMAR
    ?start: stream

    // An identifier can contain spaces
    IDEN: ("_"|"*"|LETTER) ("_"|" "|"-"|"*"|LETTER|DIGIT)*

    chan_single : IDEN
    chan_getitem : IDEN "." INT
    chan_getslice_0b : IDEN ":" INT
    chan_getslice_ab : (IDEN "." INT ":" INT) | (IDEN ":" INT ":" INT)

    // A channel code can just be an ID, or it can have a getitem
    // style syntax with a scalar or slice as an argument
    chan_code : chan_single | chan_getslice_0b | chan_getslice_ab | chan_getitem

    // Fused channels are an ordered sequence of channel codes (without sensors)
    fused : chan_code ("|" chan_code)*

    // A channel only part can be a fused channel or a sequence
    channel_rhs : fused | fused_seq

    // Channels can be specified in a sequence but must contain parens
    fused_seq : "(" fused ("," fused)* ")"

    // Sensors can be specified in a sequence but must contain parens
    sensor_seq : "(" IDEN ("," IDEN)* "):"

    sensor_lhs : (IDEN ":") | (sensor_seq)

    sensor_chan : sensor_lhs channel_rhs?

    nosensor_chan : channel_rhs

    stream_item : sensor_chan | nosensor_chan

    // A stream is an unordered sequence of fused channels, that can
    // optionally contain sensor specifications.

    stream : stream_item ("," stream_item)*

    %import common.DIGIT
    %import common.LETTER
    %import common.INT


You can think of a channel spec is that splitting the spec by "," gives groups
of channels that should be processed together and "late-fused".  Within each
group the "|" operator "early-fuses" the channels.

For instance, say we had a network and we wanted to process 3-channel rgb
images in one stream and 1-channel infrared images in a second stream and then
fuse them together. The channel specification for channels labled as 'red',
'green', 'blue', and 'infrared' would be:

.. code::

    infrared,red|green|blue


Sensors can be included with a colon prefix. Parenthesis can be used for
grouping.

.. code::


    S2:(infrared,red|green|blue)


.. |Pypi| image:: https://img.shields.io/pypi/v/delayed_image.svg
    :target: https://pypi.python.org/pypi/delayed_image

.. |PypiDownloads| image:: https://img.shields.io/pypi/dm/delayed_image.svg
    :target: https://pypistats.org/packages/delayed_image

.. |ReadTheDocs| image:: https://readthedocs.org/projects/delayed_image/badge/?version=latest
    :target: http://delayed_image.readthedocs.io/en/latest/
