← Về thư mục
📄 / / profiles / r-and-d / raw_evidence / scientific-agent-skills / skills / pathml / references / image_loading.md

Image loading, formats, levels, and coordinates

This reference targets the PyPI-stable PathML 3.0.5 API. All sources were checked on 2026-07-23 against the v3.0.5 tag and stable ReadTheDocs build.

Start with a local, de-identified file

Never infer authorization from the fact that a file is readable. Whole-slide images and DICOM objects can carry identifiers in pixels, labels, filenames, and metadata. Keep the original on approved storage, use a pseudonymous working name, and do not print arbitrary metadata. The bundled inspector emits only an allowlist of technical fields:

python scripts/slide_manifest.py inspect \
  --slide data/pseudonymous_slide.svs \
  --root .

It rejects URLs and symlinks. PathML itself accepts paths more broadly, so validate before constructing a slide object.

Stable slide classes

from pathml.core import (
    CODEXSlide,
    HESlide,
    IHCSlide,
    MultiparametricSlide,
    SlideData,
    SlideDataset,
    VectraSlide,
    types,
)

Convenience classes pass a stable SlideType:

The generic constructor is:

slide = SlideData(
    "data/pseudonymous_slide.svs",
    name="slide-001",
    backend="openslide",
    slide_type=types.HE,
)

SlideData.from_slide(), read_region(), level_dimensions, and level_downsamples are not stable SlideData APIs. Use the constructor, extract_region(), shape, and backend-specific objects where necessary.

For a local cohort, instantiate slides first:

from pathlib import Path
from pathml.core import HESlide, SlideDataset

root = Path("data/slides")
paths = sorted(root.glob("*.svs"))
slides = [HESlide(path, backend="openslide", name=path.stem) for path in paths]
dataset = SlideDataset(slides)

Do not recursively accept arbitrary user-controlled paths. Validate a manifest, freeze the patient split, and then build this list.

Backends and file types

OpenSlide

Use backend="openslide" for common brightfield pyramid formats. Stable PathML lists:

.svs, .tif, .tiff, .bif, .ndpi, .vms, .vmu, .scn, .mrxs, and .svslide.

The complete capability depends on the installed OpenSlide build and the vendor subtype, not only the suffix. Some generic TIFFs are not valid WSIs, and some files with a supported suffix use unsupported compression.

Native OpenSlide is required. Official PathML guidance uses openslide-tools on Debian/Ubuntu, Homebrew openslide on macOS, and vcpkg or official prebuilt binaries on Windows.

Bio-Formats

Use backend="bioformats" for multidimensional microscopy, OME-TIFF, QPTIFF, and formats OpenSlide cannot read. Bio-Formats supports a large catalogue (the upstream examples describe 160+ formats), including .ome.tif, .ome.tiff, .qptiff, .czi, .vsi, .zvi, and many laboratory formats.

This backend requires Java, python-bioformats, and python-javabridge. It starts a JVM and stable source configures a large maximum heap, so isolate and resource-limit untrusted images. Java has an approximately 2 GB array limit in the backend. A listed extension is not proof that every variant loads.

Bio-Formats returns five-dimensional arrays in PathML order:

(i, j, z, channel, time) = (row, column, z, c, t).

Even singleton z and time dimensions are retained until a transform such as CollapseRunsCODEX or CollapseRunsVectra changes the layout.

DICOM

Use backend="dicom" for .dcm or .dicom. Stable PathML treats DICOM frames as tiles. DICOM metadata is especially likely to contain PHI; de-identify with an approved DICOM process before PathML, preserve required UIDs consistently, and never dump the full dataset to logs.

h5path

.h5 and .h5path inputs are inferred as PathML's processed HDF5 format:

from pathml.core import SlideData

processed = SlideData("derived/slide-001.h5path")

There is no stable from_hdf5() constructor. See data_management.md.

Backend inference versus explicit selection

If backend=None, PathML infers a backend from the suffix. Prefer an explicit backend in reproducible work:

from pathml.core import HESlide

slide = HESlide("data/slide-001.svs", backend="openslide")

Reasons to be explicit:

Shape, regions, and tile generation

slide.shape returns (height, width) for the backend's default level.

height, width = slide.shape

region = slide.extract_region(
    location=(2_000, 3_000),  # (i, j) = (row, column)
    size=(512, 768),          # (height, width)
    level=1,
)

tiles = slide.generate_tiles(
    shape=(512, 512),
    stride=(256, 256),
    pad=False,
    level=1,
)

generate_tiles() is lazy. Do not materialize all tiles just to count them. Use the bounded planner first:

python scripts/plan_pipeline.py \
  --width 100000 --height 80000 \
  --tile-size 512 --stride 256 \
  --level-downsample 4

SlideData.run() uses different parameter names: tile_size, tile_stride, tile_pad, and level.

Coordinate convention

PathML's Tile.coords is the top-left (i, j):

For OpenSlide, stable PathML multiplies (i, j) by that level's downsample and swaps the order before calling OpenSlide's level-0 (x, y) API. Therefore:

row_level0 = i_level * downsample_level
col_level0 = j_level * downsample_level
y_um = row_level0 * mpp_y
x_um = col_level0 * mpp_x

Use scanner-provided level-0 MPP when reliable. Do not silently derive MPP from objective power. Record:

QuantifyMIF later writes obsm["spatial"] in (x, y) order, so a conversion is required when joining it to Tile.coords.

Pyramid levels

For OpenSlide, level 0 is highest resolution. Later levels are downsampled, but the factors are slide-specific; do not assume 4x, 16x, or a particular magnification sequence.

Backend-level inspection:

level_count = slide.slide.level_count
level0_shape = slide.slide.get_image_shape(level=0)  # (height, width)

# OpenSlide-specific internals, not a backend-neutral PathML contract:
downsamples = tuple(slide.slide.slide.level_downsamples)
dimensions_xy = tuple(slide.slide.slide.level_dimensions)

Guard backend-specific access and record it as such. Bio-Formats maps image series to levels; those series are not necessarily an optical pyramid.

Tile count and edge behavior

For one dimension D, tile extent T, and stride S, pad=False yields:

0                         if D < T
floor((D - T) / S) + 1    otherwise

With pad=True, stable PathML follows its backend implementation, which is not identical to a generic ceil(D / S) rule for every overlapping configuration. Use the bundled planner and verify a small synthetic case. Padded pixels are zero, which can bias tissue/stain/QC transforms.

Important stable limitation: SlideData.generate_tiles() does not slice slide-level masks into padded tiles. Do not combine a slide-level mask with pad=True without an explicit, tested padding policy.

Technical metadata without PHI leakage

PathML 3.0.5 has no backend-neutral slide.metadata mapping. Technical metadata is backend-specific:

Default to a strict allowlist such as:

Do not emit patient name/ID, accession, dates, institution, free text, UIDs, or file paths. Even technical fields can be identifying in a small cohort; minimize what is retained.

Loading/QC checklist

Before large-scale processing:

  1. Validate suffix, regular-file status, symlinks, size, and manifest uniqueness.
  2. Confirm backend and native dependencies with a non-sensitive test slide.
  3. Read a thumbnail or a few bounded regions, not the full level-0 image.
  4. Confirm color/channel order, dtype, level count, dimensions, and MPP.
  5. Check orientation, blank areas, focus, folds, pen, bubbles, coverslip edges, clipping, and scanner artifacts.
  6. Confirm tile coordinates by overlaying a few sampled tiles on a thumbnail.
  7. Record failures instead of silently dropping slides.

Sources, accessed 2026-07-23