"""3AM inference engine for the Hugging Face Space. 3AM routing-fusion model: SAM2.1 Hiera-L whose memory attention is wrapped by RoutingMemoryAttention (routing tokens + FiLM fusion of MUSt3R features), MUSt3R-512 run with memory_mode='kv' keeping every frame's KV (no keyframe selection, no KD-tree, no retrieval), and a SAM2 memory policy that keeps the previous frame plus the most recent positive masks. Frames are letterboxed (never cropped) to a MUSt3R-supported aspect ratio; masks are mapped back to the original resolution. """ from __future__ import annotations import ast import inspect import math import os import textwrap from types import MethodType import numpy as np import roma import torch import torch.nn as nn import torch.nn.functional as F from einops import rearrange from PIL import Image from torchvision import transforms as T from sam2.build_sam import build_sam2_video_predictor from sam2.utils.misc import fill_holes_in_mask_scores from sam2.modeling.sam.transformer import RoPEAttention from sam2.modeling.position_encoding import apply_rotary_enc from sam2.modeling.sam2_base import SAM2Base from must3r.model import ActivationType, apply_activation, load_model, get_pointmaps_activation from must3r.tools.image import get_HW_resolution import must3r.tools.path_to_dust3r # noqa: F401 registers dust3r on sys.path from dust3r.post_process import estimate_focal_knowing_depth MUST3R_SIZE = 512 SAM2_IMAGE_SIZE = 1024 MUST3R_LAYERS = (0, 5, 8, 12) # encoder output + decoder blocks 5, 8, 12 MEMORY_POLICY_NAME = 'previous_frame_then_recent_positive_masks' IMAGE_TRANSFORM = T.Compose([ T.ToTensor(), T.Resize((SAM2_IMAGE_SIZE, SAM2_IMAGE_SIZE), interpolation=T.InterpolationMode.NEAREST_EXACT), T.Normalize(mean=(0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225)), ]) # ---------------------------------------------------------------------------------------- # Geometry helpers (letterbox instead of crop) # ---------------------------------------------------------------------------------------- def letterbox_geometry(width, height): target_h, target_w = get_HW_resolution(height, width, MUST3R_SIZE) padded_w = max(width, int(math.ceil(height * target_w / target_h))) padded_h = max(height, int(math.ceil(width * target_h / target_w))) return dict(width=width, height=height, padded_w=padded_w, padded_h=padded_h, left=(padded_w - width) // 2, top=(padded_h - height) // 2, target_w=target_w, target_h=target_h) def pad_frame(rgb: np.ndarray, g: dict) -> np.ndarray: padded = np.zeros((g['padded_h'], g['padded_w'], 3), np.uint8) padded[g['top']:g['top'] + g['height'], g['left']:g['left'] + g['width']] = rgb return padded def sam2_input(padded: np.ndarray) -> torch.Tensor: """[3, 1024, 1024] normalized SAM2 input from a padded RGB uint8 frame (nearest-exact).""" return IMAGE_TRANSFORM(Image.fromarray(padded)) def must3r_input(padded: np.ndarray, g: dict) -> torch.Tensor: """[3, target_h, target_w] in [-1, 1] for MUSt3R (nearest-exact).""" import cv2 small = cv2.resize(padded, (g['target_w'], g['target_h']), interpolation=cv2.INTER_NEAREST_EXACT) return torch.from_numpy(small).permute(2, 0, 1).contiguous().float() / 255 * 2 - 1 def points_to_sam2(points, labels, g: dict, device): """Original-pixel clicks -> SAM2 1024x1024 coordinates on the padded frame.""" coords = [[(x + g['left']) * SAM2_IMAGE_SIZE / g['padded_w'], (y + g['top']) * SAM2_IMAGE_SIZE / g['padded_h']] for x, y in points] return dict(point_coords=torch.tensor([coords], dtype=torch.float32, device=device), point_labels=torch.tensor([list(labels)], dtype=torch.int32, device=device)) def logits_to_original_mask(logits: torch.Tensor, g: dict) -> np.ndarray: """[1024, 1024] logits -> bool mask at the original frame resolution.""" resized = F.interpolate(logits[None, None].float(), size=(g['padded_h'], g['padded_w']), mode='bilinear', align_corners=False)[0, 0] y, x = g['top'], g['left'] return (resized[y:y + g['height'], x:x + g['width']] > 0).cpu().numpy() def original_mask_to_sam2(mask: np.ndarray, g: dict, device) -> torch.Tensor: """bool mask at original resolution -> [1, 1, 1024, 1024] bool mask input on the padded frame.""" padded = np.zeros((g['padded_h'], g['padded_w']), np.float32) padded[g['top']:g['top'] + g['height'], g['left']:g['left'] + g['width']] = mask.astype(np.float32) t = torch.from_numpy(padded)[None, None] t = F.interpolate(t, size=(SAM2_IMAGE_SIZE, SAM2_IMAGE_SIZE), mode='nearest-exact') return (t >= 0.5).to(device) # ---------------------------------------------------------------------------------------- # Inlined 3AM modules (identical to the training / evaluation code) # ---------------------------------------------------------------------------------------- def positional_encoding(positions, freqs, dim=1): assert 0 <= dim < positions.ndim bands = 2 ** torch.arange(freqs, dtype=positions.dtype, device=positions.device) expanded = positions.unsqueeze(dim + 1) * bands.view(-1, *([1] * (positions.ndim - dim - 1))) expanded = expanded.reshape(*positions.shape[:dim], -1, *positions.shape[dim + 1:]) return torch.cat([torch.sin(expanded), torch.cos(expanded), positions], dim=dim) @torch.autocast("cuda", dtype=torch.float32) def postprocess_must3r_output(pointmaps, pointmaps_activation=ActivationType.NORM_EXP, compute_cam=True): out = {} C = pointmaps.shape[-1] out['pts3d'] = apply_activation(pointmaps[..., :3], activation=pointmaps_activation) if C >= 6: out['pts3d_local'] = apply_activation(pointmaps[..., 3:6], activation=pointmaps_activation) if C in (4, 7): out['conf'] = 1.0 + pointmaps[..., -1].exp() if not compute_cam: return out batch_dims = out['pts3d'].shape[:-3] nbd = len(batch_dims) H, W = out['conf'].shape[-2:] pp = torch.tensor((W / 2, H / 2), device=out['pts3d'].device) focal = estimate_focal_knowing_depth( out['pts3d_local'].reshape(math.prod(batch_dims), H, W, 3), pp, focal_mode='weiszfeld') out['focal'] = focal.reshape(*batch_dims) R, T_ = roma.rigid_points_registration( out['pts3d_local'].reshape(*batch_dims, -1, 3), out['pts3d'].reshape(*batch_dims, -1, 3), weights=out['conf'].reshape(*batch_dims, -1) - 1.0, compute_scaling=False) c2w = torch.eye(4, device=out['pts3d'].device).view(*([1] * nbd), 4, 4).repeat(*batch_dims, 1, 1) c2w[..., :3, :3] = R c2w[..., :3, 3] = T_.view(*batch_dims, 3) out['c2w'] = c2w ys, xs = torch.meshgrid(torch.arange(H, device=out['pts3d'].device), torch.arange(W, device=out['pts3d'].device), indexing='ij') f = out['focal'].reshape(*batch_dims, 1, 1) x = (xs - pp[0]) / f y = (ys - pp[1]) / f d_cam = F.normalize(torch.stack([x, y, torch.ones_like(x)], dim=-1), dim=-1) d_world = torch.einsum('...ij,...hwj->...hwi', R, d_cam) o_world = c2w[..., :3, 3].view(*batch_dims, 1, 1, 3).expand(*batch_dims, H, W, 3) out['ray_origin'] = o_world out['ray_dir'] = d_world out['ray_plucker'] = torch.cat([torch.cross(o_world, d_world, dim=-1), d_world], dim=-1) return out class LayerNorm2d(nn.Module): def __init__(self, num_channels: int, eps: float = 1e-6): super().__init__() self.weight = nn.Parameter(torch.ones(num_channels)) self.bias = nn.Parameter(torch.zeros(num_channels)) self.eps = eps def forward(self, x): u = x.mean(1, keepdim=True) s = (x - u).pow(2).mean(1, keepdim=True) x = (x - u) / torch.sqrt(s + self.eps) return self.weight[:, None, None] * x + self.bias[:, None, None] class RoutingFusion(nn.Module): """Additive FiLM-gated fusion of MUSt3R decoder features + explicit 3D geometry into feat_2d.""" def __init__(self, d_2d=256, d_3d=(1024, 768, 768, 768), d_route=256, d_hidden=384, explicit_3d_freqs=6): super().__init__() self.L = len(d_3d) self.d_2d = d_2d self.d_route = d_route self.explicit_3d_freqs = explicit_3d_freqs explicit_3d_in_ch = 3 * (2 * explicit_3d_freqs + 1) + 6 self.explicit_3d_embedding = nn.Conv2d(explicit_3d_in_ch, d_2d, kernel_size=16, stride=16) self.proj = nn.ModuleList([ nn.Sequential(nn.Conv2d(c, d_hidden, kernel_size=1, bias=True), LayerNorm2d(d_hidden), nn.GELU(), nn.Conv2d(d_hidden, d_2d, kernel_size=3, padding=1, bias=True)) for c in d_3d]) self.mod_mlp = nn.ModuleList([ nn.Sequential(nn.Linear(d_route, 2 * d_route), nn.GELU(), nn.Linear(2 * d_route, 2 * d_2d + 1)) for _ in range(self.L + 1)]) self.base_mod_mlp = nn.Sequential(nn.Linear(d_route, 2 * d_route), nn.GELU(), nn.Linear(2 * d_route, 2 * d_2d)) nn.init.zeros_(self.base_mod_mlp[-1].weight) nn.init.zeros_(self.base_mod_mlp[-1].bias) def _film_add(self, feat, delta, mod, C, per_layer_alpha): gamma = 1.0 + mod[:, :C, None, None] beta = mod[:, C:2 * C, None, None] alpha = mod[:, 2 * C:, None, None] feat = feat + alpha * (delta * gamma + beta) per_layer_alpha.append(alpha.view(feat.shape[0])) return feat def forward(self, feat_2d, feat_3d_list, r, explicit_3d): B, C, H, W = feat_2d.shape assert r.shape[1] == self.L + 2 base_mod = self.base_mod_mlp(r[:, self.L + 1, :]).to(dtype=feat_2d.dtype) feat = feat_2d * (1.0 + base_mod[:, :C, None, None]) + base_mod[:, C:2 * C, None, None] per_layer_alpha = [] for l in range(self.L): delta = self.proj[l](feat_3d_list[l]) delta = F.interpolate(delta, size=(H, W), mode='bilinear', align_corners=False) mod = self.mod_mlp[l](r[:, l, :]).to(dtype=feat.dtype) feat = self._film_add(feat, delta, mod, C, per_layer_alpha) pts3d = explicit_3d[:, :3] plucker = explicit_3d[:, 3:] pe_in = torch.cat([positional_encoding(pts3d, self.explicit_3d_freqs, dim=1), plucker], dim=1) delta = self.explicit_3d_embedding(pe_in.to(self.explicit_3d_embedding.weight.dtype)) delta = F.interpolate(delta, size=(H, W), mode='bilinear', align_corners=False).to(feat.dtype) mod = self.mod_mlp[self.L](r[:, self.L, :]).to(dtype=feat.dtype) feat = self._film_add(feat, delta, mod, C, per_layer_alpha) alpha_stack = torch.stack(per_layer_alpha, dim=1) return feat, alpha_stack, None, base_mod.detach() def _cross_attn_with_routing(ca, full_q, mem_k_in, mem_v_in, HW, num_k_exclude_rope=0): q = ca.q_proj(full_q) k = ca.k_proj(mem_k_in) v = ca.v_proj(mem_v_in) q = ca._separate_heads(q, ca.num_heads) k = ca._separate_heads(k, ca.num_heads) v = ca._separate_heads(v, ca.num_heads) if isinstance(ca, RoPEAttention): freqs_cis = ca.freqs_cis.to(q.device) assert freqs_cis.shape[0] == HW num_k_rope = k.size(-2) - num_k_exclude_rope q_spatial = q[:, :, :HW] q_routing = q[:, :, HW:] q_spatial, k_rope = apply_rotary_enc(q_spatial, k[:, :, :num_k_rope], freqs_cis=freqs_cis, repeat_freqs_k=ca.rope_k_repeat) q = torch.cat([q_spatial, q_routing], dim=2) k = torch.cat([k_rope, k[:, :, num_k_rope:]], dim=2) dropout_p = ca.dropout_p if ca.training else 0.0 out = F.scaled_dot_product_attention(q, k, v, dropout_p=dropout_p) out = ca._recombine_heads(out) return ca.out_proj(out) class RoutingMemoryAttention(nn.Module): """Replaces SAM2's MemoryAttention: routing tokens ride inside every layer and see the memory bank.""" def __init__(self, base, fusion: RoutingFusion, d_route=256, routing_num_heads=8, num_routing_tokens_per_layer=2): super().__init__() self.base = base self.fusion = fusion self.d_route = d_route self.L = fusion.L self.G = self.L + 2 self.K = num_routing_tokens_per_layer self.N = self.K * self.G d_model = base.d_model kv_in_dim = base.layers[0].cross_attn_image.kv_in_dim self.routing_token = nn.Parameter(torch.zeros(1, self.N, d_model)) self.r_in_proj = nn.Linear(d_route, d_model) self.r_out_proj = nn.Linear(self.K * d_model, d_route) self.r0 = nn.Parameter(torch.zeros(1, self.G, d_route)) self._expected_obj_ptr_tokens = max(1, d_model // kv_in_dim) self.obj_ptr_to_r = nn.Linear(self._expected_obj_ptr_tokens * kv_in_dim, self.G * d_route) nn.init.zeros_(self.obj_ptr_to_r.weight) nn.init.zeros_(self.obj_ptr_to_r.bias) nn.init.zeros_(self.r_out_proj.weight) nn.init.zeros_(self.r_out_proj.bias) self._feat_2d_seq = None self._feat_3d_seq = None self._explicit_3d_seq = None self._frame_idx = 1 self._r = None self._feats_for_memory_out = None self.history = {} @property def num_layers(self): return self.base.num_layers @property def d_model(self): return self.base.d_model def reset_state(self, feat_2d_seq, feat_3d_seq, explicit_3d_seq, B, device, dtype, feats_for_memory_out=None): self._feat_2d_seq = feat_2d_seq self._feat_3d_seq = feat_3d_seq self._explicit_3d_seq = explicit_3d_seq self._frame_idx = 1 self._r = self.r0.expand(B, -1, -1).to(device=device, dtype=dtype) self._feats_for_memory_out = feats_for_memory_out self.history = {'r': [self._r.detach()], 'alpha': [], 'base_mod': []} def clear_state(self): self._feat_2d_seq = None self._feat_3d_seq = None self._explicit_3d_seq = None self._frame_idx = 1 self._r = None self._feats_for_memory_out = None self.history = {} def forward(self, curr, memory, curr_pos=None, memory_pos=None, num_obj_ptr_tokens=0): curr_t = curr[0] if isinstance(curr, list) else curr curr_pos_t = curr_pos[0] if isinstance(curr_pos, list) else curr_pos base = self.base B_size = curr_t.shape[1] if self._frame_idx == 1 and num_obj_ptr_tokens > 0: assert num_obj_ptr_tokens == self._expected_obj_ptr_tokens obj_ptr_seq = memory[-num_obj_ptr_tokens:] obj_ptr_recon = obj_ptr_seq.transpose(0, 1).flatten(1, 2) r_delta = self.obj_ptr_to_r(obj_ptr_recon).view(B_size, self.G, self.d_route) self._r = self._r + r_delta.to(self._r.dtype) t = min(self._frame_idx, len(self._feat_2d_seq) - 1) feat_2d = self._feat_2d_seq[t].to(device=curr_t.device, dtype=curr_t.dtype) feat_3d = [f.to(device=curr_t.device, dtype=curr_t.dtype) for f in self._feat_3d_seq[t]] explicit_3d = self._explicit_3d_seq[t].to(device=curr_t.device, dtype=curr_t.dtype) fused, alpha, _, base_mod = self.fusion(feat_2d, feat_3d, self._r, explicit_3d) fused_flat = rearrange(fused, 'b c h w -> (h w) b c') HW, B, C = fused_flat.shape N, K, L, G = self.N, self.K, self.L, self.G if self._feats_for_memory_out is not None and t < len(self._feats_for_memory_out): self._feats_for_memory_out[t] = [fused_flat] r_inject = self.r_in_proj(self._r).to(curr_t.dtype) r_inject = r_inject.unsqueeze(2).expand(B, G, K, C).reshape(B, N, C) rt = self.routing_token.expand(1, N, C).repeat(B, 1, 1) rt = rt + r_inject spatial = fused_flat if base.pos_enc_at_input and curr_pos_t is not None: spatial = spatial + 0.1 * curr_pos_t assert base.batch_first spatial = spatial.transpose(0, 1) spatial_pos = curr_pos_t.transpose(0, 1) mem_b = memory.transpose(0, 1) mem_pos_b = memory_pos.transpose(0, 1) rt_pos = torch.zeros_like(rt) for layer in base.layers: tgt = torch.cat([spatial, rt], dim=1) qpos = torch.cat([spatial_pos, rt_pos], dim=1) tgt2 = layer.norm1(tgt) full_q = tgt2 + qpos if layer.pos_enc_at_attn else tgt2 tgt2 = _cross_attn_with_routing(layer.self_attn, full_q, full_q, tgt2, HW=HW, num_k_exclude_rope=N) tgt = tgt + layer.dropout1(tgt2) tgt2 = layer.norm2(tgt) full_q = tgt2 + qpos if layer.pos_enc_at_cross_attn_queries else tgt2 mem_k_in = mem_b + mem_pos_b if layer.pos_enc_at_cross_attn_keys else mem_b ca_out = _cross_attn_with_routing(layer.cross_attn_image, full_q, mem_k_in, mem_b, HW=HW, num_k_exclude_rope=num_obj_ptr_tokens) tgt = tgt + layer.dropout2(ca_out) tgt2 = layer.norm3(tgt) tgt2 = layer.linear2(layer.dropout(layer.activation(layer.linear1(tgt2)))) tgt = tgt + layer.dropout3(tgt2) spatial, rt = tgt[:, :HW], tgt[:, HW:] spatial = base.norm(spatial) rt = base.norm(rt) spatial = spatial.transpose(0, 1) rt_per_layer = rt.reshape(B, G, K * C) new_r = self.r_out_proj(rt_per_layer).to(self._r.dtype) self._r = new_r self._frame_idx += 1 self.history['r'].append(new_r.detach()) self.history['alpha'].append(alpha) self.history['base_mod'].append(base_mod) return spatial # ---------------------------------------------------------------------------------------- # SAM2 memory policy: previous frame, then the most recent nonempty masks # ---------------------------------------------------------------------------------------- def _previous_visible_indices(outputs, frame_idx): previous = frame_idx - 1 keys = [previous] if previous in outputs else [] for k in sorted(outputs, reverse=True): if k >= previous: continue out = outputs[k] if '_memory_visible' not in out: out['_memory_visible'] = bool((out['pred_masks'] > 0).any()) if out['_memory_visible']: keys.append(k) return keys def _build_previous_visible_method(base_cls): """AST-patch SAM2Base._prepare_memory_conditioned_features so that only the memory *selection* changes (previous frame + recent positive masks, real pointer distances); encodings stay official.""" original = base_cls._prepare_memory_conditioned_features tree = ast.parse(textwrap.dedent(inspect.getsource(original))) fn = tree.body[0] fn.name = '_prepare_previous_visible' fn.body.insert(1, ast.parse("assert not track_in_reverse, 'Harness uses forward-indexed passes'").body[0]) fn.body.insert(2, ast.parse("visible_keys = _previous_visible_indices(output_dict['non_cond_frame_outputs'], frame_idx)").body[0]) counts = {'spatial': 0, 'pointers': 0} class Patch(ast.NodeTransformer): def visit_Assign(self, node): self.generic_visit(node) if (len(node.targets) == 1 and isinstance(node.targets[0], ast.Name) and node.targets[0].id == 'out' and isinstance(node.value, ast.Call) and isinstance(node.value.func, ast.Attribute) and node.value.func.attr == 'get' and isinstance(node.value.func.value, ast.Subscript) and node.value.args and isinstance(node.value.args[0], ast.Name) and node.value.args[0].id == 'prev_frame_idx'): counts['spatial'] += 1 override = ast.parse('prev_frame_idx = visible_keys[t_rel-1] if t_rel <= len(visible_keys) else -1').body[0] return [override, node] return node def visit_For(self, node): self.generic_visit(node) if isinstance(node.target, ast.Name) and node.target.id == 't_diff': counts['pointers'] += 1 replacement = ast.parse('for t_diff, t in [(frame_idx-k,k) for k in visible_keys[:max_obj_ptrs_in_encoder-1]]:\n pass').body[0] assert isinstance(node.body[0], ast.Assign) and node.body[0].targets[0].id == 't' replacement.body = node.body[1:] return replacement return node tree = Patch().visit(tree) ast.fix_missing_locations(tree) assert counts == {'spatial': 1, 'pointers': 1}, counts namespace = dict(original.__globals__) namespace['_previous_visible_indices'] = _previous_visible_indices exec(compile(tree, '', 'exec'), namespace) return namespace['_prepare_previous_visible'] def install_memory_policy(predictor): method = _build_previous_visible_method(SAM2Base) predictor._prepare_memory_conditioned_features = MethodType(method, predictor) predictor.memory_temporal_stride_for_eval = 1 class PositiveMemory: """Keep useful (nonempty) outputs across arbitrarily long gaps of empty masks.""" def __init__(self, capacity): self.capacity = capacity self.outputs = {} def restore(self, noncond): noncond.update(self.outputs) def remember(self, index, output): output['_memory_visible'] = bool((output['pred_masks'] > 0).any()) if output['_memory_visible']: self.outputs[index] = output while len(self.outputs) > self.capacity: del self.outputs[min(self.outputs)] # ---------------------------------------------------------------------------------------- # Model construction # ---------------------------------------------------------------------------------------- import contextlib @contextlib.contextmanager def cpu_construction(): """Build models without touching CUDA. SAM2 moves RoPE tables / position caches to 'cuda' at construction when torch.cuda.is_available(); on ZeroGPU that happens at import time and the resulting placeholder tensors cannot be deep-copied next to CPU tensors. Forward passes move those tables to the input device anyway.""" original = torch.cuda.is_available torch.cuda.is_available = lambda: False try: yield finally: torch.cuda.is_available = original def build_3am_predictor(checkpoint_path, device='cpu'): with cpu_construction(): predictor = build_sam2_video_predictor('configs/sam2.1/sam2.1_hiera_l.yaml', device='cpu', apply_postprocessing=True).eval() d_route = predictor.memory_attention.d_model fusion = RoutingFusion(d_2d=256, d_3d=(1024, 768, 768, 768), d_route=d_route) predictor.memory_attention = RoutingMemoryAttention(base=predictor.memory_attention, fusion=fusion, d_route=d_route, num_routing_tokens_per_layer=2).to(device).eval() weights = torch.load(checkpoint_path, map_location='cpu', mmap=True, weights_only=False) predictor.load_state_dict(weights.get('model', weights), strict=True) del weights install_memory_policy(predictor) return predictor.to(device) def build_original_predictor(checkpoint_path, device='cpu'): with cpu_construction(): predictor = build_sam2_video_predictor('configs/sam2.1/sam2.1_hiera_l.yaml', checkpoint_path, device='cpu', apply_postprocessing=True).eval() return predictor.to(device) def build_must3r(weights_path, device='cpu'): from must3r.model.blocks.attention import has_xformers, toggle_memory_efficient_attention # xformers is off unless MUST3R_XFORMERS=1: on the Space the wheel has no CUDA ops for the runtime # GPU, and PyTorch SDPA gives the same features (verified against the reference implementation). toggle_memory_efficient_attention(enabled=bool(has_xformers and os.environ.get('MUST3R_XFORMERS', '0') == '1')) with cpu_construction(): model = load_model(weights_path, device='cpu', img_size=MUST3R_SIZE, memory_mode='kv', verbose=False) model = [m.eval().to(device) for m in model] assert model[0].patch_size == 16 assert get_pointmaps_activation(model[1]) == ActivationType.NORM_EXP return model # ---------------------------------------------------------------------------------------- # Feature extraction # ---------------------------------------------------------------------------------------- @torch.no_grad() @torch.autocast(device_type='cuda', dtype=torch.bfloat16) def get_image_feature(predictor, images): backbone_out = predictor.forward_image(images) backbone_out = {'backbone_fpn': backbone_out['backbone_fpn'].copy(), 'vision_pos_enc': backbone_out['vision_pos_enc'].copy()} backbone_out, vision_feats, vision_pos_embeds, feat_sizes = predictor._prepare_backbone_features(backbone_out) return vision_feats, vision_pos_embeds, feat_sizes @torch.no_grad() def run_must3r(must3r, padded_frames, g, device='cuda', progress=None): """MUSt3R over all frames, every frame kept in KV memory (no keyframe selection, no KD-tree). Returns must3r_feats: 4 x [N, C, h, w] (fp16, cpu) and explicit: [N, 9, target_h, target_w] (fp16, cpu).""" from must3r.engine.inference import inference_video_multi_ar encoder, decoder = must3r decoder.all_feats = [] n = len(padded_frames) imgs = [must3r_input(p, g).to(device) for p in padded_frames] ts = [torch.tensor([g['target_h'], g['target_w']], dtype=torch.int32, device=device) for _ in range(n)] mem_batches = [min(2, n)] + [1] * max(0, n - 2) pointmaps_activation = get_pointmaps_activation(decoder, verbose=False) try: with torch.autocast('cuda', dtype=torch.bfloat16): inference_video_multi_ar( encoder, decoder, imgs, ts, mem_batches, max_bs=1, verbose=False, device=device, preserve_gpu_mem=False, # keep the KV memory on the GPU (a CPU round trip per frame is very slow) post_process_function=lambda x: {'pts3d': x}, # geometry is recomputed below from layer 12 viser_server=None, num_refinements_iterations=0, local_context_size=0, is_keyframe_function=lambda *_: True, scene_state=None, scene_state_update_function=lambda res, ss: ss) assert len(decoder.all_feats) == n, (len(decoder.all_feats), n) h, w = g['target_h'] // 16, g['target_w'] // 16 per_view = decoder.all_feats feats = [rearrange(torch.stack([pv[k] for pv in per_view], 0).squeeze(1), 'b (h w) c -> b c h w', h=h, w=w).to(torch.float16) for k in MUST3R_LAYERS] explicit = [] shape = torch.tensor([[g['target_h'], g['target_w']]], dtype=torch.int32, device=device) with torch.autocast('cuda', dtype=torch.bfloat16): for i in range(n): feat_flat = per_view[i][12].to(device) if feat_flat.ndim == 2: feat_flat = feat_flat[None] pm = decoder._compute_prediction_head(shape[:, None], 1, 1, [feat_flat], norm=True).squeeze() out = postprocess_must3r_output(pm[None], pointmaps_activation=pointmaps_activation, compute_cam=True) explicit.append(torch.cat((out['pts3d'], out['ray_plucker']), dim=-1).squeeze(0).permute(2, 0, 1).to(torch.float16).cpu()) if progress is not None and (i % 10 == 0 or i == n - 1): progress(i + 1, n) explicit = torch.stack(explicit, 0) finally: decoder.all_feats = [] torch.cuda.empty_cache() return feats, explicit # ---------------------------------------------------------------------------------------- # Tracking (evaluation tracker + positive-memory bank) # ---------------------------------------------------------------------------------------- class Tracker3AM(nn.Module): def __init__(self, predictor, device='cuda'): super().__init__() self.predictor = predictor self.device = device @torch.no_grad() @torch.autocast(device_type='cuda', dtype=torch.bfloat16) def initialize(self, vision_feats_all, vision_pos_all, feat_sizes, must3r_feats, explicit_3d, processing_order, mask_inputs=None, point_inputs=None): device, bf16 = self.device, torch.bfloat16 order = list(processing_order) N = len(order) feat_2d_seq = [rearrange(vision_feats_all[i][-1], '(h w) b c -> b c h w', h=64, w=64).contiguous() for i in order] feat_3d_seq = [[must3r_feats[layer][i:i + 1] for layer in range(4)] for i in order] explicit_3d_seq = [explicit_3d[i:i + 1] for i in order] r0 = self.predictor.memory_attention.r0.expand(1, -1, -1).to(device=device, dtype=bf16) fused_0, _, _, _ = self.predictor.memory_attention.fusion( feat_2d_seq[0].to(device=device, dtype=bf16), [f.to(device=device, dtype=bf16) for f in feat_3d_seq[0]], r0, explicit_3d_seq[0].to(device=device, dtype=bf16)) feats_for_memory = [None] * N feats_for_memory[0] = [rearrange(fused_0, 'b c h w -> (h w) b c')] self.predictor.memory_attention.reset_state(feat_2d_seq=feat_2d_seq, feat_3d_seq=feat_3d_seq, explicit_3d_seq=explicit_3d_seq, B=1, device=device, dtype=bf16, feats_for_memory_out=feats_for_memory) self.processing_order, self.N = order, N self.mask_inputs = mask_inputs.to(device) if mask_inputs is not None else None self.point_inputs = point_inputs self.fused_0 = fused_0 self.feats_for_memory = feats_for_memory self.vision_feats_all, self.vision_pos_all, self.feat_sizes = vision_feats_all, vision_pos_all, feat_sizes self.output_dict = {'cond_frame_outputs': {}, 'non_cond_frame_outputs': {}} self.current_idx = 0 self.positive_memory = PositiveMemory(max(32, self.predictor.num_maskmem, self.predictor.max_obj_ptrs_in_encoder)) @torch.no_grad() @torch.autocast(device_type='cuda', dtype=torch.bfloat16) def step(self): device = self.device t = self.current_idx orig_idx = self.processing_order[t] self.positive_memory.restore(self.output_dict['non_cond_frame_outputs']) vf = [v.to(device) for v in self.vision_feats_all[orig_idx]] vpe = [v.to(device) for v in self.vision_pos_all[orig_idx]] if t == 0: vf[-1] = rearrange(self.fused_0, 'b c h w -> (h w) b c').contiguous() current_out = self.predictor.track_step( frame_idx=t, is_init_cond_frame=(t == 0), current_vision_feats=vf, current_vision_pos_embeds=vpe, feat_sizes=self.feat_sizes, point_inputs=self.point_inputs if t == 0 else None, mask_inputs=self.mask_inputs if t == 0 else None, output_dict=self.output_dict, num_frames=self.N, track_in_reverse=False, run_mem_encoder=False, prev_sam_mask_logits=None) memory_high_res = current_out['pred_masks_high_res'] if self.predictor.fill_hole_area > 0: current_out['pred_masks'] = fill_holes_in_mask_scores(current_out['pred_masks'], self.predictor.fill_hole_area) current_out['pred_masks_high_res'] = F.interpolate(current_out['pred_masks'], size=(self.predictor.image_size, self.predictor.image_size), mode='bilinear', align_corners=False) if t == 0: memory_high_res = current_out['pred_masks_high_res'] mem_vf = list(vf) mem_vf[-1] = self.feats_for_memory[t][0] maskmem_features, maskmem_pos_enc = self.predictor._encode_new_memory( current_vision_feats=mem_vf, feat_sizes=self.feat_sizes, pred_masks_high_res=memory_high_res, object_score_logits=current_out['object_score_logits'], is_mask_from_pts=(t == 0)) current_out['maskmem_features'] = maskmem_features.to(torch.bfloat16) current_out['maskmem_pos_enc'] = maskmem_pos_enc logits = current_out['pred_masks_high_res'].squeeze().float().cpu() bucket = 'cond_frame_outputs' if t == 0 else 'non_cond_frame_outputs' self.output_dict[bucket][t] = current_out self.feats_for_memory[t] = None noncond = self.output_dict['non_cond_frame_outputs'] if t > 0: self.positive_memory.remember(t, noncond[t]) for index in list(noncond): noncond[index].pop('pred_masks_high_res', None) if index < t - 32: del noncond[index] for values in self.predictor.memory_attention.history.values(): values.clear() self.current_idx += 1 return logits @torch.no_grad() def encode_sam2_frames(predictor, padded_frames, device='cuda', batch=2): """SAM2 image-encoder features for every frame (kept on the device).""" vision_feats_all, vision_pos_all, feat_sizes = [], None, None for start in range(0, len(padded_frames), batch): images = torch.stack([sam2_input(p) for p in padded_frames[start:start + batch]]).to(device) vf, vpe, fs = get_image_feature(predictor, images) for k in range(images.shape[0]): vision_feats_all.append([v[:, k:k + 1] for v in vf]) if vision_pos_all is None: vision_pos_all = [v[:, 0:1] for v in vpe] feat_sizes = fs # positional embeddings are identical for every frame; expose them per frame like the evaluator return vision_feats_all, [vision_pos_all] * len(vision_feats_all), feat_sizes @torch.no_grad() def track(predictor, padded_frames, g, must3r_feats, explicit, start_idx, first_mask=None, points=None, labels=None, device='cuda', progress=None): """Bidirectional tracking from start_idx. Prompt: first_mask (bool, original resolution) or clicks. Returns {frame_idx: bool mask at original resolution}.""" n = len(padded_frames) vision_feats_all, vision_pos_all, feat_sizes = encode_sam2_frames(predictor, padded_frames, device) feats_dev = [f.to(device) for f in must3r_feats] explicit_dev = explicit.to(device) mask_inputs = original_mask_to_sam2(first_mask, g, device) if first_mask is not None else None point_inputs = points_to_sam2(points, labels, g, device) if points else None tracker = Tracker3AM(predictor, device) masks, done = {}, 0 orders = [list(range(start_idx, n))] + ([list(range(start_idx, -1, -1))] if start_idx > 0 else []) total = sum(map(len, orders)) try: for order in orders: tracker.initialize(vision_feats_all, vision_pos_all, feat_sizes, feats_dev, explicit_dev, order, mask_inputs=mask_inputs, point_inputs=point_inputs) for i in order: logits = tracker.step() if i not in masks: masks[i] = logits_to_original_mask(logits, g) done += 1 if progress is not None and (done % 10 == 0 or done == total): progress(done, total) predictor.memory_attention.clear_state() finally: predictor.memory_attention.clear_state() del vision_feats_all, feats_dev, explicit_dev torch.cuda.empty_cache() return masks @torch.no_grad() @torch.autocast(device_type='cuda', dtype=torch.bfloat16) def single_frame_mask(original_predictor, padded_frame, g, points, labels, device='cuda'): """Original SAM2.1 prompt-to-mask on one frame (points: original pixels, labels 1/0/2/3).""" image = sam2_input(padded_frame)[None].to(device) vf, vpe, fs = get_image_feature(original_predictor, image) prompts = points_to_sam2(points, labels, g, device) out = original_predictor.track_step( frame_idx=0, is_init_cond_frame=True, current_vision_feats=vf, current_vision_pos_embeds=vpe, feat_sizes=fs, point_inputs=prompts, mask_inputs=None, output_dict={'cond_frame_outputs': {}, 'non_cond_frame_outputs': {}}, num_frames=1, track_in_reverse=False, run_mem_encoder=False, prev_sam_mask_logits=None) logits = F.interpolate(out['pred_masks'], size=(SAM2_IMAGE_SIZE, SAM2_IMAGE_SIZE), mode='bilinear', align_corners=False) return logits_to_original_mask(logits.squeeze().float().cpu(), g)