Merge pull request #255 from Kosinkadink/refactor/v3-node-api

Migrate all nodes to the ComfyUI V3 API
This commit is contained in:
Jedrzej Kosinski
2026-07-17 20:40:57 -07:00
committed by GitHub
10 changed files with 1126 additions and 1155 deletions
+5 -3
View File
@@ -1,8 +1,10 @@
from .adv_control.nodes import NODE_CLASS_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS
from .adv_control.nodes import AdvancedControlNetExtension
from .adv_control.dinklink import init_dinklink
from .adv_control.sampling import prepare_dinklink_acn_wrapper
__all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS']
init_dinklink()
prepare_dinklink_acn_wrapper()
async def comfy_entrypoint() -> AdvancedControlNetExtension:
return AdvancedControlNetExtension()
+52 -122
View File
@@ -1,4 +1,4 @@
import comfy.sample
from comfy_api.latest import ComfyExtension, io
from .nodes_main import (ControlNetLoaderAdvanced, DiffControlNetLoaderAdvanced, AnimaLLLiteLoaderAdvanced,
AdvancedControlNetApply, AdvancedControlNetApplySingle)
@@ -12,132 +12,62 @@ from .nodes_sparsectrl import SparseCtrlMergedLoaderAdvanced, SparseCtrlLoaderAd
from .nodes_reference import ReferenceControlNetNode, ReferenceControlFinetune, ReferencePreprocessorNode
from .nodes_plusplus import PlusPlusLoaderAdvanced, PlusPlusLoaderSingle, PlusPlusInputNode
from .nodes_ctrlora import CtrLoRALoader
from .nodes_loosecontrol import ControlNetLoaderWithLoraAdvanced
from .nodes_deprecated import (LoadImagesFromDirectory, ScaledSoftUniversalWeightsDeprecated,
SoftControlNetWeightsDeprecated, CustomControlNetWeightsDeprecated,
SoftT2IAdapterWeightsDeprecated, CustomT2IAdapterWeightsDeprecated,
AdvancedControlNetApplyDEPR, AdvancedControlNetApplySingleDEPR,
ControlNetLoaderAdvancedDEPR, DiffControlNetLoaderAdvancedDEPR)
from .logger import logger
# NODE MAPPING
NODE_CLASS_MAPPINGS = {
# Keyframes
"TimestepKeyframe": TimestepKeyframeNode,
"ACN_TimestepKeyframeInterpolation": TimestepKeyframeInterpolationNode,
"ACN_TimestepKeyframeFromStrengthList": TimestepKeyframeFromStrengthListNode,
"LatentKeyframe": LatentKeyframeNode,
"LatentKeyframeTiming": LatentKeyframeInterpolationNode,
"LatentKeyframeBatchedGroup": LatentKeyframeBatchedGroupNode,
"LatentKeyframeGroup": LatentKeyframeGroupNode,
# Conditioning
"ACN_AdvancedControlNetApply_v2": AdvancedControlNetApply,
"ACN_AdvancedControlNetApplySingle_v2": AdvancedControlNetApplySingle,
# Loaders
"ACN_ControlNetLoaderAdvanced": ControlNetLoaderAdvanced,
"ACN_DiffControlNetLoaderAdvanced": DiffControlNetLoaderAdvanced,
"ACN_AnimaLLLiteLoaderAdvanced": AnimaLLLiteLoaderAdvanced,
# Weights
"ACN_ScaledSoftControlNetWeights": ScaledSoftUniversalWeights,
"ScaledSoftMaskedUniversalWeights": ScaledSoftMaskedUniversalWeights,
"ACN_SoftControlNetWeightsSD15": SoftControlNetWeightsSD15,
"ACN_CustomControlNetWeightsSD15": CustomControlNetWeightsSD15,
"ACN_CustomControlNetWeightsFlux": CustomControlNetWeightsFlux,
"ACN_CustomControlNetWeightsAnima": CustomControlNetWeightsAnima,
"ACN_SoftT2IAdapterWeights": SoftT2IAdapterWeights,
"ACN_CustomT2IAdapterWeights": CustomT2IAdapterWeights,
"ACN_DefaultUniversalWeights": DefaultWeights,
"ACN_ExtrasMiddleMult": ExtrasMiddleMultNode,
"ACN_AnimaLLLiteExtras": AnimaLLLiteExtras,
# SparseCtrl
"ACN_SparseCtrlRGBPreprocessor": RgbSparseCtrlPreprocessor,
"ACN_SparseCtrlLoaderAdvanced": SparseCtrlLoaderAdvanced,
"ACN_SparseCtrlMergedLoaderAdvanced": SparseCtrlMergedLoaderAdvanced,
"ACN_SparseCtrlIndexMethodNode": SparseIndexMethodNode,
"ACN_SparseCtrlSpreadMethodNode": SparseSpreadMethodNode,
"ACN_SparseCtrlWeightExtras": SparseWeightExtras,
# ControlNet++
"ACN_ControlNet++LoaderSingle": PlusPlusLoaderSingle,
"ACN_ControlNet++LoaderAdvanced": PlusPlusLoaderAdvanced,
"ACN_ControlNet++InputNode": PlusPlusInputNode,
# CtrLoRA
"ACN_CtrLoRALoader": CtrLoRALoader,
# Reference
"ACN_ReferencePreprocessor": ReferencePreprocessorNode,
"ACN_ReferenceControlNet": ReferenceControlNetNode,
"ACN_ReferenceControlNetFinetune": ReferenceControlFinetune,
# LOOSEControl
#"ACN_ControlNetLoaderWithLoraAdvanced": ControlNetLoaderWithLoraAdvanced,
# Deprecated
"LoadImagesFromDirectory": LoadImagesFromDirectory,
"ScaledSoftControlNetWeights": ScaledSoftUniversalWeightsDeprecated,
"SoftControlNetWeights": SoftControlNetWeightsDeprecated,
"CustomControlNetWeights": CustomControlNetWeightsDeprecated,
"SoftT2IAdapterWeights": SoftT2IAdapterWeightsDeprecated,
"CustomT2IAdapterWeights": CustomT2IAdapterWeightsDeprecated,
"ACN_AdvancedControlNetApply": AdvancedControlNetApplyDEPR,
"ACN_AdvancedControlNetApplySingle": AdvancedControlNetApplySingleDEPR,
"ControlNetLoaderAdvanced": ControlNetLoaderAdvancedDEPR,
"DiffControlNetLoaderAdvanced": DiffControlNetLoaderAdvancedDEPR,
}
NODE_DISPLAY_NAME_MAPPINGS = {
# Keyframes
"TimestepKeyframe": "Timestep Keyframe 🛂🅐🅒🅝",
"ACN_TimestepKeyframeInterpolation": "Timestep Keyframe Interp. 🛂🅐🅒🅝",
"ACN_TimestepKeyframeFromStrengthList": "Timestep Keyframe From List 🛂🅐🅒🅝",
"LatentKeyframe": "Latent Keyframe 🛂🅐🅒🅝",
"LatentKeyframeTiming": "Latent Keyframe Interp. 🛂🅐🅒🅝",
"LatentKeyframeBatchedGroup": "Latent Keyframe From List 🛂🅐🅒🅝",
"LatentKeyframeGroup": "Latent Keyframe Group 🛂🅐🅒🅝",
# Conditioning
"ACN_AdvancedControlNetApply_v2": "Apply Advanced ControlNet 🛂🅐🅒🅝",
"ACN_AdvancedControlNetApplySingle_v2": "Apply Advanced ControlNet(1) 🛂🅐🅒🅝",
# Loaders
"ACN_ControlNetLoaderAdvanced": "Load Advanced ControlNet Model 🛂🅐🅒🅝",
"ACN_DiffControlNetLoaderAdvanced": "Load Advanced ControlNet Model (diff) 🛂🅐🅒🅝",
"ACN_AnimaLLLiteLoaderAdvanced": "Load Anima LLLite Model 🛂🅐🅒🅝",
# Weights
"ACN_ScaledSoftControlNetWeights": "Scaled Soft Weights 🛂🅐🅒🅝",
"ScaledSoftMaskedUniversalWeights": "Scaled Soft Masked Weights 🛂🅐🅒🅝",
"ACN_SoftControlNetWeightsSD15": "ControlNet Soft Weights [SD1.5] 🛂🅐🅒🅝",
"ACN_CustomControlNetWeightsSD15": "ControlNet Custom Weights [SD1.5] 🛂🅐🅒🅝",
"ACN_CustomControlNetWeightsFlux": "ControlNet Custom Weights [Flux] 🛂🅐🅒🅝",
"ACN_CustomControlNetWeightsAnima": "ControlNet Custom Weights [Anima] 🛂🅐🅒🅝",
"ACN_SoftT2IAdapterWeights": "T2IAdapter Soft Weights 🛂🅐🅒🅝",
"ACN_CustomT2IAdapterWeights": "T2IAdapter Custom Weights 🛂🅐🅒🅝",
"ACN_DefaultUniversalWeights": "Default Weights 🛂🅐🅒🅝",
"ACN_ExtrasMiddleMult": "Middle Weight Extras 🛂🅐🅒🅝",
"ACN_AnimaLLLiteExtras": "Anima LLLite Extras 🛂🅐🅒🅝",
# SparseCtrl
"ACN_SparseCtrlRGBPreprocessor": "RGB SparseCtrl 🛂🅐🅒🅝",
"ACN_SparseCtrlLoaderAdvanced": "Load SparseCtrl Model 🛂🅐🅒🅝",
"ACN_SparseCtrlMergedLoaderAdvanced": "🧪Load Merged SparseCtrl Model 🛂🅐🅒🅝",
"ACN_SparseCtrlIndexMethodNode": "SparseCtrl Index Method 🛂🅐🅒🅝",
"ACN_SparseCtrlSpreadMethodNode": "SparseCtrl Spread Method 🛂🅐🅒🅝",
"ACN_SparseCtrlWeightExtras": "SparseCtrl Weight Extras 🛂🅐🅒🅝",
# ControlNet++
"ACN_ControlNet++LoaderSingle": "Load ControlNet++ Model (Single) 🛂🅐🅒🅝",
"ACN_ControlNet++LoaderAdvanced": "Load ControlNet++ Model (Multi) 🛂🅐🅒🅝",
"ACN_ControlNet++InputNode": "ControlNet++ Input 🛂🅐🅒🅝",
# CtrLoRA
"ACN_CtrLoRALoader": "Load CtrLoRA Model 🛂🅐🅒🅝",
# Reference
"ACN_ReferencePreprocessor": "Reference Preproccessor 🛂🅐🅒🅝",
"ACN_ReferenceControlNet": "Reference ControlNet 🛂🅐🅒🅝",
"ACN_ReferenceControlNetFinetune": "Reference ControlNet (Finetune) 🛂🅐🅒🅝",
# LOOSEControl
#"ACN_ControlNetLoaderWithLoraAdvanced": "Load Adv. ControlNet Model w/ LoRA 🛂🅐🅒🅝",
# Deprecated
"LoadImagesFromDirectory": "🚫Load Images [DEPRECATED] 🛂🅐🅒🅝",
"ScaledSoftControlNetWeights": "Scaled Soft Weights 🛂🅐🅒🅝",
"SoftControlNetWeights": "ControlNet Soft Weights 🛂🅐🅒🅝",
"CustomControlNetWeights": "ControlNet Custom Weights 🛂🅐🅒🅝",
"SoftT2IAdapterWeights": "T2IAdapter Soft Weights 🛂🅐🅒🅝",
"CustomT2IAdapterWeights": "T2IAdapter Custom Weights 🛂🅐🅒🅝",
"ACN_AdvancedControlNetApply": "Apply Advanced ControlNet 🛂🅐🅒🅝",
"ACN_AdvancedControlNetApplySingle": "Apply Advanced ControlNet(1) 🛂🅐🅒🅝",
"ControlNetLoaderAdvanced": "Load Advanced ControlNet Model 🛂🅐🅒🅝",
"DiffControlNetLoaderAdvanced": "Load Advanced ControlNet Model (diff) 🛂🅐🅒🅝",
}
class AdvancedControlNetExtension(ComfyExtension):
async def get_node_list(self) -> list[type[io.ComfyNode]]:
return [
TimestepKeyframeNode,
TimestepKeyframeInterpolationNode,
TimestepKeyframeFromStrengthListNode,
LatentKeyframeNode,
LatentKeyframeInterpolationNode,
LatentKeyframeBatchedGroupNode,
LatentKeyframeGroupNode,
AdvancedControlNetApply,
AdvancedControlNetApplySingle,
ControlNetLoaderAdvanced,
DiffControlNetLoaderAdvanced,
AnimaLLLiteLoaderAdvanced,
ScaledSoftUniversalWeights,
ScaledSoftMaskedUniversalWeights,
SoftControlNetWeightsSD15,
CustomControlNetWeightsSD15,
CustomControlNetWeightsFlux,
CustomControlNetWeightsAnima,
SoftT2IAdapterWeights,
CustomT2IAdapterWeights,
DefaultWeights,
ExtrasMiddleMultNode,
AnimaLLLiteExtras,
RgbSparseCtrlPreprocessor,
SparseCtrlLoaderAdvanced,
SparseCtrlMergedLoaderAdvanced,
SparseIndexMethodNode,
SparseSpreadMethodNode,
SparseWeightExtras,
PlusPlusLoaderSingle,
PlusPlusLoaderAdvanced,
PlusPlusInputNode,
CtrLoRALoader,
ReferencePreprocessorNode,
ReferenceControlNetNode,
ReferenceControlFinetune,
LoadImagesFromDirectory,
ScaledSoftUniversalWeightsDeprecated,
SoftControlNetWeightsDeprecated,
CustomControlNetWeightsDeprecated,
SoftT2IAdapterWeightsDeprecated,
CustomT2IAdapterWeightsDeprecated,
AdvancedControlNetApplyDEPR,
AdvancedControlNetApplySingleDEPR,
ControlNetLoaderAdvancedDEPR,
DiffControlNetLoaderAdvancedDEPR
]
+18 -15
View File
@@ -1,25 +1,28 @@
from comfy_api.latest import io
import folder_paths
from .control_ctrlora import load_ctrlora
class CtrLoRALoader:
class CtrLoRALoader(io.ComfyNode):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"base": (folder_paths.get_filename_list("controlnet"), ),
"lora": (folder_paths.get_filename_list("controlnet"), ),
}
}
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='ACN_CtrLoRALoader',
display_name='Load CtrLoRA Model 🛂🅐🅒🅝',
category='Adv-ControlNet 🛂🅐🅒🅝/CtrLoRA',
inputs=[
io.Combo.Input('base', options=folder_paths.get_filename_list("controlnet")),
io.Combo.Input('lora', options=folder_paths.get_filename_list("controlnet"))
],
outputs=[
io.ControlNet.Output('CONTROL_NET', is_output_list=False)
]
)
RETURN_TYPES = ("CONTROL_NET",)
FUNCTION = "load_controlnet_plusplus"
CATEGORY = "Adv-ControlNet 🛂🅐🅒🅝/CtrLoRA"
def load_controlnet_plusplus(self, base: str, lora: str):
@classmethod
def execute(cls, base: str, lora: str):
base_path = folder_paths.get_full_path("controlnet", base)
lora_path = folder_paths.get_full_path("controlnet", lora)
controlnet = load_ctrlora(base_path, lora_path)
return (controlnet,)
return io.NodeOutput(controlnet,)
+257 -255
View File
@@ -1,3 +1,4 @@
from comfy_api.latest import io
import os
import torch
@@ -7,29 +8,31 @@ import numpy as np
from PIL import Image, ImageOps
from .control import load_controlnet, is_advanced_controlnet
from .nodes_main import AdvancedControlNetApply
from .utils import BIGMAX, ControlWeights, TimestepKeyframeGroup, TimestepKeyframe, get_properly_arranged_t2i_weights
from .logger import logger
from .utils import ControlWeights, TimestepKeyframeGroup, TimestepKeyframe, get_properly_arranged_t2i_weights
class LoadImagesFromDirectory:
class LoadImagesFromDirectory(io.ComfyNode):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"directory": ("STRING", {"default": ""}),
},
"optional": {
"image_load_cap": ("INT", {"default": 0, "min": 0, "max": BIGMAX, "step": 1}),
"start_index": ("INT", {"default": 0, "min": 0, "max": BIGMAX, "step": 1}),
}
}
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='LoadImagesFromDirectory',
display_name='🚫Load Images [DEPRECATED] 🛂🅐🅒🅝',
category='',
inputs=[
io.String.Input('directory', default=''),
io.Int.Input('image_load_cap', optional=True, default=0, max=9007199254740991, min=0, step=1),
io.Int.Input('start_index', optional=True, default=0, max=9007199254740991, min=0, step=1)
],
outputs=[
io.Image.Output('IMAGE', is_output_list=False),
io.Mask.Output('MASK', is_output_list=False),
io.Int.Output('INT', is_output_list=False)
],
is_deprecated=True
)
RETURN_TYPES = ("IMAGE", "MASK", "INT")
FUNCTION = "load_images"
CATEGORY = ""
def load_images(self, directory: str, image_load_cap: int = 0, start_index: int = 0):
@classmethod
def execute(cls, directory: str, image_load_cap: int = 0, start_index: int = 0):
if not os.path.isdir(directory):
raise FileNotFoundError(f"Directory '{directory} cannot be found.'")
dir_files = os.listdir(directory)
@@ -71,285 +74,283 @@ class LoadImagesFromDirectory:
if len(images) == 0:
raise FileNotFoundError(f"No images could be loaded from directory '{directory}'.")
return (torch.cat(images, dim=0), torch.stack(masks, dim=0), image_count)
return io.NodeOutput(torch.cat(images, dim=0), torch.stack(masks, dim=0), image_count)
class ScaledSoftUniversalWeightsDeprecated:
class ScaledSoftUniversalWeightsDeprecated(io.ComfyNode):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"base_multiplier": ("FLOAT", {"default": 0.825, "min": 0.0, "max": 1.0, "step": 0.001}, ),
"flip_weights": ("BOOLEAN", {"default": False}),
},
"optional": {
"uncond_multiplier": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}, ),
"cn_extras": ("CN_WEIGHTS_EXTRAS",),
},
}
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='ScaledSoftControlNetWeights',
display_name='Scaled Soft Weights 🛂🅐🅒🅝',
category='',
inputs=[
io.Float.Input('base_multiplier', default=0.825, max=1.0, min=0.0, step=0.001),
io.Boolean.Input('flip_weights', default=False),
io.Float.Input('uncond_multiplier', optional=True, default=1.0, max=1.0, min=0.0, step=0.01),
io.Custom('CN_WEIGHTS_EXTRAS').Input('cn_extras', optional=True)
],
outputs=[
io.Custom('CONTROL_NET_WEIGHTS').Output('CN_WEIGHTS', is_output_list=False),
io.Custom('TIMESTEP_KEYFRAME').Output('TK_SHORTCUT', is_output_list=False)
]
)
RETURN_TYPES = ("CONTROL_NET_WEIGHTS", "TIMESTEP_KEYFRAME",)
RETURN_NAMES = ("CN_WEIGHTS", "TK_SHORTCUT")
FUNCTION = "load_weights"
CATEGORY = ""
def load_weights(self, base_multiplier, flip_weights, uncond_multiplier: float=1.0, cn_extras: dict[str]={}):
@classmethod
def execute(cls, base_multiplier, flip_weights, uncond_multiplier: float=1.0, cn_extras: dict[str]={}):
weights = ControlWeights.universal(base_multiplier=base_multiplier, uncond_multiplier=uncond_multiplier, extras=cn_extras)
return (weights, TimestepKeyframeGroup.default(TimestepKeyframe(control_weights=weights)))
return io.NodeOutput(weights, TimestepKeyframeGroup.default(TimestepKeyframe(control_weights=weights)))
class SoftControlNetWeightsDeprecated:
class SoftControlNetWeightsDeprecated(io.ComfyNode):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"weight_00": ("FLOAT", {"default": 0.09941396206337118, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"weight_01": ("FLOAT", {"default": 0.12050177219802567, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"weight_02": ("FLOAT", {"default": 0.14606275417942507, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"weight_03": ("FLOAT", {"default": 0.17704576264172736, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"weight_04": ("FLOAT", {"default": 0.214600924414215, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"weight_05": ("FLOAT", {"default": 0.26012233262329093, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"weight_06": ("FLOAT", {"default": 0.3152997971191405, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"weight_07": ("FLOAT", {"default": 0.3821815722656249, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"weight_08": ("FLOAT", {"default": 0.4632503906249999, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"weight_09": ("FLOAT", {"default": 0.561515625, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"weight_10": ("FLOAT", {"default": 0.6806249999999999, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"weight_11": ("FLOAT", {"default": 0.825, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"weight_12": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"flip_weights": ("BOOLEAN", {"default": False}),
},
"optional": {
"uncond_multiplier": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}, ),
"cn_extras": ("CN_WEIGHTS_EXTRAS",),
},
}
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='SoftControlNetWeights',
display_name='ControlNet Soft Weights 🛂🅐🅒🅝',
category='',
inputs=[
io.Float.Input('weight_00', default=0.09941396206337118, max=10.0, min=0.0, step=0.001),
io.Float.Input('weight_01', default=0.12050177219802567, max=10.0, min=0.0, step=0.001),
io.Float.Input('weight_02', default=0.14606275417942507, max=10.0, min=0.0, step=0.001),
io.Float.Input('weight_03', default=0.17704576264172736, max=10.0, min=0.0, step=0.001),
io.Float.Input('weight_04', default=0.214600924414215, max=10.0, min=0.0, step=0.001),
io.Float.Input('weight_05', default=0.26012233262329093, max=10.0, min=0.0, step=0.001),
io.Float.Input('weight_06', default=0.3152997971191405, max=10.0, min=0.0, step=0.001),
io.Float.Input('weight_07', default=0.3821815722656249, max=10.0, min=0.0, step=0.001),
io.Float.Input('weight_08', default=0.4632503906249999, max=10.0, min=0.0, step=0.001),
io.Float.Input('weight_09', default=0.561515625, max=10.0, min=0.0, step=0.001),
io.Float.Input('weight_10', default=0.6806249999999999, max=10.0, min=0.0, step=0.001),
io.Float.Input('weight_11', default=0.825, max=10.0, min=0.0, step=0.001),
io.Float.Input('weight_12', default=1.0, max=10.0, min=0.0, step=0.001),
io.Boolean.Input('flip_weights', default=False),
io.Float.Input('uncond_multiplier', optional=True, default=1.0, max=1.0, min=0.0, step=0.01),
io.Custom('CN_WEIGHTS_EXTRAS').Input('cn_extras', optional=True)
],
outputs=[
io.Custom('CONTROL_NET_WEIGHTS').Output('CN_WEIGHTS', is_output_list=False),
io.Custom('TIMESTEP_KEYFRAME').Output('TK_SHORTCUT', is_output_list=False)
],
is_deprecated=True
)
DEPRECATED = True
RETURN_TYPES = ("CONTROL_NET_WEIGHTS", "TIMESTEP_KEYFRAME",)
RETURN_NAMES = ("CN_WEIGHTS", "TK_SHORTCUT")
FUNCTION = "load_weights"
CATEGORY = ""
def load_weights(self, weight_00, weight_01, weight_02, weight_03, weight_04, weight_05, weight_06,
@classmethod
def execute(cls, weight_00, weight_01, weight_02, weight_03, weight_04, weight_05, weight_06,
weight_07, weight_08, weight_09, weight_10, weight_11, weight_12, flip_weights,
uncond_multiplier: float=1.0, cn_extras: dict[str]={}):
weights_output = [weight_00, weight_01, weight_02, weight_03, weight_04, weight_05, weight_06,
weight_07, weight_08, weight_09, weight_10, weight_11]
weights_middle = [weight_12]
weights = ControlWeights.controlnet(weights_output=weights_output, weights_middle=weights_middle, uncond_multiplier=uncond_multiplier, extras=cn_extras)
return (weights, TimestepKeyframeGroup.default(TimestepKeyframe(control_weights=weights)))
return io.NodeOutput(weights, TimestepKeyframeGroup.default(TimestepKeyframe(control_weights=weights)))
class CustomControlNetWeightsDeprecated:
class CustomControlNetWeightsDeprecated(io.ComfyNode):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"weight_00": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"weight_01": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"weight_02": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"weight_03": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"weight_04": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"weight_05": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"weight_06": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"weight_07": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"weight_08": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"weight_09": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"weight_10": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"weight_11": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"weight_12": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"flip_weights": ("BOOLEAN", {"default": False}),
},
"optional": {
"uncond_multiplier": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}, ),
"cn_extras": ("CN_WEIGHTS_EXTRAS",),
},
}
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='CustomControlNetWeights',
display_name='ControlNet Custom Weights 🛂🅐🅒🅝',
category='',
inputs=[
io.Float.Input('weight_00', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('weight_01', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('weight_02', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('weight_03', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('weight_04', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('weight_05', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('weight_06', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('weight_07', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('weight_08', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('weight_09', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('weight_10', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('weight_11', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('weight_12', default=1.0, max=10.0, min=0.0, step=0.001),
io.Boolean.Input('flip_weights', default=False),
io.Float.Input('uncond_multiplier', optional=True, default=1.0, max=1.0, min=0.0, step=0.01),
io.Custom('CN_WEIGHTS_EXTRAS').Input('cn_extras', optional=True)
],
outputs=[
io.Custom('CONTROL_NET_WEIGHTS').Output('CN_WEIGHTS', is_output_list=False),
io.Custom('TIMESTEP_KEYFRAME').Output('TK_SHORTCUT', is_output_list=False)
],
is_deprecated=True
)
DEPRECATED = True
RETURN_TYPES = ("CONTROL_NET_WEIGHTS", "TIMESTEP_KEYFRAME",)
RETURN_NAMES = ("CN_WEIGHTS", "TK_SHORTCUT")
FUNCTION = "load_weights"
CATEGORY = ""
def load_weights(self, weight_00, weight_01, weight_02, weight_03, weight_04, weight_05, weight_06,
@classmethod
def execute(cls, weight_00, weight_01, weight_02, weight_03, weight_04, weight_05, weight_06,
weight_07, weight_08, weight_09, weight_10, weight_11, weight_12, flip_weights,
uncond_multiplier: float=1.0, cn_extras: dict[str]={}):
weights_output = [weight_00, weight_01, weight_02, weight_03, weight_04, weight_05, weight_06,
weight_07, weight_08, weight_09, weight_10, weight_11]
weights_middle = [weight_12]
weights = ControlWeights.controlnet(weights_output=weights_output, weights_middle=weights_middle, uncond_multiplier=uncond_multiplier, extras=cn_extras)
return (weights, TimestepKeyframeGroup.default(TimestepKeyframe(control_weights=weights)))
return io.NodeOutput(weights, TimestepKeyframeGroup.default(TimestepKeyframe(control_weights=weights)))
class SoftT2IAdapterWeightsDeprecated:
class SoftT2IAdapterWeightsDeprecated(io.ComfyNode):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"weight_00": ("FLOAT", {"default": 0.25, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"weight_01": ("FLOAT", {"default": 0.62, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"weight_02": ("FLOAT", {"default": 0.825, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"weight_03": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"flip_weights": ("BOOLEAN", {"default": False}),
},
"optional": {
"uncond_multiplier": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}, ),
"cn_extras": ("CN_WEIGHTS_EXTRAS",),
},
}
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='SoftT2IAdapterWeights',
display_name='T2IAdapter Soft Weights 🛂🅐🅒🅝',
category='',
inputs=[
io.Float.Input('weight_00', default=0.25, max=10.0, min=0.0, step=0.001),
io.Float.Input('weight_01', default=0.62, max=10.0, min=0.0, step=0.001),
io.Float.Input('weight_02', default=0.825, max=10.0, min=0.0, step=0.001),
io.Float.Input('weight_03', default=1.0, max=10.0, min=0.0, step=0.001),
io.Boolean.Input('flip_weights', default=False),
io.Float.Input('uncond_multiplier', optional=True, default=1.0, max=1.0, min=0.0, step=0.01),
io.Custom('CN_WEIGHTS_EXTRAS').Input('cn_extras', optional=True)
],
outputs=[
io.Custom('CONTROL_NET_WEIGHTS').Output('CN_WEIGHTS', is_output_list=False),
io.Custom('TIMESTEP_KEYFRAME').Output('TK_SHORTCUT', is_output_list=False)
],
is_deprecated=True
)
DEPRECATED = True
RETURN_TYPES = ("CONTROL_NET_WEIGHTS", "TIMESTEP_KEYFRAME",)
RETURN_NAMES = ("CN_WEIGHTS", "TK_SHORTCUT")
FUNCTION = "load_weights"
CATEGORY = ""
def load_weights(self, weight_00, weight_01, weight_02, weight_03, flip_weights,
@classmethod
def execute(cls, weight_00, weight_01, weight_02, weight_03, flip_weights,
uncond_multiplier: float=1.0, cn_extras: dict[str]={}):
weights = [weight_00, weight_01, weight_02, weight_03]
weights = get_properly_arranged_t2i_weights(weights)
weights = ControlWeights.t2iadapter(weights_input=weights, uncond_multiplier=uncond_multiplier, extras=cn_extras)
return (weights, TimestepKeyframeGroup.default(TimestepKeyframe(control_weights=weights)))
return io.NodeOutput(weights, TimestepKeyframeGroup.default(TimestepKeyframe(control_weights=weights)))
class CustomT2IAdapterWeightsDeprecated:
class CustomT2IAdapterWeightsDeprecated(io.ComfyNode):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"weight_00": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"weight_01": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"weight_02": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"weight_03": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"flip_weights": ("BOOLEAN", {"default": False}),
},
"optional": {
"uncond_multiplier": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}, ),
"cn_extras": ("CN_WEIGHTS_EXTRAS",),
},
}
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='CustomT2IAdapterWeights',
display_name='T2IAdapter Custom Weights 🛂🅐🅒🅝',
category='',
inputs=[
io.Float.Input('weight_00', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('weight_01', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('weight_02', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('weight_03', default=1.0, max=10.0, min=0.0, step=0.001),
io.Boolean.Input('flip_weights', default=False),
io.Float.Input('uncond_multiplier', optional=True, default=1.0, max=1.0, min=0.0, step=0.01),
io.Custom('CN_WEIGHTS_EXTRAS').Input('cn_extras', optional=True)
],
outputs=[
io.Custom('CONTROL_NET_WEIGHTS').Output('CN_WEIGHTS', is_output_list=False),
io.Custom('TIMESTEP_KEYFRAME').Output('TK_SHORTCUT', is_output_list=False)
],
is_deprecated=True
)
DEPRECATED = True
RETURN_TYPES = ("CONTROL_NET_WEIGHTS", "TIMESTEP_KEYFRAME",)
RETURN_NAMES = ("CN_WEIGHTS", "TK_SHORTCUT")
FUNCTION = "load_weights"
CATEGORY = ""
def load_weights(self, weight_00, weight_01, weight_02, weight_03, flip_weights,
@classmethod
def execute(cls, weight_00, weight_01, weight_02, weight_03, flip_weights,
uncond_multiplier: float=1.0, cn_extras: dict[str]={}):
weights = [weight_00, weight_01, weight_02, weight_03]
weights = get_properly_arranged_t2i_weights(weights)
weights = ControlWeights.t2iadapter(weights_input=weights, uncond_multiplier=uncond_multiplier, extras=cn_extras)
return (weights, TimestepKeyframeGroup.default(TimestepKeyframe(control_weights=weights)))
return io.NodeOutput(weights, TimestepKeyframeGroup.default(TimestepKeyframe(control_weights=weights)))
class AdvancedControlNetApplyDEPR:
class AdvancedControlNetApplyDEPR(io.ComfyNode):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"positive": ("CONDITIONING", ),
"negative": ("CONDITIONING", ),
"control_net": ("CONTROL_NET", ),
"image": ("IMAGE", ),
"strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.01}),
"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": {
"mask_optional": ("MASK", ),
"timestep_kf": ("TIMESTEP_KEYFRAME", ),
"latent_kf_override": ("LATENT_KEYFRAME", ),
"weights_override": ("CONTROL_NET_WEIGHTS", ),
"model_optional": ("MODEL",),
"vae_optional": ("VAE",),
},
}
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='ACN_AdvancedControlNetApply',
display_name='Apply Advanced ControlNet 🛂🅐🅒🅝',
category='',
inputs=[
io.Conditioning.Input('positive'),
io.Conditioning.Input('negative'),
io.ControlNet.Input('control_net'),
io.Image.Input('image'),
io.Float.Input('strength', default=1.0, max=10.0, min=0.0, step=0.01),
io.Float.Input('start_percent', default=0.0, max=1.0, min=0.0, step=0.001),
io.Float.Input('end_percent', default=1.0, max=1.0, min=0.0, step=0.001),
io.Mask.Input('mask_optional', optional=True),
io.Custom('TIMESTEP_KEYFRAME').Input('timestep_kf', optional=True),
io.Custom('LATENT_KEYFRAME').Input('latent_kf_override', optional=True),
io.Custom('CONTROL_NET_WEIGHTS').Input('weights_override', optional=True),
io.Model.Input('model_optional', optional=True),
io.Vae.Input('vae_optional', optional=True)
],
outputs=[
io.Conditioning.Output('positive', is_output_list=False),
io.Conditioning.Output('negative', is_output_list=False),
io.Model.Output('model_opt', is_output_list=False)
],
is_deprecated=True
)
DEPRECATED = True
RETURN_TYPES = ("CONDITIONING","CONDITIONING","MODEL",)
RETURN_NAMES = ("positive", "negative", "model_opt")
FUNCTION = "apply_controlnet"
CATEGORY = ""
def apply_controlnet(self, positive, negative, control_net, image, strength, start_percent, end_percent,
@classmethod
def execute(cls, positive, negative, control_net, image, strength, start_percent, end_percent,
mask_optional=None, model_optional=None, vae_optional=None,
timestep_kf: TimestepKeyframeGroup=None, latent_kf_override=None,
weights_override: ControlWeights=None, control_apply_to_uncond=False):
new_positive, new_negative = AdvancedControlNetApply.apply_controlnet(self, positive=positive, negative=negative, control_net=control_net, image=image,
new_positive, new_negative = AdvancedControlNetApply.execute(positive=positive, negative=negative, control_net=control_net, image=image,
strength=strength, start_percent=start_percent, end_percent=end_percent,
mask_optional=mask_optional, vae_optional=vae_optional,
timestep_kf=timestep_kf, latent_kf_override=latent_kf_override, weights_override=weights_override,)
return (new_positive, new_negative, model_optional)
timestep_kf=timestep_kf, latent_kf_override=latent_kf_override, weights_override=weights_override,).args
return io.NodeOutput(new_positive, new_negative, model_optional)
class AdvancedControlNetApplySingleDEPR:
class AdvancedControlNetApplySingleDEPR(io.ComfyNode):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"conditioning": ("CONDITIONING", ),
"control_net": ("CONTROL_NET", ),
"image": ("IMAGE", ),
"strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.01}),
"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": {
"mask_optional": ("MASK", ),
"timestep_kf": ("TIMESTEP_KEYFRAME", ),
"latent_kf_override": ("LATENT_KEYFRAME", ),
"weights_override": ("CONTROL_NET_WEIGHTS", ),
"model_optional": ("MODEL",),
"vae_optional": ("VAE",),
},
}
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='ACN_AdvancedControlNetApplySingle',
display_name='Apply Advanced ControlNet(1) 🛂🅐🅒🅝',
category='',
inputs=[
io.Conditioning.Input('conditioning'),
io.ControlNet.Input('control_net'),
io.Image.Input('image'),
io.Float.Input('strength', default=1.0, max=10.0, min=0.0, step=0.01),
io.Float.Input('start_percent', default=0.0, max=1.0, min=0.0, step=0.001),
io.Float.Input('end_percent', default=1.0, max=1.0, min=0.0, step=0.001),
io.Mask.Input('mask_optional', optional=True),
io.Custom('TIMESTEP_KEYFRAME').Input('timestep_kf', optional=True),
io.Custom('LATENT_KEYFRAME').Input('latent_kf_override', optional=True),
io.Custom('CONTROL_NET_WEIGHTS').Input('weights_override', optional=True),
io.Model.Input('model_optional', optional=True),
io.Vae.Input('vae_optional', optional=True)
],
outputs=[
io.Conditioning.Output('CONDITIONING', is_output_list=False),
io.Model.Output('model_opt', is_output_list=False)
],
is_deprecated=True
)
DEPRECATED = True
RETURN_TYPES = ("CONDITIONING","MODEL",)
RETURN_NAMES = ("CONDITIONING", "model_opt")
FUNCTION = "apply_controlnet"
CATEGORY = ""
def apply_controlnet(self, conditioning, control_net, image, strength, start_percent, end_percent,
@classmethod
def execute(cls, conditioning, control_net, image, strength, start_percent, end_percent,
mask_optional=None, model_optional=None, vae_optional=None,
timestep_kf: TimestepKeyframeGroup=None, latent_kf_override=None,
weights_override: ControlWeights=None):
values = AdvancedControlNetApply.apply_controlnet(self, positive=conditioning, negative=None, control_net=control_net, image=image,
values = AdvancedControlNetApply.execute(positive=conditioning, negative=None, control_net=control_net, image=image,
strength=strength, start_percent=start_percent, end_percent=end_percent,
mask_optional=mask_optional, vae_optional=vae_optional,
timestep_kf=timestep_kf, latent_kf_override=latent_kf_override, weights_override=weights_override,
control_apply_to_uncond=True)
return (values[0], model_optional)
return io.NodeOutput(values.args[0], model_optional)
class ControlNetLoaderAdvancedDEPR:
class ControlNetLoaderAdvancedDEPR(io.ComfyNode):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"control_net_name": (folder_paths.get_filename_list("controlnet"), ),
},
"optional": {
"tk_optional": ("TIMESTEP_KEYFRAME", ),
}
}
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='ControlNetLoaderAdvanced',
display_name='Load Advanced ControlNet Model 🛂🅐🅒🅝',
category='',
inputs=[
io.Combo.Input('control_net_name', options=folder_paths.get_filename_list("controlnet")),
io.Custom('TIMESTEP_KEYFRAME').Input('tk_optional', optional=True)
],
outputs=[
io.ControlNet.Output('CONTROL_NET', is_output_list=False)
],
is_deprecated=True
)
DEPRECATED = True
RETURN_TYPES = ("CONTROL_NET", )
FUNCTION = "load_controlnet"
CATEGORY = ""
def load_controlnet(self, control_net_name,
@classmethod
def execute(cls, control_net_name,
tk_optional: TimestepKeyframeGroup=None,
timestep_keyframe: TimestepKeyframeGroup=None,
):
@@ -357,29 +358,30 @@ class ControlNetLoaderAdvancedDEPR:
tk_optional = timestep_keyframe
controlnet_path = folder_paths.get_full_path("controlnet", control_net_name)
controlnet = load_controlnet(controlnet_path, tk_optional)
return (controlnet,)
return io.NodeOutput(controlnet,)
class DiffControlNetLoaderAdvancedDEPR:
class DiffControlNetLoaderAdvancedDEPR(io.ComfyNode):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"model": ("MODEL",),
"control_net_name": (folder_paths.get_filename_list("controlnet"), )
},
"optional": {
"tk_optional": ("TIMESTEP_KEYFRAME", ),
},
}
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='DiffControlNetLoaderAdvanced',
display_name='Load Advanced ControlNet Model (diff) 🛂🅐🅒🅝',
category='',
inputs=[
io.Model.Input('model'),
io.Combo.Input('control_net_name', options=folder_paths.get_filename_list("controlnet")),
io.Custom('TIMESTEP_KEYFRAME').Input('tk_optional', optional=True)
],
outputs=[
io.ControlNet.Output('CONTROL_NET', is_output_list=False)
],
is_deprecated=True
)
DEPRECATED = True
RETURN_TYPES = ("CONTROL_NET", )
FUNCTION = "load_controlnet"
CATEGORY = ""
def load_controlnet(self, control_net_name, model,
@classmethod
def execute(cls, control_net_name, model,
tk_optional: TimestepKeyframeGroup=None,
timestep_keyframe: TimestepKeyframeGroup=None
):
@@ -389,4 +391,4 @@ class DiffControlNetLoaderAdvancedDEPR:
controlnet = load_controlnet(controlnet_path, tk_optional, model)
if is_advanced_controlnet(controlnet):
controlnet.verify_all_weights()
return (controlnet,)
return io.NodeOutput(controlnet,)
+171 -171
View File
@@ -1,40 +1,40 @@
from comfy_api.latest import io
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 ControlWeights, TimestepKeyframe, TimestepKeyframeGroup, LatentKeyframe, LatentKeyframeGroup
from .utils import StrengthInterpolation as SI
from .logger import logger
class TimestepKeyframeNode:
class TimestepKeyframeNode(io.ComfyNode):
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", ),
},
}
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='TimestepKeyframe',
display_name='Timestep Keyframe 🛂🅐🅒🅝',
category='Adv-ControlNet 🛂🅐🅒🅝/keyframes',
inputs=[
io.Float.Input('start_percent', default=0.0, max=1.0, min=0.0, step=0.001),
io.Custom('TIMESTEP_KEYFRAME').Input('prev_timestep_kf', optional=True),
io.Float.Input('strength', optional=True, default=1.0, max=10.0, min=0.0, step=0.001),
io.Custom('CONTROL_NET_WEIGHTS').Input('cn_weights', optional=True),
io.Custom('LATENT_KEYFRAME').Input('latent_keyframe', optional=True),
io.Float.Input('null_latent_kf_strength', optional=True, default=0.0, max=10.0, min=0.0, step=0.001),
io.Boolean.Input('inherit_missing', optional=True, default=True),
io.Int.Input('guarantee_steps', optional=True, default=1, max=9007199254740991, min=0),
io.Mask.Input('mask_optional', optional=True)
],
outputs=[
io.Custom('TIMESTEP_KEYFRAME').Output('TIMESTEP_KF', is_output_list=False)
]
)
RETURN_NAMES = ("TIMESTEP_KF", )
RETURN_TYPES = ("TIMESTEP_KEYFRAME", )
FUNCTION = "load_keyframe"
CATEGORY = "Adv-ControlNet 🛂🅐🅒🅝/keyframes"
def load_keyframe(self,
@classmethod
def execute(cls,
start_percent: float,
strength: float=1.0,
cn_weights: ControlWeights=None, control_net_weights: ControlWeights=None, # old name
@@ -46,7 +46,7 @@ class TimestepKeyframeNode:
guarantee_usage=True, # old input
mask_optional=None,):
# if using outdated dummy value, means node on workflow is outdated and should appropriately convert behavior
if guarantee_steps == self.OUTDATED_DUMMY:
if guarantee_steps == cls.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
@@ -58,39 +58,39 @@ class TimestepKeyframeNode:
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,)
return io.NodeOutput(prev_timestep_keyframe,)
class TimestepKeyframeInterpolationNode:
class TimestepKeyframeInterpolationNode(io.ComfyNode):
@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}),
},
}
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='ACN_TimestepKeyframeInterpolation',
display_name='Timestep Keyframe Interp. 🛂🅐🅒🅝',
category='Adv-ControlNet 🛂🅐🅒🅝/keyframes',
inputs=[
io.Float.Input('start_percent', default=0.0, max=1.0, min=0.0, step=0.001),
io.Float.Input('end_percent', default=1.0, max=1.0, min=0.0, step=0.001),
io.Float.Input('strength_start', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('strength_end', default=1.0, max=10.0, min=0.0, step=0.001),
io.Combo.Input('interpolation', options=['linear', 'ease-in', 'ease-out', 'ease-in-out']),
io.Int.Input('intervals', default=50, max=100, min=2, step=1),
io.Custom('TIMESTEP_KEYFRAME').Input('prev_timestep_kf', optional=True),
io.Custom('CONTROL_NET_WEIGHTS').Input('cn_weights', optional=True),
io.Custom('LATENT_KEYFRAME').Input('latent_keyframe', optional=True),
io.Float.Input('null_latent_kf_strength', optional=True, default=0.0, max=10.0, min=0.0, step=0.001),
io.Boolean.Input('inherit_missing', optional=True, default=True),
io.Mask.Input('mask_optional', optional=True),
io.Boolean.Input('print_keyframes', optional=True, default=False)
],
outputs=[
io.Custom('TIMESTEP_KEYFRAME').Output('TIMESTEP_KF', is_output_list=False)
]
)
RETURN_NAMES = ("TIMESTEP_KF", )
RETURN_TYPES = ("TIMESTEP_KEYFRAME", )
FUNCTION = "load_keyframe"
CATEGORY = "Adv-ControlNet 🛂🅐🅒🅝/keyframes"
def load_keyframe(self,
@classmethod
def execute(cls,
start_percent: float, end_percent: float,
strength_start: float, strength_end: float, interpolation: str, intervals: int,
cn_weights: ControlWeights=None,
@@ -119,36 +119,35 @@ class TimestepKeyframeInterpolationNode:
guarantee_steps=guarantee_steps, mask_hint_orig=mask_optional))
if print_keyframes:
logger.info(f"TimestepKeyframe - start_percent:{percent} = {strength}")
return (prev_timestep_kf,)
return io.NodeOutput(prev_timestep_kf,)
class TimestepKeyframeFromStrengthListNode:
class TimestepKeyframeFromStrengthListNode(io.ComfyNode):
@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}),
},
}
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='ACN_TimestepKeyframeFromStrengthList',
display_name='Timestep Keyframe From List 🛂🅐🅒🅝',
category='Adv-ControlNet 🛂🅐🅒🅝/keyframes',
inputs=[
io.Float.Input('float_strengths', default=-1, force_input=True, min=-1, step=0.001),
io.Float.Input('start_percent', default=0.0, max=1.0, min=0.0, step=0.001),
io.Float.Input('end_percent', default=1.0, max=1.0, min=0.0, step=0.001),
io.Custom('TIMESTEP_KEYFRAME').Input('prev_timestep_kf', optional=True),
io.Custom('CONTROL_NET_WEIGHTS').Input('cn_weights', optional=True),
io.Custom('LATENT_KEYFRAME').Input('latent_keyframe', optional=True),
io.Float.Input('null_latent_kf_strength', optional=True, default=0.0, max=10.0, min=0.0, step=0.001),
io.Boolean.Input('inherit_missing', optional=True, default=True),
io.Mask.Input('mask_optional', optional=True),
io.Boolean.Input('print_keyframes', optional=True, default=False)
],
outputs=[
io.Custom('TIMESTEP_KEYFRAME').Output('TIMESTEP_KF', is_output_list=False)
]
)
RETURN_NAMES = ("TIMESTEP_KF", )
RETURN_TYPES = ("TIMESTEP_KEYFRAME", )
FUNCTION = "load_keyframe"
CATEGORY = "Adv-ControlNet 🛂🅐🅒🅝/keyframes"
def load_keyframe(self,
@classmethod
def execute(cls,
start_percent: float, end_percent: float,
float_strengths: float,
cn_weights: ControlWeights=None,
@@ -182,29 +181,27 @@ class TimestepKeyframeFromStrengthListNode:
guarantee_steps=guarantee_steps, mask_hint_orig=mask_optional))
if print_keyframes:
logger.info(f"TimestepKeyframe - start_percent:{percent} = {strength}")
return (prev_timestep_kf,)
return io.NodeOutput(prev_timestep_kf,)
class LatentKeyframeNode:
class LatentKeyframeNode(io.ComfyNode):
@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", ),
},
}
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='LatentKeyframe',
display_name='Latent Keyframe 🛂🅐🅒🅝',
category='Adv-ControlNet 🛂🅐🅒🅝/keyframes',
inputs=[
io.Int.Input('batch_index', default=0, max=9007199254740991, min=-9007199254740991, step=1),
io.Float.Input('strength', default=1.0, max=10.0, min=0.0, step=0.001),
io.Custom('LATENT_KEYFRAME').Input('prev_latent_kf', optional=True)
],
outputs=[
io.Custom('LATENT_KEYFRAME').Output('LATENT_KF', is_output_list=False)
]
)
RETURN_NAMES = ("LATENT_KF", )
RETURN_TYPES = ("LATENT_KEYFRAME", )
FUNCTION = "load_keyframe"
CATEGORY = "Adv-ControlNet 🛂🅐🅒🅝/keyframes"
def load_keyframe(self,
@classmethod
def execute(cls,
batch_index: int,
strength: float,
prev_latent_kf: LatentKeyframeGroup=None,
@@ -217,30 +214,29 @@ class LatentKeyframeNode:
prev_latent_keyframe = prev_latent_keyframe.clone()
keyframe = LatentKeyframe(batch_index, strength)
prev_latent_keyframe.add(keyframe)
return (prev_latent_keyframe,)
return io.NodeOutput(prev_latent_keyframe,)
class LatentKeyframeGroupNode:
class LatentKeyframeGroupNode(io.ComfyNode):
@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}),
},
}
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='LatentKeyframeGroup',
display_name='Latent Keyframe Group 🛂🅐🅒🅝',
category='Adv-ControlNet 🛂🅐🅒🅝/keyframes',
inputs=[
io.String.Input('index_strengths', default='', multiline=True),
io.Custom('LATENT_KEYFRAME').Input('prev_latent_kf', optional=True),
io.Latent.Input('latent_optional', optional=True),
io.Boolean.Input('print_keyframes', optional=True, default=False)
],
outputs=[
io.Custom('LATENT_KEYFRAME').Output('LATENT_KF', is_output_list=False)
]
)
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:
@staticmethod
def validate_index(index: int, latent_count: int = 0, is_range: bool = False, allow_negative = False) -> int:
# if part of range, do nothing
if is_range:
return index
@@ -258,13 +254,15 @@ class LatentKeyframeGroupNode:
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:
@classmethod
def convert_to_index_int(cls, 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)
return cls.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]:
@classmethod
def convert_to_latent_keyframes(cls, 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)]
@@ -289,8 +287,8 @@ class LatentKeyframeGroupNode:
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)
start_index = cls.convert_to_index_int(index_range[0], latent_count=latent_count, is_range=True, allow_negative=allow_negative)
end_index = cls.convert_to_index_int(index_range[1], latent_count=latent_count, is_range=True, allow_negative=allow_negative)
# if latents were passed in, base indeces on known latent count
if len(int_latent_indeces) > 0:
for i in int_latent_indeces[start_index:end_index]:
@@ -301,14 +299,16 @@ class LatentKeyframeGroupNode:
chosen_indeces.add(LatentKeyframe(i, strength))
# parse individual indeces
else:
chosen_indeces.add(LatentKeyframe(self.convert_to_index_int(g, latent_count=latent_count, allow_negative=allow_negative), strength))
chosen_indeces.add(LatentKeyframe(cls.convert_to_index_int(g, latent_count=latent_count, allow_negative=allow_negative), strength))
return chosen_indeces
def load_keyframes(self,
@classmethod
def execute(cls,
index_strengths: str,
prev_latent_kf: LatentKeyframeGroup=None,
prev_latent_keyframe: LatentKeyframeGroup=None, # old name
latent_image_opt=None,
latent_optional=None,
latent_image_opt=None, # old name
print_keyframes=False):
prev_latent_keyframe = prev_latent_keyframe if prev_latent_keyframe else prev_latent_kf
if not prev_latent_keyframe:
@@ -317,10 +317,11 @@ class LatentKeyframeGroupNode:
prev_latent_keyframe = prev_latent_keyframe.clone()
curr_latent_keyframe = LatentKeyframeGroup()
latent_image_opt = latent_image_opt if latent_image_opt is not None else latent_optional
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)
latent_keyframes = cls.convert_to_latent_keyframes(index_strengths, latent_count=latent_count)
for latent_keyframe in latent_keyframes:
curr_latent_keyframe.add(latent_keyframe)
@@ -333,32 +334,32 @@ class LatentKeyframeGroupNode:
for latent_keyframe in prev_latent_keyframe.keyframes:
curr_latent_keyframe.add(latent_keyframe)
return (curr_latent_keyframe,)
return io.NodeOutput(curr_latent_keyframe,)
class LatentKeyframeInterpolationNode:
class LatentKeyframeInterpolationNode(io.ComfyNode):
@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}),
},
}
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='LatentKeyframeTiming',
display_name='Latent Keyframe Interp. 🛂🅐🅒🅝',
category='Adv-ControlNet 🛂🅐🅒🅝/keyframes',
inputs=[
io.Int.Input('batch_index_from', default=0, max=9007199254740991, min=-9007199254740991, step=1),
io.Int.Input('batch_index_to_excl', default=0, max=9007199254740991, min=-9007199254740991, step=1),
io.Float.Input('strength_from', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('strength_to', default=1.0, max=10.0, min=0.0, step=0.001),
io.Combo.Input('interpolation', options=['linear', 'ease-in', 'ease-out', 'ease-in-out']),
io.Custom('LATENT_KEYFRAME').Input('prev_latent_kf', optional=True),
io.Boolean.Input('print_keyframes', optional=True, default=False)
],
outputs=[
io.Custom('LATENT_KEYFRAME').Output('LATENT_KF', is_output_list=False)
]
)
RETURN_NAMES = ("LATENT_KF", )
RETURN_TYPES = ("LATENT_KEYFRAME", )
FUNCTION = "load_keyframe"
CATEGORY = "Adv-ControlNet 🛂🅐🅒🅝/keyframes"
def load_keyframe(self,
@classmethod
def execute(cls,
batch_index_from: int,
strength_from: float,
batch_index_to_excl: int,
@@ -407,28 +408,27 @@ class LatentKeyframeInterpolationNode:
for latent_keyframe in prev_latent_keyframe.keyframes:
curr_latent_keyframe.add(latent_keyframe)
return (curr_latent_keyframe,)
return io.NodeOutput(curr_latent_keyframe,)
class LatentKeyframeBatchedGroupNode:
class LatentKeyframeBatchedGroupNode(io.ComfyNode):
@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}),
},
}
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='LatentKeyframeBatchedGroup',
display_name='Latent Keyframe From List 🛂🅐🅒🅝',
category='Adv-ControlNet 🛂🅐🅒🅝/keyframes',
inputs=[
io.Float.Input('float_strengths', default=-1, force_input=True, min=-1, step=0.001),
io.Custom('LATENT_KEYFRAME').Input('prev_latent_kf', optional=True),
io.Boolean.Input('print_keyframes', optional=True, default=False)
],
outputs=[
io.Custom('LATENT_KEYFRAME').Output('LATENT_KF', is_output_list=False)
]
)
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]],
@classmethod
def execute(cls, float_strengths: Union[float, list[float]],
prev_latent_kf: LatentKeyframeGroup=None,
prev_latent_keyframe: LatentKeyframeGroup=None, # old name
print_keyframes=False):
@@ -458,4 +458,4 @@ class LatentKeyframeBatchedGroupNode:
for latent_keyframe in prev_latent_keyframe.keyframes:
curr_latent_keyframe.add(latent_keyframe)
return (curr_latent_keyframe,)
return io.NodeOutput(curr_latent_keyframe,)
+111 -114
View File
@@ -1,123 +1,120 @@
from comfy_api.latest import io
from torch import Tensor
import folder_paths
from comfy.model_patcher import ModelPatcher
from .control import load_controlnet, convert_to_advanced, is_advanced_controlnet, is_sd3_advanced_controlnet
from .control_lllite import load_anima_lllite
from .utils import ControlWeights, LatentKeyframeGroup, TimestepKeyframeGroup, AbstractPreprocWrapper, BIGMAX
from .utils import ControlWeights, LatentKeyframeGroup, TimestepKeyframeGroup, AbstractPreprocWrapper
from .logger import logger
class ControlNetLoaderAdvanced:
class ControlNetLoaderAdvanced(io.ComfyNode):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"cnet": (folder_paths.get_filename_list("controlnet"), ),
},
"optional": {
"_tk_opt": ("TIMESTEP_KEYFRAME", ),
}
}
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='ACN_ControlNetLoaderAdvanced',
display_name='Load Advanced ControlNet Model 🛂🅐🅒🅝',
category='Adv-ControlNet 🛂🅐🅒🅝',
inputs=[
io.Combo.Input('cnet', options=folder_paths.get_filename_list("controlnet")),
io.Custom('TIMESTEP_KEYFRAME').Input('_tk_opt', optional=True)
],
outputs=[
io.ControlNet.Output('CONTROL_NET', is_output_list=False)
]
)
RETURN_TYPES = ("CONTROL_NET", )
FUNCTION = "load_controlnet"
CATEGORY = "Adv-ControlNet 🛂🅐🅒🅝"
def load_controlnet(self, cnet,
@classmethod
def execute(cls, cnet,
_tk_opt: TimestepKeyframeGroup=None,
):
controlnet_path = folder_paths.get_full_path("controlnet", cnet)
controlnet = load_controlnet(controlnet_path, _tk_opt)
return (controlnet,)
return io.NodeOutput(controlnet,)
class DiffControlNetLoaderAdvanced:
class DiffControlNetLoaderAdvanced(io.ComfyNode):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"model": ("MODEL",),
"cnet": (folder_paths.get_filename_list("controlnet"), )
},
"optional": {
"_tk_opt": ("TIMESTEP_KEYFRAME", ),
},
}
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='ACN_DiffControlNetLoaderAdvanced',
display_name='Load Advanced ControlNet Model (diff) 🛂🅐🅒🅝',
category='Adv-ControlNet 🛂🅐🅒🅝',
inputs=[
io.Model.Input('model'),
io.Combo.Input('cnet', options=folder_paths.get_filename_list("controlnet")),
io.Custom('TIMESTEP_KEYFRAME').Input('_tk_opt', optional=True)
],
outputs=[
io.ControlNet.Output('CONTROL_NET', is_output_list=False)
]
)
RETURN_TYPES = ("CONTROL_NET", )
FUNCTION = "load_controlnet"
CATEGORY = "Adv-ControlNet 🛂🅐🅒🅝"
def load_controlnet(self, cnet, model,
@classmethod
def execute(cls, cnet, model,
_tk_opt: TimestepKeyframeGroup=None,
):
controlnet_path = folder_paths.get_full_path("controlnet", cnet)
controlnet = load_controlnet(controlnet_path, _tk_opt, model)
if is_advanced_controlnet(controlnet):
controlnet.verify_all_weights()
return (controlnet,)
return io.NodeOutput(controlnet,)
class AnimaLLLiteLoaderAdvanced:
class AnimaLLLiteLoaderAdvanced(io.ComfyNode):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"model_patch": (folder_paths.get_filename_list("model_patches"), ),
},
"optional": {
"timestep_kf": ("TIMESTEP_KEYFRAME", ),
},
}
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='ACN_AnimaLLLiteLoaderAdvanced',
display_name='Load Anima LLLite Model 🛂🅐🅒🅝',
category='Adv-ControlNet 🛂🅐🅒🅝/loaders',
inputs=[
io.Combo.Input('model_patch', options=folder_paths.get_filename_list("model_patches")),
io.Custom('TIMESTEP_KEYFRAME').Input('timestep_kf', optional=True)
],
outputs=[
io.ControlNet.Output('CONTROL_NET', is_output_list=False)
]
)
RETURN_TYPES = ("CONTROL_NET", )
FUNCTION = "load_controlnet"
CATEGORY = "Adv-ControlNet 🛂🅐🅒🅝/loaders"
def load_controlnet(self, model_patch, timestep_kf: TimestepKeyframeGroup=None):
@classmethod
def execute(cls, model_patch, timestep_kf: TimestepKeyframeGroup=None):
model_patch_path = folder_paths.get_full_path_or_raise("model_patches", model_patch)
return (load_anima_lllite(model_patch_path, timestep_keyframe=timestep_kf),)
return io.NodeOutput(load_anima_lllite(model_patch_path, timestep_keyframe=timestep_kf),)
class AdvancedControlNetApply:
class AdvancedControlNetApply(io.ComfyNode):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"positive": ("CONDITIONING", ),
"negative": ("CONDITIONING", ),
"control_net": ("CONTROL_NET", ),
"image": ("IMAGE", ),
"strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.01}),
"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": {
"mask_optional": ("MASK", ),
"timestep_kf": ("TIMESTEP_KEYFRAME", ),
"latent_kf_override": ("LATENT_KEYFRAME", ),
"weights_override": ("CONTROL_NET_WEIGHTS", ),
"vae_optional": ("VAE",),
},
}
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='ACN_AdvancedControlNetApply_v2',
display_name='Apply Advanced ControlNet 🛂🅐🅒🅝',
category='Adv-ControlNet 🛂🅐🅒🅝',
inputs=[
io.Conditioning.Input('positive'),
io.Conditioning.Input('negative'),
io.ControlNet.Input('control_net'),
io.Image.Input('image'),
io.Float.Input('strength', default=1.0, max=10.0, min=0.0, step=0.01),
io.Float.Input('start_percent', default=0.0, max=1.0, min=0.0, step=0.001),
io.Float.Input('end_percent', default=1.0, max=1.0, min=0.0, step=0.001),
io.Mask.Input('mask_optional', optional=True),
io.Custom('TIMESTEP_KEYFRAME').Input('timestep_kf', optional=True),
io.Custom('LATENT_KEYFRAME').Input('latent_kf_override', optional=True),
io.Custom('CONTROL_NET_WEIGHTS').Input('weights_override', optional=True),
io.Vae.Input('vae_optional', optional=True)
],
outputs=[
io.Conditioning.Output('positive', is_output_list=False),
io.Conditioning.Output('negative', is_output_list=False)
]
)
RETURN_TYPES = ("CONDITIONING","CONDITIONING",)
RETURN_NAMES = ("positive", "negative")
FUNCTION = "apply_controlnet"
CATEGORY = "Adv-ControlNet 🛂🅐🅒🅝"
def apply_controlnet(self, positive, negative, control_net, image, strength, start_percent, end_percent,
@classmethod
def execute(cls, positive, negative, control_net, image, strength, start_percent, end_percent,
mask_optional: Tensor=None, vae_optional=None,
timestep_kf: TimestepKeyframeGroup=None, latent_kf_override: LatentKeyframeGroup=None,
weights_override: ControlWeights=None, control_apply_to_uncond=False):
if strength == 0:
return (positive, negative)
return io.NodeOutput(positive, negative)
control_hint = image.movedim(-1,1)
cnets = {}
@@ -183,43 +180,43 @@ class AdvancedControlNetApply:
n = [t[0], d]
c.append(n)
out.append(c)
return (out[0], out[1])
return io.NodeOutput(out[0], out[1])
class AdvancedControlNetApplySingle:
class AdvancedControlNetApplySingle(io.ComfyNode):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"conditioning": ("CONDITIONING", ),
"control_net": ("CONTROL_NET", ),
"image": ("IMAGE", ),
"strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.01}),
"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": {
"mask_optional": ("MASK", ),
"timestep_kf": ("TIMESTEP_KEYFRAME", ),
"latent_kf_override": ("LATENT_KEYFRAME", ),
"weights_override": ("CONTROL_NET_WEIGHTS", ),
"vae_optional": ("VAE",),
},
}
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='ACN_AdvancedControlNetApplySingle_v2',
display_name='Apply Advanced ControlNet(1) 🛂🅐🅒🅝',
category='Adv-ControlNet 🛂🅐🅒🅝',
inputs=[
io.Conditioning.Input('conditioning'),
io.ControlNet.Input('control_net'),
io.Image.Input('image'),
io.Float.Input('strength', default=1.0, max=10.0, min=0.0, step=0.01),
io.Float.Input('start_percent', default=0.0, max=1.0, min=0.0, step=0.001),
io.Float.Input('end_percent', default=1.0, max=1.0, min=0.0, step=0.001),
io.Mask.Input('mask_optional', optional=True),
io.Custom('TIMESTEP_KEYFRAME').Input('timestep_kf', optional=True),
io.Custom('LATENT_KEYFRAME').Input('latent_kf_override', optional=True),
io.Custom('CONTROL_NET_WEIGHTS').Input('weights_override', optional=True),
io.Vae.Input('vae_optional', optional=True)
],
outputs=[
io.Conditioning.Output('CONDITIONING', is_output_list=False),
io.Model.Output('model_opt', is_output_list=False)
]
)
RETURN_TYPES = ("CONDITIONING","MODEL",)
RETURN_NAMES = ("CONDITIONING", "model_opt")
FUNCTION = "apply_controlnet"
CATEGORY = "Adv-ControlNet 🛂🅐🅒🅝"
def apply_controlnet(self, conditioning, control_net, image, strength, start_percent, end_percent,
@classmethod
def execute(cls, conditioning, control_net, image, strength, start_percent, end_percent,
mask_optional: Tensor=None, vae_optional=None,
timestep_kf: TimestepKeyframeGroup=None, latent_kf_override: LatentKeyframeGroup=None,
weights_override: ControlWeights=None):
values = AdvancedControlNetApply.apply_controlnet(self, positive=conditioning, negative=None, control_net=control_net, image=image,
values = AdvancedControlNetApply.execute(positive=conditioning, negative=None, control_net=control_net, image=image,
strength=strength, start_percent=start_percent, end_percent=end_percent,
mask_optional=mask_optional, vae_optional=vae_optional,
timestep_kf=timestep_kf, latent_kf_override=latent_kf_override, weights_override=weights_override,
control_apply_to_uncond=True)
return (values[0],)
return io.NodeOutput(values.args[0], None)
+55 -51
View File
@@ -1,78 +1,82 @@
from comfy_api.latest import io
from torch import Tensor
import math
import folder_paths
from .control_plusplus import load_controlnetplusplus, PlusPlusType, PlusPlusInput, PlusPlusInputGroup, PlusPlusImageWrapper
from .utils import BIGMAX
from .control_plusplus import load_controlnetplusplus, PlusPlusInput, PlusPlusInputGroup, PlusPlusImageWrapper
class PlusPlusLoaderAdvanced:
class PlusPlusLoaderAdvanced(io.ComfyNode):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"plus_input": ("PLUS_INPUT", ),
"name": (folder_paths.get_filename_list("controlnet"), ),
}
}
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='ACN_ControlNet++LoaderAdvanced',
display_name='Load ControlNet++ Model (Multi) 🛂🅐🅒🅝',
category='Adv-ControlNet 🛂🅐🅒🅝/ControlNet++',
inputs=[
io.Custom('PLUS_INPUT').Input('plus_input'),
io.Combo.Input('name', options=folder_paths.get_filename_list("controlnet"))
],
outputs=[
io.ControlNet.Output('CONTROL_NET', is_output_list=False),
io.Image.Output('IMAGE', is_output_list=False)
]
)
RETURN_TYPES = ("CONTROL_NET", "IMAGE",)
FUNCTION = "load_controlnet_plusplus"
CATEGORY = "Adv-ControlNet 🛂🅐🅒🅝/ControlNet++"
def load_controlnet_plusplus(self, plus_input: PlusPlusInputGroup, name: str):
@classmethod
def execute(cls, plus_input: PlusPlusInputGroup, name: str):
controlnet_path = folder_paths.get_full_path("controlnet", name)
controlnet = load_controlnetplusplus(controlnet_path)
controlnet.verify_control_type(name, plus_input)
controlnet.allow_condhint_latents = True
return (controlnet, PlusPlusImageWrapper(plus_input),)
return io.NodeOutput(controlnet, PlusPlusImageWrapper(plus_input),)
class PlusPlusLoaderSingle:
class PlusPlusLoaderSingle(io.ComfyNode):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"name": (folder_paths.get_filename_list("controlnet"), ),
"control_type": (PlusPlusType._LIST_WITH_NONE, {"default": PlusPlusType.NONE}, ),
}
}
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='ACN_ControlNet++LoaderSingle',
display_name='Load ControlNet++ Model (Single) 🛂🅐🅒🅝',
category='Adv-ControlNet 🛂🅐🅒🅝/ControlNet++',
inputs=[
io.Combo.Input('name', options=folder_paths.get_filename_list("controlnet")),
io.Combo.Input('control_type', options=['openpose', 'depth', 'hed/pidi/scribble/ted', 'canny/lineart/mlsd', 'normal', 'segment', 'tile', 'inpaint/outpaint', 'none'], default='none')
],
outputs=[
io.ControlNet.Output('CONTROL_NET', is_output_list=False)
]
)
RETURN_TYPES = ("CONTROL_NET",)
FUNCTION = "load_controlnet_plusplus"
CATEGORY = "Adv-ControlNet 🛂🅐🅒🅝/ControlNet++"
def load_controlnet_plusplus(self, name: str, control_type: str):
@classmethod
def execute(cls, name: str, control_type: str):
controlnet_path = folder_paths.get_full_path("controlnet", name)
controlnet = load_controlnetplusplus(controlnet_path)
controlnet.single_control_type = control_type
controlnet.verify_control_type(name)
return (controlnet,)
return io.NodeOutput(controlnet,)
class PlusPlusInputNode:
class PlusPlusInputNode(io.ComfyNode):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"image": ("IMAGE",),
"control_type": (PlusPlusType._LIST,),
},
"optional": {
"prev_plus_input": ("PLUS_INPUT",),
#"strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": BIGMAX, "step": 0.01}),
},
}
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='ACN_ControlNet++InputNode',
display_name='ControlNet++ Input 🛂🅐🅒🅝',
category='Adv-ControlNet 🛂🅐🅒🅝/ControlNet++',
inputs=[
io.Image.Input('image'),
io.Combo.Input('control_type', options=['openpose', 'depth', 'hed/pidi/scribble/ted', 'canny/lineart/mlsd', 'normal', 'segment', 'tile', 'inpaint/outpaint']),
io.Custom('PLUS_INPUT').Input('prev_plus_input', optional=True)
],
outputs=[
io.Custom('PLUS_INPUT').Output('PLUS_INPUT', is_output_list=False)
]
)
RETURN_TYPES = ("PLUS_INPUT", )
FUNCTION = "wrap_images"
CATEGORY = "Adv-ControlNet 🛂🅐🅒🅝/ControlNet++"
def wrap_images(self, image: Tensor, control_type: str, strength=1.0, prev_plus_input: PlusPlusInputGroup=None):
@classmethod
def execute(cls, image: Tensor, control_type: str, strength=1.0, prev_plus_input: PlusPlusInputGroup=None):
if prev_plus_input is None:
prev_plus_input = PlusPlusInputGroup()
prev_plus_input = prev_plus_input.clone()
@@ -82,4 +86,4 @@ class PlusPlusInputNode:
pp_input = PlusPlusInput(image, control_type, strength)
prev_plus_input.add(pp_input)
return (prev_plus_input,)
return io.NodeOutput(prev_plus_input,)
+58 -53
View File
@@ -1,3 +1,4 @@
from comfy_api.latest import io
from torch import Tensor
from nodes import VAEEncode
@@ -6,77 +7,81 @@ from comfy.sd import VAE
from .control_reference import ReferenceAdvanced, ReferenceOptions, ReferenceType, ReferencePreprocWrapper
# node for ReferenceCN
class ReferenceControlNetNode:
class ReferenceControlNetNode(io.ComfyNode):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"reference_type": (ReferenceType._LIST,),
"style_fidelity": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 1.0, "step": 0.01}),
"ref_weight": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}),
},
}
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='ACN_ReferenceControlNet',
display_name='Reference ControlNet 🛂🅐🅒🅝',
category='Adv-ControlNet 🛂🅐🅒🅝/Reference',
inputs=[
io.Combo.Input('reference_type', options=['reference_attn', 'reference_adain', 'reference_attn+adain']),
io.Float.Input('style_fidelity', default=0.5, max=1.0, min=0.0, step=0.01),
io.Float.Input('ref_weight', default=1.0, max=1.0, min=0.0, step=0.01)
],
outputs=[
io.ControlNet.Output('CONTROL_NET', is_output_list=False)
]
)
RETURN_TYPES = ("CONTROL_NET", )
FUNCTION = "load_controlnet"
CATEGORY = "Adv-ControlNet 🛂🅐🅒🅝/Reference"
def load_controlnet(self, reference_type: str, style_fidelity: float, ref_weight: float):
@classmethod
def execute(cls, reference_type: str, style_fidelity: float, ref_weight: float):
ref_opts = ReferenceOptions.create_combo(reference_type=reference_type, style_fidelity=style_fidelity, ref_weight=ref_weight)
controlnet = ReferenceAdvanced(ref_opts=ref_opts, timestep_keyframes=None)
return (controlnet,)
return io.NodeOutput(controlnet,)
class ReferenceControlFinetune:
class ReferenceControlFinetune(io.ComfyNode):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"attn_style_fidelity": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 1.0, "step": 0.01}),
"attn_ref_weight": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}),
"attn_strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}),
"adain_style_fidelity": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 1.0, "step": 0.01}),
"adain_ref_weight": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}),
"adain_strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}),
},
}
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='ACN_ReferenceControlNetFinetune',
display_name='Reference ControlNet (Finetune) 🛂🅐🅒🅝',
category='Adv-ControlNet 🛂🅐🅒🅝/Reference',
inputs=[
io.Float.Input('attn_style_fidelity', default=0.5, max=1.0, min=0.0, step=0.01),
io.Float.Input('attn_ref_weight', default=1.0, max=1.0, min=0.0, step=0.01),
io.Float.Input('attn_strength', default=1.0, max=1.0, min=0.0, step=0.01),
io.Float.Input('adain_style_fidelity', default=0.5, max=1.0, min=0.0, step=0.01),
io.Float.Input('adain_ref_weight', default=1.0, max=1.0, min=0.0, step=0.01),
io.Float.Input('adain_strength', default=1.0, max=1.0, min=0.0, step=0.01)
],
outputs=[
io.ControlNet.Output('CONTROL_NET', is_output_list=False)
]
)
RETURN_TYPES = ("CONTROL_NET", )
FUNCTION = "load_controlnet"
CATEGORY = "Adv-ControlNet 🛂🅐🅒🅝/Reference"
def load_controlnet(self,
@classmethod
def execute(cls,
attn_style_fidelity: float, attn_ref_weight: float, attn_strength: float,
adain_style_fidelity: float, adain_ref_weight: float, adain_strength: float):
ref_opts = ReferenceOptions(reference_type=ReferenceType.ATTN_ADAIN,
attn_style_fidelity=attn_style_fidelity, attn_ref_weight=attn_ref_weight, attn_strength=attn_strength,
adain_style_fidelity=adain_style_fidelity, adain_ref_weight=adain_ref_weight, adain_strength=adain_strength)
controlnet = ReferenceAdvanced(ref_opts=ref_opts, timestep_keyframes=None)
return (controlnet,)
return io.NodeOutput(controlnet,)
class ReferencePreprocessorNode:
class ReferencePreprocessorNode(io.ComfyNode):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"image": ("IMAGE", ),
"vae": ("VAE", ),
"latent_size": ("LATENT", ),
}
}
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='ACN_ReferencePreprocessor',
display_name='Reference Preproccessor 🛂🅐🅒🅝',
category='Adv-ControlNet 🛂🅐🅒🅝/Reference/preprocess',
inputs=[
io.Image.Input('image'),
io.Vae.Input('vae'),
io.Latent.Input('latent_size')
],
outputs=[
io.Image.Output('proc_IMAGE', is_output_list=False)
]
)
RETURN_TYPES = ("IMAGE",)
RETURN_NAMES = ("proc_IMAGE",)
FUNCTION = "preprocess_images"
CATEGORY = "Adv-ControlNet 🛂🅐🅒🅝/Reference/preprocess"
def preprocess_images(self, vae: VAE, image: Tensor, latent_size: Tensor):
@classmethod
def execute(cls, vae: VAE, image: Tensor, latent_size: Tensor):
# first, resize image to match latents
image = image.movedim(-1,1)
image = comfy.utils.common_upscale(image, latent_size["samples"].shape[3] * 8, latent_size["samples"].shape[2] * 8, 'nearest-exact', "center")
@@ -87,4 +92,4 @@ class ReferencePreprocessorNode:
except Exception:
image = VAEEncode.vae_encode_crop_pixels(image)
encoded = vae.encode(image[:,:,:,:3])
return (ReferencePreprocWrapper(condhint=encoded),)
return io.NodeOutput(ReferencePreprocWrapper(condhint=encoded),)
+118 -112
View File
@@ -1,3 +1,4 @@
from comfy_api.latest import io
from torch import Tensor
import folder_paths
@@ -7,68 +8,68 @@ from comfy.sd import VAE
from .utils import TimestepKeyframeGroup
from .control_sparsectrl import SparseMethod, SparseIndexMethod, SparseSettings, SparseSpreadMethod, PreprocSparseRGBWrapper, SparseConst, SparseContextAware, get_idx_list_from_str
from .control import load_sparsectrl, load_controlnet, ControlNetAdvanced, SparseCtrlAdvanced
from .control import load_sparsectrl, load_controlnet, ControlNetAdvanced
# node for SparseCtrl loading
class SparseCtrlLoaderAdvanced:
class SparseCtrlLoaderAdvanced(io.ComfyNode):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"sparsectrl_name": (folder_paths.get_filename_list("controlnet"), ),
"use_motion": ("BOOLEAN", {"default": True}, ),
"motion_strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"motion_scale": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ),
},
"optional": {
"sparse_method": ("SPARSE_METHOD", ),
"tk_optional": ("TIMESTEP_KEYFRAME", ),
"context_aware": (SparseContextAware.LIST, ),
"sparse_hint_mult": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"sparse_nonhint_mult": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"sparse_mask_mult": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ),
}
}
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='ACN_SparseCtrlLoaderAdvanced',
display_name='Load SparseCtrl Model 🛂🅐🅒🅝',
category='Adv-ControlNet 🛂🅐🅒🅝/SparseCtrl',
inputs=[
io.Combo.Input('sparsectrl_name', options=folder_paths.get_filename_list("controlnet")),
io.Boolean.Input('use_motion', default=True),
io.Float.Input('motion_strength', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('motion_scale', default=1.0, max=10.0, min=0.0, step=0.001),
io.Custom('SPARSE_METHOD').Input('sparse_method', optional=True),
io.Custom('TIMESTEP_KEYFRAME').Input('tk_optional', optional=True),
io.Combo.Input('context_aware', optional=True, options=['nearest_hint', 'off']),
io.Float.Input('sparse_hint_mult', optional=True, default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('sparse_nonhint_mult', optional=True, default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('sparse_mask_mult', optional=True, default=1.0, max=10.0, min=0.0, step=0.001)
],
outputs=[
io.ControlNet.Output('CONTROL_NET', is_output_list=False)
]
)
RETURN_TYPES = ("CONTROL_NET", )
FUNCTION = "load_controlnet"
CATEGORY = "Adv-ControlNet 🛂🅐🅒🅝/SparseCtrl"
def load_controlnet(self, sparsectrl_name: str, use_motion: bool, motion_strength: float, motion_scale: float, sparse_method: SparseMethod=SparseSpreadMethod(), tk_optional: TimestepKeyframeGroup=None,
@classmethod
def execute(cls, sparsectrl_name: str, use_motion: bool, motion_strength: float, motion_scale: float, sparse_method: SparseMethod=SparseSpreadMethod(), tk_optional: TimestepKeyframeGroup=None,
context_aware=SparseContextAware.NEAREST_HINT, sparse_hint_mult=1.0, sparse_nonhint_mult=1.0, sparse_mask_mult=1.0):
sparsectrl_path = folder_paths.get_full_path("controlnet", sparsectrl_name)
sparse_settings = SparseSettings(sparse_method=sparse_method, use_motion=use_motion, motion_strength=motion_strength, motion_scale=motion_scale,
context_aware=context_aware,
sparse_mask_mult=sparse_mask_mult, sparse_hint_mult=sparse_hint_mult, sparse_nonhint_mult=sparse_nonhint_mult)
sparsectrl = load_sparsectrl(sparsectrl_path, timestep_keyframe=tk_optional, sparse_settings=sparse_settings)
return (sparsectrl,)
return io.NodeOutput(sparsectrl,)
class SparseCtrlMergedLoaderAdvanced:
class SparseCtrlMergedLoaderAdvanced(io.ComfyNode):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"sparsectrl_name": (folder_paths.get_filename_list("controlnet"), ),
"control_net_name": (folder_paths.get_filename_list("controlnet"), ),
"use_motion": ("BOOLEAN", {"default": True}, ),
"motion_strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"motion_scale": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ),
},
"optional": {
"sparse_method": ("SPARSE_METHOD", ),
"tk_optional": ("TIMESTEP_KEYFRAME", ),
}
}
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='ACN_SparseCtrlMergedLoaderAdvanced',
display_name='🧪Load Merged SparseCtrl Model 🛂🅐🅒🅝',
category='Adv-ControlNet 🛂🅐🅒🅝/SparseCtrl/experimental',
inputs=[
io.Combo.Input('sparsectrl_name', options=folder_paths.get_filename_list("controlnet")),
io.Combo.Input('control_net_name', options=folder_paths.get_filename_list("controlnet")),
io.Boolean.Input('use_motion', default=True),
io.Float.Input('motion_strength', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('motion_scale', default=1.0, max=10.0, min=0.0, step=0.001),
io.Custom('SPARSE_METHOD').Input('sparse_method', optional=True),
io.Custom('TIMESTEP_KEYFRAME').Input('tk_optional', optional=True)
],
outputs=[
io.ControlNet.Output('CONTROL_NET', is_output_list=False)
]
)
RETURN_TYPES = ("CONTROL_NET", )
FUNCTION = "load_controlnet"
CATEGORY = "Adv-ControlNet 🛂🅐🅒🅝/SparseCtrl/experimental"
def load_controlnet(self, sparsectrl_name: str, control_net_name: str, use_motion: bool, motion_strength: float, motion_scale: float, sparse_method: SparseMethod=SparseSpreadMethod(), tk_optional: TimestepKeyframeGroup=None):
@classmethod
def execute(cls, sparsectrl_name: str, control_net_name: str, use_motion: bool, motion_strength: float, motion_scale: float, sparse_method: SparseMethod=SparseSpreadMethod(), tk_optional: TimestepKeyframeGroup=None):
sparsectrl_path = folder_paths.get_full_path("controlnet", sparsectrl_name)
controlnet_path = folder_paths.get_full_path("controlnet", control_net_name)
sparse_settings = SparseSettings(sparse_method=sparse_method, use_motion=use_motion, motion_strength=motion_strength, motion_scale=motion_scale, merged=True)
@@ -85,64 +86,68 @@ class SparseCtrlMergedLoaderAdvanced:
new_state_dict[key] = value
# now, reload sparsectrl with real settings
sparsectrl = load_sparsectrl(sparsectrl_path, controlnet_data=new_state_dict, timestep_keyframe=tk_optional, sparse_settings=sparse_settings)
return (sparsectrl,)
return io.NodeOutput(sparsectrl,)
class SparseIndexMethodNode:
class SparseIndexMethodNode(io.ComfyNode):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"indexes": ("STRING", {"default": "0"}),
}
}
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='ACN_SparseCtrlIndexMethodNode',
display_name='SparseCtrl Index Method 🛂🅐🅒🅝',
category='Adv-ControlNet 🛂🅐🅒🅝/SparseCtrl',
inputs=[
io.String.Input('indexes', default='0')
],
outputs=[
io.Custom('SPARSE_METHOD').Output('SPARSE_METHOD', is_output_list=False)
]
)
RETURN_TYPES = ("SPARSE_METHOD",)
FUNCTION = "get_method"
CATEGORY = "Adv-ControlNet 🛂🅐🅒🅝/SparseCtrl"
def get_method(self, indexes: str):
@classmethod
def execute(cls, indexes: str):
idxs = get_idx_list_from_str(indexes)
return (SparseIndexMethod(idxs),)
return io.NodeOutput(SparseIndexMethod(idxs),)
class SparseSpreadMethodNode:
class SparseSpreadMethodNode(io.ComfyNode):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"spread": (SparseSpreadMethod.LIST,),
}
}
RETURN_TYPES = ("SPARSE_METHOD",)
FUNCTION = "get_method"
CATEGORY = "Adv-ControlNet 🛂🅐🅒🅝/SparseCtrl"
def get_method(self, spread: str):
return (SparseSpreadMethod(spread=spread),)
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='ACN_SparseCtrlSpreadMethodNode',
display_name='SparseCtrl Spread Method 🛂🅐🅒🅝',
category='Adv-ControlNet 🛂🅐🅒🅝/SparseCtrl',
inputs=[
io.Combo.Input('spread', options=['uniform', 'starting', 'ending', 'center'])
],
outputs=[
io.Custom('SPARSE_METHOD').Output('SPARSE_METHOD', is_output_list=False)
]
)
class RgbSparseCtrlPreprocessor:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"image": ("IMAGE", ),
"vae": ("VAE", ),
"latent_size": ("LATENT", ),
},
}
def execute(cls, spread: str):
return io.NodeOutput(SparseSpreadMethod(spread=spread),)
RETURN_TYPES = ("IMAGE",)
RETURN_NAMES = ("proc_IMAGE",)
FUNCTION = "preprocess_images"
class RgbSparseCtrlPreprocessor(io.ComfyNode):
@classmethod
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='ACN_SparseCtrlRGBPreprocessor',
display_name='RGB SparseCtrl 🛂🅐🅒🅝',
category='Adv-ControlNet 🛂🅐🅒🅝/SparseCtrl/preprocess',
inputs=[
io.Image.Input('image'),
io.Vae.Input('vae'),
io.Latent.Input('latent_size')
],
outputs=[
io.Image.Output('proc_IMAGE', is_output_list=False)
]
)
CATEGORY = "Adv-ControlNet 🛂🅐🅒🅝/SparseCtrl/preprocess"
def preprocess_images(self, vae: VAE, image: Tensor, latent_size: Tensor):
@classmethod
def execute(cls, vae: VAE, image: Tensor, latent_size: Tensor):
# first, resize image to match latents
image = image.movedim(-1,1)
image = comfy.utils.common_upscale(image, latent_size["samples"].shape[3] * 8, latent_size["samples"].shape[2] * 8, 'nearest-exact', "center")
@@ -153,30 +158,31 @@ class RgbSparseCtrlPreprocessor:
except Exception:
image = VAEEncode.vae_encode_crop_pixels(image)
encoded = vae.encode(image[:,:,:,:3])
return (PreprocSparseRGBWrapper(condhint=encoded),)
return io.NodeOutput(PreprocSparseRGBWrapper(condhint=encoded),)
class SparseWeightExtras:
class SparseWeightExtras(io.ComfyNode):
@classmethod
def INPUT_TYPES(s):
return {
"optional": {
"cn_extras": ("CN_WEIGHTS_EXTRAS",),
"sparse_hint_mult": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"sparse_nonhint_mult": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"sparse_mask_mult": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ),
},
}
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='ACN_SparseCtrlWeightExtras',
display_name='SparseCtrl Weight Extras 🛂🅐🅒🅝',
category='Adv-ControlNet 🛂🅐🅒🅝/SparseCtrl/extras',
inputs=[
io.Custom('CN_WEIGHTS_EXTRAS').Input('cn_extras', optional=True),
io.Float.Input('sparse_hint_mult', optional=True, default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('sparse_nonhint_mult', optional=True, default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('sparse_mask_mult', optional=True, default=1.0, max=10.0, min=0.0, step=0.001)
],
outputs=[
io.Custom('CN_WEIGHTS_EXTRAS').Output('cn_extras', is_output_list=False)
]
)
RETURN_TYPES = ("CN_WEIGHTS_EXTRAS", )
RETURN_NAMES = ("cn_extras", )
FUNCTION = "create_weight_extras"
CATEGORY = "Adv-ControlNet 🛂🅐🅒🅝/SparseCtrl/extras"
def create_weight_extras(self, cn_extras: dict[str]={}, sparse_hint_mult=1.0, sparse_nonhint_mult=1.0, sparse_mask_mult=1.0):
@classmethod
def execute(cls, cn_extras: dict[str]={}, sparse_hint_mult=1.0, sparse_nonhint_mult=1.0, sparse_mask_mult=1.0):
cn_extras = cn_extras.copy()
cn_extras[SparseConst.HINT_MULT] = sparse_hint_mult
cn_extras[SparseConst.NONHINT_MULT] = sparse_nonhint_mult
cn_extras[SparseConst.MASK_MULT] = sparse_mask_mult
return (cn_extras, )
return io.NodeOutput(cn_extras, )
+281 -259
View File
@@ -1,57 +1,56 @@
from comfy_api.latest import io
from torch import Tensor
import torch
from .utils import TimestepKeyframe, TimestepKeyframeGroup, ControlWeights, Extras, get_properly_arranged_t2i_weights, linear_conversion
from .control_lllite import AnimaLLLiteConst
from .logger import logger
WEIGHTS_RETURN_NAMES = ("CN_WEIGHTS", "TK_SHORTCUT")
class DefaultWeights:
class DefaultWeights(io.ComfyNode):
@classmethod
def INPUT_TYPES(s):
return {
"optional": {
"cn_extras": ("CN_WEIGHTS_EXTRAS",),
},
}
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='ACN_DefaultUniversalWeights',
display_name='Default Weights 🛂🅐🅒🅝',
category='Adv-ControlNet 🛂🅐🅒🅝/weights',
inputs=[
io.Custom('CN_WEIGHTS_EXTRAS').Input('cn_extras', optional=True)
],
outputs=[
io.Custom('CONTROL_NET_WEIGHTS').Output('CN_WEIGHTS', is_output_list=False),
io.Custom('TIMESTEP_KEYFRAME').Output('TK_SHORTCUT', is_output_list=False)
]
)
RETURN_TYPES = ("CONTROL_NET_WEIGHTS", "TIMESTEP_KEYFRAME",)
RETURN_NAMES = WEIGHTS_RETURN_NAMES
FUNCTION = "load_weights"
CATEGORY = "Adv-ControlNet 🛂🅐🅒🅝/weights"
def load_weights(self, cn_extras: dict[str]={}):
@classmethod
def execute(cls, cn_extras: dict[str]={}):
weights = ControlWeights.default(extras=cn_extras)
return (weights, TimestepKeyframeGroup.default(TimestepKeyframe(control_weights=weights)))
return io.NodeOutput(weights, TimestepKeyframeGroup.default(TimestepKeyframe(control_weights=weights)))
class ScaledSoftMaskedUniversalWeights:
class ScaledSoftMaskedUniversalWeights(io.ComfyNode):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"mask": ("MASK", ),
"min_base_multiplier": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.001}, ),
"max_base_multiplier": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.001}, ),
#"lock_min": ("BOOLEAN", {"default": False}, ),
#"lock_max": ("BOOLEAN", {"default": False}, ),
},
"optional": {
"uncond_multiplier": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}, ),
"cn_extras": ("CN_WEIGHTS_EXTRAS",),
},
}
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='ScaledSoftMaskedUniversalWeights',
display_name='Scaled Soft Masked Weights 🛂🅐🅒🅝',
category='Adv-ControlNet 🛂🅐🅒🅝/weights',
inputs=[
io.Mask.Input('mask'),
io.Float.Input('min_base_multiplier', default=0.0, max=1.0, min=0.0, step=0.001),
io.Float.Input('max_base_multiplier', default=1.0, max=1.0, min=0.0, step=0.001),
io.Float.Input('uncond_multiplier', optional=True, default=1.0, max=1.0, min=0.0, step=0.01),
io.Custom('CN_WEIGHTS_EXTRAS').Input('cn_extras', optional=True)
],
outputs=[
io.Custom('CONTROL_NET_WEIGHTS').Output('CN_WEIGHTS', is_output_list=False),
io.Custom('TIMESTEP_KEYFRAME').Output('TK_SHORTCUT', is_output_list=False)
]
)
RETURN_TYPES = ("CONTROL_NET_WEIGHTS", "TIMESTEP_KEYFRAME",)
RETURN_NAMES = WEIGHTS_RETURN_NAMES
FUNCTION = "load_weights"
CATEGORY = "Adv-ControlNet 🛂🅐🅒🅝/weights"
def load_weights(self, mask: Tensor, min_base_multiplier: float, max_base_multiplier: float, lock_min=False, lock_max=False,
@classmethod
def execute(cls, mask: Tensor, min_base_multiplier: float, max_base_multiplier: float, lock_min=False, lock_max=False,
uncond_multiplier: float=1.0, cn_extras: dict[str]={}):
# normalize mask
mask = mask.clone()
@@ -62,107 +61,107 @@ class ScaledSoftMaskedUniversalWeights:
else:
mask = linear_conversion(mask, x_min, x_max, min_base_multiplier, max_base_multiplier)
weights = ControlWeights.universal_mask(weight_mask=mask, uncond_multiplier=uncond_multiplier, extras=cn_extras)
return (weights, TimestepKeyframeGroup.default(TimestepKeyframe(control_weights=weights)))
return io.NodeOutput(weights, TimestepKeyframeGroup.default(TimestepKeyframe(control_weights=weights)))
class ScaledSoftUniversalWeights:
class ScaledSoftUniversalWeights(io.ComfyNode):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"base_multiplier": ("FLOAT", {"default": 0.825, "min": 0.0, "max": 1.0, "step": 0.001}, ),
},
"optional": {
"uncond_multiplier": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}, ),
"cn_extras": ("CN_WEIGHTS_EXTRAS",),
},
}
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='ACN_ScaledSoftControlNetWeights',
display_name='Scaled Soft Weights 🛂🅐🅒🅝',
category='Adv-ControlNet 🛂🅐🅒🅝/weights',
inputs=[
io.Float.Input('base_multiplier', default=0.825, max=1.0, min=0.0, step=0.001),
io.Float.Input('uncond_multiplier', optional=True, default=1.0, max=1.0, min=0.0, step=0.01),
io.Custom('CN_WEIGHTS_EXTRAS').Input('cn_extras', optional=True)
],
outputs=[
io.Custom('CONTROL_NET_WEIGHTS').Output('CN_WEIGHTS', is_output_list=False),
io.Custom('TIMESTEP_KEYFRAME').Output('TK_SHORTCUT', is_output_list=False)
]
)
RETURN_TYPES = ("CONTROL_NET_WEIGHTS", "TIMESTEP_KEYFRAME",)
RETURN_NAMES = WEIGHTS_RETURN_NAMES
FUNCTION = "load_weights"
CATEGORY = "Adv-ControlNet 🛂🅐🅒🅝/weights"
def load_weights(self, base_multiplier, uncond_multiplier: float=1.0, cn_extras: dict[str]={}):
@classmethod
def execute(cls, base_multiplier, uncond_multiplier: float=1.0, cn_extras: dict[str]={}):
weights = ControlWeights.universal(base_multiplier=base_multiplier, uncond_multiplier=uncond_multiplier, extras=cn_extras)
return (weights, TimestepKeyframeGroup.default(TimestepKeyframe(control_weights=weights)))
return io.NodeOutput(weights, TimestepKeyframeGroup.default(TimestepKeyframe(control_weights=weights)))
class SoftControlNetWeightsSD15:
class SoftControlNetWeightsSD15(io.ComfyNode):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"output_0": ("FLOAT", {"default": 0.09941396206337118, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"output_1": ("FLOAT", {"default": 0.12050177219802567, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"output_2": ("FLOAT", {"default": 0.14606275417942507, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"output_3": ("FLOAT", {"default": 0.17704576264172736, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"output_4": ("FLOAT", {"default": 0.214600924414215, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"output_5": ("FLOAT", {"default": 0.26012233262329093, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"output_6": ("FLOAT", {"default": 0.3152997971191405, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"output_7": ("FLOAT", {"default": 0.3821815722656249, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"output_8": ("FLOAT", {"default": 0.4632503906249999, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"output_9": ("FLOAT", {"default": 0.561515625, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"output_10": ("FLOAT", {"default": 0.6806249999999999, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"output_11": ("FLOAT", {"default": 0.825, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"middle_0": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ),
},
"optional": {
"uncond_multiplier": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}, ),
"cn_extras": ("CN_WEIGHTS_EXTRAS",),
},
}
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='ACN_SoftControlNetWeightsSD15',
display_name='ControlNet Soft Weights [SD1.5] 🛂🅐🅒🅝',
category='Adv-ControlNet 🛂🅐🅒🅝/weights/ControlNet',
inputs=[
io.Float.Input('output_0', default=0.09941396206337118, max=10.0, min=0.0, step=0.001),
io.Float.Input('output_1', default=0.12050177219802567, max=10.0, min=0.0, step=0.001),
io.Float.Input('output_2', default=0.14606275417942507, max=10.0, min=0.0, step=0.001),
io.Float.Input('output_3', default=0.17704576264172736, max=10.0, min=0.0, step=0.001),
io.Float.Input('output_4', default=0.214600924414215, max=10.0, min=0.0, step=0.001),
io.Float.Input('output_5', default=0.26012233262329093, max=10.0, min=0.0, step=0.001),
io.Float.Input('output_6', default=0.3152997971191405, max=10.0, min=0.0, step=0.001),
io.Float.Input('output_7', default=0.3821815722656249, max=10.0, min=0.0, step=0.001),
io.Float.Input('output_8', default=0.4632503906249999, max=10.0, min=0.0, step=0.001),
io.Float.Input('output_9', default=0.561515625, max=10.0, min=0.0, step=0.001),
io.Float.Input('output_10', default=0.6806249999999999, max=10.0, min=0.0, step=0.001),
io.Float.Input('output_11', default=0.825, max=10.0, min=0.0, step=0.001),
io.Float.Input('middle_0', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('uncond_multiplier', optional=True, default=1.0, max=1.0, min=0.0, step=0.01),
io.Custom('CN_WEIGHTS_EXTRAS').Input('cn_extras', optional=True)
],
outputs=[
io.Custom('CONTROL_NET_WEIGHTS').Output('CN_WEIGHTS', is_output_list=False),
io.Custom('TIMESTEP_KEYFRAME').Output('TK_SHORTCUT', is_output_list=False)
]
)
RETURN_TYPES = ("CONTROL_NET_WEIGHTS", "TIMESTEP_KEYFRAME",)
RETURN_NAMES = WEIGHTS_RETURN_NAMES
FUNCTION = "load_weights"
CATEGORY = "Adv-ControlNet 🛂🅐🅒🅝/weights/ControlNet"
def load_weights(self, output_0, output_1, output_2, output_3, output_4, output_5, output_6,
@classmethod
def execute(cls, output_0, output_1, output_2, output_3, output_4, output_5, output_6,
output_7, output_8, output_9, output_10, output_11, middle_0,
uncond_multiplier: float=1.0, cn_extras: dict[str]={}):
return CustomControlNetWeightsSD15.load_weights(self,
return CustomControlNetWeightsSD15.execute(
output_0=output_0, output_1=output_1, output_2=output_2, output_3=output_3,
output_4=output_4, output_5=output_5, output_6=output_6, output_7=output_7,
output_8=output_8, output_9=output_9, output_10=output_10, output_11=output_11,
middle_0=middle_0,
uncond_multiplier=uncond_multiplier, cn_extras=cn_extras)
class CustomControlNetWeightsSD15:
class CustomControlNetWeightsSD15(io.ComfyNode):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"output_0": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"output_1": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"output_2": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"output_3": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"output_4": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"output_5": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"output_6": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"output_7": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"output_8": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"output_9": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"output_10": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"output_11": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"middle_0": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ),
},
"optional": {
"uncond_multiplier": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}, ),
"cn_extras": ("CN_WEIGHTS_EXTRAS",),
},
}
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='ACN_CustomControlNetWeightsSD15',
display_name='ControlNet Custom Weights [SD1.5] 🛂🅐🅒🅝',
category='Adv-ControlNet 🛂🅐🅒🅝/weights/ControlNet',
inputs=[
io.Float.Input('output_0', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('output_1', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('output_2', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('output_3', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('output_4', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('output_5', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('output_6', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('output_7', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('output_8', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('output_9', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('output_10', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('output_11', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('middle_0', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('uncond_multiplier', optional=True, default=1.0, max=1.0, min=0.0, step=0.01),
io.Custom('CN_WEIGHTS_EXTRAS').Input('cn_extras', optional=True)
],
outputs=[
io.Custom('CONTROL_NET_WEIGHTS').Output('CN_WEIGHTS', is_output_list=False),
io.Custom('TIMESTEP_KEYFRAME').Output('TK_SHORTCUT', is_output_list=False)
]
)
RETURN_TYPES = ("CONTROL_NET_WEIGHTS", "TIMESTEP_KEYFRAME",)
RETURN_NAMES = WEIGHTS_RETURN_NAMES
FUNCTION = "load_weights"
CATEGORY = "Adv-ControlNet 🛂🅐🅒🅝/weights/ControlNet"
def load_weights(self, output_0, output_1, output_2, output_3, output_4, output_5, output_6,
@classmethod
def execute(cls, output_0, output_1, output_2, output_3, output_4, output_5, output_6,
output_7, output_8, output_9, output_10, output_11, middle_0,
uncond_multiplier: float=1.0, cn_extras: dict[str]={}):
weights_output = [output_0, output_1, output_2, output_3, output_4, output_5, output_6,
@@ -170,47 +169,47 @@ class CustomControlNetWeightsSD15:
weights_middle = [middle_0]
weights = ControlWeights.controlnet(weights_output=weights_output, weights_middle=weights_middle, uncond_multiplier=uncond_multiplier,
extras=cn_extras, disable_applied_to=True)
return (weights, TimestepKeyframeGroup.default(TimestepKeyframe(control_weights=weights)))
return io.NodeOutput(weights, TimestepKeyframeGroup.default(TimestepKeyframe(control_weights=weights)))
class CustomControlNetWeightsFlux:
class CustomControlNetWeightsFlux(io.ComfyNode):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"input_0": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"input_1": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"input_2": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"input_3": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"input_4": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"input_5": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"input_6": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"input_7": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"input_8": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"input_9": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"input_10": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"input_11": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"input_12": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"input_13": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"input_14": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"input_15": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"input_16": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"input_17": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"input_18": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ),
},
"optional": {
"uncond_multiplier": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}, ),
"cn_extras": ("CN_WEIGHTS_EXTRAS",),
},
}
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='ACN_CustomControlNetWeightsFlux',
display_name='ControlNet Custom Weights [Flux] 🛂🅐🅒🅝',
category='Adv-ControlNet 🛂🅐🅒🅝/weights/ControlNet',
inputs=[
io.Float.Input('input_0', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('input_1', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('input_2', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('input_3', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('input_4', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('input_5', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('input_6', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('input_7', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('input_8', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('input_9', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('input_10', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('input_11', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('input_12', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('input_13', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('input_14', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('input_15', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('input_16', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('input_17', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('input_18', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('uncond_multiplier', optional=True, default=1.0, max=1.0, min=0.0, step=0.01),
io.Custom('CN_WEIGHTS_EXTRAS').Input('cn_extras', optional=True)
],
outputs=[
io.Custom('CONTROL_NET_WEIGHTS').Output('CN_WEIGHTS', is_output_list=False),
io.Custom('TIMESTEP_KEYFRAME').Output('TK_SHORTCUT', is_output_list=False)
]
)
RETURN_TYPES = ("CONTROL_NET_WEIGHTS", "TIMESTEP_KEYFRAME",)
RETURN_NAMES = WEIGHTS_RETURN_NAMES
FUNCTION = "load_weights"
CATEGORY = "Adv-ControlNet 🛂🅐🅒🅝/weights/ControlNet"
def load_weights(self, input_0, input_1, input_2, input_3, input_4, input_5, input_6,
@classmethod
def execute(cls, input_0, input_1, input_2, input_3, input_4, input_5, input_6,
input_7, input_8, input_9, input_10, input_11, input_12, input_13,
input_14, input_15, input_16, input_17, input_18,
uncond_multiplier: float=1.0, cn_extras: dict[str]={}):
@@ -218,139 +217,162 @@ class CustomControlNetWeightsFlux:
input_6, input_7, input_8, input_9, input_10, input_11,
input_12, input_13, input_14, input_15, input_16, input_17, input_18]
weights = ControlWeights.controlnet(weights_input=weights_input, uncond_multiplier=uncond_multiplier, extras=cn_extras, disable_applied_to=True)
return (weights, TimestepKeyframeGroup.default(TimestepKeyframe(control_weights=weights)))
return io.NodeOutput(weights, TimestepKeyframeGroup.default(TimestepKeyframe(control_weights=weights)))
class CustomControlNetWeightsAnima:
class CustomControlNetWeightsAnima(io.ComfyNode):
@classmethod
def INPUT_TYPES(s):
required = {
f"block_{index}": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001})
for index in range(28)
}
return {
"required": required,
"optional": {
"uncond_multiplier": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}),
"cn_extras": ("CN_WEIGHTS_EXTRAS",),
},
}
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='ACN_CustomControlNetWeightsAnima',
display_name='ControlNet Custom Weights [Anima] 🛂🅐🅒🅝',
category='Adv-ControlNet 🛂🅐🅒🅝/weights/ControlNet',
inputs=[
io.Float.Input('block_0', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('block_1', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('block_2', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('block_3', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('block_4', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('block_5', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('block_6', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('block_7', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('block_8', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('block_9', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('block_10', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('block_11', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('block_12', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('block_13', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('block_14', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('block_15', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('block_16', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('block_17', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('block_18', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('block_19', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('block_20', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('block_21', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('block_22', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('block_23', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('block_24', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('block_25', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('block_26', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('block_27', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('uncond_multiplier', optional=True, default=1.0, max=1.0, min=0.0, step=0.01),
io.Custom('CN_WEIGHTS_EXTRAS').Input('cn_extras', optional=True)
],
outputs=[
io.Custom('CONTROL_NET_WEIGHTS').Output('CN_WEIGHTS', is_output_list=False),
io.Custom('TIMESTEP_KEYFRAME').Output('TK_SHORTCUT', is_output_list=False)
]
)
RETURN_TYPES = ("CONTROL_NET_WEIGHTS", "TIMESTEP_KEYFRAME",)
RETURN_NAMES = WEIGHTS_RETURN_NAMES
FUNCTION = "load_weights"
CATEGORY = "Adv-ControlNet 🛂🅐🅒🅝/weights/ControlNet"
def load_weights(self, uncond_multiplier: float=1.0, cn_extras: dict[str]={}, **kwargs):
@classmethod
def execute(cls, uncond_multiplier: float=1.0, cn_extras: dict[str]={}, **kwargs):
weights = [kwargs[f"block_{index}"] for index in range(28)]
control_weights = ControlWeights.controllllite(
weights_input=weights,
uncond_multiplier=uncond_multiplier,
extras=cn_extras,
)
return (control_weights, TimestepKeyframeGroup.default(TimestepKeyframe(control_weights=control_weights)))
return io.NodeOutput(control_weights, TimestepKeyframeGroup.default(TimestepKeyframe(control_weights=control_weights)))
class SoftT2IAdapterWeights:
class SoftT2IAdapterWeights(io.ComfyNode):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"input_0": ("FLOAT", {"default": 0.25, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"input_1": ("FLOAT", {"default": 0.62, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"input_2": ("FLOAT", {"default": 0.825, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"input_3": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ),
},
"optional": {
"uncond_multiplier": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}, ),
"cn_extras": ("CN_WEIGHTS_EXTRAS",),
},
}
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='ACN_SoftT2IAdapterWeights',
display_name='T2IAdapter Soft Weights 🛂🅐🅒🅝',
category='Adv-ControlNet 🛂🅐🅒🅝/weights/T2IAdapter',
inputs=[
io.Float.Input('input_0', default=0.25, max=10.0, min=0.0, step=0.001),
io.Float.Input('input_1', default=0.62, max=10.0, min=0.0, step=0.001),
io.Float.Input('input_2', default=0.825, max=10.0, min=0.0, step=0.001),
io.Float.Input('input_3', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('uncond_multiplier', optional=True, default=1.0, max=1.0, min=0.0, step=0.01),
io.Custom('CN_WEIGHTS_EXTRAS').Input('cn_extras', optional=True)
],
outputs=[
io.Custom('CONTROL_NET_WEIGHTS').Output('CN_WEIGHTS', is_output_list=False),
io.Custom('TIMESTEP_KEYFRAME').Output('TK_SHORTCUT', is_output_list=False)
]
)
RETURN_TYPES = ("CONTROL_NET_WEIGHTS", "TIMESTEP_KEYFRAME",)
RETURN_NAMES = WEIGHTS_RETURN_NAMES
FUNCTION = "load_weights"
CATEGORY = "Adv-ControlNet 🛂🅐🅒🅝/weights/T2IAdapter"
def load_weights(self, input_0, input_1, input_2, input_3,
@classmethod
def execute(cls, input_0, input_1, input_2, input_3,
uncond_multiplier: float=1.0, cn_extras: dict[str]={}):
return CustomT2IAdapterWeights.load_weights(self, input_0=input_0, input_1=input_1, input_2=input_2, input_3=input_3,
return CustomT2IAdapterWeights.execute(input_0=input_0, input_1=input_1, input_2=input_2, input_3=input_3,
uncond_multiplier=uncond_multiplier, cn_extras=cn_extras)
class CustomT2IAdapterWeights:
class CustomT2IAdapterWeights(io.ComfyNode):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"input_0": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"input_1": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"input_2": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ),
"input_3": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ),
},
"optional": {
"uncond_multiplier": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}, ),
"cn_extras": ("CN_WEIGHTS_EXTRAS",),
},
}
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='ACN_CustomT2IAdapterWeights',
display_name='T2IAdapter Custom Weights 🛂🅐🅒🅝',
category='Adv-ControlNet 🛂🅐🅒🅝/weights/T2IAdapter',
inputs=[
io.Float.Input('input_0', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('input_1', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('input_2', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('input_3', default=1.0, max=10.0, min=0.0, step=0.001),
io.Float.Input('uncond_multiplier', optional=True, default=1.0, max=1.0, min=0.0, step=0.01),
io.Custom('CN_WEIGHTS_EXTRAS').Input('cn_extras', optional=True)
],
outputs=[
io.Custom('CONTROL_NET_WEIGHTS').Output('CN_WEIGHTS', is_output_list=False),
io.Custom('TIMESTEP_KEYFRAME').Output('TK_SHORTCUT', is_output_list=False)
]
)
RETURN_TYPES = ("CONTROL_NET_WEIGHTS", "TIMESTEP_KEYFRAME",)
RETURN_NAMES = WEIGHTS_RETURN_NAMES
FUNCTION = "load_weights"
CATEGORY = "Adv-ControlNet 🛂🅐🅒🅝/weights/T2IAdapter"
def load_weights(self, input_0, input_1, input_2, input_3,
@classmethod
def execute(cls, input_0, input_1, input_2, input_3,
uncond_multiplier: float=1.0, cn_extras: dict[str]={}):
weights = [input_0, input_1, input_2, input_3]
weights = get_properly_arranged_t2i_weights(weights)
weights = ControlWeights.t2iadapter(weights_input=weights, uncond_multiplier=uncond_multiplier, extras=cn_extras, disable_applied_to=True)
return (weights, TimestepKeyframeGroup.default(TimestepKeyframe(control_weights=weights)))
return io.NodeOutput(weights, TimestepKeyframeGroup.default(TimestepKeyframe(control_weights=weights)))
class ExtrasMiddleMultNode:
class ExtrasMiddleMultNode(io.ComfyNode):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"middle_mult": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}),
},
"optional": {
"cn_extras": ("CN_WEIGHTS_EXTRAS",),
},
}
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='ACN_ExtrasMiddleMult',
display_name='Middle Weight Extras 🛂🅐🅒🅝',
category='Adv-ControlNet 🛂🅐🅒🅝/weights/extras',
inputs=[
io.Float.Input('middle_mult', default=1.0, max=10.0, min=0.0, step=0.001),
io.Custom('CN_WEIGHTS_EXTRAS').Input('cn_extras', optional=True)
],
outputs=[
io.Custom('CN_WEIGHTS_EXTRAS').Output('cn_extras', is_output_list=False)
]
)
RETURN_TYPES = ("CN_WEIGHTS_EXTRAS",)
RETURN_NAMES = ("cn_extras",)
FUNCTION = "create_extras"
CATEGORY = "Adv-ControlNet 🛂🅐🅒🅝/weights/extras"
def create_extras(self, middle_mult: float, cn_extras: dict[str]={}):
@classmethod
def execute(cls, middle_mult: float, cn_extras: dict[str]={}):
cn_extras = cn_extras.copy()
cn_extras[Extras.MIDDLE_MULT] = middle_mult
return (cn_extras,)
return io.NodeOutput(cn_extras,)
class AnimaLLLiteExtras:
class AnimaLLLiteExtras(io.ComfyNode):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"inpaint_mask": ("MASK",),
},
"optional": {
"cn_extras": ("CN_WEIGHTS_EXTRAS",),
},
}
def define_schema(cls) -> io.Schema:
return io.Schema(
node_id='ACN_AnimaLLLiteExtras',
display_name='Anima LLLite Extras 🛂🅐🅒🅝',
category='Adv-ControlNet 🛂🅐🅒🅝/weights/extras',
inputs=[
io.Mask.Input('inpaint_mask'),
io.Custom('CN_WEIGHTS_EXTRAS').Input('cn_extras', optional=True)
],
outputs=[
io.Custom('CN_WEIGHTS_EXTRAS').Output('cn_extras', is_output_list=False)
]
)
RETURN_TYPES = ("CN_WEIGHTS_EXTRAS",)
RETURN_NAMES = ("cn_extras",)
FUNCTION = "create_extras"
CATEGORY = "Adv-ControlNet 🛂🅐🅒🅝/weights/extras"
def create_extras(self, inpaint_mask: Tensor, cn_extras: dict[str]={}):
@classmethod
def execute(cls, inpaint_mask: Tensor, cn_extras: dict[str]={}):
cn_extras = cn_extras.copy()
cn_extras[AnimaLLLiteConst.INPAINT_MASK] = inpaint_mask.clone()
return (cn_extras,)
return io.NodeOutput(cn_extras,)