Files
mgfxer-ComfyUI-FrameFX/DynamicAnimatedWeights.py
T
2024-07-10 20:47:20 -05:00

603 lines
34 KiB
Python

import os
from PIL import Image, ImageDraw, ImageFilter, ImageOps, ImageChops, ImageEnhance
import numpy as np
import torch
import random
class DynamicAnimatedWeightsHelper:
def __init__(self):
pass
@classmethod
def INPUT_TYPES(cls):
base_animation_types = ["LeftToRight", "RightToLeft", "TopDown", "BottomToTop", "GrowingCircle", "ShrinkingCircle",
"DiagonalTopLeft-BottomRight", "DiagonalBottomRight-TopLeft", "DiagonalTopRight-BottomLeft",
"DiagonalBottomLeft-TopRight", "Fade", "SqSpinCw", "SqSpinCCW", "VenetianBlindsHorizontal",
"VenetianBlindsVertical", "DiagonalVenetianBlinds1", "DiagonalVenetianBlinds2"]
animation_types = base_animation_types + ["Random", "RandomNoVenetian"]
easing_options = ["ease_in", "ease_out", "ease_in_out", "false"]
generation_options = ["Only Transitions", "Generate QR", "Generate Edge-FX", "Generate All"]
return {
"required": {
"animation_type_1": (animation_types, {"default": cls.random_animation()}),
"animation_type_2": (animation_types, {"default": cls.random_animation()}),
"animation_type_3": (animation_types, {"default": cls.random_animation()}),
"animation_type_4": (animation_types, {"default": cls.random_animation()}),
"animation_type_5": (animation_types, {"default": cls.random_animation()}),
"animation_type_6": (animation_types, {"default": cls.random_animation()}),
"animation_type_7": (animation_types, {"default": cls.random_animation()}),
"animation_type_8": (animation_types, {"default": cls.random_animation()}),
"animation_type_9": (animation_types, {"default": cls.random_animation()}),
"animation_type_10": (animation_types, {"default": cls.random_animation()}),
"animation_type_11": (animation_types, {"default": cls.random_animation()}),
"animation_type_12": (animation_types, {"default": cls.random_animation()}),
"transition_easing": (easing_options, {"default": "false"}),
"blur_easing": (easing_options, {"default": "false"}),
"frame_width": ("INT", {"default": 512, "min": 1, "step": 1, "display": "number"}),
"frame_height": ("INT", {"default": 512, "min": 1, "step": 1, "display": "number"}),
"hold_frames": ("INT", {"default": 8, "min": 1, "step": 1, "display": "number"}),
"transition_frames": ("INT", {"default": 20, "min": 1, "step": 1, "display": "number"}),
"padding_frames": ("INT", {"default": 6, "min": 0, "step": 1, "display": "number"}),
"input_frames": ("INT", {"default": 5, "min": 1, "step": 1, "display": "number"}),
"gaussian_blur_amount": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 10.0, "step": 0.1, "display": "slider"}),
"edge_fx_thickness": ("INT", {"default": 1, "min": 1, "max": 10, "step": 1, "display": "number"}),
"push_fx": ("INT", {"default": 0, "min": 0, "max": 30, "step": 1, "display": "number"}),
"retract_fx": ("INT", {"default": 0, "min": 0, "max": 30, "step": 1, "display": "number"}),
"fx_cull_white_frames": ("FLOAT", {"default": 10.0, "min": 0.0, "max": 100.0, "step": 0.1, "display": "slider"}),
"qr_greyness": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 1.0, "step": 0.01, "display": "slider"}),
"random_seed": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 10.0, "step": 0.1, "display": "slider"}),
"edgeFade_contrast": ("FLOAT", {"default": 1.0, "min": 0.5, "max": 6.0, "step": 0.1, "display": "slider"}), # Increased contrast effectiveness
"edgeFade_blur": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 10.0, "step": 0.1, "display": "slider"}), # Final blur adjustment for Edge FX Fade
"generation_mode": (generation_options, {"default": "Only Transitions"}),
"edge_fx_fade_balance": ("FLOAT", {"default": 0.5, "min": 0.1, "max": 0.9, "step": 0.05, "display": "slider"}),
"venetian_bars": ("INT", {"default": 4, "min": 1, "step": 1, "display": "number"})
}
}
@classmethod
def random_animation(cls, exclude_venetian=False):
venetian_types = ["VenetianBlindsHorizontal", "VenetianBlindsVertical", "DiagonalVenetianBlinds1", "DiagonalVenetianBlinds2"]
base_animation_types = ["LeftToRight", "RightToLeft", "TopDown", "BottomToTop", "GrowingCircle", "ShrinkingCircle",
"DiagonalTopLeft-BottomRight", "DiagonalBottomRight-TopLeft", "DiagonalTopRight-BottomLeft",
"DiagonalBottomLeft-TopRight", "Fade", "SqSpinCw", "SqSpinCCW", "VenetianBlindsHorizontal",
"VenetianBlindsVertical", "DiagonalVenetianBlinds1", "DiagonalVenetianBlinds2"]
available_types = base_animation_types
if exclude_venetian:
available_types = [t for t in available_types if t not in venetian_types]
return random.choice(available_types)
RETURN_TYPES = ("IMAGE", "IMAGE", "IMAGE", "IMAGE", "INT")
RETURN_NAMES = ("transitions", "qr_mode", "edge_fx", "edge_fx_fade", "total_frames")
FUNCTION = "run"
CATEGORY = "mgfxer"
def run(self, **kwargs):
animation_types = [kwargs[f'animation_type_{i}'] for i in range(1, 13)]
frame_width = kwargs['frame_width']
frame_height = kwargs['frame_height']
hold_frames = kwargs['hold_frames']
transition_frames = kwargs['transition_frames']
padding_frames = kwargs['padding_frames']
input_frames = kwargs['input_frames']
gaussian_blur_amount = kwargs['gaussian_blur_amount'] * 20 # Amplify the blur amount here
edgeFade_blur = kwargs['edgeFade_blur'] * 10 # Amplify the final blur amount by 10
edgeFade_contrast = kwargs['edgeFade_contrast']
transition_easing = kwargs['transition_easing']
blur_easing = kwargs['blur_easing']
qr_greyness = kwargs['qr_greyness']
random_seed = int(kwargs['random_seed'] * 10)
edge_fx_thickness = kwargs['edge_fx_thickness']
push_fx = kwargs['push_fx']
retract_fx = kwargs['retract_fx']
fx_cull_white_frames = kwargs['fx_cull_white_frames']
generation_mode = kwargs['generation_mode']
edge_fx_fade_balance = kwargs['edge_fx_fade_balance']
venetian_bars = kwargs['venetian_bars']
random.seed(random_seed)
images = []
qr_images = []
edge_fx_frames = []
edge_fx_fade_frames = []
total_frames = 0
for i in range(input_frames):
animation_type = animation_types[i % 12]
if animation_type == "Random":
animation_type = self.random_animation()
elif animation_type == "RandomNoVenetian":
animation_type = self.random_animation(exclude_venetian=True)
frames, frame_count = self.generate_animation(animation_type, frame_width, frame_height, hold_frames, transition_frames, gaussian_blur_amount, transition_easing, blur_easing, venetian_bars)
if generation_mode in ["Generate QR", "Generate All"]:
qr_frames = self.apply_qr_mode(frames, i, input_frames, hold_frames + transition_frames)
qr_images.extend(qr_frames)
images.extend(frames)
total_frames += frame_count
final_padding_color_qr = 'black' if input_frames % 2 == 0 else 'white'
for _ in range(padding_frames):
frame = self.create_frame(frame_width, frame_height, is_black=False)
images.append(frame)
if generation_mode in ["Generate QR", "Generate All"]:
qr_frame = self.create_frame(frame_width, frame_height, is_black=(final_padding_color_qr == 'black'))
qr_images.append(qr_frame)
total_frames += 1
if generation_mode in ["Generate QR", "Generate All"]:
qr_images = self.apply_qr_greyness(qr_images, qr_greyness, frame_width, frame_height)
if generation_mode in ["Generate Edge-FX", "Generate All"]:
edge_fx_frames = self.generate_edge_fx(images, edge_fx_thickness, frame_width, frame_height, fx_cull_white_frames)
edge_fx_frames = self.apply_push_retract_fx(edge_fx_frames, push_fx, retract_fx, frame_width, frame_height)
edge_fx_frames = self.check_and_correct_white_frames(edge_fx_frames, frame_width, frame_height, fx_cull_white_frames)
if generation_mode == "Generate All":
unblurred_edge_fx = edge_fx_frames.copy()
final_edge_fx = self.composite_edge_fx(unblurred_edge_fx, edge_fx_frames)
edge_fx_fade_frames = self.apply_fade_to_edge_fx(final_edge_fx, transition_frames, hold_frames, edge_fx_fade_balance)
edge_fx_fade_frames = self.apply_contrast_to_frames(edge_fx_fade_frames, edgeFade_contrast)
edge_fx_fade_frames = self.apply_blur_to_frames(edge_fx_fade_frames, edgeFade_blur)
image_batch = torch.cat([self.process_image_for_output(frame) for frame in images], dim=0)
qr_image_batch = torch.cat([self.process_image_for_output(frame) for frame in (qr_images if qr_images else images)], dim=0)
edge_fx_batch = torch.cat([self.process_image_for_output(frame) for frame in (edge_fx_frames if edge_fx_frames else images)], dim=0)
edge_fx_fade_batch = torch.cat([self.process_image_for_output(frame) for frame in (edge_fx_fade_frames if edge_fx_fade_frames else images)], dim=0)
return (image_batch, qr_image_batch, edge_fx_batch, edge_fx_fade_batch, total_frames)
def generate_animation(self, animation_type, frame_width, frame_height, hold_frames, transition_frames, gaussian_blur_amount, transition_easing, blur_easing, venetian_bars):
if animation_type == "LeftToRight":
return self.generate_left_to_right_animation(frame_width, frame_height, hold_frames, transition_frames, gaussian_blur_amount, transition_easing, blur_easing)
elif animation_type == "RightToLeft":
return self.generate_rotated_animation(self.generate_left_to_right_animation, frame_width, frame_height, hold_frames, transition_frames, gaussian_blur_amount, transition_easing, blur_easing, 180)
elif animation_type == "TopDown":
return self.generate_rotated_animation(self.generate_left_to_right_animation, frame_width, frame_height, hold_frames, transition_frames, gaussian_blur_amount, transition_easing, blur_easing, 90, resize=True)
elif animation_type == "BottomToTop":
return self.generate_rotated_animation(self.generate_left_to_right_animation, frame_width, frame_height, hold_frames, transition_frames, gaussian_blur_amount, transition_easing, blur_easing, 270, resize=True)
elif animation_type == "GrowingCircle":
return self.generate_growing_circle_animation(frame_width, frame_height, hold_frames, transition_frames, gaussian_blur_amount, transition_easing, blur_easing)
elif animation_type == "ShrinkingCircle":
return self.generate_shrinking_circle_animation(frame_width, frame_height, hold_frames, transition_frames, gaussian_blur_amount, transition_easing, blur_easing)
elif animation_type in ["DiagonalTopLeft-BottomRight", "DiagonalBottomRight-TopLeft", "DiagonalTopRight-BottomLeft", "DiagonalBottomLeft-TopRight"]:
return self.generate_diagonal_animation(animation_type, frame_width, frame_height, hold_frames, transition_frames, gaussian_blur_amount, transition_easing, blur_easing)
elif animation_type == "Fade":
return self.generate_fade_animation(frame_width, frame_height, hold_frames, transition_frames, transition_easing, blur_easing)
elif animation_type == "SqSpinCw":
return self.generate_sq_spin_animation(frame_width, frame_height, hold_frames, transition_frames, gaussian_blur_amount, transition_easing, blur_easing, clockwise=True)
elif animation_type == "SqSpinCCW":
return self.generate_sq_spin_animation(frame_width, frame_height, hold_frames, transition_frames, gaussian_blur_amount, transition_easing, blur_easing, clockwise=False)
elif animation_type == "VenetianBlindsHorizontal":
return self.generate_venetian_blinds_animation(frame_width, frame_height, hold_frames, transition_frames, gaussian_blur_amount, transition_easing, blur_easing, vertical=False, venetian_bars=venetian_bars)
elif animation_type == "VenetianBlindsVertical":
return self.generate_venetian_blinds_animation(frame_width, frame_height, hold_frames, transition_frames, gaussian_blur_amount, transition_easing, blur_easing, vertical=True, venetian_bars=venetian_bars)
elif animation_type == "DiagonalVenetianBlinds1":
return self.generate_diagonal_venetian_blinds_animation(frame_width, frame_height, hold_frames, transition_frames, gaussian_blur_amount, transition_easing, blur_easing, angle=45, venetian_bars=venetian_bars)
elif animation_type == "DiagonalVenetianBlinds2":
return self.generate_diagonal_venetian_blinds_animation(frame_width, frame_height, hold_frames, transition_frames, gaussian_blur_amount, transition_easing, blur_easing, angle=135, venetian_bars=venetian_bars)
def generate_edge_fx(self, frames, offset, frame_width, frame_height, fx_cull_white_frames):
edge_fx_frames = []
for i in range(len(frames)):
current_frame = frames[i]
if i + offset < len(frames):
next_frame = frames[i + offset]
else:
next_frame = Image.new('RGB', (frame_width, frame_height), color='white')
mask = ImageChops.difference(current_frame, next_frame)
mask = mask.convert('L').point(lambda x: 255 if x > 0 else 0, mode='1')
edge_fx_frame = Image.new('RGB', (frame_width, frame_height), color='black')
edge_fx_frame.paste(Image.new('RGB', (frame_width, frame_height), color='white'), mask=mask)
if ImageChops.invert(edge_fx_frame).getbbox() is None:
edge_fx_frame = Image.new('RGB', (frame_width, frame_height), color='black')
edge_fx_frames.append(edge_fx_frame)
edge_fx_frames = self.check_and_correct_white_frames(edge_fx_frames, frame_width, frame_height, fx_cull_white_frames)
return edge_fx_frames
def check_and_correct_white_frames(self, frames, frame_width, frame_height, fx_cull_white_frames):
corrected_frames = []
for frame in frames:
white_pixels = np.sum(np.array(frame) == 255)
total_pixels = frame_width * frame_height * 3
white_percentage = (white_pixels / total_pixels) * 100
if white_percentage > fx_cull_white_frames:
corrected_frame = Image.new('RGB', (frame_width, frame_height), color='black')
else:
corrected_frame = frame
corrected_frames.append(corrected_frame)
return corrected_frames
def apply_push_retract_fx(self, frames, push_fx, retract_fx, frame_width, frame_height):
black_frame = Image.new('RGB', (frame_width, frame_height), color='black')
if push_fx > 0:
frames = [black_frame] * push_fx + frames[:-push_fx]
elif retract_fx > 0:
frames = frames[retract_fx:] + [black_frame] * retract_fx
return frames
def apply_qr_greyness(self, frames, qr_greyness, frame_width, frame_height):
grey_image_white = Image.new('RGB', (frame_width, frame_height), color=(255, 255, 255))
grey_image_black = Image.new('RGB', (frame_width, frame_height), color=(235, 235, 235))
grey_blend_white = lambda img: Image.blend(img, grey_image_white, qr_greyness)
grey_blend_black = lambda img: Image.blend(img, grey_image_black, qr_greyness)
frames = [grey_blend_black(grey_blend_white(frame)) for frame in frames]
return frames
def generate_diagonal_venetian_blinds_animation(self, frame_width, frame_height, hold_frames, transition_frames, gaussian_blur_amount, transition_easing, blur_easing, angle, venetian_bars):
frames, _ = self.generate_venetian_blinds_animation(frame_width, frame_height, hold_frames, transition_frames, gaussian_blur_amount, transition_easing, blur_easing, vertical=True, venetian_bars=venetian_bars)
enlarged_frames = []
for frame in frames:
large_canvas = Image.new('RGB', (frame_width * 2, frame_height * 2), 'black')
enlarged_frame = frame.resize((int(frame_width * 1.5), int(frame_height * 1.5)), Image.LANCZOS)
large_canvas.paste(enlarged_frame, ((large_canvas.width - enlarged_frame.width) // 2, (large_canvas.height - enlarged_frame.height) // 2))
rotated_frame = large_canvas.rotate(angle, expand=True)
cropped_frame = rotated_frame.crop(((rotated_frame.width - frame_width) // 2, (rotated_frame.height - frame_height) // 2,
(rotated_frame.width + frame_width) // 2, (rotated_frame.height + frame_height) // 2))
enlarged_frames.append(cropped_frame)
total_frames = hold_frames + transition_frames
return enlarged_frames, total_frames
def generate_venetian_blinds_animation(self, frame_width, frame_height, hold_frames, transition_frames, gaussian_blur_amount, transition_easing, blur_easing, vertical=True, venetian_bars=4):
images = []
bar_size = frame_height // venetian_bars if vertical else frame_width // venetian_bars
for _ in range(hold_frames):
frame = self.create_frame(frame_width, frame_height, is_black=True)
images.append(frame)
for i in range(transition_frames):
ease_factor = self.calculate_ease_factor(i, transition_frames, transition_easing)
blur_factor = self.calculate_blur_factor(i, transition_frames, blur_easing, gaussian_blur_amount)
frame = self.create_venetian_blinds_transition_frame(frame_width, frame_height, venetian_bars, bar_size, ease_factor, blur_factor['gaussian'], vertical)
images.append(frame)
total_frames = hold_frames + transition_frames
return images, total_frames
def create_venetian_blinds_transition_frame(self, frame_width, frame_height, num_bars, bar_size, ease_factor, gaussian_blur_amount, vertical=True):
frame = Image.new('RGB', (frame_width, frame_height), color='black')
draw = ImageDraw.Draw(frame)
for j in range(num_bars):
if vertical:
left = j * bar_size
right = left + int(bar_size * ease_factor)
draw.rectangle([left, 0, right, frame_height], fill='white')
else:
top = j * bar_size
bottom = top + int(bar_size * ease_factor)
draw.rectangle([0, top, frame_width, bottom], fill='white')
if gaussian_blur_amount > 0:
frame = frame.filter(ImageFilter.GaussianBlur(gaussian_blur_amount))
return frame
def generate_sq_spin_animation(self, frame_width, frame_height, hold_frames, transition_frames, gaussian_blur_amount, transition_easing, blur_easing, clockwise=True):
images = []
initial_square_size = 2
max_square_size = int(frame_width * 1.65)
canvas_size = int(frame_width * 2.5)
for _ in range(hold_frames):
frame = self.create_frame(frame_width, frame_height, is_black=True)
images.append(frame)
for i in range(transition_frames):
ease_factor = self.calculate_ease_factor(i, transition_frames, transition_easing)
blur_factor = self.calculate_blur_factor(i, transition_frames, blur_easing, gaussian_blur_amount)
square_size = initial_square_size + int((max_square_size - initial_square_size) * ease_factor)
rotation_angle = (235 * ease_factor) if clockwise else (-235 * ease_factor)
frame = self.create_sq_spin_transition_frame(canvas_size, frame_width, frame_height, square_size, rotation_angle, blur_factor['gaussian'])
images.append(frame)
total_frames = hold_frames + transition_frames
return images, total_frames
def create_sq_spin_transition_frame(self, canvas_size, frame_width, frame_height, square_size, rotation_angle, gaussian_blur_amount):
frame = Image.new('RGB', (canvas_size, canvas_size), color='black')
draw = ImageDraw.Draw(frame)
top_left = ((canvas_size - square_size) // 2, (canvas_size - square_size) // 2)
bottom_right = (top_left[0] + square_size, top_left[1] + square_size)
draw.rectangle([top_left, bottom_right], fill='white')
frame = frame.rotate(rotation_angle, expand=True)
left = (frame.width - frame_width) // 2
top = (frame.height - frame_height) // 2
frame = frame.crop((left, top, left + frame_width, top + frame_height))
if gaussian_blur_amount > 0:
frame = frame.filter(ImageFilter.GaussianBlur(gaussian_blur_amount))
return frame
def generate_rotated_animation(self, animation_func, frame_width, frame_height, hold_frames, transition_frames, gaussian_blur_amount, transition_easing, blur_easing, rotation_angle, resize=False):
images, total_frames = animation_func(frame_width, frame_height, hold_frames, transition_frames, gaussian_blur_amount, transition_easing, blur_easing)
rotated_images = [img.rotate(rotation_angle, expand=True) for img in images]
if resize:
rotated_images = [img.resize((frame_width, frame_height), Image.LANCZOS) for img in rotated_images]
return rotated_images, total_frames
def generate_left_to_right_animation(self, frame_width, frame_height, hold_frames, transition_frames, gaussian_blur_amount, transition_easing, blur_easing):
images = []
for _ in range(hold_frames):
frame = self.create_frame(frame_width, frame_height, is_black=True)
images.append(frame)
for i in range(transition_frames):
ease_factor = self.calculate_ease_factor(i, transition_frames, transition_easing)
blur_factor = self.calculate_blur_factor(i, transition_frames, blur_easing, gaussian_blur_amount)
frame = self.create_left_to_right_transition_frame(frame_width, frame_height, blur_factor['gaussian'], transition_frames, frame_index=i, ease_factor=ease_factor)
images.append(frame)
total_frames = hold_frames + transition_frames
return images, total_frames
def generate_growing_circle_animation(self, frame_width, frame_height, hold_frames, transition_frames, gaussian_blur_amount, transition_easing, blur_easing):
images = []
circle_size = int(frame_width * 1.5)
for _ in range(hold_frames):
frame = self.create_frame(frame_width, frame_height, is_black=True)
images.append(frame)
for i in range(transition_frames):
ease_factor = self.calculate_ease_factor(i, transition_frames, transition_easing)
blur_factor = self.calculate_blur_factor(i, transition_frames, blur_easing, gaussian_blur_amount)
frame = self.create_growing_circle_transition_frame(frame_width, frame_height, circle_size, blur_factor['gaussian'], transition_frames, frame_index=i, ease_factor=ease_factor)
images.append(frame)
total_frames = hold_frames + transition_frames
return images, total_frames
def generate_shrinking_circle_animation(self, frame_width, frame_height, hold_frames, transition_frames, gaussian_blur_amount, transition_easing, blur_easing):
images = []
circle_size = int(frame_width * 1.5)
for _ in range(hold_frames):
frame = self.create_frame(frame_width, frame_height, is_black=True)
images.append(frame)
for i in range(transition_frames):
ease_factor = self.calculate_ease_factor(i, transition_frames, transition_easing)
blur_factor = self.calculate_blur_factor(i, transition_frames, blur_easing, gaussian_blur_amount)
frame = self.create_shrinking_circle_transition_frame(frame_width, frame_height, circle_size, blur_factor['gaussian'], transition_frames, frame_index=i, ease_factor=ease_factor)
images.append(frame)
total_frames = hold_frames + transition_frames
return images, total_frames
def generate_diagonal_animation(self, animation_type, frame_width, frame_height, hold_frames, transition_frames, gaussian_blur_amount, transition_easing, blur_easing):
frames, _ = self.generate_left_to_right_animation(frame_width, frame_height, hold_frames, transition_frames, gaussian_blur_amount, transition_easing, blur_easing)
enlarged_frames = []
for frame in frames:
large_canvas = Image.new('RGB', (frame_width * 2, frame_height * 2), 'black')
enlarged_frame = frame.resize((int(frame_width * 1.5), int(frame_height * 1.5)), Image.LANCZOS)
large_canvas.paste(enlarged_frame, ((large_canvas.width - enlarged_frame.width) // 2, (large_canvas.height - enlarged_frame.height) // 2))
if animation_type == "DiagonalTopLeft-BottomRight":
rotated_frame = large_canvas.rotate(45, expand=True)
elif animation_type == "DiagonalBottomRight-TopLeft":
rotated_frame = large_canvas.rotate(225, expand=True)
elif animation_type == "DiagonalTopRight-BottomLeft":
rotated_frame = large_canvas.rotate(135, expand=True)
elif animation_type == "DiagonalBottomLeft-TopRight":
rotated_frame = large_canvas.rotate(315, expand=True)
cropped_frame = rotated_frame.crop(((rotated_frame.width - frame_width) // 2, (rotated_frame.height - frame_height) // 2,
(rotated_frame.width + frame_width) // 2, (rotated_frame.height + frame_height) // 2))
enlarged_frames.append(cropped_frame)
total_frames = hold_frames + transition_frames
return enlarged_frames, total_frames
def generate_fade_animation(self, frame_width, frame_height, hold_frames, transition_frames, transition_easing, blur_easing):
images = []
for _ in range(hold_frames):
frame = self.create_frame(frame_width, frame_height, is_black=True)
images.append(frame)
for i in range(transition_frames):
ease_factor = self.calculate_ease_factor(i, transition_frames, transition_easing)
blur_factor = self.calculate_blur_factor(i, transition_frames, blur_easing, 0)
gray_value = int(255 * (i / transition_frames) * ease_factor)
frame = Image.new('RGB', (frame_width, frame_height), color=(gray_value, gray_value, gray_value))
images.append(frame)
total_frames = hold_frames + transition_frames
return images, total_frames
def create_frame(self, frame_width, frame_height, is_black=True):
color = 'black' if is_black else 'white'
frame = Image.new('RGB', (frame_width, frame_height), color=color)
return frame
def create_left_to_right_transition_frame(self, frame_width, frame_height, blur_factor, transition_frames, frame_index, ease_factor):
frame = Image.new('RGB', (frame_width, frame_height), color='black')
draw = ImageDraw.Draw(frame)
box_width = int(frame_width * ease_factor)
draw.rectangle([0, 0, box_width, frame_height], fill='white')
if blur_factor > 0:
frame = frame.filter(ImageFilter.GaussianBlur(blur_factor))
return frame
def create_growing_circle_transition_frame(self, frame_width, frame_height, circle_size, blur_factor, transition_frames, frame_index, ease_factor):
frame = Image.new('RGB', (circle_size, circle_size), color='black')
draw = ImageDraw.Draw(frame)
max_radius = circle_size // 2
radius = int(max_radius * ease_factor)
draw.ellipse([(circle_size // 2 - radius, circle_size // 2 - radius),
(circle_size // 2 + radius, circle_size // 2 + radius)], fill='white')
frame = frame.crop((circle_size//2-frame_width//2, circle_size//2-frame_height//2, circle_size//2+frame_width//2, circle_size//2+frame_height//2))
if blur_factor > 0:
frame = frame.filter(ImageFilter.GaussianBlur(blur_factor))
return frame
def create_shrinking_circle_transition_frame(self, frame_width, frame_height, circle_size, blur_factor, transition_frames, frame_index, ease_factor):
frame = Image.new('RGB', (circle_size, circle_size), color='white')
draw = ImageDraw.Draw(frame)
max_radius = circle_size // 2
radius = int(max_radius * (1 - ease_factor))
draw.ellipse([(circle_size // 2 - radius, circle_size // 2 - radius),
(circle_size // 2 + radius, circle_size // 2 + radius)], fill='black')
frame = frame.crop((circle_size//2-frame_width//2, circle_size//2-frame_height//2, circle_size//2+frame_width//2, circle_size//2+frame_height//2))
if blur_factor > 0:
frame = frame.filter(ImageFilter.GaussianBlur(blur_factor))
return frame
def calculate_ease_factor(self, frame_index, transition_frames, transition_easing):
t = frame_index / transition_frames
if transition_easing == "ease_in":
return self.ease_in_quad(t)
elif transition_easing == "ease_out":
return self.ease_out_quad(t)
elif transition_easing == "ease_in_out":
return self.ease_in_out_quad(t)
else: # "false" or any other value
return t # Linear easing
def calculate_blur_factor(self, frame_index, transition_frames, blur_easing, gaussian_blur_amount):
t = frame_index / transition_frames
if blur_easing == "ease_in":
blur_factor = self.ease_in_quad(t)
elif blur_easing == "ease_out":
blur_factor = 1 - self.ease_out_quad(1 - t)
elif blur_easing == "ease_in_out":
blur_factor = self.ease_in_out_quad_for_blur(t)
else: # "false" or any other value
blur_factor = 1 # Constant blur amount for linear
return {
'gaussian': gaussian_blur_amount * blur_factor
}
def ease_in_quad(self, t):
return t * t
def ease_out_quad(self, t):
return t * (2 - t)
def ease_in_out_quad(self, t):
return 2 * t * t if t < 0.5 else -1 + (4 - 2 * t) * t
def ease_in_out_quad_for_blur(self, t):
if t < 0.5:
return 2 * t * t
else:
return 1 - pow(-2 * t + 2, 2) / 2
def apply_qr_mode(self, frames, cycle_index, input_frames, cycle_length):
if cycle_index % 2 == 1: # Apply inversion for even cycles
frames = [ImageOps.invert(frame) for frame in frames]
return frames
def process_image_for_output(self, image) -> torch.Tensor:
i = ImageOps.exif_transpose(image)
if i.mode == 'I':
i = i.point(lambda i: i * (1 / 255))
image = i.convert("RGB")
image_np = np.array(image).astype(np.float32) / 255.0
return torch.from_numpy(image_np)[None,]
def apply_blur_to_frames(self, frames, gaussian_blur_amount):
return [frame.filter(ImageFilter.GaussianBlur(gaussian_blur_amount)) for frame in frames]
def apply_contrast_to_frames(self, frames, contrast_factor):
return [ImageEnhance.Contrast(frame).enhance(contrast_factor * 2) for frame in frames] # Apply 3x contrast factor
def composite_edge_fx(self, unblurred_frames, blurred_frames):
final_frames = []
for unblurred, blurred in zip(unblurred_frames, blurred_frames):
# Create a black background
final_frame = Image.new('RGB', unblurred.size, color='black')
# Check if the unblurred frame has an alpha channel
if unblurred.mode == 'RGBA':
mask = unblurred.split()[3]
else:
# If no alpha channel, use the image itself as a mask
mask = unblurred.convert('L')
# Use unblurred frame as mask to cut out blurred frame
final_frame.paste(blurred, mask=mask)
final_frames.append(final_frame)
return final_frames
def apply_fade_to_edge_fx(self, edge_fx_frames, transition_frames, hold_frames, fade_balance):
faded_frames = []
frames_per_cycle = transition_frames + hold_frames
# Calculate fade-up and fade-down frames, ensuring they sum to transition_frames
fade_up_frames = max(1, min(transition_frames - 1, round(transition_frames * fade_balance)))
fade_down_frames = transition_frames - fade_up_frames
for i, frame in enumerate(edge_fx_frames):
cycle_position = i % frames_per_cycle
if cycle_position < fade_up_frames:
# Fade in
fade_factor = cycle_position / fade_up_frames
elif cycle_position >= frames_per_cycle - fade_down_frames:
# Fade out
fade_factor = (frames_per_cycle - cycle_position) / fade_down_frames
else:
# Hold at full opacity
fade_factor = 1.0
# Apply fade effect
faded_frame = Image.new('RGB', frame.size, (0, 0, 0))
faded_frame = Image.blend(faded_frame, frame, fade_factor)
faded_frames.append(faded_frame)
return faded_frames
# Mapping the class to its name for the node system
NODE_CLASS_MAPPINGS = {
"DynamicAnimatedWeightsHelper": DynamicAnimatedWeightsHelper
}
# Display name mappings for the node system
NODE_DISPLAY_NAME_MAPPINGS = {
"DynamicAnimatedWeightsHelper": "Dynamic Animated Weights"
}