SuperRealCo commited on
Commit
e21db33
·
verified ·
1 Parent(s): ea75430

Delete comfy_extras

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. comfy_extras/chainner_models/model_loading.py +0 -6
  2. comfy_extras/nodes_ace.py +0 -49
  3. comfy_extras/nodes_advanced_samplers.py +0 -111
  4. comfy_extras/nodes_align_your_steps.py +0 -53
  5. comfy_extras/nodes_apg.py +0 -76
  6. comfy_extras/nodes_attention_multiply.py +0 -120
  7. comfy_extras/nodes_audio.py +0 -334
  8. comfy_extras/nodes_camera_trajectory.py +0 -218
  9. comfy_extras/nodes_canny.py +0 -25
  10. comfy_extras/nodes_cfg.py +0 -72
  11. comfy_extras/nodes_clip_sdxl.py +0 -54
  12. comfy_extras/nodes_compositing.py +0 -214
  13. comfy_extras/nodes_cond.py +0 -49
  14. comfy_extras/nodes_controlnet.py +0 -60
  15. comfy_extras/nodes_cosmos.py +0 -128
  16. comfy_extras/nodes_custom_sampler.py +0 -932
  17. comfy_extras/nodes_differential_diffusion.py +0 -42
  18. comfy_extras/nodes_edit_model.py +0 -26
  19. comfy_extras/nodes_flux.py +0 -108
  20. comfy_extras/nodes_freelunch.py +0 -113
  21. comfy_extras/nodes_fresca.py +0 -103
  22. comfy_extras/nodes_gits.py +0 -369
  23. comfy_extras/nodes_hidream.py +0 -55
  24. comfy_extras/nodes_hooks.py +0 -745
  25. comfy_extras/nodes_hunyuan.py +0 -123
  26. comfy_extras/nodes_hunyuan3d.py +0 -634
  27. comfy_extras/nodes_hypernetwork.py +0 -120
  28. comfy_extras/nodes_hypertile.py +0 -81
  29. comfy_extras/nodes_images.py +0 -642
  30. comfy_extras/nodes_ip2p.py +0 -45
  31. comfy_extras/nodes_latent.py +0 -288
  32. comfy_extras/nodes_load_3d.py +0 -182
  33. comfy_extras/nodes_lora_extract.py +0 -119
  34. comfy_extras/nodes_lotus.py +0 -29
  35. comfy_extras/nodes_lt.py +0 -474
  36. comfy_extras/nodes_lumina2.py +0 -104
  37. comfy_extras/nodes_mahiro.py +0 -41
  38. comfy_extras/nodes_mask.py +0 -412
  39. comfy_extras/nodes_mochi.py +0 -23
  40. comfy_extras/nodes_model_advanced.py +0 -329
  41. comfy_extras/nodes_model_downscale.py +0 -53
  42. comfy_extras/nodes_model_merging.py +0 -374
  43. comfy_extras/nodes_model_merging_model_specific.py +0 -332
  44. comfy_extras/nodes_morphology.py +0 -87
  45. comfy_extras/nodes_optimalsteps.py +0 -57
  46. comfy_extras/nodes_pag.py +0 -56
  47. comfy_extras/nodes_perpneg.py +0 -146
  48. comfy_extras/nodes_photomaker.py +0 -188
  49. comfy_extras/nodes_pixart.py +0 -24
  50. 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
- }