flatcherlee's picture
Upload 2334 files
3d5837a verified
import itertools
from typing import Optional
import numpy as np
import torch
from PIL import Image, ImageDraw
import math
import cv2
def apply_variation_noise(latent_image, noise_device, variation_seed, variation_strength, mask=None, variation_method='linear'):
latent_size = latent_image.size()
latent_size_1batch = [1, latent_size[1], latent_size[2], latent_size[3]]
if noise_device == "cpu":
variation_generator = torch.manual_seed(variation_seed)
else:
torch.cuda.manual_seed(variation_seed)
variation_generator = None
variation_latent = torch.randn(latent_size_1batch, dtype=latent_image.dtype, layout=latent_image.layout,
generator=variation_generator, device=noise_device)
variation_noise = variation_latent.expand(latent_image.size()[0], -1, -1, -1)
if variation_strength == 0:
return latent_image
elif mask is None:
result = (1 - variation_strength) * latent_image + variation_strength * variation_noise
else:
# this seems precision is not enough when variation_strength is 0.0
mixed_noise = mix_noise(latent_image, variation_noise, variation_strength, variation_method=variation_method)
result = (mask == 1).float() * mixed_noise + (mask == 0).float() * latent_image
return result
# CREDIT: https://github.com/BlenderNeko/ComfyUI_Noise/blob/afb14757216257b12268c91845eac248727a55e2/nodes.py#L68
# https://discuss.pytorch.org/t/help-regarding-slerp-function-for-generative-model-sampling/32475/3
def slerp(val, low, high):
dims = low.shape
low = low.reshape(dims[0], -1)
high = high.reshape(dims[0], -1)
low_norm = low/torch.norm(low, dim=1, keepdim=True)
high_norm = high/torch.norm(high, dim=1, keepdim=True)
low_norm[low_norm != low_norm] = 0.0
high_norm[high_norm != high_norm] = 0.0
omega = torch.acos((low_norm*high_norm).sum(1))
so = torch.sin(omega)
res = (torch.sin((1.0-val)*omega)/so).unsqueeze(1)*low + (torch.sin(val*omega)/so).unsqueeze(1) * high
return res.reshape(dims)
def mix_noise(from_noise, to_noise, strength, variation_method):
to_noise = to_noise.to(from_noise.device)
if variation_method == 'slerp':
mixed_noise = slerp(strength, from_noise, to_noise)
else:
# linear
mixed_noise = (1 - strength) * from_noise + strength * to_noise
# NOTE: Since the variance of the Gaussian noise in mixed_noise has changed, it must be corrected through scaling.
scale_factor = math.sqrt((1 - strength) ** 2 + strength ** 2)
mixed_noise /= scale_factor
return mixed_noise
def prepare_noise(latent_image, seed, noise_inds=None, noise_device="cpu", incremental_seed_mode="comfy", variation_seed=None, variation_strength=None, variation_method="linear"):
"""
creates random noise given a latent image and a seed.
optional arg skip can be used to skip and discard x number of noise generations for a given seed
"""
latent_size = latent_image.size()
latent_size_1batch = [1, latent_size[1], latent_size[2], latent_size[3]]
if variation_strength is not None and variation_strength > 0 or incremental_seed_mode.startswith("variation str inc"):
if noise_device == "cpu":
variation_generator = torch.manual_seed(variation_seed)
else:
torch.cuda.manual_seed(variation_seed)
variation_generator = None
variation_latent = torch.randn(latent_size_1batch, dtype=latent_image.dtype, layout=latent_image.layout,
generator=variation_generator, device=noise_device)
else:
variation_latent = None
def apply_variation(input_latent, strength_up=None):
if variation_latent is None:
return input_latent
else:
strength = variation_strength
if strength_up is not None:
strength += strength_up
variation_noise = variation_latent.expand(input_latent.size()[0], -1, -1, -1)
mixed_noise = mix_noise(input_latent, variation_noise, strength, variation_method)
return mixed_noise
# method: incremental seed batch noise
if noise_inds is None and incremental_seed_mode == "incremental":
batch_cnt = latent_size[0]
latents = None
for i in range(batch_cnt):
if noise_device == "cpu":
generator = torch.manual_seed(seed+i)
else:
torch.cuda.manual_seed(seed+i)
generator = None
latent = torch.randn(latent_size_1batch, dtype=latent_image.dtype, layout=latent_image.layout,
generator=generator, device=noise_device)
latent = apply_variation(latent)
if latents is None:
latents = latent
else:
latents = torch.cat((latents, latent), dim=0)
return latents
# method: incremental variation batch noise
elif noise_inds is None and incremental_seed_mode.startswith("variation str inc"):
batch_cnt = latent_size[0]
latents = None
for i in range(batch_cnt):
if noise_device == "cpu":
generator = torch.manual_seed(seed)
else:
torch.cuda.manual_seed(seed)
generator = None
latent = torch.randn(latent_size_1batch, dtype=latent_image.dtype, layout=latent_image.layout,
generator=generator, device=noise_device)
step = float(incremental_seed_mode[18:])
latent = apply_variation(latent, step*i)
if latents is None:
latents = latent
else:
latents = torch.cat((latents, latent), dim=0)
return latents
# method: comfy batch noise
if noise_device == "cpu":
generator = torch.manual_seed(seed)
else:
torch.cuda.manual_seed(seed)
generator = None
if noise_inds is None:
latents = torch.randn(latent_image.size(), dtype=latent_image.dtype, layout=latent_image.layout,
generator=generator, device=noise_device)
latents = apply_variation(latents)
return latents
unique_inds, inverse = np.unique(noise_inds, return_inverse=True)
noises = []
for i in range(unique_inds[-1] + 1):
noise = torch.randn([1] + list(latent_image.size())[1:], dtype=latent_image.dtype, layout=latent_image.layout,
generator=generator, device=noise_device)
if i in unique_inds:
noises.append(noise)
noises = [noises[i] for i in inverse]
noises = torch.cat(noises, axis=0)
return noises
def pil2tensor(image):
return torch.from_numpy(np.array(image).astype(np.float32) / 255.0).unsqueeze(0)
def empty_pil_tensor(w=64, h=64):
image = Image.new("RGB", (w, h))
draw = ImageDraw.Draw(image)
draw.rectangle((0, 0, w-1, h-1), fill=(0, 0, 0))
return pil2tensor(image)
def try_install_custom_node(custom_node_url, msg):
try:
import cm_global
cm_global.try_call(api='cm.try-install-custom-node',
sender="Inspire Pack", custom_node_url=custom_node_url, msg=msg)
except Exception as e:
print(msg)
print(f"[Inspire Pack] ComfyUI-Manager is outdated. The custom node installation feature is not available.")
def empty_latent():
return torch.zeros([1, 4, 8, 8])
# wildcard trick is taken from pythongossss's
class AnyType(str):
def __ne__(self, __value: object) -> bool:
return False
any_typ = AnyType("*")
# author: Trung0246 --->
class TautologyStr(str):
def __ne__(self, other):
return False
class ByPassTypeTuple(tuple):
def __getitem__(self, index):
if index > 0:
index = 0
item = super().__getitem__(index)
if isinstance(item, str):
return TautologyStr(item)
return item
class TaggedCache:
def __init__(self, tag_settings: Optional[dict]=None):
self._tag_settings = tag_settings or {} # tag cache size
self._data = {}
def __getitem__(self, key):
for tag_data in self._data.values():
if key in tag_data:
return tag_data[key]
raise KeyError(f'Key `{key}` does not exist')
def __setitem__(self, key, value: tuple):
# value: (tag: str, (islist: bool, data: *))
# if key already exists, pop old value
for tag_data in self._data.values():
if key in tag_data:
tag_data.pop(key, None)
break
tag = value[0]
if tag not in self._data:
try:
from cachetools import LRUCache
default_size = 20
if 'ckpt' in tag:
default_size = 5
elif tag in ['latent', 'image']:
default_size = 100
self._data[tag] = LRUCache(maxsize=self._tag_settings.get(tag, default_size))
except (ImportError, ModuleNotFoundError):
# TODO: implement a simple lru dict
self._data[tag] = {}
self._data[tag][key] = value
def __delitem__(self, key):
for tag_data in self._data.values():
if key in tag_data:
del tag_data[key]
return
raise KeyError(f'Key `{key}` does not exist')
def __contains__(self, key):
return any(key in tag_data for tag_data in self._data.values())
def items(self):
yield from itertools.chain(*map(lambda x :x.items(), self._data.values()))
def get(self, key, default=None):
"""D.get(k[,d]) -> D[k] if k in D, else d. d defaults to None."""
for tag_data in self._data.values():
if key in tag_data:
return tag_data[key]
return default
def clear(self):
# clear all cache
self._data = {}
def make_3d_mask(mask):
if len(mask.shape) == 4:
return mask.squeeze(0)
elif len(mask.shape) == 2:
return mask.unsqueeze(0)
return mask
def dilate_mask(mask: torch.Tensor, dilation_factor: float) -> torch.Tensor:
"""Dilate a mask using a square kernel with a given dilation factor."""
kernel_size = int(dilation_factor * 2) + 1
kernel = np.ones((abs(kernel_size), abs(kernel_size)), np.uint8)
masks = make_3d_mask(mask).numpy()
dilated_masks = []
for m in masks:
if dilation_factor > 0:
m2 = cv2.dilate(m, kernel, iterations=1)
else:
m2 = cv2.erode(m, kernel, iterations=1)
dilated_masks.append(torch.from_numpy(m2))
return torch.stack(dilated_masks)
def flatten_non_zero_override(masks: torch.Tensor):
"""
flatten multiple layer mask tensor to 1 layer mask tensor.
Override the lower layer with the tensor from the upper layer, but only override non-zero values.
:param masks: 3d mask
:return: flatten mask
"""
final_mask = masks[0]
for i in range(1, masks.size(0)):
non_zero_mask = masks[i] != 0
final_mask[non_zero_mask] = masks[i][non_zero_mask]
return final_mask