mp_yam_code / source /bimanual /yam /motion /recorder.py
yqi19's picture
YAM bimanual task suite: env, solvers, tasks, converters
7399b6f verified
Raw
History Blame Contribute Delete
1.97 kB
"""Video recording with the per-frame HUD overlay.
Also owns the warm-up-frame drop: the first frames after a reset render with the wrong camera
pose and unresolved textures, which made reviewers describe scenes (missing containers) that
never existed in the episode.
"""
from __future__ import annotations
import os
import numpy as np
WARMUP_FRAMES = 2
class Recorder:
def __init__(self, env, title="", episode=-1, fps=14):
self.env = env
self.title = title
self.episode = episode
self.fps = fps
self.frames = []
self.phase = "start"
self.result = ""
self.lines_fn = None # optional task-supplied extra HUD lines
def capture(self):
img = self.env.render()
if img is None:
return
from PIL import Image, ImageDraw
im = Image.fromarray(np.asarray(img)[..., :3].copy())
d = ImageDraw.Draw(im)
lines = [f"=== {self.title} ===" + (f" EP {self.episode}" if self.episode >= 0 else "")]
if self.result:
lines.append(f"RESULT: {self.result}")
lines.append(f"ACTION: {self.phase}")
if self.lines_fn is not None:
try:
lines += list(self.lines_fn())
except Exception:
pass
d.rectangle([0, 0, 470, 18*len(lines)+6], fill=(0, 0, 0))
y = 3
for ln in lines:
d.text((6, y), ln, fill=(255, 235, 60)); y += 18
self.frames.append(np.array(im))
def save(self, path):
import imageio.v2 as imageio
frames = self.frames[WARMUP_FRAMES:] if len(self.frames) > WARMUP_FRAMES+4 else self.frames
if not frames:
print("[rec] no frames to write"); return None
os.makedirs(os.path.dirname(path) or ".", exist_ok=True)
imageio.mimsave(path, frames, fps=self.fps)
print(f"[rec] video -> {path} ({len(frames)} frames)", flush=True)
return path