Merge pull request #16 from dmarx/dev
Parameter Groups, Drawing, Simplified Scheduling
This commit is contained in:
@@ -49,11 +49,22 @@ Related project: https://github.com/FizzleDorf/ComfyUI_FizzNodes
|
||||
|
||||
## Prompt Scheduling
|
||||
|
||||
see also: [`Nodes > Scheduling`](https://github.com/dmarx/ComfyUI-Keyframed/blob/dev/README.md#scheduling)
|
||||
|
||||
<!--
|
||||

|
||||
-->
|
||||
|
||||

|
||||
|
||||
This one is probably why you are here. This workflow demonstrates how to use the `keyframed/schedule` nodes to achieve similar behavior as [FizzNodes'](https://github.com/FizzleDorf/ComfyUI_FizzNodes) **PromptSchedule** node, but implemented differently.
|
||||
|
||||
This schedule is essentailly a normal AnimateDiff workflow where several nodes have replaced the normal conditioning setup. Rather than a single `CLIP Text Encode` node, we can have multiple prompts which transition sequentially over time. For documentation detailing how this workflow works, see the [`Nodes > Scheduling`](https://github.com/dmarx/ComfyUI-Keyframed/blob/dev/README.md#scheduling) section below.
|
||||
This schedule is essentailly a normal AnimateDiff workflow where several nodes have replaced the normal conditioning setup. Rather than a single `CLIP Text Encode` node, we can have multiple prompts which transition sequentially over time.
|
||||
|
||||
The workflow above illustrations the most user-friendly interface, but there are nodes for lower level interfaces as well:
|
||||
|
||||

|
||||
|
||||
|
||||
## Interleaving Multiple Prompts Simultaneously (aka Prompt Entanglement, aka Prompt Superposition)
|
||||
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 972 KiB |
|
Before Width: | Height: | Size: 1.1 MiB After Width: | Height: | Size: 1.1 MiB |
+6
-1
@@ -4,6 +4,7 @@ from .debug import NODE_CLASS_MAPPINGS as ncm0, NODE_DISPLAY_NAME_MAPPINGS as nd
|
||||
from .entangled import NODE_CLASS_MAPPINGS as ncm1, NODE_DISPLAY_NAME_MAPPINGS as ndnm1
|
||||
from .schedule import NODE_CLASS_MAPPINGS as ncm2, NODE_DISPLAY_NAME_MAPPINGS as ndnm2
|
||||
from .sinusoidal import NODE_CLASS_MAPPINGS as ncm3, NODE_DISPLAY_NAME_MAPPINGS as ndnm3
|
||||
from .pgroup import NODE_CLASS_MAPPINGS as ncm4, NODE_DISPLAY_NAME_MAPPINGS as ndnm4
|
||||
|
||||
|
||||
# there's probably a cleaner, more-dummy-proof way to do this.
|
||||
@@ -12,10 +13,14 @@ NODE_CLASS_MAPPINGS.update(ncm0)
|
||||
NODE_CLASS_MAPPINGS.update(ncm1)
|
||||
NODE_CLASS_MAPPINGS.update(ncm2)
|
||||
NODE_CLASS_MAPPINGS.update(ncm3)
|
||||
NODE_CLASS_MAPPINGS.update(ncm4)
|
||||
NODE_DISPLAY_NAME_MAPPINGS.update(ndnm0)
|
||||
NODE_DISPLAY_NAME_MAPPINGS.update(ndnm1)
|
||||
NODE_DISPLAY_NAME_MAPPINGS.update(ndnm2)
|
||||
NODE_DISPLAY_NAME_MAPPINGS.update(ndnm3)
|
||||
NODE_DISPLAY_NAME_MAPPINGS.update(ndnm4)
|
||||
|
||||
|
||||
__all__ =["NODE_CLASS_MAPPINGS", "NODE_DISPLAY_NAME_MAPPINGS"]
|
||||
__all__ =["NODE_CLASS_MAPPINGS", "NODE_DISPLAY_NAME_MAPPINGS"]
|
||||
|
||||
# TODO: ensure installed: keyframed toolz
|
||||
+62
-89
@@ -248,6 +248,42 @@ class KfConditioningAddx10:
|
||||
return [((cond_t_out, cond_d_out),)] #((cond_t_out, cond_d_out),)
|
||||
|
||||
|
||||
|
||||
class KfConditioningAddx10_alt:
|
||||
CATEGORY = CATEGORY
|
||||
FUNCTION = "main"
|
||||
RETURN_TYPES = ("CONDITIONING",)
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"cond_0": ("CONDITIONING",{"forceInput": True,}),
|
||||
},
|
||||
"optional": {
|
||||
"cond_1": ("CONDITIONING",{"forceInput": True, "default": 0}),
|
||||
"cond_2": ("CONDITIONING",{"forceInput": True, "default": 0}),
|
||||
"cond_3": ("CONDITIONING",{"forceInput": True, "default": 0}),
|
||||
"cond_4": ("CONDITIONING",{"forceInput": True, "default": 0}),
|
||||
"cond_5": ("CONDITIONING",{"forceInput": True, "default": 0}),
|
||||
"cond_6": ("CONDITIONING",{"forceInput": True, "default": 0}),
|
||||
"cond_7": ("CONDITIONING",{"forceInput": True, "default": 0}),
|
||||
"cond_8": ("CONDITIONING",{"forceInput": True, "default": 0}),
|
||||
"cond_9": ("CONDITIONING",{"forceInput": True, "default": 0}),
|
||||
},
|
||||
}
|
||||
|
||||
def main(self, cond_0, **kwargs):
|
||||
((cond_t_out, cond_d_out),) = deepcopy(cond_0)
|
||||
for v in kwargs.values():
|
||||
if v not in (0, None):
|
||||
((cond_t,cond_d),) = v
|
||||
cond_t, cond_d = deepcopy(cond_t), deepcopy(cond_d)
|
||||
cond_t_out = cond_t_out + cond_t
|
||||
cond_d_out["pooled_output"] = cond_d_out["pooled_output"] + cond_d["pooled_output"]
|
||||
return [((cond_t_out, cond_d_out),)] #((cond_t_out, cond_d_out),)
|
||||
|
||||
|
||||
# class KfCurveInverse:
|
||||
# CATEGORY = CATEGORY
|
||||
# FUNCTION = "main"
|
||||
@@ -270,7 +306,7 @@ class KfConditioningAddx10:
|
||||
# return (curve,)
|
||||
|
||||
|
||||
def plot_curve(curve, n):
|
||||
def plot_curve(curve, n, show_legend, is_pgroup=False):
|
||||
"""
|
||||
|
||||
"""
|
||||
@@ -288,7 +324,7 @@ def plot_curve(curve, n):
|
||||
|
||||
m=3
|
||||
if n < m:
|
||||
n = self.duration + 1
|
||||
n = curve.duration + 1
|
||||
n = max(m, n)
|
||||
|
||||
|
||||
@@ -313,12 +349,14 @@ def plot_curve(curve, n):
|
||||
kfy = [curve[x] for x in kfx]
|
||||
plt.scatter(kfx, kfy, color=line[0].get_color())
|
||||
|
||||
if isinstance(curve, kf.ParameterGroup):
|
||||
#if isinstance(curve, kf.ParameterGroup): # type collision with kf.Composition
|
||||
if is_pgroup:
|
||||
for c in curve.parameters.values():
|
||||
draw_curve(c)
|
||||
else:
|
||||
draw_curve(curve)
|
||||
plt.legend()
|
||||
if show_legend:
|
||||
plt.legend()
|
||||
|
||||
|
||||
#width, height = 10, 5 #inches
|
||||
@@ -356,11 +394,12 @@ class KfCurveDraw:
|
||||
"required": {
|
||||
"curve": ("KEYFRAMED_CURVE", {"forceInput": True,}),
|
||||
"n": ("INT", {"default": 64}),
|
||||
"show_legend": ("BOOLEAN", {"default": True}),
|
||||
}
|
||||
}
|
||||
|
||||
def main(self, curve, n):
|
||||
img_tensor = plot_curve(curve, n)
|
||||
def main(self, curve, n, show_legend):
|
||||
img_tensor = plot_curve(curve, n, show_legend, is_pgroup=False)
|
||||
return (img_tensor,)
|
||||
|
||||
class KfPGroupDraw:
|
||||
@@ -374,11 +413,12 @@ class KfPGroupDraw:
|
||||
"required": {
|
||||
"parameter_group": ("PARAMETER_GROUP", {"forceInput": True,}),
|
||||
"n": ("INT", {"default": 64}),
|
||||
"show_legend": ("BOOLEAN", {"default": True}),
|
||||
}
|
||||
}
|
||||
|
||||
def main(self, parameter_group, n):
|
||||
img_tensor = plot_curve(parameter_group, n)
|
||||
def main(self, parameter_group, n, show_legend):
|
||||
img_tensor = plot_curve(parameter_group, n, show_legend, is_pgroup=True)
|
||||
return (img_tensor,)
|
||||
###########################################
|
||||
|
||||
@@ -429,7 +469,7 @@ class KfCurvesAddx10:
|
||||
},
|
||||
}
|
||||
|
||||
def main(self, curve_0, curve_1, curve_2, curve_3, curve_4, curve_5, curve_6, curve_7, curve_8, curve_9):
|
||||
def main(self, curve_0=0, curve_1=0, curve_2=0, curve_3=0, curve_4=0, curve_5=0, curve_6=0, curve_7=0, curve_8=0, curve_9=0):
|
||||
#curve_1 = deepcopy(curve_1)
|
||||
#curve_2 = deepcopy(curve_2)
|
||||
#return (curve_1 + curve_2, )
|
||||
@@ -499,15 +539,15 @@ class KfCurvesMultiplyx10:
|
||||
"curve_0": ("KEYFRAMED_CURVE",{"forceInput": True,}),
|
||||
},
|
||||
"optional": {
|
||||
"curve_1": ("KEYFRAMED_CURVE",{"forceInput": True, "default": 0}),
|
||||
"curve_2": ("KEYFRAMED_CURVE",{"forceInput": True, "default": 0}),
|
||||
"curve_3": ("KEYFRAMED_CURVE",{"forceInput": True, "default": 0}),
|
||||
"curve_4": ("KEYFRAMED_CURVE",{"forceInput": True, "default": 0}),
|
||||
"curve_5": ("KEYFRAMED_CURVE",{"forceInput": True, "default": 0}),
|
||||
"curve_6": ("KEYFRAMED_CURVE",{"forceInput": True, "default": 0}),
|
||||
"curve_7": ("KEYFRAMED_CURVE",{"forceInput": True, "default": 0}),
|
||||
"curve_8": ("KEYFRAMED_CURVE",{"forceInput": True, "default": 0}),
|
||||
"curve_9": ("KEYFRAMED_CURVE",{"forceInput": True, "default": 0}),
|
||||
"curve_1": ("KEYFRAMED_CURVE",{"forceInput": True, "default": 1}),
|
||||
"curve_2": ("KEYFRAMED_CURVE",{"forceInput": True, "default": 1}),
|
||||
"curve_3": ("KEYFRAMED_CURVE",{"forceInput": True, "default": 1}),
|
||||
"curve_4": ("KEYFRAMED_CURVE",{"forceInput": True, "default": 1}),
|
||||
"curve_5": ("KEYFRAMED_CURVE",{"forceInput": True, "default": 1}),
|
||||
"curve_6": ("KEYFRAMED_CURVE",{"forceInput": True, "default": 1}),
|
||||
"curve_7": ("KEYFRAMED_CURVE",{"forceInput": True, "default": 1}),
|
||||
"curve_8": ("KEYFRAMED_CURVE",{"forceInput": True, "default": 1}),
|
||||
"curve_9": ("KEYFRAMED_CURVE",{"forceInput": True, "default": 1}),
|
||||
},
|
||||
}
|
||||
|
||||
@@ -568,72 +608,6 @@ class KfCurveConstant:
|
||||
return (curve,)
|
||||
|
||||
|
||||
##################################################################
|
||||
|
||||
### TODO: Working with parameter groups
|
||||
|
||||
# add curve(s) to parameter group
|
||||
## inputs: pgroup, curve
|
||||
## returns pgroup
|
||||
## if pgroup not provided, new one created
|
||||
|
||||
class KfAddCurveToPGroup:
|
||||
CATEGORY = CATEGORY
|
||||
FUNCTION = "main"
|
||||
#RETURN_TYPES = ("KEYFRAMED_CURVE",)
|
||||
RETURN_TYPES = ("PARAMETER_GROUP",)
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"curve": ("KEYFRAMED_CURVE",{"forceInput": True,}),
|
||||
},
|
||||
"optional": {
|
||||
"parameter_group": ("PARAMETER_GROUP",{"forceInput": True,}),
|
||||
},
|
||||
}
|
||||
|
||||
def main(self, curve, parameter_group=None):
|
||||
curve = deepcopy(curve)
|
||||
if parameter_group is None:
|
||||
#parameter_group = kf.ParameterGroup({curve.label:curve})
|
||||
parameter_group = kf.ParameterGroup([curve])
|
||||
else:
|
||||
parameter_group = deepcopy(parameter_group)
|
||||
parameter_group.parameters[curve.label] = curve
|
||||
return (parameter_group,)
|
||||
|
||||
|
||||
class KfGetCurveFromPGroup:
|
||||
CATEGORY = CATEGORY
|
||||
FUNCTION = "main"
|
||||
RETURN_TYPES = ("KEYFRAMED_CURVE",)
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"curve_label": ("STRING",{"default": "My Curve",}),
|
||||
"parameter_group": ("PARAMETER_GROUP",{"forceInput": True,}),
|
||||
},
|
||||
}
|
||||
|
||||
def main(self, curve_label, parameter_group):
|
||||
curve = parameter_group.parameters[curve_label]
|
||||
return (deepcopy(curve),)
|
||||
|
||||
|
||||
# get curve from parameter group
|
||||
## inputs: pgroup, label
|
||||
## returns curve
|
||||
|
||||
# extract a time slice from the parameter group
|
||||
|
||||
|
||||
##################################################################
|
||||
|
||||
|
||||
##################################################################
|
||||
|
||||
|
||||
@@ -665,14 +639,14 @@ NODE_CLASS_MAPPINGS = {
|
||||
"KfApplyCurveToCond": KfApplyCurveToCond,
|
||||
"KfConditioningAdd": KfConditioningAdd,
|
||||
#######################################
|
||||
"KfAddCurveToPGroup": KfAddCurveToPGroup,
|
||||
"KfGetCurveFromPGroup": KfGetCurveFromPGroup,
|
||||
"KfCurveDraw": KfCurveDraw,
|
||||
"KfPGroupDraw": KfPGroupDraw,
|
||||
"KfSetCurveLabel":KfSetCurveLabel,
|
||||
#######################################
|
||||
#"KfCurveToAcnLatentKeyframe": KfCurveToAcnLatentKeyframe,
|
||||
#######################################
|
||||
#"KfCurveInverse": KfCurveInverse,
|
||||
"KfCurveDraw": KfCurveDraw,
|
||||
"KfPGroupDraw": KfPGroupDraw,
|
||||
|
||||
"KfCurvesAdd": KfCurvesAdd,
|
||||
"KfCurvesSubtract": KfCurvesSubtract,
|
||||
"KfCurvesMultiply": KfCurvesMultiply,
|
||||
@@ -682,7 +656,6 @@ NODE_CLASS_MAPPINGS = {
|
||||
"KfConditioningAddx10":KfConditioningAddx10,
|
||||
"KfCurvesAddx10":KfCurvesAddx10,
|
||||
"KfCurvesMultiplyx10":KfCurvesMultiplyx10,
|
||||
|
||||
}
|
||||
|
||||
# A dictionary that contains the friendly/humanly readable titles for the nodes
|
||||
|
||||
+211
@@ -0,0 +1,211 @@
|
||||
import keyframed as kf
|
||||
import logging
|
||||
from copy import deepcopy
|
||||
|
||||
logging.basicConfig(level=logging.INFO,
|
||||
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
CATEGORY = "keyframed/parameter group"
|
||||
|
||||
|
||||
class KfAddCurveToPGroup:
|
||||
CATEGORY = CATEGORY
|
||||
FUNCTION = "main"
|
||||
#RETURN_TYPES = ("KEYFRAMED_CURVE",)
|
||||
RETURN_TYPES = ("PARAMETER_GROUP",)
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"curve": ("KEYFRAMED_CURVE",{"forceInput": True,}),
|
||||
},
|
||||
"optional": {
|
||||
"parameter_group": ("PARAMETER_GROUP",{"forceInput": True,}),
|
||||
},
|
||||
}
|
||||
|
||||
def main(self, curve, parameter_group=None):
|
||||
curve = deepcopy(curve)
|
||||
if parameter_group is None:
|
||||
#parameter_group = kf.ParameterGroup({curve.label:curve})
|
||||
parameter_group = kf.ParameterGroup([curve])
|
||||
else:
|
||||
parameter_group = deepcopy(parameter_group)
|
||||
parameter_group.parameters[curve.label] = curve
|
||||
return (parameter_group,)
|
||||
|
||||
class KfAddCurveToPGroupx10:
|
||||
CATEGORY = CATEGORY
|
||||
FUNCTION = "main"
|
||||
#RETURN_TYPES = ("KEYFRAMED_CURVE",)
|
||||
RETURN_TYPES = ("PARAMETER_GROUP",)
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"curve0": ("KEYFRAMED_CURVE",{"forceInput": True,}),
|
||||
},
|
||||
"optional": {
|
||||
"parameter_group": ("PARAMETER_GROUP",{"forceInput": True,}),
|
||||
"curve1": ("KEYFRAMED_CURVE",{"forceInput": True,}),
|
||||
"curve2": ("KEYFRAMED_CURVE",{"forceInput": True,}),
|
||||
"curve3": ("KEYFRAMED_CURVE",{"forceInput": True,}),
|
||||
"curve4": ("KEYFRAMED_CURVE",{"forceInput": True,}),
|
||||
"curve5": ("KEYFRAMED_CURVE",{"forceInput": True,}),
|
||||
"curve6": ("KEYFRAMED_CURVE",{"forceInput": True,}),
|
||||
"curve7": ("KEYFRAMED_CURVE",{"forceInput": True,}),
|
||||
"curve8": ("KEYFRAMED_CURVE",{"forceInput": True,}),
|
||||
"curve9": ("KEYFRAMED_CURVE",{"forceInput": True,}),
|
||||
},
|
||||
}
|
||||
|
||||
def main(self, parameter_group=None, **kwargs):
|
||||
if parameter_group is None:
|
||||
#parameter_group = kf.ParameterGroup({curve.label:curve})
|
||||
parameter_group = kf.ParameterGroup(kwargs)
|
||||
else:
|
||||
parameter_group = deepcopy(parameter_group)
|
||||
for curve in parameter_group.values():
|
||||
parameter_group.parameters[curve.label] = curve
|
||||
return (parameter_group,)
|
||||
|
||||
|
||||
class KfGetCurveFromPGroup:
|
||||
CATEGORY = CATEGORY
|
||||
FUNCTION = "main"
|
||||
RETURN_TYPES = ("KEYFRAMED_CURVE",)
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"curve_label": ("STRING",{"default": "My Curve",}),
|
||||
"parameter_group": ("PARAMETER_GROUP",{"forceInput": True,}),
|
||||
},
|
||||
}
|
||||
|
||||
def main(self, curve_label, parameter_group):
|
||||
curve = parameter_group.parameters[curve_label]
|
||||
return (deepcopy(curve),)
|
||||
|
||||
|
||||
##################################################################
|
||||
|
||||
# PGroup Arithmetic
|
||||
|
||||
class KfPGroupCurveAdd:
|
||||
CATEGORY = CATEGORY
|
||||
FUNCTION = "main"
|
||||
RETURN_TYPES = ("PARAMETER_GROUP",)
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"parameter_group": ("PARAMETER_GROUP",{"forceInput": True,}),
|
||||
"curve": ("KEYFRAMED_CURVE",{"forceInput": True,}),
|
||||
},
|
||||
}
|
||||
|
||||
def main(self, parameter_group, curve):
|
||||
parameter_group = deepcopy(parameter_group)
|
||||
curve = deepcopy(curve)
|
||||
return (parameter_group + curve, )
|
||||
|
||||
|
||||
class KfPGroupCurveMultiply:
|
||||
CATEGORY = CATEGORY
|
||||
FUNCTION = "main"
|
||||
RETURN_TYPES = ("PARAMETER_GROUP",)
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"parameter_group": ("PARAMETER_GROUP",{"forceInput": True,}),
|
||||
"curve": ("KEYFRAMED_CURVE",{"forceInput": True,}),
|
||||
},
|
||||
}
|
||||
|
||||
def main(self, parameter_group, curve):
|
||||
parameter_group = deepcopy(parameter_group)
|
||||
curve = deepcopy(curve)
|
||||
return (parameter_group * curve, )
|
||||
|
||||
|
||||
# not behaving as expected :(
|
||||
|
||||
class KfPGroupSum:
|
||||
#CATEGORY = CATEGORY
|
||||
CATEGORY = "keyframed/experimental"
|
||||
FUNCTION = "main"
|
||||
RETURN_TYPES = ("KEYFRAMED_CURVE",)
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"parameter_group": ("PARAMETER_GROUP",{"forceInput": True,}),
|
||||
},
|
||||
}
|
||||
|
||||
def main(self, parameter_group):
|
||||
parameter_group = deepcopy(parameter_group)
|
||||
#curve = kf.Composition(parameter_group, reduction='sum')
|
||||
#curve = sum(parameter_group.parameters.values())
|
||||
outv = kf.Curve(0)
|
||||
for curve in parameter_group.parameters.values():
|
||||
outv += curve
|
||||
return (outv,)
|
||||
#return (curve,)
|
||||
|
||||
|
||||
class KfPGroupProd:
|
||||
pass
|
||||
# CATEGORY = CATEGORY
|
||||
# FUNCTION = "main"
|
||||
# RETURN_TYPES = ("KEYFRAMED_CURVE",)
|
||||
|
||||
# @classmethod
|
||||
# def INPUT_TYPES(s):
|
||||
# return {
|
||||
# "required": {
|
||||
# "parameter_group": ("PARAMETER_GROUP",{"forceInput": True,}),
|
||||
# },
|
||||
# }
|
||||
|
||||
# def main(self, parameter_group):
|
||||
# parameter_group = deepcopy(parameter_group)
|
||||
# curve = kf.Composition(parameter_group, reduction='prod')
|
||||
# return (curve,)
|
||||
|
||||
##################################################################
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
#"KfCurveDraw": KfCurveDraw,
|
||||
#"KfPGroupDraw": KfPGroupDraw,
|
||||
#"KfSetCurveLabel":KfSetCurveLabel,
|
||||
"KfAddCurveToPGroup": KfAddCurveToPGroup,
|
||||
"KfGetCurveFromPGroup": KfGetCurveFromPGroup,
|
||||
"KfAddCurveToPGroupx10": KfAddCurveToPGroupx10,
|
||||
"KfPGroupCurveAdd":KfPGroupCurveAdd,
|
||||
"KfPGroupCurveMultiply":KfPGroupCurveMultiply,
|
||||
"KfPGroupSum": KfPGroupSum,
|
||||
"KfPGroupProd": KfPGroupProd,
|
||||
}
|
||||
|
||||
# A dictionary that contains the friendly/humanly readable titles for the nodes
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
#"KfSetCurveLabel": "Set Curve Label",
|
||||
"KfAddCurveToPGroup": "Add Curve To Parameter Group",
|
||||
"KfGetCurveFromPGroup": "Get Curve From Parameter Group",
|
||||
"KfAddCurveToPGroupx10": "Add Curve To Parameter Group (x10)",
|
||||
"KfPGroupCurveAdd": "Parameter Group + Curve (addition)",
|
||||
"KfPGroupCurveMultiply": "Parameter Group * Curve (multiply)",
|
||||
"KfPGroupSum": "Sum Over Parameter Group",
|
||||
"KfPGroupProd": "Product Over Parameter Group",
|
||||
}
|
||||
+135
-27
@@ -27,9 +27,10 @@ class KfKeyframedCondition:
|
||||
return {
|
||||
"required": {
|
||||
"conditioning": ("CONDITIONING", {}),
|
||||
"time": ("FLOAT", {"default": 0}),
|
||||
"time": ("FLOAT", {"default": 0, "step": 1}),
|
||||
#"weight": ("FLOAT", {"default": 1}), # maybe i should hide this attribute
|
||||
"interpolation_method": (list(kf.interpolation.INTERPOLATORS.keys()),),
|
||||
#"interpolation_method": (list(kf.interpolation.INTERPOLATORS.keys()),),
|
||||
"interpolation_method": (list(kf.interpolation.EASINGS.keys()), {"default":"linear"}),
|
||||
},
|
||||
}
|
||||
|
||||
@@ -52,6 +53,74 @@ class KfKeyframedCondition:
|
||||
return ({"kf_cond_t":kf_cond_t, "kf_cond_pooled":kf_cond_pooled, "cond_dict":cond_dict},)
|
||||
|
||||
|
||||
def set_keyframed_condition(keyframed_condition, schedule=None):
|
||||
keyframed_condition = deepcopy(keyframed_condition)
|
||||
cond_dict = keyframed_condition.pop("cond_dict")
|
||||
#cond_dict = deepcopy(cond_dict)
|
||||
|
||||
if schedule is None:
|
||||
# get a new copy of the tensor
|
||||
kf_cond_t = keyframed_condition["kf_cond_t"]
|
||||
#kf_cond_t.value = kf_cond_t.value.clone() # should be redundant with the deepcopy
|
||||
curve_tokenized = kf.Curve([kf_cond_t], label="kf_cond_t")
|
||||
curves = [curve_tokenized]
|
||||
if keyframed_condition["kf_cond_pooled"] is not None:
|
||||
kf_cond_pooled = keyframed_condition["kf_cond_pooled"]
|
||||
curve_pooled = kf.Curve([kf_cond_pooled], label="kf_cond_pooled")
|
||||
curves.append(curve_pooled)
|
||||
schedule = (kf.ParameterGroup(curves), cond_dict)
|
||||
else:
|
||||
schedule = deepcopy(schedule)
|
||||
schedule, old_cond_dict = schedule
|
||||
for k, v in keyframed_condition.items():
|
||||
if (v is not None):
|
||||
# for now, assume we already have a schedule for k.
|
||||
# Not sure how to handle new conditioning type appearing.
|
||||
schedule.parameters[k][v.t] = v
|
||||
old_cond_dict.update(cond_dict) # NB: mutating this is probably bad
|
||||
schedule = (schedule, old_cond_dict)
|
||||
return schedule
|
||||
|
||||
|
||||
class KfKeyframedConditionWithText(KfKeyframedCondition):
|
||||
"""
|
||||
Attaches a condition to a keyframe
|
||||
"""
|
||||
CATEGORY=CATEGORY
|
||||
FUNCTION = 'main'
|
||||
RETURN_TYPES = ("KEYFRAMED_CONDITION","CONDITIONING", "SCHEDULE")
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
#"conditioning": ("CONDITIONING", {}),
|
||||
"clip": ("CLIP",),
|
||||
"text": ("STRING", {
|
||||
"multiline": True,
|
||||
"default": ""
|
||||
}),
|
||||
"time": ("FLOAT", {"default": 0, "step": 1}),
|
||||
#"weight": ("FLOAT", {"default": 1}), # maybe i should hide this attribute
|
||||
#"interpolation_method": (list(kf.interpolation.INTERPOLATORS.keys()),),
|
||||
"interpolation_method": (list(kf.interpolation.EASINGS.keys()), {"default":"linear"}),
|
||||
},
|
||||
"optional": {
|
||||
"schedule": ("SCHEDULE", {}),
|
||||
}
|
||||
}
|
||||
|
||||
#def main(self, conditioning, time, interpolation_method):
|
||||
def main(self, clip, text, time, interpolation_method, schedule=None):
|
||||
tokens = clip.tokenize(text)
|
||||
cond, pooled = clip.encode_from_tokens(tokens, return_pooled=True)
|
||||
conditioning = [[cond, {"pooled_output": pooled}]]
|
||||
keyframed_condition = super().main(conditioning, time, interpolation_method)[0]
|
||||
keyframed_condition["kf_cond_t"].label = text # attach prompt string as a label to the xattn keyframe for drawing
|
||||
schedule = set_keyframed_condition(keyframed_condition, schedule)
|
||||
return (keyframed_condition, conditioning, schedule)
|
||||
|
||||
|
||||
class KfSetKeyframe:
|
||||
CATEGORY=CATEGORY
|
||||
FUNCTION = 'main'
|
||||
@@ -68,31 +137,7 @@ class KfSetKeyframe:
|
||||
}
|
||||
}
|
||||
def main(self, keyframed_condition, schedule=None):
|
||||
keyframed_condition = deepcopy(keyframed_condition)
|
||||
cond_dict = keyframed_condition.pop("cond_dict")
|
||||
#cond_dict = deepcopy(cond_dict)
|
||||
|
||||
if schedule is None:
|
||||
# get a new copy of the tensor
|
||||
kf_cond_t = keyframed_condition["kf_cond_t"]
|
||||
#kf_cond_t.value = kf_cond_t.value.clone() # should be redundant with the deepcopy
|
||||
curve_tokenized = kf.Curve([kf_cond_t], label="kf_cond_t")
|
||||
curves = [curve_tokenized]
|
||||
if keyframed_condition["kf_cond_pooled"] is not None:
|
||||
kf_cond_pooled = keyframed_condition["kf_cond_pooled"]
|
||||
curve_pooled = kf.Curve([kf_cond_pooled], label="kf_cond_pooled")
|
||||
curves.append(curve_pooled)
|
||||
schedule = (kf.ParameterGroup(curves), cond_dict)
|
||||
else:
|
||||
schedule = deepcopy(schedule)
|
||||
schedule, old_cond_dict = schedule
|
||||
for k, v in keyframed_condition.items():
|
||||
if (v is not None):
|
||||
# for now, assume we already have a schedule for k.
|
||||
# Not sure how to handle new conditioning type appearing.
|
||||
schedule.parameters[k][v.t] = v
|
||||
old_cond_dict.update(cond_dict) # NB: mutating this is probably bad
|
||||
schedule = (schedule, old_cond_dict)
|
||||
schedule = set_keyframed_condition(keyframed_condition, schedule)
|
||||
return (schedule,)
|
||||
|
||||
|
||||
@@ -163,15 +208,78 @@ class KfGetScheduleConditionSlice:
|
||||
|
||||
###################################################################
|
||||
|
||||
from toolz.itertoolz import sliding_window
|
||||
|
||||
def schedule_to_weight_curves(schedule):
|
||||
"""
|
||||
Given a keyfrmaed curve, returns a curve for each keyframe giving the contribution
|
||||
of that keyframe to the schedule. Motivation here is to facilitate plotting
|
||||
"""
|
||||
schedule, _ = schedule
|
||||
schedule = schedule.parameters["kf_cond_t"]
|
||||
schedule = deepcopy(schedule)
|
||||
curves = []
|
||||
keyframes = list(schedule._data.values())
|
||||
if len(keyframes) == 1:
|
||||
keyframe = keyframes[0]
|
||||
#curves = kf.ParameterGroup({kf.label: 1})
|
||||
curves = kf.ParameterGroup({keyframe.label: keyframe.Curve(1)})
|
||||
return curves
|
||||
for (frame_in, frame_curr, frame_out) in sliding_window(3, keyframes):
|
||||
frame_in.value, frame_curr.value, frame_out.value = 0,1,0
|
||||
c = kf.Curve({frame_in.t:frame_in, frame_curr.t:frame_curr, frame_out.t:frame_out},
|
||||
label=frame_curr.label)
|
||||
c = deepcopy(c)
|
||||
curves.append(c)
|
||||
begin, end = keyframes[:2], keyframes[-2:]
|
||||
#outv = [begin]
|
||||
begin[0].value = 1
|
||||
begin[1].value = 0
|
||||
end[0].value = 0
|
||||
end[1].value = 1
|
||||
outv = [kf.Curve(begin, label=begin[0].label)]
|
||||
if len(keyframes) == 2:
|
||||
return outv
|
||||
outv += curves
|
||||
outv += [kf.Curve(end, label=end[1].label)]
|
||||
return kf.ParameterGroup(outv)
|
||||
|
||||
from .core import plot_curve
|
||||
|
||||
class KfDrawSchedule:
|
||||
CATEGORY=CATEGORY
|
||||
FUNCTION = 'main'
|
||||
RETURN_TYPES = ("IMAGE",)
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"schedule": ("SCHEDULE", {"forceInput": True,}),
|
||||
"n": ("INT", {"default": 64}),
|
||||
"show_legend": ("BOOLEAN", {"default": True}),
|
||||
}
|
||||
}
|
||||
|
||||
def main(self, schedule, n, show_legend):
|
||||
curves = schedule_to_weight_curves(schedule)
|
||||
img_tensor = plot_curve(curves, n, show_legend, is_pgroup=True)
|
||||
return (img_tensor,)
|
||||
|
||||
###################################################################
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"KfKeyframedCondition": KfKeyframedCondition,
|
||||
"KfKeyframedConditionWithText":KfKeyframedConditionWithText,
|
||||
"KfSetKeyframe": KfSetKeyframe,
|
||||
"KfGetScheduleConditionAtTime": KfGetScheduleConditionAtTime,
|
||||
"KfGetScheduleConditionSlice": KfGetScheduleConditionSlice,
|
||||
"KfDrawSchedule": KfDrawSchedule,
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"KfKeyframedCondition": "Keyframed Condition",
|
||||
"KfKeyframedConditionWithText": "Schedule Prompt",
|
||||
"KfSetKeyframe": "Set Keyframe",
|
||||
"KfGetScheduleConditionAtTime": "Evaluate Schedule At T",
|
||||
"KfGetScheduleConditionSlice": "Evaluate Schedule At T (Batch)",
|
||||
|
||||
Reference in New Issue
Block a user