Source: scripts/prepare_rgbd_arkit_system4_cohort.py · Plain text

scripts/prepare_rgbd_arkit_system4_cohort.py

#!/usr/bin/env python3
"""ARKitScenes fourth system cohort (EXP-3D-RGBD-ARKIT-SYSTEM4-DATA-V1): the never-prepared tail of the SYSTEM3 block.

EXP-3D-RGBD-ARKIT-SYSTEM3-DATA-V1 downloaded and camera-locked the 360 venues at positions 1000-1359 of the pilot's
seeded Training order and prepared them in order until 160 were eligible (block position 268). The 91 scans at block
positions 269-359 were downloaded and camera-locked but never prepared, and no image or depth byte of them was read.
This cohort prepares exactly those, in block order, with the base script's unchanged prepare_scan and SYSTEM3's
candidate lock (its locked source hashes are checked); every eligible scan is kept (no target). No download, no model
is run and no score is computed. Planned in EXP-3D-CAJU3D-SYSTEM4-HOLDOUT-V1 (plan locked before this script ran).
Stages (repository root, through cv_host_run.py, CUDA hidden):
  python prepare_rgbd_arkit_system4_cohort.py select | prepare
"""

from __future__ import annotations

import datetime
import hashlib
import importlib.util
import json
import sys
from pathlib import Path

ROOT = Path("outputs/EXP-3D-RGBD-ARKIT-SYSTEM4-DATA-V1").resolve()
SOURCE = Path("outputs/EXP-3D-RGBD-ARKIT-SYSTEM3-DATA-V1").resolve()
BASE = Path("scripts/prepare_rgbd_arkit_data.py")
SPLIT = "ARKIT_SYSTEM4"


def base():
    spec = importlib.util.spec_from_file_location("arkit_base", BASE)
    module = importlib.util.module_from_spec(spec)
    spec.loader.exec_module(module)
    return module


def sha(path):
    return hashlib.sha256(Path(path).read_bytes()).hexdigest()


def select(b):
    ROOT.mkdir(parents=True, exist_ok=True)
    if (ROOT / "selection.json").exists():
        raise FileExistsError("selection exists; never re-select")
    integrity = json.loads((SOURCE / "preparation_integrity.json").read_text())
    if integrity["status"] != "PASS" or integrity["eligible"] != integrity["target"]:
        raise PermissionError("SYSTEM3 preparation did not stop at its target")
    block = json.loads((SOURCE / "selection.json").read_text())["block"]
    prepared = {r["scene_id"].removeprefix("arkit_")
                for r in json.loads((SOURCE / "manifest_arkit_system3.json").read_text())["scenes"]}
    failures = set(json.loads((SOURCE / "scene_failures.json").read_text()))
    done = [i for i, s in enumerate(block) if s["video_id"] in prepared | failures]
    first = max(done) + 1
    if done != list(range(first)):
        raise PermissionError("the processed SYSTEM3 scans are not a prefix of its block")
    tail = block[first:]
    locked = json.loads((SOURCE / "candidate_lock.json").read_text())["scans"]
    if any(s["video_id"] not in locked for s in tail):
        raise PermissionError("a tail scan has no camera lock")
    for s in tail:
        if (b.DATA / "prepared" / s["video_id"]).exists():
            raise PermissionError(f"{s['video_id']}: a prepared directory exists already")
    b.write(ROOT / "selection.json", {
        "rule": f"the never-prepared tail of EXP-3D-RGBD-ARKIT-SYSTEM3-DATA-V1's block: positions {first}.."
                f"{len(block) - 1} of that block (Training order positions {1000 + first}..1359), in block order",
        "first_block_position": first, "source_selection_sha256": sha(SOURCE / "selection.json"),
        "source_manifest_sha256": sha(SOURCE / "manifest_arkit_system3.json"),
        "source_failures_sha256": sha(SOURCE / "scene_failures.json"),
        "source_candidate_lock_sha256": sha(SOURCE / "candidate_lock.json"),
        "block": tail, "image_or_depth_read": False,
        "created_utc": datetime.datetime.now(datetime.UTC).isoformat()})
    print("SELECTED", len(tail), "first block position", first)


def prepare(b):
    if (ROOT / "manifest_arkit_system4.json").exists():
        raise FileExistsError("manifest exists; no overwrite")
    selection = json.loads((ROOT / "selection.json").read_text())
    if sha(SOURCE / "candidate_lock.json") != selection["source_candidate_lock_sha256"]:
        raise PermissionError("SYSTEM3 candidate lock changed since the selection")
    lock_ = json.loads((SOURCE / "candidate_lock.json").read_text())
    for path, digest in lock_["source_sha256"].items():
        if sha(path) != digest:
            raise PermissionError(f"locked source changed: {path}")
    rules = b.replica()
    roles_of, prior = rules.role_rule(), rules.frozen_prior()
    records, qualities, geometry, hashes, supports, failures = [], {}, {}, {}, {}, {}
    for scan in selection["block"]:
        video = scan["video_id"]
        record, support, quality, check, failure = b.prepare_scan(
            rules, roles_of, prior, video, lock_["scans"][video], b.DATA / "prepared" / video)
        qualities[video], geometry[video] = {str(k): v for k, v in quality.items()}, check
        if failure:
            failures[video] = {"fold": "Training", **failure}
            continue
        supports[video] = support
        for frame in record["frames"]:
            for kind in ("rgb", "depth"):
                hashes[frame[kind]] = sha(frame[kind])
        record["split"] = SPLIT
        records.append(record)
    b.write(ROOT / "manifest_arkit_system4.json", {
        "schema": "mcss.rgbd_arkit.data.v1", "image_size": list(b.TARGET_SIZE),
        "depth_semantics": "ray_distance_meters", "protected_scene_ids": [],
        "candidate_lock_sha256": sha(SOURCE / "candidate_lock.json"), "selection_sha256": sha(ROOT / "selection.json"),
        "scenes": records})
    for name, value in (("frame_quality.json", qualities), ("geometry_checks.json", geometry),
                        ("role_support.json", supports), ("scene_failures.json", failures),
                        ("hashes.json", hashes)):
        b.write(ROOT / name, value)
    b.write(ROOT / "preparation_integrity.json", {
        "status": "PASS" if records else "EMPTY", "eligible": len(records), "processed": len(selection["block"]),
        "failures": len(failures), "model_forward_run": False, "score_computed": False})
    print("PREPARED", len(records), "eligible;", len(failures), "failures")


if __name__ == "__main__":
    {"select": select, "prepare": prepare}[sys.argv[1]](base())