Converting images to OME-TIFF#
spatialrefinery.convert_to_ometiff converts a whole-slide or microscopy image into a pyramidal OME-TIFF: a tiled, multi-resolution TIFF that viewers (and SpatialData) can read efficiently at any zoom level without loading the full-resolution image into memory.
It dispatches on the input file’s suffix to a registered converter – OpenSlideImageConverter for .svs, .ndpi, .tif, .tiff, .mrxs, .scn, .bif, .vms, .svslide, and BioioImageConverter for .czi (which needs the optional czi extra). What you need: one whole-slide image file. Runtime: proportional to image size; pyramid generation is the dominant cost.
from pathlib import Path
from spatialrefinery import convert_to_ometiff
from spatialrefinery.core.registry import RegistryError, get_converter_for, list_converters
Registered converters#
list_converters() shows every suffix with a registered converter, and which class handles it:
list_converters()
{'.bif': 'OpenSlideImageConverter',
'.czi': 'BioioImageConverter',
'.mrxs': 'OpenSlideImageConverter',
'.ndpi': 'OpenSlideImageConverter',
'.scn': 'OpenSlideImageConverter',
'.svs': 'OpenSlideImageConverter',
'.svslide': 'OpenSlideImageConverter',
'.tif': 'OpenSlideImageConverter',
'.tiff': 'OpenSlideImageConverter',
'.vms': 'OpenSlideImageConverter'}
Converting a single file#
subresolutions controls how many pyramid levels are generated below full resolution (each level halves the resolution); tile_size is the TIFF tile edge length in pixels. The defaults (4 subresolutions, 1024px tiles) work well for typical whole-slide images.
SOURCE = Path("/Users/rushin.gindra/Documents/Research/SpatialProjects/Phoenix/datasets/svs_files/example.svs")
OUTPUT_DIR = Path("/Users/rushin.gindra/Documents/Research/SpatialProjects/Phoenix/datasets/ometiff_output/")
ometiff_paths = convert_to_ometiff(
source=SOURCE,
output_dir=OUTPUT_DIR,
subresolutions=4,
tile_size=1024,
overwrite=False,
)
ometiff_paths
INFO Writing pyramid level 0 (shape: (44928, 99840, 3))
INFO Writing pyramid level 1 (shape: (22464, 49920, 3))
INFO Writing pyramid level 2 (shape: (11232, 24960, 3))
INFO Writing pyramid level 3 (shape: (5616, 12480, 3))
INFO Writing pyramid level 4 (shape: (2808, 6240, 3))
[PosixPath('/Users/rushin.gindra/Documents/Research/SpatialProjects/Phoenix/datasets/ometiff_output/example.ome.tif')]
A source file can contain more than one scene/plane (common for .czi), in which case convert_to_ometiff writes one .ome.tif per scene and returns every path it wrote.
Batch converting a directory#
Discover every convertible file under a directory by matching the registered suffixes, then convert each one – skipping (rather than aborting on) files with no registered converter or that fail to convert:
def find_convertible_files(input_path: Path) -> list[Path]:
"""Return every file under `input_path` with a registered converter suffix."""
suffixes = list_converters().keys()
files = []
for suffix in suffixes:
files.extend(input_path.glob(f"*{suffix}"))
files.extend(input_path.glob(f"*{suffix.upper()}"))
return sorted(set(files))
WSI_DIR = Path("wsi_dir")
all_outputs: list[Path] = []
for file in find_convertible_files(WSI_DIR):
try:
get_converter_for(file) # fail fast with a clear message before doing any work
except RegistryError as e:
print(f"Skipping {file}: {e}")
continue
try:
all_outputs.extend(convert_to_ometiff(file, OUTPUT_DIR, overwrite=False))
except Exception as e: # noqa: BLE001 - one file's failure must not abort the batch
print(f"Failed to convert {file}: {e}")
print(f"Created {len(all_outputs)} OME-TIFF file(s)")
Created 0 OME-TIFF file(s)
Handling unsupported files#
get_converter_for (and therefore convert_to_ometiff) raises RegistryError for two cases: a suffix with no registered converter, and a source that is itself already an .ome.tif/.ome.tiff – re-running a conversion over a directory that already contains outputs will not re-ingest them as inputs.
try:
convert_to_ometiff(Path("unsupported.xyz"), OUTPUT_DIR)
except RegistryError as e:
print(e)
"No converter registered for suffix '.xyz'. Known suffixes: .bif, .czi, .mrxs, .ndpi, .scn, .svs, .svslide, .tif, .tiff, .vms"
CZI support#
.czi files require the optional czi extra:
pip install "spatialrefinery[czi]"
Without it, .czi sources raise RegistryError just like any other unregistered suffix.
Running this as a script#
python scripts/convert_to_ometiff.py --input_path wsi_dir/ --output_dir ometiff_output/ -p 4
What’s next#
That’s the full pipeline – see the API reference for the complete parameter list of each function used across these tutorials.