Compare commits
32 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2bc4b5968f | ||
|
|
7395b0c0d1 | ||
|
|
0952569493 | ||
|
|
29832b3b61 | ||
|
|
be4e760648 | ||
|
|
c3d9cc4592 | ||
|
|
84cc9cb528 | ||
|
|
ebbb920163 | ||
|
|
d60fe0af4a | ||
|
|
5dbd250965 | ||
|
|
4ab1875283 | ||
|
|
11b1f27cb1 | ||
|
|
70e15fd743 | ||
|
|
e1474150de | ||
|
|
e62d72e8ca | ||
|
|
1650cda030 | ||
|
|
a13125840c | ||
|
|
dfa36e6855 | ||
|
|
0124be4d93 | ||
|
|
29a70ca101 | ||
|
|
0bef826a98 | ||
|
|
85ef295069 | ||
|
|
5d84607bf3 | ||
|
|
c1909f350f | ||
|
|
52b3469606 | ||
|
|
889519971f | ||
|
|
76739c23c3 | ||
|
|
a80bc822a2 | ||
|
|
872780d236 | ||
|
|
6d45ffbe23 | ||
|
|
77633ba77d | ||
|
|
30e6cfb1a0 |
@@ -0,0 +1,2 @@
|
|||||||
|
.\python_embeded\python.exe -s ComfyUI\main.py --windows-standalone-build --fast fp16_accumulation
|
||||||
|
pause
|
||||||
@@ -7,7 +7,7 @@ on:
|
|||||||
description: 'cuda version'
|
description: 'cuda version'
|
||||||
required: true
|
required: true
|
||||||
type: string
|
type: string
|
||||||
default: "126"
|
default: "128"
|
||||||
|
|
||||||
python_minor:
|
python_minor:
|
||||||
description: 'python minor version'
|
description: 'python minor version'
|
||||||
@@ -19,7 +19,7 @@ on:
|
|||||||
description: 'python patch version'
|
description: 'python patch version'
|
||||||
required: true
|
required: true
|
||||||
type: string
|
type: string
|
||||||
default: "1"
|
default: "2"
|
||||||
# push:
|
# push:
|
||||||
# branches:
|
# branches:
|
||||||
# - master
|
# - master
|
||||||
@@ -34,7 +34,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 30
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- uses: actions/setup-python@v5
|
- uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
@@ -74,7 +74,7 @@ jobs:
|
|||||||
pause" > ./update/update_comfyui_and_python_dependencies.bat
|
pause" > ./update/update_comfyui_and_python_dependencies.bat
|
||||||
cd ..
|
cd ..
|
||||||
|
|
||||||
"C:\Program Files\7-Zip\7z.exe" a -t7z -m0=lzma2 -mx=8 -mfb=64 -md=32m -ms=on -mf=BCJ2 ComfyUI_windows_portable_nightly_pytorch.7z ComfyUI_windows_portable_nightly_pytorch
|
"C:\Program Files\7-Zip\7z.exe" a -t7z -m0=lzma2 -mx=9 -mfb=128 -md=512m -ms=on -mf=BCJ2 ComfyUI_windows_portable_nightly_pytorch.7z ComfyUI_windows_portable_nightly_pytorch
|
||||||
mv ComfyUI_windows_portable_nightly_pytorch.7z ComfyUI/ComfyUI_windows_portable_nvidia_or_cpu_nightly_pytorch.7z
|
mv ComfyUI_windows_portable_nightly_pytorch.7z ComfyUI/ComfyUI_windows_portable_nvidia_or_cpu_nightly_pytorch.7z
|
||||||
|
|
||||||
cd ComfyUI_windows_portable_nightly_pytorch
|
cd ComfyUI_windows_portable_nightly_pytorch
|
||||||
|
|||||||
@@ -215,9 +215,9 @@ Nvidia users should install stable pytorch using this command:
|
|||||||
|
|
||||||
```pip install torch torchvision torchaudio --extra-index-url https://download.pytorch.org/whl/cu126```
|
```pip install torch torchvision torchaudio --extra-index-url https://download.pytorch.org/whl/cu126```
|
||||||
|
|
||||||
This is the command to install pytorch nightly instead which might have performance improvements:
|
This is the command to install pytorch nightly instead which supports the new blackwell 50xx series GPUs and might have performance improvements.
|
||||||
|
|
||||||
```pip install --pre torch torchvision torchaudio --index-url https://download.pytorch.org/whl/nightly/cu126```
|
```pip install --pre torch torchvision torchaudio --index-url https://download.pytorch.org/whl/nightly/cu128```
|
||||||
|
|
||||||
#### Troubleshooting
|
#### Troubleshooting
|
||||||
|
|
||||||
|
|||||||
@@ -18,15 +18,27 @@ from typing_extensions import NotRequired
|
|||||||
from comfy.cli_args import DEFAULT_VERSION_STRING
|
from comfy.cli_args import DEFAULT_VERSION_STRING
|
||||||
|
|
||||||
|
|
||||||
|
def frontend_install_warning_message():
|
||||||
|
req_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'requirements.txt'))
|
||||||
|
extra = ""
|
||||||
|
if sys.flags.no_user_site:
|
||||||
|
extra = "-s "
|
||||||
|
return f"Please install the updated requirements.txt file by running:\n{sys.executable} {extra}-m pip install -r {req_path}\n\nThis error is happening because the ComfyUI frontend is no longer shipped as part of the main repo but as a pip package instead.\n\nIf you are on the portable package you can run: update\\update_comfyui.bat to solve this problem"
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import comfyui_frontend_package
|
import comfyui_frontend_package
|
||||||
except ImportError:
|
except ImportError:
|
||||||
# TODO: Remove the check after roll out of 0.3.16
|
# TODO: Remove the check after roll out of 0.3.16
|
||||||
req_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'requirements.txt'))
|
logging.error(f"\n\n********** ERROR ***********\n\ncomfyui-frontend-package is not installed. {frontend_install_warning_message()}\n********** ERROR **********\n")
|
||||||
logging.error(f"\n\n********** ERROR ***********\n\ncomfyui-frontend-package is not installed. Please install the updated requirements.txt file by running:\n{sys.executable} -m pip install -r {req_path}\n\nThis error is happening because the ComfyUI frontend is no longer shipped as part of the main repo but as a pip package instead.\n\nIf you are on the portable package you can run: update\\update_comfyui.bat to solve this problem\n********** ERROR **********\n")
|
|
||||||
exit(-1)
|
exit(-1)
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
frontend_version = tuple(map(int, comfyui_frontend_package.__version__.split(".")))
|
||||||
|
except:
|
||||||
|
frontend_version = (0,)
|
||||||
|
pass
|
||||||
|
|
||||||
REQUEST_TIMEOUT = 10 # seconds
|
REQUEST_TIMEOUT = 10 # seconds
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import argparse
|
import argparse
|
||||||
import enum
|
import enum
|
||||||
import os
|
import os
|
||||||
from typing import Optional
|
|
||||||
import comfy.options
|
import comfy.options
|
||||||
|
|
||||||
|
|
||||||
@@ -166,13 +165,14 @@ parser.add_argument(
|
|||||||
""",
|
""",
|
||||||
)
|
)
|
||||||
|
|
||||||
def is_valid_directory(path: Optional[str]) -> Optional[str]:
|
def is_valid_directory(path: str) -> str:
|
||||||
"""Validate if the given path is a directory."""
|
"""Validate if the given path is a directory, and check permissions."""
|
||||||
if path is None:
|
if not os.path.exists(path):
|
||||||
return None
|
raise argparse.ArgumentTypeError(f"The path '{path}' does not exist.")
|
||||||
|
|
||||||
if not os.path.isdir(path):
|
if not os.path.isdir(path):
|
||||||
raise argparse.ArgumentTypeError(f"{path} is not a valid directory.")
|
raise argparse.ArgumentTypeError(f"'{path}' is not a directory.")
|
||||||
|
if not os.access(path, os.R_OK):
|
||||||
|
raise argparse.ArgumentTypeError(f"You do not have read permissions for '{path}'.")
|
||||||
return path
|
return path
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
|
|||||||
@@ -97,8 +97,12 @@ class CLIPTextModel_(torch.nn.Module):
|
|||||||
self.encoder = CLIPEncoder(num_layers, embed_dim, heads, intermediate_size, intermediate_activation, dtype, device, operations)
|
self.encoder = CLIPEncoder(num_layers, embed_dim, heads, intermediate_size, intermediate_activation, dtype, device, operations)
|
||||||
self.final_layer_norm = operations.LayerNorm(embed_dim, dtype=dtype, device=device)
|
self.final_layer_norm = operations.LayerNorm(embed_dim, dtype=dtype, device=device)
|
||||||
|
|
||||||
def forward(self, input_tokens, attention_mask=None, intermediate_output=None, final_layer_norm_intermediate=True, dtype=torch.float32):
|
def forward(self, input_tokens=None, attention_mask=None, embeds=None, num_tokens=None, intermediate_output=None, final_layer_norm_intermediate=True, dtype=torch.float32):
|
||||||
|
if embeds is not None:
|
||||||
|
x = embeds + comfy.ops.cast_to(self.embeddings.position_embedding.weight, dtype=dtype, device=embeds.device)
|
||||||
|
else:
|
||||||
x = self.embeddings(input_tokens, dtype=dtype)
|
x = self.embeddings(input_tokens, dtype=dtype)
|
||||||
|
|
||||||
mask = None
|
mask = None
|
||||||
if attention_mask is not None:
|
if attention_mask is not None:
|
||||||
mask = 1.0 - attention_mask.to(x.dtype).reshape((attention_mask.shape[0], 1, -1, attention_mask.shape[-1])).expand(attention_mask.shape[0], 1, attention_mask.shape[-1], attention_mask.shape[-1])
|
mask = 1.0 - attention_mask.to(x.dtype).reshape((attention_mask.shape[0], 1, -1, attention_mask.shape[-1])).expand(attention_mask.shape[0], 1, attention_mask.shape[-1], attention_mask.shape[-1])
|
||||||
@@ -116,6 +120,9 @@ class CLIPTextModel_(torch.nn.Module):
|
|||||||
if i is not None and final_layer_norm_intermediate:
|
if i is not None and final_layer_norm_intermediate:
|
||||||
i = self.final_layer_norm(i)
|
i = self.final_layer_norm(i)
|
||||||
|
|
||||||
|
if num_tokens is not None:
|
||||||
|
pooled_output = x[list(range(x.shape[0])), list(map(lambda a: a - 1, num_tokens))]
|
||||||
|
else:
|
||||||
pooled_output = x[torch.arange(x.shape[0], device=x.device), (torch.round(input_tokens).to(dtype=torch.int, device=x.device) == self.eos_token_id).int().argmax(dim=-1),]
|
pooled_output = x[torch.arange(x.shape[0], device=x.device), (torch.round(input_tokens).to(dtype=torch.int, device=x.device) == self.eos_token_id).int().argmax(dim=-1),]
|
||||||
return x, i, pooled_output
|
return x, i, pooled_output
|
||||||
|
|
||||||
@@ -204,6 +211,15 @@ class CLIPVision(torch.nn.Module):
|
|||||||
pooled_output = self.post_layernorm(x[:, 0, :])
|
pooled_output = self.post_layernorm(x[:, 0, :])
|
||||||
return x, i, pooled_output
|
return x, i, pooled_output
|
||||||
|
|
||||||
|
class LlavaProjector(torch.nn.Module):
|
||||||
|
def __init__(self, in_dim, out_dim, dtype, device, operations):
|
||||||
|
super().__init__()
|
||||||
|
self.linear_1 = operations.Linear(in_dim, out_dim, bias=True, device=device, dtype=dtype)
|
||||||
|
self.linear_2 = operations.Linear(out_dim, out_dim, bias=True, device=device, dtype=dtype)
|
||||||
|
|
||||||
|
def forward(self, x):
|
||||||
|
return self.linear_2(torch.nn.functional.gelu(self.linear_1(x[:, 1:])))
|
||||||
|
|
||||||
class CLIPVisionModelProjection(torch.nn.Module):
|
class CLIPVisionModelProjection(torch.nn.Module):
|
||||||
def __init__(self, config_dict, dtype, device, operations):
|
def __init__(self, config_dict, dtype, device, operations):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
@@ -213,7 +229,16 @@ class CLIPVisionModelProjection(torch.nn.Module):
|
|||||||
else:
|
else:
|
||||||
self.visual_projection = lambda a: a
|
self.visual_projection = lambda a: a
|
||||||
|
|
||||||
|
if "llava3" == config_dict.get("projector_type", None):
|
||||||
|
self.multi_modal_projector = LlavaProjector(config_dict["hidden_size"], 4096, dtype, device, operations)
|
||||||
|
else:
|
||||||
|
self.multi_modal_projector = None
|
||||||
|
|
||||||
def forward(self, *args, **kwargs):
|
def forward(self, *args, **kwargs):
|
||||||
x = self.vision_model(*args, **kwargs)
|
x = self.vision_model(*args, **kwargs)
|
||||||
out = self.visual_projection(x[2])
|
out = self.visual_projection(x[2])
|
||||||
return (x[0], x[1], out)
|
projected = None
|
||||||
|
if self.multi_modal_projector is not None:
|
||||||
|
projected = self.multi_modal_projector(x[1])
|
||||||
|
|
||||||
|
return (x[0], x[1], out, projected)
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ class ClipVisionModel():
|
|||||||
outputs["last_hidden_state"] = out[0].to(comfy.model_management.intermediate_device())
|
outputs["last_hidden_state"] = out[0].to(comfy.model_management.intermediate_device())
|
||||||
outputs["image_embeds"] = out[2].to(comfy.model_management.intermediate_device())
|
outputs["image_embeds"] = out[2].to(comfy.model_management.intermediate_device())
|
||||||
outputs["penultimate_hidden_states"] = out[1].to(comfy.model_management.intermediate_device())
|
outputs["penultimate_hidden_states"] = out[1].to(comfy.model_management.intermediate_device())
|
||||||
|
outputs["mm_projected"] = out[3]
|
||||||
return outputs
|
return outputs
|
||||||
|
|
||||||
def convert_to_transformers(sd, prefix):
|
def convert_to_transformers(sd, prefix):
|
||||||
@@ -104,6 +105,9 @@ def load_clipvision_from_sd(sd, prefix="", convert_keys=False):
|
|||||||
if sd["vision_model.encoder.layers.0.layer_norm1.weight"].shape[0] == 1152:
|
if sd["vision_model.encoder.layers.0.layer_norm1.weight"].shape[0] == 1152:
|
||||||
json_config = os.path.join(os.path.dirname(os.path.realpath(__file__)), "clip_vision_siglip_384.json")
|
json_config = os.path.join(os.path.dirname(os.path.realpath(__file__)), "clip_vision_siglip_384.json")
|
||||||
elif sd["vision_model.embeddings.position_embedding.weight"].shape[0] == 577:
|
elif sd["vision_model.embeddings.position_embedding.weight"].shape[0] == 577:
|
||||||
|
if "multi_modal_projector.linear_1.bias" in sd:
|
||||||
|
json_config = os.path.join(os.path.dirname(os.path.realpath(__file__)), "clip_vision_config_vitl_336_llava.json")
|
||||||
|
else:
|
||||||
json_config = os.path.join(os.path.dirname(os.path.realpath(__file__)), "clip_vision_config_vitl_336.json")
|
json_config = os.path.join(os.path.dirname(os.path.realpath(__file__)), "clip_vision_config_vitl_336.json")
|
||||||
else:
|
else:
|
||||||
json_config = os.path.join(os.path.dirname(os.path.realpath(__file__)), "clip_vision_config_vitl.json")
|
json_config = os.path.join(os.path.dirname(os.path.realpath(__file__)), "clip_vision_config_vitl.json")
|
||||||
|
|||||||
19
comfy/clip_vision_config_vitl_336_llava.json
Normal file
19
comfy/clip_vision_config_vitl_336_llava.json
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"attention_dropout": 0.0,
|
||||||
|
"dropout": 0.0,
|
||||||
|
"hidden_act": "quick_gelu",
|
||||||
|
"hidden_size": 1024,
|
||||||
|
"image_size": 336,
|
||||||
|
"initializer_factor": 1.0,
|
||||||
|
"initializer_range": 0.02,
|
||||||
|
"intermediate_size": 4096,
|
||||||
|
"layer_norm_eps": 1e-5,
|
||||||
|
"model_type": "clip_vision_model",
|
||||||
|
"num_attention_heads": 16,
|
||||||
|
"num_channels": 3,
|
||||||
|
"num_hidden_layers": 24,
|
||||||
|
"patch_size": 14,
|
||||||
|
"projection_dim": 768,
|
||||||
|
"projector_type": "llava3",
|
||||||
|
"torch_dtype": "float32"
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import torch
|
import torch
|
||||||
from typing import Callable, Protocol, TypedDict, Optional, List
|
from typing import Callable, Protocol, TypedDict, Optional, List
|
||||||
from .node_typing import IO, InputTypeDict, ComfyNodeABC, CheckLazyMixin
|
from .node_typing import IO, InputTypeDict, ComfyNodeABC, CheckLazyMixin, FileLocator
|
||||||
|
|
||||||
|
|
||||||
class UnetApplyFunction(Protocol):
|
class UnetApplyFunction(Protocol):
|
||||||
@@ -42,4 +42,5 @@ __all__ = [
|
|||||||
InputTypeDict.__name__,
|
InputTypeDict.__name__,
|
||||||
ComfyNodeABC.__name__,
|
ComfyNodeABC.__name__,
|
||||||
CheckLazyMixin.__name__,
|
CheckLazyMixin.__name__,
|
||||||
|
FileLocator.__name__,
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ class InputTypeOptions(TypedDict):
|
|||||||
# default: bool
|
# default: bool
|
||||||
label_on: str
|
label_on: str
|
||||||
"""The label to use in the UI when the bool is True (``BOOLEAN``)"""
|
"""The label to use in the UI when the bool is True (``BOOLEAN``)"""
|
||||||
label_on: str
|
label_off: str
|
||||||
"""The label to use in the UI when the bool is False (``BOOLEAN``)"""
|
"""The label to use in the UI when the bool is False (``BOOLEAN``)"""
|
||||||
# class InputTypeString(InputTypeOptions):
|
# class InputTypeString(InputTypeOptions):
|
||||||
# default: str
|
# default: str
|
||||||
@@ -134,6 +134,8 @@ class InputTypeOptions(TypedDict):
|
|||||||
"""
|
"""
|
||||||
remote: RemoteInputOptions
|
remote: RemoteInputOptions
|
||||||
"""Specifies the configuration for a remote input."""
|
"""Specifies the configuration for a remote input."""
|
||||||
|
control_after_generate: bool
|
||||||
|
"""Specifies whether a control widget should be added to the input, adding options to automatically change the value after each prompt is queued. Currently only used for INT and COMBO types."""
|
||||||
|
|
||||||
|
|
||||||
class HiddenInputTypeDict(TypedDict):
|
class HiddenInputTypeDict(TypedDict):
|
||||||
@@ -293,3 +295,14 @@ class CheckLazyMixin:
|
|||||||
|
|
||||||
need = [name for name in kwargs if kwargs[name] is None]
|
need = [name for name in kwargs if kwargs[name] is None]
|
||||||
return need
|
return need
|
||||||
|
|
||||||
|
|
||||||
|
class FileLocator(TypedDict):
|
||||||
|
"""Provides type hinting for the file location"""
|
||||||
|
|
||||||
|
filename: str
|
||||||
|
"""The filename of the file."""
|
||||||
|
subfolder: str
|
||||||
|
"""The subfolder of the file."""
|
||||||
|
type: Literal["input", "output", "temp"]
|
||||||
|
"""The root folder of the file."""
|
||||||
|
|||||||
@@ -19,6 +19,10 @@
|
|||||||
import torch
|
import torch
|
||||||
from torch import nn
|
from torch import nn
|
||||||
from torch.autograd import Function
|
from torch.autograd import Function
|
||||||
|
import comfy.ops
|
||||||
|
|
||||||
|
ops = comfy.ops.disable_weight_init
|
||||||
|
|
||||||
|
|
||||||
class vector_quantize(Function):
|
class vector_quantize(Function):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@@ -121,15 +125,15 @@ class ResBlock(nn.Module):
|
|||||||
self.norm1 = nn.LayerNorm(c, elementwise_affine=False, eps=1e-6)
|
self.norm1 = nn.LayerNorm(c, elementwise_affine=False, eps=1e-6)
|
||||||
self.depthwise = nn.Sequential(
|
self.depthwise = nn.Sequential(
|
||||||
nn.ReplicationPad2d(1),
|
nn.ReplicationPad2d(1),
|
||||||
nn.Conv2d(c, c, kernel_size=3, groups=c)
|
ops.Conv2d(c, c, kernel_size=3, groups=c)
|
||||||
)
|
)
|
||||||
|
|
||||||
# channelwise
|
# channelwise
|
||||||
self.norm2 = nn.LayerNorm(c, elementwise_affine=False, eps=1e-6)
|
self.norm2 = nn.LayerNorm(c, elementwise_affine=False, eps=1e-6)
|
||||||
self.channelwise = nn.Sequential(
|
self.channelwise = nn.Sequential(
|
||||||
nn.Linear(c, c_hidden),
|
ops.Linear(c, c_hidden),
|
||||||
nn.GELU(),
|
nn.GELU(),
|
||||||
nn.Linear(c_hidden, c),
|
ops.Linear(c_hidden, c),
|
||||||
)
|
)
|
||||||
|
|
||||||
self.gammas = nn.Parameter(torch.zeros(6), requires_grad=True)
|
self.gammas = nn.Parameter(torch.zeros(6), requires_grad=True)
|
||||||
@@ -171,16 +175,16 @@ class StageA(nn.Module):
|
|||||||
# Encoder blocks
|
# Encoder blocks
|
||||||
self.in_block = nn.Sequential(
|
self.in_block = nn.Sequential(
|
||||||
nn.PixelUnshuffle(2),
|
nn.PixelUnshuffle(2),
|
||||||
nn.Conv2d(3 * 4, c_levels[0], kernel_size=1)
|
ops.Conv2d(3 * 4, c_levels[0], kernel_size=1)
|
||||||
)
|
)
|
||||||
down_blocks = []
|
down_blocks = []
|
||||||
for i in range(levels):
|
for i in range(levels):
|
||||||
if i > 0:
|
if i > 0:
|
||||||
down_blocks.append(nn.Conv2d(c_levels[i - 1], c_levels[i], kernel_size=4, stride=2, padding=1))
|
down_blocks.append(ops.Conv2d(c_levels[i - 1], c_levels[i], kernel_size=4, stride=2, padding=1))
|
||||||
block = ResBlock(c_levels[i], c_levels[i] * 4)
|
block = ResBlock(c_levels[i], c_levels[i] * 4)
|
||||||
down_blocks.append(block)
|
down_blocks.append(block)
|
||||||
down_blocks.append(nn.Sequential(
|
down_blocks.append(nn.Sequential(
|
||||||
nn.Conv2d(c_levels[-1], c_latent, kernel_size=1, bias=False),
|
ops.Conv2d(c_levels[-1], c_latent, kernel_size=1, bias=False),
|
||||||
nn.BatchNorm2d(c_latent), # then normalize them to have mean 0 and std 1
|
nn.BatchNorm2d(c_latent), # then normalize them to have mean 0 and std 1
|
||||||
))
|
))
|
||||||
self.down_blocks = nn.Sequential(*down_blocks)
|
self.down_blocks = nn.Sequential(*down_blocks)
|
||||||
@@ -191,7 +195,7 @@ class StageA(nn.Module):
|
|||||||
|
|
||||||
# Decoder blocks
|
# Decoder blocks
|
||||||
up_blocks = [nn.Sequential(
|
up_blocks = [nn.Sequential(
|
||||||
nn.Conv2d(c_latent, c_levels[-1], kernel_size=1)
|
ops.Conv2d(c_latent, c_levels[-1], kernel_size=1)
|
||||||
)]
|
)]
|
||||||
for i in range(levels):
|
for i in range(levels):
|
||||||
for j in range(bottleneck_blocks if i == 0 else 1):
|
for j in range(bottleneck_blocks if i == 0 else 1):
|
||||||
@@ -199,11 +203,11 @@ class StageA(nn.Module):
|
|||||||
up_blocks.append(block)
|
up_blocks.append(block)
|
||||||
if i < levels - 1:
|
if i < levels - 1:
|
||||||
up_blocks.append(
|
up_blocks.append(
|
||||||
nn.ConvTranspose2d(c_levels[levels - 1 - i], c_levels[levels - 2 - i], kernel_size=4, stride=2,
|
ops.ConvTranspose2d(c_levels[levels - 1 - i], c_levels[levels - 2 - i], kernel_size=4, stride=2,
|
||||||
padding=1))
|
padding=1))
|
||||||
self.up_blocks = nn.Sequential(*up_blocks)
|
self.up_blocks = nn.Sequential(*up_blocks)
|
||||||
self.out_block = nn.Sequential(
|
self.out_block = nn.Sequential(
|
||||||
nn.Conv2d(c_levels[0], 3 * 4, kernel_size=1),
|
ops.Conv2d(c_levels[0], 3 * 4, kernel_size=1),
|
||||||
nn.PixelShuffle(2),
|
nn.PixelShuffle(2),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -232,17 +236,17 @@ class Discriminator(nn.Module):
|
|||||||
super().__init__()
|
super().__init__()
|
||||||
d = max(depth - 3, 3)
|
d = max(depth - 3, 3)
|
||||||
layers = [
|
layers = [
|
||||||
nn.utils.spectral_norm(nn.Conv2d(c_in, c_hidden // (2 ** d), kernel_size=3, stride=2, padding=1)),
|
nn.utils.spectral_norm(ops.Conv2d(c_in, c_hidden // (2 ** d), kernel_size=3, stride=2, padding=1)),
|
||||||
nn.LeakyReLU(0.2),
|
nn.LeakyReLU(0.2),
|
||||||
]
|
]
|
||||||
for i in range(depth - 1):
|
for i in range(depth - 1):
|
||||||
c_in = c_hidden // (2 ** max((d - i), 0))
|
c_in = c_hidden // (2 ** max((d - i), 0))
|
||||||
c_out = c_hidden // (2 ** max((d - 1 - i), 0))
|
c_out = c_hidden // (2 ** max((d - 1 - i), 0))
|
||||||
layers.append(nn.utils.spectral_norm(nn.Conv2d(c_in, c_out, kernel_size=3, stride=2, padding=1)))
|
layers.append(nn.utils.spectral_norm(ops.Conv2d(c_in, c_out, kernel_size=3, stride=2, padding=1)))
|
||||||
layers.append(nn.InstanceNorm2d(c_out))
|
layers.append(nn.InstanceNorm2d(c_out))
|
||||||
layers.append(nn.LeakyReLU(0.2))
|
layers.append(nn.LeakyReLU(0.2))
|
||||||
self.encoder = nn.Sequential(*layers)
|
self.encoder = nn.Sequential(*layers)
|
||||||
self.shuffle = nn.Conv2d((c_hidden + c_cond) if c_cond > 0 else c_hidden, 1, kernel_size=1)
|
self.shuffle = ops.Conv2d((c_hidden + c_cond) if c_cond > 0 else c_hidden, 1, kernel_size=1)
|
||||||
self.logits = nn.Sigmoid()
|
self.logits = nn.Sigmoid()
|
||||||
|
|
||||||
def forward(self, x, cond=None):
|
def forward(self, x, cond=None):
|
||||||
|
|||||||
@@ -19,6 +19,9 @@ import torch
|
|||||||
import torchvision
|
import torchvision
|
||||||
from torch import nn
|
from torch import nn
|
||||||
|
|
||||||
|
import comfy.ops
|
||||||
|
|
||||||
|
ops = comfy.ops.disable_weight_init
|
||||||
|
|
||||||
# EfficientNet
|
# EfficientNet
|
||||||
class EfficientNetEncoder(nn.Module):
|
class EfficientNetEncoder(nn.Module):
|
||||||
@@ -26,7 +29,7 @@ class EfficientNetEncoder(nn.Module):
|
|||||||
super().__init__()
|
super().__init__()
|
||||||
self.backbone = torchvision.models.efficientnet_v2_s().features.eval()
|
self.backbone = torchvision.models.efficientnet_v2_s().features.eval()
|
||||||
self.mapper = nn.Sequential(
|
self.mapper = nn.Sequential(
|
||||||
nn.Conv2d(1280, c_latent, kernel_size=1, bias=False),
|
ops.Conv2d(1280, c_latent, kernel_size=1, bias=False),
|
||||||
nn.BatchNorm2d(c_latent, affine=False), # then normalize them to have mean 0 and std 1
|
nn.BatchNorm2d(c_latent, affine=False), # then normalize them to have mean 0 and std 1
|
||||||
)
|
)
|
||||||
self.mean = nn.Parameter(torch.tensor([0.485, 0.456, 0.406]))
|
self.mean = nn.Parameter(torch.tensor([0.485, 0.456, 0.406]))
|
||||||
@@ -34,7 +37,7 @@ class EfficientNetEncoder(nn.Module):
|
|||||||
|
|
||||||
def forward(self, x):
|
def forward(self, x):
|
||||||
x = x * 0.5 + 0.5
|
x = x * 0.5 + 0.5
|
||||||
x = (x - self.mean.view([3,1,1])) / self.std.view([3,1,1])
|
x = (x - self.mean.view([3,1,1]).to(device=x.device, dtype=x.dtype)) / self.std.view([3,1,1]).to(device=x.device, dtype=x.dtype)
|
||||||
o = self.mapper(self.backbone(x))
|
o = self.mapper(self.backbone(x))
|
||||||
return o
|
return o
|
||||||
|
|
||||||
@@ -44,39 +47,39 @@ class Previewer(nn.Module):
|
|||||||
def __init__(self, c_in=16, c_hidden=512, c_out=3):
|
def __init__(self, c_in=16, c_hidden=512, c_out=3):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.blocks = nn.Sequential(
|
self.blocks = nn.Sequential(
|
||||||
nn.Conv2d(c_in, c_hidden, kernel_size=1), # 16 channels to 512 channels
|
ops.Conv2d(c_in, c_hidden, kernel_size=1), # 16 channels to 512 channels
|
||||||
nn.GELU(),
|
nn.GELU(),
|
||||||
nn.BatchNorm2d(c_hidden),
|
nn.BatchNorm2d(c_hidden),
|
||||||
|
|
||||||
nn.Conv2d(c_hidden, c_hidden, kernel_size=3, padding=1),
|
ops.Conv2d(c_hidden, c_hidden, kernel_size=3, padding=1),
|
||||||
nn.GELU(),
|
nn.GELU(),
|
||||||
nn.BatchNorm2d(c_hidden),
|
nn.BatchNorm2d(c_hidden),
|
||||||
|
|
||||||
nn.ConvTranspose2d(c_hidden, c_hidden // 2, kernel_size=2, stride=2), # 16 -> 32
|
ops.ConvTranspose2d(c_hidden, c_hidden // 2, kernel_size=2, stride=2), # 16 -> 32
|
||||||
nn.GELU(),
|
nn.GELU(),
|
||||||
nn.BatchNorm2d(c_hidden // 2),
|
nn.BatchNorm2d(c_hidden // 2),
|
||||||
|
|
||||||
nn.Conv2d(c_hidden // 2, c_hidden // 2, kernel_size=3, padding=1),
|
ops.Conv2d(c_hidden // 2, c_hidden // 2, kernel_size=3, padding=1),
|
||||||
nn.GELU(),
|
nn.GELU(),
|
||||||
nn.BatchNorm2d(c_hidden // 2),
|
nn.BatchNorm2d(c_hidden // 2),
|
||||||
|
|
||||||
nn.ConvTranspose2d(c_hidden // 2, c_hidden // 4, kernel_size=2, stride=2), # 32 -> 64
|
ops.ConvTranspose2d(c_hidden // 2, c_hidden // 4, kernel_size=2, stride=2), # 32 -> 64
|
||||||
nn.GELU(),
|
nn.GELU(),
|
||||||
nn.BatchNorm2d(c_hidden // 4),
|
nn.BatchNorm2d(c_hidden // 4),
|
||||||
|
|
||||||
nn.Conv2d(c_hidden // 4, c_hidden // 4, kernel_size=3, padding=1),
|
ops.Conv2d(c_hidden // 4, c_hidden // 4, kernel_size=3, padding=1),
|
||||||
nn.GELU(),
|
nn.GELU(),
|
||||||
nn.BatchNorm2d(c_hidden // 4),
|
nn.BatchNorm2d(c_hidden // 4),
|
||||||
|
|
||||||
nn.ConvTranspose2d(c_hidden // 4, c_hidden // 4, kernel_size=2, stride=2), # 64 -> 128
|
ops.ConvTranspose2d(c_hidden // 4, c_hidden // 4, kernel_size=2, stride=2), # 64 -> 128
|
||||||
nn.GELU(),
|
nn.GELU(),
|
||||||
nn.BatchNorm2d(c_hidden // 4),
|
nn.BatchNorm2d(c_hidden // 4),
|
||||||
|
|
||||||
nn.Conv2d(c_hidden // 4, c_hidden // 4, kernel_size=3, padding=1),
|
ops.Conv2d(c_hidden // 4, c_hidden // 4, kernel_size=3, padding=1),
|
||||||
nn.GELU(),
|
nn.GELU(),
|
||||||
nn.BatchNorm2d(c_hidden // 4),
|
nn.BatchNorm2d(c_hidden // 4),
|
||||||
|
|
||||||
nn.Conv2d(c_hidden // 4, c_out, kernel_size=1),
|
ops.Conv2d(c_hidden // 4, c_out, kernel_size=1),
|
||||||
)
|
)
|
||||||
|
|
||||||
def forward(self, x):
|
def forward(self, x):
|
||||||
|
|||||||
@@ -105,7 +105,9 @@ class Modulation(nn.Module):
|
|||||||
self.lin = operations.Linear(dim, self.multiplier * dim, bias=True, dtype=dtype, device=device)
|
self.lin = operations.Linear(dim, self.multiplier * dim, bias=True, dtype=dtype, device=device)
|
||||||
|
|
||||||
def forward(self, vec: Tensor) -> tuple:
|
def forward(self, vec: Tensor) -> tuple:
|
||||||
out = self.lin(nn.functional.silu(vec))[:, None, :].chunk(self.multiplier, dim=-1)
|
if vec.ndim == 2:
|
||||||
|
vec = vec[:, None, :]
|
||||||
|
out = self.lin(nn.functional.silu(vec)).chunk(self.multiplier, dim=-1)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
ModulationOut(*out[:3]),
|
ModulationOut(*out[:3]),
|
||||||
@@ -113,6 +115,20 @@ class Modulation(nn.Module):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def apply_mod(tensor, m_mult, m_add=None, modulation_dims=None):
|
||||||
|
if modulation_dims is None:
|
||||||
|
if m_add is not None:
|
||||||
|
return tensor * m_mult + m_add
|
||||||
|
else:
|
||||||
|
return tensor * m_mult
|
||||||
|
else:
|
||||||
|
for d in modulation_dims:
|
||||||
|
tensor[:, d[0]:d[1]] *= m_mult[:, d[2]]
|
||||||
|
if m_add is not None:
|
||||||
|
tensor[:, d[0]:d[1]] += m_add[:, d[2]]
|
||||||
|
return tensor
|
||||||
|
|
||||||
|
|
||||||
class DoubleStreamBlock(nn.Module):
|
class DoubleStreamBlock(nn.Module):
|
||||||
def __init__(self, hidden_size: int, num_heads: int, mlp_ratio: float, qkv_bias: bool = False, flipped_img_txt=False, dtype=None, device=None, operations=None):
|
def __init__(self, hidden_size: int, num_heads: int, mlp_ratio: float, qkv_bias: bool = False, flipped_img_txt=False, dtype=None, device=None, operations=None):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
@@ -143,20 +159,20 @@ class DoubleStreamBlock(nn.Module):
|
|||||||
)
|
)
|
||||||
self.flipped_img_txt = flipped_img_txt
|
self.flipped_img_txt = flipped_img_txt
|
||||||
|
|
||||||
def forward(self, img: Tensor, txt: Tensor, vec: Tensor, pe: Tensor, attn_mask=None):
|
def forward(self, img: Tensor, txt: Tensor, vec: Tensor, pe: Tensor, attn_mask=None, modulation_dims=None):
|
||||||
img_mod1, img_mod2 = self.img_mod(vec)
|
img_mod1, img_mod2 = self.img_mod(vec)
|
||||||
txt_mod1, txt_mod2 = self.txt_mod(vec)
|
txt_mod1, txt_mod2 = self.txt_mod(vec)
|
||||||
|
|
||||||
# prepare image for attention
|
# prepare image for attention
|
||||||
img_modulated = self.img_norm1(img)
|
img_modulated = self.img_norm1(img)
|
||||||
img_modulated = (1 + img_mod1.scale) * img_modulated + img_mod1.shift
|
img_modulated = apply_mod(img_modulated, (1 + img_mod1.scale), img_mod1.shift, modulation_dims)
|
||||||
img_qkv = self.img_attn.qkv(img_modulated)
|
img_qkv = self.img_attn.qkv(img_modulated)
|
||||||
img_q, img_k, img_v = img_qkv.view(img_qkv.shape[0], img_qkv.shape[1], 3, self.num_heads, -1).permute(2, 0, 3, 1, 4)
|
img_q, img_k, img_v = img_qkv.view(img_qkv.shape[0], img_qkv.shape[1], 3, self.num_heads, -1).permute(2, 0, 3, 1, 4)
|
||||||
img_q, img_k = self.img_attn.norm(img_q, img_k, img_v)
|
img_q, img_k = self.img_attn.norm(img_q, img_k, img_v)
|
||||||
|
|
||||||
# prepare txt for attention
|
# prepare txt for attention
|
||||||
txt_modulated = self.txt_norm1(txt)
|
txt_modulated = self.txt_norm1(txt)
|
||||||
txt_modulated = (1 + txt_mod1.scale) * txt_modulated + txt_mod1.shift
|
txt_modulated = apply_mod(txt_modulated, (1 + txt_mod1.scale), txt_mod1.shift, modulation_dims)
|
||||||
txt_qkv = self.txt_attn.qkv(txt_modulated)
|
txt_qkv = self.txt_attn.qkv(txt_modulated)
|
||||||
txt_q, txt_k, txt_v = txt_qkv.view(txt_qkv.shape[0], txt_qkv.shape[1], 3, self.num_heads, -1).permute(2, 0, 3, 1, 4)
|
txt_q, txt_k, txt_v = txt_qkv.view(txt_qkv.shape[0], txt_qkv.shape[1], 3, self.num_heads, -1).permute(2, 0, 3, 1, 4)
|
||||||
txt_q, txt_k = self.txt_attn.norm(txt_q, txt_k, txt_v)
|
txt_q, txt_k = self.txt_attn.norm(txt_q, txt_k, txt_v)
|
||||||
@@ -179,12 +195,12 @@ class DoubleStreamBlock(nn.Module):
|
|||||||
txt_attn, img_attn = attn[:, : txt.shape[1]], attn[:, txt.shape[1]:]
|
txt_attn, img_attn = attn[:, : txt.shape[1]], attn[:, txt.shape[1]:]
|
||||||
|
|
||||||
# calculate the img bloks
|
# calculate the img bloks
|
||||||
img = img + img_mod1.gate * self.img_attn.proj(img_attn)
|
img = img + apply_mod(self.img_attn.proj(img_attn), img_mod1.gate, None, modulation_dims)
|
||||||
img = img + img_mod2.gate * self.img_mlp((1 + img_mod2.scale) * self.img_norm2(img) + img_mod2.shift)
|
img = img + apply_mod(self.img_mlp(apply_mod(self.img_norm2(img), (1 + img_mod2.scale), img_mod2.shift, modulation_dims)), img_mod2.gate, None, modulation_dims)
|
||||||
|
|
||||||
# calculate the txt bloks
|
# calculate the txt bloks
|
||||||
txt += txt_mod1.gate * self.txt_attn.proj(txt_attn)
|
txt += apply_mod(self.txt_attn.proj(txt_attn), txt_mod1.gate, None, modulation_dims)
|
||||||
txt += txt_mod2.gate * self.txt_mlp((1 + txt_mod2.scale) * self.txt_norm2(txt) + txt_mod2.shift)
|
txt += apply_mod(self.txt_mlp(apply_mod(self.txt_norm2(txt), (1 + txt_mod2.scale), txt_mod2.shift, modulation_dims)), txt_mod2.gate, None, modulation_dims)
|
||||||
|
|
||||||
if txt.dtype == torch.float16:
|
if txt.dtype == torch.float16:
|
||||||
txt = torch.nan_to_num(txt, nan=0.0, posinf=65504, neginf=-65504)
|
txt = torch.nan_to_num(txt, nan=0.0, posinf=65504, neginf=-65504)
|
||||||
@@ -228,9 +244,9 @@ class SingleStreamBlock(nn.Module):
|
|||||||
self.mlp_act = nn.GELU(approximate="tanh")
|
self.mlp_act = nn.GELU(approximate="tanh")
|
||||||
self.modulation = Modulation(hidden_size, double=False, dtype=dtype, device=device, operations=operations)
|
self.modulation = Modulation(hidden_size, double=False, dtype=dtype, device=device, operations=operations)
|
||||||
|
|
||||||
def forward(self, x: Tensor, vec: Tensor, pe: Tensor, attn_mask=None) -> Tensor:
|
def forward(self, x: Tensor, vec: Tensor, pe: Tensor, attn_mask=None, modulation_dims=None) -> Tensor:
|
||||||
mod, _ = self.modulation(vec)
|
mod, _ = self.modulation(vec)
|
||||||
qkv, mlp = torch.split(self.linear1((1 + mod.scale) * self.pre_norm(x) + mod.shift), [3 * self.hidden_size, self.mlp_hidden_dim], dim=-1)
|
qkv, mlp = torch.split(self.linear1(apply_mod(self.pre_norm(x), (1 + mod.scale), mod.shift, modulation_dims)), [3 * self.hidden_size, self.mlp_hidden_dim], dim=-1)
|
||||||
|
|
||||||
q, k, v = qkv.view(qkv.shape[0], qkv.shape[1], 3, self.num_heads, -1).permute(2, 0, 3, 1, 4)
|
q, k, v = qkv.view(qkv.shape[0], qkv.shape[1], 3, self.num_heads, -1).permute(2, 0, 3, 1, 4)
|
||||||
q, k = self.norm(q, k, v)
|
q, k = self.norm(q, k, v)
|
||||||
@@ -239,7 +255,7 @@ class SingleStreamBlock(nn.Module):
|
|||||||
attn = attention(q, k, v, pe=pe, mask=attn_mask)
|
attn = attention(q, k, v, pe=pe, mask=attn_mask)
|
||||||
# compute activation in mlp stream, cat again and run second linear layer
|
# compute activation in mlp stream, cat again and run second linear layer
|
||||||
output = self.linear2(torch.cat((attn, self.mlp_act(mlp)), 2))
|
output = self.linear2(torch.cat((attn, self.mlp_act(mlp)), 2))
|
||||||
x += mod.gate * output
|
x += apply_mod(output, mod.gate, None, modulation_dims)
|
||||||
if x.dtype == torch.float16:
|
if x.dtype == torch.float16:
|
||||||
x = torch.nan_to_num(x, nan=0.0, posinf=65504, neginf=-65504)
|
x = torch.nan_to_num(x, nan=0.0, posinf=65504, neginf=-65504)
|
||||||
return x
|
return x
|
||||||
@@ -252,8 +268,11 @@ class LastLayer(nn.Module):
|
|||||||
self.linear = operations.Linear(hidden_size, patch_size * patch_size * out_channels, bias=True, dtype=dtype, device=device)
|
self.linear = operations.Linear(hidden_size, patch_size * patch_size * out_channels, bias=True, dtype=dtype, device=device)
|
||||||
self.adaLN_modulation = nn.Sequential(nn.SiLU(), operations.Linear(hidden_size, 2 * hidden_size, bias=True, dtype=dtype, device=device))
|
self.adaLN_modulation = nn.Sequential(nn.SiLU(), operations.Linear(hidden_size, 2 * hidden_size, bias=True, dtype=dtype, device=device))
|
||||||
|
|
||||||
def forward(self, x: Tensor, vec: Tensor) -> Tensor:
|
def forward(self, x: Tensor, vec: Tensor, modulation_dims=None) -> Tensor:
|
||||||
shift, scale = self.adaLN_modulation(vec).chunk(2, dim=1)
|
if vec.ndim == 2:
|
||||||
x = (1 + scale[:, None, :]) * self.norm_final(x) + shift[:, None, :]
|
vec = vec[:, None, :]
|
||||||
|
|
||||||
|
shift, scale = self.adaLN_modulation(vec).chunk(2, dim=-1)
|
||||||
|
x = apply_mod(self.norm_final(x), (1 + scale), shift, modulation_dims)
|
||||||
x = self.linear(x)
|
x = self.linear(x)
|
||||||
return x
|
return x
|
||||||
|
|||||||
@@ -227,6 +227,7 @@ class HunyuanVideo(nn.Module):
|
|||||||
timesteps: Tensor,
|
timesteps: Tensor,
|
||||||
y: Tensor,
|
y: Tensor,
|
||||||
guidance: Tensor = None,
|
guidance: Tensor = None,
|
||||||
|
guiding_frame_index=None,
|
||||||
control=None,
|
control=None,
|
||||||
transformer_options={},
|
transformer_options={},
|
||||||
) -> Tensor:
|
) -> Tensor:
|
||||||
@@ -237,7 +238,15 @@ class HunyuanVideo(nn.Module):
|
|||||||
img = self.img_in(img)
|
img = self.img_in(img)
|
||||||
vec = self.time_in(timestep_embedding(timesteps, 256, time_factor=1.0).to(img.dtype))
|
vec = self.time_in(timestep_embedding(timesteps, 256, time_factor=1.0).to(img.dtype))
|
||||||
|
|
||||||
|
if guiding_frame_index is not None:
|
||||||
|
token_replace_vec = self.time_in(timestep_embedding(guiding_frame_index, 256, time_factor=1.0))
|
||||||
|
vec_ = self.vector_in(y[:, :self.params.vec_in_dim])
|
||||||
|
vec = torch.cat([(vec_ + token_replace_vec).unsqueeze(1), (vec_ + vec).unsqueeze(1)], dim=1)
|
||||||
|
frame_tokens = (initial_shape[-1] // self.patch_size[-1]) * (initial_shape[-2] // self.patch_size[-2])
|
||||||
|
modulation_dims = [(0, frame_tokens, 0), (frame_tokens, None, 1)]
|
||||||
|
else:
|
||||||
vec = vec + self.vector_in(y[:, :self.params.vec_in_dim])
|
vec = vec + self.vector_in(y[:, :self.params.vec_in_dim])
|
||||||
|
modulation_dims = None
|
||||||
|
|
||||||
if self.params.guidance_embed:
|
if self.params.guidance_embed:
|
||||||
if guidance is not None:
|
if guidance is not None:
|
||||||
@@ -271,7 +280,7 @@ class HunyuanVideo(nn.Module):
|
|||||||
txt = out["txt"]
|
txt = out["txt"]
|
||||||
img = out["img"]
|
img = out["img"]
|
||||||
else:
|
else:
|
||||||
img, txt = block(img=img, txt=txt, vec=vec, pe=pe, attn_mask=attn_mask)
|
img, txt = block(img=img, txt=txt, vec=vec, pe=pe, attn_mask=attn_mask, modulation_dims=modulation_dims)
|
||||||
|
|
||||||
if control is not None: # Controlnet
|
if control is not None: # Controlnet
|
||||||
control_i = control.get("input")
|
control_i = control.get("input")
|
||||||
@@ -292,7 +301,7 @@ class HunyuanVideo(nn.Module):
|
|||||||
out = blocks_replace[("single_block", i)]({"img": img, "vec": vec, "pe": pe, "attention_mask": attn_mask}, {"original_block": block_wrap})
|
out = blocks_replace[("single_block", i)]({"img": img, "vec": vec, "pe": pe, "attention_mask": attn_mask}, {"original_block": block_wrap})
|
||||||
img = out["img"]
|
img = out["img"]
|
||||||
else:
|
else:
|
||||||
img = block(img, vec=vec, pe=pe, attn_mask=attn_mask)
|
img = block(img, vec=vec, pe=pe, attn_mask=attn_mask, modulation_dims=modulation_dims)
|
||||||
|
|
||||||
if control is not None: # Controlnet
|
if control is not None: # Controlnet
|
||||||
control_o = control.get("output")
|
control_o = control.get("output")
|
||||||
@@ -303,7 +312,7 @@ class HunyuanVideo(nn.Module):
|
|||||||
|
|
||||||
img = img[:, : img_len]
|
img = img[:, : img_len]
|
||||||
|
|
||||||
img = self.final_layer(img, vec) # (N, T, patch_size ** 2 * out_channels)
|
img = self.final_layer(img, vec, modulation_dims=modulation_dims) # (N, T, patch_size ** 2 * out_channels)
|
||||||
|
|
||||||
shape = initial_shape[-3:]
|
shape = initial_shape[-3:]
|
||||||
for i in range(len(shape)):
|
for i in range(len(shape)):
|
||||||
@@ -313,7 +322,7 @@ class HunyuanVideo(nn.Module):
|
|||||||
img = img.reshape(initial_shape[0], self.out_channels, initial_shape[2], initial_shape[3], initial_shape[4])
|
img = img.reshape(initial_shape[0], self.out_channels, initial_shape[2], initial_shape[3], initial_shape[4])
|
||||||
return img
|
return img
|
||||||
|
|
||||||
def forward(self, x, timestep, context, y, guidance=None, attention_mask=None, control=None, transformer_options={}, **kwargs):
|
def forward(self, x, timestep, context, y, guidance=None, attention_mask=None, guiding_frame_index=None, control=None, transformer_options={}, **kwargs):
|
||||||
bs, c, t, h, w = x.shape
|
bs, c, t, h, w = x.shape
|
||||||
patch_size = self.patch_size
|
patch_size = self.patch_size
|
||||||
t_len = ((t + (patch_size[0] // 2)) // patch_size[0])
|
t_len = ((t + (patch_size[0] // 2)) // patch_size[0])
|
||||||
@@ -325,5 +334,5 @@ class HunyuanVideo(nn.Module):
|
|||||||
img_ids[:, :, :, 2] = img_ids[:, :, :, 2] + torch.linspace(0, w_len - 1, steps=w_len, device=x.device, dtype=x.dtype).reshape(1, 1, -1)
|
img_ids[:, :, :, 2] = img_ids[:, :, :, 2] + torch.linspace(0, w_len - 1, steps=w_len, device=x.device, dtype=x.dtype).reshape(1, 1, -1)
|
||||||
img_ids = repeat(img_ids, "t h w c -> b (t h w) c", b=bs)
|
img_ids = repeat(img_ids, "t h w c -> b (t h w) c", b=bs)
|
||||||
txt_ids = torch.zeros((bs, context.shape[1], 3), device=x.device, dtype=x.dtype)
|
txt_ids = torch.zeros((bs, context.shape[1], 3), device=x.device, dtype=x.dtype)
|
||||||
out = self.forward_orig(x, img_ids, context, txt_ids, attention_mask, timestep, y, guidance, control, transformer_options)
|
out = self.forward_orig(x, img_ids, context, txt_ids, attention_mask, timestep, y, guidance, guiding_frame_index, control, transformer_options)
|
||||||
return out
|
return out
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ class BaseModel(torch.nn.Module):
|
|||||||
|
|
||||||
if not unet_config.get("disable_unet_model_creation", False):
|
if not unet_config.get("disable_unet_model_creation", False):
|
||||||
if model_config.custom_operations is None:
|
if model_config.custom_operations is None:
|
||||||
fp8 = model_config.optimizations.get("fp8", model_config.scaled_fp8 is not None)
|
fp8 = model_config.optimizations.get("fp8", False)
|
||||||
operations = comfy.ops.pick_operations(unet_config.get("dtype", None), self.manual_cast_dtype, fp8_optimizations=fp8, scaled_fp8=model_config.scaled_fp8)
|
operations = comfy.ops.pick_operations(unet_config.get("dtype", None), self.manual_cast_dtype, fp8_optimizations=fp8, scaled_fp8=model_config.scaled_fp8)
|
||||||
else:
|
else:
|
||||||
operations = model_config.custom_operations
|
operations = model_config.custom_operations
|
||||||
@@ -898,13 +898,31 @@ class HunyuanVideo(BaseModel):
|
|||||||
guidance = kwargs.get("guidance", 6.0)
|
guidance = kwargs.get("guidance", 6.0)
|
||||||
if guidance is not None:
|
if guidance is not None:
|
||||||
out['guidance'] = comfy.conds.CONDRegular(torch.FloatTensor([guidance]))
|
out['guidance'] = comfy.conds.CONDRegular(torch.FloatTensor([guidance]))
|
||||||
|
|
||||||
|
guiding_frame_index = kwargs.get("guiding_frame_index", None)
|
||||||
|
if guiding_frame_index is not None:
|
||||||
|
out['guiding_frame_index'] = comfy.conds.CONDRegular(torch.FloatTensor([guiding_frame_index]))
|
||||||
|
|
||||||
return out
|
return out
|
||||||
|
|
||||||
|
def scale_latent_inpaint(self, latent_image, **kwargs):
|
||||||
|
return latent_image
|
||||||
|
|
||||||
|
class HunyuanVideoI2V(HunyuanVideo):
|
||||||
|
def __init__(self, model_config, model_type=ModelType.FLOW, device=None):
|
||||||
|
super().__init__(model_config, model_type, device=device)
|
||||||
|
self.concat_keys = ("concat_image", "mask_inverted")
|
||||||
|
|
||||||
|
def scale_latent_inpaint(self, latent_image, **kwargs):
|
||||||
|
return super().scale_latent_inpaint(latent_image=latent_image, **kwargs)
|
||||||
|
|
||||||
class HunyuanVideoSkyreelsI2V(HunyuanVideo):
|
class HunyuanVideoSkyreelsI2V(HunyuanVideo):
|
||||||
def __init__(self, model_config, model_type=ModelType.FLOW, device=None):
|
def __init__(self, model_config, model_type=ModelType.FLOW, device=None):
|
||||||
super().__init__(model_config, model_type, device=device)
|
super().__init__(model_config, model_type, device=device)
|
||||||
self.concat_keys = ("concat_image",)
|
self.concat_keys = ("concat_image",)
|
||||||
|
|
||||||
|
def scale_latent_inpaint(self, latent_image, **kwargs):
|
||||||
|
return super().scale_latent_inpaint(latent_image=latent_image, **kwargs)
|
||||||
|
|
||||||
class CosmosVideo(BaseModel):
|
class CosmosVideo(BaseModel):
|
||||||
def __init__(self, model_config, model_type=ModelType.EDM, image_to_video=False, device=None):
|
def __init__(self, model_config, model_type=ModelType.EDM, image_to_video=False, device=None):
|
||||||
|
|||||||
@@ -471,6 +471,10 @@ def model_config_from_unet(state_dict, unet_key_prefix, use_base_if_no_match=Fal
|
|||||||
model_config.scaled_fp8 = scaled_fp8_weight.dtype
|
model_config.scaled_fp8 = scaled_fp8_weight.dtype
|
||||||
if model_config.scaled_fp8 == torch.float32:
|
if model_config.scaled_fp8 == torch.float32:
|
||||||
model_config.scaled_fp8 = torch.float8_e4m3fn
|
model_config.scaled_fp8 = torch.float8_e4m3fn
|
||||||
|
if scaled_fp8_weight.nelement() == 2:
|
||||||
|
model_config.optimizations["fp8"] = False
|
||||||
|
else:
|
||||||
|
model_config.optimizations["fp8"] = True
|
||||||
|
|
||||||
return model_config
|
return model_config
|
||||||
|
|
||||||
|
|||||||
@@ -581,7 +581,7 @@ def load_models_gpu(models, memory_required=0, force_patch_weights=False, minimu
|
|||||||
loaded_memory = loaded_model.model_loaded_memory()
|
loaded_memory = loaded_model.model_loaded_memory()
|
||||||
current_free_mem = get_free_memory(torch_dev) + loaded_memory
|
current_free_mem = get_free_memory(torch_dev) + loaded_memory
|
||||||
|
|
||||||
lowvram_model_memory = max(64 * 1024 * 1024, (current_free_mem - minimum_memory_required), min(current_free_mem * MIN_WEIGHT_MEMORY_RATIO, current_free_mem - minimum_inference_memory()))
|
lowvram_model_memory = max(128 * 1024 * 1024, (current_free_mem - minimum_memory_required), min(current_free_mem * MIN_WEIGHT_MEMORY_RATIO, current_free_mem - minimum_inference_memory()))
|
||||||
lowvram_model_memory = max(0.1, lowvram_model_memory - loaded_memory)
|
lowvram_model_memory = max(0.1, lowvram_model_memory - loaded_memory)
|
||||||
|
|
||||||
if vram_set_state == VRAMState.NO_VRAM:
|
if vram_set_state == VRAMState.NO_VRAM:
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import torch
|
import torch
|
||||||
|
import logging
|
||||||
import comfy.model_management
|
import comfy.model_management
|
||||||
from comfy.cli_args import args, PerformanceFeature
|
from comfy.cli_args import args, PerformanceFeature
|
||||||
import comfy.float
|
import comfy.float
|
||||||
@@ -308,6 +309,7 @@ class fp8_ops(manual_cast):
|
|||||||
return torch.nn.functional.linear(input, weight, bias)
|
return torch.nn.functional.linear(input, weight, bias)
|
||||||
|
|
||||||
def scaled_fp8_ops(fp8_matrix_mult=False, scale_input=False, override_dtype=None):
|
def scaled_fp8_ops(fp8_matrix_mult=False, scale_input=False, override_dtype=None):
|
||||||
|
logging.info("Using scaled fp8: fp8 matrix mult: {}, scale input: {}".format(fp8_matrix_mult, scale_input))
|
||||||
class scaled_fp8_op(manual_cast):
|
class scaled_fp8_op(manual_cast):
|
||||||
class Linear(manual_cast.Linear):
|
class Linear(manual_cast.Linear):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
@@ -358,7 +360,7 @@ def scaled_fp8_ops(fp8_matrix_mult=False, scale_input=False, override_dtype=None
|
|||||||
def pick_operations(weight_dtype, compute_dtype, load_device=None, disable_fast_fp8=False, fp8_optimizations=False, scaled_fp8=None):
|
def pick_operations(weight_dtype, compute_dtype, load_device=None, disable_fast_fp8=False, fp8_optimizations=False, scaled_fp8=None):
|
||||||
fp8_compute = comfy.model_management.supports_fp8_compute(load_device)
|
fp8_compute = comfy.model_management.supports_fp8_compute(load_device)
|
||||||
if scaled_fp8 is not None:
|
if scaled_fp8 is not None:
|
||||||
return scaled_fp8_ops(fp8_matrix_mult=fp8_compute, scale_input=True, override_dtype=scaled_fp8)
|
return scaled_fp8_ops(fp8_matrix_mult=fp8_compute and fp8_optimizations, scale_input=fp8_optimizations, override_dtype=scaled_fp8)
|
||||||
|
|
||||||
if (
|
if (
|
||||||
fp8_compute and
|
fp8_compute and
|
||||||
|
|||||||
@@ -158,71 +158,93 @@ class SDClipModel(torch.nn.Module, ClipTokenWeightEncoder):
|
|||||||
self.layer_idx = self.options_default[1]
|
self.layer_idx = self.options_default[1]
|
||||||
self.return_projected_pooled = self.options_default[2]
|
self.return_projected_pooled = self.options_default[2]
|
||||||
|
|
||||||
def set_up_textual_embeddings(self, tokens, current_embeds):
|
def process_tokens(self, tokens, device):
|
||||||
out_tokens = []
|
|
||||||
next_new_token = token_dict_size = current_embeds.weight.shape[0]
|
|
||||||
embedding_weights = []
|
|
||||||
|
|
||||||
for x in tokens:
|
|
||||||
tokens_temp = []
|
|
||||||
for y in x:
|
|
||||||
if isinstance(y, numbers.Integral):
|
|
||||||
tokens_temp += [int(y)]
|
|
||||||
else:
|
|
||||||
if y.shape[0] == current_embeds.weight.shape[1]:
|
|
||||||
embedding_weights += [y]
|
|
||||||
tokens_temp += [next_new_token]
|
|
||||||
next_new_token += 1
|
|
||||||
else:
|
|
||||||
logging.warning("WARNING: shape mismatch when trying to apply embedding, embedding will be ignored {} != {}".format(y.shape[0], current_embeds.weight.shape[1]))
|
|
||||||
while len(tokens_temp) < len(x):
|
|
||||||
tokens_temp += [self.special_tokens["pad"]]
|
|
||||||
out_tokens += [tokens_temp]
|
|
||||||
|
|
||||||
n = token_dict_size
|
|
||||||
if len(embedding_weights) > 0:
|
|
||||||
new_embedding = self.operations.Embedding(next_new_token + 1, current_embeds.weight.shape[1], device=current_embeds.weight.device, dtype=current_embeds.weight.dtype)
|
|
||||||
new_embedding.weight[:token_dict_size] = current_embeds.weight
|
|
||||||
for x in embedding_weights:
|
|
||||||
new_embedding.weight[n] = x
|
|
||||||
n += 1
|
|
||||||
self.transformer.set_input_embeddings(new_embedding)
|
|
||||||
|
|
||||||
processed_tokens = []
|
|
||||||
for x in out_tokens:
|
|
||||||
processed_tokens += [list(map(lambda a: n if a == -1 else a, x))] #The EOS token should always be the largest one
|
|
||||||
|
|
||||||
return processed_tokens
|
|
||||||
|
|
||||||
def forward(self, tokens):
|
|
||||||
backup_embeds = self.transformer.get_input_embeddings()
|
|
||||||
device = backup_embeds.weight.device
|
|
||||||
tokens = self.set_up_textual_embeddings(tokens, backup_embeds)
|
|
||||||
tokens = torch.LongTensor(tokens).to(device)
|
|
||||||
|
|
||||||
attention_mask = None
|
|
||||||
if self.enable_attention_masks or self.zero_out_masked or self.return_attention_masks:
|
|
||||||
attention_mask = torch.zeros_like(tokens)
|
|
||||||
end_token = self.special_tokens.get("end", None)
|
end_token = self.special_tokens.get("end", None)
|
||||||
if end_token is None:
|
if end_token is None:
|
||||||
cmp_token = self.special_tokens.get("pad", -1)
|
cmp_token = self.special_tokens.get("pad", -1)
|
||||||
else:
|
else:
|
||||||
cmp_token = end_token
|
cmp_token = end_token
|
||||||
|
|
||||||
for x in range(attention_mask.shape[0]):
|
embeds_out = []
|
||||||
for y in range(attention_mask.shape[1]):
|
attention_masks = []
|
||||||
attention_mask[x, y] = 1
|
num_tokens = []
|
||||||
if tokens[x, y] == cmp_token:
|
|
||||||
|
for x in tokens:
|
||||||
|
attention_mask = []
|
||||||
|
tokens_temp = []
|
||||||
|
other_embeds = []
|
||||||
|
eos = False
|
||||||
|
index = 0
|
||||||
|
for y in x:
|
||||||
|
if isinstance(y, numbers.Integral):
|
||||||
|
if eos:
|
||||||
|
attention_mask.append(0)
|
||||||
|
else:
|
||||||
|
attention_mask.append(1)
|
||||||
|
token = int(y)
|
||||||
|
tokens_temp += [token]
|
||||||
|
if not eos and token == cmp_token:
|
||||||
if end_token is None:
|
if end_token is None:
|
||||||
attention_mask[x, y] = 0
|
attention_mask[-1] = 0
|
||||||
break
|
eos = True
|
||||||
|
else:
|
||||||
|
other_embeds.append((index, y))
|
||||||
|
index += 1
|
||||||
|
|
||||||
|
tokens_embed = torch.tensor([tokens_temp], device=device, dtype=torch.long)
|
||||||
|
tokens_embed = self.transformer.get_input_embeddings()(tokens_embed, out_dtype=torch.float32)
|
||||||
|
index = 0
|
||||||
|
pad_extra = 0
|
||||||
|
for o in other_embeds:
|
||||||
|
emb = o[1]
|
||||||
|
if torch.is_tensor(emb):
|
||||||
|
emb = {"type": "embedding", "data": emb}
|
||||||
|
|
||||||
|
emb_type = emb.get("type", None)
|
||||||
|
if emb_type == "embedding":
|
||||||
|
emb = emb.get("data", None)
|
||||||
|
else:
|
||||||
|
if hasattr(self.transformer, "preprocess_embed"):
|
||||||
|
emb = self.transformer.preprocess_embed(emb, device=device)
|
||||||
|
else:
|
||||||
|
emb = None
|
||||||
|
|
||||||
|
if emb is None:
|
||||||
|
index += -1
|
||||||
|
continue
|
||||||
|
|
||||||
|
ind = index + o[0]
|
||||||
|
emb = emb.view(1, -1, emb.shape[-1]).to(device=device, dtype=torch.float32)
|
||||||
|
emb_shape = emb.shape[1]
|
||||||
|
if emb.shape[-1] == tokens_embed.shape[-1]:
|
||||||
|
tokens_embed = torch.cat([tokens_embed[:, :ind], emb, tokens_embed[:, ind:]], dim=1)
|
||||||
|
attention_mask = attention_mask[:ind] + [1] * emb_shape + attention_mask[ind:]
|
||||||
|
index += emb_shape - 1
|
||||||
|
else:
|
||||||
|
index += -1
|
||||||
|
pad_extra += emb_shape
|
||||||
|
logging.warning("WARNING: shape mismatch when trying to apply embedding, embedding will be ignored {} != {}".format(emb.shape[-1], tokens_embed.shape[-1]))
|
||||||
|
|
||||||
|
if pad_extra > 0:
|
||||||
|
padd_embed = self.transformer.get_input_embeddings()(torch.tensor([[self.special_tokens["pad"]] * pad_extra], device=device, dtype=torch.long), out_dtype=torch.float32)
|
||||||
|
tokens_embed = torch.cat([tokens_embed, padd_embed], dim=1)
|
||||||
|
attention_mask = attention_mask + [0] * pad_extra
|
||||||
|
|
||||||
|
embeds_out.append(tokens_embed)
|
||||||
|
attention_masks.append(attention_mask)
|
||||||
|
num_tokens.append(sum(attention_mask))
|
||||||
|
|
||||||
|
return torch.cat(embeds_out), torch.tensor(attention_masks, device=device, dtype=torch.long), num_tokens
|
||||||
|
|
||||||
|
def forward(self, tokens):
|
||||||
|
device = self.transformer.get_input_embeddings().weight.device
|
||||||
|
embeds, attention_mask, num_tokens = self.process_tokens(tokens, device)
|
||||||
|
|
||||||
attention_mask_model = None
|
attention_mask_model = None
|
||||||
if self.enable_attention_masks:
|
if self.enable_attention_masks:
|
||||||
attention_mask_model = attention_mask
|
attention_mask_model = attention_mask
|
||||||
|
|
||||||
outputs = self.transformer(tokens, attention_mask_model, intermediate_output=self.layer_idx, final_layer_norm_intermediate=self.layer_norm_hidden_state, dtype=torch.float32)
|
outputs = self.transformer(None, attention_mask_model, embeds=embeds, num_tokens=num_tokens, intermediate_output=self.layer_idx, final_layer_norm_intermediate=self.layer_norm_hidden_state, dtype=torch.float32)
|
||||||
self.transformer.set_input_embeddings(backup_embeds)
|
|
||||||
|
|
||||||
if self.layer == "last":
|
if self.layer == "last":
|
||||||
z = outputs[0].float()
|
z = outputs[0].float()
|
||||||
|
|||||||
@@ -826,6 +826,16 @@ class HunyuanVideo(supported_models_base.BASE):
|
|||||||
hunyuan_detect = comfy.text_encoders.hunyuan_video.llama_detect(state_dict, "{}llama.transformer.".format(pref))
|
hunyuan_detect = comfy.text_encoders.hunyuan_video.llama_detect(state_dict, "{}llama.transformer.".format(pref))
|
||||||
return supported_models_base.ClipTarget(comfy.text_encoders.hunyuan_video.HunyuanVideoTokenizer, comfy.text_encoders.hunyuan_video.hunyuan_video_clip(**hunyuan_detect))
|
return supported_models_base.ClipTarget(comfy.text_encoders.hunyuan_video.HunyuanVideoTokenizer, comfy.text_encoders.hunyuan_video.hunyuan_video_clip(**hunyuan_detect))
|
||||||
|
|
||||||
|
class HunyuanVideoI2V(HunyuanVideo):
|
||||||
|
unet_config = {
|
||||||
|
"image_model": "hunyuan_video",
|
||||||
|
"in_channels": 33,
|
||||||
|
}
|
||||||
|
|
||||||
|
def get_model(self, state_dict, prefix="", device=None):
|
||||||
|
out = model_base.HunyuanVideoI2V(self, device=device)
|
||||||
|
return out
|
||||||
|
|
||||||
class HunyuanVideoSkyreelsI2V(HunyuanVideo):
|
class HunyuanVideoSkyreelsI2V(HunyuanVideo):
|
||||||
unet_config = {
|
unet_config = {
|
||||||
"image_model": "hunyuan_video",
|
"image_model": "hunyuan_video",
|
||||||
@@ -921,7 +931,7 @@ class WAN21_T2V(supported_models_base.BASE):
|
|||||||
|
|
||||||
memory_usage_factor = 1.0
|
memory_usage_factor = 1.0
|
||||||
|
|
||||||
supported_inference_dtypes = [torch.bfloat16, torch.float16, torch.float32]
|
supported_inference_dtypes = [torch.float16, torch.bfloat16, torch.float32]
|
||||||
|
|
||||||
vae_key_prefix = ["vae."]
|
vae_key_prefix = ["vae."]
|
||||||
text_encoder_key_prefix = ["text_encoders."]
|
text_encoder_key_prefix = ["text_encoders."]
|
||||||
@@ -949,6 +959,6 @@ class WAN21_I2V(WAN21_T2V):
|
|||||||
out = model_base.WAN21(self, image_to_video=True, device=device)
|
out = model_base.WAN21(self, image_to_video=True, device=device)
|
||||||
return out
|
return out
|
||||||
|
|
||||||
models = [Stable_Zero123, SD15_instructpix2pix, SD15, SD20, SD21UnclipL, SD21UnclipH, SDXL_instructpix2pix, SDXLRefiner, SDXL, SSD1B, KOALA_700M, KOALA_1B, Segmind_Vega, SD_X4Upscaler, Stable_Cascade_C, Stable_Cascade_B, SV3D_u, SV3D_p, SD3, StableAudio, AuraFlow, PixArtAlpha, PixArtSigma, HunyuanDiT, HunyuanDiT1, FluxInpaint, Flux, FluxSchnell, GenmoMochi, LTXV, HunyuanVideoSkyreelsI2V, HunyuanVideo, CosmosT2V, CosmosI2V, Lumina2, WAN21_T2V, WAN21_I2V]
|
models = [Stable_Zero123, SD15_instructpix2pix, SD15, SD20, SD21UnclipL, SD21UnclipH, SDXL_instructpix2pix, SDXLRefiner, SDXL, SSD1B, KOALA_700M, KOALA_1B, Segmind_Vega, SD_X4Upscaler, Stable_Cascade_C, Stable_Cascade_B, SV3D_u, SV3D_p, SD3, StableAudio, AuraFlow, PixArtAlpha, PixArtSigma, HunyuanDiT, HunyuanDiT1, FluxInpaint, Flux, FluxSchnell, GenmoMochi, LTXV, HunyuanVideoSkyreelsI2V, HunyuanVideoI2V, HunyuanVideo, CosmosT2V, CosmosI2V, Lumina2, WAN21_T2V, WAN21_I2V]
|
||||||
|
|
||||||
models += [SVD_img2vid]
|
models += [SVD_img2vid]
|
||||||
|
|||||||
@@ -93,7 +93,10 @@ class BertEmbeddings(torch.nn.Module):
|
|||||||
|
|
||||||
self.LayerNorm = operations.LayerNorm(embed_dim, eps=layer_norm_eps, dtype=dtype, device=device)
|
self.LayerNorm = operations.LayerNorm(embed_dim, eps=layer_norm_eps, dtype=dtype, device=device)
|
||||||
|
|
||||||
def forward(self, input_tokens, token_type_ids=None, dtype=None):
|
def forward(self, input_tokens, embeds=None, token_type_ids=None, dtype=None):
|
||||||
|
if embeds is not None:
|
||||||
|
x = embeds
|
||||||
|
else:
|
||||||
x = self.word_embeddings(input_tokens, out_dtype=dtype)
|
x = self.word_embeddings(input_tokens, out_dtype=dtype)
|
||||||
x += comfy.ops.cast_to_input(self.position_embeddings.weight[:x.shape[1]], x)
|
x += comfy.ops.cast_to_input(self.position_embeddings.weight[:x.shape[1]], x)
|
||||||
if token_type_ids is not None:
|
if token_type_ids is not None:
|
||||||
@@ -113,8 +116,8 @@ class BertModel_(torch.nn.Module):
|
|||||||
self.embeddings = BertEmbeddings(config_dict["vocab_size"], config_dict["max_position_embeddings"], config_dict["type_vocab_size"], config_dict["pad_token_id"], embed_dim, layer_norm_eps, dtype, device, operations)
|
self.embeddings = BertEmbeddings(config_dict["vocab_size"], config_dict["max_position_embeddings"], config_dict["type_vocab_size"], config_dict["pad_token_id"], embed_dim, layer_norm_eps, dtype, device, operations)
|
||||||
self.encoder = BertEncoder(config_dict["num_hidden_layers"], embed_dim, config_dict["intermediate_size"], config_dict["num_attention_heads"], layer_norm_eps, dtype, device, operations)
|
self.encoder = BertEncoder(config_dict["num_hidden_layers"], embed_dim, config_dict["intermediate_size"], config_dict["num_attention_heads"], layer_norm_eps, dtype, device, operations)
|
||||||
|
|
||||||
def forward(self, input_tokens, attention_mask=None, intermediate_output=None, final_layer_norm_intermediate=True, dtype=None):
|
def forward(self, input_tokens, attention_mask=None, embeds=None, num_tokens=None, intermediate_output=None, final_layer_norm_intermediate=True, dtype=None):
|
||||||
x = self.embeddings(input_tokens, dtype=dtype)
|
x = self.embeddings(input_tokens, embeds=embeds, dtype=dtype)
|
||||||
mask = None
|
mask = None
|
||||||
if attention_mask is not None:
|
if attention_mask is not None:
|
||||||
mask = 1.0 - attention_mask.to(x.dtype).reshape((attention_mask.shape[0], 1, -1, attention_mask.shape[-1])).expand(attention_mask.shape[0], 1, attention_mask.shape[-1], attention_mask.shape[-1])
|
mask = 1.0 - attention_mask.to(x.dtype).reshape((attention_mask.shape[0], 1, -1, attention_mask.shape[-1])).expand(attention_mask.shape[0], 1, attention_mask.shape[-1], attention_mask.shape[-1])
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import comfy.text_encoders.llama
|
|||||||
from transformers import LlamaTokenizerFast
|
from transformers import LlamaTokenizerFast
|
||||||
import torch
|
import torch
|
||||||
import os
|
import os
|
||||||
|
import numbers
|
||||||
|
|
||||||
|
|
||||||
def llama_detect(state_dict, prefix=""):
|
def llama_detect(state_dict, prefix=""):
|
||||||
@@ -22,7 +23,7 @@ def llama_detect(state_dict, prefix=""):
|
|||||||
class LLAMA3Tokenizer(sd1_clip.SDTokenizer):
|
class LLAMA3Tokenizer(sd1_clip.SDTokenizer):
|
||||||
def __init__(self, embedding_directory=None, tokenizer_data={}, min_length=256):
|
def __init__(self, embedding_directory=None, tokenizer_data={}, min_length=256):
|
||||||
tokenizer_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "llama_tokenizer")
|
tokenizer_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "llama_tokenizer")
|
||||||
super().__init__(tokenizer_path, embedding_directory=embedding_directory, pad_with_end=False, embedding_size=4096, embedding_key='llama', tokenizer_class=LlamaTokenizerFast, has_start_token=True, has_end_token=False, pad_to_max_length=False, max_length=99999999, pad_token=128258, end_token=128009, min_length=min_length)
|
super().__init__(tokenizer_path, embedding_directory=embedding_directory, pad_with_end=False, embedding_size=4096, embedding_key='llama', tokenizer_class=LlamaTokenizerFast, has_start_token=True, has_end_token=False, pad_to_max_length=False, max_length=99999999, pad_token=128258, min_length=min_length)
|
||||||
|
|
||||||
class LLAMAModel(sd1_clip.SDClipModel):
|
class LLAMAModel(sd1_clip.SDClipModel):
|
||||||
def __init__(self, device="cpu", layer="hidden", layer_idx=-3, dtype=None, attention_mask=True, model_options={}):
|
def __init__(self, device="cpu", layer="hidden", layer_idx=-3, dtype=None, attention_mask=True, model_options={}):
|
||||||
@@ -38,18 +39,26 @@ class HunyuanVideoTokenizer:
|
|||||||
def __init__(self, embedding_directory=None, tokenizer_data={}):
|
def __init__(self, embedding_directory=None, tokenizer_data={}):
|
||||||
clip_l_tokenizer_class = tokenizer_data.get("clip_l_tokenizer_class", sd1_clip.SDTokenizer)
|
clip_l_tokenizer_class = tokenizer_data.get("clip_l_tokenizer_class", sd1_clip.SDTokenizer)
|
||||||
self.clip_l = clip_l_tokenizer_class(embedding_directory=embedding_directory)
|
self.clip_l = clip_l_tokenizer_class(embedding_directory=embedding_directory)
|
||||||
self.llama_template = """<|start_header_id|>system<|end_header_id|>\n\nDescribe the video by detailing the following aspects: 1. The main content and theme of the video.2. The color, shape, size, texture, quantity, text, and spatial relationships of the objects.3. Actions, events, behaviors temporal relationships, physical movement changes of the objects.4. background environment, light, style and atmosphere.5. camera angles, movements, and transitions used in the video:<|eot_id|><|start_header_id|>user<|end_header_id|>\n\n""" # 95 tokens
|
self.llama_template = """<|start_header_id|>system<|end_header_id|>\n\nDescribe the video by detailing the following aspects: 1. The main content and theme of the video.2. The color, shape, size, texture, quantity, text, and spatial relationships of the objects.3. Actions, events, behaviors temporal relationships, physical movement changes of the objects.4. background environment, light, style and atmosphere.5. camera angles, movements, and transitions used in the video:<|eot_id|><|start_header_id|>user<|end_header_id|>\n\n{}<|eot_id|>""" # 95 tokens
|
||||||
self.llama = LLAMA3Tokenizer(embedding_directory=embedding_directory, min_length=1)
|
self.llama = LLAMA3Tokenizer(embedding_directory=embedding_directory, min_length=1)
|
||||||
|
|
||||||
def tokenize_with_weights(self, text:str, return_word_ids=False, llama_template=None, **kwargs):
|
def tokenize_with_weights(self, text, return_word_ids=False, llama_template=None, image_embeds=None, image_interleave=1, **kwargs):
|
||||||
out = {}
|
out = {}
|
||||||
out["l"] = self.clip_l.tokenize_with_weights(text, return_word_ids)
|
out["l"] = self.clip_l.tokenize_with_weights(text, return_word_ids)
|
||||||
|
|
||||||
if llama_template is None:
|
if llama_template is None:
|
||||||
llama_text = "{}{}".format(self.llama_template, text)
|
llama_text = self.llama_template.format(text)
|
||||||
else:
|
else:
|
||||||
llama_text = "{}{}".format(llama_template, text)
|
llama_text = llama_template.format(text)
|
||||||
out["llama"] = self.llama.tokenize_with_weights(llama_text, return_word_ids)
|
llama_text_tokens = self.llama.tokenize_with_weights(llama_text, return_word_ids)
|
||||||
|
embed_count = 0
|
||||||
|
for r in llama_text_tokens:
|
||||||
|
for i in range(len(r)):
|
||||||
|
if r[i][0] == 128257:
|
||||||
|
if image_embeds is not None and embed_count < image_embeds.shape[0]:
|
||||||
|
r[i] = ({"type": "embedding", "data": image_embeds[embed_count], "original_type": "image", "image_interleave": image_interleave},) + r[i][1:]
|
||||||
|
embed_count += 1
|
||||||
|
out["llama"] = llama_text_tokens
|
||||||
return out
|
return out
|
||||||
|
|
||||||
def untokenize(self, token_weight_pair):
|
def untokenize(self, token_weight_pair):
|
||||||
@@ -83,20 +92,51 @@ class HunyuanVideoClipModel(torch.nn.Module):
|
|||||||
llama_out, llama_pooled, llama_extra_out = self.llama.encode_token_weights(token_weight_pairs_llama)
|
llama_out, llama_pooled, llama_extra_out = self.llama.encode_token_weights(token_weight_pairs_llama)
|
||||||
|
|
||||||
template_end = 0
|
template_end = 0
|
||||||
for i, v in enumerate(token_weight_pairs_llama[0]):
|
extra_template_end = 0
|
||||||
if v[0] == 128007: # <|end_header_id|>
|
extra_sizes = 0
|
||||||
template_end = i
|
user_end = 9999999999999
|
||||||
|
images = []
|
||||||
|
|
||||||
|
tok_pairs = token_weight_pairs_llama[0]
|
||||||
|
for i, v in enumerate(tok_pairs):
|
||||||
|
elem = v[0]
|
||||||
|
if not torch.is_tensor(elem):
|
||||||
|
if isinstance(elem, numbers.Integral):
|
||||||
|
if elem == 128006:
|
||||||
|
if tok_pairs[i + 1][0] == 882:
|
||||||
|
if tok_pairs[i + 2][0] == 128007:
|
||||||
|
template_end = i + 2
|
||||||
|
user_end = -1
|
||||||
|
if elem == 128009 and user_end == -1:
|
||||||
|
user_end = i + 1
|
||||||
|
else:
|
||||||
|
if elem.get("original_type") == "image":
|
||||||
|
elem_size = elem.get("data").shape[0]
|
||||||
|
if template_end > 0:
|
||||||
|
if user_end == -1:
|
||||||
|
extra_template_end += elem_size - 1
|
||||||
|
else:
|
||||||
|
image_start = i + extra_sizes
|
||||||
|
image_end = i + elem_size + extra_sizes
|
||||||
|
images.append((image_start, image_end, elem.get("image_interleave", 1)))
|
||||||
|
extra_sizes += elem_size - 1
|
||||||
|
|
||||||
if llama_out.shape[1] > (template_end + 2):
|
if llama_out.shape[1] > (template_end + 2):
|
||||||
if token_weight_pairs_llama[0][template_end + 1][0] == 271:
|
if tok_pairs[template_end + 1][0] == 271:
|
||||||
template_end += 2
|
template_end += 2
|
||||||
llama_out = llama_out[:, template_end:]
|
llama_output = llama_out[:, template_end + extra_sizes:user_end + extra_sizes + extra_template_end]
|
||||||
llama_extra_out["attention_mask"] = llama_extra_out["attention_mask"][:, template_end:]
|
llama_extra_out["attention_mask"] = llama_extra_out["attention_mask"][:, template_end + extra_sizes:user_end + extra_sizes + extra_template_end]
|
||||||
if llama_extra_out["attention_mask"].sum() == torch.numel(llama_extra_out["attention_mask"]):
|
if llama_extra_out["attention_mask"].sum() == torch.numel(llama_extra_out["attention_mask"]):
|
||||||
llama_extra_out.pop("attention_mask") # attention mask is useless if no masked elements
|
llama_extra_out.pop("attention_mask") # attention mask is useless if no masked elements
|
||||||
|
|
||||||
|
if len(images) > 0:
|
||||||
|
out = []
|
||||||
|
for i in images:
|
||||||
|
out.append(llama_out[:, i[0]: i[1]: i[2]])
|
||||||
|
llama_output = torch.cat(out + [llama_output], dim=1)
|
||||||
|
|
||||||
l_out, l_pooled = self.clip_l.encode_token_weights(token_weight_pairs_l)
|
l_out, l_pooled = self.clip_l.encode_token_weights(token_weight_pairs_l)
|
||||||
return llama_out, l_pooled, llama_extra_out
|
return llama_output, l_pooled, llama_extra_out
|
||||||
|
|
||||||
def load_sd(self, sd):
|
def load_sd(self, sd):
|
||||||
if "text_model.encoder.layers.1.mlp.fc1.weight" in sd:
|
if "text_model.encoder.layers.1.mlp.fc1.weight" in sd:
|
||||||
|
|||||||
@@ -241,7 +241,10 @@ class Llama2_(nn.Module):
|
|||||||
self.norm = RMSNorm(config.hidden_size, eps=config.rms_norm_eps, add=config.rms_norm_add, device=device, dtype=dtype)
|
self.norm = RMSNorm(config.hidden_size, eps=config.rms_norm_eps, add=config.rms_norm_add, device=device, dtype=dtype)
|
||||||
# self.lm_head = ops.Linear(config.hidden_size, config.vocab_size, bias=False, device=device, dtype=dtype)
|
# self.lm_head = ops.Linear(config.hidden_size, config.vocab_size, bias=False, device=device, dtype=dtype)
|
||||||
|
|
||||||
def forward(self, x, attention_mask=None, intermediate_output=None, final_layer_norm_intermediate=True, dtype=None):
|
def forward(self, x, attention_mask=None, embeds=None, num_tokens=None, intermediate_output=None, final_layer_norm_intermediate=True, dtype=None):
|
||||||
|
if embeds is not None:
|
||||||
|
x = embeds
|
||||||
|
else:
|
||||||
x = self.embed_tokens(x, out_dtype=dtype)
|
x = self.embed_tokens(x, out_dtype=dtype)
|
||||||
|
|
||||||
if self.normalize_in:
|
if self.normalize_in:
|
||||||
|
|||||||
@@ -239,8 +239,11 @@ class T5(torch.nn.Module):
|
|||||||
def set_input_embeddings(self, embeddings):
|
def set_input_embeddings(self, embeddings):
|
||||||
self.shared = embeddings
|
self.shared = embeddings
|
||||||
|
|
||||||
def forward(self, input_ids, *args, **kwargs):
|
def forward(self, input_ids, attention_mask, embeds=None, num_tokens=None, **kwargs):
|
||||||
|
if input_ids is None:
|
||||||
|
x = embeds
|
||||||
|
else:
|
||||||
x = self.shared(input_ids, out_dtype=kwargs.get("dtype", torch.float32))
|
x = self.shared(input_ids, out_dtype=kwargs.get("dtype", torch.float32))
|
||||||
if self.dtype not in [torch.float32, torch.float16, torch.bfloat16]:
|
if self.dtype not in [torch.float32, torch.float16, torch.bfloat16]:
|
||||||
x = torch.nan_to_num(x) #Fix for fp8 T5 base
|
x = torch.nan_to_num(x) #Fix for fp8 T5 base
|
||||||
return self.encoder(x, *args, **kwargs)
|
return self.encoder(x, attention_mask=attention_mask, **kwargs)
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
import torchaudio
|
import torchaudio
|
||||||
import torch
|
import torch
|
||||||
import comfy.model_management
|
import comfy.model_management
|
||||||
@@ -10,6 +12,7 @@ import random
|
|||||||
import hashlib
|
import hashlib
|
||||||
import node_helpers
|
import node_helpers
|
||||||
from comfy.cli_args import args
|
from comfy.cli_args import args
|
||||||
|
from comfy.comfy_types import FileLocator
|
||||||
|
|
||||||
class EmptyLatentAudio:
|
class EmptyLatentAudio:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@@ -164,7 +167,7 @@ class SaveAudio:
|
|||||||
def save_audio(self, audio, filename_prefix="ComfyUI", prompt=None, extra_pnginfo=None):
|
def save_audio(self, audio, filename_prefix="ComfyUI", prompt=None, extra_pnginfo=None):
|
||||||
filename_prefix += self.prefix_append
|
filename_prefix += self.prefix_append
|
||||||
full_output_folder, filename, counter, subfolder, filename_prefix = folder_paths.get_save_image_path(filename_prefix, self.output_dir)
|
full_output_folder, filename, counter, subfolder, filename_prefix = folder_paths.get_save_image_path(filename_prefix, self.output_dir)
|
||||||
results = list()
|
results: list[FileLocator] = []
|
||||||
|
|
||||||
metadata = {}
|
metadata = {}
|
||||||
if not args.disable_metadata:
|
if not args.disable_metadata:
|
||||||
|
|||||||
@@ -454,7 +454,7 @@ class SamplerCustom:
|
|||||||
return {"required":
|
return {"required":
|
||||||
{"model": ("MODEL",),
|
{"model": ("MODEL",),
|
||||||
"add_noise": ("BOOLEAN", {"default": True}),
|
"add_noise": ("BOOLEAN", {"default": True}),
|
||||||
"noise_seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}),
|
"noise_seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff, "control_after_generate": True}),
|
||||||
"cfg": ("FLOAT", {"default": 8.0, "min": 0.0, "max": 100.0, "step":0.1, "round": 0.01}),
|
"cfg": ("FLOAT", {"default": 8.0, "min": 0.0, "max": 100.0, "step":0.1, "round": 0.01}),
|
||||||
"positive": ("CONDITIONING", ),
|
"positive": ("CONDITIONING", ),
|
||||||
"negative": ("CONDITIONING", ),
|
"negative": ("CONDITIONING", ),
|
||||||
@@ -605,8 +605,14 @@ class DisableNoise:
|
|||||||
class RandomNoise(DisableNoise):
|
class RandomNoise(DisableNoise):
|
||||||
@classmethod
|
@classmethod
|
||||||
def INPUT_TYPES(s):
|
def INPUT_TYPES(s):
|
||||||
return {"required":{
|
return {
|
||||||
"noise_seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}),
|
"required": {
|
||||||
|
"noise_seed": ("INT", {
|
||||||
|
"default": 0,
|
||||||
|
"min": 0,
|
||||||
|
"max": 0xffffffffffffffff,
|
||||||
|
"control_after_generate": True,
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import nodes
|
import nodes
|
||||||
|
import node_helpers
|
||||||
import torch
|
import torch
|
||||||
import comfy.model_management
|
import comfy.model_management
|
||||||
|
|
||||||
@@ -38,7 +39,83 @@ class EmptyHunyuanLatentVideo:
|
|||||||
latent = torch.zeros([batch_size, 16, ((length - 1) // 4) + 1, height // 8, width // 8], device=comfy.model_management.intermediate_device())
|
latent = torch.zeros([batch_size, 16, ((length - 1) // 4) + 1, height // 8, width // 8], device=comfy.model_management.intermediate_device())
|
||||||
return ({"samples":latent}, )
|
return ({"samples":latent}, )
|
||||||
|
|
||||||
|
PROMPT_TEMPLATE_ENCODE_VIDEO_I2V = (
|
||||||
|
"<|start_header_id|>system<|end_header_id|>\n\n<image>\nDescribe the video by detailing the following aspects according to the reference image: "
|
||||||
|
"1. The main content and theme of the video."
|
||||||
|
"2. The color, shape, size, texture, quantity, text, and spatial relationships of the objects."
|
||||||
|
"3. Actions, events, behaviors temporal relationships, physical movement changes of the objects."
|
||||||
|
"4. background environment, light, style and atmosphere."
|
||||||
|
"5. camera angles, movements, and transitions used in the video:<|eot_id|>\n\n"
|
||||||
|
"<|start_header_id|>user<|end_header_id|>\n\n{}<|eot_id|>"
|
||||||
|
"<|start_header_id|>assistant<|end_header_id|>\n\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
class TextEncodeHunyuanVideo_ImageToVideo:
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(s):
|
||||||
|
return {"required": {
|
||||||
|
"clip": ("CLIP", ),
|
||||||
|
"clip_vision_output": ("CLIP_VISION_OUTPUT", ),
|
||||||
|
"prompt": ("STRING", {"multiline": True, "dynamicPrompts": True}),
|
||||||
|
"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."}),
|
||||||
|
}}
|
||||||
|
RETURN_TYPES = ("CONDITIONING",)
|
||||||
|
FUNCTION = "encode"
|
||||||
|
|
||||||
|
CATEGORY = "advanced/conditioning"
|
||||||
|
|
||||||
|
def encode(self, clip, clip_vision_output, prompt, image_interleave):
|
||||||
|
tokens = clip.tokenize(prompt, llama_template=PROMPT_TEMPLATE_ENCODE_VIDEO_I2V, image_embeds=clip_vision_output.mm_projected, image_interleave=image_interleave)
|
||||||
|
return (clip.encode_from_tokens_scheduled(tokens), )
|
||||||
|
|
||||||
|
class HunyuanImageToVideo:
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(s):
|
||||||
|
return {"required": {"positive": ("CONDITIONING", ),
|
||||||
|
"vae": ("VAE", ),
|
||||||
|
"width": ("INT", {"default": 848, "min": 16, "max": nodes.MAX_RESOLUTION, "step": 16}),
|
||||||
|
"height": ("INT", {"default": 480, "min": 16, "max": nodes.MAX_RESOLUTION, "step": 16}),
|
||||||
|
"length": ("INT", {"default": 53, "min": 1, "max": nodes.MAX_RESOLUTION, "step": 4}),
|
||||||
|
"batch_size": ("INT", {"default": 1, "min": 1, "max": 4096}),
|
||||||
|
"guidance_type": (["v1 (concat)", "v2 (replace)"], )
|
||||||
|
},
|
||||||
|
"optional": {"start_image": ("IMAGE", ),
|
||||||
|
}}
|
||||||
|
|
||||||
|
RETURN_TYPES = ("CONDITIONING", "LATENT")
|
||||||
|
RETURN_NAMES = ("positive", "latent")
|
||||||
|
FUNCTION = "encode"
|
||||||
|
|
||||||
|
CATEGORY = "conditioning/video_models"
|
||||||
|
|
||||||
|
def encode(self, positive, vae, width, height, length, batch_size, guidance_type, start_image=None):
|
||||||
|
latent = torch.zeros([batch_size, 16, ((length - 1) // 4) + 1, height // 8, width // 8], device=comfy.model_management.intermediate_device())
|
||||||
|
out_latent = {}
|
||||||
|
|
||||||
|
if start_image is not None:
|
||||||
|
start_image = comfy.utils.common_upscale(start_image[:length, :, :, :3].movedim(-1, 1), width, height, "bilinear", "center").movedim(1, -1)
|
||||||
|
|
||||||
|
concat_latent_image = vae.encode(start_image)
|
||||||
|
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)
|
||||||
|
mask[:, :, :((start_image.shape[0] - 1) // 4) + 1] = 0.0
|
||||||
|
|
||||||
|
if guidance_type == "v1 (concat)":
|
||||||
|
cond = {"concat_latent_image": concat_latent_image, "concat_mask": mask}
|
||||||
|
else:
|
||||||
|
cond = {'guiding_frame_index': 0}
|
||||||
|
latent[:, :, :concat_latent_image.shape[2]] = concat_latent_image
|
||||||
|
out_latent["noise_mask"] = mask
|
||||||
|
|
||||||
|
positive = node_helpers.conditioning_set_values(positive, cond)
|
||||||
|
|
||||||
|
out_latent["samples"] = latent
|
||||||
|
return (positive, out_latent)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
NODE_CLASS_MAPPINGS = {
|
NODE_CLASS_MAPPINGS = {
|
||||||
"CLIPTextEncodeHunyuanDiT": CLIPTextEncodeHunyuanDiT,
|
"CLIPTextEncodeHunyuanDiT": CLIPTextEncodeHunyuanDiT,
|
||||||
|
"TextEncodeHunyuanVideo_ImageToVideo": TextEncodeHunyuanVideo_ImageToVideo,
|
||||||
"EmptyHunyuanLatentVideo": EmptyHunyuanLatentVideo,
|
"EmptyHunyuanLatentVideo": EmptyHunyuanLatentVideo,
|
||||||
|
"HunyuanImageToVideo": HunyuanImageToVideo,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
import nodes
|
import nodes
|
||||||
import folder_paths
|
import folder_paths
|
||||||
from comfy.cli_args import args
|
from comfy.cli_args import args
|
||||||
@@ -9,6 +11,8 @@ import numpy as np
|
|||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
from comfy.comfy_types import FileLocator
|
||||||
|
|
||||||
MAX_RESOLUTION = nodes.MAX_RESOLUTION
|
MAX_RESOLUTION = nodes.MAX_RESOLUTION
|
||||||
|
|
||||||
class ImageCrop:
|
class ImageCrop:
|
||||||
@@ -99,7 +103,7 @@ class SaveAnimatedWEBP:
|
|||||||
method = self.methods.get(method)
|
method = self.methods.get(method)
|
||||||
filename_prefix += self.prefix_append
|
filename_prefix += self.prefix_append
|
||||||
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])
|
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])
|
||||||
results = list()
|
results: list[FileLocator] = []
|
||||||
pil_images = []
|
pil_images = []
|
||||||
for image in images:
|
for image in images:
|
||||||
i = 255. * image.cpu().numpy()
|
i = 255. * image.cpu().numpy()
|
||||||
|
|||||||
@@ -194,11 +194,6 @@ class LTXVAddGuide:
|
|||||||
frame_idx, latent_idx = self.get_latent_index(positive, latent_length, frame_idx, scale_factors)
|
frame_idx, latent_idx = self.get_latent_index(positive, latent_length, frame_idx, scale_factors)
|
||||||
assert latent_idx + t.shape[2] <= latent_length, "Conditioning frames exceed the length of the latent sequence."
|
assert latent_idx + t.shape[2] <= latent_length, "Conditioning frames exceed the length of the latent sequence."
|
||||||
|
|
||||||
if frame_idx == 0:
|
|
||||||
latent_image, noise_mask = self.replace_latent_frames(latent_image, noise_mask, t, latent_idx, strength)
|
|
||||||
return (positive, negative, {"samples": latent_image, "noise_mask": noise_mask},)
|
|
||||||
|
|
||||||
|
|
||||||
num_prefix_frames = min(self._num_prefix_frames, t.shape[2])
|
num_prefix_frames = min(self._num_prefix_frames, t.shape[2])
|
||||||
|
|
||||||
positive, negative, latent_image, noise_mask = self.append_keyframe(
|
positive, negative, latent_image, noise_mask = self.append_keyframe(
|
||||||
@@ -252,6 +247,8 @@ class LTXVCropGuides:
|
|||||||
noise_mask = get_noise_mask(latent)
|
noise_mask = get_noise_mask(latent)
|
||||||
|
|
||||||
_, num_keyframes = get_keyframe_idxs(positive)
|
_, num_keyframes = get_keyframe_idxs(positive)
|
||||||
|
if num_keyframes == 0:
|
||||||
|
return (positive, negative, {"samples": latent_image, "noise_mask": noise_mask},)
|
||||||
|
|
||||||
latent_image = latent_image[:, :, :-num_keyframes]
|
latent_image = latent_image[:, :, :-num_keyframes]
|
||||||
noise_mask = noise_mask[:, :, :-num_keyframes]
|
noise_mask = noise_mask[:, :, :-num_keyframes]
|
||||||
@@ -413,7 +410,7 @@ def preprocess(image: torch.Tensor, crf=29):
|
|||||||
if crf == 0:
|
if crf == 0:
|
||||||
return image
|
return image
|
||||||
|
|
||||||
image_array = (image * 255.0).byte().cpu().numpy()
|
image_array = (image[:(image.shape[0] // 2) * 2, :(image.shape[1] // 2) * 2] * 255.0).byte().cpu().numpy()
|
||||||
with io.BytesIO() as output_file:
|
with io.BytesIO() as output_file:
|
||||||
encode_single_frame(output_file, image_array, crf)
|
encode_single_frame(output_file, image_array, crf)
|
||||||
video_bytes = output_file.getvalue()
|
video_bytes = output_file.getvalue()
|
||||||
@@ -447,12 +444,11 @@ class LTXVPreprocess:
|
|||||||
CATEGORY = "image"
|
CATEGORY = "image"
|
||||||
|
|
||||||
def preprocess(self, image, img_compression):
|
def preprocess(self, image, img_compression):
|
||||||
output_image = image
|
|
||||||
if img_compression > 0:
|
if img_compression > 0:
|
||||||
output_image = torch.zeros_like(image)
|
output_images = []
|
||||||
for i in range(image.shape[0]):
|
for i in range(image.shape[0]):
|
||||||
output_image[i] = preprocess(image[i], img_compression)
|
output_images.append(preprocess(image[i], img_compression))
|
||||||
return (output_image,)
|
return (torch.stack(output_images),)
|
||||||
|
|
||||||
|
|
||||||
NODE_CLASS_MAPPINGS = {
|
NODE_CLASS_MAPPINGS = {
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import av
|
import av
|
||||||
import torch
|
import torch
|
||||||
import folder_paths
|
import folder_paths
|
||||||
import json
|
import json
|
||||||
from fractions import Fraction
|
from fractions import Fraction
|
||||||
|
from comfy.comfy_types import FileLocator
|
||||||
|
|
||||||
|
|
||||||
class SaveWEBM:
|
class SaveWEBM:
|
||||||
@@ -62,7 +65,7 @@ class SaveWEBM:
|
|||||||
container.mux(stream.encode())
|
container.mux(stream.encode())
|
||||||
container.close()
|
container.close()
|
||||||
|
|
||||||
results = [{
|
results: list[FileLocator] = [{
|
||||||
"filename": file,
|
"filename": file,
|
||||||
"subfolder": subfolder,
|
"subfolder": subfolder,
|
||||||
"type": self.type
|
"type": self.type
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
# This file is automatically generated by the build process when version is
|
# This file is automatically generated by the build process when version is
|
||||||
# updated in pyproject.toml.
|
# updated in pyproject.toml.
|
||||||
__version__ = "0.3.20"
|
__version__ = "0.3.25"
|
||||||
|
|||||||
20
main.py
20
main.py
@@ -139,6 +139,7 @@ from server import BinaryEventTypes
|
|||||||
import nodes
|
import nodes
|
||||||
import comfy.model_management
|
import comfy.model_management
|
||||||
import comfyui_version
|
import comfyui_version
|
||||||
|
import app.frontend_management
|
||||||
|
|
||||||
|
|
||||||
def cuda_malloc_warning():
|
def cuda_malloc_warning():
|
||||||
@@ -292,12 +293,29 @@ def start_comfyui(asyncio_loop=None):
|
|||||||
return asyncio_loop, prompt_server, start_all
|
return asyncio_loop, prompt_server, start_all
|
||||||
|
|
||||||
|
|
||||||
|
def warn_frontend_version(frontend_version):
|
||||||
|
try:
|
||||||
|
required_frontend = (0,)
|
||||||
|
req_path = os.path.join(os.path.dirname(__file__), 'requirements.txt')
|
||||||
|
with open(req_path, 'r') as f:
|
||||||
|
required_frontend = tuple(map(int, f.readline().split('=')[-1].split('.')))
|
||||||
|
if frontend_version < required_frontend:
|
||||||
|
logging.warning("________________________________________________________________________\nWARNING WARNING WARNING WARNING WARNING\n\nInstalled frontend version {} is lower than the recommended version {}.\n\n{}\n________________________________________________________________________".format('.'.join(map(str, frontend_version)), '.'.join(map(str, required_frontend)), app.frontend_management.frontend_install_warning_message()))
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
# Running directly, just start ComfyUI.
|
# Running directly, just start ComfyUI.
|
||||||
logging.info("ComfyUI version: {}".format(comfyui_version.__version__))
|
logging.info("ComfyUI version: {}".format(comfyui_version.__version__))
|
||||||
|
frontend_version = app.frontend_management.frontend_version
|
||||||
|
logging.info("ComfyUI frontend version: {}".format('.'.join(map(str, frontend_version))))
|
||||||
|
|
||||||
event_loop, _, start_all_func = start_comfyui()
|
event_loop, _, start_all_func = start_comfyui()
|
||||||
try:
|
try:
|
||||||
event_loop.run_until_complete(start_all_func())
|
x = start_all_func()
|
||||||
|
warn_frontend_version(frontend_version)
|
||||||
|
event_loop.run_until_complete(x)
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
logging.info("\nStopped server")
|
logging.info("\nStopped server")
|
||||||
|
|
||||||
|
|||||||
8
nodes.py
8
nodes.py
@@ -25,7 +25,7 @@ import comfy.sample
|
|||||||
import comfy.sd
|
import comfy.sd
|
||||||
import comfy.utils
|
import comfy.utils
|
||||||
import comfy.controlnet
|
import comfy.controlnet
|
||||||
from comfy.comfy_types import IO, ComfyNodeABC, InputTypeDict
|
from comfy.comfy_types import IO, ComfyNodeABC, InputTypeDict, FileLocator
|
||||||
|
|
||||||
import comfy.clip_vision
|
import comfy.clip_vision
|
||||||
|
|
||||||
@@ -479,7 +479,7 @@ class SaveLatent:
|
|||||||
|
|
||||||
file = f"{filename}_{counter:05}_.latent"
|
file = f"{filename}_{counter:05}_.latent"
|
||||||
|
|
||||||
results = list()
|
results: list[FileLocator] = []
|
||||||
results.append({
|
results.append({
|
||||||
"filename": file,
|
"filename": file,
|
||||||
"subfolder": subfolder,
|
"subfolder": subfolder,
|
||||||
@@ -1519,7 +1519,7 @@ class KSampler:
|
|||||||
return {
|
return {
|
||||||
"required": {
|
"required": {
|
||||||
"model": ("MODEL", {"tooltip": "The model used for denoising the input latent."}),
|
"model": ("MODEL", {"tooltip": "The model used for denoising the input latent."}),
|
||||||
"seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff, "tooltip": "The random seed used for creating the noise."}),
|
"seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff, "control_after_generate": True, "tooltip": "The random seed used for creating the noise."}),
|
||||||
"steps": ("INT", {"default": 20, "min": 1, "max": 10000, "tooltip": "The number of steps used in the denoising process."}),
|
"steps": ("INT", {"default": 20, "min": 1, "max": 10000, "tooltip": "The number of steps used in the denoising process."}),
|
||||||
"cfg": ("FLOAT", {"default": 8.0, "min": 0.0, "max": 100.0, "step":0.1, "round": 0.01, "tooltip": "The Classifier-Free Guidance scale balances creativity and adherence to the prompt. Higher values result in images more closely matching the prompt however too high values will negatively impact quality."}),
|
"cfg": ("FLOAT", {"default": 8.0, "min": 0.0, "max": 100.0, "step":0.1, "round": 0.01, "tooltip": "The Classifier-Free Guidance scale balances creativity and adherence to the prompt. Higher values result in images more closely matching the prompt however too high values will negatively impact quality."}),
|
||||||
"sampler_name": (comfy.samplers.KSampler.SAMPLERS, {"tooltip": "The algorithm used when sampling, this can affect the quality, speed, and style of the generated output."}),
|
"sampler_name": (comfy.samplers.KSampler.SAMPLERS, {"tooltip": "The algorithm used when sampling, this can affect the quality, speed, and style of the generated output."}),
|
||||||
@@ -1547,7 +1547,7 @@ class KSamplerAdvanced:
|
|||||||
return {"required":
|
return {"required":
|
||||||
{"model": ("MODEL",),
|
{"model": ("MODEL",),
|
||||||
"add_noise": (["enable", "disable"], ),
|
"add_noise": (["enable", "disable"], ),
|
||||||
"noise_seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}),
|
"noise_seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff, "control_after_generate": True}),
|
||||||
"steps": ("INT", {"default": 20, "min": 1, "max": 10000}),
|
"steps": ("INT", {"default": 20, "min": 1, "max": 10000}),
|
||||||
"cfg": ("FLOAT", {"default": 8.0, "min": 0.0, "max": 100.0, "step":0.1, "round": 0.01}),
|
"cfg": ("FLOAT", {"default": 8.0, "min": 0.0, "max": 100.0, "step":0.1, "round": 0.01}),
|
||||||
"sampler_name": (comfy.samplers.KSampler.SAMPLERS, ),
|
"sampler_name": (comfy.samplers.KSampler.SAMPLERS, ),
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[project]
|
[project]
|
||||||
name = "ComfyUI"
|
name = "ComfyUI"
|
||||||
version = "0.3.20"
|
version = "0.3.25"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
license = { file = "LICENSE" }
|
license = { file = "LICENSE" }
|
||||||
requires-python = ">=3.9"
|
requires-python = ">=3.9"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
comfyui-frontend-package==1.10.17
|
comfyui-frontend-package==1.11.8
|
||||||
torch
|
torch
|
||||||
torchsde
|
torchsde
|
||||||
torchvision
|
torchvision
|
||||||
|
|||||||
Reference in New Issue
Block a user