Fix DeMemWM revisit metric import
Browse files- algorithms/dememwm/df_video.py +156 -2
algorithms/dememwm/df_video.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
| 1 |
-
from collections.abc import Mapping
|
| 2 |
import csv
|
|
|
|
| 3 |
import os
|
| 4 |
import random
|
| 5 |
import time
|
|
@@ -27,7 +28,6 @@ from datasets.video.memory_selection import (
|
|
| 27 |
_select_revisit,
|
| 28 |
)
|
| 29 |
from utils.logging_utils import log_video, get_validation_metrics_for_videos
|
| 30 |
-
from algorithms.worldmem.revisit_consistency import RevisitPair, compute_frame_metric_rows
|
| 31 |
from .df_base import DiffusionForcingBase
|
| 32 |
from .models.vae import VAE_models
|
| 33 |
from .models.diffusion import Diffusion
|
|
@@ -42,6 +42,160 @@ _DEMEMWM_GEOMETRY_PROJ_MARKERS = (".query_pose_proj.", ".key_pose_proj.", ".time
|
|
| 42 |
_DEMEMWM_ADALN_MLP_MARKERS = (".r_adaLN_modulation.", ".r_mlp.")
|
| 43 |
|
| 44 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 45 |
def _cfg_get(cfg, key: str, default=None):
|
| 46 |
if cfg is None:
|
| 47 |
return default
|
|
|
|
| 1 |
+
from collections.abc import Mapping, Sequence
|
| 2 |
import csv
|
| 3 |
+
from dataclasses import dataclass
|
| 4 |
import os
|
| 5 |
import random
|
| 6 |
import time
|
|
|
|
| 28 |
_select_revisit,
|
| 29 |
)
|
| 30 |
from utils.logging_utils import log_video, get_validation_metrics_for_videos
|
|
|
|
| 31 |
from .df_base import DiffusionForcingBase
|
| 32 |
from .models.vae import VAE_models
|
| 33 |
from .models.diffusion import Diffusion
|
|
|
|
| 42 |
_DEMEMWM_ADALN_MLP_MARKERS = (".r_adaLN_modulation.", ".r_mlp.")
|
| 43 |
|
| 44 |
|
| 45 |
+
@dataclass(frozen=True)
|
| 46 |
+
class RevisitPair:
|
| 47 |
+
clip_id: str
|
| 48 |
+
source_index: int
|
| 49 |
+
target_index: int
|
| 50 |
+
source_frame: int
|
| 51 |
+
target_frame: int
|
| 52 |
+
gap: int
|
| 53 |
+
fov_overlap: float
|
| 54 |
+
plucker_overlap: float
|
| 55 |
+
position_distance: float
|
| 56 |
+
yaw_delta_deg: float
|
| 57 |
+
pitch_delta_deg: float
|
| 58 |
+
positional: bool
|
| 59 |
+
|
| 60 |
+
|
| 61 |
+
def _best_revisit_pair_by_target(pairs: Sequence[RevisitPair]) -> dict[int, RevisitPair]:
|
| 62 |
+
best: dict[int, RevisitPair] = {}
|
| 63 |
+
for pair in pairs:
|
| 64 |
+
rank = (float(pair.fov_overlap), float(pair.plucker_overlap), int(pair.gap), -int(pair.source_index))
|
| 65 |
+
current = best.get(int(pair.target_index))
|
| 66 |
+
if current is None:
|
| 67 |
+
best[int(pair.target_index)] = pair
|
| 68 |
+
continue
|
| 69 |
+
current_rank = (
|
| 70 |
+
float(current.fov_overlap),
|
| 71 |
+
float(current.plucker_overlap),
|
| 72 |
+
int(current.gap),
|
| 73 |
+
-int(current.source_index),
|
| 74 |
+
)
|
| 75 |
+
if rank > current_rank:
|
| 76 |
+
best[int(pair.target_index)] = pair
|
| 77 |
+
return best
|
| 78 |
+
|
| 79 |
+
|
| 80 |
+
def _metric_psnr_from_mse(mse: torch.Tensor) -> torch.Tensor:
|
| 81 |
+
return 10.0 * torch.log10(1.0 / mse.clamp_min(torch.finfo(mse.dtype).eps))
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
def _metric_central_crop(tensor: torch.Tensor, crop_fraction: float) -> torch.Tensor:
|
| 85 |
+
crop_fraction = float(crop_fraction)
|
| 86 |
+
if crop_fraction == 1.0:
|
| 87 |
+
return tensor
|
| 88 |
+
h = int(tensor.shape[-2])
|
| 89 |
+
w = int(tensor.shape[-1])
|
| 90 |
+
crop_h = max(1, int(round(h * crop_fraction)))
|
| 91 |
+
crop_w = max(1, int(round(w * crop_fraction)))
|
| 92 |
+
top = max(0, (h - crop_h) // 2)
|
| 93 |
+
left = max(0, (w - crop_w) // 2)
|
| 94 |
+
return tensor[..., top : top + crop_h, left : left + crop_w]
|
| 95 |
+
|
| 96 |
+
|
| 97 |
+
def _metric_lpips_scalar(lpips_model, pred: torch.Tensor, target: torch.Tensor) -> float:
|
| 98 |
+
if lpips_model is None:
|
| 99 |
+
return float("nan")
|
| 100 |
+
pred = torch.clamp(pred.float(), 0.0, 1.0)
|
| 101 |
+
target = torch.clamp(target.float(), 0.0, 1.0).to(device=pred.device)
|
| 102 |
+
if hasattr(lpips_model, "reset"):
|
| 103 |
+
lpips_model.reset()
|
| 104 |
+
with torch.no_grad():
|
| 105 |
+
try:
|
| 106 |
+
value = lpips_model(pred, target)
|
| 107 |
+
except TypeError:
|
| 108 |
+
lpips_model.update(pred, target)
|
| 109 |
+
value = lpips_model.compute()
|
| 110 |
+
if hasattr(lpips_model, "reset"):
|
| 111 |
+
lpips_model.reset()
|
| 112 |
+
if torch.is_tensor(value):
|
| 113 |
+
return float(value.detach().float().mean().cpu().item())
|
| 114 |
+
return float(value)
|
| 115 |
+
|
| 116 |
+
|
| 117 |
+
def compute_frame_metric_rows(
|
| 118 |
+
*,
|
| 119 |
+
branch: str,
|
| 120 |
+
clip_id: str,
|
| 121 |
+
pred: torch.Tensor,
|
| 122 |
+
gt: torch.Tensor | None,
|
| 123 |
+
poses: torch.Tensor | None,
|
| 124 |
+
pairs: Sequence[RevisitPair],
|
| 125 |
+
context_frames: int,
|
| 126 |
+
lpips_model=None,
|
| 127 |
+
self_crop_fraction: float = 0.50,
|
| 128 |
+
focal_length: float = 0.35,
|
| 129 |
+
compute_plucker_similarity: bool = True,
|
| 130 |
+
) -> list[dict[str, object]]:
|
| 131 |
+
if pred.ndim == 5:
|
| 132 |
+
pred = pred[:, 0]
|
| 133 |
+
if gt is not None and gt.ndim == 5:
|
| 134 |
+
gt = gt[:, 0]
|
| 135 |
+
pred = pred.detach()
|
| 136 |
+
gt = None if gt is None else gt.detach().to(device=pred.device)
|
| 137 |
+
|
| 138 |
+
best_pairs = _best_revisit_pair_by_target(pairs)
|
| 139 |
+
gt_psnr_values = None
|
| 140 |
+
if gt is not None:
|
| 141 |
+
gt_mse = (torch.clamp(pred.float(), 0.0, 1.0) - torch.clamp(gt.float(), 0.0, 1.0)).square()
|
| 142 |
+
gt_psnr_values = _metric_psnr_from_mse(gt_mse.flatten(start_dim=1).mean(dim=1)).detach().cpu().tolist()
|
| 143 |
+
|
| 144 |
+
rows: list[dict[str, object]] = []
|
| 145 |
+
for out_index in range(int(pred.shape[0])):
|
| 146 |
+
frame_index = int(context_frames) + out_index
|
| 147 |
+
pair = best_pairs.get(frame_index)
|
| 148 |
+
gt_lpips = float("nan")
|
| 149 |
+
gt_psnr = float("nan")
|
| 150 |
+
if gt is not None and gt_psnr_values is not None:
|
| 151 |
+
gt_psnr = float(gt_psnr_values[out_index])
|
| 152 |
+
gt_lpips = _metric_lpips_scalar(lpips_model, pred[out_index : out_index + 1], gt[out_index : out_index + 1])
|
| 153 |
+
|
| 154 |
+
self_psnr = float("nan")
|
| 155 |
+
self_lpips = float("nan")
|
| 156 |
+
source_index = ""
|
| 157 |
+
gap = ""
|
| 158 |
+
fov_overlap = ""
|
| 159 |
+
plucker_overlap = ""
|
| 160 |
+
self_status = "not_revisit"
|
| 161 |
+
if pair is not None:
|
| 162 |
+
source_index = int(pair.source_index)
|
| 163 |
+
gap = int(pair.gap)
|
| 164 |
+
fov_overlap = float(pair.fov_overlap)
|
| 165 |
+
plucker_overlap = float(pair.plucker_overlap)
|
| 166 |
+
source_out_index = int(pair.source_index) - int(context_frames)
|
| 167 |
+
if 0 <= source_out_index < int(pred.shape[0]):
|
| 168 |
+
pred_source = _metric_central_crop(pred[source_out_index : source_out_index + 1], self_crop_fraction)
|
| 169 |
+
pred_target = _metric_central_crop(pred[out_index : out_index + 1], self_crop_fraction)
|
| 170 |
+
self_mse = (torch.clamp(pred_source.float(), 0.0, 1.0) - torch.clamp(pred_target.float(), 0.0, 1.0)).square().mean()
|
| 171 |
+
self_psnr = float(_metric_psnr_from_mse(self_mse.reshape(1))[0].detach().cpu().item())
|
| 172 |
+
self_lpips = _metric_lpips_scalar(lpips_model, pred_target, pred_source)
|
| 173 |
+
self_status = "central_crop"
|
| 174 |
+
else:
|
| 175 |
+
self_status = "source_before_prediction_horizon"
|
| 176 |
+
|
| 177 |
+
rows.append(
|
| 178 |
+
{
|
| 179 |
+
"branch": str(branch),
|
| 180 |
+
"clip_id": str(clip_id),
|
| 181 |
+
"frame_index": int(frame_index),
|
| 182 |
+
"output_index": int(out_index),
|
| 183 |
+
"is_revisit": pair is not None,
|
| 184 |
+
"source_index": source_index,
|
| 185 |
+
"gap": gap,
|
| 186 |
+
"fov_overlap": fov_overlap,
|
| 187 |
+
"plucker_overlap": plucker_overlap,
|
| 188 |
+
"gt_lpips": gt_lpips,
|
| 189 |
+
"gt_psnr": gt_psnr,
|
| 190 |
+
"self_lpips": self_lpips,
|
| 191 |
+
"self_psnr": self_psnr,
|
| 192 |
+
"self_plucker_similarity": float("nan"),
|
| 193 |
+
"self_consistency_status": self_status,
|
| 194 |
+
}
|
| 195 |
+
)
|
| 196 |
+
return rows
|
| 197 |
+
|
| 198 |
+
|
| 199 |
def _cfg_get(cfg, key: str, default=None):
|
| 200 |
if cfg is None:
|
| 201 |
return default
|