From 0715cab7fb8fa33b5805b677ee5142103f39670e Mon Sep 17 00:00:00 2001 From: Ken Simpson Date: Mon, 29 Sep 2025 15:36:03 -0700 Subject: [PATCH] Add conditioning frequency split node --- README.md | 5 +++ __init__.py | 100 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 105 insertions(+) diff --git a/README.md b/README.md index 9e06944..ef871ff 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,10 @@ Adds seeded Gaussian noise to the CLIP conditioning embeddings (and pooled outpu Smooths the token embeddings with a Gaussian kernel along the prompt sequence, softening sharp emphasis changes while preserving the overall prompt content. +### Conditioning (Frequency Split) + +Generates low- and high-frequency prompt embeddings so you can tame the overall narrative while separately shaping high-energy emphasis tokens. + ### Conditioning (Scale) Multiplies the conditioning embeddings and pooled outputs by a user-defined factor so you can mute (0.0), keep (1.0), or amplify (>1.0) prompt influence without editing text. @@ -55,6 +59,7 @@ Image exploration rarely means chasing a single "correct" answer. These nodes ar - Use `Conditioning (Scale)` as a volume knob while iterating: dial the factor down to 0.3–0.5 when you want the image to respond mostly to the recovered latent, then crank it above 1.5 when the textual guidance should take the lead. - Pair `Latent Gaussian Blur` with `Conditioning (Gaussian Blur)` for holistic smoothing—latent blur calms texture while conditioning blur calms prompt pacing. Reintroduce energy by sprinkling in `Add Latent Noise` or ramping the conditioning scale afterwards. - Split a latent with `Latent Frequency Split`, sharpen or noise the high band while blurring the low band, then recombine via `Latent Mixer`/`Add` nodes to weave sharp detail onto soft composition. +- Carve prompts with `Conditioning (Frequency Split)`: keep low-band text steady for the story while remapping the high band through noise, blur, or scale to emphasize only certain fragments. - Swap seeds between the inverter, forward diffusion, and noise nodes as you iterate. Small tweaks here can shift the interplay between latent detail and prompt guidance, rewarding playful experimentation. Treat each slider as a brushstroke: push a setting until it breaks, back off to the sweet spot, and capture the happy accidents along the way. diff --git a/__init__.py b/__init__.py index c0afb2d..b9cb4d3 100644 --- a/__init__.py +++ b/__init__.py @@ -622,6 +622,104 @@ class ConditioningGaussianBlur: return tensor.dtype +class ConditioningFrequencySplit: + """Separates conditioning embeddings into low/high bands via Gaussian smoothing.""" + + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "conditioning": ("CONDITIONING",), + "sigma": ("FLOAT", { + "default": 0.75, + "min": 0.0, + "max": 10.0, + "step": 0.05, + "tooltip": "Cutoff for the Gaussian low-pass applied along the token axis." + }), + } + } + + RETURN_TYPES = ("CONDITIONING", "CONDITIONING") + RETURN_NAMES = ("low_pass", "high_pass") + FUNCTION = "split" + CATEGORY = "conditioning/filter" + + def split(self, conditioning, sigma): + if sigma <= 0.0: + low_list = [self._clone_entry(item) for item in conditioning] + high_list = [self._zero_entry(item) for item in conditioning] + return (low_list, high_list) + + low_list = [] + high_list = [] + + for embedding, metadata in conditioning: + if not isinstance(embedding, torch.Tensor) or embedding.dim() < 2: + low_list.append([embedding, dict(metadata)]) + high_list.append(self._zero_entry([embedding, metadata])) + continue + + needs_batch_dim = embedding.dim() == 2 + embedding_tensor = embedding.unsqueeze(0) if needs_batch_dim else embedding + + conv_dtype = ConditioningGaussianBlur._select_conv_dtype(embedding_tensor) + kernel, padding = ConditioningGaussianBlur._build_kernel(embedding_tensor.device, conv_dtype, sigma) + + original_shape = embedding_tensor.shape + tokens = original_shape[-2] + features = original_shape[-1] + + batch = math.prod(original_shape[:-2]) if len(original_shape) > 2 else original_shape[0] + reshaped = embedding_tensor.reshape(batch, tokens, features).permute(0, 2, 1) + + if reshaped.dtype != conv_dtype: + reshaped = reshaped.to(conv_dtype) + + kernel = kernel.repeat(features, 1, 1) + blurred = F.conv1d(reshaped, kernel, padding=padding, groups=features) + blurred = blurred.permute(0, 2, 1).reshape(original_shape) + + if blurred.dtype != embedding_tensor.dtype: + blurred = blurred.to(embedding_tensor.dtype) + + if needs_batch_dim: + blurred = blurred.squeeze(0) + + high_embedding = embedding - blurred + + low_meta = dict(metadata) + high_meta = dict(metadata) + + pooled_output = low_meta.get("pooled_output") + if isinstance(pooled_output, torch.Tensor): + high_meta["pooled_output"] = torch.zeros_like(pooled_output) + + low_list.append([blurred, low_meta]) + high_list.append([high_embedding, high_meta]) + + return (low_list, high_list) + + @staticmethod + def _clone_entry(entry): + embedding, metadata = entry + return [embedding, dict(metadata)] + + @staticmethod + def _zero_entry(entry): + embedding, metadata = entry + if isinstance(embedding, torch.Tensor): + zero_embedding = torch.zeros_like(embedding) + else: + zero_embedding = embedding + + meta_copy = dict(metadata) + pooled_output = meta_copy.get("pooled_output") + if isinstance(pooled_output, torch.Tensor): + meta_copy["pooled_output"] = torch.zeros_like(pooled_output) + return [zero_embedding, meta_copy] + + class ConditioningScale: """Scales conditioning embeddings (and pooled outputs) to amplify or mute prompt influence.""" @@ -675,6 +773,7 @@ NODE_CLASS_MAPPINGS = { "LatentHybridInverter": LatentHybridInverter, "ConditioningAddNoise": ConditioningAddNoise, "ConditioningGaussianBlur": ConditioningGaussianBlur, + "ConditioningFrequencySplit": ConditioningFrequencySplit, "ConditioningScale": ConditioningScale, } @@ -687,5 +786,6 @@ NODE_DISPLAY_NAME_MAPPINGS = { "LatentHybridInverter": "Latent Hybrid Inverter (Qwen)", "ConditioningAddNoise": "Conditioning (Add Noise)", "ConditioningGaussianBlur": "Conditioning (Gaussian Blur)", + "ConditioningFrequencySplit": "Conditioning (Frequency Split)", "ConditioningScale": "Conditioning (Scale)", }