|
from typing import Union |
|
import numpy as np |
|
from collections.abc import Iterable |
|
|
|
from .utils import ControlWeights, TimestepKeyframe, TimestepKeyframeGroup, LatentKeyframe, LatentKeyframeGroup, BIGMIN, BIGMAX |
|
from .utils import StrengthInterpolation as SI |
|
from .logger import logger |
|
|
|
|
|
class TimestepKeyframeNode: |
|
OUTDATED_DUMMY = -39 |
|
|
|
@classmethod |
|
def INPUT_TYPES(s): |
|
return { |
|
"required": { |
|
"start_percent": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.001}, ), |
|
}, |
|
"optional": { |
|
"prev_timestep_kf": ("TIMESTEP_KEYFRAME", ), |
|
"strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), |
|
"cn_weights": ("CONTROL_NET_WEIGHTS", ), |
|
"latent_keyframe": ("LATENT_KEYFRAME", ), |
|
"null_latent_kf_strength": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), |
|
"inherit_missing": ("BOOLEAN", {"default": True}, ), |
|
"guarantee_steps": ("INT", {"default": 1, "min": 0, "max": BIGMAX}), |
|
"mask_optional": ("MASK", ), |
|
}, |
|
"hidden": { |
|
"autosize": ("ACNAUTOSIZE", {"padding": 0}), |
|
} |
|
} |
|
|
|
RETURN_NAMES = ("TIMESTEP_KF", ) |
|
RETURN_TYPES = ("TIMESTEP_KEYFRAME", ) |
|
FUNCTION = "load_keyframe" |
|
|
|
CATEGORY = "Adv-ControlNet ππ
π
π
/keyframes" |
|
|
|
def load_keyframe(self, |
|
start_percent: float, |
|
strength: float=1.0, |
|
cn_weights: ControlWeights=None, control_net_weights: ControlWeights=None, |
|
latent_keyframe: LatentKeyframeGroup=None, |
|
prev_timestep_kf: TimestepKeyframeGroup=None, prev_timestep_keyframe: TimestepKeyframeGroup=None, |
|
null_latent_kf_strength: float=0.0, |
|
inherit_missing=True, |
|
guarantee_steps=OUTDATED_DUMMY, |
|
guarantee_usage=True, |
|
mask_optional=None,): |
|
|
|
if guarantee_steps == self.OUTDATED_DUMMY: |
|
guarantee_steps = int(guarantee_usage) |
|
control_net_weights = control_net_weights if control_net_weights else cn_weights |
|
prev_timestep_keyframe = prev_timestep_keyframe if prev_timestep_keyframe else prev_timestep_kf |
|
if not prev_timestep_keyframe: |
|
prev_timestep_keyframe = TimestepKeyframeGroup() |
|
else: |
|
prev_timestep_keyframe = prev_timestep_keyframe.clone() |
|
keyframe = TimestepKeyframe(start_percent=start_percent, strength=strength, null_latent_kf_strength=null_latent_kf_strength, |
|
control_weights=control_net_weights, latent_keyframes=latent_keyframe, inherit_missing=inherit_missing, |
|
guarantee_steps=guarantee_steps, mask_hint_orig=mask_optional) |
|
prev_timestep_keyframe.add(keyframe) |
|
return (prev_timestep_keyframe,) |
|
|
|
|
|
class TimestepKeyframeInterpolationNode: |
|
@classmethod |
|
def INPUT_TYPES(s): |
|
return { |
|
"required": { |
|
"start_percent": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.001},), |
|
"end_percent": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.001}), |
|
"strength_start": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001},), |
|
"strength_end": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001},), |
|
"interpolation": (SI._LIST, ), |
|
"intervals": ("INT", {"default": 50, "min": 2, "max": 100, "step": 1}), |
|
}, |
|
"optional": { |
|
"prev_timestep_kf": ("TIMESTEP_KEYFRAME", ), |
|
"cn_weights": ("CONTROL_NET_WEIGHTS", ), |
|
"latent_keyframe": ("LATENT_KEYFRAME", ), |
|
"null_latent_kf_strength": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 10.0, "step": 0.001},), |
|
"inherit_missing": ("BOOLEAN", {"default": True},), |
|
"mask_optional": ("MASK", ), |
|
"print_keyframes": ("BOOLEAN", {"default": False}), |
|
}, |
|
"hidden": { |
|
"autosize": ("ACNAUTOSIZE", {"padding": 0}), |
|
} |
|
} |
|
|
|
RETURN_NAMES = ("TIMESTEP_KF", ) |
|
RETURN_TYPES = ("TIMESTEP_KEYFRAME", ) |
|
FUNCTION = "load_keyframe" |
|
|
|
CATEGORY = "Adv-ControlNet ππ
π
π
/keyframes" |
|
|
|
def load_keyframe(self, |
|
start_percent: float, end_percent: float, |
|
strength_start: float, strength_end: float, interpolation: str, intervals: int, |
|
cn_weights: ControlWeights=None, |
|
latent_keyframe: LatentKeyframeGroup=None, |
|
prev_timestep_kf: TimestepKeyframeGroup=None, |
|
null_latent_kf_strength: float=0.0, |
|
inherit_missing=True, |
|
guarantee_steps=1, |
|
mask_optional=None, print_keyframes=False): |
|
if not prev_timestep_kf: |
|
prev_timestep_kf = TimestepKeyframeGroup() |
|
else: |
|
prev_timestep_kf = prev_timestep_kf.clone() |
|
|
|
percents = SI.get_weights(num_from=start_percent, num_to=end_percent, length=intervals, method=SI.LINEAR) |
|
strengths = SI.get_weights(num_from=strength_start, num_to=strength_end, length=intervals, method=interpolation) |
|
|
|
is_first = True |
|
for percent, strength in zip(percents, strengths): |
|
guarantee_steps = 0 |
|
if is_first: |
|
guarantee_steps = 1 |
|
is_first = False |
|
prev_timestep_kf.add(TimestepKeyframe(start_percent=percent, strength=strength, null_latent_kf_strength=null_latent_kf_strength, |
|
control_weights=cn_weights, latent_keyframes=latent_keyframe, inherit_missing=inherit_missing, |
|
guarantee_steps=guarantee_steps, mask_hint_orig=mask_optional)) |
|
if print_keyframes: |
|
logger.info(f"TimestepKeyframe - start_percent:{percent} = {strength}") |
|
return (prev_timestep_kf,) |
|
|
|
|
|
class TimestepKeyframeFromStrengthListNode: |
|
@classmethod |
|
def INPUT_TYPES(s): |
|
return { |
|
"required": { |
|
"float_strengths": ("FLOAT", {"default": -1, "min": -1, "step": 0.001, "forceInput": True}), |
|
"start_percent": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.001},), |
|
"end_percent": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.001}), |
|
}, |
|
"optional": { |
|
"prev_timestep_kf": ("TIMESTEP_KEYFRAME", ), |
|
"cn_weights": ("CONTROL_NET_WEIGHTS", ), |
|
"latent_keyframe": ("LATENT_KEYFRAME", ), |
|
"null_latent_kf_strength": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 10.0, "step": 0.001},), |
|
"inherit_missing": ("BOOLEAN", {"default": True},), |
|
"mask_optional": ("MASK", ), |
|
"print_keyframes": ("BOOLEAN", {"default": False}), |
|
}, |
|
"hidden": { |
|
"autosize": ("ACNAUTOSIZE", {"padding": 0}), |
|
} |
|
} |
|
|
|
RETURN_NAMES = ("TIMESTEP_KF", ) |
|
RETURN_TYPES = ("TIMESTEP_KEYFRAME", ) |
|
FUNCTION = "load_keyframe" |
|
|
|
CATEGORY = "Adv-ControlNet ππ
π
π
/keyframes" |
|
|
|
def load_keyframe(self, |
|
start_percent: float, end_percent: float, |
|
float_strengths: float, |
|
cn_weights: ControlWeights=None, |
|
latent_keyframe: LatentKeyframeGroup=None, |
|
prev_timestep_kf: TimestepKeyframeGroup=None, |
|
null_latent_kf_strength: float=0.0, |
|
inherit_missing=True, |
|
guarantee_steps=1, |
|
mask_optional=None, print_keyframes=False): |
|
if not prev_timestep_kf: |
|
prev_timestep_kf = TimestepKeyframeGroup() |
|
else: |
|
prev_timestep_kf = prev_timestep_kf.clone() |
|
|
|
if type(float_strengths) in (float, int): |
|
float_strengths = [float(float_strengths)] |
|
elif isinstance(float_strengths, Iterable): |
|
pass |
|
else: |
|
raise Exception(f"strengths_float must be either an iterable input or a float, but was {type(float_strengths).__repr__}.") |
|
percents = SI.get_weights(num_from=start_percent, num_to=end_percent, length=len(float_strengths), method=SI.LINEAR) |
|
|
|
is_first = True |
|
for percent, strength in zip(percents, float_strengths): |
|
guarantee_steps = 0 |
|
if is_first: |
|
guarantee_steps = 1 |
|
is_first = False |
|
prev_timestep_kf.add(TimestepKeyframe(start_percent=percent, strength=strength, null_latent_kf_strength=null_latent_kf_strength, |
|
control_weights=cn_weights, latent_keyframes=latent_keyframe, inherit_missing=inherit_missing, |
|
guarantee_steps=guarantee_steps, mask_hint_orig=mask_optional)) |
|
if print_keyframes: |
|
logger.info(f"TimestepKeyframe - start_percent:{percent} = {strength}") |
|
return (prev_timestep_kf,) |
|
|
|
|
|
class LatentKeyframeNode: |
|
@classmethod |
|
def INPUT_TYPES(s): |
|
return { |
|
"required": { |
|
"batch_index": ("INT", {"default": 0, "min": BIGMIN, "max": BIGMAX, "step": 1}), |
|
"strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), |
|
}, |
|
"optional": { |
|
"prev_latent_kf": ("LATENT_KEYFRAME", ), |
|
}, |
|
"hidden": { |
|
"autosize": ("ACNAUTOSIZE", {"padding": 0}), |
|
} |
|
} |
|
|
|
RETURN_NAMES = ("LATENT_KF", ) |
|
RETURN_TYPES = ("LATENT_KEYFRAME", ) |
|
FUNCTION = "load_keyframe" |
|
|
|
CATEGORY = "Adv-ControlNet ππ
π
π
/keyframes" |
|
|
|
def load_keyframe(self, |
|
batch_index: int, |
|
strength: float, |
|
prev_latent_kf: LatentKeyframeGroup=None, |
|
prev_latent_keyframe: LatentKeyframeGroup=None, |
|
): |
|
prev_latent_keyframe = prev_latent_keyframe if prev_latent_keyframe else prev_latent_kf |
|
if not prev_latent_keyframe: |
|
prev_latent_keyframe = LatentKeyframeGroup() |
|
else: |
|
prev_latent_keyframe = prev_latent_keyframe.clone() |
|
keyframe = LatentKeyframe(batch_index, strength) |
|
prev_latent_keyframe.add(keyframe) |
|
return (prev_latent_keyframe,) |
|
|
|
|
|
class LatentKeyframeGroupNode: |
|
@classmethod |
|
def INPUT_TYPES(s): |
|
return { |
|
"required": { |
|
"index_strengths": ("STRING", {"multiline": True, "default": ""}), |
|
}, |
|
"optional": { |
|
"prev_latent_kf": ("LATENT_KEYFRAME", ), |
|
"latent_optional": ("LATENT", ), |
|
"print_keyframes": ("BOOLEAN", {"default": False}), |
|
}, |
|
"hidden": { |
|
"autosize": ("ACNAUTOSIZE", {"padding": 0}), |
|
} |
|
} |
|
|
|
RETURN_NAMES = ("LATENT_KF", ) |
|
RETURN_TYPES = ("LATENT_KEYFRAME", ) |
|
FUNCTION = "load_keyframes" |
|
|
|
CATEGORY = "Adv-ControlNet ππ
π
π
/keyframes" |
|
|
|
def validate_index(self, index: int, latent_count: int = 0, is_range: bool = False, allow_negative = False) -> int: |
|
|
|
if is_range: |
|
return index |
|
|
|
|
|
if latent_count > 0 and index > latent_count-1: |
|
raise IndexError(f"Index '{index}' out of range for the total {latent_count} latents.") |
|
|
|
if index < 0: |
|
if not allow_negative: |
|
raise IndexError(f"Negative indeces not allowed, but was {index}.") |
|
conv_index = latent_count+index |
|
if conv_index < 0: |
|
raise IndexError(f"Index '{index}', converted to '{conv_index}' out of range for the total {latent_count} latents.") |
|
index = conv_index |
|
return index |
|
|
|
def convert_to_index_int(self, raw_index: str, latent_count: int = 0, is_range: bool = False, allow_negative = False) -> int: |
|
try: |
|
return self.validate_index(int(raw_index), latent_count=latent_count, is_range=is_range, allow_negative=allow_negative) |
|
except ValueError as e: |
|
raise ValueError(f"index '{raw_index}' must be an integer.", e) |
|
|
|
def convert_to_latent_keyframes(self, latent_indeces: str, latent_count: int) -> set[LatentKeyframe]: |
|
if not latent_indeces: |
|
return set() |
|
int_latent_indeces = [i for i in range(0, latent_count)] |
|
allow_negative = latent_count > 0 |
|
chosen_indeces = set() |
|
|
|
groups = latent_indeces.split(",") |
|
groups = [g.strip() for g in groups] |
|
for g in groups: |
|
|
|
strength = 1.0 |
|
if '=' in g: |
|
g, strength_str = g.split("=", 1) |
|
g = g.strip() |
|
try: |
|
strength = float(strength_str.strip()) |
|
except ValueError as e: |
|
raise ValueError(f"strength '{strength_str}' must be a float.", e) |
|
if strength < 0: |
|
raise ValueError(f"Strength '{strength}' cannot be negative.") |
|
|
|
if ':' in g: |
|
index_range = g.split(":", 1) |
|
index_range = [r.strip() for r in index_range] |
|
start_index = self.convert_to_index_int(index_range[0], latent_count=latent_count, is_range=True, allow_negative=allow_negative) |
|
end_index = self.convert_to_index_int(index_range[1], latent_count=latent_count, is_range=True, allow_negative=allow_negative) |
|
|
|
if len(int_latent_indeces) > 0: |
|
for i in int_latent_indeces[start_index:end_index]: |
|
chosen_indeces.add(LatentKeyframe(i, strength)) |
|
|
|
else: |
|
for i in range(start_index, end_index): |
|
chosen_indeces.add(LatentKeyframe(i, strength)) |
|
|
|
else: |
|
chosen_indeces.add(LatentKeyframe(self.convert_to_index_int(g, latent_count=latent_count, allow_negative=allow_negative), strength)) |
|
return chosen_indeces |
|
|
|
def load_keyframes(self, |
|
index_strengths: str, |
|
prev_latent_kf: LatentKeyframeGroup=None, |
|
prev_latent_keyframe: LatentKeyframeGroup=None, |
|
latent_image_opt=None, |
|
print_keyframes=False): |
|
prev_latent_keyframe = prev_latent_keyframe if prev_latent_keyframe else prev_latent_kf |
|
if not prev_latent_keyframe: |
|
prev_latent_keyframe = LatentKeyframeGroup() |
|
else: |
|
prev_latent_keyframe = prev_latent_keyframe.clone() |
|
curr_latent_keyframe = LatentKeyframeGroup() |
|
|
|
latent_count = -1 |
|
if latent_image_opt: |
|
latent_count = latent_image_opt['samples'].size()[0] |
|
latent_keyframes = self.convert_to_latent_keyframes(index_strengths, latent_count=latent_count) |
|
|
|
for latent_keyframe in latent_keyframes: |
|
curr_latent_keyframe.add(latent_keyframe) |
|
|
|
if print_keyframes: |
|
for keyframe in curr_latent_keyframe.keyframes: |
|
logger.info(f"LatentKeyframe {keyframe.batch_index}={keyframe.strength}") |
|
|
|
|
|
for latent_keyframe in prev_latent_keyframe.keyframes: |
|
curr_latent_keyframe.add(latent_keyframe) |
|
|
|
return (curr_latent_keyframe,) |
|
|
|
|
|
class LatentKeyframeInterpolationNode: |
|
@classmethod |
|
def INPUT_TYPES(s): |
|
return { |
|
"required": { |
|
"batch_index_from": ("INT", {"default": 0, "min": BIGMIN, "max": BIGMAX, "step": 1}), |
|
"batch_index_to_excl": ("INT", {"default": 0, "min": BIGMIN, "max": BIGMAX, "step": 1}), |
|
"strength_from": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), |
|
"strength_to": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), |
|
"interpolation": (SI._LIST, ), |
|
}, |
|
"optional": { |
|
"prev_latent_kf": ("LATENT_KEYFRAME", ), |
|
"print_keyframes": ("BOOLEAN", {"default": False}), |
|
}, |
|
"hidden": { |
|
"autosize": ("ACNAUTOSIZE", {"padding": 0}), |
|
} |
|
} |
|
|
|
RETURN_NAMES = ("LATENT_KF", ) |
|
RETURN_TYPES = ("LATENT_KEYFRAME", ) |
|
FUNCTION = "load_keyframe" |
|
CATEGORY = "Adv-ControlNet ππ
π
π
/keyframes" |
|
|
|
def load_keyframe(self, |
|
batch_index_from: int, |
|
strength_from: float, |
|
batch_index_to_excl: int, |
|
strength_to: float, |
|
interpolation: str, |
|
prev_latent_kf: LatentKeyframeGroup=None, |
|
prev_latent_keyframe: LatentKeyframeGroup=None, |
|
print_keyframes=False): |
|
|
|
if (batch_index_from > batch_index_to_excl): |
|
raise ValueError("batch_index_from must be less than or equal to batch_index_to.") |
|
|
|
if (batch_index_from < 0 and batch_index_to_excl >= 0): |
|
raise ValueError("batch_index_from and batch_index_to must be either both positive or both negative.") |
|
|
|
prev_latent_keyframe = prev_latent_keyframe if prev_latent_keyframe else prev_latent_kf |
|
if not prev_latent_keyframe: |
|
prev_latent_keyframe = LatentKeyframeGroup() |
|
else: |
|
prev_latent_keyframe = prev_latent_keyframe.clone() |
|
curr_latent_keyframe = LatentKeyframeGroup() |
|
|
|
steps = batch_index_to_excl - batch_index_from |
|
diff = strength_to - strength_from |
|
if interpolation == SI.LINEAR: |
|
weights = np.linspace(strength_from, strength_to, steps) |
|
elif interpolation == SI.EASE_IN: |
|
index = np.linspace(0, 1, steps) |
|
weights = diff * np.power(index, 2) + strength_from |
|
elif interpolation == SI.EASE_OUT: |
|
index = np.linspace(0, 1, steps) |
|
weights = diff * (1 - np.power(1 - index, 2)) + strength_from |
|
elif interpolation == SI.EASE_IN_OUT: |
|
index = np.linspace(0, 1, steps) |
|
weights = diff * ((1 - np.cos(index * np.pi)) / 2) + strength_from |
|
|
|
for i in range(steps): |
|
keyframe = LatentKeyframe(batch_index_from + i, float(weights[i])) |
|
curr_latent_keyframe.add(keyframe) |
|
|
|
if print_keyframes: |
|
for keyframe in curr_latent_keyframe.keyframes: |
|
logger.info(f"LatentKeyframe {keyframe.batch_index}={keyframe.strength}") |
|
|
|
|
|
for latent_keyframe in prev_latent_keyframe.keyframes: |
|
curr_latent_keyframe.add(latent_keyframe) |
|
|
|
return (curr_latent_keyframe,) |
|
|
|
|
|
class LatentKeyframeBatchedGroupNode: |
|
@classmethod |
|
def INPUT_TYPES(s): |
|
return { |
|
"required": { |
|
"float_strengths": ("FLOAT", {"default": -1, "min": -1, "step": 0.001, "forceInput": True}), |
|
}, |
|
"optional": { |
|
"prev_latent_kf": ("LATENT_KEYFRAME", ), |
|
"print_keyframes": ("BOOLEAN", {"default": False}), |
|
}, |
|
"hidden": { |
|
"autosize": ("ACNAUTOSIZE", {"padding": 0}), |
|
} |
|
} |
|
|
|
RETURN_NAMES = ("LATENT_KF", ) |
|
RETURN_TYPES = ("LATENT_KEYFRAME", ) |
|
FUNCTION = "load_keyframe" |
|
CATEGORY = "Adv-ControlNet ππ
π
π
/keyframes" |
|
|
|
def load_keyframe(self, float_strengths: Union[float, list[float]], |
|
prev_latent_kf: LatentKeyframeGroup=None, |
|
prev_latent_keyframe: LatentKeyframeGroup=None, |
|
print_keyframes=False): |
|
prev_latent_keyframe = prev_latent_keyframe if prev_latent_keyframe else prev_latent_kf |
|
if not prev_latent_keyframe: |
|
prev_latent_keyframe = LatentKeyframeGroup() |
|
else: |
|
prev_latent_keyframe = prev_latent_keyframe.clone() |
|
curr_latent_keyframe = LatentKeyframeGroup() |
|
|
|
|
|
if type(float_strengths) in (float, int): |
|
logger.info("No batched float_strengths passed into Latent Keyframe Batch Group node; will not create any new keyframes.") |
|
|
|
elif isinstance(float_strengths, Iterable): |
|
for idx, strength in enumerate(float_strengths): |
|
keyframe = LatentKeyframe(idx, strength) |
|
curr_latent_keyframe.add(keyframe) |
|
else: |
|
raise ValueError(f"Expected strengths to be an iterable input, but was {type(float_strengths).__repr__}.") |
|
|
|
if print_keyframes: |
|
for keyframe in curr_latent_keyframe.keyframes: |
|
logger.info(f"LatentKeyframe {keyframe.batch_index}={keyframe.strength}") |
|
|
|
|
|
for latent_keyframe in prev_latent_keyframe.keyframes: |
|
curr_latent_keyframe.add(latent_keyframe) |
|
|
|
return (curr_latent_keyframe,) |
|
|