0
# Deep Zoom
1
2
Generate Deep Zoom tiles and metadata from OpenSlide objects for web-based slide viewing. Deep Zoom provides smooth zooming and panning by creating a pyramid of pre-rendered tiles at multiple resolution levels, enabling efficient web-based visualization of large whole-slide images.
3
4
```python
5
import openslide
6
from openslide.deepzoom import DeepZoomGenerator
7
from openslide import AbstractSlide
8
from PIL import Image
9
```
10
11
## Capabilities
12
13
### Deep Zoom Generator
14
15
Create Deep Zoom tile generators that convert whole-slide images into web-compatible tile pyramids.
16
17
```python { .api }
18
from openslide.deepzoom import DeepZoomGenerator
19
import openslide
20
21
class DeepZoomGenerator:
22
"""
23
Generates Deep Zoom tiles and metadata from slide objects.
24
25
Creates a pyramid of tiles at multiple resolution levels for efficient
26
web-based viewing with smooth zooming and panning.
27
"""
28
29
def __init__(self,
30
osr: openslide.AbstractSlide,
31
tile_size: int = 254,
32
overlap: int = 1,
33
limit_bounds: bool = False):
34
"""
35
Create a DeepZoomGenerator for a slide.
36
37
Args:
38
osr: Slide object (OpenSlide, ImageSlide, or AbstractSlide)
39
tile_size: Width and height of individual tiles. For best performance,
40
tile_size + 2 * overlap should be a power of two (default: 254)
41
overlap: Number of extra pixels to add to each interior edge of tiles
42
for seamless viewing (default: 1)
43
limit_bounds: If True, only render the non-empty slide region based
44
on bounds properties (default: False)
45
"""
46
47
BOUNDS_OFFSET_PROPS = (
48
openslide.PROPERTY_NAME_BOUNDS_X,
49
openslide.PROPERTY_NAME_BOUNDS_Y,
50
)
51
BOUNDS_SIZE_PROPS = (
52
openslide.PROPERTY_NAME_BOUNDS_WIDTH,
53
openslide.PROPERTY_NAME_BOUNDS_HEIGHT,
54
)
55
```
56
57
### Deep Zoom Properties
58
59
Access information about the Deep Zoom pyramid structure and tile organization.
60
61
```python { .api }
62
class DeepZoomGenerator:
63
@property
64
def level_count(self) -> int:
65
"""
66
Number of Deep Zoom levels in the image.
67
68
Level 0 is the smallest (most zoomed out), highest level is largest.
69
"""
70
71
@property
72
def level_tiles(self) -> tuple[tuple[int, int], ...]:
73
"""
74
Number of tiles in each Deep Zoom level.
75
76
Returns tuple of (tiles_x, tiles_y) for each level.
77
level_tiles[n] contains tile counts for level n.
78
"""
79
80
@property
81
def level_dimensions(self) -> tuple[tuple[int, int], ...]:
82
"""
83
Pixel dimensions of each Deep Zoom level.
84
85
Returns tuple of (pixels_x, pixels_y) for each level.
86
level_dimensions[n] contains pixel dimensions for level n.
87
"""
88
89
@property
90
def tile_count(self) -> int:
91
"""
92
Total number of tiles across all Deep Zoom levels.
93
"""
94
```
95
96
### Tile Generation
97
98
Generate individual tiles and tile coordinates for Deep Zoom viewing.
99
100
```python { .api }
101
class DeepZoomGenerator:
102
def get_tile(self, level: int, address: tuple[int, int]) -> Image.Image:
103
"""
104
Generate a tile image for the specified level and address.
105
106
Args:
107
level: Deep Zoom level number (0 = most zoomed out)
108
address: Tile address as (col, row) tuple within the level
109
110
Returns:
111
PIL.Image in RGB format containing the tile
112
113
Raises:
114
ValueError: If level or address is invalid
115
"""
116
117
def get_tile_coordinates(self, level: int, address: tuple[int, int]) -> tuple[tuple[int, int], int, tuple[int, int]]:
118
"""
119
Get OpenSlide read_region() arguments for a tile.
120
121
Most users should use get_tile() instead of calling read_region() directly.
122
123
Args:
124
level: Deep Zoom level number
125
address: Tile address as (col, row) tuple
126
127
Returns:
128
Tuple of (location, slide_level, size) for OpenSlide.read_region()
129
"""
130
131
def get_tile_dimensions(self, level: int, address: tuple[int, int]) -> tuple[int, int]:
132
"""
133
Get pixel dimensions of a specific tile.
134
135
Args:
136
level: Deep Zoom level number
137
address: Tile address as (col, row) tuple
138
139
Returns:
140
(pixels_x, pixels_y) tuple for the tile
141
"""
142
```
143
144
### DZI Metadata Generation
145
146
Generate Deep Zoom Image (DZI) XML metadata files for web viewers.
147
148
```python { .api }
149
class DeepZoomGenerator:
150
def get_dzi(self, format: str) -> str:
151
"""
152
Generate XML metadata for the Deep Zoom Image (.dzi) file.
153
154
Args:
155
format: Image format for tiles ('png' or 'jpeg')
156
157
Returns:
158
XML string containing DZI metadata
159
"""
160
```
161
162
## Usage Examples
163
164
### Basic Deep Zoom Generation
165
166
```python
167
import openslide
168
from openslide.deepzoom import DeepZoomGenerator
169
170
# Open a slide
171
with openslide.open_slide("slide.svs") as slide:
172
# Create Deep Zoom generator
173
dz = DeepZoomGenerator(slide, tile_size=254, overlap=1)
174
175
print(f"Deep Zoom levels: {dz.level_count}")
176
print(f"Total tiles: {dz.tile_count}")
177
178
# Generate DZI metadata
179
dzi_xml = dz.get_dzi('jpeg')
180
with open('slide.dzi', 'w') as f:
181
f.write(dzi_xml)
182
183
# Generate some tiles
184
for level in range(dz.level_count):
185
tiles_x, tiles_y = dz.level_tiles[level]
186
print(f"Level {level}: {tiles_x}x{tiles_y} tiles")
187
188
# Generate first tile of each level
189
if tiles_x > 0 and tiles_y > 0:
190
tile = dz.get_tile(level, (0, 0))
191
tile.save(f'tile_{level}_0_0.jpg')
192
```
193
194
### Complete Tile Pyramid Generation
195
196
```python
197
import os
198
import openslide
199
from openslide.deepzoom import DeepZoomGenerator
200
201
def generate_tiles(slide_path, output_dir, tile_format='jpeg'):
202
"""Generate complete Deep Zoom tile pyramid for a slide."""
203
204
with openslide.open_slide(slide_path) as slide:
205
# Create Deep Zoom generator
206
dz = DeepZoomGenerator(slide, tile_size=254, overlap=1)
207
208
# Create output directory
209
os.makedirs(output_dir, exist_ok=True)
210
211
# Save DZI metadata
212
dzi_path = os.path.join(output_dir, 'slide.dzi')
213
with open(dzi_path, 'w') as f:
214
f.write(dz.get_dzi(tile_format))
215
216
# Create tiles directory
217
tiles_dir = os.path.join(output_dir, 'slide_files')
218
os.makedirs(tiles_dir, exist_ok=True)
219
220
# Generate all tiles
221
for level in range(dz.level_count):
222
level_dir = os.path.join(tiles_dir, str(level))
223
os.makedirs(level_dir, exist_ok=True)
224
225
tiles_x, tiles_y = dz.level_tiles[level]
226
print(f"Generating level {level}: {tiles_x}x{tiles_y} tiles")
227
228
for col in range(tiles_x):
229
for row in range(tiles_y):
230
tile = dz.get_tile(level, (col, row))
231
tile_path = os.path.join(level_dir, f'{col}_{row}.{tile_format}')
232
233
if tile_format == 'jpeg':
234
tile.save(tile_path, 'JPEG', quality=90)
235
else:
236
tile.save(tile_path, 'PNG')
237
238
# Usage
239
generate_tiles('slide.svs', 'output_tiles', 'jpeg')
240
```
241
242
### Bounds-Limited Deep Zoom
243
244
```python
245
import openslide
246
from openslide.deepzoom import DeepZoomGenerator
247
248
# Open a slide
249
with openslide.open_slide("slide.svs") as slide:
250
# Check if slide has bounds information
251
bounds_props = [
252
openslide.PROPERTY_NAME_BOUNDS_X,
253
openslide.PROPERTY_NAME_BOUNDS_Y,
254
openslide.PROPERTY_NAME_BOUNDS_WIDTH,
255
openslide.PROPERTY_NAME_BOUNDS_HEIGHT
256
]
257
258
has_bounds = all(prop in slide.properties for prop in bounds_props)
259
260
if has_bounds:
261
print("Slide has bounds information - using limited bounds")
262
# Create generator with bounds limiting
263
dz = DeepZoomGenerator(slide, limit_bounds=True)
264
265
# Show bounds information
266
bounds_x = int(slide.properties[openslide.PROPERTY_NAME_BOUNDS_X])
267
bounds_y = int(slide.properties[openslide.PROPERTY_NAME_BOUNDS_Y])
268
bounds_w = int(slide.properties[openslide.PROPERTY_NAME_BOUNDS_WIDTH])
269
bounds_h = int(slide.properties[openslide.PROPERTY_NAME_BOUNDS_HEIGHT])
270
271
print(f"Bounds: ({bounds_x}, {bounds_y}) {bounds_w}x{bounds_h}")
272
print(f"Original dimensions: {slide.dimensions}")
273
print(f"Deep Zoom dimensions: {dz.level_dimensions[-1]}")
274
else:
275
print("No bounds information - using full slide")
276
dz = DeepZoomGenerator(slide, limit_bounds=False)
277
278
# Generate some tiles
279
highest_level = dz.level_count - 1
280
tile = dz.get_tile(highest_level, (0, 0))
281
tile.save('bounds_tile.jpg')
282
```
283
284
### Custom Tile Sizes and Overlap
285
286
```python
287
import openslide
288
from openslide.deepzoom import DeepZoomGenerator
289
290
with openslide.open_slide("slide.svs") as slide:
291
# Different configurations for different use cases
292
293
# High quality with larger tiles (good for detailed viewing)
294
dz_hq = DeepZoomGenerator(slide, tile_size=510, overlap=2)
295
296
# Fast loading with smaller tiles (good for overview)
297
dz_fast = DeepZoomGenerator(slide, tile_size=126, overlap=1)
298
299
# Standard configuration (tile_size + 2*overlap = 256, power of 2)
300
dz_standard = DeepZoomGenerator(slide, tile_size=254, overlap=1)
301
302
print("Configuration comparison:")
303
print(f"High quality: {dz_hq.tile_count} tiles")
304
print(f"Fast loading: {dz_fast.tile_count} tiles")
305
print(f"Standard: {dz_standard.tile_count} tiles")
306
307
# Generate sample tiles with different configurations
308
for name, dz in [('hq', dz_hq), ('fast', dz_fast), ('standard', dz_standard)]:
309
if dz.level_count > 0:
310
mid_level = dz.level_count // 2
311
tiles_x, tiles_y = dz.level_tiles[mid_level]
312
if tiles_x > 0 and tiles_y > 0:
313
tile = dz.get_tile(mid_level, (0, 0))
314
tile.save(f'sample_{name}.jpg')
315
```
316
317
### Web Viewer Integration
318
319
```python
320
import json
321
import openslide
322
from openslide.deepzoom import DeepZoomGenerator
323
324
def create_viewer_config(slide_path, output_path):
325
"""Create configuration for OpenSeadragon web viewer."""
326
327
with openslide.open_slide(slide_path) as slide:
328
dz = DeepZoomGenerator(slide, tile_size=254, overlap=1)
329
330
# Extract slide information
331
config = {
332
'slide_info': {
333
'dimensions': slide.dimensions,
334
'level_count': slide.level_count,
335
'level_dimensions': slide.level_dimensions,
336
'properties': dict(slide.properties),
337
'associated_images': list(slide.associated_images.keys())
338
},
339
'deepzoom_info': {
340
'level_count': dz.level_count,
341
'level_dimensions': dz.level_dimensions,
342
'level_tiles': dz.level_tiles,
343
'tile_count': dz.tile_count,
344
'tile_size': 254,
345
'overlap': 1
346
},
347
'viewer_config': {
348
'tileSources': 'slide.dzi',
349
'prefixUrl': 'openseadragon/images/',
350
'showNavigator': True,
351
'showRotationControl': True,
352
'showHomeControl': True,
353
'showZoomControl': True,
354
'showFullPageControl': True
355
}
356
}
357
358
# Save configuration
359
with open(output_path, 'w') as f:
360
json.dump(config, f, indent=2)
361
362
return config
363
364
# Create viewer configuration
365
config = create_viewer_config('slide.svs', 'viewer_config.json')
366
print(f"Created viewer config for {config['slide_info']['dimensions']} slide")
367
```