Files
marcoc2-ComfyUI-AnotherUtils/video/video_auto_sync_hstack.py
T

101 lines
3.2 KiB
Python

import torch
import torch.nn.functional as F
class VideoAutoSyncHStack:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"image1": ("IMAGE",),
"fps1": (
"FLOAT",
{"default": 8.0, "min": 0.1, "max": 120.0, "step": 0.01},
),
"image2": ("IMAGE",),
"fps2": (
"FLOAT",
{"default": 48.0, "min": 0.1, "max": 120.0, "step": 0.01},
),
}
}
RETURN_TYPES = ("IMAGE", "FLOAT")
RETURN_NAMES = ("image", "fps")
FUNCTION = "sync_and_stack"
CATEGORY = "AnotherUtils/Video"
def sync_and_stack(self, image1, fps1, image2, fps2):
# Image shapes in ComfyUI: [B, H, W, C]
b1, h1, w1, c1 = image1.shape
b2, h2, w2, c2 = image2.shape
target_fps = max(fps1, fps2)
# Max height, ensuring it's an even number
target_height = max(h1, h2)
if target_height % 2 != 0:
target_height += 1
# Calculate durations and determine the shortest one
duration1 = b1 / fps1
duration2 = b2 / fps2
target_duration = min(duration1, duration2)
# Total frames for the final output
target_b = int(target_duration * target_fps)
if target_b == 0:
target_b = 1
# Determine target widths to match target height maintaining aspect ratio
target_w1 = int(w1 * (target_height / h1))
if target_w1 % 2 != 0:
target_w1 += 1
target_w2 = int(w2 * (target_height / h2))
if target_w2 % 2 != 0:
target_w2 += 1
# Permute to [B, C, H, W] for interpolation
img1_t = image1.permute(0, 3, 1, 2)
img2_t = image2.permute(0, 3, 1, 2)
# Resize if dimensions differ from target
if h1 != target_height or w1 != target_w1:
img1_t = F.interpolate(
img1_t,
size=(target_height, target_w1),
mode="bilinear",
align_corners=False,
)
if h2 != target_height or w2 != target_w2:
img2_t = F.interpolate(
img2_t,
size=(target_height, target_w2),
mode="bilinear",
align_corners=False,
)
# Precompute indices for FPS matching without re-encoding
# Similar to fps filter in ffmpeg duplicating/dropping frames
indices1 = []
indices2 = []
for i in range(target_b):
t = i / target_fps
idx1 = min(int(t * fps1), b1 - 1)
idx2 = min(int(t * fps2), b2 - 1)
indices1.append(idx1)
indices2.append(idx2)
# Gather the frames based on the synchronized indices
synced_img1 = img1_t[indices1] # [target_b, C, target_height, target_w1]
synced_img2 = img2_t[indices2] # [target_b, C, target_height, target_w2]
# Stack them side by side (Concatenate along Width dimension)
stacked = torch.cat((synced_img1, synced_img2), dim=3)
# Permute back to [B, H, W, C]
result = stacked.permute(0, 2, 3, 1)
return (result, float(target_fps))