mapvggt / mapgs /losses /mapdepth.py
ChenmingWu's picture
Upload folder using huggingface_hub
b2efbe4 verified
Raw
History Blame Contribute Delete
2.62 kB
"""Map-guided geometry supervision (§2.6 item 2) + map-ground coupling (§2.5).
GS-native, expressed on the alpha-composited GS depth (no NeRF ray integral):
L_mapdepth = sum_{p in Omega^g} Gamma(p) * Huber(D_hat(p) - D^map(p))
+ lambda_fs * sum_{g: z_g < h(x_g,y_g) - delta} sigma_g
with the MapNeRF self-paced weight Gamma(p) = exp(-|D_hat - D^map| / (2 eps(t))).
With small eps only pixels already agreeing with the map get weight (obstacles /
gross map errors auto-down-weighted); eps tempers upward to broaden support.
Gamma is used as a *weight* (detached). The second term is the GS analog of
MapNeRF's sub-ground density penalty: opacity penalty on Gaussians below ground.
"""
from __future__ import annotations
import torch
from mapgs.hdmap.ground_field import GroundField
from mapgs.render.gaussians import Gaussians, GROUP_DYNAMIC
def huber(x: torch.Tensor, delta: float) -> torch.Tensor:
a = x.abs()
quad = 0.5 * x ** 2
lin = delta * (a - 0.5 * delta)
return torch.where(a <= delta, quad, lin)
def mapdepth_loss(
pred_depth: torch.Tensor, # [V, H, W]
map_depth: torch.Tensor, # [V, H, W]
ground_mask: torch.Tensor, # [V, H, W] bool (Omega^g)
eps: float,
delta: float = 0.5,
) -> torch.Tensor:
resid = pred_depth - map_depth
gamma = torch.exp(-resid.abs() / (2 * max(float(eps), 1e-6))).detach() # self-paced weight
l = huber(resid, delta)
m = ground_mask.float()
denom = m.sum().clamp_min(1.0)
return (gamma * l * m).sum() / denom
def free_space_loss(
gaussians: Gaussians,
ground: GroundField,
delta: float = 0.15,
) -> torch.Tensor:
"""Opacity penalty on Gaussians whose centers lie below the ground surface."""
z = gaussians.means[:, 2]
xy = gaussians.means[:, :2]
h, valid = ground.height_at(xy)
below = (z < (h - delta)) & valid
bf = below.float()
denom = bf.sum().clamp_min(1.0)
return (gaussians.opacities * bf).sum() / denom
def ground_coupling_loss(
gaussians: Gaussians,
ground: GroundField,
eps: float,
delta: float = 0.5,
) -> torch.Tensor:
"""Soft, Gamma-weighted pull of dynamic Gaussian z toward map ground height (§2.5)."""
dyn = gaussians.group == GROUP_DYNAMIC
if not dyn.any():
return gaussians.means.sum() * 0.0
z = gaussians.means[dyn, 2]
xy = gaussians.means[dyn, :2]
h, valid = ground.height_at(xy)
resid = z - h
gamma = torch.exp(-resid.abs() / (2 * eps)).detach()
vf = valid.float()
denom = vf.sum().clamp_min(1.0)
return (vf * gamma * huber(resid, delta)).sum() / denom