Files
SeargeDP-SeargeSDXL/searge_sdxl_sampler_node.py
T
2023-08-03 08:48:06 +02:00

1439 lines
56 KiB
Python

"""
Custom nodes for SDXL in ComfyUI
MIT License
Copyright (c) 2023 SeargeDP
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""
import datetime
import comfy.samplers
import comfy_extras.nodes_upscale_model
import comfy_extras.nodes_post_processing
import folder_paths
import json
import nodes
import numpy as np
import os
from comfy.cli_args import args
from datetime import datetime
from PIL import Image, ImageOps
from PIL.PngImagePlugin import PngInfo
# SDXL Sampler with base and refiner support
class SeargeSDXLSampler:
@classmethod
def INPUT_TYPES(s):
return {"required": {
"base_model": ("MODEL",),
"base_positive": ("CONDITIONING", ),
"base_negative": ("CONDITIONING", ),
"refiner_model": ("MODEL",),
"refiner_positive": ("CONDITIONING", ),
"refiner_negative": ("CONDITIONING", ),
"latent_image": ("LATENT", ),
"noise_seed": ("INT", {"default": 0, "min": 0, "max": 0xfffffffffffffff0}),
"steps": ("INT", {"default": 20, "min": 1, "max": 200}),
"cfg": ("FLOAT", {"default": 7.0, "min": 0.0, "max": 30.0, "step": 0.5}),
"sampler_name": (comfy.samplers.KSampler.SAMPLERS, {"default": "ddim"}),
"scheduler": (comfy.samplers.KSampler.SCHEDULERS, {"default": "ddim_uniform"}),
"base_ratio": ("FLOAT", {"default": 0.8, "min": 0.0, "max": 1.0, "step": 0.01}),
"denoise": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}),
},
"optional": {
"refiner_prep_steps": ("INT", {"default": 0, "min": 0, "max": 10}),
"noise_offset": ("INT", {"default": 1, "min": 0, "max": 1}),
"refiner_strength": ("FLOAT", {"default": 1.0, "min": 0.01, "max": 1.0, "step": 0.05}),
},
}
RETURN_TYPES = ("LATENT", )
FUNCTION = "sample"
CATEGORY = "Searge/Sampling"
def sample(self, base_model, base_positive, base_negative, refiner_model, refiner_positive, refiner_negative, latent_image, noise_seed, steps, cfg, sampler_name, scheduler, base_ratio, denoise, refiner_prep_steps=None, noise_offset=None, refiner_strength=None):
base_steps = int(steps * (base_ratio + 0.0001))
if noise_offset is None:
noise_offset = 1
if refiner_strength is None:
refiner_strength = 1.0
if refiner_strength < 0.01:
refiner_strength = 0.01
if denoise < 0.01:
return (latent_image, )
start_at_step = 0
input_latent = latent_image
if refiner_prep_steps is not None:
if refiner_prep_steps >= base_steps:
refiner_prep_steps = base_steps - 1
if refiner_prep_steps > 0:
start_at_step = refiner_prep_steps
precondition_result = nodes.common_ksampler(refiner_model, noise_seed + 2, steps, cfg, sampler_name, scheduler, refiner_positive, refiner_negative, latent_image, denoise=denoise, disable_noise=False, start_step=steps - refiner_prep_steps, last_step=steps, force_full_denoise=False)
input_latent = precondition_result[0]
if base_steps >= steps:
return nodes.common_ksampler(base_model, noise_seed, steps, cfg, sampler_name, scheduler, base_positive, base_negative, input_latent, denoise=denoise, disable_noise=False, start_step=start_at_step, last_step=steps, force_full_denoise=True)
base_result = nodes.common_ksampler(base_model, noise_seed, steps, cfg, sampler_name, scheduler, base_positive, base_negative, input_latent, denoise=denoise, disable_noise=False, start_step=start_at_step, last_step=base_steps, force_full_denoise=True)
return nodes.common_ksampler(refiner_model, noise_seed + noise_offset, steps, cfg, sampler_name, scheduler, refiner_positive, refiner_negative, base_result[0], denoise=denoise * refiner_strength, disable_noise=False, start_step=base_steps, last_step=steps, force_full_denoise=True)
# SDXL Image2Image Sampler (incl. HiRes Fix)
class SeargeSDXLImage2ImageSampler:
@classmethod
def INPUT_TYPES(s):
return {"required": {
"base_model": ("MODEL",),
"base_positive": ("CONDITIONING", ),
"base_negative": ("CONDITIONING", ),
"refiner_model": ("MODEL",),
"refiner_positive": ("CONDITIONING",),
"refiner_negative": ("CONDITIONING",),
"image": ("IMAGE", ),
"vae": ("VAE",),
"noise_seed": ("INT", {"default": 0, "min": 0, "max": 0xfffffffffffffff0}),
"steps": ("INT", {"default": 20, "min": 0, "max": 200}),
"cfg": ("FLOAT", {"default": 7.0, "min": 0.0, "max": 30.0, "step": 0.5}),
"sampler_name": (comfy.samplers.KSampler.SAMPLERS, {"default": "ddim"}),
"scheduler": (comfy.samplers.KSampler.SCHEDULERS, {"default": "ddim_uniform"}),
"base_ratio": ("FLOAT", {"default": 0.8, "min": 0.0, "max": 1.0, "step": 0.01}),
"denoise": ("FLOAT", {"default": 0.25, "min": 0.0, "max": 1.0, "step": 0.01}),
},
"optional": {
"upscale_model": ("UPSCALE_MODEL",),
"scaled_width": ("INT", {"default": 1536, "min": 0, "max": nodes.MAX_RESOLUTION, "step": 8}),
"scaled_height": ("INT", {"default": 1536, "min": 0, "max": nodes.MAX_RESOLUTION, "step": 8}),
"noise_offset": ("INT", {"default": 1, "min": 0, "max": 1}),
"refiner_strength": ("FLOAT", {"default": 1.0, "min": 0.1, "max": 1.0, "step": 0.05}),
"softness": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 1.0, "step": 0.05}),
},
}
RETURN_TYPES = ("IMAGE", )
FUNCTION = "sample"
CATEGORY = "Searge/Sampling"
def sample(self, base_model, base_positive, base_negative, refiner_model, refiner_positive, refiner_negative, image, vae, noise_seed, steps, cfg, sampler_name, scheduler, base_ratio, denoise, softness, upscale_model=None, scaled_width=None, scaled_height=None, noise_offset=None, refiner_strength=None):
base_steps = int(steps * (base_ratio + 0.0001))
if noise_offset is None:
noise_offset = 1
if refiner_strength is None:
refiner_strength = 1.0
if refiner_strength < 0.01:
refiner_strength = 0.01
if steps < 1:
return (image, )
scaled_image = image
use_upscale_model = upscale_model is not None and softness < 0.9999
if use_upscale_model:
upscale_result = comfy_extras.nodes_upscale_model.ImageUpscaleWithModel().upscale(upscale_model, image)
scaled_image = upscale_result[0]
if scaled_width is not None and scaled_height is not None:
upscale_result = nodes.ImageScale().upscale(scaled_image, "bicubic", scaled_width, scaled_height, "center")
scaled_image = upscale_result[0]
if use_upscale_model:
upscale_result = nodes.ImageScale().upscale(image, "bicubic", scaled_width, scaled_height, "center")
scaled_original = upscale_result[0]
blend_result = comfy_extras.nodes_post_processing.Blend().blend_images(scaled_image, scaled_original, softness, "normal")
scaled_image = blend_result[0]
if denoise < 0.01:
return (scaled_image, )
vae_encode_result = nodes.VAEEncode().encode(vae, scaled_image)
input_latent = vae_encode_result[0]
if base_steps >= steps:
result_latent = nodes.common_ksampler(base_model, noise_seed, steps, cfg, sampler_name, scheduler, base_positive, base_negative, input_latent, denoise=denoise, disable_noise=False, start_step=0, last_step=steps, force_full_denoise=True)
else:
base_result = nodes.common_ksampler(base_model, noise_seed, steps, cfg, sampler_name, scheduler, base_positive, base_negative, input_latent, denoise=denoise, disable_noise=False, start_step=0, last_step=base_steps, force_full_denoise=True)
result_latent = nodes.common_ksampler(refiner_model, noise_seed + noise_offset, steps, cfg, sampler_name, scheduler, refiner_positive, refiner_negative, base_result[0], denoise=denoise * refiner_strength, disable_noise=False, start_step=base_steps, last_step=steps, force_full_denoise=True)
vae_decode_result = nodes.VAEDecode().decode(vae, result_latent[0])
output_image = vae_decode_result[0]
return (output_image, )
# SDXL CLIP Text Encoder for prompts with base and refiner support
class SeargeSDXLPromptEncoder:
@classmethod
def INPUT_TYPES(s):
return {"required": {
"base_clip": ("CLIP", ),
"refiner_clip": ("CLIP", ),
"pos_g": ("STRING", {"multiline": True, "default": "POS_G"}),
"pos_l": ("STRING", {"multiline": True, "default": "POS_L"}),
"pos_r": ("STRING", {"multiline": True, "default": "POS_R"}),
"neg_g": ("STRING", {"multiline": True, "default": "NEG_G"}),
"neg_l": ("STRING", {"multiline": True, "default": "NEG_L"}),
"neg_r": ("STRING", {"multiline": True, "default": "NEG_R"}),
"base_width": ("INT", {"default": 4096, "min": 0, "max": nodes.MAX_RESOLUTION, "step": 8}),
"base_height": ("INT", {"default": 4096, "min": 0, "max": nodes.MAX_RESOLUTION, "step": 8}),
"crop_w": ("INT", {"default": 0, "min": 0, "max": nodes.MAX_RESOLUTION, "step": 8}),
"crop_h": ("INT", {"default": 0, "min": 0, "max": nodes.MAX_RESOLUTION, "step": 8}),
"target_width": ("INT", {"default": 4096, "min": 0, "max": nodes.MAX_RESOLUTION, "step": 8}),
"target_height": ("INT", {"default": 4096, "min": 0, "max": nodes.MAX_RESOLUTION, "step": 8}),
"pos_ascore": ("FLOAT", {"default": 6.0, "min": 0.0, "max": 1000.0, "step": 0.01}),
"neg_ascore": ("FLOAT", {"default": 2.5, "min": 0.0, "max": 1000.0, "step": 0.01}),
"refiner_width": ("INT", {"default": 2048, "min": 0, "max": nodes.MAX_RESOLUTION, "step": 8}),
"refiner_height": ("INT", {"default": 2048, "min": 0, "max": nodes.MAX_RESOLUTION, "step": 8}),
},
}
RETURN_TYPES = ("CONDITIONING", "CONDITIONING", "CONDITIONING", "CONDITIONING", )
RETURN_NAMES = ("base_positive", "base_negative", "refiner_positive", "refiner_negative", )
FUNCTION = "encode"
CATEGORY = "Searge/ClipEncoding"
def encode(self, base_clip, refiner_clip, pos_g, pos_l, pos_r, neg_g, neg_l, neg_r, base_width, base_height, crop_w, crop_h, target_width, target_height, pos_ascore, neg_ascore, refiner_width, refiner_height, ):
empty = base_clip.tokenize("")
# positive base prompt
tokens1 = base_clip.tokenize(pos_g)
tokens1["l"] = base_clip.tokenize(pos_l)["l"]
if len(tokens1["l"]) != len(tokens1["g"]):
while len(tokens1["l"]) < len(tokens1["g"]):
tokens1["l"] += empty["l"]
while len(tokens1["l"]) > len(tokens1["g"]):
tokens1["g"] += empty["g"]
cond1, pooled1 = base_clip.encode_from_tokens(tokens1, return_pooled=True)
res1 = [[cond1, {"pooled_output": pooled1, "width": base_width, "height": base_height, "crop_w": crop_w, "crop_h": crop_h, "target_width": target_width, "target_height": target_height}]]
# negative base prompt
tokens2 = base_clip.tokenize(neg_g)
tokens2["l"] = base_clip.tokenize(neg_l)["l"]
if len(tokens2["l"]) != len(tokens2["g"]):
while len(tokens2["l"]) < len(tokens2["g"]):
tokens2["l"] += empty["l"]
while len(tokens2["l"]) > len(tokens2["g"]):
tokens2["g"] += empty["g"]
cond2, pooled2 = base_clip.encode_from_tokens(tokens2, return_pooled=True)
res2 = [[cond2, {"pooled_output": pooled2, "width": base_width, "height": base_height, "crop_w": crop_w, "crop_h": crop_h, "target_width": target_width, "target_height": target_height}]]
# positive refiner prompt
tokens3 = refiner_clip.tokenize(pos_r)
cond3, pooled3 = refiner_clip.encode_from_tokens(tokens3, return_pooled=True)
res3 = [[cond3, {"pooled_output": pooled3, "aesthetic_score": pos_ascore, "width": refiner_width, "height": refiner_height}]]
# negative refiner prompt
tokens4 = refiner_clip.tokenize(neg_r)
cond4, pooled4 = refiner_clip.encode_from_tokens(tokens4, return_pooled=True)
res4 = [[cond4, {"pooled_output": pooled4, "aesthetic_score": neg_ascore, "width": refiner_width, "height": refiner_height}]]
return (res1, res2, res3, res4, )
# SDXL CLIP Text Encoder for base prompts
class SeargeSDXLBasePromptEncoder:
@classmethod
def INPUT_TYPES(s):
return {"required": {
"base_clip": ("CLIP", ),
"pos_g": ("STRING", {"multiline": True, "default": "POS_G"}),
"pos_l": ("STRING", {"multiline": True, "default": "POS_L"}),
"neg_g": ("STRING", {"multiline": True, "default": "NEG_G"}),
"neg_l": ("STRING", {"multiline": True, "default": "NEG_L"}),
"base_width": ("INT", {"default": 4096, "min": 0, "max": nodes.MAX_RESOLUTION, "step": 8}),
"base_height": ("INT", {"default": 4096, "min": 0, "max": nodes.MAX_RESOLUTION, "step": 8}),
"crop_w": ("INT", {"default": 0, "min": 0, "max": nodes.MAX_RESOLUTION, "step": 8}),
"crop_h": ("INT", {"default": 0, "min": 0, "max": nodes.MAX_RESOLUTION, "step": 8}),
"target_width": ("INT", {"default": 4096, "min": 0, "max": nodes.MAX_RESOLUTION, "step": 8}),
"target_height": ("INT", {"default": 4096, "min": 0, "max": nodes.MAX_RESOLUTION, "step": 8}),
},
}
RETURN_TYPES = ("CONDITIONING", "CONDITIONING", )
RETURN_NAMES = ("base_positive", "base_negative", )
FUNCTION = "encode"
CATEGORY = "Searge/ClipEncoding"
def encode(self, base_clip, pos_g, pos_l, neg_g, neg_l, base_width, base_height, crop_w, crop_h, target_width, target_height, ):
empty = base_clip.tokenize("")
# positive base prompt
tokens1 = base_clip.tokenize(pos_g)
tokens1["l"] = base_clip.tokenize(pos_l)["l"]
if len(tokens1["l"]) != len(tokens1["g"]):
while len(tokens1["l"]) < len(tokens1["g"]):
tokens1["l"] += empty["l"]
while len(tokens1["l"]) > len(tokens1["g"]):
tokens1["g"] += empty["g"]
cond1, pooled1 = base_clip.encode_from_tokens(tokens1, return_pooled=True)
res1 = [[cond1, {"pooled_output": pooled1, "width": base_width, "height": base_height, "crop_w": crop_w, "crop_h": crop_h, "target_width": target_width, "target_height": target_height}]]
# negative base prompt
tokens2 = base_clip.tokenize(neg_g)
tokens2["l"] = base_clip.tokenize(neg_l)["l"]
if len(tokens2["l"]) != len(tokens2["g"]):
while len(tokens2["l"]) < len(tokens2["g"]):
tokens2["l"] += empty["l"]
while len(tokens2["l"]) > len(tokens2["g"]):
tokens2["g"] += empty["g"]
cond2, pooled2 = base_clip.encode_from_tokens(tokens2, return_pooled=True)
res2 = [[cond2, {"pooled_output": pooled2, "width": base_width, "height": base_height, "crop_w": crop_w, "crop_h": crop_h, "target_width": target_width, "target_height": target_height}]]
return (res1, res2, )
# SDXL CLIP Text Encoder for prompts with base and refiner support
class SeargeSDXLRefinerPromptEncoder:
@classmethod
def INPUT_TYPES(s):
return {"required": {
"refiner_clip": ("CLIP", ),
"pos_r": ("STRING", {"multiline": True, "default": "POS_R"}),
"neg_r": ("STRING", {"multiline": True, "default": "NEG_R"}),
"pos_ascore": ("FLOAT", {"default": 6.0, "min": 0.0, "max": 1000.0, "step": 0.01}),
"neg_ascore": ("FLOAT", {"default": 2.5, "min": 0.0, "max": 1000.0, "step": 0.01}),
"refiner_width": ("INT", {"default": 2048, "min": 0, "max": nodes.MAX_RESOLUTION, "step": 8}),
"refiner_height": ("INT", {"default": 2048, "min": 0, "max": nodes.MAX_RESOLUTION, "step": 8}),
},
}
RETURN_TYPES = ("CONDITIONING", "CONDITIONING", )
RETURN_NAMES = ("refiner_positive", "refiner_negative", )
FUNCTION = "encode"
CATEGORY = "Searge/ClipEncoding"
def encode(self, refiner_clip, pos_r, neg_r, pos_ascore, neg_ascore, refiner_width, refiner_height, ):
# positive refiner prompt
tokens1 = refiner_clip.tokenize(pos_r)
cond1, pooled1 = refiner_clip.encode_from_tokens(tokens1, return_pooled=True)
res1 = [[cond1, {"pooled_output": pooled1, "aesthetic_score": pos_ascore, "width": refiner_width, "height": refiner_height}]]
# negative refiner prompt
tokens2 = refiner_clip.tokenize(neg_r)
cond2, pooled2 = refiner_clip.encode_from_tokens(tokens2, return_pooled=True)
res2 = [[cond2, {"pooled_output": pooled2, "aesthetic_score": neg_ascore, "width": refiner_width, "height": refiner_height}]]
return (res1, res2, )
# Tool: text input node for prompt text
class SeargePromptText:
@classmethod
def INPUT_TYPES(s):
return {"required": {
"prompt": ("STRING", {"default": "", "multiline": True}),
},
}
RETURN_TYPES = ("STRING", )
RETURN_NAMES = ("prompt", )
FUNCTION = "get_value"
CATEGORY = "Searge/Prompting"
def get_value(self, prompt):
return (prompt,)
# Tool: text input node for prompt text
class SeargePromptCombiner:
@classmethod
def INPUT_TYPES(s):
return {"required": {
"prompt1": ("STRING", {"default": "", "multiline": True}),
"separator": ("STRING", {"default": ", ", "multiline": False}),
"prompt2": ("STRING", {"default": "", "multiline": True}),
},
}
RETURN_TYPES = ("STRING", )
RETURN_NAMES = ("combined prompt", )
FUNCTION = "get_value"
CATEGORY = "Searge/Prompting"
def get_value(self, prompt1, separator, prompt2, ):
len1 = len(prompt1)
len2 = len(prompt2)
prompt = ""
if len1 > 0 and len2 > 0:
prompt = prompt1 + separator + prompt2
elif len1 > 0:
prompt = prompt1
elif len2 > 0:
prompt = prompt2
return (prompt,)
# Tool: integer constant
class SeargeIntegerConstant:
@classmethod
def INPUT_TYPES(s):
return {"required": {
"value": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}),
},
}
RETURN_TYPES = ("INT", )
RETURN_NAMES = ("value", )
FUNCTION = "get_value"
CATEGORY = "Searge/Integers"
def get_value(self, value, ):
return (value,)
# Tool: integer pair
class SeargeIntegerPair:
@classmethod
def INPUT_TYPES(s):
return {"required": {
"value1": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}),
"value2": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}),
},
}
RETURN_TYPES = ("INT", "INT", )
RETURN_NAMES = ("value 1", "value 2", )
FUNCTION = "get_value"
CATEGORY = "Searge/Integers"
def get_value(self, value1, value2, ):
return (value1,value2,)
# Tool: integer math
class SeargeIntegerMath:
OPERATIONS = ["a * b + c", "a + c", "a - c", "a * b", "a / b"]
@classmethod
def INPUT_TYPES(s):
return {"required": {
"op": (SeargeIntegerMath.OPERATIONS, {"default": "a * b + c"}),
"a": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}),
"b": ("INT", {"default": 1, "min": 0, "max": 0xffffffffffffffff}),
"c": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}),
},
}
RETURN_TYPES = ("INT", )
RETURN_NAMES = ("result", )
FUNCTION = "get_value"
CATEGORY = "Searge/Integers"
def get_value(self, op, a, b, c, ):
res = 0
if op == "a * b + c":
res = a * b + c
elif op == "a + c":
res = a + c
elif op == "a - c":
res = a - c
elif op == "a * b":
res = a * b
elif op == "a / b":
res = a // b
return (int(res),)
# Tool: integer scaler
class SeargeIntegerScaler:
@classmethod
def INPUT_TYPES(s):
return {"required": {
"value": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}),
"factor": ("FLOAT", {"default": 1.0, "step": 0.01}),
"multiple_of": ("INT", {"default": 1, "min": 0, "max": 65536}),
},
}
RETURN_TYPES = ("INT", )
RETURN_NAMES = ("value", )
FUNCTION = "get_value"
CATEGORY = "Searge/Integers"
def get_value(self, value, factor, multiple_of, ):
return (int(value * factor // multiple_of) * multiple_of, )
# Tool: float constant
class SeargeFloatConstant:
@classmethod
def INPUT_TYPES(s):
return {"required": {
"value": ("FLOAT", {"default": 0.0, "step": 0.01}),
},
}
RETURN_TYPES = ("FLOAT", )
RETURN_NAMES = ("value", )
FUNCTION = "get_value"
CATEGORY = "Searge/Floats"
def get_value(self, value, ):
return (value,)
# Tool: float pair
class SeargeFloatPair:
@classmethod
def INPUT_TYPES(s):
return {"required": {
"value1": ("FLOAT", {"default": 0.0, "step": 0.01}),
"value2": ("FLOAT", {"default": 0.0, "step": 0.01}),
},
}
RETURN_TYPES = ("FLOAT", "FLOAT", )
RETURN_NAMES = ("value 1", "value 2", )
FUNCTION = "get_value"
CATEGORY = "Searge/Floats"
def get_value(self, value1, value2, ):
return (value1,value2,)
# Tool: float math
class SeargeFloatMath:
OPERATIONS = ["a * b + c", "a + c", "a - c", "a * b", "a / b"]
@classmethod
def INPUT_TYPES(s):
return {"required": {
"op": (SeargeFloatMath.OPERATIONS, {"default": "a * b + c"}),
"a": ("FLOAT", {"default": 0.0, "step": 0.01}),
"b": ("FLOAT", {"default": 1.0, "step": 0.01}),
"c": ("FLOAT", {"default": 0.0, "step": 0.01}),
},
}
RETURN_TYPES = ("FLOAT", )
RETURN_NAMES = ("result", )
FUNCTION = "get_value"
CATEGORY = "Searge/Floats"
def get_value(self, op, a, b, c, ):
res = 0.0
if op == "a * b + c":
res = a * b + c
elif op == "a + c":
res = a + c
elif op == "a - c":
res = a - c
elif op == "a * b":
res = a * b
elif op == "a / b":
res = a / b
return (res,)
# Util: custom save node (without preview)
class SeargeImageSave:
@classmethod
def INPUT_TYPES(s):
return {"required": {
"images": ("IMAGE", ),
"filename_prefix": ("STRING", {"default": "SeargeSDXL-%date%/Image"}),
"state": (SeargeParameterProcessor.STATES, {"default": "enabled"}),
"save_to": (SeargeParameterProcessor.SAVE_TO, {"default": "output folder"}),
},
"hidden": {
"prompt": "PROMPT", "extra_pnginfo": "EXTRA_PNGINFO"
},
}
RETURN_TYPES = ()
FUNCTION = "save_images"
OUTPUT_NODE = True
CATEGORY = "Searge/Files"
def save_images(self, images, filename_prefix, state, save_to, prompt=None, extra_pnginfo=None):
if state == SeargeParameterProcessor.STATES[0]:
return {}
match save_to:
case "input folder":
output_dir = folder_paths.get_input_directory()
filename_prefix = "output-%date%"
case _:
output_dir = folder_paths.get_output_directory()
filename_prefix = filename_prefix.replace("%date%", datetime.now().strftime("%Y-%m-%d"))
full_output_folder, filename, counter, subfolder, filename_prefix = folder_paths.get_save_image_path(filename_prefix, output_dir, images[0].shape[1], images[0].shape[0])
for image in images:
i = 255. * image.cpu().numpy()
img = Image.fromarray(np.clip(i, 0, 255).astype(np.uint8))
metadata = None
if not args.disable_metadata:
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)
counter += 1
return {}
# Tool: Muxer for selecting between 3 latent inputs
class SeargeLatentMuxer3:
@classmethod
def INPUT_TYPES(s):
return {"required": {
"input0": ("LATENT", ),
"input1": ("LATENT", ),
"input2": ("LATENT", ),
"input_selector": ("INT", {"default": 0, "min": 0, "max": 2}),
},
}
RETURN_TYPES = ("LATENT", )
RETURN_NAMES = ("output", )
FUNCTION = "mux"
CATEGORY = "Searge/FlowControl"
def mux(self, input0, input1, input2, input_selector, ):
match input_selector:
case 1:
return (input1,)
case 2:
return (input2,)
case _:
return (input0, )
# Tool: Muxer for selecting between 5 conditioning inputs
class SeargeConditioningMuxer5:
@classmethod
def INPUT_TYPES(s):
return {"required": {
"input0": ("CONDITIONING", ),
"input1": ("CONDITIONING", ),
"input2": ("CONDITIONING", ),
"input3": ("CONDITIONING", ),
"input4": ("CONDITIONING", ),
"input_selector": ("INT", {"default": 0, "min": 0, "max": 4}),
},
}
RETURN_TYPES = ("CONDITIONING", )
RETURN_NAMES = ("output", )
FUNCTION = "mux"
CATEGORY = "Searge/FlowControl"
def mux(self, input0, input1, input2, input3, input4, input_selector, ):
match input_selector:
case 1:
return (input1,)
case 2:
return (input2,)
case 3:
return (input3,)
case 4:
return (input4,)
case _:
return (input0, )
# UI: Parameter Processor
class SeargeParameterProcessor:
REFINER_INTENSITY = ["hard", "soft"]
HRF_SEED_OFFSET = ["same", "distinct"]
STATES = ["disabled", "enabled"]
OPERATION_MODE = ["text to image", "image to image", "inpainting"]
PROMPT_STYLE = ["simple", "subject focus", "style focus", "weighted", "overlay"]
STYLE_TEMPLATE = ["none", "test"]
SAVE_TO = ["output folder", "input folder"]
@classmethod
def INPUT_TYPES(s):
return {"required": {
"inputs": ("PARAMETER_INPUTS", ),
},
"optional": {
},
}
RETURN_TYPES = ("PARAMETERS", )
RETURN_NAMES = ("parameters", )
FUNCTION = "process"
CATEGORY = "Searge/UI"
def process(self, inputs):
if inputs is None:
parameters = {}
else:
parameters = inputs
if parameters["denoise"] is None:
parameters["denoise"] = 1.0
saturation = parameters["refiner_intensity"]
if saturation is not None:
if saturation == SeargeParameterProcessor.REFINER_INTENSITY[0]:
parameters["noise_offset"] = 0
elif saturation == SeargeParameterProcessor.REFINER_INTENSITY[1]:
parameters["noise_offset"] = 1
else:
parameters["noise_offset"] = 1
hires_fix = parameters["hires_fix"]
if hires_fix is not None and hires_fix == SeargeParameterProcessor.STATES[0]:
parameters["hrf_steps"] = 0
hrf_saturation = parameters["hrf_intensity"]
if hrf_saturation is not None:
if hrf_saturation == SeargeParameterProcessor.REFINER_INTENSITY[0]:
parameters["hrf_noise_offset"] = 0
elif hrf_saturation == SeargeParameterProcessor.REFINER_INTENSITY[1]:
parameters["hrf_noise_offset"] = 1
else:
parameters["hrf_noise_offset"] = 1
seed_offset = parameters["hrf_seed_offset"]
if seed_offset is not None:
seed = parameters["seed"] if parameters["seed"] is not None else 0
if seed_offset == SeargeParameterProcessor.HRF_SEED_OFFSET[0]:
parameters["hrf_seed"] = seed
elif seed_offset == SeargeParameterProcessor.HRF_SEED_OFFSET[1]:
parameters["hrf_seed"] = seed + 3
else:
parameters["hrf_seed"] = seed + 3
style_template = parameters["style_template"]
match style_template:
case "none":
pass
case "test":
if parameters["noise_offset"] is not None:
parameters["noise_offset"] = 1 - parameters["hrf_noise_offset"]
if parameters["hrf_noise_offset"] is not None:
parameters["hrf_noise_offset"] = 1 - parameters["hrf_noise_offset"]
case _:
# TODO: apply style based on its name here...
pass
operation_mode = parameters["operation_mode"]
match operation_mode:
case "text to image":
parameters["operation_selector"] = 0
# always fully denoise in img2img mode
parameters["denoise"] = 1.0
case "image to image":
parameters["operation_selector"] = 1
case "inpainting":
parameters["operation_selector"] = 2
# inpainting doesn't support hires fix
parameters["hrf_steps"] = 0
case _:
pass
prompt_style = parameters["prompt_style"]
match prompt_style:
case "simple":
parameters["prompt_style_selector"] = 0
main_prompt = parameters["main_prompt"]
parameters["secondary_prompt"] = main_prompt
parameters["style_prompt"] = ""
parameters["negative_style"] = ""
case "subject focus":
parameters["prompt_style_selector"] = 1
case "style focus":
parameters["prompt_style_selector"] = 2
case "weighted":
parameters["prompt_style_selector"] = 3
case "overlay":
parameters["prompt_style_selector"] = 4
case _:
pass
# TODO: replace this special logic and the dirty hacks by creating new generated parameters for saving
save_image = parameters["save_image"]
if save_image is not None:
if save_image == SeargeParameterProcessor.STATES[0]:
# when image saving is disabled, we also don't want to save the upscaled image, even if that's enabled
parameters["save_upscaled_image"] = SeargeParameterProcessor.STATES[0]
# HACK: this is a bit dirty, but the variable hires_fix determines if the image should be saved
# but when image saving is disabled, we don't want that to happen
parameters["hires_fix"] = SeargeParameterProcessor.STATES[0]
else:
# in case we are saving to the input folder, we need to enable saving after the hires fix, even
# if that's disabled in the settings
if parameters["save_directory"] == SeargeParameterProcessor.SAVE_TO[1]:
parameters["hires_fix"] = SeargeParameterProcessor.STATES[1]
return (parameters, )
# UI: Prompt Inputs
class SeargeInput1:
@classmethod
def INPUT_TYPES(s):
return {"required": {
"main_prompt": ("STRING", {"multiline": True, "default": ""}),
"secondary_prompt": ("STRING", {"multiline": True, "default": ""}),
"style_prompt": ("STRING", {"multiline": True, "default": ""}),
"negative_prompt": ("STRING", {"multiline": True, "default": ""}),
"negative_style": ("STRING", {"multiline": True, "default": ""}),
},
"optional": {
"inputs": ("PARAMETER_INPUTS", ),
"image": ("IMAGE",),
"mask": ("MASK",),
},
}
RETURN_TYPES = ("PARAMETER_INPUTS", )
RETURN_NAMES = ("inputs", )
FUNCTION = "mux"
CATEGORY = "Searge/UI/Inputs"
def mux(self, main_prompt, secondary_prompt, style_prompt, negative_prompt, negative_style, inputs=None, image=None, mask=None):
if inputs is None:
parameters = {}
else:
parameters = inputs
parameters["main_prompt"] = main_prompt
parameters["secondary_prompt"] = secondary_prompt
parameters["style_prompt"] = style_prompt
parameters["negative_prompt"] = negative_prompt
parameters["negative_style"] = negative_style
parameters["image"] = image
parameters["mask"] = mask
return (parameters, )
# UI: Prompt Outputs
class SeargeOutput1:
@classmethod
def INPUT_TYPES(s):
return {"required": {
"parameters": ("PARAMETERS", ),
},
}
RETURN_TYPES = ("PARAMETERS", "STRING", "STRING", "STRING", "STRING", "STRING", "IMAGE", "MASK", )
RETURN_NAMES = ("parameters", "main_prompt", "secondary_prompt", "style_prompt", "negative_prompt", "negative_style", "image", "mask", )
FUNCTION = "demux"
CATEGORY = "Searge/UI/Outputs"
def demux(self, parameters):
main_prompt = parameters["main_prompt"]
secondary_prompt = parameters["secondary_prompt"]
style_prompt = parameters["style_prompt"]
negative_prompt = parameters["negative_prompt"]
negative_style = parameters["negative_style"]
image = parameters["image"]
mask = parameters["mask"]
return (parameters, main_prompt, secondary_prompt, style_prompt, negative_prompt, negative_style, image, mask, )
# UI: Generation Parameters Input
class SeargeInput2:
@classmethod
def INPUT_TYPES(s):
return {"required": {
"seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}),
"image_width": ("INT", {"default": 1024, "min": 0, "max": nodes.MAX_RESOLUTION, "step": 8}),
"image_height": ("INT", {"default": 1024, "min": 0, "max": nodes.MAX_RESOLUTION, "step": 8}),
"steps": ("INT", {"default": 20, "min": 0, "max": 200}),
"cfg": ("FLOAT", {"default": 7.0, "min": 0.0, "max": 30.0, "step": 0.5}),
"sampler_name": (comfy.samplers.KSampler.SAMPLERS, {"default": "ddim"}),
"scheduler": (comfy.samplers.KSampler.SCHEDULERS, {"default": "ddim_uniform"}),
"save_image": (SeargeParameterProcessor.STATES, {"default": "enabled"}),
"save_directory": (SeargeParameterProcessor.SAVE_TO, {"default": "output folder"}),
},
"optional": {
"inputs": ("PARAMETER_INPUTS", ),
},
}
RETURN_TYPES = ("PARAMETER_INPUTS", )
RETURN_NAMES = ("inputs", )
FUNCTION = "mux"
CATEGORY = "Searge/UI/Inputs"
def mux(self, seed, image_width, image_height, steps, cfg, sampler_name, scheduler, save_image, save_directory, inputs=None):
if inputs is None:
parameters = {}
else:
parameters = inputs
parameters["seed"] = seed
parameters["image_width"] = image_width
parameters["image_height"] = image_height
parameters["steps"] = steps
parameters["cfg"] = cfg
parameters["sampler_name"] = sampler_name
parameters["scheduler"] = scheduler
parameters["save_image"] = save_image
parameters["save_directory"] = save_directory
return (parameters, )
# UI: Generation Parameters Output
class SeargeOutput2:
@classmethod
def INPUT_TYPES(s):
return {"required": {
"parameters": ("PARAMETERS", ),
},
}
RETURN_TYPES = ("PARAMETERS", "INT", "INT", "INT", "INT", "FLOAT", comfy.samplers.KSampler.SAMPLERS, comfy.samplers.KSampler.SCHEDULERS, SeargeParameterProcessor.STATES, SeargeParameterProcessor.SAVE_TO, )
RETURN_NAMES = ("parameters", "seed", "image_width", "image_height", "steps", "cfg", "sampler_name", "scheduler", "save_image", "save_directory", )
FUNCTION = "demux"
CATEGORY = "Searge/UI/Outputs"
def demux(self, parameters):
seed = parameters["seed"]
image_width = parameters["image_width"]
image_height = parameters["image_height"]
steps = parameters["steps"]
cfg = parameters["cfg"]
sampler_name = parameters["sampler_name"]
scheduler = parameters["scheduler"]
save_image = parameters["save_image"]
save_directory = parameters["save_directory"]
return (parameters, seed, image_width, image_height, steps, cfg, sampler_name, scheduler, save_image, save_directory, )
# UI: Advanced Parameters Input
class SeargeInput3:
@classmethod
def INPUT_TYPES(s):
return {"required": {
"base_ratio": ("FLOAT", {"default": 0.8, "min": 0.0, "max": 1.0, "step": 0.01}),
"refiner_strength": ("FLOAT", {"default": 1.0, "min": 0.1, "max": 1.0, "step": 0.05}),
"refiner_intensity": (SeargeParameterProcessor.REFINER_INTENSITY, {"default": "soft"}),
"precondition_steps": ("INT", {"default": 0, "min": 0, "max": 10}),
"batch_size": ("INT", {"default": 1, "min": 1, "max": 4}),
"upscale_resolution_factor": ("FLOAT", {"default": 2.0, "min": 0.25, "max": 4.0, "step": 0.25}),
"save_upscaled_image": (SeargeParameterProcessor.STATES, {"default": "enabled"}),
},
"optional": {
"inputs": ("PARAMETER_INPUTS", ),
"denoise": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}),
},
}
RETURN_TYPES = ("PARAMETER_INPUTS", )
RETURN_NAMES = ("inputs", )
FUNCTION = "mux"
CATEGORY = "Searge/UI/Inputs"
def mux(self, base_ratio, refiner_strength, refiner_intensity, precondition_steps, batch_size, upscale_resolution_factor, save_upscaled_image, inputs=None, denoise=None):
if inputs is None:
parameters = {}
else:
parameters = inputs
parameters["denoise"] = denoise
parameters["base_ratio"] = base_ratio
parameters["refiner_strength"] = refiner_strength
parameters["refiner_intensity"] = refiner_intensity
parameters["precondition_steps"] = precondition_steps
parameters["batch_size"] = batch_size
parameters["upscale_resolution_factor"] = upscale_resolution_factor
parameters["save_upscaled_image"] = save_upscaled_image
return (parameters, )
# UI: Advanced Parameters Output
class SeargeOutput3:
@classmethod
def INPUT_TYPES(s):
return {"required": {
"parameters": ("PARAMETERS", ),
},
}
RETURN_TYPES = ("PARAMETERS", "FLOAT", "FLOAT", "FLOAT", "INT", "INT", "INT", "FLOAT", SeargeParameterProcessor.STATES, )
RETURN_NAMES = ("parameters", "denoise", "base_ratio", "refiner_strength", "noise_offset", "precondition_steps", "batch_size", "upscale_resolution_factor", "save_upscaled_image", )
FUNCTION = "demux"
CATEGORY = "Searge/UI/Outputs"
def demux(self, parameters):
denoise = parameters["denoise"]
base_ratio = parameters["base_ratio"]
refiner_strength = parameters["refiner_strength"]
noise_offset = parameters["noise_offset"]
precondition_steps = parameters["precondition_steps"]
batch_size = parameters["batch_size"]
upscale_resolution_factor = parameters["upscale_resolution_factor"]
save_upscaled_image = parameters["save_upscaled_image"]
return (parameters, denoise, base_ratio, refiner_strength, noise_offset, precondition_steps, batch_size, upscale_resolution_factor, save_upscaled_image, )
# UI: Model Selector Input
class SeargeInput4:
@classmethod
def INPUT_TYPES(s):
return {"required": {
"base_model": (folder_paths.get_filename_list("checkpoints"), ),
"refiner_model": (folder_paths.get_filename_list("checkpoints"), ),
"vae_model": (folder_paths.get_filename_list("vae"), ),
"main_upscale_model": (folder_paths.get_filename_list("upscale_models"),),
"support_upscale_model": (folder_paths.get_filename_list("upscale_models"),),
"lora_model": (folder_paths.get_filename_list("loras"),),
},
"optional": {
"model_settings": ("MODEL_SETTINGS", ),
},
}
RETURN_TYPES = ("MODEL_NAMES", )
RETURN_NAMES = ("model_names", )
FUNCTION = "mux"
CATEGORY = "Searge/UI/Inputs"
def mux(self, base_model, refiner_model, vae_model, main_upscale_model, support_upscale_model, lora_model, model_settings=None):
if model_settings is None:
model_names = {}
else:
model_names = model_settings
model_names["base_model"] = base_model
model_names["refiner_model"] = refiner_model
model_names["vae_model"] = vae_model
model_names["main_upscale_model"] = main_upscale_model
model_names["support_upscale_model"] = support_upscale_model
model_names["lora_model"] = lora_model
return (model_names, )
# UI: Model Selector
class SeargeOutput4:
@classmethod
def INPUT_TYPES(s):
return {"required": {
"model_names": ("MODEL_NAMES", ),
},
}
RETURN_TYPES = ("MODEL_NAMES", folder_paths.get_filename_list("checkpoints"), folder_paths.get_filename_list("checkpoints"), folder_paths.get_filename_list("vae"), folder_paths.get_filename_list("upscale_models"), folder_paths.get_filename_list("upscale_models"), folder_paths.get_filename_list("loras"), )
RETURN_NAMES = ("model_names", "base_model", "refiner_model", "vae_model", "main_upscale_model", "support_upscale_model", "lora_model", )
FUNCTION = "demux"
CATEGORY = "Searge/UI/Outputs"
def demux(self, model_names):
base_model = model_names["base_model"]
refiner_model = model_names["refiner_model"]
vae_model = model_names["vae_model"]
main_upscale_model = model_names["main_upscale_model"]
support_upscale_model = model_names["support_upscale_model"]
lora_model = model_names["lora_model"]
return (model_names, base_model, refiner_model, vae_model, main_upscale_model, support_upscale_model, lora_model,)
# UI: Prompt Processing Input
class SeargeInput5:
@classmethod
def INPUT_TYPES(s):
return {"required": {
"base_conditioning_scale": ("FLOAT", {"default": 2.0, "min": 0.25, "max": 4.0, "step": 0.25}),
"refiner_conditioning_scale": ("FLOAT", {"default": 2.0, "min": 0.25, "max": 4.0, "step": 0.25}),
"style_prompt_power": ("FLOAT", {"default": 0.33, "min": 0.0, "max": 1.0, "step": 0.01}),
"negative_style_power": ("FLOAT", {"default": 0.67, "min": 0.0, "max": 1.0, "step": 0.01}),
"style_template": (SeargeParameterProcessor.STYLE_TEMPLATE, {"default": "none"}),
},
"optional": {
"inputs": ("PARAMETER_INPUTS", ),
},
}
RETURN_TYPES = ("PARAMETER_INPUTS", )
RETURN_NAMES = ("inputs", )
FUNCTION = "mux"
CATEGORY = "Searge/UI/Inputs"
def mux(self, base_conditioning_scale, refiner_conditioning_scale, style_prompt_power, negative_style_power, style_template, inputs=None):
if inputs is None:
parameters = {}
else:
parameters = inputs
parameters["base_conditioning_scale"] = base_conditioning_scale
parameters["refiner_conditioning_scale"] = refiner_conditioning_scale
parameters["style_prompt_power"] = style_prompt_power
parameters["negative_style_power"] = negative_style_power
parameters["style_template"] = style_template
return (parameters, )
# UI: Prompt Processing Output
class SeargeOutput5:
@classmethod
def INPUT_TYPES(s):
return {"required": {
"parameters": ("PARAMETERS", ),
},
}
RETURN_TYPES = ("PARAMETERS", "FLOAT", "FLOAT", "FLOAT", "FLOAT", )
RETURN_NAMES = ("parameters", "base_conditioning_scale", "refiner_conditioning_scale", "style_prompt_power", "negative_style_power", )
FUNCTION = "demux"
CATEGORY = "Searge/UI/Outputs"
def demux(self, parameters):
base_conditioning_scale = parameters["base_conditioning_scale"]
refiner_conditioning_scale = parameters["refiner_conditioning_scale"]
style_prompt_power = parameters["style_prompt_power"]
negative_style_power = parameters["negative_style_power"]
return (parameters, base_conditioning_scale, refiner_conditioning_scale, style_prompt_power, negative_style_power, )
# UI: HiResFix Parameters Input
class SeargeInput6:
@classmethod
def INPUT_TYPES(s):
return {"required": {
"hires_fix": (SeargeParameterProcessor.STATES, {"default": "enabled"}),
"hrf_steps": ("INT", {"default": 0, "min": 0, "max": 100}),
"hrf_denoise": ("FLOAT", {"default": 0.1, "min": 0.0, "max": 1.0, "step": 0.01}),
"hrf_upscale_factor": ("FLOAT", {"default": 1.5, "min": 0.25, "max": 4.0, "step": 0.25}),
"hrf_intensity": (SeargeParameterProcessor.REFINER_INTENSITY, {"default": "soft"}),
"hrf_seed_offset": (SeargeParameterProcessor.HRF_SEED_OFFSET, {"default": "distinct"}),
"hrf_smoothness": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.05}),
},
"optional": {
"inputs": ("PARAMETER_INPUTS", ),
},
}
RETURN_TYPES = ("PARAMETER_INPUTS", )
RETURN_NAMES = ("inputs", )
FUNCTION = "mux"
CATEGORY = "Searge/UI/Inputs"
def mux(self, hires_fix, hrf_steps, hrf_denoise, hrf_upscale_factor, hrf_intensity, hrf_seed_offset, hrf_smoothness, inputs=None):
if inputs is None:
parameters = {}
else:
parameters = inputs
parameters["hires_fix"] = hires_fix
parameters["hrf_steps"] = hrf_steps
parameters["hrf_denoise"] = hrf_denoise
parameters["hrf_upscale_factor"] = hrf_upscale_factor
parameters["hrf_intensity"] = hrf_intensity
parameters["hrf_seed_offset"] = hrf_seed_offset
parameters["hrf_smoothness"] = hrf_smoothness
return (parameters, )
# UI: HiResFix Parameters Output
class SeargeOutput6:
@classmethod
def INPUT_TYPES(s):
return {"required": {
"parameters": ("PARAMETERS", ),
},
}
RETURN_TYPES = ("PARAMETERS", "INT", "FLOAT", "FLOAT", "INT", "INT", SeargeParameterProcessor.STATES, "FLOAT", )
RETURN_NAMES = ("parameters", "hrf_steps", "hrf_denoise", "hrf_upscale_factor", "hrf_noise_offset", "hrf_seed", "hires_fix", "hrf_smoothness", )
FUNCTION = "demux"
CATEGORY = "Searge/UI/Outputs"
def demux(self, parameters):
hrf_steps = parameters["hrf_steps"]
hrf_denoise = parameters["hrf_denoise"]
hrf_upscale_factor = parameters["hrf_upscale_factor"]
hrf_noise_offset = parameters["hrf_noise_offset"]
hrf_seed = parameters["hrf_seed"]
hires_fix = parameters["hires_fix"]
hrf_smoothness = parameters["hrf_smoothness"]
return (parameters, hrf_steps, hrf_denoise, hrf_upscale_factor, hrf_noise_offset, hrf_seed, hires_fix, hrf_smoothness, )
# UI: Misc Inputs
class SeargeInput7:
@classmethod
def INPUT_TYPES(s):
return {"required": {
"lora_strength": ("FLOAT", {"default": 0.2, "min": 0.0, "max": 1.0, "step": 0.05}),
"operation_mode": (SeargeParameterProcessor.OPERATION_MODE, {"default": "text to image"}),
"prompt_style": (SeargeParameterProcessor.PROMPT_STYLE, {"default": "simple"}),
},
"optional": {
"inputs": ("PARAMETER_INPUTS", ),
},
}
RETURN_TYPES = ("PARAMETER_INPUTS", )
RETURN_NAMES = ("inputs", )
FUNCTION = "mux"
CATEGORY = "Searge/UI/Inputs"
def mux(self, lora_strength, operation_mode, prompt_style, inputs=None):
if inputs is None:
parameters = {}
else:
parameters = inputs
parameters["lora_strength"] = lora_strength
parameters["operation_mode"] = operation_mode
parameters["prompt_style"] = prompt_style
return (parameters, )
# UI: Misc Outputs
class SeargeOutput7:
@classmethod
def INPUT_TYPES(s):
return {"required": {
"parameters": ("PARAMETERS", ),
},
}
RETURN_TYPES = ("PARAMETERS", "FLOAT", )
RETURN_NAMES = ("parameters", "lora_strength", )
FUNCTION = "demux"
CATEGORY = "Searge/UI/Outputs"
def demux(self, parameters):
lora_strength = parameters["lora_strength"]
return (parameters, lora_strength, )
# UI: Generated outputs for flow control
class SeargeGenerated1:
@classmethod
def INPUT_TYPES(s):
return {"required": {
"parameters": ("PARAMETERS", ),
},
}
RETURN_TYPES = ("PARAMETERS", "INT", "INT", )
RETURN_NAMES = ("parameters", "operation_selector", "prompt_style_selector", )
FUNCTION = "demux"
CATEGORY = "Searge/UI/Generated"
def demux(self, parameters):
operation_selector = parameters["operation_selector"]
prompt_style_selector = parameters["prompt_style_selector"]
return (parameters, operation_selector, prompt_style_selector, )
# Register nodes in ComfyUI
NODE_CLASS_MAPPINGS = {
"SeargeSDXLSampler": SeargeSDXLSampler,
"SeargeSDXLImage2ImageSampler": SeargeSDXLImage2ImageSampler,
"SeargeSDXLPromptEncoder": SeargeSDXLPromptEncoder,
"SeargeSDXLBasePromptEncoder": SeargeSDXLBasePromptEncoder,
"SeargeSDXLRefinerPromptEncoder": SeargeSDXLRefinerPromptEncoder,
"SeargePromptText": SeargePromptText,
"SeargePromptCombiner": SeargePromptCombiner,
"SeargeIntegerConstant": SeargeIntegerConstant,
"SeargeIntegerPair": SeargeIntegerPair,
"SeargeIntegerMath": SeargeIntegerMath,
"SeargeIntegerScaler": SeargeIntegerScaler,
"SeargeFloatConstant": SeargeFloatConstant,
"SeargeFloatPair": SeargeFloatPair,
"SeargeFloatMath": SeargeFloatMath,
"SeargeImageSave": SeargeImageSave,
"SeargeLatentMuxer3": SeargeLatentMuxer3,
"SeargeConditioningMuxer5": SeargeConditioningMuxer5,
"SeargeParameterProcessor": SeargeParameterProcessor,
"SeargeInput1": SeargeInput1,
"SeargeOutput1": SeargeOutput1,
"SeargeInput2": SeargeInput2,
"SeargeOutput2": SeargeOutput2,
"SeargeInput3": SeargeInput3,
"SeargeOutput3": SeargeOutput3,
"SeargeInput4": SeargeInput4,
"SeargeOutput4": SeargeOutput4,
"SeargeInput5": SeargeInput5,
"SeargeOutput5": SeargeOutput5,
"SeargeInput6": SeargeInput6,
"SeargeOutput6": SeargeOutput6,
"SeargeInput7": SeargeInput7,
"SeargeOutput7": SeargeOutput7,
"SeargeGenerated1": SeargeGenerated1,
}
# Human readable names for the nodes
NODE_DISPLAY_NAME_MAPPINGS = {
"SeargeSDXLSampler": "SDXL Sampler (SeargeSDXL)",
"SeargeSDXLImage2ImageSampler": "Image2Image Sampler (SeargeSDXL)",
"SeargeSDXLPromptEncoder": "SDXL Prompt Encoder (SeargeSDXL)",
"SeargeSDXLBasePromptEncoder": "SDXL Base Prompt Encoder (SeargeSDXL)",
"SeargeSDXLRefinerPromptEncoder": "SDXL Refiner Prompt Encoder (SeargeSDXL)",
"SeargePromptText": "Prompt text input (SeargeSDXL)",
"SeargePromptCombiner": "Prompt combiner (SeargeSDXL)",
"SeargeIntegerConstant": "Integer Constant (SeargeSDXL)",
"SeargeIntegerPair": "Integer Pair (SeargeSDXL)",
"SeargeIntegerMath": "Integer Math (SeargeSDXL)",
"SeargeIntegerScaler": "Integer Scaler (SeargeSDXL)",
"SeargeFloatConstant": "Float Constant (SeargeSDXL)",
"SeargeFloatPair": "Float Pair (SeargeSDXL)",
"SeargeFloatMath": "Float Math (SeargeSDXL)",
"SeargeImageSave": "Save Image (SeargeSDXL)",
"SeargeLatentMuxer3": "3-Way Muxer for Latents (SeargeSDXL)",
"SeargeConditioningMuxer5": "5-Way Muxer for Conditioning (SeargeSDXL)",
"SeargeParameterProcessor": "Parameter Processor",
"SeargeInput1": "Prompts",
"SeargeOutput1": "Prompts",
"SeargeInput2": "Generation Parameters",
"SeargeOutput2": "Generation Parameters",
"SeargeInput3": "Advanced Parameters",
"SeargeOutput3": "Advanced Parameters",
"SeargeInput4": "Model Names",
"SeargeOutput4": "Model Names",
"SeargeInput5": "Prompt Processing",
"SeargeOutput5": "Prompt Processing",
"SeargeInput6": "HiResFix Parameters",
"SeargeOutput6": "HiResFix Parameters",
"SeargeInput7": "Misc Parameters",
"SeargeOutput7": "Misc Parameters",
"SeargeGenerated1": "Flow Control Parameters",
}