robometer_framework / robometer /scripts /single_video_benchmark_style.py
Shuyang-Yu-808
Add Robometer code + Robometer-4B weights
319eb16
Raw
History Blame Contribute Delete
6.26 kB
#!/usr/bin/env python3
from __future__ import annotations
import argparse
import json
from pathlib import Path
import numpy as np
from benchmark_progress_mark_local import (
DEFAULT_INFERENCE_MODE,
DEFAULT_MAX_FRAMES,
DEFAULT_MIN_FRAMES,
DEFAULT_MODEL_PATH,
DEFAULT_PREFIX_BATCH_SIZE,
DEFAULT_PREFIX_SAMPLE_FRAMES,
RobometerLocalRunner,
build_frame_retry_schedule,
is_cuda_oom_error,
load_all_video_frames,
sample_video_frames_with_indices,
write_overview_plot,
)
def save_results(output_dir: Path, payload: dict) -> None:
with (output_dir / "results.json").open("w", encoding="utf-8") as f:
json.dump(payload, f, indent=2, ensure_ascii=False)
f.write("\n")
def main() -> None:
parser = argparse.ArgumentParser(
description="Run Robometer on a single video with the same benchmark-style inference logic, without progress marks."
)
parser.add_argument("--video", required=True, help="Path to the input video")
parser.add_argument("--task", required=True, help="Task instruction")
parser.add_argument("--model-path", default=str(DEFAULT_MODEL_PATH))
parser.add_argument("--fps", type=float, default=3.0)
parser.add_argument("--max-frames", type=int, default=DEFAULT_MAX_FRAMES)
parser.add_argument("--min-frames", type=int, default=DEFAULT_MIN_FRAMES)
parser.add_argument(
"--inference-mode",
choices=["frame_steps", "whole"],
default=DEFAULT_INFERENCE_MODE,
help="frame_steps matches benchmark behavior; whole does one forward pass on the full sampled trajectory",
)
parser.add_argument("--prefix-sample-frames", type=int, default=DEFAULT_PREFIX_SAMPLE_FRAMES)
parser.add_argument("--prefix-batch-size", type=int, default=DEFAULT_PREFIX_BATCH_SIZE)
parser.add_argument(
"--adaptive-max-frames",
dest="adaptive_max_frames",
action="store_true",
help="On CUDA OOM in whole mode, retry with smaller frame budgets",
)
parser.add_argument(
"--no-adaptive-max-frames",
dest="adaptive_max_frames",
action="store_false",
help="Disable frame-budget retry on CUDA OOM",
)
parser.add_argument(
"--output-root",
type=Path,
default=Path(__file__).parent / "outputs_benchmark_style",
help="Root directory for outputs",
)
parser.set_defaults(adaptive_max_frames=True)
args = parser.parse_args()
video_path = Path(args.video).expanduser().resolve()
if not video_path.exists():
raise FileNotFoundError(f"Video not found: {video_path}")
output_dir = args.output_root / video_path.stem
output_dir.mkdir(parents=True, exist_ok=True)
runner = RobometerLocalRunner(model_path=str(args.model_path))
print(f"[RUN] {video_path.name}")
print(f"[TASK] {args.task}")
print(f"[VIDEO] {video_path}")
print(f"[OUT] {output_dir}")
all_frames, native_fps = load_all_video_frames(video_path)
total_frames = len(all_frames)
retry_schedule = (
build_frame_retry_schedule(args.max_frames, args.min_frames, bool(args.adaptive_max_frames))
if args.inference_mode == "whole"
else [int(args.max_frames)]
)
progress_pred = None
success_probs = None
sampled_indices = None
used_max_frames = retry_schedule[0]
for attempt_idx, frame_budget in enumerate(retry_schedule, start=1):
frames, sampled_indices = sample_video_frames_with_indices(
all_frames,
native_fps=native_fps,
fps=float(args.fps),
max_frames=int(frame_budget),
required_frames=[],
)
print(
f"Loaded {total_frames} total frames; sampled {len(frames)} frames at fps={float(args.fps):g} "
f"(max_frames={int(frame_budget)}, try {attempt_idx}/{len(retry_schedule)})"
)
try:
progress_pred, success_probs = runner.compute_rewards_per_frame(
video_frames=frames,
task=args.task,
inference_mode=args.inference_mode,
prefix_sample_frames=int(args.prefix_sample_frames),
prefix_batch_size=int(args.prefix_batch_size),
)
used_max_frames = int(frame_budget)
break
except RuntimeError as exc:
if args.inference_mode != "whole" or not is_cuda_oom_error(exc) or attempt_idx == len(retry_schedule):
raise
next_budget = retry_schedule[attempt_idx]
print(
f"[OOM] whole inference hit CUDA OOM at max_frames={int(frame_budget)}; "
f"retrying with max_frames={int(next_budget)}"
)
runner.reload_model()
if progress_pred is None or success_probs is None or sampled_indices is None:
raise RuntimeError("Failed to compute Robometer outputs.")
if progress_pred.size == 0:
raise RuntimeError("Robometer returned empty progress predictions.")
if progress_pred.size != len(sampled_indices):
raise RuntimeError(
f"Progress length mismatch: got {progress_pred.size} predictions for {len(sampled_indices)} sampled frames"
)
np.save(str(output_dir / "progress.npy"), progress_pred)
np.save(str(output_dir / "success_probs.npy"), success_probs)
payload = {
"video": str(video_path),
"instruction": args.task,
"num_total_frames": total_frames,
"native_fps": native_fps,
"sample_fps": float(args.fps),
"num_sampled_frames": len(sampled_indices),
"sampled_original_frame_indices": sampled_indices,
"max_frames_used": used_max_frames,
"inference_mode": args.inference_mode,
"prefix_sample_frames": int(args.prefix_sample_frames),
"prefix_batch_size": int(args.prefix_batch_size),
"progress_min": float(np.min(progress_pred)),
"progress_max": float(np.max(progress_pred)),
"progress_mean": float(np.mean(progress_pred)),
}
save_results(output_dir, payload)
write_overview_plot(output_dir, sampled_indices, progress_pred, [])
print(f"[OK] Saved results to {output_dir}")
if __name__ == "__main__":
main()