yitongl's picture
Upload folder using huggingface_hub
0692312 verified
Raw
History Blame Contribute Delete
3.42 kB
#!/usr/bin/env python
"""One grid video over the lambda experiments, each panel captioned with what it is.
Composed here rather than with an ffmpeg filtergraph because the caption is the whole point -- six
unlabelled panels are unreadable, and `drawtext` needs a freetype-enabled build that the bundled
imageio-ffmpeg binary does not reliably have. PIL draws the label, imageio writes the file.
Panels are scaled to a common height and letterboxed to a common width, so a canvas difference
between runs shows as padding instead of silently stretching one panel against the others.
"""
from __future__ import annotations
import argparse
from pathlib import Path
import av
import numpy as np
from PIL import Image, ImageDraw
PANELS = [
("0_bf16", "BF16 reference"),
("1_plain_w4a4", "1. plain W4A4 (lambda=1, no LoRA)"),
("2_lambda_none", "2. lambda=1 + rank-32 LoRA"),
("3_lambda_all", "3. lambda from all tokens"),
("4_lambda_video", "4. lambda from video tokens"),
("5_lambda_text", "5. lambda from text tokens"),
]
def read(path: Path, height: int) -> list[np.ndarray]:
c = av.open(str(path))
v = c.streams.video[0]
v.thread_type, v.thread_count = "NONE", 1
out = []
for f in c.decode(video=0):
a = f.to_ndarray(format="rgb24")
w = int(round(a.shape[1] * height / a.shape[0] / 2)) * 2
out.append(np.asarray(Image.fromarray(a).resize((w, height), Image.BICUBIC)))
c.close()
return out
def label(frame: np.ndarray, text: str) -> np.ndarray:
im = Image.fromarray(frame)
d = ImageDraw.Draw(im, "RGBA")
d.rectangle([0, 0, im.width, 20], fill=(0, 0, 0, 170))
d.text((6, 5), text, fill=(255, 255, 255))
return np.asarray(im)
def main() -> int:
ap = argparse.ArgumentParser()
ap.add_argument("--dir", default="out/lambda_exps")
ap.add_argument("--out", default="out/lambda_exps/grid.mp4")
ap.add_argument("--height", type=int, default=300)
ap.add_argument("--fps", type=int, default=24)
args = ap.parse_args()
d = Path(args.dir)
have = [(s, t) for s, t in PANELS if (d / f"{s}.mp4").exists()]
if len(have) < 2:
print(f"only {len(have)} panels present, nothing to stack")
return 0
clips = [read(d / f"{s}.mp4", args.height) for s, _ in have]
n = min(len(c) for c in clips)
w = max(c[0].shape[1] for c in clips)
cols = (len(have) + 1) // 2
import imageio.v2 as imageio
wr = imageio.get_writer(args.out, fps=args.fps, codec="libx264", quality=8,
macro_block_size=2)
for i in range(n):
tiles = []
for (s, t), c in zip(have, clips):
f = label(c[i], t)
if f.shape[1] < w: # letterbox, never stretch
pad = np.zeros((f.shape[0], w, 3), np.uint8)
o = (w - f.shape[1]) // 2
pad[:, o:o + f.shape[1]] = f
f = pad
tiles.append(f)
while len(tiles) % cols:
tiles.append(np.zeros_like(tiles[0]))
rows = [np.concatenate(tiles[k:k + cols], axis=1) for k in range(0, len(tiles), cols)]
wr.append_data(np.concatenate(rows, axis=0))
wr.close()
print(f"wrote {args.out}: {len(have)} panels, {n} frames, {cols} cols")
for s, t in have:
print(f" {s:<16} {t}")
return 0
if __name__ == "__main__":
raise SystemExit(main())