CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-openslide-python

Python interface to OpenSlide for reading whole-slide images used in digital pathology

Pending
Overview
Eval results
Files

deep-zoom.mddocs/

Deep Zoom

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.

import openslide
from openslide.deepzoom import DeepZoomGenerator
from openslide import AbstractSlide
from PIL import Image

Capabilities

Deep Zoom Generator

Create Deep Zoom tile generators that convert whole-slide images into web-compatible tile pyramids.

from openslide.deepzoom import DeepZoomGenerator
import openslide

class DeepZoomGenerator:
    """
    Generates Deep Zoom tiles and metadata from slide objects.
    
    Creates a pyramid of tiles at multiple resolution levels for efficient
    web-based viewing with smooth zooming and panning.
    """
    
    def __init__(self, 
                 osr: openslide.AbstractSlide, 
                 tile_size: int = 254, 
                 overlap: int = 1, 
                 limit_bounds: bool = False):
        """
        Create a DeepZoomGenerator for a slide.
        
        Args:
            osr: Slide object (OpenSlide, ImageSlide, or AbstractSlide)
            tile_size: Width and height of individual tiles. For best performance,
                      tile_size + 2 * overlap should be a power of two (default: 254)
            overlap: Number of extra pixels to add to each interior edge of tiles
                    for seamless viewing (default: 1)
            limit_bounds: If True, only render the non-empty slide region based
                         on bounds properties (default: False)
        """
    
    BOUNDS_OFFSET_PROPS = (
        openslide.PROPERTY_NAME_BOUNDS_X,
        openslide.PROPERTY_NAME_BOUNDS_Y,
    )
    BOUNDS_SIZE_PROPS = (
        openslide.PROPERTY_NAME_BOUNDS_WIDTH,
        openslide.PROPERTY_NAME_BOUNDS_HEIGHT,
    )

Deep Zoom Properties

Access information about the Deep Zoom pyramid structure and tile organization.

class DeepZoomGenerator:
    @property
    def level_count(self) -> int:
        """
        Number of Deep Zoom levels in the image.
        
        Level 0 is the smallest (most zoomed out), highest level is largest.
        """

    @property
    def level_tiles(self) -> tuple[tuple[int, int], ...]:
        """
        Number of tiles in each Deep Zoom level.
        
        Returns tuple of (tiles_x, tiles_y) for each level.
        level_tiles[n] contains tile counts for level n.
        """

    @property
    def level_dimensions(self) -> tuple[tuple[int, int], ...]:
        """
        Pixel dimensions of each Deep Zoom level.
        
        Returns tuple of (pixels_x, pixels_y) for each level.
        level_dimensions[n] contains pixel dimensions for level n.
        """

    @property
    def tile_count(self) -> int:
        """
        Total number of tiles across all Deep Zoom levels.
        """

Tile Generation

Generate individual tiles and tile coordinates for Deep Zoom viewing.

class DeepZoomGenerator:
    def get_tile(self, level: int, address: tuple[int, int]) -> Image.Image:
        """
        Generate a tile image for the specified level and address.
        
        Args:
            level: Deep Zoom level number (0 = most zoomed out)
            address: Tile address as (col, row) tuple within the level
            
        Returns:
            PIL.Image in RGB format containing the tile
            
        Raises:
            ValueError: If level or address is invalid
        """

    def get_tile_coordinates(self, level: int, address: tuple[int, int]) -> tuple[tuple[int, int], int, tuple[int, int]]:
        """
        Get OpenSlide read_region() arguments for a tile.
        
        Most users should use get_tile() instead of calling read_region() directly.
        
        Args:
            level: Deep Zoom level number
            address: Tile address as (col, row) tuple
            
        Returns:
            Tuple of (location, slide_level, size) for OpenSlide.read_region()
        """

    def get_tile_dimensions(self, level: int, address: tuple[int, int]) -> tuple[int, int]:
        """
        Get pixel dimensions of a specific tile.
        
        Args:
            level: Deep Zoom level number
            address: Tile address as (col, row) tuple
            
        Returns:
            (pixels_x, pixels_y) tuple for the tile
        """

DZI Metadata Generation

Generate Deep Zoom Image (DZI) XML metadata files for web viewers.

class DeepZoomGenerator:
    def get_dzi(self, format: str) -> str:
        """
        Generate XML metadata for the Deep Zoom Image (.dzi) file.
        
        Args:
            format: Image format for tiles ('png' or 'jpeg')
            
        Returns:
            XML string containing DZI metadata
        """

Usage Examples

Basic Deep Zoom Generation

import openslide
from openslide.deepzoom import DeepZoomGenerator

# Open a slide
with openslide.open_slide("slide.svs") as slide:
    # Create Deep Zoom generator
    dz = DeepZoomGenerator(slide, tile_size=254, overlap=1)
    
    print(f"Deep Zoom levels: {dz.level_count}")
    print(f"Total tiles: {dz.tile_count}")
    
    # Generate DZI metadata
    dzi_xml = dz.get_dzi('jpeg')
    with open('slide.dzi', 'w') as f:
        f.write(dzi_xml)
    
    # Generate some tiles
    for level in range(dz.level_count):
        tiles_x, tiles_y = dz.level_tiles[level]
        print(f"Level {level}: {tiles_x}x{tiles_y} tiles")
        
        # Generate first tile of each level
        if tiles_x > 0 and tiles_y > 0:
            tile = dz.get_tile(level, (0, 0))
            tile.save(f'tile_{level}_0_0.jpg')

Complete Tile Pyramid Generation

import os
import openslide
from openslide.deepzoom import DeepZoomGenerator

def generate_tiles(slide_path, output_dir, tile_format='jpeg'):
    """Generate complete Deep Zoom tile pyramid for a slide."""
    
    with openslide.open_slide(slide_path) as slide:
        # Create Deep Zoom generator
        dz = DeepZoomGenerator(slide, tile_size=254, overlap=1)
        
        # Create output directory
        os.makedirs(output_dir, exist_ok=True)
        
        # Save DZI metadata
        dzi_path = os.path.join(output_dir, 'slide.dzi')
        with open(dzi_path, 'w') as f:
            f.write(dz.get_dzi(tile_format))
        
        # Create tiles directory
        tiles_dir = os.path.join(output_dir, 'slide_files')
        os.makedirs(tiles_dir, exist_ok=True)
        
        # Generate all tiles
        for level in range(dz.level_count):
            level_dir = os.path.join(tiles_dir, str(level))
            os.makedirs(level_dir, exist_ok=True)
            
            tiles_x, tiles_y = dz.level_tiles[level]
            print(f"Generating level {level}: {tiles_x}x{tiles_y} tiles")
            
            for col in range(tiles_x):
                for row in range(tiles_y):
                    tile = dz.get_tile(level, (col, row))
                    tile_path = os.path.join(level_dir, f'{col}_{row}.{tile_format}')
                    
                    if tile_format == 'jpeg':
                        tile.save(tile_path, 'JPEG', quality=90)
                    else:
                        tile.save(tile_path, 'PNG')

# Usage
generate_tiles('slide.svs', 'output_tiles', 'jpeg')

Bounds-Limited Deep Zoom

import openslide
from openslide.deepzoom import DeepZoomGenerator

# Open a slide
with openslide.open_slide("slide.svs") as slide:
    # Check if slide has bounds information
    bounds_props = [
        openslide.PROPERTY_NAME_BOUNDS_X,
        openslide.PROPERTY_NAME_BOUNDS_Y,
        openslide.PROPERTY_NAME_BOUNDS_WIDTH,
        openslide.PROPERTY_NAME_BOUNDS_HEIGHT
    ]
    
    has_bounds = all(prop in slide.properties for prop in bounds_props)
    
    if has_bounds:
        print("Slide has bounds information - using limited bounds")
        # Create generator with bounds limiting
        dz = DeepZoomGenerator(slide, limit_bounds=True)
        
        # Show bounds information
        bounds_x = int(slide.properties[openslide.PROPERTY_NAME_BOUNDS_X])
        bounds_y = int(slide.properties[openslide.PROPERTY_NAME_BOUNDS_Y])
        bounds_w = int(slide.properties[openslide.PROPERTY_NAME_BOUNDS_WIDTH])
        bounds_h = int(slide.properties[openslide.PROPERTY_NAME_BOUNDS_HEIGHT])
        
        print(f"Bounds: ({bounds_x}, {bounds_y}) {bounds_w}x{bounds_h}")
        print(f"Original dimensions: {slide.dimensions}")
        print(f"Deep Zoom dimensions: {dz.level_dimensions[-1]}")
    else:
        print("No bounds information - using full slide")
        dz = DeepZoomGenerator(slide, limit_bounds=False)
    
    # Generate some tiles
    highest_level = dz.level_count - 1
    tile = dz.get_tile(highest_level, (0, 0))
    tile.save('bounds_tile.jpg')

Custom Tile Sizes and Overlap

import openslide
from openslide.deepzoom import DeepZoomGenerator

with openslide.open_slide("slide.svs") as slide:
    # Different configurations for different use cases
    
    # High quality with larger tiles (good for detailed viewing)
    dz_hq = DeepZoomGenerator(slide, tile_size=510, overlap=2)
    
    # Fast loading with smaller tiles (good for overview)
    dz_fast = DeepZoomGenerator(slide, tile_size=126, overlap=1)
    
    # Standard configuration (tile_size + 2*overlap = 256, power of 2)
    dz_standard = DeepZoomGenerator(slide, tile_size=254, overlap=1)
    
    print("Configuration comparison:")
    print(f"High quality: {dz_hq.tile_count} tiles")
    print(f"Fast loading: {dz_fast.tile_count} tiles")
    print(f"Standard: {dz_standard.tile_count} tiles")
    
    # Generate sample tiles with different configurations
    for name, dz in [('hq', dz_hq), ('fast', dz_fast), ('standard', dz_standard)]:
        if dz.level_count > 0:
            mid_level = dz.level_count // 2
            tiles_x, tiles_y = dz.level_tiles[mid_level]
            if tiles_x > 0 and tiles_y > 0:
                tile = dz.get_tile(mid_level, (0, 0))
                tile.save(f'sample_{name}.jpg')

Web Viewer Integration

import json
import openslide
from openslide.deepzoom import DeepZoomGenerator

def create_viewer_config(slide_path, output_path):
    """Create configuration for OpenSeadragon web viewer."""
    
    with openslide.open_slide(slide_path) as slide:
        dz = DeepZoomGenerator(slide, tile_size=254, overlap=1)
        
        # Extract slide information
        config = {
            'slide_info': {
                'dimensions': slide.dimensions,
                'level_count': slide.level_count,
                'level_dimensions': slide.level_dimensions,
                'properties': dict(slide.properties),
                'associated_images': list(slide.associated_images.keys())
            },
            'deepzoom_info': {
                'level_count': dz.level_count,
                'level_dimensions': dz.level_dimensions,
                'level_tiles': dz.level_tiles,
                'tile_count': dz.tile_count,
                'tile_size': 254,
                'overlap': 1
            },
            'viewer_config': {
                'tileSources': 'slide.dzi',
                'prefixUrl': 'openseadragon/images/',
                'showNavigator': True,
                'showRotationControl': True,
                'showHomeControl': True,
                'showZoomControl': True,
                'showFullPageControl': True
            }
        }
        
        # Save configuration
        with open(output_path, 'w') as f:
            json.dump(config, f, indent=2)
        
        return config

# Create viewer configuration
config = create_viewer_config('slide.svs', 'viewer_config.json')
print(f"Created viewer config for {config['slide_info']['dimensions']} slide")

Install with Tessl CLI

npx tessl i tessl/pypi-openslide-python

docs

deep-zoom.md

index.md

low-level-api.md

slide-operations.md

tile.json