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
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).
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.
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
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.
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.
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.
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.
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.
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
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.
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.
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
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
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.
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()))
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.
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
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)
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
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)
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
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
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.
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 )
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
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_
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
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.
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]
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]
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.
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()
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]
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.
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.
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.
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.