Add tooltips and descriptions for most nodes.
Add repeat_batch parameter to NoisyLatentLike node. Add a node to convert SONAR_CUSTOM_NOISE to ComfyUI NOISE. Various code cleanups and lint squashing.
This commit is contained in:
@@ -2,6 +2,12 @@
|
||||
|
||||
Note, only relatively significant changes to user-visible functionality will be included here. Most recent changes at the top.
|
||||
|
||||
## 20240823
|
||||
|
||||
* Added descriptions and tooltips for most nodes.
|
||||
* Added `repeat_batch` parameter to `NoisyLatentLike` node.
|
||||
* Added a `SONAR_CUSTOM_NOISE to NOISE` node to allow converting from Sonar's custom noise type to the built in ComfyUI `NOISE` (used by `SamplerCustomAdvanced` and possibly other nodes).
|
||||
|
||||
## 20240521
|
||||
|
||||
Mega update! Many new features, documentation reorganized.
|
||||
|
||||
@@ -74,6 +74,12 @@ This node can be used to override configuration settings for other samplers, inc
|
||||
|
||||
***
|
||||
|
||||
### `SONAR_CUSTOM_NOISE to NOISE`
|
||||
|
||||
This node can be used to convert Sonar custom noise to the `NOISE` type used by the builtin `SamplerCustomAdvanced` (and any other nodes that take a `NOISE` input).
|
||||
|
||||
***
|
||||
|
||||
### `SonarModulatedNoise`
|
||||
|
||||
Experimental noise modulation based on code stolen from
|
||||
|
||||
+105
-20
@@ -36,6 +36,7 @@ BLEND_OPS = (
|
||||
|
||||
|
||||
class FreeUExtremeConfigNode:
|
||||
DESCRIPTION = "Allows setting configuration for FreeU Extreme."
|
||||
RETURN_TYPES = ("FRUX_CONFIG",)
|
||||
FUNCTION = "go"
|
||||
CATEGORY = "model_patches"
|
||||
@@ -44,10 +45,33 @@ class FreeUExtremeConfigNode:
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"stage_1": ("BOOLEAN", {"default": True}),
|
||||
"stage_2": ("BOOLEAN", {"default": False}),
|
||||
"stage_3": ("BOOLEAN", {"default": False}),
|
||||
"target": (("backbone", "skip", "both"),),
|
||||
"stage_1": (
|
||||
"BOOLEAN",
|
||||
{
|
||||
"default": True,
|
||||
"tooltip": "Controls whether this configuration applies to stage 1.",
|
||||
},
|
||||
),
|
||||
"stage_2": (
|
||||
"BOOLEAN",
|
||||
{
|
||||
"default": False,
|
||||
"tooltip": "Controls whether this configuration applies to stage 2.",
|
||||
},
|
||||
),
|
||||
"stage_3": (
|
||||
"BOOLEAN",
|
||||
{
|
||||
"default": False,
|
||||
"tooltip": "Controls whether this configuration applies to stage 3.",
|
||||
},
|
||||
),
|
||||
"target": (
|
||||
("backbone", "skip", "both"),
|
||||
{
|
||||
"tooltip": "Controls whether this filter applies to backbone or skip layers (or both).",
|
||||
},
|
||||
),
|
||||
"start": (
|
||||
"FLOAT",
|
||||
{
|
||||
@@ -56,6 +80,7 @@ class FreeUExtremeConfigNode:
|
||||
"max": 1.0,
|
||||
"step": 0.1,
|
||||
"round": False,
|
||||
"tooltip": "Start time as percentage of sampling this configuration applies to. Inclusive.",
|
||||
},
|
||||
),
|
||||
"end": (
|
||||
@@ -66,6 +91,7 @@ class FreeUExtremeConfigNode:
|
||||
"max": 1.0,
|
||||
"step": 0.1,
|
||||
"round": False,
|
||||
"tooltip": "End time as percentage of sampling this configuration applies to. Inclusive.",
|
||||
},
|
||||
),
|
||||
"slice": (
|
||||
@@ -76,6 +102,7 @@ class FreeUExtremeConfigNode:
|
||||
"max": 1.0,
|
||||
"step": 0.1,
|
||||
"round": False,
|
||||
"tooltip": "Percentage of the layer the FreeU effect is applied to.",
|
||||
},
|
||||
),
|
||||
"slice_offset": (
|
||||
@@ -86,6 +113,7 @@ class FreeUExtremeConfigNode:
|
||||
"max": 1.0,
|
||||
"step": 0.1,
|
||||
"round": False,
|
||||
"tooltip": "Offset as a percentage the layer is applied to. For example if slice is 0.25 and slice_offset is 0.25 then the filter will apply to the range 25% through 50%.",
|
||||
},
|
||||
),
|
||||
"filter_norm": (
|
||||
@@ -96,6 +124,7 @@ class FreeUExtremeConfigNode:
|
||||
"max": 10.0,
|
||||
"step": 0.1,
|
||||
"round": False,
|
||||
"tooltip": "Normalization factor applied to the filter. 1.0 means 100% normalized.",
|
||||
},
|
||||
),
|
||||
"scale": (
|
||||
@@ -106,6 +135,7 @@ class FreeUExtremeConfigNode:
|
||||
"max": 100.0,
|
||||
"step": 0.1,
|
||||
"round": False,
|
||||
"tooltip": "Strength of the effects applied by this configuration.",
|
||||
},
|
||||
),
|
||||
"blend": (
|
||||
@@ -116,19 +146,48 @@ class FreeUExtremeConfigNode:
|
||||
"max": 10.0,
|
||||
"step": 0.1,
|
||||
"round": False,
|
||||
"tooltip": "Blends the filtered result based on the specified strength where 1.0 means 100% filtered.",
|
||||
},
|
||||
),
|
||||
"blend_mode": (
|
||||
tuple(BLEND_OPS.keys()),
|
||||
{
|
||||
"tooltip": "Mode used when blending. Generally only has an effect when blend is set to values other than 0 or 1",
|
||||
},
|
||||
),
|
||||
"hidden_mean": (
|
||||
"BOOLEAN",
|
||||
{
|
||||
"default": True,
|
||||
"tooltip": "You can think of this as FreeU V2 mode.",
|
||||
},
|
||||
),
|
||||
"final": (
|
||||
"BOOLEAN",
|
||||
{
|
||||
"default": True,
|
||||
"tooltip": "When enabled, other configurations won't be considered if this one matched. Otherwise, multiple configurations/filter effects can be stacked.",
|
||||
},
|
||||
),
|
||||
"blend_mode": (tuple(BLEND_OPS.keys()),),
|
||||
"hidden_mean": ("BOOLEAN", {"default": True}),
|
||||
"final": ("BOOLEAN", {"default": True}),
|
||||
},
|
||||
"optional": {
|
||||
"sonar_power_filter_opt": ("SONAR_POWER_FILTER",),
|
||||
"frux_config_opt": ("FRUX_CONFIG",),
|
||||
"sonar_power_filter_opt": (
|
||||
"SONAR_POWER_FILTER",
|
||||
{
|
||||
"tooltip": "Optionally attach a Power Filter here to set filtering parameters.",
|
||||
},
|
||||
),
|
||||
"frux_config_opt": (
|
||||
"FRUX_CONFIG",
|
||||
{
|
||||
"tooltip": "Optionally attach another configuration node here.",
|
||||
},
|
||||
),
|
||||
},
|
||||
}
|
||||
|
||||
def go(self, **kwargs: dict):
|
||||
@classmethod
|
||||
def go(cls, **kwargs: dict):
|
||||
return (FreeUExtremeConfig(**kwargs),)
|
||||
|
||||
|
||||
@@ -223,12 +282,10 @@ class FreeUExtremeConfig:
|
||||
return False
|
||||
if not getattr(self, f"stage_{stage}"):
|
||||
return False
|
||||
if self.target not in ("skip" if is_skip else "backbone", "both"):
|
||||
return False
|
||||
return True
|
||||
return not self.target not in {"skip" if is_skip else "backbone", "both"}
|
||||
|
||||
def apply(self, idx, x, filter_cache, cpu_fft=False):
|
||||
batch, features, height, width = x.shape
|
||||
_batch, features, _height, _width = x.shape
|
||||
scale = self.get_scale(x)
|
||||
slice_size = int(features * self.slice)
|
||||
slice_offs = int(features * self.slice_offset)
|
||||
@@ -280,6 +337,7 @@ class FreeUExtremeConfig:
|
||||
|
||||
|
||||
class FreeUExtremeNode:
|
||||
DESCRIPTION = "Main FreeU Extreme node. Allows patching a model with the FreeU (V2) effect with more control."
|
||||
RETURN_TYPES = ("MODEL",)
|
||||
FUNCTION = "go"
|
||||
CATEGORY = "model_patches"
|
||||
@@ -288,18 +346,45 @@ class FreeUExtremeNode:
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"model": ("MODEL",),
|
||||
"cpu_fft": ("BOOLEAN", {"default": False}),
|
||||
"model": (
|
||||
"MODEL",
|
||||
{
|
||||
"tooltip": "Model to patch.",
|
||||
},
|
||||
),
|
||||
"cpu_fft": (
|
||||
"BOOLEAN",
|
||||
{
|
||||
"default": False,
|
||||
"tooltip": "Controls whether to perform FFT calculations on the CPU. May be necessary for some GPUs that don't have native support for FFT operations at the cost of performance.",
|
||||
},
|
||||
),
|
||||
},
|
||||
"optional": {
|
||||
"input_config": ("FRUX_CONFIG",),
|
||||
"middle_config": ("FRUX_CONFIG",),
|
||||
"output_config": ("FRUX_CONFIG",),
|
||||
"input_config": (
|
||||
"FRUX_CONFIG",
|
||||
{
|
||||
"tooltip": "Allows specifying configuration for input blocks.",
|
||||
},
|
||||
),
|
||||
"middle_config": (
|
||||
"FRUX_CONFIG",
|
||||
{
|
||||
"tooltip": "Allows specifying configuration for middle blocks.",
|
||||
},
|
||||
),
|
||||
"output_config": (
|
||||
"FRUX_CONFIG",
|
||||
{
|
||||
"tooltip": "Allows specifying configuration for output blocks.",
|
||||
},
|
||||
),
|
||||
},
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def go(
|
||||
self,
|
||||
cls,
|
||||
model,
|
||||
cpu_fft,
|
||||
input_config=None,
|
||||
|
||||
+617
-88
File diff suppressed because it is too large
Load Diff
+6
-6
@@ -10,6 +10,7 @@ from torch import Tensor
|
||||
|
||||
from . import external
|
||||
from .noise_generation import *
|
||||
from .sonar import SonarGuidanceMixin
|
||||
|
||||
# ruff: noqa: D412, D413, D417, D212, D407, ANN002, ANN003, FBT001, FBT002, S311
|
||||
|
||||
@@ -220,7 +221,7 @@ class CompositeNoise(CustomNoiseItemBase):
|
||||
)
|
||||
|
||||
def clone_key(self, k):
|
||||
if k in ("mask", "src_noise", "dst_noise"):
|
||||
if k in {"mask", "src_noise", "dst_noise"}:
|
||||
return getattr(self, k).clone()
|
||||
return super().clone_key(k)
|
||||
|
||||
@@ -286,13 +287,11 @@ class GuidedNoise(CustomNoiseItemBase):
|
||||
)
|
||||
|
||||
def clone_key(self, k):
|
||||
if k in ("noise", "ref_latent"):
|
||||
if k in {"noise", "ref_latent"}:
|
||||
return getattr(self, k).clone()
|
||||
return super().clone_key(k)
|
||||
|
||||
def make_noise_sampler(self, x, *args, normalized=True, **kwargs):
|
||||
from .sonar import SonarGuidanceMixin
|
||||
|
||||
factor, guidance_factor = self.factor, self.guidance_factor
|
||||
normalize_noise, normalize_result = (
|
||||
self.get_normalize(f"normalize_{k}", normalized)
|
||||
@@ -325,6 +324,7 @@ class GuidedNoise(CustomNoiseItemBase):
|
||||
factor,
|
||||
normalized=normalize_result,
|
||||
)
|
||||
|
||||
case "euler":
|
||||
|
||||
def noise_sampler(s, sn):
|
||||
@@ -884,8 +884,8 @@ if "bleh" in external.MODULES:
|
||||
normalized or num_samplers > 1,
|
||||
)
|
||||
normalize_result = self.get_normalize("normalize_result", normalized)
|
||||
noise_effects = self.affect in ("noise", "both")
|
||||
result_effects = self.affect in ("result", "both")
|
||||
noise_effects = self.affect in {"noise", "both"}
|
||||
result_effects = self.affect in {"result", "both"}
|
||||
noise_init = torch.zeros_like(x)
|
||||
|
||||
def noise_sampler(s, sn):
|
||||
|
||||
@@ -133,7 +133,7 @@ def perlin_noise_tensor(
|
||||
step -- smooth step function [0, 1] -> [0, 1] (default: `smooth_step`)
|
||||
|
||||
Raises:
|
||||
Exception: if position and vector shapes do not match
|
||||
NoiseError: if position and vector shapes do not match
|
||||
|
||||
Returns:
|
||||
(batch_size, block_height * grid_height, block_width * grid_width)
|
||||
@@ -148,11 +148,11 @@ def perlin_noise_tensor(
|
||||
bh, bw = positions.shape[1:3]
|
||||
|
||||
for i in range(2):
|
||||
if positions.shape[i + 3] not in (1, vectors.shape[i + 2]):
|
||||
if positions.shape[i + 3] not in {1, vectors.shape[i + 2]}:
|
||||
msg = f"Blocks shapes do not match: vectors ({vectors.shape[1]}, {vectors.shape[2]}), positions {gh}, {gw})"
|
||||
raise NoiseError(msg)
|
||||
|
||||
if positions.shape[0] not in (1, batch_size):
|
||||
if positions.shape[0] not in {1, batch_size}:
|
||||
msg = f"Batch sizes do not match: vectors ({vectors.shape[0]}), positions ({positions.shape[0]})"
|
||||
raise NoiseError(msg)
|
||||
|
||||
@@ -206,7 +206,7 @@ def perlin_noise(
|
||||
generator -- random generator used for grid vectors (default: {None})
|
||||
|
||||
Raises:
|
||||
Exception: if grid and out shapes do not match
|
||||
NoiseError: if grid and out shapes do not match
|
||||
|
||||
Returns:
|
||||
Noise image shaped (batch_size, height, width)
|
||||
@@ -410,9 +410,8 @@ def power_noise_like(tensor, alpha=2, k=1): # This doesn't work properly right
|
||||
|
||||
|
||||
__all__ = (
|
||||
"NoiseType",
|
||||
"NoiseError",
|
||||
"scale_noise",
|
||||
"NoiseType",
|
||||
"green_noise_like",
|
||||
"highres_pyramid_noise_like",
|
||||
"laplacian_noise_like",
|
||||
@@ -421,6 +420,7 @@ __all__ = (
|
||||
"pyramid_noise_like",
|
||||
"pyramid_old_noise_like",
|
||||
"rand_perlin_like",
|
||||
"scale_noise",
|
||||
"studentt_noise_like",
|
||||
"uniform_noise_like",
|
||||
)
|
||||
|
||||
+99
-72
@@ -516,7 +516,7 @@ class PowerFilterNoiseItem(PowerNoiseItem):
|
||||
x,
|
||||
ns,
|
||||
self.make_filter(x.shape),
|
||||
self.normalize_result in (True, None),
|
||||
self.normalize_result in {True, None},
|
||||
)
|
||||
filtered_noise = filtered_ns(
|
||||
torch.scalar_tensor(14.0),
|
||||
@@ -533,11 +533,19 @@ class PowerFilterNoiseItem(PowerNoiseItem):
|
||||
|
||||
|
||||
class SonarPowerNoiseNode(SonarCustomNoiseNodeBase):
|
||||
DESCRIPTION = "Custom noise type that applies a filter to generated noise."
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls, *args: list, **kwargs: dict):
|
||||
result = super().INPUT_TYPES(*args, **kwargs)
|
||||
result["required"] |= {
|
||||
"time_brownian": ("BOOLEAN", {"default": False}),
|
||||
"time_brownian": (
|
||||
"BOOLEAN",
|
||||
{
|
||||
"default": False,
|
||||
"tooltip": "Controls whether brownian noise is used when mix isn't 1.0.",
|
||||
},
|
||||
),
|
||||
"alpha": (
|
||||
"FLOAT",
|
||||
{
|
||||
@@ -546,6 +554,7 @@ class SonarPowerNoiseNode(SonarCustomNoiseNodeBase):
|
||||
"max": 5.0,
|
||||
"step": 0.001,
|
||||
"round": False,
|
||||
"tooltip": "Values above 0 will amplify low frequencies, negative values will amplify high frequencies.",
|
||||
},
|
||||
),
|
||||
"max_freq": (
|
||||
@@ -556,6 +565,7 @@ class SonarPowerNoiseNode(SonarCustomNoiseNodeBase):
|
||||
"max": 0.7071,
|
||||
"step": 0.001,
|
||||
"round": False,
|
||||
"tooltip": "Maximum frequency to pass through the filter.",
|
||||
},
|
||||
),
|
||||
"min_freq": (
|
||||
@@ -566,6 +576,7 @@ class SonarPowerNoiseNode(SonarCustomNoiseNodeBase):
|
||||
"max": 0.7071,
|
||||
"step": 0.001,
|
||||
"round": False,
|
||||
"tooltip": "Minimum frequency to pass through the filter.",
|
||||
},
|
||||
),
|
||||
"stretch": (
|
||||
@@ -576,6 +587,7 @@ class SonarPowerNoiseNode(SonarCustomNoiseNodeBase):
|
||||
"max": 100,
|
||||
"step": 0.1,
|
||||
"round": False,
|
||||
"tooltip": "Stretches the filter's shape by the specified factor.",
|
||||
},
|
||||
),
|
||||
"rotate": (
|
||||
@@ -586,6 +598,7 @@ class SonarPowerNoiseNode(SonarCustomNoiseNodeBase):
|
||||
"max": 90,
|
||||
"step": 5,
|
||||
"round": False,
|
||||
"tooltip": "Rotates the filter.",
|
||||
},
|
||||
),
|
||||
"pnorm": (
|
||||
@@ -596,6 +609,7 @@ class SonarPowerNoiseNode(SonarCustomNoiseNodeBase):
|
||||
"max": 100,
|
||||
"step": 0.1,
|
||||
"round": False,
|
||||
"tooltip": "Factor used for cushioning the band-pass region.",
|
||||
},
|
||||
),
|
||||
"mix": (
|
||||
@@ -606,6 +620,7 @@ class SonarPowerNoiseNode(SonarCustomNoiseNodeBase):
|
||||
"max": 1.0,
|
||||
"step": 0.001,
|
||||
"round": False,
|
||||
"tooltip": "Controls the ratio of filtered noise. For example, 0.75 means 75% noise with the filter effects applied, 25% raw noise.",
|
||||
},
|
||||
),
|
||||
"common_mode": (
|
||||
@@ -616,6 +631,7 @@ class SonarPowerNoiseNode(SonarCustomNoiseNodeBase):
|
||||
"max": 100.0,
|
||||
"step": 0.001,
|
||||
"round": False,
|
||||
"tooltip": "Attempts to desaturate thelatent by injecting the average across channels (controlled by channel_correction). Applied after mix.",
|
||||
},
|
||||
),
|
||||
"channel_correlation": (
|
||||
@@ -624,13 +640,20 @@ class SonarPowerNoiseNode(SonarCustomNoiseNodeBase):
|
||||
"default": "1, 1, 1, 1, 1, 1",
|
||||
"multiline": False,
|
||||
"dynamicPrompts": False,
|
||||
"tooltip": "Comma-separated list of channel correlation strengths.",
|
||||
},
|
||||
),
|
||||
"preview": (
|
||||
("none", "no_mix", "mix"),
|
||||
{
|
||||
"tooltip": "When enabled, displays a preview of the filter shape and a sample of noise. Mix - previews noise after mix is applied. no_mix - only previews the filtered noise.",
|
||||
},
|
||||
),
|
||||
"preview": (("none", "no_mix", "mix"),),
|
||||
}
|
||||
return result
|
||||
|
||||
def get_item_class(self):
|
||||
@classmethod
|
||||
def get_item_class(cls):
|
||||
return PowerNoiseItem
|
||||
|
||||
def go(
|
||||
@@ -648,6 +671,8 @@ class SonarPowerNoiseNode(SonarCustomNoiseNodeBase):
|
||||
|
||||
|
||||
class SonarPowerFilterNoiseNode(SonarPowerNoiseNode, SonarNormalizeNoiseNodeMixin):
|
||||
DESCRIPTION = "Custom noise type that allows applying a Power Filter to another custom noise generator."
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
result = super().INPUT_TYPES(include_rescale=False, include_chain=False)
|
||||
@@ -662,8 +687,18 @@ class SonarPowerFilterNoiseNode(SonarPowerNoiseNode, SonarNormalizeNoiseNodeMixi
|
||||
):
|
||||
del result["required"][k]
|
||||
result["required"] |= {
|
||||
"sonar_custom_noise": ("SONAR_CUSTOM_NOISE",),
|
||||
"sonar_power_filter": ("SONAR_POWER_FILTER",),
|
||||
"sonar_custom_noise": (
|
||||
"SONAR_CUSTOM_NOISE",
|
||||
{
|
||||
"tooltip": "Custom noise type to filter.",
|
||||
},
|
||||
),
|
||||
"sonar_power_filter": (
|
||||
"SONAR_POWER_FILTER",
|
||||
{
|
||||
"tooltip": "Filter to use.",
|
||||
},
|
||||
),
|
||||
"filter_norm_factor": (
|
||||
"FLOAT",
|
||||
{
|
||||
@@ -672,15 +707,32 @@ class SonarPowerFilterNoiseNode(SonarPowerNoiseNode, SonarNormalizeNoiseNodeMixi
|
||||
"max": 1.0,
|
||||
"step": 0.1,
|
||||
"round": False,
|
||||
"tooltip": "Normalization factor applied to the specified filter. 1.0 means 100% normalized.",
|
||||
},
|
||||
),
|
||||
"normalize_result": (
|
||||
("default", "forced", "disabled"),
|
||||
{
|
||||
"tooltip": "Controls whether the final result is normalized to 1.0 strength.",
|
||||
},
|
||||
),
|
||||
"normalize_noise": (
|
||||
("default", "forced", "disabled"),
|
||||
{
|
||||
"tooltip": "Controls whether the generated noise is normalized to 1.0 strength.",
|
||||
},
|
||||
),
|
||||
"normalize_result": (("default", "forced", "disabled"),),
|
||||
"normalize_noise": (("default", "forced", "disabled"),),
|
||||
}
|
||||
result["required"]["preview"] = ((*result["required"]["preview"][0], "custom"),)
|
||||
result["required"]["preview"] = (
|
||||
(*result["required"]["preview"][0], "custom"),
|
||||
{
|
||||
"tooltip": "When enabled, displays a preview of the filter shape and a sample of noise. Mix - previews noise after mix is applied. no_mix - only previews the filtered noise. custom - Like no_mix, but will use a latent previewer to display a color preview of the generated noise. Works best when previewer is set to TAESD.",
|
||||
},
|
||||
)
|
||||
return result
|
||||
|
||||
def get_item_class(self):
|
||||
@classmethod
|
||||
def get_item_class(cls):
|
||||
return PowerFilterNoiseItem
|
||||
|
||||
def go(
|
||||
@@ -714,69 +766,23 @@ class SonarPowerFilterNode:
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
include_keys = {"alpha", "max_freq", "min_freq", "stretch", "rotate", "pnorm"}
|
||||
return {
|
||||
"required": {
|
||||
"alpha": (
|
||||
"FLOAT",
|
||||
k: v
|
||||
for k, v in SonarPowerNoiseNode.INPUT_TYPES()["required"].items()
|
||||
if k in include_keys
|
||||
}
|
||||
| {
|
||||
"oversample": (
|
||||
"INT",
|
||||
{
|
||||
"default": 0.0,
|
||||
"min": -5.0,
|
||||
"max": 5.0,
|
||||
"step": 0.001,
|
||||
"round": False,
|
||||
"default": 4,
|
||||
"min": 1,
|
||||
"max": 128,
|
||||
"tooltip": "Oversampling factor used for the filter size.",
|
||||
},
|
||||
),
|
||||
"max_freq": (
|
||||
"FLOAT",
|
||||
{
|
||||
"default": 0.7071,
|
||||
"min": 0.0,
|
||||
"max": 0.7071,
|
||||
"step": 0.001,
|
||||
"round": False,
|
||||
},
|
||||
),
|
||||
"min_freq": (
|
||||
"FLOAT",
|
||||
{
|
||||
"default": 0.0,
|
||||
"min": 0.0,
|
||||
"max": 0.7071,
|
||||
"step": 0.001,
|
||||
"round": False,
|
||||
},
|
||||
),
|
||||
"stretch": (
|
||||
"FLOAT",
|
||||
{
|
||||
"default": 1.0,
|
||||
"min": 0.01,
|
||||
"max": 100,
|
||||
"step": 0.1,
|
||||
"round": False,
|
||||
},
|
||||
),
|
||||
"rotate": (
|
||||
"FLOAT",
|
||||
{
|
||||
"default": 0,
|
||||
"min": -90,
|
||||
"max": 90,
|
||||
"step": 5,
|
||||
"round": False,
|
||||
},
|
||||
),
|
||||
"pnorm": (
|
||||
"FLOAT",
|
||||
{
|
||||
"default": 2,
|
||||
"min": 0.125,
|
||||
"max": 100,
|
||||
"step": 0.1,
|
||||
"round": False,
|
||||
},
|
||||
),
|
||||
"oversample": ("INT", {"default": 4, "min": 1, "max": 128}),
|
||||
"blur": (
|
||||
"FLOAT",
|
||||
{
|
||||
@@ -785,6 +791,7 @@ class SonarPowerFilterNode:
|
||||
"max": 10.0,
|
||||
"step": 0.01,
|
||||
"round": False,
|
||||
"tooltip": "Slightly blurs the filter to reduce artifacts.",
|
||||
},
|
||||
),
|
||||
"scale": (
|
||||
@@ -795,17 +802,24 @@ class SonarPowerFilterNode:
|
||||
"max": 100.0,
|
||||
"step": 0.1,
|
||||
"round": False,
|
||||
"tooltip": "Scales the filter to the specified strength. May be negative.",
|
||||
},
|
||||
),
|
||||
"compose_mode": (
|
||||
("max", "min", "add", "sub", "mul"),
|
||||
{
|
||||
"tooltip": "Controls composition of the option attached filter. For example, when set to MUL the result will be this filter multiplied by the attached filter. No effect if the optional filter input is not attached.",
|
||||
},
|
||||
),
|
||||
"compose_mode": (("max", "min", "add", "sub", "mul"),),
|
||||
},
|
||||
"optional": {
|
||||
"power_filter_opt": ("SONAR_POWER_FILTER",),
|
||||
},
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def go(
|
||||
self,
|
||||
cls,
|
||||
min_freq=0.0,
|
||||
max_freq=0.7071,
|
||||
stretch=1.0,
|
||||
@@ -836,6 +850,7 @@ class SonarPowerFilterNode:
|
||||
|
||||
|
||||
class SonarPreviewFilterNode:
|
||||
DESCRIPTION = "Allows previewing a Power Filter."
|
||||
RETURN_TYPES = ("SONAR_POWER_FILTER",)
|
||||
CATEGORY = "advanced/noise"
|
||||
FUNCTION = "go"
|
||||
@@ -845,7 +860,12 @@ class SonarPreviewFilterNode:
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"sonar_power_filter": ("SONAR_POWER_FILTER",),
|
||||
"sonar_power_filter": (
|
||||
"SONAR_POWER_FILTER",
|
||||
{
|
||||
"tooltip": "Power Filter to preview.",
|
||||
},
|
||||
),
|
||||
"filter_gain": (
|
||||
"FLOAT",
|
||||
{
|
||||
@@ -854,6 +874,7 @@ class SonarPreviewFilterNode:
|
||||
"max": 1000000.0,
|
||||
"step": 0.1,
|
||||
"round": False,
|
||||
"tooltip": "Gain factor applied to the filter part of the preview.",
|
||||
},
|
||||
),
|
||||
"kernel_gain": (
|
||||
@@ -864,6 +885,7 @@ class SonarPreviewFilterNode:
|
||||
"max": 1000000.0,
|
||||
"step": 0.1,
|
||||
"round": False,
|
||||
"tooltip": "Gain factor applied to the kernel part of the preview.",
|
||||
},
|
||||
),
|
||||
"norm_factor": (
|
||||
@@ -874,6 +896,7 @@ class SonarPreviewFilterNode:
|
||||
"max": 1.0,
|
||||
"step": 0.1,
|
||||
"round": False,
|
||||
"tooltip": "Normalization factor applied to the filter before previewing. 1.0 means 100% normalized.",
|
||||
},
|
||||
),
|
||||
"preview_size": (
|
||||
@@ -888,12 +911,16 @@ class SonarPreviewFilterNode:
|
||||
"128x127",
|
||||
"127x128",
|
||||
),
|
||||
{
|
||||
"tooltip": "Controls the size of the generated preview. Note: Sizes are in latent pixels. For most models, one latent pixel equals eight pixels",
|
||||
},
|
||||
),
|
||||
},
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def go(
|
||||
self,
|
||||
cls,
|
||||
sonar_power_filter,
|
||||
filter_gain=1 / 3,
|
||||
kernel_gain=1 / 3,
|
||||
|
||||
+15
-18
@@ -2,12 +2,14 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import importlib
|
||||
from enum import Enum, auto
|
||||
from sys import stderr
|
||||
from typing import Any, Callable, NamedTuple
|
||||
|
||||
import torch
|
||||
from comfy.k_diffusion import sampling
|
||||
from comfy.samplers import KSampler, k_diffusion_sampling
|
||||
from torch import Tensor
|
||||
from tqdm.auto import trange
|
||||
|
||||
@@ -60,10 +62,10 @@ class SonarBase:
|
||||
seed: int | None = None,
|
||||
):
|
||||
sigma_min, sigma_max = sigmas[sigmas > 0].min(), sigmas.max()
|
||||
if noise_sampler is not None and self.cfg.noise_type not in (
|
||||
if noise_sampler is not None and self.cfg.noise_type not in {
|
||||
None,
|
||||
self.DEFAULT_NOISE_TYPE,
|
||||
):
|
||||
}:
|
||||
print(
|
||||
"Sonar: Warning: Noise sampler supplied, overriding noise type from settings",
|
||||
file=stderr,
|
||||
@@ -127,7 +129,7 @@ class SonarBase:
|
||||
momentum_d = (1.0 - p) * d + p * hd
|
||||
|
||||
# Euler method with momentum
|
||||
x = x + momentum_d * dt
|
||||
x = x + momentum_d * dt # noqa: PLR6104
|
||||
|
||||
self.update_hist(momentum_d)
|
||||
|
||||
@@ -155,9 +157,8 @@ class SonarGuidanceMixin:
|
||||
return ((latent - avg_s) / std_s).to(latent.dtype)
|
||||
|
||||
def guidance_step(self, step_index: int, x: Tensor, denoised: Tensor):
|
||||
if (self.guidance is None or self.guidance.factor == 0.0) or not (
|
||||
self.guidance.start_step <= (step_index + 1) <= self.guidance.end_step
|
||||
):
|
||||
step_matched = self.guidance.start_step <= step_index <= self.guidance.end_step
|
||||
if self.guidance is None or self.guidance.factor == 0.0 or not step_matched:
|
||||
return x
|
||||
if self.ref_latent.device != x.device:
|
||||
self.ref_latent = self.ref_latent.to(device=x.device)
|
||||
@@ -263,7 +264,7 @@ class SonarEuler(SonarSampler):
|
||||
else torch.randn_like(sample)
|
||||
)
|
||||
eps = noise * self.s_noise
|
||||
sample = sample + eps * (sigma_hat**2 - sigma**2) ** 0.5
|
||||
sample = sample + eps * (sigma_hat**2 - sigma**2) ** 0.5 # noqa: PLR6104
|
||||
|
||||
denoised = self.model(sample, sigma_hat * self.s_in, **self.extra_args)
|
||||
derivative = sampling.to_d(sample, sigma, denoised)
|
||||
@@ -320,7 +321,7 @@ class SonarEuler(SonarSampler):
|
||||
)
|
||||
|
||||
for i in trange(len(sigmas) - 1, disable=disable):
|
||||
x, sigma, sigma_hat, denoised = sonar.step(
|
||||
x, _sigma, sigma_hat, denoised = sonar.step(
|
||||
i,
|
||||
x,
|
||||
)
|
||||
@@ -370,7 +371,7 @@ class SonarEulerAncestral(SonarSampler):
|
||||
result_sample = self.momentum_step(sample, derivative, dt)
|
||||
if sigma_to > 0:
|
||||
result_sample = self.guidance_step(step_index, result_sample, denoised)
|
||||
result_sample = (
|
||||
result_sample = ( # noqa: PLR6104
|
||||
result_sample
|
||||
+ self.noise_sampler(sigma_from, sigma_to) * self.s_noise * sigma_up
|
||||
)
|
||||
@@ -417,7 +418,7 @@ class SonarEulerAncestral(SonarSampler):
|
||||
)
|
||||
|
||||
for i in trange(len(sigmas) - 1, disable=disable):
|
||||
x, sigma, sigma_hat, denoised = sonar.step(
|
||||
x, _sigma, sigma_hat, denoised = sonar.step(
|
||||
i,
|
||||
x,
|
||||
)
|
||||
@@ -457,7 +458,7 @@ class SonarDPMPPSDE(SonarSampler):
|
||||
return sigma.log.neg()
|
||||
|
||||
# DPM++ solver algorithm copied from ComfyUI source.
|
||||
def momentum_step(
|
||||
def momentum_step( # noqa: PLR0914
|
||||
self,
|
||||
step_index,
|
||||
x: Tensor,
|
||||
@@ -495,7 +496,7 @@ class SonarDPMPPSDE(SonarSampler):
|
||||
self.update_hist(momentum_d)
|
||||
hd = self.history_d
|
||||
x_2 = (sigma_fn(s_) / sigma_fn(t)) * x - momentum_d
|
||||
x_2 = x_2 + self.noise_sampler(sigma_fn(t), sigma_fn(s)) * self.s_noise * su
|
||||
x_2 += self.noise_sampler(sigma_fn(t), sigma_fn(s)) * self.s_noise * su
|
||||
denoised_2 = self.model(x_2, sigma_fn(s) * self.s_in, **self.extra_args)
|
||||
|
||||
# Step 2
|
||||
@@ -527,7 +528,7 @@ class SonarDPMPPSDE(SonarSampler):
|
||||
self.init_hist_d(sample)
|
||||
|
||||
sigma_from, sigma_to = self.sigmas[step_index], self.sigmas[step_index + 1]
|
||||
sigma_down, sigma_up = sampling.get_ancestral_step(
|
||||
sigma_down, _sigma_up = sampling.get_ancestral_step(
|
||||
sigma_from,
|
||||
sigma_to,
|
||||
eta=self.eta,
|
||||
@@ -585,7 +586,7 @@ class SonarDPMPPSDE(SonarSampler):
|
||||
)
|
||||
|
||||
for i in trange(len(sigmas) - 1, disable=disable):
|
||||
x, sigma, sigma_hat, denoised = sonar.step(
|
||||
x, _sigma, sigma_hat, denoised = sonar.step(
|
||||
i,
|
||||
x,
|
||||
)
|
||||
@@ -603,10 +604,6 @@ class SonarDPMPPSDE(SonarSampler):
|
||||
|
||||
|
||||
def add_samplers():
|
||||
import importlib
|
||||
|
||||
from comfy.samplers import KSampler, k_diffusion_sampling
|
||||
|
||||
extra_samplers = {
|
||||
"sonar_euler": SonarEuler.sampler,
|
||||
"sonar_euler_ancestral": SonarEulerAncestral.sampler,
|
||||
|
||||
Reference in New Issue
Block a user