Files
2023-09-19 10:03:51 -07:00

920 lines
44 KiB
Python

import torch
import torch.nn.functional as F
import torchsde
import math
import sys
import nodes
import comfy.samplers
import comfy.k_diffusion.sampling
import comfy.model_management
from .modules.latent_util import (
normalize,
latents_to_images,
noise_sigma_scale,
scale_from_perentage,
within_percentage_range
)
from .modules.latent_filters import (
sharpen_latents,
high_pass_latents,
blending_modes
)
from .modules.latent_noise import (
CrossHatchPowerFractal,
CrossHatchLinearPowerFractal,
PerlinPowerFractal,
PowerLawNoise
)
# PERLIN POWER FRACTAL NOISE LATENT
class PPFNoiseNode:
def __init__(self):
pass
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"batch_size": ("INT", {"default": 1, "max": 64, "min": 1, "step": 1}),
"width": ("INT", {"default": 512, "max": 8192, "min": 64, "step": 1}),
"height": ("INT", {"default": 512, "max": 8192, "min": 64, "step": 1}),
"resampling": (["nearest-exact", "bilinear", "area", "bicubic", "bislerp"],),
"X": ("FLOAT", {"default": 0, "max": 99999999, "min": -99999999, "step": 0.01}),
"Y": ("FLOAT", {"default": 0, "max": 99999999, "min": -99999999, "step": 0.01}),
"Z": ("FLOAT", {"default": 0, "max": 99999999, "min": -99999999, "step": 0.01}),
"evolution": ("FLOAT", {"default": 0.0, "max": 1.0, "min": 0.0, "step": 0.01}),
"frame": ("INT", {"default": 0, "max": 99999999, "min": 0, "step": 1}),
"scale": ("FLOAT", {"default": 5, "max": 2048, "min": 2, "step": 0.01}),
"octaves": ("INT", {"default": 8, "max": 8, "min": 1, "step": 1}),
"persistence": ("FLOAT", {"default": 1.5, "max": 23.0, "min": 0.01, "step": 0.01}),
"lacunarity": ("FLOAT", {"default": 2.0, "max": 99.0, "min": 0.01, "step": 0.01}),
"exponent": ("FLOAT", {"default": 4.0, "max": 38.0, "min": 0.01, "step": 0.01}),
"brightness": ("FLOAT", {"default": 0.0, "max": 1.0, "min": -1.0, "step": 0.01}),
"contrast": ("FLOAT", {"default": 0.0, "max": 1.0, "min": -1.0, "step": 0.01}),
"clamp_min": ("FLOAT", {"default": 0.0, "max": 10.0, "min": -10.0, "step": 0.01}),
"clamp_max": ("FLOAT", {"default": 1.0, "max": 10.0, "min": -10.0, "step": 0.01}),
"seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}),
"device": (["cpu", "cuda"],),
},
"optional": {
"optional_vae": ("VAE",),
"ppf_settings": ("PPF_SETTINGS",),
}
}
RETURN_TYPES = ("LATENT","IMAGE")
RETURN_NAMES = ("latents","previews")
FUNCTION = "power_fractal_latent"
CATEGORY = "Power Noise Suite/Noise"
def power_fractal_latent(self, batch_size, width, height, resampling, X, Y, Z, evolution, frame, scale, octaves, persistence, lacunarity, exponent, brightness, contrast, clamp_min, clamp_max, seed, device, optional_vae=None, ppf_settings=None):
if ppf_settings:
ppf = ppf_settings
X = ppf['X']
Y = ppf['Y']
Z = ppf['Z']
evolution = ppf['evolution']
frame = ppf['frame']
scale = ppf['scale']
octaves = ppf['octaves']
persistence = ppf['persistence']
lacunarity = ppf['lacunarity']
exponent = ppf['exponent']
brightness = ppf['brightness']
contrast = ppf['contrast']
color_intensity = 1
masking_intensity = 1
batch_size = int(batch_size)
width = int(width)
height = int(height)
channel_tensors = []
for i in range(batch_size):
nseed = seed + i * 12
rgb_noise_maps = []
rgb_image = torch.zeros(4, height, width)
for j in range(3):
rgba_noise_map = self.generate_noise_map(width, height, X, Y, Z, frame, device, evolution, octaves, persistence, lacunarity, exponent, scale, brightness, contrast, nseed + j, clamp_min, clamp_max)
rgb_noise_map = rgba_noise_map.squeeze(-1)
rgb_noise_map *= color_intensity
rgb_noise_map *= masking_intensity
rgb_image[j] = rgb_noise_map
rgb_image[3] = torch.ones(height, width)
channel_tensors.append(rgb_image)
tensors = torch.stack(channel_tensors)
tensors = normalize(tensors)
if optional_vae is None:
latents = F.interpolate(tensors, size=((height // 8), (width // 8)), mode=resampling)
return {'samples': latents}, tensors.permute(0, 2, 3, 1)
encoder = nodes.VAEEncode()
latents = []
for tensor in tensors:
tensor = tensor.unsqueeze(0)
tensor = tensor.permute(0, 2, 3, 1)
latents.append(encoder.encode(optional_vae, tensor)[0]['samples'])
latents = torch.cat(latents)
return {'samples': latents}, tensors.permute(0, 2, 3, 1)
def generate_noise_map(self, width, height, X, Y, Z, frame, device, evolution, octaves, persistence, lacunarity, exponent, scale, brightness, contrast, seed, clamp_min, clamp_max):
PPF = PerlinPowerFractal(width, height)
noise_map = PPF(1, X, Y, Z, frame, device, evolution, octaves, persistence, lacunarity, exponent, scale, brightness, contrast, seed, clamp_min, clamp_max)
return noise_map
# POWER-LOW NOISE
class PPFNPowerLawNoise:
def __init__(self):
pass
@classmethod
def INPUT_TYPES(cls):
pln = PowerLawNoise('cpu')
return {
"required": {
"batch_size": ("INT", {"default": 1, "max": 64, "min": 1, "step": 1}),
"width": ("INT", {"default": 512, "max": 8192, "min": 64, "step": 1}),
"height": ("INT", {"default": 512, "max": 8192, "min": 64, "step": 1}),
"resampling": (["nearest-exact", "bilinear", "area", "bicubic", "bislerp"],),
"noise_type": (pln.get_noise_types(),),
"scale": ("FLOAT", {"default": 1.0, "max": 1024.0, "min": 0.01, "step": 0.001}),
"alpha_exponent": ("FLOAT", {"default": 1.0, "max": 12.0, "min": -12.0, "step": 0.001}),
"modulator": ("FLOAT", {"default": 1.0, "max": 2.0, "min": 0.1, "step": 0.01}),
"seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}),
"device": (["cpu", "cuda"],),
},
"optional": {
"optional_vae": ("VAE",),
}
}
RETURN_TYPES = ("LATENT", "IMAGE")
RETURN_NAMES = ("latents", "previews")
FUNCTION = "power_noise"
CATEGORY = "Power Noise Suite/Noise"
def power_noise(self, batch_size, width, height, resampling, noise_type, scale, alpha_exponent, modulator, seed, device, optional_vae=None):
power_law = PowerLawNoise(device=device)
tensors = power_law(batch_size, width, height, scale=scale, alpha=alpha_exponent, modulator=modulator, noise_type=noise_type, seed=seed)
alpha_channel = torch.ones((batch_size, height, width, 1), dtype=tensors.dtype, device="cpu")
tensors = torch.cat((tensors, alpha_channel), dim=3)
if optional_vae is None:
latents = tensors.permute(0, 3, 1, 2)
latents = F.interpolate(latents, size=((height // 8), (width // 8)), mode=resampling)
return {'samples': latents}, tensors
encoder = nodes.VAEEncode()
latents = []
for tensor in tensors:
tensor = tensor.unsqueeze(0)
latents.append(encoder.encode(optional_vae, tensor)[0]['samples'])
latents = torch.cat(latents)
return {'samples': latents}, tensors
# CROSS-HATCH POWER FRACTAL LATENT
class PPFNCrossHatchNode:
def __init__(self):
pass
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"batch_size": ("INT", {"default": 1, "max": 64, "min": 1, "step": 1}),
"width": ("INT", {"default": 512, "max": 8192, "min": 64, "step": 1}),
"height": ("INT", {"default": 512, "max": 8192, "min": 64, "step": 1}),
"resampling": (["nearest-exact", "bilinear", "area", "bicubic", "bislerp"],),
"frequency": ("FLOAT", {"default": 320.0, "max": 1024.0, "min": 0.001, "step": 0.001}),
"octaves": ("INT", {"default": 12, "max": 32, "min": 1, "step": 1}),
"persistence": ("FLOAT", {"default": 1.5, "max": 2.0, "min": 0.001, "step": 0.001}),
"num_colors": ("INT", {"default": 16, "max": 256, "min": 2, "step": 1}),
"color_tolerance": ("FLOAT", {"default": 0.05, "max": 1.0, "min": 0.001, "step": 0.001}),
"angle_degrees": ("FLOAT", {"default": 45.0, "max": 360.0, "min": 0.0, "step": 0.01}),
"brightness": ("FLOAT", {"default": 0.0, "max": 1.0, "min": -1.0, "step": 0.001}),
"contrast": ("FLOAT", {"default": 0.0, "max": 1.0, "min": -1.0, "step": 0.001}),
"blur": ("FLOAT", {"default": 2.5, "max": 1024, "min": 0, "step": 0.01}),
"clamp_min": ("FLOAT", {"default": 0.0, "max": 10.0, "min": -10.0, "step": 0.01}),
"clamp_max": ("FLOAT", {"default": 1.0, "max": 10.0, "min": -10.0, "step": 0.01}),
"seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}),
"device": (["cpu", "cuda"],),
},
"optional": {
"optional_vae": ("VAE",),
"ch_settings": ("CH_SETTINGS",),
}
}
RETURN_TYPES = ("LATENT","IMAGE")
RETURN_NAMES = ("latents","previews")
FUNCTION = "cross_hatch"
CATEGORY = "Power Noise Suite/Noise"
def cross_hatch(self, batch_size, width, height, resampling, frequency, octaves, persistence, color_tolerance, num_colors, angle_degrees, brightness, contrast, blur, clamp_min, clamp_max, seed, device, optional_vae=None, ch_settings=None):
if ch_settings:
ch = ch_settings
frequency = ch['frequency']
octaves = ch['octaves']
persistence = ch['persistence']
color_tolerance = ch['color_tolerance']
num_colors = ch['num_colors']
angle_degrees = ch['angle_degrees']
brightness = ch['brightness']
contrast = ch['contrast']
blur = ch['blur']
cross_hatch = CrossHatchPowerFractal(width=width, height=height, frequency=frequency, octaves=octaves, persistence=persistence, num_colors=num_colors, color_tolerance=color_tolerance, angle_degrees=angle_degrees, blur=blur, clamp_min=clamp_min, clamp_max=clamp_max)
tensors = cross_hatch(batch_size, device, seed).to(device="cpu")
tensors = torch.cat([tensors, torch.ones(batch_size, height, width, 1, dtype=tensors.dtype, device='cpu')], dim=-1)
if optional_vae is None:
latents = tensors.permute(0, 3, 1, 2)
latents = F.interpolate(latents, size=((height // 8), (width // 8)), mode=resampling)
return {'samples': latents}, tensors
encoder = nodes.VAEEncode()
latents = []
for tensor in tensors:
tensor = tensor.unsqueeze(0)
latents.append(encoder.encode(optional_vae, tensor)[0]['samples'])
latents = torch.cat(latents)
return {'samples': latents}, tensors
# LINEAR CROSS-HATCH POWER FRACTAL LATENT
class PPFNLinearCrossHatchNode:
def __init__(self):
pass
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"batch_size": ("INT", {"default": 1, "max": 64, "min": 1, "step": 1}),
"width": ("INT", {"default": 512, "max": 8192, "min": 64, "step": 1}),
"height": ("INT", {"default": 512, "max": 8192, "min": 64, "step": 1}),
"resampling": (["nearest-exact", "bilinear", "area", "bicubic", "bislerp"],),
"frequency": ("FLOAT", {"default": 320.0, "max": 1024.0, "min": 0.001, "step": 0.001}),
"gain": ("FLOAT", {"default": 0.25, "max": 1.0, "min": 0.0, "step": 0.001}),
"octaves": ("INT", {"default": 12, "max": 32, "min": 1, "step": 1}),
"persistence": ("FLOAT", {"default": 1.5, "max": 2.0, "min": 0.001, "step": 0.001}),
"add_noise": ("FLOAT", {"default": 0.0, "max": 1.0, "min": 0.0, "step": 0.001}),
"linear_range": ("INT", {"default": 16, "max": 256, "min": 2, "step": 1}),
"linear_tolerance": ("FLOAT", {"default": 0.05, "max": 1.0, "min": 0.001, "step": 0.001}),
"angle_degrees": ("FLOAT", {"default": 45.0, "max": 360.0, "min": 0.0, "step": 0.01}),
"brightness": ("FLOAT", {"default": 0.0, "max": 1.0, "min": -1.0, "step": 0.001}),
"contrast": ("FLOAT", {"default": 0.0, "max": 1.0, "min": -1.0, "step": 0.001}),
"seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}),
"device": (["cpu", "cuda"],),
},
"optional": {
"optional_vae": ("VAE",),
}
}
RETURN_TYPES = ("LATENT","IMAGE")
RETURN_NAMES = ("latents","previews")
FUNCTION = "cross_hatch"
CATEGORY = "Power Noise Suite/Noise"
def cross_hatch(self, batch_size, width, height, resampling, frequency, gain, octaves, persistence, add_noise, linear_range, linear_tolerance, angle_degrees, brightness, contrast, seed, device, optional_vae=None):
cross_hatch = CrossHatchLinearPowerFractal(width=width, height=height, frequency=frequency, gain=gain, octaves=octaves, persistence=persistence, add_noise_tolerance=add_noise, mapping_range=linear_range, angle_degrees=angle_degrees, brightness=brightness, contrast=contrast)
tensors = cross_hatch(batch_size, device, seed)
tensors = torch.cat([tensors, torch.ones(batch_size, height, width, 1, dtype=tensors.dtype, device='cpu')], dim=-1)
if optional_vae is None:
latents = tensors.permute(0, 3, 1, 2)
latents = F.interpolate(latents, size=((height // 8), (width // 8)), mode=resampling)
return {'samples': latents}, tensors
encoder = nodes.VAEEncode()
latents = []
for tensor in tensors:
tensor = tensor.unsqueeze(0)
latents.append(encoder.encode(optional_vae, tensor)[0]['samples'])
latents = torch.cat(latents)
return {'samples': latents}, tensors
# BLEND LATENTS
class PPFNBlendLatents:
def __init__(self):
pass
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"latent_a": ("LATENT",),
"latent_b": ("LATENT",),
"operation": (sorted(list(blending_modes.keys())),),
"blend_ratio": ("FLOAT", {"default": 0.5, "min": 0.01, "max": 1.0, "step": 0.01}),
"blend_strength": ("FLOAT", {"default": 1.0, "min": -100.0, "max": 100.0, "step": 0.01}),
},
"optional": {
"mask": ("MASK",),
"set_noise_mask": (["false", "true"],),
"normalize": (["false", "true"],),
"clamp_min": ("FLOAT", {"default": 0.0, "max": 10.0, "min": -10.0, "step": 0.01}),
"clamp_max": ("FLOAT", {"default": 1.0, "max": 10.0, "min": -10.0, "step": 0.01}),
"latent2rgb_preview": (["false", "true"],),
}
}
RETURN_TYPES = ("LATENT","IMAGE",)
RETURN_NAMES = ("latents", "previews")
FUNCTION = "latent_blend"
CATEGORY = "Power Noise Suite/Latent/Adjustements"
def latent_blend(self, latent_a, latent_b, operation, blend_ratio, blend_strength, mask=None, set_noise_mask=None, normalize=None, clamp_min=None, clamp_max=None, latent2rgb_preview=None):
latent_a = latent_a["samples"][:, :-1]
latent_b = latent_b["samples"][:, :-1]
assert latent_a.shape == latent_b.shape, f"Input latents must have the same shape, but got: a {latent_a.shape}, b {latent_b.shape}"
alpha_a = latent_a[:, -1:]
alpha_b = latent_b[:, -1:]
blended_rgb = self.blend_latents(latent_a, latent_b, operation, blend_ratio, blend_strength, clamp_min, clamp_max)
blended_alpha = torch.ones_like(blended_rgb[:, :1])
blended_latent = torch.cat((blended_rgb, blended_alpha), dim=1)
tensors = latents_to_images(blended_latent, (True if latent2rgb_preview and latent2rgb_preview == "true" else False))
if mask is not None:
blend_mask = self.transform_mask(mask, latent_a["samples"].shape)
blended_latent = blend_mask * blended_latent + (1 - blend_mask) * latent_a["samples"]
if set_noise_mask == 'true':
return ({"samples": blended_latent, "noise_mask": mask.reshape((-1, 1, mask.shape[-2], mask.shape[-1]))}, tensors)
else:
return ({"samples": blended_latent}, tensors)
else:
return ({"samples": blended_latent}, tensors)
def blend_latents(self, latent1, latent2, mode='add', blend_percentage=0.5, blend_strength=0.5, mask=None, clamp_min=0.0, clamp_max=1.0):
blend_func = blending_modes.get(mode)
if blend_func is None:
raise ValueError(f"Unsupported blending mode. Please choose from the supported modes: {', '.join(list(blending_modes.keys()))}")
blend_factor1 = blend_percentage
blend_factor2 = 1 - blend_percentage
blended_latent = blend_func(latent1, latent2, blend_strength * blend_factor1)
if normalize and normalize == "true":
blended_latent = normalize(blended_latent, clamp_min, clamp_max)
return blended_latent
def transform_mask(self, mask, shape):
mask = mask.view(-1, 1, mask.shape[-2], mask.shape[-1])
resized_mask = torch.nn.functional.interpolate(mask, size=(shape[2], shape[3]), mode="bilinear")
expanded_mask = resized_mask.expand(-1, shape[1], -1, -1)
if expanded_mask.shape[0] < shape[0]:
expanded_mask = expanded_mask.repeat((shape[0] - 1) // expanded_mask.shape[0] + 1, 1, 1, 1)[:shape[0]]
del mask, resized_mask
return expanded_mask
# IMAGES TO LATENTS
class PPFNImageAsLatent:
def __init__(self):
pass
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"images": ("IMAGE",),
"resampling": (["nearest-exact", "bilinear", "area", "bicubic", "bislerp"],),
},
}
RETURN_TYPES = ("LATENT","IMAGE",)
RETURN_NAMES = ("latents", "images")
FUNCTION = "image_latent"
CATEGORY = "latent/util"
def image_latent(self, images, resampling):
if images.shape[-1] != 4:
ones_channel = torch.ones(images.shape[:-1] + (1,), dtype=images.dtype, device=images.device)
images = torch.cat((images, ones_channel), dim=-1)
latents = images.permute(0, 3, 1, 2)
latents = F.interpolate(latents, size=((images.shape[1] // 8), (images.shape[2] // 8)), mode=resampling)
return {'samples': latents}, images
# LATENTS TO CPU
class PPFNLatentToCPU:
def __init__(self):
pass
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"latents": ("LATENT",),
},
}
RETURN_TYPES = ("LATENT",)
RETURN_NAMES = ("latents",)
FUNCTION = "latent_to_cpu"
CATEGORY = "Power Noise Suite/Latent/Util"
def latent_to_cpu(self, latents):
return ({'samples': latents['samples'].to(device="cpu")}, )
# LATENT ADJUSTMENT
class PPFNLatentAdjustment:
def __init__(self):
pass
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"latents": ("LATENT",),
"brightness": ("FLOAT", {"default": 1.0, "max": 2.0, "min": -1.0, "step": 0.001}),
"contrast": ("FLOAT", {"default": 1.0, "max": 2.0, "min": -1.0, "step": 0.001}),
"saturation": ("FLOAT", {"default": 1.0, "max": 2.0, "min": 0.0, "step": 0.001}),
"exposure": ("FLOAT", {"default": 0.0, "max": 2.0, "min": -1.0, "step": 0.001}),
"alpha_sharpen": ("FLOAT", {"default": 0.0, "max": 10.0, "min": 0.0, "step": 0.01}),
"high_pass_radius": ("FLOAT", {"default": 0.0, "max": 1024, "min": 0.0, "step": 0.01}),
"high_pass_strength": ("FLOAT", {"default": 1.0, "max": 2.0, "min": 0.0, "step": 0.01}),
"clamp_min": ("FLOAT", {"default": 0.0, "max": 10.0, "min": -10.0, "step": 0.01}),
"clamp_max": ("FLOAT", {"default": 1.0, "max": 10.0, "min": -10.0, "step": 0.01}),
},
"optional": {
"latent2rgb_preview": (["false", "true"],),
}
}
RETURN_TYPES = ("LATENT", "IMAGE")
RETURN_NAMES = ("latents", "previews")
FUNCTION = "adjust_latent"
CATEGORY = "Power Noise Suite/Latent/Adjustements"
def adjust_latent(self, latents, brightness, contrast, saturation, exposure, alpha_sharpen, high_pass_radius, high_pass_strength, clamp_min, clamp_max, latent2rgb_preview=False):
original_latents = latents['samples']
r, g, b, a = original_latents[:, 0:1], original_latents[:, 1:2], original_latents[:, 2:3], original_latents[:, 3:4]
r = (r - 0.5) * contrast + 0.5 + (brightness - 1.0)
g = (g - 0.5) * contrast + 0.5 + (brightness - 1.0)
b = (b - 0.5) * contrast + 0.5 + (brightness - 1.0)
gray = 0.299 * r + 0.587 * g + 0.114 * b
r = (r - gray) * saturation + gray
g = (g - gray) * saturation + gray
b = (b - gray) * saturation + gray
r = r * (2 ** exposure)
g = g * (2 ** exposure)
b = b * (2 ** exposure)
latents = torch.cat((r, g, b, a), dim=1)
if alpha_sharpen > 0:
latents = sharpen_latents(latents, alpha_sharpen)
if high_pass_radius > 0:
latents = high_pass_latents(latents, high_pass_radius, high_pass_strength)
if clamp_min != 0:
latents = normalize(latents, target_min=clamp_min)
if clamp_max != 1:
latents = normalize(latents, target_max=clamp_max)
if clamp_min != 0 and clamp_max != 1.0:
latents = normalize(latents, target_min=clamp_min, target_max=clamp_max)
tensors = latents_to_images(latents, (True if latent2rgb_preview and latent2rgb_preview == "true" else False))
return {'samples': latents}, tensors
# POWER KSAMPLER ADVANCED
class PPFNKSamplerAdvanced:
@classmethod
def INPUT_TYPES(s):
noise_types = PowerLawNoise.get_noise_types()
noise_types.append('vanilla_comfy')
samplers = ['dpmpp_sde', 'dpmpp_sde_gpu', 'dpmpp_2m_sde', 'dpmpp_2m_sde_gpu', 'dpmpp_3m_sde', 'dpmpp_3m_sde_gpu', 'euler_ancestral', 'dpm_2_ancestral', 'dpmpp_2s_ancestral', 'dpm_fast', 'dpm_adaptive']
return {
"required": {
"model": ("MODEL",),
"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_name": (samplers,),
"scheduler": (comfy.samplers.KSampler.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}),
"enable_denoise": (["false", "true"],),
"denoise": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}),
"add_noise": (["enable", "disable"],),
"return_with_leftover_noise": (["disable", "enable"],),
},
"optional": {
"noise_type": (noise_types,),
"noise_blending": (["bislerp", "cosine interp", "cuberp", "hslerp", "lerp", "add", "inject"],),
"noise_mode": (["additive", "subtractive"],),
"scale": ("FLOAT", {"default": 1.0, "max": sys.maxsize-1, "min": -(sys.maxsize-1), "step": 0.001}),
"alpha_exponent": ("FLOAT", {"default": 1.0, "max": 12.0, "min": -12.0, "step": 0.001}),
"modulator": ("FLOAT", {"default": 1.0, "max": 2.0, "min": 0.1, "step": 0.01}),
"sigma_tolerance": ("FLOAT", {"default": 0.5, "max": 1.0, "min": 0.0, "step": 0.001}),
"boost_leading_sigma": (["false", "true"],),
"tonal_guide_latent": ("LATENT",),
"ppf_settings": ("PPF_SETTINGS",),
"ch_settings": ("CH_SETTINGS",),
"guide_use_noise": (["true", "false"],),
}
}
RETURN_TYPES = ("LATENT",)
FUNCTION = "sample"
CATEGORY = "Power Noise Suite/Sampling"
def sample(self, model, add_noise, seed, steps, cfg, sampler_name, scheduler, positive, negative, latent_image, start_at_step, end_at_step, enable_denoise, denoise, return_with_leftover_noise, noise_type='grey', noise_blending="bislerp", noise_mode="additive", scale=1.0, alpha_exponent=1.0, modulator=1.0, sigma_tolerance=1.0, boost_leading_sigma="false", ppf_settings=None, ch_settings=None, tonal_guide_latent=None, guide_use_noise="true"):
# WHITE-NOISE SAMPLER HIJACK
def pns_noise_sampler(x):
seed_base = seed
noise_idx = [0]
height = int(x.shape[2] * 8)
width = int(x.shape[3] * 8)
method = noise_type if noise_type in PowerLawNoise.get_noise_types() else PowerLawNoise.get_noise_types()[0]
alpha_exp = alpha_exponent if not math.isnan(alpha_exponent) else 1.0
range_scale = scale if not math.isnan(scale) else 1.0
modu = modulator if not math.isnan(modulator) else 1.0
sigma_tol = sigma_tolerance if not math.isnan(sigma_tolerance) else 0.5
ppfs = ppf_settings
chs = ch_settings
total_steps = steps
blending_mode = noise_blending
blend_type = noise_mode
boost_sigma = (boost_leading_sigma == "true")
guide = tonal_guide_latent['samples'] if isinstance(tonal_guide_latent, dict) else None
guide_noise = (guide_use_noise == "true")
def pns_return_noise(seed, x, sigma, sigma_tol, boost_sigma, total_steps, method, alpha_exp, range_scale, modu, blending_modes, blending_mode, ppfs, chs, guide, guide_noise):
seed = seed_base + noise_idx[0]
rand_noise = torch.randn_like(x)
if sigma_tol == 0.0:
return rand_noise
sigma_min = 0
sigma_max = 14.614643096923828
if isinstance(sigma, torch.Tensor) and sigma.numel() == 1:
sigma = sigma.item()
elif isinstance(sigma, float):
sigma = sigma
else:
sigma = 0
scaled_sigma = ((sigma - sigma_min) / (sigma_max - sigma_min)) * sigma_tol
if boost_sigma and noise_idx[0] < (total_steps // 4):
scaled_sigma = scaled_sigma * 1.25 if scaled_sigma * 1.25 <= 1.0 else 1.0
if not ppfs and not chs:
power_law = PowerLawNoise(device=rand_noise.device)
noise = power_law(1, width, height, noise_type=method, alpha=alpha_exp, scale=range_scale, modulator=modu, seed=seed).to(x.device)
elif ppfs:
power_fractal = PPFNoiseNode()
noise = power_fractal.power_fractal_latent(1, width, height, 'nearest', ppfs['X'], ppfs['Y'], ppfs['Z'], ppfs['evolution'], ppfs['frame'], ppfs['scale'], ppfs['octaves'], ppfs['persistence'], ppfs['lacunarity'], ppfs['exponent'], ppfs['brightness'], ppfs['contrast'], 0.0, 1.0, seed, device=('cuda' if torch.cuda.is_available() else 'cpu'), optional_vae=None)[0]['samples'].to(device=rand_noise.device)
elif chs:
ch_fractal = PPFNCrossHatchNode()
noise = ch_fractal.cross_hatch(1, width, height, 'nearest', chs['frequency'], chs['octaves'], chs['persistence'], chs['color_tolerance'], chs['num_colors'], chs['angle_degrees'], chs['brightness'], chs['contrast'], chs['blur'], 0.0, 1.0, seed, device=('cuda' if torch.cuda.is_available() else 'cpu'), optional_vae=None)[0]['samples'].to(device=rand_noise.device)
noise = noise.permute(0, 3, 1, 2)
noise = F.interpolate(noise, size=(x.shape[2], x.shape[3]), mode='nearest')
noise = noise[:, :rand_noise.shape[1], :, :]
if not ppfs and not chs:
alpha = torch.ones((1, x.shape[2], x.shape[3], 1), dtype=x.dtype, device=x.device).permute(0, 3, 1, 2)
noise = torch.cat((noise, alpha), dim=1)
if not isinstance(guide, torch.Tensor):
if blend_type == "additive":
blended_noise = rand_noise + 0.25 * (blending_modes[blending_mode](rand_noise.to(device=rand_noise.device), noise.to(device=rand_noise.device), scaled_sigma) - rand_noise)
else:
blended_noise = rand_noise - 0.25 * (blending_modes[blending_mode](rand_noise.to(device=rand_noise.device), noise.to(device=rand_noise.device), scaled_sigma) - rand_noise)
else:
guide = guide.to(x.device)
if guide.shape[2] != x.shape[2] or guide.shape[3] != x.shape[3]:
guide = F.interpolate(guide, size=(x.shape[2], x.shape[3]), mode='nearest')
if guide_noise:
noise_blend_ratio = max(scaled_sigma, 1.0) / 2
latent_blend_ratio = max(scaled_sigma * 1.5, 1.0)
x = blending_modes["inject"](guide.to(x.device), x, latent_blend_ratio)
noise = blending_modes["inject"](guide.to(x.device), noise, noise_blend_ratio)
else:
latent_blend_ratio = max(scaled_sigma * 1.5, 1.0)
x = blending_modes["colorize"](guide.to(x.device), x, latent_blend_ratio)
noise = guide
blended_noise = blending_modes["colorize"](blending_modes[blending_mode](rand_noise, noise, scaled_sigma/32), guide, scaled_sigma/128)
noise_idx[0] += 1
return blended_noise
return lambda sigma, sigma_next, **kwargs: pns_return_noise(seed_base + noise_idx[0], x, sigma, sigma_tol, boost_sigma, total_steps, method, alpha_exp, range_scale, modu, blending_modes, blending_mode, ppfs, chs, guide, guide_noise)
# BROWNIAN NOISE SAMPLER HIJACK
class PNSNoiseSampler:
seed_base = seed
noise_idx = [0]
method = noise_type if noise_type in PowerLawNoise.get_noise_types() else PowerLawNoise.get_noise_types()[0]
alpha_exp = alpha_exponent if not math.isnan(alpha_exponent) else 1.0
range_scale = scale if not math.isnan(scale) else 1.0
modu = modulator if not math.isnan(modulator) else 1.0
sigma_tol = sigma_tolerance if not math.isnan(sigma_tolerance) else 0.5
ppfs = ppf_settings
chs = ch_settings
total_steps = steps
blending_mode = noise_blending
blend_type = noise_mode
boost_sigma = (boost_leading_sigma == "true")
guide = tonal_guide_latent['samples'] if isinstance(tonal_guide_latent, dict) else None
guide_noise = (guide_use_noise == "true")
def __init__(self, x, sigma_min, sigma_max, seed=None, transform=lambda x: x, cpu=False):
self.noise_idx = [0]
self.x = x
self.height = int(x.shape[2] * 8)
self.width = int(x.shape[3] * 8)
self.sigma_min = sigma_min
self.sigma_max = sigma_max
self.transform = transform
t0, t1 = self.transform(torch.as_tensor(sigma_min)), self.transform(torch.as_tensor(sigma_max))
self.tree = comfy.k_diffusion.sampling.BatchedBrownianTree(x, t0, t1, seed, cpu=cpu)
def __call__(self, sigma, sigma_next):
noise = self.sample_noise(self.x, sigma, sigma_next)
return noise
def sample_noise(self, x, sigma, sigma_next):
t0, t1 = self.transform(torch.as_tensor(sigma)), self.transform(torch.as_tensor(sigma_next))
tree = self.tree(t0, t1) / (t1 - t0).abs().sqrt()
seed = self.seed_base + self.noise_idx[0]
rand_noise = torch.randn_like(x)
if self.sigma_tol == 0.0:
return tree
sigma_min = self.sigma_min
sigma_max = self.sigma_max # 14.614643096923828
if isinstance(sigma_min, torch.Tensor) and sigma.numel() == 1:
sigma_min = sigma_min.item()
if isinstance(sigma_max, torch.Tensor) and sigma.numel() == 1:
sigma_max = (sigma_max / 2).item()
if isinstance(sigma, torch.Tensor) and sigma.numel() == 1:
sigma = sigma.item()
elif isinstance(sigma, float):
sigma = sigma
else:
sigma = 0
scaled_sigma = (((sigma - sigma_min) / (sigma_max - sigma_min)) * self.sigma_tol) / 2
if self.boost_sigma and self.noise_idx[0] < (self.total_steps // 4):
scaled_sigma = scaled_sigma * 1.25 if scaled_sigma * 1.25 <= 1.0 else 1.0
ppfs = self.ppfs
chs = self.chs
if not ppfs and not chs:
power_law = PowerLawNoise(device=tree.device)
noise = power_law(1, self.width, self.height, noise_type=self.method, alpha=self.alpha_exp, scale=self.range_scale, modulator=self.modu, seed=seed).to(device=tree.device)
elif ppfs:
power_fractal = PPFNoiseNode()
noise = power_fractal.power_fractal_latent(1, self.width, self.height, 'nearest', ppfs['X'], ppfs['Y'], ppfs['Z'], ppfs['evolution'], ppfs['frame'], ppfs['scale'], ppfs['octaves'], ppfs['persistence'], ppfs['lacunarity'], ppfs['exponent'], ppfs['brightness'], ppfs['contrast'], 0.0, 1.0, seed, device=('cuda' if torch.cuda.is_available() else 'cpu'), optional_vae=None)[0]['samples'].to(device=tree.device)
elif chs:
ch_fractal = PPFNCrossHatchNode()
noise = ch_fractal.cross_hatch(1, self.width, self.height, 'nearest', chs['frequency'], chs['octaves'], chs['persistence'], chs['color_tolerance'], chs['num_colors'], chs['angle_degrees'], chs['brightness'], chs['contrast'], chs['blur'], 0.0, 1.0, seed, device=('cuda' if torch.cuda.is_available() else 'cpu'), optional_vae=None)[0]['samples'].to(device=tree.device).to(device=tree.device)
noise = noise_sigma_scale(noise, self.sigma_min, self.sigma_max)
noise = noise.permute(0, 3, 1, 2)
noise = F.interpolate(noise, size=(x.shape[2], x.shape[3]), mode='nearest')
noise = noise[:, :tree.shape[1], :, :]
if not ppfs and not chs:
alpha = torch.ones((1, x.shape[2], x.shape[3], 1), dtype=x.dtype, device=x.device).permute(0, 3, 1, 2)
noise = torch.cat((noise, alpha), dim=1)
if not isinstance(self.guide, torch.Tensor):
if self.blend_type == "additive":
blended_noise = tree + 0.03 * (blending_modes[self.blending_mode](tree, sharpen_latents(noise.to(device=tree.device), 0.5), scaled_sigma) - tree)
else:
blended_noise = tree - 0.03 * (blending_modes[self.blending_mode](tree, sharpen_latents(noise.to(device=tree.device), 0.5), scaled_sigma) - tree)
else:
guide = self.guide.to(x.device)
if self.guide.shape[2] != x.shape[2] or self.guide.shape[3] != x.shape[3]:
guide = F.interpolate(self.guide.to(x.device), size=(x.shape[2], x.shape[3]), mode='nearest')
if self.guide_noise:
noise_blend_ratio = max(scaled_sigma, 1.0) / 2
noise = blending_modes["inject"](guide, noise, noise_blend_ratio)
else:
latent_blend_ratio = max(scaled_sigma * 1.5, 1.0)
x = blending_modes["colorize"](guide, x, latent_blend_ratio)
noise = guide
blended_noise = blending_modes["colorize"](blending_modes[self.blending_mode](tree, noise, scaled_sigma/64), guide, scaled_sigma/128)
self.noise_idx[0] += 1
return blended_noise
dns = None
btns = None
if enable_denoise == "true":
start_at_step = None
end_at_step = None
else:
denoise = 1.0
force_full_denoise = True
if return_with_leftover_noise == "enable":
force_full_denoise = False
disable_noise = False
if add_noise == "disable":
disable_noise = True
else:
if noise_type != "vanilla_comfy":
print("Running with 🦚 PNS Noise Samplers")
dns = comfy.k_diffusion.sampling.default_noise_sampler
btns = comfy.k_diffusion.sampling.BrownianTreeNoiseSampler
comfy.k_diffusion.sampling.default_noise_sampler = pns_noise_sampler
comfy.k_diffusion.sampling.BrownianTreeNoiseSampler = PNSNoiseSampler
try:
result = nodes.common_ksampler(model, seed, steps, cfg, sampler_name, scheduler, positive, negative, latent_image, denoise=denoise, disable_noise=disable_noise, start_step=start_at_step, last_step=end_at_step, force_full_denoise=force_full_denoise)
except comfy.model_management.InterruptProcessingException as e:
if noise_type != "vanilla_comfy":
if dns and btns:
print("Restoring ComfyUI Noise Samplers.")
comfy.k_diffusion.sampling.default_noise_sampler = dns
comfy.k_diffusion.sampling.BrownianTreeNoiseSampler = btns
raise e
if noise_type != "vanilla_comfy" and not disable_noise:
print("\nRestoring ComfyUI Noise Samplers")
if dns:
comfy.k_diffusion.sampling.default_noise_sampler = dns
if btns:
comfy.k_diffusion.sampling.BrownianTreeNoiseSampler = btns
return result
# PERLIN POWER FRACTAL SETTINGS
class PPFNoiseSettings:
def __init__(self):
pass
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"X": ("FLOAT", {"default": 0, "max": 99999999, "min": -99999999, "step": 0.01}),
"Y": ("FLOAT", {"default": 0, "max": 99999999, "min": -99999999, "step": 0.01}),
"Z": ("FLOAT", {"default": 0, "max": 99999999, "min": -99999999, "step": 0.01}),
"evolution": ("FLOAT", {"default": 0.0, "max": 1.0, "min": 0.0, "step": 0.01}),
"frame": ("INT", {"default": 0, "max": 99999999, "min": 0, "step": 1}),
"scale": ("FLOAT", {"default": 5, "max": 2048, "min": 2, "step": 0.01}),
"octaves": ("INT", {"default": 8, "max": 8, "min": 1, "step": 1}),
"persistence": ("FLOAT", {"default": 1.5, "max": 23.0, "min": 0.01, "step": 0.01}),
"lacunarity": ("FLOAT", {"default": 2.0, "max": 99.0, "min": 0.01, "step": 0.01}),
"exponent": ("FLOAT", {"default": 4.0, "max": 38.0, "min": 0.01, "step": 0.01}),
"brightness": ("FLOAT", {"default": 0.0, "max": 1.0, "min": -1.0, "step": 0.01}),
"contrast": ("FLOAT", {"default": 0.0, "max": 1.0, "min": -1.0, "step": 0.01}),
},
}
RETURN_TYPES = ("PPF_SETTINGS",)
RETURN_NAMES = ("ppf_settings",)
FUNCTION = "power_fractal_settings"
CATEGORY = "Power Noise Suite/Sampling/Settings"
def power_fractal_settings(self, X, Y, Z, evolution, frame, scale, octaves, persistence, lacunarity, exponent, brightness, contrast):
return ({"X": X, "Y": Y, "Z": Z, "evolution": evolution, "frame": frame, "scale": scale, "octaves": octaves, "persistence": persistence, "lacunarity": lacunarity, "exponent": exponent, "brightness": brightness, "contrast": contrast},)
# CROSS-HATCH POWER FRACTAL SETTINGS
class PPFNCrossHatchSettings:
def __init__(self):
pass
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"frequency": ("FLOAT", {"default": 320.0, "max": 1024.0, "min": 0.001, "step": 0.001}),
"octaves": ("INT", {"default": 12, "max": 32, "min": 1, "step": 1}),
"persistence": ("FLOAT", {"default": 1.5, "max": 2.0, "min": 0.001, "step": 0.001}),
"num_colors": ("INT", {"default": 16, "max": 256, "min": 2, "step": 1}),
"color_tolerance": ("FLOAT", {"default": 0.05, "max": 1.0, "min": 0.001, "step": 0.001}),
"angle_degrees": ("FLOAT", {"default": 45.0, "max": 360.0, "min": 0.0, "step": 0.01}),
"brightness": ("FLOAT", {"default": 0.0, "max": 1.0, "min": -1.0, "step": 0.001}),
"contrast": ("FLOAT", {"default": 0.0, "max": 1.0, "min": -1.0, "step": 0.001}),
"blur": ("FLOAT", {"default": 2.5, "max": 1024, "min": 0, "step": 0.01}),
},
}
RETURN_TYPES = ("CH_SETTINGS",)
RETURN_NAMES = ("ch_settings",)
FUNCTION = "cross_hatch_settings"
CATEGORY = "Power Noise Suite/Sampling/Settings"
def cross_hatch_settings(self, frequency, octaves, persistence, color_tolerance, num_colors, angle_degrees, brightness, contrast, blur):
return ({"frequency": frequency, "octaves": octaves, "persistence": persistence, "color_tolerance": color_tolerance, "num_colors": num_colors, "angle_degrees": angle_degrees, "brightness": brightness, "contrast": contrast, "blur": blur},)
NODE_CLASS_MAPPINGS = {
"Blend Latents (PPF Noise)": PPFNBlendLatents,
"Cross-Hatch Power Fractal (PPF Noise)": PPFNCrossHatchNode,
"Cross-Hatch Power Fractal Settings (PPF Noise)": PPFNCrossHatchSettings,
"Images as Latents (PPF Noise)": PPFNImageAsLatent,
"Latent Adjustment (PPF Noise)": PPFNLatentAdjustment,
"Latents to CPU (PPF Noise)": PPFNLatentToCPU,
"Linear Cross-Hatch Power Fractal (PPF Noise)": PPFNLinearCrossHatchNode,
"Perlin Power Fractal Latent (PPF Noise)": PPFNoiseNode,
"Perlin Power Fractal Settings (PPF Noise)": PPFNoiseSettings,
"Power-Law Noise (PPF Noise)": PPFNPowerLawNoise,
"Power KSampler Advanced (PPF Noise)": PPFNKSamplerAdvanced,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"Blend Latents (PPF Noise)": "Blend Latents 🦚",
"Cross-Hatch Power Fractal (PPF Noise)": "Cross-Hatch Power Fractal 🦚",
"Cross-Hatch Power Fractal Settings (PPF Noise)": "Cross-Hatch Power Fractal Settings 🦚",
"Images as Latents (PPF Noise)": "Images as Latents 🦚",
"Latent Adjustment (PPF Noise)": "Latent Adjustment 🦚",
"Latents to CPU (PPF Noise)": "Latents to CPU 🦚",
"Linear Cross-Hatch Power Fractal (PPF Noise)": "Linear Cross-Hatch Power Fractal 🦚",
"Perlin Power Fractal Latent (PPF Noise)": "Perlin Power Fractal Noise 🦚",
"Perlin Power Fractal Settings (PPF Noise)": "Perlin Power Fractal Settings 🦚",
"Power-Law Noise (PPF Noise)": "Power-Law Noise 🦚",
"Power KSampler Advanced (PPF Noise)": "Power KSampler Advanced 🦚",
}