intitial commit
This commit is contained in:
+46
@@ -0,0 +1,46 @@
|
||||
# Python
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
*.so
|
||||
.Python
|
||||
*.egg-info/
|
||||
dist/
|
||||
build/
|
||||
*.egg
|
||||
|
||||
# Virtual environments
|
||||
venv/
|
||||
env/
|
||||
ENV/
|
||||
|
||||
# IDE
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
.DS_Store
|
||||
|
||||
# OS
|
||||
Thumbs.db
|
||||
.DS_Store
|
||||
|
||||
# ComfyUI specific
|
||||
*.safetensors
|
||||
*.ckpt
|
||||
*.pth
|
||||
*.pt
|
||||
models/
|
||||
input/
|
||||
output/
|
||||
temp/
|
||||
|
||||
# Logs
|
||||
*.log
|
||||
|
||||
# Test files
|
||||
test_*.py
|
||||
tests/
|
||||
|
||||
TODO.md
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2025
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,48 @@
|
||||
# Wan VACE Prep
|
||||
|
||||
A ComfyUI node for smooth video transitions with Wan VACE - generates control video and masks
|
||||
|
||||
## What It Does
|
||||
|
||||
Takes two video clips and prepares them for smooth VACE-based transition
|
||||
- Extracts context frames from both clips
|
||||
- Builds VACE control video and mask
|
||||
- Output video segments for final assembly
|
||||
- Optionally include context frames in output segments for crossfade or other artifact-reduction processing
|
||||
|
||||
## Installation
|
||||
Clone this repository into your custom_nodes directory.
|
||||
|
||||
```bash
|
||||
cd /path/to/comfyui/custom_nodes
|
||||
git clone https://github.com/stuttlepress/ComfyUI-Wan-VACE-Prep
|
||||
```
|
||||
|
||||
Restart ComfyUI.
|
||||
|
||||
## Usage
|
||||
|
||||
The node appears as **"Wan VACE Prep"** in the `video/VACE` category.
|
||||
|
||||

|
||||
|
||||
## Parameters
|
||||
|
||||
### context_frames
|
||||
Reference frames from each video edge that VACE uses for interpolation. These frames guide the model and are preserved in the output. Must be a multiple of 4.
|
||||
|
||||
### replace_frames
|
||||
Number of frames at each transition edge to discard and regenerate. These create the actual transition blend zone. Must be a multiple of 4.
|
||||
|
||||
### add_frames
|
||||
Number of completely new frames to generate between the two clips, extending the transition duration. Must be a multiple of 4.
|
||||
|
||||
Note: Wan likes to generate 4n+1 frames at a time. If you ask it for some other amount, it will silently round your request down to the nearest 4n+1. For this reason, parameters are restricted to multiples of 4, and the node adds +1 to the number of generated frames.
|
||||
|
||||
### crossfade_mode
|
||||
- **False (default)**: Clean output - start_images and end_images exclude context frames. Use this for simple concatenation.
|
||||
- **True**: Cross-fade mode - includes context frames in start_images and end_images so they can be blended with the context frames included in the VACE generated clip. This can help mitigate color or brightness artifacts at the transition.
|
||||
|
||||
## License
|
||||
|
||||
MIT License - feel free to use, modify, and distribute.
|
||||
@@ -0,0 +1,7 @@
|
||||
"""
|
||||
Wan VACE Prep - Custom node for preparing videos for Wan VACE generation
|
||||
"""
|
||||
|
||||
from .wan_vace_prep import NODE_CLASS_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS
|
||||
|
||||
__all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS']
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 195 KiB |
@@ -0,0 +1,107 @@
|
||||
import torch
|
||||
|
||||
class WanVACEPrep:
|
||||
"""Generates VACE control video and mask for smooth transitions between two videos using context frames and frame replacement."""
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"video_1": ("IMAGE",),
|
||||
"video_2": ("IMAGE",),
|
||||
"context_frames": ("INT", {
|
||||
"default": 8,
|
||||
"min": 4,
|
||||
"max": 120,
|
||||
"step": 4,
|
||||
"tooltip": "Reference frames from each video edge for VACE interpolation (multiple of 4)."
|
||||
}),
|
||||
"replace_frames": ("INT", {
|
||||
"default": 8,
|
||||
"min": 0,
|
||||
"max": 120,
|
||||
"step": 4,
|
||||
"tooltip": "Number of frames to regenerate at each transition edge (multiple of 4)."
|
||||
}),
|
||||
"add_frames": ("INT", {
|
||||
"default": 0,
|
||||
"min": 0,
|
||||
"max": 240,
|
||||
"step": 4,
|
||||
"tooltip": "Number of new transition frames to generate, in addition to the replace_frames (multiple of 4)."
|
||||
}),
|
||||
"crossfade_mode": ("BOOLEAN", {
|
||||
"default": False,
|
||||
"tooltip": "Include context frames in start_images and end_images for cross-fade or other use cases."
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("IMAGE", "MASK", "INT", "INT", "INT", "IMAGE", "IMAGE")
|
||||
RETURN_NAMES = ("control_video", "control_mask", "width", "height", "length", "start_images", "end_images")
|
||||
FUNCTION = "vace_prep"
|
||||
CATEGORY = "video/VACE"
|
||||
|
||||
def vace_prep(self, video_1, video_2, context_frames, replace_frames, add_frames, crossfade_mode):
|
||||
v1_len = video_1.shape[0]
|
||||
v2_len = video_2.shape[0]
|
||||
required_frames = context_frames + replace_frames
|
||||
|
||||
if v1_len < required_frames:
|
||||
raise ValueError(
|
||||
f"context_frames ({context_frames}) + replace_frames ({replace_frames}) = {required_frames}, "
|
||||
f"which is too large for video_1 length of {v1_len}. "
|
||||
f"Reduce context_frames or replace_frames."
|
||||
)
|
||||
|
||||
if v2_len < required_frames:
|
||||
raise ValueError(
|
||||
f"context_frames ({context_frames}) + replace_frames ({replace_frames}) = {required_frames}, "
|
||||
f"which is too large for video_2 length of {v2_len}. "
|
||||
f"Reduce context_frames or replace_frames."
|
||||
)
|
||||
|
||||
if replace_frames > 0:
|
||||
v1_context = video_1[-(context_frames + replace_frames):-replace_frames]
|
||||
v2_context = video_2[replace_frames:context_frames + replace_frames]
|
||||
else:
|
||||
v1_context = video_1[-context_frames:]
|
||||
v2_context = video_2[:context_frames]
|
||||
|
||||
height = int(video_1.shape[1])
|
||||
width = int(video_1.shape[2])
|
||||
channels = video_1.shape[3]
|
||||
|
||||
# Wan wants to generate 4n+1 frames. If we don't provide that,
|
||||
# it will quietly round down to the nearest 4n+1. So we add 1 here.
|
||||
vace_count = replace_frames * 2 + add_frames + 1
|
||||
vace_frames = torch.full((vace_count, height, width, channels), 0.5, dtype=video_1.dtype, device=video_1.device)
|
||||
|
||||
control_video = torch.cat([v1_context, vace_frames, v2_context], dim=0)
|
||||
|
||||
total_frames = context_frames * 2 + vace_count
|
||||
mask = torch.zeros((total_frames, height, width), dtype=torch.float32, device=video_1.device)
|
||||
mask[context_frames:context_frames + vace_count] = 1.0
|
||||
|
||||
if crossfade_mode:
|
||||
if replace_frames > 0:
|
||||
start_images = video_1[:-replace_frames]
|
||||
else:
|
||||
start_images = video_1
|
||||
end_images = video_2[replace_frames:]
|
||||
else:
|
||||
start_images = video_1[:-(context_frames + replace_frames)]
|
||||
end_images = video_2[context_frames + replace_frames:]
|
||||
|
||||
length = int(control_video.shape[0])
|
||||
|
||||
return (control_video, mask, width, height, length, start_images, end_images)
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"WanVACEPrep": WanVACEPrep
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"WanVACEPrep": "Wan VACE Prep"
|
||||
}
|
||||
Reference in New Issue
Block a user