Delete comfy_extras
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- comfy_extras/chainner_models/model_loading.py +0 -6
- comfy_extras/nodes_ace.py +0 -49
- comfy_extras/nodes_advanced_samplers.py +0 -111
- comfy_extras/nodes_align_your_steps.py +0 -53
- comfy_extras/nodes_apg.py +0 -76
- comfy_extras/nodes_attention_multiply.py +0 -120
- comfy_extras/nodes_audio.py +0 -334
- comfy_extras/nodes_camera_trajectory.py +0 -218
- comfy_extras/nodes_canny.py +0 -25
- comfy_extras/nodes_cfg.py +0 -72
- comfy_extras/nodes_clip_sdxl.py +0 -54
- comfy_extras/nodes_compositing.py +0 -214
- comfy_extras/nodes_cond.py +0 -49
- comfy_extras/nodes_controlnet.py +0 -60
- comfy_extras/nodes_cosmos.py +0 -128
- comfy_extras/nodes_custom_sampler.py +0 -932
- comfy_extras/nodes_differential_diffusion.py +0 -42
- comfy_extras/nodes_edit_model.py +0 -26
- comfy_extras/nodes_flux.py +0 -108
- comfy_extras/nodes_freelunch.py +0 -113
- comfy_extras/nodes_fresca.py +0 -103
- comfy_extras/nodes_gits.py +0 -369
- comfy_extras/nodes_hidream.py +0 -55
- comfy_extras/nodes_hooks.py +0 -745
- comfy_extras/nodes_hunyuan.py +0 -123
- comfy_extras/nodes_hunyuan3d.py +0 -634
- comfy_extras/nodes_hypernetwork.py +0 -120
- comfy_extras/nodes_hypertile.py +0 -81
- comfy_extras/nodes_images.py +0 -642
- comfy_extras/nodes_ip2p.py +0 -45
- comfy_extras/nodes_latent.py +0 -288
- comfy_extras/nodes_load_3d.py +0 -182
- comfy_extras/nodes_lora_extract.py +0 -119
- comfy_extras/nodes_lotus.py +0 -29
- comfy_extras/nodes_lt.py +0 -474
- comfy_extras/nodes_lumina2.py +0 -104
- comfy_extras/nodes_mahiro.py +0 -41
- comfy_extras/nodes_mask.py +0 -412
- comfy_extras/nodes_mochi.py +0 -23
- comfy_extras/nodes_model_advanced.py +0 -329
- comfy_extras/nodes_model_downscale.py +0 -53
- comfy_extras/nodes_model_merging.py +0 -374
- comfy_extras/nodes_model_merging_model_specific.py +0 -332
- comfy_extras/nodes_morphology.py +0 -87
- comfy_extras/nodes_optimalsteps.py +0 -57
- comfy_extras/nodes_pag.py +0 -56
- comfy_extras/nodes_perpneg.py +0 -146
- comfy_extras/nodes_photomaker.py +0 -188
- comfy_extras/nodes_pixart.py +0 -24
- comfy_extras/nodes_post_processing.py +0 -281
comfy_extras/chainner_models/model_loading.py
DELETED
|
@@ -1,6 +0,0 @@
|
|
| 1 |
-
import logging
|
| 2 |
-
from spandrel import ModelLoader
|
| 3 |
-
|
| 4 |
-
def load_state_dict(state_dict):
|
| 5 |
-
logging.warning("comfy_extras.chainner_models is deprecated and has been replaced by the spandrel library.")
|
| 6 |
-
return ModelLoader().load_from_state_dict(state_dict).eval()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
comfy_extras/nodes_ace.py
DELETED
|
@@ -1,49 +0,0 @@
|
|
| 1 |
-
import torch
|
| 2 |
-
import comfy.model_management
|
| 3 |
-
import node_helpers
|
| 4 |
-
|
| 5 |
-
class TextEncodeAceStepAudio:
|
| 6 |
-
@classmethod
|
| 7 |
-
def INPUT_TYPES(s):
|
| 8 |
-
return {"required": {
|
| 9 |
-
"clip": ("CLIP", ),
|
| 10 |
-
"tags": ("STRING", {"multiline": True, "dynamicPrompts": True}),
|
| 11 |
-
"lyrics": ("STRING", {"multiline": True, "dynamicPrompts": True}),
|
| 12 |
-
"lyrics_strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.01}),
|
| 13 |
-
}}
|
| 14 |
-
RETURN_TYPES = ("CONDITIONING",)
|
| 15 |
-
FUNCTION = "encode"
|
| 16 |
-
|
| 17 |
-
CATEGORY = "conditioning"
|
| 18 |
-
|
| 19 |
-
def encode(self, clip, tags, lyrics, lyrics_strength):
|
| 20 |
-
tokens = clip.tokenize(tags, lyrics=lyrics)
|
| 21 |
-
conditioning = clip.encode_from_tokens_scheduled(tokens)
|
| 22 |
-
conditioning = node_helpers.conditioning_set_values(conditioning, {"lyrics_strength": lyrics_strength})
|
| 23 |
-
return (conditioning, )
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
class EmptyAceStepLatentAudio:
|
| 27 |
-
def __init__(self):
|
| 28 |
-
self.device = comfy.model_management.intermediate_device()
|
| 29 |
-
|
| 30 |
-
@classmethod
|
| 31 |
-
def INPUT_TYPES(s):
|
| 32 |
-
return {"required": {"seconds": ("FLOAT", {"default": 120.0, "min": 1.0, "max": 1000.0, "step": 0.1}),
|
| 33 |
-
"batch_size": ("INT", {"default": 1, "min": 1, "max": 4096, "tooltip": "The number of latent images in the batch."}),
|
| 34 |
-
}}
|
| 35 |
-
RETURN_TYPES = ("LATENT",)
|
| 36 |
-
FUNCTION = "generate"
|
| 37 |
-
|
| 38 |
-
CATEGORY = "latent/audio"
|
| 39 |
-
|
| 40 |
-
def generate(self, seconds, batch_size):
|
| 41 |
-
length = int(seconds * 44100 / 512 / 8)
|
| 42 |
-
latent = torch.zeros([batch_size, 8, 16, length], device=self.device)
|
| 43 |
-
return ({"samples": latent, "type": "audio"}, )
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
NODE_CLASS_MAPPINGS = {
|
| 47 |
-
"TextEncodeAceStepAudio": TextEncodeAceStepAudio,
|
| 48 |
-
"EmptyAceStepLatentAudio": EmptyAceStepLatentAudio,
|
| 49 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
comfy_extras/nodes_advanced_samplers.py
DELETED
|
@@ -1,111 +0,0 @@
|
|
| 1 |
-
import comfy.samplers
|
| 2 |
-
import comfy.utils
|
| 3 |
-
import torch
|
| 4 |
-
import numpy as np
|
| 5 |
-
from tqdm.auto import trange
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
@torch.no_grad()
|
| 9 |
-
def sample_lcm_upscale(model, x, sigmas, extra_args=None, callback=None, disable=None, total_upscale=2.0, upscale_method="bislerp", upscale_steps=None):
|
| 10 |
-
extra_args = {} if extra_args is None else extra_args
|
| 11 |
-
|
| 12 |
-
if upscale_steps is None:
|
| 13 |
-
upscale_steps = max(len(sigmas) // 2 + 1, 2)
|
| 14 |
-
else:
|
| 15 |
-
upscale_steps += 1
|
| 16 |
-
upscale_steps = min(upscale_steps, len(sigmas) + 1)
|
| 17 |
-
|
| 18 |
-
upscales = np.linspace(1.0, total_upscale, upscale_steps)[1:]
|
| 19 |
-
|
| 20 |
-
orig_shape = x.size()
|
| 21 |
-
s_in = x.new_ones([x.shape[0]])
|
| 22 |
-
for i in trange(len(sigmas) - 1, disable=disable):
|
| 23 |
-
denoised = model(x, sigmas[i] * s_in, **extra_args)
|
| 24 |
-
if callback is not None:
|
| 25 |
-
callback({'x': x, 'i': i, 'sigma': sigmas[i], 'sigma_hat': sigmas[i], 'denoised': denoised})
|
| 26 |
-
|
| 27 |
-
x = denoised
|
| 28 |
-
if i < len(upscales):
|
| 29 |
-
x = comfy.utils.common_upscale(x, round(orig_shape[-1] * upscales[i]), round(orig_shape[-2] * upscales[i]), upscale_method, "disabled")
|
| 30 |
-
|
| 31 |
-
if sigmas[i + 1] > 0:
|
| 32 |
-
x += sigmas[i + 1] * torch.randn_like(x)
|
| 33 |
-
return x
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
class SamplerLCMUpscale:
|
| 37 |
-
upscale_methods = ["bislerp", "nearest-exact", "bilinear", "area", "bicubic"]
|
| 38 |
-
|
| 39 |
-
@classmethod
|
| 40 |
-
def INPUT_TYPES(s):
|
| 41 |
-
return {"required":
|
| 42 |
-
{"scale_ratio": ("FLOAT", {"default": 1.0, "min": 0.1, "max": 20.0, "step": 0.01}),
|
| 43 |
-
"scale_steps": ("INT", {"default": -1, "min": -1, "max": 1000, "step": 1}),
|
| 44 |
-
"upscale_method": (s.upscale_methods,),
|
| 45 |
-
}
|
| 46 |
-
}
|
| 47 |
-
RETURN_TYPES = ("SAMPLER",)
|
| 48 |
-
CATEGORY = "sampling/custom_sampling/samplers"
|
| 49 |
-
|
| 50 |
-
FUNCTION = "get_sampler"
|
| 51 |
-
|
| 52 |
-
def get_sampler(self, scale_ratio, scale_steps, upscale_method):
|
| 53 |
-
if scale_steps < 0:
|
| 54 |
-
scale_steps = None
|
| 55 |
-
sampler = comfy.samplers.KSAMPLER(sample_lcm_upscale, extra_options={"total_upscale": scale_ratio, "upscale_steps": scale_steps, "upscale_method": upscale_method})
|
| 56 |
-
return (sampler, )
|
| 57 |
-
|
| 58 |
-
from comfy.k_diffusion.sampling import to_d
|
| 59 |
-
import comfy.model_patcher
|
| 60 |
-
|
| 61 |
-
@torch.no_grad()
|
| 62 |
-
def sample_euler_pp(model, x, sigmas, extra_args=None, callback=None, disable=None):
|
| 63 |
-
extra_args = {} if extra_args is None else extra_args
|
| 64 |
-
|
| 65 |
-
temp = [0]
|
| 66 |
-
def post_cfg_function(args):
|
| 67 |
-
temp[0] = args["uncond_denoised"]
|
| 68 |
-
return args["denoised"]
|
| 69 |
-
|
| 70 |
-
model_options = extra_args.get("model_options", {}).copy()
|
| 71 |
-
extra_args["model_options"] = comfy.model_patcher.set_model_options_post_cfg_function(model_options, post_cfg_function, disable_cfg1_optimization=True)
|
| 72 |
-
|
| 73 |
-
s_in = x.new_ones([x.shape[0]])
|
| 74 |
-
for i in trange(len(sigmas) - 1, disable=disable):
|
| 75 |
-
sigma_hat = sigmas[i]
|
| 76 |
-
denoised = model(x, sigma_hat * s_in, **extra_args)
|
| 77 |
-
d = to_d(x - denoised + temp[0], sigmas[i], denoised)
|
| 78 |
-
if callback is not None:
|
| 79 |
-
callback({'x': x, 'i': i, 'sigma': sigmas[i], 'sigma_hat': sigma_hat, 'denoised': denoised})
|
| 80 |
-
dt = sigmas[i + 1] - sigma_hat
|
| 81 |
-
x = x + d * dt
|
| 82 |
-
return x
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
class SamplerEulerCFGpp:
|
| 86 |
-
@classmethod
|
| 87 |
-
def INPUT_TYPES(s):
|
| 88 |
-
return {"required":
|
| 89 |
-
{"version": (["regular", "alternative"],),}
|
| 90 |
-
}
|
| 91 |
-
RETURN_TYPES = ("SAMPLER",)
|
| 92 |
-
# CATEGORY = "sampling/custom_sampling/samplers"
|
| 93 |
-
CATEGORY = "_for_testing"
|
| 94 |
-
|
| 95 |
-
FUNCTION = "get_sampler"
|
| 96 |
-
|
| 97 |
-
def get_sampler(self, version):
|
| 98 |
-
if version == "alternative":
|
| 99 |
-
sampler = comfy.samplers.KSAMPLER(sample_euler_pp)
|
| 100 |
-
else:
|
| 101 |
-
sampler = comfy.samplers.ksampler("euler_cfg_pp")
|
| 102 |
-
return (sampler, )
|
| 103 |
-
|
| 104 |
-
NODE_CLASS_MAPPINGS = {
|
| 105 |
-
"SamplerLCMUpscale": SamplerLCMUpscale,
|
| 106 |
-
"SamplerEulerCFGpp": SamplerEulerCFGpp,
|
| 107 |
-
}
|
| 108 |
-
|
| 109 |
-
NODE_DISPLAY_NAME_MAPPINGS = {
|
| 110 |
-
"SamplerEulerCFGpp": "SamplerEulerCFG++",
|
| 111 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
comfy_extras/nodes_align_your_steps.py
DELETED
|
@@ -1,53 +0,0 @@
|
|
| 1 |
-
#from: https://research.nvidia.com/labs/toronto-ai/AlignYourSteps/howto.html
|
| 2 |
-
import numpy as np
|
| 3 |
-
import torch
|
| 4 |
-
|
| 5 |
-
def loglinear_interp(t_steps, num_steps):
|
| 6 |
-
"""
|
| 7 |
-
Performs log-linear interpolation of a given array of decreasing numbers.
|
| 8 |
-
"""
|
| 9 |
-
xs = np.linspace(0, 1, len(t_steps))
|
| 10 |
-
ys = np.log(t_steps[::-1])
|
| 11 |
-
|
| 12 |
-
new_xs = np.linspace(0, 1, num_steps)
|
| 13 |
-
new_ys = np.interp(new_xs, xs, ys)
|
| 14 |
-
|
| 15 |
-
interped_ys = np.exp(new_ys)[::-1].copy()
|
| 16 |
-
return interped_ys
|
| 17 |
-
|
| 18 |
-
NOISE_LEVELS = {"SD1": [14.6146412293, 6.4745760956, 3.8636745985, 2.6946151520, 1.8841921177, 1.3943805092, 0.9642583904, 0.6523686016, 0.3977456272, 0.1515232662, 0.0291671582],
|
| 19 |
-
"SDXL":[14.6146412293, 6.3184485287, 3.7681790315, 2.1811480769, 1.3405244945, 0.8620721141, 0.5550693289, 0.3798540708, 0.2332364134, 0.1114188177, 0.0291671582],
|
| 20 |
-
"SVD": [700.00, 54.5, 15.886, 7.977, 4.248, 1.789, 0.981, 0.403, 0.173, 0.034, 0.002]}
|
| 21 |
-
|
| 22 |
-
class AlignYourStepsScheduler:
|
| 23 |
-
@classmethod
|
| 24 |
-
def INPUT_TYPES(s):
|
| 25 |
-
return {"required":
|
| 26 |
-
{"model_type": (["SD1", "SDXL", "SVD"], ),
|
| 27 |
-
"steps": ("INT", {"default": 10, "min": 1, "max": 10000}),
|
| 28 |
-
"denoise": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}),
|
| 29 |
-
}
|
| 30 |
-
}
|
| 31 |
-
RETURN_TYPES = ("SIGMAS",)
|
| 32 |
-
CATEGORY = "sampling/custom_sampling/schedulers"
|
| 33 |
-
|
| 34 |
-
FUNCTION = "get_sigmas"
|
| 35 |
-
|
| 36 |
-
def get_sigmas(self, model_type, steps, denoise):
|
| 37 |
-
total_steps = steps
|
| 38 |
-
if denoise < 1.0:
|
| 39 |
-
if denoise <= 0.0:
|
| 40 |
-
return (torch.FloatTensor([]),)
|
| 41 |
-
total_steps = round(steps * denoise)
|
| 42 |
-
|
| 43 |
-
sigmas = NOISE_LEVELS[model_type][:]
|
| 44 |
-
if (steps + 1) != len(sigmas):
|
| 45 |
-
sigmas = loglinear_interp(sigmas, steps + 1)
|
| 46 |
-
|
| 47 |
-
sigmas = sigmas[-(total_steps + 1):]
|
| 48 |
-
sigmas[-1] = 0
|
| 49 |
-
return (torch.FloatTensor(sigmas), )
|
| 50 |
-
|
| 51 |
-
NODE_CLASS_MAPPINGS = {
|
| 52 |
-
"AlignYourStepsScheduler": AlignYourStepsScheduler,
|
| 53 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
comfy_extras/nodes_apg.py
DELETED
|
@@ -1,76 +0,0 @@
|
|
| 1 |
-
import torch
|
| 2 |
-
|
| 3 |
-
def project(v0, v1):
|
| 4 |
-
v1 = torch.nn.functional.normalize(v1, dim=[-1, -2, -3])
|
| 5 |
-
v0_parallel = (v0 * v1).sum(dim=[-1, -2, -3], keepdim=True) * v1
|
| 6 |
-
v0_orthogonal = v0 - v0_parallel
|
| 7 |
-
return v0_parallel, v0_orthogonal
|
| 8 |
-
|
| 9 |
-
class APG:
|
| 10 |
-
@classmethod
|
| 11 |
-
def INPUT_TYPES(s):
|
| 12 |
-
return {
|
| 13 |
-
"required": {
|
| 14 |
-
"model": ("MODEL",),
|
| 15 |
-
"eta": ("FLOAT", {"default": 1.0, "min": -10.0, "max": 10.0, "step": 0.01, "tooltip": "Controls the scale of the parallel guidance vector. Default CFG behavior at a setting of 1."}),
|
| 16 |
-
"norm_threshold": ("FLOAT", {"default": 5.0, "min": 0.0, "max": 50.0, "step": 0.1, "tooltip": "Normalize guidance vector to this value, normalization disable at a setting of 0."}),
|
| 17 |
-
"momentum": ("FLOAT", {"default": 0.0, "min": -5.0, "max": 1.0, "step": 0.01, "tooltip":"Controls a running average of guidance during diffusion, disabled at a setting of 0."}),
|
| 18 |
-
}
|
| 19 |
-
}
|
| 20 |
-
RETURN_TYPES = ("MODEL",)
|
| 21 |
-
FUNCTION = "patch"
|
| 22 |
-
CATEGORY = "sampling/custom_sampling"
|
| 23 |
-
|
| 24 |
-
def patch(self, model, eta, norm_threshold, momentum):
|
| 25 |
-
running_avg = 0
|
| 26 |
-
prev_sigma = None
|
| 27 |
-
|
| 28 |
-
def pre_cfg_function(args):
|
| 29 |
-
nonlocal running_avg, prev_sigma
|
| 30 |
-
|
| 31 |
-
if len(args["conds_out"]) == 1: return args["conds_out"]
|
| 32 |
-
|
| 33 |
-
cond = args["conds_out"][0]
|
| 34 |
-
uncond = args["conds_out"][1]
|
| 35 |
-
sigma = args["sigma"][0]
|
| 36 |
-
cond_scale = args["cond_scale"]
|
| 37 |
-
|
| 38 |
-
if prev_sigma is not None and sigma > prev_sigma:
|
| 39 |
-
running_avg = 0
|
| 40 |
-
prev_sigma = sigma
|
| 41 |
-
|
| 42 |
-
guidance = cond - uncond
|
| 43 |
-
|
| 44 |
-
if momentum != 0:
|
| 45 |
-
if not torch.is_tensor(running_avg):
|
| 46 |
-
running_avg = guidance
|
| 47 |
-
else:
|
| 48 |
-
running_avg = momentum * running_avg + guidance
|
| 49 |
-
guidance = running_avg
|
| 50 |
-
|
| 51 |
-
if norm_threshold > 0:
|
| 52 |
-
guidance_norm = guidance.norm(p=2, dim=[-1, -2, -3], keepdim=True)
|
| 53 |
-
scale = torch.minimum(
|
| 54 |
-
torch.ones_like(guidance_norm),
|
| 55 |
-
norm_threshold / guidance_norm
|
| 56 |
-
)
|
| 57 |
-
guidance = guidance * scale
|
| 58 |
-
|
| 59 |
-
guidance_parallel, guidance_orthogonal = project(guidance, cond)
|
| 60 |
-
modified_guidance = guidance_orthogonal + eta * guidance_parallel
|
| 61 |
-
|
| 62 |
-
modified_cond = (uncond + modified_guidance) + (cond - uncond) / cond_scale
|
| 63 |
-
|
| 64 |
-
return [modified_cond, uncond] + args["conds_out"][2:]
|
| 65 |
-
|
| 66 |
-
m = model.clone()
|
| 67 |
-
m.set_model_sampler_pre_cfg_function(pre_cfg_function)
|
| 68 |
-
return (m,)
|
| 69 |
-
|
| 70 |
-
NODE_CLASS_MAPPINGS = {
|
| 71 |
-
"APG": APG,
|
| 72 |
-
}
|
| 73 |
-
|
| 74 |
-
NODE_DISPLAY_NAME_MAPPINGS = {
|
| 75 |
-
"APG": "Adaptive Projected Guidance",
|
| 76 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
comfy_extras/nodes_attention_multiply.py
DELETED
|
@@ -1,120 +0,0 @@
|
|
| 1 |
-
|
| 2 |
-
def attention_multiply(attn, model, q, k, v, out):
|
| 3 |
-
m = model.clone()
|
| 4 |
-
sd = model.model_state_dict()
|
| 5 |
-
|
| 6 |
-
for key in sd:
|
| 7 |
-
if key.endswith("{}.to_q.bias".format(attn)) or key.endswith("{}.to_q.weight".format(attn)):
|
| 8 |
-
m.add_patches({key: (None,)}, 0.0, q)
|
| 9 |
-
if key.endswith("{}.to_k.bias".format(attn)) or key.endswith("{}.to_k.weight".format(attn)):
|
| 10 |
-
m.add_patches({key: (None,)}, 0.0, k)
|
| 11 |
-
if key.endswith("{}.to_v.bias".format(attn)) or key.endswith("{}.to_v.weight".format(attn)):
|
| 12 |
-
m.add_patches({key: (None,)}, 0.0, v)
|
| 13 |
-
if key.endswith("{}.to_out.0.bias".format(attn)) or key.endswith("{}.to_out.0.weight".format(attn)):
|
| 14 |
-
m.add_patches({key: (None,)}, 0.0, out)
|
| 15 |
-
|
| 16 |
-
return m
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
class UNetSelfAttentionMultiply:
|
| 20 |
-
@classmethod
|
| 21 |
-
def INPUT_TYPES(s):
|
| 22 |
-
return {"required": { "model": ("MODEL",),
|
| 23 |
-
"q": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.01}),
|
| 24 |
-
"k": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.01}),
|
| 25 |
-
"v": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.01}),
|
| 26 |
-
"out": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.01}),
|
| 27 |
-
}}
|
| 28 |
-
RETURN_TYPES = ("MODEL",)
|
| 29 |
-
FUNCTION = "patch"
|
| 30 |
-
|
| 31 |
-
CATEGORY = "_for_testing/attention_experiments"
|
| 32 |
-
|
| 33 |
-
def patch(self, model, q, k, v, out):
|
| 34 |
-
m = attention_multiply("attn1", model, q, k, v, out)
|
| 35 |
-
return (m, )
|
| 36 |
-
|
| 37 |
-
class UNetCrossAttentionMultiply:
|
| 38 |
-
@classmethod
|
| 39 |
-
def INPUT_TYPES(s):
|
| 40 |
-
return {"required": { "model": ("MODEL",),
|
| 41 |
-
"q": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.01}),
|
| 42 |
-
"k": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.01}),
|
| 43 |
-
"v": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.01}),
|
| 44 |
-
"out": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.01}),
|
| 45 |
-
}}
|
| 46 |
-
RETURN_TYPES = ("MODEL",)
|
| 47 |
-
FUNCTION = "patch"
|
| 48 |
-
|
| 49 |
-
CATEGORY = "_for_testing/attention_experiments"
|
| 50 |
-
|
| 51 |
-
def patch(self, model, q, k, v, out):
|
| 52 |
-
m = attention_multiply("attn2", model, q, k, v, out)
|
| 53 |
-
return (m, )
|
| 54 |
-
|
| 55 |
-
class CLIPAttentionMultiply:
|
| 56 |
-
@classmethod
|
| 57 |
-
def INPUT_TYPES(s):
|
| 58 |
-
return {"required": { "clip": ("CLIP",),
|
| 59 |
-
"q": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.01}),
|
| 60 |
-
"k": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.01}),
|
| 61 |
-
"v": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.01}),
|
| 62 |
-
"out": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.01}),
|
| 63 |
-
}}
|
| 64 |
-
RETURN_TYPES = ("CLIP",)
|
| 65 |
-
FUNCTION = "patch"
|
| 66 |
-
|
| 67 |
-
CATEGORY = "_for_testing/attention_experiments"
|
| 68 |
-
|
| 69 |
-
def patch(self, clip, q, k, v, out):
|
| 70 |
-
m = clip.clone()
|
| 71 |
-
sd = m.patcher.model_state_dict()
|
| 72 |
-
|
| 73 |
-
for key in sd:
|
| 74 |
-
if key.endswith("self_attn.q_proj.weight") or key.endswith("self_attn.q_proj.bias"):
|
| 75 |
-
m.add_patches({key: (None,)}, 0.0, q)
|
| 76 |
-
if key.endswith("self_attn.k_proj.weight") or key.endswith("self_attn.k_proj.bias"):
|
| 77 |
-
m.add_patches({key: (None,)}, 0.0, k)
|
| 78 |
-
if key.endswith("self_attn.v_proj.weight") or key.endswith("self_attn.v_proj.bias"):
|
| 79 |
-
m.add_patches({key: (None,)}, 0.0, v)
|
| 80 |
-
if key.endswith("self_attn.out_proj.weight") or key.endswith("self_attn.out_proj.bias"):
|
| 81 |
-
m.add_patches({key: (None,)}, 0.0, out)
|
| 82 |
-
return (m, )
|
| 83 |
-
|
| 84 |
-
class UNetTemporalAttentionMultiply:
|
| 85 |
-
@classmethod
|
| 86 |
-
def INPUT_TYPES(s):
|
| 87 |
-
return {"required": { "model": ("MODEL",),
|
| 88 |
-
"self_structural": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.01}),
|
| 89 |
-
"self_temporal": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.01}),
|
| 90 |
-
"cross_structural": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.01}),
|
| 91 |
-
"cross_temporal": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.01}),
|
| 92 |
-
}}
|
| 93 |
-
RETURN_TYPES = ("MODEL",)
|
| 94 |
-
FUNCTION = "patch"
|
| 95 |
-
|
| 96 |
-
CATEGORY = "_for_testing/attention_experiments"
|
| 97 |
-
|
| 98 |
-
def patch(self, model, self_structural, self_temporal, cross_structural, cross_temporal):
|
| 99 |
-
m = model.clone()
|
| 100 |
-
sd = model.model_state_dict()
|
| 101 |
-
|
| 102 |
-
for k in sd:
|
| 103 |
-
if (k.endswith("attn1.to_out.0.bias") or k.endswith("attn1.to_out.0.weight")):
|
| 104 |
-
if '.time_stack.' in k:
|
| 105 |
-
m.add_patches({k: (None,)}, 0.0, self_temporal)
|
| 106 |
-
else:
|
| 107 |
-
m.add_patches({k: (None,)}, 0.0, self_structural)
|
| 108 |
-
elif (k.endswith("attn2.to_out.0.bias") or k.endswith("attn2.to_out.0.weight")):
|
| 109 |
-
if '.time_stack.' in k:
|
| 110 |
-
m.add_patches({k: (None,)}, 0.0, cross_temporal)
|
| 111 |
-
else:
|
| 112 |
-
m.add_patches({k: (None,)}, 0.0, cross_structural)
|
| 113 |
-
return (m, )
|
| 114 |
-
|
| 115 |
-
NODE_CLASS_MAPPINGS = {
|
| 116 |
-
"UNetSelfAttentionMultiply": UNetSelfAttentionMultiply,
|
| 117 |
-
"UNetCrossAttentionMultiply": UNetCrossAttentionMultiply,
|
| 118 |
-
"CLIPAttentionMultiply": CLIPAttentionMultiply,
|
| 119 |
-
"UNetTemporalAttentionMultiply": UNetTemporalAttentionMultiply,
|
| 120 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
comfy_extras/nodes_audio.py
DELETED
|
@@ -1,334 +0,0 @@
|
|
| 1 |
-
from __future__ import annotations
|
| 2 |
-
|
| 3 |
-
import av
|
| 4 |
-
import torchaudio
|
| 5 |
-
import torch
|
| 6 |
-
import comfy.model_management
|
| 7 |
-
import folder_paths
|
| 8 |
-
import os
|
| 9 |
-
import io
|
| 10 |
-
import json
|
| 11 |
-
import random
|
| 12 |
-
import hashlib
|
| 13 |
-
import node_helpers
|
| 14 |
-
from comfy.cli_args import args
|
| 15 |
-
from comfy.comfy_types import FileLocator
|
| 16 |
-
|
| 17 |
-
class EmptyLatentAudio:
|
| 18 |
-
def __init__(self):
|
| 19 |
-
self.device = comfy.model_management.intermediate_device()
|
| 20 |
-
|
| 21 |
-
@classmethod
|
| 22 |
-
def INPUT_TYPES(s):
|
| 23 |
-
return {"required": {"seconds": ("FLOAT", {"default": 47.6, "min": 1.0, "max": 1000.0, "step": 0.1}),
|
| 24 |
-
"batch_size": ("INT", {"default": 1, "min": 1, "max": 4096, "tooltip": "The number of latent images in the batch."}),
|
| 25 |
-
}}
|
| 26 |
-
RETURN_TYPES = ("LATENT",)
|
| 27 |
-
FUNCTION = "generate"
|
| 28 |
-
|
| 29 |
-
CATEGORY = "latent/audio"
|
| 30 |
-
|
| 31 |
-
def generate(self, seconds, batch_size):
|
| 32 |
-
length = round((seconds * 44100 / 2048) / 2) * 2
|
| 33 |
-
latent = torch.zeros([batch_size, 64, length], device=self.device)
|
| 34 |
-
return ({"samples":latent, "type": "audio"}, )
|
| 35 |
-
|
| 36 |
-
class ConditioningStableAudio:
|
| 37 |
-
@classmethod
|
| 38 |
-
def INPUT_TYPES(s):
|
| 39 |
-
return {"required": {"positive": ("CONDITIONING", ),
|
| 40 |
-
"negative": ("CONDITIONING", ),
|
| 41 |
-
"seconds_start": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1000.0, "step": 0.1}),
|
| 42 |
-
"seconds_total": ("FLOAT", {"default": 47.0, "min": 0.0, "max": 1000.0, "step": 0.1}),
|
| 43 |
-
}}
|
| 44 |
-
|
| 45 |
-
RETURN_TYPES = ("CONDITIONING","CONDITIONING")
|
| 46 |
-
RETURN_NAMES = ("positive", "negative")
|
| 47 |
-
|
| 48 |
-
FUNCTION = "append"
|
| 49 |
-
|
| 50 |
-
CATEGORY = "conditioning"
|
| 51 |
-
|
| 52 |
-
def append(self, positive, negative, seconds_start, seconds_total):
|
| 53 |
-
positive = node_helpers.conditioning_set_values(positive, {"seconds_start": seconds_start, "seconds_total": seconds_total})
|
| 54 |
-
negative = node_helpers.conditioning_set_values(negative, {"seconds_start": seconds_start, "seconds_total": seconds_total})
|
| 55 |
-
return (positive, negative)
|
| 56 |
-
|
| 57 |
-
class VAEEncodeAudio:
|
| 58 |
-
@classmethod
|
| 59 |
-
def INPUT_TYPES(s):
|
| 60 |
-
return {"required": { "audio": ("AUDIO", ), "vae": ("VAE", )}}
|
| 61 |
-
RETURN_TYPES = ("LATENT",)
|
| 62 |
-
FUNCTION = "encode"
|
| 63 |
-
|
| 64 |
-
CATEGORY = "latent/audio"
|
| 65 |
-
|
| 66 |
-
def encode(self, vae, audio):
|
| 67 |
-
sample_rate = audio["sample_rate"]
|
| 68 |
-
if 44100 != sample_rate:
|
| 69 |
-
waveform = torchaudio.functional.resample(audio["waveform"], sample_rate, 44100)
|
| 70 |
-
else:
|
| 71 |
-
waveform = audio["waveform"]
|
| 72 |
-
|
| 73 |
-
t = vae.encode(waveform.movedim(1, -1))
|
| 74 |
-
return ({"samples":t}, )
|
| 75 |
-
|
| 76 |
-
class VAEDecodeAudio:
|
| 77 |
-
@classmethod
|
| 78 |
-
def INPUT_TYPES(s):
|
| 79 |
-
return {"required": { "samples": ("LATENT", ), "vae": ("VAE", )}}
|
| 80 |
-
RETURN_TYPES = ("AUDIO",)
|
| 81 |
-
FUNCTION = "decode"
|
| 82 |
-
|
| 83 |
-
CATEGORY = "latent/audio"
|
| 84 |
-
|
| 85 |
-
def decode(self, vae, samples):
|
| 86 |
-
audio = vae.decode(samples["samples"]).movedim(-1, 1)
|
| 87 |
-
std = torch.std(audio, dim=[1,2], keepdim=True) * 5.0
|
| 88 |
-
std[std < 1.0] = 1.0
|
| 89 |
-
audio /= std
|
| 90 |
-
return ({"waveform": audio, "sample_rate": 44100}, )
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
def save_audio(self, audio, filename_prefix="ComfyUI", format="flac", prompt=None, extra_pnginfo=None, quality="128k"):
|
| 94 |
-
|
| 95 |
-
filename_prefix += self.prefix_append
|
| 96 |
-
full_output_folder, filename, counter, subfolder, filename_prefix = folder_paths.get_save_image_path(filename_prefix, self.output_dir)
|
| 97 |
-
results: list[FileLocator] = []
|
| 98 |
-
|
| 99 |
-
# Prepare metadata dictionary
|
| 100 |
-
metadata = {}
|
| 101 |
-
if not args.disable_metadata:
|
| 102 |
-
if prompt is not None:
|
| 103 |
-
metadata["prompt"] = json.dumps(prompt)
|
| 104 |
-
if extra_pnginfo is not None:
|
| 105 |
-
for x in extra_pnginfo:
|
| 106 |
-
metadata[x] = json.dumps(extra_pnginfo[x])
|
| 107 |
-
|
| 108 |
-
# Opus supported sample rates
|
| 109 |
-
OPUS_RATES = [8000, 12000, 16000, 24000, 48000]
|
| 110 |
-
|
| 111 |
-
for (batch_number, waveform) in enumerate(audio["waveform"].cpu()):
|
| 112 |
-
filename_with_batch_num = filename.replace("%batch_num%", str(batch_number))
|
| 113 |
-
file = f"{filename_with_batch_num}_{counter:05}_.{format}"
|
| 114 |
-
output_path = os.path.join(full_output_folder, file)
|
| 115 |
-
|
| 116 |
-
# Use original sample rate initially
|
| 117 |
-
sample_rate = audio["sample_rate"]
|
| 118 |
-
|
| 119 |
-
# Handle Opus sample rate requirements
|
| 120 |
-
if format == "opus":
|
| 121 |
-
if sample_rate > 48000:
|
| 122 |
-
sample_rate = 48000
|
| 123 |
-
elif sample_rate not in OPUS_RATES:
|
| 124 |
-
# Find the next highest supported rate
|
| 125 |
-
for rate in sorted(OPUS_RATES):
|
| 126 |
-
if rate > sample_rate:
|
| 127 |
-
sample_rate = rate
|
| 128 |
-
break
|
| 129 |
-
if sample_rate not in OPUS_RATES: # Fallback if still not supported
|
| 130 |
-
sample_rate = 48000
|
| 131 |
-
|
| 132 |
-
# Resample if necessary
|
| 133 |
-
if sample_rate != audio["sample_rate"]:
|
| 134 |
-
waveform = torchaudio.functional.resample(waveform, audio["sample_rate"], sample_rate)
|
| 135 |
-
|
| 136 |
-
# Create output with specified format
|
| 137 |
-
output_buffer = io.BytesIO()
|
| 138 |
-
output_container = av.open(output_buffer, mode='w', format=format)
|
| 139 |
-
|
| 140 |
-
# Set metadata on the container
|
| 141 |
-
for key, value in metadata.items():
|
| 142 |
-
output_container.metadata[key] = value
|
| 143 |
-
|
| 144 |
-
# Set up the output stream with appropriate properties
|
| 145 |
-
if format == "opus":
|
| 146 |
-
out_stream = output_container.add_stream("libopus", rate=sample_rate)
|
| 147 |
-
if quality == "64k":
|
| 148 |
-
out_stream.bit_rate = 64000
|
| 149 |
-
elif quality == "96k":
|
| 150 |
-
out_stream.bit_rate = 96000
|
| 151 |
-
elif quality == "128k":
|
| 152 |
-
out_stream.bit_rate = 128000
|
| 153 |
-
elif quality == "192k":
|
| 154 |
-
out_stream.bit_rate = 192000
|
| 155 |
-
elif quality == "320k":
|
| 156 |
-
out_stream.bit_rate = 320000
|
| 157 |
-
elif format == "mp3":
|
| 158 |
-
out_stream = output_container.add_stream("libmp3lame", rate=sample_rate)
|
| 159 |
-
if quality == "V0":
|
| 160 |
-
#TODO i would really love to support V3 and V5 but there doesn't seem to be a way to set the qscale level, the property below is a bool
|
| 161 |
-
out_stream.codec_context.qscale = 1
|
| 162 |
-
elif quality == "128k":
|
| 163 |
-
out_stream.bit_rate = 128000
|
| 164 |
-
elif quality == "320k":
|
| 165 |
-
out_stream.bit_rate = 320000
|
| 166 |
-
else: #format == "flac":
|
| 167 |
-
out_stream = output_container.add_stream("flac", rate=sample_rate)
|
| 168 |
-
|
| 169 |
-
frame = av.AudioFrame.from_ndarray(waveform.movedim(0, 1).reshape(1, -1).float().numpy(), format='flt', layout='mono' if waveform.shape[0] == 1 else 'stereo')
|
| 170 |
-
frame.sample_rate = sample_rate
|
| 171 |
-
frame.pts = 0
|
| 172 |
-
output_container.mux(out_stream.encode(frame))
|
| 173 |
-
|
| 174 |
-
# Flush encoder
|
| 175 |
-
output_container.mux(out_stream.encode(None))
|
| 176 |
-
|
| 177 |
-
# Close containers
|
| 178 |
-
output_container.close()
|
| 179 |
-
|
| 180 |
-
# Write the output to file
|
| 181 |
-
output_buffer.seek(0)
|
| 182 |
-
with open(output_path, 'wb') as f:
|
| 183 |
-
f.write(output_buffer.getbuffer())
|
| 184 |
-
|
| 185 |
-
results.append({
|
| 186 |
-
"filename": file,
|
| 187 |
-
"subfolder": subfolder,
|
| 188 |
-
"type": self.type
|
| 189 |
-
})
|
| 190 |
-
counter += 1
|
| 191 |
-
|
| 192 |
-
return { "ui": { "audio": results } }
|
| 193 |
-
|
| 194 |
-
class SaveAudio:
|
| 195 |
-
def __init__(self):
|
| 196 |
-
self.output_dir = folder_paths.get_output_directory()
|
| 197 |
-
self.type = "output"
|
| 198 |
-
self.prefix_append = ""
|
| 199 |
-
|
| 200 |
-
@classmethod
|
| 201 |
-
def INPUT_TYPES(s):
|
| 202 |
-
return {"required": { "audio": ("AUDIO", ),
|
| 203 |
-
"filename_prefix": ("STRING", {"default": "audio/ComfyUI"}),
|
| 204 |
-
},
|
| 205 |
-
"hidden": {"prompt": "PROMPT", "extra_pnginfo": "EXTRA_PNGINFO"},
|
| 206 |
-
}
|
| 207 |
-
|
| 208 |
-
RETURN_TYPES = ()
|
| 209 |
-
FUNCTION = "save_flac"
|
| 210 |
-
|
| 211 |
-
OUTPUT_NODE = True
|
| 212 |
-
|
| 213 |
-
CATEGORY = "audio"
|
| 214 |
-
|
| 215 |
-
def save_flac(self, audio, filename_prefix="ComfyUI", format="flac", prompt=None, extra_pnginfo=None):
|
| 216 |
-
return save_audio(self, audio, filename_prefix, format, prompt, extra_pnginfo)
|
| 217 |
-
|
| 218 |
-
class SaveAudioMP3:
|
| 219 |
-
def __init__(self):
|
| 220 |
-
self.output_dir = folder_paths.get_output_directory()
|
| 221 |
-
self.type = "output"
|
| 222 |
-
self.prefix_append = ""
|
| 223 |
-
|
| 224 |
-
@classmethod
|
| 225 |
-
def INPUT_TYPES(s):
|
| 226 |
-
return {"required": { "audio": ("AUDIO", ),
|
| 227 |
-
"filename_prefix": ("STRING", {"default": "audio/ComfyUI"}),
|
| 228 |
-
"quality": (["V0", "128k", "320k"], {"default": "V0"}),
|
| 229 |
-
},
|
| 230 |
-
"hidden": {"prompt": "PROMPT", "extra_pnginfo": "EXTRA_PNGINFO"},
|
| 231 |
-
}
|
| 232 |
-
|
| 233 |
-
RETURN_TYPES = ()
|
| 234 |
-
FUNCTION = "save_mp3"
|
| 235 |
-
|
| 236 |
-
OUTPUT_NODE = True
|
| 237 |
-
|
| 238 |
-
CATEGORY = "audio"
|
| 239 |
-
|
| 240 |
-
def save_mp3(self, audio, filename_prefix="ComfyUI", format="mp3", prompt=None, extra_pnginfo=None, quality="128k"):
|
| 241 |
-
return save_audio(self, audio, filename_prefix, format, prompt, extra_pnginfo, quality)
|
| 242 |
-
|
| 243 |
-
class SaveAudioOpus:
|
| 244 |
-
def __init__(self):
|
| 245 |
-
self.output_dir = folder_paths.get_output_directory()
|
| 246 |
-
self.type = "output"
|
| 247 |
-
self.prefix_append = ""
|
| 248 |
-
|
| 249 |
-
@classmethod
|
| 250 |
-
def INPUT_TYPES(s):
|
| 251 |
-
return {"required": { "audio": ("AUDIO", ),
|
| 252 |
-
"filename_prefix": ("STRING", {"default": "audio/ComfyUI"}),
|
| 253 |
-
"quality": (["64k", "96k", "128k", "192k", "320k"], {"default": "128k"}),
|
| 254 |
-
},
|
| 255 |
-
"hidden": {"prompt": "PROMPT", "extra_pnginfo": "EXTRA_PNGINFO"},
|
| 256 |
-
}
|
| 257 |
-
|
| 258 |
-
RETURN_TYPES = ()
|
| 259 |
-
FUNCTION = "save_opus"
|
| 260 |
-
|
| 261 |
-
OUTPUT_NODE = True
|
| 262 |
-
|
| 263 |
-
CATEGORY = "audio"
|
| 264 |
-
|
| 265 |
-
def save_opus(self, audio, filename_prefix="ComfyUI", format="opus", prompt=None, extra_pnginfo=None, quality="V3"):
|
| 266 |
-
return save_audio(self, audio, filename_prefix, format, prompt, extra_pnginfo, quality)
|
| 267 |
-
|
| 268 |
-
class PreviewAudio(SaveAudio):
|
| 269 |
-
def __init__(self):
|
| 270 |
-
self.output_dir = folder_paths.get_temp_directory()
|
| 271 |
-
self.type = "temp"
|
| 272 |
-
self.prefix_append = "_temp_" + ''.join(random.choice("abcdefghijklmnopqrstupvxyz") for x in range(5))
|
| 273 |
-
|
| 274 |
-
@classmethod
|
| 275 |
-
def INPUT_TYPES(s):
|
| 276 |
-
return {"required":
|
| 277 |
-
{"audio": ("AUDIO", ), },
|
| 278 |
-
"hidden": {"prompt": "PROMPT", "extra_pnginfo": "EXTRA_PNGINFO"},
|
| 279 |
-
}
|
| 280 |
-
|
| 281 |
-
class LoadAudio:
|
| 282 |
-
@classmethod
|
| 283 |
-
def INPUT_TYPES(s):
|
| 284 |
-
input_dir = folder_paths.get_input_directory()
|
| 285 |
-
files = folder_paths.filter_files_content_types(os.listdir(input_dir), ["audio", "video"])
|
| 286 |
-
return {"required": {"audio": (sorted(files), {"audio_upload": True})}}
|
| 287 |
-
|
| 288 |
-
CATEGORY = "audio"
|
| 289 |
-
|
| 290 |
-
RETURN_TYPES = ("AUDIO", )
|
| 291 |
-
FUNCTION = "load"
|
| 292 |
-
|
| 293 |
-
def load(self, audio):
|
| 294 |
-
audio_path = folder_paths.get_annotated_filepath(audio)
|
| 295 |
-
waveform, sample_rate = torchaudio.load(audio_path)
|
| 296 |
-
audio = {"waveform": waveform.unsqueeze(0), "sample_rate": sample_rate}
|
| 297 |
-
return (audio, )
|
| 298 |
-
|
| 299 |
-
@classmethod
|
| 300 |
-
def IS_CHANGED(s, audio):
|
| 301 |
-
image_path = folder_paths.get_annotated_filepath(audio)
|
| 302 |
-
m = hashlib.sha256()
|
| 303 |
-
with open(image_path, 'rb') as f:
|
| 304 |
-
m.update(f.read())
|
| 305 |
-
return m.digest().hex()
|
| 306 |
-
|
| 307 |
-
@classmethod
|
| 308 |
-
def VALIDATE_INPUTS(s, audio):
|
| 309 |
-
if not folder_paths.exists_annotated_filepath(audio):
|
| 310 |
-
return "Invalid audio file: {}".format(audio)
|
| 311 |
-
return True
|
| 312 |
-
|
| 313 |
-
NODE_CLASS_MAPPINGS = {
|
| 314 |
-
"EmptyLatentAudio": EmptyLatentAudio,
|
| 315 |
-
"VAEEncodeAudio": VAEEncodeAudio,
|
| 316 |
-
"VAEDecodeAudio": VAEDecodeAudio,
|
| 317 |
-
"SaveAudio": SaveAudio,
|
| 318 |
-
"SaveAudioMP3": SaveAudioMP3,
|
| 319 |
-
"SaveAudioOpus": SaveAudioOpus,
|
| 320 |
-
"LoadAudio": LoadAudio,
|
| 321 |
-
"PreviewAudio": PreviewAudio,
|
| 322 |
-
"ConditioningStableAudio": ConditioningStableAudio,
|
| 323 |
-
}
|
| 324 |
-
|
| 325 |
-
NODE_DISPLAY_NAME_MAPPINGS = {
|
| 326 |
-
"EmptyLatentAudio": "Empty Latent Audio",
|
| 327 |
-
"VAEEncodeAudio": "VAE Encode Audio",
|
| 328 |
-
"VAEDecodeAudio": "VAE Decode Audio",
|
| 329 |
-
"PreviewAudio": "Preview Audio",
|
| 330 |
-
"LoadAudio": "Load Audio",
|
| 331 |
-
"SaveAudio": "Save Audio (FLAC)",
|
| 332 |
-
"SaveAudioMP3": "Save Audio (MP3)",
|
| 333 |
-
"SaveAudioOpus": "Save Audio (Opus)",
|
| 334 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
comfy_extras/nodes_camera_trajectory.py
DELETED
|
@@ -1,218 +0,0 @@
|
|
| 1 |
-
import nodes
|
| 2 |
-
import torch
|
| 3 |
-
import numpy as np
|
| 4 |
-
from einops import rearrange
|
| 5 |
-
import comfy.model_management
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
MAX_RESOLUTION = nodes.MAX_RESOLUTION
|
| 10 |
-
|
| 11 |
-
CAMERA_DICT = {
|
| 12 |
-
"base_T_norm": 1.5,
|
| 13 |
-
"base_angle": np.pi/3,
|
| 14 |
-
"Static": { "angle":[0., 0., 0.], "T":[0., 0., 0.]},
|
| 15 |
-
"Pan Up": { "angle":[0., 0., 0.], "T":[0., -1., 0.]},
|
| 16 |
-
"Pan Down": { "angle":[0., 0., 0.], "T":[0.,1.,0.]},
|
| 17 |
-
"Pan Left": { "angle":[0., 0., 0.], "T":[-1.,0.,0.]},
|
| 18 |
-
"Pan Right": { "angle":[0., 0., 0.], "T": [1.,0.,0.]},
|
| 19 |
-
"Zoom In": { "angle":[0., 0., 0.], "T": [0.,0.,2.]},
|
| 20 |
-
"Zoom Out": { "angle":[0., 0., 0.], "T": [0.,0.,-2.]},
|
| 21 |
-
"Anti Clockwise (ACW)": { "angle": [0., 0., -1.], "T":[0., 0., 0.]},
|
| 22 |
-
"ClockWise (CW)": { "angle": [0., 0., 1.], "T":[0., 0., 0.]},
|
| 23 |
-
}
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
def process_pose_params(cam_params, width=672, height=384, original_pose_width=1280, original_pose_height=720, device='cpu'):
|
| 27 |
-
|
| 28 |
-
def get_relative_pose(cam_params):
|
| 29 |
-
"""Copied from https://github.com/hehao13/CameraCtrl/blob/main/inference.py
|
| 30 |
-
"""
|
| 31 |
-
abs_w2cs = [cam_param.w2c_mat for cam_param in cam_params]
|
| 32 |
-
abs_c2ws = [cam_param.c2w_mat for cam_param in cam_params]
|
| 33 |
-
cam_to_origin = 0
|
| 34 |
-
target_cam_c2w = np.array([
|
| 35 |
-
[1, 0, 0, 0],
|
| 36 |
-
[0, 1, 0, -cam_to_origin],
|
| 37 |
-
[0, 0, 1, 0],
|
| 38 |
-
[0, 0, 0, 1]
|
| 39 |
-
])
|
| 40 |
-
abs2rel = target_cam_c2w @ abs_w2cs[0]
|
| 41 |
-
ret_poses = [target_cam_c2w, ] + [abs2rel @ abs_c2w for abs_c2w in abs_c2ws[1:]]
|
| 42 |
-
ret_poses = np.array(ret_poses, dtype=np.float32)
|
| 43 |
-
return ret_poses
|
| 44 |
-
|
| 45 |
-
"""Modified from https://github.com/hehao13/CameraCtrl/blob/main/inference.py
|
| 46 |
-
"""
|
| 47 |
-
cam_params = [Camera(cam_param) for cam_param in cam_params]
|
| 48 |
-
|
| 49 |
-
sample_wh_ratio = width / height
|
| 50 |
-
pose_wh_ratio = original_pose_width / original_pose_height # Assuming placeholder ratios, change as needed
|
| 51 |
-
|
| 52 |
-
if pose_wh_ratio > sample_wh_ratio:
|
| 53 |
-
resized_ori_w = height * pose_wh_ratio
|
| 54 |
-
for cam_param in cam_params:
|
| 55 |
-
cam_param.fx = resized_ori_w * cam_param.fx / width
|
| 56 |
-
else:
|
| 57 |
-
resized_ori_h = width / pose_wh_ratio
|
| 58 |
-
for cam_param in cam_params:
|
| 59 |
-
cam_param.fy = resized_ori_h * cam_param.fy / height
|
| 60 |
-
|
| 61 |
-
intrinsic = np.asarray([[cam_param.fx * width,
|
| 62 |
-
cam_param.fy * height,
|
| 63 |
-
cam_param.cx * width,
|
| 64 |
-
cam_param.cy * height]
|
| 65 |
-
for cam_param in cam_params], dtype=np.float32)
|
| 66 |
-
|
| 67 |
-
K = torch.as_tensor(intrinsic)[None] # [1, 1, 4]
|
| 68 |
-
c2ws = get_relative_pose(cam_params) # Assuming this function is defined elsewhere
|
| 69 |
-
c2ws = torch.as_tensor(c2ws)[None] # [1, n_frame, 4, 4]
|
| 70 |
-
plucker_embedding = ray_condition(K, c2ws, height, width, device=device)[0].permute(0, 3, 1, 2).contiguous() # V, 6, H, W
|
| 71 |
-
plucker_embedding = plucker_embedding[None]
|
| 72 |
-
plucker_embedding = rearrange(plucker_embedding, "b f c h w -> b f h w c")[0]
|
| 73 |
-
return plucker_embedding
|
| 74 |
-
|
| 75 |
-
class Camera(object):
|
| 76 |
-
"""Copied from https://github.com/hehao13/CameraCtrl/blob/main/inference.py
|
| 77 |
-
"""
|
| 78 |
-
def __init__(self, entry):
|
| 79 |
-
fx, fy, cx, cy = entry[1:5]
|
| 80 |
-
self.fx = fx
|
| 81 |
-
self.fy = fy
|
| 82 |
-
self.cx = cx
|
| 83 |
-
self.cy = cy
|
| 84 |
-
c2w_mat = np.array(entry[7:]).reshape(4, 4)
|
| 85 |
-
self.c2w_mat = c2w_mat
|
| 86 |
-
self.w2c_mat = np.linalg.inv(c2w_mat)
|
| 87 |
-
|
| 88 |
-
def ray_condition(K, c2w, H, W, device):
|
| 89 |
-
"""Copied from https://github.com/hehao13/CameraCtrl/blob/main/inference.py
|
| 90 |
-
"""
|
| 91 |
-
# c2w: B, V, 4, 4
|
| 92 |
-
# K: B, V, 4
|
| 93 |
-
|
| 94 |
-
B = K.shape[0]
|
| 95 |
-
|
| 96 |
-
j, i = torch.meshgrid(
|
| 97 |
-
torch.linspace(0, H - 1, H, device=device, dtype=c2w.dtype),
|
| 98 |
-
torch.linspace(0, W - 1, W, device=device, dtype=c2w.dtype),
|
| 99 |
-
indexing='ij'
|
| 100 |
-
)
|
| 101 |
-
i = i.reshape([1, 1, H * W]).expand([B, 1, H * W]) + 0.5 # [B, HxW]
|
| 102 |
-
j = j.reshape([1, 1, H * W]).expand([B, 1, H * W]) + 0.5 # [B, HxW]
|
| 103 |
-
|
| 104 |
-
fx, fy, cx, cy = K.chunk(4, dim=-1) # B,V, 1
|
| 105 |
-
|
| 106 |
-
zs = torch.ones_like(i) # [B, HxW]
|
| 107 |
-
xs = (i - cx) / fx * zs
|
| 108 |
-
ys = (j - cy) / fy * zs
|
| 109 |
-
zs = zs.expand_as(ys)
|
| 110 |
-
|
| 111 |
-
directions = torch.stack((xs, ys, zs), dim=-1) # B, V, HW, 3
|
| 112 |
-
directions = directions / directions.norm(dim=-1, keepdim=True) # B, V, HW, 3
|
| 113 |
-
|
| 114 |
-
rays_d = directions @ c2w[..., :3, :3].transpose(-1, -2) # B, V, 3, HW
|
| 115 |
-
rays_o = c2w[..., :3, 3] # B, V, 3
|
| 116 |
-
rays_o = rays_o[:, :, None].expand_as(rays_d) # B, V, 3, HW
|
| 117 |
-
# c2w @ dirctions
|
| 118 |
-
rays_dxo = torch.cross(rays_o, rays_d)
|
| 119 |
-
plucker = torch.cat([rays_dxo, rays_d], dim=-1)
|
| 120 |
-
plucker = plucker.reshape(B, c2w.shape[1], H, W, 6) # B, V, H, W, 6
|
| 121 |
-
# plucker = plucker.permute(0, 1, 4, 2, 3)
|
| 122 |
-
return plucker
|
| 123 |
-
|
| 124 |
-
def get_camera_motion(angle, T, speed, n=81):
|
| 125 |
-
def compute_R_form_rad_angle(angles):
|
| 126 |
-
theta_x, theta_y, theta_z = angles
|
| 127 |
-
Rx = np.array([[1, 0, 0],
|
| 128 |
-
[0, np.cos(theta_x), -np.sin(theta_x)],
|
| 129 |
-
[0, np.sin(theta_x), np.cos(theta_x)]])
|
| 130 |
-
|
| 131 |
-
Ry = np.array([[np.cos(theta_y), 0, np.sin(theta_y)],
|
| 132 |
-
[0, 1, 0],
|
| 133 |
-
[-np.sin(theta_y), 0, np.cos(theta_y)]])
|
| 134 |
-
|
| 135 |
-
Rz = np.array([[np.cos(theta_z), -np.sin(theta_z), 0],
|
| 136 |
-
[np.sin(theta_z), np.cos(theta_z), 0],
|
| 137 |
-
[0, 0, 1]])
|
| 138 |
-
|
| 139 |
-
R = np.dot(Rz, np.dot(Ry, Rx))
|
| 140 |
-
return R
|
| 141 |
-
RT = []
|
| 142 |
-
for i in range(n):
|
| 143 |
-
_angle = (i/n)*speed*(CAMERA_DICT["base_angle"])*angle
|
| 144 |
-
R = compute_R_form_rad_angle(_angle)
|
| 145 |
-
_T=(i/n)*speed*(CAMERA_DICT["base_T_norm"])*(T.reshape(3,1))
|
| 146 |
-
_RT = np.concatenate([R,_T], axis=1)
|
| 147 |
-
RT.append(_RT)
|
| 148 |
-
RT = np.stack(RT)
|
| 149 |
-
return RT
|
| 150 |
-
|
| 151 |
-
class WanCameraEmbedding:
|
| 152 |
-
@classmethod
|
| 153 |
-
def INPUT_TYPES(cls):
|
| 154 |
-
return {
|
| 155 |
-
"required": {
|
| 156 |
-
"camera_pose":(["Static","Pan Up","Pan Down","Pan Left","Pan Right","Zoom In","Zoom Out","Anti Clockwise (ACW)", "ClockWise (CW)"],{"default":"Static"}),
|
| 157 |
-
"width": ("INT", {"default": 832, "min": 16, "max": MAX_RESOLUTION, "step": 16}),
|
| 158 |
-
"height": ("INT", {"default": 480, "min": 16, "max": MAX_RESOLUTION, "step": 16}),
|
| 159 |
-
"length": ("INT", {"default": 81, "min": 1, "max": MAX_RESOLUTION, "step": 4}),
|
| 160 |
-
},
|
| 161 |
-
"optional":{
|
| 162 |
-
"speed":("FLOAT",{"default":1.0, "min": 0, "max": 10.0, "step": 0.1}),
|
| 163 |
-
"fx":("FLOAT",{"default":0.5, "min": 0, "max": 1, "step": 0.000000001}),
|
| 164 |
-
"fy":("FLOAT",{"default":0.5, "min": 0, "max": 1, "step": 0.000000001}),
|
| 165 |
-
"cx":("FLOAT",{"default":0.5, "min": 0, "max": 1, "step": 0.01}),
|
| 166 |
-
"cy":("FLOAT",{"default":0.5, "min": 0, "max": 1, "step": 0.01}),
|
| 167 |
-
}
|
| 168 |
-
|
| 169 |
-
}
|
| 170 |
-
|
| 171 |
-
RETURN_TYPES = ("WAN_CAMERA_EMBEDDING","INT","INT","INT")
|
| 172 |
-
RETURN_NAMES = ("camera_embedding","width","height","length")
|
| 173 |
-
FUNCTION = "run"
|
| 174 |
-
CATEGORY = "camera"
|
| 175 |
-
|
| 176 |
-
def run(self, camera_pose, width, height, length, speed=1.0, fx=0.5, fy=0.5, cx=0.5, cy=0.5):
|
| 177 |
-
"""
|
| 178 |
-
Use Camera trajectory as extrinsic parameters to calculate Plücker embeddings (Sitzmannet al., 2021)
|
| 179 |
-
Adapted from https://github.com/aigc-apps/VideoX-Fun/blob/main/comfyui/comfyui_nodes.py
|
| 180 |
-
"""
|
| 181 |
-
motion_list = [camera_pose]
|
| 182 |
-
speed = speed
|
| 183 |
-
angle = np.array(CAMERA_DICT[motion_list[0]]["angle"])
|
| 184 |
-
T = np.array(CAMERA_DICT[motion_list[0]]["T"])
|
| 185 |
-
RT = get_camera_motion(angle, T, speed, length)
|
| 186 |
-
|
| 187 |
-
trajs=[]
|
| 188 |
-
for cp in RT.tolist():
|
| 189 |
-
traj=[fx,fy,cx,cy,0,0]
|
| 190 |
-
traj.extend(cp[0])
|
| 191 |
-
traj.extend(cp[1])
|
| 192 |
-
traj.extend(cp[2])
|
| 193 |
-
traj.extend([0,0,0,1])
|
| 194 |
-
trajs.append(traj)
|
| 195 |
-
|
| 196 |
-
cam_params = np.array([[float(x) for x in pose] for pose in trajs])
|
| 197 |
-
cam_params = np.concatenate([np.zeros_like(cam_params[:, :1]), cam_params], 1)
|
| 198 |
-
control_camera_video = process_pose_params(cam_params, width=width, height=height)
|
| 199 |
-
control_camera_video = control_camera_video.permute([3, 0, 1, 2]).unsqueeze(0).to(device=comfy.model_management.intermediate_device())
|
| 200 |
-
|
| 201 |
-
control_camera_video = torch.concat(
|
| 202 |
-
[
|
| 203 |
-
torch.repeat_interleave(control_camera_video[:, :, 0:1], repeats=4, dim=2),
|
| 204 |
-
control_camera_video[:, :, 1:]
|
| 205 |
-
], dim=2
|
| 206 |
-
).transpose(1, 2)
|
| 207 |
-
|
| 208 |
-
# Reshape, transpose, and view into desired shape
|
| 209 |
-
b, f, c, h, w = control_camera_video.shape
|
| 210 |
-
control_camera_video = control_camera_video.contiguous().view(b, f // 4, 4, c, h, w).transpose(2, 3)
|
| 211 |
-
control_camera_video = control_camera_video.contiguous().view(b, f // 4, c * 4, h, w).transpose(1, 2)
|
| 212 |
-
|
| 213 |
-
return (control_camera_video, width, height, length)
|
| 214 |
-
|
| 215 |
-
|
| 216 |
-
NODE_CLASS_MAPPINGS = {
|
| 217 |
-
"WanCameraEmbedding": WanCameraEmbedding,
|
| 218 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
comfy_extras/nodes_canny.py
DELETED
|
@@ -1,25 +0,0 @@
|
|
| 1 |
-
from kornia.filters import canny
|
| 2 |
-
import comfy.model_management
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
class Canny:
|
| 6 |
-
@classmethod
|
| 7 |
-
def INPUT_TYPES(s):
|
| 8 |
-
return {"required": {"image": ("IMAGE",),
|
| 9 |
-
"low_threshold": ("FLOAT", {"default": 0.4, "min": 0.01, "max": 0.99, "step": 0.01}),
|
| 10 |
-
"high_threshold": ("FLOAT", {"default": 0.8, "min": 0.01, "max": 0.99, "step": 0.01})
|
| 11 |
-
}}
|
| 12 |
-
|
| 13 |
-
RETURN_TYPES = ("IMAGE",)
|
| 14 |
-
FUNCTION = "detect_edge"
|
| 15 |
-
|
| 16 |
-
CATEGORY = "image/preprocessors"
|
| 17 |
-
|
| 18 |
-
def detect_edge(self, image, low_threshold, high_threshold):
|
| 19 |
-
output = canny(image.to(comfy.model_management.get_torch_device()).movedim(-1, 1), low_threshold, high_threshold)
|
| 20 |
-
img_out = output[1].to(comfy.model_management.intermediate_device()).repeat(1, 3, 1, 1).movedim(1, -1)
|
| 21 |
-
return (img_out,)
|
| 22 |
-
|
| 23 |
-
NODE_CLASS_MAPPINGS = {
|
| 24 |
-
"Canny": Canny,
|
| 25 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
comfy_extras/nodes_cfg.py
DELETED
|
@@ -1,72 +0,0 @@
|
|
| 1 |
-
import torch
|
| 2 |
-
|
| 3 |
-
# https://github.com/WeichenFan/CFG-Zero-star
|
| 4 |
-
def optimized_scale(positive, negative):
|
| 5 |
-
positive_flat = positive.reshape(positive.shape[0], -1)
|
| 6 |
-
negative_flat = negative.reshape(negative.shape[0], -1)
|
| 7 |
-
|
| 8 |
-
# Calculate dot production
|
| 9 |
-
dot_product = torch.sum(positive_flat * negative_flat, dim=1, keepdim=True)
|
| 10 |
-
|
| 11 |
-
# Squared norm of uncondition
|
| 12 |
-
squared_norm = torch.sum(negative_flat ** 2, dim=1, keepdim=True) + 1e-8
|
| 13 |
-
|
| 14 |
-
# st_star = v_cond^T * v_uncond / ||v_uncond||^2
|
| 15 |
-
st_star = dot_product / squared_norm
|
| 16 |
-
|
| 17 |
-
return st_star.reshape([positive.shape[0]] + [1] * (positive.ndim - 1))
|
| 18 |
-
|
| 19 |
-
class CFGZeroStar:
|
| 20 |
-
@classmethod
|
| 21 |
-
def INPUT_TYPES(s):
|
| 22 |
-
return {"required": {"model": ("MODEL",),
|
| 23 |
-
}}
|
| 24 |
-
RETURN_TYPES = ("MODEL",)
|
| 25 |
-
RETURN_NAMES = ("patched_model",)
|
| 26 |
-
FUNCTION = "patch"
|
| 27 |
-
CATEGORY = "advanced/guidance"
|
| 28 |
-
|
| 29 |
-
def patch(self, model):
|
| 30 |
-
m = model.clone()
|
| 31 |
-
def cfg_zero_star(args):
|
| 32 |
-
guidance_scale = args['cond_scale']
|
| 33 |
-
x = args['input']
|
| 34 |
-
cond_p = args['cond_denoised']
|
| 35 |
-
uncond_p = args['uncond_denoised']
|
| 36 |
-
out = args["denoised"]
|
| 37 |
-
alpha = optimized_scale(x - cond_p, x - uncond_p)
|
| 38 |
-
|
| 39 |
-
return out + uncond_p * (alpha - 1.0) + guidance_scale * uncond_p * (1.0 - alpha)
|
| 40 |
-
m.set_model_sampler_post_cfg_function(cfg_zero_star)
|
| 41 |
-
return (m, )
|
| 42 |
-
|
| 43 |
-
class CFGNorm:
|
| 44 |
-
@classmethod
|
| 45 |
-
def INPUT_TYPES(s):
|
| 46 |
-
return {"required": {"model": ("MODEL",),
|
| 47 |
-
"strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 100.0, "step": 0.01}),
|
| 48 |
-
}}
|
| 49 |
-
RETURN_TYPES = ("MODEL",)
|
| 50 |
-
RETURN_NAMES = ("patched_model",)
|
| 51 |
-
FUNCTION = "patch"
|
| 52 |
-
CATEGORY = "advanced/guidance"
|
| 53 |
-
EXPERIMENTAL = True
|
| 54 |
-
|
| 55 |
-
def patch(self, model, strength):
|
| 56 |
-
m = model.clone()
|
| 57 |
-
def cfg_norm(args):
|
| 58 |
-
cond_p = args['cond_denoised']
|
| 59 |
-
pred_text_ = args["denoised"]
|
| 60 |
-
|
| 61 |
-
norm_full_cond = torch.norm(cond_p, dim=1, keepdim=True)
|
| 62 |
-
norm_pred_text = torch.norm(pred_text_, dim=1, keepdim=True)
|
| 63 |
-
scale = (norm_full_cond / (norm_pred_text + 1e-8)).clamp(min=0.0, max=1.0)
|
| 64 |
-
return pred_text_ * scale * strength
|
| 65 |
-
|
| 66 |
-
m.set_model_sampler_post_cfg_function(cfg_norm)
|
| 67 |
-
return (m, )
|
| 68 |
-
|
| 69 |
-
NODE_CLASS_MAPPINGS = {
|
| 70 |
-
"CFGZeroStar": CFGZeroStar,
|
| 71 |
-
"CFGNorm": CFGNorm,
|
| 72 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
comfy_extras/nodes_clip_sdxl.py
DELETED
|
@@ -1,54 +0,0 @@
|
|
| 1 |
-
from nodes import MAX_RESOLUTION
|
| 2 |
-
|
| 3 |
-
class CLIPTextEncodeSDXLRefiner:
|
| 4 |
-
@classmethod
|
| 5 |
-
def INPUT_TYPES(s):
|
| 6 |
-
return {"required": {
|
| 7 |
-
"ascore": ("FLOAT", {"default": 6.0, "min": 0.0, "max": 1000.0, "step": 0.01}),
|
| 8 |
-
"width": ("INT", {"default": 1024.0, "min": 0, "max": MAX_RESOLUTION}),
|
| 9 |
-
"height": ("INT", {"default": 1024.0, "min": 0, "max": MAX_RESOLUTION}),
|
| 10 |
-
"text": ("STRING", {"multiline": True, "dynamicPrompts": True}), "clip": ("CLIP", ),
|
| 11 |
-
}}
|
| 12 |
-
RETURN_TYPES = ("CONDITIONING",)
|
| 13 |
-
FUNCTION = "encode"
|
| 14 |
-
|
| 15 |
-
CATEGORY = "advanced/conditioning"
|
| 16 |
-
|
| 17 |
-
def encode(self, clip, ascore, width, height, text):
|
| 18 |
-
tokens = clip.tokenize(text)
|
| 19 |
-
return (clip.encode_from_tokens_scheduled(tokens, add_dict={"aesthetic_score": ascore, "width": width, "height": height}), )
|
| 20 |
-
|
| 21 |
-
class CLIPTextEncodeSDXL:
|
| 22 |
-
@classmethod
|
| 23 |
-
def INPUT_TYPES(s):
|
| 24 |
-
return {"required": {
|
| 25 |
-
"clip": ("CLIP", ),
|
| 26 |
-
"width": ("INT", {"default": 1024.0, "min": 0, "max": MAX_RESOLUTION}),
|
| 27 |
-
"height": ("INT", {"default": 1024.0, "min": 0, "max": MAX_RESOLUTION}),
|
| 28 |
-
"crop_w": ("INT", {"default": 0, "min": 0, "max": MAX_RESOLUTION}),
|
| 29 |
-
"crop_h": ("INT", {"default": 0, "min": 0, "max": MAX_RESOLUTION}),
|
| 30 |
-
"target_width": ("INT", {"default": 1024.0, "min": 0, "max": MAX_RESOLUTION}),
|
| 31 |
-
"target_height": ("INT", {"default": 1024.0, "min": 0, "max": MAX_RESOLUTION}),
|
| 32 |
-
"text_g": ("STRING", {"multiline": True, "dynamicPrompts": True}),
|
| 33 |
-
"text_l": ("STRING", {"multiline": True, "dynamicPrompts": True}),
|
| 34 |
-
}}
|
| 35 |
-
RETURN_TYPES = ("CONDITIONING",)
|
| 36 |
-
FUNCTION = "encode"
|
| 37 |
-
|
| 38 |
-
CATEGORY = "advanced/conditioning"
|
| 39 |
-
|
| 40 |
-
def encode(self, clip, width, height, crop_w, crop_h, target_width, target_height, text_g, text_l):
|
| 41 |
-
tokens = clip.tokenize(text_g)
|
| 42 |
-
tokens["l"] = clip.tokenize(text_l)["l"]
|
| 43 |
-
if len(tokens["l"]) != len(tokens["g"]):
|
| 44 |
-
empty = clip.tokenize("")
|
| 45 |
-
while len(tokens["l"]) < len(tokens["g"]):
|
| 46 |
-
tokens["l"] += empty["l"]
|
| 47 |
-
while len(tokens["l"]) > len(tokens["g"]):
|
| 48 |
-
tokens["g"] += empty["g"]
|
| 49 |
-
return (clip.encode_from_tokens_scheduled(tokens, add_dict={"width": width, "height": height, "crop_w": crop_w, "crop_h": crop_h, "target_width": target_width, "target_height": target_height}), )
|
| 50 |
-
|
| 51 |
-
NODE_CLASS_MAPPINGS = {
|
| 52 |
-
"CLIPTextEncodeSDXLRefiner": CLIPTextEncodeSDXLRefiner,
|
| 53 |
-
"CLIPTextEncodeSDXL": CLIPTextEncodeSDXL,
|
| 54 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
comfy_extras/nodes_compositing.py
DELETED
|
@@ -1,214 +0,0 @@
|
|
| 1 |
-
import torch
|
| 2 |
-
import comfy.utils
|
| 3 |
-
from enum import Enum
|
| 4 |
-
|
| 5 |
-
def resize_mask(mask, shape):
|
| 6 |
-
return torch.nn.functional.interpolate(mask.reshape((-1, 1, mask.shape[-2], mask.shape[-1])), size=(shape[0], shape[1]), mode="bilinear").squeeze(1)
|
| 7 |
-
|
| 8 |
-
class PorterDuffMode(Enum):
|
| 9 |
-
ADD = 0
|
| 10 |
-
CLEAR = 1
|
| 11 |
-
DARKEN = 2
|
| 12 |
-
DST = 3
|
| 13 |
-
DST_ATOP = 4
|
| 14 |
-
DST_IN = 5
|
| 15 |
-
DST_OUT = 6
|
| 16 |
-
DST_OVER = 7
|
| 17 |
-
LIGHTEN = 8
|
| 18 |
-
MULTIPLY = 9
|
| 19 |
-
OVERLAY = 10
|
| 20 |
-
SCREEN = 11
|
| 21 |
-
SRC = 12
|
| 22 |
-
SRC_ATOP = 13
|
| 23 |
-
SRC_IN = 14
|
| 24 |
-
SRC_OUT = 15
|
| 25 |
-
SRC_OVER = 16
|
| 26 |
-
XOR = 17
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
def porter_duff_composite(src_image: torch.Tensor, src_alpha: torch.Tensor, dst_image: torch.Tensor, dst_alpha: torch.Tensor, mode: PorterDuffMode):
|
| 30 |
-
# convert mask to alpha
|
| 31 |
-
src_alpha = 1 - src_alpha
|
| 32 |
-
dst_alpha = 1 - dst_alpha
|
| 33 |
-
# premultiply alpha
|
| 34 |
-
src_image = src_image * src_alpha
|
| 35 |
-
dst_image = dst_image * dst_alpha
|
| 36 |
-
|
| 37 |
-
# composite ops below assume alpha-premultiplied images
|
| 38 |
-
if mode == PorterDuffMode.ADD:
|
| 39 |
-
out_alpha = torch.clamp(src_alpha + dst_alpha, 0, 1)
|
| 40 |
-
out_image = torch.clamp(src_image + dst_image, 0, 1)
|
| 41 |
-
elif mode == PorterDuffMode.CLEAR:
|
| 42 |
-
out_alpha = torch.zeros_like(dst_alpha)
|
| 43 |
-
out_image = torch.zeros_like(dst_image)
|
| 44 |
-
elif mode == PorterDuffMode.DARKEN:
|
| 45 |
-
out_alpha = src_alpha + dst_alpha - src_alpha * dst_alpha
|
| 46 |
-
out_image = (1 - dst_alpha) * src_image + (1 - src_alpha) * dst_image + torch.min(src_image, dst_image)
|
| 47 |
-
elif mode == PorterDuffMode.DST:
|
| 48 |
-
out_alpha = dst_alpha
|
| 49 |
-
out_image = dst_image
|
| 50 |
-
elif mode == PorterDuffMode.DST_ATOP:
|
| 51 |
-
out_alpha = src_alpha
|
| 52 |
-
out_image = src_alpha * dst_image + (1 - dst_alpha) * src_image
|
| 53 |
-
elif mode == PorterDuffMode.DST_IN:
|
| 54 |
-
out_alpha = src_alpha * dst_alpha
|
| 55 |
-
out_image = dst_image * src_alpha
|
| 56 |
-
elif mode == PorterDuffMode.DST_OUT:
|
| 57 |
-
out_alpha = (1 - src_alpha) * dst_alpha
|
| 58 |
-
out_image = (1 - src_alpha) * dst_image
|
| 59 |
-
elif mode == PorterDuffMode.DST_OVER:
|
| 60 |
-
out_alpha = dst_alpha + (1 - dst_alpha) * src_alpha
|
| 61 |
-
out_image = dst_image + (1 - dst_alpha) * src_image
|
| 62 |
-
elif mode == PorterDuffMode.LIGHTEN:
|
| 63 |
-
out_alpha = src_alpha + dst_alpha - src_alpha * dst_alpha
|
| 64 |
-
out_image = (1 - dst_alpha) * src_image + (1 - src_alpha) * dst_image + torch.max(src_image, dst_image)
|
| 65 |
-
elif mode == PorterDuffMode.MULTIPLY:
|
| 66 |
-
out_alpha = src_alpha * dst_alpha
|
| 67 |
-
out_image = src_image * dst_image
|
| 68 |
-
elif mode == PorterDuffMode.OVERLAY:
|
| 69 |
-
out_alpha = src_alpha + dst_alpha - src_alpha * dst_alpha
|
| 70 |
-
out_image = torch.where(2 * dst_image < dst_alpha, 2 * src_image * dst_image,
|
| 71 |
-
src_alpha * dst_alpha - 2 * (dst_alpha - src_image) * (src_alpha - dst_image))
|
| 72 |
-
elif mode == PorterDuffMode.SCREEN:
|
| 73 |
-
out_alpha = src_alpha + dst_alpha - src_alpha * dst_alpha
|
| 74 |
-
out_image = src_image + dst_image - src_image * dst_image
|
| 75 |
-
elif mode == PorterDuffMode.SRC:
|
| 76 |
-
out_alpha = src_alpha
|
| 77 |
-
out_image = src_image
|
| 78 |
-
elif mode == PorterDuffMode.SRC_ATOP:
|
| 79 |
-
out_alpha = dst_alpha
|
| 80 |
-
out_image = dst_alpha * src_image + (1 - src_alpha) * dst_image
|
| 81 |
-
elif mode == PorterDuffMode.SRC_IN:
|
| 82 |
-
out_alpha = src_alpha * dst_alpha
|
| 83 |
-
out_image = src_image * dst_alpha
|
| 84 |
-
elif mode == PorterDuffMode.SRC_OUT:
|
| 85 |
-
out_alpha = (1 - dst_alpha) * src_alpha
|
| 86 |
-
out_image = (1 - dst_alpha) * src_image
|
| 87 |
-
elif mode == PorterDuffMode.SRC_OVER:
|
| 88 |
-
out_alpha = src_alpha + (1 - src_alpha) * dst_alpha
|
| 89 |
-
out_image = src_image + (1 - src_alpha) * dst_image
|
| 90 |
-
elif mode == PorterDuffMode.XOR:
|
| 91 |
-
out_alpha = (1 - dst_alpha) * src_alpha + (1 - src_alpha) * dst_alpha
|
| 92 |
-
out_image = (1 - dst_alpha) * src_image + (1 - src_alpha) * dst_image
|
| 93 |
-
else:
|
| 94 |
-
return None, None
|
| 95 |
-
|
| 96 |
-
# back to non-premultiplied alpha
|
| 97 |
-
out_image = torch.where(out_alpha > 1e-5, out_image / out_alpha, torch.zeros_like(out_image))
|
| 98 |
-
out_image = torch.clamp(out_image, 0, 1)
|
| 99 |
-
# convert alpha to mask
|
| 100 |
-
out_alpha = 1 - out_alpha
|
| 101 |
-
return out_image, out_alpha
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
class PorterDuffImageComposite:
|
| 105 |
-
@classmethod
|
| 106 |
-
def INPUT_TYPES(s):
|
| 107 |
-
return {
|
| 108 |
-
"required": {
|
| 109 |
-
"source": ("IMAGE",),
|
| 110 |
-
"source_alpha": ("MASK",),
|
| 111 |
-
"destination": ("IMAGE",),
|
| 112 |
-
"destination_alpha": ("MASK",),
|
| 113 |
-
"mode": ([mode.name for mode in PorterDuffMode], {"default": PorterDuffMode.DST.name}),
|
| 114 |
-
},
|
| 115 |
-
}
|
| 116 |
-
|
| 117 |
-
RETURN_TYPES = ("IMAGE", "MASK")
|
| 118 |
-
FUNCTION = "composite"
|
| 119 |
-
CATEGORY = "mask/compositing"
|
| 120 |
-
|
| 121 |
-
def composite(self, source: torch.Tensor, source_alpha: torch.Tensor, destination: torch.Tensor, destination_alpha: torch.Tensor, mode):
|
| 122 |
-
batch_size = min(len(source), len(source_alpha), len(destination), len(destination_alpha))
|
| 123 |
-
out_images = []
|
| 124 |
-
out_alphas = []
|
| 125 |
-
|
| 126 |
-
for i in range(batch_size):
|
| 127 |
-
src_image = source[i]
|
| 128 |
-
dst_image = destination[i]
|
| 129 |
-
|
| 130 |
-
assert src_image.shape[2] == dst_image.shape[2] # inputs need to have same number of channels
|
| 131 |
-
|
| 132 |
-
src_alpha = source_alpha[i].unsqueeze(2)
|
| 133 |
-
dst_alpha = destination_alpha[i].unsqueeze(2)
|
| 134 |
-
|
| 135 |
-
if dst_alpha.shape[:2] != dst_image.shape[:2]:
|
| 136 |
-
upscale_input = dst_alpha.unsqueeze(0).permute(0, 3, 1, 2)
|
| 137 |
-
upscale_output = comfy.utils.common_upscale(upscale_input, dst_image.shape[1], dst_image.shape[0], upscale_method='bicubic', crop='center')
|
| 138 |
-
dst_alpha = upscale_output.permute(0, 2, 3, 1).squeeze(0)
|
| 139 |
-
if src_image.shape != dst_image.shape:
|
| 140 |
-
upscale_input = src_image.unsqueeze(0).permute(0, 3, 1, 2)
|
| 141 |
-
upscale_output = comfy.utils.common_upscale(upscale_input, dst_image.shape[1], dst_image.shape[0], upscale_method='bicubic', crop='center')
|
| 142 |
-
src_image = upscale_output.permute(0, 2, 3, 1).squeeze(0)
|
| 143 |
-
if src_alpha.shape != dst_alpha.shape:
|
| 144 |
-
upscale_input = src_alpha.unsqueeze(0).permute(0, 3, 1, 2)
|
| 145 |
-
upscale_output = comfy.utils.common_upscale(upscale_input, dst_alpha.shape[1], dst_alpha.shape[0], upscale_method='bicubic', crop='center')
|
| 146 |
-
src_alpha = upscale_output.permute(0, 2, 3, 1).squeeze(0)
|
| 147 |
-
|
| 148 |
-
out_image, out_alpha = porter_duff_composite(src_image, src_alpha, dst_image, dst_alpha, PorterDuffMode[mode])
|
| 149 |
-
|
| 150 |
-
out_images.append(out_image)
|
| 151 |
-
out_alphas.append(out_alpha.squeeze(2))
|
| 152 |
-
|
| 153 |
-
result = (torch.stack(out_images), torch.stack(out_alphas))
|
| 154 |
-
return result
|
| 155 |
-
|
| 156 |
-
|
| 157 |
-
class SplitImageWithAlpha:
|
| 158 |
-
@classmethod
|
| 159 |
-
def INPUT_TYPES(s):
|
| 160 |
-
return {
|
| 161 |
-
"required": {
|
| 162 |
-
"image": ("IMAGE",),
|
| 163 |
-
}
|
| 164 |
-
}
|
| 165 |
-
|
| 166 |
-
CATEGORY = "mask/compositing"
|
| 167 |
-
RETURN_TYPES = ("IMAGE", "MASK")
|
| 168 |
-
FUNCTION = "split_image_with_alpha"
|
| 169 |
-
|
| 170 |
-
def split_image_with_alpha(self, image: torch.Tensor):
|
| 171 |
-
out_images = [i[:,:,:3] for i in image]
|
| 172 |
-
out_alphas = [i[:,:,3] if i.shape[2] > 3 else torch.ones_like(i[:,:,0]) for i in image]
|
| 173 |
-
result = (torch.stack(out_images), 1.0 - torch.stack(out_alphas))
|
| 174 |
-
return result
|
| 175 |
-
|
| 176 |
-
|
| 177 |
-
class JoinImageWithAlpha:
|
| 178 |
-
@classmethod
|
| 179 |
-
def INPUT_TYPES(s):
|
| 180 |
-
return {
|
| 181 |
-
"required": {
|
| 182 |
-
"image": ("IMAGE",),
|
| 183 |
-
"alpha": ("MASK",),
|
| 184 |
-
}
|
| 185 |
-
}
|
| 186 |
-
|
| 187 |
-
CATEGORY = "mask/compositing"
|
| 188 |
-
RETURN_TYPES = ("IMAGE",)
|
| 189 |
-
FUNCTION = "join_image_with_alpha"
|
| 190 |
-
|
| 191 |
-
def join_image_with_alpha(self, image: torch.Tensor, alpha: torch.Tensor):
|
| 192 |
-
batch_size = min(len(image), len(alpha))
|
| 193 |
-
out_images = []
|
| 194 |
-
|
| 195 |
-
alpha = 1.0 - resize_mask(alpha, image.shape[1:])
|
| 196 |
-
for i in range(batch_size):
|
| 197 |
-
out_images.append(torch.cat((image[i][:,:,:3], alpha[i].unsqueeze(2)), dim=2))
|
| 198 |
-
|
| 199 |
-
result = (torch.stack(out_images),)
|
| 200 |
-
return result
|
| 201 |
-
|
| 202 |
-
|
| 203 |
-
NODE_CLASS_MAPPINGS = {
|
| 204 |
-
"PorterDuffImageComposite": PorterDuffImageComposite,
|
| 205 |
-
"SplitImageWithAlpha": SplitImageWithAlpha,
|
| 206 |
-
"JoinImageWithAlpha": JoinImageWithAlpha,
|
| 207 |
-
}
|
| 208 |
-
|
| 209 |
-
|
| 210 |
-
NODE_DISPLAY_NAME_MAPPINGS = {
|
| 211 |
-
"PorterDuffImageComposite": "Porter-Duff Image Composite",
|
| 212 |
-
"SplitImageWithAlpha": "Split Image with Alpha",
|
| 213 |
-
"JoinImageWithAlpha": "Join Image with Alpha",
|
| 214 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
comfy_extras/nodes_cond.py
DELETED
|
@@ -1,49 +0,0 @@
|
|
| 1 |
-
|
| 2 |
-
|
| 3 |
-
class CLIPTextEncodeControlnet:
|
| 4 |
-
@classmethod
|
| 5 |
-
def INPUT_TYPES(s):
|
| 6 |
-
return {"required": {"clip": ("CLIP", ), "conditioning": ("CONDITIONING", ), "text": ("STRING", {"multiline": True, "dynamicPrompts": True})}}
|
| 7 |
-
RETURN_TYPES = ("CONDITIONING",)
|
| 8 |
-
FUNCTION = "encode"
|
| 9 |
-
|
| 10 |
-
CATEGORY = "_for_testing/conditioning"
|
| 11 |
-
|
| 12 |
-
def encode(self, clip, conditioning, text):
|
| 13 |
-
tokens = clip.tokenize(text)
|
| 14 |
-
cond, pooled = clip.encode_from_tokens(tokens, return_pooled=True)
|
| 15 |
-
c = []
|
| 16 |
-
for t in conditioning:
|
| 17 |
-
n = [t[0], t[1].copy()]
|
| 18 |
-
n[1]['cross_attn_controlnet'] = cond
|
| 19 |
-
n[1]['pooled_output_controlnet'] = pooled
|
| 20 |
-
c.append(n)
|
| 21 |
-
return (c, )
|
| 22 |
-
|
| 23 |
-
class T5TokenizerOptions:
|
| 24 |
-
@classmethod
|
| 25 |
-
def INPUT_TYPES(s):
|
| 26 |
-
return {
|
| 27 |
-
"required": {
|
| 28 |
-
"clip": ("CLIP", ),
|
| 29 |
-
"min_padding": ("INT", {"default": 0, "min": 0, "max": 10000, "step": 1}),
|
| 30 |
-
"min_length": ("INT", {"default": 0, "min": 0, "max": 10000, "step": 1}),
|
| 31 |
-
}
|
| 32 |
-
}
|
| 33 |
-
|
| 34 |
-
CATEGORY = "_for_testing/conditioning"
|
| 35 |
-
RETURN_TYPES = ("CLIP",)
|
| 36 |
-
FUNCTION = "set_options"
|
| 37 |
-
|
| 38 |
-
def set_options(self, clip, min_padding, min_length):
|
| 39 |
-
clip = clip.clone()
|
| 40 |
-
for t5_type in ["t5xxl", "pile_t5xl", "t5base", "mt5xl", "umt5xxl"]:
|
| 41 |
-
clip.set_tokenizer_option("{}_min_padding".format(t5_type), min_padding)
|
| 42 |
-
clip.set_tokenizer_option("{}_min_length".format(t5_type), min_length)
|
| 43 |
-
|
| 44 |
-
return (clip, )
|
| 45 |
-
|
| 46 |
-
NODE_CLASS_MAPPINGS = {
|
| 47 |
-
"CLIPTextEncodeControlnet": CLIPTextEncodeControlnet,
|
| 48 |
-
"T5TokenizerOptions": T5TokenizerOptions,
|
| 49 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
comfy_extras/nodes_controlnet.py
DELETED
|
@@ -1,60 +0,0 @@
|
|
| 1 |
-
from comfy.cldm.control_types import UNION_CONTROLNET_TYPES
|
| 2 |
-
import nodes
|
| 3 |
-
import comfy.utils
|
| 4 |
-
|
| 5 |
-
class SetUnionControlNetType:
|
| 6 |
-
@classmethod
|
| 7 |
-
def INPUT_TYPES(s):
|
| 8 |
-
return {"required": {"control_net": ("CONTROL_NET", ),
|
| 9 |
-
"type": (["auto"] + list(UNION_CONTROLNET_TYPES.keys()),)
|
| 10 |
-
}}
|
| 11 |
-
|
| 12 |
-
CATEGORY = "conditioning/controlnet"
|
| 13 |
-
RETURN_TYPES = ("CONTROL_NET",)
|
| 14 |
-
|
| 15 |
-
FUNCTION = "set_controlnet_type"
|
| 16 |
-
|
| 17 |
-
def set_controlnet_type(self, control_net, type):
|
| 18 |
-
control_net = control_net.copy()
|
| 19 |
-
type_number = UNION_CONTROLNET_TYPES.get(type, -1)
|
| 20 |
-
if type_number >= 0:
|
| 21 |
-
control_net.set_extra_arg("control_type", [type_number])
|
| 22 |
-
else:
|
| 23 |
-
control_net.set_extra_arg("control_type", [])
|
| 24 |
-
|
| 25 |
-
return (control_net,)
|
| 26 |
-
|
| 27 |
-
class ControlNetInpaintingAliMamaApply(nodes.ControlNetApplyAdvanced):
|
| 28 |
-
@classmethod
|
| 29 |
-
def INPUT_TYPES(s):
|
| 30 |
-
return {"required": {"positive": ("CONDITIONING", ),
|
| 31 |
-
"negative": ("CONDITIONING", ),
|
| 32 |
-
"control_net": ("CONTROL_NET", ),
|
| 33 |
-
"vae": ("VAE", ),
|
| 34 |
-
"image": ("IMAGE", ),
|
| 35 |
-
"mask": ("MASK", ),
|
| 36 |
-
"strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.01}),
|
| 37 |
-
"start_percent": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.001}),
|
| 38 |
-
"end_percent": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.001})
|
| 39 |
-
}}
|
| 40 |
-
|
| 41 |
-
FUNCTION = "apply_inpaint_controlnet"
|
| 42 |
-
|
| 43 |
-
CATEGORY = "conditioning/controlnet"
|
| 44 |
-
|
| 45 |
-
def apply_inpaint_controlnet(self, positive, negative, control_net, vae, image, mask, strength, start_percent, end_percent):
|
| 46 |
-
extra_concat = []
|
| 47 |
-
if control_net.concat_mask:
|
| 48 |
-
mask = 1.0 - mask.reshape((-1, 1, mask.shape[-2], mask.shape[-1]))
|
| 49 |
-
mask_apply = comfy.utils.common_upscale(mask, image.shape[2], image.shape[1], "bilinear", "center").round()
|
| 50 |
-
image = image * mask_apply.movedim(1, -1).repeat(1, 1, 1, image.shape[3])
|
| 51 |
-
extra_concat = [mask]
|
| 52 |
-
|
| 53 |
-
return self.apply_controlnet(positive, negative, control_net, image, strength, start_percent, end_percent, vae=vae, extra_concat=extra_concat)
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
NODE_CLASS_MAPPINGS = {
|
| 58 |
-
"SetUnionControlNetType": SetUnionControlNetType,
|
| 59 |
-
"ControlNetInpaintingAliMamaApply": ControlNetInpaintingAliMamaApply,
|
| 60 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
comfy_extras/nodes_cosmos.py
DELETED
|
@@ -1,128 +0,0 @@
|
|
| 1 |
-
import nodes
|
| 2 |
-
import torch
|
| 3 |
-
import comfy.model_management
|
| 4 |
-
import comfy.utils
|
| 5 |
-
import comfy.latent_formats
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
class EmptyCosmosLatentVideo:
|
| 9 |
-
@classmethod
|
| 10 |
-
def INPUT_TYPES(s):
|
| 11 |
-
return {"required": { "width": ("INT", {"default": 1280, "min": 16, "max": nodes.MAX_RESOLUTION, "step": 16}),
|
| 12 |
-
"height": ("INT", {"default": 704, "min": 16, "max": nodes.MAX_RESOLUTION, "step": 16}),
|
| 13 |
-
"length": ("INT", {"default": 121, "min": 1, "max": nodes.MAX_RESOLUTION, "step": 8}),
|
| 14 |
-
"batch_size": ("INT", {"default": 1, "min": 1, "max": 4096})}}
|
| 15 |
-
RETURN_TYPES = ("LATENT",)
|
| 16 |
-
FUNCTION = "generate"
|
| 17 |
-
|
| 18 |
-
CATEGORY = "latent/video"
|
| 19 |
-
|
| 20 |
-
def generate(self, width, height, length, batch_size=1):
|
| 21 |
-
latent = torch.zeros([batch_size, 16, ((length - 1) // 8) + 1, height // 8, width // 8], device=comfy.model_management.intermediate_device())
|
| 22 |
-
return ({"samples": latent}, )
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
def vae_encode_with_padding(vae, image, width, height, length, padding=0):
|
| 26 |
-
pixels = comfy.utils.common_upscale(image[..., :3].movedim(-1, 1), width, height, "bilinear", "center").movedim(1, -1)
|
| 27 |
-
pixel_len = min(pixels.shape[0], length)
|
| 28 |
-
padded_length = min(length, (((pixel_len - 1) // 8) + 1 + padding) * 8 - 7)
|
| 29 |
-
padded_pixels = torch.ones((padded_length, height, width, 3)) * 0.5
|
| 30 |
-
padded_pixels[:pixel_len] = pixels[:pixel_len]
|
| 31 |
-
latent_len = ((pixel_len - 1) // 8) + 1
|
| 32 |
-
latent_temp = vae.encode(padded_pixels)
|
| 33 |
-
return latent_temp[:, :, :latent_len]
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
class CosmosImageToVideoLatent:
|
| 37 |
-
@classmethod
|
| 38 |
-
def INPUT_TYPES(s):
|
| 39 |
-
return {"required": {"vae": ("VAE", ),
|
| 40 |
-
"width": ("INT", {"default": 1280, "min": 16, "max": nodes.MAX_RESOLUTION, "step": 16}),
|
| 41 |
-
"height": ("INT", {"default": 704, "min": 16, "max": nodes.MAX_RESOLUTION, "step": 16}),
|
| 42 |
-
"length": ("INT", {"default": 121, "min": 1, "max": nodes.MAX_RESOLUTION, "step": 8}),
|
| 43 |
-
"batch_size": ("INT", {"default": 1, "min": 1, "max": 4096}),
|
| 44 |
-
},
|
| 45 |
-
"optional": {"start_image": ("IMAGE", ),
|
| 46 |
-
"end_image": ("IMAGE", ),
|
| 47 |
-
}}
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
RETURN_TYPES = ("LATENT",)
|
| 51 |
-
FUNCTION = "encode"
|
| 52 |
-
|
| 53 |
-
CATEGORY = "conditioning/inpaint"
|
| 54 |
-
|
| 55 |
-
def encode(self, vae, width, height, length, batch_size, start_image=None, end_image=None):
|
| 56 |
-
latent = torch.zeros([1, 16, ((length - 1) // 8) + 1, height // 8, width // 8], device=comfy.model_management.intermediate_device())
|
| 57 |
-
if start_image is None and end_image is None:
|
| 58 |
-
out_latent = {}
|
| 59 |
-
out_latent["samples"] = latent
|
| 60 |
-
return (out_latent,)
|
| 61 |
-
|
| 62 |
-
mask = torch.ones([latent.shape[0], 1, ((length - 1) // 8) + 1, latent.shape[-2], latent.shape[-1]], device=comfy.model_management.intermediate_device())
|
| 63 |
-
|
| 64 |
-
if start_image is not None:
|
| 65 |
-
latent_temp = vae_encode_with_padding(vae, start_image, width, height, length, padding=1)
|
| 66 |
-
latent[:, :, :latent_temp.shape[-3]] = latent_temp
|
| 67 |
-
mask[:, :, :latent_temp.shape[-3]] *= 0.0
|
| 68 |
-
|
| 69 |
-
if end_image is not None:
|
| 70 |
-
latent_temp = vae_encode_with_padding(vae, end_image, width, height, length, padding=0)
|
| 71 |
-
latent[:, :, -latent_temp.shape[-3]:] = latent_temp
|
| 72 |
-
mask[:, :, -latent_temp.shape[-3]:] *= 0.0
|
| 73 |
-
|
| 74 |
-
out_latent = {}
|
| 75 |
-
out_latent["samples"] = latent.repeat((batch_size, ) + (1,) * (latent.ndim - 1))
|
| 76 |
-
out_latent["noise_mask"] = mask.repeat((batch_size, ) + (1,) * (mask.ndim - 1))
|
| 77 |
-
return (out_latent,)
|
| 78 |
-
|
| 79 |
-
class CosmosPredict2ImageToVideoLatent:
|
| 80 |
-
@classmethod
|
| 81 |
-
def INPUT_TYPES(s):
|
| 82 |
-
return {"required": {"vae": ("VAE", ),
|
| 83 |
-
"width": ("INT", {"default": 848, "min": 16, "max": nodes.MAX_RESOLUTION, "step": 16}),
|
| 84 |
-
"height": ("INT", {"default": 480, "min": 16, "max": nodes.MAX_RESOLUTION, "step": 16}),
|
| 85 |
-
"length": ("INT", {"default": 93, "min": 1, "max": nodes.MAX_RESOLUTION, "step": 4}),
|
| 86 |
-
"batch_size": ("INT", {"default": 1, "min": 1, "max": 4096}),
|
| 87 |
-
},
|
| 88 |
-
"optional": {"start_image": ("IMAGE", ),
|
| 89 |
-
"end_image": ("IMAGE", ),
|
| 90 |
-
}}
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
RETURN_TYPES = ("LATENT",)
|
| 94 |
-
FUNCTION = "encode"
|
| 95 |
-
|
| 96 |
-
CATEGORY = "conditioning/inpaint"
|
| 97 |
-
|
| 98 |
-
def encode(self, vae, width, height, length, batch_size, start_image=None, end_image=None):
|
| 99 |
-
latent = torch.zeros([1, 16, ((length - 1) // 4) + 1, height // 8, width // 8], device=comfy.model_management.intermediate_device())
|
| 100 |
-
if start_image is None and end_image is None:
|
| 101 |
-
out_latent = {}
|
| 102 |
-
out_latent["samples"] = latent
|
| 103 |
-
return (out_latent,)
|
| 104 |
-
|
| 105 |
-
mask = torch.ones([latent.shape[0], 1, ((length - 1) // 4) + 1, latent.shape[-2], latent.shape[-1]], device=comfy.model_management.intermediate_device())
|
| 106 |
-
|
| 107 |
-
if start_image is not None:
|
| 108 |
-
latent_temp = vae_encode_with_padding(vae, start_image, width, height, length, padding=1)
|
| 109 |
-
latent[:, :, :latent_temp.shape[-3]] = latent_temp
|
| 110 |
-
mask[:, :, :latent_temp.shape[-3]] *= 0.0
|
| 111 |
-
|
| 112 |
-
if end_image is not None:
|
| 113 |
-
latent_temp = vae_encode_with_padding(vae, end_image, width, height, length, padding=0)
|
| 114 |
-
latent[:, :, -latent_temp.shape[-3]:] = latent_temp
|
| 115 |
-
mask[:, :, -latent_temp.shape[-3]:] *= 0.0
|
| 116 |
-
|
| 117 |
-
out_latent = {}
|
| 118 |
-
latent_format = comfy.latent_formats.Wan21()
|
| 119 |
-
latent = latent_format.process_out(latent) * mask + latent * (1.0 - mask)
|
| 120 |
-
out_latent["samples"] = latent.repeat((batch_size, ) + (1,) * (latent.ndim - 1))
|
| 121 |
-
out_latent["noise_mask"] = mask.repeat((batch_size, ) + (1,) * (mask.ndim - 1))
|
| 122 |
-
return (out_latent,)
|
| 123 |
-
|
| 124 |
-
NODE_CLASS_MAPPINGS = {
|
| 125 |
-
"EmptyCosmosLatentVideo": EmptyCosmosLatentVideo,
|
| 126 |
-
"CosmosImageToVideoLatent": CosmosImageToVideoLatent,
|
| 127 |
-
"CosmosPredict2ImageToVideoLatent": CosmosPredict2ImageToVideoLatent,
|
| 128 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
comfy_extras/nodes_custom_sampler.py
DELETED
|
@@ -1,932 +0,0 @@
|
|
| 1 |
-
import math
|
| 2 |
-
import comfy.samplers
|
| 3 |
-
import comfy.sample
|
| 4 |
-
from comfy.k_diffusion import sampling as k_diffusion_sampling
|
| 5 |
-
from comfy.k_diffusion import sa_solver
|
| 6 |
-
from comfy.comfy_types import IO, ComfyNodeABC, InputTypeDict
|
| 7 |
-
import latent_preview
|
| 8 |
-
import torch
|
| 9 |
-
import comfy.utils
|
| 10 |
-
import node_helpers
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
class BasicScheduler:
|
| 14 |
-
@classmethod
|
| 15 |
-
def INPUT_TYPES(s):
|
| 16 |
-
return {"required":
|
| 17 |
-
{"model": ("MODEL",),
|
| 18 |
-
"scheduler": (comfy.samplers.SCHEDULER_NAMES, ),
|
| 19 |
-
"steps": ("INT", {"default": 20, "min": 1, "max": 10000}),
|
| 20 |
-
"denoise": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}),
|
| 21 |
-
}
|
| 22 |
-
}
|
| 23 |
-
RETURN_TYPES = ("SIGMAS",)
|
| 24 |
-
CATEGORY = "sampling/custom_sampling/schedulers"
|
| 25 |
-
|
| 26 |
-
FUNCTION = "get_sigmas"
|
| 27 |
-
|
| 28 |
-
def get_sigmas(self, model, scheduler, steps, denoise):
|
| 29 |
-
total_steps = steps
|
| 30 |
-
if denoise < 1.0:
|
| 31 |
-
if denoise <= 0.0:
|
| 32 |
-
return (torch.FloatTensor([]),)
|
| 33 |
-
total_steps = int(steps/denoise)
|
| 34 |
-
|
| 35 |
-
sigmas = comfy.samplers.calculate_sigmas(model.get_model_object("model_sampling"), scheduler, total_steps).cpu()
|
| 36 |
-
sigmas = sigmas[-(steps + 1):]
|
| 37 |
-
return (sigmas, )
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
class KarrasScheduler:
|
| 41 |
-
@classmethod
|
| 42 |
-
def INPUT_TYPES(s):
|
| 43 |
-
return {"required":
|
| 44 |
-
{"steps": ("INT", {"default": 20, "min": 1, "max": 10000}),
|
| 45 |
-
"sigma_max": ("FLOAT", {"default": 14.614642, "min": 0.0, "max": 5000.0, "step":0.01, "round": False}),
|
| 46 |
-
"sigma_min": ("FLOAT", {"default": 0.0291675, "min": 0.0, "max": 5000.0, "step":0.01, "round": False}),
|
| 47 |
-
"rho": ("FLOAT", {"default": 7.0, "min": 0.0, "max": 100.0, "step":0.01, "round": False}),
|
| 48 |
-
}
|
| 49 |
-
}
|
| 50 |
-
RETURN_TYPES = ("SIGMAS",)
|
| 51 |
-
CATEGORY = "sampling/custom_sampling/schedulers"
|
| 52 |
-
|
| 53 |
-
FUNCTION = "get_sigmas"
|
| 54 |
-
|
| 55 |
-
def get_sigmas(self, steps, sigma_max, sigma_min, rho):
|
| 56 |
-
sigmas = k_diffusion_sampling.get_sigmas_karras(n=steps, sigma_min=sigma_min, sigma_max=sigma_max, rho=rho)
|
| 57 |
-
return (sigmas, )
|
| 58 |
-
|
| 59 |
-
class ExponentialScheduler:
|
| 60 |
-
@classmethod
|
| 61 |
-
def INPUT_TYPES(s):
|
| 62 |
-
return {"required":
|
| 63 |
-
{"steps": ("INT", {"default": 20, "min": 1, "max": 10000}),
|
| 64 |
-
"sigma_max": ("FLOAT", {"default": 14.614642, "min": 0.0, "max": 5000.0, "step":0.01, "round": False}),
|
| 65 |
-
"sigma_min": ("FLOAT", {"default": 0.0291675, "min": 0.0, "max": 5000.0, "step":0.01, "round": False}),
|
| 66 |
-
}
|
| 67 |
-
}
|
| 68 |
-
RETURN_TYPES = ("SIGMAS",)
|
| 69 |
-
CATEGORY = "sampling/custom_sampling/schedulers"
|
| 70 |
-
|
| 71 |
-
FUNCTION = "get_sigmas"
|
| 72 |
-
|
| 73 |
-
def get_sigmas(self, steps, sigma_max, sigma_min):
|
| 74 |
-
sigmas = k_diffusion_sampling.get_sigmas_exponential(n=steps, sigma_min=sigma_min, sigma_max=sigma_max)
|
| 75 |
-
return (sigmas, )
|
| 76 |
-
|
| 77 |
-
class PolyexponentialScheduler:
|
| 78 |
-
@classmethod
|
| 79 |
-
def INPUT_TYPES(s):
|
| 80 |
-
return {"required":
|
| 81 |
-
{"steps": ("INT", {"default": 20, "min": 1, "max": 10000}),
|
| 82 |
-
"sigma_max": ("FLOAT", {"default": 14.614642, "min": 0.0, "max": 5000.0, "step":0.01, "round": False}),
|
| 83 |
-
"sigma_min": ("FLOAT", {"default": 0.0291675, "min": 0.0, "max": 5000.0, "step":0.01, "round": False}),
|
| 84 |
-
"rho": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 100.0, "step":0.01, "round": False}),
|
| 85 |
-
}
|
| 86 |
-
}
|
| 87 |
-
RETURN_TYPES = ("SIGMAS",)
|
| 88 |
-
CATEGORY = "sampling/custom_sampling/schedulers"
|
| 89 |
-
|
| 90 |
-
FUNCTION = "get_sigmas"
|
| 91 |
-
|
| 92 |
-
def get_sigmas(self, steps, sigma_max, sigma_min, rho):
|
| 93 |
-
sigmas = k_diffusion_sampling.get_sigmas_polyexponential(n=steps, sigma_min=sigma_min, sigma_max=sigma_max, rho=rho)
|
| 94 |
-
return (sigmas, )
|
| 95 |
-
|
| 96 |
-
class LaplaceScheduler:
|
| 97 |
-
@classmethod
|
| 98 |
-
def INPUT_TYPES(s):
|
| 99 |
-
return {"required":
|
| 100 |
-
{"steps": ("INT", {"default": 20, "min": 1, "max": 10000}),
|
| 101 |
-
"sigma_max": ("FLOAT", {"default": 14.614642, "min": 0.0, "max": 5000.0, "step":0.01, "round": False}),
|
| 102 |
-
"sigma_min": ("FLOAT", {"default": 0.0291675, "min": 0.0, "max": 5000.0, "step":0.01, "round": False}),
|
| 103 |
-
"mu": ("FLOAT", {"default": 0.0, "min": -10.0, "max": 10.0, "step":0.1, "round": False}),
|
| 104 |
-
"beta": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 10.0, "step":0.1, "round": False}),
|
| 105 |
-
}
|
| 106 |
-
}
|
| 107 |
-
RETURN_TYPES = ("SIGMAS",)
|
| 108 |
-
CATEGORY = "sampling/custom_sampling/schedulers"
|
| 109 |
-
|
| 110 |
-
FUNCTION = "get_sigmas"
|
| 111 |
-
|
| 112 |
-
def get_sigmas(self, steps, sigma_max, sigma_min, mu, beta):
|
| 113 |
-
sigmas = k_diffusion_sampling.get_sigmas_laplace(n=steps, sigma_min=sigma_min, sigma_max=sigma_max, mu=mu, beta=beta)
|
| 114 |
-
return (sigmas, )
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
class SDTurboScheduler:
|
| 118 |
-
@classmethod
|
| 119 |
-
def INPUT_TYPES(s):
|
| 120 |
-
return {"required":
|
| 121 |
-
{"model": ("MODEL",),
|
| 122 |
-
"steps": ("INT", {"default": 1, "min": 1, "max": 10}),
|
| 123 |
-
"denoise": ("FLOAT", {"default": 1.0, "min": 0, "max": 1.0, "step": 0.01}),
|
| 124 |
-
}
|
| 125 |
-
}
|
| 126 |
-
RETURN_TYPES = ("SIGMAS",)
|
| 127 |
-
CATEGORY = "sampling/custom_sampling/schedulers"
|
| 128 |
-
|
| 129 |
-
FUNCTION = "get_sigmas"
|
| 130 |
-
|
| 131 |
-
def get_sigmas(self, model, steps, denoise):
|
| 132 |
-
start_step = 10 - int(10 * denoise)
|
| 133 |
-
timesteps = torch.flip(torch.arange(1, 11) * 100 - 1, (0,))[start_step:start_step + steps]
|
| 134 |
-
sigmas = model.get_model_object("model_sampling").sigma(timesteps)
|
| 135 |
-
sigmas = torch.cat([sigmas, sigmas.new_zeros([1])])
|
| 136 |
-
return (sigmas, )
|
| 137 |
-
|
| 138 |
-
class BetaSamplingScheduler:
|
| 139 |
-
@classmethod
|
| 140 |
-
def INPUT_TYPES(s):
|
| 141 |
-
return {"required":
|
| 142 |
-
{"model": ("MODEL",),
|
| 143 |
-
"steps": ("INT", {"default": 20, "min": 1, "max": 10000}),
|
| 144 |
-
"alpha": ("FLOAT", {"default": 0.6, "min": 0.0, "max": 50.0, "step":0.01, "round": False}),
|
| 145 |
-
"beta": ("FLOAT", {"default": 0.6, "min": 0.0, "max": 50.0, "step":0.01, "round": False}),
|
| 146 |
-
}
|
| 147 |
-
}
|
| 148 |
-
RETURN_TYPES = ("SIGMAS",)
|
| 149 |
-
CATEGORY = "sampling/custom_sampling/schedulers"
|
| 150 |
-
|
| 151 |
-
FUNCTION = "get_sigmas"
|
| 152 |
-
|
| 153 |
-
def get_sigmas(self, model, steps, alpha, beta):
|
| 154 |
-
sigmas = comfy.samplers.beta_scheduler(model.get_model_object("model_sampling"), steps, alpha=alpha, beta=beta)
|
| 155 |
-
return (sigmas, )
|
| 156 |
-
|
| 157 |
-
class VPScheduler:
|
| 158 |
-
@classmethod
|
| 159 |
-
def INPUT_TYPES(s):
|
| 160 |
-
return {"required":
|
| 161 |
-
{"steps": ("INT", {"default": 20, "min": 1, "max": 10000}),
|
| 162 |
-
"beta_d": ("FLOAT", {"default": 19.9, "min": 0.0, "max": 5000.0, "step":0.01, "round": False}), #TODO: fix default values
|
| 163 |
-
"beta_min": ("FLOAT", {"default": 0.1, "min": 0.0, "max": 5000.0, "step":0.01, "round": False}),
|
| 164 |
-
"eps_s": ("FLOAT", {"default": 0.001, "min": 0.0, "max": 1.0, "step":0.0001, "round": False}),
|
| 165 |
-
}
|
| 166 |
-
}
|
| 167 |
-
RETURN_TYPES = ("SIGMAS",)
|
| 168 |
-
CATEGORY = "sampling/custom_sampling/schedulers"
|
| 169 |
-
|
| 170 |
-
FUNCTION = "get_sigmas"
|
| 171 |
-
|
| 172 |
-
def get_sigmas(self, steps, beta_d, beta_min, eps_s):
|
| 173 |
-
sigmas = k_diffusion_sampling.get_sigmas_vp(n=steps, beta_d=beta_d, beta_min=beta_min, eps_s=eps_s)
|
| 174 |
-
return (sigmas, )
|
| 175 |
-
|
| 176 |
-
class SplitSigmas:
|
| 177 |
-
@classmethod
|
| 178 |
-
def INPUT_TYPES(s):
|
| 179 |
-
return {"required":
|
| 180 |
-
{"sigmas": ("SIGMAS", ),
|
| 181 |
-
"step": ("INT", {"default": 0, "min": 0, "max": 10000}),
|
| 182 |
-
}
|
| 183 |
-
}
|
| 184 |
-
RETURN_TYPES = ("SIGMAS","SIGMAS")
|
| 185 |
-
RETURN_NAMES = ("high_sigmas", "low_sigmas")
|
| 186 |
-
CATEGORY = "sampling/custom_sampling/sigmas"
|
| 187 |
-
|
| 188 |
-
FUNCTION = "get_sigmas"
|
| 189 |
-
|
| 190 |
-
def get_sigmas(self, sigmas, step):
|
| 191 |
-
sigmas1 = sigmas[:step + 1]
|
| 192 |
-
sigmas2 = sigmas[step:]
|
| 193 |
-
return (sigmas1, sigmas2)
|
| 194 |
-
|
| 195 |
-
class SplitSigmasDenoise:
|
| 196 |
-
@classmethod
|
| 197 |
-
def INPUT_TYPES(s):
|
| 198 |
-
return {"required":
|
| 199 |
-
{"sigmas": ("SIGMAS", ),
|
| 200 |
-
"denoise": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}),
|
| 201 |
-
}
|
| 202 |
-
}
|
| 203 |
-
RETURN_TYPES = ("SIGMAS","SIGMAS")
|
| 204 |
-
RETURN_NAMES = ("high_sigmas", "low_sigmas")
|
| 205 |
-
CATEGORY = "sampling/custom_sampling/sigmas"
|
| 206 |
-
|
| 207 |
-
FUNCTION = "get_sigmas"
|
| 208 |
-
|
| 209 |
-
def get_sigmas(self, sigmas, denoise):
|
| 210 |
-
steps = max(sigmas.shape[-1] - 1, 0)
|
| 211 |
-
total_steps = round(steps * denoise)
|
| 212 |
-
sigmas1 = sigmas[:-(total_steps)]
|
| 213 |
-
sigmas2 = sigmas[-(total_steps + 1):]
|
| 214 |
-
return (sigmas1, sigmas2)
|
| 215 |
-
|
| 216 |
-
class FlipSigmas:
|
| 217 |
-
@classmethod
|
| 218 |
-
def INPUT_TYPES(s):
|
| 219 |
-
return {"required":
|
| 220 |
-
{"sigmas": ("SIGMAS", ),
|
| 221 |
-
}
|
| 222 |
-
}
|
| 223 |
-
RETURN_TYPES = ("SIGMAS",)
|
| 224 |
-
CATEGORY = "sampling/custom_sampling/sigmas"
|
| 225 |
-
|
| 226 |
-
FUNCTION = "get_sigmas"
|
| 227 |
-
|
| 228 |
-
def get_sigmas(self, sigmas):
|
| 229 |
-
if len(sigmas) == 0:
|
| 230 |
-
return (sigmas,)
|
| 231 |
-
|
| 232 |
-
sigmas = sigmas.flip(0)
|
| 233 |
-
if sigmas[0] == 0:
|
| 234 |
-
sigmas[0] = 0.0001
|
| 235 |
-
return (sigmas,)
|
| 236 |
-
|
| 237 |
-
class SetFirstSigma:
|
| 238 |
-
@classmethod
|
| 239 |
-
def INPUT_TYPES(s):
|
| 240 |
-
return {"required":
|
| 241 |
-
{"sigmas": ("SIGMAS", ),
|
| 242 |
-
"sigma": ("FLOAT", {"default": 136.0, "min": 0.0, "max": 20000.0, "step": 0.001, "round": False}),
|
| 243 |
-
}
|
| 244 |
-
}
|
| 245 |
-
RETURN_TYPES = ("SIGMAS",)
|
| 246 |
-
CATEGORY = "sampling/custom_sampling/sigmas"
|
| 247 |
-
|
| 248 |
-
FUNCTION = "set_first_sigma"
|
| 249 |
-
|
| 250 |
-
def set_first_sigma(self, sigmas, sigma):
|
| 251 |
-
sigmas = sigmas.clone()
|
| 252 |
-
sigmas[0] = sigma
|
| 253 |
-
return (sigmas, )
|
| 254 |
-
|
| 255 |
-
class ExtendIntermediateSigmas:
|
| 256 |
-
@classmethod
|
| 257 |
-
def INPUT_TYPES(s):
|
| 258 |
-
return {"required":
|
| 259 |
-
{"sigmas": ("SIGMAS", ),
|
| 260 |
-
"steps": ("INT", {"default": 2, "min": 1, "max": 100}),
|
| 261 |
-
"start_at_sigma": ("FLOAT", {"default": -1.0, "min": -1.0, "max": 20000.0, "step": 0.01, "round": False}),
|
| 262 |
-
"end_at_sigma": ("FLOAT", {"default": 12.0, "min": 0.0, "max": 20000.0, "step": 0.01, "round": False}),
|
| 263 |
-
"spacing": (['linear', 'cosine', 'sine'],),
|
| 264 |
-
}
|
| 265 |
-
}
|
| 266 |
-
RETURN_TYPES = ("SIGMAS",)
|
| 267 |
-
CATEGORY = "sampling/custom_sampling/sigmas"
|
| 268 |
-
|
| 269 |
-
FUNCTION = "extend"
|
| 270 |
-
|
| 271 |
-
def extend(self, sigmas: torch.Tensor, steps: int, start_at_sigma: float, end_at_sigma: float, spacing: str):
|
| 272 |
-
if start_at_sigma < 0:
|
| 273 |
-
start_at_sigma = float("inf")
|
| 274 |
-
|
| 275 |
-
interpolator = {
|
| 276 |
-
'linear': lambda x: x,
|
| 277 |
-
'cosine': lambda x: torch.sin(x*math.pi/2),
|
| 278 |
-
'sine': lambda x: 1 - torch.cos(x*math.pi/2)
|
| 279 |
-
}[spacing]
|
| 280 |
-
|
| 281 |
-
# linear space for our interpolation function
|
| 282 |
-
x = torch.linspace(0, 1, steps + 1, device=sigmas.device)[1:-1]
|
| 283 |
-
computed_spacing = interpolator(x)
|
| 284 |
-
|
| 285 |
-
extended_sigmas = []
|
| 286 |
-
for i in range(len(sigmas) - 1):
|
| 287 |
-
sigma_current = sigmas[i]
|
| 288 |
-
sigma_next = sigmas[i+1]
|
| 289 |
-
|
| 290 |
-
extended_sigmas.append(sigma_current)
|
| 291 |
-
|
| 292 |
-
if end_at_sigma <= sigma_current <= start_at_sigma:
|
| 293 |
-
interpolated_steps = computed_spacing * (sigma_next - sigma_current) + sigma_current
|
| 294 |
-
extended_sigmas.extend(interpolated_steps.tolist())
|
| 295 |
-
|
| 296 |
-
# Add the last sigma value
|
| 297 |
-
if len(sigmas) > 0:
|
| 298 |
-
extended_sigmas.append(sigmas[-1])
|
| 299 |
-
|
| 300 |
-
extended_sigmas = torch.FloatTensor(extended_sigmas)
|
| 301 |
-
|
| 302 |
-
return (extended_sigmas,)
|
| 303 |
-
|
| 304 |
-
|
| 305 |
-
class SamplingPercentToSigma:
|
| 306 |
-
@classmethod
|
| 307 |
-
def INPUT_TYPES(cls) -> InputTypeDict:
|
| 308 |
-
return {
|
| 309 |
-
"required": {
|
| 310 |
-
"model": (IO.MODEL, {}),
|
| 311 |
-
"sampling_percent": (IO.FLOAT, {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.0001}),
|
| 312 |
-
"return_actual_sigma": (IO.BOOLEAN, {"default": False, "tooltip": "Return the actual sigma value instead of the value used for interval checks.\nThis only affects results at 0.0 and 1.0."}),
|
| 313 |
-
}
|
| 314 |
-
}
|
| 315 |
-
|
| 316 |
-
RETURN_TYPES = (IO.FLOAT,)
|
| 317 |
-
RETURN_NAMES = ("sigma_value",)
|
| 318 |
-
CATEGORY = "sampling/custom_sampling/sigmas"
|
| 319 |
-
|
| 320 |
-
FUNCTION = "get_sigma"
|
| 321 |
-
|
| 322 |
-
def get_sigma(self, model, sampling_percent, return_actual_sigma):
|
| 323 |
-
model_sampling = model.get_model_object("model_sampling")
|
| 324 |
-
sigma_val = model_sampling.percent_to_sigma(sampling_percent)
|
| 325 |
-
if return_actual_sigma:
|
| 326 |
-
if sampling_percent == 0.0:
|
| 327 |
-
sigma_val = model_sampling.sigma_max.item()
|
| 328 |
-
elif sampling_percent == 1.0:
|
| 329 |
-
sigma_val = model_sampling.sigma_min.item()
|
| 330 |
-
return (sigma_val,)
|
| 331 |
-
|
| 332 |
-
|
| 333 |
-
class KSamplerSelect:
|
| 334 |
-
@classmethod
|
| 335 |
-
def INPUT_TYPES(s):
|
| 336 |
-
return {"required":
|
| 337 |
-
{"sampler_name": (comfy.samplers.SAMPLER_NAMES, ),
|
| 338 |
-
}
|
| 339 |
-
}
|
| 340 |
-
RETURN_TYPES = ("SAMPLER",)
|
| 341 |
-
CATEGORY = "sampling/custom_sampling/samplers"
|
| 342 |
-
|
| 343 |
-
FUNCTION = "get_sampler"
|
| 344 |
-
|
| 345 |
-
def get_sampler(self, sampler_name):
|
| 346 |
-
sampler = comfy.samplers.sampler_object(sampler_name)
|
| 347 |
-
return (sampler, )
|
| 348 |
-
|
| 349 |
-
class SamplerDPMPP_3M_SDE:
|
| 350 |
-
@classmethod
|
| 351 |
-
def INPUT_TYPES(s):
|
| 352 |
-
return {"required":
|
| 353 |
-
{"eta": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 100.0, "step":0.01, "round": False}),
|
| 354 |
-
"s_noise": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 100.0, "step":0.01, "round": False}),
|
| 355 |
-
"noise_device": (['gpu', 'cpu'], ),
|
| 356 |
-
}
|
| 357 |
-
}
|
| 358 |
-
RETURN_TYPES = ("SAMPLER",)
|
| 359 |
-
CATEGORY = "sampling/custom_sampling/samplers"
|
| 360 |
-
|
| 361 |
-
FUNCTION = "get_sampler"
|
| 362 |
-
|
| 363 |
-
def get_sampler(self, eta, s_noise, noise_device):
|
| 364 |
-
if noise_device == 'cpu':
|
| 365 |
-
sampler_name = "dpmpp_3m_sde"
|
| 366 |
-
else:
|
| 367 |
-
sampler_name = "dpmpp_3m_sde_gpu"
|
| 368 |
-
sampler = comfy.samplers.ksampler(sampler_name, {"eta": eta, "s_noise": s_noise})
|
| 369 |
-
return (sampler, )
|
| 370 |
-
|
| 371 |
-
class SamplerDPMPP_2M_SDE:
|
| 372 |
-
@classmethod
|
| 373 |
-
def INPUT_TYPES(s):
|
| 374 |
-
return {"required":
|
| 375 |
-
{"solver_type": (['midpoint', 'heun'], ),
|
| 376 |
-
"eta": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 100.0, "step":0.01, "round": False}),
|
| 377 |
-
"s_noise": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 100.0, "step":0.01, "round": False}),
|
| 378 |
-
"noise_device": (['gpu', 'cpu'], ),
|
| 379 |
-
}
|
| 380 |
-
}
|
| 381 |
-
RETURN_TYPES = ("SAMPLER",)
|
| 382 |
-
CATEGORY = "sampling/custom_sampling/samplers"
|
| 383 |
-
|
| 384 |
-
FUNCTION = "get_sampler"
|
| 385 |
-
|
| 386 |
-
def get_sampler(self, solver_type, eta, s_noise, noise_device):
|
| 387 |
-
if noise_device == 'cpu':
|
| 388 |
-
sampler_name = "dpmpp_2m_sde"
|
| 389 |
-
else:
|
| 390 |
-
sampler_name = "dpmpp_2m_sde_gpu"
|
| 391 |
-
sampler = comfy.samplers.ksampler(sampler_name, {"eta": eta, "s_noise": s_noise, "solver_type": solver_type})
|
| 392 |
-
return (sampler, )
|
| 393 |
-
|
| 394 |
-
|
| 395 |
-
class SamplerDPMPP_SDE:
|
| 396 |
-
@classmethod
|
| 397 |
-
def INPUT_TYPES(s):
|
| 398 |
-
return {"required":
|
| 399 |
-
{"eta": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 100.0, "step":0.01, "round": False}),
|
| 400 |
-
"s_noise": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 100.0, "step":0.01, "round": False}),
|
| 401 |
-
"r": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 100.0, "step":0.01, "round": False}),
|
| 402 |
-
"noise_device": (['gpu', 'cpu'], ),
|
| 403 |
-
}
|
| 404 |
-
}
|
| 405 |
-
RETURN_TYPES = ("SAMPLER",)
|
| 406 |
-
CATEGORY = "sampling/custom_sampling/samplers"
|
| 407 |
-
|
| 408 |
-
FUNCTION = "get_sampler"
|
| 409 |
-
|
| 410 |
-
def get_sampler(self, eta, s_noise, r, noise_device):
|
| 411 |
-
if noise_device == 'cpu':
|
| 412 |
-
sampler_name = "dpmpp_sde"
|
| 413 |
-
else:
|
| 414 |
-
sampler_name = "dpmpp_sde_gpu"
|
| 415 |
-
sampler = comfy.samplers.ksampler(sampler_name, {"eta": eta, "s_noise": s_noise, "r": r})
|
| 416 |
-
return (sampler, )
|
| 417 |
-
|
| 418 |
-
class SamplerDPMPP_2S_Ancestral:
|
| 419 |
-
@classmethod
|
| 420 |
-
def INPUT_TYPES(s):
|
| 421 |
-
return {"required":
|
| 422 |
-
{"eta": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 100.0, "step":0.01, "round": False}),
|
| 423 |
-
"s_noise": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 100.0, "step":0.01, "round": False}),
|
| 424 |
-
}
|
| 425 |
-
}
|
| 426 |
-
RETURN_TYPES = ("SAMPLER",)
|
| 427 |
-
CATEGORY = "sampling/custom_sampling/samplers"
|
| 428 |
-
|
| 429 |
-
FUNCTION = "get_sampler"
|
| 430 |
-
|
| 431 |
-
def get_sampler(self, eta, s_noise):
|
| 432 |
-
sampler = comfy.samplers.ksampler("dpmpp_2s_ancestral", {"eta": eta, "s_noise": s_noise})
|
| 433 |
-
return (sampler, )
|
| 434 |
-
|
| 435 |
-
class SamplerEulerAncestral:
|
| 436 |
-
@classmethod
|
| 437 |
-
def INPUT_TYPES(s):
|
| 438 |
-
return {"required":
|
| 439 |
-
{"eta": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 100.0, "step":0.01, "round": False}),
|
| 440 |
-
"s_noise": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 100.0, "step":0.01, "round": False}),
|
| 441 |
-
}
|
| 442 |
-
}
|
| 443 |
-
RETURN_TYPES = ("SAMPLER",)
|
| 444 |
-
CATEGORY = "sampling/custom_sampling/samplers"
|
| 445 |
-
|
| 446 |
-
FUNCTION = "get_sampler"
|
| 447 |
-
|
| 448 |
-
def get_sampler(self, eta, s_noise):
|
| 449 |
-
sampler = comfy.samplers.ksampler("euler_ancestral", {"eta": eta, "s_noise": s_noise})
|
| 450 |
-
return (sampler, )
|
| 451 |
-
|
| 452 |
-
class SamplerEulerAncestralCFGPP:
|
| 453 |
-
@classmethod
|
| 454 |
-
def INPUT_TYPES(s):
|
| 455 |
-
return {
|
| 456 |
-
"required": {
|
| 457 |
-
"eta": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step":0.01, "round": False}),
|
| 458 |
-
"s_noise": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step":0.01, "round": False}),
|
| 459 |
-
}}
|
| 460 |
-
RETURN_TYPES = ("SAMPLER",)
|
| 461 |
-
CATEGORY = "sampling/custom_sampling/samplers"
|
| 462 |
-
|
| 463 |
-
FUNCTION = "get_sampler"
|
| 464 |
-
|
| 465 |
-
def get_sampler(self, eta, s_noise):
|
| 466 |
-
sampler = comfy.samplers.ksampler(
|
| 467 |
-
"euler_ancestral_cfg_pp",
|
| 468 |
-
{"eta": eta, "s_noise": s_noise})
|
| 469 |
-
return (sampler, )
|
| 470 |
-
|
| 471 |
-
class SamplerLMS:
|
| 472 |
-
@classmethod
|
| 473 |
-
def INPUT_TYPES(s):
|
| 474 |
-
return {"required":
|
| 475 |
-
{"order": ("INT", {"default": 4, "min": 1, "max": 100}),
|
| 476 |
-
}
|
| 477 |
-
}
|
| 478 |
-
RETURN_TYPES = ("SAMPLER",)
|
| 479 |
-
CATEGORY = "sampling/custom_sampling/samplers"
|
| 480 |
-
|
| 481 |
-
FUNCTION = "get_sampler"
|
| 482 |
-
|
| 483 |
-
def get_sampler(self, order):
|
| 484 |
-
sampler = comfy.samplers.ksampler("lms", {"order": order})
|
| 485 |
-
return (sampler, )
|
| 486 |
-
|
| 487 |
-
class SamplerDPMAdaptative:
|
| 488 |
-
@classmethod
|
| 489 |
-
def INPUT_TYPES(s):
|
| 490 |
-
return {"required":
|
| 491 |
-
{"order": ("INT", {"default": 3, "min": 2, "max": 3}),
|
| 492 |
-
"rtol": ("FLOAT", {"default": 0.05, "min": 0.0, "max": 100.0, "step":0.01, "round": False}),
|
| 493 |
-
"atol": ("FLOAT", {"default": 0.0078, "min": 0.0, "max": 100.0, "step":0.01, "round": False}),
|
| 494 |
-
"h_init": ("FLOAT", {"default": 0.05, "min": 0.0, "max": 100.0, "step":0.01, "round": False}),
|
| 495 |
-
"pcoeff": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 100.0, "step":0.01, "round": False}),
|
| 496 |
-
"icoeff": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 100.0, "step":0.01, "round": False}),
|
| 497 |
-
"dcoeff": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 100.0, "step":0.01, "round": False}),
|
| 498 |
-
"accept_safety": ("FLOAT", {"default": 0.81, "min": 0.0, "max": 100.0, "step":0.01, "round": False}),
|
| 499 |
-
"eta": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 100.0, "step":0.01, "round": False}),
|
| 500 |
-
"s_noise": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 100.0, "step":0.01, "round": False}),
|
| 501 |
-
}
|
| 502 |
-
}
|
| 503 |
-
RETURN_TYPES = ("SAMPLER",)
|
| 504 |
-
CATEGORY = "sampling/custom_sampling/samplers"
|
| 505 |
-
|
| 506 |
-
FUNCTION = "get_sampler"
|
| 507 |
-
|
| 508 |
-
def get_sampler(self, order, rtol, atol, h_init, pcoeff, icoeff, dcoeff, accept_safety, eta, s_noise):
|
| 509 |
-
sampler = comfy.samplers.ksampler("dpm_adaptive", {"order": order, "rtol": rtol, "atol": atol, "h_init": h_init, "pcoeff": pcoeff,
|
| 510 |
-
"icoeff": icoeff, "dcoeff": dcoeff, "accept_safety": accept_safety, "eta": eta,
|
| 511 |
-
"s_noise":s_noise })
|
| 512 |
-
return (sampler, )
|
| 513 |
-
|
| 514 |
-
|
| 515 |
-
class SamplerER_SDE(ComfyNodeABC):
|
| 516 |
-
@classmethod
|
| 517 |
-
def INPUT_TYPES(cls) -> InputTypeDict:
|
| 518 |
-
return {
|
| 519 |
-
"required": {
|
| 520 |
-
"solver_type": (IO.COMBO, {"options": ["ER-SDE", "Reverse-time SDE", "ODE"]}),
|
| 521 |
-
"max_stage": (IO.INT, {"default": 3, "min": 1, "max": 3}),
|
| 522 |
-
"eta": (
|
| 523 |
-
IO.FLOAT,
|
| 524 |
-
{"default": 1.0, "min": 0.0, "max": 100.0, "step": 0.01, "round": False, "tooltip": "Stochastic strength of reverse-time SDE.\nWhen eta=0, it reduces to deterministic ODE. This setting doesn't apply to ER-SDE solver type."},
|
| 525 |
-
),
|
| 526 |
-
"s_noise": (IO.FLOAT, {"default": 1.0, "min": 0.0, "max": 100.0, "step": 0.01, "round": False}),
|
| 527 |
-
}
|
| 528 |
-
}
|
| 529 |
-
|
| 530 |
-
RETURN_TYPES = (IO.SAMPLER,)
|
| 531 |
-
CATEGORY = "sampling/custom_sampling/samplers"
|
| 532 |
-
|
| 533 |
-
FUNCTION = "get_sampler"
|
| 534 |
-
|
| 535 |
-
def get_sampler(self, solver_type, max_stage, eta, s_noise):
|
| 536 |
-
if solver_type == "ODE" or (solver_type == "Reverse-time SDE" and eta == 0):
|
| 537 |
-
eta = 0
|
| 538 |
-
s_noise = 0
|
| 539 |
-
|
| 540 |
-
def reverse_time_sde_noise_scaler(x):
|
| 541 |
-
return x ** (eta + 1)
|
| 542 |
-
|
| 543 |
-
if solver_type == "ER-SDE":
|
| 544 |
-
# Use the default one in sample_er_sde()
|
| 545 |
-
noise_scaler = None
|
| 546 |
-
else:
|
| 547 |
-
noise_scaler = reverse_time_sde_noise_scaler
|
| 548 |
-
|
| 549 |
-
sampler_name = "er_sde"
|
| 550 |
-
sampler = comfy.samplers.ksampler(sampler_name, {"s_noise": s_noise, "noise_scaler": noise_scaler, "max_stage": max_stage})
|
| 551 |
-
return (sampler,)
|
| 552 |
-
|
| 553 |
-
|
| 554 |
-
class SamplerSASolver(ComfyNodeABC):
|
| 555 |
-
@classmethod
|
| 556 |
-
def INPUT_TYPES(cls) -> InputTypeDict:
|
| 557 |
-
return {
|
| 558 |
-
"required": {
|
| 559 |
-
"model": (IO.MODEL, {}),
|
| 560 |
-
"eta": (IO.FLOAT, {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.01, "round": False},),
|
| 561 |
-
"sde_start_percent": (IO.FLOAT, {"default": 0.2, "min": 0.0, "max": 1.0, "step": 0.001},),
|
| 562 |
-
"sde_end_percent": (IO.FLOAT, {"default": 0.8, "min": 0.0, "max": 1.0, "step": 0.001},),
|
| 563 |
-
"s_noise": (IO.FLOAT, {"default": 1.0, "min": 0.0, "max": 100.0, "step": 0.01, "round": False},),
|
| 564 |
-
"predictor_order": (IO.INT, {"default": 3, "min": 1, "max": 6}),
|
| 565 |
-
"corrector_order": (IO.INT, {"default": 4, "min": 0, "max": 6}),
|
| 566 |
-
"use_pece": (IO.BOOLEAN, {}),
|
| 567 |
-
"simple_order_2": (IO.BOOLEAN, {}),
|
| 568 |
-
}
|
| 569 |
-
}
|
| 570 |
-
|
| 571 |
-
RETURN_TYPES = (IO.SAMPLER,)
|
| 572 |
-
CATEGORY = "sampling/custom_sampling/samplers"
|
| 573 |
-
|
| 574 |
-
FUNCTION = "get_sampler"
|
| 575 |
-
|
| 576 |
-
def get_sampler(self, model, eta, sde_start_percent, sde_end_percent, s_noise, predictor_order, corrector_order, use_pece, simple_order_2):
|
| 577 |
-
model_sampling = model.get_model_object("model_sampling")
|
| 578 |
-
start_sigma = model_sampling.percent_to_sigma(sde_start_percent)
|
| 579 |
-
end_sigma = model_sampling.percent_to_sigma(sde_end_percent)
|
| 580 |
-
tau_func = sa_solver.get_tau_interval_func(start_sigma, end_sigma, eta=eta)
|
| 581 |
-
|
| 582 |
-
sampler_name = "sa_solver"
|
| 583 |
-
sampler = comfy.samplers.ksampler(
|
| 584 |
-
sampler_name,
|
| 585 |
-
{
|
| 586 |
-
"tau_func": tau_func,
|
| 587 |
-
"s_noise": s_noise,
|
| 588 |
-
"predictor_order": predictor_order,
|
| 589 |
-
"corrector_order": corrector_order,
|
| 590 |
-
"use_pece": use_pece,
|
| 591 |
-
"simple_order_2": simple_order_2,
|
| 592 |
-
},
|
| 593 |
-
)
|
| 594 |
-
return (sampler,)
|
| 595 |
-
|
| 596 |
-
|
| 597 |
-
class Noise_EmptyNoise:
|
| 598 |
-
def __init__(self):
|
| 599 |
-
self.seed = 0
|
| 600 |
-
|
| 601 |
-
def generate_noise(self, input_latent):
|
| 602 |
-
latent_image = input_latent["samples"]
|
| 603 |
-
return torch.zeros(latent_image.shape, dtype=latent_image.dtype, layout=latent_image.layout, device="cpu")
|
| 604 |
-
|
| 605 |
-
|
| 606 |
-
class Noise_RandomNoise:
|
| 607 |
-
def __init__(self, seed):
|
| 608 |
-
self.seed = seed
|
| 609 |
-
|
| 610 |
-
def generate_noise(self, input_latent):
|
| 611 |
-
latent_image = input_latent["samples"]
|
| 612 |
-
batch_inds = input_latent["batch_index"] if "batch_index" in input_latent else None
|
| 613 |
-
return comfy.sample.prepare_noise(latent_image, self.seed, batch_inds)
|
| 614 |
-
|
| 615 |
-
class SamplerCustom:
|
| 616 |
-
@classmethod
|
| 617 |
-
def INPUT_TYPES(s):
|
| 618 |
-
return {"required":
|
| 619 |
-
{"model": ("MODEL",),
|
| 620 |
-
"add_noise": ("BOOLEAN", {"default": True}),
|
| 621 |
-
"noise_seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff, "control_after_generate": True}),
|
| 622 |
-
"cfg": ("FLOAT", {"default": 8.0, "min": 0.0, "max": 100.0, "step":0.1, "round": 0.01}),
|
| 623 |
-
"positive": ("CONDITIONING", ),
|
| 624 |
-
"negative": ("CONDITIONING", ),
|
| 625 |
-
"sampler": ("SAMPLER", ),
|
| 626 |
-
"sigmas": ("SIGMAS", ),
|
| 627 |
-
"latent_image": ("LATENT", ),
|
| 628 |
-
}
|
| 629 |
-
}
|
| 630 |
-
|
| 631 |
-
RETURN_TYPES = ("LATENT","LATENT")
|
| 632 |
-
RETURN_NAMES = ("output", "denoised_output")
|
| 633 |
-
|
| 634 |
-
FUNCTION = "sample"
|
| 635 |
-
|
| 636 |
-
CATEGORY = "sampling/custom_sampling"
|
| 637 |
-
|
| 638 |
-
def sample(self, model, add_noise, noise_seed, cfg, positive, negative, sampler, sigmas, latent_image):
|
| 639 |
-
latent = latent_image
|
| 640 |
-
latent_image = latent["samples"]
|
| 641 |
-
latent = latent.copy()
|
| 642 |
-
latent_image = comfy.sample.fix_empty_latent_channels(model, latent_image)
|
| 643 |
-
latent["samples"] = latent_image
|
| 644 |
-
|
| 645 |
-
if not add_noise:
|
| 646 |
-
noise = Noise_EmptyNoise().generate_noise(latent)
|
| 647 |
-
else:
|
| 648 |
-
noise = Noise_RandomNoise(noise_seed).generate_noise(latent)
|
| 649 |
-
|
| 650 |
-
noise_mask = None
|
| 651 |
-
if "noise_mask" in latent:
|
| 652 |
-
noise_mask = latent["noise_mask"]
|
| 653 |
-
|
| 654 |
-
x0_output = {}
|
| 655 |
-
callback = latent_preview.prepare_callback(model, sigmas.shape[-1] - 1, x0_output)
|
| 656 |
-
|
| 657 |
-
disable_pbar = not comfy.utils.PROGRESS_BAR_ENABLED
|
| 658 |
-
samples = comfy.sample.sample_custom(model, noise, cfg, sampler, sigmas, positive, negative, latent_image, noise_mask=noise_mask, callback=callback, disable_pbar=disable_pbar, seed=noise_seed)
|
| 659 |
-
|
| 660 |
-
out = latent.copy()
|
| 661 |
-
out["samples"] = samples
|
| 662 |
-
if "x0" in x0_output:
|
| 663 |
-
out_denoised = latent.copy()
|
| 664 |
-
out_denoised["samples"] = model.model.process_latent_out(x0_output["x0"].cpu())
|
| 665 |
-
else:
|
| 666 |
-
out_denoised = out
|
| 667 |
-
return (out, out_denoised)
|
| 668 |
-
|
| 669 |
-
class Guider_Basic(comfy.samplers.CFGGuider):
|
| 670 |
-
def set_conds(self, positive):
|
| 671 |
-
self.inner_set_conds({"positive": positive})
|
| 672 |
-
|
| 673 |
-
class BasicGuider:
|
| 674 |
-
@classmethod
|
| 675 |
-
def INPUT_TYPES(s):
|
| 676 |
-
return {"required":
|
| 677 |
-
{"model": ("MODEL",),
|
| 678 |
-
"conditioning": ("CONDITIONING", ),
|
| 679 |
-
}
|
| 680 |
-
}
|
| 681 |
-
|
| 682 |
-
RETURN_TYPES = ("GUIDER",)
|
| 683 |
-
|
| 684 |
-
FUNCTION = "get_guider"
|
| 685 |
-
CATEGORY = "sampling/custom_sampling/guiders"
|
| 686 |
-
|
| 687 |
-
def get_guider(self, model, conditioning):
|
| 688 |
-
guider = Guider_Basic(model)
|
| 689 |
-
guider.set_conds(conditioning)
|
| 690 |
-
return (guider,)
|
| 691 |
-
|
| 692 |
-
class CFGGuider:
|
| 693 |
-
@classmethod
|
| 694 |
-
def INPUT_TYPES(s):
|
| 695 |
-
return {"required":
|
| 696 |
-
{"model": ("MODEL",),
|
| 697 |
-
"positive": ("CONDITIONING", ),
|
| 698 |
-
"negative": ("CONDITIONING", ),
|
| 699 |
-
"cfg": ("FLOAT", {"default": 8.0, "min": 0.0, "max": 100.0, "step":0.1, "round": 0.01}),
|
| 700 |
-
}
|
| 701 |
-
}
|
| 702 |
-
|
| 703 |
-
RETURN_TYPES = ("GUIDER",)
|
| 704 |
-
|
| 705 |
-
FUNCTION = "get_guider"
|
| 706 |
-
CATEGORY = "sampling/custom_sampling/guiders"
|
| 707 |
-
|
| 708 |
-
def get_guider(self, model, positive, negative, cfg):
|
| 709 |
-
guider = comfy.samplers.CFGGuider(model)
|
| 710 |
-
guider.set_conds(positive, negative)
|
| 711 |
-
guider.set_cfg(cfg)
|
| 712 |
-
return (guider,)
|
| 713 |
-
|
| 714 |
-
class Guider_DualCFG(comfy.samplers.CFGGuider):
|
| 715 |
-
def set_cfg(self, cfg1, cfg2, nested=False):
|
| 716 |
-
self.cfg1 = cfg1
|
| 717 |
-
self.cfg2 = cfg2
|
| 718 |
-
self.nested = nested
|
| 719 |
-
|
| 720 |
-
def set_conds(self, positive, middle, negative):
|
| 721 |
-
middle = node_helpers.conditioning_set_values(middle, {"prompt_type": "negative"})
|
| 722 |
-
self.inner_set_conds({"positive": positive, "middle": middle, "negative": negative})
|
| 723 |
-
|
| 724 |
-
def predict_noise(self, x, timestep, model_options={}, seed=None):
|
| 725 |
-
negative_cond = self.conds.get("negative", None)
|
| 726 |
-
middle_cond = self.conds.get("middle", None)
|
| 727 |
-
positive_cond = self.conds.get("positive", None)
|
| 728 |
-
|
| 729 |
-
if self.nested:
|
| 730 |
-
out = comfy.samplers.calc_cond_batch(self.inner_model, [negative_cond, middle_cond, positive_cond], x, timestep, model_options)
|
| 731 |
-
pred_text = comfy.samplers.cfg_function(self.inner_model, out[2], out[1], self.cfg1, x, timestep, model_options=model_options, cond=positive_cond, uncond=middle_cond)
|
| 732 |
-
return out[0] + self.cfg2 * (pred_text - out[0])
|
| 733 |
-
else:
|
| 734 |
-
if model_options.get("disable_cfg1_optimization", False) == False:
|
| 735 |
-
if math.isclose(self.cfg2, 1.0):
|
| 736 |
-
negative_cond = None
|
| 737 |
-
if math.isclose(self.cfg1, 1.0):
|
| 738 |
-
middle_cond = None
|
| 739 |
-
|
| 740 |
-
out = comfy.samplers.calc_cond_batch(self.inner_model, [negative_cond, middle_cond, positive_cond], x, timestep, model_options)
|
| 741 |
-
return comfy.samplers.cfg_function(self.inner_model, out[1], out[0], self.cfg2, x, timestep, model_options=model_options, cond=middle_cond, uncond=negative_cond) + (out[2] - out[1]) * self.cfg1
|
| 742 |
-
|
| 743 |
-
class DualCFGGuider:
|
| 744 |
-
@classmethod
|
| 745 |
-
def INPUT_TYPES(s):
|
| 746 |
-
return {"required":
|
| 747 |
-
{"model": ("MODEL",),
|
| 748 |
-
"cond1": ("CONDITIONING", ),
|
| 749 |
-
"cond2": ("CONDITIONING", ),
|
| 750 |
-
"negative": ("CONDITIONING", ),
|
| 751 |
-
"cfg_conds": ("FLOAT", {"default": 8.0, "min": 0.0, "max": 100.0, "step":0.1, "round": 0.01}),
|
| 752 |
-
"cfg_cond2_negative": ("FLOAT", {"default": 8.0, "min": 0.0, "max": 100.0, "step":0.1, "round": 0.01}),
|
| 753 |
-
"style": (["regular", "nested"],),
|
| 754 |
-
}
|
| 755 |
-
}
|
| 756 |
-
|
| 757 |
-
RETURN_TYPES = ("GUIDER",)
|
| 758 |
-
|
| 759 |
-
FUNCTION = "get_guider"
|
| 760 |
-
CATEGORY = "sampling/custom_sampling/guiders"
|
| 761 |
-
|
| 762 |
-
def get_guider(self, model, cond1, cond2, negative, cfg_conds, cfg_cond2_negative, style):
|
| 763 |
-
guider = Guider_DualCFG(model)
|
| 764 |
-
guider.set_conds(cond1, cond2, negative)
|
| 765 |
-
guider.set_cfg(cfg_conds, cfg_cond2_negative, nested=(style == "nested"))
|
| 766 |
-
return (guider,)
|
| 767 |
-
|
| 768 |
-
class DisableNoise:
|
| 769 |
-
@classmethod
|
| 770 |
-
def INPUT_TYPES(s):
|
| 771 |
-
return {"required":{
|
| 772 |
-
}
|
| 773 |
-
}
|
| 774 |
-
|
| 775 |
-
RETURN_TYPES = ("NOISE",)
|
| 776 |
-
FUNCTION = "get_noise"
|
| 777 |
-
CATEGORY = "sampling/custom_sampling/noise"
|
| 778 |
-
|
| 779 |
-
def get_noise(self):
|
| 780 |
-
return (Noise_EmptyNoise(),)
|
| 781 |
-
|
| 782 |
-
|
| 783 |
-
class RandomNoise(DisableNoise):
|
| 784 |
-
@classmethod
|
| 785 |
-
def INPUT_TYPES(s):
|
| 786 |
-
return {
|
| 787 |
-
"required": {
|
| 788 |
-
"noise_seed": ("INT", {
|
| 789 |
-
"default": 0,
|
| 790 |
-
"min": 0,
|
| 791 |
-
"max": 0xffffffffffffffff,
|
| 792 |
-
"control_after_generate": True,
|
| 793 |
-
}),
|
| 794 |
-
}
|
| 795 |
-
}
|
| 796 |
-
|
| 797 |
-
def get_noise(self, noise_seed):
|
| 798 |
-
return (Noise_RandomNoise(noise_seed),)
|
| 799 |
-
|
| 800 |
-
|
| 801 |
-
class SamplerCustomAdvanced:
|
| 802 |
-
@classmethod
|
| 803 |
-
def INPUT_TYPES(s):
|
| 804 |
-
return {"required":
|
| 805 |
-
{"noise": ("NOISE", ),
|
| 806 |
-
"guider": ("GUIDER", ),
|
| 807 |
-
"sampler": ("SAMPLER", ),
|
| 808 |
-
"sigmas": ("SIGMAS", ),
|
| 809 |
-
"latent_image": ("LATENT", ),
|
| 810 |
-
}
|
| 811 |
-
}
|
| 812 |
-
|
| 813 |
-
RETURN_TYPES = ("LATENT","LATENT")
|
| 814 |
-
RETURN_NAMES = ("output", "denoised_output")
|
| 815 |
-
|
| 816 |
-
FUNCTION = "sample"
|
| 817 |
-
|
| 818 |
-
CATEGORY = "sampling/custom_sampling"
|
| 819 |
-
|
| 820 |
-
def sample(self, noise, guider, sampler, sigmas, latent_image):
|
| 821 |
-
latent = latent_image
|
| 822 |
-
latent_image = latent["samples"]
|
| 823 |
-
latent = latent.copy()
|
| 824 |
-
latent_image = comfy.sample.fix_empty_latent_channels(guider.model_patcher, latent_image)
|
| 825 |
-
latent["samples"] = latent_image
|
| 826 |
-
|
| 827 |
-
noise_mask = None
|
| 828 |
-
if "noise_mask" in latent:
|
| 829 |
-
noise_mask = latent["noise_mask"]
|
| 830 |
-
|
| 831 |
-
x0_output = {}
|
| 832 |
-
callback = latent_preview.prepare_callback(guider.model_patcher, sigmas.shape[-1] - 1, x0_output)
|
| 833 |
-
|
| 834 |
-
disable_pbar = not comfy.utils.PROGRESS_BAR_ENABLED
|
| 835 |
-
samples = guider.sample(noise.generate_noise(latent), latent_image, sampler, sigmas, denoise_mask=noise_mask, callback=callback, disable_pbar=disable_pbar, seed=noise.seed)
|
| 836 |
-
samples = samples.to(comfy.model_management.intermediate_device())
|
| 837 |
-
|
| 838 |
-
out = latent.copy()
|
| 839 |
-
out["samples"] = samples
|
| 840 |
-
if "x0" in x0_output:
|
| 841 |
-
out_denoised = latent.copy()
|
| 842 |
-
out_denoised["samples"] = guider.model_patcher.model.process_latent_out(x0_output["x0"].cpu())
|
| 843 |
-
else:
|
| 844 |
-
out_denoised = out
|
| 845 |
-
return (out, out_denoised)
|
| 846 |
-
|
| 847 |
-
class AddNoise:
|
| 848 |
-
@classmethod
|
| 849 |
-
def INPUT_TYPES(s):
|
| 850 |
-
return {"required":
|
| 851 |
-
{"model": ("MODEL",),
|
| 852 |
-
"noise": ("NOISE", ),
|
| 853 |
-
"sigmas": ("SIGMAS", ),
|
| 854 |
-
"latent_image": ("LATENT", ),
|
| 855 |
-
}
|
| 856 |
-
}
|
| 857 |
-
|
| 858 |
-
RETURN_TYPES = ("LATENT",)
|
| 859 |
-
|
| 860 |
-
FUNCTION = "add_noise"
|
| 861 |
-
|
| 862 |
-
CATEGORY = "_for_testing/custom_sampling/noise"
|
| 863 |
-
|
| 864 |
-
def add_noise(self, model, noise, sigmas, latent_image):
|
| 865 |
-
if len(sigmas) == 0:
|
| 866 |
-
return latent_image
|
| 867 |
-
|
| 868 |
-
latent = latent_image
|
| 869 |
-
latent_image = latent["samples"]
|
| 870 |
-
|
| 871 |
-
noisy = noise.generate_noise(latent)
|
| 872 |
-
|
| 873 |
-
model_sampling = model.get_model_object("model_sampling")
|
| 874 |
-
process_latent_out = model.get_model_object("process_latent_out")
|
| 875 |
-
process_latent_in = model.get_model_object("process_latent_in")
|
| 876 |
-
|
| 877 |
-
if len(sigmas) > 1:
|
| 878 |
-
scale = torch.abs(sigmas[0] - sigmas[-1])
|
| 879 |
-
else:
|
| 880 |
-
scale = sigmas[0]
|
| 881 |
-
|
| 882 |
-
if torch.count_nonzero(latent_image) > 0: #Don't shift the empty latent image.
|
| 883 |
-
latent_image = process_latent_in(latent_image)
|
| 884 |
-
noisy = model_sampling.noise_scaling(scale, noisy, latent_image)
|
| 885 |
-
noisy = process_latent_out(noisy)
|
| 886 |
-
noisy = torch.nan_to_num(noisy, nan=0.0, posinf=0.0, neginf=0.0)
|
| 887 |
-
|
| 888 |
-
out = latent.copy()
|
| 889 |
-
out["samples"] = noisy
|
| 890 |
-
return (out,)
|
| 891 |
-
|
| 892 |
-
|
| 893 |
-
NODE_CLASS_MAPPINGS = {
|
| 894 |
-
"SamplerCustom": SamplerCustom,
|
| 895 |
-
"BasicScheduler": BasicScheduler,
|
| 896 |
-
"KarrasScheduler": KarrasScheduler,
|
| 897 |
-
"ExponentialScheduler": ExponentialScheduler,
|
| 898 |
-
"PolyexponentialScheduler": PolyexponentialScheduler,
|
| 899 |
-
"LaplaceScheduler": LaplaceScheduler,
|
| 900 |
-
"VPScheduler": VPScheduler,
|
| 901 |
-
"BetaSamplingScheduler": BetaSamplingScheduler,
|
| 902 |
-
"SDTurboScheduler": SDTurboScheduler,
|
| 903 |
-
"KSamplerSelect": KSamplerSelect,
|
| 904 |
-
"SamplerEulerAncestral": SamplerEulerAncestral,
|
| 905 |
-
"SamplerEulerAncestralCFGPP": SamplerEulerAncestralCFGPP,
|
| 906 |
-
"SamplerLMS": SamplerLMS,
|
| 907 |
-
"SamplerDPMPP_3M_SDE": SamplerDPMPP_3M_SDE,
|
| 908 |
-
"SamplerDPMPP_2M_SDE": SamplerDPMPP_2M_SDE,
|
| 909 |
-
"SamplerDPMPP_SDE": SamplerDPMPP_SDE,
|
| 910 |
-
"SamplerDPMPP_2S_Ancestral": SamplerDPMPP_2S_Ancestral,
|
| 911 |
-
"SamplerDPMAdaptative": SamplerDPMAdaptative,
|
| 912 |
-
"SamplerER_SDE": SamplerER_SDE,
|
| 913 |
-
"SamplerSASolver": SamplerSASolver,
|
| 914 |
-
"SplitSigmas": SplitSigmas,
|
| 915 |
-
"SplitSigmasDenoise": SplitSigmasDenoise,
|
| 916 |
-
"FlipSigmas": FlipSigmas,
|
| 917 |
-
"SetFirstSigma": SetFirstSigma,
|
| 918 |
-
"ExtendIntermediateSigmas": ExtendIntermediateSigmas,
|
| 919 |
-
"SamplingPercentToSigma": SamplingPercentToSigma,
|
| 920 |
-
|
| 921 |
-
"CFGGuider": CFGGuider,
|
| 922 |
-
"DualCFGGuider": DualCFGGuider,
|
| 923 |
-
"BasicGuider": BasicGuider,
|
| 924 |
-
"RandomNoise": RandomNoise,
|
| 925 |
-
"DisableNoise": DisableNoise,
|
| 926 |
-
"AddNoise": AddNoise,
|
| 927 |
-
"SamplerCustomAdvanced": SamplerCustomAdvanced,
|
| 928 |
-
}
|
| 929 |
-
|
| 930 |
-
NODE_DISPLAY_NAME_MAPPINGS = {
|
| 931 |
-
"SamplerEulerAncestralCFGPP": "SamplerEulerAncestralCFG++",
|
| 932 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
comfy_extras/nodes_differential_diffusion.py
DELETED
|
@@ -1,42 +0,0 @@
|
|
| 1 |
-
# code adapted from https://github.com/exx8/differential-diffusion
|
| 2 |
-
|
| 3 |
-
import torch
|
| 4 |
-
|
| 5 |
-
class DifferentialDiffusion():
|
| 6 |
-
@classmethod
|
| 7 |
-
def INPUT_TYPES(s):
|
| 8 |
-
return {"required": {"model": ("MODEL", ),
|
| 9 |
-
}}
|
| 10 |
-
RETURN_TYPES = ("MODEL",)
|
| 11 |
-
FUNCTION = "apply"
|
| 12 |
-
CATEGORY = "_for_testing"
|
| 13 |
-
INIT = False
|
| 14 |
-
|
| 15 |
-
def apply(self, model):
|
| 16 |
-
model = model.clone()
|
| 17 |
-
model.set_model_denoise_mask_function(self.forward)
|
| 18 |
-
return (model,)
|
| 19 |
-
|
| 20 |
-
def forward(self, sigma: torch.Tensor, denoise_mask: torch.Tensor, extra_options: dict):
|
| 21 |
-
model = extra_options["model"]
|
| 22 |
-
step_sigmas = extra_options["sigmas"]
|
| 23 |
-
sigma_to = model.inner_model.model_sampling.sigma_min
|
| 24 |
-
if step_sigmas[-1] > sigma_to:
|
| 25 |
-
sigma_to = step_sigmas[-1]
|
| 26 |
-
sigma_from = step_sigmas[0]
|
| 27 |
-
|
| 28 |
-
ts_from = model.inner_model.model_sampling.timestep(sigma_from)
|
| 29 |
-
ts_to = model.inner_model.model_sampling.timestep(sigma_to)
|
| 30 |
-
current_ts = model.inner_model.model_sampling.timestep(sigma[0])
|
| 31 |
-
|
| 32 |
-
threshold = (current_ts - ts_to) / (ts_from - ts_to)
|
| 33 |
-
|
| 34 |
-
return (denoise_mask >= threshold).to(denoise_mask.dtype)
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
NODE_CLASS_MAPPINGS = {
|
| 38 |
-
"DifferentialDiffusion": DifferentialDiffusion,
|
| 39 |
-
}
|
| 40 |
-
NODE_DISPLAY_NAME_MAPPINGS = {
|
| 41 |
-
"DifferentialDiffusion": "Differential Diffusion",
|
| 42 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
comfy_extras/nodes_edit_model.py
DELETED
|
@@ -1,26 +0,0 @@
|
|
| 1 |
-
import node_helpers
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
class ReferenceLatent:
|
| 5 |
-
@classmethod
|
| 6 |
-
def INPUT_TYPES(s):
|
| 7 |
-
return {"required": {"conditioning": ("CONDITIONING", ),
|
| 8 |
-
},
|
| 9 |
-
"optional": {"latent": ("LATENT", ),}
|
| 10 |
-
}
|
| 11 |
-
|
| 12 |
-
RETURN_TYPES = ("CONDITIONING",)
|
| 13 |
-
FUNCTION = "append"
|
| 14 |
-
|
| 15 |
-
CATEGORY = "advanced/conditioning/edit_models"
|
| 16 |
-
DESCRIPTION = "This node sets the guiding latent for an edit model. If the model supports it you can chain multiple to set multiple reference images."
|
| 17 |
-
|
| 18 |
-
def append(self, conditioning, latent=None):
|
| 19 |
-
if latent is not None:
|
| 20 |
-
conditioning = node_helpers.conditioning_set_values(conditioning, {"reference_latents": [latent["samples"]]}, append=True)
|
| 21 |
-
return (conditioning, )
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
NODE_CLASS_MAPPINGS = {
|
| 25 |
-
"ReferenceLatent": ReferenceLatent,
|
| 26 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
comfy_extras/nodes_flux.py
DELETED
|
@@ -1,108 +0,0 @@
|
|
| 1 |
-
import node_helpers
|
| 2 |
-
import comfy.utils
|
| 3 |
-
|
| 4 |
-
class CLIPTextEncodeFlux:
|
| 5 |
-
@classmethod
|
| 6 |
-
def INPUT_TYPES(s):
|
| 7 |
-
return {"required": {
|
| 8 |
-
"clip": ("CLIP", ),
|
| 9 |
-
"clip_l": ("STRING", {"multiline": True, "dynamicPrompts": True}),
|
| 10 |
-
"t5xxl": ("STRING", {"multiline": True, "dynamicPrompts": True}),
|
| 11 |
-
"guidance": ("FLOAT", {"default": 3.5, "min": 0.0, "max": 100.0, "step": 0.1}),
|
| 12 |
-
}}
|
| 13 |
-
RETURN_TYPES = ("CONDITIONING",)
|
| 14 |
-
FUNCTION = "encode"
|
| 15 |
-
|
| 16 |
-
CATEGORY = "advanced/conditioning/flux"
|
| 17 |
-
|
| 18 |
-
def encode(self, clip, clip_l, t5xxl, guidance):
|
| 19 |
-
tokens = clip.tokenize(clip_l)
|
| 20 |
-
tokens["t5xxl"] = clip.tokenize(t5xxl)["t5xxl"]
|
| 21 |
-
|
| 22 |
-
return (clip.encode_from_tokens_scheduled(tokens, add_dict={"guidance": guidance}), )
|
| 23 |
-
|
| 24 |
-
class FluxGuidance:
|
| 25 |
-
@classmethod
|
| 26 |
-
def INPUT_TYPES(s):
|
| 27 |
-
return {"required": {
|
| 28 |
-
"conditioning": ("CONDITIONING", ),
|
| 29 |
-
"guidance": ("FLOAT", {"default": 3.5, "min": 0.0, "max": 100.0, "step": 0.1}),
|
| 30 |
-
}}
|
| 31 |
-
|
| 32 |
-
RETURN_TYPES = ("CONDITIONING",)
|
| 33 |
-
FUNCTION = "append"
|
| 34 |
-
|
| 35 |
-
CATEGORY = "advanced/conditioning/flux"
|
| 36 |
-
|
| 37 |
-
def append(self, conditioning, guidance):
|
| 38 |
-
c = node_helpers.conditioning_set_values(conditioning, {"guidance": guidance})
|
| 39 |
-
return (c, )
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
class FluxDisableGuidance:
|
| 43 |
-
@classmethod
|
| 44 |
-
def INPUT_TYPES(s):
|
| 45 |
-
return {"required": {
|
| 46 |
-
"conditioning": ("CONDITIONING", ),
|
| 47 |
-
}}
|
| 48 |
-
|
| 49 |
-
RETURN_TYPES = ("CONDITIONING",)
|
| 50 |
-
FUNCTION = "append"
|
| 51 |
-
|
| 52 |
-
CATEGORY = "advanced/conditioning/flux"
|
| 53 |
-
DESCRIPTION = "This node completely disables the guidance embed on Flux and Flux like models"
|
| 54 |
-
|
| 55 |
-
def append(self, conditioning):
|
| 56 |
-
c = node_helpers.conditioning_set_values(conditioning, {"guidance": None})
|
| 57 |
-
return (c, )
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
PREFERED_KONTEXT_RESOLUTIONS = [
|
| 61 |
-
(672, 1568),
|
| 62 |
-
(688, 1504),
|
| 63 |
-
(720, 1456),
|
| 64 |
-
(752, 1392),
|
| 65 |
-
(800, 1328),
|
| 66 |
-
(832, 1248),
|
| 67 |
-
(880, 1184),
|
| 68 |
-
(944, 1104),
|
| 69 |
-
(1024, 1024),
|
| 70 |
-
(1104, 944),
|
| 71 |
-
(1184, 880),
|
| 72 |
-
(1248, 832),
|
| 73 |
-
(1328, 800),
|
| 74 |
-
(1392, 752),
|
| 75 |
-
(1456, 720),
|
| 76 |
-
(1504, 688),
|
| 77 |
-
(1568, 672),
|
| 78 |
-
]
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
class FluxKontextImageScale:
|
| 82 |
-
@classmethod
|
| 83 |
-
def INPUT_TYPES(s):
|
| 84 |
-
return {"required": {"image": ("IMAGE", ),
|
| 85 |
-
},
|
| 86 |
-
}
|
| 87 |
-
|
| 88 |
-
RETURN_TYPES = ("IMAGE",)
|
| 89 |
-
FUNCTION = "scale"
|
| 90 |
-
|
| 91 |
-
CATEGORY = "advanced/conditioning/flux"
|
| 92 |
-
DESCRIPTION = "This node resizes the image to one that is more optimal for flux kontext."
|
| 93 |
-
|
| 94 |
-
def scale(self, image):
|
| 95 |
-
width = image.shape[2]
|
| 96 |
-
height = image.shape[1]
|
| 97 |
-
aspect_ratio = width / height
|
| 98 |
-
_, width, height = min((abs(aspect_ratio - w / h), w, h) for w, h in PREFERED_KONTEXT_RESOLUTIONS)
|
| 99 |
-
image = comfy.utils.common_upscale(image.movedim(-1, 1), width, height, "lanczos", "center").movedim(1, -1)
|
| 100 |
-
return (image, )
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
NODE_CLASS_MAPPINGS = {
|
| 104 |
-
"CLIPTextEncodeFlux": CLIPTextEncodeFlux,
|
| 105 |
-
"FluxGuidance": FluxGuidance,
|
| 106 |
-
"FluxDisableGuidance": FluxDisableGuidance,
|
| 107 |
-
"FluxKontextImageScale": FluxKontextImageScale,
|
| 108 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
comfy_extras/nodes_freelunch.py
DELETED
|
@@ -1,113 +0,0 @@
|
|
| 1 |
-
#code originally taken from: https://github.com/ChenyangSi/FreeU (under MIT License)
|
| 2 |
-
|
| 3 |
-
import torch
|
| 4 |
-
import logging
|
| 5 |
-
|
| 6 |
-
def Fourier_filter(x, threshold, scale):
|
| 7 |
-
# FFT
|
| 8 |
-
x_freq = torch.fft.fftn(x.float(), dim=(-2, -1))
|
| 9 |
-
x_freq = torch.fft.fftshift(x_freq, dim=(-2, -1))
|
| 10 |
-
|
| 11 |
-
B, C, H, W = x_freq.shape
|
| 12 |
-
mask = torch.ones((B, C, H, W), device=x.device)
|
| 13 |
-
|
| 14 |
-
crow, ccol = H // 2, W //2
|
| 15 |
-
mask[..., crow - threshold:crow + threshold, ccol - threshold:ccol + threshold] = scale
|
| 16 |
-
x_freq = x_freq * mask
|
| 17 |
-
|
| 18 |
-
# IFFT
|
| 19 |
-
x_freq = torch.fft.ifftshift(x_freq, dim=(-2, -1))
|
| 20 |
-
x_filtered = torch.fft.ifftn(x_freq, dim=(-2, -1)).real
|
| 21 |
-
|
| 22 |
-
return x_filtered.to(x.dtype)
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
class FreeU:
|
| 26 |
-
@classmethod
|
| 27 |
-
def INPUT_TYPES(s):
|
| 28 |
-
return {"required": { "model": ("MODEL",),
|
| 29 |
-
"b1": ("FLOAT", {"default": 1.1, "min": 0.0, "max": 10.0, "step": 0.01}),
|
| 30 |
-
"b2": ("FLOAT", {"default": 1.2, "min": 0.0, "max": 10.0, "step": 0.01}),
|
| 31 |
-
"s1": ("FLOAT", {"default": 0.9, "min": 0.0, "max": 10.0, "step": 0.01}),
|
| 32 |
-
"s2": ("FLOAT", {"default": 0.2, "min": 0.0, "max": 10.0, "step": 0.01}),
|
| 33 |
-
}}
|
| 34 |
-
RETURN_TYPES = ("MODEL",)
|
| 35 |
-
FUNCTION = "patch"
|
| 36 |
-
|
| 37 |
-
CATEGORY = "model_patches/unet"
|
| 38 |
-
|
| 39 |
-
def patch(self, model, b1, b2, s1, s2):
|
| 40 |
-
model_channels = model.model.model_config.unet_config["model_channels"]
|
| 41 |
-
scale_dict = {model_channels * 4: (b1, s1), model_channels * 2: (b2, s2)}
|
| 42 |
-
on_cpu_devices = {}
|
| 43 |
-
|
| 44 |
-
def output_block_patch(h, hsp, transformer_options):
|
| 45 |
-
scale = scale_dict.get(int(h.shape[1]), None)
|
| 46 |
-
if scale is not None:
|
| 47 |
-
h[:,:h.shape[1] // 2] = h[:,:h.shape[1] // 2] * scale[0]
|
| 48 |
-
if hsp.device not in on_cpu_devices:
|
| 49 |
-
try:
|
| 50 |
-
hsp = Fourier_filter(hsp, threshold=1, scale=scale[1])
|
| 51 |
-
except:
|
| 52 |
-
logging.warning("Device {} does not support the torch.fft functions used in the FreeU node, switching to CPU.".format(hsp.device))
|
| 53 |
-
on_cpu_devices[hsp.device] = True
|
| 54 |
-
hsp = Fourier_filter(hsp.cpu(), threshold=1, scale=scale[1]).to(hsp.device)
|
| 55 |
-
else:
|
| 56 |
-
hsp = Fourier_filter(hsp.cpu(), threshold=1, scale=scale[1]).to(hsp.device)
|
| 57 |
-
|
| 58 |
-
return h, hsp
|
| 59 |
-
|
| 60 |
-
m = model.clone()
|
| 61 |
-
m.set_model_output_block_patch(output_block_patch)
|
| 62 |
-
return (m, )
|
| 63 |
-
|
| 64 |
-
class FreeU_V2:
|
| 65 |
-
@classmethod
|
| 66 |
-
def INPUT_TYPES(s):
|
| 67 |
-
return {"required": { "model": ("MODEL",),
|
| 68 |
-
"b1": ("FLOAT", {"default": 1.3, "min": 0.0, "max": 10.0, "step": 0.01}),
|
| 69 |
-
"b2": ("FLOAT", {"default": 1.4, "min": 0.0, "max": 10.0, "step": 0.01}),
|
| 70 |
-
"s1": ("FLOAT", {"default": 0.9, "min": 0.0, "max": 10.0, "step": 0.01}),
|
| 71 |
-
"s2": ("FLOAT", {"default": 0.2, "min": 0.0, "max": 10.0, "step": 0.01}),
|
| 72 |
-
}}
|
| 73 |
-
RETURN_TYPES = ("MODEL",)
|
| 74 |
-
FUNCTION = "patch"
|
| 75 |
-
|
| 76 |
-
CATEGORY = "model_patches/unet"
|
| 77 |
-
|
| 78 |
-
def patch(self, model, b1, b2, s1, s2):
|
| 79 |
-
model_channels = model.model.model_config.unet_config["model_channels"]
|
| 80 |
-
scale_dict = {model_channels * 4: (b1, s1), model_channels * 2: (b2, s2)}
|
| 81 |
-
on_cpu_devices = {}
|
| 82 |
-
|
| 83 |
-
def output_block_patch(h, hsp, transformer_options):
|
| 84 |
-
scale = scale_dict.get(int(h.shape[1]), None)
|
| 85 |
-
if scale is not None:
|
| 86 |
-
hidden_mean = h.mean(1).unsqueeze(1)
|
| 87 |
-
B = hidden_mean.shape[0]
|
| 88 |
-
hidden_max, _ = torch.max(hidden_mean.view(B, -1), dim=-1, keepdim=True)
|
| 89 |
-
hidden_min, _ = torch.min(hidden_mean.view(B, -1), dim=-1, keepdim=True)
|
| 90 |
-
hidden_mean = (hidden_mean - hidden_min.unsqueeze(2).unsqueeze(3)) / (hidden_max - hidden_min).unsqueeze(2).unsqueeze(3)
|
| 91 |
-
|
| 92 |
-
h[:,:h.shape[1] // 2] = h[:,:h.shape[1] // 2] * ((scale[0] - 1 ) * hidden_mean + 1)
|
| 93 |
-
|
| 94 |
-
if hsp.device not in on_cpu_devices:
|
| 95 |
-
try:
|
| 96 |
-
hsp = Fourier_filter(hsp, threshold=1, scale=scale[1])
|
| 97 |
-
except:
|
| 98 |
-
logging.warning("Device {} does not support the torch.fft functions used in the FreeU node, switching to CPU.".format(hsp.device))
|
| 99 |
-
on_cpu_devices[hsp.device] = True
|
| 100 |
-
hsp = Fourier_filter(hsp.cpu(), threshold=1, scale=scale[1]).to(hsp.device)
|
| 101 |
-
else:
|
| 102 |
-
hsp = Fourier_filter(hsp.cpu(), threshold=1, scale=scale[1]).to(hsp.device)
|
| 103 |
-
|
| 104 |
-
return h, hsp
|
| 105 |
-
|
| 106 |
-
m = model.clone()
|
| 107 |
-
m.set_model_output_block_patch(output_block_patch)
|
| 108 |
-
return (m, )
|
| 109 |
-
|
| 110 |
-
NODE_CLASS_MAPPINGS = {
|
| 111 |
-
"FreeU": FreeU,
|
| 112 |
-
"FreeU_V2": FreeU_V2,
|
| 113 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
comfy_extras/nodes_fresca.py
DELETED
|
@@ -1,103 +0,0 @@
|
|
| 1 |
-
# Code based on https://github.com/WikiChao/FreSca (MIT License)
|
| 2 |
-
import torch
|
| 3 |
-
import torch.fft as fft
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
def Fourier_filter(x, scale_low=1.0, scale_high=1.5, freq_cutoff=20):
|
| 7 |
-
"""
|
| 8 |
-
Apply frequency-dependent scaling to an image tensor using Fourier transforms.
|
| 9 |
-
|
| 10 |
-
Parameters:
|
| 11 |
-
x: Input tensor of shape (B, C, H, W)
|
| 12 |
-
scale_low: Scaling factor for low-frequency components (default: 1.0)
|
| 13 |
-
scale_high: Scaling factor for high-frequency components (default: 1.5)
|
| 14 |
-
freq_cutoff: Number of frequency indices around center to consider as low-frequency (default: 20)
|
| 15 |
-
|
| 16 |
-
Returns:
|
| 17 |
-
x_filtered: Filtered version of x in spatial domain with frequency-specific scaling applied.
|
| 18 |
-
"""
|
| 19 |
-
# Preserve input dtype and device
|
| 20 |
-
dtype, device = x.dtype, x.device
|
| 21 |
-
|
| 22 |
-
# Convert to float32 for FFT computations
|
| 23 |
-
x = x.to(torch.float32)
|
| 24 |
-
|
| 25 |
-
# 1) Apply FFT and shift low frequencies to center
|
| 26 |
-
x_freq = fft.fftn(x, dim=(-2, -1))
|
| 27 |
-
x_freq = fft.fftshift(x_freq, dim=(-2, -1))
|
| 28 |
-
|
| 29 |
-
# Initialize mask with high-frequency scaling factor
|
| 30 |
-
mask = torch.ones(x_freq.shape, device=device) * scale_high
|
| 31 |
-
m = mask
|
| 32 |
-
for d in range(len(x_freq.shape) - 2):
|
| 33 |
-
dim = d + 2
|
| 34 |
-
cc = x_freq.shape[dim] // 2
|
| 35 |
-
f_c = min(freq_cutoff, cc)
|
| 36 |
-
m = m.narrow(dim, cc - f_c, f_c * 2)
|
| 37 |
-
|
| 38 |
-
# Apply low-frequency scaling factor to center region
|
| 39 |
-
m[:] = scale_low
|
| 40 |
-
|
| 41 |
-
# 3) Apply frequency-specific scaling
|
| 42 |
-
x_freq = x_freq * mask
|
| 43 |
-
|
| 44 |
-
# 4) Convert back to spatial domain
|
| 45 |
-
x_freq = fft.ifftshift(x_freq, dim=(-2, -1))
|
| 46 |
-
x_filtered = fft.ifftn(x_freq, dim=(-2, -1)).real
|
| 47 |
-
|
| 48 |
-
# 5) Restore original dtype
|
| 49 |
-
x_filtered = x_filtered.to(dtype)
|
| 50 |
-
|
| 51 |
-
return x_filtered
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
class FreSca:
|
| 55 |
-
@classmethod
|
| 56 |
-
def INPUT_TYPES(s):
|
| 57 |
-
return {
|
| 58 |
-
"required": {
|
| 59 |
-
"model": ("MODEL",),
|
| 60 |
-
"scale_low": ("FLOAT", {"default": 1.0, "min": 0, "max": 10, "step": 0.01,
|
| 61 |
-
"tooltip": "Scaling factor for low-frequency components"}),
|
| 62 |
-
"scale_high": ("FLOAT", {"default": 1.25, "min": 0, "max": 10, "step": 0.01,
|
| 63 |
-
"tooltip": "Scaling factor for high-frequency components"}),
|
| 64 |
-
"freq_cutoff": ("INT", {"default": 20, "min": 1, "max": 10000, "step": 1,
|
| 65 |
-
"tooltip": "Number of frequency indices around center to consider as low-frequency"}),
|
| 66 |
-
}
|
| 67 |
-
}
|
| 68 |
-
RETURN_TYPES = ("MODEL",)
|
| 69 |
-
FUNCTION = "patch"
|
| 70 |
-
CATEGORY = "_for_testing"
|
| 71 |
-
DESCRIPTION = "Applies frequency-dependent scaling to the guidance"
|
| 72 |
-
def patch(self, model, scale_low, scale_high, freq_cutoff):
|
| 73 |
-
def custom_cfg_function(args):
|
| 74 |
-
conds_out = args["conds_out"]
|
| 75 |
-
if len(conds_out) <= 1 or None in args["conds"][:2]:
|
| 76 |
-
return conds_out
|
| 77 |
-
cond = conds_out[0]
|
| 78 |
-
uncond = conds_out[1]
|
| 79 |
-
|
| 80 |
-
guidance = cond - uncond
|
| 81 |
-
filtered_guidance = Fourier_filter(
|
| 82 |
-
guidance,
|
| 83 |
-
scale_low=scale_low,
|
| 84 |
-
scale_high=scale_high,
|
| 85 |
-
freq_cutoff=freq_cutoff,
|
| 86 |
-
)
|
| 87 |
-
filtered_cond = filtered_guidance + uncond
|
| 88 |
-
|
| 89 |
-
return [filtered_cond, uncond] + conds_out[2:]
|
| 90 |
-
|
| 91 |
-
m = model.clone()
|
| 92 |
-
m.set_model_sampler_pre_cfg_function(custom_cfg_function)
|
| 93 |
-
|
| 94 |
-
return (m,)
|
| 95 |
-
|
| 96 |
-
|
| 97 |
-
NODE_CLASS_MAPPINGS = {
|
| 98 |
-
"FreSca": FreSca,
|
| 99 |
-
}
|
| 100 |
-
|
| 101 |
-
NODE_DISPLAY_NAME_MAPPINGS = {
|
| 102 |
-
"FreSca": "FreSca",
|
| 103 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
comfy_extras/nodes_gits.py
DELETED
|
@@ -1,369 +0,0 @@
|
|
| 1 |
-
# from https://github.com/zju-pi/diff-sampler/tree/main/gits-main
|
| 2 |
-
import numpy as np
|
| 3 |
-
import torch
|
| 4 |
-
|
| 5 |
-
def loglinear_interp(t_steps, num_steps):
|
| 6 |
-
"""
|
| 7 |
-
Performs log-linear interpolation of a given array of decreasing numbers.
|
| 8 |
-
"""
|
| 9 |
-
xs = np.linspace(0, 1, len(t_steps))
|
| 10 |
-
ys = np.log(t_steps[::-1])
|
| 11 |
-
|
| 12 |
-
new_xs = np.linspace(0, 1, num_steps)
|
| 13 |
-
new_ys = np.interp(new_xs, xs, ys)
|
| 14 |
-
|
| 15 |
-
interped_ys = np.exp(new_ys)[::-1].copy()
|
| 16 |
-
return interped_ys
|
| 17 |
-
|
| 18 |
-
NOISE_LEVELS = {
|
| 19 |
-
0.80: [
|
| 20 |
-
[14.61464119, 7.49001646, 0.02916753],
|
| 21 |
-
[14.61464119, 11.54541874, 6.77309084, 0.02916753],
|
| 22 |
-
[14.61464119, 11.54541874, 7.49001646, 3.07277966, 0.02916753],
|
| 23 |
-
[14.61464119, 11.54541874, 7.49001646, 5.85520077, 2.05039096, 0.02916753],
|
| 24 |
-
[14.61464119, 12.2308979, 8.75849152, 7.49001646, 5.85520077, 2.05039096, 0.02916753],
|
| 25 |
-
[14.61464119, 12.2308979, 8.75849152, 7.49001646, 5.85520077, 3.07277966, 1.56271636, 0.02916753],
|
| 26 |
-
[14.61464119, 12.96784878, 11.54541874, 8.75849152, 7.49001646, 5.85520077, 3.07277966, 1.56271636, 0.02916753],
|
| 27 |
-
[14.61464119, 13.76078796, 12.2308979, 10.90732002, 8.75849152, 7.49001646, 5.85520077, 3.07277966, 1.56271636, 0.02916753],
|
| 28 |
-
[14.61464119, 13.76078796, 12.96784878, 12.2308979, 10.90732002, 8.75849152, 7.49001646, 5.85520077, 3.07277966, 1.56271636, 0.02916753],
|
| 29 |
-
[14.61464119, 13.76078796, 12.96784878, 12.2308979, 10.90732002, 9.24142551, 8.30717278, 7.49001646, 5.85520077, 3.07277966, 1.56271636, 0.02916753],
|
| 30 |
-
[14.61464119, 13.76078796, 12.96784878, 12.2308979, 10.90732002, 9.24142551, 8.30717278, 7.49001646, 6.14220476, 4.86714602, 3.07277966, 1.56271636, 0.02916753],
|
| 31 |
-
[14.61464119, 13.76078796, 12.96784878, 12.2308979, 11.54541874, 10.31284904, 9.24142551, 8.30717278, 7.49001646, 6.14220476, 4.86714602, 3.07277966, 1.56271636, 0.02916753],
|
| 32 |
-
[14.61464119, 13.76078796, 12.96784878, 12.2308979, 11.54541874, 10.90732002, 10.31284904, 9.24142551, 8.30717278, 7.49001646, 6.14220476, 4.86714602, 3.07277966, 1.56271636, 0.02916753],
|
| 33 |
-
[14.61464119, 13.76078796, 12.96784878, 12.2308979, 11.54541874, 10.90732002, 10.31284904, 9.24142551, 8.75849152, 8.30717278, 7.49001646, 6.14220476, 4.86714602, 3.07277966, 1.56271636, 0.02916753],
|
| 34 |
-
[14.61464119, 13.76078796, 12.96784878, 12.2308979, 11.54541874, 10.90732002, 10.31284904, 9.24142551, 8.75849152, 8.30717278, 7.49001646, 6.14220476, 4.86714602, 3.1956799, 1.98035145, 0.86115354, 0.02916753],
|
| 35 |
-
[14.61464119, 13.76078796, 12.96784878, 12.2308979, 11.54541874, 10.90732002, 10.31284904, 9.75859547, 9.24142551, 8.75849152, 8.30717278, 7.49001646, 6.14220476, 4.86714602, 3.1956799, 1.98035145, 0.86115354, 0.02916753],
|
| 36 |
-
[14.61464119, 13.76078796, 12.96784878, 12.2308979, 11.54541874, 10.90732002, 10.31284904, 9.75859547, 9.24142551, 8.75849152, 8.30717278, 7.49001646, 6.77309084, 5.85520077, 4.65472794, 3.07277966, 1.84880662, 0.83188516, 0.02916753],
|
| 37 |
-
[14.61464119, 13.76078796, 12.96784878, 12.2308979, 11.54541874, 10.90732002, 10.31284904, 9.75859547, 9.24142551, 8.75849152, 8.30717278, 7.88507891, 7.49001646, 6.77309084, 5.85520077, 4.65472794, 3.07277966, 1.84880662, 0.83188516, 0.02916753],
|
| 38 |
-
[14.61464119, 13.76078796, 12.96784878, 12.2308979, 11.54541874, 10.90732002, 10.31284904, 9.75859547, 9.24142551, 8.75849152, 8.30717278, 7.88507891, 7.49001646, 6.77309084, 5.85520077, 4.86714602, 3.75677586, 2.84484982, 1.78698075, 0.803307, 0.02916753],
|
| 39 |
-
],
|
| 40 |
-
0.85: [
|
| 41 |
-
[14.61464119, 7.49001646, 0.02916753],
|
| 42 |
-
[14.61464119, 7.49001646, 1.84880662, 0.02916753],
|
| 43 |
-
[14.61464119, 11.54541874, 6.77309084, 1.56271636, 0.02916753],
|
| 44 |
-
[14.61464119, 11.54541874, 7.11996698, 3.07277966, 1.24153244, 0.02916753],
|
| 45 |
-
[14.61464119, 11.54541874, 7.49001646, 5.09240818, 2.84484982, 0.95350921, 0.02916753],
|
| 46 |
-
[14.61464119, 12.2308979, 8.75849152, 7.49001646, 5.09240818, 2.84484982, 0.95350921, 0.02916753],
|
| 47 |
-
[14.61464119, 12.2308979, 8.75849152, 7.49001646, 5.58536053, 3.1956799, 1.84880662, 0.803307, 0.02916753],
|
| 48 |
-
[14.61464119, 12.96784878, 11.54541874, 8.75849152, 7.49001646, 5.58536053, 3.1956799, 1.84880662, 0.803307, 0.02916753],
|
| 49 |
-
[14.61464119, 12.96784878, 11.54541874, 8.75849152, 7.49001646, 6.14220476, 4.65472794, 3.07277966, 1.84880662, 0.803307, 0.02916753],
|
| 50 |
-
[14.61464119, 13.76078796, 12.2308979, 10.90732002, 8.75849152, 7.49001646, 6.14220476, 4.65472794, 3.07277966, 1.84880662, 0.803307, 0.02916753],
|
| 51 |
-
[14.61464119, 13.76078796, 12.2308979, 10.90732002, 9.24142551, 8.30717278, 7.49001646, 6.14220476, 4.65472794, 3.07277966, 1.84880662, 0.803307, 0.02916753],
|
| 52 |
-
[14.61464119, 13.76078796, 12.96784878, 12.2308979, 10.90732002, 9.24142551, 8.30717278, 7.49001646, 6.14220476, 4.65472794, 3.07277966, 1.84880662, 0.803307, 0.02916753],
|
| 53 |
-
[14.61464119, 13.76078796, 12.96784878, 12.2308979, 11.54541874, 10.31284904, 9.24142551, 8.30717278, 7.49001646, 6.14220476, 4.65472794, 3.07277966, 1.84880662, 0.803307, 0.02916753],
|
| 54 |
-
[14.61464119, 13.76078796, 12.96784878, 12.2308979, 11.54541874, 10.31284904, 9.24142551, 8.30717278, 7.49001646, 6.14220476, 4.86714602, 3.60512662, 2.6383388, 1.56271636, 0.72133851, 0.02916753],
|
| 55 |
-
[14.61464119, 13.76078796, 12.96784878, 12.2308979, 11.54541874, 10.31284904, 9.24142551, 8.30717278, 7.49001646, 6.77309084, 5.85520077, 4.65472794, 3.46139455, 2.45070267, 1.56271636, 0.72133851, 0.02916753],
|
| 56 |
-
[14.61464119, 13.76078796, 12.96784878, 12.2308979, 11.54541874, 10.31284904, 9.24142551, 8.75849152, 8.30717278, 7.49001646, 6.77309084, 5.85520077, 4.65472794, 3.46139455, 2.45070267, 1.56271636, 0.72133851, 0.02916753],
|
| 57 |
-
[14.61464119, 13.76078796, 12.96784878, 12.2308979, 11.54541874, 10.90732002, 10.31284904, 9.24142551, 8.75849152, 8.30717278, 7.49001646, 6.77309084, 5.85520077, 4.65472794, 3.46139455, 2.45070267, 1.56271636, 0.72133851, 0.02916753],
|
| 58 |
-
[14.61464119, 13.76078796, 12.96784878, 12.2308979, 11.54541874, 10.90732002, 10.31284904, 9.75859547, 9.24142551, 8.75849152, 8.30717278, 7.49001646, 6.77309084, 5.85520077, 4.65472794, 3.46139455, 2.45070267, 1.56271636, 0.72133851, 0.02916753],
|
| 59 |
-
[14.61464119, 13.76078796, 12.96784878, 12.2308979, 11.54541874, 10.90732002, 10.31284904, 9.75859547, 9.24142551, 8.75849152, 8.30717278, 7.88507891, 7.49001646, 6.77309084, 5.85520077, 4.65472794, 3.46139455, 2.45070267, 1.56271636, 0.72133851, 0.02916753],
|
| 60 |
-
],
|
| 61 |
-
0.90: [
|
| 62 |
-
[14.61464119, 6.77309084, 0.02916753],
|
| 63 |
-
[14.61464119, 7.49001646, 1.56271636, 0.02916753],
|
| 64 |
-
[14.61464119, 7.49001646, 3.07277966, 0.95350921, 0.02916753],
|
| 65 |
-
[14.61464119, 7.49001646, 4.86714602, 2.54230714, 0.89115214, 0.02916753],
|
| 66 |
-
[14.61464119, 11.54541874, 7.49001646, 4.86714602, 2.54230714, 0.89115214, 0.02916753],
|
| 67 |
-
[14.61464119, 11.54541874, 7.49001646, 5.09240818, 3.07277966, 1.61558151, 0.69515091, 0.02916753],
|
| 68 |
-
[14.61464119, 12.2308979, 8.75849152, 7.11996698, 4.86714602, 3.07277966, 1.61558151, 0.69515091, 0.02916753],
|
| 69 |
-
[14.61464119, 12.2308979, 8.75849152, 7.49001646, 5.85520077, 4.45427561, 2.95596409, 1.61558151, 0.69515091, 0.02916753],
|
| 70 |
-
[14.61464119, 12.2308979, 8.75849152, 7.49001646, 5.85520077, 4.45427561, 3.1956799, 2.19988537, 1.24153244, 0.57119018, 0.02916753],
|
| 71 |
-
[14.61464119, 12.96784878, 10.90732002, 8.75849152, 7.49001646, 5.85520077, 4.45427561, 3.1956799, 2.19988537, 1.24153244, 0.57119018, 0.02916753],
|
| 72 |
-
[14.61464119, 12.96784878, 11.54541874, 9.24142551, 8.30717278, 7.49001646, 5.85520077, 4.45427561, 3.1956799, 2.19988537, 1.24153244, 0.57119018, 0.02916753],
|
| 73 |
-
[14.61464119, 12.96784878, 11.54541874, 9.24142551, 8.30717278, 7.49001646, 6.14220476, 4.86714602, 3.75677586, 2.84484982, 1.84880662, 1.08895338, 0.52423614, 0.02916753],
|
| 74 |
-
[14.61464119, 13.76078796, 12.2308979, 10.90732002, 9.24142551, 8.30717278, 7.49001646, 6.14220476, 4.86714602, 3.75677586, 2.84484982, 1.84880662, 1.08895338, 0.52423614, 0.02916753],
|
| 75 |
-
[14.61464119, 13.76078796, 12.2308979, 10.90732002, 9.24142551, 8.30717278, 7.49001646, 6.44769001, 5.58536053, 4.45427561, 3.32507086, 2.45070267, 1.61558151, 0.95350921, 0.45573691, 0.02916753],
|
| 76 |
-
[14.61464119, 13.76078796, 12.96784878, 12.2308979, 10.90732002, 9.24142551, 8.30717278, 7.49001646, 6.44769001, 5.58536053, 4.45427561, 3.32507086, 2.45070267, 1.61558151, 0.95350921, 0.45573691, 0.02916753],
|
| 77 |
-
[14.61464119, 13.76078796, 12.96784878, 12.2308979, 10.90732002, 9.24142551, 8.30717278, 7.49001646, 6.77309084, 5.85520077, 4.86714602, 3.91689563, 3.07277966, 2.27973175, 1.56271636, 0.95350921, 0.45573691, 0.02916753],
|
| 78 |
-
[14.61464119, 13.76078796, 12.96784878, 12.2308979, 11.54541874, 10.31284904, 9.24142551, 8.30717278, 7.49001646, 6.77309084, 5.85520077, 4.86714602, 3.91689563, 3.07277966, 2.27973175, 1.56271636, 0.95350921, 0.45573691, 0.02916753],
|
| 79 |
-
[14.61464119, 13.76078796, 12.96784878, 12.2308979, 11.54541874, 10.31284904, 9.24142551, 8.75849152, 8.30717278, 7.49001646, 6.77309084, 5.85520077, 4.86714602, 3.91689563, 3.07277966, 2.27973175, 1.56271636, 0.95350921, 0.45573691, 0.02916753],
|
| 80 |
-
[14.61464119, 13.76078796, 12.96784878, 12.2308979, 11.54541874, 10.31284904, 9.24142551, 8.75849152, 8.30717278, 7.49001646, 6.77309084, 5.85520077, 5.09240818, 4.45427561, 3.60512662, 2.95596409, 2.19988537, 1.51179266, 0.89115214, 0.43325692, 0.02916753],
|
| 81 |
-
],
|
| 82 |
-
0.95: [
|
| 83 |
-
[14.61464119, 6.77309084, 0.02916753],
|
| 84 |
-
[14.61464119, 6.77309084, 1.56271636, 0.02916753],
|
| 85 |
-
[14.61464119, 7.49001646, 2.84484982, 0.89115214, 0.02916753],
|
| 86 |
-
[14.61464119, 7.49001646, 4.86714602, 2.36326075, 0.803307, 0.02916753],
|
| 87 |
-
[14.61464119, 7.49001646, 4.86714602, 2.95596409, 1.56271636, 0.64427125, 0.02916753],
|
| 88 |
-
[14.61464119, 11.54541874, 7.49001646, 4.86714602, 2.95596409, 1.56271636, 0.64427125, 0.02916753],
|
| 89 |
-
[14.61464119, 11.54541874, 7.49001646, 4.86714602, 3.07277966, 1.91321158, 1.08895338, 0.50118381, 0.02916753],
|
| 90 |
-
[14.61464119, 11.54541874, 7.49001646, 5.85520077, 4.45427561, 3.07277966, 1.91321158, 1.08895338, 0.50118381, 0.02916753],
|
| 91 |
-
[14.61464119, 12.2308979, 8.75849152, 7.49001646, 5.85520077, 4.45427561, 3.07277966, 1.91321158, 1.08895338, 0.50118381, 0.02916753],
|
| 92 |
-
[14.61464119, 12.2308979, 8.75849152, 7.49001646, 5.85520077, 4.45427561, 3.1956799, 2.19988537, 1.41535246, 0.803307, 0.38853383, 0.02916753],
|
| 93 |
-
[14.61464119, 12.2308979, 8.75849152, 7.49001646, 5.85520077, 4.65472794, 3.46139455, 2.6383388, 1.84880662, 1.24153244, 0.72133851, 0.34370604, 0.02916753],
|
| 94 |
-
[14.61464119, 12.96784878, 10.90732002, 8.75849152, 7.49001646, 5.85520077, 4.65472794, 3.46139455, 2.6383388, 1.84880662, 1.24153244, 0.72133851, 0.34370604, 0.02916753],
|
| 95 |
-
[14.61464119, 12.96784878, 10.90732002, 8.75849152, 7.49001646, 6.14220476, 4.86714602, 3.75677586, 2.95596409, 2.19988537, 1.56271636, 1.05362725, 0.64427125, 0.32104823, 0.02916753],
|
| 96 |
-
[14.61464119, 12.96784878, 10.90732002, 8.75849152, 7.49001646, 6.44769001, 5.58536053, 4.65472794, 3.60512662, 2.95596409, 2.19988537, 1.56271636, 1.05362725, 0.64427125, 0.32104823, 0.02916753],
|
| 97 |
-
[14.61464119, 12.96784878, 11.54541874, 9.24142551, 8.30717278, 7.49001646, 6.44769001, 5.58536053, 4.65472794, 3.60512662, 2.95596409, 2.19988537, 1.56271636, 1.05362725, 0.64427125, 0.32104823, 0.02916753],
|
| 98 |
-
[14.61464119, 12.96784878, 11.54541874, 9.24142551, 8.30717278, 7.49001646, 6.44769001, 5.58536053, 4.65472794, 3.75677586, 3.07277966, 2.45070267, 1.78698075, 1.24153244, 0.83188516, 0.50118381, 0.22545385, 0.02916753],
|
| 99 |
-
[14.61464119, 12.96784878, 11.54541874, 9.24142551, 8.30717278, 7.49001646, 6.77309084, 5.85520077, 5.09240818, 4.45427561, 3.60512662, 2.95596409, 2.36326075, 1.72759056, 1.24153244, 0.83188516, 0.50118381, 0.22545385, 0.02916753],
|
| 100 |
-
[14.61464119, 13.76078796, 12.2308979, 10.90732002, 9.24142551, 8.30717278, 7.49001646, 6.77309084, 5.85520077, 5.09240818, 4.45427561, 3.60512662, 2.95596409, 2.36326075, 1.72759056, 1.24153244, 0.83188516, 0.50118381, 0.22545385, 0.02916753],
|
| 101 |
-
[14.61464119, 13.76078796, 12.2308979, 10.90732002, 9.24142551, 8.30717278, 7.49001646, 6.77309084, 5.85520077, 5.09240818, 4.45427561, 3.75677586, 3.07277966, 2.45070267, 1.91321158, 1.46270394, 1.05362725, 0.72133851, 0.43325692, 0.19894916, 0.02916753],
|
| 102 |
-
],
|
| 103 |
-
1.00: [
|
| 104 |
-
[14.61464119, 1.56271636, 0.02916753],
|
| 105 |
-
[14.61464119, 6.77309084, 0.95350921, 0.02916753],
|
| 106 |
-
[14.61464119, 6.77309084, 2.36326075, 0.803307, 0.02916753],
|
| 107 |
-
[14.61464119, 7.11996698, 3.07277966, 1.56271636, 0.59516323, 0.02916753],
|
| 108 |
-
[14.61464119, 7.49001646, 4.86714602, 2.84484982, 1.41535246, 0.57119018, 0.02916753],
|
| 109 |
-
[14.61464119, 7.49001646, 4.86714602, 2.84484982, 1.61558151, 0.86115354, 0.38853383, 0.02916753],
|
| 110 |
-
[14.61464119, 11.54541874, 7.49001646, 4.86714602, 2.84484982, 1.61558151, 0.86115354, 0.38853383, 0.02916753],
|
| 111 |
-
[14.61464119, 11.54541874, 7.49001646, 4.86714602, 3.07277966, 1.98035145, 1.24153244, 0.72133851, 0.34370604, 0.02916753],
|
| 112 |
-
[14.61464119, 11.54541874, 7.49001646, 5.85520077, 4.45427561, 3.07277966, 1.98035145, 1.24153244, 0.72133851, 0.34370604, 0.02916753],
|
| 113 |
-
[14.61464119, 11.54541874, 7.49001646, 5.85520077, 4.45427561, 3.1956799, 2.27973175, 1.51179266, 0.95350921, 0.54755926, 0.25053367, 0.02916753],
|
| 114 |
-
[14.61464119, 11.54541874, 7.49001646, 5.85520077, 4.45427561, 3.1956799, 2.36326075, 1.61558151, 1.08895338, 0.72133851, 0.41087446, 0.17026083, 0.02916753],
|
| 115 |
-
[14.61464119, 11.54541874, 8.75849152, 7.49001646, 5.85520077, 4.45427561, 3.1956799, 2.36326075, 1.61558151, 1.08895338, 0.72133851, 0.41087446, 0.17026083, 0.02916753],
|
| 116 |
-
[14.61464119, 11.54541874, 8.75849152, 7.49001646, 5.85520077, 4.65472794, 3.60512662, 2.84484982, 2.12350607, 1.56271636, 1.08895338, 0.72133851, 0.41087446, 0.17026083, 0.02916753],
|
| 117 |
-
[14.61464119, 11.54541874, 8.75849152, 7.49001646, 5.85520077, 4.65472794, 3.60512662, 2.84484982, 2.19988537, 1.61558151, 1.162866, 0.803307, 0.50118381, 0.27464288, 0.09824532, 0.02916753],
|
| 118 |
-
[14.61464119, 11.54541874, 8.75849152, 7.49001646, 5.85520077, 4.65472794, 3.75677586, 3.07277966, 2.45070267, 1.84880662, 1.36964464, 1.01931262, 0.72133851, 0.45573691, 0.25053367, 0.09824532, 0.02916753],
|
| 119 |
-
[14.61464119, 11.54541874, 8.75849152, 7.49001646, 6.14220476, 5.09240818, 4.26497746, 3.46139455, 2.84484982, 2.19988537, 1.67050016, 1.24153244, 0.92192322, 0.64427125, 0.43325692, 0.25053367, 0.09824532, 0.02916753],
|
| 120 |
-
[14.61464119, 11.54541874, 8.75849152, 7.49001646, 6.14220476, 5.09240818, 4.26497746, 3.60512662, 2.95596409, 2.45070267, 1.91321158, 1.51179266, 1.12534678, 0.83188516, 0.59516323, 0.38853383, 0.22545385, 0.09824532, 0.02916753],
|
| 121 |
-
[14.61464119, 12.2308979, 9.24142551, 8.30717278, 7.49001646, 6.14220476, 5.09240818, 4.26497746, 3.60512662, 2.95596409, 2.45070267, 1.91321158, 1.51179266, 1.12534678, 0.83188516, 0.59516323, 0.38853383, 0.22545385, 0.09824532, 0.02916753],
|
| 122 |
-
[14.61464119, 12.2308979, 9.24142551, 8.30717278, 7.49001646, 6.77309084, 5.85520077, 5.09240818, 4.26497746, 3.60512662, 2.95596409, 2.45070267, 1.91321158, 1.51179266, 1.12534678, 0.83188516, 0.59516323, 0.38853383, 0.22545385, 0.09824532, 0.02916753],
|
| 123 |
-
],
|
| 124 |
-
1.05: [
|
| 125 |
-
[14.61464119, 0.95350921, 0.02916753],
|
| 126 |
-
[14.61464119, 6.77309084, 0.89115214, 0.02916753],
|
| 127 |
-
[14.61464119, 6.77309084, 2.05039096, 0.72133851, 0.02916753],
|
| 128 |
-
[14.61464119, 6.77309084, 2.84484982, 1.28281462, 0.52423614, 0.02916753],
|
| 129 |
-
[14.61464119, 6.77309084, 3.07277966, 1.61558151, 0.803307, 0.34370604, 0.02916753],
|
| 130 |
-
[14.61464119, 7.49001646, 4.86714602, 2.84484982, 1.56271636, 0.803307, 0.34370604, 0.02916753],
|
| 131 |
-
[14.61464119, 7.49001646, 4.86714602, 2.84484982, 1.61558151, 0.95350921, 0.52423614, 0.22545385, 0.02916753],
|
| 132 |
-
[14.61464119, 7.49001646, 4.86714602, 3.07277966, 1.98035145, 1.24153244, 0.74807048, 0.41087446, 0.17026083, 0.02916753],
|
| 133 |
-
[14.61464119, 7.49001646, 4.86714602, 3.1956799, 2.27973175, 1.51179266, 0.95350921, 0.59516323, 0.34370604, 0.13792117, 0.02916753],
|
| 134 |
-
[14.61464119, 7.49001646, 5.09240818, 3.46139455, 2.45070267, 1.61558151, 1.08895338, 0.72133851, 0.45573691, 0.25053367, 0.09824532, 0.02916753],
|
| 135 |
-
[14.61464119, 11.54541874, 7.49001646, 5.09240818, 3.46139455, 2.45070267, 1.61558151, 1.08895338, 0.72133851, 0.45573691, 0.25053367, 0.09824532, 0.02916753],
|
| 136 |
-
[14.61464119, 11.54541874, 7.49001646, 5.85520077, 4.45427561, 3.1956799, 2.36326075, 1.61558151, 1.08895338, 0.72133851, 0.45573691, 0.25053367, 0.09824532, 0.02916753],
|
| 137 |
-
[14.61464119, 11.54541874, 7.49001646, 5.85520077, 4.45427561, 3.1956799, 2.45070267, 1.72759056, 1.24153244, 0.86115354, 0.59516323, 0.38853383, 0.22545385, 0.09824532, 0.02916753],
|
| 138 |
-
[14.61464119, 11.54541874, 7.49001646, 5.85520077, 4.65472794, 3.60512662, 2.84484982, 2.19988537, 1.61558151, 1.162866, 0.83188516, 0.59516323, 0.38853383, 0.22545385, 0.09824532, 0.02916753],
|
| 139 |
-
[14.61464119, 11.54541874, 7.49001646, 5.85520077, 4.65472794, 3.60512662, 2.84484982, 2.19988537, 1.67050016, 1.28281462, 0.95350921, 0.72133851, 0.52423614, 0.34370604, 0.19894916, 0.09824532, 0.02916753],
|
| 140 |
-
[14.61464119, 11.54541874, 7.49001646, 5.85520077, 4.65472794, 3.60512662, 2.95596409, 2.36326075, 1.84880662, 1.41535246, 1.08895338, 0.83188516, 0.61951244, 0.45573691, 0.32104823, 0.19894916, 0.09824532, 0.02916753],
|
| 141 |
-
[14.61464119, 11.54541874, 7.49001646, 5.85520077, 4.65472794, 3.60512662, 2.95596409, 2.45070267, 1.91321158, 1.51179266, 1.20157266, 0.95350921, 0.74807048, 0.57119018, 0.43325692, 0.29807833, 0.19894916, 0.09824532, 0.02916753],
|
| 142 |
-
[14.61464119, 11.54541874, 8.30717278, 7.11996698, 5.85520077, 4.65472794, 3.60512662, 2.95596409, 2.45070267, 1.91321158, 1.51179266, 1.20157266, 0.95350921, 0.74807048, 0.57119018, 0.43325692, 0.29807833, 0.19894916, 0.09824532, 0.02916753],
|
| 143 |
-
[14.61464119, 11.54541874, 8.30717278, 7.11996698, 5.85520077, 4.65472794, 3.60512662, 2.95596409, 2.45070267, 1.98035145, 1.61558151, 1.32549286, 1.08895338, 0.86115354, 0.69515091, 0.54755926, 0.41087446, 0.29807833, 0.19894916, 0.09824532, 0.02916753],
|
| 144 |
-
],
|
| 145 |
-
1.10: [
|
| 146 |
-
[14.61464119, 0.89115214, 0.02916753],
|
| 147 |
-
[14.61464119, 2.36326075, 0.72133851, 0.02916753],
|
| 148 |
-
[14.61464119, 5.85520077, 1.61558151, 0.57119018, 0.02916753],
|
| 149 |
-
[14.61464119, 6.77309084, 2.45070267, 1.08895338, 0.45573691, 0.02916753],
|
| 150 |
-
[14.61464119, 6.77309084, 2.95596409, 1.56271636, 0.803307, 0.34370604, 0.02916753],
|
| 151 |
-
[14.61464119, 6.77309084, 3.07277966, 1.61558151, 0.89115214, 0.4783645, 0.19894916, 0.02916753],
|
| 152 |
-
[14.61464119, 6.77309084, 3.07277966, 1.84880662, 1.08895338, 0.64427125, 0.34370604, 0.13792117, 0.02916753],
|
| 153 |
-
[14.61464119, 7.49001646, 4.86714602, 2.84484982, 1.61558151, 0.95350921, 0.54755926, 0.27464288, 0.09824532, 0.02916753],
|
| 154 |
-
[14.61464119, 7.49001646, 4.86714602, 2.95596409, 1.91321158, 1.24153244, 0.803307, 0.4783645, 0.25053367, 0.09824532, 0.02916753],
|
| 155 |
-
[14.61464119, 7.49001646, 4.86714602, 3.07277966, 2.05039096, 1.41535246, 0.95350921, 0.64427125, 0.41087446, 0.22545385, 0.09824532, 0.02916753],
|
| 156 |
-
[14.61464119, 7.49001646, 4.86714602, 3.1956799, 2.27973175, 1.61558151, 1.12534678, 0.803307, 0.54755926, 0.36617002, 0.22545385, 0.09824532, 0.02916753],
|
| 157 |
-
[14.61464119, 7.49001646, 4.86714602, 3.32507086, 2.45070267, 1.72759056, 1.24153244, 0.89115214, 0.64427125, 0.45573691, 0.32104823, 0.19894916, 0.09824532, 0.02916753],
|
| 158 |
-
[14.61464119, 7.49001646, 5.09240818, 3.60512662, 2.84484982, 2.05039096, 1.51179266, 1.08895338, 0.803307, 0.59516323, 0.43325692, 0.29807833, 0.19894916, 0.09824532, 0.02916753],
|
| 159 |
-
[14.61464119, 7.49001646, 5.09240818, 3.60512662, 2.84484982, 2.12350607, 1.61558151, 1.24153244, 0.95350921, 0.72133851, 0.54755926, 0.41087446, 0.29807833, 0.19894916, 0.09824532, 0.02916753],
|
| 160 |
-
[14.61464119, 7.49001646, 5.85520077, 4.45427561, 3.1956799, 2.45070267, 1.84880662, 1.41535246, 1.08895338, 0.83188516, 0.64427125, 0.50118381, 0.36617002, 0.25053367, 0.17026083, 0.09824532, 0.02916753],
|
| 161 |
-
[14.61464119, 7.49001646, 5.85520077, 4.45427561, 3.1956799, 2.45070267, 1.91321158, 1.51179266, 1.20157266, 0.95350921, 0.74807048, 0.59516323, 0.45573691, 0.34370604, 0.25053367, 0.17026083, 0.09824532, 0.02916753],
|
| 162 |
-
[14.61464119, 7.49001646, 5.85520077, 4.45427561, 3.46139455, 2.84484982, 2.19988537, 1.72759056, 1.36964464, 1.08895338, 0.86115354, 0.69515091, 0.54755926, 0.43325692, 0.34370604, 0.25053367, 0.17026083, 0.09824532, 0.02916753],
|
| 163 |
-
[14.61464119, 11.54541874, 7.49001646, 5.85520077, 4.45427561, 3.46139455, 2.84484982, 2.19988537, 1.72759056, 1.36964464, 1.08895338, 0.86115354, 0.69515091, 0.54755926, 0.43325692, 0.34370604, 0.25053367, 0.17026083, 0.09824532, 0.02916753],
|
| 164 |
-
[14.61464119, 11.54541874, 7.49001646, 5.85520077, 4.45427561, 3.46139455, 2.84484982, 2.19988537, 1.72759056, 1.36964464, 1.08895338, 0.89115214, 0.72133851, 0.59516323, 0.4783645, 0.38853383, 0.29807833, 0.22545385, 0.17026083, 0.09824532, 0.02916753],
|
| 165 |
-
],
|
| 166 |
-
1.15: [
|
| 167 |
-
[14.61464119, 0.83188516, 0.02916753],
|
| 168 |
-
[14.61464119, 1.84880662, 0.59516323, 0.02916753],
|
| 169 |
-
[14.61464119, 5.85520077, 1.56271636, 0.52423614, 0.02916753],
|
| 170 |
-
[14.61464119, 5.85520077, 1.91321158, 0.83188516, 0.34370604, 0.02916753],
|
| 171 |
-
[14.61464119, 5.85520077, 2.45070267, 1.24153244, 0.59516323, 0.25053367, 0.02916753],
|
| 172 |
-
[14.61464119, 5.85520077, 2.84484982, 1.51179266, 0.803307, 0.41087446, 0.17026083, 0.02916753],
|
| 173 |
-
[14.61464119, 5.85520077, 2.84484982, 1.56271636, 0.89115214, 0.50118381, 0.25053367, 0.09824532, 0.02916753],
|
| 174 |
-
[14.61464119, 6.77309084, 3.07277966, 1.84880662, 1.12534678, 0.72133851, 0.43325692, 0.22545385, 0.09824532, 0.02916753],
|
| 175 |
-
[14.61464119, 6.77309084, 3.07277966, 1.91321158, 1.24153244, 0.803307, 0.52423614, 0.34370604, 0.19894916, 0.09824532, 0.02916753],
|
| 176 |
-
[14.61464119, 7.49001646, 4.86714602, 2.95596409, 1.91321158, 1.24153244, 0.803307, 0.52423614, 0.34370604, 0.19894916, 0.09824532, 0.02916753],
|
| 177 |
-
[14.61464119, 7.49001646, 4.86714602, 3.07277966, 2.05039096, 1.36964464, 0.95350921, 0.69515091, 0.4783645, 0.32104823, 0.19894916, 0.09824532, 0.02916753],
|
| 178 |
-
[14.61464119, 7.49001646, 4.86714602, 3.07277966, 2.12350607, 1.51179266, 1.08895338, 0.803307, 0.59516323, 0.43325692, 0.29807833, 0.19894916, 0.09824532, 0.02916753],
|
| 179 |
-
[14.61464119, 7.49001646, 4.86714602, 3.07277966, 2.12350607, 1.51179266, 1.08895338, 0.803307, 0.59516323, 0.45573691, 0.34370604, 0.25053367, 0.17026083, 0.09824532, 0.02916753],
|
| 180 |
-
[14.61464119, 7.49001646, 4.86714602, 3.07277966, 2.19988537, 1.61558151, 1.24153244, 0.95350921, 0.74807048, 0.59516323, 0.45573691, 0.34370604, 0.25053367, 0.17026083, 0.09824532, 0.02916753],
|
| 181 |
-
[14.61464119, 7.49001646, 4.86714602, 3.1956799, 2.45070267, 1.78698075, 1.32549286, 1.01931262, 0.803307, 0.64427125, 0.50118381, 0.38853383, 0.29807833, 0.22545385, 0.17026083, 0.09824532, 0.02916753],
|
| 182 |
-
[14.61464119, 7.49001646, 4.86714602, 3.1956799, 2.45070267, 1.78698075, 1.32549286, 1.01931262, 0.803307, 0.64427125, 0.52423614, 0.41087446, 0.32104823, 0.25053367, 0.19894916, 0.13792117, 0.09824532, 0.02916753],
|
| 183 |
-
[14.61464119, 7.49001646, 4.86714602, 3.1956799, 2.45070267, 1.84880662, 1.41535246, 1.12534678, 0.89115214, 0.72133851, 0.59516323, 0.4783645, 0.38853383, 0.32104823, 0.25053367, 0.19894916, 0.13792117, 0.09824532, 0.02916753],
|
| 184 |
-
[14.61464119, 7.49001646, 4.86714602, 3.1956799, 2.45070267, 1.84880662, 1.41535246, 1.12534678, 0.89115214, 0.72133851, 0.59516323, 0.50118381, 0.41087446, 0.34370604, 0.27464288, 0.22545385, 0.17026083, 0.13792117, 0.09824532, 0.02916753],
|
| 185 |
-
[14.61464119, 7.49001646, 4.86714602, 3.1956799, 2.45070267, 1.84880662, 1.41535246, 1.12534678, 0.89115214, 0.72133851, 0.59516323, 0.50118381, 0.41087446, 0.34370604, 0.29807833, 0.25053367, 0.19894916, 0.17026083, 0.13792117, 0.09824532, 0.02916753],
|
| 186 |
-
],
|
| 187 |
-
1.20: [
|
| 188 |
-
[14.61464119, 0.803307, 0.02916753],
|
| 189 |
-
[14.61464119, 1.56271636, 0.52423614, 0.02916753],
|
| 190 |
-
[14.61464119, 2.36326075, 0.92192322, 0.36617002, 0.02916753],
|
| 191 |
-
[14.61464119, 2.84484982, 1.24153244, 0.59516323, 0.25053367, 0.02916753],
|
| 192 |
-
[14.61464119, 5.85520077, 2.05039096, 0.95350921, 0.45573691, 0.17026083, 0.02916753],
|
| 193 |
-
[14.61464119, 5.85520077, 2.45070267, 1.24153244, 0.64427125, 0.29807833, 0.09824532, 0.02916753],
|
| 194 |
-
[14.61464119, 5.85520077, 2.45070267, 1.36964464, 0.803307, 0.45573691, 0.25053367, 0.09824532, 0.02916753],
|
| 195 |
-
[14.61464119, 5.85520077, 2.84484982, 1.61558151, 0.95350921, 0.59516323, 0.36617002, 0.19894916, 0.09824532, 0.02916753],
|
| 196 |
-
[14.61464119, 5.85520077, 2.84484982, 1.67050016, 1.08895338, 0.74807048, 0.50118381, 0.32104823, 0.19894916, 0.09824532, 0.02916753],
|
| 197 |
-
[14.61464119, 5.85520077, 2.95596409, 1.84880662, 1.24153244, 0.83188516, 0.59516323, 0.41087446, 0.27464288, 0.17026083, 0.09824532, 0.02916753],
|
| 198 |
-
[14.61464119, 5.85520077, 3.07277966, 1.98035145, 1.36964464, 0.95350921, 0.69515091, 0.50118381, 0.36617002, 0.25053367, 0.17026083, 0.09824532, 0.02916753],
|
| 199 |
-
[14.61464119, 6.77309084, 3.46139455, 2.36326075, 1.56271636, 1.08895338, 0.803307, 0.59516323, 0.45573691, 0.34370604, 0.25053367, 0.17026083, 0.09824532, 0.02916753],
|
| 200 |
-
[14.61464119, 6.77309084, 3.46139455, 2.45070267, 1.61558151, 1.162866, 0.86115354, 0.64427125, 0.50118381, 0.38853383, 0.29807833, 0.22545385, 0.17026083, 0.09824532, 0.02916753],
|
| 201 |
-
[14.61464119, 7.49001646, 4.65472794, 3.07277966, 2.12350607, 1.51179266, 1.08895338, 0.83188516, 0.64427125, 0.50118381, 0.38853383, 0.29807833, 0.22545385, 0.17026083, 0.09824532, 0.02916753],
|
| 202 |
-
[14.61464119, 7.49001646, 4.65472794, 3.07277966, 2.12350607, 1.51179266, 1.08895338, 0.83188516, 0.64427125, 0.50118381, 0.41087446, 0.32104823, 0.25053367, 0.19894916, 0.13792117, 0.09824532, 0.02916753],
|
| 203 |
-
[14.61464119, 7.49001646, 4.65472794, 3.07277966, 2.12350607, 1.51179266, 1.08895338, 0.83188516, 0.64427125, 0.50118381, 0.41087446, 0.34370604, 0.27464288, 0.22545385, 0.17026083, 0.13792117, 0.09824532, 0.02916753],
|
| 204 |
-
[14.61464119, 7.49001646, 4.65472794, 3.07277966, 2.19988537, 1.61558151, 1.20157266, 0.92192322, 0.72133851, 0.57119018, 0.45573691, 0.36617002, 0.29807833, 0.25053367, 0.19894916, 0.17026083, 0.13792117, 0.09824532, 0.02916753],
|
| 205 |
-
[14.61464119, 7.49001646, 4.65472794, 3.07277966, 2.19988537, 1.61558151, 1.24153244, 0.95350921, 0.74807048, 0.59516323, 0.4783645, 0.38853383, 0.32104823, 0.27464288, 0.22545385, 0.19894916, 0.17026083, 0.13792117, 0.09824532, 0.02916753],
|
| 206 |
-
[14.61464119, 7.49001646, 4.65472794, 3.07277966, 2.19988537, 1.61558151, 1.24153244, 0.95350921, 0.74807048, 0.59516323, 0.50118381, 0.41087446, 0.34370604, 0.29807833, 0.25053367, 0.22545385, 0.19894916, 0.17026083, 0.13792117, 0.09824532, 0.02916753],
|
| 207 |
-
],
|
| 208 |
-
1.25: [
|
| 209 |
-
[14.61464119, 0.72133851, 0.02916753],
|
| 210 |
-
[14.61464119, 1.56271636, 0.50118381, 0.02916753],
|
| 211 |
-
[14.61464119, 2.05039096, 0.803307, 0.32104823, 0.02916753],
|
| 212 |
-
[14.61464119, 2.36326075, 0.95350921, 0.43325692, 0.17026083, 0.02916753],
|
| 213 |
-
[14.61464119, 2.84484982, 1.24153244, 0.59516323, 0.27464288, 0.09824532, 0.02916753],
|
| 214 |
-
[14.61464119, 3.07277966, 1.51179266, 0.803307, 0.43325692, 0.22545385, 0.09824532, 0.02916753],
|
| 215 |
-
[14.61464119, 5.85520077, 2.36326075, 1.24153244, 0.72133851, 0.41087446, 0.22545385, 0.09824532, 0.02916753],
|
| 216 |
-
[14.61464119, 5.85520077, 2.45070267, 1.36964464, 0.83188516, 0.52423614, 0.34370604, 0.19894916, 0.09824532, 0.02916753],
|
| 217 |
-
[14.61464119, 5.85520077, 2.84484982, 1.61558151, 0.98595673, 0.64427125, 0.43325692, 0.27464288, 0.17026083, 0.09824532, 0.02916753],
|
| 218 |
-
[14.61464119, 5.85520077, 2.84484982, 1.67050016, 1.08895338, 0.74807048, 0.52423614, 0.36617002, 0.25053367, 0.17026083, 0.09824532, 0.02916753],
|
| 219 |
-
[14.61464119, 5.85520077, 2.84484982, 1.72759056, 1.162866, 0.803307, 0.59516323, 0.45573691, 0.34370604, 0.25053367, 0.17026083, 0.09824532, 0.02916753],
|
| 220 |
-
[14.61464119, 5.85520077, 2.95596409, 1.84880662, 1.24153244, 0.86115354, 0.64427125, 0.4783645, 0.36617002, 0.27464288, 0.19894916, 0.13792117, 0.09824532, 0.02916753],
|
| 221 |
-
[14.61464119, 5.85520077, 2.95596409, 1.84880662, 1.28281462, 0.92192322, 0.69515091, 0.52423614, 0.41087446, 0.32104823, 0.25053367, 0.19894916, 0.13792117, 0.09824532, 0.02916753],
|
| 222 |
-
[14.61464119, 5.85520077, 2.95596409, 1.91321158, 1.32549286, 0.95350921, 0.72133851, 0.54755926, 0.43325692, 0.34370604, 0.27464288, 0.22545385, 0.17026083, 0.13792117, 0.09824532, 0.02916753],
|
| 223 |
-
[14.61464119, 5.85520077, 2.95596409, 1.91321158, 1.32549286, 0.95350921, 0.72133851, 0.57119018, 0.45573691, 0.36617002, 0.29807833, 0.25053367, 0.19894916, 0.17026083, 0.13792117, 0.09824532, 0.02916753],
|
| 224 |
-
[14.61464119, 5.85520077, 2.95596409, 1.91321158, 1.32549286, 0.95350921, 0.74807048, 0.59516323, 0.4783645, 0.38853383, 0.32104823, 0.27464288, 0.22545385, 0.19894916, 0.17026083, 0.13792117, 0.09824532, 0.02916753],
|
| 225 |
-
[14.61464119, 5.85520077, 3.07277966, 2.05039096, 1.41535246, 1.05362725, 0.803307, 0.61951244, 0.50118381, 0.41087446, 0.34370604, 0.29807833, 0.25053367, 0.22545385, 0.19894916, 0.17026083, 0.13792117, 0.09824532, 0.02916753],
|
| 226 |
-
[14.61464119, 5.85520077, 3.07277966, 2.05039096, 1.41535246, 1.05362725, 0.803307, 0.64427125, 0.52423614, 0.43325692, 0.36617002, 0.32104823, 0.27464288, 0.25053367, 0.22545385, 0.19894916, 0.17026083, 0.13792117, 0.09824532, 0.02916753],
|
| 227 |
-
[14.61464119, 5.85520077, 3.07277966, 2.05039096, 1.46270394, 1.08895338, 0.83188516, 0.66947293, 0.54755926, 0.45573691, 0.38853383, 0.34370604, 0.29807833, 0.27464288, 0.25053367, 0.22545385, 0.19894916, 0.17026083, 0.13792117, 0.09824532, 0.02916753],
|
| 228 |
-
],
|
| 229 |
-
1.30: [
|
| 230 |
-
[14.61464119, 0.72133851, 0.02916753],
|
| 231 |
-
[14.61464119, 1.24153244, 0.43325692, 0.02916753],
|
| 232 |
-
[14.61464119, 1.56271636, 0.59516323, 0.22545385, 0.02916753],
|
| 233 |
-
[14.61464119, 1.84880662, 0.803307, 0.36617002, 0.13792117, 0.02916753],
|
| 234 |
-
[14.61464119, 2.36326075, 1.01931262, 0.52423614, 0.25053367, 0.09824532, 0.02916753],
|
| 235 |
-
[14.61464119, 2.84484982, 1.36964464, 0.74807048, 0.41087446, 0.22545385, 0.09824532, 0.02916753],
|
| 236 |
-
[14.61464119, 3.07277966, 1.56271636, 0.89115214, 0.54755926, 0.34370604, 0.19894916, 0.09824532, 0.02916753],
|
| 237 |
-
[14.61464119, 3.07277966, 1.61558151, 0.95350921, 0.61951244, 0.41087446, 0.27464288, 0.17026083, 0.09824532, 0.02916753],
|
| 238 |
-
[14.61464119, 5.85520077, 2.45070267, 1.36964464, 0.83188516, 0.54755926, 0.36617002, 0.25053367, 0.17026083, 0.09824532, 0.02916753],
|
| 239 |
-
[14.61464119, 5.85520077, 2.45070267, 1.41535246, 0.92192322, 0.64427125, 0.45573691, 0.34370604, 0.25053367, 0.17026083, 0.09824532, 0.02916753],
|
| 240 |
-
[14.61464119, 5.85520077, 2.6383388, 1.56271636, 1.01931262, 0.72133851, 0.50118381, 0.36617002, 0.27464288, 0.19894916, 0.13792117, 0.09824532, 0.02916753],
|
| 241 |
-
[14.61464119, 5.85520077, 2.84484982, 1.61558151, 1.05362725, 0.74807048, 0.54755926, 0.41087446, 0.32104823, 0.25053367, 0.19894916, 0.13792117, 0.09824532, 0.02916753],
|
| 242 |
-
[14.61464119, 5.85520077, 2.84484982, 1.61558151, 1.08895338, 0.77538133, 0.57119018, 0.43325692, 0.34370604, 0.27464288, 0.22545385, 0.17026083, 0.13792117, 0.09824532, 0.02916753],
|
| 243 |
-
[14.61464119, 5.85520077, 2.84484982, 1.61558151, 1.08895338, 0.803307, 0.59516323, 0.45573691, 0.36617002, 0.29807833, 0.25053367, 0.19894916, 0.17026083, 0.13792117, 0.09824532, 0.02916753],
|
| 244 |
-
[14.61464119, 5.85520077, 2.84484982, 1.61558151, 1.08895338, 0.803307, 0.59516323, 0.4783645, 0.38853383, 0.32104823, 0.27464288, 0.22545385, 0.19894916, 0.17026083, 0.13792117, 0.09824532, 0.02916753],
|
| 245 |
-
[14.61464119, 5.85520077, 2.84484982, 1.72759056, 1.162866, 0.83188516, 0.64427125, 0.50118381, 0.41087446, 0.34370604, 0.29807833, 0.25053367, 0.22545385, 0.19894916, 0.17026083, 0.13792117, 0.09824532, 0.02916753],
|
| 246 |
-
[14.61464119, 5.85520077, 2.84484982, 1.72759056, 1.162866, 0.83188516, 0.64427125, 0.52423614, 0.43325692, 0.36617002, 0.32104823, 0.27464288, 0.25053367, 0.22545385, 0.19894916, 0.17026083, 0.13792117, 0.09824532, 0.02916753],
|
| 247 |
-
[14.61464119, 5.85520077, 2.84484982, 1.78698075, 1.24153244, 0.92192322, 0.72133851, 0.57119018, 0.45573691, 0.38853383, 0.34370604, 0.29807833, 0.27464288, 0.25053367, 0.22545385, 0.19894916, 0.17026083, 0.13792117, 0.09824532, 0.02916753],
|
| 248 |
-
[14.61464119, 5.85520077, 2.84484982, 1.78698075, 1.24153244, 0.92192322, 0.72133851, 0.57119018, 0.4783645, 0.41087446, 0.36617002, 0.32104823, 0.29807833, 0.27464288, 0.25053367, 0.22545385, 0.19894916, 0.17026083, 0.13792117, 0.09824532, 0.02916753],
|
| 249 |
-
],
|
| 250 |
-
1.35: [
|
| 251 |
-
[14.61464119, 0.69515091, 0.02916753],
|
| 252 |
-
[14.61464119, 0.95350921, 0.34370604, 0.02916753],
|
| 253 |
-
[14.61464119, 1.56271636, 0.57119018, 0.19894916, 0.02916753],
|
| 254 |
-
[14.61464119, 1.61558151, 0.69515091, 0.29807833, 0.09824532, 0.02916753],
|
| 255 |
-
[14.61464119, 1.84880662, 0.83188516, 0.43325692, 0.22545385, 0.09824532, 0.02916753],
|
| 256 |
-
[14.61464119, 2.45070267, 1.162866, 0.64427125, 0.36617002, 0.19894916, 0.09824532, 0.02916753],
|
| 257 |
-
[14.61464119, 2.84484982, 1.36964464, 0.803307, 0.50118381, 0.32104823, 0.19894916, 0.09824532, 0.02916753],
|
| 258 |
-
[14.61464119, 2.84484982, 1.41535246, 0.83188516, 0.54755926, 0.36617002, 0.25053367, 0.17026083, 0.09824532, 0.02916753],
|
| 259 |
-
[14.61464119, 2.84484982, 1.56271636, 0.95350921, 0.64427125, 0.45573691, 0.32104823, 0.22545385, 0.17026083, 0.09824532, 0.02916753],
|
| 260 |
-
[14.61464119, 2.84484982, 1.56271636, 0.95350921, 0.64427125, 0.45573691, 0.34370604, 0.25053367, 0.19894916, 0.13792117, 0.09824532, 0.02916753],
|
| 261 |
-
[14.61464119, 3.07277966, 1.61558151, 1.01931262, 0.72133851, 0.52423614, 0.38853383, 0.29807833, 0.22545385, 0.17026083, 0.13792117, 0.09824532, 0.02916753],
|
| 262 |
-
[14.61464119, 3.07277966, 1.61558151, 1.01931262, 0.72133851, 0.52423614, 0.41087446, 0.32104823, 0.25053367, 0.19894916, 0.17026083, 0.13792117, 0.09824532, 0.02916753],
|
| 263 |
-
[14.61464119, 3.07277966, 1.61558151, 1.05362725, 0.74807048, 0.54755926, 0.43325692, 0.34370604, 0.27464288, 0.22545385, 0.19894916, 0.17026083, 0.13792117, 0.09824532, 0.02916753],
|
| 264 |
-
[14.61464119, 3.07277966, 1.72759056, 1.12534678, 0.803307, 0.59516323, 0.45573691, 0.36617002, 0.29807833, 0.25053367, 0.22545385, 0.19894916, 0.17026083, 0.13792117, 0.09824532, 0.02916753],
|
| 265 |
-
[14.61464119, 3.07277966, 1.72759056, 1.12534678, 0.803307, 0.59516323, 0.4783645, 0.38853383, 0.32104823, 0.27464288, 0.25053367, 0.22545385, 0.19894916, 0.17026083, 0.13792117, 0.09824532, 0.02916753],
|
| 266 |
-
[14.61464119, 5.85520077, 2.45070267, 1.51179266, 1.01931262, 0.74807048, 0.57119018, 0.45573691, 0.36617002, 0.32104823, 0.27464288, 0.25053367, 0.22545385, 0.19894916, 0.17026083, 0.13792117, 0.09824532, 0.02916753],
|
| 267 |
-
[14.61464119, 5.85520077, 2.6383388, 1.61558151, 1.08895338, 0.803307, 0.61951244, 0.50118381, 0.41087446, 0.34370604, 0.29807833, 0.27464288, 0.25053367, 0.22545385, 0.19894916, 0.17026083, 0.13792117, 0.09824532, 0.02916753],
|
| 268 |
-
[14.61464119, 5.85520077, 2.6383388, 1.61558151, 1.08895338, 0.803307, 0.64427125, 0.52423614, 0.43325692, 0.36617002, 0.32104823, 0.29807833, 0.27464288, 0.25053367, 0.22545385, 0.19894916, 0.17026083, 0.13792117, 0.09824532, 0.02916753],
|
| 269 |
-
[14.61464119, 5.85520077, 2.6383388, 1.61558151, 1.08895338, 0.803307, 0.64427125, 0.52423614, 0.45573691, 0.38853383, 0.34370604, 0.32104823, 0.29807833, 0.27464288, 0.25053367, 0.22545385, 0.19894916, 0.17026083, 0.13792117, 0.09824532, 0.02916753],
|
| 270 |
-
],
|
| 271 |
-
1.40: [
|
| 272 |
-
[14.61464119, 0.59516323, 0.02916753],
|
| 273 |
-
[14.61464119, 0.95350921, 0.34370604, 0.02916753],
|
| 274 |
-
[14.61464119, 1.08895338, 0.43325692, 0.13792117, 0.02916753],
|
| 275 |
-
[14.61464119, 1.56271636, 0.64427125, 0.27464288, 0.09824532, 0.02916753],
|
| 276 |
-
[14.61464119, 1.61558151, 0.803307, 0.43325692, 0.22545385, 0.09824532, 0.02916753],
|
| 277 |
-
[14.61464119, 2.05039096, 0.95350921, 0.54755926, 0.34370604, 0.19894916, 0.09824532, 0.02916753],
|
| 278 |
-
[14.61464119, 2.45070267, 1.24153244, 0.72133851, 0.43325692, 0.27464288, 0.17026083, 0.09824532, 0.02916753],
|
| 279 |
-
[14.61464119, 2.45070267, 1.24153244, 0.74807048, 0.50118381, 0.34370604, 0.25053367, 0.17026083, 0.09824532, 0.02916753],
|
| 280 |
-
[14.61464119, 2.45070267, 1.28281462, 0.803307, 0.52423614, 0.36617002, 0.27464288, 0.19894916, 0.13792117, 0.09824532, 0.02916753],
|
| 281 |
-
[14.61464119, 2.45070267, 1.28281462, 0.803307, 0.54755926, 0.38853383, 0.29807833, 0.22545385, 0.17026083, 0.13792117, 0.09824532, 0.02916753],
|
| 282 |
-
[14.61464119, 2.84484982, 1.41535246, 0.86115354, 0.59516323, 0.43325692, 0.32104823, 0.25053367, 0.19894916, 0.17026083, 0.13792117, 0.09824532, 0.02916753],
|
| 283 |
-
[14.61464119, 2.84484982, 1.51179266, 0.95350921, 0.64427125, 0.45573691, 0.34370604, 0.27464288, 0.22545385, 0.19894916, 0.17026083, 0.13792117, 0.09824532, 0.02916753],
|
| 284 |
-
[14.61464119, 2.84484982, 1.51179266, 0.95350921, 0.64427125, 0.4783645, 0.36617002, 0.29807833, 0.25053367, 0.22545385, 0.19894916, 0.17026083, 0.13792117, 0.09824532, 0.02916753],
|
| 285 |
-
[14.61464119, 2.84484982, 1.56271636, 0.98595673, 0.69515091, 0.52423614, 0.41087446, 0.34370604, 0.29807833, 0.25053367, 0.22545385, 0.19894916, 0.17026083, 0.13792117, 0.09824532, 0.02916753],
|
| 286 |
-
[14.61464119, 2.84484982, 1.56271636, 1.01931262, 0.72133851, 0.54755926, 0.43325692, 0.36617002, 0.32104823, 0.27464288, 0.25053367, 0.22545385, 0.19894916, 0.17026083, 0.13792117, 0.09824532, 0.02916753],
|
| 287 |
-
[14.61464119, 2.84484982, 1.61558151, 1.05362725, 0.74807048, 0.57119018, 0.45573691, 0.38853383, 0.34370604, 0.29807833, 0.27464288, 0.25053367, 0.22545385, 0.19894916, 0.17026083, 0.13792117, 0.09824532, 0.02916753],
|
| 288 |
-
[14.61464119, 2.84484982, 1.61558151, 1.08895338, 0.803307, 0.61951244, 0.50118381, 0.41087446, 0.36617002, 0.32104823, 0.29807833, 0.27464288, 0.25053367, 0.22545385, 0.19894916, 0.17026083, 0.13792117, 0.09824532, 0.02916753],
|
| 289 |
-
[14.61464119, 2.84484982, 1.61558151, 1.08895338, 0.803307, 0.61951244, 0.50118381, 0.43325692, 0.38853383, 0.34370604, 0.32104823, 0.29807833, 0.27464288, 0.25053367, 0.22545385, 0.19894916, 0.17026083, 0.13792117, 0.09824532, 0.02916753],
|
| 290 |
-
[14.61464119, 2.84484982, 1.61558151, 1.08895338, 0.803307, 0.64427125, 0.52423614, 0.45573691, 0.41087446, 0.36617002, 0.34370604, 0.32104823, 0.29807833, 0.27464288, 0.25053367, 0.22545385, 0.19894916, 0.17026083, 0.13792117, 0.09824532, 0.02916753],
|
| 291 |
-
],
|
| 292 |
-
1.45: [
|
| 293 |
-
[14.61464119, 0.59516323, 0.02916753],
|
| 294 |
-
[14.61464119, 0.803307, 0.25053367, 0.02916753],
|
| 295 |
-
[14.61464119, 0.95350921, 0.34370604, 0.09824532, 0.02916753],
|
| 296 |
-
[14.61464119, 1.24153244, 0.54755926, 0.25053367, 0.09824532, 0.02916753],
|
| 297 |
-
[14.61464119, 1.56271636, 0.72133851, 0.36617002, 0.19894916, 0.09824532, 0.02916753],
|
| 298 |
-
[14.61464119, 1.61558151, 0.803307, 0.45573691, 0.27464288, 0.17026083, 0.09824532, 0.02916753],
|
| 299 |
-
[14.61464119, 1.91321158, 0.95350921, 0.57119018, 0.36617002, 0.25053367, 0.17026083, 0.09824532, 0.02916753],
|
| 300 |
-
[14.61464119, 2.19988537, 1.08895338, 0.64427125, 0.41087446, 0.27464288, 0.19894916, 0.13792117, 0.09824532, 0.02916753],
|
| 301 |
-
[14.61464119, 2.45070267, 1.24153244, 0.74807048, 0.50118381, 0.34370604, 0.25053367, 0.19894916, 0.13792117, 0.09824532, 0.02916753],
|
| 302 |
-
[14.61464119, 2.45070267, 1.24153244, 0.74807048, 0.50118381, 0.36617002, 0.27464288, 0.22545385, 0.17026083, 0.13792117, 0.09824532, 0.02916753],
|
| 303 |
-
[14.61464119, 2.45070267, 1.28281462, 0.803307, 0.54755926, 0.41087446, 0.32104823, 0.25053367, 0.19894916, 0.17026083, 0.13792117, 0.09824532, 0.02916753],
|
| 304 |
-
[14.61464119, 2.45070267, 1.28281462, 0.803307, 0.57119018, 0.43325692, 0.34370604, 0.27464288, 0.22545385, 0.19894916, 0.17026083, 0.13792117, 0.09824532, 0.02916753],
|
| 305 |
-
[14.61464119, 2.45070267, 1.28281462, 0.83188516, 0.59516323, 0.45573691, 0.36617002, 0.29807833, 0.25053367, 0.22545385, 0.19894916, 0.17026083, 0.13792117, 0.09824532, 0.02916753],
|
| 306 |
-
[14.61464119, 2.45070267, 1.28281462, 0.83188516, 0.59516323, 0.45573691, 0.36617002, 0.32104823, 0.27464288, 0.25053367, 0.22545385, 0.19894916, 0.17026083, 0.13792117, 0.09824532, 0.02916753],
|
| 307 |
-
[14.61464119, 2.84484982, 1.51179266, 0.95350921, 0.69515091, 0.52423614, 0.41087446, 0.34370604, 0.29807833, 0.27464288, 0.25053367, 0.22545385, 0.19894916, 0.17026083, 0.13792117, 0.09824532, 0.02916753],
|
| 308 |
-
[14.61464119, 2.84484982, 1.51179266, 0.95350921, 0.69515091, 0.52423614, 0.43325692, 0.36617002, 0.32104823, 0.29807833, 0.27464288, 0.25053367, 0.22545385, 0.19894916, 0.17026083, 0.13792117, 0.09824532, 0.02916753],
|
| 309 |
-
[14.61464119, 2.84484982, 1.56271636, 0.98595673, 0.72133851, 0.54755926, 0.45573691, 0.38853383, 0.34370604, 0.32104823, 0.29807833, 0.27464288, 0.25053367, 0.22545385, 0.19894916, 0.17026083, 0.13792117, 0.09824532, 0.02916753],
|
| 310 |
-
[14.61464119, 2.84484982, 1.56271636, 1.01931262, 0.74807048, 0.57119018, 0.4783645, 0.41087446, 0.36617002, 0.34370604, 0.32104823, 0.29807833, 0.27464288, 0.25053367, 0.22545385, 0.19894916, 0.17026083, 0.13792117, 0.09824532, 0.02916753],
|
| 311 |
-
[14.61464119, 2.84484982, 1.56271636, 1.01931262, 0.74807048, 0.59516323, 0.50118381, 0.43325692, 0.38853383, 0.36617002, 0.34370604, 0.32104823, 0.29807833, 0.27464288, 0.25053367, 0.22545385, 0.19894916, 0.17026083, 0.13792117, 0.09824532, 0.02916753],
|
| 312 |
-
],
|
| 313 |
-
1.50: [
|
| 314 |
-
[14.61464119, 0.54755926, 0.02916753],
|
| 315 |
-
[14.61464119, 0.803307, 0.25053367, 0.02916753],
|
| 316 |
-
[14.61464119, 0.86115354, 0.32104823, 0.09824532, 0.02916753],
|
| 317 |
-
[14.61464119, 1.24153244, 0.54755926, 0.25053367, 0.09824532, 0.02916753],
|
| 318 |
-
[14.61464119, 1.56271636, 0.72133851, 0.36617002, 0.19894916, 0.09824532, 0.02916753],
|
| 319 |
-
[14.61464119, 1.61558151, 0.803307, 0.45573691, 0.27464288, 0.17026083, 0.09824532, 0.02916753],
|
| 320 |
-
[14.61464119, 1.61558151, 0.83188516, 0.52423614, 0.34370604, 0.25053367, 0.17026083, 0.09824532, 0.02916753],
|
| 321 |
-
[14.61464119, 1.84880662, 0.95350921, 0.59516323, 0.38853383, 0.27464288, 0.19894916, 0.13792117, 0.09824532, 0.02916753],
|
| 322 |
-
[14.61464119, 1.84880662, 0.95350921, 0.59516323, 0.41087446, 0.29807833, 0.22545385, 0.17026083, 0.13792117, 0.09824532, 0.02916753],
|
| 323 |
-
[14.61464119, 1.84880662, 0.95350921, 0.61951244, 0.43325692, 0.32104823, 0.25053367, 0.19894916, 0.17026083, 0.13792117, 0.09824532, 0.02916753],
|
| 324 |
-
[14.61464119, 2.19988537, 1.12534678, 0.72133851, 0.50118381, 0.36617002, 0.27464288, 0.22545385, 0.19894916, 0.17026083, 0.13792117, 0.09824532, 0.02916753],
|
| 325 |
-
[14.61464119, 2.19988537, 1.12534678, 0.72133851, 0.50118381, 0.36617002, 0.29807833, 0.25053367, 0.22545385, 0.19894916, 0.17026083, 0.13792117, 0.09824532, 0.02916753],
|
| 326 |
-
[14.61464119, 2.36326075, 1.24153244, 0.803307, 0.57119018, 0.43325692, 0.34370604, 0.29807833, 0.25053367, 0.22545385, 0.19894916, 0.17026083, 0.13792117, 0.09824532, 0.02916753],
|
| 327 |
-
[14.61464119, 2.36326075, 1.24153244, 0.803307, 0.57119018, 0.43325692, 0.34370604, 0.29807833, 0.27464288, 0.25053367, 0.22545385, 0.19894916, 0.17026083, 0.13792117, 0.09824532, 0.02916753],
|
| 328 |
-
[14.61464119, 2.36326075, 1.24153244, 0.803307, 0.59516323, 0.45573691, 0.36617002, 0.32104823, 0.29807833, 0.27464288, 0.25053367, 0.22545385, 0.19894916, 0.17026083, 0.13792117, 0.09824532, 0.02916753],
|
| 329 |
-
[14.61464119, 2.36326075, 1.24153244, 0.803307, 0.59516323, 0.45573691, 0.38853383, 0.34370604, 0.32104823, 0.29807833, 0.27464288, 0.25053367, 0.22545385, 0.19894916, 0.17026083, 0.13792117, 0.09824532, 0.02916753],
|
| 330 |
-
[14.61464119, 2.45070267, 1.32549286, 0.86115354, 0.64427125, 0.50118381, 0.41087446, 0.36617002, 0.34370604, 0.32104823, 0.29807833, 0.27464288, 0.25053367, 0.22545385, 0.19894916, 0.17026083, 0.13792117, 0.09824532, 0.02916753],
|
| 331 |
-
[14.61464119, 2.45070267, 1.36964464, 0.92192322, 0.69515091, 0.54755926, 0.45573691, 0.41087446, 0.36617002, 0.34370604, 0.32104823, 0.29807833, 0.27464288, 0.25053367, 0.22545385, 0.19894916, 0.17026083, 0.13792117, 0.09824532, 0.02916753],
|
| 332 |
-
[14.61464119, 2.45070267, 1.41535246, 0.95350921, 0.72133851, 0.57119018, 0.4783645, 0.43325692, 0.38853383, 0.36617002, 0.34370604, 0.32104823, 0.29807833, 0.27464288, 0.25053367, 0.22545385, 0.19894916, 0.17026083, 0.13792117, 0.09824532, 0.02916753],
|
| 333 |
-
],
|
| 334 |
-
}
|
| 335 |
-
|
| 336 |
-
class GITSScheduler:
|
| 337 |
-
@classmethod
|
| 338 |
-
def INPUT_TYPES(s):
|
| 339 |
-
return {"required":
|
| 340 |
-
{"coeff": ("FLOAT", {"default": 1.20, "min": 0.80, "max": 1.50, "step": 0.05}),
|
| 341 |
-
"steps": ("INT", {"default": 10, "min": 2, "max": 1000}),
|
| 342 |
-
"denoise": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}),
|
| 343 |
-
}
|
| 344 |
-
}
|
| 345 |
-
RETURN_TYPES = ("SIGMAS",)
|
| 346 |
-
CATEGORY = "sampling/custom_sampling/schedulers"
|
| 347 |
-
|
| 348 |
-
FUNCTION = "get_sigmas"
|
| 349 |
-
|
| 350 |
-
def get_sigmas(self, coeff, steps, denoise):
|
| 351 |
-
total_steps = steps
|
| 352 |
-
if denoise < 1.0:
|
| 353 |
-
if denoise <= 0.0:
|
| 354 |
-
return (torch.FloatTensor([]),)
|
| 355 |
-
total_steps = round(steps * denoise)
|
| 356 |
-
|
| 357 |
-
if steps <= 20:
|
| 358 |
-
sigmas = NOISE_LEVELS[round(coeff, 2)][steps-2][:]
|
| 359 |
-
else:
|
| 360 |
-
sigmas = NOISE_LEVELS[round(coeff, 2)][-1][:]
|
| 361 |
-
sigmas = loglinear_interp(sigmas, steps + 1)
|
| 362 |
-
|
| 363 |
-
sigmas = sigmas[-(total_steps + 1):]
|
| 364 |
-
sigmas[-1] = 0
|
| 365 |
-
return (torch.FloatTensor(sigmas), )
|
| 366 |
-
|
| 367 |
-
NODE_CLASS_MAPPINGS = {
|
| 368 |
-
"GITSScheduler": GITSScheduler,
|
| 369 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
comfy_extras/nodes_hidream.py
DELETED
|
@@ -1,55 +0,0 @@
|
|
| 1 |
-
import folder_paths
|
| 2 |
-
import comfy.sd
|
| 3 |
-
import comfy.model_management
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
class QuadrupleCLIPLoader:
|
| 7 |
-
@classmethod
|
| 8 |
-
def INPUT_TYPES(s):
|
| 9 |
-
return {"required": { "clip_name1": (folder_paths.get_filename_list("text_encoders"), ),
|
| 10 |
-
"clip_name2": (folder_paths.get_filename_list("text_encoders"), ),
|
| 11 |
-
"clip_name3": (folder_paths.get_filename_list("text_encoders"), ),
|
| 12 |
-
"clip_name4": (folder_paths.get_filename_list("text_encoders"), )
|
| 13 |
-
}}
|
| 14 |
-
RETURN_TYPES = ("CLIP",)
|
| 15 |
-
FUNCTION = "load_clip"
|
| 16 |
-
|
| 17 |
-
CATEGORY = "advanced/loaders"
|
| 18 |
-
|
| 19 |
-
DESCRIPTION = "[Recipes]\n\nhidream: long clip-l, long clip-g, t5xxl, llama_8b_3.1_instruct"
|
| 20 |
-
|
| 21 |
-
def load_clip(self, clip_name1, clip_name2, clip_name3, clip_name4):
|
| 22 |
-
clip_path1 = folder_paths.get_full_path_or_raise("text_encoders", clip_name1)
|
| 23 |
-
clip_path2 = folder_paths.get_full_path_or_raise("text_encoders", clip_name2)
|
| 24 |
-
clip_path3 = folder_paths.get_full_path_or_raise("text_encoders", clip_name3)
|
| 25 |
-
clip_path4 = folder_paths.get_full_path_or_raise("text_encoders", clip_name4)
|
| 26 |
-
clip = comfy.sd.load_clip(ckpt_paths=[clip_path1, clip_path2, clip_path3, clip_path4], embedding_directory=folder_paths.get_folder_paths("embeddings"))
|
| 27 |
-
return (clip,)
|
| 28 |
-
|
| 29 |
-
class CLIPTextEncodeHiDream:
|
| 30 |
-
@classmethod
|
| 31 |
-
def INPUT_TYPES(s):
|
| 32 |
-
return {"required": {
|
| 33 |
-
"clip": ("CLIP", ),
|
| 34 |
-
"clip_l": ("STRING", {"multiline": True, "dynamicPrompts": True}),
|
| 35 |
-
"clip_g": ("STRING", {"multiline": True, "dynamicPrompts": True}),
|
| 36 |
-
"t5xxl": ("STRING", {"multiline": True, "dynamicPrompts": True}),
|
| 37 |
-
"llama": ("STRING", {"multiline": True, "dynamicPrompts": True})
|
| 38 |
-
}}
|
| 39 |
-
RETURN_TYPES = ("CONDITIONING",)
|
| 40 |
-
FUNCTION = "encode"
|
| 41 |
-
|
| 42 |
-
CATEGORY = "advanced/conditioning"
|
| 43 |
-
|
| 44 |
-
def encode(self, clip, clip_l, clip_g, t5xxl, llama):
|
| 45 |
-
|
| 46 |
-
tokens = clip.tokenize(clip_g)
|
| 47 |
-
tokens["l"] = clip.tokenize(clip_l)["l"]
|
| 48 |
-
tokens["t5xxl"] = clip.tokenize(t5xxl)["t5xxl"]
|
| 49 |
-
tokens["llama"] = clip.tokenize(llama)["llama"]
|
| 50 |
-
return (clip.encode_from_tokens_scheduled(tokens), )
|
| 51 |
-
|
| 52 |
-
NODE_CLASS_MAPPINGS = {
|
| 53 |
-
"QuadrupleCLIPLoader": QuadrupleCLIPLoader,
|
| 54 |
-
"CLIPTextEncodeHiDream": CLIPTextEncodeHiDream,
|
| 55 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
comfy_extras/nodes_hooks.py
DELETED
|
@@ -1,745 +0,0 @@
|
|
| 1 |
-
from __future__ import annotations
|
| 2 |
-
from typing import TYPE_CHECKING, Union
|
| 3 |
-
import logging
|
| 4 |
-
import torch
|
| 5 |
-
from collections.abc import Iterable
|
| 6 |
-
|
| 7 |
-
if TYPE_CHECKING:
|
| 8 |
-
from comfy.sd import CLIP
|
| 9 |
-
|
| 10 |
-
import comfy.hooks
|
| 11 |
-
import comfy.sd
|
| 12 |
-
import comfy.utils
|
| 13 |
-
import folder_paths
|
| 14 |
-
|
| 15 |
-
###########################################
|
| 16 |
-
# Mask, Combine, and Hook Conditioning
|
| 17 |
-
#------------------------------------------
|
| 18 |
-
class PairConditioningSetProperties:
|
| 19 |
-
NodeId = 'PairConditioningSetProperties'
|
| 20 |
-
NodeName = 'Cond Pair Set Props'
|
| 21 |
-
@classmethod
|
| 22 |
-
def INPUT_TYPES(s):
|
| 23 |
-
return {
|
| 24 |
-
"required": {
|
| 25 |
-
"positive_NEW": ("CONDITIONING", ),
|
| 26 |
-
"negative_NEW": ("CONDITIONING", ),
|
| 27 |
-
"strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.01}),
|
| 28 |
-
"set_cond_area": (["default", "mask bounds"],),
|
| 29 |
-
},
|
| 30 |
-
"optional": {
|
| 31 |
-
"mask": ("MASK", ),
|
| 32 |
-
"hooks": ("HOOKS",),
|
| 33 |
-
"timesteps": ("TIMESTEPS_RANGE",),
|
| 34 |
-
}
|
| 35 |
-
}
|
| 36 |
-
|
| 37 |
-
EXPERIMENTAL = True
|
| 38 |
-
RETURN_TYPES = ("CONDITIONING", "CONDITIONING")
|
| 39 |
-
RETURN_NAMES = ("positive", "negative")
|
| 40 |
-
CATEGORY = "advanced/hooks/cond pair"
|
| 41 |
-
FUNCTION = "set_properties"
|
| 42 |
-
|
| 43 |
-
def set_properties(self, positive_NEW, negative_NEW,
|
| 44 |
-
strength: float, set_cond_area: str,
|
| 45 |
-
mask: torch.Tensor=None, hooks: comfy.hooks.HookGroup=None, timesteps: tuple=None):
|
| 46 |
-
final_positive, final_negative = comfy.hooks.set_conds_props(conds=[positive_NEW, negative_NEW],
|
| 47 |
-
strength=strength, set_cond_area=set_cond_area,
|
| 48 |
-
mask=mask, hooks=hooks, timesteps_range=timesteps)
|
| 49 |
-
return (final_positive, final_negative)
|
| 50 |
-
|
| 51 |
-
class PairConditioningSetPropertiesAndCombine:
|
| 52 |
-
NodeId = 'PairConditioningSetPropertiesAndCombine'
|
| 53 |
-
NodeName = 'Cond Pair Set Props Combine'
|
| 54 |
-
@classmethod
|
| 55 |
-
def INPUT_TYPES(s):
|
| 56 |
-
return {
|
| 57 |
-
"required": {
|
| 58 |
-
"positive": ("CONDITIONING", ),
|
| 59 |
-
"negative": ("CONDITIONING", ),
|
| 60 |
-
"positive_NEW": ("CONDITIONING", ),
|
| 61 |
-
"negative_NEW": ("CONDITIONING", ),
|
| 62 |
-
"strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.01}),
|
| 63 |
-
"set_cond_area": (["default", "mask bounds"],),
|
| 64 |
-
},
|
| 65 |
-
"optional": {
|
| 66 |
-
"mask": ("MASK", ),
|
| 67 |
-
"hooks": ("HOOKS",),
|
| 68 |
-
"timesteps": ("TIMESTEPS_RANGE",),
|
| 69 |
-
}
|
| 70 |
-
}
|
| 71 |
-
|
| 72 |
-
EXPERIMENTAL = True
|
| 73 |
-
RETURN_TYPES = ("CONDITIONING", "CONDITIONING")
|
| 74 |
-
RETURN_NAMES = ("positive", "negative")
|
| 75 |
-
CATEGORY = "advanced/hooks/cond pair"
|
| 76 |
-
FUNCTION = "set_properties"
|
| 77 |
-
|
| 78 |
-
def set_properties(self, positive, negative, positive_NEW, negative_NEW,
|
| 79 |
-
strength: float, set_cond_area: str,
|
| 80 |
-
mask: torch.Tensor=None, hooks: comfy.hooks.HookGroup=None, timesteps: tuple=None):
|
| 81 |
-
final_positive, final_negative = comfy.hooks.set_conds_props_and_combine(conds=[positive, negative], new_conds=[positive_NEW, negative_NEW],
|
| 82 |
-
strength=strength, set_cond_area=set_cond_area,
|
| 83 |
-
mask=mask, hooks=hooks, timesteps_range=timesteps)
|
| 84 |
-
return (final_positive, final_negative)
|
| 85 |
-
|
| 86 |
-
class ConditioningSetProperties:
|
| 87 |
-
NodeId = 'ConditioningSetProperties'
|
| 88 |
-
NodeName = 'Cond Set Props'
|
| 89 |
-
@classmethod
|
| 90 |
-
def INPUT_TYPES(s):
|
| 91 |
-
return {
|
| 92 |
-
"required": {
|
| 93 |
-
"cond_NEW": ("CONDITIONING", ),
|
| 94 |
-
"strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.01}),
|
| 95 |
-
"set_cond_area": (["default", "mask bounds"],),
|
| 96 |
-
},
|
| 97 |
-
"optional": {
|
| 98 |
-
"mask": ("MASK", ),
|
| 99 |
-
"hooks": ("HOOKS",),
|
| 100 |
-
"timesteps": ("TIMESTEPS_RANGE",),
|
| 101 |
-
}
|
| 102 |
-
}
|
| 103 |
-
|
| 104 |
-
EXPERIMENTAL = True
|
| 105 |
-
RETURN_TYPES = ("CONDITIONING",)
|
| 106 |
-
CATEGORY = "advanced/hooks/cond single"
|
| 107 |
-
FUNCTION = "set_properties"
|
| 108 |
-
|
| 109 |
-
def set_properties(self, cond_NEW,
|
| 110 |
-
strength: float, set_cond_area: str,
|
| 111 |
-
mask: torch.Tensor=None, hooks: comfy.hooks.HookGroup=None, timesteps: tuple=None):
|
| 112 |
-
(final_cond,) = comfy.hooks.set_conds_props(conds=[cond_NEW],
|
| 113 |
-
strength=strength, set_cond_area=set_cond_area,
|
| 114 |
-
mask=mask, hooks=hooks, timesteps_range=timesteps)
|
| 115 |
-
return (final_cond,)
|
| 116 |
-
|
| 117 |
-
class ConditioningSetPropertiesAndCombine:
|
| 118 |
-
NodeId = 'ConditioningSetPropertiesAndCombine'
|
| 119 |
-
NodeName = 'Cond Set Props Combine'
|
| 120 |
-
@classmethod
|
| 121 |
-
def INPUT_TYPES(s):
|
| 122 |
-
return {
|
| 123 |
-
"required": {
|
| 124 |
-
"cond": ("CONDITIONING", ),
|
| 125 |
-
"cond_NEW": ("CONDITIONING", ),
|
| 126 |
-
"strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.01}),
|
| 127 |
-
"set_cond_area": (["default", "mask bounds"],),
|
| 128 |
-
},
|
| 129 |
-
"optional": {
|
| 130 |
-
"mask": ("MASK", ),
|
| 131 |
-
"hooks": ("HOOKS",),
|
| 132 |
-
"timesteps": ("TIMESTEPS_RANGE",),
|
| 133 |
-
}
|
| 134 |
-
}
|
| 135 |
-
|
| 136 |
-
EXPERIMENTAL = True
|
| 137 |
-
RETURN_TYPES = ("CONDITIONING",)
|
| 138 |
-
CATEGORY = "advanced/hooks/cond single"
|
| 139 |
-
FUNCTION = "set_properties"
|
| 140 |
-
|
| 141 |
-
def set_properties(self, cond, cond_NEW,
|
| 142 |
-
strength: float, set_cond_area: str,
|
| 143 |
-
mask: torch.Tensor=None, hooks: comfy.hooks.HookGroup=None, timesteps: tuple=None):
|
| 144 |
-
(final_cond,) = comfy.hooks.set_conds_props_and_combine(conds=[cond], new_conds=[cond_NEW],
|
| 145 |
-
strength=strength, set_cond_area=set_cond_area,
|
| 146 |
-
mask=mask, hooks=hooks, timesteps_range=timesteps)
|
| 147 |
-
return (final_cond,)
|
| 148 |
-
|
| 149 |
-
class PairConditioningCombine:
|
| 150 |
-
NodeId = 'PairConditioningCombine'
|
| 151 |
-
NodeName = 'Cond Pair Combine'
|
| 152 |
-
@classmethod
|
| 153 |
-
def INPUT_TYPES(s):
|
| 154 |
-
return {
|
| 155 |
-
"required": {
|
| 156 |
-
"positive_A": ("CONDITIONING",),
|
| 157 |
-
"negative_A": ("CONDITIONING",),
|
| 158 |
-
"positive_B": ("CONDITIONING",),
|
| 159 |
-
"negative_B": ("CONDITIONING",),
|
| 160 |
-
},
|
| 161 |
-
}
|
| 162 |
-
|
| 163 |
-
EXPERIMENTAL = True
|
| 164 |
-
RETURN_TYPES = ("CONDITIONING", "CONDITIONING")
|
| 165 |
-
RETURN_NAMES = ("positive", "negative")
|
| 166 |
-
CATEGORY = "advanced/hooks/cond pair"
|
| 167 |
-
FUNCTION = "combine"
|
| 168 |
-
|
| 169 |
-
def combine(self, positive_A, negative_A, positive_B, negative_B):
|
| 170 |
-
final_positive, final_negative = comfy.hooks.set_conds_props_and_combine(conds=[positive_A, negative_A], new_conds=[positive_B, negative_B],)
|
| 171 |
-
return (final_positive, final_negative,)
|
| 172 |
-
|
| 173 |
-
class PairConditioningSetDefaultAndCombine:
|
| 174 |
-
NodeId = 'PairConditioningSetDefaultCombine'
|
| 175 |
-
NodeName = 'Cond Pair Set Default Combine'
|
| 176 |
-
@classmethod
|
| 177 |
-
def INPUT_TYPES(s):
|
| 178 |
-
return {
|
| 179 |
-
"required": {
|
| 180 |
-
"positive": ("CONDITIONING",),
|
| 181 |
-
"negative": ("CONDITIONING",),
|
| 182 |
-
"positive_DEFAULT": ("CONDITIONING",),
|
| 183 |
-
"negative_DEFAULT": ("CONDITIONING",),
|
| 184 |
-
},
|
| 185 |
-
"optional": {
|
| 186 |
-
"hooks": ("HOOKS",),
|
| 187 |
-
}
|
| 188 |
-
}
|
| 189 |
-
|
| 190 |
-
EXPERIMENTAL = True
|
| 191 |
-
RETURN_TYPES = ("CONDITIONING", "CONDITIONING")
|
| 192 |
-
RETURN_NAMES = ("positive", "negative")
|
| 193 |
-
CATEGORY = "advanced/hooks/cond pair"
|
| 194 |
-
FUNCTION = "set_default_and_combine"
|
| 195 |
-
|
| 196 |
-
def set_default_and_combine(self, positive, negative, positive_DEFAULT, negative_DEFAULT,
|
| 197 |
-
hooks: comfy.hooks.HookGroup=None):
|
| 198 |
-
final_positive, final_negative = comfy.hooks.set_default_conds_and_combine(conds=[positive, negative], new_conds=[positive_DEFAULT, negative_DEFAULT],
|
| 199 |
-
hooks=hooks)
|
| 200 |
-
return (final_positive, final_negative)
|
| 201 |
-
|
| 202 |
-
class ConditioningSetDefaultAndCombine:
|
| 203 |
-
NodeId = 'ConditioningSetDefaultCombine'
|
| 204 |
-
NodeName = 'Cond Set Default Combine'
|
| 205 |
-
@classmethod
|
| 206 |
-
def INPUT_TYPES(s):
|
| 207 |
-
return {
|
| 208 |
-
"required": {
|
| 209 |
-
"cond": ("CONDITIONING",),
|
| 210 |
-
"cond_DEFAULT": ("CONDITIONING",),
|
| 211 |
-
},
|
| 212 |
-
"optional": {
|
| 213 |
-
"hooks": ("HOOKS",),
|
| 214 |
-
}
|
| 215 |
-
}
|
| 216 |
-
|
| 217 |
-
EXPERIMENTAL = True
|
| 218 |
-
RETURN_TYPES = ("CONDITIONING",)
|
| 219 |
-
CATEGORY = "advanced/hooks/cond single"
|
| 220 |
-
FUNCTION = "set_default_and_combine"
|
| 221 |
-
|
| 222 |
-
def set_default_and_combine(self, cond, cond_DEFAULT,
|
| 223 |
-
hooks: comfy.hooks.HookGroup=None):
|
| 224 |
-
(final_conditioning,) = comfy.hooks.set_default_conds_and_combine(conds=[cond], new_conds=[cond_DEFAULT],
|
| 225 |
-
hooks=hooks)
|
| 226 |
-
return (final_conditioning,)
|
| 227 |
-
|
| 228 |
-
class SetClipHooks:
|
| 229 |
-
NodeId = 'SetClipHooks'
|
| 230 |
-
NodeName = 'Set CLIP Hooks'
|
| 231 |
-
@classmethod
|
| 232 |
-
def INPUT_TYPES(s):
|
| 233 |
-
return {
|
| 234 |
-
"required": {
|
| 235 |
-
"clip": ("CLIP",),
|
| 236 |
-
"apply_to_conds": ("BOOLEAN", {"default": True}),
|
| 237 |
-
"schedule_clip": ("BOOLEAN", {"default": False})
|
| 238 |
-
},
|
| 239 |
-
"optional": {
|
| 240 |
-
"hooks": ("HOOKS",)
|
| 241 |
-
}
|
| 242 |
-
}
|
| 243 |
-
|
| 244 |
-
EXPERIMENTAL = True
|
| 245 |
-
RETURN_TYPES = ("CLIP",)
|
| 246 |
-
CATEGORY = "advanced/hooks/clip"
|
| 247 |
-
FUNCTION = "apply_hooks"
|
| 248 |
-
|
| 249 |
-
def apply_hooks(self, clip: CLIP, schedule_clip: bool, apply_to_conds: bool, hooks: comfy.hooks.HookGroup=None):
|
| 250 |
-
if hooks is not None:
|
| 251 |
-
clip = clip.clone()
|
| 252 |
-
if apply_to_conds:
|
| 253 |
-
clip.apply_hooks_to_conds = hooks
|
| 254 |
-
clip.patcher.forced_hooks = hooks.clone()
|
| 255 |
-
clip.use_clip_schedule = schedule_clip
|
| 256 |
-
if not clip.use_clip_schedule:
|
| 257 |
-
clip.patcher.forced_hooks.set_keyframes_on_hooks(None)
|
| 258 |
-
clip.patcher.register_all_hook_patches(hooks, comfy.hooks.create_target_dict(comfy.hooks.EnumWeightTarget.Clip))
|
| 259 |
-
return (clip,)
|
| 260 |
-
|
| 261 |
-
class ConditioningTimestepsRange:
|
| 262 |
-
NodeId = 'ConditioningTimestepsRange'
|
| 263 |
-
NodeName = 'Timesteps Range'
|
| 264 |
-
@classmethod
|
| 265 |
-
def INPUT_TYPES(s):
|
| 266 |
-
return {
|
| 267 |
-
"required": {
|
| 268 |
-
"start_percent": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.001}),
|
| 269 |
-
"end_percent": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.001})
|
| 270 |
-
},
|
| 271 |
-
}
|
| 272 |
-
|
| 273 |
-
EXPERIMENTAL = True
|
| 274 |
-
RETURN_TYPES = ("TIMESTEPS_RANGE", "TIMESTEPS_RANGE", "TIMESTEPS_RANGE")
|
| 275 |
-
RETURN_NAMES = ("TIMESTEPS_RANGE", "BEFORE_RANGE", "AFTER_RANGE")
|
| 276 |
-
CATEGORY = "advanced/hooks"
|
| 277 |
-
FUNCTION = "create_range"
|
| 278 |
-
|
| 279 |
-
def create_range(self, start_percent: float, end_percent: float):
|
| 280 |
-
return ((start_percent, end_percent), (0.0, start_percent), (end_percent, 1.0))
|
| 281 |
-
#------------------------------------------
|
| 282 |
-
###########################################
|
| 283 |
-
|
| 284 |
-
|
| 285 |
-
###########################################
|
| 286 |
-
# Create Hooks
|
| 287 |
-
#------------------------------------------
|
| 288 |
-
class CreateHookLora:
|
| 289 |
-
NodeId = 'CreateHookLora'
|
| 290 |
-
NodeName = 'Create Hook LoRA'
|
| 291 |
-
def __init__(self):
|
| 292 |
-
self.loaded_lora = None
|
| 293 |
-
|
| 294 |
-
@classmethod
|
| 295 |
-
def INPUT_TYPES(s):
|
| 296 |
-
return {
|
| 297 |
-
"required": {
|
| 298 |
-
"lora_name": (folder_paths.get_filename_list("loras"), ),
|
| 299 |
-
"strength_model": ("FLOAT", {"default": 1.0, "min": -20.0, "max": 20.0, "step": 0.01}),
|
| 300 |
-
"strength_clip": ("FLOAT", {"default": 1.0, "min": -20.0, "max": 20.0, "step": 0.01}),
|
| 301 |
-
},
|
| 302 |
-
"optional": {
|
| 303 |
-
"prev_hooks": ("HOOKS",)
|
| 304 |
-
}
|
| 305 |
-
}
|
| 306 |
-
|
| 307 |
-
EXPERIMENTAL = True
|
| 308 |
-
RETURN_TYPES = ("HOOKS",)
|
| 309 |
-
CATEGORY = "advanced/hooks/create"
|
| 310 |
-
FUNCTION = "create_hook"
|
| 311 |
-
|
| 312 |
-
def create_hook(self, lora_name: str, strength_model: float, strength_clip: float, prev_hooks: comfy.hooks.HookGroup=None):
|
| 313 |
-
if prev_hooks is None:
|
| 314 |
-
prev_hooks = comfy.hooks.HookGroup()
|
| 315 |
-
prev_hooks.clone()
|
| 316 |
-
|
| 317 |
-
if strength_model == 0 and strength_clip == 0:
|
| 318 |
-
return (prev_hooks,)
|
| 319 |
-
|
| 320 |
-
lora_path = folder_paths.get_full_path("loras", lora_name)
|
| 321 |
-
lora = None
|
| 322 |
-
if self.loaded_lora is not None:
|
| 323 |
-
if self.loaded_lora[0] == lora_path:
|
| 324 |
-
lora = self.loaded_lora[1]
|
| 325 |
-
else:
|
| 326 |
-
temp = self.loaded_lora
|
| 327 |
-
self.loaded_lora = None
|
| 328 |
-
del temp
|
| 329 |
-
|
| 330 |
-
if lora is None:
|
| 331 |
-
lora = comfy.utils.load_torch_file(lora_path, safe_load=True)
|
| 332 |
-
self.loaded_lora = (lora_path, lora)
|
| 333 |
-
|
| 334 |
-
hooks = comfy.hooks.create_hook_lora(lora=lora, strength_model=strength_model, strength_clip=strength_clip)
|
| 335 |
-
return (prev_hooks.clone_and_combine(hooks),)
|
| 336 |
-
|
| 337 |
-
class CreateHookLoraModelOnly(CreateHookLora):
|
| 338 |
-
NodeId = 'CreateHookLoraModelOnly'
|
| 339 |
-
NodeName = 'Create Hook LoRA (MO)'
|
| 340 |
-
@classmethod
|
| 341 |
-
def INPUT_TYPES(s):
|
| 342 |
-
return {
|
| 343 |
-
"required": {
|
| 344 |
-
"lora_name": (folder_paths.get_filename_list("loras"), ),
|
| 345 |
-
"strength_model": ("FLOAT", {"default": 1.0, "min": -20.0, "max": 20.0, "step": 0.01}),
|
| 346 |
-
},
|
| 347 |
-
"optional": {
|
| 348 |
-
"prev_hooks": ("HOOKS",)
|
| 349 |
-
}
|
| 350 |
-
}
|
| 351 |
-
|
| 352 |
-
EXPERIMENTAL = True
|
| 353 |
-
RETURN_TYPES = ("HOOKS",)
|
| 354 |
-
CATEGORY = "advanced/hooks/create"
|
| 355 |
-
FUNCTION = "create_hook_model_only"
|
| 356 |
-
|
| 357 |
-
def create_hook_model_only(self, lora_name: str, strength_model: float, prev_hooks: comfy.hooks.HookGroup=None):
|
| 358 |
-
return self.create_hook(lora_name=lora_name, strength_model=strength_model, strength_clip=0, prev_hooks=prev_hooks)
|
| 359 |
-
|
| 360 |
-
class CreateHookModelAsLora:
|
| 361 |
-
NodeId = 'CreateHookModelAsLora'
|
| 362 |
-
NodeName = 'Create Hook Model as LoRA'
|
| 363 |
-
|
| 364 |
-
def __init__(self):
|
| 365 |
-
# when not None, will be in following format:
|
| 366 |
-
# (ckpt_path: str, weights_model: dict, weights_clip: dict)
|
| 367 |
-
self.loaded_weights = None
|
| 368 |
-
|
| 369 |
-
@classmethod
|
| 370 |
-
def INPUT_TYPES(s):
|
| 371 |
-
return {
|
| 372 |
-
"required": {
|
| 373 |
-
"ckpt_name": (folder_paths.get_filename_list("checkpoints"), ),
|
| 374 |
-
"strength_model": ("FLOAT", {"default": 1.0, "min": -20.0, "max": 20.0, "step": 0.01}),
|
| 375 |
-
"strength_clip": ("FLOAT", {"default": 1.0, "min": -20.0, "max": 20.0, "step": 0.01}),
|
| 376 |
-
},
|
| 377 |
-
"optional": {
|
| 378 |
-
"prev_hooks": ("HOOKS",)
|
| 379 |
-
}
|
| 380 |
-
}
|
| 381 |
-
|
| 382 |
-
EXPERIMENTAL = True
|
| 383 |
-
RETURN_TYPES = ("HOOKS",)
|
| 384 |
-
CATEGORY = "advanced/hooks/create"
|
| 385 |
-
FUNCTION = "create_hook"
|
| 386 |
-
|
| 387 |
-
def create_hook(self, ckpt_name: str, strength_model: float, strength_clip: float,
|
| 388 |
-
prev_hooks: comfy.hooks.HookGroup=None):
|
| 389 |
-
if prev_hooks is None:
|
| 390 |
-
prev_hooks = comfy.hooks.HookGroup()
|
| 391 |
-
prev_hooks.clone()
|
| 392 |
-
|
| 393 |
-
ckpt_path = folder_paths.get_full_path("checkpoints", ckpt_name)
|
| 394 |
-
weights_model = None
|
| 395 |
-
weights_clip = None
|
| 396 |
-
if self.loaded_weights is not None:
|
| 397 |
-
if self.loaded_weights[0] == ckpt_path:
|
| 398 |
-
weights_model = self.loaded_weights[1]
|
| 399 |
-
weights_clip = self.loaded_weights[2]
|
| 400 |
-
else:
|
| 401 |
-
temp = self.loaded_weights
|
| 402 |
-
self.loaded_weights = None
|
| 403 |
-
del temp
|
| 404 |
-
|
| 405 |
-
if weights_model is None:
|
| 406 |
-
out = comfy.sd.load_checkpoint_guess_config(ckpt_path, output_vae=True, output_clip=True, embedding_directory=folder_paths.get_folder_paths("embeddings"))
|
| 407 |
-
weights_model = comfy.hooks.get_patch_weights_from_model(out[0])
|
| 408 |
-
weights_clip = comfy.hooks.get_patch_weights_from_model(out[1].patcher if out[1] else out[1])
|
| 409 |
-
self.loaded_weights = (ckpt_path, weights_model, weights_clip)
|
| 410 |
-
|
| 411 |
-
hooks = comfy.hooks.create_hook_model_as_lora(weights_model=weights_model, weights_clip=weights_clip,
|
| 412 |
-
strength_model=strength_model, strength_clip=strength_clip)
|
| 413 |
-
return (prev_hooks.clone_and_combine(hooks),)
|
| 414 |
-
|
| 415 |
-
class CreateHookModelAsLoraModelOnly(CreateHookModelAsLora):
|
| 416 |
-
NodeId = 'CreateHookModelAsLoraModelOnly'
|
| 417 |
-
NodeName = 'Create Hook Model as LoRA (MO)'
|
| 418 |
-
@classmethod
|
| 419 |
-
def INPUT_TYPES(s):
|
| 420 |
-
return {
|
| 421 |
-
"required": {
|
| 422 |
-
"ckpt_name": (folder_paths.get_filename_list("checkpoints"), ),
|
| 423 |
-
"strength_model": ("FLOAT", {"default": 1.0, "min": -20.0, "max": 20.0, "step": 0.01}),
|
| 424 |
-
},
|
| 425 |
-
"optional": {
|
| 426 |
-
"prev_hooks": ("HOOKS",)
|
| 427 |
-
}
|
| 428 |
-
}
|
| 429 |
-
|
| 430 |
-
EXPERIMENTAL = True
|
| 431 |
-
RETURN_TYPES = ("HOOKS",)
|
| 432 |
-
CATEGORY = "advanced/hooks/create"
|
| 433 |
-
FUNCTION = "create_hook_model_only"
|
| 434 |
-
|
| 435 |
-
def create_hook_model_only(self, ckpt_name: str, strength_model: float,
|
| 436 |
-
prev_hooks: comfy.hooks.HookGroup=None):
|
| 437 |
-
return self.create_hook(ckpt_name=ckpt_name, strength_model=strength_model, strength_clip=0.0, prev_hooks=prev_hooks)
|
| 438 |
-
#------------------------------------------
|
| 439 |
-
###########################################
|
| 440 |
-
|
| 441 |
-
|
| 442 |
-
###########################################
|
| 443 |
-
# Schedule Hooks
|
| 444 |
-
#------------------------------------------
|
| 445 |
-
class SetHookKeyframes:
|
| 446 |
-
NodeId = 'SetHookKeyframes'
|
| 447 |
-
NodeName = 'Set Hook Keyframes'
|
| 448 |
-
@classmethod
|
| 449 |
-
def INPUT_TYPES(s):
|
| 450 |
-
return {
|
| 451 |
-
"required": {
|
| 452 |
-
"hooks": ("HOOKS",),
|
| 453 |
-
},
|
| 454 |
-
"optional": {
|
| 455 |
-
"hook_kf": ("HOOK_KEYFRAMES",),
|
| 456 |
-
}
|
| 457 |
-
}
|
| 458 |
-
|
| 459 |
-
EXPERIMENTAL = True
|
| 460 |
-
RETURN_TYPES = ("HOOKS",)
|
| 461 |
-
CATEGORY = "advanced/hooks/scheduling"
|
| 462 |
-
FUNCTION = "set_hook_keyframes"
|
| 463 |
-
|
| 464 |
-
def set_hook_keyframes(self, hooks: comfy.hooks.HookGroup, hook_kf: comfy.hooks.HookKeyframeGroup=None):
|
| 465 |
-
if hook_kf is not None:
|
| 466 |
-
hooks = hooks.clone()
|
| 467 |
-
hooks.set_keyframes_on_hooks(hook_kf=hook_kf)
|
| 468 |
-
return (hooks,)
|
| 469 |
-
|
| 470 |
-
class CreateHookKeyframe:
|
| 471 |
-
NodeId = 'CreateHookKeyframe'
|
| 472 |
-
NodeName = 'Create Hook Keyframe'
|
| 473 |
-
@classmethod
|
| 474 |
-
def INPUT_TYPES(s):
|
| 475 |
-
return {
|
| 476 |
-
"required": {
|
| 477 |
-
"strength_mult": ("FLOAT", {"default": 1.0, "min": -20.0, "max": 20.0, "step": 0.01}),
|
| 478 |
-
"start_percent": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.001}),
|
| 479 |
-
},
|
| 480 |
-
"optional": {
|
| 481 |
-
"prev_hook_kf": ("HOOK_KEYFRAMES",),
|
| 482 |
-
}
|
| 483 |
-
}
|
| 484 |
-
|
| 485 |
-
EXPERIMENTAL = True
|
| 486 |
-
RETURN_TYPES = ("HOOK_KEYFRAMES",)
|
| 487 |
-
RETURN_NAMES = ("HOOK_KF",)
|
| 488 |
-
CATEGORY = "advanced/hooks/scheduling"
|
| 489 |
-
FUNCTION = "create_hook_keyframe"
|
| 490 |
-
|
| 491 |
-
def create_hook_keyframe(self, strength_mult: float, start_percent: float, prev_hook_kf: comfy.hooks.HookKeyframeGroup=None):
|
| 492 |
-
if prev_hook_kf is None:
|
| 493 |
-
prev_hook_kf = comfy.hooks.HookKeyframeGroup()
|
| 494 |
-
prev_hook_kf = prev_hook_kf.clone()
|
| 495 |
-
keyframe = comfy.hooks.HookKeyframe(strength=strength_mult, start_percent=start_percent)
|
| 496 |
-
prev_hook_kf.add(keyframe)
|
| 497 |
-
return (prev_hook_kf,)
|
| 498 |
-
|
| 499 |
-
class CreateHookKeyframesInterpolated:
|
| 500 |
-
NodeId = 'CreateHookKeyframesInterpolated'
|
| 501 |
-
NodeName = 'Create Hook Keyframes Interp.'
|
| 502 |
-
@classmethod
|
| 503 |
-
def INPUT_TYPES(s):
|
| 504 |
-
return {
|
| 505 |
-
"required": {
|
| 506 |
-
"strength_start": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ),
|
| 507 |
-
"strength_end": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ),
|
| 508 |
-
"interpolation": (comfy.hooks.InterpolationMethod._LIST, ),
|
| 509 |
-
"start_percent": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.001}),
|
| 510 |
-
"end_percent": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.001}),
|
| 511 |
-
"keyframes_count": ("INT", {"default": 5, "min": 2, "max": 100, "step": 1}),
|
| 512 |
-
"print_keyframes": ("BOOLEAN", {"default": False}),
|
| 513 |
-
},
|
| 514 |
-
"optional": {
|
| 515 |
-
"prev_hook_kf": ("HOOK_KEYFRAMES",),
|
| 516 |
-
},
|
| 517 |
-
}
|
| 518 |
-
|
| 519 |
-
EXPERIMENTAL = True
|
| 520 |
-
RETURN_TYPES = ("HOOK_KEYFRAMES",)
|
| 521 |
-
RETURN_NAMES = ("HOOK_KF",)
|
| 522 |
-
CATEGORY = "advanced/hooks/scheduling"
|
| 523 |
-
FUNCTION = "create_hook_keyframes"
|
| 524 |
-
|
| 525 |
-
def create_hook_keyframes(self, strength_start: float, strength_end: float, interpolation: str,
|
| 526 |
-
start_percent: float, end_percent: float, keyframes_count: int,
|
| 527 |
-
print_keyframes=False, prev_hook_kf: comfy.hooks.HookKeyframeGroup=None):
|
| 528 |
-
if prev_hook_kf is None:
|
| 529 |
-
prev_hook_kf = comfy.hooks.HookKeyframeGroup()
|
| 530 |
-
prev_hook_kf = prev_hook_kf.clone()
|
| 531 |
-
percents = comfy.hooks.InterpolationMethod.get_weights(num_from=start_percent, num_to=end_percent, length=keyframes_count,
|
| 532 |
-
method=comfy.hooks.InterpolationMethod.LINEAR)
|
| 533 |
-
strengths = comfy.hooks.InterpolationMethod.get_weights(num_from=strength_start, num_to=strength_end, length=keyframes_count, method=interpolation)
|
| 534 |
-
|
| 535 |
-
is_first = True
|
| 536 |
-
for percent, strength in zip(percents, strengths):
|
| 537 |
-
guarantee_steps = 0
|
| 538 |
-
if is_first:
|
| 539 |
-
guarantee_steps = 1
|
| 540 |
-
is_first = False
|
| 541 |
-
prev_hook_kf.add(comfy.hooks.HookKeyframe(strength=strength, start_percent=percent, guarantee_steps=guarantee_steps))
|
| 542 |
-
if print_keyframes:
|
| 543 |
-
logging.info(f"Hook Keyframe - start_percent:{percent} = {strength}")
|
| 544 |
-
return (prev_hook_kf,)
|
| 545 |
-
|
| 546 |
-
class CreateHookKeyframesFromFloats:
|
| 547 |
-
NodeId = 'CreateHookKeyframesFromFloats'
|
| 548 |
-
NodeName = 'Create Hook Keyframes From Floats'
|
| 549 |
-
@classmethod
|
| 550 |
-
def INPUT_TYPES(s):
|
| 551 |
-
return {
|
| 552 |
-
"required": {
|
| 553 |
-
"floats_strength": ("FLOATS", {"default": -1, "min": -1, "step": 0.001, "forceInput": True}),
|
| 554 |
-
"start_percent": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.001}),
|
| 555 |
-
"end_percent": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.001}),
|
| 556 |
-
"print_keyframes": ("BOOLEAN", {"default": False}),
|
| 557 |
-
},
|
| 558 |
-
"optional": {
|
| 559 |
-
"prev_hook_kf": ("HOOK_KEYFRAMES",),
|
| 560 |
-
}
|
| 561 |
-
}
|
| 562 |
-
|
| 563 |
-
EXPERIMENTAL = True
|
| 564 |
-
RETURN_TYPES = ("HOOK_KEYFRAMES",)
|
| 565 |
-
RETURN_NAMES = ("HOOK_KF",)
|
| 566 |
-
CATEGORY = "advanced/hooks/scheduling"
|
| 567 |
-
FUNCTION = "create_hook_keyframes"
|
| 568 |
-
|
| 569 |
-
def create_hook_keyframes(self, floats_strength: Union[float, list[float]],
|
| 570 |
-
start_percent: float, end_percent: float,
|
| 571 |
-
prev_hook_kf: comfy.hooks.HookKeyframeGroup=None, print_keyframes=False):
|
| 572 |
-
if prev_hook_kf is None:
|
| 573 |
-
prev_hook_kf = comfy.hooks.HookKeyframeGroup()
|
| 574 |
-
prev_hook_kf = prev_hook_kf.clone()
|
| 575 |
-
if type(floats_strength) in (float, int):
|
| 576 |
-
floats_strength = [float(floats_strength)]
|
| 577 |
-
elif isinstance(floats_strength, Iterable):
|
| 578 |
-
pass
|
| 579 |
-
else:
|
| 580 |
-
raise Exception(f"floats_strength must be either an iterable input or a float, but was{type(floats_strength).__repr__}.")
|
| 581 |
-
percents = comfy.hooks.InterpolationMethod.get_weights(num_from=start_percent, num_to=end_percent, length=len(floats_strength),
|
| 582 |
-
method=comfy.hooks.InterpolationMethod.LINEAR)
|
| 583 |
-
|
| 584 |
-
is_first = True
|
| 585 |
-
for percent, strength in zip(percents, floats_strength):
|
| 586 |
-
guarantee_steps = 0
|
| 587 |
-
if is_first:
|
| 588 |
-
guarantee_steps = 1
|
| 589 |
-
is_first = False
|
| 590 |
-
prev_hook_kf.add(comfy.hooks.HookKeyframe(strength=strength, start_percent=percent, guarantee_steps=guarantee_steps))
|
| 591 |
-
if print_keyframes:
|
| 592 |
-
logging.info(f"Hook Keyframe - start_percent:{percent} = {strength}")
|
| 593 |
-
return (prev_hook_kf,)
|
| 594 |
-
#------------------------------------------
|
| 595 |
-
###########################################
|
| 596 |
-
|
| 597 |
-
|
| 598 |
-
class SetModelHooksOnCond:
|
| 599 |
-
@classmethod
|
| 600 |
-
def INPUT_TYPES(s):
|
| 601 |
-
return {
|
| 602 |
-
"required": {
|
| 603 |
-
"conditioning": ("CONDITIONING",),
|
| 604 |
-
"hooks": ("HOOKS",),
|
| 605 |
-
},
|
| 606 |
-
}
|
| 607 |
-
|
| 608 |
-
EXPERIMENTAL = True
|
| 609 |
-
RETURN_TYPES = ("CONDITIONING",)
|
| 610 |
-
CATEGORY = "advanced/hooks/manual"
|
| 611 |
-
FUNCTION = "attach_hook"
|
| 612 |
-
|
| 613 |
-
def attach_hook(self, conditioning, hooks: comfy.hooks.HookGroup):
|
| 614 |
-
return (comfy.hooks.set_hooks_for_conditioning(conditioning, hooks),)
|
| 615 |
-
|
| 616 |
-
|
| 617 |
-
###########################################
|
| 618 |
-
# Combine Hooks
|
| 619 |
-
#------------------------------------------
|
| 620 |
-
class CombineHooks:
|
| 621 |
-
NodeId = 'CombineHooks2'
|
| 622 |
-
NodeName = 'Combine Hooks [2]'
|
| 623 |
-
@classmethod
|
| 624 |
-
def INPUT_TYPES(s):
|
| 625 |
-
return {
|
| 626 |
-
"required": {
|
| 627 |
-
},
|
| 628 |
-
"optional": {
|
| 629 |
-
"hooks_A": ("HOOKS",),
|
| 630 |
-
"hooks_B": ("HOOKS",),
|
| 631 |
-
}
|
| 632 |
-
}
|
| 633 |
-
|
| 634 |
-
EXPERIMENTAL = True
|
| 635 |
-
RETURN_TYPES = ("HOOKS",)
|
| 636 |
-
CATEGORY = "advanced/hooks/combine"
|
| 637 |
-
FUNCTION = "combine_hooks"
|
| 638 |
-
|
| 639 |
-
def combine_hooks(self,
|
| 640 |
-
hooks_A: comfy.hooks.HookGroup=None,
|
| 641 |
-
hooks_B: comfy.hooks.HookGroup=None):
|
| 642 |
-
candidates = [hooks_A, hooks_B]
|
| 643 |
-
return (comfy.hooks.HookGroup.combine_all_hooks(candidates),)
|
| 644 |
-
|
| 645 |
-
class CombineHooksFour:
|
| 646 |
-
NodeId = 'CombineHooks4'
|
| 647 |
-
NodeName = 'Combine Hooks [4]'
|
| 648 |
-
@classmethod
|
| 649 |
-
def INPUT_TYPES(s):
|
| 650 |
-
return {
|
| 651 |
-
"required": {
|
| 652 |
-
},
|
| 653 |
-
"optional": {
|
| 654 |
-
"hooks_A": ("HOOKS",),
|
| 655 |
-
"hooks_B": ("HOOKS",),
|
| 656 |
-
"hooks_C": ("HOOKS",),
|
| 657 |
-
"hooks_D": ("HOOKS",),
|
| 658 |
-
}
|
| 659 |
-
}
|
| 660 |
-
|
| 661 |
-
EXPERIMENTAL = True
|
| 662 |
-
RETURN_TYPES = ("HOOKS",)
|
| 663 |
-
CATEGORY = "advanced/hooks/combine"
|
| 664 |
-
FUNCTION = "combine_hooks"
|
| 665 |
-
|
| 666 |
-
def combine_hooks(self,
|
| 667 |
-
hooks_A: comfy.hooks.HookGroup=None,
|
| 668 |
-
hooks_B: comfy.hooks.HookGroup=None,
|
| 669 |
-
hooks_C: comfy.hooks.HookGroup=None,
|
| 670 |
-
hooks_D: comfy.hooks.HookGroup=None):
|
| 671 |
-
candidates = [hooks_A, hooks_B, hooks_C, hooks_D]
|
| 672 |
-
return (comfy.hooks.HookGroup.combine_all_hooks(candidates),)
|
| 673 |
-
|
| 674 |
-
class CombineHooksEight:
|
| 675 |
-
NodeId = 'CombineHooks8'
|
| 676 |
-
NodeName = 'Combine Hooks [8]'
|
| 677 |
-
@classmethod
|
| 678 |
-
def INPUT_TYPES(s):
|
| 679 |
-
return {
|
| 680 |
-
"required": {
|
| 681 |
-
},
|
| 682 |
-
"optional": {
|
| 683 |
-
"hooks_A": ("HOOKS",),
|
| 684 |
-
"hooks_B": ("HOOKS",),
|
| 685 |
-
"hooks_C": ("HOOKS",),
|
| 686 |
-
"hooks_D": ("HOOKS",),
|
| 687 |
-
"hooks_E": ("HOOKS",),
|
| 688 |
-
"hooks_F": ("HOOKS",),
|
| 689 |
-
"hooks_G": ("HOOKS",),
|
| 690 |
-
"hooks_H": ("HOOKS",),
|
| 691 |
-
}
|
| 692 |
-
}
|
| 693 |
-
|
| 694 |
-
EXPERIMENTAL = True
|
| 695 |
-
RETURN_TYPES = ("HOOKS",)
|
| 696 |
-
CATEGORY = "advanced/hooks/combine"
|
| 697 |
-
FUNCTION = "combine_hooks"
|
| 698 |
-
|
| 699 |
-
def combine_hooks(self,
|
| 700 |
-
hooks_A: comfy.hooks.HookGroup=None,
|
| 701 |
-
hooks_B: comfy.hooks.HookGroup=None,
|
| 702 |
-
hooks_C: comfy.hooks.HookGroup=None,
|
| 703 |
-
hooks_D: comfy.hooks.HookGroup=None,
|
| 704 |
-
hooks_E: comfy.hooks.HookGroup=None,
|
| 705 |
-
hooks_F: comfy.hooks.HookGroup=None,
|
| 706 |
-
hooks_G: comfy.hooks.HookGroup=None,
|
| 707 |
-
hooks_H: comfy.hooks.HookGroup=None):
|
| 708 |
-
candidates = [hooks_A, hooks_B, hooks_C, hooks_D, hooks_E, hooks_F, hooks_G, hooks_H]
|
| 709 |
-
return (comfy.hooks.HookGroup.combine_all_hooks(candidates),)
|
| 710 |
-
#------------------------------------------
|
| 711 |
-
###########################################
|
| 712 |
-
|
| 713 |
-
node_list = [
|
| 714 |
-
# Create
|
| 715 |
-
CreateHookLora,
|
| 716 |
-
CreateHookLoraModelOnly,
|
| 717 |
-
CreateHookModelAsLora,
|
| 718 |
-
CreateHookModelAsLoraModelOnly,
|
| 719 |
-
# Scheduling
|
| 720 |
-
SetHookKeyframes,
|
| 721 |
-
CreateHookKeyframe,
|
| 722 |
-
CreateHookKeyframesInterpolated,
|
| 723 |
-
CreateHookKeyframesFromFloats,
|
| 724 |
-
# Combine
|
| 725 |
-
CombineHooks,
|
| 726 |
-
CombineHooksFour,
|
| 727 |
-
CombineHooksEight,
|
| 728 |
-
# Attach
|
| 729 |
-
ConditioningSetProperties,
|
| 730 |
-
ConditioningSetPropertiesAndCombine,
|
| 731 |
-
PairConditioningSetProperties,
|
| 732 |
-
PairConditioningSetPropertiesAndCombine,
|
| 733 |
-
ConditioningSetDefaultAndCombine,
|
| 734 |
-
PairConditioningSetDefaultAndCombine,
|
| 735 |
-
PairConditioningCombine,
|
| 736 |
-
SetClipHooks,
|
| 737 |
-
# Other
|
| 738 |
-
ConditioningTimestepsRange,
|
| 739 |
-
]
|
| 740 |
-
NODE_CLASS_MAPPINGS = {}
|
| 741 |
-
NODE_DISPLAY_NAME_MAPPINGS = {}
|
| 742 |
-
|
| 743 |
-
for node in node_list:
|
| 744 |
-
NODE_CLASS_MAPPINGS[node.NodeId] = node
|
| 745 |
-
NODE_DISPLAY_NAME_MAPPINGS[node.NodeId] = node.NodeName
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
comfy_extras/nodes_hunyuan.py
DELETED
|
@@ -1,123 +0,0 @@
|
|
| 1 |
-
import nodes
|
| 2 |
-
import node_helpers
|
| 3 |
-
import torch
|
| 4 |
-
import comfy.model_management
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
class CLIPTextEncodeHunyuanDiT:
|
| 8 |
-
@classmethod
|
| 9 |
-
def INPUT_TYPES(s):
|
| 10 |
-
return {"required": {
|
| 11 |
-
"clip": ("CLIP", ),
|
| 12 |
-
"bert": ("STRING", {"multiline": True, "dynamicPrompts": True}),
|
| 13 |
-
"mt5xl": ("STRING", {"multiline": True, "dynamicPrompts": True}),
|
| 14 |
-
}}
|
| 15 |
-
RETURN_TYPES = ("CONDITIONING",)
|
| 16 |
-
FUNCTION = "encode"
|
| 17 |
-
|
| 18 |
-
CATEGORY = "advanced/conditioning"
|
| 19 |
-
|
| 20 |
-
def encode(self, clip, bert, mt5xl):
|
| 21 |
-
tokens = clip.tokenize(bert)
|
| 22 |
-
tokens["mt5xl"] = clip.tokenize(mt5xl)["mt5xl"]
|
| 23 |
-
|
| 24 |
-
return (clip.encode_from_tokens_scheduled(tokens), )
|
| 25 |
-
|
| 26 |
-
class EmptyHunyuanLatentVideo:
|
| 27 |
-
@classmethod
|
| 28 |
-
def INPUT_TYPES(s):
|
| 29 |
-
return {"required": { "width": ("INT", {"default": 848, "min": 16, "max": nodes.MAX_RESOLUTION, "step": 16}),
|
| 30 |
-
"height": ("INT", {"default": 480, "min": 16, "max": nodes.MAX_RESOLUTION, "step": 16}),
|
| 31 |
-
"length": ("INT", {"default": 25, "min": 1, "max": nodes.MAX_RESOLUTION, "step": 4}),
|
| 32 |
-
"batch_size": ("INT", {"default": 1, "min": 1, "max": 4096})}}
|
| 33 |
-
RETURN_TYPES = ("LATENT",)
|
| 34 |
-
FUNCTION = "generate"
|
| 35 |
-
|
| 36 |
-
CATEGORY = "latent/video"
|
| 37 |
-
|
| 38 |
-
def generate(self, width, height, length, batch_size=1):
|
| 39 |
-
latent = torch.zeros([batch_size, 16, ((length - 1) // 4) + 1, height // 8, width // 8], device=comfy.model_management.intermediate_device())
|
| 40 |
-
return ({"samples":latent}, )
|
| 41 |
-
|
| 42 |
-
PROMPT_TEMPLATE_ENCODE_VIDEO_I2V = (
|
| 43 |
-
"<|start_header_id|>system<|end_header_id|>\n\n<image>\nDescribe the video by detailing the following aspects according to the reference image: "
|
| 44 |
-
"1. The main content and theme of the video."
|
| 45 |
-
"2. The color, shape, size, texture, quantity, text, and spatial relationships of the objects."
|
| 46 |
-
"3. Actions, events, behaviors temporal relationships, physical movement changes of the objects."
|
| 47 |
-
"4. background environment, light, style and atmosphere."
|
| 48 |
-
"5. camera angles, movements, and transitions used in the video:<|eot_id|>\n\n"
|
| 49 |
-
"<|start_header_id|>user<|end_header_id|>\n\n{}<|eot_id|>"
|
| 50 |
-
"<|start_header_id|>assistant<|end_header_id|>\n\n"
|
| 51 |
-
)
|
| 52 |
-
|
| 53 |
-
class TextEncodeHunyuanVideo_ImageToVideo:
|
| 54 |
-
@classmethod
|
| 55 |
-
def INPUT_TYPES(s):
|
| 56 |
-
return {"required": {
|
| 57 |
-
"clip": ("CLIP", ),
|
| 58 |
-
"clip_vision_output": ("CLIP_VISION_OUTPUT", ),
|
| 59 |
-
"prompt": ("STRING", {"multiline": True, "dynamicPrompts": True}),
|
| 60 |
-
"image_interleave": ("INT", {"default": 2, "min": 1, "max": 512, "tooltip": "How much the image influences things vs the text prompt. Higher number means more influence from the text prompt."}),
|
| 61 |
-
}}
|
| 62 |
-
RETURN_TYPES = ("CONDITIONING",)
|
| 63 |
-
FUNCTION = "encode"
|
| 64 |
-
|
| 65 |
-
CATEGORY = "advanced/conditioning"
|
| 66 |
-
|
| 67 |
-
def encode(self, clip, clip_vision_output, prompt, image_interleave):
|
| 68 |
-
tokens = clip.tokenize(prompt, llama_template=PROMPT_TEMPLATE_ENCODE_VIDEO_I2V, image_embeds=clip_vision_output.mm_projected, image_interleave=image_interleave)
|
| 69 |
-
return (clip.encode_from_tokens_scheduled(tokens), )
|
| 70 |
-
|
| 71 |
-
class HunyuanImageToVideo:
|
| 72 |
-
@classmethod
|
| 73 |
-
def INPUT_TYPES(s):
|
| 74 |
-
return {"required": {"positive": ("CONDITIONING", ),
|
| 75 |
-
"vae": ("VAE", ),
|
| 76 |
-
"width": ("INT", {"default": 848, "min": 16, "max": nodes.MAX_RESOLUTION, "step": 16}),
|
| 77 |
-
"height": ("INT", {"default": 480, "min": 16, "max": nodes.MAX_RESOLUTION, "step": 16}),
|
| 78 |
-
"length": ("INT", {"default": 53, "min": 1, "max": nodes.MAX_RESOLUTION, "step": 4}),
|
| 79 |
-
"batch_size": ("INT", {"default": 1, "min": 1, "max": 4096}),
|
| 80 |
-
"guidance_type": (["v1 (concat)", "v2 (replace)", "custom"], )
|
| 81 |
-
},
|
| 82 |
-
"optional": {"start_image": ("IMAGE", ),
|
| 83 |
-
}}
|
| 84 |
-
|
| 85 |
-
RETURN_TYPES = ("CONDITIONING", "LATENT")
|
| 86 |
-
RETURN_NAMES = ("positive", "latent")
|
| 87 |
-
FUNCTION = "encode"
|
| 88 |
-
|
| 89 |
-
CATEGORY = "conditioning/video_models"
|
| 90 |
-
|
| 91 |
-
def encode(self, positive, vae, width, height, length, batch_size, guidance_type, start_image=None):
|
| 92 |
-
latent = torch.zeros([batch_size, 16, ((length - 1) // 4) + 1, height // 8, width // 8], device=comfy.model_management.intermediate_device())
|
| 93 |
-
out_latent = {}
|
| 94 |
-
|
| 95 |
-
if start_image is not None:
|
| 96 |
-
start_image = comfy.utils.common_upscale(start_image[:length, :, :, :3].movedim(-1, 1), width, height, "bilinear", "center").movedim(1, -1)
|
| 97 |
-
|
| 98 |
-
concat_latent_image = vae.encode(start_image)
|
| 99 |
-
mask = torch.ones((1, 1, latent.shape[2], concat_latent_image.shape[-2], concat_latent_image.shape[-1]), device=start_image.device, dtype=start_image.dtype)
|
| 100 |
-
mask[:, :, :((start_image.shape[0] - 1) // 4) + 1] = 0.0
|
| 101 |
-
|
| 102 |
-
if guidance_type == "v1 (concat)":
|
| 103 |
-
cond = {"concat_latent_image": concat_latent_image, "concat_mask": mask}
|
| 104 |
-
elif guidance_type == "v2 (replace)":
|
| 105 |
-
cond = {'guiding_frame_index': 0}
|
| 106 |
-
latent[:, :, :concat_latent_image.shape[2]] = concat_latent_image
|
| 107 |
-
out_latent["noise_mask"] = mask
|
| 108 |
-
elif guidance_type == "custom":
|
| 109 |
-
cond = {"ref_latent": concat_latent_image}
|
| 110 |
-
|
| 111 |
-
positive = node_helpers.conditioning_set_values(positive, cond)
|
| 112 |
-
|
| 113 |
-
out_latent["samples"] = latent
|
| 114 |
-
return (positive, out_latent)
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
NODE_CLASS_MAPPINGS = {
|
| 119 |
-
"CLIPTextEncodeHunyuanDiT": CLIPTextEncodeHunyuanDiT,
|
| 120 |
-
"TextEncodeHunyuanVideo_ImageToVideo": TextEncodeHunyuanVideo_ImageToVideo,
|
| 121 |
-
"EmptyHunyuanLatentVideo": EmptyHunyuanLatentVideo,
|
| 122 |
-
"HunyuanImageToVideo": HunyuanImageToVideo,
|
| 123 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
comfy_extras/nodes_hunyuan3d.py
DELETED
|
@@ -1,634 +0,0 @@
|
|
| 1 |
-
import torch
|
| 2 |
-
import os
|
| 3 |
-
import json
|
| 4 |
-
import struct
|
| 5 |
-
import numpy as np
|
| 6 |
-
from comfy.ldm.modules.diffusionmodules.mmdit import get_1d_sincos_pos_embed_from_grid_torch
|
| 7 |
-
import folder_paths
|
| 8 |
-
import comfy.model_management
|
| 9 |
-
from comfy.cli_args import args
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
class EmptyLatentHunyuan3Dv2:
|
| 13 |
-
@classmethod
|
| 14 |
-
def INPUT_TYPES(s):
|
| 15 |
-
return {"required": {"resolution": ("INT", {"default": 3072, "min": 1, "max": 8192}),
|
| 16 |
-
"batch_size": ("INT", {"default": 1, "min": 1, "max": 4096, "tooltip": "The number of latent images in the batch."}),
|
| 17 |
-
}}
|
| 18 |
-
RETURN_TYPES = ("LATENT",)
|
| 19 |
-
FUNCTION = "generate"
|
| 20 |
-
|
| 21 |
-
CATEGORY = "latent/3d"
|
| 22 |
-
|
| 23 |
-
def generate(self, resolution, batch_size):
|
| 24 |
-
latent = torch.zeros([batch_size, 64, resolution], device=comfy.model_management.intermediate_device())
|
| 25 |
-
return ({"samples": latent, "type": "hunyuan3dv2"}, )
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
class Hunyuan3Dv2Conditioning:
|
| 29 |
-
@classmethod
|
| 30 |
-
def INPUT_TYPES(s):
|
| 31 |
-
return {"required": {"clip_vision_output": ("CLIP_VISION_OUTPUT",),
|
| 32 |
-
}}
|
| 33 |
-
|
| 34 |
-
RETURN_TYPES = ("CONDITIONING", "CONDITIONING")
|
| 35 |
-
RETURN_NAMES = ("positive", "negative")
|
| 36 |
-
|
| 37 |
-
FUNCTION = "encode"
|
| 38 |
-
|
| 39 |
-
CATEGORY = "conditioning/video_models"
|
| 40 |
-
|
| 41 |
-
def encode(self, clip_vision_output):
|
| 42 |
-
embeds = clip_vision_output.last_hidden_state
|
| 43 |
-
positive = [[embeds, {}]]
|
| 44 |
-
negative = [[torch.zeros_like(embeds), {}]]
|
| 45 |
-
return (positive, negative)
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
class Hunyuan3Dv2ConditioningMultiView:
|
| 49 |
-
@classmethod
|
| 50 |
-
def INPUT_TYPES(s):
|
| 51 |
-
return {"required": {},
|
| 52 |
-
"optional": {"front": ("CLIP_VISION_OUTPUT",),
|
| 53 |
-
"left": ("CLIP_VISION_OUTPUT",),
|
| 54 |
-
"back": ("CLIP_VISION_OUTPUT",),
|
| 55 |
-
"right": ("CLIP_VISION_OUTPUT",), }}
|
| 56 |
-
|
| 57 |
-
RETURN_TYPES = ("CONDITIONING", "CONDITIONING")
|
| 58 |
-
RETURN_NAMES = ("positive", "negative")
|
| 59 |
-
|
| 60 |
-
FUNCTION = "encode"
|
| 61 |
-
|
| 62 |
-
CATEGORY = "conditioning/video_models"
|
| 63 |
-
|
| 64 |
-
def encode(self, front=None, left=None, back=None, right=None):
|
| 65 |
-
all_embeds = [front, left, back, right]
|
| 66 |
-
out = []
|
| 67 |
-
pos_embeds = None
|
| 68 |
-
for i, e in enumerate(all_embeds):
|
| 69 |
-
if e is not None:
|
| 70 |
-
if pos_embeds is None:
|
| 71 |
-
pos_embeds = get_1d_sincos_pos_embed_from_grid_torch(e.last_hidden_state.shape[-1], torch.arange(4))
|
| 72 |
-
out.append(e.last_hidden_state + pos_embeds[i].reshape(1, 1, -1))
|
| 73 |
-
|
| 74 |
-
embeds = torch.cat(out, dim=1)
|
| 75 |
-
positive = [[embeds, {}]]
|
| 76 |
-
negative = [[torch.zeros_like(embeds), {}]]
|
| 77 |
-
return (positive, negative)
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
class VOXEL:
|
| 81 |
-
def __init__(self, data):
|
| 82 |
-
self.data = data
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
class VAEDecodeHunyuan3D:
|
| 86 |
-
@classmethod
|
| 87 |
-
def INPUT_TYPES(s):
|
| 88 |
-
return {"required": {"samples": ("LATENT", ),
|
| 89 |
-
"vae": ("VAE", ),
|
| 90 |
-
"num_chunks": ("INT", {"default": 8000, "min": 1000, "max": 500000}),
|
| 91 |
-
"octree_resolution": ("INT", {"default": 256, "min": 16, "max": 512}),
|
| 92 |
-
}}
|
| 93 |
-
RETURN_TYPES = ("VOXEL",)
|
| 94 |
-
FUNCTION = "decode"
|
| 95 |
-
|
| 96 |
-
CATEGORY = "latent/3d"
|
| 97 |
-
|
| 98 |
-
def decode(self, vae, samples, num_chunks, octree_resolution):
|
| 99 |
-
voxels = VOXEL(vae.decode(samples["samples"], vae_options={"num_chunks": num_chunks, "octree_resolution": octree_resolution}))
|
| 100 |
-
return (voxels, )
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
def voxel_to_mesh(voxels, threshold=0.5, device=None):
|
| 104 |
-
if device is None:
|
| 105 |
-
device = torch.device("cpu")
|
| 106 |
-
voxels = voxels.to(device)
|
| 107 |
-
|
| 108 |
-
binary = (voxels > threshold).float()
|
| 109 |
-
padded = torch.nn.functional.pad(binary, (1, 1, 1, 1, 1, 1), 'constant', 0)
|
| 110 |
-
|
| 111 |
-
D, H, W = binary.shape
|
| 112 |
-
|
| 113 |
-
neighbors = torch.tensor([
|
| 114 |
-
[0, 0, 1],
|
| 115 |
-
[0, 0, -1],
|
| 116 |
-
[0, 1, 0],
|
| 117 |
-
[0, -1, 0],
|
| 118 |
-
[1, 0, 0],
|
| 119 |
-
[-1, 0, 0]
|
| 120 |
-
], device=device)
|
| 121 |
-
|
| 122 |
-
z, y, x = torch.meshgrid(
|
| 123 |
-
torch.arange(D, device=device),
|
| 124 |
-
torch.arange(H, device=device),
|
| 125 |
-
torch.arange(W, device=device),
|
| 126 |
-
indexing='ij'
|
| 127 |
-
)
|
| 128 |
-
voxel_indices = torch.stack([z.flatten(), y.flatten(), x.flatten()], dim=1)
|
| 129 |
-
|
| 130 |
-
solid_mask = binary.flatten() > 0
|
| 131 |
-
solid_indices = voxel_indices[solid_mask]
|
| 132 |
-
|
| 133 |
-
corner_offsets = [
|
| 134 |
-
torch.tensor([
|
| 135 |
-
[0, 0, 1], [0, 1, 1], [1, 1, 1], [1, 0, 1]
|
| 136 |
-
], device=device),
|
| 137 |
-
torch.tensor([
|
| 138 |
-
[0, 0, 0], [1, 0, 0], [1, 1, 0], [0, 1, 0]
|
| 139 |
-
], device=device),
|
| 140 |
-
torch.tensor([
|
| 141 |
-
[0, 1, 0], [1, 1, 0], [1, 1, 1], [0, 1, 1]
|
| 142 |
-
], device=device),
|
| 143 |
-
torch.tensor([
|
| 144 |
-
[0, 0, 0], [0, 0, 1], [1, 0, 1], [1, 0, 0]
|
| 145 |
-
], device=device),
|
| 146 |
-
torch.tensor([
|
| 147 |
-
[1, 0, 1], [1, 1, 1], [1, 1, 0], [1, 0, 0]
|
| 148 |
-
], device=device),
|
| 149 |
-
torch.tensor([
|
| 150 |
-
[0, 1, 0], [0, 1, 1], [0, 0, 1], [0, 0, 0]
|
| 151 |
-
], device=device)
|
| 152 |
-
]
|
| 153 |
-
|
| 154 |
-
all_vertices = []
|
| 155 |
-
all_indices = []
|
| 156 |
-
|
| 157 |
-
vertex_count = 0
|
| 158 |
-
|
| 159 |
-
for face_idx, offset in enumerate(neighbors):
|
| 160 |
-
neighbor_indices = solid_indices + offset
|
| 161 |
-
|
| 162 |
-
padded_indices = neighbor_indices + 1
|
| 163 |
-
|
| 164 |
-
is_exposed = padded[
|
| 165 |
-
padded_indices[:, 0],
|
| 166 |
-
padded_indices[:, 1],
|
| 167 |
-
padded_indices[:, 2]
|
| 168 |
-
] == 0
|
| 169 |
-
|
| 170 |
-
if not is_exposed.any():
|
| 171 |
-
continue
|
| 172 |
-
|
| 173 |
-
exposed_indices = solid_indices[is_exposed]
|
| 174 |
-
|
| 175 |
-
corners = corner_offsets[face_idx].unsqueeze(0)
|
| 176 |
-
|
| 177 |
-
face_vertices = exposed_indices.unsqueeze(1) + corners
|
| 178 |
-
|
| 179 |
-
all_vertices.append(face_vertices.reshape(-1, 3))
|
| 180 |
-
|
| 181 |
-
num_faces = exposed_indices.shape[0]
|
| 182 |
-
face_indices = torch.arange(
|
| 183 |
-
vertex_count,
|
| 184 |
-
vertex_count + 4 * num_faces,
|
| 185 |
-
device=device
|
| 186 |
-
).reshape(-1, 4)
|
| 187 |
-
|
| 188 |
-
all_indices.append(torch.stack([face_indices[:, 0], face_indices[:, 1], face_indices[:, 2]], dim=1))
|
| 189 |
-
all_indices.append(torch.stack([face_indices[:, 0], face_indices[:, 2], face_indices[:, 3]], dim=1))
|
| 190 |
-
|
| 191 |
-
vertex_count += 4 * num_faces
|
| 192 |
-
|
| 193 |
-
if len(all_vertices) > 0:
|
| 194 |
-
vertices = torch.cat(all_vertices, dim=0)
|
| 195 |
-
faces = torch.cat(all_indices, dim=0)
|
| 196 |
-
else:
|
| 197 |
-
vertices = torch.zeros((1, 3))
|
| 198 |
-
faces = torch.zeros((1, 3))
|
| 199 |
-
|
| 200 |
-
v_min = 0
|
| 201 |
-
v_max = max(voxels.shape)
|
| 202 |
-
|
| 203 |
-
vertices = vertices - (v_min + v_max) / 2
|
| 204 |
-
|
| 205 |
-
scale = (v_max - v_min) / 2
|
| 206 |
-
if scale > 0:
|
| 207 |
-
vertices = vertices / scale
|
| 208 |
-
|
| 209 |
-
vertices = torch.fliplr(vertices)
|
| 210 |
-
return vertices, faces
|
| 211 |
-
|
| 212 |
-
def voxel_to_mesh_surfnet(voxels, threshold=0.5, device=None):
|
| 213 |
-
if device is None:
|
| 214 |
-
device = torch.device("cpu")
|
| 215 |
-
voxels = voxels.to(device)
|
| 216 |
-
|
| 217 |
-
D, H, W = voxels.shape
|
| 218 |
-
|
| 219 |
-
padded = torch.nn.functional.pad(voxels, (1, 1, 1, 1, 1, 1), 'constant', 0)
|
| 220 |
-
z, y, x = torch.meshgrid(
|
| 221 |
-
torch.arange(D, device=device),
|
| 222 |
-
torch.arange(H, device=device),
|
| 223 |
-
torch.arange(W, device=device),
|
| 224 |
-
indexing='ij'
|
| 225 |
-
)
|
| 226 |
-
cell_positions = torch.stack([z.flatten(), y.flatten(), x.flatten()], dim=1)
|
| 227 |
-
|
| 228 |
-
corner_offsets = torch.tensor([
|
| 229 |
-
[0, 0, 0], [1, 0, 0], [0, 1, 0], [1, 1, 0],
|
| 230 |
-
[0, 0, 1], [1, 0, 1], [0, 1, 1], [1, 1, 1]
|
| 231 |
-
], device=device)
|
| 232 |
-
|
| 233 |
-
corner_values = torch.zeros((cell_positions.shape[0], 8), device=device)
|
| 234 |
-
for c, (dz, dy, dx) in enumerate(corner_offsets):
|
| 235 |
-
corner_values[:, c] = padded[
|
| 236 |
-
cell_positions[:, 0] + dz,
|
| 237 |
-
cell_positions[:, 1] + dy,
|
| 238 |
-
cell_positions[:, 2] + dx
|
| 239 |
-
]
|
| 240 |
-
|
| 241 |
-
corner_signs = corner_values > threshold
|
| 242 |
-
has_inside = torch.any(corner_signs, dim=1)
|
| 243 |
-
has_outside = torch.any(~corner_signs, dim=1)
|
| 244 |
-
contains_surface = has_inside & has_outside
|
| 245 |
-
|
| 246 |
-
active_cells = cell_positions[contains_surface]
|
| 247 |
-
active_signs = corner_signs[contains_surface]
|
| 248 |
-
active_values = corner_values[contains_surface]
|
| 249 |
-
|
| 250 |
-
if active_cells.shape[0] == 0:
|
| 251 |
-
return torch.zeros((0, 3), device=device), torch.zeros((0, 3), dtype=torch.long, device=device)
|
| 252 |
-
|
| 253 |
-
edges = torch.tensor([
|
| 254 |
-
[0, 1], [0, 2], [0, 4], [1, 3],
|
| 255 |
-
[1, 5], [2, 3], [2, 6], [3, 7],
|
| 256 |
-
[4, 5], [4, 6], [5, 7], [6, 7]
|
| 257 |
-
], device=device)
|
| 258 |
-
|
| 259 |
-
cell_vertices = {}
|
| 260 |
-
progress = comfy.utils.ProgressBar(100)
|
| 261 |
-
|
| 262 |
-
for edge_idx, (e1, e2) in enumerate(edges):
|
| 263 |
-
progress.update(1)
|
| 264 |
-
crossing = active_signs[:, e1] != active_signs[:, e2]
|
| 265 |
-
if not crossing.any():
|
| 266 |
-
continue
|
| 267 |
-
|
| 268 |
-
cell_indices = torch.nonzero(crossing, as_tuple=True)[0]
|
| 269 |
-
|
| 270 |
-
v1 = active_values[cell_indices, e1]
|
| 271 |
-
v2 = active_values[cell_indices, e2]
|
| 272 |
-
|
| 273 |
-
t = torch.zeros_like(v1, device=device)
|
| 274 |
-
denom = v2 - v1
|
| 275 |
-
valid = denom != 0
|
| 276 |
-
t[valid] = (threshold - v1[valid]) / denom[valid]
|
| 277 |
-
t[~valid] = 0.5
|
| 278 |
-
|
| 279 |
-
p1 = corner_offsets[e1].float()
|
| 280 |
-
p2 = corner_offsets[e2].float()
|
| 281 |
-
|
| 282 |
-
intersection = p1.unsqueeze(0) + t.unsqueeze(1) * (p2.unsqueeze(0) - p1.unsqueeze(0))
|
| 283 |
-
|
| 284 |
-
for i, point in zip(cell_indices.tolist(), intersection):
|
| 285 |
-
if i not in cell_vertices:
|
| 286 |
-
cell_vertices[i] = []
|
| 287 |
-
cell_vertices[i].append(point)
|
| 288 |
-
|
| 289 |
-
# Calculate the final vertices as the average of intersection points for each cell
|
| 290 |
-
vertices = []
|
| 291 |
-
vertex_lookup = {}
|
| 292 |
-
|
| 293 |
-
vert_progress_mod = round(len(cell_vertices)/50)
|
| 294 |
-
|
| 295 |
-
for i, points in cell_vertices.items():
|
| 296 |
-
if not i % vert_progress_mod:
|
| 297 |
-
progress.update(1)
|
| 298 |
-
|
| 299 |
-
if points:
|
| 300 |
-
vertex = torch.stack(points).mean(dim=0)
|
| 301 |
-
vertex = vertex + active_cells[i].float()
|
| 302 |
-
vertex_lookup[tuple(active_cells[i].tolist())] = len(vertices)
|
| 303 |
-
vertices.append(vertex)
|
| 304 |
-
|
| 305 |
-
if not vertices:
|
| 306 |
-
return torch.zeros((0, 3), device=device), torch.zeros((0, 3), dtype=torch.long, device=device)
|
| 307 |
-
|
| 308 |
-
final_vertices = torch.stack(vertices)
|
| 309 |
-
|
| 310 |
-
inside_corners_mask = active_signs
|
| 311 |
-
outside_corners_mask = ~active_signs
|
| 312 |
-
|
| 313 |
-
inside_counts = inside_corners_mask.sum(dim=1, keepdim=True).float()
|
| 314 |
-
outside_counts = outside_corners_mask.sum(dim=1, keepdim=True).float()
|
| 315 |
-
|
| 316 |
-
inside_pos = torch.zeros((active_cells.shape[0], 3), device=device)
|
| 317 |
-
outside_pos = torch.zeros((active_cells.shape[0], 3), device=device)
|
| 318 |
-
|
| 319 |
-
for i in range(8):
|
| 320 |
-
mask_inside = inside_corners_mask[:, i].unsqueeze(1)
|
| 321 |
-
mask_outside = outside_corners_mask[:, i].unsqueeze(1)
|
| 322 |
-
inside_pos += corner_offsets[i].float().unsqueeze(0) * mask_inside
|
| 323 |
-
outside_pos += corner_offsets[i].float().unsqueeze(0) * mask_outside
|
| 324 |
-
|
| 325 |
-
inside_pos /= inside_counts
|
| 326 |
-
outside_pos /= outside_counts
|
| 327 |
-
gradients = inside_pos - outside_pos
|
| 328 |
-
|
| 329 |
-
pos_dirs = torch.tensor([
|
| 330 |
-
[1, 0, 0],
|
| 331 |
-
[0, 1, 0],
|
| 332 |
-
[0, 0, 1]
|
| 333 |
-
], device=device)
|
| 334 |
-
|
| 335 |
-
cross_products = [
|
| 336 |
-
torch.linalg.cross(pos_dirs[i].float(), pos_dirs[j].float())
|
| 337 |
-
for i in range(3) for j in range(i+1, 3)
|
| 338 |
-
]
|
| 339 |
-
|
| 340 |
-
faces = []
|
| 341 |
-
all_keys = set(vertex_lookup.keys())
|
| 342 |
-
|
| 343 |
-
face_progress_mod = round(len(active_cells)/38*3)
|
| 344 |
-
|
| 345 |
-
for pair_idx, (i, j) in enumerate([(0,1), (0,2), (1,2)]):
|
| 346 |
-
dir_i = pos_dirs[i]
|
| 347 |
-
dir_j = pos_dirs[j]
|
| 348 |
-
cross_product = cross_products[pair_idx]
|
| 349 |
-
|
| 350 |
-
ni_positions = active_cells + dir_i
|
| 351 |
-
nj_positions = active_cells + dir_j
|
| 352 |
-
diag_positions = active_cells + dir_i + dir_j
|
| 353 |
-
|
| 354 |
-
alignments = torch.matmul(gradients, cross_product)
|
| 355 |
-
|
| 356 |
-
valid_quads = []
|
| 357 |
-
quad_indices = []
|
| 358 |
-
|
| 359 |
-
for idx, active_cell in enumerate(active_cells):
|
| 360 |
-
if not idx % face_progress_mod:
|
| 361 |
-
progress.update(1)
|
| 362 |
-
cell_key = tuple(active_cell.tolist())
|
| 363 |
-
ni_key = tuple(ni_positions[idx].tolist())
|
| 364 |
-
nj_key = tuple(nj_positions[idx].tolist())
|
| 365 |
-
diag_key = tuple(diag_positions[idx].tolist())
|
| 366 |
-
|
| 367 |
-
if cell_key in all_keys and ni_key in all_keys and nj_key in all_keys and diag_key in all_keys:
|
| 368 |
-
v0 = vertex_lookup[cell_key]
|
| 369 |
-
v1 = vertex_lookup[ni_key]
|
| 370 |
-
v2 = vertex_lookup[nj_key]
|
| 371 |
-
v3 = vertex_lookup[diag_key]
|
| 372 |
-
|
| 373 |
-
valid_quads.append((v0, v1, v2, v3))
|
| 374 |
-
quad_indices.append(idx)
|
| 375 |
-
|
| 376 |
-
for q_idx, (v0, v1, v2, v3) in enumerate(valid_quads):
|
| 377 |
-
cell_idx = quad_indices[q_idx]
|
| 378 |
-
if alignments[cell_idx] > 0:
|
| 379 |
-
faces.append(torch.tensor([v0, v1, v3], device=device, dtype=torch.long))
|
| 380 |
-
faces.append(torch.tensor([v0, v3, v2], device=device, dtype=torch.long))
|
| 381 |
-
else:
|
| 382 |
-
faces.append(torch.tensor([v0, v3, v1], device=device, dtype=torch.long))
|
| 383 |
-
faces.append(torch.tensor([v0, v2, v3], device=device, dtype=torch.long))
|
| 384 |
-
|
| 385 |
-
if faces:
|
| 386 |
-
faces = torch.stack(faces)
|
| 387 |
-
else:
|
| 388 |
-
faces = torch.zeros((0, 3), dtype=torch.long, device=device)
|
| 389 |
-
|
| 390 |
-
v_min = 0
|
| 391 |
-
v_max = max(D, H, W)
|
| 392 |
-
|
| 393 |
-
final_vertices = final_vertices - (v_min + v_max) / 2
|
| 394 |
-
|
| 395 |
-
scale = (v_max - v_min) / 2
|
| 396 |
-
if scale > 0:
|
| 397 |
-
final_vertices = final_vertices / scale
|
| 398 |
-
|
| 399 |
-
final_vertices = torch.fliplr(final_vertices)
|
| 400 |
-
|
| 401 |
-
return final_vertices, faces
|
| 402 |
-
|
| 403 |
-
class MESH:
|
| 404 |
-
def __init__(self, vertices, faces):
|
| 405 |
-
self.vertices = vertices
|
| 406 |
-
self.faces = faces
|
| 407 |
-
|
| 408 |
-
|
| 409 |
-
class VoxelToMeshBasic:
|
| 410 |
-
@classmethod
|
| 411 |
-
def INPUT_TYPES(s):
|
| 412 |
-
return {"required": {"voxel": ("VOXEL", ),
|
| 413 |
-
"threshold": ("FLOAT", {"default": 0.6, "min": -1.0, "max": 1.0, "step": 0.01}),
|
| 414 |
-
}}
|
| 415 |
-
RETURN_TYPES = ("MESH",)
|
| 416 |
-
FUNCTION = "decode"
|
| 417 |
-
|
| 418 |
-
CATEGORY = "3d"
|
| 419 |
-
|
| 420 |
-
def decode(self, voxel, threshold):
|
| 421 |
-
vertices = []
|
| 422 |
-
faces = []
|
| 423 |
-
for x in voxel.data:
|
| 424 |
-
v, f = voxel_to_mesh(x, threshold=threshold, device=None)
|
| 425 |
-
vertices.append(v)
|
| 426 |
-
faces.append(f)
|
| 427 |
-
|
| 428 |
-
return (MESH(torch.stack(vertices), torch.stack(faces)), )
|
| 429 |
-
|
| 430 |
-
class VoxelToMesh:
|
| 431 |
-
@classmethod
|
| 432 |
-
def INPUT_TYPES(s):
|
| 433 |
-
return {"required": {"voxel": ("VOXEL", ),
|
| 434 |
-
"algorithm": (["surface net", "basic"], ),
|
| 435 |
-
"threshold": ("FLOAT", {"default": 0.6, "min": -1.0, "max": 1.0, "step": 0.01}),
|
| 436 |
-
}}
|
| 437 |
-
RETURN_TYPES = ("MESH",)
|
| 438 |
-
FUNCTION = "decode"
|
| 439 |
-
|
| 440 |
-
CATEGORY = "3d"
|
| 441 |
-
|
| 442 |
-
def decode(self, voxel, algorithm, threshold):
|
| 443 |
-
vertices = []
|
| 444 |
-
faces = []
|
| 445 |
-
|
| 446 |
-
if algorithm == "basic":
|
| 447 |
-
mesh_function = voxel_to_mesh
|
| 448 |
-
elif algorithm == "surface net":
|
| 449 |
-
mesh_function = voxel_to_mesh_surfnet
|
| 450 |
-
|
| 451 |
-
for x in voxel.data:
|
| 452 |
-
v, f = mesh_function(x, threshold=threshold, device=None)
|
| 453 |
-
vertices.append(v)
|
| 454 |
-
faces.append(f)
|
| 455 |
-
|
| 456 |
-
return (MESH(torch.stack(vertices), torch.stack(faces)), )
|
| 457 |
-
|
| 458 |
-
|
| 459 |
-
def save_glb(vertices, faces, filepath, metadata=None):
|
| 460 |
-
"""
|
| 461 |
-
Save PyTorch tensor vertices and faces as a GLB file without external dependencies.
|
| 462 |
-
|
| 463 |
-
Parameters:
|
| 464 |
-
vertices: torch.Tensor of shape (N, 3) - The vertex coordinates
|
| 465 |
-
faces: torch.Tensor of shape (M, 3) - The face indices (triangle faces)
|
| 466 |
-
filepath: str - Output filepath (should end with .glb)
|
| 467 |
-
"""
|
| 468 |
-
|
| 469 |
-
# Convert tensors to numpy arrays
|
| 470 |
-
vertices_np = vertices.cpu().numpy().astype(np.float32)
|
| 471 |
-
faces_np = faces.cpu().numpy().astype(np.uint32)
|
| 472 |
-
|
| 473 |
-
vertices_buffer = vertices_np.tobytes()
|
| 474 |
-
indices_buffer = faces_np.tobytes()
|
| 475 |
-
|
| 476 |
-
def pad_to_4_bytes(buffer):
|
| 477 |
-
padding_length = (4 - (len(buffer) % 4)) % 4
|
| 478 |
-
return buffer + b'\x00' * padding_length
|
| 479 |
-
|
| 480 |
-
vertices_buffer_padded = pad_to_4_bytes(vertices_buffer)
|
| 481 |
-
indices_buffer_padded = pad_to_4_bytes(indices_buffer)
|
| 482 |
-
|
| 483 |
-
buffer_data = vertices_buffer_padded + indices_buffer_padded
|
| 484 |
-
|
| 485 |
-
vertices_byte_length = len(vertices_buffer)
|
| 486 |
-
vertices_byte_offset = 0
|
| 487 |
-
indices_byte_length = len(indices_buffer)
|
| 488 |
-
indices_byte_offset = len(vertices_buffer_padded)
|
| 489 |
-
|
| 490 |
-
gltf = {
|
| 491 |
-
"asset": {"version": "2.0", "generator": "ComfyUI"},
|
| 492 |
-
"buffers": [
|
| 493 |
-
{
|
| 494 |
-
"byteLength": len(buffer_data)
|
| 495 |
-
}
|
| 496 |
-
],
|
| 497 |
-
"bufferViews": [
|
| 498 |
-
{
|
| 499 |
-
"buffer": 0,
|
| 500 |
-
"byteOffset": vertices_byte_offset,
|
| 501 |
-
"byteLength": vertices_byte_length,
|
| 502 |
-
"target": 34962 # ARRAY_BUFFER
|
| 503 |
-
},
|
| 504 |
-
{
|
| 505 |
-
"buffer": 0,
|
| 506 |
-
"byteOffset": indices_byte_offset,
|
| 507 |
-
"byteLength": indices_byte_length,
|
| 508 |
-
"target": 34963 # ELEMENT_ARRAY_BUFFER
|
| 509 |
-
}
|
| 510 |
-
],
|
| 511 |
-
"accessors": [
|
| 512 |
-
{
|
| 513 |
-
"bufferView": 0,
|
| 514 |
-
"byteOffset": 0,
|
| 515 |
-
"componentType": 5126, # FLOAT
|
| 516 |
-
"count": len(vertices_np),
|
| 517 |
-
"type": "VEC3",
|
| 518 |
-
"max": vertices_np.max(axis=0).tolist(),
|
| 519 |
-
"min": vertices_np.min(axis=0).tolist()
|
| 520 |
-
},
|
| 521 |
-
{
|
| 522 |
-
"bufferView": 1,
|
| 523 |
-
"byteOffset": 0,
|
| 524 |
-
"componentType": 5125, # UNSIGNED_INT
|
| 525 |
-
"count": faces_np.size,
|
| 526 |
-
"type": "SCALAR"
|
| 527 |
-
}
|
| 528 |
-
],
|
| 529 |
-
"meshes": [
|
| 530 |
-
{
|
| 531 |
-
"primitives": [
|
| 532 |
-
{
|
| 533 |
-
"attributes": {
|
| 534 |
-
"POSITION": 0
|
| 535 |
-
},
|
| 536 |
-
"indices": 1,
|
| 537 |
-
"mode": 4 # TRIANGLES
|
| 538 |
-
}
|
| 539 |
-
]
|
| 540 |
-
}
|
| 541 |
-
],
|
| 542 |
-
"nodes": [
|
| 543 |
-
{
|
| 544 |
-
"mesh": 0
|
| 545 |
-
}
|
| 546 |
-
],
|
| 547 |
-
"scenes": [
|
| 548 |
-
{
|
| 549 |
-
"nodes": [0]
|
| 550 |
-
}
|
| 551 |
-
],
|
| 552 |
-
"scene": 0
|
| 553 |
-
}
|
| 554 |
-
|
| 555 |
-
if metadata is not None:
|
| 556 |
-
gltf["asset"]["extras"] = metadata
|
| 557 |
-
|
| 558 |
-
# Convert the JSON to bytes
|
| 559 |
-
gltf_json = json.dumps(gltf).encode('utf8')
|
| 560 |
-
|
| 561 |
-
def pad_json_to_4_bytes(buffer):
|
| 562 |
-
padding_length = (4 - (len(buffer) % 4)) % 4
|
| 563 |
-
return buffer + b' ' * padding_length
|
| 564 |
-
|
| 565 |
-
gltf_json_padded = pad_json_to_4_bytes(gltf_json)
|
| 566 |
-
|
| 567 |
-
# Create the GLB header
|
| 568 |
-
# Magic glTF
|
| 569 |
-
glb_header = struct.pack('<4sII', b'glTF', 2, 12 + 8 + len(gltf_json_padded) + 8 + len(buffer_data))
|
| 570 |
-
|
| 571 |
-
# Create JSON chunk header (chunk type 0)
|
| 572 |
-
json_chunk_header = struct.pack('<II', len(gltf_json_padded), 0x4E4F534A) # "JSON" in little endian
|
| 573 |
-
|
| 574 |
-
# Create BIN chunk header (chunk type 1)
|
| 575 |
-
bin_chunk_header = struct.pack('<II', len(buffer_data), 0x004E4942) # "BIN\0" in little endian
|
| 576 |
-
|
| 577 |
-
# Write the GLB file
|
| 578 |
-
with open(filepath, 'wb') as f:
|
| 579 |
-
f.write(glb_header)
|
| 580 |
-
f.write(json_chunk_header)
|
| 581 |
-
f.write(gltf_json_padded)
|
| 582 |
-
f.write(bin_chunk_header)
|
| 583 |
-
f.write(buffer_data)
|
| 584 |
-
|
| 585 |
-
return filepath
|
| 586 |
-
|
| 587 |
-
|
| 588 |
-
class SaveGLB:
|
| 589 |
-
@classmethod
|
| 590 |
-
def INPUT_TYPES(s):
|
| 591 |
-
return {"required": {"mesh": ("MESH", ),
|
| 592 |
-
"filename_prefix": ("STRING", {"default": "mesh/ComfyUI"}), },
|
| 593 |
-
"hidden": {"prompt": "PROMPT", "extra_pnginfo": "EXTRA_PNGINFO"}, }
|
| 594 |
-
|
| 595 |
-
RETURN_TYPES = ()
|
| 596 |
-
FUNCTION = "save"
|
| 597 |
-
|
| 598 |
-
OUTPUT_NODE = True
|
| 599 |
-
|
| 600 |
-
CATEGORY = "3d"
|
| 601 |
-
|
| 602 |
-
def save(self, mesh, filename_prefix, prompt=None, extra_pnginfo=None):
|
| 603 |
-
full_output_folder, filename, counter, subfolder, filename_prefix = folder_paths.get_save_image_path(filename_prefix, folder_paths.get_output_directory())
|
| 604 |
-
results = []
|
| 605 |
-
|
| 606 |
-
metadata = {}
|
| 607 |
-
if not args.disable_metadata:
|
| 608 |
-
if prompt is not None:
|
| 609 |
-
metadata["prompt"] = json.dumps(prompt)
|
| 610 |
-
if extra_pnginfo is not None:
|
| 611 |
-
for x in extra_pnginfo:
|
| 612 |
-
metadata[x] = json.dumps(extra_pnginfo[x])
|
| 613 |
-
|
| 614 |
-
for i in range(mesh.vertices.shape[0]):
|
| 615 |
-
f = f"{filename}_{counter:05}_.glb"
|
| 616 |
-
save_glb(mesh.vertices[i], mesh.faces[i], os.path.join(full_output_folder, f), metadata)
|
| 617 |
-
results.append({
|
| 618 |
-
"filename": f,
|
| 619 |
-
"subfolder": subfolder,
|
| 620 |
-
"type": "output"
|
| 621 |
-
})
|
| 622 |
-
counter += 1
|
| 623 |
-
return {"ui": {"3d": results}}
|
| 624 |
-
|
| 625 |
-
|
| 626 |
-
NODE_CLASS_MAPPINGS = {
|
| 627 |
-
"EmptyLatentHunyuan3Dv2": EmptyLatentHunyuan3Dv2,
|
| 628 |
-
"Hunyuan3Dv2Conditioning": Hunyuan3Dv2Conditioning,
|
| 629 |
-
"Hunyuan3Dv2ConditioningMultiView": Hunyuan3Dv2ConditioningMultiView,
|
| 630 |
-
"VAEDecodeHunyuan3D": VAEDecodeHunyuan3D,
|
| 631 |
-
"VoxelToMeshBasic": VoxelToMeshBasic,
|
| 632 |
-
"VoxelToMesh": VoxelToMesh,
|
| 633 |
-
"SaveGLB": SaveGLB,
|
| 634 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
comfy_extras/nodes_hypernetwork.py
DELETED
|
@@ -1,120 +0,0 @@
|
|
| 1 |
-
import comfy.utils
|
| 2 |
-
import folder_paths
|
| 3 |
-
import torch
|
| 4 |
-
import logging
|
| 5 |
-
|
| 6 |
-
def load_hypernetwork_patch(path, strength):
|
| 7 |
-
sd = comfy.utils.load_torch_file(path, safe_load=True)
|
| 8 |
-
activation_func = sd.get('activation_func', 'linear')
|
| 9 |
-
is_layer_norm = sd.get('is_layer_norm', False)
|
| 10 |
-
use_dropout = sd.get('use_dropout', False)
|
| 11 |
-
activate_output = sd.get('activate_output', False)
|
| 12 |
-
last_layer_dropout = sd.get('last_layer_dropout', False)
|
| 13 |
-
|
| 14 |
-
valid_activation = {
|
| 15 |
-
"linear": torch.nn.Identity,
|
| 16 |
-
"relu": torch.nn.ReLU,
|
| 17 |
-
"leakyrelu": torch.nn.LeakyReLU,
|
| 18 |
-
"elu": torch.nn.ELU,
|
| 19 |
-
"swish": torch.nn.Hardswish,
|
| 20 |
-
"tanh": torch.nn.Tanh,
|
| 21 |
-
"sigmoid": torch.nn.Sigmoid,
|
| 22 |
-
"softsign": torch.nn.Softsign,
|
| 23 |
-
"mish": torch.nn.Mish,
|
| 24 |
-
}
|
| 25 |
-
|
| 26 |
-
if activation_func not in valid_activation:
|
| 27 |
-
logging.error("Unsupported Hypernetwork format, if you report it I might implement it. {} {} {} {} {} {}".format(path, activation_func, is_layer_norm, use_dropout, activate_output, last_layer_dropout))
|
| 28 |
-
return None
|
| 29 |
-
|
| 30 |
-
out = {}
|
| 31 |
-
|
| 32 |
-
for d in sd:
|
| 33 |
-
try:
|
| 34 |
-
dim = int(d)
|
| 35 |
-
except:
|
| 36 |
-
continue
|
| 37 |
-
|
| 38 |
-
output = []
|
| 39 |
-
for index in [0, 1]:
|
| 40 |
-
attn_weights = sd[dim][index]
|
| 41 |
-
keys = attn_weights.keys()
|
| 42 |
-
|
| 43 |
-
linears = filter(lambda a: a.endswith(".weight"), keys)
|
| 44 |
-
linears = list(map(lambda a: a[:-len(".weight")], linears))
|
| 45 |
-
layers = []
|
| 46 |
-
|
| 47 |
-
i = 0
|
| 48 |
-
while i < len(linears):
|
| 49 |
-
lin_name = linears[i]
|
| 50 |
-
last_layer = (i == (len(linears) - 1))
|
| 51 |
-
penultimate_layer = (i == (len(linears) - 2))
|
| 52 |
-
|
| 53 |
-
lin_weight = attn_weights['{}.weight'.format(lin_name)]
|
| 54 |
-
lin_bias = attn_weights['{}.bias'.format(lin_name)]
|
| 55 |
-
layer = torch.nn.Linear(lin_weight.shape[1], lin_weight.shape[0])
|
| 56 |
-
layer.load_state_dict({"weight": lin_weight, "bias": lin_bias})
|
| 57 |
-
layers.append(layer)
|
| 58 |
-
if activation_func != "linear":
|
| 59 |
-
if (not last_layer) or (activate_output):
|
| 60 |
-
layers.append(valid_activation[activation_func]())
|
| 61 |
-
if is_layer_norm:
|
| 62 |
-
i += 1
|
| 63 |
-
ln_name = linears[i]
|
| 64 |
-
ln_weight = attn_weights['{}.weight'.format(ln_name)]
|
| 65 |
-
ln_bias = attn_weights['{}.bias'.format(ln_name)]
|
| 66 |
-
ln = torch.nn.LayerNorm(ln_weight.shape[0])
|
| 67 |
-
ln.load_state_dict({"weight": ln_weight, "bias": ln_bias})
|
| 68 |
-
layers.append(ln)
|
| 69 |
-
if use_dropout:
|
| 70 |
-
if (not last_layer) and (not penultimate_layer or last_layer_dropout):
|
| 71 |
-
layers.append(torch.nn.Dropout(p=0.3))
|
| 72 |
-
i += 1
|
| 73 |
-
|
| 74 |
-
output.append(torch.nn.Sequential(*layers))
|
| 75 |
-
out[dim] = torch.nn.ModuleList(output)
|
| 76 |
-
|
| 77 |
-
class hypernetwork_patch:
|
| 78 |
-
def __init__(self, hypernet, strength):
|
| 79 |
-
self.hypernet = hypernet
|
| 80 |
-
self.strength = strength
|
| 81 |
-
def __call__(self, q, k, v, extra_options):
|
| 82 |
-
dim = k.shape[-1]
|
| 83 |
-
if dim in self.hypernet:
|
| 84 |
-
hn = self.hypernet[dim]
|
| 85 |
-
k = k + hn[0](k) * self.strength
|
| 86 |
-
v = v + hn[1](v) * self.strength
|
| 87 |
-
|
| 88 |
-
return q, k, v
|
| 89 |
-
|
| 90 |
-
def to(self, device):
|
| 91 |
-
for d in self.hypernet.keys():
|
| 92 |
-
self.hypernet[d] = self.hypernet[d].to(device)
|
| 93 |
-
return self
|
| 94 |
-
|
| 95 |
-
return hypernetwork_patch(out, strength)
|
| 96 |
-
|
| 97 |
-
class HypernetworkLoader:
|
| 98 |
-
@classmethod
|
| 99 |
-
def INPUT_TYPES(s):
|
| 100 |
-
return {"required": { "model": ("MODEL",),
|
| 101 |
-
"hypernetwork_name": (folder_paths.get_filename_list("hypernetworks"), ),
|
| 102 |
-
"strength": ("FLOAT", {"default": 1.0, "min": -10.0, "max": 10.0, "step": 0.01}),
|
| 103 |
-
}}
|
| 104 |
-
RETURN_TYPES = ("MODEL",)
|
| 105 |
-
FUNCTION = "load_hypernetwork"
|
| 106 |
-
|
| 107 |
-
CATEGORY = "loaders"
|
| 108 |
-
|
| 109 |
-
def load_hypernetwork(self, model, hypernetwork_name, strength):
|
| 110 |
-
hypernetwork_path = folder_paths.get_full_path_or_raise("hypernetworks", hypernetwork_name)
|
| 111 |
-
model_hypernetwork = model.clone()
|
| 112 |
-
patch = load_hypernetwork_patch(hypernetwork_path, strength)
|
| 113 |
-
if patch is not None:
|
| 114 |
-
model_hypernetwork.set_model_attn1_patch(patch)
|
| 115 |
-
model_hypernetwork.set_model_attn2_patch(patch)
|
| 116 |
-
return (model_hypernetwork,)
|
| 117 |
-
|
| 118 |
-
NODE_CLASS_MAPPINGS = {
|
| 119 |
-
"HypernetworkLoader": HypernetworkLoader
|
| 120 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
comfy_extras/nodes_hypertile.py
DELETED
|
@@ -1,81 +0,0 @@
|
|
| 1 |
-
#Taken from: https://github.com/tfernd/HyperTile/
|
| 2 |
-
|
| 3 |
-
import math
|
| 4 |
-
from einops import rearrange
|
| 5 |
-
# Use torch rng for consistency across generations
|
| 6 |
-
from torch import randint
|
| 7 |
-
|
| 8 |
-
def random_divisor(value: int, min_value: int, /, max_options: int = 1) -> int:
|
| 9 |
-
min_value = min(min_value, value)
|
| 10 |
-
|
| 11 |
-
# All big divisors of value (inclusive)
|
| 12 |
-
divisors = [i for i in range(min_value, value + 1) if value % i == 0]
|
| 13 |
-
|
| 14 |
-
ns = [value // i for i in divisors[:max_options]] # has at least 1 element
|
| 15 |
-
|
| 16 |
-
if len(ns) - 1 > 0:
|
| 17 |
-
idx = randint(low=0, high=len(ns) - 1, size=(1,)).item()
|
| 18 |
-
else:
|
| 19 |
-
idx = 0
|
| 20 |
-
|
| 21 |
-
return ns[idx]
|
| 22 |
-
|
| 23 |
-
class HyperTile:
|
| 24 |
-
@classmethod
|
| 25 |
-
def INPUT_TYPES(s):
|
| 26 |
-
return {"required": { "model": ("MODEL",),
|
| 27 |
-
"tile_size": ("INT", {"default": 256, "min": 1, "max": 2048}),
|
| 28 |
-
"swap_size": ("INT", {"default": 2, "min": 1, "max": 128}),
|
| 29 |
-
"max_depth": ("INT", {"default": 0, "min": 0, "max": 10}),
|
| 30 |
-
"scale_depth": ("BOOLEAN", {"default": False}),
|
| 31 |
-
}}
|
| 32 |
-
RETURN_TYPES = ("MODEL",)
|
| 33 |
-
FUNCTION = "patch"
|
| 34 |
-
|
| 35 |
-
CATEGORY = "model_patches/unet"
|
| 36 |
-
|
| 37 |
-
def patch(self, model, tile_size, swap_size, max_depth, scale_depth):
|
| 38 |
-
latent_tile_size = max(32, tile_size) // 8
|
| 39 |
-
self.temp = None
|
| 40 |
-
|
| 41 |
-
def hypertile_in(q, k, v, extra_options):
|
| 42 |
-
model_chans = q.shape[-2]
|
| 43 |
-
orig_shape = extra_options['original_shape']
|
| 44 |
-
apply_to = []
|
| 45 |
-
for i in range(max_depth + 1):
|
| 46 |
-
apply_to.append((orig_shape[-2] / (2 ** i)) * (orig_shape[-1] / (2 ** i)))
|
| 47 |
-
|
| 48 |
-
if model_chans in apply_to:
|
| 49 |
-
shape = extra_options["original_shape"]
|
| 50 |
-
aspect_ratio = shape[-1] / shape[-2]
|
| 51 |
-
|
| 52 |
-
hw = q.size(1)
|
| 53 |
-
h, w = round(math.sqrt(hw * aspect_ratio)), round(math.sqrt(hw / aspect_ratio))
|
| 54 |
-
|
| 55 |
-
factor = (2 ** apply_to.index(model_chans)) if scale_depth else 1
|
| 56 |
-
nh = random_divisor(h, latent_tile_size * factor, swap_size)
|
| 57 |
-
nw = random_divisor(w, latent_tile_size * factor, swap_size)
|
| 58 |
-
|
| 59 |
-
if nh * nw > 1:
|
| 60 |
-
q = rearrange(q, "b (nh h nw w) c -> (b nh nw) (h w) c", h=h // nh, w=w // nw, nh=nh, nw=nw)
|
| 61 |
-
self.temp = (nh, nw, h, w)
|
| 62 |
-
return q, k, v
|
| 63 |
-
|
| 64 |
-
return q, k, v
|
| 65 |
-
def hypertile_out(out, extra_options):
|
| 66 |
-
if self.temp is not None:
|
| 67 |
-
nh, nw, h, w = self.temp
|
| 68 |
-
self.temp = None
|
| 69 |
-
out = rearrange(out, "(b nh nw) hw c -> b nh nw hw c", nh=nh, nw=nw)
|
| 70 |
-
out = rearrange(out, "b nh nw (h w) c -> b (nh h nw w) c", h=h // nh, w=w // nw)
|
| 71 |
-
return out
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
m = model.clone()
|
| 75 |
-
m.set_model_attn1_patch(hypertile_in)
|
| 76 |
-
m.set_model_attn1_output_patch(hypertile_out)
|
| 77 |
-
return (m, )
|
| 78 |
-
|
| 79 |
-
NODE_CLASS_MAPPINGS = {
|
| 80 |
-
"HyperTile": HyperTile,
|
| 81 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
comfy_extras/nodes_images.py
DELETED
|
@@ -1,642 +0,0 @@
|
|
| 1 |
-
from __future__ import annotations
|
| 2 |
-
|
| 3 |
-
import nodes
|
| 4 |
-
import folder_paths
|
| 5 |
-
from comfy.cli_args import args
|
| 6 |
-
|
| 7 |
-
from PIL import Image
|
| 8 |
-
from PIL.PngImagePlugin import PngInfo
|
| 9 |
-
|
| 10 |
-
import numpy as np
|
| 11 |
-
import json
|
| 12 |
-
import os
|
| 13 |
-
import re
|
| 14 |
-
from io import BytesIO
|
| 15 |
-
from inspect import cleandoc
|
| 16 |
-
import torch
|
| 17 |
-
import comfy.utils
|
| 18 |
-
|
| 19 |
-
from comfy.comfy_types import FileLocator, IO
|
| 20 |
-
from server import PromptServer
|
| 21 |
-
|
| 22 |
-
MAX_RESOLUTION = nodes.MAX_RESOLUTION
|
| 23 |
-
|
| 24 |
-
class ImageCrop:
|
| 25 |
-
@classmethod
|
| 26 |
-
def INPUT_TYPES(s):
|
| 27 |
-
return {"required": { "image": ("IMAGE",),
|
| 28 |
-
"width": ("INT", {"default": 512, "min": 1, "max": MAX_RESOLUTION, "step": 1}),
|
| 29 |
-
"height": ("INT", {"default": 512, "min": 1, "max": MAX_RESOLUTION, "step": 1}),
|
| 30 |
-
"x": ("INT", {"default": 0, "min": 0, "max": MAX_RESOLUTION, "step": 1}),
|
| 31 |
-
"y": ("INT", {"default": 0, "min": 0, "max": MAX_RESOLUTION, "step": 1}),
|
| 32 |
-
}}
|
| 33 |
-
RETURN_TYPES = ("IMAGE",)
|
| 34 |
-
FUNCTION = "crop"
|
| 35 |
-
|
| 36 |
-
CATEGORY = "image/transform"
|
| 37 |
-
|
| 38 |
-
def crop(self, image, width, height, x, y):
|
| 39 |
-
x = min(x, image.shape[2] - 1)
|
| 40 |
-
y = min(y, image.shape[1] - 1)
|
| 41 |
-
to_x = width + x
|
| 42 |
-
to_y = height + y
|
| 43 |
-
img = image[:,y:to_y, x:to_x, :]
|
| 44 |
-
return (img,)
|
| 45 |
-
|
| 46 |
-
class RepeatImageBatch:
|
| 47 |
-
@classmethod
|
| 48 |
-
def INPUT_TYPES(s):
|
| 49 |
-
return {"required": { "image": ("IMAGE",),
|
| 50 |
-
"amount": ("INT", {"default": 1, "min": 1, "max": 4096}),
|
| 51 |
-
}}
|
| 52 |
-
RETURN_TYPES = ("IMAGE",)
|
| 53 |
-
FUNCTION = "repeat"
|
| 54 |
-
|
| 55 |
-
CATEGORY = "image/batch"
|
| 56 |
-
|
| 57 |
-
def repeat(self, image, amount):
|
| 58 |
-
s = image.repeat((amount, 1,1,1))
|
| 59 |
-
return (s,)
|
| 60 |
-
|
| 61 |
-
class ImageFromBatch:
|
| 62 |
-
@classmethod
|
| 63 |
-
def INPUT_TYPES(s):
|
| 64 |
-
return {"required": { "image": ("IMAGE",),
|
| 65 |
-
"batch_index": ("INT", {"default": 0, "min": 0, "max": 4095}),
|
| 66 |
-
"length": ("INT", {"default": 1, "min": 1, "max": 4096}),
|
| 67 |
-
}}
|
| 68 |
-
RETURN_TYPES = ("IMAGE",)
|
| 69 |
-
FUNCTION = "frombatch"
|
| 70 |
-
|
| 71 |
-
CATEGORY = "image/batch"
|
| 72 |
-
|
| 73 |
-
def frombatch(self, image, batch_index, length):
|
| 74 |
-
s_in = image
|
| 75 |
-
batch_index = min(s_in.shape[0] - 1, batch_index)
|
| 76 |
-
length = min(s_in.shape[0] - batch_index, length)
|
| 77 |
-
s = s_in[batch_index:batch_index + length].clone()
|
| 78 |
-
return (s,)
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
class ImageAddNoise:
|
| 82 |
-
@classmethod
|
| 83 |
-
def INPUT_TYPES(s):
|
| 84 |
-
return {"required": { "image": ("IMAGE",),
|
| 85 |
-
"seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff, "control_after_generate": True, "tooltip": "The random seed used for creating the noise."}),
|
| 86 |
-
"strength": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 1.0, "step": 0.01}),
|
| 87 |
-
}}
|
| 88 |
-
RETURN_TYPES = ("IMAGE",)
|
| 89 |
-
FUNCTION = "repeat"
|
| 90 |
-
|
| 91 |
-
CATEGORY = "image"
|
| 92 |
-
|
| 93 |
-
def repeat(self, image, seed, strength):
|
| 94 |
-
generator = torch.manual_seed(seed)
|
| 95 |
-
s = torch.clip((image + strength * torch.randn(image.size(), generator=generator, device="cpu").to(image)), min=0.0, max=1.0)
|
| 96 |
-
return (s,)
|
| 97 |
-
|
| 98 |
-
class SaveAnimatedWEBP:
|
| 99 |
-
def __init__(self):
|
| 100 |
-
self.output_dir = folder_paths.get_output_directory()
|
| 101 |
-
self.type = "output"
|
| 102 |
-
self.prefix_append = ""
|
| 103 |
-
|
| 104 |
-
methods = {"default": 4, "fastest": 0, "slowest": 6}
|
| 105 |
-
@classmethod
|
| 106 |
-
def INPUT_TYPES(s):
|
| 107 |
-
return {"required":
|
| 108 |
-
{"images": ("IMAGE", ),
|
| 109 |
-
"filename_prefix": ("STRING", {"default": "ComfyUI"}),
|
| 110 |
-
"fps": ("FLOAT", {"default": 6.0, "min": 0.01, "max": 1000.0, "step": 0.01}),
|
| 111 |
-
"lossless": ("BOOLEAN", {"default": True}),
|
| 112 |
-
"quality": ("INT", {"default": 80, "min": 0, "max": 100}),
|
| 113 |
-
"method": (list(s.methods.keys()),),
|
| 114 |
-
# "num_frames": ("INT", {"default": 0, "min": 0, "max": 8192}),
|
| 115 |
-
},
|
| 116 |
-
"hidden": {"prompt": "PROMPT", "extra_pnginfo": "EXTRA_PNGINFO"},
|
| 117 |
-
}
|
| 118 |
-
|
| 119 |
-
RETURN_TYPES = ()
|
| 120 |
-
FUNCTION = "save_images"
|
| 121 |
-
|
| 122 |
-
OUTPUT_NODE = True
|
| 123 |
-
|
| 124 |
-
CATEGORY = "image/animation"
|
| 125 |
-
|
| 126 |
-
def save_images(self, images, fps, filename_prefix, lossless, quality, method, num_frames=0, prompt=None, extra_pnginfo=None):
|
| 127 |
-
method = self.methods.get(method)
|
| 128 |
-
filename_prefix += self.prefix_append
|
| 129 |
-
full_output_folder, filename, counter, subfolder, filename_prefix = folder_paths.get_save_image_path(filename_prefix, self.output_dir, images[0].shape[1], images[0].shape[0])
|
| 130 |
-
results: list[FileLocator] = []
|
| 131 |
-
pil_images = []
|
| 132 |
-
for image in images:
|
| 133 |
-
i = 255. * image.cpu().numpy()
|
| 134 |
-
img = Image.fromarray(np.clip(i, 0, 255).astype(np.uint8))
|
| 135 |
-
pil_images.append(img)
|
| 136 |
-
|
| 137 |
-
metadata = pil_images[0].getexif()
|
| 138 |
-
if not args.disable_metadata:
|
| 139 |
-
if prompt is not None:
|
| 140 |
-
metadata[0x0110] = "prompt:{}".format(json.dumps(prompt))
|
| 141 |
-
if extra_pnginfo is not None:
|
| 142 |
-
inital_exif = 0x010f
|
| 143 |
-
for x in extra_pnginfo:
|
| 144 |
-
metadata[inital_exif] = "{}:{}".format(x, json.dumps(extra_pnginfo[x]))
|
| 145 |
-
inital_exif -= 1
|
| 146 |
-
|
| 147 |
-
if num_frames == 0:
|
| 148 |
-
num_frames = len(pil_images)
|
| 149 |
-
|
| 150 |
-
c = len(pil_images)
|
| 151 |
-
for i in range(0, c, num_frames):
|
| 152 |
-
file = f"{filename}_{counter:05}_.webp"
|
| 153 |
-
pil_images[i].save(os.path.join(full_output_folder, file), save_all=True, duration=int(1000.0/fps), append_images=pil_images[i + 1:i + num_frames], exif=metadata, lossless=lossless, quality=quality, method=method)
|
| 154 |
-
results.append({
|
| 155 |
-
"filename": file,
|
| 156 |
-
"subfolder": subfolder,
|
| 157 |
-
"type": self.type
|
| 158 |
-
})
|
| 159 |
-
counter += 1
|
| 160 |
-
|
| 161 |
-
animated = num_frames != 1
|
| 162 |
-
return { "ui": { "images": results, "animated": (animated,) } }
|
| 163 |
-
|
| 164 |
-
class SaveAnimatedPNG:
|
| 165 |
-
def __init__(self):
|
| 166 |
-
self.output_dir = folder_paths.get_output_directory()
|
| 167 |
-
self.type = "output"
|
| 168 |
-
self.prefix_append = ""
|
| 169 |
-
|
| 170 |
-
@classmethod
|
| 171 |
-
def INPUT_TYPES(s):
|
| 172 |
-
return {"required":
|
| 173 |
-
{"images": ("IMAGE", ),
|
| 174 |
-
"filename_prefix": ("STRING", {"default": "ComfyUI"}),
|
| 175 |
-
"fps": ("FLOAT", {"default": 6.0, "min": 0.01, "max": 1000.0, "step": 0.01}),
|
| 176 |
-
"compress_level": ("INT", {"default": 4, "min": 0, "max": 9})
|
| 177 |
-
},
|
| 178 |
-
"hidden": {"prompt": "PROMPT", "extra_pnginfo": "EXTRA_PNGINFO"},
|
| 179 |
-
}
|
| 180 |
-
|
| 181 |
-
RETURN_TYPES = ()
|
| 182 |
-
FUNCTION = "save_images"
|
| 183 |
-
|
| 184 |
-
OUTPUT_NODE = True
|
| 185 |
-
|
| 186 |
-
CATEGORY = "image/animation"
|
| 187 |
-
|
| 188 |
-
def save_images(self, images, fps, compress_level, filename_prefix="ComfyUI", prompt=None, extra_pnginfo=None):
|
| 189 |
-
filename_prefix += self.prefix_append
|
| 190 |
-
full_output_folder, filename, counter, subfolder, filename_prefix = folder_paths.get_save_image_path(filename_prefix, self.output_dir, images[0].shape[1], images[0].shape[0])
|
| 191 |
-
results = list()
|
| 192 |
-
pil_images = []
|
| 193 |
-
for image in images:
|
| 194 |
-
i = 255. * image.cpu().numpy()
|
| 195 |
-
img = Image.fromarray(np.clip(i, 0, 255).astype(np.uint8))
|
| 196 |
-
pil_images.append(img)
|
| 197 |
-
|
| 198 |
-
metadata = None
|
| 199 |
-
if not args.disable_metadata:
|
| 200 |
-
metadata = PngInfo()
|
| 201 |
-
if prompt is not None:
|
| 202 |
-
metadata.add(b"comf", "prompt".encode("latin-1", "strict") + b"\0" + json.dumps(prompt).encode("latin-1", "strict"), after_idat=True)
|
| 203 |
-
if extra_pnginfo is not None:
|
| 204 |
-
for x in extra_pnginfo:
|
| 205 |
-
metadata.add(b"comf", x.encode("latin-1", "strict") + b"\0" + json.dumps(extra_pnginfo[x]).encode("latin-1", "strict"), after_idat=True)
|
| 206 |
-
|
| 207 |
-
file = f"{filename}_{counter:05}_.png"
|
| 208 |
-
pil_images[0].save(os.path.join(full_output_folder, file), pnginfo=metadata, compress_level=compress_level, save_all=True, duration=int(1000.0/fps), append_images=pil_images[1:])
|
| 209 |
-
results.append({
|
| 210 |
-
"filename": file,
|
| 211 |
-
"subfolder": subfolder,
|
| 212 |
-
"type": self.type
|
| 213 |
-
})
|
| 214 |
-
|
| 215 |
-
return { "ui": { "images": results, "animated": (True,)} }
|
| 216 |
-
|
| 217 |
-
class SVG:
|
| 218 |
-
"""
|
| 219 |
-
Stores SVG representations via a list of BytesIO objects.
|
| 220 |
-
"""
|
| 221 |
-
def __init__(self, data: list[BytesIO]):
|
| 222 |
-
self.data = data
|
| 223 |
-
|
| 224 |
-
def combine(self, other: 'SVG') -> 'SVG':
|
| 225 |
-
return SVG(self.data + other.data)
|
| 226 |
-
|
| 227 |
-
@staticmethod
|
| 228 |
-
def combine_all(svgs: list['SVG']) -> 'SVG':
|
| 229 |
-
all_svgs_list: list[BytesIO] = []
|
| 230 |
-
for svg_item in svgs:
|
| 231 |
-
all_svgs_list.extend(svg_item.data)
|
| 232 |
-
return SVG(all_svgs_list)
|
| 233 |
-
|
| 234 |
-
|
| 235 |
-
class ImageStitch:
|
| 236 |
-
"""Upstreamed from https://github.com/kijai/ComfyUI-KJNodes"""
|
| 237 |
-
|
| 238 |
-
@classmethod
|
| 239 |
-
def INPUT_TYPES(s):
|
| 240 |
-
return {
|
| 241 |
-
"required": {
|
| 242 |
-
"image1": ("IMAGE",),
|
| 243 |
-
"direction": (["right", "down", "left", "up"], {"default": "right"}),
|
| 244 |
-
"match_image_size": ("BOOLEAN", {"default": True}),
|
| 245 |
-
"spacing_width": (
|
| 246 |
-
"INT",
|
| 247 |
-
{"default": 0, "min": 0, "max": 1024, "step": 2},
|
| 248 |
-
),
|
| 249 |
-
"spacing_color": (
|
| 250 |
-
["white", "black", "red", "green", "blue"],
|
| 251 |
-
{"default": "white"},
|
| 252 |
-
),
|
| 253 |
-
},
|
| 254 |
-
"optional": {
|
| 255 |
-
"image2": ("IMAGE",),
|
| 256 |
-
},
|
| 257 |
-
}
|
| 258 |
-
|
| 259 |
-
RETURN_TYPES = ("IMAGE",)
|
| 260 |
-
FUNCTION = "stitch"
|
| 261 |
-
CATEGORY = "image/transform"
|
| 262 |
-
DESCRIPTION = """
|
| 263 |
-
Stitches image2 to image1 in the specified direction.
|
| 264 |
-
If image2 is not provided, returns image1 unchanged.
|
| 265 |
-
Optional spacing can be added between images.
|
| 266 |
-
"""
|
| 267 |
-
|
| 268 |
-
def stitch(
|
| 269 |
-
self,
|
| 270 |
-
image1,
|
| 271 |
-
direction,
|
| 272 |
-
match_image_size,
|
| 273 |
-
spacing_width,
|
| 274 |
-
spacing_color,
|
| 275 |
-
image2=None,
|
| 276 |
-
):
|
| 277 |
-
if image2 is None:
|
| 278 |
-
return (image1,)
|
| 279 |
-
|
| 280 |
-
# Handle batch size differences
|
| 281 |
-
if image1.shape[0] != image2.shape[0]:
|
| 282 |
-
max_batch = max(image1.shape[0], image2.shape[0])
|
| 283 |
-
if image1.shape[0] < max_batch:
|
| 284 |
-
image1 = torch.cat(
|
| 285 |
-
[image1, image1[-1:].repeat(max_batch - image1.shape[0], 1, 1, 1)]
|
| 286 |
-
)
|
| 287 |
-
if image2.shape[0] < max_batch:
|
| 288 |
-
image2 = torch.cat(
|
| 289 |
-
[image2, image2[-1:].repeat(max_batch - image2.shape[0], 1, 1, 1)]
|
| 290 |
-
)
|
| 291 |
-
|
| 292 |
-
# Match image sizes if requested
|
| 293 |
-
if match_image_size:
|
| 294 |
-
h1, w1 = image1.shape[1:3]
|
| 295 |
-
h2, w2 = image2.shape[1:3]
|
| 296 |
-
aspect_ratio = w2 / h2
|
| 297 |
-
|
| 298 |
-
if direction in ["left", "right"]:
|
| 299 |
-
target_h, target_w = h1, int(h1 * aspect_ratio)
|
| 300 |
-
else: # up, down
|
| 301 |
-
target_w, target_h = w1, int(w1 / aspect_ratio)
|
| 302 |
-
|
| 303 |
-
image2 = comfy.utils.common_upscale(
|
| 304 |
-
image2.movedim(-1, 1), target_w, target_h, "lanczos", "disabled"
|
| 305 |
-
).movedim(1, -1)
|
| 306 |
-
|
| 307 |
-
color_map = {
|
| 308 |
-
"white": 1.0,
|
| 309 |
-
"black": 0.0,
|
| 310 |
-
"red": (1.0, 0.0, 0.0),
|
| 311 |
-
"green": (0.0, 1.0, 0.0),
|
| 312 |
-
"blue": (0.0, 0.0, 1.0),
|
| 313 |
-
}
|
| 314 |
-
|
| 315 |
-
color_val = color_map[spacing_color]
|
| 316 |
-
|
| 317 |
-
# When not matching sizes, pad to align non-concat dimensions
|
| 318 |
-
if not match_image_size:
|
| 319 |
-
h1, w1 = image1.shape[1:3]
|
| 320 |
-
h2, w2 = image2.shape[1:3]
|
| 321 |
-
pad_value = 0.0
|
| 322 |
-
if not isinstance(color_val, tuple):
|
| 323 |
-
pad_value = color_val
|
| 324 |
-
|
| 325 |
-
if direction in ["left", "right"]:
|
| 326 |
-
# For horizontal concat, pad heights to match
|
| 327 |
-
if h1 != h2:
|
| 328 |
-
target_h = max(h1, h2)
|
| 329 |
-
if h1 < target_h:
|
| 330 |
-
pad_h = target_h - h1
|
| 331 |
-
pad_top, pad_bottom = pad_h // 2, pad_h - pad_h // 2
|
| 332 |
-
image1 = torch.nn.functional.pad(image1, (0, 0, 0, 0, pad_top, pad_bottom), mode='constant', value=pad_value)
|
| 333 |
-
if h2 < target_h:
|
| 334 |
-
pad_h = target_h - h2
|
| 335 |
-
pad_top, pad_bottom = pad_h // 2, pad_h - pad_h // 2
|
| 336 |
-
image2 = torch.nn.functional.pad(image2, (0, 0, 0, 0, pad_top, pad_bottom), mode='constant', value=pad_value)
|
| 337 |
-
else: # up, down
|
| 338 |
-
# For vertical concat, pad widths to match
|
| 339 |
-
if w1 != w2:
|
| 340 |
-
target_w = max(w1, w2)
|
| 341 |
-
if w1 < target_w:
|
| 342 |
-
pad_w = target_w - w1
|
| 343 |
-
pad_left, pad_right = pad_w // 2, pad_w - pad_w // 2
|
| 344 |
-
image1 = torch.nn.functional.pad(image1, (0, 0, pad_left, pad_right), mode='constant', value=pad_value)
|
| 345 |
-
if w2 < target_w:
|
| 346 |
-
pad_w = target_w - w2
|
| 347 |
-
pad_left, pad_right = pad_w // 2, pad_w - pad_w // 2
|
| 348 |
-
image2 = torch.nn.functional.pad(image2, (0, 0, pad_left, pad_right), mode='constant', value=pad_value)
|
| 349 |
-
|
| 350 |
-
# Ensure same number of channels
|
| 351 |
-
if image1.shape[-1] != image2.shape[-1]:
|
| 352 |
-
max_channels = max(image1.shape[-1], image2.shape[-1])
|
| 353 |
-
if image1.shape[-1] < max_channels:
|
| 354 |
-
image1 = torch.cat(
|
| 355 |
-
[
|
| 356 |
-
image1,
|
| 357 |
-
torch.ones(
|
| 358 |
-
*image1.shape[:-1],
|
| 359 |
-
max_channels - image1.shape[-1],
|
| 360 |
-
device=image1.device,
|
| 361 |
-
),
|
| 362 |
-
],
|
| 363 |
-
dim=-1,
|
| 364 |
-
)
|
| 365 |
-
if image2.shape[-1] < max_channels:
|
| 366 |
-
image2 = torch.cat(
|
| 367 |
-
[
|
| 368 |
-
image2,
|
| 369 |
-
torch.ones(
|
| 370 |
-
*image2.shape[:-1],
|
| 371 |
-
max_channels - image2.shape[-1],
|
| 372 |
-
device=image2.device,
|
| 373 |
-
),
|
| 374 |
-
],
|
| 375 |
-
dim=-1,
|
| 376 |
-
)
|
| 377 |
-
|
| 378 |
-
# Add spacing if specified
|
| 379 |
-
if spacing_width > 0:
|
| 380 |
-
spacing_width = spacing_width + (spacing_width % 2) # Ensure even
|
| 381 |
-
|
| 382 |
-
if direction in ["left", "right"]:
|
| 383 |
-
spacing_shape = (
|
| 384 |
-
image1.shape[0],
|
| 385 |
-
max(image1.shape[1], image2.shape[1]),
|
| 386 |
-
spacing_width,
|
| 387 |
-
image1.shape[-1],
|
| 388 |
-
)
|
| 389 |
-
else:
|
| 390 |
-
spacing_shape = (
|
| 391 |
-
image1.shape[0],
|
| 392 |
-
spacing_width,
|
| 393 |
-
max(image1.shape[2], image2.shape[2]),
|
| 394 |
-
image1.shape[-1],
|
| 395 |
-
)
|
| 396 |
-
|
| 397 |
-
spacing = torch.full(spacing_shape, 0.0, device=image1.device)
|
| 398 |
-
if isinstance(color_val, tuple):
|
| 399 |
-
for i, c in enumerate(color_val):
|
| 400 |
-
if i < spacing.shape[-1]:
|
| 401 |
-
spacing[..., i] = c
|
| 402 |
-
if spacing.shape[-1] == 4: # Add alpha
|
| 403 |
-
spacing[..., 3] = 1.0
|
| 404 |
-
else:
|
| 405 |
-
spacing[..., : min(3, spacing.shape[-1])] = color_val
|
| 406 |
-
if spacing.shape[-1] == 4:
|
| 407 |
-
spacing[..., 3] = 1.0
|
| 408 |
-
|
| 409 |
-
# Concatenate images
|
| 410 |
-
images = [image2, image1] if direction in ["left", "up"] else [image1, image2]
|
| 411 |
-
if spacing_width > 0:
|
| 412 |
-
images.insert(1, spacing)
|
| 413 |
-
|
| 414 |
-
concat_dim = 2 if direction in ["left", "right"] else 1
|
| 415 |
-
return (torch.cat(images, dim=concat_dim),)
|
| 416 |
-
|
| 417 |
-
class ResizeAndPadImage:
|
| 418 |
-
@classmethod
|
| 419 |
-
def INPUT_TYPES(cls):
|
| 420 |
-
return {
|
| 421 |
-
"required": {
|
| 422 |
-
"image": ("IMAGE",),
|
| 423 |
-
"target_width": ("INT", {
|
| 424 |
-
"default": 512,
|
| 425 |
-
"min": 1,
|
| 426 |
-
"max": MAX_RESOLUTION,
|
| 427 |
-
"step": 1
|
| 428 |
-
}),
|
| 429 |
-
"target_height": ("INT", {
|
| 430 |
-
"default": 512,
|
| 431 |
-
"min": 1,
|
| 432 |
-
"max": MAX_RESOLUTION,
|
| 433 |
-
"step": 1
|
| 434 |
-
}),
|
| 435 |
-
"padding_color": (["white", "black"],),
|
| 436 |
-
"interpolation": (["area", "bicubic", "nearest-exact", "bilinear", "lanczos"],),
|
| 437 |
-
}
|
| 438 |
-
}
|
| 439 |
-
|
| 440 |
-
RETURN_TYPES = ("IMAGE",)
|
| 441 |
-
FUNCTION = "resize_and_pad"
|
| 442 |
-
CATEGORY = "image/transform"
|
| 443 |
-
|
| 444 |
-
def resize_and_pad(self, image, target_width, target_height, padding_color, interpolation):
|
| 445 |
-
batch_size, orig_height, orig_width, channels = image.shape
|
| 446 |
-
|
| 447 |
-
scale_w = target_width / orig_width
|
| 448 |
-
scale_h = target_height / orig_height
|
| 449 |
-
scale = min(scale_w, scale_h)
|
| 450 |
-
|
| 451 |
-
new_width = int(orig_width * scale)
|
| 452 |
-
new_height = int(orig_height * scale)
|
| 453 |
-
|
| 454 |
-
image_permuted = image.permute(0, 3, 1, 2)
|
| 455 |
-
|
| 456 |
-
resized = comfy.utils.common_upscale(image_permuted, new_width, new_height, interpolation, "disabled")
|
| 457 |
-
|
| 458 |
-
pad_value = 0.0 if padding_color == "black" else 1.0
|
| 459 |
-
padded = torch.full(
|
| 460 |
-
(batch_size, channels, target_height, target_width),
|
| 461 |
-
pad_value,
|
| 462 |
-
dtype=image.dtype,
|
| 463 |
-
device=image.device
|
| 464 |
-
)
|
| 465 |
-
|
| 466 |
-
y_offset = (target_height - new_height) // 2
|
| 467 |
-
x_offset = (target_width - new_width) // 2
|
| 468 |
-
|
| 469 |
-
padded[:, :, y_offset:y_offset + new_height, x_offset:x_offset + new_width] = resized
|
| 470 |
-
|
| 471 |
-
output = padded.permute(0, 2, 3, 1)
|
| 472 |
-
return (output,)
|
| 473 |
-
|
| 474 |
-
class SaveSVGNode:
|
| 475 |
-
"""
|
| 476 |
-
Save SVG files on disk.
|
| 477 |
-
"""
|
| 478 |
-
|
| 479 |
-
def __init__(self):
|
| 480 |
-
self.output_dir = folder_paths.get_output_directory()
|
| 481 |
-
self.type = "output"
|
| 482 |
-
self.prefix_append = ""
|
| 483 |
-
|
| 484 |
-
RETURN_TYPES = ()
|
| 485 |
-
DESCRIPTION = cleandoc(__doc__ or "") # Handle potential None value
|
| 486 |
-
FUNCTION = "save_svg"
|
| 487 |
-
CATEGORY = "image/save" # Changed
|
| 488 |
-
OUTPUT_NODE = True
|
| 489 |
-
|
| 490 |
-
@classmethod
|
| 491 |
-
def INPUT_TYPES(s):
|
| 492 |
-
return {
|
| 493 |
-
"required": {
|
| 494 |
-
"svg": ("SVG",), # Changed
|
| 495 |
-
"filename_prefix": ("STRING", {"default": "svg/ComfyUI", "tooltip": "The prefix for the file to save. This may include formatting information such as %date:yyyy-MM-dd% or %Empty Latent Image.width% to include values from nodes."})
|
| 496 |
-
},
|
| 497 |
-
"hidden": {
|
| 498 |
-
"prompt": "PROMPT",
|
| 499 |
-
"extra_pnginfo": "EXTRA_PNGINFO"
|
| 500 |
-
}
|
| 501 |
-
}
|
| 502 |
-
|
| 503 |
-
def save_svg(self, svg: SVG, filename_prefix="svg/ComfyUI", prompt=None, extra_pnginfo=None):
|
| 504 |
-
filename_prefix += self.prefix_append
|
| 505 |
-
full_output_folder, filename, counter, subfolder, filename_prefix = folder_paths.get_save_image_path(filename_prefix, self.output_dir)
|
| 506 |
-
results = list()
|
| 507 |
-
|
| 508 |
-
# Prepare metadata JSON
|
| 509 |
-
metadata_dict = {}
|
| 510 |
-
if prompt is not None:
|
| 511 |
-
metadata_dict["prompt"] = prompt
|
| 512 |
-
if extra_pnginfo is not None:
|
| 513 |
-
metadata_dict.update(extra_pnginfo)
|
| 514 |
-
|
| 515 |
-
# Convert metadata to JSON string
|
| 516 |
-
metadata_json = json.dumps(metadata_dict, indent=2) if metadata_dict else None
|
| 517 |
-
|
| 518 |
-
for batch_number, svg_bytes in enumerate(svg.data):
|
| 519 |
-
filename_with_batch_num = filename.replace("%batch_num%", str(batch_number))
|
| 520 |
-
file = f"{filename_with_batch_num}_{counter:05}_.svg"
|
| 521 |
-
|
| 522 |
-
# Read SVG content
|
| 523 |
-
svg_bytes.seek(0)
|
| 524 |
-
svg_content = svg_bytes.read().decode('utf-8')
|
| 525 |
-
|
| 526 |
-
# Inject metadata if available
|
| 527 |
-
if metadata_json:
|
| 528 |
-
# Create metadata element with CDATA section
|
| 529 |
-
metadata_element = f""" <metadata>
|
| 530 |
-
<![CDATA[
|
| 531 |
-
{metadata_json}
|
| 532 |
-
]]>
|
| 533 |
-
</metadata>
|
| 534 |
-
"""
|
| 535 |
-
# Insert metadata after opening svg tag using regex with a replacement function
|
| 536 |
-
def replacement(match):
|
| 537 |
-
# match.group(1) contains the captured <svg> tag
|
| 538 |
-
return match.group(1) + '\n' + metadata_element
|
| 539 |
-
|
| 540 |
-
# Apply the substitution
|
| 541 |
-
svg_content = re.sub(r'(<svg[^>]*>)', replacement, svg_content, flags=re.UNICODE)
|
| 542 |
-
|
| 543 |
-
# Write the modified SVG to file
|
| 544 |
-
with open(os.path.join(full_output_folder, file), 'wb') as svg_file:
|
| 545 |
-
svg_file.write(svg_content.encode('utf-8'))
|
| 546 |
-
|
| 547 |
-
results.append({
|
| 548 |
-
"filename": file,
|
| 549 |
-
"subfolder": subfolder,
|
| 550 |
-
"type": self.type
|
| 551 |
-
})
|
| 552 |
-
counter += 1
|
| 553 |
-
return { "ui": { "images": results } }
|
| 554 |
-
|
| 555 |
-
class GetImageSize:
|
| 556 |
-
|
| 557 |
-
@classmethod
|
| 558 |
-
def INPUT_TYPES(s):
|
| 559 |
-
return {
|
| 560 |
-
"required": {
|
| 561 |
-
"image": (IO.IMAGE,),
|
| 562 |
-
},
|
| 563 |
-
"hidden": {
|
| 564 |
-
"unique_id": "UNIQUE_ID",
|
| 565 |
-
}
|
| 566 |
-
}
|
| 567 |
-
|
| 568 |
-
RETURN_TYPES = (IO.INT, IO.INT, IO.INT)
|
| 569 |
-
RETURN_NAMES = ("width", "height", "batch_size")
|
| 570 |
-
FUNCTION = "get_size"
|
| 571 |
-
|
| 572 |
-
CATEGORY = "image"
|
| 573 |
-
DESCRIPTION = """Returns width and height of the image, and passes it through unchanged."""
|
| 574 |
-
|
| 575 |
-
def get_size(self, image, unique_id=None) -> tuple[int, int]:
|
| 576 |
-
height = image.shape[1]
|
| 577 |
-
width = image.shape[2]
|
| 578 |
-
batch_size = image.shape[0]
|
| 579 |
-
|
| 580 |
-
# Send progress text to display size on the node
|
| 581 |
-
if unique_id:
|
| 582 |
-
PromptServer.instance.send_progress_text(f"width: {width}, height: {height}\n batch size: {batch_size}", unique_id)
|
| 583 |
-
|
| 584 |
-
return width, height, batch_size
|
| 585 |
-
|
| 586 |
-
class ImageRotate:
|
| 587 |
-
@classmethod
|
| 588 |
-
def INPUT_TYPES(s):
|
| 589 |
-
return {"required": { "image": (IO.IMAGE,),
|
| 590 |
-
"rotation": (["none", "90 degrees", "180 degrees", "270 degrees"],),
|
| 591 |
-
}}
|
| 592 |
-
RETURN_TYPES = (IO.IMAGE,)
|
| 593 |
-
FUNCTION = "rotate"
|
| 594 |
-
|
| 595 |
-
CATEGORY = "image/transform"
|
| 596 |
-
|
| 597 |
-
def rotate(self, image, rotation):
|
| 598 |
-
rotate_by = 0
|
| 599 |
-
if rotation.startswith("90"):
|
| 600 |
-
rotate_by = 1
|
| 601 |
-
elif rotation.startswith("180"):
|
| 602 |
-
rotate_by = 2
|
| 603 |
-
elif rotation.startswith("270"):
|
| 604 |
-
rotate_by = 3
|
| 605 |
-
|
| 606 |
-
image = torch.rot90(image, k=rotate_by, dims=[2, 1])
|
| 607 |
-
return (image,)
|
| 608 |
-
|
| 609 |
-
class ImageFlip:
|
| 610 |
-
@classmethod
|
| 611 |
-
def INPUT_TYPES(s):
|
| 612 |
-
return {"required": { "image": (IO.IMAGE,),
|
| 613 |
-
"flip_method": (["x-axis: vertically", "y-axis: horizontally"],),
|
| 614 |
-
}}
|
| 615 |
-
RETURN_TYPES = (IO.IMAGE,)
|
| 616 |
-
FUNCTION = "flip"
|
| 617 |
-
|
| 618 |
-
CATEGORY = "image/transform"
|
| 619 |
-
|
| 620 |
-
def flip(self, image, flip_method):
|
| 621 |
-
if flip_method.startswith("x"):
|
| 622 |
-
image = torch.flip(image, dims=[1])
|
| 623 |
-
elif flip_method.startswith("y"):
|
| 624 |
-
image = torch.flip(image, dims=[2])
|
| 625 |
-
|
| 626 |
-
return (image,)
|
| 627 |
-
|
| 628 |
-
|
| 629 |
-
NODE_CLASS_MAPPINGS = {
|
| 630 |
-
"ImageCrop": ImageCrop,
|
| 631 |
-
"RepeatImageBatch": RepeatImageBatch,
|
| 632 |
-
"ImageFromBatch": ImageFromBatch,
|
| 633 |
-
"ImageAddNoise": ImageAddNoise,
|
| 634 |
-
"SaveAnimatedWEBP": SaveAnimatedWEBP,
|
| 635 |
-
"SaveAnimatedPNG": SaveAnimatedPNG,
|
| 636 |
-
"SaveSVGNode": SaveSVGNode,
|
| 637 |
-
"ImageStitch": ImageStitch,
|
| 638 |
-
"ResizeAndPadImage": ResizeAndPadImage,
|
| 639 |
-
"GetImageSize": GetImageSize,
|
| 640 |
-
"ImageRotate": ImageRotate,
|
| 641 |
-
"ImageFlip": ImageFlip,
|
| 642 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
comfy_extras/nodes_ip2p.py
DELETED
|
@@ -1,45 +0,0 @@
|
|
| 1 |
-
import torch
|
| 2 |
-
|
| 3 |
-
class InstructPixToPixConditioning:
|
| 4 |
-
@classmethod
|
| 5 |
-
def INPUT_TYPES(s):
|
| 6 |
-
return {"required": {"positive": ("CONDITIONING", ),
|
| 7 |
-
"negative": ("CONDITIONING", ),
|
| 8 |
-
"vae": ("VAE", ),
|
| 9 |
-
"pixels": ("IMAGE", ),
|
| 10 |
-
}}
|
| 11 |
-
|
| 12 |
-
RETURN_TYPES = ("CONDITIONING","CONDITIONING","LATENT")
|
| 13 |
-
RETURN_NAMES = ("positive", "negative", "latent")
|
| 14 |
-
FUNCTION = "encode"
|
| 15 |
-
|
| 16 |
-
CATEGORY = "conditioning/instructpix2pix"
|
| 17 |
-
|
| 18 |
-
def encode(self, positive, negative, pixels, vae):
|
| 19 |
-
x = (pixels.shape[1] // 8) * 8
|
| 20 |
-
y = (pixels.shape[2] // 8) * 8
|
| 21 |
-
|
| 22 |
-
if pixels.shape[1] != x or pixels.shape[2] != y:
|
| 23 |
-
x_offset = (pixels.shape[1] % 8) // 2
|
| 24 |
-
y_offset = (pixels.shape[2] % 8) // 2
|
| 25 |
-
pixels = pixels[:,x_offset:x + x_offset, y_offset:y + y_offset,:]
|
| 26 |
-
|
| 27 |
-
concat_latent = vae.encode(pixels)
|
| 28 |
-
|
| 29 |
-
out_latent = {}
|
| 30 |
-
out_latent["samples"] = torch.zeros_like(concat_latent)
|
| 31 |
-
|
| 32 |
-
out = []
|
| 33 |
-
for conditioning in [positive, negative]:
|
| 34 |
-
c = []
|
| 35 |
-
for t in conditioning:
|
| 36 |
-
d = t[1].copy()
|
| 37 |
-
d["concat_latent_image"] = concat_latent
|
| 38 |
-
n = [t[0], d]
|
| 39 |
-
c.append(n)
|
| 40 |
-
out.append(c)
|
| 41 |
-
return (out[0], out[1], out_latent)
|
| 42 |
-
|
| 43 |
-
NODE_CLASS_MAPPINGS = {
|
| 44 |
-
"InstructPixToPixConditioning": InstructPixToPixConditioning,
|
| 45 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
comfy_extras/nodes_latent.py
DELETED
|
@@ -1,288 +0,0 @@
|
|
| 1 |
-
import comfy.utils
|
| 2 |
-
import comfy_extras.nodes_post_processing
|
| 3 |
-
import torch
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
def reshape_latent_to(target_shape, latent, repeat_batch=True):
|
| 7 |
-
if latent.shape[1:] != target_shape[1:]:
|
| 8 |
-
latent = comfy.utils.common_upscale(latent, target_shape[-1], target_shape[-2], "bilinear", "center")
|
| 9 |
-
if repeat_batch:
|
| 10 |
-
return comfy.utils.repeat_to_batch_size(latent, target_shape[0])
|
| 11 |
-
else:
|
| 12 |
-
return latent
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
class LatentAdd:
|
| 16 |
-
@classmethod
|
| 17 |
-
def INPUT_TYPES(s):
|
| 18 |
-
return {"required": { "samples1": ("LATENT",), "samples2": ("LATENT",)}}
|
| 19 |
-
|
| 20 |
-
RETURN_TYPES = ("LATENT",)
|
| 21 |
-
FUNCTION = "op"
|
| 22 |
-
|
| 23 |
-
CATEGORY = "latent/advanced"
|
| 24 |
-
|
| 25 |
-
def op(self, samples1, samples2):
|
| 26 |
-
samples_out = samples1.copy()
|
| 27 |
-
|
| 28 |
-
s1 = samples1["samples"]
|
| 29 |
-
s2 = samples2["samples"]
|
| 30 |
-
|
| 31 |
-
s2 = reshape_latent_to(s1.shape, s2)
|
| 32 |
-
samples_out["samples"] = s1 + s2
|
| 33 |
-
return (samples_out,)
|
| 34 |
-
|
| 35 |
-
class LatentSubtract:
|
| 36 |
-
@classmethod
|
| 37 |
-
def INPUT_TYPES(s):
|
| 38 |
-
return {"required": { "samples1": ("LATENT",), "samples2": ("LATENT",)}}
|
| 39 |
-
|
| 40 |
-
RETURN_TYPES = ("LATENT",)
|
| 41 |
-
FUNCTION = "op"
|
| 42 |
-
|
| 43 |
-
CATEGORY = "latent/advanced"
|
| 44 |
-
|
| 45 |
-
def op(self, samples1, samples2):
|
| 46 |
-
samples_out = samples1.copy()
|
| 47 |
-
|
| 48 |
-
s1 = samples1["samples"]
|
| 49 |
-
s2 = samples2["samples"]
|
| 50 |
-
|
| 51 |
-
s2 = reshape_latent_to(s1.shape, s2)
|
| 52 |
-
samples_out["samples"] = s1 - s2
|
| 53 |
-
return (samples_out,)
|
| 54 |
-
|
| 55 |
-
class LatentMultiply:
|
| 56 |
-
@classmethod
|
| 57 |
-
def INPUT_TYPES(s):
|
| 58 |
-
return {"required": { "samples": ("LATENT",),
|
| 59 |
-
"multiplier": ("FLOAT", {"default": 1.0, "min": -10.0, "max": 10.0, "step": 0.01}),
|
| 60 |
-
}}
|
| 61 |
-
|
| 62 |
-
RETURN_TYPES = ("LATENT",)
|
| 63 |
-
FUNCTION = "op"
|
| 64 |
-
|
| 65 |
-
CATEGORY = "latent/advanced"
|
| 66 |
-
|
| 67 |
-
def op(self, samples, multiplier):
|
| 68 |
-
samples_out = samples.copy()
|
| 69 |
-
|
| 70 |
-
s1 = samples["samples"]
|
| 71 |
-
samples_out["samples"] = s1 * multiplier
|
| 72 |
-
return (samples_out,)
|
| 73 |
-
|
| 74 |
-
class LatentInterpolate:
|
| 75 |
-
@classmethod
|
| 76 |
-
def INPUT_TYPES(s):
|
| 77 |
-
return {"required": { "samples1": ("LATENT",),
|
| 78 |
-
"samples2": ("LATENT",),
|
| 79 |
-
"ratio": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}),
|
| 80 |
-
}}
|
| 81 |
-
|
| 82 |
-
RETURN_TYPES = ("LATENT",)
|
| 83 |
-
FUNCTION = "op"
|
| 84 |
-
|
| 85 |
-
CATEGORY = "latent/advanced"
|
| 86 |
-
|
| 87 |
-
def op(self, samples1, samples2, ratio):
|
| 88 |
-
samples_out = samples1.copy()
|
| 89 |
-
|
| 90 |
-
s1 = samples1["samples"]
|
| 91 |
-
s2 = samples2["samples"]
|
| 92 |
-
|
| 93 |
-
s2 = reshape_latent_to(s1.shape, s2)
|
| 94 |
-
|
| 95 |
-
m1 = torch.linalg.vector_norm(s1, dim=(1))
|
| 96 |
-
m2 = torch.linalg.vector_norm(s2, dim=(1))
|
| 97 |
-
|
| 98 |
-
s1 = torch.nan_to_num(s1 / m1)
|
| 99 |
-
s2 = torch.nan_to_num(s2 / m2)
|
| 100 |
-
|
| 101 |
-
t = (s1 * ratio + s2 * (1.0 - ratio))
|
| 102 |
-
mt = torch.linalg.vector_norm(t, dim=(1))
|
| 103 |
-
st = torch.nan_to_num(t / mt)
|
| 104 |
-
|
| 105 |
-
samples_out["samples"] = st * (m1 * ratio + m2 * (1.0 - ratio))
|
| 106 |
-
return (samples_out,)
|
| 107 |
-
|
| 108 |
-
class LatentBatch:
|
| 109 |
-
@classmethod
|
| 110 |
-
def INPUT_TYPES(s):
|
| 111 |
-
return {"required": { "samples1": ("LATENT",), "samples2": ("LATENT",)}}
|
| 112 |
-
|
| 113 |
-
RETURN_TYPES = ("LATENT",)
|
| 114 |
-
FUNCTION = "batch"
|
| 115 |
-
|
| 116 |
-
CATEGORY = "latent/batch"
|
| 117 |
-
|
| 118 |
-
def batch(self, samples1, samples2):
|
| 119 |
-
samples_out = samples1.copy()
|
| 120 |
-
s1 = samples1["samples"]
|
| 121 |
-
s2 = samples2["samples"]
|
| 122 |
-
|
| 123 |
-
s2 = reshape_latent_to(s1.shape, s2, repeat_batch=False)
|
| 124 |
-
s = torch.cat((s1, s2), dim=0)
|
| 125 |
-
samples_out["samples"] = s
|
| 126 |
-
samples_out["batch_index"] = samples1.get("batch_index", [x for x in range(0, s1.shape[0])]) + samples2.get("batch_index", [x for x in range(0, s2.shape[0])])
|
| 127 |
-
return (samples_out,)
|
| 128 |
-
|
| 129 |
-
class LatentBatchSeedBehavior:
|
| 130 |
-
@classmethod
|
| 131 |
-
def INPUT_TYPES(s):
|
| 132 |
-
return {"required": { "samples": ("LATENT",),
|
| 133 |
-
"seed_behavior": (["random", "fixed"],{"default": "fixed"}),}}
|
| 134 |
-
|
| 135 |
-
RETURN_TYPES = ("LATENT",)
|
| 136 |
-
FUNCTION = "op"
|
| 137 |
-
|
| 138 |
-
CATEGORY = "latent/advanced"
|
| 139 |
-
|
| 140 |
-
def op(self, samples, seed_behavior):
|
| 141 |
-
samples_out = samples.copy()
|
| 142 |
-
latent = samples["samples"]
|
| 143 |
-
if seed_behavior == "random":
|
| 144 |
-
if 'batch_index' in samples_out:
|
| 145 |
-
samples_out.pop('batch_index')
|
| 146 |
-
elif seed_behavior == "fixed":
|
| 147 |
-
batch_number = samples_out.get("batch_index", [0])[0]
|
| 148 |
-
samples_out["batch_index"] = [batch_number] * latent.shape[0]
|
| 149 |
-
|
| 150 |
-
return (samples_out,)
|
| 151 |
-
|
| 152 |
-
class LatentApplyOperation:
|
| 153 |
-
@classmethod
|
| 154 |
-
def INPUT_TYPES(s):
|
| 155 |
-
return {"required": { "samples": ("LATENT",),
|
| 156 |
-
"operation": ("LATENT_OPERATION",),
|
| 157 |
-
}}
|
| 158 |
-
|
| 159 |
-
RETURN_TYPES = ("LATENT",)
|
| 160 |
-
FUNCTION = "op"
|
| 161 |
-
|
| 162 |
-
CATEGORY = "latent/advanced/operations"
|
| 163 |
-
EXPERIMENTAL = True
|
| 164 |
-
|
| 165 |
-
def op(self, samples, operation):
|
| 166 |
-
samples_out = samples.copy()
|
| 167 |
-
|
| 168 |
-
s1 = samples["samples"]
|
| 169 |
-
samples_out["samples"] = operation(latent=s1)
|
| 170 |
-
return (samples_out,)
|
| 171 |
-
|
| 172 |
-
class LatentApplyOperationCFG:
|
| 173 |
-
@classmethod
|
| 174 |
-
def INPUT_TYPES(s):
|
| 175 |
-
return {"required": { "model": ("MODEL",),
|
| 176 |
-
"operation": ("LATENT_OPERATION",),
|
| 177 |
-
}}
|
| 178 |
-
RETURN_TYPES = ("MODEL",)
|
| 179 |
-
FUNCTION = "patch"
|
| 180 |
-
|
| 181 |
-
CATEGORY = "latent/advanced/operations"
|
| 182 |
-
EXPERIMENTAL = True
|
| 183 |
-
|
| 184 |
-
def patch(self, model, operation):
|
| 185 |
-
m = model.clone()
|
| 186 |
-
|
| 187 |
-
def pre_cfg_function(args):
|
| 188 |
-
conds_out = args["conds_out"]
|
| 189 |
-
if len(conds_out) == 2:
|
| 190 |
-
conds_out[0] = operation(latent=(conds_out[0] - conds_out[1])) + conds_out[1]
|
| 191 |
-
else:
|
| 192 |
-
conds_out[0] = operation(latent=conds_out[0])
|
| 193 |
-
return conds_out
|
| 194 |
-
|
| 195 |
-
m.set_model_sampler_pre_cfg_function(pre_cfg_function)
|
| 196 |
-
return (m, )
|
| 197 |
-
|
| 198 |
-
class LatentOperationTonemapReinhard:
|
| 199 |
-
@classmethod
|
| 200 |
-
def INPUT_TYPES(s):
|
| 201 |
-
return {"required": { "multiplier": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 100.0, "step": 0.01}),
|
| 202 |
-
}}
|
| 203 |
-
|
| 204 |
-
RETURN_TYPES = ("LATENT_OPERATION",)
|
| 205 |
-
FUNCTION = "op"
|
| 206 |
-
|
| 207 |
-
CATEGORY = "latent/advanced/operations"
|
| 208 |
-
EXPERIMENTAL = True
|
| 209 |
-
|
| 210 |
-
def op(self, multiplier):
|
| 211 |
-
def tonemap_reinhard(latent, **kwargs):
|
| 212 |
-
latent_vector_magnitude = (torch.linalg.vector_norm(latent, dim=(1)) + 0.0000000001)[:,None]
|
| 213 |
-
normalized_latent = latent / latent_vector_magnitude
|
| 214 |
-
|
| 215 |
-
mean = torch.mean(latent_vector_magnitude, dim=(1,2,3), keepdim=True)
|
| 216 |
-
std = torch.std(latent_vector_magnitude, dim=(1,2,3), keepdim=True)
|
| 217 |
-
|
| 218 |
-
top = (std * 5 + mean) * multiplier
|
| 219 |
-
|
| 220 |
-
#reinhard
|
| 221 |
-
latent_vector_magnitude *= (1.0 / top)
|
| 222 |
-
new_magnitude = latent_vector_magnitude / (latent_vector_magnitude + 1.0)
|
| 223 |
-
new_magnitude *= top
|
| 224 |
-
|
| 225 |
-
return normalized_latent * new_magnitude
|
| 226 |
-
return (tonemap_reinhard,)
|
| 227 |
-
|
| 228 |
-
class LatentOperationSharpen:
|
| 229 |
-
@classmethod
|
| 230 |
-
def INPUT_TYPES(s):
|
| 231 |
-
return {"required": {
|
| 232 |
-
"sharpen_radius": ("INT", {
|
| 233 |
-
"default": 9,
|
| 234 |
-
"min": 1,
|
| 235 |
-
"max": 31,
|
| 236 |
-
"step": 1
|
| 237 |
-
}),
|
| 238 |
-
"sigma": ("FLOAT", {
|
| 239 |
-
"default": 1.0,
|
| 240 |
-
"min": 0.1,
|
| 241 |
-
"max": 10.0,
|
| 242 |
-
"step": 0.1
|
| 243 |
-
}),
|
| 244 |
-
"alpha": ("FLOAT", {
|
| 245 |
-
"default": 0.1,
|
| 246 |
-
"min": 0.0,
|
| 247 |
-
"max": 5.0,
|
| 248 |
-
"step": 0.01
|
| 249 |
-
}),
|
| 250 |
-
}}
|
| 251 |
-
|
| 252 |
-
RETURN_TYPES = ("LATENT_OPERATION",)
|
| 253 |
-
FUNCTION = "op"
|
| 254 |
-
|
| 255 |
-
CATEGORY = "latent/advanced/operations"
|
| 256 |
-
EXPERIMENTAL = True
|
| 257 |
-
|
| 258 |
-
def op(self, sharpen_radius, sigma, alpha):
|
| 259 |
-
def sharpen(latent, **kwargs):
|
| 260 |
-
luminance = (torch.linalg.vector_norm(latent, dim=(1)) + 1e-6)[:,None]
|
| 261 |
-
normalized_latent = latent / luminance
|
| 262 |
-
channels = latent.shape[1]
|
| 263 |
-
|
| 264 |
-
kernel_size = sharpen_radius * 2 + 1
|
| 265 |
-
kernel = comfy_extras.nodes_post_processing.gaussian_kernel(kernel_size, sigma, device=luminance.device)
|
| 266 |
-
center = kernel_size // 2
|
| 267 |
-
|
| 268 |
-
kernel *= alpha * -10
|
| 269 |
-
kernel[center, center] = kernel[center, center] - kernel.sum() + 1.0
|
| 270 |
-
|
| 271 |
-
padded_image = torch.nn.functional.pad(normalized_latent, (sharpen_radius,sharpen_radius,sharpen_radius,sharpen_radius), 'reflect')
|
| 272 |
-
sharpened = torch.nn.functional.conv2d(padded_image, kernel.repeat(channels, 1, 1).unsqueeze(1), padding=kernel_size // 2, groups=channels)[:,:,sharpen_radius:-sharpen_radius, sharpen_radius:-sharpen_radius]
|
| 273 |
-
|
| 274 |
-
return luminance * sharpened
|
| 275 |
-
return (sharpen,)
|
| 276 |
-
|
| 277 |
-
NODE_CLASS_MAPPINGS = {
|
| 278 |
-
"LatentAdd": LatentAdd,
|
| 279 |
-
"LatentSubtract": LatentSubtract,
|
| 280 |
-
"LatentMultiply": LatentMultiply,
|
| 281 |
-
"LatentInterpolate": LatentInterpolate,
|
| 282 |
-
"LatentBatch": LatentBatch,
|
| 283 |
-
"LatentBatchSeedBehavior": LatentBatchSeedBehavior,
|
| 284 |
-
"LatentApplyOperation": LatentApplyOperation,
|
| 285 |
-
"LatentApplyOperationCFG": LatentApplyOperationCFG,
|
| 286 |
-
"LatentOperationTonemapReinhard": LatentOperationTonemapReinhard,
|
| 287 |
-
"LatentOperationSharpen": LatentOperationSharpen,
|
| 288 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
comfy_extras/nodes_load_3d.py
DELETED
|
@@ -1,182 +0,0 @@
|
|
| 1 |
-
import nodes
|
| 2 |
-
import folder_paths
|
| 3 |
-
import os
|
| 4 |
-
|
| 5 |
-
from comfy.comfy_types import IO
|
| 6 |
-
from comfy_api.input_impl import VideoFromFile
|
| 7 |
-
|
| 8 |
-
from pathlib import Path
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
def normalize_path(path):
|
| 12 |
-
return path.replace('\\', '/')
|
| 13 |
-
|
| 14 |
-
class Load3D():
|
| 15 |
-
@classmethod
|
| 16 |
-
def INPUT_TYPES(s):
|
| 17 |
-
input_dir = os.path.join(folder_paths.get_input_directory(), "3d")
|
| 18 |
-
|
| 19 |
-
os.makedirs(input_dir, exist_ok=True)
|
| 20 |
-
|
| 21 |
-
input_path = Path(input_dir)
|
| 22 |
-
base_path = Path(folder_paths.get_input_directory())
|
| 23 |
-
|
| 24 |
-
files = [
|
| 25 |
-
normalize_path(str(file_path.relative_to(base_path)))
|
| 26 |
-
for file_path in input_path.rglob("*")
|
| 27 |
-
if file_path.suffix.lower() in {'.gltf', '.glb', '.obj', '.fbx', '.stl'}
|
| 28 |
-
]
|
| 29 |
-
|
| 30 |
-
return {"required": {
|
| 31 |
-
"model_file": (sorted(files), {"file_upload": True}),
|
| 32 |
-
"image": ("LOAD_3D", {}),
|
| 33 |
-
"width": ("INT", {"default": 1024, "min": 1, "max": 4096, "step": 1}),
|
| 34 |
-
"height": ("INT", {"default": 1024, "min": 1, "max": 4096, "step": 1}),
|
| 35 |
-
}}
|
| 36 |
-
|
| 37 |
-
RETURN_TYPES = ("IMAGE", "MASK", "STRING", "IMAGE", "IMAGE", "LOAD3D_CAMERA", IO.VIDEO)
|
| 38 |
-
RETURN_NAMES = ("image", "mask", "mesh_path", "normal", "lineart", "camera_info", "recording_video")
|
| 39 |
-
|
| 40 |
-
FUNCTION = "process"
|
| 41 |
-
EXPERIMENTAL = True
|
| 42 |
-
|
| 43 |
-
CATEGORY = "3d"
|
| 44 |
-
|
| 45 |
-
def process(self, model_file, image, **kwargs):
|
| 46 |
-
image_path = folder_paths.get_annotated_filepath(image['image'])
|
| 47 |
-
mask_path = folder_paths.get_annotated_filepath(image['mask'])
|
| 48 |
-
normal_path = folder_paths.get_annotated_filepath(image['normal'])
|
| 49 |
-
lineart_path = folder_paths.get_annotated_filepath(image['lineart'])
|
| 50 |
-
|
| 51 |
-
load_image_node = nodes.LoadImage()
|
| 52 |
-
output_image, ignore_mask = load_image_node.load_image(image=image_path)
|
| 53 |
-
ignore_image, output_mask = load_image_node.load_image(image=mask_path)
|
| 54 |
-
normal_image, ignore_mask2 = load_image_node.load_image(image=normal_path)
|
| 55 |
-
lineart_image, ignore_mask3 = load_image_node.load_image(image=lineart_path)
|
| 56 |
-
|
| 57 |
-
video = None
|
| 58 |
-
|
| 59 |
-
if image['recording'] != "":
|
| 60 |
-
recording_video_path = folder_paths.get_annotated_filepath(image['recording'])
|
| 61 |
-
|
| 62 |
-
video = VideoFromFile(recording_video_path)
|
| 63 |
-
|
| 64 |
-
return output_image, output_mask, model_file, normal_image, lineart_image, image['camera_info'], video
|
| 65 |
-
|
| 66 |
-
class Load3DAnimation():
|
| 67 |
-
@classmethod
|
| 68 |
-
def INPUT_TYPES(s):
|
| 69 |
-
input_dir = os.path.join(folder_paths.get_input_directory(), "3d")
|
| 70 |
-
|
| 71 |
-
os.makedirs(input_dir, exist_ok=True)
|
| 72 |
-
|
| 73 |
-
input_path = Path(input_dir)
|
| 74 |
-
base_path = Path(folder_paths.get_input_directory())
|
| 75 |
-
|
| 76 |
-
files = [
|
| 77 |
-
normalize_path(str(file_path.relative_to(base_path)))
|
| 78 |
-
for file_path in input_path.rglob("*")
|
| 79 |
-
if file_path.suffix.lower() in {'.gltf', '.glb', '.fbx'}
|
| 80 |
-
]
|
| 81 |
-
|
| 82 |
-
return {"required": {
|
| 83 |
-
"model_file": (sorted(files), {"file_upload": True}),
|
| 84 |
-
"image": ("LOAD_3D_ANIMATION", {}),
|
| 85 |
-
"width": ("INT", {"default": 1024, "min": 1, "max": 4096, "step": 1}),
|
| 86 |
-
"height": ("INT", {"default": 1024, "min": 1, "max": 4096, "step": 1}),
|
| 87 |
-
}}
|
| 88 |
-
|
| 89 |
-
RETURN_TYPES = ("IMAGE", "MASK", "STRING", "IMAGE", "LOAD3D_CAMERA", IO.VIDEO)
|
| 90 |
-
RETURN_NAMES = ("image", "mask", "mesh_path", "normal", "camera_info", "recording_video")
|
| 91 |
-
|
| 92 |
-
FUNCTION = "process"
|
| 93 |
-
EXPERIMENTAL = True
|
| 94 |
-
|
| 95 |
-
CATEGORY = "3d"
|
| 96 |
-
|
| 97 |
-
def process(self, model_file, image, **kwargs):
|
| 98 |
-
image_path = folder_paths.get_annotated_filepath(image['image'])
|
| 99 |
-
mask_path = folder_paths.get_annotated_filepath(image['mask'])
|
| 100 |
-
normal_path = folder_paths.get_annotated_filepath(image['normal'])
|
| 101 |
-
|
| 102 |
-
load_image_node = nodes.LoadImage()
|
| 103 |
-
output_image, ignore_mask = load_image_node.load_image(image=image_path)
|
| 104 |
-
ignore_image, output_mask = load_image_node.load_image(image=mask_path)
|
| 105 |
-
normal_image, ignore_mask2 = load_image_node.load_image(image=normal_path)
|
| 106 |
-
|
| 107 |
-
video = None
|
| 108 |
-
|
| 109 |
-
if image['recording'] != "":
|
| 110 |
-
recording_video_path = folder_paths.get_annotated_filepath(image['recording'])
|
| 111 |
-
|
| 112 |
-
video = VideoFromFile(recording_video_path)
|
| 113 |
-
|
| 114 |
-
return output_image, output_mask, model_file, normal_image, image['camera_info'], video
|
| 115 |
-
|
| 116 |
-
class Preview3D():
|
| 117 |
-
@classmethod
|
| 118 |
-
def INPUT_TYPES(s):
|
| 119 |
-
return {"required": {
|
| 120 |
-
"model_file": ("STRING", {"default": "", "multiline": False}),
|
| 121 |
-
},
|
| 122 |
-
"optional": {
|
| 123 |
-
"camera_info": ("LOAD3D_CAMERA", {})
|
| 124 |
-
}}
|
| 125 |
-
|
| 126 |
-
OUTPUT_NODE = True
|
| 127 |
-
RETURN_TYPES = ()
|
| 128 |
-
|
| 129 |
-
CATEGORY = "3d"
|
| 130 |
-
|
| 131 |
-
FUNCTION = "process"
|
| 132 |
-
EXPERIMENTAL = True
|
| 133 |
-
|
| 134 |
-
def process(self, model_file, **kwargs):
|
| 135 |
-
camera_info = kwargs.get("camera_info", None)
|
| 136 |
-
|
| 137 |
-
return {
|
| 138 |
-
"ui": {
|
| 139 |
-
"result": [model_file, camera_info]
|
| 140 |
-
}
|
| 141 |
-
}
|
| 142 |
-
|
| 143 |
-
class Preview3DAnimation():
|
| 144 |
-
@classmethod
|
| 145 |
-
def INPUT_TYPES(s):
|
| 146 |
-
return {"required": {
|
| 147 |
-
"model_file": ("STRING", {"default": "", "multiline": False}),
|
| 148 |
-
},
|
| 149 |
-
"optional": {
|
| 150 |
-
"camera_info": ("LOAD3D_CAMERA", {})
|
| 151 |
-
}}
|
| 152 |
-
|
| 153 |
-
OUTPUT_NODE = True
|
| 154 |
-
RETURN_TYPES = ()
|
| 155 |
-
|
| 156 |
-
CATEGORY = "3d"
|
| 157 |
-
|
| 158 |
-
FUNCTION = "process"
|
| 159 |
-
EXPERIMENTAL = True
|
| 160 |
-
|
| 161 |
-
def process(self, model_file, **kwargs):
|
| 162 |
-
camera_info = kwargs.get("camera_info", None)
|
| 163 |
-
|
| 164 |
-
return {
|
| 165 |
-
"ui": {
|
| 166 |
-
"result": [model_file, camera_info]
|
| 167 |
-
}
|
| 168 |
-
}
|
| 169 |
-
|
| 170 |
-
NODE_CLASS_MAPPINGS = {
|
| 171 |
-
"Load3D": Load3D,
|
| 172 |
-
"Load3DAnimation": Load3DAnimation,
|
| 173 |
-
"Preview3D": Preview3D,
|
| 174 |
-
"Preview3DAnimation": Preview3DAnimation
|
| 175 |
-
}
|
| 176 |
-
|
| 177 |
-
NODE_DISPLAY_NAME_MAPPINGS = {
|
| 178 |
-
"Load3D": "Load 3D",
|
| 179 |
-
"Load3DAnimation": "Load 3D - Animation",
|
| 180 |
-
"Preview3D": "Preview 3D",
|
| 181 |
-
"Preview3DAnimation": "Preview 3D - Animation"
|
| 182 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
comfy_extras/nodes_lora_extract.py
DELETED
|
@@ -1,119 +0,0 @@
|
|
| 1 |
-
import torch
|
| 2 |
-
import comfy.model_management
|
| 3 |
-
import comfy.utils
|
| 4 |
-
import folder_paths
|
| 5 |
-
import os
|
| 6 |
-
import logging
|
| 7 |
-
from enum import Enum
|
| 8 |
-
|
| 9 |
-
CLAMP_QUANTILE = 0.99
|
| 10 |
-
|
| 11 |
-
def extract_lora(diff, rank):
|
| 12 |
-
conv2d = (len(diff.shape) == 4)
|
| 13 |
-
kernel_size = None if not conv2d else diff.size()[2:4]
|
| 14 |
-
conv2d_3x3 = conv2d and kernel_size != (1, 1)
|
| 15 |
-
out_dim, in_dim = diff.size()[0:2]
|
| 16 |
-
rank = min(rank, in_dim, out_dim)
|
| 17 |
-
|
| 18 |
-
if conv2d:
|
| 19 |
-
if conv2d_3x3:
|
| 20 |
-
diff = diff.flatten(start_dim=1)
|
| 21 |
-
else:
|
| 22 |
-
diff = diff.squeeze()
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
U, S, Vh = torch.linalg.svd(diff.float())
|
| 26 |
-
U = U[:, :rank]
|
| 27 |
-
S = S[:rank]
|
| 28 |
-
U = U @ torch.diag(S)
|
| 29 |
-
Vh = Vh[:rank, :]
|
| 30 |
-
|
| 31 |
-
dist = torch.cat([U.flatten(), Vh.flatten()])
|
| 32 |
-
hi_val = torch.quantile(dist, CLAMP_QUANTILE)
|
| 33 |
-
low_val = -hi_val
|
| 34 |
-
|
| 35 |
-
U = U.clamp(low_val, hi_val)
|
| 36 |
-
Vh = Vh.clamp(low_val, hi_val)
|
| 37 |
-
if conv2d:
|
| 38 |
-
U = U.reshape(out_dim, rank, 1, 1)
|
| 39 |
-
Vh = Vh.reshape(rank, in_dim, kernel_size[0], kernel_size[1])
|
| 40 |
-
return (U, Vh)
|
| 41 |
-
|
| 42 |
-
class LORAType(Enum):
|
| 43 |
-
STANDARD = 0
|
| 44 |
-
FULL_DIFF = 1
|
| 45 |
-
|
| 46 |
-
LORA_TYPES = {"standard": LORAType.STANDARD,
|
| 47 |
-
"full_diff": LORAType.FULL_DIFF}
|
| 48 |
-
|
| 49 |
-
def calc_lora_model(model_diff, rank, prefix_model, prefix_lora, output_sd, lora_type, bias_diff=False):
|
| 50 |
-
comfy.model_management.load_models_gpu([model_diff], force_patch_weights=True)
|
| 51 |
-
sd = model_diff.model_state_dict(filter_prefix=prefix_model)
|
| 52 |
-
|
| 53 |
-
for k in sd:
|
| 54 |
-
if k.endswith(".weight"):
|
| 55 |
-
weight_diff = sd[k]
|
| 56 |
-
if lora_type == LORAType.STANDARD:
|
| 57 |
-
if weight_diff.ndim < 2:
|
| 58 |
-
if bias_diff:
|
| 59 |
-
output_sd["{}{}.diff".format(prefix_lora, k[len(prefix_model):-7])] = weight_diff.contiguous().half().cpu()
|
| 60 |
-
continue
|
| 61 |
-
try:
|
| 62 |
-
out = extract_lora(weight_diff, rank)
|
| 63 |
-
output_sd["{}{}.lora_up.weight".format(prefix_lora, k[len(prefix_model):-7])] = out[0].contiguous().half().cpu()
|
| 64 |
-
output_sd["{}{}.lora_down.weight".format(prefix_lora, k[len(prefix_model):-7])] = out[1].contiguous().half().cpu()
|
| 65 |
-
except:
|
| 66 |
-
logging.warning("Could not generate lora weights for key {}, is the weight difference a zero?".format(k))
|
| 67 |
-
elif lora_type == LORAType.FULL_DIFF:
|
| 68 |
-
output_sd["{}{}.diff".format(prefix_lora, k[len(prefix_model):-7])] = weight_diff.contiguous().half().cpu()
|
| 69 |
-
|
| 70 |
-
elif bias_diff and k.endswith(".bias"):
|
| 71 |
-
output_sd["{}{}.diff_b".format(prefix_lora, k[len(prefix_model):-5])] = sd[k].contiguous().half().cpu()
|
| 72 |
-
return output_sd
|
| 73 |
-
|
| 74 |
-
class LoraSave:
|
| 75 |
-
def __init__(self):
|
| 76 |
-
self.output_dir = folder_paths.get_output_directory()
|
| 77 |
-
|
| 78 |
-
@classmethod
|
| 79 |
-
def INPUT_TYPES(s):
|
| 80 |
-
return {"required": {"filename_prefix": ("STRING", {"default": "loras/ComfyUI_extracted_lora"}),
|
| 81 |
-
"rank": ("INT", {"default": 8, "min": 1, "max": 4096, "step": 1}),
|
| 82 |
-
"lora_type": (tuple(LORA_TYPES.keys()),),
|
| 83 |
-
"bias_diff": ("BOOLEAN", {"default": True}),
|
| 84 |
-
},
|
| 85 |
-
"optional": {"model_diff": ("MODEL", {"tooltip": "The ModelSubtract output to be converted to a lora."}),
|
| 86 |
-
"text_encoder_diff": ("CLIP", {"tooltip": "The CLIPSubtract output to be converted to a lora."})},
|
| 87 |
-
}
|
| 88 |
-
RETURN_TYPES = ()
|
| 89 |
-
FUNCTION = "save"
|
| 90 |
-
OUTPUT_NODE = True
|
| 91 |
-
|
| 92 |
-
CATEGORY = "_for_testing"
|
| 93 |
-
|
| 94 |
-
def save(self, filename_prefix, rank, lora_type, bias_diff, model_diff=None, text_encoder_diff=None):
|
| 95 |
-
if model_diff is None and text_encoder_diff is None:
|
| 96 |
-
return {}
|
| 97 |
-
|
| 98 |
-
lora_type = LORA_TYPES.get(lora_type)
|
| 99 |
-
full_output_folder, filename, counter, subfolder, filename_prefix = folder_paths.get_save_image_path(filename_prefix, self.output_dir)
|
| 100 |
-
|
| 101 |
-
output_sd = {}
|
| 102 |
-
if model_diff is not None:
|
| 103 |
-
output_sd = calc_lora_model(model_diff, rank, "diffusion_model.", "diffusion_model.", output_sd, lora_type, bias_diff=bias_diff)
|
| 104 |
-
if text_encoder_diff is not None:
|
| 105 |
-
output_sd = calc_lora_model(text_encoder_diff.patcher, rank, "", "text_encoders.", output_sd, lora_type, bias_diff=bias_diff)
|
| 106 |
-
|
| 107 |
-
output_checkpoint = f"{filename}_{counter:05}_.safetensors"
|
| 108 |
-
output_checkpoint = os.path.join(full_output_folder, output_checkpoint)
|
| 109 |
-
|
| 110 |
-
comfy.utils.save_torch_file(output_sd, output_checkpoint, metadata=None)
|
| 111 |
-
return {}
|
| 112 |
-
|
| 113 |
-
NODE_CLASS_MAPPINGS = {
|
| 114 |
-
"LoraSave": LoraSave
|
| 115 |
-
}
|
| 116 |
-
|
| 117 |
-
NODE_DISPLAY_NAME_MAPPINGS = {
|
| 118 |
-
"LoraSave": "Extract and Save Lora"
|
| 119 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
comfy_extras/nodes_lotus.py
DELETED
|
@@ -1,29 +0,0 @@
|
|
| 1 |
-
import torch
|
| 2 |
-
import comfy.model_management as mm
|
| 3 |
-
|
| 4 |
-
class LotusConditioning:
|
| 5 |
-
@classmethod
|
| 6 |
-
def INPUT_TYPES(s):
|
| 7 |
-
return {
|
| 8 |
-
"required": {
|
| 9 |
-
},
|
| 10 |
-
}
|
| 11 |
-
|
| 12 |
-
RETURN_TYPES = ("CONDITIONING",)
|
| 13 |
-
RETURN_NAMES = ("conditioning",)
|
| 14 |
-
FUNCTION = "conditioning"
|
| 15 |
-
CATEGORY = "conditioning/lotus"
|
| 16 |
-
|
| 17 |
-
def conditioning(self):
|
| 18 |
-
device = mm.get_torch_device()
|
| 19 |
-
#lotus uses a frozen encoder and null conditioning, i'm just inlining the results of that operation since it doesn't change
|
| 20 |
-
#and getting parity with the reference implementation would otherwise require inference and 800mb of tensors
|
| 21 |
-
prompt_embeds = torch.tensor([[[-0.3134765625, -0.447509765625, -0.00823974609375, -0.22802734375, 0.1785888671875, -0.2342529296875, -0.2188720703125, -0.0089111328125, -0.31396484375, 0.196533203125, -0.055877685546875, -0.3828125, -0.0965576171875, 0.0073394775390625, -0.284423828125, 0.07470703125, -0.086181640625, -0.211181640625, 0.0599365234375, 0.10693359375, 0.0007929801940917969, -0.78076171875, -0.382568359375, -0.1851806640625, -0.140625, -0.0936279296875, -0.1229248046875, -0.152099609375, -0.203857421875, -0.2349853515625, -0.2437744140625, -0.10858154296875, -0.08990478515625, 0.08892822265625, -0.2391357421875, -0.1611328125, -0.427978515625, -0.1336669921875, -0.27685546875, -0.1781005859375, -0.3857421875, 0.251953125, -0.055999755859375, -0.0712890625, -0.00130462646484375, 0.033477783203125, -0.26416015625, 0.07171630859375, -0.0090789794921875, -0.2025146484375, -0.2763671875, -0.09869384765625, -0.45751953125, -0.23095703125, 0.004528045654296875, -0.369140625, -0.366943359375, -0.205322265625, -0.1505126953125, -0.45166015625, -0.2059326171875, 0.0168609619140625, -0.305419921875, -0.150634765625, 0.02685546875, -0.609375, -0.019012451171875, 0.050445556640625, -0.0084381103515625, -0.31005859375, -0.184326171875, -0.15185546875, 0.06732177734375, 0.150390625, -0.10919189453125, -0.08837890625, -0.50537109375, -0.389892578125, -0.0294342041015625, -0.10491943359375, -0.187255859375, -0.43212890625, -0.328125, -1.060546875, 0.011871337890625, 0.04730224609375, -0.09521484375, -0.07452392578125, -0.29296875, -0.109130859375, -0.250244140625, -0.3828125, -0.171875, -0.03399658203125, -0.15478515625, -0.1861572265625, -0.2398681640625, 0.1053466796875, -0.22314453125, -0.1932373046875, -0.18798828125, -0.430419921875, -0.05364990234375, -0.474609375, -0.261474609375, -0.1077880859375, -0.439208984375, 0.08966064453125, -0.185302734375, -0.338134765625, -0.297119140625, -0.298583984375, -0.175537109375, -0.373291015625, -0.1397705078125, -0.260498046875, -0.383544921875, -0.09979248046875, -0.319580078125, -0.06884765625, -0.4365234375, -0.183837890625, -0.393310546875, -0.002277374267578125, 0.11236572265625, -0.260498046875, -0.2242431640625, -0.19384765625, -0.51123046875, 0.03216552734375, -0.048004150390625, -0.279052734375, -0.2978515625, -0.255615234375, 0.115478515625, -4.08984375, -0.1668701171875, -0.278076171875, -0.5712890625, -0.1385498046875, -0.244384765625, -0.41455078125, -0.244140625, -0.0677490234375, -0.141357421875, -0.11590576171875, -0.1439208984375, -0.0185394287109375, -2.490234375, -0.1549072265625, -0.2305908203125, -0.3828125, -0.1173095703125, -0.08258056640625, -0.1719970703125, -0.325439453125, -0.292724609375, -0.08154296875, -0.412353515625, -0.3115234375, -0.00832366943359375, 0.00489044189453125, -0.2236328125, -0.151123046875, -0.457275390625, -0.135009765625, -0.163330078125, -0.0819091796875, 0.06689453125, 0.0209197998046875, -0.11907958984375, -0.10369873046875, -0.2998046875, -0.478759765625, -0.07940673828125, -0.01517486572265625, -0.3017578125, -0.343994140625, -0.258544921875, -0.44775390625, -0.392822265625, -0.0255584716796875, -0.2998046875, 0.10833740234375, -0.271728515625, -0.36181640625, -0.255859375, -0.2056884765625, -0.055450439453125, 0.060516357421875, -0.45751953125, -0.2322998046875, -0.1737060546875, -0.40576171875, -0.2286376953125, -0.053070068359375, -0.0283660888671875, -0.1898193359375, -4.291534423828125e-05, -0.6591796875, -0.1717529296875, -0.479736328125, -0.1400146484375, -0.40771484375, 0.154296875, 0.003101348876953125, 0.00661468505859375, -0.2073974609375, -0.493408203125, 2.171875, -0.45361328125, -0.283935546875, -0.302001953125, -0.25146484375, -0.207275390625, -0.1524658203125, -0.72998046875, -0.08203125, 0.053192138671875, -0.2685546875, 0.1834716796875, -0.270263671875, -0.091552734375, -0.08319091796875, -0.1297607421875, -0.453857421875, 0.0687255859375, 0.0268096923828125, -0.16552734375, -0.4208984375, -0.1552734375, -0.057373046875, -0.300537109375, -0.04541015625, -0.486083984375, -0.2205810546875, -0.39013671875, 0.007488250732421875, -0.005329132080078125, -0.09759521484375, -0.1448974609375, -0.21923828125, -0.429443359375, -0.40087890625, -0.19384765625, -0.064453125, -0.0306243896484375, -0.045806884765625, -0.056793212890625, 0.119384765625, -0.2073974609375, -0.356201171875, -0.168212890625, -0.291748046875, -0.289794921875, -0.205322265625, -0.419677734375, -0.478271484375, -0.2037353515625, -0.368408203125, -0.186279296875, -0.427734375, -0.1756591796875, 0.07501220703125, -0.2457275390625, -0.03692626953125, 0.003997802734375, -5.7578125, -0.01052093505859375, -0.2305908203125, -0.2252197265625, -0.197509765625, -0.1566162109375, -0.1668701171875, -0.383056640625, -0.05413818359375, 0.12188720703125, -0.369873046875, -0.0184478759765625, -0.150146484375, -0.51123046875, -0.45947265625, -0.1561279296875, 0.060455322265625, 0.043487548828125, -0.1370849609375, -0.069091796875, -0.285888671875, -0.44482421875, -0.2374267578125, -0.2191162109375, -0.434814453125, -0.0360107421875, 0.1298828125, 0.0217742919921875, -0.51220703125, -0.13525390625, -0.09381103515625, -0.276611328125, -0.171875, -0.17138671875, -0.4443359375, -0.2178955078125, -0.269775390625, -0.38623046875, -0.31591796875, -0.42333984375, -0.280029296875, -0.255615234375, -0.17041015625, 0.06268310546875, -0.1878662109375, -0.00677490234375, -0.23583984375, -0.08795166015625, -0.2232666015625, -0.1719970703125, -0.484130859375, -0.328857421875, 0.04669189453125, -0.0419921875, -0.11114501953125, 0.02313232421875, -0.0033130645751953125, -0.6005859375, 0.09051513671875, -0.1884765625, -0.262939453125, -0.375732421875, -0.525390625, -0.1170654296875, -0.3779296875, -0.242919921875, -0.419921875, 0.0665283203125, -0.343017578125, 0.06658935546875, -0.346435546875, -0.1363525390625, -0.2000732421875, -0.3837890625, 0.028167724609375, 0.043853759765625, -0.0171051025390625, -0.477294921875, -0.107421875, -0.129150390625, -0.319580078125, -0.32177734375, -0.4951171875, -0.010589599609375, -0.1778564453125, -0.40234375, -0.0810546875, 0.03314208984375, -0.13720703125, -0.31591796875, -0.048248291015625, -0.274658203125, -0.0689697265625, -0.027130126953125, -0.0953369140625, 0.146728515625, -0.38671875, -0.025390625, -0.42333984375, -0.41748046875, -0.379638671875, -0.1978759765625, -0.533203125, -0.33544921875, 0.0694580078125, -0.322998046875, -0.1876220703125, 0.0094451904296875, 0.1839599609375, -0.254150390625, -0.30078125, -0.09228515625, -0.0885009765625, 0.12371826171875, 0.1500244140625, -0.12152099609375, -0.29833984375, 0.03924560546875, -0.1470947265625, -0.1610107421875, -0.2049560546875, -0.01708984375, -0.2470703125, -0.1522216796875, -0.25830078125, 0.10870361328125, -0.302490234375, -0.2376708984375, -0.360107421875, -0.443359375, -0.0784912109375, -0.63623046875, -0.0980224609375, -0.332275390625, -0.1749267578125, -0.30859375, -0.1968994140625, -0.250244140625, -0.447021484375, -0.18408203125, -0.006908416748046875, -0.2044677734375, -0.2548828125, -0.369140625, -0.11328125, -0.1103515625, -0.27783203125, -0.325439453125, 0.01381683349609375, 0.036773681640625, -0.1458740234375, -0.34619140625, -0.232177734375, -0.0562744140625, -0.4482421875, -0.21875, -0.0855712890625, -0.276123046875, -0.1544189453125, -0.223388671875, -0.259521484375, 0.0865478515625, -0.0038013458251953125, -0.340087890625, -0.076171875, -0.25341796875, -0.0007548332214355469, -0.060455322265625, -0.352294921875, 0.035736083984375, -0.2181396484375, -0.2318115234375, -0.1707763671875, 0.018646240234375, 0.093505859375, -0.197021484375, 0.033477783203125, -0.035247802734375, 0.0440673828125, -0.2056884765625, -0.040924072265625, -0.05865478515625, 0.056884765625, -0.08807373046875, -0.10845947265625, 0.09564208984375, -0.10888671875, -0.332275390625, -0.1119384765625, -0.115478515625, 13.0234375, 0.0030040740966796875, -0.53662109375, -0.1856689453125, -0.068115234375, -0.143798828125, -0.177978515625, -0.32666015625, -0.353515625, -0.1563720703125, -0.3203125, 0.0085906982421875, -0.1043701171875, -0.365478515625, -0.303466796875, -0.34326171875, -0.410888671875, -0.03790283203125, -0.11419677734375, -0.2939453125, 0.074462890625, -0.21826171875, 0.0242767333984375, -0.226318359375, -0.353515625, -0.177734375, -0.169189453125, -0.2423095703125, -0.12115478515625, -0.07843017578125, -0.341064453125, -0.2117919921875, -0.505859375, -0.544921875, -0.3935546875, -0.10772705078125, -0.2054443359375, -0.136474609375, -0.1796875, -0.396240234375, -0.1971435546875, -0.68408203125, -0.032684326171875, -0.03863525390625, -0.0709228515625, -0.1005859375, -0.156005859375, -0.3837890625, -0.319580078125, 0.11102294921875, -0.394287109375, 0.0799560546875, -0.50341796875, -0.1572265625, 0.004131317138671875, -0.12286376953125, -0.2347412109375, -0.29150390625, -0.10321044921875, -0.286376953125, 0.018798828125, -0.152099609375, -0.321044921875, 0.0191650390625, -0.11376953125, -0.54736328125, 0.15869140625, -0.257568359375, -0.2490234375, -0.3115234375, -0.09765625, -0.350830078125, -0.36376953125, -0.0771484375, -0.2298583984375, -0.30615234375, -0.052154541015625, -0.12091064453125, -0.40283203125, -0.1649169921875, 0.0206451416015625, -0.312744140625, -0.10308837890625, -0.50341796875, -0.1754150390625, -0.2003173828125, -0.173583984375, -0.204833984375, -0.1876220703125, -0.12176513671875, -0.06201171875, -0.03485107421875, -0.20068359375, -0.21484375, -0.246337890625, -0.006587982177734375, -0.09674072265625, -0.4658203125, -0.3994140625, -0.2210693359375, -0.09588623046875, -0.126220703125, -0.09222412109375, -0.145751953125, -0.217529296875, -0.289306640625, -0.28271484375, -0.1787109375, -0.169189453125, -0.359375, -0.21826171875, -0.043792724609375, -0.205322265625, -0.2900390625, -0.055419921875, -0.1490478515625, -0.340576171875, -0.045928955078125, -0.30517578125, -0.51123046875, -0.1046142578125, -0.349853515625, -0.10882568359375, -0.16748046875, -0.267333984375, -0.122314453125, -0.0985107421875, -0.3076171875, -0.1766357421875, -0.251708984375, 0.1964111328125, -0.2220458984375, -0.2349853515625, -0.035980224609375, -0.1749267578125, -0.237060546875, -0.480224609375, -0.240234375, -0.09539794921875, -0.2481689453125, -0.389404296875, -0.1748046875, -0.370849609375, -0.010650634765625, -0.147705078125, -0.0035457611083984375, -0.32568359375, -0.29931640625, -0.1395263671875, -0.28173828125, -0.09820556640625, -0.0176239013671875, -0.05926513671875, -0.0755615234375, -0.1746826171875, -0.283203125, -0.1617431640625, -0.4404296875, 0.046234130859375, -0.183837890625, -0.052032470703125, -0.24658203125, -0.11224365234375, -0.100830078125, -0.162841796875, -0.29736328125, -0.396484375, 0.11798095703125, -0.006496429443359375, -0.32568359375, -0.347900390625, -0.04595947265625, -0.09637451171875, -0.344970703125, -0.01166534423828125, -0.346435546875, -0.2861328125, -0.1845703125, -0.276611328125, -0.01312255859375, -0.395263671875, -0.50927734375, -0.1114501953125, -0.1861572265625, -0.2158203125, -0.1812744140625, 0.055419921875, -0.294189453125, 0.06500244140625, -0.1444091796875, -0.06365966796875, -0.18408203125, -0.0091705322265625, -0.1640625, -0.1856689453125, 0.090087890625, 0.024566650390625, -0.0195159912109375, -0.5546875, -0.301025390625, -0.438232421875, -0.072021484375, 0.030517578125, -0.1490478515625, 0.04888916015625, -0.23681640625, -0.1553955078125, -0.018096923828125, -0.229736328125, -0.2919921875, -0.355712890625, -0.285400390625, -0.1756591796875, -0.08355712890625, -0.416259765625, 0.022674560546875, -0.417236328125, 0.410400390625, -0.249755859375, 0.015625, -0.033599853515625, -0.040313720703125, -0.51708984375, -0.0518798828125, -0.08843994140625, -0.2022705078125, -0.3740234375, -0.285888671875, -0.176025390625, -0.292724609375, -0.369140625, -0.08367919921875, -0.356689453125, -0.38623046875, 0.06549072265625, 0.1669921875, -0.2099609375, -0.007434844970703125, 0.12890625, -0.0040740966796875, -0.2174072265625, -0.025115966796875, -0.2364501953125, -0.1695556640625, -0.0469970703125, -0.03924560546875, -0.36181640625, -0.047515869140625, -0.3154296875, -0.275634765625, -0.25634765625, -0.061920166015625, -0.12164306640625, -0.47314453125, -0.10784912109375, -0.74755859375, -0.13232421875, -0.32421875, -0.04998779296875, -0.286376953125, 0.10345458984375, -0.1710205078125, -0.388916015625, 0.12744140625, -0.3359375, -0.302490234375, -0.238525390625, -0.1455078125, -0.15869140625, -0.2427978515625, -0.0355224609375, -0.11944580078125, -0.31298828125, 0.11456298828125, -0.287841796875, -0.5439453125, -0.3076171875, -0.08642578125, -0.2408447265625, -0.283447265625, -0.428466796875, -0.085693359375, -0.1683349609375, 0.255126953125, 0.07635498046875, -0.38623046875, -0.2025146484375, -0.1331787109375, -0.10821533203125, -0.49951171875, 0.09130859375, -0.19677734375, -0.01904296875, -0.151123046875, -0.344482421875, -0.316650390625, -0.03900146484375, 0.1397705078125, 0.1334228515625, -0.037200927734375, -0.01861572265625, -0.1351318359375, -0.07037353515625, -0.380615234375, -0.34033203125, -0.06903076171875, 0.219970703125, 0.0132598876953125, -0.15869140625, -0.6376953125, 0.158935546875, -0.5283203125, -0.2320556640625, -0.185791015625, -0.2132568359375, -0.436767578125, -0.430908203125, -0.1763916015625, -0.0007672309875488281, -0.424072265625, -0.06719970703125, -0.347900390625, -0.14453125, -0.3056640625, -0.36474609375, -0.35986328125, -0.46240234375, -0.446044921875, -0.1905517578125, -0.1114501953125, -0.42919921875, -0.0643310546875, -0.3662109375, -0.4296875, -0.10968017578125, -0.2998046875, -0.1756591796875, -0.4052734375, -0.0841064453125, -0.252197265625, -0.047393798828125, 0.00434112548828125, -0.10040283203125, -0.271484375, -0.185302734375, -0.1910400390625, 0.10260009765625, 0.01393890380859375, -0.03350830078125, -0.33935546875, -0.329345703125, 0.0574951171875, -0.18896484375, -0.17724609375, -0.42919921875, -0.26708984375, -0.4189453125, -0.149169921875, -0.265625, -0.198974609375, -0.1722412109375, 0.1563720703125, -0.20947265625, -0.267822265625, -0.06353759765625, -0.365478515625, -0.340087890625, -0.3095703125, -0.320068359375, -0.0880126953125, -0.353759765625, -0.0005812644958496094, -0.1617431640625, -0.1866455078125, -0.201416015625, -0.181396484375, -0.2349853515625, -0.384765625, -0.5244140625, 0.01227569580078125, -0.21337890625, -0.30810546875, -0.17578125, -0.3037109375, -0.52978515625, -0.1561279296875, -0.296142578125, 0.057342529296875, -0.369384765625, -0.107666015625, -0.338623046875, -0.2060546875, -0.0213775634765625, -0.394775390625, -0.219482421875, -0.125732421875, -0.03997802734375, -0.42431640625, -0.134521484375, -0.2418212890625, -0.10504150390625, 0.1552734375, 0.1126708984375, -0.1427001953125, -0.133544921875, -0.111083984375, -0.375732421875, -0.2783203125, -0.036834716796875, -0.11053466796875, 0.2471923828125, -0.2529296875, -0.56494140625, -0.374755859375, -0.326416015625, 0.2137451171875, -0.09454345703125, -0.337158203125, -0.3359375, -0.34375, -0.0999755859375, -0.388671875, 0.0103302001953125, 0.14990234375, -0.2041015625, -0.39501953125, -0.39013671875, -0.1258544921875, 0.1453857421875, -0.250732421875, -0.06732177734375, -0.10638427734375, -0.032379150390625, -0.35888671875, -0.098876953125, -0.172607421875, 0.05126953125, -0.1956787109375, -0.183837890625, -0.37060546875, 0.1556396484375, -0.34375, -0.28662109375, -0.06982421875, -0.302490234375, -0.281005859375, -0.1640625, -0.5302734375, -0.1368408203125, -0.1268310546875, -0.35302734375, -0.1473388671875, -0.45556640625, -0.35986328125, -0.273681640625, -0.2249755859375, -0.1893310546875, 0.09356689453125, -0.248291015625, -0.197998046875, -0.3525390625, -0.30126953125, -0.228271484375, -0.2421875, -0.0906982421875, 0.227783203125, -0.296875, -0.009796142578125, -0.2939453125, -0.1021728515625, -0.215576171875, -0.267822265625, -0.052642822265625, 0.203369140625, -0.1417236328125, 0.18505859375, 0.12347412109375, -0.0972900390625, -0.54052734375, -0.430419921875, -0.0906982421875, -0.5419921875, -0.22900390625, -0.0625, -0.12152099609375, -0.495849609375, -0.206787109375, -0.025848388671875, 0.039031982421875, -0.453857421875, -0.318359375, -0.426025390625, -0.3701171875, -0.2169189453125, 0.0845947265625, -0.045654296875, 0.11090087890625, 0.0012454986572265625, 0.2066650390625, -0.046356201171875, -0.2337646484375, -0.295654296875, 0.057891845703125, -0.1639404296875, -0.0535888671875, -0.2607421875, -0.1488037109375, -0.16015625, -0.54345703125, -0.2305908203125, -0.55029296875, -0.178955078125, -0.222412109375, -0.0711669921875, -0.12298583984375, -0.119140625, -0.253662109375, -0.33984375, -0.11322021484375, -0.10723876953125, -0.205078125, -0.360595703125, 0.085205078125, -0.252197265625, -0.365966796875, -0.26953125, 0.2000732421875, -0.50634765625, 0.05706787109375, -0.3115234375, 0.0242919921875, -0.1689453125, -0.2401123046875, -0.3759765625, -0.2125244140625, 0.076416015625, -0.489013671875, -0.11749267578125, -0.55908203125, -0.313232421875, -0.572265625, -0.1387939453125, -0.037078857421875, -0.385498046875, 0.0323486328125, -0.39404296875, -0.05072021484375, -0.10430908203125, -0.10919189453125, -0.28759765625, -0.37451171875, -0.016937255859375, -0.2200927734375, -0.296875, -0.0286712646484375, -0.213134765625, 0.052001953125, -0.052337646484375, -0.253662109375, 0.07269287109375, -0.2498779296875, -0.150146484375, -0.09930419921875, -0.343505859375, 0.254150390625, -0.032440185546875, -0.296142578125], [1.4111328125, 0.00757598876953125, -0.428955078125, 0.089599609375, 0.0227813720703125, -0.0350341796875, -1.0986328125, 0.194091796875, 2.115234375, -0.75439453125, 0.269287109375, -0.73486328125, -1.1025390625, -0.050262451171875, -0.5830078125, 0.0268707275390625, -0.603515625, -0.6025390625, -1.1689453125, 0.25048828125, -0.4189453125, -0.5517578125, -0.30322265625, 0.7724609375, 0.931640625, -0.1422119140625, 2.27734375, -0.56591796875, 1.013671875, -0.9638671875, -0.66796875, -0.8125, 1.3740234375, -1.060546875, -1.029296875, -1.6796875, 0.62890625, 0.49365234375, 0.671875, 0.99755859375, -1.0185546875, -0.047027587890625, -0.374267578125, 0.2354736328125, 1.4970703125, -1.5673828125, 0.448974609375, 0.2078857421875, -1.060546875, -0.171875, -0.6201171875, -0.1607666015625, 0.7548828125, -0.58935546875, -0.2052001953125, 0.060791015625, 0.200439453125, 3.154296875, -3.87890625, 2.03515625, 1.126953125, 0.1640625, -1.8447265625, 0.002620697021484375, 0.7998046875, -0.337158203125, 0.47216796875, -0.5849609375, 0.9970703125, 0.3935546875, 1.22265625, -1.5048828125, -0.65673828125, 1.1474609375, -1.73046875, -1.8701171875, 1.529296875, -0.6787109375, -1.4453125, 1.556640625, -0.327392578125, 2.986328125, -0.146240234375, -2.83984375, 0.303466796875, -0.71728515625, -0.09698486328125, -0.2423095703125, 0.6767578125, -2.197265625, -0.86279296875, -0.53857421875, -1.2236328125, 1.669921875, -1.1689453125, -0.291259765625, -0.54736328125, -0.036346435546875, 1.041015625, -1.7265625, -0.6064453125, -0.1634521484375, 0.2381591796875, 0.65087890625, -1.169921875, 1.9208984375, 0.5634765625, 0.37841796875, 0.798828125, -1.021484375, -0.4091796875, 2.275390625, -0.302734375, -1.7783203125, 1.0458984375, 1.478515625, 0.708984375, -1.541015625, -0.0006041526794433594, 1.1884765625, 2.041015625, 0.560546875, -0.1131591796875, 1.0341796875, 0.06121826171875, 2.6796875, -0.53369140625, -1.2490234375, -0.7333984375, -1.017578125, -1.0078125, 1.3212890625, -0.47607421875, -1.4189453125, 0.54052734375, -0.796875, -0.73095703125, -1.412109375, -0.94873046875, -2.2734375, -1.1220703125, -1.3837890625, -0.5087890625, -1.0380859375, -0.93603515625, -0.58349609375, -1.0703125, -1.10546875, -2.60546875, 0.062225341796875, 0.38232421875, -0.411376953125, -0.369140625, -0.9833984375, -0.7294921875, -0.181396484375, -0.47216796875, -0.56884765625, -0.11041259765625, -2.673828125, 0.27783203125, -0.857421875, 0.9296875, 1.9580078125, 0.1385498046875, -1.91796875, -1.529296875, 0.53857421875, 0.509765625, -0.90380859375, -0.0947265625, -2.083984375, 0.9228515625, -0.28564453125, -0.80859375, -0.093505859375, -0.6015625, -1.255859375, 0.6533203125, 0.327880859375, -0.07598876953125, -0.22705078125, -0.30078125, -0.5185546875, -1.6044921875, 1.5927734375, 1.416015625, -0.91796875, -0.276611328125, -0.75830078125, -1.1689453125, -1.7421875, 1.0546875, -0.26513671875, -0.03314208984375, 0.278076171875, -1.337890625, 0.055023193359375, 0.10546875, -1.064453125, 1.048828125, -1.4052734375, -1.1240234375, -0.51416015625, -1.05859375, -1.7265625, -1.1328125, 0.43310546875, -2.576171875, -2.140625, -0.79345703125, 0.50146484375, 1.96484375, 0.98583984375, 0.337646484375, -0.77978515625, 0.85498046875, -0.65185546875, -0.484375, 2.708984375, 0.55810546875, -0.147216796875, -0.5537109375, -0.75439453125, -1.736328125, 1.1259765625, -1.095703125, -0.2587890625, 2.978515625, 0.335205078125, 0.357666015625, -0.09356689453125, 0.295654296875, -0.23779296875, 1.5751953125, 0.10400390625, 1.7001953125, -0.72900390625, -1.466796875, -0.2012939453125, 0.634765625, -0.1556396484375, -2.01171875, 0.32666015625, 0.047454833984375, -0.1671142578125, -0.78369140625, -0.994140625, 0.7802734375, -0.1429443359375, -0.115234375, 0.53271484375, -0.96142578125, -0.064208984375, 1.396484375, 1.654296875, -1.6015625, -0.77392578125, 0.276123046875, -0.42236328125, 0.8642578125, 0.533203125, 0.397216796875, -1.21484375, 0.392578125, -0.501953125, -0.231689453125, 1.474609375, 1.6669921875, 1.8662109375, -1.2998046875, 0.223876953125, -0.51318359375, -0.437744140625, -1.16796875, -0.7724609375, 1.6826171875, 0.62255859375, 2.189453125, -0.599609375, -0.65576171875, -1.1005859375, -0.45263671875, -0.292236328125, 2.58203125, -1.3779296875, 0.23486328125, -1.708984375, -1.4111328125, -0.5078125, -0.8525390625, -0.90771484375, 0.861328125, -2.22265625, -1.380859375, 0.7275390625, 0.85595703125, -0.77978515625, 2.044921875, -0.430908203125, 0.78857421875, -1.21484375, -0.09130859375, 0.5146484375, -1.92578125, -0.1396484375, 0.289306640625, 0.60498046875, 0.93896484375, -0.09295654296875, -0.45751953125, -0.986328125, -0.66259765625, 1.48046875, 0.274169921875, -0.267333984375, -1.3017578125, -1.3623046875, -1.982421875, -0.86083984375, -0.41259765625, -0.2939453125, -1.91015625, 1.6826171875, 0.437255859375, 1.0029296875, 0.376220703125, -0.010467529296875, -0.82861328125, -0.513671875, -3.134765625, 1.0205078125, -1.26171875, -1.009765625, 1.0869140625, -0.95703125, 0.0103759765625, 1.642578125, 0.78564453125, 1.029296875, 0.496826171875, 1.2880859375, 0.5234375, 0.05322265625, -0.206787109375, -0.79443359375, -1.1669921875, 0.049530029296875, -0.27978515625, 0.0237884521484375, -0.74169921875, -1.068359375, 0.86083984375, 1.1787109375, 0.91064453125, -0.453857421875, -1.822265625, -0.9228515625, -0.50048828125, 0.359130859375, 0.802734375, -1.3564453125, -0.322509765625, -1.1123046875, -1.0390625, -0.52685546875, -1.291015625, -0.343017578125, -1.2109375, -0.19091796875, 2.146484375, -0.04315185546875, -0.3701171875, -2.044921875, -0.429931640625, -0.56103515625, -0.166015625, -0.4658203125, -2.29296875, -1.078125, -1.0927734375, -0.1033935546875, -0.56103515625, -0.05743408203125, -1.986328125, -0.513671875, 0.70361328125, -2.484375, -1.3037109375, -1.6650390625, 0.4814453125, -0.84912109375, -2.697265625, -0.197998046875, 0.0869140625, -0.172607421875, -1.326171875, -1.197265625, 1.23828125, -0.38720703125, -0.075927734375, 0.02569580078125, -1.2119140625, 0.09027099609375, -2.12890625, -1.640625, -0.1524658203125, 0.2373046875, 1.37109375, 2.248046875, 1.4619140625, 0.3134765625, 0.50244140625, -0.1383056640625, -1.2705078125, 0.7353515625, 0.65771484375, -0.431396484375, -1.341796875, 0.10089111328125, 0.208984375, -0.0099945068359375, 0.83203125, 1.314453125, -0.422607421875, -1.58984375, -0.6044921875, 0.23681640625, -1.60546875, -0.61083984375, -1.5615234375, 1.62890625, -0.6728515625, -0.68212890625, -0.5224609375, -0.9150390625, -0.468994140625, 0.268310546875, 0.287353515625, -0.025543212890625, 0.443603515625, 1.62109375, -1.08984375, -0.5556640625, 1.03515625, -0.31298828125, -0.041778564453125, 0.260986328125, 0.34716796875, -2.326171875, 0.228271484375, -0.85107421875, -2.255859375, 0.3486328125, -0.25830078125, -0.3671875, -0.796875, -1.115234375, 1.8369140625, -0.19775390625, -1.236328125, -0.0447998046875, 0.69921875, 1.37890625, 1.11328125, 0.0928955078125, 0.6318359375, -0.62353515625, 0.55859375, -0.286865234375, 1.5361328125, -0.391357421875, -0.052215576171875, -1.12890625, 0.55517578125, -0.28515625, -0.3603515625, 0.68896484375, 0.67626953125, 0.003070831298828125, 1.2236328125, 0.1597900390625, -1.3076171875, 0.99951171875, -2.5078125, -1.2119140625, 0.1749267578125, -1.1865234375, -1.234375, -0.1180419921875, -1.751953125, 0.033050537109375, 0.234130859375, -3.107421875, -1.0380859375, 0.61181640625, -0.87548828125, 0.3154296875, -1.103515625, 0.261474609375, -1.130859375, -0.7470703125, -0.43408203125, 1.3828125, -0.41259765625, -1.7587890625, 0.765625, 0.004852294921875, 0.135498046875, -0.76953125, -0.1314697265625, 0.400390625, 1.43359375, 0.07135009765625, 0.0645751953125, -0.5869140625, -0.5810546875, -0.2900390625, -1.3037109375, 0.1287841796875, -0.27490234375, 0.59228515625, 2.333984375, -0.54541015625, -0.556640625, 0.447265625, -0.806640625, 0.09149169921875, -0.70654296875, -0.357177734375, -1.099609375, -0.5576171875, -0.44189453125, 0.400390625, -0.666015625, -1.4619140625, 0.728515625, -1.5986328125, 0.153076171875, -0.126708984375, -2.83984375, -1.84375, -0.2469482421875, 0.677734375, 0.43701171875, 3.298828125, 1.1591796875, -0.7158203125, -0.8251953125, 0.451171875, -2.376953125, -0.58642578125, -0.86767578125, 0.0789794921875, 0.1351318359375, -0.325439453125, 0.484375, 1.166015625, -0.1610107421875, -0.15234375, -0.54638671875, -0.806640625, 0.285400390625, 0.1661376953125, -0.50146484375, -1.0478515625, 1.5751953125, 0.0313720703125, 0.2396240234375, -0.6572265625, -0.1258544921875, -1.060546875, 1.3076171875, -0.301513671875, -1.2412109375, 0.6376953125, -1.5693359375, 0.354248046875, 0.2427978515625, -0.392333984375, 0.61962890625, -0.58837890625, -1.71484375, -0.2098388671875, -0.828125, 0.330810546875, 0.16357421875, -0.2259521484375, 0.0972900390625, -0.451416015625, 1.79296875, -1.673828125, -1.58203125, -2.099609375, -0.487548828125, -0.87060546875, 0.62646484375, -1.470703125, -0.1558837890625, 0.4609375, 1.3369140625, 0.2322998046875, 0.1632080078125, 0.65966796875, 1.0810546875, 0.1041259765625, 0.63232421875, -0.32421875, -1.04296875, -1.046875, -1.3720703125, -0.8486328125, 0.1290283203125, 0.137939453125, 0.1549072265625, -1.0908203125, 0.0167694091796875, -0.31689453125, 1.390625, 0.07269287109375, 1.0390625, 1.1162109375, -0.455810546875, -0.06689453125, -0.053741455078125, 0.5048828125, -0.8408203125, -1.19921875, 0.87841796875, 0.7421875, 0.2030029296875, 0.109619140625, -0.59912109375, -1.337890625, -0.74169921875, -0.64453125, -1.326171875, 0.21044921875, -1.3583984375, -1.685546875, -0.472900390625, -0.270263671875, 0.99365234375, -0.96240234375, 1.1279296875, -0.45947265625, -0.45654296875, -0.99169921875, -3.515625, -1.9853515625, 0.73681640625, 0.92333984375, -0.56201171875, -1.4453125, -2.078125, 0.94189453125, -1.333984375, 0.0982666015625, 0.60693359375, 0.367431640625, 3.015625, -1.1357421875, -1.5634765625, 0.90234375, -0.1783447265625, 0.1802978515625, -0.317138671875, -0.513671875, 1.2353515625, -0.033203125, 1.4482421875, 1.0087890625, 0.9248046875, 0.10418701171875, 0.7626953125, -1.3798828125, 0.276123046875, 0.55224609375, 1.1005859375, -0.62158203125, -0.806640625, 0.65087890625, 0.270263671875, -0.339111328125, -0.9384765625, -0.09381103515625, -0.7216796875, 1.37890625, -0.398193359375, -0.3095703125, -1.4912109375, 0.96630859375, 0.43798828125, 0.62255859375, 0.0213470458984375, 0.235595703125, -1.2958984375, 0.0157318115234375, -0.810546875, 1.9736328125, -0.2462158203125, 0.720703125, 0.822265625, -0.755859375, -0.658203125, 0.344482421875, -2.892578125, -0.282470703125, 1.2529296875, -0.294189453125, 0.6748046875, -0.80859375, 0.9287109375, 1.27734375, -1.71875, -0.166015625, 0.47412109375, -0.41259765625, -1.3681640625, -0.978515625, -0.77978515625, -1.044921875, -0.90380859375, -0.08184814453125, -0.86181640625, -0.10772705078125, -0.299560546875, -0.4306640625, -0.47119140625, 0.95703125, 1.107421875, 0.91796875, 0.76025390625, 0.7392578125, -0.09161376953125, -0.7392578125, 0.9716796875, -0.395751953125, -0.75390625, -0.164306640625, -0.087646484375, 0.028564453125, -0.91943359375, -0.66796875, 2.486328125, 0.427734375, 0.626953125, 0.474853515625, 0.0926513671875, 0.830078125, -0.6923828125, 0.7841796875, -0.89208984375, -2.482421875, 0.034912109375, -1.3447265625, -0.475341796875, -0.286376953125, -0.732421875, 0.190673828125, -0.491455078125, -3.091796875, -1.2783203125, -0.66015625, -0.1507568359375, 0.042236328125, -1.025390625, 0.12744140625, -1.984375, -0.393798828125, -1.25, -1.140625, 1.77734375, 0.2457275390625, -0.8017578125, 0.7763671875, -0.387939453125, -0.3662109375, 1.1572265625, 0.123291015625, -0.07135009765625, 1.412109375, -0.685546875, -3.078125, 0.031524658203125, -0.70458984375, 0.78759765625, 0.433837890625, -1.861328125, -1.33203125, 2.119140625, -1.3544921875, -0.6591796875, -1.4970703125, 0.40625, -2.078125, -1.30859375, 0.050262451171875, -0.60107421875, 1.0078125, 0.05657958984375, -0.96826171875, 0.0264892578125, 0.159912109375, 0.84033203125, -1.1494140625, -0.0433349609375, -0.2034912109375, 1.09765625, -1.142578125, -0.283203125, -0.427978515625, 1.0927734375, -0.67529296875, -0.61572265625, 2.517578125, 0.84130859375, 1.8662109375, 0.1748046875, -0.407958984375, -0.029449462890625, -0.27587890625, -0.958984375, -0.10028076171875, 1.248046875, -0.0792236328125, -0.45556640625, 0.7685546875, 1.5556640625, -1.8759765625, -0.131591796875, -1.3583984375, 0.7890625, 0.80810546875, -1.0322265625, -0.53076171875, -0.1484375, -1.7841796875, -1.2470703125, 0.17138671875, -0.04864501953125, -0.80322265625, -0.0933837890625, 0.984375, 0.7001953125, 0.5380859375, 0.2022705078125, -1.1865234375, 0.5439453125, 1.1318359375, 0.79931640625, 0.32666015625, -1.26171875, 0.457763671875, 1.1591796875, -0.34423828125, 0.65771484375, 0.216552734375, 1.19140625, -0.2744140625, -0.020416259765625, -0.86376953125, 0.93017578125, 1.0556640625, 0.69873046875, -0.15087890625, -0.33056640625, 0.8505859375, 0.06890869140625, 0.359375, -0.262939453125, 0.12493896484375, 0.017059326171875, -0.98974609375, 0.5107421875, 0.2408447265625, 0.615234375, -0.62890625, 0.86962890625, -0.07427978515625, 0.85595703125, 0.300537109375, -1.072265625, -1.6064453125, -0.353515625, -0.484130859375, -0.6044921875, -0.455810546875, 0.95849609375, 1.3671875, 0.544921875, 0.560546875, 0.34521484375, -0.6513671875, -0.410400390625, -0.2021484375, -0.1656494140625, 0.073486328125, 0.84716796875, -1.7998046875, -1.0126953125, -0.1324462890625, 0.95849609375, -0.669921875, -0.79052734375, -2.193359375, -0.42529296875, -1.7275390625, -1.04296875, 0.716796875, -0.4423828125, -1.193359375, 0.61572265625, -1.5224609375, 0.62890625, -0.705078125, 0.677734375, -0.213134765625, -1.6748046875, -1.087890625, -0.65185546875, -1.1337890625, 2.314453125, -0.352783203125, -0.27001953125, -2.01953125, -1.2685546875, 0.308837890625, -0.280517578125, -1.3798828125, -1.595703125, 0.642578125, 1.693359375, -0.82470703125, -1.255859375, 0.57373046875, 1.5859375, 1.068359375, -0.876953125, 0.370849609375, 1.220703125, 0.59765625, 0.007602691650390625, 0.09326171875, -0.9521484375, -0.024932861328125, -0.94775390625, -0.299560546875, -0.002536773681640625, 1.41796875, -0.06903076171875, -1.5927734375, 0.353515625, 3.63671875, -0.765625, -1.1142578125, 0.4287109375, -0.86865234375, -0.9267578125, -0.21826171875, -1.10546875, 0.29296875, -0.225830078125, 0.5400390625, -0.45556640625, -0.68701171875, -0.79150390625, -1.0810546875, 0.25439453125, -1.2998046875, -0.494140625, -0.1510009765625, 1.5615234375, -0.4248046875, -0.486572265625, 0.45458984375, 0.047637939453125, -0.11639404296875, 0.057403564453125, 0.130126953125, -0.10125732421875, -0.56201171875, 1.4765625, -1.7451171875, 1.34765625, -0.45703125, 0.873046875, -0.056121826171875, -0.8876953125, -0.986328125, 1.5654296875, 0.49853515625, 0.55859375, -0.2198486328125, 0.62548828125, 0.2734375, -0.63671875, -0.41259765625, -1.2705078125, 0.0665283203125, 1.3369140625, 0.90283203125, -0.77685546875, -1.5, -1.8525390625, -1.314453125, -0.86767578125, -0.331787109375, 0.1590576171875, 0.94775390625, -0.1771240234375, 1.638671875, -2.17578125, 0.58740234375, 0.424560546875, -0.3466796875, 0.642578125, 0.473388671875, 0.96435546875, 1.38671875, -0.91357421875, 1.0361328125, -0.67333984375, 1.5009765625]]]).to(device)
|
| 22 |
-
|
| 23 |
-
cond = [[prompt_embeds, {}]]
|
| 24 |
-
|
| 25 |
-
return (cond,)
|
| 26 |
-
|
| 27 |
-
NODE_CLASS_MAPPINGS = {
|
| 28 |
-
"LotusConditioning" : LotusConditioning,
|
| 29 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
comfy_extras/nodes_lt.py
DELETED
|
@@ -1,474 +0,0 @@
|
|
| 1 |
-
import io
|
| 2 |
-
import nodes
|
| 3 |
-
import node_helpers
|
| 4 |
-
import torch
|
| 5 |
-
import comfy.model_management
|
| 6 |
-
import comfy.model_sampling
|
| 7 |
-
import comfy.utils
|
| 8 |
-
import math
|
| 9 |
-
import numpy as np
|
| 10 |
-
import av
|
| 11 |
-
from comfy.ldm.lightricks.symmetric_patchifier import SymmetricPatchifier, latent_to_pixel_coords
|
| 12 |
-
|
| 13 |
-
class EmptyLTXVLatentVideo:
|
| 14 |
-
@classmethod
|
| 15 |
-
def INPUT_TYPES(s):
|
| 16 |
-
return {"required": { "width": ("INT", {"default": 768, "min": 64, "max": nodes.MAX_RESOLUTION, "step": 32}),
|
| 17 |
-
"height": ("INT", {"default": 512, "min": 64, "max": nodes.MAX_RESOLUTION, "step": 32}),
|
| 18 |
-
"length": ("INT", {"default": 97, "min": 1, "max": nodes.MAX_RESOLUTION, "step": 8}),
|
| 19 |
-
"batch_size": ("INT", {"default": 1, "min": 1, "max": 4096})}}
|
| 20 |
-
RETURN_TYPES = ("LATENT",)
|
| 21 |
-
FUNCTION = "generate"
|
| 22 |
-
|
| 23 |
-
CATEGORY = "latent/video/ltxv"
|
| 24 |
-
|
| 25 |
-
def generate(self, width, height, length, batch_size=1):
|
| 26 |
-
latent = torch.zeros([batch_size, 128, ((length - 1) // 8) + 1, height // 32, width // 32], device=comfy.model_management.intermediate_device())
|
| 27 |
-
return ({"samples": latent}, )
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
class LTXVImgToVideo:
|
| 31 |
-
@classmethod
|
| 32 |
-
def INPUT_TYPES(s):
|
| 33 |
-
return {"required": {"positive": ("CONDITIONING", ),
|
| 34 |
-
"negative": ("CONDITIONING", ),
|
| 35 |
-
"vae": ("VAE",),
|
| 36 |
-
"image": ("IMAGE",),
|
| 37 |
-
"width": ("INT", {"default": 768, "min": 64, "max": nodes.MAX_RESOLUTION, "step": 32}),
|
| 38 |
-
"height": ("INT", {"default": 512, "min": 64, "max": nodes.MAX_RESOLUTION, "step": 32}),
|
| 39 |
-
"length": ("INT", {"default": 97, "min": 9, "max": nodes.MAX_RESOLUTION, "step": 8}),
|
| 40 |
-
"batch_size": ("INT", {"default": 1, "min": 1, "max": 4096}),
|
| 41 |
-
"strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0}),
|
| 42 |
-
}}
|
| 43 |
-
|
| 44 |
-
RETURN_TYPES = ("CONDITIONING", "CONDITIONING", "LATENT")
|
| 45 |
-
RETURN_NAMES = ("positive", "negative", "latent")
|
| 46 |
-
|
| 47 |
-
CATEGORY = "conditioning/video_models"
|
| 48 |
-
FUNCTION = "generate"
|
| 49 |
-
|
| 50 |
-
def generate(self, positive, negative, image, vae, width, height, length, batch_size, strength):
|
| 51 |
-
pixels = comfy.utils.common_upscale(image.movedim(-1, 1), width, height, "bilinear", "center").movedim(1, -1)
|
| 52 |
-
encode_pixels = pixels[:, :, :, :3]
|
| 53 |
-
t = vae.encode(encode_pixels)
|
| 54 |
-
|
| 55 |
-
latent = torch.zeros([batch_size, 128, ((length - 1) // 8) + 1, height // 32, width // 32], device=comfy.model_management.intermediate_device())
|
| 56 |
-
latent[:, :, :t.shape[2]] = t
|
| 57 |
-
|
| 58 |
-
conditioning_latent_frames_mask = torch.ones(
|
| 59 |
-
(batch_size, 1, latent.shape[2], 1, 1),
|
| 60 |
-
dtype=torch.float32,
|
| 61 |
-
device=latent.device,
|
| 62 |
-
)
|
| 63 |
-
conditioning_latent_frames_mask[:, :, :t.shape[2]] = 1.0 - strength
|
| 64 |
-
|
| 65 |
-
return (positive, negative, {"samples": latent, "noise_mask": conditioning_latent_frames_mask}, )
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
def conditioning_get_any_value(conditioning, key, default=None):
|
| 69 |
-
for t in conditioning:
|
| 70 |
-
if key in t[1]:
|
| 71 |
-
return t[1][key]
|
| 72 |
-
return default
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
def get_noise_mask(latent):
|
| 76 |
-
noise_mask = latent.get("noise_mask", None)
|
| 77 |
-
latent_image = latent["samples"]
|
| 78 |
-
if noise_mask is None:
|
| 79 |
-
batch_size, _, latent_length, _, _ = latent_image.shape
|
| 80 |
-
noise_mask = torch.ones(
|
| 81 |
-
(batch_size, 1, latent_length, 1, 1),
|
| 82 |
-
dtype=torch.float32,
|
| 83 |
-
device=latent_image.device,
|
| 84 |
-
)
|
| 85 |
-
else:
|
| 86 |
-
noise_mask = noise_mask.clone()
|
| 87 |
-
return noise_mask
|
| 88 |
-
|
| 89 |
-
def get_keyframe_idxs(cond):
|
| 90 |
-
keyframe_idxs = conditioning_get_any_value(cond, "keyframe_idxs", None)
|
| 91 |
-
if keyframe_idxs is None:
|
| 92 |
-
return None, 0
|
| 93 |
-
num_keyframes = torch.unique(keyframe_idxs[:, 0]).shape[0]
|
| 94 |
-
return keyframe_idxs, num_keyframes
|
| 95 |
-
|
| 96 |
-
class LTXVAddGuide:
|
| 97 |
-
@classmethod
|
| 98 |
-
def INPUT_TYPES(s):
|
| 99 |
-
return {"required": {"positive": ("CONDITIONING", ),
|
| 100 |
-
"negative": ("CONDITIONING", ),
|
| 101 |
-
"vae": ("VAE",),
|
| 102 |
-
"latent": ("LATENT",),
|
| 103 |
-
"image": ("IMAGE", {"tooltip": "Image or video to condition the latent video on. Must be 8*n + 1 frames."
|
| 104 |
-
"If the video is not 8*n + 1 frames, it will be cropped to the nearest 8*n + 1 frames."}),
|
| 105 |
-
"frame_idx": ("INT", {"default": 0, "min": -9999, "max": 9999,
|
| 106 |
-
"tooltip": "Frame index to start the conditioning at. For single-frame images or "
|
| 107 |
-
"videos with 1-8 frames, any frame_idx value is acceptable. For videos with 9+ "
|
| 108 |
-
"frames, frame_idx must be divisible by 8, otherwise it will be rounded down to "
|
| 109 |
-
"the nearest multiple of 8. Negative values are counted from the end of the video."}),
|
| 110 |
-
"strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}),
|
| 111 |
-
}
|
| 112 |
-
}
|
| 113 |
-
|
| 114 |
-
RETURN_TYPES = ("CONDITIONING", "CONDITIONING", "LATENT")
|
| 115 |
-
RETURN_NAMES = ("positive", "negative", "latent")
|
| 116 |
-
|
| 117 |
-
CATEGORY = "conditioning/video_models"
|
| 118 |
-
FUNCTION = "generate"
|
| 119 |
-
|
| 120 |
-
def __init__(self):
|
| 121 |
-
self._num_prefix_frames = 2
|
| 122 |
-
self._patchifier = SymmetricPatchifier(1)
|
| 123 |
-
|
| 124 |
-
def encode(self, vae, latent_width, latent_height, images, scale_factors):
|
| 125 |
-
time_scale_factor, width_scale_factor, height_scale_factor = scale_factors
|
| 126 |
-
images = images[:(images.shape[0] - 1) // time_scale_factor * time_scale_factor + 1]
|
| 127 |
-
pixels = comfy.utils.common_upscale(images.movedim(-1, 1), latent_width * width_scale_factor, latent_height * height_scale_factor, "bilinear", crop="disabled").movedim(1, -1)
|
| 128 |
-
encode_pixels = pixels[:, :, :, :3]
|
| 129 |
-
t = vae.encode(encode_pixels)
|
| 130 |
-
return encode_pixels, t
|
| 131 |
-
|
| 132 |
-
def get_latent_index(self, cond, latent_length, guide_length, frame_idx, scale_factors):
|
| 133 |
-
time_scale_factor, _, _ = scale_factors
|
| 134 |
-
_, num_keyframes = get_keyframe_idxs(cond)
|
| 135 |
-
latent_count = latent_length - num_keyframes
|
| 136 |
-
frame_idx = frame_idx if frame_idx >= 0 else max((latent_count - 1) * time_scale_factor + 1 + frame_idx, 0)
|
| 137 |
-
if guide_length > 1 and frame_idx != 0:
|
| 138 |
-
frame_idx = (frame_idx - 1) // time_scale_factor * time_scale_factor + 1 # frame index - 1 must be divisible by 8 or frame_idx == 0
|
| 139 |
-
|
| 140 |
-
latent_idx = (frame_idx + time_scale_factor - 1) // time_scale_factor
|
| 141 |
-
|
| 142 |
-
return frame_idx, latent_idx
|
| 143 |
-
|
| 144 |
-
def add_keyframe_index(self, cond, frame_idx, guiding_latent, scale_factors):
|
| 145 |
-
keyframe_idxs, _ = get_keyframe_idxs(cond)
|
| 146 |
-
_, latent_coords = self._patchifier.patchify(guiding_latent)
|
| 147 |
-
pixel_coords = latent_to_pixel_coords(latent_coords, scale_factors, causal_fix=frame_idx == 0) # we need the causal fix only if we're placing the new latents at index 0
|
| 148 |
-
pixel_coords[:, 0] += frame_idx
|
| 149 |
-
if keyframe_idxs is None:
|
| 150 |
-
keyframe_idxs = pixel_coords
|
| 151 |
-
else:
|
| 152 |
-
keyframe_idxs = torch.cat([keyframe_idxs, pixel_coords], dim=2)
|
| 153 |
-
return node_helpers.conditioning_set_values(cond, {"keyframe_idxs": keyframe_idxs})
|
| 154 |
-
|
| 155 |
-
def append_keyframe(self, positive, negative, frame_idx, latent_image, noise_mask, guiding_latent, strength, scale_factors):
|
| 156 |
-
_, latent_idx = self.get_latent_index(
|
| 157 |
-
cond=positive,
|
| 158 |
-
latent_length=latent_image.shape[2],
|
| 159 |
-
guide_length=guiding_latent.shape[2],
|
| 160 |
-
frame_idx=frame_idx,
|
| 161 |
-
scale_factors=scale_factors,
|
| 162 |
-
)
|
| 163 |
-
noise_mask[:, :, latent_idx:latent_idx + guiding_latent.shape[2]] = 1.0
|
| 164 |
-
|
| 165 |
-
positive = self.add_keyframe_index(positive, frame_idx, guiding_latent, scale_factors)
|
| 166 |
-
negative = self.add_keyframe_index(negative, frame_idx, guiding_latent, scale_factors)
|
| 167 |
-
|
| 168 |
-
mask = torch.full(
|
| 169 |
-
(noise_mask.shape[0], 1, guiding_latent.shape[2], 1, 1),
|
| 170 |
-
1.0 - strength,
|
| 171 |
-
dtype=noise_mask.dtype,
|
| 172 |
-
device=noise_mask.device,
|
| 173 |
-
)
|
| 174 |
-
|
| 175 |
-
latent_image = torch.cat([latent_image, guiding_latent], dim=2)
|
| 176 |
-
noise_mask = torch.cat([noise_mask, mask], dim=2)
|
| 177 |
-
return positive, negative, latent_image, noise_mask
|
| 178 |
-
|
| 179 |
-
def replace_latent_frames(self, latent_image, noise_mask, guiding_latent, latent_idx, strength):
|
| 180 |
-
cond_length = guiding_latent.shape[2]
|
| 181 |
-
assert latent_image.shape[2] >= latent_idx + cond_length, "Conditioning frames exceed the length of the latent sequence."
|
| 182 |
-
|
| 183 |
-
mask = torch.full(
|
| 184 |
-
(noise_mask.shape[0], 1, cond_length, 1, 1),
|
| 185 |
-
1.0 - strength,
|
| 186 |
-
dtype=noise_mask.dtype,
|
| 187 |
-
device=noise_mask.device,
|
| 188 |
-
)
|
| 189 |
-
|
| 190 |
-
latent_image = latent_image.clone()
|
| 191 |
-
noise_mask = noise_mask.clone()
|
| 192 |
-
|
| 193 |
-
latent_image[:, :, latent_idx : latent_idx + cond_length] = guiding_latent
|
| 194 |
-
noise_mask[:, :, latent_idx : latent_idx + cond_length] = mask
|
| 195 |
-
|
| 196 |
-
return latent_image, noise_mask
|
| 197 |
-
|
| 198 |
-
def generate(self, positive, negative, vae, latent, image, frame_idx, strength):
|
| 199 |
-
scale_factors = vae.downscale_index_formula
|
| 200 |
-
latent_image = latent["samples"]
|
| 201 |
-
noise_mask = get_noise_mask(latent)
|
| 202 |
-
|
| 203 |
-
_, _, latent_length, latent_height, latent_width = latent_image.shape
|
| 204 |
-
image, t = self.encode(vae, latent_width, latent_height, image, scale_factors)
|
| 205 |
-
|
| 206 |
-
frame_idx, latent_idx = self.get_latent_index(positive, latent_length, len(image), frame_idx, scale_factors)
|
| 207 |
-
assert latent_idx + t.shape[2] <= latent_length, "Conditioning frames exceed the length of the latent sequence."
|
| 208 |
-
|
| 209 |
-
num_prefix_frames = min(self._num_prefix_frames, t.shape[2])
|
| 210 |
-
|
| 211 |
-
positive, negative, latent_image, noise_mask = self.append_keyframe(
|
| 212 |
-
positive,
|
| 213 |
-
negative,
|
| 214 |
-
frame_idx,
|
| 215 |
-
latent_image,
|
| 216 |
-
noise_mask,
|
| 217 |
-
t[:, :, :num_prefix_frames],
|
| 218 |
-
strength,
|
| 219 |
-
scale_factors,
|
| 220 |
-
)
|
| 221 |
-
|
| 222 |
-
latent_idx += num_prefix_frames
|
| 223 |
-
|
| 224 |
-
t = t[:, :, num_prefix_frames:]
|
| 225 |
-
if t.shape[2] == 0:
|
| 226 |
-
return (positive, negative, {"samples": latent_image, "noise_mask": noise_mask},)
|
| 227 |
-
|
| 228 |
-
latent_image, noise_mask = self.replace_latent_frames(
|
| 229 |
-
latent_image,
|
| 230 |
-
noise_mask,
|
| 231 |
-
t,
|
| 232 |
-
latent_idx,
|
| 233 |
-
strength,
|
| 234 |
-
)
|
| 235 |
-
|
| 236 |
-
return (positive, negative, {"samples": latent_image, "noise_mask": noise_mask},)
|
| 237 |
-
|
| 238 |
-
|
| 239 |
-
class LTXVCropGuides:
|
| 240 |
-
@classmethod
|
| 241 |
-
def INPUT_TYPES(s):
|
| 242 |
-
return {"required": {"positive": ("CONDITIONING", ),
|
| 243 |
-
"negative": ("CONDITIONING", ),
|
| 244 |
-
"latent": ("LATENT",),
|
| 245 |
-
}
|
| 246 |
-
}
|
| 247 |
-
|
| 248 |
-
RETURN_TYPES = ("CONDITIONING", "CONDITIONING", "LATENT")
|
| 249 |
-
RETURN_NAMES = ("positive", "negative", "latent")
|
| 250 |
-
|
| 251 |
-
CATEGORY = "conditioning/video_models"
|
| 252 |
-
FUNCTION = "crop"
|
| 253 |
-
|
| 254 |
-
def __init__(self):
|
| 255 |
-
self._patchifier = SymmetricPatchifier(1)
|
| 256 |
-
|
| 257 |
-
def crop(self, positive, negative, latent):
|
| 258 |
-
latent_image = latent["samples"].clone()
|
| 259 |
-
noise_mask = get_noise_mask(latent)
|
| 260 |
-
|
| 261 |
-
_, num_keyframes = get_keyframe_idxs(positive)
|
| 262 |
-
if num_keyframes == 0:
|
| 263 |
-
return (positive, negative, {"samples": latent_image, "noise_mask": noise_mask},)
|
| 264 |
-
|
| 265 |
-
latent_image = latent_image[:, :, :-num_keyframes]
|
| 266 |
-
noise_mask = noise_mask[:, :, :-num_keyframes]
|
| 267 |
-
|
| 268 |
-
positive = node_helpers.conditioning_set_values(positive, {"keyframe_idxs": None})
|
| 269 |
-
negative = node_helpers.conditioning_set_values(negative, {"keyframe_idxs": None})
|
| 270 |
-
|
| 271 |
-
return (positive, negative, {"samples": latent_image, "noise_mask": noise_mask},)
|
| 272 |
-
|
| 273 |
-
|
| 274 |
-
class LTXVConditioning:
|
| 275 |
-
@classmethod
|
| 276 |
-
def INPUT_TYPES(s):
|
| 277 |
-
return {"required": {"positive": ("CONDITIONING", ),
|
| 278 |
-
"negative": ("CONDITIONING", ),
|
| 279 |
-
"frame_rate": ("FLOAT", {"default": 25.0, "min": 0.0, "max": 1000.0, "step": 0.01}),
|
| 280 |
-
}}
|
| 281 |
-
RETURN_TYPES = ("CONDITIONING", "CONDITIONING")
|
| 282 |
-
RETURN_NAMES = ("positive", "negative")
|
| 283 |
-
FUNCTION = "append"
|
| 284 |
-
|
| 285 |
-
CATEGORY = "conditioning/video_models"
|
| 286 |
-
|
| 287 |
-
def append(self, positive, negative, frame_rate):
|
| 288 |
-
positive = node_helpers.conditioning_set_values(positive, {"frame_rate": frame_rate})
|
| 289 |
-
negative = node_helpers.conditioning_set_values(negative, {"frame_rate": frame_rate})
|
| 290 |
-
return (positive, negative)
|
| 291 |
-
|
| 292 |
-
|
| 293 |
-
class ModelSamplingLTXV:
|
| 294 |
-
@classmethod
|
| 295 |
-
def INPUT_TYPES(s):
|
| 296 |
-
return {"required": { "model": ("MODEL",),
|
| 297 |
-
"max_shift": ("FLOAT", {"default": 2.05, "min": 0.0, "max": 100.0, "step":0.01}),
|
| 298 |
-
"base_shift": ("FLOAT", {"default": 0.95, "min": 0.0, "max": 100.0, "step":0.01}),
|
| 299 |
-
},
|
| 300 |
-
"optional": {"latent": ("LATENT",), }
|
| 301 |
-
}
|
| 302 |
-
|
| 303 |
-
RETURN_TYPES = ("MODEL",)
|
| 304 |
-
FUNCTION = "patch"
|
| 305 |
-
|
| 306 |
-
CATEGORY = "advanced/model"
|
| 307 |
-
|
| 308 |
-
def patch(self, model, max_shift, base_shift, latent=None):
|
| 309 |
-
m = model.clone()
|
| 310 |
-
|
| 311 |
-
if latent is None:
|
| 312 |
-
tokens = 4096
|
| 313 |
-
else:
|
| 314 |
-
tokens = math.prod(latent["samples"].shape[2:])
|
| 315 |
-
|
| 316 |
-
x1 = 1024
|
| 317 |
-
x2 = 4096
|
| 318 |
-
mm = (max_shift - base_shift) / (x2 - x1)
|
| 319 |
-
b = base_shift - mm * x1
|
| 320 |
-
shift = (tokens) * mm + b
|
| 321 |
-
|
| 322 |
-
sampling_base = comfy.model_sampling.ModelSamplingFlux
|
| 323 |
-
sampling_type = comfy.model_sampling.CONST
|
| 324 |
-
|
| 325 |
-
class ModelSamplingAdvanced(sampling_base, sampling_type):
|
| 326 |
-
pass
|
| 327 |
-
|
| 328 |
-
model_sampling = ModelSamplingAdvanced(model.model.model_config)
|
| 329 |
-
model_sampling.set_parameters(shift=shift)
|
| 330 |
-
m.add_object_patch("model_sampling", model_sampling)
|
| 331 |
-
|
| 332 |
-
return (m, )
|
| 333 |
-
|
| 334 |
-
|
| 335 |
-
class LTXVScheduler:
|
| 336 |
-
@classmethod
|
| 337 |
-
def INPUT_TYPES(s):
|
| 338 |
-
return {"required":
|
| 339 |
-
{"steps": ("INT", {"default": 20, "min": 1, "max": 10000}),
|
| 340 |
-
"max_shift": ("FLOAT", {"default": 2.05, "min": 0.0, "max": 100.0, "step":0.01}),
|
| 341 |
-
"base_shift": ("FLOAT", {"default": 0.95, "min": 0.0, "max": 100.0, "step":0.01}),
|
| 342 |
-
"stretch": ("BOOLEAN", {
|
| 343 |
-
"default": True,
|
| 344 |
-
"tooltip": "Stretch the sigmas to be in the range [terminal, 1]."
|
| 345 |
-
}),
|
| 346 |
-
"terminal": (
|
| 347 |
-
"FLOAT",
|
| 348 |
-
{
|
| 349 |
-
"default": 0.1, "min": 0.0, "max": 0.99, "step": 0.01,
|
| 350 |
-
"tooltip": "The terminal value of the sigmas after stretching."
|
| 351 |
-
},
|
| 352 |
-
),
|
| 353 |
-
},
|
| 354 |
-
"optional": {"latent": ("LATENT",), }
|
| 355 |
-
}
|
| 356 |
-
|
| 357 |
-
RETURN_TYPES = ("SIGMAS",)
|
| 358 |
-
CATEGORY = "sampling/custom_sampling/schedulers"
|
| 359 |
-
|
| 360 |
-
FUNCTION = "get_sigmas"
|
| 361 |
-
|
| 362 |
-
def get_sigmas(self, steps, max_shift, base_shift, stretch, terminal, latent=None):
|
| 363 |
-
if latent is None:
|
| 364 |
-
tokens = 4096
|
| 365 |
-
else:
|
| 366 |
-
tokens = math.prod(latent["samples"].shape[2:])
|
| 367 |
-
|
| 368 |
-
sigmas = torch.linspace(1.0, 0.0, steps + 1)
|
| 369 |
-
|
| 370 |
-
x1 = 1024
|
| 371 |
-
x2 = 4096
|
| 372 |
-
mm = (max_shift - base_shift) / (x2 - x1)
|
| 373 |
-
b = base_shift - mm * x1
|
| 374 |
-
sigma_shift = (tokens) * mm + b
|
| 375 |
-
|
| 376 |
-
power = 1
|
| 377 |
-
sigmas = torch.where(
|
| 378 |
-
sigmas != 0,
|
| 379 |
-
math.exp(sigma_shift) / (math.exp(sigma_shift) + (1 / sigmas - 1) ** power),
|
| 380 |
-
0,
|
| 381 |
-
)
|
| 382 |
-
|
| 383 |
-
# Stretch sigmas so that its final value matches the given terminal value.
|
| 384 |
-
if stretch:
|
| 385 |
-
non_zero_mask = sigmas != 0
|
| 386 |
-
non_zero_sigmas = sigmas[non_zero_mask]
|
| 387 |
-
one_minus_z = 1.0 - non_zero_sigmas
|
| 388 |
-
scale_factor = one_minus_z[-1] / (1.0 - terminal)
|
| 389 |
-
stretched = 1.0 - (one_minus_z / scale_factor)
|
| 390 |
-
sigmas[non_zero_mask] = stretched
|
| 391 |
-
|
| 392 |
-
return (sigmas,)
|
| 393 |
-
|
| 394 |
-
def encode_single_frame(output_file, image_array: np.ndarray, crf):
|
| 395 |
-
container = av.open(output_file, "w", format="mp4")
|
| 396 |
-
try:
|
| 397 |
-
stream = container.add_stream(
|
| 398 |
-
"libx264", rate=1, options={"crf": str(crf), "preset": "veryfast"}
|
| 399 |
-
)
|
| 400 |
-
stream.height = image_array.shape[0]
|
| 401 |
-
stream.width = image_array.shape[1]
|
| 402 |
-
av_frame = av.VideoFrame.from_ndarray(image_array, format="rgb24").reformat(
|
| 403 |
-
format="yuv420p"
|
| 404 |
-
)
|
| 405 |
-
container.mux(stream.encode(av_frame))
|
| 406 |
-
container.mux(stream.encode())
|
| 407 |
-
finally:
|
| 408 |
-
container.close()
|
| 409 |
-
|
| 410 |
-
|
| 411 |
-
def decode_single_frame(video_file):
|
| 412 |
-
container = av.open(video_file)
|
| 413 |
-
try:
|
| 414 |
-
stream = next(s for s in container.streams if s.type == "video")
|
| 415 |
-
frame = next(container.decode(stream))
|
| 416 |
-
finally:
|
| 417 |
-
container.close()
|
| 418 |
-
return frame.to_ndarray(format="rgb24")
|
| 419 |
-
|
| 420 |
-
|
| 421 |
-
def preprocess(image: torch.Tensor, crf=29):
|
| 422 |
-
if crf == 0:
|
| 423 |
-
return image
|
| 424 |
-
|
| 425 |
-
image_array = (image[:(image.shape[0] // 2) * 2, :(image.shape[1] // 2) * 2] * 255.0).byte().cpu().numpy()
|
| 426 |
-
with io.BytesIO() as output_file:
|
| 427 |
-
encode_single_frame(output_file, image_array, crf)
|
| 428 |
-
video_bytes = output_file.getvalue()
|
| 429 |
-
with io.BytesIO(video_bytes) as video_file:
|
| 430 |
-
image_array = decode_single_frame(video_file)
|
| 431 |
-
tensor = torch.tensor(image_array, dtype=image.dtype, device=image.device) / 255.0
|
| 432 |
-
return tensor
|
| 433 |
-
|
| 434 |
-
|
| 435 |
-
class LTXVPreprocess:
|
| 436 |
-
@classmethod
|
| 437 |
-
def INPUT_TYPES(s):
|
| 438 |
-
return {
|
| 439 |
-
"required": {
|
| 440 |
-
"image": ("IMAGE",),
|
| 441 |
-
"img_compression": (
|
| 442 |
-
"INT",
|
| 443 |
-
{
|
| 444 |
-
"default": 35,
|
| 445 |
-
"min": 0,
|
| 446 |
-
"max": 100,
|
| 447 |
-
"tooltip": "Amount of compression to apply on image.",
|
| 448 |
-
},
|
| 449 |
-
),
|
| 450 |
-
}
|
| 451 |
-
}
|
| 452 |
-
|
| 453 |
-
FUNCTION = "preprocess"
|
| 454 |
-
RETURN_TYPES = ("IMAGE",)
|
| 455 |
-
RETURN_NAMES = ("output_image",)
|
| 456 |
-
CATEGORY = "image"
|
| 457 |
-
|
| 458 |
-
def preprocess(self, image, img_compression):
|
| 459 |
-
output_images = []
|
| 460 |
-
for i in range(image.shape[0]):
|
| 461 |
-
output_images.append(preprocess(image[i], img_compression))
|
| 462 |
-
return (torch.stack(output_images),)
|
| 463 |
-
|
| 464 |
-
|
| 465 |
-
NODE_CLASS_MAPPINGS = {
|
| 466 |
-
"EmptyLTXVLatentVideo": EmptyLTXVLatentVideo,
|
| 467 |
-
"LTXVImgToVideo": LTXVImgToVideo,
|
| 468 |
-
"ModelSamplingLTXV": ModelSamplingLTXV,
|
| 469 |
-
"LTXVConditioning": LTXVConditioning,
|
| 470 |
-
"LTXVScheduler": LTXVScheduler,
|
| 471 |
-
"LTXVAddGuide": LTXVAddGuide,
|
| 472 |
-
"LTXVPreprocess": LTXVPreprocess,
|
| 473 |
-
"LTXVCropGuides": LTXVCropGuides,
|
| 474 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
comfy_extras/nodes_lumina2.py
DELETED
|
@@ -1,104 +0,0 @@
|
|
| 1 |
-
from comfy.comfy_types import IO, ComfyNodeABC, InputTypeDict
|
| 2 |
-
import torch
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
class RenormCFG:
|
| 6 |
-
@classmethod
|
| 7 |
-
def INPUT_TYPES(s):
|
| 8 |
-
return {"required": { "model": ("MODEL",),
|
| 9 |
-
"cfg_trunc": ("FLOAT", {"default": 100, "min": 0.0, "max": 100.0, "step": 0.01}),
|
| 10 |
-
"renorm_cfg": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 100.0, "step": 0.01}),
|
| 11 |
-
}}
|
| 12 |
-
RETURN_TYPES = ("MODEL",)
|
| 13 |
-
FUNCTION = "patch"
|
| 14 |
-
|
| 15 |
-
CATEGORY = "advanced/model"
|
| 16 |
-
|
| 17 |
-
def patch(self, model, cfg_trunc, renorm_cfg):
|
| 18 |
-
def renorm_cfg_func(args):
|
| 19 |
-
cond_denoised = args["cond_denoised"]
|
| 20 |
-
uncond_denoised = args["uncond_denoised"]
|
| 21 |
-
cond_scale = args["cond_scale"]
|
| 22 |
-
timestep = args["timestep"]
|
| 23 |
-
x_orig = args["input"]
|
| 24 |
-
in_channels = model.model.diffusion_model.in_channels
|
| 25 |
-
|
| 26 |
-
if timestep[0] < cfg_trunc:
|
| 27 |
-
cond_eps, uncond_eps = cond_denoised[:, :in_channels], uncond_denoised[:, :in_channels]
|
| 28 |
-
cond_rest, _ = cond_denoised[:, in_channels:], uncond_denoised[:, in_channels:]
|
| 29 |
-
half_eps = uncond_eps + cond_scale * (cond_eps - uncond_eps)
|
| 30 |
-
half_rest = cond_rest
|
| 31 |
-
|
| 32 |
-
if float(renorm_cfg) > 0.0:
|
| 33 |
-
ori_pos_norm = torch.linalg.vector_norm(cond_eps
|
| 34 |
-
, dim=tuple(range(1, len(cond_eps.shape))), keepdim=True
|
| 35 |
-
)
|
| 36 |
-
max_new_norm = ori_pos_norm * float(renorm_cfg)
|
| 37 |
-
new_pos_norm = torch.linalg.vector_norm(
|
| 38 |
-
half_eps, dim=tuple(range(1, len(half_eps.shape))), keepdim=True
|
| 39 |
-
)
|
| 40 |
-
if new_pos_norm >= max_new_norm:
|
| 41 |
-
half_eps = half_eps * (max_new_norm / new_pos_norm)
|
| 42 |
-
else:
|
| 43 |
-
cond_eps, uncond_eps = cond_denoised[:, :in_channels], uncond_denoised[:, :in_channels]
|
| 44 |
-
cond_rest, _ = cond_denoised[:, in_channels:], uncond_denoised[:, in_channels:]
|
| 45 |
-
half_eps = cond_eps
|
| 46 |
-
half_rest = cond_rest
|
| 47 |
-
|
| 48 |
-
cfg_result = torch.cat([half_eps, half_rest], dim=1)
|
| 49 |
-
|
| 50 |
-
# cfg_result = uncond_denoised + (cond_denoised - uncond_denoised) * cond_scale
|
| 51 |
-
|
| 52 |
-
return x_orig - cfg_result
|
| 53 |
-
|
| 54 |
-
m = model.clone()
|
| 55 |
-
m.set_model_sampler_cfg_function(renorm_cfg_func)
|
| 56 |
-
return (m, )
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
class CLIPTextEncodeLumina2(ComfyNodeABC):
|
| 60 |
-
SYSTEM_PROMPT = {
|
| 61 |
-
"superior": "You are an assistant designed to generate superior images with the superior "\
|
| 62 |
-
"degree of image-text alignment based on textual prompts or user prompts.",
|
| 63 |
-
"alignment": "You are an assistant designed to generate high-quality images with the "\
|
| 64 |
-
"highest degree of image-text alignment based on textual prompts."
|
| 65 |
-
}
|
| 66 |
-
SYSTEM_PROMPT_TIP = "Lumina2 provide two types of system prompts:" \
|
| 67 |
-
"Superior: You are an assistant designed to generate superior images with the superior "\
|
| 68 |
-
"degree of image-text alignment based on textual prompts or user prompts. "\
|
| 69 |
-
"Alignment: You are an assistant designed to generate high-quality images with the highest "\
|
| 70 |
-
"degree of image-text alignment based on textual prompts."
|
| 71 |
-
@classmethod
|
| 72 |
-
def INPUT_TYPES(s) -> InputTypeDict:
|
| 73 |
-
return {
|
| 74 |
-
"required": {
|
| 75 |
-
"system_prompt": (list(CLIPTextEncodeLumina2.SYSTEM_PROMPT.keys()), {"tooltip": CLIPTextEncodeLumina2.SYSTEM_PROMPT_TIP}),
|
| 76 |
-
"user_prompt": (IO.STRING, {"multiline": True, "dynamicPrompts": True, "tooltip": "The text to be encoded."}),
|
| 77 |
-
"clip": (IO.CLIP, {"tooltip": "The CLIP model used for encoding the text."})
|
| 78 |
-
}
|
| 79 |
-
}
|
| 80 |
-
RETURN_TYPES = (IO.CONDITIONING,)
|
| 81 |
-
OUTPUT_TOOLTIPS = ("A conditioning containing the embedded text used to guide the diffusion model.",)
|
| 82 |
-
FUNCTION = "encode"
|
| 83 |
-
|
| 84 |
-
CATEGORY = "conditioning"
|
| 85 |
-
DESCRIPTION = "Encodes a system prompt and a user prompt using a CLIP model into an embedding that can be used to guide the diffusion model towards generating specific images."
|
| 86 |
-
|
| 87 |
-
def encode(self, clip, user_prompt, system_prompt):
|
| 88 |
-
if clip is None:
|
| 89 |
-
raise RuntimeError("ERROR: clip input is invalid: None\n\nIf the clip is from a checkpoint loader node your checkpoint does not contain a valid clip or text encoder model.")
|
| 90 |
-
system_prompt = CLIPTextEncodeLumina2.SYSTEM_PROMPT[system_prompt]
|
| 91 |
-
prompt = f'{system_prompt} <Prompt Start> {user_prompt}'
|
| 92 |
-
tokens = clip.tokenize(prompt)
|
| 93 |
-
return (clip.encode_from_tokens_scheduled(tokens), )
|
| 94 |
-
|
| 95 |
-
|
| 96 |
-
NODE_CLASS_MAPPINGS = {
|
| 97 |
-
"CLIPTextEncodeLumina2": CLIPTextEncodeLumina2,
|
| 98 |
-
"RenormCFG": RenormCFG
|
| 99 |
-
}
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
NODE_DISPLAY_NAME_MAPPINGS = {
|
| 103 |
-
"CLIPTextEncodeLumina2": "CLIP Text Encode for Lumina2",
|
| 104 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
comfy_extras/nodes_mahiro.py
DELETED
|
@@ -1,41 +0,0 @@
|
|
| 1 |
-
import torch
|
| 2 |
-
import torch.nn.functional as F
|
| 3 |
-
|
| 4 |
-
class Mahiro:
|
| 5 |
-
@classmethod
|
| 6 |
-
def INPUT_TYPES(s):
|
| 7 |
-
return {"required": {"model": ("MODEL",),
|
| 8 |
-
}}
|
| 9 |
-
RETURN_TYPES = ("MODEL",)
|
| 10 |
-
RETURN_NAMES = ("patched_model",)
|
| 11 |
-
FUNCTION = "patch"
|
| 12 |
-
CATEGORY = "_for_testing"
|
| 13 |
-
DESCRIPTION = "Modify the guidance to scale more on the 'direction' of the positive prompt rather than the difference between the negative prompt."
|
| 14 |
-
def patch(self, model):
|
| 15 |
-
m = model.clone()
|
| 16 |
-
def mahiro_normd(args):
|
| 17 |
-
scale: float = args['cond_scale']
|
| 18 |
-
cond_p: torch.Tensor = args['cond_denoised']
|
| 19 |
-
uncond_p: torch.Tensor = args['uncond_denoised']
|
| 20 |
-
#naive leap
|
| 21 |
-
leap = cond_p * scale
|
| 22 |
-
#sim with uncond leap
|
| 23 |
-
u_leap = uncond_p * scale
|
| 24 |
-
cfg = args["denoised"]
|
| 25 |
-
merge = (leap + cfg) / 2
|
| 26 |
-
normu = torch.sqrt(u_leap.abs()) * u_leap.sign()
|
| 27 |
-
normm = torch.sqrt(merge.abs()) * merge.sign()
|
| 28 |
-
sim = F.cosine_similarity(normu, normm).mean()
|
| 29 |
-
simsc = 2 * (sim+1)
|
| 30 |
-
wm = (simsc*cfg + (4-simsc)*leap) / 4
|
| 31 |
-
return wm
|
| 32 |
-
m.set_model_sampler_post_cfg_function(mahiro_normd)
|
| 33 |
-
return (m, )
|
| 34 |
-
|
| 35 |
-
NODE_CLASS_MAPPINGS = {
|
| 36 |
-
"Mahiro": Mahiro
|
| 37 |
-
}
|
| 38 |
-
|
| 39 |
-
NODE_DISPLAY_NAME_MAPPINGS = {
|
| 40 |
-
"Mahiro": "Mahiro is so cute that she deserves a better guidance function!! (。・ω・。)",
|
| 41 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
comfy_extras/nodes_mask.py
DELETED
|
@@ -1,412 +0,0 @@
|
|
| 1 |
-
import numpy as np
|
| 2 |
-
import scipy.ndimage
|
| 3 |
-
import torch
|
| 4 |
-
import comfy.utils
|
| 5 |
-
import node_helpers
|
| 6 |
-
import folder_paths
|
| 7 |
-
import random
|
| 8 |
-
|
| 9 |
-
import nodes
|
| 10 |
-
from nodes import MAX_RESOLUTION
|
| 11 |
-
|
| 12 |
-
def composite(destination, source, x, y, mask = None, multiplier = 8, resize_source = False):
|
| 13 |
-
source = source.to(destination.device)
|
| 14 |
-
if resize_source:
|
| 15 |
-
source = torch.nn.functional.interpolate(source, size=(destination.shape[2], destination.shape[3]), mode="bilinear")
|
| 16 |
-
|
| 17 |
-
source = comfy.utils.repeat_to_batch_size(source, destination.shape[0])
|
| 18 |
-
|
| 19 |
-
x = max(-source.shape[3] * multiplier, min(x, destination.shape[3] * multiplier))
|
| 20 |
-
y = max(-source.shape[2] * multiplier, min(y, destination.shape[2] * multiplier))
|
| 21 |
-
|
| 22 |
-
left, top = (x // multiplier, y // multiplier)
|
| 23 |
-
right, bottom = (left + source.shape[3], top + source.shape[2],)
|
| 24 |
-
|
| 25 |
-
if mask is None:
|
| 26 |
-
mask = torch.ones_like(source)
|
| 27 |
-
else:
|
| 28 |
-
mask = mask.to(destination.device, copy=True)
|
| 29 |
-
mask = torch.nn.functional.interpolate(mask.reshape((-1, 1, mask.shape[-2], mask.shape[-1])), size=(source.shape[2], source.shape[3]), mode="bilinear")
|
| 30 |
-
mask = comfy.utils.repeat_to_batch_size(mask, source.shape[0])
|
| 31 |
-
|
| 32 |
-
# calculate the bounds of the source that will be overlapping the destination
|
| 33 |
-
# this prevents the source trying to overwrite latent pixels that are out of bounds
|
| 34 |
-
# of the destination
|
| 35 |
-
visible_width, visible_height = (destination.shape[3] - left + min(0, x), destination.shape[2] - top + min(0, y),)
|
| 36 |
-
|
| 37 |
-
mask = mask[:, :, :visible_height, :visible_width]
|
| 38 |
-
inverse_mask = torch.ones_like(mask) - mask
|
| 39 |
-
|
| 40 |
-
source_portion = mask * source[:, :, :visible_height, :visible_width]
|
| 41 |
-
destination_portion = inverse_mask * destination[:, :, top:bottom, left:right]
|
| 42 |
-
|
| 43 |
-
destination[:, :, top:bottom, left:right] = source_portion + destination_portion
|
| 44 |
-
return destination
|
| 45 |
-
|
| 46 |
-
class LatentCompositeMasked:
|
| 47 |
-
@classmethod
|
| 48 |
-
def INPUT_TYPES(s):
|
| 49 |
-
return {
|
| 50 |
-
"required": {
|
| 51 |
-
"destination": ("LATENT",),
|
| 52 |
-
"source": ("LATENT",),
|
| 53 |
-
"x": ("INT", {"default": 0, "min": 0, "max": MAX_RESOLUTION, "step": 8}),
|
| 54 |
-
"y": ("INT", {"default": 0, "min": 0, "max": MAX_RESOLUTION, "step": 8}),
|
| 55 |
-
"resize_source": ("BOOLEAN", {"default": False}),
|
| 56 |
-
},
|
| 57 |
-
"optional": {
|
| 58 |
-
"mask": ("MASK",),
|
| 59 |
-
}
|
| 60 |
-
}
|
| 61 |
-
RETURN_TYPES = ("LATENT",)
|
| 62 |
-
FUNCTION = "composite"
|
| 63 |
-
|
| 64 |
-
CATEGORY = "latent"
|
| 65 |
-
|
| 66 |
-
def composite(self, destination, source, x, y, resize_source, mask = None):
|
| 67 |
-
output = destination.copy()
|
| 68 |
-
destination = destination["samples"].clone()
|
| 69 |
-
source = source["samples"]
|
| 70 |
-
output["samples"] = composite(destination, source, x, y, mask, 8, resize_source)
|
| 71 |
-
return (output,)
|
| 72 |
-
|
| 73 |
-
class ImageCompositeMasked:
|
| 74 |
-
@classmethod
|
| 75 |
-
def INPUT_TYPES(s):
|
| 76 |
-
return {
|
| 77 |
-
"required": {
|
| 78 |
-
"destination": ("IMAGE",),
|
| 79 |
-
"source": ("IMAGE",),
|
| 80 |
-
"x": ("INT", {"default": 0, "min": 0, "max": MAX_RESOLUTION, "step": 1}),
|
| 81 |
-
"y": ("INT", {"default": 0, "min": 0, "max": MAX_RESOLUTION, "step": 1}),
|
| 82 |
-
"resize_source": ("BOOLEAN", {"default": False}),
|
| 83 |
-
},
|
| 84 |
-
"optional": {
|
| 85 |
-
"mask": ("MASK",),
|
| 86 |
-
}
|
| 87 |
-
}
|
| 88 |
-
RETURN_TYPES = ("IMAGE",)
|
| 89 |
-
FUNCTION = "composite"
|
| 90 |
-
|
| 91 |
-
CATEGORY = "image"
|
| 92 |
-
|
| 93 |
-
def composite(self, destination, source, x, y, resize_source, mask = None):
|
| 94 |
-
destination, source = node_helpers.image_alpha_fix(destination, source)
|
| 95 |
-
destination = destination.clone().movedim(-1, 1)
|
| 96 |
-
output = composite(destination, source.movedim(-1, 1), x, y, mask, 1, resize_source).movedim(1, -1)
|
| 97 |
-
return (output,)
|
| 98 |
-
|
| 99 |
-
class MaskToImage:
|
| 100 |
-
@classmethod
|
| 101 |
-
def INPUT_TYPES(s):
|
| 102 |
-
return {
|
| 103 |
-
"required": {
|
| 104 |
-
"mask": ("MASK",),
|
| 105 |
-
}
|
| 106 |
-
}
|
| 107 |
-
|
| 108 |
-
CATEGORY = "mask"
|
| 109 |
-
|
| 110 |
-
RETURN_TYPES = ("IMAGE",)
|
| 111 |
-
FUNCTION = "mask_to_image"
|
| 112 |
-
|
| 113 |
-
def mask_to_image(self, mask):
|
| 114 |
-
result = mask.reshape((-1, 1, mask.shape[-2], mask.shape[-1])).movedim(1, -1).expand(-1, -1, -1, 3)
|
| 115 |
-
return (result,)
|
| 116 |
-
|
| 117 |
-
class ImageToMask:
|
| 118 |
-
@classmethod
|
| 119 |
-
def INPUT_TYPES(s):
|
| 120 |
-
return {
|
| 121 |
-
"required": {
|
| 122 |
-
"image": ("IMAGE",),
|
| 123 |
-
"channel": (["red", "green", "blue", "alpha"],),
|
| 124 |
-
}
|
| 125 |
-
}
|
| 126 |
-
|
| 127 |
-
CATEGORY = "mask"
|
| 128 |
-
|
| 129 |
-
RETURN_TYPES = ("MASK",)
|
| 130 |
-
FUNCTION = "image_to_mask"
|
| 131 |
-
|
| 132 |
-
def image_to_mask(self, image, channel):
|
| 133 |
-
channels = ["red", "green", "blue", "alpha"]
|
| 134 |
-
mask = image[:, :, :, channels.index(channel)]
|
| 135 |
-
return (mask,)
|
| 136 |
-
|
| 137 |
-
class ImageColorToMask:
|
| 138 |
-
@classmethod
|
| 139 |
-
def INPUT_TYPES(s):
|
| 140 |
-
return {
|
| 141 |
-
"required": {
|
| 142 |
-
"image": ("IMAGE",),
|
| 143 |
-
"color": ("INT", {"default": 0, "min": 0, "max": 0xFFFFFF, "step": 1, "display": "color"}),
|
| 144 |
-
}
|
| 145 |
-
}
|
| 146 |
-
|
| 147 |
-
CATEGORY = "mask"
|
| 148 |
-
|
| 149 |
-
RETURN_TYPES = ("MASK",)
|
| 150 |
-
FUNCTION = "image_to_mask"
|
| 151 |
-
|
| 152 |
-
def image_to_mask(self, image, color):
|
| 153 |
-
temp = (torch.clamp(image, 0, 1.0) * 255.0).round().to(torch.int)
|
| 154 |
-
temp = torch.bitwise_left_shift(temp[:,:,:,0], 16) + torch.bitwise_left_shift(temp[:,:,:,1], 8) + temp[:,:,:,2]
|
| 155 |
-
mask = torch.where(temp == color, 1.0, 0).float()
|
| 156 |
-
return (mask,)
|
| 157 |
-
|
| 158 |
-
class SolidMask:
|
| 159 |
-
@classmethod
|
| 160 |
-
def INPUT_TYPES(cls):
|
| 161 |
-
return {
|
| 162 |
-
"required": {
|
| 163 |
-
"value": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}),
|
| 164 |
-
"width": ("INT", {"default": 512, "min": 1, "max": MAX_RESOLUTION, "step": 1}),
|
| 165 |
-
"height": ("INT", {"default": 512, "min": 1, "max": MAX_RESOLUTION, "step": 1}),
|
| 166 |
-
}
|
| 167 |
-
}
|
| 168 |
-
|
| 169 |
-
CATEGORY = "mask"
|
| 170 |
-
|
| 171 |
-
RETURN_TYPES = ("MASK",)
|
| 172 |
-
|
| 173 |
-
FUNCTION = "solid"
|
| 174 |
-
|
| 175 |
-
def solid(self, value, width, height):
|
| 176 |
-
out = torch.full((1, height, width), value, dtype=torch.float32, device="cpu")
|
| 177 |
-
return (out,)
|
| 178 |
-
|
| 179 |
-
class InvertMask:
|
| 180 |
-
@classmethod
|
| 181 |
-
def INPUT_TYPES(cls):
|
| 182 |
-
return {
|
| 183 |
-
"required": {
|
| 184 |
-
"mask": ("MASK",),
|
| 185 |
-
}
|
| 186 |
-
}
|
| 187 |
-
|
| 188 |
-
CATEGORY = "mask"
|
| 189 |
-
|
| 190 |
-
RETURN_TYPES = ("MASK",)
|
| 191 |
-
|
| 192 |
-
FUNCTION = "invert"
|
| 193 |
-
|
| 194 |
-
def invert(self, mask):
|
| 195 |
-
out = 1.0 - mask
|
| 196 |
-
return (out,)
|
| 197 |
-
|
| 198 |
-
class CropMask:
|
| 199 |
-
@classmethod
|
| 200 |
-
def INPUT_TYPES(cls):
|
| 201 |
-
return {
|
| 202 |
-
"required": {
|
| 203 |
-
"mask": ("MASK",),
|
| 204 |
-
"x": ("INT", {"default": 0, "min": 0, "max": MAX_RESOLUTION, "step": 1}),
|
| 205 |
-
"y": ("INT", {"default": 0, "min": 0, "max": MAX_RESOLUTION, "step": 1}),
|
| 206 |
-
"width": ("INT", {"default": 512, "min": 1, "max": MAX_RESOLUTION, "step": 1}),
|
| 207 |
-
"height": ("INT", {"default": 512, "min": 1, "max": MAX_RESOLUTION, "step": 1}),
|
| 208 |
-
}
|
| 209 |
-
}
|
| 210 |
-
|
| 211 |
-
CATEGORY = "mask"
|
| 212 |
-
|
| 213 |
-
RETURN_TYPES = ("MASK",)
|
| 214 |
-
|
| 215 |
-
FUNCTION = "crop"
|
| 216 |
-
|
| 217 |
-
def crop(self, mask, x, y, width, height):
|
| 218 |
-
mask = mask.reshape((-1, mask.shape[-2], mask.shape[-1]))
|
| 219 |
-
out = mask[:, y:y + height, x:x + width]
|
| 220 |
-
return (out,)
|
| 221 |
-
|
| 222 |
-
class MaskComposite:
|
| 223 |
-
@classmethod
|
| 224 |
-
def INPUT_TYPES(cls):
|
| 225 |
-
return {
|
| 226 |
-
"required": {
|
| 227 |
-
"destination": ("MASK",),
|
| 228 |
-
"source": ("MASK",),
|
| 229 |
-
"x": ("INT", {"default": 0, "min": 0, "max": MAX_RESOLUTION, "step": 1}),
|
| 230 |
-
"y": ("INT", {"default": 0, "min": 0, "max": MAX_RESOLUTION, "step": 1}),
|
| 231 |
-
"operation": (["multiply", "add", "subtract", "and", "or", "xor"],),
|
| 232 |
-
}
|
| 233 |
-
}
|
| 234 |
-
|
| 235 |
-
CATEGORY = "mask"
|
| 236 |
-
|
| 237 |
-
RETURN_TYPES = ("MASK",)
|
| 238 |
-
|
| 239 |
-
FUNCTION = "combine"
|
| 240 |
-
|
| 241 |
-
def combine(self, destination, source, x, y, operation):
|
| 242 |
-
output = destination.reshape((-1, destination.shape[-2], destination.shape[-1])).clone()
|
| 243 |
-
source = source.reshape((-1, source.shape[-2], source.shape[-1]))
|
| 244 |
-
|
| 245 |
-
left, top = (x, y,)
|
| 246 |
-
right, bottom = (min(left + source.shape[-1], destination.shape[-1]), min(top + source.shape[-2], destination.shape[-2]))
|
| 247 |
-
visible_width, visible_height = (right - left, bottom - top,)
|
| 248 |
-
|
| 249 |
-
source_portion = source[:, :visible_height, :visible_width]
|
| 250 |
-
destination_portion = output[:, top:bottom, left:right]
|
| 251 |
-
|
| 252 |
-
if operation == "multiply":
|
| 253 |
-
output[:, top:bottom, left:right] = destination_portion * source_portion
|
| 254 |
-
elif operation == "add":
|
| 255 |
-
output[:, top:bottom, left:right] = destination_portion + source_portion
|
| 256 |
-
elif operation == "subtract":
|
| 257 |
-
output[:, top:bottom, left:right] = destination_portion - source_portion
|
| 258 |
-
elif operation == "and":
|
| 259 |
-
output[:, top:bottom, left:right] = torch.bitwise_and(destination_portion.round().bool(), source_portion.round().bool()).float()
|
| 260 |
-
elif operation == "or":
|
| 261 |
-
output[:, top:bottom, left:right] = torch.bitwise_or(destination_portion.round().bool(), source_portion.round().bool()).float()
|
| 262 |
-
elif operation == "xor":
|
| 263 |
-
output[:, top:bottom, left:right] = torch.bitwise_xor(destination_portion.round().bool(), source_portion.round().bool()).float()
|
| 264 |
-
|
| 265 |
-
output = torch.clamp(output, 0.0, 1.0)
|
| 266 |
-
|
| 267 |
-
return (output,)
|
| 268 |
-
|
| 269 |
-
class FeatherMask:
|
| 270 |
-
@classmethod
|
| 271 |
-
def INPUT_TYPES(cls):
|
| 272 |
-
return {
|
| 273 |
-
"required": {
|
| 274 |
-
"mask": ("MASK",),
|
| 275 |
-
"left": ("INT", {"default": 0, "min": 0, "max": MAX_RESOLUTION, "step": 1}),
|
| 276 |
-
"top": ("INT", {"default": 0, "min": 0, "max": MAX_RESOLUTION, "step": 1}),
|
| 277 |
-
"right": ("INT", {"default": 0, "min": 0, "max": MAX_RESOLUTION, "step": 1}),
|
| 278 |
-
"bottom": ("INT", {"default": 0, "min": 0, "max": MAX_RESOLUTION, "step": 1}),
|
| 279 |
-
}
|
| 280 |
-
}
|
| 281 |
-
|
| 282 |
-
CATEGORY = "mask"
|
| 283 |
-
|
| 284 |
-
RETURN_TYPES = ("MASK",)
|
| 285 |
-
|
| 286 |
-
FUNCTION = "feather"
|
| 287 |
-
|
| 288 |
-
def feather(self, mask, left, top, right, bottom):
|
| 289 |
-
output = mask.reshape((-1, mask.shape[-2], mask.shape[-1])).clone()
|
| 290 |
-
|
| 291 |
-
left = min(left, output.shape[-1])
|
| 292 |
-
right = min(right, output.shape[-1])
|
| 293 |
-
top = min(top, output.shape[-2])
|
| 294 |
-
bottom = min(bottom, output.shape[-2])
|
| 295 |
-
|
| 296 |
-
for x in range(left):
|
| 297 |
-
feather_rate = (x + 1.0) / left
|
| 298 |
-
output[:, :, x] *= feather_rate
|
| 299 |
-
|
| 300 |
-
for x in range(right):
|
| 301 |
-
feather_rate = (x + 1) / right
|
| 302 |
-
output[:, :, -x] *= feather_rate
|
| 303 |
-
|
| 304 |
-
for y in range(top):
|
| 305 |
-
feather_rate = (y + 1) / top
|
| 306 |
-
output[:, y, :] *= feather_rate
|
| 307 |
-
|
| 308 |
-
for y in range(bottom):
|
| 309 |
-
feather_rate = (y + 1) / bottom
|
| 310 |
-
output[:, -y, :] *= feather_rate
|
| 311 |
-
|
| 312 |
-
return (output,)
|
| 313 |
-
|
| 314 |
-
class GrowMask:
|
| 315 |
-
@classmethod
|
| 316 |
-
def INPUT_TYPES(cls):
|
| 317 |
-
return {
|
| 318 |
-
"required": {
|
| 319 |
-
"mask": ("MASK",),
|
| 320 |
-
"expand": ("INT", {"default": 0, "min": -MAX_RESOLUTION, "max": MAX_RESOLUTION, "step": 1}),
|
| 321 |
-
"tapered_corners": ("BOOLEAN", {"default": True}),
|
| 322 |
-
},
|
| 323 |
-
}
|
| 324 |
-
|
| 325 |
-
CATEGORY = "mask"
|
| 326 |
-
|
| 327 |
-
RETURN_TYPES = ("MASK",)
|
| 328 |
-
|
| 329 |
-
FUNCTION = "expand_mask"
|
| 330 |
-
|
| 331 |
-
def expand_mask(self, mask, expand, tapered_corners):
|
| 332 |
-
c = 0 if tapered_corners else 1
|
| 333 |
-
kernel = np.array([[c, 1, c],
|
| 334 |
-
[1, 1, 1],
|
| 335 |
-
[c, 1, c]])
|
| 336 |
-
mask = mask.reshape((-1, mask.shape[-2], mask.shape[-1]))
|
| 337 |
-
out = []
|
| 338 |
-
for m in mask:
|
| 339 |
-
output = m.numpy()
|
| 340 |
-
for _ in range(abs(expand)):
|
| 341 |
-
if expand < 0:
|
| 342 |
-
output = scipy.ndimage.grey_erosion(output, footprint=kernel)
|
| 343 |
-
else:
|
| 344 |
-
output = scipy.ndimage.grey_dilation(output, footprint=kernel)
|
| 345 |
-
output = torch.from_numpy(output)
|
| 346 |
-
out.append(output)
|
| 347 |
-
return (torch.stack(out, dim=0),)
|
| 348 |
-
|
| 349 |
-
class ThresholdMask:
|
| 350 |
-
@classmethod
|
| 351 |
-
def INPUT_TYPES(s):
|
| 352 |
-
return {
|
| 353 |
-
"required": {
|
| 354 |
-
"mask": ("MASK",),
|
| 355 |
-
"value": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 1.0, "step": 0.01}),
|
| 356 |
-
}
|
| 357 |
-
}
|
| 358 |
-
|
| 359 |
-
CATEGORY = "mask"
|
| 360 |
-
|
| 361 |
-
RETURN_TYPES = ("MASK",)
|
| 362 |
-
FUNCTION = "image_to_mask"
|
| 363 |
-
|
| 364 |
-
def image_to_mask(self, mask, value):
|
| 365 |
-
mask = (mask > value).float()
|
| 366 |
-
return (mask,)
|
| 367 |
-
|
| 368 |
-
# Mask Preview - original implement from
|
| 369 |
-
# https://github.com/cubiq/ComfyUI_essentials/blob/9d9f4bedfc9f0321c19faf71855e228c93bd0dc9/mask.py#L81
|
| 370 |
-
# upstream requested in https://github.com/Kosinkadink/rfcs/blob/main/rfcs/0000-corenodes.md#preview-nodes
|
| 371 |
-
class MaskPreview(nodes.SaveImage):
|
| 372 |
-
def __init__(self):
|
| 373 |
-
self.output_dir = folder_paths.get_temp_directory()
|
| 374 |
-
self.type = "temp"
|
| 375 |
-
self.prefix_append = "_temp_" + ''.join(random.choice("abcdefghijklmnopqrstupvxyz") for x in range(5))
|
| 376 |
-
self.compress_level = 4
|
| 377 |
-
|
| 378 |
-
@classmethod
|
| 379 |
-
def INPUT_TYPES(s):
|
| 380 |
-
return {
|
| 381 |
-
"required": {"mask": ("MASK",), },
|
| 382 |
-
"hidden": {"prompt": "PROMPT", "extra_pnginfo": "EXTRA_PNGINFO"},
|
| 383 |
-
}
|
| 384 |
-
|
| 385 |
-
FUNCTION = "execute"
|
| 386 |
-
CATEGORY = "mask"
|
| 387 |
-
|
| 388 |
-
def execute(self, mask, filename_prefix="ComfyUI", prompt=None, extra_pnginfo=None):
|
| 389 |
-
preview = mask.reshape((-1, 1, mask.shape[-2], mask.shape[-1])).movedim(1, -1).expand(-1, -1, -1, 3)
|
| 390 |
-
return self.save_images(preview, filename_prefix, prompt, extra_pnginfo)
|
| 391 |
-
|
| 392 |
-
|
| 393 |
-
NODE_CLASS_MAPPINGS = {
|
| 394 |
-
"LatentCompositeMasked": LatentCompositeMasked,
|
| 395 |
-
"ImageCompositeMasked": ImageCompositeMasked,
|
| 396 |
-
"MaskToImage": MaskToImage,
|
| 397 |
-
"ImageToMask": ImageToMask,
|
| 398 |
-
"ImageColorToMask": ImageColorToMask,
|
| 399 |
-
"SolidMask": SolidMask,
|
| 400 |
-
"InvertMask": InvertMask,
|
| 401 |
-
"CropMask": CropMask,
|
| 402 |
-
"MaskComposite": MaskComposite,
|
| 403 |
-
"FeatherMask": FeatherMask,
|
| 404 |
-
"GrowMask": GrowMask,
|
| 405 |
-
"ThresholdMask": ThresholdMask,
|
| 406 |
-
"MaskPreview": MaskPreview
|
| 407 |
-
}
|
| 408 |
-
|
| 409 |
-
NODE_DISPLAY_NAME_MAPPINGS = {
|
| 410 |
-
"ImageToMask": "Convert Image to Mask",
|
| 411 |
-
"MaskToImage": "Convert Mask to Image",
|
| 412 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
comfy_extras/nodes_mochi.py
DELETED
|
@@ -1,23 +0,0 @@
|
|
| 1 |
-
import nodes
|
| 2 |
-
import torch
|
| 3 |
-
import comfy.model_management
|
| 4 |
-
|
| 5 |
-
class EmptyMochiLatentVideo:
|
| 6 |
-
@classmethod
|
| 7 |
-
def INPUT_TYPES(s):
|
| 8 |
-
return {"required": { "width": ("INT", {"default": 848, "min": 16, "max": nodes.MAX_RESOLUTION, "step": 16}),
|
| 9 |
-
"height": ("INT", {"default": 480, "min": 16, "max": nodes.MAX_RESOLUTION, "step": 16}),
|
| 10 |
-
"length": ("INT", {"default": 25, "min": 7, "max": nodes.MAX_RESOLUTION, "step": 6}),
|
| 11 |
-
"batch_size": ("INT", {"default": 1, "min": 1, "max": 4096})}}
|
| 12 |
-
RETURN_TYPES = ("LATENT",)
|
| 13 |
-
FUNCTION = "generate"
|
| 14 |
-
|
| 15 |
-
CATEGORY = "latent/video"
|
| 16 |
-
|
| 17 |
-
def generate(self, width, height, length, batch_size=1):
|
| 18 |
-
latent = torch.zeros([batch_size, 12, ((length - 1) // 6) + 1, height // 8, width // 8], device=comfy.model_management.intermediate_device())
|
| 19 |
-
return ({"samples":latent}, )
|
| 20 |
-
|
| 21 |
-
NODE_CLASS_MAPPINGS = {
|
| 22 |
-
"EmptyMochiLatentVideo": EmptyMochiLatentVideo,
|
| 23 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
comfy_extras/nodes_model_advanced.py
DELETED
|
@@ -1,329 +0,0 @@
|
|
| 1 |
-
import comfy.sd
|
| 2 |
-
import comfy.model_sampling
|
| 3 |
-
import comfy.latent_formats
|
| 4 |
-
import nodes
|
| 5 |
-
import torch
|
| 6 |
-
import node_helpers
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
class LCM(comfy.model_sampling.EPS):
|
| 10 |
-
def calculate_denoised(self, sigma, model_output, model_input):
|
| 11 |
-
timestep = self.timestep(sigma).view(sigma.shape[:1] + (1,) * (model_output.ndim - 1))
|
| 12 |
-
sigma = sigma.view(sigma.shape[:1] + (1,) * (model_output.ndim - 1))
|
| 13 |
-
x0 = model_input - model_output * sigma
|
| 14 |
-
|
| 15 |
-
sigma_data = 0.5
|
| 16 |
-
scaled_timestep = timestep * 10.0 #timestep_scaling
|
| 17 |
-
|
| 18 |
-
c_skip = sigma_data**2 / (scaled_timestep**2 + sigma_data**2)
|
| 19 |
-
c_out = scaled_timestep / (scaled_timestep**2 + sigma_data**2) ** 0.5
|
| 20 |
-
|
| 21 |
-
return c_out * x0 + c_skip * model_input
|
| 22 |
-
|
| 23 |
-
class ModelSamplingDiscreteDistilled(comfy.model_sampling.ModelSamplingDiscrete):
|
| 24 |
-
original_timesteps = 50
|
| 25 |
-
|
| 26 |
-
def __init__(self, model_config=None, zsnr=None):
|
| 27 |
-
super().__init__(model_config, zsnr=zsnr)
|
| 28 |
-
|
| 29 |
-
self.skip_steps = self.num_timesteps // self.original_timesteps
|
| 30 |
-
|
| 31 |
-
sigmas_valid = torch.zeros((self.original_timesteps), dtype=torch.float32)
|
| 32 |
-
for x in range(self.original_timesteps):
|
| 33 |
-
sigmas_valid[self.original_timesteps - 1 - x] = self.sigmas[self.num_timesteps - 1 - x * self.skip_steps]
|
| 34 |
-
|
| 35 |
-
self.set_sigmas(sigmas_valid)
|
| 36 |
-
|
| 37 |
-
def timestep(self, sigma):
|
| 38 |
-
log_sigma = sigma.log()
|
| 39 |
-
dists = log_sigma.to(self.log_sigmas.device) - self.log_sigmas[:, None]
|
| 40 |
-
return (dists.abs().argmin(dim=0).view(sigma.shape) * self.skip_steps + (self.skip_steps - 1)).to(sigma.device)
|
| 41 |
-
|
| 42 |
-
def sigma(self, timestep):
|
| 43 |
-
t = torch.clamp(((timestep.float().to(self.log_sigmas.device) - (self.skip_steps - 1)) / self.skip_steps).float(), min=0, max=(len(self.sigmas) - 1))
|
| 44 |
-
low_idx = t.floor().long()
|
| 45 |
-
high_idx = t.ceil().long()
|
| 46 |
-
w = t.frac()
|
| 47 |
-
log_sigma = (1 - w) * self.log_sigmas[low_idx] + w * self.log_sigmas[high_idx]
|
| 48 |
-
return log_sigma.exp().to(timestep.device)
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
class ModelSamplingDiscrete:
|
| 52 |
-
@classmethod
|
| 53 |
-
def INPUT_TYPES(s):
|
| 54 |
-
return {"required": { "model": ("MODEL",),
|
| 55 |
-
"sampling": (["eps", "v_prediction", "lcm", "x0", "img_to_img"],),
|
| 56 |
-
"zsnr": ("BOOLEAN", {"default": False}),
|
| 57 |
-
}}
|
| 58 |
-
|
| 59 |
-
RETURN_TYPES = ("MODEL",)
|
| 60 |
-
FUNCTION = "patch"
|
| 61 |
-
|
| 62 |
-
CATEGORY = "advanced/model"
|
| 63 |
-
|
| 64 |
-
def patch(self, model, sampling, zsnr):
|
| 65 |
-
m = model.clone()
|
| 66 |
-
|
| 67 |
-
sampling_base = comfy.model_sampling.ModelSamplingDiscrete
|
| 68 |
-
if sampling == "eps":
|
| 69 |
-
sampling_type = comfy.model_sampling.EPS
|
| 70 |
-
elif sampling == "v_prediction":
|
| 71 |
-
sampling_type = comfy.model_sampling.V_PREDICTION
|
| 72 |
-
elif sampling == "lcm":
|
| 73 |
-
sampling_type = LCM
|
| 74 |
-
sampling_base = ModelSamplingDiscreteDistilled
|
| 75 |
-
elif sampling == "x0":
|
| 76 |
-
sampling_type = comfy.model_sampling.X0
|
| 77 |
-
elif sampling == "img_to_img":
|
| 78 |
-
sampling_type = comfy.model_sampling.IMG_TO_IMG
|
| 79 |
-
|
| 80 |
-
class ModelSamplingAdvanced(sampling_base, sampling_type):
|
| 81 |
-
pass
|
| 82 |
-
|
| 83 |
-
model_sampling = ModelSamplingAdvanced(model.model.model_config, zsnr=zsnr)
|
| 84 |
-
|
| 85 |
-
m.add_object_patch("model_sampling", model_sampling)
|
| 86 |
-
return (m, )
|
| 87 |
-
|
| 88 |
-
class ModelSamplingStableCascade:
|
| 89 |
-
@classmethod
|
| 90 |
-
def INPUT_TYPES(s):
|
| 91 |
-
return {"required": { "model": ("MODEL",),
|
| 92 |
-
"shift": ("FLOAT", {"default": 2.0, "min": 0.0, "max": 100.0, "step":0.01}),
|
| 93 |
-
}}
|
| 94 |
-
|
| 95 |
-
RETURN_TYPES = ("MODEL",)
|
| 96 |
-
FUNCTION = "patch"
|
| 97 |
-
|
| 98 |
-
CATEGORY = "advanced/model"
|
| 99 |
-
|
| 100 |
-
def patch(self, model, shift):
|
| 101 |
-
m = model.clone()
|
| 102 |
-
|
| 103 |
-
sampling_base = comfy.model_sampling.StableCascadeSampling
|
| 104 |
-
sampling_type = comfy.model_sampling.EPS
|
| 105 |
-
|
| 106 |
-
class ModelSamplingAdvanced(sampling_base, sampling_type):
|
| 107 |
-
pass
|
| 108 |
-
|
| 109 |
-
model_sampling = ModelSamplingAdvanced(model.model.model_config)
|
| 110 |
-
model_sampling.set_parameters(shift)
|
| 111 |
-
m.add_object_patch("model_sampling", model_sampling)
|
| 112 |
-
return (m, )
|
| 113 |
-
|
| 114 |
-
class ModelSamplingSD3:
|
| 115 |
-
@classmethod
|
| 116 |
-
def INPUT_TYPES(s):
|
| 117 |
-
return {"required": { "model": ("MODEL",),
|
| 118 |
-
"shift": ("FLOAT", {"default": 3.0, "min": 0.0, "max": 100.0, "step":0.01}),
|
| 119 |
-
}}
|
| 120 |
-
|
| 121 |
-
RETURN_TYPES = ("MODEL",)
|
| 122 |
-
FUNCTION = "patch"
|
| 123 |
-
|
| 124 |
-
CATEGORY = "advanced/model"
|
| 125 |
-
|
| 126 |
-
def patch(self, model, shift, multiplier=1000):
|
| 127 |
-
m = model.clone()
|
| 128 |
-
|
| 129 |
-
sampling_base = comfy.model_sampling.ModelSamplingDiscreteFlow
|
| 130 |
-
sampling_type = comfy.model_sampling.CONST
|
| 131 |
-
|
| 132 |
-
class ModelSamplingAdvanced(sampling_base, sampling_type):
|
| 133 |
-
pass
|
| 134 |
-
|
| 135 |
-
model_sampling = ModelSamplingAdvanced(model.model.model_config)
|
| 136 |
-
model_sampling.set_parameters(shift=shift, multiplier=multiplier)
|
| 137 |
-
m.add_object_patch("model_sampling", model_sampling)
|
| 138 |
-
return (m, )
|
| 139 |
-
|
| 140 |
-
class ModelSamplingAuraFlow(ModelSamplingSD3):
|
| 141 |
-
@classmethod
|
| 142 |
-
def INPUT_TYPES(s):
|
| 143 |
-
return {"required": { "model": ("MODEL",),
|
| 144 |
-
"shift": ("FLOAT", {"default": 1.73, "min": 0.0, "max": 100.0, "step":0.01}),
|
| 145 |
-
}}
|
| 146 |
-
|
| 147 |
-
FUNCTION = "patch_aura"
|
| 148 |
-
|
| 149 |
-
def patch_aura(self, model, shift):
|
| 150 |
-
return self.patch(model, shift, multiplier=1.0)
|
| 151 |
-
|
| 152 |
-
class ModelSamplingFlux:
|
| 153 |
-
@classmethod
|
| 154 |
-
def INPUT_TYPES(s):
|
| 155 |
-
return {"required": { "model": ("MODEL",),
|
| 156 |
-
"max_shift": ("FLOAT", {"default": 1.15, "min": 0.0, "max": 100.0, "step":0.01}),
|
| 157 |
-
"base_shift": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 100.0, "step":0.01}),
|
| 158 |
-
"width": ("INT", {"default": 1024, "min": 16, "max": nodes.MAX_RESOLUTION, "step": 8}),
|
| 159 |
-
"height": ("INT", {"default": 1024, "min": 16, "max": nodes.MAX_RESOLUTION, "step": 8}),
|
| 160 |
-
}}
|
| 161 |
-
|
| 162 |
-
RETURN_TYPES = ("MODEL",)
|
| 163 |
-
FUNCTION = "patch"
|
| 164 |
-
|
| 165 |
-
CATEGORY = "advanced/model"
|
| 166 |
-
|
| 167 |
-
def patch(self, model, max_shift, base_shift, width, height):
|
| 168 |
-
m = model.clone()
|
| 169 |
-
|
| 170 |
-
x1 = 256
|
| 171 |
-
x2 = 4096
|
| 172 |
-
mm = (max_shift - base_shift) / (x2 - x1)
|
| 173 |
-
b = base_shift - mm * x1
|
| 174 |
-
shift = (width * height / (8 * 8 * 2 * 2)) * mm + b
|
| 175 |
-
|
| 176 |
-
sampling_base = comfy.model_sampling.ModelSamplingFlux
|
| 177 |
-
sampling_type = comfy.model_sampling.CONST
|
| 178 |
-
|
| 179 |
-
class ModelSamplingAdvanced(sampling_base, sampling_type):
|
| 180 |
-
pass
|
| 181 |
-
|
| 182 |
-
model_sampling = ModelSamplingAdvanced(model.model.model_config)
|
| 183 |
-
model_sampling.set_parameters(shift=shift)
|
| 184 |
-
m.add_object_patch("model_sampling", model_sampling)
|
| 185 |
-
return (m, )
|
| 186 |
-
|
| 187 |
-
|
| 188 |
-
class ModelSamplingContinuousEDM:
|
| 189 |
-
@classmethod
|
| 190 |
-
def INPUT_TYPES(s):
|
| 191 |
-
return {"required": { "model": ("MODEL",),
|
| 192 |
-
"sampling": (["v_prediction", "edm", "edm_playground_v2.5", "eps", "cosmos_rflow"],),
|
| 193 |
-
"sigma_max": ("FLOAT", {"default": 120.0, "min": 0.0, "max": 1000.0, "step":0.001, "round": False}),
|
| 194 |
-
"sigma_min": ("FLOAT", {"default": 0.002, "min": 0.0, "max": 1000.0, "step":0.001, "round": False}),
|
| 195 |
-
}}
|
| 196 |
-
|
| 197 |
-
RETURN_TYPES = ("MODEL",)
|
| 198 |
-
FUNCTION = "patch"
|
| 199 |
-
|
| 200 |
-
CATEGORY = "advanced/model"
|
| 201 |
-
|
| 202 |
-
def patch(self, model, sampling, sigma_max, sigma_min):
|
| 203 |
-
m = model.clone()
|
| 204 |
-
|
| 205 |
-
sampling_base = comfy.model_sampling.ModelSamplingContinuousEDM
|
| 206 |
-
latent_format = None
|
| 207 |
-
sigma_data = 1.0
|
| 208 |
-
if sampling == "eps":
|
| 209 |
-
sampling_type = comfy.model_sampling.EPS
|
| 210 |
-
elif sampling == "edm":
|
| 211 |
-
sampling_type = comfy.model_sampling.EDM
|
| 212 |
-
sigma_data = 0.5
|
| 213 |
-
elif sampling == "v_prediction":
|
| 214 |
-
sampling_type = comfy.model_sampling.V_PREDICTION
|
| 215 |
-
elif sampling == "edm_playground_v2.5":
|
| 216 |
-
sampling_type = comfy.model_sampling.EDM
|
| 217 |
-
sigma_data = 0.5
|
| 218 |
-
latent_format = comfy.latent_formats.SDXL_Playground_2_5()
|
| 219 |
-
elif sampling == "cosmos_rflow":
|
| 220 |
-
sampling_type = comfy.model_sampling.COSMOS_RFLOW
|
| 221 |
-
sampling_base = comfy.model_sampling.ModelSamplingCosmosRFlow
|
| 222 |
-
|
| 223 |
-
class ModelSamplingAdvanced(sampling_base, sampling_type):
|
| 224 |
-
pass
|
| 225 |
-
|
| 226 |
-
model_sampling = ModelSamplingAdvanced(model.model.model_config)
|
| 227 |
-
model_sampling.set_parameters(sigma_min, sigma_max, sigma_data)
|
| 228 |
-
m.add_object_patch("model_sampling", model_sampling)
|
| 229 |
-
if latent_format is not None:
|
| 230 |
-
m.add_object_patch("latent_format", latent_format)
|
| 231 |
-
return (m, )
|
| 232 |
-
|
| 233 |
-
class ModelSamplingContinuousV:
|
| 234 |
-
@classmethod
|
| 235 |
-
def INPUT_TYPES(s):
|
| 236 |
-
return {"required": { "model": ("MODEL",),
|
| 237 |
-
"sampling": (["v_prediction"],),
|
| 238 |
-
"sigma_max": ("FLOAT", {"default": 500.0, "min": 0.0, "max": 1000.0, "step":0.001, "round": False}),
|
| 239 |
-
"sigma_min": ("FLOAT", {"default": 0.03, "min": 0.0, "max": 1000.0, "step":0.001, "round": False}),
|
| 240 |
-
}}
|
| 241 |
-
|
| 242 |
-
RETURN_TYPES = ("MODEL",)
|
| 243 |
-
FUNCTION = "patch"
|
| 244 |
-
|
| 245 |
-
CATEGORY = "advanced/model"
|
| 246 |
-
|
| 247 |
-
def patch(self, model, sampling, sigma_max, sigma_min):
|
| 248 |
-
m = model.clone()
|
| 249 |
-
|
| 250 |
-
sigma_data = 1.0
|
| 251 |
-
if sampling == "v_prediction":
|
| 252 |
-
sampling_type = comfy.model_sampling.V_PREDICTION
|
| 253 |
-
|
| 254 |
-
class ModelSamplingAdvanced(comfy.model_sampling.ModelSamplingContinuousV, sampling_type):
|
| 255 |
-
pass
|
| 256 |
-
|
| 257 |
-
model_sampling = ModelSamplingAdvanced(model.model.model_config)
|
| 258 |
-
model_sampling.set_parameters(sigma_min, sigma_max, sigma_data)
|
| 259 |
-
m.add_object_patch("model_sampling", model_sampling)
|
| 260 |
-
return (m, )
|
| 261 |
-
|
| 262 |
-
class RescaleCFG:
|
| 263 |
-
@classmethod
|
| 264 |
-
def INPUT_TYPES(s):
|
| 265 |
-
return {"required": { "model": ("MODEL",),
|
| 266 |
-
"multiplier": ("FLOAT", {"default": 0.7, "min": 0.0, "max": 1.0, "step": 0.01}),
|
| 267 |
-
}}
|
| 268 |
-
RETURN_TYPES = ("MODEL",)
|
| 269 |
-
FUNCTION = "patch"
|
| 270 |
-
|
| 271 |
-
CATEGORY = "advanced/model"
|
| 272 |
-
|
| 273 |
-
def patch(self, model, multiplier):
|
| 274 |
-
def rescale_cfg(args):
|
| 275 |
-
cond = args["cond"]
|
| 276 |
-
uncond = args["uncond"]
|
| 277 |
-
cond_scale = args["cond_scale"]
|
| 278 |
-
sigma = args["sigma"]
|
| 279 |
-
sigma = sigma.view(sigma.shape[:1] + (1,) * (cond.ndim - 1))
|
| 280 |
-
x_orig = args["input"]
|
| 281 |
-
|
| 282 |
-
#rescale cfg has to be done on v-pred model output
|
| 283 |
-
x = x_orig / (sigma * sigma + 1.0)
|
| 284 |
-
cond = ((x - (x_orig - cond)) * (sigma ** 2 + 1.0) ** 0.5) / (sigma)
|
| 285 |
-
uncond = ((x - (x_orig - uncond)) * (sigma ** 2 + 1.0) ** 0.5) / (sigma)
|
| 286 |
-
|
| 287 |
-
#rescalecfg
|
| 288 |
-
x_cfg = uncond + cond_scale * (cond - uncond)
|
| 289 |
-
ro_pos = torch.std(cond, dim=(1,2,3), keepdim=True)
|
| 290 |
-
ro_cfg = torch.std(x_cfg, dim=(1,2,3), keepdim=True)
|
| 291 |
-
|
| 292 |
-
x_rescaled = x_cfg * (ro_pos / ro_cfg)
|
| 293 |
-
x_final = multiplier * x_rescaled + (1.0 - multiplier) * x_cfg
|
| 294 |
-
|
| 295 |
-
return x_orig - (x - x_final * sigma / (sigma * sigma + 1.0) ** 0.5)
|
| 296 |
-
|
| 297 |
-
m = model.clone()
|
| 298 |
-
m.set_model_sampler_cfg_function(rescale_cfg)
|
| 299 |
-
return (m, )
|
| 300 |
-
|
| 301 |
-
class ModelComputeDtype:
|
| 302 |
-
@classmethod
|
| 303 |
-
def INPUT_TYPES(s):
|
| 304 |
-
return {"required": { "model": ("MODEL",),
|
| 305 |
-
"dtype": (["default", "fp32", "fp16", "bf16"],),
|
| 306 |
-
}}
|
| 307 |
-
|
| 308 |
-
RETURN_TYPES = ("MODEL",)
|
| 309 |
-
FUNCTION = "patch"
|
| 310 |
-
|
| 311 |
-
CATEGORY = "advanced/debug/model"
|
| 312 |
-
|
| 313 |
-
def patch(self, model, dtype):
|
| 314 |
-
m = model.clone()
|
| 315 |
-
m.set_model_compute_dtype(node_helpers.string_to_torch_dtype(dtype))
|
| 316 |
-
return (m, )
|
| 317 |
-
|
| 318 |
-
|
| 319 |
-
NODE_CLASS_MAPPINGS = {
|
| 320 |
-
"ModelSamplingDiscrete": ModelSamplingDiscrete,
|
| 321 |
-
"ModelSamplingContinuousEDM": ModelSamplingContinuousEDM,
|
| 322 |
-
"ModelSamplingContinuousV": ModelSamplingContinuousV,
|
| 323 |
-
"ModelSamplingStableCascade": ModelSamplingStableCascade,
|
| 324 |
-
"ModelSamplingSD3": ModelSamplingSD3,
|
| 325 |
-
"ModelSamplingAuraFlow": ModelSamplingAuraFlow,
|
| 326 |
-
"ModelSamplingFlux": ModelSamplingFlux,
|
| 327 |
-
"RescaleCFG": RescaleCFG,
|
| 328 |
-
"ModelComputeDtype": ModelComputeDtype,
|
| 329 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
comfy_extras/nodes_model_downscale.py
DELETED
|
@@ -1,53 +0,0 @@
|
|
| 1 |
-
import comfy.utils
|
| 2 |
-
|
| 3 |
-
class PatchModelAddDownscale:
|
| 4 |
-
upscale_methods = ["bicubic", "nearest-exact", "bilinear", "area", "bislerp"]
|
| 5 |
-
@classmethod
|
| 6 |
-
def INPUT_TYPES(s):
|
| 7 |
-
return {"required": { "model": ("MODEL",),
|
| 8 |
-
"block_number": ("INT", {"default": 3, "min": 1, "max": 32, "step": 1}),
|
| 9 |
-
"downscale_factor": ("FLOAT", {"default": 2.0, "min": 0.1, "max": 9.0, "step": 0.001}),
|
| 10 |
-
"start_percent": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.001}),
|
| 11 |
-
"end_percent": ("FLOAT", {"default": 0.35, "min": 0.0, "max": 1.0, "step": 0.001}),
|
| 12 |
-
"downscale_after_skip": ("BOOLEAN", {"default": True}),
|
| 13 |
-
"downscale_method": (s.upscale_methods,),
|
| 14 |
-
"upscale_method": (s.upscale_methods,),
|
| 15 |
-
}}
|
| 16 |
-
RETURN_TYPES = ("MODEL",)
|
| 17 |
-
FUNCTION = "patch"
|
| 18 |
-
|
| 19 |
-
CATEGORY = "model_patches/unet"
|
| 20 |
-
|
| 21 |
-
def patch(self, model, block_number, downscale_factor, start_percent, end_percent, downscale_after_skip, downscale_method, upscale_method):
|
| 22 |
-
model_sampling = model.get_model_object("model_sampling")
|
| 23 |
-
sigma_start = model_sampling.percent_to_sigma(start_percent)
|
| 24 |
-
sigma_end = model_sampling.percent_to_sigma(end_percent)
|
| 25 |
-
|
| 26 |
-
def input_block_patch(h, transformer_options):
|
| 27 |
-
if transformer_options["block"][1] == block_number:
|
| 28 |
-
sigma = transformer_options["sigmas"][0].item()
|
| 29 |
-
if sigma <= sigma_start and sigma >= sigma_end:
|
| 30 |
-
h = comfy.utils.common_upscale(h, round(h.shape[-1] * (1.0 / downscale_factor)), round(h.shape[-2] * (1.0 / downscale_factor)), downscale_method, "disabled")
|
| 31 |
-
return h
|
| 32 |
-
|
| 33 |
-
def output_block_patch(h, hsp, transformer_options):
|
| 34 |
-
if h.shape[2] != hsp.shape[2]:
|
| 35 |
-
h = comfy.utils.common_upscale(h, hsp.shape[-1], hsp.shape[-2], upscale_method, "disabled")
|
| 36 |
-
return h, hsp
|
| 37 |
-
|
| 38 |
-
m = model.clone()
|
| 39 |
-
if downscale_after_skip:
|
| 40 |
-
m.set_model_input_block_patch_after_skip(input_block_patch)
|
| 41 |
-
else:
|
| 42 |
-
m.set_model_input_block_patch(input_block_patch)
|
| 43 |
-
m.set_model_output_block_patch(output_block_patch)
|
| 44 |
-
return (m, )
|
| 45 |
-
|
| 46 |
-
NODE_CLASS_MAPPINGS = {
|
| 47 |
-
"PatchModelAddDownscale": PatchModelAddDownscale,
|
| 48 |
-
}
|
| 49 |
-
|
| 50 |
-
NODE_DISPLAY_NAME_MAPPINGS = {
|
| 51 |
-
# Sampling
|
| 52 |
-
"PatchModelAddDownscale": "PatchModelAddDownscale (Kohya Deep Shrink)",
|
| 53 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
comfy_extras/nodes_model_merging.py
DELETED
|
@@ -1,374 +0,0 @@
|
|
| 1 |
-
import comfy.sd
|
| 2 |
-
import comfy.utils
|
| 3 |
-
import comfy.model_base
|
| 4 |
-
import comfy.model_management
|
| 5 |
-
import comfy.model_sampling
|
| 6 |
-
|
| 7 |
-
import torch
|
| 8 |
-
import folder_paths
|
| 9 |
-
import json
|
| 10 |
-
import os
|
| 11 |
-
|
| 12 |
-
from comfy.cli_args import args
|
| 13 |
-
|
| 14 |
-
class ModelMergeSimple:
|
| 15 |
-
@classmethod
|
| 16 |
-
def INPUT_TYPES(s):
|
| 17 |
-
return {"required": { "model1": ("MODEL",),
|
| 18 |
-
"model2": ("MODEL",),
|
| 19 |
-
"ratio": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}),
|
| 20 |
-
}}
|
| 21 |
-
RETURN_TYPES = ("MODEL",)
|
| 22 |
-
FUNCTION = "merge"
|
| 23 |
-
|
| 24 |
-
CATEGORY = "advanced/model_merging"
|
| 25 |
-
|
| 26 |
-
def merge(self, model1, model2, ratio):
|
| 27 |
-
m = model1.clone()
|
| 28 |
-
kp = model2.get_key_patches("diffusion_model.")
|
| 29 |
-
for k in kp:
|
| 30 |
-
m.add_patches({k: kp[k]}, 1.0 - ratio, ratio)
|
| 31 |
-
return (m, )
|
| 32 |
-
|
| 33 |
-
class ModelSubtract:
|
| 34 |
-
@classmethod
|
| 35 |
-
def INPUT_TYPES(s):
|
| 36 |
-
return {"required": { "model1": ("MODEL",),
|
| 37 |
-
"model2": ("MODEL",),
|
| 38 |
-
"multiplier": ("FLOAT", {"default": 1.0, "min": -10.0, "max": 10.0, "step": 0.01}),
|
| 39 |
-
}}
|
| 40 |
-
RETURN_TYPES = ("MODEL",)
|
| 41 |
-
FUNCTION = "merge"
|
| 42 |
-
|
| 43 |
-
CATEGORY = "advanced/model_merging"
|
| 44 |
-
|
| 45 |
-
def merge(self, model1, model2, multiplier):
|
| 46 |
-
m = model1.clone()
|
| 47 |
-
kp = model2.get_key_patches("diffusion_model.")
|
| 48 |
-
for k in kp:
|
| 49 |
-
m.add_patches({k: kp[k]}, - multiplier, multiplier)
|
| 50 |
-
return (m, )
|
| 51 |
-
|
| 52 |
-
class ModelAdd:
|
| 53 |
-
@classmethod
|
| 54 |
-
def INPUT_TYPES(s):
|
| 55 |
-
return {"required": { "model1": ("MODEL",),
|
| 56 |
-
"model2": ("MODEL",),
|
| 57 |
-
}}
|
| 58 |
-
RETURN_TYPES = ("MODEL",)
|
| 59 |
-
FUNCTION = "merge"
|
| 60 |
-
|
| 61 |
-
CATEGORY = "advanced/model_merging"
|
| 62 |
-
|
| 63 |
-
def merge(self, model1, model2):
|
| 64 |
-
m = model1.clone()
|
| 65 |
-
kp = model2.get_key_patches("diffusion_model.")
|
| 66 |
-
for k in kp:
|
| 67 |
-
m.add_patches({k: kp[k]}, 1.0, 1.0)
|
| 68 |
-
return (m, )
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
class CLIPMergeSimple:
|
| 72 |
-
@classmethod
|
| 73 |
-
def INPUT_TYPES(s):
|
| 74 |
-
return {"required": { "clip1": ("CLIP",),
|
| 75 |
-
"clip2": ("CLIP",),
|
| 76 |
-
"ratio": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}),
|
| 77 |
-
}}
|
| 78 |
-
RETURN_TYPES = ("CLIP",)
|
| 79 |
-
FUNCTION = "merge"
|
| 80 |
-
|
| 81 |
-
CATEGORY = "advanced/model_merging"
|
| 82 |
-
|
| 83 |
-
def merge(self, clip1, clip2, ratio):
|
| 84 |
-
m = clip1.clone()
|
| 85 |
-
kp = clip2.get_key_patches()
|
| 86 |
-
for k in kp:
|
| 87 |
-
if k.endswith(".position_ids") or k.endswith(".logit_scale"):
|
| 88 |
-
continue
|
| 89 |
-
m.add_patches({k: kp[k]}, 1.0 - ratio, ratio)
|
| 90 |
-
return (m, )
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
class CLIPSubtract:
|
| 94 |
-
@classmethod
|
| 95 |
-
def INPUT_TYPES(s):
|
| 96 |
-
return {"required": { "clip1": ("CLIP",),
|
| 97 |
-
"clip2": ("CLIP",),
|
| 98 |
-
"multiplier": ("FLOAT", {"default": 1.0, "min": -10.0, "max": 10.0, "step": 0.01}),
|
| 99 |
-
}}
|
| 100 |
-
RETURN_TYPES = ("CLIP",)
|
| 101 |
-
FUNCTION = "merge"
|
| 102 |
-
|
| 103 |
-
CATEGORY = "advanced/model_merging"
|
| 104 |
-
|
| 105 |
-
def merge(self, clip1, clip2, multiplier):
|
| 106 |
-
m = clip1.clone()
|
| 107 |
-
kp = clip2.get_key_patches()
|
| 108 |
-
for k in kp:
|
| 109 |
-
if k.endswith(".position_ids") or k.endswith(".logit_scale"):
|
| 110 |
-
continue
|
| 111 |
-
m.add_patches({k: kp[k]}, - multiplier, multiplier)
|
| 112 |
-
return (m, )
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
class CLIPAdd:
|
| 116 |
-
@classmethod
|
| 117 |
-
def INPUT_TYPES(s):
|
| 118 |
-
return {"required": { "clip1": ("CLIP",),
|
| 119 |
-
"clip2": ("CLIP",),
|
| 120 |
-
}}
|
| 121 |
-
RETURN_TYPES = ("CLIP",)
|
| 122 |
-
FUNCTION = "merge"
|
| 123 |
-
|
| 124 |
-
CATEGORY = "advanced/model_merging"
|
| 125 |
-
|
| 126 |
-
def merge(self, clip1, clip2):
|
| 127 |
-
m = clip1.clone()
|
| 128 |
-
kp = clip2.get_key_patches()
|
| 129 |
-
for k in kp:
|
| 130 |
-
if k.endswith(".position_ids") or k.endswith(".logit_scale"):
|
| 131 |
-
continue
|
| 132 |
-
m.add_patches({k: kp[k]}, 1.0, 1.0)
|
| 133 |
-
return (m, )
|
| 134 |
-
|
| 135 |
-
|
| 136 |
-
class ModelMergeBlocks:
|
| 137 |
-
@classmethod
|
| 138 |
-
def INPUT_TYPES(s):
|
| 139 |
-
return {"required": { "model1": ("MODEL",),
|
| 140 |
-
"model2": ("MODEL",),
|
| 141 |
-
"input": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}),
|
| 142 |
-
"middle": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}),
|
| 143 |
-
"out": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01})
|
| 144 |
-
}}
|
| 145 |
-
RETURN_TYPES = ("MODEL",)
|
| 146 |
-
FUNCTION = "merge"
|
| 147 |
-
|
| 148 |
-
CATEGORY = "advanced/model_merging"
|
| 149 |
-
|
| 150 |
-
def merge(self, model1, model2, **kwargs):
|
| 151 |
-
m = model1.clone()
|
| 152 |
-
kp = model2.get_key_patches("diffusion_model.")
|
| 153 |
-
default_ratio = next(iter(kwargs.values()))
|
| 154 |
-
|
| 155 |
-
for k in kp:
|
| 156 |
-
ratio = default_ratio
|
| 157 |
-
k_unet = k[len("diffusion_model."):]
|
| 158 |
-
|
| 159 |
-
last_arg_size = 0
|
| 160 |
-
for arg in kwargs:
|
| 161 |
-
if k_unet.startswith(arg) and last_arg_size < len(arg):
|
| 162 |
-
ratio = kwargs[arg]
|
| 163 |
-
last_arg_size = len(arg)
|
| 164 |
-
|
| 165 |
-
m.add_patches({k: kp[k]}, 1.0 - ratio, ratio)
|
| 166 |
-
return (m, )
|
| 167 |
-
|
| 168 |
-
def save_checkpoint(model, clip=None, vae=None, clip_vision=None, filename_prefix=None, output_dir=None, prompt=None, extra_pnginfo=None):
|
| 169 |
-
full_output_folder, filename, counter, subfolder, filename_prefix = folder_paths.get_save_image_path(filename_prefix, output_dir)
|
| 170 |
-
prompt_info = ""
|
| 171 |
-
if prompt is not None:
|
| 172 |
-
prompt_info = json.dumps(prompt)
|
| 173 |
-
|
| 174 |
-
metadata = {}
|
| 175 |
-
|
| 176 |
-
enable_modelspec = True
|
| 177 |
-
if isinstance(model.model, comfy.model_base.SDXL):
|
| 178 |
-
if isinstance(model.model, comfy.model_base.SDXL_instructpix2pix):
|
| 179 |
-
metadata["modelspec.architecture"] = "stable-diffusion-xl-v1-edit"
|
| 180 |
-
else:
|
| 181 |
-
metadata["modelspec.architecture"] = "stable-diffusion-xl-v1-base"
|
| 182 |
-
elif isinstance(model.model, comfy.model_base.SDXLRefiner):
|
| 183 |
-
metadata["modelspec.architecture"] = "stable-diffusion-xl-v1-refiner"
|
| 184 |
-
elif isinstance(model.model, comfy.model_base.SVD_img2vid):
|
| 185 |
-
metadata["modelspec.architecture"] = "stable-video-diffusion-img2vid-v1"
|
| 186 |
-
elif isinstance(model.model, comfy.model_base.SD3):
|
| 187 |
-
metadata["modelspec.architecture"] = "stable-diffusion-v3-medium" #TODO: other SD3 variants
|
| 188 |
-
else:
|
| 189 |
-
enable_modelspec = False
|
| 190 |
-
|
| 191 |
-
if enable_modelspec:
|
| 192 |
-
metadata["modelspec.sai_model_spec"] = "1.0.0"
|
| 193 |
-
metadata["modelspec.implementation"] = "sgm"
|
| 194 |
-
metadata["modelspec.title"] = "{} {}".format(filename, counter)
|
| 195 |
-
|
| 196 |
-
#TODO:
|
| 197 |
-
# "stable-diffusion-v1", "stable-diffusion-v1-inpainting", "stable-diffusion-v2-512",
|
| 198 |
-
# "stable-diffusion-v2-768-v", "stable-diffusion-v2-unclip-l", "stable-diffusion-v2-unclip-h",
|
| 199 |
-
# "v2-inpainting"
|
| 200 |
-
|
| 201 |
-
extra_keys = {}
|
| 202 |
-
model_sampling = model.get_model_object("model_sampling")
|
| 203 |
-
if isinstance(model_sampling, comfy.model_sampling.ModelSamplingContinuousEDM):
|
| 204 |
-
if isinstance(model_sampling, comfy.model_sampling.V_PREDICTION):
|
| 205 |
-
extra_keys["edm_vpred.sigma_max"] = torch.tensor(model_sampling.sigma_max).float()
|
| 206 |
-
extra_keys["edm_vpred.sigma_min"] = torch.tensor(model_sampling.sigma_min).float()
|
| 207 |
-
|
| 208 |
-
if model.model.model_type == comfy.model_base.ModelType.EPS:
|
| 209 |
-
metadata["modelspec.predict_key"] = "epsilon"
|
| 210 |
-
elif model.model.model_type == comfy.model_base.ModelType.V_PREDICTION:
|
| 211 |
-
metadata["modelspec.predict_key"] = "v"
|
| 212 |
-
extra_keys["v_pred"] = torch.tensor([])
|
| 213 |
-
if getattr(model_sampling, "zsnr", False):
|
| 214 |
-
extra_keys["ztsnr"] = torch.tensor([])
|
| 215 |
-
|
| 216 |
-
if not args.disable_metadata:
|
| 217 |
-
metadata["prompt"] = prompt_info
|
| 218 |
-
if extra_pnginfo is not None:
|
| 219 |
-
for x in extra_pnginfo:
|
| 220 |
-
metadata[x] = json.dumps(extra_pnginfo[x])
|
| 221 |
-
|
| 222 |
-
output_checkpoint = f"{filename}_{counter:05}_.safetensors"
|
| 223 |
-
output_checkpoint = os.path.join(full_output_folder, output_checkpoint)
|
| 224 |
-
|
| 225 |
-
comfy.sd.save_checkpoint(output_checkpoint, model, clip, vae, clip_vision, metadata=metadata, extra_keys=extra_keys)
|
| 226 |
-
|
| 227 |
-
class CheckpointSave:
|
| 228 |
-
def __init__(self):
|
| 229 |
-
self.output_dir = folder_paths.get_output_directory()
|
| 230 |
-
|
| 231 |
-
@classmethod
|
| 232 |
-
def INPUT_TYPES(s):
|
| 233 |
-
return {"required": { "model": ("MODEL",),
|
| 234 |
-
"clip": ("CLIP",),
|
| 235 |
-
"vae": ("VAE",),
|
| 236 |
-
"filename_prefix": ("STRING", {"default": "checkpoints/ComfyUI"}),},
|
| 237 |
-
"hidden": {"prompt": "PROMPT", "extra_pnginfo": "EXTRA_PNGINFO"},}
|
| 238 |
-
RETURN_TYPES = ()
|
| 239 |
-
FUNCTION = "save"
|
| 240 |
-
OUTPUT_NODE = True
|
| 241 |
-
|
| 242 |
-
CATEGORY = "advanced/model_merging"
|
| 243 |
-
|
| 244 |
-
def save(self, model, clip, vae, filename_prefix, prompt=None, extra_pnginfo=None):
|
| 245 |
-
save_checkpoint(model, clip=clip, vae=vae, filename_prefix=filename_prefix, output_dir=self.output_dir, prompt=prompt, extra_pnginfo=extra_pnginfo)
|
| 246 |
-
return {}
|
| 247 |
-
|
| 248 |
-
class CLIPSave:
|
| 249 |
-
def __init__(self):
|
| 250 |
-
self.output_dir = folder_paths.get_output_directory()
|
| 251 |
-
|
| 252 |
-
@classmethod
|
| 253 |
-
def INPUT_TYPES(s):
|
| 254 |
-
return {"required": { "clip": ("CLIP",),
|
| 255 |
-
"filename_prefix": ("STRING", {"default": "clip/ComfyUI"}),},
|
| 256 |
-
"hidden": {"prompt": "PROMPT", "extra_pnginfo": "EXTRA_PNGINFO"},}
|
| 257 |
-
RETURN_TYPES = ()
|
| 258 |
-
FUNCTION = "save"
|
| 259 |
-
OUTPUT_NODE = True
|
| 260 |
-
|
| 261 |
-
CATEGORY = "advanced/model_merging"
|
| 262 |
-
|
| 263 |
-
def save(self, clip, filename_prefix, prompt=None, extra_pnginfo=None):
|
| 264 |
-
prompt_info = ""
|
| 265 |
-
if prompt is not None:
|
| 266 |
-
prompt_info = json.dumps(prompt)
|
| 267 |
-
|
| 268 |
-
metadata = {}
|
| 269 |
-
if not args.disable_metadata:
|
| 270 |
-
metadata["format"] = "pt"
|
| 271 |
-
metadata["prompt"] = prompt_info
|
| 272 |
-
if extra_pnginfo is not None:
|
| 273 |
-
for x in extra_pnginfo:
|
| 274 |
-
metadata[x] = json.dumps(extra_pnginfo[x])
|
| 275 |
-
|
| 276 |
-
comfy.model_management.load_models_gpu([clip.load_model()], force_patch_weights=True)
|
| 277 |
-
clip_sd = clip.get_sd()
|
| 278 |
-
|
| 279 |
-
for prefix in ["clip_l.", "clip_g.", "clip_h.", "t5xxl.", "pile_t5xl.", "mt5xl.", "umt5xxl.", "t5base.", "gemma2_2b.", "llama.", "hydit_clip.", ""]:
|
| 280 |
-
k = list(filter(lambda a: a.startswith(prefix), clip_sd.keys()))
|
| 281 |
-
current_clip_sd = {}
|
| 282 |
-
for x in k:
|
| 283 |
-
current_clip_sd[x] = clip_sd.pop(x)
|
| 284 |
-
if len(current_clip_sd) == 0:
|
| 285 |
-
continue
|
| 286 |
-
|
| 287 |
-
p = prefix[:-1]
|
| 288 |
-
replace_prefix = {}
|
| 289 |
-
filename_prefix_ = filename_prefix
|
| 290 |
-
if len(p) > 0:
|
| 291 |
-
filename_prefix_ = "{}_{}".format(filename_prefix_, p)
|
| 292 |
-
replace_prefix[prefix] = ""
|
| 293 |
-
replace_prefix["transformer."] = ""
|
| 294 |
-
|
| 295 |
-
full_output_folder, filename, counter, subfolder, filename_prefix_ = folder_paths.get_save_image_path(filename_prefix_, self.output_dir)
|
| 296 |
-
|
| 297 |
-
output_checkpoint = f"{filename}_{counter:05}_.safetensors"
|
| 298 |
-
output_checkpoint = os.path.join(full_output_folder, output_checkpoint)
|
| 299 |
-
|
| 300 |
-
current_clip_sd = comfy.utils.state_dict_prefix_replace(current_clip_sd, replace_prefix)
|
| 301 |
-
|
| 302 |
-
comfy.utils.save_torch_file(current_clip_sd, output_checkpoint, metadata=metadata)
|
| 303 |
-
return {}
|
| 304 |
-
|
| 305 |
-
class VAESave:
|
| 306 |
-
def __init__(self):
|
| 307 |
-
self.output_dir = folder_paths.get_output_directory()
|
| 308 |
-
|
| 309 |
-
@classmethod
|
| 310 |
-
def INPUT_TYPES(s):
|
| 311 |
-
return {"required": { "vae": ("VAE",),
|
| 312 |
-
"filename_prefix": ("STRING", {"default": "vae/ComfyUI_vae"}),},
|
| 313 |
-
"hidden": {"prompt": "PROMPT", "extra_pnginfo": "EXTRA_PNGINFO"},}
|
| 314 |
-
RETURN_TYPES = ()
|
| 315 |
-
FUNCTION = "save"
|
| 316 |
-
OUTPUT_NODE = True
|
| 317 |
-
|
| 318 |
-
CATEGORY = "advanced/model_merging"
|
| 319 |
-
|
| 320 |
-
def save(self, vae, filename_prefix, prompt=None, extra_pnginfo=None):
|
| 321 |
-
full_output_folder, filename, counter, subfolder, filename_prefix = folder_paths.get_save_image_path(filename_prefix, self.output_dir)
|
| 322 |
-
prompt_info = ""
|
| 323 |
-
if prompt is not None:
|
| 324 |
-
prompt_info = json.dumps(prompt)
|
| 325 |
-
|
| 326 |
-
metadata = {}
|
| 327 |
-
if not args.disable_metadata:
|
| 328 |
-
metadata["prompt"] = prompt_info
|
| 329 |
-
if extra_pnginfo is not None:
|
| 330 |
-
for x in extra_pnginfo:
|
| 331 |
-
metadata[x] = json.dumps(extra_pnginfo[x])
|
| 332 |
-
|
| 333 |
-
output_checkpoint = f"{filename}_{counter:05}_.safetensors"
|
| 334 |
-
output_checkpoint = os.path.join(full_output_folder, output_checkpoint)
|
| 335 |
-
|
| 336 |
-
comfy.utils.save_torch_file(vae.get_sd(), output_checkpoint, metadata=metadata)
|
| 337 |
-
return {}
|
| 338 |
-
|
| 339 |
-
class ModelSave:
|
| 340 |
-
def __init__(self):
|
| 341 |
-
self.output_dir = folder_paths.get_output_directory()
|
| 342 |
-
|
| 343 |
-
@classmethod
|
| 344 |
-
def INPUT_TYPES(s):
|
| 345 |
-
return {"required": { "model": ("MODEL",),
|
| 346 |
-
"filename_prefix": ("STRING", {"default": "diffusion_models/ComfyUI"}),},
|
| 347 |
-
"hidden": {"prompt": "PROMPT", "extra_pnginfo": "EXTRA_PNGINFO"},}
|
| 348 |
-
RETURN_TYPES = ()
|
| 349 |
-
FUNCTION = "save"
|
| 350 |
-
OUTPUT_NODE = True
|
| 351 |
-
|
| 352 |
-
CATEGORY = "advanced/model_merging"
|
| 353 |
-
|
| 354 |
-
def save(self, model, filename_prefix, prompt=None, extra_pnginfo=None):
|
| 355 |
-
save_checkpoint(model, filename_prefix=filename_prefix, output_dir=self.output_dir, prompt=prompt, extra_pnginfo=extra_pnginfo)
|
| 356 |
-
return {}
|
| 357 |
-
|
| 358 |
-
NODE_CLASS_MAPPINGS = {
|
| 359 |
-
"ModelMergeSimple": ModelMergeSimple,
|
| 360 |
-
"ModelMergeBlocks": ModelMergeBlocks,
|
| 361 |
-
"ModelMergeSubtract": ModelSubtract,
|
| 362 |
-
"ModelMergeAdd": ModelAdd,
|
| 363 |
-
"CheckpointSave": CheckpointSave,
|
| 364 |
-
"CLIPMergeSimple": CLIPMergeSimple,
|
| 365 |
-
"CLIPMergeSubtract": CLIPSubtract,
|
| 366 |
-
"CLIPMergeAdd": CLIPAdd,
|
| 367 |
-
"CLIPSave": CLIPSave,
|
| 368 |
-
"VAESave": VAESave,
|
| 369 |
-
"ModelSave": ModelSave,
|
| 370 |
-
}
|
| 371 |
-
|
| 372 |
-
NODE_DISPLAY_NAME_MAPPINGS = {
|
| 373 |
-
"CheckpointSave": "Save Checkpoint",
|
| 374 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
comfy_extras/nodes_model_merging_model_specific.py
DELETED
|
@@ -1,332 +0,0 @@
|
|
| 1 |
-
import comfy_extras.nodes_model_merging
|
| 2 |
-
|
| 3 |
-
class ModelMergeSD1(comfy_extras.nodes_model_merging.ModelMergeBlocks):
|
| 4 |
-
CATEGORY = "advanced/model_merging/model_specific"
|
| 5 |
-
@classmethod
|
| 6 |
-
def INPUT_TYPES(s):
|
| 7 |
-
arg_dict = { "model1": ("MODEL",),
|
| 8 |
-
"model2": ("MODEL",)}
|
| 9 |
-
|
| 10 |
-
argument = ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01})
|
| 11 |
-
|
| 12 |
-
arg_dict["time_embed."] = argument
|
| 13 |
-
arg_dict["label_emb."] = argument
|
| 14 |
-
|
| 15 |
-
for i in range(12):
|
| 16 |
-
arg_dict["input_blocks.{}.".format(i)] = argument
|
| 17 |
-
|
| 18 |
-
for i in range(3):
|
| 19 |
-
arg_dict["middle_block.{}.".format(i)] = argument
|
| 20 |
-
|
| 21 |
-
for i in range(12):
|
| 22 |
-
arg_dict["output_blocks.{}.".format(i)] = argument
|
| 23 |
-
|
| 24 |
-
arg_dict["out."] = argument
|
| 25 |
-
|
| 26 |
-
return {"required": arg_dict}
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
class ModelMergeSDXL(comfy_extras.nodes_model_merging.ModelMergeBlocks):
|
| 30 |
-
CATEGORY = "advanced/model_merging/model_specific"
|
| 31 |
-
|
| 32 |
-
@classmethod
|
| 33 |
-
def INPUT_TYPES(s):
|
| 34 |
-
arg_dict = { "model1": ("MODEL",),
|
| 35 |
-
"model2": ("MODEL",)}
|
| 36 |
-
|
| 37 |
-
argument = ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01})
|
| 38 |
-
|
| 39 |
-
arg_dict["time_embed."] = argument
|
| 40 |
-
arg_dict["label_emb."] = argument
|
| 41 |
-
|
| 42 |
-
for i in range(9):
|
| 43 |
-
arg_dict["input_blocks.{}".format(i)] = argument
|
| 44 |
-
|
| 45 |
-
for i in range(3):
|
| 46 |
-
arg_dict["middle_block.{}".format(i)] = argument
|
| 47 |
-
|
| 48 |
-
for i in range(9):
|
| 49 |
-
arg_dict["output_blocks.{}".format(i)] = argument
|
| 50 |
-
|
| 51 |
-
arg_dict["out."] = argument
|
| 52 |
-
|
| 53 |
-
return {"required": arg_dict}
|
| 54 |
-
|
| 55 |
-
class ModelMergeSD3_2B(comfy_extras.nodes_model_merging.ModelMergeBlocks):
|
| 56 |
-
CATEGORY = "advanced/model_merging/model_specific"
|
| 57 |
-
|
| 58 |
-
@classmethod
|
| 59 |
-
def INPUT_TYPES(s):
|
| 60 |
-
arg_dict = { "model1": ("MODEL",),
|
| 61 |
-
"model2": ("MODEL",)}
|
| 62 |
-
|
| 63 |
-
argument = ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01})
|
| 64 |
-
|
| 65 |
-
arg_dict["pos_embed."] = argument
|
| 66 |
-
arg_dict["x_embedder."] = argument
|
| 67 |
-
arg_dict["context_embedder."] = argument
|
| 68 |
-
arg_dict["y_embedder."] = argument
|
| 69 |
-
arg_dict["t_embedder."] = argument
|
| 70 |
-
|
| 71 |
-
for i in range(24):
|
| 72 |
-
arg_dict["joint_blocks.{}.".format(i)] = argument
|
| 73 |
-
|
| 74 |
-
arg_dict["final_layer."] = argument
|
| 75 |
-
|
| 76 |
-
return {"required": arg_dict}
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
class ModelMergeAuraflow(comfy_extras.nodes_model_merging.ModelMergeBlocks):
|
| 80 |
-
CATEGORY = "advanced/model_merging/model_specific"
|
| 81 |
-
|
| 82 |
-
@classmethod
|
| 83 |
-
def INPUT_TYPES(s):
|
| 84 |
-
arg_dict = { "model1": ("MODEL",),
|
| 85 |
-
"model2": ("MODEL",)}
|
| 86 |
-
|
| 87 |
-
argument = ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01})
|
| 88 |
-
|
| 89 |
-
arg_dict["init_x_linear."] = argument
|
| 90 |
-
arg_dict["positional_encoding"] = argument
|
| 91 |
-
arg_dict["cond_seq_linear."] = argument
|
| 92 |
-
arg_dict["register_tokens"] = argument
|
| 93 |
-
arg_dict["t_embedder."] = argument
|
| 94 |
-
|
| 95 |
-
for i in range(4):
|
| 96 |
-
arg_dict["double_layers.{}.".format(i)] = argument
|
| 97 |
-
|
| 98 |
-
for i in range(32):
|
| 99 |
-
arg_dict["single_layers.{}.".format(i)] = argument
|
| 100 |
-
|
| 101 |
-
arg_dict["modF."] = argument
|
| 102 |
-
arg_dict["final_linear."] = argument
|
| 103 |
-
|
| 104 |
-
return {"required": arg_dict}
|
| 105 |
-
|
| 106 |
-
class ModelMergeFlux1(comfy_extras.nodes_model_merging.ModelMergeBlocks):
|
| 107 |
-
CATEGORY = "advanced/model_merging/model_specific"
|
| 108 |
-
|
| 109 |
-
@classmethod
|
| 110 |
-
def INPUT_TYPES(s):
|
| 111 |
-
arg_dict = { "model1": ("MODEL",),
|
| 112 |
-
"model2": ("MODEL",)}
|
| 113 |
-
|
| 114 |
-
argument = ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01})
|
| 115 |
-
|
| 116 |
-
arg_dict["img_in."] = argument
|
| 117 |
-
arg_dict["time_in."] = argument
|
| 118 |
-
arg_dict["guidance_in"] = argument
|
| 119 |
-
arg_dict["vector_in."] = argument
|
| 120 |
-
arg_dict["txt_in."] = argument
|
| 121 |
-
|
| 122 |
-
for i in range(19):
|
| 123 |
-
arg_dict["double_blocks.{}.".format(i)] = argument
|
| 124 |
-
|
| 125 |
-
for i in range(38):
|
| 126 |
-
arg_dict["single_blocks.{}.".format(i)] = argument
|
| 127 |
-
|
| 128 |
-
arg_dict["final_layer."] = argument
|
| 129 |
-
|
| 130 |
-
return {"required": arg_dict}
|
| 131 |
-
|
| 132 |
-
class ModelMergeSD35_Large(comfy_extras.nodes_model_merging.ModelMergeBlocks):
|
| 133 |
-
CATEGORY = "advanced/model_merging/model_specific"
|
| 134 |
-
|
| 135 |
-
@classmethod
|
| 136 |
-
def INPUT_TYPES(s):
|
| 137 |
-
arg_dict = { "model1": ("MODEL",),
|
| 138 |
-
"model2": ("MODEL",)}
|
| 139 |
-
|
| 140 |
-
argument = ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01})
|
| 141 |
-
|
| 142 |
-
arg_dict["pos_embed."] = argument
|
| 143 |
-
arg_dict["x_embedder."] = argument
|
| 144 |
-
arg_dict["context_embedder."] = argument
|
| 145 |
-
arg_dict["y_embedder."] = argument
|
| 146 |
-
arg_dict["t_embedder."] = argument
|
| 147 |
-
|
| 148 |
-
for i in range(38):
|
| 149 |
-
arg_dict["joint_blocks.{}.".format(i)] = argument
|
| 150 |
-
|
| 151 |
-
arg_dict["final_layer."] = argument
|
| 152 |
-
|
| 153 |
-
return {"required": arg_dict}
|
| 154 |
-
|
| 155 |
-
class ModelMergeMochiPreview(comfy_extras.nodes_model_merging.ModelMergeBlocks):
|
| 156 |
-
CATEGORY = "advanced/model_merging/model_specific"
|
| 157 |
-
|
| 158 |
-
@classmethod
|
| 159 |
-
def INPUT_TYPES(s):
|
| 160 |
-
arg_dict = { "model1": ("MODEL",),
|
| 161 |
-
"model2": ("MODEL",)}
|
| 162 |
-
|
| 163 |
-
argument = ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01})
|
| 164 |
-
|
| 165 |
-
arg_dict["pos_frequencies."] = argument
|
| 166 |
-
arg_dict["t_embedder."] = argument
|
| 167 |
-
arg_dict["t5_y_embedder."] = argument
|
| 168 |
-
arg_dict["t5_yproj."] = argument
|
| 169 |
-
|
| 170 |
-
for i in range(48):
|
| 171 |
-
arg_dict["blocks.{}.".format(i)] = argument
|
| 172 |
-
|
| 173 |
-
arg_dict["final_layer."] = argument
|
| 174 |
-
|
| 175 |
-
return {"required": arg_dict}
|
| 176 |
-
|
| 177 |
-
class ModelMergeLTXV(comfy_extras.nodes_model_merging.ModelMergeBlocks):
|
| 178 |
-
CATEGORY = "advanced/model_merging/model_specific"
|
| 179 |
-
|
| 180 |
-
@classmethod
|
| 181 |
-
def INPUT_TYPES(s):
|
| 182 |
-
arg_dict = { "model1": ("MODEL",),
|
| 183 |
-
"model2": ("MODEL",)}
|
| 184 |
-
|
| 185 |
-
argument = ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01})
|
| 186 |
-
|
| 187 |
-
arg_dict["patchify_proj."] = argument
|
| 188 |
-
arg_dict["adaln_single."] = argument
|
| 189 |
-
arg_dict["caption_projection."] = argument
|
| 190 |
-
|
| 191 |
-
for i in range(28):
|
| 192 |
-
arg_dict["transformer_blocks.{}.".format(i)] = argument
|
| 193 |
-
|
| 194 |
-
arg_dict["scale_shift_table"] = argument
|
| 195 |
-
arg_dict["proj_out."] = argument
|
| 196 |
-
|
| 197 |
-
return {"required": arg_dict}
|
| 198 |
-
|
| 199 |
-
class ModelMergeCosmos7B(comfy_extras.nodes_model_merging.ModelMergeBlocks):
|
| 200 |
-
CATEGORY = "advanced/model_merging/model_specific"
|
| 201 |
-
|
| 202 |
-
@classmethod
|
| 203 |
-
def INPUT_TYPES(s):
|
| 204 |
-
arg_dict = { "model1": ("MODEL",),
|
| 205 |
-
"model2": ("MODEL",)}
|
| 206 |
-
|
| 207 |
-
argument = ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01})
|
| 208 |
-
|
| 209 |
-
arg_dict["pos_embedder."] = argument
|
| 210 |
-
arg_dict["extra_pos_embedder."] = argument
|
| 211 |
-
arg_dict["x_embedder."] = argument
|
| 212 |
-
arg_dict["t_embedder."] = argument
|
| 213 |
-
arg_dict["affline_norm."] = argument
|
| 214 |
-
|
| 215 |
-
|
| 216 |
-
for i in range(28):
|
| 217 |
-
arg_dict["blocks.block{}.".format(i)] = argument
|
| 218 |
-
|
| 219 |
-
arg_dict["final_layer."] = argument
|
| 220 |
-
|
| 221 |
-
return {"required": arg_dict}
|
| 222 |
-
|
| 223 |
-
class ModelMergeCosmos14B(comfy_extras.nodes_model_merging.ModelMergeBlocks):
|
| 224 |
-
CATEGORY = "advanced/model_merging/model_specific"
|
| 225 |
-
|
| 226 |
-
@classmethod
|
| 227 |
-
def INPUT_TYPES(s):
|
| 228 |
-
arg_dict = { "model1": ("MODEL",),
|
| 229 |
-
"model2": ("MODEL",)}
|
| 230 |
-
|
| 231 |
-
argument = ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01})
|
| 232 |
-
|
| 233 |
-
arg_dict["pos_embedder."] = argument
|
| 234 |
-
arg_dict["extra_pos_embedder."] = argument
|
| 235 |
-
arg_dict["x_embedder."] = argument
|
| 236 |
-
arg_dict["t_embedder."] = argument
|
| 237 |
-
arg_dict["affline_norm."] = argument
|
| 238 |
-
|
| 239 |
-
|
| 240 |
-
for i in range(36):
|
| 241 |
-
arg_dict["blocks.block{}.".format(i)] = argument
|
| 242 |
-
|
| 243 |
-
arg_dict["final_layer."] = argument
|
| 244 |
-
|
| 245 |
-
return {"required": arg_dict}
|
| 246 |
-
|
| 247 |
-
class ModelMergeWAN2_1(comfy_extras.nodes_model_merging.ModelMergeBlocks):
|
| 248 |
-
CATEGORY = "advanced/model_merging/model_specific"
|
| 249 |
-
DESCRIPTION = "1.3B model has 30 blocks, 14B model has 40 blocks. Image to video model has the extra img_emb."
|
| 250 |
-
|
| 251 |
-
@classmethod
|
| 252 |
-
def INPUT_TYPES(s):
|
| 253 |
-
arg_dict = { "model1": ("MODEL",),
|
| 254 |
-
"model2": ("MODEL",)}
|
| 255 |
-
|
| 256 |
-
argument = ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01})
|
| 257 |
-
|
| 258 |
-
arg_dict["patch_embedding."] = argument
|
| 259 |
-
arg_dict["time_embedding."] = argument
|
| 260 |
-
arg_dict["time_projection."] = argument
|
| 261 |
-
arg_dict["text_embedding."] = argument
|
| 262 |
-
arg_dict["img_emb."] = argument
|
| 263 |
-
|
| 264 |
-
for i in range(40):
|
| 265 |
-
arg_dict["blocks.{}.".format(i)] = argument
|
| 266 |
-
|
| 267 |
-
arg_dict["head."] = argument
|
| 268 |
-
|
| 269 |
-
return {"required": arg_dict}
|
| 270 |
-
|
| 271 |
-
class ModelMergeCosmosPredict2_2B(comfy_extras.nodes_model_merging.ModelMergeBlocks):
|
| 272 |
-
CATEGORY = "advanced/model_merging/model_specific"
|
| 273 |
-
|
| 274 |
-
@classmethod
|
| 275 |
-
def INPUT_TYPES(s):
|
| 276 |
-
arg_dict = { "model1": ("MODEL",),
|
| 277 |
-
"model2": ("MODEL",)}
|
| 278 |
-
|
| 279 |
-
argument = ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01})
|
| 280 |
-
|
| 281 |
-
arg_dict["pos_embedder."] = argument
|
| 282 |
-
arg_dict["x_embedder."] = argument
|
| 283 |
-
arg_dict["t_embedder."] = argument
|
| 284 |
-
arg_dict["t_embedding_norm."] = argument
|
| 285 |
-
|
| 286 |
-
|
| 287 |
-
for i in range(28):
|
| 288 |
-
arg_dict["blocks.{}.".format(i)] = argument
|
| 289 |
-
|
| 290 |
-
arg_dict["final_layer."] = argument
|
| 291 |
-
|
| 292 |
-
return {"required": arg_dict}
|
| 293 |
-
|
| 294 |
-
class ModelMergeCosmosPredict2_14B(comfy_extras.nodes_model_merging.ModelMergeBlocks):
|
| 295 |
-
CATEGORY = "advanced/model_merging/model_specific"
|
| 296 |
-
|
| 297 |
-
@classmethod
|
| 298 |
-
def INPUT_TYPES(s):
|
| 299 |
-
arg_dict = { "model1": ("MODEL",),
|
| 300 |
-
"model2": ("MODEL",)}
|
| 301 |
-
|
| 302 |
-
argument = ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01})
|
| 303 |
-
|
| 304 |
-
arg_dict["pos_embedder."] = argument
|
| 305 |
-
arg_dict["x_embedder."] = argument
|
| 306 |
-
arg_dict["t_embedder."] = argument
|
| 307 |
-
arg_dict["t_embedding_norm."] = argument
|
| 308 |
-
|
| 309 |
-
|
| 310 |
-
for i in range(36):
|
| 311 |
-
arg_dict["blocks.{}.".format(i)] = argument
|
| 312 |
-
|
| 313 |
-
arg_dict["final_layer."] = argument
|
| 314 |
-
|
| 315 |
-
return {"required": arg_dict}
|
| 316 |
-
|
| 317 |
-
NODE_CLASS_MAPPINGS = {
|
| 318 |
-
"ModelMergeSD1": ModelMergeSD1,
|
| 319 |
-
"ModelMergeSD2": ModelMergeSD1, #SD1 and SD2 have the same blocks
|
| 320 |
-
"ModelMergeSDXL": ModelMergeSDXL,
|
| 321 |
-
"ModelMergeSD3_2B": ModelMergeSD3_2B,
|
| 322 |
-
"ModelMergeAuraflow": ModelMergeAuraflow,
|
| 323 |
-
"ModelMergeFlux1": ModelMergeFlux1,
|
| 324 |
-
"ModelMergeSD35_Large": ModelMergeSD35_Large,
|
| 325 |
-
"ModelMergeMochiPreview": ModelMergeMochiPreview,
|
| 326 |
-
"ModelMergeLTXV": ModelMergeLTXV,
|
| 327 |
-
"ModelMergeCosmos7B": ModelMergeCosmos7B,
|
| 328 |
-
"ModelMergeCosmos14B": ModelMergeCosmos14B,
|
| 329 |
-
"ModelMergeWAN2_1": ModelMergeWAN2_1,
|
| 330 |
-
"ModelMergeCosmosPredict2_2B": ModelMergeCosmosPredict2_2B,
|
| 331 |
-
"ModelMergeCosmosPredict2_14B": ModelMergeCosmosPredict2_14B,
|
| 332 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
comfy_extras/nodes_morphology.py
DELETED
|
@@ -1,87 +0,0 @@
|
|
| 1 |
-
import torch
|
| 2 |
-
import comfy.model_management
|
| 3 |
-
|
| 4 |
-
from kornia.morphology import dilation, erosion, opening, closing, gradient, top_hat, bottom_hat
|
| 5 |
-
import kornia.color
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
class Morphology:
|
| 9 |
-
@classmethod
|
| 10 |
-
def INPUT_TYPES(s):
|
| 11 |
-
return {"required": {"image": ("IMAGE",),
|
| 12 |
-
"operation": (["erode", "dilate", "open", "close", "gradient", "bottom_hat", "top_hat"],),
|
| 13 |
-
"kernel_size": ("INT", {"default": 3, "min": 3, "max": 999, "step": 1}),
|
| 14 |
-
}}
|
| 15 |
-
|
| 16 |
-
RETURN_TYPES = ("IMAGE",)
|
| 17 |
-
FUNCTION = "process"
|
| 18 |
-
|
| 19 |
-
CATEGORY = "image/postprocessing"
|
| 20 |
-
|
| 21 |
-
def process(self, image, operation, kernel_size):
|
| 22 |
-
device = comfy.model_management.get_torch_device()
|
| 23 |
-
kernel = torch.ones(kernel_size, kernel_size, device=device)
|
| 24 |
-
image_k = image.to(device).movedim(-1, 1)
|
| 25 |
-
if operation == "erode":
|
| 26 |
-
output = erosion(image_k, kernel)
|
| 27 |
-
elif operation == "dilate":
|
| 28 |
-
output = dilation(image_k, kernel)
|
| 29 |
-
elif operation == "open":
|
| 30 |
-
output = opening(image_k, kernel)
|
| 31 |
-
elif operation == "close":
|
| 32 |
-
output = closing(image_k, kernel)
|
| 33 |
-
elif operation == "gradient":
|
| 34 |
-
output = gradient(image_k, kernel)
|
| 35 |
-
elif operation == "top_hat":
|
| 36 |
-
output = top_hat(image_k, kernel)
|
| 37 |
-
elif operation == "bottom_hat":
|
| 38 |
-
output = bottom_hat(image_k, kernel)
|
| 39 |
-
else:
|
| 40 |
-
raise ValueError(f"Invalid operation {operation} for morphology. Must be one of 'erode', 'dilate', 'open', 'close', 'gradient', 'tophat', 'bottomhat'")
|
| 41 |
-
img_out = output.to(comfy.model_management.intermediate_device()).movedim(1, -1)
|
| 42 |
-
return (img_out,)
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
class ImageRGBToYUV:
|
| 46 |
-
@classmethod
|
| 47 |
-
def INPUT_TYPES(s):
|
| 48 |
-
return {"required": { "image": ("IMAGE",),
|
| 49 |
-
}}
|
| 50 |
-
|
| 51 |
-
RETURN_TYPES = ("IMAGE", "IMAGE", "IMAGE")
|
| 52 |
-
RETURN_NAMES = ("Y", "U", "V")
|
| 53 |
-
FUNCTION = "execute"
|
| 54 |
-
|
| 55 |
-
CATEGORY = "image/batch"
|
| 56 |
-
|
| 57 |
-
def execute(self, image):
|
| 58 |
-
out = kornia.color.rgb_to_ycbcr(image.movedim(-1, 1)).movedim(1, -1)
|
| 59 |
-
return (out[..., 0:1].expand_as(image), out[..., 1:2].expand_as(image), out[..., 2:3].expand_as(image))
|
| 60 |
-
|
| 61 |
-
class ImageYUVToRGB:
|
| 62 |
-
@classmethod
|
| 63 |
-
def INPUT_TYPES(s):
|
| 64 |
-
return {"required": {"Y": ("IMAGE",),
|
| 65 |
-
"U": ("IMAGE",),
|
| 66 |
-
"V": ("IMAGE",),
|
| 67 |
-
}}
|
| 68 |
-
|
| 69 |
-
RETURN_TYPES = ("IMAGE",)
|
| 70 |
-
FUNCTION = "execute"
|
| 71 |
-
|
| 72 |
-
CATEGORY = "image/batch"
|
| 73 |
-
|
| 74 |
-
def execute(self, Y, U, V):
|
| 75 |
-
image = torch.cat([torch.mean(Y, dim=-1, keepdim=True), torch.mean(U, dim=-1, keepdim=True), torch.mean(V, dim=-1, keepdim=True)], dim=-1)
|
| 76 |
-
out = kornia.color.ycbcr_to_rgb(image.movedim(-1, 1)).movedim(1, -1)
|
| 77 |
-
return (out,)
|
| 78 |
-
|
| 79 |
-
NODE_CLASS_MAPPINGS = {
|
| 80 |
-
"Morphology": Morphology,
|
| 81 |
-
"ImageRGBToYUV": ImageRGBToYUV,
|
| 82 |
-
"ImageYUVToRGB": ImageYUVToRGB,
|
| 83 |
-
}
|
| 84 |
-
|
| 85 |
-
NODE_DISPLAY_NAME_MAPPINGS = {
|
| 86 |
-
"Morphology": "ImageMorphology",
|
| 87 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
comfy_extras/nodes_optimalsteps.py
DELETED
|
@@ -1,57 +0,0 @@
|
|
| 1 |
-
# from https://github.com/bebebe666/OptimalSteps
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
import numpy as np
|
| 5 |
-
import torch
|
| 6 |
-
|
| 7 |
-
def loglinear_interp(t_steps, num_steps):
|
| 8 |
-
"""
|
| 9 |
-
Performs log-linear interpolation of a given array of decreasing numbers.
|
| 10 |
-
"""
|
| 11 |
-
xs = np.linspace(0, 1, len(t_steps))
|
| 12 |
-
ys = np.log(t_steps[::-1])
|
| 13 |
-
|
| 14 |
-
new_xs = np.linspace(0, 1, num_steps)
|
| 15 |
-
new_ys = np.interp(new_xs, xs, ys)
|
| 16 |
-
|
| 17 |
-
interped_ys = np.exp(new_ys)[::-1].copy()
|
| 18 |
-
return interped_ys
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
NOISE_LEVELS = {"FLUX": [0.9968, 0.9886, 0.9819, 0.975, 0.966, 0.9471, 0.9158, 0.8287, 0.5512, 0.2808, 0.001],
|
| 22 |
-
"Wan":[1.0, 0.997, 0.995, 0.993, 0.991, 0.989, 0.987, 0.985, 0.98, 0.975, 0.973, 0.968, 0.96, 0.946, 0.927, 0.902, 0.864, 0.776, 0.539, 0.208, 0.001],
|
| 23 |
-
"Chroma": [0.992, 0.99, 0.988, 0.985, 0.982, 0.978, 0.973, 0.968, 0.961, 0.953, 0.943, 0.931, 0.917, 0.9, 0.881, 0.858, 0.832, 0.802, 0.769, 0.731, 0.69, 0.646, 0.599, 0.55, 0.501, 0.451, 0.402, 0.355, 0.311, 0.27, 0.232, 0.199, 0.169, 0.143, 0.12, 0.101, 0.084, 0.07, 0.058, 0.048, 0.001],
|
| 24 |
-
}
|
| 25 |
-
|
| 26 |
-
class OptimalStepsScheduler:
|
| 27 |
-
@classmethod
|
| 28 |
-
def INPUT_TYPES(s):
|
| 29 |
-
return {"required":
|
| 30 |
-
{"model_type": (["FLUX", "Wan", "Chroma"], ),
|
| 31 |
-
"steps": ("INT", {"default": 20, "min": 3, "max": 1000}),
|
| 32 |
-
"denoise": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}),
|
| 33 |
-
}
|
| 34 |
-
}
|
| 35 |
-
RETURN_TYPES = ("SIGMAS",)
|
| 36 |
-
CATEGORY = "sampling/custom_sampling/schedulers"
|
| 37 |
-
|
| 38 |
-
FUNCTION = "get_sigmas"
|
| 39 |
-
|
| 40 |
-
def get_sigmas(self, model_type, steps, denoise):
|
| 41 |
-
total_steps = steps
|
| 42 |
-
if denoise < 1.0:
|
| 43 |
-
if denoise <= 0.0:
|
| 44 |
-
return (torch.FloatTensor([]),)
|
| 45 |
-
total_steps = round(steps * denoise)
|
| 46 |
-
|
| 47 |
-
sigmas = NOISE_LEVELS[model_type][:]
|
| 48 |
-
if (steps + 1) != len(sigmas):
|
| 49 |
-
sigmas = loglinear_interp(sigmas, steps + 1)
|
| 50 |
-
|
| 51 |
-
sigmas = sigmas[-(total_steps + 1):]
|
| 52 |
-
sigmas[-1] = 0
|
| 53 |
-
return (torch.FloatTensor(sigmas), )
|
| 54 |
-
|
| 55 |
-
NODE_CLASS_MAPPINGS = {
|
| 56 |
-
"OptimalStepsScheduler": OptimalStepsScheduler,
|
| 57 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
comfy_extras/nodes_pag.py
DELETED
|
@@ -1,56 +0,0 @@
|
|
| 1 |
-
#Modified/simplified version of the node from: https://github.com/pamparamm/sd-perturbed-attention
|
| 2 |
-
#If you want the one with more options see the above repo.
|
| 3 |
-
|
| 4 |
-
#My modified one here is more basic but has less chances of breaking with ComfyUI updates.
|
| 5 |
-
|
| 6 |
-
import comfy.model_patcher
|
| 7 |
-
import comfy.samplers
|
| 8 |
-
|
| 9 |
-
class PerturbedAttentionGuidance:
|
| 10 |
-
@classmethod
|
| 11 |
-
def INPUT_TYPES(s):
|
| 12 |
-
return {
|
| 13 |
-
"required": {
|
| 14 |
-
"model": ("MODEL",),
|
| 15 |
-
"scale": ("FLOAT", {"default": 3.0, "min": 0.0, "max": 100.0, "step": 0.01, "round": 0.01}),
|
| 16 |
-
}
|
| 17 |
-
}
|
| 18 |
-
|
| 19 |
-
RETURN_TYPES = ("MODEL",)
|
| 20 |
-
FUNCTION = "patch"
|
| 21 |
-
|
| 22 |
-
CATEGORY = "model_patches/unet"
|
| 23 |
-
|
| 24 |
-
def patch(self, model, scale):
|
| 25 |
-
unet_block = "middle"
|
| 26 |
-
unet_block_id = 0
|
| 27 |
-
m = model.clone()
|
| 28 |
-
|
| 29 |
-
def perturbed_attention(q, k, v, extra_options, mask=None):
|
| 30 |
-
return v
|
| 31 |
-
|
| 32 |
-
def post_cfg_function(args):
|
| 33 |
-
model = args["model"]
|
| 34 |
-
cond_pred = args["cond_denoised"]
|
| 35 |
-
cond = args["cond"]
|
| 36 |
-
cfg_result = args["denoised"]
|
| 37 |
-
sigma = args["sigma"]
|
| 38 |
-
model_options = args["model_options"].copy()
|
| 39 |
-
x = args["input"]
|
| 40 |
-
|
| 41 |
-
if scale == 0:
|
| 42 |
-
return cfg_result
|
| 43 |
-
|
| 44 |
-
# Replace Self-attention with PAG
|
| 45 |
-
model_options = comfy.model_patcher.set_model_options_patch_replace(model_options, perturbed_attention, "attn1", unet_block, unet_block_id)
|
| 46 |
-
(pag,) = comfy.samplers.calc_cond_batch(model, [cond], x, sigma, model_options)
|
| 47 |
-
|
| 48 |
-
return cfg_result + (cond_pred - pag) * scale
|
| 49 |
-
|
| 50 |
-
m.set_model_sampler_post_cfg_function(post_cfg_function)
|
| 51 |
-
|
| 52 |
-
return (m,)
|
| 53 |
-
|
| 54 |
-
NODE_CLASS_MAPPINGS = {
|
| 55 |
-
"PerturbedAttentionGuidance": PerturbedAttentionGuidance,
|
| 56 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
comfy_extras/nodes_perpneg.py
DELETED
|
@@ -1,146 +0,0 @@
|
|
| 1 |
-
import torch
|
| 2 |
-
import comfy.model_management
|
| 3 |
-
import comfy.sampler_helpers
|
| 4 |
-
import comfy.samplers
|
| 5 |
-
import comfy.utils
|
| 6 |
-
import node_helpers
|
| 7 |
-
import math
|
| 8 |
-
|
| 9 |
-
def perp_neg(x, noise_pred_pos, noise_pred_neg, noise_pred_nocond, neg_scale, cond_scale):
|
| 10 |
-
pos = noise_pred_pos - noise_pred_nocond
|
| 11 |
-
neg = noise_pred_neg - noise_pred_nocond
|
| 12 |
-
|
| 13 |
-
perp = neg - ((torch.mul(neg, pos).sum())/(torch.norm(pos)**2)) * pos
|
| 14 |
-
perp_neg = perp * neg_scale
|
| 15 |
-
cfg_result = noise_pred_nocond + cond_scale*(pos - perp_neg)
|
| 16 |
-
return cfg_result
|
| 17 |
-
|
| 18 |
-
#TODO: This node should be removed, it has been replaced with PerpNegGuider
|
| 19 |
-
class PerpNeg:
|
| 20 |
-
@classmethod
|
| 21 |
-
def INPUT_TYPES(s):
|
| 22 |
-
return {"required": {"model": ("MODEL", ),
|
| 23 |
-
"empty_conditioning": ("CONDITIONING", ),
|
| 24 |
-
"neg_scale": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 100.0, "step": 0.01}),
|
| 25 |
-
}}
|
| 26 |
-
RETURN_TYPES = ("MODEL",)
|
| 27 |
-
FUNCTION = "patch"
|
| 28 |
-
|
| 29 |
-
CATEGORY = "_for_testing"
|
| 30 |
-
DEPRECATED = True
|
| 31 |
-
|
| 32 |
-
def patch(self, model, empty_conditioning, neg_scale):
|
| 33 |
-
m = model.clone()
|
| 34 |
-
nocond = comfy.sampler_helpers.convert_cond(empty_conditioning)
|
| 35 |
-
|
| 36 |
-
def cfg_function(args):
|
| 37 |
-
model = args["model"]
|
| 38 |
-
noise_pred_pos = args["cond_denoised"]
|
| 39 |
-
noise_pred_neg = args["uncond_denoised"]
|
| 40 |
-
cond_scale = args["cond_scale"]
|
| 41 |
-
x = args["input"]
|
| 42 |
-
sigma = args["sigma"]
|
| 43 |
-
model_options = args["model_options"]
|
| 44 |
-
nocond_processed = comfy.samplers.encode_model_conds(model.extra_conds, nocond, x, x.device, "negative")
|
| 45 |
-
|
| 46 |
-
(noise_pred_nocond,) = comfy.samplers.calc_cond_batch(model, [nocond_processed], x, sigma, model_options)
|
| 47 |
-
|
| 48 |
-
cfg_result = x - perp_neg(x, noise_pred_pos, noise_pred_neg, noise_pred_nocond, neg_scale, cond_scale)
|
| 49 |
-
return cfg_result
|
| 50 |
-
|
| 51 |
-
m.set_model_sampler_cfg_function(cfg_function)
|
| 52 |
-
|
| 53 |
-
return (m, )
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
class Guider_PerpNeg(comfy.samplers.CFGGuider):
|
| 57 |
-
def set_conds(self, positive, negative, empty_negative_prompt):
|
| 58 |
-
empty_negative_prompt = node_helpers.conditioning_set_values(empty_negative_prompt, {"prompt_type": "negative"})
|
| 59 |
-
self.inner_set_conds({"positive": positive, "empty_negative_prompt": empty_negative_prompt, "negative": negative})
|
| 60 |
-
|
| 61 |
-
def set_cfg(self, cfg, neg_scale):
|
| 62 |
-
self.cfg = cfg
|
| 63 |
-
self.neg_scale = neg_scale
|
| 64 |
-
|
| 65 |
-
def predict_noise(self, x, timestep, model_options={}, seed=None):
|
| 66 |
-
# in CFGGuider.predict_noise, we call sampling_function(), which uses cfg_function() to compute pos & neg
|
| 67 |
-
# but we'd rather do a single batch of sampling pos, neg, and empty, so we call calc_cond_batch([pos,neg,empty]) directly
|
| 68 |
-
|
| 69 |
-
positive_cond = self.conds.get("positive", None)
|
| 70 |
-
negative_cond = self.conds.get("negative", None)
|
| 71 |
-
empty_cond = self.conds.get("empty_negative_prompt", None)
|
| 72 |
-
|
| 73 |
-
if model_options.get("disable_cfg1_optimization", False) == False:
|
| 74 |
-
if math.isclose(self.neg_scale, 0.0):
|
| 75 |
-
negative_cond = None
|
| 76 |
-
if math.isclose(self.cfg, 1.0):
|
| 77 |
-
empty_cond = None
|
| 78 |
-
|
| 79 |
-
conds = [positive_cond, negative_cond, empty_cond]
|
| 80 |
-
|
| 81 |
-
out = comfy.samplers.calc_cond_batch(self.inner_model, conds, x, timestep, model_options)
|
| 82 |
-
|
| 83 |
-
# Apply pre_cfg_functions since sampling_function() is skipped
|
| 84 |
-
for fn in model_options.get("sampler_pre_cfg_function", []):
|
| 85 |
-
args = {"conds":conds, "conds_out": out, "cond_scale": self.cfg, "timestep": timestep,
|
| 86 |
-
"input": x, "sigma": timestep, "model": self.inner_model, "model_options": model_options}
|
| 87 |
-
out = fn(args)
|
| 88 |
-
|
| 89 |
-
noise_pred_pos, noise_pred_neg, noise_pred_empty = out
|
| 90 |
-
cfg_result = perp_neg(x, noise_pred_pos, noise_pred_neg, noise_pred_empty, self.neg_scale, self.cfg)
|
| 91 |
-
|
| 92 |
-
# normally this would be done in cfg_function, but we skipped
|
| 93 |
-
# that for efficiency: we can compute the noise predictions in
|
| 94 |
-
# a single call to calc_cond_batch() (rather than two)
|
| 95 |
-
# so we replicate the hook here
|
| 96 |
-
for fn in model_options.get("sampler_post_cfg_function", []):
|
| 97 |
-
args = {
|
| 98 |
-
"denoised": cfg_result,
|
| 99 |
-
"cond": positive_cond,
|
| 100 |
-
"uncond": negative_cond,
|
| 101 |
-
"cond_scale": self.cfg,
|
| 102 |
-
"model": self.inner_model,
|
| 103 |
-
"uncond_denoised": noise_pred_neg,
|
| 104 |
-
"cond_denoised": noise_pred_pos,
|
| 105 |
-
"sigma": timestep,
|
| 106 |
-
"model_options": model_options,
|
| 107 |
-
"input": x,
|
| 108 |
-
# not in the original call in samplers.py:cfg_function, but made available for future hooks
|
| 109 |
-
"empty_cond": empty_cond,
|
| 110 |
-
"empty_cond_denoised": noise_pred_empty,}
|
| 111 |
-
cfg_result = fn(args)
|
| 112 |
-
|
| 113 |
-
return cfg_result
|
| 114 |
-
|
| 115 |
-
class PerpNegGuider:
|
| 116 |
-
@classmethod
|
| 117 |
-
def INPUT_TYPES(s):
|
| 118 |
-
return {"required":
|
| 119 |
-
{"model": ("MODEL",),
|
| 120 |
-
"positive": ("CONDITIONING", ),
|
| 121 |
-
"negative": ("CONDITIONING", ),
|
| 122 |
-
"empty_conditioning": ("CONDITIONING", ),
|
| 123 |
-
"cfg": ("FLOAT", {"default": 8.0, "min": 0.0, "max": 100.0, "step":0.1, "round": 0.01}),
|
| 124 |
-
"neg_scale": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 100.0, "step": 0.01}),
|
| 125 |
-
}
|
| 126 |
-
}
|
| 127 |
-
|
| 128 |
-
RETURN_TYPES = ("GUIDER",)
|
| 129 |
-
|
| 130 |
-
FUNCTION = "get_guider"
|
| 131 |
-
CATEGORY = "_for_testing"
|
| 132 |
-
|
| 133 |
-
def get_guider(self, model, positive, negative, empty_conditioning, cfg, neg_scale):
|
| 134 |
-
guider = Guider_PerpNeg(model)
|
| 135 |
-
guider.set_conds(positive, negative, empty_conditioning)
|
| 136 |
-
guider.set_cfg(cfg, neg_scale)
|
| 137 |
-
return (guider,)
|
| 138 |
-
|
| 139 |
-
NODE_CLASS_MAPPINGS = {
|
| 140 |
-
"PerpNeg": PerpNeg,
|
| 141 |
-
"PerpNegGuider": PerpNegGuider,
|
| 142 |
-
}
|
| 143 |
-
|
| 144 |
-
NODE_DISPLAY_NAME_MAPPINGS = {
|
| 145 |
-
"PerpNeg": "Perp-Neg (DEPRECATED by PerpNegGuider)",
|
| 146 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
comfy_extras/nodes_photomaker.py
DELETED
|
@@ -1,188 +0,0 @@
|
|
| 1 |
-
import torch
|
| 2 |
-
import torch.nn as nn
|
| 3 |
-
import folder_paths
|
| 4 |
-
import comfy.clip_model
|
| 5 |
-
import comfy.clip_vision
|
| 6 |
-
import comfy.ops
|
| 7 |
-
|
| 8 |
-
# code for model from: https://github.com/TencentARC/PhotoMaker/blob/main/photomaker/model.py under Apache License Version 2.0
|
| 9 |
-
VISION_CONFIG_DICT = {
|
| 10 |
-
"hidden_size": 1024,
|
| 11 |
-
"image_size": 224,
|
| 12 |
-
"intermediate_size": 4096,
|
| 13 |
-
"num_attention_heads": 16,
|
| 14 |
-
"num_channels": 3,
|
| 15 |
-
"num_hidden_layers": 24,
|
| 16 |
-
"patch_size": 14,
|
| 17 |
-
"projection_dim": 768,
|
| 18 |
-
"hidden_act": "quick_gelu",
|
| 19 |
-
"model_type": "clip_vision_model",
|
| 20 |
-
}
|
| 21 |
-
|
| 22 |
-
class MLP(nn.Module):
|
| 23 |
-
def __init__(self, in_dim, out_dim, hidden_dim, use_residual=True, operations=comfy.ops):
|
| 24 |
-
super().__init__()
|
| 25 |
-
if use_residual:
|
| 26 |
-
assert in_dim == out_dim
|
| 27 |
-
self.layernorm = operations.LayerNorm(in_dim)
|
| 28 |
-
self.fc1 = operations.Linear(in_dim, hidden_dim)
|
| 29 |
-
self.fc2 = operations.Linear(hidden_dim, out_dim)
|
| 30 |
-
self.use_residual = use_residual
|
| 31 |
-
self.act_fn = nn.GELU()
|
| 32 |
-
|
| 33 |
-
def forward(self, x):
|
| 34 |
-
residual = x
|
| 35 |
-
x = self.layernorm(x)
|
| 36 |
-
x = self.fc1(x)
|
| 37 |
-
x = self.act_fn(x)
|
| 38 |
-
x = self.fc2(x)
|
| 39 |
-
if self.use_residual:
|
| 40 |
-
x = x + residual
|
| 41 |
-
return x
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
class FuseModule(nn.Module):
|
| 45 |
-
def __init__(self, embed_dim, operations):
|
| 46 |
-
super().__init__()
|
| 47 |
-
self.mlp1 = MLP(embed_dim * 2, embed_dim, embed_dim, use_residual=False, operations=operations)
|
| 48 |
-
self.mlp2 = MLP(embed_dim, embed_dim, embed_dim, use_residual=True, operations=operations)
|
| 49 |
-
self.layer_norm = operations.LayerNorm(embed_dim)
|
| 50 |
-
|
| 51 |
-
def fuse_fn(self, prompt_embeds, id_embeds):
|
| 52 |
-
stacked_id_embeds = torch.cat([prompt_embeds, id_embeds], dim=-1)
|
| 53 |
-
stacked_id_embeds = self.mlp1(stacked_id_embeds) + prompt_embeds
|
| 54 |
-
stacked_id_embeds = self.mlp2(stacked_id_embeds)
|
| 55 |
-
stacked_id_embeds = self.layer_norm(stacked_id_embeds)
|
| 56 |
-
return stacked_id_embeds
|
| 57 |
-
|
| 58 |
-
def forward(
|
| 59 |
-
self,
|
| 60 |
-
prompt_embeds,
|
| 61 |
-
id_embeds,
|
| 62 |
-
class_tokens_mask,
|
| 63 |
-
) -> torch.Tensor:
|
| 64 |
-
# id_embeds shape: [b, max_num_inputs, 1, 2048]
|
| 65 |
-
id_embeds = id_embeds.to(prompt_embeds.dtype)
|
| 66 |
-
num_inputs = class_tokens_mask.sum().unsqueeze(0) # TODO: check for training case
|
| 67 |
-
batch_size, max_num_inputs = id_embeds.shape[:2]
|
| 68 |
-
# seq_length: 77
|
| 69 |
-
seq_length = prompt_embeds.shape[1]
|
| 70 |
-
# flat_id_embeds shape: [b*max_num_inputs, 1, 2048]
|
| 71 |
-
flat_id_embeds = id_embeds.view(
|
| 72 |
-
-1, id_embeds.shape[-2], id_embeds.shape[-1]
|
| 73 |
-
)
|
| 74 |
-
# valid_id_mask [b*max_num_inputs]
|
| 75 |
-
valid_id_mask = (
|
| 76 |
-
torch.arange(max_num_inputs, device=flat_id_embeds.device)[None, :]
|
| 77 |
-
< num_inputs[:, None]
|
| 78 |
-
)
|
| 79 |
-
valid_id_embeds = flat_id_embeds[valid_id_mask.flatten()]
|
| 80 |
-
|
| 81 |
-
prompt_embeds = prompt_embeds.view(-1, prompt_embeds.shape[-1])
|
| 82 |
-
class_tokens_mask = class_tokens_mask.view(-1)
|
| 83 |
-
valid_id_embeds = valid_id_embeds.view(-1, valid_id_embeds.shape[-1])
|
| 84 |
-
# slice out the image token embeddings
|
| 85 |
-
image_token_embeds = prompt_embeds[class_tokens_mask]
|
| 86 |
-
stacked_id_embeds = self.fuse_fn(image_token_embeds, valid_id_embeds)
|
| 87 |
-
assert class_tokens_mask.sum() == stacked_id_embeds.shape[0], f"{class_tokens_mask.sum()} != {stacked_id_embeds.shape[0]}"
|
| 88 |
-
prompt_embeds.masked_scatter_(class_tokens_mask[:, None], stacked_id_embeds.to(prompt_embeds.dtype))
|
| 89 |
-
updated_prompt_embeds = prompt_embeds.view(batch_size, seq_length, -1)
|
| 90 |
-
return updated_prompt_embeds
|
| 91 |
-
|
| 92 |
-
class PhotoMakerIDEncoder(comfy.clip_model.CLIPVisionModelProjection):
|
| 93 |
-
def __init__(self):
|
| 94 |
-
self.load_device = comfy.model_management.text_encoder_device()
|
| 95 |
-
offload_device = comfy.model_management.text_encoder_offload_device()
|
| 96 |
-
dtype = comfy.model_management.text_encoder_dtype(self.load_device)
|
| 97 |
-
|
| 98 |
-
super().__init__(VISION_CONFIG_DICT, dtype, offload_device, comfy.ops.manual_cast)
|
| 99 |
-
self.visual_projection_2 = comfy.ops.manual_cast.Linear(1024, 1280, bias=False)
|
| 100 |
-
self.fuse_module = FuseModule(2048, comfy.ops.manual_cast)
|
| 101 |
-
|
| 102 |
-
def forward(self, id_pixel_values, prompt_embeds, class_tokens_mask):
|
| 103 |
-
b, num_inputs, c, h, w = id_pixel_values.shape
|
| 104 |
-
id_pixel_values = id_pixel_values.view(b * num_inputs, c, h, w)
|
| 105 |
-
|
| 106 |
-
shared_id_embeds = self.vision_model(id_pixel_values)[2]
|
| 107 |
-
id_embeds = self.visual_projection(shared_id_embeds)
|
| 108 |
-
id_embeds_2 = self.visual_projection_2(shared_id_embeds)
|
| 109 |
-
|
| 110 |
-
id_embeds = id_embeds.view(b, num_inputs, 1, -1)
|
| 111 |
-
id_embeds_2 = id_embeds_2.view(b, num_inputs, 1, -1)
|
| 112 |
-
|
| 113 |
-
id_embeds = torch.cat((id_embeds, id_embeds_2), dim=-1)
|
| 114 |
-
updated_prompt_embeds = self.fuse_module(prompt_embeds, id_embeds, class_tokens_mask)
|
| 115 |
-
|
| 116 |
-
return updated_prompt_embeds
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
class PhotoMakerLoader:
|
| 120 |
-
@classmethod
|
| 121 |
-
def INPUT_TYPES(s):
|
| 122 |
-
return {"required": { "photomaker_model_name": (folder_paths.get_filename_list("photomaker"), )}}
|
| 123 |
-
|
| 124 |
-
RETURN_TYPES = ("PHOTOMAKER",)
|
| 125 |
-
FUNCTION = "load_photomaker_model"
|
| 126 |
-
|
| 127 |
-
CATEGORY = "_for_testing/photomaker"
|
| 128 |
-
|
| 129 |
-
def load_photomaker_model(self, photomaker_model_name):
|
| 130 |
-
photomaker_model_path = folder_paths.get_full_path_or_raise("photomaker", photomaker_model_name)
|
| 131 |
-
photomaker_model = PhotoMakerIDEncoder()
|
| 132 |
-
data = comfy.utils.load_torch_file(photomaker_model_path, safe_load=True)
|
| 133 |
-
if "id_encoder" in data:
|
| 134 |
-
data = data["id_encoder"]
|
| 135 |
-
photomaker_model.load_state_dict(data)
|
| 136 |
-
return (photomaker_model,)
|
| 137 |
-
|
| 138 |
-
|
| 139 |
-
class PhotoMakerEncode:
|
| 140 |
-
@classmethod
|
| 141 |
-
def INPUT_TYPES(s):
|
| 142 |
-
return {"required": { "photomaker": ("PHOTOMAKER",),
|
| 143 |
-
"image": ("IMAGE",),
|
| 144 |
-
"clip": ("CLIP", ),
|
| 145 |
-
"text": ("STRING", {"multiline": True, "dynamicPrompts": True, "default": "photograph of photomaker"}),
|
| 146 |
-
}}
|
| 147 |
-
|
| 148 |
-
RETURN_TYPES = ("CONDITIONING",)
|
| 149 |
-
FUNCTION = "apply_photomaker"
|
| 150 |
-
|
| 151 |
-
CATEGORY = "_for_testing/photomaker"
|
| 152 |
-
|
| 153 |
-
def apply_photomaker(self, photomaker, image, clip, text):
|
| 154 |
-
special_token = "photomaker"
|
| 155 |
-
pixel_values = comfy.clip_vision.clip_preprocess(image.to(photomaker.load_device)).float()
|
| 156 |
-
try:
|
| 157 |
-
index = text.split(" ").index(special_token) + 1
|
| 158 |
-
except ValueError:
|
| 159 |
-
index = -1
|
| 160 |
-
tokens = clip.tokenize(text, return_word_ids=True)
|
| 161 |
-
out_tokens = {}
|
| 162 |
-
for k in tokens:
|
| 163 |
-
out_tokens[k] = []
|
| 164 |
-
for t in tokens[k]:
|
| 165 |
-
f = list(filter(lambda x: x[2] != index, t))
|
| 166 |
-
while len(f) < len(t):
|
| 167 |
-
f.append(t[-1])
|
| 168 |
-
out_tokens[k].append(f)
|
| 169 |
-
|
| 170 |
-
cond, pooled = clip.encode_from_tokens(out_tokens, return_pooled=True)
|
| 171 |
-
|
| 172 |
-
if index > 0:
|
| 173 |
-
token_index = index - 1
|
| 174 |
-
num_id_images = 1
|
| 175 |
-
class_tokens_mask = [True if token_index <= i < token_index+num_id_images else False for i in range(77)]
|
| 176 |
-
out = photomaker(id_pixel_values=pixel_values.unsqueeze(0), prompt_embeds=cond.to(photomaker.load_device),
|
| 177 |
-
class_tokens_mask=torch.tensor(class_tokens_mask, dtype=torch.bool, device=photomaker.load_device).unsqueeze(0))
|
| 178 |
-
else:
|
| 179 |
-
out = cond
|
| 180 |
-
|
| 181 |
-
return ([[out, {"pooled_output": pooled}]], )
|
| 182 |
-
|
| 183 |
-
|
| 184 |
-
NODE_CLASS_MAPPINGS = {
|
| 185 |
-
"PhotoMakerLoader": PhotoMakerLoader,
|
| 186 |
-
"PhotoMakerEncode": PhotoMakerEncode,
|
| 187 |
-
}
|
| 188 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
comfy_extras/nodes_pixart.py
DELETED
|
@@ -1,24 +0,0 @@
|
|
| 1 |
-
from nodes import MAX_RESOLUTION
|
| 2 |
-
|
| 3 |
-
class CLIPTextEncodePixArtAlpha:
|
| 4 |
-
@classmethod
|
| 5 |
-
def INPUT_TYPES(s):
|
| 6 |
-
return {"required": {
|
| 7 |
-
"width": ("INT", {"default": 1024.0, "min": 0, "max": MAX_RESOLUTION}),
|
| 8 |
-
"height": ("INT", {"default": 1024.0, "min": 0, "max": MAX_RESOLUTION}),
|
| 9 |
-
# "aspect_ratio": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.01}),
|
| 10 |
-
"text": ("STRING", {"multiline": True, "dynamicPrompts": True}), "clip": ("CLIP", ),
|
| 11 |
-
}}
|
| 12 |
-
|
| 13 |
-
RETURN_TYPES = ("CONDITIONING",)
|
| 14 |
-
FUNCTION = "encode"
|
| 15 |
-
CATEGORY = "advanced/conditioning"
|
| 16 |
-
DESCRIPTION = "Encodes text and sets the resolution conditioning for PixArt Alpha. Does not apply to PixArt Sigma."
|
| 17 |
-
|
| 18 |
-
def encode(self, clip, width, height, text):
|
| 19 |
-
tokens = clip.tokenize(text)
|
| 20 |
-
return (clip.encode_from_tokens_scheduled(tokens, add_dict={"width": width, "height": height}),)
|
| 21 |
-
|
| 22 |
-
NODE_CLASS_MAPPINGS = {
|
| 23 |
-
"CLIPTextEncodePixArtAlpha": CLIPTextEncodePixArtAlpha,
|
| 24 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
comfy_extras/nodes_post_processing.py
DELETED
|
@@ -1,281 +0,0 @@
|
|
| 1 |
-
import numpy as np
|
| 2 |
-
import torch
|
| 3 |
-
import torch.nn.functional as F
|
| 4 |
-
from PIL import Image
|
| 5 |
-
import math
|
| 6 |
-
|
| 7 |
-
import comfy.utils
|
| 8 |
-
import comfy.model_management
|
| 9 |
-
import node_helpers
|
| 10 |
-
|
| 11 |
-
class Blend:
|
| 12 |
-
def __init__(self):
|
| 13 |
-
pass
|
| 14 |
-
|
| 15 |
-
@classmethod
|
| 16 |
-
def INPUT_TYPES(s):
|
| 17 |
-
return {
|
| 18 |
-
"required": {
|
| 19 |
-
"image1": ("IMAGE",),
|
| 20 |
-
"image2": ("IMAGE",),
|
| 21 |
-
"blend_factor": ("FLOAT", {
|
| 22 |
-
"default": 0.5,
|
| 23 |
-
"min": 0.0,
|
| 24 |
-
"max": 1.0,
|
| 25 |
-
"step": 0.01
|
| 26 |
-
}),
|
| 27 |
-
"blend_mode": (["normal", "multiply", "screen", "overlay", "soft_light", "difference"],),
|
| 28 |
-
},
|
| 29 |
-
}
|
| 30 |
-
|
| 31 |
-
RETURN_TYPES = ("IMAGE",)
|
| 32 |
-
FUNCTION = "blend_images"
|
| 33 |
-
|
| 34 |
-
CATEGORY = "image/postprocessing"
|
| 35 |
-
|
| 36 |
-
def blend_images(self, image1: torch.Tensor, image2: torch.Tensor, blend_factor: float, blend_mode: str):
|
| 37 |
-
image1, image2 = node_helpers.image_alpha_fix(image1, image2)
|
| 38 |
-
image2 = image2.to(image1.device)
|
| 39 |
-
if image1.shape != image2.shape:
|
| 40 |
-
image2 = image2.permute(0, 3, 1, 2)
|
| 41 |
-
image2 = comfy.utils.common_upscale(image2, image1.shape[2], image1.shape[1], upscale_method='bicubic', crop='center')
|
| 42 |
-
image2 = image2.permute(0, 2, 3, 1)
|
| 43 |
-
|
| 44 |
-
blended_image = self.blend_mode(image1, image2, blend_mode)
|
| 45 |
-
blended_image = image1 * (1 - blend_factor) + blended_image * blend_factor
|
| 46 |
-
blended_image = torch.clamp(blended_image, 0, 1)
|
| 47 |
-
return (blended_image,)
|
| 48 |
-
|
| 49 |
-
def blend_mode(self, img1, img2, mode):
|
| 50 |
-
if mode == "normal":
|
| 51 |
-
return img2
|
| 52 |
-
elif mode == "multiply":
|
| 53 |
-
return img1 * img2
|
| 54 |
-
elif mode == "screen":
|
| 55 |
-
return 1 - (1 - img1) * (1 - img2)
|
| 56 |
-
elif mode == "overlay":
|
| 57 |
-
return torch.where(img1 <= 0.5, 2 * img1 * img2, 1 - 2 * (1 - img1) * (1 - img2))
|
| 58 |
-
elif mode == "soft_light":
|
| 59 |
-
return torch.where(img2 <= 0.5, img1 - (1 - 2 * img2) * img1 * (1 - img1), img1 + (2 * img2 - 1) * (self.g(img1) - img1))
|
| 60 |
-
elif mode == "difference":
|
| 61 |
-
return img1 - img2
|
| 62 |
-
else:
|
| 63 |
-
raise ValueError(f"Unsupported blend mode: {mode}")
|
| 64 |
-
|
| 65 |
-
def g(self, x):
|
| 66 |
-
return torch.where(x <= 0.25, ((16 * x - 12) * x + 4) * x, torch.sqrt(x))
|
| 67 |
-
|
| 68 |
-
def gaussian_kernel(kernel_size: int, sigma: float, device=None):
|
| 69 |
-
x, y = torch.meshgrid(torch.linspace(-1, 1, kernel_size, device=device), torch.linspace(-1, 1, kernel_size, device=device), indexing="ij")
|
| 70 |
-
d = torch.sqrt(x * x + y * y)
|
| 71 |
-
g = torch.exp(-(d * d) / (2.0 * sigma * sigma))
|
| 72 |
-
return g / g.sum()
|
| 73 |
-
|
| 74 |
-
class Blur:
|
| 75 |
-
def __init__(self):
|
| 76 |
-
pass
|
| 77 |
-
|
| 78 |
-
@classmethod
|
| 79 |
-
def INPUT_TYPES(s):
|
| 80 |
-
return {
|
| 81 |
-
"required": {
|
| 82 |
-
"image": ("IMAGE",),
|
| 83 |
-
"blur_radius": ("INT", {
|
| 84 |
-
"default": 1,
|
| 85 |
-
"min": 1,
|
| 86 |
-
"max": 31,
|
| 87 |
-
"step": 1
|
| 88 |
-
}),
|
| 89 |
-
"sigma": ("FLOAT", {
|
| 90 |
-
"default": 1.0,
|
| 91 |
-
"min": 0.1,
|
| 92 |
-
"max": 10.0,
|
| 93 |
-
"step": 0.1
|
| 94 |
-
}),
|
| 95 |
-
},
|
| 96 |
-
}
|
| 97 |
-
|
| 98 |
-
RETURN_TYPES = ("IMAGE",)
|
| 99 |
-
FUNCTION = "blur"
|
| 100 |
-
|
| 101 |
-
CATEGORY = "image/postprocessing"
|
| 102 |
-
|
| 103 |
-
def blur(self, image: torch.Tensor, blur_radius: int, sigma: float):
|
| 104 |
-
if blur_radius == 0:
|
| 105 |
-
return (image,)
|
| 106 |
-
|
| 107 |
-
image = image.to(comfy.model_management.get_torch_device())
|
| 108 |
-
batch_size, height, width, channels = image.shape
|
| 109 |
-
|
| 110 |
-
kernel_size = blur_radius * 2 + 1
|
| 111 |
-
kernel = gaussian_kernel(kernel_size, sigma, device=image.device).repeat(channels, 1, 1).unsqueeze(1)
|
| 112 |
-
|
| 113 |
-
image = image.permute(0, 3, 1, 2) # Torch wants (B, C, H, W) we use (B, H, W, C)
|
| 114 |
-
padded_image = F.pad(image, (blur_radius,blur_radius,blur_radius,blur_radius), 'reflect')
|
| 115 |
-
blurred = F.conv2d(padded_image, kernel, padding=kernel_size // 2, groups=channels)[:,:,blur_radius:-blur_radius, blur_radius:-blur_radius]
|
| 116 |
-
blurred = blurred.permute(0, 2, 3, 1)
|
| 117 |
-
|
| 118 |
-
return (blurred.to(comfy.model_management.intermediate_device()),)
|
| 119 |
-
|
| 120 |
-
class Quantize:
|
| 121 |
-
def __init__(self):
|
| 122 |
-
pass
|
| 123 |
-
|
| 124 |
-
@classmethod
|
| 125 |
-
def INPUT_TYPES(s):
|
| 126 |
-
return {
|
| 127 |
-
"required": {
|
| 128 |
-
"image": ("IMAGE",),
|
| 129 |
-
"colors": ("INT", {
|
| 130 |
-
"default": 256,
|
| 131 |
-
"min": 1,
|
| 132 |
-
"max": 256,
|
| 133 |
-
"step": 1
|
| 134 |
-
}),
|
| 135 |
-
"dither": (["none", "floyd-steinberg", "bayer-2", "bayer-4", "bayer-8", "bayer-16"],),
|
| 136 |
-
},
|
| 137 |
-
}
|
| 138 |
-
|
| 139 |
-
RETURN_TYPES = ("IMAGE",)
|
| 140 |
-
FUNCTION = "quantize"
|
| 141 |
-
|
| 142 |
-
CATEGORY = "image/postprocessing"
|
| 143 |
-
|
| 144 |
-
@staticmethod
|
| 145 |
-
def bayer(im, pal_im, order):
|
| 146 |
-
def normalized_bayer_matrix(n):
|
| 147 |
-
if n == 0:
|
| 148 |
-
return np.zeros((1,1), "float32")
|
| 149 |
-
else:
|
| 150 |
-
q = 4 ** n
|
| 151 |
-
m = q * normalized_bayer_matrix(n - 1)
|
| 152 |
-
return np.bmat(((m-1.5, m+0.5), (m+1.5, m-0.5))) / q
|
| 153 |
-
|
| 154 |
-
num_colors = len(pal_im.getpalette()) // 3
|
| 155 |
-
spread = 2 * 256 / num_colors
|
| 156 |
-
bayer_n = int(math.log2(order))
|
| 157 |
-
bayer_matrix = torch.from_numpy(spread * normalized_bayer_matrix(bayer_n) + 0.5)
|
| 158 |
-
|
| 159 |
-
result = torch.from_numpy(np.array(im).astype(np.float32))
|
| 160 |
-
tw = math.ceil(result.shape[0] / bayer_matrix.shape[0])
|
| 161 |
-
th = math.ceil(result.shape[1] / bayer_matrix.shape[1])
|
| 162 |
-
tiled_matrix = bayer_matrix.tile(tw, th).unsqueeze(-1)
|
| 163 |
-
result.add_(tiled_matrix[:result.shape[0],:result.shape[1]]).clamp_(0, 255)
|
| 164 |
-
result = result.to(dtype=torch.uint8)
|
| 165 |
-
|
| 166 |
-
im = Image.fromarray(result.cpu().numpy())
|
| 167 |
-
im = im.quantize(palette=pal_im, dither=Image.Dither.NONE)
|
| 168 |
-
return im
|
| 169 |
-
|
| 170 |
-
def quantize(self, image: torch.Tensor, colors: int, dither: str):
|
| 171 |
-
batch_size, height, width, _ = image.shape
|
| 172 |
-
result = torch.zeros_like(image)
|
| 173 |
-
|
| 174 |
-
for b in range(batch_size):
|
| 175 |
-
im = Image.fromarray((image[b] * 255).to(torch.uint8).numpy(), mode='RGB')
|
| 176 |
-
|
| 177 |
-
pal_im = im.quantize(colors=colors) # Required as described in https://github.com/python-pillow/Pillow/issues/5836
|
| 178 |
-
|
| 179 |
-
if dither == "none":
|
| 180 |
-
quantized_image = im.quantize(palette=pal_im, dither=Image.Dither.NONE)
|
| 181 |
-
elif dither == "floyd-steinberg":
|
| 182 |
-
quantized_image = im.quantize(palette=pal_im, dither=Image.Dither.FLOYDSTEINBERG)
|
| 183 |
-
elif dither.startswith("bayer"):
|
| 184 |
-
order = int(dither.split('-')[-1])
|
| 185 |
-
quantized_image = Quantize.bayer(im, pal_im, order)
|
| 186 |
-
|
| 187 |
-
quantized_array = torch.tensor(np.array(quantized_image.convert("RGB"))).float() / 255
|
| 188 |
-
result[b] = quantized_array
|
| 189 |
-
|
| 190 |
-
return (result,)
|
| 191 |
-
|
| 192 |
-
class Sharpen:
|
| 193 |
-
def __init__(self):
|
| 194 |
-
pass
|
| 195 |
-
|
| 196 |
-
@classmethod
|
| 197 |
-
def INPUT_TYPES(s):
|
| 198 |
-
return {
|
| 199 |
-
"required": {
|
| 200 |
-
"image": ("IMAGE",),
|
| 201 |
-
"sharpen_radius": ("INT", {
|
| 202 |
-
"default": 1,
|
| 203 |
-
"min": 1,
|
| 204 |
-
"max": 31,
|
| 205 |
-
"step": 1
|
| 206 |
-
}),
|
| 207 |
-
"sigma": ("FLOAT", {
|
| 208 |
-
"default": 1.0,
|
| 209 |
-
"min": 0.1,
|
| 210 |
-
"max": 10.0,
|
| 211 |
-
"step": 0.01
|
| 212 |
-
}),
|
| 213 |
-
"alpha": ("FLOAT", {
|
| 214 |
-
"default": 1.0,
|
| 215 |
-
"min": 0.0,
|
| 216 |
-
"max": 5.0,
|
| 217 |
-
"step": 0.01
|
| 218 |
-
}),
|
| 219 |
-
},
|
| 220 |
-
}
|
| 221 |
-
|
| 222 |
-
RETURN_TYPES = ("IMAGE",)
|
| 223 |
-
FUNCTION = "sharpen"
|
| 224 |
-
|
| 225 |
-
CATEGORY = "image/postprocessing"
|
| 226 |
-
|
| 227 |
-
def sharpen(self, image: torch.Tensor, sharpen_radius: int, sigma:float, alpha: float):
|
| 228 |
-
if sharpen_radius == 0:
|
| 229 |
-
return (image,)
|
| 230 |
-
|
| 231 |
-
batch_size, height, width, channels = image.shape
|
| 232 |
-
image = image.to(comfy.model_management.get_torch_device())
|
| 233 |
-
|
| 234 |
-
kernel_size = sharpen_radius * 2 + 1
|
| 235 |
-
kernel = gaussian_kernel(kernel_size, sigma, device=image.device) * -(alpha*10)
|
| 236 |
-
center = kernel_size // 2
|
| 237 |
-
kernel[center, center] = kernel[center, center] - kernel.sum() + 1.0
|
| 238 |
-
kernel = kernel.repeat(channels, 1, 1).unsqueeze(1)
|
| 239 |
-
|
| 240 |
-
tensor_image = image.permute(0, 3, 1, 2) # Torch wants (B, C, H, W) we use (B, H, W, C)
|
| 241 |
-
tensor_image = F.pad(tensor_image, (sharpen_radius,sharpen_radius,sharpen_radius,sharpen_radius), 'reflect')
|
| 242 |
-
sharpened = F.conv2d(tensor_image, kernel, padding=center, groups=channels)[:,:,sharpen_radius:-sharpen_radius, sharpen_radius:-sharpen_radius]
|
| 243 |
-
sharpened = sharpened.permute(0, 2, 3, 1)
|
| 244 |
-
|
| 245 |
-
result = torch.clamp(sharpened, 0, 1)
|
| 246 |
-
|
| 247 |
-
return (result.to(comfy.model_management.intermediate_device()),)
|
| 248 |
-
|
| 249 |
-
class ImageScaleToTotalPixels:
|
| 250 |
-
upscale_methods = ["nearest-exact", "bilinear", "area", "bicubic", "lanczos"]
|
| 251 |
-
crop_methods = ["disabled", "center"]
|
| 252 |
-
|
| 253 |
-
@classmethod
|
| 254 |
-
def INPUT_TYPES(s):
|
| 255 |
-
return {"required": { "image": ("IMAGE",), "upscale_method": (s.upscale_methods,),
|
| 256 |
-
"megapixels": ("FLOAT", {"default": 1.0, "min": 0.01, "max": 16.0, "step": 0.01}),
|
| 257 |
-
}}
|
| 258 |
-
RETURN_TYPES = ("IMAGE",)
|
| 259 |
-
FUNCTION = "upscale"
|
| 260 |
-
|
| 261 |
-
CATEGORY = "image/upscaling"
|
| 262 |
-
|
| 263 |
-
def upscale(self, image, upscale_method, megapixels):
|
| 264 |
-
samples = image.movedim(-1,1)
|
| 265 |
-
total = int(megapixels * 1024 * 1024)
|
| 266 |
-
|
| 267 |
-
scale_by = math.sqrt(total / (samples.shape[3] * samples.shape[2]))
|
| 268 |
-
width = round(samples.shape[3] * scale_by)
|
| 269 |
-
height = round(samples.shape[2] * scale_by)
|
| 270 |
-
|
| 271 |
-
s = comfy.utils.common_upscale(samples, width, height, upscale_method, "disabled")
|
| 272 |
-
s = s.movedim(1,-1)
|
| 273 |
-
return (s,)
|
| 274 |
-
|
| 275 |
-
NODE_CLASS_MAPPINGS = {
|
| 276 |
-
"ImageBlend": Blend,
|
| 277 |
-
"ImageBlur": Blur,
|
| 278 |
-
"ImageQuantize": Quantize,
|
| 279 |
-
"ImageSharpen": Sharpen,
|
| 280 |
-
"ImageScaleToTotalPixels": ImageScaleToTotalPixels,
|
| 281 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|