From 558fbb52f6a3fee34d7c6f096cb4ed2a1436d8fb Mon Sep 17 00:00:00 2001 From: lovisdotio <158358668+lovisdotio@users.noreply.github.com> Date: Thu, 20 Nov 2025 15:22:00 -0800 Subject: [PATCH] update resize --- README.md | 8 +++---- replace_frames_node.py | 53 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 3f3b162..0b42015 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # ComfyUI Replace First & Last Frames -Replace the first and last frames of an image sequence in ComfyUI. Handles single frames or batches, auto-adjusts if needed. +Replace the first and last frames of an image sequence in ComfyUI. Auto-resizes frames to match video dimensions, handles batches, and adjusts parameters if needed. ## Installation @@ -9,12 +9,12 @@ Clone into `ComfyUI/custom_nodes/` and restart ComfyUI. Node appears in `image/a ## Usage - **images**: Your image sequence -- **start_frames**: Frame(s) to replace at start (repeats if needed) -- **last_frames**: Frame(s) to replace at end (repeats if needed) +- **start_frames**: Frame(s) to replace at start (auto-resized to match video) +- **last_frames**: Frame(s) to replace at end (auto-resized to match video) - **num_start_frames**: How many frames to replace at start (0-10000) - **num_last_frames**: How many frames to replace at end (0-10000) -Works with any values - auto-adjusts if you request too many frames. +**Auto-resizing**: Start and last frames are automatically resized to match the video dimensions (height, width, channels). No manual resizing needed. ## License diff --git a/replace_frames_node.py b/replace_frames_node.py index 8cf184a..a02588e 100644 --- a/replace_frames_node.py +++ b/replace_frames_node.py @@ -1,4 +1,5 @@ import torch +import torch.nn.functional as F class ReplaceFirstLastFrames: @@ -32,6 +33,15 @@ class ReplaceFirstLastFrames: CATEGORY = "image/animation" def replace_frames(self, images, start_frames, last_frames, num_start_frames, num_last_frames): + # Get dimensions from the main video + target_height = images.shape[1] + target_width = images.shape[2] + target_channels = images.shape[3] + + # Resize start_frames and last_frames to match video dimensions + start_frames = self._resize_frames(start_frames, target_height, target_width, target_channels) + last_frames = self._resize_frames(last_frames, target_height, target_width, target_channels) + # Get batch sizes num_images = images.shape[0] num_start_available = start_frames.shape[0] @@ -113,6 +123,49 @@ class ReplaceFirstLastFrames: return (output,) + def _resize_frames(self, frames, target_height, target_width, target_channels): + # Check if resize is needed + if frames.shape[1] == target_height and frames.shape[2] == target_width and frames.shape[3] == target_channels: + return frames + + # ComfyUI format: [batch, height, width, channels] + # PyTorch interpolate needs: [batch, channels, height, width] + frames_permuted = frames.permute(0, 3, 1, 2) + + # Resize spatial dimensions + if frames.shape[1] != target_height or frames.shape[2] != target_width: + frames_permuted = F.interpolate( + frames_permuted, + size=(target_height, target_width), + mode='bilinear', + align_corners=False + ) + + # Convert back to ComfyUI format + frames_resized = frames_permuted.permute(0, 2, 3, 1) + + # Handle channel mismatch (e.g., RGB vs RGBA) + if frames_resized.shape[3] != target_channels: + if target_channels == 3 and frames_resized.shape[3] == 4: + # RGBA to RGB: drop alpha + frames_resized = frames_resized[:, :, :, :3] + elif target_channels == 4 and frames_resized.shape[3] == 3: + # RGB to RGBA: add alpha channel (fully opaque) + alpha = torch.ones(frames_resized.shape[0], frames_resized.shape[1], frames_resized.shape[2], 1, + dtype=frames_resized.dtype, device=frames_resized.device) + frames_resized = torch.cat([frames_resized, alpha], dim=3) + elif frames_resized.shape[3] == 1 and target_channels == 3: + # Grayscale to RGB: repeat channel + frames_resized = frames_resized.repeat(1, 1, 1, 3) + elif frames_resized.shape[3] == 1 and target_channels == 4: + # Grayscale to RGBA: repeat channel + add alpha + frames_resized = frames_resized.repeat(1, 1, 1, 3) + alpha = torch.ones(frames_resized.shape[0], frames_resized.shape[1], frames_resized.shape[2], 1, + dtype=frames_resized.dtype, device=frames_resized.device) + frames_resized = torch.cat([frames_resized, alpha], dim=3) + + return frames_resized + def _prepare_frames(self, source_frames, num_needed): if num_needed <= 0: return None