Skip to content

feat(wan video): Introduce transition_video for seamless action-transfer concatenation, compatible with context mode and Wan Animate loop#1946

Open
wuwukaka wants to merge 20 commits into
kijai:mainfrom
wuwukaka:main
Open

feat(wan video): Introduce transition_video for seamless action-transfer concatenation, compatible with context mode and Wan Animate loop#1946
wuwukaka wants to merge 20 commits into
kijai:mainfrom
wuwukaka:main

Conversation

@wuwukaka

Copy link
Copy Markdown

Motivation

Currently, concatenating video batches for action transfer heavily relies on the start_ref_image parameter. However, under the current logic, using this parameter forces the generation into the looping (Wan Animate) mode and is architecturally incompatible with the context (context windowing) mode. In long video generation, relying solely on the looping mode leads to error accumulation, resulting in noticeable visual degradation in subsequent frames.

This PR introduces the transition_video parameter, allowing users to input a short video clip as a hard conditioning guide. This approach removes the restriction that video concatenation must be strictly bound to the looping mode, making it compatible with both context mode and Wan Animate loop mode. This grants users the option to utilize the context mode during action-transfer concatenation to maintain higher visual stability.

Usage

  1. Node Connection: In the WanVideo Wrapper node, connect the prepared transition video to the newly added transition_video input.
  2. Mode Configuration: This interface is agnostic to the generation mode. Users can either connect context parameters via a standalone Context node to prevent visual degradation, or retain the Wan Animate chunked settings; the concatenation guidance will function correctly in both scenarios.

Implementation Details by File

1. nodes.py (Wrapper Node & Preprocessing)
Responsible for robust video preprocessing and strictly aligning the timing of control signals.

  • Parameter Injection & Canvas Expansion: Added the transition_video input. When provided, the total num_frames is forced to expand by 32 frames (corresponding to 8 latent frames).
  • Control Signal Padding: Prepends padding to spatial/conditioning control signals (pose_images, face_images, bg_images, mask). Extracts the 0-th frame of each signal, duplicates it 32 times using .repeat(32, ...), and prepends it to the original sequence, ensuring the original control actions are accurately shifted to the 33rd frame.
  • Video Preprocessing & Precise Frame Alignment:
    • Frame Correction: Sets the target frame count to 32. If input frames b > 32, uses torch.linspace to generate uniform indices for downsampling; if b < 32, calculates the ceiling repeat_factor to duplicate the video tensor, then truncates it using [:32] slicing, ensuring the output is strictly 32 frames.
    • Spatial & Value Adjustment: Reshapes and resizes the aligned video to the target dimensions, adjusts dimensions to [C, T, H, W], extracts the first 3 RGB channels, and normalizes the values to [-1, 1] via * 2 - 1.
  • VAE Encoding & Hard Mask: Encodes the processed 32-frame video into an 8-frame transition_latent via VAE; constructs an all-1.0 tensor of length 8 as transition_mask_values, instructing the sampler to execute a 100% hard replacement.
  • Conflict Resolution: If both transition_video and start_ref_image are provided, logs a warning and prioritizes the transition_video pipeline.

2. nodes_sampler.py (Sampler)
Responsible for precisely slicing and replacing the starting tensors of the target latent sequence during the initialization phase.

  • Context Windowing Mode Adaptation:

    • Mask Channel Construction: Before each denoising step, extracts the first 4 channels from the 36-channel image_cond as window_msk.
    • Precise Tensor Replacement: When processing the first window (c[0] == 0), slices partial_img_emb. Skips the first 4 mask channels and directly replaces the target latent section (channels 4:20, frames 1:1+transition_len) with transition_latent.
    • Mask Application: Assigns transition_mask_values to the corresponding timesteps of window_msk, and overwrites the first 4 channels of the input condition image_cond_in before passing it to the model prediction.
  • Wan Animate (Looping) Mode Adaptation:

    • Trigger Condition: Appends the has_transition flag to the logic evaluating the reconstruction of temporal_ref_latents.
    • Image Concatenation Reconstruction: Independently extracts current_ref_images and bg_image_slice, concatenates them using torch.cat, and sends them to VAE encoding.
    • Condition Injection: When processing the first chunk (start == 0), retains the original target latent sequence after transition_len, and hard-replaces the first 8 frames with transition_latent.

Impact

  • Backward Compatibility: When no transition video is connected, all newly added preprocessing and replacement logic is skipped, leaving the original workflow and performance completely unaffected.

@kslz

kslz commented Mar 2, 2026

Copy link
Copy Markdown

Does this support I2V and T2V? Is the input frame length configurable, or is it hardcoded to 32 frames?

@kslz

kslz commented Mar 6, 2026

Copy link
Copy Markdown

something wrong
image

…ext_options

This commit addresses an issue where the `transition_video` padding conflicted with WanAnimate's built-in loop logic:

- Issue: The 32-frame padding added for `transition_video` artificially increased `num_frames`, causing the condition `num_frames > frame_window_size` to evaluate to True. This falsely triggered the built-in loop mechanism, which subsequently crashed when `context_options` was also enabled.
- Fix: Introduced an `effective_frames` calculation that subtracts the 32 padding frames before evaluating the loop condition. This ensures that the built-in loop is only triggered based on the actual target video length.
@wuwukaka

wuwukaka commented Mar 6, 2026

Copy link
Copy Markdown
Author

something wrong image

Thanks for pointing this out. You were absolutely right—there was a minor issue where the transition padding falsely triggered the built-in loop. I've just pushed a fix for this.

wuwukaka and others added 16 commits April 5, 2026 22:34
Resolves a RuntimeError during standard WanAnimate loop generation when
transition_video is disabled. The error occurred because the VAE time
dimension alignment logic was incorrectly scoped inside the `else` block
during the transition_video feature integration.
 Use the same BHWC -> CTHW -> RGB -> normalize -> VAE encode flow for transition_video regardless of input size. For mismatched spatial dimensions, resize
  in BCHW and convert back to BHWC, removing reshape-based tensor reinterpretation that could corrupt channel/frame ordering.
  In the context-window sampling branch, partial_img_emb can be None when no image condition is provided (for example, some SCAIL runs). The previous code
  unconditionally called partial_img_emb.clone(), causing:

  AttributeError: 'NoneType' object has no attribute 'clone'

  This change makes image conditioning optional in that path:

  - build image_cond_in only when partial_img_emb is available
  - otherwise pass None through to predict_with_cfg

  This restores safe behavior for context-window sampling without image conditioning and prevents the runtime crash.
     - Embed prefix images into bg canvas at encode time (like transition_video), replacing per-window injection during sampling
     - Canvas expands by 37 frames (17 prefix + 20 reserve for optional transition)
     - Dynamic 1-5 image support: img0(×1), img1(×4), ..., imgN-1(×4)
     - Prefix mask=1 for actual frames, unused reserve stays mask=0
     - Compatible with transition_video: when both present, transition also embedded in canvas (last 20 frames of 37 expansion), sampler injection skipped
     - Non-first context windows prepend indices 0-5 (ref+prefix) to model input for consistency, predictions sliced off after model call
     - VAE decode trims first 37 pixel frames when prefix is active
     - Fix latent_window_size for non-looping to cover full expanded bg range
…of repeating frame 0

  Replace pose_images[0:1].repeat(37) and face_images[0:1].repeat(37) with sparse temporal sampling (every 2nd frame from index 0, reversed) so the 37-frame expansion region carries a varied motion preview rather than a static copy of the first frame.
  Replace repeat-first-frame padding with sparse stride-2 reversed sampling for mask and bg_images, matching the pose/face expansion logic. All four control signals now share consistent temporal context in the 37-frame expansion region.
  - Add dynamic prefix frames (1-5 images, img0×1 + imgN×4 pattern)
  - Expand canvas by 21/37 frames at encode time (transition/prefix)
  - Embed prefix and transition into canvas before VAE encode, removing per-window injection logic from the sampling loop
  - Unify pose/face/mask/bg padding to stride-2 reversed sampling
  - Support looping mode: prefix_ctx (ref+prefix latent) prepended to every chunk like ref_latent; pose/face copy first frame of current chunk to fill prefix region
  - Dynamic decode trim via canvas_expansion_px (21/37/0)
  - Fix latent_window_size to cover full expanded bg range, preventing pose index clamping in non-first context windows
  - Pad uni3c render_latent with 10 leading first-frame copies when prefix is active
  - Remove independent VAE encode for transition (now embedded in canvas)
…ing VAE unsqueeze, transition 12-frame adjustment
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants