From 002714a4733feb8bbe31dbe51ce3de5832789ea5 Mon Sep 17 00:00:00 2001 From: WAS Date: Tue, 26 Sep 2023 15:17:37 -0700 Subject: [PATCH] Add files via upload --- WAS_ConditioningBlend.py | 191 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 191 insertions(+) create mode 100644 WAS_ConditioningBlend.py diff --git a/WAS_ConditioningBlend.py b/WAS_ConditioningBlend.py new file mode 100644 index 0000000..1286b53 --- /dev/null +++ b/WAS_ConditioningBlend.py @@ -0,0 +1,191 @@ +# Provides demonstration of PR https://github.com/comfyanonymous/ComfyUI/pull/1574/commits/297d1cff422198806cda40e4b6d71a6e6aa05453 + +import torch +import math + +def normalize(latent, target_min=None, target_max=None): + """ + Normalize a tensor `latent` between `target_min` and `target_max`. + + Args: + latent (torch.Tensor): The input tensor to be normalized. + target_min (float, optional): The minimum value after normalization. + - When `None` min will be tensor min range value. + target_max (float, optional): The maximum value after normalization. + - When `None` max will be tensor max range value. + + Returns: + torch.Tensor: The normalized tensor + """ + min_val = latent.min() + max_val = latent.max() + + if target_min is None: + target_min = min_val + if target_max is None: + target_max = max_val + + normalized = (latent - min_val) / (max_val - min_val) + scaled = normalized * (target_max - target_min) + target_min + return scaled + +def slerp(a, b, t): + """ + Perform Spherical Linear Interpolation (SLERP) between two tensors. + + This function interpolates between two input tensors `a` and `b` using SLERP, + which is a method for smoothly transitioning between orientations or vectors + represented as tensors. + + Args: + a (tensor): The first input tensor. + b (tensor): The second input tensor. + t (float): The blending factor, a value between 0 and 1 that controls the interpolation. + + Returns: + tensor: The result of SLERP interpolation between `a` and `b`. + + Note: + SLERP provides a smooth, shortest-path interpolation between two orientations or vectors + represented as tensors. It's commonly used in applications like 3D graphics and robotics. + """ + if a.shape != b.shape: + raise ValueError("Input tensors a and b must have the same shape.") + + a = torch.nn.functional.normalize(a, dim=-1) + b = torch.nn.functional.normalize(b, dim=-1) + + dot_product = torch.sum(a * b, dim=-1).clamp(-1.0, 1.0) + angle = torch.acos(dot_product) + + slerp_result = ( + (a * torch.sin((1 - t) * angle) + b * torch.sin(t * angle)) / + torch.sin(angle) + ) + + slerp_result = normalize(slerp_result) + + return slerp_result + +def hslerp(a, b, t): + """ + Perform Hybrid Spherical Linear Interpolation (HSLERP) between two tensors. + + This function combines two input tensors `a` and `b` using HSLERP, which is a specialized + interpolation method for smooth transitions between orientations or colors. + + Args: + a (tensor): The first input tensor. + b (tensor): The second input tensor. + t (float): The blending factor, a value between 0 and 1 that controls the interpolation. + + Returns: + tensor: The result of HSLERP interpolation between `a` and `b`. + + Note: + HSLERP provides smooth transitions between orientations or colors, particularly useful + in applications like image processing and 3D graphics. + """ + if a.shape != b.shape: + raise ValueError("Input tensors a and b must have the same shape.") + + num_channels = a.size(1) + + interpolation_tensor = torch.zeros(1, num_channels, 1, 1, device=a.device, dtype=a.dtype) + interpolation_tensor[0, 0, 0, 0] = 1.0 + + result = (1 - t) * a + t * b + + if t < 0.5: + result += (torch.norm(b - a, dim=1, keepdim=True) / 6) * interpolation_tensor + else: + result -= (torch.norm(b - a, dim=1, keepdim=True) / 6) * interpolation_tensor + + return result + +import torch + +blending_modes = { + # Linearly combines the two input tensors a and b using the parameter t. + 'add': lambda a, b, t: (a * t + b * (1 - t)), + + # Interpolates between tensors a and b using normalized linear interpolation. + 'bislerp': lambda a, b, t: (a * (1 - t) + b * t), + + # Interpolates between tensors a and b using cosine interpolation. + 'cosine interp': lambda a, b, t: (a + b - (a - b) * torch.cos(t * torch.tensor(math.pi))) / 2, + + # Interpolates between tensors a and b using cubic interpolation. + 'cuberp': lambda a, b, t: a + (b - a) * (3 * t ** 2 - 2 * t ** 3), + + # Computes the absolute difference between tensors a and b, scaled by t. + 'difference': lambda a, b, t: (abs(a - b) * t), + + # Combines tensors a and b using an exclusion formula, scaled by t. + 'exclusion': lambda a, b, t: ((a + b - 2 * a * b) * t), + + # Interpolates between tensors a and b using normalized linear interpolation, + # with a twist when t is greater than or equal to 0.5. + 'hslerp': lambda a, b, t: (a * (1 - t) + b * t) if t < 0.5 else (a * t + b * (1 - t)), + + # Adds tensor b to tensor a, scaled by t. + 'inject': lambda a, b, t: (a + b * t), + + # Interpolates between tensors a and b using linear interpolation. + 'lerp': lambda a, b, t: (a * (1 - t) + b * t), + + # Generates random values and combines tensors a and b with random weights, scaled by t. + 'random': lambda a, b, t: (a + (torch.rand_like(b) * b - a) * t), + + # Interpolates between tensors a and b using spherical linear interpolation (SLERP). + 'slerp': lambda a, b, t: (a * (1 - t) + b * t), + + # Subtracts tensor b from tensor a, scaled by t. + 'subtract': lambda a, b, t: (a * t - b * t), +} + +class WAS_ConditioningBlend: + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "conditioning_a": ("CONDITIONING", ), + "conditioning_b": ("CONDITIONING", ), + "blending_mode": (list(blending_modes.keys()), ), + "blending_strength": ("FLOAT", {"default": 0.5, "min": -10.0, "max": 10.0, "step": 0.001}), + "seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}), + } + } + + RETURN_TYPES = ("CONDITIONING",) + RETURN_NAMES = ("conditioning",) + FUNCTION = "combine" + + CATEGORY = "conditioning" + + def combine(self, conditioning_a, conditioning_b, blending_mode, blending_strength, seed): + + if seed > 0: + torch.manual_seed(seed) + + a = conditioning_a[0][0].clone() + b = conditioning_b[0][0].clone() + + pa = conditioning_a[0][1]["pooled_output"].clone() + pb = conditioning_b[0][1]["pooled_output"].clone() + + cond = normalize(blending_modes[blending_mode](a, b, 1 - blending_strength)) + pooled = normalize(blending_modes[blending_mode](pa, pb, 1 - blending_strength)) + + conditioning = [[cond, {"pooled_output": pooled}]] + + return (conditioning, ) + + +NODE_CLASS_MAPPINGS = { + "ConditioningBlend": WAS_ConditioningBlend, +} + +NODE_DISPLAY_NAME_MAPPINGS = { + "ConditioningBlend": "Conditioning (Blend)", +} \ No newline at end of file