Slide reading#

Open a slide, find tissue and choose regions to predict. For a complete workflow, see the whole-slide guide.

Open a slide#

from cellseg_models_pytorch.wsi import SlideReader

reader = SlideReader("data/slide.svs", backend="OPENSLIDE")
level = reader.level_from_max_dimension(1024)
thumbnail = reader.read_level(level)

Choose a reader installed in your environment:

Backend

Required software

OPENSLIDE

OpenSlide Python bindings and native library

CUCIM

cuCIM; CUDA tile reads also need CuPy and a compatible GPU

BIOIO

BioIO and a reader plugin for your format

Use OpenSlide or cuCIM for this workflow. BioIO’s whole-level reads currently need correction.

Choose tiles#

threshold, tissue_mask = reader.get_tissue_mask(level=level)
tiles = reader.get_tile_coordinates(
    width=256,
    tissue_mask=tissue_mask,
    overlap=0.25,
    out_of_bounds=False,
)
tile = reader.read_region(tiles[0], level=0)
preview = reader.get_annotated_thumbnail(thumbnail, tiles)
properties = tiles.get_properties(
    level=0, level_downsample=reader.level_downsamples[0]
)

This assumes at least one tissue tile is found. Pass tissue_mask=None to keep background tiles. Use a mask spanning the full slide; height and width may use different scales.

Dimensions use (height, width). Region bounds use (x, y, width, height) in full-resolution pixels, with (0, 0) at the top left. Reading another level reduces the returned image size; keep the region bounds in level-zero pixels.

pixel_size gives physical height and width per level-zero pixel, in micrometers. Reading pixel_size raises an error if this information is missing. You can still read tiles without it.

class SlideReader(path, backend='OPENSLIDE')#

Read slide images, find tissue and choose tiles for prediction.

Open a slide with the selected reader.

Parameters:
  • path (Union[str, Path]) – Path to a slide supported by the backend.

  • backend (str) – “OPENSLIDE”, “CUCIM” or “BIOIO”. Install the chosen reader and its native libraries or format plugins before opening the slide.

Raises:
  • FileNotFoundError – The slide path does not exist.

  • ValueError – The backend name or file suffix is unsupported.

  • ImportError – The selected reader is not installed.

property backend_name: str#

OPENSLIDE, CUCIM or BIOIO.

Type:

Active reader name

property data_bounds: tuple[int, int, int, int]#

Image-data bounds as (x, y, width, height) in level-zero pixels.

Some formats store tissue in a smaller area than the full image. Region coordinates still refer to the full image origin.

property dimensions: tuple[int, int]#

Full-resolution slide size as (height, width) in pixels.

get_annotated_thumbnail(image, coordinates, linewidth=1)#

Draw tile or spot rectangles on a slide overview.

Parameters:
  • image (ndarray) – RGB uint8 NumPy overview spanning the full slide.

  • coordinates (Sequence[tuple[int, int, int, int]] | TileCoordinates | SpotCoordinates) – Level-zero bounds, as a TileCoordinates, SpotCoordinates or a list of (x, y, width, height) tuples.

  • linewidth (int) – Rectangle line width in overview pixels.

Returns:

A new PIL image. Spot collections include names; tile collections highlight the first tile. Coordinates are scaled to the overview size.

Return type:

Image

get_spot_coordinates(tissue_mask, *, min_area_pixel=10, max_area_pixel=None, min_area_relative=0.2, max_area_relative=2.0)#

Find tissue microarray spots from a tissue mask.

Parameters:
  • tissue_mask (ndarray) – 2D uint8 full-slide mask; height and width may use different scales. Use 1 for tissue and 0 for background. Remove artifacts first.

  • min_area_pixel (int) – Smallest contour area in mask pixels.

  • max_area_pixel (int | None) – Largest contour area in mask pixels, or None for no limit.

  • min_area_relative (float) – Smallest area as a fraction of the median area after pixel-area filtering.

  • max_area_relative (float | None) – Largest area as a multiple of that median, or None.

Returns:

SpotCoordinates with level-zero bounds, corresponding spot_names and the cleaned mask at its original resolution. Bounds and names have matching order; names describe positions in the detected spot grid.

Return type:

SpotCoordinates

get_tile_coordinates(width, *, tissue_mask, height=None, overlap=0.0, max_background=0.95, out_of_bounds=True)#

Choose slide tiles, optionally excluding mostly empty regions.

Parameters:
  • width (int) – Tile width in level-zero pixels; no larger than the slide width.

  • tissue_mask (ndarray | None) – 2D tissue mask spanning the full slide. Height and width may use different scales. Pass None to disable background filtering.

  • height (int | None) – Tile height in level-zero pixels. None uses width.

  • overlap (float) – Fraction shared by neighboring tiles, in [0, 1).

  • max_background (float) – Largest allowed background fraction. Ignored without a tissue mask. Outside-slide pixels count as background.

  • out_of_bounds (bool) – Keep tiles extending beyond the slide; reads pad those areas.

Returns:

TileCoordinates with level-zero (x, y, width, height) bounds in row order. An all-background mask can leave the collection empty.

Raises:
  • TypeError – Tile dimensions are not integers.

  • ValueError – Tile dimensions are invalid or overlap is outside [0, 1).

Return type:

TileCoordinates

get_tissue_mask(*, level=None, threshold=None, multiplier=1.05, sigma=0.0)#

Find tissue on a slide image level.

Parameters:
  • level (int | None) – Level for detection. None chooses a level with level_from_max_dimension().

  • threshold (int | None) – Grayscale cutoff from 0 to 255. None chooses it automatically with Otsu’s method. Darker pixels are treated as tissue.

  • multiplier (float) – Scale the automatic cutoff; ignored with a fixed threshold.

  • sigma (float) – Blur strength in detection-image pixels; 0 disables blurring.

Returns:

1 is tissue and 0 is background. The first value is the cutoff, not the image level.

Return type:

(threshold, mask), with a 2D uint8 mask at the detection image size

Raises:

ValueError – The threshold or level is invalid.

property level_count: int#

Number of image levels, from full resolution to smaller images.

property level_dimensions: dict[int, tuple[int, int]]#

Dictionary mapping each level to its (height, width) in pixels.

property level_downsamples: dict[int, tuple[float, float]]#

Dictionary mapping each level to its height/width reduction factors.

For example, (2.0, 2.0) means half as many pixels along each axis.

level_from_dimensions(dimensions)#

Choose the level closest to an image size.

Parameters:

dimensions (tuple[int, int]) – Desired (height, width) in pixels.

Returns:

Level with the smallest total difference in height and width. Ties use the first matching level.

Return type:

int

level_from_max_dimension(max_dimension=4096)#

Choose a level small enough to fit within the requested size.

Parameters:

max_dimension (int) – Maximum height and width, in pixels.

Returns:

First level with both dimensions within the limit, or the smallest available level if none fits.

Return type:

int

property name: str#

Slide filename without its final extension.

property path: str#

Absolute slide file path.

property pixel_size: tuple[float, float]#

Level-zero pixel size as (height, width) in micrometers.

The two values can differ. They describe physical size, not level downsampling.

Raises:

ValueError – Calibration is missing, invalid or uses unsupported units.

read_level(level)#

Read a whole image level into CPU memory.

Parameters:

level (int) – Image level; 0 is full resolution and -1 is the smallest level.

Returns:

OpenSlide/CUCIM return an RGB uint8 NumPy image with height, width, channels axes. OpenSlide uses its thumbnail reader, which can return a slightly different image size from the level metadata. A full-resolution read can be large. BIOIO whole-level reading currently needs a separate correction.

Raises:

ValueError – The requested level is unavailable.

Return type:

ndarray

read_region(xywh, level=0, *, device=None)#

Read one rectangular region of the slide.

Parameters:
  • xywh (tuple[int, int, int, int]) – (x, y, width, height) bounds in level-zero pixels, even when reading another level. Out-of-image areas are padded with white pixels.

  • level (int) – Image level to read; 0 gives full resolution.

  • device (str | torch.device | None) – None returns a CPU NumPy array. A CUDA device is supported only by CUCIM and returns a tensor on that device.

Returns:

RGB uint8 pixels with height, width, channels axes. Width and height are reduced by the chosen level’s downsample factors.

Raises:

ValueError – The level or device request is unsupported.

Return type:

np.ndarray | torch.Tensor

property suffix: str#

Final file extension, including the dot.

Coordinate collections#

from cellseg_models_pytorch.wsi.data import SpotCoordinates, TileCoordinates

Index or iterate over either collection to get region bounds. Use tiles.coordinates when passing a list to WsiSegmenter.

class TileCoordinates(coordinates, width, height, overlap, max_background=None, tissue_mask=None)#

Tile bounds and the settings used to generate them.

Iterate or index this collection to get (x, y, width, height) tuples in level-zero slide pixels. Names and filtering settings are stored separately.

Parameters:
  • coordinates (list[tuple[int, int, int, int]]) – Tile bounds in reading order.

  • width (int) – Tile width in level-zero pixels.

  • height (int) – Tile height in level-zero pixels.

  • overlap (float) – Fraction shared by neighboring tiles, from 0 up to but excluding 1.

  • max_background (float | None) – Largest allowed background fraction, or None if not filtered.

  • tissue_mask (ndarray | None) – Optional 2D mask used for filtering: 1 is tissue, 0 background.

__getitem__(index)#

Return the tile bounds at an index; negative indices count from the end.

Parameters:

index (int)

Return type:

tuple[int, int, int, int]

__iter__()#

Iterate over tile bounds in their stored order.

Return type:

Iterator[tuple[int, int, int, int]]

__len__()#

Return the number of tile bounds.

Return type:

int

get_properties(level, level_downsample)#

Return the tile settings as a dictionary.

Parameters:
  • level (int) – Read level to record.

  • level_downsample (tuple[float, float]) – Height/width downsample factors to record for that level.

Returns:

Keys num_tiles, level, level_downsample, width, height, overlap and max_background. Width and height keep their level-zero values; this method records the supplied level metadata without changing coordinates.

Return type:

dict[str, int | float | tuple[float, float] | None]

cellseg_models_pytorch.wsi.data.TileCoordinates.coordinates#

Tile bounds in reading order, as level-zero (x, y, width, height) tuples.

cellseg_models_pytorch.wsi.data.TileCoordinates.width#

Tile width in level-zero pixels.

cellseg_models_pytorch.wsi.data.TileCoordinates.height#

Tile height in level-zero pixels.

cellseg_models_pytorch.wsi.data.TileCoordinates.overlap#

Fraction shared by neighboring tiles.

cellseg_models_pytorch.wsi.data.TileCoordinates.max_background#

Largest allowed background fraction, or None when filtering was disabled.

cellseg_models_pytorch.wsi.data.TileCoordinates.tissue_mask#

Mask used to choose tiles, or None.

For tissue microarrays, detect spots from a cleaned tissue mask:

spots = reader.get_spot_coordinates(tissue_mask)
named_spots = list(zip(spots.spot_names, spots))

Names and bounds have the same order. Iterating over spots returns bounds only.

class SpotCoordinates(coordinates, spot_names, tissue_mask)#

Detected tissue microarray spots and their names.

Iterate or index this collection to get (x, y, width, height) bounds in level-zero pixels. Iteration returns bounds only; pair them with spot_names when you also need names.

Parameters:
  • coordinates (list[tuple[int, int, int, int]]) – One bounding box per spot.

  • spot_names (list[str]) – Names in the same order as coordinates, such as “spot_1”.

  • tissue_mask (ndarray) – Cleaned 2D uint8 mask at the detection image size; 1 is tissue.

__getitem__(index)#

Return the spot bounds at an index; negative indices count from the end.

Parameters:

index (int)

Return type:

tuple[int, int, int, int]

__iter__()#

Iterate over spot bounds; names remain in spot_names.

Return type:

Iterator[tuple[int, int, int, int]]

__len__()#

Return the number of spots.

Return type:

int

cellseg_models_pytorch.wsi.data.SpotCoordinates.coordinates#

Spot bounds as a list of level-zero (x, y, width, height) tuples.

cellseg_models_pytorch.wsi.data.SpotCoordinates.spot_names#

Names in the same order as coordinates.

cellseg_models_pytorch.wsi.data.SpotCoordinates.tissue_mask#

Cleaned tissue mask at its detection resolution.