Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
96e5fa3ae8 | ||
|
|
4418ba9454 | ||
|
|
fefe43ebe3 | ||
|
|
533386aff5 | ||
|
|
84bd74c652 |
+11
-3
@@ -5,12 +5,15 @@ from .base import *
|
||||
from .colors import *
|
||||
from .curves import *
|
||||
from .image_processing import *
|
||||
from .inputfields import *
|
||||
from .loaders import *
|
||||
from .noise import *
|
||||
from .output import *
|
||||
from .prompting import *
|
||||
from .seq_processing import *
|
||||
from .switches import *
|
||||
from .utility import *
|
||||
from .inputfields import *
|
||||
from .calculate import *
|
||||
|
||||
_NODE_CLASSES: List[Type] = [DreamSineWave, DreamLinear, DreamCSVCurve, DreamBeatCurve, DreamFrameDimensions,
|
||||
DreamImageMotion, DreamNoiseFromPalette, DreamAnalyzePalette, DreamColorShift,
|
||||
@@ -19,12 +22,17 @@ _NODE_CLASSES: List[Type] = [DreamSineWave, DreamLinear, DreamCSVCurve, DreamBea
|
||||
DreamImageSequenceOutput, DreamCSVGenerator, DreamImageAreaSampler,
|
||||
DreamVideoEncoder, DreamSequenceTweening, DreamSequenceBlend, DreamColorAlign,
|
||||
DreamImageSampler, DreamNoiseFromAreaPalettes, DreamVideoEncoderMpegCoder,
|
||||
DreamInputString, DreamInputFloat, DreamInputInt, DreamInputText]
|
||||
DreamInputString, DreamInputFloat, DreamInputInt, DreamInputText, DreamBigLatentSwitch,
|
||||
DreamFrameCountCalculator, DreamBigImageSwitch, DreamBigTextSwitch, DreamBigFloatSwitch,
|
||||
DreamBigIntSwitch, DreamBigPaletteSwitch, DreamWeightedPromptBuilder, DreamPromptFinalizer,
|
||||
DreamFrameCounterInfo, DreamBoolToFloat, DreamBoolToInt, DreamSawWave, DreamTriangleWave,
|
||||
DreamTriangleEvent, DreamSmoothEvent, DreamCalculation, DreamImageColorShift,
|
||||
DreamComparePalette, DreamImageContrast, DreamImageBrightness]
|
||||
_SIGNATURE_SUFFIX = " [Dream]"
|
||||
|
||||
MANIFEST = {
|
||||
"name": "Dream Project Animation",
|
||||
"version": (3, 0, 1),
|
||||
"version": (3, 2, 0),
|
||||
"author": "Dream Project",
|
||||
"project": "https://github.com/alt-key-project/comfyui-dream-project",
|
||||
"description": "Various utility nodes for creating animations with ComfyUI",
|
||||
|
||||
@@ -6,6 +6,37 @@ from .shared import *
|
||||
from .types import *
|
||||
|
||||
|
||||
class DreamFrameCounterInfo:
|
||||
NODE_NAME = "Frame Counter Info"
|
||||
ICON = "⚋"
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": SharedTypes.frame_counter
|
||||
}
|
||||
|
||||
CATEGORY = NodeCategories.ANIMATION
|
||||
RETURN_TYPES = ("INT", "INT", "BOOLEAN", "BOOLEAN", "FLOAT", "FLOAT", "FLOAT", "FLOAT")
|
||||
RETURN_NAMES = ("frames_completed", "total_frames", "first_frame", "last_frame",
|
||||
"elapsed_seconds", "remaining_seconds", "total_seconds", "completion")
|
||||
FUNCTION = "result"
|
||||
|
||||
@classmethod
|
||||
def IS_CHANGED(cls, *v):
|
||||
return ALWAYS_CHANGED_FLAG
|
||||
|
||||
def result(self, frame_counter: FrameCounter):
|
||||
return (frame_counter.current_frame,
|
||||
frame_counter.total_frames,
|
||||
frame_counter.is_first_frame,
|
||||
frame_counter.is_final_frame,
|
||||
frame_counter.current_time_in_seconds,
|
||||
frame_counter.remaining_time_in_seconds,
|
||||
frame_counter.total_time_in_seconds,
|
||||
frame_counter.current_time_in_seconds / max(0.01, frame_counter.total_time_in_seconds))
|
||||
|
||||
|
||||
class DreamDirectoryFileCount:
|
||||
NODE_NAME = "File Count"
|
||||
ICON = "📂"
|
||||
@@ -35,7 +66,7 @@ class DreamDirectoryFileCount:
|
||||
for pattern in patterns.split("|"):
|
||||
files = list(glob.glob(pattern, root_dir=directory_path))
|
||||
total += len(files)
|
||||
print("total "+str(total))
|
||||
print("total " + str(total))
|
||||
return (total,)
|
||||
|
||||
|
||||
@@ -105,7 +136,7 @@ class DreamDirectoryBackedFrameCounter:
|
||||
"pattern": ("STRING", {"default": '*', "multiline": False}),
|
||||
"indexing": (["numeric", "alphabetic order"],),
|
||||
"total_frames": ("INT", {"default": 100, "min": 2, "max": 24 * 3600 * 60}),
|
||||
"frames_per_second": ("INT", {"min": 1, "default": 25}),
|
||||
"frames_per_second": ("INT", {"min": 1, "default": 30}),
|
||||
},
|
||||
}
|
||||
|
||||
@@ -124,3 +155,33 @@ class DreamDirectoryBackedFrameCounter:
|
||||
return (FrameCounter(0, total_frames, frames_per_second),)
|
||||
n = max(results.keys()) + 1
|
||||
return (FrameCounter(n, total_frames, frames_per_second),)
|
||||
|
||||
|
||||
class DreamFrameCountCalculator:
|
||||
NODE_NAME = "Frame Count Calculator"
|
||||
ICON = "⌛"
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"hours": ("INT", {"min": 0, "default": 0, "max": 23}),
|
||||
"minutes": ("INT", {"min": 0, "default": 0, "max": 59}),
|
||||
"seconds": ("INT", {"min": 0, "default": 10, "max": 59}),
|
||||
"milliseconds": ("INT", {"min": 0, "default": 0, "max": 59}),
|
||||
"frames_per_second": ("INT", {"min": 1, "default": 30})
|
||||
},
|
||||
}
|
||||
|
||||
CATEGORY = NodeCategories.ANIMATION
|
||||
RETURN_TYPES = ("INT",)
|
||||
RETURN_NAMES = ("TOTAL",)
|
||||
FUNCTION = "result"
|
||||
|
||||
@classmethod
|
||||
def IS_CHANGED(cls, *v):
|
||||
return ALWAYS_CHANGED_FLAG
|
||||
|
||||
def result(self, hours, minutes, seconds, milliseconds, frames_per_second):
|
||||
total_s = seconds + 0.001 * milliseconds + minutes * 60 + hours * 3600
|
||||
return (round(total_s * frames_per_second),)
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import math
|
||||
|
||||
from evalidate import Expr, EvalException, base_eval_model
|
||||
|
||||
from .categories import *
|
||||
from .err import on_error
|
||||
from .shared import hashed_as_strings
|
||||
|
||||
|
||||
class DreamCalculation:
|
||||
NODE_NAME = "Calculation"
|
||||
ICON = "🖩"
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"expression": ("STRING", {"default": "a + b + c - (r * s * t)", "multiline": True})
|
||||
},
|
||||
"optional": {
|
||||
"a_int": ("INT", {"default": 0, "multiline": False}),
|
||||
"b_int": ("INT", {"default": 0, "multiline": False}),
|
||||
"c_int": ("INT", {"default": 0, "multiline": False}),
|
||||
"r_float": ("FLOAT", {"default": 0.0, "multiline": False}),
|
||||
"s_float": ("FLOAT", {"default": 0.0, "multiline": False}),
|
||||
"t_float": ("FLOAT", {"default": 0.0, "multiline": False})
|
||||
}
|
||||
}
|
||||
|
||||
CATEGORY = NodeCategories.UTILS
|
||||
RETURN_TYPES = ("FLOAT", "INT")
|
||||
RETURN_NAMES = ("FLOAT", "INT")
|
||||
FUNCTION = "result"
|
||||
|
||||
@classmethod
|
||||
def IS_CHANGED(cls, *values):
|
||||
return hashed_as_strings(*values)
|
||||
|
||||
def _make_model(self):
|
||||
funcs = self._make_functions()
|
||||
m = base_eval_model.clone()
|
||||
m.nodes.append('Mult')
|
||||
m.nodes.append('Call')
|
||||
for funname in funcs.keys():
|
||||
m.allowed_functions.append(funname)
|
||||
return (m, funcs)
|
||||
|
||||
def _make_functions(self):
|
||||
return {
|
||||
"round": round,
|
||||
"float": float,
|
||||
"int": int,
|
||||
"abs": abs,
|
||||
"min": min,
|
||||
"max": max,
|
||||
"tan": math.tan,
|
||||
"tanh": math.tanh,
|
||||
"sin": math.sin,
|
||||
"sinh": math.sinh,
|
||||
"cos": math.cos,
|
||||
"cosh": math.cosh,
|
||||
"pow": math.pow,
|
||||
"sqrt": math.sqrt,
|
||||
"ceil": math.ceil,
|
||||
"floor": math.floor,
|
||||
"pi": math.pi,
|
||||
"log": math.log,
|
||||
"log2": math.log2,
|
||||
"acos": math.acos,
|
||||
"asin": math.asin,
|
||||
"acosh": math.acosh,
|
||||
"asinh": math.asinh,
|
||||
"atan": math.atan,
|
||||
"atanh": math.atanh,
|
||||
"exp": math.exp,
|
||||
"fmod": math.fmod,
|
||||
"factorial": math.factorial,
|
||||
"dist": math.dist,
|
||||
"atan2": math.atan2,
|
||||
"log10": math.log10
|
||||
}
|
||||
|
||||
def result(self, expression, **values):
|
||||
model, funcs = self._make_model()
|
||||
vars = funcs
|
||||
for key in ("a_int", "b_int", "c_int", "r_float", "s_float", "t_float"):
|
||||
nm = key.split("_")[0]
|
||||
v = values.get(key, None)
|
||||
if v is not None:
|
||||
vars[nm] = v
|
||||
try:
|
||||
data = Expr(expression, model=model).eval(vars)
|
||||
if isinstance(data, (int, float)):
|
||||
return float(data), int(round(data))
|
||||
else:
|
||||
return 0.0, 0
|
||||
except EvalException as e:
|
||||
on_error(DreamCalculation, str(e))
|
||||
+3
-1
@@ -5,9 +5,11 @@ class NodeCategories:
|
||||
ANIMATION_POSTPROCESSING = ANIMATION + "/postprocessing"
|
||||
ANIMATION_TRANSFORMS = ANIMATION + "/transforms"
|
||||
ANIMATION_CURVES = "animation/curves"
|
||||
CONDITIONING = "conditioning"
|
||||
IMAGE_POSTPROCESSING = "image/postprocessing"
|
||||
IMAGE_ANIMATION = "image/animation"
|
||||
IMAGE_COLORS = "image/color"
|
||||
IMAGE_GENERATE = "image/generate"
|
||||
IMAGE = "image"
|
||||
UTILS = "utils"
|
||||
UTILS = "utils"
|
||||
UTILS_SWITCHES = "utils/switches"
|
||||
@@ -1,4 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from .categories import NodeCategories
|
||||
from .shared import *
|
||||
from .types import *
|
||||
@@ -128,7 +129,7 @@ class DreamColorAlign:
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": SharedTypes.palette | {
|
||||
"target_align": (RGBPalette.ID, ),
|
||||
"target_align": (RGBPalette.ID,),
|
||||
"alignment_factor": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 10.0, "step": 0.1}),
|
||||
}
|
||||
}
|
||||
@@ -151,8 +152,8 @@ class DreamColorAlign:
|
||||
for i in range(len(palette)):
|
||||
p = palette[i]
|
||||
t = target_align[i]
|
||||
(_, r1, g1, b1) = p.analyze()
|
||||
(_, r2, g2, b2) = t.analyze()
|
||||
(_, _, r1, g1, b1) = p.analyze()
|
||||
(_, _, r2, g2, b2) = t.analyze()
|
||||
|
||||
dr = (r2 - r1) * alignment_factor
|
||||
dg = (g2 - g1) * alignment_factor
|
||||
@@ -214,9 +215,150 @@ class DreamColorShift:
|
||||
return (tuple(results),)
|
||||
|
||||
|
||||
class DreamImageColorShift:
|
||||
NODE_NAME = "Image Color Shift"
|
||||
ICON = "🖼"
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {"image": ("IMAGE",),
|
||||
"red_multiplier": ("FLOAT", {"default": 1.0, "min": 0.0}),
|
||||
"green_multiplier": ("FLOAT", {"default": 1.0, "min": 0.0}),
|
||||
"blue_multiplier": ("FLOAT", {"default": 1.0, "min": 0.0}),
|
||||
},
|
||||
|
||||
}
|
||||
|
||||
CATEGORY = NodeCategories.IMAGE_COLORS
|
||||
RETURN_TYPES = ("IMAGE",)
|
||||
RETURN_NAMES = ("image",)
|
||||
FUNCTION = "result"
|
||||
|
||||
@classmethod
|
||||
def IS_CHANGED(cls, *values):
|
||||
return ALWAYS_CHANGED_FLAG
|
||||
|
||||
def result(self, image, red_multiplier, green_multiplier, blue_multiplier):
|
||||
proc = DreamImageProcessor(inputs=image)
|
||||
|
||||
def recolor(im: DreamImage, *a, **args):
|
||||
return (im.adjust_colors(red_multiplier, green_multiplier, blue_multiplier),)
|
||||
|
||||
return proc.process(recolor)
|
||||
|
||||
|
||||
class DreamImageBrightness:
|
||||
NODE_NAME = "Image Brightness Adjustment"
|
||||
ICON = "☼"
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {"image": ("IMAGE",),
|
||||
"factor": ("FLOAT", {"default": 1.0, "min": 0.0}),
|
||||
},
|
||||
|
||||
}
|
||||
|
||||
CATEGORY = NodeCategories.IMAGE_COLORS
|
||||
RETURN_TYPES = ("IMAGE",)
|
||||
RETURN_NAMES = ("image",)
|
||||
FUNCTION = "result"
|
||||
|
||||
@classmethod
|
||||
def IS_CHANGED(cls, *values):
|
||||
return ALWAYS_CHANGED_FLAG
|
||||
|
||||
def result(self, image, factor):
|
||||
proc = DreamImageProcessor(inputs=image)
|
||||
|
||||
def change(im: DreamImage, *a, **args):
|
||||
return (im.change_brightness(factor),)
|
||||
|
||||
return proc.process(change)
|
||||
|
||||
|
||||
class DreamImageContrast:
|
||||
NODE_NAME = "Image Contrast Adjustment"
|
||||
ICON = "◐"
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {"image": ("IMAGE",),
|
||||
"factor": ("FLOAT", {"default": 1.0, "min": 0.0}),
|
||||
},
|
||||
|
||||
}
|
||||
|
||||
CATEGORY = NodeCategories.IMAGE_COLORS
|
||||
RETURN_TYPES = ("IMAGE",)
|
||||
RETURN_NAMES = ("image",)
|
||||
FUNCTION = "result"
|
||||
|
||||
@classmethod
|
||||
def IS_CHANGED(cls, *values):
|
||||
return ALWAYS_CHANGED_FLAG
|
||||
|
||||
def result(self, image, factor):
|
||||
proc = DreamImageProcessor(inputs=image)
|
||||
|
||||
def change(im: DreamImage, *a, **args):
|
||||
return (im.change_contrast(factor),)
|
||||
|
||||
return proc.process(change)
|
||||
|
||||
|
||||
class DreamComparePalette:
|
||||
NODE_NAME = "Compare Palettes"
|
||||
ICON = "📊"
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"a": (RGBPalette.ID,),
|
||||
"b": (RGBPalette.ID,),
|
||||
},
|
||||
}
|
||||
|
||||
CATEGORY = NodeCategories.IMAGE_COLORS
|
||||
RETURN_TYPES = ("FLOAT", "FLOAT", "FLOAT", "FLOAT")
|
||||
RETURN_NAMES = (
|
||||
"brightness_multiplier", "contrast_multiplier", "red_multiplier", "green_multiplier", "blue_multiplier")
|
||||
FUNCTION = "result"
|
||||
|
||||
@classmethod
|
||||
def IS_CHANGED(cls, *values):
|
||||
return ALWAYS_CHANGED_FLAG
|
||||
|
||||
def result(self, a, b):
|
||||
MIN_VALUE = 1 / 255.0
|
||||
|
||||
brightness = list()
|
||||
contrasts = list()
|
||||
reds = list()
|
||||
greens = list()
|
||||
blues = list()
|
||||
|
||||
for i in range(min(len(a), len(b))):
|
||||
(bright, ctr, red, green, blue) = a[i].analyze()
|
||||
(bright2, ctr2, red2, green2, blue2) = b[i].analyze()
|
||||
brightness.append(bright2 / max(MIN_VALUE, bright))
|
||||
contrasts.append(ctr2 / max(MIN_VALUE, ctr))
|
||||
reds.append(red2 / max(MIN_VALUE, red))
|
||||
greens.append(green2 / max(MIN_VALUE, green))
|
||||
blues.append(blue2 / max(MIN_VALUE, blue))
|
||||
|
||||
n = len(brightness)
|
||||
|
||||
return (sum(brightness) / n, sum(contrasts) / n, sum(reds) / n,
|
||||
sum(greens) / n, sum(blues) / n)
|
||||
|
||||
|
||||
class DreamAnalyzePalette:
|
||||
NODE_NAME = "Analyze Palette"
|
||||
NODE = "📊"
|
||||
ICON = "📊"
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
@@ -226,8 +368,8 @@ class DreamAnalyzePalette:
|
||||
}
|
||||
|
||||
CATEGORY = NodeCategories.IMAGE_COLORS
|
||||
RETURN_TYPES = ("FLOAT", "FLOAT", "FLOAT", "FLOAT")
|
||||
RETURN_NAMES = ("brightness", "redness", "greenness", "blueness")
|
||||
RETURN_TYPES = ("FLOAT", "FLOAT", "FLOAT", "FLOAT", "FLOAT")
|
||||
RETURN_NAMES = ("brightness", "contrast", "redness", "greenness", "blueness")
|
||||
FUNCTION = "result"
|
||||
|
||||
@classmethod
|
||||
@@ -236,12 +378,13 @@ class DreamAnalyzePalette:
|
||||
|
||||
def result(self, palette):
|
||||
f = 1.0 / len(palette)
|
||||
(w, r, g, b) = (0, 0, 0, 0)
|
||||
(w, c, r, g, b) = (0, 0, 0, 0, 0)
|
||||
for p in palette:
|
||||
(brightness, red, green, blue) = p.analyze()
|
||||
(brightness, contrast, red, green, blue) = p.analyze()
|
||||
w += brightness
|
||||
c += contrast
|
||||
r += red
|
||||
g += green
|
||||
b += blue
|
||||
|
||||
return w * f, r * f, g * f, b * f
|
||||
return w * f, c * f, r * f, g * f, b * f
|
||||
|
||||
@@ -7,6 +7,20 @@ from .shared import hashed_as_strings
|
||||
from .types import SharedTypes, FrameCounter
|
||||
|
||||
|
||||
def _linear_value_calc(x, x_start, x_end, y_start, y_end):
|
||||
if x <= x_start:
|
||||
return y_start
|
||||
if x >= x_end:
|
||||
return y_end
|
||||
dx = max(x_end - x_start, 0.0001)
|
||||
n = (x - x_start) / dx
|
||||
return (y_end - y_start) * n + y_start
|
||||
|
||||
|
||||
def _curve_result(f: float):
|
||||
return (f, int(round(f)))
|
||||
|
||||
|
||||
class DreamSineWave:
|
||||
NODE_NAME = "Sine Curve"
|
||||
|
||||
@@ -37,7 +51,149 @@ class DreamSineWave:
|
||||
b = 2 * math.pi / periodicity_seconds
|
||||
d = (max_value + min_value) / 2
|
||||
y = a * math.sin(b * (x + c)) + d
|
||||
return (y, int(round(y)))
|
||||
return _curve_result(y)
|
||||
|
||||
|
||||
class DreamSawWave:
|
||||
NODE_NAME = "Saw Curve"
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": SharedTypes.frame_counter | {
|
||||
"max_value": ("FLOAT", {"default": 1.0, "multiline": False}),
|
||||
"min_value": ("FLOAT", {"default": 0.0, "multiline": False}),
|
||||
"periodicity_seconds": ("FLOAT", {"default": 10.0, "multiline": False, "min": 0.01}),
|
||||
"phase": ("FLOAT", {"default": 0.0, "multiline": False, "min": -1, "max": 1}),
|
||||
},
|
||||
}
|
||||
|
||||
CATEGORY = NodeCategories.ANIMATION_CURVES
|
||||
RETURN_TYPES = ("FLOAT", "INT")
|
||||
RETURN_NAMES = ("FLOAT", "INT")
|
||||
FUNCTION = "result"
|
||||
|
||||
@classmethod
|
||||
def IS_CHANGED(cls, *values):
|
||||
return hashed_as_strings(*values)
|
||||
|
||||
def result(self, frame_counter: FrameCounter, max_value, min_value, periodicity_seconds, phase):
|
||||
x = frame_counter.current_time_in_seconds
|
||||
x = ((x + periodicity_seconds * phase) % periodicity_seconds) / periodicity_seconds
|
||||
y = x * (max_value - min_value) + min_value
|
||||
return _curve_result(y)
|
||||
|
||||
|
||||
class DreamTriangleWave:
|
||||
NODE_NAME = "Triangle Curve"
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": SharedTypes.frame_counter | {
|
||||
"max_value": ("FLOAT", {"default": 1.0, "multiline": False}),
|
||||
"min_value": ("FLOAT", {"default": 0.0, "multiline": False}),
|
||||
"periodicity_seconds": ("FLOAT", {"default": 10.0, "multiline": False, "min": 0.01}),
|
||||
"phase": ("FLOAT", {"default": 0.0, "multiline": False, "min": -1, "max": 1}),
|
||||
},
|
||||
}
|
||||
|
||||
CATEGORY = NodeCategories.ANIMATION_CURVES
|
||||
RETURN_TYPES = ("FLOAT", "INT")
|
||||
RETURN_NAMES = ("FLOAT", "INT")
|
||||
FUNCTION = "result"
|
||||
|
||||
@classmethod
|
||||
def IS_CHANGED(cls, *values):
|
||||
return hashed_as_strings(*values)
|
||||
|
||||
def result(self, frame_counter: FrameCounter, max_value, min_value, periodicity_seconds, phase):
|
||||
x = frame_counter.current_time_in_seconds
|
||||
x = ((x + periodicity_seconds * phase) % periodicity_seconds) / periodicity_seconds
|
||||
if x <= 0.5:
|
||||
x *= 2
|
||||
y = x * (max_value - min_value) + min_value
|
||||
else:
|
||||
x = (x - 0.5) * 2
|
||||
y = max_value - x * (max_value - min_value)
|
||||
return _curve_result(y)
|
||||
|
||||
|
||||
class DreamTriangleEvent:
|
||||
NODE_NAME = "Triangle Event Curve"
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": SharedTypes.frame_counter | {
|
||||
"max_value": ("FLOAT", {"default": 1.0, "multiline": False}),
|
||||
"min_value": ("FLOAT", {"default": 0.0, "multiline": False}),
|
||||
"width_seconds": ("FLOAT", {"default": 1.0, "multiline": False, "min": 0.1}),
|
||||
"center_seconds": ("FLOAT", {"default": 10.0, "multiline": False, "min": 0.0}),
|
||||
},
|
||||
}
|
||||
|
||||
CATEGORY = NodeCategories.ANIMATION_CURVES
|
||||
RETURN_TYPES = ("FLOAT", "INT")
|
||||
RETURN_NAMES = ("FLOAT", "INT")
|
||||
FUNCTION = "result"
|
||||
|
||||
@classmethod
|
||||
def IS_CHANGED(cls, *values):
|
||||
return hashed_as_strings(*values)
|
||||
|
||||
def result(self, frame_counter: FrameCounter, max_value, min_value, width_seconds, center_seconds):
|
||||
x = frame_counter.current_time_in_seconds
|
||||
start = center_seconds - width_seconds * 0.5
|
||||
end = center_seconds + width_seconds * 0.5
|
||||
if start <= x <= center_seconds:
|
||||
y = _linear_value_calc(x, start, center_seconds, min_value, max_value)
|
||||
elif center_seconds < x <= end:
|
||||
y = _linear_value_calc(x, center_seconds, end, max_value, min_value)
|
||||
else:
|
||||
y = min_value
|
||||
return _curve_result(y)
|
||||
|
||||
|
||||
class DreamSmoothEvent:
|
||||
NODE_NAME = "Smooth Event Curve"
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": SharedTypes.frame_counter | {
|
||||
"max_value": ("FLOAT", {"default": 1.0, "multiline": False}),
|
||||
"min_value": ("FLOAT", {"default": 0.0, "multiline": False}),
|
||||
"width_seconds": ("FLOAT", {"default": 1.0, "multiline": False, "min": 0.1}),
|
||||
"center_seconds": ("FLOAT", {"default": 10.0, "multiline": False, "min": 0.0}),
|
||||
},
|
||||
}
|
||||
|
||||
CATEGORY = NodeCategories.ANIMATION_CURVES
|
||||
RETURN_TYPES = ("FLOAT", "INT")
|
||||
RETURN_NAMES = ("FLOAT", "INT")
|
||||
FUNCTION = "result"
|
||||
|
||||
@classmethod
|
||||
def IS_CHANGED(cls, *values):
|
||||
return hashed_as_strings(*values)
|
||||
|
||||
def result(self, frame_counter: FrameCounter, max_value, min_value, width_seconds, center_seconds):
|
||||
x = frame_counter.current_time_in_seconds
|
||||
start = center_seconds - width_seconds * 0.5
|
||||
end = center_seconds + width_seconds * 0.5
|
||||
if start <= x <= center_seconds:
|
||||
y = _linear_value_calc(x, start, center_seconds, 0.0, 1.0)
|
||||
elif center_seconds < x <= end:
|
||||
y = _linear_value_calc(x, center_seconds, end, 1.0, 0.0)
|
||||
else:
|
||||
y = 0.0
|
||||
if y < 0.5:
|
||||
y = ((y + y) * (y + y)) * 0.5
|
||||
else:
|
||||
a = (y - 0.5) * 2
|
||||
y = math.pow(a, 0.25) * 0.5 + 0.5
|
||||
return _curve_result(y * (max_value - min_value) + min_value)
|
||||
|
||||
|
||||
class DreamBeatCurve:
|
||||
@@ -96,7 +252,7 @@ class DreamBeatCurve:
|
||||
v = 1.0 - v
|
||||
|
||||
r = low_value + v * (high_value - low_value)
|
||||
return (r, int(round(r)))
|
||||
return _curve_result(r)
|
||||
|
||||
|
||||
class DreamLinear:
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
EMBEDDED_CONFIGURATION = {
|
||||
"ffmpeg": {
|
||||
"file_extension": "mp4",
|
||||
"path": "ffmpeg",
|
||||
"arguments": ["-r", "%FPS%", "-f", "concat", "-safe", "0", "-vsync",
|
||||
"cfr", "-i", "%FRAMES%", "-c:v", "libx265", "-pix_fmt",
|
||||
"yuv420p", "%OUTPUT%"]
|
||||
},
|
||||
"mpeg_coder": {
|
||||
"encoding_threads": 4,
|
||||
"bitrate_factor": 1.0,
|
||||
"max_b_frame": 2,
|
||||
"file_extension": "mp4",
|
||||
"codec_name": "libx265"
|
||||
},
|
||||
"encoding": {
|
||||
"jpeg_quality": 95
|
||||
},
|
||||
"debug": False,
|
||||
"ui": {
|
||||
"top_category": "Dream",
|
||||
"prepend_icon_to_category": True,
|
||||
"append_icon_to_category": False,
|
||||
"prepend_icon_to_node": True,
|
||||
"append_icon_to_node": False,
|
||||
"category_icons": {
|
||||
"animation": "🎥",
|
||||
"postprocessing": "⚙",
|
||||
"transforms": "🔀",
|
||||
"curves": "📈",
|
||||
"color": "🎨",
|
||||
"generate": "⚡",
|
||||
"utils": "🛠",
|
||||
"image": "🌄",
|
||||
"switches": "⭆",
|
||||
"conditioning": "☯",
|
||||
"Dream": "✨"
|
||||
}
|
||||
},
|
||||
|
||||
}
|
||||
+22
-1
@@ -1,15 +1,32 @@
|
||||
{
|
||||
"Analyze Palette [Dream]": "Output brightness, red, green and blue averages of a palette",
|
||||
"Analyze Palette [Dream]": "Output brightness, contrast, red, green and blue averages of a palette",
|
||||
"Beat Curve [Dream]": "Beat pattern curve with impulses at specified beats of a measure",
|
||||
"Big Float Switch [Dream]": "Switch for up to 10 inputs",
|
||||
"Big Image Switch [Dream]": "Switch for up to 10 inputs",
|
||||
"Big Int Switch [Dream]": "Switch for up to 10 inputs",
|
||||
"Big Latent Switch [Dream]": "Switch for up to 10 inputs",
|
||||
"Big Palette Switch [Dream]": "Switch for up to 10 inputs",
|
||||
"Big Text Switch [Dream]": "Switch for up to 10 inputs",
|
||||
"Boolean To Float [Dream]": "Converts a boolean value to two different float values",
|
||||
"Boolean To Int [Dream]": "Converts a boolean value to two different int values",
|
||||
"Build Prompt [Dream]": "Weighted text prompt builder utility",
|
||||
"CSV Curve [Dream]": "CSV input curve where first column is frame or second and second column is value",
|
||||
"CSV Generator [Dream]": "CSV output, mainly for debugging purposes",
|
||||
"Calculation [Dream]": "Mathematical calculation node",
|
||||
"Common Frame Dimensions [Dream]": "Utility for calculating good width/height based on common video dimensions",
|
||||
"Compare Palettes [Dream]": "Analyses two palettes producing the factor for each color channel",
|
||||
"FFMPEG Video Encoder [Dream]": "Post processing for animation sequences calling FFMPEG to generate video file",
|
||||
"File Count [Dream]": "Finds the number of files in a directory matching specified patterns",
|
||||
"Finalize Prompt [Dream]": "Used in conjunction with 'Build Prompt'",
|
||||
"Float Input [Dream]": "Float input (until primitive routing issues are solved)",
|
||||
"Frame Count Calculator [Dream]": "Simple utility to calculate number of frames based on duration and framerate",
|
||||
"Frame Counter (Directory) [Dream]": "Directory backed frame counter, for output directories",
|
||||
"Frame Counter (Simple) [Dream]": "Integer value used as frame counter",
|
||||
"Frame Counter Info [Dream]": "Extracts information from the frame counter",
|
||||
"Frame Counter Offset [Dream]": "Adds an offset to a frame counter",
|
||||
"Image Brightness Adjustment [Dream]": "Adjusts the brightness of an image by a factor",
|
||||
"Image Color Shift [Dream]": "Adjust the colors (or brightness) of an image",
|
||||
"Image Contrast Adjustment [Dream]": "Adjusts the contrast of an image by a factor",
|
||||
"Image Motion [Dream]": "Node supporting zooming in/out and translating an image",
|
||||
"Image Sequence Blend [Dream]": "Post processing for animation sequences blending frame for a smoother blurred effect",
|
||||
"Image Sequence Loader [Dream]": "Loads a frame from a directory of images",
|
||||
@@ -23,8 +40,12 @@
|
||||
"Palette Color Shift [Dream]": "Multiplies the color values in a palette",
|
||||
"Sample Image Area as Palette [Dream]": "Samples a palette from an image based on pre-defined areas",
|
||||
"Sample Image as Palette [Dream]": "Randomly samples pixel values to build a palette from an image",
|
||||
"Saw Curve [Dream]": "Saw wave curve",
|
||||
"Sine Curve [Dream]": "Simple sine wave curve",
|
||||
"Smooth Event Curve [Dream]": "Single event/peak curve with a slight bell-shape",
|
||||
"String Input [Dream]": "String input (until primitive routing issues are solved)",
|
||||
"Text Input [Dream]": "Multiline string input (until primitive routing issues are solved)",
|
||||
"Triangle Curve [Dream]": "Triangle wave curve",
|
||||
"Triangle Event Curve [Dream]": "Single event/peak curve with triangular shape",
|
||||
"Video Encoder (mpegCoder) [Dream]": "Post processing for animation sequences using mpegCoder module to generate video file"
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
from .categories import NodeCategories
|
||||
from .shared import hashed_as_strings
|
||||
from .types import PartialPrompt
|
||||
|
||||
|
||||
class DreamWeightedPromptBuilder:
|
||||
NODE_NAME = "Build Prompt"
|
||||
ICON = "⚖"
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"optional": {
|
||||
"partial_prompt": (PartialPrompt.ID,)
|
||||
},
|
||||
"required": {
|
||||
"added_prompt": ("STRING", {"default": "", "multiline": True}),
|
||||
"weight": ("FLOAT", {"default": 1.0}),
|
||||
},
|
||||
}
|
||||
|
||||
CATEGORY = NodeCategories.CONDITIONING
|
||||
RETURN_TYPES = (PartialPrompt.ID,)
|
||||
RETURN_NAMES = ("partial_prompt",)
|
||||
FUNCTION = "result"
|
||||
|
||||
@classmethod
|
||||
def IS_CHANGED(cls, *values):
|
||||
return hashed_as_strings(*values)
|
||||
|
||||
def result(self, added_prompt, weight, **args):
|
||||
input = args.get("partial_prompt", PartialPrompt())
|
||||
p = input.add(added_prompt, weight)
|
||||
return (p,)
|
||||
|
||||
|
||||
class DreamPromptFinalizer:
|
||||
NODE_NAME = "Finalize Prompt"
|
||||
ICON = "🗫"
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"partial_prompt": (PartialPrompt.ID,),
|
||||
"adjustment": (["raw", "by_abs_max", "by_abs_sum"],),
|
||||
"clamp": ("FLOAT", {"default": 2.0, "min": 0.1, "step": 0.1}),
|
||||
"adjustment_reference": ("FLOAT", {"default": 1.0, "min": 0.1}),
|
||||
},
|
||||
}
|
||||
|
||||
CATEGORY = NodeCategories.CONDITIONING
|
||||
RETURN_TYPES = ("STRING", "STRING")
|
||||
RETURN_NAMES = ("positive", "negative")
|
||||
FUNCTION = "result"
|
||||
|
||||
@classmethod
|
||||
def IS_CHANGED(cls, *values):
|
||||
return hashed_as_strings(*values)
|
||||
|
||||
def result(self, partial_prompt: PartialPrompt, adjustment, adjustment_reference, clamp):
|
||||
if adjustment == "raw" or partial_prompt.is_empty():
|
||||
return partial_prompt.finalize(clamp)
|
||||
elif adjustment == "by_abs_sum":
|
||||
f = adjustment_reference / partial_prompt.abs_sum()
|
||||
return partial_prompt.scaled_by(f).finalize(clamp)
|
||||
else:
|
||||
f = adjustment_reference / partial_prompt.abs_max()
|
||||
return partial_prompt.scaled_by(f).finalize(clamp)
|
||||
@@ -133,6 +133,23 @@ Output brightness, red, green and blue averages of a palette. Useful to control
|
||||
### Beat Curve [Dream]
|
||||
Beat pattern curve with impulses at specified beats of a measure.
|
||||
|
||||
### Big *** Switch [Dream]
|
||||
Switch nodes for different type for up to ten inputs.
|
||||
|
||||
### Boolean To Float/Int [Dream]
|
||||
Converts a boolean value to two different numeric values.
|
||||
|
||||
### Build Prompt [Dream] (and Finalize Prompt [Dream])
|
||||
Weighted text prompt builder utility. Chain any number of these nodes and terminate with 'Finalize Prompt'.
|
||||
|
||||
### Calculation [Dream]
|
||||
Mathematical calculation node. Exposes most of the mathematical functions in the python
|
||||
[math module](https://docs.python.org/3/library/math.html), mathematical operators as well as round, abs, int,
|
||||
float, max and min.
|
||||
|
||||
### Compare Palettes [Dream]
|
||||
Analyses two palettes and produces the quotient for each individual channel (b/a) and brightness.
|
||||
|
||||
### CSV Curve [Dream]
|
||||
CSV input curve where first column is frame or second and second column is value.
|
||||
|
||||
@@ -152,6 +169,9 @@ Post processing for animation sequences using the python module mpegCoder with f
|
||||
### File Count [Dream]
|
||||
Finds the number of files in a directory matching specified patterns.
|
||||
|
||||
### Frame Count Calculator [Dream]
|
||||
Simple utility to calculate number of frames based on time and framerate.
|
||||
|
||||
### Frame Counter (Directory) [Dream]
|
||||
Directory backed frame counter, for output directories.
|
||||
|
||||
@@ -159,9 +179,21 @@ Directory backed frame counter, for output directories.
|
||||
Integer value used as frame counter. Useful for testing or if an auto-incrementing primitive is used as a frame
|
||||
counter.
|
||||
|
||||
### Frame Counter Info [Dream]
|
||||
Extracts information from the frame counter.
|
||||
|
||||
### Frame Counter Offset [Dream]
|
||||
Adds an offset to a frame counter.
|
||||
|
||||
### Image Brightness Adjustment [Dream]
|
||||
Adjusts the brightness of an image by a factor.
|
||||
|
||||
### Image Color Shift [Dream]
|
||||
Allows changing the colors of an image with a multiplier for each channel (RGB).
|
||||
|
||||
### Image Contrast Adjustment [Dream]
|
||||
Adjusts the contrast of an image by a factor.
|
||||
|
||||
### Image Motion [Dream]
|
||||
Node supporting zooming in/out and translating an image.
|
||||
|
||||
@@ -202,9 +234,21 @@ of equal size and each node may sample one of these.
|
||||
### Sample Image as Palette [Dream]
|
||||
Randomly samples pixels from a source image to build a palette from it.
|
||||
|
||||
### Saw Curve [Dream]
|
||||
Saw wave curve.
|
||||
|
||||
### Sine Curve [Dream]
|
||||
Simple sine wave curve.
|
||||
|
||||
### Smooth Event Curve [Dream]
|
||||
Single event/peak curve with a slight bell-shape.
|
||||
|
||||
### Triangle Curve [Dream]
|
||||
Triangle wave curve.
|
||||
|
||||
### Triangle Event Curve [Dream]
|
||||
Single event/peak curve with triangular shape.
|
||||
|
||||
### Other custom nodes
|
||||
|
||||
Many of the nodes found in 'WAS Node Suite' are useful the Dream Project Animation nodes - I suggest you install those
|
||||
|
||||
@@ -4,3 +4,4 @@ scipy
|
||||
numpy<1.24>=1.18
|
||||
torchvision
|
||||
mpegCoder
|
||||
evalidate
|
||||
|
||||
+5
-7
@@ -4,9 +4,7 @@ import shutil
|
||||
import subprocess
|
||||
import tempfile
|
||||
from functools import lru_cache
|
||||
|
||||
from PIL import Image
|
||||
|
||||
from PIL import Image as PilImage
|
||||
from .categories import NodeCategories
|
||||
from .err import on_error
|
||||
from .shared import DreamConfig, MpegEncoderUtility
|
||||
@@ -16,8 +14,8 @@ CONFIG = DreamConfig()
|
||||
|
||||
|
||||
@lru_cache(5)
|
||||
def _load_image_cached(filename) -> Image:
|
||||
return Image.open(filename)
|
||||
def _load_image_cached(filename):
|
||||
return PilImage.open(filename)
|
||||
|
||||
|
||||
class TempFileSet:
|
||||
@@ -166,7 +164,7 @@ class DreamVideoEncoderMpegCoder:
|
||||
if not sequence.is_defined:
|
||||
return ()
|
||||
config = DreamConfig()
|
||||
filename = _make_video_filename(name, config.get("mpeg_coder.file_exension"))
|
||||
filename = _make_video_filename(name, config.get("mpeg_coder.file_extension", "mp4"))
|
||||
for batch_num in sequence.batches:
|
||||
try:
|
||||
images = list(sequence.get_image_files_of_batch(batch_num))
|
||||
@@ -236,7 +234,7 @@ class DreamVideoEncoder:
|
||||
return ()
|
||||
|
||||
config = DreamConfig()
|
||||
filename = _make_video_filename(name, config.get("ffmpeg.file_exension"))
|
||||
filename = _make_video_filename(name, config.get("ffmpeg.file_extension", "mp4"))
|
||||
for batch_num in sequence.batches:
|
||||
try:
|
||||
images = list(sequence.get_image_files_of_batch(batch_num))
|
||||
|
||||
@@ -10,12 +10,13 @@ import folder_paths as comfy_paths
|
||||
import glob
|
||||
import numpy
|
||||
import torch
|
||||
from PIL import Image, ImageFilter
|
||||
from PIL import Image, ImageFilter, ImageEnhance
|
||||
from PIL.ImageDraw import ImageDraw
|
||||
from PIL.PngImagePlugin import PngInfo
|
||||
from typing import Dict, Tuple, List
|
||||
|
||||
from .dreamlogger import DreamLog
|
||||
from .embedded_config import EMBEDDED_CONFIGURATION
|
||||
|
||||
NODE_FILE = os.path.abspath(__file__)
|
||||
DREAM_NODES_SOURCE_ROOT = os.path.dirname(NODE_FILE)
|
||||
@@ -43,45 +44,7 @@ _config_data = None
|
||||
|
||||
class DreamConfig:
|
||||
FILEPATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), "config.json")
|
||||
DEFAULT_CONFIG = {
|
||||
"ffmpeg": {
|
||||
"file_exension": "mp4",
|
||||
"path": "ffmpeg",
|
||||
"arguments": ["-r", "%FPS%", "-f", "concat", "-safe", "0", "-vsync",
|
||||
"cfr", "-i", "%FRAMES%", "-c:v", "libx265", "-pix_fmt",
|
||||
"yuv420p", "%OUTPUT%"]
|
||||
},
|
||||
"mpeg_coder": {
|
||||
"encoding_threads": 4,
|
||||
"bitrate_factor": 1.0,
|
||||
"max_b_frame": 2,
|
||||
"file_exension": "mp4",
|
||||
"codec_name": "libx265"
|
||||
},
|
||||
"encoding": {
|
||||
"jpeg_quality": 95
|
||||
},
|
||||
"debug": False,
|
||||
"ui": {
|
||||
"top_category": "Dream",
|
||||
"prepend_icon_to_category": True,
|
||||
"append_icon_to_category": False,
|
||||
"prepend_icon_to_node": True,
|
||||
"append_icon_to_node": False,
|
||||
"category_icons": {
|
||||
"animation": "🎥",
|
||||
"postprocessing": "⚙",
|
||||
"transforms": "🔀",
|
||||
"curves": "📈",
|
||||
"color": "🎨",
|
||||
"generate": "⚡",
|
||||
"utils": "🛠",
|
||||
"image": "🌄",
|
||||
"Dream": "✨"
|
||||
}
|
||||
},
|
||||
|
||||
}
|
||||
DEFAULT_CONFIG = EMBEDDED_CONFIGURATION
|
||||
|
||||
def __init__(self):
|
||||
global _config_data
|
||||
@@ -188,6 +151,14 @@ class DreamImage:
|
||||
self.size = self.pil_image.size
|
||||
self._draw = ImageDraw(self.pil_image)
|
||||
|
||||
def change_brightness(self, factor):
|
||||
enhancer = ImageEnhance.Brightness(self.pil_image)
|
||||
return DreamImage(pil_image=enhancer.enhance(factor))
|
||||
|
||||
def change_contrast(self, factor):
|
||||
enhancer = ImageEnhance.Contrast(self.pil_image)
|
||||
return DreamImage(pil_image=enhancer.enhance(factor))
|
||||
|
||||
def numpy_array(self):
|
||||
return numpy.array(self.pil_image)
|
||||
|
||||
@@ -232,6 +203,15 @@ class DreamImage:
|
||||
def blur(self, amount):
|
||||
return DreamImage(pil_image=self.pil_image.filter(ImageFilter.GaussianBlur(amount)))
|
||||
|
||||
def adjust_colors(self, red_factor=1.0, green_factor=1.0, blue_factor=1.0):
|
||||
# newRed = 1.1*oldRed + 0*oldGreen + 0*oldBlue + constant
|
||||
# newGreen = 0*oldRed + 0.9*OldGreen + 0*OldBlue + constant
|
||||
# newBlue = 0*oldRed + 0*OldGreen + 1*OldBlue + constant
|
||||
matrix = (red_factor, 0, 0, 0,
|
||||
0, green_factor, 0, 0,
|
||||
0, 0, blue_factor, 0)
|
||||
return DreamImage(pil_image=self.pil_image.convert("RGB", matrix))
|
||||
|
||||
def get_pixel(self, x, y):
|
||||
p = self.pil_image.getpixel((x, y))
|
||||
if len(p) == 4:
|
||||
@@ -397,7 +377,7 @@ class MpegEncoderUtility:
|
||||
self._logger = get_logger()
|
||||
self._enc = mpegCoder.MpegEncoder()
|
||||
bit_rate = self._calculate_bit_rate(width, height, fps, bit_rate_factor)
|
||||
self._logger.info("Bitrate "+str(bit_rate))
|
||||
self._logger.info("Bitrate " + str(bit_rate))
|
||||
self._enc.setParameter(
|
||||
videoPath=video_path, codecName=codec_name,
|
||||
nthread=encoding_threads, bitRate=bit_rate, width=width, height=height, widthSrc=width,
|
||||
@@ -405,7 +385,7 @@ class MpegEncoderUtility:
|
||||
GOPSize=len(files), maxBframe=max_b_frame, frameRate=self._fps_to_tuple(fps))
|
||||
|
||||
def _calculate_bit_rate(self, width: int, height: int, fps: float, bit_rate_factor: float):
|
||||
bits_per_pixel_base = 0.075
|
||||
bits_per_pixel_base = 0.125
|
||||
return round(max(10, float(width * height * fps * bits_per_pixel_base * bit_rate_factor * 0.001)))
|
||||
|
||||
def encode(self):
|
||||
@@ -427,6 +407,7 @@ class MpegEncoderUtility:
|
||||
def _fps_to_tuple(self, fps: float):
|
||||
def _is_almost_int(f: float):
|
||||
return abs(f - int(f)) < 0.001
|
||||
|
||||
a = fps
|
||||
b = 1
|
||||
while not _is_almost_int(a) and b < 100:
|
||||
|
||||
+212
@@ -0,0 +1,212 @@
|
||||
from .categories import NodeCategories
|
||||
from .err import *
|
||||
from .shared import ALWAYS_CHANGED_FLAG, hashed_as_strings
|
||||
from .types import RGBPalette
|
||||
|
||||
|
||||
def _generate_switch_input(type: str):
|
||||
d = dict()
|
||||
for i in range(10):
|
||||
d["input_" + str(i)] = (type,)
|
||||
return {
|
||||
"required": {
|
||||
"select": ("INT", {"defualt": 0, "min": 0, "max": 9}),
|
||||
"on_missing": (["previous", "next"],)
|
||||
},
|
||||
"optional": d
|
||||
}
|
||||
|
||||
|
||||
def _do_pick(cls, select, on_missing, **args):
|
||||
direction = 1
|
||||
if on_missing == "previous":
|
||||
direction = -1
|
||||
if len(args) == 0:
|
||||
on_error(cls, "No inputs provided!")
|
||||
while args.get("input_" + str(select), None) is None:
|
||||
select = (select + direction) % 10
|
||||
return args["input_" + str(select)],
|
||||
|
||||
|
||||
class DreamBigImageSwitch:
|
||||
_switch_type = "IMAGE"
|
||||
NODE_NAME = "Big Image Switch"
|
||||
ICON = "⭆"
|
||||
CATEGORY = NodeCategories.UTILS_SWITCHES
|
||||
RETURN_TYPES = (_switch_type,)
|
||||
RETURN_NAMES = ("selected",)
|
||||
FUNCTION = "pick"
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return _generate_switch_input(cls._switch_type)
|
||||
|
||||
@classmethod
|
||||
def IS_CHANGED(cls, *values):
|
||||
return ALWAYS_CHANGED_FLAG
|
||||
|
||||
def pick(self, select, on_missing, **args):
|
||||
return _do_pick(self.__class__, select, on_missing, **args)
|
||||
|
||||
|
||||
class DreamBigLatentSwitch:
|
||||
_switch_type = "LATENT"
|
||||
NODE_NAME = "Big Latent Switch"
|
||||
ICON = "⭆"
|
||||
CATEGORY = NodeCategories.UTILS_SWITCHES
|
||||
RETURN_TYPES = (_switch_type,)
|
||||
RETURN_NAMES = ("selected",)
|
||||
FUNCTION = "pick"
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return _generate_switch_input(cls._switch_type)
|
||||
|
||||
@classmethod
|
||||
def IS_CHANGED(cls, *values):
|
||||
return ALWAYS_CHANGED_FLAG
|
||||
|
||||
def pick(self, select, on_missing, **args):
|
||||
return _do_pick(self.__class__, select, on_missing, **args)
|
||||
|
||||
|
||||
class DreamBigTextSwitch:
|
||||
_switch_type = "STRING"
|
||||
NODE_NAME = "Big Text Switch"
|
||||
ICON = "⭆"
|
||||
CATEGORY = NodeCategories.UTILS_SWITCHES
|
||||
RETURN_TYPES = (_switch_type,)
|
||||
RETURN_NAMES = ("selected",)
|
||||
FUNCTION = "pick"
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return _generate_switch_input(cls._switch_type)
|
||||
|
||||
@classmethod
|
||||
def IS_CHANGED(cls, *values):
|
||||
return hashed_as_strings(values)
|
||||
|
||||
def pick(self, select, on_missing, **args):
|
||||
return _do_pick(self.__class__, select, on_missing, **args)
|
||||
|
||||
|
||||
class DreamBigPaletteSwitch:
|
||||
_switch_type = RGBPalette.ID
|
||||
NODE_NAME = "Big Palette Switch"
|
||||
ICON = "⭆"
|
||||
CATEGORY = NodeCategories.UTILS_SWITCHES
|
||||
RETURN_TYPES = (_switch_type,)
|
||||
RETURN_NAMES = ("selected",)
|
||||
FUNCTION = "pick"
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return _generate_switch_input(cls._switch_type)
|
||||
|
||||
@classmethod
|
||||
def IS_CHANGED(cls, *values):
|
||||
return ALWAYS_CHANGED_FLAG
|
||||
|
||||
def pick(self, select, on_missing, **args):
|
||||
return _do_pick(self.__class__, select, on_missing, **args)
|
||||
|
||||
|
||||
class DreamBigFloatSwitch:
|
||||
_switch_type = "FLOAT"
|
||||
NODE_NAME = "Big Float Switch"
|
||||
ICON = "⭆"
|
||||
CATEGORY = NodeCategories.UTILS_SWITCHES
|
||||
RETURN_TYPES = (_switch_type,)
|
||||
RETURN_NAMES = ("selected",)
|
||||
FUNCTION = "pick"
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return _generate_switch_input(cls._switch_type)
|
||||
|
||||
@classmethod
|
||||
def IS_CHANGED(cls, *values):
|
||||
return hashed_as_strings(values)
|
||||
|
||||
def pick(self, select, on_missing, **args):
|
||||
return _do_pick(self.__class__, select, on_missing, **args)
|
||||
|
||||
|
||||
class DreamBigIntSwitch:
|
||||
_switch_type = "INT"
|
||||
NODE_NAME = "Big Int Switch"
|
||||
ICON = "⭆"
|
||||
CATEGORY = NodeCategories.UTILS_SWITCHES
|
||||
RETURN_TYPES = (_switch_type,)
|
||||
RETURN_NAMES = ("selected",)
|
||||
FUNCTION = "pick"
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return _generate_switch_input(cls._switch_type)
|
||||
|
||||
@classmethod
|
||||
def IS_CHANGED(cls, *values):
|
||||
return hashed_as_strings(values)
|
||||
|
||||
def pick(self, select, on_missing, **args):
|
||||
return _do_pick(self.__class__, select, on_missing, **args)
|
||||
|
||||
|
||||
class DreamBoolToFloat:
|
||||
NODE_NAME = "Boolean To Float"
|
||||
ICON = "⬖"
|
||||
CATEGORY = NodeCategories.UTILS_SWITCHES
|
||||
RETURN_TYPES = ("FLOAT",)
|
||||
RETURN_NAMES = ("result",)
|
||||
FUNCTION = "pick"
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"boolean": ("BOOLEAN", {"default": False}),
|
||||
"on_true": ("FLOAT", {"default": 1.0}),
|
||||
"on_false": ("FLOAT", {"default": 0.0})
|
||||
}
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def IS_CHANGED(cls, *values):
|
||||
return hashed_as_strings(values)
|
||||
|
||||
def pick(self, boolean, on_true, on_false):
|
||||
if boolean:
|
||||
return (on_true,)
|
||||
else:
|
||||
return (on_false,)
|
||||
|
||||
|
||||
class DreamBoolToInt:
|
||||
NODE_NAME = "Boolean To Int"
|
||||
ICON = "⬖"
|
||||
CATEGORY = NodeCategories.UTILS_SWITCHES
|
||||
RETURN_TYPES = ("INT",)
|
||||
RETURN_NAMES = ("result",)
|
||||
FUNCTION = "pick"
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"boolean": ("BOOLEAN", {"default": False}),
|
||||
"on_true": ("INT", {"default": 1}),
|
||||
"on_false": ("INT", {"default": 0})
|
||||
}
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def IS_CHANGED(cls, *values):
|
||||
return hashed_as_strings(values)
|
||||
|
||||
def pick(self, boolean, on_true, on_false):
|
||||
if boolean:
|
||||
return (on_true,)
|
||||
else:
|
||||
return (on_false,)
|
||||
@@ -26,6 +26,24 @@ class RGBPalette:
|
||||
for c in colors:
|
||||
self._colors.append(_fix_tuple(c))
|
||||
|
||||
def _calculate_channel_contrast(self, c):
|
||||
hist = list(map(lambda _: 0, range(16)))
|
||||
for pixel in self._colors:
|
||||
hist[pixel[c] // 16] += 1
|
||||
s = 0
|
||||
max_possible = (15 - 0) * (len(self) // 2) * (len(self) // 2)
|
||||
for i in range(16):
|
||||
for j in range(i):
|
||||
if i != j:
|
||||
s += abs(i - j) * hist[i] * hist[j]
|
||||
return s / max_possible
|
||||
|
||||
def _calculate_combined_contrast(self):
|
||||
s = 0
|
||||
for c in range(3):
|
||||
s += self._calculate_channel_contrast(c)
|
||||
return s / 3
|
||||
|
||||
def analyze(self):
|
||||
total_red = 0
|
||||
total_blue = 0
|
||||
@@ -38,7 +56,7 @@ class RGBPalette:
|
||||
r = float(total_red) / (255 * n)
|
||||
g = float(total_green) / (255 * n)
|
||||
b = float(total_blue) / (255 * n)
|
||||
return ((r + g + b) / 3.0, r, g, b)
|
||||
return ((r + g + b) / 3.0, self._calculate_combined_contrast(), r, g, b)
|
||||
|
||||
def __len__(self):
|
||||
return len(self._colors)
|
||||
@@ -64,6 +82,57 @@ class RGBPalette:
|
||||
return _ColorIterator()
|
||||
|
||||
|
||||
class PartialPrompt:
|
||||
ID = "PARTIAL_PROMPT"
|
||||
|
||||
def __init__(self):
|
||||
self._data = {}
|
||||
|
||||
def add(self, text: str, weight: float):
|
||||
output = PartialPrompt()
|
||||
output._data = dict(self._data)
|
||||
for parts in text.split(","):
|
||||
parts = parts.strip()
|
||||
if " " in parts:
|
||||
output._data["(" + parts + ")"] = weight
|
||||
else:
|
||||
output._data[parts] = weight
|
||||
return output
|
||||
|
||||
def is_empty(self):
|
||||
return not self._data
|
||||
|
||||
def abs_sum(self):
|
||||
if not self._data:
|
||||
return 0.0
|
||||
return sum(map(abs, self._data.values()))
|
||||
|
||||
def abs_max(self):
|
||||
if not self._data:
|
||||
return 0.0
|
||||
return max(map(abs, self._data.values()))
|
||||
|
||||
def scaled_by(self, f: float):
|
||||
new_data = PartialPrompt()
|
||||
new_data._data = dict(self._data)
|
||||
for text, weight in new_data._data.items():
|
||||
new_data._data[text] = weight * f
|
||||
return new_data
|
||||
|
||||
def finalize(self, clamp: float):
|
||||
items = self._data.items()
|
||||
items = sorted(items, key=lambda pair: (pair[1], pair[0]))
|
||||
pos = list()
|
||||
neg = list()
|
||||
for text, w in sorted(items, key=lambda pair: (-pair[1], pair[0])):
|
||||
if w >= 0.0001:
|
||||
pos.append("({}:{:.3f})".format(text, min(clamp, w)))
|
||||
for text, w in sorted(items, key=lambda pair: (pair[1], pair[0])):
|
||||
if w <= -0.0001:
|
||||
neg.append("({}:{:.3f})".format(text, min(clamp, -w)))
|
||||
return ", ".join(pos), ", ".join(neg)
|
||||
|
||||
|
||||
class FrameCounter:
|
||||
ID = "FRAME_COUNTER"
|
||||
|
||||
@@ -91,6 +160,14 @@ class FrameCounter:
|
||||
def current_time_in_seconds(self):
|
||||
return float(self.current_frame) / self.frames_per_second
|
||||
|
||||
@property
|
||||
def total_time_in_seconds(self):
|
||||
return float(self.total_frames) / self.frames_per_second
|
||||
|
||||
@property
|
||||
def remaining_time_in_seconds(self):
|
||||
return self.total_time_in_seconds - self.current_time_in_seconds
|
||||
|
||||
@property
|
||||
def progress(self):
|
||||
return float(self.current_frame) / (max(2, self.total_frames) - 1)
|
||||
|
||||
Reference in New Issue
Block a user