From 46f18d8cd2bf4c0da85fd8eec61a085972f82949 Mon Sep 17 00:00:00 2001 From: will Date: Tue, 5 May 2026 11:38:17 -0700 Subject: [PATCH] [fix]: magi-human latent prep: honor batch.num_frames MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- .../basic/magi_human/stages/latent_preparation.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/fastvideo/pipelines/basic/magi_human/stages/latent_preparation.py b/fastvideo/pipelines/basic/magi_human/stages/latent_preparation.py index cb362cf77..c3b47d7e1 100644 --- a/fastvideo/pipelines/basic/magi_human/stages/latent_preparation.py +++ b/fastvideo/pipelines/basic/magi_human/stages/latent_preparation.py @@ -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: