Source: docs/experiments/EXP-3D-RGBD-FULL-SYSTEM-ARKIT-V1/exploratory_posthoc/eval_carrier_7view.py · Plain text

docs/experiments/EXP-3D-RGBD-FULL-SYSTEM-ARKIT-V1/exploratory_posthoc/eval_carrier_7view.py

"""Sealed predictions of V17-style carrier arms built from SEVEN views: the role's three context frames followed by
its four Core B stream frames, through the training-time extra-view path (rgbd_viewcount_carrier.with_extra_views,
the path that trained every V17-style carrier on 3-7 views). The bounds stay the CONTEXT_DEPTH bounds of the three
context frames, as in training. Everything else is the arms evaluator's: its contract (arms, seeds, cohort), its
context loader, its arm checkpoints (hash-checked) and its renderer. Only context and stream depth are read; no
query depth is read. Output: ROOT/raw/eval7/COHORT/ (predictions with <ARM>_<seed> depth and OPACITY_<ARM>_<seed>,
prediction_seal.json, state hashes and the access log). Run through cv_host_run.py from the repository root
(CUDA hidden):  python eval_carrier_7view.py ROOT COHORT
"""
import importlib.util
import json
import sys
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.rgbd_depth_bounds import context_depth_bounds
from mcss.mechanism_pilot.rgbd_stream_write import StreamRGBDLoader
from mcss.mechanism_pilot.rgbd_viewcount_carrier import with_extra_views
from mcss.mechanism_pilot.small_training import sha, write_json
from mcss.types import Cameras

ARMS = Path("scripts/evaluate_rgbd_carrier_arms.py")


def module(name, path):
    spec = importlib.util.spec_from_file_location(name, path)
    loaded = importlib.util.module_from_spec(spec)
    spec.loader.exec_module(loaded)
    return loaded


def main(root, cohort):
    arms_eval = module("arms_eval_7view", ARMS)
    config, cohorts = arms_eval.contract(root)
    entry, manifest, base = cohorts[cohort]
    out = root / "raw" / "eval7" / cohort
    if (out / "prediction_seal.json").exists():
        raise FileExistsError("7-view predictions are sealed once")
    frozen = arms_eval.frozen_v17_evaluation()
    v11 = frozen.host()
    geo = v11.geometry()
    models = arms_eval.arm_carriers(config)
    renderer = FixedMeasurementRenderer(n_samples=64, ray_chunk_size=2048)
    lock = json.loads((root / "frame_role_lock.json").read_text())["cohorts"][cohort]
    locked_streams = {(e["scene_id"], e["role"]): list(e["stream"]) for e in lock["episodes"]}
    ids = [r["scene_id"] for r in manifest["scenes"]]
    access, entries, states = [], {}, {}
    for record in manifest["scenes"]:
        sid = record["scene_id"]
        streams = StreamRGBDLoader(record, manifest["image_size"], base, "cpu", access, allowed_scene_ids=ids,
                                   holdout_scene_ids=manifest.get("protected_scene_ids", []))
        for role, context in v11.context_of(record, manifest, base, access).items():
            if list(streams.frame_ids(role)) != locked_streams[(sid, role)]:
                raise PermissionError(f"{sid}/{role}: stream differs from the frame-role lock")
            bounds = context_depth_bounds(context, context[0].camera.c2w)
            seven = with_extra_views(context, streams.stream(role), len(locked_streams[(sid, role)]))
            built = {}
            with torch.no_grad():
                for (arm, seed), carrier in models.items():
                    state, anchor = frozen.build_state(carrier, seven, bounds, f"{cohort.lower()}:{sid}:{role}:7view")
                    built[arm, seed] = (state, anchor)
                    states[f"{cohort}:{arm}:{seed}:{sid}/{role}:7view"] = hash_scene_state(state)
            for qid in record["roles"][entry["role"]]:
                access.append({"cohort": cohort, "scene_id": sid, "frame_id": qid, "purpose": "QUERY_CAMERA_ONLY"})
                camera = geo.query_camera(record, qid, manifest["image_size"])
                batched = Cameras(camera.intrinsics[None, None], camera.c2w[None, None], camera.image_size)
                arrays = {}
                with torch.no_grad():
                    for (arm, seed), (state, anchor) in built.items():
                        pred = renderer(state, transform_cameras(batched, torch.linalg.inv(anchor)), ("depth", "visibility"))
                        arrays[f"{arm}_{seed}"] = pred["depth"][0, 0, 0].numpy().astype(np.float64)
                        arrays[f"OPACITY_{arm}_{seed}"] = pred["visibility"][0, 0, 0].numpy()
                path = out / "predictions" / f"{sid}_{role}_{qid}.npz"
                path.parent.mkdir(parents=True, exist_ok=True)
                np.savez_compressed(path, **arrays)
                entries[f"{cohort}/{sid}/{role}/{qid}"] = {"path": str(path), "sha256": sha(path)}
    if any("depth" in e.get("channels", []) and e.get("purpose") not in ("CONTEXT_ONLY_RGBD", "STREAM_RGBD")
           for e in access):
        raise AssertionError("only context and stream depth may be read before the seal")
    write_json(out / "state_hashes.json", states)
    write_json(out / "GT_access_before_seal.json", access)
    write_json(out / "prediction_seal.json", {
        "cohort": cohort, "arms": sorted(config["arms"]), "seeds": config["seeds"], "views": "3 context + 4 stream",
        "predictions": entries, "state_hashes_sha256": sha(out / "state_hashes.json"), "query_depth_read": False})
    print("SEVEN_VIEW_PREDICTIONS_SEALED", cohort, len(entries), flush=True)


if __name__ == "__main__":
    main(Path(sys.argv[1]).resolve(), sys.argv[2])