2222This module provides functionality for generating Deep Zoom images from
2323OpenSlide objects.
2424"""
25+ from __future__ import annotations
2526
2627from io import BytesIO
2728import math
@@ -44,7 +45,13 @@ class DeepZoomGenerator:
4445 openslide .PROPERTY_NAME_BOUNDS_HEIGHT ,
4546 )
4647
47- def __init__ (self , osr , tile_size = 254 , overlap = 1 , limit_bounds = False ):
48+ def __init__ (
49+ self ,
50+ osr : openslide .AbstractSlide ,
51+ tile_size : int = 254 ,
52+ overlap : int = 1 ,
53+ limit_bounds : bool = False ,
54+ ):
4855 """Create a DeepZoomGenerator wrapping an OpenSlide object.
4956
5057 osr: a slide object.
@@ -99,7 +106,7 @@ def __init__(self, osr, tile_size=254, overlap=1, limit_bounds=False):
99106 self ._z_dimensions = tuple (reversed (z_dimensions ))
100107
101108 # Tile
102- def tiles (z_lim ) :
109+ def tiles (z_lim : int ) -> int :
103110 return int (math .ceil (z_lim / self ._z_t_downsample ))
104111
105112 self ._t_dimensions = tuple (
@@ -110,7 +117,8 @@ def tiles(z_lim):
110117 self ._dz_levels = len (self ._z_dimensions )
111118
112119 # Total downsamples for each Deep Zoom level
113- l0_z_downsamples = tuple (
120+ # mypy infers this as a tuple[Any, ...] due to the ** operator
121+ l0_z_downsamples : tuple [int , ...] = tuple (
114122 2 ** (self ._dz_levels - dz_level - 1 ) for dz_level in range (self ._dz_levels )
115123 )
116124
@@ -132,7 +140,7 @@ def tiles(z_lim):
132140 openslide .PROPERTY_NAME_BACKGROUND_COLOR , 'ffffff'
133141 )
134142
135- def __repr__ (self ):
143+ def __repr__ (self ) -> str :
136144 return '{}({!r}, tile_size={!r}, overlap={!r}, limit_bounds={!r})' .format (
137145 self .__class__ .__name__ ,
138146 self ._osr ,
@@ -142,26 +150,26 @@ def __repr__(self):
142150 )
143151
144152 @property
145- def level_count (self ):
153+ def level_count (self ) -> int :
146154 """The number of Deep Zoom levels in the image."""
147155 return self ._dz_levels
148156
149157 @property
150- def level_tiles (self ):
158+ def level_tiles (self ) -> tuple [ tuple [ int , int ], ...] :
151159 """A list of (tiles_x, tiles_y) tuples for each Deep Zoom level."""
152160 return self ._t_dimensions
153161
154162 @property
155- def level_dimensions (self ):
163+ def level_dimensions (self ) -> tuple [ tuple [ int , ...], ...] :
156164 """A list of (pixels_x, pixels_y) tuples for each Deep Zoom level."""
157165 return self ._z_dimensions
158166
159167 @property
160- def tile_count (self ):
168+ def tile_count (self ) -> int :
161169 """The total number of Deep Zoom tiles in the image."""
162170 return sum (t_cols * t_rows for t_cols , t_rows in self ._t_dimensions )
163171
164- def get_tile (self , level , address ) :
172+ def get_tile (self , level : int , address : tuple [ int , int ]) -> Image . Image :
165173 """Return an RGB PIL.Image for a tile.
166174
167175 level: the Deep Zoom level.
@@ -189,7 +197,9 @@ def get_tile(self, level, address):
189197
190198 return tile
191199
192- def _get_tile_info (self , dz_level , t_location ):
200+ def _get_tile_info (
201+ self , dz_level : int , t_location : tuple [int , int ]
202+ ) -> tuple [tuple [tuple [int , int ], int , tuple [int , int ]], tuple [int , int ]]:
193203 # Check parameters
194204 if dz_level < 0 or dz_level >= self ._dz_levels :
195205 raise ValueError ("Invalid level" )
@@ -208,42 +218,62 @@ def _get_tile_info(self, dz_level, t_location):
208218 )
209219
210220 # Get final size of the tile
211- z_size = tuple (
212- min (self . _z_t_downsample , z_lim - self . _z_t_downsample * t ) + z_tl + z_br
213- for t , z_lim , z_tl , z_br in zip (
214- t_location , self ._z_dimensions [dz_level ], z_overlap_tl , z_overlap_br
221+ z_size = (
222+ min (
223+ self . _z_t_downsample ,
224+ self ._z_dimensions [dz_level ][ 0 ] - self . _z_t_downsample * t_location [ 0 ],
215225 )
226+ + z_overlap_tl [0 ]
227+ + z_overlap_br [0 ],
228+ min (
229+ self ._z_t_downsample ,
230+ self ._z_dimensions [dz_level ][1 ] - self ._z_t_downsample * t_location [1 ],
231+ )
232+ + z_overlap_tl [1 ]
233+ + z_overlap_br [1 ],
216234 )
217235
218236 # Obtain the region coordinates
219- z_location = [ self ._z_from_t (t ) for t in t_location ]
220- l_location = [
221- self ._l_from_z (dz_level , z - z_tl )
222- for z , z_tl in zip ( z_location , z_overlap_tl )
223- ]
237+ z_location = ( self ._z_from_t (t_location [ 0 ]), self . _z_from_t ( t_location [ 1 ]))
238+ l_location = (
239+ self ._l_from_z (dz_level , z_location [ 0 ] - z_overlap_tl [ 0 ]),
240+ self . _l_from_z ( dz_level , z_location [ 1 ] - z_overlap_tl [ 1 ]),
241+ )
224242 # Round location down and size up, and add offset of active area
225- l0_location = tuple (
226- int (self ._l0_from_l (slide_level , l ) + l0_off )
227- for l , l0_off in zip ( l_location , self ._l0_offset )
243+ l0_location = (
244+ int (self ._l0_from_l (slide_level , l_location [ 0 ] ) + self . _l0_offset [ 0 ]),
245+ int ( self . _l0_from_l ( slide_level , l_location [ 1 ]) + self ._l0_offset [ 1 ]),
228246 )
229- l_size = tuple (
230- int (min (math .ceil (self ._l_from_z (dz_level , dz )), l_lim - math .ceil (l )))
231- for l , dz , l_lim in zip (l_location , z_size , self ._l_dimensions [slide_level ])
247+ l_size = (
248+ int (
249+ min (
250+ math .ceil (self ._l_from_z (dz_level , z_size [0 ])),
251+ self ._l_dimensions [slide_level ][0 ] - math .ceil (l_location [0 ]),
252+ )
253+ ),
254+ int (
255+ min (
256+ math .ceil (self ._l_from_z (dz_level , z_size [1 ])),
257+ self ._l_dimensions [slide_level ][1 ] - math .ceil (l_location [1 ]),
258+ )
259+ ),
232260 )
233261
234262 # Return read_region() parameters plus tile size for final scaling
235263 return ((l0_location , slide_level , l_size ), z_size )
236264
237- def _l0_from_l (self , slide_level , l ) :
265+ def _l0_from_l (self , slide_level : int , l : float ) -> float :
238266 return self ._l0_l_downsamples [slide_level ] * l
239267
240- def _l_from_z (self , dz_level , z ) :
268+ def _l_from_z (self , dz_level : int , z : int ) -> float :
241269 return self ._l_z_downsamples [dz_level ] * z
242270
243- def _z_from_t (self , t ) :
271+ def _z_from_t (self , t : int ) -> int :
244272 return self ._z_t_downsample * t
245273
246- def get_tile_coordinates (self , level , address ):
274+ def get_tile_coordinates (
275+ self , level : int , address : tuple [int , int ]
276+ ) -> tuple [tuple [int , int ], int , tuple [int , int ]]:
247277 """Return the OpenSlide.read_region() arguments for the specified tile.
248278
249279 Most users should call get_tile() rather than calling
@@ -254,15 +284,17 @@ def get_tile_coordinates(self, level, address):
254284 tuple."""
255285 return self ._get_tile_info (level , address )[0 ]
256286
257- def get_tile_dimensions (self , level , address ):
287+ def get_tile_dimensions (
288+ self , level : int , address : tuple [int , int ]
289+ ) -> tuple [int , int ]:
258290 """Return a (pixels_x, pixels_y) tuple for the specified tile.
259291
260292 level: the Deep Zoom level.
261293 address: the address of the tile within the level as a (col, row)
262294 tuple."""
263295 return self ._get_tile_info (level , address )[1 ]
264296
265- def get_dzi (self , format ) :
297+ def get_dzi (self , format : str ) -> str :
266298 """Return a string containing the XML metadata for the .dzi file.
267299
268300 format: the format of the individual tiles ('png' or 'jpeg')"""
0 commit comments