large-image 1.27.1.dev44__tar.gz → 1.27.1.dev59__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {large-image-1.27.1.dev44/large_image.egg-info → large-image-1.27.1.dev59}/PKG-INFO +1 -1
- {large-image-1.27.1.dev44 → large-image-1.27.1.dev59}/large_image/tilesource/base.py +22 -514
- large-image-1.27.1.dev59/large_image/tilesource/tileiterator.py +544 -0
- {large-image-1.27.1.dev44 → large-image-1.27.1.dev59/large_image.egg-info}/PKG-INFO +1 -1
- {large-image-1.27.1.dev44 → large-image-1.27.1.dev59}/large_image.egg-info/SOURCES.txt +1 -0
- large-image-1.27.1.dev59/large_image.egg-info/requires.txt +145 -0
- large-image-1.27.1.dev44/large_image.egg-info/requires.txt +0 -145
- {large-image-1.27.1.dev44 → large-image-1.27.1.dev59}/LICENSE +0 -0
- {large-image-1.27.1.dev44 → large-image-1.27.1.dev59}/NOTICE +0 -0
- {large-image-1.27.1.dev44 → large-image-1.27.1.dev59}/README.rst +0 -0
- {large-image-1.27.1.dev44 → large-image-1.27.1.dev59}/docs/annotations.rst +0 -0
- {large-image-1.27.1.dev44 → large-image-1.27.1.dev59}/docs/caching.rst +0 -0
- {large-image-1.27.1.dev44 → large-image-1.27.1.dev59}/docs/conf.py +0 -0
- {large-image-1.27.1.dev44 → large-image-1.27.1.dev59}/docs/config_options.rst +0 -0
- {large-image-1.27.1.dev44 → large-image-1.27.1.dev59}/docs/development.rst +0 -0
- {large-image-1.27.1.dev44 → large-image-1.27.1.dev59}/docs/example_usage.rst +0 -0
- {large-image-1.27.1.dev44 → large-image-1.27.1.dev59}/docs/girder_annotation_config_options.rst +0 -0
- {large-image-1.27.1.dev44 → large-image-1.27.1.dev59}/docs/girder_config_options.rst +0 -0
- {large-image-1.27.1.dev44 → large-image-1.27.1.dev59}/docs/image_conversion.rst +0 -0
- {large-image-1.27.1.dev44 → large-image-1.27.1.dev59}/docs/index.rst +0 -0
- {large-image-1.27.1.dev44 → large-image-1.27.1.dev59}/docs/large_image_examples.ipynb +0 -0
- {large-image-1.27.1.dev44 → large-image-1.27.1.dev59}/docs/make_docs.sh +0 -0
- {large-image-1.27.1.dev44 → large-image-1.27.1.dev59}/docs/multi_source_specification.rst +0 -0
- {large-image-1.27.1.dev44 → large-image-1.27.1.dev59}/docs/notebooks.rst +0 -0
- {large-image-1.27.1.dev44 → large-image-1.27.1.dev59}/docs/static/custom.css +0 -0
- {large-image-1.27.1.dev44 → large-image-1.27.1.dev59}/docs/tilesource_options.rst +0 -0
- {large-image-1.27.1.dev44 → large-image-1.27.1.dev59}/docs/upgrade.rst +0 -0
- {large-image-1.27.1.dev44 → large-image-1.27.1.dev59}/large_image/__init__.py +0 -0
- {large-image-1.27.1.dev44 → large-image-1.27.1.dev59}/large_image/cache_util/__init__.py +0 -0
- {large-image-1.27.1.dev44 → large-image-1.27.1.dev59}/large_image/cache_util/base.py +0 -0
- {large-image-1.27.1.dev44 → large-image-1.27.1.dev59}/large_image/cache_util/cache.py +0 -0
- {large-image-1.27.1.dev44 → large-image-1.27.1.dev59}/large_image/cache_util/cachefactory.py +0 -0
- {large-image-1.27.1.dev44 → large-image-1.27.1.dev59}/large_image/cache_util/memcache.py +0 -0
- {large-image-1.27.1.dev44 → large-image-1.27.1.dev59}/large_image/config.py +0 -0
- {large-image-1.27.1.dev44 → large-image-1.27.1.dev59}/large_image/constants.py +0 -0
- {large-image-1.27.1.dev44 → large-image-1.27.1.dev59}/large_image/exceptions.py +0 -0
- {large-image-1.27.1.dev44 → large-image-1.27.1.dev59}/large_image/tilesource/__init__.py +0 -0
- {large-image-1.27.1.dev44 → large-image-1.27.1.dev59}/large_image/tilesource/geo.py +0 -0
- {large-image-1.27.1.dev44 → large-image-1.27.1.dev59}/large_image/tilesource/jupyter.py +0 -0
- {large-image-1.27.1.dev44 → large-image-1.27.1.dev59}/large_image/tilesource/stylefuncs.py +0 -0
- {large-image-1.27.1.dev44 → large-image-1.27.1.dev59}/large_image/tilesource/tiledict.py +0 -0
- {large-image-1.27.1.dev44 → large-image-1.27.1.dev59}/large_image/tilesource/utilities.py +0 -0
- {large-image-1.27.1.dev44 → large-image-1.27.1.dev59}/large_image.egg-info/dependency_links.txt +0 -0
- {large-image-1.27.1.dev44 → large-image-1.27.1.dev59}/large_image.egg-info/not-zip-safe +0 -0
- {large-image-1.27.1.dev44 → large-image-1.27.1.dev59}/large_image.egg-info/top_level.txt +0 -0
- {large-image-1.27.1.dev44 → large-image-1.27.1.dev59}/setup.cfg +0 -0
- {large-image-1.27.1.dev44 → large-image-1.27.1.dev59}/setup.py +0 -0
|
@@ -27,6 +27,7 @@ from ..constants import (TILE_FORMAT_IMAGE, TILE_FORMAT_NUMPY, TILE_FORMAT_PIL,
|
|
|
27
27
|
from . import utilities
|
|
28
28
|
from .jupyter import IPyLeafletMixin
|
|
29
29
|
from .tiledict import LazyTileDict
|
|
30
|
+
from .tileiterator import TileIterator
|
|
30
31
|
from .utilities import (ImageBytes, JSONDict, _imageToNumpy, # noqa: F401
|
|
31
32
|
_imageToPIL, dictToEtree, etreeToDict,
|
|
32
33
|
getPaletteColors, histogramThreshold, nearPowerOfTwo)
|
|
@@ -488,478 +489,6 @@ class TileSource(IPyLeafletMixin):
|
|
|
488
489
|
|
|
489
490
|
return cast(int, left), cast(int, top), cast(int, right), cast(int, bottom)
|
|
490
491
|
|
|
491
|
-
def _tileIteratorInfo(self, **kwargs) -> Optional[Dict[str, Any]]: # noqa
|
|
492
|
-
"""
|
|
493
|
-
Get information necessary to construct a tile iterator.
|
|
494
|
-
If one of width or height is specified, the other is determined by
|
|
495
|
-
preserving aspect ratio. If both are specified, the result may not be
|
|
496
|
-
that size, as aspect ratio is always preserved. If neither are
|
|
497
|
-
specified, magnification, mm_x, and/or mm_y are used to determine the
|
|
498
|
-
size. If none of those are specified, the original maximum resolution
|
|
499
|
-
is returned.
|
|
500
|
-
|
|
501
|
-
:param format: a tuple of allowed formats. Formats are members of
|
|
502
|
-
TILE_FORMAT_*. This will avoid converting images if they are
|
|
503
|
-
in the desired output encoding (regardless of subparameters).
|
|
504
|
-
Otherwise, TILE_FORMAT_NUMPY is returned.
|
|
505
|
-
:param region: a dictionary of optional values which specify the part
|
|
506
|
-
of the image to process.
|
|
507
|
-
|
|
508
|
-
:left: the left edge (inclusive) of the region to process.
|
|
509
|
-
:top: the top edge (inclusive) of the region to process.
|
|
510
|
-
:right: the right edge (exclusive) of the region to process.
|
|
511
|
-
:bottom: the bottom edge (exclusive) of the region to process.
|
|
512
|
-
:width: the width of the region to process.
|
|
513
|
-
:height: the height of the region to process.
|
|
514
|
-
:units: either 'base_pixels' (default), 'pixels', 'mm', or
|
|
515
|
-
'fraction'. base_pixels are in maximum resolution pixels.
|
|
516
|
-
pixels is in the specified magnification pixels. mm is in the
|
|
517
|
-
specified magnification scale. fraction is a scale of 0 to 1.
|
|
518
|
-
pixels and mm are only available if the magnification and mm
|
|
519
|
-
per pixel are defined for the image.
|
|
520
|
-
:unitsWH: if not specified, this is the same as `units`.
|
|
521
|
-
Otherwise, these units will be used for the width and height if
|
|
522
|
-
specified.
|
|
523
|
-
|
|
524
|
-
:param output: a dictionary of optional values which specify the size
|
|
525
|
-
of the output.
|
|
526
|
-
|
|
527
|
-
:maxWidth: maximum width in pixels.
|
|
528
|
-
:maxHeight: maximum height in pixels.
|
|
529
|
-
|
|
530
|
-
:param scale: a dictionary of optional values which specify the scale
|
|
531
|
-
of the region and / or output. This applies to region if
|
|
532
|
-
pixels or mm are used for units. It applies to output if
|
|
533
|
-
neither output maxWidth nor maxHeight is specified.
|
|
534
|
-
|
|
535
|
-
:magnification: the magnification ratio.
|
|
536
|
-
:mm_x: the horizontal size of a pixel in millimeters.
|
|
537
|
-
:mm_y: the vertical size of a pixel in millimeters.
|
|
538
|
-
:exact: if True, only a level that matches exactly will be
|
|
539
|
-
returned. This is only applied if magnification, mm_x, or mm_y
|
|
540
|
-
is used.
|
|
541
|
-
|
|
542
|
-
:param tile_position: if present, either a number to only yield the
|
|
543
|
-
(tile_position)th tile [0 to (xmax - min) * (ymax - ymin)) that the
|
|
544
|
-
iterator would yield, or a dictionary of {region_x, region_y} to
|
|
545
|
-
yield that tile, where 0, 0 is the first tile yielded, and
|
|
546
|
-
xmax - xmin - 1, ymax - ymin - 1 is the last tile yielded, or a
|
|
547
|
-
dictionary of {level_x, level_y} to yield that specific tile if it
|
|
548
|
-
is in the region.
|
|
549
|
-
:param tile_size: if present, retile the output to the specified tile
|
|
550
|
-
size. If only width or only height is specified, the resultant
|
|
551
|
-
tiles will be square. This is a dictionary containing at least
|
|
552
|
-
one of:
|
|
553
|
-
|
|
554
|
-
:width: the desired tile width.
|
|
555
|
-
:height: the desired tile height.
|
|
556
|
-
|
|
557
|
-
:param tile_overlap: if present, retile the output adding a symmetric
|
|
558
|
-
overlap to the tiles. If either x or y is not specified, it
|
|
559
|
-
defaults to zero. The overlap does not change the tile size,
|
|
560
|
-
only the stride of the tiles. This is a dictionary containing:
|
|
561
|
-
|
|
562
|
-
:x: the horizontal overlap in pixels.
|
|
563
|
-
:y: the vertical overlap in pixels.
|
|
564
|
-
:edges: if True, then the edge tiles will exclude the overlap
|
|
565
|
-
distance. If unset or False, the edge tiles are full size.
|
|
566
|
-
|
|
567
|
-
:param tile_offset: if present, adjust tile positions so that the
|
|
568
|
-
corner of one tile is at the specified location.
|
|
569
|
-
|
|
570
|
-
:left: the left offset in pixels.
|
|
571
|
-
:top: the top offset in pixels.
|
|
572
|
-
:auto: a boolean, if True, automatically set the offset to align
|
|
573
|
-
with the region's left and top.
|
|
574
|
-
|
|
575
|
-
:param kwargs: optional arguments. Some options are encoding,
|
|
576
|
-
jpegQuality, jpegSubsampling, tiffCompression, frame.
|
|
577
|
-
:returns: a dictionary of information needed for the tile iterator.
|
|
578
|
-
This is None if no tiles will be returned. Otherwise, this
|
|
579
|
-
contains:
|
|
580
|
-
|
|
581
|
-
:region: a dictionary of the source region information:
|
|
582
|
-
|
|
583
|
-
:width, height: the total output of the iterator in pixels.
|
|
584
|
-
This may be larger than the requested resolution (given by
|
|
585
|
-
output width and output height) if there isn't an exact
|
|
586
|
-
match between the requested resolution and available native
|
|
587
|
-
tiles.
|
|
588
|
-
:left, top, right, bottom: the coordinates within the image of
|
|
589
|
-
the region returned in the level pixel space.
|
|
590
|
-
|
|
591
|
-
:xmin, ymin, xmax, ymax: the tiles that will be included during the
|
|
592
|
-
iteration: [xmin, xmax) and [ymin, ymax).
|
|
593
|
-
:mode: either 'RGB' or 'RGBA'. This determines the color space
|
|
594
|
-
used for tiles.
|
|
595
|
-
:level: the tile level used for iteration.
|
|
596
|
-
:metadata: tile source metadata (from getMetadata)
|
|
597
|
-
:output: a dictionary of the output resolution information.
|
|
598
|
-
|
|
599
|
-
:width, height: the requested output resolution in pixels. If
|
|
600
|
-
this is different that region width and region height, then
|
|
601
|
-
the original request was asking for a different scale than
|
|
602
|
-
is being delivered.
|
|
603
|
-
|
|
604
|
-
:frame: the frame value for the base image.
|
|
605
|
-
:format: a tuple of allowed output formats.
|
|
606
|
-
:encoding: if the output format is TILE_FORMAT_IMAGE, the desired
|
|
607
|
-
encoding.
|
|
608
|
-
:requestedScale: the scale needed to convert from the region width
|
|
609
|
-
and height to the output width and height.
|
|
610
|
-
"""
|
|
611
|
-
maxWidth = kwargs.get('output', {}).get('maxWidth')
|
|
612
|
-
maxHeight = kwargs.get('output', {}).get('maxHeight')
|
|
613
|
-
if ((maxWidth is not None and
|
|
614
|
-
(not isinstance(maxWidth, int) or maxWidth < 0)) or
|
|
615
|
-
(maxHeight is not None and
|
|
616
|
-
(not isinstance(maxHeight, int) or maxHeight < 0))):
|
|
617
|
-
msg = 'Invalid output width or height. Minimum value is 0.'
|
|
618
|
-
raise ValueError(msg)
|
|
619
|
-
|
|
620
|
-
magLevel = None
|
|
621
|
-
mag = None
|
|
622
|
-
if maxWidth is None and maxHeight is None:
|
|
623
|
-
# If neither width nor height as specified, see if magnification,
|
|
624
|
-
# mm_x, or mm_y are requested.
|
|
625
|
-
magArgs = (kwargs.get('scale') or {}).copy()
|
|
626
|
-
magArgs['rounding'] = None
|
|
627
|
-
magLevel = self.getLevelForMagnification(**magArgs)
|
|
628
|
-
if magLevel is None and kwargs.get('scale', {}).get('exact'):
|
|
629
|
-
return None
|
|
630
|
-
mag = self.getMagnificationForLevel(magLevel)
|
|
631
|
-
metadata = self.getMetadata()
|
|
632
|
-
left, top, right, bottom = self._getRegionBounds(
|
|
633
|
-
metadata, desiredMagnification=mag, **kwargs.get('region', {}))
|
|
634
|
-
regionWidth = right - left
|
|
635
|
-
regionHeight = bottom - top
|
|
636
|
-
magRequestedScale: Optional[float] = None
|
|
637
|
-
if maxWidth is None and maxHeight is None and mag:
|
|
638
|
-
if mag.get('scale') in (1.0, None):
|
|
639
|
-
maxWidth, maxHeight = regionWidth, regionHeight
|
|
640
|
-
magRequestedScale = 1
|
|
641
|
-
else:
|
|
642
|
-
maxWidth = regionWidth / cast(float, mag['scale'])
|
|
643
|
-
maxHeight = regionHeight / cast(float, mag['scale'])
|
|
644
|
-
magRequestedScale = cast(float, mag['scale'])
|
|
645
|
-
outWidth, outHeight, calcScale = utilities._calculateWidthHeight(
|
|
646
|
-
maxWidth, maxHeight, regionWidth, regionHeight)
|
|
647
|
-
requestedScale = calcScale if magRequestedScale is None else magRequestedScale
|
|
648
|
-
if (regionWidth < 0 or regionHeight < 0 or outWidth == 0 or
|
|
649
|
-
outHeight == 0):
|
|
650
|
-
return None
|
|
651
|
-
|
|
652
|
-
preferredLevel = metadata['levels'] - 1
|
|
653
|
-
# If we are scaling the result, pick the tile level that is at least
|
|
654
|
-
# the resolution we need and is preferred by the tile source.
|
|
655
|
-
if outWidth != regionWidth or outHeight != regionHeight:
|
|
656
|
-
newLevel = self.getPreferredLevel(preferredLevel + int(
|
|
657
|
-
math.ceil(round(math.log(max(float(outWidth) / regionWidth,
|
|
658
|
-
float(outHeight) / regionHeight)) /
|
|
659
|
-
math.log(2), 4))))
|
|
660
|
-
if newLevel < preferredLevel:
|
|
661
|
-
# scale the bounds to the level we will use
|
|
662
|
-
factor = 2 ** (preferredLevel - newLevel)
|
|
663
|
-
left = int(left / factor)
|
|
664
|
-
right = int(right / factor)
|
|
665
|
-
regionWidth = right - left
|
|
666
|
-
top = int(top / factor)
|
|
667
|
-
bottom = int(bottom / factor)
|
|
668
|
-
regionHeight = bottom - top
|
|
669
|
-
preferredLevel = newLevel
|
|
670
|
-
requestedScale /= factor
|
|
671
|
-
# If an exact magnification was requested and this tile source doesn't
|
|
672
|
-
# have tiles at the appropriate level, indicate that we won't return
|
|
673
|
-
# anything.
|
|
674
|
-
if (magLevel is not None and magLevel != preferredLevel and
|
|
675
|
-
kwargs.get('scale', {}).get('exact')):
|
|
676
|
-
return None
|
|
677
|
-
|
|
678
|
-
tile_size = {
|
|
679
|
-
'width': metadata['tileWidth'],
|
|
680
|
-
'height': metadata['tileHeight'],
|
|
681
|
-
}
|
|
682
|
-
tile_overlap = {
|
|
683
|
-
'x': int(kwargs.get('tile_overlap', {}).get('x', 0) or 0),
|
|
684
|
-
'y': int(kwargs.get('tile_overlap', {}).get('y', 0) or 0),
|
|
685
|
-
'edges': kwargs.get('tile_overlap', {}).get('edges', False),
|
|
686
|
-
'offset_x': 0,
|
|
687
|
-
'offset_y': 0,
|
|
688
|
-
'range_x': 0,
|
|
689
|
-
'range_y': 0,
|
|
690
|
-
}
|
|
691
|
-
if not tile_overlap['edges']:
|
|
692
|
-
# offset by half the overlap
|
|
693
|
-
tile_overlap['offset_x'] = tile_overlap['x'] // 2
|
|
694
|
-
tile_overlap['offset_y'] = tile_overlap['y'] // 2
|
|
695
|
-
tile_overlap['range_x'] = tile_overlap['x']
|
|
696
|
-
tile_overlap['range_y'] = tile_overlap['y']
|
|
697
|
-
if 'tile_size' in kwargs:
|
|
698
|
-
tile_size['width'] = int(kwargs['tile_size'].get(
|
|
699
|
-
'width', kwargs['tile_size'].get('height', tile_size['width'])))
|
|
700
|
-
tile_size['height'] = int(kwargs['tile_size'].get(
|
|
701
|
-
'height', kwargs['tile_size'].get('width', tile_size['height'])))
|
|
702
|
-
# Tile size includes the overlap
|
|
703
|
-
tile_size['width'] -= tile_overlap['x']
|
|
704
|
-
tile_size['height'] -= tile_overlap['y']
|
|
705
|
-
if tile_size['width'] <= 0 or tile_size['height'] <= 0:
|
|
706
|
-
msg = 'Invalid tile_size or tile_overlap.'
|
|
707
|
-
raise ValueError(msg)
|
|
708
|
-
|
|
709
|
-
resample = (
|
|
710
|
-
False if round(requestedScale, 2) == 1.0 or
|
|
711
|
-
kwargs.get('resample') in (None, False) else kwargs.get('resample'))
|
|
712
|
-
# If we need to resample to make tiles at a non-native resolution,
|
|
713
|
-
# adjust the tile size and tile overlap parameters appropriately.
|
|
714
|
-
if resample is not False:
|
|
715
|
-
tile_size['width'] = max(1, int(math.ceil(tile_size['width'] * requestedScale)))
|
|
716
|
-
tile_size['height'] = max(1, int(math.ceil(tile_size['height'] * requestedScale)))
|
|
717
|
-
tile_overlap['x'] = int(math.ceil(tile_overlap['x'] * requestedScale))
|
|
718
|
-
tile_overlap['y'] = int(math.ceil(tile_overlap['y'] * requestedScale))
|
|
719
|
-
|
|
720
|
-
offset_x = kwargs.get('tile_offset', {}).get('left', 0)
|
|
721
|
-
offset_y = kwargs.get('tile_offset', {}).get('top', 0)
|
|
722
|
-
if kwargs.get('tile_offset', {}).get('auto'):
|
|
723
|
-
offset_x = left
|
|
724
|
-
offset_y = top
|
|
725
|
-
offset_x = (left - left % tile_size['width']) if offset_x > left else offset_x
|
|
726
|
-
offset_y = (top - top % tile_size['height']) if offset_y > top else offset_y
|
|
727
|
-
# If the overlapped tiles don't run over the edge, then the functional
|
|
728
|
-
# size of the region is reduced by the overlap. This factor is stored
|
|
729
|
-
# in the overlap offset_*.
|
|
730
|
-
xmin = int((left - offset_x) / tile_size['width'])
|
|
731
|
-
xmax = max(int(math.ceil((float(right - offset_x) - tile_overlap['range_x']) /
|
|
732
|
-
tile_size['width'])), xmin + 1)
|
|
733
|
-
ymin = int((top - offset_y) / tile_size['height'])
|
|
734
|
-
ymax = max(int(math.ceil((float(bottom - offset_y) - tile_overlap['range_y']) /
|
|
735
|
-
tile_size['height'])), ymin + 1)
|
|
736
|
-
tile_overlap.update({'xmin': xmin, 'xmax': xmax,
|
|
737
|
-
'ymin': ymin, 'ymax': ymax})
|
|
738
|
-
tile_overlap['offset_x'] += offset_x
|
|
739
|
-
tile_overlap['offset_y'] += offset_y
|
|
740
|
-
|
|
741
|
-
# Use RGB for JPEG, RGBA for PNG
|
|
742
|
-
mode = 'RGBA' if kwargs.get('encoding') in {'PNG', 'TIFF', 'TILED'} else 'RGB'
|
|
743
|
-
|
|
744
|
-
info = {
|
|
745
|
-
'region': {
|
|
746
|
-
'top': top,
|
|
747
|
-
'left': left,
|
|
748
|
-
'bottom': bottom,
|
|
749
|
-
'right': right,
|
|
750
|
-
'width': regionWidth,
|
|
751
|
-
'height': regionHeight,
|
|
752
|
-
},
|
|
753
|
-
'xmin': xmin,
|
|
754
|
-
'ymin': ymin,
|
|
755
|
-
'xmax': xmax,
|
|
756
|
-
'ymax': ymax,
|
|
757
|
-
'mode': mode,
|
|
758
|
-
'level': preferredLevel,
|
|
759
|
-
'metadata': metadata,
|
|
760
|
-
'output': {
|
|
761
|
-
'width': outWidth,
|
|
762
|
-
'height': outHeight,
|
|
763
|
-
},
|
|
764
|
-
'frame': kwargs.get('frame'),
|
|
765
|
-
'format': kwargs.get('format', (TILE_FORMAT_NUMPY, )),
|
|
766
|
-
'encoding': kwargs.get('encoding'),
|
|
767
|
-
'requestedScale': requestedScale,
|
|
768
|
-
'resample': resample,
|
|
769
|
-
'tile_overlap': tile_overlap,
|
|
770
|
-
'tile_position': kwargs.get('tile_position'),
|
|
771
|
-
'tile_size': tile_size,
|
|
772
|
-
}
|
|
773
|
-
return info
|
|
774
|
-
|
|
775
|
-
def _tileIterator(self, iterInfo: Dict[str, Any]) -> Iterator[LazyTileDict]:
|
|
776
|
-
"""
|
|
777
|
-
Given tile iterator information, iterate through the tiles.
|
|
778
|
-
Each tile is returned as part of a dictionary that includes
|
|
779
|
-
|
|
780
|
-
:x, y: (left, top) coordinate in current magnification pixels
|
|
781
|
-
:width, height: size of current tile in current magnification
|
|
782
|
-
pixels
|
|
783
|
-
:tile: cropped tile image
|
|
784
|
-
:format: format of the tile. One of TILE_FORMAT_NUMPY,
|
|
785
|
-
TILE_FORMAT_PIL, or TILE_FORMAT_IMAGE. TILE_FORMAT_IMAGE is
|
|
786
|
-
only returned if it was explicitly allowed and the tile is
|
|
787
|
-
already in the correct image encoding.
|
|
788
|
-
:level: level of the current tile
|
|
789
|
-
:level_x, level_y: the tile reference number within the level.
|
|
790
|
-
Tiles are numbered (0, 0), (1, 0), (2, 0), etc. The 0th tile
|
|
791
|
-
yielded may not be (0, 0) if a region is specified.
|
|
792
|
-
:tile_position: a dictionary of the tile position within the
|
|
793
|
-
iterator, containing:
|
|
794
|
-
|
|
795
|
-
:level_x, level_y: the tile reference number within the level.
|
|
796
|
-
:region_x, region_y: 0, 0 is the first tile in the full
|
|
797
|
-
iteration (when not restricting the iteration to a single
|
|
798
|
-
tile).
|
|
799
|
-
:position: a 0-based value for the tile within the full
|
|
800
|
-
iteration.
|
|
801
|
-
|
|
802
|
-
:iterator_range: a dictionary of the output range of the iterator:
|
|
803
|
-
|
|
804
|
-
:level_x_min, level_x_max: the tiles that are be included
|
|
805
|
-
during the full iteration: [layer_x_min, layer_x_max).
|
|
806
|
-
:level_y_min, level_y_max: the tiles that are be included
|
|
807
|
-
during the full iteration: [layer_y_min, layer_y_max).
|
|
808
|
-
:region_x_max, region_y_max: the number of tiles included during
|
|
809
|
-
the full iteration. This is layer_x_max - layer_x_min,
|
|
810
|
-
layer_y_max - layer_y_min.
|
|
811
|
-
:position: the total number of tiles included in the full
|
|
812
|
-
iteration. This is region_x_max * region_y_max.
|
|
813
|
-
|
|
814
|
-
:magnification: magnification of the current tile
|
|
815
|
-
:mm_x, mm_y: size of the current tile pixel in millimeters.
|
|
816
|
-
:gx, gy: (left, top) coordinates in maximum-resolution pixels
|
|
817
|
-
:gwidth, gheight: size of of the current tile in maximum-resolution
|
|
818
|
-
pixels.
|
|
819
|
-
:tile_overlap: the amount of overlap with neighboring tiles (left,
|
|
820
|
-
top, right, and bottom). Overlap never extends outside of the
|
|
821
|
-
requested region.
|
|
822
|
-
|
|
823
|
-
If a region that includes partial tiles is requested, those tiles are
|
|
824
|
-
cropped appropriately. Most images will have tiles that get cropped
|
|
825
|
-
along the right and bottom edges in any case.
|
|
826
|
-
|
|
827
|
-
:param iterInfo: tile iterator information. See _tileIteratorInfo.
|
|
828
|
-
:yields: an iterator that returns a dictionary as listed above.
|
|
829
|
-
"""
|
|
830
|
-
regionWidth = iterInfo['region']['width']
|
|
831
|
-
regionHeight = iterInfo['region']['height']
|
|
832
|
-
left = iterInfo['region']['left']
|
|
833
|
-
top = iterInfo['region']['top']
|
|
834
|
-
xmin = iterInfo['xmin']
|
|
835
|
-
ymin = iterInfo['ymin']
|
|
836
|
-
xmax = iterInfo['xmax']
|
|
837
|
-
ymax = iterInfo['ymax']
|
|
838
|
-
level = iterInfo['level']
|
|
839
|
-
metadata = iterInfo['metadata']
|
|
840
|
-
tileSize = iterInfo['tile_size']
|
|
841
|
-
tileOverlap = iterInfo['tile_overlap']
|
|
842
|
-
format = iterInfo['format']
|
|
843
|
-
encoding = iterInfo['encoding']
|
|
844
|
-
|
|
845
|
-
self.logger.debug(
|
|
846
|
-
'Fetching region of an image with a source size of %d x %d; '
|
|
847
|
-
'getting %d tile%s',
|
|
848
|
-
regionWidth, regionHeight, (xmax - xmin) * (ymax - ymin),
|
|
849
|
-
'' if (xmax - xmin) * (ymax - ymin) == 1 else 's')
|
|
850
|
-
|
|
851
|
-
# If tile is specified, return at most one tile
|
|
852
|
-
if iterInfo.get('tile_position') is not None:
|
|
853
|
-
tilePos = iterInfo.get('tile_position')
|
|
854
|
-
if isinstance(tilePos, dict):
|
|
855
|
-
if tilePos.get('position') is not None:
|
|
856
|
-
tilePos = tilePos['position']
|
|
857
|
-
elif 'region_x' in tilePos and 'region_y' in tilePos:
|
|
858
|
-
tilePos = (tilePos['region_x'] +
|
|
859
|
-
tilePos['region_y'] * (xmax - xmin))
|
|
860
|
-
elif 'level_x' in tilePos and 'level_y' in tilePos:
|
|
861
|
-
tilePos = ((tilePos['level_x'] - xmin) +
|
|
862
|
-
(tilePos['level_y'] - ymin) * (xmax - xmin))
|
|
863
|
-
if tilePos < 0 or tilePos >= (ymax - ymin) * (xmax - xmin):
|
|
864
|
-
xmax = xmin
|
|
865
|
-
else:
|
|
866
|
-
ymin += int(tilePos / (xmax - xmin))
|
|
867
|
-
ymax = ymin + 1
|
|
868
|
-
xmin += int(tilePos % (xmax - xmin))
|
|
869
|
-
xmax = xmin + 1
|
|
870
|
-
mag = self.getMagnificationForLevel(level)
|
|
871
|
-
scale = mag.get('scale', 1.0)
|
|
872
|
-
retile = (tileSize['width'] != metadata['tileWidth'] or
|
|
873
|
-
tileSize['height'] != metadata['tileHeight'] or
|
|
874
|
-
tileOverlap['x'] or tileOverlap['y'])
|
|
875
|
-
for y in range(ymin, ymax):
|
|
876
|
-
for x in range(xmin, xmax):
|
|
877
|
-
crop = None
|
|
878
|
-
posX = int(x * tileSize['width'] - tileOverlap['x'] // 2 +
|
|
879
|
-
tileOverlap['offset_x'] - left)
|
|
880
|
-
posY = int(y * tileSize['height'] - tileOverlap['y'] // 2 +
|
|
881
|
-
tileOverlap['offset_y'] - top)
|
|
882
|
-
tileWidth = tileSize['width'] + tileOverlap['x']
|
|
883
|
-
tileHeight = tileSize['height'] + tileOverlap['y']
|
|
884
|
-
# crop as needed
|
|
885
|
-
if (posX < 0 or posY < 0 or posX + tileWidth > regionWidth or
|
|
886
|
-
posY + tileHeight > regionHeight):
|
|
887
|
-
crop = (max(0, -posX),
|
|
888
|
-
max(0, -posY),
|
|
889
|
-
int(min(tileWidth, regionWidth - posX)),
|
|
890
|
-
int(min(tileHeight, regionHeight - posY)))
|
|
891
|
-
posX += crop[0]
|
|
892
|
-
posY += crop[1]
|
|
893
|
-
tileWidth = crop[2] - crop[0]
|
|
894
|
-
tileHeight = crop[3] - crop[1]
|
|
895
|
-
overlap = {
|
|
896
|
-
'left': max(0, x * tileSize['width'] + tileOverlap['offset_x'] - left - posX),
|
|
897
|
-
'top': max(0, y * tileSize['height'] + tileOverlap['offset_y'] - top - posY),
|
|
898
|
-
}
|
|
899
|
-
overlap['right'] = (
|
|
900
|
-
max(0, tileWidth - tileSize['width'] - overlap['left'])
|
|
901
|
-
if x != xmin or not tileOverlap['range_x'] else
|
|
902
|
-
min(tileWidth, tileOverlap['range_x'] - tileOverlap['offset_x']))
|
|
903
|
-
overlap['bottom'] = (
|
|
904
|
-
max(0, tileHeight - tileSize['height'] - overlap['top'])
|
|
905
|
-
if y != ymin or not tileOverlap['range_y'] else
|
|
906
|
-
min(tileHeight, tileOverlap['range_y'] - tileOverlap['offset_y']))
|
|
907
|
-
if tileOverlap['range_x']:
|
|
908
|
-
overlap['left'] = 0 if x == tileOverlap['xmin'] else overlap['left']
|
|
909
|
-
overlap['right'] = 0 if x + 1 == tileOverlap['xmax'] else overlap['right']
|
|
910
|
-
if tileOverlap['range_y']:
|
|
911
|
-
overlap['top'] = 0 if y == tileOverlap['ymin'] else overlap['top']
|
|
912
|
-
overlap['bottom'] = 0 if y + 1 == tileOverlap['ymax'] else overlap['bottom']
|
|
913
|
-
tile = LazyTileDict({
|
|
914
|
-
'x': x,
|
|
915
|
-
'y': y,
|
|
916
|
-
'frame': iterInfo.get('frame'),
|
|
917
|
-
'level': level,
|
|
918
|
-
'format': format,
|
|
919
|
-
'encoding': encoding,
|
|
920
|
-
'crop': crop,
|
|
921
|
-
'requestedScale': iterInfo['requestedScale'],
|
|
922
|
-
'retile': retile,
|
|
923
|
-
'metadata': metadata,
|
|
924
|
-
'source': self,
|
|
925
|
-
}, {
|
|
926
|
-
'x': posX + left,
|
|
927
|
-
'y': posY + top,
|
|
928
|
-
'width': tileWidth,
|
|
929
|
-
'height': tileHeight,
|
|
930
|
-
'level': level,
|
|
931
|
-
'level_x': x,
|
|
932
|
-
'level_y': y,
|
|
933
|
-
'magnification': mag['magnification'],
|
|
934
|
-
'mm_x': mag['mm_x'],
|
|
935
|
-
'mm_y': mag['mm_y'],
|
|
936
|
-
'tile_position': {
|
|
937
|
-
'level_x': x,
|
|
938
|
-
'level_y': y,
|
|
939
|
-
'region_x': x - iterInfo['xmin'],
|
|
940
|
-
'region_y': y - iterInfo['ymin'],
|
|
941
|
-
'position': ((x - iterInfo['xmin']) +
|
|
942
|
-
(y - iterInfo['ymin']) *
|
|
943
|
-
(iterInfo['xmax'] - iterInfo['xmin'])),
|
|
944
|
-
},
|
|
945
|
-
'iterator_range': {
|
|
946
|
-
'level_x_min': iterInfo['xmin'],
|
|
947
|
-
'level_y_min': iterInfo['ymin'],
|
|
948
|
-
'level_x_max': iterInfo['xmax'],
|
|
949
|
-
'level_y_max': iterInfo['ymax'],
|
|
950
|
-
'region_x_max': iterInfo['xmax'] - iterInfo['xmin'],
|
|
951
|
-
'region_y_max': iterInfo['ymax'] - iterInfo['ymin'],
|
|
952
|
-
'position': ((iterInfo['xmax'] - iterInfo['xmin']) *
|
|
953
|
-
(iterInfo['ymax'] - iterInfo['ymin'])),
|
|
954
|
-
},
|
|
955
|
-
'tile_overlap': overlap,
|
|
956
|
-
})
|
|
957
|
-
tile['gx'] = tile['x'] * scale
|
|
958
|
-
tile['gy'] = tile['y'] * scale
|
|
959
|
-
tile['gwidth'] = tile['width'] * scale
|
|
960
|
-
tile['gheight'] = tile['height'] * scale
|
|
961
|
-
yield tile
|
|
962
|
-
|
|
963
492
|
def _pilFormatMatches(self, image: Any, match: Union[bool, str] = True, **kwargs) -> bool:
|
|
964
493
|
"""
|
|
965
494
|
Determine if the specified PIL image matches the format of the tile
|
|
@@ -2199,20 +1728,20 @@ class TileSource(IPyLeafletMixin):
|
|
|
2199
1728
|
'width': max(self.tileWidth, 4096),
|
|
2200
1729
|
'height': max(self.tileHeight, 4096)}
|
|
2201
1730
|
kwargs['tile_offset'] = {'auto': True}
|
|
2202
|
-
|
|
2203
|
-
if
|
|
1731
|
+
tileIter = TileIterator(self, format=TILE_FORMAT_NUMPY, resample=None, **kwargs)
|
|
1732
|
+
if tileIter.info is None:
|
|
2204
1733
|
pilimage = PIL.Image.new('RGB', (0, 0))
|
|
2205
1734
|
return utilities._encodeImage(pilimage, format=format, **kwargs)
|
|
2206
|
-
regionWidth =
|
|
2207
|
-
regionHeight =
|
|
2208
|
-
top =
|
|
2209
|
-
left =
|
|
2210
|
-
mode = None if TILE_FORMAT_NUMPY in format else
|
|
2211
|
-
outWidth =
|
|
2212
|
-
outHeight =
|
|
1735
|
+
regionWidth = tileIter.info['region']['width']
|
|
1736
|
+
regionHeight = tileIter.info['region']['height']
|
|
1737
|
+
top = tileIter.info['region']['top']
|
|
1738
|
+
left = tileIter.info['region']['left']
|
|
1739
|
+
mode = None if TILE_FORMAT_NUMPY in format else tileIter.info['mode']
|
|
1740
|
+
outWidth = tileIter.info['output']['width']
|
|
1741
|
+
outHeight = tileIter.info['output']['height']
|
|
2213
1742
|
image: Optional[Union[np.ndarray, PIL.Image.Image, ImageBytes, bytes]] = None
|
|
2214
1743
|
tiledimage = None
|
|
2215
|
-
for tile in
|
|
1744
|
+
for tile in tileIter:
|
|
2216
1745
|
# Add each tile to the image
|
|
2217
1746
|
subimage, _ = _imageToNumpy(tile['tile'])
|
|
2218
1747
|
x0, y0 = tile['x'] - left, tile['y'] - top
|
|
@@ -2230,7 +1759,7 @@ class TileSource(IPyLeafletMixin):
|
|
|
2230
1759
|
outHeight = int(math.floor(outHeight))
|
|
2231
1760
|
if tiled:
|
|
2232
1761
|
return self._encodeTiledImage(
|
|
2233
|
-
cast(Dict[str, Any], tiledimage), outWidth, outHeight,
|
|
1762
|
+
cast(Dict[str, Any], tiledimage), outWidth, outHeight, tileIter.info, **kwargs)
|
|
2234
1763
|
if outWidth != regionWidth or outHeight != regionHeight:
|
|
2235
1764
|
dtype = cast(np.ndarray, image).dtype
|
|
2236
1765
|
image = _imageToPIL(cast(np.ndarray, image), mode).resize(
|
|
@@ -2401,19 +1930,20 @@ class TileSource(IPyLeafletMixin):
|
|
|
2401
1930
|
if not isinstance(format, (tuple, set, list)):
|
|
2402
1931
|
format = (format, )
|
|
2403
1932
|
tiled = TILE_FORMAT_IMAGE in format and kwargs.get('encoding') == 'TILED'
|
|
2404
|
-
|
|
2405
|
-
|
|
1933
|
+
tileIter = TileIterator(self, format=TILE_FORMAT_NUMPY, resample=None,
|
|
1934
|
+
frame=frameList[0], **kwargs)
|
|
1935
|
+
if tileIter.info is None:
|
|
2406
1936
|
pilimage = PIL.Image.new('RGB', (0, 0))
|
|
2407
1937
|
return utilities._encodeImage(pilimage, format=format, **kwargs)
|
|
2408
|
-
frameWidth =
|
|
2409
|
-
frameHeight =
|
|
1938
|
+
frameWidth = tileIter.info['output']['width']
|
|
1939
|
+
frameHeight = tileIter.info['output']['height']
|
|
2410
1940
|
maxWidth = kwargs.get('output', {}).get('maxWidth')
|
|
2411
1941
|
maxHeight = kwargs.get('output', {}).get('maxHeight')
|
|
2412
1942
|
if kwargs.get('fill') and maxWidth and maxHeight:
|
|
2413
1943
|
frameWidth, frameHeight = maxWidth, maxHeight
|
|
2414
1944
|
outWidth = frameWidth * framesAcross
|
|
2415
1945
|
outHeight = frameHeight * framesHigh
|
|
2416
|
-
tile = next(
|
|
1946
|
+
tile = next(tileIter)
|
|
2417
1947
|
image = None
|
|
2418
1948
|
tiledimage = None
|
|
2419
1949
|
if max_workers is not None and max_workers < 0:
|
|
@@ -2446,7 +1976,7 @@ class TileSource(IPyLeafletMixin):
|
|
|
2446
1976
|
outWidth, outHeight)
|
|
2447
1977
|
if tiled:
|
|
2448
1978
|
return self._encodeTiledImage(
|
|
2449
|
-
cast(Dict[str, Any], tiledimage), outWidth, outHeight,
|
|
1979
|
+
cast(Dict[str, Any], tiledimage), outWidth, outHeight, tileIter.info, **kwargs)
|
|
2450
1980
|
return utilities._encodeImage(cast(np.ndarray, image), format=format, **kwargs)
|
|
2451
1981
|
|
|
2452
1982
|
def getRegionAtAnotherScale(
|
|
@@ -2750,26 +2280,7 @@ class TileSource(IPyLeafletMixin):
|
|
|
2750
2280
|
:param kwargs: optional arguments.
|
|
2751
2281
|
:yields: an iterator that returns a dictionary as listed above.
|
|
2752
2282
|
"""
|
|
2753
|
-
|
|
2754
|
-
format = (format, )
|
|
2755
|
-
if TILE_FORMAT_IMAGE in format:
|
|
2756
|
-
encoding = kwargs.get('encoding')
|
|
2757
|
-
if encoding not in TileOutputMimeTypes:
|
|
2758
|
-
raise ValueError('Invalid encoding "%s"' % encoding)
|
|
2759
|
-
iterFormat = format if resample in (False, None) else (
|
|
2760
|
-
TILE_FORMAT_PIL, )
|
|
2761
|
-
iterInfo = self._tileIteratorInfo(format=iterFormat, resample=resample,
|
|
2762
|
-
**kwargs)
|
|
2763
|
-
if not iterInfo:
|
|
2764
|
-
return
|
|
2765
|
-
# check if the desired scale is different from the actual scale and
|
|
2766
|
-
# resampling is needed. Ignore small scale differences.
|
|
2767
|
-
if (resample in (False, None) or
|
|
2768
|
-
round(iterInfo['requestedScale'], 2) == 1.0):
|
|
2769
|
-
resample = False
|
|
2770
|
-
for tile in self._tileIterator(iterInfo):
|
|
2771
|
-
tile.setFormat(format, resample, kwargs)
|
|
2772
|
-
yield tile
|
|
2283
|
+
return TileIterator(self, format=format, resample=resample, **kwargs)
|
|
2773
2284
|
|
|
2774
2285
|
def tileIteratorAtAnotherScale(
|
|
2775
2286
|
self, sourceRegion: Dict[str, Any],
|
|
@@ -2879,11 +2390,8 @@ class TileSource(IPyLeafletMixin):
|
|
|
2879
2390
|
# This could be
|
|
2880
2391
|
# img, format = self.getRegion(format=TILE_FORMAT_PIL, **regionArgs)
|
|
2881
2392
|
# where img is the PIL image (rather than tile['tile'], but using
|
|
2882
|
-
#
|
|
2883
|
-
|
|
2884
|
-
if iterInfo is None:
|
|
2885
|
-
return JSONDict(pixel)
|
|
2886
|
-
tile = next(self._tileIterator(iterInfo), None)
|
|
2393
|
+
# TileIterator is slightly more efficient.
|
|
2394
|
+
tile = next(TileIterator(self, format=TILE_FORMAT_NUMPY, **regionArgs), None)
|
|
2887
2395
|
if tile is None:
|
|
2888
2396
|
return JSONDict(pixel)
|
|
2889
2397
|
if includeTileRecord:
|