Spaces:
Running on Zero
Running on Zero
File size: 10,604 Bytes
1811aab | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 | """Stream open-world examples -- the released ones (code-world-model release/examples/example_XX: Picture 1, a
Depth + Semantic-ID proxy, per-window captions) or open-code-world-model takes (examples/gallery.py: a released
first frame, a proxy compiled by the same renderer and rig as our CARLA proxies). There is no ground truth: a
qualitative test outside CARLA.
python -m rtcwm.eval.open_world prepare --examples DIR/example_01 [...] [--chunks 24] # one GPU
python -m rtcwm.eval.open_world generate --lora LORA --out DIR --examples example_01 [...] [--chunks 24]
python -m rtcwm.eval.open_world release --out DIR --examples example_01 [...] [--chunks 24]
prepare writes each example in the training-cache layout (train_cache/ow_example_XX/), so
rtcwm.train.dataset.load_window streams it like a CARLA clip:
stream.safetensors {"duv"} proxy latents of the first --chunks 17-frame clips
anchorn_c00 / staticn_c00 Picture 1 at 1344x768 (the release's first target frame) and the Qwen states
of the static caption
The static caption is the example's first-window "Appearance and scene" section -- the starting state --
without its action timeline (real time: the text tells nothing of the future).
generate writes <example>_latents.pt and <example>_stream.mp4: generated | proxy (Semantic-ID colours), full
resolution.
release writes DIR/<example>/config.json: the example's own release configuration cut to the windows that
cover --chunks chunks, for the released (bidirectional, windowed) CWM sampler -- python -m
cwm_h3_inference prepare / generate --config ...; it keeps the full per-window captions, timeline included.
"""
from __future__ import annotations
import argparse
import json
from pathlib import Path
import numpy as np
import torch
from cwm_h3_inference.constants import TARGET_HEIGHT, TARGET_WIDTH
from .. import paths
from ..data.anchors import cache_paths
from ..model.layout import CLIP_FRAMES
TIMELINE = "Protagonist action timeline:"
PALETTE = np.array([[0, 0, 0], [135, 206, 235], [30, 90, 200], [150, 120, 80], [90, 90, 90], [60, 160, 60],
[200, 120, 80], [220, 200, 60], [230, 40, 40], [200, 80, 200], [40, 220, 220], [250, 250, 250]],
dtype=np.uint8) # Semantic-ID classes 0..11 (INFERENCE.md)
def cache_name(example: str) -> str:
return "ow_" + Path(example).name
def static_caption(prompt: str) -> str:
"""A window prompt cut to its "Appearance and scene:" section -- the starting state, no timeline (and no
instruction preamble, which open-code-world-model's captions carry)."""
head = "Appearance and scene:"
return (head + prompt.split(head, 1)[1] if head in prompt else prompt).split(TIMELINE)[0].strip()
def example_inputs(ex: Path):
"""(Picture 1, condition directory, first-window prompt) of a released example (config.json) or of an
open-code-world-model take (first_frame.png, conditions/, prompts/000.txt)."""
if (ex / "config.json").exists():
cfg = json.loads((ex / "config.json").read_text())
return ex / cfg["anchor_image"], ex / cfg["condition_root"], cfg["windows"][0]["prompt"]
return ex / "first_frame.png", ex / "conditions", (ex / "prompts" / "000.txt").read_text()
def prepare(examples: list[Path], chunks: int):
from PIL import Image
from musubi_tuner.minimax_h3.text_encoder import load_h3_processor, load_h3_text_encoder
from musubi_tuner.minimax_h3.video_vae import encode_video_condition, load_video_vae
from musubi_tuner.minimax_h3_generate_video import _prepare_pixels
from cwm_h3_inference.cache import _load_anchor
from cwm_h3_inference.config import _canonical_caption
from cwm_h3_inference.duv import load_duv_frame
from ..train.preencode import atomic_save, static_text_states
dev = torch.device("cuda:0")
vae = load_video_vae(paths.VIDEO_VAE, device=dev, dtype=torch.float16, disable_mmap=False).eval().requires_grad_(False)
proc = load_h3_processor(str(paths.PROCESSOR))
qwen = load_h3_text_encoder(str(paths.TEXT_ENCODER), processor_path=str(paths.PROCESSOR), device=dev,
dtype=torch.bfloat16).eval()
for ex in examples:
anchor, cond, prompt = example_inputs(ex)
name = cache_name(ex)
(paths.CACHE / name).mkdir(parents=True, exist_ok=True)
k = min(chunks, len(list(cond.glob("*.semantic_id.png"))) // CLIP_FRAMES)
duv = np.stack([load_duv_frame(cond, i, for_qwen=False) for i in range(k * CLIP_FRAMES)])
duv = np.concatenate([duv, np.repeat(duv[-1:], CLIP_FRAMES, 0)], 0) # the encoder drops 3 latents
with torch.inference_mode():
ref = encode_video_condition(vae, _prepare_pixels(torch.from_numpy(duv)).to(dev, torch.float16))[0]
atomic_save(paths.CACHE / name / "stream.safetensors", {"duv": ref.float().cpu()[:, :5 * k].contiguous()},
{"clip_frames": str(CLIP_FRAMES), "vae_clips": str(k), "source": str(ex)})
cp = cache_paths(name, 0, "native")
picture1 = _load_anchor(anchor, size=(TARGET_WIDTH, TARGET_HEIGHT))
Image.fromarray(picture1).save(cp["png"])
with torch.inference_mode():
lat = encode_video_condition(vae, _prepare_pixels(torch.from_numpy(picture1).unsqueeze(0)).to(dev, torch.float16))
atomic_save(cp["latent"], {"latents_ref_000_image": lat[0].float().cpu()})
caption = _canonical_caption(static_caption(prompt), f"{name}/static_c00")
hidden, tags = static_text_states(proc, qwen, picture1, cp["png"], cond, 0, caption)
atomic_save(cp["text"], {"hidden": hidden.to("cpu", torch.bfloat16), "tags": tags.to("cpu", torch.int64)},
{"system_prompt": "w0", "duv": "start frame repeated", "caption": "first window, no timeline"})
print(f"{name}: {k} chunks, caption {len(caption.split())} words, Qwen rows {hidden.shape[0]}", flush=True)
def proxy_frames(example: Path, n: int) -> np.ndarray:
from PIL import Image
cond = example_inputs(example)[1]
sem = [np.asarray(Image.open(cond / f"{i:06d}.semantic_id.png")) for i in range(n)]
out = PALETTE[np.clip(np.stack(sem), 0, 11)]
return np.stack([np.asarray(Image.fromarray(f).resize((TARGET_WIDTH, TARGET_HEIGHT), Image.NEAREST)) for f in out])
def generate(a):
from cwm_h3_inference.constants import VIDEO_SHIFT
from ..model.loading import load_for_inference
from ..model.sampler import generate_stream
from ..train.dataset import load_window, silence, stream_chunks
from .video import decode, load_vae, write_mp4
dev = torch.device("cuda", 0)
a.out.mkdir(parents=True, exist_ok=True)
from .evaluate import check_span
check_span(a.lora)
from ..model import sampler
sampler.HISTORY_FROM_LAST_STEP = a.history_from_last_step
model = load_for_inference(a.lora, dev, fp8=a.fp8)
audio, results = silence(), {}
for ex in a.examples:
name = cache_name(ex)
n = min(a.chunks, stream_chunks(name))
lat = generate_stream(model, lambda j: load_window(name, j, audio, start=0, with_target=False), n, a.steps,
torch.Generator().manual_seed(a.seed), dev, a.rule, a.shift if a.shift else VIDEO_SHIFT,
history_noise=a.history_noise, kv_reuse=a.kv_reuse, on_chunk=lambda k, s: print(f"{name} chunk {k + 1}/{n} {s:.1f}s", flush=True))
results[ex] = lat.cpu()
del model
torch.cuda.empty_cache()
vae = load_vae(dev)
for ex, lat in results.items():
torch.save(lat, a.out / f"{ex}_latents.pt")
gen = decode(vae, lat, dev)
write_mp4(a.out / f"{ex}_stream.mp4", np.concatenate([gen, proxy_frames(Path(a.root) / ex, len(gen))], 2), crf=20)
print("wrote", a.out / f"{ex}_stream.mp4", flush=True)
def release_configs(a):
from cwm_h3_inference.constants import STRIDE_FRAMES, WINDOW_FRAMES
windows = 1 + max(0, -(-(a.chunks * CLIP_FRAMES - WINDOW_FRAMES) // STRIDE_FRAMES))
for e in a.examples:
ex = Path(a.root) / e
cfg = json.loads((ex / "config.json").read_text())
d = (a.out / e).resolve()
d.mkdir(parents=True, exist_ok=True)
absolute = lambda v: str((ex / v).resolve())
cfg.update(anchor_image=absolute(cfg["anchor_image"]), condition_root=absolute(cfg["condition_root"]),
models={k: absolute(v) for k, v in cfg["models"].items()}, windows=cfg["windows"][:windows],
cache_dir=str(d / "cache"), output=str(d / "output.mp4"), blocks_to_swap=16)
(d / "config.json").write_text(json.dumps(cfg, indent=1))
print(f"{e}: {windows} windows, {WINDOW_FRAMES + STRIDE_FRAMES * (windows - 1)} frames -> {d / 'config.json'}")
def main():
ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
ap.add_argument("stage", choices=["prepare", "generate", "release"])
ap.add_argument("--examples", nargs="+", required=True, help="prepare: example directories; generate: their names")
ap.add_argument("--root", default=str(Path.home() / "projects/code-world-model/release/examples"))
ap.add_argument("--chunks", type=int, default=24)
ap.add_argument("--lora", type=Path)
ap.add_argument("--out", type=Path)
ap.add_argument("--steps", type=int, default=35)
ap.add_argument("--seed", type=int, default=0)
ap.add_argument("--fp8", action="store_true", help="the blocks' large linears in FP8 (Hopper)")
ap.add_argument("--rule", choices=["euler", "x0"], default="euler", help="x0: few-step (distilled) sampling")
ap.add_argument("--shift", type=float, default=0.0, help="sampling flow shift (0: the released 12)")
ap.add_argument("--kv-reuse", action="store_true", help="carry the history's keys/values across chunks")
ap.add_argument("--history-from-last-step", action=argparse.BooleanOptionalAction, default=True,
help="kv-reuse: cache each chunk from its last denoising step (one forward fewer per chunk; "
"--no-history-from-last-step: one more clean forward)")
ap.add_argument("--history-noise", type=float, default=0.0, help="noise level of the history chunks")
a = ap.parse_args()
if a.stage == "prepare":
prepare([Path(e) if Path(e).is_dir() else Path(a.root) / e for e in a.examples], a.chunks)
elif a.stage == "release":
release_configs(a)
else:
generate(a)
if __name__ == "__main__":
main()
|