Download scripts/data/render_trex_track_samples.py from zhicao/dreamzero: direct link, hf CLI and curl.
- Browser
- Download file 5.54 kB
-
https://huggingface.co/zhicao/dreamzero/resolve/main/scripts/data/render_trex_track_samples.py
- Command line
-
hf download hf://zhicao/dreamzero/scripts/data/render_trex_track_samples.py
-
curl -L -o render_trex_track_samples.py https://huggingface.co/zhicao/dreamzero/resolve/main/scripts/data/render_trex_track_samples.py
5.54 kB
| #!/usr/bin/env python3 | |
| """Render uniformly sampled T-Rex track-cache episodes without rerunning models.""" | |
| from __future__ import annotations | |
| import argparse | |
| import json | |
| import sys | |
| from pathlib import Path | |
| import numpy as np | |
| _SCRIPT_DIR = Path(__file__).resolve().parent | |
| _REPO_ROOT = _SCRIPT_DIR.parents[1] | |
| _SCRIPTS_DIR = _REPO_ROOT / "scripts" | |
| if str(_SCRIPTS_DIR) not in sys.path: | |
| sys.path.insert(0, str(_SCRIPTS_DIR)) | |
| from extract_track import load_episode_videos # noqa: E402 | |
| from trex_track.layout import VIEW_ORDER # noqa: E402 | |
| from trex_track.trex_viz_tracks import render_three_view_combined_video # noqa: E402 | |
| def _parse_args() -> argparse.Namespace: | |
| parser = argparse.ArgumentParser(description=__doc__) | |
| parser.add_argument( | |
| "--dataset-root", | |
| type=Path, | |
| default=_REPO_ROOT / "data" / "trex_full_force", | |
| ) | |
| parser.add_argument("--track-cache", type=Path, default=None) | |
| parser.add_argument( | |
| "--output-dir", | |
| type=Path, | |
| default=_REPO_ROOT / "outputs" / "trex_track_samples", | |
| ) | |
| parser.add_argument("--num-samples", type=int, default=3) | |
| parser.add_argument("--episodes", type=int, nargs="*", default=None) | |
| parser.add_argument("--fps", type=int, default=0) | |
| parser.add_argument("--trail", type=int, default=15) | |
| return parser.parse_args() | |
| def _uniform_episode_indices(total_episodes: int, count: int) -> list[int]: | |
| if count < 1: | |
| raise ValueError("--num-samples must be positive") | |
| if count > total_episodes: | |
| raise ValueError("--num-samples cannot exceed total episodes") | |
| return [ | |
| int(index) | |
| for index in np.rint( | |
| np.linspace(0, total_episodes - 1, count, dtype=np.float64) | |
| ) | |
| ] | |
| def _load_tracks(path: Path) -> tuple[dict[str, np.ndarray], dict[str, np.ndarray]]: | |
| if not path.is_file(): | |
| raise FileNotFoundError(path) | |
| with np.load(path, allow_pickle=False) as payload: | |
| tracks = { | |
| view: np.asarray(payload[f"tracks_{view}"], dtype=np.float32) | |
| for view in VIEW_ORDER | |
| } | |
| visibility = { | |
| view: np.asarray(payload[f"vis_{view}"], dtype=np.float32) | |
| for view in VIEW_ORDER | |
| } | |
| for view in VIEW_ORDER: | |
| if visibility[view].shape != tracks[view].shape[:2]: | |
| raise ValueError( | |
| f"{path}: {view} visibility {visibility[view].shape} " | |
| f"does not match tracks {tracks[view].shape}" | |
| ) | |
| return tracks, visibility | |
| def main() -> int: | |
| args = _parse_args() | |
| dataset_root = args.dataset_root.expanduser().resolve() | |
| track_cache = ( | |
| args.track_cache.expanduser().resolve() | |
| if args.track_cache is not None | |
| else dataset_root / "tracks_trex_track_force_v2" | |
| ) | |
| output_dir = args.output_dir.expanduser().resolve() | |
| info = json.loads((dataset_root / "meta" / "info.json").read_text()) | |
| total_episodes = int(info["total_episodes"]) | |
| video_shape = info["features"]["observation.images.head_left"]["shape"] | |
| out_hw = (int(video_shape[0]), int(video_shape[1])) | |
| fps = int(args.fps) if args.fps > 0 else int(info["fps"]) | |
| episode_indices = ( | |
| [int(index) for index in args.episodes] | |
| if args.episodes | |
| else _uniform_episode_indices(total_episodes, int(args.num_samples)) | |
| ) | |
| if any(index < 0 or index >= total_episodes for index in episode_indices): | |
| raise ValueError(f"episode indices must be in [0, {total_episodes})") | |
| output_dir.mkdir(parents=True, exist_ok=True) | |
| rendered: list[dict[str, object]] = [] | |
| for episode_index in episode_indices: | |
| cache_path = track_cache / f"episode_{episode_index:06d}.npz" | |
| output_path = output_dir / f"episode_{episode_index:06d}_tracks.mp4" | |
| print(f"Rendering episode {episode_index}: {output_path}", flush=True) | |
| tracks, visibility = _load_tracks(cache_path) | |
| videos = load_episode_videos( | |
| dataset_root, | |
| episode_index, | |
| out_hw=out_hw, | |
| ) | |
| frame_counts = { | |
| view: int(videos[view].shape[0]) | |
| for view in VIEW_ORDER | |
| } | |
| for view in VIEW_ORDER: | |
| if int(tracks[view].shape[0]) != frame_counts[view]: | |
| raise ValueError( | |
| f"episode {episode_index} {view}: " | |
| f"{tracks[view].shape[0]} track frames != " | |
| f"{frame_counts[view]} video frames" | |
| ) | |
| render_three_view_combined_video( | |
| view_images=videos, | |
| view_tracks=tracks, | |
| view_vis=visibility, | |
| out_path=output_path, | |
| fps=fps, | |
| draw_trail=int(args.trail), | |
| dim_low_vis=True, | |
| ) | |
| rendered.append( | |
| { | |
| "episode_index": episode_index, | |
| "frames": min(frame_counts.values()), | |
| "fps": fps, | |
| "track_cache": str(cache_path), | |
| "video": str(output_path), | |
| } | |
| ) | |
| del tracks, visibility, videos | |
| summary_path = output_dir / "samples.json" | |
| summary_path.write_text( | |
| json.dumps( | |
| { | |
| "dataset_root": str(dataset_root), | |
| "sampling": "uniform endpoints and midpoint", | |
| "episodes": rendered, | |
| }, | |
| indent=2, | |
| ) | |
| + "\n" | |
| ) | |
| print(f"Wrote {summary_path}") | |
| return 0 | |
| if __name__ == "__main__": | |
| raise SystemExit(main()) | |