scripts/evaluate_rgbd_replica_transfer.py
#!/usr/bin/env python3
"""Cross-dataset transfer to Replica of the frozen carriers (no training), used exactly once.
outputs/EXP-3D-RGBD-REPLICA-TRANSFER-V1/PLAN_BEFORE_REPLICA_USE.md (written before any model saw
Replica) fixes the design: the method M is V11 C1 inside HFILL8 (V12 C1 if V12 ran and reached
branch A on EVAL-V4); V11 C0 and the qualified Core A carrier V7 C1 are descriptive references.
Stages: `lock` (after V11, the EVAL-V4 replication and V12 if it runs) freezes the inputs and the
rule-selected method; `seal` builds every state from context frames only and seals every
prediction and geometric baseline before any query depth is read; `score`; `analyze`.
"""
# ruff: noqa: E501 -- Chinese scientific report prose
from __future__ import annotations
import argparse
import datetime
import functools
import hashlib
import importlib.util
import json
from pathlib import Path
import numpy as np
import torch
from mcss.dynamic.types import hash_scene_state
from mcss.geometry import transform_cameras
from mcss.measurements import FixedMeasurementRenderer
from mcss.mechanism_pilot import rgbd_completion_carrier as v11_carrier
from mcss.mechanism_pilot import rgbd_evidence_carrier as v5
from mcss.mechanism_pilot.readout_reanalysis import depth_metrics
from mcss.mechanism_pilot.rgbd_depth_bounds import context_depth_bounds
from mcss.types import Cameras
EXPERIMENT = "EXP-3D-RGBD-REPLICA-TRANSFER-V1"
PLAN = "PLAN_BEFORE_REPLICA_USE.md"
PLAN_SHA256 = "a3578c547a8bb659bf688e92e38063fb8f4ea7e9f3fb1a004c5b9118be4cda93"
V7 = Path("outputs/EXP-3D-RGBD-DEPTH-BOUNDS-V7")
V11 = Path("outputs/EXP-3D-RGBD-COMPLETION-V11")
V12 = Path("outputs/EXP-3D-RGBD-WIDTH-V12")
REPLICATION = Path("outputs/EXP-3D-RGBD-REPLICATION-EVAL-V4")
REPLICA = Path("outputs/EXP-3D-RGBD-REPLICA-DATA-V1")
SCRIPTS = Path(__file__).resolve().parent
REGIONS = ("ALL", "NEAR", "FAR")
def sha(path):
return hashlib.sha256(Path(path).read_bytes()).hexdigest()
def write(path, value):
path = Path(path)
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(json.dumps(value, indent=2, sort_keys=True, allow_nan=False) + "\n")
def read(path):
return json.loads(Path(path).read_text())
def module(name, filename):
spec = importlib.util.spec_from_file_location(name, SCRIPTS / filename)
loaded = importlib.util.module_from_spec(spec)
spec.loader.exec_module(loaded)
return loaded
@functools.cache
def host():
"""V11's frozen EVAL-V3 module: context loader, aggregation, bootstrap and V2 gate."""
return module("transfer_v11_eval", "evaluate_rgbd_completion_eval_v3.py")
@functools.cache
def inpainting():
"""INPAINT-V1's frozen module: ray cosine, harmonic/Telea filling, NEAR mask, label rule."""
return module("transfer_inpaint", "run_rgbd_inpaint_baselines.py")
def method_rule(v11=V11, v12=V12):
"""The plan's selection: V12 C1 only if V12 ran (it must when COMPLETION_GAIN > 0) with branch A."""
gain = float(read(v11 / "eval_v3_results.json")["COMPLETION_GAIN"]["mean"])
if gain > 0:
if read(v12 / "integrity.json")["status"] != "PASS":
raise PermissionError("V12 must be finalized before Replica is used")
branch = read(v12 / "eval_v4_results.json")["INTERPRETATION_BRANCH"]
return ("V12C1" if branch == "A" else "V11C1"), {
"v11_completion_gain": gain,
"v12_branch": branch,
}
return "V11C1", {"v11_completion_gain": gain, "v12_branch": None}
def carrier_specs(method, v7=V7, v11=V11, v12=V12):
"""{name: (selected file, variant key, loader, state builder)} of every carrier rendered."""
specs = {
"V7C1": (v7 / "selected_checkpoints.json", "C1", v5.load_checkpoint, v5.build_state),
"V11C0": (
v11 / "selected_checkpoints.json",
"C0",
v11_carrier.load_checkpoint,
v11_carrier.build_state,
),
"V11C1": (
v11 / "selected_checkpoints.json",
"C1",
v11_carrier.load_checkpoint,
v11_carrier.build_state,
),
}
if method == "V12C1":
from mcss.mechanism_pilot import rgbd_width_carrier as v12_module
specs["V12C1"] = (
v12 / "selected_checkpoints.json",
"C1",
v12_module.load_checkpoint,
v12_module.build_state,
)
return specs
def inputs(root, method, replica=REPLICA, v7=V7, v11=V11, v12=V12, replication=REPLICATION):
paths = {
"protocol": root / "PROTOCOL.md",
"plan": root / PLAN,
"script": Path(__file__).resolve(),
"v11_eval_script": SCRIPTS / "evaluate_rgbd_completion_eval_v3.py",
"inpaint_script": SCRIPTS / "run_rgbd_inpaint_baselines.py",
"geometric_script": SCRIPTS / "run_rgbd_geometric_baselines.py",
"replica_manifest": replica / "manifest_replica.json",
"replica_integrity": replica / "preparation_integrity.json",
"replica_lock": replica / "candidate_lock.json",
"v11_contract": v11 / "training_contract.json",
"v11_eval_v3_results": v11 / "eval_v3_results.json",
"replication_results": replication / "replication_results.json",
}
for name, (selected, _, _, _) in carrier_specs(method, v7, v11, v12).items():
paths[f"{name}_selected"] = selected
if method == "V12C1":
paths["v12_eval_v4_results"] = v12 / "eval_v4_results.json"
return paths
def lock(root, replica=REPLICA, v7=V7, v11=V11, v12=V12, replication=REPLICATION):
root = Path(root).resolve()
if root.name != EXPERIMENT:
raise PermissionError("Exact experiment directory required")
if (root / "lock.json").exists():
raise FileExistsError("Never re-lock")
if not (root / "PROTOCOL.md").is_file():
raise PermissionError("Protocol text required before locking")
if sha(root / PLAN) != PLAN_SHA256:
raise PermissionError("The pre-Replica plan changed")
if read(v11 / "integrity.json")["status"] != "PASS":
raise PermissionError("V11 must be finalized")
if not (replication / "replication_results.json").exists():
raise PermissionError("The EVAL-V4 replication must be complete before Replica is used")
if read(replica / "preparation_integrity.json")["status"] != "PASS":
raise PermissionError("Replica data must be prepared with integrity PASS")
if (root / "raw").exists():
raise PermissionError("No prediction may exist before the lock")
method, basis = method_rule(v11, v12)
paths = inputs(root, method, replica, v7, v11, v12, replication)
write(
root / "lock.json",
{
"status": "FROZEN_BEFORE_ANY_REPLICA_PREDICTION",
"experiment": EXPERIMENT,
"created_utc": datetime.datetime.now(datetime.UTC).isoformat(),
"method": method,
"method_basis": basis,
"carriers": sorted(carrier_specs(method, v7, v11, v12)),
"primary": f"AbsRel(REPROJ_HARMONIC) - AbsRel(HFILL8 of {method}) on Replica (INPAINT-V1 label rule)",
"input_sha256": {
name: {"path": str(Path(p).resolve()), "sha256": sha(p)}
for name, p in paths.items()
},
"replica_media_read_by_a_model_before_lock": False,
"final_holdout_opened": False,
},
)
print("LOCKED", EXPERIMENT, "method", method, flush=True)
def verify(root):
lock_ = read(root / "lock.json")
for name, entry in lock_["input_sha256"].items():
if sha(entry["path"]) != entry["sha256"]:
raise PermissionError(f"Locked input changed: {name}")
return lock_
def load_carriers(method, seeds, v7=V7, v11=V11, v12=V12):
models = {}
for name, (selected_file, variant, loader, builder) in carrier_specs(
method, v7, v11, v12
).items():
selected = read(selected_file)[variant]
for seed in seeds:
item = selected[str(seed)]
if sha(Path(item["path"])) != item["sha256"]:
raise PermissionError(f"{name} selected checkpoint changed")
model, _ = loader(item["path"], "cpu")
models[name, seed] = (model.eval(), builder)
return models
@torch.no_grad()
def replay(models, v7=V7, v11=V11, v12=V12):
"""Our construction/rendering on the first DEV scene must reproduce each source's sealed DEV depth."""
sealed = {
"V7C1": v7 / "raw/selected_dev" / "C1_{seed}",
"V11C0": v11 / "raw/selected_dev" / "C0_{seed}",
"V11C1": v11 / "raw/selected_dev" / "C1_{seed}",
"V12C1": v12 / "raw/selected_dev" / "C1_{seed}",
}
manifest_path = v7 / "scene_split.json"
manifest = read(manifest_path)
record = min(
(r for r in manifest["scenes"] if r["split"] == "DEV"), key=lambda r: r["scene_id"]
)
sid, stage = record["scene_id"], host()
geo = stage.geometry()
renderer = FixedMeasurementRenderer(n_samples=64, ray_chunk_size=2048)
worst, compared = 0.0, 0
for role, context in stage.context_of(record, manifest, manifest_path.parent, []).items():
bounds = context_depth_bounds(context, context[0].camera.c2w)
for (name, seed), (model, builder) in models.items():
state, state_anchor = builder(model, context, bounds, f"replay:{sid}:{role}")
for qid in record["roles"]["primary_query"]:
camera = geo.query_camera(record, qid, manifest["image_size"])
batched = Cameras(
camera.intrinsics[None, None], camera.c2w[None, None], camera.image_size
)
local = transform_cameras(batched, torch.linalg.inv(state_anchor))
mine = renderer(state, local, ("depth",))["depth"][0, 0, 0].numpy()
directory = Path(str(sealed[name]).format(seed=seed))
theirs = np.load(directory / "predictions" / f"{sid}_{role}_{qid}_direct.npz")[
"depth"
]
worst = max(worst, float(np.max(np.abs(mine.astype(np.float64) - theirs))))
compared += 1
if worst > 1e-4:
raise AssertionError(f"DEV replay of the sealed carrier predictions failed: {worst} m")
return {"dev_scene": sid, "arrays_compared": compared, "max_abs_depth_deviation_m": worst}
@torch.no_grad()
def seal(root, v7=V7, v11=V11, v12=V12):
root = Path(root).resolve()
lock_ = verify(root)
out = root / "raw"
if (out / "prediction_seal.json").exists():
raise FileExistsError("Replica predictions are sealed once")
contract = read(lock_["input_sha256"]["v11_contract"]["path"])
seeds, constant = contract["seeds"], contract["eval_v3"]["reprojection_fill_constant_m"]
manifest_path = Path(lock_["input_sha256"]["replica_manifest"]["path"])
manifest = read(manifest_path)
if any(r["split"] != "REPLICA" for r in manifest["scenes"]):
raise PermissionError("Replica records only")
models = load_carriers(lock_["method"], seeds, v7, v11, v12)
replayed = replay(models, v7, v11, v12)
stage, inp = host(), inpainting()
geo = stage.geometry()
renderer = FixedMeasurementRenderer(n_samples=64, ray_chunk_size=2048)
access, entries, states = [], {}, {}
for record in manifest["scenes"]:
sid = record["scene_id"]
for role, context in stage.context_of(
record, manifest, manifest_path.parent, access
).items():
anchor = context[0].camera.c2w
bounds = context_depth_bounds(context, anchor)
points = geo.context_points(context)
built = {}
for (name, seed), (model, builder) in models.items():
state, state_anchor = builder(model, context, bounds, f"replica:{sid}:{role}")
built[name, seed] = (state, state_anchor)
states[f"{name}:{seed}:{sid}/{role}"] = hash_scene_state(state)
for qid in record["roles"]["primary_query"]:
access.append({"scene_id": sid, "frame_id": qid, "purpose": "QUERY_CAMERA_ONLY"})
camera = geo.query_camera(record, qid, manifest["image_size"])
depth, hit = geo.reproject(points, camera)
reproj = geo.fill_nearest(depth, hit, constant) if hit.any() else None
if reproj is None:
reproj = np.full(camera.image_size, constant, dtype=np.float64)
cosine = inp.ray_cosine(camera)
telea, fallback = inp.telea_fill(depth, hit, cosine, constant)
arrays = {
"REPROJ_NN": reproj,
"hit": hit,
"near": inp.near_mask(hit),
"REPROJ_HARMONIC": inp.harmonic_fill(depth, hit, cosine, constant),
"REPROJ_TELEA": telea,
"telea_fallback": np.array(fallback),
}
batched = Cameras(
camera.intrinsics[None, None], camera.c2w[None, None], camera.image_size
)
for (name, seed), (state, state_anchor) in built.items():
local = transform_cameras(batched, torch.linalg.inv(state_anchor))
pred = renderer(state, local, ("depth",))["depth"][0, 0, 0].numpy()
arrays[f"{name}_{seed}"] = pred.astype(np.float64)
path = out / "predictions" / f"{sid}_{role}_{qid}.npz"
path.parent.mkdir(parents=True, exist_ok=True)
np.savez_compressed(path, **arrays)
entries[f"{sid}/{role}/{qid}"] = {"path": str(path), "sha256": sha(path)}
if any(
"depth" in e.get("channels", []) and e.get("purpose") != "CONTEXT_ONLY_RGBD" for e in access
):
raise AssertionError("Only context depth may be read before the seal")
write(out / "state_hashes.json", states)
write(out / "GT_access_before_seal.json", access)
write(
out / "prediction_seal.json",
{
"predictions": entries,
"state_hashes_sha256": sha(out / "state_hashes.json"),
"query_depth_read": False,
"near_px": inp.NEAR_PX,
"reprojection_fill_constant_m": constant,
"replay_on_dev": replayed,
},
)
print("REPLICA_PREDICTIONS_SEALED", len(entries), json.dumps(replayed), flush=True)
def score(root):
root = Path(root).resolve()
lock_ = read(root / "lock.json")
seal_file = read(root / "raw" / "prediction_seal.json")
manifest = read(lock_["input_sha256"]["replica_manifest"]["path"])
seeds = read(lock_["input_sha256"]["v11_contract"]["path"])["seeds"]
constant = seal_file["reprojection_fill_constant_m"]
rows, access = [], []
for record in manifest["scenes"]:
sid = record["scene_id"]
frames = {f["frame_id"]: f for f in record["frames"]}
for role in ("A", "B"):
for qid in record["roles"]["primary_query"]:
entry = seal_file["predictions"][f"{sid}/{role}/{qid}"]
if sha(entry["path"]) != entry["sha256"]:
raise PermissionError("A sealed prediction changed")
arrays = dict(np.load(entry["path"]))
access.append(
{"scene_id": sid, "frame_id": qid, "purpose": "POST_SEAL_QUERY_DEPTH"}
)
gt = np.load(frames[qid]["depth"]).squeeze().astype(np.float64)
valid = np.isfinite(gt) & (gt > 0)
near = arrays["near"].astype(bool)
masks = {"ALL": valid, "NEAR": valid & near, "FAR": valid & ~near}
predictions = {
(name, 0): arrays[name]
for name in ("REPROJ_NN", "REPROJ_HARMONIC", "REPROJ_TELEA")
}
predictions["CONSTANT", 0] = np.full(gt.shape, constant, dtype=np.float64)
for name in lock_["carriers"]:
for seed in seeds:
carrier = arrays[f"{name}_{seed}"]
predictions[name, seed] = carrier
predictions[f"HFILL8_{name}", seed] = np.where(
near, arrays["REPROJ_HARMONIC"], carrier
)
predictions[f"FILL8_{name}", seed] = np.where(
near, arrays["REPROJ_NN"], carrier
)
for (method, seed), value in predictions.items():
row = {
"scene_id": sid,
"role": role,
"query_id": qid,
"method": method,
"seed": seed,
}
for region, mask in masks.items():
row[f"absrel_{region}"] = (
float(np.mean(np.abs(value[mask] - gt[mask]) / gt[mask]))
if mask.any()
else None
)
row["depth_absrel"] = depth_metrics(value, gt)["depth_absrel"]
row["far_fraction"] = float(masks["FAR"].sum() / max(valid.sum(), 1))
rows.append(row)
write(root / "raw" / "query_rows.json", rows)
write(root / "raw" / "GT_access_after_seal.json", access)
print("REPLICA_SCORED", len(rows), flush=True)
return rows
def contrasts(method):
return {
"HARMONIC_HYBRID_GAIN": ("REPROJ_HARMONIC", "ALL", f"HFILL8_{method}", "ALL"),
"HARMONIC_FAR_GAIN": ("REPROJ_HARMONIC", "FAR", method, "FAR"),
"CONSTANT_GAIN": ("CONSTANT", "ALL", method, "ALL"),
"NN_HYBRID_GAIN": ("REPROJ_NN", "ALL", f"FILL8_{method}", "ALL"),
"WIDTH_FAR_GAIN": ("V11C0", "FAR", method, "FAR"),
"V7C1_CONSTANT_GAIN": ("CONSTANT", "ALL", "V7C1", "ALL"),
"V7C1_REPROJ_GAIN": ("REPROJ_NN", "ALL", "V7C1", "ALL"),
"NN_MINUS_HARMONIC_ALL": ("REPROJ_NN", "ALL", "REPROJ_HARMONIC", "ALL"),
"TELEA_HYBRID_GAIN": ("REPROJ_TELEA", "ALL", f"HFILL8_{method}", "ALL"),
}
def analyze_rows(rows, method):
stage, label = host(), inpainting().geometry().label
methods = sorted({r["method"] for r in rows})
def values(name, region):
return stage.scene_values(rows, name, f"absrel_{region}")
block = {
"absolute_absrel": {
m: {region: stage.summary(values(m, region)) for region in REGIONS} for m in methods
},
"contrasts": {},
"n_scenes": len({r["scene_id"] for r in rows}),
"far_pixel_fraction": float(
np.mean([r["far_fraction"] for r in rows if r["method"] == "REPROJ_NN"])
),
"method": method,
}
for name, (a, ra, b, rb) in contrasts(method).items():
stats = stage.contrast(values(a, ra), values(b, rb))
entry = {"gain": stats, "definition": f"AbsRel {ra}({a}) - AbsRel {rb}({b})"}
if stats is not None:
entry["label"] = label(stats)
entry["strict_gate"], entry["strict_checks"] = stage.gate(stats)
block["contrasts"][name] = entry
primary = block["contrasts"]["HARMONIC_HYBRID_GAIN"]
block["PRIMARY_LABEL"] = primary.get("label", "UNDEFINED")
block["PRIMARY_STRICT_GATE"] = primary.get("strict_gate", "UNDEFINED")
block["INTERPRETATION_BRANCH"] = {"ABOVE": "A", "NOT_DISTINGUISHABLE": "B", "BELOW": "C"}.get(
primary.get("label"), "UNDEFINED"
)
return block
def analyze(root):
root = Path(root).resolve()
lock_ = read(root / "lock.json")
rows_path = root / "raw" / "query_rows.json"
result = analyze_rows(read(rows_path), lock_["method"])
result["input_sha256"] = {str(rows_path): sha(rows_path)}
result["final_holdout_opened"] = False
write(root / "transfer_results.json", result)
report(root, result)
return result
def fmt(stats):
if not stats:
return "UNDEFINED"
lo, hi = stats["ci95"]
return f"{stats['mean']:+.4f} [{lo:+.4f}, {hi:+.4f}],场景 {stats['positive']}/{stats['tied']}/{stats['negative']}"
def report(root, result):
method = result["method"]
lines = [
f"# {EXPERIMENT}",
"",
f"跨数据集迁移:冻结的 carrier 不经任何训练,直接用于 Replica({result['n_scenes']} 个场景,FAR 像素占 {result['far_pixel_fraction']:.1%})。被检验的方法按事先写定的规则选为 {method}(放在 HFILL8 组合中)。所有预测在读取 query 深度之前封存。",
"",
f"主对比:AbsRel(REPROJ_HARMONIC) − AbsRel(HFILL8 {method}) = {fmt(result['contrasts']['HARMONIC_HYBRID_GAIN']['gain'])};标签 {result['PRIMARY_LABEL']},严格 gate {result['PRIMARY_STRICT_GATE']},分支 {result['INTERPRETATION_BRANCH']}。",
"",
"| 方法 | ALL | NEAR | FAR |",
"|---|---:|---:|---:|",
]
for name in (
"CONSTANT",
"REPROJ_NN",
"REPROJ_HARMONIC",
"V7C1",
"V11C0",
"V11C1",
"V12C1",
f"FILL8_{method}",
f"HFILL8_{method}",
):
cells = [result["absolute_absrel"].get(name, {}).get(r) for r in REGIONS]
if any(cells):
lines.append(
f"| {name} | "
+ " | ".join("—" if not c else f"{c['mean']:.4f}" for c in cells)
+ " |"
)
lines += ["", "| 对比 | 定义 | 差值 [95% CI],场景 好/平/差 | 标签 |", "|---|---|---|---|"]
for name, entry in result["contrasts"].items():
lines.append(
f"| {name} | {entry['definition']} | {fmt(entry['gain'])} | {entry.get('label', 'UNDEFINED')} |"
)
lines += [
"",
"说明:Replica 与 Hypersim 没有任何共享的场景、资产或相机;只有 8 个场景,功效有限;这是它唯一一次被使用;受保护的 Hypersim final holdout 未打开。",
]
(root / "README.md").write_text("\n".join(lines) + "\n")
summary = [
"RGBD REPLICA TRANSFER V1",
f"METHOD={method}",
f"PRIMARY_LABEL={result['PRIMARY_LABEL']}",
f"PRIMARY_STRICT_GATE={result['PRIMARY_STRICT_GATE']}",
f"INTERPRETATION_BRANCH={result['INTERPRETATION_BRANCH']}",
f"N_SCENES={result['n_scenes']}",
]
for name, entry in result["contrasts"].items():
if entry["gain"]:
summary.append(
f"{name}={entry['gain']['mean']:.6f} CI={entry['gain']['ci95']} LABEL={entry['label']}"
)
summary.append("FINAL_HOLDOUT_TOUCHED=false")
(root / "terminal_summary.txt").write_text("\n".join(summary) + "\n")
print("\n".join(summary), flush=True)
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--stage", choices=("lock", "seal", "score", "analyze"), required=True)
parser.add_argument("--root", type=Path, default=Path("outputs") / EXPERIMENT)
args = parser.parse_args()
torch.set_num_threads(1)
{"lock": lock, "seal": seal, "score": score, "analyze": analyze}[args.stage](args.root)
if __name__ == "__main__":
main()