diff --git a/openslide/deepzoom.py b/openslide/deepzoom.py index 66734d70..df802559 100644 --- a/openslide/deepzoom.py +++ b/openslide/deepzoom.py @@ -46,7 +46,13 @@ class DeepZoomGenerator: openslide.PROPERTY_NAME_BOUNDS_HEIGHT, ) - def __init__(self, osr, tile_size=254, overlap=1, limit_bounds=False): + def __init__( + self, + osr: openslide.AbstractSlide, + tile_size: int = 254, + overlap: int = 1, + limit_bounds: bool = False, + ): """Create a DeepZoomGenerator wrapping an OpenSlide object. osr: a slide object. @@ -101,7 +107,7 @@ def __init__(self, osr, tile_size=254, overlap=1, limit_bounds=False): self._z_dimensions = tuple(reversed(z_dimensions)) # Tile - def tiles(z_lim): + def tiles(z_lim: int) -> int: return int(math.ceil(z_lim / self._z_t_downsample)) self._t_dimensions = tuple( @@ -112,7 +118,8 @@ def tiles(z_lim): self._dz_levels = len(self._z_dimensions) # Total downsamples for each Deep Zoom level - l0_z_downsamples = tuple( + # mypy infers this as a tuple[Any, ...] due to the ** operator + l0_z_downsamples: tuple[int, ...] = tuple( 2 ** (self._dz_levels - dz_level - 1) for dz_level in range(self._dz_levels) ) @@ -134,7 +141,7 @@ def tiles(z_lim): openslide.PROPERTY_NAME_BACKGROUND_COLOR, 'ffffff' ) - def __repr__(self): + def __repr__(self) -> str: return '{}({!r}, tile_size={!r}, overlap={!r}, limit_bounds={!r})'.format( self.__class__.__name__, self._osr, @@ -144,26 +151,26 @@ def __repr__(self): ) @property - def level_count(self): + def level_count(self) -> int: """The number of Deep Zoom levels in the image.""" return self._dz_levels @property - def level_tiles(self): + def level_tiles(self) -> tuple[tuple[int, int], ...]: """A list of (tiles_x, tiles_y) tuples for each Deep Zoom level.""" return self._t_dimensions @property - def level_dimensions(self): + def level_dimensions(self) -> tuple[tuple[int, ...], ...]: """A list of (pixels_x, pixels_y) tuples for each Deep Zoom level.""" return self._z_dimensions @property - def tile_count(self): + def tile_count(self) -> int: """The total number of Deep Zoom tiles in the image.""" return sum(t_cols * t_rows for t_cols, t_rows in self._t_dimensions) - def get_tile(self, level, address): + def get_tile(self, level: int, address: tuple[int, int]) -> Image.Image: """Return an RGB PIL.Image for a tile. level: the Deep Zoom level. @@ -191,7 +198,9 @@ def get_tile(self, level, address): return tile - def _get_tile_info(self, dz_level, t_location): + def _get_tile_info( + self, dz_level: int, t_location: tuple[int, int] + ) -> tuple[tuple[tuple[int, int], int, tuple[int, int]], tuple[int, int]]: # Check parameters if dz_level < 0 or dz_level >= self._dz_levels: raise ValueError("Invalid level") @@ -210,42 +219,62 @@ def _get_tile_info(self, dz_level, t_location): ) # Get final size of the tile - z_size = tuple( - min(self._z_t_downsample, z_lim - self._z_t_downsample * t) + z_tl + z_br - for t, z_lim, z_tl, z_br in zip( - t_location, self._z_dimensions[dz_level], z_overlap_tl, z_overlap_br + z_size = ( + min( + self._z_t_downsample, + self._z_dimensions[dz_level][0] - self._z_t_downsample * t_location[0], ) + + z_overlap_tl[0] + + z_overlap_br[0], + min( + self._z_t_downsample, + self._z_dimensions[dz_level][1] - self._z_t_downsample * t_location[1], + ) + + z_overlap_tl[1] + + z_overlap_br[1], ) # Obtain the region coordinates - z_location = [self._z_from_t(t) for t in t_location] - l_location = [ - self._l_from_z(dz_level, z - z_tl) - for z, z_tl in zip(z_location, z_overlap_tl) - ] + z_location = (self._z_from_t(t_location[0]), self._z_from_t(t_location[1])) + l_location = ( + self._l_from_z(dz_level, z_location[0] - z_overlap_tl[0]), + self._l_from_z(dz_level, z_location[1] - z_overlap_tl[1]), + ) # Round location down and size up, and add offset of active area - l0_location = tuple( - int(self._l0_from_l(slide_level, l) + l0_off) - for l, l0_off in zip(l_location, self._l0_offset) + l0_location = ( + int(self._l0_from_l(slide_level, l_location[0]) + self._l0_offset[0]), + int(self._l0_from_l(slide_level, l_location[1]) + self._l0_offset[1]), ) - l_size = tuple( - int(min(math.ceil(self._l_from_z(dz_level, dz)), l_lim - math.ceil(l))) - for l, dz, l_lim in zip(l_location, z_size, self._l_dimensions[slide_level]) + l_size = ( + int( + min( + math.ceil(self._l_from_z(dz_level, z_size[0])), + self._l_dimensions[slide_level][0] - math.ceil(l_location[0]), + ) + ), + int( + min( + math.ceil(self._l_from_z(dz_level, z_size[1])), + self._l_dimensions[slide_level][1] - math.ceil(l_location[1]), + ) + ), ) # Return read_region() parameters plus tile size for final scaling return ((l0_location, slide_level, l_size), z_size) - def _l0_from_l(self, slide_level, l): + def _l0_from_l(self, slide_level: int, l: float) -> float: return self._l0_l_downsamples[slide_level] * l - def _l_from_z(self, dz_level, z): + def _l_from_z(self, dz_level: int, z: int) -> float: return self._l_z_downsamples[dz_level] * z - def _z_from_t(self, t): + def _z_from_t(self, t: int) -> int: return self._z_t_downsample * t - def get_tile_coordinates(self, level, address): + def get_tile_coordinates( + self, level: int, address: tuple[int, int] + ) -> tuple[tuple[int, int], int, tuple[int, int]]: """Return the OpenSlide.read_region() arguments for the specified tile. Most users should call get_tile() rather than calling @@ -256,7 +285,9 @@ def get_tile_coordinates(self, level, address): tuple.""" return self._get_tile_info(level, address)[0] - def get_tile_dimensions(self, level, address): + def get_tile_dimensions( + self, level: int, address: tuple[int, int] + ) -> tuple[int, int]: """Return a (pixels_x, pixels_y) tuple for the specified tile. level: the Deep Zoom level. @@ -264,7 +295,7 @@ def get_tile_dimensions(self, level, address): tuple.""" return self._get_tile_info(level, address)[1] - def get_dzi(self, format): + def get_dzi(self, format: str) -> str: """Return a string containing the XML metadata for the .dzi file. format: the format of the individual tiles ('png' or 'jpeg')"""