12 Commits
22 changed files with 5350 additions and 1743 deletions
+4 -3
View File
@@ -22,19 +22,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, 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": (5, 0, 0),
"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
+64 -139
View File
@@ -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": {},
+162 -281
View File
@@ -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": {},
+1212 -1181
View File
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])
+3 -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,10 @@
"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"
"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 -4
View File
@@ -163,9 +163,6 @@ 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.
@@ -186,7 +183,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 +255,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 +298,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
View File
@@ -3,5 +3,4 @@ pilgram
scipy
numpy<1.24>=1.18
torchvision
mpegCoder
evalidate
+64 -63
View File
@@ -9,8 +9,9 @@ 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()
@@ -131,67 +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 = (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 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"
+50 -50
View File
@@ -366,53 +366,53 @@ def hashed_as_strings(*items):
m = hashlib.sha256()
m.update(tokens.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.5
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)
#
#
# 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.5
# 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)
+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"