Skip to content

Commit 56000f3

Browse files
authored
Merge PR #130 from Kosinkadink/develop - ControlNet++ support
ControlNet++ support
2 parents 7a456aa + 36fdc79 commit 56000f3

7 files changed

+583
-2
lines changed

adv_control/control.py

+9
Original file line numberDiff line numberDiff line change
@@ -519,6 +519,11 @@ def copy(self):
519519

520520
def load_controlnet(ckpt_path, timestep_keyframe: TimestepKeyframeGroup=None, model=None):
521521
controlnet_data = comfy.utils.load_torch_file(ckpt_path, safe_load=True)
522+
# from pathlib import Path
523+
# log_name = ckpt_path.split('\\')[-1]
524+
# with open(Path(__file__).parent.parent.parent / rf"keys_{log_name}.txt", "w") as afile:
525+
# for key, value in controlnet_data.items():
526+
# afile.write(f"{key}:\t{value.shape}\n")
522527
control = None
523528
# check if a non-vanilla ControlNet
524529
controlnet_type = ControlWeightType.DEFAULT
@@ -538,6 +543,10 @@ def load_controlnet(ckpt_path, timestep_keyframe: TimestepKeyframeGroup=None, mo
538543
# SVD-ControlNet check
539544
elif "temporal_res_block" in key:
540545
has_temporal_res_block_key = True
546+
# ControlNet++ check
547+
elif "task_embedding" in key:
548+
raise Exception("ControlNet++ model detected; must be loaded using the Load ControlNet++ Model nodes.")
549+
541550
if has_controlnet_key and has_motion_modules_key:
542551
controlnet_type = ControlWeightType.SPARSECTRL
543552
elif has_controlnet_key and has_temporal_res_block_key:

adv_control/control_plusplus.py

+478
Large diffs are not rendered by default.

adv_control/nodes.py

+9
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
TimestepKeyframeNode, TimestepKeyframeInterpolationNode, TimestepKeyframeFromStrengthListNode)
1313
from .nodes_sparsectrl import SparseCtrlMergedLoaderAdvanced, SparseCtrlLoaderAdvanced, SparseIndexMethodNode, SparseSpreadMethodNode, RgbSparseCtrlPreprocessor, SparseWeightExtras
1414
from .nodes_reference import ReferenceControlNetNode, ReferenceControlFinetune, ReferencePreprocessorNode
15+
from .nodes_plusplus import PlusPlusLoaderAdvanced, PlusPlusLoaderSingle, PlusPlusInputNode
1516
from .nodes_loosecontrol import ControlNetLoaderWithLoraAdvanced
1617
from .nodes_deprecated import LoadImagesFromDirectory
1718
from .logger import logger
@@ -199,6 +200,10 @@ def apply_controlnet(self, positive, negative, control_net, image, strength, sta
199200
"ACN_SparseCtrlIndexMethodNode": SparseIndexMethodNode,
200201
"ACN_SparseCtrlSpreadMethodNode": SparseSpreadMethodNode,
201202
"ACN_SparseCtrlWeightExtras": SparseWeightExtras,
203+
# ControlNet++
204+
"ACN_ControlNet++LoaderSingle": PlusPlusLoaderSingle,
205+
"ACN_ControlNet++LoaderAdvanced": PlusPlusLoaderAdvanced,
206+
"ACN_ControlNet++InputNode": PlusPlusInputNode,
202207
# Reference
203208
"ACN_ReferencePreprocessor": ReferencePreprocessorNode,
204209
"ACN_ReferenceControlNet": ReferenceControlNetNode,
@@ -238,6 +243,10 @@ def apply_controlnet(self, positive, negative, control_net, image, strength, sta
238243
"ACN_SparseCtrlIndexMethodNode": "SparseCtrl Index Method 🛂🅐🅒🅝",
239244
"ACN_SparseCtrlSpreadMethodNode": "SparseCtrl Spread Method 🛂🅐🅒🅝",
240245
"ACN_SparseCtrlWeightExtras": "SparseCtrl Weight Extras 🛂🅐🅒🅝",
246+
# ControlNet++
247+
"ACN_ControlNet++LoaderSingle": "Load ControlNet++ Model (Single) 🛂🅐🅒🅝",
248+
"ACN_ControlNet++LoaderAdvanced": "Load ControlNet++ Model (Multi) 🛂🅐🅒🅝",
249+
"ACN_ControlNet++InputNode": "ControlNet++ Input 🛂🅐🅒🅝",
241250
# Reference
242251
"ACN_ReferencePreprocessor": "Reference Preproccessor 🛂🅐🅒🅝",
243252
"ACN_ReferenceControlNet": "Reference ControlNet 🛂🅐🅒🅝",

adv_control/nodes_plusplus.py

+84
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
from torch import Tensor
2+
import math
3+
4+
import folder_paths
5+
6+
from .control_plusplus import load_controlnetplusplus, PlusPlusType, PlusPlusInput, PlusPlusInputGroup, PlusPlusImageWrapper
7+
from .utils import BIGMAX
8+
9+
10+
class PlusPlusLoaderAdvanced:
11+
@classmethod
12+
def INPUT_TYPES(s):
13+
return {
14+
"required": {
15+
"plus_input": ("PLUS_INPUT", ),
16+
"name": (folder_paths.get_filename_list("controlnet"), ),
17+
}
18+
}
19+
20+
RETURN_TYPES = ("CONTROL_NET", "IMAGE",)
21+
FUNCTION = "load_controlnet_plusplus"
22+
23+
CATEGORY = "Adv-ControlNet 🛂🅐🅒🅝/ControlNet++"
24+
25+
def load_controlnet_plusplus(self, plus_input: PlusPlusInputGroup, name: str):
26+
controlnet_path = folder_paths.get_full_path("controlnet", name)
27+
controlnet = load_controlnetplusplus(controlnet_path)
28+
controlnet.verify_control_type(name, plus_input)
29+
return (controlnet, PlusPlusImageWrapper(plus_input),)
30+
31+
32+
class PlusPlusLoaderSingle:
33+
@classmethod
34+
def INPUT_TYPES(s):
35+
return {
36+
"required": {
37+
"name": (folder_paths.get_filename_list("controlnet"), ),
38+
"control_type": (PlusPlusType._LIST_WITH_NONE, {"default": PlusPlusType.NONE}, ),
39+
}
40+
}
41+
42+
RETURN_TYPES = ("CONTROL_NET",)
43+
FUNCTION = "load_controlnet_plusplus"
44+
45+
CATEGORY = "Adv-ControlNet 🛂🅐🅒🅝/ControlNet++"
46+
47+
def load_controlnet_plusplus(self, name: str, control_type: str):
48+
controlnet_path = folder_paths.get_full_path("controlnet", name)
49+
controlnet = load_controlnetplusplus(controlnet_path)
50+
controlnet.single_control_type = control_type
51+
controlnet.verify_control_type(name)
52+
return (controlnet,)
53+
54+
55+
class PlusPlusInputNode:
56+
@classmethod
57+
def INPUT_TYPES(s):
58+
return {
59+
"required": {
60+
"image": ("IMAGE",),
61+
"control_type": (PlusPlusType._LIST,),
62+
},
63+
"optional": {
64+
"prev_plus_input": ("PLUS_INPUT",),
65+
#"strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": BIGMAX, "step": 0.01}),
66+
}
67+
}
68+
69+
RETURN_TYPES = ("PLUS_INPUT", )
70+
FUNCTION = "wrap_images"
71+
72+
CATEGORY = "Adv-ControlNet 🛂🅐🅒🅝/ControlNet++"
73+
74+
def wrap_images(self, image: Tensor, control_type: str, strength=1.0, prev_plus_input: PlusPlusInputGroup=None):
75+
if prev_plus_input is None:
76+
prev_plus_input = PlusPlusInputGroup()
77+
prev_plus_input = prev_plus_input.clone()
78+
79+
if math.isclose(strength, 0.0):
80+
strength = 0.0000001
81+
pp_input = PlusPlusInput(image, control_type, strength)
82+
prev_plus_input.add(pp_input)
83+
84+
return (prev_plus_input,)

adv_control/utils.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ class ControlWeightType:
145145
UNIVERSAL = "universal"
146146
T2IADAPTER = "t2iadapter"
147147
CONTROLNET = "controlnet"
148+
CONTROLNETPLUSPLUS = "controlnet++"
148149
CONTROLLORA = "controllora"
149150
CONTROLLLLITE = "controllllite"
150151
SVD_CONTROLNET = "svd_controlnet"
@@ -380,7 +381,7 @@ def default(cls, keyframe: TimestepKeyframe) -> 'TimestepKeyframeGroup':
380381

381382
class AbstractPreprocWrapper:
382383
error_msg = "Invalid use of [InsertHere] output. The output of [InsertHere] preprocessor is NOT a usual image, but a latent pretending to be an image - you must connect the output directly to an Apply ControlNet node (advanced or otherwise). It cannot be used for anything else that accepts IMAGE input."
383-
def __init__(self, condhint: Tensor):
384+
def __init__(self, condhint):
384385
self.condhint = condhint
385386

386387
def movedim(self, *args, **kwargs):

pyproject.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[project]
22
name = "comfyui-advanced-controlnet"
33
description = "Nodes for scheduling ControlNet strength across timesteps and batched latents, as well as applying custom weights and attention masks."
4-
version = "1.1.0"
4+
version = "1.1.1"
55
license = "LICENSE"
66
dependencies = []
77

requirements.txt

Whitespace-only changes.

0 commit comments

Comments
 (0)