190 lines
6.5 KiB
Python
190 lines
6.5 KiB
Python
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)",
|
|
}
|