Add latent frequency split node
This commit is contained in:
@@ -18,6 +18,10 @@ Runs the rectified-flow ODE backwards to re-noise an encoded latent. Supports de
|
||||
|
||||
Applies a Gaussian blur directly in latent space. `Spatial Only` blurs each channel independently; `Spatial and Channel` performs a joint 3D blur across channels for stronger smoothing.
|
||||
|
||||
### Latent Frequency Split
|
||||
|
||||
Separates a latent into low-pass structure and high-frequency detail bands using a Gaussian crossover so you can process each side differently before recombining.
|
||||
|
||||
### Add Latent Noise (Seeded)
|
||||
|
||||
Adds seeded Gaussian noise scaled by the input latent's standard deviation. Useful whenever a latent needs a controlled bump in noise without touching the schedule.
|
||||
@@ -50,6 +54,7 @@ Image exploration rarely means chasing a single "correct" answer. These nodes ar
|
||||
- Follow the noise with `Conditioning (Gaussian Blur)` to soften abrupt emphasis changes or to merge multi-prompt blends into a single vibe. Blurring after noise often produces dreamlike, painterly shifts instead of chaotic drift.
|
||||
- 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.
|
||||
- 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.
|
||||
|
||||
+69
@@ -302,6 +302,73 @@ class LatentGaussianBlur:
|
||||
out["samples"] = blurred_tensor.cpu()
|
||||
return (out,)
|
||||
|
||||
|
||||
class LatentFrequencySplit:
|
||||
"""Splits a latent into low- and high-frequency bands via Gaussian smoothing."""
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"latent": ("LATENT",),
|
||||
"sigma": ("FLOAT", {
|
||||
"default": 1.5,
|
||||
"min": 0.0,
|
||||
"max": 20.0,
|
||||
"step": 0.1,
|
||||
"tooltip": "Radius of the Gaussian used for the low-pass. Higher values move detail into the high band."
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("LATENT", "LATENT")
|
||||
RETURN_NAMES = ("low_pass", "high_pass")
|
||||
FUNCTION = "split"
|
||||
CATEGORY = "Latent/Filter"
|
||||
|
||||
def split(self, latent, sigma):
|
||||
device = comfy.model_management.get_torch_device()
|
||||
latent_tensor = latent["samples"].clone().to(device)
|
||||
|
||||
if sigma <= 0.0:
|
||||
low_samples = latent_tensor
|
||||
high_samples = torch.zeros_like(latent_tensor)
|
||||
else:
|
||||
low_samples = self._gaussian_blur(latent_tensor, sigma)
|
||||
high_samples = latent_tensor - low_samples
|
||||
|
||||
low_latent = latent.copy()
|
||||
low_latent["samples"] = low_samples.cpu()
|
||||
|
||||
high_latent = latent.copy()
|
||||
high_latent["samples"] = high_samples.cpu()
|
||||
|
||||
return (low_latent, high_latent)
|
||||
|
||||
@staticmethod
|
||||
def _gaussian_blur(latent_tensor, sigma):
|
||||
tensor = latent_tensor
|
||||
is_5d = tensor.dim() == 5
|
||||
|
||||
if is_5d:
|
||||
b, c, t, h, w = tensor.shape
|
||||
tensor_4d = tensor.permute(0, 2, 1, 3, 4).reshape(b * t, c, h, w)
|
||||
else:
|
||||
tensor_4d = tensor
|
||||
|
||||
kernel_size = int(sigma * 6) + 1
|
||||
if kernel_size % 2 == 0:
|
||||
kernel_size += 1
|
||||
|
||||
blurred_4d = TF.gaussian_blur(tensor_4d, kernel_size=kernel_size, sigma=sigma)
|
||||
|
||||
if is_5d:
|
||||
blurred = blurred_4d.view(b, t, c, h, w).permute(0, 2, 1, 3, 4)
|
||||
else:
|
||||
blurred = blurred_4d
|
||||
|
||||
return blurred
|
||||
|
||||
class LatentAddNoise:
|
||||
"""
|
||||
Adds a configurable amount of seeded random noise to a latent tensor.
|
||||
@@ -602,6 +669,7 @@ class ConditioningScale:
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"QwenRectifiedFlowInverter": QwenRectifiedFlowInverter,
|
||||
"LatentGaussianBlur": LatentGaussianBlur,
|
||||
"LatentFrequencySplit": LatentFrequencySplit,
|
||||
"LatentAddNoise": LatentAddNoise,
|
||||
"LatentForwardDiffusion": LatentForwardDiffusion,
|
||||
"LatentHybridInverter": LatentHybridInverter,
|
||||
@@ -613,6 +681,7 @@ NODE_CLASS_MAPPINGS = {
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"QwenRectifiedFlowInverter": "Qwen Rectified Flow Inverter",
|
||||
"LatentGaussianBlur": "Latent Gaussian Blur",
|
||||
"LatentFrequencySplit": "Latent Frequency Split",
|
||||
"LatentAddNoise": "Add Latent Noise (Seeded)",
|
||||
"LatentForwardDiffusion": "Forward Diffusion (Add Scheduled Noise)",
|
||||
"LatentHybridInverter": "Latent Hybrid Inverter (Qwen)",
|
||||
|
||||
Reference in New Issue
Block a user