from __future__ import annotations from typing import Callable import torch import torch.nn.functional as F from unisharp.utils.pixel_convention import scale_intrinsics_align_corners_false def resize_k3_align_corners_false(k: torch.Tensor, *, sx: float, sy: float) -> torch.Tensor: return scale_intrinsics_align_corners_false(k, sx=float(sx), sy=float(sy)) def resize_rgb_u8_chw_high_quality(image: torch.Tensor, *, size: tuple[int, int]) -> torch.Tensor: if not torch.is_tensor(image) or image.ndim != 3: raise ValueError(f"Expected CHW tensor, got {tuple(image.shape) if torch.is_tensor(image) else type(image)}") dst_h, dst_w = int(size[0]), int(size[1]) if tuple(image.shape[-2:]) == (dst_h, dst_w): return image.contiguous() resized = F.interpolate( image.unsqueeze(0).to(torch.float32), size=(dst_h, dst_w), mode="bicubic", align_corners=False, antialias=True, ) return resized[0].round().clamp(0, 255).to(torch.uint8).contiguous() def project_overlap_ratio( src_w2c: torch.Tensor, tgt_w2c: torch.Tensor, src_k: torch.Tensor, tgt_k: torch.Tensor, h: int, w: int, src_hw: tuple[int, int] | None = None, tgt_hw: tuple[int, int] | None = None, sample_h: int = 32, sample_w: int = 56, proxy_depth: float = 1.0, ) -> float: device = src_w2c.device src_h, src_w = tuple(int(v) for v in (src_hw or (h, w))) tgt_h, tgt_w = tuple(int(v) for v in (tgt_hw or (h, w))) ys = torch.linspace(0, src_h - 1, steps=sample_h, device=device) xs = torch.linspace(0, src_w - 1, steps=sample_w, device=device) vv, uu = torch.meshgrid(ys, xs, indexing="ij") u = uu.reshape(-1) v = vv.reshape(-1) fx, fy = src_k[0, 0], src_k[1, 1] cx, cy = src_k[0, 2], src_k[1, 2] x = (u - cx) / fx y = (v - cy) / fy z = torch.ones_like(x) rays = torch.stack([x, y, z], dim=-1) rays = rays / torch.norm(rays, dim=-1, keepdim=True).clamp(min=1e-6) pts_src = rays * float(proxy_depth) src_c2w = torch.linalg.inv(src_w2c) pts_src_h = torch.cat([pts_src, torch.ones_like(pts_src[:, :1])], dim=-1) pts_w = (src_c2w @ pts_src_h.T).T pts_tgt = (tgt_w2c @ pts_w.T).T xt, yt, zt = pts_tgt[:, 0], pts_tgt[:, 1], pts_tgt[:, 2].clamp(min=1e-6) ut = tgt_k[0, 0] * (xt / zt) + tgt_k[0, 2] vt = tgt_k[1, 1] * (yt / zt) + tgt_k[1, 2] inside = (zt > 0.0) & (ut >= 0.0) & (ut <= float(tgt_w - 1)) & (vt >= 0.0) & (vt <= float(tgt_h - 1)) return float(inside.float().mean().item()) def select_targets_for_source( *, src_idx: int, candidate_indices: list[int], centers: torch.Tensor, min_index_gap: int, max_index_gap: int, pair_max_translation_m: float, pair_min_overlap: float, overlap_score_fn: Callable[[int, int], float], ) -> list[int]: src_c = centers[int(src_idx)] tgt_cands: list[int] = [] for j in candidate_indices: j = int(j) if j == int(src_idx): continue gap = abs(int(j) - int(src_idx)) if gap < int(min_index_gap) or gap > int(max_index_gap): continue trans = float(torch.norm(centers[j] - src_c, p=2).item()) if trans > float(pair_max_translation_m): continue if float(overlap_score_fn(int(src_idx), j)) >= float(pair_min_overlap): tgt_cands.append(j) return tgt_cands