blur-slam-bpn-code / scripts /diag_e1_bpn_kernel_vs_raw.py
zhaoshiwen's picture
Initial upload: BPN deblur pipeline code (scripts, triangle-splatting, BAGS, EVSSM forks)
c75b162 verified
Raw
History Blame Contribute Delete
6.22 kB
#!/usr/bin/env python3
"""Diagnostic for the BPN supervision-target fix (E1, tum_fr1_desk).
Checks whether, for the 47 "nonsharp" frames where loss_blur was active
during training, the learned BPN kernel actually moved the rendered image
TOWARDS the RAW (motion-blurred) source frame:
PSNR(render_sharp, RAW) -- baseline: how far the sharp 3D render
already is from the blurry photo
PSNR(BPN(render_sharp), RAW) -- after applying the learned kernel
If the kernel learned something meaningful, BPN(render_sharp) should be
CLOSER to RAW (higher PSNR / lower L1) than render_sharp is. Also reports
mask.mean() (how much blending BPN applies) and both renders' distance to
the EVSSM target, for context.
Run with: conda run -n trigsplat --cwd <repo> python <this_script>
"""
import os, sys, json, glob
import numpy as np
import cv2
import torch
from skimage.metrics import structural_similarity as ssim_fn
from skimage.metrics import peak_signal_noise_ratio as psnr_fn
REPO_TRI = "/srv2/szha0669/blur_slam_exp/repos/triangle-splatting"
sys.path.insert(0, REPO_TRI)
os.chdir(REPO_TRI)
from scene import Scene, TriangleModel
from triangle_renderer import render
from arguments import ModelParams, PipelineParams, get_combined_args
from train_bpn import build_bpn_modules, apply_bpn_blur
from argparse import ArgumentParser
BASE = "/home/szha0669/storage/blur_slam_exp"
SCENE_NAME = "tum_fr1_desk"
ITERATION = 30000
MODEL_PATH = f"{BASE}/outputs/trigsplat_i2slam_gtall_30k_colmappose_sparsedepth_E1/{SCENE_NAME}"
DATA = f"{BASE}/data/i2slam_trigsplat/tum_fr1_desk_abl1"
SHARP_JSON = f"{BASE}/outputs/logs/tum_fr1desk_sharp_frames.json"
RAW_GLOB = f"{BASE}/data/TUM_RGBD/rgbd_dataset_freiburg1_desk/rgb/*.png"
STRIDE = 10
sys.argv = ["diag.py", "-m", MODEL_PATH, "-s", DATA, "--images", "images", "-r", "2",
"--iteration", str(ITERATION)]
parser = ArgumentParser()
model = ModelParams(parser, sentinel=True)
pipeline = PipelineParams(parser)
parser.add_argument("--iteration", default=-1, type=int)
args = get_combined_args(parser)
dataset = model.extract(args)
pipe = pipeline.extract(args)
bg_color = [1, 1, 1] if dataset.white_background else [0, 0, 0]
background = torch.tensor(bg_color, dtype=torch.float32, device="cuda")
triangles = TriangleModel(dataset.sh_degree)
scene = Scene(args=dataset, triangles=triangles, init_opacity=None, init_size=None,
nb_points=None, set_sigma=None, no_dome=False,
load_iteration=args.iteration, shuffle=False)
train_cameras = scene.getTrainCameras()
n_cams = len(train_cameras)
cam_index_by_name = {cam.image_name: idx for idx, cam in enumerate(train_cameras)}
h, w = int(scene.orig_h), int(scene.orig_w)
# cfg_args on disk only stores the ModelParams group (not train_bpn.py's custom
# BPN args), so the kernel sizes used for E1 training are hardcoded here.
ks1, ks2, ks3, ks_ss = 5, 9, 21, 21
mlp_ms, mlp_ss = build_bpn_modules(n_cams, h, w, ks1, ks2, ks3, ks_ss, args)
bpn_ckpt = torch.load(os.path.join(MODEL_PATH, f"bpn_{ITERATION}.pth"), map_location="cuda")
mlp_ms.load_state_dict(bpn_ckpt["mlp_ms"])
mlp_ss.load_state_dict(bpn_ckpt["mlp_ss"])
mlp_ms.eval()
mlp_ss.eval()
bpn = {
"mlp_ms": mlp_ms, "mlp_ss": mlp_ss,
"ks1": ks1, "ks2": ks2, "ks3": ks3, "ks_ss": ks_ss,
"blur_chunk_rows": 96,
"no_curriculum": getattr(args, "no_bpn_curriculum", False),
}
sharp_set = set(json.load(open(SHARP_JSON)))
raw_paths = sorted(glob.glob(RAW_GLOB))
def to_np(t):
return (torch.clamp(t, 0, 1).detach().cpu().numpy().transpose(1, 2, 0) * 255).astype(np.uint8)
rows = []
with torch.no_grad():
for cam in train_cameras:
if cam.image_name in sharp_set:
continue
render_pkg = render(cam, triangles, pipe, background)
render_sharp = render_pkg["render"]
depth = render_pkg.get("surf_depth", torch.zeros_like(render_sharp[:1]))
cam_idx = cam_index_by_name[cam.image_name]
blur_image, mask, _ = apply_bpn_blur(render_sharp, depth, cam_idx, ITERATION, bpn)
raw_idx = int(cam.image_name) * STRIDE
raw_img = cv2.imread(raw_paths[raw_idx])
raw_img = cv2.cvtColor(raw_img, cv2.COLOR_BGR2RGB)
raw_img = cv2.resize(raw_img, (cam.image_width, cam.image_height), interpolation=cv2.INTER_LANCZOS4)
evssm = to_np(cam.original_image)
sharp_np = to_np(render_sharp)
blur_np = to_np(blur_image)
row = dict(
frame=cam.image_name,
mask_mean=float(mask.mean().item()),
psnr_sharp_raw=float(psnr_fn(raw_img, sharp_np, data_range=255)),
psnr_blur_raw=float(psnr_fn(raw_img, blur_np, data_range=255)),
ssim_sharp_raw=float(ssim_fn(raw_img, sharp_np, channel_axis=2, data_range=255)),
ssim_blur_raw=float(ssim_fn(raw_img, blur_np, channel_axis=2, data_range=255)),
psnr_sharp_evssm=float(psnr_fn(evssm, sharp_np, data_range=255)),
psnr_blur_evssm=float(psnr_fn(evssm, blur_np, data_range=255)),
l1_sharp_blur=float(np.abs(sharp_np.astype(np.float32) - blur_np.astype(np.float32)).mean()),
)
rows.append(row)
print(f"{row['frame']} mask={row['mask_mean']:.4f} "
f"PSNR(sharp,RAW)={row['psnr_sharp_raw']:.3f} PSNR(blur,RAW)={row['psnr_blur_raw']:.3f} "
f"d_raw={row['psnr_blur_raw']-row['psnr_sharp_raw']:+.3f} "
f"PSNR(sharp,EVSSM)={row['psnr_sharp_evssm']:.3f} PSNR(blur,EVSSM)={row['psnr_blur_evssm']:.3f} "
f"|sharp-blur|={row['l1_sharp_blur']:.3f}")
n = len(rows)
agg = {k: float(np.mean([r[k] for r in rows])) for k in rows[0] if k != "frame"}
print(f"\n=== aggregate over {n} nonsharp frames ===")
for k, v in agg.items():
print(f" {k:20s} = {v:.4f}")
print(f"\nDelta PSNR vs RAW (blur - sharp): {agg['psnr_blur_raw'] - agg['psnr_sharp_raw']:+.4f} dB "
f"({'kernel moved render TOWARDS raw blur' if agg['psnr_blur_raw'] > agg['psnr_sharp_raw'] else 'kernel did NOT help match raw blur'})")
out = f"{BASE}/outputs/logs/diag_e1_bpn_kernel_vs_raw.json"
json.dump({"per_frame": rows, "aggregate": agg}, open(out, "w"), indent=1)
print(f"\nSaved -> {out}")