11 Commits
Author SHA1 Message Date
Morgan Johansson 07bfda783d rename of types.py to dreamtypes.py 2023-10-22 12:08:37 +02:00
alt-key-project d74fdb8de7 Merge pull request #6 from linnkoln/master
Allows to output name of frame file
2023-10-17 21:22:49 +02:00
linnkoln 5def36f87f Allows to output name of frame file 2023-10-12 17:11:54 +03:00
Morgan Johansson 08a0a06820 Removed debug logging. 2023-09-19 20:39:38 +02:00
Morgan Johansson 5ce2e63f01 WAV curves. 2023-09-19 20:19:49 +02:00
Morgan Johansson ebed0e60d9 Update to examples. 2023-09-18 19:29:24 +02:00
Morgan Johansson f65df84de6 New example. 2023-09-18 19:22:54 +02:00
Morgan Johansson c5579afb12 minor fix 2023-09-18 06:59:15 +02:00
Morgan Johansson 7d5b6d8f4a Added string tokenizer utility 2023-09-18 06:54:59 +02:00
Morgan Johansson af492b7669 Example update. 2023-09-17 16:44:40 +02:00
Morgan Johansson 15084a9264 Label on string log entry. 2023-09-17 16:12:59 +02:00
18 changed files with 4896 additions and 894 deletions
+3 -2
View File
@@ -29,12 +29,13 @@ _NODE_CLASSES: List[Type] = [DreamSineWave, DreamLinear, DreamCSVCurve, DreamBea
DreamFrameCounterInfo, DreamBoolToFloat, DreamBoolToInt, DreamSawWave, DreamTriangleWave,
DreamTriangleEvent, DreamSmoothEvent, DreamCalculation, DreamImageColorShift,
DreamComparePalette, DreamImageContrast, DreamImageBrightness, DreamLogFile,
DreamLaboratory, DreamStringToLog, DreamIntToLog, DreamFloatToLog, DreamJoinLog]
DreamLaboratory, DreamStringToLog, DreamIntToLog, DreamFloatToLog, DreamJoinLog,
DreamStringTokenizer, DreamWavCurve, DreamFrameCounterTimeOffset]
_SIGNATURE_SUFFIX = " [Dream]"
MANIFEST = {
"name": "Dream Project Animation",
"version": (4, 0, 0),
"version": (4, 3, 1),
"author": "Dream Project",
"project": "https://github.com/alt-key-project/comfyui-dream-project",
"description": "Various utility nodes for creating animations with ComfyUI",
+27 -1
View File
@@ -3,7 +3,7 @@ import glob
from .categories import NodeCategories
from .shared import *
from .types import *
from .dreamtypes import *
class DreamFrameCounterInfo:
@@ -95,6 +95,32 @@ class DreamFrameCounterOffset:
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"
@classmethod
def IS_CHANGED(cls, frame_counter, offset):
return hashed_as_strings(frame_counter, offset)
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)"
+1 -1
View File
@@ -2,7 +2,7 @@
from .categories import NodeCategories
from .shared import *
from .types import *
from .dreamtypes import *
class DreamImageAreaSampler:
+69 -1
View File
@@ -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):
@@ -119,6 +123,70 @@ 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})
},
}
@classmethod
def IS_CHANGED(cls, *values):
return hashed_as_strings(*values)
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"
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -9,7 +9,7 @@ from PIL.Image import Resampling
from .categories import *
from .shared import ALWAYS_CHANGED_FLAG, convertTensorImageToPIL, DreamImageProcessor, \
DreamImage, DreamMask
from .types import SharedTypes, FrameCounter
from .dreamtypes import SharedTypes, FrameCounter
class DreamImageMotion:
+1 -1
View File
@@ -6,7 +6,7 @@ import json
from .categories import *
from .shared import ALWAYS_CHANGED_FLAG, DreamStateFile
from .types import *
from .dreamtypes import *
_laboratory_state = DreamStateFile("laboratory")
+6 -4
View File
@@ -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 .dreamtypes import SharedTypes, FrameCounter
import os
class DreamImageSequenceInputWithDefaultFallback:
@@ -22,8 +23,8 @@ class DreamImageSequenceInputWithDefaultFallback:
}
CATEGORY = NodeCategories.IMAGE_ANIMATION
RETURN_TYPES = ("IMAGE",)
RETURN_NAMES = ("image",)
RETURN_TYPES = ("IMAGE","STRING")
RETURN_NAMES = ("image","frame_name")
FUNCTION = "result"
@classmethod
@@ -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])
+4 -1
View File
@@ -25,6 +25,7 @@
"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",
@@ -49,9 +50,11 @@
"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"
"Video Encoder (mpegCoder) [Dream]": "Post processing for animation sequences using mpegCoder module to generate video file",
"WAV Curve [Dream]": "WAV audio file as a curve"
}
+1 -1
View File
@@ -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,
+1 -1
View File
@@ -8,7 +8,7 @@ from PIL.PngImagePlugin import PngInfo
from .categories import NodeCategories
from .shared import hashed_as_strings, DreamImageProcessor, DreamImage, \
list_images_in_directory, DreamConfig
from .types import SharedTypes, FrameCounter, AnimationSequence, LogEntry
from .dreamtypes import SharedTypes, FrameCounter, AnimationSequence, LogEntry
CONFIG = DreamConfig()
+1 -1
View File
@@ -1,6 +1,6 @@
from .categories import NodeCategories
from .shared import hashed_as_strings
from .types import PartialPrompt
from .dreamtypes import PartialPrompt
class DreamWeightedPromptBuilder:
+16 -1
View File
@@ -186,7 +186,10 @@ 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.
@@ -255,12 +258,18 @@ 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.
### 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
@@ -292,6 +301,12 @@ This flow demonstrates prompt building with weights based on curves and brightne
[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 -1
View File
@@ -10,7 +10,7 @@ from PIL import Image as PilImage
from .categories import NodeCategories
from .err import on_error
from .shared import DreamConfig, MpegEncoderUtility
from .types import *
from .dreamtypes import *
CONFIG = DreamConfig()
+1 -1
View File
@@ -1,7 +1,7 @@
from .categories import NodeCategories
from .err import *
from .shared import ALWAYS_CHANGED_FLAG, hashed_as_strings
from .types import RGBPalette
from .dreamtypes import RGBPalette
def _generate_switch_input(type: str):
+40 -7
View File
@@ -7,7 +7,7 @@ import folder_paths as comfy_paths
from .categories import NodeCategories
from .shared import hashed_as_strings, DreamStateFile
from .types import LogEntry, SharedTypes, FrameCounter
from .dreamtypes import LogEntry, SharedTypes, FrameCounter
_logfile_state = DreamStateFile("logging")
@@ -97,10 +97,43 @@ class DreamStringToLog:
"required": {
"text": ("STRING", {"default": ""}),
},
"optional": {
"label": ("STRING", {"default": ""}),
}
}
def convert(self, text):
return (LogEntry.new(text),)
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:
@@ -169,10 +202,10 @@ class DreamLogFile:
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()))
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"