torch_em.model.vit

   1import math
   2from functools import partial
   3from collections import OrderedDict
   4from typing import Tuple, List, Optional
   5
   6import torch
   7import torch.nn as nn
   8import torch.nn.functional as F
   9
  10# we catch ImportErrors here because segment_anything, micro_sam, scale_mae and timm should
  11# only be optional dependencies for torch_em
  12try:
  13    from segment_anything.modeling import ImageEncoderViT
  14    _sam_import_success = True
  15except ImportError:
  16    ImageEncoderViT = object
  17    _sam_import_success = False
  18
  19try:
  20    from timm.models.vision_transformer import VisionTransformer, PatchEmbed
  21    _timm_import_success = True
  22except ImportError:
  23    VisionTransformer = object
  24    PatchEmbed = object
  25    _timm_import_success = False
  26
  27try:
  28    from sam2.modeling.backbones.hieradet import Hiera
  29    from sam2.modeling.position_encoding import PositionEmbeddingSine
  30    from sam2.modeling.backbones.image_encoder import ImageEncoder, FpnNeck
  31    _sam2_import_success = True
  32except ImportError:
  33    ImageEncoder = object
  34    _sam2_import_success = False
  35
  36try:
  37    from dinov2.layers.attention import Attention
  38    from dinov2.layers.block import NestedTensorBlock as Block
  39    from dinov2.layers.swiglu_ffn import SwiGLUFFN, SwiGLUFFNFused
  40    from dinov2.models.vision_transformer import DinoVisionTransformer as DinoV2VisionTransformer
  41    _dinov2_import_success = True
  42except ImportError:
  43    DinoV2VisionTransformer = object
  44    _dinov2_import_success = False
  45
  46try:
  47    from dinov3.models.vision_transformer import DinoVisionTransformer as DinoV3VisionTransformer
  48    _dinov3_import_success = True
  49except ImportError:
  50    DinoV3VisionTransformer = object
  51    _dinov3_import_success = False
  52
  53
  54try:
  55    from sam3.model.vitdet import ViT as SAM3ViT, get_abs_pos
  56    _sam3_import_success = True
  57except ImportError:
  58    SAM3ViT = object
  59    _sam3_import_success = False
  60
  61try:
  62    import torchvision.models as _tv_models
  63    _torchvision_import_success = True
  64except ImportError:
  65    _tv_models = None
  66    _torchvision_import_success = False
  67
  68from .util import VIT_CONFIGS
  69
  70
  71class ViT_Sam(ImageEncoderViT):
  72    """Vision Transformer derived from the Segment Anything Codebase (https://arxiv.org/abs/2304.02643).
  73
  74    Based on:
  75    https://github.com/facebookresearch/segment-anything/blob/main/segment_anything/modeling/image_encoder.py
  76
  77    Args:
  78        in_chans: The number of input channels.
  79        embed_dim: The embedding dimension, corresponding to the number of output channels of the vision transformer.
  80        global_attn_indexes: The global attention indices.
  81        apply_neck: Whether to apply the convolutional bottleneck after outputs of the last attention head.
  82        kwargs: Keyword arguments for the image encoder base class.
  83    """
  84    def __init__(
  85        self,
  86        in_chans: int = 3,
  87        embed_dim: int = 768,
  88        global_attn_indexes: Tuple[int, ...] = [2, 5, 8, 11],
  89        apply_neck: bool = False,
  90        **kwargs,
  91    ) -> None:
  92        if not _sam_import_success:
  93            raise RuntimeError(
  94                "The vision transformer backend can only be initialized if segment anything is installed. "
  95                "Please install segment anything from https://github.com/facebookresearch/segment-anything "
  96                "and then rerun your code."
  97            )
  98
  99        super().__init__(embed_dim=embed_dim, global_attn_indexes=global_attn_indexes, **kwargs)
 100        self.chunks_for_projection = global_attn_indexes
 101        self.in_chans = in_chans
 102        self.embed_dim = embed_dim
 103        self.apply_neck = apply_neck
 104
 105    def forward(self, x: torch.Tensor) -> torch.Tensor:
 106        """Apply the vision transformer to input data.
 107
 108        Args:
 109            x: The input data.
 110
 111        Returns:
 112            The vision transformer output.
 113        """
 114        x = self.patch_embed(x)
 115        if self.pos_embed is not None:
 116            x = x + self.pos_embed
 117
 118        list_from_encoder = []
 119        for i, blk in enumerate(self.blocks):
 120            x = blk(x)
 121            if i in self.chunks_for_projection:
 122                list_from_encoder.append(x)
 123
 124        x = x.permute(0, 3, 1, 2)
 125
 126        if self.apply_neck:
 127            x = self.neck(x)
 128
 129        list_from_encoder = [e.permute(0, 3, 1, 2) for e in list_from_encoder]
 130        return x, list_from_encoder[:3]
 131
 132
 133class ViT_CellposeSAM(nn.Module):
 134    """Vision Transformer derived from the CellposeSAM Codebase (https://doi.org/10.1038/s41592-025-02595-x).
 135
 136    This replicates CellposeSAM's actual initialization: instantiate SAM's ``ImageEncoderViT`` via
 137    ``sam_model_registry``, then modify the patch embedding, position embeddings, and set global attention.
 138    This preserves SAM's original relative position bias sizes, enabling direct checkpoint loading
 139    without any interpolation.
 140
 141    Based on: https://github.com/MouseLand/cellpose/blob/main/cellpose/vit_sam.py
 142
 143    NOTE: The pretrained CellposeSAM model uses ``vit_l`` exclusively.
 144
 145    Args:
 146        ps: The patch size (default for CellposeSAM is 8).
 147        bsize: The input image size (default for CellposeSAM is 256).
 148        apply_neck: Whether to apply the convolutional bottleneck after outputs of the last attention head.
 149    """
 150    def __init__(self, ps: int = 8, bsize: int = 256, apply_neck: bool = True) -> None:
 151        super().__init__()
 152
 153        if not _sam_import_success:
 154            raise RuntimeError(
 155                "The vision transformer backend can only be initialized if segment anything is installed. "
 156                "Please install segment anything from https://github.com/facebookresearch/segment-anything "
 157                "and then rerun your code."
 158            )
 159
 160        from segment_anything import sam_model_registry
 161
 162        # Creates the SAM vit_l encoder and applies CellposeSAM's modifications (same as cellpose.vit_sam.Transformer).
 163        encoder = sam_model_registry["vit_l"](None).image_encoder
 164
 165        w = encoder.patch_embed.proj.weight.detach()
 166        nchan = w.shape[0]
 167
 168        # CellPoseSAM changes the patch size from 16 to 'ps'.
 169        encoder.patch_embed.proj = nn.Conv2d(3, nchan, stride=ps, kernel_size=ps)
 170        encoder.patch_embed.proj.weight.data = w[:, :, ::16 // ps, ::16 // ps]
 171
 172        # Next, they subsample position embeddings for the new patch size and input resolution.
 173        ds = (1024 // 16) // (bsize // ps)
 174        encoder.pos_embed = nn.Parameter(encoder.pos_embed[:, ::ds, ::ds], requires_grad=True)
 175
 176        # Finally, they set all blocks to global attention.
 177        for blk in encoder.blocks:
 178            blk.window_size = 0
 179
 180        # Store encoder submodules directly ('state_dict' keys match CellposeSAM after prefix stripping).
 181        self.patch_embed = encoder.patch_embed
 182        self.pos_embed = encoder.pos_embed
 183        self.blocks = encoder.blocks
 184        self.neck = encoder.neck
 185
 186        # Additional attributes expected by UNETR.
 187        self.embed_dim = nchan
 188        self.img_size = bsize
 189        self.in_chans = 3
 190        self.apply_neck = apply_neck
 191
 192        # Feature extraction at evenly-spaced depths.
 193        depth = len(self.blocks)
 194        _chunks = depth // 4
 195        self.chunks_for_projection = [_chunks - 1, 2 * _chunks - 1, 3 * _chunks - 1, 4 * _chunks - 1]
 196
 197    def forward(self, x: torch.Tensor) -> torch.Tensor:
 198        """Apply the vision transformer to input data.
 199
 200        Args:
 201            x: The input data.
 202
 203        Returns:
 204            The vision transformer output.
 205        """
 206        x = self.patch_embed(x)
 207        if self.pos_embed is not None:
 208            x = x + self.pos_embed
 209
 210        list_from_encoder = []
 211        for i, blk in enumerate(self.blocks):
 212            x = blk(x)
 213            if i in self.chunks_for_projection:
 214                list_from_encoder.append(x)
 215
 216        x = x.permute(0, 3, 1, 2)
 217
 218        if self.apply_neck:
 219            x = self.neck(x)
 220
 221        list_from_encoder = [e.permute(0, 3, 1, 2) for e in list_from_encoder]
 222        return x, list_from_encoder[:3]
 223
 224
 225class ViT_MAE(VisionTransformer):
 226    """Vision Transformer derived from the Masked Auto Encoder Codebase (https://arxiv.org/abs/2111.06377).
 227
 228    Based on:
 229    https://github.com/facebookresearch/mae/blob/main/models_vit.py#L20-L53
 230
 231    Args:
 232        img_size: The size of the input for the image encoder. Input images will be resized to match this size.
 233        in_chans: The number of input channels.
 234        depth: The depth of the vision transformer.
 235        kwargs: Additional keyword arguments for the vision transformer base class.
 236    """
 237    def __init__(
 238        self,
 239        img_size: int = 1024,  # chosen to match our experiments with segment anything
 240        in_chans: int = 3,
 241        depth: int = 12,
 242        **kwargs
 243    ):
 244        if not _timm_import_success:
 245            raise RuntimeError(
 246                "The vision transformer backend can only be initialized if timm is installed. "
 247                "Please install timm (using conda/mamba) for using https://github.com/facebookresearch/mae/ "
 248                "and then rerun your code"
 249            )
 250        kwargs["num_classes"] = 0
 251        super().__init__(img_size=img_size, in_chans=in_chans, depth=depth, **kwargs)
 252        self.img_size = img_size
 253        self.in_chans = in_chans
 254        self.depth = depth
 255
 256    def convert_to_expected_dim(self, inputs_):
 257        """@private
 258        """
 259        inputs_ = inputs_[:, 1:, :]  # removing the class tokens
 260        # reshape the outputs to desired shape (N x H*W X C -> N x H x W x C)
 261        rdim = inputs_.shape[1]
 262        dshape = int(rdim ** 0.5)  # finding the square root of the outputs for obtaining the patch shape
 263        inputs_ = torch.unflatten(inputs_, 1, (dshape, dshape))
 264        inputs_ = inputs_.permute(0, 3, 1, 2)
 265        return inputs_
 266
 267    def forward_features(self, x):
 268        """@private
 269        """
 270        B = x.shape[0]
 271        x = self.patch_embed(x)
 272
 273        cls_tokens = self.cls_token.expand(B, -1, -1)
 274        x = torch.cat((cls_tokens, x), dim=1)
 275
 276        x = x + self.pos_embed
 277        x = self.pos_drop(x)
 278
 279        # chunks obtained for getting the projections for conjuctions with upsampling blocks
 280        _chunks = int(self.depth / 4)
 281        chunks_for_projection = [_chunks - 1, 2*_chunks - 1, 3*_chunks - 1, 4*_chunks - 1]
 282
 283        list_from_encoder = []
 284        for i, blk in enumerate(self.blocks):
 285            x = blk(x)
 286            if i in chunks_for_projection:
 287                list_from_encoder.append(self.convert_to_expected_dim(x))
 288
 289        x = self.convert_to_expected_dim(self.norm(x))
 290        return x, list_from_encoder[:3]
 291
 292    def forward(self, x: torch.Tensor) -> torch.Tensor:
 293        """Apply the vision transformer to input data.
 294
 295        Args:
 296            x: The input data.
 297
 298        Returns:
 299            The vision transformer output.
 300        """
 301        x, list_from_encoder = self.forward_features(x)
 302        return x, list_from_encoder
 303
 304
 305class ViT_Sam2(ImageEncoder):
 306    """Vision Transformer derived from the Segment Anything 2 Codebase (https://arxiv.org/abs/2408.00714).
 307
 308    Based on https://github.com/facebookresearch/sam2/blob/main/sam2/modeling/backbones/image_encoder.py.
 309
 310    Args:
 311        backbone_channel_list: The channels throughout the entire backbone.
 312        embed_dim: The initial embedding dimension.
 313        num_heads: The initial number of heads.
 314        stages: The number of blocks per stage.
 315        global_att_blocks: The parameter to decide which blocks have global attention.
 316        window_pos_embed_bkg_spatial_size: The spatial size of windowed positional embedding.
 317        window_spec: The window size per stage, when not using global attention.
 318        scalp: The count of lowest resolution features to discard.
 319    """
 320    def __init__(
 321        self,
 322        backbone_channel_list: List[int],
 323        img_size: int = 1024,
 324        embed_dim: int = 96,
 325        num_heads: int = 1,
 326        stages: Tuple[int, ...] = (2, 3, 16, 3),
 327        global_att_blocks: Tuple[int, ...] = (12, 16, 20),
 328        window_pos_embed_bkg_spatial_size: Tuple[int, int] = (14, 14),
 329        window_spec: Tuple[int, ...] = (8, 4, 14, 7),
 330        scalp: int = 1,
 331        **kwargs
 332    ):
 333        if not _sam2_import_success:
 334            raise RuntimeError(
 335                "The vision transformer backend can only be initialized if segment anything 2 is installed. "
 336                "Please install segment anything 2 from https://github.com/facebookresearch/sam2 "
 337                "and then rerun your code"
 338            )
 339
 340        trunk = Hiera(
 341            embed_dim=embed_dim,
 342            num_heads=num_heads,
 343            stages=stages,
 344            global_att_blocks=global_att_blocks,
 345            window_pos_embed_bkg_spatial_size=window_pos_embed_bkg_spatial_size,
 346            window_spec=window_spec,
 347        )
 348        neck = FpnNeck(
 349            position_encoding=PositionEmbeddingSine(num_pos_feats=256),
 350            d_model=256,
 351            backbone_channel_list=backbone_channel_list,
 352            fpn_top_down_levels=[2, 3],
 353            fpn_interp_model="nearest",
 354        )
 355
 356        super().__init__(trunk=trunk, neck=neck, scalp=scalp, **kwargs)
 357        self.scalp = scalp
 358        self.embed_dim = neck.d_model
 359        self.img_size = img_size
 360
 361    def forward(self, x: torch.Tensor):
 362        # The forward pass throught the backbone.
 363        features, pos = self.neck(self.trunk(x))
 364        if self.scalp > 0:  # This discard the "n" lowest resolution features.
 365            features, pos = features[:-self.scalp], pos[:-self.scalp]
 366
 367        return features[-1], features
 368
 369
 370class ViT_Sam3(SAM3ViT):
 371    """Vision Transformer derived from the Segment Anything 3 Codebase (https://arxiv.org/abs/2511.16719).
 372
 373    Based on https://github.com/facebookresearch/sam3/blob/main/sam3/model/vitdet.py.
 374
 375    Args:
 376        img_size: The input image size.
 377        embed_dim: The embedding dimension, corresponding to the number of output channels of the vision transformer.
 378        kwargs: Keyword arguments for the image encoder base class.
 379    """
 380    def __init__(self, img_size: int = 1024, embed_dim: int = 768, **kwargs):
 381        if not _sam3_import_success:
 382            raise RuntimeError(
 383                "The vision transformer backend can only be initialized if segment anything 3 is installed. "
 384                "Please install segment anything 3 from https://github.com/facebookresearch/sam3 "
 385                "and then rerun your code"
 386            )
 387
 388        super().__init__(img_size=img_size, embed_dim=embed_dim, **kwargs)
 389        self.img_size = img_size
 390        self.embed_dim = embed_dim
 391        # Native layers preserve autograd and the input dtype.
 392        for block in self.blocks:
 393            block.mlp = nn.Sequential(OrderedDict(block.mlp.named_children()))
 394
 395    def _load_from_state_dict(
 396        self, state_dict, prefix, local_metadata, strict, missing_keys, unexpected_keys, error_msgs,
 397    ):
 398        # Rotary buffers depend on the input size, so keep the constructed values.
 399        for name, buffer in self.named_buffers():
 400            if name.endswith(("attn.freqs_cis", "attn.freqs_cis_real", "attn.freqs_cis_imag")):
 401                state_dict[prefix + name] = buffer
 402        super()._load_from_state_dict(
 403            state_dict, prefix, local_metadata, strict, missing_keys, unexpected_keys, error_msgs,
 404        )
 405
 406    def forward_features(self, x):
 407        """@private
 408        """
 409        x = self.patch_embed(x)
 410        h, w = x.shape[1], x.shape[2]
 411
 412        s = 0
 413        if self.retain_cls_token:
 414            # If the 'cls_token' is retained, we don't maintain the spatial shape.
 415            x = torch.cat([self.class_embedding, x.flatten(1, 2)], dim=1)
 416            s = 1
 417
 418        if self.pos_embed is not None:
 419            x = x + get_abs_pos(
 420                self.pos_embed, self.pretrain_use_cls_token, (h, w), self.retain_cls_token, tiling=self.tile_abs_pos,
 421            )
 422
 423        x = self.ln_pre(x)
 424
 425        list_from_encoder = []
 426        for i, blk in enumerate(self.blocks):
 427            if self.use_act_checkpoint and self.training:
 428                x = torch.utils.checkpoint.checkpoint(blk, x, use_reentrant=False)
 429            else:
 430                x = blk(x)
 431
 432            x = self._convert_to_expected_dim(x, i, s)
 433
 434            if i in self.full_attn_ids:
 435                list_from_encoder.append(x)
 436
 437        return x, list_from_encoder
 438
 439    def _convert_to_expected_dim(self, x, i, s):
 440        if (i == self.full_attn_ids[-1]) or (
 441            self.return_interm_layers and i in self.full_attn_ids
 442        ):
 443            if i == self.full_attn_ids[-1]:
 444                x = self.ln_post(x)
 445
 446            feats = x[:, s:]
 447            if feats.ndim == 4:
 448                feats = feats.permute(0, 3, 1, 2)
 449            else:
 450                assert feats.ndim == 3
 451                h = w = math.sqrt(feats.shape[1])
 452                feats = feats.reshape(feats.shape[0], h, w, feats.shape[-1]).permute(0, 3, 1, 2)
 453            return feats
 454
 455        else:
 456            return x
 457
 458    def forward(self, x: torch.Tensor):
 459        """Apply the vision transformer to input data.
 460
 461        Args:
 462            x: The input data.
 463
 464        Returns:
 465            The vision transformer output.
 466        """
 467        x, list_from_encoder = self.forward_features(x)
 468        return x, list_from_encoder
 469
 470#
 471# Utilities for ScaleMAE's ViT
 472#
 473
 474
 475class CustomCompose:
 476    def __init__(self, rescale_transform, other_transforms, src_transform):
 477        self.rescale_transform = rescale_transform
 478        self.other_transforms = other_transforms
 479        self.src_transform = src_transform
 480
 481    def __call__(self, x, valid_masks=None):
 482        if valid_masks is not None:
 483            nodata = (x * (1 - valid_masks.float())).max()
 484        x_aug = self.rescale_transform(x)
 485        parms = self.rescale_transform._params
 486
 487        # sanity check, comment if this is working
 488        # valid_masks = self.rescale_transform(valid_masks.float(), params=parms)
 489        # assert (x_aug==self.rescale_transform(x, params=parms)).all() #
 490
 491        if valid_masks is not None:
 492            valid_masks = x_aug != nodata
 493            _, c, h, w = x_aug.shape
 494            zero_ratio = ((valid_masks == 0).sum((1, 2, 3)) / (h * w * c)).cpu().numpy()
 495        else:
 496            zero_ratio = -1
 497
 498        if self.other_transforms:
 499            x_aug = self.other_transforms(x_aug)
 500        x_src = self.src_transform(x_aug)
 501        dx = parms["src"][:, 1, 0] - parms["src"][:, 0, 0]
 502
 503        # dy = (parms['src'][:,2,1] - parms['src'][:,1,1])
 504        # assert (dx == dy).all()
 505
 506        h, w = x_aug.shape[-2:]
 507        # assert h == w
 508
 509        return x_aug, x_src, dx / h, zero_ratio, valid_masks
 510
 511
 512def get_2d_sincos_pos_embed_with_resolution(embed_dim, grid_size, res, cls_token=False, device="cpu"):
 513    """
 514    grid_size: int of the grid height and width
 515    res: array of size n, representing the resolution of a pixel (say, in meters),
 516    return:
 517    pos_embed: [n,grid_size*grid_size, embed_dim] or [n,1+grid_size*grid_size, embed_dim] (w/ or w/o cls_token)
 518    """
 519    # res = torch.FloatTensor(res).to(device)
 520    res = res.to(device)
 521    grid_h = torch.arange(grid_size, dtype=torch.float32, device=device)
 522    grid_w = torch.arange(grid_size, dtype=torch.float32, device=device)
 523    grid = torch.meshgrid(grid_w, grid_h, indexing="xy")  # here h goes first,direction reversed for numpy
 524    grid = torch.stack(grid, dim=0)  # 2 x h x w
 525
 526    # grid = grid.reshape([2, 1, grid_size, grid_size])
 527    grid = torch.einsum("chw,n->cnhw", grid, res)  # 2 x n x h x w
 528    _, n, h, w = grid.shape
 529    pos_embed = get_2d_sincos_pos_embed_from_grid_torch(embed_dim, grid)  # (nxH*W, D/2)
 530    pos_embed = pos_embed.reshape(n, h * w, embed_dim)
 531    if cls_token:
 532        pos_embed = torch.cat(
 533            [torch.zeros([n, 1, embed_dim], dtype=torch.float32, device=pos_embed.device), pos_embed], dim=1
 534        )
 535
 536    return pos_embed
 537
 538
 539def get_2d_sincos_pos_embed_from_grid_torch(embed_dim, grid):
 540    assert embed_dim % 2 == 0
 541
 542    # use half of dimensions to encode grid_h
 543    emb_h = get_1d_sincos_pos_embed_from_grid_torch(embed_dim // 2, grid[0])  # (H*W, D/2)
 544    emb_w = get_1d_sincos_pos_embed_from_grid_torch(embed_dim // 2, grid[1])  # (H*W, D/2)
 545
 546    emb = torch.cat([emb_h, emb_w], dim=1)  # (H*W, D)
 547    return emb
 548
 549
 550def get_1d_sincos_pos_embed_from_grid_torch(embed_dim, pos):
 551    """
 552    embed_dim: output dimension for each position
 553    pos: a list of positions to be encoded: size (M,)
 554    out: (M, D)
 555    """
 556    assert embed_dim % 2 == 0
 557    # old_shape = pos
 558    omega = torch.arange(embed_dim // 2, dtype=torch.float32, device=pos.device)
 559    omega /= embed_dim / 2.0
 560    omega = 1.0 / 10000**omega  # (D/2,)
 561
 562    pos = pos.reshape(-1)  # (M,)
 563    out = torch.einsum("m,d->md", pos, omega)  # (M, D/2), outer product
 564
 565    emb_sin = torch.sin(out)  # (M, D/2)
 566    emb_cos = torch.cos(out)  # (M, D/2)
 567
 568    emb = torch.cat([emb_sin, emb_cos], dim=1)  # (M, D)
 569    return emb
 570
 571
 572class PatchEmbedUnSafe(PatchEmbed):
 573    """Image to Patch Embedding"""
 574
 575    def forward(self, x):
 576        B, C, H, W = x.shape
 577
 578        # NOTE: Comment code from ScaleMAE: Dropped size check in timm
 579        # assert H == self.img_size[0] and W == self.img_size[1], \
 580        #     f"Input image size ({H}*{W}) doesn't match model ({self.img_size[0]}*{self.img_size[1]})."
 581
 582        x = self.proj(x).flatten(2).transpose(1, 2)
 583        return x
 584
 585
 586class ViT_ScaleMAE(VisionTransformer):
 587    """Vision Transformer dervied from the Scale Masked Auto Encoder codebase (TODO: paper and github link).
 588
 589    NOTE: For downstream tasks, the "base_resoulution" parameter needs to be adjusted manually when using
 590    the model on a different zoom factor dataset.
 591    """
 592
 593    def __init__(
 594        self, img_size=224, patch_size=16, in_chans=3, embed_dim=1024, depth=12, base_resolution=2.5, **kwargs
 595    ):
 596        if not _timm_import_success:
 597            raise RuntimeError("The ScaleMAE backend requires timm.")
 598        kwargs["num_classes"] = 0
 599        super().__init__(
 600            img_size=img_size, patch_size=patch_size, in_chans=in_chans, embed_dim=embed_dim, depth=depth, **kwargs
 601        )
 602        self.img_size = img_size
 603        self.in_chans = in_chans
 604        self.depth = depth
 605        self.base_resolution = base_resolution
 606        self.pos_embed = None
 607
 608        self.patch_embed = PatchEmbedUnSafe(
 609            img_size=img_size,
 610            patch_size=patch_size,
 611            in_chans=in_chans,
 612            embed_dim=embed_dim,
 613        )
 614
 615    def transform_inputs(self, x):
 616        import kornia.augmentation as K
 617        from kornia.constants import Resample
 618
 619        self._transforms = CustomCompose(
 620            rescale_transform=K.RandomResizedCrop(
 621                (2 * self.img_size, 2 * self.img_size),
 622                ratio=(1.0, 1.0),
 623                scale=(1.0, 1.0),
 624                resample=Resample.BICUBIC.name,
 625            ),
 626            other_transforms=None,
 627            src_transform=K.Resize((self.img_size, self.img_size)),
 628        )
 629        x, _, ratios, _, _ = self._transforms(x)
 630        input_res = ratios * self.base_resolution
 631        return x, input_res
 632
 633    def convert_to_expected_dim(self, x):
 634        inputs_ = x[:, 1:, :]  # removing the class tokens
 635        # reshape the outputs to desired shape (N X H*W X C -> N X H X W X C)
 636        rdim = inputs_.shape[1]
 637        dshape = int(rdim ** 0.5)  # finding square root of the outputs for obtaining the patch shape
 638        inputs_ = torch.unflatten(inputs_, 1, (dshape, dshape))
 639        inputs_ = inputs_.permute(0, 3, 1, 2)
 640        return inputs_
 641
 642    def forward_features(self, x):
 643        x, input_res = self.transform_inputs(x)
 644
 645        B, _, h, w = x.shape
 646        x = self.patch_embed(x)
 647
 648        num_patches = int((h * w) / (self.patch_embed.patch_size[0] * self.patch_embed.patch_size[1]))
 649        pos_embed = get_2d_sincos_pos_embed_with_resolution(
 650            x.shape[-1],
 651            int(num_patches ** 0.5),
 652            input_res,
 653            cls_token=True,
 654            device=x.device,
 655        )
 656
 657        cls_tokens = self.cls_token.expand(B, -1, -1)  # stole cls_tokens impl from Phil Wang, thanks
 658        x = torch.cat((cls_tokens, x), dim=1)
 659        x = x + pos_embed
 660        x = self.pos_drop(x)
 661
 662        # chunks obtained for getting the projections for conjuctions with upsampling blocks
 663        _chunks = int(self.depth / 4)
 664        chunks_for_projection = [_chunks - 1, 2*_chunks - 1, 3*_chunks - 1, 4*_chunks - 1]
 665
 666        list_from_encoder = []
 667        for i, blk in enumerate(self.blocks):
 668            x = blk(x)
 669            if i in chunks_for_projection:
 670                list_from_encoder.append(self.convert_to_expected_dim(x))
 671
 672        x = self.convert_to_expected_dim(self.norm(x))
 673
 674        return x, list_from_encoder
 675
 676    def forward(self, x):
 677        x, list_from_encoder = self.forward_features(x)
 678        return x, list_from_encoder
 679
 680
 681def _get_dinov2_swiglu(in_features, hidden_features, **kwargs):
 682    hidden_features = (int(hidden_features * 2 / 3) + 7) // 8 * 8
 683    return SwiGLUFFN(in_features=in_features, hidden_features=hidden_features, **kwargs)
 684
 685
 686def _get_dinov2_block(*args, ffn_layer, **kwargs):
 687    if ffn_layer is SwiGLUFFNFused:
 688        ffn_layer = _get_dinov2_swiglu
 689    return Block(*args, attn_class=Attention, ffn_layer=ffn_layer, **kwargs)
 690
 691
 692class ViT_DINOv2(DinoV2VisionTransformer):
 693    """Vision Transformer derived from the DINOv2 Codebase (https://arxiv.org/abs/2304.07193).
 694
 695    Based on:
 696    https://github.com/facebookresearch/dinov2/blob/main/dinov2/models/vision_transformer.py.
 697
 698    Args:
 699        img_size: The input image size.
 700        patch_size: The patch size.
 701        depth: The depth of the network.
 702        num_register_tokens: The number of registers added (in addition to the class tokens).
 703            It's important to know for ViTs trained with registers, to remove them at the end.
 704    """
 705    def __init__(
 706        self,
 707        img_size: int = 224,
 708        patch_size: int = 16,
 709        depth: int = 12,
 710        num_register_tokens: int = 0,
 711        **kwargs
 712    ):
 713        if not _dinov2_import_success:
 714            raise RuntimeError(
 715                "The vision transformer backend can only be initialized if DINOv2 is installed. "
 716                "Please install DINOv2 from https://github.com/facebookresearch/dinov2 "
 717                "and then rerun your code."
 718            )
 719
 720        super().__init__(
 721            img_size=img_size,
 722            depth=depth,
 723            patch_size=patch_size,
 724            num_register_tokens=num_register_tokens,
 725            **kwargs
 726        )
 727
 728        self.img_size = img_size
 729        self.num_register_tokens = num_register_tokens
 730        self.patch_size = patch_size
 731        self.attn_outs = [i for i in range(depth) if i % 3 == 2]
 732
 733    def forward(self, x, masks=None) -> torch.Tensor:
 734
 735        B = x.shape[0]
 736
 737        x = self.prepare_tokens_with_masks(x)
 738
 739        list_of_encoder = []
 740        for i, blk in enumerate(self.blocks):
 741            x = blk(x)
 742            if i in self.attn_outs:
 743                list_of_encoder.append(x)
 744
 745        x = self.norm(x)
 746        x = x[:, self.num_register_tokens + 1:].reshape(
 747            B, self.img_size // self.patch_size, self.img_size // self.patch_size, -1
 748        ).permute(0, 3, 1, 2).contiguous()
 749
 750        list_of_encoder = [
 751            o[:, self.num_register_tokens + 1:].reshape(
 752                B, self.img_size // self.patch_size, self.img_size // self.patch_size, -1
 753            ).permute(0, 3, 1, 2).contiguous() for o in list_of_encoder
 754        ]
 755
 756        return x, list_of_encoder[:3]
 757
 758
 759class ViT_DINOv3(DinoV3VisionTransformer):
 760    """Vision Transformer derived from the DINOv3 Codebase (https://arxiv.org/abs/2508.10104).
 761
 762    Based on:
 763    https://github.com/facebookresearch/dinov3/blob/main/dinov3/models/vision_transformer.py.
 764
 765    Args:
 766        img_size: The input image size.
 767        patch_size: The patch size.
 768        embed_dim: The embedding dimension.
 769        depth: The depth of the network.
 770        num_heads: The number of heads.
 771        ffn_ratio: The FFN rato.
 772        n_storage_tokens: The number of storage (class) tokens to remove.
 773        kwargs: Keyword arguments for the image encoder base class.
 774    """
 775    def __init__(
 776        self,
 777        in_chans: int = 3,
 778        img_size: int = 224,
 779        patch_size: int = 16,
 780        embed_dim: int = 768,
 781        depth: int = 12,
 782        num_heads: int = 12,
 783        ffn_ratio: float = 4.0,
 784        n_storage_tokens: int = 0,
 785        **kwargs
 786    ):
 787        if not _dinov3_import_success:
 788            raise RuntimeError(
 789                "The vision transformer backend can only be initialized if DINOv3 is installed. "
 790                "Please install DINOv3 from https://github.com/facebookresearch/dinov3 "
 791                "and then rerun your code."
 792            )
 793
 794        super().__init__(
 795            in_chans=in_chans,
 796            img_size=img_size,
 797            patch_size=patch_size,
 798            embed_dim=embed_dim,
 799            depth=depth,
 800            num_heads=num_heads,
 801            ffn_ratio=ffn_ratio,
 802            n_storage_tokens=n_storage_tokens,
 803            **kwargs
 804        )
 805
 806        self.in_chans = in_chans
 807        self.img_size = img_size
 808        self.n_storage_tokens = n_storage_tokens
 809        self.attn_outs = [i for i in range(depth) if i % 3 == 2]
 810        self.init_weights()
 811
 812    def forward(self, x) -> torch.Tensor:
 813
 814        B = x.shape[0]
 815
 816        x, hw_tuple = self.prepare_tokens_with_masks(x)
 817
 818        list_of_encoder = []
 819        for i, blk in enumerate(self.blocks):
 820            rope_sincos = self.rope_embed(H=hw_tuple[0], W=hw_tuple[1])
 821            x = blk(x, rope_sincos)
 822            if i in self.attn_outs:
 823                list_of_encoder.append(x)
 824
 825        x = self.norm(x)
 826        x = x[:, self.n_storage_tokens + 1:].reshape(
 827            B, self.img_size // self.patch_size, self.img_size // self.patch_size, -1
 828        ).permute(0, 3, 1, 2).contiguous()
 829
 830        list_of_encoder = [
 831            o[:, self.n_storage_tokens + 1:].reshape(
 832                B, self.img_size // self.patch_size, self.img_size // self.patch_size, -1
 833            ).permute(0, 3, 1, 2).contiguous() for o in list_of_encoder
 834        ]
 835
 836        return x, list_of_encoder[:3]
 837
 838
 839class ViT_Torchvision(nn.Module):
 840    """Vision Transformer from torchvision (https://arxiv.org/abs/2010.11929).
 841
 842    Wraps torchvision ViT models for use as UNETR encoders. Intermediate patch-token grids
 843    are collected at quarter-depth intervals and returned as spatial feature maps alongside
 844    the final encoder output, matching the `(x, list_from_encoder)` contract of other ViT classes.
 845
 846    Supported models: vit_b_16, vit_b_32, vit_l_16, vit_l_32, vit_h_14.
 847
 848    All five models can be used with UNETR. vit_b_16 and vit_l_16 (patch_size=16) work with
 849    any skip-connection setting. vit_b_32, vit_l_32, and vit_h_14 require use_skip_connection=False
 850    in UNETR; the decoder's internal cropping handles the spatial size difference, and postprocess_masks
 851    resizes the output back to the input resolution.
 852
 853    Args:
 854        model_name: Torchvision ViT model name (e.g. 'vit_b_16').
 855        img_size: Expected input image size used by UNETR preprocessing.
 856        in_chans: Number of input channels. If != 3, a 1x1 conv projects to 3 channels.
 857        pretrained: Whether to load ImageNet-pretrained weights.
 858    """
 859    def __init__(
 860        self,
 861        model_name: str,
 862        img_size: int = 224,
 863        in_chans: int = 3,
 864        pretrained: bool = True,
 865    ):
 866        super().__init__()
 867        if not _torchvision_import_success:
 868            raise RuntimeError(
 869                "The vision transformer backend can only be initialized if torchvision is installed. "
 870                "Please install torchvision from https://github.com/pytorch/vision and then rerun your code."
 871            )
 872
 873        fn = getattr(_tv_models, model_name)
 874        backbone = fn(weights="DEFAULT" if pretrained else None)
 875
 876        self.conv_proj = backbone.conv_proj
 877        self.class_token = backbone.class_token
 878        self.encoder = backbone.encoder  # pos_embedding, dropout, layers, ln
 879
 880        self.img_size = img_size
 881        self.in_chans = in_chans
 882        self.embed_dim = backbone.hidden_dim
 883
 884        depth = len(backbone.encoder.layers)
 885        _c = depth // 4
 886        self.chunks_for_projection = [_c - 1, 2 * _c - 1, 3 * _c - 1]
 887
 888        self.input_proj = nn.Conv2d(in_chans, 3, kernel_size=1) if in_chans != 3 else None
 889
 890    def _load_from_state_dict(
 891        self, state_dict, prefix, local_metadata, strict, missing_keys, unexpected_keys, error_msgs,
 892    ):
 893        pos_embed = state_dict.get(prefix + "encoder.pos_embedding")
 894        current = self.encoder.pos_embedding
 895        if (
 896            pos_embed is not None and pos_embed.ndim == 3
 897            and pos_embed.shape[0] == current.shape[0] and pos_embed.shape[2] == current.shape[2]
 898            and pos_embed.shape[1] != current.shape[1]
 899        ):
 900            self.encoder.pos_embedding = nn.Parameter(
 901                current.new_empty(pos_embed.shape), requires_grad=current.requires_grad,
 902            )
 903        super()._load_from_state_dict(
 904            state_dict, prefix, local_metadata, strict, missing_keys, unexpected_keys, error_msgs,
 905        )
 906
 907    def _interpolate_pos_embed(self, pos_embed: torch.Tensor, H_p: int, W_p: int) -> torch.Tensor:
 908        cls_pos, patch_pos = pos_embed[:, :1], pos_embed[:, 1:]
 909        N = patch_pos.shape[1]
 910        H_t = W_t = int(N ** 0.5)
 911        patch_pos = patch_pos.reshape(1, H_t, W_t, -1).permute(0, 3, 1, 2)
 912        patch_pos = F.interpolate(patch_pos, size=(H_p, W_p), mode="bicubic", align_corners=False)
 913        patch_pos = patch_pos.permute(0, 2, 3, 1).reshape(1, H_p * W_p, -1)
 914        return torch.cat([cls_pos, patch_pos], dim=1)
 915
 916    def forward(self, x: torch.Tensor) -> torch.Tensor:
 917        """Apply the vision transformer to input data.
 918
 919        Args:
 920            x: The input data.
 921
 922        Returns:
 923            The vision transformer output.
 924        """
 925        if self.input_proj is not None:
 926            x = self.input_proj(x)
 927
 928        x = self.conv_proj(x)  # (B, D, H_p, W_p)
 929        B, D, H_p, W_p = x.shape
 930        x = x.reshape(B, D, H_p * W_p).permute(0, 2, 1)  # (B, N, D)
 931
 932        cls = self.class_token.expand(B, -1, -1)
 933        x = torch.cat([cls, x], dim=1)  # (B, 1+N, D)
 934
 935        pos = self.encoder.pos_embedding
 936        if pos.shape[1] != x.shape[1]:
 937            pos = self._interpolate_pos_embed(pos, H_p, W_p)
 938        x = x + pos
 939        x = self.encoder.dropout(x)
 940
 941        list_from_encoder = []
 942        for i, blk in enumerate(self.encoder.layers):
 943            x = blk(x)
 944            if i in self.chunks_for_projection:
 945                feat = x[:, 1:].reshape(B, H_p, W_p, D).permute(0, 3, 1, 2).contiguous()
 946                list_from_encoder.append(feat)
 947
 948        x = self.encoder.ln(x)
 949        x = x[:, 1:].reshape(B, H_p, W_p, D).permute(0, 3, 1, 2).contiguous()
 950        return x, list_from_encoder
 951
 952
 953def get_vision_transformer(backbone: str, model: str, img_size: Optional[int] = None, **kwargs) -> nn.Module:
 954    """Get vision transformer encoder.
 955
 956    Args:
 957        backbone: The name of the vision transformer implementation.
 958            One of "sam" / "cellpose_sam" / "sam2" / "sam3" / "mae" / "scalemae" / "dinov2" / "dinov3" / "torchvision".
 959        model: The model name supported by the selected backbone.
 960        img_size: The encoder input size. If None, use the registered default for the backbone.
 961        kwargs: Additional kwargs which can be expected by the vision transformer,
 962            e.g. 'base_resolution' for `ViT_ScaleMAE`.
 963
 964    Returns:
 965        The vision transformer.
 966    """
 967    if backbone not in VIT_CONFIGS:
 968        raise ValueError(f"Unsupported backbone '{backbone}'.")
 969    if model not in VIT_CONFIGS[backbone].models:
 970        raise ValueError(f"Unsupported encoder '{model}' for backbone '{backbone}'.")
 971    if img_size is None:
 972        img_size = VIT_CONFIGS[backbone].img_size
 973
 974    if backbone == "sam":
 975        if model == "vit_b":
 976            encoder = ViT_Sam(
 977                depth=12, embed_dim=768, img_size=img_size, mlp_ratio=4,
 978                norm_layer=partial(torch.nn.LayerNorm, eps=1e-6),
 979                num_heads=12, patch_size=16, qkv_bias=True, use_rel_pos=True,
 980                global_attn_indexes=[2, 5, 8, 11],
 981                window_size=14, out_chans=256,
 982            )
 983        elif model == "vit_l":
 984            encoder = ViT_Sam(
 985                depth=24, embed_dim=1024, img_size=img_size, mlp_ratio=4,
 986                norm_layer=partial(torch.nn.LayerNorm, eps=1e-6),
 987                num_heads=16, patch_size=16, qkv_bias=True, use_rel_pos=True,
 988                global_attn_indexes=[5, 11, 17, 23],
 989                window_size=14, out_chans=256,
 990            )
 991        elif model == "vit_h":
 992            encoder = ViT_Sam(
 993                depth=32, embed_dim=1280, img_size=img_size, mlp_ratio=4,
 994                norm_layer=partial(torch.nn.LayerNorm, eps=1e-6),
 995                num_heads=16, patch_size=16, qkv_bias=True, use_rel_pos=True,
 996                global_attn_indexes=[7, 15, 23, 31],
 997                window_size=14, out_chans=256,
 998            )
 999        else:
1000            raise ValueError(f"'{model}' is not supported by SAM. Currently, 'vit_b', 'vit_l', 'vit_h' are supported.")
1001
1002    elif backbone == "cellpose_sam":
1003        if model != "vit_l":
1004            raise ValueError(f"'{model}' is not supported by CellposeSAM. Only 'vit_l' is supported.")
1005        encoder = ViT_CellposeSAM(ps=8, bsize=img_size)
1006
1007    elif backbone == "sam2":
1008        if model == "hvit_t":
1009            encoder = ViT_Sam2(
1010                img_size=img_size, embed_dim=96, num_heads=1, stages=[1, 2, 7, 2], global_att_blocks=[5, 7, 9],
1011                window_pos_embed_bkg_spatial_size=[7, 7], backbone_channel_list=[768, 384, 192, 96],
1012            )
1013        elif model == "hvit_s":
1014            encoder = ViT_Sam2(
1015                img_size=img_size, embed_dim=96, num_heads=1, stages=[1, 2, 11, 2], global_att_blocks=[7, 10, 13],
1016                window_pos_embed_bkg_spatial_size=[7, 7], backbone_channel_list=[768, 384, 192, 96],
1017            )
1018        elif model == "hvit_b":
1019            encoder = ViT_Sam2(
1020                img_size=img_size, embed_dim=112, num_heads=2, backbone_channel_list=[896, 448, 224, 112],
1021            )
1022        elif model == "hvit_l":
1023            encoder = ViT_Sam2(
1024                img_size=img_size, embed_dim=144, num_heads=2, stages=[2, 6, 36, 4], global_att_blocks=[23, 33, 43],
1025                window_spec=[8, 4, 16, 8], window_pos_embed_bkg_spatial_size=[7, 7],
1026                backbone_channel_list=[1152, 576, 288, 144],
1027            )
1028        else:
1029            raise ValueError(
1030                f"'{model}' is not supported by SAM2. Currently, 'hvit_t', 'hvit_s', 'hvit_b', 'hvit_l' are supported."
1031            )
1032
1033    elif backbone == "sam3":
1034        if model != "vit_pe":
1035            raise ValueError(
1036                "'sam3' does not have multiple model configurations. Please use 'vit_pe' as the model configuration."
1037            )
1038
1039        encoder = ViT_Sam3(
1040            img_size=img_size, pretrain_img_size=336, patch_size=14, embed_dim=1024, depth=32, num_heads=16,
1041            mlp_ratio=4.625, norm_layer="LayerNorm", drop_path_rate=0.1, qkv_bias=True, use_abs_pos=True,
1042            tile_abs_pos=True, global_att_blocks=(7, 15, 23, 31), rel_pos_blocks=(), use_rope=True,
1043            use_interp_rope=True, window_size=24, pretrain_use_cls_token=True, retain_cls_token=False, ln_pre=True,
1044            ln_post=False, return_interm_layers=False, bias_patch_embed=False, compile_mode=None,
1045        )
1046
1047    elif backbone == "mae":
1048        if model == "vit_b":
1049            encoder = ViT_MAE(
1050                img_size=img_size, patch_size=16, embed_dim=768, depth=12, num_heads=12, mlp_ratio=4,
1051                qkv_bias=True, norm_layer=partial(nn.LayerNorm, eps=1e-6)
1052            )
1053        elif model == "vit_l":
1054            encoder = ViT_MAE(
1055                img_size=img_size, patch_size=16, embed_dim=1024, depth=24, num_heads=16, mlp_ratio=4,
1056                qkv_bias=True, norm_layer=partial(nn.LayerNorm, eps=1e-6)
1057            )
1058        elif model == "vit_h":
1059            encoder = ViT_MAE(
1060                img_size=img_size, patch_size=14, embed_dim=1280, depth=32, num_heads=16, mlp_ratio=4,
1061                qkv_bias=True, norm_layer=partial(nn.LayerNorm, eps=1e-6)
1062            )
1063        else:
1064            raise ValueError(f"'{model}' is not supported by MAE. Currently, 'vit_b', 'vit_l', 'vit_h' are supported.")
1065
1066    elif backbone == "scalemae":
1067        base_resolution = kwargs.get("base_resolution", 2.5)
1068
1069        if model == "vit_b":
1070            encoder = ViT_ScaleMAE(
1071                img_size=img_size, patch_size=16, embed_dim=768, depth=12, num_heads=12,
1072                mlp_ratio=4, qkv_bias=True, norm_layer=partial(nn.LayerNorm, eps=1e-6),
1073                base_resolution=base_resolution,
1074            )
1075        elif model == "vit_l":
1076            encoder = ViT_ScaleMAE(
1077                img_size=img_size, patch_size=16, embed_dim=1024, depth=24, num_heads=16,
1078                mlp_ratio=4, qkv_bias=True, norm_layer=partial(nn.LayerNorm, eps=1e-6),
1079                base_resolution=base_resolution,
1080            )
1081        elif model == "vit_h":
1082            encoder = ViT_ScaleMAE(
1083                img_size=img_size, patch_size=14, embed_dim=1280, depth=32, num_heads=16,
1084                mlp_ratio=4, qkv_bias=True, norm_layer=partial(nn.LayerNorm, eps=1e-6),
1085                base_resolution=base_resolution,
1086            )
1087        else:
1088            raise ValueError(
1089                f"'{model}' is not supported by ScaleMAE. Currently, 'vit_b', 'vit_l' and 'vit_h' are supported."
1090            )
1091
1092    elif backbone == "dinov2":
1093        block_fn = _get_dinov2_block
1094        msg = "The model name should be either 'vit_<X>' or 'vit_<X>_reg<Y>."
1095
1096        if model.startswith("vit_s"):
1097            assert model in ["vit_s", "vit_s_reg4"], msg
1098            encoder = ViT_DINOv2(
1099                img_size=img_size, patch_size=14, embed_dim=384, depth=12, num_heads=6, mlp_ratio=4,
1100                block_fn=block_fn, in_chans=3, init_values=1e-5, block_chunks=0,
1101                num_register_tokens=4 if model.endswith("_reg4") else 0,
1102            )
1103        elif model.startswith("vit_b"):
1104            assert model in ["vit_b", "vit_b_reg4"], msg
1105            encoder = ViT_DINOv2(
1106                img_size=img_size, patch_size=14, embed_dim=768, depth=12, num_heads=12, mlp_ratio=4,
1107                block_fn=block_fn, in_chans=3, init_values=1e-5, block_chunks=0,
1108                num_register_tokens=4 if model.endswith("_reg4") else 0,
1109            )
1110        elif model.startswith("vit_l"):
1111            assert model in ["vit_l", "vit_l_reg4"], msg
1112            encoder = ViT_DINOv2(
1113                img_size=img_size, patch_size=14, embed_dim=1024, depth=24, num_heads=16, mlp_ratio=4,
1114                block_fn=block_fn, in_chans=3, init_values=1e-5, block_chunks=0,
1115                num_register_tokens=4 if model.endswith("_reg4") else 0,
1116            )
1117        elif model.startswith("vit_g"):
1118            assert model in ["vit_g", "vit_g_reg4"], msg
1119            encoder = ViT_DINOv2(
1120                img_size=img_size, patch_size=14, embed_dim=1536, depth=40, num_heads=24, mlp_ratio=4,
1121                block_fn=block_fn, in_chans=3, init_values=1e-5, block_chunks=0,
1122                num_register_tokens=4 if model.endswith("_reg4") else 0, ffn_layer="swiglu",
1123            )
1124        else:
1125            raise ValueError(
1126                f"'{model}' is not supported by DINOv2. Currently, 'vit_s', 'vit_b', 'vit_l' and 'vit_g' are supported."
1127            )
1128
1129    elif backbone == "dinov3":
1130
1131        if model == "vit_s":
1132            encoder = ViT_DINOv3(
1133                img_size=img_size, pos_embed_rope_rescale_coords=2, pos_embed_rope_dtype="fp32", embed_dim=384,
1134                num_heads=6, layerscale_init=1.0e-05, norm_layer="layernormbf16", n_storage_tokens=4, mask_k_bias=True,
1135            )
1136        elif model == "vit_s+":
1137            encoder = ViT_DINOv3(
1138                img_size=img_size, pos_embed_rope_rescale_coords=2, pos_embed_rope_dtype="fp32", embed_dim=384,
1139                num_heads=6, ffn_ratio=6, layerscale_init=1.0e-05, norm_layer="layernormbf16",
1140                ffn_layer="swiglu", n_storage_tokens=4, mask_k_bias=True,
1141            )
1142
1143        elif model == "vit_b":
1144            encoder = ViT_DINOv3(
1145                img_size=img_size, pos_embed_rope_rescale_coords=2, pos_embed_rope_dtype="fp32",
1146                layerscale_init=1.0e-05, norm_layer="layernormbf16", n_storage_tokens=4, mask_k_bias=True,
1147            )
1148        elif model == "vit_l":
1149            encoder = ViT_DINOv3(
1150                img_size=img_size, pos_embed_rope_rescale_coords=2, pos_embed_rope_dtype="fp32", embed_dim=1024,
1151                depth=24, num_heads=16, layerscale_init=1.0e-05, norm_layer="layernormbf16",
1152                n_storage_tokens=4, mask_k_bias=True,
1153            )
1154        elif model == "vit_l+":
1155            encoder = ViT_DINOv3(
1156                img_size=img_size, pos_embed_rope_rescale_coords=2, pos_embed_rope_dtype="fp32", embed_dim=1024,
1157                depth=24, num_heads=16, ffn_ratio=6.0, layerscale_init=1.0e-05, norm_layer="layernormbf16",
1158                ffn_layer="swiglu", n_storage_tokens=4, mask_k_bias=True,
1159            )
1160        elif model == "vit_h+":
1161            encoder = ViT_DINOv3(
1162                img_size=img_size, pos_embed_rope_rescale_coords=2, pos_embed_rope_dtype="fp32", embed_dim=1280,
1163                depth=32, num_heads=20, ffn_ratio=6.0, layerscale_init=1.0e-05, norm_layer="layernormbf16",
1164                ffn_layer="swiglu", n_storage_tokens=4, mask_k_bias=True,
1165            )
1166        elif model == "vit_7b":
1167            encoder = ViT_DINOv3(
1168                img_size=img_size, pos_embed_rope_rescale_coords=2, pos_embed_rope_dtype="fp32", embed_dim=4096,
1169                depth=40, num_heads=32, ffn_ratio=3, qkv_bias=False, drop_path_rate=0.0, layerscale_init=1.0e-05,
1170                norm_layer="layernormbf16", ffn_layer="swiglu64", n_storage_tokens=4, mask_k_bias=True,
1171                untie_global_and_local_cls_norm=True,
1172            )
1173        else:
1174            raise ValueError(
1175                f"'{model}' is not supported by DINOv3. Currently, "
1176                " 'vit_s', 'vit_s+', 'vit_b', 'vit_l', 'vit_l+', 'vit_h+'. 'vit_7b' are supported."
1177            )
1178
1179    elif backbone == "torchvision":
1180        supported = ["vit_b_16", "vit_b_32", "vit_l_16", "vit_l_32", "vit_h_14"]
1181        if model not in supported:
1182            raise ValueError(f"'{model}' is not supported by the torchvision backbone. Choose from: {supported}.")
1183        encoder = ViT_Torchvision(model_name=model, img_size=img_size, **kwargs)
1184
1185    else:
1186        raise ValueError(f"The encoder constructor for '{backbone}' is not implemented.")
1187
1188    encoder.model_type = model
1189    return encoder
class ViT_Sam:
 72class ViT_Sam(ImageEncoderViT):
 73    """Vision Transformer derived from the Segment Anything Codebase (https://arxiv.org/abs/2304.02643).
 74
 75    Based on:
 76    https://github.com/facebookresearch/segment-anything/blob/main/segment_anything/modeling/image_encoder.py
 77
 78    Args:
 79        in_chans: The number of input channels.
 80        embed_dim: The embedding dimension, corresponding to the number of output channels of the vision transformer.
 81        global_attn_indexes: The global attention indices.
 82        apply_neck: Whether to apply the convolutional bottleneck after outputs of the last attention head.
 83        kwargs: Keyword arguments for the image encoder base class.
 84    """
 85    def __init__(
 86        self,
 87        in_chans: int = 3,
 88        embed_dim: int = 768,
 89        global_attn_indexes: Tuple[int, ...] = [2, 5, 8, 11],
 90        apply_neck: bool = False,
 91        **kwargs,
 92    ) -> None:
 93        if not _sam_import_success:
 94            raise RuntimeError(
 95                "The vision transformer backend can only be initialized if segment anything is installed. "
 96                "Please install segment anything from https://github.com/facebookresearch/segment-anything "
 97                "and then rerun your code."
 98            )
 99
100        super().__init__(embed_dim=embed_dim, global_attn_indexes=global_attn_indexes, **kwargs)
101        self.chunks_for_projection = global_attn_indexes
102        self.in_chans = in_chans
103        self.embed_dim = embed_dim
104        self.apply_neck = apply_neck
105
106    def forward(self, x: torch.Tensor) -> torch.Tensor:
107        """Apply the vision transformer to input data.
108
109        Args:
110            x: The input data.
111
112        Returns:
113            The vision transformer output.
114        """
115        x = self.patch_embed(x)
116        if self.pos_embed is not None:
117            x = x + self.pos_embed
118
119        list_from_encoder = []
120        for i, blk in enumerate(self.blocks):
121            x = blk(x)
122            if i in self.chunks_for_projection:
123                list_from_encoder.append(x)
124
125        x = x.permute(0, 3, 1, 2)
126
127        if self.apply_neck:
128            x = self.neck(x)
129
130        list_from_encoder = [e.permute(0, 3, 1, 2) for e in list_from_encoder]
131        return x, list_from_encoder[:3]

Vision Transformer derived from the Segment Anything Codebase (https://arxiv.org/abs/2304.02643).

Based on: https://github.com/facebookresearch/segment-anything/blob/main/segment_anything/modeling/image_encoder.py

Arguments:
  • in_chans: The number of input channels.
  • embed_dim: The embedding dimension, corresponding to the number of output channels of the vision transformer.
  • global_attn_indexes: The global attention indices.
  • apply_neck: Whether to apply the convolutional bottleneck after outputs of the last attention head.
  • kwargs: Keyword arguments for the image encoder base class.
ViT_Sam( in_chans: int = 3, embed_dim: int = 768, global_attn_indexes: Tuple[int, ...] = [2, 5, 8, 11], apply_neck: bool = False, **kwargs)
 85    def __init__(
 86        self,
 87        in_chans: int = 3,
 88        embed_dim: int = 768,
 89        global_attn_indexes: Tuple[int, ...] = [2, 5, 8, 11],
 90        apply_neck: bool = False,
 91        **kwargs,
 92    ) -> None:
 93        if not _sam_import_success:
 94            raise RuntimeError(
 95                "The vision transformer backend can only be initialized if segment anything is installed. "
 96                "Please install segment anything from https://github.com/facebookresearch/segment-anything "
 97                "and then rerun your code."
 98            )
 99
100        super().__init__(embed_dim=embed_dim, global_attn_indexes=global_attn_indexes, **kwargs)
101        self.chunks_for_projection = global_attn_indexes
102        self.in_chans = in_chans
103        self.embed_dim = embed_dim
104        self.apply_neck = apply_neck
chunks_for_projection
in_chans
embed_dim
apply_neck
def forward(self, x: torch.Tensor) -> torch.Tensor:
106    def forward(self, x: torch.Tensor) -> torch.Tensor:
107        """Apply the vision transformer to input data.
108
109        Args:
110            x: The input data.
111
112        Returns:
113            The vision transformer output.
114        """
115        x = self.patch_embed(x)
116        if self.pos_embed is not None:
117            x = x + self.pos_embed
118
119        list_from_encoder = []
120        for i, blk in enumerate(self.blocks):
121            x = blk(x)
122            if i in self.chunks_for_projection:
123                list_from_encoder.append(x)
124
125        x = x.permute(0, 3, 1, 2)
126
127        if self.apply_neck:
128            x = self.neck(x)
129
130        list_from_encoder = [e.permute(0, 3, 1, 2) for e in list_from_encoder]
131        return x, list_from_encoder[:3]

Apply the vision transformer to input data.

Arguments:
  • x: The input data.
Returns:

The vision transformer output.

class ViT_CellposeSAM(torch.nn.modules.module.Module):
134class ViT_CellposeSAM(nn.Module):
135    """Vision Transformer derived from the CellposeSAM Codebase (https://doi.org/10.1038/s41592-025-02595-x).
136
137    This replicates CellposeSAM's actual initialization: instantiate SAM's ``ImageEncoderViT`` via
138    ``sam_model_registry``, then modify the patch embedding, position embeddings, and set global attention.
139    This preserves SAM's original relative position bias sizes, enabling direct checkpoint loading
140    without any interpolation.
141
142    Based on: https://github.com/MouseLand/cellpose/blob/main/cellpose/vit_sam.py
143
144    NOTE: The pretrained CellposeSAM model uses ``vit_l`` exclusively.
145
146    Args:
147        ps: The patch size (default for CellposeSAM is 8).
148        bsize: The input image size (default for CellposeSAM is 256).
149        apply_neck: Whether to apply the convolutional bottleneck after outputs of the last attention head.
150    """
151    def __init__(self, ps: int = 8, bsize: int = 256, apply_neck: bool = True) -> None:
152        super().__init__()
153
154        if not _sam_import_success:
155            raise RuntimeError(
156                "The vision transformer backend can only be initialized if segment anything is installed. "
157                "Please install segment anything from https://github.com/facebookresearch/segment-anything "
158                "and then rerun your code."
159            )
160
161        from segment_anything import sam_model_registry
162
163        # Creates the SAM vit_l encoder and applies CellposeSAM's modifications (same as cellpose.vit_sam.Transformer).
164        encoder = sam_model_registry["vit_l"](None).image_encoder
165
166        w = encoder.patch_embed.proj.weight.detach()
167        nchan = w.shape[0]
168
169        # CellPoseSAM changes the patch size from 16 to 'ps'.
170        encoder.patch_embed.proj = nn.Conv2d(3, nchan, stride=ps, kernel_size=ps)
171        encoder.patch_embed.proj.weight.data = w[:, :, ::16 // ps, ::16 // ps]
172
173        # Next, they subsample position embeddings for the new patch size and input resolution.
174        ds = (1024 // 16) // (bsize // ps)
175        encoder.pos_embed = nn.Parameter(encoder.pos_embed[:, ::ds, ::ds], requires_grad=True)
176
177        # Finally, they set all blocks to global attention.
178        for blk in encoder.blocks:
179            blk.window_size = 0
180
181        # Store encoder submodules directly ('state_dict' keys match CellposeSAM after prefix stripping).
182        self.patch_embed = encoder.patch_embed
183        self.pos_embed = encoder.pos_embed
184        self.blocks = encoder.blocks
185        self.neck = encoder.neck
186
187        # Additional attributes expected by UNETR.
188        self.embed_dim = nchan
189        self.img_size = bsize
190        self.in_chans = 3
191        self.apply_neck = apply_neck
192
193        # Feature extraction at evenly-spaced depths.
194        depth = len(self.blocks)
195        _chunks = depth // 4
196        self.chunks_for_projection = [_chunks - 1, 2 * _chunks - 1, 3 * _chunks - 1, 4 * _chunks - 1]
197
198    def forward(self, x: torch.Tensor) -> torch.Tensor:
199        """Apply the vision transformer to input data.
200
201        Args:
202            x: The input data.
203
204        Returns:
205            The vision transformer output.
206        """
207        x = self.patch_embed(x)
208        if self.pos_embed is not None:
209            x = x + self.pos_embed
210
211        list_from_encoder = []
212        for i, blk in enumerate(self.blocks):
213            x = blk(x)
214            if i in self.chunks_for_projection:
215                list_from_encoder.append(x)
216
217        x = x.permute(0, 3, 1, 2)
218
219        if self.apply_neck:
220            x = self.neck(x)
221
222        list_from_encoder = [e.permute(0, 3, 1, 2) for e in list_from_encoder]
223        return x, list_from_encoder[:3]

Vision Transformer derived from the CellposeSAM Codebase (https://doi.org/10.1038/s41592-025-02595-x).

This replicates CellposeSAM's actual initialization: instantiate SAM's ImageEncoderViT via sam_model_registry, then modify the patch embedding, position embeddings, and set global attention. This preserves SAM's original relative position bias sizes, enabling direct checkpoint loading without any interpolation.

Based on: https://github.com/MouseLand/cellpose/blob/main/cellpose/vit_sam.py

NOTE: The pretrained CellposeSAM model uses vit_l exclusively.

Arguments:
  • ps: The patch size (default for CellposeSAM is 8).
  • bsize: The input image size (default for CellposeSAM is 256).
  • apply_neck: Whether to apply the convolutional bottleneck after outputs of the last attention head.
ViT_CellposeSAM(ps: int = 8, bsize: int = 256, apply_neck: bool = True)
151    def __init__(self, ps: int = 8, bsize: int = 256, apply_neck: bool = True) -> None:
152        super().__init__()
153
154        if not _sam_import_success:
155            raise RuntimeError(
156                "The vision transformer backend can only be initialized if segment anything is installed. "
157                "Please install segment anything from https://github.com/facebookresearch/segment-anything "
158                "and then rerun your code."
159            )
160
161        from segment_anything import sam_model_registry
162
163        # Creates the SAM vit_l encoder and applies CellposeSAM's modifications (same as cellpose.vit_sam.Transformer).
164        encoder = sam_model_registry["vit_l"](None).image_encoder
165
166        w = encoder.patch_embed.proj.weight.detach()
167        nchan = w.shape[0]
168
169        # CellPoseSAM changes the patch size from 16 to 'ps'.
170        encoder.patch_embed.proj = nn.Conv2d(3, nchan, stride=ps, kernel_size=ps)
171        encoder.patch_embed.proj.weight.data = w[:, :, ::16 // ps, ::16 // ps]
172
173        # Next, they subsample position embeddings for the new patch size and input resolution.
174        ds = (1024 // 16) // (bsize // ps)
175        encoder.pos_embed = nn.Parameter(encoder.pos_embed[:, ::ds, ::ds], requires_grad=True)
176
177        # Finally, they set all blocks to global attention.
178        for blk in encoder.blocks:
179            blk.window_size = 0
180
181        # Store encoder submodules directly ('state_dict' keys match CellposeSAM after prefix stripping).
182        self.patch_embed = encoder.patch_embed
183        self.pos_embed = encoder.pos_embed
184        self.blocks = encoder.blocks
185        self.neck = encoder.neck
186
187        # Additional attributes expected by UNETR.
188        self.embed_dim = nchan
189        self.img_size = bsize
190        self.in_chans = 3
191        self.apply_neck = apply_neck
192
193        # Feature extraction at evenly-spaced depths.
194        depth = len(self.blocks)
195        _chunks = depth // 4
196        self.chunks_for_projection = [_chunks - 1, 2 * _chunks - 1, 3 * _chunks - 1, 4 * _chunks - 1]

Initialize internal Module state, shared by both nn.Module and ScriptModule.

patch_embed
pos_embed
blocks
neck
embed_dim
img_size
in_chans
apply_neck
chunks_for_projection
def forward(self, x: torch.Tensor) -> torch.Tensor:
198    def forward(self, x: torch.Tensor) -> torch.Tensor:
199        """Apply the vision transformer to input data.
200
201        Args:
202            x: The input data.
203
204        Returns:
205            The vision transformer output.
206        """
207        x = self.patch_embed(x)
208        if self.pos_embed is not None:
209            x = x + self.pos_embed
210
211        list_from_encoder = []
212        for i, blk in enumerate(self.blocks):
213            x = blk(x)
214            if i in self.chunks_for_projection:
215                list_from_encoder.append(x)
216
217        x = x.permute(0, 3, 1, 2)
218
219        if self.apply_neck:
220            x = self.neck(x)
221
222        list_from_encoder = [e.permute(0, 3, 1, 2) for e in list_from_encoder]
223        return x, list_from_encoder[:3]

Apply the vision transformer to input data.

Arguments:
  • x: The input data.
Returns:

The vision transformer output.

class ViT_MAE:
226class ViT_MAE(VisionTransformer):
227    """Vision Transformer derived from the Masked Auto Encoder Codebase (https://arxiv.org/abs/2111.06377).
228
229    Based on:
230    https://github.com/facebookresearch/mae/blob/main/models_vit.py#L20-L53
231
232    Args:
233        img_size: The size of the input for the image encoder. Input images will be resized to match this size.
234        in_chans: The number of input channels.
235        depth: The depth of the vision transformer.
236        kwargs: Additional keyword arguments for the vision transformer base class.
237    """
238    def __init__(
239        self,
240        img_size: int = 1024,  # chosen to match our experiments with segment anything
241        in_chans: int = 3,
242        depth: int = 12,
243        **kwargs
244    ):
245        if not _timm_import_success:
246            raise RuntimeError(
247                "The vision transformer backend can only be initialized if timm is installed. "
248                "Please install timm (using conda/mamba) for using https://github.com/facebookresearch/mae/ "
249                "and then rerun your code"
250            )
251        kwargs["num_classes"] = 0
252        super().__init__(img_size=img_size, in_chans=in_chans, depth=depth, **kwargs)
253        self.img_size = img_size
254        self.in_chans = in_chans
255        self.depth = depth
256
257    def convert_to_expected_dim(self, inputs_):
258        """@private
259        """
260        inputs_ = inputs_[:, 1:, :]  # removing the class tokens
261        # reshape the outputs to desired shape (N x H*W X C -> N x H x W x C)
262        rdim = inputs_.shape[1]
263        dshape = int(rdim ** 0.5)  # finding the square root of the outputs for obtaining the patch shape
264        inputs_ = torch.unflatten(inputs_, 1, (dshape, dshape))
265        inputs_ = inputs_.permute(0, 3, 1, 2)
266        return inputs_
267
268    def forward_features(self, x):
269        """@private
270        """
271        B = x.shape[0]
272        x = self.patch_embed(x)
273
274        cls_tokens = self.cls_token.expand(B, -1, -1)
275        x = torch.cat((cls_tokens, x), dim=1)
276
277        x = x + self.pos_embed
278        x = self.pos_drop(x)
279
280        # chunks obtained for getting the projections for conjuctions with upsampling blocks
281        _chunks = int(self.depth / 4)
282        chunks_for_projection = [_chunks - 1, 2*_chunks - 1, 3*_chunks - 1, 4*_chunks - 1]
283
284        list_from_encoder = []
285        for i, blk in enumerate(self.blocks):
286            x = blk(x)
287            if i in chunks_for_projection:
288                list_from_encoder.append(self.convert_to_expected_dim(x))
289
290        x = self.convert_to_expected_dim(self.norm(x))
291        return x, list_from_encoder[:3]
292
293    def forward(self, x: torch.Tensor) -> torch.Tensor:
294        """Apply the vision transformer to input data.
295
296        Args:
297            x: The input data.
298
299        Returns:
300            The vision transformer output.
301        """
302        x, list_from_encoder = self.forward_features(x)
303        return x, list_from_encoder

Vision Transformer derived from the Masked Auto Encoder Codebase (https://arxiv.org/abs/2111.06377).

Based on: https://github.com/facebookresearch/mae/blob/main/models_vit.py#L20-L53

Arguments:
  • img_size: The size of the input for the image encoder. Input images will be resized to match this size.
  • in_chans: The number of input channels.
  • depth: The depth of the vision transformer.
  • kwargs: Additional keyword arguments for the vision transformer base class.
ViT_MAE(img_size: int = 1024, in_chans: int = 3, depth: int = 12, **kwargs)
238    def __init__(
239        self,
240        img_size: int = 1024,  # chosen to match our experiments with segment anything
241        in_chans: int = 3,
242        depth: int = 12,
243        **kwargs
244    ):
245        if not _timm_import_success:
246            raise RuntimeError(
247                "The vision transformer backend can only be initialized if timm is installed. "
248                "Please install timm (using conda/mamba) for using https://github.com/facebookresearch/mae/ "
249                "and then rerun your code"
250            )
251        kwargs["num_classes"] = 0
252        super().__init__(img_size=img_size, in_chans=in_chans, depth=depth, **kwargs)
253        self.img_size = img_size
254        self.in_chans = in_chans
255        self.depth = depth
img_size
in_chans
depth
def forward(self, x: torch.Tensor) -> torch.Tensor:
293    def forward(self, x: torch.Tensor) -> torch.Tensor:
294        """Apply the vision transformer to input data.
295
296        Args:
297            x: The input data.
298
299        Returns:
300            The vision transformer output.
301        """
302        x, list_from_encoder = self.forward_features(x)
303        return x, list_from_encoder

Apply the vision transformer to input data.

Arguments:
  • x: The input data.
Returns:

The vision transformer output.

class ViT_Sam2:
306class ViT_Sam2(ImageEncoder):
307    """Vision Transformer derived from the Segment Anything 2 Codebase (https://arxiv.org/abs/2408.00714).
308
309    Based on https://github.com/facebookresearch/sam2/blob/main/sam2/modeling/backbones/image_encoder.py.
310
311    Args:
312        backbone_channel_list: The channels throughout the entire backbone.
313        embed_dim: The initial embedding dimension.
314        num_heads: The initial number of heads.
315        stages: The number of blocks per stage.
316        global_att_blocks: The parameter to decide which blocks have global attention.
317        window_pos_embed_bkg_spatial_size: The spatial size of windowed positional embedding.
318        window_spec: The window size per stage, when not using global attention.
319        scalp: The count of lowest resolution features to discard.
320    """
321    def __init__(
322        self,
323        backbone_channel_list: List[int],
324        img_size: int = 1024,
325        embed_dim: int = 96,
326        num_heads: int = 1,
327        stages: Tuple[int, ...] = (2, 3, 16, 3),
328        global_att_blocks: Tuple[int, ...] = (12, 16, 20),
329        window_pos_embed_bkg_spatial_size: Tuple[int, int] = (14, 14),
330        window_spec: Tuple[int, ...] = (8, 4, 14, 7),
331        scalp: int = 1,
332        **kwargs
333    ):
334        if not _sam2_import_success:
335            raise RuntimeError(
336                "The vision transformer backend can only be initialized if segment anything 2 is installed. "
337                "Please install segment anything 2 from https://github.com/facebookresearch/sam2 "
338                "and then rerun your code"
339            )
340
341        trunk = Hiera(
342            embed_dim=embed_dim,
343            num_heads=num_heads,
344            stages=stages,
345            global_att_blocks=global_att_blocks,
346            window_pos_embed_bkg_spatial_size=window_pos_embed_bkg_spatial_size,
347            window_spec=window_spec,
348        )
349        neck = FpnNeck(
350            position_encoding=PositionEmbeddingSine(num_pos_feats=256),
351            d_model=256,
352            backbone_channel_list=backbone_channel_list,
353            fpn_top_down_levels=[2, 3],
354            fpn_interp_model="nearest",
355        )
356
357        super().__init__(trunk=trunk, neck=neck, scalp=scalp, **kwargs)
358        self.scalp = scalp
359        self.embed_dim = neck.d_model
360        self.img_size = img_size
361
362    def forward(self, x: torch.Tensor):
363        # The forward pass throught the backbone.
364        features, pos = self.neck(self.trunk(x))
365        if self.scalp > 0:  # This discard the "n" lowest resolution features.
366            features, pos = features[:-self.scalp], pos[:-self.scalp]
367
368        return features[-1], features

Vision Transformer derived from the Segment Anything 2 Codebase (https://arxiv.org/abs/2408.00714).

Based on https://github.com/facebookresearch/sam2/blob/main/sam2/modeling/backbones/image_encoder.py.

Arguments:
  • backbone_channel_list: The channels throughout the entire backbone.
  • embed_dim: The initial embedding dimension.
  • num_heads: The initial number of heads.
  • stages: The number of blocks per stage.
  • global_att_blocks: The parameter to decide which blocks have global attention.
  • window_pos_embed_bkg_spatial_size: The spatial size of windowed positional embedding.
  • window_spec: The window size per stage, when not using global attention.
  • scalp: The count of lowest resolution features to discard.
ViT_Sam2( backbone_channel_list: List[int], img_size: int = 1024, embed_dim: int = 96, num_heads: int = 1, stages: Tuple[int, ...] = (2, 3, 16, 3), global_att_blocks: Tuple[int, ...] = (12, 16, 20), window_pos_embed_bkg_spatial_size: Tuple[int, int] = (14, 14), window_spec: Tuple[int, ...] = (8, 4, 14, 7), scalp: int = 1, **kwargs)
321    def __init__(
322        self,
323        backbone_channel_list: List[int],
324        img_size: int = 1024,
325        embed_dim: int = 96,
326        num_heads: int = 1,
327        stages: Tuple[int, ...] = (2, 3, 16, 3),
328        global_att_blocks: Tuple[int, ...] = (12, 16, 20),
329        window_pos_embed_bkg_spatial_size: Tuple[int, int] = (14, 14),
330        window_spec: Tuple[int, ...] = (8, 4, 14, 7),
331        scalp: int = 1,
332        **kwargs
333    ):
334        if not _sam2_import_success:
335            raise RuntimeError(
336                "The vision transformer backend can only be initialized if segment anything 2 is installed. "
337                "Please install segment anything 2 from https://github.com/facebookresearch/sam2 "
338                "and then rerun your code"
339            )
340
341        trunk = Hiera(
342            embed_dim=embed_dim,
343            num_heads=num_heads,
344            stages=stages,
345            global_att_blocks=global_att_blocks,
346            window_pos_embed_bkg_spatial_size=window_pos_embed_bkg_spatial_size,
347            window_spec=window_spec,
348        )
349        neck = FpnNeck(
350            position_encoding=PositionEmbeddingSine(num_pos_feats=256),
351            d_model=256,
352            backbone_channel_list=backbone_channel_list,
353            fpn_top_down_levels=[2, 3],
354            fpn_interp_model="nearest",
355        )
356
357        super().__init__(trunk=trunk, neck=neck, scalp=scalp, **kwargs)
358        self.scalp = scalp
359        self.embed_dim = neck.d_model
360        self.img_size = img_size
scalp
embed_dim
img_size
def forward(self, x: torch.Tensor):
362    def forward(self, x: torch.Tensor):
363        # The forward pass throught the backbone.
364        features, pos = self.neck(self.trunk(x))
365        if self.scalp > 0:  # This discard the "n" lowest resolution features.
366            features, pos = features[:-self.scalp], pos[:-self.scalp]
367
368        return features[-1], features
class ViT_Sam3:
371class ViT_Sam3(SAM3ViT):
372    """Vision Transformer derived from the Segment Anything 3 Codebase (https://arxiv.org/abs/2511.16719).
373
374    Based on https://github.com/facebookresearch/sam3/blob/main/sam3/model/vitdet.py.
375
376    Args:
377        img_size: The input image size.
378        embed_dim: The embedding dimension, corresponding to the number of output channels of the vision transformer.
379        kwargs: Keyword arguments for the image encoder base class.
380    """
381    def __init__(self, img_size: int = 1024, embed_dim: int = 768, **kwargs):
382        if not _sam3_import_success:
383            raise RuntimeError(
384                "The vision transformer backend can only be initialized if segment anything 3 is installed. "
385                "Please install segment anything 3 from https://github.com/facebookresearch/sam3 "
386                "and then rerun your code"
387            )
388
389        super().__init__(img_size=img_size, embed_dim=embed_dim, **kwargs)
390        self.img_size = img_size
391        self.embed_dim = embed_dim
392        # Native layers preserve autograd and the input dtype.
393        for block in self.blocks:
394            block.mlp = nn.Sequential(OrderedDict(block.mlp.named_children()))
395
396    def _load_from_state_dict(
397        self, state_dict, prefix, local_metadata, strict, missing_keys, unexpected_keys, error_msgs,
398    ):
399        # Rotary buffers depend on the input size, so keep the constructed values.
400        for name, buffer in self.named_buffers():
401            if name.endswith(("attn.freqs_cis", "attn.freqs_cis_real", "attn.freqs_cis_imag")):
402                state_dict[prefix + name] = buffer
403        super()._load_from_state_dict(
404            state_dict, prefix, local_metadata, strict, missing_keys, unexpected_keys, error_msgs,
405        )
406
407    def forward_features(self, x):
408        """@private
409        """
410        x = self.patch_embed(x)
411        h, w = x.shape[1], x.shape[2]
412
413        s = 0
414        if self.retain_cls_token:
415            # If the 'cls_token' is retained, we don't maintain the spatial shape.
416            x = torch.cat([self.class_embedding, x.flatten(1, 2)], dim=1)
417            s = 1
418
419        if self.pos_embed is not None:
420            x = x + get_abs_pos(
421                self.pos_embed, self.pretrain_use_cls_token, (h, w), self.retain_cls_token, tiling=self.tile_abs_pos,
422            )
423
424        x = self.ln_pre(x)
425
426        list_from_encoder = []
427        for i, blk in enumerate(self.blocks):
428            if self.use_act_checkpoint and self.training:
429                x = torch.utils.checkpoint.checkpoint(blk, x, use_reentrant=False)
430            else:
431                x = blk(x)
432
433            x = self._convert_to_expected_dim(x, i, s)
434
435            if i in self.full_attn_ids:
436                list_from_encoder.append(x)
437
438        return x, list_from_encoder
439
440    def _convert_to_expected_dim(self, x, i, s):
441        if (i == self.full_attn_ids[-1]) or (
442            self.return_interm_layers and i in self.full_attn_ids
443        ):
444            if i == self.full_attn_ids[-1]:
445                x = self.ln_post(x)
446
447            feats = x[:, s:]
448            if feats.ndim == 4:
449                feats = feats.permute(0, 3, 1, 2)
450            else:
451                assert feats.ndim == 3
452                h = w = math.sqrt(feats.shape[1])
453                feats = feats.reshape(feats.shape[0], h, w, feats.shape[-1]).permute(0, 3, 1, 2)
454            return feats
455
456        else:
457            return x
458
459    def forward(self, x: torch.Tensor):
460        """Apply the vision transformer to input data.
461
462        Args:
463            x: The input data.
464
465        Returns:
466            The vision transformer output.
467        """
468        x, list_from_encoder = self.forward_features(x)
469        return x, list_from_encoder

Vision Transformer derived from the Segment Anything 3 Codebase (https://arxiv.org/abs/2511.16719).

Based on https://github.com/facebookresearch/sam3/blob/main/sam3/model/vitdet.py.

Arguments:
  • img_size: The input image size.
  • embed_dim: The embedding dimension, corresponding to the number of output channels of the vision transformer.
  • kwargs: Keyword arguments for the image encoder base class.
ViT_Sam3(img_size: int = 1024, embed_dim: int = 768, **kwargs)
381    def __init__(self, img_size: int = 1024, embed_dim: int = 768, **kwargs):
382        if not _sam3_import_success:
383            raise RuntimeError(
384                "The vision transformer backend can only be initialized if segment anything 3 is installed. "
385                "Please install segment anything 3 from https://github.com/facebookresearch/sam3 "
386                "and then rerun your code"
387            )
388
389        super().__init__(img_size=img_size, embed_dim=embed_dim, **kwargs)
390        self.img_size = img_size
391        self.embed_dim = embed_dim
392        # Native layers preserve autograd and the input dtype.
393        for block in self.blocks:
394            block.mlp = nn.Sequential(OrderedDict(block.mlp.named_children()))
img_size
embed_dim
def forward(self, x: torch.Tensor):
459    def forward(self, x: torch.Tensor):
460        """Apply the vision transformer to input data.
461
462        Args:
463            x: The input data.
464
465        Returns:
466            The vision transformer output.
467        """
468        x, list_from_encoder = self.forward_features(x)
469        return x, list_from_encoder

Apply the vision transformer to input data.

Arguments:
  • x: The input data.
Returns:

The vision transformer output.

class CustomCompose:
476class CustomCompose:
477    def __init__(self, rescale_transform, other_transforms, src_transform):
478        self.rescale_transform = rescale_transform
479        self.other_transforms = other_transforms
480        self.src_transform = src_transform
481
482    def __call__(self, x, valid_masks=None):
483        if valid_masks is not None:
484            nodata = (x * (1 - valid_masks.float())).max()
485        x_aug = self.rescale_transform(x)
486        parms = self.rescale_transform._params
487
488        # sanity check, comment if this is working
489        # valid_masks = self.rescale_transform(valid_masks.float(), params=parms)
490        # assert (x_aug==self.rescale_transform(x, params=parms)).all() #
491
492        if valid_masks is not None:
493            valid_masks = x_aug != nodata
494            _, c, h, w = x_aug.shape
495            zero_ratio = ((valid_masks == 0).sum((1, 2, 3)) / (h * w * c)).cpu().numpy()
496        else:
497            zero_ratio = -1
498
499        if self.other_transforms:
500            x_aug = self.other_transforms(x_aug)
501        x_src = self.src_transform(x_aug)
502        dx = parms["src"][:, 1, 0] - parms["src"][:, 0, 0]
503
504        # dy = (parms['src'][:,2,1] - parms['src'][:,1,1])
505        # assert (dx == dy).all()
506
507        h, w = x_aug.shape[-2:]
508        # assert h == w
509
510        return x_aug, x_src, dx / h, zero_ratio, valid_masks
CustomCompose(rescale_transform, other_transforms, src_transform)
477    def __init__(self, rescale_transform, other_transforms, src_transform):
478        self.rescale_transform = rescale_transform
479        self.other_transforms = other_transforms
480        self.src_transform = src_transform
rescale_transform
other_transforms
src_transform
def get_2d_sincos_pos_embed_with_resolution(embed_dim, grid_size, res, cls_token=False, device='cpu'):
513def get_2d_sincos_pos_embed_with_resolution(embed_dim, grid_size, res, cls_token=False, device="cpu"):
514    """
515    grid_size: int of the grid height and width
516    res: array of size n, representing the resolution of a pixel (say, in meters),
517    return:
518    pos_embed: [n,grid_size*grid_size, embed_dim] or [n,1+grid_size*grid_size, embed_dim] (w/ or w/o cls_token)
519    """
520    # res = torch.FloatTensor(res).to(device)
521    res = res.to(device)
522    grid_h = torch.arange(grid_size, dtype=torch.float32, device=device)
523    grid_w = torch.arange(grid_size, dtype=torch.float32, device=device)
524    grid = torch.meshgrid(grid_w, grid_h, indexing="xy")  # here h goes first,direction reversed for numpy
525    grid = torch.stack(grid, dim=0)  # 2 x h x w
526
527    # grid = grid.reshape([2, 1, grid_size, grid_size])
528    grid = torch.einsum("chw,n->cnhw", grid, res)  # 2 x n x h x w
529    _, n, h, w = grid.shape
530    pos_embed = get_2d_sincos_pos_embed_from_grid_torch(embed_dim, grid)  # (nxH*W, D/2)
531    pos_embed = pos_embed.reshape(n, h * w, embed_dim)
532    if cls_token:
533        pos_embed = torch.cat(
534            [torch.zeros([n, 1, embed_dim], dtype=torch.float32, device=pos_embed.device), pos_embed], dim=1
535        )
536
537    return pos_embed

grid_size: int of the grid height and width res: array of size n, representing the resolution of a pixel (say, in meters), return: pos_embed: [n,grid_sizegrid_size, embed_dim] or [n,1+grid_sizegrid_size, embed_dim] (w/ or w/o cls_token)

def get_2d_sincos_pos_embed_from_grid_torch(embed_dim, grid):
540def get_2d_sincos_pos_embed_from_grid_torch(embed_dim, grid):
541    assert embed_dim % 2 == 0
542
543    # use half of dimensions to encode grid_h
544    emb_h = get_1d_sincos_pos_embed_from_grid_torch(embed_dim // 2, grid[0])  # (H*W, D/2)
545    emb_w = get_1d_sincos_pos_embed_from_grid_torch(embed_dim // 2, grid[1])  # (H*W, D/2)
546
547    emb = torch.cat([emb_h, emb_w], dim=1)  # (H*W, D)
548    return emb
def get_1d_sincos_pos_embed_from_grid_torch(embed_dim, pos):
551def get_1d_sincos_pos_embed_from_grid_torch(embed_dim, pos):
552    """
553    embed_dim: output dimension for each position
554    pos: a list of positions to be encoded: size (M,)
555    out: (M, D)
556    """
557    assert embed_dim % 2 == 0
558    # old_shape = pos
559    omega = torch.arange(embed_dim // 2, dtype=torch.float32, device=pos.device)
560    omega /= embed_dim / 2.0
561    omega = 1.0 / 10000**omega  # (D/2,)
562
563    pos = pos.reshape(-1)  # (M,)
564    out = torch.einsum("m,d->md", pos, omega)  # (M, D/2), outer product
565
566    emb_sin = torch.sin(out)  # (M, D/2)
567    emb_cos = torch.cos(out)  # (M, D/2)
568
569    emb = torch.cat([emb_sin, emb_cos], dim=1)  # (M, D)
570    return emb

embed_dim: output dimension for each position pos: a list of positions to be encoded: size (M,) out: (M, D)

class PatchEmbedUnSafe:
573class PatchEmbedUnSafe(PatchEmbed):
574    """Image to Patch Embedding"""
575
576    def forward(self, x):
577        B, C, H, W = x.shape
578
579        # NOTE: Comment code from ScaleMAE: Dropped size check in timm
580        # assert H == self.img_size[0] and W == self.img_size[1], \
581        #     f"Input image size ({H}*{W}) doesn't match model ({self.img_size[0]}*{self.img_size[1]})."
582
583        x = self.proj(x).flatten(2).transpose(1, 2)
584        return x

Image to Patch Embedding

def forward(self, x):
576    def forward(self, x):
577        B, C, H, W = x.shape
578
579        # NOTE: Comment code from ScaleMAE: Dropped size check in timm
580        # assert H == self.img_size[0] and W == self.img_size[1], \
581        #     f"Input image size ({H}*{W}) doesn't match model ({self.img_size[0]}*{self.img_size[1]})."
582
583        x = self.proj(x).flatten(2).transpose(1, 2)
584        return x
class ViT_ScaleMAE:
587class ViT_ScaleMAE(VisionTransformer):
588    """Vision Transformer dervied from the Scale Masked Auto Encoder codebase (TODO: paper and github link).
589
590    NOTE: For downstream tasks, the "base_resoulution" parameter needs to be adjusted manually when using
591    the model on a different zoom factor dataset.
592    """
593
594    def __init__(
595        self, img_size=224, patch_size=16, in_chans=3, embed_dim=1024, depth=12, base_resolution=2.5, **kwargs
596    ):
597        if not _timm_import_success:
598            raise RuntimeError("The ScaleMAE backend requires timm.")
599        kwargs["num_classes"] = 0
600        super().__init__(
601            img_size=img_size, patch_size=patch_size, in_chans=in_chans, embed_dim=embed_dim, depth=depth, **kwargs
602        )
603        self.img_size = img_size
604        self.in_chans = in_chans
605        self.depth = depth
606        self.base_resolution = base_resolution
607        self.pos_embed = None
608
609        self.patch_embed = PatchEmbedUnSafe(
610            img_size=img_size,
611            patch_size=patch_size,
612            in_chans=in_chans,
613            embed_dim=embed_dim,
614        )
615
616    def transform_inputs(self, x):
617        import kornia.augmentation as K
618        from kornia.constants import Resample
619
620        self._transforms = CustomCompose(
621            rescale_transform=K.RandomResizedCrop(
622                (2 * self.img_size, 2 * self.img_size),
623                ratio=(1.0, 1.0),
624                scale=(1.0, 1.0),
625                resample=Resample.BICUBIC.name,
626            ),
627            other_transforms=None,
628            src_transform=K.Resize((self.img_size, self.img_size)),
629        )
630        x, _, ratios, _, _ = self._transforms(x)
631        input_res = ratios * self.base_resolution
632        return x, input_res
633
634    def convert_to_expected_dim(self, x):
635        inputs_ = x[:, 1:, :]  # removing the class tokens
636        # reshape the outputs to desired shape (N X H*W X C -> N X H X W X C)
637        rdim = inputs_.shape[1]
638        dshape = int(rdim ** 0.5)  # finding square root of the outputs for obtaining the patch shape
639        inputs_ = torch.unflatten(inputs_, 1, (dshape, dshape))
640        inputs_ = inputs_.permute(0, 3, 1, 2)
641        return inputs_
642
643    def forward_features(self, x):
644        x, input_res = self.transform_inputs(x)
645
646        B, _, h, w = x.shape
647        x = self.patch_embed(x)
648
649        num_patches = int((h * w) / (self.patch_embed.patch_size[0] * self.patch_embed.patch_size[1]))
650        pos_embed = get_2d_sincos_pos_embed_with_resolution(
651            x.shape[-1],
652            int(num_patches ** 0.5),
653            input_res,
654            cls_token=True,
655            device=x.device,
656        )
657
658        cls_tokens = self.cls_token.expand(B, -1, -1)  # stole cls_tokens impl from Phil Wang, thanks
659        x = torch.cat((cls_tokens, x), dim=1)
660        x = x + pos_embed
661        x = self.pos_drop(x)
662
663        # chunks obtained for getting the projections for conjuctions with upsampling blocks
664        _chunks = int(self.depth / 4)
665        chunks_for_projection = [_chunks - 1, 2*_chunks - 1, 3*_chunks - 1, 4*_chunks - 1]
666
667        list_from_encoder = []
668        for i, blk in enumerate(self.blocks):
669            x = blk(x)
670            if i in chunks_for_projection:
671                list_from_encoder.append(self.convert_to_expected_dim(x))
672
673        x = self.convert_to_expected_dim(self.norm(x))
674
675        return x, list_from_encoder
676
677    def forward(self, x):
678        x, list_from_encoder = self.forward_features(x)
679        return x, list_from_encoder

Vision Transformer dervied from the Scale Masked Auto Encoder codebase (TODO: paper and github link).

NOTE: For downstream tasks, the "base_resoulution" parameter needs to be adjusted manually when using the model on a different zoom factor dataset.

ViT_ScaleMAE( img_size=224, patch_size=16, in_chans=3, embed_dim=1024, depth=12, base_resolution=2.5, **kwargs)
594    def __init__(
595        self, img_size=224, patch_size=16, in_chans=3, embed_dim=1024, depth=12, base_resolution=2.5, **kwargs
596    ):
597        if not _timm_import_success:
598            raise RuntimeError("The ScaleMAE backend requires timm.")
599        kwargs["num_classes"] = 0
600        super().__init__(
601            img_size=img_size, patch_size=patch_size, in_chans=in_chans, embed_dim=embed_dim, depth=depth, **kwargs
602        )
603        self.img_size = img_size
604        self.in_chans = in_chans
605        self.depth = depth
606        self.base_resolution = base_resolution
607        self.pos_embed = None
608
609        self.patch_embed = PatchEmbedUnSafe(
610            img_size=img_size,
611            patch_size=patch_size,
612            in_chans=in_chans,
613            embed_dim=embed_dim,
614        )
img_size
in_chans
depth
base_resolution
pos_embed
patch_embed
def transform_inputs(self, x):
616    def transform_inputs(self, x):
617        import kornia.augmentation as K
618        from kornia.constants import Resample
619
620        self._transforms = CustomCompose(
621            rescale_transform=K.RandomResizedCrop(
622                (2 * self.img_size, 2 * self.img_size),
623                ratio=(1.0, 1.0),
624                scale=(1.0, 1.0),
625                resample=Resample.BICUBIC.name,
626            ),
627            other_transforms=None,
628            src_transform=K.Resize((self.img_size, self.img_size)),
629        )
630        x, _, ratios, _, _ = self._transforms(x)
631        input_res = ratios * self.base_resolution
632        return x, input_res
def convert_to_expected_dim(self, x):
634    def convert_to_expected_dim(self, x):
635        inputs_ = x[:, 1:, :]  # removing the class tokens
636        # reshape the outputs to desired shape (N X H*W X C -> N X H X W X C)
637        rdim = inputs_.shape[1]
638        dshape = int(rdim ** 0.5)  # finding square root of the outputs for obtaining the patch shape
639        inputs_ = torch.unflatten(inputs_, 1, (dshape, dshape))
640        inputs_ = inputs_.permute(0, 3, 1, 2)
641        return inputs_
def forward_features(self, x):
643    def forward_features(self, x):
644        x, input_res = self.transform_inputs(x)
645
646        B, _, h, w = x.shape
647        x = self.patch_embed(x)
648
649        num_patches = int((h * w) / (self.patch_embed.patch_size[0] * self.patch_embed.patch_size[1]))
650        pos_embed = get_2d_sincos_pos_embed_with_resolution(
651            x.shape[-1],
652            int(num_patches ** 0.5),
653            input_res,
654            cls_token=True,
655            device=x.device,
656        )
657
658        cls_tokens = self.cls_token.expand(B, -1, -1)  # stole cls_tokens impl from Phil Wang, thanks
659        x = torch.cat((cls_tokens, x), dim=1)
660        x = x + pos_embed
661        x = self.pos_drop(x)
662
663        # chunks obtained for getting the projections for conjuctions with upsampling blocks
664        _chunks = int(self.depth / 4)
665        chunks_for_projection = [_chunks - 1, 2*_chunks - 1, 3*_chunks - 1, 4*_chunks - 1]
666
667        list_from_encoder = []
668        for i, blk in enumerate(self.blocks):
669            x = blk(x)
670            if i in chunks_for_projection:
671                list_from_encoder.append(self.convert_to_expected_dim(x))
672
673        x = self.convert_to_expected_dim(self.norm(x))
674
675        return x, list_from_encoder
def forward(self, x):
677    def forward(self, x):
678        x, list_from_encoder = self.forward_features(x)
679        return x, list_from_encoder
class ViT_DINOv2:
693class ViT_DINOv2(DinoV2VisionTransformer):
694    """Vision Transformer derived from the DINOv2 Codebase (https://arxiv.org/abs/2304.07193).
695
696    Based on:
697    https://github.com/facebookresearch/dinov2/blob/main/dinov2/models/vision_transformer.py.
698
699    Args:
700        img_size: The input image size.
701        patch_size: The patch size.
702        depth: The depth of the network.
703        num_register_tokens: The number of registers added (in addition to the class tokens).
704            It's important to know for ViTs trained with registers, to remove them at the end.
705    """
706    def __init__(
707        self,
708        img_size: int = 224,
709        patch_size: int = 16,
710        depth: int = 12,
711        num_register_tokens: int = 0,
712        **kwargs
713    ):
714        if not _dinov2_import_success:
715            raise RuntimeError(
716                "The vision transformer backend can only be initialized if DINOv2 is installed. "
717                "Please install DINOv2 from https://github.com/facebookresearch/dinov2 "
718                "and then rerun your code."
719            )
720
721        super().__init__(
722            img_size=img_size,
723            depth=depth,
724            patch_size=patch_size,
725            num_register_tokens=num_register_tokens,
726            **kwargs
727        )
728
729        self.img_size = img_size
730        self.num_register_tokens = num_register_tokens
731        self.patch_size = patch_size
732        self.attn_outs = [i for i in range(depth) if i % 3 == 2]
733
734    def forward(self, x, masks=None) -> torch.Tensor:
735
736        B = x.shape[0]
737
738        x = self.prepare_tokens_with_masks(x)
739
740        list_of_encoder = []
741        for i, blk in enumerate(self.blocks):
742            x = blk(x)
743            if i in self.attn_outs:
744                list_of_encoder.append(x)
745
746        x = self.norm(x)
747        x = x[:, self.num_register_tokens + 1:].reshape(
748            B, self.img_size // self.patch_size, self.img_size // self.patch_size, -1
749        ).permute(0, 3, 1, 2).contiguous()
750
751        list_of_encoder = [
752            o[:, self.num_register_tokens + 1:].reshape(
753                B, self.img_size // self.patch_size, self.img_size // self.patch_size, -1
754            ).permute(0, 3, 1, 2).contiguous() for o in list_of_encoder
755        ]
756
757        return x, list_of_encoder[:3]

Vision Transformer derived from the DINOv2 Codebase (https://arxiv.org/abs/2304.07193).

Based on: https://github.com/facebookresearch/dinov2/blob/main/dinov2/models/vision_transformer.py.

Arguments:
  • img_size: The input image size.
  • patch_size: The patch size.
  • depth: The depth of the network.
  • num_register_tokens: The number of registers added (in addition to the class tokens). It's important to know for ViTs trained with registers, to remove them at the end.
ViT_DINOv2( img_size: int = 224, patch_size: int = 16, depth: int = 12, num_register_tokens: int = 0, **kwargs)
706    def __init__(
707        self,
708        img_size: int = 224,
709        patch_size: int = 16,
710        depth: int = 12,
711        num_register_tokens: int = 0,
712        **kwargs
713    ):
714        if not _dinov2_import_success:
715            raise RuntimeError(
716                "The vision transformer backend can only be initialized if DINOv2 is installed. "
717                "Please install DINOv2 from https://github.com/facebookresearch/dinov2 "
718                "and then rerun your code."
719            )
720
721        super().__init__(
722            img_size=img_size,
723            depth=depth,
724            patch_size=patch_size,
725            num_register_tokens=num_register_tokens,
726            **kwargs
727        )
728
729        self.img_size = img_size
730        self.num_register_tokens = num_register_tokens
731        self.patch_size = patch_size
732        self.attn_outs = [i for i in range(depth) if i % 3 == 2]
img_size
num_register_tokens
patch_size
attn_outs
def forward(self, x, masks=None) -> torch.Tensor:
734    def forward(self, x, masks=None) -> torch.Tensor:
735
736        B = x.shape[0]
737
738        x = self.prepare_tokens_with_masks(x)
739
740        list_of_encoder = []
741        for i, blk in enumerate(self.blocks):
742            x = blk(x)
743            if i in self.attn_outs:
744                list_of_encoder.append(x)
745
746        x = self.norm(x)
747        x = x[:, self.num_register_tokens + 1:].reshape(
748            B, self.img_size // self.patch_size, self.img_size // self.patch_size, -1
749        ).permute(0, 3, 1, 2).contiguous()
750
751        list_of_encoder = [
752            o[:, self.num_register_tokens + 1:].reshape(
753                B, self.img_size // self.patch_size, self.img_size // self.patch_size, -1
754            ).permute(0, 3, 1, 2).contiguous() for o in list_of_encoder
755        ]
756
757        return x, list_of_encoder[:3]
class ViT_DINOv3:
760class ViT_DINOv3(DinoV3VisionTransformer):
761    """Vision Transformer derived from the DINOv3 Codebase (https://arxiv.org/abs/2508.10104).
762
763    Based on:
764    https://github.com/facebookresearch/dinov3/blob/main/dinov3/models/vision_transformer.py.
765
766    Args:
767        img_size: The input image size.
768        patch_size: The patch size.
769        embed_dim: The embedding dimension.
770        depth: The depth of the network.
771        num_heads: The number of heads.
772        ffn_ratio: The FFN rato.
773        n_storage_tokens: The number of storage (class) tokens to remove.
774        kwargs: Keyword arguments for the image encoder base class.
775    """
776    def __init__(
777        self,
778        in_chans: int = 3,
779        img_size: int = 224,
780        patch_size: int = 16,
781        embed_dim: int = 768,
782        depth: int = 12,
783        num_heads: int = 12,
784        ffn_ratio: float = 4.0,
785        n_storage_tokens: int = 0,
786        **kwargs
787    ):
788        if not _dinov3_import_success:
789            raise RuntimeError(
790                "The vision transformer backend can only be initialized if DINOv3 is installed. "
791                "Please install DINOv3 from https://github.com/facebookresearch/dinov3 "
792                "and then rerun your code."
793            )
794
795        super().__init__(
796            in_chans=in_chans,
797            img_size=img_size,
798            patch_size=patch_size,
799            embed_dim=embed_dim,
800            depth=depth,
801            num_heads=num_heads,
802            ffn_ratio=ffn_ratio,
803            n_storage_tokens=n_storage_tokens,
804            **kwargs
805        )
806
807        self.in_chans = in_chans
808        self.img_size = img_size
809        self.n_storage_tokens = n_storage_tokens
810        self.attn_outs = [i for i in range(depth) if i % 3 == 2]
811        self.init_weights()
812
813    def forward(self, x) -> torch.Tensor:
814
815        B = x.shape[0]
816
817        x, hw_tuple = self.prepare_tokens_with_masks(x)
818
819        list_of_encoder = []
820        for i, blk in enumerate(self.blocks):
821            rope_sincos = self.rope_embed(H=hw_tuple[0], W=hw_tuple[1])
822            x = blk(x, rope_sincos)
823            if i in self.attn_outs:
824                list_of_encoder.append(x)
825
826        x = self.norm(x)
827        x = x[:, self.n_storage_tokens + 1:].reshape(
828            B, self.img_size // self.patch_size, self.img_size // self.patch_size, -1
829        ).permute(0, 3, 1, 2).contiguous()
830
831        list_of_encoder = [
832            o[:, self.n_storage_tokens + 1:].reshape(
833                B, self.img_size // self.patch_size, self.img_size // self.patch_size, -1
834            ).permute(0, 3, 1, 2).contiguous() for o in list_of_encoder
835        ]
836
837        return x, list_of_encoder[:3]

Vision Transformer derived from the DINOv3 Codebase (https://arxiv.org/abs/2508.10104).

Based on: https://github.com/facebookresearch/dinov3/blob/main/dinov3/models/vision_transformer.py.

Arguments:
  • img_size: The input image size.
  • patch_size: The patch size.
  • embed_dim: The embedding dimension.
  • depth: The depth of the network.
  • num_heads: The number of heads.
  • ffn_ratio: The FFN rato.
  • n_storage_tokens: The number of storage (class) tokens to remove.
  • kwargs: Keyword arguments for the image encoder base class.
ViT_DINOv3( in_chans: int = 3, img_size: int = 224, patch_size: int = 16, embed_dim: int = 768, depth: int = 12, num_heads: int = 12, ffn_ratio: float = 4.0, n_storage_tokens: int = 0, **kwargs)
776    def __init__(
777        self,
778        in_chans: int = 3,
779        img_size: int = 224,
780        patch_size: int = 16,
781        embed_dim: int = 768,
782        depth: int = 12,
783        num_heads: int = 12,
784        ffn_ratio: float = 4.0,
785        n_storage_tokens: int = 0,
786        **kwargs
787    ):
788        if not _dinov3_import_success:
789            raise RuntimeError(
790                "The vision transformer backend can only be initialized if DINOv3 is installed. "
791                "Please install DINOv3 from https://github.com/facebookresearch/dinov3 "
792                "and then rerun your code."
793            )
794
795        super().__init__(
796            in_chans=in_chans,
797            img_size=img_size,
798            patch_size=patch_size,
799            embed_dim=embed_dim,
800            depth=depth,
801            num_heads=num_heads,
802            ffn_ratio=ffn_ratio,
803            n_storage_tokens=n_storage_tokens,
804            **kwargs
805        )
806
807        self.in_chans = in_chans
808        self.img_size = img_size
809        self.n_storage_tokens = n_storage_tokens
810        self.attn_outs = [i for i in range(depth) if i % 3 == 2]
811        self.init_weights()
in_chans
img_size
n_storage_tokens
attn_outs
def forward(self, x) -> torch.Tensor:
813    def forward(self, x) -> torch.Tensor:
814
815        B = x.shape[0]
816
817        x, hw_tuple = self.prepare_tokens_with_masks(x)
818
819        list_of_encoder = []
820        for i, blk in enumerate(self.blocks):
821            rope_sincos = self.rope_embed(H=hw_tuple[0], W=hw_tuple[1])
822            x = blk(x, rope_sincos)
823            if i in self.attn_outs:
824                list_of_encoder.append(x)
825
826        x = self.norm(x)
827        x = x[:, self.n_storage_tokens + 1:].reshape(
828            B, self.img_size // self.patch_size, self.img_size // self.patch_size, -1
829        ).permute(0, 3, 1, 2).contiguous()
830
831        list_of_encoder = [
832            o[:, self.n_storage_tokens + 1:].reshape(
833                B, self.img_size // self.patch_size, self.img_size // self.patch_size, -1
834            ).permute(0, 3, 1, 2).contiguous() for o in list_of_encoder
835        ]
836
837        return x, list_of_encoder[:3]
class ViT_Torchvision(torch.nn.modules.module.Module):
840class ViT_Torchvision(nn.Module):
841    """Vision Transformer from torchvision (https://arxiv.org/abs/2010.11929).
842
843    Wraps torchvision ViT models for use as UNETR encoders. Intermediate patch-token grids
844    are collected at quarter-depth intervals and returned as spatial feature maps alongside
845    the final encoder output, matching the `(x, list_from_encoder)` contract of other ViT classes.
846
847    Supported models: vit_b_16, vit_b_32, vit_l_16, vit_l_32, vit_h_14.
848
849    All five models can be used with UNETR. vit_b_16 and vit_l_16 (patch_size=16) work with
850    any skip-connection setting. vit_b_32, vit_l_32, and vit_h_14 require use_skip_connection=False
851    in UNETR; the decoder's internal cropping handles the spatial size difference, and postprocess_masks
852    resizes the output back to the input resolution.
853
854    Args:
855        model_name: Torchvision ViT model name (e.g. 'vit_b_16').
856        img_size: Expected input image size used by UNETR preprocessing.
857        in_chans: Number of input channels. If != 3, a 1x1 conv projects to 3 channels.
858        pretrained: Whether to load ImageNet-pretrained weights.
859    """
860    def __init__(
861        self,
862        model_name: str,
863        img_size: int = 224,
864        in_chans: int = 3,
865        pretrained: bool = True,
866    ):
867        super().__init__()
868        if not _torchvision_import_success:
869            raise RuntimeError(
870                "The vision transformer backend can only be initialized if torchvision is installed. "
871                "Please install torchvision from https://github.com/pytorch/vision and then rerun your code."
872            )
873
874        fn = getattr(_tv_models, model_name)
875        backbone = fn(weights="DEFAULT" if pretrained else None)
876
877        self.conv_proj = backbone.conv_proj
878        self.class_token = backbone.class_token
879        self.encoder = backbone.encoder  # pos_embedding, dropout, layers, ln
880
881        self.img_size = img_size
882        self.in_chans = in_chans
883        self.embed_dim = backbone.hidden_dim
884
885        depth = len(backbone.encoder.layers)
886        _c = depth // 4
887        self.chunks_for_projection = [_c - 1, 2 * _c - 1, 3 * _c - 1]
888
889        self.input_proj = nn.Conv2d(in_chans, 3, kernel_size=1) if in_chans != 3 else None
890
891    def _load_from_state_dict(
892        self, state_dict, prefix, local_metadata, strict, missing_keys, unexpected_keys, error_msgs,
893    ):
894        pos_embed = state_dict.get(prefix + "encoder.pos_embedding")
895        current = self.encoder.pos_embedding
896        if (
897            pos_embed is not None and pos_embed.ndim == 3
898            and pos_embed.shape[0] == current.shape[0] and pos_embed.shape[2] == current.shape[2]
899            and pos_embed.shape[1] != current.shape[1]
900        ):
901            self.encoder.pos_embedding = nn.Parameter(
902                current.new_empty(pos_embed.shape), requires_grad=current.requires_grad,
903            )
904        super()._load_from_state_dict(
905            state_dict, prefix, local_metadata, strict, missing_keys, unexpected_keys, error_msgs,
906        )
907
908    def _interpolate_pos_embed(self, pos_embed: torch.Tensor, H_p: int, W_p: int) -> torch.Tensor:
909        cls_pos, patch_pos = pos_embed[:, :1], pos_embed[:, 1:]
910        N = patch_pos.shape[1]
911        H_t = W_t = int(N ** 0.5)
912        patch_pos = patch_pos.reshape(1, H_t, W_t, -1).permute(0, 3, 1, 2)
913        patch_pos = F.interpolate(patch_pos, size=(H_p, W_p), mode="bicubic", align_corners=False)
914        patch_pos = patch_pos.permute(0, 2, 3, 1).reshape(1, H_p * W_p, -1)
915        return torch.cat([cls_pos, patch_pos], dim=1)
916
917    def forward(self, x: torch.Tensor) -> torch.Tensor:
918        """Apply the vision transformer to input data.
919
920        Args:
921            x: The input data.
922
923        Returns:
924            The vision transformer output.
925        """
926        if self.input_proj is not None:
927            x = self.input_proj(x)
928
929        x = self.conv_proj(x)  # (B, D, H_p, W_p)
930        B, D, H_p, W_p = x.shape
931        x = x.reshape(B, D, H_p * W_p).permute(0, 2, 1)  # (B, N, D)
932
933        cls = self.class_token.expand(B, -1, -1)
934        x = torch.cat([cls, x], dim=1)  # (B, 1+N, D)
935
936        pos = self.encoder.pos_embedding
937        if pos.shape[1] != x.shape[1]:
938            pos = self._interpolate_pos_embed(pos, H_p, W_p)
939        x = x + pos
940        x = self.encoder.dropout(x)
941
942        list_from_encoder = []
943        for i, blk in enumerate(self.encoder.layers):
944            x = blk(x)
945            if i in self.chunks_for_projection:
946                feat = x[:, 1:].reshape(B, H_p, W_p, D).permute(0, 3, 1, 2).contiguous()
947                list_from_encoder.append(feat)
948
949        x = self.encoder.ln(x)
950        x = x[:, 1:].reshape(B, H_p, W_p, D).permute(0, 3, 1, 2).contiguous()
951        return x, list_from_encoder

Vision Transformer from torchvision (https://arxiv.org/abs/2010.11929).

Wraps torchvision ViT models for use as UNETR encoders. Intermediate patch-token grids are collected at quarter-depth intervals and returned as spatial feature maps alongside the final encoder output, matching the (x, list_from_encoder) contract of other ViT classes.

Supported models: vit_b_16, vit_b_32, vit_l_16, vit_l_32, vit_h_14.

All five models can be used with UNETR. vit_b_16 and vit_l_16 (patch_size=16) work with any skip-connection setting. vit_b_32, vit_l_32, and vit_h_14 require use_skip_connection=False in UNETR; the decoder's internal cropping handles the spatial size difference, and postprocess_masks resizes the output back to the input resolution.

Arguments:
  • model_name: Torchvision ViT model name (e.g. 'vit_b_16').
  • img_size: Expected input image size used by UNETR preprocessing.
  • in_chans: Number of input channels. If != 3, a 1x1 conv projects to 3 channels.
  • pretrained: Whether to load ImageNet-pretrained weights.
ViT_Torchvision( model_name: str, img_size: int = 224, in_chans: int = 3, pretrained: bool = True)
860    def __init__(
861        self,
862        model_name: str,
863        img_size: int = 224,
864        in_chans: int = 3,
865        pretrained: bool = True,
866    ):
867        super().__init__()
868        if not _torchvision_import_success:
869            raise RuntimeError(
870                "The vision transformer backend can only be initialized if torchvision is installed. "
871                "Please install torchvision from https://github.com/pytorch/vision and then rerun your code."
872            )
873
874        fn = getattr(_tv_models, model_name)
875        backbone = fn(weights="DEFAULT" if pretrained else None)
876
877        self.conv_proj = backbone.conv_proj
878        self.class_token = backbone.class_token
879        self.encoder = backbone.encoder  # pos_embedding, dropout, layers, ln
880
881        self.img_size = img_size
882        self.in_chans = in_chans
883        self.embed_dim = backbone.hidden_dim
884
885        depth = len(backbone.encoder.layers)
886        _c = depth // 4
887        self.chunks_for_projection = [_c - 1, 2 * _c - 1, 3 * _c - 1]
888
889        self.input_proj = nn.Conv2d(in_chans, 3, kernel_size=1) if in_chans != 3 else None

Initialize internal Module state, shared by both nn.Module and ScriptModule.

conv_proj
class_token
encoder
img_size
in_chans
embed_dim
chunks_for_projection
input_proj
def forward(self, x: torch.Tensor) -> torch.Tensor:
917    def forward(self, x: torch.Tensor) -> torch.Tensor:
918        """Apply the vision transformer to input data.
919
920        Args:
921            x: The input data.
922
923        Returns:
924            The vision transformer output.
925        """
926        if self.input_proj is not None:
927            x = self.input_proj(x)
928
929        x = self.conv_proj(x)  # (B, D, H_p, W_p)
930        B, D, H_p, W_p = x.shape
931        x = x.reshape(B, D, H_p * W_p).permute(0, 2, 1)  # (B, N, D)
932
933        cls = self.class_token.expand(B, -1, -1)
934        x = torch.cat([cls, x], dim=1)  # (B, 1+N, D)
935
936        pos = self.encoder.pos_embedding
937        if pos.shape[1] != x.shape[1]:
938            pos = self._interpolate_pos_embed(pos, H_p, W_p)
939        x = x + pos
940        x = self.encoder.dropout(x)
941
942        list_from_encoder = []
943        for i, blk in enumerate(self.encoder.layers):
944            x = blk(x)
945            if i in self.chunks_for_projection:
946                feat = x[:, 1:].reshape(B, H_p, W_p, D).permute(0, 3, 1, 2).contiguous()
947                list_from_encoder.append(feat)
948
949        x = self.encoder.ln(x)
950        x = x[:, 1:].reshape(B, H_p, W_p, D).permute(0, 3, 1, 2).contiguous()
951        return x, list_from_encoder

Apply the vision transformer to input data.

Arguments:
  • x: The input data.
Returns:

The vision transformer output.

def get_vision_transformer( backbone: str, model: str, img_size: Optional[int] = None, **kwargs) -> torch.nn.modules.module.Module:
 954def get_vision_transformer(backbone: str, model: str, img_size: Optional[int] = None, **kwargs) -> nn.Module:
 955    """Get vision transformer encoder.
 956
 957    Args:
 958        backbone: The name of the vision transformer implementation.
 959            One of "sam" / "cellpose_sam" / "sam2" / "sam3" / "mae" / "scalemae" / "dinov2" / "dinov3" / "torchvision".
 960        model: The model name supported by the selected backbone.
 961        img_size: The encoder input size. If None, use the registered default for the backbone.
 962        kwargs: Additional kwargs which can be expected by the vision transformer,
 963            e.g. 'base_resolution' for `ViT_ScaleMAE`.
 964
 965    Returns:
 966        The vision transformer.
 967    """
 968    if backbone not in VIT_CONFIGS:
 969        raise ValueError(f"Unsupported backbone '{backbone}'.")
 970    if model not in VIT_CONFIGS[backbone].models:
 971        raise ValueError(f"Unsupported encoder '{model}' for backbone '{backbone}'.")
 972    if img_size is None:
 973        img_size = VIT_CONFIGS[backbone].img_size
 974
 975    if backbone == "sam":
 976        if model == "vit_b":
 977            encoder = ViT_Sam(
 978                depth=12, embed_dim=768, img_size=img_size, mlp_ratio=4,
 979                norm_layer=partial(torch.nn.LayerNorm, eps=1e-6),
 980                num_heads=12, patch_size=16, qkv_bias=True, use_rel_pos=True,
 981                global_attn_indexes=[2, 5, 8, 11],
 982                window_size=14, out_chans=256,
 983            )
 984        elif model == "vit_l":
 985            encoder = ViT_Sam(
 986                depth=24, embed_dim=1024, img_size=img_size, mlp_ratio=4,
 987                norm_layer=partial(torch.nn.LayerNorm, eps=1e-6),
 988                num_heads=16, patch_size=16, qkv_bias=True, use_rel_pos=True,
 989                global_attn_indexes=[5, 11, 17, 23],
 990                window_size=14, out_chans=256,
 991            )
 992        elif model == "vit_h":
 993            encoder = ViT_Sam(
 994                depth=32, embed_dim=1280, img_size=img_size, mlp_ratio=4,
 995                norm_layer=partial(torch.nn.LayerNorm, eps=1e-6),
 996                num_heads=16, patch_size=16, qkv_bias=True, use_rel_pos=True,
 997                global_attn_indexes=[7, 15, 23, 31],
 998                window_size=14, out_chans=256,
 999            )
1000        else:
1001            raise ValueError(f"'{model}' is not supported by SAM. Currently, 'vit_b', 'vit_l', 'vit_h' are supported.")
1002
1003    elif backbone == "cellpose_sam":
1004        if model != "vit_l":
1005            raise ValueError(f"'{model}' is not supported by CellposeSAM. Only 'vit_l' is supported.")
1006        encoder = ViT_CellposeSAM(ps=8, bsize=img_size)
1007
1008    elif backbone == "sam2":
1009        if model == "hvit_t":
1010            encoder = ViT_Sam2(
1011                img_size=img_size, embed_dim=96, num_heads=1, stages=[1, 2, 7, 2], global_att_blocks=[5, 7, 9],
1012                window_pos_embed_bkg_spatial_size=[7, 7], backbone_channel_list=[768, 384, 192, 96],
1013            )
1014        elif model == "hvit_s":
1015            encoder = ViT_Sam2(
1016                img_size=img_size, embed_dim=96, num_heads=1, stages=[1, 2, 11, 2], global_att_blocks=[7, 10, 13],
1017                window_pos_embed_bkg_spatial_size=[7, 7], backbone_channel_list=[768, 384, 192, 96],
1018            )
1019        elif model == "hvit_b":
1020            encoder = ViT_Sam2(
1021                img_size=img_size, embed_dim=112, num_heads=2, backbone_channel_list=[896, 448, 224, 112],
1022            )
1023        elif model == "hvit_l":
1024            encoder = ViT_Sam2(
1025                img_size=img_size, embed_dim=144, num_heads=2, stages=[2, 6, 36, 4], global_att_blocks=[23, 33, 43],
1026                window_spec=[8, 4, 16, 8], window_pos_embed_bkg_spatial_size=[7, 7],
1027                backbone_channel_list=[1152, 576, 288, 144],
1028            )
1029        else:
1030            raise ValueError(
1031                f"'{model}' is not supported by SAM2. Currently, 'hvit_t', 'hvit_s', 'hvit_b', 'hvit_l' are supported."
1032            )
1033
1034    elif backbone == "sam3":
1035        if model != "vit_pe":
1036            raise ValueError(
1037                "'sam3' does not have multiple model configurations. Please use 'vit_pe' as the model configuration."
1038            )
1039
1040        encoder = ViT_Sam3(
1041            img_size=img_size, pretrain_img_size=336, patch_size=14, embed_dim=1024, depth=32, num_heads=16,
1042            mlp_ratio=4.625, norm_layer="LayerNorm", drop_path_rate=0.1, qkv_bias=True, use_abs_pos=True,
1043            tile_abs_pos=True, global_att_blocks=(7, 15, 23, 31), rel_pos_blocks=(), use_rope=True,
1044            use_interp_rope=True, window_size=24, pretrain_use_cls_token=True, retain_cls_token=False, ln_pre=True,
1045            ln_post=False, return_interm_layers=False, bias_patch_embed=False, compile_mode=None,
1046        )
1047
1048    elif backbone == "mae":
1049        if model == "vit_b":
1050            encoder = ViT_MAE(
1051                img_size=img_size, patch_size=16, embed_dim=768, depth=12, num_heads=12, mlp_ratio=4,
1052                qkv_bias=True, norm_layer=partial(nn.LayerNorm, eps=1e-6)
1053            )
1054        elif model == "vit_l":
1055            encoder = ViT_MAE(
1056                img_size=img_size, patch_size=16, embed_dim=1024, depth=24, num_heads=16, mlp_ratio=4,
1057                qkv_bias=True, norm_layer=partial(nn.LayerNorm, eps=1e-6)
1058            )
1059        elif model == "vit_h":
1060            encoder = ViT_MAE(
1061                img_size=img_size, patch_size=14, embed_dim=1280, depth=32, num_heads=16, mlp_ratio=4,
1062                qkv_bias=True, norm_layer=partial(nn.LayerNorm, eps=1e-6)
1063            )
1064        else:
1065            raise ValueError(f"'{model}' is not supported by MAE. Currently, 'vit_b', 'vit_l', 'vit_h' are supported.")
1066
1067    elif backbone == "scalemae":
1068        base_resolution = kwargs.get("base_resolution", 2.5)
1069
1070        if model == "vit_b":
1071            encoder = ViT_ScaleMAE(
1072                img_size=img_size, patch_size=16, embed_dim=768, depth=12, num_heads=12,
1073                mlp_ratio=4, qkv_bias=True, norm_layer=partial(nn.LayerNorm, eps=1e-6),
1074                base_resolution=base_resolution,
1075            )
1076        elif model == "vit_l":
1077            encoder = ViT_ScaleMAE(
1078                img_size=img_size, patch_size=16, embed_dim=1024, depth=24, num_heads=16,
1079                mlp_ratio=4, qkv_bias=True, norm_layer=partial(nn.LayerNorm, eps=1e-6),
1080                base_resolution=base_resolution,
1081            )
1082        elif model == "vit_h":
1083            encoder = ViT_ScaleMAE(
1084                img_size=img_size, patch_size=14, embed_dim=1280, depth=32, num_heads=16,
1085                mlp_ratio=4, qkv_bias=True, norm_layer=partial(nn.LayerNorm, eps=1e-6),
1086                base_resolution=base_resolution,
1087            )
1088        else:
1089            raise ValueError(
1090                f"'{model}' is not supported by ScaleMAE. Currently, 'vit_b', 'vit_l' and 'vit_h' are supported."
1091            )
1092
1093    elif backbone == "dinov2":
1094        block_fn = _get_dinov2_block
1095        msg = "The model name should be either 'vit_<X>' or 'vit_<X>_reg<Y>."
1096
1097        if model.startswith("vit_s"):
1098            assert model in ["vit_s", "vit_s_reg4"], msg
1099            encoder = ViT_DINOv2(
1100                img_size=img_size, patch_size=14, embed_dim=384, depth=12, num_heads=6, mlp_ratio=4,
1101                block_fn=block_fn, in_chans=3, init_values=1e-5, block_chunks=0,
1102                num_register_tokens=4 if model.endswith("_reg4") else 0,
1103            )
1104        elif model.startswith("vit_b"):
1105            assert model in ["vit_b", "vit_b_reg4"], msg
1106            encoder = ViT_DINOv2(
1107                img_size=img_size, patch_size=14, embed_dim=768, depth=12, num_heads=12, mlp_ratio=4,
1108                block_fn=block_fn, in_chans=3, init_values=1e-5, block_chunks=0,
1109                num_register_tokens=4 if model.endswith("_reg4") else 0,
1110            )
1111        elif model.startswith("vit_l"):
1112            assert model in ["vit_l", "vit_l_reg4"], msg
1113            encoder = ViT_DINOv2(
1114                img_size=img_size, patch_size=14, embed_dim=1024, depth=24, num_heads=16, mlp_ratio=4,
1115                block_fn=block_fn, in_chans=3, init_values=1e-5, block_chunks=0,
1116                num_register_tokens=4 if model.endswith("_reg4") else 0,
1117            )
1118        elif model.startswith("vit_g"):
1119            assert model in ["vit_g", "vit_g_reg4"], msg
1120            encoder = ViT_DINOv2(
1121                img_size=img_size, patch_size=14, embed_dim=1536, depth=40, num_heads=24, mlp_ratio=4,
1122                block_fn=block_fn, in_chans=3, init_values=1e-5, block_chunks=0,
1123                num_register_tokens=4 if model.endswith("_reg4") else 0, ffn_layer="swiglu",
1124            )
1125        else:
1126            raise ValueError(
1127                f"'{model}' is not supported by DINOv2. Currently, 'vit_s', 'vit_b', 'vit_l' and 'vit_g' are supported."
1128            )
1129
1130    elif backbone == "dinov3":
1131
1132        if model == "vit_s":
1133            encoder = ViT_DINOv3(
1134                img_size=img_size, pos_embed_rope_rescale_coords=2, pos_embed_rope_dtype="fp32", embed_dim=384,
1135                num_heads=6, layerscale_init=1.0e-05, norm_layer="layernormbf16", n_storage_tokens=4, mask_k_bias=True,
1136            )
1137        elif model == "vit_s+":
1138            encoder = ViT_DINOv3(
1139                img_size=img_size, pos_embed_rope_rescale_coords=2, pos_embed_rope_dtype="fp32", embed_dim=384,
1140                num_heads=6, ffn_ratio=6, layerscale_init=1.0e-05, norm_layer="layernormbf16",
1141                ffn_layer="swiglu", n_storage_tokens=4, mask_k_bias=True,
1142            )
1143
1144        elif model == "vit_b":
1145            encoder = ViT_DINOv3(
1146                img_size=img_size, pos_embed_rope_rescale_coords=2, pos_embed_rope_dtype="fp32",
1147                layerscale_init=1.0e-05, norm_layer="layernormbf16", n_storage_tokens=4, mask_k_bias=True,
1148            )
1149        elif model == "vit_l":
1150            encoder = ViT_DINOv3(
1151                img_size=img_size, pos_embed_rope_rescale_coords=2, pos_embed_rope_dtype="fp32", embed_dim=1024,
1152                depth=24, num_heads=16, layerscale_init=1.0e-05, norm_layer="layernormbf16",
1153                n_storage_tokens=4, mask_k_bias=True,
1154            )
1155        elif model == "vit_l+":
1156            encoder = ViT_DINOv3(
1157                img_size=img_size, pos_embed_rope_rescale_coords=2, pos_embed_rope_dtype="fp32", embed_dim=1024,
1158                depth=24, num_heads=16, ffn_ratio=6.0, layerscale_init=1.0e-05, norm_layer="layernormbf16",
1159                ffn_layer="swiglu", n_storage_tokens=4, mask_k_bias=True,
1160            )
1161        elif model == "vit_h+":
1162            encoder = ViT_DINOv3(
1163                img_size=img_size, pos_embed_rope_rescale_coords=2, pos_embed_rope_dtype="fp32", embed_dim=1280,
1164                depth=32, num_heads=20, ffn_ratio=6.0, layerscale_init=1.0e-05, norm_layer="layernormbf16",
1165                ffn_layer="swiglu", n_storage_tokens=4, mask_k_bias=True,
1166            )
1167        elif model == "vit_7b":
1168            encoder = ViT_DINOv3(
1169                img_size=img_size, pos_embed_rope_rescale_coords=2, pos_embed_rope_dtype="fp32", embed_dim=4096,
1170                depth=40, num_heads=32, ffn_ratio=3, qkv_bias=False, drop_path_rate=0.0, layerscale_init=1.0e-05,
1171                norm_layer="layernormbf16", ffn_layer="swiglu64", n_storage_tokens=4, mask_k_bias=True,
1172                untie_global_and_local_cls_norm=True,
1173            )
1174        else:
1175            raise ValueError(
1176                f"'{model}' is not supported by DINOv3. Currently, "
1177                " 'vit_s', 'vit_s+', 'vit_b', 'vit_l', 'vit_l+', 'vit_h+'. 'vit_7b' are supported."
1178            )
1179
1180    elif backbone == "torchvision":
1181        supported = ["vit_b_16", "vit_b_32", "vit_l_16", "vit_l_32", "vit_h_14"]
1182        if model not in supported:
1183            raise ValueError(f"'{model}' is not supported by the torchvision backbone. Choose from: {supported}.")
1184        encoder = ViT_Torchvision(model_name=model, img_size=img_size, **kwargs)
1185
1186    else:
1187        raise ValueError(f"The encoder constructor for '{backbone}' is not implemented.")
1188
1189    encoder.model_type = model
1190    return encoder

Get vision transformer encoder.

Arguments:
  • backbone: The name of the vision transformer implementation. One of "sam" / "cellpose_sam" / "sam2" / "sam3" / "mae" / "scalemae" / "dinov2" / "dinov3" / "torchvision".
  • model: The model name supported by the selected backbone.
  • img_size: The encoder input size. If None, use the registered default for the backbone.
  • kwargs: Additional kwargs which can be expected by the vision transformer, e.g. 'base_resolution' for ViT_ScaleMAE.
Returns:

The vision transformer.