Add all project files

This commit is contained in:
mgfxer
2024-07-10 20:47:20 -05:00
parent 8b456d1b76
commit 4f5b66eb17
10 changed files with 1242 additions and 1 deletions
+1 -1
View File
@@ -7,4 +7,4 @@
# Denote all files that are truly binary and should not be modified.
*.jpg binary
*.png binary
*.gif binary
*.gif binary
File diff suppressed because one or more lines are too long
+156
View File
@@ -0,0 +1,156 @@
import subprocess
from time import gmtime, strftime
# Live Prompt Interpolation
# A new node to execute python with text interpolation and character stability check.
# The resulting print() statement ends up as the output, if any.
# (c) 2024 Atom, ChatGPT, and mgfxer.
# Internal code to handle the interpolation and prompt logic
def internal_code():
return """
def lerp(a: float, b: float, t: float) -> float:
return (1 - t) * a + t * b
def generate_interpolated_prompt(prev_prompt: str, curr_prompt: str, t: float) -> str:
if t == 0:
return f"{prev_prompt}"
elif t == 1:
return f"{curr_prompt}"
else:
weighted_prev = f"({prev_prompt}:{1 - t:.2f})"
weighted_curr = f"({curr_prompt}:{t:.2f})"
return f"{weighted_prev} {weighted_curr}"
current_frame = PYTHON_CODE_BOX_SEED % (TOTAL_FRAMES + 1)
# Example previous and current prompts
previous_prompt = 'PREVIOUS_PROMPT'
current_prompt = 'CURRENT_PROMPT'
# Interpolation factor based on frame count
if current_frame > TOTAL_FRAMES:
current_frame = TOTAL_FRAMES
t = current_frame / TOTAL_FRAMES
# Generate the interpolated prompt
interpolated_prompt = generate_interpolated_prompt(previous_prompt, current_prompt, t)
# Print the interpolated prompt
print(interpolated_prompt)
"""
class LivePromptInterpolation:
RETURN_TYPES = ("STRING", "FLOAT", "FLOAT", "INT",)
RETURN_NAMES = ("text", "current_strength", "previous_strength", "toggle_state",)
FUNCTION = "node_update_with_text_v3"
CATEGORY = "Scripting"
def __init__(self):
self.previous_prompt = ""
self.current_prompt = ""
self.current_frame = 0
self.total_frames = 100
self.update_cycle = 10
self.char_stability_frames = 5
self.stable_char_count_frame = 0
self.last_char_count = 0
self.toggle_state = 0 # Initialize the toggle state
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"new_prompt": ("STRING", {"default": "Enter your prompt here..."}),
"seed": ("INT", {"default": 311, "step": 1, "display": "number"}),
"total_frames": ("INT", {"default": 100, "step": 1, "display": "number"}),
"update_cycle": ("INT", {"default": 10, "step": 1, "display": "number"}),
"min_char_count": ("INT", {"default": 30, "step": 1, "display": "number"}),
"char_stability_frames": ("INT", {"default": 5, "step": 1, "display": "number"})
},
}
def node_update_with_text_v3(self, new_prompt, seed, total_frames, update_cycle, min_char_count, char_stability_frames):
# Check if the new prompt is significant
def is_significant_change(prev_prompt, new_prompt, min_char_count):
char_count = len(new_prompt)
return char_count >= min_char_count
# Debug logging
print(f"Current Frame: {self.current_frame}")
print(f"Previous Prompt: {self.previous_prompt}")
print(f"Current Prompt: {self.current_prompt}")
print(f"New Prompt: {new_prompt}")
print(f"Update Cycle: {update_cycle}")
print(f"Character Stability Frames: {char_stability_frames}")
self.update_cycle = update_cycle # Update cycle dynamically
self.char_stability_frames = char_stability_frames # Update char stability frames dynamically
# Check if character count has been stable
if len(new_prompt) == self.last_char_count:
self.stable_char_count_frame += 1
else:
self.stable_char_count_frame = 0
self.last_char_count = len(new_prompt)
# Check if it's time to update the prompt
if (self.stable_char_count_frame >= self.char_stability_frames and
new_prompt != self.current_prompt and
is_significant_change(self.current_prompt, new_prompt, min_char_count)):
self.previous_prompt = self.current_prompt
self.current_prompt = new_prompt
self.current_frame = 1 # Reset frame count for new interpolation
self.stable_char_count_frame = 0 # Reset stability counter
self.toggle_state = 1 - self.toggle_state # Toggle the state between 0 and 1
print("New prompt detected, resetting frames and toggling state.")
elif self.current_frame < self.total_frames:
self.current_frame += 1
print("Update cycle not reached yet or prompt not stable long enough.")
# Ensure current_frame doesn't exceed total_frames
if self.current_frame > self.total_frames:
self.current_frame = self.total_frames
# Update total_frames dynamically
self.total_frames = total_frames
# Interpolation calculation
t = self.current_frame / self.total_frames
current_strength = t if self.toggle_state == 0 else 1 - t
previous_strength = 1 - current_strength
# Modify the code to include the previous and current prompts
modified_code = internal_code().replace('PREVIOUS_PROMPT', self.previous_prompt).replace('CURRENT_PROMPT', self.current_prompt).replace('TOTAL_FRAMES', str(self.total_frames))
code = f"import random; random.seed({seed}); PYTHON_CODE_BOX_SEED={self.current_frame}; {modified_code}"
try:
proc = subprocess.Popen(["python", "-c", code], stdout=subprocess.PIPE)
code_result = proc.communicate(timeout=10)[0] # Add timeout here
# Fix up result.
convert_result = code_result.decode().strip()
except subprocess.TimeoutExpired:
proc.kill()
print("Subprocess timed out and was killed.")
convert_result = ""
except Exception as e:
print(f"Error during code execution: {e}")
convert_result = ""
t = strftime("%m-%d-%Y %H:%M:%S", gmtime())
print(f"\033[36m[{t}] PCBv3--> {convert_result}\033[0m")
return (convert_result, current_strength, previous_strength, self.toggle_state,)
@classmethod
def IS_CHANGED(cls, new_prompt, seed):
return True
# NOTE: names should be globally unique
NODE_CLASS_MAPPINGS = {"LivePromptInterpolation": LivePromptInterpolation}
# A dictionary that contains the friendly/humanly readable titles for the nodes
NODE_DISPLAY_NAME_MAPPINGS = {"LivePromptInterpolation": "Live Prompt Interpolation"}
+49
View File
@@ -0,0 +1,49 @@
class PromptStackManager:
def __init__(self):
self.prompts = []
self.current_index = 0
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"prompt_stack": ("STRING", {"multiline": True, "dynamicPrompts": False, "default": "Prompt 1\nPrompt 2\nPrompt 3"}),
"frames_per_prompt": ("INT", {"default": 1, "min": 1, "max": 1000, "step": 1}),
"seed": ("INT", {"default": 0, "min": 0, "max": 1000000, "step": 1}),
}
}
RETURN_TYPES = ("STRING", "STRING", "INT")
RETURN_NAMES = ("current_prompt", "previous_prompt", "current_frame")
FUNCTION = "manage_prompts"
CATEGORY = "Prompt Management"
DESCRIPTION = """
Manages a stack of prompts from a multiline text box and outputs the current and previous prompts.
The node cycles through the prompts based on the frame count derived from the seed input.
"""
def manage_prompts(self, prompt_stack, frames_per_prompt, seed):
self.prompts = prompt_stack.strip().split("\n")
num_prompts = len(self.prompts)
if num_prompts == 0:
return ("", "", 0)
# Calculate the current frame based on the seed
current_frame = seed % (frames_per_prompt * num_prompts)
# Determine the current and previous prompt indices
self.current_index = current_frame // frames_per_prompt
previous_index = (self.current_index - 1) if self.current_index > 0 else (num_prompts - 1)
current_prompt = self.prompts[self.current_index]
previous_prompt = self.prompts[previous_index] if self.current_index > 0 else current_prompt
return (current_prompt, previous_prompt, current_frame)
@classmethod
def IS_CHANGED(cls, prompt_stack, frames_per_prompt, seed):
return True
NODE_CLASS_MAPPINGS = {"PromptStackManager": PromptStackManager}
NODE_DISPLAY_NAME_MAPPINGS = {"PromptStackManager": "Prompt Stack Manager"}
+602
View File
@@ -0,0 +1,602 @@
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"
}
+124
View File
@@ -0,0 +1,124 @@
import torch
class EdgeFXSourceImages:
def __init__(self):
pass # This constructor does nothing but is required for class instantiation
@classmethod
def INPUT_TYPES(cls):
# Define the input types for the node
return {
"required": {
"image_stream": ("IMAGE",), # Single image input for the stream of images
"num_images": ("INT", {"default": 4, "min": 1}), # Integer input for the number of images
"hold_length": ("INT", {"default": 5, "min": 1}), # Integer input for hold length with default 5 and minimum 1
"transition_length": ("INT", {"default": 20, "min": 1}), # Integer input for transition length with default 20 and minimum 1
"padding_frames": ("INT", {"default": 0, "min": 0}), # Integer input for padding frames with default 0 and minimum 0
"push": ("INT", {"default": 0, "min": 0}),
"retract": ("INT", {"default": 0, "min": 0}),
}
}
# Define the return types for the function outputs
RETURN_TYPES = ("IMAGE", "IMAGE", "STRING", "STRING", "INT") # The function will return two image tensors, two strings, and an integer
# Define the return names for the function outputs
RETURN_NAMES = ("first_timeline", "second_timeline", "first_text_output", "second_text_output", "total_frames")
# Name of the function to be executed
FUNCTION = "generate_mask_definitions"
# Category of the node
CATEGORY = "advanced"
def generate_mask_definitions(self, image_stream, num_images, hold_length, transition_length, padding_frames, push, retract):
# Initialize lists to hold the timelines and mask definitions
first_timeline = []
second_timeline = []
first_text_output = ""
second_text_output = ""
# Calculate the total number of frames for each image and the total number of frames for the entire animation
frame_distance = hold_length + transition_length
total_frames = num_images * frame_distance + padding_frames
# Get the actual number of images provided
num_images_provided = image_stream.shape[0]
# Adjust hold value for the first cycle
adjusted_first_hold = hold_length - retract + push
# Generate the first timeline with adjusted first hold value
for i in range(num_images):
index = i % num_images_provided
current_hold = adjusted_first_hold if i == 0 else hold_length
frame_distance = current_hold + transition_length
repeated_images = image_stream[index:index+1].repeat(frame_distance, 1, 1, 1)
first_timeline.append(repeated_images)
# Concatenate the list of tensors into a single tensor
first_timeline = torch.cat(first_timeline, dim=0)
# Adjust padding length
adjusted_padding_length = padding_frames + retract - push
# Generate the second timeline
for i in range(1, num_images):
index = i % num_images_provided
repeated_images = image_stream[index:index+1].repeat(frame_distance, 1, 1, 1)
second_timeline.append(repeated_images)
repeated_images = image_stream[0:1].repeat(frame_distance, 1, 1, 1)
second_timeline.append(repeated_images)
# Concatenate the list of tensors into a single tensor
second_timeline = torch.cat(second_timeline, dim=0)
# Initialize frame number and create mask text outputs
frame = 0
while frame < num_images * frame_distance:
frame_end_hold = frame + hold_length - 1
frame_start_transition = frame_end_hold + 1
frame_end_transition = frame_start_transition + transition_length - 1
first_text_output += f"{frame}:(1.0),\n"
first_text_output += f"{frame_end_hold}:(1.0),\n"
first_text_output += f"{frame_end_transition}:(0.0),\n"
second_text_output += f"{frame}:(0.0),\n"
second_text_output += f"{frame_end_hold}:(0.0),\n"
second_text_output += f"{frame_end_transition}:(1.0),\n"
frame = frame_end_transition + 1
# Add padding frames if specified
if adjusted_padding_length > 0:
padding_start_frame = frame
last_image_first_timeline = first_timeline[-1:]
last_image_second_timeline = second_timeline[-1:]
# Add padding frames to the timelines
padding_images_first = last_image_first_timeline.repeat(adjusted_padding_length, 1, 1, 1)
first_timeline = torch.cat((first_timeline, padding_images_first), dim=0)
padding_images_second = last_image_second_timeline.repeat(adjusted_padding_length, 1, 1, 1)
second_timeline = torch.cat((second_timeline, padding_images_second), dim=0)
# Append padding frames to the text outputs
for i in range(adjusted_padding_length):
frame = padding_start_frame + i
first_text_output += f"{frame}:(0.0),\n"
second_text_output += f"{frame}:(1.0),\n" # Set to 1.0 for the second timeline
# Ensure the text output ends correctly
first_text_output = first_text_output.strip().rstrip(',')
second_text_output = second_text_output.strip().rstrip(',')
# Return the generated timelines, text outputs, and total frames
return first_timeline, second_timeline, first_text_output, second_text_output, total_frames
# Mapping the class to its name for the node system
NODE_CLASS_MAPPINGS = {
"EdgeFXSourceImages": EdgeFXSourceImages # Map the class name to the node system
}
# Display name mappings for the node system
NODE_DISPLAY_NAME_MAPPINGS = {
"EdgeFXSourceImages": "EdgeFX Source Images" # Map the display name to the node system
}
+86
View File
@@ -0,0 +1,86 @@
# Florence Prompt Travel Helper
# Version: 2.5
import re
from collections import defaultdict
class FlorencePromptTravelHelper:
def __init__(self):
pass
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"bulk_text_input": ("STRING", {
"multiline": True,
"default": "",
}),
"hold_length": ("INT", {
"default": 5,
"min": 0
}),
"transition_length": ("INT", {
"default": 5,
"min": 0
}),
"end_padding_frames": ("INT", {
"default": 10,
"min": 0
}),
}
}
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ("formatted_prompts",)
FUNCTION = "process_bulk_text"
CATEGORY = "advanced"
def process_bulk_text(self, bulk_text_input="", hold_length=5, transition_length=5, end_padding_frames=10):
# Handle both string and list inputs
if isinstance(bulk_text_input, list):
bulk_text_input = ".,".join(bulk_text_input)
if not bulk_text_input or not bulk_text_input.strip():
return ("", )
# Split the bulk input into individual prompts
prompts = bulk_text_input.strip().split('.,')
# Strip whitespace from all prompts except the last one
prompts = [prompt.strip() for prompt in prompts[:-1]] + [prompts[-1].strip()]
frames = []
current_frame = 0
# Create frames based on hold and transition lengths
for prompt in prompts:
prompt = prompt.replace('"', '\\"') # Escape quotes
frames.append((current_frame, prompt))
current_frame += hold_length
frames.append((current_frame, prompt))
current_frame += transition_length
# Add the first prompt again at the end to create a loop
if prompts:
first_prompt = prompts[0].replace('"', '\\"')
frames.append((current_frame, first_prompt))
current_frame += end_padding_frames
# Add the final piece of the travel schedule
frames.append((current_frame, first_prompt))
frame_dict = defaultdict(tuple)
for frame, text in frames:
frame_dict[frame] += (text,)
formatted_prompts = []
for frame in sorted(frame_dict.keys()):
formatted_prompts.append(f'"{frame}": "{", ".join(frame_dict[frame])}"')
return (',\n'.join(formatted_prompts), )
NODE_CLASS_MAPPINGS = {
"FlorencePromptTravelHelper": FlorencePromptTravelHelper
}
NODE_DISPLAY_NAME_MAPPINGS = {
"FlorencePromptTravelHelper": "Florence Prompt Travel Helper"
}
+114
View File
@@ -0,0 +1,114 @@
import torch
class MaskSequenceHelper:
def __init__(self):
pass # This constructor does nothing but is required for class instantiation
@classmethod
def INPUT_TYPES(cls):
# Define the input types for the node
return {
"required": {
"image_stream": ("IMAGE",), # Single image input for the stream of images
"num_images": ("INT", {"default": 4, "min": 1}), # Integer input for the number of images
"hold_length": ("INT", {"default": 5, "min": 1}), # Integer input for hold length with default 5 and minimum 1
"transition_length": ("INT", {"default": 20, "min": 1}), # Integer input for transition length with default 20 and minimum 1
"padding_frames": ("INT", {"default": 0, "min": 0}), # Integer input for padding frames with default 0 and minimum 0
}
}
# Define the return types for the function outputs
RETURN_TYPES = ("IMAGE", "IMAGE", "STRING", "STRING", "INT") # The function will return two image tensors, two strings, and an integer
# Define the return names for the function outputs
RETURN_NAMES = ("first_timeline", "second_timeline", "first_text_output", "second_text_output", "total_frames")
# Name of the function to be executed
FUNCTION = "generate_mask_definitions"
# Category of the node
CATEGORY = "advanced"
def generate_mask_definitions(self, image_stream, num_images, hold_length, transition_length, padding_frames):
# Initialize lists to hold the timelines and mask definitions
first_timeline = []
second_timeline = []
first_text_output = ""
second_text_output = ""
# Calculate the total number of frames for each image and the total number of frames for the entire animation
frame_distance = hold_length + transition_length
total_frames = num_images * frame_distance + padding_frames
# Get the actual number of images provided
num_images_provided = image_stream.shape[0]
# Generate the first timeline
for i in range(num_images):
index = i % num_images_provided
repeated_images = image_stream[index:index+1].repeat(frame_distance, 1, 1, 1)
first_timeline.append(repeated_images)
# Concatenate the list of tensors into a single tensor
first_timeline = torch.cat(first_timeline, dim=0)
# Generate the second timeline
for i in range(1, num_images):
index = i % num_images_provided
repeated_images = image_stream[index:index+1].repeat(frame_distance, 1, 1, 1)
second_timeline.append(repeated_images)
repeated_images = image_stream[0:1].repeat(frame_distance, 1, 1, 1)
second_timeline.append(repeated_images)
# Concatenate the list of tensors into a single tensor
second_timeline = torch.cat(second_timeline, dim=0)
# Initialize frame number and create mask text outputs
frame = 0
while frame < num_images * frame_distance:
frame_end_hold = frame + hold_length - 1
frame_start_transition = frame_end_hold + 1
frame_end_transition = frame_start_transition + transition_length - 1
first_text_output += f"{frame}:(1.0),\n"
first_text_output += f"{frame_end_hold}:(1.0),\n"
first_text_output += f"{frame_end_transition}:(0.0),\n"
second_text_output += f"{frame}:(0.0),\n"
second_text_output += f"{frame_end_hold}:(0.0),\n"
second_text_output += f"{frame_end_transition}:(1.0),\n"
frame = frame_end_transition + 1
# Add padding frames if specified
if padding_frames > 0:
padding_start_frame = frame
last_image_first_timeline = first_timeline[-1:]
last_image_second_timeline = second_timeline[-1:]
# Add padding frames to the timelines
padding_images_first = last_image_first_timeline.repeat(padding_frames, 1, 1, 1)
first_timeline = torch.cat((first_timeline, padding_images_first), dim=0)
padding_images_second = last_image_second_timeline.repeat(padding_frames, 1, 1, 1)
second_timeline = torch.cat((second_timeline, padding_images_second), dim=0)
# Append padding frames to the text outputs
for i in range(padding_frames):
frame = padding_start_frame + i
first_text_output += f"{frame}:(0.0),\n"
second_text_output += f"{frame}:(1.0),\n" # Set to 1.0 for the second timeline
# Ensure the text output ends correctly
first_text_output = first_text_output.strip().rstrip(',')
second_text_output = second_text_output.strip().rstrip(',')
# Return the generated timelines, text outputs, and total frames
return first_timeline, second_timeline, first_text_output, second_text_output, total_frames
# Mapping the class to its name for the node system
NODE_CLASS_MAPPINGS = {
"MaskSequenceHelper": MaskSequenceHelper # Map the class name to the node system
}
# Display name mappings for the node system
NODE_DISPLAY_NAME_MAPPINGS = {
"MaskSequenceHelper": "Mask Sequence Helper" # Map the display name to the node system
}
+83
View File
@@ -0,0 +1,83 @@
# Prompt Travel Helper
# Version: 2.2
import re
from collections import defaultdict
class promptTravelHelper:
def __init__(self):
pass
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"bulk_text_input": ("STRING", {
"multiline": True,
"default": "",
}),
"hold_length": ("INT", {
"default": 5,
"min": 0
}),
"transition_length": ("INT", {
"default": 5,
"min": 0
}),
"end_padding_frames": ("INT", {
"default": 10,
"min": 0
}),
}
}
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ("formatted_prompts",)
FUNCTION = "process_bulk_text"
CATEGORY = "advanced"
def process_bulk_text(self, bulk_text_input="", hold_length=5, transition_length=5, end_padding_frames=10):
if not bulk_text_input.strip():
return ("", )
# Split the bulk input into individual prompts
prompts = [prompt.strip() for prompt in bulk_text_input.strip().split('\n') if prompt.strip()]
frames = []
current_frame = 0
# Create frames based on hold and transition lengths
for prompt in prompts:
prompt = prompt.replace('"', '\\"') # Escape quotes
frames.append((current_frame, prompt))
current_frame += hold_length
frames.append((current_frame, prompt))
current_frame += transition_length
# Add the first prompt again at the end to create a loop
if prompts:
first_prompt = prompts[0].replace('"', '\\"')
frames.append((current_frame, first_prompt))
current_frame += end_padding_frames
# Add the final piece of the travel schedule
frames.append((current_frame, first_prompt))
frame_dict = defaultdict(tuple)
for frame, text in frames:
frame_dict[frame] += (text,)
formatted_prompts = []
for frame in sorted(frame_dict.keys()):
formatted_prompts.append(f'"{frame}": "{", ".join(frame_dict[frame])}"')
return (',\n'.join(formatted_prompts), )
NODE_CLASS_MAPPINGS = {
"PromptTravelHelper": promptTravelHelper
}
NODE_DISPLAY_NAME_MAPPINGS = {
"PromptTravelHelper": "Prompt Travel Helper"
}
+26
View File
@@ -0,0 +1,26 @@
# init.py
from .ComfyUI_Live_Prompt_Interpolation import LivePromptInterpolation
from .ComfyUI_Prompt_Stack_Manager import PromptStackManager
from .DynamicAnimatedWeights import DynamicAnimatedWeightsHelper
from .FlorenceTravelHelper import FlorencePromptTravelHelper
from .MaskSequenceHelper import MaskSequenceHelper
from .PromptTravelHelper import promptTravelHelper
# Add any other necessary imports or initialization code here
# Mapping the class to its name for the node system
NODE_CLASS_MAPPINGS = {
"LivePromptInterpolation": LivePromptInterpolation,
"PromptStackManager": PromptStackManager,
"DynamicAnimatedWeightsHelper": DynamicAnimatedWeightsHelper,
"FlorencePromptTravelHelper": FlorencePromptTravelHelper,
"MaskSequenceHelper": MaskSequenceHelper,
"PromptTravelHelper": promptTravelHelper
}
# Display name mappings for the node system
NODE_DISPLAY_NAME_MAPPINGS = {
"LivePromptInterpolation": "Live Prompt Interpolation",
"PromptStackManager": "Prompt Stack Manager",
"DynamicAnimatedWeightsHelper": "Dynamic Animated Weights",
"FlorencePromptTravelHelper": "Florence Prompt Travel Helper",
"MaskSequenceHelper": "Mask Sequence Helper",
"PromptTravelHelper": "Prompt Travel Helper"
}