Source: scripts/prepare_dl3dv_download.py · Plain text

scripts/prepare_dl3dv_download.py

"""Inventory and selectively download the pinned DL3DV-140 benchmark.

The default command only writes an authenticated, revision-pinned inventory and
does not download images.  Add ``--download-metadata`` to materialize one
canonical ``nerfstudio/transforms.json`` per scene.  Add ``--images-4`` and
``--download-images`` only after the protocol owner has supplied the exact frame
selection.
"""

from __future__ import annotations

import argparse
import json
from pathlib import Path

from mcss.data.dl3dv_download import (
    DEFAULT_REPRESENTATION,
    build_inventory,
    download_selected,
    protocol_frame_selection,
    select_files,
    write_inventory_artifacts,
)

PROJECT = Path(__file__).resolve().parents[1]
DEFAULT_OUTPUT = PROJECT / "outputs/dl3dv_download_20260920"
DEFAULT_DATA_ROOT = PROJECT / "data/dl3dv_benchmark"


def _parser() -> argparse.ArgumentParser:
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("--output-dir", type=Path, default=DEFAULT_OUTPUT)
    parser.add_argument("--data-root", type=Path, default=DEFAULT_DATA_ROOT)
    parser.add_argument(
        "--inventory-dir",
        type=Path,
        help="reuse an existing pinned inventory instead of querying the Hub tree",
    )
    parser.add_argument("--scenes", nargs="*", help="64-hex scene hashes; default is all 140")
    parser.add_argument("--representation", default=DEFAULT_REPRESENTATION)
    parser.add_argument("--include-cameras", action="store_true")
    parser.add_argument(
        "--images-4", action="store_true", help="include level-4 PNGs in the selection"
    )
    parser.add_argument("--frames", nargs="*", type=int)
    parser.add_argument("--protocol-json", type=Path)
    parser.add_argument("--target-every", type=int, default=8)
    parser.add_argument(
        "--download-metadata",
        action="store_true",
        help="download selected transforms.json/cameras.bin files",
    )
    parser.add_argument(
        "--download-images",
        action="store_true",
        help="download selected images_4 files; requires --images-4",
    )
    parser.add_argument("--workers", type=int, default=8)
    parser.add_argument("--reserve-gib", type=float, default=10.0)
    parser.add_argument("--max-attempts", type=int, default=5)
    return parser


def run(args: argparse.Namespace) -> int:
    if args.download_images and not args.images_4:
        raise ValueError("--download-images requires --images-4")
    if args.frames is not None and not args.images_4:
        raise ValueError("--frames requires --images-4")
    if args.protocol_json is not None and not args.images_4:
        raise ValueError("--protocol-json requires --images-4")
    if args.reserve_gib < 0:
        raise ValueError("--reserve-gib must be non-negative")
    if args.inventory_dir is None:
        inventory = build_inventory(workers=args.workers)
        artifacts = write_inventory_artifacts(
            inventory,
            args.output_dir,
            representation=args.representation,
        )
    else:
        from mcss.data.dl3dv_download import load_inventory_artifacts

        inventory = load_inventory_artifacts(args.inventory_dir)
        artifacts = {
            "metadata": args.inventory_dir / "benchmark-meta.csv",
            "tree": args.inventory_dir / "tree_inventory.jsonl",
            "manifest": args.inventory_dir / "manifest.json",
        }
    protocol_frames = None
    if args.protocol_json is not None:
        protocol_frames = protocol_frame_selection(
            args.protocol_json,
            inventory,
            data_root=args.data_root,
            representation=args.representation,
            target_every=args.target_every,
        )
    selected = select_files(
        inventory,
        args.data_root,
        scenes=args.scenes,
        representation=args.representation,
        include_images4=args.images_4,
        frame_ids=args.frames,
        frame_ids_by_scene=protocol_frames,
        include_cameras=args.include_cameras,
    )
    metadata_selected = tuple(
        item for item in selected if item.remote.kind in {"transforms", "cameras"}
    )
    image_selected = tuple(item for item in selected if item.remote.kind == "images_4")
    downloaded = None
    if args.download_metadata or args.download_images:
        requested = []
        if args.download_metadata:
            requested.extend(metadata_selected)
        if args.download_images:
            requested.extend(image_selected)
        downloaded = download_selected(
            requested,
            status_path=args.output_dir / "status.json",
            lock_path=args.output_dir / "download.lock",
            reserve_bytes=int(args.reserve_gib * 1024**3),
            max_attempts=args.max_attempts,
            workers=args.workers,
        )
    report = {
        "schema": "mcss.dl3dv.prepare.v1",
        "inventory": {key: str(value) for key, value in artifacts.items()},
        "revision": inventory.manifest(args.representation)["revision"],
        "scene_count": len(inventory.scenes),
        "selected_files": len(selected),
        "selected_metadata_files": len(metadata_selected),
        "selected_images_4_files": len(image_selected),
        "selected_bytes": sum(item.remote.size for item in selected),
        "downloaded": downloaded,
        "images_download_started": bool(args.download_images),
    }
    report_path = args.output_dir / "prepare_report.json"
    report_path.write_text(
        json.dumps(report, indent=2, sort_keys=True) + "\n", encoding="utf-8"
    )
    print(json.dumps(report, indent=2, sort_keys=True))
    return 0


if __name__ == "__main__":
    raise SystemExit(run(_parser().parse_args()))