[fix]: magi-human latent prep: honor batch.num_frames

The latent preparation stage was deriving num_frames from
batch.num_seconds * fps + 1 unconditionally and ignoring
batch.num_frames. num_seconds was never set anywhere (no plumbing in
SamplingParam or ForwardBatch), so every generation defaulted to 4
seconds = 101 frames, regardless of what the caller passed via
SamplingParam.num_frames.

Now we prefer batch.num_frames when it's a sensible video length (>1)
and fall back to the seconds-based derivation when the caller explicitly
passes num_seconds. Backward-compatible with the production preset
(num_frames=101 == 4*25+1). Fixes the SSIM test budget — it was running
at 101 frames despite asking for 26.

All 8 runnable magi-human parity tests still pass.
This commit is contained in:
will
2026-05-05 11:38:17 -07:00
parent 2e8db18d94
commit 46f18d8cd2
@@ -129,9 +129,20 @@ class MagiHumanLatentPreparationStage(PipelineStage):
def forward(self, batch: ForwardBatch, fastvideo_args: FastVideoArgs) -> ForwardBatch:
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
seconds = int(getattr(batch, "num_seconds", None) or 4)
fps = self.fps
num_frames = seconds * fps + 1
# Prefer the caller-provided `batch.num_frames` (the standard
# SamplingParam knob — production preset and SSIM tests both set
# it). Fall back to `batch.num_seconds * fps + 1` when num_frames
# is unset or the image-default sentinel (1). This matches
# upstream MagiDataProxy.process_input which derives `num_frames
# = seconds * fps + 1` and rejects values that don't satisfy
# `(num_frames - 1) % vae_temporal_stride == 0`.
requested_num_frames = int(getattr(batch, "num_frames", None) or 0)
if requested_num_frames > 1:
num_frames = requested_num_frames
else:
seconds = int(getattr(batch, "num_seconds", None) or 4)
num_frames = seconds * fps + 1
latent_T = (num_frames - 1) // 4 + 1
# Match upstream pipeline.py:61-64 + video_generate.py:254-261: