Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f48bed5b8a | ||
|
|
b09b8a79eb | ||
|
|
44afbb07c6 | ||
|
|
a8071b2089 | ||
|
|
12d7ef2a54 | ||
|
|
a94e21dbdd | ||
|
|
ae524cd593 | ||
|
|
d45fffff23 | ||
|
|
411948551a | ||
|
|
d87e5d8a7c | ||
|
|
9e87f209aa | ||
|
|
f09e0f3e36 | ||
|
|
b2dfaea97c | ||
|
|
1e50facb9e | ||
|
|
b63340f4b7 | ||
|
|
f77719bf34 | ||
|
|
c8fe03350e | ||
|
|
96258138b6 | ||
|
|
98776cccb6 | ||
|
|
942d7d8931 | ||
|
|
1d38975ce3 | ||
|
|
403095b513 | ||
|
|
170664c592 | ||
|
|
7b73e65cec | ||
|
|
d5c107e541 | ||
|
|
a22b11b43c | ||
|
|
4b059e5095 | ||
|
|
b5c804a332 | ||
|
|
801bcaf1d7 | ||
|
|
b9e6959c1f | ||
|
|
ed2c58cf2f | ||
|
|
f44a70900a | ||
|
|
bff5ca4fbe | ||
|
|
a81c591e9d | ||
|
|
426a6e85e9 | ||
|
|
e1a7ec58ed | ||
|
|
b2ddca87a9 | ||
|
|
6bc4c1616f | ||
|
|
bff8f66047 | ||
|
|
8babb241dd | ||
|
|
f456af2ec5 | ||
|
|
c3c7a4ccfd | ||
|
|
b56e007c57 | ||
|
|
e6545ec3c9 | ||
|
|
2d642150da | ||
|
|
c34e9f5213 | ||
|
|
68246e3adf | ||
|
|
ab73436fc2 | ||
|
|
d9efc1516a | ||
|
|
6880bde78c | ||
|
|
25cbdff39c | ||
|
|
07bfda783d | ||
|
|
d74fdb8de7 | ||
|
|
5def36f87f | ||
|
|
08a0a06820 | ||
|
|
5ce2e63f01 | ||
|
|
ebed0e60d9 | ||
|
|
f65df84de6 | ||
|
|
c5579afb12 | ||
|
|
7d5b6d8f4a | ||
|
|
af492b7669 | ||
|
|
15084a9264 | ||
|
|
01a9d17b42 | ||
|
|
f4f5a5c7c5 | ||
|
|
bcac6e1a33 | ||
|
|
96e5fa3ae8 | ||
|
|
4418ba9454 | ||
|
|
fefe43ebe3 |
@@ -0,0 +1,21 @@
|
||||
name: Publish to Comfy registry
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
paths:
|
||||
- "pyproject.toml"
|
||||
|
||||
jobs:
|
||||
publish-node:
|
||||
name: Publish Custom Node to registry
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
- name: Publish Custom Node
|
||||
uses: Comfy-Org/publish-node-action@main
|
||||
with:
|
||||
## Add your own personal access token to your Github Repository secrets and reference it here.
|
||||
personal_access_token: ${{ secrets.REGISTRY_ACCESS_TOKEN }}
|
||||
+2
-1
@@ -1,3 +1,4 @@
|
||||
.idea
|
||||
__pycache__
|
||||
config.json
|
||||
config.json
|
||||
*.cmd
|
||||
+11
-3
@@ -1,5 +1,8 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from typing import Type
|
||||
import sys,os
|
||||
|
||||
sys.path.append(str(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
from .base import *
|
||||
from .colors import *
|
||||
@@ -14,6 +17,8 @@ from .seq_processing import *
|
||||
from .switches import *
|
||||
from .utility import *
|
||||
from .calculate import *
|
||||
from .laboratory import *
|
||||
#from .lazyswitches import *
|
||||
|
||||
_NODE_CLASSES: List[Type] = [DreamSineWave, DreamLinear, DreamCSVCurve, DreamBeatCurve, DreamFrameDimensions,
|
||||
DreamImageMotion, DreamNoiseFromPalette, DreamAnalyzePalette, DreamColorShift,
|
||||
@@ -21,17 +26,20 @@ _NODE_CLASSES: List[Type] = [DreamSineWave, DreamLinear, DreamCSVCurve, DreamBea
|
||||
DreamSimpleFrameCounter, DreamImageSequenceInputWithDefaultFallback,
|
||||
DreamImageSequenceOutput, DreamCSVGenerator, DreamImageAreaSampler,
|
||||
DreamVideoEncoder, DreamSequenceTweening, DreamSequenceBlend, DreamColorAlign,
|
||||
DreamImageSampler, DreamNoiseFromAreaPalettes, DreamVideoEncoderMpegCoder,
|
||||
DreamImageSampler, DreamNoiseFromAreaPalettes,
|
||||
DreamInputString, DreamInputFloat, DreamInputInt, DreamInputText, DreamBigLatentSwitch,
|
||||
DreamFrameCountCalculator, DreamBigImageSwitch, DreamBigTextSwitch, DreamBigFloatSwitch,
|
||||
DreamBigIntSwitch, DreamBigPaletteSwitch, DreamWeightedPromptBuilder, DreamPromptFinalizer,
|
||||
DreamFrameCounterInfo, DreamBoolToFloat, DreamBoolToInt, DreamSawWave, DreamTriangleWave,
|
||||
DreamTriangleEvent, DreamSmoothEvent, DreamCalculation]
|
||||
DreamTriangleEvent, DreamSmoothEvent, DreamCalculation, DreamImageColorShift,
|
||||
DreamComparePalette, DreamImageContrast, DreamImageBrightness, DreamLogFile,
|
||||
DreamLaboratory, DreamStringToLog, DreamIntToLog, DreamFloatToLog, DreamJoinLog,
|
||||
DreamStringTokenizer, DreamWavCurve, DreamFrameCounterTimeOffset, DreamRandomPromptWords]
|
||||
_SIGNATURE_SUFFIX = " [Dream]"
|
||||
|
||||
MANIFEST = {
|
||||
"name": "Dream Project Animation",
|
||||
"version": (3, 1, 0),
|
||||
"version": (5, 1, 2),
|
||||
"author": "Dream Project",
|
||||
"project": "https://github.com/alt-key-project/comfyui-dream-project",
|
||||
"description": "Various utility nodes for creating animations with ComfyUI",
|
||||
|
||||
@@ -3,7 +3,7 @@ import glob
|
||||
|
||||
from .categories import NodeCategories
|
||||
from .shared import *
|
||||
from .types import *
|
||||
from .dreamtypes import *
|
||||
|
||||
|
||||
class DreamFrameCounterInfo:
|
||||
@@ -22,10 +22,6 @@ class DreamFrameCounterInfo:
|
||||
"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,
|
||||
@@ -56,8 +52,14 @@ class DreamDirectoryFileCount:
|
||||
FUNCTION = "result"
|
||||
|
||||
@classmethod
|
||||
def IS_CHANGED(cls, *v):
|
||||
return ALWAYS_CHANGED_FLAG
|
||||
def IS_CHANGED(cls, directory_path, patterns):
|
||||
if not os.path.isdir(directory_path):
|
||||
return ""
|
||||
total = 0
|
||||
for pattern in patterns.split("|"):
|
||||
files = list(glob.glob(pattern, root_dir=directory_path))
|
||||
total += len(files)
|
||||
return total
|
||||
|
||||
def result(self, directory_path, patterns):
|
||||
if not os.path.isdir(directory_path):
|
||||
@@ -66,7 +68,6 @@ class DreamDirectoryFileCount:
|
||||
for pattern in patterns.split("|"):
|
||||
files = list(glob.glob(pattern, root_dir=directory_path))
|
||||
total += len(files)
|
||||
print("total " + str(total))
|
||||
return (total,)
|
||||
|
||||
|
||||
@@ -88,13 +89,31 @@ class DreamFrameCounterOffset:
|
||||
RETURN_NAMES = ("frame_counter",)
|
||||
FUNCTION = "result"
|
||||
|
||||
@classmethod
|
||||
def IS_CHANGED(cls, frame_counter, offset):
|
||||
return hashed_as_strings(frame_counter, offset)
|
||||
|
||||
def result(self, frame_counter: FrameCounter, offset):
|
||||
return (frame_counter.incremented(offset),)
|
||||
|
||||
class DreamFrameCounterTimeOffset:
|
||||
NODE_NAME = "Frame Counter Time Offset"
|
||||
|
||||
ICON = "±"
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": SharedTypes.frame_counter | {
|
||||
"offset_seconds": ("FLOAT", {"default": 0.0}),
|
||||
},
|
||||
}
|
||||
|
||||
CATEGORY = NodeCategories.ANIMATION
|
||||
RETURN_TYPES = (FrameCounter.ID,)
|
||||
RETURN_NAMES = ("frame_counter",)
|
||||
FUNCTION = "result"
|
||||
|
||||
def result(self, frame_counter: FrameCounter, offset_seconds):
|
||||
offset = offset_seconds * frame_counter.frames_per_second
|
||||
return (frame_counter.incremented(offset),)
|
||||
|
||||
|
||||
class DreamSimpleFrameCounter:
|
||||
NODE_NAME = "Frame Counter (Simple)"
|
||||
@@ -115,10 +134,6 @@ class DreamSimpleFrameCounter:
|
||||
RETURN_NAMES = ("frame_counter",)
|
||||
FUNCTION = "result"
|
||||
|
||||
@classmethod
|
||||
def IS_CHANGED(cls, *values):
|
||||
return ALWAYS_CHANGED_FLAG
|
||||
|
||||
def result(self, frame_index, total_frames, frames_per_second):
|
||||
n = frame_index
|
||||
return (FrameCounter(n, total_frames, frames_per_second),)
|
||||
@@ -146,8 +161,14 @@ class DreamDirectoryBackedFrameCounter:
|
||||
FUNCTION = "result"
|
||||
|
||||
@classmethod
|
||||
def IS_CHANGED(cls, *values):
|
||||
return ALWAYS_CHANGED_FLAG
|
||||
def IS_CHANGED(cls, directory_path, patterns, indexing, total_frames, frames_per_second):
|
||||
if not os.path.isdir(directory_path):
|
||||
return ""
|
||||
total = 0
|
||||
for pattern in patterns.split("|"):
|
||||
files = list(glob.glob(pattern, root_dir=directory_path))
|
||||
total += len(files)
|
||||
return (total, indexing, total_frames, frames_per_second)
|
||||
|
||||
def result(self, directory_path, pattern, indexing, total_frames, frames_per_second):
|
||||
results = list_images_in_directory(directory_path, pattern, indexing == "alphabetic order")
|
||||
@@ -178,10 +199,6 @@ class DreamFrameCountCalculator:
|
||||
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),)
|
||||
|
||||
+2
-5
@@ -10,6 +10,7 @@ from .shared import hashed_as_strings
|
||||
|
||||
class DreamCalculation:
|
||||
NODE_NAME = "Calculation"
|
||||
ICON = "🖩"
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
@@ -27,15 +28,11 @@ class DreamCalculation:
|
||||
}
|
||||
}
|
||||
|
||||
CATEGORY = NodeCategories.ANIMATION_CURVES
|
||||
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()
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from .categories import NodeCategories
|
||||
from .shared import *
|
||||
from .types import *
|
||||
from .dreamtypes import *
|
||||
|
||||
|
||||
class DreamImageAreaSampler:
|
||||
@@ -25,10 +26,6 @@ class DreamImageAreaSampler:
|
||||
RETURN_NAMES = ("palette",)
|
||||
FUNCTION = "result"
|
||||
|
||||
@classmethod
|
||||
def IS_CHANGED(cls, *values):
|
||||
return ALWAYS_CHANGED_FLAG
|
||||
|
||||
def _get_pixel_area(self, img: DreamImage, area):
|
||||
w = img.width
|
||||
h = img.height
|
||||
@@ -101,10 +98,6 @@ class DreamImageSampler:
|
||||
RETURN_NAMES = ("palette",)
|
||||
FUNCTION = "result"
|
||||
|
||||
@classmethod
|
||||
def IS_CHANGED(cls, *values):
|
||||
return ALWAYS_CHANGED_FLAG
|
||||
|
||||
def result(self, image, samples, seed):
|
||||
result = list()
|
||||
r = random.Random()
|
||||
@@ -128,7 +121,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}),
|
||||
}
|
||||
}
|
||||
@@ -138,10 +131,6 @@ class DreamColorAlign:
|
||||
RETURN_NAMES = ("palette",)
|
||||
FUNCTION = "result"
|
||||
|
||||
@classmethod
|
||||
def IS_CHANGED(cls, *values):
|
||||
return ALWAYS_CHANGED_FLAG
|
||||
|
||||
def result(self, palette: Tuple[RGBPalette], target_align: Tuple[RGBPalette], alignment_factor: float):
|
||||
results = list()
|
||||
|
||||
@@ -151,8 +140,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
|
||||
@@ -186,10 +175,6 @@ class DreamColorShift:
|
||||
RETURN_NAMES = ("palette",)
|
||||
FUNCTION = "result"
|
||||
|
||||
@classmethod
|
||||
def IS_CHANGED(cls, *values):
|
||||
return ALWAYS_CHANGED_FLAG
|
||||
|
||||
def result(self, palette, red_multiplier, green_multiplier, blue_multiplier, fixed_brightness):
|
||||
results = list()
|
||||
|
||||
@@ -214,9 +199,134 @@ 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"
|
||||
|
||||
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"
|
||||
|
||||
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"
|
||||
|
||||
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"
|
||||
|
||||
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,22 +336,19 @@ 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
|
||||
def IS_CHANGED(cls, *values):
|
||||
return ALWAYS_CHANGED_FLAG
|
||||
|
||||
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
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import csv
|
||||
import functools
|
||||
import math
|
||||
import os
|
||||
|
||||
from scipy.io.wavfile import read as wav_read
|
||||
|
||||
from .categories import NodeCategories
|
||||
from .shared import hashed_as_strings
|
||||
from .types import SharedTypes, FrameCounter
|
||||
from .dreamtypes import SharedTypes, FrameCounter
|
||||
|
||||
|
||||
def _linear_value_calc(x, x_start, x_end, y_start, y_end):
|
||||
@@ -40,10 +44,6 @@ class DreamSineWave:
|
||||
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
|
||||
a = (max_value - min_value) * 0.5
|
||||
@@ -73,10 +73,6 @@ class DreamSawWave:
|
||||
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
|
||||
@@ -103,10 +99,6 @@ class DreamTriangleWave:
|
||||
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
|
||||
@@ -119,6 +111,66 @@ class DreamTriangleWave:
|
||||
return _curve_result(y)
|
||||
|
||||
|
||||
class WavData:
|
||||
def __init__(self, sampling_rate: float, single_channel_samples, fps: float):
|
||||
self._length_in_seconds = len(single_channel_samples) / sampling_rate
|
||||
self._num_buckets = round(self._length_in_seconds * fps * 3)
|
||||
self._bucket_size = len(single_channel_samples) / float(self._num_buckets)
|
||||
self._buckets = list()
|
||||
self._rate = sampling_rate
|
||||
self._max_bucket_value = 0
|
||||
for i in range(self._num_buckets):
|
||||
start_index = round(i * self._bucket_size)
|
||||
end_index = round((i + 1) * self._bucket_size) - 1
|
||||
samples = list(map(lambda n: abs(n), single_channel_samples[start_index:end_index]))
|
||||
bucket_total = sum(samples)
|
||||
self._buckets.append(bucket_total)
|
||||
self._max_bucket_value=max(bucket_total, self._max_bucket_value)
|
||||
|
||||
for i in range(self._num_buckets):
|
||||
self._buckets[i] = float(self._buckets[i]) / self._max_bucket_value
|
||||
|
||||
def value_at_time(self, second: float) -> float:
|
||||
if second < 0.0 or second > self._length_in_seconds:
|
||||
return 0.0
|
||||
nsample = second * self._rate
|
||||
nbucket = min(max(0, round(nsample / self._bucket_size)), self._num_buckets - 1)
|
||||
return self._buckets[nbucket]
|
||||
|
||||
|
||||
@functools.lru_cache(4)
|
||||
def _wav_loader(filepath, fps):
|
||||
sampling_rate, samples = wav_read(filepath)
|
||||
single_channel = samples[:, 0]
|
||||
return WavData(sampling_rate, single_channel, fps)
|
||||
|
||||
|
||||
class DreamWavCurve:
|
||||
NODE_NAME = "WAV Curve"
|
||||
CATEGORY = NodeCategories.ANIMATION_CURVES
|
||||
RETURN_TYPES = ("FLOAT", "INT")
|
||||
RETURN_NAMES = ("FLOAT", "INT")
|
||||
FUNCTION = "result"
|
||||
ICON = "∿"
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": SharedTypes.frame_counter | {
|
||||
"wav_path": ("STRING", {"default": "audio.wav"}),
|
||||
"scale": ("FLOAT", {"default": 1.0, "multiline": False})
|
||||
},
|
||||
}
|
||||
|
||||
def result(self, frame_counter: FrameCounter, wav_path, scale):
|
||||
if not os.path.isfile(wav_path):
|
||||
return (0.0, 0)
|
||||
data = _wav_loader(wav_path, frame_counter.frames_per_second)
|
||||
frame_counter.current_time_in_seconds
|
||||
v = data.value_at_time(frame_counter.current_time_in_seconds)
|
||||
return (v * scale, round(v * scale))
|
||||
|
||||
|
||||
class DreamTriangleEvent:
|
||||
NODE_NAME = "Triangle Event Curve"
|
||||
|
||||
@@ -138,10 +190,6 @@ class DreamTriangleEvent:
|
||||
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
|
||||
@@ -174,10 +222,6 @@ class DreamSmoothEvent:
|
||||
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
|
||||
@@ -213,9 +257,9 @@ class DreamBeatCurve:
|
||||
"accent_1": ("INT", {"default": 1, "min": 1, "max": 24}),
|
||||
},
|
||||
"optional": {
|
||||
"accent_2": ("INT", {"default": 3, "min": 1, "max": 24}),
|
||||
"accent_3": ("INT", {"default": 0}),
|
||||
"accent_4": ("INT", {"default": 0}),
|
||||
"accent_2": ("INT", {"default": 0, "min": 0, "max": 24}),
|
||||
"accent_3": ("INT", {"default": 0, "min": 0, "max": 24}),
|
||||
"accent_4": ("INT", {"default": 0, "min": 0, "max": 24}),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -224,10 +268,6 @@ class DreamBeatCurve:
|
||||
RETURN_NAMES = ("FLOAT", "INT")
|
||||
FUNCTION = "result"
|
||||
|
||||
@classmethod
|
||||
def IS_CHANGED(cls, *values):
|
||||
return hashed_as_strings(*values)
|
||||
|
||||
def _get_value_for_accent(self, accent, measure_length, bpm, frame_counter: FrameCounter, frame_offset):
|
||||
current_frame = frame_counter.current_frame + frame_offset
|
||||
frames_per_minute = frame_counter.frames_per_second * 60.0
|
||||
@@ -272,10 +312,6 @@ class DreamLinear:
|
||||
RETURN_NAMES = ("FLOAT", "INT")
|
||||
FUNCTION = "result"
|
||||
|
||||
@classmethod
|
||||
def IS_CHANGED(cls, *values):
|
||||
return hashed_as_strings(*values)
|
||||
|
||||
def result(self, initial_value, final_value, frame_counter: FrameCounter):
|
||||
d = final_value - initial_value
|
||||
v = initial_value + frame_counter.progress * d
|
||||
@@ -310,10 +346,6 @@ class DreamCSVGenerator:
|
||||
FUNCTION = "write"
|
||||
OUTPUT_NODE = True
|
||||
|
||||
@classmethod
|
||||
def IS_CHANGED(cls, *values):
|
||||
return hashed_as_strings(*values)
|
||||
|
||||
def write(self, csvfile, frame_counter: FrameCounter, value, csv_dialect):
|
||||
if frame_counter.is_first_frame and csvfile:
|
||||
with open(csvfile, 'w', newline='') as csvfile:
|
||||
@@ -346,10 +378,6 @@ class DreamCSVCurve:
|
||||
RETURN_NAMES = ("FLOAT", "INT")
|
||||
FUNCTION = "result"
|
||||
|
||||
@classmethod
|
||||
def IS_CHANGED(cls, *values):
|
||||
return hashed_as_strings(*values)
|
||||
|
||||
def _row_yield(self, file, csv_dialect):
|
||||
prev_row = None
|
||||
for row in csv.reader(file, dialect=csv_dialect):
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import random
|
||||
import time
|
||||
|
||||
from typing import List, Dict
|
||||
from typing import List, Dict, Tuple
|
||||
|
||||
from .shared import DreamImage
|
||||
|
||||
@@ -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)
|
||||
@@ -73,7 +91,12 @@ class PartialPrompt:
|
||||
def add(self, text: str, weight: float):
|
||||
output = PartialPrompt()
|
||||
output._data = dict(self._data)
|
||||
output._data[text.strip()] = weight
|
||||
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):
|
||||
@@ -110,6 +133,35 @@ class PartialPrompt:
|
||||
return ", ".join(pos), ", ".join(neg)
|
||||
|
||||
|
||||
class LogEntry:
|
||||
ID = "LOG_ENTRY"
|
||||
|
||||
@classmethod
|
||||
def new(cls, text):
|
||||
return LogEntry([(time.time(), text)])
|
||||
|
||||
def __init__(self, data: List[Tuple[float, str]] = None):
|
||||
if data is None:
|
||||
self._data = list()
|
||||
else:
|
||||
self._data = list(data)
|
||||
|
||||
def add(self, text: str):
|
||||
new_data = list(self._data)
|
||||
new_data.append((time.time(), text))
|
||||
return LogEntry(new_data)
|
||||
|
||||
def merge(self, log_entry):
|
||||
new_data = list(self._data)
|
||||
new_data.extend(log_entry._data)
|
||||
return LogEntry(new_data)
|
||||
|
||||
def get_filtered_entries(self, t: float):
|
||||
for d in sorted(self._data):
|
||||
if d[0] > t:
|
||||
yield d
|
||||
|
||||
|
||||
class FrameCounter:
|
||||
ID = "FRAME_COUNTER"
|
||||
|
||||
+2
-2
@@ -3,7 +3,7 @@ EMBEDDED_CONFIGURATION = {
|
||||
"file_extension": "mp4",
|
||||
"path": "ffmpeg",
|
||||
"arguments": ["-r", "%FPS%", "-f", "concat", "-safe", "0", "-vsync",
|
||||
"cfr", "-i", "%FRAMES%", "-c:v", "libx265", "-pix_fmt",
|
||||
"cfr", "-i", "%FRAMES%", "-c:v", "libx264", "-pix_fmt",
|
||||
"yuv420p", "%OUTPUT%"]
|
||||
},
|
||||
"mpeg_coder": {
|
||||
@@ -11,7 +11,7 @@ EMBEDDED_CONFIGURATION = {
|
||||
"bitrate_factor": 1.0,
|
||||
"max_b_frame": 2,
|
||||
"file_extension": "mp4",
|
||||
"codec_name": "libx265"
|
||||
"codec_name": "libx264"
|
||||
},
|
||||
"encoding": {
|
||||
"jpeg_quality": 95
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"last_node_id": 422,
|
||||
"last_link_id": 5466,
|
||||
"last_node_id": 423,
|
||||
"last_link_id": 5467,
|
||||
"nodes": [
|
||||
{
|
||||
"id": 77,
|
||||
@@ -24,13 +24,7 @@
|
||||
2312
|
||||
],
|
||||
"widget": {
|
||||
"name": "text_2",
|
||||
"config": [
|
||||
"STRING",
|
||||
{
|
||||
"multiline": true
|
||||
}
|
||||
]
|
||||
"name": "text"
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -65,13 +59,7 @@
|
||||
],
|
||||
"slot_index": 0,
|
||||
"widget": {
|
||||
"name": "text",
|
||||
"config": [
|
||||
"STRING",
|
||||
{
|
||||
"multiline": true
|
||||
}
|
||||
]
|
||||
"name": "text"
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -1101,15 +1089,7 @@
|
||||
"type": "INT",
|
||||
"link": 5351,
|
||||
"widget": {
|
||||
"name": "total_frames",
|
||||
"config": [
|
||||
"INT",
|
||||
{
|
||||
"default": 100,
|
||||
"min": 2,
|
||||
"max": 5184000
|
||||
}
|
||||
]
|
||||
"name": "total_frames"
|
||||
},
|
||||
"slot_index": 0
|
||||
},
|
||||
@@ -1118,14 +1098,7 @@
|
||||
"type": "STRING",
|
||||
"link": 5347,
|
||||
"widget": {
|
||||
"name": "directory_path",
|
||||
"config": [
|
||||
"STRING",
|
||||
{
|
||||
"default": "",
|
||||
"multiline": false
|
||||
}
|
||||
]
|
||||
"name": "directory_path"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -1133,14 +1106,7 @@
|
||||
"type": "INT",
|
||||
"link": 5350,
|
||||
"widget": {
|
||||
"name": "frames_per_second",
|
||||
"config": [
|
||||
"INT",
|
||||
{
|
||||
"min": 1,
|
||||
"default": 25
|
||||
}
|
||||
]
|
||||
"name": "frames_per_second"
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -1503,13 +1469,7 @@
|
||||
"type": "STRING",
|
||||
"link": 2312,
|
||||
"widget": {
|
||||
"name": "text",
|
||||
"config": [
|
||||
"STRING",
|
||||
{
|
||||
"multiline": true
|
||||
}
|
||||
]
|
||||
"name": "text"
|
||||
},
|
||||
"slot_index": 1
|
||||
}
|
||||
@@ -1560,13 +1520,7 @@
|
||||
"type": "STRING",
|
||||
"link": 2309,
|
||||
"widget": {
|
||||
"name": "text",
|
||||
"config": [
|
||||
"STRING",
|
||||
{
|
||||
"multiline": true
|
||||
}
|
||||
]
|
||||
"name": "text"
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -1995,16 +1949,7 @@
|
||||
"type": "INT",
|
||||
"link": 5074,
|
||||
"widget": {
|
||||
"name": "width",
|
||||
"config": [
|
||||
"INT",
|
||||
{
|
||||
"default": 512,
|
||||
"min": 1,
|
||||
"max": 8192,
|
||||
"step": 1
|
||||
}
|
||||
]
|
||||
"name": "width"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -2012,16 +1957,7 @@
|
||||
"type": "INT",
|
||||
"link": 5075,
|
||||
"widget": {
|
||||
"name": "height",
|
||||
"config": [
|
||||
"INT",
|
||||
{
|
||||
"default": 512,
|
||||
"min": 1,
|
||||
"max": 8192,
|
||||
"step": 1
|
||||
}
|
||||
]
|
||||
"name": "height"
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -2462,16 +2398,7 @@
|
||||
"type": "FLOAT",
|
||||
"link": 5222,
|
||||
"widget": {
|
||||
"name": "x_translation",
|
||||
"config": [
|
||||
"FLOAT",
|
||||
{
|
||||
"default": 0,
|
||||
"min": -10,
|
||||
"max": 10,
|
||||
"step": 0.01
|
||||
}
|
||||
]
|
||||
"name": "x_translation"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -2479,16 +2406,7 @@
|
||||
"type": "FLOAT",
|
||||
"link": 5223,
|
||||
"widget": {
|
||||
"name": "y_translation",
|
||||
"config": [
|
||||
"FLOAT",
|
||||
{
|
||||
"default": 0,
|
||||
"min": -10,
|
||||
"max": 10,
|
||||
"step": 0.01
|
||||
}
|
||||
]
|
||||
"name": "y_translation"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -2496,16 +2414,7 @@
|
||||
"type": "FLOAT",
|
||||
"link": 5225,
|
||||
"widget": {
|
||||
"name": "zoom",
|
||||
"config": [
|
||||
"FLOAT",
|
||||
{
|
||||
"default": 0,
|
||||
"min": -10,
|
||||
"max": 10,
|
||||
"step": 0.01
|
||||
}
|
||||
]
|
||||
"name": "zoom"
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -2707,14 +2616,7 @@
|
||||
"type": "STRING",
|
||||
"link": 5442,
|
||||
"widget": {
|
||||
"name": "directory_path",
|
||||
"config": [
|
||||
"STRING",
|
||||
{
|
||||
"default": "I:\\AI\\ComfyUI\\ComfyUI\\output",
|
||||
"multiline": false
|
||||
}
|
||||
]
|
||||
"name": "directory_path"
|
||||
},
|
||||
"slot_index": 2
|
||||
}
|
||||
@@ -2728,6 +2630,12 @@
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
},
|
||||
{
|
||||
"name": "log_entry",
|
||||
"type": "LOG_ENTRY",
|
||||
"links": null,
|
||||
"shape": 3
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
@@ -3206,14 +3114,7 @@
|
||||
"type": "STRING",
|
||||
"link": 5348,
|
||||
"widget": {
|
||||
"name": "directory_path",
|
||||
"config": [
|
||||
"STRING",
|
||||
{
|
||||
"default": "",
|
||||
"multiline": false
|
||||
}
|
||||
]
|
||||
"name": "directory_path"
|
||||
},
|
||||
"slot_index": 2
|
||||
}
|
||||
@@ -3227,6 +3128,12 @@
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
},
|
||||
{
|
||||
"name": "frame_name",
|
||||
"type": "STRING",
|
||||
"links": null,
|
||||
"shape": 3
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
@@ -4006,7 +3913,7 @@
|
||||
"name": "sequence",
|
||||
"type": "ANIMATION_SEQUENCE",
|
||||
"links": [
|
||||
5466
|
||||
5467
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
@@ -4022,11 +3929,11 @@
|
||||
"bgcolor": "#593930"
|
||||
},
|
||||
{
|
||||
"id": 422,
|
||||
"type": "Video Encoder (mpegCoder) [Dream]",
|
||||
"id": 423,
|
||||
"type": "FFMPEG Video Encoder [Dream]",
|
||||
"pos": [
|
||||
4785,
|
||||
561
|
||||
557
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
@@ -4039,11 +3946,19 @@
|
||||
{
|
||||
"name": "sequence",
|
||||
"type": "ANIMATION_SEQUENCE",
|
||||
"link": 5466
|
||||
"link": 5467
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "log_entry",
|
||||
"type": "LOG_ENTRY",
|
||||
"links": null,
|
||||
"shape": 3
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "Video Encoder (mpegCoder) [Dream]"
|
||||
"Node name for S&R": "FFMPEG Video Encoder [Dream]"
|
||||
},
|
||||
"widgets_values": [
|
||||
"video",
|
||||
@@ -4942,10 +4857,10 @@
|
||||
"*"
|
||||
],
|
||||
[
|
||||
5466,
|
||||
5467,
|
||||
281,
|
||||
0,
|
||||
422,
|
||||
423,
|
||||
0,
|
||||
"ANIMATION_SEQUENCE"
|
||||
]
|
||||
@@ -4959,7 +4874,8 @@
|
||||
474,
|
||||
885
|
||||
],
|
||||
"color": "#a1309b"
|
||||
"color": "#a1309b",
|
||||
"font_size": 24
|
||||
},
|
||||
{
|
||||
"title": "Settings",
|
||||
@@ -4969,7 +4885,8 @@
|
||||
482,
|
||||
742
|
||||
],
|
||||
"color": "#b58b2a"
|
||||
"color": "#b58b2a",
|
||||
"font_size": 24
|
||||
},
|
||||
{
|
||||
"title": "Inpainting/Outpainting",
|
||||
@@ -4979,7 +4896,8 @@
|
||||
1711,
|
||||
528
|
||||
],
|
||||
"color": "#929054"
|
||||
"color": "#929054",
|
||||
"font_size": 24
|
||||
},
|
||||
{
|
||||
"title": "Prev Frame Move",
|
||||
@@ -4989,7 +4907,8 @@
|
||||
1450,
|
||||
456
|
||||
],
|
||||
"color": "#3f789e"
|
||||
"color": "#3f789e",
|
||||
"font_size": 24
|
||||
},
|
||||
{
|
||||
"title": "Full frame sampler",
|
||||
@@ -4999,7 +4918,8 @@
|
||||
691,
|
||||
700
|
||||
],
|
||||
"color": "#88A"
|
||||
"color": "#88A",
|
||||
"font_size": 24
|
||||
},
|
||||
{
|
||||
"title": "Output",
|
||||
@@ -5009,7 +4929,8 @@
|
||||
1458,
|
||||
381
|
||||
],
|
||||
"color": "#b06634"
|
||||
"color": "#b06634",
|
||||
"font_size": 24
|
||||
},
|
||||
{
|
||||
"title": "Animation Driver",
|
||||
@@ -5019,7 +4940,8 @@
|
||||
336,
|
||||
373
|
||||
],
|
||||
"color": "#3f789e"
|
||||
"color": "#3f789e",
|
||||
"font_size": 24
|
||||
},
|
||||
{
|
||||
"title": "Motion Control",
|
||||
@@ -5029,7 +4951,8 @@
|
||||
1451,
|
||||
392
|
||||
],
|
||||
"color": "#ef75ff"
|
||||
"color": "#ef75ff",
|
||||
"font_size": 24
|
||||
},
|
||||
{
|
||||
"title": "Model selection",
|
||||
@@ -5039,7 +4962,8 @@
|
||||
476,
|
||||
204
|
||||
],
|
||||
"color": "#3f789e"
|
||||
"color": "#3f789e",
|
||||
"font_size": 24
|
||||
},
|
||||
{
|
||||
"title": "Previews",
|
||||
@@ -5049,7 +4973,8 @@
|
||||
6473,
|
||||
329
|
||||
],
|
||||
"color": "#444"
|
||||
"color": "#444",
|
||||
"font_size": 24
|
||||
}
|
||||
],
|
||||
"config": {},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"last_node_id": 421,
|
||||
"last_link_id": 5462,
|
||||
"last_node_id": 422,
|
||||
"last_link_id": 5463,
|
||||
"nodes": [
|
||||
{
|
||||
"id": 77,
|
||||
@@ -24,13 +24,7 @@
|
||||
2312
|
||||
],
|
||||
"widget": {
|
||||
"name": "text_2",
|
||||
"config": [
|
||||
"STRING",
|
||||
{
|
||||
"multiline": true
|
||||
}
|
||||
]
|
||||
"name": "text"
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -65,13 +59,7 @@
|
||||
],
|
||||
"slot_index": 0,
|
||||
"widget": {
|
||||
"name": "text",
|
||||
"config": [
|
||||
"STRING",
|
||||
{
|
||||
"multiline": true
|
||||
}
|
||||
]
|
||||
"name": "text"
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -327,7 +315,7 @@
|
||||
26
|
||||
],
|
||||
"flags": {},
|
||||
"order": 24,
|
||||
"order": 23,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
@@ -365,7 +353,7 @@
|
||||
"1": 130
|
||||
},
|
||||
"flags": {},
|
||||
"order": 49,
|
||||
"order": 54,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
@@ -390,7 +378,7 @@
|
||||
},
|
||||
"widgets_values": [
|
||||
256,
|
||||
183250672320803,
|
||||
984424700736240,
|
||||
"randomize",
|
||||
"center-right"
|
||||
],
|
||||
@@ -409,7 +397,7 @@
|
||||
"1": 130
|
||||
},
|
||||
"flags": {},
|
||||
"order": 50,
|
||||
"order": 55,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
@@ -434,7 +422,7 @@
|
||||
},
|
||||
"widgets_values": [
|
||||
256,
|
||||
57339603779837,
|
||||
393866830673316,
|
||||
"randomize",
|
||||
"bottom-center"
|
||||
],
|
||||
@@ -709,7 +697,7 @@
|
||||
},
|
||||
"widgets_values": [
|
||||
256,
|
||||
902082438494247,
|
||||
912873459964733,
|
||||
"randomize",
|
||||
"bottom-center"
|
||||
],
|
||||
@@ -753,7 +741,7 @@
|
||||
},
|
||||
"widgets_values": [
|
||||
256,
|
||||
638849047502927,
|
||||
257321647530942,
|
||||
"randomize",
|
||||
"center-right"
|
||||
],
|
||||
@@ -790,16 +778,7 @@
|
||||
"type": "FLOAT",
|
||||
"link": 5334,
|
||||
"widget": {
|
||||
"name": "alignment_factor",
|
||||
"config": [
|
||||
"FLOAT",
|
||||
{
|
||||
"default": 0.5,
|
||||
"min": 0,
|
||||
"max": 10,
|
||||
"step": 0.1
|
||||
}
|
||||
]
|
||||
"name": "alignment_factor"
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -818,7 +797,7 @@
|
||||
"Node name for S&R": "Palette Color Align [Dream]"
|
||||
},
|
||||
"widgets_values": [
|
||||
1.2
|
||||
1.2000000000000002
|
||||
],
|
||||
"color": "#323",
|
||||
"bgcolor": "#535"
|
||||
@@ -853,16 +832,7 @@
|
||||
"type": "FLOAT",
|
||||
"link": 5325,
|
||||
"widget": {
|
||||
"name": "alignment_factor",
|
||||
"config": [
|
||||
"FLOAT",
|
||||
{
|
||||
"default": 0.5,
|
||||
"min": 0,
|
||||
"max": 10,
|
||||
"step": 0.1
|
||||
}
|
||||
]
|
||||
"name": "alignment_factor"
|
||||
},
|
||||
"slot_index": 2
|
||||
}
|
||||
@@ -882,7 +852,7 @@
|
||||
"Node name for S&R": "Palette Color Align [Dream]"
|
||||
},
|
||||
"widgets_values": [
|
||||
1.2
|
||||
1.2000000000000002
|
||||
],
|
||||
"color": "#322",
|
||||
"bgcolor": "#533"
|
||||
@@ -917,16 +887,7 @@
|
||||
"type": "FLOAT",
|
||||
"link": 5333,
|
||||
"widget": {
|
||||
"name": "alignment_factor",
|
||||
"config": [
|
||||
"FLOAT",
|
||||
{
|
||||
"default": 0.5,
|
||||
"min": 0,
|
||||
"max": 10,
|
||||
"step": 0.1
|
||||
}
|
||||
]
|
||||
"name": "alignment_factor"
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -945,7 +906,7 @@
|
||||
"Node name for S&R": "Palette Color Align [Dream]"
|
||||
},
|
||||
"widgets_values": [
|
||||
1.2
|
||||
1.2000000000000002
|
||||
],
|
||||
"color": "#223",
|
||||
"bgcolor": "#335"
|
||||
@@ -980,16 +941,7 @@
|
||||
"type": "FLOAT",
|
||||
"link": 5328,
|
||||
"widget": {
|
||||
"name": "alignment_factor",
|
||||
"config": [
|
||||
"FLOAT",
|
||||
{
|
||||
"default": 0.5,
|
||||
"min": 0,
|
||||
"max": 10,
|
||||
"step": 0.1
|
||||
}
|
||||
]
|
||||
"name": "alignment_factor"
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -1008,7 +960,7 @@
|
||||
"Node name for S&R": "Palette Color Align [Dream]"
|
||||
},
|
||||
"widgets_values": [
|
||||
1.2
|
||||
1.2000000000000002
|
||||
],
|
||||
"color": "#432",
|
||||
"bgcolor": "#653"
|
||||
@@ -1209,7 +1161,7 @@
|
||||
"Node name for S&R": "KSampler"
|
||||
},
|
||||
"widgets_values": [
|
||||
586618692559734,
|
||||
838611060522510,
|
||||
"randomize",
|
||||
25,
|
||||
9,
|
||||
@@ -1530,15 +1482,7 @@
|
||||
"type": "INT",
|
||||
"link": 5351,
|
||||
"widget": {
|
||||
"name": "total_frames",
|
||||
"config": [
|
||||
"INT",
|
||||
{
|
||||
"default": 100,
|
||||
"min": 2,
|
||||
"max": 5184000
|
||||
}
|
||||
]
|
||||
"name": "total_frames"
|
||||
},
|
||||
"slot_index": 0
|
||||
},
|
||||
@@ -1547,14 +1491,7 @@
|
||||
"type": "STRING",
|
||||
"link": 5347,
|
||||
"widget": {
|
||||
"name": "directory_path",
|
||||
"config": [
|
||||
"STRING",
|
||||
{
|
||||
"default": "",
|
||||
"multiline": false
|
||||
}
|
||||
]
|
||||
"name": "directory_path"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -1562,14 +1499,7 @@
|
||||
"type": "INT",
|
||||
"link": 5350,
|
||||
"widget": {
|
||||
"name": "frames_per_second",
|
||||
"config": [
|
||||
"INT",
|
||||
{
|
||||
"min": 1,
|
||||
"default": 25
|
||||
}
|
||||
]
|
||||
"name": "frames_per_second"
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -1638,47 +1568,6 @@
|
||||
"color": "#432",
|
||||
"bgcolor": "#653"
|
||||
},
|
||||
{
|
||||
"id": 236,
|
||||
"type": "LoadImage",
|
||||
"pos": [
|
||||
-2106,
|
||||
679
|
||||
],
|
||||
"size": {
|
||||
"0": 430,
|
||||
"1": 340
|
||||
},
|
||||
"flags": {},
|
||||
"order": 12,
|
||||
"mode": 0,
|
||||
"outputs": [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
5313
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
},
|
||||
{
|
||||
"name": "MASK",
|
||||
"type": "MASK",
|
||||
"links": null,
|
||||
"shape": 3
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "LoadImage"
|
||||
},
|
||||
"widgets_values": [
|
||||
"after (3).jpg",
|
||||
"image"
|
||||
],
|
||||
"color": "#2a363b",
|
||||
"bgcolor": "#3f5159"
|
||||
},
|
||||
{
|
||||
"id": 373,
|
||||
"type": "Reroute",
|
||||
@@ -1691,7 +1580,7 @@
|
||||
26
|
||||
],
|
||||
"flags": {},
|
||||
"order": 25,
|
||||
"order": 24,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
@@ -1878,7 +1767,7 @@
|
||||
"1": 82
|
||||
},
|
||||
"flags": {},
|
||||
"order": 13,
|
||||
"order": 12,
|
||||
"mode": 0,
|
||||
"outputs": [
|
||||
{
|
||||
@@ -1892,16 +1781,7 @@
|
||||
],
|
||||
"slot_index": 0,
|
||||
"widget": {
|
||||
"name": "alignment_factor",
|
||||
"config": [
|
||||
"FLOAT",
|
||||
{
|
||||
"default": 0.5,
|
||||
"min": 0,
|
||||
"max": 10,
|
||||
"step": 0.1
|
||||
}
|
||||
]
|
||||
"name": "alignment_factor"
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -1965,7 +1845,7 @@
|
||||
"1": 130
|
||||
},
|
||||
"flags": {},
|
||||
"order": 48,
|
||||
"order": 53,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
@@ -1990,7 +1870,7 @@
|
||||
},
|
||||
"widgets_values": [
|
||||
256,
|
||||
1019724837772866,
|
||||
919440470904266,
|
||||
"randomize",
|
||||
"center-left"
|
||||
],
|
||||
@@ -2034,7 +1914,7 @@
|
||||
},
|
||||
"widgets_values": [
|
||||
256,
|
||||
1014748475294523,
|
||||
528123820881935,
|
||||
"randomize",
|
||||
"center-left"
|
||||
],
|
||||
@@ -2055,7 +1935,7 @@
|
||||
"flags": {
|
||||
"collapsed": true
|
||||
},
|
||||
"order": 34,
|
||||
"order": 32,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
@@ -2068,13 +1948,7 @@
|
||||
"type": "STRING",
|
||||
"link": 2312,
|
||||
"widget": {
|
||||
"name": "text",
|
||||
"config": [
|
||||
"STRING",
|
||||
{
|
||||
"multiline": true
|
||||
}
|
||||
]
|
||||
"name": "text"
|
||||
},
|
||||
"slot_index": 1
|
||||
}
|
||||
@@ -2112,7 +1986,7 @@
|
||||
"flags": {
|
||||
"collapsed": true
|
||||
},
|
||||
"order": 33,
|
||||
"order": 31,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
@@ -2125,13 +1999,7 @@
|
||||
"type": "STRING",
|
||||
"link": 2309,
|
||||
"widget": {
|
||||
"name": "text",
|
||||
"config": [
|
||||
"STRING",
|
||||
{
|
||||
"multiline": true
|
||||
}
|
||||
]
|
||||
"name": "text"
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -2166,7 +2034,7 @@
|
||||
26
|
||||
],
|
||||
"flags": {},
|
||||
"order": 41,
|
||||
"order": 40,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
@@ -2316,7 +2184,7 @@
|
||||
26
|
||||
],
|
||||
"flags": {},
|
||||
"order": 42,
|
||||
"order": 41,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
@@ -2353,7 +2221,7 @@
|
||||
26
|
||||
],
|
||||
"flags": {},
|
||||
"order": 23,
|
||||
"order": 22,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
@@ -2389,7 +2257,7 @@
|
||||
26
|
||||
],
|
||||
"flags": {},
|
||||
"order": 26,
|
||||
"order": 25,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
@@ -2464,7 +2332,7 @@
|
||||
"Node name for S&R": "KSampler"
|
||||
},
|
||||
"widgets_values": [
|
||||
55143763493153,
|
||||
700759510514483,
|
||||
"randomize",
|
||||
5,
|
||||
5,
|
||||
@@ -2523,7 +2391,7 @@
|
||||
"1": 60
|
||||
},
|
||||
"flags": {},
|
||||
"order": 14,
|
||||
"order": 13,
|
||||
"mode": 0,
|
||||
"title": "Note on full frame sampler",
|
||||
"properties": {
|
||||
@@ -2547,7 +2415,7 @@
|
||||
26
|
||||
],
|
||||
"flags": {},
|
||||
"order": 39,
|
||||
"order": 44,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
@@ -2667,7 +2535,7 @@
|
||||
26
|
||||
],
|
||||
"flags": {},
|
||||
"order": 30,
|
||||
"order": 35,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
@@ -2707,7 +2575,7 @@
|
||||
"1": 270
|
||||
},
|
||||
"flags": {},
|
||||
"order": 15,
|
||||
"order": 14,
|
||||
"mode": 0,
|
||||
"title": "Note on noise",
|
||||
"properties": {
|
||||
@@ -2780,16 +2648,7 @@
|
||||
"type": "INT",
|
||||
"link": 5074,
|
||||
"widget": {
|
||||
"name": "width",
|
||||
"config": [
|
||||
"INT",
|
||||
{
|
||||
"default": 512,
|
||||
"min": 1,
|
||||
"max": 8192,
|
||||
"step": 1
|
||||
}
|
||||
]
|
||||
"name": "width"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -2797,16 +2656,7 @@
|
||||
"type": "INT",
|
||||
"link": 5075,
|
||||
"widget": {
|
||||
"name": "height",
|
||||
"config": [
|
||||
"INT",
|
||||
{
|
||||
"default": 512,
|
||||
"min": 1,
|
||||
"max": 8192,
|
||||
"step": 1
|
||||
}
|
||||
]
|
||||
"name": "height"
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -2844,7 +2694,7 @@
|
||||
"1": 130
|
||||
},
|
||||
"flags": {},
|
||||
"order": 47,
|
||||
"order": 52,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
@@ -2869,7 +2719,7 @@
|
||||
},
|
||||
"widgets_values": [
|
||||
256,
|
||||
309821851176166,
|
||||
502577689536295,
|
||||
"randomize",
|
||||
"top-center"
|
||||
],
|
||||
@@ -2913,7 +2763,7 @@
|
||||
},
|
||||
"widgets_values": [
|
||||
256,
|
||||
973129878717336,
|
||||
1125637252645999,
|
||||
"randomize",
|
||||
"top-center"
|
||||
],
|
||||
@@ -2934,7 +2784,7 @@
|
||||
"flags": {
|
||||
"collapsed": false
|
||||
},
|
||||
"order": 16,
|
||||
"order": 15,
|
||||
"mode": 0,
|
||||
"outputs": [
|
||||
{
|
||||
@@ -3007,7 +2857,7 @@
|
||||
26
|
||||
],
|
||||
"flags": {},
|
||||
"order": 55,
|
||||
"order": 51,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
@@ -3083,7 +2933,7 @@
|
||||
26
|
||||
],
|
||||
"flags": {},
|
||||
"order": 44,
|
||||
"order": 43,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
@@ -3119,7 +2969,7 @@
|
||||
26
|
||||
],
|
||||
"flags": {},
|
||||
"order": 35,
|
||||
"order": 33,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
@@ -3156,7 +3006,7 @@
|
||||
26
|
||||
],
|
||||
"flags": {},
|
||||
"order": 53,
|
||||
"order": 49,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
@@ -3196,7 +3046,7 @@
|
||||
26
|
||||
],
|
||||
"flags": {},
|
||||
"order": 52,
|
||||
"order": 48,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
@@ -3335,16 +3185,7 @@
|
||||
"type": "FLOAT",
|
||||
"link": 5222,
|
||||
"widget": {
|
||||
"name": "x_translation",
|
||||
"config": [
|
||||
"FLOAT",
|
||||
{
|
||||
"default": 0,
|
||||
"min": -10,
|
||||
"max": 10,
|
||||
"step": 0.01
|
||||
}
|
||||
]
|
||||
"name": "x_translation"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -3352,16 +3193,7 @@
|
||||
"type": "FLOAT",
|
||||
"link": 5223,
|
||||
"widget": {
|
||||
"name": "y_translation",
|
||||
"config": [
|
||||
"FLOAT",
|
||||
{
|
||||
"default": 0,
|
||||
"min": -10,
|
||||
"max": 10,
|
||||
"step": 0.01
|
||||
}
|
||||
]
|
||||
"name": "y_translation"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -3369,16 +3201,7 @@
|
||||
"type": "FLOAT",
|
||||
"link": 5225,
|
||||
"widget": {
|
||||
"name": "zoom",
|
||||
"config": [
|
||||
"FLOAT",
|
||||
{
|
||||
"default": 0,
|
||||
"min": -10,
|
||||
"max": 10,
|
||||
"step": 0.01
|
||||
}
|
||||
]
|
||||
"name": "zoom"
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -3580,14 +3403,7 @@
|
||||
"type": "STRING",
|
||||
"link": 5442,
|
||||
"widget": {
|
||||
"name": "directory_path",
|
||||
"config": [
|
||||
"STRING",
|
||||
{
|
||||
"default": "I:\\AI\\ComfyUI\\ComfyUI\\output",
|
||||
"multiline": false
|
||||
}
|
||||
]
|
||||
"name": "directory_path"
|
||||
},
|
||||
"slot_index": 2
|
||||
}
|
||||
@@ -3601,6 +3417,12 @@
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
},
|
||||
{
|
||||
"name": "log_entry",
|
||||
"type": "LOG_ENTRY",
|
||||
"links": null,
|
||||
"shape": 3
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
@@ -3664,7 +3486,7 @@
|
||||
26
|
||||
],
|
||||
"flags": {},
|
||||
"order": 51,
|
||||
"order": 47,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
@@ -3700,7 +3522,7 @@
|
||||
26
|
||||
],
|
||||
"flags": {},
|
||||
"order": 40,
|
||||
"order": 39,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
@@ -3737,7 +3559,7 @@
|
||||
26
|
||||
],
|
||||
"flags": {},
|
||||
"order": 32,
|
||||
"order": 30,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
@@ -3773,7 +3595,7 @@
|
||||
26
|
||||
],
|
||||
"flags": {},
|
||||
"order": 43,
|
||||
"order": 42,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
@@ -3809,7 +3631,7 @@
|
||||
26
|
||||
],
|
||||
"flags": {},
|
||||
"order": 54,
|
||||
"order": 50,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
@@ -4079,14 +3901,7 @@
|
||||
"type": "STRING",
|
||||
"link": 5348,
|
||||
"widget": {
|
||||
"name": "directory_path",
|
||||
"config": [
|
||||
"STRING",
|
||||
{
|
||||
"default": "",
|
||||
"multiline": false
|
||||
}
|
||||
]
|
||||
"name": "directory_path"
|
||||
},
|
||||
"slot_index": 2
|
||||
}
|
||||
@@ -4100,6 +3915,12 @@
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
},
|
||||
{
|
||||
"name": "frame_name",
|
||||
"type": "STRING",
|
||||
"links": null,
|
||||
"shape": 3
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
@@ -4123,7 +3944,7 @@
|
||||
26
|
||||
],
|
||||
"flags": {},
|
||||
"order": 31,
|
||||
"order": 36,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
@@ -4306,7 +4127,7 @@
|
||||
256,
|
||||
0.11818237304687501,
|
||||
0.5,
|
||||
340337213218643,
|
||||
855083604429355,
|
||||
"randomize"
|
||||
],
|
||||
"color": "#2a363b",
|
||||
@@ -4443,7 +4264,7 @@
|
||||
"flags": {
|
||||
"collapsed": false
|
||||
},
|
||||
"order": 17,
|
||||
"order": 16,
|
||||
"mode": 0,
|
||||
"inputs": [],
|
||||
"outputs": [
|
||||
@@ -4493,7 +4314,7 @@
|
||||
26
|
||||
],
|
||||
"flags": {},
|
||||
"order": 27,
|
||||
"order": 26,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
@@ -4529,7 +4350,7 @@
|
||||
26
|
||||
],
|
||||
"flags": {},
|
||||
"order": 22,
|
||||
"order": 27,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
@@ -4607,7 +4428,7 @@
|
||||
"1": 90
|
||||
},
|
||||
"flags": {},
|
||||
"order": 18,
|
||||
"order": 17,
|
||||
"mode": 0,
|
||||
"title": "Note on motion",
|
||||
"properties": {
|
||||
@@ -4705,7 +4526,7 @@
|
||||
26
|
||||
],
|
||||
"flags": {},
|
||||
"order": 36,
|
||||
"order": 34,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
@@ -5031,7 +4852,7 @@
|
||||
"name": "sequence",
|
||||
"type": "ANIMATION_SEQUENCE",
|
||||
"links": [
|
||||
5462
|
||||
5463
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
@@ -5047,11 +4868,11 @@
|
||||
"bgcolor": "#593930"
|
||||
},
|
||||
{
|
||||
"id": 421,
|
||||
"type": "Video Encoder (mpegCoder) [Dream]",
|
||||
"id": 422,
|
||||
"type": "FFMPEG Video Encoder [Dream]",
|
||||
"pos": [
|
||||
4778,
|
||||
559
|
||||
4781,
|
||||
562
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
@@ -5064,17 +4885,66 @@
|
||||
{
|
||||
"name": "sequence",
|
||||
"type": "ANIMATION_SEQUENCE",
|
||||
"link": 5462
|
||||
"link": 5463
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "log_entry",
|
||||
"type": "LOG_ENTRY",
|
||||
"links": null,
|
||||
"shape": 3
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "Video Encoder (mpegCoder) [Dream]"
|
||||
"Node name for S&R": "FFMPEG Video Encoder [Dream]"
|
||||
},
|
||||
"widgets_values": [
|
||||
"video",
|
||||
1,
|
||||
true
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 236,
|
||||
"type": "LoadImage",
|
||||
"pos": [
|
||||
-2106,
|
||||
679
|
||||
],
|
||||
"size": {
|
||||
"0": 430,
|
||||
"1": 340
|
||||
},
|
||||
"flags": {},
|
||||
"order": 18,
|
||||
"mode": 0,
|
||||
"outputs": [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
5313
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
},
|
||||
{
|
||||
"name": "MASK",
|
||||
"type": "MASK",
|
||||
"links": null,
|
||||
"shape": 3
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "LoadImage"
|
||||
},
|
||||
"widgets_values": [
|
||||
"7159726-HSC00002-7 (1).jpg",
|
||||
"image"
|
||||
],
|
||||
"color": "#2a363b",
|
||||
"bgcolor": "#3f5159"
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
@@ -6223,10 +6093,10 @@
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
5462,
|
||||
5463,
|
||||
281,
|
||||
0,
|
||||
421,
|
||||
422,
|
||||
0,
|
||||
"ANIMATION_SEQUENCE"
|
||||
]
|
||||
@@ -6240,7 +6110,8 @@
|
||||
474,
|
||||
885
|
||||
],
|
||||
"color": "#a1309b"
|
||||
"color": "#a1309b",
|
||||
"font_size": 24
|
||||
},
|
||||
{
|
||||
"title": "Settings",
|
||||
@@ -6250,7 +6121,8 @@
|
||||
482,
|
||||
742
|
||||
],
|
||||
"color": "#b58b2a"
|
||||
"color": "#b58b2a",
|
||||
"font_size": 24
|
||||
},
|
||||
{
|
||||
"title": "Inpainting/Outpainting",
|
||||
@@ -6260,7 +6132,8 @@
|
||||
1711,
|
||||
528
|
||||
],
|
||||
"color": "#929054"
|
||||
"color": "#929054",
|
||||
"font_size": 24
|
||||
},
|
||||
{
|
||||
"title": "Prev Frame Move",
|
||||
@@ -6270,7 +6143,8 @@
|
||||
1450,
|
||||
456
|
||||
],
|
||||
"color": "#3f789e"
|
||||
"color": "#3f789e",
|
||||
"font_size": 24
|
||||
},
|
||||
{
|
||||
"title": "Full frame sampler",
|
||||
@@ -6280,7 +6154,8 @@
|
||||
691,
|
||||
700
|
||||
],
|
||||
"color": "#88A"
|
||||
"color": "#88A",
|
||||
"font_size": 24
|
||||
},
|
||||
{
|
||||
"title": "Output",
|
||||
@@ -6290,7 +6165,8 @@
|
||||
1458,
|
||||
381
|
||||
],
|
||||
"color": "#b06634"
|
||||
"color": "#b06634",
|
||||
"font_size": 24
|
||||
},
|
||||
{
|
||||
"title": "Animation Driver",
|
||||
@@ -6300,7 +6176,8 @@
|
||||
336,
|
||||
373
|
||||
],
|
||||
"color": "#3f789e"
|
||||
"color": "#3f789e",
|
||||
"font_size": 24
|
||||
},
|
||||
{
|
||||
"title": "Motion Control",
|
||||
@@ -6310,7 +6187,8 @@
|
||||
1451,
|
||||
392
|
||||
],
|
||||
"color": "#ef75ff"
|
||||
"color": "#ef75ff",
|
||||
"font_size": 24
|
||||
},
|
||||
{
|
||||
"title": "Model selection",
|
||||
@@ -6320,7 +6198,8 @@
|
||||
476,
|
||||
204
|
||||
],
|
||||
"color": "#3f789e"
|
||||
"color": "#3f789e",
|
||||
"font_size": 24
|
||||
},
|
||||
{
|
||||
"title": "Noise",
|
||||
@@ -6330,7 +6209,8 @@
|
||||
1840,
|
||||
765
|
||||
],
|
||||
"color": "#3f789e"
|
||||
"color": "#3f789e",
|
||||
"font_size": 24
|
||||
},
|
||||
{
|
||||
"title": "Previews",
|
||||
@@ -6340,7 +6220,8 @@
|
||||
6473,
|
||||
329
|
||||
],
|
||||
"color": "#444"
|
||||
"color": "#444",
|
||||
"font_size": 24
|
||||
}
|
||||
],
|
||||
"config": {},
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+5
-11
@@ -4,13 +4,11 @@ import math
|
||||
import numpy
|
||||
import torch
|
||||
from PIL import Image, ImageDraw
|
||||
from PIL.Image import Resampling
|
||||
|
||||
from .categories import *
|
||||
from .shared import ALWAYS_CHANGED_FLAG, convertTensorImageToPIL, DreamImageProcessor, \
|
||||
from .shared import convertTensorImageToPIL, DreamImageProcessor, \
|
||||
DreamImage, DreamMask
|
||||
from .types import SharedTypes, FrameCounter
|
||||
|
||||
from .dreamtypes import SharedTypes, FrameCounter
|
||||
|
||||
class DreamImageMotion:
|
||||
NODE_NAME = "Image Motion"
|
||||
@@ -42,10 +40,6 @@ class DreamImageMotion:
|
||||
RETURN_NAMES = ("image", "mask1", "mask2", "mask3")
|
||||
FUNCTION = "result"
|
||||
|
||||
@classmethod
|
||||
def IS_CHANGED(cls, *values):
|
||||
return ALWAYS_CHANGED_FLAG
|
||||
|
||||
def _mk_PIL_image(self, size, color=None, mode="RGB") -> Image:
|
||||
im = Image.new(mode=mode, size=size)
|
||||
if color:
|
||||
@@ -77,7 +71,7 @@ class DreamImageMotion:
|
||||
return min(max(i, 1), 32767)
|
||||
|
||||
if output_resize_height and output_resize_width:
|
||||
return lambda img: img.resize((bound(output_resize_width), bound(output_resize_height)), Resampling.NEAREST)
|
||||
return lambda img: img.resize((bound(output_resize_width), bound(output_resize_height)))
|
||||
else:
|
||||
return lambda img: img
|
||||
|
||||
@@ -98,12 +92,12 @@ class DreamImageMotion:
|
||||
noise = other.get("noise", None)
|
||||
multiplier = math.pow(2, zoom)
|
||||
resized_image = pil_image.resize((round(pil_image.width * multiplier),
|
||||
round(pil_image.height * multiplier)), Resampling.BILINEAR)
|
||||
round(pil_image.height * multiplier)))
|
||||
|
||||
if noise is None:
|
||||
base_image = self._mk_PIL_image(pil_image.size, "black")
|
||||
else:
|
||||
base_image = convertTensorImageToPIL(noise).resize(pil_image.size, Resampling.BILINEAR)
|
||||
base_image = convertTensorImageToPIL(noise).resize(pil_image.size)
|
||||
|
||||
selection_offset = (round(x_translation * pil_image.width), round(y_translation * pil_image.height))
|
||||
selection = ((pil_image.width - resized_image.width) // 2 + selection_offset[0],
|
||||
|
||||
@@ -18,10 +18,6 @@ class DreamInputText:
|
||||
RETURN_NAMES = ("STRING",)
|
||||
FUNCTION = "noop"
|
||||
|
||||
@classmethod
|
||||
def IS_CHANGED(cls, *values):
|
||||
return hashed_as_strings(*values)
|
||||
|
||||
def noop(self, value):
|
||||
return (value,)
|
||||
|
||||
@@ -42,10 +38,6 @@ class DreamInputString:
|
||||
RETURN_NAMES = ("STRING",)
|
||||
FUNCTION = "noop"
|
||||
|
||||
@classmethod
|
||||
def IS_CHANGED(cls, *values):
|
||||
return hashed_as_strings(*values)
|
||||
|
||||
def noop(self, value):
|
||||
return (value,)
|
||||
|
||||
@@ -67,10 +59,6 @@ class DreamInputFloat:
|
||||
RETURN_NAMES = ("FLOAT",)
|
||||
FUNCTION = "noop"
|
||||
|
||||
@classmethod
|
||||
def IS_CHANGED(cls, *values):
|
||||
return hashed_as_strings(*values)
|
||||
|
||||
def noop(self, value):
|
||||
return (value,)
|
||||
|
||||
@@ -92,9 +80,5 @@ class DreamInputInt:
|
||||
RETURN_NAMES = ("INT",)
|
||||
FUNCTION = "noop"
|
||||
|
||||
@classmethod
|
||||
def IS_CHANGED(cls, *values):
|
||||
return hashed_as_strings(*values)
|
||||
|
||||
def noop(self, value):
|
||||
return (value,)
|
||||
|
||||
+7
-3
@@ -1,10 +1,14 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from .shared import DreamConfig
|
||||
import sys, os
|
||||
|
||||
#sys.path.append(str(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
#from . import shared
|
||||
|
||||
|
||||
def setup_default_config():
|
||||
DreamConfig()
|
||||
|
||||
#shared.DreamConfig()
|
||||
pass
|
||||
|
||||
def run_install():
|
||||
setup_default_config()
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import json
|
||||
|
||||
from .categories import *
|
||||
from .shared import DreamStateFile
|
||||
from .dreamtypes import *
|
||||
|
||||
_laboratory_state = DreamStateFile("laboratory")
|
||||
|
||||
|
||||
class DreamLaboratory:
|
||||
NODE_NAME = "Laboratory"
|
||||
ICON = "🧪"
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": SharedTypes.frame_counter | {
|
||||
"key": ("STRING", {"default": "Random value " + str(random.randint(0, 1000000))}),
|
||||
"seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}),
|
||||
"renew_policy": (["every frame", "first frame"],),
|
||||
"min_value": ("FLOAT", {"default": 0.0}),
|
||||
"max_value": ("FLOAT", {"default": 1.0}),
|
||||
"mode": (["random uniform", "random bell", "ladder", "random walk"],),
|
||||
},
|
||||
"optional": {
|
||||
"step_size": ("FLOAT", {"default": 0.1}),
|
||||
},
|
||||
}
|
||||
|
||||
CATEGORY = NodeCategories.UTILS
|
||||
RETURN_TYPES = ("FLOAT", "INT", LogEntry.ID)
|
||||
RETURN_NAMES = ("FLOAT", "INT", "log_entry")
|
||||
FUNCTION = "result"
|
||||
|
||||
def _generate(self, seed, last_value, min_value, max_value, mode, step_size):
|
||||
rnd = random.Random()
|
||||
rnd.seed(seed)
|
||||
|
||||
def jsonify(v: float):
|
||||
return json.loads(json.dumps(v))
|
||||
|
||||
if mode == "random uniform":
|
||||
return jsonify(self._mode_uniform(rnd, last_value, min_value, max_value, step_size))
|
||||
elif mode == "random bell":
|
||||
return jsonify(self._mode_bell(rnd, last_value, min_value, max_value, step_size))
|
||||
elif mode == "ladder":
|
||||
return jsonify(self._mode_ladder(rnd, last_value, min_value, max_value, step_size))
|
||||
else:
|
||||
return jsonify(self._mode_walk(rnd, last_value, min_value, max_value, step_size))
|
||||
|
||||
def _mode_uniform(self, rnd: random.Random, last_value: float, min_value: float, max_value: float, step_size):
|
||||
return rnd.random() * (max_value - min_value) + min_value
|
||||
|
||||
def _mode_bell(self, rnd: random.Random, last_value: float, min_value: float, max_value: float, step_size):
|
||||
s = 0.0
|
||||
for i in range(3):
|
||||
s += rnd.random() * (max_value - min_value) + min_value
|
||||
return s / 3.0
|
||||
|
||||
def _mode_ladder(self, rnd: random.Random, last_value: float, min_value: float, max_value: float, step_size):
|
||||
if last_value is None:
|
||||
last_value = min_value - step_size
|
||||
next_value = last_value + step_size
|
||||
if next_value > max_value:
|
||||
d = abs(max_value - min_value)
|
||||
next_value = (next_value - min_value) % d + min_value
|
||||
return next_value
|
||||
|
||||
def _mode_walk(self, rnd: random.Random, last_value: float, min_value: float, max_value: float, step_size):
|
||||
if last_value is None:
|
||||
last_value = (max_value - min_value) * 0.5
|
||||
if rnd.random() >= 0.5:
|
||||
return min(max_value, last_value + step_size)
|
||||
else:
|
||||
return max(min_value, last_value - step_size)
|
||||
|
||||
def result(self, key, frame_counter: FrameCounter, seed, renew_policy, min_value, max_value, mode, **values):
|
||||
if min_value > max_value:
|
||||
t = max_value
|
||||
max_value = min_value
|
||||
min_value = t
|
||||
step_size = values.get("step_size", abs(max_value - min_value) * 0.1)
|
||||
last_value = _laboratory_state.get_section("values").get(key, None)
|
||||
|
||||
if (last_value is None) or (renew_policy == "every frame") or frame_counter.is_first_frame:
|
||||
v = _laboratory_state.get_section("values") \
|
||||
.update(key, 0, lambda old: self._generate(seed, last_value, min_value, max_value, mode, step_size))
|
||||
return v, round(v), LogEntry.new(
|
||||
"Laboratory generated new value for '{}': {} ({})".format(key, v, round(v)))
|
||||
else:
|
||||
return last_value, round(last_value), LogEntry.new("Laboratory reused value for '{}': {} ({})"
|
||||
.format(key, last_value, round(last_value)))
|
||||
+151
@@ -0,0 +1,151 @@
|
||||
from .categories import NodeCategories
|
||||
from .dreamtypes import RGBPalette
|
||||
|
||||
_NOT_A_VALUE_I = 9223372036854775807
|
||||
_NOT_A_VALUE_F = float(_NOT_A_VALUE_I)
|
||||
_NOT_A_VALUE_S = "⭆"
|
||||
|
||||
def _generate_switch_input(type_nm: str, default_value=None):
|
||||
d = dict()
|
||||
for i in range(10):
|
||||
if default_value is None:
|
||||
d["input_" + str(i)] = (type_nm, {"lazy": True})
|
||||
else:
|
||||
d["input_" + str(i)] = (type_nm, {"default": default_value, "forceInput": True, "lazy": True})
|
||||
|
||||
return {
|
||||
"required": {
|
||||
"select": ("INT", {"default": 0, "min": 0, "max": 9})
|
||||
},
|
||||
"optional": d
|
||||
}
|
||||
|
||||
|
||||
def _check_big_switch_lazy_status(*args, **kwargs):
|
||||
n = int(kwargs['select'])
|
||||
input_name = f"input_{n}"
|
||||
print(f"SELECTED: {input_name}")
|
||||
if input_name in kwargs:
|
||||
return [input_name]
|
||||
else:
|
||||
return []
|
||||
|
||||
|
||||
class DreamLazyImageSwitch:
|
||||
_switch_type = "IMAGE"
|
||||
NODE_NAME = "Lazy 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)
|
||||
|
||||
def check_lazy_status(self, *args, **kwargs):
|
||||
return _check_big_switch_lazy_status(*args, **kwargs)
|
||||
|
||||
def pick(self, select, **args):
|
||||
return (args.get("input_"+str(select), None),)
|
||||
|
||||
|
||||
class DreamLazyLatentSwitch:
|
||||
_switch_type = "LATENT"
|
||||
NODE_NAME = "Lazy 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)
|
||||
|
||||
def check_lazy_status(self, *args, **kwargs):
|
||||
return _check_big_switch_lazy_status(*args, **kwargs)
|
||||
|
||||
def pick(self, select, **args):
|
||||
return (args.get("input_" + str(select), None),)
|
||||
|
||||
|
||||
class DreamLazyTextSwitch:
|
||||
_switch_type = "STRING"
|
||||
NODE_NAME = "Lazy 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, _NOT_A_VALUE_S)
|
||||
|
||||
def check_lazy_status(self, *args, **kwargs):
|
||||
return _check_big_switch_lazy_status(*args, **kwargs)
|
||||
|
||||
def pick(self, select, **args):
|
||||
return (args.get("input_" + str(select), None),)
|
||||
|
||||
|
||||
class DreamLazyPaletteSwitch:
|
||||
_switch_type = RGBPalette.ID
|
||||
NODE_NAME = "Lazy 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)
|
||||
|
||||
def check_lazy_status(self, *args, **kwargs):
|
||||
return _check_big_switch_lazy_status(*args, **kwargs)
|
||||
|
||||
def pick(self, select, **args):
|
||||
return (args.get("input_" + str(select), None),)
|
||||
|
||||
|
||||
class DreamLazyFloatSwitch:
|
||||
_switch_type = "FLOAT"
|
||||
NODE_NAME = "Lazy 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, _NOT_A_VALUE_F)
|
||||
|
||||
def check_lazy_status(self, *args, **kwargs):
|
||||
return _check_big_switch_lazy_status(*args, **kwargs)
|
||||
|
||||
def pick(self, select, **args):
|
||||
return (args.get("input_" + str(select), None),)
|
||||
|
||||
|
||||
class DreamLazyIntSwitch:
|
||||
_switch_type = "INT"
|
||||
NODE_NAME = "Lazy 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, _NOT_A_VALUE_I)
|
||||
|
||||
def check_lazy_status(self, *args, **kwargs):
|
||||
return _check_big_switch_lazy_status(*args, **kwargs)
|
||||
|
||||
def pick(self, select, **args):
|
||||
return (args.get("input_" + str(select), None),)
|
||||
+9
-7
@@ -1,7 +1,8 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from .categories import NodeCategories
|
||||
from .shared import ALWAYS_CHANGED_FLAG, list_images_in_directory, DreamImage
|
||||
from .types import SharedTypes, FrameCounter
|
||||
from .shared import list_images_in_directory, DreamImage
|
||||
from .dreamtypes import SharedTypes, FrameCounter
|
||||
import os
|
||||
|
||||
|
||||
class DreamImageSequenceInputWithDefaultFallback:
|
||||
@@ -22,13 +23,13 @@ class DreamImageSequenceInputWithDefaultFallback:
|
||||
}
|
||||
|
||||
CATEGORY = NodeCategories.IMAGE_ANIMATION
|
||||
RETURN_TYPES = ("IMAGE",)
|
||||
RETURN_NAMES = ("image",)
|
||||
RETURN_TYPES = ("IMAGE","STRING")
|
||||
RETURN_NAMES = ("image","frame_name")
|
||||
FUNCTION = "result"
|
||||
|
||||
@classmethod
|
||||
def IS_CHANGED(cls, *values):
|
||||
return ALWAYS_CHANGED_FLAG
|
||||
def IS_CHANGED(cls, *values, **kwargs):
|
||||
return float("NaN")
|
||||
|
||||
def result(self, frame_counter: FrameCounter, directory_path, pattern, indexing, **other):
|
||||
default_image = other.get("default_image", None)
|
||||
@@ -37,5 +38,6 @@ class DreamImageSequenceInputWithDefaultFallback:
|
||||
if not entry:
|
||||
return (default_image, "")
|
||||
else:
|
||||
image_names = [os.path.basename(file_path) for file_path in entry]
|
||||
images = map(lambda f: DreamImage(file_path=f), entry)
|
||||
return (DreamImage.join_to_tensor_data(images),)
|
||||
return (DreamImage.join_to_tensor_data(images), image_names[0])
|
||||
|
||||
+16
-3
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"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",
|
||||
@@ -14,34 +14,47 @@
|
||||
"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)",
|
||||
"Float to Log Entry [Dream]": "Logging for float values",
|
||||
"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",
|
||||
"Frame Counter Time Offset [Dream]": "Adds an offset to a frame counter in seconds",
|
||||
"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",
|
||||
"Image Sequence Saver [Dream]": "Saves a frame to a directory",
|
||||
"Image Sequence Tweening [Dream]": "Post processing for animation sequences generating blended in-between frames",
|
||||
"Int Input [Dream]": "Integer input (until primitive routing issues are solved)",
|
||||
"Int to Log Entry [Dream]": "Logging for int values",
|
||||
"Laboratory [Dream]": "Super-charged number generator for experimenting with ComfyUI",
|
||||
"Linear Curve [Dream]": "Linear interpolation between two value over the full animation",
|
||||
"Log Entry Joiner [Dream]": "Merges multiple log entries (reduces noodling)",
|
||||
"Log File [Dream]": "Logging node for output to file",
|
||||
"Noise from Area Palettes [Dream]": "Generates noise based on the colors of up to nine different palettes",
|
||||
"Noise from Palette [Dream]": "Generates noise based on the colors in a palette",
|
||||
"Palette Color Align [Dream]": "Shifts the colors of one palette towards another target palette",
|
||||
"Palette Color Shift [Dream]": "Multiplies the color values in a palette",
|
||||
"Random Prompt Words [Dream]": "Picks random words from input",
|
||||
"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)",
|
||||
"String Tokenizer [Dream]": "Extract individual words or phrases from a text as tokens",
|
||||
"String to Log Entry [Dream]": "Use any string as a log entry",
|
||||
"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"
|
||||
} and switches.
|
||||
"WAV Curve [Dream]": "WAV audio file as a curve"
|
||||
}
|
||||
@@ -3,7 +3,7 @@ import math
|
||||
|
||||
from .categories import NodeCategories
|
||||
from .shared import *
|
||||
from .types import *
|
||||
from .dreamtypes import *
|
||||
|
||||
|
||||
def _generate_noise(image: DreamImage, color_function, rng: random.Random, block_size, blur_amount,
|
||||
@@ -44,10 +44,6 @@ class DreamNoiseFromPalette:
|
||||
RETURN_NAMES = ("image",)
|
||||
FUNCTION = "result"
|
||||
|
||||
@classmethod
|
||||
def IS_CHANGED(cls, *values):
|
||||
return ALWAYS_CHANGED_FLAG
|
||||
|
||||
def result(self, palette: Tuple[RGBPalette], width, height, seed, blur_amount, density):
|
||||
outputs = list()
|
||||
rng = random.Random()
|
||||
@@ -95,10 +91,6 @@ class DreamNoiseFromAreaPalettes:
|
||||
RETURN_NAMES = ("image",)
|
||||
FUNCTION = "result"
|
||||
|
||||
@classmethod
|
||||
def IS_CHANGED(cls, *values):
|
||||
return ALWAYS_CHANGED_FLAG
|
||||
|
||||
def _area_coordinates(self, width, height):
|
||||
dx = width / 6
|
||||
dy = height / 6
|
||||
|
||||
@@ -6,9 +6,9 @@ import folder_paths as comfy_paths
|
||||
from PIL.PngImagePlugin import PngInfo
|
||||
|
||||
from .categories import NodeCategories
|
||||
from .shared import hashed_as_strings, DreamImageProcessor, DreamImage, \
|
||||
from .shared import DreamImageProcessor, DreamImage, \
|
||||
list_images_in_directory, DreamConfig
|
||||
from .types import SharedTypes, FrameCounter, AnimationSequence
|
||||
from .dreamtypes import SharedTypes, FrameCounter, AnimationSequence, LogEntry
|
||||
|
||||
CONFIG = DreamConfig()
|
||||
|
||||
@@ -42,7 +42,7 @@ class DreamImageSequenceOutput:
|
||||
"directory_path": ("STRING", {"default": comfy_paths.output_directory, "multiline": False}),
|
||||
"prefix": ("STRING", {"default": 'frame', "multiline": False}),
|
||||
"digits": ("INT", {"default": 5}),
|
||||
"at_end": (["stop output", "keep going"],),
|
||||
"at_end": (["stop output", "raise error", "keep going"],),
|
||||
"filetype": (['png with embedded workflow', "png", 'jpg'],),
|
||||
},
|
||||
"hidden": {
|
||||
@@ -52,23 +52,24 @@ class DreamImageSequenceOutput:
|
||||
}
|
||||
|
||||
CATEGORY = NodeCategories.IMAGE_ANIMATION
|
||||
RETURN_TYPES = (AnimationSequence.ID,)
|
||||
RETURN_TYPES = (AnimationSequence.ID, LogEntry.ID)
|
||||
OUTPUT_NODE = True
|
||||
RETURN_NAMES = ("sequence",)
|
||||
RETURN_NAMES = ("sequence", "log_entry")
|
||||
FUNCTION = "save"
|
||||
|
||||
@classmethod
|
||||
def IS_CHANGED(cls, *values):
|
||||
return hashed_as_strings(*values)
|
||||
|
||||
def _get_new_filename(self, current_frame, prefix, digits, filetype):
|
||||
return prefix + "_" + str(current_frame).zfill(digits) + "." + filetype.split(" ")[0]
|
||||
|
||||
def _save_single_image(self, dream_image: DreamImage, batch_counter, frame_counter: FrameCounter, directory_path,
|
||||
prefix, digits, filetype, prompt, extra_pnginfo, at_end):
|
||||
def _save_single_image(self, dream_image: DreamImage, batch_counter, frame_counter: FrameCounter,
|
||||
directory_path,
|
||||
prefix, digits, filetype, prompt, extra_pnginfo, at_end, logger):
|
||||
|
||||
if at_end == "stop output" and frame_counter.is_after_last_frame:
|
||||
print("Reached end of animation - not saving output!")
|
||||
logger("Reached end of animation - not saving output!")
|
||||
return ()
|
||||
if at_end == "raise error" and frame_counter.is_after_last_frame:
|
||||
logger("Reached end of animation - raising error to stop processing!")
|
||||
raise Exception("Reached end of animation!")
|
||||
filename = self._get_new_filename(frame_counter.current_frame, prefix, digits, filetype)
|
||||
if batch_counter >= 0:
|
||||
filepath = os.path.join(directory_path, "batch_" + (str(batch_counter).zfill(4)), filename)
|
||||
@@ -81,7 +82,7 @@ class DreamImageSequenceOutput:
|
||||
dream_image.save_png(filepath, filetype == 'png with embedded workflow', prompt, extra_pnginfo)
|
||||
elif filetype == "jpg":
|
||||
dream_image.save_jpg(filepath, int(CONFIG.get("encoding.jpeg_quality", 95)))
|
||||
print("Saved {} in {}".format(filename, os.path.abspath(save_dir)))
|
||||
logger("Saved {} in {}".format(filename, os.path.abspath(save_dir)))
|
||||
return ()
|
||||
|
||||
def _generate_animation_sequence(self, filetype, directory_path, frame_counter):
|
||||
@@ -93,13 +94,19 @@ class DreamImageSequenceOutput:
|
||||
return AnimationSequence(frame_counter, frames)
|
||||
|
||||
def save(self, image, **args):
|
||||
log_texts = list()
|
||||
logger = lambda s: log_texts.append(s)
|
||||
if not args.get("directory_path", ""):
|
||||
args["directory_path"] = comfy_paths.output_directory
|
||||
args["logger"] = logger
|
||||
proc = DreamImageProcessor(image, **args)
|
||||
proc.process(self._save_single_image)
|
||||
frame_counter: FrameCounter = args["frame_counter"]
|
||||
frame_counter = args["frame_counter"]
|
||||
log_entry = LogEntry([])
|
||||
for text in log_texts:
|
||||
log_entry = log_entry.add(text)
|
||||
if frame_counter.is_final_frame:
|
||||
return (self._generate_animation_sequence(args["filetype"], args["directory_path"],
|
||||
frame_counter),)
|
||||
frame_counter), log_entry)
|
||||
else:
|
||||
return (AnimationSequence(frame_counter),)
|
||||
return (AnimationSequence(frame_counter), log_entry)
|
||||
|
||||
+40
-9
@@ -1,6 +1,44 @@
|
||||
from .categories import NodeCategories
|
||||
from .shared import hashed_as_strings
|
||||
from .types import PartialPrompt
|
||||
from .dreamtypes import PartialPrompt
|
||||
import random
|
||||
|
||||
class DreamRandomPromptWords:
|
||||
NODE_NAME = "Random Prompt Words"
|
||||
ICON = "⚅"
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"optional": {
|
||||
"partial_prompt": (PartialPrompt.ID,)
|
||||
},
|
||||
"required": {
|
||||
"words": ("STRING", {"default": "", "multiline": True}),
|
||||
"separator": ("STRING", {"default": ",", "multiline": False}),
|
||||
"samples": ("INT", {"default": 1, "min": 1, "max": 100}),
|
||||
"min_weight": ("FLOAT", {"default": 1.0, "min": -10, "max": 10}),
|
||||
"max_weight": ("FLOAT", {"default": 1.0, "min": -10, "max": 10}),
|
||||
"seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}),
|
||||
},
|
||||
}
|
||||
|
||||
CATEGORY = NodeCategories.CONDITIONING
|
||||
RETURN_TYPES = (PartialPrompt.ID,)
|
||||
RETURN_NAMES = ("partial_prompt",)
|
||||
FUNCTION = "result"
|
||||
|
||||
def result(self, words: str, separator, samples, min_weight, max_weight, seed, **args):
|
||||
p = args.get("partial_prompt", PartialPrompt())
|
||||
rnd = random.Random()
|
||||
rnd.seed(seed)
|
||||
words = list(set(map(lambda s: s.strip(), filter(lambda s: s.strip() != "", words.split(separator)))))
|
||||
samples = min(samples, len(words))
|
||||
for i in range(samples):
|
||||
picked_word = words[rnd.randint(0, len(words)-1)]
|
||||
words = list(filter(lambda s: s!=picked_word, words))
|
||||
weight = rnd.uniform(min_weight, max_weight)
|
||||
p = p.add(picked_word, weight)
|
||||
return (p,)
|
||||
|
||||
|
||||
class DreamWeightedPromptBuilder:
|
||||
@@ -24,10 +62,6 @@ class DreamWeightedPromptBuilder:
|
||||
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)
|
||||
@@ -54,9 +88,6 @@ class DreamPromptFinalizer:
|
||||
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():
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
[project]
|
||||
name = "comfyui-dream-project"
|
||||
description = "This extension offers various nodes that are useful for Deforum-like animations in ComfyUI."
|
||||
version = "5.1.2"
|
||||
license = { text = "MIT License" }
|
||||
dependencies = ["imageio", "pilgram", "scipy", "numpy<2.0,>=1.18", "torchvision", "evalidate"]
|
||||
|
||||
[project.urls]
|
||||
Repository = "https://github.com/alt-key-project/comfyui-dream-project"
|
||||
# Used by Comfy Registry https://comfyregistry.org
|
||||
|
||||
[tool.comfy]
|
||||
PublisherId = "altkeyproject"
|
||||
DisplayName = "comfyui-dream-project"
|
||||
Icon = ""
|
||||
@@ -7,6 +7,17 @@ and useful to many ComfyUI users.
|
||||
|
||||
I have demonstrated the use of these custom nodes in this [youtube video](https://youtu.be/pZ6Li3qF-Kk).
|
||||
|
||||
# Notice!
|
||||
|
||||
This custom node pack is currently not being updated. Stable Diffusion video generation is moving towards a different
|
||||
workflow with AnimateDiff and Stable Video Diffusion. I decided to not try to update this node pack, but I am instead
|
||||
creating a separate custom node pack here:
|
||||
|
||||
[github](https://github.com/alt-key-project/comfyui-dream-video-batches)
|
||||
|
||||
This new node pack will be getting my attention from now on (at least as long as stable diffusion video generation is done mostly
|
||||
in batches).
|
||||
|
||||
## Installation
|
||||
|
||||
### Simple option
|
||||
@@ -147,6 +158,9 @@ Mathematical calculation node. Exposes most of the mathematical functions in the
|
||||
[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.
|
||||
|
||||
@@ -160,12 +174,12 @@ Utility for calculating good width/height based on common video dimensions.
|
||||
### Video Encoder (FFMPEG) [Dream]
|
||||
Post processing for animation sequences calling FFMPEG to generate video files.
|
||||
|
||||
### Video Encoder (mpegCoder) [Dream]
|
||||
Post processing for animation sequences using the python module mpegCoder with ffmpeg library to generate video files.
|
||||
|
||||
### File Count [Dream]
|
||||
Finds the number of files in a directory matching specified patterns.
|
||||
|
||||
### Float/Int/string to Log Entry [Dream]
|
||||
Logging for float/int/string values.
|
||||
|
||||
### Frame Count Calculator [Dream]
|
||||
Simple utility to calculate number of frames based on time and framerate.
|
||||
|
||||
@@ -180,7 +194,19 @@ counter.
|
||||
Extracts information from the frame counter.
|
||||
|
||||
### Frame Counter Offset [Dream]
|
||||
Adds an offset to a frame counter.
|
||||
Adds an offset (in frames) to a frame counter.
|
||||
|
||||
### Frame Counter Time Offset [Dream]
|
||||
Adds an offset in seconds 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.
|
||||
@@ -197,6 +223,19 @@ Saves a frame to a directory.
|
||||
### Image Sequence Tweening [Dream]
|
||||
Post processing for animation sequences generating blended in-between frames.
|
||||
|
||||
### Laboratory [Dream]
|
||||
Super-charged number generator for experimenting with ComfyUI.
|
||||
|
||||
### Lazy *** Switch [Dream]
|
||||
Switch nodes for different type for up to ten inputs. The lazy version only evaluates the
|
||||
selected input, but first/next of the big switch is unsupported.
|
||||
|
||||
### Log Entry Joiner [Dream]
|
||||
Merges multiple log entries (reduces noodling).
|
||||
|
||||
### Log File [Dream]
|
||||
The text logging facility for the Dream Project Animation nodes.
|
||||
|
||||
### Linear Curve [Dream]
|
||||
Linear interpolation between two values over the full animation.
|
||||
|
||||
@@ -231,12 +270,21 @@ Simple sine wave curve.
|
||||
### Smooth Event Curve [Dream]
|
||||
Single event/peak curve with a slight bell-shape.
|
||||
|
||||
### String Tokenizer [Dream]
|
||||
Splits a text into tokens by a separator and returns one of the tokens based on a given index.
|
||||
|
||||
### Random Prompt Words [Dream]
|
||||
Randomly picks words/tokens/phrases from an input text.
|
||||
|
||||
### Triangle Curve [Dream]
|
||||
Triangle wave curve.
|
||||
|
||||
### Triangle Event Curve [Dream]
|
||||
Single event/peak curve with triangular shape.
|
||||
|
||||
### WAV Curve [Dream]
|
||||
Use an uncompressed WAV audio file as a curve.
|
||||
|
||||
### Other custom nodes
|
||||
|
||||
Many of the nodes found in 'WAS Node Suite' are useful the Dream Project Animation nodes - I suggest you install those
|
||||
@@ -262,6 +310,18 @@ This flow demonstrates sampling image areas into palettes and generating noise f
|
||||
|
||||
[area-sampled-noise](examples/area-sampled-noise.json)
|
||||
|
||||
### Prompt Morphing
|
||||
|
||||
This flow demonstrates prompt building with weights based on curves and brightness and contrast control.
|
||||
|
||||
[prompt-morphing](examples/prompt-morphing.json)
|
||||
|
||||
### Laboratory
|
||||
|
||||
This flow demonstrates use of the Laboratory and Logging nodes.
|
||||
|
||||
[laboratory](examples/laboratory.json)
|
||||
|
||||
## Known issues
|
||||
|
||||
### FFMPEG
|
||||
|
||||
+1
-2
@@ -1,7 +1,6 @@
|
||||
imageio
|
||||
pilgram
|
||||
scipy
|
||||
numpy<1.24>=1.18
|
||||
numpy<2.0,>=1.18
|
||||
torchvision
|
||||
mpegCoder
|
||||
evalidate
|
||||
|
||||
+76
-69
@@ -5,19 +5,20 @@ 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
|
||||
from .types import *
|
||||
from .shared import DreamConfig
|
||||
#from .shared import MpegEncoderUtility
|
||||
from .dreamtypes import *
|
||||
|
||||
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:
|
||||
@@ -131,65 +132,67 @@ def _make_video_filename(name, file_ext):
|
||||
(b, _) = os.path.splitext(name)
|
||||
return b + "." + file_ext.strip(".")
|
||||
|
||||
|
||||
class DreamVideoEncoderMpegCoder:
|
||||
NODE_NAME = "Video Encoder (mpegCoder)"
|
||||
ICON = "🎬"
|
||||
CATEGORY = NodeCategories.ANIMATION_POSTPROCESSING
|
||||
RETURN_TYPES = ()
|
||||
RETURN_NAMES = ()
|
||||
OUTPUT_NODE = True
|
||||
FUNCTION = "encode"
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": SharedTypes.sequence | {
|
||||
"name": ("STRING", {"default": 'video', "multiline": False}),
|
||||
"framerate_factor": ("FLOAT", {"default": 1.0, "min": 0.01, "max": 100.0}),
|
||||
"remove_images": ("BOOLEAN", {"default": True})
|
||||
},
|
||||
}
|
||||
|
||||
def _find_free_filename(self, filename, defaultdir):
|
||||
if os.path.basename(filename) == filename:
|
||||
filename = os.path.join(defaultdir, filename)
|
||||
n = 1
|
||||
tested = filename
|
||||
while os.path.exists(tested):
|
||||
n += 1
|
||||
(b, ext) = os.path.splitext(filename)
|
||||
tested = b + "_" + str(n) + ext
|
||||
return tested
|
||||
|
||||
def encode(self, sequence, name, framerate_factor, remove_images):
|
||||
if not sequence.is_defined:
|
||||
return ()
|
||||
config = DreamConfig()
|
||||
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))
|
||||
filename = self._find_free_filename(filename, os.path.dirname(images[0]))
|
||||
first_image = DreamImage.from_file(images[0])
|
||||
enc = MpegEncoderUtility(video_path=filename,
|
||||
bit_rate_factor=float(config.get("mpeg_coder.bitrate_factor", 1.0)),
|
||||
encoding_threads=int(config.get("mpeg_coder.encoding_threads", 4)),
|
||||
max_b_frame=int(config.get("mpeg_coder.max_b_frame", 2)),
|
||||
width=first_image.width,
|
||||
height=first_image.height,
|
||||
files=images,
|
||||
fps=sequence.fps * framerate_factor,
|
||||
codec_name=config.get("mpeg_coder.codec_name", "libx265"))
|
||||
enc.encode()
|
||||
if remove_images:
|
||||
for imagepath in images:
|
||||
if os.path.isfile(imagepath):
|
||||
os.unlink(imagepath)
|
||||
except Exception as e:
|
||||
on_error(self.__class__, str(e))
|
||||
return ()
|
||||
|
||||
#
|
||||
# class DreamVideoEncoderMpegCoder:
|
||||
# NODE_NAME = "Video Encoder (mpegCoder)"
|
||||
# ICON = "🎬"
|
||||
# CATEGORY = NodeCategories.ANIMATION_POSTPROCESSING
|
||||
# RETURN_TYPES = (LogEntry.ID,)
|
||||
# RETURN_NAMES = ("log_entry",)
|
||||
# OUTPUT_NODE = True
|
||||
# FUNCTION = "encode"
|
||||
#
|
||||
# @classmethod
|
||||
# def INPUT_TYPES(cls):
|
||||
# return {
|
||||
# "required": SharedTypes.sequence | {
|
||||
# "name": ("STRING", {"default": 'video', "multiline": False}),
|
||||
# "framerate_factor": ("FLOAT", {"default": 1.0, "min": 0.01, "max": 100.0}),
|
||||
# "remove_images": ("BOOLEAN", {"default": True})
|
||||
# },
|
||||
# }
|
||||
#
|
||||
# def _find_free_filename(self, filename, defaultdir):
|
||||
# if os.path.basename(filename) == filename:
|
||||
# filename = os.path.join(defaultdir, filename)
|
||||
# n = 1
|
||||
# tested = filename
|
||||
# while os.path.exists(tested):
|
||||
# n += 1
|
||||
# (b, ext) = os.path.splitext(filename)
|
||||
# tested = b + "_" + str(n) + ext
|
||||
# return tested
|
||||
#
|
||||
# def encode(self, sequence, name, framerate_factor, remove_images):
|
||||
# if not sequence.is_defined:
|
||||
# return (LogEntry([]),)
|
||||
# config = DreamConfig()
|
||||
# filename = _make_video_filename(name, config.get("mpeg_coder.file_extension", "mp4"))
|
||||
# log_entry = LogEntry([])
|
||||
# for batch_num in sequence.batches:
|
||||
# try:
|
||||
# images = list(sequence.get_image_files_of_batch(batch_num))
|
||||
# filename = self._find_free_filename(filename, os.path.dirname(images[0]))
|
||||
# first_image = DreamImage.from_file(images[0])
|
||||
# enc = MpegEncoderUtility(video_path=filename,
|
||||
# bit_rate_factor=float(config.get("mpeg_coder.bitrate_factor", 1.0)),
|
||||
# encoding_threads=int(config.get("mpeg_coder.encoding_threads", 4)),
|
||||
# max_b_frame=int(config.get("mpeg_coder.max_b_frame", 2)),
|
||||
# width=first_image.width,
|
||||
# height=first_image.height,
|
||||
# files=images,
|
||||
# fps=sequence.fps * framerate_factor,
|
||||
# codec_name=config.get("mpeg_coder.codec_name", "libx265"))
|
||||
# enc.encode()
|
||||
# log_entry = log_entry.add("Generated video '{}'".format(filename))
|
||||
# if remove_images:
|
||||
# for imagepath in images:
|
||||
# if os.path.isfile(imagepath):
|
||||
# os.unlink(imagepath)
|
||||
# except Exception as e:
|
||||
# on_error(self.__class__, str(e))
|
||||
# return (log_entry,)
|
||||
#
|
||||
|
||||
class DreamVideoEncoder:
|
||||
NODE_NAME = "FFMPEG Video Encoder"
|
||||
@@ -207,8 +210,8 @@ class DreamVideoEncoder:
|
||||
}
|
||||
|
||||
CATEGORY = NodeCategories.ANIMATION_POSTPROCESSING
|
||||
RETURN_TYPES = ()
|
||||
RETURN_NAMES = ()
|
||||
RETURN_TYPES = (LogEntry.ID,)
|
||||
RETURN_NAMES = ("log_entry",)
|
||||
OUTPUT_NODE = True
|
||||
FUNCTION = "encode"
|
||||
|
||||
@@ -230,24 +233,28 @@ class DreamVideoEncoder:
|
||||
def generate_video(self, files, fps, filename, config):
|
||||
filename = self._find_free_filename(filename, os.path.dirname(files[0]))
|
||||
_ffmpeg(config, files, fps, filename)
|
||||
return filename
|
||||
|
||||
def encode(self, sequence: AnimationSequence, name: str, remove_images, framerate_factor):
|
||||
if not sequence.is_defined:
|
||||
return ()
|
||||
return (LogEntry([]),)
|
||||
|
||||
config = DreamConfig()
|
||||
filename = _make_video_filename(name, config.get("ffmpeg.file_extension", "mp4"))
|
||||
log_entry = LogEntry([])
|
||||
for batch_num in sequence.batches:
|
||||
try:
|
||||
images = list(sequence.get_image_files_of_batch(batch_num))
|
||||
self.generate_video(images, sequence.fps * framerate_factor, filename, config)
|
||||
actual_filename = self.generate_video(images, sequence.fps * framerate_factor, filename, config)
|
||||
|
||||
log_entry = log_entry.add("Generated video '{}'".format(actual_filename))
|
||||
if remove_images:
|
||||
for imagepath in images:
|
||||
if os.path.isfile(imagepath):
|
||||
os.unlink(imagepath)
|
||||
except Exception as e:
|
||||
on_error(self.__class__, str(e))
|
||||
return ()
|
||||
return (log_entry,)
|
||||
|
||||
|
||||
class DreamSequenceTweening:
|
||||
|
||||
@@ -4,24 +4,25 @@ import hashlib
|
||||
import json
|
||||
import os
|
||||
import random
|
||||
import time
|
||||
|
||||
import folder_paths as comfy_paths
|
||||
import tempfile
|
||||
import glob
|
||||
from io import BytesIO
|
||||
|
||||
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 .embedded_config import EMBEDDED_CONFIGURATION
|
||||
from typing import Dict, Tuple, List
|
||||
|
||||
from .dreamlogger import DreamLog
|
||||
from .embedded_config import EMBEDDED_CONFIGURATION
|
||||
|
||||
tmpDir = tempfile.TemporaryDirectory("Dream_Anim")
|
||||
|
||||
NODE_FILE = os.path.abspath(__file__)
|
||||
DREAM_NODES_SOURCE_ROOT = os.path.dirname(NODE_FILE)
|
||||
TEMP_PATH = os.path.join(os.path.abspath(comfy_paths.temp_directory), "Dream_Anim")
|
||||
ALWAYS_CHANGED_FLAG = float("NaN")
|
||||
TEMP_PATH = os.path.join(os.path.abspath(tempfile.gettempdir()), "Dream_Anim")
|
||||
|
||||
|
||||
def convertTensorImageToPIL(tensor_image) -> Image:
|
||||
@@ -151,6 +152,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)
|
||||
|
||||
@@ -195,6 +204,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:
|
||||
@@ -257,7 +275,7 @@ def list_images_in_directory(directory_path: str, pattern: str, alphabetic_index
|
||||
|
||||
def _num_from_filename(fn):
|
||||
(text, _) = os.path.splitext(fn)
|
||||
token: str = text.split("_")[-1]
|
||||
token = text.split("_")[-1]
|
||||
if token.isdigit():
|
||||
return int(token)
|
||||
else:
|
||||
@@ -315,13 +333,13 @@ class DreamStateStore:
|
||||
|
||||
|
||||
class DreamStateFile:
|
||||
def __init__(self, state_file_path=os.path.join(TEMP_PATH, "state.json")):
|
||||
self._dirname = os.path.dirname(state_file_path)
|
||||
self._filepath = state_file_path
|
||||
def __init__(self, state_collection_name="state"):
|
||||
self._filepath = os.path.join(TEMP_PATH, state_collection_name+".json")
|
||||
self._dirname = os.path.dirname(self._filepath)
|
||||
if not os.path.isdir(self._dirname):
|
||||
os.makedirs(self._dirname)
|
||||
if not os.path.isfile(self._filepath):
|
||||
self._data: dict = {}
|
||||
self._data = {}
|
||||
else:
|
||||
with open(self._filepath, encoding="utf-8") as f:
|
||||
self._data = json.load(f)
|
||||
@@ -341,61 +359,33 @@ class DreamStateFile:
|
||||
self._data[key] = value
|
||||
with open(self._filepath, "w", encoding="utf-8") as f:
|
||||
json.dump(self._data, f)
|
||||
print("* {} -> {}".format(key, value))
|
||||
return previous
|
||||
|
||||
|
||||
def hashed_as_strings(*items):
|
||||
def hash_tensor_data(image, hasher = None):
|
||||
m = hashlib.sha256() if hasher is None else hasher
|
||||
if isinstance(image, torch.Tensor):
|
||||
buff = BytesIO()
|
||||
torch.save(image, buff)
|
||||
print("HASHING TENSOR")
|
||||
m.update(buff.getvalue())
|
||||
elif isinstance(image, bytes) or isinstance(image, bytearray):
|
||||
print("HASHING BYTES")
|
||||
m.update(image)
|
||||
elif isinstance(image, list) or isinstance(image, tuple):
|
||||
print("HASHING ITERABLE")
|
||||
for item in image:
|
||||
hash_tensor_data(item, m)
|
||||
else:
|
||||
print("HASING_AS_TEXT - "+str(type(image)))
|
||||
m.update(str(image).encode(encoding="utf-8"))
|
||||
return m.digest().hex()
|
||||
|
||||
def hashed_as_strings(*items, **kwargs):
|
||||
tokens = "|".join(list(map(str, items)))
|
||||
m = hashlib.sha256()
|
||||
m.update(tokens.encode(encoding="utf-8"))
|
||||
for pair in kwargs.items():
|
||||
m.update(str(pair).encode(encoding="utf-8"))
|
||||
return m.digest().hex()
|
||||
|
||||
|
||||
class MpegEncoderUtility:
|
||||
def __init__(self, video_path: str, bit_rate_factor: float, width: int, height: int, files: List[str],
|
||||
fps: float, encoding_threads: int, codec_name, max_b_frame):
|
||||
import mpegCoder
|
||||
self._files = files
|
||||
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._enc.setParameter(
|
||||
videoPath=video_path, codecName=codec_name,
|
||||
nthread=encoding_threads, bitRate=bit_rate, width=width, height=height, widthSrc=width,
|
||||
heightSrc=height,
|
||||
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
|
||||
return round(max(10, float(width * height * fps * bits_per_pixel_base * bit_rate_factor * 0.001)))
|
||||
|
||||
def encode(self):
|
||||
if not self._enc.FFmpegSetup():
|
||||
raise Exception("Failed to setup MPEG Encoder - check parameters!")
|
||||
try:
|
||||
t = time.time()
|
||||
|
||||
for filepath in self._files:
|
||||
self._logger.debug("Encoding frame {}", filepath)
|
||||
image = DreamImage.from_file(filepath).convert("RGB")
|
||||
self._enc.EncodeFrame(image.numpy_array())
|
||||
self._enc.FFmpegClose()
|
||||
self._logger.info("Completed video encoding of {n} frames in {t} seconds", n=len(self._files),
|
||||
t=round(time.time() - t))
|
||||
finally:
|
||||
self._enc.clear()
|
||||
|
||||
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:
|
||||
a /= 10
|
||||
b *= 10
|
||||
a = round(a)
|
||||
b = round(b)
|
||||
self._logger.info("Video specified as {fps} fps - encoder framerate {a}/{b}", fps=fps, a=a, b=b)
|
||||
return (a, b)
|
||||
|
||||
+27
-49
@@ -1,30 +1,40 @@
|
||||
from .categories import NodeCategories
|
||||
from .dreamtypes import RGBPalette
|
||||
from .err import *
|
||||
from .shared import ALWAYS_CHANGED_FLAG, hashed_as_strings
|
||||
from .types import RGBPalette
|
||||
from .shared import hashed_as_strings, hash_tensor_data
|
||||
|
||||
_NOT_A_VALUE_I = 9223372036854775807
|
||||
_NOT_A_VALUE_F = float(_NOT_A_VALUE_I)
|
||||
_NOT_A_VALUE_S = "⭆"
|
||||
|
||||
def _generate_switch_input(type: str):
|
||||
def _generate_switch_input(type_nm: str, default_value=None):
|
||||
d = dict()
|
||||
for i in range(10):
|
||||
d["input_" + str(i)] = (type,)
|
||||
if default_value is None:
|
||||
d["input_" + str(i)] = (type_nm,)
|
||||
else:
|
||||
d["input_" + str(i)] = (type_nm, {"default": default_value, "forceInput": True})
|
||||
|
||||
return {
|
||||
"required": {
|
||||
"select": ("INT", {"defualt": 0, "min": 0, "max": 9}),
|
||||
"select": ("INT", {"default": 0, "min": 0, "max": 9}),
|
||||
"on_missing": (["previous", "next"],)
|
||||
},
|
||||
"optional": d
|
||||
}
|
||||
|
||||
|
||||
def _do_pick(cls, select, on_missing, **args):
|
||||
def _do_pick(cls, select, test_val, 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:
|
||||
n = len(args)
|
||||
while not test_val(args.get("input_" + str(select), None)):
|
||||
if n<0:
|
||||
return (None,)
|
||||
select = (select + direction) % 10
|
||||
n = n - 1
|
||||
return args["input_" + str(select)],
|
||||
|
||||
|
||||
@@ -41,12 +51,8 @@ class DreamBigImageSwitch:
|
||||
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)
|
||||
return _do_pick(self.__class__, select, lambda n: n is not None, on_missing, **args)
|
||||
|
||||
|
||||
class DreamBigLatentSwitch:
|
||||
@@ -62,12 +68,8 @@ class DreamBigLatentSwitch:
|
||||
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)
|
||||
return _do_pick(self.__class__, select, lambda n: n is not None, on_missing, **args)
|
||||
|
||||
|
||||
class DreamBigTextSwitch:
|
||||
@@ -81,14 +83,10 @@ class DreamBigTextSwitch:
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return _generate_switch_input(cls._switch_type)
|
||||
|
||||
@classmethod
|
||||
def IS_CHANGED(cls, *values):
|
||||
return hashed_as_strings(values)
|
||||
return _generate_switch_input(cls._switch_type, _NOT_A_VALUE_S)
|
||||
|
||||
def pick(self, select, on_missing, **args):
|
||||
return _do_pick(self.__class__, select, on_missing, **args)
|
||||
return _do_pick(self.__class__, select, lambda n: (n is not None) and (n != _NOT_A_VALUE_S), on_missing, **args)
|
||||
|
||||
|
||||
class DreamBigPaletteSwitch:
|
||||
@@ -104,12 +102,8 @@ class DreamBigPaletteSwitch:
|
||||
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)
|
||||
return _do_pick(self.__class__, select, lambda n: (n is not None), on_missing, **args)
|
||||
|
||||
|
||||
class DreamBigFloatSwitch:
|
||||
@@ -123,14 +117,10 @@ class DreamBigFloatSwitch:
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return _generate_switch_input(cls._switch_type)
|
||||
|
||||
@classmethod
|
||||
def IS_CHANGED(cls, *values):
|
||||
return hashed_as_strings(values)
|
||||
return _generate_switch_input(cls._switch_type, _NOT_A_VALUE_F)
|
||||
|
||||
def pick(self, select, on_missing, **args):
|
||||
return _do_pick(self.__class__, select, on_missing, **args)
|
||||
return _do_pick(self.__class__, select, lambda n: (n is not None) and (n != _NOT_A_VALUE_F), on_missing, **args)
|
||||
|
||||
|
||||
class DreamBigIntSwitch:
|
||||
@@ -144,14 +134,10 @@ class DreamBigIntSwitch:
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return _generate_switch_input(cls._switch_type)
|
||||
|
||||
@classmethod
|
||||
def IS_CHANGED(cls, *values):
|
||||
return hashed_as_strings(values)
|
||||
return _generate_switch_input(cls._switch_type, _NOT_A_VALUE_I)
|
||||
|
||||
def pick(self, select, on_missing, **args):
|
||||
return _do_pick(self.__class__, select, on_missing, **args)
|
||||
return _do_pick(self.__class__, select, lambda n: (n is not None) and (n != _NOT_A_VALUE_I), on_missing, **args)
|
||||
|
||||
|
||||
class DreamBoolToFloat:
|
||||
@@ -172,10 +158,6 @@ class DreamBoolToFloat:
|
||||
}
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def IS_CHANGED(cls, *values):
|
||||
return hashed_as_strings(values)
|
||||
|
||||
def pick(self, boolean, on_true, on_false):
|
||||
if boolean:
|
||||
return (on_true,)
|
||||
@@ -201,10 +183,6 @@ class DreamBoolToInt:
|
||||
}
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def IS_CHANGED(cls, *values):
|
||||
return hashed_as_strings(values)
|
||||
|
||||
def pick(self, boolean, on_true, on_false):
|
||||
if boolean:
|
||||
return (on_true,)
|
||||
|
||||
+213
-6
@@ -1,8 +1,220 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import datetime
|
||||
import math
|
||||
import os
|
||||
|
||||
import folder_paths as comfy_paths
|
||||
|
||||
from .categories import NodeCategories
|
||||
from .shared import hashed_as_strings
|
||||
from .shared import hashed_as_strings, DreamStateFile
|
||||
from .dreamtypes import LogEntry, SharedTypes, FrameCounter
|
||||
|
||||
_logfile_state = DreamStateFile("logging")
|
||||
|
||||
|
||||
class DreamJoinLog:
|
||||
NODE_NAME = "Log Entry Joiner"
|
||||
ICON = "🗎"
|
||||
CATEGORY = NodeCategories.UTILS
|
||||
RETURN_TYPES = (LogEntry.ID,)
|
||||
RETURN_NAMES = ("log_entry",)
|
||||
FUNCTION = "convert"
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"optional": {
|
||||
"entry_0": (LogEntry.ID,),
|
||||
"entry_1": (LogEntry.ID,),
|
||||
"entry_2": (LogEntry.ID,),
|
||||
"entry_3": (LogEntry.ID,),
|
||||
}
|
||||
}
|
||||
|
||||
def convert(self, **values):
|
||||
entry = LogEntry([])
|
||||
for i in range(4):
|
||||
txt = values.get("entry_" + str(i), None)
|
||||
if txt:
|
||||
entry = entry.merge(txt)
|
||||
return (entry,)
|
||||
|
||||
|
||||
class DreamFloatToLog:
|
||||
NODE_NAME = "Float to Log Entry"
|
||||
ICON = "🗎"
|
||||
CATEGORY = NodeCategories.UTILS
|
||||
RETURN_TYPES = (LogEntry.ID,)
|
||||
RETURN_NAMES = ("log_entry",)
|
||||
FUNCTION = "convert"
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"value": ("FLOAT", {"default": 0}),
|
||||
"label": ("STRING", {"default": ""}),
|
||||
},
|
||||
}
|
||||
|
||||
def convert(self, label, value):
|
||||
return (LogEntry.new(label + ": " + str(value)),)
|
||||
|
||||
|
||||
class DreamIntToLog:
|
||||
NODE_NAME = "Int to Log Entry"
|
||||
ICON = "🗎"
|
||||
CATEGORY = NodeCategories.UTILS
|
||||
RETURN_TYPES = (LogEntry.ID,)
|
||||
RETURN_NAMES = ("log_entry",)
|
||||
FUNCTION = "convert"
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"value": ("INT", {"default": 0}),
|
||||
"label": ("STRING", {"default": ""}),
|
||||
},
|
||||
}
|
||||
|
||||
def convert(self, label, value):
|
||||
return (LogEntry.new(label + ": " + str(value)),)
|
||||
|
||||
|
||||
class DreamStringToLog:
|
||||
NODE_NAME = "String to Log Entry"
|
||||
ICON = "🗎"
|
||||
OUTPUT_NODE = True
|
||||
CATEGORY = NodeCategories.UTILS
|
||||
RETURN_TYPES = (LogEntry.ID,)
|
||||
RETURN_NAMES = ("log_entry",)
|
||||
FUNCTION = "convert"
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"text": ("STRING", {"default": ""}),
|
||||
},
|
||||
"optional": {
|
||||
"label": ("STRING", {"default": ""}),
|
||||
}
|
||||
}
|
||||
|
||||
def convert(self, text, **values):
|
||||
label = values.get("label", "")
|
||||
if label:
|
||||
return (LogEntry.new(label + ": " + text),)
|
||||
else:
|
||||
return (LogEntry.new(text),)
|
||||
|
||||
|
||||
class DreamStringTokenizer:
|
||||
NODE_NAME = "String Tokenizer"
|
||||
ICON = "🪙"
|
||||
OUTPUT_NODE = True
|
||||
CATEGORY = NodeCategories.UTILS
|
||||
RETURN_TYPES = ("STRING",)
|
||||
RETURN_NAMES = ("token",)
|
||||
FUNCTION = "exec"
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"text": ("STRING", {"default": "", "multiline": True}),
|
||||
"separator": ("STRING", {"default": ","}),
|
||||
"selected": ("INT", {"default": 0, "min": 0})
|
||||
},
|
||||
}
|
||||
|
||||
def exec(self, text: str, separator: str, selected: int):
|
||||
if separator is None or separator == "":
|
||||
separator = " "
|
||||
parts = text.split(sep=separator)
|
||||
return (parts[abs(selected) % len(parts)].strip(),)
|
||||
|
||||
|
||||
class DreamLogFile:
|
||||
NODE_NAME = "Log File"
|
||||
ICON = "🗎"
|
||||
OUTPUT_NODE = True
|
||||
CATEGORY = NodeCategories.UTILS
|
||||
RETURN_TYPES = ()
|
||||
RETURN_NAMES = ()
|
||||
FUNCTION = "write"
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": SharedTypes.frame_counter | {
|
||||
"log_directory": ("STRING", {"default": comfy_paths.output_directory}),
|
||||
"log_filename": ("STRING", {"default": "dreamlog.txt"}),
|
||||
"stdout": ("BOOLEAN", {"default": True}),
|
||||
"active": ("BOOLEAN", {"default": True}),
|
||||
"clock_has_24_hours": ("BOOLEAN", {"default": True}),
|
||||
},
|
||||
"optional": {
|
||||
"entry_0": (LogEntry.ID,),
|
||||
"entry_1": (LogEntry.ID,),
|
||||
"entry_2": (LogEntry.ID,),
|
||||
"entry_3": (LogEntry.ID,),
|
||||
"entry_4": (LogEntry.ID,),
|
||||
"entry_5": (LogEntry.ID,),
|
||||
"entry_6": (LogEntry.ID,),
|
||||
"entry_7": (LogEntry.ID,),
|
||||
},
|
||||
}
|
||||
|
||||
def _path_to_log_file(self, log_directory, logfile):
|
||||
if os.path.isabs(logfile):
|
||||
return os.path.normpath(os.path.abspath(logfile))
|
||||
elif os.path.isabs(log_directory):
|
||||
return os.path.normpath(os.path.abspath(os.path.join(log_directory, logfile)))
|
||||
elif log_directory:
|
||||
return os.path.normpath(os.path.abspath(os.path.join(comfy_paths.output_directory, log_directory, logfile)))
|
||||
else:
|
||||
return os.path.normpath(os.path.abspath(os.path.join(comfy_paths.output_directory, logfile)))
|
||||
|
||||
def _get_tm_format(self, clock_has_24_hours):
|
||||
if clock_has_24_hours:
|
||||
return "%a %H:%M:%S"
|
||||
else:
|
||||
return "%a %I:%M:%S %p"
|
||||
|
||||
def write(self, frame_counter: FrameCounter, log_directory, log_filename, stdout, active, clock_has_24_hours,
|
||||
**entries):
|
||||
if not active:
|
||||
return ()
|
||||
log_entry = None
|
||||
for i in range(8):
|
||||
e = entries.get("entry_" + str(i), None)
|
||||
if e is not None:
|
||||
if log_entry is None:
|
||||
log_entry = e
|
||||
else:
|
||||
log_entry = log_entry.merge(e)
|
||||
log_file_path = self._path_to_log_file(log_directory, log_filename)
|
||||
ts = _logfile_state.get_section("timestamps").get(log_file_path, 0)
|
||||
output_text = list()
|
||||
last_t = 0
|
||||
for (t, text) in log_entry.get_filtered_entries(ts):
|
||||
dt = datetime.datetime.fromtimestamp(t)
|
||||
output_text.append("[frame {}/{} (~{}%), timestamp {}]\n{}".format(frame_counter.current_frame + 1,
|
||||
frame_counter.total_frames,
|
||||
round(frame_counter.progress * 100),
|
||||
dt.strftime(self._get_tm_format(
|
||||
clock_has_24_hours)), text.rstrip()))
|
||||
output_text.append("---")
|
||||
last_t = max(t, last_t)
|
||||
output_text = "\n".join(output_text) + "\n"
|
||||
if stdout:
|
||||
print(output_text)
|
||||
with open(log_file_path, "a", encoding="utf-8") as f:
|
||||
f.write(output_text)
|
||||
_logfile_state.get_section("timestamps").update(log_file_path, 0, lambda _: last_t)
|
||||
return ()
|
||||
|
||||
|
||||
def _align_num(n: int, alignment: int, type: str):
|
||||
@@ -38,10 +250,6 @@ class DreamFrameDimensions:
|
||||
RETURN_NAMES = ("width", "height", "final_width", "final_height")
|
||||
FUNCTION = "result"
|
||||
|
||||
@classmethod
|
||||
def IS_CHANGED(cls, *values):
|
||||
return hashed_as_strings(*values)
|
||||
|
||||
def result(self, size, aspect_ratio, orientation, divisor, alignment, alignment_type):
|
||||
ratio = tuple(map(int, aspect_ratio.split(":")))
|
||||
final_width = int(size)
|
||||
@@ -52,4 +260,3 @@ class DreamFrameDimensions:
|
||||
return (width, height, final_width, final_height)
|
||||
else:
|
||||
return (height, width, final_height, final_width)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user