Model Zoo

Choose a tile, slide, or patient encoder below. List installed presets without loading weights:

from slide2vec import list_models

list_models()           # all presets
list_models("tile")     # tile-level only
list_models("slide")    # slide-level only
list_models("patient")  # patient-level only

Tile-level encoders

Spacing values are supported scales, in microns per pixel. If a preset supports several scales and has no registered default, pass PreprocessingConfig(tiling=hs2p.TilingConfig(...)) explicitly for slide extraction.

Registry input_size is the default final model input size. Slide extraction reads tiles at requested_tile_size_px (default: input_size), applies only the encoder’s photometric preprocessing (dtype, scaling, normalization), and encodes exactly that size. No encoder-side resize or center crop follows the read. Lunit, mSTAR, GigaPath, GPFM and DINOv2 default to 224px; DINOv3 to 256px. Slide and patient presets inherit the default of their tile encoder.

This is slide2vec’s declared extraction policy, not a reproduction of each model’s published sampling protocol. Earlier releases read Lunit and mSTAR at 248px and GigaPath at 256px, then center-cropped to 224px, leaving unencoded margins between non-overlapping tiles. A 224px grid changes the tile count and coverage; embeddings from the two policies are not equivalent. resume refuses to reuse tile, hierarchical or slide embeddings whose metadata records a different requested_tile_size_px (see compatibility).

An off-default size requires allow_non_recommended_settings=True, an encoder that supports variable input, and a multiple of the patch size. The permission only allows the size; preprocessing stays geometry-preserving. For DINOv2 at its native checkpoint resolution:

model = Model.from_preset("dinov2-vitb14", allow_non_recommended_settings=True)
model.embed_slides(
    slides,
    preprocessing=PreprocessingConfig(
        tiling=TilingConfig(
            requested_spacing_um=0.5,
            requested_tile_size_px=518,
            tolerance=0.05,
            overlap=0.0,
        ),
    ),
)

This forwards exactly 518×518 pixels. Without the flag, an off-default request raises; 525px raises even with the flag (not a multiple of 14). With dinov3-vitb16, requested_tile_size_px=224 forwards 224×224 pixels through its 16px patch grid.

Pre-cropped images (embed_images, embed_tiles) keep each encoder’s shipped get_transform recipe: Lunit Resize 248 → CenterCrop 224, GigaPath and DINOv2 Resize 256 → CenterCrop 224, DINOv3 Resize 256 → CenterCrop 256, GPFM and mSTAR direct 224 resize. Dense extraction is unchanged.

Preset

Model

Output dim

Spacing (µm/px)

lunit

Lunit ViT-S/8

384

0.5

prost40m

Prost40M

384

0.5

conch

CONCH

512

0.5

dinov2-vitb14

DINOv2 ViT-B/14

768

any (0.5 default)

dinov3-vitb16

DINOv3 ViT-B/16

768

any (0.5 default)

phikon

Phikon

768

0.5

conchv15

CONCHv1.5

768

0.5

hibou-b

Hibou-B

768

0.5

h0-mini

H0-mini

768 / 1536

0.5

phikonv2

Phikon-v2

1024

0.5

phaet

Phaet

1024

0.5

hibou-l

Hibou-L

1024

0.5

mstar

mSTAR

1024

0.5

gpfm

GPFM

1024

0.5

uni

UNI

1024

0.5

isight

iSight

1024

0.5

musk

MUSK

1024 / 2048

0.25, 0.5, 1.0

virchow

Virchow

1280 / 2560

0.5

virchow2

Virchow2

1280 / 2560

0.25, 0.5, 1.0, 2.0

uni2

UNI2

1536

0.5

gigapath

GigaPath

1536

0.5

h-optimus-0

H-Optimus-0

1536

0.5

h-optimus-1

H-Optimus-1

1536

0.5

mettle

Mettle

1536 / 3072

0.5

rudolfv2

RudolfV 2

1536 / 3072

0.25, 0.5, 1.0, 2.0

rudolfv2-b

RudolfV 2-B

768 / 1536

0.25, 0.5, 1.0, 2.0

rudolfv2-s

RudolfV 2-S

384 / 768

0.25, 0.5, 1.0, 2.0

mascaret

Mascaret

1536

0.5

midnight

Midnight

3072

0.25, 0.5, 1.0, 2.0

genbio-pathfm

GenBio-PathFM

4608

0.5

Slash-separated dimensions denote output variants. For example, Virchow2 uses cls_patch_mean (2560 dimensions) by default; select its 1280-dimensional CLS vector with:

from slide2vec import Model

model = Model.from_preset("virchow2", output_variant="cls")

Natural-image baselines

dinov2-vitb14 and dinov3-vitb16 are natural-image DINO ViTs with no intrinsic micron spacing: any requested spacing is accepted and 0.5 is the tiling default. dinov3-vitb16 (patch 16, four register tokens, RoPE) defaults to 256px tiles. Its patch_mean output (default) is the mean of the spatial patch tokens after the final norm, excluding the CLS and register tokens, which is the pooled output the timm checkpoint ships; cls returns the CLS token. Both are 768-dimensional. Dense grids are H/16 × W/16 (14 × 14 at 224px, 16 × 16 at 256px) and attention maps apply the backbone’s rotary embeddings. dinov3-vitb16 requires timm>=1.0.20, which the base install provides. Weights are public under the DINOv3 license; see the model card.

Slide-level encoders

Preset

Model

Tile encoder

Spacing (µm/px)

Output dim

gigapath-slide

GigaPath

gigapath

0.5

768

titan

TITAN

conchv15

0.5

768

prism

PRISM

virchow

0.5

1280

prism2

PRISM2

virchow2

0.5

2560 (base, default); 3072 (output_variant="diagnostic")

moozy-slide

MOOZY

lunit

0.5

768

Patient-level encoders

Patient-level encoders aggregate multiple slide embeddings for the same patient into a single patient-level embedding. They require a patient_id column in the input manifest (or patient_id keys in each slide dict when using the Python API).

Preset

Model

Tile encoder

Spacing (µm/px)

Output dim

moozy

MOOZY

lunit

0.5

768

Installation extras

The base install (pip install slide2vec) covers most presets. Extras add the packages a few models need. They follow one rule:

  • Extras carry lower bounds only. An exact or upper pin is kept only when the model’s code breaks outside it.

  • A model whose stack conflicts with the shared one gets its own extra, and the conflict is declared in pyproject.toml.

slide2vec[fm] combines every model extra. The presets that need more than the base install are:

Preset

Extra

prism

prism (or fm)

prism2

prism2 (or fm) plus flash-attn

phaet, mascaret

waiv (or fm)

titan, conchv15

base, or titan

musk

fm (for fairscale) plus an upstream package (below)

gigapath-slide

fm (for fairscale), flash-attn or xformers, plus an upstream package (below)

conch

base plus an upstream package (below)

Every other preset needs only the base install.

prism2 imports FlashAttention, so it needs the flash-attn extra as well. Its text decoder calls a Phi-3 helper that transformers 4.53 removed; slide2vec supplies that helper on newer transformers, so prism2 shares the fm environment.

gigapath-slide needs an attention backend: the flash-attn extra on GPUs with compute capability above 7, and the xformers extra on older GPUs. PRISM uses xformers optionally, when PERCEIVER_MEM_EFF_ATTN=true. The xformers extra is unpinned because each xformers build pins its own torch.

The musk, conch, and gigapath-slide presets also require upstream packages that are not on PyPI. The tile-only gigapath preset uses timm and does not need the GigaPath package. Install the relevant package below:

pip install git+https://github.com/lilab-stanford/MUSK.git
pip install git+https://github.com/Mahmoodlab/CONCH.git
pip install git+https://github.com/prov-gigapath/prov-gigapath.git

From a checkout, uv sync --group models-git installs the same packages at the commits recorded in uv.lock. uv sync --extra cu128 --extra fm --group models-git reproduces the default Docker image.

Gated models require access approval on their linked Hugging Face page, plus hf auth login or an HF_TOKEN environment variable. The base install also includes hs2p’s sam2 dependencies for AtlasPatch tissue segmentation; see Preprocessing to enable it.

Dense grids and attention maps

Tile encoders can also return a spatial patch-token grid (B, d, h, w) instead of the pooled (B, D) vector, and most can return per-head CLS-attention grids (B, K, h, w). See API Guide for the dense and attention APIs.

Support varies by preset. Check it without loading weights:

from slide2vec.encoders import resolve_encoder_capabilities

capabilities = resolve_encoder_capabilities("uni2")
print(capabilities.level)       # "tile"
print(capabilities.pooled)      # True
print(capabilities.dense)       # True
print(capabilities.dense_prenorm)  # True (timm ViT presets only)
print(capabilities.attention)   # True
print(capabilities.patch_size)  # (14, 14)

Resolution reads the registry only: it does not instantiate the encoder, download files, or access the network. Slide and patient reports include tile_encoder and tile_encoder_output_variant so you can preflight the fixed tile dependency the same way.

Bring your own encoder

Any encoder can be a preset. Subclass TorchTileEncoder for a torch.nn.Module that maps (B, 3, H, W) to (B, D), or TimmTileEncoder for a timm model (dense grids and attention maps included), and register it with register_encoder(). Load weights in the constructor, never at import time.

import torch

from slide2vec import TorchTileEncoder, register_encoder


@register_encoder(
    "my-tile-model",
    encode_dim=768,
    input_size=224,
    supports_variable_input_size=False,
    supported_spacing_um=0.5,
    precision="fp16",
    source="/models/my-tile-model.ts",
)
class MyTileModel(TorchTileEncoder):
    def __init__(self, *, output_variant: str | None = None):
        model = torch.jit.load("/models/my-tile-model.ts", map_location="cpu")
        super().__init__(
            model,
            encode_dim=768,
            input_size=224,
            mean=(0.485, 0.456, 0.406),
            std=(0.229, 0.224, 0.225),
            output_variant=output_variant,
        )

mean and std are the model’s own normalization; check its model card rather than assuming ImageNet values. For a timm checkpoint, copy slide2vec/encoders/models/uni.py and change the model name, encode_dim, patch_size and spacing. Then make the module importable in one of two ways.

From a source checkout

Save the file as slide2vec/encoders/models/my_tile_model.py. Every module in that directory is imported on the first registry read (list_models(), a preset lookup, loading a model), so the preset appears in list_models(), works as model.name in YAML and on the CLI, and is rebuilt by every torchrun worker. Files whose name starts with an underscore are skipped. A file that fails to import makes that first read fail; the traceback names the file. Register at module scope, but call list_models() or read preset metadata only inside functions: a read while the directory is still importing is refused.

From an installed package

With pip install slide2vec or the Docker image, ship the module as its own distribution with a zero-argument provider in the slide2vec.encoders entry-point group. Installing it is enough: discovery is lazy, a provider that raises is skipped and reported in the “Available: …” error, and workers rediscover the preset by name.

my-slide2vec-encoders/
├── pyproject.toml
└── src/
    └── my_slide2vec_encoders/
        ├── __init__.py
        └── tile_model.py      # the class above
[build-system]
requires = ["setuptools>=61"]
build-backend = "setuptools.build_meta"

[project]
name = "my-slide2vec-encoders"
version = "0.1.0"
dependencies = ["slide2vec>=7.1", "torch", "torchvision"]

[project.entry-points."slide2vec.encoders"]
my_org = "my_slide2vec_encoders:register_encoders"

[tool.setuptools.packages.find]
where = ["src"]

src/my_slide2vec_encoders/__init__.py defines the provider, which imports the module so its decorator runs. The provider stays metadata-only: no weights, no network.

def register_encoders() -> None:
    from my_slide2vec_encoders import tile_model  # noqa: F401

Install and use it:

pip install ./my-slide2vec-encoders
from slide2vec import Model, list_models

assert "my-tile-model" in list_models()
model = Model.from_preset("my-tile-model")

For distributed extraction, install the distribution, and make its weights and credentials reachable, in the same way on every worker and node.