Files
Scholar01-ComfyUI-Keyframe/keyframe/nodes.py
T
2023-12-09 01:36:52 +08:00

169 lines
5.5 KiB
Python

import numpy as np
from .interface import KeyframePartGroup, KeyframePart, ModelInjectParam
from .sampling import keyframe_sample_factory
from .util import inject_model
from .samples import inject_samples
import comfy.sample as comfy_sample
inject_samples()
comfy_sample.sample = keyframe_sample_factory(comfy_sample.sample)
class KeyframePartNode:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"image": ("IMAGE",),
"batch_index": ("INT", {"default": 0, "min": 0, "max": 9999, "step": 1}),
"denoise": ("FLOAT", {"default": 0.1, "min": 0.001, "max": 1.0, "step": 0.01}),
},
"optional": {
"part": ("LATENT_KEYFRAME_PART",),
}
}
RETURN_TYPES = ("LATENT_KEYFRAME_PART",)
RETURN_NAMES = ("part",)
FUNCTION = "load_keyframe_part"
CATEGORY = "KeyframePart"
def load_keyframe_part(self, image, batch_index, denoise, part=None):
if not part:
part = KeyframePartGroup()
part = part.clone()
keyframe = KeyframePart(batch_index, image, denoise)
part.add(keyframe)
return (part,)
class KeyframeInterpolationPartNode:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"image": ("IMAGE",),
"batch_index_from": ("INT", {"default": 0, "min": 0, "max": 9999, "step": 1}),
"batch_index_to": ("INT", {"default": 4, "min": 1, "max": 9999, "step": 1}),
"denoise_from": ("FLOAT", {"default": 0.1, "min": 0.001, "max": 1.0, "step": 0.01}),
"denoise_to": ("FLOAT", {"default": 1.0, "min": 0.001, "max": 1.0, "step": 0.01}),
"interpolation": (["linear", "ease-in", "ease-out", "ease-in-out"],),
},
"optional": {
"part": ("LATENT_KEYFRAME_PART",),
}
}
RETURN_TYPES = ("LATENT_KEYFRAME_PART",)
RETURN_NAMES = ("part",)
FUNCTION = "load_keyframe_part"
CATEGORY = "KeyframeInterpolationPartNode"
def load_keyframe_part(self,
image,
batch_index_from,
batch_index_to,
denoise_from,
denoise_to,
interpolation,
part=None):
if batch_index_from >= batch_index_to:
raise ValueError("batch_index_from must be less than batch_index_to")
if not part:
part = KeyframePartGroup()
part = part.clone()
current_group = KeyframePartGroup()
steps = batch_index_to - batch_index_from
diff = denoise_to - denoise_from
if interpolation == "linear":
weights = np.linspace(denoise_from, denoise_to, steps)
elif interpolation == "ease-in":
index = np.linspace(0, 1, steps)
weights = diff * np.power(index, 2) + denoise_from
elif interpolation == "ease-out":
index = np.linspace(0, 1, steps)
weights = diff * (1 - np.power(1 - index, 2)) + denoise_from
elif interpolation == "ease-in-out":
index = np.linspace(0, 1, steps)
weights = diff * ((1 - np.cos(index * np.pi)) / 2) + denoise_from
for i in range(steps):
keyframe = KeyframePart(batch_index_from + i, image, float(weights[i]))
current_group.add(keyframe)
# replace values with prev_latent_keyframes
for latent_keyframe in part.keyframes:
current_group.add(latent_keyframe)
return (current_group,)
class KeyframeApplyNode:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"model": ("MODEL",),
"latent": ("LATENT",),
"part_group": ("LATENT_KEYFRAME_PART",),
"vae": ("VAE",),
}
}
RETURN_TYPES = ("MODEL", "LATENT",)
RETURN_NAMES = ("model", "latent",)
FUNCTION = "apply_latent_keyframe"
CATEGORY = "LatentKeyframeApply"
@staticmethod
def vae_encode_crop_pixels(pixels):
x = (pixels.shape[1] // 8) * 8
y = (pixels.shape[2] // 8) * 8
if pixels.shape[1] != x or pixels.shape[2] != y:
x_offset = (pixels.shape[1] % 8) // 2
y_offset = (pixels.shape[2] % 8) // 2
pixels = pixels[:, x_offset:x + x_offset, y_offset:y + y_offset, :]
return pixels
def encode(self, vae, pixels):
pixels = self.vae_encode_crop_pixels(pixels)
t = vae.encode(pixels[:, :, :, :3])
return t
def apply_latent_keyframe(self, model, latent, part_group: KeyframePartGroup, vae):
# 预处理latent,把图片替换进去
for part in part_group.keyframes:
latent['samples'][part.batch_index] = self.encode(vae, part.image)
print(f"apply keyframe {part.batch_index}:{part.denoise}")
# 注入参数,后续处理
inject_param = ModelInjectParam(part_group, latent)
inject_model(model.model, inject_param)
return (model, latent,)
# NODE MAPPING
NODE_CLASS_MAPPINGS = {
# Keyframes
"KeyframePart": KeyframePartNode,
"KeyframeInterpolationPart": KeyframeInterpolationPartNode,
"KeyframeApply": KeyframeApplyNode,
}
NODE_DISPLAY_NAME_MAPPINGS = {
# Keyframes
"KeyframePart": "Keyframe Part",
"KeyframeInterpolationPart": "Keyframe Interpolation Part",
"KeyframeApply": "Keyframe Apply",
}