213 lines
5.4 KiB
Python
213 lines
5.4 KiB
Python
from diffusers.pipelines.controlnet.multicontrolnet import MultiControlNetModel
|
|
import numpy as np
|
|
from PIL import Image
|
|
import torch
|
|
|
|
|
|
class AnyType(str):
|
|
"""A special class that is always equal in not equal comparisons. Credit to pythongosssss"""
|
|
|
|
def __eq__(self, _) -> bool:
|
|
return True
|
|
|
|
def __ne__(self, __value: object) -> bool:
|
|
return False
|
|
|
|
any = AnyType("*")
|
|
|
|
|
|
class MultiControlNetConverter:
|
|
def __init__(self):
|
|
pass
|
|
|
|
@classmethod
|
|
def INPUT_TYPES(cls):
|
|
return {
|
|
"required": {
|
|
"controlnets": (any,),
|
|
},
|
|
}
|
|
|
|
RETURN_TYPES = ("MODEL",)
|
|
RETURN_NAMES = ("multicontrolnet",)
|
|
CATEGORY = "ControlNetUtils"
|
|
FUNCTION = "execute"
|
|
|
|
def execute(self, controlnets):
|
|
model = MultiControlNetModel(controlnets)
|
|
return [model]
|
|
|
|
|
|
class NHWC2NCHWTensor:
|
|
@classmethod
|
|
def INPUT_TYPES(cls):
|
|
return {
|
|
"required": {
|
|
"image": ("IMAGE",),
|
|
},
|
|
"optional": {
|
|
}
|
|
}
|
|
|
|
CATEGORY = "ControlNetUtils"
|
|
RETURN_TYPES = ("IMAGE",)
|
|
RETURN_NAMES = ("image",)
|
|
|
|
FUNCTION = "execute"
|
|
def execute(self, image):
|
|
print(image.shape)
|
|
image.permute(0, 3, 1, 2)
|
|
print(image.shape)
|
|
return [image]
|
|
|
|
class NHWCTensor2PIL:
|
|
@classmethod
|
|
def INPUT_TYPES(cls):
|
|
return {
|
|
"required": {
|
|
"image": ("IMAGE",),
|
|
},
|
|
"optional": {
|
|
}
|
|
}
|
|
|
|
CATEGORY = "ControlNetUtils"
|
|
RETURN_TYPES = ("IMAGE",)
|
|
RETURN_NAMES = ("image",)
|
|
|
|
FUNCTION = "execute"
|
|
|
|
def execute(self, image):
|
|
# convert NHWC Tensor to PIL Image
|
|
img_array = image.squeeze(0).cpu().numpy() * 255.0
|
|
img_pil = Image.fromarray(np.clip(img_array, 0, 255).astype(np.uint8))
|
|
return [img_pil]
|
|
|
|
|
|
class PIL2NHWCTensor:
|
|
@classmethod
|
|
def INPUT_TYPES(cls):
|
|
return {
|
|
"required": {
|
|
"image": ("IMAGE",),
|
|
},
|
|
"optional": {
|
|
}
|
|
}
|
|
|
|
CATEGORY = "ControlNetUtils"
|
|
RETURN_TYPES = ("IMAGE",)
|
|
RETURN_NAMES = ("image",)
|
|
|
|
FUNCTION = "execute"
|
|
def execute(self, image):
|
|
# 将 PIL.Image 转换为 numpy.ndarray
|
|
img_array = np.array(image)
|
|
# 转换 numpy.ndarray 为 torch.Tensor
|
|
img_tensor = torch.from_numpy(img_array).float() / 255.
|
|
# 转换图像格式为 CHW (如果需要)
|
|
if img_tensor.ndim == 3 and img_tensor.shape[-1] == 3:
|
|
img_tensor = img_tensor.permute(2, 0, 1)
|
|
# 添加批次维度并转换为 NHWC
|
|
img_tensor = img_tensor.unsqueeze(0).permute(0, 2, 3, 1)
|
|
return [img_tensor]
|
|
|
|
|
|
class ListOfImages:
|
|
def __init__(self):
|
|
pass
|
|
|
|
@classmethod
|
|
def INPUT_TYPES(cls):
|
|
return {
|
|
"required": {
|
|
},
|
|
"optional": {
|
|
"any_1": ("IMAGE",),
|
|
"any_2": ("IMAGE",),
|
|
"any_3": ("IMAGE",),
|
|
"any_4": ("IMAGE",),
|
|
"any_5": ("IMAGE",),
|
|
"any_6": ("IMAGE",),
|
|
"any_7": ("IMAGE",),
|
|
"any_8": ("IMAGE",),
|
|
}
|
|
}
|
|
|
|
CATEGORY = "ControlNetUtils"
|
|
RETURN_TYPES = ("IMAGE",)
|
|
RETURN_NAMES = ("images",)
|
|
|
|
FUNCTION = "execute"
|
|
|
|
def execute(self,
|
|
any_1=None,
|
|
any_2=None,
|
|
any_3=None,
|
|
any_4=None,
|
|
any_5=None,
|
|
any_6=None,
|
|
any_7=None,
|
|
any_8=None):
|
|
|
|
list_any = []
|
|
|
|
if any_1 is not None:
|
|
try:
|
|
list_any.append(any_1)
|
|
except Exception as e:
|
|
raise e
|
|
if any_2 is not None:
|
|
try:
|
|
list_any.append(any_2)
|
|
except Exception as e:
|
|
raise e
|
|
if any_3 is not None:
|
|
try:
|
|
list_any.append(any_3)
|
|
except Exception as e:
|
|
raise e
|
|
if any_4 is not None:
|
|
try:
|
|
list_any.append(any_4)
|
|
except Exception as e:
|
|
raise e
|
|
if any_5 is not None:
|
|
try:
|
|
list_any.append(any_5)
|
|
except Exception as e:
|
|
raise e
|
|
if any_6 is not None:
|
|
try:
|
|
list_any.append(any_6)
|
|
except Exception as e:
|
|
raise e
|
|
if any_7 is not None:
|
|
try:
|
|
list_any.append(any_7)
|
|
except Exception as e:
|
|
raise e
|
|
if any_8 is not None:
|
|
try:
|
|
list_any.append(any_8)
|
|
except Exception as e:
|
|
raise e
|
|
|
|
# yes, double brackets are needed because of the OUTPUT_IS_LIST... ¯\_(ツ)_/¯
|
|
return [list_any]
|
|
|
|
NODE_CLASS_MAPPINGS = {
|
|
"MultiControlNetConverter": MultiControlNetConverter,
|
|
"ListOfImages": ListOfImages,
|
|
"PIL2NHWCTensor": PIL2NHWCTensor,
|
|
"NHWC2NCHWTensor": NHWC2NCHWTensor,
|
|
"NHWCTensor2PIL": NHWCTensor2PIL
|
|
}
|
|
|
|
NODE_DISPLAY_NAME_MAPPINGS = {
|
|
"MultiControlNetConverter": "Multi-ControlNet Converter",
|
|
"ListOfImages": "List of Images",
|
|
"PIL2NHWCTensor": "Convert PIL to Tensor (NHWC)",
|
|
"NHWC2NCHWTensor": "Convert Tensor (NHWC) to (NCHW)",
|
|
"NHWCTensor2PIL": "Convert Tensor (NHWC) to PIL"
|
|
} |