Download augment/mimicgen/render_from_plan.py from mlnha/pose6daug-scripts: direct link, hf CLI and curl.
- Browser
- Download file 5.48 kB
-
https://huggingface.co/mlnha/pose6daug-scripts/resolve/main/augment/mimicgen/render_from_plan.py
- Command line
-
hf download hf://mlnha/pose6daug-scripts/augment/mimicgen/render_from_plan.py
-
curl -L -o render_from_plan.py https://huggingface.co/mlnha/pose6daug-scripts/resolve/main/augment/mimicgen/render_from_plan.py
5.48 kB
| #!/usr/bin/env python3 | |
| """Render episodes listed in a render plan, one mp4 per camera. | |
| The plan fixes every folder name up front, in one global pass over all runs, so a name is | |
| unique across the whole output rather than only within the run that produced it. Assigning | |
| names per run is what silently dropped episodes: a later run's success collided with an | |
| earlier one's folder and was skipped as already done. | |
| Plan entries are `{run_dir, demo, target, name}`. Shard with `--shard i --num-shards n` to | |
| render in parallel; each episode writes a `.done` marker, so re-running skips finished work. | |
| MUJOCO_GL=egl python render_from_plan.py --plan render_plan.json \ | |
| --source src150_fix.hdf5 --filter-key valid --out output_aug_256 --shard 0 --num-shards 6 | |
| """ | |
| from __future__ import annotations | |
| import argparse | |
| import json | |
| import os | |
| import time | |
| os.environ.setdefault("MUJOCO_GL", "egl") | |
| os.environ.setdefault("PYOPENGL_PLATFORM", "egl") | |
| import h5py | |
| import imageio | |
| import mujoco | |
| import numpy as np | |
| import robocasa # noqa: F401 | |
| import robosuite | |
| from robosuite.controllers import load_composite_controller_config | |
| CAMERAS = ["robot0_agentview_left", "robot0_agentview_right", "robot0_eye_in_hand"] | |
| def source_index(path, filter_key): | |
| out = {} | |
| with h5py.File(path, "r") as f: | |
| keys = ([k.decode() for k in f[f"mask/{filter_key}"][:]] if filter_key | |
| else sorted(f["data"].keys(), key=lambda x: int(x.split("_")[1]))) | |
| for i, k in enumerate(keys): | |
| meta = json.loads(f[f"data/{k}"].attrs["ep_meta"]) | |
| cat = next((c["info"]["cat"] for c in meta.get("object_cfgs", []) | |
| if c.get("name") == "obj"), "unknown") | |
| out[i] = (k, cat) | |
| return out | |
| def build_env(env_args, res): | |
| kw = dict(env_args["env_kwargs"]) | |
| kw.pop("env_name", None) | |
| kw.update(has_renderer=False, has_offscreen_renderer=True, use_camera_obs=False, | |
| use_object_obs=True, camera_depths=False, camera_names=CAMERAS, | |
| camera_widths=res, camera_heights=res, ignore_done=True) | |
| kw.setdefault("controller_configs", | |
| load_composite_controller_config(controller=None, robot="PandaOmron")) | |
| return robosuite.make(env_name=env_args["env_name"], **kw) | |
| def main(): | |
| ap = argparse.ArgumentParser() | |
| ap.add_argument("--plan", required=True) | |
| ap.add_argument("--source", required=True) | |
| ap.add_argument("--filter-key", default=None) | |
| ap.add_argument("--out", required=True) | |
| ap.add_argument("--shard", type=int, default=0) | |
| ap.add_argument("--num-shards", type=int, default=1) | |
| ap.add_argument("--res", type=int, default=256) | |
| ap.add_argument("--fps", type=int, default=20) | |
| a = ap.parse_args() | |
| plan = [e for i, e in enumerate(json.load(open(a.plan))) if i % a.num_shards == a.shard] | |
| src = source_index(a.source, a.filter_key) | |
| os.makedirs(a.out, exist_ok=True) | |
| env, opened, done = None, None, 0 | |
| for e in plan: | |
| ep_dir = os.path.join(a.out, e["name"]) | |
| if os.path.exists(os.path.join(ep_dir, ".done")): | |
| done += 1 | |
| continue | |
| t0 = time.time() | |
| with h5py.File(os.path.join(e["run_dir"], "demo.hdf5"), "r") as f: | |
| g = f[f"data/{e['demo']}"] | |
| states = g["states"][:] | |
| meta = json.loads(g.attrs["ep_meta"]) | |
| model_xml = g.attrs["model_file"] | |
| si = int(g["src_demo_inds"][0]) if "src_demo_inds" in g else -1 | |
| env_args = json.loads(f["data"].attrs["env_args"]) | |
| sk, scat = src.get(si, (f"idx{si}", "unknown")) | |
| if env is None: | |
| env = build_env(env_args, a.res) | |
| env.set_ep_meta(meta) | |
| env.reset() | |
| env.reset_from_xml_string(model_xml) | |
| env.sim.reset() | |
| model, data = env.sim.model._model, env.sim.data._data | |
| opt = mujoco.MjvOption() | |
| opt.geomgroup[:] = 0 | |
| opt.geomgroup[1] = 1 # visual geoms, as the dataset's own videos are drawn | |
| opt.sitegroup[:] = 0 | |
| r = mujoco.Renderer(model, height=a.res, width=a.res) | |
| cam_ids = {c: mujoco.mj_name2id(model, mujoco.mjtObj.mjOBJ_CAMERA, c) for c in CAMERAS} | |
| frames = {c: [] for c in CAMERAS} | |
| for t in range(len(states)): | |
| env.sim.set_state_from_flattened(states[t]) | |
| env.sim.forward() | |
| for c in CAMERAS: | |
| r.update_scene(data, camera=cam_ids[c], scene_option=opt) | |
| frames[c].append(r.render().copy()) | |
| r.close() | |
| for c in CAMERAS: | |
| d = os.path.join(ep_dir, c) | |
| os.makedirs(d, exist_ok=True) | |
| imageio.mimwrite(os.path.join(d, f"{c}.mp4"), frames[c], fps=a.fps, | |
| codec="libx264", macro_block_size=1, | |
| ffmpeg_params=["-pix_fmt", "yuv420p"]) | |
| with open(os.path.join(ep_dir, ".done"), "w") as fh: | |
| json.dump(dict(generated_demo=e["demo"], run_dir=e["run_dir"], | |
| source_demo=sk, source_object=scat, target_object=e["target"], | |
| frames=len(states), layout=meta.get("layout_id"), | |
| style=meta.get("style_id"), lang=meta.get("lang")), fh, indent=1) | |
| done += 1 | |
| print(f" [{a.shard}] {e['name']}: {len(states)}f {time.time()-t0:.0f}s", flush=True) | |
| print(f"shard {a.shard}: {done}/{len(plan)}", flush=True) | |
| if __name__ == "__main__": | |
| raise SystemExit(main()) | |