twanghcmut's picture
download
raw
3.62 kB
#!/usr/bin/env python
"""S8 CLI: re-export the ``vace/`` training tier from an episode's ``master/`` artifacts.
A thin wrapper over :func:`fpgm.datagen.pipeline.run_s8_export` -- the same function
:class:`~fpgm.datagen.pipeline.EpisodePipeline`'s own S8 stage calls in-process. This
script exists only for the ``--caption`` override (a manual one-off render with a
hand-written caption), which :meth:`EpisodePipeline.run`'s fixed signature has no room
for; every other code path is identical to running ``run_datagen.py --stages s8``.
Requires S2 (robot buffers + chosen extrinsics), S3 (``background_depth.h5``/
``plate.png``) and S6 (``poses.npz`` + ``master/meshes/*``) to already exist for this
uuid/camera.
Usage:
PYTHONPATH=src python scripts/export_vace_samples.py \\
--uuid AUTOLab+0d4edc83+2023-10-21-19h-07m-04s --camera 22008760
"""
from __future__ import annotations
import argparse
import sys
from pathlib import Path
REPO_ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(REPO_ROOT / "src"))
from fpgm.config_datagen import DatagenProfile # noqa: E402
from fpgm.datagen.episode_spec import EpisodeMetadata, EpisodeSpecResolver # noqa: E402
from fpgm.datagen.pipeline import run_s8_export # noqa: E402
from fpgm.types import DataError # noqa: E402
from fpgm.utils.logging import get_logger, setup_logging # noqa: E402
logger = get_logger("export_vace_samples")
_DEFAULT_CONFIG = REPO_ROOT / "configs" / "datagen_droid.yaml"
def parse_args() -> argparse.Namespace:
p = argparse.ArgumentParser(
description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter
)
p.add_argument("--uuid", required=True)
p.add_argument(
"--camera", required=True, help="camera serial (e.g. 22008760) or role (ext1/ext2)"
)
p.add_argument("--config", type=Path, default=_DEFAULT_CONFIG, help="DatagenProfile YAML")
p.add_argument(
"--out-root", type=Path, default=None,
help="override the profile's paths.output_root",
)
p.add_argument("--caption", default=None, help="override the DROID current_task caption")
p.add_argument("--force", action="store_true", help="ignore the on-disk window cache")
p.add_argument("--log-level", default="INFO")
return p.parse_args()
def main() -> int:
args = parse_args()
setup_logging(args.log_level)
profile = DatagenProfile.from_yaml(args.config)
if args.out_root is not None:
profile.paths.output_root = args.out_root.resolve()
metadata = EpisodeMetadata.from_flows_h5(profile.paths.flows_h5(args.uuid))
spec = EpisodeSpecResolver(profile).resolve(metadata, camera_role=args.camera)
master_dir = profile.paths.master_dir(spec.uuid, spec.camera_serial)
if not master_dir.exists():
raise DataError(
f"no master/ directory at {master_dir} -- run run_datagen.py --stages s6 first"
)
payload = run_s8_export(
profile, spec, master_dir, force=args.force, caption_override=args.caption
)
caption_kind = "template-composed" if payload["caption_is_template_composed"] else "override"
print(f"\ncaption ({caption_kind}): {payload['caption']!r}")
print(f"exported {payload['n_windows']} window(s):")
for w in payload["windows"]:
res = f"{w['resolution'][0]}x{w['resolution'][1]}"
print(
f" [{w['video_frame_start']:4d}, {w['video_frame_end']:4d}) {res} "
f"overlaps_pose_gap={w['overlaps_pose_gap']} -> {Path(w['target_video']).parent}"
)
return 0
if __name__ == "__main__":
raise SystemExit(main())

Xet Storage Details

Size:
3.62 kB
·
Xet hash:
9b66b3348de47832ba7e1f335ce3cb09577c0c80f36fc831f51bdc60b6b55e0e

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.