Files
blepping-ComfyUI-sonar/py/nodes.py
T

1114 lines
34 KiB
Python

from __future__ import annotations
import abc
import inspect
from types import SimpleNamespace
from typing import Any, Callable
import torch
from comfy import samplers
from . import external, noise
from .noise import NoiseType
from .noise_generation import scale_noise
from .sonar import (
GuidanceConfig,
GuidanceType,
HistoryType,
SonarConfig,
SonarDPMPPSDE,
SonarEuler,
SonarEulerAncestral,
)
class NoisyLatentLikeNode:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"noise_type": (tuple(NoiseType.get_names(skip=(NoiseType.BROWNIAN,))),),
"seed": ("INT", {"default": 0, "min": 0, "max": 0xFFFFFFFFFFFFFFFF}),
"latent": ("LATENT",),
"multiplier": ("FLOAT", {"default": 1.0}),
"add_to_latent": ("BOOLEAN", {"default": False}),
},
"optional": {
"custom_noise_opt": ("SONAR_CUSTOM_NOISE",),
"mul_by_sigmas_opt": ("SIGMAS",),
"model_opt": ("MODEL",),
},
}
RETURN_TYPES = ("LATENT",)
CATEGORY = "latent/noise"
FUNCTION = "go"
def go(
self,
noise_type: str,
seed: None | int,
latent: dict,
multiplier: float = 1.0,
add_to_latent=False,
custom_noise_opt: object | None = None,
mul_by_sigmas_opt: None | torch.Tensor = None,
model_opt: object | None = None,
):
model, sigmas = model_opt, mul_by_sigmas_opt
if sigmas is not None and len(sigmas) > 0:
if model is None:
raise ValueError(
"NoisyLatentLike requires a model when sigmas are connected!",
)
while hasattr(model, "model"):
model = model.model
latent_scale_factor = model.latent_format.scale_factor
max_denoise = samplers.Sampler().max_denoise(
SimpleNamespace(inner_model=model),
sigmas,
)
multiplier *= (
float(
torch.sqrt(1.0 + sigmas[0] ** 2.0) if max_denoise else sigmas[0],
)
/ latent_scale_factor
)
if sigmas is not None and sigmas.numel() > 1:
sigma_min, sigma_max = sigmas[0], sigmas[-1]
sigma, sigma_next = sigmas[0], sigmas[1]
else:
sigma_min, sigma_max, sigma, sigma_next = (None,) * 4
latent_samples = latent["samples"]
if custom_noise_opt is not None:
ns = custom_noise_opt.make_noise_sampler(
latent_samples,
sigma_min=sigma_min,
sigma_max=sigma_max,
)
else:
ns = noise.get_noise_sampler(
NoiseType[noise_type.upper()],
latent_samples,
sigma_min,
sigma_max,
seed=seed,
cpu=True,
)
randst = torch.random.get_rng_state()
try:
torch.random.manual_seed(seed)
result = ns(sigma, sigma_next)
finally:
torch.random.set_rng_state(randst)
result = scale_noise(result, multiplier, normalized=True)
if add_to_latent:
result += latent_samples.to(result)
return ({"samples": result},)
class SonarCustomNoiseNodeBase(abc.ABC):
RETURN_TYPES = ("SONAR_CUSTOM_NOISE",)
CATEGORY = "advanced/noise"
FUNCTION = "go"
@abc.abstractmethod
def get_item_class(self):
raise NotImplementedError
@classmethod
def INPUT_TYPES(cls, *, include_rescale=True, include_chain=True):
result = {
"required": {
"factor": (
"FLOAT",
{
"default": 1.0,
"min": -100.0,
"max": 100.0,
"step": 0.001,
"round": False,
},
),
},
"optional": {},
}
if include_rescale:
result["required"] |= {
"rescale": (
"FLOAT",
{
"default": 0.0,
"min": 0.0,
"max": 100.0,
"step": 0.001,
"round": False,
},
),
}
if include_chain:
result["optional"] |= {
"sonar_custom_noise_opt": ("SONAR_CUSTOM_NOISE",),
}
return result
def go(
self,
factor=1.0,
rescale=0.0,
sonar_custom_noise_opt=None,
**kwargs: dict[str, Any],
):
nis = (
sonar_custom_noise_opt.clone()
if sonar_custom_noise_opt
else noise.CustomNoiseChain()
)
if factor != 0:
nis.add(self.get_item_class()(factor, **kwargs))
return (nis if rescale == 0 else nis.rescaled(rescale),)
class SonarCustomNoiseNode(SonarCustomNoiseNodeBase):
@classmethod
def INPUT_TYPES(cls):
result = super().INPUT_TYPES()
result["required"] |= {
"noise_type": (tuple(NoiseType.get_names()),),
}
return result
def get_item_class(self):
return noise.CustomNoiseItem
class SonarNormalizeNoiseNodeMixin:
@staticmethod
def get_normalize(val: str) -> None | bool:
return None if val == "default" else val == "forced"
class SonarModulatedNoiseNode(SonarCustomNoiseNodeBase, SonarNormalizeNoiseNodeMixin):
@classmethod
def INPUT_TYPES(cls):
result = super().INPUT_TYPES(include_rescale=False, include_chain=False)
result["required"] |= {
"sonar_custom_noise": ("SONAR_CUSTOM_NOISE",),
"modulation_type": (
(
"intensity",
"frequency",
"spectral_signum",
"none",
),
),
"dims": ("INT", {"default": 3, "min": 1, "max": 3}),
"strength": ("FLOAT", {"default": 2.0, "min": -100.0, "max": 100.0}),
"normalize_result": (("default", "forced", "disabled"),),
"normalize_noise": (("default", "forced", "disabled"),),
"normalize_ref": (
"BOOLEAN",
{"default": True},
),
}
result["optional"] |= {"ref_latent_opt": ("LATENT",)}
return result
def get_item_class(self):
return noise.ModulatedNoise
def go(
self,
factor,
sonar_custom_noise,
modulation_type,
dims,
strength,
normalize_result,
normalize_noise,
normalize_ref,
ref_latent_opt=None,
):
if ref_latent_opt is not None:
ref_latent_opt = ref_latent_opt["samples"].clone()
return super().go(
factor,
noise=sonar_custom_noise,
modulation_type=modulation_type,
modulation_dims=dims,
modulation_strength=strength,
normalize_result=self.get_normalize(normalize_result),
normalize_noise=self.get_normalize(normalize_noise),
normalize_ref=self.get_normalize(normalize_ref),
ref_latent_opt=ref_latent_opt,
)
class SonarRepeatedNoiseNode(SonarCustomNoiseNodeBase, SonarNormalizeNoiseNodeMixin):
@classmethod
def INPUT_TYPES(cls):
result = super().INPUT_TYPES(include_rescale=False, include_chain=False)
result["required"] |= {
"sonar_custom_noise": ("SONAR_CUSTOM_NOISE",),
"repeat_length": ("INT", {"default": 8, "min": 1, "max": 100}),
"max_recycle": ("INT", {"default": 1000, "min": 1, "max": 1000}),
"normalize": (("default", "forced", "disabled"),),
"permute": (("enabled", "disabled", "always"),),
}
return result
def get_item_class(self):
return noise.RepeatedNoise
def go(
self,
factor,
sonar_custom_noise,
repeat_length,
max_recycle,
normalize,
permute=True,
):
return super().go(
factor,
noise=sonar_custom_noise,
repeat_length=repeat_length,
max_recycle=max_recycle,
normalize=self.get_normalize(normalize),
permute=permute,
)
class SonarScheduledNoiseNode(SonarCustomNoiseNodeBase, SonarNormalizeNoiseNodeMixin):
@classmethod
def INPUT_TYPES(cls):
result = super().INPUT_TYPES(include_rescale=False, include_chain=False)
result["required"] |= {
"model": ("MODEL",),
"sonar_custom_noise": ("SONAR_CUSTOM_NOISE",),
"start_percent": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0}),
"end_percent": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0}),
"normalize": (("default", "forced", "disabled"),),
}
result["optional"] |= {"fallback_sonar_custom_noise": ("SONAR_CUSTOM_NOISE",)}
return result
def get_item_class(self):
return noise.ScheduledNoise
def go(
self,
model,
factor,
sonar_custom_noise,
start_percent,
end_percent,
normalize,
fallback_sonar_custom_noise=None,
):
ms = model.get_model_object("model_sampling")
start_sigma = ms.percent_to_sigma(start_percent)
end_sigma = ms.percent_to_sigma(end_percent)
return super().go(
factor,
noise=sonar_custom_noise,
start_sigma=start_sigma,
end_sigma=end_sigma,
normalize=self.get_normalize(normalize),
fallback_noise=fallback_sonar_custom_noise,
)
class SonarCompositeNoiseNode(SonarCustomNoiseNodeBase, SonarNormalizeNoiseNodeMixin):
@classmethod
def INPUT_TYPES(cls):
result = super().INPUT_TYPES(include_rescale=False, include_chain=False)
result["required"] |= {
"sonar_custom_noise_dst": ("SONAR_CUSTOM_NOISE",),
"sonar_custom_noise_src": ("SONAR_CUSTOM_NOISE",),
"normalize_dst": (("default", "forced", "disabled"),),
"normalize_src": (("default", "forced", "disabled"),),
"normalize_result": (("default", "forced", "disabled"),),
"mask": ("MASK",),
}
return result
def get_item_class(self):
return noise.CompositeNoise
def go(
self,
factor,
sonar_custom_noise_dst,
sonar_custom_noise_src,
normalize_src,
normalize_dst,
normalize_result,
mask,
):
return super().go(
factor,
dst_noise=sonar_custom_noise_dst,
src_noise=sonar_custom_noise_src,
normalize_dst=self.get_normalize(normalize_src),
normalize_src=self.get_normalize(normalize_dst),
normalize_result=self.get_normalize(normalize_result),
mask=mask,
)
class SonarGuidedNoiseNode(SonarCustomNoiseNodeBase, SonarNormalizeNoiseNodeMixin):
@classmethod
def INPUT_TYPES(cls):
result = super().INPUT_TYPES(include_rescale=False, include_chain=False)
result["required"] |= {
"latent": ("LATENT",),
"sonar_custom_noise": ("SONAR_CUSTOM_NOISE",),
"method": (("euler", "linear"),),
"guidance_factor": (
"FLOAT",
{
"default": 0.0125,
"min": -100.0,
"max": 100.0,
"step": 0.001,
"round": False,
},
),
"normalize_noise": (("default", "forced", "disabled"),),
"normalize_result": (("default", "forced", "disabled"),),
"normalize_ref": (
"BOOLEAN",
{"default": True},
),
}
return result
def get_item_class(self):
return noise.GuidedNoise
def go(
self,
factor,
latent,
sonar_custom_noise,
normalize_noise,
normalize_result,
normalize_ref=True,
method="euler",
guidance_factor=0.5,
):
from .sonar import SonarGuidanceMixin
return super().go(
factor,
ref_latent=scale_noise(
SonarGuidanceMixin.prepare_ref_latent(latent["samples"].clone()),
normalized=normalize_ref,
),
guidance_factor=guidance_factor,
noise=sonar_custom_noise.clone(),
method=method,
normalize_noise=self.get_normalize(normalize_noise),
normalize_result=self.get_normalize(normalize_result),
)
class SonarRandomNoiseNode(SonarCustomNoiseNodeBase, SonarNormalizeNoiseNodeMixin):
@classmethod
def INPUT_TYPES(cls):
result = super().INPUT_TYPES(include_rescale=False, include_chain=False)
result["required"] |= {
"sonar_custom_noise": ("SONAR_CUSTOM_NOISE",),
"mix_count": ("INT", {"default": 1, "min": 1, "max": 100}),
"normalize": (("default", "forced", "disabled"),),
}
return result
def get_item_class(self):
return noise.RandomNoise
def go(
self,
factor,
sonar_custom_noise,
mix_count,
normalize,
):
return super().go(
factor,
noise=sonar_custom_noise,
mix_count=mix_count,
normalize=self.get_normalize(normalize),
)
class GuidanceConfigNode:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"factor": (
"FLOAT",
{
"default": 0.01,
"min": -2.0,
"max": 2.0,
"step": 0.001,
"round": False,
},
),
"guidance_type": (tuple(t.name.lower() for t in GuidanceType),),
"start_step": ("INT", {"default": 1, "min": 1}),
"end_step": ("INT", {"default": 9999, "min": 1}),
"latent": ("LATENT",),
},
}
RETURN_TYPES = ("SONAR_GUIDANCE_CFG",)
CATEGORY = "sampling/custom_sampling/samplers"
FUNCTION = "make_guidance_cfg"
def make_guidance_cfg(
self,
guidance_type,
factor,
start_step,
end_step,
latent,
):
return (
GuidanceConfig(
guidance_type=GuidanceType[guidance_type.upper()],
factor=factor,
start_step=start_step,
end_step=end_step,
latent=latent.get("samples"),
),
)
class SamplerNodeSonarBase:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"momentum": (
"FLOAT",
{
"default": 0.95,
"min": -0.5,
"max": 2.5,
"step": 0.01,
"round": False,
},
),
"momentum_hist": (
"FLOAT",
{
"default": 0.75,
"min": -1.5,
"max": 1.5,
"step": 0.01,
"round": False,
},
),
"momentum_init": (tuple(t.name for t in HistoryType),),
"direction": (
"FLOAT",
{
"default": 1.0,
"min": -30.0,
"max": 15.0,
"step": 0.01,
"round": False,
},
),
"rand_init_noise_type": (
tuple(NoiseType.get_names(skip=(NoiseType.BROWNIAN,))),
),
},
"optional": {
"guidance_cfg_opt": ("SONAR_GUIDANCE_CFG",),
},
}
RETURN_TYPES = ("SAMPLER",)
CATEGORY = "sampling/custom_sampling/samplers"
class SamplerNodeSonarEuler(SamplerNodeSonarBase):
@classmethod
def INPUT_TYPES(cls):
result = super().INPUT_TYPES()
result["required"].update(
{
"s_noise": (
"FLOAT",
{
"default": 1.0,
"min": 0.0,
"max": 100.0,
"step": 0.01,
"round": False,
},
),
},
)
return result
RETURN_TYPES = ("SAMPLER",)
CATEGORY = "sampling/custom_sampling/samplers"
FUNCTION = "get_sampler"
def get_sampler(
self,
momentum,
momentum_hist,
momentum_init,
direction,
rand_init_noise_type,
s_noise,
guidance_cfg_opt=None,
):
cfg = SonarConfig(
momentum=momentum,
init=HistoryType[momentum_init.upper()],
momentum_hist=momentum_hist,
direction=direction,
rand_init_noise_type=NoiseType[rand_init_noise_type.upper()],
guidance=guidance_cfg_opt,
)
return (
samplers.KSAMPLER(
SonarEuler.sampler,
{
"s_noise": s_noise,
"sonar_config": cfg,
},
),
)
class SamplerNodeSonarEulerAncestral(SamplerNodeSonarEuler):
@classmethod
def INPUT_TYPES(cls):
result = super().INPUT_TYPES()
result["required"].update(
{
"eta": (
"FLOAT",
{
"default": 1.0,
"min": 0.0,
"max": 100.0,
"step": 0.01,
"round": False,
},
),
"noise_type": (tuple(NoiseType.get_names()),),
},
)
result["optional"].update(
{
"custom_noise_opt": ("SONAR_CUSTOM_NOISE",),
},
)
return result
def get_sampler(
self,
momentum,
momentum_hist,
momentum_init,
direction,
rand_init_noise_type,
noise_type,
eta,
s_noise,
guidance_cfg_opt=None,
custom_noise_opt=None,
):
cfg = SonarConfig(
momentum=momentum,
init=HistoryType[momentum_init.upper()],
momentum_hist=momentum_hist,
direction=direction,
rand_init_noise_type=NoiseType[rand_init_noise_type.upper()],
noise_type=NoiseType[noise_type.upper()],
custom_noise=custom_noise_opt.clone() if custom_noise_opt else None,
guidance=guidance_cfg_opt,
)
return (
samplers.KSAMPLER(
SonarEulerAncestral.sampler,
{
"sonar_config": cfg,
"eta": eta,
"s_noise": s_noise,
},
),
)
class SamplerNodeSonarDPMPPSDE(SamplerNodeSonarEuler):
@classmethod
def INPUT_TYPES(cls):
result = super().INPUT_TYPES()
result["required"].update(
{
"eta": (
"FLOAT",
{
"default": 1.0,
"min": 0.0,
"max": 100.0,
"step": 0.01,
"round": False,
},
),
"noise_type": (tuple(NoiseType.get_names(default=NoiseType.BROWNIAN)),),
},
)
result["optional"].update(
{
"custom_noise_opt": ("SONAR_CUSTOM_NOISE",),
},
)
return result
def get_sampler(
self,
momentum,
momentum_hist,
momentum_init,
direction,
rand_init_noise_type,
noise_type,
eta,
s_noise,
guidance_cfg_opt=None,
custom_noise_opt=None,
):
cfg = SonarConfig(
momentum=momentum,
init=HistoryType[momentum_init.upper()],
momentum_hist=momentum_hist,
direction=direction,
rand_init_noise_type=NoiseType[rand_init_noise_type.upper()],
noise_type=NoiseType[noise_type.upper()],
custom_noise=custom_noise_opt.clone() if custom_noise_opt else None,
guidance=guidance_cfg_opt,
)
return (
samplers.KSAMPLER(
SonarDPMPPSDE.sampler,
{
"sonar_config": cfg,
"eta": eta,
"s_noise": s_noise,
},
),
)
class SamplerNodeConfigOverride:
KWARG_OVERRIDES = ("s_noise", "eta", "s_churn", "r", "solver_type")
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"sampler": ("SAMPLER",),
"eta": (
"FLOAT",
{
"default": 1.0,
"step": 0.01,
"round": False,
},
),
"s_noise": (
"FLOAT",
{
"default": 1.0,
"step": 0.01,
"round": False,
},
),
"s_churn": (
"FLOAT",
{
"default": 0.0,
"min": 0.0,
"step": 0.01,
"round": False,
},
),
"r": (
"FLOAT",
{
"default": 0.5,
"step": 0.01,
"round": False,
},
),
"sde_solver": (("midpoint", "heun"),),
"cpu_noise": ("BOOLEAN", {"default": True}),
"normalize": ("BOOLEAN", {"default": True}),
},
"optional": {
"noise_type": (tuple(NoiseType.get_names()),),
"custom_noise_opt": ("SONAR_CUSTOM_NOISE",),
},
}
RETURN_TYPES = ("SAMPLER",)
CATEGORY = "sampling/custom_sampling/samplers"
FUNCTION = "get_sampler"
def get_sampler(
self,
sampler,
eta,
s_noise,
s_churn,
r,
sde_solver,
cpu_noise=True,
noise_type=None,
custom_noise_opt=None,
normalize=True,
):
return (
samplers.KSAMPLER(
self.sampler_function,
extra_options=sampler.extra_options
| {
"override_sampler_cfg": {
"sampler": sampler,
"noise_type": NoiseType[noise_type.upper()]
if noise_type is not None
else None,
"custom_noise": custom_noise_opt,
"s_noise": s_noise,
"eta": eta,
"s_churn": s_churn,
"r": r,
"solver_type": sde_solver,
"cpu_noise": cpu_noise,
"normalize": normalize,
},
},
inpaint_options=sampler.inpaint_options | {},
),
)
@classmethod
@torch.no_grad()
def sampler_function(
cls,
model,
x,
sigmas,
*args: list[Any],
override_sampler_cfg: dict[str, Any] | None = None,
noise_sampler: Callable | None = None,
extra_args: dict[str, Any] | None = None,
**kwargs: dict[str, Any],
):
if not override_sampler_cfg:
raise ValueError("Override sampler config missing!")
if extra_args is None:
extra_args = {}
cfg = override_sampler_cfg
sampler, noise_type, custom_noise, cpu, normalize = (
cfg["sampler"],
cfg.get("noise_type"),
cfg.get("custom_noise"),
cfg.get("cpu_noise", True),
cfg.get("normalize", True),
)
sigma_min, sigma_max = sigmas[sigmas > 0].min(), sigmas.max()
seed = extra_args.get("seed")
if custom_noise is not None:
noise_sampler = custom_noise.make_noise_sampler(
x,
sigma_min,
sigma_max,
seed=seed,
cpu=cpu,
normalized=normalize,
)
elif noise_type is not None:
noise_sampler = noise.get_noise_sampler(
noise_type,
x,
sigma_min,
sigma_max,
seed=seed,
cpu=cpu,
normalized=normalize,
)
sig = inspect.signature(sampler.sampler_function)
params = sig.parameters
kwargs = kwargs | {}
if "noise_sampler" in params:
kwargs["noise_sampler"] = noise_sampler
for k in cls.KWARG_OVERRIDES:
if k not in params or cfg.get(k) is None:
continue
kwargs[k] = cfg[k]
return sampler.sampler_function(
model,
x,
sigmas,
*args,
extra_args=extra_args,
**kwargs,
)
NODE_CLASS_MAPPINGS = {
"SamplerSonarEuler": SamplerNodeSonarEuler,
"SamplerSonarEulerA": SamplerNodeSonarEulerAncestral,
"SamplerSonarDPMPPSDE": SamplerNodeSonarDPMPPSDE,
"SonarGuidanceConfig": GuidanceConfigNode,
"SamplerConfigOverride": SamplerNodeConfigOverride,
"NoisyLatentLike": NoisyLatentLikeNode,
"SonarCustomNoise": SonarCustomNoiseNode,
"SonarCompositeNoise": SonarCompositeNoiseNode,
"SonarModulatedNoise": SonarModulatedNoiseNode,
"SonarRepeatedNoise": SonarRepeatedNoiseNode,
"SonarScheduledNoise": SonarScheduledNoiseNode,
"SonarGuidedNoise": SonarGuidedNoiseNode,
"SonarRandomNoise": SonarRandomNoiseNode,
}
NODE_DISPLAY_NAME_MAPPINGS = {}
if "bleh" in external.MODULES:
bleh = external.MODULES["bleh"]
bleh_latentutils = bleh.py.latent_utils
class SonarBlendFilterNoiseNode(
SonarCustomNoiseNodeBase,
SonarNormalizeNoiseNodeMixin,
):
@classmethod
def INPUT_TYPES(cls):
result = super().INPUT_TYPES(include_rescale=False, include_chain=False)
result["required"] |= {
"sonar_custom_noise": ("SONAR_CUSTOM_NOISE",),
"blend_mode": (
("simple_add", *bleh_latentutils.BLENDING_MODES.keys()),
),
"ffilter": (tuple(bleh_latentutils.FILTER_PRESETS.keys()),),
"ffilter_custom": ("STRING", {"default": ""}),
"ffilter_scale": (
"FLOAT",
{"default": 1.0, "min": -100.0, "max": 100.0},
),
"ffilter_strength": (
"FLOAT",
{"default": 0.0, "min": -100.0, "max": 100.0},
),
"ffilter_threshold": (
"INT",
{"default": 1, "min": 1, "max": 32},
),
"enhance_mode": (("none", *bleh_latentutils.ENHANCE_METHODS),),
"enhance_strength": (
"FLOAT",
{"default": 0.0, "min": -100.0, "max": 100.0},
),
"affect": (("result", "noise", "both"),),
"normalize_result": (("default", "forced", "disabled"),),
"normalize_noise": (("default", "forced", "disabled"),),
}
return result
def get_item_class(self):
return noise.BlendFilterNoise
def go(
self,
factor,
sonar_custom_noise,
blend_mode,
ffilter,
ffilter_custom,
ffilter_scale,
ffilter_strength,
ffilter_threshold,
enhance_mode,
enhance_strength,
affect,
normalize_result,
normalize_noise,
):
import ast
ffilter_custom = ffilter_custom.strip()
normalize_result = (
None if normalize_result == "default" else normalize_result == "forced"
)
normalize_noise = (
None if normalize_noise == "default" else normalize_noise == "forced"
)
if ffilter_custom:
ffilter = ast.literal_eval(f"[{ffilter_custom}]")
else:
ffilter = bleh_latentutils.FILTER_PRESETS[ffilter]
return super().go(
factor,
noise=sonar_custom_noise.clone(),
blend_mode=blend_mode,
ffilter=ffilter,
ffilter_scale=ffilter_scale,
ffilter_strength=ffilter_strength,
ffilter_threshold=ffilter_threshold,
enhance_mode=enhance_mode,
enhance_strength=enhance_strength,
affect=affect,
normalize_noise=self.get_normalize(normalize_noise),
normalize_result=self.get_normalize(normalize_result),
)
NODE_CLASS_MAPPINGS["SonarBlendFilterNoise"] = SonarBlendFilterNoiseNode
if "restart" in external.MODULES:
rs = external.MODULES["restart"]
class KRestartSamplerCustomNoise:
@classmethod
def INPUT_TYPES(cls):
get_normal_schedulers = getattr(
rs.nodes,
"get_supported_normal_schedulers",
rs.nodes.get_supported_restart_schedulers,
)
return {
"required": {
"model": ("MODEL",),
"add_noise": (["enable", "disable"],),
"noise_seed": (
"INT",
{"default": 0, "min": 0, "max": 0xFFFFFFFFFFFFFFFF},
),
"steps": ("INT", {"default": 20, "min": 1, "max": 10000}),
"cfg": ("FLOAT", {"default": 8.0, "min": 0.0, "max": 100.0}),
"sampler": ("SAMPLER",),
"scheduler": (get_normal_schedulers(),),
"positive": ("CONDITIONING",),
"negative": ("CONDITIONING",),
"latent_image": ("LATENT",),
"start_at_step": ("INT", {"default": 0, "min": 0, "max": 10000}),
"end_at_step": ("INT", {"default": 10000, "min": 0, "max": 10000}),
"return_with_leftover_noise": (["disable", "enable"],),
"segments": (
"STRING",
{
"default": rs.restart_sampling.DEFAULT_SEGMENTS,
"multiline": False,
},
),
"restart_scheduler": (rs.nodes.get_supported_restart_schedulers(),),
"chunked_mode": ("BOOLEAN", {"default": True}),
},
"optional": {
"custom_noise_opt": ("SONAR_CUSTOM_NOISE",),
},
}
RETURN_TYPES = ("LATENT", "LATENT")
RETURN_NAMES = ("output", "denoised_output")
FUNCTION = "sample"
CATEGORY = "sampling"
def sample(
self,
model,
add_noise,
noise_seed,
steps,
cfg,
sampler,
scheduler,
positive,
negative,
latent_image,
start_at_step,
end_at_step,
return_with_leftover_noise,
segments,
restart_scheduler,
chunked_mode=False,
custom_noise_opt=None,
):
return rs.restart_sampling.restart_sampling(
model,
noise_seed,
steps,
cfg,
sampler,
scheduler,
positive,
negative,
latent_image,
segments,
restart_scheduler,
disable_noise=add_noise == "disable",
step_range=(start_at_step, end_at_step),
force_full_denoise=return_with_leftover_noise != "enable",
output_only=False,
chunked_mode=chunked_mode,
custom_noise=custom_noise_opt.make_noise_sampler
if custom_noise_opt
else None,
)
NODE_CLASS_MAPPINGS["KRestartSamplerCustomNoise"] = KRestartSamplerCustomNoise
if hasattr(rs.restart_sampling, "RestartSampler"):
class RestartSamplerCustomNoise:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"sampler": ("SAMPLER",),
"chunked_mode": ("BOOLEAN", {"default": True}),
},
"optional": {
"custom_noise_opt": ("SONAR_CUSTOM_NOISE",),
},
}
RETURN_TYPES = ("SAMPLER",)
FUNCTION = "go"
CATEGORY = "sampling/custom_sampling/samplers"
def go(self, sampler, chunked_mode, custom_noise_opt=None):
restart_options = {
"restart_chunked": chunked_mode,
"restart_wrapped_sampler": sampler,
"restart_custom_noise": None
if custom_noise_opt is None
else custom_noise_opt.make_noise_sampler,
}
restart_sampler = samplers.KSAMPLER(
rs.restart_sampling.RestartSampler.sampler_function,
extra_options=sampler.extra_options | restart_options,
inpaint_options=sampler.inpaint_options,
)
return (restart_sampler,)
NODE_CLASS_MAPPINGS["RestartSamplerCustomNoise"] = RestartSamplerCustomNoise