mapvggt / mapgs /eval /robustness.py
ChenmingWu's picture
Upload folder using huggingface_hub
b2efbe4 verified
Raw
History Blame Contribute Delete
3.12 kB
"""Robustness / generalization studies (§4.6).
* pose noise : inject up to N degrees of rotation (+ optional translation) on the
non-reference context cameras and measure the reconstruction drop. MAGT's
map-anchored scaffold should degrade more gracefully than pixel-aligned methods.
* map noise : reuse :class:`mapgs.data.MapNoise` to build an imperfect map
(height noise / lane offsets / dropped segments) and re-evaluate — validates
the *weak-supervision* claim (no hard map dependence).
* cross-dataset: train on one dataset, evaluate (zero-shot / finetuned) on another
via the dataset adapters; map structure should transfer.
"""
from __future__ import annotations
import math
from dataclasses import replace
from typing import Dict, List, Optional
import torch
from mapgs.eval.metrics import psnr, ssim
from mapgs.train.render import render_scene_views, extract_scene_dynamic
def _random_small_rotation(max_deg: float, device) -> torch.Tensor:
axis = torch.randn(3, device=device)
axis = axis / axis.norm().clamp_min(1e-8)
ang = math.radians(max_deg) * float(torch.empty(1).uniform_(-1, 1))
x, y, z = axis
c, s, C = math.cos(ang), math.sin(ang), 1 - math.cos(ang)
return torch.tensor([
[c + x * x * C, x * y * C - z * s, x * z * C + y * s],
[y * x * C + z * s, c + y * y * C, y * z * C - x * s],
[z * x * C - y * s, z * y * C + x * s, c + z * z * C],
], device=device)
def perturb_context_poses(sample, rot_deg: float, trans: float = 0.0, device="cuda"):
"""Return a copy of ``sample`` with noisy non-reference (idx>0) context poses."""
c2w = sample.ctx_c2w.clone().to(device)
for v in range(1, c2w.shape[0]):
R = _random_small_rotation(rot_deg, device)
c2w[v, :3, :3] = R @ c2w[v, :3, :3]
if trans > 0:
c2w[v, :3, 3] = c2w[v, :3, 3] + trans * torch.randn(3, device=device)
return replace(sample, ctx_c2w=c2w.cpu())
@torch.no_grad()
def pose_noise_sweep(evaluator, dataset, rot_degs: Optional[List[float]] = None,
trans: float = 0.0, max_scenes: int = 20) -> Dict[float, Dict[str, float]]:
rot_degs = rot_degs or [0.0, 2.0, 5.0, 10.0]
cfg = evaluator.cfg
H, W = cfg.data.height, cfg.data.width
out: Dict[float, Dict[str, float]] = {}
n = min(len(dataset), max_scenes)
for rd in rot_degs:
ps, ss = [], []
for i in range(n):
s = dataset[i]
s = s if rd == 0.0 else perturb_context_poses(s, rd, trans, evaluator.device)
g, dyn = evaluator._decode(s)
rend = render_scene_views(evaluator.model, evaluator.ras, g, dyn,
s.sup_K.to(evaluator.device), s.sup_c2w.to(evaluator.device),
s.sup_frame.to(evaluator.device), H, W, evaluator.model.uses_features)
gt = s.sup_images.to(evaluator.device)
ps.append(float(psnr(rend["rgb"], gt)))
ss.append(float(ssim(rend["rgb"], gt)))
out[rd] = {"PSNR": sum(ps) / len(ps), "SSIM": sum(ss) / len(ss), "n": n}
return out