""" Custom nodes for SDXL in ComfyUI MIT License Copyright (c) 2023 Searge 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 from PIL.PngImagePlugin import PngInfo # from ._experimental import SeargeSamplerAdvanced from .custom_sdxl_ksampler import sdxl_ksampler # ==================================================================================================== # 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": 0xffffffffffffffff}), "steps": ("INT", {"default": 30, "min": 1, "max": 1000}), "cfg": ("FLOAT", {"default": 7.0, "min": 0.0, "max": 100.0, "step": 0.5}), "sampler_name": (comfy.samplers.KSampler.SAMPLERS, {"default": "dpmpp_2m"}), "scheduler": (comfy.samplers.KSampler.SCHEDULERS, {"default": "karras"}), "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}), }, } RETURN_TYPES = ("LATENT",) RETURN_NAMES = ("",) FUNCTION = "sample" CATEGORY = "Searge/_deprecated_/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): base_steps = int(steps * base_ratio) if denoise < 0.01: return (latent_image,) if base_steps >= steps: return nodes.common_ksampler(base_model, noise_seed, steps, cfg, sampler_name, scheduler, base_positive, base_negative, latent_image, denoise=denoise, disable_noise=False, start_step=0, 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, latent_image, denoise=denoise, disable_noise=False, start_step=0, last_step=base_steps, force_full_denoise=False) return nodes.common_ksampler(refiner_model, noise_seed, steps, cfg, sampler_name, scheduler, refiner_positive, refiner_negative, base_result[0], denoise=1.0, disable_noise=True, start_step=base_steps, last_step=steps, force_full_denoise=True) # ==================================================================================================== # SDXL Sampler with base and refiner support # ==================================================================================================== class SeargeSDXLSamplerV3: @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": ("SAMPLER_NAME", {"default": "ddim"}), "scheduler": ("SCHEDULER_NAME", {"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}), }, } RETURN_TYPES = ("LATENT",) FUNCTION = "sample" CATEGORY = "Searge/_deprecated_/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): base_steps = int(steps * (base_ratio + 0.0001)) refiner_steps = max(0, steps - base_steps) 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) return sdxl_ksampler(base_model, refiner_model, noise_seed, base_steps, refiner_steps, cfg, sampler_name, scheduler, base_positive, base_negative, refiner_positive, refiner_negative, input_latent, denoise=denoise, disable_noise=False, start_step=start_at_step, 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": ("SAMPLER_NAME", {"default": "ddim"}), "scheduler": ("SCHEDULER_NAME", {"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.0, "min": 0.0, "max": 1.0, "step": 0.05}), }, } RETURN_TYPES = ("IMAGE",) FUNCTION = "sample" CATEGORY = "Searge/_deprecated_/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 and softness > 0.0001: 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 Sampler with base and refiner support # ==================================================================================================== class SeargeSDXLSampler2: @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": ("SAMPLER_NAME", {"default": "ddim"}), "scheduler": ("SCHEDULER_NAME", {"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/_deprecated_/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 SeargeSDXLImage2ImageSampler2: @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": ("SAMPLER_NAME", {"default": "ddim"}), "scheduler": ("SCHEDULER_NAME", {"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.0, "min": 0.0, "max": 1.0, "step": 0.05}), }, } RETURN_TYPES = ("IMAGE",) FUNCTION = "sample" CATEGORY = "Searge/_deprecated_/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 and softness > 0.0001: 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,) # ==================================================================================================== # UI: Parameter Processor # ==================================================================================================== class SeargeParameterProcessor: # hard - refiner uses same seed as base | soft - refiner uses different seed from base REFINER_INTENSITY = ["hard", "soft"] # same - HRF uses same seed as image generation | distinct - HRF uses different seed than image generation HRF_SEED_OFFSET = ["same", "distinct"] # simple "boolean"-like type STATES = ["disabled", "enabled"] # operating modes, determine if the latent source is empty or from a source image, inpainting also uses mask OPERATION_MODE = ["text to image", "image to image", "inpainting"] # sorted from easy-to-use to harder-to-use PROMPT_STYLE = ["simple", "3 prompts G+L-N", "subject focus", "style focus", "weighted", "overlay", "subject - style", "style - subject", "style only", "weighted - overlay", "overlay - weighted"] # (work in progress) STYLE_TEMPLATE = ["none", "from preprocessor", "test"] # save folder for generated images SAVE_TO = ["output folder", "input folder"] @classmethod def INPUT_TYPES(s): return {"required": { "inputs": ("PARAMETER_INPUTS",), }, } RETURN_TYPES = ("PARAMETERS",) RETURN_NAMES = ("parameters",) FUNCTION = "process" CATEGORY = "Searge/_deprecated_/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: # "soft" if saturation == SeargeParameterProcessor.REFINER_INTENSITY[1]: parameters["noise_offset"] = 1 # incl. SeargeParameterProcessor.REFINER_INTENSITY[1] -> "hard" else: parameters["noise_offset"] = 0 hires_fix = parameters["hires_fix"] # "disabled" 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: # "soft" if hrf_saturation == SeargeParameterProcessor.REFINER_INTENSITY[1]: parameters["hrf_noise_offset"] = 1 # incl. SeargeParameterProcessor.REFINER_INTENSITY[0] -> "hard" else: parameters["hrf_noise_offset"] = 0 seed_offset = parameters["hrf_seed_offset"] if seed_offset is not None: seed = parameters["seed"] if parameters["seed"] is not None else 0 # "distinct" if seed_offset == SeargeParameterProcessor.HRF_SEED_OFFSET[1]: parameters["hrf_seed"] = seed + 3 # incl. SeargeParameterProcessor.HRF_SEED_OFFSET[0] -> "same" else: parameters["hrf_seed"] = seed style_template = parameters["style_template"] if style_template is not None: # "from preprocessor" if style_template == SeargeParameterProcessor.STYLE_TEMPLATE[1]: # this does nothing here, but will be used in the preprocessor pass # "test" if style_template == SeargeParameterProcessor.STYLE_TEMPLATE[2]: 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"] # incl. SeargeParameterProcessor.STYLE_TEMPLATE[0] -> "none" else: # TODO: apply style based on its name here... pass operation_mode = parameters["operation_mode"] if operation_mode is not None: # "image to image": if operation_mode == SeargeParameterProcessor.OPERATION_MODE[1]: parameters["operation_selector"] = 1 # "inpainting": elif operation_mode == SeargeParameterProcessor.OPERATION_MODE[2]: parameters["operation_selector"] = 2 # incl. SeargeParameterProcessor.OPERATION_MODE[0] -> "text to image": else: parameters["operation_selector"] = 0 # always fully denoise in img2img mode parameters["denoise"] = 1.0 prompt_style = parameters["prompt_style"] if prompt_style is not None: # "simple" if prompt_style == SeargeParameterProcessor.PROMPT_STYLE[0]: parameters["prompt_style_selector"] = 0 parameters["prompt_style_group"] = 0 main_prompt = parameters["main_prompt"] parameters["secondary_prompt"] = main_prompt parameters["style_prompt"] = "" parameters["negative_style"] = "" # "subject focus" elif prompt_style == SeargeParameterProcessor.PROMPT_STYLE[2]: parameters["prompt_style_selector"] = 1 parameters["prompt_style_group"] = 0 # "style focus" elif prompt_style == SeargeParameterProcessor.PROMPT_STYLE[3]: parameters["prompt_style_selector"] = 2 parameters["prompt_style_group"] = 0 # "weighted" elif prompt_style == SeargeParameterProcessor.PROMPT_STYLE[4]: parameters["prompt_style_selector"] = 3 parameters["prompt_style_group"] = 0 # "overlay" elif prompt_style == SeargeParameterProcessor.PROMPT_STYLE[5]: parameters["prompt_style_selector"] = 4 parameters["prompt_style_group"] = 0 # "subject - style" elif prompt_style == SeargeParameterProcessor.PROMPT_STYLE[6]: parameters["prompt_style_selector"] = 0 parameters["prompt_style_group"] = 1 # "style - subject" elif prompt_style == SeargeParameterProcessor.PROMPT_STYLE[7]: parameters["prompt_style_selector"] = 1 parameters["prompt_style_group"] = 1 # "style only" elif prompt_style == SeargeParameterProcessor.PROMPT_STYLE[8]: parameters["prompt_style_selector"] = 2 parameters["prompt_style_group"] = 1 # "weighted - overlay" elif prompt_style == SeargeParameterProcessor.PROMPT_STYLE[9]: parameters["prompt_style_selector"] = 3 parameters["prompt_style_group"] = 1 # "overlay - weighted" elif prompt_style == SeargeParameterProcessor.PROMPT_STYLE[10]: parameters["prompt_style_selector"] = 4 parameters["prompt_style_group"] = 1 # incl. SeargeParameterProcessor.PROMPT_STYLE[1] -> "3 prompts G+L-N" else: parameters["prompt_style_selector"] = 0 parameters["prompt_style_group"] = 0 parameters["style_prompt"] = "" parameters["negative_style"] = "" # 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: # "disabled" 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] # "enabled" 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: Style Processor # ==================================================================================================== class SeargeStylePreprocessor: @classmethod def INPUT_TYPES(s): return {"required": { "inputs": ("PARAMETER_INPUTS",), "active_style_name": ("STRING", {"multiline": False, "default": ""}), "style_definitions": ("STRING", {"multiline": True, "default": "[unfinished work in progress]"}), }, } RETURN_TYPES = ("PARAMETER_INPUTS",) RETURN_NAMES = ("inputs",) FUNCTION = "process" CATEGORY = "Searge/_deprecated_/UI" def process(self, inputs, active_style_name, style_definitions): if inputs is None: inputs = {} style_template = inputs["style_template"] # not "from preprocessor" if style_template is None or style_template != SeargeParameterProcessor.STYLE_TEMPLATE[1]: return (inputs,) # TODO: do what needs to be done to apply the selected style return (inputs,) # ==================================================================================================== # 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/_deprecated_/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: 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": SeargeParameterProcessor.STATES[1]}), "save_directory": (SeargeParameterProcessor.SAVE_TO, {"default": SeargeParameterProcessor.SAVE_TO[0]}), }, "optional": { "inputs": ("PARAMETER_INPUTS",), }, } RETURN_TYPES = ("PARAMETER_INPUTS",) RETURN_NAMES = ("inputs",) FUNCTION = "mux" CATEGORY = "Searge/_deprecated_/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: 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": SeargeParameterProcessor.REFINER_INTENSITY[1]}), "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": SeargeParameterProcessor.STATES[1]}), }, "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/_deprecated_/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: 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/_deprecated_/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: 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": SeargeParameterProcessor.STYLE_TEMPLATE[0]}), }, "optional": { "inputs": ("PARAMETER_INPUTS",), }, } RETURN_TYPES = ("PARAMETER_INPUTS",) RETURN_NAMES = ("inputs",) FUNCTION = "mux" CATEGORY = "Searge/_deprecated_/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: HiResFix Parameters Input # ==================================================================================================== class SeargeInput6: @classmethod def INPUT_TYPES(s): return {"required": { "hires_fix": (SeargeParameterProcessor.STATES, {"default": SeargeParameterProcessor.STATES[1]}), "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": SeargeParameterProcessor.REFINER_INTENSITY[1]}), "hrf_seed_offset": (SeargeParameterProcessor.HRF_SEED_OFFSET, {"default": SeargeParameterProcessor.HRF_SEED_OFFSET[1]}), "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/_deprecated_/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: Misc Inputs # ==================================================================================================== class SeargeInput7: @classmethod def INPUT_TYPES(s): return {"required": { "lora_strength": ("FLOAT", {"default": 0.2, "min": -10.0, "max": 10.0, "step": 0.05}), "operation_mode": (SeargeParameterProcessor.OPERATION_MODE, {"default": SeargeParameterProcessor.OPERATION_MODE[0]}), "prompt_style": (SeargeParameterProcessor.PROMPT_STYLE, {"default": SeargeParameterProcessor.PROMPT_STYLE[0]}), }, "optional": { "inputs": ("PARAMETER_INPUTS",), }, } RETURN_TYPES = ("PARAMETER_INPUTS",) RETURN_NAMES = ("inputs",) FUNCTION = "mux" CATEGORY = "Searge/_deprecated_/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: 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/_deprecated_/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 Output # ==================================================================================================== class SeargeOutput2: @classmethod def INPUT_TYPES(s): return {"required": { "parameters": ("PARAMETERS",), }, } RETURN_TYPES = ("PARAMETERS", "INT", "INT", "INT", "INT", "FLOAT", "SAMPLER_NAME", "SCHEDULER_NAME", "ENABLE_STATE", "SAVE_FOLDER",) RETURN_NAMES = ("parameters", "seed", "image_width", "image_height", "steps", "cfg", "sampler_name", "scheduler", "save_image", "save_directory",) FUNCTION = "demux" CATEGORY = "Searge/_deprecated_/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 Output # ==================================================================================================== class SeargeOutput3: @classmethod def INPUT_TYPES(s): return {"required": { "parameters": ("PARAMETERS",), }, } RETURN_TYPES = ("PARAMETERS", "FLOAT", "FLOAT", "FLOAT", "INT", "INT", "INT", "FLOAT", "ENABLE_STATE",) RETURN_NAMES = ("parameters", "denoise", "base_ratio", "refiner_strength", "noise_offset", "precondition_steps", "batch_size", "upscale_resolution_factor", "save_upscaled_image",) FUNCTION = "demux" CATEGORY = "Searge/_deprecated_/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 # ==================================================================================================== class SeargeOutput4: @classmethod def INPUT_TYPES(s): return {"required": { "model_names": ("MODEL_NAMES",), }, } RETURN_TYPES = ("MODEL_NAMES", "CHECKPOINT_NAME", "CHECKPOINT_NAME", "VAE_NAME", "UPSCALER_NAME", "UPSCALER_NAME", "LORA_NAME",) RETURN_NAMES = ("model_names", "base_model", "refiner_model", "vae_model", "main_upscale_model", "support_upscale_model", "lora_model",) FUNCTION = "demux" CATEGORY = "Searge/_deprecated_/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 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/_deprecated_/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 Output # ==================================================================================================== class SeargeOutput6: @classmethod def INPUT_TYPES(s): return {"required": { "parameters": ("PARAMETERS",), }, } RETURN_TYPES = ("PARAMETERS", "INT", "FLOAT", "FLOAT", "INT", "INT", "ENABLE_STATE", "FLOAT",) RETURN_NAMES = ("parameters", "hrf_steps", "hrf_denoise", "hrf_upscale_factor", "hrf_noise_offset", "hrf_seed", "hires_fix", "hrf_smoothness",) FUNCTION = "demux" CATEGORY = "Searge/_deprecated_/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 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/_deprecated_/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", "INT",) RETURN_NAMES = ("parameters", "operation_selector", "prompt_style_selector", "prompt_style_group",) FUNCTION = "demux" CATEGORY = "Searge/_deprecated_/UI/Generated" def demux(self, parameters): operation_selector = parameters["operation_selector"] prompt_style_selector = parameters["prompt_style_selector"] prompt_style_group = parameters["prompt_style_group"] return (parameters, operation_selector, prompt_style_selector, prompt_style_group,) # ==================================================================================================== # 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/_deprecated_/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/_deprecated_/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/_deprecated_/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/_deprecated_/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/_deprecated_/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,) # ==================================================================================================== # Input: sampler inputs # ==================================================================================================== class SeargeSamplerInputs: @classmethod def INPUT_TYPES(s): return {"required": { "sampler_name": (comfy.samplers.KSampler.SAMPLERS, {"default": "ddim"}), "scheduler": (comfy.samplers.KSampler.SCHEDULERS, {"default": "ddim_uniform"}), }, } RETURN_TYPES = ("SAMPLER_NAME", "SCHEDULER_NAME",) RETURN_NAMES = ("sampler_name", "scheduler",) FUNCTION = "get_value" CATEGORY = "Searge/_deprecated_/Inputs" def get_value(self, sampler_name, scheduler): return (sampler_name, scheduler,) # ==================================================================================================== # Input: enabler inputs # ==================================================================================================== class SeargeEnablerInputs: @classmethod def INPUT_TYPES(s): return {"required": { "state": (SeargeParameterProcessor.STATES, {"default": SeargeParameterProcessor.STATES[1]}), }, } RETURN_TYPES = ("ENABLE_STATE",) RETURN_NAMES = ("state",) FUNCTION = "get_value" CATEGORY = "Searge/_deprecated_/Inputs" def get_value(self, state): return (state,) # ==================================================================================================== # Input: save folder inputs # ==================================================================================================== class SeargeSaveFolderInputs: @classmethod def INPUT_TYPES(s): return {"required": { "save_to": (SeargeParameterProcessor.SAVE_TO, {"default": SeargeParameterProcessor.SAVE_TO[0]}), }, } RETURN_TYPES = ("SAVE_FOLDER",) RETURN_NAMES = ("save_to",) FUNCTION = "get_value" CATEGORY = "Searge/_deprecated_/Inputs" def get_value(self, save_to): return (save_to,) # ==================================================================================================== # 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/_deprecated_/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/_deprecated_/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/_deprecated_/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/_deprecated_/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/_deprecated_/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/_deprecated_/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/_deprecated_/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": ("ENABLE_STATE", {"default": SeargeParameterProcessor.STATES[1]}), "save_to": ("SAVE_FOLDER", {"default": SeargeParameterProcessor.SAVE_TO[0]}), }, "hidden": { "prompt": "PROMPT", "extra_pnginfo": "EXTRA_PNGINFO" }, } RETURN_TYPES = () FUNCTION = "save_images" OUTPUT_NODE = True CATEGORY = "Searge/_deprecated_/Files" def save_images(self, images, filename_prefix, state, save_to, prompt=None, extra_pnginfo=None): # "disabled" if state == SeargeParameterProcessor.STATES[0]: return {} # "input folder" if save_to == SeargeParameterProcessor.SAVE_TO[1]: output_dir = folder_paths.get_input_directory() filename_prefix = "output-%date%" # incl. SeargeParameterProcessor.SAVE_TO[0] -> "output folder" else: 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 args.disable_metadata is None or 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 {} # ==================================================================================================== # Util: custom checkpoint loader # ==================================================================================================== class SeargeCheckpointLoader: def __init__(self): self.chkp_loader = nodes.CheckpointLoaderSimple() @classmethod def INPUT_TYPES(s): return {"required": { "ckpt_name": ("CHECKPOINT_NAME",), }, } RETURN_TYPES = ("MODEL", "CLIP", "VAE",) FUNCTION = "load_checkpoint" CATEGORY = "Searge/_deprecated_/Files" def load_checkpoint(self, ckpt_name): return self.chkp_loader.load_checkpoint(ckpt_name) # ==================================================================================================== # Util: custom vae loader # ==================================================================================================== class SeargeVAELoader: def __init__(self): self.vae_loader = nodes.VAELoader() @classmethod def INPUT_TYPES(s): return {"required": { "vae_name": ("VAE_NAME",), }, } RETURN_TYPES = ("VAE",) FUNCTION = "load_vae" CATEGORY = "Searge/_deprecated_/Files" def load_vae(self, vae_name): return self.vae_loader.load_vae(vae_name) # ==================================================================================================== # Util: custom upscale model loader # ==================================================================================================== class SeargeUpscaleModelLoader: def __init__(self): self.upscale_model_loader = comfy_extras.nodes_upscale_model.UpscaleModelLoader() @classmethod def INPUT_TYPES(s): return {"required": { "upscaler_name": ("UPSCALER_NAME",), }, } RETURN_TYPES = ("UPSCALE_MODEL",) FUNCTION = "load_upscaler" CATEGORY = "Searge/_deprecated_/Files" def load_upscaler(self, upscaler_name): return self.upscale_model_loader.load_model(upscaler_name) # ==================================================================================================== # Util: custom upscale model loader # ==================================================================================================== class SeargeLoraLoader: def __init__(self): self.lora_loader = nodes.LoraLoader() @classmethod def INPUT_TYPES(s): return {"required": { "model": ("MODEL",), "clip": ("CLIP",), "lora_name": ("LORA_NAME",), "strength_model": ("FLOAT", {"default": 1.0, "min": -10.0, "max": 10.0, "step": 0.01}), "strength_clip": ("FLOAT", {"default": 1.0, "min": -10.0, "max": 10.0, "step": 0.01}), }, } RETURN_TYPES = ("MODEL", "CLIP",) FUNCTION = "load_lora" CATEGORY = "Searge/_deprecated_/Files" def load_lora(self, model, clip, lora_name, strength_model, strength_clip): return self.lora_loader.load_lora(model, clip, lora_name, strength_model, strength_clip) # ==================================================================================================== # 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/_deprecated_/FlowControl" def mux(self, input0, input1, input2, input_selector): if input_selector == 1: return (input1,) elif input_selector == 2: return (input2,) else: return (input0,) # ==================================================================================================== # Tool: Muxer for selecting between 5 conditioning inputs # ==================================================================================================== class SeargeConditioningMuxer2: @classmethod def INPUT_TYPES(s): return {"required": { "input0": ("CONDITIONING",), "input1": ("CONDITIONING",), "input_selector": ("INT", {"default": 0, "min": 0, "max": 1}), }, } RETURN_TYPES = ("CONDITIONING",) RETURN_NAMES = ("output",) FUNCTION = "mux" CATEGORY = "Searge/_deprecated_/FlowControl" def mux(self, input0, input1, input_selector): if input_selector == 1: return (input1,) else: 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/_deprecated_/FlowControl" def mux(self, input0, input1, input2, input3, input4, input_selector): if input_selector == 1: return (input1,) elif input_selector == 2: return (input2,) elif input_selector == 3: return (input3,) elif input_selector == 4: return (input4,) else: return (input0,) # ==================================================================================================== # Register nodes in ComfyUI # ==================================================================================================== LEGACY_CLASS_MAPPINGS = { "SeargeSDXLSampler": SeargeSDXLSampler, "SeargeSDXLSampler2": SeargeSDXLSampler2, "SeargeSDXLSamplerV3": SeargeSDXLSamplerV3, "SeargeSDXLImage2ImageSampler": SeargeSDXLImage2ImageSampler, "SeargeSDXLImage2ImageSampler2": SeargeSDXLImage2ImageSampler2, "SeargeSamplerInputs": SeargeSamplerInputs, "SeargeEnablerInputs": SeargeEnablerInputs, "SeargeSaveFolderInputs": SeargeSaveFolderInputs, "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, "SeargeCheckpointLoader": SeargeCheckpointLoader, "SeargeVAELoader": SeargeVAELoader, "SeargeUpscaleModelLoader": SeargeUpscaleModelLoader, "SeargeLoraLoader": SeargeLoraLoader, "SeargeLatentMuxer3": SeargeLatentMuxer3, "SeargeConditioningMuxer2": SeargeConditioningMuxer2, "SeargeConditioningMuxer5": SeargeConditioningMuxer5, "SeargeParameterProcessor": SeargeParameterProcessor, "SeargeStylePreprocessor": SeargeStylePreprocessor, "SeargeInput1": SeargeInput1, "SeargeInput2": SeargeInput2, "SeargeInput3": SeargeInput3, "SeargeInput4": SeargeInput4, "SeargeInput5": SeargeInput5, "SeargeInput6": SeargeInput6, "SeargeInput7": SeargeInput7, "SeargeOutput1": SeargeOutput1, "SeargeOutput2": SeargeOutput2, "SeargeOutput3": SeargeOutput3, "SeargeOutput4": SeargeOutput4, "SeargeOutput5": SeargeOutput5, "SeargeOutput6": SeargeOutput6, "SeargeOutput7": SeargeOutput7, "SeargeGenerated1": SeargeGenerated1, # "SeargeSamplerAdvanced": SeargeSamplerAdvanced, } # ==================================================================================================== # Human readable names for the nodes # ==================================================================================================== LEGACY_DISPLAY_NAME_MAPPINGS = { "SeargeSDXLSampler": "SDXL Sampler v1 (Searge)", "SeargeSDXLSampler2": "SDXL Sampler v2 (Searge)", "SeargeSDXLSamplerV3": "SDXL Sampler v3 (Searge)", "SeargeSDXLImage2ImageSampler": "Image2Image Sampler v1 (Searge)", "SeargeSDXLImage2ImageSampler2": "Image2Image Sampler v2 (Searge)", "SeargeSamplerInputs": "Sampler Settings", "SeargeEnablerInputs": "Enable / Disable", "SeargeSaveFolderInputs": "Save Folder", "SeargeSDXLPromptEncoder": "SDXL Prompt Encoder (Searge)", "SeargeSDXLBasePromptEncoder": "SDXL Base Prompt Encoder (Searge)", "SeargeSDXLRefinerPromptEncoder": "SDXL Refiner Prompt Encoder (Searge)", "SeargePromptText": "Prompt text input", "SeargePromptCombiner": "Prompt combiner", "SeargeIntegerConstant": "Integer Constant", "SeargeIntegerPair": "Integer Pair", "SeargeIntegerMath": "Integer Math", "SeargeIntegerScaler": "Integer Scaler", "SeargeFloatConstant": "Float Constant", "SeargeFloatPair": "Float Pair", "SeargeFloatMath": "Float Math", "SeargeImageSave": "Save Image (Searge)", "SeargeCheckpointLoader": "Checkpoint Loader (Searge)", "SeargeVAELoader": "VAE Loader (Searge)", "SeargeUpscaleModelLoader": "Upscale Model Loader (Searge)", "SeargeLoraLoader": "Lora Loader (Searge)", "SeargeLatentMuxer3": "3-Way Muxer for Latents", "SeargeConditioningMuxer2": "2-Way Muxer for Conditioning", "SeargeConditioningMuxer5": "5-Way Muxer for Conditioning", "SeargeParameterProcessor": "Parameter Processor", "SeargeStylePreprocessor": "Style Preprocessor (wip)", "SeargeInput1": "Prompts", "SeargeInput2": "Generation Parameters", "SeargeInput3": "Advanced Parameters", "SeargeInput4": "Model Names", "SeargeInput5": "Prompt Processing", "SeargeInput6": "HiResFix Parameters", "SeargeInput7": "Misc Parameters", "SeargeOutput1": "Prompts", "SeargeOutput2": "Generation Parameters", "SeargeOutput3": "Advanced Parameters", "SeargeOutput4": "Model Names", "SeargeOutput5": "Prompt Processing", "SeargeOutput6": "HiResFix Parameters", "SeargeOutput7": "Misc Parameters", "SeargeGenerated1": "Flow Control Parameters", # "SeargeSamplerAdvanced": "Experimental Sampler" } """ # ==================================================================================================== # SDXL Image2Image Sampler (incl. HiRes Fix) # ==================================================================================================== class SeargeSDXLImage2ImageSamplerV3: @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": ("SAMPLER_NAME", {"default": "ddim"}), "scheduler": ("SCHEDULER_NAME", {"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}), "softness": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.05}), "parameters": ("PARAMETERS",), # "noise_offset": ("INT", {"default": 1, "min": 0, "max": 1}), # "refiner_strength": ("FLOAT", {"default": 1.0, "min": 0.1, "max": 1.0, "step": 0.05}), }, } RETURN_TYPES = ("IMAGE", ) FUNCTION = "sample" CATEGORY = "Searge/_deprecated_/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): 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, upscale_model=None, scaled_width=None, scaled_height=None, softness=0.0, parameters=None): base_steps = int(steps * (base_ratio + 0.0001)) refiner_steps = max(0, steps - base_steps) # 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 and softness > 0.0001: 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: result_latent = sdxl_ksampler(base_model, refiner_model, noise_seed, base_steps, refiner_steps, cfg, sampler_name, scheduler, base_positive, base_negative, refiner_positive, refiner_negative, input_latent, denoise=denoise, disable_noise=False, start_step=0, 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=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, ) """