Add files using upload-large-folder tool
Browse files- .gitattributes +1 -0
- ComfyUI/comfy_extras/nodes_attention_multiply.py +120 -0
- ComfyUI/comfy_extras/nodes_clip_sdxl.py +54 -0
- ComfyUI/comfy_extras/nodes_controlnet.py +60 -0
- ComfyUI/comfy_extras/nodes_differential_diffusion.py +42 -0
- ComfyUI/comfy_extras/nodes_freelunch.py +113 -0
- ComfyUI/comfy_extras/nodes_fresca.py +103 -0
- ComfyUI/comfy_extras/nodes_gits.py +369 -0
- ComfyUI/comfy_extras/nodes_hidream.py +55 -0
- ComfyUI/comfy_extras/nodes_hooks.py +745 -0
- ComfyUI/comfy_extras/nodes_hunyuan.py +123 -0
- ComfyUI/comfy_extras/nodes_hunyuan3d.py +634 -0
- ComfyUI/comfy_extras/nodes_hypernetwork.py +120 -0
- ComfyUI/comfy_extras/nodes_hypertile.py +81 -0
- ComfyUI/comfy_extras/nodes_images.py +642 -0
- ComfyUI/comfy_extras/nodes_ip2p.py +45 -0
- ComfyUI/comfy_extras/nodes_latent.py +288 -0
- ComfyUI/comfy_extras/nodes_load_3d.py +182 -0
- ComfyUI/comfy_extras/nodes_lora_extract.py +119 -0
- ComfyUI/comfy_extras/nodes_lotus.py +29 -0
- ComfyUI/comfy_extras/nodes_lt.py +474 -0
- ComfyUI/comfy_extras/nodes_lumina2.py +104 -0
- ComfyUI/comfy_extras/nodes_mahiro.py +41 -0
- ComfyUI/comfy_extras/nodes_mask.py +412 -0
- ComfyUI/comfy_extras/nodes_mochi.py +23 -0
- ComfyUI/comfy_extras/nodes_model_advanced.py +329 -0
- ComfyUI/comfy_extras/nodes_pag.py +56 -0
- ComfyUI/comfy_extras/nodes_perpneg.py +146 -0
- ComfyUI/comfy_extras/nodes_preview_any.py +43 -0
- ComfyUI/comfy_extras/nodes_tcfg.py +71 -0
- ComfyUI/comfy_extras/nodes_tomesd.py +176 -0
- ComfyUI/comfy_extras/nodes_torch_compile.py +23 -0
- ComfyUI/comfy_extras/nodes_train.py +877 -0
- ComfyUI/comfy_extras/nodes_video.py +241 -0
- ComfyUI/comfy_extras/nodes_wan.py +742 -0
- ComfyUI/custom_nodes/comfyui-kjnodes/web/js/jsnodes.js +413 -0
- ComfyUI/models/configs/anything_v3.yaml +73 -0
- ComfyUI/models/configs/v1-inference.yaml +70 -0
- ComfyUI/models/configs/v1-inference_clip_skip_2.yaml +73 -0
- ComfyUI/models/configs/v1-inference_clip_skip_2_fp16.yaml +74 -0
- ComfyUI/models/configs/v1-inference_fp16.yaml +71 -0
- ComfyUI/models/configs/v1-inpainting-inference.yaml +71 -0
- ComfyUI/models/configs/v2-inference-v.yaml +68 -0
- ComfyUI/models/configs/v2-inference-v_fp32.yaml +68 -0
- ComfyUI/models/configs/v2-inference.yaml +67 -0
- ComfyUI/models/configs/v2-inference_fp32.yaml +67 -0
- ComfyUI/models/configs/v2-inpainting-inference.yaml +158 -0
- ComfyUI/models/controlnet/put_controlnets_and_t2i_here +0 -0
- ComfyUI/models/diffusers/put_diffusers_models_here +0 -0
- ComfyUI/models/diffusion_models/put_diffusion_model_files_here.safetensors +0 -0
.gitattributes
CHANGED
|
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
+
*.whl filter=lfs diff=lfs merge=lfs -text
|
ComfyUI/comfy_extras/nodes_attention_multiply.py
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
+
}
|
ComfyUI/comfy_extras/nodes_clip_sdxl.py
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
+
}
|
ComfyUI/comfy_extras/nodes_controlnet.py
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
+
}
|
ComfyUI/comfy_extras/nodes_differential_diffusion.py
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
+
}
|
ComfyUI/comfy_extras/nodes_freelunch.py
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
+
}
|
ComfyUI/comfy_extras/nodes_fresca.py
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
+
}
|
ComfyUI/comfy_extras/nodes_gits.py
ADDED
|
@@ -0,0 +1,369 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
+
}
|
ComfyUI/comfy_extras/nodes_hidream.py
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
+
}
|
ComfyUI/comfy_extras/nodes_hooks.py
ADDED
|
@@ -0,0 +1,745 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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
|
ComfyUI/comfy_extras/nodes_hunyuan.py
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
+
}
|
ComfyUI/comfy_extras/nodes_hunyuan3d.py
ADDED
|
@@ -0,0 +1,634 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
+
}
|
ComfyUI/comfy_extras/nodes_hypernetwork.py
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
+
}
|
ComfyUI/comfy_extras/nodes_hypertile.py
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
+
}
|
ComfyUI/comfy_extras/nodes_images.py
ADDED
|
@@ -0,0 +1,642 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
+
}
|
ComfyUI/comfy_extras/nodes_ip2p.py
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
+
}
|
ComfyUI/comfy_extras/nodes_latent.py
ADDED
|
@@ -0,0 +1,288 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
+
}
|
ComfyUI/comfy_extras/nodes_load_3d.py
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
+
}
|
ComfyUI/comfy_extras/nodes_lora_extract.py
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
+
}
|
ComfyUI/comfy_extras/nodes_lotus.py
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
+
}
|
ComfyUI/comfy_extras/nodes_lt.py
ADDED
|
@@ -0,0 +1,474 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
+
}
|
ComfyUI/comfy_extras/nodes_lumina2.py
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
+
}
|
ComfyUI/comfy_extras/nodes_mahiro.py
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
+
}
|
ComfyUI/comfy_extras/nodes_mask.py
ADDED
|
@@ -0,0 +1,412 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
+
}
|
ComfyUI/comfy_extras/nodes_mochi.py
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
+
}
|
ComfyUI/comfy_extras/nodes_model_advanced.py
ADDED
|
@@ -0,0 +1,329 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
+
}
|
ComfyUI/comfy_extras/nodes_pag.py
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
+
}
|
ComfyUI/comfy_extras/nodes_perpneg.py
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
+
}
|
ComfyUI/comfy_extras/nodes_preview_any.py
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import json
|
| 2 |
+
from comfy.comfy_types.node_typing import IO
|
| 3 |
+
|
| 4 |
+
# Preview Any - original implement from
|
| 5 |
+
# https://github.com/rgthree/rgthree-comfy/blob/main/py/display_any.py
|
| 6 |
+
# upstream requested in https://github.com/Kosinkadink/rfcs/blob/main/rfcs/0000-corenodes.md#preview-nodes
|
| 7 |
+
class PreviewAny():
|
| 8 |
+
@classmethod
|
| 9 |
+
def INPUT_TYPES(cls):
|
| 10 |
+
return {
|
| 11 |
+
"required": {"source": (IO.ANY, {})},
|
| 12 |
+
}
|
| 13 |
+
|
| 14 |
+
RETURN_TYPES = ()
|
| 15 |
+
FUNCTION = "main"
|
| 16 |
+
OUTPUT_NODE = True
|
| 17 |
+
|
| 18 |
+
CATEGORY = "utils"
|
| 19 |
+
|
| 20 |
+
def main(self, source=None):
|
| 21 |
+
value = 'None'
|
| 22 |
+
if isinstance(source, str):
|
| 23 |
+
value = source
|
| 24 |
+
elif isinstance(source, (int, float, bool)):
|
| 25 |
+
value = str(source)
|
| 26 |
+
elif source is not None:
|
| 27 |
+
try:
|
| 28 |
+
value = json.dumps(source)
|
| 29 |
+
except Exception:
|
| 30 |
+
try:
|
| 31 |
+
value = str(source)
|
| 32 |
+
except Exception:
|
| 33 |
+
value = 'source exists, but could not be serialized.'
|
| 34 |
+
|
| 35 |
+
return {"ui": {"text": (value,)}}
|
| 36 |
+
|
| 37 |
+
NODE_CLASS_MAPPINGS = {
|
| 38 |
+
"PreviewAny": PreviewAny,
|
| 39 |
+
}
|
| 40 |
+
|
| 41 |
+
NODE_DISPLAY_NAME_MAPPINGS = {
|
| 42 |
+
"PreviewAny": "Preview Any",
|
| 43 |
+
}
|
ComfyUI/comfy_extras/nodes_tcfg.py
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# TCFG: Tangential Damping Classifier-free Guidance - (arXiv: https://arxiv.org/abs/2503.18137)
|
| 2 |
+
|
| 3 |
+
import torch
|
| 4 |
+
|
| 5 |
+
from comfy.comfy_types import IO, ComfyNodeABC, InputTypeDict
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
def score_tangential_damping(cond_score: torch.Tensor, uncond_score: torch.Tensor) -> torch.Tensor:
|
| 9 |
+
"""Drop tangential components from uncond score to align with cond score."""
|
| 10 |
+
# (B, 1, ...)
|
| 11 |
+
batch_num = cond_score.shape[0]
|
| 12 |
+
cond_score_flat = cond_score.reshape(batch_num, 1, -1).float()
|
| 13 |
+
uncond_score_flat = uncond_score.reshape(batch_num, 1, -1).float()
|
| 14 |
+
|
| 15 |
+
# Score matrix A (B, 2, ...)
|
| 16 |
+
score_matrix = torch.cat((uncond_score_flat, cond_score_flat), dim=1)
|
| 17 |
+
try:
|
| 18 |
+
_, _, Vh = torch.linalg.svd(score_matrix, full_matrices=False)
|
| 19 |
+
except RuntimeError:
|
| 20 |
+
# Fallback to CPU
|
| 21 |
+
_, _, Vh = torch.linalg.svd(score_matrix.cpu(), full_matrices=False)
|
| 22 |
+
|
| 23 |
+
# Drop the tangential components
|
| 24 |
+
v1 = Vh[:, 0:1, :].to(uncond_score_flat.device) # (B, 1, ...)
|
| 25 |
+
uncond_score_td = (uncond_score_flat @ v1.transpose(-2, -1)) * v1
|
| 26 |
+
return uncond_score_td.reshape_as(uncond_score).to(uncond_score.dtype)
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
class TCFG(ComfyNodeABC):
|
| 30 |
+
@classmethod
|
| 31 |
+
def INPUT_TYPES(cls) -> InputTypeDict:
|
| 32 |
+
return {
|
| 33 |
+
"required": {
|
| 34 |
+
"model": (IO.MODEL, {}),
|
| 35 |
+
}
|
| 36 |
+
}
|
| 37 |
+
|
| 38 |
+
RETURN_TYPES = (IO.MODEL,)
|
| 39 |
+
RETURN_NAMES = ("patched_model",)
|
| 40 |
+
FUNCTION = "patch"
|
| 41 |
+
|
| 42 |
+
CATEGORY = "advanced/guidance"
|
| 43 |
+
DESCRIPTION = "TCFG – Tangential Damping CFG (2503.18137)\n\nRefine the uncond (negative) to align with the cond (positive) for improving quality."
|
| 44 |
+
|
| 45 |
+
def patch(self, model):
|
| 46 |
+
m = model.clone()
|
| 47 |
+
|
| 48 |
+
def tangential_damping_cfg(args):
|
| 49 |
+
# Assume [cond, uncond, ...]
|
| 50 |
+
x = args["input"]
|
| 51 |
+
conds_out = args["conds_out"]
|
| 52 |
+
if len(conds_out) <= 1 or None in args["conds"][:2]:
|
| 53 |
+
# Skip when either cond or uncond is None
|
| 54 |
+
return conds_out
|
| 55 |
+
cond_pred = conds_out[0]
|
| 56 |
+
uncond_pred = conds_out[1]
|
| 57 |
+
uncond_td = score_tangential_damping(x - cond_pred, x - uncond_pred)
|
| 58 |
+
uncond_pred_td = x - uncond_td
|
| 59 |
+
return [cond_pred, uncond_pred_td] + conds_out[2:]
|
| 60 |
+
|
| 61 |
+
m.set_model_sampler_pre_cfg_function(tangential_damping_cfg)
|
| 62 |
+
return (m,)
|
| 63 |
+
|
| 64 |
+
|
| 65 |
+
NODE_CLASS_MAPPINGS = {
|
| 66 |
+
"TCFG": TCFG,
|
| 67 |
+
}
|
| 68 |
+
|
| 69 |
+
NODE_DISPLAY_NAME_MAPPINGS = {
|
| 70 |
+
"TCFG": "Tangential Damping CFG",
|
| 71 |
+
}
|
ComfyUI/comfy_extras/nodes_tomesd.py
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#Taken from: https://github.com/dbolya/tomesd
|
| 2 |
+
|
| 3 |
+
import torch
|
| 4 |
+
from typing import Tuple, Callable
|
| 5 |
+
import math
|
| 6 |
+
|
| 7 |
+
def do_nothing(x: torch.Tensor, mode:str=None):
|
| 8 |
+
return x
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
def mps_gather_workaround(input, dim, index):
|
| 12 |
+
if input.shape[-1] == 1:
|
| 13 |
+
return torch.gather(
|
| 14 |
+
input.unsqueeze(-1),
|
| 15 |
+
dim - 1 if dim < 0 else dim,
|
| 16 |
+
index.unsqueeze(-1)
|
| 17 |
+
).squeeze(-1)
|
| 18 |
+
else:
|
| 19 |
+
return torch.gather(input, dim, index)
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
def bipartite_soft_matching_random2d(metric: torch.Tensor,
|
| 23 |
+
w: int, h: int, sx: int, sy: int, r: int,
|
| 24 |
+
no_rand: bool = False) -> Tuple[Callable, Callable]:
|
| 25 |
+
"""
|
| 26 |
+
Partitions the tokens into src and dst and merges r tokens from src to dst.
|
| 27 |
+
Dst tokens are partitioned by choosing one randomy in each (sx, sy) region.
|
| 28 |
+
Args:
|
| 29 |
+
- metric [B, N, C]: metric to use for similarity
|
| 30 |
+
- w: image width in tokens
|
| 31 |
+
- h: image height in tokens
|
| 32 |
+
- sx: stride in the x dimension for dst, must divide w
|
| 33 |
+
- sy: stride in the y dimension for dst, must divide h
|
| 34 |
+
- r: number of tokens to remove (by merging)
|
| 35 |
+
- no_rand: if true, disable randomness (use top left corner only)
|
| 36 |
+
"""
|
| 37 |
+
B, N, _ = metric.shape
|
| 38 |
+
|
| 39 |
+
if r <= 0 or w == 1 or h == 1:
|
| 40 |
+
return do_nothing, do_nothing
|
| 41 |
+
|
| 42 |
+
gather = mps_gather_workaround if metric.device.type == "mps" else torch.gather
|
| 43 |
+
|
| 44 |
+
with torch.no_grad():
|
| 45 |
+
hsy, wsx = h // sy, w // sx
|
| 46 |
+
|
| 47 |
+
# For each sy by sx kernel, randomly assign one token to be dst and the rest src
|
| 48 |
+
if no_rand:
|
| 49 |
+
rand_idx = torch.zeros(hsy, wsx, 1, device=metric.device, dtype=torch.int64)
|
| 50 |
+
else:
|
| 51 |
+
rand_idx = torch.randint(sy*sx, size=(hsy, wsx, 1), device=metric.device)
|
| 52 |
+
|
| 53 |
+
# The image might not divide sx and sy, so we need to work on a view of the top left if the idx buffer instead
|
| 54 |
+
idx_buffer_view = torch.zeros(hsy, wsx, sy*sx, device=metric.device, dtype=torch.int64)
|
| 55 |
+
idx_buffer_view.scatter_(dim=2, index=rand_idx, src=-torch.ones_like(rand_idx, dtype=rand_idx.dtype))
|
| 56 |
+
idx_buffer_view = idx_buffer_view.view(hsy, wsx, sy, sx).transpose(1, 2).reshape(hsy * sy, wsx * sx)
|
| 57 |
+
|
| 58 |
+
# Image is not divisible by sx or sy so we need to move it into a new buffer
|
| 59 |
+
if (hsy * sy) < h or (wsx * sx) < w:
|
| 60 |
+
idx_buffer = torch.zeros(h, w, device=metric.device, dtype=torch.int64)
|
| 61 |
+
idx_buffer[:(hsy * sy), :(wsx * sx)] = idx_buffer_view
|
| 62 |
+
else:
|
| 63 |
+
idx_buffer = idx_buffer_view
|
| 64 |
+
|
| 65 |
+
# We set dst tokens to be -1 and src to be 0, so an argsort gives us dst|src indices
|
| 66 |
+
rand_idx = idx_buffer.reshape(1, -1, 1).argsort(dim=1)
|
| 67 |
+
|
| 68 |
+
# We're finished with these
|
| 69 |
+
del idx_buffer, idx_buffer_view
|
| 70 |
+
|
| 71 |
+
# rand_idx is currently dst|src, so split them
|
| 72 |
+
num_dst = hsy * wsx
|
| 73 |
+
a_idx = rand_idx[:, num_dst:, :] # src
|
| 74 |
+
b_idx = rand_idx[:, :num_dst, :] # dst
|
| 75 |
+
|
| 76 |
+
def split(x):
|
| 77 |
+
C = x.shape[-1]
|
| 78 |
+
src = gather(x, dim=1, index=a_idx.expand(B, N - num_dst, C))
|
| 79 |
+
dst = gather(x, dim=1, index=b_idx.expand(B, num_dst, C))
|
| 80 |
+
return src, dst
|
| 81 |
+
|
| 82 |
+
# Cosine similarity between A and B
|
| 83 |
+
metric = metric / metric.norm(dim=-1, keepdim=True)
|
| 84 |
+
a, b = split(metric)
|
| 85 |
+
scores = a @ b.transpose(-1, -2)
|
| 86 |
+
|
| 87 |
+
# Can't reduce more than the # tokens in src
|
| 88 |
+
r = min(a.shape[1], r)
|
| 89 |
+
|
| 90 |
+
# Find the most similar greedily
|
| 91 |
+
node_max, node_idx = scores.max(dim=-1)
|
| 92 |
+
edge_idx = node_max.argsort(dim=-1, descending=True)[..., None]
|
| 93 |
+
|
| 94 |
+
unm_idx = edge_idx[..., r:, :] # Unmerged Tokens
|
| 95 |
+
src_idx = edge_idx[..., :r, :] # Merged Tokens
|
| 96 |
+
dst_idx = gather(node_idx[..., None], dim=-2, index=src_idx)
|
| 97 |
+
|
| 98 |
+
def merge(x: torch.Tensor, mode="mean") -> torch.Tensor:
|
| 99 |
+
src, dst = split(x)
|
| 100 |
+
n, t1, c = src.shape
|
| 101 |
+
|
| 102 |
+
unm = gather(src, dim=-2, index=unm_idx.expand(n, t1 - r, c))
|
| 103 |
+
src = gather(src, dim=-2, index=src_idx.expand(n, r, c))
|
| 104 |
+
dst = dst.scatter_reduce(-2, dst_idx.expand(n, r, c), src, reduce=mode)
|
| 105 |
+
|
| 106 |
+
return torch.cat([unm, dst], dim=1)
|
| 107 |
+
|
| 108 |
+
def unmerge(x: torch.Tensor) -> torch.Tensor:
|
| 109 |
+
unm_len = unm_idx.shape[1]
|
| 110 |
+
unm, dst = x[..., :unm_len, :], x[..., unm_len:, :]
|
| 111 |
+
_, _, c = unm.shape
|
| 112 |
+
|
| 113 |
+
src = gather(dst, dim=-2, index=dst_idx.expand(B, r, c))
|
| 114 |
+
|
| 115 |
+
# Combine back to the original shape
|
| 116 |
+
out = torch.zeros(B, N, c, device=x.device, dtype=x.dtype)
|
| 117 |
+
out.scatter_(dim=-2, index=b_idx.expand(B, num_dst, c), src=dst)
|
| 118 |
+
out.scatter_(dim=-2, index=gather(a_idx.expand(B, a_idx.shape[1], 1), dim=1, index=unm_idx).expand(B, unm_len, c), src=unm)
|
| 119 |
+
out.scatter_(dim=-2, index=gather(a_idx.expand(B, a_idx.shape[1], 1), dim=1, index=src_idx).expand(B, r, c), src=src)
|
| 120 |
+
|
| 121 |
+
return out
|
| 122 |
+
|
| 123 |
+
return merge, unmerge
|
| 124 |
+
|
| 125 |
+
|
| 126 |
+
def get_functions(x, ratio, original_shape):
|
| 127 |
+
b, c, original_h, original_w = original_shape
|
| 128 |
+
original_tokens = original_h * original_w
|
| 129 |
+
downsample = int(math.ceil(math.sqrt(original_tokens // x.shape[1])))
|
| 130 |
+
stride_x = 2
|
| 131 |
+
stride_y = 2
|
| 132 |
+
max_downsample = 1
|
| 133 |
+
|
| 134 |
+
if downsample <= max_downsample:
|
| 135 |
+
w = int(math.ceil(original_w / downsample))
|
| 136 |
+
h = int(math.ceil(original_h / downsample))
|
| 137 |
+
r = int(x.shape[1] * ratio)
|
| 138 |
+
no_rand = False
|
| 139 |
+
m, u = bipartite_soft_matching_random2d(x, w, h, stride_x, stride_y, r, no_rand)
|
| 140 |
+
return m, u
|
| 141 |
+
|
| 142 |
+
nothing = lambda y: y
|
| 143 |
+
return nothing, nothing
|
| 144 |
+
|
| 145 |
+
|
| 146 |
+
|
| 147 |
+
class TomePatchModel:
|
| 148 |
+
@classmethod
|
| 149 |
+
def INPUT_TYPES(s):
|
| 150 |
+
return {"required": { "model": ("MODEL",),
|
| 151 |
+
"ratio": ("FLOAT", {"default": 0.3, "min": 0.0, "max": 1.0, "step": 0.01}),
|
| 152 |
+
}}
|
| 153 |
+
RETURN_TYPES = ("MODEL",)
|
| 154 |
+
FUNCTION = "patch"
|
| 155 |
+
|
| 156 |
+
CATEGORY = "model_patches/unet"
|
| 157 |
+
|
| 158 |
+
def patch(self, model, ratio):
|
| 159 |
+
self.u = None
|
| 160 |
+
def tomesd_m(q, k, v, extra_options):
|
| 161 |
+
#NOTE: In the reference code get_functions takes x (input of the transformer block) as the argument instead of q
|
| 162 |
+
#however from my basic testing it seems that using q instead gives better results
|
| 163 |
+
m, self.u = get_functions(q, ratio, extra_options["original_shape"])
|
| 164 |
+
return m(q), k, v
|
| 165 |
+
def tomesd_u(n, extra_options):
|
| 166 |
+
return self.u(n)
|
| 167 |
+
|
| 168 |
+
m = model.clone()
|
| 169 |
+
m.set_model_attn1_patch(tomesd_m)
|
| 170 |
+
m.set_model_attn1_output_patch(tomesd_u)
|
| 171 |
+
return (m, )
|
| 172 |
+
|
| 173 |
+
|
| 174 |
+
NODE_CLASS_MAPPINGS = {
|
| 175 |
+
"TomePatchModel": TomePatchModel,
|
| 176 |
+
}
|
ComfyUI/comfy_extras/nodes_torch_compile.py
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from comfy_api.torch_helpers import set_torch_compile_wrapper
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
class TorchCompileModel:
|
| 5 |
+
@classmethod
|
| 6 |
+
def INPUT_TYPES(s):
|
| 7 |
+
return {"required": { "model": ("MODEL",),
|
| 8 |
+
"backend": (["inductor", "cudagraphs"],),
|
| 9 |
+
}}
|
| 10 |
+
RETURN_TYPES = ("MODEL",)
|
| 11 |
+
FUNCTION = "patch"
|
| 12 |
+
|
| 13 |
+
CATEGORY = "_for_testing"
|
| 14 |
+
EXPERIMENTAL = True
|
| 15 |
+
|
| 16 |
+
def patch(self, model, backend):
|
| 17 |
+
m = model.clone()
|
| 18 |
+
set_torch_compile_wrapper(model=m, backend=backend)
|
| 19 |
+
return (m, )
|
| 20 |
+
|
| 21 |
+
NODE_CLASS_MAPPINGS = {
|
| 22 |
+
"TorchCompileModel": TorchCompileModel,
|
| 23 |
+
}
|
ComfyUI/comfy_extras/nodes_train.py
ADDED
|
@@ -0,0 +1,877 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import datetime
|
| 2 |
+
import json
|
| 3 |
+
import logging
|
| 4 |
+
import os
|
| 5 |
+
|
| 6 |
+
import numpy as np
|
| 7 |
+
import safetensors
|
| 8 |
+
import torch
|
| 9 |
+
from PIL import Image, ImageDraw, ImageFont
|
| 10 |
+
from PIL.PngImagePlugin import PngInfo
|
| 11 |
+
import torch.utils.checkpoint
|
| 12 |
+
import tqdm
|
| 13 |
+
|
| 14 |
+
import comfy.samplers
|
| 15 |
+
import comfy.sd
|
| 16 |
+
import comfy.utils
|
| 17 |
+
import comfy.model_management
|
| 18 |
+
import comfy_extras.nodes_custom_sampler
|
| 19 |
+
import folder_paths
|
| 20 |
+
import node_helpers
|
| 21 |
+
from comfy.cli_args import args
|
| 22 |
+
from comfy.comfy_types.node_typing import IO
|
| 23 |
+
from comfy.weight_adapter import adapters, adapter_maps
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
def make_batch_extra_option_dict(d, indicies, full_size=None):
|
| 27 |
+
new_dict = {}
|
| 28 |
+
for k, v in d.items():
|
| 29 |
+
newv = v
|
| 30 |
+
if isinstance(v, dict):
|
| 31 |
+
newv = make_batch_extra_option_dict(v, indicies, full_size=full_size)
|
| 32 |
+
elif isinstance(v, torch.Tensor):
|
| 33 |
+
if full_size is None or v.size(0) == full_size:
|
| 34 |
+
newv = v[indicies]
|
| 35 |
+
elif isinstance(v, (list, tuple)) and len(v) == full_size:
|
| 36 |
+
newv = [v[i] for i in indicies]
|
| 37 |
+
new_dict[k] = newv
|
| 38 |
+
return new_dict
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
class TrainSampler(comfy.samplers.Sampler):
|
| 42 |
+
def __init__(self, loss_fn, optimizer, loss_callback=None, batch_size=1, grad_acc=1, total_steps=1, seed=0, training_dtype=torch.bfloat16):
|
| 43 |
+
self.loss_fn = loss_fn
|
| 44 |
+
self.optimizer = optimizer
|
| 45 |
+
self.loss_callback = loss_callback
|
| 46 |
+
self.batch_size = batch_size
|
| 47 |
+
self.total_steps = total_steps
|
| 48 |
+
self.grad_acc = grad_acc
|
| 49 |
+
self.seed = seed
|
| 50 |
+
self.training_dtype = training_dtype
|
| 51 |
+
|
| 52 |
+
def sample(self, model_wrap, sigmas, extra_args, callback, noise, latent_image=None, denoise_mask=None, disable_pbar=False):
|
| 53 |
+
cond = model_wrap.conds["positive"]
|
| 54 |
+
dataset_size = sigmas.size(0)
|
| 55 |
+
torch.cuda.empty_cache()
|
| 56 |
+
for i in (pbar:=tqdm.trange(self.total_steps, desc="Training LoRA", smoothing=0.01, disable=not comfy.utils.PROGRESS_BAR_ENABLED)):
|
| 57 |
+
noisegen = comfy_extras.nodes_custom_sampler.Noise_RandomNoise(self.seed + i * 1000)
|
| 58 |
+
indicies = torch.randperm(dataset_size)[:self.batch_size].tolist()
|
| 59 |
+
|
| 60 |
+
batch_latent = torch.stack([latent_image[i] for i in indicies])
|
| 61 |
+
batch_noise = noisegen.generate_noise({"samples": batch_latent}).to(batch_latent.device)
|
| 62 |
+
batch_sigmas = [
|
| 63 |
+
model_wrap.inner_model.model_sampling.percent_to_sigma(
|
| 64 |
+
torch.rand((1,)).item()
|
| 65 |
+
) for _ in range(min(self.batch_size, dataset_size))
|
| 66 |
+
]
|
| 67 |
+
batch_sigmas = torch.tensor(batch_sigmas).to(batch_latent.device)
|
| 68 |
+
|
| 69 |
+
xt = model_wrap.inner_model.model_sampling.noise_scaling(
|
| 70 |
+
batch_sigmas,
|
| 71 |
+
batch_noise,
|
| 72 |
+
batch_latent,
|
| 73 |
+
False
|
| 74 |
+
)
|
| 75 |
+
x0 = model_wrap.inner_model.model_sampling.noise_scaling(
|
| 76 |
+
torch.zeros_like(batch_sigmas),
|
| 77 |
+
torch.zeros_like(batch_noise),
|
| 78 |
+
batch_latent,
|
| 79 |
+
False
|
| 80 |
+
)
|
| 81 |
+
|
| 82 |
+
model_wrap.conds["positive"] = [
|
| 83 |
+
cond[i] for i in indicies
|
| 84 |
+
]
|
| 85 |
+
batch_extra_args = make_batch_extra_option_dict(extra_args, indicies, full_size=dataset_size)
|
| 86 |
+
|
| 87 |
+
with torch.autocast(xt.device.type, dtype=self.training_dtype):
|
| 88 |
+
x0_pred = model_wrap(xt, batch_sigmas, **batch_extra_args)
|
| 89 |
+
loss = self.loss_fn(x0_pred, x0)
|
| 90 |
+
loss.backward()
|
| 91 |
+
if self.loss_callback:
|
| 92 |
+
self.loss_callback(loss.item())
|
| 93 |
+
pbar.set_postfix({"loss": f"{loss.item():.4f}"})
|
| 94 |
+
|
| 95 |
+
if (i+1) % self.grad_acc == 0:
|
| 96 |
+
self.optimizer.step()
|
| 97 |
+
self.optimizer.zero_grad()
|
| 98 |
+
torch.cuda.empty_cache()
|
| 99 |
+
return torch.zeros_like(latent_image)
|
| 100 |
+
|
| 101 |
+
|
| 102 |
+
class BiasDiff(torch.nn.Module):
|
| 103 |
+
def __init__(self, bias):
|
| 104 |
+
super().__init__()
|
| 105 |
+
self.bias = bias
|
| 106 |
+
|
| 107 |
+
def __call__(self, b):
|
| 108 |
+
org_dtype = b.dtype
|
| 109 |
+
return (b.to(self.bias) + self.bias).to(org_dtype)
|
| 110 |
+
|
| 111 |
+
def passive_memory_usage(self):
|
| 112 |
+
return self.bias.nelement() * self.bias.element_size()
|
| 113 |
+
|
| 114 |
+
def move_to(self, device):
|
| 115 |
+
self.to(device=device)
|
| 116 |
+
return self.passive_memory_usage()
|
| 117 |
+
|
| 118 |
+
|
| 119 |
+
def load_and_process_images(image_files, input_dir, resize_method="None", w=None, h=None):
|
| 120 |
+
"""Utility function to load and process a list of images.
|
| 121 |
+
|
| 122 |
+
Args:
|
| 123 |
+
image_files: List of image filenames
|
| 124 |
+
input_dir: Base directory containing the images
|
| 125 |
+
resize_method: How to handle images of different sizes ("None", "Stretch", "Crop", "Pad")
|
| 126 |
+
|
| 127 |
+
Returns:
|
| 128 |
+
torch.Tensor: Batch of processed images
|
| 129 |
+
"""
|
| 130 |
+
if not image_files:
|
| 131 |
+
raise ValueError("No valid images found in input")
|
| 132 |
+
|
| 133 |
+
output_images = []
|
| 134 |
+
|
| 135 |
+
for file in image_files:
|
| 136 |
+
image_path = os.path.join(input_dir, file)
|
| 137 |
+
img = node_helpers.pillow(Image.open, image_path)
|
| 138 |
+
|
| 139 |
+
if img.mode == "I":
|
| 140 |
+
img = img.point(lambda i: i * (1 / 255))
|
| 141 |
+
img = img.convert("RGB")
|
| 142 |
+
|
| 143 |
+
if w is None and h is None:
|
| 144 |
+
w, h = img.size[0], img.size[1]
|
| 145 |
+
|
| 146 |
+
# Resize image to first image
|
| 147 |
+
if img.size[0] != w or img.size[1] != h:
|
| 148 |
+
if resize_method == "Stretch":
|
| 149 |
+
img = img.resize((w, h), Image.Resampling.LANCZOS)
|
| 150 |
+
elif resize_method == "Crop":
|
| 151 |
+
img = img.crop((0, 0, w, h))
|
| 152 |
+
elif resize_method == "Pad":
|
| 153 |
+
img = img.resize((w, h), Image.Resampling.LANCZOS)
|
| 154 |
+
elif resize_method == "None":
|
| 155 |
+
raise ValueError(
|
| 156 |
+
"Your input image size does not match the first image in the dataset. Either select a valid resize method or use the same size for all images."
|
| 157 |
+
)
|
| 158 |
+
|
| 159 |
+
img_array = np.array(img).astype(np.float32) / 255.0
|
| 160 |
+
img_tensor = torch.from_numpy(img_array)[None,]
|
| 161 |
+
output_images.append(img_tensor)
|
| 162 |
+
|
| 163 |
+
return torch.cat(output_images, dim=0)
|
| 164 |
+
|
| 165 |
+
|
| 166 |
+
class LoadImageSetNode:
|
| 167 |
+
@classmethod
|
| 168 |
+
def INPUT_TYPES(s):
|
| 169 |
+
return {
|
| 170 |
+
"required": {
|
| 171 |
+
"images": (
|
| 172 |
+
[
|
| 173 |
+
f
|
| 174 |
+
for f in os.listdir(folder_paths.get_input_directory())
|
| 175 |
+
if f.endswith((".png", ".jpg", ".jpeg", ".webp", ".bmp", ".gif", ".jpe", ".apng", ".tif", ".tiff"))
|
| 176 |
+
],
|
| 177 |
+
{"image_upload": True, "allow_batch": True},
|
| 178 |
+
)
|
| 179 |
+
},
|
| 180 |
+
"optional": {
|
| 181 |
+
"resize_method": (
|
| 182 |
+
["None", "Stretch", "Crop", "Pad"],
|
| 183 |
+
{"default": "None"},
|
| 184 |
+
),
|
| 185 |
+
},
|
| 186 |
+
}
|
| 187 |
+
|
| 188 |
+
INPUT_IS_LIST = True
|
| 189 |
+
RETURN_TYPES = ("IMAGE",)
|
| 190 |
+
FUNCTION = "load_images"
|
| 191 |
+
CATEGORY = "loaders"
|
| 192 |
+
EXPERIMENTAL = True
|
| 193 |
+
DESCRIPTION = "Loads a batch of images from a directory for training."
|
| 194 |
+
|
| 195 |
+
@classmethod
|
| 196 |
+
def VALIDATE_INPUTS(s, images, resize_method):
|
| 197 |
+
filenames = images[0] if isinstance(images[0], list) else images
|
| 198 |
+
|
| 199 |
+
for image in filenames:
|
| 200 |
+
if not folder_paths.exists_annotated_filepath(image):
|
| 201 |
+
return "Invalid image file: {}".format(image)
|
| 202 |
+
return True
|
| 203 |
+
|
| 204 |
+
def load_images(self, input_files, resize_method):
|
| 205 |
+
input_dir = folder_paths.get_input_directory()
|
| 206 |
+
valid_extensions = [".png", ".jpg", ".jpeg", ".webp", ".bmp", ".gif", ".jpe", ".apng", ".tif", ".tiff"]
|
| 207 |
+
image_files = [
|
| 208 |
+
f
|
| 209 |
+
for f in input_files
|
| 210 |
+
if any(f.lower().endswith(ext) for ext in valid_extensions)
|
| 211 |
+
]
|
| 212 |
+
output_tensor = load_and_process_images(image_files, input_dir, resize_method)
|
| 213 |
+
return (output_tensor,)
|
| 214 |
+
|
| 215 |
+
|
| 216 |
+
class LoadImageSetFromFolderNode:
|
| 217 |
+
@classmethod
|
| 218 |
+
def INPUT_TYPES(s):
|
| 219 |
+
return {
|
| 220 |
+
"required": {
|
| 221 |
+
"folder": (folder_paths.get_input_subfolders(), {"tooltip": "The folder to load images from."})
|
| 222 |
+
},
|
| 223 |
+
"optional": {
|
| 224 |
+
"resize_method": (
|
| 225 |
+
["None", "Stretch", "Crop", "Pad"],
|
| 226 |
+
{"default": "None"},
|
| 227 |
+
),
|
| 228 |
+
},
|
| 229 |
+
}
|
| 230 |
+
|
| 231 |
+
RETURN_TYPES = ("IMAGE",)
|
| 232 |
+
FUNCTION = "load_images"
|
| 233 |
+
CATEGORY = "loaders"
|
| 234 |
+
EXPERIMENTAL = True
|
| 235 |
+
DESCRIPTION = "Loads a batch of images from a directory for training."
|
| 236 |
+
|
| 237 |
+
def load_images(self, folder, resize_method):
|
| 238 |
+
sub_input_dir = os.path.join(folder_paths.get_input_directory(), folder)
|
| 239 |
+
valid_extensions = [".png", ".jpg", ".jpeg", ".webp"]
|
| 240 |
+
image_files = [
|
| 241 |
+
f
|
| 242 |
+
for f in os.listdir(sub_input_dir)
|
| 243 |
+
if any(f.lower().endswith(ext) for ext in valid_extensions)
|
| 244 |
+
]
|
| 245 |
+
output_tensor = load_and_process_images(image_files, sub_input_dir, resize_method)
|
| 246 |
+
return (output_tensor,)
|
| 247 |
+
|
| 248 |
+
|
| 249 |
+
class LoadImageTextSetFromFolderNode:
|
| 250 |
+
@classmethod
|
| 251 |
+
def INPUT_TYPES(s):
|
| 252 |
+
return {
|
| 253 |
+
"required": {
|
| 254 |
+
"folder": (folder_paths.get_input_subfolders(), {"tooltip": "The folder to load images from."}),
|
| 255 |
+
"clip": (IO.CLIP, {"tooltip": "The CLIP model used for encoding the text."}),
|
| 256 |
+
},
|
| 257 |
+
"optional": {
|
| 258 |
+
"resize_method": (
|
| 259 |
+
["None", "Stretch", "Crop", "Pad"],
|
| 260 |
+
{"default": "None"},
|
| 261 |
+
),
|
| 262 |
+
"width": (
|
| 263 |
+
IO.INT,
|
| 264 |
+
{
|
| 265 |
+
"default": -1,
|
| 266 |
+
"min": -1,
|
| 267 |
+
"max": 10000,
|
| 268 |
+
"step": 1,
|
| 269 |
+
"tooltip": "The width to resize the images to. -1 means use the original width.",
|
| 270 |
+
},
|
| 271 |
+
),
|
| 272 |
+
"height": (
|
| 273 |
+
IO.INT,
|
| 274 |
+
{
|
| 275 |
+
"default": -1,
|
| 276 |
+
"min": -1,
|
| 277 |
+
"max": 10000,
|
| 278 |
+
"step": 1,
|
| 279 |
+
"tooltip": "The height to resize the images to. -1 means use the original height.",
|
| 280 |
+
},
|
| 281 |
+
)
|
| 282 |
+
},
|
| 283 |
+
}
|
| 284 |
+
|
| 285 |
+
RETURN_TYPES = ("IMAGE", IO.CONDITIONING,)
|
| 286 |
+
FUNCTION = "load_images"
|
| 287 |
+
CATEGORY = "loaders"
|
| 288 |
+
EXPERIMENTAL = True
|
| 289 |
+
DESCRIPTION = "Loads a batch of images and caption from a directory for training."
|
| 290 |
+
|
| 291 |
+
def load_images(self, folder, clip, resize_method, width=None, height=None):
|
| 292 |
+
if clip is None:
|
| 293 |
+
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.")
|
| 294 |
+
|
| 295 |
+
logging.info(f"Loading images from folder: {folder}")
|
| 296 |
+
|
| 297 |
+
sub_input_dir = os.path.join(folder_paths.get_input_directory(), folder)
|
| 298 |
+
valid_extensions = [".png", ".jpg", ".jpeg", ".webp"]
|
| 299 |
+
|
| 300 |
+
image_files = []
|
| 301 |
+
for item in os.listdir(sub_input_dir):
|
| 302 |
+
path = os.path.join(sub_input_dir, item)
|
| 303 |
+
if any(item.lower().endswith(ext) for ext in valid_extensions):
|
| 304 |
+
image_files.append(path)
|
| 305 |
+
elif os.path.isdir(path):
|
| 306 |
+
# Support kohya-ss/sd-scripts folder structure
|
| 307 |
+
repeat = 1
|
| 308 |
+
if item.split("_")[0].isdigit():
|
| 309 |
+
repeat = int(item.split("_")[0])
|
| 310 |
+
image_files.extend([
|
| 311 |
+
os.path.join(path, f) for f in os.listdir(path) if any(f.lower().endswith(ext) for ext in valid_extensions)
|
| 312 |
+
] * repeat)
|
| 313 |
+
|
| 314 |
+
caption_file_path = [
|
| 315 |
+
f.replace(os.path.splitext(f)[1], ".txt")
|
| 316 |
+
for f in image_files
|
| 317 |
+
]
|
| 318 |
+
captions = []
|
| 319 |
+
for caption_file in caption_file_path:
|
| 320 |
+
caption_path = os.path.join(sub_input_dir, caption_file)
|
| 321 |
+
if os.path.exists(caption_path):
|
| 322 |
+
with open(caption_path, "r", encoding="utf-8") as f:
|
| 323 |
+
caption = f.read().strip()
|
| 324 |
+
captions.append(caption)
|
| 325 |
+
else:
|
| 326 |
+
captions.append("")
|
| 327 |
+
|
| 328 |
+
width = width if width != -1 else None
|
| 329 |
+
height = height if height != -1 else None
|
| 330 |
+
output_tensor = load_and_process_images(image_files, sub_input_dir, resize_method, width, height)
|
| 331 |
+
|
| 332 |
+
logging.info(f"Loaded {len(output_tensor)} images from {sub_input_dir}.")
|
| 333 |
+
|
| 334 |
+
logging.info(f"Encoding captions from {sub_input_dir}.")
|
| 335 |
+
conditions = []
|
| 336 |
+
empty_cond = clip.encode_from_tokens_scheduled(clip.tokenize(""))
|
| 337 |
+
for text in captions:
|
| 338 |
+
if text == "":
|
| 339 |
+
conditions.append(empty_cond)
|
| 340 |
+
tokens = clip.tokenize(text)
|
| 341 |
+
conditions.extend(clip.encode_from_tokens_scheduled(tokens))
|
| 342 |
+
logging.info(f"Encoded {len(conditions)} captions from {sub_input_dir}.")
|
| 343 |
+
return (output_tensor, conditions)
|
| 344 |
+
|
| 345 |
+
|
| 346 |
+
def draw_loss_graph(loss_map, steps):
|
| 347 |
+
width, height = 500, 300
|
| 348 |
+
img = Image.new("RGB", (width, height), "white")
|
| 349 |
+
draw = ImageDraw.Draw(img)
|
| 350 |
+
|
| 351 |
+
min_loss, max_loss = min(loss_map.values()), max(loss_map.values())
|
| 352 |
+
scaled_loss = [(l - min_loss) / (max_loss - min_loss) for l in loss_map.values()]
|
| 353 |
+
|
| 354 |
+
prev_point = (0, height - int(scaled_loss[0] * height))
|
| 355 |
+
for i, l in enumerate(scaled_loss[1:], start=1):
|
| 356 |
+
x = int(i / (steps - 1) * width)
|
| 357 |
+
y = height - int(l * height)
|
| 358 |
+
draw.line([prev_point, (x, y)], fill="blue", width=2)
|
| 359 |
+
prev_point = (x, y)
|
| 360 |
+
|
| 361 |
+
return img
|
| 362 |
+
|
| 363 |
+
|
| 364 |
+
def find_all_highest_child_module_with_forward(model: torch.nn.Module, result = None, name = None):
|
| 365 |
+
if result is None:
|
| 366 |
+
result = []
|
| 367 |
+
elif hasattr(model, "forward") and not isinstance(model, (torch.nn.ModuleList, torch.nn.Sequential, torch.nn.ModuleDict)):
|
| 368 |
+
result.append(model)
|
| 369 |
+
logging.debug(f"Found module with forward: {name} ({model.__class__.__name__})")
|
| 370 |
+
return result
|
| 371 |
+
name = name or "root"
|
| 372 |
+
for next_name, child in model.named_children():
|
| 373 |
+
find_all_highest_child_module_with_forward(child, result, f"{name}.{next_name}")
|
| 374 |
+
return result
|
| 375 |
+
|
| 376 |
+
|
| 377 |
+
def patch(m):
|
| 378 |
+
if not hasattr(m, "forward"):
|
| 379 |
+
return
|
| 380 |
+
org_forward = m.forward
|
| 381 |
+
def fwd(args, kwargs):
|
| 382 |
+
return org_forward(*args, **kwargs)
|
| 383 |
+
def checkpointing_fwd(*args, **kwargs):
|
| 384 |
+
return torch.utils.checkpoint.checkpoint(
|
| 385 |
+
fwd, args, kwargs, use_reentrant=False
|
| 386 |
+
)
|
| 387 |
+
m.org_forward = org_forward
|
| 388 |
+
m.forward = checkpointing_fwd
|
| 389 |
+
|
| 390 |
+
|
| 391 |
+
def unpatch(m):
|
| 392 |
+
if hasattr(m, "org_forward"):
|
| 393 |
+
m.forward = m.org_forward
|
| 394 |
+
del m.org_forward
|
| 395 |
+
|
| 396 |
+
|
| 397 |
+
class TrainLoraNode:
|
| 398 |
+
@classmethod
|
| 399 |
+
def INPUT_TYPES(s):
|
| 400 |
+
return {
|
| 401 |
+
"required": {
|
| 402 |
+
"model": (IO.MODEL, {"tooltip": "The model to train the LoRA on."}),
|
| 403 |
+
"latents": (
|
| 404 |
+
"LATENT",
|
| 405 |
+
{
|
| 406 |
+
"tooltip": "The Latents to use for training, serve as dataset/input of the model."
|
| 407 |
+
},
|
| 408 |
+
),
|
| 409 |
+
"positive": (
|
| 410 |
+
IO.CONDITIONING,
|
| 411 |
+
{"tooltip": "The positive conditioning to use for training."},
|
| 412 |
+
),
|
| 413 |
+
"batch_size": (
|
| 414 |
+
IO.INT,
|
| 415 |
+
{
|
| 416 |
+
"default": 1,
|
| 417 |
+
"min": 1,
|
| 418 |
+
"max": 10000,
|
| 419 |
+
"step": 1,
|
| 420 |
+
"tooltip": "The batch size to use for training.",
|
| 421 |
+
},
|
| 422 |
+
),
|
| 423 |
+
"grad_accumulation_steps": (
|
| 424 |
+
IO.INT,
|
| 425 |
+
{
|
| 426 |
+
"default": 1,
|
| 427 |
+
"min": 1,
|
| 428 |
+
"max": 1024,
|
| 429 |
+
"step": 1,
|
| 430 |
+
"tooltip": "The number of gradient accumulation steps to use for training.",
|
| 431 |
+
}
|
| 432 |
+
),
|
| 433 |
+
"steps": (
|
| 434 |
+
IO.INT,
|
| 435 |
+
{
|
| 436 |
+
"default": 16,
|
| 437 |
+
"min": 1,
|
| 438 |
+
"max": 100000,
|
| 439 |
+
"tooltip": "The number of steps to train the LoRA for.",
|
| 440 |
+
},
|
| 441 |
+
),
|
| 442 |
+
"learning_rate": (
|
| 443 |
+
IO.FLOAT,
|
| 444 |
+
{
|
| 445 |
+
"default": 0.0005,
|
| 446 |
+
"min": 0.0000001,
|
| 447 |
+
"max": 1.0,
|
| 448 |
+
"step": 0.000001,
|
| 449 |
+
"tooltip": "The learning rate to use for training.",
|
| 450 |
+
},
|
| 451 |
+
),
|
| 452 |
+
"rank": (
|
| 453 |
+
IO.INT,
|
| 454 |
+
{
|
| 455 |
+
"default": 8,
|
| 456 |
+
"min": 1,
|
| 457 |
+
"max": 128,
|
| 458 |
+
"tooltip": "The rank of the LoRA layers.",
|
| 459 |
+
},
|
| 460 |
+
),
|
| 461 |
+
"optimizer": (
|
| 462 |
+
["AdamW", "Adam", "SGD", "RMSprop"],
|
| 463 |
+
{
|
| 464 |
+
"default": "AdamW",
|
| 465 |
+
"tooltip": "The optimizer to use for training.",
|
| 466 |
+
},
|
| 467 |
+
),
|
| 468 |
+
"loss_function": (
|
| 469 |
+
["MSE", "L1", "Huber", "SmoothL1"],
|
| 470 |
+
{
|
| 471 |
+
"default": "MSE",
|
| 472 |
+
"tooltip": "The loss function to use for training.",
|
| 473 |
+
},
|
| 474 |
+
),
|
| 475 |
+
"seed": (
|
| 476 |
+
IO.INT,
|
| 477 |
+
{
|
| 478 |
+
"default": 0,
|
| 479 |
+
"min": 0,
|
| 480 |
+
"max": 0xFFFFFFFFFFFFFFFF,
|
| 481 |
+
"tooltip": "The seed to use for training (used in generator for LoRA weight initialization and noise sampling)",
|
| 482 |
+
},
|
| 483 |
+
),
|
| 484 |
+
"training_dtype": (
|
| 485 |
+
["bf16", "fp32"],
|
| 486 |
+
{"default": "bf16", "tooltip": "The dtype to use for training."},
|
| 487 |
+
),
|
| 488 |
+
"lora_dtype": (
|
| 489 |
+
["bf16", "fp32"],
|
| 490 |
+
{"default": "bf16", "tooltip": "The dtype to use for lora."},
|
| 491 |
+
),
|
| 492 |
+
"algorithm": (
|
| 493 |
+
list(adapter_maps.keys()),
|
| 494 |
+
{"default": list(adapter_maps.keys())[0], "tooltip": "The algorithm to use for training."},
|
| 495 |
+
),
|
| 496 |
+
"gradient_checkpointing": (
|
| 497 |
+
IO.BOOLEAN,
|
| 498 |
+
{
|
| 499 |
+
"default": True,
|
| 500 |
+
"tooltip": "Use gradient checkpointing for training.",
|
| 501 |
+
}
|
| 502 |
+
),
|
| 503 |
+
"existing_lora": (
|
| 504 |
+
folder_paths.get_filename_list("loras") + ["[None]"],
|
| 505 |
+
{
|
| 506 |
+
"default": "[None]",
|
| 507 |
+
"tooltip": "The existing LoRA to append to. Set to None for new LoRA.",
|
| 508 |
+
},
|
| 509 |
+
),
|
| 510 |
+
},
|
| 511 |
+
}
|
| 512 |
+
|
| 513 |
+
RETURN_TYPES = (IO.MODEL, IO.LORA_MODEL, IO.LOSS_MAP, IO.INT)
|
| 514 |
+
RETURN_NAMES = ("model_with_lora", "lora", "loss", "steps")
|
| 515 |
+
FUNCTION = "train"
|
| 516 |
+
CATEGORY = "training"
|
| 517 |
+
EXPERIMENTAL = True
|
| 518 |
+
|
| 519 |
+
def train(
|
| 520 |
+
self,
|
| 521 |
+
model,
|
| 522 |
+
latents,
|
| 523 |
+
positive,
|
| 524 |
+
batch_size,
|
| 525 |
+
steps,
|
| 526 |
+
grad_accumulation_steps,
|
| 527 |
+
learning_rate,
|
| 528 |
+
rank,
|
| 529 |
+
optimizer,
|
| 530 |
+
loss_function,
|
| 531 |
+
seed,
|
| 532 |
+
training_dtype,
|
| 533 |
+
lora_dtype,
|
| 534 |
+
algorithm,
|
| 535 |
+
gradient_checkpointing,
|
| 536 |
+
existing_lora,
|
| 537 |
+
):
|
| 538 |
+
mp = model.clone()
|
| 539 |
+
dtype = node_helpers.string_to_torch_dtype(training_dtype)
|
| 540 |
+
lora_dtype = node_helpers.string_to_torch_dtype(lora_dtype)
|
| 541 |
+
mp.set_model_compute_dtype(dtype)
|
| 542 |
+
|
| 543 |
+
latents = latents["samples"].to(dtype)
|
| 544 |
+
num_images = latents.shape[0]
|
| 545 |
+
logging.info(f"Total Images: {num_images}, Total Captions: {len(positive)}")
|
| 546 |
+
if len(positive) == 1 and num_images > 1:
|
| 547 |
+
positive = positive * num_images
|
| 548 |
+
elif len(positive) != num_images:
|
| 549 |
+
raise ValueError(
|
| 550 |
+
f"Number of positive conditions ({len(positive)}) does not match number of images ({num_images})."
|
| 551 |
+
)
|
| 552 |
+
|
| 553 |
+
with torch.inference_mode(False):
|
| 554 |
+
lora_sd = {}
|
| 555 |
+
generator = torch.Generator()
|
| 556 |
+
generator.manual_seed(seed)
|
| 557 |
+
|
| 558 |
+
# Load existing LoRA weights if provided
|
| 559 |
+
existing_weights = {}
|
| 560 |
+
existing_steps = 0
|
| 561 |
+
if existing_lora != "[None]":
|
| 562 |
+
lora_path = folder_paths.get_full_path_or_raise("loras", existing_lora)
|
| 563 |
+
# Extract steps from filename like "trained_lora_10_steps_20250225_203716"
|
| 564 |
+
existing_steps = int(existing_lora.split("_steps_")[0].split("_")[-1])
|
| 565 |
+
if lora_path:
|
| 566 |
+
existing_weights = comfy.utils.load_torch_file(lora_path)
|
| 567 |
+
|
| 568 |
+
all_weight_adapters = []
|
| 569 |
+
for n, m in mp.model.named_modules():
|
| 570 |
+
if hasattr(m, "weight_function"):
|
| 571 |
+
if m.weight is not None:
|
| 572 |
+
key = "{}.weight".format(n)
|
| 573 |
+
shape = m.weight.shape
|
| 574 |
+
if len(shape) >= 2:
|
| 575 |
+
alpha = float(existing_weights.get(f"{key}.alpha", 1.0))
|
| 576 |
+
dora_scale = existing_weights.get(
|
| 577 |
+
f"{key}.dora_scale", None
|
| 578 |
+
)
|
| 579 |
+
for adapter_cls in adapters:
|
| 580 |
+
existing_adapter = adapter_cls.load(
|
| 581 |
+
n, existing_weights, alpha, dora_scale
|
| 582 |
+
)
|
| 583 |
+
if existing_adapter is not None:
|
| 584 |
+
break
|
| 585 |
+
else:
|
| 586 |
+
existing_adapter = None
|
| 587 |
+
adapter_cls = adapter_maps[algorithm]
|
| 588 |
+
|
| 589 |
+
if existing_adapter is not None:
|
| 590 |
+
train_adapter = existing_adapter.to_train().to(lora_dtype)
|
| 591 |
+
else:
|
| 592 |
+
# Use LoRA with alpha=1.0 by default
|
| 593 |
+
train_adapter = adapter_cls.create_train(
|
| 594 |
+
m.weight, rank=rank, alpha=1.0
|
| 595 |
+
).to(lora_dtype)
|
| 596 |
+
for name, parameter in train_adapter.named_parameters():
|
| 597 |
+
lora_sd[f"{n}.{name}"] = parameter
|
| 598 |
+
|
| 599 |
+
mp.add_weight_wrapper(key, train_adapter)
|
| 600 |
+
all_weight_adapters.append(train_adapter)
|
| 601 |
+
else:
|
| 602 |
+
diff = torch.nn.Parameter(
|
| 603 |
+
torch.zeros(
|
| 604 |
+
m.weight.shape, dtype=lora_dtype, requires_grad=True
|
| 605 |
+
)
|
| 606 |
+
)
|
| 607 |
+
diff_module = BiasDiff(diff)
|
| 608 |
+
mp.add_weight_wrapper(key, BiasDiff(diff))
|
| 609 |
+
all_weight_adapters.append(diff_module)
|
| 610 |
+
lora_sd["{}.diff".format(n)] = diff
|
| 611 |
+
if hasattr(m, "bias") and m.bias is not None:
|
| 612 |
+
key = "{}.bias".format(n)
|
| 613 |
+
bias = torch.nn.Parameter(
|
| 614 |
+
torch.zeros(m.bias.shape, dtype=lora_dtype, requires_grad=True)
|
| 615 |
+
)
|
| 616 |
+
bias_module = BiasDiff(bias)
|
| 617 |
+
lora_sd["{}.diff_b".format(n)] = bias
|
| 618 |
+
mp.add_weight_wrapper(key, BiasDiff(bias))
|
| 619 |
+
all_weight_adapters.append(bias_module)
|
| 620 |
+
|
| 621 |
+
if optimizer == "Adam":
|
| 622 |
+
optimizer = torch.optim.Adam(lora_sd.values(), lr=learning_rate)
|
| 623 |
+
elif optimizer == "AdamW":
|
| 624 |
+
optimizer = torch.optim.AdamW(lora_sd.values(), lr=learning_rate)
|
| 625 |
+
elif optimizer == "SGD":
|
| 626 |
+
optimizer = torch.optim.SGD(lora_sd.values(), lr=learning_rate)
|
| 627 |
+
elif optimizer == "RMSprop":
|
| 628 |
+
optimizer = torch.optim.RMSprop(lora_sd.values(), lr=learning_rate)
|
| 629 |
+
|
| 630 |
+
# Setup loss function based on selection
|
| 631 |
+
if loss_function == "MSE":
|
| 632 |
+
criterion = torch.nn.MSELoss()
|
| 633 |
+
elif loss_function == "L1":
|
| 634 |
+
criterion = torch.nn.L1Loss()
|
| 635 |
+
elif loss_function == "Huber":
|
| 636 |
+
criterion = torch.nn.HuberLoss()
|
| 637 |
+
elif loss_function == "SmoothL1":
|
| 638 |
+
criterion = torch.nn.SmoothL1Loss()
|
| 639 |
+
|
| 640 |
+
# setup models
|
| 641 |
+
if gradient_checkpointing:
|
| 642 |
+
for m in find_all_highest_child_module_with_forward(mp.model.diffusion_model):
|
| 643 |
+
patch(m)
|
| 644 |
+
mp.model.requires_grad_(False)
|
| 645 |
+
comfy.model_management.load_models_gpu([mp], memory_required=1e20, force_full_load=True)
|
| 646 |
+
|
| 647 |
+
# Setup sampler and guider like in test script
|
| 648 |
+
loss_map = {"loss": []}
|
| 649 |
+
def loss_callback(loss):
|
| 650 |
+
loss_map["loss"].append(loss)
|
| 651 |
+
train_sampler = TrainSampler(
|
| 652 |
+
criterion,
|
| 653 |
+
optimizer,
|
| 654 |
+
loss_callback=loss_callback,
|
| 655 |
+
batch_size=batch_size,
|
| 656 |
+
grad_acc=grad_accumulation_steps,
|
| 657 |
+
total_steps=steps*grad_accumulation_steps,
|
| 658 |
+
seed=seed,
|
| 659 |
+
training_dtype=dtype
|
| 660 |
+
)
|
| 661 |
+
guider = comfy_extras.nodes_custom_sampler.Guider_Basic(mp)
|
| 662 |
+
guider.set_conds(positive) # Set conditioning from input
|
| 663 |
+
|
| 664 |
+
# Training loop
|
| 665 |
+
try:
|
| 666 |
+
# Generate dummy sigmas and noise
|
| 667 |
+
sigmas = torch.tensor(range(num_images))
|
| 668 |
+
noise = comfy_extras.nodes_custom_sampler.Noise_RandomNoise(seed)
|
| 669 |
+
guider.sample(
|
| 670 |
+
noise.generate_noise({"samples": latents}),
|
| 671 |
+
latents,
|
| 672 |
+
train_sampler,
|
| 673 |
+
sigmas,
|
| 674 |
+
seed=noise.seed
|
| 675 |
+
)
|
| 676 |
+
finally:
|
| 677 |
+
for m in mp.model.modules():
|
| 678 |
+
unpatch(m)
|
| 679 |
+
del train_sampler, optimizer
|
| 680 |
+
|
| 681 |
+
for adapter in all_weight_adapters:
|
| 682 |
+
adapter.requires_grad_(False)
|
| 683 |
+
|
| 684 |
+
for param in lora_sd:
|
| 685 |
+
lora_sd[param] = lora_sd[param].to(lora_dtype)
|
| 686 |
+
|
| 687 |
+
return (mp, lora_sd, loss_map, steps + existing_steps)
|
| 688 |
+
|
| 689 |
+
|
| 690 |
+
class LoraModelLoader:
|
| 691 |
+
def __init__(self):
|
| 692 |
+
self.loaded_lora = None
|
| 693 |
+
|
| 694 |
+
@classmethod
|
| 695 |
+
def INPUT_TYPES(s):
|
| 696 |
+
return {
|
| 697 |
+
"required": {
|
| 698 |
+
"model": ("MODEL", {"tooltip": "The diffusion model the LoRA will be applied to."}),
|
| 699 |
+
"lora": (IO.LORA_MODEL, {"tooltip": "The LoRA model to apply to the diffusion model."}),
|
| 700 |
+
"strength_model": ("FLOAT", {"default": 1.0, "min": -100.0, "max": 100.0, "step": 0.01, "tooltip": "How strongly to modify the diffusion model. This value can be negative."}),
|
| 701 |
+
}
|
| 702 |
+
}
|
| 703 |
+
|
| 704 |
+
RETURN_TYPES = ("MODEL",)
|
| 705 |
+
OUTPUT_TOOLTIPS = ("The modified diffusion model.",)
|
| 706 |
+
FUNCTION = "load_lora_model"
|
| 707 |
+
|
| 708 |
+
CATEGORY = "loaders"
|
| 709 |
+
DESCRIPTION = "Load Trained LoRA weights from Train LoRA node."
|
| 710 |
+
EXPERIMENTAL = True
|
| 711 |
+
|
| 712 |
+
def load_lora_model(self, model, lora, strength_model):
|
| 713 |
+
if strength_model == 0:
|
| 714 |
+
return (model, )
|
| 715 |
+
|
| 716 |
+
model_lora, _ = comfy.sd.load_lora_for_models(model, None, lora, strength_model, 0)
|
| 717 |
+
return (model_lora, )
|
| 718 |
+
|
| 719 |
+
|
| 720 |
+
class SaveLoRA:
|
| 721 |
+
def __init__(self):
|
| 722 |
+
self.output_dir = folder_paths.get_output_directory()
|
| 723 |
+
|
| 724 |
+
@classmethod
|
| 725 |
+
def INPUT_TYPES(s):
|
| 726 |
+
return {
|
| 727 |
+
"required": {
|
| 728 |
+
"lora": (
|
| 729 |
+
IO.LORA_MODEL,
|
| 730 |
+
{
|
| 731 |
+
"tooltip": "The LoRA model to save. Do not use the model with LoRA layers."
|
| 732 |
+
},
|
| 733 |
+
),
|
| 734 |
+
"prefix": (
|
| 735 |
+
"STRING",
|
| 736 |
+
{
|
| 737 |
+
"default": "loras/ComfyUI_trained_lora",
|
| 738 |
+
"tooltip": "The prefix to use for the saved LoRA file.",
|
| 739 |
+
},
|
| 740 |
+
),
|
| 741 |
+
},
|
| 742 |
+
"optional": {
|
| 743 |
+
"steps": (
|
| 744 |
+
IO.INT,
|
| 745 |
+
{
|
| 746 |
+
"forceInput": True,
|
| 747 |
+
"tooltip": "Optional: The number of steps to LoRA has been trained for, used to name the saved file.",
|
| 748 |
+
},
|
| 749 |
+
),
|
| 750 |
+
},
|
| 751 |
+
}
|
| 752 |
+
|
| 753 |
+
RETURN_TYPES = ()
|
| 754 |
+
FUNCTION = "save"
|
| 755 |
+
CATEGORY = "loaders"
|
| 756 |
+
EXPERIMENTAL = True
|
| 757 |
+
OUTPUT_NODE = True
|
| 758 |
+
|
| 759 |
+
def save(self, lora, prefix, steps=None):
|
| 760 |
+
full_output_folder, filename, counter, subfolder, filename_prefix = folder_paths.get_save_image_path(prefix, self.output_dir)
|
| 761 |
+
if steps is None:
|
| 762 |
+
output_checkpoint = f"{filename}_{counter:05}_.safetensors"
|
| 763 |
+
else:
|
| 764 |
+
output_checkpoint = f"{filename}_{steps}_steps_{counter:05}_.safetensors"
|
| 765 |
+
output_checkpoint = os.path.join(full_output_folder, output_checkpoint)
|
| 766 |
+
safetensors.torch.save_file(lora, output_checkpoint)
|
| 767 |
+
return {}
|
| 768 |
+
|
| 769 |
+
|
| 770 |
+
class LossGraphNode:
|
| 771 |
+
def __init__(self):
|
| 772 |
+
self.output_dir = folder_paths.get_temp_directory()
|
| 773 |
+
|
| 774 |
+
@classmethod
|
| 775 |
+
def INPUT_TYPES(s):
|
| 776 |
+
return {
|
| 777 |
+
"required": {
|
| 778 |
+
"loss": (IO.LOSS_MAP, {"default": {}}),
|
| 779 |
+
"filename_prefix": (IO.STRING, {"default": "loss_graph"}),
|
| 780 |
+
},
|
| 781 |
+
"hidden": {"prompt": "PROMPT", "extra_pnginfo": "EXTRA_PNGINFO"},
|
| 782 |
+
}
|
| 783 |
+
|
| 784 |
+
RETURN_TYPES = ()
|
| 785 |
+
FUNCTION = "plot_loss"
|
| 786 |
+
OUTPUT_NODE = True
|
| 787 |
+
CATEGORY = "training"
|
| 788 |
+
EXPERIMENTAL = True
|
| 789 |
+
DESCRIPTION = "Plots the loss graph and saves it to the output directory."
|
| 790 |
+
|
| 791 |
+
def plot_loss(self, loss, filename_prefix, prompt=None, extra_pnginfo=None):
|
| 792 |
+
loss_values = loss["loss"]
|
| 793 |
+
width, height = 800, 480
|
| 794 |
+
margin = 40
|
| 795 |
+
|
| 796 |
+
img = Image.new(
|
| 797 |
+
"RGB", (width + margin, height + margin), "white"
|
| 798 |
+
) # Extend canvas
|
| 799 |
+
draw = ImageDraw.Draw(img)
|
| 800 |
+
|
| 801 |
+
min_loss, max_loss = min(loss_values), max(loss_values)
|
| 802 |
+
scaled_loss = [(l - min_loss) / (max_loss - min_loss) for l in loss_values]
|
| 803 |
+
|
| 804 |
+
steps = len(loss_values)
|
| 805 |
+
|
| 806 |
+
prev_point = (margin, height - int(scaled_loss[0] * height))
|
| 807 |
+
for i, l in enumerate(scaled_loss[1:], start=1):
|
| 808 |
+
x = margin + int(i / steps * width) # Scale X properly
|
| 809 |
+
y = height - int(l * height)
|
| 810 |
+
draw.line([prev_point, (x, y)], fill="blue", width=2)
|
| 811 |
+
prev_point = (x, y)
|
| 812 |
+
|
| 813 |
+
draw.line([(margin, 0), (margin, height)], fill="black", width=2) # Y-axis
|
| 814 |
+
draw.line(
|
| 815 |
+
[(margin, height), (width + margin, height)], fill="black", width=2
|
| 816 |
+
) # X-axis
|
| 817 |
+
|
| 818 |
+
font = None
|
| 819 |
+
try:
|
| 820 |
+
font = ImageFont.truetype("arial.ttf", 12)
|
| 821 |
+
except IOError:
|
| 822 |
+
font = ImageFont.load_default()
|
| 823 |
+
|
| 824 |
+
# Add axis labels
|
| 825 |
+
draw.text((5, height // 2), "Loss", font=font, fill="black")
|
| 826 |
+
draw.text((width // 2, height + 10), "Steps", font=font, fill="black")
|
| 827 |
+
|
| 828 |
+
# Add min/max loss values
|
| 829 |
+
draw.text((margin - 30, 0), f"{max_loss:.2f}", font=font, fill="black")
|
| 830 |
+
draw.text(
|
| 831 |
+
(margin - 30, height - 10), f"{min_loss:.2f}", font=font, fill="black"
|
| 832 |
+
)
|
| 833 |
+
|
| 834 |
+
metadata = None
|
| 835 |
+
if not args.disable_metadata:
|
| 836 |
+
metadata = PngInfo()
|
| 837 |
+
if prompt is not None:
|
| 838 |
+
metadata.add_text("prompt", json.dumps(prompt))
|
| 839 |
+
if extra_pnginfo is not None:
|
| 840 |
+
for x in extra_pnginfo:
|
| 841 |
+
metadata.add_text(x, json.dumps(extra_pnginfo[x]))
|
| 842 |
+
|
| 843 |
+
date = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
|
| 844 |
+
img.save(
|
| 845 |
+
os.path.join(self.output_dir, f"{filename_prefix}_{date}.png"),
|
| 846 |
+
pnginfo=metadata,
|
| 847 |
+
)
|
| 848 |
+
return {
|
| 849 |
+
"ui": {
|
| 850 |
+
"images": [
|
| 851 |
+
{
|
| 852 |
+
"filename": f"{filename_prefix}_{date}.png",
|
| 853 |
+
"subfolder": "",
|
| 854 |
+
"type": "temp",
|
| 855 |
+
}
|
| 856 |
+
]
|
| 857 |
+
}
|
| 858 |
+
}
|
| 859 |
+
|
| 860 |
+
|
| 861 |
+
NODE_CLASS_MAPPINGS = {
|
| 862 |
+
"TrainLoraNode": TrainLoraNode,
|
| 863 |
+
"SaveLoRANode": SaveLoRA,
|
| 864 |
+
"LoraModelLoader": LoraModelLoader,
|
| 865 |
+
"LoadImageSetFromFolderNode": LoadImageSetFromFolderNode,
|
| 866 |
+
"LoadImageTextSetFromFolderNode": LoadImageTextSetFromFolderNode,
|
| 867 |
+
"LossGraphNode": LossGraphNode,
|
| 868 |
+
}
|
| 869 |
+
|
| 870 |
+
NODE_DISPLAY_NAME_MAPPINGS = {
|
| 871 |
+
"TrainLoraNode": "Train LoRA",
|
| 872 |
+
"SaveLoRANode": "Save LoRA Weights",
|
| 873 |
+
"LoraModelLoader": "Load LoRA Model",
|
| 874 |
+
"LoadImageSetFromFolderNode": "Load Image Dataset from Folder",
|
| 875 |
+
"LoadImageTextSetFromFolderNode": "Load Image and Text Dataset from Folder",
|
| 876 |
+
"LossGraphNode": "Plot Loss Graph",
|
| 877 |
+
}
|
ComfyUI/comfy_extras/nodes_video.py
ADDED
|
@@ -0,0 +1,241 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import os
|
| 4 |
+
import av
|
| 5 |
+
import torch
|
| 6 |
+
import folder_paths
|
| 7 |
+
import json
|
| 8 |
+
from typing import Optional, Literal
|
| 9 |
+
from fractions import Fraction
|
| 10 |
+
from comfy.comfy_types import IO, FileLocator, ComfyNodeABC
|
| 11 |
+
from comfy_api.input import ImageInput, AudioInput, VideoInput
|
| 12 |
+
from comfy_api.util import VideoContainer, VideoCodec, VideoComponents
|
| 13 |
+
from comfy_api.input_impl import VideoFromFile, VideoFromComponents
|
| 14 |
+
from comfy.cli_args import args
|
| 15 |
+
|
| 16 |
+
class SaveWEBM:
|
| 17 |
+
def __init__(self):
|
| 18 |
+
self.output_dir = folder_paths.get_output_directory()
|
| 19 |
+
self.type = "output"
|
| 20 |
+
self.prefix_append = ""
|
| 21 |
+
|
| 22 |
+
@classmethod
|
| 23 |
+
def INPUT_TYPES(s):
|
| 24 |
+
return {"required":
|
| 25 |
+
{"images": ("IMAGE", ),
|
| 26 |
+
"filename_prefix": ("STRING", {"default": "ComfyUI"}),
|
| 27 |
+
"codec": (["vp9", "av1"],),
|
| 28 |
+
"fps": ("FLOAT", {"default": 24.0, "min": 0.01, "max": 1000.0, "step": 0.01}),
|
| 29 |
+
"crf": ("FLOAT", {"default": 32.0, "min": 0, "max": 63.0, "step": 1, "tooltip": "Higher crf means lower quality with a smaller file size, lower crf means higher quality higher filesize."}),
|
| 30 |
+
},
|
| 31 |
+
"hidden": {"prompt": "PROMPT", "extra_pnginfo": "EXTRA_PNGINFO"},
|
| 32 |
+
}
|
| 33 |
+
|
| 34 |
+
RETURN_TYPES = ()
|
| 35 |
+
FUNCTION = "save_images"
|
| 36 |
+
|
| 37 |
+
OUTPUT_NODE = True
|
| 38 |
+
|
| 39 |
+
CATEGORY = "image/video"
|
| 40 |
+
|
| 41 |
+
EXPERIMENTAL = True
|
| 42 |
+
|
| 43 |
+
def save_images(self, images, codec, fps, filename_prefix, crf, prompt=None, extra_pnginfo=None):
|
| 44 |
+
filename_prefix += self.prefix_append
|
| 45 |
+
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])
|
| 46 |
+
|
| 47 |
+
file = f"{filename}_{counter:05}_.webm"
|
| 48 |
+
container = av.open(os.path.join(full_output_folder, file), mode="w")
|
| 49 |
+
|
| 50 |
+
if prompt is not None:
|
| 51 |
+
container.metadata["prompt"] = json.dumps(prompt)
|
| 52 |
+
|
| 53 |
+
if extra_pnginfo is not None:
|
| 54 |
+
for x in extra_pnginfo:
|
| 55 |
+
container.metadata[x] = json.dumps(extra_pnginfo[x])
|
| 56 |
+
|
| 57 |
+
codec_map = {"vp9": "libvpx-vp9", "av1": "libsvtav1"}
|
| 58 |
+
stream = container.add_stream(codec_map[codec], rate=Fraction(round(fps * 1000), 1000))
|
| 59 |
+
stream.width = images.shape[-2]
|
| 60 |
+
stream.height = images.shape[-3]
|
| 61 |
+
stream.pix_fmt = "yuv420p10le" if codec == "av1" else "yuv420p"
|
| 62 |
+
stream.bit_rate = 0
|
| 63 |
+
stream.options = {'crf': str(crf)}
|
| 64 |
+
if codec == "av1":
|
| 65 |
+
stream.options["preset"] = "6"
|
| 66 |
+
|
| 67 |
+
for frame in images:
|
| 68 |
+
frame = av.VideoFrame.from_ndarray(torch.clamp(frame[..., :3] * 255, min=0, max=255).to(device=torch.device("cpu"), dtype=torch.uint8).numpy(), format="rgb24")
|
| 69 |
+
for packet in stream.encode(frame):
|
| 70 |
+
container.mux(packet)
|
| 71 |
+
container.mux(stream.encode())
|
| 72 |
+
container.close()
|
| 73 |
+
|
| 74 |
+
results: list[FileLocator] = [{
|
| 75 |
+
"filename": file,
|
| 76 |
+
"subfolder": subfolder,
|
| 77 |
+
"type": self.type
|
| 78 |
+
}]
|
| 79 |
+
|
| 80 |
+
return {"ui": {"images": results, "animated": (True,)}} # TODO: frontend side
|
| 81 |
+
|
| 82 |
+
class SaveVideo(ComfyNodeABC):
|
| 83 |
+
def __init__(self):
|
| 84 |
+
self.output_dir = folder_paths.get_output_directory()
|
| 85 |
+
self.type: Literal["output"] = "output"
|
| 86 |
+
self.prefix_append = ""
|
| 87 |
+
|
| 88 |
+
@classmethod
|
| 89 |
+
def INPUT_TYPES(cls):
|
| 90 |
+
return {
|
| 91 |
+
"required": {
|
| 92 |
+
"video": (IO.VIDEO, {"tooltip": "The video to save."}),
|
| 93 |
+
"filename_prefix": ("STRING", {"default": "video/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."}),
|
| 94 |
+
"format": (VideoContainer.as_input(), {"default": "auto", "tooltip": "The format to save the video as."}),
|
| 95 |
+
"codec": (VideoCodec.as_input(), {"default": "auto", "tooltip": "The codec to use for the video."}),
|
| 96 |
+
},
|
| 97 |
+
"hidden": {
|
| 98 |
+
"prompt": "PROMPT",
|
| 99 |
+
"extra_pnginfo": "EXTRA_PNGINFO"
|
| 100 |
+
},
|
| 101 |
+
}
|
| 102 |
+
|
| 103 |
+
RETURN_TYPES = ()
|
| 104 |
+
FUNCTION = "save_video"
|
| 105 |
+
|
| 106 |
+
OUTPUT_NODE = True
|
| 107 |
+
|
| 108 |
+
CATEGORY = "image/video"
|
| 109 |
+
DESCRIPTION = "Saves the input images to your ComfyUI output directory."
|
| 110 |
+
|
| 111 |
+
def save_video(self, video: VideoInput, filename_prefix, format, codec, prompt=None, extra_pnginfo=None):
|
| 112 |
+
filename_prefix += self.prefix_append
|
| 113 |
+
width, height = video.get_dimensions()
|
| 114 |
+
full_output_folder, filename, counter, subfolder, filename_prefix = folder_paths.get_save_image_path(
|
| 115 |
+
filename_prefix,
|
| 116 |
+
self.output_dir,
|
| 117 |
+
width,
|
| 118 |
+
height
|
| 119 |
+
)
|
| 120 |
+
results: list[FileLocator] = list()
|
| 121 |
+
saved_metadata = None
|
| 122 |
+
if not args.disable_metadata:
|
| 123 |
+
metadata = {}
|
| 124 |
+
if extra_pnginfo is not None:
|
| 125 |
+
metadata.update(extra_pnginfo)
|
| 126 |
+
if prompt is not None:
|
| 127 |
+
metadata["prompt"] = prompt
|
| 128 |
+
if len(metadata) > 0:
|
| 129 |
+
saved_metadata = metadata
|
| 130 |
+
file = f"{filename}_{counter:05}_.{VideoContainer.get_extension(format)}"
|
| 131 |
+
video.save_to(
|
| 132 |
+
os.path.join(full_output_folder, file),
|
| 133 |
+
format=format,
|
| 134 |
+
codec=codec,
|
| 135 |
+
metadata=saved_metadata
|
| 136 |
+
)
|
| 137 |
+
|
| 138 |
+
results.append({
|
| 139 |
+
"filename": file,
|
| 140 |
+
"subfolder": subfolder,
|
| 141 |
+
"type": self.type
|
| 142 |
+
})
|
| 143 |
+
counter += 1
|
| 144 |
+
|
| 145 |
+
return { "ui": { "images": results, "animated": (True,) } }
|
| 146 |
+
|
| 147 |
+
class CreateVideo(ComfyNodeABC):
|
| 148 |
+
@classmethod
|
| 149 |
+
def INPUT_TYPES(cls):
|
| 150 |
+
return {
|
| 151 |
+
"required": {
|
| 152 |
+
"images": (IO.IMAGE, {"tooltip": "The images to create a video from."}),
|
| 153 |
+
"fps": ("FLOAT", {"default": 30.0, "min": 1.0, "max": 120.0, "step": 1.0}),
|
| 154 |
+
},
|
| 155 |
+
"optional": {
|
| 156 |
+
"audio": (IO.AUDIO, {"tooltip": "The audio to add to the video."}),
|
| 157 |
+
}
|
| 158 |
+
}
|
| 159 |
+
|
| 160 |
+
RETURN_TYPES = (IO.VIDEO,)
|
| 161 |
+
FUNCTION = "create_video"
|
| 162 |
+
|
| 163 |
+
CATEGORY = "image/video"
|
| 164 |
+
DESCRIPTION = "Create a video from images."
|
| 165 |
+
|
| 166 |
+
def create_video(self, images: ImageInput, fps: float, audio: Optional[AudioInput] = None):
|
| 167 |
+
return (VideoFromComponents(
|
| 168 |
+
VideoComponents(
|
| 169 |
+
images=images,
|
| 170 |
+
audio=audio,
|
| 171 |
+
frame_rate=Fraction(fps),
|
| 172 |
+
)
|
| 173 |
+
),)
|
| 174 |
+
|
| 175 |
+
class GetVideoComponents(ComfyNodeABC):
|
| 176 |
+
@classmethod
|
| 177 |
+
def INPUT_TYPES(cls):
|
| 178 |
+
return {
|
| 179 |
+
"required": {
|
| 180 |
+
"video": (IO.VIDEO, {"tooltip": "The video to extract components from."}),
|
| 181 |
+
}
|
| 182 |
+
}
|
| 183 |
+
RETURN_TYPES = (IO.IMAGE, IO.AUDIO, IO.FLOAT)
|
| 184 |
+
RETURN_NAMES = ("images", "audio", "fps")
|
| 185 |
+
FUNCTION = "get_components"
|
| 186 |
+
|
| 187 |
+
CATEGORY = "image/video"
|
| 188 |
+
DESCRIPTION = "Extracts all components from a video: frames, audio, and framerate."
|
| 189 |
+
|
| 190 |
+
def get_components(self, video: VideoInput):
|
| 191 |
+
components = video.get_components()
|
| 192 |
+
|
| 193 |
+
return (components.images, components.audio, float(components.frame_rate))
|
| 194 |
+
|
| 195 |
+
class LoadVideo(ComfyNodeABC):
|
| 196 |
+
@classmethod
|
| 197 |
+
def INPUT_TYPES(cls):
|
| 198 |
+
input_dir = folder_paths.get_input_directory()
|
| 199 |
+
files = [f for f in os.listdir(input_dir) if os.path.isfile(os.path.join(input_dir, f))]
|
| 200 |
+
files = folder_paths.filter_files_content_types(files, ["video"])
|
| 201 |
+
return {"required":
|
| 202 |
+
{"file": (sorted(files), {"video_upload": True})},
|
| 203 |
+
}
|
| 204 |
+
|
| 205 |
+
CATEGORY = "image/video"
|
| 206 |
+
|
| 207 |
+
RETURN_TYPES = (IO.VIDEO,)
|
| 208 |
+
FUNCTION = "load_video"
|
| 209 |
+
def load_video(self, file):
|
| 210 |
+
video_path = folder_paths.get_annotated_filepath(file)
|
| 211 |
+
return (VideoFromFile(video_path),)
|
| 212 |
+
|
| 213 |
+
@classmethod
|
| 214 |
+
def IS_CHANGED(cls, file):
|
| 215 |
+
video_path = folder_paths.get_annotated_filepath(file)
|
| 216 |
+
mod_time = os.path.getmtime(video_path)
|
| 217 |
+
# Instead of hashing the file, we can just use the modification time to avoid
|
| 218 |
+
# rehashing large files.
|
| 219 |
+
return mod_time
|
| 220 |
+
|
| 221 |
+
@classmethod
|
| 222 |
+
def VALIDATE_INPUTS(cls, file):
|
| 223 |
+
if not folder_paths.exists_annotated_filepath(file):
|
| 224 |
+
return "Invalid video file: {}".format(file)
|
| 225 |
+
|
| 226 |
+
return True
|
| 227 |
+
|
| 228 |
+
NODE_CLASS_MAPPINGS = {
|
| 229 |
+
"SaveWEBM": SaveWEBM,
|
| 230 |
+
"SaveVideo": SaveVideo,
|
| 231 |
+
"CreateVideo": CreateVideo,
|
| 232 |
+
"GetVideoComponents": GetVideoComponents,
|
| 233 |
+
"LoadVideo": LoadVideo,
|
| 234 |
+
}
|
| 235 |
+
|
| 236 |
+
NODE_DISPLAY_NAME_MAPPINGS = {
|
| 237 |
+
"SaveVideo": "Save Video",
|
| 238 |
+
"CreateVideo": "Create Video",
|
| 239 |
+
"GetVideoComponents": "Get Video Components",
|
| 240 |
+
"LoadVideo": "Load Video",
|
| 241 |
+
}
|
ComfyUI/comfy_extras/nodes_wan.py
ADDED
|
@@ -0,0 +1,742 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import math
|
| 2 |
+
import nodes
|
| 3 |
+
import node_helpers
|
| 4 |
+
import torch
|
| 5 |
+
import comfy.model_management
|
| 6 |
+
import comfy.utils
|
| 7 |
+
import comfy.latent_formats
|
| 8 |
+
import comfy.clip_vision
|
| 9 |
+
import json
|
| 10 |
+
import numpy as np
|
| 11 |
+
from typing import Tuple
|
| 12 |
+
|
| 13 |
+
class WanImageToVideo:
|
| 14 |
+
@classmethod
|
| 15 |
+
def INPUT_TYPES(s):
|
| 16 |
+
return {"required": {"positive": ("CONDITIONING", ),
|
| 17 |
+
"negative": ("CONDITIONING", ),
|
| 18 |
+
"vae": ("VAE", ),
|
| 19 |
+
"width": ("INT", {"default": 832, "min": 16, "max": nodes.MAX_RESOLUTION, "step": 16}),
|
| 20 |
+
"height": ("INT", {"default": 480, "min": 16, "max": nodes.MAX_RESOLUTION, "step": 16}),
|
| 21 |
+
"length": ("INT", {"default": 81, "min": 1, "max": nodes.MAX_RESOLUTION, "step": 4}),
|
| 22 |
+
"batch_size": ("INT", {"default": 1, "min": 1, "max": 4096}),
|
| 23 |
+
},
|
| 24 |
+
"optional": {"clip_vision_output": ("CLIP_VISION_OUTPUT", ),
|
| 25 |
+
"start_image": ("IMAGE", ),
|
| 26 |
+
}}
|
| 27 |
+
|
| 28 |
+
RETURN_TYPES = ("CONDITIONING", "CONDITIONING", "LATENT")
|
| 29 |
+
RETURN_NAMES = ("positive", "negative", "latent")
|
| 30 |
+
FUNCTION = "encode"
|
| 31 |
+
|
| 32 |
+
CATEGORY = "conditioning/video_models"
|
| 33 |
+
|
| 34 |
+
def encode(self, positive, negative, vae, width, height, length, batch_size, start_image=None, clip_vision_output=None):
|
| 35 |
+
latent = torch.zeros([batch_size, 16, ((length - 1) // 4) + 1, height // 8, width // 8], device=comfy.model_management.intermediate_device())
|
| 36 |
+
if start_image is not None:
|
| 37 |
+
start_image = comfy.utils.common_upscale(start_image[:length].movedim(-1, 1), width, height, "bilinear", "center").movedim(1, -1)
|
| 38 |
+
image = torch.ones((length, height, width, start_image.shape[-1]), device=start_image.device, dtype=start_image.dtype) * 0.5
|
| 39 |
+
image[:start_image.shape[0]] = start_image
|
| 40 |
+
|
| 41 |
+
concat_latent_image = vae.encode(image[:, :, :, :3])
|
| 42 |
+
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)
|
| 43 |
+
mask[:, :, :((start_image.shape[0] - 1) // 4) + 1] = 0.0
|
| 44 |
+
|
| 45 |
+
positive = node_helpers.conditioning_set_values(positive, {"concat_latent_image": concat_latent_image, "concat_mask": mask})
|
| 46 |
+
negative = node_helpers.conditioning_set_values(negative, {"concat_latent_image": concat_latent_image, "concat_mask": mask})
|
| 47 |
+
|
| 48 |
+
if clip_vision_output is not None:
|
| 49 |
+
positive = node_helpers.conditioning_set_values(positive, {"clip_vision_output": clip_vision_output})
|
| 50 |
+
negative = node_helpers.conditioning_set_values(negative, {"clip_vision_output": clip_vision_output})
|
| 51 |
+
|
| 52 |
+
out_latent = {}
|
| 53 |
+
out_latent["samples"] = latent
|
| 54 |
+
return (positive, negative, out_latent)
|
| 55 |
+
|
| 56 |
+
|
| 57 |
+
class WanFunControlToVideo:
|
| 58 |
+
@classmethod
|
| 59 |
+
def INPUT_TYPES(s):
|
| 60 |
+
return {"required": {"positive": ("CONDITIONING", ),
|
| 61 |
+
"negative": ("CONDITIONING", ),
|
| 62 |
+
"vae": ("VAE", ),
|
| 63 |
+
"width": ("INT", {"default": 832, "min": 16, "max": nodes.MAX_RESOLUTION, "step": 16}),
|
| 64 |
+
"height": ("INT", {"default": 480, "min": 16, "max": nodes.MAX_RESOLUTION, "step": 16}),
|
| 65 |
+
"length": ("INT", {"default": 81, "min": 1, "max": nodes.MAX_RESOLUTION, "step": 4}),
|
| 66 |
+
"batch_size": ("INT", {"default": 1, "min": 1, "max": 4096}),
|
| 67 |
+
},
|
| 68 |
+
"optional": {"clip_vision_output": ("CLIP_VISION_OUTPUT", ),
|
| 69 |
+
"start_image": ("IMAGE", ),
|
| 70 |
+
"control_video": ("IMAGE", ),
|
| 71 |
+
}}
|
| 72 |
+
|
| 73 |
+
RETURN_TYPES = ("CONDITIONING", "CONDITIONING", "LATENT")
|
| 74 |
+
RETURN_NAMES = ("positive", "negative", "latent")
|
| 75 |
+
FUNCTION = "encode"
|
| 76 |
+
|
| 77 |
+
CATEGORY = "conditioning/video_models"
|
| 78 |
+
|
| 79 |
+
def encode(self, positive, negative, vae, width, height, length, batch_size, start_image=None, clip_vision_output=None, control_video=None):
|
| 80 |
+
latent = torch.zeros([batch_size, 16, ((length - 1) // 4) + 1, height // 8, width // 8], device=comfy.model_management.intermediate_device())
|
| 81 |
+
concat_latent = torch.zeros([batch_size, 16, ((length - 1) // 4) + 1, height // 8, width // 8], device=comfy.model_management.intermediate_device())
|
| 82 |
+
concat_latent = comfy.latent_formats.Wan21().process_out(concat_latent)
|
| 83 |
+
concat_latent = concat_latent.repeat(1, 2, 1, 1, 1)
|
| 84 |
+
|
| 85 |
+
if start_image is not None:
|
| 86 |
+
start_image = comfy.utils.common_upscale(start_image[:length].movedim(-1, 1), width, height, "bilinear", "center").movedim(1, -1)
|
| 87 |
+
concat_latent_image = vae.encode(start_image[:, :, :, :3])
|
| 88 |
+
concat_latent[:,16:,:concat_latent_image.shape[2]] = concat_latent_image[:,:,:concat_latent.shape[2]]
|
| 89 |
+
|
| 90 |
+
if control_video is not None:
|
| 91 |
+
control_video = comfy.utils.common_upscale(control_video[:length].movedim(-1, 1), width, height, "bilinear", "center").movedim(1, -1)
|
| 92 |
+
concat_latent_image = vae.encode(control_video[:, :, :, :3])
|
| 93 |
+
concat_latent[:,:16,:concat_latent_image.shape[2]] = concat_latent_image[:,:,:concat_latent.shape[2]]
|
| 94 |
+
|
| 95 |
+
positive = node_helpers.conditioning_set_values(positive, {"concat_latent_image": concat_latent})
|
| 96 |
+
negative = node_helpers.conditioning_set_values(negative, {"concat_latent_image": concat_latent})
|
| 97 |
+
|
| 98 |
+
if clip_vision_output is not None:
|
| 99 |
+
positive = node_helpers.conditioning_set_values(positive, {"clip_vision_output": clip_vision_output})
|
| 100 |
+
negative = node_helpers.conditioning_set_values(negative, {"clip_vision_output": clip_vision_output})
|
| 101 |
+
|
| 102 |
+
out_latent = {}
|
| 103 |
+
out_latent["samples"] = latent
|
| 104 |
+
return (positive, negative, out_latent)
|
| 105 |
+
|
| 106 |
+
class WanFirstLastFrameToVideo:
|
| 107 |
+
@classmethod
|
| 108 |
+
def INPUT_TYPES(s):
|
| 109 |
+
return {"required": {"positive": ("CONDITIONING", ),
|
| 110 |
+
"negative": ("CONDITIONING", ),
|
| 111 |
+
"vae": ("VAE", ),
|
| 112 |
+
"width": ("INT", {"default": 832, "min": 16, "max": nodes.MAX_RESOLUTION, "step": 16}),
|
| 113 |
+
"height": ("INT", {"default": 480, "min": 16, "max": nodes.MAX_RESOLUTION, "step": 16}),
|
| 114 |
+
"length": ("INT", {"default": 81, "min": 1, "max": nodes.MAX_RESOLUTION, "step": 4}),
|
| 115 |
+
"batch_size": ("INT", {"default": 1, "min": 1, "max": 4096}),
|
| 116 |
+
},
|
| 117 |
+
"optional": {"clip_vision_start_image": ("CLIP_VISION_OUTPUT", ),
|
| 118 |
+
"clip_vision_end_image": ("CLIP_VISION_OUTPUT", ),
|
| 119 |
+
"start_image": ("IMAGE", ),
|
| 120 |
+
"end_image": ("IMAGE", ),
|
| 121 |
+
}}
|
| 122 |
+
|
| 123 |
+
RETURN_TYPES = ("CONDITIONING", "CONDITIONING", "LATENT")
|
| 124 |
+
RETURN_NAMES = ("positive", "negative", "latent")
|
| 125 |
+
FUNCTION = "encode"
|
| 126 |
+
|
| 127 |
+
CATEGORY = "conditioning/video_models"
|
| 128 |
+
|
| 129 |
+
def encode(self, positive, negative, vae, width, height, length, batch_size, start_image=None, end_image=None, clip_vision_start_image=None, clip_vision_end_image=None):
|
| 130 |
+
latent = torch.zeros([batch_size, 16, ((length - 1) // 4) + 1, height // 8, width // 8], device=comfy.model_management.intermediate_device())
|
| 131 |
+
if start_image is not None:
|
| 132 |
+
start_image = comfy.utils.common_upscale(start_image[:length].movedim(-1, 1), width, height, "bilinear", "center").movedim(1, -1)
|
| 133 |
+
if end_image is not None:
|
| 134 |
+
end_image = comfy.utils.common_upscale(end_image[-length:].movedim(-1, 1), width, height, "bilinear", "center").movedim(1, -1)
|
| 135 |
+
|
| 136 |
+
image = torch.ones((length, height, width, 3)) * 0.5
|
| 137 |
+
mask = torch.ones((1, 1, latent.shape[2] * 4, latent.shape[-2], latent.shape[-1]))
|
| 138 |
+
|
| 139 |
+
if start_image is not None:
|
| 140 |
+
image[:start_image.shape[0]] = start_image
|
| 141 |
+
mask[:, :, :start_image.shape[0] + 3] = 0.0
|
| 142 |
+
|
| 143 |
+
if end_image is not None:
|
| 144 |
+
image[-end_image.shape[0]:] = end_image
|
| 145 |
+
mask[:, :, -end_image.shape[0]:] = 0.0
|
| 146 |
+
|
| 147 |
+
concat_latent_image = vae.encode(image[:, :, :, :3])
|
| 148 |
+
mask = mask.view(1, mask.shape[2] // 4, 4, mask.shape[3], mask.shape[4]).transpose(1, 2)
|
| 149 |
+
positive = node_helpers.conditioning_set_values(positive, {"concat_latent_image": concat_latent_image, "concat_mask": mask})
|
| 150 |
+
negative = node_helpers.conditioning_set_values(negative, {"concat_latent_image": concat_latent_image, "concat_mask": mask})
|
| 151 |
+
|
| 152 |
+
if clip_vision_start_image is not None:
|
| 153 |
+
clip_vision_output = clip_vision_start_image
|
| 154 |
+
|
| 155 |
+
if clip_vision_end_image is not None:
|
| 156 |
+
if clip_vision_output is not None:
|
| 157 |
+
states = torch.cat([clip_vision_output.penultimate_hidden_states, clip_vision_end_image.penultimate_hidden_states], dim=-2)
|
| 158 |
+
clip_vision_output = comfy.clip_vision.Output()
|
| 159 |
+
clip_vision_output.penultimate_hidden_states = states
|
| 160 |
+
else:
|
| 161 |
+
clip_vision_output = clip_vision_end_image
|
| 162 |
+
|
| 163 |
+
if clip_vision_output is not None:
|
| 164 |
+
positive = node_helpers.conditioning_set_values(positive, {"clip_vision_output": clip_vision_output})
|
| 165 |
+
negative = node_helpers.conditioning_set_values(negative, {"clip_vision_output": clip_vision_output})
|
| 166 |
+
|
| 167 |
+
out_latent = {}
|
| 168 |
+
out_latent["samples"] = latent
|
| 169 |
+
return (positive, negative, out_latent)
|
| 170 |
+
|
| 171 |
+
|
| 172 |
+
class WanFunInpaintToVideo:
|
| 173 |
+
@classmethod
|
| 174 |
+
def INPUT_TYPES(s):
|
| 175 |
+
return {"required": {"positive": ("CONDITIONING", ),
|
| 176 |
+
"negative": ("CONDITIONING", ),
|
| 177 |
+
"vae": ("VAE", ),
|
| 178 |
+
"width": ("INT", {"default": 832, "min": 16, "max": nodes.MAX_RESOLUTION, "step": 16}),
|
| 179 |
+
"height": ("INT", {"default": 480, "min": 16, "max": nodes.MAX_RESOLUTION, "step": 16}),
|
| 180 |
+
"length": ("INT", {"default": 81, "min": 1, "max": nodes.MAX_RESOLUTION, "step": 4}),
|
| 181 |
+
"batch_size": ("INT", {"default": 1, "min": 1, "max": 4096}),
|
| 182 |
+
},
|
| 183 |
+
"optional": {"clip_vision_output": ("CLIP_VISION_OUTPUT", ),
|
| 184 |
+
"start_image": ("IMAGE", ),
|
| 185 |
+
"end_image": ("IMAGE", ),
|
| 186 |
+
}}
|
| 187 |
+
|
| 188 |
+
RETURN_TYPES = ("CONDITIONING", "CONDITIONING", "LATENT")
|
| 189 |
+
RETURN_NAMES = ("positive", "negative", "latent")
|
| 190 |
+
FUNCTION = "encode"
|
| 191 |
+
|
| 192 |
+
CATEGORY = "conditioning/video_models"
|
| 193 |
+
|
| 194 |
+
def encode(self, positive, negative, vae, width, height, length, batch_size, start_image=None, end_image=None, clip_vision_output=None):
|
| 195 |
+
flfv = WanFirstLastFrameToVideo()
|
| 196 |
+
return flfv.encode(positive, negative, vae, width, height, length, batch_size, start_image=start_image, end_image=end_image, clip_vision_start_image=clip_vision_output)
|
| 197 |
+
|
| 198 |
+
|
| 199 |
+
class WanVaceToVideo:
|
| 200 |
+
@classmethod
|
| 201 |
+
def INPUT_TYPES(s):
|
| 202 |
+
return {"required": {"positive": ("CONDITIONING", ),
|
| 203 |
+
"negative": ("CONDITIONING", ),
|
| 204 |
+
"vae": ("VAE", ),
|
| 205 |
+
"width": ("INT", {"default": 832, "min": 16, "max": nodes.MAX_RESOLUTION, "step": 16}),
|
| 206 |
+
"height": ("INT", {"default": 480, "min": 16, "max": nodes.MAX_RESOLUTION, "step": 16}),
|
| 207 |
+
"length": ("INT", {"default": 81, "min": 1, "max": nodes.MAX_RESOLUTION, "step": 4}),
|
| 208 |
+
"batch_size": ("INT", {"default": 1, "min": 1, "max": 4096}),
|
| 209 |
+
"strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1000.0, "step": 0.01}),
|
| 210 |
+
},
|
| 211 |
+
"optional": {"control_video": ("IMAGE", ),
|
| 212 |
+
"control_masks": ("MASK", ),
|
| 213 |
+
"reference_image": ("IMAGE", ),
|
| 214 |
+
}}
|
| 215 |
+
|
| 216 |
+
RETURN_TYPES = ("CONDITIONING", "CONDITIONING", "LATENT", "INT")
|
| 217 |
+
RETURN_NAMES = ("positive", "negative", "latent", "trim_latent")
|
| 218 |
+
FUNCTION = "encode"
|
| 219 |
+
|
| 220 |
+
CATEGORY = "conditioning/video_models"
|
| 221 |
+
|
| 222 |
+
EXPERIMENTAL = True
|
| 223 |
+
|
| 224 |
+
def encode(self, positive, negative, vae, width, height, length, batch_size, strength, control_video=None, control_masks=None, reference_image=None):
|
| 225 |
+
latent_length = ((length - 1) // 4) + 1
|
| 226 |
+
if control_video is not None:
|
| 227 |
+
control_video = comfy.utils.common_upscale(control_video[:length].movedim(-1, 1), width, height, "bilinear", "center").movedim(1, -1)
|
| 228 |
+
if control_video.shape[0] < length:
|
| 229 |
+
control_video = torch.nn.functional.pad(control_video, (0, 0, 0, 0, 0, 0, 0, length - control_video.shape[0]), value=0.5)
|
| 230 |
+
else:
|
| 231 |
+
control_video = torch.ones((length, height, width, 3)) * 0.5
|
| 232 |
+
|
| 233 |
+
if reference_image is not None:
|
| 234 |
+
reference_image = comfy.utils.common_upscale(reference_image[:1].movedim(-1, 1), width, height, "bilinear", "center").movedim(1, -1)
|
| 235 |
+
reference_image = vae.encode(reference_image[:, :, :, :3])
|
| 236 |
+
reference_image = torch.cat([reference_image, comfy.latent_formats.Wan21().process_out(torch.zeros_like(reference_image))], dim=1)
|
| 237 |
+
|
| 238 |
+
if control_masks is None:
|
| 239 |
+
mask = torch.ones((length, height, width, 1))
|
| 240 |
+
else:
|
| 241 |
+
mask = control_masks
|
| 242 |
+
if mask.ndim == 3:
|
| 243 |
+
mask = mask.unsqueeze(1)
|
| 244 |
+
mask = comfy.utils.common_upscale(mask[:length], width, height, "bilinear", "center").movedim(1, -1)
|
| 245 |
+
if mask.shape[0] < length:
|
| 246 |
+
mask = torch.nn.functional.pad(mask, (0, 0, 0, 0, 0, 0, 0, length - mask.shape[0]), value=1.0)
|
| 247 |
+
|
| 248 |
+
control_video = control_video - 0.5
|
| 249 |
+
inactive = (control_video * (1 - mask)) + 0.5
|
| 250 |
+
reactive = (control_video * mask) + 0.5
|
| 251 |
+
|
| 252 |
+
inactive = vae.encode(inactive[:, :, :, :3])
|
| 253 |
+
reactive = vae.encode(reactive[:, :, :, :3])
|
| 254 |
+
control_video_latent = torch.cat((inactive, reactive), dim=1)
|
| 255 |
+
if reference_image is not None:
|
| 256 |
+
control_video_latent = torch.cat((reference_image, control_video_latent), dim=2)
|
| 257 |
+
|
| 258 |
+
vae_stride = 8
|
| 259 |
+
height_mask = height // vae_stride
|
| 260 |
+
width_mask = width // vae_stride
|
| 261 |
+
mask = mask.view(length, height_mask, vae_stride, width_mask, vae_stride)
|
| 262 |
+
mask = mask.permute(2, 4, 0, 1, 3)
|
| 263 |
+
mask = mask.reshape(vae_stride * vae_stride, length, height_mask, width_mask)
|
| 264 |
+
mask = torch.nn.functional.interpolate(mask.unsqueeze(0), size=(latent_length, height_mask, width_mask), mode='nearest-exact').squeeze(0)
|
| 265 |
+
|
| 266 |
+
trim_latent = 0
|
| 267 |
+
if reference_image is not None:
|
| 268 |
+
mask_pad = torch.zeros_like(mask[:, :reference_image.shape[2], :, :])
|
| 269 |
+
mask = torch.cat((mask_pad, mask), dim=1)
|
| 270 |
+
latent_length += reference_image.shape[2]
|
| 271 |
+
trim_latent = reference_image.shape[2]
|
| 272 |
+
|
| 273 |
+
mask = mask.unsqueeze(0)
|
| 274 |
+
|
| 275 |
+
positive = node_helpers.conditioning_set_values(positive, {"vace_frames": [control_video_latent], "vace_mask": [mask], "vace_strength": [strength]}, append=True)
|
| 276 |
+
negative = node_helpers.conditioning_set_values(negative, {"vace_frames": [control_video_latent], "vace_mask": [mask], "vace_strength": [strength]}, append=True)
|
| 277 |
+
|
| 278 |
+
latent = torch.zeros([batch_size, 16, latent_length, height // 8, width // 8], device=comfy.model_management.intermediate_device())
|
| 279 |
+
out_latent = {}
|
| 280 |
+
out_latent["samples"] = latent
|
| 281 |
+
return (positive, negative, out_latent, trim_latent)
|
| 282 |
+
|
| 283 |
+
class TrimVideoLatent:
|
| 284 |
+
@classmethod
|
| 285 |
+
def INPUT_TYPES(s):
|
| 286 |
+
return {"required": { "samples": ("LATENT",),
|
| 287 |
+
"trim_amount": ("INT", {"default": 0, "min": 0, "max": 99999}),
|
| 288 |
+
}}
|
| 289 |
+
|
| 290 |
+
RETURN_TYPES = ("LATENT",)
|
| 291 |
+
FUNCTION = "op"
|
| 292 |
+
|
| 293 |
+
CATEGORY = "latent/video"
|
| 294 |
+
|
| 295 |
+
EXPERIMENTAL = True
|
| 296 |
+
|
| 297 |
+
def op(self, samples, trim_amount):
|
| 298 |
+
samples_out = samples.copy()
|
| 299 |
+
|
| 300 |
+
s1 = samples["samples"]
|
| 301 |
+
samples_out["samples"] = s1[:, :, trim_amount:]
|
| 302 |
+
return (samples_out,)
|
| 303 |
+
|
| 304 |
+
class WanCameraImageToVideo:
|
| 305 |
+
@classmethod
|
| 306 |
+
def INPUT_TYPES(s):
|
| 307 |
+
return {"required": {"positive": ("CONDITIONING", ),
|
| 308 |
+
"negative": ("CONDITIONING", ),
|
| 309 |
+
"vae": ("VAE", ),
|
| 310 |
+
"width": ("INT", {"default": 832, "min": 16, "max": nodes.MAX_RESOLUTION, "step": 16}),
|
| 311 |
+
"height": ("INT", {"default": 480, "min": 16, "max": nodes.MAX_RESOLUTION, "step": 16}),
|
| 312 |
+
"length": ("INT", {"default": 81, "min": 1, "max": nodes.MAX_RESOLUTION, "step": 4}),
|
| 313 |
+
"batch_size": ("INT", {"default": 1, "min": 1, "max": 4096}),
|
| 314 |
+
},
|
| 315 |
+
"optional": {"clip_vision_output": ("CLIP_VISION_OUTPUT", ),
|
| 316 |
+
"start_image": ("IMAGE", ),
|
| 317 |
+
"camera_conditions": ("WAN_CAMERA_EMBEDDING", ),
|
| 318 |
+
}}
|
| 319 |
+
|
| 320 |
+
RETURN_TYPES = ("CONDITIONING", "CONDITIONING", "LATENT")
|
| 321 |
+
RETURN_NAMES = ("positive", "negative", "latent")
|
| 322 |
+
FUNCTION = "encode"
|
| 323 |
+
|
| 324 |
+
CATEGORY = "conditioning/video_models"
|
| 325 |
+
|
| 326 |
+
def encode(self, positive, negative, vae, width, height, length, batch_size, start_image=None, clip_vision_output=None, camera_conditions=None):
|
| 327 |
+
latent = torch.zeros([batch_size, 16, ((length - 1) // 4) + 1, height // 8, width // 8], device=comfy.model_management.intermediate_device())
|
| 328 |
+
concat_latent = torch.zeros([batch_size, 16, ((length - 1) // 4) + 1, height // 8, width // 8], device=comfy.model_management.intermediate_device())
|
| 329 |
+
concat_latent = comfy.latent_formats.Wan21().process_out(concat_latent)
|
| 330 |
+
|
| 331 |
+
if start_image is not None:
|
| 332 |
+
start_image = comfy.utils.common_upscale(start_image[:length].movedim(-1, 1), width, height, "bilinear", "center").movedim(1, -1)
|
| 333 |
+
concat_latent_image = vae.encode(start_image[:, :, :, :3])
|
| 334 |
+
concat_latent[:,:,:concat_latent_image.shape[2]] = concat_latent_image[:,:,:concat_latent.shape[2]]
|
| 335 |
+
|
| 336 |
+
positive = node_helpers.conditioning_set_values(positive, {"concat_latent_image": concat_latent})
|
| 337 |
+
negative = node_helpers.conditioning_set_values(negative, {"concat_latent_image": concat_latent})
|
| 338 |
+
|
| 339 |
+
if camera_conditions is not None:
|
| 340 |
+
positive = node_helpers.conditioning_set_values(positive, {'camera_conditions': camera_conditions})
|
| 341 |
+
negative = node_helpers.conditioning_set_values(negative, {'camera_conditions': camera_conditions})
|
| 342 |
+
|
| 343 |
+
if clip_vision_output is not None:
|
| 344 |
+
positive = node_helpers.conditioning_set_values(positive, {"clip_vision_output": clip_vision_output})
|
| 345 |
+
negative = node_helpers.conditioning_set_values(negative, {"clip_vision_output": clip_vision_output})
|
| 346 |
+
|
| 347 |
+
out_latent = {}
|
| 348 |
+
out_latent["samples"] = latent
|
| 349 |
+
return (positive, negative, out_latent)
|
| 350 |
+
|
| 351 |
+
class WanPhantomSubjectToVideo:
|
| 352 |
+
@classmethod
|
| 353 |
+
def INPUT_TYPES(s):
|
| 354 |
+
return {"required": {"positive": ("CONDITIONING", ),
|
| 355 |
+
"negative": ("CONDITIONING", ),
|
| 356 |
+
"vae": ("VAE", ),
|
| 357 |
+
"width": ("INT", {"default": 832, "min": 16, "max": nodes.MAX_RESOLUTION, "step": 16}),
|
| 358 |
+
"height": ("INT", {"default": 480, "min": 16, "max": nodes.MAX_RESOLUTION, "step": 16}),
|
| 359 |
+
"length": ("INT", {"default": 81, "min": 1, "max": nodes.MAX_RESOLUTION, "step": 4}),
|
| 360 |
+
"batch_size": ("INT", {"default": 1, "min": 1, "max": 4096}),
|
| 361 |
+
},
|
| 362 |
+
"optional": {"images": ("IMAGE", ),
|
| 363 |
+
}}
|
| 364 |
+
|
| 365 |
+
RETURN_TYPES = ("CONDITIONING", "CONDITIONING", "CONDITIONING", "LATENT")
|
| 366 |
+
RETURN_NAMES = ("positive", "negative_text", "negative_img_text", "latent")
|
| 367 |
+
FUNCTION = "encode"
|
| 368 |
+
|
| 369 |
+
CATEGORY = "conditioning/video_models"
|
| 370 |
+
|
| 371 |
+
def encode(self, positive, negative, vae, width, height, length, batch_size, images):
|
| 372 |
+
latent = torch.zeros([batch_size, 16, ((length - 1) // 4) + 1, height // 8, width // 8], device=comfy.model_management.intermediate_device())
|
| 373 |
+
cond2 = negative
|
| 374 |
+
if images is not None:
|
| 375 |
+
images = comfy.utils.common_upscale(images[:length].movedim(-1, 1), width, height, "bilinear", "center").movedim(1, -1)
|
| 376 |
+
latent_images = []
|
| 377 |
+
for i in images:
|
| 378 |
+
latent_images += [vae.encode(i.unsqueeze(0)[:, :, :, :3])]
|
| 379 |
+
concat_latent_image = torch.cat(latent_images, dim=2)
|
| 380 |
+
|
| 381 |
+
positive = node_helpers.conditioning_set_values(positive, {"time_dim_concat": concat_latent_image})
|
| 382 |
+
cond2 = node_helpers.conditioning_set_values(negative, {"time_dim_concat": concat_latent_image})
|
| 383 |
+
negative = node_helpers.conditioning_set_values(negative, {"time_dim_concat": comfy.latent_formats.Wan21().process_out(torch.zeros_like(concat_latent_image))})
|
| 384 |
+
|
| 385 |
+
out_latent = {}
|
| 386 |
+
out_latent["samples"] = latent
|
| 387 |
+
return (positive, cond2, negative, out_latent)
|
| 388 |
+
|
| 389 |
+
def parse_json_tracks(tracks):
|
| 390 |
+
"""Parse JSON track data into a standardized format"""
|
| 391 |
+
tracks_data = []
|
| 392 |
+
try:
|
| 393 |
+
# If tracks is a string, try to parse it as JSON
|
| 394 |
+
if isinstance(tracks, str):
|
| 395 |
+
parsed = json.loads(tracks.replace("'", '"'))
|
| 396 |
+
tracks_data.extend(parsed)
|
| 397 |
+
else:
|
| 398 |
+
# If tracks is a list of strings, parse each one
|
| 399 |
+
for track_str in tracks:
|
| 400 |
+
parsed = json.loads(track_str.replace("'", '"'))
|
| 401 |
+
tracks_data.append(parsed)
|
| 402 |
+
|
| 403 |
+
# Check if we have a single track (dict with x,y) or a list of tracks
|
| 404 |
+
if tracks_data and isinstance(tracks_data[0], dict) and 'x' in tracks_data[0]:
|
| 405 |
+
# Single track detected, wrap it in a list
|
| 406 |
+
tracks_data = [tracks_data]
|
| 407 |
+
elif tracks_data and isinstance(tracks_data[0], list) and tracks_data[0] and isinstance(tracks_data[0][0], dict) and 'x' in tracks_data[0][0]:
|
| 408 |
+
# Already a list of tracks, nothing to do
|
| 409 |
+
pass
|
| 410 |
+
else:
|
| 411 |
+
# Unexpected format
|
| 412 |
+
pass
|
| 413 |
+
|
| 414 |
+
except json.JSONDecodeError:
|
| 415 |
+
tracks_data = []
|
| 416 |
+
return tracks_data
|
| 417 |
+
|
| 418 |
+
def process_tracks(tracks_np: np.ndarray, frame_size: Tuple[int, int], num_frames, quant_multi: int = 8, **kwargs):
|
| 419 |
+
# tracks: shape [t, h, w, 3] => samples align with 24 fps, model trained with 16 fps.
|
| 420 |
+
# frame_size: tuple (W, H)
|
| 421 |
+
tracks = torch.from_numpy(tracks_np).float()
|
| 422 |
+
|
| 423 |
+
if tracks.shape[1] == 121:
|
| 424 |
+
tracks = torch.permute(tracks, (1, 0, 2, 3))
|
| 425 |
+
|
| 426 |
+
tracks, visibles = tracks[..., :2], tracks[..., 2:3]
|
| 427 |
+
|
| 428 |
+
short_edge = min(*frame_size)
|
| 429 |
+
|
| 430 |
+
frame_center = torch.tensor([*frame_size]).type_as(tracks) / 2
|
| 431 |
+
tracks = tracks - frame_center
|
| 432 |
+
|
| 433 |
+
tracks = tracks / short_edge * 2
|
| 434 |
+
|
| 435 |
+
visibles = visibles * 2 - 1
|
| 436 |
+
|
| 437 |
+
trange = torch.linspace(-1, 1, tracks.shape[0]).view(-1, 1, 1, 1).expand(*visibles.shape)
|
| 438 |
+
|
| 439 |
+
out_ = torch.cat([trange, tracks, visibles], dim=-1).view(121, -1, 4)
|
| 440 |
+
|
| 441 |
+
out_0 = out_[:1]
|
| 442 |
+
|
| 443 |
+
out_l = out_[1:] # 121 => 120 | 1
|
| 444 |
+
a = 120 // math.gcd(120, num_frames)
|
| 445 |
+
b = num_frames // math.gcd(120, num_frames)
|
| 446 |
+
out_l = torch.repeat_interleave(out_l, b, dim=0)[1::a] # 120 => 120 * b => 120 * b / a == F
|
| 447 |
+
|
| 448 |
+
final_result = torch.cat([out_0, out_l], dim=0)
|
| 449 |
+
|
| 450 |
+
return final_result
|
| 451 |
+
|
| 452 |
+
FIXED_LENGTH = 121
|
| 453 |
+
def pad_pts(tr):
|
| 454 |
+
"""Convert list of {x,y} to (FIXED_LENGTH,1,3) array, padding/truncating."""
|
| 455 |
+
pts = np.array([[p['x'], p['y'], 1] for p in tr], dtype=np.float32)
|
| 456 |
+
n = pts.shape[0]
|
| 457 |
+
if n < FIXED_LENGTH:
|
| 458 |
+
pad = np.zeros((FIXED_LENGTH - n, 3), dtype=np.float32)
|
| 459 |
+
pts = np.vstack((pts, pad))
|
| 460 |
+
else:
|
| 461 |
+
pts = pts[:FIXED_LENGTH]
|
| 462 |
+
return pts.reshape(FIXED_LENGTH, 1, 3)
|
| 463 |
+
|
| 464 |
+
def ind_sel(target: torch.Tensor, ind: torch.Tensor, dim: int = 1):
|
| 465 |
+
"""Index selection utility function"""
|
| 466 |
+
assert (
|
| 467 |
+
len(ind.shape) > dim
|
| 468 |
+
), "Index must have the target dim, but get dim: %d, ind shape: %s" % (dim, str(ind.shape))
|
| 469 |
+
|
| 470 |
+
target = target.expand(
|
| 471 |
+
*tuple(
|
| 472 |
+
[ind.shape[k] if target.shape[k] == 1 else -1 for k in range(dim)]
|
| 473 |
+
+ [
|
| 474 |
+
-1,
|
| 475 |
+
]
|
| 476 |
+
* (len(target.shape) - dim)
|
| 477 |
+
)
|
| 478 |
+
)
|
| 479 |
+
|
| 480 |
+
ind_pad = ind
|
| 481 |
+
|
| 482 |
+
if len(target.shape) > dim + 1:
|
| 483 |
+
for _ in range(len(target.shape) - (dim + 1)):
|
| 484 |
+
ind_pad = ind_pad.unsqueeze(-1)
|
| 485 |
+
ind_pad = ind_pad.expand(*(-1,) * (dim + 1), *target.shape[(dim + 1) : :])
|
| 486 |
+
|
| 487 |
+
return torch.gather(target, dim=dim, index=ind_pad)
|
| 488 |
+
|
| 489 |
+
def merge_final(vert_attr: torch.Tensor, weight: torch.Tensor, vert_assign: torch.Tensor):
|
| 490 |
+
"""Merge vertex attributes with weights"""
|
| 491 |
+
target_dim = len(vert_assign.shape) - 1
|
| 492 |
+
if len(vert_attr.shape) == 2:
|
| 493 |
+
assert vert_attr.shape[0] > vert_assign.max()
|
| 494 |
+
new_shape = [1] * target_dim + list(vert_attr.shape)
|
| 495 |
+
tensor = vert_attr.reshape(new_shape)
|
| 496 |
+
sel_attr = ind_sel(tensor, vert_assign.type(torch.long), dim=target_dim)
|
| 497 |
+
else:
|
| 498 |
+
assert vert_attr.shape[1] > vert_assign.max()
|
| 499 |
+
new_shape = [vert_attr.shape[0]] + [1] * (target_dim - 1) + list(vert_attr.shape[1:])
|
| 500 |
+
tensor = vert_attr.reshape(new_shape)
|
| 501 |
+
sel_attr = ind_sel(tensor, vert_assign.type(torch.long), dim=target_dim)
|
| 502 |
+
|
| 503 |
+
final_attr = torch.sum(sel_attr * weight.unsqueeze(-1), dim=-2)
|
| 504 |
+
return final_attr
|
| 505 |
+
|
| 506 |
+
|
| 507 |
+
def _patch_motion_single(
|
| 508 |
+
tracks: torch.FloatTensor, # (B, T, N, 4)
|
| 509 |
+
vid: torch.FloatTensor, # (C, T, H, W)
|
| 510 |
+
temperature: float,
|
| 511 |
+
vae_divide: tuple,
|
| 512 |
+
topk: int,
|
| 513 |
+
):
|
| 514 |
+
"""Apply motion patching based on tracks"""
|
| 515 |
+
_, T, H, W = vid.shape
|
| 516 |
+
N = tracks.shape[2]
|
| 517 |
+
_, tracks_xy, visible = torch.split(
|
| 518 |
+
tracks, [1, 2, 1], dim=-1
|
| 519 |
+
) # (B, T, N, 2) | (B, T, N, 1)
|
| 520 |
+
tracks_n = tracks_xy / torch.tensor([W / min(H, W), H / min(H, W)], device=tracks_xy.device)
|
| 521 |
+
tracks_n = tracks_n.clamp(-1, 1)
|
| 522 |
+
visible = visible.clamp(0, 1)
|
| 523 |
+
|
| 524 |
+
xx = torch.linspace(-W / min(H, W), W / min(H, W), W)
|
| 525 |
+
yy = torch.linspace(-H / min(H, W), H / min(H, W), H)
|
| 526 |
+
|
| 527 |
+
grid = torch.stack(torch.meshgrid(yy, xx, indexing="ij")[::-1], dim=-1).to(
|
| 528 |
+
tracks_xy.device
|
| 529 |
+
)
|
| 530 |
+
|
| 531 |
+
tracks_pad = tracks_xy[:, 1:]
|
| 532 |
+
visible_pad = visible[:, 1:]
|
| 533 |
+
|
| 534 |
+
visible_align = visible_pad.view(T - 1, 4, *visible_pad.shape[2:]).sum(1)
|
| 535 |
+
tracks_align = (tracks_pad * visible_pad).view(T - 1, 4, *tracks_pad.shape[2:]).sum(
|
| 536 |
+
1
|
| 537 |
+
) / (visible_align + 1e-5)
|
| 538 |
+
dist_ = (
|
| 539 |
+
(tracks_align[:, None, None] - grid[None, :, :, None]).pow(2).sum(-1)
|
| 540 |
+
) # T, H, W, N
|
| 541 |
+
weight = torch.exp(-dist_ * temperature) * visible_align.clamp(0, 1).view(
|
| 542 |
+
T - 1, 1, 1, N
|
| 543 |
+
)
|
| 544 |
+
vert_weight, vert_index = torch.topk(
|
| 545 |
+
weight, k=min(topk, weight.shape[-1]), dim=-1
|
| 546 |
+
)
|
| 547 |
+
|
| 548 |
+
grid_mode = "bilinear"
|
| 549 |
+
point_feature = torch.nn.functional.grid_sample(
|
| 550 |
+
vid.permute(1, 0, 2, 3)[:1],
|
| 551 |
+
tracks_n[:, :1].type(vid.dtype),
|
| 552 |
+
mode=grid_mode,
|
| 553 |
+
padding_mode="zeros",
|
| 554 |
+
align_corners=False,
|
| 555 |
+
)
|
| 556 |
+
point_feature = point_feature.squeeze(0).squeeze(1).permute(1, 0) # N, C=16
|
| 557 |
+
|
| 558 |
+
out_feature = merge_final(point_feature, vert_weight, vert_index).permute(3, 0, 1, 2) # T - 1, H, W, C => C, T - 1, H, W
|
| 559 |
+
out_weight = vert_weight.sum(-1) # T - 1, H, W
|
| 560 |
+
|
| 561 |
+
# out feature -> already soft weighted
|
| 562 |
+
mix_feature = out_feature + vid[:, 1:] * (1 - out_weight.clamp(0, 1))
|
| 563 |
+
|
| 564 |
+
out_feature_full = torch.cat([vid[:, :1], mix_feature], dim=1) # C, T, H, W
|
| 565 |
+
out_mask_full = torch.cat([torch.ones_like(out_weight[:1]), out_weight], dim=0) # T, H, W
|
| 566 |
+
|
| 567 |
+
return out_mask_full[None].expand(vae_divide[0], -1, -1, -1), out_feature_full
|
| 568 |
+
|
| 569 |
+
|
| 570 |
+
def patch_motion(
|
| 571 |
+
tracks: torch.FloatTensor, # (B, TB, T, N, 4)
|
| 572 |
+
vid: torch.FloatTensor, # (C, T, H, W)
|
| 573 |
+
temperature: float = 220.0,
|
| 574 |
+
vae_divide: tuple = (4, 16),
|
| 575 |
+
topk: int = 2,
|
| 576 |
+
):
|
| 577 |
+
B = len(tracks)
|
| 578 |
+
|
| 579 |
+
# Process each batch separately
|
| 580 |
+
out_masks = []
|
| 581 |
+
out_features = []
|
| 582 |
+
|
| 583 |
+
for b in range(B):
|
| 584 |
+
mask, feature = _patch_motion_single(
|
| 585 |
+
tracks[b], # (T, N, 4)
|
| 586 |
+
vid[b], # (C, T, H, W)
|
| 587 |
+
temperature,
|
| 588 |
+
vae_divide,
|
| 589 |
+
topk
|
| 590 |
+
)
|
| 591 |
+
out_masks.append(mask)
|
| 592 |
+
out_features.append(feature)
|
| 593 |
+
|
| 594 |
+
# Stack results: (B, C, T, H, W)
|
| 595 |
+
out_mask_full = torch.stack(out_masks, dim=0)
|
| 596 |
+
out_feature_full = torch.stack(out_features, dim=0)
|
| 597 |
+
|
| 598 |
+
return out_mask_full, out_feature_full
|
| 599 |
+
|
| 600 |
+
class WanTrackToVideo:
|
| 601 |
+
@classmethod
|
| 602 |
+
def INPUT_TYPES(s):
|
| 603 |
+
return {"required": {
|
| 604 |
+
"positive": ("CONDITIONING", ),
|
| 605 |
+
"negative": ("CONDITIONING", ),
|
| 606 |
+
"vae": ("VAE", ),
|
| 607 |
+
"tracks": ("STRING", {"multiline": True, "default": "[]"}),
|
| 608 |
+
"width": ("INT", {"default": 832, "min": 16, "max": nodes.MAX_RESOLUTION, "step": 16}),
|
| 609 |
+
"height": ("INT", {"default": 480, "min": 16, "max": nodes.MAX_RESOLUTION, "step": 16}),
|
| 610 |
+
"length": ("INT", {"default": 81, "min": 1, "max": nodes.MAX_RESOLUTION, "step": 4}),
|
| 611 |
+
"batch_size": ("INT", {"default": 1, "min": 1, "max": 4096}),
|
| 612 |
+
"temperature": ("FLOAT", {"default": 220.0, "min": 1.0, "max": 1000.0, "step": 0.1}),
|
| 613 |
+
"topk": ("INT", {"default": 2, "min": 1, "max": 10}),
|
| 614 |
+
"start_image": ("IMAGE", ),
|
| 615 |
+
},
|
| 616 |
+
"optional": {
|
| 617 |
+
"clip_vision_output": ("CLIP_VISION_OUTPUT", ),
|
| 618 |
+
}}
|
| 619 |
+
|
| 620 |
+
RETURN_TYPES = ("CONDITIONING", "CONDITIONING", "LATENT")
|
| 621 |
+
RETURN_NAMES = ("positive", "negative", "latent")
|
| 622 |
+
FUNCTION = "encode"
|
| 623 |
+
|
| 624 |
+
CATEGORY = "conditioning/video_models"
|
| 625 |
+
|
| 626 |
+
def encode(self, positive, negative, vae, tracks, width, height, length, batch_size,
|
| 627 |
+
temperature, topk, start_image=None, clip_vision_output=None):
|
| 628 |
+
|
| 629 |
+
tracks_data = parse_json_tracks(tracks)
|
| 630 |
+
|
| 631 |
+
if not tracks_data:
|
| 632 |
+
return WanImageToVideo().encode(positive, negative, vae, width, height, length, batch_size, start_image=start_image, clip_vision_output=clip_vision_output)
|
| 633 |
+
|
| 634 |
+
latent = torch.zeros([batch_size, 16, ((length - 1) // 4) + 1, height // 8, width // 8],
|
| 635 |
+
device=comfy.model_management.intermediate_device())
|
| 636 |
+
|
| 637 |
+
if isinstance(tracks_data[0][0], dict):
|
| 638 |
+
tracks_data = [tracks_data]
|
| 639 |
+
|
| 640 |
+
processed_tracks = []
|
| 641 |
+
for batch in tracks_data:
|
| 642 |
+
arrs = []
|
| 643 |
+
for track in batch:
|
| 644 |
+
pts = pad_pts(track)
|
| 645 |
+
arrs.append(pts)
|
| 646 |
+
|
| 647 |
+
tracks_np = np.stack(arrs, axis=0)
|
| 648 |
+
processed_tracks.append(process_tracks(tracks_np, (width, height), length - 1).unsqueeze(0))
|
| 649 |
+
|
| 650 |
+
if start_image is not None:
|
| 651 |
+
start_image = comfy.utils.common_upscale(start_image[:batch_size].movedim(-1, 1), width, height, "bilinear", "center").movedim(1, -1)
|
| 652 |
+
videos = torch.ones((start_image.shape[0], length, height, width, start_image.shape[-1]), device=start_image.device, dtype=start_image.dtype) * 0.5
|
| 653 |
+
for i in range(start_image.shape[0]):
|
| 654 |
+
videos[i, 0] = start_image[i]
|
| 655 |
+
|
| 656 |
+
latent_videos = []
|
| 657 |
+
videos = comfy.utils.resize_to_batch_size(videos, batch_size)
|
| 658 |
+
for i in range(batch_size):
|
| 659 |
+
latent_videos += [vae.encode(videos[i, :, :, :, :3])]
|
| 660 |
+
y = torch.cat(latent_videos, dim=0)
|
| 661 |
+
|
| 662 |
+
# Scale latent since patch_motion is non-linear
|
| 663 |
+
y = comfy.latent_formats.Wan21().process_in(y)
|
| 664 |
+
|
| 665 |
+
processed_tracks = comfy.utils.resize_list_to_batch_size(processed_tracks, batch_size)
|
| 666 |
+
res = patch_motion(
|
| 667 |
+
processed_tracks, y, temperature=temperature, topk=topk, vae_divide=(4, 16)
|
| 668 |
+
)
|
| 669 |
+
|
| 670 |
+
mask, concat_latent_image = res
|
| 671 |
+
concat_latent_image = comfy.latent_formats.Wan21().process_out(concat_latent_image)
|
| 672 |
+
mask = -mask + 1.0 # Invert mask to match expected format
|
| 673 |
+
positive = node_helpers.conditioning_set_values(positive,
|
| 674 |
+
{"concat_mask": mask,
|
| 675 |
+
"concat_latent_image": concat_latent_image})
|
| 676 |
+
negative = node_helpers.conditioning_set_values(negative,
|
| 677 |
+
{"concat_mask": mask,
|
| 678 |
+
"concat_latent_image": concat_latent_image})
|
| 679 |
+
|
| 680 |
+
if clip_vision_output is not None:
|
| 681 |
+
positive = node_helpers.conditioning_set_values(positive, {"clip_vision_output": clip_vision_output})
|
| 682 |
+
negative = node_helpers.conditioning_set_values(negative, {"clip_vision_output": clip_vision_output})
|
| 683 |
+
|
| 684 |
+
out_latent = {}
|
| 685 |
+
out_latent["samples"] = latent
|
| 686 |
+
return (positive, negative, out_latent)
|
| 687 |
+
|
| 688 |
+
|
| 689 |
+
class Wan22ImageToVideoLatent:
|
| 690 |
+
@classmethod
|
| 691 |
+
def INPUT_TYPES(s):
|
| 692 |
+
return {"required": {"vae": ("VAE", ),
|
| 693 |
+
"width": ("INT", {"default": 1280, "min": 32, "max": nodes.MAX_RESOLUTION, "step": 32}),
|
| 694 |
+
"height": ("INT", {"default": 704, "min": 32, "max": nodes.MAX_RESOLUTION, "step": 32}),
|
| 695 |
+
"length": ("INT", {"default": 49, "min": 1, "max": nodes.MAX_RESOLUTION, "step": 4}),
|
| 696 |
+
"batch_size": ("INT", {"default": 1, "min": 1, "max": 4096}),
|
| 697 |
+
},
|
| 698 |
+
"optional": {"start_image": ("IMAGE", ),
|
| 699 |
+
}}
|
| 700 |
+
|
| 701 |
+
|
| 702 |
+
RETURN_TYPES = ("LATENT",)
|
| 703 |
+
FUNCTION = "encode"
|
| 704 |
+
|
| 705 |
+
CATEGORY = "conditioning/inpaint"
|
| 706 |
+
|
| 707 |
+
def encode(self, vae, width, height, length, batch_size, start_image=None):
|
| 708 |
+
latent = torch.zeros([1, 48, ((length - 1) // 4) + 1, height // 16, width // 16], device=comfy.model_management.intermediate_device())
|
| 709 |
+
|
| 710 |
+
if start_image is None:
|
| 711 |
+
out_latent = {}
|
| 712 |
+
out_latent["samples"] = latent
|
| 713 |
+
return (out_latent,)
|
| 714 |
+
|
| 715 |
+
mask = torch.ones([latent.shape[0], 1, ((length - 1) // 4) + 1, latent.shape[-2], latent.shape[-1]], device=comfy.model_management.intermediate_device())
|
| 716 |
+
|
| 717 |
+
if start_image is not None:
|
| 718 |
+
start_image = comfy.utils.common_upscale(start_image[:length].movedim(-1, 1), width, height, "bilinear", "center").movedim(1, -1)
|
| 719 |
+
latent_temp = vae.encode(start_image)
|
| 720 |
+
latent[:, :, :latent_temp.shape[-3]] = latent_temp
|
| 721 |
+
mask[:, :, :latent_temp.shape[-3]] *= 0.0
|
| 722 |
+
|
| 723 |
+
out_latent = {}
|
| 724 |
+
latent_format = comfy.latent_formats.Wan22()
|
| 725 |
+
latent = latent_format.process_out(latent) * mask + latent * (1.0 - mask)
|
| 726 |
+
out_latent["samples"] = latent.repeat((batch_size, ) + (1,) * (latent.ndim - 1))
|
| 727 |
+
out_latent["noise_mask"] = mask.repeat((batch_size, ) + (1,) * (mask.ndim - 1))
|
| 728 |
+
return (out_latent,)
|
| 729 |
+
|
| 730 |
+
|
| 731 |
+
NODE_CLASS_MAPPINGS = {
|
| 732 |
+
"WanTrackToVideo": WanTrackToVideo,
|
| 733 |
+
"WanImageToVideo": WanImageToVideo,
|
| 734 |
+
"WanFunControlToVideo": WanFunControlToVideo,
|
| 735 |
+
"WanFunInpaintToVideo": WanFunInpaintToVideo,
|
| 736 |
+
"WanFirstLastFrameToVideo": WanFirstLastFrameToVideo,
|
| 737 |
+
"WanVaceToVideo": WanVaceToVideo,
|
| 738 |
+
"TrimVideoLatent": TrimVideoLatent,
|
| 739 |
+
"WanCameraImageToVideo": WanCameraImageToVideo,
|
| 740 |
+
"WanPhantomSubjectToVideo": WanPhantomSubjectToVideo,
|
| 741 |
+
"Wan22ImageToVideoLatent": Wan22ImageToVideoLatent,
|
| 742 |
+
}
|
ComfyUI/custom_nodes/comfyui-kjnodes/web/js/jsnodes.js
ADDED
|
@@ -0,0 +1,413 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { app } from "../../../scripts/app.js";
|
| 2 |
+
import { applyTextReplacements } from "../../../scripts/utils.js";
|
| 3 |
+
|
| 4 |
+
app.registerExtension({
|
| 5 |
+
name: "KJNodes.jsnodes",
|
| 6 |
+
async beforeRegisterNodeDef(nodeType, nodeData, app) {
|
| 7 |
+
if(!nodeData?.category?.startsWith("KJNodes")) {
|
| 8 |
+
return;
|
| 9 |
+
}
|
| 10 |
+
switch (nodeData.name) {
|
| 11 |
+
case "ConditioningMultiCombine":
|
| 12 |
+
nodeType.prototype.onNodeCreated = function () {
|
| 13 |
+
this._type = "CONDITIONING"
|
| 14 |
+
this.inputs_offset = nodeData.name.includes("selective")?1:0
|
| 15 |
+
this.addWidget("button", "Update inputs", null, () => {
|
| 16 |
+
if (!this.inputs) {
|
| 17 |
+
this.inputs = [];
|
| 18 |
+
}
|
| 19 |
+
const target_number_of_inputs = this.widgets.find(w => w.name === "inputcount")["value"];
|
| 20 |
+
const num_inputs = this.inputs.filter(input => input.type === this._type).length
|
| 21 |
+
if(target_number_of_inputs===num_inputs)return; // already set, do nothing
|
| 22 |
+
|
| 23 |
+
if(target_number_of_inputs < num_inputs){
|
| 24 |
+
const inputs_to_remove = num_inputs - target_number_of_inputs;
|
| 25 |
+
for(let i = 0; i < inputs_to_remove; i++) {
|
| 26 |
+
this.removeInput(this.inputs.length - 1);
|
| 27 |
+
}
|
| 28 |
+
}
|
| 29 |
+
else{
|
| 30 |
+
for(let i = num_inputs+1; i <= target_number_of_inputs; ++i)
|
| 31 |
+
this.addInput(`conditioning_${i}`, this._type)
|
| 32 |
+
}
|
| 33 |
+
});
|
| 34 |
+
}
|
| 35 |
+
break;
|
| 36 |
+
case "ImageBatchMulti":
|
| 37 |
+
case "ImageAddMulti":
|
| 38 |
+
case "ImageConcatMulti":
|
| 39 |
+
case "CrossFadeImagesMulti":
|
| 40 |
+
case "TransitionImagesMulti":
|
| 41 |
+
nodeType.prototype.onNodeCreated = function () {
|
| 42 |
+
this._type = "IMAGE"
|
| 43 |
+
this.addWidget("button", "Update inputs", null, () => {
|
| 44 |
+
if (!this.inputs) {
|
| 45 |
+
this.inputs = [];
|
| 46 |
+
}
|
| 47 |
+
const target_number_of_inputs = this.widgets.find(w => w.name === "inputcount")["value"];
|
| 48 |
+
const num_inputs = this.inputs.filter(input => input.type === this._type).length
|
| 49 |
+
if(target_number_of_inputs===num_inputs)return; // already set, do nothing
|
| 50 |
+
|
| 51 |
+
if(target_number_of_inputs < num_inputs){
|
| 52 |
+
const inputs_to_remove = num_inputs - target_number_of_inputs;
|
| 53 |
+
for(let i = 0; i < inputs_to_remove; i++) {
|
| 54 |
+
this.removeInput(this.inputs.length - 1);
|
| 55 |
+
}
|
| 56 |
+
}
|
| 57 |
+
else{
|
| 58 |
+
for(let i = num_inputs+1; i <= target_number_of_inputs; ++i)
|
| 59 |
+
this.addInput(`image_${i}`, this._type, {shape: 7});
|
| 60 |
+
}
|
| 61 |
+
|
| 62 |
+
});
|
| 63 |
+
}
|
| 64 |
+
break;
|
| 65 |
+
case "MaskBatchMulti":
|
| 66 |
+
nodeType.prototype.onNodeCreated = function () {
|
| 67 |
+
this._type = "MASK"
|
| 68 |
+
this.addWidget("button", "Update inputs", null, () => {
|
| 69 |
+
if (!this.inputs) {
|
| 70 |
+
this.inputs = [];
|
| 71 |
+
}
|
| 72 |
+
const target_number_of_inputs = this.widgets.find(w => w.name === "inputcount")["value"];
|
| 73 |
+
const num_inputs = this.inputs.filter(input => input.type === this._type).length
|
| 74 |
+
if(target_number_of_inputs===num_inputs)return; // already set, do nothing
|
| 75 |
+
|
| 76 |
+
if(target_number_of_inputs < num_inputs){
|
| 77 |
+
const inputs_to_remove = num_inputs - target_number_of_inputs;
|
| 78 |
+
for(let i = 0; i < inputs_to_remove; i++) {
|
| 79 |
+
this.removeInput(this.inputs.length - 1);
|
| 80 |
+
}
|
| 81 |
+
}
|
| 82 |
+
else{
|
| 83 |
+
for(let i = num_inputs+1; i <= target_number_of_inputs; ++i)
|
| 84 |
+
this.addInput(`mask_${i}`, this._type)
|
| 85 |
+
}
|
| 86 |
+
});
|
| 87 |
+
}
|
| 88 |
+
break;
|
| 89 |
+
|
| 90 |
+
case "FluxBlockLoraSelect":
|
| 91 |
+
case "HunyuanVideoBlockLoraSelect":
|
| 92 |
+
case "Wan21BlockLoraSelect":
|
| 93 |
+
nodeType.prototype.onNodeCreated = function () {
|
| 94 |
+
this.addWidget("button", "Set all", null, () => {
|
| 95 |
+
const userInput = prompt("Enter the values to set for widgets (e.g., s0,1,2-7=2.0, d0,1,2-7=2.0, or 1.0):", "");
|
| 96 |
+
if (userInput) {
|
| 97 |
+
const regex = /([sd])?(\d+(?:,\d+|-?\d+)*?)?=(\d+(\.\d+)?)/;
|
| 98 |
+
const match = userInput.match(regex);
|
| 99 |
+
if (match) {
|
| 100 |
+
const type = match[1];
|
| 101 |
+
const indicesPart = match[2];
|
| 102 |
+
const value = parseFloat(match[3]);
|
| 103 |
+
|
| 104 |
+
let targetWidgets = [];
|
| 105 |
+
if (type === 's') {
|
| 106 |
+
targetWidgets = this.widgets.filter(widget => widget.name.includes("single"));
|
| 107 |
+
} else if (type === 'd') {
|
| 108 |
+
targetWidgets = this.widgets.filter(widget => widget.name.includes("double"));
|
| 109 |
+
} else {
|
| 110 |
+
targetWidgets = this.widgets; // No type specified, all widgets
|
| 111 |
+
}
|
| 112 |
+
|
| 113 |
+
if (indicesPart) {
|
| 114 |
+
const indices = indicesPart.split(',').flatMap(part => {
|
| 115 |
+
if (part.includes('-')) {
|
| 116 |
+
const [start, end] = part.split('-').map(Number);
|
| 117 |
+
return Array.from({ length: end - start + 1 }, (_, i) => start + i);
|
| 118 |
+
}
|
| 119 |
+
return Number(part);
|
| 120 |
+
});
|
| 121 |
+
|
| 122 |
+
for (const index of indices) {
|
| 123 |
+
if (index < targetWidgets.length) {
|
| 124 |
+
targetWidgets[index].value = value;
|
| 125 |
+
}
|
| 126 |
+
}
|
| 127 |
+
} else {
|
| 128 |
+
// No indices provided, set value for all target widgets
|
| 129 |
+
for (const widget of targetWidgets) {
|
| 130 |
+
widget.value = value;
|
| 131 |
+
}
|
| 132 |
+
}
|
| 133 |
+
} else if (!isNaN(parseFloat(userInput))) {
|
| 134 |
+
// Single value provided, set it for all widgets
|
| 135 |
+
const value = parseFloat(userInput);
|
| 136 |
+
for (const widget of this.widgets) {
|
| 137 |
+
widget.value = value;
|
| 138 |
+
}
|
| 139 |
+
} else {
|
| 140 |
+
alert("Invalid input format. Please use the format s0,1,2-7=2.0, d0,1,2-7=2.0, or 1.0");
|
| 141 |
+
}
|
| 142 |
+
} else {
|
| 143 |
+
alert("Invalid input. Please enter a value.");
|
| 144 |
+
}
|
| 145 |
+
});
|
| 146 |
+
};
|
| 147 |
+
break;
|
| 148 |
+
|
| 149 |
+
case "GetMaskSizeAndCount":
|
| 150 |
+
const onGetMaskSizeConnectInput = nodeType.prototype.onConnectInput;
|
| 151 |
+
nodeType.prototype.onConnectInput = function (targetSlot, type, output, originNode, originSlot) {
|
| 152 |
+
const v = onGetMaskSizeConnectInput? onGetMaskSizeConnectInput.apply(this, arguments): undefined
|
| 153 |
+
this.outputs[1]["label"] = "width"
|
| 154 |
+
this.outputs[2]["label"] = "height"
|
| 155 |
+
this.outputs[3]["label"] = "count"
|
| 156 |
+
return v;
|
| 157 |
+
}
|
| 158 |
+
const onGetMaskSizeExecuted = nodeType.prototype.onAfterExecuteNode;
|
| 159 |
+
nodeType.prototype.onExecuted = function(message) {
|
| 160 |
+
const r = onGetMaskSizeExecuted? onGetMaskSizeExecuted.apply(this,arguments): undefined
|
| 161 |
+
let values = message["text"].toString().split('x').map(Number);
|
| 162 |
+
this.outputs[1]["label"] = values[1] + " width"
|
| 163 |
+
this.outputs[2]["label"] = values[2] + " height"
|
| 164 |
+
this.outputs[3]["label"] = values[0] + " count"
|
| 165 |
+
return r
|
| 166 |
+
}
|
| 167 |
+
break;
|
| 168 |
+
|
| 169 |
+
case "GetImageSizeAndCount":
|
| 170 |
+
const onGetImageSizeConnectInput = nodeType.prototype.onConnectInput;
|
| 171 |
+
nodeType.prototype.onConnectInput = function (targetSlot, type, output, originNode, originSlot) {
|
| 172 |
+
console.log(this)
|
| 173 |
+
const v = onGetImageSizeConnectInput? onGetImageSizeConnectInput.apply(this, arguments): undefined
|
| 174 |
+
//console.log(this)
|
| 175 |
+
this.outputs[1]["label"] = "width"
|
| 176 |
+
this.outputs[2]["label"] = "height"
|
| 177 |
+
this.outputs[3]["label"] = "count"
|
| 178 |
+
return v;
|
| 179 |
+
}
|
| 180 |
+
//const onGetImageSizeExecuted = nodeType.prototype.onExecuted;
|
| 181 |
+
const onGetImageSizeExecuted = nodeType.prototype.onAfterExecuteNode;
|
| 182 |
+
nodeType.prototype.onExecuted = function(message) {
|
| 183 |
+
console.log(this)
|
| 184 |
+
const r = onGetImageSizeExecuted? onGetImageSizeExecuted.apply(this,arguments): undefined
|
| 185 |
+
let values = message["text"].toString().split('x').map(Number);
|
| 186 |
+
console.log(values)
|
| 187 |
+
this.outputs[1]["label"] = values[1] + " width"
|
| 188 |
+
this.outputs[2]["label"] = values[2] + " height"
|
| 189 |
+
this.outputs[3]["label"] = values[0] + " count"
|
| 190 |
+
return r
|
| 191 |
+
}
|
| 192 |
+
break;
|
| 193 |
+
|
| 194 |
+
case "GetLatentSizeAndCount":
|
| 195 |
+
const onGetLatentConnectInput = nodeType.prototype.onConnectInput;
|
| 196 |
+
nodeType.prototype.onConnectInput = function (targetSlot, type, output, originNode, originSlot) {
|
| 197 |
+
console.log(this)
|
| 198 |
+
const v = onGetLatentConnectInput? onGetLatentConnectInput.apply(this, arguments): undefined
|
| 199 |
+
//console.log(this)
|
| 200 |
+
this.outputs[1]["label"] = "width"
|
| 201 |
+
this.outputs[2]["label"] = "height"
|
| 202 |
+
this.outputs[3]["label"] = "count"
|
| 203 |
+
return v;
|
| 204 |
+
}
|
| 205 |
+
//const onGetImageSizeExecuted = nodeType.prototype.onExecuted;
|
| 206 |
+
const onGetLatentSizeExecuted = nodeType.prototype.onAfterExecuteNode;
|
| 207 |
+
nodeType.prototype.onExecuted = function(message) {
|
| 208 |
+
console.log(this)
|
| 209 |
+
const r = onGetLatentSizeExecuted? onGetLatentSizeExecuted.apply(this,arguments): undefined
|
| 210 |
+
let values = message["text"].toString().split('x').map(Number);
|
| 211 |
+
console.log(values)
|
| 212 |
+
this.outputs[1]["label"] = values[0] + " batch"
|
| 213 |
+
this.outputs[2]["label"] = values[1] + " channels"
|
| 214 |
+
this.outputs[3]["label"] = values[2] + " frames"
|
| 215 |
+
this.outputs[4]["label"] = values[3] + " height"
|
| 216 |
+
this.outputs[5]["label"] = values[4] + " width"
|
| 217 |
+
return r
|
| 218 |
+
}
|
| 219 |
+
break;
|
| 220 |
+
|
| 221 |
+
case "PreviewAnimation":
|
| 222 |
+
const onPreviewAnimationConnectInput = nodeType.prototype.onConnectInput;
|
| 223 |
+
nodeType.prototype.onConnectInput = function (targetSlot, type, output, originNode, originSlot) {
|
| 224 |
+
const v = onPreviewAnimationConnectInput? onPreviewAnimationConnectInput.apply(this, arguments): undefined
|
| 225 |
+
this.title = "Preview Animation"
|
| 226 |
+
return v;
|
| 227 |
+
}
|
| 228 |
+
const onPreviewAnimationExecuted = nodeType.prototype.onAfterExecuteNode;
|
| 229 |
+
nodeType.prototype.onExecuted = function(message) {
|
| 230 |
+
const r = onPreviewAnimationExecuted? onPreviewAnimationExecuted.apply(this,arguments): undefined
|
| 231 |
+
let values = message["text"].toString();
|
| 232 |
+
this.title = "Preview Animation " + values
|
| 233 |
+
return r
|
| 234 |
+
}
|
| 235 |
+
break;
|
| 236 |
+
|
| 237 |
+
case "VRAM_Debug":
|
| 238 |
+
const onVRAM_DebugConnectInput = nodeType.prototype.onConnectInput;
|
| 239 |
+
nodeType.prototype.onConnectInput = function (targetSlot, type, output, originNode, originSlot) {
|
| 240 |
+
const v = onVRAM_DebugConnectInput? onVRAM_DebugConnectInput.apply(this, arguments): undefined
|
| 241 |
+
this.outputs[3]["label"] = "freemem_before"
|
| 242 |
+
this.outputs[4]["label"] = "freemem_after"
|
| 243 |
+
return v;
|
| 244 |
+
}
|
| 245 |
+
const onVRAM_DebugExecuted = nodeType.prototype.onAfterExecuteNode;
|
| 246 |
+
nodeType.prototype.onExecuted = function(message) {
|
| 247 |
+
const r = onVRAM_DebugExecuted? onVRAM_DebugExecuted.apply(this,arguments): undefined
|
| 248 |
+
let values = message["text"].toString().split('x');
|
| 249 |
+
this.outputs[3]["label"] = values[0] + " freemem_before"
|
| 250 |
+
this.outputs[4]["label"] = values[1] + " freemem_after"
|
| 251 |
+
return r
|
| 252 |
+
}
|
| 253 |
+
break;
|
| 254 |
+
|
| 255 |
+
case "JoinStringMulti":
|
| 256 |
+
const originalOnNodeCreated = nodeType.prototype.onNodeCreated || function() {};
|
| 257 |
+
nodeType.prototype.onNodeCreated = function () {
|
| 258 |
+
originalOnNodeCreated.apply(this, arguments);
|
| 259 |
+
|
| 260 |
+
this._type = "STRING";
|
| 261 |
+
this.addWidget("button", "Update inputs", null, () => {
|
| 262 |
+
if (!this.inputs) {
|
| 263 |
+
this.inputs = [];
|
| 264 |
+
}
|
| 265 |
+
const target_number_of_inputs = this.widgets.find(w => w.name === "inputcount")["value"];
|
| 266 |
+
const num_inputs = this.inputs.filter(input => input.name && input.name.toLowerCase().includes("string_")).length
|
| 267 |
+
if (target_number_of_inputs === num_inputs) return; // already set, do nothing
|
| 268 |
+
|
| 269 |
+
if(target_number_of_inputs < num_inputs){
|
| 270 |
+
const inputs_to_remove = num_inputs - target_number_of_inputs;
|
| 271 |
+
for(let i = 0; i < inputs_to_remove; i++) {
|
| 272 |
+
this.removeInput(this.inputs.length - 1);
|
| 273 |
+
}
|
| 274 |
+
}
|
| 275 |
+
else{
|
| 276 |
+
for(let i = num_inputs+1; i <= target_number_of_inputs; ++i)
|
| 277 |
+
this.addInput(`string_${i}`, this._type, {shape: 7});
|
| 278 |
+
}
|
| 279 |
+
});
|
| 280 |
+
}
|
| 281 |
+
break;
|
| 282 |
+
case "SoundReactive":
|
| 283 |
+
nodeType.prototype.onNodeCreated = function () {
|
| 284 |
+
let audioContext;
|
| 285 |
+
let microphoneStream;
|
| 286 |
+
let animationFrameId;
|
| 287 |
+
let analyser;
|
| 288 |
+
let dataArray;
|
| 289 |
+
let startRangeHz;
|
| 290 |
+
let endRangeHz;
|
| 291 |
+
let smoothingFactor = 0.5;
|
| 292 |
+
let smoothedSoundLevel = 0;
|
| 293 |
+
|
| 294 |
+
// Function to update the widget value in real-time
|
| 295 |
+
const updateWidgetValueInRealTime = () => {
|
| 296 |
+
// Ensure analyser and dataArray are defined before using them
|
| 297 |
+
if (analyser && dataArray) {
|
| 298 |
+
analyser.getByteFrequencyData(dataArray);
|
| 299 |
+
|
| 300 |
+
const startRangeHzWidget = this.widgets.find(w => w.name === "start_range_hz");
|
| 301 |
+
if (startRangeHzWidget) startRangeHz = startRangeHzWidget.value;
|
| 302 |
+
const endRangeHzWidget = this.widgets.find(w => w.name === "end_range_hz");
|
| 303 |
+
if (endRangeHzWidget) endRangeHz = endRangeHzWidget.value;
|
| 304 |
+
const smoothingFactorWidget = this.widgets.find(w => w.name === "smoothing_factor");
|
| 305 |
+
if (smoothingFactorWidget) smoothingFactor = smoothingFactorWidget.value;
|
| 306 |
+
|
| 307 |
+
// Calculate frequency bin width (frequency resolution)
|
| 308 |
+
const frequencyBinWidth = audioContext.sampleRate / analyser.fftSize;
|
| 309 |
+
// Convert the widget values from Hz to indices
|
| 310 |
+
const startRangeIndex = Math.floor(startRangeHz / frequencyBinWidth);
|
| 311 |
+
const endRangeIndex = Math.floor(endRangeHz / frequencyBinWidth);
|
| 312 |
+
|
| 313 |
+
// Function to calculate the average value for a frequency range
|
| 314 |
+
const calculateAverage = (start, end) => {
|
| 315 |
+
const sum = dataArray.slice(start, end).reduce((acc, val) => acc + val, 0);
|
| 316 |
+
const average = sum / (end - start);
|
| 317 |
+
|
| 318 |
+
// Apply exponential moving average smoothing
|
| 319 |
+
smoothedSoundLevel = (average * (1 - smoothingFactor)) + (smoothedSoundLevel * smoothingFactor);
|
| 320 |
+
return smoothedSoundLevel;
|
| 321 |
+
};
|
| 322 |
+
// Calculate the average levels for each frequency range
|
| 323 |
+
const soundLevel = calculateAverage(startRangeIndex, endRangeIndex);
|
| 324 |
+
|
| 325 |
+
// Update the widget values
|
| 326 |
+
|
| 327 |
+
const lowLevelWidget = this.widgets.find(w => w.name === "sound_level");
|
| 328 |
+
if (lowLevelWidget) lowLevelWidget.value = soundLevel;
|
| 329 |
+
|
| 330 |
+
animationFrameId = requestAnimationFrame(updateWidgetValueInRealTime);
|
| 331 |
+
}
|
| 332 |
+
};
|
| 333 |
+
|
| 334 |
+
// Function to start capturing audio from the microphone
|
| 335 |
+
const startMicrophoneCapture = () => {
|
| 336 |
+
// Only create the audio context and analyser once
|
| 337 |
+
if (!audioContext) {
|
| 338 |
+
audioContext = new (window.AudioContext || window.webkitAudioContext)();
|
| 339 |
+
// Access the sample rate of the audio context
|
| 340 |
+
console.log(`Sample rate: ${audioContext.sampleRate}Hz`);
|
| 341 |
+
analyser = audioContext.createAnalyser();
|
| 342 |
+
analyser.fftSize = 2048;
|
| 343 |
+
dataArray = new Uint8Array(analyser.frequencyBinCount);
|
| 344 |
+
// Get the range values from widgets (assumed to be in Hz)
|
| 345 |
+
const lowRangeWidget = this.widgets.find(w => w.name === "low_range_hz");
|
| 346 |
+
if (lowRangeWidget) startRangeHz = lowRangeWidget.value;
|
| 347 |
+
|
| 348 |
+
const midRangeWidget = this.widgets.find(w => w.name === "mid_range_hz");
|
| 349 |
+
if (midRangeWidget) endRangeHz = midRangeWidget.value;
|
| 350 |
+
}
|
| 351 |
+
|
| 352 |
+
navigator.mediaDevices.getUserMedia({ audio: true }).then(stream => {
|
| 353 |
+
microphoneStream = stream;
|
| 354 |
+
const microphone = audioContext.createMediaStreamSource(stream);
|
| 355 |
+
microphone.connect(analyser);
|
| 356 |
+
updateWidgetValueInRealTime();
|
| 357 |
+
}).catch(error => {
|
| 358 |
+
console.error('Access to microphone was denied or an error occurred:', error);
|
| 359 |
+
});
|
| 360 |
+
};
|
| 361 |
+
|
| 362 |
+
// Function to stop capturing audio from the microphone
|
| 363 |
+
const stopMicrophoneCapture = () => {
|
| 364 |
+
if (animationFrameId) {
|
| 365 |
+
cancelAnimationFrame(animationFrameId);
|
| 366 |
+
}
|
| 367 |
+
if (microphoneStream) {
|
| 368 |
+
microphoneStream.getTracks().forEach(track => track.stop());
|
| 369 |
+
}
|
| 370 |
+
if (audioContext) {
|
| 371 |
+
audioContext.close();
|
| 372 |
+
// Reset audioContext to ensure it can be created again when starting
|
| 373 |
+
audioContext = null;
|
| 374 |
+
}
|
| 375 |
+
};
|
| 376 |
+
|
| 377 |
+
// Add start button
|
| 378 |
+
this.addWidget("button", "Start mic capture", null, startMicrophoneCapture);
|
| 379 |
+
|
| 380 |
+
// Add stop button
|
| 381 |
+
this.addWidget("button", "Stop mic capture", null, stopMicrophoneCapture);
|
| 382 |
+
};
|
| 383 |
+
break;
|
| 384 |
+
case "SaveImageKJ":
|
| 385 |
+
const onNodeCreated = nodeType.prototype.onNodeCreated;
|
| 386 |
+
nodeType.prototype.onNodeCreated = function() {
|
| 387 |
+
const r = onNodeCreated ? onNodeCreated.apply(this, arguments) : void 0;
|
| 388 |
+
const widget = this.widgets.find((w) => w.name === "filename_prefix");
|
| 389 |
+
widget.serializeValue = () => {
|
| 390 |
+
return applyTextReplacements(app, widget.value);
|
| 391 |
+
};
|
| 392 |
+
return r;
|
| 393 |
+
};
|
| 394 |
+
break;
|
| 395 |
+
|
| 396 |
+
}
|
| 397 |
+
|
| 398 |
+
},
|
| 399 |
+
async setup() {
|
| 400 |
+
// to keep Set/Get node virtual connections visible when offscreen
|
| 401 |
+
const originalComputeVisibleNodes = LGraphCanvas.prototype.computeVisibleNodes;
|
| 402 |
+
LGraphCanvas.prototype.computeVisibleNodes = function () {
|
| 403 |
+
const visibleNodesSet = new Set(originalComputeVisibleNodes.apply(this, arguments));
|
| 404 |
+
for (const node of this.graph._nodes) {
|
| 405 |
+
if ((node.type === "SetNode" || node.type === "GetNode") && node.drawConnection) {
|
| 406 |
+
visibleNodesSet.add(node);
|
| 407 |
+
}
|
| 408 |
+
}
|
| 409 |
+
return Array.from(visibleNodesSet);
|
| 410 |
+
};
|
| 411 |
+
|
| 412 |
+
}
|
| 413 |
+
});
|
ComfyUI/models/configs/anything_v3.yaml
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
model:
|
| 2 |
+
base_learning_rate: 1.0e-04
|
| 3 |
+
target: ldm.models.diffusion.ddpm.LatentDiffusion
|
| 4 |
+
params:
|
| 5 |
+
linear_start: 0.00085
|
| 6 |
+
linear_end: 0.0120
|
| 7 |
+
num_timesteps_cond: 1
|
| 8 |
+
log_every_t: 200
|
| 9 |
+
timesteps: 1000
|
| 10 |
+
first_stage_key: "jpg"
|
| 11 |
+
cond_stage_key: "txt"
|
| 12 |
+
image_size: 64
|
| 13 |
+
channels: 4
|
| 14 |
+
cond_stage_trainable: false # Note: different from the one we trained before
|
| 15 |
+
conditioning_key: crossattn
|
| 16 |
+
monitor: val/loss_simple_ema
|
| 17 |
+
scale_factor: 0.18215
|
| 18 |
+
use_ema: False
|
| 19 |
+
|
| 20 |
+
scheduler_config: # 10000 warmup steps
|
| 21 |
+
target: ldm.lr_scheduler.LambdaLinearScheduler
|
| 22 |
+
params:
|
| 23 |
+
warm_up_steps: [ 10000 ]
|
| 24 |
+
cycle_lengths: [ 10000000000000 ] # incredibly large number to prevent corner cases
|
| 25 |
+
f_start: [ 1.e-6 ]
|
| 26 |
+
f_max: [ 1. ]
|
| 27 |
+
f_min: [ 1. ]
|
| 28 |
+
|
| 29 |
+
unet_config:
|
| 30 |
+
target: ldm.modules.diffusionmodules.openaimodel.UNetModel
|
| 31 |
+
params:
|
| 32 |
+
image_size: 32 # unused
|
| 33 |
+
in_channels: 4
|
| 34 |
+
out_channels: 4
|
| 35 |
+
model_channels: 320
|
| 36 |
+
attention_resolutions: [ 4, 2, 1 ]
|
| 37 |
+
num_res_blocks: 2
|
| 38 |
+
channel_mult: [ 1, 2, 4, 4 ]
|
| 39 |
+
num_heads: 8
|
| 40 |
+
use_spatial_transformer: True
|
| 41 |
+
transformer_depth: 1
|
| 42 |
+
context_dim: 768
|
| 43 |
+
use_checkpoint: True
|
| 44 |
+
legacy: False
|
| 45 |
+
|
| 46 |
+
first_stage_config:
|
| 47 |
+
target: ldm.models.autoencoder.AutoencoderKL
|
| 48 |
+
params:
|
| 49 |
+
embed_dim: 4
|
| 50 |
+
monitor: val/rec_loss
|
| 51 |
+
ddconfig:
|
| 52 |
+
double_z: true
|
| 53 |
+
z_channels: 4
|
| 54 |
+
resolution: 256
|
| 55 |
+
in_channels: 3
|
| 56 |
+
out_ch: 3
|
| 57 |
+
ch: 128
|
| 58 |
+
ch_mult:
|
| 59 |
+
- 1
|
| 60 |
+
- 2
|
| 61 |
+
- 4
|
| 62 |
+
- 4
|
| 63 |
+
num_res_blocks: 2
|
| 64 |
+
attn_resolutions: []
|
| 65 |
+
dropout: 0.0
|
| 66 |
+
lossconfig:
|
| 67 |
+
target: torch.nn.Identity
|
| 68 |
+
|
| 69 |
+
cond_stage_config:
|
| 70 |
+
target: ldm.modules.encoders.modules.FrozenCLIPEmbedder
|
| 71 |
+
params:
|
| 72 |
+
layer: "hidden"
|
| 73 |
+
layer_idx: -2
|
ComfyUI/models/configs/v1-inference.yaml
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
model:
|
| 2 |
+
base_learning_rate: 1.0e-04
|
| 3 |
+
target: ldm.models.diffusion.ddpm.LatentDiffusion
|
| 4 |
+
params:
|
| 5 |
+
linear_start: 0.00085
|
| 6 |
+
linear_end: 0.0120
|
| 7 |
+
num_timesteps_cond: 1
|
| 8 |
+
log_every_t: 200
|
| 9 |
+
timesteps: 1000
|
| 10 |
+
first_stage_key: "jpg"
|
| 11 |
+
cond_stage_key: "txt"
|
| 12 |
+
image_size: 64
|
| 13 |
+
channels: 4
|
| 14 |
+
cond_stage_trainable: false # Note: different from the one we trained before
|
| 15 |
+
conditioning_key: crossattn
|
| 16 |
+
monitor: val/loss_simple_ema
|
| 17 |
+
scale_factor: 0.18215
|
| 18 |
+
use_ema: False
|
| 19 |
+
|
| 20 |
+
scheduler_config: # 10000 warmup steps
|
| 21 |
+
target: ldm.lr_scheduler.LambdaLinearScheduler
|
| 22 |
+
params:
|
| 23 |
+
warm_up_steps: [ 10000 ]
|
| 24 |
+
cycle_lengths: [ 10000000000000 ] # incredibly large number to prevent corner cases
|
| 25 |
+
f_start: [ 1.e-6 ]
|
| 26 |
+
f_max: [ 1. ]
|
| 27 |
+
f_min: [ 1. ]
|
| 28 |
+
|
| 29 |
+
unet_config:
|
| 30 |
+
target: ldm.modules.diffusionmodules.openaimodel.UNetModel
|
| 31 |
+
params:
|
| 32 |
+
image_size: 32 # unused
|
| 33 |
+
in_channels: 4
|
| 34 |
+
out_channels: 4
|
| 35 |
+
model_channels: 320
|
| 36 |
+
attention_resolutions: [ 4, 2, 1 ]
|
| 37 |
+
num_res_blocks: 2
|
| 38 |
+
channel_mult: [ 1, 2, 4, 4 ]
|
| 39 |
+
num_heads: 8
|
| 40 |
+
use_spatial_transformer: True
|
| 41 |
+
transformer_depth: 1
|
| 42 |
+
context_dim: 768
|
| 43 |
+
use_checkpoint: True
|
| 44 |
+
legacy: False
|
| 45 |
+
|
| 46 |
+
first_stage_config:
|
| 47 |
+
target: ldm.models.autoencoder.AutoencoderKL
|
| 48 |
+
params:
|
| 49 |
+
embed_dim: 4
|
| 50 |
+
monitor: val/rec_loss
|
| 51 |
+
ddconfig:
|
| 52 |
+
double_z: true
|
| 53 |
+
z_channels: 4
|
| 54 |
+
resolution: 256
|
| 55 |
+
in_channels: 3
|
| 56 |
+
out_ch: 3
|
| 57 |
+
ch: 128
|
| 58 |
+
ch_mult:
|
| 59 |
+
- 1
|
| 60 |
+
- 2
|
| 61 |
+
- 4
|
| 62 |
+
- 4
|
| 63 |
+
num_res_blocks: 2
|
| 64 |
+
attn_resolutions: []
|
| 65 |
+
dropout: 0.0
|
| 66 |
+
lossconfig:
|
| 67 |
+
target: torch.nn.Identity
|
| 68 |
+
|
| 69 |
+
cond_stage_config:
|
| 70 |
+
target: ldm.modules.encoders.modules.FrozenCLIPEmbedder
|
ComfyUI/models/configs/v1-inference_clip_skip_2.yaml
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
model:
|
| 2 |
+
base_learning_rate: 1.0e-04
|
| 3 |
+
target: ldm.models.diffusion.ddpm.LatentDiffusion
|
| 4 |
+
params:
|
| 5 |
+
linear_start: 0.00085
|
| 6 |
+
linear_end: 0.0120
|
| 7 |
+
num_timesteps_cond: 1
|
| 8 |
+
log_every_t: 200
|
| 9 |
+
timesteps: 1000
|
| 10 |
+
first_stage_key: "jpg"
|
| 11 |
+
cond_stage_key: "txt"
|
| 12 |
+
image_size: 64
|
| 13 |
+
channels: 4
|
| 14 |
+
cond_stage_trainable: false # Note: different from the one we trained before
|
| 15 |
+
conditioning_key: crossattn
|
| 16 |
+
monitor: val/loss_simple_ema
|
| 17 |
+
scale_factor: 0.18215
|
| 18 |
+
use_ema: False
|
| 19 |
+
|
| 20 |
+
scheduler_config: # 10000 warmup steps
|
| 21 |
+
target: ldm.lr_scheduler.LambdaLinearScheduler
|
| 22 |
+
params:
|
| 23 |
+
warm_up_steps: [ 10000 ]
|
| 24 |
+
cycle_lengths: [ 10000000000000 ] # incredibly large number to prevent corner cases
|
| 25 |
+
f_start: [ 1.e-6 ]
|
| 26 |
+
f_max: [ 1. ]
|
| 27 |
+
f_min: [ 1. ]
|
| 28 |
+
|
| 29 |
+
unet_config:
|
| 30 |
+
target: ldm.modules.diffusionmodules.openaimodel.UNetModel
|
| 31 |
+
params:
|
| 32 |
+
image_size: 32 # unused
|
| 33 |
+
in_channels: 4
|
| 34 |
+
out_channels: 4
|
| 35 |
+
model_channels: 320
|
| 36 |
+
attention_resolutions: [ 4, 2, 1 ]
|
| 37 |
+
num_res_blocks: 2
|
| 38 |
+
channel_mult: [ 1, 2, 4, 4 ]
|
| 39 |
+
num_heads: 8
|
| 40 |
+
use_spatial_transformer: True
|
| 41 |
+
transformer_depth: 1
|
| 42 |
+
context_dim: 768
|
| 43 |
+
use_checkpoint: True
|
| 44 |
+
legacy: False
|
| 45 |
+
|
| 46 |
+
first_stage_config:
|
| 47 |
+
target: ldm.models.autoencoder.AutoencoderKL
|
| 48 |
+
params:
|
| 49 |
+
embed_dim: 4
|
| 50 |
+
monitor: val/rec_loss
|
| 51 |
+
ddconfig:
|
| 52 |
+
double_z: true
|
| 53 |
+
z_channels: 4
|
| 54 |
+
resolution: 256
|
| 55 |
+
in_channels: 3
|
| 56 |
+
out_ch: 3
|
| 57 |
+
ch: 128
|
| 58 |
+
ch_mult:
|
| 59 |
+
- 1
|
| 60 |
+
- 2
|
| 61 |
+
- 4
|
| 62 |
+
- 4
|
| 63 |
+
num_res_blocks: 2
|
| 64 |
+
attn_resolutions: []
|
| 65 |
+
dropout: 0.0
|
| 66 |
+
lossconfig:
|
| 67 |
+
target: torch.nn.Identity
|
| 68 |
+
|
| 69 |
+
cond_stage_config:
|
| 70 |
+
target: ldm.modules.encoders.modules.FrozenCLIPEmbedder
|
| 71 |
+
params:
|
| 72 |
+
layer: "hidden"
|
| 73 |
+
layer_idx: -2
|
ComfyUI/models/configs/v1-inference_clip_skip_2_fp16.yaml
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
model:
|
| 2 |
+
base_learning_rate: 1.0e-04
|
| 3 |
+
target: ldm.models.diffusion.ddpm.LatentDiffusion
|
| 4 |
+
params:
|
| 5 |
+
linear_start: 0.00085
|
| 6 |
+
linear_end: 0.0120
|
| 7 |
+
num_timesteps_cond: 1
|
| 8 |
+
log_every_t: 200
|
| 9 |
+
timesteps: 1000
|
| 10 |
+
first_stage_key: "jpg"
|
| 11 |
+
cond_stage_key: "txt"
|
| 12 |
+
image_size: 64
|
| 13 |
+
channels: 4
|
| 14 |
+
cond_stage_trainable: false # Note: different from the one we trained before
|
| 15 |
+
conditioning_key: crossattn
|
| 16 |
+
monitor: val/loss_simple_ema
|
| 17 |
+
scale_factor: 0.18215
|
| 18 |
+
use_ema: False
|
| 19 |
+
|
| 20 |
+
scheduler_config: # 10000 warmup steps
|
| 21 |
+
target: ldm.lr_scheduler.LambdaLinearScheduler
|
| 22 |
+
params:
|
| 23 |
+
warm_up_steps: [ 10000 ]
|
| 24 |
+
cycle_lengths: [ 10000000000000 ] # incredibly large number to prevent corner cases
|
| 25 |
+
f_start: [ 1.e-6 ]
|
| 26 |
+
f_max: [ 1. ]
|
| 27 |
+
f_min: [ 1. ]
|
| 28 |
+
|
| 29 |
+
unet_config:
|
| 30 |
+
target: ldm.modules.diffusionmodules.openaimodel.UNetModel
|
| 31 |
+
params:
|
| 32 |
+
use_fp16: True
|
| 33 |
+
image_size: 32 # unused
|
| 34 |
+
in_channels: 4
|
| 35 |
+
out_channels: 4
|
| 36 |
+
model_channels: 320
|
| 37 |
+
attention_resolutions: [ 4, 2, 1 ]
|
| 38 |
+
num_res_blocks: 2
|
| 39 |
+
channel_mult: [ 1, 2, 4, 4 ]
|
| 40 |
+
num_heads: 8
|
| 41 |
+
use_spatial_transformer: True
|
| 42 |
+
transformer_depth: 1
|
| 43 |
+
context_dim: 768
|
| 44 |
+
use_checkpoint: True
|
| 45 |
+
legacy: False
|
| 46 |
+
|
| 47 |
+
first_stage_config:
|
| 48 |
+
target: ldm.models.autoencoder.AutoencoderKL
|
| 49 |
+
params:
|
| 50 |
+
embed_dim: 4
|
| 51 |
+
monitor: val/rec_loss
|
| 52 |
+
ddconfig:
|
| 53 |
+
double_z: true
|
| 54 |
+
z_channels: 4
|
| 55 |
+
resolution: 256
|
| 56 |
+
in_channels: 3
|
| 57 |
+
out_ch: 3
|
| 58 |
+
ch: 128
|
| 59 |
+
ch_mult:
|
| 60 |
+
- 1
|
| 61 |
+
- 2
|
| 62 |
+
- 4
|
| 63 |
+
- 4
|
| 64 |
+
num_res_blocks: 2
|
| 65 |
+
attn_resolutions: []
|
| 66 |
+
dropout: 0.0
|
| 67 |
+
lossconfig:
|
| 68 |
+
target: torch.nn.Identity
|
| 69 |
+
|
| 70 |
+
cond_stage_config:
|
| 71 |
+
target: ldm.modules.encoders.modules.FrozenCLIPEmbedder
|
| 72 |
+
params:
|
| 73 |
+
layer: "hidden"
|
| 74 |
+
layer_idx: -2
|
ComfyUI/models/configs/v1-inference_fp16.yaml
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
model:
|
| 2 |
+
base_learning_rate: 1.0e-04
|
| 3 |
+
target: ldm.models.diffusion.ddpm.LatentDiffusion
|
| 4 |
+
params:
|
| 5 |
+
linear_start: 0.00085
|
| 6 |
+
linear_end: 0.0120
|
| 7 |
+
num_timesteps_cond: 1
|
| 8 |
+
log_every_t: 200
|
| 9 |
+
timesteps: 1000
|
| 10 |
+
first_stage_key: "jpg"
|
| 11 |
+
cond_stage_key: "txt"
|
| 12 |
+
image_size: 64
|
| 13 |
+
channels: 4
|
| 14 |
+
cond_stage_trainable: false # Note: different from the one we trained before
|
| 15 |
+
conditioning_key: crossattn
|
| 16 |
+
monitor: val/loss_simple_ema
|
| 17 |
+
scale_factor: 0.18215
|
| 18 |
+
use_ema: False
|
| 19 |
+
|
| 20 |
+
scheduler_config: # 10000 warmup steps
|
| 21 |
+
target: ldm.lr_scheduler.LambdaLinearScheduler
|
| 22 |
+
params:
|
| 23 |
+
warm_up_steps: [ 10000 ]
|
| 24 |
+
cycle_lengths: [ 10000000000000 ] # incredibly large number to prevent corner cases
|
| 25 |
+
f_start: [ 1.e-6 ]
|
| 26 |
+
f_max: [ 1. ]
|
| 27 |
+
f_min: [ 1. ]
|
| 28 |
+
|
| 29 |
+
unet_config:
|
| 30 |
+
target: ldm.modules.diffusionmodules.openaimodel.UNetModel
|
| 31 |
+
params:
|
| 32 |
+
use_fp16: True
|
| 33 |
+
image_size: 32 # unused
|
| 34 |
+
in_channels: 4
|
| 35 |
+
out_channels: 4
|
| 36 |
+
model_channels: 320
|
| 37 |
+
attention_resolutions: [ 4, 2, 1 ]
|
| 38 |
+
num_res_blocks: 2
|
| 39 |
+
channel_mult: [ 1, 2, 4, 4 ]
|
| 40 |
+
num_heads: 8
|
| 41 |
+
use_spatial_transformer: True
|
| 42 |
+
transformer_depth: 1
|
| 43 |
+
context_dim: 768
|
| 44 |
+
use_checkpoint: True
|
| 45 |
+
legacy: False
|
| 46 |
+
|
| 47 |
+
first_stage_config:
|
| 48 |
+
target: ldm.models.autoencoder.AutoencoderKL
|
| 49 |
+
params:
|
| 50 |
+
embed_dim: 4
|
| 51 |
+
monitor: val/rec_loss
|
| 52 |
+
ddconfig:
|
| 53 |
+
double_z: true
|
| 54 |
+
z_channels: 4
|
| 55 |
+
resolution: 256
|
| 56 |
+
in_channels: 3
|
| 57 |
+
out_ch: 3
|
| 58 |
+
ch: 128
|
| 59 |
+
ch_mult:
|
| 60 |
+
- 1
|
| 61 |
+
- 2
|
| 62 |
+
- 4
|
| 63 |
+
- 4
|
| 64 |
+
num_res_blocks: 2
|
| 65 |
+
attn_resolutions: []
|
| 66 |
+
dropout: 0.0
|
| 67 |
+
lossconfig:
|
| 68 |
+
target: torch.nn.Identity
|
| 69 |
+
|
| 70 |
+
cond_stage_config:
|
| 71 |
+
target: ldm.modules.encoders.modules.FrozenCLIPEmbedder
|
ComfyUI/models/configs/v1-inpainting-inference.yaml
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
model:
|
| 2 |
+
base_learning_rate: 7.5e-05
|
| 3 |
+
target: ldm.models.diffusion.ddpm.LatentInpaintDiffusion
|
| 4 |
+
params:
|
| 5 |
+
linear_start: 0.00085
|
| 6 |
+
linear_end: 0.0120
|
| 7 |
+
num_timesteps_cond: 1
|
| 8 |
+
log_every_t: 200
|
| 9 |
+
timesteps: 1000
|
| 10 |
+
first_stage_key: "jpg"
|
| 11 |
+
cond_stage_key: "txt"
|
| 12 |
+
image_size: 64
|
| 13 |
+
channels: 4
|
| 14 |
+
cond_stage_trainable: false # Note: different from the one we trained before
|
| 15 |
+
conditioning_key: hybrid # important
|
| 16 |
+
monitor: val/loss_simple_ema
|
| 17 |
+
scale_factor: 0.18215
|
| 18 |
+
finetune_keys: null
|
| 19 |
+
|
| 20 |
+
scheduler_config: # 10000 warmup steps
|
| 21 |
+
target: ldm.lr_scheduler.LambdaLinearScheduler
|
| 22 |
+
params:
|
| 23 |
+
warm_up_steps: [ 2500 ] # NOTE for resuming. use 10000 if starting from scratch
|
| 24 |
+
cycle_lengths: [ 10000000000000 ] # incredibly large number to prevent corner cases
|
| 25 |
+
f_start: [ 1.e-6 ]
|
| 26 |
+
f_max: [ 1. ]
|
| 27 |
+
f_min: [ 1. ]
|
| 28 |
+
|
| 29 |
+
unet_config:
|
| 30 |
+
target: ldm.modules.diffusionmodules.openaimodel.UNetModel
|
| 31 |
+
params:
|
| 32 |
+
image_size: 32 # unused
|
| 33 |
+
in_channels: 9 # 4 data + 4 downscaled image + 1 mask
|
| 34 |
+
out_channels: 4
|
| 35 |
+
model_channels: 320
|
| 36 |
+
attention_resolutions: [ 4, 2, 1 ]
|
| 37 |
+
num_res_blocks: 2
|
| 38 |
+
channel_mult: [ 1, 2, 4, 4 ]
|
| 39 |
+
num_heads: 8
|
| 40 |
+
use_spatial_transformer: True
|
| 41 |
+
transformer_depth: 1
|
| 42 |
+
context_dim: 768
|
| 43 |
+
use_checkpoint: True
|
| 44 |
+
legacy: False
|
| 45 |
+
|
| 46 |
+
first_stage_config:
|
| 47 |
+
target: ldm.models.autoencoder.AutoencoderKL
|
| 48 |
+
params:
|
| 49 |
+
embed_dim: 4
|
| 50 |
+
monitor: val/rec_loss
|
| 51 |
+
ddconfig:
|
| 52 |
+
double_z: true
|
| 53 |
+
z_channels: 4
|
| 54 |
+
resolution: 256
|
| 55 |
+
in_channels: 3
|
| 56 |
+
out_ch: 3
|
| 57 |
+
ch: 128
|
| 58 |
+
ch_mult:
|
| 59 |
+
- 1
|
| 60 |
+
- 2
|
| 61 |
+
- 4
|
| 62 |
+
- 4
|
| 63 |
+
num_res_blocks: 2
|
| 64 |
+
attn_resolutions: []
|
| 65 |
+
dropout: 0.0
|
| 66 |
+
lossconfig:
|
| 67 |
+
target: torch.nn.Identity
|
| 68 |
+
|
| 69 |
+
cond_stage_config:
|
| 70 |
+
target: ldm.modules.encoders.modules.FrozenCLIPEmbedder
|
| 71 |
+
|
ComfyUI/models/configs/v2-inference-v.yaml
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
model:
|
| 2 |
+
base_learning_rate: 1.0e-4
|
| 3 |
+
target: ldm.models.diffusion.ddpm.LatentDiffusion
|
| 4 |
+
params:
|
| 5 |
+
parameterization: "v"
|
| 6 |
+
linear_start: 0.00085
|
| 7 |
+
linear_end: 0.0120
|
| 8 |
+
num_timesteps_cond: 1
|
| 9 |
+
log_every_t: 200
|
| 10 |
+
timesteps: 1000
|
| 11 |
+
first_stage_key: "jpg"
|
| 12 |
+
cond_stage_key: "txt"
|
| 13 |
+
image_size: 64
|
| 14 |
+
channels: 4
|
| 15 |
+
cond_stage_trainable: false
|
| 16 |
+
conditioning_key: crossattn
|
| 17 |
+
monitor: val/loss_simple_ema
|
| 18 |
+
scale_factor: 0.18215
|
| 19 |
+
use_ema: False # we set this to false because this is an inference only config
|
| 20 |
+
|
| 21 |
+
unet_config:
|
| 22 |
+
target: ldm.modules.diffusionmodules.openaimodel.UNetModel
|
| 23 |
+
params:
|
| 24 |
+
use_checkpoint: True
|
| 25 |
+
use_fp16: True
|
| 26 |
+
image_size: 32 # unused
|
| 27 |
+
in_channels: 4
|
| 28 |
+
out_channels: 4
|
| 29 |
+
model_channels: 320
|
| 30 |
+
attention_resolutions: [ 4, 2, 1 ]
|
| 31 |
+
num_res_blocks: 2
|
| 32 |
+
channel_mult: [ 1, 2, 4, 4 ]
|
| 33 |
+
num_head_channels: 64 # need to fix for flash-attn
|
| 34 |
+
use_spatial_transformer: True
|
| 35 |
+
use_linear_in_transformer: True
|
| 36 |
+
transformer_depth: 1
|
| 37 |
+
context_dim: 1024
|
| 38 |
+
legacy: False
|
| 39 |
+
|
| 40 |
+
first_stage_config:
|
| 41 |
+
target: ldm.models.autoencoder.AutoencoderKL
|
| 42 |
+
params:
|
| 43 |
+
embed_dim: 4
|
| 44 |
+
monitor: val/rec_loss
|
| 45 |
+
ddconfig:
|
| 46 |
+
#attn_type: "vanilla-xformers"
|
| 47 |
+
double_z: true
|
| 48 |
+
z_channels: 4
|
| 49 |
+
resolution: 256
|
| 50 |
+
in_channels: 3
|
| 51 |
+
out_ch: 3
|
| 52 |
+
ch: 128
|
| 53 |
+
ch_mult:
|
| 54 |
+
- 1
|
| 55 |
+
- 2
|
| 56 |
+
- 4
|
| 57 |
+
- 4
|
| 58 |
+
num_res_blocks: 2
|
| 59 |
+
attn_resolutions: []
|
| 60 |
+
dropout: 0.0
|
| 61 |
+
lossconfig:
|
| 62 |
+
target: torch.nn.Identity
|
| 63 |
+
|
| 64 |
+
cond_stage_config:
|
| 65 |
+
target: ldm.modules.encoders.modules.FrozenOpenCLIPEmbedder
|
| 66 |
+
params:
|
| 67 |
+
freeze: True
|
| 68 |
+
layer: "penultimate"
|
ComfyUI/models/configs/v2-inference-v_fp32.yaml
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
model:
|
| 2 |
+
base_learning_rate: 1.0e-4
|
| 3 |
+
target: ldm.models.diffusion.ddpm.LatentDiffusion
|
| 4 |
+
params:
|
| 5 |
+
parameterization: "v"
|
| 6 |
+
linear_start: 0.00085
|
| 7 |
+
linear_end: 0.0120
|
| 8 |
+
num_timesteps_cond: 1
|
| 9 |
+
log_every_t: 200
|
| 10 |
+
timesteps: 1000
|
| 11 |
+
first_stage_key: "jpg"
|
| 12 |
+
cond_stage_key: "txt"
|
| 13 |
+
image_size: 64
|
| 14 |
+
channels: 4
|
| 15 |
+
cond_stage_trainable: false
|
| 16 |
+
conditioning_key: crossattn
|
| 17 |
+
monitor: val/loss_simple_ema
|
| 18 |
+
scale_factor: 0.18215
|
| 19 |
+
use_ema: False # we set this to false because this is an inference only config
|
| 20 |
+
|
| 21 |
+
unet_config:
|
| 22 |
+
target: ldm.modules.diffusionmodules.openaimodel.UNetModel
|
| 23 |
+
params:
|
| 24 |
+
use_checkpoint: True
|
| 25 |
+
use_fp16: False
|
| 26 |
+
image_size: 32 # unused
|
| 27 |
+
in_channels: 4
|
| 28 |
+
out_channels: 4
|
| 29 |
+
model_channels: 320
|
| 30 |
+
attention_resolutions: [ 4, 2, 1 ]
|
| 31 |
+
num_res_blocks: 2
|
| 32 |
+
channel_mult: [ 1, 2, 4, 4 ]
|
| 33 |
+
num_head_channels: 64 # need to fix for flash-attn
|
| 34 |
+
use_spatial_transformer: True
|
| 35 |
+
use_linear_in_transformer: True
|
| 36 |
+
transformer_depth: 1
|
| 37 |
+
context_dim: 1024
|
| 38 |
+
legacy: False
|
| 39 |
+
|
| 40 |
+
first_stage_config:
|
| 41 |
+
target: ldm.models.autoencoder.AutoencoderKL
|
| 42 |
+
params:
|
| 43 |
+
embed_dim: 4
|
| 44 |
+
monitor: val/rec_loss
|
| 45 |
+
ddconfig:
|
| 46 |
+
#attn_type: "vanilla-xformers"
|
| 47 |
+
double_z: true
|
| 48 |
+
z_channels: 4
|
| 49 |
+
resolution: 256
|
| 50 |
+
in_channels: 3
|
| 51 |
+
out_ch: 3
|
| 52 |
+
ch: 128
|
| 53 |
+
ch_mult:
|
| 54 |
+
- 1
|
| 55 |
+
- 2
|
| 56 |
+
- 4
|
| 57 |
+
- 4
|
| 58 |
+
num_res_blocks: 2
|
| 59 |
+
attn_resolutions: []
|
| 60 |
+
dropout: 0.0
|
| 61 |
+
lossconfig:
|
| 62 |
+
target: torch.nn.Identity
|
| 63 |
+
|
| 64 |
+
cond_stage_config:
|
| 65 |
+
target: ldm.modules.encoders.modules.FrozenOpenCLIPEmbedder
|
| 66 |
+
params:
|
| 67 |
+
freeze: True
|
| 68 |
+
layer: "penultimate"
|
ComfyUI/models/configs/v2-inference.yaml
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
model:
|
| 2 |
+
base_learning_rate: 1.0e-4
|
| 3 |
+
target: ldm.models.diffusion.ddpm.LatentDiffusion
|
| 4 |
+
params:
|
| 5 |
+
linear_start: 0.00085
|
| 6 |
+
linear_end: 0.0120
|
| 7 |
+
num_timesteps_cond: 1
|
| 8 |
+
log_every_t: 200
|
| 9 |
+
timesteps: 1000
|
| 10 |
+
first_stage_key: "jpg"
|
| 11 |
+
cond_stage_key: "txt"
|
| 12 |
+
image_size: 64
|
| 13 |
+
channels: 4
|
| 14 |
+
cond_stage_trainable: false
|
| 15 |
+
conditioning_key: crossattn
|
| 16 |
+
monitor: val/loss_simple_ema
|
| 17 |
+
scale_factor: 0.18215
|
| 18 |
+
use_ema: False # we set this to false because this is an inference only config
|
| 19 |
+
|
| 20 |
+
unet_config:
|
| 21 |
+
target: ldm.modules.diffusionmodules.openaimodel.UNetModel
|
| 22 |
+
params:
|
| 23 |
+
use_checkpoint: True
|
| 24 |
+
use_fp16: True
|
| 25 |
+
image_size: 32 # unused
|
| 26 |
+
in_channels: 4
|
| 27 |
+
out_channels: 4
|
| 28 |
+
model_channels: 320
|
| 29 |
+
attention_resolutions: [ 4, 2, 1 ]
|
| 30 |
+
num_res_blocks: 2
|
| 31 |
+
channel_mult: [ 1, 2, 4, 4 ]
|
| 32 |
+
num_head_channels: 64 # need to fix for flash-attn
|
| 33 |
+
use_spatial_transformer: True
|
| 34 |
+
use_linear_in_transformer: True
|
| 35 |
+
transformer_depth: 1
|
| 36 |
+
context_dim: 1024
|
| 37 |
+
legacy: False
|
| 38 |
+
|
| 39 |
+
first_stage_config:
|
| 40 |
+
target: ldm.models.autoencoder.AutoencoderKL
|
| 41 |
+
params:
|
| 42 |
+
embed_dim: 4
|
| 43 |
+
monitor: val/rec_loss
|
| 44 |
+
ddconfig:
|
| 45 |
+
#attn_type: "vanilla-xformers"
|
| 46 |
+
double_z: true
|
| 47 |
+
z_channels: 4
|
| 48 |
+
resolution: 256
|
| 49 |
+
in_channels: 3
|
| 50 |
+
out_ch: 3
|
| 51 |
+
ch: 128
|
| 52 |
+
ch_mult:
|
| 53 |
+
- 1
|
| 54 |
+
- 2
|
| 55 |
+
- 4
|
| 56 |
+
- 4
|
| 57 |
+
num_res_blocks: 2
|
| 58 |
+
attn_resolutions: []
|
| 59 |
+
dropout: 0.0
|
| 60 |
+
lossconfig:
|
| 61 |
+
target: torch.nn.Identity
|
| 62 |
+
|
| 63 |
+
cond_stage_config:
|
| 64 |
+
target: ldm.modules.encoders.modules.FrozenOpenCLIPEmbedder
|
| 65 |
+
params:
|
| 66 |
+
freeze: True
|
| 67 |
+
layer: "penultimate"
|
ComfyUI/models/configs/v2-inference_fp32.yaml
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
model:
|
| 2 |
+
base_learning_rate: 1.0e-4
|
| 3 |
+
target: ldm.models.diffusion.ddpm.LatentDiffusion
|
| 4 |
+
params:
|
| 5 |
+
linear_start: 0.00085
|
| 6 |
+
linear_end: 0.0120
|
| 7 |
+
num_timesteps_cond: 1
|
| 8 |
+
log_every_t: 200
|
| 9 |
+
timesteps: 1000
|
| 10 |
+
first_stage_key: "jpg"
|
| 11 |
+
cond_stage_key: "txt"
|
| 12 |
+
image_size: 64
|
| 13 |
+
channels: 4
|
| 14 |
+
cond_stage_trainable: false
|
| 15 |
+
conditioning_key: crossattn
|
| 16 |
+
monitor: val/loss_simple_ema
|
| 17 |
+
scale_factor: 0.18215
|
| 18 |
+
use_ema: False # we set this to false because this is an inference only config
|
| 19 |
+
|
| 20 |
+
unet_config:
|
| 21 |
+
target: ldm.modules.diffusionmodules.openaimodel.UNetModel
|
| 22 |
+
params:
|
| 23 |
+
use_checkpoint: True
|
| 24 |
+
use_fp16: False
|
| 25 |
+
image_size: 32 # unused
|
| 26 |
+
in_channels: 4
|
| 27 |
+
out_channels: 4
|
| 28 |
+
model_channels: 320
|
| 29 |
+
attention_resolutions: [ 4, 2, 1 ]
|
| 30 |
+
num_res_blocks: 2
|
| 31 |
+
channel_mult: [ 1, 2, 4, 4 ]
|
| 32 |
+
num_head_channels: 64 # need to fix for flash-attn
|
| 33 |
+
use_spatial_transformer: True
|
| 34 |
+
use_linear_in_transformer: True
|
| 35 |
+
transformer_depth: 1
|
| 36 |
+
context_dim: 1024
|
| 37 |
+
legacy: False
|
| 38 |
+
|
| 39 |
+
first_stage_config:
|
| 40 |
+
target: ldm.models.autoencoder.AutoencoderKL
|
| 41 |
+
params:
|
| 42 |
+
embed_dim: 4
|
| 43 |
+
monitor: val/rec_loss
|
| 44 |
+
ddconfig:
|
| 45 |
+
#attn_type: "vanilla-xformers"
|
| 46 |
+
double_z: true
|
| 47 |
+
z_channels: 4
|
| 48 |
+
resolution: 256
|
| 49 |
+
in_channels: 3
|
| 50 |
+
out_ch: 3
|
| 51 |
+
ch: 128
|
| 52 |
+
ch_mult:
|
| 53 |
+
- 1
|
| 54 |
+
- 2
|
| 55 |
+
- 4
|
| 56 |
+
- 4
|
| 57 |
+
num_res_blocks: 2
|
| 58 |
+
attn_resolutions: []
|
| 59 |
+
dropout: 0.0
|
| 60 |
+
lossconfig:
|
| 61 |
+
target: torch.nn.Identity
|
| 62 |
+
|
| 63 |
+
cond_stage_config:
|
| 64 |
+
target: ldm.modules.encoders.modules.FrozenOpenCLIPEmbedder
|
| 65 |
+
params:
|
| 66 |
+
freeze: True
|
| 67 |
+
layer: "penultimate"
|
ComfyUI/models/configs/v2-inpainting-inference.yaml
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
model:
|
| 2 |
+
base_learning_rate: 5.0e-05
|
| 3 |
+
target: ldm.models.diffusion.ddpm.LatentInpaintDiffusion
|
| 4 |
+
params:
|
| 5 |
+
linear_start: 0.00085
|
| 6 |
+
linear_end: 0.0120
|
| 7 |
+
num_timesteps_cond: 1
|
| 8 |
+
log_every_t: 200
|
| 9 |
+
timesteps: 1000
|
| 10 |
+
first_stage_key: "jpg"
|
| 11 |
+
cond_stage_key: "txt"
|
| 12 |
+
image_size: 64
|
| 13 |
+
channels: 4
|
| 14 |
+
cond_stage_trainable: false
|
| 15 |
+
conditioning_key: hybrid
|
| 16 |
+
scale_factor: 0.18215
|
| 17 |
+
monitor: val/loss_simple_ema
|
| 18 |
+
finetune_keys: null
|
| 19 |
+
use_ema: False
|
| 20 |
+
|
| 21 |
+
unet_config:
|
| 22 |
+
target: ldm.modules.diffusionmodules.openaimodel.UNetModel
|
| 23 |
+
params:
|
| 24 |
+
use_checkpoint: True
|
| 25 |
+
image_size: 32 # unused
|
| 26 |
+
in_channels: 9
|
| 27 |
+
out_channels: 4
|
| 28 |
+
model_channels: 320
|
| 29 |
+
attention_resolutions: [ 4, 2, 1 ]
|
| 30 |
+
num_res_blocks: 2
|
| 31 |
+
channel_mult: [ 1, 2, 4, 4 ]
|
| 32 |
+
num_head_channels: 64 # need to fix for flash-attn
|
| 33 |
+
use_spatial_transformer: True
|
| 34 |
+
use_linear_in_transformer: True
|
| 35 |
+
transformer_depth: 1
|
| 36 |
+
context_dim: 1024
|
| 37 |
+
legacy: False
|
| 38 |
+
|
| 39 |
+
first_stage_config:
|
| 40 |
+
target: ldm.models.autoencoder.AutoencoderKL
|
| 41 |
+
params:
|
| 42 |
+
embed_dim: 4
|
| 43 |
+
monitor: val/rec_loss
|
| 44 |
+
ddconfig:
|
| 45 |
+
#attn_type: "vanilla-xformers"
|
| 46 |
+
double_z: true
|
| 47 |
+
z_channels: 4
|
| 48 |
+
resolution: 256
|
| 49 |
+
in_channels: 3
|
| 50 |
+
out_ch: 3
|
| 51 |
+
ch: 128
|
| 52 |
+
ch_mult:
|
| 53 |
+
- 1
|
| 54 |
+
- 2
|
| 55 |
+
- 4
|
| 56 |
+
- 4
|
| 57 |
+
num_res_blocks: 2
|
| 58 |
+
attn_resolutions: [ ]
|
| 59 |
+
dropout: 0.0
|
| 60 |
+
lossconfig:
|
| 61 |
+
target: torch.nn.Identity
|
| 62 |
+
|
| 63 |
+
cond_stage_config:
|
| 64 |
+
target: ldm.modules.encoders.modules.FrozenOpenCLIPEmbedder
|
| 65 |
+
params:
|
| 66 |
+
freeze: True
|
| 67 |
+
layer: "penultimate"
|
| 68 |
+
|
| 69 |
+
|
| 70 |
+
data:
|
| 71 |
+
target: ldm.data.laion.WebDataModuleFromConfig
|
| 72 |
+
params:
|
| 73 |
+
tar_base: null # for concat as in LAION-A
|
| 74 |
+
p_unsafe_threshold: 0.1
|
| 75 |
+
filter_word_list: "data/filters.yaml"
|
| 76 |
+
max_pwatermark: 0.45
|
| 77 |
+
batch_size: 8
|
| 78 |
+
num_workers: 6
|
| 79 |
+
multinode: True
|
| 80 |
+
min_size: 512
|
| 81 |
+
train:
|
| 82 |
+
shards:
|
| 83 |
+
- "pipe:aws s3 cp s3://stability-aws/laion-a-native/part-0/{00000..18699}.tar -"
|
| 84 |
+
- "pipe:aws s3 cp s3://stability-aws/laion-a-native/part-1/{00000..18699}.tar -"
|
| 85 |
+
- "pipe:aws s3 cp s3://stability-aws/laion-a-native/part-2/{00000..18699}.tar -"
|
| 86 |
+
- "pipe:aws s3 cp s3://stability-aws/laion-a-native/part-3/{00000..18699}.tar -"
|
| 87 |
+
- "pipe:aws s3 cp s3://stability-aws/laion-a-native/part-4/{00000..18699}.tar -" #{00000-94333}.tar"
|
| 88 |
+
shuffle: 10000
|
| 89 |
+
image_key: jpg
|
| 90 |
+
image_transforms:
|
| 91 |
+
- target: torchvision.transforms.Resize
|
| 92 |
+
params:
|
| 93 |
+
size: 512
|
| 94 |
+
interpolation: 3
|
| 95 |
+
- target: torchvision.transforms.RandomCrop
|
| 96 |
+
params:
|
| 97 |
+
size: 512
|
| 98 |
+
postprocess:
|
| 99 |
+
target: ldm.data.laion.AddMask
|
| 100 |
+
params:
|
| 101 |
+
mode: "512train-large"
|
| 102 |
+
p_drop: 0.25
|
| 103 |
+
# NOTE use enough shards to avoid empty validation loops in workers
|
| 104 |
+
validation:
|
| 105 |
+
shards:
|
| 106 |
+
- "pipe:aws s3 cp s3://deep-floyd-s3/datasets/laion_cleaned-part5/{93001..94333}.tar - "
|
| 107 |
+
shuffle: 0
|
| 108 |
+
image_key: jpg
|
| 109 |
+
image_transforms:
|
| 110 |
+
- target: torchvision.transforms.Resize
|
| 111 |
+
params:
|
| 112 |
+
size: 512
|
| 113 |
+
interpolation: 3
|
| 114 |
+
- target: torchvision.transforms.CenterCrop
|
| 115 |
+
params:
|
| 116 |
+
size: 512
|
| 117 |
+
postprocess:
|
| 118 |
+
target: ldm.data.laion.AddMask
|
| 119 |
+
params:
|
| 120 |
+
mode: "512train-large"
|
| 121 |
+
p_drop: 0.25
|
| 122 |
+
|
| 123 |
+
lightning:
|
| 124 |
+
find_unused_parameters: True
|
| 125 |
+
modelcheckpoint:
|
| 126 |
+
params:
|
| 127 |
+
every_n_train_steps: 5000
|
| 128 |
+
|
| 129 |
+
callbacks:
|
| 130 |
+
metrics_over_trainsteps_checkpoint:
|
| 131 |
+
params:
|
| 132 |
+
every_n_train_steps: 10000
|
| 133 |
+
|
| 134 |
+
image_logger:
|
| 135 |
+
target: main.ImageLogger
|
| 136 |
+
params:
|
| 137 |
+
enable_autocast: False
|
| 138 |
+
disabled: False
|
| 139 |
+
batch_frequency: 1000
|
| 140 |
+
max_images: 4
|
| 141 |
+
increase_log_steps: False
|
| 142 |
+
log_first_step: False
|
| 143 |
+
log_images_kwargs:
|
| 144 |
+
use_ema_scope: False
|
| 145 |
+
inpaint: False
|
| 146 |
+
plot_progressive_rows: False
|
| 147 |
+
plot_diffusion_rows: False
|
| 148 |
+
N: 4
|
| 149 |
+
unconditional_guidance_scale: 5.0
|
| 150 |
+
unconditional_guidance_label: [""]
|
| 151 |
+
ddim_steps: 50 # todo check these out for depth2img,
|
| 152 |
+
ddim_eta: 0.0 # todo check these out for depth2img,
|
| 153 |
+
|
| 154 |
+
trainer:
|
| 155 |
+
benchmark: True
|
| 156 |
+
val_check_interval: 5000000
|
| 157 |
+
num_sanity_val_steps: 0
|
| 158 |
+
accumulate_grad_batches: 1
|
ComfyUI/models/controlnet/put_controlnets_and_t2i_here
ADDED
|
File without changes
|
ComfyUI/models/diffusers/put_diffusers_models_here
ADDED
|
File without changes
|
ComfyUI/models/diffusion_models/put_diffusion_model_files_here.safetensors
ADDED
|
File without changes
|