99 lines
4.0 KiB
Python
99 lines
4.0 KiB
Python
"""Generate one LTX2 video and score it with VBench metrics.
|
||
|
||
The generation block is the same as
|
||
``examples/inference/basic/basic_ltx2.py`` — same prompt, same model,
|
||
same shape, same num_frames. After ``shutdown()`` the script loads the
|
||
mp4 back, builds a single :class:`fastvideo.eval.Evaluator`, and runs
|
||
the prompt-aware VBench subset that's meaningful for an arbitrary
|
||
text→video sample.
|
||
|
||
The first run downloads CLIP / DINO / RAFT / AMT / ViCLIP / MUSIQ
|
||
weights to ``~/.cache/fastvideo/eval/`` (~few GB total).
|
||
|
||
GPU memory caveat
|
||
-----------------
|
||
Scoring 1088×1920×121 with all 8 metrics needs a dedicated GPU (~80 GB).
|
||
On a shared GPU, ``vbench.motion_smoothness`` (AMT correlation volume)
|
||
will OOM — its memory autoscale reads ``total_memory`` rather than
|
||
``mem_get_info()`` free memory and therefore underestimates the
|
||
required scale-down. Drop ``motion_smoothness`` from ``METRICS`` if
|
||
sharing, or run on a smaller-resolution generation.
|
||
"""
|
||
import torch
|
||
|
||
from fastvideo import VideoGenerator
|
||
from fastvideo.eval import Evaluator
|
||
from fastvideo.eval.io import build_eval_kwargs
|
||
|
||
PROMPT = ("A warm sunny backyard. The camera starts in a tight cinematic close-up "
|
||
"of a woman and a man in their 30s, facing each other with serious "
|
||
"expressions. The woman, emotional and dramatic, says softly, \"That's "
|
||
"it... Dad's lost it. And we've lost Dad.\" The man exhales, slightly "
|
||
"annoyed: \"Stop being so dramatic, Jess.\" A beat. He glances aside, "
|
||
"then mutters defensively, \"He's just having fun.\" The camera slowly "
|
||
"pans right, revealing the grandfather in the garden wearing enormous "
|
||
"butterfly wings, waving his arms in the air like he's trying to take "
|
||
"off. He shouts, \"Wheeeew!\" as he flaps his wings with full commitment. "
|
||
"The woman covers her face, on the verge of tears. The tone is deadpan, "
|
||
"absurd, and quietly tragic.")
|
||
|
||
# VBench sub-metrics meaningful for an arbitrary text→video sample
|
||
# (just the generated frames, optionally fps + the source prompt).
|
||
# Structured-prompt metrics (vbench.color, vbench.multiple_objects,
|
||
# vbench.scene, ...) are excluded — they need prompts built to a
|
||
# specific schema.
|
||
METRICS = [
|
||
"vbench.aesthetic_quality", # CLIP + LAION aesthetic head
|
||
"vbench.subject_consistency", # DINO frame-to-first cosine
|
||
"vbench.background_consistency", # DINO on background patches
|
||
"vbench.imaging_quality", # pyiqa MUSIQ
|
||
"vbench.temporal_flickering", # pixel-wise frame deltas
|
||
"vbench.motion_smoothness", # AMT frame interpolator residual
|
||
"vbench.dynamic_degree", # RAFT optical-flow magnitude (needs fps)
|
||
"vbench.overall_consistency", # ViCLIP video↔prompt similarity
|
||
]
|
||
|
||
|
||
def main() -> None:
|
||
# ----- generation (matches examples/inference/basic/basic_ltx2.py) -----
|
||
generator = VideoGenerator.from_pretrained(
|
||
"Davids048/LTX2-Base-Diffusers",
|
||
num_gpus=1,
|
||
)
|
||
|
||
output_path = "outputs_video/ltx2_basic/output_ltx2_base_t2v_1088_1920_1.1.mp4"
|
||
generator.generate_video(
|
||
prompt=PROMPT,
|
||
output_path=output_path,
|
||
save_video=True,
|
||
num_frames=121,
|
||
height=1088,
|
||
width=1920,
|
||
)
|
||
generator.shutdown()
|
||
# Free residual CUDA memory the generator left behind so the
|
||
# evaluator can grab the largest possible workspace for AMT/RAFT.
|
||
torch.cuda.empty_cache()
|
||
|
||
# ----- scoring -----
|
||
print(f"\n[eval] building evaluator: {METRICS}")
|
||
evaluator = Evaluator(metrics=METRICS)
|
||
|
||
# LTX2 outputs at 24 fps by default.
|
||
sample = build_eval_kwargs({"prompt": PROMPT}, output_path, fps=24.0)
|
||
print(f"[eval] running ({sample['video'].shape[1]} frames @ 24 fps)...")
|
||
results = evaluator.evaluate(**sample)
|
||
|
||
print("\n=== VBench scores ===")
|
||
for name in METRICS:
|
||
r = results[name]
|
||
if r.score is None:
|
||
reason = r.details.get("skipped", "no score")
|
||
print(f" {name}: SKIPPED ({reason})")
|
||
else:
|
||
print(f" {name}: {r.score:.4f}")
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|