StupidGame's picture
Upload 1941 files
baa8e90
#---------------------------------------------------------------------------------------------------------------------#
# Comfyroll Custom Nodes by RockOfFire and Akatsuzi https://github.com/Suzie1/ComfyUI_Comfyroll_CustomNodes
# for ComfyUI https://github.com/comfyanonymous/ComfyUI
#---------------------------------------------------------------------------------------------------------------------#
import torch
import numpy as np
import os
import sys
import io
import comfy.sd
from PIL import Image
from PIL.PngImagePlugin import PngInfo
import json
import folder_paths
import typing as tg
import random
from .graphics_functions import random_hex_color, random_rgb
from ..categories import icons
sys.path.insert(0, os.path.join(os.path.dirname(os.path.realpath(__file__)), "comfy"))
#---------------------------------------------------------------------------------------------------------------------#
# Aspect Ratio Nodes
#---------------------------------------------------------------------------------------------------------------------#
class CR_AspectRatioSD15:
def __init__(self):
pass
@classmethod
def INPUT_TYPES(s):
aspect_ratios = ["custom",
"1:1 square 512x512",
"1:1 square 1024x1024",
"2:3 portrait 512x768",
"3:4 portrait 512x682",
"3:2 landscape 768x512",
"4:3 landscape 682x512",
"16:9 cinema 910x512",
"1.85:1 cinema 952x512",
"2:1 cinema 1024x512",
"2.39:1 anamorphic 1224x512"]
return {
"required": {
"width": ("INT", {"default": 512, "min": 64, "max": 8192}),
"height": ("INT", {"default": 512, "min": 64, "max": 8192}),
"aspect_ratio": (aspect_ratios,),
"swap_dimensions": (["Off", "On"],),
"upscale_factor": ("FLOAT", {"default": 1.0, "min": 0.1, "max": 100.0, "step":0.1}),
"batch_size": ("INT", {"default": 1, "min": 1, "max": 64})
}
}
RETURN_TYPES = ("INT", "INT", "FLOAT", "INT", "LATENT", "STRING", )
RETURN_NAMES = ("width", "height", "upscale_factor", "batch_size", "empty_latent", "show_help", )
FUNCTION = "Aspect_Ratio"
CATEGORY = icons.get("Comfyroll/Aspect Ratio")
def Aspect_Ratio(self, width, height, aspect_ratio, swap_dimensions, upscale_factor, batch_size):
if aspect_ratio == "2:3 portrait 512x768":
width, height = 512, 768
elif aspect_ratio == "3:2 landscape 768x512":
width, height = 768, 512
elif aspect_ratio == "1:1 square 512x512":
width, height = 512, 512
elif aspect_ratio == "1:1 square 1024x1024":
width, height = 1024, 1024
elif aspect_ratio == "16:9 cinema 910x512":
width, height = 910, 512
elif aspect_ratio == "3:4 portrait 512x682":
width, height = 512, 682
elif aspect_ratio == "4:3 landscape 682x512":
width, height = 682, 512
elif aspect_ratio == "1.85:1 cinema 952x512":
width, height = 952, 512
elif aspect_ratio == "2:1 cinema 1024x512":
width, height = 1024, 512
elif aspect_ratio == "2.39:1 anamorphic 1224x512":
width, height = 1224, 512
if swap_dimensions == "On":
width, height = height, width
latent = torch.zeros([batch_size, 4, height // 8, width // 8])
show_help = "https://github.com/Suzie1/ComfyUI_Comfyroll_CustomNodes/wiki/Aspect-Ratio-Nodes#cr-sd15-aspect-ratio"
return(width, height, upscale_factor, batch_size, {"samples":latent}, show_help, )
#---------------------------------------------------------------------------------------------------------------------#
class CR_SDXLAspectRatio:
def __init__(self):
pass
@classmethod
def INPUT_TYPES(s):
aspect_ratios = ["custom",
"1:1 square 1024x1024",
"3:4 portrait 896x1152",
"5:8 portrait 832x1216",
"9:16 portrait 768x1344",
"9:21 portrait 640x1536",
"4:3 landscape 1152x896",
"3:2 landscape 1216x832",
"16:9 landscape 1344x768",
"21:9 landscape 1536x640"]
return {
"required": {
"width": ("INT", {"default": 1024, "min": 64, "max": 8192}),
"height": ("INT", {"default": 1024, "min": 64, "max": 8192}),
"aspect_ratio": (aspect_ratios,),
"swap_dimensions": (["Off", "On"],),
"upscale_factor": ("FLOAT", {"default": 1.0, "min": 0.1, "max": 100.0, "step":0.1}),
"batch_size": ("INT", {"default": 1, "min": 1, "max": 64})
}
}
RETURN_TYPES = ("INT", "INT", "FLOAT", "INT", "LATENT", "STRING", )
RETURN_NAMES = ("width", "height", "upscale_factor", "batch_size", "empty_latent", "show_help", )
FUNCTION = "Aspect_Ratio"
CATEGORY = icons.get("Comfyroll/Aspect Ratio")
def Aspect_Ratio(self, width, height, aspect_ratio, swap_dimensions, upscale_factor, batch_size):
if aspect_ratio == "1:1 square 1024x1024":
width, height = 1024, 1024
elif aspect_ratio == "3:4 portrait 896x1152":
width, height = 896, 1152
elif aspect_ratio == "5:8 portrait 832x1216":
width, height = 832, 1216
elif aspect_ratio == "9:16 portrait 768x1344":
width, height = 768, 1344
elif aspect_ratio == "9:21 portrait 640x1536":
width, height = 640, 1536
elif aspect_ratio == "4:3 landscape 1152x896":
width, height = 1152, 896
elif aspect_ratio == "3:2 landscape 1216x832":
width, height = 1216, 832
elif aspect_ratio == "16:9 landscape 1344x768":
width, height = 1344, 768
elif aspect_ratio == "21:9 landscape 1536x640":
width, height = 1536, 640
if swap_dimensions == "On":
width, height = height, width
latent = torch.zeros([batch_size, 4, height // 8, width // 8])
show_help = "https://github.com/Suzie1/ComfyUI_Comfyroll_CustomNodes/wiki/Aspect-Ratio-Nodes#cr-sdxl-aspect-ratio"
return(width, height, upscale_factor, batch_size, {"samples":latent}, show_help, )
#---------------------------------------------------------------------------------------------------------------------#
class CR_AspectRatio:
def __init__(self):
pass
@classmethod
def INPUT_TYPES(s):
aspect_ratios = ["custom",
"SD1.5 - 1:1 square 512x512",
"SD1.5 - 2:3 portrait 512x768",
"SD1.5 - 3:4 portrait 512x682",
"SD1.5 - 3:2 landscape 768x512",
"SD1.5 - 4:3 landscape 682x512",
"SD1.5 - 16:9 cinema 910x512",
"SD1.5 - 1.85:1 cinema 952x512",
"SD1.5 - 2:1 cinema 1024x512",
"SDXL - 1:1 square 1024x1024",
"SDXL - 3:4 portrait 896x1152",
"SDXL - 5:8 portrait 832x1216",
"SDXL - 9:16 portrait 768x1344",
"SDXL - 9:21 portrait 640x1536",
"SDXL - 4:3 landscape 1152x896",
"SDXL - 3:2 landscape 1216x832",
"SDXL - 16:9 landscape 1344x768",
"SDXL - 21:9 landscape 1536x640"]
return {
"required": {
"width": ("INT", {"default": 1024, "min": 64, "max": 8192}),
"height": ("INT", {"default": 1024, "min": 64, "max": 8192}),
"aspect_ratio": (aspect_ratios,),
"swap_dimensions": (["Off", "On"],),
"upscale_factor": ("FLOAT", {"default": 1.0, "min": 0.1, "max": 100.0, "step":0.1}),
"prescale_factor": ("FLOAT", {"default": 1.0, "min": 0.1, "max": 100.0, "step":0.1}),
"batch_size": ("INT", {"default": 1, "min": 1, "max": 64})
}
}
RETURN_TYPES = ("INT", "INT", "FLOAT", "FLOAT", "INT", "LATENT", "STRING", )
RETURN_NAMES = ("width", "height", "upscale_factor", "prescale_factor", "batch_size", "empty_latent", "show_help", )
FUNCTION = "Aspect_Ratio"
CATEGORY = icons.get("Comfyroll/Aspect Ratio")
def Aspect_Ratio(self, width, height, aspect_ratio, swap_dimensions, upscale_factor, prescale_factor, batch_size):
# SD1.5
if aspect_ratio == "SD1.5 - 1:1 square 512x512":
width, height = 512, 512
elif aspect_ratio == "SD1.5 - 2:3 portrait 512x768":
width, height = 512, 768
elif aspect_ratio == "SD1.5 - 16:9 cinema 910x512":
width, height = 910, 512
elif aspect_ratio == "SD1.5 - 3:4 portrait 512x682":
width, height = 512, 682
elif aspect_ratio == "SD1.5 - 3:2 landscape 768x512":
width, height = 768, 512
elif aspect_ratio == "SD1.5 - 4:3 landscape 682x512":
width, height = 682, 512
elif aspect_ratio == "SD1.5 - 1.85:1 cinema 952x512":
width, height = 952, 512
elif aspect_ratio == "SD1.5 - 2:1 cinema 1024x512":
width, height = 1024, 512
elif aspect_ratio == "SD1.5 - 2.39:1 anamorphic 1224x512":
width, height = 1224, 512
# SDXL
if aspect_ratio == "SDXL - 1:1 square 1024x1024":
width, height = 1024, 1024
elif aspect_ratio == "SDXL - 3:4 portrait 896x1152":
width, height = 896, 1152
elif aspect_ratio == "SDXL - 5:8 portrait 832x1216":
width, height = 832, 1216
elif aspect_ratio == "SDXL - 9:16 portrait 768x1344":
width, height = 768, 1344
elif aspect_ratio == "SDXL - 9:21 portrait 640x1536":
width, height = 640, 1536
elif aspect_ratio == "SDXL - 4:3 landscape 1152x896":
width, height = 1152, 896
elif aspect_ratio == "SDXL - 3:2 landscape 1216x832":
width, height = 1216, 832
elif aspect_ratio == "SDXL - 16:9 landscape 1344x768":
width, height = 1344, 768
elif aspect_ratio == "SDXL - 21:9 landscape 1536x640":
width, height = 1536, 640
if swap_dimensions == "On":
width, height = height, width
width = int(width*prescale_factor)
height = int(height*prescale_factor)
latent = torch.zeros([batch_size, 4, height // 8, width // 8])
show_help = "https://github.com/Suzie1/ComfyUI_Comfyroll_CustomNodes/wiki/Aspect-Ratio-Nodes#cr-aspect-ratio"
return(width, height, upscale_factor, prescale_factor, batch_size, {"samples":latent}, show_help, )
#---------------------------------------------------------------------------------------------------------------------#
# Other Nodes
#---------------------------------------------------------------------------------------------------------------------#
class CR_ImageOutput:
def __init__(self):
self.output_dir = folder_paths.get_output_directory()
self.type = "output"
@classmethod
def INPUT_TYPES(s):
return {"required":
{"images": ("IMAGE", ),
"output_type": (["Preview", "Save"],),
"filename_prefix": ("STRING", {"default": "ComfyUI"})},
"hidden": {"prompt": "PROMPT", "extra_pnginfo": "EXTRA_PNGINFO"},
"optional": {
"trigger": ("BOOLEAN", {"default": False},),}
}
RETURN_TYPES = ("BOOLEAN", )
RETURN_NAMES = ("trigger", )
FUNCTION = "save_images"
OUTPUT_NODE = True
CATEGORY = icons.get("Comfyroll/Other")
def save_images(self, images, filename_prefix="ComfyUI", trigger = False, output_type = "Preview", prompt=None, extra_pnginfo=None):
def map_filename(filename):
prefix_len = len(os.path.basename(filename_prefix))
prefix = filename[:prefix_len + 1]
try:
digits = int(filename[prefix_len + 1:].split('_')[0])
except:
digits = 0
return (digits, prefix)
def compute_vars(input):
input = input.replace("%width%", str(images[0].shape[1]))
input = input.replace("%height%", str(images[0].shape[0]))
return input
if output_type == "Save":
self.output_dir = folder_paths.get_output_directory()
self.type = "output"
elif output_type == "Preview":
self.output_dir = folder_paths.get_temp_directory()
self.type = "temp"
filename_prefix = compute_vars(filename_prefix)
subfolder = os.path.dirname(os.path.normpath(filename_prefix))
filename = os.path.basename(os.path.normpath(filename_prefix))
full_output_folder = os.path.join(self.output_dir, subfolder)
if os.path.commonpath((self.output_dir, os.path.abspath(full_output_folder))) != self.output_dir:
return {}
try:
counter = max(filter(lambda a: a[1][:-1] == filename and a[1][-1] == "_", map(map_filename, os.listdir(full_output_folder))))[0] + 1
except ValueError:
counter = 1
except FileNotFoundError:
os.makedirs(full_output_folder, exist_ok=True)
counter = 1
results = list()
for image in images:
i = 255. * image.cpu().numpy()
img = Image.fromarray(np.clip(i, 0, 255).astype(np.uint8))
metadata = PngInfo()
if prompt is not None:
metadata.add_text("prompt", json.dumps(prompt))
if extra_pnginfo is not None:
for x in extra_pnginfo:
metadata.add_text(x, json.dumps(extra_pnginfo[x]))
file = f"{filename}_{counter:05}_.png"
img.save(os.path.join(full_output_folder, file), pnginfo=metadata, compress_level=4)
results.append({
"filename": file,
"subfolder": subfolder,
"type": self.type
})
counter += 1
show_help = "https://github.com/Suzie1/ComfyUI_Comfyroll_CustomNodes/wiki/Other-Nodes#cr-image-output"
return { "ui": { "images": results }, "result": (trigger,) }
#---------------------------------------------------------------------------------------------------------------------#
class CR_IntegerMultipleOf:
def __init__(self):
pass
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"integer": ("INT", {"default": 1, "min": -18446744073709551615, "max": 18446744073709551615}),
"multiple": ("FLOAT", {"default": 8, "min": 1, "max": 18446744073709551615}),
}
}
RETURN_TYPES =("INT", "STRING", )
RETURN_NAMES =("INT", "show_help", )
FUNCTION = "int_multiple_of"
CATEGORY = icons.get("Comfyroll/Other")
def int_multiple_of(self, integer, multiple=8):
show_help = "https://github.com/Suzie1/ComfyUI_Comfyroll_CustomNodes/wiki/Other-Nodes#cr-integer-multiple"
if multiple == 0:
return (int(integer), show_help, )
integer = integer * multiple
return (int(integer), show_help, )
#---------------------------------------------------------------------------------------------------------------------#
class CR_Seed:
def __init__(self):
pass
@classmethod
def INPUT_TYPES(s):
return {"required": {"seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff})}}
RETURN_TYPES = ("INT", "STRING", )
RETURN_NAMES = ("seed", "show_help", )
FUNCTION = "seedint"
OUTPUT_NODE = True
CATEGORY = icons.get("Comfyroll/Other")
@staticmethod
def seedint(seed):
show_help = "https://github.com/Suzie1/ComfyUI_Comfyroll_CustomNodes/wiki/Other-Nodes#cr-seed"
return (seed, show_help,)
#---------------------------------------------------------------------------------------------------------------------#
class CR_LatentBatchSize:
def __init__(self):
pass
@classmethod
def INPUT_TYPES(s):
return {"required": {"latent": ("LATENT", ),
"batch_size": ("INT", {"default": 2, "min": 1, "max": 16, "step": 1}),
}
}
RETURN_TYPES = ("LATENT", )
FUNCTION = "batchsize"
CATEGORY = icons.get("Comfyroll/Other")
def batchsize(self, latent: tg.Sequence[tg.Mapping[tg.Text, torch.Tensor]], batch_size: int):
samples = latent['samples']
shape = samples.shape
sample_list = [samples] + [
torch.clone(samples) for _ in range(batch_size - 1)
]
show_help = "https://github.com/Suzie1/ComfyUI_Comfyroll_CustomNodes/wiki/Other-Nodes#cr-latent-batch-size"
return ({
'samples': torch.cat(sample_list),
}, )
#---------------------------------------------------------------------------------------------------------------------#
class CR_PromptText:
@classmethod
def INPUT_TYPES(s):
return {"required": {"prompt": ("STRING", {"default": "prompt", "multiline": True})}}
RETURN_TYPES = ("STRING", "STRING", )
RETURN_NAMES = ("prompt", "show_help", )
FUNCTION = "get_value"
CATEGORY = icons.get("Comfyroll/Other")
def get_value(self, prompt):
show_help = "https://github.com/Suzie1/ComfyUI_Comfyroll_CustomNodes/wiki/Other-Nodes#cr-prompt-text"
return (prompt, show_help, )
#---------------------------------------------------------------------------------------------------------------------#
class CR_SplitString:
@classmethod
def INPUT_TYPES(s):
return {"required": {"text": ("STRING", {"multiline": False, "default": "text"}),
"delimiter": ("STRING", {"multiline": False, "default": ","}),
}
}
RETURN_TYPES = ("STRING", "STRING", "STRING", "STRING", "STRING", )
RETURN_NAMES = ("string_1", "string_2", "string_3", "string_4", "show_help", )
FUNCTION = "split"
CATEGORY = icons.get("Comfyroll/Other")
def split(self, text, delimiter):
# Split the text string
parts = text.split(delimiter)
strings = [part.strip() for part in parts[:4]]
string_1, string_2, string_3, string_4 = strings + [""] * (4 - len(strings))
show_help = "https://github.com/Suzie1/ComfyUI_Comfyroll_CustomNodes/wiki/Other-Nodes#cr-split-string"
return (string_1, string_2, string_3, string_4, show_help, )
#---------------------------------------------------------------------------------------------------------------------#
class CR_Value:
@classmethod
def INPUT_TYPES(s):
return {"required": {"value": ("FLOAT", {"default": 1.0,},)}}
RETURN_TYPES = ("FLOAT", "INT", "STRING", )
RETURN_NAMES = ("FLOAT", "INT", "show_help", )
CATEGORY = icons.get("Comfyroll/Other")
FUNCTION = "get_value"
def get_value(self, value):
show_help = "https://github.com/Suzie1/ComfyUI_Comfyroll_CustomNodes/wiki/Other-Nodes#cr-value"
return (float(value), int(value), show_help, )
#---------------------------------------------------------------------------------------------------------------------#
class CR_ConditioningMixer:
@classmethod
def INPUT_TYPES(s):
mix_methods = ["Combine", "Average", "Concatenate"]
return {"required":
{"conditioning_1": ("CONDITIONING", ),
"conditioning_2": ("CONDITIONING", ),
"mix_method": (mix_methods, ),
"average_strength": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 1.0, "step": 0.01}),
}
}
RETURN_TYPES = ("CONDITIONING", "STRING", )
RETURN_NAMES = ("CONDITIONING", "show_help", )
FUNCTION = "conditioning"
CATEGORY = icons.get("Comfyroll/Other")
def conditioning(self, mix_method, conditioning_1, conditioning_2, average_strength):
conditioning_from = conditioning_1
conditioning_to = conditioning_2
conditioning_to_strength = average_strength
show_help = "https://github.com/Suzie1/ComfyUI_Comfyroll_CustomNodes/wiki/Other-Nodes#cr-conditioning-mixer"
if mix_method == "Combine":
return (conditioning_1 + conditioning_2, show_help, )
if mix_method == "Average":
out = []
if len(conditioning_from) > 1:
print("Warning: ConditioningAverage conditioning_from contains more than 1 cond, only the first one will actually be applied to conditioning_to.")
cond_from = conditioning_from[0][0]
pooled_output_from = conditioning_from[0][1].get("pooled_output", None)
for i in range(len(conditioning_to)):
t1 = conditioning_to[i][0]
pooled_output_to = conditioning_to[i][1].get("pooled_output", pooled_output_from)
t0 = cond_from[:,:t1.shape[1]]
if t0.shape[1] < t1.shape[1]:
t0 = torch.cat([t0] + [torch.zeros((1, (t1.shape[1] - t0.shape[1]), t1.shape[2]))], dim=1)
tw = torch.mul(t1, conditioning_to_strength) + torch.mul(t0, (1.0 - conditioning_to_strength))
t_to = conditioning_to[i][1].copy()
if pooled_output_from is not None and pooled_output_to is not None:
t_to["pooled_output"] = torch.mul(pooled_output_to, conditioning_to_strength) + torch.mul(pooled_output_from, (1.0 - conditioning_to_strength))
elif pooled_output_from is not None:
t_to["pooled_output"] = pooled_output_from
n = [tw, t_to]
out.append(n)
return (out, show_help, )
if mix_method == "Concatenate":
out = []
if len(conditioning_from) > 1:
print("Warning: ConditioningConcat conditioning_from contains more than 1 cond, only the first one will actually be applied to conditioning_to.")
cond_from = conditioning_from[0][0]
for i in range(len(conditioning_to)):
t1 = conditioning_to[i][0]
tw = torch.cat((t1, cond_from),1)
n = [tw, conditioning_to[i][1].copy()]
out.append(n)
return (out, show_help, )
#---------------------------------------------------------------------------------------------------------------------#
class CR_SelectModel:
@classmethod
def INPUT_TYPES(cls):
checkpoint_files = ["None"] + folder_paths.get_filename_list("checkpoints")
return {"required": {"ckpt_name1": (checkpoint_files,),
"ckpt_name2": (checkpoint_files,),
"ckpt_name3": (checkpoint_files,),
"ckpt_name4": (checkpoint_files,),
"ckpt_name5": (checkpoint_files,),
"select_model": ("INT", {"default": 1, "min": 1, "max": 5}),
}
}
RETURN_TYPES = ("MODEL", "CLIP", "VAE", "STRING", "STRING", )
RETURN_NAMES = ("MODEL", "CLIP", "VAE", "ckpt_name", "show_help", )
FUNCTION = "select_model"
CATEGORY = icons.get("Comfyroll/Other")
def select_model(self, ckpt_name1, ckpt_name2, ckpt_name3, ckpt_name4, ckpt_name5, select_model):
# Initialise the list
model_list = list()
if select_model == 1:
model_name = ckpt_name1
elif select_model == 2:
model_name = ckpt_name2
elif select_model == 3:
model_name = ckpt_name3
elif select_model == 4:
model_name = ckpt_name4
elif select_model == 5:
model_name = ckpt_name5
if model_name == "None":
print(f"CR Select Model: No model selected")
return()
ckpt_path = folder_paths.get_full_path("checkpoints", model_name)
model, clip, vae, clipvision = comfy.sd.load_checkpoint_guess_config(ckpt_path, output_vae=True, output_clip=True,
embedding_directory=folder_paths.get_folder_paths("embeddings"))
show_help = "https://github.com/Suzie1/ComfyUI_Comfyroll_CustomNodes/wiki/Other-Nodes#cr-select-model"
return (model, clip, vae, model_name, show_help, )
#---------------------------------------------------------------------------------------------------------------------#
# MAPPINGS
#---------------------------------------------------------------------------------------------------------------------#
# For reference only, actual mappings are in __init__.py
'''
NODE_CLASS_MAPPINGS = {
### Aspect ratio
"CR SD1.5 Aspect Ratio": CR_AspectRatioSD15,
"CR SDXL Aspect Ratio":CR_SDXLAspectRatio,
"CR Aspect Ratio": CR_AspectRatio,
### Other
"CR Image Output": CR_ImageOutput,
"CR Integer Multiple": CR_IntegerMultipleOf,
"CR Latent Batch Size":CR_LatentBatchSize
"CR Seed":CR_Seed,
"CR Prompt Text":CR_PromptText,
"CR Split String":CR_SplitString,
"CR Value": CR_Value,
"CR Conditioning Mixer":CR_ConditioningMixer,
"CR Select Model": CR_SelectModel,
}
'''