Skip to content

Commit 2e5d2ee

Browse files
[bugfix]: address review — drop false BC docstring claim, dead code; add contract test
- paths.py: remove the claim that the pool auto-squeezes legacy (1,T,C,H,W) input — no such code exists anywhere in fastvideo/eval/ — and dedupe the shape sentence. - basic_ltx2_audio_eval.py: drop the unreachable .corpus branch and always-true hasattr/isinstance guards (kwargs-form evaluate() returns a plain dict and rejects set metrics), restoring the direct results[name] lookup; fixes the double-printed skip reason. - both examples: stdlib import ordering; drop the dead .mp4 suffix check on a hardcoded .mp4 path. - new fastvideo/tests/eval/test_build_eval_kwargs.py locks the (T,C,H,W) no-batch-dim contract (CPU-only, PNG frame dir — no video decoder needed).
1 parent d036569 commit 2e5d2ee

4 files changed

Lines changed: 62 additions & 32 deletions

File tree

examples/inference/eval/basic_ltx2_audio_eval.py

Lines changed: 7 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,12 @@
1818
Install: ``uv pip install -e .[eval-audio]`` covers both metrics here
1919
(and the rest of the audio suite).
2020
"""
21+
from pathlib import Path
22+
2123
import torch
2224

2325
from fastvideo import VideoGenerator
2426
from fastvideo.eval import create_evaluator
25-
from pathlib import Path
2627

2728
PROMPT = (
2829
"A warm sunny backyard. The camera starts in a tight cinematic close-up "
@@ -70,33 +71,14 @@ def main() -> None:
7071

7172
print("\n=== Audio scores ===")
7273
for name in METRICS:
73-
r = None
74-
75-
# per-sample metric
76-
if hasattr(results, "__contains__") and name in results:
77-
r = results[name]
78-
79-
# corpus-level metric (e.g. audio.frechet_distance)
80-
elif hasattr(results, "corpus") and name in results.corpus:
81-
r = results.corpus[name]
82-
83-
if r is None:
84-
print(f" {name}: MISSING")
85-
continue
86-
74+
r = results[name]
8775
if r.score is None:
88-
skipped = (
89-
r.details.get("skipped", "no score")
90-
if isinstance(r.details, dict)
91-
else "no score"
92-
)
93-
print(f" {name}: SKIPPED ({skipped})")
76+
print(f" {name}: SKIPPED ({r.details.get('skipped', 'no score')})")
9477
else:
9578
print(f" {name}: {r.score:.4f}")
96-
97-
if r.details:
98-
for k, v in r.details.items():
99-
print(f" {k}: {v}")
79+
if r.details:
80+
for k, v in r.details.items():
81+
print(f" {k}: {v}")
10082

10183

10284
if __name__ == "__main__":

examples/inference/eval/basic_ltx2_eval.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,13 @@
2525
required scale-down. Drop ``motion_smoothness`` from ``METRICS`` if
2626
sharing, or run on a smaller-resolution generation.
2727
"""
28+
from pathlib import Path
29+
2830
import torch
2931

3032
from fastvideo import VideoGenerator
3133
from fastvideo.eval import Evaluator
3234
from fastvideo.eval.io import build_eval_kwargs
33-
from pathlib import Path
3435

3536
PROMPT = (
3637
"A warm sunny backyard. The camera starts in a tight cinematic close-up "
@@ -68,7 +69,7 @@ def main() -> None:
6869
output_file = Path(output_path)
6970

7071
# ----- generation (matches examples/inference/basic/basic_ltx2.py) -----
71-
if output_file.exists() and output_file.suffix.lower() == ".mp4":
72+
if output_file.exists():
7273
print(f"[eval] found existing video: {output_file}")
7374
print("[eval] skipping generation")
7475
else:

fastvideo/eval/io/paths.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,14 +44,11 @@ def _idx(p: Path) -> int:
4444
def build_eval_kwargs(row: dict, video_path: Path, *, fps: float = 24.0) -> dict[str, Any]:
4545
"""Build evaluator kwargs from a sample row + a video on disk.
4646
47-
Loads the video as ``(T, C, H, W)`` in ``[0, 1]``.
47+
Loads the video as ``(T, C, H, W)`` float in ``[0, 1]`` — the shape
48+
every metric consumes (see :meth:`BaseMetric.compute`).
4849
Forwards ``prompt`` as scalar ``text_prompt`` and ``auxiliary_info``
4950
as scalar dict when present on the row, matching the one-sample-per-call
5051
contract used by ``Evaluator.evaluate(**sample)``.
51-
52-
The returned ``video`` tensor has shape ``(T, C, H, W)`` in ``[0, 1]``.
53-
The eval pool still auto-squeezes legacy ``(1, T, C, H, W)`` callers
54-
for backward compatibility.
5552
"""
5653
from fastvideo.eval.io.video import load_video
5754

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
"""Contract test for :func:`fastvideo.eval.io.build_eval_kwargs`.
2+
3+
Locks the sample shape handed to ``Evaluator.evaluate(**sample)``:
4+
``video`` must be ``(T, C, H, W)`` float in ``[0, 1]`` — the shape every
5+
metric consumes (see :meth:`BaseMetric.compute`) — with no leading batch
6+
dim. Regression guard for the ``unsqueeze(0)`` bug fixed in #1412.
7+
8+
CPU-only; uses a directory of PNG frames so no video decoder is needed.
9+
"""
10+
from __future__ import annotations
11+
12+
import numpy as np
13+
from PIL import Image
14+
15+
from fastvideo.eval.io import build_eval_kwargs
16+
17+
T, H, W = 3, 8, 10
18+
19+
20+
def _write_frames(dir_path) -> None:
21+
dir_path.mkdir()
22+
for i in range(T):
23+
arr = np.full((H, W, 3), i * 40, dtype=np.uint8)
24+
Image.fromarray(arr).save(dir_path / f"frame_{i:03d}.png")
25+
26+
27+
def test_build_eval_kwargs_video_is_4d_tchw(tmp_path):
28+
frames = tmp_path / "frames"
29+
_write_frames(frames)
30+
31+
row = {"prompt": "a test prompt", "auxiliary_info": {"key": "val"}}
32+
sample = build_eval_kwargs(row, frames, fps=24.0)
33+
34+
video = sample["video"]
35+
assert video.shape == (T, 3, H, W), "video must be (T, C, H, W) with no batch dim"
36+
assert video.dtype.is_floating_point
37+
assert 0.0 <= video.min() and video.max() <= 1.0
38+
assert sample["fps"] == 24.0
39+
assert sample["text_prompt"] == "a test prompt"
40+
assert sample["auxiliary_info"] == {"key": "val"}
41+
42+
43+
def test_build_eval_kwargs_omits_absent_row_keys(tmp_path):
44+
frames = tmp_path / "frames"
45+
_write_frames(frames)
46+
47+
sample = build_eval_kwargs({}, frames)
48+
49+
assert set(sample) == {"video", "fps"}
50+
assert sample["fps"] == 24.0

0 commit comments

Comments
 (0)