diff --git a/ComfyUI/comfy/ldm/ace/vae/autoencoder_dc.py b/ComfyUI/comfy/ldm/ace/vae/autoencoder_dc.py new file mode 100644 index 0000000000000000000000000000000000000000..e7b1d4801e7b3f47f29cb90f41adff4d415868cd --- /dev/null +++ b/ComfyUI/comfy/ldm/ace/vae/autoencoder_dc.py @@ -0,0 +1,644 @@ +# Rewritten from diffusers +import torch +import torch.nn as nn +import torch.nn.functional as F +from typing import Tuple, Union + +import comfy.model_management +import comfy.ops +ops = comfy.ops.disable_weight_init + + +class RMSNorm(ops.RMSNorm): + def __init__(self, dim, eps=1e-5, elementwise_affine=True, bias=False): + super().__init__(dim, eps=eps, elementwise_affine=elementwise_affine) + if elementwise_affine: + self.bias = nn.Parameter(torch.empty(dim)) if bias else None + + def forward(self, x): + x = super().forward(x) + if self.elementwise_affine: + if self.bias is not None: + x = x + comfy.model_management.cast_to(self.bias, dtype=x.dtype, device=x.device) + return x + + +def get_normalization(norm_type, num_features, num_groups=32, eps=1e-5): + if norm_type == "batch_norm": + return nn.BatchNorm2d(num_features) + elif norm_type == "group_norm": + return ops.GroupNorm(num_groups, num_features) + elif norm_type == "layer_norm": + return ops.LayerNorm(num_features) + elif norm_type == "rms_norm": + return RMSNorm(num_features, eps=eps, elementwise_affine=True, bias=True) + else: + raise ValueError(f"Unknown normalization type: {norm_type}") + + +def get_activation(activation_type): + if activation_type == "relu": + return nn.ReLU() + elif activation_type == "relu6": + return nn.ReLU6() + elif activation_type == "silu": + return nn.SiLU() + elif activation_type == "leaky_relu": + return nn.LeakyReLU(0.2) + else: + raise ValueError(f"Unknown activation type: {activation_type}") + + +class ResBlock(nn.Module): + def __init__( + self, + in_channels: int, + out_channels: int, + norm_type: str = "batch_norm", + act_fn: str = "relu6", + ) -> None: + super().__init__() + + self.norm_type = norm_type + self.nonlinearity = get_activation(act_fn) if act_fn is not None else nn.Identity() + self.conv1 = ops.Conv2d(in_channels, in_channels, 3, 1, 1) + self.conv2 = ops.Conv2d(in_channels, out_channels, 3, 1, 1, bias=False) + self.norm = get_normalization(norm_type, out_channels) + + def forward(self, hidden_states: torch.Tensor) -> torch.Tensor: + residual = hidden_states + hidden_states = self.conv1(hidden_states) + hidden_states = self.nonlinearity(hidden_states) + hidden_states = self.conv2(hidden_states) + + if self.norm_type == "rms_norm": + # move channel to the last dimension so we apply RMSnorm across channel dimension + hidden_states = self.norm(hidden_states.movedim(1, -1)).movedim(-1, 1) + else: + hidden_states = self.norm(hidden_states) + + return hidden_states + residual + +class SanaMultiscaleAttentionProjection(nn.Module): + def __init__( + self, + in_channels: int, + num_attention_heads: int, + kernel_size: int, + ) -> None: + super().__init__() + + channels = 3 * in_channels + self.proj_in = ops.Conv2d( + channels, + channels, + kernel_size, + padding=kernel_size // 2, + groups=channels, + bias=False, + ) + self.proj_out = ops.Conv2d(channels, channels, 1, 1, 0, groups=3 * num_attention_heads, bias=False) + + def forward(self, hidden_states: torch.Tensor) -> torch.Tensor: + hidden_states = self.proj_in(hidden_states) + hidden_states = self.proj_out(hidden_states) + return hidden_states + +class SanaMultiscaleLinearAttention(nn.Module): + def __init__( + self, + in_channels: int, + out_channels: int, + num_attention_heads: int = None, + attention_head_dim: int = 8, + mult: float = 1.0, + norm_type: str = "batch_norm", + kernel_sizes: tuple = (5,), + eps: float = 1e-15, + residual_connection: bool = False, + ): + super().__init__() + + self.eps = eps + self.attention_head_dim = attention_head_dim + self.norm_type = norm_type + self.residual_connection = residual_connection + + num_attention_heads = ( + int(in_channels // attention_head_dim * mult) + if num_attention_heads is None + else num_attention_heads + ) + inner_dim = num_attention_heads * attention_head_dim + + self.to_q = ops.Linear(in_channels, inner_dim, bias=False) + self.to_k = ops.Linear(in_channels, inner_dim, bias=False) + self.to_v = ops.Linear(in_channels, inner_dim, bias=False) + + self.to_qkv_multiscale = nn.ModuleList() + for kernel_size in kernel_sizes: + self.to_qkv_multiscale.append( + SanaMultiscaleAttentionProjection(inner_dim, num_attention_heads, kernel_size) + ) + + self.nonlinearity = nn.ReLU() + self.to_out = ops.Linear(inner_dim * (1 + len(kernel_sizes)), out_channels, bias=False) + self.norm_out = get_normalization(norm_type, out_channels) + + def apply_linear_attention(self, query, key, value): + value = F.pad(value, (0, 0, 0, 1), mode="constant", value=1) + scores = torch.matmul(value, key.transpose(-1, -2)) + hidden_states = torch.matmul(scores, query) + + hidden_states = hidden_states.to(dtype=torch.float32) + hidden_states = hidden_states[:, :, :-1] / (hidden_states[:, :, -1:] + self.eps) + return hidden_states + + def apply_quadratic_attention(self, query, key, value): + scores = torch.matmul(key.transpose(-1, -2), query) + scores = scores.to(dtype=torch.float32) + scores = scores / (torch.sum(scores, dim=2, keepdim=True) + self.eps) + hidden_states = torch.matmul(value, scores.to(value.dtype)) + return hidden_states + + def forward(self, hidden_states): + height, width = hidden_states.shape[-2:] + if height * width > self.attention_head_dim: + use_linear_attention = True + else: + use_linear_attention = False + + residual = hidden_states + + batch_size, _, height, width = list(hidden_states.size()) + original_dtype = hidden_states.dtype + + hidden_states = hidden_states.movedim(1, -1) + query = self.to_q(hidden_states) + key = self.to_k(hidden_states) + value = self.to_v(hidden_states) + hidden_states = torch.cat([query, key, value], dim=3) + hidden_states = hidden_states.movedim(-1, 1) + + multi_scale_qkv = [hidden_states] + for block in self.to_qkv_multiscale: + multi_scale_qkv.append(block(hidden_states)) + + hidden_states = torch.cat(multi_scale_qkv, dim=1) + + if use_linear_attention: + # for linear attention upcast hidden_states to float32 + hidden_states = hidden_states.to(dtype=torch.float32) + + hidden_states = hidden_states.reshape(batch_size, -1, 3 * self.attention_head_dim, height * width) + + query, key, value = hidden_states.chunk(3, dim=2) + query = self.nonlinearity(query) + key = self.nonlinearity(key) + + if use_linear_attention: + hidden_states = self.apply_linear_attention(query, key, value) + hidden_states = hidden_states.to(dtype=original_dtype) + else: + hidden_states = self.apply_quadratic_attention(query, key, value) + + hidden_states = torch.reshape(hidden_states, (batch_size, -1, height, width)) + hidden_states = self.to_out(hidden_states.movedim(1, -1)).movedim(-1, 1) + + if self.norm_type == "rms_norm": + hidden_states = self.norm_out(hidden_states.movedim(1, -1)).movedim(-1, 1) + else: + hidden_states = self.norm_out(hidden_states) + + if self.residual_connection: + hidden_states = hidden_states + residual + + return hidden_states + + +class EfficientViTBlock(nn.Module): + def __init__( + self, + in_channels: int, + mult: float = 1.0, + attention_head_dim: int = 32, + qkv_multiscales: tuple = (5,), + norm_type: str = "batch_norm", + ) -> None: + super().__init__() + + self.attn = SanaMultiscaleLinearAttention( + in_channels=in_channels, + out_channels=in_channels, + mult=mult, + attention_head_dim=attention_head_dim, + norm_type=norm_type, + kernel_sizes=qkv_multiscales, + residual_connection=True, + ) + + self.conv_out = GLUMBConv( + in_channels=in_channels, + out_channels=in_channels, + norm_type="rms_norm", + ) + + def forward(self, x: torch.Tensor) -> torch.Tensor: + x = self.attn(x) + x = self.conv_out(x) + return x + + +class GLUMBConv(nn.Module): + def __init__( + self, + in_channels: int, + out_channels: int, + expand_ratio: float = 4, + norm_type: str = None, + residual_connection: bool = True, + ) -> None: + super().__init__() + + hidden_channels = int(expand_ratio * in_channels) + self.norm_type = norm_type + self.residual_connection = residual_connection + + self.nonlinearity = nn.SiLU() + self.conv_inverted = ops.Conv2d(in_channels, hidden_channels * 2, 1, 1, 0) + self.conv_depth = ops.Conv2d(hidden_channels * 2, hidden_channels * 2, 3, 1, 1, groups=hidden_channels * 2) + self.conv_point = ops.Conv2d(hidden_channels, out_channels, 1, 1, 0, bias=False) + + self.norm = None + if norm_type == "rms_norm": + self.norm = RMSNorm(out_channels, eps=1e-5, elementwise_affine=True, bias=True) + + def forward(self, hidden_states: torch.Tensor) -> torch.Tensor: + if self.residual_connection: + residual = hidden_states + + hidden_states = self.conv_inverted(hidden_states) + hidden_states = self.nonlinearity(hidden_states) + + hidden_states = self.conv_depth(hidden_states) + hidden_states, gate = torch.chunk(hidden_states, 2, dim=1) + hidden_states = hidden_states * self.nonlinearity(gate) + + hidden_states = self.conv_point(hidden_states) + + if self.norm_type == "rms_norm": + # move channel to the last dimension so we apply RMSnorm across channel dimension + hidden_states = self.norm(hidden_states.movedim(1, -1)).movedim(-1, 1) + + if self.residual_connection: + hidden_states = hidden_states + residual + + return hidden_states + + +def get_block( + block_type: str, + in_channels: int, + out_channels: int, + attention_head_dim: int, + norm_type: str, + act_fn: str, + qkv_mutliscales: tuple = (), +): + if block_type == "ResBlock": + block = ResBlock(in_channels, out_channels, norm_type, act_fn) + elif block_type == "EfficientViTBlock": + block = EfficientViTBlock( + in_channels, + attention_head_dim=attention_head_dim, + norm_type=norm_type, + qkv_multiscales=qkv_mutliscales + ) + else: + raise ValueError(f"Block with {block_type=} is not supported.") + + return block + + +class DCDownBlock2d(nn.Module): + def __init__(self, in_channels: int, out_channels: int, downsample: bool = False, shortcut: bool = True) -> None: + super().__init__() + + self.downsample = downsample + self.factor = 2 + self.stride = 1 if downsample else 2 + self.group_size = in_channels * self.factor**2 // out_channels + self.shortcut = shortcut + + out_ratio = self.factor**2 + if downsample: + assert out_channels % out_ratio == 0 + out_channels = out_channels // out_ratio + + self.conv = ops.Conv2d( + in_channels, + out_channels, + kernel_size=3, + stride=self.stride, + padding=1, + ) + + def forward(self, hidden_states: torch.Tensor) -> torch.Tensor: + x = self.conv(hidden_states) + if self.downsample: + x = F.pixel_unshuffle(x, self.factor) + + if self.shortcut: + y = F.pixel_unshuffle(hidden_states, self.factor) + y = y.unflatten(1, (-1, self.group_size)) + y = y.mean(dim=2) + hidden_states = x + y + else: + hidden_states = x + + return hidden_states + + +class DCUpBlock2d(nn.Module): + def __init__( + self, + in_channels: int, + out_channels: int, + interpolate: bool = False, + shortcut: bool = True, + interpolation_mode: str = "nearest", + ) -> None: + super().__init__() + + self.interpolate = interpolate + self.interpolation_mode = interpolation_mode + self.shortcut = shortcut + self.factor = 2 + self.repeats = out_channels * self.factor**2 // in_channels + + out_ratio = self.factor**2 + if not interpolate: + out_channels = out_channels * out_ratio + + self.conv = ops.Conv2d(in_channels, out_channels, 3, 1, 1) + + def forward(self, hidden_states: torch.Tensor) -> torch.Tensor: + if self.interpolate: + x = F.interpolate(hidden_states, scale_factor=self.factor, mode=self.interpolation_mode) + x = self.conv(x) + else: + x = self.conv(hidden_states) + x = F.pixel_shuffle(x, self.factor) + + if self.shortcut: + y = hidden_states.repeat_interleave(self.repeats, dim=1, output_size=hidden_states.shape[1] * self.repeats) + y = F.pixel_shuffle(y, self.factor) + hidden_states = x + y + else: + hidden_states = x + + return hidden_states + + +class Encoder(nn.Module): + def __init__( + self, + in_channels: int, + latent_channels: int, + attention_head_dim: int = 32, + block_type: str or tuple = "ResBlock", + block_out_channels: tuple = (128, 256, 512, 512, 1024, 1024), + layers_per_block: tuple = (2, 2, 2, 2, 2, 2), + qkv_multiscales: tuple = ((), (), (), (5,), (5,), (5,)), + downsample_block_type: str = "pixel_unshuffle", + out_shortcut: bool = True, + ): + super().__init__() + + num_blocks = len(block_out_channels) + + if isinstance(block_type, str): + block_type = (block_type,) * num_blocks + + if layers_per_block[0] > 0: + self.conv_in = ops.Conv2d( + in_channels, + block_out_channels[0] if layers_per_block[0] > 0 else block_out_channels[1], + kernel_size=3, + stride=1, + padding=1, + ) + else: + self.conv_in = DCDownBlock2d( + in_channels=in_channels, + out_channels=block_out_channels[0] if layers_per_block[0] > 0 else block_out_channels[1], + downsample=downsample_block_type == "pixel_unshuffle", + shortcut=False, + ) + + down_blocks = [] + for i, (out_channel, num_layers) in enumerate(zip(block_out_channels, layers_per_block)): + down_block_list = [] + + for _ in range(num_layers): + block = get_block( + block_type[i], + out_channel, + out_channel, + attention_head_dim=attention_head_dim, + norm_type="rms_norm", + act_fn="silu", + qkv_mutliscales=qkv_multiscales[i], + ) + down_block_list.append(block) + + if i < num_blocks - 1 and num_layers > 0: + downsample_block = DCDownBlock2d( + in_channels=out_channel, + out_channels=block_out_channels[i + 1], + downsample=downsample_block_type == "pixel_unshuffle", + shortcut=True, + ) + down_block_list.append(downsample_block) + + down_blocks.append(nn.Sequential(*down_block_list)) + + self.down_blocks = nn.ModuleList(down_blocks) + + self.conv_out = ops.Conv2d(block_out_channels[-1], latent_channels, 3, 1, 1) + + self.out_shortcut = out_shortcut + if out_shortcut: + self.out_shortcut_average_group_size = block_out_channels[-1] // latent_channels + + def forward(self, hidden_states: torch.Tensor) -> torch.Tensor: + hidden_states = self.conv_in(hidden_states) + for down_block in self.down_blocks: + hidden_states = down_block(hidden_states) + + if self.out_shortcut: + x = hidden_states.unflatten(1, (-1, self.out_shortcut_average_group_size)) + x = x.mean(dim=2) + hidden_states = self.conv_out(hidden_states) + x + else: + hidden_states = self.conv_out(hidden_states) + + return hidden_states + + +class Decoder(nn.Module): + def __init__( + self, + in_channels: int, + latent_channels: int, + attention_head_dim: int = 32, + block_type: str or tuple = "ResBlock", + block_out_channels: tuple = (128, 256, 512, 512, 1024, 1024), + layers_per_block: tuple = (2, 2, 2, 2, 2, 2), + qkv_multiscales: tuple = ((), (), (), (5,), (5,), (5,)), + norm_type: str or tuple = "rms_norm", + act_fn: str or tuple = "silu", + upsample_block_type: str = "pixel_shuffle", + in_shortcut: bool = True, + ): + super().__init__() + + num_blocks = len(block_out_channels) + + if isinstance(block_type, str): + block_type = (block_type,) * num_blocks + if isinstance(norm_type, str): + norm_type = (norm_type,) * num_blocks + if isinstance(act_fn, str): + act_fn = (act_fn,) * num_blocks + + self.conv_in = ops.Conv2d(latent_channels, block_out_channels[-1], 3, 1, 1) + + self.in_shortcut = in_shortcut + if in_shortcut: + self.in_shortcut_repeats = block_out_channels[-1] // latent_channels + + up_blocks = [] + for i, (out_channel, num_layers) in reversed(list(enumerate(zip(block_out_channels, layers_per_block)))): + up_block_list = [] + + if i < num_blocks - 1 and num_layers > 0: + upsample_block = DCUpBlock2d( + block_out_channels[i + 1], + out_channel, + interpolate=upsample_block_type == "interpolate", + shortcut=True, + ) + up_block_list.append(upsample_block) + + for _ in range(num_layers): + block = get_block( + block_type[i], + out_channel, + out_channel, + attention_head_dim=attention_head_dim, + norm_type=norm_type[i], + act_fn=act_fn[i], + qkv_mutliscales=qkv_multiscales[i], + ) + up_block_list.append(block) + + up_blocks.insert(0, nn.Sequential(*up_block_list)) + + self.up_blocks = nn.ModuleList(up_blocks) + + channels = block_out_channels[0] if layers_per_block[0] > 0 else block_out_channels[1] + + self.norm_out = RMSNorm(channels, 1e-5, elementwise_affine=True, bias=True) + self.conv_act = nn.ReLU() + self.conv_out = None + + if layers_per_block[0] > 0: + self.conv_out = ops.Conv2d(channels, in_channels, 3, 1, 1) + else: + self.conv_out = DCUpBlock2d( + channels, in_channels, interpolate=upsample_block_type == "interpolate", shortcut=False + ) + + def forward(self, hidden_states: torch.Tensor) -> torch.Tensor: + if self.in_shortcut: + x = hidden_states.repeat_interleave( + self.in_shortcut_repeats, dim=1, output_size=hidden_states.shape[1] * self.in_shortcut_repeats + ) + hidden_states = self.conv_in(hidden_states) + x + else: + hidden_states = self.conv_in(hidden_states) + + for up_block in reversed(self.up_blocks): + hidden_states = up_block(hidden_states) + + hidden_states = self.norm_out(hidden_states.movedim(1, -1)).movedim(-1, 1) + hidden_states = self.conv_act(hidden_states) + hidden_states = self.conv_out(hidden_states) + return hidden_states + + +class AutoencoderDC(nn.Module): + def __init__( + self, + in_channels: int = 2, + latent_channels: int = 8, + attention_head_dim: int = 32, + encoder_block_types: Union[str, Tuple[str]] = ["ResBlock", "ResBlock", "ResBlock", "EfficientViTBlock"], + decoder_block_types: Union[str, Tuple[str]] = ["ResBlock", "ResBlock", "ResBlock", "EfficientViTBlock"], + encoder_block_out_channels: Tuple[int, ...] = (128, 256, 512, 1024), + decoder_block_out_channels: Tuple[int, ...] = (128, 256, 512, 1024), + encoder_layers_per_block: Tuple[int] = (2, 2, 3, 3), + decoder_layers_per_block: Tuple[int] = (3, 3, 3, 3), + encoder_qkv_multiscales: Tuple[Tuple[int, ...], ...] = ((), (), (5,), (5,)), + decoder_qkv_multiscales: Tuple[Tuple[int, ...], ...] = ((), (), (5,), (5,)), + upsample_block_type: str = "interpolate", + downsample_block_type: str = "Conv", + decoder_norm_types: Union[str, Tuple[str]] = "rms_norm", + decoder_act_fns: Union[str, Tuple[str]] = "silu", + scaling_factor: float = 0.41407, + ) -> None: + super().__init__() + + self.encoder = Encoder( + in_channels=in_channels, + latent_channels=latent_channels, + attention_head_dim=attention_head_dim, + block_type=encoder_block_types, + block_out_channels=encoder_block_out_channels, + layers_per_block=encoder_layers_per_block, + qkv_multiscales=encoder_qkv_multiscales, + downsample_block_type=downsample_block_type, + ) + + self.decoder = Decoder( + in_channels=in_channels, + latent_channels=latent_channels, + attention_head_dim=attention_head_dim, + block_type=decoder_block_types, + block_out_channels=decoder_block_out_channels, + layers_per_block=decoder_layers_per_block, + qkv_multiscales=decoder_qkv_multiscales, + norm_type=decoder_norm_types, + act_fn=decoder_act_fns, + upsample_block_type=upsample_block_type, + ) + + self.scaling_factor = scaling_factor + self.spatial_compression_ratio = 2 ** (len(encoder_block_out_channels) - 1) + + def encode(self, x: torch.Tensor) -> torch.Tensor: + """Internal encoding function.""" + encoded = self.encoder(x) + return encoded * self.scaling_factor + + def decode(self, z: torch.Tensor) -> torch.Tensor: + # Scale the latents back + z = z / self.scaling_factor + decoded = self.decoder(z) + return decoded + + def forward(self, x: torch.Tensor) -> torch.Tensor: + z = self.encode(x) + return self.decode(z) + diff --git a/ComfyUI/comfy/ldm/ace/vae/music_dcae_pipeline.py b/ComfyUI/comfy/ldm/ace/vae/music_dcae_pipeline.py new file mode 100644 index 0000000000000000000000000000000000000000..af81280eb0dd5fce49c0aa4b86a4f0feb841b3fd --- /dev/null +++ b/ComfyUI/comfy/ldm/ace/vae/music_dcae_pipeline.py @@ -0,0 +1,109 @@ +# Original from: https://github.com/ace-step/ACE-Step/blob/main/music_dcae/music_dcae_pipeline.py +import torch +from .autoencoder_dc import AutoencoderDC +import logging +try: + import torchaudio +except: + logging.warning("torchaudio missing, ACE model will be broken") + +import torchvision.transforms as transforms +from .music_vocoder import ADaMoSHiFiGANV1 + + +class MusicDCAE(torch.nn.Module): + def __init__(self, source_sample_rate=None, dcae_config={}, vocoder_config={}): + super(MusicDCAE, self).__init__() + + self.dcae = AutoencoderDC(**dcae_config) + self.vocoder = ADaMoSHiFiGANV1(**vocoder_config) + + if source_sample_rate is None: + self.source_sample_rate = 48000 + else: + self.source_sample_rate = source_sample_rate + + # self.resampler = torchaudio.transforms.Resample(source_sample_rate, 44100) + + self.transform = transforms.Compose([ + transforms.Normalize(0.5, 0.5), + ]) + self.min_mel_value = -11.0 + self.max_mel_value = 3.0 + self.audio_chunk_size = int(round((1024 * 512 / 44100 * 48000))) + self.mel_chunk_size = 1024 + self.time_dimention_multiple = 8 + self.latent_chunk_size = self.mel_chunk_size // self.time_dimention_multiple + self.scale_factor = 0.1786 + self.shift_factor = -1.9091 + + def load_audio(self, audio_path): + audio, sr = torchaudio.load(audio_path) + return audio, sr + + def forward_mel(self, audios): + mels = [] + for i in range(len(audios)): + image = self.vocoder.mel_transform(audios[i]) + mels.append(image) + mels = torch.stack(mels) + return mels + + @torch.no_grad() + def encode(self, audios, audio_lengths=None, sr=None): + if audio_lengths is None: + audio_lengths = torch.tensor([audios.shape[2]] * audios.shape[0]) + audio_lengths = audio_lengths.to(audios.device) + + if sr is None: + sr = self.source_sample_rate + + if sr != 44100: + audios = torchaudio.functional.resample(audios, sr, 44100) + + max_audio_len = audios.shape[-1] + if max_audio_len % (8 * 512) != 0: + audios = torch.nn.functional.pad(audios, (0, 8 * 512 - max_audio_len % (8 * 512))) + + mels = self.forward_mel(audios) + mels = (mels - self.min_mel_value) / (self.max_mel_value - self.min_mel_value) + mels = self.transform(mels) + latents = [] + for mel in mels: + latent = self.dcae.encoder(mel.unsqueeze(0)) + latents.append(latent) + latents = torch.cat(latents, dim=0) + # latent_lengths = (audio_lengths / sr * 44100 / 512 / self.time_dimention_multiple).long() + latents = (latents - self.shift_factor) * self.scale_factor + return latents + # return latents, latent_lengths + + @torch.no_grad() + def decode(self, latents, audio_lengths=None, sr=None): + latents = latents / self.scale_factor + self.shift_factor + + pred_wavs = [] + + for latent in latents: + mels = self.dcae.decoder(latent.unsqueeze(0)) + mels = mels * 0.5 + 0.5 + mels = mels * (self.max_mel_value - self.min_mel_value) + self.min_mel_value + wav = self.vocoder.decode(mels[0]).squeeze(1) + + if sr is not None: + # resampler = torchaudio.transforms.Resample(44100, sr).to(latents.device).to(latents.dtype) + wav = torchaudio.functional.resample(wav, 44100, sr) + # wav = resampler(wav) + else: + sr = 44100 + pred_wavs.append(wav) + + if audio_lengths is not None: + pred_wavs = [wav[:, :length].cpu() for wav, length in zip(pred_wavs, audio_lengths)] + return torch.stack(pred_wavs) + # return sr, pred_wavs + + def forward(self, audios, audio_lengths=None, sr=None): + latents, latent_lengths = self.encode(audios=audios, audio_lengths=audio_lengths, sr=sr) + sr, pred_wavs = self.decode(latents=latents, audio_lengths=audio_lengths, sr=sr) + return sr, pred_wavs, latents, latent_lengths diff --git a/ComfyUI/comfy/ldm/ace/vae/music_vocoder.py b/ComfyUI/comfy/ldm/ace/vae/music_vocoder.py new file mode 100644 index 0000000000000000000000000000000000000000..2f989fa86e8c964994886c6a22ed53e742279ec4 --- /dev/null +++ b/ComfyUI/comfy/ldm/ace/vae/music_vocoder.py @@ -0,0 +1,538 @@ +# Original from: https://github.com/ace-step/ACE-Step/blob/main/music_dcae/music_vocoder.py +import torch +from torch import nn + +from functools import partial +from math import prod +from typing import Callable, Tuple, List + +import numpy as np +import torch.nn.functional as F +from torch.nn.utils.parametrize import remove_parametrizations as remove_weight_norm + +from .music_log_mel import LogMelSpectrogram + +import comfy.model_management +import comfy.ops +ops = comfy.ops.disable_weight_init + + +def drop_path( + x, drop_prob: float = 0.0, training: bool = False, scale_by_keep: bool = True +): + """Drop paths (Stochastic Depth) per sample (when applied in main path of residual blocks). + + This is the same as the DropConnect impl I created for EfficientNet, etc networks, however, + the original name is misleading as 'Drop Connect' is a different form of dropout in a separate paper... + See discussion: https://github.com/tensorflow/tpu/issues/494#issuecomment-532968956 ... I've opted for + changing the layer and argument names to 'drop path' rather than mix DropConnect as a layer name and use + 'survival rate' as the argument. + + """ # noqa: E501 + + if drop_prob == 0.0 or not training: + return x + keep_prob = 1 - drop_prob + shape = (x.shape[0],) + (1,) * ( + x.ndim - 1 + ) # work with diff dim tensors, not just 2D ConvNets + random_tensor = x.new_empty(shape).bernoulli_(keep_prob) + if keep_prob > 0.0 and scale_by_keep: + random_tensor.div_(keep_prob) + return x * random_tensor + + +class DropPath(nn.Module): + """Drop paths (Stochastic Depth) per sample (when applied in main path of residual blocks).""" # noqa: E501 + + def __init__(self, drop_prob: float = 0.0, scale_by_keep: bool = True): + super(DropPath, self).__init__() + self.drop_prob = drop_prob + self.scale_by_keep = scale_by_keep + + def forward(self, x): + return drop_path(x, self.drop_prob, self.training, self.scale_by_keep) + + def extra_repr(self): + return f"drop_prob={round(self.drop_prob,3):0.3f}" + + +class LayerNorm(nn.Module): + r"""LayerNorm that supports two data formats: channels_last (default) or channels_first. + The ordering of the dimensions in the inputs. channels_last corresponds to inputs with + shape (batch_size, height, width, channels) while channels_first corresponds to inputs + with shape (batch_size, channels, height, width). + """ # noqa: E501 + + def __init__(self, normalized_shape, eps=1e-6, data_format="channels_last"): + super().__init__() + self.weight = nn.Parameter(torch.ones(normalized_shape)) + self.bias = nn.Parameter(torch.zeros(normalized_shape)) + self.eps = eps + self.data_format = data_format + if self.data_format not in ["channels_last", "channels_first"]: + raise NotImplementedError + self.normalized_shape = (normalized_shape,) + + def forward(self, x): + if self.data_format == "channels_last": + return F.layer_norm( + x, self.normalized_shape, comfy.model_management.cast_to(self.weight, dtype=x.dtype, device=x.device), comfy.model_management.cast_to(self.bias, dtype=x.dtype, device=x.device), self.eps + ) + elif self.data_format == "channels_first": + u = x.mean(1, keepdim=True) + s = (x - u).pow(2).mean(1, keepdim=True) + x = (x - u) / torch.sqrt(s + self.eps) + x = comfy.model_management.cast_to(self.weight[:, None], dtype=x.dtype, device=x.device) * x + comfy.model_management.cast_to(self.bias[:, None], dtype=x.dtype, device=x.device) + return x + + +class ConvNeXtBlock(nn.Module): + r"""ConvNeXt Block. There are two equivalent implementations: + (1) DwConv -> LayerNorm (channels_first) -> 1x1 Conv -> GELU -> 1x1 Conv; all in (N, C, H, W) + (2) DwConv -> Permute to (N, H, W, C); LayerNorm (channels_last) -> Linear -> GELU -> Linear; Permute back + We use (2) as we find it slightly faster in PyTorch + + Args: + dim (int): Number of input channels. + drop_path (float): Stochastic depth rate. Default: 0.0 + layer_scale_init_value (float): Init value for Layer Scale. Default: 1e-6. + mlp_ratio (float): Ratio of mlp hidden dim to embedding dim. Default: 4.0. + kernel_size (int): Kernel size for depthwise conv. Default: 7. + dilation (int): Dilation for depthwise conv. Default: 1. + """ # noqa: E501 + + def __init__( + self, + dim: int, + drop_path: float = 0.0, + layer_scale_init_value: float = 1e-6, + mlp_ratio: float = 4.0, + kernel_size: int = 7, + dilation: int = 1, + ): + super().__init__() + + self.dwconv = ops.Conv1d( + dim, + dim, + kernel_size=kernel_size, + padding=int(dilation * (kernel_size - 1) / 2), + groups=dim, + ) # depthwise conv + self.norm = LayerNorm(dim, eps=1e-6) + self.pwconv1 = ops.Linear( + dim, int(mlp_ratio * dim) + ) # pointwise/1x1 convs, implemented with linear layers + self.act = nn.GELU() + self.pwconv2 = ops.Linear(int(mlp_ratio * dim), dim) + self.gamma = ( + nn.Parameter(torch.empty((dim)), requires_grad=False) + if layer_scale_init_value > 0 + else None + ) + self.drop_path = DropPath( + drop_path) if drop_path > 0.0 else nn.Identity() + + def forward(self, x, apply_residual: bool = True): + input = x + + x = self.dwconv(x) + x = x.permute(0, 2, 1) # (N, C, L) -> (N, L, C) + x = self.norm(x) + x = self.pwconv1(x) + x = self.act(x) + x = self.pwconv2(x) + + if self.gamma is not None: + x = comfy.model_management.cast_to(self.gamma, dtype=x.dtype, device=x.device) * x + + x = x.permute(0, 2, 1) # (N, L, C) -> (N, C, L) + x = self.drop_path(x) + + if apply_residual: + x = input + x + + return x + + +class ParallelConvNeXtBlock(nn.Module): + def __init__(self, kernel_sizes: List[int], *args, **kwargs): + super().__init__() + self.blocks = nn.ModuleList( + [ + ConvNeXtBlock(kernel_size=kernel_size, *args, **kwargs) + for kernel_size in kernel_sizes + ] + ) + + def forward(self, x: torch.Tensor) -> torch.Tensor: + return torch.stack( + [block(x, apply_residual=False) for block in self.blocks] + [x], + dim=1, + ).sum(dim=1) + + +class ConvNeXtEncoder(nn.Module): + def __init__( + self, + input_channels=3, + depths=[3, 3, 9, 3], + dims=[96, 192, 384, 768], + drop_path_rate=0.0, + layer_scale_init_value=1e-6, + kernel_sizes: Tuple[int] = (7,), + ): + super().__init__() + assert len(depths) == len(dims) + + self.channel_layers = nn.ModuleList() + stem = nn.Sequential( + ops.Conv1d( + input_channels, + dims[0], + kernel_size=7, + padding=3, + padding_mode="replicate", + ), + LayerNorm(dims[0], eps=1e-6, data_format="channels_first"), + ) + self.channel_layers.append(stem) + + for i in range(len(depths) - 1): + mid_layer = nn.Sequential( + LayerNorm(dims[i], eps=1e-6, data_format="channels_first"), + ops.Conv1d(dims[i], dims[i + 1], kernel_size=1), + ) + self.channel_layers.append(mid_layer) + + block_fn = ( + partial(ConvNeXtBlock, kernel_size=kernel_sizes[0]) + if len(kernel_sizes) == 1 + else partial(ParallelConvNeXtBlock, kernel_sizes=kernel_sizes) + ) + + self.stages = nn.ModuleList() + drop_path_rates = [ + x.item() for x in torch.linspace(0, drop_path_rate, sum(depths)) + ] + + cur = 0 + for i in range(len(depths)): + stage = nn.Sequential( + *[ + block_fn( + dim=dims[i], + drop_path=drop_path_rates[cur + j], + layer_scale_init_value=layer_scale_init_value, + ) + for j in range(depths[i]) + ] + ) + self.stages.append(stage) + cur += depths[i] + + self.norm = LayerNorm(dims[-1], eps=1e-6, data_format="channels_first") + + def forward( + self, + x: torch.Tensor, + ) -> torch.Tensor: + for channel_layer, stage in zip(self.channel_layers, self.stages): + x = channel_layer(x) + x = stage(x) + + return self.norm(x) + + +def get_padding(kernel_size, dilation=1): + return (kernel_size * dilation - dilation) // 2 + + +class ResBlock1(torch.nn.Module): + def __init__(self, channels, kernel_size=3, dilation=(1, 3, 5)): + super().__init__() + + self.convs1 = nn.ModuleList( + [ + torch.nn.utils.parametrizations.weight_norm( + ops.Conv1d( + channels, + channels, + kernel_size, + 1, + dilation=dilation[0], + padding=get_padding(kernel_size, dilation[0]), + ) + ), + torch.nn.utils.parametrizations.weight_norm( + ops.Conv1d( + channels, + channels, + kernel_size, + 1, + dilation=dilation[1], + padding=get_padding(kernel_size, dilation[1]), + ) + ), + torch.nn.utils.parametrizations.weight_norm( + ops.Conv1d( + channels, + channels, + kernel_size, + 1, + dilation=dilation[2], + padding=get_padding(kernel_size, dilation[2]), + ) + ), + ] + ) + + self.convs2 = nn.ModuleList( + [ + torch.nn.utils.parametrizations.weight_norm( + ops.Conv1d( + channels, + channels, + kernel_size, + 1, + dilation=1, + padding=get_padding(kernel_size, 1), + ) + ), + torch.nn.utils.parametrizations.weight_norm( + ops.Conv1d( + channels, + channels, + kernel_size, + 1, + dilation=1, + padding=get_padding(kernel_size, 1), + ) + ), + torch.nn.utils.parametrizations.weight_norm( + ops.Conv1d( + channels, + channels, + kernel_size, + 1, + dilation=1, + padding=get_padding(kernel_size, 1), + ) + ), + ] + ) + + def forward(self, x): + for c1, c2 in zip(self.convs1, self.convs2): + xt = F.silu(x) + xt = c1(xt) + xt = F.silu(xt) + xt = c2(xt) + x = xt + x + return x + + def remove_weight_norm(self): + for conv in self.convs1: + remove_weight_norm(conv) + for conv in self.convs2: + remove_weight_norm(conv) + + +class HiFiGANGenerator(nn.Module): + def __init__( + self, + *, + hop_length: int = 512, + upsample_rates: Tuple[int] = (8, 8, 2, 2, 2), + upsample_kernel_sizes: Tuple[int] = (16, 16, 8, 2, 2), + resblock_kernel_sizes: Tuple[int] = (3, 7, 11), + resblock_dilation_sizes: Tuple[Tuple[int]] = ( + (1, 3, 5), (1, 3, 5), (1, 3, 5)), + num_mels: int = 128, + upsample_initial_channel: int = 512, + use_template: bool = True, + pre_conv_kernel_size: int = 7, + post_conv_kernel_size: int = 7, + post_activation: Callable = partial(nn.SiLU, inplace=True), + ): + super().__init__() + + assert ( + prod(upsample_rates) == hop_length + ), f"hop_length must be {prod(upsample_rates)}" + + self.conv_pre = torch.nn.utils.parametrizations.weight_norm( + ops.Conv1d( + num_mels, + upsample_initial_channel, + pre_conv_kernel_size, + 1, + padding=get_padding(pre_conv_kernel_size), + ) + ) + + self.num_upsamples = len(upsample_rates) + self.num_kernels = len(resblock_kernel_sizes) + + self.noise_convs = nn.ModuleList() + self.use_template = use_template + self.ups = nn.ModuleList() + + for i, (u, k) in enumerate(zip(upsample_rates, upsample_kernel_sizes)): + c_cur = upsample_initial_channel // (2 ** (i + 1)) + self.ups.append( + torch.nn.utils.parametrizations.weight_norm( + ops.ConvTranspose1d( + upsample_initial_channel // (2**i), + upsample_initial_channel // (2 ** (i + 1)), + k, + u, + padding=(k - u) // 2, + ) + ) + ) + + if not use_template: + continue + + if i + 1 < len(upsample_rates): + stride_f0 = np.prod(upsample_rates[i + 1:]) + self.noise_convs.append( + ops.Conv1d( + 1, + c_cur, + kernel_size=stride_f0 * 2, + stride=stride_f0, + padding=stride_f0 // 2, + ) + ) + else: + self.noise_convs.append(ops.Conv1d(1, c_cur, kernel_size=1)) + + self.resblocks = nn.ModuleList() + for i in range(len(self.ups)): + ch = upsample_initial_channel // (2 ** (i + 1)) + for k, d in zip(resblock_kernel_sizes, resblock_dilation_sizes): + self.resblocks.append(ResBlock1(ch, k, d)) + + self.activation_post = post_activation() + self.conv_post = torch.nn.utils.parametrizations.weight_norm( + ops.Conv1d( + ch, + 1, + post_conv_kernel_size, + 1, + padding=get_padding(post_conv_kernel_size), + ) + ) + + def forward(self, x, template=None): + x = self.conv_pre(x) + + for i in range(self.num_upsamples): + x = F.silu(x, inplace=True) + x = self.ups[i](x) + + if self.use_template: + x = x + self.noise_convs[i](template) + + xs = None + + for j in range(self.num_kernels): + if xs is None: + xs = self.resblocks[i * self.num_kernels + j](x) + else: + xs += self.resblocks[i * self.num_kernels + j](x) + + x = xs / self.num_kernels + + x = self.activation_post(x) + x = self.conv_post(x) + x = torch.tanh(x) + + return x + + def remove_weight_norm(self): + for up in self.ups: + remove_weight_norm(up) + for block in self.resblocks: + block.remove_weight_norm() + remove_weight_norm(self.conv_pre) + remove_weight_norm(self.conv_post) + + +class ADaMoSHiFiGANV1(nn.Module): + def __init__( + self, + input_channels: int = 128, + depths: List[int] = [3, 3, 9, 3], + dims: List[int] = [128, 256, 384, 512], + drop_path_rate: float = 0.0, + kernel_sizes: Tuple[int] = (7,), + upsample_rates: Tuple[int] = (4, 4, 2, 2, 2, 2, 2), + upsample_kernel_sizes: Tuple[int] = (8, 8, 4, 4, 4, 4, 4), + resblock_kernel_sizes: Tuple[int] = (3, 7, 11, 13), + resblock_dilation_sizes: Tuple[Tuple[int]] = ( + (1, 3, 5), (1, 3, 5), (1, 3, 5), (1, 3, 5)), + num_mels: int = 512, + upsample_initial_channel: int = 1024, + use_template: bool = False, + pre_conv_kernel_size: int = 13, + post_conv_kernel_size: int = 13, + sampling_rate: int = 44100, + n_fft: int = 2048, + win_length: int = 2048, + hop_length: int = 512, + f_min: int = 40, + f_max: int = 16000, + n_mels: int = 128, + ): + super().__init__() + + self.backbone = ConvNeXtEncoder( + input_channels=input_channels, + depths=depths, + dims=dims, + drop_path_rate=drop_path_rate, + kernel_sizes=kernel_sizes, + ) + + self.head = HiFiGANGenerator( + hop_length=hop_length, + upsample_rates=upsample_rates, + upsample_kernel_sizes=upsample_kernel_sizes, + resblock_kernel_sizes=resblock_kernel_sizes, + resblock_dilation_sizes=resblock_dilation_sizes, + num_mels=num_mels, + upsample_initial_channel=upsample_initial_channel, + use_template=use_template, + pre_conv_kernel_size=pre_conv_kernel_size, + post_conv_kernel_size=post_conv_kernel_size, + ) + self.sampling_rate = sampling_rate + self.mel_transform = LogMelSpectrogram( + sample_rate=sampling_rate, + n_fft=n_fft, + win_length=win_length, + hop_length=hop_length, + f_min=f_min, + f_max=f_max, + n_mels=n_mels, + ) + self.eval() + + @torch.no_grad() + def decode(self, mel): + y = self.backbone(mel) + y = self.head(y) + return y + + @torch.no_grad() + def encode(self, x): + return self.mel_transform(x) + + def forward(self, mel): + y = self.backbone(mel) + y = self.head(y) + return y diff --git a/ComfyUI/comfy/ldm/cosmos/cosmos_tokenizer/layers3d.py b/ComfyUI/comfy/ldm/cosmos/cosmos_tokenizer/layers3d.py new file mode 100644 index 0000000000000000000000000000000000000000..9a3ebed6aa5362c79aff91c088cce8ba7b6f3141 --- /dev/null +++ b/ComfyUI/comfy/ldm/cosmos/cosmos_tokenizer/layers3d.py @@ -0,0 +1,1041 @@ +# SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""The model definition for 3D layers + +Adapted from: https://github.com/lucidrains/magvit2-pytorch/blob/ +9f49074179c912736e617d61b32be367eb5f993a/magvit2_pytorch/magvit2_pytorch.py#L889 + +[MIT License Copyright (c) 2023 Phil Wang] +https://github.com/lucidrains/magvit2-pytorch/blob/ +9f49074179c912736e617d61b32be367eb5f993a/LICENSE +""" +import math +from typing import Tuple, Union + +import numpy as np +import torch +import torch.nn as nn +import torch.nn.functional as F +import logging + +from comfy.ldm.modules.diffusionmodules.model import vae_attention + +from .patching import ( + Patcher, + Patcher3D, + UnPatcher, + UnPatcher3D, +) +from .utils import ( + CausalNormalize, + batch2space, + batch2time, + cast_tuple, + is_odd, + nonlinearity, + replication_pad, + space2batch, + time2batch, +) + +import comfy.ops +ops = comfy.ops.disable_weight_init + +_LEGACY_NUM_GROUPS = 32 + + +class CausalConv3d(nn.Module): + def __init__( + self, + chan_in: int = 1, + chan_out: int = 1, + kernel_size: Union[int, Tuple[int, int, int]] = 3, + pad_mode: str = "constant", + **kwargs, + ): + super().__init__() + kernel_size = cast_tuple(kernel_size, 3) + + time_kernel_size, height_kernel_size, width_kernel_size = kernel_size + + assert is_odd(height_kernel_size) and is_odd(width_kernel_size) + + dilation = kwargs.pop("dilation", 1) + stride = kwargs.pop("stride", 1) + time_stride = kwargs.pop("time_stride", 1) + time_dilation = kwargs.pop("time_dilation", 1) + padding = kwargs.pop("padding", 1) + + self.pad_mode = pad_mode + time_pad = time_dilation * (time_kernel_size - 1) + (1 - time_stride) + self.time_pad = time_pad + + self.spatial_pad = (padding, padding, padding, padding) + + stride = (time_stride, stride, stride) + dilation = (time_dilation, dilation, dilation) + self.conv3d = ops.Conv3d( + chan_in, + chan_out, + kernel_size, + stride=stride, + dilation=dilation, + **kwargs, + ) + + def _replication_pad(self, x: torch.Tensor) -> torch.Tensor: + x_prev = x[:, :, :1, ...].repeat(1, 1, self.time_pad, 1, 1) + x = torch.cat([x_prev, x], dim=2) + padding = self.spatial_pad + (0, 0) + return F.pad(x, padding, mode=self.pad_mode, value=0.0) + + def forward(self, x: torch.Tensor) -> torch.Tensor: + x = self._replication_pad(x) + return self.conv3d(x) + + +class CausalUpsample3d(nn.Module): + def __init__(self, in_channels: int) -> None: + super().__init__() + self.conv = CausalConv3d( + in_channels, in_channels, kernel_size=3, stride=1, padding=1 + ) + + def forward(self, x: torch.Tensor) -> torch.Tensor: + x = x.repeat_interleave(2, dim=3).repeat_interleave(2, dim=4) + time_factor = 1.0 + 1.0 * (x.shape[2] > 1) + if isinstance(time_factor, torch.Tensor): + time_factor = time_factor.item() + x = x.repeat_interleave(int(time_factor), dim=2) + # TODO(freda): Check if this causes temporal inconsistency. + # Shoule reverse the order of the following two ops, + # better perf and better temporal smoothness. + x = self.conv(x) + return x[..., int(time_factor - 1) :, :, :] + + +class CausalDownsample3d(nn.Module): + def __init__(self, in_channels: int) -> None: + super().__init__() + self.conv = CausalConv3d( + in_channels, + in_channels, + kernel_size=3, + stride=2, + time_stride=2, + padding=0, + ) + + def forward(self, x: torch.Tensor) -> torch.Tensor: + pad = (0, 1, 0, 1, 0, 0) + x = F.pad(x, pad, mode="constant", value=0) + x = replication_pad(x) + x = self.conv(x) + return x + + +class CausalHybridUpsample3d(nn.Module): + def __init__( + self, + in_channels: int, + spatial_up: bool = True, + temporal_up: bool = True, + **kwargs, + ) -> None: + super().__init__() + self.spatial_up = spatial_up + self.temporal_up = temporal_up + if not self.spatial_up and not self.temporal_up: + return + + self.conv1 = CausalConv3d( + in_channels, + in_channels, + kernel_size=(3, 1, 1), + stride=1, + time_stride=1, + padding=0, + ) + self.conv2 = CausalConv3d( + in_channels, + in_channels, + kernel_size=(1, 3, 3), + stride=1, + time_stride=1, + padding=1, + ) + self.conv3 = CausalConv3d( + in_channels, + in_channels, + kernel_size=1, + stride=1, + time_stride=1, + padding=0, + ) + + def forward(self, x: torch.Tensor) -> torch.Tensor: + if not self.spatial_up and not self.temporal_up: + return x + + # hybrid upsample temporally. + if self.temporal_up: + time_factor = 1.0 + 1.0 * (x.shape[2] > 1) + if isinstance(time_factor, torch.Tensor): + time_factor = time_factor.item() + x = x.repeat_interleave(int(time_factor), dim=2) + x = x[..., int(time_factor - 1) :, :, :] + x = self.conv1(x) + x + + # hybrid upsample spatially. + if self.spatial_up: + x = x.repeat_interleave(2, dim=3).repeat_interleave(2, dim=4) + x = self.conv2(x) + x + + # final 1x1x1 conv. + x = self.conv3(x) + return x + + +class CausalHybridDownsample3d(nn.Module): + def __init__( + self, + in_channels: int, + spatial_down: bool = True, + temporal_down: bool = True, + **kwargs, + ) -> None: + super().__init__() + self.spatial_down = spatial_down + self.temporal_down = temporal_down + if not self.spatial_down and not self.temporal_down: + return + + self.conv1 = CausalConv3d( + in_channels, + in_channels, + kernel_size=(1, 3, 3), + stride=2, + time_stride=1, + padding=0, + ) + self.conv2 = CausalConv3d( + in_channels, + in_channels, + kernel_size=(3, 1, 1), + stride=1, + time_stride=2, + padding=0, + ) + self.conv3 = CausalConv3d( + in_channels, + in_channels, + kernel_size=1, + stride=1, + time_stride=1, + padding=0, + ) + + + def forward(self, x: torch.Tensor) -> torch.Tensor: + if not self.spatial_down and not self.temporal_down: + return x + + # hybrid downsample spatially. + if self.spatial_down: + pad = (0, 1, 0, 1, 0, 0) + x = F.pad(x, pad, mode="constant", value=0) + x1 = self.conv1(x) + x2 = F.avg_pool3d(x, kernel_size=(1, 2, 2), stride=(1, 2, 2)) + x = x1 + x2 + + # hybrid downsample temporally. + if self.temporal_down: + x = replication_pad(x) + x1 = self.conv2(x) + x2 = F.avg_pool3d(x, kernel_size=(2, 1, 1), stride=(2, 1, 1)) + x = x1 + x2 + + # final 1x1x1 conv. + x = self.conv3(x) + return x + + +class CausalResnetBlock3d(nn.Module): + def __init__( + self, + *, + in_channels: int, + out_channels: int = None, + dropout: float, + num_groups: int, + ) -> None: + super().__init__() + self.in_channels = in_channels + out_channels = in_channels if out_channels is None else out_channels + + self.norm1 = CausalNormalize(in_channels, num_groups=num_groups) + self.conv1 = CausalConv3d( + in_channels, out_channels, kernel_size=3, stride=1, padding=1 + ) + self.norm2 = CausalNormalize(out_channels, num_groups=num_groups) + self.dropout = torch.nn.Dropout(dropout) + self.conv2 = CausalConv3d( + out_channels, out_channels, kernel_size=3, stride=1, padding=1 + ) + self.nin_shortcut = ( + CausalConv3d(in_channels, out_channels, kernel_size=1, stride=1, padding=0) + if in_channels != out_channels + else nn.Identity() + ) + + def forward(self, x: torch.Tensor) -> torch.Tensor: + h = x + h = self.norm1(h) + h = nonlinearity(h) + h = self.conv1(h) + + h = self.norm2(h) + h = nonlinearity(h) + h = self.dropout(h) + h = self.conv2(h) + x = self.nin_shortcut(x) + + return x + h + + +class CausalResnetBlockFactorized3d(nn.Module): + def __init__( + self, + *, + in_channels: int, + out_channels: int = None, + dropout: float, + num_groups: int, + ) -> None: + super().__init__() + self.in_channels = in_channels + out_channels = in_channels if out_channels is None else out_channels + + self.norm1 = CausalNormalize(in_channels, num_groups=1) + self.conv1 = nn.Sequential( + CausalConv3d( + in_channels, + out_channels, + kernel_size=(1, 3, 3), + stride=1, + padding=1, + ), + CausalConv3d( + out_channels, + out_channels, + kernel_size=(3, 1, 1), + stride=1, + padding=0, + ), + ) + self.norm2 = CausalNormalize(out_channels, num_groups=num_groups) + self.dropout = torch.nn.Dropout(dropout) + self.conv2 = nn.Sequential( + CausalConv3d( + out_channels, + out_channels, + kernel_size=(1, 3, 3), + stride=1, + padding=1, + ), + CausalConv3d( + out_channels, + out_channels, + kernel_size=(3, 1, 1), + stride=1, + padding=0, + ), + ) + self.nin_shortcut = ( + CausalConv3d(in_channels, out_channels, kernel_size=1, stride=1, padding=0) + if in_channels != out_channels + else nn.Identity() + ) + + def forward(self, x: torch.Tensor) -> torch.Tensor: + h = x + h = self.norm1(h) + h = nonlinearity(h) + h = self.conv1(h) + + h = self.norm2(h) + h = nonlinearity(h) + h = self.dropout(h) + h = self.conv2(h) + x = self.nin_shortcut(x) + + return x + h + + +class CausalAttnBlock(nn.Module): + def __init__(self, in_channels: int, num_groups: int) -> None: + super().__init__() + + self.norm = CausalNormalize(in_channels, num_groups=num_groups) + self.q = CausalConv3d( + in_channels, in_channels, kernel_size=1, stride=1, padding=0 + ) + self.k = CausalConv3d( + in_channels, in_channels, kernel_size=1, stride=1, padding=0 + ) + self.v = CausalConv3d( + in_channels, in_channels, kernel_size=1, stride=1, padding=0 + ) + self.proj_out = CausalConv3d( + in_channels, in_channels, kernel_size=1, stride=1, padding=0 + ) + + self.optimized_attention = vae_attention() + + def forward(self, x: torch.Tensor) -> torch.Tensor: + h_ = x + h_ = self.norm(h_) + q = self.q(h_) + k = self.k(h_) + v = self.v(h_) + + # compute attention + q, batch_size = time2batch(q) + k, batch_size = time2batch(k) + v, batch_size = time2batch(v) + + b, c, h, w = q.shape + h_ = self.optimized_attention(q, k, v) + + h_ = batch2time(h_, batch_size) + h_ = self.proj_out(h_) + return x + h_ + + +class CausalTemporalAttnBlock(nn.Module): + def __init__(self, in_channels: int, num_groups: int) -> None: + super().__init__() + + self.norm = CausalNormalize(in_channels, num_groups=num_groups) + self.q = CausalConv3d( + in_channels, in_channels, kernel_size=1, stride=1, padding=0 + ) + self.k = CausalConv3d( + in_channels, in_channels, kernel_size=1, stride=1, padding=0 + ) + self.v = CausalConv3d( + in_channels, in_channels, kernel_size=1, stride=1, padding=0 + ) + self.proj_out = CausalConv3d( + in_channels, in_channels, kernel_size=1, stride=1, padding=0 + ) + + def forward(self, x: torch.Tensor) -> torch.Tensor: + h_ = x + h_ = self.norm(h_) + q = self.q(h_) + k = self.k(h_) + v = self.v(h_) + + # compute attention + q, batch_size, height = space2batch(q) + k, _, _ = space2batch(k) + v, _, _ = space2batch(v) + + bhw, c, t = q.shape + q = q.permute(0, 2, 1) # (bhw, t, c) + k = k.permute(0, 2, 1) # (bhw, t, c) + v = v.permute(0, 2, 1) # (bhw, t, c) + + w_ = torch.bmm(q, k.permute(0, 2, 1)) # (bhw, t, t) + w_ = w_ * (int(c) ** (-0.5)) + + # Apply causal mask + mask = torch.tril(torch.ones_like(w_)) + w_ = w_.masked_fill(mask == 0, float("-inf")) + w_ = F.softmax(w_, dim=2) + + # attend to values + h_ = torch.bmm(w_, v) # (bhw, t, c) + h_ = h_.permute(0, 2, 1).reshape(bhw, c, t) # (bhw, c, t) + + h_ = batch2space(h_, batch_size, height) + h_ = self.proj_out(h_) + return x + h_ + + +class EncoderBase(nn.Module): + def __init__( + self, + in_channels: int, + channels: int, + channels_mult: list[int], + num_res_blocks: int, + attn_resolutions: list[int], + dropout: float, + resolution: int, + z_channels: int, + **ignore_kwargs, + ) -> None: + super().__init__() + self.num_resolutions = len(channels_mult) + self.num_res_blocks = num_res_blocks + + # Patcher. + patch_size = ignore_kwargs.get("patch_size", 1) + self.patcher = Patcher( + patch_size, ignore_kwargs.get("patch_method", "rearrange") + ) + in_channels = in_channels * patch_size * patch_size + + # downsampling + self.conv_in = CausalConv3d( + in_channels, channels, kernel_size=3, stride=1, padding=1 + ) + + # num of groups for GroupNorm, num_groups=1 for LayerNorm. + num_groups = ignore_kwargs.get("num_groups", _LEGACY_NUM_GROUPS) + curr_res = resolution // patch_size + in_ch_mult = (1,) + tuple(channels_mult) + self.in_ch_mult = in_ch_mult + self.down = nn.ModuleList() + for i_level in range(self.num_resolutions): + block = nn.ModuleList() + attn = nn.ModuleList() + block_in = channels * in_ch_mult[i_level] + block_out = channels * channels_mult[i_level] + for _ in range(self.num_res_blocks): + block.append( + CausalResnetBlock3d( + in_channels=block_in, + out_channels=block_out, + dropout=dropout, + num_groups=num_groups, + ) + ) + block_in = block_out + if curr_res in attn_resolutions: + attn.append(CausalAttnBlock(block_in, num_groups=num_groups)) + down = nn.Module() + down.block = block + down.attn = attn + if i_level != self.num_resolutions - 1: + down.downsample = CausalDownsample3d(block_in) + curr_res = curr_res // 2 + self.down.append(down) + + # middle + self.mid = nn.Module() + self.mid.block_1 = CausalResnetBlock3d( + in_channels=block_in, + out_channels=block_in, + dropout=dropout, + num_groups=num_groups, + ) + self.mid.attn_1 = CausalAttnBlock(block_in, num_groups=num_groups) + self.mid.block_2 = CausalResnetBlock3d( + in_channels=block_in, + out_channels=block_in, + dropout=dropout, + num_groups=num_groups, + ) + + # end + self.norm_out = CausalNormalize(block_in, num_groups=num_groups) + self.conv_out = CausalConv3d( + block_in, z_channels, kernel_size=3, stride=1, padding=1 + ) + + def patcher3d(self, x: torch.Tensor) -> torch.Tensor: + x, batch_size = time2batch(x) + x = self.patcher(x) + x = batch2time(x, batch_size) + return x + + def forward(self, x: torch.Tensor) -> torch.Tensor: + x = self.patcher3d(x) + + # downsampling + hs = [self.conv_in(x)] + for i_level in range(self.num_resolutions): + for i_block in range(self.num_res_blocks): + h = self.down[i_level].block[i_block](hs[-1]) + if len(self.down[i_level].attn) > 0: + h = self.down[i_level].attn[i_block](h) + hs.append(h) + if i_level != self.num_resolutions - 1: + hs.append(self.down[i_level].downsample(hs[-1])) + else: + # temporal downsample (last level) + time_factor = 1 + 1 * (hs[-1].shape[2] > 1) + if isinstance(time_factor, torch.Tensor): + time_factor = time_factor.item() + hs[-1] = replication_pad(hs[-1]) + hs.append( + F.avg_pool3d( + hs[-1], + kernel_size=[time_factor, 1, 1], + stride=[2, 1, 1], + ) + ) + + # middle + h = hs[-1] + h = self.mid.block_1(h) + h = self.mid.attn_1(h) + h = self.mid.block_2(h) + + # end + h = self.norm_out(h) + h = nonlinearity(h) + h = self.conv_out(h) + return h + + +class DecoderBase(nn.Module): + def __init__( + self, + out_channels: int, + channels: int, + channels_mult: list[int], + num_res_blocks: int, + attn_resolutions: list[int], + dropout: float, + resolution: int, + z_channels: int, + **ignore_kwargs, + ): + super().__init__() + self.num_resolutions = len(channels_mult) + self.num_res_blocks = num_res_blocks + + # UnPatcher. + patch_size = ignore_kwargs.get("patch_size", 1) + self.unpatcher = UnPatcher( + patch_size, ignore_kwargs.get("patch_method", "rearrange") + ) + out_ch = out_channels * patch_size * patch_size + + block_in = channels * channels_mult[self.num_resolutions - 1] + curr_res = (resolution // patch_size) // 2 ** (self.num_resolutions - 1) + self.z_shape = (1, z_channels, curr_res, curr_res) + logging.debug( + "Working with z of shape {} = {} dimensions.".format( + self.z_shape, np.prod(self.z_shape) + ) + ) + + # z to block_in + self.conv_in = CausalConv3d( + z_channels, block_in, kernel_size=3, stride=1, padding=1 + ) + + # num of groups for GroupNorm, num_groups=1 for LayerNorm. + num_groups = ignore_kwargs.get("num_groups", _LEGACY_NUM_GROUPS) + + # middle + self.mid = nn.Module() + self.mid.block_1 = CausalResnetBlock3d( + in_channels=block_in, + out_channels=block_in, + dropout=dropout, + num_groups=num_groups, + ) + self.mid.attn_1 = CausalAttnBlock(block_in, num_groups=num_groups) + self.mid.block_2 = CausalResnetBlock3d( + in_channels=block_in, + out_channels=block_in, + dropout=dropout, + num_groups=num_groups, + ) + + # upsampling + self.up = nn.ModuleList() + for i_level in reversed(range(self.num_resolutions)): + block = nn.ModuleList() + attn = nn.ModuleList() + block_out = channels * channels_mult[i_level] + for _ in range(self.num_res_blocks + 1): + block.append( + CausalResnetBlock3d( + in_channels=block_in, + out_channels=block_out, + dropout=dropout, + num_groups=num_groups, + ) + ) + block_in = block_out + if curr_res in attn_resolutions: + attn.append(CausalAttnBlock(block_in, num_groups=num_groups)) + up = nn.Module() + up.block = block + up.attn = attn + if i_level != 0: + up.upsample = CausalUpsample3d(block_in) + curr_res = curr_res * 2 + self.up.insert(0, up) # prepend to get consistent order + + # end + self.norm_out = CausalNormalize(block_in, num_groups=num_groups) + self.conv_out = CausalConv3d( + block_in, out_ch, kernel_size=3, stride=1, padding=1 + ) + + def unpatcher3d(self, x: torch.Tensor) -> torch.Tensor: + x, batch_size = time2batch(x) + x = self.unpatcher(x) + x = batch2time(x, batch_size) + + return x + + def forward(self, z): + h = self.conv_in(z) + + # middle block. + h = self.mid.block_1(h) + h = self.mid.attn_1(h) + h = self.mid.block_2(h) + + # decoder blocks. + for i_level in reversed(range(self.num_resolutions)): + for i_block in range(self.num_res_blocks + 1): + h = self.up[i_level].block[i_block](h) + if len(self.up[i_level].attn) > 0: + h = self.up[i_level].attn[i_block](h) + if i_level != 0: + h = self.up[i_level].upsample(h) + else: + # temporal upsample (last level) + time_factor = 1.0 + 1.0 * (h.shape[2] > 1) + if isinstance(time_factor, torch.Tensor): + time_factor = time_factor.item() + h = h.repeat_interleave(int(time_factor), dim=2) + h = h[..., int(time_factor - 1) :, :, :] + + h = self.norm_out(h) + h = nonlinearity(h) + h = self.conv_out(h) + h = self.unpatcher3d(h) + return h + + +class EncoderFactorized(nn.Module): + def __init__( + self, + in_channels: int, + channels: int, + channels_mult: list[int], + num_res_blocks: int, + attn_resolutions: list[int], + dropout: float, + resolution: int, + z_channels: int, + spatial_compression: int = 8, + temporal_compression: int = 8, + **ignore_kwargs, + ) -> None: + super().__init__() + self.num_resolutions = len(channels_mult) + self.num_res_blocks = num_res_blocks + + # Patcher. + patch_size = ignore_kwargs.get("patch_size", 1) + self.patcher3d = Patcher3D( + patch_size, ignore_kwargs.get("patch_method", "haar") + ) + in_channels = in_channels * patch_size * patch_size * patch_size + + # calculate the number of downsample operations + self.num_spatial_downs = int(math.log2(spatial_compression)) - int( + math.log2(patch_size) + ) + assert ( + self.num_spatial_downs <= self.num_resolutions + ), f"Spatially downsample {self.num_resolutions} times at most" + + self.num_temporal_downs = int(math.log2(temporal_compression)) - int( + math.log2(patch_size) + ) + assert ( + self.num_temporal_downs <= self.num_resolutions + ), f"Temporally downsample {self.num_resolutions} times at most" + + # downsampling + self.conv_in = nn.Sequential( + CausalConv3d( + in_channels, + channels, + kernel_size=(1, 3, 3), + stride=1, + padding=1, + ), + CausalConv3d( + channels, channels, kernel_size=(3, 1, 1), stride=1, padding=0 + ), + ) + + curr_res = resolution // patch_size + in_ch_mult = (1,) + tuple(channels_mult) + self.in_ch_mult = in_ch_mult + self.down = nn.ModuleList() + for i_level in range(self.num_resolutions): + block = nn.ModuleList() + attn = nn.ModuleList() + block_in = channels * in_ch_mult[i_level] + block_out = channels * channels_mult[i_level] + for _ in range(self.num_res_blocks): + block.append( + CausalResnetBlockFactorized3d( + in_channels=block_in, + out_channels=block_out, + dropout=dropout, + num_groups=1, + ) + ) + block_in = block_out + if curr_res in attn_resolutions: + attn.append( + nn.Sequential( + CausalAttnBlock(block_in, num_groups=1), + CausalTemporalAttnBlock(block_in, num_groups=1), + ) + ) + down = nn.Module() + down.block = block + down.attn = attn + if i_level != self.num_resolutions - 1: + spatial_down = i_level < self.num_spatial_downs + temporal_down = i_level < self.num_temporal_downs + down.downsample = CausalHybridDownsample3d( + block_in, + spatial_down=spatial_down, + temporal_down=temporal_down, + ) + curr_res = curr_res // 2 + self.down.append(down) + + # middle + self.mid = nn.Module() + self.mid.block_1 = CausalResnetBlockFactorized3d( + in_channels=block_in, + out_channels=block_in, + dropout=dropout, + num_groups=1, + ) + self.mid.attn_1 = nn.Sequential( + CausalAttnBlock(block_in, num_groups=1), + CausalTemporalAttnBlock(block_in, num_groups=1), + ) + self.mid.block_2 = CausalResnetBlockFactorized3d( + in_channels=block_in, + out_channels=block_in, + dropout=dropout, + num_groups=1, + ) + + # end + self.norm_out = CausalNormalize(block_in, num_groups=1) + self.conv_out = nn.Sequential( + CausalConv3d( + block_in, z_channels, kernel_size=(1, 3, 3), stride=1, padding=1 + ), + CausalConv3d( + z_channels, + z_channels, + kernel_size=(3, 1, 1), + stride=1, + padding=0, + ), + ) + + def forward(self, x: torch.Tensor) -> torch.Tensor: + x = self.patcher3d(x) + + # downsampling + h = self.conv_in(x) + for i_level in range(self.num_resolutions): + for i_block in range(self.num_res_blocks): + h = self.down[i_level].block[i_block](h) + if len(self.down[i_level].attn) > 0: + h = self.down[i_level].attn[i_block](h) + if i_level != self.num_resolutions - 1: + h = self.down[i_level].downsample(h) + + # middle + h = self.mid.block_1(h) + h = self.mid.attn_1(h) + h = self.mid.block_2(h) + + # end + h = self.norm_out(h) + h = nonlinearity(h) + h = self.conv_out(h) + return h + + +class DecoderFactorized(nn.Module): + def __init__( + self, + out_channels: int, + channels: int, + channels_mult: list[int], + num_res_blocks: int, + attn_resolutions: list[int], + dropout: float, + resolution: int, + z_channels: int, + spatial_compression: int = 8, + temporal_compression: int = 8, + **ignore_kwargs, + ): + super().__init__() + self.num_resolutions = len(channels_mult) + self.num_res_blocks = num_res_blocks + + # UnPatcher. + patch_size = ignore_kwargs.get("patch_size", 1) + self.unpatcher3d = UnPatcher3D( + patch_size, ignore_kwargs.get("patch_method", "haar") + ) + out_ch = out_channels * patch_size * patch_size * patch_size + + # calculate the number of upsample operations + self.num_spatial_ups = int(math.log2(spatial_compression)) - int( + math.log2(patch_size) + ) + assert ( + self.num_spatial_ups <= self.num_resolutions + ), f"Spatially upsample {self.num_resolutions} times at most" + self.num_temporal_ups = int(math.log2(temporal_compression)) - int( + math.log2(patch_size) + ) + assert ( + self.num_temporal_ups <= self.num_resolutions + ), f"Temporally upsample {self.num_resolutions} times at most" + + block_in = channels * channels_mult[self.num_resolutions - 1] + curr_res = (resolution // patch_size) // 2 ** (self.num_resolutions - 1) + self.z_shape = (1, z_channels, curr_res, curr_res) + logging.debug( + "Working with z of shape {} = {} dimensions.".format( + self.z_shape, np.prod(self.z_shape) + ) + ) + + # z to block_in + self.conv_in = nn.Sequential( + CausalConv3d( + z_channels, block_in, kernel_size=(1, 3, 3), stride=1, padding=1 + ), + CausalConv3d( + block_in, block_in, kernel_size=(3, 1, 1), stride=1, padding=0 + ), + ) + + # middle + self.mid = nn.Module() + self.mid.block_1 = CausalResnetBlockFactorized3d( + in_channels=block_in, + out_channels=block_in, + dropout=dropout, + num_groups=1, + ) + self.mid.attn_1 = nn.Sequential( + CausalAttnBlock(block_in, num_groups=1), + CausalTemporalAttnBlock(block_in, num_groups=1), + ) + self.mid.block_2 = CausalResnetBlockFactorized3d( + in_channels=block_in, + out_channels=block_in, + dropout=dropout, + num_groups=1, + ) + + legacy_mode = ignore_kwargs.get("legacy_mode", False) + # upsampling + self.up = nn.ModuleList() + for i_level in reversed(range(self.num_resolutions)): + block = nn.ModuleList() + attn = nn.ModuleList() + block_out = channels * channels_mult[i_level] + for _ in range(self.num_res_blocks + 1): + block.append( + CausalResnetBlockFactorized3d( + in_channels=block_in, + out_channels=block_out, + dropout=dropout, + num_groups=1, + ) + ) + block_in = block_out + if curr_res in attn_resolutions: + attn.append( + nn.Sequential( + CausalAttnBlock(block_in, num_groups=1), + CausalTemporalAttnBlock(block_in, num_groups=1), + ) + ) + up = nn.Module() + up.block = block + up.attn = attn + if i_level != 0: + # The layer index for temporal/spatial downsampling performed + # in the encoder should correspond to the layer index in + # reverse order where upsampling is performed in the decoder. + # If you've a pre-trained model, you can simply finetune. + i_level_reverse = self.num_resolutions - i_level - 1 + if legacy_mode: + temporal_up = i_level_reverse < self.num_temporal_ups + else: + temporal_up = 0 < i_level_reverse < self.num_temporal_ups + 1 + spatial_up = temporal_up or ( + i_level_reverse < self.num_spatial_ups + and self.num_spatial_ups > self.num_temporal_ups + ) + up.upsample = CausalHybridUpsample3d( + block_in, spatial_up=spatial_up, temporal_up=temporal_up + ) + curr_res = curr_res * 2 + self.up.insert(0, up) # prepend to get consistent order + + # end + self.norm_out = CausalNormalize(block_in, num_groups=1) + self.conv_out = nn.Sequential( + CausalConv3d(block_in, out_ch, kernel_size=(1, 3, 3), stride=1, padding=1), + CausalConv3d(out_ch, out_ch, kernel_size=(3, 1, 1), stride=1, padding=0), + ) + + def forward(self, z): + h = self.conv_in(z) + + # middle block. + h = self.mid.block_1(h) + h = self.mid.attn_1(h) + h = self.mid.block_2(h) + + # decoder blocks. + for i_level in reversed(range(self.num_resolutions)): + for i_block in range(self.num_res_blocks + 1): + h = self.up[i_level].block[i_block](h) + if len(self.up[i_level].attn) > 0: + h = self.up[i_level].attn[i_block](h) + if i_level != 0: + h = self.up[i_level].upsample(h) + + h = self.norm_out(h) + h = nonlinearity(h) + h = self.conv_out(h) + h = self.unpatcher3d(h) + return h diff --git a/ComfyUI/comfy/ldm/cosmos/cosmos_tokenizer/patching.py b/ComfyUI/comfy/ldm/cosmos/cosmos_tokenizer/patching.py new file mode 100644 index 0000000000000000000000000000000000000000..87a53a1d9f9ac4fa6bdbb1f8f682c298fdd4376c --- /dev/null +++ b/ComfyUI/comfy/ldm/cosmos/cosmos_tokenizer/patching.py @@ -0,0 +1,377 @@ +# SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""The patcher and unpatcher implementation for 2D and 3D data. + +The idea of Haar wavelet is to compute LL, LH, HL, HH component as two 1D convolutions. +One on the rows and one on the columns. +For example, in 1D signal, we have [a, b], then the low-freq compoenent is [a + b] / 2 and high-freq is [a - b] / 2. +We can use a 1D convolution with kernel [1, 1] and stride 2 to represent the L component. +For H component, we can use a 1D convolution with kernel [1, -1] and stride 2. +Although in principle, we typically only do additional Haar wavelet over the LL component. But here we do it for all + as we need to support downsampling for more than 2x. +For example, 4x downsampling can be done by 2x Haar and additional 2x Haar, and the shape would be. + [3, 256, 256] -> [12, 128, 128] -> [48, 64, 64] +""" + +import torch +import torch.nn.functional as F +from einops import rearrange + +_WAVELETS = { + "haar": torch.tensor([0.7071067811865476, 0.7071067811865476]), + "rearrange": torch.tensor([1.0, 1.0]), +} +_PERSISTENT = False + + +class Patcher(torch.nn.Module): + """A module to convert image tensors into patches using torch operations. + + The main difference from `class Patching` is that this module implements + all operations using torch, rather than python or numpy, for efficiency purpose. + + It's bit-wise identical to the Patching module outputs, with the added + benefit of being torch.jit scriptable. + """ + + def __init__(self, patch_size=1, patch_method="haar"): + super().__init__() + self.patch_size = patch_size + self.patch_method = patch_method + self.register_buffer( + "wavelets", _WAVELETS[patch_method], persistent=_PERSISTENT + ) + self.range = range(int(torch.log2(torch.tensor(self.patch_size)).item())) + self.register_buffer( + "_arange", + torch.arange(_WAVELETS[patch_method].shape[0]), + persistent=_PERSISTENT, + ) + for param in self.parameters(): + param.requires_grad = False + + def forward(self, x): + if self.patch_method == "haar": + return self._haar(x) + elif self.patch_method == "rearrange": + return self._arrange(x) + else: + raise ValueError("Unknown patch method: " + self.patch_method) + + def _dwt(self, x, mode="reflect", rescale=False): + dtype = x.dtype + h = self.wavelets.to(device=x.device) + + n = h.shape[0] + g = x.shape[1] + hl = h.flip(0).reshape(1, 1, -1).repeat(g, 1, 1) + hh = (h * ((-1) ** self._arange.to(device=x.device))).reshape(1, 1, -1).repeat(g, 1, 1) + hh = hh.to(dtype=dtype) + hl = hl.to(dtype=dtype) + + x = F.pad(x, pad=(n - 2, n - 1, n - 2, n - 1), mode=mode).to(dtype) + xl = F.conv2d(x, hl.unsqueeze(2), groups=g, stride=(1, 2)) + xh = F.conv2d(x, hh.unsqueeze(2), groups=g, stride=(1, 2)) + xll = F.conv2d(xl, hl.unsqueeze(3), groups=g, stride=(2, 1)) + xlh = F.conv2d(xl, hh.unsqueeze(3), groups=g, stride=(2, 1)) + xhl = F.conv2d(xh, hl.unsqueeze(3), groups=g, stride=(2, 1)) + xhh = F.conv2d(xh, hh.unsqueeze(3), groups=g, stride=(2, 1)) + + out = torch.cat([xll, xlh, xhl, xhh], dim=1) + if rescale: + out = out / 2 + return out + + def _haar(self, x): + for _ in self.range: + x = self._dwt(x, rescale=True) + return x + + def _arrange(self, x): + x = rearrange( + x, + "b c (h p1) (w p2) -> b (c p1 p2) h w", + p1=self.patch_size, + p2=self.patch_size, + ).contiguous() + return x + + +class Patcher3D(Patcher): + """A 3D discrete wavelet transform for video data, expects 5D tensor, i.e. a batch of videos.""" + + def __init__(self, patch_size=1, patch_method="haar"): + super().__init__(patch_method=patch_method, patch_size=patch_size) + self.register_buffer( + "patch_size_buffer", + patch_size * torch.ones([1], dtype=torch.int32), + persistent=_PERSISTENT, + ) + + def _dwt(self, x, wavelet, mode="reflect", rescale=False): + dtype = x.dtype + h = self.wavelets.to(device=x.device) + + n = h.shape[0] + g = x.shape[1] + hl = h.flip(0).reshape(1, 1, -1).repeat(g, 1, 1) + hh = (h * ((-1) ** self._arange.to(device=x.device))).reshape(1, 1, -1).repeat(g, 1, 1) + hh = hh.to(dtype=dtype) + hl = hl.to(dtype=dtype) + + # Handles temporal axis. + x = F.pad( + x, pad=(max(0, n - 2), n - 1, n - 2, n - 1, n - 2, n - 1), mode=mode + ).to(dtype) + xl = F.conv3d(x, hl.unsqueeze(3).unsqueeze(4), groups=g, stride=(2, 1, 1)) + xh = F.conv3d(x, hh.unsqueeze(3).unsqueeze(4), groups=g, stride=(2, 1, 1)) + + # Handles spatial axes. + xll = F.conv3d(xl, hl.unsqueeze(2).unsqueeze(4), groups=g, stride=(1, 2, 1)) + xlh = F.conv3d(xl, hh.unsqueeze(2).unsqueeze(4), groups=g, stride=(1, 2, 1)) + xhl = F.conv3d(xh, hl.unsqueeze(2).unsqueeze(4), groups=g, stride=(1, 2, 1)) + xhh = F.conv3d(xh, hh.unsqueeze(2).unsqueeze(4), groups=g, stride=(1, 2, 1)) + + xlll = F.conv3d(xll, hl.unsqueeze(2).unsqueeze(3), groups=g, stride=(1, 1, 2)) + xllh = F.conv3d(xll, hh.unsqueeze(2).unsqueeze(3), groups=g, stride=(1, 1, 2)) + xlhl = F.conv3d(xlh, hl.unsqueeze(2).unsqueeze(3), groups=g, stride=(1, 1, 2)) + xlhh = F.conv3d(xlh, hh.unsqueeze(2).unsqueeze(3), groups=g, stride=(1, 1, 2)) + xhll = F.conv3d(xhl, hl.unsqueeze(2).unsqueeze(3), groups=g, stride=(1, 1, 2)) + xhlh = F.conv3d(xhl, hh.unsqueeze(2).unsqueeze(3), groups=g, stride=(1, 1, 2)) + xhhl = F.conv3d(xhh, hl.unsqueeze(2).unsqueeze(3), groups=g, stride=(1, 1, 2)) + xhhh = F.conv3d(xhh, hh.unsqueeze(2).unsqueeze(3), groups=g, stride=(1, 1, 2)) + + out = torch.cat([xlll, xllh, xlhl, xlhh, xhll, xhlh, xhhl, xhhh], dim=1) + if rescale: + out = out / (2 * torch.sqrt(torch.tensor(2.0))) + return out + + def _haar(self, x): + xi, xv = torch.split(x, [1, x.shape[2] - 1], dim=2) + x = torch.cat([xi.repeat_interleave(self.patch_size, dim=2), xv], dim=2) + for _ in self.range: + x = self._dwt(x, "haar", rescale=True) + return x + + def _arrange(self, x): + xi, xv = torch.split(x, [1, x.shape[2] - 1], dim=2) + x = torch.cat([xi.repeat_interleave(self.patch_size, dim=2), xv], dim=2) + x = rearrange( + x, + "b c (t p1) (h p2) (w p3) -> b (c p1 p2 p3) t h w", + p1=self.patch_size, + p2=self.patch_size, + p3=self.patch_size, + ).contiguous() + return x + + +class UnPatcher(torch.nn.Module): + """A module to convert patches into image tensorsusing torch operations. + + The main difference from `class Unpatching` is that this module implements + all operations using torch, rather than python or numpy, for efficiency purpose. + + It's bit-wise identical to the Unpatching module outputs, with the added + benefit of being torch.jit scriptable. + """ + + def __init__(self, patch_size=1, patch_method="haar"): + super().__init__() + self.patch_size = patch_size + self.patch_method = patch_method + self.register_buffer( + "wavelets", _WAVELETS[patch_method], persistent=_PERSISTENT + ) + self.range = range(int(torch.log2(torch.tensor(self.patch_size)).item())) + self.register_buffer( + "_arange", + torch.arange(_WAVELETS[patch_method].shape[0]), + persistent=_PERSISTENT, + ) + for param in self.parameters(): + param.requires_grad = False + + def forward(self, x): + if self.patch_method == "haar": + return self._ihaar(x) + elif self.patch_method == "rearrange": + return self._iarrange(x) + else: + raise ValueError("Unknown patch method: " + self.patch_method) + + def _idwt(self, x, wavelet="haar", mode="reflect", rescale=False): + dtype = x.dtype + h = self.wavelets.to(device=x.device) + n = h.shape[0] + + g = x.shape[1] // 4 + hl = h.flip([0]).reshape(1, 1, -1).repeat([g, 1, 1]) + hh = (h * ((-1) ** self._arange.to(device=x.device))).reshape(1, 1, -1).repeat(g, 1, 1) + hh = hh.to(dtype=dtype) + hl = hl.to(dtype=dtype) + + xll, xlh, xhl, xhh = torch.chunk(x.to(dtype), 4, dim=1) + + # Inverse transform. + yl = torch.nn.functional.conv_transpose2d( + xll, hl.unsqueeze(3), groups=g, stride=(2, 1), padding=(n - 2, 0) + ) + yl += torch.nn.functional.conv_transpose2d( + xlh, hh.unsqueeze(3), groups=g, stride=(2, 1), padding=(n - 2, 0) + ) + yh = torch.nn.functional.conv_transpose2d( + xhl, hl.unsqueeze(3), groups=g, stride=(2, 1), padding=(n - 2, 0) + ) + yh += torch.nn.functional.conv_transpose2d( + xhh, hh.unsqueeze(3), groups=g, stride=(2, 1), padding=(n - 2, 0) + ) + y = torch.nn.functional.conv_transpose2d( + yl, hl.unsqueeze(2), groups=g, stride=(1, 2), padding=(0, n - 2) + ) + y += torch.nn.functional.conv_transpose2d( + yh, hh.unsqueeze(2), groups=g, stride=(1, 2), padding=(0, n - 2) + ) + + if rescale: + y = y * 2 + return y + + def _ihaar(self, x): + for _ in self.range: + x = self._idwt(x, "haar", rescale=True) + return x + + def _iarrange(self, x): + x = rearrange( + x, + "b (c p1 p2) h w -> b c (h p1) (w p2)", + p1=self.patch_size, + p2=self.patch_size, + ) + return x + + +class UnPatcher3D(UnPatcher): + """A 3D inverse discrete wavelet transform for video wavelet decompositions.""" + + def __init__(self, patch_size=1, patch_method="haar"): + super().__init__(patch_method=patch_method, patch_size=patch_size) + + def _idwt(self, x, wavelet="haar", mode="reflect", rescale=False): + dtype = x.dtype + h = self.wavelets.to(device=x.device) + + g = x.shape[1] // 8 # split into 8 spatio-temporal filtered tesnors. + hl = h.flip([0]).reshape(1, 1, -1).repeat([g, 1, 1]) + hh = (h * ((-1) ** self._arange.to(device=x.device))).reshape(1, 1, -1).repeat(g, 1, 1) + hl = hl.to(dtype=dtype) + hh = hh.to(dtype=dtype) + + xlll, xllh, xlhl, xlhh, xhll, xhlh, xhhl, xhhh = torch.chunk(x, 8, dim=1) + del x + + # Height height transposed convolutions. + xll = F.conv_transpose3d( + xlll, hl.unsqueeze(2).unsqueeze(3), groups=g, stride=(1, 1, 2) + ) + del xlll + + xll += F.conv_transpose3d( + xllh, hh.unsqueeze(2).unsqueeze(3), groups=g, stride=(1, 1, 2) + ) + del xllh + + xlh = F.conv_transpose3d( + xlhl, hl.unsqueeze(2).unsqueeze(3), groups=g, stride=(1, 1, 2) + ) + del xlhl + + xlh += F.conv_transpose3d( + xlhh, hh.unsqueeze(2).unsqueeze(3), groups=g, stride=(1, 1, 2) + ) + del xlhh + + xhl = F.conv_transpose3d( + xhll, hl.unsqueeze(2).unsqueeze(3), groups=g, stride=(1, 1, 2) + ) + del xhll + + xhl += F.conv_transpose3d( + xhlh, hh.unsqueeze(2).unsqueeze(3), groups=g, stride=(1, 1, 2) + ) + del xhlh + + xhh = F.conv_transpose3d( + xhhl, hl.unsqueeze(2).unsqueeze(3), groups=g, stride=(1, 1, 2) + ) + del xhhl + + xhh += F.conv_transpose3d( + xhhh, hh.unsqueeze(2).unsqueeze(3), groups=g, stride=(1, 1, 2) + ) + del xhhh + + # Handles width transposed convolutions. + xl = F.conv_transpose3d( + xll, hl.unsqueeze(2).unsqueeze(4), groups=g, stride=(1, 2, 1) + ) + del xll + + xl += F.conv_transpose3d( + xlh, hh.unsqueeze(2).unsqueeze(4), groups=g, stride=(1, 2, 1) + ) + del xlh + + xh = F.conv_transpose3d( + xhl, hl.unsqueeze(2).unsqueeze(4), groups=g, stride=(1, 2, 1) + ) + del xhl + + xh += F.conv_transpose3d( + xhh, hh.unsqueeze(2).unsqueeze(4), groups=g, stride=(1, 2, 1) + ) + del xhh + + # Handles time axis transposed convolutions. + x = F.conv_transpose3d( + xl, hl.unsqueeze(3).unsqueeze(4), groups=g, stride=(2, 1, 1) + ) + del xl + + x += F.conv_transpose3d( + xh, hh.unsqueeze(3).unsqueeze(4), groups=g, stride=(2, 1, 1) + ) + + if rescale: + x = x * (2 * torch.sqrt(torch.tensor(2.0))) + return x + + def _ihaar(self, x): + for _ in self.range: + x = self._idwt(x, "haar", rescale=True) + x = x[:, :, self.patch_size - 1 :, ...] + return x + + def _iarrange(self, x): + x = rearrange( + x, + "b (c p1 p2 p3) t h w -> b c (t p1) (h p2) (w p3)", + p1=self.patch_size, + p2=self.patch_size, + p3=self.patch_size, + ) + x = x[:, :, self.patch_size - 1 :, ...] + return x diff --git a/ComfyUI/comfy/ldm/cosmos/cosmos_tokenizer/utils.py b/ComfyUI/comfy/ldm/cosmos/cosmos_tokenizer/utils.py new file mode 100644 index 0000000000000000000000000000000000000000..3af8d0d0571c04b87c66f5280442f5f4e345ba0e --- /dev/null +++ b/ComfyUI/comfy/ldm/cosmos/cosmos_tokenizer/utils.py @@ -0,0 +1,112 @@ +# SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Shared utilities for the networks module.""" + +from typing import Any + +import torch +from einops import rearrange + + +import comfy.ops +ops = comfy.ops.disable_weight_init + +def time2batch(x: torch.Tensor) -> tuple[torch.Tensor, int]: + batch_size = x.shape[0] + return rearrange(x, "b c t h w -> (b t) c h w"), batch_size + + +def batch2time(x: torch.Tensor, batch_size: int) -> torch.Tensor: + return rearrange(x, "(b t) c h w -> b c t h w", b=batch_size) + + +def space2batch(x: torch.Tensor) -> tuple[torch.Tensor, int]: + batch_size, height = x.shape[0], x.shape[-2] + return rearrange(x, "b c t h w -> (b h w) c t"), batch_size, height + + +def batch2space(x: torch.Tensor, batch_size: int, height: int) -> torch.Tensor: + return rearrange(x, "(b h w) c t -> b c t h w", b=batch_size, h=height) + + +def cast_tuple(t: Any, length: int = 1) -> Any: + return t if isinstance(t, tuple) else ((t,) * length) + + +def replication_pad(x): + return torch.cat([x[:, :, :1, ...], x], dim=2) + + +def divisible_by(num: int, den: int) -> bool: + return (num % den) == 0 + + +def is_odd(n: int) -> bool: + return not divisible_by(n, 2) + + +def nonlinearity(x): + return x * torch.sigmoid(x) + + +def Normalize(in_channels, num_groups=32): + return ops.GroupNorm( + num_groups=num_groups, num_channels=in_channels, eps=1e-6, affine=True + ) + + +class CausalNormalize(torch.nn.Module): + def __init__(self, in_channels, num_groups=1): + super().__init__() + self.norm = ops.GroupNorm( + num_groups=num_groups, + num_channels=in_channels, + eps=1e-6, + affine=True, + ) + self.num_groups = num_groups + + def forward(self, x): + # if num_groups !=1, we apply a spatio-temporal groupnorm for backward compatibility purpose. + # All new models should use num_groups=1, otherwise causality is not guaranteed. + if self.num_groups == 1: + x, batch_size = time2batch(x) + return batch2time(self.norm(x), batch_size) + return self.norm(x) + + +def exists(v): + return v is not None + + +def default(*args): + for arg in args: + if exists(arg): + return arg + return None + + +def round_ste(z: torch.Tensor) -> torch.Tensor: + """Round with straight through gradients.""" + zhat = z.round() + return z + (zhat - z).detach() + + +def log(t, eps=1e-5): + return t.clamp(min=eps).log() + + +def entropy(prob): + return (-prob * log(prob)).sum(dim=-1) diff --git a/ComfyUI/comfy/ldm/genmo/joint_model/asymm_models_joint.py b/ComfyUI/comfy/ldm/genmo/joint_model/asymm_models_joint.py new file mode 100644 index 0000000000000000000000000000000000000000..366a8b7133cb665731148a2cdb2ce1013cf0ae92 --- /dev/null +++ b/ComfyUI/comfy/ldm/genmo/joint_model/asymm_models_joint.py @@ -0,0 +1,556 @@ +#original code from https://github.com/genmoai/models under apache 2.0 license +#adapted to ComfyUI + +from typing import Dict, List, Optional, Tuple + +import torch +import torch.nn as nn +import torch.nn.functional as F +from einops import rearrange +# from flash_attn import flash_attn_varlen_qkvpacked_func +from comfy.ldm.modules.attention import optimized_attention + +from .layers import ( + FeedForward, + PatchEmbed, + TimestepEmbedder, +) + +from .rope_mixed import ( + compute_mixed_rotation, + create_position_matrix, +) +from .temporal_rope import apply_rotary_emb_qk_real +from .utils import ( + AttentionPool, + modulate, +) + +import comfy.ldm.common_dit +import comfy.ops + + +def modulated_rmsnorm(x, scale, eps=1e-6): + # Normalize and modulate + x_normed = comfy.ldm.common_dit.rms_norm(x, eps=eps) + x_modulated = x_normed * (1 + scale.unsqueeze(1)) + + return x_modulated + + +def residual_tanh_gated_rmsnorm(x, x_res, gate, eps=1e-6): + # Apply tanh to gate + tanh_gate = torch.tanh(gate).unsqueeze(1) + + # Normalize and apply gated scaling + x_normed = comfy.ldm.common_dit.rms_norm(x_res, eps=eps) * tanh_gate + + # Apply residual connection + output = x + x_normed + + return output + +class AsymmetricAttention(nn.Module): + def __init__( + self, + dim_x: int, + dim_y: int, + num_heads: int = 8, + qkv_bias: bool = True, + qk_norm: bool = False, + attn_drop: float = 0.0, + update_y: bool = True, + out_bias: bool = True, + attend_to_padding: bool = False, + softmax_scale: Optional[float] = None, + device: Optional[torch.device] = None, + dtype=None, + operations=None, + ): + super().__init__() + self.dim_x = dim_x + self.dim_y = dim_y + self.num_heads = num_heads + self.head_dim = dim_x // num_heads + self.attn_drop = attn_drop + self.update_y = update_y + self.attend_to_padding = attend_to_padding + self.softmax_scale = softmax_scale + if dim_x % num_heads != 0: + raise ValueError( + f"dim_x={dim_x} should be divisible by num_heads={num_heads}" + ) + + # Input layers. + self.qkv_bias = qkv_bias + self.qkv_x = operations.Linear(dim_x, 3 * dim_x, bias=qkv_bias, device=device, dtype=dtype) + # Project text features to match visual features (dim_y -> dim_x) + self.qkv_y = operations.Linear(dim_y, 3 * dim_x, bias=qkv_bias, device=device, dtype=dtype) + + # Query and key normalization for stability. + assert qk_norm + self.q_norm_x = operations.RMSNorm(self.head_dim, eps=1e-5, device=device, dtype=dtype) + self.k_norm_x = operations.RMSNorm(self.head_dim, eps=1e-5, device=device, dtype=dtype) + self.q_norm_y = operations.RMSNorm(self.head_dim, eps=1e-5, device=device, dtype=dtype) + self.k_norm_y = operations.RMSNorm(self.head_dim, eps=1e-5, device=device, dtype=dtype) + + # Output layers. y features go back down from dim_x -> dim_y. + self.proj_x = operations.Linear(dim_x, dim_x, bias=out_bias, device=device, dtype=dtype) + self.proj_y = ( + operations.Linear(dim_x, dim_y, bias=out_bias, device=device, dtype=dtype) + if update_y + else nn.Identity() + ) + + def forward( + self, + x: torch.Tensor, # (B, N, dim_x) + y: torch.Tensor, # (B, L, dim_y) + scale_x: torch.Tensor, # (B, dim_x), modulation for pre-RMSNorm. + scale_y: torch.Tensor, # (B, dim_y), modulation for pre-RMSNorm. + crop_y, + **rope_rotation, + ) -> Tuple[torch.Tensor, torch.Tensor]: + rope_cos = rope_rotation.get("rope_cos") + rope_sin = rope_rotation.get("rope_sin") + # Pre-norm for visual features + x = modulated_rmsnorm(x, scale_x) # (B, M, dim_x) where M = N / cp_group_size + + # Process visual features + # qkv_x = self.qkv_x(x) # (B, M, 3 * dim_x) + # assert qkv_x.dtype == torch.bfloat16 + # qkv_x = all_to_all_collect_tokens( + # qkv_x, self.num_heads + # ) # (3, B, N, local_h, head_dim) + + # Process text features + y = modulated_rmsnorm(y, scale_y) # (B, L, dim_y) + q_y, k_y, v_y = self.qkv_y(y).view(y.shape[0], y.shape[1], 3, self.num_heads, -1).unbind(2) # (B, N, local_h, head_dim) + + q_y = self.q_norm_y(q_y) + k_y = self.k_norm_y(k_y) + + # Split qkv_x into q, k, v + q_x, k_x, v_x = self.qkv_x(x).view(x.shape[0], x.shape[1], 3, self.num_heads, -1).unbind(2) # (B, N, local_h, head_dim) + q_x = self.q_norm_x(q_x) + q_x = apply_rotary_emb_qk_real(q_x, rope_cos, rope_sin) + k_x = self.k_norm_x(k_x) + k_x = apply_rotary_emb_qk_real(k_x, rope_cos, rope_sin) + + q = torch.cat([q_x, q_y[:, :crop_y]], dim=1).transpose(1, 2) + k = torch.cat([k_x, k_y[:, :crop_y]], dim=1).transpose(1, 2) + v = torch.cat([v_x, v_y[:, :crop_y]], dim=1).transpose(1, 2) + + xy = optimized_attention(q, + k, + v, self.num_heads, skip_reshape=True) + + x, y = torch.tensor_split(xy, (q_x.shape[1],), dim=1) + x = self.proj_x(x) + o = torch.zeros(y.shape[0], q_y.shape[1], y.shape[-1], device=y.device, dtype=y.dtype) + o[:, :y.shape[1]] = y + + y = self.proj_y(o) + # print("ox", x) + # print("oy", y) + return x, y + + +class AsymmetricJointBlock(nn.Module): + def __init__( + self, + hidden_size_x: int, + hidden_size_y: int, + num_heads: int, + *, + mlp_ratio_x: float = 8.0, # Ratio of hidden size to d_model for MLP for visual tokens. + mlp_ratio_y: float = 4.0, # Ratio of hidden size to d_model for MLP for text tokens. + update_y: bool = True, # Whether to update text tokens in this block. + device: Optional[torch.device] = None, + dtype=None, + operations=None, + **block_kwargs, + ): + super().__init__() + self.update_y = update_y + self.hidden_size_x = hidden_size_x + self.hidden_size_y = hidden_size_y + self.mod_x = operations.Linear(hidden_size_x, 4 * hidden_size_x, device=device, dtype=dtype) + if self.update_y: + self.mod_y = operations.Linear(hidden_size_x, 4 * hidden_size_y, device=device, dtype=dtype) + else: + self.mod_y = operations.Linear(hidden_size_x, hidden_size_y, device=device, dtype=dtype) + + # Self-attention: + self.attn = AsymmetricAttention( + hidden_size_x, + hidden_size_y, + num_heads=num_heads, + update_y=update_y, + device=device, + dtype=dtype, + operations=operations, + **block_kwargs, + ) + + # MLP. + mlp_hidden_dim_x = int(hidden_size_x * mlp_ratio_x) + assert mlp_hidden_dim_x == int(1536 * 8) + self.mlp_x = FeedForward( + in_features=hidden_size_x, + hidden_size=mlp_hidden_dim_x, + multiple_of=256, + ffn_dim_multiplier=None, + device=device, + dtype=dtype, + operations=operations, + ) + + # MLP for text not needed in last block. + if self.update_y: + mlp_hidden_dim_y = int(hidden_size_y * mlp_ratio_y) + self.mlp_y = FeedForward( + in_features=hidden_size_y, + hidden_size=mlp_hidden_dim_y, + multiple_of=256, + ffn_dim_multiplier=None, + device=device, + dtype=dtype, + operations=operations, + ) + + def forward( + self, + x: torch.Tensor, + c: torch.Tensor, + y: torch.Tensor, + **attn_kwargs, + ): + """Forward pass of a block. + + Args: + x: (B, N, dim) tensor of visual tokens + c: (B, dim) tensor of conditioned features + y: (B, L, dim) tensor of text tokens + num_frames: Number of frames in the video. N = num_frames * num_spatial_tokens + + Returns: + x: (B, N, dim) tensor of visual tokens after block + y: (B, L, dim) tensor of text tokens after block + """ + N = x.size(1) + + c = F.silu(c) + mod_x = self.mod_x(c) + scale_msa_x, gate_msa_x, scale_mlp_x, gate_mlp_x = mod_x.chunk(4, dim=1) + + mod_y = self.mod_y(c) + if self.update_y: + scale_msa_y, gate_msa_y, scale_mlp_y, gate_mlp_y = mod_y.chunk(4, dim=1) + else: + scale_msa_y = mod_y + + # Self-attention block. + x_attn, y_attn = self.attn( + x, + y, + scale_x=scale_msa_x, + scale_y=scale_msa_y, + **attn_kwargs, + ) + + assert x_attn.size(1) == N + x = residual_tanh_gated_rmsnorm(x, x_attn, gate_msa_x) + if self.update_y: + y = residual_tanh_gated_rmsnorm(y, y_attn, gate_msa_y) + + # MLP block. + x = self.ff_block_x(x, scale_mlp_x, gate_mlp_x) + if self.update_y: + y = self.ff_block_y(y, scale_mlp_y, gate_mlp_y) + + return x, y + + def ff_block_x(self, x, scale_x, gate_x): + x_mod = modulated_rmsnorm(x, scale_x) + x_res = self.mlp_x(x_mod) + x = residual_tanh_gated_rmsnorm(x, x_res, gate_x) # Sandwich norm + return x + + def ff_block_y(self, y, scale_y, gate_y): + y_mod = modulated_rmsnorm(y, scale_y) + y_res = self.mlp_y(y_mod) + y = residual_tanh_gated_rmsnorm(y, y_res, gate_y) # Sandwich norm + return y + + +class FinalLayer(nn.Module): + """ + The final layer of DiT. + """ + + def __init__( + self, + hidden_size, + patch_size, + out_channels, + device: Optional[torch.device] = None, + dtype=None, + operations=None, + ): + super().__init__() + self.norm_final = operations.LayerNorm( + hidden_size, elementwise_affine=False, eps=1e-6, device=device, dtype=dtype + ) + self.mod = operations.Linear(hidden_size, 2 * hidden_size, device=device, dtype=dtype) + self.linear = operations.Linear( + hidden_size, patch_size * patch_size * out_channels, device=device, dtype=dtype + ) + + def forward(self, x, c): + c = F.silu(c) + shift, scale = self.mod(c).chunk(2, dim=1) + x = modulate(self.norm_final(x), shift, scale) + x = self.linear(x) + return x + + +class AsymmDiTJoint(nn.Module): + """ + Diffusion model with a Transformer backbone. + + Ingests text embeddings instead of a label. + """ + + def __init__( + self, + *, + patch_size=2, + in_channels=4, + hidden_size_x=1152, + hidden_size_y=1152, + depth=48, + num_heads=16, + mlp_ratio_x=8.0, + mlp_ratio_y=4.0, + use_t5: bool = False, + t5_feat_dim: int = 4096, + t5_token_length: int = 256, + learn_sigma=True, + patch_embed_bias: bool = True, + timestep_mlp_bias: bool = True, + attend_to_padding: bool = False, + timestep_scale: Optional[float] = None, + use_extended_posenc: bool = False, + posenc_preserve_area: bool = False, + rope_theta: float = 10000.0, + image_model=None, + device: Optional[torch.device] = None, + dtype=None, + operations=None, + **block_kwargs, + ): + super().__init__() + + self.dtype = dtype + self.learn_sigma = learn_sigma + self.in_channels = in_channels + self.out_channels = in_channels * 2 if learn_sigma else in_channels + self.patch_size = patch_size + self.num_heads = num_heads + self.hidden_size_x = hidden_size_x + self.hidden_size_y = hidden_size_y + self.head_dim = ( + hidden_size_x // num_heads + ) # Head dimension and count is determined by visual. + self.attend_to_padding = attend_to_padding + self.use_extended_posenc = use_extended_posenc + self.posenc_preserve_area = posenc_preserve_area + self.use_t5 = use_t5 + self.t5_token_length = t5_token_length + self.t5_feat_dim = t5_feat_dim + self.rope_theta = ( + rope_theta # Scaling factor for frequency computation for temporal RoPE. + ) + + self.x_embedder = PatchEmbed( + patch_size=patch_size, + in_chans=in_channels, + embed_dim=hidden_size_x, + bias=patch_embed_bias, + dtype=dtype, + device=device, + operations=operations + ) + # Conditionings + # Timestep + self.t_embedder = TimestepEmbedder( + hidden_size_x, bias=timestep_mlp_bias, timestep_scale=timestep_scale, dtype=dtype, device=device, operations=operations + ) + + if self.use_t5: + # Caption Pooling (T5) + self.t5_y_embedder = AttentionPool( + t5_feat_dim, num_heads=8, output_dim=hidden_size_x, dtype=dtype, device=device, operations=operations + ) + + # Dense Embedding Projection (T5) + self.t5_yproj = operations.Linear( + t5_feat_dim, hidden_size_y, bias=True, dtype=dtype, device=device + ) + + # Initialize pos_frequencies as an empty parameter. + self.pos_frequencies = nn.Parameter( + torch.empty(3, self.num_heads, self.head_dim // 2, dtype=dtype, device=device) + ) + + assert not self.attend_to_padding + + # for depth 48: + # b = 0: AsymmetricJointBlock, update_y=True + # b = 1: AsymmetricJointBlock, update_y=True + # ... + # b = 46: AsymmetricJointBlock, update_y=True + # b = 47: AsymmetricJointBlock, update_y=False. No need to update text features. + blocks = [] + for b in range(depth): + # Joint multi-modal block + update_y = b < depth - 1 + block = AsymmetricJointBlock( + hidden_size_x, + hidden_size_y, + num_heads, + mlp_ratio_x=mlp_ratio_x, + mlp_ratio_y=mlp_ratio_y, + update_y=update_y, + attend_to_padding=attend_to_padding, + device=device, + dtype=dtype, + operations=operations, + **block_kwargs, + ) + + blocks.append(block) + self.blocks = nn.ModuleList(blocks) + + self.final_layer = FinalLayer( + hidden_size_x, patch_size, self.out_channels, dtype=dtype, device=device, operations=operations + ) + + def embed_x(self, x: torch.Tensor) -> torch.Tensor: + """ + Args: + x: (B, C=12, T, H, W) tensor of visual tokens + + Returns: + x: (B, C=3072, N) tensor of visual tokens with positional embedding. + """ + return self.x_embedder(x) # Convert BcTHW to BCN + + def prepare( + self, + x: torch.Tensor, + sigma: torch.Tensor, + t5_feat: torch.Tensor, + t5_mask: torch.Tensor, + ): + """Prepare input and conditioning embeddings.""" + # Visual patch embeddings with positional encoding. + T, H, W = x.shape[-3:] + pH, pW = H // self.patch_size, W // self.patch_size + x = self.embed_x(x) # (B, N, D), where N = T * H * W / patch_size ** 2 + assert x.ndim == 3 + + pH, pW = H // self.patch_size, W // self.patch_size + N = T * pH * pW + assert x.size(1) == N + pos = create_position_matrix( + T, pH=pH, pW=pW, device=x.device, dtype=torch.float32 + ) # (N, 3) + rope_cos, rope_sin = compute_mixed_rotation( + freqs=comfy.ops.cast_to(self.pos_frequencies, dtype=x.dtype, device=x.device), pos=pos + ) # Each are (N, num_heads, dim // 2) + + c_t = self.t_embedder(1 - sigma, out_dtype=x.dtype) # (B, D) + + t5_y_pool = self.t5_y_embedder(t5_feat, t5_mask) # (B, D) + + c = c_t + t5_y_pool + + y_feat = self.t5_yproj(t5_feat) # (B, L, t5_feat_dim) --> (B, L, D) + + return x, c, y_feat, rope_cos, rope_sin + + def forward( + self, + x: torch.Tensor, + timestep: torch.Tensor, + context: List[torch.Tensor], + attention_mask: List[torch.Tensor], + num_tokens=256, + packed_indices: Dict[str, torch.Tensor] = None, + rope_cos: torch.Tensor = None, + rope_sin: torch.Tensor = None, + control=None, transformer_options={}, **kwargs + ): + patches_replace = transformer_options.get("patches_replace", {}) + y_feat = context + y_mask = attention_mask + sigma = timestep + """Forward pass of DiT. + + Args: + x: (B, C, T, H, W) tensor of spatial inputs (images or latent representations of images) + sigma: (B,) tensor of noise standard deviations + y_feat: List((B, L, y_feat_dim) tensor of caption token features. For SDXL text encoders: L=77, y_feat_dim=2048) + y_mask: List((B, L) boolean tensor indicating which tokens are not padding) + packed_indices: Dict with keys for Flash Attention. Result of compute_packed_indices. + """ + B, _, T, H, W = x.shape + + x, c, y_feat, rope_cos, rope_sin = self.prepare( + x, sigma, y_feat, y_mask + ) + del y_mask + + blocks_replace = patches_replace.get("dit", {}) + for i, block in enumerate(self.blocks): + if ("double_block", i) in blocks_replace: + def block_wrap(args): + out = {} + out["img"], out["txt"] = block( + args["img"], + args["vec"], + args["txt"], + rope_cos=args["rope_cos"], + rope_sin=args["rope_sin"], + crop_y=args["num_tokens"] + ) + return out + out = blocks_replace[("double_block", i)]({"img": x, "txt": y_feat, "vec": c, "rope_cos": rope_cos, "rope_sin": rope_sin, "num_tokens": num_tokens}, {"original_block": block_wrap}) + y_feat = out["txt"] + x = out["img"] + else: + x, y_feat = block( + x, + c, + y_feat, + rope_cos=rope_cos, + rope_sin=rope_sin, + crop_y=num_tokens, + ) # (B, M, D), (B, L, D) + del y_feat # Final layers don't use dense text features. + + x = self.final_layer(x, c) # (B, M, patch_size ** 2 * out_channels) + x = rearrange( + x, + "B (T hp wp) (p1 p2 c) -> B c T (hp p1) (wp p2)", + T=T, + hp=H // self.patch_size, + wp=W // self.patch_size, + p1=self.patch_size, + p2=self.patch_size, + c=self.out_channels, + ) + + return -x diff --git a/ComfyUI/comfy/ldm/genmo/joint_model/layers.py b/ComfyUI/comfy/ldm/genmo/joint_model/layers.py new file mode 100644 index 0000000000000000000000000000000000000000..e310bd717833b29e3730894370a55cd49802de93 --- /dev/null +++ b/ComfyUI/comfy/ldm/genmo/joint_model/layers.py @@ -0,0 +1,153 @@ +#original code from https://github.com/genmoai/models under apache 2.0 license +#adapted to ComfyUI + +import collections.abc +import math +from itertools import repeat +from typing import Callable, Optional + +import torch +import torch.nn as nn +import torch.nn.functional as F +from einops import rearrange +import comfy.ldm.common_dit + + +# From PyTorch internals +def _ntuple(n): + def parse(x): + if isinstance(x, collections.abc.Iterable) and not isinstance(x, str): + return tuple(x) + return tuple(repeat(x, n)) + + return parse + + +to_2tuple = _ntuple(2) + + +class TimestepEmbedder(nn.Module): + def __init__( + self, + hidden_size: int, + frequency_embedding_size: int = 256, + *, + bias: bool = True, + timestep_scale: Optional[float] = None, + dtype=None, + device=None, + operations=None, + ): + super().__init__() + self.mlp = nn.Sequential( + operations.Linear(frequency_embedding_size, hidden_size, bias=bias, dtype=dtype, device=device), + nn.SiLU(), + operations.Linear(hidden_size, hidden_size, bias=bias, dtype=dtype, device=device), + ) + self.frequency_embedding_size = frequency_embedding_size + self.timestep_scale = timestep_scale + + @staticmethod + def timestep_embedding(t, dim, max_period=10000): + half = dim // 2 + freqs = torch.arange(start=0, end=half, dtype=torch.float32, device=t.device) + freqs.mul_(-math.log(max_period) / half).exp_() + args = t[:, None].float() * freqs[None] + embedding = torch.cat([torch.cos(args), torch.sin(args)], dim=-1) + if dim % 2: + embedding = torch.cat( + [embedding, torch.zeros_like(embedding[:, :1])], dim=-1 + ) + return embedding + + def forward(self, t, out_dtype): + if self.timestep_scale is not None: + t = t * self.timestep_scale + t_freq = self.timestep_embedding(t, self.frequency_embedding_size).to(dtype=out_dtype) + t_emb = self.mlp(t_freq) + return t_emb + + +class FeedForward(nn.Module): + def __init__( + self, + in_features: int, + hidden_size: int, + multiple_of: int, + ffn_dim_multiplier: Optional[float], + device: Optional[torch.device] = None, + dtype=None, + operations=None, + ): + super().__init__() + # keep parameter count and computation constant compared to standard FFN + hidden_size = int(2 * hidden_size / 3) + # custom dim factor multiplier + if ffn_dim_multiplier is not None: + hidden_size = int(ffn_dim_multiplier * hidden_size) + hidden_size = multiple_of * ((hidden_size + multiple_of - 1) // multiple_of) + + self.hidden_dim = hidden_size + self.w1 = operations.Linear(in_features, 2 * hidden_size, bias=False, device=device, dtype=dtype) + self.w2 = operations.Linear(hidden_size, in_features, bias=False, device=device, dtype=dtype) + + def forward(self, x): + x, gate = self.w1(x).chunk(2, dim=-1) + x = self.w2(F.silu(x) * gate) + return x + + +class PatchEmbed(nn.Module): + def __init__( + self, + patch_size: int = 16, + in_chans: int = 3, + embed_dim: int = 768, + norm_layer: Optional[Callable] = None, + flatten: bool = True, + bias: bool = True, + dynamic_img_pad: bool = False, + dtype=None, + device=None, + operations=None, + ): + super().__init__() + self.patch_size = to_2tuple(patch_size) + self.flatten = flatten + self.dynamic_img_pad = dynamic_img_pad + + self.proj = operations.Conv2d( + in_chans, + embed_dim, + kernel_size=patch_size, + stride=patch_size, + bias=bias, + device=device, + dtype=dtype, + ) + assert norm_layer is None + self.norm = ( + norm_layer(embed_dim, device=device) if norm_layer else nn.Identity() + ) + + def forward(self, x): + B, _C, T, H, W = x.shape + if not self.dynamic_img_pad: + assert H % self.patch_size[0] == 0, f"Input height ({H}) should be divisible by patch size ({self.patch_size[0]})." + assert W % self.patch_size[1] == 0, f"Input width ({W}) should be divisible by patch size ({self.patch_size[1]})." + else: + pad_h = (self.patch_size[0] - H % self.patch_size[0]) % self.patch_size[0] + pad_w = (self.patch_size[1] - W % self.patch_size[1]) % self.patch_size[1] + x = F.pad(x, (0, pad_w, 0, pad_h)) + + x = rearrange(x, "B C T H W -> (B T) C H W", B=B, T=T) + x = comfy.ldm.common_dit.pad_to_patch_size(x, self.patch_size, padding_mode='circular') + x = self.proj(x) + + # Flatten temporal and spatial dimensions. + if not self.flatten: + raise NotImplementedError("Must flatten output.") + x = rearrange(x, "(B T) C H W -> B (T H W) C", B=B, T=T) + + x = self.norm(x) + return x diff --git a/ComfyUI/comfy/ldm/genmo/joint_model/rope_mixed.py b/ComfyUI/comfy/ldm/genmo/joint_model/rope_mixed.py new file mode 100644 index 0000000000000000000000000000000000000000..dee3fa21f5318a610321fc9372553d618462f773 --- /dev/null +++ b/ComfyUI/comfy/ldm/genmo/joint_model/rope_mixed.py @@ -0,0 +1,88 @@ +#original code from https://github.com/genmoai/models under apache 2.0 license + +# import functools +import math + +import torch + + +def centers(start: float, stop, num, dtype=None, device=None): + """linspace through bin centers. + + Args: + start (float): Start of the range. + stop (float): End of the range. + num (int): Number of points. + dtype (torch.dtype): Data type of the points. + device (torch.device): Device of the points. + + Returns: + centers (Tensor): Centers of the bins. Shape: (num,). + """ + edges = torch.linspace(start, stop, num + 1, dtype=dtype, device=device) + return (edges[:-1] + edges[1:]) / 2 + + +# @functools.lru_cache(maxsize=1) +def create_position_matrix( + T: int, + pH: int, + pW: int, + device: torch.device, + dtype: torch.dtype, + *, + target_area: float = 36864, +): + """ + Args: + T: int - Temporal dimension + pH: int - Height dimension after patchify + pW: int - Width dimension after patchify + + Returns: + pos: [T * pH * pW, 3] - position matrix + """ + # Create 1D tensors for each dimension + t = torch.arange(T, dtype=dtype) + + # Positionally interpolate to area 36864. + # (3072x3072 frame with 16x16 patches = 192x192 latents). + # This automatically scales rope positions when the resolution changes. + # We use a large target area so the model is more sensitive + # to changes in the learned pos_frequencies matrix. + scale = math.sqrt(target_area / (pW * pH)) + w = centers(-pW * scale / 2, pW * scale / 2, pW) + h = centers(-pH * scale / 2, pH * scale / 2, pH) + + # Use meshgrid to create 3D grids + grid_t, grid_h, grid_w = torch.meshgrid(t, h, w, indexing="ij") + + # Stack and reshape the grids. + pos = torch.stack([grid_t, grid_h, grid_w], dim=-1) # [T, pH, pW, 3] + pos = pos.view(-1, 3) # [T * pH * pW, 3] + pos = pos.to(dtype=dtype, device=device) + + return pos + + +def compute_mixed_rotation( + freqs: torch.Tensor, + pos: torch.Tensor, +): + """ + Project each 3-dim position into per-head, per-head-dim 1D frequencies. + + Args: + freqs: [3, num_heads, num_freqs] - learned rotation frequency (for t, row, col) for each head position + pos: [N, 3] - position of each token + num_heads: int + + Returns: + freqs_cos: [N, num_heads, num_freqs] - cosine components + freqs_sin: [N, num_heads, num_freqs] - sine components + """ + assert freqs.ndim == 3 + freqs_sum = torch.einsum("Nd,dhf->Nhf", pos.to(freqs), freqs) + freqs_cos = torch.cos(freqs_sum) + freqs_sin = torch.sin(freqs_sum) + return freqs_cos, freqs_sin diff --git a/ComfyUI/comfy/ldm/genmo/joint_model/temporal_rope.py b/ComfyUI/comfy/ldm/genmo/joint_model/temporal_rope.py new file mode 100644 index 0000000000000000000000000000000000000000..88f5d6d26151db0c8ad0a89fcf748c45d4b89bc0 --- /dev/null +++ b/ComfyUI/comfy/ldm/genmo/joint_model/temporal_rope.py @@ -0,0 +1,34 @@ +#original code from https://github.com/genmoai/models under apache 2.0 license + +# Based on Llama3 Implementation. +import torch + + +def apply_rotary_emb_qk_real( + xqk: torch.Tensor, + freqs_cos: torch.Tensor, + freqs_sin: torch.Tensor, +) -> torch.Tensor: + """ + Apply rotary embeddings to input tensors using the given frequency tensor without complex numbers. + + Args: + xqk (torch.Tensor): Query and/or Key tensors to apply rotary embeddings. Shape: (B, S, *, num_heads, D) + Can be either just query or just key, or both stacked along some batch or * dim. + freqs_cos (torch.Tensor): Precomputed cosine frequency tensor. + freqs_sin (torch.Tensor): Precomputed sine frequency tensor. + + Returns: + torch.Tensor: The input tensor with rotary embeddings applied. + """ + # Split the last dimension into even and odd parts + xqk_even = xqk[..., 0::2] + xqk_odd = xqk[..., 1::2] + + # Apply rotation + cos_part = (xqk_even * freqs_cos - xqk_odd * freqs_sin).type_as(xqk) + sin_part = (xqk_even * freqs_sin + xqk_odd * freqs_cos).type_as(xqk) + + # Interleave the results back into the original shape + out = torch.stack([cos_part, sin_part], dim=-1).flatten(-2) + return out diff --git a/ComfyUI/comfy/ldm/genmo/joint_model/utils.py b/ComfyUI/comfy/ldm/genmo/joint_model/utils.py new file mode 100644 index 0000000000000000000000000000000000000000..1b399d5d2128b7f5c7be141c3d8e7047df93b273 --- /dev/null +++ b/ComfyUI/comfy/ldm/genmo/joint_model/utils.py @@ -0,0 +1,102 @@ +#original code from https://github.com/genmoai/models under apache 2.0 license +#adapted to ComfyUI + +from typing import Optional + +import torch +import torch.nn as nn +import torch.nn.functional as F + + +def modulate(x, shift, scale): + return x * (1 + scale.unsqueeze(1)) + shift.unsqueeze(1) + + +def pool_tokens(x: torch.Tensor, mask: torch.Tensor, *, keepdim=False) -> torch.Tensor: + """ + Pool tokens in x using mask. + + NOTE: We assume x does not require gradients. + + Args: + x: (B, L, D) tensor of tokens. + mask: (B, L) boolean tensor indicating which tokens are not padding. + + Returns: + pooled: (B, D) tensor of pooled tokens. + """ + assert x.size(1) == mask.size(1) # Expected mask to have same length as tokens. + assert x.size(0) == mask.size(0) # Expected mask to have same batch size as tokens. + mask = mask[:, :, None].to(dtype=x.dtype) + mask = mask / mask.sum(dim=1, keepdim=True).clamp(min=1) + pooled = (x * mask).sum(dim=1, keepdim=keepdim) + return pooled + + +class AttentionPool(nn.Module): + def __init__( + self, + embed_dim: int, + num_heads: int, + output_dim: int = None, + device: Optional[torch.device] = None, + dtype=None, + operations=None, + ): + """ + Args: + spatial_dim (int): Number of tokens in sequence length. + embed_dim (int): Dimensionality of input tokens. + num_heads (int): Number of attention heads. + output_dim (int): Dimensionality of output tokens. Defaults to embed_dim. + """ + super().__init__() + self.num_heads = num_heads + self.to_kv = operations.Linear(embed_dim, 2 * embed_dim, device=device, dtype=dtype) + self.to_q = operations.Linear(embed_dim, embed_dim, device=device, dtype=dtype) + self.to_out = operations.Linear(embed_dim, output_dim or embed_dim, device=device, dtype=dtype) + + def forward(self, x, mask): + """ + Args: + x (torch.Tensor): (B, L, D) tensor of input tokens. + mask (torch.Tensor): (B, L) boolean tensor indicating which tokens are not padding. + + NOTE: We assume x does not require gradients. + + Returns: + x (torch.Tensor): (B, D) tensor of pooled tokens. + """ + D = x.size(2) + + # Construct attention mask, shape: (B, 1, num_queries=1, num_keys=1+L). + attn_mask = mask[:, None, None, :].bool() # (B, 1, 1, L). + attn_mask = F.pad(attn_mask, (1, 0), value=True) # (B, 1, 1, 1+L). + + # Average non-padding token features. These will be used as the query. + x_pool = pool_tokens(x, mask, keepdim=True) # (B, 1, D) + + # Concat pooled features to input sequence. + x = torch.cat([x_pool, x], dim=1) # (B, L+1, D) + + # Compute queries, keys, values. Only the mean token is used to create a query. + kv = self.to_kv(x) # (B, L+1, 2 * D) + q = self.to_q(x[:, 0]) # (B, D) + + # Extract heads. + head_dim = D // self.num_heads + kv = kv.unflatten(2, (2, self.num_heads, head_dim)) # (B, 1+L, 2, H, head_dim) + kv = kv.transpose(1, 3) # (B, H, 2, 1+L, head_dim) + k, v = kv.unbind(2) # (B, H, 1+L, head_dim) + q = q.unflatten(1, (self.num_heads, head_dim)) # (B, H, head_dim) + q = q.unsqueeze(2) # (B, H, 1, head_dim) + + # Compute attention. + x = F.scaled_dot_product_attention( + q, k, v, attn_mask=attn_mask, dropout_p=0.0 + ) # (B, H, 1, head_dim) + + # Concatenate heads and run output. + x = x.squeeze(2).flatten(1, 2) # (B, D = H * head_dim) + x = self.to_out(x) + return x diff --git a/ComfyUI/comfy/ldm/genmo/vae/model.py b/ComfyUI/comfy/ldm/genmo/vae/model.py new file mode 100644 index 0000000000000000000000000000000000000000..1bde0c1ed7372be184e634777b9e27b67692b1f4 --- /dev/null +++ b/ComfyUI/comfy/ldm/genmo/vae/model.py @@ -0,0 +1,711 @@ +#original code from https://github.com/genmoai/models under apache 2.0 license +#adapted to ComfyUI + +from typing import List, Optional, Tuple, Union +from functools import partial +import math + +import torch +import torch.nn as nn +import torch.nn.functional as F +from einops import rearrange + +from comfy.ldm.modules.attention import optimized_attention + +import comfy.ops +ops = comfy.ops.disable_weight_init + +# import mochi_preview.dit.joint_model.context_parallel as cp +# from mochi_preview.vae.cp_conv import cp_pass_frames, gather_all_frames + + +def cast_tuple(t, length=1): + return t if isinstance(t, tuple) else ((t,) * length) + + +class GroupNormSpatial(ops.GroupNorm): + """ + GroupNorm applied per-frame. + """ + + def forward(self, x: torch.Tensor, *, chunk_size: int = 8): + B, C, T, H, W = x.shape + x = rearrange(x, "B C T H W -> (B T) C H W") + # Run group norm in chunks. + output = torch.empty_like(x) + for b in range(0, B * T, chunk_size): + output[b : b + chunk_size] = super().forward(x[b : b + chunk_size]) + return rearrange(output, "(B T) C H W -> B C T H W", B=B, T=T) + +class PConv3d(ops.Conv3d): + def __init__( + self, + in_channels, + out_channels, + kernel_size: Union[int, Tuple[int, int, int]], + stride: Union[int, Tuple[int, int, int]], + causal: bool = True, + context_parallel: bool = True, + **kwargs, + ): + self.causal = causal + self.context_parallel = context_parallel + kernel_size = cast_tuple(kernel_size, 3) + stride = cast_tuple(stride, 3) + height_pad = (kernel_size[1] - 1) // 2 + width_pad = (kernel_size[2] - 1) // 2 + + super().__init__( + in_channels=in_channels, + out_channels=out_channels, + kernel_size=kernel_size, + stride=stride, + dilation=(1, 1, 1), + padding=(0, height_pad, width_pad), + **kwargs, + ) + + def forward(self, x: torch.Tensor): + # Compute padding amounts. + context_size = self.kernel_size[0] - 1 + if self.causal: + pad_front = context_size + pad_back = 0 + else: + pad_front = context_size // 2 + pad_back = context_size - pad_front + + # Apply padding. + assert self.padding_mode == "replicate" # DEBUG + mode = "constant" if self.padding_mode == "zeros" else self.padding_mode + x = F.pad(x, (0, 0, 0, 0, pad_front, pad_back), mode=mode) + return super().forward(x) + + +class Conv1x1(ops.Linear): + """*1x1 Conv implemented with a linear layer.""" + + def __init__(self, in_features: int, out_features: int, *args, **kwargs): + super().__init__(in_features, out_features, *args, **kwargs) + + def forward(self, x: torch.Tensor): + """Forward pass. + + Args: + x: Input tensor. Shape: [B, C, *] or [B, *, C]. + + Returns: + x: Output tensor. Shape: [B, C', *] or [B, *, C']. + """ + x = x.movedim(1, -1) + x = super().forward(x) + x = x.movedim(-1, 1) + return x + + +class DepthToSpaceTime(nn.Module): + def __init__( + self, + temporal_expansion: int, + spatial_expansion: int, + ): + super().__init__() + self.temporal_expansion = temporal_expansion + self.spatial_expansion = spatial_expansion + + # When printed, this module should show the temporal and spatial expansion factors. + def extra_repr(self): + return f"texp={self.temporal_expansion}, sexp={self.spatial_expansion}" + + def forward(self, x: torch.Tensor): + """Forward pass. + + Args: + x: Input tensor. Shape: [B, C, T, H, W]. + + Returns: + x: Rearranged tensor. Shape: [B, C/(st*s*s), T*st, H*s, W*s]. + """ + x = rearrange( + x, + "B (C st sh sw) T H W -> B C (T st) (H sh) (W sw)", + st=self.temporal_expansion, + sh=self.spatial_expansion, + sw=self.spatial_expansion, + ) + + # cp_rank, _ = cp.get_cp_rank_size() + if self.temporal_expansion > 1: # and cp_rank == 0: + # Drop the first self.temporal_expansion - 1 frames. + # This is because we always want the 3x3x3 conv filter to only apply + # to the first frame, and the first frame doesn't need to be repeated. + assert all(x.shape) + x = x[:, :, self.temporal_expansion - 1 :] + assert all(x.shape) + + return x + + +def norm_fn( + in_channels: int, + affine: bool = True, +): + return GroupNormSpatial(affine=affine, num_groups=32, num_channels=in_channels) + + +class ResBlock(nn.Module): + """Residual block that preserves the spatial dimensions.""" + + def __init__( + self, + channels: int, + *, + affine: bool = True, + attn_block: Optional[nn.Module] = None, + causal: bool = True, + prune_bottleneck: bool = False, + padding_mode: str, + bias: bool = True, + ): + super().__init__() + self.channels = channels + + assert causal + self.stack = nn.Sequential( + norm_fn(channels, affine=affine), + nn.SiLU(inplace=True), + PConv3d( + in_channels=channels, + out_channels=channels // 2 if prune_bottleneck else channels, + kernel_size=(3, 3, 3), + stride=(1, 1, 1), + padding_mode=padding_mode, + bias=bias, + causal=causal, + ), + norm_fn(channels, affine=affine), + nn.SiLU(inplace=True), + PConv3d( + in_channels=channels // 2 if prune_bottleneck else channels, + out_channels=channels, + kernel_size=(3, 3, 3), + stride=(1, 1, 1), + padding_mode=padding_mode, + bias=bias, + causal=causal, + ), + ) + + self.attn_block = attn_block if attn_block else nn.Identity() + + def forward(self, x: torch.Tensor): + """Forward pass. + + Args: + x: Input tensor. Shape: [B, C, T, H, W]. + """ + residual = x + x = self.stack(x) + x = x + residual + del residual + + return self.attn_block(x) + + +class Attention(nn.Module): + def __init__( + self, + dim: int, + head_dim: int = 32, + qkv_bias: bool = False, + out_bias: bool = True, + qk_norm: bool = True, + ) -> None: + super().__init__() + self.head_dim = head_dim + self.num_heads = dim // head_dim + self.qk_norm = qk_norm + + self.qkv = nn.Linear(dim, 3 * dim, bias=qkv_bias) + self.out = nn.Linear(dim, dim, bias=out_bias) + + def forward( + self, + x: torch.Tensor, + ) -> torch.Tensor: + """Compute temporal self-attention. + + Args: + x: Input tensor. Shape: [B, C, T, H, W]. + chunk_size: Chunk size for large tensors. + + Returns: + x: Output tensor. Shape: [B, C, T, H, W]. + """ + B, _, T, H, W = x.shape + + if T == 1: + # No attention for single frame. + x = x.movedim(1, -1) # [B, C, T, H, W] -> [B, T, H, W, C] + qkv = self.qkv(x) + _, _, x = qkv.chunk(3, dim=-1) # Throw away queries and keys. + x = self.out(x) + return x.movedim(-1, 1) # [B, T, H, W, C] -> [B, C, T, H, W] + + # 1D temporal attention. + x = rearrange(x, "B C t h w -> (B h w) t C") + qkv = self.qkv(x) + + # Input: qkv with shape [B, t, 3 * num_heads * head_dim] + # Output: x with shape [B, num_heads, t, head_dim] + q, k, v = qkv.view(qkv.shape[0], qkv.shape[1], 3, self.num_heads, self.head_dim).transpose(1, 3).unbind(2) + + if self.qk_norm: + q = F.normalize(q, p=2, dim=-1) + k = F.normalize(k, p=2, dim=-1) + + x = optimized_attention(q, k, v, self.num_heads, skip_reshape=True) + + assert x.size(0) == q.size(0) + + x = self.out(x) + x = rearrange(x, "(B h w) t C -> B C t h w", B=B, h=H, w=W) + return x + + +class AttentionBlock(nn.Module): + def __init__( + self, + dim: int, + **attn_kwargs, + ) -> None: + super().__init__() + self.norm = norm_fn(dim) + self.attn = Attention(dim, **attn_kwargs) + + def forward(self, x: torch.Tensor) -> torch.Tensor: + return x + self.attn(self.norm(x)) + + +class CausalUpsampleBlock(nn.Module): + def __init__( + self, + in_channels: int, + out_channels: int, + num_res_blocks: int, + *, + temporal_expansion: int = 2, + spatial_expansion: int = 2, + **block_kwargs, + ): + super().__init__() + + blocks = [] + for _ in range(num_res_blocks): + blocks.append(block_fn(in_channels, **block_kwargs)) + self.blocks = nn.Sequential(*blocks) + + self.temporal_expansion = temporal_expansion + self.spatial_expansion = spatial_expansion + + # Change channels in the final convolution layer. + self.proj = Conv1x1( + in_channels, + out_channels * temporal_expansion * (spatial_expansion**2), + ) + + self.d2st = DepthToSpaceTime( + temporal_expansion=temporal_expansion, spatial_expansion=spatial_expansion + ) + + def forward(self, x): + x = self.blocks(x) + x = self.proj(x) + x = self.d2st(x) + return x + + +def block_fn(channels, *, affine: bool = True, has_attention: bool = False, **block_kwargs): + attn_block = AttentionBlock(channels) if has_attention else None + return ResBlock(channels, affine=affine, attn_block=attn_block, **block_kwargs) + + +class DownsampleBlock(nn.Module): + def __init__( + self, + in_channels: int, + out_channels: int, + num_res_blocks, + *, + temporal_reduction=2, + spatial_reduction=2, + **block_kwargs, + ): + """ + Downsample block for the VAE encoder. + + Args: + in_channels: Number of input channels. + out_channels: Number of output channels. + num_res_blocks: Number of residual blocks. + temporal_reduction: Temporal reduction factor. + spatial_reduction: Spatial reduction factor. + """ + super().__init__() + layers = [] + + # Change the channel count in the strided convolution. + # This lets the ResBlock have uniform channel count, + # as in ConvNeXt. + assert in_channels != out_channels + layers.append( + PConv3d( + in_channels=in_channels, + out_channels=out_channels, + kernel_size=(temporal_reduction, spatial_reduction, spatial_reduction), + stride=(temporal_reduction, spatial_reduction, spatial_reduction), + # First layer in each block always uses replicate padding + padding_mode="replicate", + bias=block_kwargs["bias"], + ) + ) + + for _ in range(num_res_blocks): + layers.append(block_fn(out_channels, **block_kwargs)) + + self.layers = nn.Sequential(*layers) + + def forward(self, x): + return self.layers(x) + + +def add_fourier_features(inputs: torch.Tensor, start=6, stop=8, step=1): + num_freqs = (stop - start) // step + assert inputs.ndim == 5 + C = inputs.size(1) + + # Create Base 2 Fourier features. + freqs = torch.arange(start, stop, step, dtype=inputs.dtype, device=inputs.device) + assert num_freqs == len(freqs) + w = torch.pow(2.0, freqs) * (2 * torch.pi) # [num_freqs] + C = inputs.shape[1] + w = w.repeat(C)[None, :, None, None, None] # [1, C * num_freqs, 1, 1, 1] + + # Interleaved repeat of input channels to match w. + h = inputs.repeat_interleave(num_freqs, dim=1) # [B, C * num_freqs, T, H, W] + # Scale channels by frequency. + h = w * h + + return torch.cat( + [ + inputs, + torch.sin(h), + torch.cos(h), + ], + dim=1, + ) + + +class FourierFeatures(nn.Module): + def __init__(self, start: int = 6, stop: int = 8, step: int = 1): + super().__init__() + self.start = start + self.stop = stop + self.step = step + + def forward(self, inputs): + """Add Fourier features to inputs. + + Args: + inputs: Input tensor. Shape: [B, C, T, H, W] + + Returns: + h: Output tensor. Shape: [B, (1 + 2 * num_freqs) * C, T, H, W] + """ + return add_fourier_features(inputs, self.start, self.stop, self.step) + + +class Decoder(nn.Module): + def __init__( + self, + *, + out_channels: int = 3, + latent_dim: int, + base_channels: int, + channel_multipliers: List[int], + num_res_blocks: List[int], + temporal_expansions: Optional[List[int]] = None, + spatial_expansions: Optional[List[int]] = None, + has_attention: List[bool], + output_norm: bool = True, + nonlinearity: str = "silu", + output_nonlinearity: str = "silu", + causal: bool = True, + **block_kwargs, + ): + super().__init__() + self.input_channels = latent_dim + self.base_channels = base_channels + self.channel_multipliers = channel_multipliers + self.num_res_blocks = num_res_blocks + self.output_nonlinearity = output_nonlinearity + assert nonlinearity == "silu" + assert causal + + ch = [mult * base_channels for mult in channel_multipliers] + self.num_up_blocks = len(ch) - 1 + assert len(num_res_blocks) == self.num_up_blocks + 2 + + blocks = [] + + first_block = [ + ops.Conv3d(latent_dim, ch[-1], kernel_size=(1, 1, 1)) + ] # Input layer. + # First set of blocks preserve channel count. + for _ in range(num_res_blocks[-1]): + first_block.append( + block_fn( + ch[-1], + has_attention=has_attention[-1], + causal=causal, + **block_kwargs, + ) + ) + blocks.append(nn.Sequential(*first_block)) + + assert len(temporal_expansions) == len(spatial_expansions) == self.num_up_blocks + assert len(num_res_blocks) == len(has_attention) == self.num_up_blocks + 2 + + upsample_block_fn = CausalUpsampleBlock + + for i in range(self.num_up_blocks): + block = upsample_block_fn( + ch[-i - 1], + ch[-i - 2], + num_res_blocks=num_res_blocks[-i - 2], + has_attention=has_attention[-i - 2], + temporal_expansion=temporal_expansions[-i - 1], + spatial_expansion=spatial_expansions[-i - 1], + causal=causal, + **block_kwargs, + ) + blocks.append(block) + + assert not output_norm + + # Last block. Preserve channel count. + last_block = [] + for _ in range(num_res_blocks[0]): + last_block.append( + block_fn( + ch[0], has_attention=has_attention[0], causal=causal, **block_kwargs + ) + ) + blocks.append(nn.Sequential(*last_block)) + + self.blocks = nn.ModuleList(blocks) + self.output_proj = Conv1x1(ch[0], out_channels) + + def forward(self, x): + """Forward pass. + + Args: + x: Latent tensor. Shape: [B, input_channels, t, h, w]. Scaled [-1, 1]. + + Returns: + x: Reconstructed video tensor. Shape: [B, C, T, H, W]. Scaled to [-1, 1]. + T + 1 = (t - 1) * 4. + H = h * 16, W = w * 16. + """ + for block in self.blocks: + x = block(x) + + if self.output_nonlinearity == "silu": + x = F.silu(x, inplace=not self.training) + else: + assert ( + not self.output_nonlinearity + ) # StyleGAN3 omits the to-RGB nonlinearity. + + return self.output_proj(x).contiguous() + +class LatentDistribution: + def __init__(self, mean: torch.Tensor, logvar: torch.Tensor): + """Initialize latent distribution. + + Args: + mean: Mean of the distribution. Shape: [B, C, T, H, W]. + logvar: Logarithm of variance of the distribution. Shape: [B, C, T, H, W]. + """ + assert mean.shape == logvar.shape + self.mean = mean + self.logvar = logvar + + def sample(self, temperature=1.0, generator: torch.Generator = None, noise=None): + if temperature == 0.0: + return self.mean + + if noise is None: + noise = torch.randn(self.mean.shape, device=self.mean.device, dtype=self.mean.dtype, generator=generator) + else: + assert noise.device == self.mean.device + noise = noise.to(self.mean.dtype) + + if temperature != 1.0: + raise NotImplementedError(f"Temperature {temperature} is not supported.") + + # Just Gaussian sample with no scaling of variance. + return noise * torch.exp(self.logvar * 0.5) + self.mean + + def mode(self): + return self.mean + +class Encoder(nn.Module): + def __init__( + self, + *, + in_channels: int, + base_channels: int, + channel_multipliers: List[int], + num_res_blocks: List[int], + latent_dim: int, + temporal_reductions: List[int], + spatial_reductions: List[int], + prune_bottlenecks: List[bool], + has_attentions: List[bool], + affine: bool = True, + bias: bool = True, + input_is_conv_1x1: bool = False, + padding_mode: str, + ): + super().__init__() + self.temporal_reductions = temporal_reductions + self.spatial_reductions = spatial_reductions + self.base_channels = base_channels + self.channel_multipliers = channel_multipliers + self.num_res_blocks = num_res_blocks + self.latent_dim = latent_dim + + self.fourier_features = FourierFeatures() + ch = [mult * base_channels for mult in channel_multipliers] + num_down_blocks = len(ch) - 1 + assert len(num_res_blocks) == num_down_blocks + 2 + + layers = ( + [ops.Conv3d(in_channels, ch[0], kernel_size=(1, 1, 1), bias=True)] + if not input_is_conv_1x1 + else [Conv1x1(in_channels, ch[0])] + ) + + assert len(prune_bottlenecks) == num_down_blocks + 2 + assert len(has_attentions) == num_down_blocks + 2 + block = partial(block_fn, padding_mode=padding_mode, affine=affine, bias=bias) + + for _ in range(num_res_blocks[0]): + layers.append(block(ch[0], has_attention=has_attentions[0], prune_bottleneck=prune_bottlenecks[0])) + prune_bottlenecks = prune_bottlenecks[1:] + has_attentions = has_attentions[1:] + + assert len(temporal_reductions) == len(spatial_reductions) == len(ch) - 1 + for i in range(num_down_blocks): + layer = DownsampleBlock( + ch[i], + ch[i + 1], + num_res_blocks=num_res_blocks[i + 1], + temporal_reduction=temporal_reductions[i], + spatial_reduction=spatial_reductions[i], + prune_bottleneck=prune_bottlenecks[i], + has_attention=has_attentions[i], + affine=affine, + bias=bias, + padding_mode=padding_mode, + ) + + layers.append(layer) + + # Additional blocks. + for _ in range(num_res_blocks[-1]): + layers.append(block(ch[-1], has_attention=has_attentions[-1], prune_bottleneck=prune_bottlenecks[-1])) + + self.layers = nn.Sequential(*layers) + + # Output layers. + self.output_norm = norm_fn(ch[-1]) + self.output_proj = Conv1x1(ch[-1], 2 * latent_dim, bias=False) + + @property + def temporal_downsample(self): + return math.prod(self.temporal_reductions) + + @property + def spatial_downsample(self): + return math.prod(self.spatial_reductions) + + def forward(self, x) -> LatentDistribution: + """Forward pass. + + Args: + x: Input video tensor. Shape: [B, C, T, H, W]. Scaled to [-1, 1] + + Returns: + means: Latent tensor. Shape: [B, latent_dim, t, h, w]. Scaled [-1, 1]. + h = H // 8, w = W // 8, t - 1 = (T - 1) // 6 + logvar: Shape: [B, latent_dim, t, h, w]. + """ + assert x.ndim == 5, f"Expected 5D input, got {x.shape}" + x = self.fourier_features(x) + + x = self.layers(x) + + x = self.output_norm(x) + x = F.silu(x, inplace=True) + x = self.output_proj(x) + + means, logvar = torch.chunk(x, 2, dim=1) + + assert means.ndim == 5 + assert logvar.shape == means.shape + assert means.size(1) == self.latent_dim + + return LatentDistribution(means, logvar) + + +class VideoVAE(nn.Module): + def __init__(self): + super().__init__() + self.encoder = Encoder( + in_channels=15, + base_channels=64, + channel_multipliers=[1, 2, 4, 6], + num_res_blocks=[3, 3, 4, 6, 3], + latent_dim=12, + temporal_reductions=[1, 2, 3], + spatial_reductions=[2, 2, 2], + prune_bottlenecks=[False, False, False, False, False], + has_attentions=[False, True, True, True, True], + affine=True, + bias=True, + input_is_conv_1x1=True, + padding_mode="replicate" + ) + self.decoder = Decoder( + out_channels=3, + base_channels=128, + channel_multipliers=[1, 2, 4, 6], + temporal_expansions=[1, 2, 3], + spatial_expansions=[2, 2, 2], + num_res_blocks=[3, 3, 4, 6, 3], + latent_dim=12, + has_attention=[False, False, False, False, False], + padding_mode="replicate", + output_norm=False, + nonlinearity="silu", + output_nonlinearity="silu", + causal=True, + ) + + def encode(self, x): + return self.encoder(x).mode() + + def decode(self, x): + return self.decoder(x) diff --git a/ComfyUI/comfy/ldm/lightricks/vae/causal_video_autoencoder.py b/ComfyUI/comfy/ldm/lightricks/vae/causal_video_autoencoder.py new file mode 100644 index 0000000000000000000000000000000000000000..75ed069ad45dc2f915f0c749f1afc3536d292e33 --- /dev/null +++ b/ComfyUI/comfy/ldm/lightricks/vae/causal_video_autoencoder.py @@ -0,0 +1,1092 @@ +from __future__ import annotations +import torch +from torch import nn +from functools import partial +import math +from einops import rearrange +from typing import List, Optional, Tuple, Union +from .conv_nd_factory import make_conv_nd, make_linear_nd +from .pixel_norm import PixelNorm +from ..model import PixArtAlphaCombinedTimestepSizeEmbeddings +import comfy.ops + +ops = comfy.ops.disable_weight_init + +class Encoder(nn.Module): + r""" + The `Encoder` layer of a variational autoencoder that encodes its input into a latent representation. + + Args: + dims (`int` or `Tuple[int, int]`, *optional*, defaults to 3): + The number of dimensions to use in convolutions. + in_channels (`int`, *optional*, defaults to 3): + The number of input channels. + out_channels (`int`, *optional*, defaults to 3): + The number of output channels. + blocks (`List[Tuple[str, int]]`, *optional*, defaults to `[("res_x", 1)]`): + The blocks to use. Each block is a tuple of the block name and the number of layers. + base_channels (`int`, *optional*, defaults to 128): + The number of output channels for the first convolutional layer. + norm_num_groups (`int`, *optional*, defaults to 32): + The number of groups for normalization. + patch_size (`int`, *optional*, defaults to 1): + The patch size to use. Should be a power of 2. + norm_layer (`str`, *optional*, defaults to `group_norm`): + The normalization layer to use. Can be either `group_norm` or `pixel_norm`. + latent_log_var (`str`, *optional*, defaults to `per_channel`): + The number of channels for the log variance. Can be either `per_channel`, `uniform`, `constant` or `none`. + """ + + def __init__( + self, + dims: Union[int, Tuple[int, int]] = 3, + in_channels: int = 3, + out_channels: int = 3, + blocks: List[Tuple[str, int | dict]] = [("res_x", 1)], + base_channels: int = 128, + norm_num_groups: int = 32, + patch_size: Union[int, Tuple[int]] = 1, + norm_layer: str = "group_norm", # group_norm, pixel_norm + latent_log_var: str = "per_channel", + spatial_padding_mode: str = "zeros", + ): + super().__init__() + self.patch_size = patch_size + self.norm_layer = norm_layer + self.latent_channels = out_channels + self.latent_log_var = latent_log_var + self.blocks_desc = blocks + + in_channels = in_channels * patch_size**2 + output_channel = base_channels + + self.conv_in = make_conv_nd( + dims=dims, + in_channels=in_channels, + out_channels=output_channel, + kernel_size=3, + stride=1, + padding=1, + causal=True, + spatial_padding_mode=spatial_padding_mode, + ) + + self.down_blocks = nn.ModuleList([]) + + for block_name, block_params in blocks: + input_channel = output_channel + if isinstance(block_params, int): + block_params = {"num_layers": block_params} + + if block_name == "res_x": + block = UNetMidBlock3D( + dims=dims, + in_channels=input_channel, + num_layers=block_params["num_layers"], + resnet_eps=1e-6, + resnet_groups=norm_num_groups, + norm_layer=norm_layer, + spatial_padding_mode=spatial_padding_mode, + ) + elif block_name == "res_x_y": + output_channel = block_params.get("multiplier", 2) * output_channel + block = ResnetBlock3D( + dims=dims, + in_channels=input_channel, + out_channels=output_channel, + eps=1e-6, + groups=norm_num_groups, + norm_layer=norm_layer, + spatial_padding_mode=spatial_padding_mode, + ) + elif block_name == "compress_time": + block = make_conv_nd( + dims=dims, + in_channels=input_channel, + out_channels=output_channel, + kernel_size=3, + stride=(2, 1, 1), + causal=True, + spatial_padding_mode=spatial_padding_mode, + ) + elif block_name == "compress_space": + block = make_conv_nd( + dims=dims, + in_channels=input_channel, + out_channels=output_channel, + kernel_size=3, + stride=(1, 2, 2), + causal=True, + spatial_padding_mode=spatial_padding_mode, + ) + elif block_name == "compress_all": + block = make_conv_nd( + dims=dims, + in_channels=input_channel, + out_channels=output_channel, + kernel_size=3, + stride=(2, 2, 2), + causal=True, + spatial_padding_mode=spatial_padding_mode, + ) + elif block_name == "compress_all_x_y": + output_channel = block_params.get("multiplier", 2) * output_channel + block = make_conv_nd( + dims=dims, + in_channels=input_channel, + out_channels=output_channel, + kernel_size=3, + stride=(2, 2, 2), + causal=True, + spatial_padding_mode=spatial_padding_mode, + ) + elif block_name == "compress_all_res": + output_channel = block_params.get("multiplier", 2) * output_channel + block = SpaceToDepthDownsample( + dims=dims, + in_channels=input_channel, + out_channels=output_channel, + stride=(2, 2, 2), + spatial_padding_mode=spatial_padding_mode, + ) + elif block_name == "compress_space_res": + output_channel = block_params.get("multiplier", 2) * output_channel + block = SpaceToDepthDownsample( + dims=dims, + in_channels=input_channel, + out_channels=output_channel, + stride=(1, 2, 2), + spatial_padding_mode=spatial_padding_mode, + ) + elif block_name == "compress_time_res": + output_channel = block_params.get("multiplier", 2) * output_channel + block = SpaceToDepthDownsample( + dims=dims, + in_channels=input_channel, + out_channels=output_channel, + stride=(2, 1, 1), + spatial_padding_mode=spatial_padding_mode, + ) + else: + raise ValueError(f"unknown block: {block_name}") + + self.down_blocks.append(block) + + # out + if norm_layer == "group_norm": + self.conv_norm_out = nn.GroupNorm( + num_channels=output_channel, num_groups=norm_num_groups, eps=1e-6 + ) + elif norm_layer == "pixel_norm": + self.conv_norm_out = PixelNorm() + elif norm_layer == "layer_norm": + self.conv_norm_out = LayerNorm(output_channel, eps=1e-6) + + self.conv_act = nn.SiLU() + + conv_out_channels = out_channels + if latent_log_var == "per_channel": + conv_out_channels *= 2 + elif latent_log_var == "uniform": + conv_out_channels += 1 + elif latent_log_var == "constant": + conv_out_channels += 1 + elif latent_log_var != "none": + raise ValueError(f"Invalid latent_log_var: {latent_log_var}") + self.conv_out = make_conv_nd( + dims, + output_channel, + conv_out_channels, + 3, + padding=1, + causal=True, + spatial_padding_mode=spatial_padding_mode, + ) + + self.gradient_checkpointing = False + + def forward(self, sample: torch.FloatTensor) -> torch.FloatTensor: + r"""The forward method of the `Encoder` class.""" + + sample = patchify(sample, patch_size_hw=self.patch_size, patch_size_t=1) + sample = self.conv_in(sample) + + checkpoint_fn = ( + partial(torch.utils.checkpoint.checkpoint, use_reentrant=False) + if self.gradient_checkpointing and self.training + else lambda x: x + ) + + for down_block in self.down_blocks: + sample = checkpoint_fn(down_block)(sample) + + sample = self.conv_norm_out(sample) + sample = self.conv_act(sample) + sample = self.conv_out(sample) + + if self.latent_log_var == "uniform": + last_channel = sample[:, -1:, ...] + num_dims = sample.dim() + + if num_dims == 4: + # For shape (B, C, H, W) + repeated_last_channel = last_channel.repeat( + 1, sample.shape[1] - 2, 1, 1 + ) + sample = torch.cat([sample, repeated_last_channel], dim=1) + elif num_dims == 5: + # For shape (B, C, F, H, W) + repeated_last_channel = last_channel.repeat( + 1, sample.shape[1] - 2, 1, 1, 1 + ) + sample = torch.cat([sample, repeated_last_channel], dim=1) + else: + raise ValueError(f"Invalid input shape: {sample.shape}") + elif self.latent_log_var == "constant": + sample = sample[:, :-1, ...] + approx_ln_0 = ( + -30 + ) # this is the minimal clamp value in DiagonalGaussianDistribution objects + sample = torch.cat( + [sample, torch.ones_like(sample, device=sample.device) * approx_ln_0], + dim=1, + ) + + return sample + + +class Decoder(nn.Module): + r""" + The `Decoder` layer of a variational autoencoder that decodes its latent representation into an output sample. + + Args: + dims (`int` or `Tuple[int, int]`, *optional*, defaults to 3): + The number of dimensions to use in convolutions. + in_channels (`int`, *optional*, defaults to 3): + The number of input channels. + out_channels (`int`, *optional*, defaults to 3): + The number of output channels. + blocks (`List[Tuple[str, int]]`, *optional*, defaults to `[("res_x", 1)]`): + The blocks to use. Each block is a tuple of the block name and the number of layers. + base_channels (`int`, *optional*, defaults to 128): + The number of output channels for the first convolutional layer. + norm_num_groups (`int`, *optional*, defaults to 32): + The number of groups for normalization. + patch_size (`int`, *optional*, defaults to 1): + The patch size to use. Should be a power of 2. + norm_layer (`str`, *optional*, defaults to `group_norm`): + The normalization layer to use. Can be either `group_norm` or `pixel_norm`. + causal (`bool`, *optional*, defaults to `True`): + Whether to use causal convolutions or not. + """ + + def __init__( + self, + dims, + in_channels: int = 3, + out_channels: int = 3, + blocks: List[Tuple[str, int | dict]] = [("res_x", 1)], + base_channels: int = 128, + layers_per_block: int = 2, + norm_num_groups: int = 32, + patch_size: int = 1, + norm_layer: str = "group_norm", + causal: bool = True, + timestep_conditioning: bool = False, + spatial_padding_mode: str = "zeros", + ): + super().__init__() + self.patch_size = patch_size + self.layers_per_block = layers_per_block + out_channels = out_channels * patch_size**2 + self.causal = causal + self.blocks_desc = blocks + + # Compute output channel to be product of all channel-multiplier blocks + output_channel = base_channels + for block_name, block_params in list(reversed(blocks)): + block_params = block_params if isinstance(block_params, dict) else {} + if block_name == "res_x_y": + output_channel = output_channel * block_params.get("multiplier", 2) + if block_name == "compress_all": + output_channel = output_channel * block_params.get("multiplier", 1) + + self.conv_in = make_conv_nd( + dims, + in_channels, + output_channel, + kernel_size=3, + stride=1, + padding=1, + causal=True, + spatial_padding_mode=spatial_padding_mode, + ) + + self.up_blocks = nn.ModuleList([]) + + for block_name, block_params in list(reversed(blocks)): + input_channel = output_channel + if isinstance(block_params, int): + block_params = {"num_layers": block_params} + + if block_name == "res_x": + block = UNetMidBlock3D( + dims=dims, + in_channels=input_channel, + num_layers=block_params["num_layers"], + resnet_eps=1e-6, + resnet_groups=norm_num_groups, + norm_layer=norm_layer, + inject_noise=block_params.get("inject_noise", False), + timestep_conditioning=timestep_conditioning, + spatial_padding_mode=spatial_padding_mode, + ) + elif block_name == "attn_res_x": + block = UNetMidBlock3D( + dims=dims, + in_channels=input_channel, + num_layers=block_params["num_layers"], + resnet_groups=norm_num_groups, + norm_layer=norm_layer, + inject_noise=block_params.get("inject_noise", False), + timestep_conditioning=timestep_conditioning, + attention_head_dim=block_params["attention_head_dim"], + spatial_padding_mode=spatial_padding_mode, + ) + elif block_name == "res_x_y": + output_channel = output_channel // block_params.get("multiplier", 2) + block = ResnetBlock3D( + dims=dims, + in_channels=input_channel, + out_channels=output_channel, + eps=1e-6, + groups=norm_num_groups, + norm_layer=norm_layer, + inject_noise=block_params.get("inject_noise", False), + timestep_conditioning=False, + spatial_padding_mode=spatial_padding_mode, + ) + elif block_name == "compress_time": + block = DepthToSpaceUpsample( + dims=dims, + in_channels=input_channel, + stride=(2, 1, 1), + spatial_padding_mode=spatial_padding_mode, + ) + elif block_name == "compress_space": + block = DepthToSpaceUpsample( + dims=dims, + in_channels=input_channel, + stride=(1, 2, 2), + spatial_padding_mode=spatial_padding_mode, + ) + elif block_name == "compress_all": + output_channel = output_channel // block_params.get("multiplier", 1) + block = DepthToSpaceUpsample( + dims=dims, + in_channels=input_channel, + stride=(2, 2, 2), + residual=block_params.get("residual", False), + out_channels_reduction_factor=block_params.get("multiplier", 1), + spatial_padding_mode=spatial_padding_mode, + ) + else: + raise ValueError(f"unknown layer: {block_name}") + + self.up_blocks.append(block) + + if norm_layer == "group_norm": + self.conv_norm_out = nn.GroupNorm( + num_channels=output_channel, num_groups=norm_num_groups, eps=1e-6 + ) + elif norm_layer == "pixel_norm": + self.conv_norm_out = PixelNorm() + elif norm_layer == "layer_norm": + self.conv_norm_out = LayerNorm(output_channel, eps=1e-6) + + self.conv_act = nn.SiLU() + self.conv_out = make_conv_nd( + dims, + output_channel, + out_channels, + 3, + padding=1, + causal=True, + spatial_padding_mode=spatial_padding_mode, + ) + + self.gradient_checkpointing = False + + self.timestep_conditioning = timestep_conditioning + + if timestep_conditioning: + self.timestep_scale_multiplier = nn.Parameter( + torch.tensor(1000.0, dtype=torch.float32) + ) + self.last_time_embedder = PixArtAlphaCombinedTimestepSizeEmbeddings( + output_channel * 2, 0, operations=ops, + ) + self.last_scale_shift_table = nn.Parameter(torch.empty(2, output_channel)) + + # def forward(self, sample: torch.FloatTensor, target_shape) -> torch.FloatTensor: + def forward( + self, + sample: torch.FloatTensor, + timestep: Optional[torch.Tensor] = None, + ) -> torch.FloatTensor: + r"""The forward method of the `Decoder` class.""" + batch_size = sample.shape[0] + + sample = self.conv_in(sample, causal=self.causal) + + checkpoint_fn = ( + partial(torch.utils.checkpoint.checkpoint, use_reentrant=False) + if self.gradient_checkpointing and self.training + else lambda x: x + ) + + scaled_timestep = None + if self.timestep_conditioning: + assert ( + timestep is not None + ), "should pass timestep with timestep_conditioning=True" + scaled_timestep = timestep * self.timestep_scale_multiplier.to(dtype=sample.dtype, device=sample.device) + + for up_block in self.up_blocks: + if self.timestep_conditioning and isinstance(up_block, UNetMidBlock3D): + sample = checkpoint_fn(up_block)( + sample, causal=self.causal, timestep=scaled_timestep + ) + else: + sample = checkpoint_fn(up_block)(sample, causal=self.causal) + + sample = self.conv_norm_out(sample) + + if self.timestep_conditioning: + embedded_timestep = self.last_time_embedder( + timestep=scaled_timestep.flatten(), + resolution=None, + aspect_ratio=None, + batch_size=sample.shape[0], + hidden_dtype=sample.dtype, + ) + embedded_timestep = embedded_timestep.view( + batch_size, embedded_timestep.shape[-1], 1, 1, 1 + ) + ada_values = self.last_scale_shift_table[ + None, ..., None, None, None + ].to(device=sample.device, dtype=sample.dtype) + embedded_timestep.reshape( + batch_size, + 2, + -1, + embedded_timestep.shape[-3], + embedded_timestep.shape[-2], + embedded_timestep.shape[-1], + ) + shift, scale = ada_values.unbind(dim=1) + sample = sample * (1 + scale) + shift + + sample = self.conv_act(sample) + sample = self.conv_out(sample, causal=self.causal) + + sample = unpatchify(sample, patch_size_hw=self.patch_size, patch_size_t=1) + + return sample + + +class UNetMidBlock3D(nn.Module): + """ + A 3D UNet mid-block [`UNetMidBlock3D`] with multiple residual blocks. + + Args: + in_channels (`int`): The number of input channels. + dropout (`float`, *optional*, defaults to 0.0): The dropout rate. + num_layers (`int`, *optional*, defaults to 1): The number of residual blocks. + resnet_eps (`float`, *optional*, 1e-6 ): The epsilon value for the resnet blocks. + resnet_groups (`int`, *optional*, defaults to 32): + The number of groups to use in the group normalization layers of the resnet blocks. + norm_layer (`str`, *optional*, defaults to `group_norm`): + The normalization layer to use. Can be either `group_norm` or `pixel_norm`. + inject_noise (`bool`, *optional*, defaults to `False`): + Whether to inject noise into the hidden states. + timestep_conditioning (`bool`, *optional*, defaults to `False`): + Whether to condition the hidden states on the timestep. + + Returns: + `torch.FloatTensor`: The output of the last residual block, which is a tensor of shape `(batch_size, + in_channels, height, width)`. + + """ + + def __init__( + self, + dims: Union[int, Tuple[int, int]], + in_channels: int, + dropout: float = 0.0, + num_layers: int = 1, + resnet_eps: float = 1e-6, + resnet_groups: int = 32, + norm_layer: str = "group_norm", + inject_noise: bool = False, + timestep_conditioning: bool = False, + spatial_padding_mode: str = "zeros", + ): + super().__init__() + resnet_groups = ( + resnet_groups if resnet_groups is not None else min(in_channels // 4, 32) + ) + + self.timestep_conditioning = timestep_conditioning + + if timestep_conditioning: + self.time_embedder = PixArtAlphaCombinedTimestepSizeEmbeddings( + in_channels * 4, 0, operations=ops, + ) + + self.res_blocks = nn.ModuleList( + [ + ResnetBlock3D( + dims=dims, + in_channels=in_channels, + out_channels=in_channels, + eps=resnet_eps, + groups=resnet_groups, + dropout=dropout, + norm_layer=norm_layer, + inject_noise=inject_noise, + timestep_conditioning=timestep_conditioning, + spatial_padding_mode=spatial_padding_mode, + ) + for _ in range(num_layers) + ] + ) + + def forward( + self, + hidden_states: torch.FloatTensor, + causal: bool = True, + timestep: Optional[torch.Tensor] = None, + ) -> torch.FloatTensor: + timestep_embed = None + if self.timestep_conditioning: + assert ( + timestep is not None + ), "should pass timestep with timestep_conditioning=True" + batch_size = hidden_states.shape[0] + timestep_embed = self.time_embedder( + timestep=timestep.flatten(), + resolution=None, + aspect_ratio=None, + batch_size=batch_size, + hidden_dtype=hidden_states.dtype, + ) + timestep_embed = timestep_embed.view( + batch_size, timestep_embed.shape[-1], 1, 1, 1 + ) + + for resnet in self.res_blocks: + hidden_states = resnet(hidden_states, causal=causal, timestep=timestep_embed) + + return hidden_states + + +class SpaceToDepthDownsample(nn.Module): + def __init__(self, dims, in_channels, out_channels, stride, spatial_padding_mode): + super().__init__() + self.stride = stride + self.group_size = in_channels * math.prod(stride) // out_channels + self.conv = make_conv_nd( + dims=dims, + in_channels=in_channels, + out_channels=out_channels // math.prod(stride), + kernel_size=3, + stride=1, + causal=True, + spatial_padding_mode=spatial_padding_mode, + ) + + def forward(self, x, causal: bool = True): + if self.stride[0] == 2: + x = torch.cat( + [x[:, :, :1, :, :], x], dim=2 + ) # duplicate first frames for padding + + # skip connection + x_in = rearrange( + x, + "b c (d p1) (h p2) (w p3) -> b (c p1 p2 p3) d h w", + p1=self.stride[0], + p2=self.stride[1], + p3=self.stride[2], + ) + x_in = rearrange(x_in, "b (c g) d h w -> b c g d h w", g=self.group_size) + x_in = x_in.mean(dim=2) + + # conv + x = self.conv(x, causal=causal) + x = rearrange( + x, + "b c (d p1) (h p2) (w p3) -> b (c p1 p2 p3) d h w", + p1=self.stride[0], + p2=self.stride[1], + p3=self.stride[2], + ) + + x = x + x_in + + return x + + +class DepthToSpaceUpsample(nn.Module): + def __init__( + self, + dims, + in_channels, + stride, + residual=False, + out_channels_reduction_factor=1, + spatial_padding_mode="zeros", + ): + super().__init__() + self.stride = stride + self.out_channels = ( + math.prod(stride) * in_channels // out_channels_reduction_factor + ) + self.conv = make_conv_nd( + dims=dims, + in_channels=in_channels, + out_channels=self.out_channels, + kernel_size=3, + stride=1, + causal=True, + spatial_padding_mode=spatial_padding_mode, + ) + self.residual = residual + self.out_channels_reduction_factor = out_channels_reduction_factor + + def forward(self, x, causal: bool = True, timestep: Optional[torch.Tensor] = None): + if self.residual: + # Reshape and duplicate the input to match the output shape + x_in = rearrange( + x, + "b (c p1 p2 p3) d h w -> b c (d p1) (h p2) (w p3)", + p1=self.stride[0], + p2=self.stride[1], + p3=self.stride[2], + ) + num_repeat = math.prod(self.stride) // self.out_channels_reduction_factor + x_in = x_in.repeat(1, num_repeat, 1, 1, 1) + if self.stride[0] == 2: + x_in = x_in[:, :, 1:, :, :] + x = self.conv(x, causal=causal) + x = rearrange( + x, + "b (c p1 p2 p3) d h w -> b c (d p1) (h p2) (w p3)", + p1=self.stride[0], + p2=self.stride[1], + p3=self.stride[2], + ) + if self.stride[0] == 2: + x = x[:, :, 1:, :, :] + if self.residual: + x = x + x_in + return x + +class LayerNorm(nn.Module): + def __init__(self, dim, eps, elementwise_affine=True) -> None: + super().__init__() + self.norm = ops.LayerNorm(dim, eps=eps, elementwise_affine=elementwise_affine) + + def forward(self, x): + x = rearrange(x, "b c d h w -> b d h w c") + x = self.norm(x) + x = rearrange(x, "b d h w c -> b c d h w") + return x + + +class ResnetBlock3D(nn.Module): + r""" + A Resnet block. + + Parameters: + in_channels (`int`): The number of channels in the input. + out_channels (`int`, *optional*, default to be `None`): + The number of output channels for the first conv layer. If None, same as `in_channels`. + dropout (`float`, *optional*, defaults to `0.0`): The dropout probability to use. + groups (`int`, *optional*, default to `32`): The number of groups to use for the first normalization layer. + eps (`float`, *optional*, defaults to `1e-6`): The epsilon to use for the normalization. + """ + + def __init__( + self, + dims: Union[int, Tuple[int, int]], + in_channels: int, + out_channels: Optional[int] = None, + dropout: float = 0.0, + groups: int = 32, + eps: float = 1e-6, + norm_layer: str = "group_norm", + inject_noise: bool = False, + timestep_conditioning: bool = False, + spatial_padding_mode: str = "zeros", + ): + super().__init__() + self.in_channels = in_channels + out_channels = in_channels if out_channels is None else out_channels + self.out_channels = out_channels + self.inject_noise = inject_noise + + if norm_layer == "group_norm": + self.norm1 = nn.GroupNorm( + num_groups=groups, num_channels=in_channels, eps=eps, affine=True + ) + elif norm_layer == "pixel_norm": + self.norm1 = PixelNorm() + elif norm_layer == "layer_norm": + self.norm1 = LayerNorm(in_channels, eps=eps, elementwise_affine=True) + + self.non_linearity = nn.SiLU() + + self.conv1 = make_conv_nd( + dims, + in_channels, + out_channels, + kernel_size=3, + stride=1, + padding=1, + causal=True, + spatial_padding_mode=spatial_padding_mode, + ) + + if inject_noise: + self.per_channel_scale1 = nn.Parameter(torch.zeros((in_channels, 1, 1))) + + if norm_layer == "group_norm": + self.norm2 = nn.GroupNorm( + num_groups=groups, num_channels=out_channels, eps=eps, affine=True + ) + elif norm_layer == "pixel_norm": + self.norm2 = PixelNorm() + elif norm_layer == "layer_norm": + self.norm2 = LayerNorm(out_channels, eps=eps, elementwise_affine=True) + + self.dropout = torch.nn.Dropout(dropout) + + self.conv2 = make_conv_nd( + dims, + out_channels, + out_channels, + kernel_size=3, + stride=1, + padding=1, + causal=True, + spatial_padding_mode=spatial_padding_mode, + ) + + if inject_noise: + self.per_channel_scale2 = nn.Parameter(torch.zeros((in_channels, 1, 1))) + + self.conv_shortcut = ( + make_linear_nd( + dims=dims, in_channels=in_channels, out_channels=out_channels + ) + if in_channels != out_channels + else nn.Identity() + ) + + self.norm3 = ( + LayerNorm(in_channels, eps=eps, elementwise_affine=True) + if in_channels != out_channels + else nn.Identity() + ) + + self.timestep_conditioning = timestep_conditioning + + if timestep_conditioning: + self.scale_shift_table = nn.Parameter( + torch.randn(4, in_channels) / in_channels**0.5 + ) + + def _feed_spatial_noise( + self, hidden_states: torch.FloatTensor, per_channel_scale: torch.FloatTensor + ) -> torch.FloatTensor: + spatial_shape = hidden_states.shape[-2:] + device = hidden_states.device + dtype = hidden_states.dtype + + # similar to the "explicit noise inputs" method in style-gan + spatial_noise = torch.randn(spatial_shape, device=device, dtype=dtype)[None] + scaled_noise = (spatial_noise * per_channel_scale)[None, :, None, ...] + hidden_states = hidden_states + scaled_noise + + return hidden_states + + def forward( + self, + input_tensor: torch.FloatTensor, + causal: bool = True, + timestep: Optional[torch.Tensor] = None, + ) -> torch.FloatTensor: + hidden_states = input_tensor + batch_size = hidden_states.shape[0] + + hidden_states = self.norm1(hidden_states) + if self.timestep_conditioning: + assert ( + timestep is not None + ), "should pass timestep with timestep_conditioning=True" + ada_values = self.scale_shift_table[ + None, ..., None, None, None + ].to(device=hidden_states.device, dtype=hidden_states.dtype) + timestep.reshape( + batch_size, + 4, + -1, + timestep.shape[-3], + timestep.shape[-2], + timestep.shape[-1], + ) + shift1, scale1, shift2, scale2 = ada_values.unbind(dim=1) + + hidden_states = hidden_states * (1 + scale1) + shift1 + + hidden_states = self.non_linearity(hidden_states) + + hidden_states = self.conv1(hidden_states, causal=causal) + + if self.inject_noise: + hidden_states = self._feed_spatial_noise( + hidden_states, self.per_channel_scale1.to(device=hidden_states.device, dtype=hidden_states.dtype) + ) + + hidden_states = self.norm2(hidden_states) + + if self.timestep_conditioning: + hidden_states = hidden_states * (1 + scale2) + shift2 + + hidden_states = self.non_linearity(hidden_states) + + hidden_states = self.dropout(hidden_states) + + hidden_states = self.conv2(hidden_states, causal=causal) + + if self.inject_noise: + hidden_states = self._feed_spatial_noise( + hidden_states, self.per_channel_scale2.to(device=hidden_states.device, dtype=hidden_states.dtype) + ) + + input_tensor = self.norm3(input_tensor) + + batch_size = input_tensor.shape[0] + + input_tensor = self.conv_shortcut(input_tensor) + + output_tensor = input_tensor + hidden_states + + return output_tensor + + +def patchify(x, patch_size_hw, patch_size_t=1): + if patch_size_hw == 1 and patch_size_t == 1: + return x + if x.dim() == 4: + x = rearrange( + x, "b c (h q) (w r) -> b (c r q) h w", q=patch_size_hw, r=patch_size_hw + ) + elif x.dim() == 5: + x = rearrange( + x, + "b c (f p) (h q) (w r) -> b (c p r q) f h w", + p=patch_size_t, + q=patch_size_hw, + r=patch_size_hw, + ) + else: + raise ValueError(f"Invalid input shape: {x.shape}") + + return x + + +def unpatchify(x, patch_size_hw, patch_size_t=1): + if patch_size_hw == 1 and patch_size_t == 1: + return x + + if x.dim() == 4: + x = rearrange( + x, "b (c r q) h w -> b c (h q) (w r)", q=patch_size_hw, r=patch_size_hw + ) + elif x.dim() == 5: + x = rearrange( + x, + "b (c p r q) f h w -> b c (f p) (h q) (w r)", + p=patch_size_t, + q=patch_size_hw, + r=patch_size_hw, + ) + + return x + +class processor(nn.Module): + def __init__(self): + super().__init__() + self.register_buffer("std-of-means", torch.empty(128)) + self.register_buffer("mean-of-means", torch.empty(128)) + self.register_buffer("mean-of-stds", torch.empty(128)) + self.register_buffer("mean-of-stds_over_std-of-means", torch.empty(128)) + self.register_buffer("channel", torch.empty(128)) + + def un_normalize(self, x): + return (x * self.get_buffer("std-of-means").view(1, -1, 1, 1, 1).to(x)) + self.get_buffer("mean-of-means").view(1, -1, 1, 1, 1).to(x) + + def normalize(self, x): + return (x - self.get_buffer("mean-of-means").view(1, -1, 1, 1, 1).to(x)) / self.get_buffer("std-of-means").view(1, -1, 1, 1, 1).to(x) + +class VideoVAE(nn.Module): + def __init__(self, version=0, config=None): + super().__init__() + + if config is None: + config = self.guess_config(version) + + self.timestep_conditioning = config.get("timestep_conditioning", False) + double_z = config.get("double_z", True) + latent_log_var = config.get( + "latent_log_var", "per_channel" if double_z else "none" + ) + + self.encoder = Encoder( + dims=config["dims"], + in_channels=config.get("in_channels", 3), + out_channels=config["latent_channels"], + blocks=config.get("encoder_blocks", config.get("encoder_blocks", config.get("blocks"))), + patch_size=config.get("patch_size", 1), + latent_log_var=latent_log_var, + norm_layer=config.get("norm_layer", "group_norm"), + spatial_padding_mode=config.get("spatial_padding_mode", "zeros"), + ) + + self.decoder = Decoder( + dims=config["dims"], + in_channels=config["latent_channels"], + out_channels=config.get("out_channels", 3), + blocks=config.get("decoder_blocks", config.get("decoder_blocks", config.get("blocks"))), + patch_size=config.get("patch_size", 1), + norm_layer=config.get("norm_layer", "group_norm"), + causal=config.get("causal_decoder", False), + timestep_conditioning=self.timestep_conditioning, + spatial_padding_mode=config.get("spatial_padding_mode", "reflect"), + ) + + self.per_channel_statistics = processor() + + def guess_config(self, version): + if version == 0: + config = { + "_class_name": "CausalVideoAutoencoder", + "dims": 3, + "in_channels": 3, + "out_channels": 3, + "latent_channels": 128, + "blocks": [ + ["res_x", 4], + ["compress_all", 1], + ["res_x_y", 1], + ["res_x", 3], + ["compress_all", 1], + ["res_x_y", 1], + ["res_x", 3], + ["compress_all", 1], + ["res_x", 3], + ["res_x", 4], + ], + "scaling_factor": 1.0, + "norm_layer": "pixel_norm", + "patch_size": 4, + "latent_log_var": "uniform", + "use_quant_conv": False, + "causal_decoder": False, + } + elif version == 1: + config = { + "_class_name": "CausalVideoAutoencoder", + "dims": 3, + "in_channels": 3, + "out_channels": 3, + "latent_channels": 128, + "decoder_blocks": [ + ["res_x", {"num_layers": 5, "inject_noise": True}], + ["compress_all", {"residual": True, "multiplier": 2}], + ["res_x", {"num_layers": 6, "inject_noise": True}], + ["compress_all", {"residual": True, "multiplier": 2}], + ["res_x", {"num_layers": 7, "inject_noise": True}], + ["compress_all", {"residual": True, "multiplier": 2}], + ["res_x", {"num_layers": 8, "inject_noise": False}] + ], + "encoder_blocks": [ + ["res_x", {"num_layers": 4}], + ["compress_all", {}], + ["res_x_y", 1], + ["res_x", {"num_layers": 3}], + ["compress_all", {}], + ["res_x_y", 1], + ["res_x", {"num_layers": 3}], + ["compress_all", {}], + ["res_x", {"num_layers": 3}], + ["res_x", {"num_layers": 4}] + ], + "scaling_factor": 1.0, + "norm_layer": "pixel_norm", + "patch_size": 4, + "latent_log_var": "uniform", + "use_quant_conv": False, + "causal_decoder": False, + "timestep_conditioning": True, + } + else: + config = { + "_class_name": "CausalVideoAutoencoder", + "dims": 3, + "in_channels": 3, + "out_channels": 3, + "latent_channels": 128, + "encoder_blocks": [ + ["res_x", {"num_layers": 4}], + ["compress_space_res", {"multiplier": 2}], + ["res_x", {"num_layers": 6}], + ["compress_time_res", {"multiplier": 2}], + ["res_x", {"num_layers": 6}], + ["compress_all_res", {"multiplier": 2}], + ["res_x", {"num_layers": 2}], + ["compress_all_res", {"multiplier": 2}], + ["res_x", {"num_layers": 2}] + ], + "decoder_blocks": [ + ["res_x", {"num_layers": 5, "inject_noise": False}], + ["compress_all", {"residual": True, "multiplier": 2}], + ["res_x", {"num_layers": 5, "inject_noise": False}], + ["compress_all", {"residual": True, "multiplier": 2}], + ["res_x", {"num_layers": 5, "inject_noise": False}], + ["compress_all", {"residual": True, "multiplier": 2}], + ["res_x", {"num_layers": 5, "inject_noise": False}] + ], + "scaling_factor": 1.0, + "norm_layer": "pixel_norm", + "patch_size": 4, + "latent_log_var": "uniform", + "use_quant_conv": False, + "causal_decoder": False, + "timestep_conditioning": True + } + return config + + def encode(self, x): + frames_count = x.shape[2] + if ((frames_count - 1) % 8) != 0: + raise ValueError("Invalid number of frames: Encode input must have 1 + 8 * x frames (e.g., 1, 9, 17, ...). Please check your input.") + means, logvar = torch.chunk(self.encoder(x), 2, dim=1) + return self.per_channel_statistics.normalize(means) + + def decode(self, x, timestep=0.05, noise_scale=0.025): + if self.timestep_conditioning: #TODO: seed + x = torch.randn_like(x) * noise_scale + (1.0 - noise_scale) * x + return self.decoder(self.per_channel_statistics.un_normalize(x), timestep=timestep) + diff --git a/ComfyUI/comfy/ldm/lightricks/vae/conv_nd_factory.py b/ComfyUI/comfy/ldm/lightricks/vae/conv_nd_factory.py new file mode 100644 index 0000000000000000000000000000000000000000..b4026b14fae386850459b18fb06e8b5d7f79ab94 --- /dev/null +++ b/ComfyUI/comfy/ldm/lightricks/vae/conv_nd_factory.py @@ -0,0 +1,90 @@ +from typing import Tuple, Union + + +from .dual_conv3d import DualConv3d +from .causal_conv3d import CausalConv3d +import comfy.ops +ops = comfy.ops.disable_weight_init + +def make_conv_nd( + dims: Union[int, Tuple[int, int]], + in_channels: int, + out_channels: int, + kernel_size: int, + stride=1, + padding=0, + dilation=1, + groups=1, + bias=True, + causal=False, + spatial_padding_mode="zeros", + temporal_padding_mode="zeros", +): + if not (spatial_padding_mode == temporal_padding_mode or causal): + raise NotImplementedError("spatial and temporal padding modes must be equal") + if dims == 2: + return ops.Conv2d( + in_channels=in_channels, + out_channels=out_channels, + kernel_size=kernel_size, + stride=stride, + padding=padding, + dilation=dilation, + groups=groups, + bias=bias, + padding_mode=spatial_padding_mode, + ) + elif dims == 3: + if causal: + return CausalConv3d( + in_channels=in_channels, + out_channels=out_channels, + kernel_size=kernel_size, + stride=stride, + padding=padding, + dilation=dilation, + groups=groups, + bias=bias, + spatial_padding_mode=spatial_padding_mode, + ) + return ops.Conv3d( + in_channels=in_channels, + out_channels=out_channels, + kernel_size=kernel_size, + stride=stride, + padding=padding, + dilation=dilation, + groups=groups, + bias=bias, + padding_mode=spatial_padding_mode, + ) + elif dims == (2, 1): + return DualConv3d( + in_channels=in_channels, + out_channels=out_channels, + kernel_size=kernel_size, + stride=stride, + padding=padding, + bias=bias, + padding_mode=spatial_padding_mode, + ) + else: + raise ValueError(f"unsupported dimensions: {dims}") + + +def make_linear_nd( + dims: int, + in_channels: int, + out_channels: int, + bias=True, +): + if dims == 2: + return ops.Conv2d( + in_channels=in_channels, out_channels=out_channels, kernel_size=1, bias=bias + ) + elif dims == 3 or dims == (2, 1): + return ops.Conv3d( + in_channels=in_channels, out_channels=out_channels, kernel_size=1, bias=bias + ) + else: + raise ValueError(f"unsupported dimensions: {dims}") diff --git a/ComfyUI/comfy/ldm/lightricks/vae/dual_conv3d.py b/ComfyUI/comfy/ldm/lightricks/vae/dual_conv3d.py new file mode 100644 index 0000000000000000000000000000000000000000..dcf889296750d3d7e553af37ecf77d1b10245af3 --- /dev/null +++ b/ComfyUI/comfy/ldm/lightricks/vae/dual_conv3d.py @@ -0,0 +1,217 @@ +import math +from typing import Tuple, Union + +import torch +import torch.nn as nn +import torch.nn.functional as F +from einops import rearrange + + +class DualConv3d(nn.Module): + def __init__( + self, + in_channels, + out_channels, + kernel_size, + stride: Union[int, Tuple[int, int, int]] = 1, + padding: Union[int, Tuple[int, int, int]] = 0, + dilation: Union[int, Tuple[int, int, int]] = 1, + groups=1, + bias=True, + padding_mode="zeros", + ): + super(DualConv3d, self).__init__() + + self.in_channels = in_channels + self.out_channels = out_channels + self.padding_mode = padding_mode + # Ensure kernel_size, stride, padding, and dilation are tuples of length 3 + if isinstance(kernel_size, int): + kernel_size = (kernel_size, kernel_size, kernel_size) + if kernel_size == (1, 1, 1): + raise ValueError( + "kernel_size must be greater than 1. Use make_linear_nd instead." + ) + if isinstance(stride, int): + stride = (stride, stride, stride) + if isinstance(padding, int): + padding = (padding, padding, padding) + if isinstance(dilation, int): + dilation = (dilation, dilation, dilation) + + # Set parameters for convolutions + self.groups = groups + self.bias = bias + + # Define the size of the channels after the first convolution + intermediate_channels = ( + out_channels if in_channels < out_channels else in_channels + ) + + # Define parameters for the first convolution + self.weight1 = nn.Parameter( + torch.Tensor( + intermediate_channels, + in_channels // groups, + 1, + kernel_size[1], + kernel_size[2], + ) + ) + self.stride1 = (1, stride[1], stride[2]) + self.padding1 = (0, padding[1], padding[2]) + self.dilation1 = (1, dilation[1], dilation[2]) + if bias: + self.bias1 = nn.Parameter(torch.Tensor(intermediate_channels)) + else: + self.register_parameter("bias1", None) + + # Define parameters for the second convolution + self.weight2 = nn.Parameter( + torch.Tensor( + out_channels, intermediate_channels // groups, kernel_size[0], 1, 1 + ) + ) + self.stride2 = (stride[0], 1, 1) + self.padding2 = (padding[0], 0, 0) + self.dilation2 = (dilation[0], 1, 1) + if bias: + self.bias2 = nn.Parameter(torch.Tensor(out_channels)) + else: + self.register_parameter("bias2", None) + + # Initialize weights and biases + self.reset_parameters() + + def reset_parameters(self): + nn.init.kaiming_uniform_(self.weight1, a=math.sqrt(5)) + nn.init.kaiming_uniform_(self.weight2, a=math.sqrt(5)) + if self.bias: + fan_in1, _ = nn.init._calculate_fan_in_and_fan_out(self.weight1) + bound1 = 1 / math.sqrt(fan_in1) + nn.init.uniform_(self.bias1, -bound1, bound1) + fan_in2, _ = nn.init._calculate_fan_in_and_fan_out(self.weight2) + bound2 = 1 / math.sqrt(fan_in2) + nn.init.uniform_(self.bias2, -bound2, bound2) + + def forward(self, x, use_conv3d=False, skip_time_conv=False): + if use_conv3d: + return self.forward_with_3d(x=x, skip_time_conv=skip_time_conv) + else: + return self.forward_with_2d(x=x, skip_time_conv=skip_time_conv) + + def forward_with_3d(self, x, skip_time_conv): + # First convolution + x = F.conv3d( + x, + self.weight1, + self.bias1, + self.stride1, + self.padding1, + self.dilation1, + self.groups, + padding_mode=self.padding_mode, + ) + + if skip_time_conv: + return x + + # Second convolution + x = F.conv3d( + x, + self.weight2, + self.bias2, + self.stride2, + self.padding2, + self.dilation2, + self.groups, + padding_mode=self.padding_mode, + ) + + return x + + def forward_with_2d(self, x, skip_time_conv): + b, c, d, h, w = x.shape + + # First 2D convolution + x = rearrange(x, "b c d h w -> (b d) c h w") + # Squeeze the depth dimension out of weight1 since it's 1 + weight1 = self.weight1.squeeze(2) + # Select stride, padding, and dilation for the 2D convolution + stride1 = (self.stride1[1], self.stride1[2]) + padding1 = (self.padding1[1], self.padding1[2]) + dilation1 = (self.dilation1[1], self.dilation1[2]) + x = F.conv2d( + x, + weight1, + self.bias1, + stride1, + padding1, + dilation1, + self.groups, + padding_mode=self.padding_mode, + ) + + _, _, h, w = x.shape + + if skip_time_conv: + x = rearrange(x, "(b d) c h w -> b c d h w", b=b) + return x + + # Second convolution which is essentially treated as a 1D convolution across the 'd' dimension + x = rearrange(x, "(b d) c h w -> (b h w) c d", b=b) + + # Reshape weight2 to match the expected dimensions for conv1d + weight2 = self.weight2.squeeze(-1).squeeze(-1) + # Use only the relevant dimension for stride, padding, and dilation for the 1D convolution + stride2 = self.stride2[0] + padding2 = self.padding2[0] + dilation2 = self.dilation2[0] + x = F.conv1d( + x, + weight2, + self.bias2, + stride2, + padding2, + dilation2, + self.groups, + padding_mode=self.padding_mode, + ) + x = rearrange(x, "(b h w) c d -> b c d h w", b=b, h=h, w=w) + + return x + + @property + def weight(self): + return self.weight2 + + +def test_dual_conv3d_consistency(): + # Initialize parameters + in_channels = 3 + out_channels = 5 + kernel_size = (3, 3, 3) + stride = (2, 2, 2) + padding = (1, 1, 1) + + # Create an instance of the DualConv3d class + dual_conv3d = DualConv3d( + in_channels=in_channels, + out_channels=out_channels, + kernel_size=kernel_size, + stride=stride, + padding=padding, + bias=True, + ) + + # Example input tensor + test_input = torch.randn(1, 3, 10, 10, 10) + + # Perform forward passes with both 3D and 2D settings + output_conv3d = dual_conv3d(test_input, use_conv3d=True) + output_2d = dual_conv3d(test_input, use_conv3d=False) + + # Assert that the outputs from both methods are sufficiently close + assert torch.allclose( + output_conv3d, output_2d, atol=1e-6 + ), "Outputs are not consistent between 3D and 2D convolutions." diff --git a/ComfyUI/comfy/ldm/lightricks/vae/pixel_norm.py b/ComfyUI/comfy/ldm/lightricks/vae/pixel_norm.py new file mode 100644 index 0000000000000000000000000000000000000000..9bc3ea60e8a6453e7e12a7fb5aca4de3958a2567 --- /dev/null +++ b/ComfyUI/comfy/ldm/lightricks/vae/pixel_norm.py @@ -0,0 +1,12 @@ +import torch +from torch import nn + + +class PixelNorm(nn.Module): + def __init__(self, dim=1, eps=1e-8): + super(PixelNorm, self).__init__() + self.dim = dim + self.eps = eps + + def forward(self, x): + return x / torch.sqrt(torch.mean(x**2, dim=self.dim, keepdim=True) + self.eps) diff --git a/ComfyUI/comfy/ldm/modules/diffusionmodules/__init__.py b/ComfyUI/comfy/ldm/modules/diffusionmodules/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/ComfyUI/comfy/ldm/modules/diffusionmodules/mmdit.py b/ComfyUI/comfy/ldm/modules/diffusionmodules/mmdit.py new file mode 100644 index 0000000000000000000000000000000000000000..eaf3e73a4cdfab3afde74a01eaf12defedafb917 --- /dev/null +++ b/ComfyUI/comfy/ldm/modules/diffusionmodules/mmdit.py @@ -0,0 +1,1029 @@ +from functools import partial +from typing import Dict, Optional, List + +import numpy as np +import torch +import torch.nn as nn +from ..attention import optimized_attention +from einops import rearrange, repeat +from .util import timestep_embedding +import comfy.ops +import comfy.ldm.common_dit + +def default(x, y): + if x is not None: + return x + return y + +class Mlp(nn.Module): + """ MLP as used in Vision Transformer, MLP-Mixer and related networks + """ + def __init__( + self, + in_features, + hidden_features=None, + out_features=None, + act_layer=nn.GELU, + norm_layer=None, + bias=True, + drop=0., + use_conv=False, + dtype=None, + device=None, + operations=None, + ): + super().__init__() + out_features = out_features or in_features + hidden_features = hidden_features or in_features + drop_probs = drop + linear_layer = partial(operations.Conv2d, kernel_size=1) if use_conv else operations.Linear + + self.fc1 = linear_layer(in_features, hidden_features, bias=bias, dtype=dtype, device=device) + self.act = act_layer() + self.drop1 = nn.Dropout(drop_probs) + self.norm = norm_layer(hidden_features) if norm_layer is not None else nn.Identity() + self.fc2 = linear_layer(hidden_features, out_features, bias=bias, dtype=dtype, device=device) + self.drop2 = nn.Dropout(drop_probs) + + def forward(self, x): + x = self.fc1(x) + x = self.act(x) + x = self.drop1(x) + x = self.norm(x) + x = self.fc2(x) + x = self.drop2(x) + return x + +class PatchEmbed(nn.Module): + """ 2D Image to Patch Embedding + """ + dynamic_img_pad: torch.jit.Final[bool] + + def __init__( + self, + img_size: Optional[int] = 224, + patch_size: int = 16, + in_chans: int = 3, + embed_dim: int = 768, + norm_layer = None, + flatten: bool = True, + bias: bool = True, + strict_img_size: bool = True, + dynamic_img_pad: bool = True, + padding_mode='circular', + conv3d=False, + dtype=None, + device=None, + operations=None, + ): + super().__init__() + try: + len(patch_size) + self.patch_size = patch_size + except: + if conv3d: + self.patch_size = (patch_size, patch_size, patch_size) + else: + self.patch_size = (patch_size, patch_size) + self.padding_mode = padding_mode + + # flatten spatial dim and transpose to channels last, kept for bwd compat + self.flatten = flatten + self.strict_img_size = strict_img_size + self.dynamic_img_pad = dynamic_img_pad + if conv3d: + self.proj = operations.Conv3d(in_chans, embed_dim, kernel_size=patch_size, stride=patch_size, bias=bias, dtype=dtype, device=device) + else: + self.proj = operations.Conv2d(in_chans, embed_dim, kernel_size=patch_size, stride=patch_size, bias=bias, dtype=dtype, device=device) + self.norm = norm_layer(embed_dim) if norm_layer else nn.Identity() + + def forward(self, x): + if self.dynamic_img_pad: + x = comfy.ldm.common_dit.pad_to_patch_size(x, self.patch_size, padding_mode=self.padding_mode) + x = self.proj(x) + if self.flatten: + x = x.flatten(2).transpose(1, 2) # NCHW -> NLC + x = self.norm(x) + return x + +def modulate(x, shift, scale): + if shift is None: + shift = torch.zeros_like(scale) + return x * (1 + scale.unsqueeze(1)) + shift.unsqueeze(1) + + +################################################################################# +# Sine/Cosine Positional Embedding Functions # +################################################################################# + + +def get_2d_sincos_pos_embed( + embed_dim, + grid_size, + cls_token=False, + extra_tokens=0, + scaling_factor=None, + offset=None, +): + """ + grid_size: int of the grid height and width + return: + pos_embed: [grid_size*grid_size, embed_dim] or [1+grid_size*grid_size, embed_dim] (w/ or w/o cls_token) + """ + grid_h = np.arange(grid_size, dtype=np.float32) + grid_w = np.arange(grid_size, dtype=np.float32) + grid = np.meshgrid(grid_w, grid_h) # here w goes first + grid = np.stack(grid, axis=0) + if scaling_factor is not None: + grid = grid / scaling_factor + if offset is not None: + grid = grid - offset + + grid = grid.reshape([2, 1, grid_size, grid_size]) + pos_embed = get_2d_sincos_pos_embed_from_grid(embed_dim, grid) + if cls_token and extra_tokens > 0: + pos_embed = np.concatenate( + [np.zeros([extra_tokens, embed_dim]), pos_embed], axis=0 + ) + return pos_embed + + +def get_2d_sincos_pos_embed_from_grid(embed_dim, grid): + assert embed_dim % 2 == 0 + + # use half of dimensions to encode grid_h + emb_h = get_1d_sincos_pos_embed_from_grid(embed_dim // 2, grid[0]) # (H*W, D/2) + emb_w = get_1d_sincos_pos_embed_from_grid(embed_dim // 2, grid[1]) # (H*W, D/2) + + emb = np.concatenate([emb_h, emb_w], axis=1) # (H*W, D) + return emb + + +def get_1d_sincos_pos_embed_from_grid(embed_dim, pos): + """ + embed_dim: output dimension for each position + pos: a list of positions to be encoded: size (M,) + out: (M, D) + """ + assert embed_dim % 2 == 0 + omega = np.arange(embed_dim // 2, dtype=np.float64) + omega /= embed_dim / 2.0 + omega = 1.0 / 10000**omega # (D/2,) + + pos = pos.reshape(-1) # (M,) + out = np.einsum("m,d->md", pos, omega) # (M, D/2), outer product + + emb_sin = np.sin(out) # (M, D/2) + emb_cos = np.cos(out) # (M, D/2) + + emb = np.concatenate([emb_sin, emb_cos], axis=1) # (M, D) + return emb + +def get_1d_sincos_pos_embed_from_grid_torch(embed_dim, pos, device=None, dtype=torch.float32): + omega = torch.arange(embed_dim // 2, device=device, dtype=dtype) + omega /= embed_dim / 2.0 + omega = 1.0 / 10000**omega # (D/2,) + pos = pos.reshape(-1) # (M,) + out = torch.einsum("m,d->md", pos, omega) # (M, D/2), outer product + emb_sin = torch.sin(out) # (M, D/2) + emb_cos = torch.cos(out) # (M, D/2) + emb = torch.cat([emb_sin, emb_cos], dim=1) # (M, D) + return emb + +def get_2d_sincos_pos_embed_torch(embed_dim, w, h, val_center=7.5, val_magnitude=7.5, device=None, dtype=torch.float32): + small = min(h, w) + val_h = (h / small) * val_magnitude + val_w = (w / small) * val_magnitude + grid_h, grid_w = torch.meshgrid(torch.linspace(-val_h + val_center, val_h + val_center, h, device=device, dtype=dtype), torch.linspace(-val_w + val_center, val_w + val_center, w, device=device, dtype=dtype), indexing='ij') + emb_h = get_1d_sincos_pos_embed_from_grid_torch(embed_dim // 2, grid_h, device=device, dtype=dtype) + emb_w = get_1d_sincos_pos_embed_from_grid_torch(embed_dim // 2, grid_w, device=device, dtype=dtype) + emb = torch.cat([emb_w, emb_h], dim=1) # (H*W, D) + return emb + + +################################################################################# +# Embedding Layers for Timesteps and Class Labels # +################################################################################# + + +class TimestepEmbedder(nn.Module): + """ + Embeds scalar timesteps into vector representations. + """ + + def __init__(self, hidden_size, frequency_embedding_size=256, dtype=None, device=None, operations=None): + super().__init__() + self.mlp = nn.Sequential( + operations.Linear(frequency_embedding_size, hidden_size, bias=True, dtype=dtype, device=device), + nn.SiLU(), + operations.Linear(hidden_size, hidden_size, bias=True, dtype=dtype, device=device), + ) + self.frequency_embedding_size = frequency_embedding_size + + def forward(self, t, dtype, **kwargs): + t_freq = timestep_embedding(t, self.frequency_embedding_size).to(dtype) + t_emb = self.mlp(t_freq) + return t_emb + + +class VectorEmbedder(nn.Module): + """ + Embeds a flat vector of dimension input_dim + """ + + def __init__(self, input_dim: int, hidden_size: int, dtype=None, device=None, operations=None): + super().__init__() + self.mlp = nn.Sequential( + operations.Linear(input_dim, hidden_size, bias=True, dtype=dtype, device=device), + nn.SiLU(), + operations.Linear(hidden_size, hidden_size, bias=True, dtype=dtype, device=device), + ) + + def forward(self, x: torch.Tensor) -> torch.Tensor: + emb = self.mlp(x) + return emb + + +################################################################################# +# Core DiT Model # +################################################################################# + + +def split_qkv(qkv, head_dim): + qkv = qkv.reshape(qkv.shape[0], qkv.shape[1], 3, -1, head_dim).movedim(2, 0) + return qkv[0], qkv[1], qkv[2] + + +class SelfAttention(nn.Module): + ATTENTION_MODES = ("xformers", "torch", "torch-hb", "math", "debug") + + def __init__( + self, + dim: int, + num_heads: int = 8, + qkv_bias: bool = False, + qk_scale: Optional[float] = None, + proj_drop: float = 0.0, + attn_mode: str = "xformers", + pre_only: bool = False, + qk_norm: Optional[str] = None, + rmsnorm: bool = False, + dtype=None, + device=None, + operations=None, + ): + super().__init__() + self.num_heads = num_heads + self.head_dim = dim // num_heads + + self.qkv = operations.Linear(dim, dim * 3, bias=qkv_bias, dtype=dtype, device=device) + if not pre_only: + self.proj = operations.Linear(dim, dim, dtype=dtype, device=device) + self.proj_drop = nn.Dropout(proj_drop) + assert attn_mode in self.ATTENTION_MODES + self.attn_mode = attn_mode + self.pre_only = pre_only + + if qk_norm == "rms": + self.ln_q = RMSNorm(self.head_dim, elementwise_affine=True, eps=1.0e-6, dtype=dtype, device=device) + self.ln_k = RMSNorm(self.head_dim, elementwise_affine=True, eps=1.0e-6, dtype=dtype, device=device) + elif qk_norm == "ln": + self.ln_q = operations.LayerNorm(self.head_dim, elementwise_affine=True, eps=1.0e-6, dtype=dtype, device=device) + self.ln_k = operations.LayerNorm(self.head_dim, elementwise_affine=True, eps=1.0e-6, dtype=dtype, device=device) + elif qk_norm is None: + self.ln_q = nn.Identity() + self.ln_k = nn.Identity() + else: + raise ValueError(qk_norm) + + def pre_attention(self, x: torch.Tensor) -> torch.Tensor: + B, L, C = x.shape + qkv = self.qkv(x) + q, k, v = split_qkv(qkv, self.head_dim) + q = self.ln_q(q).reshape(q.shape[0], q.shape[1], -1) + k = self.ln_k(k).reshape(q.shape[0], q.shape[1], -1) + return (q, k, v) + + def post_attention(self, x: torch.Tensor) -> torch.Tensor: + assert not self.pre_only + x = self.proj(x) + x = self.proj_drop(x) + return x + + def forward(self, x: torch.Tensor) -> torch.Tensor: + q, k, v = self.pre_attention(x) + x = optimized_attention( + q, k, v, heads=self.num_heads + ) + x = self.post_attention(x) + return x + + +class RMSNorm(torch.nn.Module): + def __init__( + self, dim: int, elementwise_affine: bool = False, eps: float = 1e-6, device=None, dtype=None, **kwargs + ): + """ + Initialize the RMSNorm normalization layer. + Args: + dim (int): The dimension of the input tensor. + eps (float, optional): A small value added to the denominator for numerical stability. Default is 1e-6. + Attributes: + eps (float): A small value added to the denominator for numerical stability. + weight (nn.Parameter): Learnable scaling parameter. + """ + super().__init__() + self.eps = eps + self.learnable_scale = elementwise_affine + if self.learnable_scale: + self.weight = nn.Parameter(torch.empty(dim, device=device, dtype=dtype)) + else: + self.register_parameter("weight", None) + + def forward(self, x): + return comfy.ldm.common_dit.rms_norm(x, self.weight, self.eps) + + + +class SwiGLUFeedForward(nn.Module): + def __init__( + self, + dim: int, + hidden_dim: int, + multiple_of: int, + ffn_dim_multiplier: Optional[float] = None, + ): + """ + Initialize the FeedForward module. + + Args: + dim (int): Input dimension. + hidden_dim (int): Hidden dimension of the feedforward layer. + multiple_of (int): Value to ensure hidden dimension is a multiple of this value. + ffn_dim_multiplier (float, optional): Custom multiplier for hidden dimension. Defaults to None. + + Attributes: + w1 (ColumnParallelLinear): Linear transformation for the first layer. + w2 (RowParallelLinear): Linear transformation for the second layer. + w3 (ColumnParallelLinear): Linear transformation for the third layer. + + """ + super().__init__() + hidden_dim = int(2 * hidden_dim / 3) + # custom dim factor multiplier + if ffn_dim_multiplier is not None: + hidden_dim = int(ffn_dim_multiplier * hidden_dim) + hidden_dim = multiple_of * ((hidden_dim + multiple_of - 1) // multiple_of) + + self.w1 = nn.Linear(dim, hidden_dim, bias=False) + self.w2 = nn.Linear(hidden_dim, dim, bias=False) + self.w3 = nn.Linear(dim, hidden_dim, bias=False) + + def forward(self, x): + return self.w2(nn.functional.silu(self.w1(x)) * self.w3(x)) + + +class DismantledBlock(nn.Module): + """ + A DiT block with gated adaptive layer norm (adaLN) conditioning. + """ + + ATTENTION_MODES = ("xformers", "torch", "torch-hb", "math", "debug") + + def __init__( + self, + hidden_size: int, + num_heads: int, + mlp_ratio: float = 4.0, + attn_mode: str = "xformers", + qkv_bias: bool = False, + pre_only: bool = False, + rmsnorm: bool = False, + scale_mod_only: bool = False, + swiglu: bool = False, + qk_norm: Optional[str] = None, + x_block_self_attn: bool = False, + dtype=None, + device=None, + operations=None, + **block_kwargs, + ): + super().__init__() + assert attn_mode in self.ATTENTION_MODES + if not rmsnorm: + self.norm1 = operations.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6, dtype=dtype, device=device) + else: + self.norm1 = RMSNorm(hidden_size, elementwise_affine=False, eps=1e-6) + self.attn = SelfAttention( + dim=hidden_size, + num_heads=num_heads, + qkv_bias=qkv_bias, + attn_mode=attn_mode, + pre_only=pre_only, + qk_norm=qk_norm, + rmsnorm=rmsnorm, + dtype=dtype, + device=device, + operations=operations + ) + if x_block_self_attn: + assert not pre_only + assert not scale_mod_only + self.x_block_self_attn = True + self.attn2 = SelfAttention( + dim=hidden_size, + num_heads=num_heads, + qkv_bias=qkv_bias, + attn_mode=attn_mode, + pre_only=False, + qk_norm=qk_norm, + rmsnorm=rmsnorm, + dtype=dtype, + device=device, + operations=operations + ) + else: + self.x_block_self_attn = False + if not pre_only: + if not rmsnorm: + self.norm2 = operations.LayerNorm( + hidden_size, elementwise_affine=False, eps=1e-6, dtype=dtype, device=device + ) + else: + self.norm2 = RMSNorm(hidden_size, elementwise_affine=False, eps=1e-6) + mlp_hidden_dim = int(hidden_size * mlp_ratio) + if not pre_only: + if not swiglu: + self.mlp = Mlp( + in_features=hidden_size, + hidden_features=mlp_hidden_dim, + act_layer=lambda: nn.GELU(approximate="tanh"), + drop=0, + dtype=dtype, + device=device, + operations=operations + ) + else: + self.mlp = SwiGLUFeedForward( + dim=hidden_size, + hidden_dim=mlp_hidden_dim, + multiple_of=256, + ) + self.scale_mod_only = scale_mod_only + if x_block_self_attn: + assert not pre_only + assert not scale_mod_only + n_mods = 9 + elif not scale_mod_only: + n_mods = 6 if not pre_only else 2 + else: + n_mods = 4 if not pre_only else 1 + self.adaLN_modulation = nn.Sequential( + nn.SiLU(), operations.Linear(hidden_size, n_mods * hidden_size, bias=True, dtype=dtype, device=device) + ) + self.pre_only = pre_only + + def pre_attention(self, x: torch.Tensor, c: torch.Tensor) -> torch.Tensor: + if not self.pre_only: + if not self.scale_mod_only: + ( + shift_msa, + scale_msa, + gate_msa, + shift_mlp, + scale_mlp, + gate_mlp, + ) = self.adaLN_modulation(c).chunk(6, dim=1) + else: + shift_msa = None + shift_mlp = None + ( + scale_msa, + gate_msa, + scale_mlp, + gate_mlp, + ) = self.adaLN_modulation( + c + ).chunk(4, dim=1) + qkv = self.attn.pre_attention(modulate(self.norm1(x), shift_msa, scale_msa)) + return qkv, ( + x, + gate_msa, + shift_mlp, + scale_mlp, + gate_mlp, + ) + else: + if not self.scale_mod_only: + ( + shift_msa, + scale_msa, + ) = self.adaLN_modulation( + c + ).chunk(2, dim=1) + else: + shift_msa = None + scale_msa = self.adaLN_modulation(c) + qkv = self.attn.pre_attention(modulate(self.norm1(x), shift_msa, scale_msa)) + return qkv, None + + def post_attention(self, attn, x, gate_msa, shift_mlp, scale_mlp, gate_mlp): + assert not self.pre_only + x = x + gate_msa.unsqueeze(1) * self.attn.post_attention(attn) + x = x + gate_mlp.unsqueeze(1) * self.mlp( + modulate(self.norm2(x), shift_mlp, scale_mlp) + ) + return x + + def pre_attention_x(self, x: torch.Tensor, c: torch.Tensor) -> torch.Tensor: + assert self.x_block_self_attn + ( + shift_msa, + scale_msa, + gate_msa, + shift_mlp, + scale_mlp, + gate_mlp, + shift_msa2, + scale_msa2, + gate_msa2, + ) = self.adaLN_modulation(c).chunk(9, dim=1) + x_norm = self.norm1(x) + qkv = self.attn.pre_attention(modulate(x_norm, shift_msa, scale_msa)) + qkv2 = self.attn2.pre_attention(modulate(x_norm, shift_msa2, scale_msa2)) + return qkv, qkv2, ( + x, + gate_msa, + shift_mlp, + scale_mlp, + gate_mlp, + gate_msa2, + ) + + def post_attention_x(self, attn, attn2, x, gate_msa, shift_mlp, scale_mlp, gate_mlp, gate_msa2): + assert not self.pre_only + attn1 = self.attn.post_attention(attn) + attn2 = self.attn2.post_attention(attn2) + out1 = gate_msa.unsqueeze(1) * attn1 + out2 = gate_msa2.unsqueeze(1) * attn2 + x = x + out1 + x = x + out2 + x = x + gate_mlp.unsqueeze(1) * self.mlp( + modulate(self.norm2(x), shift_mlp, scale_mlp) + ) + return x + + def forward(self, x: torch.Tensor, c: torch.Tensor) -> torch.Tensor: + assert not self.pre_only + if self.x_block_self_attn: + qkv, qkv2, intermediates = self.pre_attention_x(x, c) + attn, _ = optimized_attention( + qkv[0], qkv[1], qkv[2], + num_heads=self.attn.num_heads, + ) + attn2, _ = optimized_attention( + qkv2[0], qkv2[1], qkv2[2], + num_heads=self.attn2.num_heads, + ) + return self.post_attention_x(attn, attn2, *intermediates) + else: + qkv, intermediates = self.pre_attention(x, c) + attn = optimized_attention( + qkv[0], qkv[1], qkv[2], + heads=self.attn.num_heads, + ) + return self.post_attention(attn, *intermediates) + + +def block_mixing(*args, use_checkpoint=True, **kwargs): + if use_checkpoint: + return torch.utils.checkpoint.checkpoint( + _block_mixing, *args, use_reentrant=False, **kwargs + ) + else: + return _block_mixing(*args, **kwargs) + + +def _block_mixing(context, x, context_block, x_block, c): + context_qkv, context_intermediates = context_block.pre_attention(context, c) + + if x_block.x_block_self_attn: + x_qkv, x_qkv2, x_intermediates = x_block.pre_attention_x(x, c) + else: + x_qkv, x_intermediates = x_block.pre_attention(x, c) + + o = [] + for t in range(3): + o.append(torch.cat((context_qkv[t], x_qkv[t]), dim=1)) + qkv = tuple(o) + + attn = optimized_attention( + qkv[0], qkv[1], qkv[2], + heads=x_block.attn.num_heads, + ) + context_attn, x_attn = ( + attn[:, : context_qkv[0].shape[1]], + attn[:, context_qkv[0].shape[1] :], + ) + + if not context_block.pre_only: + context = context_block.post_attention(context_attn, *context_intermediates) + + else: + context = None + if x_block.x_block_self_attn: + attn2 = optimized_attention( + x_qkv2[0], x_qkv2[1], x_qkv2[2], + heads=x_block.attn2.num_heads, + ) + x = x_block.post_attention_x(x_attn, attn2, *x_intermediates) + else: + x = x_block.post_attention(x_attn, *x_intermediates) + return context, x + + +class JointBlock(nn.Module): + """just a small wrapper to serve as a fsdp unit""" + + def __init__( + self, + *args, + **kwargs, + ): + super().__init__() + pre_only = kwargs.pop("pre_only") + qk_norm = kwargs.pop("qk_norm", None) + x_block_self_attn = kwargs.pop("x_block_self_attn", False) + self.context_block = DismantledBlock(*args, pre_only=pre_only, qk_norm=qk_norm, **kwargs) + self.x_block = DismantledBlock(*args, + pre_only=False, + qk_norm=qk_norm, + x_block_self_attn=x_block_self_attn, + **kwargs) + + def forward(self, *args, **kwargs): + return block_mixing( + *args, context_block=self.context_block, x_block=self.x_block, **kwargs + ) + + +class FinalLayer(nn.Module): + """ + The final layer of DiT. + """ + + def __init__( + self, + hidden_size: int, + patch_size: int, + out_channels: int, + total_out_channels: Optional[int] = None, + dtype=None, + device=None, + operations=None, + ): + super().__init__() + self.norm_final = operations.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6, dtype=dtype, device=device) + self.linear = ( + operations.Linear(hidden_size, patch_size * patch_size * out_channels, bias=True, dtype=dtype, device=device) + if (total_out_channels is None) + else operations.Linear(hidden_size, total_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) + ) + + def forward(self, x: torch.Tensor, c: torch.Tensor) -> torch.Tensor: + shift, scale = self.adaLN_modulation(c).chunk(2, dim=1) + x = modulate(self.norm_final(x), shift, scale) + x = self.linear(x) + return x + +class SelfAttentionContext(nn.Module): + def __init__(self, dim, heads=8, dim_head=64, dtype=None, device=None, operations=None): + super().__init__() + dim_head = dim // heads + inner_dim = dim + + self.heads = heads + self.dim_head = dim_head + + self.qkv = operations.Linear(dim, dim * 3, bias=True, dtype=dtype, device=device) + + self.proj = operations.Linear(inner_dim, dim, dtype=dtype, device=device) + + def forward(self, x): + qkv = self.qkv(x) + q, k, v = split_qkv(qkv, self.dim_head) + x = optimized_attention(q.reshape(q.shape[0], q.shape[1], -1), k, v, heads=self.heads) + return self.proj(x) + +class ContextProcessorBlock(nn.Module): + def __init__(self, context_size, dtype=None, device=None, operations=None): + super().__init__() + self.norm1 = operations.LayerNorm(context_size, elementwise_affine=False, eps=1e-6, dtype=dtype, device=device) + self.attn = SelfAttentionContext(context_size, dtype=dtype, device=device, operations=operations) + self.norm2 = operations.LayerNorm(context_size, elementwise_affine=False, eps=1e-6, dtype=dtype, device=device) + self.mlp = Mlp(in_features=context_size, hidden_features=(context_size * 4), act_layer=lambda: nn.GELU(approximate="tanh"), drop=0, dtype=dtype, device=device, operations=operations) + + def forward(self, x): + x += self.attn(self.norm1(x)) + x += self.mlp(self.norm2(x)) + return x + +class ContextProcessor(nn.Module): + def __init__(self, context_size, num_layers, dtype=None, device=None, operations=None): + super().__init__() + self.layers = torch.nn.ModuleList([ContextProcessorBlock(context_size, dtype=dtype, device=device, operations=operations) for i in range(num_layers)]) + self.norm = operations.LayerNorm(context_size, elementwise_affine=False, eps=1e-6, dtype=dtype, device=device) + + def forward(self, x): + for i, l in enumerate(self.layers): + x = l(x) + return self.norm(x) + +class MMDiT(nn.Module): + """ + Diffusion model with a Transformer backbone. + """ + + def __init__( + self, + input_size: int = 32, + patch_size: int = 2, + in_channels: int = 4, + depth: int = 28, + # hidden_size: Optional[int] = None, + # num_heads: Optional[int] = None, + mlp_ratio: float = 4.0, + learn_sigma: bool = False, + adm_in_channels: Optional[int] = None, + context_embedder_config: Optional[Dict] = None, + compile_core: bool = False, + use_checkpoint: bool = False, + register_length: int = 0, + attn_mode: str = "torch", + rmsnorm: bool = False, + scale_mod_only: bool = False, + swiglu: bool = False, + out_channels: Optional[int] = None, + pos_embed_scaling_factor: Optional[float] = None, + pos_embed_offset: Optional[float] = None, + pos_embed_max_size: Optional[int] = None, + num_patches = None, + qk_norm: Optional[str] = None, + qkv_bias: bool = True, + context_processor_layers = None, + x_block_self_attn: bool = False, + x_block_self_attn_layers: Optional[List[int]] = [], + context_size = 4096, + num_blocks = None, + final_layer = True, + skip_blocks = False, + dtype = None, #TODO + device = None, + operations = None, + ): + super().__init__() + self.dtype = dtype + self.learn_sigma = learn_sigma + self.in_channels = in_channels + default_out_channels = in_channels * 2 if learn_sigma else in_channels + self.out_channels = default(out_channels, default_out_channels) + self.patch_size = patch_size + self.pos_embed_scaling_factor = pos_embed_scaling_factor + self.pos_embed_offset = pos_embed_offset + self.pos_embed_max_size = pos_embed_max_size + self.x_block_self_attn_layers = x_block_self_attn_layers + + # hidden_size = default(hidden_size, 64 * depth) + # num_heads = default(num_heads, hidden_size // 64) + + # apply magic --> this defines a head_size of 64 + self.hidden_size = 64 * depth + num_heads = depth + if num_blocks is None: + num_blocks = depth + + self.depth = depth + self.num_heads = num_heads + + self.x_embedder = PatchEmbed( + input_size, + patch_size, + in_channels, + self.hidden_size, + bias=True, + strict_img_size=self.pos_embed_max_size is None, + dtype=dtype, + device=device, + operations=operations + ) + self.t_embedder = TimestepEmbedder(self.hidden_size, dtype=dtype, device=device, operations=operations) + + self.y_embedder = None + if adm_in_channels is not None: + assert isinstance(adm_in_channels, int) + self.y_embedder = VectorEmbedder(adm_in_channels, self.hidden_size, dtype=dtype, device=device, operations=operations) + + if context_processor_layers is not None: + self.context_processor = ContextProcessor(context_size, context_processor_layers, dtype=dtype, device=device, operations=operations) + else: + self.context_processor = None + + self.context_embedder = nn.Identity() + if context_embedder_config is not None: + if context_embedder_config["target"] == "torch.nn.Linear": + self.context_embedder = operations.Linear(**context_embedder_config["params"], dtype=dtype, device=device) + + self.register_length = register_length + if self.register_length > 0: + self.register = nn.Parameter(torch.randn(1, register_length, self.hidden_size, dtype=dtype, device=device)) + + # num_patches = self.x_embedder.num_patches + # Will use fixed sin-cos embedding: + # just use a buffer already + if num_patches is not None: + self.register_buffer( + "pos_embed", + torch.empty(1, num_patches, self.hidden_size, dtype=dtype, device=device), + ) + else: + self.pos_embed = None + + self.use_checkpoint = use_checkpoint + if not skip_blocks: + self.joint_blocks = nn.ModuleList( + [ + JointBlock( + self.hidden_size, + num_heads, + mlp_ratio=mlp_ratio, + qkv_bias=qkv_bias, + attn_mode=attn_mode, + pre_only=(i == num_blocks - 1) and final_layer, + rmsnorm=rmsnorm, + scale_mod_only=scale_mod_only, + swiglu=swiglu, + qk_norm=qk_norm, + x_block_self_attn=(i in self.x_block_self_attn_layers) or x_block_self_attn, + dtype=dtype, + device=device, + operations=operations, + ) + for i in range(num_blocks) + ] + ) + + if final_layer: + self.final_layer = FinalLayer(self.hidden_size, patch_size, self.out_channels, dtype=dtype, device=device, operations=operations) + + if compile_core: + assert False + self.forward_core_with_concat = torch.compile(self.forward_core_with_concat) + + def cropped_pos_embed(self, hw, device=None): + p = self.x_embedder.patch_size[0] + h, w = hw + # patched size + h = (h + 1) // p + w = (w + 1) // p + if self.pos_embed is None: + return get_2d_sincos_pos_embed_torch(self.hidden_size, w, h, device=device) + assert self.pos_embed_max_size is not None + assert h <= self.pos_embed_max_size, (h, self.pos_embed_max_size) + assert w <= self.pos_embed_max_size, (w, self.pos_embed_max_size) + top = (self.pos_embed_max_size - h) // 2 + left = (self.pos_embed_max_size - w) // 2 + spatial_pos_embed = rearrange( + self.pos_embed, + "1 (h w) c -> 1 h w c", + h=self.pos_embed_max_size, + w=self.pos_embed_max_size, + ) + spatial_pos_embed = spatial_pos_embed[:, top : top + h, left : left + w, :] + spatial_pos_embed = rearrange(spatial_pos_embed, "1 h w c -> 1 (h w) c") + # print(spatial_pos_embed, top, left, h, w) + # # t = get_2d_sincos_pos_embed_torch(self.hidden_size, w, h, 7.875, 7.875, device=device) #matches exactly for 1024 res + # t = get_2d_sincos_pos_embed_torch(self.hidden_size, w, h, 7.5, 7.5, device=device) #scales better + # # print(t) + # return t + return spatial_pos_embed + + def unpatchify(self, x, hw=None): + """ + x: (N, T, patch_size**2 * C) + imgs: (N, H, W, C) + """ + c = self.out_channels + p = self.x_embedder.patch_size[0] + if hw is None: + h = w = int(x.shape[1] ** 0.5) + else: + h, w = hw + h = (h + 1) // p + w = (w + 1) // p + assert h * w == x.shape[1] + + x = x.reshape(shape=(x.shape[0], h, w, p, p, c)) + x = torch.einsum("nhwpqc->nchpwq", x) + imgs = x.reshape(shape=(x.shape[0], c, h * p, w * p)) + return imgs + + def forward_core_with_concat( + self, + x: torch.Tensor, + c_mod: torch.Tensor, + context: Optional[torch.Tensor] = None, + control = None, + transformer_options = {}, + ) -> torch.Tensor: + patches_replace = transformer_options.get("patches_replace", {}) + if self.register_length > 0: + context = torch.cat( + ( + repeat(self.register, "1 ... -> b ...", b=x.shape[0]), + default(context, torch.Tensor([]).type_as(x)), + ), + 1, + ) + + # context is B, L', D + # x is B, L, D + blocks_replace = patches_replace.get("dit", {}) + blocks = len(self.joint_blocks) + for i in range(blocks): + if ("double_block", i) in blocks_replace: + def block_wrap(args): + out = {} + out["txt"], out["img"] = self.joint_blocks[i](args["txt"], args["img"], c=args["vec"]) + return out + + out = blocks_replace[("double_block", i)]({"img": x, "txt": context, "vec": c_mod}, {"original_block": block_wrap}) + context = out["txt"] + x = out["img"] + else: + context, x = self.joint_blocks[i]( + context, + x, + c=c_mod, + use_checkpoint=self.use_checkpoint, + ) + if control is not None: + control_o = control.get("output") + if i < len(control_o): + add = control_o[i] + if add is not None: + x += add + + x = self.final_layer(x, c_mod) # (N, T, patch_size ** 2 * out_channels) + return x + + def forward( + self, + x: torch.Tensor, + t: torch.Tensor, + y: Optional[torch.Tensor] = None, + context: Optional[torch.Tensor] = None, + control = None, + transformer_options = {}, + ) -> torch.Tensor: + """ + Forward pass of DiT. + x: (N, C, H, W) tensor of spatial inputs (images or latent representations of images) + t: (N,) tensor of diffusion timesteps + y: (N,) tensor of class labels + """ + + if self.context_processor is not None: + context = self.context_processor(context) + + hw = x.shape[-2:] + x = self.x_embedder(x) + comfy.ops.cast_to_input(self.cropped_pos_embed(hw, device=x.device), x) + c = self.t_embedder(t, dtype=x.dtype) # (N, D) + if y is not None and self.y_embedder is not None: + y = self.y_embedder(y) # (N, D) + c = c + y # (N, D) + + if context is not None: + context = self.context_embedder(context) + + x = self.forward_core_with_concat(x, c, context, control, transformer_options) + + x = self.unpatchify(x, hw=hw) # (N, out_channels, H, W) + return x[:,:,:hw[-2],:hw[-1]] + + +class OpenAISignatureMMDITWrapper(MMDiT): + def forward( + self, + x: torch.Tensor, + timesteps: torch.Tensor, + context: Optional[torch.Tensor] = None, + y: Optional[torch.Tensor] = None, + control = None, + transformer_options = {}, + **kwargs, + ) -> torch.Tensor: + return super().forward(x, timesteps, context=context, y=y, control=control, transformer_options=transformer_options) + diff --git a/ComfyUI/comfy/ldm/modules/diffusionmodules/model.py b/ComfyUI/comfy/ldm/modules/diffusionmodules/model.py new file mode 100644 index 0000000000000000000000000000000000000000..8162742cf034a576b33355beb5a1c23825f1488d --- /dev/null +++ b/ComfyUI/comfy/ldm/modules/diffusionmodules/model.py @@ -0,0 +1,734 @@ +# pytorch_diffusion + derived encoder decoder +import math +import torch +import torch.nn as nn +import numpy as np +import logging + +from comfy import model_management +import comfy.ops +ops = comfy.ops.disable_weight_init + +if model_management.xformers_enabled_vae(): + import xformers + import xformers.ops + +def get_timestep_embedding(timesteps, embedding_dim): + """ + This matches the implementation in Denoising Diffusion Probabilistic Models: + From Fairseq. + Build sinusoidal embeddings. + This matches the implementation in tensor2tensor, but differs slightly + from the description in Section 3.5 of "Attention Is All You Need". + """ + assert len(timesteps.shape) == 1 + + half_dim = embedding_dim // 2 + emb = math.log(10000) / (half_dim - 1) + emb = torch.exp(torch.arange(half_dim, dtype=torch.float32) * -emb) + emb = emb.to(device=timesteps.device) + emb = timesteps.float()[:, None] * emb[None, :] + emb = torch.cat([torch.sin(emb), torch.cos(emb)], dim=1) + if embedding_dim % 2 == 1: # zero pad + emb = torch.nn.functional.pad(emb, (0,1,0,0)) + return emb + + +def nonlinearity(x): + # swish + return x*torch.sigmoid(x) + + +def Normalize(in_channels, num_groups=32): + return ops.GroupNorm(num_groups=num_groups, num_channels=in_channels, eps=1e-6, affine=True) + + +class VideoConv3d(nn.Module): + def __init__(self, n_channels, out_channels, kernel_size, stride=1, dilation=1, padding_mode='replicate', padding=1, **kwargs): + super().__init__() + + self.padding_mode = padding_mode + if padding != 0: + padding = (padding, padding, padding, padding, kernel_size - 1, 0) + else: + kwargs["padding"] = padding + + self.padding = padding + self.conv = ops.Conv3d(n_channels, out_channels, kernel_size, stride=stride, dilation=dilation, **kwargs) + + def forward(self, x): + if self.padding != 0: + x = torch.nn.functional.pad(x, self.padding, mode=self.padding_mode) + return self.conv(x) + +def interpolate_up(x, scale_factor): + try: + return torch.nn.functional.interpolate(x, scale_factor=scale_factor, mode="nearest") + except: #operation not implemented for bf16 + orig_shape = list(x.shape) + out_shape = orig_shape[:2] + for i in range(len(orig_shape) - 2): + out_shape.append(round(orig_shape[i + 2] * scale_factor[i])) + out = torch.empty(out_shape, dtype=x.dtype, layout=x.layout, device=x.device) + split = 8 + l = out.shape[1] // split + for i in range(0, out.shape[1], l): + out[:,i:i+l] = torch.nn.functional.interpolate(x[:,i:i+l].to(torch.float32), scale_factor=scale_factor, mode="nearest").to(x.dtype) + return out + +class Upsample(nn.Module): + def __init__(self, in_channels, with_conv, conv_op=ops.Conv2d, scale_factor=2.0): + super().__init__() + self.with_conv = with_conv + self.scale_factor = scale_factor + + if self.with_conv: + self.conv = conv_op(in_channels, + in_channels, + kernel_size=3, + stride=1, + padding=1) + + def forward(self, x): + scale_factor = self.scale_factor + if isinstance(scale_factor, (int, float)): + scale_factor = (scale_factor,) * (x.ndim - 2) + + if x.ndim == 5 and scale_factor[0] > 1.0: + t = x.shape[2] + if t > 1: + a, b = x.split((1, t - 1), dim=2) + del x + b = interpolate_up(b, scale_factor) + else: + a = x + + a = interpolate_up(a.squeeze(2), scale_factor=scale_factor[1:]).unsqueeze(2) + if t > 1: + x = torch.cat((a, b), dim=2) + else: + x = a + else: + x = interpolate_up(x, scale_factor) + if self.with_conv: + x = self.conv(x) + return x + + +class Downsample(nn.Module): + def __init__(self, in_channels, with_conv, stride=2, conv_op=ops.Conv2d): + super().__init__() + self.with_conv = with_conv + if self.with_conv: + # no asymmetric padding in torch conv, must do it ourselves + self.conv = conv_op(in_channels, + in_channels, + kernel_size=3, + stride=stride, + padding=0) + + def forward(self, x): + if self.with_conv: + if x.ndim == 4: + pad = (0, 1, 0, 1) + mode = "constant" + x = torch.nn.functional.pad(x, pad, mode=mode, value=0) + elif x.ndim == 5: + pad = (1, 1, 1, 1, 2, 0) + mode = "replicate" + x = torch.nn.functional.pad(x, pad, mode=mode) + x = self.conv(x) + else: + x = torch.nn.functional.avg_pool2d(x, kernel_size=2, stride=2) + return x + + +class ResnetBlock(nn.Module): + def __init__(self, *, in_channels, out_channels=None, conv_shortcut=False, + dropout, temb_channels=512, conv_op=ops.Conv2d): + super().__init__() + self.in_channels = in_channels + out_channels = in_channels if out_channels is None else out_channels + self.out_channels = out_channels + self.use_conv_shortcut = conv_shortcut + + self.swish = torch.nn.SiLU(inplace=True) + self.norm1 = Normalize(in_channels) + self.conv1 = conv_op(in_channels, + out_channels, + kernel_size=3, + stride=1, + padding=1) + if temb_channels > 0: + self.temb_proj = ops.Linear(temb_channels, + out_channels) + self.norm2 = Normalize(out_channels) + self.dropout = torch.nn.Dropout(dropout, inplace=True) + self.conv2 = conv_op(out_channels, + out_channels, + kernel_size=3, + stride=1, + padding=1) + if self.in_channels != self.out_channels: + if self.use_conv_shortcut: + self.conv_shortcut = conv_op(in_channels, + out_channels, + kernel_size=3, + stride=1, + padding=1) + else: + self.nin_shortcut = conv_op(in_channels, + out_channels, + kernel_size=1, + stride=1, + padding=0) + + def forward(self, x, temb): + h = x + h = self.norm1(h) + h = self.swish(h) + h = self.conv1(h) + + if temb is not None: + h = h + self.temb_proj(self.swish(temb))[:,:,None,None] + + h = self.norm2(h) + h = self.swish(h) + h = self.dropout(h) + h = self.conv2(h) + + if self.in_channels != self.out_channels: + if self.use_conv_shortcut: + x = self.conv_shortcut(x) + else: + x = self.nin_shortcut(x) + + return x+h + +def slice_attention(q, k, v): + r1 = torch.zeros_like(k, device=q.device) + scale = (int(q.shape[-1])**(-0.5)) + + mem_free_total = model_management.get_free_memory(q.device) + + tensor_size = q.shape[0] * q.shape[1] * k.shape[2] * q.element_size() + modifier = 3 if q.element_size() == 2 else 2.5 + mem_required = tensor_size * modifier + steps = 1 + + if mem_required > mem_free_total: + steps = 2**(math.ceil(math.log(mem_required / mem_free_total, 2))) + + while True: + try: + slice_size = q.shape[1] // steps if (q.shape[1] % steps) == 0 else q.shape[1] + for i in range(0, q.shape[1], slice_size): + end = i + slice_size + s1 = torch.bmm(q[:, i:end], k) * scale + + s2 = torch.nn.functional.softmax(s1, dim=2).permute(0,2,1) + del s1 + + r1[:, :, i:end] = torch.bmm(v, s2) + del s2 + break + except model_management.OOM_EXCEPTION as e: + model_management.soft_empty_cache(True) + steps *= 2 + if steps > 128: + raise e + logging.warning("out of memory error, increasing steps and trying again {}".format(steps)) + + return r1 + +def normal_attention(q, k, v): + # compute attention + orig_shape = q.shape + b = orig_shape[0] + c = orig_shape[1] + + q = q.reshape(b, c, -1) + q = q.permute(0, 2, 1) # b,hw,c + k = k.reshape(b, c, -1) # b,c,hw + v = v.reshape(b, c, -1) + + r1 = slice_attention(q, k, v) + h_ = r1.reshape(orig_shape) + del r1 + return h_ + +def xformers_attention(q, k, v): + # compute attention + orig_shape = q.shape + B = orig_shape[0] + C = orig_shape[1] + q, k, v = map( + lambda t: t.view(B, C, -1).transpose(1, 2).contiguous(), + (q, k, v), + ) + + try: + out = xformers.ops.memory_efficient_attention(q, k, v, attn_bias=None) + out = out.transpose(1, 2).reshape(orig_shape) + except NotImplementedError: + out = slice_attention(q.view(B, -1, C), k.view(B, -1, C).transpose(1, 2), v.view(B, -1, C).transpose(1, 2)).reshape(orig_shape) + return out + +def pytorch_attention(q, k, v): + # compute attention + orig_shape = q.shape + B = orig_shape[0] + C = orig_shape[1] + q, k, v = map( + lambda t: t.view(B, 1, C, -1).transpose(2, 3).contiguous(), + (q, k, v), + ) + + try: + out = torch.nn.functional.scaled_dot_product_attention(q, k, v, attn_mask=None, dropout_p=0.0, is_causal=False) + out = out.transpose(2, 3).reshape(orig_shape) + except model_management.OOM_EXCEPTION: + logging.warning("scaled_dot_product_attention OOMed: switched to slice attention") + out = slice_attention(q.view(B, -1, C), k.view(B, -1, C).transpose(1, 2), v.view(B, -1, C).transpose(1, 2)).reshape(orig_shape) + return out + + +def vae_attention(): + if model_management.xformers_enabled_vae(): + logging.info("Using xformers attention in VAE") + return xformers_attention + elif model_management.pytorch_attention_enabled_vae(): + logging.info("Using pytorch attention in VAE") + return pytorch_attention + else: + logging.info("Using split attention in VAE") + return normal_attention + +class AttnBlock(nn.Module): + def __init__(self, in_channels, conv_op=ops.Conv2d): + super().__init__() + self.in_channels = in_channels + + self.norm = Normalize(in_channels) + self.q = conv_op(in_channels, + in_channels, + kernel_size=1, + stride=1, + padding=0) + self.k = conv_op(in_channels, + in_channels, + kernel_size=1, + stride=1, + padding=0) + self.v = conv_op(in_channels, + in_channels, + kernel_size=1, + stride=1, + padding=0) + self.proj_out = conv_op(in_channels, + in_channels, + kernel_size=1, + stride=1, + padding=0) + + self.optimized_attention = vae_attention() + + def forward(self, x): + h_ = x + h_ = self.norm(h_) + q = self.q(h_) + k = self.k(h_) + v = self.v(h_) + + h_ = self.optimized_attention(q, k, v) + + h_ = self.proj_out(h_) + + return x+h_ + + +def make_attn(in_channels, attn_type="vanilla", attn_kwargs=None, conv_op=ops.Conv2d): + return AttnBlock(in_channels, conv_op=conv_op) + + +class Model(nn.Module): + def __init__(self, *, ch, out_ch, ch_mult=(1,2,4,8), num_res_blocks, + attn_resolutions, dropout=0.0, resamp_with_conv=True, in_channels, + resolution, use_timestep=True, use_linear_attn=False, attn_type="vanilla"): + super().__init__() + if use_linear_attn: attn_type = "linear" + self.ch = ch + self.temb_ch = self.ch*4 + self.num_resolutions = len(ch_mult) + self.num_res_blocks = num_res_blocks + self.resolution = resolution + self.in_channels = in_channels + + self.use_timestep = use_timestep + if self.use_timestep: + # timestep embedding + self.temb = nn.Module() + self.temb.dense = nn.ModuleList([ + ops.Linear(self.ch, + self.temb_ch), + ops.Linear(self.temb_ch, + self.temb_ch), + ]) + + # downsampling + self.conv_in = ops.Conv2d(in_channels, + self.ch, + kernel_size=3, + stride=1, + padding=1) + + curr_res = resolution + in_ch_mult = (1,)+tuple(ch_mult) + self.down = nn.ModuleList() + for i_level in range(self.num_resolutions): + block = nn.ModuleList() + attn = nn.ModuleList() + block_in = ch*in_ch_mult[i_level] + block_out = ch*ch_mult[i_level] + for i_block in range(self.num_res_blocks): + block.append(ResnetBlock(in_channels=block_in, + out_channels=block_out, + temb_channels=self.temb_ch, + dropout=dropout)) + block_in = block_out + if curr_res in attn_resolutions: + attn.append(make_attn(block_in, attn_type=attn_type)) + down = nn.Module() + down.block = block + down.attn = attn + if i_level != self.num_resolutions-1: + down.downsample = Downsample(block_in, resamp_with_conv) + curr_res = curr_res // 2 + self.down.append(down) + + # middle + self.mid = nn.Module() + self.mid.block_1 = ResnetBlock(in_channels=block_in, + out_channels=block_in, + temb_channels=self.temb_ch, + dropout=dropout) + self.mid.attn_1 = make_attn(block_in, attn_type=attn_type) + self.mid.block_2 = ResnetBlock(in_channels=block_in, + out_channels=block_in, + temb_channels=self.temb_ch, + dropout=dropout) + + # upsampling + self.up = nn.ModuleList() + for i_level in reversed(range(self.num_resolutions)): + block = nn.ModuleList() + attn = nn.ModuleList() + block_out = ch*ch_mult[i_level] + skip_in = ch*ch_mult[i_level] + for i_block in range(self.num_res_blocks+1): + if i_block == self.num_res_blocks: + skip_in = ch*in_ch_mult[i_level] + block.append(ResnetBlock(in_channels=block_in+skip_in, + out_channels=block_out, + temb_channels=self.temb_ch, + dropout=dropout)) + block_in = block_out + if curr_res in attn_resolutions: + attn.append(make_attn(block_in, attn_type=attn_type)) + up = nn.Module() + up.block = block + up.attn = attn + if i_level != 0: + up.upsample = Upsample(block_in, resamp_with_conv) + curr_res = curr_res * 2 + self.up.insert(0, up) # prepend to get consistent order + + # end + self.norm_out = Normalize(block_in) + self.conv_out = ops.Conv2d(block_in, + out_ch, + kernel_size=3, + stride=1, + padding=1) + + def forward(self, x, t=None, context=None): + #assert x.shape[2] == x.shape[3] == self.resolution + if context is not None: + # assume aligned context, cat along channel axis + x = torch.cat((x, context), dim=1) + if self.use_timestep: + # timestep embedding + assert t is not None + temb = get_timestep_embedding(t, self.ch) + temb = self.temb.dense[0](temb) + temb = nonlinearity(temb) + temb = self.temb.dense[1](temb) + else: + temb = None + + # downsampling + hs = [self.conv_in(x)] + for i_level in range(self.num_resolutions): + for i_block in range(self.num_res_blocks): + h = self.down[i_level].block[i_block](hs[-1], temb) + if len(self.down[i_level].attn) > 0: + h = self.down[i_level].attn[i_block](h) + hs.append(h) + if i_level != self.num_resolutions-1: + hs.append(self.down[i_level].downsample(hs[-1])) + + # middle + h = hs[-1] + h = self.mid.block_1(h, temb) + h = self.mid.attn_1(h) + h = self.mid.block_2(h, temb) + + # upsampling + for i_level in reversed(range(self.num_resolutions)): + for i_block in range(self.num_res_blocks+1): + h = self.up[i_level].block[i_block]( + torch.cat([h, hs.pop()], dim=1), temb) + if len(self.up[i_level].attn) > 0: + h = self.up[i_level].attn[i_block](h) + if i_level != 0: + h = self.up[i_level].upsample(h) + + # end + h = self.norm_out(h) + h = nonlinearity(h) + h = self.conv_out(h) + return h + + def get_last_layer(self): + return self.conv_out.weight + + +class Encoder(nn.Module): + def __init__(self, *, ch, out_ch, ch_mult=(1,2,4,8), num_res_blocks, + attn_resolutions, dropout=0.0, resamp_with_conv=True, in_channels, + resolution, z_channels, double_z=True, use_linear_attn=False, attn_type="vanilla", + conv3d=False, time_compress=None, + **ignore_kwargs): + super().__init__() + if use_linear_attn: attn_type = "linear" + self.ch = ch + self.temb_ch = 0 + self.num_resolutions = len(ch_mult) + self.num_res_blocks = num_res_blocks + self.resolution = resolution + self.in_channels = in_channels + + if conv3d: + conv_op = VideoConv3d + mid_attn_conv_op = ops.Conv3d + else: + conv_op = ops.Conv2d + mid_attn_conv_op = ops.Conv2d + + # downsampling + self.conv_in = conv_op(in_channels, + self.ch, + kernel_size=3, + stride=1, + padding=1) + + curr_res = resolution + in_ch_mult = (1,)+tuple(ch_mult) + self.in_ch_mult = in_ch_mult + self.down = nn.ModuleList() + for i_level in range(self.num_resolutions): + block = nn.ModuleList() + attn = nn.ModuleList() + block_in = ch*in_ch_mult[i_level] + block_out = ch*ch_mult[i_level] + for i_block in range(self.num_res_blocks): + block.append(ResnetBlock(in_channels=block_in, + out_channels=block_out, + temb_channels=self.temb_ch, + dropout=dropout, + conv_op=conv_op)) + block_in = block_out + if curr_res in attn_resolutions: + attn.append(make_attn(block_in, attn_type=attn_type, conv_op=conv_op)) + down = nn.Module() + down.block = block + down.attn = attn + if i_level != self.num_resolutions-1: + stride = 2 + if time_compress is not None: + if (self.num_resolutions - 1 - i_level) > math.log2(time_compress): + stride = (1, 2, 2) + down.downsample = Downsample(block_in, resamp_with_conv, stride=stride, conv_op=conv_op) + curr_res = curr_res // 2 + self.down.append(down) + + # middle + self.mid = nn.Module() + self.mid.block_1 = ResnetBlock(in_channels=block_in, + out_channels=block_in, + temb_channels=self.temb_ch, + dropout=dropout, + conv_op=conv_op) + self.mid.attn_1 = make_attn(block_in, attn_type=attn_type, conv_op=mid_attn_conv_op) + self.mid.block_2 = ResnetBlock(in_channels=block_in, + out_channels=block_in, + temb_channels=self.temb_ch, + dropout=dropout, + conv_op=conv_op) + + # end + self.norm_out = Normalize(block_in) + self.conv_out = conv_op(block_in, + 2*z_channels if double_z else z_channels, + kernel_size=3, + stride=1, + padding=1) + + def forward(self, x): + # timestep embedding + temb = None + # downsampling + h = self.conv_in(x) + for i_level in range(self.num_resolutions): + for i_block in range(self.num_res_blocks): + h = self.down[i_level].block[i_block](h, temb) + if len(self.down[i_level].attn) > 0: + h = self.down[i_level].attn[i_block](h) + if i_level != self.num_resolutions-1: + h = self.down[i_level].downsample(h) + + # middle + h = self.mid.block_1(h, temb) + h = self.mid.attn_1(h) + h = self.mid.block_2(h, temb) + + # end + h = self.norm_out(h) + h = nonlinearity(h) + h = self.conv_out(h) + return h + + +class Decoder(nn.Module): + def __init__(self, *, ch, out_ch, ch_mult=(1,2,4,8), num_res_blocks, + attn_resolutions, dropout=0.0, resamp_with_conv=True, in_channels, + resolution, z_channels, give_pre_end=False, tanh_out=False, use_linear_attn=False, + conv_out_op=ops.Conv2d, + resnet_op=ResnetBlock, + attn_op=AttnBlock, + conv3d=False, + time_compress=None, + **ignorekwargs): + super().__init__() + self.ch = ch + self.temb_ch = 0 + self.num_resolutions = len(ch_mult) + self.num_res_blocks = num_res_blocks + self.resolution = resolution + self.in_channels = in_channels + self.give_pre_end = give_pre_end + self.tanh_out = tanh_out + + if conv3d: + conv_op = VideoConv3d + conv_out_op = VideoConv3d + mid_attn_conv_op = ops.Conv3d + else: + conv_op = ops.Conv2d + mid_attn_conv_op = ops.Conv2d + + # compute block_in and curr_res at lowest res + block_in = ch*ch_mult[self.num_resolutions-1] + curr_res = resolution // 2**(self.num_resolutions-1) + self.z_shape = (1,z_channels,curr_res,curr_res) + logging.debug("Working with z of shape {} = {} dimensions.".format( + self.z_shape, np.prod(self.z_shape))) + + # z to block_in + self.conv_in = conv_op(z_channels, + block_in, + kernel_size=3, + stride=1, + padding=1) + + # middle + self.mid = nn.Module() + self.mid.block_1 = resnet_op(in_channels=block_in, + out_channels=block_in, + temb_channels=self.temb_ch, + dropout=dropout, + conv_op=conv_op) + self.mid.attn_1 = attn_op(block_in, conv_op=mid_attn_conv_op) + self.mid.block_2 = resnet_op(in_channels=block_in, + out_channels=block_in, + temb_channels=self.temb_ch, + dropout=dropout, + conv_op=conv_op) + + # upsampling + self.up = nn.ModuleList() + for i_level in reversed(range(self.num_resolutions)): + block = nn.ModuleList() + attn = nn.ModuleList() + block_out = ch*ch_mult[i_level] + for i_block in range(self.num_res_blocks+1): + block.append(resnet_op(in_channels=block_in, + out_channels=block_out, + temb_channels=self.temb_ch, + dropout=dropout, + conv_op=conv_op)) + block_in = block_out + if curr_res in attn_resolutions: + attn.append(attn_op(block_in, conv_op=conv_op)) + up = nn.Module() + up.block = block + up.attn = attn + if i_level != 0: + scale_factor = 2.0 + if time_compress is not None: + if i_level > math.log2(time_compress): + scale_factor = (1.0, 2.0, 2.0) + + up.upsample = Upsample(block_in, resamp_with_conv, conv_op=conv_op, scale_factor=scale_factor) + curr_res = curr_res * 2 + self.up.insert(0, up) # prepend to get consistent order + + # end + self.norm_out = Normalize(block_in) + self.conv_out = conv_out_op(block_in, + out_ch, + kernel_size=3, + stride=1, + padding=1) + + def forward(self, z, **kwargs): + # timestep embedding + temb = None + + # z to block_in + h = self.conv_in(z) + + # middle + h = self.mid.block_1(h, temb, **kwargs) + h = self.mid.attn_1(h, **kwargs) + h = self.mid.block_2(h, temb, **kwargs) + + # upsampling + for i_level in reversed(range(self.num_resolutions)): + for i_block in range(self.num_res_blocks+1): + h = self.up[i_level].block[i_block](h, temb, **kwargs) + if len(self.up[i_level].attn) > 0: + h = self.up[i_level].attn[i_block](h, **kwargs) + if i_level != 0: + h = self.up[i_level].upsample(h) + + # end + if self.give_pre_end: + return h + + h = self.norm_out(h) + h = nonlinearity(h) + h = self.conv_out(h, **kwargs) + if self.tanh_out: + h = torch.tanh(h) + return h diff --git a/ComfyUI/comfy/ldm/modules/diffusionmodules/openaimodel.py b/ComfyUI/comfy/ldm/modules/diffusionmodules/openaimodel.py new file mode 100644 index 0000000000000000000000000000000000000000..4c8d53cac9c2631f6985a3c670128bb4316b11ca --- /dev/null +++ b/ComfyUI/comfy/ldm/modules/diffusionmodules/openaimodel.py @@ -0,0 +1,913 @@ +from abc import abstractmethod + +import torch as th +import torch.nn as nn +import torch.nn.functional as F +from einops import rearrange +import logging + +from .util import ( + checkpoint, + avg_pool_nd, + timestep_embedding, + AlphaBlender, +) +from ..attention import SpatialTransformer, SpatialVideoTransformer, default +from comfy.ldm.util import exists +import comfy.patcher_extension +import comfy.ops +ops = comfy.ops.disable_weight_init + +class TimestepBlock(nn.Module): + """ + Any module where forward() takes timestep embeddings as a second argument. + """ + + @abstractmethod + def forward(self, x, emb): + """ + Apply the module to `x` given `emb` timestep embeddings. + """ + +#This is needed because accelerate makes a copy of transformer_options which breaks "transformer_index" +def forward_timestep_embed(ts, x, emb, context=None, transformer_options={}, output_shape=None, time_context=None, num_video_frames=None, image_only_indicator=None): + for layer in ts: + if isinstance(layer, VideoResBlock): + x = layer(x, emb, num_video_frames, image_only_indicator) + elif isinstance(layer, TimestepBlock): + x = layer(x, emb) + elif isinstance(layer, SpatialVideoTransformer): + x = layer(x, context, time_context, num_video_frames, image_only_indicator, transformer_options) + if "transformer_index" in transformer_options: + transformer_options["transformer_index"] += 1 + elif isinstance(layer, SpatialTransformer): + x = layer(x, context, transformer_options) + if "transformer_index" in transformer_options: + transformer_options["transformer_index"] += 1 + elif isinstance(layer, Upsample): + x = layer(x, output_shape=output_shape) + else: + if "patches" in transformer_options and "forward_timestep_embed_patch" in transformer_options["patches"]: + found_patched = False + for class_type, handler in transformer_options["patches"]["forward_timestep_embed_patch"]: + if isinstance(layer, class_type): + x = handler(layer, x, emb, context, transformer_options, output_shape, time_context, num_video_frames, image_only_indicator) + found_patched = True + break + if found_patched: + continue + x = layer(x) + return x + +class TimestepEmbedSequential(nn.Sequential, TimestepBlock): + """ + A sequential module that passes timestep embeddings to the children that + support it as an extra input. + """ + + def forward(self, *args, **kwargs): + return forward_timestep_embed(self, *args, **kwargs) + +class Upsample(nn.Module): + """ + An upsampling layer with an optional convolution. + :param channels: channels in the inputs and outputs. + :param use_conv: a bool determining if a convolution is applied. + :param dims: determines if the signal is 1D, 2D, or 3D. If 3D, then + upsampling occurs in the inner-two dimensions. + """ + + def __init__(self, channels, use_conv, dims=2, out_channels=None, padding=1, dtype=None, device=None, operations=ops): + super().__init__() + self.channels = channels + self.out_channels = out_channels or channels + self.use_conv = use_conv + self.dims = dims + if use_conv: + self.conv = operations.conv_nd(dims, self.channels, self.out_channels, 3, padding=padding, dtype=dtype, device=device) + + def forward(self, x, output_shape=None): + assert x.shape[1] == self.channels + if self.dims == 3: + shape = [x.shape[2], x.shape[3] * 2, x.shape[4] * 2] + if output_shape is not None: + shape[1] = output_shape[3] + shape[2] = output_shape[4] + else: + shape = [x.shape[2] * 2, x.shape[3] * 2] + if output_shape is not None: + shape[0] = output_shape[2] + shape[1] = output_shape[3] + + x = F.interpolate(x, size=shape, mode="nearest") + if self.use_conv: + x = self.conv(x) + return x + +class Downsample(nn.Module): + """ + A downsampling layer with an optional convolution. + :param channels: channels in the inputs and outputs. + :param use_conv: a bool determining if a convolution is applied. + :param dims: determines if the signal is 1D, 2D, or 3D. If 3D, then + downsampling occurs in the inner-two dimensions. + """ + + def __init__(self, channels, use_conv, dims=2, out_channels=None, padding=1, dtype=None, device=None, operations=ops): + super().__init__() + self.channels = channels + self.out_channels = out_channels or channels + self.use_conv = use_conv + self.dims = dims + stride = 2 if dims != 3 else (1, 2, 2) + if use_conv: + self.op = operations.conv_nd( + dims, self.channels, self.out_channels, 3, stride=stride, padding=padding, dtype=dtype, device=device + ) + else: + assert self.channels == self.out_channels + self.op = avg_pool_nd(dims, kernel_size=stride, stride=stride) + + def forward(self, x): + assert x.shape[1] == self.channels + return self.op(x) + + +class ResBlock(TimestepBlock): + """ + A residual block that can optionally change the number of channels. + :param channels: the number of input channels. + :param emb_channels: the number of timestep embedding channels. + :param dropout: the rate of dropout. + :param out_channels: if specified, the number of out channels. + :param use_conv: if True and out_channels is specified, use a spatial + convolution instead of a smaller 1x1 convolution to change the + channels in the skip connection. + :param dims: determines if the signal is 1D, 2D, or 3D. + :param use_checkpoint: if True, use gradient checkpointing on this module. + :param up: if True, use this block for upsampling. + :param down: if True, use this block for downsampling. + """ + + def __init__( + self, + channels, + emb_channels, + dropout, + out_channels=None, + use_conv=False, + use_scale_shift_norm=False, + dims=2, + use_checkpoint=False, + up=False, + down=False, + kernel_size=3, + exchange_temb_dims=False, + skip_t_emb=False, + dtype=None, + device=None, + operations=ops + ): + super().__init__() + self.channels = channels + self.emb_channels = emb_channels + self.dropout = dropout + self.out_channels = out_channels or channels + self.use_conv = use_conv + self.use_checkpoint = use_checkpoint + self.use_scale_shift_norm = use_scale_shift_norm + self.exchange_temb_dims = exchange_temb_dims + + if isinstance(kernel_size, list): + padding = [k // 2 for k in kernel_size] + else: + padding = kernel_size // 2 + + self.in_layers = nn.Sequential( + operations.GroupNorm(32, channels, dtype=dtype, device=device), + nn.SiLU(), + operations.conv_nd(dims, channels, self.out_channels, kernel_size, padding=padding, dtype=dtype, device=device), + ) + + self.updown = up or down + + if up: + self.h_upd = Upsample(channels, False, dims, dtype=dtype, device=device) + self.x_upd = Upsample(channels, False, dims, dtype=dtype, device=device) + elif down: + self.h_upd = Downsample(channels, False, dims, dtype=dtype, device=device) + self.x_upd = Downsample(channels, False, dims, dtype=dtype, device=device) + else: + self.h_upd = self.x_upd = nn.Identity() + + self.skip_t_emb = skip_t_emb + if self.skip_t_emb: + self.emb_layers = None + self.exchange_temb_dims = False + else: + self.emb_layers = nn.Sequential( + nn.SiLU(), + operations.Linear( + emb_channels, + 2 * self.out_channels if use_scale_shift_norm else self.out_channels, dtype=dtype, device=device + ), + ) + self.out_layers = nn.Sequential( + operations.GroupNorm(32, self.out_channels, dtype=dtype, device=device), + nn.SiLU(), + nn.Dropout(p=dropout), + operations.conv_nd(dims, self.out_channels, self.out_channels, kernel_size, padding=padding, dtype=dtype, device=device) + , + ) + + if self.out_channels == channels: + self.skip_connection = nn.Identity() + elif use_conv: + self.skip_connection = operations.conv_nd( + dims, channels, self.out_channels, kernel_size, padding=padding, dtype=dtype, device=device + ) + else: + self.skip_connection = operations.conv_nd(dims, channels, self.out_channels, 1, dtype=dtype, device=device) + + def forward(self, x, emb): + """ + Apply the block to a Tensor, conditioned on a timestep embedding. + :param x: an [N x C x ...] Tensor of features. + :param emb: an [N x emb_channels] Tensor of timestep embeddings. + :return: an [N x C x ...] Tensor of outputs. + """ + return checkpoint( + self._forward, (x, emb), self.parameters(), self.use_checkpoint + ) + + + def _forward(self, x, emb): + if self.updown: + in_rest, in_conv = self.in_layers[:-1], self.in_layers[-1] + h = in_rest(x) + h = self.h_upd(h) + x = self.x_upd(x) + h = in_conv(h) + else: + h = self.in_layers(x) + + emb_out = None + if not self.skip_t_emb: + emb_out = self.emb_layers(emb).type(h.dtype) + while len(emb_out.shape) < len(h.shape): + emb_out = emb_out[..., None] + if self.use_scale_shift_norm: + out_norm, out_rest = self.out_layers[0], self.out_layers[1:] + h = out_norm(h) + if emb_out is not None: + scale, shift = th.chunk(emb_out, 2, dim=1) + h *= (1 + scale) + h += shift + h = out_rest(h) + else: + if emb_out is not None: + if self.exchange_temb_dims: + emb_out = emb_out.movedim(1, 2) + h = h + emb_out + h = self.out_layers(h) + return self.skip_connection(x) + h + + +class VideoResBlock(ResBlock): + def __init__( + self, + channels: int, + emb_channels: int, + dropout: float, + video_kernel_size=3, + merge_strategy: str = "fixed", + merge_factor: float = 0.5, + out_channels=None, + use_conv: bool = False, + use_scale_shift_norm: bool = False, + dims: int = 2, + use_checkpoint: bool = False, + up: bool = False, + down: bool = False, + dtype=None, + device=None, + operations=ops + ): + super().__init__( + channels, + emb_channels, + dropout, + out_channels=out_channels, + use_conv=use_conv, + use_scale_shift_norm=use_scale_shift_norm, + dims=dims, + use_checkpoint=use_checkpoint, + up=up, + down=down, + dtype=dtype, + device=device, + operations=operations + ) + + self.time_stack = ResBlock( + default(out_channels, channels), + emb_channels, + dropout=dropout, + dims=3, + out_channels=default(out_channels, channels), + use_scale_shift_norm=False, + use_conv=False, + up=False, + down=False, + kernel_size=video_kernel_size, + use_checkpoint=use_checkpoint, + exchange_temb_dims=True, + dtype=dtype, + device=device, + operations=operations + ) + self.time_mixer = AlphaBlender( + alpha=merge_factor, + merge_strategy=merge_strategy, + rearrange_pattern="b t -> b 1 t 1 1", + ) + + def forward( + self, + x: th.Tensor, + emb: th.Tensor, + num_video_frames: int, + image_only_indicator = None, + ) -> th.Tensor: + x = super().forward(x, emb) + + x_mix = rearrange(x, "(b t) c h w -> b c t h w", t=num_video_frames) + x = rearrange(x, "(b t) c h w -> b c t h w", t=num_video_frames) + + x = self.time_stack( + x, rearrange(emb, "(b t) ... -> b t ...", t=num_video_frames) + ) + x = self.time_mixer( + x_spatial=x_mix, x_temporal=x, image_only_indicator=image_only_indicator + ) + x = rearrange(x, "b c t h w -> (b t) c h w") + return x + + +class Timestep(nn.Module): + def __init__(self, dim): + super().__init__() + self.dim = dim + + def forward(self, t): + return timestep_embedding(t, self.dim) + +def apply_control(h, control, name): + if control is not None and name in control and len(control[name]) > 0: + ctrl = control[name].pop() + if ctrl is not None: + try: + h += ctrl + except: + logging.warning("warning control could not be applied {} {}".format(h.shape, ctrl.shape)) + return h + +class UNetModel(nn.Module): + """ + The full UNet model with attention and timestep embedding. + :param in_channels: channels in the input Tensor. + :param model_channels: base channel count for the model. + :param out_channels: channels in the output Tensor. + :param num_res_blocks: number of residual blocks per downsample. + :param dropout: the dropout probability. + :param channel_mult: channel multiplier for each level of the UNet. + :param conv_resample: if True, use learned convolutions for upsampling and + downsampling. + :param dims: determines if the signal is 1D, 2D, or 3D. + :param num_classes: if specified (as an int), then this model will be + class-conditional with `num_classes` classes. + :param use_checkpoint: use gradient checkpointing to reduce memory usage. + :param num_heads: the number of attention heads in each attention layer. + :param num_heads_channels: if specified, ignore num_heads and instead use + a fixed channel width per attention head. + :param num_heads_upsample: works with num_heads to set a different number + of heads for upsampling. Deprecated. + :param use_scale_shift_norm: use a FiLM-like conditioning mechanism. + :param resblock_updown: use residual blocks for up/downsampling. + :param use_new_attention_order: use a different attention pattern for potentially + increased efficiency. + """ + + def __init__( + self, + image_size, + in_channels, + model_channels, + out_channels, + num_res_blocks, + dropout=0, + channel_mult=(1, 2, 4, 8), + conv_resample=True, + dims=2, + num_classes=None, + use_checkpoint=False, + dtype=th.float32, + num_heads=-1, + num_head_channels=-1, + num_heads_upsample=-1, + use_scale_shift_norm=False, + resblock_updown=False, + use_new_attention_order=False, + use_spatial_transformer=False, # custom transformer support + transformer_depth=1, # custom transformer support + context_dim=None, # custom transformer support + n_embed=None, # custom support for prediction of discrete ids into codebook of first stage vq model + legacy=True, + disable_self_attentions=None, + num_attention_blocks=None, + disable_middle_self_attn=False, + use_linear_in_transformer=False, + adm_in_channels=None, + transformer_depth_middle=None, + transformer_depth_output=None, + use_temporal_resblock=False, + use_temporal_attention=False, + time_context_dim=None, + extra_ff_mix_layer=False, + use_spatial_context=False, + merge_strategy=None, + merge_factor=0.0, + video_kernel_size=None, + disable_temporal_crossattention=False, + max_ddpm_temb_period=10000, + attn_precision=None, + device=None, + operations=ops, + ): + super().__init__() + + if context_dim is not None: + assert use_spatial_transformer, 'Fool!! You forgot to use the spatial transformer for your cross-attention conditioning...' + # from omegaconf.listconfig import ListConfig + # if type(context_dim) == ListConfig: + # context_dim = list(context_dim) + + if num_heads_upsample == -1: + num_heads_upsample = num_heads + + if num_heads == -1: + assert num_head_channels != -1, 'Either num_heads or num_head_channels has to be set' + + if num_head_channels == -1: + assert num_heads != -1, 'Either num_heads or num_head_channels has to be set' + + self.in_channels = in_channels + self.model_channels = model_channels + self.out_channels = out_channels + + if isinstance(num_res_blocks, int): + self.num_res_blocks = len(channel_mult) * [num_res_blocks] + else: + if len(num_res_blocks) != len(channel_mult): + raise ValueError("provide num_res_blocks either as an int (globally constant) or " + "as a list/tuple (per-level) with the same length as channel_mult") + self.num_res_blocks = num_res_blocks + + if disable_self_attentions is not None: + # should be a list of booleans, indicating whether to disable self-attention in TransformerBlocks or not + assert len(disable_self_attentions) == len(channel_mult) + if num_attention_blocks is not None: + assert len(num_attention_blocks) == len(self.num_res_blocks) + + transformer_depth = transformer_depth[:] + transformer_depth_output = transformer_depth_output[:] + + self.dropout = dropout + self.channel_mult = channel_mult + self.conv_resample = conv_resample + self.num_classes = num_classes + self.use_checkpoint = use_checkpoint + self.dtype = dtype + self.num_heads = num_heads + self.num_head_channels = num_head_channels + self.num_heads_upsample = num_heads_upsample + self.use_temporal_resblocks = use_temporal_resblock + self.predict_codebook_ids = n_embed is not None + + self.default_num_video_frames = None + + time_embed_dim = model_channels * 4 + self.time_embed = nn.Sequential( + operations.Linear(model_channels, time_embed_dim, dtype=self.dtype, device=device), + nn.SiLU(), + operations.Linear(time_embed_dim, time_embed_dim, dtype=self.dtype, device=device), + ) + + if self.num_classes is not None: + if isinstance(self.num_classes, int): + self.label_emb = nn.Embedding(num_classes, time_embed_dim, dtype=self.dtype, device=device) + elif self.num_classes == "continuous": + logging.debug("setting up linear c_adm embedding layer") + self.label_emb = nn.Linear(1, time_embed_dim) + elif self.num_classes == "sequential": + assert adm_in_channels is not None + self.label_emb = nn.Sequential( + nn.Sequential( + operations.Linear(adm_in_channels, time_embed_dim, dtype=self.dtype, device=device), + nn.SiLU(), + operations.Linear(time_embed_dim, time_embed_dim, dtype=self.dtype, device=device), + ) + ) + else: + raise ValueError() + + self.input_blocks = nn.ModuleList( + [ + TimestepEmbedSequential( + operations.conv_nd(dims, in_channels, model_channels, 3, padding=1, dtype=self.dtype, device=device) + ) + ] + ) + self._feature_size = model_channels + input_block_chans = [model_channels] + ch = model_channels + ds = 1 + + def get_attention_layer( + ch, + num_heads, + dim_head, + depth=1, + context_dim=None, + use_checkpoint=False, + disable_self_attn=False, + ): + if use_temporal_attention: + return SpatialVideoTransformer( + ch, + num_heads, + dim_head, + depth=depth, + context_dim=context_dim, + time_context_dim=time_context_dim, + dropout=dropout, + ff_in=extra_ff_mix_layer, + use_spatial_context=use_spatial_context, + merge_strategy=merge_strategy, + merge_factor=merge_factor, + checkpoint=use_checkpoint, + use_linear=use_linear_in_transformer, + disable_self_attn=disable_self_attn, + disable_temporal_crossattention=disable_temporal_crossattention, + max_time_embed_period=max_ddpm_temb_period, + attn_precision=attn_precision, + dtype=self.dtype, device=device, operations=operations + ) + else: + return SpatialTransformer( + ch, num_heads, dim_head, depth=depth, context_dim=context_dim, + disable_self_attn=disable_self_attn, use_linear=use_linear_in_transformer, + use_checkpoint=use_checkpoint, attn_precision=attn_precision, dtype=self.dtype, device=device, operations=operations + ) + + def get_resblock( + merge_factor, + merge_strategy, + video_kernel_size, + ch, + time_embed_dim, + dropout, + out_channels, + dims, + use_checkpoint, + use_scale_shift_norm, + down=False, + up=False, + dtype=None, + device=None, + operations=ops + ): + if self.use_temporal_resblocks: + return VideoResBlock( + merge_factor=merge_factor, + merge_strategy=merge_strategy, + video_kernel_size=video_kernel_size, + channels=ch, + emb_channels=time_embed_dim, + dropout=dropout, + out_channels=out_channels, + dims=dims, + use_checkpoint=use_checkpoint, + use_scale_shift_norm=use_scale_shift_norm, + down=down, + up=up, + dtype=dtype, + device=device, + operations=operations + ) + else: + return ResBlock( + channels=ch, + emb_channels=time_embed_dim, + dropout=dropout, + out_channels=out_channels, + use_checkpoint=use_checkpoint, + dims=dims, + use_scale_shift_norm=use_scale_shift_norm, + down=down, + up=up, + dtype=dtype, + device=device, + operations=operations + ) + + for level, mult in enumerate(channel_mult): + for nr in range(self.num_res_blocks[level]): + layers = [ + get_resblock( + merge_factor=merge_factor, + merge_strategy=merge_strategy, + video_kernel_size=video_kernel_size, + ch=ch, + time_embed_dim=time_embed_dim, + dropout=dropout, + out_channels=mult * model_channels, + dims=dims, + use_checkpoint=use_checkpoint, + use_scale_shift_norm=use_scale_shift_norm, + dtype=self.dtype, + device=device, + operations=operations, + ) + ] + ch = mult * model_channels + num_transformers = transformer_depth.pop(0) + if num_transformers > 0: + if num_head_channels == -1: + dim_head = ch // num_heads + else: + num_heads = ch // num_head_channels + dim_head = num_head_channels + if legacy: + #num_heads = 1 + dim_head = ch // num_heads if use_spatial_transformer else num_head_channels + if exists(disable_self_attentions): + disabled_sa = disable_self_attentions[level] + else: + disabled_sa = False + + if not exists(num_attention_blocks) or nr < num_attention_blocks[level]: + layers.append(get_attention_layer( + ch, num_heads, dim_head, depth=num_transformers, context_dim=context_dim, + disable_self_attn=disabled_sa, use_checkpoint=use_checkpoint) + ) + self.input_blocks.append(TimestepEmbedSequential(*layers)) + self._feature_size += ch + input_block_chans.append(ch) + if level != len(channel_mult) - 1: + out_ch = ch + self.input_blocks.append( + TimestepEmbedSequential( + get_resblock( + merge_factor=merge_factor, + merge_strategy=merge_strategy, + video_kernel_size=video_kernel_size, + ch=ch, + time_embed_dim=time_embed_dim, + dropout=dropout, + out_channels=out_ch, + dims=dims, + use_checkpoint=use_checkpoint, + use_scale_shift_norm=use_scale_shift_norm, + down=True, + dtype=self.dtype, + device=device, + operations=operations + ) + if resblock_updown + else Downsample( + ch, conv_resample, dims=dims, out_channels=out_ch, dtype=self.dtype, device=device, operations=operations + ) + ) + ) + ch = out_ch + input_block_chans.append(ch) + ds *= 2 + self._feature_size += ch + + if num_head_channels == -1: + dim_head = ch // num_heads + else: + num_heads = ch // num_head_channels + dim_head = num_head_channels + if legacy: + #num_heads = 1 + dim_head = ch // num_heads if use_spatial_transformer else num_head_channels + mid_block = [ + get_resblock( + merge_factor=merge_factor, + merge_strategy=merge_strategy, + video_kernel_size=video_kernel_size, + ch=ch, + time_embed_dim=time_embed_dim, + dropout=dropout, + out_channels=None, + dims=dims, + use_checkpoint=use_checkpoint, + use_scale_shift_norm=use_scale_shift_norm, + dtype=self.dtype, + device=device, + operations=operations + )] + + self.middle_block = None + if transformer_depth_middle >= -1: + if transformer_depth_middle >= 0: + mid_block += [get_attention_layer( # always uses a self-attn + ch, num_heads, dim_head, depth=transformer_depth_middle, context_dim=context_dim, + disable_self_attn=disable_middle_self_attn, use_checkpoint=use_checkpoint + ), + get_resblock( + merge_factor=merge_factor, + merge_strategy=merge_strategy, + video_kernel_size=video_kernel_size, + ch=ch, + time_embed_dim=time_embed_dim, + dropout=dropout, + out_channels=None, + dims=dims, + use_checkpoint=use_checkpoint, + use_scale_shift_norm=use_scale_shift_norm, + dtype=self.dtype, + device=device, + operations=operations + )] + self.middle_block = TimestepEmbedSequential(*mid_block) + self._feature_size += ch + + self.output_blocks = nn.ModuleList([]) + for level, mult in list(enumerate(channel_mult))[::-1]: + for i in range(self.num_res_blocks[level] + 1): + ich = input_block_chans.pop() + layers = [ + get_resblock( + merge_factor=merge_factor, + merge_strategy=merge_strategy, + video_kernel_size=video_kernel_size, + ch=ch + ich, + time_embed_dim=time_embed_dim, + dropout=dropout, + out_channels=model_channels * mult, + dims=dims, + use_checkpoint=use_checkpoint, + use_scale_shift_norm=use_scale_shift_norm, + dtype=self.dtype, + device=device, + operations=operations + ) + ] + ch = model_channels * mult + num_transformers = transformer_depth_output.pop() + if num_transformers > 0: + if num_head_channels == -1: + dim_head = ch // num_heads + else: + num_heads = ch // num_head_channels + dim_head = num_head_channels + if legacy: + #num_heads = 1 + dim_head = ch // num_heads if use_spatial_transformer else num_head_channels + if exists(disable_self_attentions): + disabled_sa = disable_self_attentions[level] + else: + disabled_sa = False + + if not exists(num_attention_blocks) or i < num_attention_blocks[level]: + layers.append( + get_attention_layer( + ch, num_heads, dim_head, depth=num_transformers, context_dim=context_dim, + disable_self_attn=disabled_sa, use_checkpoint=use_checkpoint + ) + ) + if level and i == self.num_res_blocks[level]: + out_ch = ch + layers.append( + get_resblock( + merge_factor=merge_factor, + merge_strategy=merge_strategy, + video_kernel_size=video_kernel_size, + ch=ch, + time_embed_dim=time_embed_dim, + dropout=dropout, + out_channels=out_ch, + dims=dims, + use_checkpoint=use_checkpoint, + use_scale_shift_norm=use_scale_shift_norm, + up=True, + dtype=self.dtype, + device=device, + operations=operations + ) + if resblock_updown + else Upsample(ch, conv_resample, dims=dims, out_channels=out_ch, dtype=self.dtype, device=device, operations=operations) + ) + ds //= 2 + self.output_blocks.append(TimestepEmbedSequential(*layers)) + self._feature_size += ch + + self.out = nn.Sequential( + operations.GroupNorm(32, ch, dtype=self.dtype, device=device), + nn.SiLU(), + operations.conv_nd(dims, model_channels, out_channels, 3, padding=1, dtype=self.dtype, device=device), + ) + if self.predict_codebook_ids: + self.id_predictor = nn.Sequential( + operations.GroupNorm(32, ch, dtype=self.dtype, device=device), + operations.conv_nd(dims, model_channels, n_embed, 1, dtype=self.dtype, device=device), + #nn.LogSoftmax(dim=1) # change to cross_entropy and produce non-normalized logits + ) + + def forward(self, x, timesteps=None, context=None, y=None, control=None, transformer_options={}, **kwargs): + return comfy.patcher_extension.WrapperExecutor.new_class_executor( + self._forward, + self, + comfy.patcher_extension.get_all_wrappers(comfy.patcher_extension.WrappersMP.DIFFUSION_MODEL, transformer_options) + ).execute(x, timesteps, context, y, control, transformer_options, **kwargs) + + def _forward(self, x, timesteps=None, context=None, y=None, control=None, transformer_options={}, **kwargs): + """ + Apply the model to an input batch. + :param x: an [N x C x ...] Tensor of inputs. + :param timesteps: a 1-D batch of timesteps. + :param context: conditioning plugged in via crossattn + :param y: an [N] Tensor of labels, if class-conditional. + :return: an [N x C x ...] Tensor of outputs. + """ + transformer_options["original_shape"] = list(x.shape) + transformer_options["transformer_index"] = 0 + transformer_patches = transformer_options.get("patches", {}) + + num_video_frames = kwargs.get("num_video_frames", self.default_num_video_frames) + image_only_indicator = kwargs.get("image_only_indicator", None) + time_context = kwargs.get("time_context", None) + + assert (y is not None) == ( + self.num_classes is not None + ), "must specify y if and only if the model is class-conditional" + hs = [] + t_emb = timestep_embedding(timesteps, self.model_channels, repeat_only=False).to(x.dtype) + emb = self.time_embed(t_emb) + + if "emb_patch" in transformer_patches: + patch = transformer_patches["emb_patch"] + for p in patch: + emb = p(emb, self.model_channels, transformer_options) + + if self.num_classes is not None: + assert y.shape[0] == x.shape[0] + emb = emb + self.label_emb(y) + + h = x + for id, module in enumerate(self.input_blocks): + transformer_options["block"] = ("input", id) + h = forward_timestep_embed(module, h, emb, context, transformer_options, time_context=time_context, num_video_frames=num_video_frames, image_only_indicator=image_only_indicator) + h = apply_control(h, control, 'input') + if "input_block_patch" in transformer_patches: + patch = transformer_patches["input_block_patch"] + for p in patch: + h = p(h, transformer_options) + + hs.append(h) + if "input_block_patch_after_skip" in transformer_patches: + patch = transformer_patches["input_block_patch_after_skip"] + for p in patch: + h = p(h, transformer_options) + + transformer_options["block"] = ("middle", 0) + if self.middle_block is not None: + h = forward_timestep_embed(self.middle_block, h, emb, context, transformer_options, time_context=time_context, num_video_frames=num_video_frames, image_only_indicator=image_only_indicator) + h = apply_control(h, control, 'middle') + + + for id, module in enumerate(self.output_blocks): + transformer_options["block"] = ("output", id) + hsp = hs.pop() + hsp = apply_control(hsp, control, 'output') + + if "output_block_patch" in transformer_patches: + patch = transformer_patches["output_block_patch"] + for p in patch: + h, hsp = p(h, hsp, transformer_options) + + h = th.cat([h, hsp], dim=1) + del hsp + if len(hs) > 0: + output_shape = hs[-1].shape + else: + output_shape = None + h = forward_timestep_embed(module, h, emb, context, transformer_options, output_shape, time_context=time_context, num_video_frames=num_video_frames, image_only_indicator=image_only_indicator) + h = h.type(x.dtype) + if self.predict_codebook_ids: + return self.id_predictor(h) + else: + return self.out(h) diff --git a/ComfyUI/comfy/ldm/modules/diffusionmodules/upscaling.py b/ComfyUI/comfy/ldm/modules/diffusionmodules/upscaling.py new file mode 100644 index 0000000000000000000000000000000000000000..9dbf1fe7b93b0c7f1a9e8cbf8e89d0b33c282a08 --- /dev/null +++ b/ComfyUI/comfy/ldm/modules/diffusionmodules/upscaling.py @@ -0,0 +1,84 @@ +import torch +import torch.nn as nn +import numpy as np +from functools import partial + +from .util import extract_into_tensor, make_beta_schedule + + +class AbstractLowScaleModel(nn.Module): + # for concatenating a downsampled image to the latent representation + def __init__(self, noise_schedule_config=None): + super(AbstractLowScaleModel, self).__init__() + if noise_schedule_config is not None: + self.register_schedule(**noise_schedule_config) + + def register_schedule(self, beta_schedule="linear", timesteps=1000, + linear_start=1e-4, linear_end=2e-2, cosine_s=8e-3): + betas = make_beta_schedule(beta_schedule, timesteps, linear_start=linear_start, linear_end=linear_end, + cosine_s=cosine_s) + alphas = 1. - betas + alphas_cumprod = np.cumprod(alphas, axis=0) + alphas_cumprod_prev = np.append(1., alphas_cumprod[:-1]) + + timesteps, = betas.shape + self.num_timesteps = int(timesteps) + self.linear_start = linear_start + self.linear_end = linear_end + assert alphas_cumprod.shape[0] == self.num_timesteps, 'alphas have to be defined for each timestep' + + to_torch = partial(torch.tensor, dtype=torch.float32) + + self.register_buffer('betas', to_torch(betas)) + self.register_buffer('alphas_cumprod', to_torch(alphas_cumprod)) + self.register_buffer('alphas_cumprod_prev', to_torch(alphas_cumprod_prev)) + + # calculations for diffusion q(x_t | x_{t-1}) and others + self.register_buffer('sqrt_alphas_cumprod', to_torch(np.sqrt(alphas_cumprod))) + self.register_buffer('sqrt_one_minus_alphas_cumprod', to_torch(np.sqrt(1. - alphas_cumprod))) + self.register_buffer('log_one_minus_alphas_cumprod', to_torch(np.log(1. - alphas_cumprod))) + self.register_buffer('sqrt_recip_alphas_cumprod', to_torch(np.sqrt(1. / alphas_cumprod))) + self.register_buffer('sqrt_recipm1_alphas_cumprod', to_torch(np.sqrt(1. / alphas_cumprod - 1))) + + def q_sample(self, x_start, t, noise=None, seed=None): + if noise is None: + if seed is None: + noise = torch.randn_like(x_start) + else: + noise = torch.randn(x_start.size(), dtype=x_start.dtype, layout=x_start.layout, generator=torch.manual_seed(seed)).to(x_start.device) + return (extract_into_tensor(self.sqrt_alphas_cumprod.to(x_start.device), t, x_start.shape) * x_start + + extract_into_tensor(self.sqrt_one_minus_alphas_cumprod.to(x_start.device), t, x_start.shape) * noise) + + def forward(self, x): + return x, None + + def decode(self, x): + return x + + +class SimpleImageConcat(AbstractLowScaleModel): + # no noise level conditioning + def __init__(self): + super(SimpleImageConcat, self).__init__(noise_schedule_config=None) + self.max_noise_level = 0 + + def forward(self, x): + # fix to constant noise level + return x, torch.zeros(x.shape[0], device=x.device).long() + + +class ImageConcatWithNoiseAugmentation(AbstractLowScaleModel): + def __init__(self, noise_schedule_config, max_noise_level=1000, to_cuda=False): + super().__init__(noise_schedule_config=noise_schedule_config) + self.max_noise_level = max_noise_level + + def forward(self, x, noise_level=None, seed=None): + if noise_level is None: + noise_level = torch.randint(0, self.max_noise_level, (x.shape[0],), device=x.device).long() + else: + assert isinstance(noise_level, torch.Tensor) + z = self.q_sample(x, noise_level, seed=seed) + return z, noise_level + + + diff --git a/ComfyUI/comfy/ldm/modules/diffusionmodules/util.py b/ComfyUI/comfy/ldm/modules/diffusionmodules/util.py new file mode 100644 index 0000000000000000000000000000000000000000..233011dc9524c3d5258c69c673385aaf45329c81 --- /dev/null +++ b/ComfyUI/comfy/ldm/modules/diffusionmodules/util.py @@ -0,0 +1,306 @@ +# adopted from +# https://github.com/openai/improved-diffusion/blob/main/improved_diffusion/gaussian_diffusion.py +# and +# https://github.com/lucidrains/denoising-diffusion-pytorch/blob/7706bdfc6f527f58d33f84b7b522e61e6e3164b3/denoising_diffusion_pytorch/denoising_diffusion_pytorch.py +# and +# https://github.com/openai/guided-diffusion/blob/0ba878e517b276c45d1195eb29f6f5f72659a05b/guided_diffusion/nn.py +# +# thanks! + + +import math +import logging +import torch +import torch.nn as nn +import numpy as np +from einops import repeat, rearrange + +from comfy.ldm.util import instantiate_from_config + +class AlphaBlender(nn.Module): + strategies = ["learned", "fixed", "learned_with_images"] + + def __init__( + self, + alpha: float, + merge_strategy: str = "learned_with_images", + rearrange_pattern: str = "b t -> (b t) 1 1", + ): + super().__init__() + self.merge_strategy = merge_strategy + self.rearrange_pattern = rearrange_pattern + + assert ( + merge_strategy in self.strategies + ), f"merge_strategy needs to be in {self.strategies}" + + if self.merge_strategy == "fixed": + self.register_buffer("mix_factor", torch.Tensor([alpha])) + elif ( + self.merge_strategy == "learned" + or self.merge_strategy == "learned_with_images" + ): + self.register_parameter( + "mix_factor", torch.nn.Parameter(torch.Tensor([alpha])) + ) + else: + raise ValueError(f"unknown merge strategy {self.merge_strategy}") + + def get_alpha(self, image_only_indicator: torch.Tensor, device) -> torch.Tensor: + # skip_time_mix = rearrange(repeat(skip_time_mix, 'b -> (b t) () () ()', t=t), '(b t) 1 ... -> b 1 t ...', t=t) + if self.merge_strategy == "fixed": + # make shape compatible + # alpha = repeat(self.mix_factor, '1 -> b () t () ()', t=t, b=bs) + alpha = self.mix_factor.to(device) + elif self.merge_strategy == "learned": + alpha = torch.sigmoid(self.mix_factor.to(device)) + # make shape compatible + # alpha = repeat(alpha, '1 -> s () ()', s = t * bs) + elif self.merge_strategy == "learned_with_images": + if image_only_indicator is None: + alpha = rearrange(torch.sigmoid(self.mix_factor.to(device)), "... -> ... 1") + else: + alpha = torch.where( + image_only_indicator.bool(), + torch.ones(1, 1, device=image_only_indicator.device), + rearrange(torch.sigmoid(self.mix_factor.to(image_only_indicator.device)), "... -> ... 1"), + ) + alpha = rearrange(alpha, self.rearrange_pattern) + # make shape compatible + # alpha = repeat(alpha, '1 -> s () ()', s = t * bs) + else: + raise NotImplementedError() + return alpha + + def forward( + self, + x_spatial, + x_temporal, + image_only_indicator=None, + ) -> torch.Tensor: + alpha = self.get_alpha(image_only_indicator, x_spatial.device) + x = ( + alpha.to(x_spatial.dtype) * x_spatial + + (1.0 - alpha).to(x_spatial.dtype) * x_temporal + ) + return x + + +def make_beta_schedule(schedule, n_timestep, linear_start=1e-4, linear_end=2e-2, cosine_s=8e-3): + if schedule == "linear": + betas = ( + torch.linspace(linear_start ** 0.5, linear_end ** 0.5, n_timestep, dtype=torch.float64) ** 2 + ) + + elif schedule == "cosine": + timesteps = ( + torch.arange(n_timestep + 1, dtype=torch.float64) / n_timestep + cosine_s + ) + alphas = timesteps / (1 + cosine_s) * np.pi / 2 + alphas = torch.cos(alphas).pow(2) + alphas = alphas / alphas[0] + betas = 1 - alphas[1:] / alphas[:-1] + betas = torch.clamp(betas, min=0, max=0.999) + + elif schedule == "squaredcos_cap_v2": # used for karlo prior + # return early + return betas_for_alpha_bar( + n_timestep, + lambda t: math.cos((t + 0.008) / 1.008 * math.pi / 2) ** 2, + ) + + elif schedule == "sqrt_linear": + betas = torch.linspace(linear_start, linear_end, n_timestep, dtype=torch.float64) + elif schedule == "sqrt": + betas = torch.linspace(linear_start, linear_end, n_timestep, dtype=torch.float64) ** 0.5 + else: + raise ValueError(f"schedule '{schedule}' unknown.") + return betas + + +def make_ddim_timesteps(ddim_discr_method, num_ddim_timesteps, num_ddpm_timesteps, verbose=True): + if ddim_discr_method == 'uniform': + c = num_ddpm_timesteps // num_ddim_timesteps + ddim_timesteps = np.asarray(list(range(0, num_ddpm_timesteps, c))) + elif ddim_discr_method == 'quad': + ddim_timesteps = ((np.linspace(0, np.sqrt(num_ddpm_timesteps * .8), num_ddim_timesteps)) ** 2).astype(int) + else: + raise NotImplementedError(f'There is no ddim discretization method called "{ddim_discr_method}"') + + # assert ddim_timesteps.shape[0] == num_ddim_timesteps + # add one to get the final alpha values right (the ones from first scale to data during sampling) + steps_out = ddim_timesteps + 1 + if verbose: + logging.info(f'Selected timesteps for ddim sampler: {steps_out}') + return steps_out + + +def make_ddim_sampling_parameters(alphacums, ddim_timesteps, eta, verbose=True): + # select alphas for computing the variance schedule + alphas = alphacums[ddim_timesteps] + alphas_prev = np.asarray([alphacums[0]] + alphacums[ddim_timesteps[:-1]].tolist()) + + # according the the formula provided in https://arxiv.org/abs/2010.02502 + sigmas = eta * np.sqrt((1 - alphas_prev) / (1 - alphas) * (1 - alphas / alphas_prev)) + if verbose: + logging.info(f'Selected alphas for ddim sampler: a_t: {alphas}; a_(t-1): {alphas_prev}') + logging.info(f'For the chosen value of eta, which is {eta}, ' + f'this results in the following sigma_t schedule for ddim sampler {sigmas}') + return sigmas, alphas, alphas_prev + + +def betas_for_alpha_bar(num_diffusion_timesteps, alpha_bar, max_beta=0.999): + """ + Create a beta schedule that discretizes the given alpha_t_bar function, + which defines the cumulative product of (1-beta) over time from t = [0,1]. + :param num_diffusion_timesteps: the number of betas to produce. + :param alpha_bar: a lambda that takes an argument t from 0 to 1 and + produces the cumulative product of (1-beta) up to that + part of the diffusion process. + :param max_beta: the maximum beta to use; use values lower than 1 to + prevent singularities. + """ + betas = [] + for i in range(num_diffusion_timesteps): + t1 = i / num_diffusion_timesteps + t2 = (i + 1) / num_diffusion_timesteps + betas.append(min(1 - alpha_bar(t2) / alpha_bar(t1), max_beta)) + return np.array(betas) + + +def extract_into_tensor(a, t, x_shape): + b, *_ = t.shape + out = a.gather(-1, t) + return out.reshape(b, *((1,) * (len(x_shape) - 1))) + + +def checkpoint(func, inputs, params, flag): + """ + Evaluate a function without caching intermediate activations, allowing for + reduced memory at the expense of extra compute in the backward pass. + :param func: the function to evaluate. + :param inputs: the argument sequence to pass to `func`. + :param params: a sequence of parameters `func` depends on but does not + explicitly take as arguments. + :param flag: if False, disable gradient checkpointing. + """ + if flag: + args = tuple(inputs) + tuple(params) + return CheckpointFunction.apply(func, len(inputs), *args) + else: + return func(*inputs) + + +class CheckpointFunction(torch.autograd.Function): + @staticmethod + def forward(ctx, run_function, length, *args): + ctx.run_function = run_function + ctx.input_tensors = list(args[:length]) + ctx.input_params = list(args[length:]) + ctx.gpu_autocast_kwargs = {"enabled": torch.is_autocast_enabled(), + "dtype": torch.get_autocast_gpu_dtype(), + "cache_enabled": torch.is_autocast_cache_enabled()} + with torch.no_grad(): + output_tensors = ctx.run_function(*ctx.input_tensors) + return output_tensors + + @staticmethod + def backward(ctx, *output_grads): + ctx.input_tensors = [x.detach().requires_grad_(True) for x in ctx.input_tensors] + with torch.enable_grad(), \ + torch.cuda.amp.autocast(**ctx.gpu_autocast_kwargs): + # Fixes a bug where the first op in run_function modifies the + # Tensor storage in place, which is not allowed for detach()'d + # Tensors. + shallow_copies = [x.view_as(x) for x in ctx.input_tensors] + output_tensors = ctx.run_function(*shallow_copies) + input_grads = torch.autograd.grad( + output_tensors, + ctx.input_tensors + ctx.input_params, + output_grads, + allow_unused=True, + ) + del ctx.input_tensors + del ctx.input_params + del output_tensors + return (None, None) + input_grads + + +def timestep_embedding(timesteps, dim, max_period=10000, repeat_only=False): + """ + Create sinusoidal timestep embeddings. + :param timesteps: a 1-D Tensor of N indices, one per batch element. + These may be fractional. + :param dim: the dimension of the output. + :param max_period: controls the minimum frequency of the embeddings. + :return: an [N x dim] Tensor of positional embeddings. + """ + if not repeat_only: + half = dim // 2 + freqs = torch.exp( + -math.log(max_period) * torch.arange(start=0, end=half, dtype=torch.float32, device=timesteps.device) / half + ) + args = timesteps[:, None].float() * freqs[None] + embedding = torch.cat([torch.cos(args), torch.sin(args)], dim=-1) + if dim % 2: + embedding = torch.cat([embedding, torch.zeros_like(embedding[:, :1])], dim=-1) + else: + embedding = repeat(timesteps, 'b -> b d', d=dim) + return embedding + + +def zero_module(module): + """ + Zero out the parameters of a module and return it. + """ + for p in module.parameters(): + p.detach().zero_() + return module + + +def scale_module(module, scale): + """ + Scale the parameters of a module and return it. + """ + for p in module.parameters(): + p.detach().mul_(scale) + return module + + +def mean_flat(tensor): + """ + Take the mean over all non-batch dimensions. + """ + return tensor.mean(dim=list(range(1, len(tensor.shape)))) + + +def avg_pool_nd(dims, *args, **kwargs): + """ + Create a 1D, 2D, or 3D average pooling module. + """ + if dims == 1: + return nn.AvgPool1d(*args, **kwargs) + elif dims == 2: + return nn.AvgPool2d(*args, **kwargs) + elif dims == 3: + return nn.AvgPool3d(*args, **kwargs) + raise ValueError(f"unsupported dimensions: {dims}") + + +class HybridConditioner(nn.Module): + + def __init__(self, c_concat_config, c_crossattn_config): + super().__init__() + self.concat_conditioner = instantiate_from_config(c_concat_config) + self.crossattn_conditioner = instantiate_from_config(c_crossattn_config) + + def forward(self, c_concat, c_crossattn): + c_concat = self.concat_conditioner(c_concat) + c_crossattn = self.crossattn_conditioner(c_crossattn) + return {'c_concat': [c_concat], 'c_crossattn': [c_crossattn]} + + +def noise_like(shape, device, repeat=False): + repeat_noise = lambda: torch.randn((1, *shape[1:]), device=device).repeat(shape[0], *((1,) * (len(shape) - 1))) + noise = lambda: torch.randn(shape, device=device) + return repeat_noise() if repeat else noise() diff --git a/ComfyUI/comfy/ldm/modules/distributions/__init__.py b/ComfyUI/comfy/ldm/modules/distributions/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/ComfyUI/comfy/ldm/modules/distributions/distributions.py b/ComfyUI/comfy/ldm/modules/distributions/distributions.py new file mode 100644 index 0000000000000000000000000000000000000000..df987c5ec3faaba7da2b02029ad413134bfa08ad --- /dev/null +++ b/ComfyUI/comfy/ldm/modules/distributions/distributions.py @@ -0,0 +1,92 @@ +import torch +import numpy as np + + +class AbstractDistribution: + def sample(self): + raise NotImplementedError() + + def mode(self): + raise NotImplementedError() + + +class DiracDistribution(AbstractDistribution): + def __init__(self, value): + self.value = value + + def sample(self): + return self.value + + def mode(self): + return self.value + + +class DiagonalGaussianDistribution(object): + def __init__(self, parameters, deterministic=False): + self.parameters = parameters + self.mean, self.logvar = torch.chunk(parameters, 2, dim=1) + self.logvar = torch.clamp(self.logvar, -30.0, 20.0) + self.deterministic = deterministic + self.std = torch.exp(0.5 * self.logvar) + self.var = torch.exp(self.logvar) + if self.deterministic: + self.var = self.std = torch.zeros_like(self.mean, device=self.parameters.device) + + def sample(self): + x = self.mean + self.std * torch.randn(self.mean.shape, device=self.parameters.device) + return x + + def kl(self, other=None): + if self.deterministic: + return torch.Tensor([0.]) + else: + if other is None: + return 0.5 * torch.sum(torch.pow(self.mean, 2) + + self.var - 1.0 - self.logvar, + dim=[1, 2, 3]) + else: + return 0.5 * torch.sum( + torch.pow(self.mean - other.mean, 2) / other.var + + self.var / other.var - 1.0 - self.logvar + other.logvar, + dim=[1, 2, 3]) + + def nll(self, sample, dims=[1,2,3]): + if self.deterministic: + return torch.Tensor([0.]) + logtwopi = np.log(2.0 * np.pi) + return 0.5 * torch.sum( + logtwopi + self.logvar + torch.pow(sample - self.mean, 2) / self.var, + dim=dims) + + def mode(self): + return self.mean + + +def normal_kl(mean1, logvar1, mean2, logvar2): + """ + source: https://github.com/openai/guided-diffusion/blob/27c20a8fab9cb472df5d6bdd6c8d11c8f430b924/guided_diffusion/losses.py#L12 + Compute the KL divergence between two gaussians. + Shapes are automatically broadcasted, so batches can be compared to + scalars, among other use cases. + """ + tensor = None + for obj in (mean1, logvar1, mean2, logvar2): + if isinstance(obj, torch.Tensor): + tensor = obj + break + assert tensor is not None, "at least one argument must be a Tensor" + + # Force variances to be Tensors. Broadcasting helps convert scalars to + # Tensors, but it does not work for torch.exp(). + logvar1, logvar2 = [ + x if isinstance(x, torch.Tensor) else torch.tensor(x).to(tensor) + for x in (logvar1, logvar2) + ] + + return 0.5 * ( + -1.0 + + logvar2 + - logvar1 + + torch.exp(logvar1 - logvar2) + + ((mean1 - mean2) ** 2) * torch.exp(-logvar2) + ) diff --git a/ComfyUI/comfy/ldm/modules/encoders/__init__.py b/ComfyUI/comfy/ldm/modules/encoders/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/ComfyUI/comfy/ldm/modules/encoders/noise_aug_modules.py b/ComfyUI/comfy/ldm/modules/encoders/noise_aug_modules.py new file mode 100644 index 0000000000000000000000000000000000000000..a5d8660301636fde75808cba50afa539cf1162e0 --- /dev/null +++ b/ComfyUI/comfy/ldm/modules/encoders/noise_aug_modules.py @@ -0,0 +1,35 @@ +from ..diffusionmodules.upscaling import ImageConcatWithNoiseAugmentation +from ..diffusionmodules.openaimodel import Timestep +import torch + +class CLIPEmbeddingNoiseAugmentation(ImageConcatWithNoiseAugmentation): + def __init__(self, *args, clip_stats_path=None, timestep_dim=256, **kwargs): + super().__init__(*args, **kwargs) + if clip_stats_path is None: + clip_mean, clip_std = torch.zeros(timestep_dim), torch.ones(timestep_dim) + else: + clip_mean, clip_std = torch.load(clip_stats_path, map_location="cpu") + self.register_buffer("data_mean", clip_mean[None, :], persistent=False) + self.register_buffer("data_std", clip_std[None, :], persistent=False) + self.time_embed = Timestep(timestep_dim) + + def scale(self, x): + # re-normalize to centered mean and unit variance + x = (x - self.data_mean.to(x.device)) * 1. / self.data_std.to(x.device) + return x + + def unscale(self, x): + # back to original data stats + x = (x * self.data_std.to(x.device)) + self.data_mean.to(x.device) + return x + + def forward(self, x, noise_level=None, seed=None): + if noise_level is None: + noise_level = torch.randint(0, self.max_noise_level, (x.shape[0],), device=x.device).long() + else: + assert isinstance(noise_level, torch.Tensor) + x = self.scale(x) + z = self.q_sample(x, noise_level, seed=seed) + z = self.unscale(z) + noise_level = self.time_embed(noise_level) + return z, noise_level diff --git a/ComfyUI/comfy/ldm/pixart/blocks.py b/ComfyUI/comfy/ldm/pixart/blocks.py new file mode 100644 index 0000000000000000000000000000000000000000..2225076e5754d08cd618014469c2464bad055334 --- /dev/null +++ b/ComfyUI/comfy/ldm/pixart/blocks.py @@ -0,0 +1,380 @@ +# Based on: +# https://github.com/PixArt-alpha/PixArt-alpha [Apache 2.0 license] +# https://github.com/PixArt-alpha/PixArt-sigma [Apache 2.0 license] +import torch +import torch.nn as nn +import torch.nn.functional as F +from einops import rearrange + +from comfy.ldm.modules.diffusionmodules.mmdit import TimestepEmbedder, Mlp, timestep_embedding +from comfy.ldm.modules.attention import optimized_attention + +# if model_management.xformers_enabled(): +# import xformers.ops +# if int((xformers.__version__).split(".")[2].split("+")[0]) >= 28: +# block_diagonal_mask_from_seqlens = xformers.ops.fmha.attn_bias.BlockDiagonalMask.from_seqlens +# else: +# block_diagonal_mask_from_seqlens = xformers.ops.fmha.BlockDiagonalMask.from_seqlens + +def modulate(x, shift, scale): + return x * (1 + scale.unsqueeze(1)) + shift.unsqueeze(1) + +def t2i_modulate(x, shift, scale): + return x * (1 + scale) + shift + +class MultiHeadCrossAttention(nn.Module): + def __init__(self, d_model, num_heads, attn_drop=0., proj_drop=0., dtype=None, device=None, operations=None, **kwargs): + super(MultiHeadCrossAttention, self).__init__() + assert d_model % num_heads == 0, "d_model must be divisible by num_heads" + + self.d_model = d_model + self.num_heads = num_heads + self.head_dim = d_model // num_heads + + self.q_linear = operations.Linear(d_model, d_model, dtype=dtype, device=device) + self.kv_linear = operations.Linear(d_model, d_model*2, dtype=dtype, device=device) + self.attn_drop = nn.Dropout(attn_drop) + self.proj = operations.Linear(d_model, d_model, dtype=dtype, device=device) + self.proj_drop = nn.Dropout(proj_drop) + + def forward(self, x, cond, mask=None): + # query/value: img tokens; key: condition; mask: if padding tokens + B, N, C = x.shape + + q = self.q_linear(x).view(1, -1, self.num_heads, self.head_dim) + kv = self.kv_linear(cond).view(1, -1, 2, self.num_heads, self.head_dim) + k, v = kv.unbind(2) + + assert mask is None # TODO? + # # TODO: xformers needs separate mask logic here + # if model_management.xformers_enabled(): + # attn_bias = None + # if mask is not None: + # attn_bias = block_diagonal_mask_from_seqlens([N] * B, mask) + # x = xformers.ops.memory_efficient_attention(q, k, v, p=0, attn_bias=attn_bias) + # else: + # q, k, v = map(lambda t: t.transpose(1, 2), (q, k, v),) + # attn_mask = None + # mask = torch.ones(()) + # if mask is not None and len(mask) > 1: + # # Create equivalent of xformer diagonal block mask, still only correct for square masks + # # But depth doesn't matter as tensors can expand in that dimension + # attn_mask_template = torch.ones( + # [q.shape[2] // B, mask[0]], + # dtype=torch.bool, + # device=q.device + # ) + # attn_mask = torch.block_diag(attn_mask_template) + # + # # create a mask on the diagonal for each mask in the batch + # for _ in range(B - 1): + # attn_mask = torch.block_diag(attn_mask, attn_mask_template) + # x = optimized_attention(q, k, v, self.num_heads, mask=attn_mask, skip_reshape=True) + + x = optimized_attention(q.view(B, -1, C), k.view(B, -1, C), v.view(B, -1, C), self.num_heads, mask=None) + x = self.proj(x) + x = self.proj_drop(x) + return x + + +class AttentionKVCompress(nn.Module): + """Multi-head Attention block with KV token compression and qk norm.""" + def __init__(self, dim, num_heads=8, qkv_bias=True, sampling='conv', sr_ratio=1, qk_norm=False, dtype=None, device=None, operations=None, **kwargs): + """ + Args: + dim (int): Number of input channels. + num_heads (int): Number of attention heads. + qkv_bias (bool: If True, add a learnable bias to query, key, value. + """ + super().__init__() + assert dim % num_heads == 0, 'dim should be divisible by num_heads' + self.num_heads = num_heads + self.head_dim = dim // num_heads + self.scale = self.head_dim ** -0.5 + + self.qkv = operations.Linear(dim, dim * 3, bias=qkv_bias, dtype=dtype, device=device) + self.proj = operations.Linear(dim, dim, dtype=dtype, device=device) + + self.sampling=sampling # ['conv', 'ave', 'uniform', 'uniform_every'] + self.sr_ratio = sr_ratio + if sr_ratio > 1 and sampling == 'conv': + # Avg Conv Init. + self.sr = operations.Conv2d(dim, dim, groups=dim, kernel_size=sr_ratio, stride=sr_ratio, dtype=dtype, device=device) + # self.sr.weight.data.fill_(1/sr_ratio**2) + # self.sr.bias.data.zero_() + self.norm = operations.LayerNorm(dim, dtype=dtype, device=device) + if qk_norm: + self.q_norm = operations.LayerNorm(dim, dtype=dtype, device=device) + self.k_norm = operations.LayerNorm(dim, dtype=dtype, device=device) + else: + self.q_norm = nn.Identity() + self.k_norm = nn.Identity() + + def downsample_2d(self, tensor, H, W, scale_factor, sampling=None): + if sampling is None or scale_factor == 1: + return tensor + B, N, C = tensor.shape + + if sampling == 'uniform_every': + return tensor[:, ::scale_factor], int(N // scale_factor) + + tensor = tensor.reshape(B, H, W, C).permute(0, 3, 1, 2) + new_H, new_W = int(H / scale_factor), int(W / scale_factor) + new_N = new_H * new_W + + if sampling == 'ave': + tensor = F.interpolate( + tensor, scale_factor=1 / scale_factor, mode='nearest' + ).permute(0, 2, 3, 1) + elif sampling == 'uniform': + tensor = tensor[:, :, ::scale_factor, ::scale_factor].permute(0, 2, 3, 1) + elif sampling == 'conv': + tensor = self.sr(tensor).reshape(B, C, -1).permute(0, 2, 1) + tensor = self.norm(tensor) + else: + raise ValueError + + return tensor.reshape(B, new_N, C).contiguous(), new_N + + def forward(self, x, mask=None, HW=None, block_id=None): + B, N, C = x.shape # 2 4096 1152 + new_N = N + if HW is None: + H = W = int(N ** 0.5) + else: + H, W = HW + qkv = self.qkv(x).reshape(B, N, 3, C) + + q, k, v = qkv.unbind(2) + q = self.q_norm(q) + k = self.k_norm(k) + + # KV compression + if self.sr_ratio > 1: + k, new_N = self.downsample_2d(k, H, W, self.sr_ratio, sampling=self.sampling) + v, new_N = self.downsample_2d(v, H, W, self.sr_ratio, sampling=self.sampling) + + q = q.reshape(B, N, self.num_heads, C // self.num_heads) + k = k.reshape(B, new_N, self.num_heads, C // self.num_heads) + v = v.reshape(B, new_N, self.num_heads, C // self.num_heads) + + if mask is not None: + raise NotImplementedError("Attn mask logic not added for self attention") + + # This is never called at the moment + # attn_bias = None + # if mask is not None: + # attn_bias = torch.zeros([B * self.num_heads, q.shape[1], k.shape[1]], dtype=q.dtype, device=q.device) + # attn_bias.masked_fill_(mask.squeeze(1).repeat(self.num_heads, 1, 1) == 0, float('-inf')) + + # attention 2 + q, k, v = map(lambda t: t.transpose(1, 2), (q, k, v),) + x = optimized_attention(q, k, v, self.num_heads, mask=None, skip_reshape=True) + + x = x.view(B, N, C) + x = self.proj(x) + return x + + +class FinalLayer(nn.Module): + """ + The final layer of PixArt. + """ + def __init__(self, hidden_size, patch_size, out_channels, dtype=None, device=None, operations=None): + super().__init__() + self.norm_final = operations.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6, 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) + ) + + def forward(self, x, c): + shift, scale = self.adaLN_modulation(c).chunk(2, dim=1) + x = modulate(self.norm_final(x), shift, scale) + x = self.linear(x) + return x + +class T2IFinalLayer(nn.Module): + """ + The final layer of PixArt. + """ + def __init__(self, hidden_size, patch_size, out_channels, dtype=None, device=None, operations=None): + super().__init__() + self.norm_final = operations.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6, dtype=dtype, device=device) + self.linear = operations.Linear(hidden_size, patch_size * patch_size * out_channels, bias=True, dtype=dtype, device=device) + self.scale_shift_table = nn.Parameter(torch.randn(2, hidden_size) / hidden_size ** 0.5) + self.out_channels = out_channels + + def forward(self, x, t): + shift, scale = (self.scale_shift_table[None].to(dtype=x.dtype, device=x.device) + t[:, None]).chunk(2, dim=1) + x = t2i_modulate(self.norm_final(x), shift, scale) + x = self.linear(x) + return x + + +class MaskFinalLayer(nn.Module): + """ + The final layer of PixArt. + """ + def __init__(self, final_hidden_size, c_emb_size, patch_size, out_channels, dtype=None, device=None, operations=None): + super().__init__() + self.norm_final = operations.LayerNorm(final_hidden_size, elementwise_affine=False, eps=1e-6, dtype=dtype, device=device) + self.linear = operations.Linear(final_hidden_size, patch_size * patch_size * out_channels, bias=True, dtype=dtype, device=device) + self.adaLN_modulation = nn.Sequential( + nn.SiLU(), + operations.Linear(c_emb_size, 2 * final_hidden_size, bias=True, dtype=dtype, device=device) + ) + def forward(self, x, t): + shift, scale = self.adaLN_modulation(t).chunk(2, dim=1) + x = modulate(self.norm_final(x), shift, scale) + x = self.linear(x) + return x + + +class DecoderLayer(nn.Module): + """ + The final layer of PixArt. + """ + def __init__(self, hidden_size, decoder_hidden_size, dtype=None, device=None, operations=None): + super().__init__() + self.norm_decoder = operations.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6, dtype=dtype, device=device) + self.linear = operations.Linear(hidden_size, decoder_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, t): + shift, scale = self.adaLN_modulation(t).chunk(2, dim=1) + x = modulate(self.norm_decoder(x), shift, scale) + x = self.linear(x) + return x + + +class SizeEmbedder(TimestepEmbedder): + """ + Embeds scalar timesteps into vector representations. + """ + def __init__(self, hidden_size, frequency_embedding_size=256, dtype=None, device=None, operations=None): + super().__init__(hidden_size=hidden_size, frequency_embedding_size=frequency_embedding_size, operations=operations) + self.mlp = nn.Sequential( + operations.Linear(frequency_embedding_size, hidden_size, bias=True, dtype=dtype, device=device), + nn.SiLU(), + operations.Linear(hidden_size, hidden_size, bias=True, dtype=dtype, device=device), + ) + self.frequency_embedding_size = frequency_embedding_size + self.outdim = hidden_size + + def forward(self, s, bs): + if s.ndim == 1: + s = s[:, None] + assert s.ndim == 2 + if s.shape[0] != bs: + s = s.repeat(bs//s.shape[0], 1) + assert s.shape[0] == bs + b, dims = s.shape[0], s.shape[1] + s = rearrange(s, "b d -> (b d)") + s_freq = timestep_embedding(s, self.frequency_embedding_size) + s_emb = self.mlp(s_freq.to(s.dtype)) + s_emb = rearrange(s_emb, "(b d) d2 -> b (d d2)", b=b, d=dims, d2=self.outdim) + return s_emb + + +class LabelEmbedder(nn.Module): + """ + Embeds class labels into vector representations. Also handles label dropout for classifier-free guidance. + """ + def __init__(self, num_classes, hidden_size, dropout_prob, dtype=None, device=None, operations=None): + super().__init__() + use_cfg_embedding = dropout_prob > 0 + self.embedding_table = operations.Embedding(num_classes + use_cfg_embedding, hidden_size, dtype=dtype, device=device), + self.num_classes = num_classes + self.dropout_prob = dropout_prob + + def token_drop(self, labels, force_drop_ids=None): + """ + Drops labels to enable classifier-free guidance. + """ + if force_drop_ids is None: + drop_ids = torch.rand(labels.shape[0]).cuda() < self.dropout_prob + else: + drop_ids = force_drop_ids == 1 + labels = torch.where(drop_ids, self.num_classes, labels) + return labels + + def forward(self, labels, train, force_drop_ids=None): + use_dropout = self.dropout_prob > 0 + if (train and use_dropout) or (force_drop_ids is not None): + labels = self.token_drop(labels, force_drop_ids) + embeddings = self.embedding_table(labels) + return embeddings + + +class CaptionEmbedder(nn.Module): + """ + Embeds class labels into vector representations. Also handles label dropout for classifier-free guidance. + """ + def __init__(self, in_channels, hidden_size, uncond_prob, act_layer=nn.GELU(approximate='tanh'), token_num=120, dtype=None, device=None, operations=None): + super().__init__() + self.y_proj = Mlp( + in_features=in_channels, hidden_features=hidden_size, out_features=hidden_size, act_layer=act_layer, + dtype=dtype, device=device, operations=operations, + ) + self.register_buffer("y_embedding", nn.Parameter(torch.randn(token_num, in_channels) / in_channels ** 0.5)) + self.uncond_prob = uncond_prob + + def token_drop(self, caption, force_drop_ids=None): + """ + Drops labels to enable classifier-free guidance. + """ + if force_drop_ids is None: + drop_ids = torch.rand(caption.shape[0]).cuda() < self.uncond_prob + else: + drop_ids = force_drop_ids == 1 + caption = torch.where(drop_ids[:, None, None, None], self.y_embedding, caption) + return caption + + def forward(self, caption, train, force_drop_ids=None): + if train: + assert caption.shape[2:] == self.y_embedding.shape + use_dropout = self.uncond_prob > 0 + if (train and use_dropout) or (force_drop_ids is not None): + caption = self.token_drop(caption, force_drop_ids) + caption = self.y_proj(caption) + return caption + + +class CaptionEmbedderDoubleBr(nn.Module): + """ + Embeds class labels into vector representations. Also handles label dropout for classifier-free guidance. + """ + def __init__(self, in_channels, hidden_size, uncond_prob, act_layer=nn.GELU(approximate='tanh'), token_num=120, dtype=None, device=None, operations=None): + super().__init__() + self.proj = Mlp( + in_features=in_channels, hidden_features=hidden_size, out_features=hidden_size, act_layer=act_layer, + dtype=dtype, device=device, operations=operations, + ) + self.embedding = nn.Parameter(torch.randn(1, in_channels) / 10 ** 0.5) + self.y_embedding = nn.Parameter(torch.randn(token_num, in_channels) / 10 ** 0.5) + self.uncond_prob = uncond_prob + + def token_drop(self, global_caption, caption, force_drop_ids=None): + """ + Drops labels to enable classifier-free guidance. + """ + if force_drop_ids is None: + drop_ids = torch.rand(global_caption.shape[0]).cuda() < self.uncond_prob + else: + drop_ids = force_drop_ids == 1 + global_caption = torch.where(drop_ids[:, None], self.embedding, global_caption) + caption = torch.where(drop_ids[:, None, None, None], self.y_embedding, caption) + return global_caption, caption + + def forward(self, caption, train, force_drop_ids=None): + assert caption.shape[2: ] == self.y_embedding.shape + global_caption = caption.mean(dim=2).squeeze() + use_dropout = self.uncond_prob > 0 + if (train and use_dropout) or (force_drop_ids is not None): + global_caption, caption = self.token_drop(global_caption, caption, force_drop_ids) + y_embed = self.proj(global_caption) + return y_embed, caption diff --git a/ComfyUI/comfy/ldm/wan/vae.py b/ComfyUI/comfy/ldm/wan/vae.py new file mode 100644 index 0000000000000000000000000000000000000000..6b07840fc2eefbf3a7f31331f2cb7644b716e465 --- /dev/null +++ b/ComfyUI/comfy/ldm/wan/vae.py @@ -0,0 +1,517 @@ +# original version: https://github.com/Wan-Video/Wan2.1/blob/main/wan/modules/vae.py +# Copyright 2024-2025 The Alibaba Wan Team Authors. All rights reserved. + +import torch +import torch.nn as nn +import torch.nn.functional as F +from einops import rearrange +from comfy.ldm.modules.diffusionmodules.model import vae_attention + +import comfy.ops +ops = comfy.ops.disable_weight_init + +CACHE_T = 2 + + +class CausalConv3d(ops.Conv3d): + """ + Causal 3d convolusion. + """ + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self._padding = (self.padding[2], self.padding[2], self.padding[1], + self.padding[1], 2 * self.padding[0], 0) + self.padding = (0, 0, 0) + + def forward(self, x, cache_x=None): + padding = list(self._padding) + if cache_x is not None and self._padding[4] > 0: + cache_x = cache_x.to(x.device) + x = torch.cat([cache_x, x], dim=2) + padding[4] -= cache_x.shape[2] + x = F.pad(x, padding) + + return super().forward(x) + + +class RMS_norm(nn.Module): + + def __init__(self, dim, channel_first=True, images=True, bias=False): + super().__init__() + broadcastable_dims = (1, 1, 1) if not images else (1, 1) + shape = (dim, *broadcastable_dims) if channel_first else (dim,) + + self.channel_first = channel_first + self.scale = dim**0.5 + self.gamma = nn.Parameter(torch.ones(shape)) + self.bias = nn.Parameter(torch.zeros(shape)) if bias else None + + def forward(self, x): + return F.normalize( + x, dim=(1 if self.channel_first else -1)) * self.scale * self.gamma.to(x) + (self.bias.to(x) if self.bias is not None else 0) + + +class Resample(nn.Module): + + def __init__(self, dim, mode): + assert mode in ('none', 'upsample2d', 'upsample3d', 'downsample2d', + 'downsample3d') + super().__init__() + self.dim = dim + self.mode = mode + + # layers + if mode == 'upsample2d': + self.resample = nn.Sequential( + nn.Upsample(scale_factor=(2., 2.), mode='nearest-exact'), + ops.Conv2d(dim, dim // 2, 3, padding=1)) + elif mode == 'upsample3d': + self.resample = nn.Sequential( + nn.Upsample(scale_factor=(2., 2.), mode='nearest-exact'), + ops.Conv2d(dim, dim // 2, 3, padding=1)) + self.time_conv = CausalConv3d( + dim, dim * 2, (3, 1, 1), padding=(1, 0, 0)) + + elif mode == 'downsample2d': + self.resample = nn.Sequential( + nn.ZeroPad2d((0, 1, 0, 1)), + ops.Conv2d(dim, dim, 3, stride=(2, 2))) + elif mode == 'downsample3d': + self.resample = nn.Sequential( + nn.ZeroPad2d((0, 1, 0, 1)), + ops.Conv2d(dim, dim, 3, stride=(2, 2))) + self.time_conv = CausalConv3d( + dim, dim, (3, 1, 1), stride=(2, 1, 1), padding=(0, 0, 0)) + + else: + self.resample = nn.Identity() + + def forward(self, x, feat_cache=None, feat_idx=[0]): + b, c, t, h, w = x.size() + if self.mode == 'upsample3d': + if feat_cache is not None: + idx = feat_idx[0] + if feat_cache[idx] is None: + feat_cache[idx] = 'Rep' + feat_idx[0] += 1 + else: + + cache_x = x[:, :, -CACHE_T:, :, :].clone() + if cache_x.shape[2] < 2 and feat_cache[ + idx] is not None and feat_cache[idx] != 'Rep': + # cache last frame of last two chunk + cache_x = torch.cat([ + feat_cache[idx][:, :, -1, :, :].unsqueeze(2).to( + cache_x.device), cache_x + ], + dim=2) + if cache_x.shape[2] < 2 and feat_cache[ + idx] is not None and feat_cache[idx] == 'Rep': + cache_x = torch.cat([ + torch.zeros_like(cache_x).to(cache_x.device), + cache_x + ], + dim=2) + if feat_cache[idx] == 'Rep': + x = self.time_conv(x) + else: + x = self.time_conv(x, feat_cache[idx]) + feat_cache[idx] = cache_x + feat_idx[0] += 1 + + x = x.reshape(b, 2, c, t, h, w) + x = torch.stack((x[:, 0, :, :, :, :], x[:, 1, :, :, :, :]), + 3) + x = x.reshape(b, c, t * 2, h, w) + t = x.shape[2] + x = rearrange(x, 'b c t h w -> (b t) c h w') + x = self.resample(x) + x = rearrange(x, '(b t) c h w -> b c t h w', t=t) + + if self.mode == 'downsample3d': + if feat_cache is not None: + idx = feat_idx[0] + if feat_cache[idx] is None: + feat_cache[idx] = x.clone() + feat_idx[0] += 1 + else: + + cache_x = x[:, :, -1:, :, :].clone() + # if cache_x.shape[2] < 2 and feat_cache[idx] is not None and feat_cache[idx]!='Rep': + # # cache last frame of last two chunk + # cache_x = torch.cat([feat_cache[idx][:, :, -1, :, :].unsqueeze(2).to(cache_x.device), cache_x], dim=2) + + x = self.time_conv( + torch.cat([feat_cache[idx][:, :, -1:, :, :], x], 2)) + feat_cache[idx] = cache_x + feat_idx[0] += 1 + return x + + +class ResidualBlock(nn.Module): + + def __init__(self, in_dim, out_dim, dropout=0.0): + super().__init__() + self.in_dim = in_dim + self.out_dim = out_dim + + # layers + self.residual = nn.Sequential( + RMS_norm(in_dim, images=False), nn.SiLU(), + CausalConv3d(in_dim, out_dim, 3, padding=1), + RMS_norm(out_dim, images=False), nn.SiLU(), nn.Dropout(dropout), + CausalConv3d(out_dim, out_dim, 3, padding=1)) + self.shortcut = CausalConv3d(in_dim, out_dim, 1) \ + if in_dim != out_dim else nn.Identity() + + def forward(self, x, feat_cache=None, feat_idx=[0]): + h = self.shortcut(x) + for layer in self.residual: + if isinstance(layer, CausalConv3d) and feat_cache is not None: + idx = feat_idx[0] + cache_x = x[:, :, -CACHE_T:, :, :].clone() + if cache_x.shape[2] < 2 and feat_cache[idx] is not None: + # cache last frame of last two chunk + cache_x = torch.cat([ + feat_cache[idx][:, :, -1, :, :].unsqueeze(2).to( + cache_x.device), cache_x + ], + dim=2) + x = layer(x, feat_cache[idx]) + feat_cache[idx] = cache_x + feat_idx[0] += 1 + else: + x = layer(x) + return x + h + + +class AttentionBlock(nn.Module): + """ + Causal self-attention with a single head. + """ + + def __init__(self, dim): + super().__init__() + self.dim = dim + + # layers + self.norm = RMS_norm(dim) + self.to_qkv = ops.Conv2d(dim, dim * 3, 1) + self.proj = ops.Conv2d(dim, dim, 1) + self.optimized_attention = vae_attention() + + def forward(self, x): + identity = x + b, c, t, h, w = x.size() + x = rearrange(x, 'b c t h w -> (b t) c h w') + x = self.norm(x) + # compute query, key, value + + q, k, v = self.to_qkv(x).chunk(3, dim=1) + x = self.optimized_attention(q, k, v) + + # output + x = self.proj(x) + x = rearrange(x, '(b t) c h w-> b c t h w', t=t) + return x + identity + + +class Encoder3d(nn.Module): + + def __init__(self, + dim=128, + z_dim=4, + dim_mult=[1, 2, 4, 4], + num_res_blocks=2, + attn_scales=[], + temperal_downsample=[True, True, False], + dropout=0.0): + super().__init__() + self.dim = dim + self.z_dim = z_dim + self.dim_mult = dim_mult + self.num_res_blocks = num_res_blocks + self.attn_scales = attn_scales + self.temperal_downsample = temperal_downsample + + # dimensions + dims = [dim * u for u in [1] + dim_mult] + scale = 1.0 + + # init block + self.conv1 = CausalConv3d(3, dims[0], 3, padding=1) + + # downsample blocks + downsamples = [] + for i, (in_dim, out_dim) in enumerate(zip(dims[:-1], dims[1:])): + # residual (+attention) blocks + for _ in range(num_res_blocks): + downsamples.append(ResidualBlock(in_dim, out_dim, dropout)) + if scale in attn_scales: + downsamples.append(AttentionBlock(out_dim)) + in_dim = out_dim + + # downsample block + if i != len(dim_mult) - 1: + mode = 'downsample3d' if temperal_downsample[ + i] else 'downsample2d' + downsamples.append(Resample(out_dim, mode=mode)) + scale /= 2.0 + self.downsamples = nn.Sequential(*downsamples) + + # middle blocks + self.middle = nn.Sequential( + ResidualBlock(out_dim, out_dim, dropout), AttentionBlock(out_dim), + ResidualBlock(out_dim, out_dim, dropout)) + + # output blocks + self.head = nn.Sequential( + RMS_norm(out_dim, images=False), nn.SiLU(), + CausalConv3d(out_dim, z_dim, 3, padding=1)) + + def forward(self, x, feat_cache=None, feat_idx=[0]): + if feat_cache is not None: + idx = feat_idx[0] + cache_x = x[:, :, -CACHE_T:, :, :].clone() + if cache_x.shape[2] < 2 and feat_cache[idx] is not None: + # cache last frame of last two chunk + cache_x = torch.cat([ + feat_cache[idx][:, :, -1, :, :].unsqueeze(2).to( + cache_x.device), cache_x + ], + dim=2) + x = self.conv1(x, feat_cache[idx]) + feat_cache[idx] = cache_x + feat_idx[0] += 1 + else: + x = self.conv1(x) + + ## downsamples + for layer in self.downsamples: + if feat_cache is not None: + x = layer(x, feat_cache, feat_idx) + else: + x = layer(x) + + ## middle + for layer in self.middle: + if isinstance(layer, ResidualBlock) and feat_cache is not None: + x = layer(x, feat_cache, feat_idx) + else: + x = layer(x) + + ## head + for layer in self.head: + if isinstance(layer, CausalConv3d) and feat_cache is not None: + idx = feat_idx[0] + cache_x = x[:, :, -CACHE_T:, :, :].clone() + if cache_x.shape[2] < 2 and feat_cache[idx] is not None: + # cache last frame of last two chunk + cache_x = torch.cat([ + feat_cache[idx][:, :, -1, :, :].unsqueeze(2).to( + cache_x.device), cache_x + ], + dim=2) + x = layer(x, feat_cache[idx]) + feat_cache[idx] = cache_x + feat_idx[0] += 1 + else: + x = layer(x) + return x + + +class Decoder3d(nn.Module): + + def __init__(self, + dim=128, + z_dim=4, + dim_mult=[1, 2, 4, 4], + num_res_blocks=2, + attn_scales=[], + temperal_upsample=[False, True, True], + dropout=0.0): + super().__init__() + self.dim = dim + self.z_dim = z_dim + self.dim_mult = dim_mult + self.num_res_blocks = num_res_blocks + self.attn_scales = attn_scales + self.temperal_upsample = temperal_upsample + + # dimensions + dims = [dim * u for u in [dim_mult[-1]] + dim_mult[::-1]] + scale = 1.0 / 2**(len(dim_mult) - 2) + + # init block + self.conv1 = CausalConv3d(z_dim, dims[0], 3, padding=1) + + # middle blocks + self.middle = nn.Sequential( + ResidualBlock(dims[0], dims[0], dropout), AttentionBlock(dims[0]), + ResidualBlock(dims[0], dims[0], dropout)) + + # upsample blocks + upsamples = [] + for i, (in_dim, out_dim) in enumerate(zip(dims[:-1], dims[1:])): + # residual (+attention) blocks + if i == 1 or i == 2 or i == 3: + in_dim = in_dim // 2 + for _ in range(num_res_blocks + 1): + upsamples.append(ResidualBlock(in_dim, out_dim, dropout)) + if scale in attn_scales: + upsamples.append(AttentionBlock(out_dim)) + in_dim = out_dim + + # upsample block + if i != len(dim_mult) - 1: + mode = 'upsample3d' if temperal_upsample[i] else 'upsample2d' + upsamples.append(Resample(out_dim, mode=mode)) + scale *= 2.0 + self.upsamples = nn.Sequential(*upsamples) + + # output blocks + self.head = nn.Sequential( + RMS_norm(out_dim, images=False), nn.SiLU(), + CausalConv3d(out_dim, 3, 3, padding=1)) + + def forward(self, x, feat_cache=None, feat_idx=[0]): + ## conv1 + if feat_cache is not None: + idx = feat_idx[0] + cache_x = x[:, :, -CACHE_T:, :, :].clone() + if cache_x.shape[2] < 2 and feat_cache[idx] is not None: + # cache last frame of last two chunk + cache_x = torch.cat([ + feat_cache[idx][:, :, -1, :, :].unsqueeze(2).to( + cache_x.device), cache_x + ], + dim=2) + x = self.conv1(x, feat_cache[idx]) + feat_cache[idx] = cache_x + feat_idx[0] += 1 + else: + x = self.conv1(x) + + ## middle + for layer in self.middle: + if isinstance(layer, ResidualBlock) and feat_cache is not None: + x = layer(x, feat_cache, feat_idx) + else: + x = layer(x) + + ## upsamples + for layer in self.upsamples: + if feat_cache is not None: + x = layer(x, feat_cache, feat_idx) + else: + x = layer(x) + + ## head + for layer in self.head: + if isinstance(layer, CausalConv3d) and feat_cache is not None: + idx = feat_idx[0] + cache_x = x[:, :, -CACHE_T:, :, :].clone() + if cache_x.shape[2] < 2 and feat_cache[idx] is not None: + # cache last frame of last two chunk + cache_x = torch.cat([ + feat_cache[idx][:, :, -1, :, :].unsqueeze(2).to( + cache_x.device), cache_x + ], + dim=2) + x = layer(x, feat_cache[idx]) + feat_cache[idx] = cache_x + feat_idx[0] += 1 + else: + x = layer(x) + return x + + +def count_conv3d(model): + count = 0 + for m in model.modules(): + if isinstance(m, CausalConv3d): + count += 1 + return count + + +class WanVAE(nn.Module): + + def __init__(self, + dim=128, + z_dim=4, + dim_mult=[1, 2, 4, 4], + num_res_blocks=2, + attn_scales=[], + temperal_downsample=[True, True, False], + dropout=0.0): + super().__init__() + self.dim = dim + self.z_dim = z_dim + self.dim_mult = dim_mult + self.num_res_blocks = num_res_blocks + self.attn_scales = attn_scales + self.temperal_downsample = temperal_downsample + self.temperal_upsample = temperal_downsample[::-1] + + # modules + self.encoder = Encoder3d(dim, z_dim * 2, dim_mult, num_res_blocks, + attn_scales, self.temperal_downsample, dropout) + self.conv1 = CausalConv3d(z_dim * 2, z_dim * 2, 1) + self.conv2 = CausalConv3d(z_dim, z_dim, 1) + self.decoder = Decoder3d(dim, z_dim, dim_mult, num_res_blocks, + attn_scales, self.temperal_upsample, dropout) + + def encode(self, x): + self.clear_cache() + ## cache + t = x.shape[2] + iter_ = 1 + (t - 1) // 4 + ## 对encode输入的x,按时间拆分为1、4、4、4.... + for i in range(iter_): + self._enc_conv_idx = [0] + if i == 0: + out = self.encoder( + x[:, :, :1, :, :], + feat_cache=self._enc_feat_map, + feat_idx=self._enc_conv_idx) + else: + out_ = self.encoder( + x[:, :, 1 + 4 * (i - 1):1 + 4 * i, :, :], + feat_cache=self._enc_feat_map, + feat_idx=self._enc_conv_idx) + out = torch.cat([out, out_], 2) + mu, log_var = self.conv1(out).chunk(2, dim=1) + self.clear_cache() + return mu + + def decode(self, z): + self.clear_cache() + # z: [b,c,t,h,w] + + iter_ = z.shape[2] + x = self.conv2(z) + for i in range(iter_): + self._conv_idx = [0] + if i == 0: + out = self.decoder( + x[:, :, i:i + 1, :, :], + feat_cache=self._feat_map, + feat_idx=self._conv_idx) + else: + out_ = self.decoder( + x[:, :, i:i + 1, :, :], + feat_cache=self._feat_map, + feat_idx=self._conv_idx) + out = torch.cat([out, out_], 2) + self.clear_cache() + return out + + def clear_cache(self): + self._conv_num = count_conv3d(self.decoder) + self._conv_idx = [0] + self._feat_map = [None] * self._conv_num + #cache encode + self._enc_conv_num = count_conv3d(self.encoder) + self._enc_conv_idx = [0] + self._enc_feat_map = [None] * self._enc_conv_num diff --git a/ComfyUI/comfy/text_encoders/ace_lyrics_tokenizer/vocab.json b/ComfyUI/comfy/text_encoders/ace_lyrics_tokenizer/vocab.json new file mode 100644 index 0000000000000000000000000000000000000000..519ed340c36e19dfe15ccffd7aa5a31a261d70a5 --- /dev/null +++ b/ComfyUI/comfy/text_encoders/ace_lyrics_tokenizer/vocab.json @@ -0,0 +1,15535 @@ +{ + "version": "1.0", + "truncation": null, + "padding": null, + "added_tokens": [ + { + "id": 0, + "special": true, + "content": "[STOP]", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false + }, + { + "id": 1, + "special": true, + "content": "[UNK]", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false + }, + { + "id": 2, + "special": true, + "content": "[SPACE]", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false + }, + { + "id": 259, + "special": true, + "content": "[en]", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false + }, + { + "id": 260, + "special": true, + "content": "[de]", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false + }, + { + "id": 261, + "special": true, + "content": "[START]", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false + }, + { + "id": 262, + "special": true, + "content": "[fr]", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false + }, + { + "id": 284, + "special": true, + "content": "[es]", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false + }, + { + "id": 285, + "special": true, + "content": "[it]", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false + }, + { + "id": 286, + "special": true, + "content": "[pt]", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false + }, + { + "id": 294, + "special": true, + "content": "[pl]", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false + }, + { + "id": 295, + "special": true, + "content": "[tr]", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false + }, + { + "id": 267, + "special": true, + "content": "[ru]", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false + }, + { + "id": 293, + "special": true, + "content": "[cs]", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false + }, + { + "id": 297, + "special": true, + "content": "[nl]", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false + }, + { + "id": 5022, + "special": true, + "content": "[ar]", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false + }, + { + "id": 5023, + "special": true, + "content": "[zh-cn]", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false + }, + { + "id": 5412, + "special": true, + "content": "[ja]", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false + }, + { + "id": 5753, + "special": true, + "content": "[hu]", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false + }, + { + "id": 6152, + "special": true, + "content": "[ko]", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false + }, + { + "id": 6680, + "special": true, + "content": "[hi]", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false + }, + { + "id": 6681, + "special": true, + "content": "[start]", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false + }, + { + "id": 6682, + "special": true, + "content": "[intro]", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false + }, + { + "id": 6683, + "special": true, + "content": "[verse]", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false + }, + { + "id": 6684, + "special": true, + "content": "[chorus]", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false + }, + { + "id": 6685, + "special": true, + "content": "[bridge]", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false + }, + { + "id": 6686, + "special": true, + "content": "[outro]", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false + }, + { + "id": 6687, + "special": true, + "content": "[end]", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false + }, + { + "id": 6688, + "special": true, + "content": "[inst]", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false + }, + { + "id": 6689, + "special": true, + "content": "[solo]", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false + }, + { + "id": 6690, + "special": true, + "content": "[hook]", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false + }, + { + "id": 6691, + "special": true, + "content": "[pre-chorus]", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false + }, + { + "id": 6692, + "special": true, + "content": "[break]", + "single_word": false, + "lstrip": false, + "rstrip": false, + "normalized": false + } + ], + "normalizer": null, + "pre_tokenizer": { + "type": "Whitespace" + }, + "post_processor": null, + "decoder": null, + "model": { + "type": "BPE", + "dropout": null, + "unk_token": "[UNK]", + "continuing_subword_prefix": null, + "end_of_word_suffix": null, + "fuse_unk": false, + "vocab": { + "[STOP]": 0, + "[UNK]": 1, + "[SPACE]": 2, + "!": 3, + "'": 4, + "(": 5, + ")": 6, + ",": 7, + "-": 8, + ".": 9, + "/": 10, + ":": 11, + ";": 12, + "?": 13, + "a": 14, + "b": 15, + "c": 16, + "d": 17, + "e": 18, + "f": 19, + "g": 20, + "h": 21, + "i": 22, + "j": 23, + "k": 24, + "l": 25, + "m": 26, + "n": 27, + "o": 28, + "p": 29, + "q": 30, + "r": 31, + "s": 32, + "t": 33, + "u": 34, + "v": 35, + "w": 36, + "x": 37, + "y": 38, + "z": 39, + "th": 40, + "in": 41, + "the": 42, + "an": 43, + "er": 44, + "ou": 45, + "re": 46, + "on": 47, + "at": 48, + "ed": 49, + "en": 50, + "to": 51, + "ing": 52, + "and": 53, + "is": 54, + "as": 55, + "al": 56, + "or": 57, + "of": 58, + "ar": 59, + "it": 60, + "es": 61, + "he": 62, + "st": 63, + "le": 64, + "om": 65, + "se": 66, + "be": 67, + "ad": 68, + "ow": 69, + "ly": 70, + "ch": 71, + "wh": 72, + "that": 73, + "you": 74, + "li": 75, + "ve": 76, + "ac": 77, + "ti": 78, + "ld": 79, + "me": 80, + "was": 81, + "gh": 82, + "id": 83, + "ll": 84, + "wi": 85, + "ent": 86, + "for": 87, + "ay": 88, + "ro": 89, + "ver": 90, + "ic": 91, + "her": 92, + "ke": 93, + "his": 94, + "no": 95, + "ut": 96, + "un": 97, + "ir": 98, + "lo": 99, + "we": 100, + "ri": 101, + "ha": 102, + "with": 103, + "ght": 104, + "out": 105, + "im": 106, + "ion": 107, + "all": 108, + "ab": 109, + "one": 110, + "ne": 111, + "ge": 112, + "ould": 113, + "ter": 114, + "mo": 115, + "had": 116, + "ce": 117, + "she": 118, + "go": 119, + "sh": 120, + "ur": 121, + "am": 122, + "so": 123, + "pe": 124, + "my": 125, + "de": 126, + "are": 127, + "but": 128, + "ome": 129, + "fr": 130, + "ther": 131, + "fe": 132, + "su": 133, + "do": 134, + "con": 135, + "te": 136, + "ain": 137, + "ere": 138, + "po": 139, + "if": 140, + "they": 141, + "us": 142, + "ag": 143, + "tr": 144, + "now": 145, + "oun": 146, + "this": 147, + "have": 148, + "not": 149, + "sa": 150, + "il": 151, + "up": 152, + "thing": 153, + "from": 154, + "ap": 155, + "him": 156, + "ack": 157, + "ation": 158, + "ant": 159, + "our": 160, + "op": 161, + "like": 162, + "ust": 163, + "ess": 164, + "bo": 165, + "ok": 166, + "ul": 167, + "ind": 168, + "ex": 169, + "com": 170, + "some": 171, + "there": 172, + "ers": 173, + "co": 174, + "res": 175, + "man": 176, + "ard": 177, + "pl": 178, + "wor": 179, + "way": 180, + "tion": 181, + "fo": 182, + "ca": 183, + "were": 184, + "by": 185, + "ate": 186, + "pro": 187, + "ted": 188, + "ound": 189, + "own": 190, + "would": 191, + "ts": 192, + "what": 193, + "qu": 194, + "ally": 195, + "ight": 196, + "ck": 197, + "gr": 198, + "when": 199, + "ven": 200, + "can": 201, + "ough": 202, + "ine": 203, + "end": 204, + "per": 205, + "ous": 206, + "od": 207, + "ide": 208, + "know": 209, + "ty": 210, + "very": 211, + "si": 212, + "ak": 213, + "who": 214, + "about": 215, + "ill": 216, + "them": 217, + "est": 218, + "red": 219, + "ye": 220, + "could": 221, + "ong": 222, + "your": 223, + "their": 224, + "em": 225, + "just": 226, + "other": 227, + "into": 228, + "any": 229, + "whi": 230, + "um": 231, + "tw": 232, + "ast": 233, + "der": 234, + "did": 235, + "ie": 236, + "been": 237, + "ace": 238, + "ink": 239, + "ity": 240, + "back": 241, + "ting": 242, + "br": 243, + "more": 244, + "ake": 245, + "pp": 246, + "then": 247, + "sp": 248, + "el": 249, + "use": 250, + "bl": 251, + "said": 252, + "over": 253, + "get": 254, + "ß": 255, + "ä": 256, + "ö": 257, + "ü": 258, + "[en]": 259, + "[de]": 260, + "[START]": 261, + "[fr]": 262, + "œ": 263, + "ï": 264, + "ê": 265, + "â": 266, + "[ru]": 267, + "ÿ": 268, + "è": 269, + "à": 270, + "ë": 271, + "ù": 272, + "î": 273, + "ç": 274, + "æ": 275, + "ô": 276, + "û": 277, + "á": 278, + "é": 279, + "í": 280, + "ó": 281, + "ú": 282, + "ñ": 283, + "[es]": 284, + "[it]": 285, + "[pt]": 286, + "ń": 287, + "ś": 288, + "ę": 289, + "ą": 290, + "ż": 291, + "ć": 292, + "[cs]": 293, + "[pl]": 294, + "[tr]": 295, + "ã": 296, + "[nl]": 297, + "ş": 298, + "ğ": 299, + "ı": 300, + "ò": 301, + "ì": 302, + "¿": 303, + "…": 304, + "i̇": 305, + "õ": 306, + "\"": 307, + "´": 308, + "ø": 309, + "č": 310, + "ō": 311, + "š": 312, + "ž": 313, + "̇": 314, + "ei": 315, + "ich": 316, + "ein": 317, + "au": 318, + "sch": 319, + "und": 320, + "die": 321, + "da": 322, + "den": 323, + "gen": 324, + "zu": 325, + "hr": 326, + "ten": 327, + "mi": 328, + "sie": 329, + "das": 330, + "eine": 331, + "icht": 332, + "ber": 333, + "ach": 334, + "auf": 335, + "lich": 336, + "nicht": 337, + "mm": 338, + "ben": 339, + "war": 340, + "mit": 341, + "sich": 342, + "ig": 343, + "aus": 344, + "ist": 345, + "wie": 346, + "och": 347, + "ung": 348, + "ann": 349, + "ür": 350, + "hn": 351, + "ihr": 352, + "sen": 353, + "tz": 354, + "dem": 355, + "eit": 356, + "hat": 357, + "wir": 358, + "von": 359, + "wei": 360, + "ier": 361, + "ra": 362, + "einen": 363, + "vor": 364, + "als": 365, + "wo": 366, + "rei": 367, + "ste": 368, + "lie": 369, + "auch": 370, + "du": 371, + "des": 372, + "ko": 373, + "über": 374, + "bei": 375, + "hen": 376, + "hm": 377, + "lei": 378, + "aber": 379, + "wen": 380, + "hl": 381, + "ger": 382, + "nach": 383, + "ft": 384, + "imm": 385, + "je": 386, + "schen": 387, + "wer": 388, + "ser": 389, + "än": 390, + "sein": 391, + "ol": 392, + "cht": 393, + "für": 394, + "kl": 395, + "ff": 396, + "einem": 397, + "nen": 398, + "ja": 399, + "noch": 400, + "hatte": 401, + "pf": 402, + "hin": 403, + "di": 404, + "chen": 405, + "rü": 406, + "iel": 407, + "sel": 408, + "dass": 409, + "ihn": 410, + "mir": 411, + "schl": 412, + "ön": 413, + "gan": 414, + "gt": 415, + "einer": 416, + "sten": 417, + "mich": 418, + "wenn": 419, + "ell": 420, + "gte": 421, + "mal": 422, + "gel": 423, + "ken": 424, + "nur": 425, + "mmen": 426, + "fü": 427, + "ern": 428, + "ör": 429, + "unter": 430, + "ander": 431, + "dur": 432, + "uch": 433, + "ta": 434, + "men": 435, + "mach": 436, + "doch": 437, + "durch": 438, + "os": 439, + "gl": 440, + "hal": 441, + "ihre": 442, + "wä": 443, + "immer": 444, + "ihm": 445, + "kann": 446, + "ort": 447, + "dann": 448, + "lan": 449, + "tzt": 450, + "oder": 451, + "hren": 452, + "et": 453, + "kön": 454, + "ick": 455, + "fa": 456, + "wieder": 457, + "daß": 458, + "mein": 459, + "fen": 460, + "ganz": 461, + "diese": 462, + "ster": 463, + "dar": 464, + "wa": 465, + "ges": 466, + "na": 467, + "fl": 468, + "igen": 469, + "sche": 470, + "ungen": 471, + "mehr": 472, + "ßen": 473, + "ot": 474, + "kon": 475, + "gew": 476, + "haben": 477, + "geh": 478, + "ät": 479, + "sind": 480, + "dr": 481, + "wel": 482, + "uns": 483, + "vo": 484, + "ma": 485, + "ute": 486, + "schon": 487, + "bes": 488, + "gesch": 489, + "bt": 490, + "che": 491, + "son": 492, + "ob": 493, + "la": 494, + "rück": 495, + "seine": 496, + "kr": 497, + "fre": 498, + "eil": 499, + "zum": 500, + "hier": 501, + "kt": 502, + "ige": 503, + "spr": 504, + "leben": 505, + "bst": 506, + "zeit": 507, + "gro": 508, + "denn": 509, + "ho": 510, + "scha": 511, + "bar": 512, + "alle": 513, + "gegen": 514, + "wür": 515, + "mü": 516, + "ze": 517, + "werden": 518, + "jetzt": 519, + "kommen": 520, + "nie": 521, + "sei": 522, + "heit": 523, + "soll": 524, + "glei": 525, + "meine": 526, + "woll": 527, + "ner": 528, + "habe": 529, + "wur": 530, + "lichen": 531, + "assen": 532, + "nte": 533, + "sehen": 534, + "wird": 535, + "bis": 536, + "gar": 537, + "ien": 538, + "mus": 539, + "uß": 540, + "är": 541, + "stell": 542, + "keit": 543, + "zwei": 544, + "selbst": 545, + "sta": 546, + "pa": 547, + "sagte": 548, + "tet": 549, + "kam": 550, + "ssen": 551, + "viel": 552, + "ug": 553, + "zen": 554, + "hei": 555, + "mann": 556, + "will": 557, + "geb": 558, + "waren": 559, + "ück": 560, + "äch": 561, + "mer": 562, + "ru": 563, + "hau": 564, + "eigen": 565, + "ang": 566, + "weg": 567, + "blick": 568, + "fra": 569, + "alles": 570, + "ka": 571, + "augen": 572, + "fin": 573, + "liche": 574, + "unser": 575, + "dern": 576, + "herr": 577, + "nun": 578, + "vie": 579, + "chte": 580, + "wohl": 581, + "fall": 582, + "ht": 583, + "ün": 584, + "etwas": 585, + "stand": 586, + "äu": 587, + "mö": 588, + "tel": 589, + "rie": 590, + "dich": 591, + "dies": 592, + "hand": 593, + "bin": 594, + "ffen": 595, + "nichts": 596, + "dan": 597, + "hne": 598, + "ihnen": 599, + "esen": 600, + "dieser": 601, + "frau": 602, + "art": 603, + "dir": 604, + "isch": 605, + "erst": 606, + "gleich": 607, + "komm": 608, + "hör": 609, + "ße": 610, + "dig": 611, + "sehr": 612, + "zei": 613, + "sam": 614, + "aum": 615, + "hät": 616, + "ingen": 617, + "gut": 618, + "mut": 619, + "cken": 620, + "konnte": 621, + "stimm": 622, + "zur": 623, + "itz": 624, + "weil": 625, + "würde": 626, + "fä": 627, + "können": 628, + "keine": 629, + "fer": 630, + "ischen": 631, + "voll": 632, + "eines": 633, + "setz": 634, + "zie": 635, + "del": 636, + "tete": 637, + "seiner": 638, + "ieren": 639, + "gest": 640, + "zurück": 641, + "wurde": 642, + "schn": 643, + "pr": 644, + "ließ": 645, + "tra": 646, + "mä": 647, + "gend": 648, + "fol": 649, + "ik": 650, + "schla": 651, + "schaft": 652, + "ater": 653, + "weiß": 654, + "seinen": 655, + "lassen": 656, + "lu": 657, + "unden": 658, + "teil": 659, + "neu": 660, + "iert": 661, + "menschen": 662, + "hmen": 663, + "str": 664, + "gi": 665, + "sah": 666, + "ihren": 667, + "eln": 668, + "weiter": 669, + "gehen": 670, + "iger": 671, + "macht": 672, + "tag": 673, + "also": 674, + "halten": 675, + "nis": 676, + "acht": 677, + "geben": 678, + "og": 679, + "nat": 680, + "mar": 681, + "det": 682, + "ohne": 683, + "haus": 684, + "tro": 685, + "ange": 686, + "lau": 687, + "spiel": 688, + "tre": 689, + "schr": 690, + "inn": 691, + "los": 692, + "machen": 693, + "hätte": 694, + "beg": 695, + "wirk": 696, + "alt": 697, + "glich": 698, + "tes": 699, + "richt": 700, + "freund": 701, + "ihrer": 702, + "fel": 703, + "bel": 704, + "sol": 705, + "einmal": 706, + "eben": 707, + "hol": 708, + "hän": 709, + "tern": 710, + "hö": 711, + "schw": 712, + "recht": 713, + "wahr": 714, + "seinem": 715, + "stehen": 716, + "hlen": 717, + "ins": 718, + "ging": 719, + "wollte": 720, + "wissen": 721, + "ungs": 722, + "ald": 723, + "ass": 724, + "jahr": 725, + "mor": 726, + "welt": 727, + "under": 728, + "zusa": 729, + "kopf": 730, + "lang": 731, + "hinter": 732, + "atz": 733, + "stra": 734, + "angen": 735, + "ank": 736, + "ade": 737, + "glau": 738, + "fach": 739, + "hatten": 740, + "fort": 741, + "eicht": 742, + "iff": 743, + "ler": 744, + "mei": 745, + "diesem": 746, + "kein": 747, + "frei": 748, + "führ": 749, + "vom": 750, + "β": 751, + "ai": 752, + "ait": 753, + "que": 754, + "les": 755, + "av": 756, + "ais": 757, + "oi": 758, + "eu": 759, + "lle": 760, + "par": 761, + "ans": 762, + "ment": 763, + "ét": 764, + "une": 765, + "pas": 766, + "qui": 767, + "elle": 768, + "dé": 769, + "pour": 770, + "dans": 771, + "ré": 772, + "tou": 773, + "vous": 774, + "vi": 775, + "ouv": 776, + "mon": 777, + "sur": 778, + "ci": 779, + "plu": 780, + "ère": 781, + "mais": 782, + "ois": 783, + "plus": 784, + "ée": 785, + "aient": 786, + "mp": 787, + "lui": 788, + "ave": 789, + "était": 790, + "ses": 791, + "tout": 792, + "oir": 793, + "avait": 794, + "és": 795, + "mes": 796, + "nous": 797, + "eux": 798, + "bi": 799, + "ons": 800, + "pu": 801, + "ces": 802, + "tu": 803, + "leur": 804, + "don": 805, + "eur": 806, + "ette": 807, + "aire": 808, + "avec": 809, + "dit": 810, + "té": 811, + "ille": 812, + "comme": 813, + "cr": 814, + "ux": 815, + "ès": 816, + "aux": 817, + "jour": 818, + "ils": 819, + "bien": 820, + "cou": 821, + "quel": 822, + "peu": 823, + "cette": 824, + "cu": 825, + "mê": 826, + "fait": 827, + "gu": 828, + "être": 829, + "ité": 830, + "ens": 831, + "ni": 832, + "lé": 833, + "dis": 834, + "ble": 835, + "né": 836, + "puis": 837, + "même": 838, + "ques": 839, + "fi": 840, + "age": 841, + "moi": 842, + "ence": 843, + "ont": 844, + "main": 845, + "ors": 846, + "aut": 847, + "ance": 848, + "mé": 849, + "sans": 850, + "sé": 851, + "lon": 852, + "hom": 853, + "car": 854, + "able": 855, + "cher": 856, + "deux": 857, + "enf": 858, + "où": 859, + "ph": 860, + "ure": 861, + "temp": 862, + "pos": 863, + "rent": 864, + "pé": 865, + "faire": 866, + "pi": 867, + "tres": 868, + "ça": 869, + "endre": 870, + "bon": 871, + "sou": 872, + "int": 873, + "pré": 874, + "sent": 875, + "tant": 876, + "cer": 877, + "là": 878, + "lais": 879, + "près": 880, + "bre": 881, + "cour": 882, + "pet": 883, + "comp": 884, + "lait": 885, + "trouv": 886, + "entre": 887, + "sont": 888, + "dev": 889, + "nu": 890, + "temps": 891, + "dou": 892, + "rait": 893, + "bou": 894, + "quand": 895, + "jours": 896, + "avoir": 897, + "été": 898, + "ale": 899, + "pre": 900, + "fois": 901, + "orte": 902, + "vé": 903, + "non": 904, + "tous": 905, + "jus": 906, + "coup": 907, + "homme": 908, + "ête": 909, + "aussi": 910, + "urs": 911, + "seu": 912, + "ord": 913, + "min": 914, + "gé": 915, + "core": 916, + "va": 917, + "vre": 918, + "encore": 919, + "sem": 920, + "ite": 921, + "autre": 922, + "pris": 923, + "peut": 924, + "ue": 925, + "ante": 926, + "gn": 927, + "rép": 928, + "hu": 929, + "sion": 930, + "votre": 931, + "dire": 932, + "ez": 933, + "fem": 934, + "leurs": 935, + "met": 936, + "cri": 937, + "mis": 938, + "tour": 939, + "rai": 940, + "jam": 941, + "regar": 942, + "rien": 943, + "vers": 944, + "suis": 945, + "pouv": 946, + "vis": 947, + "grand": 948, + "ants": 949, + "cor": 950, + "rer": 951, + "cé": 952, + "tent": 953, + "pres": 954, + "vou": 955, + "alors": 956, + "sieur": 957, + "aine": 958, + "quoi": 959, + "fon": 960, + "endant": 961, + "arri": 962, + "eure": 963, + "après": 964, + "donc": 965, + "itu": 966, + "lè": 967, + "sait": 968, + "toi": 969, + "cha": 970, + "ail": 971, + "asse": 972, + "imp": 973, + "voy": 974, + "conn": 975, + "pla": 976, + "petit": 977, + "avant": 978, + "nom": 979, + "tin": 980, + "dont": 981, + "sous": 982, + "emp": 983, + "person": 984, + "elles": 985, + "beau": 986, + "parti": 987, + "cho": 988, + "prit": 989, + "toujours": 990, + "rais": 991, + "jamais": 992, + "trav": 993, + "tions": 994, + "très": 995, + "voi": 996, + "ren": 997, + "yeux": 998, + "voir": 999, + "premi": 1000, + "gne": 1001, + "heure": 1002, + "rou": 1003, + "eff": 1004, + "notre": 1005, + "ments": 1006, + "ton": 1007, + "fais": 1008, + "cela": 1009, + "répon": 1010, + "cons": 1011, + "air": 1012, + "ôt": 1013, + "pendant": 1014, + "ici": 1015, + "toute": 1016, + "jet": 1017, + "port": 1018, + "étaient": 1019, + "pen": 1020, + "hé": 1021, + "autres": 1022, + "père": 1023, + "oc": 1024, + "quelques": 1025, + "ique": 1026, + "lis": 1027, + "femme": 1028, + "jou": 1029, + "teur": 1030, + "monde": 1031, + "nes": 1032, + "dre": 1033, + "aff": 1034, + "rap": 1035, + "part": 1036, + "lement": 1037, + "cla": 1038, + "fut": 1039, + "quelque": 1040, + "prendre": 1041, + "rê": 1042, + "aille": 1043, + "sais": 1044, + "ches": 1045, + "let": 1046, + "char": 1047, + "ères": 1048, + "ents": 1049, + "moins": 1050, + "eau": 1051, + "aî": 1052, + "jeu": 1053, + "heur": 1054, + "ées": 1055, + "tri": 1056, + "point": 1057, + "mom": 1058, + "vent": 1059, + "nouv": 1060, + "gran": 1061, + "trois": 1062, + "sant": 1063, + "toutes": 1064, + "contre": 1065, + "èrent": 1066, + "chez": 1067, + "avez": 1068, + "ût": 1069, + "att": 1070, + "pau": 1071, + "porte": 1072, + "ouver": 1073, + "lit": 1074, + "prés": 1075, + "chose": 1076, + "vit": 1077, + "monsieur": 1078, + "hab": 1079, + "tête": 1080, + "ju": 1081, + "tement": 1082, + "ction": 1083, + "vrai": 1084, + "lar": 1085, + "cet": 1086, + "regard": 1087, + "lant": 1088, + "som": 1089, + "moment": 1090, + "illes": 1091, + "ple": 1092, + "ps": 1093, + "mère": 1094, + "cl": 1095, + "sour": 1096, + "ys": 1097, + "trop": 1098, + "enne": 1099, + "jusqu": 1100, + "avaient": 1101, + "avais": 1102, + "jeune": 1103, + "depuis": 1104, + "personne": 1105, + "fit": 1106, + "cert": 1107, + "jo": 1108, + "oui": 1109, + "rest": 1110, + "semb": 1111, + "cap": 1112, + "mat": 1113, + "mu": 1114, + "long": 1115, + "fran": 1116, + "faut": 1117, + "iti": 1118, + "bli": 1119, + "chev": 1120, + "pri": 1121, + "ente": 1122, + "ainsi": 1123, + "cham": 1124, + "lors": 1125, + "cas": 1126, + "ili": 1127, + "bé": 1128, + "nos": 1129, + "sui": 1130, + "rit": 1131, + "cro": 1132, + "gue": 1133, + "ía": 1134, + "por": 1135, + "las": 1136, + "ón": 1137, + "una": 1138, + "aba": 1139, + "dos": 1140, + "era": 1141, + "mb": 1142, + "para": 1143, + "ás": 1144, + "mos": 1145, + "ando": 1146, + "como": 1147, + "más": 1148, + "ción": 1149, + "tan": 1150, + "dad": 1151, + "ado": 1152, + "fu": 1153, + "cia": 1154, + "mente": 1155, + "sus": 1156, + "tar": 1157, + "za": 1158, + "ba": 1159, + "pero": 1160, + "sin": 1161, + "lla": 1162, + "án": 1163, + "ia": 1164, + "ran": 1165, + "ga": 1166, + "yo": 1167, + "tos": 1168, + "cos": 1169, + "ya": 1170, + "ones": 1171, + "había": 1172, + "hi": 1173, + "esta": 1174, + "mas": 1175, + "tor": 1176, + "aban": 1177, + "dor": 1178, + "ían": 1179, + "tas": 1180, + "én": 1181, + "endo": 1182, + "aque": 1183, + "ero": 1184, + "io": 1185, + "qué": 1186, + "cab": 1187, + "tal": 1188, + "señ": 1189, + "ora": 1190, + "todo": 1191, + "sal": 1192, + "cuando": 1193, + "gun": 1194, + "bu": 1195, + "ras": 1196, + "esto": 1197, + "pare": 1198, + "él": 1199, + "tras": 1200, + "jos": 1201, + "mien": 1202, + "pue": 1203, + "cre": 1204, + "pon": 1205, + "día": 1206, + "tros": 1207, + "sab": 1208, + "sobre": 1209, + "ese": 1210, + "mbre": 1211, + "eron": 1212, + "añ": 1213, + "ido": 1214, + "porque": 1215, + "ella": 1216, + "cen": 1217, + "muy": 1218, + "cal": 1219, + "este": 1220, + "has": 1221, + "có": 1222, + "gra": 1223, + "ros": 1224, + "aquel": 1225, + "dijo": 1226, + "cía": 1227, + "zo": 1228, + "ciones": 1229, + "mbi": 1230, + "elo": 1231, + "tó": 1232, + "ina": 1233, + "todos": 1234, + "tien": 1235, + "estaba": 1236, + "deci": 1237, + "cio": 1238, + "ño": 1239, + "lor": 1240, + "nues": 1241, + "medi": 1242, + "len": 1243, + "vida": 1244, + "ali": 1245, + "pues": 1246, + "ales": 1247, + "vol": 1248, + "mí": 1249, + "rar": 1250, + "cion": 1251, + "hasta": 1252, + "señor": 1253, + "cono": 1254, + "ah": 1255, + "dios": 1256, + "esa": 1257, + "ún": 1258, + "var": 1259, + "san": 1260, + "gui": 1261, + "otros": 1262, + "tado": 1263, + "buen": 1264, + "ña": 1265, + "tiemp": 1266, + "hacer": 1267, + "jer": 1268, + "vu": 1269, + "ana": 1270, + "así": 1271, + "antes": 1272, + "vez": 1273, + "miento": 1274, + "jar": 1275, + "lab": 1276, + "casa": 1277, + "eso": 1278, + "ego": 1279, + "dió": 1280, + "está": 1281, + "encia": 1282, + "eli": 1283, + "ías": 1284, + "tiempo": 1285, + "zar": 1286, + "van": 1287, + "mun": 1288, + "erta": 1289, + "tambi": 1290, + "sí": 1291, + "aun": 1292, + "mismo": 1293, + "entes": 1294, + "mano": 1295, + "ele": 1296, + "nada": 1297, + "segu": 1298, + "mej": 1299, + "erra": 1300, + "tir": 1301, + "uno": 1302, + "donde": 1303, + "toda": 1304, + "desde": 1305, + "también": 1306, + "cuer": 1307, + "hombre": 1308, + "otro": 1309, + "lib": 1310, + "trar": 1311, + "cual": 1312, + "hay": 1313, + "cada": 1314, + "taba": 1315, + "mento": 1316, + "tenía": 1317, + "quer": 1318, + "eran": 1319, + "siemp": 1320, + "siempre": 1321, + "erto": 1322, + "quí": 1323, + "gos": 1324, + "pués": 1325, + "ellos": 1326, + "después": 1327, + "nue": 1328, + "llo": 1329, + "inter": 1330, + "cómo": 1331, + "ahora": 1332, + "uste": 1333, + "traba": 1334, + "lado": 1335, + "ino": 1336, + "poco": 1337, + "erte": 1338, + "mujer": 1339, + "quier": 1340, + "algun": 1341, + "fue": 1342, + "ojos": 1343, + "enton": 1344, + "vos": 1345, + "esper": 1346, + "much": 1347, + "otra": 1348, + "az": 1349, + "eza": 1350, + "aquí": 1351, + "cias": 1352, + "gua": 1353, + "mucho": 1354, + "decir": 1355, + "esti": 1356, + "idad": 1357, + "algo": 1358, + "ocu": 1359, + "entonces": 1360, + "dido": 1361, + "entos": 1362, + "gri": 1363, + "dado": 1364, + "ios": 1365, + "dose": 1366, + "usted": 1367, + "quien": 1368, + "ami": 1369, + "unto": 1370, + "mejor": 1371, + "bas": 1372, + "solo": 1373, + "pregun": 1374, + "tur": 1375, + "alg": 1376, + "todas": 1377, + "parte": 1378, + "emb": 1379, + "cto": 1380, + "mundo": 1381, + "tiene": 1382, + "tante": 1383, + "palab": 1384, + "tran": 1385, + "aquella": 1386, + "cios": 1387, + "aunque": 1388, + "cuen": 1389, + "tener": 1390, + "fun": 1391, + "respon": 1392, + "allí": 1393, + "xi": 1394, + "han": 1395, + "pens": 1396, + "contra": 1397, + "tura": 1398, + "val": 1399, + "dio": 1400, + "tanto": 1401, + "camin": 1402, + "mó": 1403, + "esp": 1404, + "ada": 1405, + "ío": 1406, + "hacia": 1407, + "dej": 1408, + "estar": 1409, + "ión": 1410, + "gas": 1411, + "vas": 1412, + "noche": 1413, + "ér": 1414, + "años": 1415, + "padre": 1416, + "gus": 1417, + "ár": 1418, + "sino": 1419, + "manos": 1420, + "cido": 1421, + "estu": 1422, + "hubi": 1423, + "vir": 1424, + "bri": 1425, + "raz": 1426, + "chi": 1427, + "puede": 1428, + "menos": 1429, + "habi": 1430, + "homb": 1431, + "neces": 1432, + "may": 1433, + "eros": 1434, + "ría": 1435, + "hecho": 1436, + "escu": 1437, + "lti": 1438, + "ándo": 1439, + "bus": 1440, + "cosas": 1441, + "tú": 1442, + "espa": 1443, + "reci": 1444, + "ctor": 1445, + "prim": 1446, + "dia": 1447, + "dese": 1448, + "mientras": 1449, + "hor": 1450, + "fuer": 1451, + "ida": 1452, + "posi": 1453, + "lante": 1454, + "ano": 1455, + "estas": 1456, + "pli": 1457, + "luego": 1458, + "sión": 1459, + "cin": 1460, + "tierra": 1461, + "guar": 1462, + "cado": 1463, + "encon": 1464, + "pren": 1465, + "mayor": 1466, + "fal": 1467, + "ð": 1468, + "ħ": 1469, + "ň": 1470, + "ə": 1471, + "θ": 1472, + "’": 1473, + "“": 1474, + "”": 1475, + "zi": 1476, + "gli": 1477, + "tto": 1478, + "ono": 1479, + "nel": 1480, + "tti": 1481, + "della": 1482, + "zione": 1483, + "tta": 1484, + "tà": 1485, + "uo": 1486, + "come": 1487, + "alla": 1488, + "oni": 1489, + "ggi": 1490, + "ssi": 1491, + "più": 1492, + "ini": 1493, + "bb": 1494, + "sto": 1495, + "sono": 1496, + "eri": 1497, + "sse": 1498, + "sc": 1499, + "sul": 1500, + "vano": 1501, + "sti": 1502, + "suo": 1503, + "cchi": 1504, + "zza": 1505, + "anche": 1506, + "tte": 1507, + "sci": 1508, + "col": 1509, + "sso": 1510, + "ssa": 1511, + "dei": 1512, + "aveva": 1513, + "zz": 1514, + "amo": 1515, + "gno": 1516, + "sua": 1517, + "ria": 1518, + "sì": 1519, + "ché": 1520, + "dal": 1521, + "ona": 1522, + "spe": 1523, + "gni": 1524, + "tt": 1525, + "delle": 1526, + "questo": 1527, + "nella": 1528, + "dere": 1529, + "anno": 1530, + "dell": 1531, + "uni": 1532, + "bbe": 1533, + "anti": 1534, + "ene": 1535, + "gio": 1536, + "uto": 1537, + "qual": 1538, + "glia": 1539, + "quando": 1540, + "tutto": 1541, + "glio": 1542, + "zioni": 1543, + "cam": 1544, + "esso": 1545, + "ss": 1546, + "mol": 1547, + "loro": 1548, + "perché": 1549, + "cosa": 1550, + "due": 1551, + "poi": 1552, + "sco": 1553, + "cco": 1554, + "gna": 1555, + "tem": 1556, + "prima": 1557, + "così": 1558, + "essere": 1559, + "ani": 1560, + "bra": 1561, + "rio": 1562, + "anco": 1563, + "cui": 1564, + "spi": 1565, + "via": 1566, + "gior": 1567, + "bile": 1568, + "ggio": 1569, + "mai": 1570, + "tare": 1571, + "indi": 1572, + "rebbe": 1573, + "senza": 1574, + "zio": 1575, + "tutti": 1576, + "stato": 1577, + "zia": 1578, + "dalla": 1579, + "mia": 1580, + "vita": 1581, + "quella": 1582, + "qua": 1583, + "dove": 1584, + "allo": 1585, + "sempre": 1586, + "zzo": 1587, + "sia": 1588, + "dopo": 1589, + "porta": 1590, + "ccia": 1591, + "erano": 1592, + "anni": 1593, + "chia": 1594, + "enza": 1595, + "propri": 1596, + "anda": 1597, + "cca": 1598, + "occhi": 1599, + "questa": 1600, + "ffi": 1601, + "ron": 1602, + "mio": 1603, + "ris": 1604, + "ogni": 1605, + "rin": 1606, + "far": 1607, + "menti": 1608, + "ancora": 1609, + "fatto": 1610, + "mani": 1611, + "senti": 1612, + "pra": 1613, + "tempo": 1614, + "essi": 1615, + "bbi": 1616, + "lare": 1617, + "pers": 1618, + "sor": 1619, + "anza": 1620, + "pie": 1621, + "verso": 1622, + "altro": 1623, + "tato": 1624, + "cato": 1625, + "ato": 1626, + "volta": 1627, + "cc": 1628, + "fare": 1629, + "ciò": 1630, + "bili": 1631, + "nuo": 1632, + "quello": 1633, + "colo": 1634, + "ppo": 1635, + "trova": 1636, + "ore": 1637, + "rono": 1638, + "molto": 1639, + "almente": 1640, + "sca": 1641, + "vole": 1642, + "tali": 1643, + "sulla": 1644, + "sce": 1645, + "meno": 1646, + "anto": 1647, + "pun": 1648, + "stu": 1649, + "capi": 1650, + "giu": 1651, + "mini": 1652, + "pia": 1653, + "lavo": 1654, + "vero": 1655, + "rsi": 1656, + "altri": 1657, + "scia": 1658, + "suoi": 1659, + "glie": 1660, + "sotto": 1661, + "bene": 1662, + "scri": 1663, + "tale": 1664, + "degli": 1665, + "alc": 1666, + "uomo": 1667, + "pel": 1668, + "pote": 1669, + "essa": 1670, + "scu": 1671, + "signo": 1672, + "stro": 1673, + "uti": 1674, + "sione": 1675, + "gre": 1676, + "fini": 1677, + "lun": 1678, + "esi": 1679, + "passa": 1680, + "rà": 1681, + "mentre": 1682, + "hanno": 1683, + "usci": 1684, + "gia": 1685, + "già": 1686, + "mina": 1687, + "tica": 1688, + "giorno": 1689, + "esse": 1690, + "modo": 1691, + "spa": 1692, + "proprio": 1693, + "ori": 1694, + "contro": 1695, + "stru": 1696, + "diven": 1697, + "disse": 1698, + "rato": 1699, + "noi": 1700, + "vere": 1701, + "può": 1702, + "dice": 1703, + "cci": 1704, + "secon": 1705, + "ccio": 1706, + "qualche": 1707, + "tutta": 1708, + "gg": 1709, + "mondo": 1710, + "forma": 1711, + "mma": 1712, + "pensa": 1713, + "deva": 1714, + "fosse": 1715, + "sopra": 1716, + "tamente": 1717, + "ness": 1718, + "quanto": 1719, + "raga": 1720, + "unque": 1721, + "care": 1722, + "stre": 1723, + "grande": 1724, + "picco": 1725, + "guarda": 1726, + "nell": 1727, + "possi": 1728, + "presen": 1729, + "rò": 1730, + "paro": 1731, + "tua": 1732, + "vin": 1733, + "ane": 1734, + "stesso": 1735, + "dav": 1736, + "nei": 1737, + "nelle": 1738, + "ghi": 1739, + "pio": 1740, + "lato": 1741, + "sid": 1742, + "fine": 1743, + "fuo": 1744, + "quasi": 1745, + "ulti": 1746, + "ito": 1747, + "sue": 1748, + "fil": 1749, + "allora": 1750, + "veni": 1751, + "tano": 1752, + "ello": 1753, + "ão": 1754, + "não": 1755, + "uma": 1756, + "ela": 1757, + "lh": 1758, + "ção": 1759, + "cê": 1760, + "inha": 1761, + "você": 1762, + "ec": 1763, + "dade": 1764, + "ao": 1765, + "ram": 1766, + "vel": 1767, + "ém": 1768, + "pode": 1769, + "estava": 1770, + "isso": 1771, + "mui": 1772, + "faz": 1773, + "ões": 1774, + "pes": 1775, + "ix": 1776, + "sim": 1777, + "olh": 1778, + "isa": 1779, + "ên": 1780, + "tinha": 1781, + "meu": 1782, + "são": 1783, + "minha": 1784, + "muito": 1785, + "foi": 1786, + "bem": 1787, + "diz": 1788, + "parec": 1789, + "ço": 1790, + "pesso": 1791, + "pois": 1792, + "mesmo": 1793, + "ções": 1794, + "seus": 1795, + "até": 1796, + "ência": 1797, + "lhe": 1798, + "tiv": 1799, + "mã": 1800, + "só": 1801, + "tão": 1802, + "tudo": 1803, + "então": 1804, + "inda": 1805, + "bal": 1806, + "indo": 1807, + "ndo": 1808, + "já": 1809, + "vam": 1810, + "eito": 1811, + "depois": 1812, + "mel": 1813, + "lha": 1814, + "ainda": 1815, + "fazer": 1816, + "pou": 1817, + "pergun": 1818, + "deix": 1819, + "tamb": 1820, + "ala": 1821, + "pelo": 1822, + "também": 1823, + "fica": 1824, + "prec": 1825, + "eles": 1826, + "havia": 1827, + "lá": 1828, + "nas": 1829, + "gem": 1830, + "mem": 1831, + "ós": 1832, + "deu": 1833, + "eiro": 1834, + "..": 1835, + "assim": 1836, + "ior": 1837, + "har": 1838, + "aqui": 1839, + "cul": 1840, + "sar": 1841, + "outra": 1842, + "olhos": 1843, + "ima": 1844, + "mim": 1845, + "ago": 1846, + "pessoas": 1847, + "eram": 1848, + "eira": 1849, + "pela": 1850, + "coisa": 1851, + "mão": 1852, + "conh": 1853, + "agora": 1854, + "iam": 1855, + "há": 1856, + "suas": 1857, + "guém": 1858, + "cabe": 1859, + "nem": 1860, + "ível": 1861, + "consegu": 1862, + "trabal": 1863, + "lev": 1864, + "lem": 1865, + "vai": 1866, + "tei": 1867, + "pró": 1868, + "quem": 1869, + "onde": 1870, + "cabeça": 1871, + "nunca": 1872, + "mentos": 1873, + "hum": 1874, + "dele": 1875, + "verdade": 1876, + "tá": 1877, + "hos": 1878, + "algum": 1879, + "dizer": 1880, + "penas": 1881, + "nós": 1882, + "enquanto": 1883, + "outro": 1884, + "lho": 1885, + "melhor": 1886, + "primei": 1887, + "iu": 1888, + "apenas": 1889, + "estou": 1890, + "conte": 1891, + "homem": 1892, + "dois": 1893, + "ças": 1894, + "pouco": 1895, + "senhor": 1896, + "tando": 1897, + "espera": 1898, + "pai": 1899, + "rios": 1900, + "baix": 1901, + "ase": 1902, + "isas": 1903, + "hora": 1904, + "ficar": 1905, + "seja": 1906, + "ân": 1907, + "clar": 1908, + "inc": 1909, + "fos": 1910, + "ouvi": 1911, + "vem": 1912, + "tava": 1913, + "ário": 1914, + "sos": 1915, + "inho": 1916, + "rando": 1917, + "ês": 1918, + "coisas": 1919, + "aconte": 1920, + "lher": 1921, + "anos": 1922, + "talvez": 1923, + "estão": 1924, + "liv": 1925, + "outros": 1926, + "qualquer": 1927, + "gou": 1928, + "lí": 1929, + "tivesse": 1930, + "rado": 1931, + "precisa": 1932, + "mãe": 1933, + "dela": 1934, + "entra": 1935, + "maior": 1936, + "noite": 1937, + "tiva": 1938, + "pala": 1939, + "ração": 1940, + "deus": 1941, + "sas": 1942, + "inte": 1943, + "fei": 1944, + "palav": 1945, + "trás": 1946, + "cidade": 1947, + "lugar": 1948, + "vezes": 1949, + "encontra": 1950, + "tru": 1951, + "eci": 1952, + "ın": 1953, + "bir": 1954, + "yor": 1955, + "ek": 1956, + "dı": 1957, + "ey": 1958, + "tı": 1959, + "mı": 1960, + "iz": 1961, + "ır": 1962, + "gö": 1963, + "sı": 1964, + "bil": 1965, + "lı": 1966, + "üz": 1967, + "iç": 1968, + "iy": 1969, + "ım": 1970, + "uz": 1971, + "cak": 1972, + "iş": 1973, + "ını": 1974, + "iyor": 1975, + "baş": 1976, + "dü": 1977, + "değ": 1978, + "kar": 1979, + "ev": 1980, + "öy": 1981, + "bun": 1982, + "yap": 1983, + "sun": 1984, + "gör": 1985, + "yı": 1986, + "ki": 1987, + "ara": 1988, + "alı": 1989, + "onu": 1990, + "çı": 1991, + "şey": 1992, + "sın": 1993, + "kı": 1994, + "kad": 1995, + "ağ": 1996, + "değil": 1997, + "ük": 1998, + "çok": 1999, + "şı": 2000, + "ül": 2001, + "için": 2002, + "eye": 2003, + "oldu": 2004, + "mış": 2005, + "kal": 2006, + "mek": 2007, + "öyle": 2008, + "yordu": 2009, + "yüz": 2010, + "miş": 2011, + "mak": 2012, + "ola": 2013, + "yan": 2014, + "cek": 2015, + "yorum": 2016, + "bak": 2017, + "üm": 2018, + "ları": 2019, + "oğ": 2020, + "kadar": 2021, + "arı": 2022, + "ında": 2023, + "gün": 2024, + "yok": 2025, + "yer": 2026, + "dım": 2027, + "daha": 2028, + "ına": 2029, + "dim": 2030, + "bilir": 2031, + "iki": 2032, + "siz": 2033, + "diğ": 2034, + "bü": 2035, + "düş": 2036, + "üç": 2037, + "unu": 2038, + "aman": 2039, + "fak": 2040, + "ede": 2041, + "sonra": 2042, + "hiç": 2043, + "aki": 2044, + "ğı": 2045, + "bul": 2046, + "maz": 2047, + "anla": 2048, + "bura": 2049, + "geç": 2050, + "maya": 2051, + "konu": 2052, + "din": 2053, + "tek": 2054, + "zaman": 2055, + "eler": 2056, + "öz": 2057, + "dır": 2058, + "gibi": 2059, + "şa": 2060, + "leri": 2061, + "kim": 2062, + "ku": 2063, + "fakat": 2064, + "yar": 2065, + "göz": 2066, + "cı": 2067, + "yorsun": 2068, + "bek": 2069, + "inde": 2070, + "pek": 2071, + "bunu": 2072, + "lik": 2073, + "iler": 2074, + "edi": 2075, + "öl": 2076, + "sür": 2077, + "sır": 2078, + "çık": 2079, + "sıl": 2080, + "alar": 2081, + "kes": 2082, + "yak": 2083, + "çek": 2084, + "yıl": 2085, + "ecek": 2086, + "ız": 2087, + "git": 2088, + "kap": 2089, + "ama": 2090, + "ıl": 2091, + "ların": 2092, + "biz": 2093, + "tır": 2094, + "oy": 2095, + "ancak": 2096, + "doğ": 2097, + "bana": 2098, + "şim": 2099, + "başla": 2100, + "lü": 2101, + "madı": 2102, + "beni": 2103, + "yük": 2104, + "lık": 2105, + "beş": 2106, + "nasıl": 2107, + "tık": 2108, + "tür": 2109, + "daki": 2110, + "ceğ": 2111, + "zı": 2112, + "iyi": 2113, + "dok": 2114, + "benim": 2115, + "cağ": 2116, + "yen": 2117, + "şu": 2118, + "mez": 2119, + "düşün": 2120, + "kendi": 2121, + "şimdi": 2122, + "yol": 2123, + "yu": 2124, + "iste": 2125, + "sek": 2126, + "mam": 2127, + "söyle": 2128, + "dik": 2129, + "kur": 2130, + "olduğ": 2131, + "sını": 2132, + "biliyor": 2133, + "kan": 2134, + "yal": 2135, + "meye": 2136, + "muş": 2137, + "kaç": 2138, + "iye": 2139, + "tü": 2140, + "ef": 2141, + "tım": 2142, + "evet": 2143, + "yet": 2144, + "burada": 2145, + "tim": 2146, + "biraz": 2147, + "kor": 2148, + "doğru": 2149, + "inin": 2150, + "kız": 2151, + "diye": 2152, + "dör": 2153, + "etti": 2154, + "onun": 2155, + "isti": 2156, + "ği": 2157, + "sana": 2158, + "üş": 2159, + "arka": 2160, + "hayır": 2161, + "karşı": 2162, + "ile": 2163, + "hak": 2164, + "ıyor": 2165, + "neden": 2166, + "sev": 2167, + "sız": 2168, + "çocu": 2169, + "çalı": 2170, + "olur": 2171, + "bır": 2172, + "gir": 2173, + "ise": 2174, + "ih": 2175, + "kır": 2176, + "dön": 2177, + "böyle": 2178, + "seni": 2179, + "!\"": 2180, + "dört": 2181, + "söy": 2182, + "oş": 2183, + "musun": 2184, + "laş": 2185, + "ip": 2186, + "kay": 2187, + "hem": 2188, + "büyük": 2189, + "aç": 2190, + "bırak": 2191, + "misin": 2192, + "söz": 2193, + "değiş": 2194, + "ünü": 2195, + "gül": 2196, + "kö": 2197, + "karı": 2198, + "tamam": 2199, + "olu": 2200, + "yeni": 2201, + "lam": 2202, + "mıştı": 2203, + "yaş": 2204, + "iniz": 2205, + "kadın": 2206, + "bunun": 2207, + "mey": 2208, + "altı": 2209, + "yi": 2210, + "inden": 2211, + "senin": 2212, + "yat": 2213, + "top": 2214, + "isi": 2215, + "dün": 2216, + "hiçbir": 2217, + "yon": 2218, + "dın": 2219, + "tün": 2220, + "başka": 2221, + "hep": 2222, + "irmi": 2223, + "devam": 2224, + "olacak": 2225, + "artık": 2226, + "durum": 2227, + "imiz": 2228, + "üzel": 2229, + "lerini": 2230, + "sağ": 2231, + "gerek": 2232, + "yirmi": 2233, + "şek": 2234, + "bağ": 2235, + "lara": 2236, + "yür": 2237, + "ması": 2238, + "katı": 2239, + "dedi": 2240, + "gü": 2241, + "sorun": 2242, + "üne": 2243, + "mız": 2244, + "yapı": 2245, + "mil": 2246, + "ğını": 2247, + "tara": 2248, + "vardı": 2249, + "konuş": 2250, + "arak": 2251, + "larak": 2252, + "çocuk": 2253, + "bütün": 2254, + "ley": 2255, + "dür": 2256, + "güzel": 2257, + "ayı": 2258, + "yapa": 2259, + "nı": 2260, + "ayr": 2261, + "öne": 2262, + "yordum": 2263, + "ban": 2264, + "i̇ş": 2265, + "dum": 2266, + "yorlar": 2267, + "larını": 2268, + "çıkar": 2269, + "zan": 2270, + "seç": 2271, + "liyor": 2272, + "tak": 2273, + "şık": 2274, + "tekrar": 2275, + "aş": 2276, + "eş": 2277, + "mişti": 2278, + "kin": 2279, + "imi": 2280, + "eğ": 2281, + "gidi": 2282, + "leş": 2283, + "başladı": 2284, + "gide": 2285, + "otur": 2286, + "dde": 2287, + "ından": 2288, + "üzer": 2289, + "ının": 2290, + "nız": 2291, + "uy": 2292, + "yedi": 2293, + "kat": 2294, + "olarak": 2295, + "ladı": 2296, + "yalnız": 2297, + "bah": 2298, + "iyet": 2299, + "sak": 2300, + "açık": 2301, + "sında": 2302, + "...": 2303, + "insan": 2304, + "aynı": 2305, + "eder": 2306, + "istan": 2307, + "uzun": 2308, + "geri": 2309, + "erek": 2310, + "olan": 2311, + "gerçek": 2312, + "alan": 2313, + "dış": 2314, + "alık": 2315, + "fark": 2316, + "üst": 2317, + "sade": 2318, + "kiş": 2319, + "ldı": 2320, + "zor": 2321, + "etir": 2322, + "herkes": 2323, + "ömer": 2324, + "unda": 2325, + "haf": 2326, + "buna": 2327, + "ydı": 2328, + "peki": 2329, + "adam": 2330, + "haz": 2331, + "sına": 2332, + "kapı": 2333, + "görüş": 2334, + "sadece": 2335, + "aldı": 2336, + "geldi": 2337, + "rz": 2338, + "sz": 2339, + "cz": 2340, + "ię": 2341, + "dz": 2342, + "ał": 2343, + "się": 2344, + "rze": 2345, + "że": 2346, + "wy": 2347, + "rzy": 2348, + "ła": 2349, + "ło": 2350, + "ny": 2351, + "dzie": 2352, + "dzi": 2353, + "czy": 2354, + "cie": 2355, + "prze": 2356, + "dy": 2357, + "kie": 2358, + "ry": 2359, + "ją": 2360, + "ów": 2361, + "przy": 2362, + "mie": 2363, + "szy": 2364, + "cze": 2365, + "bie": 2366, + "cy": 2367, + "nia": 2368, + "ści": 2369, + "sze": 2370, + "jest": 2371, + "ży": 2372, + "ną": 2373, + "któ": 2374, + "ała": 2375, + "mnie": 2376, + "ły": 2377, + "cza": 2378, + "jak": 2379, + "roz": 2380, + "ró": 2381, + "zna": 2382, + "łu": 2383, + "ść": 2384, + "wia": 2385, + "wszy": 2386, + "spo": 2387, + "gdy": 2388, + "wał": 2389, + "wię": 2390, + "łem": 2391, + "ję": 2392, + "sk": 2393, + "rę": 2394, + "dob": 2395, + "już": 2396, + "bę": 2397, + "ałem": 2398, + "sza": 2399, + "pod": 2400, + "dla": 2401, + "pan": 2402, + "nę": 2403, + "może": 2404, + "śli": 2405, + "ało": 2406, + "lko": 2407, + "nych": 2408, + "powie": 2409, + "cię": 2410, + "tylko": 2411, + "naj": 2412, + "tego": 2413, + "ski": 2414, + "nego": 2415, + "wszyst": 2416, + "szcze": 2417, + "jed": 2418, + "jej": 2419, + "two": 2420, + "ąd": 2421, + "śmy": 2422, + "czę": 2423, + "wać": 2424, + "jego": 2425, + "ża": 2426, + "sy": 2427, + "praw": 2428, + "tym": 2429, + "który": 2430, + "ały": 2431, + "trze": 2432, + "niej": 2433, + "nym": 2434, + "gło": 2435, + "jąc": 2436, + "mówi": 2437, + "ska": 2438, + "nej": 2439, + "słu": 2440, + "wła": 2441, + "będzie": 2442, + "dę": 2443, + "pó": 2444, + "bez": 2445, + "nic": 2446, + "pła": 2447, + "ście": 2448, + "są": 2449, + "trzy": 2450, + "kiem": 2451, + "był": 2452, + "mog": 2453, + "robi": 2454, + "tam": 2455, + "mię": 2456, + "zy": 2457, + "pew": 2458, + "myś": 2459, + "przed": 2460, + "sko": 2461, + "które": 2462, + "lę": 2463, + "wsze": 2464, + "ąc": 2465, + "było": 2466, + "sobie": 2467, + "py": 2468, + "cią": 2469, + "jeszcze": 2470, + "tę": 2471, + "czas": 2472, + "szę": 2473, + "gł": 2474, + "kę": 2475, + "czu": 2476, + "przez": 2477, + "sło": 2478, + "wz": 2479, + "kto": 2480, + "ków": 2481, + "czo": 2482, + "liśmy": 2483, + "więc": 2484, + "rą": 2485, + "wó": 2486, + "rza": 2487, + "ności": 2488, + "wet": 2489, + "nął": 2490, + "śmie": 2491, + "nawet": 2492, + "musi": 2493, + "swo": 2494, + "tej": 2495, + "wą": 2496, + "wu": 2497, + "wią": 2498, + "niu": 2499, + "czą": 2500, + "dzo": 2501, + "skie": 2502, + "jeśli": 2503, + "czego": 2504, + "chy": 2505, + "dł": 2506, + "tych": 2507, + "bym": 2508, + "żo": 2509, + "eś": 2510, + "sią": 2511, + "kiedy": 2512, + "wró": 2513, + "dze": 2514, + "dro": 2515, + "rów": 2516, + "pani": 2517, + "kul": 2518, + "nad": 2519, + "chwi": 2520, + "nim": 2521, + "być": 2522, + "chodzi": 2523, + "nio": 2524, + "dobrze": 2525, + "teraz": 2526, + "wokul": 2527, + "coś": 2528, + "kł": 2529, + "pier": 2530, + "gdzie": 2531, + "dzy": 2532, + "pię": 2533, + "dź": 2534, + "ką": 2535, + "gó": 2536, + "zda": 2537, + "chce": 2538, + "stę": 2539, + "świa": 2540, + "wszystko": 2541, + "peł": 2542, + "wiem": 2543, + "wiel": 2544, + "każ": 2545, + "rzu": 2546, + "sły": 2547, + "jedna": 2548, + "myśl": 2549, + "mój": 2550, + "jestem": 2551, + "óż": 2552, + "miej": 2553, + "moż": 2554, + "kła": 2555, + "resz": 2556, + "dłu": 2557, + "stwo": 2558, + "nię": 2559, + "masz": 2560, + "żeby": 2561, + "niem": 2562, + "jakie": 2563, + "sty": 2564, + "nią": 2565, + "wej": 2566, + "oj": 2567, + "sła": 2568, + "ność": 2569, + "zło": 2570, + "szczę": 2571, + "lej": 2572, + "wego": 2573, + "cał": 2574, + "dział": 2575, + "kich": 2576, + "dza": 2577, + "dzię": 2578, + "oczy": 2579, + "zosta": 2580, + "czło": 2581, + "nam": 2582, + "kil": 2583, + "szu": 2584, + "wę": 2585, + "miał": 2586, + "strze": 2587, + "cej": 2588, + "ej": 2589, + "znaj": 2590, + "dać": 2591, + "miejs": 2592, + "kró": 2593, + "kry": 2594, + "bardzo": 2595, + "śnie": 2596, + "lą": 2597, + "gie": 2598, + "ciebie": 2599, + "dni": 2600, + "potrze": 2601, + "wokulski": 2602, + "uwa": 2603, + "umie": 2604, + "jednak": 2605, + "kra": 2606, + "wróci": 2607, + "człowie": 2608, + "czyć": 2609, + "była": 2610, + "żeli": 2611, + "mę": 2612, + "cę": 2613, + "zrobi": 2614, + "mogę": 2615, + "prowa": 2616, + "rem": 2617, + "niech": 2618, + "cznie": 2619, + "kro": 2620, + "tą": 2621, + "chci": 2622, + "bro": 2623, + "dzieć": 2624, + "szą": 2625, + "pad": 2626, + "trz": 2627, + "jem": 2628, + "tów": 2629, + "dru": 2630, + "taj": 2631, + "rzekł": 2632, + "niego": 2633, + "takie": 2634, + "wała": 2635, + "towa": 2636, + "kapła": 2637, + "widzi": 2638, + "podob": 2639, + "dzę": 2640, + "tał": 2641, + "stęp": 2642, + "bą": 2643, + "poko": 2644, + "wem": 2645, + "gę": 2646, + "aby": 2647, + "albo": 2648, + "spra": 2649, + "zno": 2650, + "smo": 2651, + "jesz": 2652, + "księ": 2653, + "jesteś": 2654, + "poz": 2655, + "nigdy": 2656, + "ksią": 2657, + "cóż": 2658, + "ws": 2659, + "pow": 2660, + "tka": 2661, + "świe": 2662, + "szka": 2663, + "samo": 2664, + "sł": 2665, + "rzę": 2666, + "nale": 2667, + "chcesz": 2668, + "nik": 2669, + "pę": 2670, + "chyba": 2671, + "ciąg": 2672, + "jący": 2673, + "woj": 2674, + "nasze": 2675, + "mniej": 2676, + "więcej": 2677, + "zwy": 2678, + "osta": 2679, + "waż": 2680, + "śmier": 2681, + "wier": 2682, + "dzą": 2683, + "zaś": 2684, + "gdyby": 2685, + "jaki": 2686, + "wol": 2687, + "win": 2688, + "dą": 2689, + "ścia": 2690, + "rozma": 2691, + "wal": 2692, + "panie": 2693, + "star": 2694, + "kaz": 2695, + "jeżeli": 2696, + "wra": 2697, + "koń": 2698, + "siebie": 2699, + "znowu": 2700, + "czem": 2701, + "stwa": 2702, + "isto": 2703, + "pół": 2704, + "dał": 2705, + "kobie": 2706, + "ałam": 2707, + "wych": 2708, + "cesa": 2709, + "nich": 2710, + "zawsze": 2711, + "dzić": 2712, + "też": 2713, + "lepie": 2714, + "proszę": 2715, + "kre": 2716, + "twa": 2717, + "łą": 2718, + "chu": 2719, + "cą": 2720, + "prz": 2721, + "łe": 2722, + "szedł": 2723, + "odpowie": 2724, + "myśli": 2725, + "świą": 2726, + "ź": 2727, + "ł": 2728, + "&": 2729, + "=": 2730, + "ă": 2731, + "đ": 2732, + "ţ": 2733, + "–": 2734, + "‘": 2735, + "ij": 2736, + "aa": 2737, + "een": 2738, + "het": 2739, + "aar": 2740, + "oor": 2741, + "ijn": 2742, + "dat": 2743, + "oe": 2744, + "ijk": 2745, + "aan": 2746, + "voor": 2747, + "iet": 2748, + "zijn": 2749, + "niet": 2750, + "oo": 2751, + "moet": 2752, + "heb": 2753, + "uit": 2754, + "wij": 2755, + "aat": 2756, + "lijk": 2757, + "sl": 2758, + "daar": 2759, + "deze": 2760, + "worden": 2761, + "moeten": 2762, + "onder": 2763, + "hebben": 2764, + "ook": 2765, + "ct": 2766, + "nog": 2767, + "aal": 2768, + "eer": 2769, + "bij": 2770, + "mijn": 2771, + "kom": 2772, + "atie": 2773, + "eft": 2774, + "kel": 2775, + "rij": 2776, + "heid": 2777, + "af": 2778, + "stel": 2779, + "maar": 2780, + "wee": 2781, + "heeft": 2782, + "waar": 2783, + "eren": 2784, + "wat": 2785, + "wil": 2786, + "aag": 2787, + "bet": 2788, + "hij": 2789, + "kun": 2790, + "uw": 2791, + "dt": 2792, + "door": 2793, + "tij": 2794, + "ond": 2795, + "geen": 2796, + "gev": 2797, + "veel": 2798, + "naar": 2799, + "aten": 2800, + "kunnen": 2801, + "echt": 2802, + "goe": 2803, + "twee": 2804, + "delijk": 2805, + "uur": 2806, + "toe": 2807, + "meer": 2808, + "onze": 2809, + "tijd": 2810, + "hoe": 2811, + "tot": 2812, + "zou": 2813, + "aak": 2814, + "amen": 2815, + "woor": 2816, + "wordt": 2817, + "gelijk": 2818, + "gaan": 2819, + "ker": 2820, + "eld": 2821, + "hou": 2822, + "zel": 2823, + "tegen": 2824, + "komen": 2825, + "werk": 2826, + "goed": 2827, + "zal": 2828, + "zij": 2829, + "slag": 2830, + "zien": 2831, + "echter": 2832, + "itie": 2833, + "tie": 2834, + "elijk": 2835, + "ische": 2836, + "belan": 2837, + "haar": 2838, + "vr": 2839, + "grijk": 2840, + "doen": 2841, + "land": 2842, + "belangrijk": 2843, + "open": 2844, + "ctie": 2845, + "zelf": 2846, + "mij": 2847, + "iteit": 2848, + "stem": 2849, + "mee": 2850, + "aren": 2851, + "dien": 2852, + "gaat": 2853, + "prob": 2854, + "moe": 2855, + "ullen": 2856, + "zich": 2857, + "daarom": 2858, + "orm": 2859, + "staat": 2860, + "zit": 2861, + "dui": 2862, + "dus": 2863, + "ds": 2864, + "verslag": 2865, + "kelijk": 2866, + "proble": 2867, + "schap": 2868, + "gd": 2869, + "hun": 2870, + "erd": 2871, + "zet": 2872, + "staan": 2873, + "maal": 2874, + "inder": 2875, + "eid": 2876, + "kken": 2877, + "ged": 2878, + "zullen": 2879, + "mensen": 2880, + "jaar": 2881, + "regel": 2882, + "ieder": 2883, + "volgen": 2884, + "geven": 2885, + "even": 2886, + "blij": 2887, + "ië": 2888, + "uwe": 2889, + "maken": 2890, + "oek": 2891, + "nieuwe": 2892, + "baar": 2893, + "andere": 2894, + "ruik": 2895, + "agen": 2896, + "ouw": 2897, + "willen": 2898, + "aakt": 2899, + "hoo": 2900, + "anden": 2901, + "lig": 2902, + "samen": 2903, + "zeer": 2904, + "duidelijk": 2905, + "antwoor": 2906, + "heel": 2907, + "punt": 2908, + "houden": 2909, + "vraag": 2910, + "gele": 2911, + "eens": 2912, + "besch": 2913, + "omen": 2914, + "erg": 2915, + "doel": 2916, + "dag": 2917, + "uren": 2918, + "ings": 2919, + "oren": 2920, + "delen": 2921, + "steun": 2922, + "innen": 2923, + "pol": 2924, + "oon": 2925, + "sn": 2926, + "zonder": 2927, + "nodig": 2928, + "alleen": 2929, + "mid": 2930, + "ragen": 2931, + "iets": 2932, + "versch": 2933, + "gebruik": 2934, + "rouw": 2935, + "stellen": 2936, + "menten": 2937, + "eerste": 2938, + "laat": 2939, + "groot": 2940, + "ood": 2941, + "toch": 2942, + "laten": 2943, + "aard": 2944, + "sle": 2945, + "deel": 2946, + "plaat": 2947, + "ree": 2948, + "betre": 2949, + "lid": 2950, + "uiten": 2951, + "racht": 2952, + "beleid": 2953, + "stie": 2954, + "staten": 2955, + "ggen": 2956, + "reken": 2957, + "alen": 2958, + "ming": 2959, + "mogelijk": 2960, + "grote": 2961, + "altijd": 2962, + "enkel": 2963, + "wik": 2964, + "politie": 2965, + "elk": 2966, + "handel": 2967, + "kwe": 2968, + "maat": 2969, + "elen": 2970, + "vrij": 2971, + "jes": 2972, + "aam": 2973, + "huis": 2974, + "weer": 2975, + "lidstaten": 2976, + "king": 2977, + "kle": 2978, + "bed": 2979, + "geval": 2980, + "wikkel": 2981, + "kwestie": 2982, + "stee": 2983, + "hel": 2984, + "komst": 2985, + "iden": 2986, + "eerd": 2987, + "tweede": 2988, + "probleem": 2989, + "ussen": 2990, + "snel": 2991, + "tig": 2992, + "ult": 2993, + "nemen": 2994, + "commis": 2995, + "verschil": 2996, + "zoek": 2997, + "krij": 2998, + "graag": 2999, + "denk": 3000, + "landen": 3001, + "reden": 3002, + "besl": 3003, + "oeg": 3004, + "beter": 3005, + "heden": 3006, + "mag": 3007, + "boven": 3008, + "cont": 3009, + "fd": 3010, + "hele": 3011, + "vier": 3012, + "gez": 3013, + "kw": 3014, + "aas": 3015, + "ontwikkel": 3016, + "drie": 3017, + "vaak": 3018, + "plaats": 3019, + "gang": 3020, + "ijf": 3021, + "natuur": 3022, + "tussen": 3023, + "bat": 3024, + "komt": 3025, + "wacht": 3026, + "aad": 3027, + "achter": 3028, + "gebie": 3029, + "verk": 3030, + "ligt": 3031, + "nieuw": 3032, + "vand": 3033, + "ý": 3034, + "ď": 3035, + "ě": 3036, + "ř": 3037, + "ť": 3038, + "ů": 3039, + "„": 3040, + "ní": 3041, + "ně": 3042, + "ře": 3043, + "ná": 3044, + "vě": 3045, + "vá": 3046, + "rá": 3047, + "vy": 3048, + "mě": 3049, + "ři": 3050, + "ří": 3051, + "že": 3052, + "jí": 3053, + "vý": 3054, + "ji": 3055, + "dě": 3056, + "če": 3057, + "tě": 3058, + "ky": 3059, + "še": 3060, + "ké": 3061, + "ší": 3062, + "pře": 3063, + "ví": 3064, + "ný": 3065, + "ži": 3066, + "má": 3067, + "cí": 3068, + "zá": 3069, + "ské": 3070, + "dá": 3071, + "byl": 3072, + "tí": 3073, + "pří": 3074, + "při": 3075, + "či": 3076, + "vní": 3077, + "ča": 3078, + "dí": 3079, + "dní": 3080, + "ká": 3081, + "nou": 3082, + "vět": 3083, + "pě": 3084, + "kou": 3085, + "ých": 3086, + "bě": 3087, + "prá": 3088, + "jako": 3089, + "ží": 3090, + "zí": 3091, + "jsou": 3092, + "jsem": 3093, + "lní": 3094, + "cké": 3095, + "vat": 3096, + "před": 3097, + "hla": 3098, + "stá": 3099, + "čí": 3100, + "ši": 3101, + "kla": 3102, + "ště": 3103, + "lou": 3104, + "mů": 3105, + "chá": 3106, + "pů": 3107, + "také": 3108, + "dů": 3109, + "nost": 3110, + "tře": 3111, + "sku": 3112, + "vše": 3113, + "tní": 3114, + "byla": 3115, + "ční": 3116, + "jeho": 3117, + "bý": 3118, + "vání": 3119, + "ných": 3120, + "tři": 3121, + "vz": 3122, + "stře": 3123, + "dva": 3124, + "hle": 3125, + "čá": 3126, + "nosti": 3127, + "vš": 3128, + "hra": 3129, + "jen": 3130, + "slo": 3131, + "však": 3132, + "kdy": 3133, + "bylo": 3134, + "bude": 3135, + "jší": 3136, + "vých": 3137, + "ním": 3138, + "sm": 3139, + "koli": 3140, + "rů": 3141, + "může": 3142, + "není": 3143, + "hod": 3144, + "bí": 3145, + "tý": 3146, + "stě": 3147, + "uje": 3148, + "sá": 3149, + "pět": 3150, + "krá": 3151, + "tom": 3152, + "ství": 3153, + "vně": 3154, + "sed": 3155, + "své": 3156, + "pí": 3157, + "musí": 3158, + "už": 3159, + "tím": 3160, + "jící": 3161, + "jedno": 3162, + "čas": 3163, + "čty": 3164, + "ský": 3165, + "evro": 3166, + "toho": 3167, + "hy": 3168, + "kter": 3169, + "rní": 3170, + "stí": 3171, + "svě": 3172, + "pak": 3173, + "všech": 3174, + "ků": 3175, + "ng": 3176, + "ád": 3177, + "chází": 3178, + "být": 3179, + "první": 3180, + "mno": 3181, + "ského": 3182, + "pá": 3183, + "nebo": 3184, + "kem": 3185, + "sla": 3186, + "ného": 3187, + "zde": 3188, + "další": 3189, + "řa": 3190, + "čtyři": 3191, + "hrá": 3192, + "druh": 3193, + "lně": 3194, + "vla": 3195, + "ských": 3196, + "ško": 3197, + "půso": 3198, + "proto": 3199, + "vů": 3200, + "ská": 3201, + "šest": 3202, + "dně": 3203, + "ještě": 3204, + "mezi": 3205, + "několi": 3206, + "již": 3207, + "čně": 3208, + "slu": 3209, + "zná": 3210, + "sedm": 3211, + "vlá": 3212, + "osm": 3213, + "byly": 3214, + "vám": 3215, + "cký": 3216, + "tech": 3217, + "ději": 3218, + "velmi": 3219, + "leži": 3220, + "vala": 3221, + "lý": 3222, + "tvo": 3223, + "spole": 3224, + "stup": 3225, + "mož": 3226, + "evrop": 3227, + "stal": 3228, + "jde": 3229, + "rodi": 3230, + "její": 3231, + "poli": 3232, + "devět": 3233, + "sme": 3234, + "až": 3235, + "této": 3236, + "tento": 3237, + "kaž": 3238, + "nula": 3239, + "bych": 3240, + "moc": 3241, + "stou": 3242, + "kdo": 3243, + "zd": 3244, + "praco": 3245, + "tomu": 3246, + "ným": 3247, + "živo": 3248, + "zem": 3249, + "násle": 3250, + "sky": 3251, + "jich": 3252, + "měl": 3253, + "děla": 3254, + "jsme": 3255, + "nice": 3256, + "stej": 3257, + "stní": 3258, + "náro": 3259, + "nit": 3260, + "později": 3261, + "tako": 3262, + "nce": 3263, + "čer": 3264, + "ším": 3265, + "něco": 3266, + "vál": 3267, + "řej": 3268, + "krát": 3269, + "ální": 3270, + "asi": 3271, + "které": 3272, + "stav": 3273, + "mají": 3274, + "mys": 3275, + "době": 3276, + "sně": 3277, + "zku": 3278, + "tů": 3279, + "chod": 3280, + "spě": 3281, + "jejich": 3282, + "součas": 3283, + "vali": 3284, + "kte": 3285, + "prů": 3286, + "zení": 3287, + "pat": 3288, + "potře": 3289, + "dnes": 3290, + "zemí": 3291, + "znam": 3292, + "mám": 3293, + "tedy": 3294, + "hlavní": 3295, + "použí": 3296, + "bní": 3297, + "vede": 3298, + "lep": 3299, + "jek": 3300, + "prav": 3301, + "politi": 3302, + "dne": 3303, + "čení": 3304, + "než": 3305, + "děl": 3306, + "čo": 3307, + "cích": 3308, + "sté": 3309, + "dlou": 3310, + "několik": 3311, + "vyu": 3312, + "ckých": 3313, + "nové": 3314, + "čin": 3315, + "dělá": 3316, + "ký": 3317, + "obla": 3318, + "podle": 3319, + "důleži": 3320, + "poku": 3321, + "kone": 3322, + "dý": 3323, + "dvě": 3324, + "žád": 3325, + "nout": 3326, + "tku": 3327, + "tvr": 3328, + "ckého": 3329, + "rov": 3330, + "tele": 3331, + "psa": 3332, + "svět": 3333, + "tivní": 3334, + "dosta": 3335, + "šel": 3336, + "druhé": 3337, + "skou": 3338, + "žo": 3339, + "jedná": 3340, + "význam": 3341, + "problé": 3342, + "publi": 3343, + "ván": 3344, + "odpo": 3345, + "podpo": 3346, + "dle": 3347, + "jaké": 3348, + "šení": 3349, + "vím": 3350, + "během": 3351, + "nachází": 3352, + "slou": 3353, + "pouze": 3354, + "otá": 3355, + "plo": 3356, + "tové": 3357, + "větši": 3358, + "komi": 3359, + "vají": 3360, + "tyto": 3361, + "zápa": 3362, + "změ": 3363, + "moh": 3364, + "více": 3365, + "společ": 3366, + "auto": 3367, + "proti": 3368, + "dět": 3369, + "cháze": 3370, + "žel": 3371, + "«": 3372, + "»": 3373, + "а": 3374, + "б": 3375, + "в": 3376, + "г": 3377, + "д": 3378, + "е": 3379, + "ж": 3380, + "з": 3381, + "и": 3382, + "й": 3383, + "к": 3384, + "л": 3385, + "м": 3386, + "н": 3387, + "о": 3388, + "п": 3389, + "р": 3390, + "с": 3391, + "т": 3392, + "у": 3393, + "ф": 3394, + "х": 3395, + "ц": 3396, + "ч": 3397, + "ш": 3398, + "щ": 3399, + "ъ": 3400, + "ы": 3401, + "ь": 3402, + "э": 3403, + "ю": 3404, + "я": 3405, + "ё": 3406, + "‑": 3407, + "−": 3408, + "ст": 3409, + "ен": 3410, + "но": 3411, + "на": 3412, + "пр": 3413, + "то": 3414, + "по": 3415, + "ра": 3416, + "го": 3417, + "ко": 3418, + "не": 3419, + "во": 3420, + "ва": 3421, + "ет": 3422, + "ер": 3423, + "ни": 3424, + "ел": 3425, + "ит": 3426, + "ны": 3427, + "за": 3428, + "ро": 3429, + "ени": 3430, + "ка": 3431, + "ли": 3432, + "ем": 3433, + "да": 3434, + "об": 3435, + "ла": 3436, + "до": 3437, + "ся": 3438, + "ть": 3439, + "от": 3440, + "ло": 3441, + "ль": 3442, + "ед": 3443, + "со": 3444, + "ми": 3445, + "ре": 3446, + "мо": 3447, + "ци": 3448, + "про": 3449, + "та": 3450, + "это": 3451, + "ки": 3452, + "ру": 3453, + "при": 3454, + "ти": 3455, + "се": 3456, + "ста": 3457, + "вы": 3458, + "мы": 3459, + "ви": 3460, + "бы": 3461, + "ма": 3462, + "ес": 3463, + "ля": 3464, + "сти": 3465, + "ле": 3466, + "что": 3467, + "ме": 3468, + "ри": 3469, + "ча": 3470, + "од": 3471, + "ей": 3472, + "ель": 3473, + "ения": 3474, + "га": 3475, + "ну": 3476, + "си": 3477, + "па": 3478, + "раз": 3479, + "бо": 3480, + "сто": 3481, + "су": 3482, + "са": 3483, + "ду": 3484, + "его": 3485, + "ест": 3486, + "ин": 3487, + "ить": 3488, + "из": 3489, + "же": 3490, + "му": 3491, + "пер": 3492, + "под": 3493, + "ение": 3494, + "сь": 3495, + "ку": 3496, + "пред": 3497, + "ного": 3498, + "ных": 3499, + "вер": 3500, + "те": 3501, + "ной": 3502, + "ции": 3503, + "де": 3504, + "ры": 3505, + "дел": 3506, + "лю": 3507, + "ве": 3508, + "он": 3509, + "мен": 3510, + "ги": 3511, + "ня": 3512, + "бу": 3513, + "пра": 3514, + "все": 3515, + "ется": 3516, + "сть": 3517, + "жа": 3518, + "дол": 3519, + "жи": 3520, + "бе": 3521, + "кон": 3522, + "сл": 3523, + "ши": 3524, + "ди": 3525, + "ств": 3526, + "ско": 3527, + "ные": 3528, + "чи": 3529, + "ют": 3530, + "дер": 3531, + "стра": 3532, + "ты": 3533, + "ход": 3534, + "щи": 3535, + "зо": 3536, + "зна": 3537, + "ности": 3538, + "чес": 3539, + "вля": 3540, + "вать": 3541, + "ор": 3542, + "пол": 3543, + "вет": 3544, + "так": 3545, + "ша": 3546, + "ту": 3547, + "сво": 3548, + "пре": 3549, + "она": 3550, + "итель": 3551, + "ный": 3552, + "сло": 3553, + "как": 3554, + "вл": 3555, + "ность": 3556, + "хо": 3557, + "мож": 3558, + "пе": 3559, + "для": 3560, + "ния": 3561, + "ное": 3562, + "рас": 3563, + "долж": 3564, + "дар": 3565, + "тель": 3566, + "ска": 3567, + "пу": 3568, + "ство": 3569, + "кото": 3570, + "раб": 3571, + "ее": 3572, + "род": 3573, + "эти": 3574, + "соб": 3575, + "ору": 3576, + "жен": 3577, + "ным": 3578, + "ити": 3579, + "ние": 3580, + "ком": 3581, + "дет": 3582, + "сту": 3583, + "гу": 3584, + "пи": 3585, + "меж": 3586, + "ению": 3587, + "тер": 3588, + "работ": 3589, + "воз": 3590, + "ция": 3591, + "кой": 3592, + "щест": 3593, + "гра": 3594, + "зи": 3595, + "ря": 3596, + "между": 3597, + "ства": 3598, + "вс": 3599, + "ело": 3600, + "ше": 3601, + "мер": 3602, + "ба": 3603, + "зы": 3604, + "лу": 3605, + "аль": 3606, + "дей": 3607, + "гла": 3608, + "народ": 3609, + "кти": 3610, + "предста": 3611, + "лся": 3612, + "явля": 3613, + "ски": 3614, + "нов": 3615, + "един": 3616, + "ров": 3617, + "ис": 3618, + "нима": 3619, + "рем": 3620, + "ходи": 3621, + "также": 3622, + "дру": 3623, + "ать": 3624, + "след": 3625, + "гово": 3626, + "ная": 3627, + "ющи": 3628, + "ень": 3629, + "которы": 3630, + "хот": 3631, + "ву": 3632, + "их": 3633, + "ему": 3634, + "чит": 3635, + "важ": 3636, + "орга": 3637, + "чески": 3638, + "ще": 3639, + "ке": 3640, + "ха": 3641, + "пос": 3642, + "том": 3643, + "боль": 3644, + "мне": 3645, + "пас": 3646, + "объ": 3647, + "прав": 3648, + "конф": 3649, + "слу": 3650, + "поддер": 3651, + "стви": 3652, + "наш": 3653, + "лько": 3654, + "стоя": 3655, + "ную": 3656, + "лем": 3657, + "енных": 3658, + "кра": 3659, + "ды": 3660, + "международ": 3661, + "гда": 3662, + "необ": 3663, + "госу": 3664, + "ству": 3665, + "ении": 3666, + "государ": 3667, + "кто": 3668, + "им": 3669, + "чест": 3670, + "рет": 3671, + "вопро": 3672, + "лен": 3673, + "ели": 3674, + "рова": 3675, + "ций": 3676, + "нам": 3677, + "этой": 3678, + "жения": 3679, + "необходи": 3680, + "меня": 3681, + "было": 3682, + "сили": 3683, + "фи": 3684, + "вя": 3685, + "шь": 3686, + "этого": 3687, + "они": 3688, + "органи": 3689, + "безо": 3690, + "проб": 3691, + "име": 3692, + "реш": 3693, + "би": 3694, + "безопас": 3695, + "ются": 3696, + "оста": 3697, + "енно": 3698, + "год": 3699, + "ела": 3700, + "представ": 3701, + "ться": 3702, + "слово": 3703, + "организа": 3704, + "должны": 3705, + "этом": 3706, + "бла": 3707, + "че": 3708, + "чу": 3709, + "благо": 3710, + "этому": 3711, + "врем": 3712, + "спе": 3713, + "ном": 3714, + "ений": 3715, + "спо": 3716, + "нас": 3717, + "нет": 3718, + "зу": 3719, + "вед": 3720, + "еще": 3721, + "сказа": 3722, + "сей": 3723, + "ерен": 3724, + "дан": 3725, + "сам": 3726, + "еля": 3727, + "ран": 3728, + "зыва": 3729, + "является": 3730, + "будет": 3731, + "ктив": 3732, + "тре": 3733, + "деле": 3734, + "мот": 3735, + "конферен": 3736, + "лась": 3737, + "час": 3738, + "сторо": 3739, + "кого": 3740, + "ез": 3741, + "ней": 3742, + "ос": 3743, + "лись": 3744, + "разору": 3745, + "пере": 3746, + "сси": 3747, + "ными": 3748, + "проц": 3749, + "голо": 3750, + "чело": 3751, + "боле": 3752, + "челове": 3753, + "сер": 3754, + "пл": 3755, + "чет": 3756, + "стран": 3757, + "пя": 3758, + "был": 3759, + "кла": 3760, + "тов": 3761, + "жд": 3762, + "дела": 3763, + "ера": 3764, + "уже": 3765, + "совет": 3766, + "ген": 3767, + "безопасности": 3768, + "ца": 3769, + "седа": 3770, + "поз": 3771, + "ответ": 3772, + "проблем": 3773, + "нако": 3774, + "тем": 3775, + "доста": 3776, + "пы": 3777, + "ща": 3778, + "вой": 3779, + "сущест": 3780, + "необходимо": 3781, + "быть": 3782, + "может": 3783, + "дем": 3784, + "чтобы": 3785, + "ек": 3786, + "чер": 3787, + "усили": 3788, + "рес": 3789, + "руд": 3790, + "единенных": 3791, + "доб": 3792, + "дости": 3793, + "ствен": 3794, + "ядер": 3795, + "годня": 3796, + "каза": 3797, + "сегодня": 3798, + "сейчас": 3799, + "только": 3800, + "вод": 3801, + "есь": 3802, + "много": 3803, + "буду": 3804, + "ев": 3805, + "есть": 3806, + "три": 3807, + "общест": 3808, + "явл": 3809, + "высту": 3810, + "ред": 3811, + "счит": 3812, + "сит": 3813, + "делега": 3814, + "лож": 3815, + "этот": 3816, + "фор": 3817, + "клю": 3818, + "возмож": 3819, + "вания": 3820, + "бли": 3821, + "или": 3822, + "вз": 3823, + "наций": 3824, + "ского": 3825, + "приня": 3826, + "пла": 3827, + "оч": 3828, + "иться": 3829, + "сте": 3830, + "наши": 3831, + "которые": 3832, + "ар": 3833, + "имеет": 3834, + "сот": 3835, + "знач": 3836, + "перь": 3837, + "следу": 3838, + "ены": 3839, + "таки": 3840, + "объединенных": 3841, + "стро": 3842, + "теперь": 3843, + "бле": 3844, + "благодар": 3845, + "разв": 3846, + "ан": 3847, + "жива": 3848, + "очень": 3849, + "ят": 3850, + "без": 3851, + "обес": 3852, + "гро": 3853, + "лось": 3854, + "сы": 3855, + "организации": 3856, + "член": 3857, + "того": 3858, + "ональ": 3859, + "жда": 3860, + "всех": 3861, + "свя": 3862, + "более": 3863, + "сов": 3864, + "когда": 3865, + "вот": 3866, + "кре": 3867, + "кры": 3868, + "поэтому": 3869, + "воль": 3870, + "ой": 3871, + "генера": 3872, + "чем": 3873, + "лы": 3874, + "полити": 3875, + "вен": 3876, + "конференции": 3877, + "процес": 3878, + "бя": 3879, + "ите": 3880, + "отно": 3881, + "развити": 3882, + "аф": 3883, + "ющ": 3884, + "вно": 3885, + "мир": 3886, + "нии": 3887, + "кая": 3888, + "ас": 3889, + "ительно": 3890, + "вто": 3891, + "ением": 3892, + "генераль": 3893, + "прот": 3894, + "всем": 3895, + "самбле": 3896, + "ассамбле": 3897, + "ом": 3898, + "зд": 3899, + "смот": 3900, + "реги": 3901, + "чего": 3902, + "однако": 3903, + "усилия": 3904, + "действи": 3905, + "чно": 3906, + "уча": 3907, + "образ": 3908, + "вос": 3909, + "эта": 3910, + "перего": 3911, + "говор": 3912, + "вам": 3913, + "моло": 3914, + "время": 3915, + "дь": 3916, + "хотел": 3917, + "гру": 3918, + "заявл": 3919, + "предоста": 3920, + "поль": 3921, + "нее": 3922, + "резо": 3923, + "перегово": 3924, + "резолю": 3925, + "крет": 3926, + "поддерж": 3927, + "обеспе": 3928, + "него": 3929, + "представит": 3930, + "наде": 3931, + "кри": 3932, + "чь": 3933, + "проек": 3934, + "лет": 3935, + "други": 3936, + "_": 3937, + "،": 3938, + "؛": 3939, + "؟": 3940, + "ء": 3941, + "آ": 3942, + "أ": 3943, + "ؤ": 3944, + "إ": 3945, + "ئ": 3946, + "ا": 3947, + "ب": 3948, + "ة": 3949, + "ت": 3950, + "ث": 3951, + "ج": 3952, + "ح": 3953, + "خ": 3954, + "د": 3955, + "ذ": 3956, + "ر": 3957, + "ز": 3958, + "س": 3959, + "ش": 3960, + "ص": 3961, + "ض": 3962, + "ط": 3963, + "ظ": 3964, + "ع": 3965, + "غ": 3966, + "ـ": 3967, + "ف": 3968, + "ق": 3969, + "ك": 3970, + "ل": 3971, + "م": 3972, + "ن": 3973, + "ه": 3974, + "و": 3975, + "ى": 3976, + "ي": 3977, + "ً": 3978, + "ٌ": 3979, + "ٍ": 3980, + "َ": 3981, + "ُ": 3982, + "ِ": 3983, + "ّ": 3984, + "ْ": 3985, + "ٰ": 3986, + "چ": 3987, + "ڨ": 3988, + "ک": 3989, + "ھ": 3990, + "ی": 3991, + "ۖ": 3992, + "ۗ": 3993, + "ۘ": 3994, + "ۚ": 3995, + "ۛ": 3996, + "—": 3997, + "☭": 3998, + "ﺃ": 3999, + "ﻻ": 4000, + "ال": 4001, + "َا": 4002, + "وَ": 4003, + "َّ": 4004, + "ِي": 4005, + "أَ": 4006, + "لَ": 4007, + "نَ": 4008, + "الْ": 4009, + "هُ": 4010, + "ُو": 4011, + "ما": 4012, + "نْ": 4013, + "من": 4014, + "عَ": 4015, + "نا": 4016, + "لا": 4017, + "مَ": 4018, + "تَ": 4019, + "فَ": 4020, + "أن": 4021, + "لي": 4022, + "مِ": 4023, + "ان": 4024, + "في": 4025, + "رَ": 4026, + "يَ": 4027, + "هِ": 4028, + "مْ": 4029, + "قَ": 4030, + "بِ": 4031, + "لى": 4032, + "ين": 4033, + "إِ": 4034, + "لِ": 4035, + "وا": 4036, + "كَ": 4037, + "ها": 4038, + "ًا": 4039, + "مُ": 4040, + "ون": 4041, + "الم": 4042, + "بَ": 4043, + "يا": 4044, + "ذا": 4045, + "سا": 4046, + "الل": 4047, + "مي": 4048, + "يْ": 4049, + "را": 4050, + "ري": 4051, + "لك": 4052, + "مَا": 4053, + "نَّ": 4054, + "لم": 4055, + "إن": 4056, + "ست": 4057, + "وم": 4058, + "َّا": 4059, + "لَا": 4060, + "هم": 4061, + "ِّ": 4062, + "كُ": 4063, + "كان": 4064, + "سَ": 4065, + "با": 4066, + "دي": 4067, + "حَ": 4068, + "عْ": 4069, + "بي": 4070, + "الأ": 4071, + "ول": 4072, + "فِي": 4073, + "رِ": 4074, + "دا": 4075, + "مِنْ": 4076, + "ُونَ": 4077, + "وْ": 4078, + "هَا": 4079, + "ُّ": 4080, + "الس": 4081, + "الَ": 4082, + "ني": 4083, + "لْ": 4084, + "تُ": 4085, + "هل": 4086, + "رة": 4087, + "دَ": 4088, + "سْ": 4089, + "تِ": 4090, + "نَا": 4091, + "رْ": 4092, + "اللَّ": 4093, + "سامي": 4094, + "كن": 4095, + "كل": 4096, + "هَ": 4097, + "عَلَ": 4098, + "على": 4099, + "مع": 4100, + "إلى": 4101, + "قد": 4102, + "الر": 4103, + "ُوا": 4104, + "ير": 4105, + "عن": 4106, + "يُ": 4107, + "نِ": 4108, + "بْ": 4109, + "الح": 4110, + "هُمْ": 4111, + "قا": 4112, + "ذه": 4113, + "الت": 4114, + "ِينَ": 4115, + "جَ": 4116, + "هذا": 4117, + "عد": 4118, + "الع": 4119, + "دْ": 4120, + "قَالَ": 4121, + "رُ": 4122, + "يم": 4123, + "ية": 4124, + "نُ": 4125, + "خَ": 4126, + "رب": 4127, + "الك": 4128, + "وَا": 4129, + "أنا": 4130, + "ةِ": 4131, + "الن": 4132, + "حد": 4133, + "عِ": 4134, + "تا": 4135, + "هو": 4136, + "فا": 4137, + "عا": 4138, + "الش": 4139, + "لُ": 4140, + "يت": 4141, + "ذَا": 4142, + "يع": 4143, + "الذ": 4144, + "حْ": 4145, + "الص": 4146, + "إِنَّ": 4147, + "جا": 4148, + "علي": 4149, + "كَا": 4150, + "بُ": 4151, + "تع": 4152, + "وق": 4153, + "مل": 4154, + "لَّ": 4155, + "يد": 4156, + "أخ": 4157, + "رف": 4158, + "تي": 4159, + "الِ": 4160, + "ّا": 4161, + "ذلك": 4162, + "أَنْ": 4163, + "سِ": 4164, + "توم": 4165, + "مر": 4166, + "مَنْ": 4167, + "بل": 4168, + "الق": 4169, + "الله": 4170, + "ِيَ": 4171, + "كم": 4172, + "ذَ": 4173, + "عل": 4174, + "حب": 4175, + "سي": 4176, + "عُ": 4177, + "الج": 4178, + "الد": 4179, + "شَ": 4180, + "تك": 4181, + "فْ": 4182, + "صَ": 4183, + "لل": 4184, + "دِ": 4185, + "بر": 4186, + "فِ": 4187, + "ته": 4188, + "أع": 4189, + "تْ": 4190, + "قْ": 4191, + "الْأَ": 4192, + "ئِ": 4193, + "عَنْ": 4194, + "ور": 4195, + "حا": 4196, + "الَّ": 4197, + "مت": 4198, + "فر": 4199, + "دُ": 4200, + "هنا": 4201, + "وَأَ": 4202, + "تب": 4203, + "ةُ": 4204, + "أي": 4205, + "سب": 4206, + "ريد": 4207, + "وج": 4208, + "كُمْ": 4209, + "حِ": 4210, + "كْ": 4211, + "در": 4212, + "َاء": 4213, + "هذه": 4214, + "الط": 4215, + "الْمُ": 4216, + "دة": 4217, + "قل": 4218, + "غَ": 4219, + "يوم": 4220, + "الَّذ": 4221, + "كر": 4222, + "تر": 4223, + "كِ": 4224, + "كي": 4225, + "عَلَى": 4226, + "رَب": 4227, + "عة": 4228, + "قُ": 4229, + "جْ": 4230, + "فض": 4231, + "لة": 4232, + "هْ": 4233, + "رَا": 4234, + "وَلَ": 4235, + "الْمَ": 4236, + "أَنَّ": 4237, + "يَا": 4238, + "أُ": 4239, + "شي": 4240, + "اللَّهُ": 4241, + "لَى": 4242, + "قِ": 4243, + "أت": 4244, + "عَلَيْ": 4245, + "اللَّهِ": 4246, + "الب": 4247, + "ضَ": 4248, + "ةً": 4249, + "قي": 4250, + "ار": 4251, + "بد": 4252, + "خْ": 4253, + "سْتَ": 4254, + "طَ": 4255, + "قَدْ": 4256, + "ذهب": 4257, + "أم": 4258, + "ماذا": 4259, + "وَإِ": 4260, + "ةٌ": 4261, + "ونَ": 4262, + "ليلى": 4263, + "ولا": 4264, + "حُ": 4265, + "هي": 4266, + "صل": 4267, + "الخ": 4268, + "ود": 4269, + "ليس": 4270, + "لدي": 4271, + "قال": 4272, + "كَانَ": 4273, + "مَّ": 4274, + "حي": 4275, + "تم": 4276, + "لن": 4277, + "وَلَا": 4278, + "بع": 4279, + "يمكن": 4280, + "سُ": 4281, + "ةَ": 4282, + "حت": 4283, + "رًا": 4284, + "كا": 4285, + "شا": 4286, + "هِمْ": 4287, + "لَهُ": 4288, + "زَ": 4289, + "داً": 4290, + "مس": 4291, + "كث": 4292, + "الْعَ": 4293, + "جِ": 4294, + "صْ": 4295, + "فَا": 4296, + "له": 4297, + "وي": 4298, + "عَا": 4299, + "هُوَ": 4300, + "بِي": 4301, + "بَا": 4302, + "أس": 4303, + "ثَ": 4304, + "لِي": 4305, + "رض": 4306, + "الرَّ": 4307, + "لِكَ": 4308, + "تَّ": 4309, + "فُ": 4310, + "قة": 4311, + "فعل": 4312, + "مِن": 4313, + "الآ": 4314, + "ثُ": 4315, + "سم": 4316, + "مَّا": 4317, + "بِهِ": 4318, + "تق": 4319, + "خر": 4320, + "لقد": 4321, + "خل": 4322, + "شر": 4323, + "أنت": 4324, + "لَّا": 4325, + "سن": 4326, + "السَّ": 4327, + "الذي": 4328, + "سَا": 4329, + "وما": 4330, + "زل": 4331, + "وب": 4332, + "أْ": 4333, + "إذا": 4334, + "رِي": 4335, + "حة": 4336, + "نِي": 4337, + "الْحَ": 4338, + "وَقَالَ": 4339, + "به": 4340, + "ةٍ": 4341, + "سأ": 4342, + "رٌ": 4343, + "بال": 4344, + "مة": 4345, + "شْ": 4346, + "وت": 4347, + "عند": 4348, + "فس": 4349, + "بَعْ": 4350, + "هر": 4351, + "قط": 4352, + "أح": 4353, + "إنه": 4354, + "وع": 4355, + "فت": 4356, + "غا": 4357, + "هناك": 4358, + "بت": 4359, + "مِنَ": 4360, + "سر": 4361, + "ذَلِكَ": 4362, + "رس": 4363, + "حدث": 4364, + "غْ": 4365, + "ِّي": 4366, + "الإ": 4367, + "وَيَ": 4368, + "جل": 4369, + "است": 4370, + "قِي": 4371, + "عب": 4372, + "وس": 4373, + "يش": 4374, + "الَّذِينَ": 4375, + "تاب": 4376, + "دِي": 4377, + "جب": 4378, + "كون": 4379, + "بن": 4380, + "الث": 4381, + "لَيْ": 4382, + "بعد": 4383, + "وَالْ": 4384, + "فَأَ": 4385, + "عم": 4386, + "هُم": 4387, + "تن": 4388, + "ذْ": 4389, + "أص": 4390, + "أين": 4391, + "رَبِّ": 4392, + "الذين": 4393, + "إِن": 4394, + "بين": 4395, + "جُ": 4396, + "عَلَيْهِ": 4397, + "حَا": 4398, + "لو": 4399, + "ستط": 4400, + "ظر": 4401, + "لَمْ": 4402, + "ءِ": 4403, + "كُل": 4404, + "طل": 4405, + "تَا": 4406, + "ضُ": 4407, + "كنت": 4408, + "لًا": 4409, + "مٌ": 4410, + "قبل": 4411, + "ــ": 4412, + "ذِ": 4413, + "قَوْ": 4414, + "صِ": 4415, + "مًا": 4416, + "كانت": 4417, + "صا": 4418, + "يق": 4419, + "الف": 4420, + "النا": 4421, + "مٍ": 4422, + "إِنْ": 4423, + "النَّ": 4424, + "جد": 4425, + "وَمَا": 4426, + "تت": 4427, + "بح": 4428, + "مكان": 4429, + "كيف": 4430, + "ّة": 4431, + "الا": 4432, + "جَا": 4433, + "أو": 4434, + "ساعد": 4435, + "ضِ": 4436, + "إلا": 4437, + "راً": 4438, + "قَا": 4439, + "رأ": 4440, + "عت": 4441, + "أحد": 4442, + "هد": 4443, + "ضا": 4444, + "طر": 4445, + "أق": 4446, + "ماء": 4447, + "دَّ": 4448, + "البا": 4449, + "مُو": 4450, + "أَوْ": 4451, + "طا": 4452, + "قُو": 4453, + "خِ": 4454, + "تل": 4455, + "ستطيع": 4456, + "دَا": 4457, + "النَّا": 4458, + "إلَى": 4459, + "وَتَ": 4460, + "هَذَا": 4461, + "بة": 4462, + "عليك": 4463, + "جر": 4464, + "المن": 4465, + "زا": 4466, + "رٍ": 4467, + "دع": 4468, + "ًّا": 4469, + "سة": 4470, + "ثُمَّ": 4471, + "شيء": 4472, + "الغ": 4473, + "تح": 4474, + "رُونَ": 4475, + "اليوم": 4476, + "مِي": 4477, + "نُوا": 4478, + "أر": 4479, + "تُمْ": 4480, + "عر": 4481, + "يف": 4482, + "أب": 4483, + "دًا": 4484, + "صَا": 4485, + "التَّ": 4486, + "أريد": 4487, + "الز": 4488, + "يَوْ": 4489, + "إلي": 4490, + "جي": 4491, + "يَعْ": 4492, + "فضل": 4493, + "الإن": 4494, + "أنه": 4495, + "1": 4496, + "2": 4497, + "3": 4498, + "4": 4499, + "5": 4500, + "·": 4501, + "×": 4502, + "̃": 4503, + "̌": 4504, + "ε": 4505, + "λ": 4506, + "μ": 4507, + "•": 4508, + "‧": 4509, + "─": 4510, + "□": 4511, + "、": 4512, + "。": 4513, + "〈": 4514, + "〉": 4515, + "《": 4516, + "》": 4517, + "「": 4518, + "」": 4519, + "『": 4520, + "』": 4521, + "ア": 4522, + "オ": 4523, + "カ": 4524, + "チ": 4525, + "ド": 4526, + "ベ": 4527, + "ャ": 4528, + "ヤ": 4529, + "ン": 4530, + "・": 4531, + "ー": 4532, + "ㄟ": 4533, + "!": 4534, + "(": 4535, + ")": 4536, + ",": 4537, + "-": 4538, + "/": 4539, + ":": 4540, + ";": 4541, + "?": 4542, + "p": 4543, + "i4": 4544, + "zh": 4545, + "i2": 4546, + "ng1": 4547, + "u4": 4548, + "i1": 4549, + "ng2": 4550, + "u3": 4551, + "de5": 4552, + "e4": 4553, + "i3": 4554, + "ng4": 4555, + "an4": 4556, + "shi4": 4557, + "an2": 4558, + "u2": 4559, + "u1": 4560, + "ng3": 4561, + "a1": 4562, + "an1": 4563, + "e2": 4564, + "a4": 4565, + "ei4": 4566, + "ong1": 4567, + "ai4": 4568, + "ao4": 4569, + "ang1": 4570, + "an3": 4571, + "wei4": 4572, + "uo2": 4573, + "n1": 4574, + "en2": 4575, + "ao3": 4576, + "e1": 4577, + "qi": 4578, + "eng2": 4579, + "zho": 4580, + "ang3": 4581, + "ang4": 4582, + "ang2": 4583, + "uo4": 4584, + "ge4": 4585, + "yi1": 4586, + "guo2": 4587, + "a3": 4588, + "he2": 4589, + "e3": 4590, + "yi2": 4591, + "di4": 4592, + "zhong1": 4593, + "bu4": 4594, + "ai2": 4595, + "n2": 4596, + "zai4": 4597, + "shi2": 4598, + "eng1": 4599, + "ren2": 4600, + "ong2": 4601, + "xian4": 4602, + "xu": 4603, + "n4": 4604, + "li4": 4605, + "en4": 4606, + "yu2": 4607, + "ei2": 4608, + "yi2ge4": 4609, + "ou4": 4610, + "ei3": 4611, + "ui4": 4612, + "a2": 4613, + "you3": 4614, + "ao1": 4615, + "da4": 4616, + "cheng2": 4617, + "en1": 4618, + "eng4": 4619, + "yi4": 4620, + "si1": 4621, + "zhi4": 4622, + "jia1": 4623, + "yuan2": 4624, + "ta1": 4625, + "de5yi2ge4": 4626, + "ke1": 4627, + "shu3": 4628, + "xi1": 4629, + "ji2": 4630, + "ao2": 4631, + "ou3": 4632, + "ong4": 4633, + "xia4": 4634, + "ai1": 4635, + "gong1": 4636, + "zhi1": 4637, + "en3": 4638, + "wei2": 4639, + "xue2": 4640, + "qu1": 4641, + "zhou1": 4642, + "er3": 4643, + "ming2": 4644, + "zhong3": 4645, + "li3": 4646, + "wu4": 4647, + "yi3": 4648, + "uo1": 4649, + "e5": 4650, + "ji4": 4651, + "xing2": 4652, + "jian4": 4653, + "hua4": 4654, + "yu3": 4655, + "uo3": 4656, + "ji1": 4657, + "ai3": 4658, + "zuo4": 4659, + "hou4": 4660, + "hui4": 4661, + "ei1": 4662, + "nian2": 4663, + "qi2": 4664, + "dao4": 4665, + "sheng1": 4666, + "de2": 4667, + "dai4": 4668, + "uan2": 4669, + "zhe4": 4670, + "zheng4": 4671, + "ben3": 4672, + "shang4": 4673, + "zhu3": 4674, + "bei4": 4675, + "ye4": 4676, + "chu1": 4677, + "zhan4": 4678, + "le5": 4679, + "lai2": 4680, + "shi3": 4681, + "nan2": 4682, + "ren4": 4683, + "you2": 4684, + "ke4": 4685, + "ba1": 4686, + "fu4": 4687, + "dui4": 4688, + "ya4": 4689, + "mei3": 4690, + "zi4": 4691, + "xin1": 4692, + "jing1": 4693, + "zhu": 4694, + "n3": 4695, + "yong4": 4696, + "mu4": 4697, + "jiao4": 4698, + "ye3": 4699, + "jin4": 4700, + "bian4": 4701, + "lu4": 4702, + "qi1": 4703, + "she4": 4704, + "xiang1": 4705, + "ong3": 4706, + "shu4": 4707, + "dong4": 4708, + "suo3": 4709, + "guan1": 4710, + "san1": 4711, + "te4": 4712, + "duo1": 4713, + "fu2": 4714, + "min2": 4715, + "la1": 4716, + "zhi2": 4717, + "zhen4": 4718, + "ou1": 4719, + "wu3": 4720, + "ma3": 4721, + "i5": 4722, + "zi5": 4723, + "ju4": 4724, + "er4": 4725, + "yao4": 4726, + "xia4de5yi2ge4": 4727, + "si4": 4728, + "tu2": 4729, + "shan1": 4730, + "zui4": 4731, + "yin1": 4732, + "er2": 4733, + "tong2": 4734, + "dong1": 4735, + "yu4": 4736, + "yan2": 4737, + "qian2": 4738, + "shu3xia4de5yi2ge4": 4739, + "jun1": 4740, + "ke3": 4741, + "wen2": 4742, + "fa3": 4743, + "luo2": 4744, + "zhu4": 4745, + "xi4": 4746, + "kou3": 4747, + "bei3": 4748, + "jian1": 4749, + "fa1": 4750, + "dian4": 4751, + "jiang1": 4752, + "wei4yu2": 4753, + "xiang4": 4754, + "zhi3": 4755, + "eng3": 4756, + "fang1": 4757, + "lan2": 4758, + "shu": 4759, + "ri4": 4760, + "lian2": 4761, + "shou3": 4762, + "qiu2": 4763, + "jin1": 4764, + "huo4": 4765, + "shu3xia4de5yi2ge4zhong3": 4766, + "fen1": 4767, + "nei4": 4768, + "gai1": 4769, + "mei3guo2": 4770, + "un2": 4771, + "ge2": 4772, + "bao3": 4773, + "qing1": 4774, + "gao1": 4775, + "tai2": 4776, + "xiao3": 4777, + "jie2": 4778, + "tian1": 4779, + "chang2": 4780, + "quan2": 4781, + "lie4": 4782, + "hai3": 4783, + "fei1": 4784, + "ti3": 4785, + "jue2": 4786, + "ou2": 4787, + "ci3": 4788, + "zu2": 4789, + "ni2": 4790, + "biao3": 4791, + "zhong1guo2": 4792, + "du4": 4793, + "yue4": 4794, + "xing4": 4795, + "sheng4": 4796, + "che1": 4797, + "dan1": 4798, + "jie1": 4799, + "lin2": 4800, + "ping2": 4801, + "fu3": 4802, + "gu3": 4803, + "jie4": 4804, + "v3": 4805, + "sheng3": 4806, + "na4": 4807, + "yuan4": 4808, + "zhang3": 4809, + "guan3": 4810, + "dao3": 4811, + "zu3": 4812, + "ding4": 4813, + "dian3": 4814, + "ceng2": 4815, + "ren2kou3": 4816, + "tai4": 4817, + "tong1": 4818, + "guo4": 4819, + "neng2": 4820, + "chang3": 4821, + "hua2": 4822, + "liu2": 4823, + "ying1": 4824, + "xiao4": 4825, + "ci4": 4826, + "bian4hua4": 4827, + "liang3": 4828, + "gong4": 4829, + "zhong4": 4830, + "de5yi1": 4831, + "se4": 4832, + "kai1": 4833, + "wang2": 4834, + "jiu4": 4835, + "shi1": 4836, + "shou4": 4837, + "mei2": 4838, + "feng1": 4839, + "ze2": 4840, + "tu2shi4": 4841, + "ti2": 4842, + "qi4": 4843, + "jiu3": 4844, + "shen1": 4845, + "zhe3": 4846, + "ren2kou3bian4hua4": 4847, + "ren2kou3bian4hua4tu2shi4": 4848, + "di4qu1": 4849, + "yang2": 4850, + "men5": 4851, + "long2": 4852, + "bing4": 4853, + "chan3": 4854, + "zhu1": 4855, + "wei3": 4856, + "wai4": 4857, + "xing1": 4858, + "bo1": 4859, + "bi3": 4860, + "tang2": 4861, + "hua1": 4862, + "bo2": 4863, + "shui3": 4864, + "shu1": 4865, + "dou1": 4866, + "sai4": 4867, + "chao2": 4868, + "bi4": 4869, + "ling2": 4870, + "lei4": 4871, + "da4xue2": 4872, + "fen4": 4873, + "shu3de5": 4874, + "mu3": 4875, + "jiao1": 4876, + "dang1": 4877, + "cheng1": 4878, + "tong3": 4879, + "nv3": 4880, + "qi3": 4881, + "yan3": 4882, + "mian4": 4883, + "luo4": 4884, + "jing4": 4885, + "ge1": 4886, + "ru4": 4887, + "dan4": 4888, + "ri4ben3": 4889, + "pu3": 4890, + "yun4": 4891, + "huang2": 4892, + "wo3": 4893, + "lv": 4894, + "hai2": 4895, + "shi4yi1": 4896, + "xie1": 4897, + "ying3": 4898, + "wu2": 4899, + "shen2": 4900, + "wang3": 4901, + "guang3": 4902, + "liu4": 4903, + "su4": 4904, + "shi4zhen4": 4905, + "can1": 4906, + "cao3": 4907, + "xia2": 4908, + "ka3": 4909, + "da2": 4910, + "hu4": 4911, + "ban4": 4912, + "dang3": 4913, + "hu2": 4914, + "zong3": 4915, + "deng3": 4916, + "de5yi2ge4shi4zhen4": 4917, + "chuan2": 4918, + "mo4": 4919, + "zhang1": 4920, + "ban1": 4921, + "mo2": 4922, + "cha2": 4923, + "ce4": 4924, + "zhu3yao4": 4925, + "tou2": 4926, + "ju2": 4927, + "shi4wei4yu2": 4928, + "sa4": 4929, + "un1": 4930, + "ke3yi3": 4931, + "du1": 4932, + "han4": 4933, + "liang4": 4934, + "sha1": 4935, + "jia3": 4936, + "zi1": 4937, + "lv4": 4938, + "fu1": 4939, + "xian1": 4940, + "xu4": 4941, + "guang1": 4942, + "meng2": 4943, + "bao4": 4944, + "you4": 4945, + "rong2": 4946, + "zhi1yi1": 4947, + "wei1": 4948, + "mao2": 4949, + "guo2jia1": 4950, + "cong2": 4951, + "gou4": 4952, + "tie3": 4953, + "zhen1": 4954, + "du2": 4955, + "bian1": 4956, + "ci2": 4957, + "qu3": 4958, + "fan4": 4959, + "xiang3": 4960, + "men2": 4961, + "ju1": 4962, + "hong2": 4963, + "zi3": 4964, + "ta1men5": 4965, + "ji3": 4966, + "zong1": 4967, + "zhou1de5yi2ge4shi4zhen4": 4968, + "tuan2": 4969, + "jing3": 4970, + "gong1si1": 4971, + "xie4": 4972, + "li2": 4973, + "li4shi3": 4974, + "bao1": 4975, + "gang3": 4976, + "gui1": 4977, + "zheng1": 4978, + "zhi2wu4": 4979, + "ta1de5": 4980, + "pin3": 4981, + "zhuan1": 4982, + "chong2": 4983, + "shi3yong4": 4984, + "wa3": 4985, + "shuo1": 4986, + "chuan1": 4987, + "lei2": 4988, + "wan1": 4989, + "huo2": 4990, + "su1": 4991, + "zao3": 4992, + "gai3": 4993, + "qu4": 4994, + "gu4": 4995, + "xi2": 4996, + "hang2": 4997, + "ying4": 4998, + "cun1": 4999, + "gen1": 5000, + "ying2": 5001, + "ting2": 5002, + "cheng2shi4": 5003, + "jiang3": 5004, + "ling3": 5005, + "lun2": 5006, + "bu4fen4": 5007, + "deng1": 5008, + "xuan3": 5009, + "dong4wu4": 5010, + "de2guo2": 5011, + "xian3": 5012, + "fan3": 5013, + "zhe5": 5014, + "han2": 5015, + "hao4": 5016, + "mi4": 5017, + "ran2": 5018, + "qin1": 5019, + "tiao2": 5020, + "zhan3": 5021, + "[ar]": 5022, + "[zh-cn]": 5023, + "¡": 5024, + "é": 5025, + "shi": 5026, + "tsu": 5027, + "teki": 5028, + "nai": 5029, + "aru": 5030, + "uu": 5031, + "kai": 5032, + "shite": 5033, + "mono": 5034, + "koto": 5035, + "kara": 5036, + "shita": 5037, + "suru": 5038, + "masu": 5039, + "tai": 5040, + "ware": 5041, + "shin": 5042, + "oku": 5043, + "yuu": 5044, + "iru": 5045, + "jiko": 5046, + "desu": 5047, + "rare": 5048, + "shou": 5049, + "sha": 5050, + "sekai": 5051, + "kyou": 5052, + "mashita": 5053, + "nara": 5054, + "kei": 5055, + "ita": 5056, + "ari": 5057, + "itsu": 5058, + "kono": 5059, + "naka": 5060, + "chou": 5061, + "sore": 5062, + "naru": 5063, + "gaku": 5064, + "reba": 5065, + "hito": 5066, + "sai": 5067, + "nan": 5068, + "dai": 5069, + "tsuku": 5070, + "shiki": 5071, + "sare": 5072, + "naku": 5073, + "jun": 5074, + "kaku": 5075, + "zai": 5076, + "wata": 5077, + "shuu": 5078, + "ii": 5079, + "kare": 5080, + "shii": 5081, + "made": 5082, + "sho": 5083, + "kereba": 5084, + "shika": 5085, + "ichi": 5086, + "deki": 5087, + "nin": 5088, + "wareware": 5089, + "nakereba": 5090, + "oite": 5091, + "yaku": 5092, + "mujun": 5093, + "yoku": 5094, + "butsu": 5095, + "omo": 5096, + "gae": 5097, + "naranai": 5098, + "tachi": 5099, + "chuu": 5100, + "kangae": 5101, + "toki": 5102, + "koro": 5103, + "mujunteki": 5104, + "naga": 5105, + "jin": 5106, + "shima": 5107, + "iku": 5108, + "imasu": 5109, + "hon": 5110, + "kae": 5111, + "kore": 5112, + "kita": 5113, + "datta": 5114, + "jitsu": 5115, + "mae": 5116, + "toku": 5117, + "douitsu": 5118, + "ritsu": 5119, + "kyuu": 5120, + "hyou": 5121, + "rareta": 5122, + "keisei": 5123, + "kkan": 5124, + "rareru": 5125, + "mou": 5126, + "doko": 5127, + "ryou": 5128, + "dake": 5129, + "nakatta": 5130, + "soko": 5131, + "tabe": 5132, + "hana": 5133, + "fuku": 5134, + "yasu": 5135, + "wataku": 5136, + "yama": 5137, + "kyo": 5138, + "genzai": 5139, + "boku": 5140, + "ata": 5141, + "kawa": 5142, + "masen": 5143, + "juu": 5144, + "natte": 5145, + "watakushi": 5146, + "yotte": 5147, + "hai": 5148, + "jishin": 5149, + "rete": 5150, + "oka": 5151, + "kagaku": 5152, + "natta": 5153, + "karu": 5154, + "nari": 5155, + "mata": 5156, + "kuru": 5157, + "gai": 5158, + "kari": 5159, + "shakai": 5160, + "koui": 5161, + "yori": 5162, + "setsu": 5163, + "reru": 5164, + "tokoro": 5165, + "jutsu": 5166, + "saku": 5167, + "ttai": 5168, + "ningen": 5169, + "tame": 5170, + "kankyou": 5171, + "ooku": 5172, + "watashi": 5173, + "tsukuru": 5174, + "sugi": 5175, + "jibun": 5176, + "shitsu": 5177, + "keru": 5178, + "kishi": 5179, + "shikashi": 5180, + "moto": 5181, + "mari": 5182, + "itte": 5183, + "deshita": 5184, + "nde": 5185, + "arimasu": 5186, + "koe": 5187, + "zettai": 5188, + "kkanteki": 5189, + "rekishi": 5190, + "dekiru": 5191, + "tsuka": 5192, + "itta": 5193, + "kobutsu": 5194, + "miru": 5195, + "shoku": 5196, + "shimasu": 5197, + "gijutsu": 5198, + "gyou": 5199, + "joushiki": 5200, + "atta": 5201, + "hodo": 5202, + "koko": 5203, + "tsukurareta": 5204, + "zoku": 5205, + "hitei": 5206, + "koku": 5207, + "rekishiteki": 5208, + "kete": 5209, + "kako": 5210, + "nagara": 5211, + "kakaru": 5212, + "shutai": 5213, + "haji": 5214, + "taku": 5215, + "douitsuteki": 5216, + "mete": 5217, + "tsuu": 5218, + "sarete": 5219, + "genjitsu": 5220, + "bai": 5221, + "nawa": 5222, + "jikan": 5223, + "waru": 5224, + "rt": 5225, + "atsu": 5226, + "soku": 5227, + "kouiteki": 5228, + "kata": 5229, + "tetsu": 5230, + "gawa": 5231, + "kedo": 5232, + "reta": 5233, + "sayou": 5234, + "tteru": 5235, + "tori": 5236, + "kimi": 5237, + "mura": 5238, + "sareru": 5239, + "machi": 5240, + "kya": 5241, + "osa": 5242, + "konna": 5243, + "aku": 5244, + "sareta": 5245, + "ipp": 5246, + "shiku": 5247, + "uchi": 5248, + "hitotsu": 5249, + "hatara": 5250, + "tachiba": 5251, + "shiro": 5252, + "katachi": 5253, + "tomo": 5254, + "ete": 5255, + "meru": 5256, + "nichi": 5257, + "dare": 5258, + "katta": 5259, + "eru": 5260, + "suki": 5261, + "ooki": 5262, + "maru": 5263, + "moku": 5264, + "oko": 5265, + "kangaerareru": 5266, + "oto": 5267, + "tanni": 5268, + "tada": 5269, + "taiteki": 5270, + "motte": 5271, + "kinou": 5272, + "shinai": 5273, + "kki": 5274, + "tari": 5275, + "ranai": 5276, + "kkou": 5277, + "mirai": 5278, + "ppon": 5279, + "goto": 5280, + "hitsu": 5281, + "teru": 5282, + "mochi": 5283, + "katsu": 5284, + "nyuu": 5285, + "zuka": 5286, + "tsuite": 5287, + "nomi": 5288, + "sugu": 5289, + "kuda": 5290, + "tetsugaku": 5291, + "ika": 5292, + "ronri": 5293, + "oki": 5294, + "nippon": 5295, + "shimashita": 5296, + "chishiki": 5297, + "chokkanteki": 5298, + "suko": 5299, + "kuu": 5300, + "arou": 5301, + "katte": 5302, + "kuri": 5303, + "inai": 5304, + "hyougen": 5305, + "ishiki": 5306, + "doku": 5307, + "atte": 5308, + "atara": 5309, + "wari": 5310, + "kao": 5311, + "seisan": 5312, + "hanashi": 5313, + "kake": 5314, + "naji": 5315, + "sunawa": 5316, + "sunawachi": 5317, + "ugo": 5318, + "suu": 5319, + "bara": 5320, + "hiro": 5321, + "iwa": 5322, + "betsu": 5323, + "yoi": 5324, + "seru": 5325, + "shiteru": 5326, + "rarete": 5327, + "toshi": 5328, + "seki": 5329, + "tairitsu": 5330, + "wakara": 5331, + "tokyo": 5332, + "kka": 5333, + "kyoku": 5334, + "iro": 5335, + "mite": 5336, + "saki": 5337, + "kanji": 5338, + "mita": 5339, + "sube": 5340, + "ryoku": 5341, + "matta": 5342, + "kudasai": 5343, + "omoi": 5344, + "wareru": 5345, + "hitsuyou": 5346, + "kashi": 5347, + "renai": 5348, + "kankei": 5349, + "gatte": 5350, + "ochi": 5351, + "motsu": 5352, + "sonzai": 5353, + "taishite": 5354, + "ame": 5355, + "seimei": 5356, + "kano": 5357, + "giri": 5358, + "kangaeru": 5359, + "yue": 5360, + "asa": 5361, + "onaji": 5362, + "yoru": 5363, + "niku": 5364, + "osaka": 5365, + "sukoshi": 5366, + "tama": 5367, + "kanojo": 5368, + "kite": 5369, + "mondai": 5370, + "amari": 5371, + "eki": 5372, + "kojin": 5373, + "haya": 5374, + "dete": 5375, + "atarashii": 5376, + "awa": 5377, + "gakkou": 5378, + "tsuzu": 5379, + "shukan": 5380, + "imashita": 5381, + "atae": 5382, + "darou": 5383, + "hataraku": 5384, + "gata": 5385, + "dachi": 5386, + "matsu": 5387, + "arimasen": 5388, + "seibutsu": 5389, + "mitsu": 5390, + "heya": 5391, + "yasui": 5392, + "deni": 5393, + "noko": 5394, + "haha": 5395, + "domo": 5396, + "kami": 5397, + "sudeni": 5398, + "nao": 5399, + "raku": 5400, + "ike": 5401, + "meta": 5402, + "kodomo": 5403, + "soshite": 5404, + "game": 5405, + "bakari": 5406, + "tote": 5407, + "hatsu": 5408, + "mise": 5409, + "mokuteki": 5410, + "dakara": 5411, + "[ja]": 5412, + "ő": 5413, + "ű": 5414, + "そ": 5415, + "な": 5416, + "ん": 5417, + "포": 5418, + "�": 5419, + "gy": 5420, + "eg": 5421, + "cs": 5422, + "ál": 5423, + "egy": 5424, + "át": 5425, + "ott": 5426, + "ett": 5427, + "meg": 5428, + "hogy": 5429, + "ég": 5430, + "ól": 5431, + "nek": 5432, + "volt": 5433, + "ág": 5434, + "nk": 5435, + "ék": 5436, + "ít": 5437, + "ák": 5438, + "ud": 5439, + "szer": 5440, + "mind": 5441, + "oz": 5442, + "ép": 5443, + "ért": 5444, + "mond": 5445, + "szt": 5446, + "nak": 5447, + "ől": 5448, + "csak": 5449, + "oly": 5450, + "áll": 5451, + "ány": 5452, + "mint": 5453, + "már": 5454, + "ött": 5455, + "nagy": 5456, + "ész": 5457, + "azt": 5458, + "elő": 5459, + "tud": 5460, + "ény": 5461, + "áz": 5462, + "még": 5463, + "köz": 5464, + "ely": 5465, + "ség": 5466, + "hoz": 5467, + "uk": 5468, + "kez": 5469, + "ám": 5470, + "aj": 5471, + "unk": 5472, + "vagy": 5473, + "szem": 5474, + "ember": 5475, + "fog": 5476, + "mert": 5477, + "ös": 5478, + "ság": 5479, + "leg": 5480, + "ünk": 5481, + "hát": 5482, + "ony": 5483, + "ezt": 5484, + "minden": 5485, + "ült": 5486, + "jó": 5487, + "kis": 5488, + "áj": 5489, + "úgy": 5490, + "most": 5491, + "ír": 5492, + "itt": 5493, + "elt": 5494, + "mondta": 5495, + "kell": 5496, + "ált": 5497, + "érd": 5498, + "tö": 5499, + "vár": 5500, + "lát": 5501, + "ők": 5502, + "vet": 5503, + "után": 5504, + "két": 5505, + "nap": 5506, + "ív": 5507, + "ály": 5508, + "vég": 5509, + "ök": 5510, + "dul": 5511, + "néz": 5512, + "ában": 5513, + "kül": 5514, + "akkor": 5515, + "szél": 5516, + "új": 5517, + "olyan": 5518, + "ked": 5519, + "hely": 5520, + "tör": 5521, + "ból": 5522, + "elm": 5523, + "ára": 5524, + "ló": 5525, + "volna": 5526, + "lehet": 5527, + "ebb": 5528, + "sok": 5529, + "olt": 5530, + "eket": 5531, + "bor": 5532, + "fej": 5533, + "gond": 5534, + "akar": 5535, + "fél": 5536, + "úl": 5537, + "otta": 5538, + "valami": 5539, + "jel": 5540, + "éd": 5541, + "arc": 5542, + "hall": 5543, + "föl": 5544, + "ába": 5545, + "olg": 5546, + "kir": 5547, + "old": 5548, + "kérd": 5549, + "jár": 5550, + "úr": 5551, + "zs": 5552, + "élet": 5553, + "ját": 5554, + "ov": 5555, + "éz": 5556, + "vil": 5557, + "őr": 5558, + "ög": 5559, + "lesz": 5560, + "koz": 5561, + "ább": 5562, + "király": 5563, + "eng": 5564, + "igaz": 5565, + "haj": 5566, + "kod": 5567, + "ról": 5568, + "több": 5569, + "szó": 5570, + "ében": 5571, + "öt": 5572, + "nyi": 5573, + "szól": 5574, + "gondol": 5575, + "egész": 5576, + "így": 5577, + "ős": 5578, + "obb": 5579, + "osan": 5580, + "ből": 5581, + "abb": 5582, + "őt": 5583, + "nál": 5584, + "kép": 5585, + "aztán": 5586, + "tart": 5587, + "beszél": 5588, + "előtt": 5589, + "aszt": 5590, + "maj": 5591, + "kör": 5592, + "hang": 5593, + "íz": 5594, + "incs": 5595, + "év": 5596, + "ód": 5597, + "ók": 5598, + "hozz": 5599, + "okat": 5600, + "nagyon": 5601, + "ház": 5602, + "ped": 5603, + "ezte": 5604, + "etlen": 5605, + "neki": 5606, + "majd": 5607, + "szony": 5608, + "ának": 5609, + "felé": 5610, + "egyszer": 5611, + "adt": 5612, + "gyer": 5613, + "amikor": 5614, + "foly": 5615, + "szak": 5616, + "őd": 5617, + "hú": 5618, + "ász": 5619, + "amely": 5620, + "ére": 5621, + "ilyen": 5622, + "oda": 5623, + "ják": 5624, + "tár": 5625, + "ával": 5626, + "lak": 5627, + "gyan": 5628, + "ély": 5629, + "út": 5630, + "kezd": 5631, + "mell": 5632, + "mikor": 5633, + "hez": 5634, + "való": 5635, + "szeret": 5636, + "rend": 5637, + "vissza": 5638, + "fő": 5639, + "asszony": 5640, + "ről": 5641, + "pedig": 5642, + "szép": 5643, + "ták": 5644, + "öv": 5645, + "világ": 5646, + "maga": 5647, + "szik": 5648, + "éj": 5649, + "ént": 5650, + "jött": 5651, + "szí": 5652, + "gat": 5653, + "ettem": 5654, + "hány": 5655, + "ást": 5656, + "ahol": 5657, + "őket": 5658, + "hár": 5659, + "nő": 5660, + "csi": 5661, + "talál": 5662, + "elte": 5663, + "látt": 5664, + "tört": 5665, + "hagy": 5666, + "esz": 5667, + "nél": 5668, + "kut": 5669, + "lány": 5670, + "amit": 5671, + "ső": 5672, + "ellen": 5673, + "magát": 5674, + "ugyan": 5675, + "külön": 5676, + "asz": 5677, + "mindig": 5678, + "lép": 5679, + "talán": 5680, + "szor": 5681, + "illan": 5682, + "nincs": 5683, + "vagyok": 5684, + "telen": 5685, + "ismer": 5686, + "isten": 5687, + "ított": 5688, + "jobb": 5689, + "ves": 5690, + "dult": 5691, + "juk": 5692, + "szen": 5693, + "öm": 5694, + "lett": 5695, + "egyik": 5696, + "bár": 5697, + "szi": 5698, + "szív": 5699, + "azon": 5700, + "eszt": 5701, + "föld": 5702, + "kuty": 5703, + "pillan": 5704, + "fér": 5705, + "től": 5706, + "tű": 5707, + "ébe": 5708, + "tött": 5709, + "barát": 5710, + "íg": 5711, + "ahogy": 5712, + "eh": 5713, + "ep": 5714, + "jelent": 5715, + "tat": 5716, + "szeg": 5717, + "mintha": 5718, + "egyen": 5719, + "szab": 5720, + "bizony": 5721, + "jon": 5722, + "öreg": 5723, + "dolg": 5724, + "csap": 5725, + "tiszt": 5726, + "állt": 5727, + "ancs": 5728, + "idő": 5729, + "ügy": 5730, + "miért": 5731, + "ót": 5732, + "csin": 5733, + "ének": 5734, + "vér": 5735, + "jól": 5736, + "alatt": 5737, + "mely": 5738, + "semmi": 5739, + "nyug": 5740, + "vág": 5741, + "követ": 5742, + "össze": 5743, + "mad": 5744, + "acs": 5745, + "fiú": 5746, + "másik": 5747, + "jön": 5748, + "szám": 5749, + "rész": 5750, + "kér": 5751, + "ével": 5752, + "[hu]": 5753, + "%": 5754, + "0": 5755, + "6": 5756, + "7": 5757, + "8": 5758, + "9": 5759, + "A": 5760, + "B": 5761, + "C": 5762, + "D": 5763, + "E": 5764, + "F": 5765, + "G": 5766, + "H": 5767, + "I": 5768, + "J": 5769, + "K": 5770, + "L": 5771, + "M": 5772, + "N": 5773, + "O": 5774, + "P": 5775, + "Q": 5776, + "R": 5777, + "S": 5778, + "T": 5779, + "U": 5780, + "V": 5781, + "W": 5782, + "X": 5783, + "Y": 5784, + "Z": 5785, + "Ł": 5786, + "α": 5787, + "ς": 5788, + "♥": 5789, + "か": 5790, + "ズ": 5791, + "因": 5792, + "国": 5793, + "怎": 5794, + "抱": 5795, + "推": 5796, + "有": 5797, + "樣": 5798, + "為": 5799, + "群": 5800, + "麼": 5801, + "eo": 5802, + "eul": 5803, + "eun": 5804, + "eon": 5805, + "ae": 5806, + "yeon": 5807, + "yeo": 5808, + "ui": 5809, + "hae": 5810, + "geo": 5811, + "neun": 5812, + "ssda": 5813, + "seo": 5814, + "eong": 5815, + "kk": 5816, + "jeo": 5817, + "deul": 5818, + "eum": 5819, + "yeong": 5820, + "geos": 5821, + "hag": 5822, + "aneun": 5823, + "iss": 5824, + "dae": 5825, + "eob": 5826, + "eol": 5827, + "geu": 5828, + "jeong": 5829, + "sae": 5830, + "doe": 5831, + "geul": 5832, + "eulo": 5833, + "bn": 5834, + "sang": 5835, + "bnida": 5836, + "haneun": 5837, + "jeog": 5838, + "saeng": 5839, + "ineun": 5840, + "anh": 5841, + "salam": 5842, + "eom": 5843, + "nae": 5844, + "gwa": 5845, + "yeol": 5846, + "eseo": 5847, + "myeon": 5848, + "ttae": 5849, + "hw": 5850, + "eobs": 5851, + "jang": 5852, + "gw": 5853, + "ileul": 5854, + "yeog": 5855, + "jeon": 5856, + "sig": 5857, + "jag": 5858, + "hago": 5859, + "deun": 5860, + "seong": 5861, + "gag": 5862, + "ham": 5863, + "dang": 5864, + "leul": 5865, + "sil": 5866, + "dong": 5867, + "handa": 5868, + "eossda": 5869, + "aeg": 5870, + "seon": 5871, + "haessda": 5872, + "issda": 5873, + "ege": 5874, + "mul": 5875, + "jung": 5876, + "jig": 5877, + "issneun": 5878, + "geun": 5879, + "seubnida": 5880, + "won": 5881, + "daneun": 5882, + "eoh": 5883, + "deo": 5884, + "gam": 5885, + "jal": 5886, + "haeng": 5887, + "yang": 5888, + "bang": 5889, + "jae": 5890, + "saenggag": 5891, + "hage": 5892, + "sog": 5893, + "eoss": 5894, + "jasin": 5895, + "jil": 5896, + "eog": 5897, + "gyeong": 5898, + "gong": 5899, + "deon": 5900, + "haess": 5901, + "eung": 5902, + "joh": 5903, + "nal": 5904, + "myeong": 5905, + "eona": 5906, + "igo": 5907, + "gyeol": 5908, + "yag": 5909, + "gwan": 5910, + "uli": 5911, + "yong": 5912, + "lyeo": 5913, + "jog": 5914, + "eohge": 5915, + "bog": 5916, + "tong": 5917, + "manh": 5918, + "jeol": 5919, + "geol": 5920, + "aga": 5921, + "naneun": 5922, + "uneun": 5923, + "cheol": 5924, + "dol": 5925, + "bad": 5926, + "hamyeon": 5927, + "yeossda": 5928, + "ibnida": 5929, + "gye": 5930, + "eos": 5931, + "hwal": 5932, + "salamdeul": 5933, + "jiman": 5934, + "dangsin": 5935, + "jib": 5936, + "ttaemun": 5937, + "ib": 5938, + "eneun": 5939, + "eug": 5940, + "jeom": 5941, + "geuleon": 5942, + "hwa": 5943, + "assda": 5944, + "beob": 5945, + "bae": 5946, + "yeoss": 5947, + "chin": 5948, + "chaeg": 5949, + "geon": 5950, + "naega": 5951, + "iga": 5952, + "sigan": 5953, + "gil": 5954, + "hyeon": 5955, + "lyeog": 5956, + "gug": 5957, + "pyeon": 5958, + "wae": 5959, + "jul": 5960, + "seul": 5961, + "deung": 5962, + "hajiman": 5963, + "eumyeon": 5964, + "pil": 5965, + "nyeon": 5966, + "tae": 5967, + "pyo": 5968, + "jineun": 5969, + "beon": 5970, + "hada": 5971, + "seol": 5972, + "sip": 5973, + "daleun": 5974, + "salm": 5975, + "gyo": 5976, + "cheon": 5977, + "hagi": 5978, + "cheoleom": 5979, + "gal": 5980, + "ila": 5981, + "kkaji": 5982, + "anhneun": 5983, + "habnida": 5984, + "tteon": 5985, + "haeseo": 5986, + "doenda": 5987, + "ttal": 5988, + "ilo": 5989, + "seub": 5990, + "byeon": 5991, + "myeo": 5992, + "beol": 5993, + "jeung": 5994, + "chim": 5995, + "hwang": 5996, + "euneun": 5997, + "jong": 5998, + "boda": 5999, + "nol": 6000, + "neom": 6001, + "buteo": 6002, + "jigeum": 6003, + "eobsda": 6004, + "daelo": 6005, + "yul": 6006, + "pyeong": 6007, + "seoneun": 6008, + "salang": 6009, + "seut": 6010, + "heom": 6011, + "hyang": 6012, + "gwang": 6013, + "eobsneun": 6014, + "hwag": 6015, + "gess": 6016, + "jagi": 6017, + "ileon": 6018, + "wihae": 6019, + "daehan": 6020, + "gaji": 6021, + "meog": 6022, + "jyeo": 6023, + "chaj": 6024, + "byeong": 6025, + "eod": 6026, + "gyeo": 6027, + "eoji": 6028, + "gul": 6029, + "modeun": 6030, + "insaeng": 6031, + "geulae": 6032, + "sasil": 6033, + "sib": 6034, + "chal": 6035, + "ilago": 6036, + "geum": 6037, + "doeneun": 6038, + "bol": 6039, + "gajang": 6040, + "geuligo": 6041, + "hyeong": 6042, + "haengbog": 6043, + "chul": 6044, + "chae": 6045, + "mang": 6046, + "dam": 6047, + "choe": 6048, + "sijag": 6049, + "cheong": 6050, + "ilaneun": 6051, + "ulineun": 6052, + "aen": 6053, + "kke": 6054, + "munje": 6055, + "teu": 6056, + "geuneun": 6057, + "bge": 6058, + "cheo": 6059, + "baeg": 6060, + "jug": 6061, + "sangdae": 6062, + "geugeos": 6063, + "dog": 6064, + "eus": 6065, + "jab": 6066, + "hyeo": 6067, + "tteohge": 6068, + "chil": 6069, + "swi": 6070, + "jileul": 6071, + "chang": 6072, + "ganeun": 6073, + "iji": 6074, + "dago": 6075, + "yohan": 6076, + "teug": 6077, + "ppun": 6078, + "aleul": 6079, + "haengdong": 6080, + "sesang": 6081, + "edo": 6082, + "mandeul": 6083, + "amyeon": 6084, + "kkae": 6085, + "bag": 6086, + "ideul": 6087, + "pum": 6088, + "meol": 6089, + "neul": 6090, + "hamkke": 6091, + "chung": 6092, + "dab": 6093, + "yug": 6094, + "sag": 6095, + "gwangye": 6096, + "ileohge": 6097, + "balo": 6098, + "neunde": 6099, + "hamyeo": 6100, + "geuleoh": 6101, + "anila": 6102, + "bangbeob": 6103, + "dasi": 6104, + "byeol": 6105, + "gyeon": 6106, + "gamjeong": 6107, + "oneul": 6108, + "janeun": 6109, + "yeom": 6110, + "lago": 6111, + "igi": 6112, + "hwan": 6113, + "teul": 6114, + "eoseo": 6115, + "sik": 6116, + "jaga": 6117, + "geuleom": 6118, + "geuleona": 6119, + "jeongdo": 6120, + "gyeog": 6121, + "geuleohge": 6122, + "geudeul": 6123, + "eut": 6124, + "imyeon": 6125, + "jjae": 6126, + "keun": 6127, + "isang": 6128, + "malhaessda": 6129, + "euge": 6130, + "nop": 6131, + "ingan": 6132, + "bomyeon": 6133, + "taeg": 6134, + "dwi": 6135, + "saneun": 6136, + "wan": 6137, + "anhgo": 6138, + "nugu": 6139, + "sung": 6140, + "damyeon": 6141, + "adeul": 6142, + "peul": 6143, + "ttala": 6144, + "geosdo": 6145, + "aji": 6146, + "meon": 6147, + "eumyeo": 6148, + "dolog": 6149, + "neung": 6150, + "modu": 6151, + "[ko]": 6152, + "\u0014": 6153, + "\u0016": 6154, + "$": 6155, + "*": 6156, + "|": 6157, + "°": 6158, + "º": 6159, + "ँ": 6160, + "ं": 6161, + "ः": 6162, + "अ": 6163, + "आ": 6164, + "इ": 6165, + "ई": 6166, + "उ": 6167, + "ऊ": 6168, + "ऋ": 6169, + "ऎ": 6170, + "ए": 6171, + "ऐ": 6172, + "ऑ": 6173, + "ऒ": 6174, + "ओ": 6175, + "औ": 6176, + "क": 6177, + "ख": 6178, + "ग": 6179, + "घ": 6180, + "ङ": 6181, + "च": 6182, + "छ": 6183, + "ज": 6184, + "झ": 6185, + "ञ": 6186, + "ट": 6187, + "ठ": 6188, + "ड": 6189, + "ढ": 6190, + "ण": 6191, + "त": 6192, + "थ": 6193, + "द": 6194, + "ध": 6195, + "न": 6196, + "ऩ": 6197, + "प": 6198, + "फ": 6199, + "ब": 6200, + "भ": 6201, + "म": 6202, + "य": 6203, + "र": 6204, + "ऱ": 6205, + "ल": 6206, + "ळ": 6207, + "व": 6208, + "श": 6209, + "ष": 6210, + "स": 6211, + "ह": 6212, + "़": 6213, + "ा": 6214, + "ि": 6215, + "ी": 6216, + "ु": 6217, + "ू": 6218, + "ृ": 6219, + "ॄ": 6220, + "ॅ": 6221, + "ॆ": 6222, + "े": 6223, + "ै": 6224, + "ॉ": 6225, + "ॊ": 6226, + "ो": 6227, + "ौ": 6228, + "्": 6229, + "ॐ": 6230, + "ॖ": 6231, + "क़": 6232, + "ख़": 6233, + "ग़": 6234, + "ज़": 6235, + "ड़": 6236, + "ढ़": 6237, + "फ़": 6238, + "य़": 6239, + "ॠ": 6240, + "।": 6241, + "॥": 6242, + "०": 6243, + "१": 6244, + "२": 6245, + "३": 6246, + "४": 6247, + "५": 6248, + "६": 6249, + "७": 6250, + "८": 6251, + "९": 6252, + "॰": 6253, + "ॲ": 6254, + "​": 6255, + "‌": 6256, + "‍": 6257, + "‎": 6258, + "₹": 6259, + "के": 6260, + "है": 6261, + "ें": 6262, + "्र": 6263, + "ार": 6264, + "ने": 6265, + "या": 6266, + "में": 6267, + "से": 6268, + "की": 6269, + "का": 6270, + "ों": 6271, + "ता": 6272, + "कर": 6273, + "स्": 6274, + "कि": 6275, + "को": 6276, + "र्": 6277, + "ना": 6278, + "क्": 6279, + "ही": 6280, + "और": 6281, + "पर": 6282, + "ते": 6283, + "हो": 6284, + "प्र": 6285, + "ान": 6286, + "्य": 6287, + "ला": 6288, + "वा": 6289, + "ले": 6290, + "सा": 6291, + "हैं": 6292, + "लि": 6293, + "जा": 6294, + "हा": 6295, + "भी": 6296, + "वि": 6297, + "इस": 6298, + "ती": 6299, + "न्": 6300, + "रा": 6301, + "मा": 6302, + "दे": 6303, + "दि": 6304, + "बा": 6305, + "ति": 6306, + "था": 6307, + "नि": 6308, + "कार": 6309, + "एक": 6310, + "हीं": 6311, + "हु": 6312, + "ंग": 6313, + "ैं": 6314, + "नी": 6315, + "सी": 6316, + "अप": 6317, + "त्": 6318, + "नहीं": 6319, + "री": 6320, + "मे": 6321, + "मु": 6322, + "ित": 6323, + "तो": 6324, + "पा": 6325, + "ली": 6326, + "लिए": 6327, + "गा": 6328, + "ल्": 6329, + "रह": 6330, + "रे": 6331, + "क्ष": 6332, + "मैं": 6333, + "सम": 6334, + "उस": 6335, + "जि": 6336, + "त्र": 6337, + "मि": 6338, + "चा": 6339, + "ोग": 6340, + "सं": 6341, + "द्": 6342, + "सि": 6343, + "आप": 6344, + "तु": 6345, + "दा": 6346, + "कु": 6347, + "यों": 6348, + "वे": 6349, + "जी": 6350, + "्या": 6351, + "उन": 6352, + "िक": 6353, + "ये": 6354, + "भा": 6355, + "्ट": 6356, + "हम": 6357, + "स्ट": 6358, + "शा": 6359, + "ड़": 6360, + "ंद": 6361, + "खा": 6362, + "म्": 6363, + "श्": 6364, + "यह": 6365, + "सक": 6366, + "पू": 6367, + "किया": 6368, + "अपने": 6369, + "रू": 6370, + "सु": 6371, + "मी": 6372, + "हि": 6373, + "जो": 6374, + "थे": 6375, + "रि": 6376, + "दी": 6377, + "थी": 6378, + "गी": 6379, + "लोग": 6380, + "गया": 6381, + "तर": 6382, + "न्ह": 6383, + "च्": 6384, + "वार": 6385, + "बी": 6386, + "प्": 6387, + "दो": 6388, + "टी": 6389, + "शि": 6390, + "करने": 6391, + "गे": 6392, + "ैसे": 6393, + "इन": 6394, + "ंड": 6395, + "साथ": 6396, + "पु": 6397, + "बे": 6398, + "बार": 6399, + "वी": 6400, + "अन": 6401, + "हर": 6402, + "उन्ह": 6403, + "होता": 6404, + "जब": 6405, + "कुछ": 6406, + "मान": 6407, + "क्र": 6408, + "बि": 6409, + "पह": 6410, + "फि": 6411, + "सर": 6412, + "ारी": 6413, + "रो": 6414, + "दू": 6415, + "कहा": 6416, + "तक": 6417, + "शन": 6418, + "ब्": 6419, + "स्थ": 6420, + "वह": 6421, + "बाद": 6422, + "ओं": 6423, + "गु": 6424, + "ज्": 6425, + "्रे": 6426, + "गर": 6427, + "रहे": 6428, + "वर्": 6429, + "हू": 6430, + "ार्": 6431, + "पी": 6432, + "बहु": 6433, + "मुझ": 6434, + "्रा": 6435, + "दिया": 6436, + "सब": 6437, + "करते": 6438, + "अपनी": 6439, + "बहुत": 6440, + "कह": 6441, + "टे": 6442, + "हुए": 6443, + "किसी": 6444, + "रहा": 6445, + "ष्ट": 6446, + "ज़": 6447, + "बना": 6448, + "सो": 6449, + "डि": 6450, + "कोई": 6451, + "व्य": 6452, + "बात": 6453, + "रु": 6454, + "वो": 6455, + "मुझे": 6456, + "द्ध": 6457, + "चार": 6458, + "मेरे": 6459, + "वर": 6460, + "्री": 6461, + "जाता": 6462, + "नों": 6463, + "प्रा": 6464, + "देख": 6465, + "टा": 6466, + "क्या": 6467, + "अध": 6468, + "लग": 6469, + "लो": 6470, + "पि": 6471, + "यु": 6472, + "चे": 6473, + "जिस": 6474, + "ंत": 6475, + "ानी": 6476, + "पै": 6477, + "जन": 6478, + "ारे": 6479, + "ची": 6480, + "मिल": 6481, + "दु": 6482, + "देश": 6483, + "च्छ": 6484, + "ष्": 6485, + "सू": 6486, + "खे": 6487, + "चु": 6488, + "िया": 6489, + "लगा": 6490, + "बु": 6491, + "उनके": 6492, + "ज्ञ": 6493, + "क्षा": 6494, + "तरह": 6495, + "्यादा": 6496, + "वाले": 6497, + "पूर्": 6498, + "मैंने": 6499, + "काम": 6500, + "रूप": 6501, + "होती": 6502, + "उप": 6503, + "जान": 6504, + "प्रकार": 6505, + "भार": 6506, + "मन": 6507, + "हुआ": 6508, + "टर": 6509, + "हूँ": 6510, + "परि": 6511, + "पास": 6512, + "अनु": 6513, + "राज": 6514, + "लोगों": 6515, + "अब": 6516, + "समझ": 6517, + "डी": 6518, + "मौ": 6519, + "शु": 6520, + "चि": 6521, + "पे": 6522, + "कृ": 6523, + "सकते": 6524, + "मह": 6525, + "योग": 6526, + "दर्": 6527, + "उसे": 6528, + "ंध": 6529, + "डा": 6530, + "जाए": 6531, + "बो": 6532, + "ूल": 6533, + "मो": 6534, + "ोंने": 6535, + "ंस": 6536, + "तुम": 6537, + "पहले": 6538, + "बता": 6539, + "तथा": 6540, + "यो": 6541, + "गई": 6542, + "उत्": 6543, + "सकता": 6544, + "कम": 6545, + "ज्यादा": 6546, + "रख": 6547, + "समय": 6548, + "ारा": 6549, + "अगर": 6550, + "स्त": 6551, + "चल": 6552, + "फिर": 6553, + "वारा": 6554, + "करना": 6555, + "शी": 6556, + "गए": 6557, + "बन": 6558, + "ौर": 6559, + "होने": 6560, + "चाह": 6561, + "खु": 6562, + "हाँ": 6563, + "उन्हें": 6564, + "उन्होंने": 6565, + "छो": 6566, + "म्ह": 6567, + "प्रति": 6568, + "निक": 6569, + "वन": 6570, + "्यू": 6571, + "रही": 6572, + "तुम्ह": 6573, + "जैसे": 6574, + "ियों": 6575, + "क्यों": 6576, + "लों": 6577, + "फ़": 6578, + "ंत्र": 6579, + "होते": 6580, + "क्ति": 6581, + "त्य": 6582, + "कर्": 6583, + "कई": 6584, + "वं": 6585, + "किन": 6586, + "पो": 6587, + "कारण": 6588, + "ड़ी": 6589, + "भि": 6590, + "इसके": 6591, + "बर": 6592, + "उसके": 6593, + "द्वारा": 6594, + "शे": 6595, + "कॉ": 6596, + "दिन": 6597, + "न्न": 6598, + "ड़ा": 6599, + "स्व": 6600, + "निर्": 6601, + "मुख": 6602, + "लिया": 6603, + "टि": 6604, + "ज्ञान": 6605, + "क्त": 6606, + "द्र": 6607, + "ग्": 6608, + "क्स": 6609, + "मै": 6610, + "गो": 6611, + "जे": 6612, + "ट्र": 6613, + "मार": 6614, + "त्व": 6615, + "धार": 6616, + "भाव": 6617, + "करता": 6618, + "खि": 6619, + "कं": 6620, + "चाहि": 6621, + "यर": 6622, + "प्त": 6623, + "कों": 6624, + "ंच": 6625, + "जु": 6626, + "मत": 6627, + "अच्छ": 6628, + "हुई": 6629, + "कभी": 6630, + "लेकिन": 6631, + "भू": 6632, + "अपना": 6633, + "दूस": 6634, + "चाहिए": 6635, + "यू": 6636, + "घर": 6637, + "सबसे": 6638, + "मेरी": 6639, + "नाम": 6640, + "ढ़": 6641, + "ंट": 6642, + "ेंगे": 6643, + "बै": 6644, + "फा": 6645, + "एवं": 6646, + "यी": 6647, + "ग्र": 6648, + "क्षे": 6649, + "आज": 6650, + "आपको": 6651, + "भाग": 6652, + "ठा": 6653, + "कै": 6654, + "भारत": 6655, + "उनकी": 6656, + "पहु": 6657, + "सभी": 6658, + "धा": 6659, + "णा": 6660, + "सान": 6661, + "होगा": 6662, + "तब": 6663, + "संग": 6664, + "पर्": 6665, + "अव": 6666, + "तना": 6667, + "गि": 6668, + "यन": 6669, + "स्था": 6670, + "चित": 6671, + "ट्": 6672, + "छा": 6673, + "जाने": 6674, + "क्षेत्र": 6675, + "वाली": 6676, + "पूर्ण": 6677, + "समा": 6678, + "कारी": 6679, + "[hi]": 6680 + }, + "merges": [ + "t h", + "i n", + "th e", + "a n", + "e r", + "o u", + "r e", + "o n", + "a t", + "e d", + "e n", + "t o", + "in g", + "an d", + "i s", + "a s", + "a l", + "o r", + "o f", + "a r", + "i t", + "e s", + "h e", + "s t", + "l e", + "o m", + "s e", + "b e", + "a d", + "o w", + "l y", + "c h", + "w h", + "th at", + "y ou", + "l i", + "v e", + "a c", + "t i", + "l d", + "m e", + "w as", + "g h", + "i d", + "l l", + "w i", + "en t", + "f or", + "a y", + "r o", + "v er", + "i c", + "h er", + "k e", + "h is", + "n o", + "u t", + "u n", + "i r", + "l o", + "w e", + "r i", + "h a", + "wi th", + "gh t", + "ou t", + "i m", + "i on", + "al l", + "a b", + "on e", + "n e", + "g e", + "ou ld", + "t er", + "m o", + "h ad", + "c e", + "s he", + "g o", + "s h", + "u r", + "a m", + "s o", + "p e", + "m y", + "d e", + "a re", + "b ut", + "om e", + "f r", + "the r", + "f e", + "s u", + "d o", + "c on", + "t e", + "a in", + "er e", + "p o", + "i f", + "the y", + "u s", + "a g", + "t r", + "n ow", + "ou n", + "th is", + "ha ve", + "no t", + "s a", + "i l", + "u p", + "th ing", + "fr om", + "a p", + "h im", + "ac k", + "at ion", + "an t", + "ou r", + "o p", + "li ke", + "u st", + "es s", + "b o", + "o k", + "u l", + "in d", + "e x", + "c om", + "s ome", + "the re", + "er s", + "c o", + "re s", + "m an", + "ar d", + "p l", + "w or", + "w ay", + "ti on", + "f o", + "c a", + "w ere", + "b y", + "at e", + "p ro", + "t ed", + "oun d", + "ow n", + "w ould", + "t s", + "wh at", + "q u", + "al ly", + "i ght", + "c k", + "g r", + "wh en", + "v en", + "c an", + "ou gh", + "in e", + "en d", + "p er", + "ou s", + "o d", + "id e", + "k now", + "t y", + "ver y", + "s i", + "a k", + "wh o", + "ab out", + "i ll", + "the m", + "es t", + "re d", + "y e", + "c ould", + "on g", + "you r", + "the ir", + "e m", + "j ust", + "o ther", + "in to", + "an y", + "wh i", + "u m", + "t w", + "as t", + "d er", + "d id", + "i e", + "be en", + "ac e", + "in k", + "it y", + "b ack", + "t ing", + "b r", + "mo re", + "a ke", + "p p", + "the n", + "s p", + "e l", + "u se", + "b l", + "sa id", + "o ver", + "ge t", + "e n", + "e r", + "c h", + "e i", + "i e", + "u n", + "i ch", + "ei n", + "s t", + "a n", + "t e", + "g e", + "a u", + "i n", + "s ch", + "d er", + "un d", + "d ie", + "d a", + "e s", + "a l", + "d en", + "a r", + "g en", + "z u", + "d e", + "h r", + "o n", + "t en", + "e l", + "o r", + "m i", + "s ie", + "da s", + "a t", + "b e", + "ein e", + "ich t", + "b er", + "l e", + "a ch", + "v er", + "s e", + "au f", + "w i", + "s o", + "t er", + "l ich", + "c k", + "u r", + "n icht", + "m m", + "b en", + "a s", + "w ar", + "r e", + "mi t", + "s ich", + "i g", + "l l", + "au s", + "i st", + "w ie", + "o ch", + "un g", + "an n", + "ü r", + "h n", + "i hr", + "s a", + "s en", + "t z", + "de m", + "ei t", + "u m", + "h at", + "wi r", + "v on", + "h a", + "s p", + "w ei", + "i er", + "r o", + "h er", + "r a", + "ein en", + "n e", + "v or", + "al s", + "an d", + "al l", + "w as", + "w o", + "r ei", + "st e", + "l ie", + "au ch", + "d u", + "d es", + "k o", + "ü ber", + "a m", + "b ei", + "h en", + "h m", + "l ei", + "a ber", + "w en", + "h l", + "g er", + "i m", + "u t", + "n ach", + "h e", + "i s", + "b r", + "f t", + "en t", + "i mm", + "j e", + "sch en", + "w er", + "s er", + "a b", + "ä n", + "m e", + "s ein", + "i t", + "o l", + "ch t", + "f ür", + "k l", + "f f", + "eine m", + "n en", + "w e", + "j a", + "u s", + "n och", + "hat te", + "t r", + "p f", + "h in", + "d i", + "ch en", + "b l", + "m an", + "r ü", + "ie l", + "s el", + "das s", + "i hn", + "mi r", + "sch l", + "ö n", + "g an", + "g t", + "ein er", + "st en", + "m ich", + "wen n", + "el l", + "g te", + "in d", + "m al", + "ge l", + "k en", + "n ur", + "mm en", + "f ü", + "er n", + "ö r", + "un ter", + "f r", + "an der", + "g r", + "i l", + "d ur", + "u ch", + "f e", + "t a", + "m en", + "m ach", + "d och", + "t i", + "dur ch", + "o s", + "g l", + "h al", + "ihr e", + "w ä", + "imm er", + "i hm", + "k ann", + "or t", + "d ann", + "l an", + "tz t", + "o der", + "hr en", + "e t", + "k ön", + "i ck", + "f a", + "in g", + "i r", + "wie der", + "da ß", + "m ein", + "f en", + "gan z", + "die se", + "st er", + "da r", + "w a", + "ge s", + "n a", + "f l", + "i gen", + "sch e", + "un gen", + "me hr", + "ß en", + "o t", + "k on", + "ge w", + "ha ben", + "ge h", + "ä t", + "s ind", + "d r", + "w el", + "un s", + "v o", + "m a", + "u te", + "sch on", + "b es", + "ge sch", + "b t", + "ch e", + "s on", + "o b", + "l a", + "p p", + "rü ck", + "s eine", + "k r", + "f re", + "ei l", + "zu m", + "u l", + "h ier", + "k t", + "i ge", + "sp r", + "k e", + "le ben", + "b st", + "z eit", + "i on", + "g ro", + "den n", + "h o", + "sch a", + "b ar", + "al le", + "ge gen", + "w ür", + "m ü", + "z e", + "wer den", + "je tzt", + "ko mmen", + "n ie", + "s ei", + "h eit", + "so ll", + "g lei", + "m eine", + "wo ll", + "n er", + "ha be", + "w ur", + "lich en", + "p er", + "as sen", + "n te", + "se hen", + "wir d", + "b is", + "g ar", + "i en", + "m us", + "u ß", + "ä r", + "st ell", + "k eit", + "z wei", + "sel bst", + "st a", + "p a", + "sa gte", + "te t", + "k am", + "s sen", + "v iel", + "u g", + "z en", + "h ei", + "m ann", + "wi ll", + "ge b", + "war en", + "ü ck", + "ä ch", + "m er", + "r u", + "w or", + "h au", + "ei gen", + "an g", + "we g", + "bl ick", + "f ra", + "all es", + "k a", + "au gen", + "f in", + "lich e", + "t o", + "un ser", + "der n", + "her r", + "n un", + "v ie", + "ch te", + "wo hl", + "f all", + "h t", + "ü n", + "et was", + "st and", + "en d", + "ä u", + "e m", + "m ö", + "te l", + "r ie", + "d ich", + "die s", + "h and", + "b in", + "ff en", + "nicht s", + "d an", + "p l", + "hn e", + "ihn en", + "es en", + "die ser", + "fr au", + "an t", + "ar t", + "di r", + "i sch", + "er st", + "glei ch", + "ko mm", + "h ör", + "ß e", + "d ig", + "se hr", + "z ei", + "sa m", + "au m", + "h ät", + "in gen", + "g ut", + "b o", + "m ut", + "ck en", + "kon nte", + "st imm", + "p ro", + "zu r", + "i tz", + "wei l", + "wür de", + "f ä", + "kön nen", + "k eine", + "f er", + "i schen", + "vo ll", + "ein es", + "se tz", + "z ie", + "de l", + "te te", + "sein er", + "ier en", + "ge st", + "zu rück", + "wur de", + "sch n", + "p r", + "lie ß", + "t ra", + "m ä", + "gen d", + "f ol", + "i k", + "schl a", + "scha ft", + "at er", + "wei ß", + "s einen", + "l assen", + "l u", + "und en", + "t eil", + "ne u", + "ier t", + "men schen", + "hm en", + "st r", + "g i", + "sa h", + "ihr en", + "el n", + "wei ter", + "ge hen", + "ig er", + "mach t", + "ta g", + "al so", + "hal ten", + "n is", + "ach t", + "ge ben", + "f or", + "o g", + "n at", + "m ar", + "de t", + "o hne", + "h aus", + "t ro", + "an ge", + "l au", + "sp iel", + "t re", + "sch r", + "in n", + "s u", + "l os", + "mach en", + "hät te", + "be g", + "wir k", + "al t", + "g lich", + "te s", + "r icht", + "fre und", + "m o", + "ihr er", + "f el", + "b el", + "so l", + "ein mal", + "e ben", + "h ol", + "h än", + "q u", + "ter n", + "h ö", + "sch w", + "re cht", + "wa hr", + "s einem", + "ste hen", + "hl en", + "in s", + "g ing", + "woll te", + "wi ssen", + "ung s", + "al d", + "as s", + "ja hr", + "m or", + "wel t", + "un der", + "zu sa", + "at ion", + "ko pf", + "lan g", + "hin ter", + "at z", + "st ra", + "an gen", + "an k", + "a de", + "gl au", + "f ach", + "hat ten", + "l o", + "f ort", + "ei cht", + "i ff", + "l er", + "m ei", + "diese m", + "k ein", + "f rei", + "fü hr", + "vo m", + "e s", + "e n", + "a i", + "o u", + "o n", + "l e", + "d e", + "r e", + "q u", + "a n", + "e r", + "en t", + "e t", + "l a", + "n e", + "i l", + "a r", + "i s", + "ai t", + "t e", + "a u", + "i n", + "qu e", + "i t", + "u r", + "s e", + "l es", + "c h", + "c e", + "m e", + "o r", + "ou r", + "a s", + "p r", + "a v", + "o m", + "ai s", + "u n", + "an t", + "ou s", + "t r", + "t i", + "l u", + "o i", + "e u", + "l le", + "s i", + "p ar", + "d es", + "an s", + "m ent", + "é t", + "es t", + "j e", + "u ne", + "a l", + "p as", + "t re", + "qu i", + "d u", + "r i", + "c on", + "s on", + "c om", + "e lle", + "d é", + "p our", + "d ans", + "l i", + "s a", + "r é", + "t ou", + "v ous", + "d i", + "v i", + "a g", + "a m", + "a t", + "ou v", + "a p", + "ti on", + "m on", + "s ur", + "c i", + "o s", + "p lu", + "s u", + "en d", + "a b", + "è re", + "ai n", + "m ais", + "o is", + "r es", + "plu s", + "é e", + "ai ent", + "m p", + "ch e", + "lu i", + "av e", + "ét ait", + "m a", + "s es", + "tou t", + "i r", + "v o", + "a c", + "s er", + "an d", + "f f", + "oi r", + "g r", + "av ait", + "é s", + "m es", + "n ous", + "eu x", + "b i", + "t er", + "c o", + "on s", + "p u", + "c es", + "g e", + "t u", + "le ur", + "pr o", + "d on", + "e ur", + "et te", + "ai re", + "ave c", + "d it", + "t é", + "i e", + "u s", + "il le", + "p er", + "com me", + "c r", + "or t", + "m i", + "e x", + "u x", + "v er", + "m o", + "è s", + "v e", + "au x", + "r a", + "j our", + "il s", + "bi en", + "c ou", + "p e", + "que l", + "p eu", + "c ette", + "t es", + "p o", + "in s", + "c u", + "m ê", + "s o", + "f ait", + "g u", + "m ar", + "ê tre", + "l o", + "it é", + "f r", + "a tion", + "en s", + "b r", + "n i", + "l é", + "d is", + "b le", + "m an", + "n é", + "pu is", + "mê me", + "qu es", + "f i", + "e l", + "ag e", + "g ar", + "m oi", + "en ce", + "on t", + "m ain", + "or s", + "au t", + "an ce", + "v en", + "m é", + "s ans", + "e m", + "s é", + "l on", + "h om", + "r o", + "u t", + "c ar", + "ab le", + "i m", + "de r", + "ch er", + "n o", + "vi e", + "au s", + "b e", + "de ux", + "en f", + "o ù", + "t en", + "p h", + "u re", + "te mp", + "p os", + "r ent", + "p é", + "f aire", + "p i", + "tr es", + "ç a", + "an g", + "end re", + "f or", + "p a", + "b on", + "s ou", + "in t", + "pr é", + "s ent", + "t ant", + "n er", + "c er", + "l à", + "l ais", + "pr ès", + "b re", + "c our", + "p et", + "i on", + "i ne", + "com p", + "l ait", + "tr ouv", + "t a", + "ent re", + "son t", + "de v", + "n u", + "temp s", + "d ou", + "r ait", + "b ou", + "qu and", + "jour s", + "l an", + "er s", + "av oir", + "ét é", + "a le", + "p re", + "f ois", + "or te", + "v é", + "m er", + "n on", + "t ous", + "j us", + "cou p", + "t s", + "hom me", + "ê te", + "a d", + "aus si", + "ur s", + "se u", + "or d", + "o b", + "m in", + "g é", + "co re", + "v a", + "v re", + "en core", + "se m", + "i te", + "au tre", + "pr is", + "peu t", + "u e", + "an te", + "m al", + "g n", + "ré p", + "h u", + "si on", + "vo tre", + "di re", + "e z", + "f em", + "leur s", + "m et", + "f in", + "c ri", + "m is", + "t our", + "r ai", + "j am", + "re gar", + "ri en", + "ver s", + "su is", + "p ouv", + "o p", + "v is", + "gr and", + "ant s", + "c or", + "re r", + "ar d", + "c é", + "t ent", + "pr es", + "v ou", + "f a", + "al ors", + "si eur", + "ai ne", + "le r", + "qu oi", + "f on", + "end ant", + "ar ri", + "eu re", + "a près", + "don c", + "it u", + "l è", + "s ait", + "t oi", + "ch a", + "ai l", + "as se", + "i mp", + "vo y", + "con n", + "p la", + "pet it", + "av ant", + "n om", + "t in", + "don t", + "d a", + "s ous", + "e mp", + "per son", + "el les", + "be au", + "par ti", + "ch o", + "pr it", + "tou jours", + "m en", + "r ais", + "jam ais", + "tr av", + "tion s", + "tr ès", + "v oi", + "r en", + "y eux", + "f er", + "v oir", + "pre mi", + "c a", + "g ne", + "h eure", + "r ou", + "e ff", + "no tre", + "ment s", + "t on", + "f ais", + "ce la", + "i er", + "rép on", + "con s", + "ai r", + "ô t", + "p endant", + "i ci", + "tou te", + "j et", + "p ort", + "ét aient", + "p en", + "h é", + "au tres", + "p ère", + "o c", + "quel ques", + "i que", + "l is", + "fem me", + "j ou", + "te ur", + "mon de", + "u se", + "n es", + "d re", + "a ff", + "r ap", + "par t", + "le ment", + "c la", + "f ut", + "quel que", + "pr endre", + "r ê", + "ai lle", + "s ais", + "ch es", + "le t", + "ch ar", + "è res", + "ent s", + "b er", + "g er", + "mo ins", + "e au", + "a î", + "j eu", + "h eur", + "é es", + "tr i", + "po int", + "m om", + "v ent", + "n ouv", + "gr an", + "tr ois", + "s ant", + "tout es", + "con tre", + "è rent", + "che z", + "ave z", + "û t", + "a lle", + "at t", + "p au", + "p orte", + "ouv er", + "b ar", + "l it", + "f ort", + "o t", + "as s", + "pr és", + "cho se", + "v it", + "mon sieur", + "h ab", + "t ête", + "j u", + "te ment", + "c tion", + "v rai", + "la r", + "c et", + "regar d", + "l ant", + "de m", + "s om", + "mom ent", + "il les", + "p le", + "p s", + "b es", + "m ère", + "c l", + "s our", + "y s", + "tr op", + "en ne", + "jus qu", + "av aient", + "av ais", + "jeu ne", + "de puis", + "person ne", + "f it", + "cer t", + "j o", + "g es", + "ou i", + "r est", + "sem b", + "c ap", + "m at", + "m u", + "lon g", + "fr an", + "f aut", + "it i", + "b li", + "che v", + "pr i", + "ent e", + "ain si", + "ch am", + "l ors", + "c as", + "d o", + "il i", + "b é", + "n os", + "an ge", + "su i", + "r it", + "cr o", + "gu e", + "d e", + "e n", + "e s", + "o s", + "l a", + "e r", + "q u", + "a r", + "a n", + "o n", + "qu e", + "a s", + "o r", + "e l", + "d o", + "a l", + "c i", + "u n", + "r e", + "a b", + "i n", + "t e", + "t o", + "s e", + "d i", + "t r", + "d a", + "c on", + "t a", + "s u", + "m i", + "c o", + "t i", + "l e", + "l os", + "n o", + "l o", + "í a", + "c u", + "c a", + "s i", + "v i", + "m e", + "p or", + "m o", + "p ar", + "r a", + "r i", + "la s", + "c h", + "r o", + "m a", + "p er", + "ó n", + "m en", + "de s", + "un a", + "m p", + "s o", + "ab a", + "p u", + "d os", + "t u", + "g u", + "er a", + "de l", + "h a", + "m u", + "l i", + "en t", + "m b", + "h ab", + "es t", + "g o", + "p a", + "r es", + "par a", + "p o", + "á s", + "m os", + "tr a", + "t en", + "an do", + "p i", + "qu i", + "b i", + "m an", + "co mo", + "v e", + "m ás", + "j o", + "ci ón", + "i s", + "t an", + "v o", + "da d", + "c e", + "a do", + "v er", + "f u", + "ci a", + "c er", + "p e", + "c as", + "c ar", + "men te", + "n i", + "su s", + "t ar", + "n a", + "f i", + "t er", + "z a", + "p ro", + "tr o", + "s a", + "l u", + "b a", + "per o", + "s er", + "c es", + "d as", + "d u", + "s in", + "e mp", + "m ar", + "l la", + "e x", + "á n", + "c or", + "i a", + "v a", + "r an", + "ch o", + "g a", + "y o", + "t os", + "c os", + "mi s", + "l es", + "t es", + "v en", + "h o", + "y a", + "en te", + "on es", + "hab ía", + "n u", + "u s", + "p as", + "h i", + "n os", + "es ta", + "la n", + "m as", + "t or", + "l le", + "h e", + "s on", + "b re", + "p re", + "ab an", + "d or", + "í an", + "i r", + "t as", + "é n", + "r u", + "en do", + "a que", + "er o", + "i o", + "qu é", + "m in", + "c ab", + "j a", + "de r", + "t al", + "é s", + "se ñ", + "or a", + "to do", + "la r", + "d on", + "g ar", + "s al", + "p r", + "cu ando", + "j e", + "h u", + "g un", + "b u", + "g i", + "d ar", + "n e", + "r as", + "de n", + "es to", + "par e", + "p en", + "é l", + "tr as", + "c an", + "b o", + "j os", + "mi en", + "pu e", + "c re", + "co mp", + "p on", + "d ía", + "tr os", + "s ab", + "so bre", + "es e", + "mb re", + "er on", + "a ñ", + "m or", + "f or", + "i do", + "por que", + "el la", + "p ri", + "g ran", + "f a", + "c en", + "di s", + "c ri", + "mu y", + "ch a", + "c al", + "es te", + "h as", + "c ó", + "g ra", + "r os", + "p os", + "o b", + "al l", + "aque l", + "j u", + "p res", + "m er", + "di jo", + "c ía", + "ent re", + "z o", + "ci ones", + "bi en", + "mb i", + "el o", + "t ó", + "in a", + "to dos", + "g en", + "ti en", + "est aba", + "de ci", + "ci o", + "h er", + "ñ o", + "l or", + "nu es", + "me di", + "l en", + "vi da", + "f e", + "al i", + "m on", + "c la", + "d re", + "pu es", + "al es", + "vo l", + "m í", + "r ar", + "b le", + "ci on", + "has ta", + "señ or", + "con o", + "a h", + "di os", + "s en", + "es a", + "ú n", + "v ar", + "s an", + "gu i", + "a c", + "o tros", + "ta do", + "bu en", + "ñ a", + "ti emp", + "ha cer", + "j er", + "f er", + "v u", + "f in", + "an a", + "as í", + "an tes", + "t in", + "ve z", + "mien to", + "j ar", + "la b", + "ch e", + "cas a", + "d r", + "es o", + "e go", + "di ó", + "an te", + "est á", + "m al", + "en cia", + "el i", + "í as", + "tiemp o", + "z ar", + "v an", + "m un", + "er ta", + "ta mbi", + "s í", + "b ar", + "a un", + "al e", + "mis mo", + "ent es", + "vi s", + "man o", + "el e", + "na da", + "se gu", + "me j", + "er ra", + "ab le", + "b e", + "ti r", + "un o", + "don de", + "to da", + "des de", + "r en", + "tambi én", + "cu er", + "per son", + "ho mbre", + "o tro", + "li b", + "tr ar", + "cu al", + "ha y", + "a u", + "ca da", + "t aba", + "i mp", + "men to", + "ten ía", + "qu er", + "er an", + "si emp", + "siemp re", + "er to", + "qu í", + "g os", + "pu és", + "el los", + "des pués", + "nu e", + "g an", + "l lo", + "in ter", + "có mo", + "tr i", + "ah ora", + "us te", + "tr aba", + "la do", + "in o", + "po co", + "er te", + "mu jer", + "i m", + "qui er", + "al gun", + "fu e", + "o jos", + "ent on", + "v os", + "es per", + "mu ch", + "o tra", + "a z", + "a d", + "in g", + "e za", + "a quí", + "ci as", + "gu a", + "mu cho", + "deci r", + "es ti", + "i dad", + "al go", + "e z", + "o cu", + "enton ces", + "di do", + "ent os", + "g ri", + "da do", + "i os", + "so l", + "dos e", + "uste d", + "qui en", + "a mi", + "un to", + "f r", + "mi r", + "mej or", + "b as", + "so lo", + "pre gun", + "tu r", + "al g", + "p la", + "to das", + "par te", + "e mb", + "c to", + "mun do", + "tien e", + "tan te", + "pa lab", + "tr an", + "aque lla", + "ci os", + "aun que", + "a y", + "cu en", + "ten er", + "f un", + "res pon", + "all í", + "x i", + "h an", + "pen s", + "con tra", + "tu ra", + "v al", + "di o", + "tr es", + "t re", + "tan to", + "ca min", + "m ó", + "es p", + "a da", + "í o", + "in s", + "ha cia", + "de j", + "est ar", + "i ón", + "g as", + "b er", + "v as", + "no che", + "é r", + "añ os", + "pa dre", + "gu s", + "á r", + "sin o", + "man os", + "ci do", + "es tu", + "a de", + "hu bi", + "vi r", + "b ri", + "ra z", + "ch i", + "pue de", + "men os", + "hab i", + "ho mb", + "ne ces", + "ma y", + "er os", + "r ía", + "he cho", + "es cu", + "l ti", + "án do", + "b us", + "cos as", + "t ú", + "es pa", + "re ci", + "c tor", + "pri m", + "di a", + "de se", + "mien tras", + "h or", + "fu er", + "i da", + "pos i", + "lan te", + "t on", + "an o", + "est as", + "p li", + "ch ar", + "lu ego", + "si ón", + "ci n", + "ti erra", + "m es", + "gu ar", + "ca do", + "en con", + "pr en", + "may or", + "f al", + "e r", + "o n", + "a n", + "t o", + "d i", + "r e", + "l a", + "i n", + "e n", + "a l", + "t a", + "c h", + "e l", + "r i", + "c o", + "t i", + "t e", + "s i", + "r a", + "u n", + "l e", + "l i", + "ch e", + "r o", + "c i", + "c a", + "s e", + "q u", + "m a", + "p o", + "s o", + "i l", + "d o", + "e s", + "v a", + "p er", + "l o", + "c on", + "d el", + "p a", + "m o", + "s a", + "p i", + "d a", + "m i", + "g i", + "s u", + "d e", + "v i", + "z i", + "m e", + "g li", + "n o", + "m en", + "v o", + "t u", + "n on", + "v e", + "t to", + "s t", + "on e", + "an o", + "ch i", + "er a", + "er e", + "f a", + "c e", + "z a", + "un a", + "b i", + "p re", + "s ta", + "o r", + "a r", + "f i", + "on o", + "t ra", + "n a", + "n el", + "n e", + "p ro", + "t ro", + "al e", + "v er", + "n i", + "c u", + "t ti", + "men te", + "del la", + "t er", + "zi one", + "g u", + "p e", + "t ta", + "an do", + "t à", + "al i", + "u o", + "qu el", + "co m", + "s en", + "co me", + "b a", + "al la", + "p ri", + "d u", + "qu es", + "l u", + "on i", + "g gi", + "pa r", + "s si", + "v en", + "in a", + "g a", + "pi ù", + "ci a", + "i m", + "co r", + "m an", + "in o", + "in i", + "t en", + "r an", + "b b", + "g o", + "s to", + "t re", + "a ve", + "a v", + "s ono", + "er i", + "a c", + "s se", + "er o", + "h a", + "s c", + "su l", + "f or", + "v ano", + "po r", + "s ti", + "su o", + "c chi", + "t an", + "z za", + "an che", + "p u", + "i o", + "t te", + "vo l", + "es s", + "s ci", + "co l", + "r u", + "p en", + "f u", + "al l", + "s so", + "s te", + "se m", + "s sa", + "d en", + "a d", + "t ri", + "de i", + "in e", + "ave va", + "men to", + "z z", + "a mo", + "g no", + "f o", + "un o", + "su a", + "g en", + "ri a", + "g e", + "st ra", + "s ì", + "c er", + "ch é", + "b u", + "a p", + "c en", + "d al", + "on a", + "s pe", + "g ni", + "b o", + "t t", + "del le", + "ques to", + "nel la", + "f f", + "d ere", + "an no", + "del l", + "un i", + "bb e", + "an ti", + "g ra", + "s p", + "en e", + "gi o", + "u to", + "qu al", + "gli a", + "qu ando", + "tu tto", + "c an", + "gli o", + "zi oni", + "ca m", + "h o", + "es so", + "s s", + "mo l", + "a t", + "lo ro", + "per ché", + "co sa", + "du e", + "po i", + "ca r", + "s co", + "ci o", + "to r", + "c co", + "c re", + "a m", + "g na", + "te m", + "pri ma", + "lu i", + "co sì", + "qu e", + "gu ar", + "ess ere", + "an i", + "con o", + "b ra", + "al le", + "m on", + "ri o", + "an co", + "cu i", + "s pi", + "vi a", + "g ran", + "gi or", + "a i", + "bi le", + "u l", + "ggi o", + "f e", + "an te", + "ma i", + "ta re", + "in ter", + "in di", + "re bbe", + "sen za", + "so lo", + "zi o", + "e d", + "en te", + "tu tti", + "sta to", + "zi a", + "d alla", + "tu ra", + "mi a", + "vi ta", + "quel la", + "qu a", + "ma r", + "do ve", + "g h", + "al lo", + "sem pre", + "zz o", + "si a", + "mo r", + "do po", + "por ta", + "d re", + "c cia", + "er ano", + "an ni", + "di o", + "chi a", + "en za", + "pro pri", + "qu i", + "m u", + "m b", + "an da", + "c ca", + "o cchi", + "ques ta", + "f fi", + "le i", + "par te", + "d on", + "r on", + "mi o", + "tan to", + "ri s", + "o gni", + "di s", + "r in", + "fa r", + "men ti", + "t el", + "anco ra", + "f ra", + "fa tto", + "man i", + "sen ti", + "p ra", + "tem po", + "es si", + "b bi", + "f in", + "a re", + "la re", + "per s", + "f on", + "b el", + "so r", + "d er", + "pre n", + "an za", + "di re", + "pi e", + "o ra", + "ver so", + "se gu", + "al tro", + "ta to", + "ca to", + "a to", + "vol ta", + "c c", + "fa re", + "pa re", + "ci ò", + "li b", + "bi li", + "n uo", + "s er", + "quel lo", + "co lo", + "p po", + "ca sa", + "tro va", + "o re", + "f er", + "r ono", + "d es", + "mol to", + "al mente", + "s ca", + "vo le", + "t ali", + "sul la", + "s ce", + "men o", + "an to", + "p un", + "s tu", + "ca pi", + "so l", + "gi u", + "m ini", + "m ano", + "z e", + "pi a", + "par ti", + "s al", + "la vo", + "ver o", + "r si", + "al tri", + "es ti", + "s cia", + "suo i", + "gli e", + "so tto", + "b ene", + "sc ri", + "t ale", + "de gli", + "n u", + "al c", + "uo mo", + "p el", + "f re", + "po te", + "es sa", + "s cu", + "si gno", + "el e", + "st ro", + "u ti", + "di a", + "si one", + "g re", + "f ini", + "ar ri", + "l un", + "c ri", + "e si", + "pa ssa", + "r à", + "men tre", + "an d", + "h anno", + "el o", + "u sci", + "gi a", + "gi à", + "di e", + "m ina", + "b e", + "ti ca", + "gior no", + "t in", + "es se", + "mo do", + "c al", + "s pa", + "propri o", + "l en", + "o ri", + "con tro", + "st ru", + "di ven", + "di sse", + "ra to", + "no i", + "v ere", + "pu ò", + "di ce", + "s an", + "es a", + "c ci", + "se con", + "re n", + "c cio", + "qual che", + "tu tta", + "g g", + "mon do", + "for ma", + "p li", + "m ma", + "pen sa", + "de va", + "tu r", + "fo sse", + "so pra", + "ta mente", + "n ess", + "qu anto", + "ra ga", + "un que", + "ca re", + "st re", + "gran de", + "pi cco", + "guar da", + "b en", + "nel l", + "a ff", + "po ssi", + "pre sen", + "r ò", + "pa ro", + "tu a", + "v in", + "an e", + "a s", + "ste sso", + "da v", + "ne i", + "nel le", + "gh i", + "pi o", + "ta r", + "an a", + "la to", + "si d", + "f ine", + "f uo", + "m er", + "z o", + "qua si", + "ul ti", + "i to", + "su e", + "si e", + "f il", + "allo ra", + "m in", + "ven i", + "t ano", + "el lo", + "d e", + "r a", + "e s", + "d o", + "e n", + "q u", + "c o", + "a s", + "o s", + "e r", + "a r", + "s e", + "qu e", + "a n", + "i n", + "i s", + "t o", + "ã o", + "t e", + "d a", + "m a", + "e l", + "t a", + "o r", + "i a", + "r e", + "e m", + "a l", + "co m", + "p a", + "o u", + "c a", + "u m", + "r o", + "v a", + "t i", + "s o", + "m en", + "n ão", + "h a", + "co n", + "m e", + "r i", + "pa ra", + "p o", + "d i", + "s a", + "v o", + "u ma", + "c i", + "n a", + "p or", + "n o", + "g u", + "s u", + "h o", + "an do", + "t ra", + "e i", + "v i", + "e u", + "i m", + "do s", + "el e", + "r es", + "m o", + "en t", + "f i", + "l a", + "e ra", + "l e", + "de s", + "el a", + "men te", + "l h", + "p er", + "l i", + "ç ão", + "m as", + "t er", + "m u", + "es t", + "v e", + "g o", + "l o", + "u s", + "ma is", + "v er", + "c ê", + "in ha", + "vo cê", + "f a", + "t u", + "c u", + "p ar", + "com o", + "p ro", + "s i", + "m os", + "e c", + "p re", + "d as", + "ç a", + "es ta", + "s er", + "u n", + "da de", + "d is", + "f o", + "e x", + "c h", + "i r", + "ra n", + "t ar", + "en te", + "g a", + "t r", + "p e", + "t os", + "b o", + "c ia", + "p en", + "c ar", + "s en", + "su a", + "se m", + "c as", + "f or", + "to u", + "n os", + "te m", + "r ia", + "m es", + "se u", + "co r", + "o n", + "a o", + "p os", + "ra m", + "v el", + "é m", + "t en", + "po de", + "t es", + "esta va", + "c e", + "b a", + "qu ando", + "m i", + "qu er", + "men to", + "se gu", + "t as", + "is so", + "mu i", + "g ar", + "t ro", + "d u", + "fa z", + "õ es", + "p es", + "an to", + "l u", + "p i", + "i x", + "ve z", + "s im", + "j a", + "p r", + "m in", + "b e", + "ra s", + "m an", + "p res", + "est á", + "c er", + "b re", + "p as", + "d ia", + "m b", + "dis se", + "n i", + "r os", + "es se", + "v ia", + "o lh", + "is a", + "an te", + "ê n", + "z a", + "qu i", + "b i", + "t inha", + "me u", + "s ão", + "m inha", + "a c", + "ri o", + "m ar", + "a t", + "p el", + "mui to", + "ta l", + "to r", + "fo i", + "h or", + "j o", + "b em", + "g i", + "f al", + "vo l", + "po n", + "di z", + "l ar", + "gu n", + "m or", + "r u", + "par ec", + "ç o", + "do r", + "pes so", + "n e", + "f er", + "b er", + "p u", + "po is", + "in a", + "es p", + "d ar", + "en do", + "de n", + "so bre", + "co s", + "p ri", + "al i", + "mes mo", + "ç ões", + "g ra", + "se us", + "me i", + "b ra", + "vi da", + "an tes", + "b ri", + "at é", + "ên cia", + "lh e", + "ti v", + "m ã", + "al g", + "qu anto", + "s ó", + "g os", + "de r", + "t ão", + "tu do", + "ent ão", + "r ou", + "es s", + "in da", + "b al", + "in do", + "ci o", + "n do", + "j á", + "va m", + "re i", + "l es", + "ei to", + "v is", + "tem po", + "de pois", + "c ha", + "m el", + "ch e", + "l ha", + "a inda", + "faz er", + "con tra", + "p ou", + "per gun", + "de ix", + "ta mb", + "ra r", + "al a", + "v en", + "t in", + "pel o", + "tamb ém", + "fi ca", + "pre c", + "el es", + "tra n", + "ha via", + "l á", + "to dos", + "j u", + "qu al", + "c an", + "ta do", + "cas a", + "es sa", + "n as", + "g em", + "m em", + "se i", + "na da", + "sen ti", + "c ri", + "ó s", + "de u", + "ei ro", + ". .", + "f un", + "as sim", + "s ou", + "ent re", + "com e", + "i or", + "h ar", + "f e", + "por que", + "s or", + "f in", + "ta mente", + "a qui", + "cu l", + "t ó", + "for ma", + "s ar", + "ou tra", + "olh os", + "i ma", + "m im", + "a go", + "in s", + "co u", + "g ran", + "v al", + "pesso as", + "era m", + "ei ra", + "a que", + "com p", + "de i", + "p ela", + "co isa", + "m ão", + "con h", + "ca da", + "ago ra", + "ia m", + "h á", + "con s", + "su as", + "gu ém", + "o b", + "l an", + "es ti", + "á s", + "la do", + "in ter", + "ca be", + "por ta", + "n em", + "í vel", + "r is", + "j e", + "n un", + "sem pre", + "con segu", + "h as", + "tra bal", + "f u", + "le v", + "l em", + "l as", + "va i", + "tr os", + "t ante", + "te i", + "pr ó", + "que m", + "tu ra", + "on de", + "cabe ça", + "nun ca", + "men tos", + "h um", + "de le", + "ver dade", + "t á", + "h os", + "el i", + "ent es", + "m er", + "alg um", + "diz er", + "s in", + "pen as", + "n ós", + "en quanto", + "ou tro", + "l ho", + "es te", + "mel hor", + "est ar", + "g an", + "b ar", + "pri mei", + "a u", + "i u", + "pen sa", + "a penas", + "p ra", + "es tou", + "con te", + "res pon", + "ho mem", + "do is", + "a do", + "c al", + "a b", + "l os", + "ç as", + "pou co", + "sen hor", + "t ando", + "esp era", + "pa i", + "ri os", + "no i", + "i da", + "ba ix", + "as e", + "is as", + "f r", + "ho ra", + "mu ndo", + "pas sa", + "fi car", + "to do", + "se ja", + "al mente", + "â n", + "c lar", + "a d", + "in c", + "f os", + "lo n", + "g ri", + "ou vi", + "v em", + "g e", + "ta va", + "á rio", + "mo n", + "s os", + "in ho", + "ma l", + "t an", + "t re", + "gran de", + "ran do", + "b u", + "v ou", + "ê s", + "co isas", + "a conte", + "lh er", + "g en", + "ci on", + "an os", + "i do", + "tal vez", + "est ão", + "li v", + "sa b", + "su r", + "ou tros", + "c re", + "qual quer", + "g ou", + "t ri", + "l í", + "tiv esse", + "ra do", + "prec isa", + "mã e", + "su s", + "t anto", + "de la", + "men os", + "s al", + "en tra", + "p é", + "ma ior", + "noi te", + "ti va", + "p ala", + "so n", + "ra ção", + "de us", + "s as", + "un i", + "l or", + "u l", + "in te", + "f ei", + "an o", + "par ti", + "pala v", + "tr ás", + "par te", + "b el", + "ci dade", + "lu gar", + "v os", + "vez es", + "do u", + "en contra", + "tr u", + "e ci", + "a r", + "e r", + "a n", + "e n", + "i n", + "i r", + "o r", + "d e", + "a k", + "ı n", + "a l", + "d i", + "d a", + "b u", + "b ir", + "y or", + "i l", + "e k", + "y a", + "m a", + "l a", + "e l", + "u n", + "k a", + "l ar", + "i m", + "d ı", + "e t", + "o n", + "d u", + "o l", + "e y", + "t ı", + "m i", + "h a", + "b a", + "l er", + "ü n", + "m ı", + "i z", + "l e", + "ı r", + "m e", + "i s", + "n e", + "o k", + "t a", + "s a", + "u m", + "r a", + "g ö", + "i k", + "s ı", + "d en", + "e s", + "b il", + "t i", + "l ı", + "ü z", + "i ç", + "ü r", + "g i", + "u r", + "t e", + "b en", + "d an", + "i y", + "ı m", + "u z", + "v e", + "c ak", + "a y", + "c e", + "i ş", + "ın ı", + "i yor", + "ba ş", + "d ü", + "a t", + "a m", + "g el", + "de ğ", + "k ar", + "i ̇", + "m u", + "e v", + "ö y", + "bu n", + "v ar", + "ya p", + "s en", + "an a", + "s un", + "in i", + "gö r", + "y ı", + "k i", + "l i", + "ar a", + "al ı", + "on u", + "ç ı", + "ş ey", + "s ın", + "k ı", + "ka d", + "s e", + "t an", + "a ğ", + "değ il", + "s in", + "ü k", + "a z", + "ç ok", + "s on", + "ş ı", + "b i", + "ü l", + "t u", + "v er", + "iç in", + "g e", + "k en", + "ey e", + "ol du", + "mı ş", + "y e", + "k al", + "m ek", + "l an", + "öy le", + "yor du", + "er i", + "y üz", + "mi ş", + "b e", + "m ak", + "o la", + "in e", + "y an", + "h er", + "c ek", + "yor um", + "b ak", + "ü m", + "ö n", + "lar ı", + "o ğ", + "d er", + "kad ar", + "h al", + "ar ı", + "s t", + "s an", + "ın da", + "du r", + "g ün", + "v a", + "y ok", + "y er", + "dı m", + "k o", + "da ha", + "l u", + "ın a", + "di m", + "e m", + "bil ir", + "ik i", + "s iz", + "s i", + "n a", + "di ğ", + "s u", + "b ü", + "ha y", + "s or", + "dü ş", + "ü ç", + "un u", + "ö r", + "d ir", + "m ü", + "c a", + "am an", + "f ak", + "a da", + "e de", + "son ra", + "h iç", + "ak i", + "ğ ı", + "bu l", + "r u", + "ma z", + "an la", + "bu ra", + "ge ç", + "ma ya", + "l en", + "k onu", + "c i", + "c u", + "d in", + "t ek", + "z aman", + "el er", + "ö z", + "dı r", + "gi bi", + "o t", + "ş a", + "g er", + "ler i", + "k im", + "k u", + "fak at", + "y ar", + "gö z", + "c ı", + "yor sun", + "b ek", + "in de", + "r o", + "p ek", + "bun u", + "l ik", + "m an", + "il er", + "e di", + "ö l", + "s ür", + "b in", + "s ır", + "çı k", + "sı l", + "al ar", + "k es", + "y ak", + "ç ek", + "yı l", + "e cek", + "ı z", + "gi t", + "ka p", + "a ma", + "ı l", + "lar ın", + "b iz", + "tı r", + "o y", + "an cak", + "d oğ", + "ç a", + "b ana", + "ş im", + "baş la", + "l ü", + "ma dı", + "ben i", + "t ir", + "y ük", + "lı k", + "be ş", + "b el", + "b er", + "m er", + "na sıl", + "tı k", + "k e", + "t ür", + "a v", + ". .", + "d aki", + "p ar", + "t er", + "ce ğ", + "t en", + "z ı", + "iy i", + "d ok", + "ben im", + "c ağ", + "n er", + "y en", + "ş u", + "me z", + "düş ün", + "ken di", + "şim di", + "y ol", + "y u", + "de v", + "is te", + "s ek", + "ma m", + "s öyle", + "di k", + "t o", + "k ur", + "oldu ğ", + "s ını", + "t ar", + "bil iyor", + "k an", + "y al", + "m eye", + "mu ş", + "f a", + "ka ç", + "bil e", + "iy e", + "t ü", + "e f", + "tı m", + "ev et", + "ç o", + "y et", + "g en", + "bura da", + "t im", + "bir az", + "es i", + "k or", + "doğ ru", + "in in", + "kı z", + "di ye", + "d ör", + "et ti", + "on un", + "is ti", + "ğ i", + "h e", + "s ana", + "ü ş", + "ar ka", + "hay ır", + "kar şı", + "h ar", + "il e", + "h ak", + "ı yor", + "ne den", + "s ev", + "sı z", + "ço cu", + "me m", + "ç alı", + "ol ur", + "b ır", + "g ir", + "is e", + "i h", + "c an", + "k ır", + "d ön", + "b öyle", + "sen i", + "! \"", + "al t", + "dör t", + "s öy", + "o ş", + "mu sun", + "la ş", + "h an", + "i p", + "ka y", + "h em", + "bü yük", + "a ç", + "bır ak", + "mi sin", + "s öz", + "u l", + "değ iş", + "ün ü", + "g ül", + "k ö", + "kar ı", + "ta mam", + "ol u", + "r ar", + "yen i", + "la m", + "mış tı", + "ya ş", + "al a", + "in iz", + "kad ın", + "bun un", + "m ey", + "al tı", + "y i", + "s o", + "in den", + "sen in", + "ya t", + "to p", + "s er", + "is i", + "d ün", + "s es", + "hiç bir", + "y on", + "d ın", + "t ün", + "baş ka", + "a s", + "he p", + "i t", + "ir mi", + "dev am", + "ola cak", + "ar tık", + "r e", + "dur um", + "im iz", + "üz el", + "ler ini", + "sa ğ", + "p ro", + "ger ek", + "y irmi", + "ş ek", + "ba ğ", + "me di", + "lar a", + "a h", + "t ur", + "y ür", + "ma sı", + "ka tı", + "de di", + "g ü", + "sor un", + "el i", + "ün e", + "mı z", + "yap ı", + "m il", + "ğ ını", + "t ara", + "m en", + "ha t", + "var dı", + "m et", + "konu ş", + "ar ak", + "lar ak", + "çocu k", + "bü tün", + "l ey", + "d ür", + "g üzel", + "ay ı", + "yap a", + "n ı", + "ay r", + "ö ne", + "yordu m", + "b an", + "i̇ ş", + "du m", + "un a", + "on a", + "yor lar", + "lar ını", + "çı kar", + "z an", + "se ç", + "l iyor", + "t ak", + "şı k", + "tek rar", + "a ş", + "e ş", + "miş ti", + "f ar", + "k in", + "im i", + "i f", + "e ğ", + "gi di", + "le ş", + "başla dı", + "gi de", + "ot ur", + "d de", + "ın dan", + "üz er", + "ın ın", + "n ız", + "u y", + "ye di", + "ka t", + "o larak", + "la dı", + "yal nız", + "ba h", + "iy et", + "m al", + "s ak", + "a çık", + "sın da", + ".. .", + "in san", + "ay nı", + "e der", + "is tan", + "uz un", + "sa h", + "d o", + "g eri", + "er ek", + "ol an", + "ger çek", + "f en", + "al an", + "dı ş", + "alı k", + "far k", + "ü st", + "sa de", + "r i", + "k iş", + "l dı", + "z or", + "et ir", + "her kes", + "s al", + "ö mer", + "s el", + "un da", + "ha f", + "bun a", + "y dı", + "pek i", + "ada m", + "ha z", + "sın a", + "kap ı", + "gör üş", + "sade ce", + "al dı", + "gel di", + "i e", + "n ie", + "n a", + "r z", + "s z", + "c z", + "p o", + "s t", + "c h", + "i ę", + "d z", + "n i", + "a ł", + "r a", + "j e", + "r o", + "d o", + "s ię", + "z a", + "g o", + "e m", + "w i", + "c i", + "rz e", + "k o", + "l e", + "l i", + "w a", + "t o", + "k a", + "m i", + "ż e", + "t a", + "w ie", + "b y", + "m o", + "w y", + "rz y", + "ł a", + "j a", + "n o", + "ł o", + "w o", + "p a", + "m a", + "t e", + "t y", + "n y", + "k i", + "d a", + "n e", + "dz ie", + "dz i", + "cz y", + "c ie", + "m y", + "p rze", + "d y", + "o d", + "l a", + "k ie", + "r y", + "st a", + "j ą", + "ó w", + "c e", + "p rzy", + "c o", + "k u", + "m ie", + "sz y", + "cz e", + "r e", + "b a", + "s i", + "b ie", + "m u", + "w e", + "c y", + "ni a", + "ś ci", + "sz e", + "je st", + "k t", + "s a", + "b o", + "t u", + "ż y", + "n ą", + "b i", + "r u", + "a le", + "kt ó", + "p ra", + "ał a", + "m nie", + "p ie", + "ł y", + "cz a", + "ja k", + "ro z", + "r ó", + "l u", + "z na", + "g a", + "ra z", + "ł u", + "ta k", + "j u", + "p i", + "ś ć", + "s o", + "wi a", + "m ó", + "ch o", + "w szy", + "p e", + "s po", + "c a", + "g dy", + "w ał", + "w ię", + "d e", + "b e", + "p ro", + "ł em", + "j ę", + "s k", + "z e", + "l o", + "g i", + "r ę", + "do b", + "d u", + "ju ż", + "st o", + "b ę", + "ał em", + "sz a", + "m e", + "po d", + "d la", + "pa n", + "n ę", + "z o", + "mo że", + "ś li", + "s ie", + "ał o", + "t em", + "l ko", + "ny ch", + "po wie", + "c ię", + "s u", + "ty lko", + "i n", + "b u", + "na j", + "ch a", + "te go", + "p u", + "s ki", + "ne go", + "wszy st", + "sz cze", + "je d", + "je j", + "t wo", + "ą d", + "ś my", + "cz ę", + "wa ć", + "je go", + "ż a", + "i m", + "s y", + "pra w", + "ty m", + "któ ry", + "ał y", + "t rze", + "nie j", + "s e", + "ny m", + "i ch", + "o b", + ". .", + "g ło", + "ją c", + "mó wi", + "s ka", + "o n", + "ne j", + "s łu", + "w ła", + "bę dzie", + "d ę", + "p ó", + "be z", + "ni c", + "p ła", + "ś cie", + "mi a", + "s ą", + "t rzy", + "kie m", + "by ł", + "mo g", + "ro bi", + "ta m", + "c u", + "te n", + "m ię", + "z y", + "pe w", + "ci a", + "my ś", + "prze d", + "s ko", + "n u", + "któ re", + "a l", + "l ę", + "w sze", + "ą c", + "by ło", + "so bie", + "p y", + "ci ą", + "ba r", + "je szcze", + "h a", + "t ę", + "b ra", + "cza s", + "sz ę", + "g ł", + "k ę", + "ma r", + "cz u", + "prze z", + "f i", + "s ło", + "w z", + "k to", + "k ów", + "cz o", + "li śmy", + "st ra", + "wię c", + "r ą", + "ma m", + "w ó", + "rz a", + "g ro", + "no ści", + "f a", + "we t", + "ną ł", + "ś mie", + "na wet", + "mu si", + "s wo", + "te j", + "w ą", + "w u", + "wi ą", + "ni u", + "cz ą", + "b li", + "dz o", + "s kie", + "n em", + "je śli", + "cze go", + "ch y", + "d ł", + "ty ch", + "by m", + "ż o", + "e ś", + "si ą", + "kie dy", + "na s", + "w ró", + "dz e", + "d ro", + "t ra", + "r ów", + "pa ni", + "z ie", + "ku l", + "na d", + "ch wi", + "ni m", + "t ro", + "by ć", + "cho dzi", + "ni o", + "dob rze", + "te raz", + "wo kul", + "co ś", + "k ł", + "pie r", + "h e", + "g dzie", + "dz y", + "p ię", + "d ź", + "k ą", + "g ó", + "z da", + "ch ce", + "st ę", + "o r", + "ś wia", + "wszyst ko", + "st ro", + "pe ł", + "wie m", + "wie l", + "ka ż", + "ki m", + "rz u", + "s ły", + "jed na", + "z u", + "myś l", + "mó j", + "g u", + "wa r", + "jest em", + "ó ż", + "mie j", + "mo ż", + "k ła", + "re sz", + "d łu", + "st wo", + "n ię", + "ma sz", + "że by", + "nie m", + "ja kie", + "st y", + "ni ą", + "we j", + "o j", + "g ra", + "s ła", + "no ść", + "z ło", + "sz czę", + ".. .", + "r i", + "le j", + "we go", + "c ał", + "dzi ał", + "ki ch", + "dz a", + "dz ię", + "o czy", + "zo sta", + "cz ło", + "na m", + "ki l", + "o na", + "sz u", + "w ę", + "pa r", + "mi ał", + "st rze", + "ce j", + "e j", + "zna j", + "da ć", + "miej s", + "k ró", + "k ry", + "bar dzo", + "si a", + "z i", + "ś nie", + "l ą", + "g ie", + "cie bie", + "d ni", + "st u", + "po trze", + "wokul ski", + "u wa", + "u mie", + "jedna k", + "k ra", + "wró ci", + "czło wie", + "czy ć", + "by ła", + "że li", + "m ę", + "c ę", + "z robi", + "mog ę", + "pro wa", + "r em", + "nie ch", + "cz nie", + "k ro", + "t ą", + "ch ci", + "b ro", + "dzie ć", + "sz ą", + "pa d", + "t rz", + "t ru", + "je m", + "a ni", + "t ów", + "a r", + "d ru", + "ta j", + "rze kł", + "sa m", + "st e", + "nie go", + "ta kie", + "w ała", + "to wa", + "ka pła", + "wi dzi", + "po dob", + "dz ę", + "t ał", + "stę p", + "b ą", + "po ko", + "w em", + "g ę", + "a by", + "g e", + "al bo", + "s pra", + "z no", + "de n", + "s mo", + "je sz", + "k się", + "jest eś", + "po z", + "ni gdy", + "k sią", + "c óż", + "w s", + "po w", + "t ka", + "ś wie", + "sz ka", + "sa mo", + "s ł", + "rz ę", + "na le", + "chce sz", + "ni k", + "p ę", + "chy ba", + "cią g", + "ją cy", + "wo j", + "na sze", + "mnie j", + "wię cej", + "z wy", + "o sta", + "f e", + "wa ż", + "h o", + "se r", + "śmie r", + "wie r", + "dz ą", + "za ś", + "gdy by", + "ja ki", + "wo l", + "wi n", + "d ą", + "ści a", + "roz ma", + "wa l", + "pa nie", + "sta r", + "ka z", + "je żeli", + "d em", + "w ra", + "ko ń", + "sie bie", + "zno wu", + "p ró", + "cz em", + "st wa", + "i sto", + "pó ł", + "d ał", + "ko bie", + "ała m", + "wy ch", + "ce sa", + "ni ch", + "za wsze", + "dzi ć", + "te ż", + "le pie", + "pro szę", + "k re", + "t wa", + "o t", + "ł ą", + "ch u", + "c ą", + "p rz", + "ł e", + "sze dł", + "od powie", + "my śli", + "ś wią", + "e n", + "e r", + "d e", + "a n", + "e t", + "i j", + "i n", + "e l", + "a a", + "s t", + "o r", + "g e", + "i s", + "a t", + "i e", + "c h", + "o n", + "e en", + "h et", + "i t", + "v er", + "aa r", + "a l", + "o or", + "g en", + "v an", + "o p", + "d en", + "h e", + "o m", + "t e", + "w e", + "i k", + "r e", + "z e", + "ij n", + "d at", + "b e", + "d er", + "in g", + "o e", + "ij k", + "a an", + "ch t", + "v oor", + "l e", + "i et", + "r o", + "m o", + "k en", + "z ijn", + "m en", + "i g", + "j e", + "n iet", + "a r", + "o o", + "i d", + "u n", + "i l", + "s ch", + "mo et", + "st e", + "u r", + "o l", + "he b", + "u it", + "g el", + "w ij", + "a s", + "m e", + "t en", + "w or", + "o u", + "v en", + "l en", + "aa t", + "d it", + "m et", + "r a", + "b en", + "s p", + "o ver", + "d ie", + "n o", + "w er", + "l ijk", + "f t", + "s l", + "an d", + "v e", + "t er", + "i er", + "i en", + "t o", + "d aar", + "g r", + "b el", + "de ze", + "d u", + "a g", + "k an", + "wor den", + "in gen", + "moet en", + "n en", + "on der", + "heb ben", + "r u", + "oo k", + "s en", + "c t", + "k t", + "no g", + "aa l", + "w as", + "u l", + "e er", + "b ij", + "m ijn", + "p ro", + "v ol", + "d o", + "k om", + "at ie", + "e ft", + "k el", + "al s", + "r ij", + "he id", + "a f", + "st el", + "m aar", + "a p", + "we e", + "a d", + "he eft", + "w aar", + "i cht", + "d an", + "er en", + "n e", + "w el", + "w at", + "w il", + "a cht", + "aa g", + "ge b", + "c on", + "z o", + "k e", + "b et", + "h ij", + "d ig", + "k un", + "u w", + "d t", + "d oor", + "t ij", + "a m", + "an g", + "on d", + "er s", + "is ch", + "ge en", + "i ge", + "ge v", + "ve el", + "n u", + "m a", + "on s", + "o f", + "b l", + "n aar", + "g ro", + "p l", + "an der", + "at en", + "kun nen", + "e cht", + "h ier", + "g oe", + "an t", + "u s", + "t wee", + "on t", + "de lijk", + "el e", + "u ur", + "al le", + "t oe", + "me er", + "i st", + "n a", + "n ie", + "on ze", + "l o", + "i m", + "p en", + "h ad", + "tij d", + "h oe", + "to t", + "z ou", + "a k", + "aa k", + "a men", + "d r", + "w oor", + "s e", + "wor dt", + "o t", + "gel ijk", + "g aan", + "i c", + "g er", + "k er", + "el d", + "e m", + "h ou", + "de l", + "z en", + "z el", + "te gen", + "b o", + "kom en", + "c om", + "i gen", + "e it", + "wer k", + "goe d", + "z al", + "z ij", + "sl ag", + "e s", + "z ien", + "a st", + "echt er", + "it ie", + "t ie", + "el ijk", + "m is", + "isch e", + "bel an", + "h aar", + "i ch", + "b er", + "h an", + "v r", + "al e", + "c i", + "gr ijk", + "in d", + "do en", + "l and", + "belan grijk", + "p un", + "op en", + "ct ie", + "zel f", + "m ij", + "it eit", + "ste m", + "me e", + "ar en", + "al l", + "b r", + "re cht", + "d ien", + "h u", + "g aat", + "pro b", + "m oe", + "p er", + "a u", + "ul len", + "z ich", + "daar om", + "or m", + "k l", + "v o", + "en t", + "st aat", + "z it", + "du i", + "n at", + "du s", + "d s", + "ver slag", + "kel ijk", + "prob le", + "w et", + "ge m", + "c r", + "i on", + "p r", + "sch ap", + "g d", + "h un", + "z a", + "er d", + "z et", + "st aan", + "st r", + "m aal", + "in der", + "e id", + "st en", + "p ar", + "k ken", + "ge d", + "z ullen", + "re s", + "men sen", + "j aar", + "re gel", + "ie der", + "vol gen", + "ge ven", + "e ven", + "l u", + "bl ij", + "i ë", + "k o", + "u we", + "m an", + "ma ken", + "l ie", + "g a", + "oe k", + "nie uwe", + "b aar", + "h o", + "h er", + "in ter", + "ander e", + "ru ik", + "s u", + "a gen", + "or t", + "m er", + "ou w", + "st er", + "wil len", + "aa kt", + "h oo", + "an den", + "f f", + "l ig", + "t re", + "s amen", + "ze er", + "dui delijk", + "ant woor", + "he el", + "men t", + "pun t", + "hou den", + "we g", + "vr aag", + "gel e", + "een s", + "be sch", + "om en", + "er g", + "do el", + "d ag", + "sp e", + "ur en", + "ing s", + "or en", + "l ang", + "de len", + "m ar", + "ste un", + "in nen", + "p ol", + "o on", + "i de", + "s n", + "s ie", + "r icht", + "z onder", + "no dig", + "all een", + "m id", + "ra gen", + "iet s", + "ver sch", + "geb ruik", + "st u", + "ro uw", + "stel len", + "be g", + "men ten", + "v in", + "eer ste", + "l aat", + "gro ot", + "oo d", + "to ch", + "l aten", + "aar d", + "s le", + "de el", + "st and", + "pl aat", + "re e", + "bet re", + "d i", + "l id", + "uit en", + "ra cht", + "bel eid", + "g et", + "ar t", + "st ie", + "st aten", + "g gen", + "re ken", + "e in", + "al en", + "m ing", + "mo gelijk", + "gro te", + "al tijd", + "z or", + "en kel", + "w ik", + "pol itie", + "e igen", + "el k", + "han del", + "g t", + "k we", + "m aat", + "el en", + "i p", + "v rij", + "s om", + "je s", + "aa m", + "hu is", + "v al", + "we er", + "lid staten", + "k ing", + "k le", + "be d", + "gev al", + "stel l", + "a i", + "wik kel", + "kwe stie", + "t al", + "ste e", + "a b", + "h el", + "kom st", + "p as", + "s s", + "it u", + "i den", + "eer d", + "m in", + "c e", + "p o", + "twee de", + "proble em", + "w aren", + "us sen", + "sn el", + "t ig", + "ge w", + "j u", + "ul t", + "ne men", + "com mis", + "versch il", + "k on", + "z oek", + "k rij", + "gr aag", + "den k", + "l anden", + "re den", + "be sl", + "oe g", + "bet er", + "he den", + "m ag", + "p e", + "bo ven", + "a c", + "con t", + "f d", + "h ele", + "k r", + "v ier", + "w in", + "ge z", + "k w", + "m il", + "v or", + "he m", + "ra m", + "aa s", + "ont wikkel", + "dr ie", + "v aak", + "plaat s", + "l a", + "g ang", + "ij f", + "f in", + "nat uur", + "t ussen", + "u g", + "in e", + "d a", + "b at", + "kom t", + "w acht", + "aa d", + "u t", + "é n", + "acht er", + "geb ie", + "ver k", + "lig t", + "c es", + "nie uw", + "van d", + "s t", + "n í", + "j e", + "p o", + "c h", + "r o", + "n a", + "s e", + "t o", + "n e", + "l e", + "k o", + "l a", + "d o", + "r a", + "n o", + "t e", + "h o", + "n ě", + "v a", + "l i", + "l o", + "ř e", + "c e", + "d e", + "v e", + "b y", + "n i", + "s k", + "t a", + "n á", + "z a", + "p ro", + "v o", + "v ě", + "m e", + "v á", + "s o", + "k a", + "r á", + "v y", + "z e", + "m i", + "p a", + "t i", + "st a", + "m ě", + "n é", + "ř i", + "ř í", + "m o", + "ž e", + "m a", + "j í", + "v ý", + "j i", + "d ě", + "r e", + "d a", + "k u", + "j a", + "c i", + "r u", + "č e", + "o b", + "t ě", + "m u", + "k y", + "d i", + "š e", + "k é", + "š í", + "t u", + "v i", + "p ře", + "v í", + "s i", + "n ý", + "o d", + "so u", + "v é", + "n y", + "r i", + "d y", + "b u", + "b o", + "t y", + "l á", + "l u", + "n u", + "ž i", + "m á", + "st i", + "c í", + "z á", + "p ra", + "sk é", + "m í", + "c o", + "d u", + "d á", + "by l", + "st o", + "s a", + "t í", + "je d", + "p ří", + "p ři", + "t é", + "s í", + "č i", + "v ní", + "č a", + "d í", + "z i", + "st u", + "p e", + "b a", + "d ní", + "ro z", + "va l", + "l í", + "s po", + "k á", + "b e", + "p i", + "no u", + "ta k", + "st e", + "r y", + "l é", + "vě t", + "se m", + "p ě", + "ko n", + "ne j", + "l y", + "ko u", + "ý ch", + "b ě", + "p r", + "f i", + "p rá", + "a le", + "ja ko", + "po d", + "ž í", + "z í", + "j sou", + "j sem", + "ch o", + "l ní", + "c ké", + "t á", + "m y", + "a k", + "h u", + "va t", + "pře d", + "h la", + "k e", + "st á", + "č í", + "š i", + "s le", + "k la", + "š tě", + "lo u", + "m ů", + "z na", + "ch á", + "o r", + "p ů", + "h a", + "b i", + "ta ké", + "d ů", + "no st", + "t ře", + "te r", + "p u", + "i n", + "v r", + "ve l", + "sk u", + "v še", + "t ní", + "do b", + "by la", + "č ní", + "ja k", + "v u", + "je ho", + "b ý", + "vá ní", + "ný ch", + "po u", + "te n", + "t ři", + "v z", + "st ře", + "d va", + "h le", + "č á", + "no sti", + "c k", + "v š", + "vo u", + "s u", + "h e", + "h ra", + "je n", + "s y", + "da l", + "po z", + "s lo", + "te l", + "d ru", + "de n", + "vš ak", + "g i", + "k dy", + "by lo", + "bu de", + "st ra", + "j ší", + "m é", + "me n", + "vý ch", + "ní m", + "s m", + "ko li", + "r ů", + "t ra", + "mů že", + "ne ní", + "ho d", + "b í", + "do u", + "sk a", + "t ý", + "st ě", + "u je", + "s á", + "pě t", + "ne s", + "k rá", + "to m", + "st ví", + "v ně", + "se d", + "s vé", + "p í", + "z o", + "mu sí", + "u ž", + "tí m", + "jí cí", + "jed no", + "t r", + "ča s", + "e v", + "č ty", + "sk ý", + "ni c", + "ev ro", + "to ho", + "h y", + "k ter", + "r ní", + "st í", + "s vě", + "pa k", + "vše ch", + "k ů", + "n g", + "á d", + "chá zí", + "a ni", + "a r", + "jed na", + "bý t", + "t ro", + "k ra", + "pr vní", + "m no", + "ské ho", + "p á", + "p la", + "le m", + "ne bo", + "ke m", + "st ro", + "s la", + "né ho", + "z de", + "dal ší", + "ř a", + "čty ři", + "h rá", + "dru h", + "l ně", + "v la", + "sk ých", + "š ko", + "pů so", + "pro to", + "v ů", + "sk á", + "ve n", + "še st", + "d ně", + "je ště", + "me zi", + "te k", + "s ko", + "ch a", + "ně koli", + "be z", + "g ra", + "ji ž", + "č ně", + "j á", + "s lu", + "z ná", + "ve r", + "sed m", + "k ro", + "ta m", + "a no", + "v lá", + "o sm", + "byl y", + "vá m", + "ck ý", + "te ch", + "dě ji", + "vel mi", + "le ži", + "va la", + "l ý", + "t vo", + "spo le", + "ch u", + "stu p", + "mo ž", + "evro p", + "g e", + "sta l", + "j de", + "ch y", + "ro di", + "je jí", + "po li", + "de vět", + "s me", + "a ž", + "té to", + "re m", + "d é", + "f or", + "u ni", + "f o", + "ten to", + "a u", + "ka ž", + "nu la", + "na d", + "by ch", + "mo c", + "sto u", + "e x", + "le n", + "k do", + "z d", + "pra co", + "to mu", + "ný m", + "ži vo", + "ze m", + "f e", + "f u", + "ná sle", + "j o", + "sk y", + "ji ch", + "h á", + "mě l", + "dě la", + "j sme", + "p re", + "ni ce", + "ste j", + "ne m", + "st ní", + "he m", + "ná ro", + "z u", + "b li", + "ni t", + "pa r", + "a l", + "poz ději", + "ta ko", + "n ce", + "če r", + "ší m", + "ně co", + "vá l", + "ře j", + "krá t", + "á lní", + "u r", + ". .", + "a si", + "kter é", + "sta v", + "ma jí", + "my s", + "do bě", + "s ně", + "ce n", + "z y", + "z ku", + "t ů", + "ch od", + "s pě", + "je jich", + "sou čas", + "d r", + "va li", + "ri e", + "k te", + "pr ů", + "ze ní", + "pa t", + "a n", + "po tře", + "de m", + "d nes", + "ze mí", + "sa mo", + "zna m", + "b ra", + "má m", + "te dy", + "g o", + "hla vní", + "pou ží", + "b ní", + "ve de", + "le p", + "je k", + "pra v", + "poli ti", + "d ne", + "je m", + "le t", + "če ní", + "pro b", + "ne ž", + "dě l", + "fi l", + "č o", + "cí ch", + "st é", + "d lou", + "h i", + "a by", + "to u", + "několi k", + "d la", + "vy u", + "vi t", + "ho u", + "ck ých", + "no vé", + "či n", + "st y", + "dě lá", + "k ý", + "ob la", + "pod le", + "ra n", + "dů leži", + "ta to", + "po ku", + "ko ne", + "d ý", + "d vě", + "ž ád", + "nou t", + "t ku", + "t vr", + "cké ho", + "ro v", + "r é", + "te le", + "p sa", + "s vět", + "ti vní", + "do sta", + "te m", + "še l", + "druh é", + "s kou", + "ž o", + "jed ná", + "vý znam", + "prob lé", + "pu bli", + "vá n", + "od po", + "pod po", + "d le", + "ja ké", + "še ní", + "ví m", + "bě hem", + "na chází", + "s lou", + "pou ze", + "o tá", + "p lo", + "to vé", + "vět ši", + "ko mi", + "va jí", + "ty to", + "zá pa", + "z mě", + "mo h", + "ví ce", + "spole č", + "au to", + "pro ti", + "st ru", + "dě t", + "chá ze", + "že l", + "с т", + "е н", + "н о", + "н а", + "п р", + "т о", + "п о", + "р а", + "г о", + "к о", + "н е", + "в о", + "в а", + "е т", + "е р", + "н и", + "е л", + "и т", + "н ы", + "з а", + "р о", + "ен и", + "к а", + "л и", + "е м", + "д а", + "о б", + "л а", + "д о", + "с я", + "т ь", + "о т", + "л о", + "л ь", + "е д", + "с о", + "м и", + "р е", + "м о", + "ц и", + "пр о", + "т а", + "э то", + "к и", + "р у", + "пр и", + "т и", + "с е", + "ст а", + "в ы", + "м ы", + "в и", + "б ы", + "м а", + "е с", + "л я", + "ст и", + "л е", + "ч то", + "м е", + "р и", + "ч а", + "о д", + "е й", + "ел ь", + "ени я", + "г а", + "н у", + "с и", + "п а", + "ра з", + "б о", + "ст о", + "с у", + "с а", + "д у", + "е го", + "е ст", + "и н", + "ит ь", + "и з", + "ж е", + "м у", + "п ер", + "по д", + "ени е", + "с ь", + "к у", + "пр ед", + "но го", + "ны х", + "в ер", + "т е", + "но й", + "ци и", + "д е", + "р ы", + "д ел", + "л ю", + "в е", + "о н", + "м ен", + "г и", + "н я", + "б у", + "пр а", + "в се", + "ет ся", + "ст ь", + "ж а", + "до л", + "ж и", + "б е", + "ко н", + "с л", + "ш и", + "д и", + "ст в", + "с ко", + "ны е", + "ч и", + "ю т", + "д ер", + "ст ра", + "т ы", + "х од", + "щ и", + "з о", + "з на", + "но сти", + "ч ес", + "в ля", + "ва ть", + "о р", + "по л", + "в ет", + "та к", + "ш а", + "т у", + "с во", + "пр е", + "о на", + "ит ель", + "ны й", + "с ло", + "ка к", + "в л", + "но сть", + "х о", + "мо ж", + "п е", + "д ля", + "ни я", + "но е", + "ра с", + "дол ж", + "да р", + "т ель", + "с ка", + "п у", + "ст во", + "ко то", + "ра б", + "е е", + "ро д", + "э ти", + "с об", + "о ру", + "ж ен", + "ны м", + "ит и", + "ни е", + "ко м", + "д ет", + "ст у", + "г у", + "п и", + "ме ж", + "ени ю", + "т ер", + "раб от", + "во з", + "ци я", + "ко й", + "щ ест", + "г ра", + "з и", + "р я", + "меж ду", + "ст ва", + "в с", + "ел о", + "ш е", + "м ер", + "б а", + "з ы", + "л у", + "а ль", + "д ей", + "г ла", + "на род", + "к ти", + "пред ста", + "л ся", + "я вля", + "с ки", + "но в", + "ед ин", + "ро в", + "и с", + "ни ма", + "р ем", + "ход и", + "так же", + "д ру", + "а ть", + "сл ед", + "го во", + "на я", + "ю щи", + "ен ь", + "кото ры", + "х от", + "в у", + "и х", + "ем у", + "ч ит", + "ва ж", + "ор га", + "чес ки", + "щ е", + "к е", + "х а", + "по с", + "то м", + "бо ль", + "м не", + "па с", + "об ъ", + "пра в", + "кон ф", + "сл у", + "под дер", + "ст ви", + "на ш", + "ль ко", + "сто я", + "ну ю", + "л ем", + "ен ных", + "к ра", + "д ы", + "между народ", + "г да", + "не об", + "го су", + "ств у", + "ени и", + "госу дар", + "к то", + "и м", + "ч ест", + "р ет", + "во про", + "л ен", + "ел и", + "ро ва", + "ци й", + "на м", + "это й", + "ж ения", + "необ ходи", + "мен я", + "бы ло", + "си ли", + "ф и", + "в я", + "ш ь", + "это го", + "о ни", + "орга ни", + "бе зо", + "пр об", + "и ме", + "ре ш", + "б и", + "безо пас", + "ют ся", + "о ста", + "ен но", + "го д", + "ел а", + "предста в", + "ть ся", + "сло во", + "органи за", + "долж ны", + "это м", + "б ла", + "ч е", + "ч у", + "бла го", + "это му", + "в рем", + "с пе", + "но м", + "ени й", + "с по", + "на с", + "не т", + "з у", + "в ед", + "е ще", + "ска за", + "се й", + "ер ен", + "да н", + "са м", + "ел я", + "ра н", + "зы ва", + "явля ется", + "бу дет", + "кти в", + "т ре", + "дел е", + "м от", + "конф ерен", + "ла сь", + "ча с", + "сто ро", + "ко го", + "е з", + "не й", + "о с", + "ли сь", + "раз ору", + "пер е", + "с си", + "ны ми", + "про ц", + "го ло", + "ч ело", + "бо ле", + "чело ве", + "с ер", + "п л", + "ч ет", + "стра н", + "п я", + "бы л", + "к ла", + "то в", + "ж д", + "дел а", + "е ра", + "у же", + "со вет", + "г ен", + "безопас ности", + "ц а", + "се да", + "по з", + "от вет", + "проб лем", + "на ко", + "т ем", + "до ста", + "п ы", + "щ а", + "во й", + "су щест", + "необходи мо", + "бы ть", + "мож ет", + "д ем", + "что бы", + "е к", + "ч ер", + "у сили", + "ре с", + "ру д", + "един енных", + "д об", + "до сти", + "ств ен", + "я дер", + "год ня", + "ка за", + "се годня", + "сей час", + "то лько", + "во д", + "ес ь", + "м ного", + "бу ду", + "е в", + "ест ь", + "т ри", + "об щест", + ". .", + "я вл", + "вы сту", + "р ед", + "с чит", + "с ит", + "деле га", + "ло ж", + "это т", + "ф ор", + "к лю", + "воз мож", + "ва ния", + "б ли", + "и ли", + "в з", + "на ций", + "ско го", + "при ня", + "п ла", + "о ч", + "ить ся", + "ст е", + "на ши", + "которы е", + "а р", + "име ет", + "с от", + "зна ч", + "пер ь", + "след у", + "ен ы", + "та ки", + "объ единенных", + "ст ро", + "те перь", + "б ле", + "благо дар", + "раз в", + "а н", + "жи ва", + "оч ень", + "я т", + "бе з", + "об ес", + "г ро", + "ло сь", + "с ы", + "организа ции", + "ч лен", + "то го", + "она ль", + "ж да", + "все х", + "с вя", + "боле е", + "со в", + "ко гда", + "во т", + "к ре", + "к ры", + "по этому", + "во ль", + "о й", + "ген ера", + "ч ем", + "л ы", + "пол ити", + "в ен", + "конферен ции", + "проц ес", + "б я", + "ит е", + "от но", + "разв ити", + "а ф", + "ю щ", + "в но", + "ми р", + "ни и", + "ка я", + "а с", + "итель но", + "в то", + "ени ем", + "генера ль", + "пр от", + "вс ем", + "сам бле", + "ас самбле", + "о м", + "з д", + "с мот", + "ре ги", + "ч его", + "од нако", + "усили я", + "дей стви", + "ч но", + "у ча", + "об раз", + "во с", + "э та", + "пер его", + "гово р", + "ва м", + "мо ло", + "врем я", + "д ь", + "хот ел", + "г ру", + "за явл", + "пре доста", + "по ль", + "не е", + "ре зо", + "перего во", + "резо лю", + "к рет", + "поддер ж", + "обес пе", + "не го", + "представ ит", + "на де", + "к ри", + "ч ь", + "про ек", + "л ет", + "дру ги", + "ا ل", + "َ ا", + "و َ", + "ّ َ", + "ِ ي", + "أ َ", + "ل َ", + "ن َ", + "ال ْ", + "ه ُ", + "ُ و", + "م ا", + "ن ْ", + "م ن", + "ع َ", + "ن ا", + "ل ا", + "م َ", + "ت َ", + "ف َ", + "أ ن", + "ل ي", + "م ِ", + "ا ن", + "ف ي", + "ر َ", + "ي َ", + "ه ِ", + "م ْ", + "ق َ", + "ب ِ", + "ل ى", + "ي ن", + "إ ِ", + "ل ِ", + "و ا", + "ك َ", + "ه ا", + "ً ا", + "م ُ", + "و ن", + "ال م", + "ب َ", + "ي ا", + "ذ ا", + "س ا", + "ال ل", + "م ي", + "ي ْ", + "ر ا", + "ر ي", + "ل ك", + "م َا", + "ن َّ", + "ل م", + "إ ن", + "س ت", + "و م", + "ّ َا", + "ل َا", + "ه م", + "ّ ِ", + "ك ُ", + "ك ان", + "س َ", + "ب ا", + "د ي", + "ح َ", + "ع ْ", + "ب ي", + "ال أ", + "و ل", + "ف ِي", + "ر ِ", + "د ا", + "مِ نْ", + "ُو نَ", + "و ْ", + "ه َا", + "ّ ُ", + "ال س", + "ال َ", + "ن ي", + "ل ْ", + "ت ُ", + "ه ل", + "ر ة", + "د َ", + "س ْ", + "ت ِ", + "ن َا", + "ر ْ", + "الل َّ", + "سا مي", + "ك ن", + "ك ل", + "ه َ", + "عَ لَ", + "ع لى", + "م ع", + "إ لى", + "ق د", + "ال ر", + "ُو ا", + "ي ر", + "ع ن", + "ي ُ", + "ن ِ", + "ب ْ", + "ال ح", + "هُ مْ", + "ق ا", + "ذ ه", + "ال ت", + "ِي نَ", + "ج َ", + "ه ذا", + "ع د", + "ال ع", + "د ْ", + "قَ الَ", + "ر ُ", + "ي م", + "ي ة", + "ن ُ", + "خ َ", + "ر ب", + "ال ك", + "و َا", + "أ نا", + "ة ِ", + "ال ن", + "ح د", + "ع ِ", + "ت ا", + "ه و", + "ف ا", + "ع ا", + "ال ش", + "ل ُ", + "ي ت", + "ذ َا", + "ي ع", + "ال ذ", + "ح ْ", + "ال ص", + "إِ نَّ", + "ج ا", + "ع لي", + "ك َا", + "ب ُ", + "ت ع", + "و ق", + "م ل", + "ل َّ", + "ي د", + "أ خ", + "ر ف", + "ت ي", + "ال ِ", + "ّ ا", + "ذ لك", + "أَ نْ", + "س ِ", + "ت وم", + "م ر", + "مَ نْ", + "ب ل", + "ال ق", + "الل ه", + "ِي َ", + "ك م", + "ذ َ", + "ع ل", + "ح ب", + "س ي", + "ع ُ", + "ال ج", + "ال د", + "ش َ", + "ت ك", + "ف ْ", + "ص َ", + "ل ل", + "د ِ", + "ب ر", + "ف ِ", + "ت ه", + "أ ع", + "ت ْ", + "ق ْ", + "الْ أَ", + "ئ ِ", + "عَ نْ", + "و ر", + "ح ا", + "ال َّ", + "م ت", + "ف ر", + "د ُ", + "ه نا", + "وَ أَ", + "ت ب", + "ة ُ", + "أ ي", + "س ب", + "ري د", + "و ج", + "كُ مْ", + "ح ِ", + "ك ْ", + "د ر", + "َا ء", + "ه ذه", + "ال ط", + "الْ مُ", + "د ة", + "ق ل", + "غ َ", + "ي وم", + "الَّ ذ", + "ك ر", + "ت ر", + "ك ِ", + "ك ي", + "عَلَ ى", + "رَ ب", + "ع ة", + "ق ُ", + "ج ْ", + "ف ض", + "ل ة", + "ه ْ", + "ر َا", + "وَ لَ", + "الْ مَ", + "أَ نَّ", + "ي َا", + "أ ُ", + "ش ي", + "اللَّ هُ", + "لَ ى", + "ق ِ", + "أ ت", + "عَلَ يْ", + "اللَّ هِ", + "ال ب", + "ض َ", + "ة ً", + "ق ي", + "ا ر", + "ب د", + "خ ْ", + "سْ تَ", + "ط َ", + "قَ دْ", + "ذه ب", + "أ م", + "ما ذا", + "وَ إِ", + "ة ٌ", + "و نَ", + "لي لى", + "و لا", + "ح ُ", + "ه ي", + "ص ل", + "ال خ", + "و د", + "لي س", + "ل دي", + "ق ال", + "كَا نَ", + "م َّ", + "ح ي", + "ت م", + "ل ن", + "وَ لَا", + "ب ع", + "يم كن", + "س ُ", + "ة َ", + "ح ت", + "ر ًا", + "ك ا", + "ش ا", + "هِ مْ", + "لَ هُ", + "ز َ", + "دا ً", + "م س", + "ك ث", + "الْ عَ", + "ج ِ", + "ص ْ", + "ف َا", + "ل ه", + "و ي", + "ع َا", + "هُ وَ", + "ب ِي", + "ب َا", + "أ س", + "ث َ", + "ل ِي", + "ر ض", + "الر َّ", + "لِ كَ", + "ت َّ", + "ف ُ", + "ق ة", + "ف عل", + "مِ ن", + "ال آ", + "ث ُ", + "س م", + "م َّا", + "بِ هِ", + "ت ق", + "خ ر", + "ل قد", + "خ ل", + "ش ر", + "أن ت", + "ل َّا", + "س ن", + "الس َّ", + "الذ ي", + "س َا", + "و ما", + "ز ل", + "و ب", + "أ ْ", + "إ ذا", + "ر ِي", + "ح ة", + "ن ِي", + "الْ حَ", + "وَ قَالَ", + "ب ه", + "ة ٍ", + "س أ", + "ر ٌ", + "ب ال", + "م ة", + "ش ْ", + "و ت", + "عن د", + "ف س", + "بَ عْ", + "ه ر", + "ق ط", + "أ ح", + "إن ه", + "و ع", + "ف ت", + "غ ا", + "هنا ك", + "ب ت", + "مِ نَ", + "س ر", + "ذَ لِكَ", + "ر س", + "حد ث", + "غ ْ", + "ّ ِي", + "ال إ", + "وَ يَ", + "ج ل", + "ا ست", + "ق ِي", + "ع ب", + "و س", + "ي ش", + "الَّذ ِينَ", + "تا ب", + "د ِي", + "ج ب", + "ك ون", + "ب ن", + "ال ث", + "لَ يْ", + "ب عد", + "وَ الْ", + "فَ أَ", + "ع م", + "هُ م", + "ت ن", + "ذ ْ", + "أ ص", + "أ ين", + "رَب ِّ", + "الذ ين", + "إِ ن", + "ب ين", + "ج ُ", + "عَلَيْ هِ", + "ح َا", + "ل و", + "ست ط", + "ظ ر", + "لَ مْ", + "ء ِ", + "كُ ل", + "ط ل", + "ت َا", + "ض ُ", + "كن ت", + "ل ًا", + "م ٌ", + "ق بل", + "ـ ـ", + "ذ ِ", + "قَ وْ", + "ص ِ", + "م ًا", + "كان ت", + "ص ا", + "ي ق", + "ال ف", + "ال نا", + "م ٍ", + "إِ نْ", + "ال نَّ", + "ج د", + "وَ مَا", + "ت ت", + "ب ح", + "م كان", + "كي ف", + "ّ ة", + "ال ا", + "ج َا", + "أ و", + "سا عد", + "ض ِ", + "إ لا", + "را ً", + "ق َا", + "ر أ", + "ع ت", + "أ حد", + "ه د", + "ض ا", + "ط ر", + "أ ق", + "ما ء", + "د َّ", + "ال با", + "م ُو", + "أَ وْ", + "ط ا", + "ق ُو", + "خ ِ", + "ت ل", + "ستط يع", + "د َا", + "الن َّا", + "إ لَى", + "وَ تَ", + "هَ ذَا", + "ب ة", + "علي ك", + "ج ر", + "ال من", + "ز ا", + "ر ٍ", + "د ع", + "ّ ًا", + "س ة", + "ثُ مَّ", + "شي ء", + "ال غ", + "ت ح", + "ر ُونَ", + "ال يوم", + "م ِي", + "ن ُوا", + "أ ر", + "تُ مْ", + "ع ر", + "ي ف", + "أ ب", + "د ًا", + "ص َا", + "الت َّ", + "أ ريد", + "ال ز", + "يَ وْ", + "إ لي", + "ج ي", + "يَ عْ", + "فض ل", + "ال إن", + "أن ه", + "n g", + "i 4", + "a n", + "s h", + "z h", + "i 2", + "ng 1", + "u 4", + "i 1", + "ng 2", + "d e", + "j i", + "a o", + "x i", + "u 3", + "de 5", + "e 4", + "i 3", + "ng 4", + "an 4", + "e n", + "u o", + "sh i4", + "an 2", + "u 2", + "c h", + "u 1", + "ng 3", + "a 1", + "an 1", + "e 2", + "a 4", + "e i4", + "o ng1", + "a i4", + "ao 4", + "h u", + "a ng1", + "l i", + "y o", + "an 3", + "w ei4", + "uo 2", + "n 1", + "en 2", + "ao 3", + "e 1", + "y u", + "q i", + "e ng2", + "zh o", + "a ng3", + "a ng4", + "a ng2", + "uo 4", + "m i", + "g e4", + "y i1", + "g uo2", + "e r", + "b i", + "a 3", + "h e2", + "e 3", + "y i2", + "d i4", + "zh ong1", + "b u4", + "g u", + "a i2", + "n 2", + "z ai4", + "sh i2", + "e ng1", + "r en2", + "o ng2", + "xi an4", + "y i", + "x u", + "n 4", + "l i4", + "en 4", + "y u2", + "e i2", + "yi2 ge4", + "o u4", + "e i3", + "d i", + "u i4", + "a 2", + "yo u3", + "ao 1", + "d a4", + "ch eng2", + "en 1", + "e ng4", + "y i4", + "s i1", + "zh i4", + "ji a1", + "yu an2", + "n i", + "t a1", + "de5 yi2ge4", + "k e1", + "sh u3", + "x i1", + "j i2", + "ao 2", + "t i", + "o u3", + "o ng4", + "xi a4", + "a i1", + "g ong1", + "zh i1", + "en 3", + "w ei2", + "j u", + "xu e2", + "q u1", + "zho u1", + "er 3", + "mi ng2", + "zho ng3", + "l i3", + "w u4", + "y i3", + "uo 1", + "e 5", + "j i4", + "xi ng2", + "ji an4", + "hu a4", + "y u3", + "uo 3", + "j i1", + "a i3", + "z uo4", + "h ou4", + "hu i4", + "e i1", + "ni an2", + "q i2", + "p i", + "d ao4", + "sh eng1", + "de 2", + "d ai4", + "u an2", + "zh e4", + "zh eng4", + "b en3", + "sh ang4", + "zh u3", + "b ei4", + "y e4", + "ch u1", + "zh an4", + "l e5", + "l ai2", + "sh i3", + "n an2", + "r en4", + "yo u2", + "k e4", + "b a1", + "f u4", + "d ui4", + "y a4", + "m ei3", + "z i4", + "xi n1", + "ji ng1", + "zh u", + "n 3", + "yo ng4", + "m u4", + "ji ao4", + "y e3", + "ji n4", + "bi an4", + "l u4", + "q i1", + "sh e4", + "xi ang1", + "o ng3", + "sh u4", + "d ong4", + "s uo3", + "gu an1", + "s an1", + "b o", + "t e4", + "d uo1", + "f u2", + "mi n2", + "l a1", + "zh i2", + "zh en4", + "o u1", + "w u3", + "m a3", + "i 5", + "z i5", + "j u4", + "er 4", + "y ao4", + "xia4 de5yi2ge4", + "s i4", + "t u2", + "sh an1", + "z ui4", + "ch u", + "yi n1", + "er 2", + "t ong2", + "d ong1", + "y u4", + "y an2", + "qi an2", + "shu3 xia4de5yi2ge4", + "ju n1", + "k e3", + "w en2", + "f a3", + "l uo2", + "zh u4", + "x i4", + "k ou3", + "b ei3", + "ji an1", + "f a1", + "di an4", + "ji ang1", + "wei4 yu2", + "xi ang4", + "zh i3", + "e ng3", + "f ang1", + "l an2", + "sh u", + "r i4", + "li an2", + "sh ou3", + "m o", + "qi u2", + "ji n1", + "h uo4", + "shu3xia4de5yi2ge4 zhong3", + "f en1", + "n ei4", + "g ai1", + "mei3 guo2", + "u n2", + "g e2", + "b ao3", + "qi ng1", + "g ao1", + "t ai2", + "d u", + "xi ao3", + "ji e2", + "ti an1", + "ch ang2", + "q uan2", + "li e4", + "h ai3", + "f ei1", + "t i3", + "ju e2", + "o u2", + "c i3", + "z u2", + "n i2", + "bi ao3", + "zhong1 guo2", + "d u4", + "yu e4", + "xi ng4", + "sh eng4", + "ch e1", + "d an1", + "ji e1", + "li n2", + "pi ng2", + "f u3", + "g u3", + "ji e4", + "w o", + "v 3", + "sh eng3", + "n a4", + "yu an4", + "zh ang3", + "gu an3", + "d ao3", + "z u3", + "di ng4", + "di an3", + "c eng2", + "ren2 kou3", + "t ai4", + "t ong1", + "g uo4", + "n eng2", + "ch ang3", + "hu a2", + "li u2", + "yi ng1", + "xi ao4", + "c i4", + "bian4 hua4", + "li ang3", + "g ong4", + "zho ng4", + "de5 yi1", + "s e4", + "k ai1", + "w ang2", + "ji u4", + "sh i1", + "sh ou4", + "m ei2", + "k u", + "s u", + "f eng1", + "z e2", + "tu2 shi4", + "t i2", + "q i4", + "ji u3", + "sh en1", + "zh e3", + "ren2kou3 bian4hua4", + "ren2kou3bian4hua4 tu2shi4", + "di4 qu1", + "y ang2", + "m en", + "men 5", + "l ong2", + "bi ng4", + "ch an3", + "zh u1", + "w ei3", + "w ai4", + "xi ng1", + "bo 1", + "b i3", + "t ang2", + "hu a1", + "bo 2", + "shu i3", + "sh u1", + "d ou1", + "s ai4", + "ch ao2", + "b i4", + "li ng2", + "l ei4", + "da4 xue2", + "f en4", + "shu3 de5", + "m u3", + "ji ao1", + "d ang1", + "ch eng1", + "t ong3", + "n v3", + "q i3", + "y an3", + "mi an4", + "l uo4", + "ji ng4", + "g e1", + "r u4", + "d an4", + "ri4 ben3", + "p u3", + "yu n4", + "hu ang2", + "wo 3", + "l v", + "h ai2", + "shi4 yi1", + "xi e1", + "yi ng3", + "w u2", + "sh en2", + "w ang3", + "gu ang3", + "li u4", + "s u4", + "shi4 zhen4", + "c an1", + "c ao3", + "xi a2", + "k a3", + "d a2", + "h u4", + "b an4", + "d ang3", + "h u2", + "z ong3", + "de ng3", + "de5yi2ge4 shi4zhen4", + "ch uan2", + "mo 4", + "zh ang1", + "b an1", + "mo 2", + "ch a2", + "c e4", + "zhu3 yao4", + "t ou2", + "j u2", + "shi4 wei4yu2", + "s a4", + "u n1", + "ke3 yi3", + "d u1", + "h an4", + "li ang4", + "sh a1", + "ji a3", + "z i1", + "lv 4", + "f u1", + "xi an1", + "x u4", + "gu ang1", + "m eng2", + "b ao4", + "yo u4", + "r ong2", + "zhi1 yi1", + "w ei1", + "m ao2", + "guo2 jia1", + "c ong2", + "g ou4", + "ti e3", + "zh en1", + "d u2", + "bi an1", + "c i2", + "q u3", + "f an4", + "xi ang3", + "m en2", + "j u1", + "h ong2", + "z i3", + "ta1 men5", + "ji 3", + "z ong1", + "zhou1 de5yi2ge4shi4zhen4", + "t uan2", + "ji ng3", + "gong1 si1", + "xi e4", + "l i2", + "li4 shi3", + "b ao1", + "g ang3", + "gu i1", + "zh eng1", + "zhi2 wu4", + "ta1 de5", + "pi n3", + "zhu an1", + "ch ong2", + "shi3 yong4", + "w a3", + "sh uo1", + "chu an1", + "l ei2", + "w an1", + "h uo2", + "q u", + "s u1", + "z ao3", + "g ai3", + "q u4", + "g u4", + "l u", + "x i2", + "h ang2", + "yi ng4", + "c un1", + "g en1", + "yi ng2", + "ti ng2", + "cheng2 shi4", + "ji ang3", + "li ng3", + "l un2", + "bu4 fen4", + "de ng1", + "xu an3", + "dong4 wu4", + "de2 guo2", + "xi an3", + "f an3", + "zh e5", + "h an2", + "h ao4", + "m i4", + "r an2", + "qi n1", + "ti ao2", + "zh an3", + "h i", + "k a", + "n o", + "t e", + "s u", + "s hi", + "t a", + "t o", + "n a", + "w a", + "o u", + "r u", + "n i", + "k u", + "k i", + "g a", + "d e", + "k o", + "m a", + "r e", + "r a", + "m o", + "t su", + "w o", + "e n", + "r i", + "s a", + "d a", + "s e", + "j i", + "h a", + "c hi", + "k e", + "te ki", + "m i", + "y ou", + "s h", + "s o", + "y o", + "y a", + "na i", + "t te", + "a ru", + "b a", + "u u", + "t ta", + "ka i", + "ka n", + "shi te", + "m e", + "d o", + "mo no", + "se i", + "r o", + "ko to", + "ka ra", + "shi ta", + "b u", + "m u", + "c h", + "su ru", + "k ou", + "g o", + "ma su", + "ta i", + "f u", + "k en", + "i u", + "g en", + "wa re", + "shi n", + "z u", + "a i", + "o n", + "o ku", + "g i", + "d ou", + "n e", + "y uu", + "i ru", + "i te", + "ji ko", + "de su", + "j u", + "ra re", + "sh u", + "b e", + "sh ou", + "s ha", + "se kai", + "s ou", + "k you", + "ma shita", + "s en", + "na ra", + "sa n", + "ke i", + "i ta", + "a ri", + "i tsu", + "ko no", + "j ou", + "na ka", + "ch ou", + "so re", + "g u", + "na ru", + "ga ku", + "re ba", + "g e", + "h o", + "i n", + "hi to", + "sa i", + "na n", + "da i", + "tsu ku", + "shi ki", + "sa re", + "na ku", + "p p", + "bu n", + "ju n", + "so no", + "ka ku", + "z ai", + "b i", + "to u", + "wa ta", + "sh uu", + "i i", + "te i", + "ka re", + "y u", + "shi i", + "ma de", + "sh o", + "a n", + "ke reba", + "shi ka", + "i chi", + "ha n", + "de ki", + "ni n", + "ware ware", + "na kereba", + "o ite", + "h ou", + "ya ku", + "ra i", + "mu jun", + "l e", + "yo ku", + "bu tsu", + "o o", + "ko n", + "o mo", + "ga e", + "nara nai", + "ta chi", + "z en", + "ch uu", + "kan gae", + "ta ra", + "to ki", + "ko ro", + "mujun teki", + "z e", + "na ga", + "ji n", + "shi ma", + "te n", + "i ki", + "i ku", + "no u", + "i masu", + "r ou", + "h on", + "ka e", + "t to", + "ko re", + "ta n", + "ki ta", + "i s", + "da tta", + "ji tsu", + "ma e", + "i e", + "me i", + "da n", + "h e", + "to ku", + "dou itsu", + "ri tsu", + "k yuu", + "h you", + "rare ta", + "kei sei", + "k kan", + "rare ru", + "m ou", + "do ko", + "r you", + "da ke", + "naka tta", + "so ko", + "ta be", + "e r", + "ha na", + "c o", + "fu ku", + "p a", + "so n", + "ya su", + "ch o", + "wata ku", + "ya ma", + "z a", + "k yo", + "gen zai", + "b oku", + "a ta", + "j a", + "ka wa", + "ma sen", + "j uu", + "ro n", + "b o", + "na tte", + "wataku shi", + "yo tte", + "ma i", + "g ou", + "ha i", + "mo n", + "ba n", + "ji shin", + "c a", + "re te", + "n en", + "o ka", + "ka gaku", + "na tta", + "p o", + "ka ru", + "na ri", + "m en", + "ma ta", + "e i", + "ku ru", + "ga i", + "ka ri", + "sha kai", + "kou i", + "yo ri", + "se tsu", + "j o", + "re ru", + "to koro", + "ju tsu", + "i on", + "sa ku", + "tta i", + "c ha", + "nin gen", + "n u", + "c e", + "ta me", + "kan kyou", + "de n", + "o oku", + "i ma", + "wata shi", + "tsuku ru", + "su gi", + "b en", + "ji bun", + "shi tsu", + "ke ru", + "ki n", + "ki shi", + "shika shi", + "mo to", + "ma ri", + "i tte", + "de shita", + "n de", + "ari masu", + "te r", + "z ou", + "ko e", + "ze ttai", + "kkan teki", + "h en", + "re kishi", + "deki ru", + "tsu ka", + "l a", + "i tta", + "o i", + "ko butsu", + "mi ru", + "sh oku", + "shi masu", + "gi jutsu", + "g you", + "jou shiki", + "a tta", + "ho do", + "ko ko", + "tsuku rareta", + "z oku", + "hi tei", + "ko ku", + "rekishi teki", + "ke te", + "o ri", + "i mi", + "ka ko", + "naga ra", + "ka karu", + "shu tai", + "ha ji", + "ma n", + "ta ku", + "ra n", + "douitsu teki", + "z o", + "me te", + "re i", + "tsu u", + "sare te", + "gen jitsu", + "p e", + "s t", + "ba i", + "na wa", + "ji kan", + "wa ru", + "r t", + "a tsu", + "so ku", + "koui teki", + "a ra", + "u ma", + "a no", + "i de", + "ka ta", + "te tsu", + "ga wa", + "ke do", + "re ta", + "mi n", + "sa you", + "tte ru", + "to ri", + "p u", + "ki mi", + "b ou", + "mu ra", + "sare ru", + "ma chi", + "k ya", + "o sa", + "kon na", + "a ku", + "a l", + "sare ta", + "i pp", + "shi ku", + "u chi", + "hito tsu", + "ha tara", + "tachi ba", + "shi ro", + "ka tachi", + "to mo", + "e te", + "me ru", + "ni chi", + "da re", + "ka tta", + "e ru", + "su ki", + "a ge", + "oo ki", + "ma ru", + "mo ku", + "o ko", + "kangae rareru", + "o to", + "tan ni", + "ta da", + "tai teki", + "mo tte", + "ki nou", + "shi nai", + "k ki", + "u e", + "ta ri", + "l i", + "ra nai", + "k kou", + "mi rai", + "pp on", + "go to", + "hi n", + "hi tsu", + "te ru", + "mo chi", + "ka tsu", + "re n", + "n yuu", + "su i", + "zu ka", + "tsu ite", + "no mi", + "su gu", + "ku da", + "tetsu gaku", + "i ka", + "ron ri", + "o ki", + "ni ppon", + "p er", + "shi mashita", + "chi shiki", + "cho kkanteki", + "su ko", + "t ion", + "ku u", + "a na", + "a rou", + "ka tte", + "ku ri", + "i nai", + "hyou gen", + "i shiki", + "do ku", + "a tte", + "a tara", + "to n", + "wa ri", + "ka o", + "sei san", + "hana shi", + "s i", + "ka ke", + "na ji", + "su nawa", + "sunawa chi", + "u go", + "su u", + "ba ra", + "le v", + "hi ro", + "i wa", + "be tsu", + "yo i", + "se ru", + "shite ru", + "rare te", + "to shi", + "se ki", + "tai ritsu", + "wa kara", + "to kyo", + "k ka", + "k yoku", + "u n", + "i ro", + "mi te", + "sa ki", + "kan ji", + "mi ta", + "su be", + "r yoku", + "ma tta", + "kuda sai", + "omo i", + "ta no", + "ware ru", + "co m", + "hitsu you", + "ka shi", + "re nai", + "kan kei", + "a to", + "ga tte", + "o chi", + "mo tsu", + "in g", + "son zai", + "l l", + "o re", + "tai shite", + "a me", + "sei mei", + "ka no", + "gi ri", + "kangae ru", + "yu e", + "a sa", + "o naji", + "yo ru", + "ni ku", + "osa ka", + "suko shi", + "c k", + "ta ma", + "kano jo", + "ki te", + "mon dai", + "a mari", + "e ki", + "ko jin", + "ha ya", + "i t", + "de te", + "atara shii", + "a wa", + "ga kkou", + "tsu zu", + "shu kan", + "i mashita", + "mi na", + "ata e", + "da rou", + "hatara ku", + "ga ta", + "da chi", + "ma tsu", + "ari masen", + "sei butsu", + "mi tsu", + "he ya", + "yasu i", + "d i", + "de ni", + "no ko", + "ha ha", + "do mo", + "ka mi", + "su deni", + "na o", + "ra ku", + "i ke", + "a ki", + "me ta", + "l o", + "ko domo", + "so shite", + "ga me", + "ba kari", + "to te", + "ha tsu", + "mi se", + "moku teki", + "da kara", + "s z", + "e l", + "g y", + "e n", + "t t", + "e m", + "a n", + "a k", + "e r", + "a z", + "a l", + "e t", + "o l", + "e g", + "e k", + "m i", + "o n", + "é s", + "c s", + "a t", + "á r", + "h o", + "e z", + "á l", + "i s", + "á n", + "o r", + "a r", + "e gy", + "e s", + "é r", + "á t", + "o tt", + "e tt", + "m eg", + "t a", + "o k", + "o s", + "ho gy", + "n em", + "é g", + "n y", + "k i", + "é l", + "h a", + "á s", + "ü l", + "i n", + "mi n", + "n a", + "e d", + "o m", + "i k", + "k ö", + "m a", + "n i", + "v a", + "v ol", + "é t", + "b b", + "f el", + "i g", + "l e", + "r a", + "é n", + "t e", + "d e", + "a d", + "ó l", + "b e", + "on d", + "j a", + "r e", + "u l", + "b en", + "n ek", + "u t", + "vol t", + "b an", + "ö r", + "o g", + "a p", + "o d", + "á g", + "n k", + "é k", + "v al", + "k or", + "a m", + "i l", + "í t", + "á k", + "b a", + "u d", + "sz er", + "min d", + "o z", + "é p", + "el l", + "ér t", + "m ond", + "i t", + "sz t", + "n ak", + "a mi", + "n e", + "ő l", + "cs ak", + "n é", + "ma g", + "ol y", + "m er", + "ál l", + "án y", + "ö n", + "ö l", + "min t", + "m ár", + "ö tt", + "na gy", + "é sz", + "az t", + "el ő", + "t ud", + "o t", + "é ny", + "á z", + "m ég", + "kö z", + "el y", + "s ég", + "en t", + "s em", + "ta m", + "h et", + "h al", + "f i", + "a s", + "v an", + "ho z", + "v e", + "u k", + "k ez", + "á m", + "v el", + "b er", + "a j", + "u nk", + "i z", + "va gy", + "m os", + "sz em", + "em ber", + "f og", + "mer t", + "ü k", + "l en", + "ö s", + "e j", + "t al", + "h at", + "t ak", + "h i", + "m ás", + "s ág", + "ett e", + "l eg", + "ü nk", + "h át", + "sz a", + "on y", + "ez t", + "mind en", + "en d", + "ül t", + "h an", + "j ó", + "k is", + "á j", + "in t", + "ú gy", + "i d", + "mos t", + "ar t", + "í r", + "k er", + "i tt", + "a tt", + "el t", + "mond ta", + "k ell", + "l á", + "ak i", + "ál t", + "ér d", + "t ö", + "l an", + "v ár", + "h ol", + "t el", + "l át", + "ő k", + "v et", + "s e", + "ut án", + "k ét", + "na p", + "í v", + "ál y", + "v ég", + "ö k", + "i r", + "d ul", + "v is", + "né z", + "t er", + "á ban", + "k ül", + "ak kor", + "k ap", + "sz él", + "y en", + "ú j", + "i m", + "oly an", + "es en", + "k ed", + "h ely", + "t ör", + "b ól", + "el m", + "r á", + "ár a", + "r ó", + "l ó", + "vol na", + "t an", + "le het", + "e bb", + "t en", + "t ek", + "s ok", + "k al", + "f or", + "u g", + "ol t", + "k a", + "ek et", + "b or", + "f ej", + "g ond", + "a g", + "ak ar", + "f él", + "ú l", + "b el", + "ott a", + "mi t", + "val ami", + "j el", + "é d", + "ar c", + "u r", + "hal l", + "t i", + "f öl", + "á ba", + "ol g", + "ki r", + "ol d", + "m ar", + "k érd", + "j ár", + "ú r", + "sz e", + "z s", + "él et", + "j át", + "o v", + "u s", + "é z", + "v il", + "v er", + "ő r", + "á d", + "ö g", + "le sz", + "on t", + "b iz", + "k oz", + "á bb", + "kir ály", + "es t", + "a b", + "en g", + "ig az", + "b ar", + "ha j", + "d i", + "o b", + "k od", + "r ól", + "v ez", + "tö bb", + "sz ó", + "é ben", + "ö t", + "ny i", + "t á", + "sz ól", + "gond ol", + "eg ész", + "í gy", + "ő s", + "o bb", + "os an", + "b ől", + "a bb", + "c i", + "ő t", + "n ál", + "k ép", + "azt án", + "v i", + "t art", + "be szél", + "m en", + "elő tt", + "a szt", + "ma j", + "kö r", + "han g", + "í z", + "in cs", + "a i", + "é v", + "ó d", + "ó k", + "hoz z", + "t em", + "ok at", + "an y", + "nagy on", + "h áz", + "p er", + "p ed", + "ez te", + "et len", + "nek i", + "maj d", + "sz ony", + "án ak", + "fel é", + "egy szer", + "j e", + "ad t", + "gy er", + "ami kor", + "f oly", + "sz ak", + "ő d", + "h ú", + "á sz", + "am ely", + "h ar", + "ér e", + "il yen", + "od a", + "j ák", + "t ár", + "á val", + "l ak", + "t ó", + "m ent", + "gy an", + "él y", + "ú t", + "v ar", + "kez d", + "m ell", + "mi kor", + "h ez", + "val ó", + "k o", + "m es", + "szer et", + "r end", + "l et", + "vis sza", + "ig en", + "f ő", + "va s", + "as szony", + "r ől", + "ped ig", + "p i", + "sz ép", + "t ák", + "ö v", + "an i", + "vil ág", + "p en", + "mag a", + "t et", + "sz ik", + "é j", + "én t", + "j ött", + "s an", + "sz í", + "i de", + "g at", + "ett em", + "ul t", + "h ány", + "ás t", + "a hol", + "ők et", + "h ár", + "k el", + "n ő", + "cs i", + "tal ál", + "el te", + "lá tt", + "tör t", + "ha gy", + "e sz", + "s en", + "n él", + "p ar", + "v ál", + "k ut", + "l ány", + "ami t", + "s ő", + "ell en", + "mag át", + "in k", + "u gyan", + "kül ön", + "a sz", + "mind ig", + "l ép", + "tal án", + "u n", + "sz or", + "k e", + "il lan", + "n incs", + "z et", + "vagy ok", + "tel en", + "is mer", + "s or", + "is ten", + "ít ott", + "j obb", + "v es", + "dul t", + "j uk", + "sz en", + "r o", + "ö m", + "l ett", + "k ar", + "egy ik", + "b ár", + "sz i", + "sz ív", + "az on", + "e szt", + "föl d", + "kut y", + "p illan", + "f ér", + "k om", + "t ől", + "t ű", + "é be", + "t ött", + "bar át", + "í g", + "a hogy", + "e h", + "e p", + "s o", + "v en", + "jel ent", + "t at", + "sz eg", + "mint ha", + "f al", + "egy en", + "mi l", + "sza b", + "r i", + "é m", + "biz ony", + "j on", + "ör eg", + "d olg", + "cs ap", + "ti szt", + "áll t", + "an cs", + "id ő", + "k at", + "ü gy", + "mi ért", + "ó t", + "ü r", + "cs in", + "h az", + "b et", + "én ek", + "v ér", + "j ól", + "al att", + "m ely", + "l o", + "sem mi", + "ny ug", + "v ág", + "kö vet", + "ös sze", + "ma d", + "l i", + "a cs", + "fi ú", + "kö n", + "más ik", + "j ön", + "sz ám", + "g er", + "s ó", + "r ész", + "k ér", + "z el", + "é vel", + "e o", + "e u", + "a n", + "eu l", + "eu n", + "eo n", + "a e", + "d a", + "a l", + "s s", + "i n", + "i l", + "a g", + "an g", + "y eon", + "y eo", + "d o", + "c h", + "n g", + "j i", + "h an", + "g a", + "g o", + "u i", + "h ae", + "a m", + "u l", + "u n", + "g eo", + "s i", + "n eun", + "ss da", + "s eo", + "eon g", + "y o", + "i da", + "t t", + "k k", + "j eo", + "d eul", + "w a", + "eu m", + "g e", + "o n", + "o g", + "s al", + "m an", + "yeon g", + "geo s", + "h ag", + "an eun", + "j a", + "g i", + "s u", + "i ss", + "o l", + "d ae", + "eo b", + "h a", + "j u", + "eo l", + "g eu", + "j eong", + "s ae", + "do e", + "g eul", + "s eu", + "s in", + "eul o", + "b n", + "s ang", + "bn ida", + "h al", + "b o", + "han eun", + "m al", + "i m", + "m o", + "b u", + "jeo g", + "sae ng", + "in eun", + "an h", + "m a", + "sal am", + "j o", + "s a", + "eo m", + "n ae", + "w i", + "l o", + "g wa", + "yeo l", + "n a", + "e seo", + "y e", + "m yeon", + "tt ae", + "h w", + "j e", + "eob s", + "j ang", + "g u", + "g w", + "il eul", + "yeo g", + "j eon", + "si g", + "j ag", + "j in", + "y u", + "o e", + "s e", + "hag o", + "d eun", + "y a", + "m un", + "s eong", + "g ag", + "h am", + "d ang", + "b a", + "l eul", + "s il", + "do ng", + "kk a", + "b al", + "da l", + "han da", + "eo ssda", + "ae g", + "l i", + "ha ji", + "s eon", + "o ng", + "hae ssda", + "d e", + "i ssda", + "e ge", + "b un", + "m ul", + "ju ng", + "ji g", + "m u", + "iss neun", + "b i", + "g eun", + "seu bnida", + "w on", + "p p", + "d aneun", + "eo h", + "d eo", + "ga m", + "j al", + "hae ng", + "ag o", + "y ang", + "b ul", + "b ang", + "u m", + "s o", + "h i", + "j ae", + "si m", + "saeng gag", + "hag e", + "s og", + "eo ss", + "d an", + "ja sin", + "j il", + "eo g", + "g yeong", + "doe n", + "go ng", + "m i", + "ch i", + "d eu", + "d eon", + "hae ss", + "d u", + "n am", + "eun g", + "jo h", + "n al", + "m yeong", + "w o", + "eon a", + "i go", + "g yeol", + "y ag", + "gw an", + "ul i", + "yo ng", + "n o", + "l yeo", + "j og", + "eoh ge", + "ga t", + "b og", + "mo s", + "t ong", + "ch a", + "man h", + "jeo l", + "geo l", + "h oe", + "ag a", + "n aneun", + "g an", + "un eun", + "ch eol", + "ch e", + "do l", + "b on", + "b an", + "ba d", + "ch u", + "ham yeon", + "yeo ssda", + "i bnida", + "g ye", + "eo s", + "hw al", + "salam deul", + "ji man", + "dang sin", + "ji b", + "ttae mun", + "m ae", + "i b", + "e neun", + "eu g", + "jeo m", + "geul eon", + "h wa", + "a ssda", + "b eob", + "bu t", + "b ae", + "yeo ss", + "ch in", + "ch aeg", + "g eon", + "g ae", + "nae ga", + "i ga", + "m og", + "sig an", + "g il", + "h yeon", + "l yeog", + "gu g", + "p yeon", + "s an", + "w ae", + "j ul", + "s eul", + "deun g", + "haji man", + "eum yeon", + "p il", + "m ol", + "n eu", + "a ss", + "n yeon", + "t ae", + "h u", + "p yo", + "s ul", + "g ang", + "j ineun", + "b eon", + "ha da", + "seo l", + "si p", + "dal eun", + "a p", + "sal m", + "g yo", + "ch eon", + "hag i", + "in a", + "cheol eom", + "g al", + "il a", + "kka ji", + "anh neun", + "ha bnida", + "tt eon", + "n u", + "hae seo", + "doen da", + "s ol", + "tt al", + "l a", + "il o", + "seu b", + "b yeon", + "m yeo", + "b eol", + "s on", + "n un", + "j un", + "j am", + "j eung", + "tt o", + "e n", + "mo m", + "h o", + "ch im", + "hw ang", + "eun eun", + "jo ng", + "bo da", + "n ol", + "n eom", + "but eo", + "jig eum", + "eobs da", + "dae lo", + "i g", + "y ul", + "p yeong", + "seon eun", + "sal ang", + "seu t", + "h im", + "n an", + "h eom", + "h yang", + "p i", + "gw ang", + "eobs neun", + "hw ag", + "ge ss", + "jag i", + "il eon", + "wi hae", + "dae han", + "ga ji", + "m eog", + "j yeo", + "cha j", + "b yeong", + "eo d", + "g yeo", + "do n", + "eo ji", + "g ul", + "mo deun", + "j on", + "in saeng", + "geul ae", + "h ang", + "sa sil", + "si b", + "ch al", + "il ago", + "doe l", + "g eum", + "doe neun", + "b ol", + "ga jang", + "geul igo", + "e l", + "h yeong", + "haeng bog", + "ch ul", + "h on", + "ch ae", + "s am", + "m ang", + "in da", + "da m", + "w ol", + "ch oe", + "d ul", + "si jag", + "ch eong", + "il aneun", + "ul ineun", + "ae n", + "kk e", + "mun je", + "a do", + "t eu", + "g un", + "geun eun", + "b ge", + "ch eo", + "b aeg", + "ju g", + "t a", + "sang dae", + "geu geos", + "do g", + "eu s", + "deu s", + "ja b", + "h yeo", + "tt eohge", + "u g", + "ma j", + "ch il", + "s wi", + "j ileul", + "ch ang", + "g aneun", + "m ag", + "i ji", + "da go", + "m in", + "yo han", + "t eug", + "pp un", + "al eul", + "haeng dong", + "p o", + "m il", + "ch am", + "se sang", + "e do", + "p an", + "man deul", + "am yeon", + "a b", + "kk ae", + "b ag", + "i deul", + "p um", + "m eol", + "s un", + "n eul", + "ham kke", + "chu ng", + "da b", + "yu g", + "s ag", + "gwang ye", + "il eohge", + "bal o", + "neun de", + "ham yeo", + "go s", + "geul eoh", + "an ila", + "bang beob", + "da si", + "b yeol", + "g yeon", + "gam jeong", + "on eul", + "j aneun", + "yeo m", + "l ago", + "i gi", + "hw an", + "t eul", + "eo seo", + "si k", + "ch o", + "jag a", + "geul eom", + "geul eona", + "jeong do", + "g yeog", + "geul eohge", + "geu deul", + "eu t", + "im yeon", + "j jae", + "k eun", + "i sang", + "mal haessda", + "eu ge", + "no p", + "in gan", + "bo myeon", + "t aeg", + "seu s", + "d wi", + "s aneun", + "w an", + "anh go", + "t an", + "nu gu", + "su ng", + "da myeon", + "a deul", + "p eul", + "ttal a", + "d i", + "geos do", + "a ji", + "m eon", + "eum yeo", + "dol og", + "neun g", + "mo du", + "क े", + "ह ै", + "े ं", + "् र", + "ा र", + "न े", + "य ा", + "म ें", + "स े", + "क ी", + "क ा", + "ो ं", + "त ा", + "क र", + "स ्", + "क ि", + "क ो", + "र ्", + "न ा", + "क ्", + "ह ी", + "औ र", + "प र", + "त े", + "ह ो", + "प ्र", + "ा न", + "् य", + "ल ा", + "व ा", + "ल े", + "स ा", + "है ं", + "ल ि", + "ज ा", + "ह ा", + "भ ी", + "व ि", + "इ स", + "त ी", + "न ्", + "र ा", + "म ा", + "द े", + "द ि", + "ब ा", + "त ि", + "थ ा", + "न ि", + "क ार", + "ए क", + "ही ं", + "ह ु", + "ं ग", + "ै ं", + "न ी", + "स ी", + "अ प", + "त ्", + "न हीं", + "र ी", + "म े", + "म ु", + "ि त", + "त ो", + "प ा", + "ल ी", + "लि ए", + "ग ा", + "ल ्", + "र ह", + "र े", + "क् ष", + "म ैं", + "स म", + "उ स", + "ज ि", + "त ्र", + "म ि", + "च ा", + "ो ग", + "स ं", + "द ्", + "स ि", + "आ प", + "त ु", + "द ा", + "क ु", + "य ों", + "व े", + "ज ी", + "् या", + "उ न", + "ि क", + "य े", + "भ ा", + "् ट", + "ह म", + "स् ट", + "श ा", + "ड ़", + "ं द", + "ख ा", + "म ्", + "श ्", + "य ह", + "स क", + "प ू", + "कि या", + "अप ने", + "र ू", + "स ु", + "म ी", + "ह ि", + "ज ो", + "थ े", + "र ि", + "द ी", + "थ ी", + "ग ी", + "ल ोग", + "ग या", + "त र", + "न् ह", + "च ्", + "व ार", + "ब ी", + "प ्", + "द ो", + "ट ी", + "श ि", + "कर ने", + "ग े", + "ै से", + "इ न", + "ं ड", + "सा थ", + "प ु", + "ब े", + "ब ार", + "व ी", + "अ न", + "ह र", + "उ न्ह", + "हो ता", + "ज ब", + "कु छ", + "म ान", + "क ्र", + "ब ि", + "प ह", + "फ ि", + "स र", + "ार ी", + "र ो", + "द ू", + "क हा", + "त क", + "श न", + "ब ्", + "स् थ", + "व ह", + "बा द", + "ओ ं", + "ग ु", + "ज ्", + "्र े", + "ग र", + "रह े", + "व र्", + "ह ू", + "ार ्", + "प ी", + "ब हु", + "मु झ", + "्र ा", + "दि या", + "स ब", + "कर ते", + "अप नी", + "बहु त", + "क ह", + "ट े", + "हु ए", + "कि सी", + "र हा", + "ष ्ट", + "ज ़", + "ब ना", + "स ो", + "ड ि", + "को ई", + "व ्य", + "बा त", + "र ु", + "व ो", + "मुझ े", + "द् ध", + "च ार", + "मे रे", + "व र", + "्र ी", + "जा ता", + "न ों", + "प्र ा", + "दे ख", + "ट ा", + "क् या", + "अ ध", + "ल ग", + "ल ो", + "प ि", + "य ु", + "च े", + "जि स", + "ं त", + "ान ी", + "प ै", + "ज न", + "ार े", + "च ी", + "मि ल", + "द ु", + "दे श", + "च् छ", + "ष ्", + "स ू", + "ख े", + "च ु", + "ि या", + "ल गा", + "ब ु", + "उन के", + "ज् ञ", + "क्ष ा", + "त रह", + "्या दा", + "वा ले", + "पू र्", + "मैं ने", + "का म", + "रू प", + "हो ती", + "उ प", + "ज ान", + "प्र कार", + "भ ार", + "म न", + "हु आ", + "ट र", + "हू ँ", + "पर ि", + "पा स", + "अन ु", + "रा ज", + "लोग ों", + "अ ब", + "सम झ", + "ड ी", + "म ौ", + "श ु", + "च ि", + "प े", + "क ृ", + "सक ते", + "म ह", + "य ोग", + "द र्", + "उ से", + "ं ध", + "ड ा", + "जा ए", + "ब ो", + "ू ल", + "म ो", + "ों ने", + "ं स", + "तु म", + "पह ले", + "ब ता", + "त था", + "य ो", + "ग ई", + "उ त्", + "सक ता", + "क म", + "ज ्यादा", + "र ख", + "सम य", + "ार ा", + "अ गर", + "स् त", + "च ल", + "फि र", + "वार ा", + "कर ना", + "श ी", + "ग ए", + "ब न", + "ौ र", + "हो ने", + "चा ह", + "ख ु", + "हा ँ", + "उन्ह ें", + "उन्ह ोंने", + "छ ो", + "म् ह", + "प्र ति", + "नि क", + "व न", + "्य ू", + "र ही", + "तु म्ह", + "ज ैसे", + "ि यों", + "क् यों", + "ल ों", + "फ ़", + "ं त्र", + "हो ते", + "क् ति", + "त ्य", + "कर ्", + "क ई", + "व ं", + "कि न", + "प ो", + "कार ण", + "ड़ ी", + "भ ि", + "इस के", + "ब र", + "उस के", + "द् वारा", + "श े", + "क ॉ", + "दि न", + "न् न", + "ड़ ा", + "स् व", + "नि र्", + "मु ख", + "लि या", + "ट ि", + "ज्ञ ान", + "क् त", + "द ्र", + "ग ्", + "क् स", + "म ै", + "ग ो", + "ज े", + "ट ्र", + "म ार", + "त् व", + "ध ार", + "भा व", + "कर ता", + "ख ि", + "क ं", + "चा हि", + "य र", + "प् त", + "क ों", + "ं च", + "ज ु", + "म त", + "अ च्छ", + "हु ई", + "क भी", + "ले किन", + "भ ू", + "अप ना", + "दू स", + "चाहि ए", + "य ू", + "घ र", + "सब से", + "मे री", + "ना म", + "ढ ़", + "ं ट", + "ें गे", + "ब ै", + "फ ा", + "ए वं", + "य ी", + "ग ्र", + "क्ष े", + "आ ज", + "आप को", + "भा ग", + "ठ ा", + "क ै", + "भार त", + "उन की", + "प हु", + "स भी", + "ध ा", + "ण ा", + "स ान", + "हो गा", + "त ब", + "स ंग", + "प र्", + "अ व", + "त ना", + "ग ि", + "य न", + "स् था", + "च ित", + "ट ्", + "छ ा", + "जा ने", + "क्षे त्र", + "वा ली", + "पूर् ण", + "स मा", + "कार ी" + ] + } +} \ No newline at end of file diff --git a/ComfyUI/comfy/text_encoders/hydit_clip_tokenizer/special_tokens_map.json b/ComfyUI/comfy/text_encoders/hydit_clip_tokenizer/special_tokens_map.json new file mode 100644 index 0000000000000000000000000000000000000000..a8b3208c2884c4efb86e49300fdd3dc877220cdf --- /dev/null +++ b/ComfyUI/comfy/text_encoders/hydit_clip_tokenizer/special_tokens_map.json @@ -0,0 +1,7 @@ +{ + "cls_token": "[CLS]", + "mask_token": "[MASK]", + "pad_token": "[PAD]", + "sep_token": "[SEP]", + "unk_token": "[UNK]" +} diff --git a/ComfyUI/comfy/text_encoders/llama_tokenizer/tokenizer_config.json b/ComfyUI/comfy/text_encoders/llama_tokenizer/tokenizer_config.json new file mode 100644 index 0000000000000000000000000000000000000000..0b336f5a5199c43e7305481f4e9e26e292b2f0fc --- /dev/null +++ b/ComfyUI/comfy/text_encoders/llama_tokenizer/tokenizer_config.json @@ -0,0 +1,2095 @@ +{ + "add_bos_token": true, + "add_eos_token": false, + "add_prefix_space": null, + "added_tokens_decoder": { + "128000": { + "content": "<|begin_of_text|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128001": { + "content": "<|end_of_text|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128002": { + "content": "<|reserved_special_token_0|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128003": { + "content": "<|reserved_special_token_1|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128004": { + "content": "<|reserved_special_token_2|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128005": { + "content": "<|reserved_special_token_3|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128006": { + "content": "<|start_header_id|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128007": { + "content": "<|end_header_id|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128008": { + "content": "<|reserved_special_token_4|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128009": { + "content": "<|eot_id|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128010": { + "content": "<|reserved_special_token_5|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128011": { + "content": "<|reserved_special_token_6|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128012": { + "content": "<|reserved_special_token_7|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128013": { + "content": "<|reserved_special_token_8|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128014": { + "content": "<|reserved_special_token_9|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128015": { + "content": "<|reserved_special_token_10|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128016": { + "content": "<|reserved_special_token_11|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128017": { + "content": "<|reserved_special_token_12|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128018": { + "content": "<|reserved_special_token_13|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128019": { + "content": "<|reserved_special_token_14|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128020": { + "content": "<|reserved_special_token_15|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128021": { + "content": "<|reserved_special_token_16|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128022": { + "content": "<|reserved_special_token_17|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128023": { + "content": "<|reserved_special_token_18|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128024": { + "content": "<|reserved_special_token_19|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128025": { + "content": "<|reserved_special_token_20|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128026": { + "content": "<|reserved_special_token_21|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128027": { + "content": "<|reserved_special_token_22|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128028": { + "content": "<|reserved_special_token_23|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128029": { + "content": "<|reserved_special_token_24|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128030": { + "content": "<|reserved_special_token_25|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128031": { + "content": "<|reserved_special_token_26|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128032": { + "content": "<|reserved_special_token_27|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128033": { + "content": "<|reserved_special_token_28|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128034": { + "content": "<|reserved_special_token_29|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128035": { + "content": "<|reserved_special_token_30|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128036": { + "content": "<|reserved_special_token_31|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128037": { + "content": "<|reserved_special_token_32|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128038": { + "content": "<|reserved_special_token_33|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128039": { + "content": "<|reserved_special_token_34|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128040": { + "content": "<|reserved_special_token_35|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128041": { + "content": "<|reserved_special_token_36|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128042": { + "content": "<|reserved_special_token_37|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128043": { + "content": "<|reserved_special_token_38|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128044": { + "content": "<|reserved_special_token_39|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128045": { + "content": "<|reserved_special_token_40|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128046": { + "content": "<|reserved_special_token_41|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128047": { + "content": "<|reserved_special_token_42|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128048": { + "content": "<|reserved_special_token_43|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128049": { + "content": "<|reserved_special_token_44|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128050": { + "content": "<|reserved_special_token_45|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128051": { + "content": "<|reserved_special_token_46|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128052": { + "content": "<|reserved_special_token_47|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128053": { + "content": "<|reserved_special_token_48|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128054": { + "content": "<|reserved_special_token_49|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128055": { + "content": "<|reserved_special_token_50|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128056": { + "content": "<|reserved_special_token_51|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128057": { + "content": "<|reserved_special_token_52|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128058": { + "content": "<|reserved_special_token_53|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128059": { + "content": "<|reserved_special_token_54|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128060": { + "content": "<|reserved_special_token_55|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128061": { + "content": "<|reserved_special_token_56|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128062": { + "content": "<|reserved_special_token_57|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128063": { + "content": "<|reserved_special_token_58|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128064": { + "content": "<|reserved_special_token_59|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128065": { + "content": "<|reserved_special_token_60|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128066": { + "content": "<|reserved_special_token_61|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128067": { + "content": "<|reserved_special_token_62|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128068": { + "content": "<|reserved_special_token_63|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128069": { + "content": "<|reserved_special_token_64|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128070": { + "content": "<|reserved_special_token_65|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128071": { + "content": "<|reserved_special_token_66|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128072": { + "content": "<|reserved_special_token_67|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128073": { + "content": "<|reserved_special_token_68|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128074": { + "content": "<|reserved_special_token_69|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128075": { + "content": "<|reserved_special_token_70|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128076": { + "content": "<|reserved_special_token_71|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128077": { + "content": "<|reserved_special_token_72|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128078": { + "content": "<|reserved_special_token_73|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128079": { + "content": "<|reserved_special_token_74|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128080": { + "content": "<|reserved_special_token_75|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128081": { + "content": "<|reserved_special_token_76|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128082": { + "content": "<|reserved_special_token_77|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128083": { + "content": "<|reserved_special_token_78|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128084": { + "content": "<|reserved_special_token_79|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128085": { + "content": "<|reserved_special_token_80|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128086": { + "content": "<|reserved_special_token_81|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128087": { + "content": "<|reserved_special_token_82|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128088": { + "content": "<|reserved_special_token_83|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128089": { + "content": "<|reserved_special_token_84|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128090": { + "content": "<|reserved_special_token_85|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128091": { + "content": "<|reserved_special_token_86|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128092": { + "content": "<|reserved_special_token_87|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128093": { + "content": "<|reserved_special_token_88|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128094": { + "content": "<|reserved_special_token_89|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128095": { + "content": "<|reserved_special_token_90|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128096": { + "content": "<|reserved_special_token_91|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128097": { + "content": "<|reserved_special_token_92|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128098": { + "content": "<|reserved_special_token_93|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128099": { + "content": "<|reserved_special_token_94|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128100": { + "content": "<|reserved_special_token_95|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128101": { + "content": "<|reserved_special_token_96|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128102": { + "content": "<|reserved_special_token_97|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128103": { + "content": "<|reserved_special_token_98|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128104": { + "content": "<|reserved_special_token_99|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128105": { + "content": "<|reserved_special_token_100|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128106": { + "content": "<|reserved_special_token_101|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128107": { + "content": "<|reserved_special_token_102|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128108": { + "content": "<|reserved_special_token_103|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128109": { + "content": "<|reserved_special_token_104|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128110": { + "content": "<|reserved_special_token_105|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128111": { + "content": "<|reserved_special_token_106|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128112": { + "content": "<|reserved_special_token_107|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128113": { + "content": "<|reserved_special_token_108|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128114": { + "content": "<|reserved_special_token_109|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128115": { + "content": "<|reserved_special_token_110|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128116": { + "content": "<|reserved_special_token_111|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128117": { + "content": "<|reserved_special_token_112|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128118": { + "content": "<|reserved_special_token_113|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128119": { + "content": "<|reserved_special_token_114|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128120": { + "content": "<|reserved_special_token_115|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128121": { + "content": "<|reserved_special_token_116|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128122": { + "content": "<|reserved_special_token_117|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128123": { + "content": "<|reserved_special_token_118|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128124": { + "content": "<|reserved_special_token_119|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128125": { + "content": "<|reserved_special_token_120|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128126": { + "content": "<|reserved_special_token_121|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128127": { + "content": "<|reserved_special_token_122|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128128": { + "content": "<|reserved_special_token_123|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128129": { + "content": "<|reserved_special_token_124|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128130": { + "content": "<|reserved_special_token_125|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128131": { + "content": "<|reserved_special_token_126|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128132": { + "content": "<|reserved_special_token_127|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128133": { + "content": "<|reserved_special_token_128|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128134": { + "content": "<|reserved_special_token_129|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128135": { + "content": "<|reserved_special_token_130|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128136": { + "content": "<|reserved_special_token_131|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128137": { + "content": "<|reserved_special_token_132|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128138": { + "content": "<|reserved_special_token_133|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128139": { + "content": "<|reserved_special_token_134|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128140": { + "content": "<|reserved_special_token_135|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128141": { + "content": "<|reserved_special_token_136|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128142": { + "content": "<|reserved_special_token_137|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128143": { + "content": "<|reserved_special_token_138|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128144": { + "content": "<|reserved_special_token_139|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128145": { + "content": "<|reserved_special_token_140|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128146": { + "content": "<|reserved_special_token_141|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128147": { + "content": "<|reserved_special_token_142|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128148": { + "content": "<|reserved_special_token_143|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128149": { + "content": "<|reserved_special_token_144|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128150": { + "content": "<|reserved_special_token_145|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128151": { + "content": "<|reserved_special_token_146|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128152": { + "content": "<|reserved_special_token_147|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128153": { + "content": "<|reserved_special_token_148|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128154": { + "content": "<|reserved_special_token_149|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128155": { + "content": "<|reserved_special_token_150|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128156": { + "content": "<|reserved_special_token_151|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128157": { + "content": "<|reserved_special_token_152|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128158": { + "content": "<|reserved_special_token_153|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128159": { + "content": "<|reserved_special_token_154|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128160": { + "content": "<|reserved_special_token_155|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128161": { + "content": "<|reserved_special_token_156|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128162": { + "content": "<|reserved_special_token_157|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128163": { + "content": "<|reserved_special_token_158|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128164": { + "content": "<|reserved_special_token_159|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128165": { + "content": "<|reserved_special_token_160|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128166": { + "content": "<|reserved_special_token_161|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128167": { + "content": "<|reserved_special_token_162|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128168": { + "content": "<|reserved_special_token_163|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128169": { + "content": "<|reserved_special_token_164|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128170": { + "content": "<|reserved_special_token_165|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128171": { + "content": "<|reserved_special_token_166|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128172": { + "content": "<|reserved_special_token_167|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128173": { + "content": "<|reserved_special_token_168|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128174": { + "content": "<|reserved_special_token_169|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128175": { + "content": "<|reserved_special_token_170|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128176": { + "content": "<|reserved_special_token_171|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128177": { + "content": "<|reserved_special_token_172|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128178": { + "content": "<|reserved_special_token_173|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128179": { + "content": "<|reserved_special_token_174|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128180": { + "content": "<|reserved_special_token_175|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128181": { + "content": "<|reserved_special_token_176|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128182": { + "content": "<|reserved_special_token_177|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128183": { + "content": "<|reserved_special_token_178|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128184": { + "content": "<|reserved_special_token_179|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128185": { + "content": "<|reserved_special_token_180|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128186": { + "content": "<|reserved_special_token_181|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128187": { + "content": "<|reserved_special_token_182|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128188": { + "content": "<|reserved_special_token_183|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128189": { + "content": "<|reserved_special_token_184|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128190": { + "content": "<|reserved_special_token_185|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128191": { + "content": "<|reserved_special_token_186|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128192": { + "content": "<|reserved_special_token_187|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128193": { + "content": "<|reserved_special_token_188|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128194": { + "content": "<|reserved_special_token_189|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128195": { + "content": "<|reserved_special_token_190|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128196": { + "content": "<|reserved_special_token_191|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128197": { + "content": "<|reserved_special_token_192|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128198": { + "content": "<|reserved_special_token_193|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128199": { + "content": "<|reserved_special_token_194|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128200": { + "content": "<|reserved_special_token_195|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128201": { + "content": "<|reserved_special_token_196|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128202": { + "content": "<|reserved_special_token_197|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128203": { + "content": "<|reserved_special_token_198|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128204": { + "content": "<|reserved_special_token_199|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128205": { + "content": "<|reserved_special_token_200|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128206": { + "content": "<|reserved_special_token_201|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128207": { + "content": "<|reserved_special_token_202|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128208": { + "content": "<|reserved_special_token_203|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128209": { + "content": "<|reserved_special_token_204|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128210": { + "content": "<|reserved_special_token_205|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128211": { + "content": "<|reserved_special_token_206|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128212": { + "content": "<|reserved_special_token_207|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128213": { + "content": "<|reserved_special_token_208|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128214": { + "content": "<|reserved_special_token_209|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128215": { + "content": "<|reserved_special_token_210|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128216": { + "content": "<|reserved_special_token_211|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128217": { + "content": "<|reserved_special_token_212|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128218": { + "content": "<|reserved_special_token_213|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128219": { + "content": "<|reserved_special_token_214|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128220": { + "content": "<|reserved_special_token_215|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128221": { + "content": "<|reserved_special_token_216|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128222": { + "content": "<|reserved_special_token_217|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128223": { + "content": "<|reserved_special_token_218|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128224": { + "content": "<|reserved_special_token_219|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128225": { + "content": "<|reserved_special_token_220|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128226": { + "content": "<|reserved_special_token_221|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128227": { + "content": "<|reserved_special_token_222|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128228": { + "content": "<|reserved_special_token_223|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128229": { + "content": "<|reserved_special_token_224|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128230": { + "content": "<|reserved_special_token_225|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128231": { + "content": "<|reserved_special_token_226|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128232": { + "content": "<|reserved_special_token_227|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128233": { + "content": "<|reserved_special_token_228|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128234": { + "content": "<|reserved_special_token_229|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128235": { + "content": "<|reserved_special_token_230|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128236": { + "content": "<|reserved_special_token_231|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128237": { + "content": "<|reserved_special_token_232|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128238": { + "content": "<|reserved_special_token_233|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128239": { + "content": "<|reserved_special_token_234|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128240": { + "content": "<|reserved_special_token_235|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128241": { + "content": "<|reserved_special_token_236|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128242": { + "content": "<|reserved_special_token_237|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128243": { + "content": "<|reserved_special_token_238|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128244": { + "content": "<|reserved_special_token_239|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128245": { + "content": "<|reserved_special_token_240|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128246": { + "content": "<|reserved_special_token_241|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128247": { + "content": "<|reserved_special_token_242|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128248": { + "content": "<|reserved_special_token_243|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128249": { + "content": "<|reserved_special_token_244|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128250": { + "content": "<|reserved_special_token_245|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128251": { + "content": "<|reserved_special_token_246|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128252": { + "content": "<|reserved_special_token_247|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128253": { + "content": "<|reserved_special_token_248|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128254": { + "content": "<|reserved_special_token_249|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128255": { + "content": "<|reserved_special_token_250|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128256": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128257": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "128258": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + } + }, + "bos_token": "<|begin_of_text|>", + "chat_template": "{% set loop_messages = messages %}{% for message in loop_messages %}{% set content = '<|start_header_id|>' + message['role'] + '<|end_header_id|>\n\n'+ message['content'] | trim + '<|eot_id|>' %}{% if loop.index0 == 0 %}{% set content = bos_token + content %}{% endif %}{{ content }}{% endfor %}{{ '<|start_header_id|>assistant<|end_header_id|>\n\n' }}", + "clean_up_tokenization_spaces": true, + "eos_token": "<|end_of_text|>", + "legacy": true, + "model_input_names": [ + "input_ids", + "attention_mask" + ], + "model_max_length": 1000000000000000019884624838656, + "pad_token": "", + "padding_side": "right", + "processor_class": "LlavaProcessor", + "tokenizer_class": "LlamaTokenizer", + "unk_token": "", + "use_default_system_prompt": false +} diff --git a/ComfyUI/comfy/text_encoders/qwen25_tokenizer/tokenizer_config.json b/ComfyUI/comfy/text_encoders/qwen25_tokenizer/tokenizer_config.json new file mode 100644 index 0000000000000000000000000000000000000000..67688e82ccf6bf5b0fa2d95130fb8564acce6398 --- /dev/null +++ b/ComfyUI/comfy/text_encoders/qwen25_tokenizer/tokenizer_config.json @@ -0,0 +1,241 @@ +{ + "add_bos_token": false, + "add_prefix_space": false, + "added_tokens_decoder": { + "151643": { + "content": "<|endoftext|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "151644": { + "content": "<|im_start|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "151645": { + "content": "<|im_end|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "151646": { + "content": "<|object_ref_start|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "151647": { + "content": "<|object_ref_end|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "151648": { + "content": "<|box_start|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "151649": { + "content": "<|box_end|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "151650": { + "content": "<|quad_start|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "151651": { + "content": "<|quad_end|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "151652": { + "content": "<|vision_start|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "151653": { + "content": "<|vision_end|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "151654": { + "content": "<|vision_pad|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "151655": { + "content": "<|image_pad|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "151656": { + "content": "<|video_pad|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "151657": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": false + }, + "151658": { + "content": "", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": false + }, + "151659": { + "content": "<|fim_prefix|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": false + }, + "151660": { + "content": "<|fim_middle|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": false + }, + "151661": { + "content": "<|fim_suffix|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": false + }, + "151662": { + "content": "<|fim_pad|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": false + }, + "151663": { + "content": "<|repo_name|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": false + }, + "151664": { + "content": "<|file_sep|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": false + }, + "151665": { + "content": "<|img|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "151666": { + "content": "<|endofimg|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "151667": { + "content": "<|meta|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + }, + "151668": { + "content": "<|endofmeta|>", + "lstrip": false, + "normalized": false, + "rstrip": false, + "single_word": false, + "special": true + } + }, + "additional_special_tokens": [ + "<|im_start|>", + "<|im_end|>", + "<|object_ref_start|>", + "<|object_ref_end|>", + "<|box_start|>", + "<|box_end|>", + "<|quad_start|>", + "<|quad_end|>", + "<|vision_start|>", + "<|vision_end|>", + "<|vision_pad|>", + "<|image_pad|>", + "<|video_pad|>" + ], + "bos_token": null, + "chat_template": "{%- if tools %}\n {{- '<|im_start|>system\\n' }}\n {%- if messages[0]['role'] == 'system' %}\n {{- messages[0]['content'] }}\n {%- else %}\n {{- 'You are a helpful assistant.' }}\n {%- endif %}\n {{- \"\\n\\n# Tools\\n\\nYou may call one or more functions to assist with the user query.\\n\\nYou are provided with function signatures within XML tags:\\n\" }}\n {%- for tool in tools %}\n {{- \"\\n\" }}\n {{- tool | tojson }}\n {%- endfor %}\n {{- \"\\n\\n\\nFor each function call, return a json object with function name and arguments within XML tags:\\n\\n{\\\"name\\\": , \\\"arguments\\\": }\\n<|im_end|>\\n\" }}\n{%- else %}\n {%- if messages[0]['role'] == 'system' %}\n {{- '<|im_start|>system\\n' + messages[0]['content'] + '<|im_end|>\\n' }}\n {%- else %}\n {{- '<|im_start|>system\\nYou are a helpful assistant.<|im_end|>\\n' }}\n {%- endif %}\n{%- endif %}\n{%- for message in messages %}\n {%- if (message.role == \"user\") or (message.role == \"system\" and not loop.first) or (message.role == \"assistant\" and not message.tool_calls) %}\n {{- '<|im_start|>' + message.role + '\\n' + message.content + '<|im_end|>' + '\\n' }}\n {%- elif message.role == \"assistant\" %}\n {{- '<|im_start|>' + message.role }}\n {%- if message.content %}\n {{- '\\n' + message.content }}\n {%- endif %}\n {%- for tool_call in message.tool_calls %}\n {%- if tool_call.function is defined %}\n {%- set tool_call = tool_call.function %}\n {%- endif %}\n {{- '\\n\\n{\"name\": \"' }}\n {{- tool_call.name }}\n {{- '\", \"arguments\": ' }}\n {{- tool_call.arguments | tojson }}\n {{- '}\\n' }}\n {%- endfor %}\n {{- '<|im_end|>\\n' }}\n {%- elif message.role == \"tool\" %}\n {%- if (loop.index0 == 0) or (messages[loop.index0 - 1].role != \"tool\") %}\n {{- '<|im_start|>user' }}\n {%- endif %}\n {{- '\\n\\n' }}\n {{- message.content }}\n {{- '\\n' }}\n {%- if loop.last or (messages[loop.index0 + 1].role != \"tool\") %}\n {{- '<|im_end|>\\n' }}\n {%- endif %}\n {%- endif %}\n{%- endfor %}\n{%- if add_generation_prompt %}\n {{- '<|im_start|>assistant\\n' }}\n{%- endif %}\n", + "clean_up_tokenization_spaces": false, + "eos_token": "<|im_end|>", + "errors": "replace", + "extra_special_tokens": {}, + "model_max_length": 131072, + "pad_token": "<|endoftext|>", + "processor_class": "Qwen2_5_VLProcessor", + "split_special_tokens": false, + "tokenizer_class": "Qwen2Tokenizer", + "unk_token": null +} diff --git a/ComfyUI/comfy_api/input/__init__.py b/ComfyUI/comfy_api/input/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..66667946f117bcafaa5081ec4c0ade05b9888511 --- /dev/null +++ b/ComfyUI/comfy_api/input/__init__.py @@ -0,0 +1,8 @@ +from .basic_types import ImageInput, AudioInput +from .video_types import VideoInput + +__all__ = [ + "ImageInput", + "AudioInput", + "VideoInput", +] diff --git a/ComfyUI/comfy_api/input/basic_types.py b/ComfyUI/comfy_api/input/basic_types.py new file mode 100644 index 0000000000000000000000000000000000000000..033fb7e272bc20b84ec4c2078ffa4dc5013fc65e --- /dev/null +++ b/ComfyUI/comfy_api/input/basic_types.py @@ -0,0 +1,20 @@ +import torch +from typing import TypedDict + +ImageInput = torch.Tensor +""" +An image in format [B, H, W, C] where B is the batch size, C is the number of channels, +""" + +class AudioInput(TypedDict): + """ + TypedDict representing audio input. + """ + + waveform: torch.Tensor + """ + Tensor in the format [B, C, T] where B is the batch size, C is the number of channels, + """ + + sample_rate: int + diff --git a/ComfyUI/comfy_api/input_impl/__init__.py b/ComfyUI/comfy_api/input_impl/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..02901b8b933caa569cd9dd45eb8e39e7c66e43a1 --- /dev/null +++ b/ComfyUI/comfy_api/input_impl/__init__.py @@ -0,0 +1,7 @@ +from .video_types import VideoFromFile, VideoFromComponents + +__all__ = [ + # Implementations + "VideoFromFile", + "VideoFromComponents", +] diff --git a/ComfyUI/custom_nodes/ComfyUI-Manager/.github/workflows/publish.yml b/ComfyUI/custom_nodes/ComfyUI-Manager/.github/workflows/publish.yml new file mode 100644 index 0000000000000000000000000000000000000000..463ecca9ef06e4c6a8dbd47fcf8acb9335eeef13 --- /dev/null +++ b/ComfyUI/custom_nodes/ComfyUI-Manager/.github/workflows/publish.yml @@ -0,0 +1,25 @@ +name: Publish to Comfy registry +on: + workflow_dispatch: + push: + branches: + - main-blocked + paths: + - "pyproject.toml" + +permissions: + issues: write + +jobs: + publish-node: + name: Publish Custom Node to registry + runs-on: ubuntu-latest + if: ${{ github.repository_owner == 'ltdrdata' }} + steps: + - name: Check out code + uses: actions/checkout@v4 + - name: Publish Custom Node + uses: Comfy-Org/publish-node-action@v1 + with: + ## Add your own personal access token to your Github Repository secrets and reference it here. + personal_access_token: ${{ secrets.REGISTRY_ACCESS_TOKEN }} diff --git a/ComfyUI/custom_nodes/ComfyUI-Manager/.github/workflows/ruff.yml b/ComfyUI/custom_nodes/ComfyUI-Manager/.github/workflows/ruff.yml new file mode 100644 index 0000000000000000000000000000000000000000..4c1a025948b4f4102d162e9a791d1f5fbf4f168c --- /dev/null +++ b/ComfyUI/custom_nodes/ComfyUI-Manager/.github/workflows/ruff.yml @@ -0,0 +1,23 @@ +name: Python Linting + +on: [push, pull_request] + +jobs: + ruff: + name: Run Ruff + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: 3.x + + - name: Install Ruff + run: pip install ruff + + - name: Run Ruff + run: ruff check . diff --git a/ComfyUI/custom_nodes/ComfyUI-Manager/docs/en/cm-cli.md b/ComfyUI/custom_nodes/ComfyUI-Manager/docs/en/cm-cli.md new file mode 100644 index 0000000000000000000000000000000000000000..d00bf3b3a546ed4a07cc1090b897631e476d6bbb --- /dev/null +++ b/ComfyUI/custom_nodes/ComfyUI-Manager/docs/en/cm-cli.md @@ -0,0 +1,147 @@ +# `cm-cli`: ComfyUI-Manager CLI + +`cm-cli` is a tool that allows you to use various functions of ComfyUI-Manager from the command line without launching ComfyUI. + + +``` +-= ComfyUI-Manager CLI (V2.24) =- + + +python cm-cli.py [OPTIONS] + +OPTIONS: + [install|reinstall|uninstall|update|disable|enable|fix] node_name ... ?[--channel ] ?[--mode [remote|local|cache]] + [update|disable|enable|fix] all ?[--channel ] ?[--mode [remote|local|cache]] + [simple-show|show] [installed|enabled|not-installed|disabled|all|snapshot|snapshot-list] ?[--channel ] ?[--mode [remote|local|cache]] + save-snapshot ?[--output ] + restore-snapshot ?[--pip-non-url] ?[--pip-non-local-url] ?[--pip-local-url] + cli-only-mode [enable|disable] + restore-dependencies + clear +``` + +## How To Use? +* You can execute it via `python cm-cli.py`. +* For example, if you want to update all custom nodes: + * In the ComfyUI-Manager directory, you can execute the command `python cm-cli.py update all`. + * If running from the ComfyUI directory, you can specify the path to cm-cli.py like this: `python custom_nodes/ComfyUI-Manager/cm-cli.py update all`. + +## Prerequisite +* It must be run in the same Python environment as the one running ComfyUI. + * If using a venv, you must run it with the venv activated. + * If using a portable version, and you are in the directory with the run_nvidia_gpu.bat file, you should execute the command as follows: + `.\python_embeded\python.exe ComfyUI\custom_nodes\ComfyUI-Manager\cm-cli.py update all` +* The path for ComfyUI can be set with the COMFYUI_PATH environment variable. If omitted, a warning message will appear, and the path will be set relative to the installed location of ComfyUI-Manager: + ``` + WARN: The `COMFYUI_PATH` environment variable is not set. Assuming `custom_nodes/ComfyUI-Manager/../../` as the ComfyUI path. + ``` + +## Features + +### 1. --channel, --mode +* For viewing information and managing custom nodes, you can set the information database through --channel and --mode. +* For instance, executing the command `python cm-cli.py update all --channel recent --mode remote` will operate based on the latest information from remote rather than local data embedded in the current ComfyUI-Manager repo and will only target the list in the recent channel. +* --channel, --mode are only available with the commands `simple-show, show, install, uninstall, update, disable, enable, fix`. + +### 2. Viewing Management Information + +`[simple-show|show] [installed|enabled|not-installed|disabled|all|snapshot|snapshot-list] ?[--channel ] ?[--mode [remote|local|cache]]` + +* `[show|simple-show]` - `show` provides detailed information, while `simple-show` displays information more simply. + +Executing a command like `python cm-cli.py show installed` will display detailed information about the installed custom nodes. + +``` +-= ComfyUI-Manager CLI (V2.24) =- + +FETCH DATA from: https://raw.githubusercontent.com/ltdrdata/ComfyUI-Manager/main/custom-node-list.json +[ ENABLED ] ComfyUI-Manager (author: Dr.Lt.Data) +[ ENABLED ] ComfyUI-Impact-Pack (author: Dr.Lt.Data) +[ ENABLED ] ComfyUI-Inspire-Pack (author: Dr.Lt.Data) +[ ENABLED ] ComfyUI_experiments (author: comfyanonymous) +[ ENABLED ] ComfyUI-SAI_API (author: Stability-AI) +[ ENABLED ] stability-ComfyUI-nodes (author: Stability-AI) +[ ENABLED ] comfyui_controlnet_aux (author: Fannovel16) +[ ENABLED ] ComfyUI-Frame-Interpolation (author: Fannovel16) +[ DISABLED ] ComfyUI-Loopchain (author: Fannovel16) +``` + +Using a command like `python cm-cli.py simple-show installed` will simply display information about the installed custom nodes. + +``` +-= ComfyUI-Manager CLI (V2.24) =- + +FETCH DATA from: https://raw.githubusercontent.com/ltdrdata/ComfyUI-Manager/main/custom-node-list.json +ComfyUI-Manager +ComfyUI-Impact-Pack +ComfyUI-Inspire-Pack +ComfyUI_experiments +ComfyUI-SAI_API +stability-ComfyUI-nodes +comfyui_controlnet_aux +ComfyUI-Frame-Interpolation +ComfyUI-Loopchain +``` + +`[installed|enabled|not-installed|disabled|all|snapshot|snapshot-list]` + * `enabled`, `disabled`: Shows nodes that have been enabled or disabled among the installed custom nodes. + * `installed`: Shows all nodes that have been installed, regardless of whether they are enabled or disabled. + * `not-installed`: Shows a list of custom nodes that have not been installed. + * `all`: Shows a list of all custom nodes. + * `snapshot`: Displays snapshot information of the currently installed custom nodes. When viewed with `show`, it is displayed in JSON format, and with `simple-show`, it is displayed simply, along with the commit hash. + * `snapshot-list`: Shows a list of snapshot files stored in ComfyUI-Manager/snapshots. + +### 3. Managing Custom Nodes + +`[install|reinstall|uninstall|update|disable|enable|fix] node_name ... ?[--channel ] ?[--mode [remote|local|cache]]` + +* You can apply management functions by listing the names of custom nodes, such as `python cm-cli.py install ComfyUI-Impact-Pack ComfyUI-Inspire-Pack ComfyUI_experiments`. +* The names of the custom nodes are as shown by `show` and are the names of the git repositories. +(Plans are to update the use of nicknames in the future.) + +`[update|disable|enable|fix] all ?[--channel ] ?[--mode [remote|local|cache]]` + +* The `update, disable, enable, fix` functions can be specified for all. + +* Detailed Operations + * `install`: Installs the specified custom nodes. + * `reinstall`: Removes and then reinstalls the specified custom nodes. + * `uninstall`: Uninstalls the specified custom nodes. + * `update`: Updates the specified custom nodes. + * `disable`: Disables the specified custom nodes. + * `enable`: Enables the specified custom nodes. + * `fix`: Attempts to fix dependencies for the specified custom nodes. + + +### 4. Snapshot Management +* `python cm-cli.py save-snapshot [--output ]`: Saves the current snapshot. + * With `--output`, you can save a file in .yaml format to any specified path. +* `python cm-cli.py restore-snapshot `: Restores to the specified snapshot. + * If a file exists at the snapshot path, that snapshot is loaded. + * If no file exists at the snapshot path, it is implicitly assumed to be in ComfyUI-Manager/snapshots. + * `--pip-non-url`: Restore for pip packages registered on PyPI. + * `--pip-non-local-url`: Restore for pip packages registered at web URLs. + * `--pip-local-url`: Restore for pip packages specified by local paths. + * `--user-directory`: Set the user directory. + * `--restore-to`: The path where the restored custom nodes will be installed. (When this option is applied, only the custom nodes installed in the target path are recognized as installed.) + +### 5. CLI Only Mode + +You can set whether to use ComfyUI-Manager solely via CLI. + +`cli-only-mode [enable|disable]` + +* This mode can be used if you want to restrict the use of ComfyUI-Manager through the GUI for security or policy reasons. + * When CLI only mode is enabled, ComfyUI-Manager is loaded in a very restricted state, the internal web API is disabled, and the Manager button is not displayed in the main menu. + +### 6. Dependency Restoration + +`restore-dependencies` + +* This command can be used if custom nodes are installed under the `ComfyUI/custom_nodes` path but their dependencies are not installed. +* It is useful when starting a new cloud instance, like colab, where dependencies need to be reinstalled and installation scripts re-executed. +* It can also be utilized if ComfyUI is reinstalled and only the custom_nodes path has been backed up and restored. + +### 7. Clear + +In the GUI, installations, updates, or snapshot restorations are scheduled to execute the next time ComfyUI is launched. The `clear` command clears this scheduled state, ensuring no pre-execution actions are applied. \ No newline at end of file diff --git a/ComfyUI/custom_nodes/ComfyUI-Manager/docs/en/use_aria2.md b/ComfyUI/custom_nodes/ComfyUI-Manager/docs/en/use_aria2.md new file mode 100644 index 0000000000000000000000000000000000000000..10a7c6dd55fbb96874e4159af976a8125f836f24 --- /dev/null +++ b/ComfyUI/custom_nodes/ComfyUI-Manager/docs/en/use_aria2.md @@ -0,0 +1,40 @@ +# Use `aria2` as downloader + +Two environment variables are needed to use `aria2` as the downloader. + +```bash +export COMFYUI_MANAGER_ARIA2_SERVER=http://127.0.0.1:6800 +export COMFYUI_MANAGER_ARIA2_SECRET=__YOU_MUST_CHANGE_IT__ +``` + +An example `docker-compose.yml` + +```yaml +services: + + aria2: + container_name: aria2 + image: p3terx/aria2-pro + environment: + - PUID=1000 + - PGID=1000 + - UMASK_SET=022 + - RPC_SECRET=__YOU_MUST_CHANGE_IT__ + - RPC_PORT=5080 + - DISK_CACHE=64M + - IPV6_MODE=false + - UPDATE_TRACKERS=false + - CUSTOM_TRACKER_URL= + volumes: + - ./config:/config + - ./downloads:/downloads + - ~/ComfyUI/models:/models + - ~/ComfyUI/custom_nodes:/custom_nodes + ports: + - 6800:6800 + restart: unless-stopped + logging: + driver: json-file + options: + max-size: 1m +``` diff --git a/ComfyUI/custom_nodes/ComfyUI-Manager/docs/ko/cm-cli.md b/ComfyUI/custom_nodes/ComfyUI-Manager/docs/ko/cm-cli.md new file mode 100644 index 0000000000000000000000000000000000000000..6d6a74a36c770c84c979daa1675ddd144ac79894 --- /dev/null +++ b/ComfyUI/custom_nodes/ComfyUI-Manager/docs/ko/cm-cli.md @@ -0,0 +1,150 @@ +# `cm-cli`: ComfyUI-Manager CLI + +`cm-cli` 는 ComfyUI를 실행시키지 않고 command line에서 ComfyUI-Manager의 여러가지 기능을 사용할 수 있도록 도와주는 도구입니다. + + +``` +-= ComfyUI-Manager CLI (V2.24) =- + + +python cm-cli.py [OPTIONS] + +OPTIONS: + [install|reinstall|uninstall|update|disable|enable|fix] node_name ... ?[--channel ] ?[--mode [remote|local|cache]] + [update|disable|enable|fix] all ?[--channel ] ?[--mode [remote|local|cache]] + [simple-show|show] [installed|enabled|not-installed|disabled|all|snapshot|snapshot-list] ?[--channel ] ?[--mode [remote|local|cache]] + save-snapshot ?[--output ] + restore-snapshot ?[--pip-non-url] ?[--pip-non-local-url] ?[--pip-local-url] + cli-only-mode [enable|disable] + restore-dependencies + clear +``` + +## How To Use? +* `python cm-cli.py` 를 통해서 실행 시킬 수 있습니다. +* 예를 들어 custom node를 모두 업데이트 하고 싶다면 + * ComfyUI-Manager경로 에서 `python cm-cli.py update all` 를 command를 실행할 수 있습니다. + * ComfyUI 경로에서 실행한다면, `python custom_nodes/ComfyUI-Manager/cm-cli.py update all` 와 같이 cm-cli.py 의 경로를 지정할 수도 있습니다. + +## Prerequisite +* ComfyUI 를 실행하는 python과 동일한 python 환경에서 실행해야 합니다. + * venv를 사용할 경우 해당 venv를 activate 한 상태에서 실행해야 합니다. + * portable 버전을 사용할 경우 run_nvidia_gpu.bat 파일이 있는 경로인 경우, 다음과 같은 방식으로 코맨드를 실행해야 합니다. + `.\python_embeded\python.exe ComfyUI\custom_nodes\ComfyUI-Manager\cm-cli.py update all` +* ComfyUI 의 경로는 COMFYUI_PATH 환경 변수로 설정할 수 있습니다. 만약 생략할 경우 다음과 같은 경고 메시지가 나타나며, ComfyUI-Manager가 설치된 경로를 기준으로 상대 경로로 설정됩니다. + ``` + WARN: The `COMFYUI_PATH` environment variable is not set. Assuming `custom_nodes/ComfyUI-Manager/../../` as the ComfyUI path. + ``` + +## Features + +### 1. --channel, --mode +* 정보 보기 기능과 커스텀 노드 관리 기능의 경우는 --channel과 --mode를 통해 정보 DB를 설정할 수 있습니다. +* 예들 들어 `python cm-cli.py update all --channel recent --mode remote`와 같은 command를 실행할 경우, 현재 ComfyUI-Manager repo에 내장된 로컬의 정보가 아닌 remote의 최신 정보를 기준으로 동작하며, recent channel에 있는 목록을 대상으로만 동작합니다. +* --channel, --mode 는 `simple-show, show, install, uninstall, update, disable, enable, fix` command에서만 사용 가능합니다. + +### 2. 관리 정보 보기 + +`[simple-show|show] [installed|enabled|not-installed|disabled|all|snapshot|snapshot-list] ?[--channel ] ?[--mode [remote|local|cache]]` + + +* `[show|simple-show]` - `show`는 상세하게 정보를 보여주며, `simple-show`는 간단하게 정보를 보여줍니다. + + +`python cm-cli.py show installed` 와 같은 코맨드를 실행하면 설치된 커스텀 노드의 정보를 상세하게 보여줍니다. +``` +-= ComfyUI-Manager CLI (V2.24) =- + +FETCH DATA from: https://raw.githubusercontent.com/ltdrdata/ComfyUI-Manager/main/custom-node-list.json +[ ENABLED ] ComfyUI-Manager (author: Dr.Lt.Data) +[ ENABLED ] ComfyUI-Impact-Pack (author: Dr.Lt.Data) +[ ENABLED ] ComfyUI-Inspire-Pack (author: Dr.Lt.Data) +[ ENABLED ] ComfyUI_experiments (author: comfyanonymous) +[ ENABLED ] ComfyUI-SAI_API (author: Stability-AI) +[ ENABLED ] stability-ComfyUI-nodes (author: Stability-AI) +[ ENABLED ] comfyui_controlnet_aux (author: Fannovel16) +[ ENABLED ] ComfyUI-Frame-Interpolation (author: Fannovel16) +[ DISABLED ] ComfyUI-Loopchain (author: Fannovel16) +``` + +`python cm-cli.py simple-show installed` 와 같은 코맨드를 이용해서 설치된 커스텀 노드의 정보를 간단하게 보여줍니다. + +``` +-= ComfyUI-Manager CLI (V2.24) =- + +FETCH DATA from: https://raw.githubusercontent.com/ltdrdata/ComfyUI-Manager/main/custom-node-list.json +ComfyUI-Manager +ComfyUI-Impact-Pack +ComfyUI-Inspire-Pack +ComfyUI_experiments +ComfyUI-SAI_API +stability-ComfyUI-nodes +comfyui_controlnet_aux +ComfyUI-Frame-Interpolation +ComfyUI-Loopchain +``` + +* `[installed|enabled|not-installed|disabled|all|snapshot|snapshot-list]` + * `enabled`, `disabled`: 설치된 커스텀 노드들 중 enable 되었거나, disable된 노드들을 보여줍니다. + * `installed`: enable, disable 여부와 상관없이 설치된 모든 노드를 보여줍니다 + * `not-installed`: 설치되지 않은 커스텀 노드의 목록을 보여줍니다. + * `all`: 모든 커스텀 노드의 목록을 보여줍니다. + * `snapshot`: 현재 설치된 커스텀 노드의 snapshot 정보를 보여줍니다. `show`롤 통해서 볼 경우는 json 출력 형태로 보여주며, `simple-show`를 통해서 볼 경우는 간단하게, 커밋 해시와 함께 보여줍니다. + * `snapshot-list`: ComfyUI-Manager/snapshots 에 저장된 snapshot 파일의 목록을 보여줍니다. + +### 3. 커스텀 노드 관리 하기 + +`[install|reinstall|uninstall|update|disable|enable|fix] node_name ... ?[--channel ] ?[--mode [remote|local|cache]]` + +* `python cm-cli.py install ComfyUI-Impact-Pack ComfyUI-Inspire-Pack ComfyUI_experiments` 와 같이 커스텀 노드의 이름을 나열해서 관리 기능을 적용할 수 있습니다. +* 커스텀 노드의 이름은 `show`를 했을 때 보여주는 이름이며, git repository의 이름입니다. +(추후 nickname 을 사용가능하돌고 업데이트 할 예정입니다.) + +`[update|disable|enable|fix] all ?[--channel ] ?[--mode [remote|local|cache]]` + +* `update, disable, enable, fix` 기능은 all 로 지정 가능합니다. + +* 세부 동작 + * `install`: 지정된 커스텀 노드들을 설치합니다 + * `reinstall`: 지정된 커스텀 노드를 삭제하고 재설치 합니다. + * `uninstall`: 지정된 커스텀 노드들을 삭제합니다. + * `update`: 지정된 커스텀 노드들을 업데이트합니다. + * `disable`: 지정된 커스텀 노드들을 비활성화합니다. + * `enable`: 지정된 커스텀 노드들을 활성화합니다. + * `fix`: 지정된 커스텀 노드의 의존성을 고치기 위한 시도를 합니다. + + +### 4. 스냅샷 관리 기능 +* `python cm-cli.py save-snapshot ?[--output ]`: 현재의 snapshot을 저장합니다. + * --output 으로 임의의 경로에 .yaml 파일과 format으로 저장할 수 있습니다. +* `python cm-cli.py restore-snapshot `: 지정된 snapshot으로 복구합니다. + * snapshot 경로에 파일이 존재하는 경우 해당 snapshot을 로드합니다. + * snapshot 경로에 파일이 존재하지 않는 경우 묵시적으로, ComfyUI-Manager/snapshots 에 있다고 가정합니다. + * `--pip-non-url`: PyPI 에 등록된 pip 패키지들에 대해서 복구를 수행 + * `--pip-non-local-url`: web URL에 등록된 pip 패키지들에 대해서 복구를 수행 + * `--pip-local-url`: local 경로를 지정하고 있는 pip 패키지들에 대해서 복구를 수행 + * `--user-directory`: 사용자 디렉토리 설정 + * `--restore-to`: 복구될 커스텀 노드가 설치될 경로. (이 옵션을 적용할 경우 오직 대상 경로에 설치된 custom nodes 만 설치된 것으로 인식함.) + +### 5. CLI only mode + +ComfyUI-Manager를 CLI로만 사용할 것인지를 설정할 수 있습니다. + +`cli-only-mode [enable|disable]` + +* security 혹은 policy 의 이유로 GUI 를 통한 ComfyUI-Manager 사용을 제한하고 싶은 경우 이 모드를 사용할 수 있습니다. + * CLI only mode를 적용할 경우 ComfyUI-Manager 가 매우 제한된 상태로 로드되어, 내부적으로 제공하는 web API가 비활성화 되며, 메인 메뉴에서도 Manager 버튼이 표시되지 않습니다. + + +### 6. 의존성 설치 + +`restore-dependencies` + +* `ComfyUI/custom_nodes` 하위 경로에 커스텀 노드들이 설치되어 있긴 하지만, 의존성이 설치되지 않은 경우 사용할 수 있습니다. +* colab 과 같이 cloud instance를 새로 시작하는 경우 의존성 재설치 및 설치 스크립트가 재실행 되어야 하는 경우 사용합니다. +* ComfyUI을 재설치할 경우, custom_nodes 경로만 백업했다가 재설치 할 경우 활용 가능합니다. + + +### 7. clear + +GUI에서 install, update를 하거나 snapshot 을 restore하는 경우 예약을 통해서 다음번 ComfyUI를 실행할 경우 실행되는 구조입니다. `clear` 는 이런 예약 상태를 clear해서, 아무런 사전 실행이 적용되지 않도록 합니다. \ No newline at end of file diff --git a/ComfyUI/custom_nodes/ComfyUI-Manager/glob/manager_downloader.py b/ComfyUI/custom_nodes/ComfyUI-Manager/glob/manager_downloader.py new file mode 100644 index 0000000000000000000000000000000000000000..2718de5013b9b8a37440b3b3eac2eaa325285f78 --- /dev/null +++ b/ComfyUI/custom_nodes/ComfyUI-Manager/glob/manager_downloader.py @@ -0,0 +1,163 @@ +import os +from urllib.parse import urlparse +import urllib +import sys +import logging +import requests +from huggingface_hub import HfApi +from tqdm.auto import tqdm + + +aria2 = os.getenv('COMFYUI_MANAGER_ARIA2_SERVER') +HF_ENDPOINT = os.getenv('HF_ENDPOINT') + + +if aria2 is not None: + secret = os.getenv('COMFYUI_MANAGER_ARIA2_SECRET') + url = urlparse(aria2) + port = url.port + host = url.scheme + '://' + url.hostname + import aria2p + + aria2 = aria2p.API(aria2p.Client(host=host, port=port, secret=secret)) + + +def basic_download_url(url, dest_folder: str, filename: str): + ''' + Download file from url to dest_folder with filename + using requests library. + ''' + import requests + + # Ensure the destination folder exists + if not os.path.exists(dest_folder): + os.makedirs(dest_folder) + + # Full path to save the file + dest_path = os.path.join(dest_folder, filename) + + # Download the file + response = requests.get(url, stream=True) + if response.status_code == 200: + with open(dest_path, 'wb') as file: + for chunk in response.iter_content(chunk_size=1024): + if chunk: + file.write(chunk) + else: + raise Exception(f"Failed to download file from {url}") + + +def download_url(model_url: str, model_dir: str, filename: str): + if HF_ENDPOINT: + model_url = model_url.replace('https://huggingface.co', HF_ENDPOINT) + logging.info(f"model_url replaced by HF_ENDPOINT, new = {model_url}") + if aria2: + return aria2_download_url(model_url, model_dir, filename) + else: + from torchvision.datasets.utils import download_url as torchvision_download_url + try: + return torchvision_download_url(model_url, model_dir, filename) + except Exception as e: + logging.error(f"[ComfyUI-Manager] Failed to download: {model_url} / {repr(e)}") + raise + + +def aria2_find_task(dir: str, filename: str): + target = os.path.join(dir, filename) + + downloads = aria2.get_downloads() + + for download in downloads: + for file in download.files: + if file.is_metadata: + continue + if str(file.path) == target: + return download + + +def aria2_download_url(model_url: str, model_dir: str, filename: str): + import manager_core as core + import tqdm + import time + + if model_dir.startswith(core.comfy_path): + model_dir = model_dir[len(core.comfy_path) :] + + download_dir = model_dir if model_dir.startswith('/') else os.path.join('/models', model_dir) + + download = aria2_find_task(download_dir, filename) + if download is None: + options = {'dir': download_dir, 'out': filename} + download = aria2.add(model_url, options)[0] + + if download.is_active: + with tqdm.tqdm( + total=download.total_length, + bar_format='{l_bar}{bar}{r_bar}', + desc=filename, + unit='B', + unit_scale=True, + ) as progress_bar: + while download.is_active: + if progress_bar.total == 0 and download.total_length != 0: + progress_bar.reset(download.total_length) + progress_bar.update(download.completed_length - progress_bar.n) + time.sleep(1) + download.update() + + +def download_url_with_agent(url, save_path): + try: + headers = { + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3'} + + req = urllib.request.Request(url, headers=headers) + response = urllib.request.urlopen(req) + data = response.read() + + if not os.path.exists(os.path.dirname(save_path)): + os.makedirs(os.path.dirname(save_path)) + + with open(save_path, 'wb') as f: + f.write(data) + + except Exception as e: + print(f"Download error: {url} / {e}", file=sys.stderr) + return False + + print("Installation was successful.") + return True + +# NOTE: snapshot_download doesn't provide file size tqdm. +def download_repo_in_bytes(repo_id, local_dir): + api = HfApi() + repo_info = api.repo_info(repo_id=repo_id, files_metadata=True) + + os.makedirs(local_dir, exist_ok=True) + + total_size = 0 + for file_info in repo_info.siblings: + if file_info.size is not None: + total_size += file_info.size + + pbar = tqdm(total=total_size, unit="B", unit_scale=True, desc="Downloading") + + for file_info in repo_info.siblings: + out_path = os.path.join(local_dir, file_info.rfilename) + os.makedirs(os.path.dirname(out_path), exist_ok=True) + + if file_info.size is None: + continue + + download_url = f"https://huggingface.co/{repo_id}/resolve/main/{file_info.rfilename}" + + with requests.get(download_url, stream=True) as r, open(out_path, "wb") as f: + r.raise_for_status() + for chunk in r.iter_content(chunk_size=65536): + if chunk: + f.write(chunk) + pbar.update(len(chunk)) + + pbar.close() + + diff --git a/ComfyUI/custom_nodes/ComfyUI-Manager/glob/manager_server.py b/ComfyUI/custom_nodes/ComfyUI-Manager/glob/manager_server.py new file mode 100644 index 0000000000000000000000000000000000000000..cb3bcd92e8a7c0f78fc1f3541b16ef8259944913 --- /dev/null +++ b/ComfyUI/custom_nodes/ComfyUI-Manager/glob/manager_server.py @@ -0,0 +1,1777 @@ +import traceback + +import folder_paths +import locale +import subprocess # don't remove this +import concurrent +import nodes +import os +import sys +import threading +import re +import shutil +import git +from datetime import datetime + +from server import PromptServer +import manager_core as core +import manager_util +import cm_global +import logging +import asyncio +import queue + +import manager_downloader + + +logging.info(f"### Loading: ComfyUI-Manager ({core.version_str})") +logging.info("[ComfyUI-Manager] network_mode: " + core.get_config()['network_mode']) + +comfy_ui_hash = "-" +comfyui_tag = None + +SECURITY_MESSAGE_MIDDLE_OR_BELOW = "ERROR: To use this action, a security_level of `middle or below` is required. Please contact the administrator.\nReference: https://github.com/ltdrdata/ComfyUI-Manager#security-policy" +SECURITY_MESSAGE_NORMAL_MINUS = "ERROR: To use this feature, you must either set '--listen' to a local IP and set the security level to 'normal-' or lower, or set the security level to 'middle' or 'weak'. Please contact the administrator.\nReference: https://github.com/ltdrdata/ComfyUI-Manager#security-policy" +SECURITY_MESSAGE_GENERAL = "ERROR: This installation is not allowed in this security_level. Please contact the administrator.\nReference: https://github.com/ltdrdata/ComfyUI-Manager#security-policy" +SECURITY_MESSAGE_NORMAL_MINUS_MODEL = "ERROR: Downloading models that are not in '.safetensors' format is only allowed for models registered in the 'default' channel at this security level. If you want to download this model, set the security level to 'normal-' or lower." + +routes = PromptServer.instance.routes + +def handle_stream(stream, prefix): + stream.reconfigure(encoding=locale.getpreferredencoding(), errors='replace') + for msg in stream: + if prefix == '[!]' and ('it/s]' in msg or 's/it]' in msg) and ('%|' in msg or 'it [' in msg): + if msg.startswith('100%'): + print('\r' + msg, end="", file=sys.stderr), + else: + print('\r' + msg[:-1], end="", file=sys.stderr), + else: + if prefix == '[!]': + print(prefix, msg, end="", file=sys.stderr) + else: + print(prefix, msg, end="") + + +from comfy.cli_args import args +import latent_preview + +def is_loopback(address): + import ipaddress + try: + return ipaddress.ip_address(address).is_loopback + except ValueError: + return False + +is_local_mode = is_loopback(args.listen) + + +model_dir_name_map = { + "checkpoints": "checkpoints", + "checkpoint": "checkpoints", + "unclip": "checkpoints", + "text_encoders": "text_encoders", + "clip": "text_encoders", + "vae": "vae", + "lora": "loras", + "t2i-adapter": "controlnet", + "t2i-style": "controlnet", + "controlnet": "controlnet", + "clip_vision": "clip_vision", + "gligen": "gligen", + "upscale": "upscale_models", + "embedding": "embeddings", + "embeddings": "embeddings", + "unet": "diffusion_models", + "diffusion_model": "diffusion_models", +} + + +def is_allowed_security_level(level): + if level == 'block': + return False + elif level == 'high': + if is_local_mode: + return core.get_config()['security_level'] in ['weak', 'normal-'] + else: + return core.get_config()['security_level'] == 'weak' + elif level == 'middle': + return core.get_config()['security_level'] in ['weak', 'normal', 'normal-'] + else: + return True + + +async def get_risky_level(files, pip_packages): + json_data1 = await core.get_data_by_mode('local', 'custom-node-list.json') + json_data2 = await core.get_data_by_mode('cache', 'custom-node-list.json', channel_url='https://raw.githubusercontent.com/ltdrdata/ComfyUI-Manager/main') + + all_urls = set() + for x in json_data1['custom_nodes'] + json_data2['custom_nodes']: + all_urls.update(x.get('files', [])) + + for x in files: + if x not in all_urls: + return "high" + + all_pip_packages = set() + for x in json_data1['custom_nodes'] + json_data2['custom_nodes']: + all_pip_packages.update(x.get('pip', [])) + + for p in pip_packages: + if p not in all_pip_packages: + return "block" + + return "middle" + + +class ManagerFuncsInComfyUI(core.ManagerFuncs): + def get_current_preview_method(self): + if args.preview_method == latent_preview.LatentPreviewMethod.Auto: + return "auto" + elif args.preview_method == latent_preview.LatentPreviewMethod.Latent2RGB: + return "latent2rgb" + elif args.preview_method == latent_preview.LatentPreviewMethod.TAESD: + return "taesd" + else: + return "none" + + def run_script(self, cmd, cwd='.'): + if len(cmd) > 0 and cmd[0].startswith("#"): + logging.error(f"[ComfyUI-Manager] Unexpected behavior: `{cmd}`") + return 0 + + process = subprocess.Popen(cmd, cwd=cwd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, bufsize=1, env=core.get_script_env()) + + stdout_thread = threading.Thread(target=handle_stream, args=(process.stdout, "")) + stderr_thread = threading.Thread(target=handle_stream, args=(process.stderr, "[!]")) + + stdout_thread.start() + stderr_thread.start() + + stdout_thread.join() + stderr_thread.join() + + return process.wait() + + +core.manager_funcs = ManagerFuncsInComfyUI() + +sys.path.append('../..') + +from manager_downloader import download_url, download_url_with_agent + +core.comfy_path = os.path.dirname(folder_paths.__file__) +core.js_path = os.path.join(core.comfy_path, "web", "extensions") + +local_db_model = os.path.join(manager_util.comfyui_manager_path, "model-list.json") +local_db_alter = os.path.join(manager_util.comfyui_manager_path, "alter-list.json") +local_db_custom_node_list = os.path.join(manager_util.comfyui_manager_path, "custom-node-list.json") +local_db_extension_node_mappings = os.path.join(manager_util.comfyui_manager_path, "extension-node-map.json") + + +def set_preview_method(method): + if method == 'auto': + args.preview_method = latent_preview.LatentPreviewMethod.Auto + elif method == 'latent2rgb': + args.preview_method = latent_preview.LatentPreviewMethod.Latent2RGB + elif method == 'taesd': + args.preview_method = latent_preview.LatentPreviewMethod.TAESD + else: + args.preview_method = latent_preview.LatentPreviewMethod.NoPreviews + + core.get_config()['preview_method'] = method + + +if args.preview_method == latent_preview.LatentPreviewMethod.NoPreviews: + set_preview_method(core.get_config()['preview_method']) +else: + logging.warning("[ComfyUI-Manager] Since --preview-method is set, ComfyUI-Manager's preview method feature will be ignored.") + + +def set_component_policy(mode): + core.get_config()['component_policy'] = mode + +def set_update_policy(mode): + core.get_config()['update_policy'] = mode + +def set_db_mode(mode): + core.get_config()['db_mode'] = mode + +def print_comfyui_version(): + global comfy_ui_hash + global comfyui_tag + + is_detached = False + try: + repo = git.Repo(os.path.dirname(folder_paths.__file__)) + core.comfy_ui_revision = len(list(repo.iter_commits('HEAD'))) + + comfy_ui_hash = repo.head.commit.hexsha + cm_global.variables['comfyui.revision'] = core.comfy_ui_revision + + core.comfy_ui_commit_datetime = repo.head.commit.committed_datetime + cm_global.variables['comfyui.commit_datetime'] = core.comfy_ui_commit_datetime + + is_detached = repo.head.is_detached + current_branch = repo.active_branch.name + + comfyui_tag = core.get_comfyui_tag() + + try: + if not os.environ.get('__COMFYUI_DESKTOP_VERSION__') and core.comfy_ui_commit_datetime.date() < core.comfy_ui_required_commit_datetime.date(): + logging.warning(f"\n\n## [WARN] ComfyUI-Manager: Your ComfyUI version ({core.comfy_ui_revision})[{core.comfy_ui_commit_datetime.date()}] is too old. Please update to the latest version. ##\n\n") + except: + pass + + # process on_revision_detected --> + if 'cm.on_revision_detected_handler' in cm_global.variables: + for k, f in cm_global.variables['cm.on_revision_detected_handler']: + try: + f(core.comfy_ui_revision) + except Exception: + logging.error(f"[ERROR] '{k}' on_revision_detected_handler") + traceback.print_exc() + + del cm_global.variables['cm.on_revision_detected_handler'] + else: + logging.warning("[ComfyUI-Manager] Some features are restricted due to your ComfyUI being outdated.") + # <-- + + if current_branch == "master": + if comfyui_tag: + logging.info(f"### ComfyUI Version: {comfyui_tag} | Released on '{core.comfy_ui_commit_datetime.date()}'") + else: + logging.info(f"### ComfyUI Revision: {core.comfy_ui_revision} [{comfy_ui_hash[:8]}] | Released on '{core.comfy_ui_commit_datetime.date()}'") + else: + if comfyui_tag: + logging.info(f"### ComfyUI Version: {comfyui_tag} on '{current_branch}' | Released on '{core.comfy_ui_commit_datetime.date()}'") + else: + logging.info(f"### ComfyUI Revision: {core.comfy_ui_revision} on '{current_branch}' [{comfy_ui_hash[:8]}] | Released on '{core.comfy_ui_commit_datetime.date()}'") + except: + if is_detached: + logging.info(f"### ComfyUI Revision: {core.comfy_ui_revision} [{comfy_ui_hash[:8]}] *DETACHED | Released on '{core.comfy_ui_commit_datetime.date()}'") + else: + logging.info("### ComfyUI Revision: UNKNOWN (The currently installed ComfyUI is not a Git repository)") + + +print_comfyui_version() +core.check_invalid_nodes() + + + +def setup_environment(): + git_exe = core.get_config()['git_exe'] + + if git_exe != '': + git.Git().update_environment(GIT_PYTHON_GIT_EXECUTABLE=git_exe) + + +setup_environment() + +# Expand Server api + +from aiohttp import web +import aiohttp +import json +import zipfile +import urllib.request + + +def get_model_dir(data, show_log=False): + if 'download_model_base' in folder_paths.folder_names_and_paths: + models_base = folder_paths.folder_names_and_paths['download_model_base'][0][0] + else: + models_base = folder_paths.models_dir + + # NOTE: Validate to prevent path traversal. + if any(char in data['filename'] for char in {'/', '\\', ':'}): + return None + + def resolve_custom_node(save_path): + save_path = save_path[13:] # remove 'custom_nodes/' + + # NOTE: Validate to prevent path traversal. + if save_path.startswith(os.path.sep) or ':' in save_path: + return None + + repo_name = save_path.replace('\\','/').split('/')[0] # get custom node repo name + + # NOTE: The creation of files within the custom node path should be removed in the future. + repo_path = core.lookup_installed_custom_nodes_legacy(repo_name) + if repo_path is not None and repo_path[0]: + # Returns the retargeted path based on the actually installed repository + return os.path.join(os.path.dirname(repo_path[1]), save_path) + else: + return None + + if data['save_path'] != 'default': + if '..' in data['save_path'] or data['save_path'].startswith('/'): + if show_log: + logging.info(f"[WARN] '{data['save_path']}' is not allowed path. So it will be saved into 'models/etc'.") + base_model = os.path.join(models_base, "etc") + else: + if data['save_path'].startswith("custom_nodes"): + base_model = resolve_custom_node(data['save_path']) + if base_model is None: + if show_log: + logging.info(f"[ComfyUI-Manager] The target custom node for model download is not installed: {data['save_path']}") + return None + else: + base_model = os.path.join(models_base, data['save_path']) + else: + model_dir_name = model_dir_name_map.get(data['type'].lower()) + if model_dir_name is not None: + base_model = folder_paths.folder_names_and_paths[model_dir_name][0][0] + else: + base_model = os.path.join(models_base, "etc") + + return base_model + + +def get_model_path(data, show_log=False): + base_model = get_model_dir(data, show_log) + if base_model is None: + return None + else: + if data['filename'] == '': + return os.path.join(base_model, os.path.basename(data['url'])) + else: + return os.path.join(base_model, data['filename']) + + +def check_state_of_git_node_pack(node_packs, do_fetch=False, do_update_check=True, do_update=False): + if do_fetch: + print("Start fetching...", end="") + elif do_update: + print("Start updating...", end="") + elif do_update_check: + print("Start update check...", end="") + + def process_custom_node(item): + core.check_state_of_git_node_pack_single(item, do_fetch, do_update_check, do_update) + + with concurrent.futures.ThreadPoolExecutor(4) as executor: + for k, v in node_packs.items(): + if v.get('active_version') in ['unknown', 'nightly']: + executor.submit(process_custom_node, v) + + if do_fetch: + print("\x1b[2K\rFetching done.") + elif do_update: + update_exists = any(item.get('updatable', False) for item in node_packs.values()) + if update_exists: + print("\x1b[2K\rUpdate done.") + else: + print("\x1b[2K\rAll extensions are already up-to-date.") + elif do_update_check: + print("\x1b[2K\rUpdate check done.") + + +def nickname_filter(json_obj): + preemptions_map = {} + + for k, x in json_obj.items(): + if 'preemptions' in x[1]: + for y in x[1]['preemptions']: + preemptions_map[y] = k + elif k.endswith("/ComfyUI"): + for y in x[0]: + preemptions_map[y] = k + + updates = {} + for k, x in json_obj.items(): + removes = set() + for y in x[0]: + k2 = preemptions_map.get(y) + if k2 is not None and k != k2: + removes.add(y) + + if len(removes) > 0: + updates[k] = [y for y in x[0] if y not in removes] + + for k, v in updates.items(): + json_obj[k][0] = v + + return json_obj + + +task_queue = queue.Queue() +nodepack_result = {} +model_result = {} +tasks_in_progress = set() +task_worker_lock = threading.Lock() + +async def task_worker(): + global task_queue + global nodepack_result + global model_result + global tasks_in_progress + + async def do_install(item) -> str: + ui_id, node_spec_str, channel, mode, skip_post_install = item + + try: + node_spec = core.unified_manager.resolve_node_spec(node_spec_str) + if node_spec is None: + logging.error(f"Cannot resolve install target: '{node_spec_str}'") + return f"Cannot resolve install target: '{node_spec_str}'" + + node_name, version_spec, is_specified = node_spec + res = await core.unified_manager.install_by_id(node_name, version_spec, channel, mode, return_postinstall=skip_post_install) + # discard post install if skip_post_install mode + + if res.action not in ['skip', 'enable', 'install-git', 'install-cnr', 'switch-cnr']: + logging.error(f"[ComfyUI-Manager] Installation failed:\n{res.msg}") + return res.msg + + elif not res.result: + logging.error(f"[ComfyUI-Manager] Installation failed:\n{res.msg}") + return res.msg + + return 'success' + except Exception: + traceback.print_exc() + return f"Installation failed:\n{node_spec_str}" + + async def do_update(item): + ui_id, node_name, node_ver = item + + try: + res = core.unified_manager.unified_update(node_name, node_ver) + + if res.ver == 'unknown': + url = core.unified_manager.unknown_active_nodes[node_name][0] + try: + title = os.path.basename(url) + except Exception: + title = node_name + else: + url = core.unified_manager.cnr_map[node_name].get('repository') + title = core.unified_manager.cnr_map[node_name]['name'] + + manager_util.clear_pip_cache() + + if url is not None: + base_res = {'url': url, 'title': title} + else: + base_res = {'title': title} + + if res.result: + if res.action == 'skip': + base_res['msg'] = 'skip' + return base_res + else: + base_res['msg'] = 'success' + return base_res + + base_res['msg'] = f"An error occurred while updating '{node_name}'." + logging.error(f"\nERROR: An error occurred while updating '{node_name}'. (res.result={res.result}, res.action={res.action})") + return base_res + except Exception: + traceback.print_exc() + + return {'msg':f"An error occurred while updating '{node_name}'."} + + async def do_update_comfyui(is_stable) -> str: + try: + repo_path = os.path.dirname(folder_paths.__file__) + latest_tag = None + if is_stable: + res, latest_tag = core.update_to_stable_comfyui(repo_path) + else: + res = core.update_path(repo_path) + + if res == "fail": + logging.error("ComfyUI update failed") + return "fail" + elif res == "updated": + if is_stable: + logging.info("ComfyUI is updated to latest stable version.") + return "success-stable-"+latest_tag + else: + logging.info("ComfyUI is updated to latest nightly version.") + return "success-nightly" + else: # skipped + logging.info("ComfyUI is up-to-date.") + return "skip" + + except Exception: + traceback.print_exc() + + return "An error occurred while updating 'comfyui'." + + async def do_fix(item) -> str: + ui_id, node_name, node_ver = item + + try: + res = core.unified_manager.unified_fix(node_name, node_ver) + + if res.result: + return 'success' + else: + logging.error(res.msg) + + logging.error(f"\nERROR: An error occurred while fixing '{node_name}@{node_ver}'.") + except Exception: + traceback.print_exc() + + return f"An error occurred while fixing '{node_name}@{node_ver}'." + + async def do_uninstall(item) -> str: + ui_id, node_name, is_unknown = item + + try: + res = core.unified_manager.unified_uninstall(node_name, is_unknown) + + if res.result: + return 'success' + + logging.error(f"\nERROR: An error occurred while uninstalling '{node_name}'.") + except Exception: + traceback.print_exc() + + return f"An error occurred while uninstalling '{node_name}'." + + async def do_disable(item) -> str: + ui_id, node_name, is_unknown = item + + try: + res = core.unified_manager.unified_disable(node_name, is_unknown) + + if res: + return 'success' + + except Exception: + traceback.print_exc() + + return f"Failed to disable: '{node_name}'" + + async def do_install_model(item) -> str: + ui_id, json_data = item + + model_path = get_model_path(json_data) + model_url = json_data['url'] + + res = False + + try: + if model_path is not None: + logging.info(f"Install model '{json_data['name']}' from '{model_url}' into '{model_path}'") + + if json_data['filename'] == '': + if os.path.exists(os.path.join(model_path, os.path.dirname(json_data['url']))): + logging.error(f"[ComfyUI-Manager] the model path already exists: {model_path}") + return f"The model path already exists: {model_path}" + + logging.info(f"[ComfyUI-Manager] Downloading '{model_url}' into '{model_path}'") + manager_downloader.download_repo_in_bytes(repo_id=model_url, local_dir=model_path) + + return 'success' + + elif not core.get_config()['model_download_by_agent'] and ( + model_url.startswith('https://github.com') or model_url.startswith('https://huggingface.co') or model_url.startswith('https://heibox.uni-heidelberg.de')): + model_dir = get_model_dir(json_data, True) + download_url(model_url, model_dir, filename=json_data['filename']) + if model_path.endswith('.zip'): + res = core.unzip(model_path) + else: + res = True + + if res: + return 'success' + else: + res = download_url_with_agent(model_url, model_path) + if res and model_path.endswith('.zip'): + res = core.unzip(model_path) + else: + logging.error(f"[ComfyUI-Manager] Model installation error: invalid model type - {json_data['type']}") + + if res: + return 'success' + + except Exception as e: + logging.error(f"[ComfyUI-Manager] ERROR: {e}") + + return f"Model installation error: {model_url}" + + stats = {} + + while True: + done_count = len(nodepack_result) + len(model_result) + total_count = done_count + task_queue.qsize() + + if task_queue.empty(): + logging.info(f"\n[ComfyUI-Manager] Queued works are completed.\n{stats}") + + logging.info("\nAfter restarting ComfyUI, please refresh the browser.") + PromptServer.instance.send_sync("cm-queue-status", + {'status': 'done', + 'nodepack_result': nodepack_result, 'model_result': model_result, + 'total_count': total_count, 'done_count': done_count}) + nodepack_result = {} + task_queue = queue.Queue() + return # terminate worker thread + + with task_worker_lock: + kind, item = task_queue.get() + tasks_in_progress.add((kind, item[0])) + + try: + if kind == 'install': + msg = await do_install(item) + elif kind == 'install-model': + msg = await do_install_model(item) + elif kind == 'update': + msg = await do_update(item) + elif kind == 'update-main': + msg = await do_update(item) + elif kind == 'update-comfyui': + msg = await do_update_comfyui(item[1]) + elif kind == 'fix': + msg = await do_fix(item) + elif kind == 'uninstall': + msg = await do_uninstall(item) + elif kind == 'disable': + msg = await do_disable(item) + else: + msg = "Unexpected kind: " + kind + except Exception: + traceback.print_exc() + msg = f"Exception: {(kind, item)}" + + with task_worker_lock: + tasks_in_progress.remove((kind, item[0])) + + ui_id = item[0] + if kind == 'install-model': + model_result[ui_id] = msg + ui_target = "model_manager" + elif kind == 'update-main': + nodepack_result[ui_id] = msg + ui_target = "main" + elif kind == 'update-comfyui': + nodepack_result['comfyui'] = msg + ui_target = "main" + elif kind == 'update': + nodepack_result[ui_id] = msg['msg'] + ui_target = "nodepack_manager" + else: + nodepack_result[ui_id] = msg + ui_target = "nodepack_manager" + + stats[kind] = stats.get(kind, 0) + 1 + + PromptServer.instance.send_sync("cm-queue-status", + {'status': 'in_progress', 'target': item[0], 'ui_target': ui_target, + 'total_count': total_count, 'done_count': done_count}) + + +@routes.get("/customnode/getmappings") +async def fetch_customnode_mappings(request): + """ + provide unified (node -> node pack) mapping list + """ + mode = request.rel_url.query["mode"] + + nickname_mode = False + if mode == "nickname": + mode = "local" + nickname_mode = True + + json_obj = await core.get_data_by_mode(mode, 'extension-node-map.json') + json_obj = core.map_to_unified_keys(json_obj) + + if nickname_mode: + json_obj = nickname_filter(json_obj) + + all_nodes = set() + patterns = [] + for k, x in json_obj.items(): + all_nodes.update(set(x[0])) + + if 'nodename_pattern' in x[1]: + patterns.append((x[1]['nodename_pattern'], x[0])) + + missing_nodes = set(nodes.NODE_CLASS_MAPPINGS.keys()) - all_nodes + + for x in missing_nodes: + for pat, item in patterns: + if re.match(pat, x): + item.append(x) + + return web.json_response(json_obj, content_type='application/json') + + +@routes.get("/customnode/fetch_updates") +async def fetch_updates(request): + try: + if request.rel_url.query["mode"] == "local": + channel = 'local' + else: + channel = core.get_config()['channel_url'] + + await core.unified_manager.reload(request.rel_url.query["mode"]) + await core.unified_manager.get_custom_nodes(channel, request.rel_url.query["mode"]) + + res = core.unified_manager.fetch_or_pull_git_repo(is_pull=False) + + for x in res['failed']: + logging.error(f"FETCH FAILED: {x}") + + logging.info("\nDone.") + + if len(res['updated']) > 0: + return web.Response(status=201) + + return web.Response(status=200) + except: + traceback.print_exc() + return web.Response(status=400) + + +@routes.get("/manager/queue/update_all") +async def update_all(request): + if not is_allowed_security_level('middle'): + logging.error(SECURITY_MESSAGE_MIDDLE_OR_BELOW) + return web.Response(status=403) + + with task_worker_lock: + is_processing = task_worker_thread is not None and task_worker_thread.is_alive() + if is_processing: + return web.Response(status=401) + + await core.save_snapshot_with_postfix('autosave') + + if request.rel_url.query["mode"] == "local": + channel = 'local' + else: + channel = core.get_config()['channel_url'] + + await core.unified_manager.reload(request.rel_url.query["mode"]) + await core.unified_manager.get_custom_nodes(channel, request.rel_url.query["mode"]) + + for k, v in core.unified_manager.active_nodes.items(): + if k == 'comfyui-manager': + # skip updating comfyui-manager if desktop version + if os.environ.get('__COMFYUI_DESKTOP_VERSION__'): + continue + + update_item = k, k, v[0] + task_queue.put(("update-main", update_item)) + + for k, v in core.unified_manager.unknown_active_nodes.items(): + if k == 'comfyui-manager': + # skip updating comfyui-manager if desktop version + if os.environ.get('__COMFYUI_DESKTOP_VERSION__'): + continue + + update_item = k, k, 'unknown' + task_queue.put(("update-main", update_item)) + + return web.Response(status=200) + + +def convert_markdown_to_html(input_text): + pattern_a = re.compile(r'\[a/([^]]+)]\(([^)]+)\)') + pattern_w = re.compile(r'\[w/([^]]+)]') + pattern_i = re.compile(r'\[i/([^]]+)]') + pattern_bold = re.compile(r'\*\*([^*]+)\*\*') + pattern_white = re.compile(r'%%([^*]+)%%') + + def replace_a(match): + return f"{match.group(1)}" + + def replace_w(match): + return f"

{match.group(1)}

" + + def replace_i(match): + return f"

{match.group(1)}

" + + def replace_bold(match): + return f"{match.group(1)}" + + def replace_white(match): + return f"{match.group(1)}" + + input_text = input_text.replace('\\[', '[').replace('\\]', ']').replace('<', '<').replace('>', '>') + + result_text = re.sub(pattern_a, replace_a, input_text) + result_text = re.sub(pattern_w, replace_w, result_text) + result_text = re.sub(pattern_i, replace_i, result_text) + result_text = re.sub(pattern_bold, replace_bold, result_text) + result_text = re.sub(pattern_white, replace_white, result_text) + + return result_text.replace("\n", "
") + + +def populate_markdown(x): + if 'description' in x: + x['description'] = convert_markdown_to_html(manager_util.sanitize_tag(x['description'])) + + if 'name' in x: + x['name'] = manager_util.sanitize_tag(x['name']) + + if 'title' in x: + x['title'] = manager_util.sanitize_tag(x['title']) + + +# freeze imported version +startup_time_installed_node_packs = core.get_installed_node_packs() +@routes.get("/customnode/installed") +async def installed_list(request): + mode = request.query.get('mode', 'default') + + if mode == 'imported': + res = startup_time_installed_node_packs + else: + res = core.get_installed_node_packs() + + return web.json_response(res, content_type='application/json') + + +@routes.get("/customnode/getlist") +async def fetch_customnode_list(request): + """ + provide unified custom node list + """ + if request.rel_url.query.get("skip_update", '').lower() == "true": + skip_update = True + else: + skip_update = False + + if request.rel_url.query["mode"] == "local": + channel = 'local' + else: + channel = core.get_config()['channel_url'] + + node_packs = await core.get_unified_total_nodes(channel, request.rel_url.query["mode"], 'cache') + json_obj_github = core.get_data_by_mode(request.rel_url.query["mode"], 'github-stats.json', 'default') + json_obj_extras = core.get_data_by_mode(request.rel_url.query["mode"], 'extras.json', 'default') + + core.populate_github_stats(node_packs, await json_obj_github) + core.populate_favorites(node_packs, await json_obj_extras) + + check_state_of_git_node_pack(node_packs, not skip_update, do_update_check=not skip_update) + + for v in node_packs.values(): + populate_markdown(v) + + if channel != 'local': + found = 'custom' + + for name, url in core.get_channel_dict().items(): + if url == channel: + found = name + break + + channel = found + + result = dict(channel=channel, node_packs=node_packs.to_dict()) + + return web.json_response(result, content_type='application/json') + + +@routes.get("/customnode/alternatives") +async def fetch_customnode_alternatives(request): + alter_json = await core.get_data_by_mode(request.rel_url.query["mode"], 'alter-list.json') + + res = {} + + for item in alter_json['items']: + populate_markdown(item) + res[item['id']] = item + + res = core.map_to_unified_keys(res) + + return web.json_response(res, content_type='application/json') + + +def check_model_installed(json_obj): + def is_exists(model_dir_name, filename, url): + if filename == '': + filename = os.path.basename(url) + + dirs = folder_paths.get_folder_paths(model_dir_name) + + for x in dirs: + if os.path.exists(os.path.join(x, filename)): + return True + + return False + + model_dir_names = ['checkpoints', 'loras', 'vae', 'text_encoders', 'diffusion_models', 'clip_vision', 'embeddings', + 'diffusers', 'vae_approx', 'controlnet', 'gligen', 'upscale_models', 'hypernetworks', + 'photomaker', 'classifiers'] + + total_models_files = set() + for x in model_dir_names: + for y in folder_paths.get_filename_list(x): + total_models_files.add(y) + + def process_model_phase(item): + if 'diffusion' not in item['filename'] and 'pytorch' not in item['filename'] and 'model' not in item['filename']: + # non-general name case + if item['filename'] in total_models_files: + item['installed'] = 'True' + return + + if item['save_path'] == 'default': + model_dir_name = model_dir_name_map.get(item['type'].lower()) + if model_dir_name is not None: + item['installed'] = str(is_exists(model_dir_name, item['filename'], item['url'])) + else: + item['installed'] = 'False' + else: + model_dir_name = item['save_path'].split('/')[0] + if model_dir_name in folder_paths.folder_names_and_paths: + if is_exists(model_dir_name, item['filename'], item['url']): + item['installed'] = 'True' + + if 'installed' not in item: + if item['filename'] == '': + filename = os.path.basename(item['url']) + else: + filename = item['filename'] + + fullpath = os.path.join(folder_paths.models_dir, item['save_path'], filename) + + item['installed'] = 'True' if os.path.exists(fullpath) else 'False' + + with concurrent.futures.ThreadPoolExecutor(8) as executor: + for item in json_obj['models']: + executor.submit(process_model_phase, item) + + +@routes.get("/externalmodel/getlist") +async def fetch_externalmodel_list(request): + # The model list is only allowed in the default channel, yet. + json_obj = await core.get_data_by_mode(request.rel_url.query["mode"], 'model-list.json') + + check_model_installed(json_obj) + + for x in json_obj['models']: + populate_markdown(x) + + return web.json_response(json_obj, content_type='application/json') + + +@PromptServer.instance.routes.get("/snapshot/getlist") +async def get_snapshot_list(request): + items = [f[:-5] for f in os.listdir(core.manager_snapshot_path) if f.endswith('.json')] + items.sort(reverse=True) + return web.json_response({'items': items}, content_type='application/json') + + +@routes.get("/snapshot/remove") +async def remove_snapshot(request): + if not is_allowed_security_level('middle'): + logging.error(SECURITY_MESSAGE_MIDDLE_OR_BELOW) + return web.Response(status=403) + + try: + target = request.rel_url.query["target"] + + path = os.path.join(core.manager_snapshot_path, f"{target}.json") + if os.path.exists(path): + os.remove(path) + + return web.Response(status=200) + except: + return web.Response(status=400) + + +@routes.get("/snapshot/restore") +async def restore_snapshot(request): + if not is_allowed_security_level('middle'): + logging.error(SECURITY_MESSAGE_MIDDLE_OR_BELOW) + return web.Response(status=403) + + try: + target = request.rel_url.query["target"] + + path = os.path.join(core.manager_snapshot_path, f"{target}.json") + if os.path.exists(path): + if not os.path.exists(core.manager_startup_script_path): + os.makedirs(core.manager_startup_script_path) + + target_path = os.path.join(core.manager_startup_script_path, "restore-snapshot.json") + shutil.copy(path, target_path) + + logging.info(f"Snapshot restore scheduled: `{target}`") + return web.Response(status=200) + + logging.error(f"Snapshot file not found: `{path}`") + return web.Response(status=400) + except: + return web.Response(status=400) + + +@routes.get("/snapshot/get_current") +async def get_current_snapshot_api(request): + try: + return web.json_response(await core.get_current_snapshot(), content_type='application/json') + except: + return web.Response(status=400) + + +@routes.get("/snapshot/save") +async def save_snapshot(request): + try: + await core.save_snapshot_with_postfix('snapshot') + return web.Response(status=200) + except: + return web.Response(status=400) + + +def unzip_install(files): + temp_filename = 'manager-temp.zip' + for url in files: + if url.endswith("/"): + url = url[:-1] + try: + headers = { + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3'} + + req = urllib.request.Request(url, headers=headers) + response = urllib.request.urlopen(req) + data = response.read() + + with open(temp_filename, 'wb') as f: + f.write(data) + + with zipfile.ZipFile(temp_filename, 'r') as zip_ref: + zip_ref.extractall(core.get_default_custom_nodes_path()) + + os.remove(temp_filename) + except Exception as e: + logging.error(f"Install(unzip) error: {url} / {e}", file=sys.stderr) + return False + + logging.info("Installation was successful.") + return True + + +def copy_install(files, js_path_name=None): + for url in files: + if url.endswith("/"): + url = url[:-1] + try: + filename = os.path.basename(url) + if url.endswith(".py"): + download_url(url, core.get_default_custom_nodes_path(), filename) + else: + path = os.path.join(core.js_path, js_path_name) if js_path_name is not None else core.js_path + if not os.path.exists(path): + os.makedirs(path) + download_url(url, path, filename) + + except Exception as e: + logging.error(f"Install(copy) error: {url} / {e}", file=sys.stderr) + return False + + logging.info("Installation was successful.") + return True + + +def copy_uninstall(files, js_path_name='.'): + for url in files: + if url.endswith("/"): + url = url[:-1] + dir_name = os.path.basename(url) + base_path = core.get_default_custom_nodes_path() if url.endswith('.py') else os.path.join(core.js_path, js_path_name) + file_path = os.path.join(base_path, dir_name) + + try: + if os.path.exists(file_path): + os.remove(file_path) + elif os.path.exists(file_path + ".disabled"): + os.remove(file_path + ".disabled") + except Exception as e: + logging.error(f"Uninstall(copy) error: {url} / {e}", file=sys.stderr) + return False + + logging.info("Uninstallation was successful.") + return True + + +def copy_set_active(files, is_disable, js_path_name='.'): + if is_disable: + action_name = "Disable" + else: + action_name = "Enable" + + for url in files: + if url.endswith("/"): + url = url[:-1] + dir_name = os.path.basename(url) + base_path = core.get_default_custom_nodes_path() if url.endswith('.py') else os.path.join(core.js_path, js_path_name) + file_path = os.path.join(base_path, dir_name) + + try: + if is_disable: + current_name = file_path + new_name = file_path + ".disabled" + else: + current_name = file_path + ".disabled" + new_name = file_path + + os.rename(current_name, new_name) + + except Exception as e: + logging.error(f"{action_name}(copy) error: {url} / {e}", file=sys.stderr) + + return False + + logging.info(f"{action_name} was successful.") + return True + + +@routes.get("/customnode/versions/{node_name}") +async def get_cnr_versions(request): + node_name = request.match_info.get("node_name", None) + versions = core.cnr_utils.all_versions_of_node(node_name) + + if versions is not None: + return web.json_response(versions, content_type='application/json') + + return web.Response(status=400) + + +@routes.get("/customnode/disabled_versions/{node_name}") +async def get_disabled_versions(request): + node_name = request.match_info.get("node_name", None) + versions = [] + if node_name in core.unified_manager.nightly_inactive_nodes: + versions.append(dict(version='nightly')) + + for v in core.unified_manager.cnr_inactive_nodes.get(node_name, {}).keys(): + versions.append(dict(version=v)) + + if versions: + return web.json_response(versions, content_type='application/json') + + return web.Response(status=400) + + +@routes.post("/customnode/import_fail_info") +async def import_fail_info(request): + json_data = await request.json() + + if 'cnr_id' in json_data: + module_name = core.unified_manager.get_module_name(json_data['cnr_id']) + else: + module_name = core.unified_manager.get_module_name(json_data['url']) + + if module_name is not None: + info = cm_global.error_dict.get(module_name) + if info is not None: + return web.json_response(info) + + return web.Response(status=400) + + +@routes.post("/manager/queue/reinstall") +async def reinstall_custom_node(request): + await uninstall_custom_node(request) + await install_custom_node(request) + + +@routes.get("/manager/queue/reset") +async def reset_queue(request): + global task_queue + task_queue = queue.Queue() + return web.Response(status=200) + + +@routes.get("/manager/queue/status") +async def queue_count(request): + global task_queue + + with task_worker_lock: + done_count = len(nodepack_result) + len(model_result) + in_progress_count = len(tasks_in_progress) + total_count = done_count + in_progress_count + task_queue.qsize() + is_processing = task_worker_thread is not None and task_worker_thread.is_alive() + + return web.json_response({ + 'total_count': total_count, 'done_count': done_count, 'in_progress_count': in_progress_count, + 'is_processing': is_processing}) + + +@routes.post("/manager/queue/install") +async def install_custom_node(request): + if not is_allowed_security_level('middle'): + logging.error(SECURITY_MESSAGE_MIDDLE_OR_BELOW) + return web.Response(status=403, text="A security error has occurred. Please check the terminal logs") + + json_data = await request.json() + + # non-nightly cnr is safe + risky_level = None + cnr_id = json_data.get('id') + skip_post_install = json_data.get('skip_post_install') + + git_url = None + + selected_version = json_data.get('selected_version') + if json_data['version'] != 'unknown' and selected_version != 'unknown': + if skip_post_install: + if cnr_id in core.unified_manager.nightly_inactive_nodes or cnr_id in core.unified_manager.cnr_inactive_nodes: + core.unified_manager.unified_enable(cnr_id) + return web.Response(status=200) + elif selected_version is None: + selected_version = 'latest' + + if selected_version != 'nightly': + risky_level = 'low' + node_spec_str = f"{cnr_id}@{selected_version}" + else: + node_spec_str = f"{cnr_id}@nightly" + git_url = [json_data.get('repository')] + if git_url is None: + logging.error(f"[ComfyUI-Manager] Following node pack doesn't provide `nightly` version: ${git_url}") + return web.Response(status=404, text=f"Following node pack doesn't provide `nightly` version: ${git_url}") + elif json_data['version'] != 'unknown' and selected_version == 'unknown': + logging.error(f"[ComfyUI-Manager] Invalid installation request: {json_data}") + return web.Response(status=400, text="Invalid installation request") + else: + # unknown + unknown_name = os.path.basename(json_data['files'][0]) + node_spec_str = f"{unknown_name}@unknown" + git_url = json_data.get('files') + + # apply security policy if not cnr node (nightly isn't regarded as cnr node) + if risky_level is None: + if git_url is not None: + risky_level = await get_risky_level(git_url, json_data.get('pip', [])) + else: + return web.Response(status=404, text=f"Following node pack doesn't provide `nightly` version: ${git_url}") + + if not is_allowed_security_level(risky_level): + logging.error(SECURITY_MESSAGE_GENERAL) + return web.Response(status=404, text="A security error has occurred. Please check the terminal logs") + + install_item = json_data.get('ui_id'), node_spec_str, json_data['channel'], json_data['mode'], skip_post_install + task_queue.put(("install", install_item)) + + return web.Response(status=200) + + +task_worker_thread:threading.Thread = None + +@routes.get("/manager/queue/start") +async def queue_start(request): + global nodepack_result + global model_result + global task_worker_thread + + if task_worker_thread is not None and task_worker_thread.is_alive(): + return web.Response(status=201) # already in-progress + + nodepack_result = {} + model_result = {} + + task_worker_thread = threading.Thread(target=lambda: asyncio.run(task_worker())) + task_worker_thread.start() + + return web.Response(status=200) + + +@routes.post("/manager/queue/fix") +async def fix_custom_node(request): + if not is_allowed_security_level('middle'): + logging.error(SECURITY_MESSAGE_GENERAL) + return web.Response(status=403, text="A security error has occurred. Please check the terminal logs") + + json_data = await request.json() + + node_id = json_data.get('id') + node_ver = json_data['version'] + if node_ver != 'unknown': + node_name = node_id + else: + # unknown + node_name = os.path.basename(json_data['files'][0]) + + update_item = json_data.get('ui_id'), node_name, json_data['version'] + task_queue.put(("fix", update_item)) + + return web.Response(status=200) + + +@routes.post("/customnode/install/git_url") +async def install_custom_node_git_url(request): + if not is_allowed_security_level('high'): + logging.error(SECURITY_MESSAGE_NORMAL_MINUS) + return web.Response(status=403) + + url = await request.text() + res = await core.gitclone_install(url) + + if res.action == 'skip': + logging.info(f"\nAlready installed: '{res.target}'") + return web.Response(status=200) + elif res.result: + logging.info("\nAfter restarting ComfyUI, please refresh the browser.") + return web.Response(status=200) + + logging.error(res.msg) + return web.Response(status=400) + + +@routes.post("/customnode/install/pip") +async def install_custom_node_pip(request): + if not is_allowed_security_level('high'): + logging.error(SECURITY_MESSAGE_NORMAL_MINUS) + return web.Response(status=403) + + packages = await request.text() + core.pip_install(packages.split(' ')) + + return web.Response(status=200) + + +@routes.post("/manager/queue/uninstall") +async def uninstall_custom_node(request): + if not is_allowed_security_level('middle'): + logging.error(SECURITY_MESSAGE_MIDDLE_OR_BELOW) + return web.Response(status=403, text="A security error has occurred. Please check the terminal logs") + + json_data = await request.json() + + node_id = json_data.get('id') + if json_data['version'] != 'unknown': + is_unknown = False + node_name = node_id + else: + # unknown + is_unknown = True + node_name = os.path.basename(json_data['files'][0]) + + uninstall_item = json_data.get('ui_id'), node_name, is_unknown + task_queue.put(("uninstall", uninstall_item)) + + return web.Response(status=200) + + +@routes.post("/manager/queue/update") +async def update_custom_node(request): + if not is_allowed_security_level('middle'): + logging.error(SECURITY_MESSAGE_MIDDLE_OR_BELOW) + return web.Response(status=403, text="A security error has occurred. Please check the terminal logs") + + json_data = await request.json() + + node_id = json_data.get('id') + if json_data['version'] != 'unknown': + node_name = node_id + else: + # unknown + node_name = os.path.basename(json_data['files'][0]) + + update_item = json_data.get('ui_id'), node_name, json_data['version'] + task_queue.put(("update", update_item)) + + return web.Response(status=200) + + +@routes.get("/manager/queue/update_comfyui") +async def update_comfyui(request): + is_stable = core.get_config()['update_policy'] != 'nightly-comfyui' + task_queue.put(("update-comfyui", ('comfyui', is_stable))) + return web.Response(status=200) + + +@routes.get("/comfyui_manager/comfyui_versions") +async def comfyui_versions(request): + try: + res, current, latest = core.get_comfyui_versions() + return web.json_response({'versions': res, 'current': current}, status=200, content_type='application/json') + except Exception as e: + logging.error(f"ComfyUI update fail: {e}", file=sys.stderr) + + return web.Response(status=400) + + +@routes.get("/comfyui_manager/comfyui_switch_version") +async def comfyui_switch_version(request): + try: + if "ver" in request.rel_url.query: + core.switch_comfyui(request.rel_url.query['ver']) + + return web.Response(status=200) + except Exception as e: + logging.error(f"ComfyUI update fail: {e}", file=sys.stderr) + + return web.Response(status=400) + + +@routes.post("/manager/queue/disable") +async def disable_node(request): + json_data = await request.json() + + node_id = json_data.get('id') + if json_data['version'] != 'unknown': + is_unknown = False + node_name = node_id + else: + # unknown + is_unknown = True + node_name = os.path.basename(json_data['files'][0]) + + update_item = json_data.get('ui_id'), node_name, is_unknown + task_queue.put(("disable", update_item)) + + return web.Response(status=200) + + +async def check_whitelist_for_model(item): + json_obj = await core.get_data_by_mode('cache', 'model-list.json') + + for x in json_obj.get('models', []): + if x['save_path'] == item['save_path'] and x['base'] == item['base'] and x['filename'] == item['filename']: + return True + + json_obj = await core.get_data_by_mode('local', 'model-list.json') + + for x in json_obj.get('models', []): + if x['save_path'] == item['save_path'] and x['base'] == item['base'] and x['filename'] == item['filename']: + return True + + return False + + +@routes.post("/manager/queue/install_model") +async def install_model(request): + json_data = await request.json() + + if not is_allowed_security_level('middle'): + logging.error(SECURITY_MESSAGE_MIDDLE_OR_BELOW) + return web.Response(status=403, text="A security error has occurred. Please check the terminal logs") + + # validate request + if not await check_whitelist_for_model(json_data): + logging.error(f"[ComfyUI-Manager] Invalid model install request is detected: {json_data}") + return web.Response(status=400, text="Invalid model install request is detected") + + if not json_data['filename'].endswith('.safetensors') and not is_allowed_security_level('high'): + models_json = await core.get_data_by_mode('cache', 'model-list.json', 'default') + + is_belongs_to_whitelist = False + for x in models_json['models']: + if x.get('url') == json_data['url']: + is_belongs_to_whitelist = True + break + + if not is_belongs_to_whitelist: + logging.error(SECURITY_MESSAGE_NORMAL_MINUS_MODEL) + return web.Response(status=403, text="A security error has occurred. Please check the terminal logs") + + install_item = json_data.get('ui_id'), json_data + task_queue.put(("install-model", install_item)) + + return web.Response(status=200) + + +@routes.get("/manager/preview_method") +async def preview_method(request): + if "value" in request.rel_url.query: + set_preview_method(request.rel_url.query['value']) + core.write_config() + else: + return web.Response(text=core.manager_funcs.get_current_preview_method(), status=200) + + return web.Response(status=200) + + +@routes.get("/manager/db_mode") +async def db_mode(request): + if "value" in request.rel_url.query: + set_db_mode(request.rel_url.query['value']) + core.write_config() + else: + return web.Response(text=core.get_config()['db_mode'], status=200) + + return web.Response(status=200) + + + +@routes.get("/manager/policy/component") +async def component_policy(request): + if "value" in request.rel_url.query: + set_component_policy(request.rel_url.query['value']) + core.write_config() + else: + return web.Response(text=core.get_config()['component_policy'], status=200) + + return web.Response(status=200) + + +@routes.get("/manager/policy/update") +async def update_policy(request): + if "value" in request.rel_url.query: + set_update_policy(request.rel_url.query['value']) + core.write_config() + else: + return web.Response(text=core.get_config()['update_policy'], status=200) + + return web.Response(status=200) + + +@routes.get("/manager/channel_url_list") +async def channel_url_list(request): + channels = core.get_channel_dict() + if "value" in request.rel_url.query: + channel_url = channels.get(request.rel_url.query['value']) + if channel_url is not None: + core.get_config()['channel_url'] = channel_url + core.write_config() + else: + selected = 'custom' + selected_url = core.get_config()['channel_url'] + + for name, url in channels.items(): + if url == selected_url: + selected = name + break + + res = {'selected': selected, + 'list': core.get_channel_list()} + return web.json_response(res, status=200) + + return web.Response(status=200) + + +def add_target_blank(html_text): + pattern = r'(]*)(>)' + + def add_target(match): + if 'target=' not in match.group(1): + return match.group(1) + ' target="_blank"' + match.group(2) + return match.group(0) + + modified_html = re.sub(pattern, add_target, html_text) + + return modified_html + + +@routes.get("/manager/notice") +async def get_notice(request): + url = "github.com" + path = "/ltdrdata/ltdrdata.github.io/wiki/News" + + async with aiohttp.ClientSession(trust_env=True, connector=aiohttp.TCPConnector(verify_ssl=False)) as session: + async with session.get(f"https://{url}{path}") as response: + if response.status == 200: + # html_content = response.read().decode('utf-8') + html_content = await response.text() + + pattern = re.compile(r'
([\s\S]*?)
') + match = pattern.search(html_content) + + if match: + markdown_content = match.group(1) + version_tag = os.environ.get('__COMFYUI_DESKTOP_VERSION__') + if version_tag is not None: + markdown_content += f"
ComfyUI: {version_tag} [Desktop]" + else: + version_tag = core.get_comfyui_tag() + if version_tag is None: + markdown_content += f"
ComfyUI: {core.comfy_ui_revision}[{comfy_ui_hash[:6]}]({core.comfy_ui_commit_datetime.date()})" + else: + markdown_content += (f"
ComfyUI: {version_tag}
" + f"         ({core.comfy_ui_commit_datetime.date()})") + # markdown_content += f"
         ()" + markdown_content += f"
Manager: {core.version_str}" + + markdown_content = add_target_blank(markdown_content) + + try: + if '__COMFYUI_DESKTOP_VERSION__' not in os.environ: + if core.comfy_ui_commit_datetime == datetime(1900, 1, 1, 0, 0, 0): + markdown_content = '

Your ComfyUI isn\'t git repo.

' + markdown_content + elif core.comfy_ui_required_commit_datetime.date() > core.comfy_ui_commit_datetime.date(): + markdown_content = '

Your ComfyUI is too OUTDATED!!!

' + markdown_content + except: + pass + + return web.Response(text=markdown_content, status=200) + else: + return web.Response(text="Unable to retrieve Notice", status=200) + else: + return web.Response(text="Unable to retrieve Notice", status=200) + + +@routes.get("/manager/reboot") +def restart(self): + if not is_allowed_security_level('middle'): + logging.error(SECURITY_MESSAGE_MIDDLE_OR_BELOW) + return web.Response(status=403) + + try: + sys.stdout.close_log() + except Exception: + pass + + if '__COMFY_CLI_SESSION__' in os.environ: + with open(os.path.join(os.environ['__COMFY_CLI_SESSION__'] + '.reboot'), 'w'): + pass + + print("\nRestarting...\n\n") # This printing should not be logging - that will be ugly + exit(0) + + print("\nRestarting... [Legacy Mode]\n\n") # This printing should not be logging - that will be ugly + + sys_argv = sys.argv.copy() + if '--windows-standalone-build' in sys_argv: + sys_argv.remove('--windows-standalone-build') + + if sys_argv[0].endswith("__main__.py"): # this is a python module + module_name = os.path.basename(os.path.dirname(sys_argv[0])) + cmds = [sys.executable, '-m', module_name] + sys_argv[1:] + elif sys.platform.startswith('win32'): + cmds = ['"' + sys.executable + '"', '"' + sys_argv[0] + '"'] + sys_argv[1:] + else: + cmds = [sys.executable] + sys_argv + + print(f"Command: {cmds}", flush=True) + + return os.execv(sys.executable, cmds) + + +@routes.post("/manager/component/save") +async def save_component(request): + try: + data = await request.json() + name = data['name'] + workflow = data['workflow'] + + if not os.path.exists(core.manager_components_path): + os.mkdir(core.manager_components_path) + + if 'packname' in workflow and workflow['packname'] != '': + sanitized_name = manager_util.sanitize_filename(workflow['packname']) + '.pack' + else: + sanitized_name = manager_util.sanitize_filename(name) + '.json' + + filepath = os.path.join(core.manager_components_path, sanitized_name) + components = {} + if os.path.exists(filepath): + with open(filepath) as f: + components = json.load(f) + + components[name] = workflow + + with open(filepath, 'w') as f: + json.dump(components, f, indent=4, sort_keys=True) + return web.Response(text=filepath, status=200) + except: + return web.Response(status=400) + + +@routes.post("/manager/component/loads") +async def load_components(request): + if os.path.exists(core.manager_components_path): + try: + json_files = [f for f in os.listdir(core.manager_components_path) if f.endswith('.json')] + pack_files = [f for f in os.listdir(core.manager_components_path) if f.endswith('.pack')] + + components = {} + for json_file in json_files + pack_files: + file_path = os.path.join(core.manager_components_path, json_file) + with open(file_path, 'r') as file: + try: + # When there is a conflict between the .pack and the .json, the pack takes precedence and overrides. + components.update(json.load(file)) + except json.JSONDecodeError as e: + logging.error(f"[ComfyUI-Manager] Error decoding component file in file {json_file}: {e}") + + return web.json_response(components) + except Exception as e: + logging.error(f"[ComfyUI-Manager] failed to load components\n{e}") + return web.Response(status=400) + else: + return web.json_response({}) + + +@routes.get("/manager/version") +async def get_version(request): + return web.Response(text=core.version_str, status=200) + + +async def _confirm_try_install(sender, custom_node_url, msg): + json_obj = await core.get_data_by_mode('default', 'custom-node-list.json') + + sender = manager_util.sanitize_tag(sender) + msg = manager_util.sanitize_tag(msg) + target = core.lookup_customnode_by_url(json_obj, custom_node_url) + + if target is not None: + PromptServer.instance.send_sync("cm-api-try-install-customnode", + {"sender": sender, "target": target, "msg": msg}) + else: + logging.error(f"[ComfyUI Manager API] Failed to try install - Unknown custom node url '{custom_node_url}'") + + +def confirm_try_install(sender, custom_node_url, msg): + asyncio.run(_confirm_try_install(sender, custom_node_url, msg)) + + +cm_global.register_api('cm.try-install-custom-node', confirm_try_install) + + +async def default_cache_update(): + core.refresh_channel_dict() + channel_url = core.get_config()['channel_url'] + async def get_cache(filename): + try: + if core.get_config()['default_cache_as_channel_url']: + uri = f"{channel_url}/{filename}" + else: + uri = f"{core.DEFAULT_CHANNEL}/{filename}" + + cache_uri = str(manager_util.simple_hash(uri)) + '_' + filename + cache_uri = os.path.join(manager_util.cache_dir, cache_uri) + + json_obj = await manager_util.get_data(uri, True) + + with manager_util.cache_lock: + with open(cache_uri, "w", encoding='utf-8') as file: + json.dump(json_obj, file, indent=4, sort_keys=True) + logging.info(f"[ComfyUI-Manager] default cache updated: {uri}") + except Exception as e: + logging.error(f"[ComfyUI-Manager] Failed to perform initial fetching '{filename}': {e}") + traceback.print_exc() + + if core.get_config()['network_mode'] != 'offline': + a = get_cache("custom-node-list.json") + b = get_cache("extension-node-map.json") + c = get_cache("model-list.json") + d = get_cache("alter-list.json") + e = get_cache("github-stats.json") + + await asyncio.gather(a, b, c, d, e) + + if core.get_config()['network_mode'] == 'private': + logging.info("[ComfyUI-Manager] The private comfyregistry is not yet supported in `network_mode=private`.") + else: + # load at least once + await core.unified_manager.reload('remote', dont_wait=False) + await core.unified_manager.get_custom_nodes(channel_url, 'remote') + + logging.info("[ComfyUI-Manager] All startup tasks have been completed.") + + +threading.Thread(target=lambda: asyncio.run(default_cache_update())).start() + +if not os.path.exists(core.manager_config_path): + core.get_config() + core.write_config() + + +cm_global.register_extension('ComfyUI-Manager', + {'version': core.version, + 'name': 'ComfyUI Manager', + 'nodes': {}, + 'description': 'This extension provides the ability to manage custom nodes in ComfyUI.', }) + + diff --git a/ComfyUI/custom_nodes/ComfyUI-Manager/js/cm-api.js b/ComfyUI/custom_nodes/ComfyUI-Manager/js/cm-api.js new file mode 100644 index 0000000000000000000000000000000000000000..dabc6f1dd1383a3a5c5b9893a7fb3d5f3991a0c2 --- /dev/null +++ b/ComfyUI/custom_nodes/ComfyUI-Manager/js/cm-api.js @@ -0,0 +1,67 @@ +import { api } from "../../scripts/api.js"; +import { app } from "../../scripts/app.js"; +import { sleep, customConfirm, customAlert } from "./common.js"; + +async function tryInstallCustomNode(event) { + let msg = '-= [ComfyUI Manager] extension installation request =-\n\n'; + msg += `The '${event.detail.sender}' extension requires the installation of the '${event.detail.target.title}' extension. `; + + if(event.detail.target.installed == 'Disabled') { + msg += 'However, the extension is currently disabled. Would you like to enable it and reboot?' + } + else if(event.detail.target.installed == 'True') { + msg += 'However, it seems that the extension is in an import-fail state or is not compatible with the current version. Please address this issue.'; + } + else { + msg += `Would you like to install it and reboot?`; + } + + msg += `\n\nRequest message:\n${event.detail.msg}`; + + if(event.detail.target.installed == 'True') { + customAlert(msg); + return; + } + const res = await customConfirm(msg); + if(res) { + if(event.detail.target.installed == 'Disabled') { + const response = await api.fetchApi(`/customnode/toggle_active`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(event.detail.target) + }); + } + else { + await sleep(300); + app.ui.dialog.show(`Installing... '${event.detail.target.title}'`); + + const response = await api.fetchApi(`/customnode/install`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(event.detail.target) + }); + + if(response.status == 403) { + show_message('This action is not allowed with this security level configuration.'); + return false; + } + else if(response.status == 400) { + let msg = await res.text(); + show_message(msg); + return false; + } + } + + let response = await api.fetchApi("/manager/reboot"); + if(response.status == 403) { + show_message('This action is not allowed with this security level configuration.'); + return false; + } + + await sleep(300); + + app.ui.dialog.show(`Rebooting...`); + } +} + +api.addEventListener("cm-api-try-install-customnode", tryInstallCustomNode); diff --git a/ComfyUI/custom_nodes/ComfyUI-Manager/js/comfyui-share-copus.js b/ComfyUI/custom_nodes/ComfyUI-Manager/js/comfyui-share-copus.js new file mode 100644 index 0000000000000000000000000000000000000000..46288e5960d02c4531d4e0a2d571949757767de1 --- /dev/null +++ b/ComfyUI/custom_nodes/ComfyUI-Manager/js/comfyui-share-copus.js @@ -0,0 +1,1168 @@ +import { app } from "../../scripts/app.js"; +import { $el, ComfyDialog } from "../../scripts/ui.js"; +import { customAlert } from "./common.js"; + +const env = "prod"; + +let DEFAULT_HOMEPAGE_URL = "https://copus.io"; + +let API_ENDPOINT = "https://api.client.prod.copus.io"; + +if (env !== "prod") { + API_ENDPOINT = "https://api.test.copus.io"; + DEFAULT_HOMEPAGE_URL = "https://test.copus.io"; +} + +const style = ` + .copus-share-dialog a { + color: #f8f8f8; + } + .copus-share-dialog a:hover { + color: #007bff; + } + .output_label { + border: 5px solid transparent; + } + .output_label:hover { + border: 5px solid #59E8C6; + } + .output_label.checked { + border: 5px solid #59E8C6; + } +`; + +// Shared component styles +const sectionStyle = { + marginBottom: 0, + padding: 0, + borderRadius: "8px", + boxShadow: "0 2px 4px rgba(0, 0, 0, 0.05)", + display: "flex", + flexDirection: "column", + justifyContent: "center", + position: "relative", +}; + +export class CopusShareDialog extends ComfyDialog { + static instance = null; + + constructor() { + super(); + $el("style", { + textContent: style, + parent: document.head, + }); + this.element = $el( + "div.comfy-modal.copus-share-dialog", + { + parent: document.body, + style: { + "overflow-y": "auto", + }, + }, + [$el("div.comfy-modal-content", {}, [...this.createButtons()])] + ); + this.selectedOutputIndex = 0; + this.selectedOutput_lock = 0; + this.selectedNodeId = null; + this.uploadedImages = []; + this.allFilesImages = []; + this.selectedFile = null; + this.allFiles = []; + this.titleNum = 0; + } + + createButtons() { + const inputStyle = { + display: "block", + minWidth: "500px", + width: "100%", + padding: "10px", + margin: "10px 0", + borderRadius: "4px", + border: "1px solid #ddd", + boxSizing: "border-box", + }; + + const textAreaStyle = { + display: "block", + minWidth: "500px", + width: "100%", + padding: "10px", + margin: "10px 0", + borderRadius: "4px", + border: "1px solid #ddd", + boxSizing: "border-box", + minHeight: "100px", + background: "#222", + resize: "vertical", + color: "#f2f2f2", + fontFamily: "Arial", + fontWeight: "400", + fontSize: "15px", + }; + + const hyperLinkStyle = { + display: "block", + marginBottom: "15px", + fontWeight: "bold", + fontSize: "14px", + }; + + const labelStyle = { + color: "#f8f8f8", + display: "block", + margin: "10px 0 0 0", + fontWeight: "bold", + textDecoration: "none", + }; + + const buttonStyle = { + padding: "10px 80px", + margin: "10px 5px", + borderRadius: "4px", + border: "none", + cursor: "pointer", + color: "#fff", + backgroundColor: "#007bff", + }; + + // upload images input + this.uploadImagesInput = $el("input", { + type: "file", + multiple: false, + style: inputStyle, + accept: "image/*", + }); + + this.uploadImagesInput.addEventListener("change", async (e) => { + const file = e.target.files[0]; + if (!file) { + this.previewImage.src = ""; + this.previewImage.style.display = "none"; + return; + } + const reader = new FileReader(); + reader.onload = async (e) => { + const imgData = e.target.result; + this.previewImage.src = imgData; + this.previewImage.style.display = "block"; + this.selectedFile = null; + // Once user uploads an image, we uncheck all radio buttons + this.radioButtons.forEach((ele) => { + ele.checked = false; + ele.parentElement.classList.remove("checked"); + }); + + // Add the opacity style toggle here to indicate that they only need + // to upload one image or choose one from the outputs. + this.outputsSection.style.opacity = 0.35; + this.uploadImagesInput.style.opacity = 1; + }; + reader.readAsDataURL(file); + }); + + // preview image + this.previewImage = $el("img", { + src: "", + style: { + width: "100%", + maxHeight: "100px", + objectFit: "contain", + display: "none", + marginTop: "10px", + }, + }); + + this.keyInput = $el("input", { + type: "password", + placeholder: "Copy & paste your API key", + style: inputStyle, + }); + this.TitleInput = $el("input", { + type: "text", + placeholder: "Title (Required)", + style: inputStyle, + maxLength: "70", + oninput: () => { + const titleNum = this.TitleInput.value.length; + titleNumDom.textContent = `${titleNum}/70`; + }, + }); + this.SubTitleInput = $el("input", { + type: "text", + placeholder: "Subtitle (Optional)", + style: inputStyle, + maxLength: "350", + oninput: () => { + const titleNum = this.SubTitleInput.value.length; + subTitleNumDom.textContent = `${titleNum}/350`; + }, + }); + this.LockInput = $el("input", { + type: "text", + placeholder: "0", + style: { + width: "100px", + padding: "7px", + paddingLeft: "30px", + borderRadius: "4px", + border: "1px solid #ddd", + boxSizing: "border-box", + position: "relative", + }, + oninput: (event) => { + let input = event.target.value; + // Use a regular expression to match a number with up to two decimal places + const regex = /^\d*\.?\d{0,2}$/; + if (!regex.test(input)) { + // If the input doesn't match, remove the last entered character + event.target.value = input.slice(0, -1); + } + const numericValue = parseFloat(input); + if (numericValue > 9999) { + input = "9999"; + } + // Update the input field with the valid value + event.target.value = input; + }, + }); + this.descriptionInput = $el("textarea", { + placeholder: "Content (Optional)", + style: { + ...textAreaStyle, + minHeight: "100px", + }, + }); + + // Header Section + const headerSection = $el("h3", { + textContent: "Share your workflow to Copus", + size: 3, + color: "white", + style: { + "text-align": "center", + color: "white", + margin: "0 0 10px 0", + }, + }); + this.getAPIKeyLink = $el( + "a", + { + style: { + ...hyperLinkStyle, + color: "#59E8C6", + }, + href: `${DEFAULT_HOMEPAGE_URL}?fromPage=comfyUI`, + target: "_blank", + }, + ["👉 Get your API key here"] + ); + const linkSection = $el( + "div", + { + style: { + marginTop: "10px", + display: "flex", + flexDirection: "column", + }, + }, + [ + // this.communityLink, + this.getAPIKeyLink, + ] + ); + + // Account Section + const accountSection = $el("div", { style: sectionStyle }, [ + $el("label", { style: labelStyle }, ["1️⃣ Copus API Key"]), + this.keyInput, + ]); + + // Output Upload Section + const outputUploadSection = $el("div", { style: sectionStyle }, [ + $el( + "label", + { + style: { + ...labelStyle, + margin: "10px 0 0 0", + }, + }, + ["2️⃣ Image/Thumbnail (Required)"] + ), + this.previewImage, + this.uploadImagesInput, + ]); + + // Outputs Section + this.outputsSection = $el( + "div", + { + id: "selectOutputs", + }, + [] + ); + + const titleNumDom = $el( + "label", + { + style: { + fontSize: "12px", + position: "absolute", + right: "10px", + bottom: "-10px", + color: "#999", + }, + }, + ["0/70"] + ); + const subTitleNumDom = $el( + "label", + { + style: { + fontSize: "12px", + position: "absolute", + right: "10px", + bottom: "-10px", + color: "#999", + }, + }, + ["0/350"] + ); + const descriptionNumDom = $el( + "label", + { + style: { + fontSize: "12px", + position: "absolute", + right: "10px", + bottom: "-10px", + color: "#999", + }, + }, + ["0/70"] + ); + // Additional Inputs Section + const additionalInputsSection = $el("div", { style: { ...sectionStyle } }, [ + $el("label", { style: labelStyle }, ["3️⃣ Title "]), + this.TitleInput, + titleNumDom, + ]); + const SubtitleSection = $el("div", { style: sectionStyle }, [ + $el("label", { style: labelStyle }, ["4️⃣ Subtitle "]), + this.SubTitleInput, + subTitleNumDom, + ]); + const DescriptionSection = $el("div", { style: sectionStyle }, [ + $el("label", { style: labelStyle }, ["5️⃣ Description "]), + this.descriptionInput, + // descriptionNumDom, + ]); + // switch between outputs section and additional inputs section + this.radioButtons_lock = []; + + this.radioButtonsCheck_lock = $el("input", { + type: "radio", + name: "output_type_lock", + value: "0", + id: "blockchain1_lock", + checked: true, + }); + this.radioButtonsCheckOff_lock = $el("input", { + type: "radio", + name: "output_type_lock", + value: "1", + id: "blockchain_lock", + }); + + const blockChainSection_lock = $el("div", { style: sectionStyle }, [ + $el("label", { style: labelStyle }, ["6️⃣ Download threshold"]), + $el( + "label", + { + style: { + marginTop: "10px", + display: "flex", + alignItems: "center", + cursor: "pointer", + }, + }, + [ + this.radioButtonsCheck_lock, + $el( + "div", + { + style: { + marginLeft: "5px", + display: "flex", + alignItems: "center", + position: "relative", + }, + }, + [ + $el("span", { style: { marginLeft: "5px" } }, ["ON"]), + $el( + "span", + { + style: { + marginLeft: "20px", + marginRight: "10px", + color: "#fff", + }, + }, + ["Unlock with"] + ), + $el("img", { + style: { + width: "16px", + height: "16px", + position: "absolute", + right: "75px", + zIndex: "100", + }, + src: "https://static.copus.io/images/admin/202507/prod/e2919a1d8f3c2d99d3b8fe27ff94b841.png", + }), + this.LockInput, + ] + ), + ] + ), + $el( + "label", + { style: { display: "flex", alignItems: "center", cursor: "pointer" } }, + [ + this.radioButtonsCheckOff_lock, + $el( + "div", + { + style: { + marginLeft: "5px", + display: "flex", + alignItems: "center", + }, + }, + [$el("span", { style: { marginLeft: "5px" } }, ["OFF"])] + ), + ] + ), + + $el( + "p", + { style: { fontSize: "16px", color: "#fff", margin: "10px 0 0 0" } }, + [ + ] + ), + ]); + + this.radioButtons = []; + + this.radioButtonsCheck = $el("input", { + type: "radio", + name: "output_type", + value: "0", + id: "blockchain1", + checked: true, + }); + this.radioButtonsCheckOff = $el("input", { + type: "radio", + name: "output_type", + value: "1", + id: "blockchain", + }); + + const blockChainSection = $el("div", { style: sectionStyle }, [ + $el("label", { style: labelStyle }, ["8️⃣ Store on blockchain "]), + $el( + "label", + { + style: { + marginTop: "10px", + display: "flex", + alignItems: "center", + cursor: "pointer", + }, + }, + [ + this.radioButtonsCheck, + $el("span", { style: { marginLeft: "5px" } }, ["ON"]), + ] + ), + $el( + "label", + { style: { display: "flex", alignItems: "center", cursor: "pointer" } }, + [ + this.radioButtonsCheckOff, + $el("span", { style: { marginLeft: "5px" } }, ["OFF"]), + ] + ), + $el( + "p", + { style: { fontSize: "16px", color: "#fff", margin: "10px 0 0 0" } }, + ["Secure ownership with a permanent & decentralized storage"] + ), + ]); + + this.ratingRadioButtonsCheck0 = $el("input", { + type: "radio", + name: "content_rating", + value: "0", + id: "content_rating0", + }); + this.ratingRadioButtonsCheck1 = $el("input", { + type: "radio", + name: "content_rating", + value: "1", + id: "content_rating1", + }); + this.ratingRadioButtonsCheck2 = $el("input", { + type: "radio", + name: "content_rating", + value: "2", + id: "content_rating2", + }); + this.ratingRadioButtonsCheck_1 = $el("input", { + type: "radio", + name: "content_rating", + value: "-1", + id: "content_rating_1", + checked: true, + }); + + // content rating + const contentRatingSection = $el("div", { style: sectionStyle }, [ + $el("label", { style: labelStyle }, ["7️⃣ Content rating "]), + $el( + "label", + { + style: { + marginTop: "10px", + display: "flex", + alignItems: "center", + cursor: "pointer", + }, + }, + [ + this.ratingRadioButtonsCheck0, + $el("img", { + style: { + width: "12px", + height: "12px", + marginLeft: "5px", + }, + src: "https://static.copus.io/images/client/202507/test/b9f17da83b054d53cd0cb4508c2c30dc.png", + }), + $el("span", { style: { marginLeft: "5px", color: "#fff" } }, [ + "All ages", + ]), + ] + ), + $el( + "p", + { style: { fontSize: "10px", color: "#fff", marginLeft: "20px" } }, + ["Safe for all viewers; no profanity, violence, or mature themes."] + ), + $el( + "label", + { style: { display: "flex", alignItems: "center", cursor: "pointer" } }, + [ + this.ratingRadioButtonsCheck1, + $el("img", { + style: { + width: "12px", + height: "12px", + marginLeft: "5px", + }, + src: "https://static.copus.io/images/client/202507/test/7848bc0d3690671df21c7cf00c4cfc81.png", + }), + $el("span", { style: { marginLeft: "5px", color: "#fff" } }, [ + "13+ (Teen)", + ]), + ] + ), + $el( + "p", + { style: { fontSize: "10px", color: "#fff", marginLeft: "20px" } }, + [ + "Mild language, light themes, or cartoon violence; no explicit content. ", + ] + ), + $el( + "label", + { style: { display: "flex", alignItems: "center", cursor: "pointer" } }, + [ + this.ratingRadioButtonsCheck2, + $el("img", { + style: { + width: "12px", + height: "12px", + marginLeft: "5px", + }, + src: "https://static.copus.io/images/client/202507/test/bc51839c208d68d91173e43c23bff039.png", + }), + $el("span", { style: { marginLeft: "5px", color: "#fff" } }, [ + "18+ (Explicit)", + ]), + ] + ), + $el( + "p", + { style: { fontSize: "10px", color: "#fff", marginLeft: "20px" } }, + [ + "Explicit content, including sexual content, strong violence, or intense themes. ", + ] + ), + $el( + "label", + { style: { display: "flex", alignItems: "center", cursor: "pointer" } }, + [ + this.ratingRadioButtonsCheck_1, + $el("img", { + style: { + width: "12px", + height: "12px", + marginLeft: "5px", + }, + src: "https://static.copus.io/images/client/202507/test/5c802fdcaaea4e7bbed37393eec0d5ba.png", + }), + $el("span", { style: { marginLeft: "5px", color: "#fff" } }, [ + "Not Rated", + ]), + ] + ), + $el( + "p", + { style: { fontSize: "10px", color: "#fff", marginLeft: "20px" } }, + ["No age rating provided."] + ), + ]); + + // Message Section + this.message = $el( + "div", + { + style: { + color: "#ff3d00", + textAlign: "center", + padding: "10px", + fontSize: "20px", + }, + }, + [] + ); + + this.shareButton = $el("button", { + type: "submit", + textContent: "Share", + style: buttonStyle, + onclick: () => { + this.handleShareButtonClick(); + }, + }); + + // Share and Close Buttons + const buttonsSection = $el( + "div", + { + style: { + textAlign: "right", + marginTop: "20px", + display: "flex", + justifyContent: "space-between", + }, + }, + [ + $el("button", { + type: "button", + textContent: "Close", + style: { + ...buttonStyle, + backgroundColor: undefined, + }, + onclick: () => { + this.close(); + }, + }), + this.shareButton, + ] + ); + + // Composing the full layout + const layout = [ + headerSection, + linkSection, + accountSection, + outputUploadSection, + this.outputsSection, + additionalInputsSection, + SubtitleSection, + DescriptionSection, + // contestSection, + blockChainSection_lock, + contentRatingSection, + blockChainSection, + this.message, + buttonsSection, + ]; + + return layout; + } + /** + * api + * @param {url} path + * @param {params} options + * @param {statusText} statusText + * @returns + */ + async fetchApi(path, options, statusText) { + if (statusText) { + this.message.textContent = statusText; + } + const fullPath = new URL(API_ENDPOINT + path); + const response = await fetch(fullPath, options); + if (!response.ok) { + throw new Error(response.statusText); + } + if (statusText) { + this.message.textContent = ""; + } + const data = await response.json(); + return { + ok: response.ok, + statusText: response.statusText, + status: response.status, + data, + }; + } + /** + * @param {file} uploadFile + */ + async uploadThumbnail(uploadFile, type) { + const form = new FormData(); + form.append("file", uploadFile); + form.append("apiToken", this.keyInput.value); + try { + const res = await this.fetchApi( + `/client/common/opus/uploadImage`, + { + method: "POST", + body: form, + }, + "Uploading thumbnail..." + ); + if (res.status && res.data.status && res.data) { + const { data } = res.data; + if (type) { + this.allFilesImages.push({ + url: data, + }); + } + this.uploadedImages.push({ + url: data, + }); + } else { + throw new Error( + "make sure your API key is correct and try again later" + ); + } + } catch (e) { + if (e?.response?.status === 413) { + throw new Error("File size is too large (max 20MB)"); + } else { + throw new Error("Error uploading thumbnail: " + e.message); + } + } + } + + async handleShareButtonClick() { + this.message.textContent = ""; + try { + this.shareButton.disabled = true; + this.shareButton.textContent = "Sharing..."; + await this.share(); + } catch (e) { + customAlert(e.message); + } + this.shareButton.disabled = false; + this.shareButton.textContent = "Share"; + } + /** + * share + * @param {string} title + * @param {string} subtitle + * @param {string} content + * @param {boolean} storeOnChain + * @param {string} coverUrl + * @param {string[]} imageUrls + * @param {string} apiToken + */ + async share() { + const prompt = await app.graphToPrompt(); + const workflowJSON = prompt["workflow"]; + const form_values = { + title: this.TitleInput.value, + subTitle: this.SubTitleInput.value, + content: this.descriptionInput.value, + storeOnChain: this.radioButtonsCheck.checked ? true : false, + lockState: this.radioButtonsCheck_lock.checked ? 2 : 0, + unlockPrice: this.LockInput.value, + rating: this.ratingRadioButtonsCheck0.checked + ? 0 + : this.ratingRadioButtonsCheck1.checked + ? 1 + : this.ratingRadioButtonsCheck2.checked + ? 2 + : -1, + }; + + if (!this.keyInput.value) { + throw new Error("API key is required"); + } + + if (!this.uploadImagesInput.files[0] && !this.selectedFile) { + throw new Error("Thumbnail is required"); + } + + if (!form_values.title) { + throw new Error("Title is required"); + } + + if (this.radioButtonsCheck_lock.checked) { + if (!this.LockInput.value) { + throw new Error("Price is required"); + } + } + + if (!this.uploadedImages.length) { + if (this.selectedFile) { + await this.uploadThumbnail(this.selectedFile); + } else { + for (const file of this.uploadImagesInput.files) { + try { + await this.uploadThumbnail(file); + } catch (e) { + this.uploadedImages = []; + throw new Error(e.message); + } + } + + if (this.uploadImagesInput.files.length === 0) { + throw new Error("No thumbnail uploaded"); + } + } + } + if (this.allFiles.length > 0) { + for (const file of this.allFiles) { + try { + await this.uploadThumbnail(file, true); + } catch (e) { + this.allFilesImages = []; + throw new Error(e.message); + } + } + } + try { + const res = await this.fetchApi( + "/client/common/opus/shareFromComfyUI", + { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + workflowJson: workflowJSON, + apiToken: this.keyInput.value, + coverUrl: this.uploadedImages[0].url, + imageUrls: this.allFilesImages.map((image) => image.url), + ...form_values, + }), + }, + "Uploading workflow..." + ); + + if (res.status && res.data.status && res.data) { + localStorage.setItem("copus_token", this.keyInput.value); + const { data } = res.data; + if (data) { + const url = `${DEFAULT_HOMEPAGE_URL}/work/${data}`; + this.message.innerHTML = `Workflow has been shared successfully. Click here to view it.`; + this.previewImage.src = ""; + this.previewImage.style.display = "none"; + this.uploadedImages = []; + this.allFilesImages = []; + this.allFiles = []; + this.TitleInput.value = ""; + this.SubTitleInput.value = ""; + this.descriptionInput.value = ""; + this.selectedFile = null; + } + } + } catch (e) { + throw new Error("Error sharing workflow: " + e.message); + } + } + + async fetchImageBlob(url) { + const response = await fetch(url); + const blob = await response.blob(); + return blob; + } + + async show({ potential_outputs, potential_output_nodes } = {}) { + // Sort `potential_output_nodes` by node ID to make the order always + // consistent, but we should also keep `potential_outputs` in the same + // order as `potential_output_nodes`. + const potential_output_to_order = {}; + potential_output_nodes.forEach((node, index) => { + if (node.id in potential_output_to_order) { + potential_output_to_order[node.id][1].push(potential_outputs[index]); + } else { + potential_output_to_order[node.id] = [node, [potential_outputs[index]]]; + } + }); + // Sort the object `potential_output_to_order` by key (node ID) + const sorted_potential_output_to_order = Object.fromEntries( + Object.entries(potential_output_to_order).sort( + (a, b) => a[0].id - b[0].id + ) + ); + const sorted_potential_outputs = []; + const sorted_potential_output_nodes = []; + for (const [key, value] of Object.entries( + sorted_potential_output_to_order + )) { + sorted_potential_output_nodes.push(value[0]); + sorted_potential_outputs.push(...value[1]); + } + potential_output_nodes = sorted_potential_output_nodes; + potential_outputs = sorted_potential_outputs; + const apiToken = localStorage.getItem("copus_token"); + this.message.innerHTML = ""; + this.message.textContent = ""; + this.element.style.display = "block"; + this.previewImage.src = ""; + this.previewImage.style.display = "none"; + this.keyInput.value = apiToken != null ? apiToken : ""; + this.uploadedImages = []; + this.allFilesImages = []; + this.allFiles = []; + // If `selectedNodeId` is provided, we will select the corresponding radio + // button for the node. In addition, we move the selected radio button to + // the top of the list. + if (this.selectedNodeId) { + const index = potential_output_nodes.findIndex( + (node) => node.id === this.selectedNodeId + ); + if (index >= 0) { + this.selectedOutputIndex = index; + } + } + + this.radioButtons = []; + const new_radio_buttons = $el( + "div", + { + id: "selectOutput-Options", + style: { + "overflow-y": "scroll", + "max-height": "200px", + display: "grid", + "grid-template-columns": "repeat(auto-fit, minmax(100px, 1fr))", + "grid-template-rows": "auto", + "grid-column-gap": "10px", + "grid-row-gap": "10px", + "margin-bottom": "10px", + padding: "10px", + "border-radius": "8px", + "box-shadow": "0 2px 4px rgba(0, 0, 0, 0.05)", + "background-color": "var(--bg-color)", + }, + }, + potential_outputs.map((output, index) => { + const { node_id } = output; + const radio_button = $el( + "input", + { + type: "radio", + name: "selectOutputImages", + value: index, + required: index === 0, + }, + [] + ); + let radio_button_img; + let filename; + if (output.type === "image" || output.type === "temp") { + radio_button_img = $el( + "img", + { + src: `/view?filename=${output.image.filename}&subfolder=${output.image.subfolder}&type=${output.image.type}`, + style: { + width: "100px", + height: "100px", + objectFit: "cover", + borderRadius: "5px", + }, + }, + [] + ); + filename = output.image.filename; + } else if (output.type === "output") { + radio_button_img = $el( + "img", + { + src: output.output.value, + style: { + width: "auto", + height: "100px", + objectFit: "cover", + borderRadius: "5px", + }, + }, + [] + ); + filename = output.filename; + } else { + // unsupported output type + // this should never happen + radio_button_img = $el( + "img", + { + src: "", + style: { width: "auto", height: "100px" }, + }, + [] + ); + } + const radio_button_text = $el( + "span", + { + style: { + color: "gray", + display: "block", + fontSize: "12px", + overflowX: "hidden", + textOverflow: "ellipsis", + textWrap: "nowrap", + maxWidth: "100px", + }, + }, + [output.title] + ); + const node_id_chip = $el( + "span", + { + style: { + color: "#FBFBFD", + display: "block", + backgroundColor: "rgba(0, 0, 0, 0.5)", + fontSize: "12px", + overflowX: "hidden", + padding: "2px 3px", + textOverflow: "ellipsis", + textWrap: "nowrap", + maxWidth: "100px", + position: "absolute", + top: "3px", + left: "3px", + borderRadius: "3px", + }, + }, + [`Node: ${node_id}`] + ); + radio_button.style.color = "var(--fg-color)"; + radio_button.checked = this.selectedOutputIndex === index; + + radio_button.onchange = async () => { + this.selectedOutputIndex = parseInt(radio_button.value); + + // Remove the "checked" class from all radio buttons + this.radioButtons.forEach((ele) => { + ele.parentElement.classList.remove("checked"); + }); + radio_button.parentElement.classList.add("checked"); + + this.fetchImageBlob(radio_button_img.src).then((blob) => { + const file = new File([blob], filename, { + type: blob.type, + }); + this.previewImage.src = radio_button_img.src; + this.previewImage.style.display = "block"; + this.selectedFile = file; + }); + + // Add the opacity style toggle here to indicate that they only need + // to upload one image or choose one from the outputs. + this.outputsSection.style.opacity = 1; + this.uploadImagesInput.style.opacity = 0.35; + }; + + if (radio_button.checked) { + this.fetchImageBlob(radio_button_img.src).then((blob) => { + const file = new File([blob], filename, { + type: blob.type, + }); + this.previewImage.src = radio_button_img.src; + this.previewImage.style.display = "block"; + this.selectedFile = file; + }); + // Add the opacity style toggle here to indicate that they only need + // to upload one image or choose one from the outputs. + this.outputsSection.style.opacity = 1; + this.uploadImagesInput.style.opacity = 0.35; + } + this.radioButtons.push(radio_button); + let src = ""; + if (output.type === "image" || output.type === "temp") { + filename = output.image.filename; + src = `/view?filename=${output.image.filename}&subfolder=${output.image.subfolder}&type=${output.image.type}`; + } else if (output.type === "output") { + src = output.output.value; + filename = output.filename; + } + if (src) { + this.fetchImageBlob(src).then((blob) => { + const file = new File([blob], filename, { + type: blob.type, + }); + this.allFiles.push(file); + }); + } + return $el( + `label.output_label${radio_button.checked ? ".checked" : ""}`, + { + style: { + display: "flex", + flexDirection: "column", + alignItems: "center", + justifyContent: "center", + marginBottom: "10px", + cursor: "pointer", + position: "relative", + }, + }, + [radio_button_img, radio_button_text, radio_button, node_id_chip] + ); + }) + ); + + const header = $el( + "p", + { + textContent: + this.radioButtons.length === 0 + ? "Queue Prompt to see the outputs" + : "Or choose one from the outputs (scroll to see all)", + size: 2, + color: "white", + style: { + color: "white", + margin: "0 0 5px 0", + fontSize: "12px", + }, + }, + [] + ); + this.outputsSection.innerHTML = ""; + this.outputsSection.appendChild(header); + this.outputsSection.appendChild(new_radio_buttons); + } +} diff --git a/ComfyUI/custom_nodes/ComfyUI-Manager/js/comfyui-share-openart.js b/ComfyUI/custom_nodes/ComfyUI-Manager/js/comfyui-share-openart.js new file mode 100644 index 0000000000000000000000000000000000000000..1c96a8c73fae61a6c599fd5901e2fc0b26f8d55d --- /dev/null +++ b/ComfyUI/custom_nodes/ComfyUI-Manager/js/comfyui-share-openart.js @@ -0,0 +1,746 @@ +import {app} from "../../scripts/app.js"; +import {api} from "../../scripts/api.js"; +import {ComfyDialog, $el} from "../../scripts/ui.js"; +import { customAlert } from "./common.js"; + +const LOCAL_STORAGE_KEY = "openart_comfy_workflow_key"; +const DEFAULT_HOMEPAGE_URL = "https://openart.ai/workflows/dev?developer=true"; +//const DEFAULT_HOMEPAGE_URL = "http://localhost:8080/workflows/dev?developer=true"; + +const API_ENDPOINT = "https://openart.ai/api"; +//const API_ENDPOINT = "http://localhost:8080/api"; + +const style = ` + .openart-share-dialog a { + color: #f8f8f8; + } + .openart-share-dialog a:hover { + color: #007bff; + } + .output_label { + border: 5px solid transparent; + } + .output_label:hover { + border: 5px solid #59E8C6; + } + .output_label.checked { + border: 5px solid #59E8C6; + } +`; + +// Shared component styles +const sectionStyle = { + marginBottom: 0, + padding: 0, + borderRadius: "8px", + boxShadow: "0 2px 4px rgba(0, 0, 0, 0.05)", + display: "flex", + flexDirection: "column", + justifyContent: "center", +}; + +export class OpenArtShareDialog extends ComfyDialog { + static instance = null; + + constructor() { + super(); + $el("style", { + textContent: style, + parent: document.head, + }); + this.element = $el( + "div.comfy-modal.openart-share-dialog", + { + parent: document.body, + style: { + "overflow-y": "auto", + }, + }, + [$el("div.comfy-modal-content", {}, [...this.createButtons()])] + ); + this.selectedOutputIndex = 0; + this.selectedNodeId = null; + this.uploadedImages = []; + this.selectedFile = null; + } + + async readKey() { + let key = "" + try { + key = await api.fetchApi(`/manager/get_openart_auth`) + .then(response => response.json()) + .then(data => { + return data.openart_key; + }) + .catch(error => { + // console.log(error); + }); + } catch (error) { + // console.log(error); + } + return key || ""; + } + + async saveKey(value) { + await api.fetchApi(`/manager/set_openart_auth`, { + method: 'POST', + headers: {'Content-Type': 'application/json'}, + body: JSON.stringify({ + openart_key: value + }) + }); + } + + createButtons() { + const inputStyle = { + display: "block", + minWidth: "500px", + width: "100%", + padding: "10px", + margin: "10px 0", + borderRadius: "4px", + border: "1px solid #ddd", + boxSizing: "border-box", + }; + + const hyperLinkStyle = { + display: "block", + marginBottom: "15px", + fontWeight: "bold", + fontSize: "14px", + }; + + const labelStyle = { + color: "#f8f8f8", + display: "block", + margin: "10px 0 0 0", + fontWeight: "bold", + textDecoration: "none", + }; + + const buttonStyle = { + padding: "10px 80px", + margin: "10px 5px", + borderRadius: "4px", + border: "none", + cursor: "pointer", + color: "#fff", + backgroundColor: "#007bff", + }; + + // upload images input + this.uploadImagesInput = $el("input", { + type: "file", + multiple: false, + style: inputStyle, + accept: "image/*", + }); + + this.uploadImagesInput.addEventListener("change", async (e) => { + const file = e.target.files[0]; + if (!file) { + this.previewImage.src = ""; + this.previewImage.style.display = "none"; + return; + } + const reader = new FileReader(); + reader.onload = async (e) => { + const imgData = e.target.result; + this.previewImage.src = imgData; + this.previewImage.style.display = "block"; + this.selectedFile = null + // Once user uploads an image, we uncheck all radio buttons + this.radioButtons.forEach((ele) => { + ele.checked = false; + ele.parentElement.classList.remove("checked"); + }); + + // Add the opacity style toggle here to indicate that they only need + // to upload one image or choose one from the outputs. + this.outputsSection.style.opacity = 0.35; + this.uploadImagesInput.style.opacity = 1; + }; + reader.readAsDataURL(file); + }); + + // preview image + this.previewImage = $el("img", { + src: "", + style: { + width: "100%", + maxHeight: "100px", + objectFit: "contain", + display: "none", + marginTop: '10px', + }, + }); + + this.keyInput = $el("input", { + type: "password", + placeholder: "Copy & paste your API key", + style: inputStyle, + }); + this.NameInput = $el("input", { + type: "text", + placeholder: "Title (required)", + style: inputStyle, + }); + this.descriptionInput = $el("textarea", { + placeholder: "Description (optional)", + style: { + ...inputStyle, + minHeight: "100px", + }, + }); + + // Header Section + const headerSection = $el("h3", { + textContent: "Share your workflow to OpenArt", + size: 3, + color: "white", + style: { + 'text-align': 'center', + color: 'var(--input-text)', + margin: '0 0 10px 0', + } + }); + + // LinkSection + this.communityLink = $el("a", { + style: hyperLinkStyle, + href: DEFAULT_HOMEPAGE_URL, + target: "_blank" + }, ["👉 Check out thousands of workflows shared from the community"]) + this.getAPIKeyLink = $el("a", { + style: { + ...hyperLinkStyle, + color: "#59E8C6" + }, + href: DEFAULT_HOMEPAGE_URL, + target: "_blank" + }, ["👉 Get your API key here"]) + const linkSection = $el( + "div", + { + style: { + marginTop: "10px", + display: "flex", + flexDirection: "column", + }, + }, + [ + this.communityLink, + this.getAPIKeyLink, + ] + ); + + // Account Section + const accountSection = $el("div", {style: sectionStyle}, [ + $el("label", {style: labelStyle}, ["1️⃣ OpenArt API Key"]), + this.keyInput, + ]); + + // Output Upload Section + const outputUploadSection = $el("div", {style: sectionStyle}, [ + $el("label", { + style: { + ...labelStyle, + margin: "10px 0 0 0" + } + }, ["2️⃣ Image/Thumbnail (Required)"]), + this.previewImage, + this.uploadImagesInput, + ]); + + // Outputs Section + this.outputsSection = $el("div", { + id: "selectOutputs", + }, []); + + // Additional Inputs Section + const additionalInputsSection = $el("div", {style: sectionStyle}, [ + $el("label", {style: labelStyle}, ["3️⃣ Workflow Information"]), + this.NameInput, + this.descriptionInput, + ]); + + // OpenArt Contest Section + /* + this.joinContestCheckbox = $el("input", { + type: 'checkbox', + id: "join_contest"s + }, []) + this.joinContestDescription = $el("a", { + style: { + ...hyperLinkStyle, + display: 'inline-block', + color: "#59E8C6", + fontSize: '12px', + marginLeft: '10px', + marginBottom: 0, + }, + href: "https://contest.openart.ai/", + target: "_blank" + }, ["🏆 I'm participating in the OpenArt workflow contest"]) + this.joinContestLabel = $el("label", { + style: { + display: 'flex', + alignItems: 'center', + cursor: 'pointer', + } + }, [this.joinContestCheckbox, this.joinContestDescription]) + const contestSection = $el("div", {style: sectionStyle}, [ + this.joinContestLabel, + ]); + */ + + // Message Section + this.message = $el( + "div", + { + style: { + color: "#ff3d00", + textAlign: "center", + padding: "10px", + fontSize: "20px", + }, + }, + [] + ); + + this.shareButton = $el("button", { + type: "submit", + textContent: "Share", + style: buttonStyle, + onclick: () => { + this.handleShareButtonClick(); + }, + }); + + // Share and Close Buttons + const buttonsSection = $el( + "div", + { + style: { + textAlign: "right", + marginTop: "20px", + display: "flex", + justifyContent: "space-between", + }, + }, + [ + $el("button", { + type: "button", + textContent: "Close", + style: { + ...buttonStyle, + backgroundColor: undefined, + }, + onclick: () => { + this.close(); + }, + }), + this.shareButton, + ] + ); + + // Composing the full layout + const layout = [ + headerSection, + linkSection, + accountSection, + outputUploadSection, + this.outputsSection, + additionalInputsSection, + // contestSection, + this.message, + buttonsSection, + ]; + + return layout; + } + + async fetchApi(path, options, statusText) { + if (statusText) { + this.message.textContent = statusText; + } + const addSearchParams = (url, params = {}) => + new URL( + `${url.origin}${url.pathname}?${new URLSearchParams([ + ...Array.from(url.searchParams.entries()), + ...Object.entries(params), + ])}` + ); + + const fullPath = addSearchParams(new URL(API_ENDPOINT + path), { + workflow_api_key: this.keyInput.value, + }); + + const response = await fetch(fullPath, options); + + if (!response.ok) { + throw new Error(response.statusText); + } + + if (statusText) { + this.message.textContent = ""; + } + const data = await response.json(); + return { + ok: response.ok, + statusText: response.statusText, + status: response.status, + data, + }; + } + + async uploadThumbnail(uploadFile) { + const form = new FormData(); + form.append("file", uploadFile); + try { + const res = await this.fetchApi( + `/workflows/upload_thumbnail`, + { + method: "POST", + body: form, + }, + "Uploading thumbnail..." + ); + + if (res.ok && res.data) { + const {image_url, width, height} = res.data; + this.uploadedImages.push({ + url: image_url, + width, + height, + }); + } + } catch (e) { + if (e?.response?.status === 413) { + throw new Error("File size is too large (max 20MB)"); + } else { + throw new Error("Error uploading thumbnail: " + e.message); + } + } + } + + async handleShareButtonClick() { + this.message.textContent = ""; + await this.saveKey(this.keyInput.value); + try { + this.shareButton.disabled = true; + this.shareButton.textContent = "Sharing..."; + await this.share(); + } catch (e) { + customAlert(e.message); + } + this.shareButton.disabled = false; + this.shareButton.textContent = "Share"; + } + + async share() { + const prompt = await app.graphToPrompt(); + const workflowJSON = prompt["workflow"]; + const workflowAPIJSON = prompt["output"]; + const form_values = { + name: this.NameInput.value, + description: this.descriptionInput.value, + }; + + if (!this.keyInput.value) { + throw new Error("API key is required"); + } + + if (!this.uploadImagesInput.files[0] && !this.selectedFile) { + throw new Error("Thumbnail is required"); + } + + if (!form_values.name) { + throw new Error("Title is required"); + } + + const current_snapshot = await api.fetchApi(`/snapshot/get_current`) + .then(response => response.json()) + .catch(error => { + // console.log(error); + }); + + + if (!this.uploadedImages.length) { + if (this.selectedFile) { + await this.uploadThumbnail(this.selectedFile); + } else { + for (const file of this.uploadImagesInput.files) { + try { + await this.uploadThumbnail(file); + } catch (e) { + this.uploadedImages = []; + throw new Error(e.message); + } + } + + if (this.uploadImagesInput.files.length === 0) { + throw new Error("No thumbnail uploaded"); + } + } + } + + // const join_contest = this.joinContestCheckbox.checked; + + try { + const response = await this.fetchApi( + "/workflows/publish", + { + method: "POST", + headers: {"Content-Type": "application/json"}, + body: JSON.stringify({ + workflow_json: workflowJSON, + upload_images: this.uploadedImages, + form_values, + advanced_config: { + workflow_api_json: workflowAPIJSON, + snapshot: current_snapshot, + }, + // join_contest, + }), + }, + "Uploading workflow..." + ); + + if (response.ok) { + const {workflow_id} = response.data; + if (workflow_id) { + const url = `https://openart.ai/workflows/-/-/${workflow_id}`; + this.message.innerHTML = `Workflow has been shared successfully. Click here to view it.`; + this.previewImage.src = ""; + this.previewImage.style.display = "none"; + this.uploadedImages = []; + this.NameInput.value = ""; + this.descriptionInput.value = ""; + this.radioButtons.forEach((ele) => { + ele.checked = false; + ele.parentElement.classList.remove("checked"); + }); + this.selectedOutputIndex = 0; + this.selectedNodeId = null; + this.selectedFile = null; + } + } + } catch (e) { + throw new Error("Error sharing workflow: " + e.message); + } + } + + async fetchImageBlob(url) { + const response = await fetch(url); + const blob = await response.blob(); + return blob; + } + + async show({potential_outputs, potential_output_nodes} = {}) { + // Sort `potential_output_nodes` by node ID to make the order always + // consistent, but we should also keep `potential_outputs` in the same + // order as `potential_output_nodes`. + const potential_output_to_order = {}; + potential_output_nodes.forEach((node, index) => { + if (node.id in potential_output_to_order) { + potential_output_to_order[node.id][1].push(potential_outputs[index]); + } else { + potential_output_to_order[node.id] = [node, [potential_outputs[index]]]; + } + }) + // Sort the object `potential_output_to_order` by key (node ID) + const sorted_potential_output_to_order = Object.fromEntries( + Object.entries(potential_output_to_order).sort((a, b) => a[0].id - b[0].id) + ); + const sorted_potential_outputs = [] + const sorted_potential_output_nodes = [] + for (const [key, value] of Object.entries(sorted_potential_output_to_order)) { + sorted_potential_output_nodes.push(value[0]); + sorted_potential_outputs.push(...value[1]); + } + potential_output_nodes = sorted_potential_output_nodes; + potential_outputs = sorted_potential_outputs; + + this.message.innerHTML = ""; + this.message.textContent = ""; + this.element.style.display = "block"; + this.previewImage.src = ""; + this.previewImage.style.display = "none"; + const key = await this.readKey(); + this.keyInput.value = key; + this.uploadedImages = []; + + // If `selectedNodeId` is provided, we will select the corresponding radio + // button for the node. In addition, we move the selected radio button to + // the top of the list. + if (this.selectedNodeId) { + const index = potential_output_nodes.findIndex(node => node.id === this.selectedNodeId); + if (index >= 0) { + this.selectedOutputIndex = index; + } + } + + this.radioButtons = []; + const new_radio_buttons = $el("div", + { + id: "selectOutput-Options", + style: { + 'overflow-y': 'scroll', + 'max-height': '200px', + + 'display': 'grid', + 'grid-template-columns': 'repeat(auto-fit, minmax(100px, 1fr))', + 'grid-template-rows': 'auto', + 'grid-column-gap': '10px', + 'grid-row-gap': '10px', + 'margin-bottom': '10px', + 'padding': '10px', + 'border-radius': '8px', + 'box-shadow': '0 2px 4px rgba(0, 0, 0, 0.05)', + 'background-color': 'var(--bg-color)', + } + }, + potential_outputs.map((output, index) => { + const {node_id} = output; + const radio_button = $el("input", { + type: 'radio', + name: "selectOutputImages", + value: index, + required: index === 0 + }, []) + let radio_button_img; + let filename; + if (output.type === "image" || output.type === "temp") { + radio_button_img = $el("img", { + src: `/view?filename=${output.image.filename}&subfolder=${output.image.subfolder}&type=${output.image.type}`, + style: { + width: "100px", + height: "100px", + objectFit: "cover", + borderRadius: "5px" + } + }, []); + filename = output.image.filename + } else if (output.type === "output") { + radio_button_img = $el("img", { + src: output.output.value, + style: { + width: "auto", + height: "100px", + objectFit: "cover", + borderRadius: "5px" + } + }, []); + filename = output.filename + } else { + // unsupported output type + // this should never happen + // TODO + radio_button_img = $el("img", { + src: "", + style: {width: "auto", height: "100px"} + }, []); + } + const radio_button_text = $el("span", { + style: { + color: 'gray', + display: 'block', + fontSize: '12px', + overflowX: 'hidden', + textOverflow: 'ellipsis', + textWrap: 'nowrap', + maxWidth: '100px', + } + }, [output.title]) + const node_id_chip = $el("span", { + style: { + color: '#FBFBFD', + display: 'block', + backgroundColor: 'rgba(0, 0, 0, 0.5)', + fontSize: '12px', + overflowX: 'hidden', + padding: '2px 3px', + textOverflow: 'ellipsis', + textWrap: 'nowrap', + maxWidth: '100px', + position: 'absolute', + top: '3px', + left: '3px', + borderRadius: '3px', + } + }, [`Node: ${node_id}`]) + radio_button.style.color = "var(--fg-color)"; + radio_button.checked = this.selectedOutputIndex === index; + + radio_button.onchange = async () => { + this.selectedOutputIndex = parseInt(radio_button.value); + + // Remove the "checked" class from all radio buttons + this.radioButtons.forEach((ele) => { + ele.parentElement.classList.remove("checked"); + }); + radio_button.parentElement.classList.add("checked"); + + this.fetchImageBlob(radio_button_img.src).then((blob) => { + const file = new File([blob], filename, { + type: blob.type, + }); + this.previewImage.src = radio_button_img.src; + this.previewImage.style.display = "block"; + this.selectedFile = file; + }) + + // Add the opacity style toggle here to indicate that they only need + // to upload one image or choose one from the outputs. + this.outputsSection.style.opacity = 1; + this.uploadImagesInput.style.opacity = 0.35; + }; + + if (radio_button.checked) { + this.fetchImageBlob(radio_button_img.src).then((blob) => { + const file = new File([blob], filename, { + type: blob.type, + }); + this.previewImage.src = radio_button_img.src; + this.previewImage.style.display = "block"; + this.selectedFile = file; + }) + // Add the opacity style toggle here to indicate that they only need + // to upload one image or choose one from the outputs. + this.outputsSection.style.opacity = 1; + this.uploadImagesInput.style.opacity = 0.35; + } + + this.radioButtons.push(radio_button); + + return $el(`label.output_label${radio_button.checked ? '.checked' : ''}`, { + style: { + display: "flex", + flexDirection: "column", + alignItems: "center", + justifyContent: "center", + marginBottom: "10px", + cursor: "pointer", + position: 'relative', + } + }, [radio_button_img, radio_button_text, radio_button, node_id_chip]); + }) + ); + + const header = + $el("p", { + textContent: this.radioButtons.length === 0 ? "Queue Prompt to see the outputs" : "Or choose one from the outputs (scroll to see all)", + size: 2, + color: "white", + style: { + color: 'var(--input-text)', + margin: '0 0 5px 0', + fontSize: '12px', + }, + }, []) + this.outputsSection.innerHTML = ""; + this.outputsSection.appendChild(header); + this.outputsSection.appendChild(new_radio_buttons); + } +} diff --git a/ComfyUI/custom_nodes/ComfyUI-Manager/js/components-manager.js b/ComfyUI/custom_nodes/ComfyUI-Manager/js/components-manager.js new file mode 100644 index 0000000000000000000000000000000000000000..9244d2a4874b9aab9f5423f75ed4a9e952661415 --- /dev/null +++ b/ComfyUI/custom_nodes/ComfyUI-Manager/js/components-manager.js @@ -0,0 +1,812 @@ +import { app } from "../../scripts/app.js"; +import { api } from "../../scripts/api.js" +import { sleep, show_message, customConfirm, customAlert } from "./common.js"; +import { GroupNodeConfig, GroupNodeHandler } from "../../extensions/core/groupNode.js"; +import { ComfyDialog, $el } from "../../scripts/ui.js"; + +const SEPARATOR = ">" + +let pack_map = {}; +let rpack_map = {}; + +export function getPureName(node) { + // group nodes/ + let category = null; + if(node.category) { + category = node.category.substring(12); + } + else { + category = node.constructor.category?.substring(12); + } + if(category) { + let purename = node.comfyClass.substring(category.length+1); + return purename; + } + else if(node.comfyClass.startsWith('workflow/') || node.comfyClass.startsWith(`workflow${SEPARATOR}`)) { + return node.comfyClass.substring(9); + } + else { + return node.comfyClass; + } +} + +function isValidVersionString(version) { + const versionPattern = /^(\d+)\.(\d+)(\.(\d+))?$/; + + const match = version.match(versionPattern); + + return match !== null && + parseInt(match[1], 10) >= 0 && + parseInt(match[2], 10) >= 0 && + (!match[3] || parseInt(match[4], 10) >= 0); +} + +function register_pack_map(name, data) { + if(data.packname) { + pack_map[data.packname] = name; + rpack_map[name] = data; + } + else { + rpack_map[name] = data; + } +} + +function storeGroupNode(name, data, register=true) { + let extra = app.graph.extra; + if (!extra) app.graph.extra = extra = {}; + let groupNodes = extra.groupNodes; + if (!groupNodes) extra.groupNodes = groupNodes = {}; + groupNodes[name] = data; + + if(register) { + register_pack_map(name, data); + } +} + +export async function load_components() { + let data = await api.fetchApi('/manager/component/loads', {method: "POST"}); + let components = await data.json(); + + let start_time = Date.now(); + let failed = []; + let failed2 = []; + + for(let name in components) { + if(app.graph.extra?.groupNodes?.[name]) { + if(data) { + let data = components[name]; + + let category = data.packname; + if(data.category) { + category += SEPARATOR + data.category; + } + if(category == '') { + category = 'components'; + } + + const config = new GroupNodeConfig(name, data); + await config.registerType(category); + + register_pack_map(name, data); + continue; + } + } + + let nodeData = components[name]; + + storeGroupNode(name, nodeData); + + const config = new GroupNodeConfig(name, nodeData); + + while(true) { + try { + let category = nodeData.packname; + if(nodeData.category) { + category += SEPARATOR + nodeData.category; + } + if(category == '') { + category = 'components'; + } + + await config.registerType(category); + register_pack_map(name, nodeData); + break; + } + catch { + let elapsed_time = Date.now() - start_time; + if (elapsed_time > 5000) { + failed.push(name); + break; + } else { + await sleep(100); + } + } + } + } + + // fallback1 + for(let i in failed) { + let name = failed[i]; + + if(app.graph.extra?.groupNodes?.[name]) { + continue; + } + + let nodeData = components[name]; + + storeGroupNode(name, nodeData); + + const config = new GroupNodeConfig(name, nodeData); + while(true) { + try { + let category = nodeData.packname; + if(nodeData.workflow.category) { + category += SEPARATOR + nodeData.category; + } + if(category == '') { + category = 'components'; + } + + await config.registerType(category); + register_pack_map(name, nodeData); + break; + } + catch { + let elapsed_time = Date.now() - start_time; + if (elapsed_time > 10000) { + failed2.push(name); + break; + } else { + await sleep(100); + } + } + } + } + + // fallback2 + for(let name in failed2) { + let name = failed2[i]; + + let nodeData = components[name]; + + storeGroupNode(name, nodeData); + + const config = new GroupNodeConfig(name, nodeData); + while(true) { + try { + let category = nodeData.workflow.packname; + if(nodeData.workflow.category) { + category += SEPARATOR + nodeData.category; + } + if(category == '') { + category = 'components'; + } + + await config.registerType(category); + register_pack_map(name, nodeData); + break; + } + catch { + let elapsed_time = Date.now() - start_time; + if (elapsed_time > 30000) { + failed.push(name); + break; + } else { + await sleep(100); + } + } + } + } +} + +async function save_as_component(node, version, author, prefix, nodename, packname, category) { + let component_name = `${prefix}::${nodename}`; + + let subgraph = app.graph.extra?.groupNodes?.[component_name]; + if(!subgraph) { + subgraph = app.graph.extra?.groupNodes?.[getPureName(node)]; + } + + subgraph.version = version; + subgraph.author = author; + subgraph.datetime = Date.now(); + subgraph.packname = packname; + subgraph.category = category; + + let body = + { + name: component_name, + workflow: subgraph + }; + + pack_map[packname] = component_name; + rpack_map[component_name] = subgraph; + + const res = await api.fetchApi('/manager/component/save', { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(body), + }); + + if(res.status == 200) { + storeGroupNode(component_name, subgraph); + const config = new GroupNodeConfig(component_name, subgraph); + + let category = body.workflow.packname; + if(body.workflow.category) { + category += SEPARATOR + body.workflow.category; + } + if(category == '') { + category = 'components'; + } + + await config.registerType(category); + + let path = await res.text(); + show_message(`Component '${component_name}' is saved into:\n${path}`); + } + else + show_message(`Failed to save component.`); +} + +async function import_component(component_name, component, mode) { + if(mode) { + let body = + { + name: component_name, + workflow: component + }; + + const res = await api.fetchApi('/manager/component/save', { + method: "POST", + headers: { "Content-Type": "application/json", }, + body: JSON.stringify(body) + }); + } + + let category = component.packname; + if(component.category) { + category += SEPARATOR + component.category; + } + if(category == '') { + category = 'components'; + } + + storeGroupNode(component_name, component); + const config = new GroupNodeConfig(component_name, component); + await config.registerType(category); +} + +function restore_to_loaded_component(component_name) { + if(rpack_map[component_name]) { + let component = rpack_map[component_name]; + storeGroupNode(component_name, component, false); + const config = new GroupNodeConfig(component_name, component); + config.registerType(component.category); + } +} + +// Using a timestamp prevents duplicate pastes and ensures the prevention of re-deletion of litegrapheditor_clipboard. +let last_paste_timestamp = null; + +function versionCompare(v1, v2) { + let ver1; + let ver2; + if(v1 && v1 != '') { + ver1 = v1.split('.'); + ver1[0] = parseInt(ver1[0]); + ver1[1] = parseInt(ver1[1]); + if(ver1.length == 2) + ver1.push(0); + else + ver1[2] = parseInt(ver2[2]); + } + else { + ver1 = [0,0,0]; + } + + if(v2 && v2 != '') { + ver2 = v2.split('.'); + ver2[0] = parseInt(ver2[0]); + ver2[1] = parseInt(ver2[1]); + if(ver2.length == 2) + ver2.push(0); + else + ver2[2] = parseInt(ver2[2]); + } + else { + ver2 = [0,0,0]; + } + + if(ver1[0] > ver2[0]) + return -1; + else if(ver1[0] < ver2[0]) + return 1; + + if(ver1[1] > ver2[1]) + return -1; + else if(ver1[1] < ver2[1]) + return 1; + + if(ver1[2] > ver2[2]) + return -1; + else if(ver1[2] < ver2[2]) + return 1; + + return 0; +} + +function checkVersion(name, component) { + let msg = ''; + if(rpack_map[name]) { + let old_version = rpack_map[name].version; + if(!old_version || old_version == '') { + msg = ` '${name}' Upgrade (V0.0 -> V${component.version})`; + } + else { + let c = versionCompare(old_version, component.version); + if(c < 0) { + msg = ` '${name}' Downgrade (V${old_version} -> V${component.version})`; + } + else if(c > 0) { + msg = ` '${name}' Upgrade (V${old_version} -> V${component.version})`; + } + else { + msg = ` '${name}' Same version (V${component.version})`; + } + } + } + else { + msg = `'${name}' NEW (V${component.version})`; + } + + return msg; +} + +async function handle_import_components(components) { + let msg = 'Components:\n'; + let cnt = 0; + for(let name in components) { + let component = components[name]; + let v = checkVersion(name, component); + + if(cnt < 10) { + msg += v + '\n'; + } + else if (cnt == 10) { + msg += '...\n'; + } + else { + // do nothing + } + + cnt++; + } + + let last_name = null; + msg += '\nWill you load components?\n'; + const confirmed = await customConfirm(msg); + if(confirmed) { + const mode = await customConfirm('\nWill you save components?\n(cancel=load without save)'); + + for(let name in components) { + let component = components[name]; + import_component(name, component, mode); + last_name = name; + } + + if(mode) { + show_message('Components are saved.'); + } + else { + show_message('Components are loaded.'); + } + } + + if(cnt == 1 && last_name) { + const node = LiteGraph.createNode(`workflow${SEPARATOR}${last_name}`); + node.pos = [app.canvas.graph_mouse[0], app.canvas.graph_mouse[1]]; + app.canvas.graph.add(node, false); + } +} + +async function handlePaste(e) { + let data = (e.clipboardData || window.clipboardData); + const items = data.items; + for(const item of items) { + if(item.kind == 'string' && item.type == 'text/plain') { + data = data.getData("text/plain"); + try { + let json_data = JSON.parse(data); + if(json_data.kind == 'ComfyUI Components' && last_paste_timestamp != json_data.timestamp) { + last_paste_timestamp = json_data.timestamp; + await handle_import_components(json_data.components); + + // disable paste node + localStorage.removeItem("litegrapheditor_clipboard", null); + } + else { + console.log('This components are already pasted: ignored'); + } + } + catch { + // nothing to do + } + } + } +} + +document.addEventListener("paste", handlePaste); + + +export class ComponentBuilderDialog extends ComfyDialog { + constructor() { + super(); + } + + clear() { + while (this.element.children.length) { + this.element.removeChild(this.element.children[0]); + } + } + + show() { + this.invalidateControl(); + + this.element.style.display = "block"; + this.element.style.zIndex = 1099; + this.element.style.width = "500px"; + this.element.style.height = "480px"; + } + + invalidateControl() { + this.clear(); + + let self = this; + + const close_button = $el("button", { id: "cm-close-button", type: "button", textContent: "Close", onclick: () => self.close() }); + this.save_button = $el("button", + { id: "cm-save-button", type: "button", textContent: "Save", onclick: () => + { + save_as_component(self.target_node, self.version_string.value.trim(), self.author.value.trim(), self.node_prefix.value.trim(), + self.getNodeName(), self.getPackName(), self.category.value.trim()); + } + }); + + let default_nodename = getPureName(this.target_node).trim(); + + let groupNode = app.graph.extra.groupNodes[default_nodename]; + let default_packname = groupNode.packname; + if(!default_packname) { + default_packname = ''; + } + + let default_category = groupNode.category; + if(!default_category) { + default_category = ''; + } + + this.default_ver = groupNode.version; + if(!this.default_ver) { + this.default_ver = '0.0'; + } + + let default_author = groupNode.author; + if(!default_author) { + default_author = ''; + } + + let delimiterIndex = default_nodename.indexOf('::'); + let default_prefix = ""; + if(delimiterIndex != -1) { + default_prefix = default_nodename.substring(0, delimiterIndex); + default_nodename = default_nodename.substring(delimiterIndex + 2); + } + + if(!default_prefix) { + this.save_button.disabled = true; + } + + this.pack_list = this.createPackListCombo(); + + let version_string = this.createLabeledInput('input version (e.g. 1.0)', '*Version : ', this.default_ver); + this.version_string = version_string[1]; + this.version_string.disabled = true; + + let author = this.createLabeledInput('input author (e.g. Dr.Lt.Data)', 'Author : ', default_author); + this.author = author[1]; + + let node_prefix = this.createLabeledInput('input node prefix (e.g. mypack)', '*Prefix : ', default_prefix); + this.node_prefix = node_prefix[1]; + + let manual_nodename = this.createLabeledInput('input node name (e.g. MAKE_BASIC_PIPE)', 'Nodename : ', default_nodename); + this.manual_nodename = manual_nodename[1]; + + let manual_packname = this.createLabeledInput('input pack name (e.g. mypack)', 'Packname : ', default_packname); + this.manual_packname = manual_packname[1]; + + let category = this.createLabeledInput('input category (e.g. util/pipe)', 'Category : ', default_category); + this.category = category[1]; + + this.node_label = this.createNodeLabel(); + + let author_mode = this.createAuthorModeCheck(); + this.author_mode = author_mode[0]; + + const content = + $el("div.comfy-modal-content", + [ + $el("tr.cm-title", {}, [ + $el("font", {size:6, color:"white"}, [`ComfyUI-Manager: Component Builder`])] + ), + $el("br", {}, []), + $el("div.cm-menu-container", + [ + author_mode[0], + author_mode[1], + category[0], + author[0], + node_prefix[0], + manual_nodename[0], + manual_packname[0], + version_string[0], + this.pack_list, + $el("br", {}, []), + this.node_label + ]), + + $el("br", {}, []), + this.save_button, + close_button, + ] + ); + + content.style.width = '100%'; + content.style.height = '100%'; + + this.element = $el("div.comfy-modal", { id:'cm-manager-dialog', parent: document.body }, [ content ]); + } + + validateInput() { + let msg = ""; + + if(!isValidVersionString(this.version_string.value)) { + msg += 'Invalid version string: '+event.value+"\n"; + } + + if(this.node_prefix.value.trim() == '') { + msg += 'Node prefix cannot be empty\n'; + } + + if(this.manual_nodename.value.trim() == '') { + msg += 'Node name cannot be empty\n'; + } + + if(msg != '') { +// alert(msg); + } + + this.save_button.disabled = msg != ""; + } + + getPackName() { + if(this.pack_list.selectedIndex == 0) { + return this.manual_packname.value.trim(); + } + + return this.pack_list.value.trim(); + } + + getNodeName() { + if(this.manual_nodename.value.trim() != '') { + return this.manual_nodename.value.trim(); + } + + return getPureName(this.target_node); + } + + createAuthorModeCheck() { + let check = $el("input",{type:'checkbox', id:"author-mode"},[]) + const check_label = $el("label",{for:"author-mode"},["Enable author mode"]); + check_label.style.color = "var(--fg-color)"; + check_label.style.cursor = "pointer"; + check.checked = false; + + let self = this; + check.onchange = () => { + self.version_string.disabled = !check.checked; + + if(!check.checked) { + self.version_string.value = self.default_ver; + } + else { + customAlert('If you are not the author, it is not recommended to change the version, as it may cause component update issues.'); + } + }; + + return [check, check_label]; + } + + createNodeLabel() { + let label = $el('p'); + label.className = 'cb-node-label'; + if(this.target_node.comfyClass.includes('::')) + label.textContent = getPureName(this.target_node); + else + label.textContent = " _::" + getPureName(this.target_node); + return label; + } + + createLabeledInput(placeholder, label, value) { + let textbox = $el('input.cb-widget-input', {type:'text', placeholder:placeholder, value:value}, []); + + let self = this; + textbox.onchange = () => { + this.validateInput.call(self); + this.node_label.textContent = this.node_prefix.value + "::" + this.manual_nodename.value; + } + let row = $el('span.cb-widget', {}, [ $el('span.cb-widget-input-label', label), textbox]); + + return [row, textbox]; + } + + createPackListCombo() { + let combo = document.createElement("select"); + combo.className = "cb-widget"; + let default_packname_option = { value: '##manual', text: 'Packname: Manual' }; + + combo.appendChild($el('option', default_packname_option, [])); + for(let name in pack_map) { + combo.appendChild($el('option', { value: name, text: 'Packname: '+ name }, [])); + } + + let self = this; + combo.onchange = function () { + if(combo.selectedIndex == 0) { + self.manual_packname.disabled = false; + } + else { + self.manual_packname.disabled = true; + } + }; + + return combo; + } +} + +let orig_handleFile = app.handleFile; + +async function handleFile(file) { + if (file.name?.endsWith(".json") || file.name?.endsWith(".pack")) { + const reader = new FileReader(); + reader.onload = async () => { + let is_component = false; + const jsonContent = JSON.parse(reader.result); + for(let name in jsonContent) { + let cand = jsonContent[name]; + is_component = cand.datetime && cand.version; + break; + } + + if(is_component) { + await handle_import_components(jsonContent); + } + else { + orig_handleFile.call(app, file); + } + }; + reader.readAsText(file); + + return; + } + + orig_handleFile.call(app, file); +} + +app.handleFile = handleFile; + +let current_component_policy = 'workflow'; +try { + api.fetchApi('/manager/policy/component') + .then(response => response.text()) + .then(data => { current_component_policy = data; }); +} +catch {} + +function getChangedVersion(groupNodes) { + if(!Object.keys(pack_map).length || !groupNodes) + return null; + + let res = {}; + for(let component_name in groupNodes) { + let data = groupNodes[component_name]; + + if(rpack_map[component_name]) { + let v = versionCompare(data.version, rpack_map[component_name].version); + res[component_name] = v; + } + } + + return res; +} + +const loadGraphData = app.loadGraphData; +app.loadGraphData = async function () { + if(arguments.length == 0) + return await loadGraphData.apply(this, arguments); + + let graphData = arguments[0]; + let groupNodes = graphData.extra?.groupNodes; + let res = getChangedVersion(groupNodes); + + if(res) { + let target_components = null; + switch(current_component_policy) { + case 'higher': + target_components = Object.keys(res).filter(key => res[key] == 1); + break; + + case 'mine': + target_components = Object.keys(res); + break; + + default: + // do nothing + } + + if(target_components) { + for(let i in target_components) { + let component_name = target_components[i]; + let component = rpack_map[component_name]; + if(component && graphData.extra?.groupNodes) { + graphData.extra.groupNodes[component_name] = component; + } + } + } + } + else { + console.log('Empty components: policy ignored'); + } + + arguments[0] = graphData; + return await loadGraphData.apply(this, arguments); +}; + +export function set_component_policy(v) { + current_component_policy = v; +} + +let graphToPrompt = app.graphToPrompt; +app.graphToPrompt = async function () { + let p = await graphToPrompt.call(app); + try { + let groupNodes = p.workflow.extra?.groupNodes; + if(groupNodes) { + p.workflow.extra = { ... p.workflow.extra}; + + // get used group nodes + let used_group_nodes = new Set(); + for(let node of p.workflow.nodes) { + if(node.type.startsWith(`workflow/`) || node.type.startsWith(`workflow${SEPARATOR}`)) { + used_group_nodes.add(node.type.substring(9)); + } + } + + // remove unused group nodes + let new_groupNodes = {}; + for (let key in p.workflow.extra.groupNodes) { + if (used_group_nodes.has(key)) { + new_groupNodes[key] = p.workflow.extra.groupNodes[key]; + } + } + p.workflow.extra.groupNodes = new_groupNodes; + } + } + catch(e) { + console.log(`Failed to filtering group nodes: ${e}`); + } + + return p; +} diff --git a/ComfyUI/custom_nodes/ComfyUI-Manager/js/custom-nodes-manager.css b/ComfyUI/custom_nodes/ComfyUI-Manager/js/custom-nodes-manager.css new file mode 100644 index 0000000000000000000000000000000000000000..00e1e4c19c3ca36d0465d56bb610b477fea4a087 --- /dev/null +++ b/ComfyUI/custom_nodes/ComfyUI-Manager/js/custom-nodes-manager.css @@ -0,0 +1,699 @@ +.cn-manager { + --grid-font: -apple-system, BlinkMacSystemFont, "Segue UI", "Noto Sans", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; + z-index: 1099; + width: 80%; + height: 80%; + display: flex; + flex-direction: column; + gap: 10px; + color: var(--fg-color); + font-family: arial, sans-serif; + text-underline-offset: 3px; + outline: none; +} + +.cn-manager .cn-flex-auto { + flex: auto; +} + +.cn-manager button { + font-size: 16px; + color: var(--input-text); + background-color: var(--comfy-input-bg); + border-radius: 8px; + border-color: var(--border-color); + border-style: solid; + margin: 0; + padding: 4px 8px; + min-width: 100px; +} + +.cn-manager button:disabled, +.cn-manager input:disabled, +.cn-manager select:disabled { + color: gray; +} + +.cn-manager button:disabled { + background-color: var(--comfy-input-bg); +} + +.cn-manager .cn-manager-restart { + display: none; + background-color: #500000; + color: white; +} + +.cn-manager .cn-manager-stop { + display: none; + background-color: #500000; + color: white; +} + +.cn-manager .cn-manager-back { + align-items: center; + justify-content: center; +} + +.arrow-icon { + height: 1em; + width: 1em; + margin-right: 5px; + transform: translateY(2px); +} + +.cn-icon { + display: block; + width: 16px; + height: 16px; +} + +.cn-icon svg { + display: block; + margin: 0; + pointer-events: none; +} + +.cn-manager-header { + display: flex; + flex-wrap: wrap; + gap: 5px; + align-items: center; + padding: 0 5px; +} + +.cn-manager-header label { + display: flex; + gap: 5px; + align-items: center; +} + +.cn-manager-filter { + height: 28px; + line-height: 28px; +} + +.cn-manager-keywords { + height: 28px; + line-height: 28px; + padding: 0 5px 0 26px; + background-size: 16px; + background-position: 5px center; + background-repeat: no-repeat; + background-image: url("data:image/svg+xml;charset=utf8,%3Csvg%20viewBox%3D%220%200%2024%2024%22%20width%3D%22100%25%22%20height%3D%22100%25%22%20pointer-events%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20fill%3D%22none%22%20stroke%3D%22%23888%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20stroke-width%3D%222%22%20d%3D%22m21%2021-4.486-4.494M19%2010.5a8.5%208.5%200%201%201-17%200%208.5%208.5%200%200%201%2017%200%22%2F%3E%3C%2Fsvg%3E"); +} + +.cn-manager-status { + padding-left: 10px; +} + +.cn-manager-grid { + flex: auto; + border: 1px solid var(--border-color); + overflow: hidden; + position: relative; +} + +.cn-manager-selection { + display: flex; + flex-wrap: wrap; + gap: 10px; + align-items: center; +} + +.cn-manager-message { + position: relative; +} + +.cn-manager-footer { + display: flex; + flex-wrap: wrap; + gap: 10px; + align-items: center; +} + +.cn-manager-grid .tg-turbogrid { + font-family: var(--grid-font); + font-size: 15px; + background: var(--bg-color); +} + +.cn-manager-grid .tg-turbogrid .tg-highlight::after { + position: absolute; + top: 0; + left: 0; + content: ""; + display: block; + width: 100%; + height: 100%; + box-sizing: border-box; + background-color: #80bdff11; + pointer-events: none; +} + +.cn-manager-grid .cn-pack-name a { + color: skyblue; + text-decoration: none; + word-break: break-word; +} + +.cn-manager-grid .cn-pack-desc a { + color: #5555FF; + font-weight: bold; + text-decoration: none; +} + +.cn-manager-grid .tg-cell a:hover { + text-decoration: underline; +} + +.cn-manager-grid .cn-pack-version { + line-height: 100%; + display: flex; + flex-direction: column; + justify-content: center; + height: 100%; + gap: 5px; +} + +.cn-manager-grid .cn-pack-nodes { + line-height: 100%; + display: flex; + flex-direction: column; + justify-content: center; + gap: 5px; + cursor: pointer; + height: 100%; +} + +.cn-manager-grid .cn-pack-nodes:hover { + text-decoration: underline; +} + +.cn-manager-grid .cn-pack-conflicts { + color: orange; +} + +.cn-popover { + position: fixed; + z-index: 10000; + padding: 20px; + color: #1e1e1e; + filter: drop-shadow(1px 5px 5px rgb(0 0 0 / 30%)); + overflow: hidden; +} + +.cn-flyover { + position: absolute; + top: 0; + right: 0; + z-index: 1000; + display: none; + width: 50%; + height: 100%; + background-color: var(--comfy-menu-bg); + animation-duration: 0.2s; + animation-fill-mode: both; + flex-direction: column; +} + +.cn-flyover::before { + position: absolute; + top: 0; + content: ""; + z-index: 10; + display: block; + width: 10px; + height: 100%; + pointer-events: none; + left: -10px; + background-image: linear-gradient(to left, rgb(0 0 0 / 20%), rgb(0 0 0 / 0%)); +} + +.cn-flyover-header { + height: 45px; + display: flex; + align-items: center; + gap: 5px; + border-bottom: 1px solid var(--border-color); +} + +.cn-flyover-close { + display: flex; + align-items: center; + padding: 0 10px; + justify-content: center; + cursor: pointer; + opacity: 0.8; + height: 100%; +} + +.cn-flyover-close:hover { + opacity: 1; +} + +.cn-flyover-close svg { + display: block; + margin: 0; + pointer-events: none; + width: 20px; + height: 20px; +} + +.cn-flyover-title { + display: flex; + align-items: center; + font-weight: bold; + gap: 10px; + flex: auto; +} + +.cn-flyover-body { + height: calc(100% - 45px); + overflow-y: auto; + position: relative; + background-color: var(--comfy-menu-secondary-bg); +} + +@keyframes cn-slide-in-right { + from { + visibility: visible; + transform: translate3d(100%, 0, 0); + } + + to { + transform: translate3d(0, 0, 0); + } +} + +.cn-slide-in-right { + animation-name: cn-slide-in-right; +} + +@keyframes cn-slide-out-right { + from { + transform: translate3d(0, 0, 0); + } + + to { + visibility: hidden; + transform: translate3d(100%, 0, 0); + } +} + +.cn-slide-out-right { + animation-name: cn-slide-out-right; +} + +.cn-nodes-list { + width: 100%; +} + +.cn-nodes-row { + display: flex; + align-items: center; + gap: 10px; +} + +.cn-nodes-row:nth-child(odd) { + background-color: rgb(0 0 0 / 5%); +} + +.cn-nodes-row:hover { + background-color: rgb(0 0 0 / 10%); +} + +.cn-nodes-sn { + text-align: right; + min-width: 35px; + color: var(--drag-text); + flex-shrink: 0; + font-size: 12px; + padding: 8px 5px; +} + +.cn-nodes-name { + cursor: pointer; + white-space: nowrap; + flex-shrink: 0; + position: relative; + padding: 8px 5px; +} + +.cn-nodes-name::after { + content: attr(action); + position: absolute; + pointer-events: none; + top: 50%; + left: 100%; + transform: translate(5px, -50%); + font-size: 12px; + color: var(--drag-text); + background-color: var(--comfy-input-bg); + border-radius: 10px; + border: 1px solid var(--border-color); + padding: 3px 8px; + display: none; +} + +.cn-nodes-name.action::after { + display: block; +} + +.cn-nodes-name:hover { + text-decoration: underline; +} + +.cn-nodes-conflict .cn-nodes-name, +.cn-nodes-conflict .cn-icon { + color: orange; +} + +.cn-conflicts-list { + display: flex; + flex-wrap: wrap; + gap: 5px; + align-items: center; + padding: 5px 0; +} + +.cn-conflicts-list b { + font-weight: normal; + color: var(--descrip-text); +} + +.cn-nodes-pack { + cursor: pointer; + color: skyblue; +} + +.cn-nodes-pack:hover { + text-decoration: underline; +} + +.cn-pack-badge { + font-size: 12px; + font-weight: normal; + background-color: var(--comfy-input-bg); + border-radius: 10px; + border: 1px solid var(--border-color); + padding: 3px 8px; + color: var(--error-text); +} + +.cn-preview { + min-width: 300px; + max-width: 500px; + min-height: 120px; + overflow: hidden; + font-size: 12px; + pointer-events: none; + padding: 12px; + color: var(--fg-color); +} + +.cn-preview-header { + display: flex; + gap: 8px; + align-items: center; + border-bottom: 1px solid var(--comfy-input-bg); + padding: 5px 10px; +} + +.cn-preview-dot { + width: 8px; + height: 8px; + border-radius: 50%; + background-color: grey; + position: relative; + filter: drop-shadow(1px 2px 3px rgb(0 0 0 / 30%)); +} + +.cn-preview-dot.cn-preview-optional::after { + content: ""; + position: absolute; + pointer-events: none; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + background-color: var(--comfy-input-bg); + border-radius: 50%; + width: 3px; + height: 3px; +} + +.cn-preview-dot.cn-preview-grid { + border-radius: 0; +} + +.cn-preview-dot.cn-preview-grid::before { + content: ''; + position: absolute; + border-left: 1px solid var(--comfy-input-bg); + border-right: 1px solid var(--comfy-input-bg); + width: 4px; + height: 100%; + left: 2px; + top: 0; + z-index: 1; +} + +.cn-preview-dot.cn-preview-grid::after { + content: ''; + position: absolute; + border-top: 1px solid var(--comfy-input-bg); + border-bottom: 1px solid var(--comfy-input-bg); + width: 100%; + height: 4px; + left: 0; + top: 2px; + z-index: 1; +} + +.cn-preview-name { + flex: auto; + font-size: 14px; +} + +.cn-preview-io { + display: flex; + justify-content: space-between; + padding: 10px 10px; +} + +.cn-preview-column > div { + display: flex; + gap: 10px; + align-items: center; + height: 18px; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; +} + +.cn-preview-input { + justify-content: flex-start; +} + +.cn-preview-output { + justify-content: flex-end; +} + +.cn-preview-list { + display: flex; + flex-direction: column; + gap: 3px; + padding: 0 10px 10px 10px; +} + +.cn-preview-switch { + position: relative; + display: flex; + justify-content: space-between; + align-items: center; + background: var(--bg-color); + border: 2px solid var(--border-color); + border-radius: 10px; + text-wrap: nowrap; + padding: 2px 20px; + gap: 10px; +} + +.cn-preview-switch::before, +.cn-preview-switch::after { + position: absolute; + pointer-events: none; + top: 50%; + transform: translate(0, -50%); + color: var(--fg-color); + opacity: 0.8; +} + +.cn-preview-switch::before { + content: "◀"; + left: 5px; +} + +.cn-preview-switch::after { + content: "▶"; + right: 5px; +} + +.cn-preview-value { + color: var(--descrip-text); +} + +.cn-preview-string { + min-height: 30px; + max-height: 300px; + background: var(--bg-color); + color: var(--descrip-text); + border-radius: 3px; + padding: 3px 5px; + overflow-y: auto; + overflow-x: hidden; +} + +.cn-preview-description { + margin: 0px 10px 10px 10px; + padding: 6px; + background: var(--border-color); + color: var(--descrip-text); + border-radius: 5px; + font-style: italic; + word-break: break-word; +} + +.cn-tag-list { + display: flex; + flex-wrap: wrap; + gap: 5px; + align-items: center; + margin-bottom: 5px; +} + +.cn-tag-list > div { + background-color: var(--border-color); + border-radius: 5px; + padding: 0 5px; +} + +.cn-install-buttons { + display: flex; + flex-direction: column; + gap: 3px; + padding: 3px; + align-items: center; + justify-content: center; + height: 100%; +} + +.cn-selected-buttons { + display: flex; + gap: 5px; + align-items: center; + padding-right: 20px; +} + +.cn-manager .cn-btn-enable { + background-color: #333399; + color: white; +} + +.cn-manager .cn-btn-disable { + background-color: #442277; + color: white; +} + +.cn-manager .cn-btn-update { + background-color: #1155AA; + color: white; +} + +.cn-manager .cn-btn-try-update { + background-color: Gray; + color: white; +} + +.cn-manager .cn-btn-try-fix { + background-color: #6495ED; + color: white; +} + +.cn-manager .cn-btn-import-failed { + background-color: #AA1111; + font-size: 10px; + font-weight: bold; + color: white; +} + +.cn-manager .cn-btn-install { + background-color: black; + color: white; +} + +.cn-manager .cn-btn-try-install { + background-color: Gray; + color: white; +} + +.cn-manager .cn-btn-uninstall { + background-color: #993333; + color: white; +} + +.cn-manager .cn-btn-reinstall { + background-color: #993333; + color: white; +} + +.cn-manager .cn-btn-switch { + background-color: #448833; + color: white; + +} + +@keyframes cn-btn-loading-bg { + 0% { + left: 0; + } + 100% { + left: -105px; + } +} + +.cn-manager button.cn-btn-loading { + position: relative; + overflow: hidden; + border-color: rgb(0 119 207 / 80%); + background-color: var(--comfy-input-bg); +} + +.cn-manager button.cn-btn-loading::after { + position: absolute; + top: 0; + left: 0; + content: ""; + width: 500px; + height: 100%; + background-image: repeating-linear-gradient( + -45deg, + rgb(0 119 207 / 30%), + rgb(0 119 207 / 30%) 10px, + transparent 10px, + transparent 15px + ); + animation: cn-btn-loading-bg 2s linear infinite; +} + +.cn-manager-light .cn-pack-name a { + color: blue; +} + +.cn-manager-light .cm-warn-note { + background-color: #ccc !important; +} + +.cn-manager-light .cn-btn-install { + background-color: #333; +} \ No newline at end of file diff --git a/ComfyUI/custom_nodes/ComfyUI-Manager/js/custom-nodes-manager.js b/ComfyUI/custom_nodes/ComfyUI-Manager/js/custom-nodes-manager.js new file mode 100644 index 0000000000000000000000000000000000000000..f2fdc50ce4bf5eb84ff19be0287328e5bb9fb917 --- /dev/null +++ b/ComfyUI/custom_nodes/ComfyUI-Manager/js/custom-nodes-manager.js @@ -0,0 +1,2175 @@ +import { app } from "../../scripts/app.js"; +import { ComfyDialog, $el } from "../../scripts/ui.js"; +import { api } from "../../scripts/api.js"; + +import { + manager_instance, rebootAPI, install_via_git_url, + fetchData, md5, icons, show_message, customConfirm, customAlert, customPrompt, + sanitizeHTML, infoToast, showTerminal, setNeedRestart, + storeColumnWidth, restoreColumnWidth, getTimeAgo, copyText, loadCss, + showPopover, hidePopover +} from "./common.js"; + +// https://cenfun.github.io/turbogrid/api.html +import TG from "./turbogrid.esm.js"; + +loadCss("./custom-nodes-manager.css"); + +const gridId = "node"; + +const pageHtml = ` +
+ + +
+
+
+
+
+
+
+ +`; + +const ShowMode = { + NORMAL: "Normal", + UPDATE: "Update", + MISSING: "Missing", + FAVORITES: "Favorites", + ALTERNATIVES: "Alternatives", + IN_WORKFLOW: "In Workflow", +}; + +export class CustomNodesManager { + static instance = null; + static ShowMode = ShowMode; + + constructor(app, manager_dialog) { + this.app = app; + this.manager_dialog = manager_dialog; + this.id = "cn-manager"; + + app.registerExtension({ + name: "Comfy.CustomNodesManager", + afterConfigureGraph: (missingNodeTypes) => { + const item = this.getFilterItem(ShowMode.MISSING); + if (item) { + item.hasData = false; + item.hashMap = null; + } + } + }); + + this.filter = ''; + this.keywords = ''; + this.restartMap = {}; + + this.init(); + + api.addEventListener("cm-queue-status", this.onQueueStatus); + api.getNodeDefs().then(objs => { + this.nodeMap = objs; + }) + } + + init() { + this.element = $el("div", { + parent: document.body, + className: "comfy-modal cn-manager" + }); + this.element.innerHTML = pageHtml; + this.element.setAttribute("tabindex", 0); + this.element.focus(); + + this.initFilter(); + this.bindEvents(); + this.initGrid(); + } + + showVersionSelectorDialog(versions, onSelect) { + const dialog = new ComfyDialog(); + dialog.element.style.zIndex = 1100; + dialog.element.style.width = "300px"; + dialog.element.style.padding = "0"; + dialog.element.style.backgroundColor = "#2a2a2a"; + dialog.element.style.border = "1px solid #3a3a3a"; + dialog.element.style.borderRadius = "8px"; + dialog.element.style.boxSizing = "border-box"; + dialog.element.style.overflow = "hidden"; + + const contentStyle = { + width: "300px", + display: "flex", + flexDirection: "column", + alignItems: "center", + padding: "20px", + boxSizing: "border-box", + gap: "15px" + }; + + let selectedVersion = versions[0]; + + const versionList = $el("select", { + multiple: true, + size: Math.min(10, versions.length), + style: { + width: "260px", + height: "auto", + backgroundColor: "#383838", + color: "#ffffff", + border: "1px solid #4a4a4a", + borderRadius: "4px", + padding: "5px", + boxSizing: "border-box" + } + }, + versions.map((v, index) => $el("option", { + value: v, + textContent: v, + selected: index === 0 + })) + ); + + versionList.addEventListener('change', (e) => { + selectedVersion = e.target.value; + Array.from(e.target.options).forEach(opt => { + opt.selected = opt.value === selectedVersion; + }); + }); + + const content = $el("div", { + style: contentStyle + }, [ + $el("h3", { + textContent: "Select Version", + style: { + color: "#ffffff", + backgroundColor: "#1a1a1a", + padding: "10px 15px", + margin: "0 0 10px 0", + width: "260px", + textAlign: "center", + borderRadius: "4px", + boxSizing: "border-box", + whiteSpace: "nowrap", + overflow: "hidden", + textOverflow: "ellipsis" + } + }), + versionList, + $el("div", { + style: { + display: "flex", + justifyContent: "space-between", + width: "260px", + gap: "10px" + } + }, [ + $el("button", { + textContent: "Cancel", + onclick: () => dialog.close(), + style: { + flex: "1", + padding: "8px", + backgroundColor: "#4a4a4a", + color: "#ffffff", + border: "none", + borderRadius: "4px", + cursor: "pointer", + whiteSpace: "nowrap", + overflow: "hidden", + textOverflow: "ellipsis" + } + }), + $el("button", { + textContent: "Select", + onclick: () => { + if (selectedVersion) { + onSelect(selectedVersion); + dialog.close(); + } else { + customAlert("Please select a version."); + } + }, + style: { + flex: "1", + padding: "8px", + backgroundColor: "#4CAF50", + color: "#ffffff", + border: "none", + borderRadius: "4px", + cursor: "pointer", + whiteSpace: "nowrap", + overflow: "hidden", + textOverflow: "ellipsis" + } + }), + ]) + ]); + + dialog.show(content); + } + + initFilter() { + const $filter = this.element.querySelector(".cn-manager-filter"); + const filterList = [{ + label: "All", + value: "", + hasData: true + }, { + label: "Installed", + value: "installed", + hasData: true + }, { + label: "Enabled", + value: "enabled", + hasData: true + }, { + label: "Disabled", + value: "disabled", + hasData: true + }, { + label: "Import Failed", + value: "import-fail", + hasData: true + }, { + label: "Not Installed", + value: "not-installed", + hasData: true + }, { + label: "ComfyRegistry", + value: "cnr", + hasData: true + }, { + label: "Non-ComfyRegistry", + value: "unknown", + hasData: true + }, { + label: "Update", + value: ShowMode.UPDATE, + hasData: false + }, { + label: "In Workflow", + value: ShowMode.IN_WORKFLOW, + hasData: false + }, { + label: "Missing", + value: ShowMode.MISSING, + hasData: false + }, { + label: "Favorites", + value: ShowMode.FAVORITES, + hasData: false + }, { + label: "Alternatives of A1111", + value: ShowMode.ALTERNATIVES, + hasData: false + }]; + this.filterList = filterList; + $filter.innerHTML = filterList.map(item => { + return `` + }).join(""); + } + + getFilterItem(filter) { + return this.filterList.find(it => it.value === filter) + } + + getActionButtons(action, rowItem, is_selected_button) { + const buttons = { + "enable": { + label: "Enable", + mode: "enable" + }, + "disable": { + label: "Disable", + mode: "disable" + }, + + "update": { + label: "Update", + mode: "update" + }, + "try-update": { + label: "Try update", + mode: "update" + }, + + "try-fix": { + label: "Try fix", + mode: "fix" + }, + + "reinstall": { + label: "Reinstall", + mode: "reinstall" + }, + + "install": { + label: "Install", + mode: "install" + }, + + "try-install": { + label: "Try install", + mode: "install" + }, + + "uninstall": { + label: "Uninstall", + mode: "uninstall" + }, + + "switch": { + label: "Switch Ver", + mode: "switch" + } + } + + const installGroups = { + "disabled": ["enable", "switch", "uninstall"], + "updatable": ["update", "switch", "disable", "uninstall"], + "import-fail": ["try-fix", "switch", "disable", "uninstall"], + "enabled": ["try-update", "switch", "disable", "uninstall"], + "not-installed": ["install"], + 'unknown': ["try-install"], + "invalid-installation": ["reinstall"], + } + + if (!installGroups.updatable) { + installGroups.enabled = installGroups.enabled.filter(it => it !== "try-update"); + } + + if (rowItem?.title === "ComfyUI-Manager") { + installGroups.enabled = installGroups.enabled.filter(it => it !== "disable" && it !== "uninstall" && it !== "switch"); + } + + let list = installGroups[action]; + + if(is_selected_button || rowItem?.version === "unknown") { + list = list.filter(it => it !== "switch"); + } + + if (!list) { + return ""; + } + + return list.map(id => { + const bt = buttons[id]; + return ``; + }).join(""); + } + + getButton(target) { + if(!target) { + return; + } + const mode = target.getAttribute("mode"); + if (!mode) { + return; + } + const group = target.getAttribute("group"); + if (!group) { + return; + } + return { + group, + mode, + target, + label: target.innerText + } + } + + bindEvents() { + const eventsMap = { + ".cn-manager-filter": { + change: (e) => { + + if (this.grid) { + this.grid.selectAll(false); + } + + const value = e.target.value + this.filter = value; + const item = this.getFilterItem(value); + if (item && (!item.hasData)) { + this.loadData(value); + return; + } + this.updateGrid(); + } + }, + + ".cn-manager-keywords": { + input: (e) => { + const keywords = `${e.target.value}`.trim(); + if (keywords !== this.keywords) { + this.keywords = keywords; + this.updateGrid(); + } + }, + focus: (e) => e.target.select() + }, + + ".cn-manager-selection": { + click: (e) => { + const btn = this.getButton(e.target); + if (btn) { + const nodes = this.selectedMap[btn.group]; + if (nodes) { + this.installNodes(nodes, btn); + } + } + } + }, + + ".cn-manager-back": { + click: (e) => { + this.flyover.hide(true); + this.removeHighlight(); + hidePopover(); + this.close() + manager_instance.show(); + } + }, + + ".cn-manager-restart": { + click: () => { + this.close(); + this.manager_dialog.close(); + rebootAPI(); + } + }, + + ".cn-manager-stop": { + click: () => { + api.fetchApi('/manager/queue/reset'); + infoToast('Cancel', 'Remaining tasks will stop after completing the current task.'); + } + }, + + ".cn-manager-used-in-workflow": { + click: (e) => { + e.target.classList.add("cn-btn-loading"); + this.setFilter(ShowMode.IN_WORKFLOW); + this.loadData(ShowMode.IN_WORKFLOW); + } + }, + + ".cn-manager-check-update": { + click: (e) => { + e.target.classList.add("cn-btn-loading"); + this.setFilter(ShowMode.UPDATE); + this.loadData(ShowMode.UPDATE); + } + }, + + ".cn-manager-check-missing": { + click: (e) => { + e.target.classList.add("cn-btn-loading"); + this.setFilter(ShowMode.MISSING); + this.loadData(ShowMode.MISSING); + } + }, + + ".cn-manager-install-url": { + click: async (e) => { + const url = await customPrompt("Please enter the URL of the Git repository to install", ""); + if (url !== null) { + install_via_git_url(url, this.manager_dialog); + } + } + } + }; + Object.keys(eventsMap).forEach(selector => { + const target = this.element.querySelector(selector); + if (target) { + const events = eventsMap[selector]; + if (events) { + Object.keys(events).forEach(type => { + target.addEventListener(type, events[type]); + }); + } + } + }); + + } + + // =========================================================================================== + + initGrid() { + const container = this.element.querySelector(".cn-manager-grid"); + const grid = new TG.Grid(container); + this.grid = grid; + + this.flyover = this.createFlyover(container); + + let prevViewRowsLength = -1; + grid.bind('onUpdated', (e, d) => { + const viewRows = grid.viewRows; + prevViewRowsLength = viewRows.length; + this.showStatus(`${prevViewRowsLength.toLocaleString()} custom nodes`); + }); + + grid.bind('onSelectChanged', (e, changes) => { + this.renderSelected(); + }); + + grid.bind("onColumnWidthChanged", (e, columnItem) => { + storeColumnWidth(gridId, columnItem) + }); + + grid.bind('onClick', (e, d) => { + + this.addHighlight(d.rowItem); + + if (d.columnItem.id === "nodes") { + this.showNodes(d); + return; + } + + const btn = this.getButton(d.e.target); + if (btn) { + const item = this.grid.getRowItemBy("hash", d.rowItem.hash); + + const { target, label, mode} = btn; + if((mode === "install" || mode === "switch" || mode == "enable") && item.originalData.version != 'unknown') { + // install after select version via dialog if item is cnr node + this.installNodeWithVersion(d.rowItem, btn, mode == 'enable'); + } else { + this.installNodes([d.rowItem.hash], btn, d.rowItem.title); + } + return; + } + + }); + + // iteration events + this.element.addEventListener("click", (e) => { + if (container === e.target || container.contains(e.target)) { + return; + } + this.removeHighlight(); + }); + // proxy keyboard events + this.element.addEventListener("keydown", (e) => { + if (e.target === this.element) { + grid.containerKeyDownHandler(e); + } + }, true); + + + grid.setOption({ + theme: 'dark', + selectVisible: true, + selectMultiple: true, + selectAllVisible: true, + + textSelectable: true, + scrollbarRound: true, + + frozenColumn: 1, + rowNotFound: "No Results", + + rowHeight: 40, + bindWindowResize: true, + bindContainerResize: true, + + cellResizeObserver: (rowItem, columnItem) => { + const autoHeightColumns = ['title', 'action', 'description', "alternatives"]; + return autoHeightColumns.includes(columnItem.id) + }, + + // updateGrid handler for filter and keywords + rowFilter: (rowItem) => { + + const searchableColumns = ["title", "author", "description"]; + if (this.hasAlternatives()) { + searchableColumns.push("alternatives"); + } + + let shouldShown = grid.highlightKeywordsFilter(rowItem, searchableColumns, this.keywords); + + if (shouldShown) { + if(this.filter && rowItem.filterTypes) { + shouldShown = rowItem.filterTypes.includes(this.filter); + } + } + + return shouldShown; + } + }); + + } + + hasAlternatives() { + return this.filter === ShowMode.ALTERNATIVES + } + + async handleImportFail(rowItem) { + var info; + if(rowItem.version == 'unknown'){ + info = { + 'url': rowItem.originalData.files[0] + }; + } + else{ + info = { + 'cnr_id': rowItem.originalData.id + }; + } + + const response = await api.fetchApi(`/customnode/import_fail_info`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(info) + }); + + let res = await response.json(); + + let title = `Error message occurred while importing the '${rowItem.title}' module.


` + + if(res.code == 400) + { + show_message(title+'The information is not available.') + } + else { + show_message(title+sanitizeHTML(res['msg']).replace(/ /g, ' ').replace(/\n/g, '
')); + } + } + + renderGrid() { + + // update theme + const globalStyle = window.getComputedStyle(document.body); + this.colorVars = { + bgColor: globalStyle.getPropertyValue('--comfy-menu-bg'), + borderColor: globalStyle.getPropertyValue('--border-color') + } + + const colorPalette = this.app.ui.settings.settingsValues['Comfy.ColorPalette']; + this.colorPalette = colorPalette; + Array.from(this.element.classList).forEach(cn => { + if (cn.startsWith("cn-manager-")) { + this.element.classList.remove(cn); + } + }); + this.element.classList.add(`cn-manager-${colorPalette}`); + + const options = { + theme: colorPalette === "light" ? "" : "dark" + }; + + + let self = this; + const columns = [{ + id: 'id', + name: 'ID', + width: 50, + align: 'center' + }, { + id: 'title', + name: 'Title', + width: 200, + minWidth: 100, + maxWidth: 500, + classMap: 'cn-pack-name', + formatter: (title, rowItem, columnItem) => { + const container = document.createElement('div'); + + if (rowItem.action === 'invalid-installation') { + const invalidTag = document.createElement('span'); + invalidTag.style.color = 'red'; + invalidTag.innerHTML = '(INVALID)'; + container.appendChild(invalidTag); + } else if (rowItem.action === 'import-fail') { + const button = document.createElement('button'); + button.className = 'cn-btn-import-failed'; + button.innerText = 'IMPORT FAILED ↗'; + button.onclick = () => self.handleImportFail(rowItem); + container.appendChild(button); + container.appendChild(document.createElement('br')); + } + + const link = document.createElement('a'); + if(rowItem.originalData.repository) + link.href = rowItem.originalData.repository; + else + link.href = rowItem.reference; + link.target = '_blank'; + link.innerHTML = `${title}`; + link.title = rowItem.originalData.id; + container.appendChild(link); + + return container; + } + }, { + id: 'version', + name: 'Version', + width: 100, + minWidth: 80, + maxWidth: 300, + classMap: 'cn-pack-version', + formatter: (version, rowItem, columnItem) => { + if(!version) { + return; + } + if(rowItem.cnr_latest && version != rowItem.cnr_latest) { + if(version == 'nightly') { + return `
${version}
[${rowItem.cnr_latest}]
`; + } + return `
${version}
[↑${rowItem.cnr_latest}]
`; + } + return version; + } + }, { + id: 'action', + name: 'Action', + width: 130, + minWidth: 110, + maxWidth: 200, + sortable: false, + align: 'center', + formatter: (action, rowItem, columnItem) => { + if (rowItem.restart) { + return `Restart Required`; + } + const buttons = this.getActionButtons(action, rowItem); + return `
${buttons}
`; + } + }, { + id: "nodes", + name: "Nodes", + width: 100, + formatter: (v, rowItem, columnItem) => { + if (!rowItem.nodes) { + return ''; + } + const list = [`
`]; + list.push(`
${rowItem.nodes} node${(rowItem.nodes>1?'s':'')}
`); + if (rowItem.conflicts) { + list.push(`
${rowItem.conflicts} conflict${(rowItem.conflicts>1?'s':'')}
`); + } + list.push('
'); + return list.join(""); + } + }, { + id: "alternatives", + name: "Alternatives", + width: 400, + maxWidth: 5000, + invisible: !this.hasAlternatives(), + classMap: 'cn-pack-desc' + }, { + id: 'description', + name: 'Description', + width: 400, + maxWidth: 5000, + classMap: 'cn-pack-desc' + }, { + id: 'author', + name: 'Author', + width: 120, + classMap: "cn-pack-author", + formatter: (author, rowItem, columnItem) => { + if (rowItem.trust) { + return `✅ ${author}`; + } + return author; + } + }, { + id: 'stars', + name: '★', + align: 'center', + classMap: "cn-pack-stars", + formatter: (stars) => { + if (stars < 0) { + return 'N/A'; + } + if (typeof stars === 'number') { + return stars.toLocaleString(); + } + return stars; + } + }, { + id: 'last_update', + name: 'Last Update', + align: 'center', + type: 'date', + width: 100, + classMap: "cn-pack-last-update", + formatter: (last_update) => { + if (last_update < 0) { + return 'N/A'; + } + const ago = getTimeAgo(last_update); + const short = `${last_update}`.split(' ')[0]; + return `${short}`; + } + }]; + + restoreColumnWidth(gridId, columns); + + const rows_values = Object.values(this.custom_nodes); + rows_values.sort((a, b) => { + if (a.version == 'unknown' && b.version != 'unknown') return 1; + if (a.version != 'unknown' && b.version == 'unknown') return -1; + + if (a.stars !== b.stars) { + return b.stars - a.stars; + } + + if (a.last_update !== b.last_update) { + return new Date(b.last_update) - new Date(a.last_update); + } + + return 0; + }); + + rows_values.forEach((it, i) => { + it.id = i + 1; + }); + + this.grid.setData({ + options: options, + rows: rows_values, + columns: columns + }); + + this.grid.render(); + } + + updateGrid() { + if (this.grid) { + this.grid.update(); + if (this.hasAlternatives()) { + this.grid.showColumn("alternatives"); + } else { + this.grid.hideColumn("alternatives"); + } + } + } + + addHighlight(rowItem) { + this.removeHighlight(); + if (this.grid && rowItem) { + this.grid.setRowState(rowItem, 'highlight', true); + this.highlightRow = rowItem; + } + } + + removeHighlight() { + if (this.grid && this.highlightRow) { + this.grid.setRowState(this.highlightRow, 'highlight', false); + this.highlightRow = null; + } + } + + // =========================================================================================== + + getWidgetType(type, inputName) { + if (type === 'COMBO') { + return 'COMBO' + } + const widgets = app.widgets; + if (`${type}:${inputName}` in widgets) { + return `${type}:${inputName}` + } + if (type in widgets) { + return type + } + } + + createNodePreview(nodeItem) { + // console.log(nodeItem); + const list = [`
+
+
${nodeItem.name}
+
Preview
+
`]; + + // Node slot I/O + const inputList = []; + nodeItem.input_order.required?.map(name => { + inputList.push({ + name + }); + }) + nodeItem.input_order.optional?.map(name => { + inputList.push({ + name, + optional: true + }); + }); + + const slotInputList = []; + const widgetInputList = []; + const inputMap = Object.assign({}, nodeItem.input.optional, nodeItem.input.required); + inputList.forEach(it => { + const inputName = it.name; + const _inputData = inputMap[inputName]; + let type = _inputData[0]; + let options = _inputData[1] || {}; + if (Array.isArray(type)) { + options.default = type[0]; + type = 'COMBO'; + } + it.type = type; + it.options = options; + + // convert force/default inputs + if (options.forceInput || options.defaultInput) { + slotInputList.push(it); + return; + } + + const widgetType = this.getWidgetType(type, inputName); + if (widgetType) { + it.default = options.default; + widgetInputList.push(it); + } else { + slotInputList.push(it); + } + }); + + const outputList = nodeItem.output.map((type, i) => { + return { + type, + name: nodeItem.output_name[i], + list: nodeItem.output_is_list[i] + } + }); + + // dark + const colorMap = { + "CLIP": "#FFD500", + "CLIP_VISION": "#A8DADC", + "CLIP_VISION_OUTPUT": "#ad7452", + "CONDITIONING": "#FFA931", + "CONTROL_NET": "#6EE7B7", + "IMAGE": "#64B5F6", + "LATENT": "#FF9CF9", + "MASK": "#81C784", + "MODEL": "#B39DDB", + "STYLE_MODEL": "#C2FFAE", + "VAE": "#FF6E6E", + "NOISE": "#B0B0B0", + "GUIDER": "#66FFFF", + "SAMPLER": "#ECB4B4", + "SIGMAS": "#CDFFCD", + "TAESD": "#DCC274" + } + + const inputHtml = slotInputList.map(it => { + const color = colorMap[it.type] || "gray"; + const optional = it.optional ? " cn-preview-optional" : "" + return `
+
+ ${it.name} +
`; + }).join(""); + + const outputHtml = outputList.map(it => { + const color = colorMap[it.type] || "gray"; + const grid = it.list ? " cn-preview-grid" : ""; + return `
+ ${it.name} +
+
`; + }).join(""); + + list.push(`
+
${inputHtml}
+
${outputHtml}
+
`); + + // Node widget inputs + if (widgetInputList.length) { + list.push(`
`); + + // console.log(widgetInputList); + widgetInputList.forEach(it => { + + let value = it.default; + if (typeof value === "object" && value && Object.prototype.hasOwnProperty.call(value, "content")) { + value = value.content; + } + if (typeof value === "undefined" || value === null) { + value = ""; + } else { + value = `${value}`; + } + + if ( + (it.type === "STRING" && (value || it.options.multiline)) + || it.type === "MARKDOWN" + ) { + if (value) { + value = value.replace(/\r?\n/g, "
") + } + list.push(`
${value || it.name}
`); + return; + } + + list.push(`
+
${it.name}
+
${value}
+
`); + }); + list.push(`
`); + } + + if (nodeItem.description) { + list.push(`
${nodeItem.description}
`) + } + + return list.join(""); + } + + showNodePreview(target) { + const nodeName = target.innerText; + const nodeItem = this.nodeMap[nodeName]; + if (!nodeItem) { + this.hideNodePreview(); + return; + } + const html = this.createNodePreview(nodeItem); + showPopover(target, html, "cn-preview cn-preview-"+this.colorPalette, { + positions: ['left'], + bgColor: this.colorVars.bgColor, + borderColor: this.colorVars.borderColor + }) + } + + hideNodePreview() { + hidePopover(); + } + + createFlyover(container) { + const $flyover = document.createElement("div"); + $flyover.className = "cn-flyover"; + $flyover.innerHTML = `
+
${icons.arrowRight}
+
+
${icons.close}
+
+
` + container.appendChild($flyover); + + const $flyoverTitle = $flyover.querySelector(".cn-flyover-title"); + const $flyoverBody = $flyover.querySelector(".cn-flyover-body"); + + let width = '50%'; + let visible = false; + + let timeHide; + const closeHandler = (e) => { + if ($flyover === e.target || $flyover.contains(e.target)) { + return; + } + clearTimeout(timeHide); + timeHide = setTimeout(() => { + flyover.hide(); + }, 100); + } + + const hoverHandler = (e) => { + if(e.type === "mouseenter") { + if(e.target.classList.contains("cn-nodes-name")) { + this.showNodePreview(e.target); + } + return; + } + this.hideNodePreview(); + } + + const displayHandler = () => { + if (visible) { + $flyover.classList.remove("cn-slide-in-right"); + } else { + $flyover.classList.remove("cn-slide-out-right"); + $flyover.style.width = '0px'; + $flyover.style.display = "none"; + } + } + + const flyover = { + show: (titleHtml, bodyHtml) => { + clearTimeout(timeHide); + this.element.removeEventListener("click", closeHandler); + $flyoverTitle.innerHTML = titleHtml; + $flyoverBody.innerHTML = bodyHtml; + $flyover.style.display = "block"; + $flyover.style.width = width; + if(!visible) { + $flyover.classList.add("cn-slide-in-right"); + } + visible = true; + setTimeout(() => { + this.element.addEventListener("click", closeHandler); + }, 100); + }, + hide: (now) => { + visible = false; + this.element.removeEventListener("click", closeHandler); + if(now) { + displayHandler(); + return; + } + $flyover.classList.add("cn-slide-out-right"); + } + } + + $flyover.addEventListener("animationend", (e) => { + displayHandler(); + }); + + $flyover.addEventListener("mouseenter", hoverHandler, true); + $flyover.addEventListener("mouseleave", hoverHandler, true); + + $flyover.addEventListener("click", (e) => { + + if(e.target.classList.contains("cn-nodes-name")) { + const nodeName = e.target.innerText; + const nodeItem = this.nodeMap[nodeName]; + if (!nodeItem) { + copyText(nodeName).then((res) => { + if (res) { + e.target.setAttribute("action", "Copied"); + e.target.classList.add("action"); + setTimeout(() => { + e.target.classList.remove("action"); + e.target.removeAttribute("action"); + }, 1000); + } + }); + return; + } + + const [x, y, w, h] = app.canvas.ds.visible_area; + const dpi = Math.max(window.devicePixelRatio ?? 1, 1); + const node = window.LiteGraph?.createNode( + nodeItem.name, + nodeItem.display_name, + { + pos: [x + (w-300) / dpi / 2, y] + } + ); + if (node) { + app.graph.add(node); + e.target.setAttribute("action", "Added to Workflow"); + e.target.classList.add("action"); + setTimeout(() => { + e.target.classList.remove("action"); + e.target.removeAttribute("action"); + }, 1000); + } + + return; + } + if(e.target.classList.contains("cn-nodes-pack")) { + const hash = e.target.getAttribute("hash"); + const rowItem = this.grid.getRowItemBy("hash", hash); + //console.log(rowItem); + this.grid.scrollToRow(rowItem); + this.addHighlight(rowItem); + return; + } + if(e.target.classList.contains("cn-flyover-close")) { + flyover.hide(); + return; + } + }); + + return flyover; + } + + showNodes(d) { + const nodesList = d.rowItem.nodesList; + if (!nodesList) { + return; + } + + const rowItem = d.rowItem; + const isNotInstalled = rowItem.action == "not-installed"; + + let titleHtml = `
${rowItem.title}
`; + if (isNotInstalled) { + titleHtml += '
Not Installed
' + } + + const list = []; + list.push(`
`); + + nodesList.forEach((it, i) => { + let rowClass = 'cn-nodes-row' + if (it.conflicts) { + rowClass += ' cn-nodes-conflict'; + } + + list.push(`
`); + list.push(`
${i+1}
`); + list.push(`
${it.name}
`); + + if (it.conflicts) { + list.push(`
${icons.conflicts}
Conflict with${it.conflicts.map(c => { + return `
${c.title}
`; + }).join(",")}
`); + } + list.push(`
`); + }); + + list.push("
"); + const bodyHtml = list.join(""); + + this.flyover.show(titleHtml, bodyHtml); + } + + async loadNodes(node_packs) { + const mode = manager_instance.datasrc_combo.value; + this.showStatus(`Loading node mappings (${mode}) ...`); + const res = await fetchData(`/customnode/getmappings?mode=${mode}`); + if (res.error) { + console.log(res.error); + return; + } + + const data = res.data; + + const findNode = (k, title) => { + let item = node_packs[k]; + if (item) { + return item; + } + + // git url + if (k.includes("/")) { + const gitName = k.split("/").pop(); + item = node_packs[gitName]; + if (item) { + return item; + } + } + + return node_packs[title]; + } + + const conflictsMap = {}; + + // add nodes data + Object.keys(data).forEach(k => { + const [nodes, metadata] = data[k]; + if (nodes?.length) { + const title = metadata?.title_aux; + const nodeItem = findNode(k, title); + if (nodeItem) { + + // deduped + const eList = Array.from(new Set(nodes)); + + nodeItem.nodes = eList.length; + const nodesMap = {}; + eList.forEach(extName => { + nodesMap[extName] = { + name: extName + }; + let cList = conflictsMap[extName]; + if(!cList) { + cList = []; + conflictsMap[extName] = cList; + } + cList.push(nodeItem.key); + }); + nodeItem.nodesMap = nodesMap; + } else { + // should be removed + // console.log("not found", k, title, nodes) + } + } + }); + + // calculate conflicts data + Object.keys(conflictsMap).forEach(extName => { + const cList = conflictsMap[extName]; + if(cList.length <= 1) { + return; + } + cList.forEach(key => { + const nodeItem = node_packs[key]; + const extItem = nodeItem.nodesMap[extName]; + if(!extItem.conflicts) { + extItem.conflicts = [] + } + const conflictsList = cList.filter(k => k !== key); + conflictsList.forEach(k => { + const nItem = node_packs[k]; + extItem.conflicts.push({ + key: k, + title: nItem.title, + hash: nItem.hash + }) + + }) + }) + }) + + Object.values(node_packs).forEach(nodeItem => { + if (nodeItem.nodesMap) { + nodeItem.nodesList = Object.values(nodeItem.nodesMap); + nodeItem.conflicts = nodeItem.nodesList.filter(it => it.conflicts).length; + } + }) + + } + + // =========================================================================================== + + renderSelected() { + const selectedList = this.grid.getSelectedRows(); + if (!selectedList.length) { + this.showSelection(""); + return; + } + + const selectedMap = {}; + selectedList.forEach(item => { + let type = item.action; + if (item.restart) { + type = "Restart Required"; + } + if (selectedMap[type]) { + selectedMap[type].push(item.hash); + } else { + selectedMap[type] = [item.hash]; + } + }); + + this.selectedMap = selectedMap; + + const list = []; + Object.keys(selectedMap).forEach(v => { + const filterItem = this.getFilterItem(v); + list.push(`
+ Selected ${selectedMap[v].length} ${filterItem ? filterItem.label : v} + ${this.grid.hasMask ? "" : this.getActionButtons(v, null, true)} +
`); + }); + + this.showSelection(list.join("")); + } + + focusInstall(item, mode) { + const cellNode = this.grid.getCellNode(item, "action"); + if (cellNode) { + const cellBtn = cellNode.querySelector(`button[mode="${mode}"]`); + if (cellBtn) { + cellBtn.classList.add("cn-btn-loading"); + return true + } + } + } + + async installNodeWithVersion(rowItem, btn, is_enable) { + let hash = rowItem.hash; + let title = rowItem.title; + + const item = this.grid.getRowItemBy("hash", hash); + + let node_id = item.originalData.id; + + this.showLoading(); + let res; + if(is_enable) { + res = await api.fetchApi(`/customnode/disabled_versions/${node_id}`, { cache: "no-store" }); + } + else { + res = await api.fetchApi(`/customnode/versions/${node_id}`, { cache: "no-store" }); + } + this.hideLoading(); + + if(res.status == 200) { + let obj = await res.json(); + + let versions = []; + let default_version; + let version_cnt = 0; + + if(!is_enable) { + + if(rowItem.cnr_latest != rowItem.originalData.active_version && obj.length > 0) { + versions.push('latest'); + } + + if(rowItem.originalData.active_version != 'nightly') { + versions.push('nightly'); + default_version = 'nightly'; + version_cnt++; + } + } + + for(let v of obj) { + if(rowItem.originalData.active_version != v.version) { + default_version = v.version; + versions.push(v.version); + version_cnt++; + } + } + + this.showVersionSelectorDialog(versions, (selected_version) => { + this.installNodes([hash], btn, title, selected_version); + }); + } + else { + show_message('Failed to fetch versions from ComfyRegistry.'); + } + } + + async installNodes(list, btn, title, selected_version) { + let stats = await api.fetchApi('/manager/queue/status'); + stats = await stats.json(); + if(stats.is_processing) { + customAlert(`[ComfyUI-Manager] There are already tasks in progress. Please try again after it is completed. (${stats.done_count}/${stats.total_count})`); + return; + } + + const { target, label, mode} = btn; + + if(mode === "uninstall") { + title = title || `${list.length} custom nodes`; + + const confirmed = await customConfirm(`Are you sure uninstall ${title}?`); + if (!confirmed) { + return; + } + } + + if(mode === "reinstall") { + title = title || `${list.length} custom nodes`; + + const confirmed = await customConfirm(`Are you sure reinstall ${title}?`); + if (!confirmed) { + return; + } + } + + target.classList.add("cn-btn-loading"); + this.showError(""); + + let needRestart = false; + let errorMsg = ""; + + await api.fetchApi('/manager/queue/reset'); + + let target_items = []; + + for (const hash of list) { + const item = this.grid.getRowItemBy("hash", hash); + target_items.push(item); + + if (!item) { + errorMsg = `Not found custom node: ${hash}`; + break; + } + + this.grid.scrollRowIntoView(item); + + if (!this.focusInstall(item, mode)) { + this.grid.onNextUpdated(() => { + this.focusInstall(item, mode); + }); + } + + this.showStatus(`${label} ${item.title} ...`); + + const data = item.originalData; + data.selected_version = selected_version; + data.channel = this.channel; + data.mode = this.mode; + data.ui_id = hash; + + let install_mode = mode; + if(mode == 'switch') { + install_mode = 'install'; + } + + // don't post install if install_mode == 'enable' + data.skip_post_install = install_mode == 'enable'; + let api_mode = install_mode; + if(install_mode == 'enable') { + api_mode = 'install'; + } + + if(install_mode == 'reinstall') { + api_mode = 'reinstall'; + } + + const res = await api.fetchApi(`/manager/queue/${api_mode}`, { + method: 'POST', + body: JSON.stringify(data) + }); + + if (res.status != 200) { + errorMsg = `'${item.title}': `; + + if(res.status == 403) { + errorMsg += `This action is not allowed with this security level configuration.\n`; + } else if(res.status == 404) { + errorMsg += `With the current security level configuration, only custom nodes from the "default channel" can be installed.\n`; + } else { + errorMsg += await res.text() + '\n'; + } + + break; + } + } + + this.install_context = {btn: btn, targets: target_items}; + + if(errorMsg) { + this.showError(errorMsg); + show_message("[Installation Errors]\n"+errorMsg); + + // reset + for(let k in target_items) { + const item = target_items[k]; + this.grid.updateCell(item, "action"); + } + } + else { + await api.fetchApi('/manager/queue/start'); + this.showStop(); + showTerminal(); + } + } + + async onQueueStatus(event) { + let self = CustomNodesManager.instance; + if(event.detail.status == 'in_progress' && event.detail.ui_target == 'nodepack_manager') { + const hash = event.detail.target; + + const item = self.grid.getRowItemBy("hash", hash); + + item.restart = true; + self.restartMap[item.hash] = true; + self.grid.updateCell(item, "action"); + self.grid.setRowSelected(item, false); + } + else if(event.detail.status == 'done') { + self.hideStop(); + self.onQueueCompleted(event.detail); + } + } + + async onQueueCompleted(info) { + let result = info.nodepack_result; + + if(result.length == 0) { + return; + } + + let self = CustomNodesManager.instance; + + if(!self.install_context) { + return; + } + + const { target, label, mode } = self.install_context.btn; + target.classList.remove("cn-btn-loading"); + + let errorMsg = ""; + + for(let hash in result){ + let v = result[hash]; + + if(v != 'success' && v != 'skip') + errorMsg += v+'\n'; + } + + for(let k in self.install_context.targets) { + let item = self.install_context.targets[k]; + self.grid.updateCell(item, "action"); + } + + if (errorMsg) { + self.showError(errorMsg); + show_message("Installation Error:\n"+errorMsg); + } else { + self.showStatus(`${label} ${result.length} custom node(s) successfully`); + } + + self.showRestart(); + self.showMessage(`To apply the installed/updated/disabled/enabled custom node, please restart ComfyUI. And refresh browser.`, "red"); + + infoToast(`[ComfyUI-Manager] All node pack tasks in the queue have been completed.\n${info.done_count}/${info.total_count}`); + self.install_context = undefined; + } + + // =========================================================================================== + + getNodesInWorkflow() { + let usedGroupNodes = new Set(); + let allUsedNodes = {}; + + for(let k in app.graph._nodes) { + let node = app.graph._nodes[k]; + + if(node.type.startsWith('workflow>')) { + usedGroupNodes.add(node.type.slice(9)); + continue; + } + + allUsedNodes[node.type] = node; + } + + for(let k of usedGroupNodes) { + let subnodes = app.graph.extra.groupNodes[k]?.nodes; + + if(subnodes) { + for(let k2 in subnodes) { + let node = subnodes[k2]; + allUsedNodes[node.type] = node; + } + } + } + + return allUsedNodes; + } + + async getMissingNodes() { + let unresolved_missing_nodes = new Set(); + let hashMap = {}; + let allUsedNodes = this.getNodesInWorkflow(); + + const registered_nodes = new Set(); + for (let i in LiteGraph.registered_node_types) { + registered_nodes.add(LiteGraph.registered_node_types[i].type); + } + + let unresolved_aux_ids = {}; + let outdated_comfyui = false; + let unresolved_cnr_list = []; + + for(let k in allUsedNodes) { + let node = allUsedNodes[k]; + + if(!registered_nodes.has(node.type)) { + // missing node + if(node.properties.cnr_id) { + if(node.properties.cnr_id == 'comfy-core') { + outdated_comfyui = true; + } + + let item = this.custom_nodes[node.properties.cnr_id]; + if(item) { + hashMap[item.hash] = true; + } + else { + console.log(`CM: cannot find '${node.properties.cnr_id}' from cnr list.`); + unresolved_aux_ids[node.properties.cnr_id] = node.type; + unresolved_cnr_list.push(node.properties.cnr_id); + } + } + else if(node.properties.aux_id) { + unresolved_aux_ids[node.properties.aux_id] = node.type; + } + else { + unresolved_missing_nodes.add(node.type); + } + } + } + + + if(unresolved_cnr_list.length > 0) { + let error_msg = "Failed to find the following ComfyRegistry list.\nThe cache may be outdated, or the nodes may have been removed from ComfyRegistry.
"; + for(let i in unresolved_cnr_list) { + error_msg += '
  • '+unresolved_cnr_list[i]+'
  • '; + } + + show_message(error_msg); + } + + if(outdated_comfyui) { + customAlert('ComfyUI is outdated, so some built-in nodes cannot be used.'); + } + + if(Object.keys(unresolved_aux_ids).length > 0) { + // building aux_id to nodepack map + let aux_id_to_pack = {}; + for(let k in this.custom_nodes) { + let nodepack = this.custom_nodes[k]; + let aux_id; + if(nodepack.repository?.startsWith('https://github.com')) { + aux_id = nodepack.repository.split('/').slice(-2).join('/'); + aux_id_to_pack[aux_id] = nodepack; + } + else if(nodepack.repository) { + aux_id = nodepack.repository.split('/').slice(-1); + aux_id_to_pack[aux_id] = nodepack; + } + } + + // resolving aux_id + for(let k in unresolved_aux_ids) { + let nodepack = aux_id_to_pack[k]; + if(nodepack) { + hashMap[nodepack.hash] = true; + } + else { + unresolved_missing_nodes.add(unresolved_aux_ids[k]); + } + } + } + + if(unresolved_missing_nodes.size > 0) { + await this.getMissingNodesLegacy(hashMap, unresolved_missing_nodes); + } + + return hashMap; + } + + async getMissingNodesLegacy(hashMap, missing_nodes) { + const mode = manager_instance.datasrc_combo.value; + this.showStatus(`Loading missing nodes (${mode}) ...`); + const res = await fetchData(`/customnode/getmappings?mode=${mode}`); + if (res.error) { + this.showError(`Failed to get custom node mappings: ${res.error}`); + return; + } + + const mappings = res.data; + + // build regex->url map + const regex_to_pack = []; + for(let k in this.custom_nodes) { + let node = this.custom_nodes[k]; + + if(node.nodename_pattern) { + regex_to_pack.push({ + regex: new RegExp(node.nodename_pattern), + url: node.files[0] + }); + } + } + + // build name->url map + const name_to_packs = {}; + for (const url in mappings) { + const names = mappings[url]; + + for(const name in names[0]) { + let v = name_to_packs[names[0][name]]; + if(v == undefined) { + v = []; + name_to_packs[names[0][name]] = v; + } + v.push(url); + } + } + + let unresolved_missing_nodes = new Set(); + for (let node_type of missing_nodes) { + const packs = name_to_packs[node_type.trim()]; + if(packs) + packs.forEach(url => { + unresolved_missing_nodes.add(url); + }); + else { + for(let j in regex_to_pack) { + if(regex_to_pack[j].regex.test(node_type)) { + unresolved_missing_nodes.add(regex_to_pack[j].url); + } + } + } + } + + for(let k in this.custom_nodes) { + let item = this.custom_nodes[k]; + + if(unresolved_missing_nodes.has(item.id)) { + hashMap[item.hash] = true; + } + else if (item.files?.some(file => unresolved_missing_nodes.has(file))) { + hashMap[item.hash] = true; + } + } + + return hashMap; + } + + async getFavorites() { + const hashMap = {}; + for(let k in this.custom_nodes) { + let item = this.custom_nodes[k]; + if(item.is_favorite) + hashMap[item.hash] = true; + } + + return hashMap; + } + + async getNodepackInWorkflow() { + let allUsedNodes = this.getNodesInWorkflow(); + + // building aux_id to nodepack map + let aux_id_to_pack = {}; + for(let k in this.custom_nodes) { + let nodepack = this.custom_nodes[k]; + let aux_id; + if(nodepack.repository?.startsWith('https://github.com')) { + aux_id = nodepack.repository.split('/').slice(-2).join('/'); + aux_id_to_pack[aux_id] = nodepack; + } + else if(nodepack.repository) { + aux_id = nodepack.repository.split('/').slice(-1); + aux_id_to_pack[aux_id] = nodepack; + } + } + + const hashMap = {}; + for(let k in allUsedNodes) { + var item; + if(allUsedNodes[k].properties.cnr_id) { + item = this.custom_nodes[allUsedNodes[k].properties.cnr_id]; + } + else if(allUsedNodes[k].properties.aux_id) { + item = aux_id_to_pack[allUsedNodes[k].properties.aux_id]; + } + + if(item) + hashMap[item.hash] = true; + } + + return hashMap; + } + + async getAlternatives() { + const mode = manager_instance.datasrc_combo.value; + this.showStatus(`Loading alternatives (${mode}) ...`); + const res = await fetchData(`/customnode/alternatives?mode=${mode}`); + if (res.error) { + this.showError(`Failed to get alternatives: ${res.error}`); + return []; + } + + const hashMap = {}; + const items = res.data; + + for(let i in items) { + let item = items[i]; + let custom_node = this.custom_nodes[i]; + + if (!custom_node) { + console.log(`Not found custom node: ${item.id}`); + continue; + } + + const tags = `${item.tags}`.split(",").map(tag => { + return `
    ${tag.trim()}
    `; + }).join(""); + + hashMap[custom_node.hash] = { + alternatives: `
    ${tags}
    ${item.description}` + } + + } + + return hashMap; + } + + async loadData(show_mode = ShowMode.NORMAL) { + const isElectron = 'electronAPI' in window; + + this.show_mode = show_mode; + console.log("Show mode:", show_mode); + + this.showLoading(); + + const mode = manager_instance.datasrc_combo.value; + this.showStatus(`Loading custom nodes (${mode}) ...`); + + const skip_update = this.show_mode === ShowMode.UPDATE ? "" : "&skip_update=true"; + + if(this.show_mode === ShowMode.UPDATE) { + infoToast('Fetching updated information. This may take some time if many custom nodes are installed.'); + } + + const res = await fetchData(`/customnode/getlist?mode=${mode}${skip_update}`); + if (res.error) { + this.showError("Failed to get custom node list."); + this.hideLoading(); + return; + } + + const { channel, node_packs } = res.data; + + if(isElectron) { + delete node_packs['comfyui-manager']; + } + + this.channel = channel; + this.mode = mode; + this.custom_nodes = node_packs; + + if(this.channel !== 'default') { + this.element.querySelector(".cn-manager-channel").innerHTML = `Channel: ${this.channel} (Incomplete list)`; + } + + for (const k in node_packs) { + let item = node_packs[k]; + item.originalData = JSON.parse(JSON.stringify(item)); + if(item.originalData.id == undefined) { + item.originalData.id = k; + } + item.key = k; + item.hash = md5(k); + } + + await this.loadNodes(node_packs); + + const filterItem = this.getFilterItem(this.show_mode); + if(filterItem) { + let hashMap; + if(this.show_mode == ShowMode.UPDATE) { + hashMap = {}; + for (const k in node_packs) { + let it = node_packs[k]; + if (it['update-state'] === "true") { + hashMap[it.hash] = true; + } + } + } else if(this.show_mode == ShowMode.MISSING) { + hashMap = await this.getMissingNodes(); + } else if(this.show_mode == ShowMode.ALTERNATIVES) { + hashMap = await this.getAlternatives(); + } else if(this.show_mode == ShowMode.FAVORITES) { + hashMap = await this.getFavorites(); + } else if(this.show_mode == ShowMode.IN_WORKFLOW) { + hashMap = await this.getNodepackInWorkflow(); + } + filterItem.hashMap = hashMap; + + if(this.show_mode != ShowMode.IN_WORKFLOW) { + filterItem.hasData = true; + } + } + + for(let k in node_packs) { + let nodeItem = node_packs[k]; + + if (this.restartMap[nodeItem.hash]) { + nodeItem.restart = true; + } + + if(nodeItem['update-state'] == "true") { + nodeItem.action = 'updatable'; + } + else if(nodeItem['import-fail']) { + nodeItem.action = 'import-fail'; + } + else { + nodeItem.action = nodeItem.state; + } + + if(nodeItem['invalid-installation']) { + nodeItem.action = 'invalid-installation'; + } + + const filterTypes = new Set(); + this.filterList.forEach(filterItem => { + const { value, hashMap } = filterItem; + if (hashMap) { + const hashData = hashMap[nodeItem.hash] + if (hashData) { + filterTypes.add(value); + if (value === ShowMode.UPDATE) { + nodeItem['update-state'] = "true"; + } + if (value === ShowMode.MISSING) { + nodeItem['missing-node'] = "true"; + } + if (typeof hashData === "object") { + Object.assign(nodeItem, hashData); + } + } + } else { + if (nodeItem.state === value) { + filterTypes.add(value); + } + + switch(nodeItem.state) { + case "enabled": + filterTypes.add("enabled"); + case "disabled": + filterTypes.add("installed"); + break; + case "not-installed": + filterTypes.add("not-installed"); + break; + } + + if(nodeItem.version != 'unknown') { + filterTypes.add("cnr"); + } + else { + filterTypes.add("unknown"); + } + + if(nodeItem['update-state'] == 'true') { + filterTypes.add("updatable"); + } + + if(nodeItem['import-fail']) { + filterTypes.add("import-fail"); + } + + if(nodeItem['invalid-installation']) { + filterTypes.add("invalid-installation"); + } + } + }); + + nodeItem.filterTypes = Array.from(filterTypes); + } + + this.renderGrid(); + + this.hideLoading(); + + } + + // =========================================================================================== + + showSelection(msg) { + this.element.querySelector(".cn-manager-selection").innerHTML = msg; + } + + showError(err) { + this.showMessage(err, "red"); + } + + showMessage(msg, color) { + if (color) { + msg = `${msg}`; + } + this.element.querySelector(".cn-manager-message").innerHTML = msg; + } + + showStatus(msg, color) { + if (color) { + msg = `${msg}`; + } + this.element.querySelector(".cn-manager-status").innerHTML = msg; + } + + showLoading() { + this.setDisabled(true); + if (this.grid) { + this.grid.showLoading(); + this.grid.showMask({ + opacity: 0.05 + }); + } + } + + hideLoading() { + this.setDisabled(false); + if (this.grid) { + this.grid.hideLoading(); + this.grid.hideMask(); + } + } + + setDisabled(disabled) { + const $close = this.element.querySelector(".cn-manager-close"); + const $restart = this.element.querySelector(".cn-manager-restart"); + const $stop = this.element.querySelector(".cn-manager-stop"); + + const list = [ + ".cn-manager-header input", + ".cn-manager-header select", + ".cn-manager-footer button", + ".cn-manager-selection button" + ].map(s => { + return Array.from(this.element.querySelectorAll(s)); + }) + .flat() + .filter(it => { + return it !== $close && it !== $restart && it !== $stop; + }); + + list.forEach($elem => { + if (disabled) { + $elem.setAttribute("disabled", "disabled"); + } else { + $elem.removeAttribute("disabled"); + } + }); + + Array.from(this.element.querySelectorAll(".cn-btn-loading")).forEach($elem => { + $elem.classList.remove("cn-btn-loading"); + }); + + } + + showRestart() { + this.element.querySelector(".cn-manager-restart").style.display = "block"; + setNeedRestart(true); + } + + showStop() { + this.element.querySelector(".cn-manager-stop").style.display = "block"; + } + + hideStop() { + this.element.querySelector(".cn-manager-stop").style.display = "none"; + } + + setFilter(filterValue) { + let filter = ""; + const filterItem = this.getFilterItem(filterValue); + if(filterItem) { + filter = filterItem.value; + } + this.filter = filter; + this.element.querySelector(".cn-manager-filter").value = filter; + } + + setKeywords(keywords = "") { + this.keywords = keywords; + this.element.querySelector(".cn-manager-keywords").value = keywords; + } + + show(show_mode) { + this.element.style.display = "flex"; + this.element.focus(); + this.setFilter(show_mode); + this.setKeywords(""); + this.showSelection(""); + this.showMessage(""); + this.loadData(show_mode); + } + + close() { + this.element.style.display = "none"; + } + + get isVisible() { + return this.element?.style?.display !== "none"; + } +} \ No newline at end of file diff --git a/ComfyUI/custom_nodes/ComfyUI-Manager/js/model-manager.css b/ComfyUI/custom_nodes/ComfyUI-Manager/js/model-manager.css new file mode 100644 index 0000000000000000000000000000000000000000..3a34cb2db82d958da7cbbf82542fd953099b9b49 --- /dev/null +++ b/ComfyUI/custom_nodes/ComfyUI-Manager/js/model-manager.css @@ -0,0 +1,213 @@ +.cmm-manager { + --grid-font: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; + z-index: 1099; + width: 80%; + height: 80%; + display: flex; + flex-direction: column; + gap: 10px; + color: var(--fg-color); + font-family: arial, sans-serif; +} + +.cmm-manager .cmm-flex-auto { + flex: auto; +} + +.cmm-manager button { + font-size: 16px; + color: var(--input-text); + background-color: var(--comfy-input-bg); + border-radius: 8px; + border-color: var(--border-color); + border-style: solid; + margin: 0; + padding: 4px 8px; + min-width: 100px; +} + +.cmm-manager button:disabled, +.cmm-manager input:disabled, +.cmm-manager select:disabled { + color: gray; +} + +.cmm-manager button:disabled { + background-color: var(--comfy-input-bg); +} + +.cmm-manager .cmm-manager-refresh { + display: none; + background-color: #000080; + color: white; +} + +.cmm-manager .cmm-manager-stop { + display: none; + background-color: #500000; + color: white; +} + +.cmm-manager-header { + display: flex; + flex-wrap: wrap; + gap: 5px; + align-items: center; + padding: 0 5px; +} + +.cmm-manager-header label { + display: flex; + gap: 5px; + align-items: center; +} + +.cmm-manager-type, +.cmm-manager-base, +.cmm-manager-filter { + height: 28px; + line-height: 28px; +} + +.cmm-manager-keywords { + height: 28px; + line-height: 28px; + padding: 0 5px 0 26px; + background-size: 16px; + background-position: 5px center; + background-repeat: no-repeat; + background-image: url("data:image/svg+xml;charset=utf8,%3Csvg%20viewBox%3D%220%200%2024%2024%22%20width%3D%22100%25%22%20height%3D%22100%25%22%20pointer-events%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20fill%3D%22none%22%20stroke%3D%22%23888%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20stroke-width%3D%222%22%20d%3D%22m21%2021-4.486-4.494M19%2010.5a8.5%208.5%200%201%201-17%200%208.5%208.5%200%200%201%2017%200%22%2F%3E%3C%2Fsvg%3E"); +} + +.cmm-manager-status { + padding-left: 10px; +} + +.cmm-manager-grid { + flex: auto; + border: 1px solid var(--border-color); + overflow: hidden; +} + +.cmm-manager-selection { + display: flex; + flex-wrap: wrap; + gap: 10px; + align-items: center; +} + +.cmm-manager-footer { + display: flex; + flex-wrap: wrap; + gap: 10px; + align-items: center; +} + +.cmm-manager-grid .tg-turbogrid { + font-family: var(--grid-font); + font-size: 15px; + background: var(--bg-color); +} + +.cmm-manager-grid .cmm-node-name a { + color: skyblue; + text-decoration: none; + word-break: break-word; +} + +.cmm-manager-grid .cmm-node-desc a { + color: #5555FF; + font-weight: bold; + text-decoration: none; +} + +.cmm-manager-grid .tg-cell a:hover { + text-decoration: underline; +} + +.cmm-icon-passed { + width: 20px; + height: 20px; + position: absolute; + left: calc(50% - 10px); + top: calc(50% - 10px); +} + +.cmm-manager .cmm-btn-enable { + background-color: blue; + color: white; +} + +.cmm-manager .cmm-btn-disable { + background-color: MediumSlateBlue; + color: white; +} + +.cmm-manager .cmm-btn-install { + background-color: black; + color: white; +} + +.cmm-btn-download { + width: 18px; + height: 18px; + position: absolute; + left: calc(50% - 10px); + top: calc(50% - 10px); + cursor: pointer; + opacity: 0.8; + color: #fff; +} + +.cmm-btn-download:hover { + opacity: 1; +} + +.cmm-manager-light .cmm-btn-download { + color: #000; +} + +@keyframes cmm-btn-loading-bg { + 0% { + left: 0; + } + 100% { + left: -105px; + } +} + +.cmm-manager button.cmm-btn-loading { + position: relative; + overflow: hidden; + border-color: rgb(0 119 207 / 80%); + background-color: var(--comfy-input-bg); +} + +.cmm-manager button.cmm-btn-loading::after { + position: absolute; + top: 0; + left: 0; + content: ""; + width: 500px; + height: 100%; + background-image: repeating-linear-gradient( + -45deg, + rgb(0 119 207 / 30%), + rgb(0 119 207 / 30%) 10px, + transparent 10px, + transparent 15px + ); + animation: cmm-btn-loading-bg 2s linear infinite; +} + +.cmm-manager-light .cmm-node-name a { + color: blue; +} + +.cmm-manager-light .cm-warn-note { + background-color: #ccc !important; +} + +.cmm-manager-light .cmm-btn-install { + background-color: #333; +} \ No newline at end of file diff --git a/ComfyUI/custom_nodes/ComfyUI-Manager/js/model-manager.js b/ComfyUI/custom_nodes/ComfyUI-Manager/js/model-manager.js new file mode 100644 index 0000000000000000000000000000000000000000..7811ab657ab7e9c64ece59f8e001d101ea1eb84c --- /dev/null +++ b/ComfyUI/custom_nodes/ComfyUI-Manager/js/model-manager.js @@ -0,0 +1,820 @@ +import { app } from "../../scripts/app.js"; +import { $el } from "../../scripts/ui.js"; +import { + manager_instance, rebootAPI, + fetchData, md5, icons, show_message, customAlert, infoToast, showTerminal, + storeColumnWidth, restoreColumnWidth, loadCss +} from "./common.js"; +import { api } from "../../scripts/api.js"; + +// https://cenfun.github.io/turbogrid/api.html +import TG from "./turbogrid.esm.js"; + +loadCss("./model-manager.css"); + +const gridId = "model"; + +const pageHtml = ` +
    + + + + +
    +
    +
    +
    +
    +
    + +`; + +export class ModelManager { + static instance = null; + + constructor(app, manager_dialog) { + this.app = app; + this.manager_dialog = manager_dialog; + this.id = "cmm-manager"; + + this.filter = ''; + this.type = ''; + this.base = ''; + this.keywords = ''; + + this.init(); + + api.addEventListener("cm-queue-status", this.onQueueStatus); + } + + init() { + this.element = $el("div", { + parent: document.body, + className: "comfy-modal cmm-manager" + }); + this.element.innerHTML = pageHtml; + this.initFilter(); + this.bindEvents(); + this.initGrid(); + } + + initFilter() { + + this.filterList = [{ + label: "All", + value: "" + }, { + label: "Installed", + value: "installed" + }, { + label: "Not Installed", + value: "not_installed" + }, { + label: "In Workflow", + value: "in_workflow" + }]; + + this.typeList = [{ + label: "All", + value: "" + }]; + + this.baseList = [{ + label: "All", + value: "" + }]; + + this.updateFilter(); + + } + + updateFilter() { + const $filter = this.element.querySelector(".cmm-manager-filter"); + $filter.innerHTML = this.filterList.map(item => { + const selected = item.value === this.filter ? " selected" : ""; + return `` + }).join(""); + + const $type = this.element.querySelector(".cmm-manager-type"); + $type.innerHTML = this.typeList.map(item => { + const selected = item.value === this.type ? " selected" : ""; + return `` + }).join(""); + + const $base = this.element.querySelector(".cmm-manager-base"); + $base.innerHTML = this.baseList.map(item => { + const selected = item.value === this.base ? " selected" : ""; + return `` + }).join(""); + + } + + bindEvents() { + const eventsMap = { + ".cmm-manager-filter": { + change: (e) => { + this.filter = e.target.value; + this.updateGrid(); + } + }, + ".cmm-manager-type": { + change: (e) => { + this.type = e.target.value; + this.updateGrid(); + } + }, + ".cmm-manager-base": { + change: (e) => { + this.base = e.target.value; + this.updateGrid(); + } + }, + + ".cmm-manager-keywords": { + input: (e) => { + const keywords = `${e.target.value}`.trim(); + if (keywords !== this.keywords) { + this.keywords = keywords; + this.updateGrid(); + } + }, + focus: (e) => e.target.select() + }, + + ".cmm-manager-selection": { + click: (e) => { + const target = e.target; + const mode = target.getAttribute("mode"); + if (mode === "install") { + this.installModels(this.selectedModels, target); + } + } + }, + + ".cmm-manager-refresh": { + click: () => { + app.refreshComboInNodes(); + } + }, + + ".cmm-manager-stop": { + click: () => { + api.fetchApi('/manager/queue/reset'); + infoToast('Cancel', 'Remaining tasks will stop after completing the current task.'); + } + }, + + ".cmm-manager-back": { + click: (e) => { + this.close() + manager_instance.show(); + } + } + }; + Object.keys(eventsMap).forEach(selector => { + const target = this.element.querySelector(selector); + if (target) { + const events = eventsMap[selector]; + if (events) { + Object.keys(events).forEach(type => { + target.addEventListener(type, events[type]); + }); + } + } + }); + } + + // =========================================================================================== + + initGrid() { + const container = this.element.querySelector(".cmm-manager-grid"); + const grid = new TG.Grid(container); + this.grid = grid; + + grid.bind('onUpdated', (e, d) => { + + this.showStatus(`${grid.viewRows.length.toLocaleString()} external models`); + + }); + + grid.bind('onSelectChanged', (e, changes) => { + this.renderSelected(); + }); + + grid.bind("onColumnWidthChanged", (e, columnItem) => { + storeColumnWidth(gridId, columnItem) + }); + + grid.bind('onClick', (e, d) => { + const { rowItem } = d; + const target = d.e.target; + const mode = target.getAttribute("mode"); + if (mode === "install") { + this.installModels([rowItem], target); + } + + }); + + grid.setOption({ + theme: 'dark', + + selectVisible: true, + selectMultiple: true, + selectAllVisible: true, + + textSelectable: true, + scrollbarRound: true, + + frozenColumn: 1, + rowNotFound: "No Results", + + rowHeight: 40, + bindWindowResize: true, + bindContainerResize: true, + + cellResizeObserver: (rowItem, columnItem) => { + const autoHeightColumns = ['name', 'description']; + return autoHeightColumns.includes(columnItem.id) + }, + + // updateGrid handler for filter and keywords + rowFilter: (rowItem) => { + + const searchableColumns = ["name", "type", "base", "description", "filename", "save_path"]; + const models_extensions = ['.ckpt', '.pt', '.pt2', '.bin', '.pth', '.safetensors', '.pkl', '.sft']; + + let shouldShown = grid.highlightKeywordsFilter(rowItem, searchableColumns, this.keywords); + + if (shouldShown) { + if(this.filter) { + if (this.filter == "in_workflow") { + rowItem.in_workflow = null; + if (Array.isArray(app.graph._nodes)) { + app.graph._nodes.forEach((item, i) => { + if (Array.isArray(item.widgets_values)) { + item.widgets_values.forEach((_item, i) => { + if (rowItem.in_workflow === null && _item !== null && models_extensions.includes("." + _item.toString().split('.').pop())) { + let filename = _item.match(/([^\/]+)(?=\.\w+$)/)[0]; + if (grid.highlightKeywordsFilter(rowItem, searchableColumns, filename)) { + rowItem.in_workflow = "True"; + grid.highlightKeywordsFilter(rowItem, searchableColumns, ""); + } + } + }); + } + }); + } + } + return ((this.filter == "installed" && rowItem.installed == "True") || (this.filter == "not_installed" && rowItem.installed == "False") || (this.filter == "in_workflow" && rowItem.in_workflow == "True")); + } + + if(this.type && rowItem.type !== this.type) { + return false; + } + + if(this.base && rowItem.base !== this.base) { + return false; + } + + } + + return shouldShown; + } + }); + + } + + renderGrid() { + + // update theme + const colorPalette = this.app.ui.settings.settingsValues['Comfy.ColorPalette']; + Array.from(this.element.classList).forEach(cn => { + if (cn.startsWith("cmm-manager-")) { + this.element.classList.remove(cn); + } + }); + this.element.classList.add(`cmm-manager-${colorPalette}`); + + const options = { + theme: colorPalette === "light" ? "" : "dark" + }; + + const rows = this.modelList || []; + + const columns = [{ + id: 'id', + name: 'ID', + width: 50, + align: 'center' + }, { + id: 'name', + name: 'Name', + width: 200, + minWidth: 100, + maxWidth: 500, + classMap: 'cmm-node-name', + formatter: function(name, rowItem, columnItem, cellNode) { + return `${name}`; + } + }, { + id: 'installed', + name: 'Install', + width: 130, + minWidth: 110, + maxWidth: 200, + sortable: false, + align: 'center', + formatter: (installed, rowItem, columnItem) => { + if (rowItem.refresh) { + return `Refresh Required`; + } + if (installed === "True") { + return `
    ${icons.passed}
    `; + } + return ``; + } + }, { + id: 'url', + name: '', + width: 50, + sortable: false, + align: 'center', + formatter: (url, rowItem, columnItem) => { + return `${icons.download}`; + } + }, { + id: 'size', + name: 'Size', + width: 100, + formatter: (size) => { + if (typeof size === "number") { + return this.formatSize(size); + } + return size; + } + }, { + id: 'type', + name: 'Type', + width: 100 + }, { + id: 'base', + name: 'Base' + }, { + id: 'description', + name: 'Description', + width: 400, + maxWidth: 5000, + classMap: 'cmm-node-desc' + }, { + id: "save_path", + name: 'Save Path', + width: 200 + }, { + id: 'filename', + name: 'Filename', + width: 200 + }]; + + restoreColumnWidth(gridId, columns); + + this.grid.setData({ + options, + rows, + columns + }); + + this.grid.render(); + + } + + updateGrid() { + if (this.grid) { + this.grid.update(); + } + } + + // =========================================================================================== + + renderSelected() { + const selectedList = this.grid.getSelectedRows(); + if (!selectedList.length) { + this.showSelection(""); + this.selectedModels = []; + return; + } + + this.selectedModels = selectedList; + this.showSelection(`Selected ${selectedList.length} models `); + } + + focusInstall(item) { + const cellNode = this.grid.getCellNode(item, "installed"); + if (cellNode) { + const cellBtn = cellNode.querySelector(`button[mode="install"]`); + if (cellBtn) { + cellBtn.classList.add("cmm-btn-loading"); + return true + } + } + } + + async installModels(list, btn) { + let stats = await api.fetchApi('/manager/queue/status'); + + stats = await stats.json(); + if(stats.is_processing) { + customAlert(`[ComfyUI-Manager] There are already tasks in progress. Please try again after it is completed. (${stats.done_count}/${stats.total_count})`); + return; + } + + btn.classList.add("cmm-btn-loading"); + this.showError(""); + + let needRefresh = false; + let errorMsg = ""; + + await api.fetchApi('/manager/queue/reset'); + + let target_items = []; + + for (const item of list) { + this.grid.scrollRowIntoView(item); + target_items.push(item); + + if (!this.focusInstall(item)) { + this.grid.onNextUpdated(() => { + this.focusInstall(item); + }); + } + + this.showStatus(`Install ${item.name} ...`); + + const data = item.originalData; + data.ui_id = item.hash; + + const res = await api.fetchApi(`/manager/queue/install_model`, { + method: 'POST', + body: JSON.stringify(data) + }); + + if (res.status != 200) { + errorMsg = `'${item.name}': `; + + if(res.status == 403) { + errorMsg += `This action is not allowed with this security level configuration.\n`; + } else { + errorMsg += await res.text() + '\n'; + } + + break; + } + } + + this.install_context = {btn: btn, targets: target_items}; + + if(errorMsg) { + this.showError(errorMsg); + show_message("[Installation Errors]\n"+errorMsg); + + // reset + for(let k in target_items) { + const item = target_items[k]; + this.grid.updateCell(item, "installed"); + } + } + else { + await api.fetchApi('/manager/queue/start'); + this.showStop(); + showTerminal(); + } + } + + async onQueueStatus(event) { + let self = ModelManager.instance; + + if(event.detail.status == 'in_progress' && event.detail.ui_target == 'model_manager') { + const hash = event.detail.target; + + const item = self.grid.getRowItemBy("hash", hash); + + item.refresh = true; + self.grid.setRowSelected(item, false); + item.selectable = false; +// self.grid.updateCell(item, "tg-column-select"); + self.grid.updateRow(item); + } + else if(event.detail.status == 'done') { + self.hideStop(); + self.onQueueCompleted(event.detail); + } + } + + async onQueueCompleted(info) { + let result = info.model_result; + + if(result.length == 0) { + return; + } + + let self = ModelManager.instance; + + if(!self.install_context) { + return; + } + + let btn = self.install_context.btn; + + self.hideLoading(); + btn.classList.remove("cmm-btn-loading"); + + let errorMsg = ""; + + for(let hash in result){ + let v = result[hash]; + + if(v != 'success') + errorMsg += v + '\n'; + } + + for(let k in self.install_context.targets) { + let item = self.install_context.targets[k]; + self.grid.updateCell(item, "installed"); + } + + if (errorMsg) { + self.showError(errorMsg); + show_message("Installation Error:\n"+errorMsg); + } else { + self.showStatus(`Install ${result.length} models successfully`); + } + + self.showRefresh(); + self.showMessage(`To apply the installed model, please click the 'Refresh' button.`, "red") + + infoToast('Tasks done', `[ComfyUI-Manager] All model downloading tasks in the queue have been completed.\n${info.done_count}/${info.total_count}`); + self.install_context = undefined; + } + + getModelList(models) { + const typeMap = new Map(); + const baseMap = new Map(); + + models.forEach((item, i) => { + const { type, base, name, reference, installed } = item; + item.originalData = JSON.parse(JSON.stringify(item)); + item.size = this.sizeToBytes(item.size); + item.hash = md5(name + reference); + item.id = i + 1; + + if (installed === "True") { + item.selectable = false; + } + + typeMap.set(type, type); + baseMap.set(base, base); + + }); + + const typeList = []; + typeMap.forEach(type => { + typeList.push({ + label: type, + value: type + }); + }); + typeList.sort((a,b)=> { + const au = a.label.toUpperCase(); + const bu = b.label.toUpperCase(); + if (au !== bu) { + return au > bu ? 1 : -1; + } + return 0; + }); + this.typeList = [{ + label: "All", + value: "" + }].concat(typeList); + + + const baseList = []; + baseMap.forEach(base => { + baseList.push({ + label: base, + value: base + }); + }); + baseList.sort((a,b)=> { + const au = a.label.toUpperCase(); + const bu = b.label.toUpperCase(); + if (au !== bu) { + return au > bu ? 1 : -1; + } + return 0; + }); + this.baseList = [{ + label: "All", + value: "" + }].concat(baseList); + + return models; + } + + // =========================================================================================== + + async loadData() { + + this.showLoading(); + + this.showStatus(`Loading external model list ...`); + + const mode = manager_instance.datasrc_combo.value; + + const res = await fetchData(`/externalmodel/getlist?mode=${mode}`); + if (res.error) { + this.showError("Failed to get external model list."); + this.hideLoading(); + return + } + + const { models } = res.data; + + this.modelList = this.getModelList(models); + // console.log("models", this.modelList); + + this.updateFilter(); + + this.renderGrid(); + + this.hideLoading(); + + } + + // =========================================================================================== + + formatSize(v) { + const base = 1000; + const units = ['', 'K', 'M', 'G', 'T', 'P']; + const space = ''; + const postfix = 'B'; + if (v <= 0) { + return `0${space}${postfix}`; + } + for (let i = 0, l = units.length; i < l; i++) { + const min = Math.pow(base, i); + const max = Math.pow(base, i + 1); + if (v > min && v <= max) { + const unit = units[i]; + if (unit) { + const n = v / min; + const nl = n.toString().split('.')[0].length; + const fl = Math.max(3 - nl, 1); + v = n.toFixed(fl); + } + v = v + space + unit + postfix; + break; + } + } + return v; + } + + // for size sort + sizeToBytes(v) { + if (typeof v === "number") { + return v; + } + if (typeof v === "string") { + const n = parseFloat(v); + const unit = v.replace(/[0-9.B]+/g, "").trim().toUpperCase(); + if (unit === "K") { + return n * 1000; + } + if (unit === "M") { + return n * 1000 * 1000; + } + if (unit === "G") { + return n * 1000 * 1000 * 1000; + } + if (unit === "T") { + return n * 1000 * 1000 * 1000 * 1000; + } + } + return v; + } + + showSelection(msg) { + this.element.querySelector(".cmm-manager-selection").innerHTML = msg; + } + + showError(err) { + this.showMessage(err, "red"); + } + + showMessage(msg, color) { + if (color) { + msg = `${msg}`; + } + this.element.querySelector(".cmm-manager-message").innerHTML = msg; + } + + showStatus(msg, color) { + if (color) { + msg = `${msg}`; + } + this.element.querySelector(".cmm-manager-status").innerHTML = msg; + } + + showLoading() { +// this.setDisabled(true); + if (this.grid) { + this.grid.showLoading(); + this.grid.showMask({ + opacity: 0.05 + }); + } + } + + hideLoading() { +// this.setDisabled(false); + if (this.grid) { + this.grid.hideLoading(); + this.grid.hideMask(); + } + } + + setDisabled(disabled) { + const $close = this.element.querySelector(".cmm-manager-close"); + const $refresh = this.element.querySelector(".cmm-manager-refresh"); + const $stop = this.element.querySelector(".cmm-manager-stop"); + + const list = [ + ".cmm-manager-header input", + ".cmm-manager-header select", + ".cmm-manager-footer button", + ".cmm-manager-selection button" + ].map(s => { + return Array.from(this.element.querySelectorAll(s)); + }) + .flat() + .filter(it => { + return it !== $close && it !== $refresh && it !== $stop; + }); + + list.forEach($elem => { + if (disabled) { + $elem.setAttribute("disabled", "disabled"); + } else { + $elem.removeAttribute("disabled"); + } + }); + + Array.from(this.element.querySelectorAll(".cmm-btn-loading")).forEach($elem => { + $elem.classList.remove("cmm-btn-loading"); + }); + + } + + showRefresh() { + this.element.querySelector(".cmm-manager-refresh").style.display = "block"; + } + + showStop() { + this.element.querySelector(".cmm-manager-stop").style.display = "block"; + } + + hideStop() { + this.element.querySelector(".cmm-manager-stop").style.display = "none"; + } + + setKeywords(keywords = "") { + this.keywords = keywords; + this.element.querySelector(".cmm-manager-keywords").value = keywords; + } + + show() { + this.element.style.display = "flex"; + this.setKeywords(""); + this.showSelection(""); + this.showMessage(""); + this.loadData(); + } + + close() { + this.element.style.display = "none"; + } +} diff --git a/ComfyUI/custom_nodes/ComfyUI-Manager/js/node_fixer.js b/ComfyUI/custom_nodes/ComfyUI-Manager/js/node_fixer.js new file mode 100644 index 0000000000000000000000000000000000000000..867a7b815df90f18498639f74b68cec57c5e3576 --- /dev/null +++ b/ComfyUI/custom_nodes/ComfyUI-Manager/js/node_fixer.js @@ -0,0 +1,161 @@ +import { app } from "../../scripts/app.js"; +import { api } from "../../scripts/api.js"; + +function addMenuHandler(nodeType, cb) { + const getOpts = nodeType.prototype.getExtraMenuOptions; + nodeType.prototype.getExtraMenuOptions = function () { + const r = getOpts.apply(this, arguments); + cb.apply(this, arguments); + return r; + }; +} + +function distance(node1, node2) { + let dx = (node1.pos[0] + node1.size[0]/2) - (node2.pos[0] + node2.size[0]/2); + let dy = (node1.pos[1] + node1.size[1]/2) - (node2.pos[1] + node2.size[1]/2); + return Math.sqrt(dx * dx + dy * dy); +} + +function lookup_nearest_nodes(node) { + let nearest_distance = Infinity; + let nearest_node = null; + for(let other of app.graph._nodes) { + if(other === node) + continue; + + let dist = distance(node, other); + if (dist < nearest_distance && dist < 1000) { + nearest_distance = dist; + nearest_node = other; + } + } + + return nearest_node; +} + +function lookup_nearest_inputs(node) { + let input_map = {}; + + for(let i in node.inputs) { + let input = node.inputs[i]; + + if(input.link || input_map[input.type]) + continue; + + input_map[input.type] = {distance: Infinity, input_name: input.name, node: null, slot: null}; + } + + let x = node.pos[0]; + let y = node.pos[1] + node.size[1]/2; + + for(let other of app.graph._nodes) { + if(other === node || !other.outputs) + continue; + + let dx = x - (other.pos[0] + other.size[0]); + let dy = y - (other.pos[1] + other.size[1]/2); + + if(dx < 0) + continue; + + let dist = Math.sqrt(dx * dx + dy * dy); + + for(let input_type in input_map) { + for(let j in other.outputs) { + let output = other.outputs[j]; + if(output.type == input_type) { + if(input_map[input_type].distance > dist) { + input_map[input_type].distance = dist; + input_map[input_type].node = other; + input_map[input_type].slot = parseInt(j); + } + } + } + } + } + + let res = {}; + for (let i in input_map) { + if (input_map[i].node) { + res[i] = input_map[i]; + } + } + + return res; +} + +function connect_inputs(nearest_inputs, node) { + for(let i in nearest_inputs) { + let info = nearest_inputs[i]; + info.node.connect(info.slot, node.id, info.input_name); + } +} + +function node_info_copy(src, dest, connect_both, copy_shape) { + // copy input connections + for(let i in src.inputs) { + let input = src.inputs[i]; + if (input.widget !== undefined) { + const destWidget = dest.widgets.find(x => x.name === input.widget.name); + dest.convertWidgetToInput(destWidget); + } + if(input.link) { + let link = app.graph.links[input.link]; + let src_node = app.graph.getNodeById(link.origin_id); + src_node.connect(link.origin_slot, dest.id, input.name); + } + } + + // copy output connections + if(connect_both) { + let output_links = {}; + for(let i in src.outputs) { + let output = src.outputs[i]; + if(output.links) { + let links = []; + for(let j in output.links) { + links.push(app.graph.links[output.links[j]]); + } + output_links[output.name] = links; + } + } + + for(let i in dest.outputs) { + let links = output_links[dest.outputs[i].name]; + if(links) { + for(let j in links) { + let link = links[j]; + let target_node = app.graph.getNodeById(link.target_id); + dest.connect(parseInt(i), target_node, link.target_slot); + } + } + } + } + + if(copy_shape) { + dest.color = src.color; + dest.bgcolor = src.bgcolor; + dest.size = max(src.size, dest.size); + } + + app.graph.afterChange(); +} + +app.registerExtension({ + name: "Comfy.Manager.NodeFixer", + beforeRegisterNodeDef(nodeType, nodeData, app) { + addMenuHandler(nodeType, function (_, options) { + options.push({ + content: "Fix node (recreate)", + callback: () => { + let new_node = LiteGraph.createNode(nodeType.comfyClass); + new_node.pos = [this.pos[0], this.pos[1]]; + app.canvas.graph.add(new_node, false); + node_info_copy(this, new_node, true); + app.canvas.graph.remove(this); + requestAnimationFrame(() => app.canvas.setDirty(true, true)) + }, + }); + }); + } +}); diff --git a/ComfyUI/custom_nodes/ComfyUI-Manager/js/popover-helper.js b/ComfyUI/custom_nodes/ComfyUI-Manager/js/popover-helper.js new file mode 100644 index 0000000000000000000000000000000000000000..8c214b8c6298ff42e60976c12d07ba9755636199 --- /dev/null +++ b/ComfyUI/custom_nodes/ComfyUI-Manager/js/popover-helper.js @@ -0,0 +1,619 @@ +const hasOwn = function(obj, key) { + return Object.prototype.hasOwnProperty.call(obj, key); +}; + +const isNum = function(num) { + if (typeof num !== 'number' || isNaN(num)) { + return false; + } + const isInvalid = function(n) { + if (n === Number.MAX_VALUE || n === Number.MIN_VALUE || n === Number.NEGATIVE_INFINITY || n === Number.POSITIVE_INFINITY) { + return true; + } + return false; + }; + if (isInvalid(num)) { + return false; + } + return true; +}; + +const toNum = (num) => { + if (typeof (num) !== 'number') { + num = parseFloat(num); + } + if (isNaN(num)) { + num = 0; + } + num = Math.round(num); + return num; +}; + +const clamp = function(value, min, max) { + return Math.max(min, Math.min(max, value)); +}; + +const isWindow = (obj) => { + return Boolean(obj && obj === obj.window); +}; + +const isDocument = (obj) => { + return Boolean(obj && obj.nodeType === 9); +}; + +const isElement = (obj) => { + return Boolean(obj && obj.nodeType === 1); +}; + +// =========================================================================================== + +export const toRect = (obj) => { + if (obj) { + return { + left: toNum(obj.left || obj.x), + top: toNum(obj.top || obj.y), + width: toNum(obj.width), + height: toNum(obj.height) + }; + } + return { + left: 0, + top: 0, + width: 0, + height: 0 + }; +}; + +export const getElement = (selector) => { + if (typeof selector === 'string' && selector) { + if (selector.startsWith('#')) { + return document.getElementById(selector.slice(1)); + } + return document.querySelector(selector); + } + + if (isDocument(selector)) { + return selector.body; + } + if (isElement(selector)) { + return selector; + } +}; + +export const getRect = (target, fixed) => { + if (!target) { + return toRect(); + } + + if (isWindow(target)) { + return { + left: 0, + top: 0, + width: window.innerWidth, + height: window.innerHeight + }; + } + + const elem = getElement(target); + if (!elem) { + return toRect(target); + } + + const br = elem.getBoundingClientRect(); + const rect = toRect(br); + + // fix offset + if (!fixed) { + rect.left += window.scrollX; + rect.top += window.scrollY; + } + + rect.width = elem.offsetWidth; + rect.height = elem.offsetHeight; + + return rect; +}; + +// =========================================================================================== + +const calculators = { + + bottom: (info, containerRect, targetRect) => { + info.space = containerRect.top + containerRect.height - targetRect.top - targetRect.height - info.height; + info.top = targetRect.top + targetRect.height; + info.left = Math.round(targetRect.left + targetRect.width * 0.5 - info.width * 0.5); + }, + + top: (info, containerRect, targetRect) => { + info.space = targetRect.top - info.height - containerRect.top; + info.top = targetRect.top - info.height; + info.left = Math.round(targetRect.left + targetRect.width * 0.5 - info.width * 0.5); + }, + + right: (info, containerRect, targetRect) => { + info.space = containerRect.left + containerRect.width - targetRect.left - targetRect.width - info.width; + info.top = Math.round(targetRect.top + targetRect.height * 0.5 - info.height * 0.5); + info.left = targetRect.left + targetRect.width; + }, + + left: (info, containerRect, targetRect) => { + info.space = targetRect.left - info.width - containerRect.left; + info.top = Math.round(targetRect.top + targetRect.height * 0.5 - info.height * 0.5); + info.left = targetRect.left - info.width; + } +}; + +// with order +export const getDefaultPositions = () => { + return Object.keys(calculators); +}; + +const calculateSpace = (info, containerRect, targetRect) => { + const calculator = calculators[info.position]; + calculator(info, containerRect, targetRect); + if (info.space >= 0) { + info.passed += 1; + } +}; + +// =========================================================================================== + +const calculateAlignOffset = (info, containerRect, targetRect, alignType, sizeType) => { + + const popoverStart = info[alignType]; + const popoverSize = info[sizeType]; + + const containerStart = containerRect[alignType]; + const containerSize = containerRect[sizeType]; + + const targetStart = targetRect[alignType]; + const targetSize = targetRect[sizeType]; + + const targetCenter = targetStart + targetSize * 0.5; + + // size overflow + if (popoverSize > containerSize) { + const overflow = (popoverSize - containerSize) * 0.5; + info[alignType] = containerStart - overflow; + info.offset = targetCenter - containerStart + overflow; + return; + } + + const space1 = popoverStart - containerStart; + const space2 = (containerStart + containerSize) - (popoverStart + popoverSize); + + // both side passed, default to center + if (space1 >= 0 && space2 >= 0) { + if (info.passed) { + info.passed += 2; + } + info.offset = popoverSize * 0.5; + return; + } + + // one side passed + if (info.passed) { + info.passed += 1; + } + + if (space1 < 0) { + const min = containerStart; + info[alignType] = min; + info.offset = targetCenter - min; + return; + } + + // space2 < 0 + const max = containerStart + containerSize - popoverSize; + info[alignType] = max; + info.offset = targetCenter - max; + +}; + +const calculateHV = (info, containerRect) => { + if (['top', 'bottom'].includes(info.position)) { + info.top = clamp(info.top, containerRect.top, containerRect.top + containerRect.height - info.height); + return ['left', 'width']; + } + info.left = clamp(info.left, containerRect.left, containerRect.left + containerRect.width - info.width); + return ['top', 'height']; +}; + +const calculateOffset = (info, containerRect, targetRect) => { + + const [alignType, sizeType] = calculateHV(info, containerRect); + + calculateAlignOffset(info, containerRect, targetRect, alignType, sizeType); + + info.offset = clamp(info.offset, 0, info[sizeType]); + +}; + +// =========================================================================================== + +const calculateDistance = (info, previousPositionInfo) => { + if (!previousPositionInfo) { + return; + } + // no change if position no change with previous + if (info.position === previousPositionInfo.position) { + return; + } + const ax = info.left + info.width * 0.5; + const ay = info.top + info.height * 0.5; + const bx = previousPositionInfo.left + previousPositionInfo.width * 0.5; + const by = previousPositionInfo.top + previousPositionInfo.height * 0.5; + const dx = Math.abs(ax - bx); + const dy = Math.abs(ay - by); + info.distance = Math.round(Math.sqrt(dx * dx + dy * dy)); +}; + +// =========================================================================================== + +const calculatePositionInfo = (info, containerRect, targetRect, previousPositionInfo) => { + calculateSpace(info, containerRect, targetRect); + calculateOffset(info, containerRect, targetRect); + calculateDistance(info, previousPositionInfo); +}; + +// =========================================================================================== + +const calculateBestPosition = (containerRect, targetRect, infoMap, withOrder, previousPositionInfo) => { + + // position space: +1 + // align space: + // two side passed: +2 + // one side passed: +1 + + const safePassed = 3; + + if (previousPositionInfo) { + const prevInfo = infoMap[previousPositionInfo.position]; + if (prevInfo) { + calculatePositionInfo(prevInfo, containerRect, targetRect); + if (prevInfo.passed >= safePassed) { + return prevInfo; + } + prevInfo.calculated = true; + } + } + + const positionList = []; + Object.values(infoMap).forEach((info) => { + if (!info.calculated) { + calculatePositionInfo(info, containerRect, targetRect, previousPositionInfo); + } + positionList.push(info); + }); + + positionList.sort((a, b) => { + if (a.passed !== b.passed) { + return b.passed - a.passed; + } + + if (withOrder && a.passed >= safePassed && b.passed >= safePassed) { + return a.index - b.index; + } + + if (a.space !== b.space) { + return b.space - a.space; + } + + return a.index - b.index; + }); + + // logTable(positionList); + + return positionList[0]; +}; + +// const logTable = (() => { +// let time_id; +// return (info) => { +// clearTimeout(time_id); +// time_id = setTimeout(() => { +// console.table(info); +// }, 10); +// }; +// })(); + +// =========================================================================================== + +const getAllowPositions = (positions, defaultAllowPositions) => { + if (!positions) { + return; + } + if (Array.isArray(positions)) { + positions = positions.join(','); + } + positions = String(positions).split(',').map((it) => it.trim().toLowerCase()).filter((it) => it); + positions = positions.filter((it) => defaultAllowPositions.includes(it)); + if (!positions.length) { + return; + } + return positions; +}; + +const isPositionChanged = (info, previousPositionInfo) => { + if (!previousPositionInfo) { + return true; + } + + if (info.left !== previousPositionInfo.left) { + return true; + } + + if (info.top !== previousPositionInfo.top) { + return true; + } + + return false; +}; + +// =========================================================================================== + +// const log = (name, time) => { +// if (time > 0.1) { +// console.log(name, time); +// } +// }; + +export const getBestPosition = (containerRect, targetRect, popoverRect, positions, previousPositionInfo) => { + + const defaultAllowPositions = getDefaultPositions(); + let withOrder = true; + let allowPositions = getAllowPositions(positions, defaultAllowPositions); + if (!allowPositions) { + allowPositions = defaultAllowPositions; + withOrder = false; + } + + // console.log('withOrder', withOrder); + + // const start_time = performance.now(); + + const infoMap = {}; + allowPositions.forEach((k, i) => { + infoMap[k] = { + position: k, + index: i, + + top: 0, + left: 0, + width: popoverRect.width, + height: popoverRect.height, + + space: 0, + + offset: 0, + passed: 0, + + distance: 0 + }; + }); + + // log('infoMap', performance.now() - start_time); + + + const bestPosition = calculateBestPosition(containerRect, targetRect, infoMap, withOrder, previousPositionInfo); + + // check left/top + bestPosition.changed = isPositionChanged(bestPosition, previousPositionInfo); + + return bestPosition; +}; + +// =========================================================================================== + +const getTemplatePath = (width, height, arrowOffset, arrowSize, borderRadius) => { + const p = (px, py) => { + return [px, py].join(','); + }; + + const px = function(num, alignEnd) { + const floor = Math.floor(num); + let n = num < floor + 0.5 ? floor + 0.5 : floor + 1.5; + if (alignEnd) { + n -= 1; + } + return n; + }; + + const pxe = function(num) { + return px(num, true); + }; + + const ls = []; + + const innerLeft = px(arrowSize); + const innerRight = pxe(width - arrowSize); + arrowOffset = clamp(arrowOffset, innerLeft, innerRight); + + const innerTop = px(arrowSize); + const innerBottom = pxe(height - arrowSize); + + const startPoint = p(innerLeft, innerTop + borderRadius); + const arrowPoint = p(arrowOffset, 1); + + const LT = p(innerLeft, innerTop); + const RT = p(innerRight, innerTop); + + const AOT = p(arrowOffset - arrowSize, innerTop); + const RRT = p(innerRight - borderRadius, innerTop); + + ls.push(`M${startPoint}`); + ls.push(`V${innerBottom - borderRadius}`); + ls.push(`Q${p(innerLeft, innerBottom)} ${p(innerLeft + borderRadius, innerBottom)}`); + ls.push(`H${innerRight - borderRadius}`); + ls.push(`Q${p(innerRight, innerBottom)} ${p(innerRight, innerBottom - borderRadius)}`); + ls.push(`V${innerTop + borderRadius}`); + + if (arrowOffset < innerLeft + arrowSize + borderRadius) { + ls.push(`Q${RT} ${RRT}`); + ls.push(`H${arrowOffset + arrowSize}`); + ls.push(`L${arrowPoint}`); + if (arrowOffset < innerLeft + arrowSize) { + ls.push(`L${LT}`); + ls.push(`L${startPoint}`); + } else { + ls.push(`L${AOT}`); + ls.push(`Q${LT} ${startPoint}`); + } + } else if (arrowOffset > innerRight - arrowSize - borderRadius) { + if (arrowOffset > innerRight - arrowSize) { + ls.push(`L${RT}`); + } else { + ls.push(`Q${RT} ${p(arrowOffset + arrowSize, innerTop)}`); + } + ls.push(`L${arrowPoint}`); + ls.push(`L${AOT}`); + ls.push(`H${innerLeft + borderRadius}`); + ls.push(`Q${LT} ${startPoint}`); + } else { + ls.push(`Q${RT} ${RRT}`); + ls.push(`H${arrowOffset + arrowSize}`); + ls.push(`L${arrowPoint}`); + ls.push(`L${AOT}`); + ls.push(`H${innerLeft + borderRadius}`); + ls.push(`Q${LT} ${startPoint}`); + } + return ls.join(''); +}; + +const getPathData = function(position, width, height, arrowOffset, arrowSize, borderRadius) { + + const handlers = { + + bottom: () => { + const d = getTemplatePath(width, height, arrowOffset, arrowSize, borderRadius); + return { + d, + transform: '' + }; + }, + + top: () => { + const d = getTemplatePath(width, height, width - arrowOffset, arrowSize, borderRadius); + return { + d, + transform: `rotate(180,${width * 0.5},${height * 0.5})` + }; + }, + + left: () => { + const d = getTemplatePath(height, width, arrowOffset, arrowSize, borderRadius); + const x = (width - height) * 0.5; + const y = (height - width) * 0.5; + return { + d, + transform: `translate(${x} ${y}) rotate(90,${height * 0.5},${width * 0.5})` + }; + }, + + right: () => { + const d = getTemplatePath(height, width, height - arrowOffset, arrowSize, borderRadius); + const x = (width - height) * 0.5; + const y = (height - width) * 0.5; + return { + d, + transform: `translate(${x} ${y}) rotate(-90,${height * 0.5},${width * 0.5})` + }; + } + }; + + return handlers[position](); +}; + +// =========================================================================================== + +// position style cache +const styleCache = { + // position: '', + // top: {}, + // bottom: {}, + // left: {}, + // right: {} +}; + +export const getPositionStyle = (info, options = {}) => { + + const o = { + bgColor: '#fff', + borderColor: '#ccc', + borderRadius: 5, + arrowSize: 10 + }; + Object.keys(o).forEach((k) => { + + if (hasOwn(options, k)) { + const d = o[k]; + const v = options[k]; + + if (typeof d === 'string') { + // string + if (typeof v === 'string' && v) { + o[k] = v; + } + } else { + // number + if (isNum(v) && v >= 0) { + o[k] = v; + } + + } + + } + }); + + const key = [ + info.width, + info.height, + info.offset, + o.arrowSize, + o.borderRadius, + o.bgColor, + o.borderColor + ].join('-'); + + const positionCache = styleCache[info.position]; + if (positionCache && key === positionCache.key) { + const st = positionCache.style; + st.changed = styleCache.position !== info.position; + styleCache.position = info.position; + return st; + } + + // console.log(options); + + const data = getPathData(info.position, info.width, info.height, info.offset, o.arrowSize, o.borderRadius); + // console.log(data); + + const viewBox = [0, 0, info.width, info.height].join(' '); + const svg = [ + ``, + ``, + '' + ].join(''); + + // console.log(svg); + const backgroundImage = `url("data:image/svg+xml;charset=utf8,${encodeURIComponent(svg)}")`; + + const background = `${backgroundImage} center no-repeat`; + + const padding = `${o.arrowSize + o.borderRadius}px`; + + const style = { + background, + backgroundImage, + padding, + changed: true + }; + + styleCache.position = info.position; + styleCache[info.position] = { + key, + style + }; + + return style; +}; diff --git a/ComfyUI/custom_nodes/ComfyUI-Manager/js/snapshot.js b/ComfyUI/custom_nodes/ComfyUI-Manager/js/snapshot.js new file mode 100644 index 0000000000000000000000000000000000000000..520ca61504b9b92e21de879651be726b0f3aaf10 --- /dev/null +++ b/ComfyUI/custom_nodes/ComfyUI-Manager/js/snapshot.js @@ -0,0 +1,300 @@ +import { app } from "../../scripts/app.js"; +import { api } from "../../scripts/api.js" +import { ComfyDialog, $el } from "../../scripts/ui.js"; +import { manager_instance, rebootAPI, show_message } from "./common.js"; + + +async function restore_snapshot(target) { + if(SnapshotManager.instance) { + try { + const response = await api.fetchApi(`/snapshot/restore?target=${target}`, { cache: "no-store" }); + + if(response.status == 403) { + show_message('This action is not allowed with this security level configuration.'); + return false; + } + + if(response.status == 400) { + show_message(`Restore snapshot failed: ${target.title} / ${exception}`); + } + + app.ui.dialog.close(); + return true; + } + catch(exception) { + show_message(`Restore snapshot failed: ${target.title} / ${exception}`); + return false; + } + finally { + await SnapshotManager.instance.invalidateControl(); + SnapshotManager.instance.updateMessage("
    To apply the snapshot, please ComfyUI. And refresh browser.", 'cm-reboot-button2'); + } + } +} + +async function remove_snapshot(target) { + if(SnapshotManager.instance) { + try { + const response = await api.fetchApi(`/snapshot/remove?target=${target}`, { cache: "no-store" }); + + if(response.status == 403) { + show_message('This action is not allowed with this security level configuration.'); + return false; + } + + if(response.status == 400) { + show_message(`Remove snapshot failed: ${target.title} / ${exception}`); + } + + app.ui.dialog.close(); + return true; + } + catch(exception) { + show_message(`Restore snapshot failed: ${target.title} / ${exception}`); + return false; + } + finally { + await SnapshotManager.instance.invalidateControl(); + } + } +} + +async function save_current_snapshot() { + try { + const response = await api.fetchApi('/snapshot/save', { cache: "no-store" }); + app.ui.dialog.close(); + return true; + } + catch(exception) { + show_message(`Backup snapshot failed: ${exception}`); + return false; + } + finally { + await SnapshotManager.instance.invalidateControl(); + SnapshotManager.instance.updateMessage("
    Current snapshot saved."); + } +} + +async function getSnapshotList() { + const response = await api.fetchApi(`/snapshot/getlist`); + const data = await response.json(); + return data; +} + +export class SnapshotManager extends ComfyDialog { + static instance = null; + + restore_buttons = []; + message_box = null; + data = null; + + clear() { + this.restore_buttons = []; + this.message_box = null; + this.data = null; + } + + constructor(app, manager_dialog) { + super(); + this.manager_dialog = manager_dialog; + this.search_keyword = ''; + this.element = $el("div.comfy-modal", { parent: document.body }, []); + } + + async remove_item() { + caller.disableButtons(); + + await caller.invalidateControl(); + } + + createControls() { + return [ + $el("button.cm-small-button", { + type: "button", + textContent: "Close", + onclick: () => { this.close(); } + }) + ]; + } + + startRestore(target) { + const self = SnapshotManager.instance; + + self.updateMessage(`
    Restore snapshot '${target.name}'`); + + for(let i in self.restore_buttons) { + self.restore_buttons[i].disabled = true; + self.restore_buttons[i].style.backgroundColor = 'gray'; + } + } + + async invalidateControl() { + this.clear(); + this.data = (await getSnapshotList()).items; + + while (this.element.children.length) { + this.element.removeChild(this.element.children[0]); + } + + await this.createGrid(); + await this.createBottomControls(); + } + + updateMessage(msg, btn_id) { + this.message_box.innerHTML = msg; + if(btn_id) { + const rebootButton = document.getElementById(btn_id); + const self = this; + rebootButton.onclick = function() { + if(rebootAPI()) { + self.close(); + self.manager_dialog.close(); + } + }; + } + } + + async createGrid(models_json) { + var grid = document.createElement('table'); + grid.setAttribute('id', 'snapshot-list-grid'); + + var thead = document.createElement('thead'); + var tbody = document.createElement('tbody'); + + var headerRow = document.createElement('tr'); + thead.style.position = "sticky"; + thead.style.top = "0px"; + thead.style.borderCollapse = "collapse"; + thead.style.tableLayout = "fixed"; + + var header1 = document.createElement('th'); + header1.innerHTML = '  ID  '; + header1.style.width = "20px"; + var header2 = document.createElement('th'); + header2.innerHTML = 'Datetime'; + header2.style.width = "100%"; + var header_button = document.createElement('th'); + header_button.innerHTML = 'Action'; + header_button.style.width = "100px"; + + thead.appendChild(headerRow); + headerRow.appendChild(header1); + headerRow.appendChild(header2); + headerRow.appendChild(header_button); + + headerRow.style.backgroundColor = "Black"; + headerRow.style.color = "White"; + headerRow.style.textAlign = "center"; + headerRow.style.width = "100%"; + headerRow.style.padding = "0"; + + grid.appendChild(thead); + grid.appendChild(tbody); + + this.grid_rows = {}; + + if(this.data) + for (var i = 0; i < this.data.length; i++) { + const data = this.data[i]; + var dataRow = document.createElement('tr'); + var data1 = document.createElement('td'); + data1.style.textAlign = "center"; + data1.innerHTML = i+1; + var data2 = document.createElement('td'); + data2.innerHTML = ` ${data}`; + var data_button = document.createElement('td'); + data_button.style.textAlign = "center"; + + var restoreBtn = document.createElement('button'); + restoreBtn.innerHTML = 'Restore'; + restoreBtn.style.width = "100px"; + restoreBtn.style.backgroundColor = 'blue'; + + restoreBtn.addEventListener('click', function() { + restore_snapshot(data); + }); + + var removeBtn = document.createElement('button'); + removeBtn.innerHTML = 'Remove'; + removeBtn.style.width = "100px"; + removeBtn.style.backgroundColor = 'red'; + + removeBtn.addEventListener('click', function() { + remove_snapshot(data); + }); + + data_button.appendChild(restoreBtn); + data_button.appendChild(removeBtn); + + dataRow.style.backgroundColor = "var(--bg-color)"; + dataRow.style.color = "var(--fg-color)"; + dataRow.style.textAlign = "left"; + + dataRow.appendChild(data1); + dataRow.appendChild(data2); + dataRow.appendChild(data_button); + tbody.appendChild(dataRow); + + this.grid_rows[i] = {data:data, control:dataRow}; + } + + let self = this; + const panel = document.createElement('div'); + panel.style.width = "100%"; + panel.appendChild(grid); + + function handleResize() { + const parentHeight = self.element.clientHeight; + const gridHeight = parentHeight - 200; + + grid.style.height = gridHeight + "px"; + } + window.addEventListener("resize", handleResize); + + grid.style.position = "relative"; + grid.style.display = "inline-block"; + grid.style.width = "100%"; + grid.style.height = "100%"; + grid.style.overflowY = "scroll"; + this.element.style.height = "85%"; + this.element.style.width = "80%"; + this.element.appendChild(panel); + + handleResize(); + } + + async createBottomControls() { + var close_button = document.createElement("button"); + close_button.className = "cm-small-button"; + close_button.innerHTML = "Close"; + close_button.onclick = () => { this.close(); } + close_button.style.display = "inline-block"; + + var save_button = document.createElement("button"); + save_button.className = "cm-small-button"; + save_button.innerHTML = "Save snapshot"; + save_button.onclick = () => { save_current_snapshot(); } + save_button.style.display = "inline-block"; + save_button.style.horizontalAlign = "right"; + save_button.style.width = "170px"; + + this.message_box = $el('div', {id:'custom-download-message'}, [$el('br'), '']); + this.message_box.style.height = '60px'; + this.message_box.style.verticalAlign = 'middle'; + + this.element.appendChild(this.message_box); + this.element.appendChild(close_button); + this.element.appendChild(save_button); + } + + async show() { + try { + this.invalidateControl(); + this.element.style.display = "block"; + this.element.style.zIndex = 1099; + } + catch(exception) { + app.ui.dialog.show(`Failed to get external model list. / ${exception}`); + } + } +} diff --git a/ComfyUI/custom_nodes/ComfyUI-Manager/js/turbogrid.esm.js b/ComfyUI/custom_nodes/ComfyUI-Manager/js/turbogrid.esm.js new file mode 100644 index 0000000000000000000000000000000000000000..fd0bfb57db2af168bac5642b78e503fcac9d82af --- /dev/null +++ b/ComfyUI/custom_nodes/ComfyUI-Manager/js/turbogrid.esm.js @@ -0,0 +1 @@ +var t={915:(t,e,i)=>{i.d(e,{A:()=>l});var o=i(256),n=i.n(o),s=i(505),r=i.n(s)()(n());r.push([t.id,'.tg-turbogrid{position:relative;z-index:0;width:100%;height:100%;margin:0;padding:0;box-sizing:border-box;font-size:14px;font-family:arial,sans-serif;outline:0;cursor:default;overflow:hidden}.tg-turbogrid *,.tg-turbogrid *::before,.tg-turbogrid *::after{box-sizing:border-box}.tg-text-unselectable.tg-turbogrid{user-select:none}.tg-turbogrid svg{display:block;pointer-events:none}.tg-turbogrid .tg-symbols{font-family:webdings,sans-serif}.tg-turbogrid .tg-nowrap{white-space:nowrap}.tg-turbogrid .tg-align-left{text-align:left}.tg-turbogrid .tg-align-center{text-align:center}.tg-turbogrid .tg-align-right{text-align:right}@keyframes tg-fade-in{from{opacity:0}to{opacity:1}}@keyframes tg-fade-out{from{opacity:1}to{opacity:0}}.tg-turbogrid .tg-fade-in{animation-name:tg-fade-in;animation-duration:.2s;animation-fill-mode:both}.tg-turbogrid .tg-fade-in .tg-scrollbar-track{display:none}.tg-turbogrid .tg-fade-out{animation-name:tg-fade-out;animation-duration:.2s;animation-fill-mode:both}.tg-turbogrid .tg-fade-out .tg-scrollbar-track{display:none}.tg-turbogrid .tg-mask{position:absolute;top:0;left:0;z-index:200;display:none;width:100%;height:100%;background-color:#000;opacity:.1}@keyframes tg-loading-animation{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}.tg-turbogrid .tg-loading{position:absolute;top:50%;left:50%;z-index:300;display:none;transform:translate(-50%, -50%);pointer-events:none}.tg-turbogrid .tg-loading-default{width:35px;height:35px;color:#0077cf;animation:1s tg-loading-animation linear infinite}.tg-turbogrid .tg-loading-fast{animation:.382s tg-loading-animation linear infinite}.tg-turbogrid .tg-checkbox{width:100%;height:100%;cursor:pointer;overflow:hidden}.tg-turbogrid .tg-checkbox:hover .tg-checkbox-item{fill:#005ba1}.tg-turbogrid .tg-checkbox.tg-select-icon-all{height:18px}.tg-turbogrid .tg-checkbox .tg-icon-checkbox{position:absolute;top:50%;left:50%;display:block;width:16px;height:16px;transform:translate(-50%, -50%)}.tg-turbogrid .tg-checkbox .tg-checkbox-item{display:none;fill:gray}.tg-turbogrid .tg-checkbox .tg-checkbox-none{display:block}.tg-turbogrid .tg-checkbox.tg-selected .tg-checkbox-selected{display:block;fill:#0077cf}.tg-turbogrid .tg-checkbox.tg-mixed .tg-checkbox-mixed{display:block;fill:#0077cf}.tg-turbogrid .tg-radio{cursor:pointer;overflow:hidden}.tg-turbogrid .tg-radio:hover .tg-icon-radio::before{border-color:#005ba1}.tg-turbogrid .tg-radio .tg-icon-radio{position:absolute;top:50%;left:50%;width:16px;height:16px;transform:translate(-50%, -50%)}.tg-turbogrid .tg-radio .tg-icon-radio::before{position:absolute;top:50%;left:50%;content:"";display:block;width:16px;height:16px;border:thin solid gray;border-radius:50%;background:#fff;transform:translate(-50%, -50%)}.tg-turbogrid .tg-radio .tg-icon-radio::after{position:absolute;top:50%;left:50%;content:"";display:none;width:10px;height:10px;border-radius:50%;background:#0077cf;transform:translate(-50%, -50%)}.tg-turbogrid .tg-radio.tg-selected .tg-icon-radio::after{display:block;border-color:#0077cf}.tg-turbogrid .tg-scrollbar{position:absolute;z-index:100;overflow:hidden;user-select:none}.tg-turbogrid .tg-scrollbar-v{top:0;right:0}.tg-turbogrid .tg-scrollbar-h{left:0;bottom:0}.tg-turbogrid .tg-scrollbar-track{position:relative;width:100%;height:100%;background:#f9f9f9;overflow:hidden;user-select:none}.tg-turbogrid .tg-scrollbar-thumb{position:absolute;top:0;left:0;border-radius:1px;background:#999;overflow:hidden;user-select:none}.tg-turbogrid .tg-scrollbar-thumb:hover{background:#888}.tg-turbogrid .tg-scrollbar-thumb-hold{background:#666}.tg-turbogrid .tg-scrollbar-thumb-hold:hover{background:#666}.tg-turbogrid .tg-scrollbar-round .tg-scrollbar-track{border-radius:10px}.tg-turbogrid .tg-scrollbar-round .tg-scrollbar-thumb{border-radius:10px}.tg-turbogrid .tg-scroll-pane{position:relative;margin:0;padding:0;border:none;outline:none;overflow:hidden}.tg-turbogrid .tg-scroll-view{position:relative;width:100%;height:100%;margin:0;padding:0;border:none;overflow:hidden}.tg-turbogrid .tg-scroll-body{position:absolute}.tg-turbogrid .tg-header{position:relative;width:10000px;border-left:0;overflow:hidden}.tg-turbogrid .tg-header-table{position:relative;color:#5e5e5e;font-weight:bold;font-size:14px;line-height:16px;border-bottom:thin solid #e5e5e5;overflow:hidden}.tg-turbogrid .tg-header-item{position:absolute;bottom:0}.tg-turbogrid .tg-header-group-item{overflow:hidden}.tg-turbogrid .tg-header-group-item::after{position:absolute;left:5px;bottom:0;content:"";display:block;width:calc(100% - 10px);height:1px;border-bottom:thin solid #ccc}.tg-turbogrid .tg-column-header{position:absolute;bottom:0;overflow:hidden}.tg-turbogrid .tg-column-header .tg-column-name{padding:10px 5px;text-overflow:ellipsis;overflow:hidden}.tg-turbogrid .tg-column-header .tg-column-name.tg-header-group-name{margin:0 5px;padding:5px 0}.tg-turbogrid .tg-column-resizing{position:absolute;top:0;right:-5px;z-index:100;width:10px;height:100%;background:#ccc;cursor:ew-resize;opacity:0}.tg-turbogrid .tg-header-column-last .tg-column-resizing{right:0}.tg-turbogrid .tg-column-sortable .tg-column-name{cursor:pointer}.tg-turbogrid .tg-column-sortable .tg-sort-indicator{cursor:pointer}.tg-turbogrid .tg-column-sorted{color:#000}.tg-turbogrid .tg-header-sort-h .tg-column-name{padding:12px 5px 15px}.tg-turbogrid .tg-header-sort-h .tg-column-sort{width:100%;height:15px;margin-top:-15px;padding:0 5px;overflow:hidden}.tg-turbogrid .tg-header-sort-h .tg-sort-indicator{position:relative;display:none;width:100%;height:100%}.tg-turbogrid .tg-header-sort-h .tg-column-sorted .tg-column-sort .tg-sort-indicator{display:block}.tg-turbogrid .tg-header-sort-h .tg-sort-indicator-line{position:absolute;top:1px;width:100%;height:0;border-top:thin solid #1e1e1e;overflow:hidden}.tg-turbogrid .tg-header-sort-h .tg-sort-indicator-icon{position:absolute;top:5px;left:0;right:inherit}.tg-turbogrid .tg-header-sort-h .tg-align-right .tg-sort-indicator-icon{left:inherit;right:0}.tg-turbogrid .tg-header-sort-h .tg-align-center .tg-sort-indicator-icon{left:50%;transform:translateX(-50%)}.tg-turbogrid .tg-header-sort-h .tg-sort-indicator-icon .tg-icon-sort-h{display:block;width:19px;height:6px}.tg-turbogrid .tg-header-sort-h .tg-sort-indicator-icon .tg-icon-item{display:none;fill:#1e1e1e}.tg-turbogrid .tg-header-sort-h .tg-sort-indicator-icon .tg-icon-item-light{fill:#ababab}.tg-turbogrid .tg-column-sort-v{display:flex;flex-direction:row;align-items:center}.tg-turbogrid .tg-column-sort-v .tg-column-name{white-space:nowrap;text-overflow:ellipsis;overflow:hidden}.tg-turbogrid .tg-column-sort-v.tg-align-right{justify-content:right}.tg-turbogrid .tg-column-sort-v .tg-sort-indicator{position:relative;width:16px;height:16px}.tg-turbogrid .tg-column-sort-v .tg-sort-indicator-icon{position:absolute}.tg-turbogrid .tg-column-sort-v .tg-sort-indicator-icon .tg-icon-sort-v{display:block;width:10px;height:16px}.tg-turbogrid .tg-column-sort-v .tg-sort-indicator-icon .tg-icon-item{fill:#ababab}.tg-turbogrid .tg-column-sort-v .tg-sort-indicator-icon .tg-icon-item-light{fill:#ababab}.tg-turbogrid .tg-column-sort-v.tg-column-sorted .tg-sort-indicator-icon .tg-icon-item{fill:#1e1e1e}.tg-turbogrid .tg-column-sort-v.tg-column-sorted .tg-sort-indicator-icon .tg-icon-item-light{fill:#ababab}.tg-turbogrid .tg-sort-desc .tg-sort-indicator .tg-sort-indicator-icon .tg-desc{display:block}.tg-turbogrid .tg-sort-desc .tg-sort-indicator .tg-sort-indicator-icon .tg-asc{display:none}.tg-turbogrid .tg-sort-asc .tg-sort-indicator .tg-sort-indicator-icon .tg-desc{display:none}.tg-turbogrid .tg-sort-asc .tg-sort-indicator .tg-sort-indicator-icon .tg-asc{display:block}.tg-turbogrid .tg-column-line{position:absolute;top:0;left:0;z-index:100;display:none;height:100%;pointer-events:none}.tg-turbogrid .tg-column-line-item{position:absolute;top:0;bottom:0;display:block;width:0;height:100%;border-left:thin solid #ccc}.tg-turbogrid .tg-column-line-item.tg-active{border-left:thin solid #0077cf}.tg-turbogrid .tg-column-dragging{cursor:ew-resize}.tg-turbogrid .tg-column-dragging .tg-column-name{cursor:ew-resize}.tg-turbogrid .tg-column-dragging .tg-column-resizing:not(.tg-resizing-active){display:none}.tg-turbogrid .tg-tree{position:relative;display:flex;flex-direction:row;place-items:center left;width:100%;height:100%;overflow:hidden}.tg-turbogrid .tg-tree-icon{position:relative;width:15px;height:100%;min-height:9px;text-align:left;cursor:pointer;overflow:hidden}.tg-turbogrid .tg-tree-icon .tg-icon-tree{position:absolute;top:50%;left:0;display:block;width:9px;height:9px;transform:translate(0, -50%);overflow:hidden}.tg-turbogrid .tg-tree-icon .tg-tree-item{display:none}.tg-turbogrid .tg-tree-icon-collapsed .tg-tree-collapsed{display:block}.tg-turbogrid .tg-tree-icon-empty .tg-tree-collapsed{opacity:.5}.tg-turbogrid .tg-tree-icon-expanded .tg-tree-expanded{display:block}.tg-turbogrid .tg-tree-name{flex:1;text-overflow:ellipsis;overflow:hidden}.tg-turbogrid .tg-tree-header .tg-tree .tg-tree-icon{display:none}.tg-turbogrid .tg-tree-icon-all{position:relative;height:17px}.tg-turbogrid .tg-tree-header-indent .tg-tree{padding-left:5px}.tg-turbogrid .tg-tree-header-indent .tg-tree .tg-tree-icon{display:block}.tg-turbogrid .tg-tree-header-indent.tg-column-sort-h .tg-column-sort{width:calc(100% - 20px);margin-left:20px}.tg-turbogrid .tg-pane{position:absolute;width:100%;outline:0;overflow:hidden}.tg-turbogrid .tg-header-frame{position:relative;display:block;outline:0;overflow:hidden}.tg-turbogrid .tg-header-frame .tg-pane{height:100%}.tg-turbogrid .tg-body-frame{position:relative;width:100%;outline:0}.tg-turbogrid .tg-body-message{position:absolute;display:none;width:100%;height:100%;padding:10px;overflow:hidden}.tg-turbogrid .tg-body-message img,.tg-turbogrid .tg-body-message div{position:absolute;top:50%;left:50%;transform:translate(-50%, -50%)}.tg-turbogrid .tg-body{position:absolute;outline:0}.tg-turbogrid .tg-cell-hover-icon{display:none}.tg-touch-device.tg-turbogrid .tg-cell-hover-icon{display:inherit}.tg-turbogrid .tg-cell-row-number{font-weight:normal}.tg-turbogrid .tg-cell-row-drag .tg-row-drag-icon{position:absolute;top:50%;left:50%;width:24px;height:24px;cursor:move;opacity:.8;transform:translate(-50%, -50%)}.tg-turbogrid .tg-cell-row-drag .tg-row-drag-icon:hover{opacity:1}.tg-turbogrid .tg-cell{position:absolute;z-index:1;height:100%;margin:0;padding:0 5px;color:#1e1e1e;white-space:nowrap;text-overflow:ellipsis;vertical-align:middle;overflow:hidden}.tg-turbogrid .tg-cell:focus{outline:none}.tg-turbogrid .tg-cell.tg-flashing{border:1px solid red !important}.tg-turbogrid .tg-cell.tg-selected{background-color:beige}.tg-turbogrid .tg-cell.tg-align-left.tg-cell-negative{padding-left:1px}.tg-turbogrid .tg-cell.tg-align-right.tg-cell-negative{padding-right:1px}.tg-turbogrid .tg-cell.tg-cell-observer{display:flex;flex-direction:column;justify-content:center;padding:5px;line-height:normal;white-space:normal;text-overflow:ellipsis;overflow:hidden}.tg-turbogrid .tg-cell.tg-cell-observer .tg-observer{position:relative;margin:0;padding:0;border:none}.tg-turbogrid .tg-row{position:absolute;width:100%;border:0;border-bottom:thin solid #e5e5e5}.tg-turbogrid .tg-row.tg-group-line{border-bottom:thin solid #999}.tg-turbogrid .tg-row.tg-none-line{border-bottom:none}.tg-turbogrid .tg-row.tg-top-line{border-top:thin solid #e5e5e5}.tg-turbogrid .tg-row.tg-group{font-weight:bold;overflow:hidden}.tg-turbogrid .tg-row.tg-group .tg-cell.tg-align-left.tg-cell-negative{padding-left:0}.tg-turbogrid .tg-row.tg-group .tg-cell.tg-align-right.tg-cell-negative{padding-right:0}.tg-turbogrid .tg-row.tg-hover .tg-cell .tg-cell-hover-icon{display:inherit}.tg-turbogrid .tg-row.tg-dragging{opacity:.3}.tg-turbogrid .tg-row.tg-clone{z-index:1000;border:1px dashed #ccc;border-right:none;border-left:none;background:#fff;cursor:move;opacity:.5}.tg-turbogrid .tg-row.tg-clone *{cursor:move}.tg-turbogrid .tg-row-placeholder{position:absolute;z-index:9999;width:100%;border-top:2px solid #00a8e1;pointer-events:none}.tg-turbogrid .tg-row::before,.tg-turbogrid .tg-row::after{position:absolute;top:0;left:0;content:"";z-index:100;display:none;width:100%;height:100%;pointer-events:none}.tg-turbogrid .tg-hover.tg-row::before{display:block;background:rgba(0,0,0,.08)}.tg-turbogrid .tg-selected.tg-row::after{display:block;background:rgba(0,0,0,.13)}.tg-lightblue .tg-header-item{border-top:thin solid #e8eaf0;border-right:thin solid #e8eaf0}.tg-lightblue .tg-column-name{padding:5px;color:#304265}.tg-lightblue .tg-header-group-item::after{display:none}.tg-lightblue .tg-checkbox .tg-icon-item{fill:#d4d7e0}.tg-lightblue .tg-checkbox:hover .tg-icon-item{fill:#107fff}.tg-lightblue .tg-checkbox.tg-selected .tg-select-checkbox{fill:#107fff}.tg-lightblue .tg-checkbox.tg-mixed .tg-select-mixed{fill:#107fff}.tg-lightblue .tg-cell{color:#304265;border-right:thin solid #e8eaf0}.tg-lightblue .tg-row{border-bottom:thin solid #e8eaf0}.tg-lightblue .tg-row.tg-group-line{border-bottom:thin solid #c9ccd8}.tg-lightblue .tg-row.tg-selected{background:rgba(58,116,213,.05)}.tg-lightblue .tg-row.tg-hover{background:rgba(58,116,213,.05)}.tg-lightblue .tg-row.tg-even{background:#fbfcfe}.tg-lightblue .tg-row.tg-odd{background:#fff}.tg-lightblue .tg-hover.tg-row::before{background:rgba(58,116,213,.05)}.tg-lightblue .tg-selected.tg-row::after{background:rgba(58,116,213,.1)}.tg-lightblue .tg-header-frame{border-bottom:thin solid #e8eaf0}.tg-lightblue .tg-row-not-found .tg-frozen-line-v{border-right:none}.tg-lightblue .tg-scrollbar-track{background:#fff}.tg-lightblue .tg-scrollbar-thumb{background:rgba(48,66,101,.35)}.tg-lightblue .tg-scrollbar-thumb:hover{background-color:#a8a8a8}.tg-lightblue .tg-scrollbar-thumb:active{background-color:#787878}.tg-dark{background:#1e1e1e}.tg-dark .tg-checkbox .tg-icon-item{fill:#ababab}.tg-dark .tg-header-table{color:#ccc;border-bottom:thin solid #333}.tg-dark .tg-header-group-item::after{border-bottom:1px solid #999}.tg-dark .tg-column-sorted{color:#fff}.tg-dark .tg-column-sorted .tg-tree-icon-all .tg-icon-item{fill:#fff}.tg-dark .tg-header-sort-h .tg-sort-indicator-line{border-top:thin solid #eee}.tg-dark .tg-header-sort-h .tg-sort-indicator-icon .tg-icon-item{fill:#eee}.tg-dark .tg-header-sort-h .tg-sort-indicator-icon .tg-icon-item-light{fill:#666}.tg-dark .tg-column-sort-v .tg-sort-indicator-icon .tg-icon-item{fill:#666}.tg-dark .tg-column-sort-v .tg-sort-indicator-icon .tg-icon-item-light{fill:#666}.tg-dark .tg-column-sort-v.tg-column-sorted .tg-sort-indicator-icon .tg-icon-item{fill:#fff}.tg-dark .tg-column-sort-v.tg-column-sorted .tg-sort-indicator-icon .tg-icon-item-light{fill:#666}.tg-dark .tg-tree-icon .tg-icon-item{fill:#fff}.tg-dark .tg-tree-icon-all .tg-icon-item{fill:#999}.tg-dark .tg-header-item .tg-tree-icon .tg-icon-item{fill:#999}.tg-dark .tg-header-item .tg-column-sorted .tg-tree-icon .tg-icon-item{fill:#fff}.tg-dark .tg-row{border-bottom:thin solid #333}.tg-dark .tg-row.tg-group-line{border-bottom:thin solid #666}.tg-dark .tg-row.tg-clone{border:1px dashed #1e1e1e;opacity:.1}.tg-dark .tg-cell{color:#eee}.tg-dark .tg-body-message{color:#eee}.tg-dark .tg-hover.tg-row::before{background:rgba(255,255,255,.1)}.tg-dark .tg-selected.tg-row::after{background:rgba(255,255,255,.2)}.tg-dark .tg-mask{background-color:#fff}.tg-dark .tg-scrollbar-track{background:#333}.tg-dark .tg-scrollbar-thumb{background:#bbb}.tg-dark .tg-scrollbar-thumb:hover{background:#ddd}.tg-dark .tg-scrollbar-thumb-hold{background:#eee}.tg-dark .tg-scrollbar-thumb-hold:hover{background:#eee}.tg-pointer-events-none{pointer-events:none}',""]);const l=r},505:t=>{t.exports=function(t){var e=[];return e.toString=function(){return this.map((function(e){var i="",o=void 0!==e[5];return e[4]&&(i+="@supports (".concat(e[4],") {")),e[2]&&(i+="@media ".concat(e[2]," {")),o&&(i+="@layer".concat(e[5].length>0?" ".concat(e[5]):""," {")),i+=t(e),o&&(i+="}"),e[2]&&(i+="}"),e[4]&&(i+="}"),i})).join("")},e.i=function(t,i,o,n,s){"string"==typeof t&&(t=[[null,t,void 0]]);var r={};if(o)for(var l=0;l0?" ".concat(c[5]):""," {").concat(c[1],"}")),c[5]=s),i&&(c[2]?(c[1]="@media ".concat(c[2]," {").concat(c[1],"}"),c[2]=i):c[2]=i),n&&(c[4]?(c[1]="@supports (".concat(c[4],") {").concat(c[1],"}"),c[4]=n):c[4]="".concat(n)),e.push(c))}},e}},256:t=>{t.exports=function(t){return t[1]}}},e={};function i(o){var n=e[o];if(void 0!==n)return n.exports;var s=e[o]={id:o,exports:{}};return t[o](s,s.exports,i),s.exports}i.n=t=>{var e=t&&t.__esModule?()=>t.default:()=>t;return i.d(e,{a:e}),e},i.d=(t,e)=>{for(var o in e)i.o(e,o)&&!i.o(t,o)&&Object.defineProperty(t,o,{enumerable:!0,get:e[o]})},i.o=(t,e)=>Object.prototype.hasOwnProperty.call(t,e);var o={};(()=>{i.d(o,{$:()=>w,MP:()=>e,_d:()=>k,xA:()=>Xt,In:()=>X,T8:()=>B,Gr:()=>kt,ht:()=>Kt,J0:()=>d,xv:()=>Yt,Ay:()=>qt});const t="turbogrid",e={ID:t,NS:`tg-${t}`,VERSION:"3.2.0",TIMESTAMP:"2024-06-20T10:33:49.165Z",UP:"up",DOWN:"down",LEFT:"left",RIGHT:"right",TREE_INDENT:15},n=function(t){if(!t||"object"!=typeof t)return!1;const e=Object.prototype.toString.call(t);return!!["[object Object]","[object Array]"].includes(e)&&(!t.constructor||!![Object,Array].includes(t.constructor))},s=function(t,e){let i;return t.forEach((t=>{n(t)&&(i||(i=t instanceof Array?[]:{}),t instanceof Array?function(t,e,i){const o=e.length;for(let s=0;s{this.execute()})):Promise.resolve().then((()=>{this.execute()}))}execute(){if(!this.started)return;this.started=!1;const t=this.callback;this.callback=null,"function"==typeof t&&t.call(this)}cancel(){this.started=!1,this.callback=null}}const a=new WeakMap,c={isObject:n,merge:l,hasOwn:function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},uid:function(){let t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:8;const e="0123456789abcdefghijklmnopqrstuvwxyz";let i=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"";for(;t--;)i+=e[36*Math.random()|0];return i},isNum:function(t){if("number"!=typeof t||isNaN(t))return!1;return(e=t)!==Number.MAX_VALUE&&e!==Number.MIN_VALUE&&e!==Number.NEGATIVE_INFINITY&&e!==Number.POSITIVE_INFINITY;var e},toNum:function(t,e){return"number"!=typeof t&&(t=parseFloat(t)),isNaN(t)&&(t=0),e&&!Number.isInteger(t)&&(t=Math.round(t)),t},convertNum:function(t){if("string"==typeof t){if(/^[-+]?\d+(\.\d+)?$/gi.test(t))return parseFloat(t)}return t},clamp:function(t,e,i){return Math.max(Math.min(t,i),e)},per:function(t){return t=c.toNum(t),t=c.clamp(t,0,1)},replace:function(t,e){return t=`${t}`,e?t=t.replace(/\{([^}]+)\}/g,(function(t,i){return c.hasOwn(e,i)?e[i]:t})):t},isArray:function(t){return!!(t&&t instanceof Array)},toList:function(t){return t instanceof Array?t:void 0===t?[]:"string"==typeof t?[t]:t&&c.hasOwn(t,"length")?Array.from(t):[t]},isList:function(t){return!!(c.isArray(t)&&t.length>0)},inList:function(t,e){if(!c.isList(e))return!1;for(let i=0,o=e.length;i{if(!c.isList(t))return;let n=0;const s=t.length;for(;nt.startsWith(e))).forEach((e=>{t[e]=null}))},hasShiftKey:function(t){let e=!1;return t&&(e=t.shiftKey),e},isTouchDevice:function(){return"ontouchstart"in window||navigator.maxTouchPoints>0||navigator.msMaxTouchPoints>0},contains:function(t,e){if(!t||!e)return!1;if(t===e)return!0;if("function"==typeof t.contains)return t.contains(e);let i=e.parentNode;for(;i;){if(i===t)return!0;i=i.parentNode}return!1},isNarrowCharacter:function(t){const e=t.codePointAt(0);return e>=32&&e<=126||162===e||163===e||165===e||166===e||172===e||175===e||8361===e||e>=10214&&e<=10221||10629===e||10630===e||e>=65377&&e<=65470||e>=65474&&e<=65479||e>=65482&&e<=65487||e>=65490&&e<=65495||e>=65498&&e<=65500||e>=65512&&e<=65518},getCharLen:function(t){let e=0;if(!t)return e;for(const i of String(t))e+=c.isNarrowCharacter(i)?1:2;return e},pascalToKebabCase:function(t){return`${t}`.trim().replace(/([a-z])([A-Z])/g,"$1-$2").replace(/\W/g,(t=>/[À-ž]/.test(t)?t:"-")).replace(/^-+|-+$/g,"").replace(/-{2,}/g,"-").toLowerCase()},classMap:function(t){if("string"==typeof t)return t.trim();if(Array.isArray(t)){let e=t.filter((t=>t));return e=e.map((t=>t&&"object"==typeof t?c.classMap(t):String(t).trim())),e=e.filter((t=>t)),e=Array.from(new Set(e)),e.join(" ")}if(t&&"object"==typeof t){const e=[];return Object.keys(t).forEach((i=>{t[i]&&e.push(i)})),e.join(" ")}return""},styleMap:function(t){if("string"==typeof t)return t.trim();if(Array.isArray(t)){let e=t.filter((t=>t));return e=e.map((t=>{const e=String(t).trim();return e?-1===e.indexOf(":")?"":e.endsWith(";")?e:`${e};`:""})),e=e.filter((t=>t)),e=Array.from(new Set(e)),e.join(" ")}if(t&&"object"==typeof t){const e=[];return Object.keys(t).forEach((i=>{const o=t[i];if(o||0===o){const t=String(o).trim();t&&e.push(`${c.pascalToKebabCase(i)}: ${t};`)}})),e.join(" ")}return""},getInstance:function(t){if(t){const e=document.getElementById(t);if(e)return a.get(e)}},setInstance:function(t,e){t&&a.set(t,e)},bindEvents:function(t,e){t&&(c.unbindEvents(t),Object.keys(t).forEach((i=>{const o=t[i];o.target=o.target||e,o.target.addEventListener(i,o.handler,o.options)})))},unbindEvents:function(t){t&&Object.keys(t).forEach((e=>{const i=t[e];i.target&&i.target.removeEventListener(e,i.handler,i.options)}))},preventDefault:function(t){t&&"function"==typeof t.preventDefault&&t.cancelable&&t.preventDefault()},debounce:function(t){let e,i=arguments.length>1&&void 0!==arguments[1]?arguments[1]:100;const o=function(){clearTimeout(e),e=setTimeout((()=>{t.apply(this,arguments)}),i)};return o.cancel=()=>{clearTimeout(e)},o},throttle:function(t){let e,i=arguments.length>1&&void 0!==arguments[1]?arguments[1]:100,o=0;const n=function(){const n=Date.now();if(n>o+i)return clearTimeout(e),o=n,void t.apply(this,arguments);clearTimeout(e),e=setTimeout((()=>{o=n,t.apply(this,arguments)}),i)};return n.cancel=()=>{clearTimeout(e),o=0},n},microtask:function(t){const e=new h,i=function(){e.start((()=>{t.apply(this,arguments)}))};return i.cancel=()=>{e.cancel()},i},nextTick:function(t){"function"==typeof window.queueMicrotask?window.queueMicrotask((()=>{t()})):Promise.resolve().then((()=>{t()}))},cancelAsync:function(t){t&&(Object.keys(t).filter((e=>e.startsWith("async")&&"function"==typeof t[e])).forEach((e=>{const i=t[e];"function"==typeof i.cancel&&(i.cancel(),t[e]=null)})),Object.keys(t).filter((t=>t.startsWith("timeout"))).forEach((e=>{clearTimeout(t[e])})))}},d=c,u={animationIterationCount:!0,columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},g=function(t){return null!==t&&1===t.nodeType},f=function(t){let e=t.ownerDocument.defaultView;return e&&e.opener||(e=window),e.getComputedStyle(t)},p={},m=function(t,e){return null!=(i=t)&&i===i.window?t[`inner${e}`]:(function(t){return null!==t&&9===t.nodeType}(t)&&(t=t.body),t[`client${e}`]);var i},b=function(t){return this.list=[],t?this.create(t):this};function w(t){return new b(t)}b.prototype={constructor:b,Query:"Query",list:[],create:function(t){return t instanceof b?t:"string"==typeof t?this.createFromString(t):((t.nodeType||t===window)&&(this.list=[t]),this)},createFromString:function(t){if("<"===(t=t.trim())[0]&&">"===t[t.length-1]&&t.length>=3)this.parseHTML(t);else{const e=document.querySelectorAll(t);for(let t=0,i=e.length;t{this.css(e,t[e])}))}var i;return this.each((function(i){let o=e;"number"!=typeof o||u[t]||(o+="px"),i.style[t]=o})),this},attr:function(t,e){if(!t)return this;if(1===arguments.length){if("object"==typeof t)return Object.keys(t).forEach((e=>{this.attr(e,t[e])})),this;const e=this.get(0);return e?e.getAttribute(t):void 0}return this.each((function(i){i.setAttribute(t,e)})),this},removeAttr:function(t){return t?(this.each((function(e){e.hasAttribute(t)&&e.removeAttribute(t)})),this):this},removeClass:function(t){if(!arguments.length)return this.each((function(t){t.className=""})),this;if(!t||"string"!=typeof t)return this;const e=t.split(" ");return this.each((function(t){e.forEach((function(e){e&&t.classList.remove(e)}))})),this},addClass:function(t){if(!t||"string"!=typeof t)return this;const e=t.split(" ");return this.each((function(t){e.forEach((function(e){e&&t.classList.add(e)}))})),this},hasClass:function(t){if(!t||"string"!=typeof t)return!1;let e=!1;return this.each((function(i){if(i.classList.contains(t))return e=!0,!1})),e},show:function(){return this.each((function(t){if(!g(t))return;const e=function(t){if(!p[t]){const e=document.createElement(t);document.body.appendChild(e);const i=f(e).display;e.parentNode.removeChild(e),p[t]=i}return p[t]}(t.nodeName);t.style.display=e})),this},hide:function(){return this.each((function(t){if(!g(t))return;"none"!==t.style.display&&(t.style.display="none")})),this},click:function(){const t=this.get(0);return t&&"function"==typeof t.click&&t.click(),this},offset:function(){const t={left:0,top:0},e=this.get(0);if(e){const i=e.getBoundingClientRect();t.left=i.left+window.scrollX,t.top=i.top+window.scrollY}return t},clone:function(){const t=new b;return this.each((function(e){if(e&&e.cloneNode){const i=e.cloneNode(!0);t.add(i)}})),t},children:function(){const t=new b;return this.each((function(e){let i=e.firstChild;for(;i;)t.add(i),i=i.nextSibling})),t},parent:function(){const t=this.get(0);return t?new b(t.parentNode):new b},is:function(t){if(!t)return!1;const e=t.split(",");let i=!0;return this.each((function(t){if(!t.nodeName)return i=!1,!1;const o=t.nodeName.toLowerCase();return d.inList(o,e)?void 0:(i=!1,!1)})),i}},Object.defineProperty(b.prototype,"length",{get:function(){return this.list.length}});const v={createCache:function(){this.headerCache=new Map,this.rowsCache=new Map,this.dataCache=new WeakMap,this.cellResizeObserver=this.createResizeObserver((t=>{this.cellResizeHandler(t)}))},setHeaderCache:function(t,e){this.headerCache.set(t,e)},getHeaderCache:function(t){return this.headerCache.get(t)},clearHeaderCache:function(){this.headerCache.clear()},setRowCache:function(t,e){this.rowsCache.set(t,{rowNodes:e,cellNodes:new Map,observerNodes:new Map})},getRowCache:function(t){return this.rowsCache.get(t)},deleteRowCache:function(t){const e=this.getRowCache(t);if(!e)return;this.rowsCache.delete(t);const i=e.observerNodes;i&&i.forEach((t=>{t&&this.cellResizeObserver.unobserve(t)}));const o=e.rowNodes;o&&o.each((t=>{this.removeNode(t)}))},deleteCellCache:function(t,e,i){if(i){const e=i.get(t);e&&this.cellResizeObserver.unobserve(e),i.delete(t)}e&&(this.removeNode(e.get(t)),e.delete(t))},getRowNodesByIndex:function(t){const e=this.getRowCache(t);if(e)return e.rowNodes},getCellNodeByIndex:function(t,e){const i=this.getRowCache(t);if(i)return i.cellNodes.get(e)},forEachRowsCache:function(t){this.rowsCache.forEach(((e,i)=>{t.call(this,i,e.rowNodes,e.cellNodes,e.observerNodes)}))},updateRowCacheTopOffset:function(){const t=this.frozenInfo.row;this.forEachRowsCache(((e,i)=>{if(!(e<=t)&&i){const t=this.getViewRowItem(e),o=this.getViewRowTop(t);i.css("top",o)}}))},updateRowCacheTopAndHeight:function(){this.forEachRowsCache(((t,e)=>{if(e){const i=this.getViewRowItem(t),o=this.getViewRowTop(i),n=this.getRowHeight(i);e.css({top:o,height:n,"line-height":n})}}))},setNodeDataCache:function(t,e){if(t)return this.dataCache.set(t,e)},getNodeDataCache:function(t){if(t)return this.dataCache.get(t)},removeCache:function(){this.headerCache=null,this.rowsCache=null,this.dataCache=null,this.cellResizeObserver&&(this.cellResizeObserver.disconnect(),this.cellResizeObserver=null)}},H=["onUpdated","onFirstUpdated","onHeaderUpdated","onSort","onColumnAdded","onColumnRemoved","onColumnWidthChanged","onRowAdded","onRowRemoved","onRowExpanded","onRowCollapsed","onRowSubsRequest","onRowDragged","onRowDropped","onRowMoved","onRowMouseEnter","onRowMouseLeave","onSelectChanged","onCellUpdated","onCellMouseEnter","onCellMouseLeave","onClick","onDblClick","onContextMenu","onMouseOver","onMouseOut","onTouchStart","onTouchMove","onTouchEnd","onScroll","onScrollStateChanged","onMouseWheel","onResize","onLayout","onKeyDown","onDestroy"],C={};H.forEach((t=>{C[t]=t}));const y=C,R={renderCells:function(t,e){t.forEach((t=>{this.drawRowCells(t,e)}))},getCellValue:function(t,e){return t[e.id]},renderCell:function(t){const{rowItem:e,columnItem:i,cellNode:o,observerNode:n}=t,s=this.getCellValue(e,i);let r=s;this.nullFormatter&&(r=this.nullFormatter.call(this,r,e,i,o,n));const l=e.tg_formatter||i.tg_formatter;"function"==typeof l&&(r=l.call(this,r,e,i,o,n));const h=n||o;this.renderNodeContent(h,r);const{highlightKey:a}=this.options.highlightKeywords;e[a+i.id]&&this.renderSettings.highlightCells.push(o),this.trigger(y.onCellUpdated,{value:s,rowItem:e,columnItem:i,node:o})},getPreRenderColumnList:function(t,e){const i=[];if(!e.length)return i;for(let o=0,n=e.length;o{this.createCellNode(t,e)}))},getCellClass:function(t,e,i){const o=e.tg_view_index,n=["tg-cell"];return i&&n.push("tg-cell-observer"),n.push(`tg-c-${o}`),e.align&&n.push(`tg-align-${e.align}`),0===e.tg_list_index&&n.push("tg-list-first"),e.tg_list_last&&n.push("tg-list-last"),n.push(d.classMap(e.classMap)),n.push(d.classMap(t[`${e.id}ClassMap`])),d.classMap(n)},cellResizeObserverHandler:function(t,e){const i=this.options.cellResizeObserver;if("function"==typeof i)return i.apply(this,[t,e])},cellResizeHandler:function(t){const e=new Map;t.forEach((t=>{const{target:i}=t,o=this.getNodeDataCache(i.parentNode);if(!o)return;const{row:n,rowItem:s}=o;e.set(n,s)}));let i=!1;e.forEach(((t,e)=>{const o=this.getRowCache(e);if(!o)return;const n=o.observerNodes;if(!n)return;const{rowHeight:s,rowMinHeight:r}=this.options;let l=Math.max(r||s,1);n.forEach((t=>{if(t){const e=t.clientHeight+11;e>l&&(l=e)}}));this.getRowHeight(t)!==l&&(t.tg_height=l,i=!0)})),i&&this.render("rows_cache")},createCellNode:function(t,e){const i=this.getRowCache(t);if(!i)return;const o=this.getViewRowItem(t),n=this.getViewColumnItem(e);if(!o||!n)return;const s=this.cellResizeObserverHandler(o,n),r=document.createElement("div");r.setAttribute("column",e);const l=this.getCellClass(o,n,s);r.className=l;const h=d.styleMap(n.styleMap)+d.styleMap(o[`${n.id}StyleMap`]);h&&(r.style.cssText=h);const a=i.rowNodes,c=n.tg_frozen,u=this.getCellRowNode(a,c);let g;this.appendNode(u,r),s&&(g=document.createElement("div"),g.className="tg-observer",r.appendChild(g),this.cellResizeObserver.observe(g),i.observerNodes.set(e,g));const f={row:t,rowItem:o,rowNode:u,column:e,columnItem:n,cellNode:r,observerNode:g};this.setNodeDataCache(r,f),i.cellNodes.set(e,r),this.renderCell(f)},getCellRowNode:function(t,e){const i=t.get(0);if(this.frozenInfo.columns){const o=t.get(1);return this.frozenInfo.right?e?o:i:e?i:o}return i}},S={addColumn:function(t,e,i){let o=!(arguments.length>3&&void 0!==arguments[3])||arguments[3];const n=this.getToBeAddedItemList(t);if(!n.length)return!1;let s;if(null!=e&&(s=this.getColumnItem(e),!s))return!1;const r=this.getToBeAddedParentSubs(s,this.columns),l=[this.getToBeAddedPositionIndex(i,r),0].concat(n);r.splice.apply(r,l),this.onNextUpdated((function(){this.trigger(y.onColumnAdded,n)}));const h={type:"columns"};return o&&(h.scrollColumn=n[n.length-1]),this.render(h),!0},deleteColumn:function(t){const e=this.toColumnItemList(t,(t=>!t.private));return!!e.length&&(this.removeColumnsHandler(e),this.onNextUpdated((function(){this.trigger(y.onColumnRemoved,e)})),this.render("columns"),!0)},removeColumnsHandler:function(t){const e=[].concat(t);e.sort((function(t,e){return e.tg_index-t.tg_index})),e.forEach((t=>{let e;if(t===this.sortColumn&&this.removeSortColumn(),t.tg_parent)e=t.tg_parent.subs,e.splice(t.tg_sub_index,1);else{e=this.columns;const i=e.findIndex((e=>e===t));-1!==i&&e.splice(i,1)}!e.length&&t.tg_parent&&(t.tg_parent.subs=null)}))}},T={setColumnWidth:function(t,e){return this.updateColumnWidth(t,e)?(this.resize(),this):this},updateColumnWidth:function(t,e){const i=this.getColumnItem(t);return!!i&&(!!d.isNum(e)&&(e=Math.round(e),e=Math.max(0,e),i.tg_width!==e&&(i.width=e,i.minWidth=Math.min(i.minWidth,e),i.maxWidth=Math.max(i.maxWidth,e),this.updateViewColumnWidth(i),!0)))},showColumn:function(t){return this.updateColumnsInvisible(this.toColumnItemList(t),!1)},hideColumn:function(t){return this.updateColumnsInvisible(this.toColumnItemList(t),!0)},updateColumnsInvisible:function(t,e){if(!t.length)return!1;const i=[];return t.forEach((t=>{t.invisible!==e&&(t.invisible=e,t.tg_invisible=e,i.push(t))})),!!i.length&&(this.render("columns"),!0)}},E={showColumnLine:function(t){t&&(this.$columnLineContainer.show(),this.renderColumnLine(t))},hideColumnLine:function(){this.previousColumnLineActive||this.$columnLineContainer.hide()},setColumnLineActive:function(t){this.setTextSelectable(!t),this.rowHoverable=!t,t!==this.previousColumnLineActive&&(this.previousColumnLineActive=t,t?this.$columnLineItem.addClass("tg-active"):this.$columnLineItem.removeClass("tg-active"))},getColumnLineLeft:function(t){let e=t.tg_left;return t.tg_frozen||(e-=this.scrollLeft),this.frozenInfo.right&&(t.tg_frozen?e=t.tg_left+this.paneWidthL:e-=this.columnsWidthR),e},renderColumnLine:function(t){const e=this.getHeaderItemNode(t).offsetTop,i=t.tg_width,o=this.getColumnLineLeft(t);this.$columnLineItemL.css({top:e,left:o}),this.$columnLineItemR.css({top:e,left:o+i-1}),this.frozenInfo.right||(this.frozenInfo.columns&&!t.tg_frozen&&o{this.renderColumnLine(e.columnItem)})),this.resize()},columnWidthTouchStartHandler:function(t,e){d.preventDefault(e.e);const i=e.columnItem;this.showColumnLine(i),this.setColumnLineActive(!0),e.index=i.tg_index;const o=this.getColumnHeaderNode(i);e.width=o.clientWidth},columnWidthTouchMoveHandler:function(t,e){d.preventDefault(e.e);const i=e.columnItem;let o=e.width+e.offsetX;o=d.clamp(o,i.minWidth,i.maxWidth),i.tg_width!==o&&(i.width=o,this.updateViewColumnWidth(i),this.renderColumnLine(i))},columnWidthTouchEndHandler:function(t,e){d.preventDefault(e.e),this.setColumnLineActive(!1),this.hideColumnLine(),this.resize()}},I={getColumnItem:function(t){return d.isNum(t)?(t<0&&(t=this.columnsInfo.length+t),this.columnsInfo.indexCache[t]):t?d.isNum(t.tg_index)?t:this.getColumnItemById(t.id||t):void 0},getColumnItemById:function(t){return this.getColumnItemBy("id",t)},getColumnItemBy:function(t,e){if(void 0!==e)return this.columnsInfo.indexCache.find((i=>i[t]===e))},getColumnsLength:function(t){return t?this.columnsInfo.length:this.viewColumns.length},getViewColumnItem:function(t){return this.viewAllColumns[t]},isColumnSortable:function(t){return!!t&&(!t.tg_group&&(!(!t.name||!t.id)&&this.isSortable(t)))},isColumnResizable:function(t){return!!t&&(!t.tg_group&&(!d.hasOwn(t,"resizable")||Boolean(t.resizable)))},updateViewColumnWidth:function(t){return t.tg_width=t.width,this.updateColumnHeaderSize(t),this.updateTotalColumnsWidth(),this.updateHeaderLayerHeight(),this.cssRulesInvalid=!0,this.resizeBodyHandler(),this.trigger(y.onColumnWidthChanged,t),!0},updateTotalColumnsWidth:function(){this.blankColumn.tg_width=0;const t=this.viewColumns;let e=0,i=0;const o=this.frozenInfo.columns,n=t.length;let s=0;for(let r=0;r0&&(s+=l,o&&r>=o?i+=l:e+=l)}if(this.frozenInfo.right){const t=e;e=i,i=t}this.columnsWidthL=e,this.columnsWidthR=i,this.columnsWidth=e+i},updateColumnHeaderSize:function(t){this.updateColumnHeaderWidth(t),this.updateColumnHeaderHeight(t,!0),this.updateColumnGroupWidth(t)},updateColumnHeaderWidth:function(t){const e=this.getColumnHeaderNode(t);if(!e)return;const i=t.tg_width;this.isInvisible(t)||i<=0?e.style.display="none":(e.style.display="",e.style.width=`${i}px`)},updateColumnHeaderHeight:function(t,e){if(t.tg_height=0,t.tg_width<=0)return;if(this.isInvisible(t))return;e&&(t.tg_element_height=0);const i=t.tg_element_height;if(i)return void(t.tg_height=i);const o=this.getColumnHeaderHeight(t);t.tg_height=o,t.tg_element_height=o},getColumnHeaderHeight:function(t){const e=this.getColumnHeaderNode(t);return e?e.clientHeight:0},updateColumnGroupWidth:function(t){const e=t.tg_parent;if(!e)return;const i=this.getColumnGroupWidth(e);e.tg_width!==i&&(e.tg_width=i,this.updateColumnHeaderSize(e))},getColumnGroupWidth:function(t){if(this.isInvisible(t))return 0;let e=0;return t.subs&&t.subs.forEach((t=>{this.isInvisible(t)||d.isNum(t.tg_width)&&(e+=t.tg_width)})),e}},L={initTreeInfo:function(t,e){const i=[];let o=!1,n=0,s=0;const r=function(t,r,l){(t=>{t.invisible?t.tg_invisible=!0:t.tg_invisible&&(t.tg_invisible=!1)})(t),((t,i)=>{if(e>=0&&!t.tg_invisible)return t.tg_frozen=!0,void(e-=1);t.tg_frozen&&(t.tg_frozen=!1)})(t),(t=>{if(d.hasOwn(t,"subs")){if(Array.isArray(t.subs))return o=!0,t.tg_group=!0,void(t.tg_subs_length=t.subs.length);t.subs=null}t.tg_group&&(t.tg_group=!1)})(t),((t,e)=>{t.tg_parent=e;let i=0;e&&(i=e.tg_level+1,i>n&&(n=i)),t.tg_level=i})(t,l),t.tg_index=s,t.tg_sub_index=r,i.push(t),s+=1},l=function(t,e){let i=0;const o=t.length;for(;i{if(!this.isInvisible(e))return this.isRowSelectable(e)?t(e,i,o):void 0})),this},toRowItemList:function(t,e){let i=d.toList(t).map((t=>this.getRowItem(t))).filter((t=>t));return"function"==typeof e&&(i=i.filter(e)),i},toColumnItemList:function(t,e){let i=d.toList(t).map((t=>this.getColumnItem(t))).filter((t=>t));return"function"==typeof e&&(i=i.filter(e)),i},isRowLeaf:function(t){return!!t&&("blank"!==t.formatter&&(!t.tg_frozen&&!t.tg_group))},isRowSelectable:function(t){return!!t&&(d.hasOwn(t,"selectable")?Boolean(t.selectable):this.isRowLeaf(t))},isEmptyGroup:function(t){return!(!t||!t.tg_group||0!==t.tg_subs_length)},isInvisible:function(t){return!!t&&(!(!t.tg_filtered&&!t.tg_invisible)||!!this.isInvisible(t.tg_parent))},isSortable:function(t){return!!t&&(!d.hasOwn(t,"sortable")||Boolean(t.sortable))},isCollapsedChanged:function(t,e){return Boolean(t.collapsed)!==e},isSelectedChanged:function(t,e){return Boolean(t.selected)!==e}},x={updateCssRules:function(){this.cssRulesInvalid&&(this.cssRulesInvalid=!1,this.initCssRules(),this.updateColumnsCssRules(),this.updateHeadersCssRules(),this.updateStyleElement())},initCssRules:function(){this.removeCssRules(),this.cssList={},this.cssDisplayCache={};const t=this.getRowHeight(),e=this.createCssRule(".tg-row");e.height=`${t}px`,e["line-height"]=`${t}px`},resetCssDisplay:function(t){if(this.cssDisplayCache){t=t||"";for(const e in this.cssDisplayCache)if(d.hasOwn(this.cssDisplayCache,e)){this.cssDisplayCache[e].style.display=t}}},updateColumnsCssRules:function(){const t=this.viewColumns,e=this.frozenInfo.column,i={};let o=0;for(let n=0,s=t.length;n=0;i--){const e=this.headerLayerHeight[i],o=this.createCssRule(`.tg-h-${i}`);o.bottom=`${t}px`,o.height=`${e}px`,t+=e}this.getLayerCombinations(e).forEach((t=>{const e=this.createCssRule(`.tg-h-${t}`);let i=0;t.split("").forEach((t=>{i+=this.headerLayerHeight[t]||0})),e.height=`${i}px`}))},getLayerCombinations:function(t){let e="";for(;t>=0;)e+=t,t--;if(e.length<2)return[];const i=[],o=function(t,e){const n=t.length;let s=e+2;for(;s<=n;){const o=t.substring(e,s);i.push(o),s++}e=i){let t="Possible Event memory leak detected. ";return t+=`More than ${i} (max limit) listeners added. `,t+="Use setMaxListeners(n) to increase limit.",void console.warn(t,e)}t.events.push(e)},addEvents:function(t,e,i){e.forEach((function(e){const o=e.type;t[o]||(t[o]={events:[]});if("function"!=typeof e.handler)return;const n=t[o];N.addEvent(n,e,i)}))},removeEventByNamespace:function(t,e){Object.keys(t).forEach((function(i){const o=t[i],n=[];o.events.forEach((function(t){t&&t.namespace!==e&&n.push(t)})),o.events=n}))},removeEventByHandler:function(t,e,i){const o=t[e];if(!o)return;const n=[];o.events.forEach((function(t){t&&t.handler!==i&&n.push(t)})),o.events=n},removeEventByType:function(t,e){const i=t[e];i&&(i.events=[])},removeEvent:function(t,e){const i=e.type,o=e.namespace;if(!i&&o)return void N.removeEventByNamespace(t,o);const n=e.handler;"function"!=typeof n?N.removeEventByType(t,i):N.removeEventByHandler(t,i,n)},removeEvents:function(t,e){e.forEach((function(e){N.removeEvent(t,e)}))},removeAllEvents:function(t){Object.keys(t).forEach((function(e){N.removeEventByType(t,e)}))},sendEventList:function(t,e,i,o){const n=e.events;for(let e=0;e!t.onceCalled))},sendEvent:function(t,e,i,o){const n=e[i];if(!n)return;const s=new P({type:i,target:t,currentTarget:t,data:o});N.sendEventList(t,n,s,o)}},_=N;class k{maxListeners=10;setMaxListeners(t){this.maxListeners=Number(t)||10}getMaxListeners(){return this.maxListeners}getEventListeners(){return this.eventListeners||(this.eventListeners={}),this.eventListeners}delEventListeners(){this.eventListeners=null}bind(t,e,i){const o=_.getEventList(this,t,e,i);if(!o.length)return this;const n=this.getEventListeners();return _.addEvents(n,o,this.maxListeners),this}once(t,e){return this.bind(t,e,{once:!0})}unbind(t,e,i){const o=this.getEventListeners();if(!arguments.length)return _.removeAllEvents(o),this;const n=_.getEventList(this,t,e,i);return n.length?(_.removeEvents(o,n),this):this}trigger(t,e){const i=this.getEventListeners();return _.sendEvent(this,i,t,e),this}}const V={DRAG_START:"drag_start",DRAG_MOVE:"drag_move",DRAG_END:"drag_end"};class O extends k{static EVENT=V;generateOptions(t){return d.merge({type:"mouse",startX:0,startY:0,previousX:0,previousY:0,currentX:0,currentY:0,moveX:0,moveY:0,offsetX:0,offsetY:0,changed:!1},t)}start(t,e){t&&(this.unbindEvents(),this.bindEvents(),this.options=this.generateOptions(e),this.startHandler(t))}bindEvents(){this.windowEvents={mousemove:{handler:t=>{this.iframeHandler(t),this.mouseMoveHandler(t)},options:!0},mouseup:{handler:t=>{this.mouseUpHandler(t)},options:{once:!0}}},d.bindEvents(this.windowEvents,window)}unbindEvents(){d.unbindEvents(this.windowEvents),this.windowEvents=null,this.previousIframe&&(this.previousIframe.classList.remove("tg-pointer-events-none"),this.previousIframe=null)}iframeHandler(t){const e=t.target;"IFRAME"===e.nodeName&&e!==this.previousIframe&&(this.previousIframe&&this.previousIframe.classList.remove("tg-pointer-events-none"),e.classList.add("tg-pointer-events-none"),this.previousIframe=e)}startHandler(t){const e=this.options;e.e=t,e.startX=t.pageX,e.startY=t.pageY,e.currentX=e.startX,e.currentY=e.startY,this.hasMoved=!1}mouseMoveHandler(t){d.preventDefault(t);const e=this.options;e.e=t,e.previousX=e.currentX,e.previousY=e.currentY,e.currentX=t.pageX,e.currentY=t.pageY,e.moveX=e.currentX-e.previousX,e.moveY=e.currentY-e.previousY,e.offsetX=e.currentX-e.startX,e.offsetY=e.currentY-e.startY,e.changed=!(0===e.offsetX&&0===e.offsetY),this.hasMoved?this.trigger(V.DRAG_MOVE,e):(this.hasMoved=!0,this.trigger(V.DRAG_START,e))}mouseUpHandler(t){this.unbindEvents();const e=this.options;this.hasMoved&&(e.e=t,d.preventDefault(t),this.trigger(V.DRAG_END,e))}destroy(){this.unbindEvents(),this.unbind()}}const $={Linear:{None:function(t){return t}}},D={MOTION_START:"motion_start",MOTION_MOVE:"motion_move",MOTION_END:"motion_end",MOTION_STOP:"motion_stop"};class B extends k{static EVENT=D;constructor(t){super(),this.constructorOptions=t,this.stopped=!0}generateOptions(t){return d.merge({easing:null,duration:100,from:0,till:1,data:0},this.constructorOptions,t)}stop(){return this.stopped||(this.stopped=!0,this.cancelAnimationFrame(),this.trigger(D.MOTION_STOP,this.data)),this}start(t){return this.stop(),this.stopped=!1,this.options=this.generateOptions(t),this.initCalculation(),this.data=this.calculateHandler(0),this.trigger(D.MOTION_START,this.data),this.stopped||(this.time=Date.now(),this.requestAnimationFrame(this.moveHandler)),this}requestAnimationFrame(t){this.requestId=window.requestAnimationFrame((()=>{t.apply(this)}))}cancelAnimationFrame(){window.cancelAnimationFrame(this.requestId)}getEasing(t){return"function"!=typeof t&&(t=d.getValue($,t,$.Linear.None)),t}moveHandler(){const t=Date.now()-this.time,e=this.duration;if(t{o[n]=this.calculateNumber(t,e[n],i[n])})),o):(this.calculateKeys=[],Object.keys(e).forEach((n=>{const s=e[n],r=i[n];d.isNum(s)&&d.isNum(r)&&(o[n]=this.calculateNumber(t,s,r),this.calculateKeys.push(n))})),o)}calculateNumber(t,e,i){return(i-e)*t+e}calculateNone(t,e,i){return e}destroy(){this.stop(),this.unbind()}}const A={TOUCH_START:"touch_start",TOUCH_MOVE:"touch_move",TOUCH_END:"touch_end",TOUCH_INERTIA:"touch_inertia"};class W extends k{static EVENT=A;generateOptions(t){return d.merge({type:"touch",startX:0,startY:0,previousX:0,previousY:0,currentX:0,currentY:0,moveX:0,moveY:0,offsetX:0,offsetY:0,changed:!1,touchLength:0,direction:"",inertia:!1,inertiaTime:200},t)}start(t,e){t&&(this.unbindEvents(),this.bindEvents(),this.options=this.generateOptions(e),this.startHandler(t))}bindEvents(){this.touchEvents={touchmove:{handler:t=>{this.touchMoveHandler(t)},options:{passive:!1}},touchend:{handler:t=>{this.touchEndHandler(t)},options:{passive:!1,once:!0}},touchcancel:{handler:t=>{this.touchCancelHandler(t)},options:{passive:!1,once:!0}}},d.bindEvents(this.touchEvents,document.body)}unbindEvents(){this.motionStop(),d.unbindEvents(this.touchEvents),this.touchEvents=null}startHandler(t){this.trackingPoints=[];const e=t.touches,i=e[0];if(!i)return;const o=this.options;o.e=t,o.startX=i.clientX,o.startY=i.clientY,o.currentX=o.startX,o.currentY=o.startY,o.touchLength=e.length,this.addTrackingPoint(o),this.trigger(A.TOUCH_START,o)}touchMoveHandler(t){const e=t.touches,i=e[0];if(!i)return;const o=this.options;o.e=t,o.previousX=o.currentX,o.previousY=o.currentY,o.currentX=i.clientX,o.currentY=i.clientY,o.moveX=o.currentX-o.previousX,o.moveY=o.currentY-o.previousY,o.offsetX=o.currentX-o.startX,o.offsetY=o.currentY-o.startY,o.changed=!(0===o.offsetX&&0===o.offsetY),o.touchLength=e.length,o.direction=this.getDirection(o),this.addTrackingPoint(o),this.trigger(A.TOUCH_MOVE,o)}touchEndHandler(t){this.unbindEvents();const e=this.options;e.e=t,this.trigger(A.TOUCH_END,e);const i=t.changedTouches[0];if(!i)return;const o=t.touches;e.touchLength=o.length,e.touchLength>0||(e.currentX=i.clientX,e.currentY=i.clientY,this.addTrackingPoint(e),this.motionStart())}touchCancelHandler(t){this.unbindEvents(),this.trigger(A.TOUCH_END,this.options)}getMotionInfo(){const t=this.trackingPoints;if(t.length<2)return;if(this.filterTrackingPoints(t),t.length<2)return;const e=t[0],i=t[t.length-1],o=i.t-e.t;if(o<=0)return;let n=i.x-e.x,s=i.y-e.y;const r=Math.abs(n),l=Math.abs(s);r>l?s=0:n=0;return{offsetDistance:Math.max(r,l),offsetTime:o,offsetX:n,offsetY:s}}motionStart(){const t=this.options;if(!t.inertia)return;const e=this.getMotionInfo();if(!e)return;const i=500*e.offsetDistance/50,o=d.clamp(i,20,2e3),n={x:20*(e.offsetX/e.offsetTime),y:20*(e.offsetY/e.offsetTime)};this.motion=new B,this.motion.bind(B.EVENT.MOTION_MOVE,((e,i)=>{t.touchInertiaX=i.x,t.touchInertiaY=i.y,this.trigger(A.TOUCH_INERTIA,t)})),this.motion.start({duration:o,from:n,till:{x:0,y:0}})}motionStop(){this.motion&&(this.motion.destroy(),this.motion=null)}getDirection(t){const i=t.offsetX,o=t.offsetY,n=Math.abs(i),s=Math.abs(o);if(n0)return e.UP;if(o<0)return e.DOWN}if(n>s){if(i>0)return e.LEFT;if(i<0)return e.RIGHT}return""}filterTrackingPoints(t){t.reverse();const e=t.length,i=Date.now(),o=this.options.inertiaTime;for(let n=0;no){t.length=n;break}t.reverse()}addTrackingPoint(t){if(!t.inertia)return;const e=t.currentX,i=t.currentY,o=Date.now(),n=this.trackingPoints;n.push({x:e,y:i,t:o}),n.length>100&&this.filterTrackingPoints(n)}destroy(){this.unbindEvents(),this.unbind()}}const F={getAllEvents:function(){return[].concat(H)},bindEvents:function(){this.unbindEvents(),this.containerEvents={mousedown:{handler:t=>{this.containerMouseDownHandler(t)},options:!0},mousemove:{handler:t=>{this.containerMouseMoveHandler(t)},options:!0},mouseover:{handler:t=>{this.containerMouseOverOutHandler(t,!0)},options:!0},mouseout:{handler:t=>{this.containerMouseOverOutHandler(t,!1)},options:!0},mouseenter:{handler:t=>{this.containerMouseEnterLeaveHandler(t,!0)},options:!0},mouseleave:{handler:t=>{this.containerMouseEnterLeaveHandler(t,!1)},options:!0},touchstart:{handler:t=>{this.containerTouchStartHandler(t)},options:{passive:!1}},touchmove:{handler:t=>{this.containerTouchMoveHandler(t)},options:{passive:!1}},touchend:{handler:t=>{this.containerTouchEndHandler(t)},options:{passive:!1}},touchcancel:{handler:t=>{this.containerTouchCancelHandler(t)},options:{passive:!1}},wheel:{handler:t=>{this.containerWheelHandler(t)},options:{passive:!1}},click:{handler:t=>{this.containerClickHandler(t)},options:!0},dblclick:{handler:t=>{this.containerDblClickHandler(t)},options:!0},contextmenu:{handler:t=>{this.containerContextMenuHandler(t)},options:!0},selectstart:{handler:t=>{this.containerSelectStartHandler(t)},options:!0},keydown:{handler:t=>{this.containerKeyDownHandler(t)},options:!0}},d.bindEvents(this.containerEvents,this.container),this.columnWidthDrag=new O,this.columnWidthDrag.bind(O.EVENT.DRAG_START,((t,e)=>{this.columnWidthDragStartHandler(t,e)})).bind(O.EVENT.DRAG_MOVE,((t,e)=>{this.columnWidthDragMoveHandler(t,e)})).bind(O.EVENT.DRAG_END,((t,e)=>{this.columnWidthDragEndHandler(t,e)})),this.columnWidthTouch=new W,this.columnWidthTouch.bind(W.EVENT.TOUCH_START,((t,e)=>{this.columnWidthTouchStartHandler(t,e)})).bind(W.EVENT.TOUCH_MOVE,((t,e)=>{this.columnWidthTouchMoveHandler(t,e)})).bind(W.EVENT.TOUCH_END,((t,e)=>{this.columnWidthTouchEndHandler(t,e)})),this.rowDrag=new O,this.rowDrag.bind(O.EVENT.DRAG_START,((t,e)=>{this.rowDragStartHandler(t,e)})).bind(O.EVENT.DRAG_MOVE,((t,e)=>{this.rowDragMoveHandler(t,e)})).bind(O.EVENT.DRAG_END,((t,e)=>{this.rowDragEndHandler(t,e)})),this.rowTouch=new W,this.rowTouch.bind(W.EVENT.TOUCH_START,((t,e)=>{this.rowDragStartHandler(t,e)})).bind(W.EVENT.TOUCH_MOVE,((t,e)=>{this.rowDragMoveHandler(t,e)})).bind(W.EVENT.TOUCH_END,((t,e)=>{this.rowDragEndHandler(t,e)})),this.scrollTouch=new W,this.scrollTouch.bind(W.EVENT.TOUCH_START,((t,e)=>{this.scrollTouchStartHandler(t,e)})).bind(W.EVENT.TOUCH_MOVE,((t,e)=>{this.scrollTouchMoveHandler(t,e)})).bind(W.EVENT.TOUCH_END,((t,e)=>{this.scrollTouchEndHandler(t,e)})).bind(W.EVENT.TOUCH_INERTIA,((t,e)=>{this.scrollTouchInertiaHandler(t,e)}))},isDefaultPrevented:function(t){if(t){if(t.defaultPrevented)return!0;if(t.e&&t.e.defaultPrevented)return!0}return!1},getEventClosestNode:function(t,e){if(t&&t!==this.container)return t.classList.contains(e)?t:this.getEventClosestNode(t.parentNode,e)},getEventClosestData:function(t){if(!t||t===this.container)return;const e=this.getNodeDataCache(t);return e||this.getEventClosestData(t.parentNode)},getEventData:function(t){const e=this.getEventClosestData(t.target);if(e)return e.e=t,e},getWheelDelta:function(t,e,i){let o=t.deltaX,n=t.deltaY;return d.isNum(o)||(o=d.toNum(t.wheelDeltaX)),d.isNum(n)||(n=d.toNum(t.wheelDeltaY||t.wheelDelta)),1===t.deltaMode?(n*=e,o*=e):2===t.deltaMode&&(n*=i,o*=i),{deltaX:o,deltaY:n}},columnResizingMouseDownHandler:function(t){const e=this.getEventData(t);e&&this.columnWidthDrag.start(t,{columnItem:e.columnItem})},columnResizingTouchStartHandler:function(t){const e=this.getEventData(t);e&&this.columnWidthTouch.start(t,{columnItem:e.columnItem})},columnResizingMouseEnterLeaveHandler:function(t,e){const i=this.getEventData(t);i&&(e?this.showColumnLine(i.columnItem):this.hideColumnLine())},rowDragMouseDownHandler:function(t){const e=this.getEventData(t);e&&this.rowDrag.start(t,{rowItem:e.rowItem})},rowDragTouchStartHandler:function(t){const e=this.getEventData(t);e&&(this.protectedItem=e,this.rowTouch.start(t,{rowItem:e.rowItem}))},scrollPaneTouchStartHandler:function(t){if(!this.hasHScroll&&!this.hasVScroll)return;const e=this.getEventData(t);this.protectedItem=e,this.scrollTouch.start(t,{inertia:!0})},sortHandler:function(t,e){const i=e.columnItem;if(!this.isColumnSortable(i))return;const o=this.getEventClosestNode(t.target,"tg-column-name"),n=this.getEventClosestNode(t.target,"tg-column-sort");(o||n)&&(this.trigger(y.onSort,e),this.isDefaultPrevented(e)||this.setSortColumn(i))},selectIconAllClickHandler:function(t){const e=w(t);let i=!1;(e.hasClass("tg-selected")||e.hasClass("tg-mixed"))&&(i=!0),i=!i,this.selectAll(i)},cellEnterLeaveHandler:function(t,e){const i=this.getEventData(t);i&&(e?this.trigger(y.onCellMouseEnter,i):this.trigger(y.onCellMouseLeave,i))},rowEnterLeaveHandler:function(t,e){const i=this.getEventData(t);if(i&&(e?this.trigger(y.onRowMouseEnter,i):this.trigger(y.onRowMouseLeave,i),!this.isDefaultPrevented(i)))return this.renderRowHover(i.rowItem,e),this},containerMouseDownHandler:function(t){if(this.getEventClosestNode(t.target,"tg-column-resizing"))this.columnResizingMouseDownHandler(t);else if(this.options.rowDragVisible){this.getEventClosestNode(t.target,"tg-row-drag-icon")&&this.rowDragMouseDownHandler(t)}},containerMouseMoveHandler:function(t){this.scrollbarFadeInOutHandler(t,!0)},containerMouseOverOutHandler:function(t,e){const i=this.getEventClosestNode(t.target,"tg-cell"),o=this.getEventClosestNode(t.target,"tg-header-item");if(i||o){const i=this.getEventData(t);if(!i)return;e?this.trigger(y.onMouseOver,i):this.trigger(y.onMouseOut,i)}},containerMouseEnterLeaveHandler:function(t,e){this.scrollbarFadeInOutHandler(t,e);if(w(t.target).hasClass("tg-column-resizing"))return void this.columnResizingMouseEnterLeaveHandler(t,e);if(w(t.target).hasClass("tg-cell"))return void this.cellEnterLeaveHandler(t,e);w(t.target).hasClass("tg-row")&&this.rowEnterLeaveHandler(t,e)},containerTouchStartHandler:function(t){this.scrollTouch.motionStop();if(this.getEventClosestNode(t.target,"tg-column-resizing"))return void this.columnResizingTouchStartHandler(t);if(this.options.rowDragVisible){if(this.getEventClosestNode(t.target,"tg-row-drag-icon"))return void this.rowDragTouchStartHandler(t)}const e=this.getEventData(t);e&&(this.trigger(y.onTouchStart,e),this.isDefaultPrevented(e))||this.scrollPaneTouchStartHandler(t)},containerTouchMoveHandler:function(t){const e=this.getEventData(t);e&&this.trigger(y.onTouchMove,e)},containerTouchEndHandler:function(t){const e=this.getEventData(t);e&&this.trigger(y.onTouchEnd,e)},containerTouchCancelHandler:function(t){this.trigger(y.onTouchEnd,{e:t})},containerWheelHandler:function(t){if(this.hasMask)return;const e=this.getRowHeight(),i=this.bodyHeight,o=this.getWheelDelta(t,e,i),n={e:t,deltaX:o.deltaX,deltaY:o.deltaY,delta:o};if(this.trigger(y.onMouseWheel,n),this.isDefaultPrevented(n))return;let s=!1;this.scrollPaneHidden&&(s=this.scrollPaneFrozen.setOffsetH(o.deltaX),o.deltaX=0);(this.scrollPane.mouseWheelHandler(o)||s)&&d.preventDefault(t)},containerClickHandler:function(t){if(this.getEventClosestNode(t.target,"tg-tree-icon-all"))return void this.toggleAllRows();const e=this.getEventClosestNode(t.target,"tg-select-icon-all");if(e)return void this.selectIconAllClickHandler(e);const i=this.getEventData(t);if(!i)return;if(this.getEventClosestNode(t.target,"tg-header-item")){if(this.trigger(y.onClick,i),this.isDefaultPrevented(i))return;return void this.sortHandler(t,i)}if(this.getEventClosestNode(t.target,"tg-tree-icon"))return void this.toggleRow(i.rowItem);this.getEventClosestNode(t.target,"tg-select-icon")?this.setRowSelected(i.rowItem,t):this.trigger(y.onClick,i)},containerDblClickHandler:function(t){const e=this.getEventData(t)||{e:t};this.trigger(y.onDblClick,e)},containerContextMenuHandler:function(t){const e=this.getEventData(t)||{e:t};this.trigger(y.onContextMenu,e)},containerSelectStartHandler:function(t){if(this.options.textSelectable)return;w(t.target).is("input,textarea,code")||d.preventDefault(t)},containerKeyDownHandler:function(t){if(this.hasMask)return;const e={e:t};if(this.trigger(y.onKeyDown,e),this.isDefaultPrevented(e))return;const i=t.keyCode,o={9:this.keyTabHandler,13:this.keyEnterHandler,27:this.keyEscHandler,33:this.keyPageUpHandler,34:this.keyPageDownHandler,35:this.keyEndHandler,36:this.keyHomeHandler,37:this.keyLeftHandler,38:this.keyUpHandler,39:this.keyRightHandler,40:this.keyDownHandler}[i];if(!o)return;o.call(this,t)&&d.preventDefault(t)},unbindEvents:function(){d.unbindEvents(this.containerEvents),this.containerEvents=null,this.columnWidthDrag&&(this.columnWidthDrag.destroy(),this.columnWidthDrag=null),this.columnWidthTouch&&(this.columnWidthTouch.destroy(),this.columnWidthTouch=null),this.rowDrag&&(this.rowDrag.destroy(),this.rowDrag=null),this.rowTouch&&(this.rowTouch.destroy(),this.rowTouch=null),this.scrollTouch&&(this.scrollTouch.destroy(),this.scrollTouch=null),this.protectedItem=null}},G={exportData:function(t){const e=this.getData();return{columns:this.getTreeSnapshot(e.columns,t),rows:this.getTreeSnapshot(e.rows,t)}},isItemExportable:function(t){return!!t&&(!d.hasOwn(t,"exportable")||Boolean(t.exportable))},getTreeSnapshot:function(t,e){const i=(t,o)=>{d.isList(o)&&o.forEach((o=>{if(!this.isItemExportable(o))return;const n=this.getItemSnapshot(o,e),s=o.subs;Array.isArray(s)&&(n.subs=[],i(n.subs,s)),t.push(n)}))},o=[];return i(o,t),o},getItemSnapshot:function(t){let e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};const i={};return Object.keys(t).forEach((o=>{!0!==e[o]?!1!==e[o]&&"subs"!==o&&0!==o.indexOf("tg_")&&(i[o]=t[o]):i[o]=t[o]})),i}},j={flushRow:function(t){d.toList(t).forEach((t=>{this.deleteRowCache(t)}))},flushRowFrom:function(t){d.isNum(t)&&(0!==t?this.forEachRowsCache((e=>{e>=t&&this.deleteRowCache(e)})):this.flushBody())},flushBody:function(){this.forEachRowsCache((t=>{this.deleteRowCache(t)}))},flushSort:function(){this.frozenInfo.rows?this.flushRowFrom(this.frozenInfo.rows):this.flushBody()},flushColumn:function(t){const e=d.toList(t);this.forEachRowsCache(((t,i,o,n)=>{e.forEach((t=>{this.deleteCellCache(t,o,n)}))}))},flushColumnFrom:function(t){d.isNum(t)&&this.forEachRowsCache(((e,i,o,n)=>{o.forEach(((e,i)=>{i>=t&&this.deleteCellCache(i,o,n)}))}))},flushCell:function(t,e){const i=d.toList(t),o=d.toList(e);i.forEach((t=>{const e=this.getRowCache(t);if(!e)return;const i=e.cellNodes,n=e.observerNodes;o.forEach((t=>{this.deleteCellCache(t,i,n)}))}))},flushWithViewport:function(){const{rows:t,columns:e}=this.viewport;this.forEachRowsCache(((i,o,n,s)=>{t.includes(i)?n.forEach(((t,i)=>{e.includes(i)||this.deleteCellCache(i,n,s)})):this.deleteRowCache(i)}))}},U={"sort-h":'\n\n \n \n \n \n\n',"sort-v":'\n\n \n \n \n \n\n',checkbox:'\n\n \n \n \n\n',radio:'
    ',drag:'\n\n \n\n',tree:'\n\n \n \n\n'},X={icons:U,getIcon:function(t){let e=U[t];return e=String(e).trim(),e}},Y={header:function(t,e,i,o){return t},null:function(t,e,i,o){return e&&e.tg_group?t:null==t?"—":t},blank:function(t,e,i,o){return""},string:function(t,e,i,o){return t},number:function(t,e,i,o){return t},icon:function(t,e,i,o){return`${t}`},select:function(t,e,i,o){return this.isRowSelectable(e)?this.getSelectFormatterContent(e):""},rowDrag:function(t,e,i,o){return this.getRowDragFormatterContent(e)},rowNumber:function(t,e,i,o){return e.tg_row_number||""},tree:function(t,e,i,o){return this.getTreeFormatterContent(t,e)}},K={setFormatter:function(t,e){this.renderType="all";let i=t;if("string"==typeof t){if(this.formatters)return this.formatters[t]=e,this;i={},i[t]=e}return this.customFormatters=i,this},getFormatter:function(t){if(!t)return;const e=this.formatters[t];return"function"==typeof e?e.bind(this):void 0},getDefaultFormatter:function(t){return(Y[t]||Y.string).bind(this)},getSelectFormatterContent:function(t){let e="radio";this.options.selectMultiple&&(e="checkbox");const i=X.getIcon(e);return`
    ${i}
    `},getRowDragFormatterContent:function(t){if(t.tg_frozen)return"";return`
    ${X.getIcon("drag")}
    `},getTreeIndentWidth:function(t,i,o){if(!t)return 0;let n=5;return i||(n+=e.TREE_INDENT),n+=o*e.TREE_INDENT,n},getTreeFormatterContent:function(t,e){const i=this.rowsInfo.isTree,o=e.tg_group,n=this.isEmptyGroup(e);n&&(e.collapsed=!0);const s=e.collapsed,r=d.toNum(e.tg_level),l=this.getTreeIndentWidth(i,o,r),h=[];if(h.push(`
    `),o){const t={"tg-tree-icon":!0,"tg-tree-icon-collapsed":s,"tg-tree-icon-expanded":!s,"tg-tree-icon-empty":n},e=X.getIcon("tree"),i=`
    ${e}
    `;h.push(i)}return h.push(`
    ${t}
    `),h.push("
    "),h.join("")}},q={renderHeaderTables:function(){this.clearHeaderCache();const t=this.viewColumns,e=this.frozenInfo.columns;this.hasTreeColumn=!1,this.hasSortColumn=!1;let i=[],o=[];for(let n=0,s=t.length;n=e?o.push(s):i.push(s)}if(this.frozenInfo.right){const t=i;i=o,o=t}this.renderHeaderTable(i,this.$headerL),this.renderHeaderTable(o,this.$headerR)},renderHeaderTable:function(t,e){const i=document.createElement("div"),o=["tg-header-table"];this.hasSortColumn&&(o.push("tg-header-sortable"),o.push(`tg-header-sort-${this.options.sortIndicator}`)),i.className=d.classMap(o);const n=t.length;if(n){let e=t[n-1];e&&"tg-column-blank"===e.id&&(e=t[n-2]),t.forEach((t=>{this.renderHeaderItem(t,i,e)}))}e.append(i)},renderHeaderItem:function(t,e,i){const o=t.tg_view_index;if(this.getHeaderCache(o))return;const n=this.getHeaderItemClass(t,i),s=d.styleMap(t.headerStyleMap),r={column:o,class:n,data:t.id};s&&(r.style=s);const l=[this.createColumnHeader(t)];if(this.isColumnResizable(t)){const e=this.createColumnResizing(t);l.push(e)}const h=this.createElement("div",r,l);e.appendChild(h),this.setHeaderCache(o,h),this.setNodeDataCache(h,{rowItem:this.headerRowItem,column:o,columnItem:t,headerNode:h}),t.tg_parent&&this.renderHeaderItem(t.tg_parent,e)},createColumnHeader:function(t){const e={class:this.getHeaderClass(t),style:this.getHeaderStyle(t)},i=[this.createColumnName(t)];if(this.hasSortColumn&&!t.tg_group){const e=this.createColumnSort(t);i.push(e)}return this.createElement("div",e,i)},createColumnName:function(t){const e=["tg-column-name"];t.tg_group&&e.push("tg-header-group-name");const i={class:e.join(" ")};let o=t.name;const n=t.tg_headerFormatter;return"function"==typeof n&&(o=n.call(this,o,this.headerRowItem,t)),"tree"===t.formatter?o=this.createHeaderTreeName(o):t===this.selectColumn&&this.isSelectAllVisible()&&(o=this.createHeaderSelectName()),this.createElement("div",i,o)},createHeaderTreeName:function(t){this.hasTreeColumn=!0;const e=[];if(this.options.collapseAllVisible){const t=X.getIcon("tree"),i=this.createElement("div",{class:"tg-tree-icon tg-tree-icon-all"},t);e.push(i)}else{const t=this.createElement("div",{class:"tg-tree-icon"});e.push(t)}const i=this.createElement("div",{class:"tg-tree-name"},t);e.push(i);return this.createElement("div",{class:"tg-tree"},e)},createHeaderSelectName:function(){const t=X.getIcon("checkbox");return this.createElement("div",{class:"tg-select-icon-all tg-checkbox"},t)},createColumnSort:function(t){let e;return this.isColumnSortable(t)&&(e="h"===this.options.sortIndicator?this.createSortIndicatorH(t):this.createSortIndicatorV(t)),this.createElement("div",{class:"tg-column-sort"},e)},createSortIndicatorH:function(t){const e=X.getIcon("sort-h"),i=[this.createElement("div",{class:"tg-sort-indicator-line"}),this.createElement("div",{class:"tg-sort-indicator-icon"},e)];return this.createElement("div",{class:"tg-sort-indicator"},i)},createSortIndicatorV:function(t){const e=X.getIcon("sort-v"),i=[this.createElement("div",{class:"tg-sort-indicator-icon"},e)];return this.createElement("div",{class:"tg-sort-indicator"},i)},createColumnResizing:function(){return this.createElement("div",{class:"tg-column-resizing"})},getHeaderItemClass:function(t,e){const i=["tg-header-item"];return t.tg_group&&i.push("tg-header-group-item"),t===e&&i.push("tg-header-column-last"),i.push(`tg-c-${t.tg_view_index}`),i.push(`tg-h-${t.tg_layer}`),t.tg_combination&&i.push(`tg-h-${t.tg_combination}`),i.push(d.classMap(t.headerClassMap)),d.classMap(i)},getHeaderClass:function(t){const e=["tg-column-header"];return"tree"===t.formatter&&(e.push("tg-tree-header"),this.rowsInfo.isTree&&e.push("tg-tree-header-indent")),this.isColumnSortable(t)&&e.push(`tg-column-sortable tg-column-sort-${this.options.sortIndicator}`),t.align&&e.push(`tg-align-${t.align}`),e.join(" ")},getHeaderStyle:function(t){const e=[d.styleMap(t.headerStyleMap)],i=t.tg_width;return this.isInvisible(t)||i<=0?e.push("display:none;"):e.push(`width:${i}px;`),e.join("")}},J={renderHeader:function(){this.cssRulesInvalid=!0,this.$headerL.empty(),this.$headerR.empty(),this.resetCssDisplay(),this.renderHeaderTables(),this.renderHeaderSort(),this.headerCreated=!0,this.trigger(y.onHeaderUpdated,{node:this.$headerFrame.get(0)})},initHeaderLayerHeight:function(){this.updateScrollPaneHiddenState(),this.resetCssDisplay(),this.viewAllColumns.forEach((t=>{this.updateColumnHeaderHeight(t)})),this.resetCssDisplay("none"),this.updateHeaderLayerHeight()},updateHeaderLayerHeight:function(){const t={},e=this.columnsInfo.maxLevel;for(let i=0;i<=e;i++)t[i]=0;const i=[];this.viewAllColumns.forEach((function(e){if(e.tg_combination)i.push(e);else{const i=e.tg_height,o=e.tg_layer;t[o]=Math.max(t[o],i)}})),i.forEach((function(e){let i=e.tg_height;const o=e.tg_combination.split(""),n=o.pop();o.forEach((function(e){i-=t[e]||0})),t[n]=Math.max(t[n],i)}));const o=JSON.stringify(t);this.previousHeaderLayerHeight!==o&&(this.previousHeaderLayerHeight=o,this.headerLayerHeight=t,this.cssRulesInvalid=!0)}};var Q=i(915);const Z={create:function(t){this.id=d.uid(4,"tg-"),d.isObject(t)||(t={container:t}),this.constructorOptions=t,this.createCache(),this.createView(t.container)},createView:function(t){this.createHolder(t),this.$holder?(this.createGlobalStyle(),this.createContainer()):console.error("ERROR: Grid requires a container")},createHolder:function(t){const e=w(t);if(!e.length)return;this.$holder=e,this.$holder.empty(),this.holder=this.$holder.get(0);const i=this.holder.getRootNode();this.shadowRoot=null,i&&i.host&&(this.shadowRoot=i)},createGlobalStyle:function(){const t=this.shadowRoot||document.head;if(t.querySelector(`style[context="${e.ID}"]`))return;const i=document.createElement("style");i.setAttribute("context",e.ID),i.innerHTML=Q.A.toString(),t.appendChild(i)},createContainer:function(){return this.$container=w('
    \r\n\r\n
    \r\n\r\n
    \r\n
    \r\n
    \r\n
    \r\n
    \r\n\r\n
    \r\n
    \r\n
    \r\n
    \r\n
    \r\n\r\n
    \r\n\r\n
    \r\n\r\n
    \r\n
    \r\n
    \r\n
    \r\n
    \r\n\r\n
    \r\n
    \r\n
    \r\n
    \r\n
    \r\n\r\n
    \r\n
    \r\n
    \r\n
    \r\n
    \r\n\r\n
    \r\n
    \r\n
    \r\n
    \r\n
    \r\n\r\n
    \r\n\r\n
    \r\n\r\n
    \r\n
    \r\n
    \r\n
    \r\n\r\n
    \r\n\r\n
    \r\n
    \r\n \r\n \r\n \r\n
    \r\n
    \r\n\r\n
    \r\n').appendTo(this.$holder),this.$container.attr("id",this.id),this.$container.addClass(`${e.NS} ${this.id}`),this.container=this.$container.get(0),d.setInstance(this.container,this),this.$headerFrame=this.$container.find(".tg-header-frame"),this.$paneHL=this.$headerFrame.find(".tg-pane-header-left"),this.$paneHR=this.$headerFrame.find(".tg-pane-header-right"),this.$headerL=this.$paneHL.find(".tg-header-left"),this.$headerR=this.$paneHR.find(".tg-header-right"),this.$header=w().add(this.$headerL).add(this.$headerR),this.$bodyFrame=this.$container.find(".tg-body-frame"),this.$paneTL=this.$bodyFrame.find(".tg-pane-top-left"),this.$paneTR=this.$bodyFrame.find(".tg-pane-top-right"),this.$paneBL=this.$bodyFrame.find(".tg-pane-bottom-left"),this.$paneBR=this.$bodyFrame.find(".tg-pane-bottom-right"),this.$bodyTL=this.$paneTL.find(".tg-body-top-left"),this.$bodyTR=this.$paneTR.find(".tg-body-top-right"),this.$bodyBL=this.$paneBL.find(".tg-body-bottom-left"),this.$bodyBR=this.$paneBR.find(".tg-body-bottom-right"),this.$body=w().add(this.$bodyTL).add(this.$bodyTR).add(this.$bodyBL).add(this.$bodyBR),this.$columnLineContainer=this.$container.find(".tg-column-line"),this.$columnLineItem=this.$columnLineContainer.find(".tg-column-line-item"),this.$columnLineItemL=this.$columnLineContainer.find(".tg-column-line-l"),this.$columnLineItemR=this.$columnLineContainer.find(".tg-column-line-r"),this}},tt={initColumnsHandler:function(){this.columns=this.data.columns,this.columns.forEach(((t,e)=>{t&&"object"==typeof t||(this.columns[e]={})}));const t=this.getPrivateColumns();this.columnsInfo=this.initTreeInfo(t,this.frozenInfo.column);const e=[],i=[],o=(t,n)=>{if(!d.isList(t))return;let s,r=0;t.forEach((t=>{if(!this.isInvisible(t))if(t.tg_group){if(this.isEmptyGroup(t))return;i.push(t),o(t.subs,t)}else t.tg_list_index=r,r+=1,t.tg_list_last=!1,s=t,e.push(t)})),s&&(s.tg_list_last=!0)};o(t),e.forEach((t=>{this.initColumnItemHandler(t)})),i.forEach((t=>{this.initColumnGroupHandler(t)}));const n=[].concat(e).concat(i);this.initViewList(n,((t,e)=>{})),this.viewColumns=e,this.viewGroupColumns=i,this.viewAllColumns=n,this.initHeaderHandler(t),this.initSortColumn()},getPrivateColumns:function(){const t=this.options;this.selectColumn=t.selectColumn,this.rowDragColumn=t.rowDragColumn,this.rowNumberColumn=t.rowNumberColumn,this.blankColumn=t.blankColumn;let e=[];const i=()=>{t.selectVisible&&e.push(this.selectColumn),t.rowDragVisible&&e.push(this.rowDragColumn),t.rowNumberVisible&&(this.rowNumberColumn.width=t.rowNumberWidth,e.push(this.rowNumberColumn))};if(this.frozenInfo.right){const t=this.frozenInfo.column;this.columns.forEach(((o,n)=>{e.push(o),n===t&&i()}))}else i(),e=e.concat(this.columns);return e.push(this.blankColumn),e},setColumns:function(t){this.data.columns=d.toList(t),this.rerender()},getColumns:function(){return this.columns},getViewColumns:function(t){return t?this.viewAllColumns:this.viewColumns},initColumnItemHandler:function(t){this.initColumnProps(t),this.initColumnFormatter(t),this.initColumnWidth(t)},initColumnGroupHandler:function(t){this.initColumnFormatterByName(t,"headerFormatter","header")},initColumnProps:function(t){const e=this.options.columnTypes;if(!d.hasOwn(t,"type")){const i=e[t.id];"string"==typeof i&&(t.type=i)}let i=this.options.columnProps;const o=e[t.type];o&&"object"==typeof o&&(i=d.merge(i,o));for(const e in i)d.hasOwn(t,e)||(t[e]=i[e])},initColumnFormatter:function(t){this.initColumnFormatterByName(t,"headerFormatter","header");let e=t.type;const i=t.formatter;"string"==typeof i&&(e=i),this.initColumnFormatterByName(t,"formatter",e)},initColumnFormatterByName:function(t,e,i){let o=t[e];"function"!=typeof o?(o=this.getFormatter(i),t[`tg_${e}`]=o||this.getFormatter("string")):t[`tg_${e}`]=o.bind(this)},initColumnWidth:function(t){if(t!==this.blankColumn)return d.isNum(t.width)&&t.width>=0?(t.tg_width=t.width,t.minWidth=Math.min(t.minWidth,t.tg_width),void(t.maxWidth=Math.max(t.maxWidth,t.tg_width))):void this.initColumnWidthByName(t);t.tg_width=0},initColumnWidthByName:function(t){const e=this.getComputedColumnWidth(t);d.isNum(e)&&(t.tg_width=e)},getComputedColumnWidth:function(t){const e=t.name||"",i=d.getCharLen(e);let o=Math.round(10*i);return o>103&&(o=Math.max(103,Math.round(10*i/2)),o>133&&(o=Math.max(133,Math.round(10*i/3)),o>163&&(o=Math.max(163,Math.round(10*i/4))))),d.clamp(o,t.minWidth,t.maxWidth)},initSortColumn:function(){this.sortColumn=null;const t=this.options,e=t.sortField;if(!e)return;const i=this.getColumnItemById(e);return i&&this.isColumnSortable(i)?(d.hasOwn(i,"sortAsc")||(i.sortAsc=t.sortAsc),this.sortColumn=i,this):void 0}},et={initHeaderHandler:function(t){this.initHeaderRowItem(),this.viewGroupColumns.reverse(),this.initGroupColumnsWidth(),this.initGroupColumnsLayer(t)},initHeaderRowItem:function(){this.headerRowItem={tg_index:-1,tg_view_index:-1},this.viewAllColumns.forEach((t=>{d.hasOwn(t,"id")&&(this.headerRowItem[t.id]=t.name)}))},initGroupColumnsWidth:function(){this.viewGroupColumns.forEach((t=>{let e=0;t.subs.forEach((t=>{this.isInvisible(t)||(e+=t.tg_width)})),t.tg_width=e}))},initGroupColumnsLayer:function(t){const e=this.columnsInfo.maxLevel;this.viewColumns.forEach((function(t){t.tg_layer=e,t.tg_parent&&(t.tg_parent.tg_layer=e-1)})),this.viewGroupColumns.forEach((function(t){const e=t.tg_layer,i=t.tg_parent;if(i){let t=e-1;d.isNum(i.tg_layer)&&(t=Math.min(t,i.tg_layer)),i.tg_layer=t}})),this.initColumnRowspanHandler(t,0)},initColumnRowspanHandler:function(t,e){t.forEach((t=>{const i=this.initColumnCombinationHandler(t,e);t.tg_group&&this.initColumnRowspanHandler(t.subs,e+i)}))},initColumnCombinationHandler:function(t,e){const i=[],o=t.tg_layer;for(;e<=o;)i.push(e),e+=1;i.reverse();const n=i.length;let s="";return n>1&&(s=i.join("")),t.tg_combination=s,n}},it={},ot={name:"",minWidth:81,maxWidth:300},nt=function(t){return null==t},st=function(t,e){const i=nt(t),o=nt(e);return i&&o?0:i?1:o?-1:void 0},rt=function(t,e){return t.tg_index>e.tg_index?1:-1},lt=function(t,e){return rt(t,e)},ht=function(t,e){if("string"==typeof t&&"string"==typeof e){const i=t.toUpperCase(),o=e.toUpperCase();if(i!==o)return i>o?-1:1}return t>e?-1:1},at=function(t,e,i,o){return t?-1:e?1:ht(i,o)},ct=function(t,e){const i="number"==typeof t,o="number"==typeof e;return i&&o?t>e?-1:1:at(i,o,t,e)},dt=function(t,e){const i=new Date(t),o=new Date(e),n=d.isDate(i),s=d.isDate(o);if(n&&s){const t=i.getTime(),e=o.getTime();if(t===e)return;return t>e?-1:1}return at(n,s,t,e)},ut=function(t,e){const i="boolean"==typeof t,o="boolean"==typeof e;return i&&o?t>e?-1:1:at(i,o,t,e)},gt=function(t,e,i,o){const n=t[i.sortField],s=e[i.sortField],r=st(n,s);if("number"==typeof r)return 0===r?lt(t,e):i.sortBlankFactor*r;if(n!==s&&"function"==typeof o){const t=o(n,s);if(d.isNum(t))return i.sortFactor*t}return lt(t,e)},ft={blankValue:st,equal:lt,index:rt,value:gt,diffType:at,string:function(t,e,i){return gt(t,e,i,ht)},stringValue:ht,number:function(t,e,i){return gt(t,e,i,ct)},numberValue:ct,date:function(t,e,i){return gt(t,e,i,dt)},dateValue:dt,boolean:function(t,e,i){return gt(t,e,i,ut)},booleanValue:ut};const pt={initOptionsHandler:function(){return this.options=this.generateOptions(),this.initOptionsFormatters(),this.initOptionsSort(),this.initOptionsFrozen(),this.initOptionsScrollbar(),this.initOptionsContainer(),this.initBindWindowResize(),this.initBindContainerResize(),this},generateOptions(){const t={className:e.NS,theme:e.ID,headerVisible:!0,rowHeight:32,rowFilter:null,rowFilteredSort:null,rowNotFound:"",rowMoveCrossLevel:!0,rowCacheLength:0,rowProps:it,columnTypes:{tree:{type:"tree",formatter:"tree",width:230,minWidth:120,maxWidth:810},number:{type:"number",align:"right"},date:{type:"date",align:"right"},name:"tree"},columnCacheLength:0,columnProps:ot,collapseAllOnInit:null,collapseAllVisible:!0,selectAllOnInit:null,selectVisible:!1,selectAllVisible:!0,selectMultiple:!0,selectColumn:{private:!0,id:"tg-column-select",name:"",formatter:"select",headerClassMap:"tg-header-select",classMap:"tg-cell-select",width:36,align:"center",resizable:!1,sortable:!1,exportable:!1},rowDragCrossLevel:!0,rowDragVisible:!1,rowDragColumn:{private:!0,id:"tg-column-row-drag",name:"",formatter:"rowDrag",headerClassMap:"tg-header-row-drag",classMap:"tg-cell-row-drag",align:"center",width:36,resizable:!1,sortable:!1,exportable:!1},rowNumberWidth:36,rowNumberFilter:null,rowNumberVisible:!1,rowNumberColumn:{private:!0,id:"tg-column-row-number",name:"",formatter:"rowNumber",headerClassMap:"tg-header-row-number",classMap:"tg-cell-row-number",align:"center",maxWidth:100,sortable:!1,exportable:!1},blankColumn:{private:!0,id:"tg-column-blank",name:"",formatter:"blank",headerClassMap:"tg-header-blank",classMap:"tg-cell-blank",width:0,minWidth:0,maxWidth:4096,resizable:!1,sortable:!1,exportable:!1},sortField:"",sortAsc:!0,sortBlankValueBottom:!0,sortComparers:ft,sortOnInit:!1,sortIndicator:"h",highlightKeywords:{textKey:"tg_text_",textGenerator:null,highlightKey:"tg_highlight_",highlightPre:"",highlightPost:""},frozenRow:-1,frozenRowMax:10,frozenRowHoverable:!1,frozenBottom:!1,frozenColumn:-1,frozenColumnMax:10,frozenRight:!1,scrollbarSize:12,scrollbarSizeH:null,scrollbarSizeV:null,scrollbarRound:!1,scrollbarFade:!1,scrollbarFadeTimeout:1e3,scrollbarType:"auto",scrollPaneMinWidth:30,scrollPaneGradient:30,autoHeight:!1,textSelectable:!1,bindWindowResize:!1,bindContainerResize:!1,cellResizeObserver:null},i=this.generateThemeOptions();return d.merge(t,i,this.constructorOptions,this.customOptions,this.dataOptions)},generateThemeOptions(){const t=this.pickOptions("theme").pop();if(t)return this.getThemeOptions(t)},pickOptions(t){return[this.constructorOptions,this.customOptions,this.dataOptions].map((e=>e&&e[t])).filter((t=>t))},initOptionsFormatters(){let t;const e=this.pickOptions("formatters");e.length&&(t=d.merge.apply(null,e)),this.formatters=d.merge(Y,t,this.customFormatters),this.nullFormatter=this.getFormatter("null")},initOptionsSort(){"v"!==this.options.sortIndicator&&(this.options.sortIndicator="h")},initOptionsFrozen:function(){const t=this.options;this.frozenInfo={column:-1,row:-1,columns:0,rows:0,bottom:Boolean(t.frozenBottom),right:Boolean(t.frozenRight)};let e=d.toNum(t.frozenColumn,!0);e=d.clamp(e,-1,t.frozenColumnMax),e>-1&&!this.frozenInfo.right&&(t.selectVisible&&(e+=1),t.rowDragVisible&&(e+=1),t.rowNumberVisible&&(e+=1)),this.frozenInfo.column=e,e>-1?this.frozenInfo.columns=e+1:(this.frozenInfo.columns=0,this.frozenInfo.right=!1);let i=d.toNum(t.frozenRow,!0);i=d.clamp(i,-1,t.frozenRowMax),this.frozenInfo.row=i,i>-1?this.frozenInfo.rows=i+1:(this.frozenInfo.rows=0,this.frozenInfo.bottom=!1)},initOptionsScrollbar:function(){const t=this.options;("auto"===t.scrollbarType&&d.isTouchDevice()||["touch","mobile"].includes(t.scrollbarType))&&(t.scrollbarFade=!0,t.scrollbarSize=6,t.scrollbarRound=!0);const e=d.toNum(t.scrollbarSize);this.scrollbarSizeH=e,d.isNum(t.scrollbarSizeH)&&(this.scrollbarSizeH=t.scrollbarSizeH),this.scrollbarSizeV=e,d.isNum(t.scrollbarSizeV)&&(this.scrollbarSizeV=t.scrollbarSizeV)},initOptionsContainer:function(){this.$container.attr("id",this.id);const t=this.options;this.$container.removeClass();const i=[e.NS,this.id,`tg-${t.theme}`,t.className];t.textSelectable||i.push("tg-text-unselectable"),d.isTouchDevice()&&i.push("tg-touch-device"),this.$container.addClass(d.classMap(i))},setTextSelectable:function(t){this.options.textSelectable&&(t?this.$container.removeClass("tg-text-unselectable"):this.$container.addClass("tg-text-unselectable"))}},mt={initBindWindowResize:function(){this.unbindWindowResize(),this.options.bindWindowResize&&(this.windowResizeEvents={resize:{handler:t=>{this.resize()}}},d.bindEvents(this.windowResizeEvents,window))},unbindWindowResize:function(){d.unbindEvents(this.windowResizeEvents)},createResizeObserver:function(t){if("undefined"==typeof ResizeObserver)return console.error("ERROR: This browser does not support ResizeObserver"),{observe:()=>{},unobserve:()=>{},disconnect:()=>{}};return new ResizeObserver((e=>{t.call(this,e)}))},initBindContainerResize:function(){this.unbindContainerResize(),this.options.bindContainerResize&&this.holder&&(this.resizeObserver=this.createResizeObserver((t=>{Boolean(this.holder.offsetWidth||this.holder.offsetHeight||this.holder.getClientRects().length)&&this.resize()})),this.resizeObserver.observe(this.holder))},unbindContainerResize:function(){this.resizeObserver&&(this.resizeObserver.disconnect(),this.resizeObserver=null)}},bt={initRowsHandler:function(){this.rows=this.data.rows,this.rowsInfo=this.initTreeInfo(this.rows,this.frozenInfo.row)},getRows:function(){return this.rows},getViewRows:function(){return this.viewRows},createViewRows:function(){this.initRowFilterHandler();const t=[],e=this.getRowNumberFilter();let i=1;const o=(t,o)=>{if(e.call(this,t,o))return t.tg_row_number=i,void(i+=1);t.tg_row_number=""},n=(e,i,s)=>{if(!d.isList(e))return;let r,l=0;e.forEach((e=>{if(this.isInvisible(e))return;e.tg_list_index=l,l+=1,e.tg_list_last=!1,r=e,this.gridRowItemHandler(e),o(e,l),s||t.push(e);const i=s||e.tg_group&&e.collapsed;n(e.subs,e,i)})),r&&(r.tg_list_last=!0)};n(this.rows);let s,r=0;return this.initViewList(t,((t,e)=>{t.tg_top=r,r+=this.getRowHeight(t),t.tg_group_line=!1,t.collapsed&&(t.tg_group_line=!0),s&&(t.tg_group||t.tg_level{if(e.tg_invisible)return;const n=!t.call(this,e,i,o);if(e.tg_filtered=n,!n){let t=e;for(;t.tg_parent;)t.tg_parent.tg_filtered=!1,t=t.tg_parent}})),this.sortColumn)return;let e=this.options.rowFilteredSort;if("function"==typeof e&&(e=e.call(this)),!e)return;"string"==typeof e&&(e={sortField:e,sortAsc:this.options.sortAsc});const i=e.sortField||e.id;i&&this.sortRows(i,e)},highlightKeywordsFilter:function(t,e,i){const{textKey:o,textGenerator:n,highlightKey:s}=this.options.highlightKeywords;if(e.forEach((e=>{t[`${s}${e}`]=null})),!i)return!0;const r=`${i}`.trim().toLowerCase().split(/\s+/g).filter((t=>t));if(!r.length)return!0;let l=!1;const h=(e,i)=>(/<\/?[a-z][\s\S]*>/i.test(e)&&(e=((e,i)=>{const n=`${o}${i}`,s=t[n];if(s)return s;const r=document.createElement("div");r.innerHTML=e;const l=r.innerText;return t[n]=l,l})(e,i)),(t=>{const e=t.toLowerCase();let i=0;for(const t of r){const o=e.indexOf(t,i);if(-1===o)return;i=o+t.length}return!0})(e));let a=function(t,e){return t[e]};return"function"==typeof n&&(a=n),e.forEach((e=>{const i=a(t,e);if(null==i)return;const o=`${i}`.trim();if(!o)return;const n=h(o,e);n&&(t[`${s}${e}`]=n,l=!0,this.highlightKeywords=r)})),l},highlightKeywordsHandler:function(){const{highlightCells:t}=this.renderSettings;if(!t.length)return;const e=this.highlightKeywords;e&&(this.asyncHighlightKeywords||(this.asyncHighlightKeywords=d.debounce(this.highlightKeywordsSync,10)),this.asyncHighlightKeywords.apply(this,[t,e]))},highlightKeywordsSync:function(t,e){t.forEach((t=>{const i=Array.from(t.querySelectorAll("svg")).concat(Array.from(t.querySelectorAll("textarea"))),o=document.createTreeWalker(t,NodeFilter.SHOW_TEXT,(t=>{if(i.length)for(const e of i)if(e.contains(t))return NodeFilter.FILTER_SKIP;return NodeFilter.FILTER_ACCEPT})),n=[];let s=o.nextNode();for(;s;)n.push(s),s=o.nextNode();n.length&&this.highlightTextNodes(n,e)}))},highlightTextNodes:function(t,e){const{highlightPre:i,highlightPost:o}=this.options.highlightKeywords;let n=0;const s=()=>(n>=e.length&&(n=0),e[n++]);let r=s();t.forEach((t=>{const e=t.textContent,n=e.toLowerCase(),l=[];let h=0;const a=e.length;let c=!1;for(;h{if(e.selected){if(t)return void(e.selected=!1);t=e}}))}const t=this.options.selectAllOnInit;!0!==t?!1===t&&this.updateAllRowsSelected(!1):this.updateAllRowsSelected(!0)},updateAllRowsSelected:function(t){this.forEachSelectableRow((e=>{e.selected=t}))},initCollapseAllOnInitHandler:function(){const t=this.options.collapseAllOnInit;!0!==t?!1===t&&this.updateAllRowsCollapsed(!1):this.updateAllRowsCollapsed(!0)},getToBeAddedItemList:function(t){const e=[];return d.toList(t).forEach((t=>{t&&"object"==typeof t?e.push(t):void 0!==t&&e.push({name:t})})),e},getToBeAddedParentSubs:function(t,e){return t?(t.subs||(t.subs=[]),t.subs):e},getToBeAddedPositionIndex:function(t,e){const i=e.length;return d.isNum(t)&&t>=0&&t<=i?Math.round(t):i},generateDataSnapshot:function(t){if(!t||"object"!=typeof t)return t;const e=this.cleanTreeList(t.rows),i=this.cleanTreeList(t.columns);return this.convertNumberType(e,i),t.rows=e,t.columns=i,t},cleanTreeList:function(t){if(!d.isList(t))return[];const e=(t,i)=>{i.forEach((i=>{if(!i||"object"!=typeof i)return void t.push({});const o=this.getItemSnapshot(i),n=i.subs;Array.isArray(n)&&(o.subs=[],e(o.subs,n)),t.push(o)}))},i=[];return e(i,t),i},convertNumberType:function(t,e){const i=[];d.forEachTree(e,(function(t){"number"===t.type&&t.id&&i.push(t.id)})),i.length&&d.forEachTree(t,(function(t){i.forEach((function(e){t[e]=d.convertNum(t[e])}))}))}},vt={setDefaultLoading:function(t){let e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};if(!t)return;const i=t.style;e.size&&(i.width=e.size,i.height=e.size),e.color&&(i.color=e.color),e.size||e.color||t.removeAttribute("style"),e.fast?t.classList.add("tg-loading-fast"):t.classList.remove("tg-loading-fast")},getDefaultLoading:function(t){return this.setDefaultLoading(this.$defaultLoading,t),this.$defaultLoading},getLoadingHolder:function(){return this.$container?this.$container.find(".tg-loading"):w()},setLoading:function(t){if(!this.$container)return this;this.$defaultLoading||(this.$defaultLoading=this.$container.find(".tg-loading-default").get(0));const e=this.getLoadingHolder().get(0);return"function"==typeof t&&(t=t.call(this,e)),d.isObject(t)&&(t=this.getDefaultLoading(t)),t||(t=this.getDefaultLoading()),this.renderNodeContent(e,t),this},showLoading:function(){return this.getLoadingHolder().show(),this},hideLoading:function(){return this.getLoadingHolder().hide(),this}},Ht={showMask:function(t){if(!this.hasMask||t){const e=this.$container.find(".tg-mask"),i=e.get(0);if(t&&i){const e=d.styleMap(t);e&&(i.style.cssText=e)}e.show(),this.hasMask=!0}return this},hideMask:function(){return this.hasMask&&(this.$container.find(".tg-mask").hide(),this.hasMask=!1),this}},Ct={renderNodeContent:function(t,e){if(t){if(e&&e.nodeType)return this.emptyNode(t),void t.appendChild(e);if(Array.isArray(e))return this.emptyNode(t),void e.forEach((e=>{e&&e.nodeType&&t.appendChild(e)}));void 0===e&&(e=""),t.innerHTML=e}},emptyNode:function(t){if(t)for(;t.firstChild;)t.removeChild(t.firstChild)},removeNode:function(t){t&&t.parentNode&&t.parentNode.removeChild(t)},appendNode:function(t,e){t&&e&&t.appendChild(e)},createElement:function(t,e,i){const o=document.createElement(t);e&&Object.keys(e).forEach((function(t){const i=e[t];void 0!==i&&o.setAttribute(t,i)})),d.isArray(i)||(i=[i]);let n="";return i.forEach((function(t){t&&t.nodeType?o.appendChild(t):void 0!==t&&(n+=t)})),n&&(o.innerHTML=n),o},find:function(t,e){return w(e||this.$container).find(t)},getRowNodes:function(t){const e=this.getRowItem(t);if(e)return this.getRowNodesByIndex(e.tg_view_index)},getCellNode:function(t,e){const i=this.getRowItem(t);if(!i)return;const o=this.getColumnItem(e);return o?this.getCellNodeByIndex(i.tg_view_index,o.tg_view_index):void 0},getHeaderItemNode:function(t){const e=this.getColumnItem(t);if(e)return this.getHeaderCache(e.tg_view_index)},getColumnHeaderNode:function(t){const e=this.getHeaderItemNode(t);if(e)return e.querySelector(".tg-column-header")}},yt={render:function(){this.asyncRender||(this.asyncRender=d.microtask(this.renderSync)),this.asyncRender.apply(this,arguments)},renderSync:function(){this.renderStartedTimestamp=Date.now();const t=this.generateRenderSettings.apply(this,arguments);return this.renderSettings=t,"all"===t.type?(this.flushBody(),this.initHandler(),this.renderHeader(),this.updateViewRowsAndSize(),this.renderBody(),this):"columns"===t.type?(this.flushBody(),this.initColumnsHandler(),this.renderHeader(),this.updateViewRowsAndSize(),this.renderBody(),this):"rows"===t.type?(this.updateViewRowsAndSize(),this.renderBody(),this):"rows_cache"===t.type?(this.cssRulesInvalid=!0,this.updateViewRowsAndSize(),this.updateRowCacheTopAndHeight(),this.renderBody(),this):"resize"===t.type?(this.resizeHandler(),this.renderBody(),this):(this.renderBody(),this)},generateRenderSettings:function(t){const e={type:this.renderType,scrollLeft:null,scrollTop:null,scrollColumn:null,scrollRow:null,highlightCells:[]};return"string"==typeof t?e.type=t:t&&Object.assign(e,t),this.headerCreated||(e.type="all"),e},renderBody:function(){this.scrollOnInit(),this.scrollTopOffset=this.scrollPane.getScrollTopOffset();const t=this.getViewport();return this.viewport=t,this.flushWithViewport(),this.previousScrollTopOffset!==this.scrollTopOffset&&(this.previousScrollTopOffset=this.scrollTopOffset,this.updateRowCacheTopOffset()),this.renderRows(t.rows),this.renderCells(t.rows,t.columns),this.renderUpdatedTimestamp=Date.now(),this.renderDuration=this.renderUpdatedTimestamp-this.renderStartedTimestamp,this.trigger(y.onUpdated,t),this.firstUpdated||(this.firstUpdated=!0,this.trigger(y.onFirstUpdated,t)),this.layoutEventHandler(),this.resizeEventHandler(),this.highlightKeywordsHandler(),this.renderSettings=null,this.renderType=null,this},rerender:function(){return this.render("all"),this}},Rt={resize:function(){return this.asyncResize||(this.asyncResize=d.throttle(this.resizeSync,100)),this.asyncResize.apply(this,arguments),this},resizeSync:function(){return this.headerCreated?(this.resizeHolderHandler.apply(this,arguments),this.firstUpdated&&this.isHolderInvisible()||this.render("resize"),this):this},resizeHolderHandler(t,e){if(0!==arguments.length)return 1===arguments.length?t&&"object"==typeof t?void this.$holder.css(t):void this.$holder.css({width:t}):void this.$holder.css({width:t,height:e})},isHolderInvisible(){const t=this.$holder.width(),e=this.$holder.height();return!t||!e},resizeHandler:function(){this.containerWidth=this.$container.width(),this.containerHeight=this.$container.height(),this.headerWidth=this.containerWidth,this.bodyWidth=this.containerWidth,this.updateTotalColumnsWidth(),this.resizeHeaderHandler(),this.resizeBodyHandler()},layoutEventHandler:function(){const t=this.previousLayout||{},e={headerWidth:this.headerWidth,headerHeight:this.headerHeight,bodyWidth:this.bodyWidth,bodyHeight:this.bodyHeight,scrollbarWidth:this.getScrollbarWidth(),scrollbarHeight:this.getScrollbarHeight()};Object.values(e).join("")!==Object.values(t).join("")&&(this.previousLayout=e,this.trigger(y.onLayout,d.merge({previous:t},e)))},resizeEventHandler:function(){const t=this.previousSize||{},e={width:this.containerWidth,height:this.containerHeight};Object.values(e).join("")!==Object.values(t).join("")&&(this.previousSize=e,this.trigger(y.onResize,d.merge({previous:t},e)))},resizeHeaderHandler:function(){this.initHeaderLayerHeight();const t=this.options;t.autoHeight&&this.viewRows.length>5e3&&(t.autoHeight=!1),this.headerHeight=0,t.headerVisible&&(this.containerHeight>0||t.autoHeight)&&this.updateHeaderTableHeight(),this.$headerFrame.css({width:this.headerWidth,height:this.headerHeight})},updateHeaderTableHeight:function(){let t=0;Object.keys(this.headerLayerHeight).forEach((e=>{t+=this.headerLayerHeight[e]}));const e=this.$headerL.find(".tg-header-table"),i=this.$headerR.find(".tg-header-table");e.css({height:t}),i.css({height:t}),this.headerHeight=t},resizeBodyHandler:function(){this.updateScrollState(),this.bodyHeight=this.containerHeight-this.headerHeight,this.$bodyFrame.css({width:this.bodyWidth,height:this.bodyHeight}),this.updatePaneWidth(),this.updatePaneHeight(),this.updateCanvasWidth(),this.updateCanvasHeight(),this.updateScrollPane(),this.updateCssRules()},updatePaneWidth:function(){let t=this.bodyWidth,e=0;if(this.frozenInfo.columns){const i=this.getScrollbarWidth();this.frozenInfo.right?(e=this.columnsWidthR+i,t=this.bodyWidth-e):(t=this.columnsWidthL,e=this.bodyWidth-t),this.scrollPaneHidden&&(this.frozenInfo.right?(t<=0&&(t=0),e=Math.max(0,this.bodyWidth-t)):(e3&&void 0!==arguments[3])||arguments[3];const n=this.getToBeAddedItemList(t);if(!n.length)return!1;let s;if(null!=e&&(s=this.getRowItem(e),!s))return!1;const r=this.getToBeAddedParentSubs(s,this.rows),l=this.getToBeAddedPositionIndex(i,r),h=[l,0].concat(n);r.splice.apply(r,h),this.initRowsHandler(),s?(s.collapsed=!1,this.flushRowFrom(s.tg_view_index+l)):this.flushRowFrom(l),this.onNextUpdated((function(){this.trigger(y.onRowAdded,n)}));const a={type:"rows"};return o&&(a.scrollRow=n[n.length-1]),this.render(a),!0},deleteRow:function(t){const e=d.toList(t),i=[];if(e.forEach((t=>{const e=this.getRowItem(t);e&&i.push(e)})),!i.length)return!1;const o=this.removeRowsHandler(i);this.initRowsHandler();const n=this.getRemovedMinIndex(o);return this.flushRowFrom(n),this.onNextUpdated((function(){this.trigger(y.onRowRemoved,i)})),this.render("rows"),!0},getRemovedMinIndex:function(t){let e=0;const i=t[t.length-1];if(this.isInvisible(i))return e;e=i.tg_view_index,e>0&&(e-=1);let o=i.tg_parent;for(;o;)o.collapsed&&(e=o.tg_view_index),o=o.tg_parent;return e},removeRowsHandler:function(t){const e=[].concat(t);e.sort((function(t,e){return e.tg_index-t.tg_index}));const i=[];return e.forEach((t=>{this.getRowParentSubs(t).splice(t.tg_sub_index,1),i.push(t)})),i}},Tt={renderCollapseAllState:function(){this.hasTreeColumn&&(this.asyncRenderCollapseAllState||(this.asyncRenderCollapseAllState=d.microtask(this.renderCollapseAllStateSync)),this.asyncRenderCollapseAllState.apply(this,arguments))},renderCollapseAllStateSync:function(){const t=this.$header.find(".tg-tree-header");this.rowsInfo.isTree?t.addClass("tg-tree-header-indent"):t.removeClass("tg-tree-header-indent"),this.renderCollapseAllIcon()},checkCollapseAllState:function(t){if(t!==this.allRowsCollapsed){if(t){let t=0;const e=this.rows.length;for(;t{if(e.tg_group&&e.tg_subs_length&&e.collapsed)return t=!0,!1})),t)return}this.allRowsCollapsed=t,this.renderCollapseAllIcon()}},expandAllRows:function(){return this.renderAllRowsCollapsed(!1)},collapseAllRows:function(){return this.renderAllRowsCollapsed(!0)},toggleAllRows:function(){return this.allRowsCollapsed?this.expandAllRows():this.collapseAllRows()},renderAllRowsCollapsed:function(t){const e=this.updateAllRowsCollapsed(t);return e.length?(this.flushBody(),this.onNextUpdated((()=>{this.renderCollapseAllIcon(),t?this.trigger(y.onRowCollapsed,e):this.trigger(y.onRowExpanded,e)})),this.render("rows"),this):this},updateAllRowsCollapsed:function(t){this.allRowsCollapsed=t;const e=[];return this.forEachRow((i=>{i.subs&&i.tg_subs_length&&this.isCollapsedChanged(i,t)&&(i.collapsed=t,e.push(i))})),e},expandRow:function(t){const e=this.getRowItem(t);return e?this.isEmptyGroup(e)?(this.trigger(y.onRowSubsRequest,e),this):this.isCollapsedChanged(e,!1)?(e.collapsed=!1,this.flushRowFrom(e.tg_view_index),this.renderCollapseIcon(e),this.onNextUpdated((()=>{this.checkCollapseAllState(!1),this.trigger(y.onRowExpanded,e)})),this.render("rows"),this):this:this},collapseRow:function(t){const e=this.getRowItem(t);return e&&e.subs&&e.tg_subs_length&&this.isCollapsedChanged(e,!0)?(e.collapsed=!0,this.flushRowFrom(e.tg_view_index),this.renderCollapseIcon(e),this.onNextUpdated((()=>{this.checkCollapseAllState(!0),this.trigger(y.onRowCollapsed,e)})),this.render("rows"),this):this},toggleRow:function(t){const e=this.getRowItem(t);return e?(e.collapsed?this.expandRow(e):this.collapseRow(e),this):this},expandRowLevel:function(t){t=d.toNum(t,!0);const e=[],i=[];return this.forEachRow((o=>{o.subs&&o.tg_subs_length&&(o.tg_level<=t?this.isCollapsedChanged(o,!1)&&(o.collapsed=!1,i.push(o)):this.isCollapsedChanged(o,!0)&&(o.collapsed=!0,e.push(o)))})),e.length||i.length?(this.flushBody(),this.onNextUpdated((()=>{e.length&&this.trigger(y.onRowCollapsed,e),i.length&&this.trigger(y.onRowExpanded,i)})),this.render("rows"),this):this},renderCollapseAllIcon:function(){if(!this.options.collapseAllVisible||!this.hasTreeColumn)return;const t=this.$header.find(".tg-tree-icon-all");this.renderTreeIcon(t,this.allRowsCollapsed)},renderCollapseIcon:function(t){if(!this.headerCreated)return;const e=this.getRowNodesByIndex(t.tg_view_index);if(!e)return;const i=e.find(".tg-tree-icon");this.renderTreeIcon(i,t.collapsed)},renderTreeIcon:function(t,e){t&&(e?t.removeClass("tg-tree-icon-expanded").addClass("tg-tree-icon-collapsed"):t.removeClass("tg-tree-icon-collapsed").addClass("tg-tree-icon-expanded"))}},Et={rowDragStartHandler:function(t,e){this.removeSortColumn();const i=e.rowItem;if(!i)return;const o=this.getRowNodesByIndex(i.tg_view_index);if(!o)return;e.dragCloneNodes=this.getRowDragCloneNodes(o),e.dropPlaceholder=this.getRowDropPlaceholder(o),e.dragStartTop=this.getRowTop(i),e.dragRowHeight=this.getRowHeight(i),e.dragStartScrollTop=this.scrollTop,e.dragMaxScrollTop=this.scrollPane.getMaxScrollTop();const n={e:t,rowItem:i};this.trigger(y.onRowDragged,n),this.isDefaultPrevented(n)||("touch"===e.type&&d.preventDefault(e.e),this.setRowState(i,"dragging"),this.setTextSelectable(!1),this.rowDropListHandler(e),this.updateDragCloneRowPosition(e))},rowDragMoveHandler:function(t,e){"touch"===e.type&&d.preventDefault(e.e),this.updateDragCloneRowPosition(e),this.updateDragPlaceholderPosition(e),this.rowDragAutoScrollHandler(e)},rowDragEndHandler:function(t,e){"touch"===e.type&&(this.protectedItem=null,d.preventDefault(e.e)),this.autoScrollStop(),this.setRowState(e.rowItem,"dragging",!1),this.setTextSelectable(!0),e.dragCloneNodes&&(e.dragCloneNodes.remove(),e.dragCloneNodes=null),e.dropPlaceholder&&(e.dropPlaceholder.remove(),e.dropPlaceholder=null),e.changed&&this.rowDropHandler(e)},updateDragCloneRowPosition:function(t){const e=this.scrollTop-t.dragStartScrollTop,i=t.dragStartTop+t.offsetY+e,o=i-this.scrollTopOffset;t.dragCloneNodes&&t.dragCloneNodes.css("top",o).show(),t.dragCurrentPosition=i+.5*t.dragRowHeight},getRowDragCloneNodes:function(t){const e=w();return t.each((function(t){const i=w(t),o=i.clone();o.appendTo(i.parent()),e.add(o)})),e.addClass("tg-clone").hide(),e},getRowDropPlaceholder:function(t){const e=w();return t.each((function(t){const i=w(t),o=w("
    ").addClass("tg-row-placeholder").hide(),n=i.parent();n.find(".tg-row-placeholder").remove(),o.appendTo(n),e.add(o)})),e},updateDragPlaceholderPosition:function(t){this.rowDropItemHandler(t);const e=t.dropItem;if(!e)return;let i=t.dropPosition-1;t.dropBottom?e.tg_view_last&&(i=t.dropPosition-2):e.tg_view_index-this.frozenInfo.rows==0&&(i=t.dropPosition);const o=i-this.scrollTopOffset;t.dropPlaceholder&&t.dropPlaceholder.css("top",o).show()},rowDragAutoScrollHandler:function(t){const e=t.dragCurrentPosition,i=this.scrollTop,o=this.bodyHeight-this.frozenRowsHeight,n=i+o,s=this.options.rowHeight,r=Math.min(3*s,.5*o);if(!(rn-r){const i=e-(n-r),o=this.getAutoScrollOffset(i,r);this.autoScrollStart(o,t)}else this.autoScrollStop()},getAutoScrollOffset:function(t,e){return Math.floor(t/e*20)},autoScrollStop:function(){this.autoScrollMotion&&(this.autoScrollMotion.destroy(),this.autoScrollMotion=null)},autoScrollStart:function(t,e){this.autoScrollStop();const i=e.dragMaxScrollTop;this.autoScrollMotion=new B,this.autoScrollMotion.bind(B.EVENT.MOTION_MOVE,(()=>{const o=d.clamp(this.scrollTop+t,0,i);o!==this.scrollTop?(this.setScrollTop(o),this.updateDragCloneRowPosition(e),this.updateDragPlaceholderPosition(e)):this.autoScrollStop()})),this.autoScrollMotion.once(B.EVENT.MOTION_END,(()=>{this.autoScrollStart(t,e)})),this.autoScrollMotion.start({duration:200})},rowDropListHandler:function(t){const e=this.getRowDropList(t);if(!d.isList(e))return;const i=t.rowItem,o=e.filter((t=>{if(t===i)return!1;if(t.tg_frozen)return!1;let e=t.tg_parent;for(;e;){if(e===i)return!1;e=e.tg_parent}return!0}));if(!d.isList(o))return;const n=[];o.forEach((t=>{const e=this.getRowTop(t),i=this.getRowHeight(t);n.push({rowItem:t,position:e}),n.push({rowItem:t,position:e+i-1,dropBottom:!0})})),t.dropList=n},getRowDropList:function(t){const e=this.options.rowDragCrossLevel;return e?"function"==typeof e?e.call(this,t):this.viewRows:this.getRowParentSubs(t.rowItem)},rowDropItemHandler:function(t){const e=t.dropList;if(!e)return;const i=t.dragCurrentPosition;let o=Number.MAX_VALUE;for(let n=0,s=e.length;no)break;o=r,t.dropItem=s.rowItem,t.dropBottom=s.dropBottom,t.dropPosition=s.position}},rowDragDropPositionHandler:function(t,e,i){const o=this.getRowParentSubs(t),n=t.tg_sub_index;let s,r;return this.isDropIntoGroupFirstChild(e,i)?(s=e.subs,r=0):(s=this.getRowParentSubs(e),r=e.tg_sub_index,o===s&&n{this.trigger(y.onRowDropped,n)})),this.render({type:"rows",scrollRow:i})}},It={getMoveFocusRow:function(t,e){let i=t[0];return e>0&&(i=t[t.length-1]),i},getMoveLengthInList:function(t,e){let i=0;return t.forEach((t=>{this.getRowParentSubs(t)===e&&(i+=1)})),i},getMoveInfo:function(t,e,i){const o=this.getRowParentSubs(i);let n=i.tg_sub_index+e;const s=i.tg_parent;if(s&&this.options.rowMoveCrossLevel){const e=0,i=s.tg_subs_length-1;if(ni){const e=n-i;return this.getMoveInfo(t,e,s)}}if(e>0){n-=this.getMoveLengthInList(t,o)-1}return n=d.clamp(n,0,o.length),{list:o,index:n}},moveRowsHandler:function(t,e){(t=this.removeRowsHandler(t)).reverse();const i=this.getMoveFocusRow(t,e),o=this.getMoveInfo(t,e,i),n=[o.index,0].concat(t);return o.list.splice.apply(o.list,n),this.initRowsHandler(),this.onNextUpdated((function(){this.scrollRowIntoView(i),this.trigger(y.onRowMoved,t)})),this.removeSortColumn(),this.update(),!0},moveRows:function(t,e){t=d.toList(t);const i=[];return t.forEach((t=>{const e=this.getRowItem(t);e&&i.push(e)})),!!i.length&&(!(i.length>=this.getRowsLength())&&(0!==(e=d.toNum(e,!0))&&this.moveRowsHandler(i,e)))},moveRowsUp:function(t){return this.moveRows(t,-1)},moveRowsDown:function(t){return this.moveRows(t,1)},moveRowsToTop:function(t){return this.moveRows(t,-this.getRowsLength(!0))},moveRowsToBottom:function(t){return this.moveRows(t,this.getRowsLength(!0))},moveSelectedRowsUp:function(){return this.moveRows(this.getSelectedRows(),-1)},moveSelectedRowsDown:function(){return this.moveRows(this.getSelectedRows(),1)},moveSelectedRowsToTop:function(){return this.moveRows(this.getSelectedRows(),-this.getRowsLength(!0))},moveSelectedRowsToBottom:function(){return this.moveRows(this.getSelectedRows(),this.getRowsLength(!0))}},Lt={getSelectedRow:function(){let t=null;return this.forEachSelectableRow((function(e){if(e.selected)return t=e,!1})),t},getSelectedRows:function(){const t=[];return this.forEachSelectableRow((function(e){e.selected&&t.push(e)})),t.length>1&&t.sort((function(t,e){const i=t.tg_selected_index,o=e.tg_selected_index;return i>o?1:i0&&void 0!==arguments[0])||arguments[0];if(t=Boolean(t),this.globalSelectedIndex=0,t&&!this.options.selectMultiple)return this;const e=this.getAllSelectedChangedList(t);return e.length?(this.updateRowsSelectedState(e),this):this},setRowSelected:function(){return(this.options.selectMultiple?this.setRowMultipleSelected:this.setRowSingleSelected).apply(this,arguments)},setRowSingleSelected:function(t){const e=this.getRowItem(t);if(!e)return this;if(!this.isRowSelectable(e))return this;if(e.selected)return this;const i=[],o=this.getSelectedRow();return o&&o.selected&&i.push(o),e.selected||i.push(e),i.length?(this.updateRowsSelectedState(i),this):this},setRowMultipleSelected:function(t,e){if(0===arguments.length)return this;if(1===arguments.length&&!1===arguments[0])return this.selectAll(!1);const i=this.toRowItemList(t,(t=>this.isRowSelectable(t)));return i.length?!1===e?(this.setRowListUnselected(i),this):d.hasShiftKey(e)&&1===i.length?(this.setRowBetweenListSelected(i[0]),this):(this.updateRowsSelectedState(i),this):this},setRowListUnselected:function(t){const e=this.getSelectedChangedList(t,!1);e.length&&this.updateRowsSelectedState(e)},setRowBetweenListSelected:function(t){const e=this.previousSelectedRow;if(e&&e!==t){const i=this.getBetweenSelectedChangedList(e,t);if(!i.length)return;this.updateRowsSelectedState(i,!0)}else this.updateRowsSelectedState([t])},getAllSelectedChangedList:function(t){const e=[];return this.forEachSelectableRow((i=>{this.isSelectedChanged(i,t)&&e.push(i)})),e},getSelectedChangedList:function(t,e){const i=[];return t.forEach((t=>{this.isSelectedChanged(t,e)&&i.push(t)})),i},getBetweenSelectedChangedList:function(t,e){const i=t.tg_index,o=e.tg_index,n=[];if(i=o;)n.push(t),t--}return this.toRowItemList(n,(t=>this.isRowSelectable(t)&&!t.selected))},updateRowsSelectedState:function(t,e){let i;t.forEach((t=>{const e=!t.selected;t.selected=e,e&&(t.tg_selected_index=this.globalSelectedIndex++,i=t),this.renderRowSelectedState(t)})),e||(this.previousSelectedRow=i),this.renderSelectAllState(),this.onNextUpdated((()=>{this.trigger(y.onSelectChanged,t)})),this.render()},renderRowSelectedState:function(t){const e=t.tg_view_index;this.viewport.rows.includes(e)&&(this.renderRowState(t,"selected"),this.flushCell(e,this.selectColumn.tg_view_index))},renderSelectAllState:function(){this.isSelectAllVisible()&&(this.asyncRenderSelectAllState||(this.asyncRenderSelectAllState=d.microtask(this.renderSelectAllStateSync)),this.asyncRenderSelectAllState.apply(this,arguments))},renderSelectAllStateSync:function(){const t=this.getSelectAllState();if(t===this.previousSelectAllState)return;this.previousSelectAllState=t;const e=this.selectColumn,i=w(this.getColumnHeaderNode(e)).find(".tg-select-icon-all");i.length&&(i.removeClass("tg-selected tg-mixed"),t&&i.addClass(`tg-${t}`))},getSelectAllState:function(){let t=0;this.forEachSelectableRow((e=>{t+=1}));const e=this.getSelectedRows().length;let i="mixed";return 0===e?(i="",this.previousSelectedRow=null):e===t&&(i="selected"),i},isSelectAllVisible:function(){const t=this.options;return!!(t.selectVisible&&t.selectAllVisible&&t.selectMultiple)}},xt={setRowHover:function(t,e){const i=this.getRowItem(t);return i?(this.renderRowHover(i,e),this):this},renderRowHover:function(t,e){if(this.previousHover&&(this.previousHover.removeClass("tg-hover"),this.previousHover=null),!e)return this;if(!1===this.rowHoverable)return;if(!1===t.hoverable)return this;if(t.tg_frozen&&!this.options.frozenRowHoverable)return this;const i=t.tg_view_index;return this.previousHover=this.$body.find(`.tg-row[row='${i}']`).addClass("tg-hover"),this},setRowState:function(t,e){let i=!(arguments.length>2&&void 0!==arguments[2])||arguments[2];const o=this.getRowItem(t);return o?(o.tg_state_names||(o.tg_state_names=new Set),o.tg_state_names.add(e),o[e]=i,this.renderRowState(o,e),this):this},renderRowState:function(t,e){const i=this.getRowNodesByIndex(t.tg_view_index);if(i){const o=`tg-${e}`;t[e]?i.addClass(o):i.removeClass(o)}}},zt={getRowItem:function(t){return d.isNum(t)?(t<0&&(t=this.rowsInfo.length+t),this.rowsInfo.indexCache[t]):t?d.isNum(t.tg_index)?t:this.getRowItemById(t.id||t):void 0},getRowItemById:function(t){return this.getRowItemBy("id",t)},getRowItemBy:function(t,e){if(void 0!==e)return this.rowsInfo.indexCache.find((i=>i[t]===e))},getRowsLength:function(t){return t?this.rowsInfo.length:this.viewRows.length},getViewRowItem:function(t){return this.viewRows[t]},getPreRenderRowInfo:function(t){const e={rows:[],rowNotFound:!1,benchmark:0};if(!t.length)return 0===this.getRowsLength()&&(e.rowNotFound=!0),e;const i=[],o=this.frozenInfo.row;return t.forEach((t=>{this.getRowCache(t)?t>o&&i.push(t):e.rows.push(t)})),i.length&&(e.benchmark=Math.min.apply(Math,i)),e.rows.sort((function(t,i){return t{this.renderRowNodes(t,e.benchmark)}))},createRowNode:function(t,e,i,o,n,s){const r=document.createElement("div");return r.setAttribute("row",t),r.className=i,o&&(r.style.cssText=o),r.style.top=`${n}px`,s!==this.options.rowHeight&&(r.style.height=`${s}px`,r.style.lineHeight=`${s}px`),this.setNodeDataCache(r,{row:t,rowItem:e,rowNode:r}),r},appendRowNode:function(t,e,i,o){ithis.frozenInfo.row))return e;e-=this.frozenRowsHeight}return e-=this.scrollTopOffset,e},getRowTop:function(t){let e=t.tg_top;return t.tg_frozen||(e-=this.frozenRowsHeight),e},getRowVPos:function(t){const e=this.frozenInfo.bottom,i=this.frozenInfo.row;let o="top";return this.frozenInfo.rows&&(t<=i?e&&(o="bottom"):e||(o="bottom")),o},getRowCanvas:function(t,e){return"top"===t?"left"===e?this.$bodyTL:this.$bodyTR:"left"===e?this.$bodyBL:this.$bodyBR}},Mt={CHANGE:"change",START:"start",END:"end"},Pt={h:{type:"h",className:"tg-scrollbar-h",offset:"left",size:"width",page:"pageX",axis:"x",offsetName:"offsetX"},v:{type:"v",className:"tg-scrollbar-v",offset:"top",size:"height",page:"pageY",axis:"y",offsetName:"offsetY"}};class Nt extends k{static EVENT=Mt;static H="h";static V="v";type="h";settings={};size=0;viewSize=0;bodySize=0;trackSize=0;position=0;scale=0;thumbPosition=0;thumbScale=0;constructor(t,e){super(),this.settings=Pt[t]||Pt.h,this.type=this.settings.type,this.id=d.uid(4,`tg-scrollbar-${this.type}-`),this.$holder=w(e),this.$holder.find(`.${this.settings.className}`).remove(),this.options=this.generateOptions()}generateOptions(t){return d.merge({size:15,round:!1,blank:!1,motionDuration:200},t)}updateOptions(t){this.options=this.generateOptions(t);let e=this.options.size;d.isNum(e)||(e=d.toNum(e)),e=Math.round(e),e=Math.max(e,0),e=Math.min(e,30),this.size=e}create(){this.$container=w('
    ').appendTo(this.$holder),this.$container.attr("id",this.id),this.$container.addClass(d.classMap(["tg-scrollbar",this.settings.className,{"tg-scrollbar-round":this.options.round}])),this.$track=this.$container.find(".tg-scrollbar-track"),this.$thumb=this.$container.find(".tg-scrollbar-thumb"),this.thumbDrag=new O,this.thumbDrag.bind(O.EVENT.DRAG_START,((t,e)=>{this.thumbDragStart(e)})).bind(O.EVENT.DRAG_MOVE,((t,e)=>{this.thumbDragMove(e)})).bind(O.EVENT.DRAG_END,((t,e)=>{this.thumbDragEnd(e)}));const t=this.$container.get(0);return this.scrollEvents={mousedown:{handler:e=>{e.target.classList.contains("tg-scrollbar-thumb")?this.thumbMouseDownHandler(e):(this.trackEvents={mouseup:{handler:t=>{this.trackMouseupHandler(t)},options:{once:!0}}},d.bindEvents(this.trackEvents,t),this.trackMousedownHandler(e))},options:!0},selectstart:{handler:t=>{d.preventDefault(t)},options:!0}},d.bindEvents(this.scrollEvents,t),this}getBlank(){return this.options.blank}getSize(){return this.size}getViewSize(){return this.viewSize}getBodySize(){return this.bodySize}getTrackMouseDirection(){let t=1;return this.trackMousePosition0){const i=this.getMaxThumbPosition();t=Math.round(i*this.position/e),t=d.clamp(t,0,i)}return this.setThumbPosition(t),this}trackMousedownHandler(t){return this.motionStop(),this.trackMousePosition=this.getTrackMousePos(t),this.motionStart(),this}trackMouseupHandler(t){return d.unbindEvents(this.trackEvents),this.motionStop(),this.motionStarted||(this.trackMousePosition=this.getTrackMousePos(t),this.trackScrollHandler(),this.triggerEvent()),this}trackScrollHandler(){const t=Math.max(0,this.viewSize-20)*this.getTrackMouseDirection();return this.setOffset(t),this}motionStop(){return this.motion&&(this.motion.destroy(),this.motion=null),this}motionStart(){const t=this.position,e=Math.round(this.trackMousePosition/this.viewSize*this.getMaxPosition());return this.motionStarted=!1,this.motion=new B,this.motion.bind(B.EVENT.MOTION_START,((t,e)=>{this.motionStarted=!0})),this.motion.bind(B.EVENT.MOTION_MOVE,((t,e)=>{this.motionUpdateHandler(t,e)})),this.motion.start({duration:this.options.motionDuration,from:t,till:e}),this}motionUpdateHandler(t,e){e!==this.position&&(this.setPosition(e),this.triggerEvent())}thumbMouseDownHandler(t){this.$thumb.addClass("tg-scrollbar-thumb-hold"),this.thumbDrag.start(t,{target:this.$thumb})}thumbDragStart(t){this.motionStop(),t.thumbPositionStart=this.thumbPosition,this.trigger(Mt.START)}thumbDragMove(t){let e=t.thumbPositionStart+t[this.settings.offsetName];const i=this.getMaxThumbPosition();e=d.clamp(e,0,i),this.setThumbPosition(e);let o=0;i>0&&(o=d.per(e/i)*this.getMaxPosition(),o=Math.round(o)),this.position=o,this.triggerEvent()}thumbDragEnd(t){this.$thumb&&this.$thumb.removeClass("tg-scrollbar-thumb-hold"),this.trigger(Mt.END)}triggerEvent(){this.trigger(Mt.CHANGE,this.position)}getPosition(){return this.position}setPosition(t){t=d.toNum(t,!0);const e=this.getMaxPosition();t=d.clamp(t,0,e),this.position=t,this.updateThumbPosition()}getMaxPosition(){return this.bodySize-this.viewSize}updatePosition(){const t=this.getMaxPosition(),e=d.clamp(this.position,0,t);this.position=e}setOffset(t){t=d.toNum(t);const e=this.position+t;return this.setPosition(e),this}getScale(){return this.scale}setScale(t){return t=d.per(t),this.scale=t,this.scaleChangeHandler(),this}scaleChangeHandler(){let t=Math.round(this.viewSize*this.scale);if(t=Math.max(t,Math.round(1.5*this.options.size)),t=Math.min(t,this.viewSize-1),this.thumbSize=t,this.$thumb){const t={};"h"===this.type?(t.height=this.size,t.width=this.thumbSize):(t.width=this.size,t.height=this.thumbSize),this.$thumb.css(t)}}updateTrackSize(){const t={};return"h"===this.type?(t.width=this.trackSize,t.height=this.size):(t.height=this.trackSize,t.width=this.size),this.$container.css(t),this}updateThumbSize(){let t=0;return this.bodySize&&(t=this.trackSize/this.bodySize),this.setScale(t),this}parseSize(t){return t=d.toNum(t),t=Math.round(t),t=Math.max(t,0)}updateSize(t,e,i){t=this.parseSize(t),this.viewSize=t,e=this.parseSize(e),this.bodySize=e,i=d.isNum(i)?this.parseSize(i):t,this.trackSize=i,this.previousFadeIn=null}fade(t){return!(!this.$container||!this.size)&&(this.previousFadeIn!==t&&(this.previousFadeIn=t,t?this.$container.hasClass("tg-fade-out")&&this.$container.removeClass("tg-fade-out").addClass("tg-fade-in"):this.$container.removeClass("tg-fade-in").addClass("tg-fade-out"),!0))}show(){if(this.updatePosition(),!this.getBlank())return!this.$container&&this.size>0&&this.create(),this.$container?(this.updateTrackSize(),this.updateThumbSize(),this):this;this.remove()}hide(){return this.updatePosition(),this.remove(),this}remove(){if(this.motionStop(),d.unbindEvents(this.scrollEvents),d.unbindEvents(this.trackEvents),this.thumbDrag&&(this.thumbDrag.destroy(),this.thumbDrag=null),!this.$container)return this;this.$thumb=null,this.$track=null,this.$container.remove(),this.$container=null}destroy(){return this.remove(),this}}const _t={CHANGE:"change",START:"start",END:"end"};class kt extends k{static EVENT=_t;visible=!0;constructor(t,e){super(),this.id=d.uid(4,`tg-scroll-pane-${e}-`),this.gradientInfo=[],this.$container=w(t).attr("id",this.id),this.$container.addClass("tg-scroll-pane"),this.$scrollView=this.$container.find(".tg-scroll-view"),this.$scrollBody=this.$scrollView.find(".tg-scroll-body"),this.scrollbarH=new Nt(Nt.H,this.$container),this.scrollbarH.bind(Nt.EVENT.CHANGE,((t,e)=>{this.scrollHChangeHandler()})).bind(Nt.EVENT.START,(t=>{this.scrollStartEndHandler(!0)})).bind(Nt.EVENT.END,(t=>{this.scrollStartEndHandler()})),this.scrollbarV=new Nt(Nt.V,this.$container),this.scrollbarV.bind(Nt.EVENT.CHANGE,((t,e)=>{this.scrollVChangeHandler()})).bind(Nt.EVENT.START,(t=>{this.scrollStartEndHandler(!0)})).bind(Nt.EVENT.END,(t=>{this.scrollStartEndHandler()})),this.options=this.generateOptions()}generateOptions(t){return d.merge({scrollbarH:{},scrollbarV:{},scrollbarFade:!1,scrollSizeOnKeyPress:20,gradient:30},t)}scrollStartEndHandler(t){t?this.trigger(_t.START):this.trigger(_t.END)}show(){return this.$container.show(),this.visible=!0,this}hide(){return this.$container.hide(),this.visible=!1,this}width(){return this.scrollPaneW}height(){return this.scrollPaneH}render(t){return this.visible?(this.options=this.generateOptions(t),this.update(),this):this}update(){this.scrollPaneW=this.options.scrollPaneW,this.scrollPaneH=this.options.scrollPaneH,this.scrollBodyW=this.options.scrollBodyW,this.scrollBodyH=this.options.scrollBodyH,this.updateScrollbar()}setGroupH(t){this.groupH=d.toList(t)}setGroupV(t){this.groupV=d.toList(t)}updateGroupH(){if(!d.isList(this.groupH))return this;const t=this.scrollbarH.getPosition();return this.groupH.forEach((function(e){e&&e.updateScrollHFromGroup(t)})),this}updateGroupV(){if(!d.isList(this.groupV))return this;const t=this.scrollbarV.getPosition();return this.groupV.forEach((function(e){e&&e.updateScrollVFromGroup(t)})),this}updateGroupList(){this.updateGroupH(),this.updateGroupV()}updateScrollHFromGroup(t){this.scrollbarH.getPosition()!==t&&(this.scrollbarH.setPosition(t),this.updateScrollLeft(),this.triggerEvent())}updateScrollVFromGroup(t){this.scrollbarV.getPosition()!==t&&(this.scrollbarV.setPosition(t),this.updateScrollTop(),this.triggerEvent())}setPosition(t,e){return this.scrollbarH.setPosition(t),this.scrollbarV.setPosition(e),this.updateScrollLeft(),this.updateScrollTop(),this.updateGroupList(),this}updateScrollbar(){this.scrollbarH.updateOptions(this.options.scrollbarH),this.scrollbarV.updateOptions(this.options.scrollbarV),this.updateScrollState(),this.updateScrollView(),this.updateScrollTrack(),this.scrollbarH.updateSize(this.scrollViewW,this.scrollBodyW,this.scrollTrackW),this.scrollbarV.updateSize(this.scrollViewH,this.scrollBodyH,this.scrollTrackH),this.hasScrollH?(this.scrollbarH.show(),this.scrollbarH.setPosition(this.scrollbarH.getPosition())):this.scrollbarH.hide(),this.hasScrollV?(this.scrollbarV.show(),this.scrollbarV.setPosition(this.scrollbarV.getPosition())):this.scrollbarV.hide(),this.updateScrollLeft(),this.updateScrollTop(),this.updateGroupList()}updateScrollState(){const t=this.scrollbarH.getSize(),e=this.scrollbarV.getSize(),i=this.scrollbarH.getBlank(),o=this.scrollbarV.getBlank(),n=this.options.scrollbarFade;let s=!1,r=0;(function(){(this.scrollPaneWe&&t.push("left"),ie&&t.push("top"),o{const i=`tg-gradient-${e}`;t.includes(e)?this.$container.addClass(i):this.$container.removeClass(i)})))}getScrollLeft(){return this.scrollbarH.getPosition()}getScrollTop(){return this.scrollbarV.getPosition()}getMaxScrollLeft(){return this.scrollbarH.getMaxPosition()}getMaxScrollTop(){return this.scrollbarV.getMaxPosition()}getScrollTopOffset(){const t=this.getScrollTop();return t-t%1e4}triggerEvent(){this.trigger(_t.CHANGE,{scrollLeft:this.getScrollLeft(),scrollTop:this.getScrollTop()})}scrollHChangeHandler(){this.updateScrollLeft(),this.updateGroupList(),this.triggerEvent()}scrollVChangeHandler(){this.updateScrollTop(),this.updateGroupList(),this.triggerEvent()}setOffsetH(t){const e=this.getScrollLeft();this.scrollbarH.setOffset(t);return this.getScrollLeft()!==e&&(this.updateScrollLeft(),this.updateGroupList(),this.triggerEvent(),!0)}setOffsetV(t){const e=this.getScrollTop();this.scrollbarV.setOffset(t);return this.getScrollTop()!==e&&(this.updateScrollTop(),this.updateGroupList(),this.triggerEvent(),!0)}mouseWheelHandler(t){const e=t.deltaX,i=t.deltaY,o=Math.abs(e);if(o>Math.abs(i)){if(this.hasScrollH)return this.setOffsetH(e)}else{if(this.hasScrollV)return this.setOffsetV(i);if(this.hasScrollH&&!o)return this.setOffsetH(i)}return!1}keyPageUpHandler(t){return this.setOffsetV(-this.scrollViewH)}keyPageDownHandler(t){return this.setOffsetV(this.scrollViewH)}keyEndHandler(t){return this.setOffsetV(this.scrollBodyH)}keyHomeHandler(t){return this.setOffsetV(-this.scrollBodyH)}keyLeftHandler(t){return this.setOffsetH(-this.options.scrollSizeOnKeyPress)}keyUpHandler(t){return this.setOffsetV(-this.options.scrollSizeOnKeyPress)}keyRightHandler(t){return this.setOffsetH(this.options.scrollSizeOnKeyPress)}keyDownHandler(t){return this.setOffsetV(this.options.scrollSizeOnKeyPress)}destroy(){return this.visible=!1,this.groupH=null,this.groupV=null,this.scrollbarV&&(this.scrollbarV.destroy(),this.scrollbarV=null),this.scrollbarH&&(this.scrollbarH.destroy(),this.scrollbarH=null),this.$container=null,this.$scrollView=null,this.$scrollBody=null,this}}const Vt={initScrollPane:function(){this.initFrozenStyle(),this.createScrollPane()},initFrozenStyle:function(){const t={HL:{container:this.$paneHL,cls:[]},HR:{container:this.$paneHR,cls:[]},TL:{container:this.$paneTL,cls:[]},TR:{container:this.$paneTR,cls:[]},BL:{container:this.$paneBL,cls:[]},BR:{container:this.$paneBR,cls:[]}},e="tg-frozen-h";this.frozenInfo.rows&&(this.frozenInfo.bottom?(t.BL.cls.push(e),t.BR.cls.push(e)):(t.TL.cls.push(e),t.TR.cls.push(e)));const i="tg-frozen-v",o="tg-frozen-line-v";this.frozenInfo.columns&&(this.frozenInfo.right?(t.HR.cls.push(i),t.TR.cls.push(i),t.BR.cls.push(i)):(t.HL.cls.push(i),t.TL.cls.push(i),t.BL.cls.push(i)),t.HL.cls.push(o),t.TL.cls.push(o),t.BL.cls.push(o));const n="tg-frozen",s=[n,e,i,o].join(" ");Object.keys(t).forEach((function(e){const i=t[e],o=i.container;o.removeClass(s);const r=i.cls;if(!r.length)return;const l=[n].concat(r).join(" ");o.addClass(l)}))},createScrollPane:function(){this.removeScrollPane(),this.scrollPaneMap={HL:new kt(this.$paneHL,"header-left"),HR:new kt(this.$paneHR,"header-right"),TL:new kt(this.$paneTL,"top-left"),TR:new kt(this.$paneTR,"top-right"),BL:new kt(this.$paneBL,"bottom-left"),BR:new kt(this.$paneBR,"bottom-right")},this.scrollPaneMap.BR.setGroupH([this.scrollPaneMap.HR,this.scrollPaneMap.TR]),this.scrollPaneMap.TR.setGroupH([this.scrollPaneMap.HR,this.scrollPaneMap.BR]),this.scrollPaneMap.BL.setGroupH([this.scrollPaneMap.HL,this.scrollPaneMap.TL]),this.scrollPaneMap.TL.setGroupH([this.scrollPaneMap.HL,this.scrollPaneMap.BL]),this.scrollPaneMap.BR.setGroupV(this.scrollPaneMap.BL),this.scrollPaneMap.BL.setGroupV(this.scrollPaneMap.BR),this.scrollPaneMap.TR.setGroupV(this.scrollPaneMap.TL),this.scrollPaneMap.TL.setGroupV(this.scrollPaneMap.TR),this.initActiveScrollPane(),this.initPaneVisibility()},initActiveScrollPane:function(){const t=this.getScrollPaneVP(),e=this.getScrollPaneHP(),i=`${t}${e}`;this.scrollPane=this.scrollPaneMap[i],this.scrollPane.bind(kt.EVENT.CHANGE,((t,e)=>{this.scrollPaneChangeHandler(t,e)})).bind(kt.EVENT.START,(t=>{this.rowHoverable=!1})).bind(kt.EVENT.END,(t=>{this.rowHoverable=!0}));let o={L:"L",R:"L"};this.frozenInfo.columns&&this.frozenInfo.right&&(o={L:"R",R:"L"});const n=`${t}${o[e]}`;this.scrollPaneFrozen=this.scrollPaneMap[n]},getScrollPaneVP:function(){return this.frozenInfo.rows&&!this.frozenInfo.bottom?"B":"T"},getScrollPaneHP:function(){return this.frozenInfo.columns&&!this.frozenInfo.right?"R":"L"},initPaneVisibility:function(){this.scrollPaneMap.HL.show(),this.scrollPaneMap.TL.show(),this.frozenInfo.columns?(this.scrollPaneMap.HR.show(),this.scrollPaneMap.TR.show(),this.frozenInfo.rows?(this.scrollPaneMap.BL.show(),this.scrollPaneMap.BR.show()):(this.scrollPaneMap.BL.hide(),this.scrollPaneMap.BR.hide())):(this.scrollPaneMap.HR.hide(),this.scrollPaneMap.TR.hide(),this.scrollPaneMap.BR.hide(),this.frozenInfo.rows?this.scrollPaneMap.BL.show():this.scrollPaneMap.BL.hide())},scrollPaneChangeHandler:function(t,e){this.hideColumnLine(),this.scrollLeft=e.scrollLeft,this.scrollTop=e.scrollTop,this.scrollRenderHandler()},scrollbarFadeInOutHandler:function(t,e){this.options.scrollbarFade&&(e?this.updateScrollPaneFade(!0):this.options.scrollbarFadeTimeout||this.updateScrollPaneFade(!1))},updateScrollPaneFade:function(t){if(!this.options.scrollbarFade)return;this.updateScrollPaneFadeSync(t);const e=this.options.scrollbarFadeTimeout;e&&(clearTimeout(this.timeout_fade),this.timeout_fade=setTimeout((()=>{this.updateScrollPaneFadeSync(!1)}),e))},updateScrollPaneFadeSync:function(t){if(this.previousScrollbarFadeIn===t)return;this.previousScrollbarFadeIn=t;const e=[];Object.keys(this.scrollPaneMap).forEach((t=>{const i=this.scrollPaneMap[t];i.hasScrollbar()&&e.push(i)})),e.length&&e.forEach((function(e){e.fade(t)}))},updateScrollPane:function(){const t=this.getScrollbarOptions();this.scrollPaneMap.HL.render(this.getScrollPaneOptions({scrollPaneW:this.paneWidthL,scrollPaneH:this.headerHeight,scrollBodyW:this.bodyWidthL,scrollBodyH:this.headerHeight,scrollbarV:t.HLV,scrollbarH:t.HLH})),this.scrollPaneMap.HR.render(this.getScrollPaneOptions({scrollPaneW:this.paneWidthR,scrollPaneH:this.headerHeight,scrollBodyW:this.bodyWidthR,scrollBodyH:this.headerHeight,scrollbarV:t.HRV,scrollbarH:t.HRH})),this.scrollPaneMap.TL.render(this.getScrollPaneOptions({scrollPaneW:this.paneWidthL,scrollPaneH:this.paneHeightT,scrollBodyW:this.bodyWidthL,scrollBodyH:this.bodyHeightT,scrollbarV:t.TLV,scrollbarH:t.TLH})),this.scrollPaneMap.TR.render(this.getScrollPaneOptions({scrollPaneW:this.paneWidthR,scrollPaneH:this.paneHeightT,scrollBodyW:this.bodyWidthR,scrollBodyH:this.bodyHeightT,scrollbarV:t.TRV,scrollbarH:t.TRH})),this.scrollPaneMap.BL.render(this.getScrollPaneOptions({scrollPaneW:this.paneWidthL,scrollPaneH:this.paneHeightB,scrollBodyW:this.bodyWidthL,scrollBodyH:this.bodyHeightB,scrollbarV:t.BLV,scrollbarH:t.BLH})),this.scrollPaneMap.BR.render(this.getScrollPaneOptions({scrollPaneW:this.paneWidthR,scrollPaneH:this.paneHeightB,scrollBodyW:this.bodyWidthR,scrollBodyH:this.bodyHeightB,scrollbarV:t.BRV,scrollbarH:t.BRH})),this.scrollLeft=this.getScrollLeft(),this.scrollTop=this.getScrollTop(),this.updateScrollPaneFade(Boolean(this.options.scrollbarFadeTimeout))},getScrollPaneOptions:function(t){const e=this.options;return t.scrollbarFade=e.scrollbarFade,t.gradient=d.clamp(d.toNum(e.scrollPaneGradient,!0),0,100),t},getScrollbarOptions:function(){const t=this.options.scrollbarRound,e={};return["HLH","HLV","HRH","HRV","TLH","TLV","TRH","TRV","BLH","BLV","BRH","BRV"].forEach((function(i){e[i]={size:0,round:t,blank:!1}})),this.scrollbarOptionsHandler(e),this.scrollbarFadeHandler(e),e},scrollbarOptionsHandler:function(t){const e=this.scrollbarSizeH,i=this.scrollbarSizeV;this.scrollbarHeaderHandler(t,e,i),this.frozenInfo.columns?this.frozenInfo.rows?this.scrollbarC1R1Handler(t,e,i):this.scrollbarC1R0Handler(t,e,i):this.frozenInfo.rows?this.scrollbarC0R1Handler(t,e,i):this.scrollbarC0R0Handler(t,e,i)},scrollbarFadeHandler:function(t){if(this.options.scrollbarFade)for(const e in t)if(d.hasOwn(t,e)){const i=t[e];i.size>0&&i.blank&&(i.blank=!1,i.size=0)}},scrollbarHeaderHandler:function(t,e,i){this.hasVScroll&&(this.frozenInfo.columns?(t.HRV.size=i,t.HRV.blank=1):(t.HLV.size=i,t.HLV.blank=1))},scrollbarC0R0Handler:function(t,e,i){t.TLH.size=e,t.TLV.size=i},scrollbarC0R1Handler:function(t,e,i){this.frozenInfo.bottom?this.scrollbarC0R1B1Handler(t,e,i):this.scrollbarC0R1B0Handler(t,e,i)},scrollbarC0R1B1Handler:function(t,e,i){t.BLH.size=e,t.TLV.size=i,this.hasVScroll&&(t.BLV.size=i,t.BLV.blank=1)},scrollbarC0R1B0Handler:function(t,e,i){t.BLH.size=e,t.BLV.size=i,this.hasVScroll&&(t.TLV.size=i,t.TLV.blank=1)},scrollbarC1R0Handler:function(t,e,i){this.frozenInfo.right?this.scrollbarC1R0R1Handler(t,e,i):this.scrollbarC1R0R0Handler(t,e,i)},scrollbarC1R0R1Handler:function(t,e,i){this.hasHScroll&&(t.TLH.size=e,this.scrollPaneHidden?(t.TRH.size=e,t.TLH.blank=!0):(t.TRH.size=e,t.TRH.blank=!0)),t.TRV.size=i},scrollbarC1R0R0Handler:function(t,e,i){this.hasHScroll&&(t.TRH.size=e,this.scrollPaneHidden?(t.TLH.size=e,t.TRH.blank=!0):(t.TLH.size=e,t.TLH.blank=!0)),t.TRV.size=i},scrollbarC1R1Handler:function(t,e,i){this.frozenInfo.right?this.frozenInfo.bottom?this.scrollbarC1R1R1B1Handler(t,e,i):this.scrollbarC1R1R1B0Handler(t,e,i):this.frozenInfo.bottom?this.scrollbarC1R1R0B1Handler(t,e,i):this.scrollbarC1R1R0B0Handler(t,e,i)},scrollbarC1R1R1B1Handler:function(t,e,i){this.hasHScroll&&(t.BLH.size=e,this.scrollPaneHidden&&(t.BRH.size=e,t.BLH.blank=!0)),t.TRV.size=i,this.hasVScroll&&(t.BRV.size=i,t.BRV.blank=1)},scrollbarC1R1R1B0Handler:function(t,e,i){this.hasHScroll&&(t.BLH.size=e,this.scrollPaneHidden?(t.BRH.size=e,t.BLH.blank=!0):(t.BRH.size=e,t.BRH.blank=!0)),t.BRV.size=i,this.hasVScroll&&(t.TRV.size=i,t.TRV.blank=1)},scrollbarC1R1R0B1Handler:function(t,e,i){this.hasHScroll&&(t.BRH.size=e,this.scrollPaneHidden&&(t.BLH.size=e,t.BRH.blank=!0)),t.TRV.size=i,this.hasVScroll&&(t.BRV.size=i,t.BRV.blank=1)},scrollbarC1R1R0B0Handler:function(t,e,i){this.hasHScroll&&(t.BRH.size=e,this.scrollPaneHidden?(t.BLH.size=e,t.BRH.blank=!0):(t.BLH.size=e,t.BLH.blank=!0)),t.BRV.size=i,this.hasVScroll&&(t.TRV.size=i,t.TRV.blank=1)},removeScrollPane:function(){clearTimeout(this.timeout_fade),this.previousScrollbarFadeIn=null,this.scrollPaneMap&&(Object.keys(this.scrollPaneMap).forEach((t=>{const e=this.scrollPaneMap[t];e&&e.destroy()})),this.scrollPaneMap=null,this.scrollPane=null,this.scrollPaneFrozen=null)}},Ot={updateScrollState:function(){this.updateGlobalScrollInfo(),this.updateHScrollState(),this.updateVScrollState(),this.updateBlankColumnWidth(),this.scrollStateChanged=!1,this.previousHasHScroll===this.hasHScroll&&this.previousHasVScroll===this.hasVScroll||(this.scrollStateChanged=!0,this.previousHasHScroll=this.hasHScroll,this.previousHasVScroll=this.hasVScroll,this.trigger(y.onScrollStateChanged,{hasHScroll:this.hasHScroll,hasVScroll:this.hasVScroll}))},updateGlobalScrollInfo:function(){this.totalRowsLength=this.getRowsLength(),this.totalRowsHeight=this.getRowsHeight(),this.frozenRowsHeight=this.getFrozenRowsHeight(),this.scrollRowsHeight=this.totalRowsHeight-this.frozenRowsHeight,this.totalRowsHeight=Math.max(this.totalRowsHeight,1),this.scrollRowsHeight=Math.max(this.scrollRowsHeight,1),this.flushRowFrom(this.totalRowsLength)},updateHScrollState:function(){if(this.hasHScroll=!0,this.updateScrollPaneHiddenState(),this.updateHScrollByScrollPaneHidden(),this.scrollPaneHidden)return;this.containerWidth-this.columnsWidth>=0&&(this.hasHScroll=!1)},getScrollPaneCurrentWidth:function(){return this.frozenInfo.right?this.bodyWidth-this.columnsWidthR:this.bodyWidth-this.columnsWidthL},updateHScrollByScrollPaneHidden:function(){if(this.scrollPaneHidden){this.hasHScroll=!1;this.getScrollPaneCurrentWidth()=this.totalRowsHeight&&(this.hasVScroll=!1)}},updateBlankColumnWidth:function(){let t=this.containerWidth-this.columnsWidth;!this.hasVScroll||this.hasHScroll||this.options.scrollbarFade||(t-=this.scrollbarSizeV),this.scrollPaneHidden&&(t=0),this.hasHScroll||(t>=0?(this.frozenInfo.columns?this.columnsWidthR+=t:this.columnsWidthL+=t,this.blankColumn.tg_width=t):this.hasHScroll=!0)}},$t={scrollToRow:function(t){const e=this.getRowItem(t);return this.scrollToItem(e,null),this},scrollToColumn:function(t){const e=this.getColumnItem(t);return this.scrollToItem(null,e),this},scrollToCell:function(t,e){const i=this.getRowItem(t),o=this.getColumnItem(e);return this.scrollToItem(i,o),this},scrollToFirstRow:function(){return this.setScrollTop(0),this},scrollToLastRow:function(){const t=this.getViewRows(),e=t[t.length-1],i=this.getScrollRowPosition(e);if(d.isNum(i))return this.setScrollTop(i),this},scrollToFirstColumn:function(){return this.setScrollLeft(0),this},scrollToLastColumn:function(t){const e=this.getViewColumns();let i=e[e.length-2];t&&(i=e[e.length-1]);const o=this.getScrollColumnPosition(i);if(d.isNum(o))return this.setScrollLeft(o),this},scrollRowIntoView:function(t){const e=this.getRowItem(t);return this.scrollItemIntoView(e,null),this},scrollColumnIntoView:function(t){const e=this.getColumnItem(t);return this.scrollItemIntoView(null,e),this},scrollCellIntoView:function(t,e){const i=this.getRowItem(t),o=this.getColumnItem(e);return this.scrollItemIntoView(i,o),this},setScroll:function(t,e){return t===this.scrollLeft&&e===this.scrollTop||(this.scrollLeft=t,this.scrollTop=e,this.scrollHandler()),this},setScrollLeft:function(t){return t===this.scrollLeft||(this.scrollLeft=t,this.scrollHandler()),this},setScrollTop:function(t){return t===this.scrollTop||(this.scrollTop=t,this.scrollHandler()),this},getScrollRowPosition:function(t){if(!t)return;let e=t.tg_view_index;return e-=this.frozenInfo.rows,e>=0?this.getRowTop(t):void 0},getScrollColumnPosition:function(t){if(!t)return;let e=t.tg_left;return this.frozenInfo.columns&&(e-=this.bodyWidthL),e>=0?e:void 0},scrollToItem:function(t,e){return this.scrollToChanged=!1,this.scrollToRowHandler(t),this.scrollToColumnHandler(e),this.scrollToChanged?(this.scrollHandler(),this):this},scrollToRowHandler:function(t){if(!t)return;const e=this.getScrollRowPosition(t);d.isNum(e)&&e!==this.scrollTop&&(this.scrollTop=e,this.scrollToChanged=!0)},scrollToColumnHandler:function(t){if(!t)return;const e=this.getScrollColumnPosition(t);d.isNum(e)&&e!==this.scrollLeft&&(this.scrollLeft=e,this.scrollToChanged=!0)},scrollItemIntoView:function(t,e){return this.scrollIntoViewChanged=!1,this.scrollRowIntoViewHandler(t),this.scrollColumnIntoViewHandler(e),this.scrollIntoViewChanged?(this.scrollHandler(),this):this},scrollRowIntoViewHandler:function(t){if(!t)return;const e=this.getScrollRowPosition(t);if(!d.isNum(e))return;if(ethis.scrollTop+o){const t=e-(o-i);this.scrollTop=t,this.scrollIntoViewChanged=!0}},scrollColumnIntoViewHandler:function(t){if(!t)return;const e=this.getScrollColumnPosition(t);if(!d.isNum(e))return;if(ethis.scrollLeft+o){const t=e-(o-i);this.scrollLeft=t,this.scrollIntoViewChanged=!0}},scrollOnInit:function(){const{scrollLeft:t,scrollTop:e,scrollColumn:i,scrollRow:o}=this.renderSettings;this.scrollIntoViewChanged=!1,Number.isInteger(t)&&t!==this.scrollLeft&&(this.scrollLeft=t,this.scrollIntoViewChanged=!0),Number.isInteger(e)&&e!==this.scrollTop&&(this.scrollTop=e,this.scrollIntoViewChanged=!0),i&&this.scrollColumnIntoViewHandler(i),o&&this.scrollRowIntoViewHandler(o),this.scrollIntoViewChanged&&this.scrollPane.setPosition(this.scrollLeft,this.scrollTop)},scrollHandler:function(){this.scrollPane.setPosition(this.scrollLeft,this.scrollTop),this.scrollRenderHandler()},scrollRenderHandler:function(){this.previousScrollLeft===this.scrollLeft&&this.previousScrollTop===this.scrollTop||(this.previousScrollLeft=this.scrollLeft,this.previousScrollTop=this.scrollTop,this.onNextUpdated((()=>{this.updateScrollPaneFade(!0),this.trigger(y.onScroll,{scrollLeft:this.scrollLeft,scrollTop:this.scrollTop})})),this.render())},scrollTouchStartHandler:function(t,e){this.hideColumnLine(),this.scrollTouchLeft=this.getScrollLeft(),this.scrollTouchTop=this.getScrollTop(),this.scrollMaxTouchLeft=this.getMaxScrollLeft(),this.scrollMaxTouchTop=this.getMaxScrollTop()},getTouchOrientation:function(t){return t.orientation?t.orientation:[e.LEFT,e.RIGHT].includes(t.direction)?(t.orientation||(t.orientation="Y"),t.orientation):[e.UP,e.DOWN].includes(t.direction)?(t.orientation||(t.orientation="X"),t.orientation):void 0},scrollTouchMoveHandler:function(t,e){if(e.touchLength>1)return;let i=e.offsetX,o=e.offsetY;const n=this.getTouchOrientation(e);"X"===n?i=0:"Y"===n&&(o=0);let s=this.scrollTouchLeft-i,r=this.scrollTouchTop-o;s=d.clamp(s,0,this.scrollMaxTouchLeft),r=d.clamp(r,0,this.scrollMaxTouchTop);let l=!1;this.scrollPaneHidden&&(l=this.scrollPaneFrozen.setOffsetH(-e.moveX),s=0);const h=this.getScrollLeft(),a=this.getScrollTop();(s!==h||r!==a||l)&&(d.preventDefault(e.e),this.setScroll(s,r))},scrollTouchEndHandler:function(){this.protectedItem=null},scrollTouchInertiaHandler:function(t,e){const i=this.getScrollLeft(),o=this.getScrollTop(),n=i-e.touchInertiaX,s=o-e.touchInertiaY;this.setScroll(n,s)},getScrollViewWidth:function(){let t=this.getScrollPaneWidth();return this.frozenInfo.right||(t-=this.getScrollbarWidth()),t},getScrollViewHeight:function(){let t=this.getScrollPaneHeight();return this.frozenInfo.bottom||(t-=this.getScrollbarHeight()),t},getScrollPaneWidth:function(){return this.scrollPane.width()},getScrollPaneHeight:function(){return this.scrollPane.height()},getScrollbarWidth:function(){return this.hasVScroll&&!this.options.scrollbarFade?this.scrollbarSizeV:0},getScrollbarHeight:function(){return this.hasHScroll&&!this.options.scrollbarFade?this.scrollbarSizeH:0},getScrollLeft:function(){return this.scrollPane.getScrollLeft()},getScrollTop:function(){return this.scrollPane.getScrollTop()},getMaxScrollLeft:function(){return this.scrollPane.getMaxScrollLeft()},getMaxScrollTop:function(){return this.scrollPane.getMaxScrollTop()}};class Dt{constructor(t){this.options=this.generateOptions(t)}generateOptions(t){return d.merge({ignore:null,sortField:"",sortFactor:1,sortBlankFactor:1,sortComparer:null},t)}sortList(t){if(!d.isList(t)||1===t.length)return!1;this.ignoreExcludeHandler(t);const e=this.comparerHandler(t);return this.ignoreIncludeHandler(t),e}getDefaultComparer(t){return(ft[t]||ft.string).bind(this)}comparerHandler(t){const e=this.options,i=e.sortField,o=e.sortFactor,n=e.sortBlankFactor,s=e.sortComparer;return"function"==typeof s&&(t.sort(((t,e)=>s.call(this,t,e,{sortField:i,sortFactor:o,sortBlankFactor:n}))),!0)}ignoreExcludeHandler(t){const e=this.options.ignore;this.ignoreListTop=[],this.ignoreListBottom=[];const i=[];for(let o=0,n=t.length;o{t.unshift(e.item)})),this.ignoreListBottom.forEach((e=>{t.push(e.item)}))}}const Bt={removeSortColumn:function(){return this.sortColumn=null,this.$header&&this.$header.find(".tg-column-sorted").removeClass("tg-column-sorted"),this},setSortColumn:function(t){if(!(t=this.getColumnItem(t)))return;if(!this.isColumnSortable(t))return;t===this.sortColumn?t.sortAsc=!t.sortAsc:d.hasOwn(t,"sortAsc")||(t.sortAsc=this.options.sortAsc),this.sortColumn=t;if(this.getRowsLength()-this.frozenInfo.rows<2)return;if(!this.headerCreated)return;this.updateRowsSort()&&(this.renderHeaderSort(),this.flushSort(),this.render("rows"))},renderHeaderSort:function(){const t=this.sortColumn;if(!t)return this;if(!this.isColumnSortable(t))return this;this.$header.find(".tg-column-sorted").removeClass("tg-column-sorted");const e=t.tg_view_index,i=this.$header.find(`.tg-header-item[column='${e}']`).find(".tg-column-header").addClass("tg-column-sorted");return t.sortAsc?i.removeClass("tg-sort-desc").addClass("tg-sort-asc"):i.removeClass("tg-sort-asc").addClass("tg-sort-desc"),this},getSortComparer:function(t){const e=t.comparer;if("function"==typeof e)return e;const i=this.options.sortComparers,o=i[e||t.type];return"function"==typeof o?o:i.string},updateRowsSort:function(){const t=this.sortColumn;if(!t)return!1;const e=t.id;return!!e&&this.sortRows(e,t)},sortRows:function(t){let e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};const i=e.sortAsc?-1:1,o=this.options.sortBlankValueBottom?1:i,n=this.getSortComparer(e);let s=!1;const r=new Dt({ignore:function(t){return t.tg_frozen?{item:t,top:!0}:t.sortFixed?{item:t,top:"top"===t.sortFixed}:void 0},sortField:t,sortFactor:i,sortBlankFactor:o,sortComparer:n}),l=function(t){r.sortList(t)&&(s=!0),t.forEach((function(t,e){t.tg_sub_index=e,t.subs&&l(t.subs)}))};return l(this.rows),s&&this.initRowsHandler(),s}},At={default:{},lightblue:{rowHeight:35,scrollbarSize:10,scrollbarRound:!0},dark:{}},Wt={getAllThemes:function(){return Object.keys(At)},getThemeOptions:function(t){return At[t]}},Ft={update:function(){return this.flushBody(),this.render("rows"),this},updateRow:function(t,e){const i=this.getRowItem(t);if(!i)return this;if(e&&"object"==typeof e){const t=this.getItemSnapshot(e);Object.keys(t).forEach((function(e){i[e]=t[e]}))}return this.flushRow(i.tg_view_index),this.render("rows"),this},updateCell:function(t,e,i){const o=this.getRowItem(t);if(!o)return this;const n=this.getColumnItem(e);return n?(arguments.length>2&&(o[n.id]=i),this.flushCell(o.tg_view_index,n.tg_view_index),this.render("rows"),this):this},onNextUpdated:function(t){return"function"!=typeof t||this.once(y.onUpdated,t),this}},Gt={getViewport:function(){this.scrollLeft=this.getScrollLeft(),this.scrollTop=this.getScrollTop();return{rows:this.getViewportRows(),columns:this.getViewportColumns()}},getViewportRows:function(){const t=[],e=this.viewRows,i=e.length;if(!i)return t;let o=this.options.rowCacheLength;o=d.clamp(d.toNum(o,!0),0,i);const n=this.frozenInfo.rows;if(n){let e=0;for(;e1;){const n=Math.floor(.5*(e+i)),s=t[n],r=this.getRowTop(s),l=this.getRowHeight(s);if(or+l))return n;e=n}}const n=t[i];return o=e)return[];const i=[],o=this.frozenInfo.columns,n=this.viewColumns;for(let s=o,r=n.length;s0;){const n=t[0]-1;n>o&&t.unshift(n);const s=t[t.length-1]+1;si)&&!(n0&&(e.rows.length=o),i=t.options}return this.data=e,this.dataOptions=i,this}setDataSnapshot(t){return this.setData(this.generateDataSnapshot(t)),this}getData(){return this.data}toString(){return"[object Grid]"}}var Ut;Ut=jt.prototype,[v,R,S,T,E,I,L,x,z,F,G,j,K,q,J,Z,tt,et,pt,mt,bt,wt,vt,Ht,{keyTabHandler:function(t){},keyEnterHandler:function(t){},keyEscHandler:function(t){},keyPageUpHandler:function(t){return this.scrollPane.keyPageUpHandler(t)},keyPageDownHandler:function(t){return this.scrollPane.keyPageDownHandler(t)},keyEndHandler:function(t){return this.scrollPane.keyEndHandler(t)},keyHomeHandler:function(t){return this.scrollPane.keyHomeHandler(t)},keyLeftHandler:function(t){return this.scrollPaneHidden?this.scrollPaneFrozen.keyLeftHandler(t):this.scrollPane.keyLeftHandler(t)},keyUpHandler:function(t){return this.scrollPane.keyUpHandler(t)},keyRightHandler:function(t){return this.scrollPaneHidden?this.scrollPaneFrozen.keyRightHandler(t):this.scrollPane.keyRightHandler(t)},keyDownHandler:function(t){return this.scrollPane.keyDownHandler(t)}},Ct,yt,Rt,St,Tt,{showRow:function(t){return this.updateRowsInvisible(this.toRowItemList(t),!1)},hideRow:function(t){return this.updateRowsInvisible(this.toRowItemList(t),!0)},updateRowsInvisible:function(t,e){if(!t.length)return!1;const i=[];return t.forEach((t=>{t.invisible!==e&&(t.invisible=e,t.tg_invisible=e,i.push(t))})),!!i.length&&(this.update(),!0)}},Et,It,Lt,xt,zt,Vt,Ot,$t,Bt,Wt,Ft,Gt].forEach((t=>{for(const e in t){if(d.hasOwn(Ut,e))throw new Error(`ERROR: extends with an existing key: "${e}"`);Ut[e]=t[e]}}));const Xt=jt,Yt=e.VERSION,Kt=e.TIMESTAMP,qt={VERSION:Yt,TIMESTAMP:Kt,Grid:Xt,$:w,CONST:e,EventBase:k,Icon:X,Motion:B,ScrollPane:kt,Util:d}})();var n=o.$,s=o.MP,r=o._d,l=o.xA,h=o.In,a=o.T8,c=o.Gr,d=o.ht,u=o.J0,g=o.xv,f=o.Ay;export{n as $,s as CONST,r as EventBase,l as Grid,h as Icon,a as Motion,c as ScrollPane,d as TIMESTAMP,u as Util,g as VERSION,f as default}; \ No newline at end of file diff --git a/ComfyUI/custom_nodes/ComfyUI-Manager/js/workflow-metadata.js b/ComfyUI/custom_nodes/ComfyUI-Manager/js/workflow-metadata.js new file mode 100644 index 0000000000000000000000000000000000000000..82dbe0168e1fd7c359243f98bf144811cbd7ce6d --- /dev/null +++ b/ComfyUI/custom_nodes/ComfyUI-Manager/js/workflow-metadata.js @@ -0,0 +1,84 @@ +/** + * Attaches metadata to the workflow on save + * - custom node pack version to all custom nodes used in the workflow + * + * Example metadata: + * "nodes": { + * "1": { + * type: "CheckpointLoaderSimple", + * ... + * properties: { + * cnr_id: "comfy-core", + * version: "0.3.8", + * }, + * }, + * } + * + * @typedef {Object} NodeInfo + * @property {string} ver - Version (git hash or semantic version) + * @property {string} cnr_id - ComfyRegistry node ID + * @property {boolean} enabled - Whether the node is enabled + */ + +import { app } from "../../scripts/app.js"; +import { api } from "../../scripts/api.js"; + +class WorkflowMetadataExtension { + constructor() { + this.name = "Comfy.CustomNodesManager.WorkflowMetadata"; + this.installedNodes = {}; + this.comfyCoreVersion = null; + } + + /** + * Get the installed nodes info + * @returns {Promise>} The mapping from node name to its info. + * ver can either be a git commit hash or a semantic version such as "1.0.0" + * cnr_id is the id of the node in the ComfyRegistry + * enabled is true if the node is enabled, false if it is disabled + */ + async getInstalledNodes() { + const res = await api.fetchApi("/customnode/installed"); + return await res.json(); + } + + async init() { + this.installedNodes = await this.getInstalledNodes(); + this.comfyCoreVersion = (await api.getSystemStats()).system.comfyui_version; + } + + /** + * Called when any node is created + * @param {LGraphNode} node The newly created node + */ + nodeCreated(node) { + try { + // nodeData doesn't exist if node is missing or node is frontend only node + if (!node?.constructor?.nodeData?.python_module) return; + + const nodeProperties = (node.properties ??= {}); + const modules = node.constructor.nodeData.python_module.split("."); + const moduleType = modules[0]; + + if (moduleType === "custom_nodes") { + const nodePackageName = modules[1]; + const { cnr_id, aux_id, ver } = + this.installedNodes[nodePackageName] ?? + this.installedNodes[nodePackageName.toLowerCase()] ?? + {}; + + if (cnr_id === "comfy-core") return; // don't allow hijacking comfy-core name + if (cnr_id) nodeProperties.cnr_id = cnr_id; + else nodeProperties.aux_id = aux_id; + if (ver) nodeProperties.ver = ver.trim(); + } else if (["nodes", "comfy_extras", "comfy_api_nodes"].includes(moduleType)) { + nodeProperties.cnr_id = "comfy-core"; + nodeProperties.ver = this.comfyCoreVersion; + } + } catch (e) { + console.error(e); + } + } +} + +app.registerExtension(new WorkflowMetadataExtension()); diff --git a/ComfyUI/custom_nodes/ComfyUI-Manager/misc/Impact.pack b/ComfyUI/custom_nodes/ComfyUI-Manager/misc/Impact.pack new file mode 100644 index 0000000000000000000000000000000000000000..93fd32847cc827929cb6a0987466aaf2628c3145 --- /dev/null +++ b/ComfyUI/custom_nodes/ComfyUI-Manager/misc/Impact.pack @@ -0,0 +1,444 @@ +{ + "Impact::MAKE_BASIC_PIPE": { + "category": "", + "config": { + "1": { + "input": { + "text": { + "name": "Positive prompt" + } + } + }, + "2": { + "input": { + "text": { + "name": "Negative prompt" + } + } + } + }, + "datetime": 1705418802481, + "external": [], + "links": [ + [ + 0, + 1, + 1, + 0, + 1, + "CLIP" + ], + [ + 0, + 1, + 2, + 0, + 1, + "CLIP" + ], + [ + 0, + 0, + 3, + 0, + 1, + "MODEL" + ], + [ + 0, + 1, + 3, + 1, + 1, + "CLIP" + ], + [ + 0, + 2, + 3, + 2, + 1, + "VAE" + ], + [ + 1, + 0, + 3, + 3, + 3, + "CONDITIONING" + ], + [ + 2, + 0, + 3, + 4, + 4, + "CONDITIONING" + ] + ], + "nodes": [ + { + "flags": {}, + "index": 0, + "mode": 0, + "order": 0, + "outputs": [ + { + "links": [], + "name": "MODEL", + "shape": 3, + "slot_index": 0, + "type": "MODEL" + }, + { + "links": [], + "name": "CLIP", + "shape": 3, + "slot_index": 1, + "type": "CLIP" + }, + { + "links": [], + "name": "VAE", + "shape": 3, + "slot_index": 2, + "type": "VAE" + } + ], + "pos": [ + 550, + 360 + ], + "properties": { + "Node name for S&R": "CheckpointLoaderSimple" + }, + "size": { + "0": 315, + "1": 98 + }, + "type": "CheckpointLoaderSimple", + "widgets_values": [ + "SDXL/sd_xl_base_1.0_0.9vae.safetensors" + ] + }, + { + "flags": {}, + "index": 1, + "inputs": [ + { + "link": null, + "name": "clip", + "type": "CLIP" + } + ], + "mode": 0, + "order": 1, + "outputs": [ + { + "links": [], + "name": "CONDITIONING", + "shape": 3, + "slot_index": 0, + "type": "CONDITIONING" + } + ], + "pos": [ + 940, + 480 + ], + "properties": { + "Node name for S&R": "CLIPTextEncode" + }, + "size": { + "0": 263, + "1": 99 + }, + "title": "Positive", + "type": "CLIPTextEncode", + "widgets_values": [ + "" + ] + }, + { + "flags": {}, + "index": 2, + "inputs": [ + { + "link": null, + "name": "clip", + "type": "CLIP" + } + ], + "mode": 0, + "order": 2, + "outputs": [ + { + "links": [], + "name": "CONDITIONING", + "shape": 3, + "slot_index": 0, + "type": "CONDITIONING" + } + ], + "pos": [ + 940, + 640 + ], + "properties": { + "Node name for S&R": "CLIPTextEncode" + }, + "size": { + "0": 263, + "1": 99 + }, + "title": "Negative", + "type": "CLIPTextEncode", + "widgets_values": [ + "" + ] + }, + { + "flags": {}, + "index": 3, + "inputs": [ + { + "link": null, + "name": "model", + "type": "MODEL" + }, + { + "link": null, + "name": "clip", + "type": "CLIP" + }, + { + "link": null, + "name": "vae", + "type": "VAE" + }, + { + "link": null, + "name": "positive", + "type": "CONDITIONING" + }, + { + "link": null, + "name": "negative", + "type": "CONDITIONING" + } + ], + "mode": 0, + "order": 3, + "outputs": [ + { + "links": null, + "name": "basic_pipe", + "shape": 3, + "slot_index": 0, + "type": "BASIC_PIPE" + } + ], + "pos": [ + 1320, + 360 + ], + "properties": { + "Node name for S&R": "ToBasicPipe" + }, + "size": { + "0": 241.79998779296875, + "1": 106 + }, + "type": "ToBasicPipe" + } + ], + "packname": "Impact", + "version": "1.0" + }, + "Impact::SIMPLE_DETAILER_PIPE": { + "category": "", + "config": { + "0": { + "output": { + "0": { + "visible": false + }, + "1": { + "visible": false + } + } + }, + "2": { + "input": { + "Select to add LoRA": { + "visible": false + }, + "Select to add Wildcard": { + "visible": false + }, + "wildcard": { + "visible": false + } + } + } + }, + "datetime": 1705419147116, + "external": [], + "links": [ + [ + null, + 0, + 2, + 0, + 6, + "BASIC_PIPE" + ], + [ + 0, + 0, + 2, + 1, + 13, + "BBOX_DETECTOR" + ], + [ + 1, + 0, + 2, + 2, + 15, + "SAM_MODEL" + ] + ], + "nodes": [ + { + "flags": {}, + "index": 0, + "mode": 0, + "order": 2, + "outputs": [ + { + "links": [], + "name": "BBOX_DETECTOR", + "shape": 3, + "type": "BBOX_DETECTOR" + }, + { + "links": null, + "name": "SEGM_DETECTOR", + "shape": 3, + "type": "SEGM_DETECTOR" + } + ], + "pos": [ + 590, + 830 + ], + "properties": { + "Node name for S&R": "UltralyticsDetectorProvider" + }, + "size": { + "0": 315, + "1": 78 + }, + "type": "UltralyticsDetectorProvider", + "widgets_values": [ + "bbox/Eyeful_v1.pt" + ] + }, + { + "flags": {}, + "index": 1, + "mode": 0, + "order": 3, + "outputs": [ + { + "links": [], + "name": "SAM_MODEL", + "shape": 3, + "type": "SAM_MODEL" + } + ], + "pos": [ + 590, + 960 + ], + "properties": { + "Node name for S&R": "SAMLoader" + }, + "size": { + "0": 315, + "1": 82 + }, + "type": "SAMLoader", + "widgets_values": [ + "sam_vit_b_01ec64.pth", + "AUTO" + ] + }, + { + "flags": {}, + "index": 2, + "inputs": [ + { + "link": null, + "name": "basic_pipe", + "type": "BASIC_PIPE" + }, + { + "link": null, + "name": "bbox_detector", + "slot_index": 1, + "type": "BBOX_DETECTOR" + }, + { + "link": null, + "name": "sam_model_opt", + "slot_index": 2, + "type": "SAM_MODEL" + }, + { + "link": null, + "name": "segm_detector_opt", + "type": "SEGM_DETECTOR" + }, + { + "link": null, + "name": "detailer_hook", + "type": "DETAILER_HOOK" + } + ], + "mode": 0, + "order": 5, + "outputs": [ + { + "links": null, + "name": "detailer_pipe", + "shape": 3, + "type": "DETAILER_PIPE" + } + ], + "pos": [ + 1044, + 812 + ], + "properties": { + "Node name for S&R": "BasicPipeToDetailerPipe" + }, + "size": { + "0": 400, + "1": 204 + }, + "type": "BasicPipeToDetailerPipe", + "widgets_values": [ + "", + "Select the LoRA to add to the text", + "Select the Wildcard to add to the text" + ] + } + ], + "packname": "Impact", + "version": "1.0" + } +} \ No newline at end of file diff --git a/ComfyUI/custom_nodes/ComfyUI-Manager/node_db/README.md b/ComfyUI/custom_nodes/ComfyUI-Manager/node_db/README.md new file mode 100644 index 0000000000000000000000000000000000000000..378845c5ed7cedfae4d4529b0dc13d25d749e48c --- /dev/null +++ b/ComfyUI/custom_nodes/ComfyUI-Manager/node_db/README.md @@ -0,0 +1,95 @@ +# ComfyUI-Manager: Node Database (node_db) + +This directory contains the JSON database files that power ComfyUI-Manager's legacy node registry system. While the manager is gradually transitioning to the online Custom Node Registry (CNR), these local JSON files continue to provide important metadata about custom nodes, models, and their integrations. + +## Directory Structure + +The node_db directory is organized into several subdirectories, each serving a specific purpose: + +- **dev/**: Development channel files with latest additions and experimental nodes +- **legacy/**: Historical/legacy nodes that may require special handling +- **new/**: New nodes that have passed initial verification but are still being evaluated +- **forked/**: Forks of existing nodes with modifications +- **tutorial/**: Example and tutorial nodes designed for learning purposes + +## Core Database Files + +Each subdirectory contains a standard set of JSON files: + +- **custom-node-list.json**: Primary database of custom nodes with metadata +- **extension-node-map.json**: Maps between extensions and individual nodes they provide +- **model-list.json**: Catalog of models that can be downloaded through the manager +- **alter-list.json**: Alternative implementations of nodes for compatibility or functionality +- **github-stats.json**: GitHub repository statistics for node popularity metrics + +## Database Schema + +### custom-node-list.json +```json +{ + "custom_nodes": [ + { + "title": "Node display name", + "name": "Repository name", + "reference": "Original repository if forked", + "files": ["GitHub URL or other source location"], + "install_type": "git", + "description": "Description of the node's functionality", + "pip": ["optional pip dependencies"], + "js": ["optional JavaScript files"], + "tags": ["categorization tags"] + } + ] +} +``` + +### extension-node-map.json +```json +{ + "extension-id": [ + ["list", "of", "node", "classes"], + { + "author": "Author name", + "description": "Extension description", + "nodename_pattern": "Optional regex pattern for node name matching" + } + ] +} +``` + +## Transition to Custom Node Registry (CNR) + +This local database system is being progressively replaced by the online Custom Node Registry (CNR), which provides: +- Real-time updates without manual JSON maintenance +- Improved versioning support +- Better security validation +- Enhanced metadata + +The Manager supports both systems simultaneously during the transition period. + +## Implementation Details + +- The database follows a channel-based architecture for different sources +- Multiple database modes are supported: Channel, Local, and Remote +- The system supports differential updates to minimize bandwidth usage +- Security levels are enforced for different node installations based on source + +## Usage in the Application + +The Manager's backend uses these database files to: + +1. Provide browsable lists of available nodes and models +2. Resolve dependencies for installation +3. Track updates and new versions +4. Map node classes to their source repositories +5. Assess risk levels for installation security + +## Maintenance Scripts + +Each subdirectory contains a `scan.sh` script that assists with: +- Scanning repositories for new nodes +- Updating metadata +- Validating database integrity +- Generating proper JSON structures + +This database system enables a flexible, secure, and comprehensive management system for the ComfyUI ecosystem while the transition to CNR continues. \ No newline at end of file diff --git a/ComfyUI/custom_nodes/ComfyUI-Manager/node_db/dev/custom-node-list.json b/ComfyUI/custom_nodes/ComfyUI-Manager/node_db/dev/custom-node-list.json new file mode 100644 index 0000000000000000000000000000000000000000..efa57622a2cc45563ce076f0aa2b928a425ab383 --- /dev/null +++ b/ComfyUI/custom_nodes/ComfyUI-Manager/node_db/dev/custom-node-list.json @@ -0,0 +1,7564 @@ +{ + "custom_nodes": [ + { + "author": "tankenyuen-ola", + "title": "comfyui-wanvideo-scheduler-loop", + "reference": "https://github.com/tankenyuen-ola/comfyui-wanvideo-scheduler-loop", + "files": [ + "https://github.com/tankenyuen-ola/comfyui-wanvideo-scheduler-loop" + ], + "install_type": "git-clone", + "description": "NODES: WanVideo Scheduler Selector, WanVideo Scheduler Loop, WanVideo Scheduler Info" + }, + { + "author": "ElyZeng", + "title": "ComfyUI-Translator [NAME CONFLICT]", + "reference": "https://github.com/ElyZeng/ComfyUI-Translator", + "files": [ + "https://github.com/ElyZeng/ComfyUI-Translator" + ], + "install_type": "git-clone", + "description": "A ComfyUI node to translate text between multiple languages using Argos Translate." + }, + { + "author": "lggcfx2020", + "title": "ComfyUI-LGGCFX-Tools", + "reference": "https://github.com/lggcfx2020/ComfyUI-LGGCFX-Tools", + "files": [ + "https://github.com/lggcfx2020/ComfyUI-LGGCFX-Tools" + ], + "install_type": "git-clone", + "description": "A small tool for calculating the frame number and total frames of a video. Originally required four nodes, but has been merged into a single node for convenience. Includes quick resolution selection and landscape mode switching." + }, + { + "author": "206811", + "title": "ComfyUI_ZhipuAIO", + "reference": "https://github.com/206811/ComfyUI_ZhipuAIO", + "files": [ + "https://github.com/206811/ComfyUI_ZhipuAIO" + ], + "install_type": "git-clone", + "description": "NODES: ZhipuAI AIO Config, ZhipuAI Translator, ZhipuAI GLM-4V Vision\nNOTE: The files in the repo are not organized." + }, + { + "author": "ahmedbana", + "title": "upload-to-azure", + "reference": "https://github.com/ahmedbana/upload-to-azure", + "files": [ + "https://github.com/ahmedbana/upload-to-azure" + ], + "install_type": "git-clone", + "description": "ComfyUI Upload to Azure Node" + }, + { + "author": "Huangcj2005", + "title": "comfyui-HandDetect", + "reference": "https://github.com/Huangcj2005/comfyui-HandDetect", + "files": [ + "https://github.com/Huangcj2005/comfyui-HandDetect" + ], + "install_type": "git-clone", + "description": "NODES: Hand Mask Generator (YOLOv8)" + }, + { + "author": "Aero-Ex", + "title": "comfyui_diffswap", + "reference": "https://github.com/Aero-Ex/comfyui_diffswap", + "files": [ + "https://github.com/Aero-Ex/comfyui_diffswap" + ], + "install_type": "git-clone", + "description": "NODES: DiffSwap" + }, + { + "author": "eggsbenedicto", + "title": "DiffusionRenderer-ComfyUI [WIP]", + "reference": "https://github.com/eggsbenedicto/DiffusionRenderer-ComfyUI", + "files": [ + "https://github.com/eggsbenedicto/DiffusionRenderer-ComfyUI" + ], + "install_type": "git-clone", + "description": "Experimental wrapper for diffusion-renderer in ComfyUI.\nNOTE: Currently unfinished and non-functioning. Will update" + }, + { + "author": "system-out-cho", + "title": "displayHistory [WIP]", + "reference": "https://github.com/system-out-cho/displayHistory_ComfyUI", + "files": [ + "https://github.com/system-out-cho/displayHistory_ComfyUI" + ], + "install_type": "git-clone", + "description": "A node that displays any node's history\nNOTE: The files in the repo are not organized." + }, + { + "author": "lazybuttalented", + "title": "ComfyUI_LBT [WIP]", + "reference": "https://github.com/lazybuttalented/ComfyUI_LBT", + "files": [ + "https://github.com/lazybuttalented/ComfyUI_LBT" + ], + "install_type": "git-clone", + "description": "A custom node to program the image & text processing flows.\nNOTE: The files in the repo are not organized." + }, + { + "author": "xgfone", + "title": "ComfyUI_PromptLogoCleaner", + "reference": "https://github.com/xgfone/ComfyUI_PromptLogoCleaner", + "files": [ + "https://github.com/xgfone/ComfyUI_PromptLogoCleaner" + ], + "install_type": "git-clone", + "description": "NODES: Prompt Cleaner (Remove Logo Words)" + }, + { + "author": "dexintenebri", + "title": "comfyui_voxel_nodes [WIP]", + "reference": "https://github.com/dexintenebri/comfyui_voxel_nodes", + "files": [ + "https://github.com/dexintenebri/comfyui_voxel_nodes" + ], + "install_type": "git-clone", + "description": "Extracts RGB and depthmap from image to create 3D Voxels, for integration into MagicaVoxel and Unity Engine\nNOTE: The files in the repo are not organized." + }, + { + "author": "1H-hobit", + "title": "ComfyComfyUI_InternVL3 [WIP]", + "reference": "https://github.com/1H-hobit/ComfyUI_InternVL3", + "files": [ + "https://github.com/1H-hobit/ComfyUI_InternVL3" + ], + "install_type": "git-clone", + "description": "ComfyUI nodes to use InternVL3" + }, + { + "author": "LucianoCirino", + "title": "ComfyUI-invAIder-Nodes", + "reference": "https://github.com/LucianoCirino/ComfyUI-invAIder-Nodes", + "files": [ + "https://github.com/LucianoCirino/ComfyUI-invAIder-Nodes" + ], + "install_type": "git-clone", + "description": "Custom nodes I've created for my own personal workflows. Use at your own discretion.\nNOTE: The files in the repo are not organized." + }, + { + "author": "zhuanvi", + "title": "ComfyUI-ZVNodes [WIP]", + "reference": "https://github.com/zhuanvi/ComfyUI-ZVNodes", + "files": [ + "https://github.com/zhuanvi/ComfyUI-ZVNodes" + ], + "install_type": "git-clone", + "description": "NODES: Load One Image (Directory), Save Image (Directory), Count Image (Directory), Json Reader, Json List Node, Json List Length, Json List Indexer, Json List Slicer, Triangle Character Layout, Json List To Mask, Random Select From List, Join List, ..." + }, + { + "author": "Filexor", + "title": "File_x_dynamic_prompt2", + "reference": "https://github.com/Filexor/File_x_dynamic_prompt2", + "files": [ + "https://github.com/Filexor/File_x_dynamic_prompt2" + ], + "install_type": "git-clone", + "description": "ComfyUI node for random prompt generation" + }, + { + "author": "FileSystem Manager Team", + "title": "Comfyui-FileSytem-Manager", + "reference": "https://github.com/bleash-dev/Comfyui-FileSytem-Manager", + "files": [ + "https://github.com/bleash-dev/Comfyui-FileSytem-Manager" + ], + "install_type": "git-clone", + "description": "Comprehensive file system management for ComfyUI with multiple upload sources" + }, + { + "author": "thavocado", + "title": "comfyui-danbooru-lookup", + "reference": "https://github.com/thavocado/comfyui-danbooru-lookup", + "files": [ + "https://github.com/thavocado/comfyui-danbooru-lookup" + ], + "install_type": "git-clone", + "description": "A ComfyUI custom node that performs FAISS cosine similarity lookup on Danbooru embeddings using multiple input modes: CLIP conditioning, images with WD14 tagging, or text tags.[w/This node pack installs its dependencies automatically during execution.]" + }, + { + "author": "love2hina-net", + "title": "ComfyUI-Local-Translator", + "reference": "https://github.com/love2hina-net/ComfyUI-Local-Translator", + "files": [ + "https://github.com/love2hina-net/ComfyUI-Local-Translator" + ], + "install_type": "git-clone", + "description": "This is a text translation node using a local SLM (Microsoft Phi-4) for ComfyUI." + }, + { + "author": "DenRakEiw", + "title": "Denrakeiw Nodes [WIP]", + "reference": "https://github.com/DenRakEiw/DenRakEiw_Nodes", + "files": [ + "https://github.com/DenRakEiw/DenRakEiw_Nodes" + ], + "install_type": "git-clone", + "description": "A custom node pack for ComfyUI that provides utility nodes for image generation and manipulation.\nNOTE: The files in the repo are not organized." + }, + { + "author": "ahmedbana", + "title": "json-creator [WIP]", + "reference": "https://github.com/ahmedbana/json-creator", + "files": [ + "https://github.com/ahmedbana/json-creator" + ], + "install_type": "git-clone", + "description": "Create Json for ComfyUI\nNOTE: The files in the repo are not organized." + }, + { + "author": "ahmedbana", + "title": "File-Rename [UNSAFE]", + "reference": "https://github.com/ahmedbana/File-Rename", + "files": [ + "https://github.com/ahmedbana/File-Rename" + ], + "install_type": "git-clone", + "description": "A custom ComfyUI node package that allows you to rename files with incremented numbers based on various mathematical operations. Includes both basic and advanced functionality.[w/This node pack includes a node that can rename files to arbitrary paths.]" + }, + { + "author": "ahmedbana", + "title": "ComfyUI_AC_FUNV7-FLUX- [WIP]", + "reference": "https://github.com/A719689614/ComfyUI_AC_FUNV7-FLUX-", + "files": [ + "https://github.com/A719689614/ComfyUI_AC_FUNV7-FLUX-" + ], + "install_type": "git-clone", + "description": "NODES: AC_Super_UNET(FLUX), AC_Super_CLIP(FLUX)\nNOTE: The files in the repo are not organized." + }, + { + "author": "broumbroum", + "title": "comfyui-time-system [WIP]", + "reference": "https://github.com/broumbroum/comfyui-time-system", + "files": [ + "https://github.com/broumbroum/comfyui-time-system" + ], + "install_type": "git-clone", + "description": "Package that adds nodes to retrieve the system date and time.\nNOTE: The files in the repo are not organized." + }, + { + "author": "rodpl", + "title": "comfyui-asset-manager", + "reference": "https://github.com/rodpl/comfyui-asset-manager", + "files": [ + "https://github.com/rodpl/comfyui-asset-manager" + ], + "install_type": "git-clone", + "description": "ComfyUI Asset Manager for managing assets in ComfyUI" + }, + { + "author": "blepping", + "title": "ComfyUI 'dum' samplers [WIP]", + "reference": "https://github.com/blepping/comfyui_dum_samplers", + "files": [ + "https://github.com/blepping/comfyui_dum_samplers" + ], + "install_type": "git-clone", + "description": "A collection of random, experimental (and most likely 'dum') samplers for ComfyUI." + }, + { + "author": "crimro-se", + "title": "ComfyUI-CascadedGaze", + "reference": "https://github.com/crimro-se/ComfyUI-CascadedGaze", + "files": [ + "https://github.com/crimro-se/ComfyUI-CascadedGaze" + ], + "install_type": "git-clone", + "description": "Two custom nodes that bring the CascadedGaze image denoising model architecture to ComfyUI." + }, + { + "author": "RamonGuthrie", + "title": "ComfyUI-RBG-LoRA-Converter [UNSAFE]", + "reference": "https://github.com/RamonGuthrie/ComfyUI-RBG-LoraConverter", + "files": [ + "https://github.com/RamonGuthrie/ComfyUI-RBG-LoraConverter" + ], + "install_type": "git-clone", + "description": "A node for converting LoRA (Low-Rank Adaptation) keys in ComfyUI. [w/This node pack contains a node that has a vulnerability allowing write to arbitrary file paths.]" + }, + { + "author": "Estanislao-Oviedo", + "title": "ComfyUI-CustomNodes [NAME CONFLICT]", + "reference": "https://github.com/Estanislao-Oviedo/ComfyUI-CustomNodes", + "files": [ + "https://github.com/Estanislao-Oviedo/ComfyUI-CustomNodes" + ], + "install_type": "git-clone", + "description": "NODES: Load Image Folder (Custom), Make Batch from Single Image (Custom)" + }, + { + "author": "BAIS1C", + "title": "ComfyUI-AudioDuration [WIP]", + "reference": "https://github.com/BAIS1C/ComfyUI_BASICDancePoser", + "files": [ + "https://github.com/BAIS1C/ComfyUI_BASICDancePoser" + ], + "install_type": "git-clone", + "description": "Node to extract Dance poses from Music to control Video Generations.\nNOTE: The files in the repo are not organized." + }, + { + "author": "ctf05", + "title": "ComfyUI-AudioDuration", + "reference": "https://github.com/ctf05/ComfyUI-AudioDuration", + "files": [ + "https://github.com/ctf05/ComfyUI-AudioDuration" + ], + "install_type": "git-clone", + "description": "NODES: Audio Duration, Audio Overlay (Mix)" + }, + { + "author": "Baverne", + "title": "TiledWan ComfyUI Node Set [WIP]", + "reference": "https://github.com/Baverne/comfyUI-TiledWan", + "files": [ + "https://github.com/Baverne/comfyUI-TiledWan" + ], + "install_type": "git-clone", + "description": "A custom node set for ComfyUI that provides tiled processing capabilities.\nNOTE: The files in the repo are not organized." + }, + { + "author": "soliton", + "title": "Watermark Detection YOLO Custom Node [WIP]", + "reference": "https://github.com/Soliton80/ComfyUI-Watermark-Detection-YOLO", + "files": [ + "https://github.com/Soliton80/ComfyUI-Watermark-Detection-YOLO" + ], + "install_type": "git-clone", + "description": "Custom watermark detection using rained on 24,558 watermark images YOLO11 model for ComfyUI\nNOTE: The files in the repo are not organized." + }, + { + "author": "Jpzz", + "title": "IxiWorks StoryBoard Nodes [WIP]", + "reference": "https://github.com/Jpzz/comfyui-ixiworks", + "files": [ + "https://github.com/Jpzz/comfyui-ixiworks" + ], + "install_type": "git-clone", + "description": "StoryBoard nodes for ComfyUI - Parse JSON templates and build prompts for generative movie creation\nNOTE: The files in the repo are not organized." + }, + { + "author": "siyonomicon", + "title": "ComfyUI-Pin", + "reference": "https://github.com/siyonomicon/ComfyUI-Pin", + "files": [ + "https://github.com/siyonomicon/ComfyUI-Pin" + ], + "install_type": "git-clone", + "description": "NODES: Pin Grid Node" + }, + { + "author": "rakete", + "title": "comfyui-rakete", + "reference": "https://github.com/rakete/comfyui-rakete", + "files": [ + "https://github.com/rakete/comfyui-rakete" + ], + "install_type": "git-clone", + "description": "NODES: Get Widget or Default Value, GPU Garbage Collector, Build String from Widget Values" + }, + { + "author": "boricuapab", + "title": "ComfyUI-Bori-KontextPresets [WIP]", + "reference": "https://github.com/boricuapab/ComfyUI-Bori-KontextPresets", + "files": [ + "https://github.com/boricuapab/ComfyUI-Bori-KontextPresets" + ], + "install_type": "git-clone", + "description": "This is a custom node for ComfyUI that uses the Kontext Presets.\nNOTE: The files in the repo are not organized." + }, + { + "author": "sh570655308", + "title": "Comfyui-RayNodes [WIP]", + "reference": "https://github.com/sh570655308/Comfyui-RayNodes", + "files": [ + "https://github.com/sh570655308/Comfyui-RayNodes" + ], + "install_type": "git-clone", + "description": "NODES: Bracketed Tag-Index Merger, Florence2 Tag Processor, Image List Converter, Image Selector, Mask Blackener, Mask Applier and Combiner, Mask Processor, Tag Array to Lines, Tag-Index Merger, Grabber Tag Processor, Image Resizer, Save Image Websocket, Border Mask, SaturationAdjuster, ...\nNOTE: The files in the repo are not organized." + }, + { + "author": "Rocky-Lee-001", + "title": "ComfyUI_SZtools", + "reference": "https://github.com/Rocky-Lee-001/ComfyUI_SZtools", + "files": [ + "https://github.com/Rocky-Lee-001/ComfyUI_SZtools" + ], + "install_type": "git-clone", + "description": "This project is the comfyui implementation of ComfyUI_SZtools, a labeling and naming tool developed for Kontext's local training package T2ITrainer.\nNOTE: The files in the repo are not organized." + }, + { + "author": "stalkervr", + "title": "Custom Path Nodes for ComfyUI [UNSAFE]", + "reference": "https://github.com/stalkervr/comfyui-custom-path-nodes", + "files": [ + "https://github.com/stalkervr/comfyui-custom-path-nodes" + ], + "install_type": "git-clone", + "description": "Nodes for path handling and image cropping.[w/This node pack contains a node that has a vulnerability allowing access to arbitrary file paths.]" + }, + { + "author": "gorillaframeai", + "title": "GF_pixtral_node [WIP]", + "reference": "https://github.com/gorillaframeai/GF_pixtral_node", + "files": [ + "https://github.com/gorillaframeai/GF_pixtral_node" + ], + "install_type": "git-clone", + "description": "NODES: GF Mistral & Pixtral" + }, + { + "author": "enlo", + "title": "ComfyUI-CheckpointSettings", + "reference": "https://github.com/enlo/ComfyUI-CheckpointSettings", + "files": [ + "https://github.com/enlo/ComfyUI-CheckpointSettings" + ], + "install_type": "git-clone", + "description": "A custom node created to fulfill a personal need I thought of while playing around with ComfyUI — 'I want to save checkpoint names and KSampler settings together and randomly switch between them for fun.'" + }, + { + "author": "Mzikart", + "title": "ComfyUI-Mzikart-Player [WIP]", + "reference": "https://github.com/Dream-Pixels-Forge/ComfyUI-Mzikart-Player", + "files": [ + "https://github.com/Dream-Pixels-Forge/ComfyUI-Mzikart-Player" + ], + "install_type": "git-clone", + "description": "Interactive audio player for ComfyUI\nNOTE: The files in the repo are not organized." + }, + { + "author": "babydjac", + "title": "comfyui-grok-ponyxl [WIP]", + "reference": "https://github.com/babydjac/comfyui-grok-ponyxl", + "files": [ + "https://github.com/babydjac/comfyui-grok-ponyxl" + ], + "install_type": "git-clone", + "description": "NODES: GrokPonyXLPrompter\nNOTE: The files in the repo are not organized." + }, + { + "author": "MarkFreeDom168", + "title": "ComfyUI-image-load-url [WIP]", + "reference": "https://github.com/MarkFreeDom168/ComfyUI-image-load-url", + "files": [ + "https://github.com/MarkFreeDom168/ComfyUI-image-load-url" + ], + "install_type": "git-clone", + "description": "NODES: Load Image From URL/Base64, Load Mask From URL/Base64, Load img and mask from url\nNOTE: The files in the repo are not organized." + }, + { + "author": "realm-weaver", + "title": "Tile Seamstress 360° [WIP]", + "reference": "https://github.com/realm-weaver/ComfyUI-tile-seamstress-360", + "files": [ + "https://github.com/realm-weaver/ComfyUI-tile-seamstress-360" + ], + "install_type": "git-clone", + "description": "Tile Seamstress 360 is a set of tools for fixing seams & poles in 360° panoramic equirectangular images inside ComfyUI." + }, + { + "author": "jisenhua", + "title": "ComfyUI-yolov5-face [WIP]", + "reference": "https://github.com/UmutGuzel/tryvariantai-comfyui", + "files": [ + "https://github.com/UmutGuzel/tryvariantai-comfyui" + ], + "install_type": "git-clone", + "description": "NODES: Fill Transparency, Mask Expand Border, Mask Expand Border (Advanced), Mask to Transparent, Debug Mask Visualizer, White to Transparent, White Detector\nNOTE: The files in the repo are not organized." + }, + { + "author": "visualbruno", + "title": "ComfyUI-QRemeshify", + "reference": "https://github.com/visualbruno/ComfyUI-QRemeshify", + "files": [ + "https://github.com/visualbruno/ComfyUI-QRemeshify" + ], + "install_type": "git-clone", + "description": "NODES: QRemeshify" + }, + { + "author": "jisenhua", + "title": "ComfyUI-yolov5-face [WIP]", + "reference": "https://github.com/JiSenHua/ComfyUI-yolov5-face", + "files": [ + "https://github.com/JiSenHua/ComfyUI-yolov5-face" + ], + "install_type": "git-clone", + "description": "A YOLOv5 face detection project for ComfyUI.\nNOTE: The files in the repo are not organized." + }, + { + "author": "zopieux", + "title": "ComfyUI-zopi [UNSAFE]", + "reference": "https://github.com/zopieux/ComfyUI-zopi", + "files": [ + "https://github.com/zopieux/ComfyUI-zopi" + ], + "install_type": "git-clone", + "description": "NODES: Eval Python, Load TensortRT + checkpoint + CLIP + VAE [w/This node pack contains a vulnerability that allows remote code execution.]" + }, + { + "author": "przewodo", + "title": "ComfyUI-Przewodo-Utils [WIP]", + "reference": "https://github.com/przewodo/ComfyUI-Przewodo-Utils", + "files": [ + "https://github.com/przewodo/ComfyUI-Przewodo-Utils" + ], + "install_type": "git-clone", + "description": "Utilities to make it easy to develop advanced Workflows without having to use a lot of nodes for simple stuff.\nNOTE: The files in the repo are not organized." + }, + { + "author": "hulipanpan", + "title": "Comfyui_tuteng [WIP]", + "reference": "https://github.com/hulipanpan/Comfyui_tuteng", + "files": [ + "https://github.com/hulipanpan/Comfyui_tuteng" + ], + "install_type": "git-clone", + "description": "NODES: Tuteng Mj, Tuteng Mj Style, Tuteng Upload, Tuteng Mj Upscale, Tuteng Mj Vary/Zoom, Tuteng Kling Text2Video, Tuteng Kling Image2Video, Tuteng Kling Video Extend, Tuteng Gemini API, Tuteng Doubao SeedEdit, Tuteng ChatGPT API, Tuteng Jimeng API, Tuteng GPT-Image-1 Edit, ...\nNOTE: The files in the repo are not organized." + }, + { + "author": "PaleBloodq", + "title": "ComfyUI-HFTransformers", + "reference": "https://github.com/PaleBloodq/ComfyUI-HFTransformers", + "files": [ + "https://github.com/PaleBloodq/ComfyUI-HFTransformers" + ], + "install_type": "git-clone", + "description": "NODES: HFT Pipeline Loader, HFT Classifier, HFT Classification Selector, HFT Object Detector, HFT Image to Text, HFT Depth Estimator" + }, + { + "author": "whmc76", + "title": "ComfyUI-AudioSuiteAdvanced [WIP]", + "reference": "https://github.com/whmc76/ComfyUI-AudioSuiteAdvanced", + "files": [ + "https://github.com/whmc76/ComfyUI-AudioSuiteAdvanced" + ], + "install_type": "git-clone", + "description": "A ComfyUI plugin for processing long text files and generating speech, supporting features such as audio separation, text segmentation, and audio merging.\nNOTE: The files in the repo are not organized." + }, + { + "author": "Letz-AI", + "title": "ComfyUI-LetzAI [UNSAFE]", + "reference": "https://github.com/Letz-AI/ComfyUI-LetzAI", + "files": [ + "https://github.com/Letz-AI/ComfyUI-LetzAI" + ], + "install_type": "git-clone", + "description": "Custom ComfyUI Node for LetzAI Image Generation[w/The API key is embedded in the workflow.]" + }, + { + "author": "ZhouNLP", + "title": "comfyui_LK_selfuse", + "reference": "https://github.com/LK-168/comfyui_LK_selfuse", + "files": [ + "https://github.com/LK-168/comfyui_LK_selfuse" + ], + "install_type": "git-clone", + "description": "NODES: Mask Diff, Mask Connected Remove, Mask Get Max, Mask Filter with Rate, InspectModelArchitecture, Print Sigma, Adv Scheduler, LK_MaskToSEGS, LK_SegsAdjust, String Filter, String Remove Duplicate, String Modify, ... \nNOTE: The files in the repo are not organized." + }, + { + "author": "junhe421", + "title": "comfyui_batch_image_processor [WIP]", + "reference": "https://github.com/junhe421/comfyui_batch_image_processor", + "files": [ + "https://github.com/junhe421/comfyui_batch_image_processor" + ], + "install_type": "git-clone", + "description": "A Kontext Bench-style ComfyUI image difference analysis node that supports instruction-based prompt generation and batch TXT editing.\nNOTE: The files in the repo are not organized." + }, + { + "author": "TinyBeeman", + "title": "ComfyUI-TinyBee", + "reference": "https://github.com/TinyBeeman/ComfyUI-TinyBee", + "files": [ + "https://github.com/TinyBeeman/ComfyUI-TinyBee" + ], + "install_type": "git-clone", + "description": "NODES: List Count, Random Entry, Indexed Entry, Incrementer, Get File List" + }, + { + "author": "Tr1dae", + "title": "ComfyUI-CustomNodes-MVM", + "reference": "https://github.com/Tr1dae/ComfyUI-CustomNodes-MVM", + "files": [ + "https://github.com/Tr1dae/ComfyUI-CustomNodes-MVM" + ], + "install_type": "git-clone", + "description": "NODES: Load Image From Folder MVM, Load Guidance Images From Folder MVM, Load Text From Folder MVM" + }, + { + "author": "Vkabuto23", + "title": "ComfyUI Custom Nodes: OpenRouter & Ollama [UNSAFE]", + "reference": "https://github.com/Vkabuto23/comfyui_openrouter_ollama", + "files": [ + "https://github.com/Vkabuto23/comfyui_openrouter_ollama" + ], + "install_type": "git-clone", + "description": "ComfyUI Custom Nodes: OpenRouter & Ollama[w/The API key is embedded in the workflow.]" + }, + { + "author": "subnet99", + "title": "ComfyUI-URLLoader", + "reference": "https://github.com/subnet99/ComfyUI-URLLoader", + "files": [ + "https://github.com/subnet99/ComfyUI-URLLoader" + ], + "install_type": "git-clone", + "description": "ComfyUI plugin for downloading and loading media files from URLs." + }, + { + "author": "bikiam", + "title": "Comfyui_AudioRecoder", + "reference": "https://github.com/bikiam/Comfyui_AudioRecoder", + "files": [ + "https://github.com/bikiam/Comfyui_AudioRecoder" + ], + "install_type": "git-clone", + "description": "NODES: AUDIO Recorder" + }, + { + "author": "SaulQiu", + "title": "comfyui-saul-plugin [WIP]", + "reference": "https://github.com/SaulQcy/comfy_saul_plugin", + "files": [ + "https://github.com/SaulQcy/comfy_saul_plugin" + ], + "install_type": "git-clone", + "description": "NODES: Cutting Video\nNOTE: The files in the repo are not organized." + }, + { + "author": "wasilone11", + "title": "comfyui-sync-translate-node", + "reference": "https://github.com/wasilone11/comfyui-sync-translate-node", + "files": [ + "https://github.com/wasilone11/comfyui-sync-translate-node" + ], + "install_type": "git-clone", + "description": "NODES: Sync.so Translator" + }, + { + "author": "ashllay", + "title": "ComfyUI_MoreComfy", + "reference": "https://github.com/ashllay/ComfyUI_MoreComfy", + "files": [ + "https://github.com/ashllay/ComfyUI_MoreComfy" + ], + "install_type": "git-clone", + "description": "NODES: MC Switch Seed, MC Switch Image, MC Switch String, MC Alter Seed, MC Set Tile Size, MC Get Image Size, MC Get Image Min Max, MC Multi Concat, MC Multi Concat(Advanced), MC Noise" + }, + { + "author": "gaowei-space", + "title": "ComfyUI Doubao LLM [WIP]", + "reference": "https://github.com/gaowei-space/ComfyUI-Doubao-LLM", + "files": [ + "https://github.com/gaowei-space/ComfyUI-Doubao-LLM" + ], + "install_type": "git-clone", + "description": "ComfyUI nodes for Doubao (ByteDance) LLM and Vision Language Model integration\nNOTE: The files in the repo are not organized." + }, + { + "author": "BrettMedia", + "title": "comfyui-bhtools [WIP]", + "reference": "https://github.com/BrettMedia/comfyui-bhtools", + "files": [ + "https://github.com/BrettMedia/comfyui-bhtools" + ], + "install_type": "git-clone", + "description": "A suite of creative tools designed to help AI artists with continuity, brainstorming, and workflow optimization. Born from real-world needs during my AI journey, these nodes solve common pain points in creative workflows.\nNOTE: The files in the repo are not organized." + }, + { + "author": "XiaoHeiziGGG", + "title": "ComfyUI-Gemini-Kontext [WIP]", + "reference": "https://github.com/XiaoHeiziGGG/ComfyUI-Gemini-Kontext", + "files": [ + "https://github.com/XiaoHeiziGGG/ComfyUI-Gemini-Kontext" + ], + "install_type": "git-clone", + "description": "Google Gemini API powered translation nodes for ComfyUI\nNOTE: The files in the repo are not organized." + }, + { + "author": "Bwebbfx", + "title": "ComfyUI Face Parsing Nodes [WIP]", + "reference": "https://github.com/Bwebbfx/ComfyUI_FaceParsing", + "files": [ + "https://github.com/Bwebbfx/ComfyUI_FaceParsing" + ], + "install_type": "git-clone", + "description": "This package provides ComfyUI nodes for face parsing using BiSeNet (from yakhyo/face-parsing), supporting batch and video workflows.\nNOTE: The files in the repo are not organized." + }, + { + "author": "orion4d", + "title": "Unified List Selector for ComfyUI [UNSAFE]", + "reference": "https://github.com/orion4d/ComfyUI_unified_list_selector", + "files": [ + "https://github.com/orion4d/ComfyUI_unified_list_selector" + ], + "install_type": "git-clone", + "description": "This project is a custom node for ComfyUI that allows you to dynamically load lists from text (.txt) or CSV (.csv) files and select an item to use in your workflow. It features a manual selection mode (via a dropdown list) and a random selection mode, as well as the ability to add prefixes and suffixes to the selected text.[w/This node pack contains a node with a vulnerability that allows reading files from arbitrary paths.]" + }, + { + "author": "kongds1999", + "title": "ComfyUI_was_image", + "reference": "https://github.com/kongds1999/ComfyUI_was_image", + "files": [ + "https://github.com/kongds1999/ComfyUI_was_image" + ], + "install_type": "git-clone", + "description": "NODES: Replace Color By Palette, ConvertGrayToImage" + }, + { + "author": "zl9739379", + "title": "ComfyUI Qwen Vision Language API Node [NAME CONFLICT]", + "reference": "https://github.com/zl9739379/comfyui-qwen-vl-api", + "files": [ + "https://github.com/zl9739379/comfyui-qwen-vl-api" + ], + "install_type": "git-clone", + "description": "A ComfyUI custom node for describing images using Qwen Vision Language models through OpenAI-compatible APIs." + }, + { + "author": "bikiam", + "title": "ComfyUi_WhisperGTranslate", + "reference": "https://github.com/bikiam/ComfyUi_WhisperGTranslate", + "files": [ + "https://github.com/bikiam/ComfyUi_WhisperGTranslate" + ], + "install_type": "git-clone", + "description": "NODES: Whisper + AudioTranslate, Google Translate Node" + }, + { + "author": "edgerunner", + "title": "ComfyUI Queue Manager [WIP]", + "reference": "https://github.com/QuietNoise/ComfyUI-Queue-Manager", + "files": [ + "https://github.com/QuietNoise/ComfyUI-Queue-Manager" + ], + "install_type": "git-clone", + "description": "An extension supporting more streamlined prompt queue management." + }, + { + "author": "fylrid2", + "title": "lockValue", + "reference": "https://github.com/fylrid2/comfyui_lock_previous_value", + "files": [ + "https://github.com/fylrid2/comfyui_lock_previous_value" + ], + "install_type": "git-clone", + "description": "Allows the locking of a nodes value\nNOTE: The files in the repo are not organized." + }, + { + "author": "XiaoHeiziGGG", + "title": "ComfyUI Gemini Translator [WIP]", + "reference": "https://github.com/XiaoHeiziGGG/ComfyUI-GeminiTranslator", + "files": [ + "https://github.com/XiaoHeiziGGG/ComfyUI-GeminiTranslator" + ], + "install_type": "git-clone", + "description": "The API node library of gemini can be translated and recognized.The API node library of gemini can be translated and recognized.\nNOTE: The files in the repo are not organized." + }, + { + "author": "DiffusionWave-YT", + "title": "DiffusionWave_PickResolution [WIP]", + "reference": "https://github.com/DiffusionWave-YT/DiffusionWave_PickResolution", + "files": [ + "https://github.com/DiffusionWave-YT/DiffusionWave_PickResolution" + ], + "install_type": "git-clone", + "description": "Change of resolutions for ComfyUI and Upscalers\nNOTE: The files in the repo are not organized." + }, + { + "author": "pixixai", + "title": "ComfyUI_Pixix-Tools [UNSAFE/WIP]", + "reference": "https://github.com/pixixai/ComfyUI_Pixix-Tools", + "files": [ + "https://github.com/pixixai/ComfyUI_Pixix-Tools" + ], + "install_type": "git-clone", + "description": "Load Text (from folder)\nNOTE: The files in the repo are not organized.[w/The contents of files from arbitrary paths can be read remotely through this node.]" + }, + { + "author": "PeterMikhai", + "title": "DoomFLUX Nodes [WIP]", + "reference": "https://github.com/PeterMikhai/Doom_Flux_NodePack", + "files": [ + "https://github.com/PeterMikhai/Doom_Flux_NodePack" + ], + "install_type": "git-clone", + "description": "Custom nodes for FLUX models, including a loader and specialized samplers for standard and inpaint generation.\nNOTE: The files in the repo are not organized." + }, + { + "author": "maque", + "title": "comfyui_video_BC [WIP]", + "reference": "https://github.com/JioJe/comfyui_video_BC", + "files": [ + "https://github.com/JioJe/comfyui_video_BC" + ], + "install_type": "git-clone", + "description": "Batch load video nodes and save videos in custom paths\nNOTE: The files in the repo are not organized." + }, + { + "author": "ZHO-ZHO-ZHO", + "title": "ComfyUI-Gemini [NAME CONFLICT]", + "id": "gemini", + "reference": "https://github.com/ZHO-ZHO-ZHO/ComfyUI-Gemini", + "files": [ + "https://github.com/ZHO-ZHO-ZHO/ComfyUI-Gemini" + ], + "install_type": "git-clone", + "description": "Using Gemini-pro & Gemini-pro-vision in ComfyUI." + }, + { + "author": "No-22-Github", + "title": "ComfyUI_SaveImageCustom", + "reference": "https://github.com/No-22-Github/ComfyUI_SaveImageCustom", + "files": [ + "https://github.com/No-22-Github/ComfyUI_SaveImageCustom" + ], + "install_type": "git-clone", + "description": "Easy save image with dir+name" + }, + { + "author": "jiafuzeng", + "title": "comfyui-fishSpeech", + "reference": "https://github.com/jiafuzeng/comfyui-fishSpeech", + "files": [ + "https://github.com/jiafuzeng/comfyui-fishSpeech" + ], + "install_type": "git-clone", + "description": "NODES: Fish-Speech Loader, Fish-Speech TTS, Fish-Speech Audio Preview" + }, + { + "author": "bleash-dev", + "title": "ComfyUI-Auth-Manager", + "reference": "https://github.com/bleash-dev/ComfyUI-Auth-Manager", + "files": [ + "https://github.com/bleash-dev/ComfyUI-Auth-Manager" + ], + "install_type": "git-clone", + "description": "A custom node that provides email/password authentication for ComfyUI pods with a beautiful modal interface." + }, + { + "author": "filliptm", + "title": "ComfyUI_Fill-Node-Loader [WIP]", + "reference": "https://github.com/filliptm/ComfyUI_Fill-Node-Loader", + "files": [ + "https://github.com/filliptm/ComfyUI_Fill-Node-Loader" + ], + "install_type": "git-clone", + "description": "A ComfyUI plugin to simplify loading and managing custom nodes with a sidebar interface." + }, + { + "author": "diogod", + "title": "Comfy Inpainting Works [WIP]", + "reference": "https://github.com/diodiogod/Comfy-Inpainting-Works", + "files": [ + "https://github.com/diodiogod/Comfy-Inpainting-Works" + ], + "install_type": "git-clone", + "description": "Go to the top menu>Workflow>Browse Templates. This is a collection of my Inpainting workflows for Flux (expanded and COMPACT) + others. Previously called: 'Proper Flux Control-Net inpainting and/or outpainting with batch size - Alimama or Flux Fill'. By installing this 'node' you can always keep them up to date by updating on the manager. This is not a new custom node. You will still need to install all other custom nodes used on the workflows. You will also find my 'Flux LoRA Block Weights Preset Tester' here as well.\nNOTE: The files in the repo are not organized." + }, + { + "author": "Malloc-pix", + "title": "comfyui-QwenVL", + "reference": "https://github.com/Malloc-pix/comfyui-QwenVL", + "files": [ + "https://github.com/Malloc-pix/comfyui-QwenVL" + ], + "install_type": "git-clone", + "description": "NODES: Qwen2.5VL, Qwen2.5" + }, + { + "author": "artifyfun", + "title": "ComfyUI-JS [UNSAFE]", + "reference": "https://github.com/artifyfun/ComfyUI-JS", + "files": [ + "https://github.com/artifyfun/ComfyUI-JS" + ], + "install_type": "git-clone", + "description": "A ComfyUI custom node capable of executing JavaScript code: it takes JavaScript code as input and outputs the execution result.[w/This extension has an XSS vulnerability that can be triggered through workflow execution.]" + }, + { + "author": "OgreLemonSoup", + "title": "ComfyUI-Notes-manager", + "reference": "https://github.com/OgreLemonSoup/ComfyUI-Notes-manager", + "files": [ + "https://github.com/OgreLemonSoup/ComfyUI-Notes-manager" + ], + "install_type": "git-clone", + "description": "This extension provides the note feature." + }, + { + "author": "WozStudios", + "title": "ComfyUI-WozNodes", + "reference": "https://github.com/WozStudios/ComfyUI-WozNodes", + "files": [ + "https://github.com/WozStudios/ComfyUI-WozNodes" + ], + "install_type": "git-clone", + "description": "NODES: Trim Image Batch, Create Image Batch, Select Image Batch by Mask, Advanced Batch Creator" + }, + { + "author": "stalkervr", + "title": "comfyui-custom-path-nodes [UNSAFE]", + "reference": "https://github.com/stalkervr/comfyui-custom-path-nodes", + "files": [ + "https://github.com/stalkervr/comfyui-custom-path-nodes" + ], + "install_type": "git-clone", + "description": "Nodes for path handling and image cropping.[w/This nodepack has a vulnerability that allows remote access to arbitrary file paths.]" + }, + { + "author": "vovler", + "title": "comfyui-vovlertools", + "reference": "https://github.com/vovler/ComfyUI-vovlerTools", + "files": [ + "https://github.com/vovler/ComfyUI-vovlerTools" + ], + "install_type": "git-clone", + "description": "Advanced ComfyUI nodes for WD14 tagging, image filtering, and CLIP to TensorRT conversion" + }, + { + "author": "ELiZswe", + "title": "ComfyUI-ELiZTools", + "reference": "https://github.com/ELiZswe/ComfyUI-ELiZTools", + "files": [ + "https://github.com/ELiZswe/ComfyUI-ELiZTools" + ], + "install_type": "git-clone", + "description": "ELIZ Tools" + }, + { + "author": "yamanacn", + "title": "comfyui_qwenbbox", + "reference": "https://github.com/yamanacn/comfyui_qwenbbox", + "files": [ + "https://github.com/yamanacn/comfyui_qwenbbox" + ], + "install_type": "git-clone", + "description": "NODES: Load Qwen Model (v2), Qwen Bbox Detection, Prepare BBox for SAM (v2)" + }, + { + "author": "mikheys", + "title": "ComfyUI-mikheys", + "reference": "https://github.com/mikheys/ComfyUI-mikheys", + "files": [ + "https://github.com/mikheys/ComfyUI-mikheys" + ], + "install_type": "git-clone", + "description": "NODES: WAN Optimal Resolution Selector, WAN Show Image Dimensions" + }, + { + "author": "iacoposk8", + "title": "ComfyUI XOR Pickle Nodes", + "reference": "https://github.com/iacoposk8/xor_pickle_nodes", + "files": [ + "https://github.com/iacoposk8/xor_pickle_nodes" + ], + "install_type": "git-clone", + "description": "Two custom nodes for ComfyUI that allow you to encrypt and decrypt Python objects using simple XOR encryption with pickle." + }, + { + "author": "yamanacn", + "title": "comfyui_qwen_object [WIP]", + "reference": "https://github.com/yamanacn/comfyui_qwen_object", + "files": [ + "https://github.com/yamanacn/comfyui_qwen_object" + ], + "install_type": "git-clone", + "description": "This is a custom node for ComfyUI that integrates the Qwen vision model for tasks such as object detection.\nNOTE: The files in the repo are not organized." + }, + { + "author": "neverbiasu", + "title": "ComfyUI-Show-o [WIP]", + "reference": "https://github.com/neverbiasu/ComfyUI-Show-o", + "files": [ + "https://github.com/neverbiasu/ComfyUI-Show-o" + ], + "install_type": "git-clone", + "description": "NODES: Show-o Model Loader, Show-o Text to Image, Show-o Image Captioning, Show-o Image Inpainting" + }, + { + "author": "zyquon", + "title": "ComfyUI Stash", + "reference": "https://github.com/zyquon/ComfyUI-Stash", + "files": [ + "https://github.com/zyquon/ComfyUI-Stash" + ], + "install_type": "git-clone", + "description": "Nodes to use Stash within Comfy workflows" + }, + { + "author": "tankenyuen-ola", + "title": "comfyui-env-variable-reader [UNSAFE]", + "reference": "https://github.com/tankenyuen-ola/comfyui-env-variable-reader", + "files": [ + "https://github.com/tankenyuen-ola/comfyui-env-variable-reader" + ], + "install_type": "git-clone", + "description": "NODES: Environment Variable Reader [w/Installing this node may expose environment variables that contain sensitive information such as API keys.]" + }, + { + "author": "ftf001-tech", + "title": "ComfyUI-Lucian [WIP]", + "reference": "https://github.com/ftf001-tech/ComfyUI-ExternalLLMDetector", + "files": [ + "https://github.com/ftf001-tech/ComfyUI-ExternalLLMDetector" + ], + "install_type": "git-clone", + "description": "These nodes allow you to configure LLM API connections, send images with custom prompts, and convert the LLM's JSON bounding box responses into a format compatible with segmentation nodes like SAM2\nNOTE: The files in the repo are not organized." + }, + { + "author": "LucianGnn", + "title": "ComfyUI-Lucian [WIP]", + "reference": "https://github.com/LucianGnn/ComfyUI-Lucian", + "files": [ + "https://github.com/LucianGnn/ComfyUI-Lucian" + ], + "install_type": "git-clone", + "description": "NODES: Audio Duration Calculator\nNOTE: The files in the repo are not organized." + }, + { + "author": "akatz-ai", + "title": "ComfyUI-Execution-Inversion", + "reference": "https://github.com/akatz-ai/ComfyUI-Execution-Inversion", + "files": [ + "https://github.com/akatz-ai/ComfyUI-Execution-Inversion" + ], + "install_type": "git-clone", + "description": "Contains nodes related to the new execution inversion engine in ComfyUI. Node pack originally from [a/https://github.com/BadCafeCode/execution-inversion-demo-comfyui](https://github.com/BadCafeCode/execution-inversion-demo-comfyui)" + }, + { + "author": "mamorett", + "title": "comfyui_minicpm_vision", + "reference": "https://github.com/mamorett/comfyui_minicpm_vision", + "files": [ + "https://github.com/mamorett/comfyui_minicpm_vision" + ], + "install_type": "git-clone", + "description": "NODES: MiniCPM Vision GGUF" + }, + { + "author": "BigStationW", + "title": "flowmatch_scheduler-comfyui", + "reference": "https://github.com/BigStationW/flowmatch_scheduler-comfyui", + "files": [ + "https://github.com/BigStationW/flowmatch_scheduler-comfyui" + ], + "install_type": "git-clone", + "description": "NODES: FlowMatchSigmas" + }, + { + "author": "casterpollux", + "title": "MiniMax-bmo", + "reference": "https://github.com/casterpollux/MiniMax-bmo", + "files": [ + "https://github.com/casterpollux/MiniMax-bmo" + ], + "install_type": "git-clone", + "description": "ComfyUI MiniMax Remover Node" + }, + { + "author": "franky519", + "title": "ComfyUI Face Four Image Matcher [WIP]", + "reference": "https://github.com/franky519/comfyui_fnckc_Face_analysis", + "files": [ + "https://github.com/franky519/comfyui_fnckc_Face_analysis" + ], + "install_type": "git-clone", + "description": "ComfyUI custom node for four face image matching and face swap control\nNOTE: Invalid pyproject.toml" + }, + { + "author": "bleash-dev", + "title": "Comfyui-Iddle-Checker", + "reference": "https://github.com/bleash-dev/Comfyui-Idle-Checker", + "files": [ + "https://github.com/bleash-dev/Comfyui-Idle-Checker" + ], + "install_type": "git-clone", + "description": "front extension for idle checker" + }, + { + "author": "fangg2000", + "title": "ComfyUI-StableAudioFG [WIP]", + "reference": "https://github.com/fangg2000/ComfyUI-StableAudioFG", + "files": [ + "https://github.com/fangg2000/ComfyUI-StableAudioFG" + ], + "install_type": "git-clone", + "description": "The ComfyUI plugin for stable-audio (supports offline use)\nNOTE: The files in the repo are not organized." + }, + { + "author": "hdfhssg", + "title": "comfyui_EvoSearch [WIP]", + "reference": "https://github.com/hdfhssg/comfyui_EvoSearch", + "files": [ + "https://github.com/hdfhssg/comfyui_EvoSearch" + ], + "install_type": "git-clone", + "description": "NODES: EvoSearch_FLUX, EvoSearch_SD21, EvoSearch_WAN, EvolutionScheduleGenerator, GuidanceRewardsGenerator" + }, + { + "author": "simonjaq", + "title": "ComfyUI-sjnodes", + "reference": "https://github.com/simonjaq/ComfyUI-sjnodes", + "files": [ + "https://github.com/simonjaq/ComfyUI-sjnodes" + ], + "install_type": "git-clone", + "description": "Some modified ComfyUI custom nodes" + }, + { + "author": "A4P7J1N7M05OT", + "title": "ComfyUI-VAELoaderSDXLmod", + "reference": "https://github.com/A4P7J1N7M05OT/ComfyUI-VAELoaderSDXLmod", + "files": [ + "https://github.com/A4P7J1N7M05OT/ComfyUI-VAELoaderSDXLmod" + ], + "install_type": "git-clone", + "description": "NODES: Modified SDXL VAE Loader, Empty Latent Image Variable" + }, + { + "author": "xzuyn", + "title": "xzuynodes-ComfyUI", + "reference": "https://github.com/xzuyn/ComfyUI-xzuynodes", + "files": [ + "https://github.com/xzuyn/ComfyUI-xzuynodes" + ], + "install_type": "git-clone", + "description": "NODES: First/Last Frame (XZ), Resize Image (Original KJ), Resize Image (XZ), CLIP Text Encode (XZ), Load CLIP (XZ), TripleCLIPLoader (XZ), WanImageToVideo (XZ)" + }, + { + "author": "gilons", + "title": "ComfyUI-GoogleDrive-Downloader [UNSAFE]", + "reference": "https://github.com/gilons/ComfyUI-GoogleDrive-Downloader", + "files": [ + "https://github.com/gilons/ComfyUI-GoogleDrive-Downloader" + ], + "install_type": "git-clone", + "description": "ComfyUI custom node for downloading files from Google Drive.[w/There is a vulnerability that allows saving a remote file to an arbitrary local path.]" + }, + { + "author": "moonwhaler", + "title": "ComfyUI-FileBrowserAPI [UNSAFE]", + "reference": "https://github.com/GalactusX31/ComfyUI-FileBrowserAPI", + "files": [ + "https://github.com/GalactusX31/ComfyUI-FileBrowserAPI" + ], + "install_type": "git-clone", + "description": "A general-purpose, dependency-free File and Folder Browser API for ComfyUI custom nodes.[w/path traversal vulnerability]" + }, + { + "author": "moonwhaler", + "title": "comfyui-moonpack", + "reference": "https://github.com/moonwhaler/comfyui-moonpack", + "files": [ + "https://github.com/moonwhaler/comfyui-moonpack" + ], + "install_type": "git-clone", + "description": "NODES: Proportional Dimension, Simple String Replace, Regex String Replace, VACE Looper Frame Scheduler" + }, + { + "author": "DreamsInAutumn", + "title": "ComfyUI-Autumn-LLM-Nodes", + "reference": "https://github.com/DreamsInAutumn/ComfyUI-Autumn-LLM-Nodes", + "files": [ + "https://github.com/DreamsInAutumn/ComfyUI-Autumn-LLM-Nodes" + ], + "install_type": "git-clone", + "description": "NODES: Gemini-Image-To-Prompt, Gemini-Prompt-Builder, LLM-Prompt-Builder" + }, + { + "author": "alexgenovese", + "title": "ComfyUI-Reica", + "reference": "https://github.com/alexgenovese/ComfyUI-Reica", + "files": [ + "https://github.com/alexgenovese/ComfyUI-Reica" + ], + "install_type": "git-clone", + "description": "NODES: 'Reica GCP: Read Image', 'Reica GCP: Write Image & Get URL', 'Reica Text Image Display', 'Reica Read Image URL', 'Reica URL Image Loader Filename', 'Reica API: Send HTTP Notification', 'Insert Anything'" + }, + { + "author": "yichengup", + "title": "ComfyUI-Transition", + "reference": "https://github.com/yichengup/ComfyUI-Transition", + "files": [ + "https://github.com/yichengup/ComfyUI-Transition" + ], + "install_type": "git-clone", + "description": "NODES: Linear Transition, Gradient Transition, Dual Line Transition, Sequence Transition, Circular Transition, Circular Sequence Transition" + }, + { + "author": "wildminder", + "title": "ComfyUI-MagCache [NAME CONFLICT|WIP]", + "reference": "https://github.com/wildminder/ComfyUI-MagCache", + "files": [ + "https://github.com/wildminder/ComfyUI-MagCache" + ], + "install_type": "git-clone", + "description": "official implementation of [zehong-ma/MagCache](https://github.com/zehong-ma/MagCache) for ComfyUI" + }, + { + "author": "laubsauger", + "title": "ComfyUI Storyboard [WIP]", + "reference": "https://github.com/laubsauger/comfyui-storyboard", + "files": [ + "https://github.com/laubsauger/comfyui-storyboard" + ], + "install_type": "git-clone", + "description": "This custom node for ComfyUI provides a markdown renderer to display formatted text and notes within your workflow." + }, + { + "author": "IsItDanOrAi", + "title": "ComfyUI-exLoadout [WIP]", + "reference": "https://github.com/IsItDanOrAi/ComfyUI-exLoadout", + "files": [ + "https://github.com/IsItDanOrAi/ComfyUI-exLoadout" + ], + "install_type": "git-clone", + "description": "exLoadout is a suite of lightweight ComfyUI custom nodes that let you define and switch between full loadouts stored in an Excel sheet. A loadout could include any node inputs that expect string values—models (checkpoints, CLIP, VAE, ControlNets, LoRAs, UNets), numeric or text variables (CFG, sampler names, scheduler types, etc.)—all pulled from a row in your sheet. By selecting a row, you instantly apply all of its settings in your workflow, with built‑in support for editing and reading those cells right inside the UI." + }, + { + "author": "grokuku", + "title": "ComfyUI-Holaf-Terminal [UNSAFE]", + "reference": "https://github.com/grokuku/ComfyUI-Holaf-Utilities", + "files": [ + "https://github.com/grokuku/ComfyUI-Holaf-Utilities" + ], + "install_type": "git-clone", + "description": "Interactive Terminal in a node for ComfyUI[w/This custom extension provides a remote web-based shell (terminal) interface to the machine running the ComfyUI server. By installing and using this extension, you are opening a direct, powerful, and potentially dangerous access point to your system.]" + }, + { + "author": "usrname0", + "title": "ComfyUI-AllergicPack [WIP]", + "reference": "https://github.com/usrname0/ComfyUI-AllergicPack", + "files": [ + "https://github.com/usrname0/ComfyUI-AllergicPack" + ], + "install_type": "git-clone", + "description": "This package is not ready for primetime but I'm making it public anyway. If I'm using the node then I'm putting it here. Might make it more official later. Use at your own risk." + }, + { + "author": "cesilk10", + "title": "cesilk-comfyui-nodes", + "reference": "https://github.com/cesilk10/cesilk-comfyui-nodes", + "files": [ + "https://github.com/cesilk10/cesilk-comfyui-nodes" + ], + "install_type": "git-clone", + "description": "NODES: Save and Upload to S3, SDXL Image Sizes" + }, + { + "author": "COcisuts", + "title": "CObot-ComfyUI-WhisperToTranscription [WIP]", + "reference": "https://github.com/COcisuts/CObot-ComfyUI-WhisperToTranscription", + "files": [ + "https://github.com/COcisuts/CObot-ComfyUI-WhisperToTranscription" + ], + "install_type": "git-clone", + "description": "CObot-ComfyUI-WhisperToTranscription\nNOTE: missing requirements.txt" + }, + { + "author": "xuhuan2048", + "title": "ExtractStoryboards [WIP]", + "reference": "https://github.com/gitadmini/comfyui_extractstoryboards", + "files": [ + "https://github.com/gitadmini/comfyui_extractstoryboards" + ], + "install_type": "git-clone", + "description": "A tool for decomposing video storyboards, which can obtain storyboards and keyframes" + }, + { + "author": "jinchanz", + "title": "ComfyUI-AliCloud-Bailian [WIP]", + "reference": "https://github.com/jinchanz/ComfyUI-AliCloud-Bailian", + "files": [ + "https://github.com/jinchanz/ComfyUI-AliCloud-Bailian" + ], + "install_type": "git-clone", + "description": "This is a collection of custom nodes for invoking Alibaba Cloud's DashScope API within ComfyUI.\nNOTE: The files in the repo are not organized." + }, + { + "author": "Yukinoshita-Yukinoe", + "title": "ComfyUI-KontextOfficialNode", + "reference": "https://github.com/Yukinoshita-Yukinoe/ComfyUI-KontextOfficialNode", + "files": [ + "https://github.com/Yukinoshita-Yukinoe/ComfyUI-KontextOfficialNode" + ], + "install_type": "git-clone", + "description": "NODES: Kontext Text-to-Image (Official Max), Kontext Image Editing (Official Max)" + }, + { + "author": "takoyaki1118", + "title": "ComfyUI_PromptExtractor", + "reference": "https://github.com/takoyaki1118/ComfyUI_PromptExtractor", + "files": [ + "https://github.com/takoyaki1118/ComfyUI_PromptExtractor" + ], + "install_type": "git-clone", + "description": "NODES: Custom Load Image With Path, Prompt Extractor Node" + }, + { + "author": "littleowl", + "title": "ComfyUI-MV-HECV", + "reference": "https://github.com/littleowl/ComfyUI-MV-HECV", + "files": [ + "https://github.com/littleowl/ComfyUI-MV-HECV" + ], + "install_type": "git-clone", + "description": "ComfyUI export of 3D Videos and Images Compatible with VR / XR, including the AVP." + }, + { + "author": "BinglongLi", + "title": "ComfyUI_ToolsForAutomask", + "reference": "https://github.com/BinglongLi/ComfyUI_ToolsForAutomask", + "files": [ + "https://github.com/BinglongLi/ComfyUI_ToolsForAutomask" + ], + "install_type": "git-clone", + "description": "NODES: Directional Mask Expansion, Remove Small Regions Mask, Precise Subtract Mask, Precise Add Mask, Closing Mask, Opening Mask, Conditional Mask Selector, Prune Thin Branches Mask, Mask Fill Gaps Convex Hull" + }, + { + "author": "strhwste", + "title": "CSV Utils [WIP]", + "reference": "https://github.com/strhwste/comfyui_csv_utils", + "files": [ + "https://github.com/strhwste/comfyui_csv_utils" + ], + "install_type": "git-clone", + "description": "Custom CSV handling nodes for ComfyUI\nNOTE: invalid pyproject.toml" + }, + { + "author": "retech995", + "title": "ComfyUI_SaveImageBulk [UNSAFE]", + "reference": "https://github.com/retech995/Save_Florence2_Bulk_Prompts", + "files": [ + "https://github.com/retech995/Save_Florence2_Bulk_Prompts" + ], + "install_type": "git-clone", + "description": "This comfyui node helps save image[w/This node can write files to an arbitrary path.]" + }, + { + "author": "Oct7", + "title": "ComfyUI-LaplaMask", + "reference": "https://github.com/Oct7/ComfyUI-LaplaMask", + "files": [ + "https://github.com/Oct7/ComfyUI-LaplaMask" + ], + "install_type": "git-clone", + "description": "NODES: Blur→Mask" + }, + { + "author": "etng", + "title": "ComfyUI-Heartbeat [UNSAFE]", + "reference": "https://github.com/etng/ComfyUI-Heartbeat", + "files": [ + "https://github.com/etng/ComfyUI-Heartbeat" + ], + "install_type": "git-clone", + "description": "A plugin for ComfyUI that sends periodic heartbeat requests to a configured gateway, including system information and node status." + }, + { + "author": "Novavision0313", + "title": "ComfyUI-NVVS [WIP]", + "reference": "https://github.com/Novavision0313/ComfyUI-NVVS", + "files": [ + "https://github.com/Novavision0313/ComfyUI-NVVS" + ], + "install_type": "git-clone", + "description": "A ComfyUI plugin customized by NOVEVISION\nNOTE: The files in the repo are not organized." + }, + { + "author": "zackabrams", + "title": "ComfyUI-KeySyncWrapper [WIP]", + "reference": "https://github.com/zackabrams/ComfyUI-KeySyncWrapper", + "files": [ + "https://github.com/zackabrams/ComfyUI-KeySyncWrapper" + ], + "install_type": "git-clone", + "description": "implementation of KeySync in ComfyUI" + }, + { + "author": "godric8", + "title": "ComfyUI_Step1X-Edit [NAME CONFLICT]", + "reference": "https://github.com/godric8/ComfyUI_Step1X-Edit", + "files": [ + "https://github.com/godric8/ComfyUI_Step1X-Edit" + ], + "install_type": "git-clone", + "description": "ComfyUI nodes for Step1X-Edit" + }, + { + "author": "violet0927", + "title": "ComfyUI-Direct3DS2 [WIP]", + "reference": "https://github.com/y4my4my4m/ComfyUI_Direct3DS2", + "files": [ + "https://github.com/y4my4my4m/ComfyUI_Direct3DS2" + ], + "install_type": "git-clone", + "description": "Direct3D-S2 plugin for ComfyUI. [w/Doesn't work yet]" + }, + { + "author": "gamtruliar", + "title": "ComfyUI-N_SwapInput [UNSAFE]", + "reference": "https://github.com/gamtruliar/ComfyUI-N_SwapInput", + "files": [ + "https://github.com/gamtruliar/ComfyUI-N_SwapInput" + ], + "install_type": "git-clone", + "description": "This is a simple tool for swapping input folders with custom suffix in comfy-UI[w/]This node pack performs deletion operations on local files and contains a vulnerability that allows arbitrary paths to be deleted." + }, + { + "author": "bulldog68", + "title": "ComfyUI_FMJ [WIP]", + "reference": "https://github.com/bulldog68/ComfyUI_FMJ", + "files": [ + "https://github.com/bulldog68/ComfyUI_FMJ" + ], + "install_type": "git-clone", + "description": "Generate random prompts easily for FMJ.\nNOTE: The files in the repo are not organized." + }, + { + "author": "amamisonlyuser", + "title": "MixvtonComfyui [WIP]", + "reference": "https://github.com/amamisonlyuser/MixvtonComfyui", + "files": [ + "https://github.com/amamisonlyuser/MixvtonComfyui" + ], + "install_type": "git-clone", + "description": "NODES: CXH_Leffa_Viton_Load, CXH_Leffa_Viton_Run\nNOTE: The files in the repo are not organized." + }, + { + "author": "pictorialink", + "title": "comfyui-static-resource[UNSAFE]", + "reference": "https://github.com/pictorialink/ComfyUI-static-resource", + "files": [ + "https://github.com/pictorialink/ComfyUI-static-resource" + ], + "install_type": "git-clone", + "description": "Use model bending to push your model beyond its visuals' limits. These nodes allow you to apply transformations to the intemediate densoising steps during sampling, e.g. add, multiplty, scale, rotate, dilate, erode ..etc.[w/This node pack includes a feature that allows downloading remote files to arbitrary local paths. This is a vulnerability that can lead to Remote Code Execution.]" + }, + { + "author": "brace-great", + "title": "comfyui-mc [WIP]", + "reference": "https://github.com/brace-great/comfyui-mc", + "files": [ + "https://github.com/brace-great/comfyui-mc" + ], + "install_type": "git-clone", + "description": "NODES: IncrementCounterOnMatch\nNOTE: The files in the repo are not organized." + }, + { + "author": "blueraincoatli", + "title": "ComfyModelCleaner [WIP]", + "reference": "https://github.com/blueraincoatli/ComfyUI-Model-Cleaner", + "files": [ + "https://github.com/blueraincoatli/ComfyUI-Model-Cleaner" + ], + "install_type": "git-clone", + "description": "This plugin helps identify and clean up unused model files in ComfyUI installations. It analyzes workflows, custom nodes, and model usage to safely identify redundant files." + }, + { + "author": "avocadori", + "title": "ComfyUI Audio Amplitude Converter [WIP]", + "reference": "https://github.com/avocadori/ComfyUI-AudioAmplitudeConverter", + "files": [ + "https://github.com/avocadori/ComfyUI-AudioAmplitudeConverter" + ], + "install_type": "git-clone", + "description": "This is a high-performance custom node for ComfyUI that performs audio amplitude conversion.\nNOTE: The files in the repo are not organized." + }, + { + "author": "wTechArtist", + "title": "ComfyUI_VVL_VideoCamera", + "reference": "https://github.com/wTechArtist/ComfyUI_VVL_VideoCamera", + "files": [ + "https://github.com/wTechArtist/ComfyUI_VVL_VideoCamera" + ], + "install_type": "git-clone", + "description": "NODES: VVL Video Camera Estimator, VVL Video Frame Extractor" + }, + { + "author": "wTechArtist", + "title": "ComfyUI_VVL_Segmentation [WIP]", + "reference": "https://github.com/wTechArtist/ComfyUI_VVL_Segmentation", + "files": [ + "https://github.com/wTechArtist/ComfyUI_VVL_Segmentation" + ], + "install_type": "git-clone", + "description": "NODES: VVL Mask2Former Panoptic (Enhanced), VVL OneFormer Universal Segmentation\nNOTE: The files in the repo are not organized." + }, + { + "author": "lum3on", + "title": "comfyui_RollingDepth [WIP]", + "reference": "https://github.com/lum3on/comfyui_RollingDepth", + "files": [ + "https://github.com/lum3on/comfyui_RollingDepth" + ], + "install_type": "git-clone", + "description": "ComfyuI Needs longer to start the first time, because the mode gets downloaded.\nNOTE: The files in the repo are not organized." + }, + { + "author": "abuzreq", + "title": "ComfyUI Model Bending [UNSAFE]", + "reference": "https://github.com/abuzreq/ComfyUI-Model-Bending", + "files": [ + "https://github.com/abuzreq/ComfyUI-Model-Bending" + ], + "install_type": "git-clone", + "description": "Use model bending to push your model beyond its visuals' limits. These nodes allow you to apply transformations to the intemediate densoising steps during sampling, e.g. add, multiplty, scale, rotate, dilate, erode ..etc.[w/This node pack contains a vulnerability that allows remote code execution.]" + }, + { + "author": "Stable Diffusion VN", + "title": "SDVN Comfy node [UNSAFE]", + "id": "SDVN", + "reference": "https://github.com/StableDiffusionVN/SDVN_Comfy_node", + "files": [ + "https://github.com/StableDiffusionVN/SDVN_Comfy_node" + ], + "install_type": "git-clone", + "description": "Update IC Lora Layout Support Node[w/This node pack contains a vulnerability that allows remote code execution.]" + }, + { + "author": "Sephrael", + "title": "comfyui_caption-around-image", + "reference": "https://github.com/Sephrael/comfyui_caption-around-image", + "files": [ + "https://github.com/Sephrael/comfyui_caption-around-image" + ], + "install_type": "git-clone", + "description": "NODES: A comfyUI node to create captions around a generated image with the ability to dynamically include generation parameters" + }, + { + "author": "EQXai", + "title": "ComfyUI_EQX", + "reference": "https://github.com/EQXai/ComfyUI_EQX", + "files": [ + "https://github.com/EQXai/ComfyUI_EQX" + ], + "install_type": "git-clone", + "description": "NODES: SaveImage_EQX, File Image Selector, Load Prompt From File - EQX, LoraStackEQX_random, Extract Filename - EQX, Extract LORA name - EQX, NSFW Detector EQX, NSFW Detector Advanced EQX" + }, + { + "author": "yincangshiwei", + "title": "ComfyUI-SEQLToolNode", + "reference": "https://github.com/yincangshiwei/ComfyUI-SEQLToolNode", + "files": [ + "https://github.com/yincangshiwei/ComfyUI-SEQLToolNode" + ], + "install_type": "git-clone", + "description": "NODES: ImageCropAlphaNode (Image), CanvasFusionNode (Image)" + }, + { + "author": "gabe-init", + "title": "comfyui_ui_render [UNSAFE]", + "reference": "https://github.com/gabe-init/comfyui_ui_render", + "files": [ + "https://github.com/gabe-init/comfyui_ui_render" + ], + "install_type": "git-clone", + "description": "ComfyUI HTML Renderer Node - Display rich HTML content within ComfyUI nodes[w/This nodepack contains nodes that potentially have XSS vulnerabilities.]" + }, + { + "author": "gabe-init", + "title": "ComfyUI LM Studio Node [WIP]", + "reference": "https://github.com/gabe-init/ComfyUI-LM-Studio", + "files": [ + "https://github.com/gabe-init/ComfyUI-LM-Studio" + ], + "install_type": "git-clone", + "description": "A powerful ComfyUI custom node that seamlessly integrates LM Studio's local language models into your ComfyUI workflows. This node supports both text-only and multimodal (text + image) inputs, making it perfect for complex AI-driven creative workflows.\nNOTE: The files in the repo are not organized." + }, + { + "author": "LyazS", + "title": "ComfyUI-aznodes", + "reference": "https://github.com/LyazS/ComfyUI-aznodes", + "files": [ + "https://github.com/LyazS/ComfyUI-aznodes" + ], + "install_type": "git-clone", + "description": "NODES: CrossFadeImageSequence, SaveImageAZ" + }, + { + "author": "truebillyblue", + "title": "lC.ComfyUI_epistemic_nodes [WIP]", + "reference": "https://github.com/truebillyblue/lC.ComfyUI_epistemic_nodes", + "files": [ + "https://github.com/truebillyblue/lC.ComfyUI_epistemic_nodes" + ], + "install_type": "git-clone", + "description": "NODES: lC L1 Startle, lC L2 FrameClick, lC L3 KeymapClick, lC L4 AnchorClick, lC L5 FieldClick, lC L6 ReflectBoom, lC Epistemic Pipeline (L1-L7), Create PBI (lC), Query PBIs (lC), Update PBI (lC), lC API LLM Agent, lC Web LLM Agent, ...\nNOTE: The files in the repo are not organized." + }, + { + "author": "aklevecz", + "title": "ComfyUI-AutoPrompt [WIP]", + "reference": "https://github.com/aklevecz/ComfyUI-AutoPrompt", + "files": [ + "https://github.com/aklevecz/ComfyUI-AutoPrompt" + ], + "install_type": "git-clone", + "description": "NODES: Ollama Prompt Generator, Ollama Model Lister, Ollama Chat, Text Display" + }, + { + "author": "AlexYez", + "title": "ComfyUI Timesaver Nodes", + "reference": "https://github.com/AlexYez/comfyui-timesaver", + "files": [ + "https://github.com/AlexYez/comfyui-timesaver" + ], + "install_type": "git-clone", + "description": "ComfyUI nodes from [Timesaver](https://github.com/AlexYez/comfyui-timesaver)." + }, + { + "author": "aa-parky", + "title": "pipemind-comfyui", + "reference": "https://github.com/aa-parky/pipemind-comfyui", + "files": [ + "https://github.com/aa-parky/pipemind-comfyui" + ], + "install_type": "git-clone", + "description": "NODES: Random Line from File (Seeded), Keyword Prompt Composer, Simple Prompt Combiner (5x), Boolean Switch (Any), Select Line from TxT (Any), Multiline Text Input, Flux 2M Aspect Ratios, SDXL Aspect Ratios, Room Mapper, ..." + }, + { + "author": "pacchikAI", + "title": "ImagePromptBatch [UNSAFE]", + "reference": "https://github.com/pacchikAI/ImagePromptBatch", + "files": [ + "https://github.com/pacchikAI/ImagePromptBatch" + ], + "install_type": "git-clone", + "description": "NODES: Load Image and Prompt[w/This includes a node that can read the contents of a `.csv` file from an arbitrary path.]" + }, + { + "author": "papcorns", + "title": "ComfyUI-Papcorns-Node-UploadToGCS", + "reference": "https://github.com/papcorns/ComfyUI-Papcorns-Node-UploadToGCS", + "files": [ + "https://github.com/papcorns/ComfyUI-Papcorns-Node-UploadToGCS" + ], + "install_type": "git-clone", + "description": "NODES: Upload Image To GCS" + }, + { + "author": "ZHO-ZHO-ZHO", + "title": "Qwen-2.5 in ComfyUI [NAME CONFLICT]", + "reference": "https://github.com/ZHO-ZHO-ZHO/ComfyUI-Qwen", + "files": [ + "https://github.com/ZHO-ZHO-ZHO/ComfyUI-Qwen" + ], + "install_type": "git-clone", + "description": "Using Qwen-2.5 in ComfyUI" + }, + { + "author": "Charonartist", + "title": "gabe-init [WIP]", + "reference": "https://github.com/gabe-init/ComfyUI-Repo-Eater", + "files": [ + "https://github.com/gabe-init/ComfyUI-Repo-Eater" + ], + "install_type": "git-clone", + "description": "A ComfyUI custom node that ingests GitHub repositories and outputs their content as text along with token count.\nNOTE: The files in the repo are not organized." + }, + { + "author": "Charonartist", + "title": "ComfyUI-send-eagle-pro", + "reference": "https://github.com/Charonartist/ComfyUI-send-eagle-pro_2", + "files": [ + "https://github.com/Charonartist/ComfyUI-send-eagle-pro_2" + ], + "install_type": "git-clone", + "description": "Eagle integration extension node for ComfyUI (Pro version)" + }, + { + "author": "Mervent", + "title": "comfyui-yaml-prompt", + "reference": "https://github.com/Mervent/comfyui-yaml-prompt", + "files": [ + "https://github.com/Mervent/comfyui-yaml-prompt" + ], + "install_type": "git-clone", + "description": "NODES: YAMLPromptParser" + }, + { + "author": "dhpdong", + "title": "ComfyUI-IPAdapter-Flux-Repair", + "reference": "https://github.com/dhpdong/ComfyUI-IPAdapter-Flux-Repair", + "files": [ + "https://github.com/dhpdong/ComfyUI-IPAdapter-Flux-Repair" + ], + "install_type": "git-clone", + "description": "The IPAdapter-Flux node may cause some GPU memory to not be properly released during multiple inferences or when alternating between two nodes, eventually leading to a memory overflow. This project addresses and fixes that issue." + }, + { + "author": "usman2003", + "title": "ComfyUI-RaceDetect", + "reference": "https://github.com/usman2003/ComfyUI-RaceDetect", + "files": [ + "https://github.com/usman2003/ComfyUI-RaceDetect" + ], + "install_type": "git-clone", + "description": "NODES: Race Detection V2" + }, + { + "author": "Mervent", + "title": "comfyui-telegram-send", + "reference": "https://github.com/Mervent/comfyui-telegram-send", + "files": [ + "https://github.com/Mervent/comfyui-telegram-send" + ], + "install_type": "git-clone", + "description": "NODES: TelegramSend, TelegramReply" + }, + { + "author": "qlikpetersen", + "title": "ComfyUI-AI_Tools [UNSAFE]", + "reference": "https://github.com/qlikpetersen/ComfyUI-AI_Tools", + "files": [ + "https://github.com/qlikpetersen/ComfyUI-AI_Tools" + ], + "install_type": "git-clone", + "description": "NODES: DoLogin, HttpRequest, Json2String, String2Json, CreateListString, CreateListJSON, Query_OpenAI, Image_Attachment, JSON_Attachment, String_Attachment, RunPython\n[w/This node pack contains a node with a vulnerability that allows arbitrary code execution.]" + }, + { + "author": "MuAIGC", + "title": "DMXAPI Nodes [WIP]", + "reference": "https://github.com/MuAIGC/ComfyUI-DMXAPI_mmx", + "files": [ + "https://github.com/MuAIGC/ComfyUI-DMXAPI_mmx" + ], + "install_type": "git-clone", + "description": "DMXAPI integration for ComfyUI with Seedream-3.0 text-to-image model\nNOTE: invalid pyproject.toml" + }, + { + "author": "Hapseleg", + "title": "This n that (Hapse)", + "reference": "https://github.com/Hapseleg/ComfyUI-This-n-That", + "files": [ + "https://github.com/Hapseleg/ComfyUI-This-n-That" + ], + "install_type": "git-clone", + "description": "Comfyui custom nodes I use for... This n That..." + }, + { + "author": "matDobek", + "title": "ComfyUI_duck", + "reference": "https://github.com/matDobek/ComfyUI_duck", + "files": [ + "https://github.com/matDobek/ComfyUI_duck" + ], + "install_type": "git-clone", + "description": "NODES: Combine Images (duck)" + }, + { + "author": "usman2003", + "title": "ComfyUI-Classifiers", + "reference": "https://github.com/usman2003/ComfyUI-Classifiers", + "files": [ + "https://github.com/usman2003/ComfyUI-Classifiers" + ], + "install_type": "git-clone", + "description": "NODES: Gender Classification" + }, + { + "author": "wTechArtist", + "title": "ComfyUI_vvl_BBOX", + "reference": "https://github.com/wTechArtist/ComfyUI_vvl_BBOX", + "files": [ + "https://github.com/wTechArtist/ComfyUI_vvl_BBOX" + ], + "install_type": "git-clone", + "description": "NODES: vvl BBox Input" + }, + { + "author": "zhengxyz123", + "title": "zhengxyz123/ComfyUI-CLIPSeg [NAME CONFLICT]", + "reference": "https://github.com/zhengxyz123/ComfyUI-CLIPSeg", + "files": [ + "https://github.com/zhengxyz123/ComfyUI-CLIPSeg" + ], + "install_type": "git-clone", + "description": "Using CLIPSeg model to generate masks for image inpainting tasks based on text or image prompts." + }, + { + "author": "Alazuaka", + "title": "ComfyUI Image Analysis Toolkit [WIP]", + "reference": "https://github.com/ThatGlennD/ComfyUI-Image-Analysis-Tools", + "files": [ + "https://github.com/ThatGlennD/ComfyUI-Image-Analysis-Tools" + ], + "install_type": "git-clone", + "description": "A suite of custom ComfyUI nodes built to evaluate and diagnose the technical qualities of images—especially those generated by AI models. Rather than creating visuals, these tools measure them, offering precise insights into sharpness, noise, exposure, color balance, and more.\nNOTE: The files in the repo are not organized." + }, + { + "author": "trampolin", + "title": "comfy-ui-scryfall", + "reference": "https://github.com/trampolin/comfy-ui-scryfall", + "files": [ + "https://github.com/trampolin/comfy-ui-scryfall" + ], + "install_type": "git-clone", + "description": "Some ComfyUI nodes to fetch cards from scryfall" + }, + { + "author": "pomelyu", + "title": "cy-prompt-tools", + "reference": "https://github.com/pomelyu/cy-prompt-tools", + "files": [ + "https://github.com/pomelyu/cy-prompt-tools" + ], + "install_type": "git-clone", + "description": "prompt tools for comfyui" + }, + { + "author": "Alazuaka", + "title": "ES_nodes for ComfyUI by Alazuka [WIP]", + "reference": "https://github.com/Alazuaka/comfyui-lora-stack-node", + "files": [ + "https://github.com/Alazuaka/comfyui-lora-stack-node" + ], + "install_type": "git-clone", + "description": "Node for LoRA stack management in ComfyUI\nNOTE: The files in the repo are not organized." + }, + { + "author": "fuzr0dah", + "title": "comfyui-sceneassembly", + "reference": "https://github.com/fuzr0dah/comfyui-sceneassembly", + "files": [ + "https://github.com/fuzr0dah/comfyui-sceneassembly" + ], + "install_type": "git-clone", + "description": "A bunch of nodes I created that I also find useful." + }, + { + "author": "PabloGrant", + "title": "comfyui-giraffe-test-panel", + "reference": "https://github.com/PabloGrant/comfyui-giraffe-test-panel", + "files": [ + "https://github.com/PabloGrant/comfyui-giraffe-test-panel" + ], + "install_type": "git-clone", + "description": "General-purpose test node. [w/Use at your own risk. No warranties. No guaranteed support or future updates. Feel free to fork, but remember to share in case anyone else can benefit.]" + }, + { + "author": "lrzjason", + "title": "Comfyui-Condition-Utils [WIP]", + "reference": "https://github.com/lrzjason/Comfyui-Condition-Utils", + "files": [ + "https://github.com/lrzjason/Comfyui-Condition-Utils" + ], + "install_type": "git-clone", + "description": "A collection of utility nodes for handling condition tensors in ComfyUI." + }, + { + "author": "gordon123", + "title": "ComfyUI_DreamBoard [WIP]", + "reference": "https://github.com/gordon123/ComfyUI_DreamBoard", + "files": [ + "https://github.com/gordon123/ComfyUI_DreamBoard" + ], + "install_type": "git-clone", + "description": "for making storyboard UNDERCONSTRUCTION!" + }, + { + "author": "erosDiffusion", + "title": "Select key from JSON (Alpha) [UNSAFE]", + "reference": "https://github.com/erosDiffusion/ComfyUI-enricos-json-file-load-and-value-selector", + "files": [ + "https://github.com/erosDiffusion/ComfyUI-enricos-json-file-load-and-value-selector" + ], + "install_type": "git-clone", + "description": "this node lists json files in the ComfyUI input folder[w/If this node pack is installed and the server is running with remote access enabled, it can read the contents of JSON files located in arbitrary paths.]" + }, + { + "author": "yichengup", + "title": "ComfyUI-YCNodes_Advance", + "reference": "https://github.com/yichengup/ComfyUI-YCNodes_Advance", + "files": [ + "https://github.com/yichengup/ComfyUI-YCNodes_Advance" + ], + "install_type": "git-clone", + "description": "NODES: Face Detector Selector, YC Human Parts Ultra(Advance), Color Match (YC)" + }, + { + "author": "rakki194", + "title": "ComfyUI_WolfSigmas [UNSAFE]", + "reference": "https://github.com/rakki194/ComfyUI_WolfSigmas", + "files": [ + "https://github.com/rakki194/ComfyUI_WolfSigmas" + ], + "install_type": "git-clone", + "description": "This custom node pack for ComfyUI provides a suite of tools for generating and manipulating sigma schedules for diffusion models. These nodes are particularly useful for fine-tuning the sampling process, experimenting with different step counts, and adapting schedules for specific models.[w/Security Warning: Remote Code Execution]" + }, + { + "author": "xl0", + "title": "q_tools", + "reference": "https://github.com/xl0/q_tools", + "files": [ + "https://github.com/xl0/q_tools" + ], + "install_type": "git-clone", + "description": "NODES: QLoadLatent, QLinearScheduler, QPreviewLatent, QGaussianLatent, QUniformLatent, QKSampler" + }, + { + "author": "virallover", + "reference": "https://github.com/maizerrr/comfyui-code-nodes", + "files": [ + "https://github.com/maizerrr/comfyui-code-nodes" + ], + "install_type": "git-clone", + "description": "NODES: BBox Drawer, BBox Parser, Dummy Passthrough Node, Batch Images (up to 5), Mask Editor, OpenAI GPT-Image-1 Node, GhatGPT Node" + }, + { + "author": "virallover", + "title": "comfyui-virallover", + "reference": "https://github.com/virallover/comfyui-virallover", + "files": [ + "https://github.com/virallover/comfyui-virallover" + ], + "install_type": "git-clone", + "description": "NODES: Download and Load Lora Model Only, Depth Fitter, Brightness Correction, Edge Noise, Feathered Sharpen, Concat Horizontal With Mask" + }, + { + "author": "nobandegani", + "title": "Ino Custom Nodes", + "reference": "https://github.com/nobandegani/comfyui_ino_nodes", + "files": [ + "https://github.com/nobandegani/comfyui_ino_nodes" + ], + "install_type": "git-clone", + "description": "NODES: BeDrive Save Image, BeDrive Save File, BeDrive Get Parent ID, Ino Parse File Path, Ino Not Boolean, Ino Count Files" + }, + { + "author": "jax-explorer", + "title": "ComfyUI-DreamO", + "reference": "https://github.com/jax-explorer/ComfyUI-DreamO", + "files": [ + "https://github.com/jax-explorer/ComfyUI-DreamO" + ], + "install_type": "git-clone", + "description": "[a/https://github.com/bytedance/DreamO](https://github.com/bytedance/DreamO]) ComfyUI Warpper" + }, + { + "author": "MakkiShizu", + "title": "ComfyUI-MakkiTools", + "reference": "https://github.com/MakkiShizu/ComfyUI-MakkiTools", + "files": [ + "https://github.com/MakkiShizu/ComfyUI-MakkiTools" + ], + "install_type": "git-clone", + "description": "NODES: GetImageNthCount, ImageChannelSeparate, ImageCountConcatenate, MergeImageChannels, ImageWidthStitch, ImageHeigthStitch" + }, + { + "author": "SKBv0", + "title": "Retro Engine Node for ComfyUI", + "reference": "https://github.com/SKBv0/ComfyUI-RetroEngine", + "files": [ + "https://github.com/SKBv0/ComfyUI-RetroEngine" + ], + "install_type": "git-clone", + "description": "This custom node integrates [a/EmulatorJS](https://github.com/EmulatorJS/EmulatorJS) into ComfyUI, allowing you to run retro games and capture their screens for your image generation workflows." + }, + { + "author": "brace-great", + "title": "comfyui-eim", + "reference": "https://github.com/brace-great/comfyui-eim", + "files": [ + "https://github.com/brace-great/comfyui-eim" + ], + "install_type": "git-clone", + "description": "NODES: EncryptImage" + }, + { + "author": "p1atdev", + "title": "comfyui-aesthetic-predictor", + "reference": "https://github.com/p1atdev/comfyui-aesthetic-predictor", + "files": [ + "https://github.com/p1atdev/comfyui-aesthetic-predictor" + ], + "install_type": "git-clone", + "description": "NODES: Load Aesthetic Predictor, Predict Aesthetic Score" + }, + { + "author": "barakapa", + "title": "barakapa-nodes", + "reference": "https://github.com/barakapa/barakapa-nodes", + "files": [ + "https://github.com/barakapa/barakapa-nodes" + ], + "install_type": "git-clone", + "description": "Compare and save unique workflows, count tokens in prompt, and other utility." + }, + { + "author": "VictorLopes643", + "title": "ComfyUI-Video-Dataset-Tools [WIP]", + "reference": "https://github.com/VictorLopes643/ComfyUI-Video-Dataset-Tools", + "files": [ + "https://github.com/VictorLopes643/ComfyUI-Video-Dataset-Tools" + ], + "install_type": "git-clone", + "description": "NODES: Video Frame Extractor, Image Frame Saver\nNOTE: The files in the repo are not organized." + }, + { + "author": "George0726", + "title": "ComfyUI-video-accessory [WIP]", + "reference": "https://github.com/George0726/ComfyUI-video-accessory", + "files": [ + "https://github.com/George0726/ComfyUI-video-accessory" + ], + "install_type": "git-clone", + "description": "accessory nodes for video generation" + }, + { + "author": "bheins", + "title": "ComfyUI-glb-to-stl [WIP]", + "reference": "https://github.com/maurorilla/ComfyUI-MisterMR-Nodes", + "files": [ + "https://github.com/maurorilla/ComfyUI-MisterMR-Nodes" + ], + "install_type": "git-clone", + "description": "A collection of custom nodes for ComfyUI that add drawing capabilities to your workflow.\nNOTE: The files in the repo are not organized." + }, + { + "author": "TheJorseman", + "title": "IntrinsicCompositingClean-ComfyUI", + "reference": "https://github.com/TheJorseman/IntrinsicCompositingClean-ComfyUI", + "files": [ + "https://github.com/TheJorseman/IntrinsicCompositingClean-ComfyUI" + ], + "install_type": "git-clone", + "description": "NODES: DepthModelLoader, NormalsModelLoader, IntrinsicModelLoader, AlbedoModelLoader, ReshadingModelLoader, ReshadingProcessor, ...\nNOTE: The files in the repo are not organized." + }, + { + "author": "bheins", + "title": "ComfyUI-glb-to-stl [WIP]", + "reference": "https://github.com/bheins/ComfyUI-glb-to-stl", + "files": [ + "https://github.com/bheins/ComfyUI-glb-to-stl" + ], + "install_type": "git-clone", + "description": "GLB conversion to STL node for ComfyUI\nNOTE: The files in the repo are not organized." + }, + { + "author": "cyberhirsch", + "title": "seb_nodes [WIP]", + "reference": "https://github.com/cyberhirsch/seb_nodes", + "files": [ + "https://github.com/cyberhirsch/seb_nodes" + ], + "install_type": "git-clone", + "description": "A custom node for ComfyUI providing more control over image saving, including dynamic subfolder creation and a convenient button to open the last used output folder directly from the UI.\nNOTE: The files in the repo are not organized." + }, + { + "author": "Anonymzx", + "title": "ComfyUI-Indonesia-TTS [WIP]", + "reference": "https://github.com/Anonymzx/ComfyUI-Indonesia-TTS", + "files": [ + "https://github.com/Anonymzx/ComfyUI-Indonesia-TTS" + ], + "description": "Repositori ini menyediakan integrasi model Text-to-Speech (TTS) Bahasa Indonesia dari Facebook (MMS-TTS-IND) ke dalam ComfyUI, sehingga Anda dapat langsung menyintesis suara berbahasa Indonesia dengan kontrol penuh via antarmuka node-based.\nNOTE: The files in the repo are not organized.", + "install_type": "git-clone" + }, + { + "author": "3dmindscapper", + "title": "ComfyUI-Sam-Mesh [WIP]", + "reference": "https://github.com/3dmindscapper/ComfyUI-Sam-Mesh", + "files": [ + "https://github.com/3dmindscapper/ComfyUI-Sam-Mesh" + ], + "install_type": "git-clone", + "description": "comfyui implementation of SaMesh segmentation of 3d meshes\nNOTE: The files in the repo are not organized." + }, + { + "author": "shinich39", + "title": "comfyui-run-js [UNSAFE]", + "reference": "https://github.com/shinich39/comfyui-run-js", + "files": [ + "https://github.com/shinich39/comfyui-run-js" + ], + "description": "Manipulate workflow via javascript on node.", + "install_type": "git-clone" + }, + { + "author": "fangg2000", + "title": "ComfyUI-SenseVoice [WIP]", + "reference": "https://github.com/fangg2000/ComfyUI-SenseVoice", + "files": [ + "https://github.com/fangg2000/ComfyUI-SenseVoice" + ], + "description": "A comfyui node plug-in developed based on the SenseVoise project, and a simple recording node.\nNOTE: The files in the repo are not organized.", + "install_type": "git-clone" + }, + { + "author": "risunobushi", + "title": "ComfyUI_FaceMesh_Eyewear_Mask", + "reference": "https://github.com/risunobushi/ComfyUI_FaceMesh_Eyewear_Mask", + "files": [ + "https://github.com/risunobushi/ComfyUI_FaceMesh_Eyewear_Mask" + ], + "description": "NODES: Face Mesh Eyewear Mask, OpenPose Eyewear Mask (DWPose), Mask From Facial Keypoints", + "install_type": "git-clone" + }, + { + "author": "machinesarenotpeople", + "title": "comfyui-energycost", + "reference": "https://github.com/machinesarenotpeople/comfyui-energycost", + "files": [ + "https://github.com/machinesarenotpeople/comfyui-energycost" + ], + "description": "NODES: Energy Cost Timer, Energy Cost Calculator", + "install_type": "git-clone" + }, + { + "author": "xqqe", + "title": "honey_nodes [WIP]", + "reference": "https://github.com/xqqe/honey_nodes", + "files": [ + "https://github.com/xqqe/honey_nodes" + ], + "description": "honey nodes for comfyui\nNOTE: The files in the repo are not organized.", + "install_type": "git-clone" + }, + { + "author": "Raidez", + "title": "Kuniklo Collection", + "reference": "https://github.com/Raidez/comfyui-kuniklo-collection", + "files": [ + "https://github.com/Raidez/comfyui-kuniklo-collection" + ], + "description": "NODES: Properties, Apply SVG to Image", + "install_type": "git-clone" + }, + { + "author": "AhBumm", + "title": "ComfyUI_MangaLineExtraction", + "reference": "https://github.com/AhBumm/ComfyUI_MangaLineExtraction-hf", + "files": [ + "https://github.com/AhBumm/ComfyUI_MangaLineExtraction-hf" + ], + "description": "p1atdev/MangaLineExtraction-hf as a node in comfyui", + "install_type": "git-clone" + }, + { + "author": "Kur0butiMegane", + "title": "Comfyui-StringUtils", + "reference": "https://github.com/Kur0butiMegane/Comfyui-StringUtils2", + "files": [ + "https://github.com/Kur0butiMegane/Comfyui-StringUtils2" + ], + "install_type": "git-clone", + "description": "NODES: Normalizer, Splitter, Selector, XML Parser, XML Parser, Make Property, Add XML Tag, Is String Empty, Cond Passthrough, CLIP Passthrough, ClipRegion Passthrough, Scheduler Selector (Impact), Scheduler Selector (Inspire), Save Text, XML to Cutoff" + }, + { + "author": "ronaldstg", + "title": "comfyui-plus-integrations [WIP]", + "reference": "https://github.com/ronalds-eu/comfyui-plus-integrations", + "files": [ + "https://github.com/ronalds-eu/comfyui-plus-integrations" + ], + "install_type": "git-clone", + "description": "NODES: Image Pass Through, Upload Image to S3\nNOTE: The files in the repo are not organized." + }, + { + "author": "kevin314", + "title": "ComfyUI-FastVideo", + "reference": "https://github.com/kevin314/ComfyUI-FastVideo", + "files": [ + "https://github.com/kevin314/ComfyUI-FastVideo" + ], + "description": "NODES: Video Generator, Inference Args, VAE Config, Text Encoder Config, DIT Config", + "install_type": "git-clone" + }, + { + "author": "benda1989", + "title": "Comfyui lama remover [WIP]", + "reference": "https://github.com/benda1989/WaterMarkRemover_ComfyUI", + "files": [ + "https://github.com/benda1989/WaterMarkRemover_ComfyUI" + ], + "install_type": "git-clone", + "description": "A very simple ComfyUI node to remove item like image/video with mask watermark\nNOTE: The files in the repo are not organized." + }, + { + "author": "3dmindscapper", + "title": "ComfyUI-PartField [WIP]", + "reference": "https://github.com/3dmindscapper/ComfyUI-PartField", + "files": [ + "https://github.com/3dmindscapper/ComfyUI-PartField" + ], + "install_type": "git-clone", + "description": "ComfyUI implementation of the partfield nvidea segmentation models\nNOTE: The files in the repo are not organized." + }, + { + "author": "shinich39", + "title": "comfyui-textarea-is-shit", + "reference": "https://github.com/shinich39/comfyui-textarea-is-shit", + "files": [ + "https://github.com/shinich39/comfyui-textarea-is-shit" + ], + "description": "HTML gives me a textarea like piece of shit.", + "install_type": "git-clone" + }, + { + "author": "shinich39", + "title": "comfyui-nothing-happened", + "reference": "httphttps://github.com/shinich39/comfyui-nothing-happened", + "files": [ + "https://github.com/shinich39/comfyui-nothing-happened" + ], + "description": "Save image and keep metadata.", + "install_type": "git-clone" + }, + { + "author": "CY-CHENYUE", + "title": "ComfyUI-FramePack-HY", + "reference": "https://github.com/CY-CHENYUE/ComfyUI-FramePack-HY", + "files": [ + "https://github.com/CY-CHENYUE/ComfyUI-FramePack-HY" + ], + "description": "FramePack in ComfyUI", + "install_type": "git-clone" + }, + { + "author": "silveroxides", + "title": "ComfyUI_ReduxEmbedToolkit", + "reference": "https://github.com/silveroxides/ComfyUI_ReduxEmbedToolkit", + "files": [ + "https://github.com/silveroxides/ComfyUI_ReduxEmbedToolkit" + ], + "install_type": "git-clone", + "description": "Custom nodes for managing, saving and loading of Redux/Style based embeddings." + }, + { + "author": "StaffsGull", + "title": "comfyui_scene_builder [WIP]", + "reference": "https://github.com/StaffsGull/comfyui_scene_builder", + "files": [ + "https://github.com/StaffsGull/comfyui_scene_builder" + ], + "install_type": "git-clone", + "description": "NODES: CharacterBuilderNode, ClothingItemNode, ClothingMergerNode, EnvironmentBuilderNode, MergeCharactersNode, PhotoStyleBuilderNode, SceneCombinerNode\nNOTE: The files in the repo are not organized." + }, + { + "author": "gagaprince", + "title": "ComfyUI_gaga_utils", + "reference": "https://github.com/gagaprince/ComfyUI_gaga_utils", + "files": [ + "https://github.com/gagaprince/ComfyUI_gaga_utils" + ], + "install_type": "git-clone", + "description": "NODES: GagaGetFileList, GagaGetStringListSize, GagaSplitStringToList, GagaTest, GagaBatchStringReplace" + }, + { + "author": "ftechmax", + "title": "ComfyUI-NovaKit-Pack", + "reference": "https://github.com/ftechmax/ComfyUI-NovaKit-Pack", + "files": [ + "https://github.com/ftechmax/ComfyUI-NovaKit-Pack" + ], + "install_type": "git-clone", + "description": "NODES: Count Tokens" + }, + { + "author": "BobRandomNumber", + "title": "ComfyUI DiaTest TTS Node [WIP]", + "reference": "https://github.com/BobRandomNumber/ComfyUI-DiaTTS", + "files": [ + "https://github.com/BobRandomNumber/ComfyUI-DiaTTS" + ], + "install_type": "git-clone", + "description": "Partial ComfyUI Dia implementation" + }, + { + "author": "jtydhr88", + "title": "ComfyUI-1hewNodes [WIP]", + "reference": "https://github.com/1hew/ComfyUI-1hewNodes", + "files": [ + "https://github.com/1hew/ComfyUI-1hewNodes" + ], + "install_type": "git-clone", + "description": "NODES: Solid, Luma Matte, Image Concatenate, Image Crop With BBox, Image Paste\nNOTE: The files in the repo are not organized." + }, + { + "author": "jtydhr88", + "title": "ComfyUI Frontend Vue Basic [WIP]", + "reference": "https://github.com/jtydhr88/ComfyUI_frontend_vue_basic", + "files": [ + "https://github.com/jtydhr88/ComfyUI_frontend_vue_basic" + ], + "install_type": "git-clone", + "description": "A demonstration custom node that showcases how to integrate Vue as a frontend framework within ComfyUI, complete with PrimeVue components and vue-i18n support." + }, + { + "author": "silent-rain", + "title": "ComfyUI-SilentRain", + "reference": "https://github.com/silent-rain/ComfyUI-SilentRain", + "files": [ + "https://github.com/silent-rain/ComfyUI-SilentRain" + ], + "install_type": "git-clone", + "description": "Ecological extension of comfyui using Rust language." + }, + { + "author": "Linsoo", + "title": "ComfyUI-Linsoo-Custom-Nodes", + "reference": "https://github.com/Linsoo/ComfyUI-Linsoo-Custom-Nodes", + "files": [ + "https://github.com/Linsoo/ComfyUI-Linsoo-Custom-Nodes" + ], + "install_type": "git-clone", + "description": "NODES: Linsoo Save Image, Linsoo Load Image (In development.. not working), Linsoo Empty Latent Image, Linsoo Multi Inputs, Linsoo Multi Outputs" + }, + { + "author": "facok", + "title": "ComfyUI-FokToolset", + "reference": "https://github.com/facok/ComfyUI-FokToolset", + "files": [ + "https://github.com/facok/ComfyUI-FokToolset" + ], + "install_type": "git-clone", + "description": "NODES: Fok Preprocess Ref Image (Phantom)" + }, + { + "author": "EricRollei", + "title": "Comfy-Metadata-System [WIP]", + "reference": "https://github.com/EricRollei/Comfy-Metadata-System", + "files": [ + "https://github.com/EricRollei/Comfy-Metadata-System" + ], + "install_type": "git-clone", + "description": "Series of custom Comfyui Nodes that collects and saves metadata to embedded (png, jpg) as well as optional xmp and txt sidecars and database" + }, + { + "author": "turskeli", + "title": "comfyui-SetWallpaper", + "reference": "https://github.com/turskeli/comfyui-SetWallpaper", + "files": [ + "https://github.com/turskeli/comfyui-SetWallpaper" + ], + "install_type": "git-clone", + "description": "Simple wallpaper node for ComfyUI. Curently only supports Windows OS" + }, + { + "author": "Sophylax", + "title": "ComfyUI-ReferenceMerge", + "reference": "https://github.com/Sophylax/ComfyUI-ReferenceMerge", + "files": [ + "https://github.com/Sophylax/ComfyUI-ReferenceMerge" + ], + "install_type": "git-clone", + "description": "NODES: Combine Images and Mask, Restitch Combined Crop" + }, + { + "author": "bandido37", + "title": "Kaggle ComfyUI Local Save Node [WIP]", + "reference": "https://github.com/bandido37/comfyui-kaggle-local-save", + "files": [ + "https://github.com/bandido37/comfyui-kaggle-local-save" + ], + "install_type": "git-clone", + "description": "This custom node for ComfyUI allows you to save generated images directly to your local PC instead of Kaggle's cloud output folder.\nNOTE: The files in the repo are not organized." + }, + { + "author": "springjk", + "title": "Psutil Container Memory Patch", + "reference": "https://github.com/springjk/ComfyUI-Psutil-Container-Memory-Patch", + "files": [ + "https://github.com/springjk/ComfyUI-Psutil-Container-Memory-Patch" + ], + "install_type": "git-clone", + "description": "Make ComfyUI get correct memory information in the container (psutil monkey path)" + }, + { + "author": "songtianhui", + "title": "ComfyUI-DMM [WIP]", + "reference": "https://github.com/songtianhui/ComfyUI-DMM", + "files": [ + "https://github.com/songtianhui/ComfyUI-DMM" + ], + "install_type": "git-clone", + "description": "NODES: DMMLoader, DMMApply" + }, + { + "author": "leon-etienne", + "title": "ComfyUI_Scoring-Nodes", + "reference": "https://github.com/leon-etienne/ComfyUI_Scoring-Nodes", + "files": [ + "https://github.com/leon-etienne/ComfyUI_Scoring-Nodes" + ], + "install_type": "git-clone", + "description": "NODES: Text Similarity (CLIP), Image Similarity (CLIP), Multi Text→Image Similarity, Multi Image→Text Similarity, Aesthetic Score, Multi Aesthetic Comparison" + }, + { + "author": "tanmoy-it", + "title": "comfyuiCustomNode", + "reference": "https://github.com/tanmoy-it/comfyuiCustomNode", + "files": [ + "https://github.com/tanmoy-it/comfyuiCustomNode" + ], + "install_type": "git-clone", + "description": "NODES: Download Image (Direct/No Save)" + }, + { + "author": "Jingwen-genies", + "title": "comfyui-genies-nodes", + "reference": "https://github.com/Jingwen-genies/comfyui-genies-nodes", + "files": [ + "https://github.com/Jingwen-genies/comfyui-genies-nodes" + ], + "install_type": "git-clone", + "description": "NODES: Genies Pose Estimation, Genies Scale Face by Keypoints, Get V Channel from HSV, Select RGB by Mask" + }, + { + "author": "Tawbaware", + "title": "ComfyUI-Tawbaware [WIP]", + "reference": "https://github.com/Tawbaware/ComfyUI-Tawbaware", + "files": [ + "https://github.com/Tawbaware/ComfyUI-Tawbaware" + ], + "install_type": "git-clone", + "description": "A collection of custom nodes for ComfyUI\nNOTE: The files in the repo are not organized." + }, + { + "author": "lucafoscili", + "title": "LF Nodes [UNSAFE]", + "reference": "https://github.com/lucafoscili/lf-nodes", + "files": [ + "https://github.com/lucafoscili/lf-nodes" + ], + "install_type": "git-clone", + "description": "Custom nodes with a touch of extra UX, including: history for primitives, JSON manipulation, logic switches with visual feedback, LLM chat... and more!\n[w/This node pack contains a node with a vulnerability that allows arbitrary code execution.]" + }, + { + "author": "jerryname2022", + "title": "ComfyUI-Real-ESRGAN [WIP]", + "reference": "https://github.com/jerryname2022/ComfyUI-Real-ESRGAN", + "files": [ + "https://github.com/jerryname2022/ComfyUI-Real-ESRGAN" + ], + "install_type": "git-clone", + "description": "NODES: Real-ESRGAN Model Loader, GFPGAN Model Loader, Real-ESRGAN Image Generator, GFPGAN Image Generator" + }, + { + "author": "mm-akhtar", + "title": "comfyui-mask-selector-node", + "reference": "https://github.com/mm-akhtar/comfyui-mask-selector-node", + "files": [ + "https://github.com/mm-akhtar/comfyui-mask-selector-node" + ], + "install_type": "git-clone", + "description": "NODES: Mask Selector" + }, + { + "author": "ryanontheinside", + "title": "ComfyUI-Livepeer [WIP]", + "reference": "https://github.com/ryanontheinside/ComfyUI-Livepeer", + "files": [ + "https://github.com/ryanontheinside/ComfyUI-Livepeer" + ], + "install_type": "git-clone", + "description": "A ComfyUI extension that provides integration with [a/Livepeer](https://livepeer.org/)'s AI services allowing for both sync and async generation." + }, + { + "author": "newraina", + "title": "ComfyUI-Remote-Save-Image [UNSAFE]", + "reference": "https://github.com/newraina/ComfyUI-Remote-Save-Image", + "files": [ + "https://github.com/newraina/ComfyUI-Remote-Save-Image" + ], + "install_type": "git-clone", + "description": "A custom node for ComfyUI that allows uploading generated images to any HTTP endpoint.[w/This node allows any users to send any locally stored image to a specified URL.]" + }, + { + "author": "SXQBW", + "title": "ComfyUI-Qwen-VLM [WIP]", + "reference": "https://github.com/SXQBW/ComfyUI-Qwen3", + "files": [ + "https://github.com/SXQBW/ComfyUI-Qwen3" + ], + "install_type": "git-clone", + "description": "NODES: QwenVLM" + }, + { + "author": "kijai", + "title": "ComfyUI-FramePackWrapper [WIP]", + "reference": "https://github.com/kijai/ComfyUI-FramePackWrapper", + "files": [ + "https://github.com/kijai/ComfyUI-FramePackWrapper" + ], + "install_type": "git-clone", + "description": "ComfyUI Wrapper for FramePack by lllyasviel" + }, + { + "author": "WaiyanLing", + "title": "ComfyUI-Tracking [WIP]", + "reference": "https://github.com/WaiyanLing/ComfyUI-Tracking", + "files": [ + "https://github.com/WaiyanLing/ComfyUI-Tracking" + ], + "install_type": "git-clone", + "description": "ComfyUI-Tracking This node pack helps to conveniently collect invocation data from workflows for further study.\nNOTE: The files in the repo are not organized." + }, + { + "author": "vladp0727", + "title": "ComfyUI Simple Image Tools [WIP]", + "reference": "https://github.com/vladp0727/Comfyui-with-Furniture", + "files": [ + "https://github.com/vladp0727/Comfyui-with-Furniture" + ], + "install_type": "git-clone", + "description": "NODES: Get Mask From Alpha, Get Quadrilateral Outfit\nNOTE: The files in the repo are not organized." + }, + { + "author": "Simlym", + "title": "Simlym/comfyui-prompt-helper [WIP]", + "reference": "https://github.com/Simlym/comfyui-prompt-helper", + "files": [ + "https://github.com/Simlym/comfyui-prompt-helper" + ], + "install_type": "git-clone", + "description": "A ComfyUI custom node for processing Chinese prompts and generating English prompts with LLM\nNOTE: The files in the repo are not organized." + }, + { + "author": "ryanontheinside", + "title": "ComfyUI MineWorld Nodes [WIP]", + "reference": "https://github.com/ryanontheinside/ComfyUI-MineWorld", + "files": [ + "https://github.com/ryanontheinside/ComfyUI-MineWorld" + ], + "install_type": "git-clone", + "description": "This extension integrates Microsoft's MineWorld - an interactive world model for Minecraft - into ComfyUI.\nMineWorld allows you to generate interactive Minecraft gameplay based on actions you provide, creating realistic Minecraft gameplay videos." + }, + { + "author": "SanDiegoDude", + "title": "HiDreamSampler for ComfyUI [WIP]", + "reference": "https://github.com/SanDiegoDude/ComfyUI-HiDream-Sampler", + "files": [ + "https://github.com/SanDiegoDude/ComfyUI-HiDream-Sampler" + ], + "install_type": "git-clone", + "description": "A custom ComfyUI node for generating images using the HiDream AI model.\nNOTE: The files in the repo are not organized." + }, + { + "author": "ZenAI-Vietnam", + "title": "ComfyUI_InfiniteYou [NAME CONFLICT]", + "reference": "https://github.com/ZenAI-Vietnam/ComfyUI_InfiniteYou", + "files": [ + "https://github.com/ZenAI-Vietnam/ComfyUI_InfiniteYou" + ], + "install_type": "git-clone", + "description": "An implementation of InfiniteYou for ComfyUI. Native support for [a/InfiniteYou](https://github.com/bytedance/InfiniteYou) in ComfyUI, designed by the ZenAI team." + }, + { + "author": "filipemeneses", + "title": "ComfyUI_html [UNSAFE]", + "reference": "https://github.com/filipemeneses/ComfyUI_html", + "files": [ + "https://github.com/filipemeneses/ComfyUI_html" + ], + "install_type": "git-clone", + "description": "Nodes to manipulate HTML.[w/This extension poses a risk of XSS vulnerability.]" + }, + { + "author": "FaberVS", + "title": "MultiModel", + "reference": "https://github.com/FaberVS/MultiModel", + "files": [ + "https://github.com/FaberVS/MultiModel" + ], + "install_type": "git-clone", + "description": "A collection of ComfyUI nodes enabling seamless integration of multiple models into workflows without requiring constant configuration." + }, + { + "author": "m-ai-studio", + "title": "mai-prompt-progress", + "reference": "https://github.com/m-ai-studio/mai-prompt-progress", + "files": [ + "https://github.com/m-ai-studio/mai-prompt-progress" + ], + "install_type": "git-clone", + "description": "ComfyUI extensions for sending prompt progress to webhook" + }, + { + "author": "ashllay", + "title": "ComfyUI_MoreComfy", + "reference": "https://github.com/ashllay/ComfyUI_MoreComfy", + "files": [ + "https://github.com/ashllay/ComfyUI_MoreComfy" + ], + "install_type": "git-clone", + "description": "NODES: MC Switch Seed/Image/Latent/Model/String, MC Alter Seed, MC Set Tile Size, MC Get Image Size, MC Multi Concat" + }, + { + "author": "gordon1chuge2623", + "title": "ComfyUI_seal_migration [WIP]", + "reference": "https://github.com/chuge26/ComfyUI_seal_migration", + "files": [ + "https://github.com/chuge26/ComfyUI_seal_migration" + ], + "install_type": "git-clone", + "description": "This project implements stamp migration in PDF files based on ComfyUI, allowing stamps from specified pages of a source PDF to be transferred to specified pages of a target PDF.\nNOTE: The files in the repo are not organized." + }, + { + "author": "gordon123", + "title": "ComfyUI_srt2speech [WIP]", + "reference": "https://github.com/gordon123/ComfyUI_srt2speech", + "files": [ + "https://github.com/gordon123/ComfyUI_srt2speech" + ], + "install_type": "git-clone", + "description": "ComfyUI_srt2speech" + }, + { + "author": "hnmr293", + "title": "ComfyUI-SamOne - one-step sampling", + "reference": "https://github.com/hnmr293/ComfyUI-SamOne", + "files": [ + "https://github.com/hnmr293/ComfyUI-SamOne" + ], + "install_type": "git-clone", + "description": "This is a node that advances sampling by just one step in ComfyUI." + }, + { + "author": "rphmeier", + "title": "comfyui-videodepthanything", + "reference": "https://github.com/rphmeier/comfyui-videodepthanything", + "files": [ + "https://github.com/rphmeier/comfyui-videodepthanything" + ], + "install_type": "git-clone", + "description": "VideoDepthAnything nodes for ComfyUI" + }, + { + "author": "benmizrahi", + "title": "ComfyGCS [WIP]", + "reference": "https://github.com/benmizrahi/ComfyGCS", + "files": [ + "https://github.com/benmizrahi/ComfyGCS" + ], + "install_type": "git-clone", + "description": "ComfyGCS is a robust read/write plugin for Google Cloud Storage, designed to simplify interaction with GCS buckets in your projects.\nNOTE: The files in the repo are not organized." + }, + { + "author": "dogcomplex", + "title": "ComfyUI-LOKI [WIP]", + "reference": "https://github.com/dogcomplex/ComfyUI-LOKI", + "files": [ + "https://github.com/dogcomplex/ComfyUI-LOKI" + ], + "install_type": "git-clone", + "description": "NODES: Glamour\nNOTE: This node pack installs pip dependencies outside the control of ComfyUI-Manager." + }, + { + "author": "hunzmusic", + "title": "Comfyui-CraftsMan3DWrapper [WIP]", + "reference": "https://github.com/hunzmusic/Comfyui-CraftsMan3DWrapper", + "files": [ + "https://github.com/hunzmusic/Comfyui-CraftsMan3DWrapper" + ], + "install_type": "git-clone", + "description": "A wrapper for CraftsMan\nNOTE: The files in the repo are not organized." + }, + { + "author": "Slix-M-Lestragg", + "title": "comfyui-enhanced [WIP]", + "reference": "https://github.com/Slix-M-Lestragg/comfyui-enhanced", + "files": [ + "https://github.com/Slix-M-Lestragg/comfyui-enhanced" + ], + "install_type": "git-clone", + "description": "A collection of enhanced nodes for ComfyUI that provide powerful additional functionality to your workflows.\nNOTE: The files in the repo are not organized." + }, + { + "author": "tzsoulcap", + "title": "ComfyUI-SaveImg-W-MetaData", + "reference": "https://github.com/tzsoulcap/ComfyUI-SaveImg-W-MetaData", + "files": [ + "https://github.com/tzsoulcap/ComfyUI-SaveImg-W-MetaData" + ], + "install_type": "git-clone", + "description": "NODES: CAP Checkpoint Selector, CAP Save Image w/Metadata, CAP Load Image with Metadata, CAP Tag Image, CAP Sampler Selector, CAP Scheduler Selector, CAP Seed Generator, CAP String Literal, CAP Width/Height Literal, CAP Cfg Literal, CAP Int Literal" + }, + { + "author": "hylarucoder", + "title": "comfyui-copilot", + "reference": "https://github.com/hylarucoder/comfyui-copilot", + "files": [ + "https://github.com/hylarucoder/comfyui-copilot" + ], + "install_type": "git-clone", + "description": "NODES: Eagle Image Node for PNGInfo, SDXL Resolution Presets (ws), SDXL Prompt Styler, SDXL Prompt Styler Advanced" + }, + { + "author": "SS-snap", + "title": "Comfyui_SSsnap_pose-Remapping", + "reference": "https://github.com/SS-snap/Comfyui_SSsnap_pose-Remapping", + "files": [ + "https://github.com/SS-snap/Comfyui_SSsnap_pose-Remapping" + ], + "install_type": "git-clone", + "description": "NODES: SSsnap Apply Pose Diff ✂️, SSsnap Pose Diff Calculator 🛠️" + }, + { + "author": "AlejandroTuzzi", + "title": "TUZZI-ByPass [WIP]", + "reference": "https://github.com/AlejandroTuzzi/TUZZI-ByPass", + "files": [ + "https://github.com/AlejandroTuzzi/TUZZI-ByPass" + ], + "install_type": "git-clone", + "description": "Custom nodes for automated AI pipelines\nNOTE: The files in the repo are not organized." + }, + { + "author": "oxysoft", + "title": "Comfy-Compel", + "reference": "https://github.com/oxysoft/Comfy-Compel", + "files": [ + "https://github.com/oxysoft/Comfy-Compel" + ], + "install_type": "git-clone", + "description": "NODES: CLIP Embed (Compel)" + }, + { + "author": "QingLuanWithoutHeart", + "title": "ComfyUI File/Image Utils Nodes [UNSAFE]", + "reference": "https://github.com/QingLuanWithoutHeart/comfyui-file-image-utils", + "files": [ + "https://github.com/QingLuanWithoutHeart/comfyui-file-image-utils" + ], + "install_type": "git-clone", + "description": "This custom node set provides useful utilities for file operations and image loading in ComfyUI." + }, + { + "author": "pmarmotte2", + "title": "VibeVoiceSelector [WIP]", + "reference": "https://github.com/pmarmotte2/Comfyui-VibeVoiceSelector", + "files": [ + "https://github.com/pmarmotte2/Comfyui-VibeVoiceSelector" + ], + "install_type": "git-clone", + "description": "NODES: Vibe Voice Selector" + }, + { + "author": "Temult", + "title": "TWanVideoSigmaSampler: EXPERIMENTAL [WIP]", + "reference": "https://github.com/Temult/TWanSigmaSampler", + "files": [ + "https://github.com/Temult/TWanSigmaSampler" + ], + "install_type": "git-clone", + "description": "A ComfyUI custom node that modifies the WanVideoSampler to accept an external sigma schedule. Allows for customized and non-standard noise schedules in Wan 2.1 video generation workflow.\nNOTE: The files in the repo are not organized." + }, + { + "author": "wordbrew", + "title": "WAN Control Nodes for ComfyUI [WIP]", + "reference": "https://github.com/wordbrew/comfyui-wan-control-nodes", + "files": [ + "https://github.com/wordbrew/comfyui-wan-control-nodes" + ], + "install_type": "git-clone", + "description": "This pack provides enhanced control nodes for working with Wan video models in ComfyUI. It is under active development and may change regularly, or may not. Depends entirely on my free time and waning interest. Please don't come to rely on it for anything, but you are welcome to improve on it.\nNOTE: The files in the repo are not organized." + }, + { + "author": "techtruth", + "title": "ComfyUI-Dreambooth", + "reference": "https://github.com/techtruth/ComfyUI-Dreambooth", + "files": [ + "https://github.com/techtruth/ComfyUI-Dreambooth" + ], + "install_type": "git-clone", + "description": "NODES: Dreambooth Trainer" + }, + { + "author": "438443467", + "title": "ComfyUI-SanMian-Nodes", + "reference": "https://github.com/438443467/ComfyUI-SanMian-Nodes", + "files": [ + "https://github.com/438443467/ComfyUI-SanMian-Nodes" + ], + "install_type": "git-clone", + "description": "NODES: Add Text To Image, Adjust Hex Brightness, Adjust Transparency By Mask, Align Images with Mask, Align Restore Json, Binarize Mask, Blend ICLight, ..." + }, + { + "author": "alexgenovese", + "title": "ComfyUI-Reica", + "reference": "https://github.com/alexgenovese/ComfyUI-Reica", + "files": [ + "https://github.com/alexgenovese/ComfyUI-Reica" + ], + "install_type": "git-clone", + "description": "NODES: Reica Text Image Display, Flux Image Generator, Reica GCP: Read Image, Reica GCP: Write Image & Get URL, Reica API: Send HTTP Notification" + }, + { + "author": "yanlang0123", + "title": "ComfyUI_Lam", + "reference": "https://github.com/yanlang0123/ComfyUI_Lam", + "files": [ + "https://github.com/yanlang0123/ComfyUI_Lam" + ], + "install_type": "git-clone", + "description": "This extension has some useful nodes, loops, wechat public number +AI chat drawing, distributed cluster." + }, + { + "author": "Stable-X", + "title": "ComfyUI-Hi3DGen", + "reference": "https://github.com/Stable-X/ComfyUI-Hi3DGen", + "files": [ + "https://github.com/Stable-X/ComfyUI-Hi3DGen" + ], + "install_type": "git-clone", + "description": "This extension integrates [a/Hi3DGen](https://github.com/Stable-X/Hi3DGen) into ComfyUI, allowing user to generate high-fidelity 3D geometry generation from Images.[w/If the *sageattention* package is installed, this node pack causes problems.]" + }, + { + "author": "stiffy-committee", + "title": "comfyui-stiffy-nodes", + "reference": "https://github.com/V-woodpecker-V/comfyui-stiffy-nodes", + "files": [ + "https://github.com/V-woodpecker-V/comfyui-stiffy-nodes" + ], + "install_type": "git-clone", + "description": "NODES: StiffyPrompter, StiffyPersistentPrompter, StiffyDecoder, StiffyDebugger, ..." + }, + { + "author": "chetusangolgi", + "title": "Comfyui-supabase", + "reference": "https://github.com/chetusangolgi/Comfyui-supabase", + "files": [ + "https://github.com/chetusangolgi/Comfyui-supabase" + ], + "install_type": "git-clone", + "description": "NODES: Watch Supabase Bucket, Upload Image to Supabase" + }, + { + "author": "rickyars", + "title": "sd-cn-animation", + "reference": "https://github.com/rickyars/sd-cn-animation", + "files": [ + "https://github.com/rickyars/sd-cn-animation" + ], + "install_type": "git-clone", + "description": "SD-CN animation for Comfyui" + }, + { + "author": "daracazamea", + "title": "DCNodess [WIP]", + "reference": "https://github.com/daracazamea/comfyUI-DCNodes", + "files": [ + "https://github.com/daracazamea/comfyUI-DCNodes" + ], + "install_type": "git-clone", + "description": "NODES: Start Timer (Pass-Through), Get Generation Time, Manual Trigger, Flux: Resolution Picker, SDXL: Resolution Picker\nNOTE: The files in the repo are not organized." + }, + { + "author": "hunzmusic", + "title": "ComfyUI-Hunyuan3DTools [WIP]", + "reference": "https://github.com/hunzmusic/ComfyUI-Hunyuan3DTools", + "files": [ + "https://github.com/hunzmusic/ComfyUI-Hunyuan3DTools" + ], + "install_type": "git-clone", + "description": "NODES: Hy3DTools Render Specific View, Hy3DTools Back-Project Inpaint\nNOTE: The files in the repo are not organized." + }, + { + "author": "grokuku", + "title": "Holaf Custom Nodes for ComfyUI", + "reference": "https://github.com/grokuku/ComfyUI-Holaf", + "files": [ + "https://github.com/grokuku/ComfyUI-Holaf" + ], + "install_type": "git-clone", + "description": "NODES: Neurogrid Overload, Tile Calculator, Slice Calculator, Save Image, Tiled KSampler, KSampler, Image Comparer, Upscale, Overlay, Resolution Preset, Benchmark Runner, Benchmark Plotter, Benchmark Loader" + }, + { + "author": "Burgstall-labs", + "title": "ComfyUI-BS_FalAi-API-Video [WIP]", + "reference": "https://github.com/Burgstall-labs/ComfyUI-BS_FalAi-API-Video", + "files": [ + "https://github.com/Burgstall-labs/ComfyUI-BS_FalAi-API-Video" + ], + "install_type": "git-clone", + "description": "Experimental ComfyUI Custom Node for generating videos using various FAL AI API endpoints.\nNOTE: The files in the repo are not organized." + }, + { + "author": "uauaouau", + "title": "Mycraft [WIP]", + "reference": "https://github.com/sorption-dev/mycraft-comfyui", + "files": [ + "https://github.com/sorption-dev/mycraft-comfyui" + ], + "install_type": "git-clone", + "description": "Mycraft provides a limitless storyboard experience for image generation, powered by the ComfyUI API.\nEach container functions as an independent ComfyUI workflow, Supports workflows (text-to-text) and fine-tuning (image-to-image), Supports workflow customization." + }, + { + "author": "zhaorishuai", + "title": "ComfyUI-StoryboardDistributor", + "reference": "https://github.com/zhaorishuai/ComfyUI-StoryboardDistributor", + "files": [ + "https://github.com/zhaorishuai/ComfyUI-StoryboardDistributor" + ], + "install_type": "git-clone", + "description": "A ComfyUI plugin that automatically assigns storyboard content to 9 storyboard nodes." + }, + { + "author": "alexgenovese", + "title": "ComfyUI-Diffusion-4k [WIP]", + "reference": "https://github.com/alexgenovese/ComfyUI-Diffusion-4k", + "files": [ + "https://github.com/alexgenovese/ComfyUI-Diffusion-4k" + ], + "install_type": "git-clone", + "description": "A ComfyUI custom node implementation of the Diffusion 4K research paper.\nNOTE: The files in the repo are not organized." + }, + { + "author": "KERRY-YUAN", + "title": "Python_Executor [UNSAFE]", + "id": "PythonExecutor", + "reference": "https://github.com/KERRY-YUAN/ComfyUI_Python_Executor", + "files": [ + "https://github.com/KERRY-YUAN/ComfyUI_Python_Executor" + ], + "install_type": "git-clone", + "description": "Nodes: Provides nodes to execute arbitrary Python code snippets and Resize images directly within ComfyUI workflows. [w/This node allows you to execute arbitrary code via the workflow.]" + }, + { + "author": "ashllay", + "title": "ComfyUI_MoreComfy", + "reference": "https://github.com/ashllay/ComfyUI_MoreComfy", + "files": [ + "https://github.com/ashllay/ComfyUI_MoreComfy" + ], + "install_type": "git-clone", + "description": "NODES: MC Switch Seed, MC Alter Seed, MC Set Tile Size, MC Multi Concat" + }, + { + "author": "ayaoayaoayaoaya", + "title": "ComfyUI-KLUT-DeepSeek-API [WIP]", + "reference": "https://github.com/ayaoayaoayaoaya/ComfyUI-KLUT-DeepSeek-API", + "files": [ + "https://github.com/ayaoayaoayaoaya/ComfyUI-KLUT-DeepSeek-API" + ], + "install_type": "git-clone", + "description": "A collection of utility / quality-of-life nodes for ComfyUI. Probably only useful to me.\nNOTE: The files in the repo are not organized." + }, + { + "author": "olyyarm", + "title": "ComfyUI-VLMStudio", + "reference": "https://github.com/KurtHokke/ComfyUI_KurtHokke_Nodes", + "files": [ + "https://github.com/KurtHokke/ComfyUI_KurtHokke_Nodes" + ], + "install_type": "git-clone", + "description": "NODES: Node_BOOL/INT/Float, BooleanToPipe, BooleanFromPipe, ExpMath, ExpMathDual/Quad, ...." + }, + { + "author": "olyyarm", + "title": "ComfyUI-VLMStudio", + "reference": "https://github.com/olyyarm/ComfyUI-VLMStudio", + "files": [ + "https://raw.githubusercontent.com/olyyarm/ComfyUI-VLMStudio/refs/heads/master/vlm_visionary_node_v3_.py" + ], + "install_type": "copy", + "description": "NODES: GemmaMultimodalAnalyzer" + }, + { + "author": "apetitbois", + "title": "nova_utils", + "reference": "https://github.com/apetitbois/nova_utils", + "files": [ + "https://github.com/apetitbois/nova_utils" + ], + "install_type": "git-clone", + "description": "Nova utils for ComfyUI" + }, + { + "author": "sugarkwork", + "title": "comfyui_my_img_util", + "reference": "https://github.com/sugarkwork/comfyui_my_img_util", + "files": [ + "https://github.com/sugarkwork/comfyui_my_img_util" + ], + "install_type": "git-clone", + "description": "NODES: Simple Image Rotate" + }, + { + "author": "DonutsDelivery", + "title": "ComfyUI-DonutDetailer", + "reference": "https://github.com/DonutsDelivery/ComfyUI-DonutNodes", + "files": [ + "https://github.com/DonutsDelivery/ComfyUI-DonutNodes" + ], + "install_type": "git-clone", + "description": "This is an experimental node I made to mimick the 'adjust' in A1111 Supermerger [a/https://github.com/hako-mikan/sd-webui-supermerger?tab=readme-ov-file#adjust](https://github.com/hako-mikan/sd-webui-supermerger?tab=readme-ov-file#adjust)." + }, + { + "author": "ZenAI-Vietnam", + "title": "ComfyUI-gemini-IG", + "reference": "https://github.com/ZenAI-Vietnam/ComfyUI-gemini-IG", + "files": [ + "https://github.com/ZenAI-Vietnam/ComfyUI-gemini-IG" + ], + "install_type": "git-clone", + "description": "NODES: Gemini Image Generation, Gemini Text Generation" + }, + { + "author": "hunzmusic", + "title": "comfyui-hnznodes", + "reference": "https://github.com/hunzmusic/comfyui-hnznodes", + "files": [ + "https://github.com/hunzmusic/comfyui-hnznodes" + ], + "install_type": "git-clone", + "description": "NODES: Combine Channels Grayscale, Reorder Image Batch, Male Character Prompt" + }, + { + "author": "cidiro", + "title": "cid-node-pack", + "reference": "https://github.com/cidiro/cid-node-pack", + "files": [ + "https://github.com/cidiro/cid-node-pack" + ], + "install_type": "git-clone", + "description": "A lightweight node pack for ComfyUI that adds a few handy nodes that I use in my workflows" + }, + { + "author": "CeeVeeR", + "title": "ComfyUi-Text-Tiler", + "reference": "https://github.com/CeeVeeR/ComfyUi-Text-Tiler", + "files": [ + "https://github.com/CeeVeeR/ComfyUi-Text-Tiler" + ], + "install_type": "git-clone", + "description": "NODES: Text Tiler" + }, + { + "author": "Dreamshot-io", + "title": "ComfyUI-Extend-Resolution", + "reference": "https://github.com/Dreamshot-io/ComfyUI-Extend-Resolution", + "files": [ + "https://github.com/Dreamshot-io/ComfyUI-Extend-Resolution" + ], + "install_type": "git-clone", + "description": "NODES: Resolution Padding" + }, + { + "author": "l1yongch1", + "title": "ComfyUI-YcNodes", + "reference": "https://github.com/l1yongch1/ComfyUI-YcNodes", + "files": [ + "https://github.com/l1yongch1/ComfyUI-YcNodes" + ], + "install_type": "git-clone", + "description": "NODES: RemoveHighlightAndBlur, RoundedCorners, PaddingAccordingToBackground\npersonal custom nodes for learning" + }, + { + "author": "vchopine", + "title": "ComfyUI_Toolbox", + "reference": "https://github.com/vchopine/ComfyUI_Toolbox", + "files": [ + "https://github.com/vchopine/ComfyUI_Toolbox" + ], + "install_type": "git-clone", + "description": "Model & Aspect Ratio Selector Node for ComfyUI\nNOTE: The files in the repo are not organized." + }, + { + "author": "Solankimayursinh", + "title": "PMSnodes [WIP]", + "reference": "https://github.com/Solankimayursinh/PMSnodes", + "files": [ + "https://github.com/Solankimayursinh/PMSnodes" + ], + "install_type": "git-clone", + "description": "A custom nodes for ComfyUI to Load audio in Base64 format and Send Audio to Websocket in Base64 Format for creating API of Audio related AI\nNOTE: The files in the repo are not organized." + }, + { + "author": "rhinoflavored", + "title": "comfyui_QT", + "reference": "https://github.com/rhinoflavored/comfyui_QT", + "files": [ + "https://github.com/rhinoflavored/comfyui_QT" + ], + "install_type": "git-clone", + "description": "bunch of image manipulation nodes....\nNOTE: The files in the repo are not organized." + }, + { + "author": "ricklove", + "title": "ComfyUI-AutoSeg-SAM2", + "reference": "https://github.com/ricklove/ComfyUI-AutoSeg-SAM2", + "files": [ + "https://github.com/ricklove/ComfyUI-AutoSeg-SAM2" + ], + "install_type": "git-clone", + "description": "NODES: AutoSeg-SAM2 Batch Segmentation" + }, + { + "author": "JoeAu", + "title": "ComfyUI-PythonNode [UNSAFE]", + "reference": "https://github.com/JoeAu/ComfyUI-PythonNode", + "files": [ + "https://github.com/JoeAu/ComfyUI-PythonNode" + ], + "install_type": "git-clone", + "description": "A custom ComfyUI node that allows users to execute arbitrary Python code with a single input (value) and output (result), enabling flexible processing of the input value using any Python code before assigning the final result to result. It also captures print() output and exceptions for debugging.[w/This node is an unsafe node that includes the capability to execute arbitrary python script.]" + }, + { + "author": "smthemex", + "title": "ComfyUI_GPT_SoVITS_Lite", + "reference": "https://github.com/smthemex/ComfyUI_GPT_SoVITS_Lite", + "files": [ + "https://github.com/smthemex/ComfyUI_GPT_SoVITS_Lite" + ], + "install_type": "git-clone", + "description": "[a/GPT_SoVITS](https://github.com/RVC-Boss/GPT-SoVITS) infer only for ComfyUI users\nNOTE: The files in the repo are not organized." + }, + { + "author": "Nambi24", + "title": "ComfyUI-Save_Image", + "reference": "https://github.com/Nambi24/ComfyUI-Save_Image", + "files": [ + "https://github.com/Nambi24/ComfyUI-Save_Image" + ], + "description": "NODES: Save Image With Subfolder, Extract Last Path Component\nNOTE: The files in the repo are not organized.", + "install_type": "git-clone" + }, + { + "author": "sugarkwork", + "title": "comfyui_image_crop", + "reference": "https://github.com/sugarkwork/comfyui_image_crop", + "files": [ + "https://github.com/sugarkwork/comfyui_image_crop" + ], + "description": "NODES: CropTransparent, RestoreCrop, ExpandMultiple, CropReapply", + "install_type": "git-clone" + }, + { + "author": "AkiEvansDev", + "title": "ComfyUI-Tools", + "reference": "https://github.com/AkiEvansDev/ComfyUI-Tools", + "files": [ + "https://github.com/AkiEvansDev/ComfyUI-Tools" + ], + "install_type": "git-clone", + "description": "Custom nodes for basic actions." + }, + { + "author": "longzoho", + "title": "ComfyUI-Qdrant-Saver", + "reference": "https://github.com/longzoho/ComfyUI-Qdrant-Saver", + "files": [ + "https://github.com/longzoho/ComfyUI-Qdrant-Saver" + ], + "install_type": "git-clone", + "description": "NODES: QDrant Saver Node" + }, + { + "author": "RUFFY-369", + "title": "ComfyUI-FeatureBank", + "reference": "https://github.com/RUFFY-369/ComfyUI-FeatureBank", + "files": [ + "https://github.com/RUFFY-369/ComfyUI-FeatureBank" + ], + "install_type": "git-clone", + "description": "NODES: FeatureBankAttentionProcessor" + }, + { + "author": "Pablerdo", + "title": "ComfyUI-Sa2VAWrapper [WIP]", + "reference": "https://github.com/Pablerdo/ComfyUI-Sa2VAWrapper", + "files": [ + "https://github.com/Pablerdo/ComfyUI-Sa2VAWrapper" + ], + "install_type": "git-clone", + "description": "Wrapper for the Sa2VA model" + }, + { + "author": "aria1th", + "title": "ComfyUI-camietagger-onnx", + "reference": "https://github.com/aria1th/ComfyUI-camietagger-onnx", + "files": [ + "https://github.com/aria1th/ComfyUI-camietagger-onnx" + ], + "install_type": "git-clone", + "description": "NODES: Camie Tagger" + }, + { + "author": "zjkhurry", + "title": "comfyui_MetalFX [WIP]", + "reference": "https://github.com/zjkhurry/comfyui_MetalFX", + "files": [ + "https://github.com/zjkhurry/comfyui_MetalFX" + ], + "install_type": "git-clone", + "description": "A custom node for ComfyUI that enables high-quality image and video upscaling using Apple MetalFX technology.\nNOTE: The files in the repo are not organized." + }, + { + "author": "RoyKillington", + "title": "Miscomfy Nodes [WIP]", + "reference": "https://github.com/RoyKillington/miscomfy-nodes", + "files": [ + "https://github.com/RoyKillington/miscomfy-nodes" + ], + "install_type": "git-clone", + "description": "A repo of custom nodes for ComfyUI, from interacting with certain APIs to whatever other miscellanea I end up making" + }, + { + "author": "xmarked-ai", + "title": "ComfyUI_misc", + "reference": "https://github.com/xmarked-ai/ComfyUI_misc", + "files": [ + "https://github.com/xmarked-ai/ComfyUI_misc" + ], + "install_type": "git-clone", + "description": "NODES: Ace IntegerX, Ace FloatX, Ace Color FixX, White Balance X, Depth Displace X, Empty Latent X, KSampler Combo X, ..." + }, + { + "author": "Elypha", + "title": "ComfyUI-Prompt-Helper [WIP]", + "reference": "https://github.com/Elypha/ComfyUI-Prompt-Helper", + "files": [ + "https://github.com/Elypha/ComfyUI-Prompt-Helper" + ], + "install_type": "git-clone", + "description": "Concat conditions and prompts for ComfyUI" + }, + { + "author": "StoryWalker", + "title": "comfyui_flux_collection_advanced [WIP]", + "reference": "https://github.com/StoryWalker/comfyui_flux_collection_advanced", + "files": [ + "https://github.com/StoryWalker/comfyui_flux_collection_advanced" + ], + "install_type": "git-clone", + "description": "This is a collection focused in give a little more flexibility in the use of Flux models." + }, + { + "author": "OSAnimate", + "title": "ComfyUI-SpriteSheetMaker [WIP]", + "reference": "https://github.com/OSAnimate/ComfyUI-SpriteSheetMaker", + "files": [ + "https://github.com/OSAnimate/ComfyUI-SpriteSheetMaker" + ], + "install_type": "git-clone", + "description": "The sprite sheet maker node is a simple way to create sprite sheets and image grids.\nNOTE: The files in the repo are not organized." + }, + { + "author": "BuffMcBigHuge", + "title": "ComfyUI-Buff-Nodes [WIP]", + "reference": "https://github.com/BuffMcBigHuge/ComfyUI-Buff-Nodes", + "files": [ + "https://github.com/BuffMcBigHuge/ComfyUI-Buff-Nodes" + ], + "install_type": "git-clone", + "description": "Several quality-of-life batch operation and string manipulation nodes." + }, + { + "author": "ritikvirus", + "title": "ComfyUI Terminal Command Node [UNSAFE]", + "reference": "https://github.com/ritikvirus/comfyui-terminal-modal-node", + "files": [ + "https://github.com/ritikvirus/comfyui-terminal-modal-node" + ], + "install_type": "git-clone", + "description": "This repository provides a custom ComfyUI node that lets you execute arbitrary terminal commands directly from the ComfyUI interface. [w/This extension allows remote command execution.]" + }, + { + "author": "pixuai", + "title": "ComfyUI-PixuAI", + "reference": "https://github.com/pixuai/ComfyUI-PixuAI", + "files": [ + "https://github.com/pixuai/ComfyUI-PixuAI" + ], + "install_type": "git-clone", + "description": "A collection of ComfyUI nodes designed to streamline prompt creation, organization, and discovery - making your workflows faster and more intuitive." + }, + { + "author": "techidsk", + "title": "comfyui_molook_nodes [WIP]", + "reference": "https://github.com/techidsk/comfyui_molook_nodes", + "files": [ + "https://github.com/techidsk/comfyui_molook_nodes" + ], + "install_type": "git-clone", + "description": "Some extra nodes" + }, + { + "author": "Northerner1", + "title": "ComfyUI_North_Noise [WIP]", + "reference": "https://github.com/Northerner1/ComfyUI_North_Noise", + "files": [ + "https://github.com/Northerner1/ComfyUI_North_Noise" + ], + "install_type": "git-clone", + "description": "NODES: North Noise" + }, + { + "author": "ManuShamil", + "title": "ComfyUI_BodyEstimation_Nodes", + "reference": "https://github.com/ManuShamil/ComfyUI_BodyEstimation_Nodes", + "files": [ + "https://github.com/ManuShamil/ComfyUI_BodyEstimation_Nodes" + ], + "install_type": "git-clone", + "description": "NODES: CogitareLabsPoseIDExtractor" + }, + { + "author": "MockbaTheBorg", + "title": "ComfyUI-Mockba", + "reference": "https://github.com/MockbaTheBorg/ComfyUI-Mockba", + "files": [ + "https://github.com/MockbaTheBorg/ComfyUI-Mockba" + ], + "install_type": "git-clone", + "description": "NODES: Image Batch/Flip/Rotate/Subtract/Dither, Barcode, Select, ..." + }, + { + "author": "jcomeme", + "title": "AsunaroTools", + "reference": "https://github.com/jcomeme/ComfyUI-AsunaroTools", + "files": [ + "https://github.com/jcomeme/ComfyUI-AsunaroTools" + ], + "install_type": "git-clone", + "description": "A collection of custom nodes for ComfyUI" + }, + { + "author": "ZHO-ZHO-ZHO", + "title": "ComfyUI Wan2.1 [WIP]", + "reference": "https://github.com/ZHO-ZHO-ZHO/ComfyUI-Wan-ZHO", + "files": [ + "https://github.com/ZHO-ZHO-ZHO/ComfyUI-Wan-ZHO" + ], + "install_type": "git-clone", + "description": "It’s estimated that ComfyUI itself will support it soon, so go ahead and give it a try!" + }, + { + "author": "ltdrdata", + "title": "comfyui-unsafe-torch [UNSAFE]", + "reference": "https://github.com/ltdrdata/comfyui-unsafe-torch", + "files": [ + "https://github.com/ltdrdata/comfyui-unsafe-torch" + ], + "install_type": "git-clone", + "description": "disable torch.load's `weigths_only`" + }, + { + "author": "muvich3n", + "title": "ComfyUI-Crop-Border", + "reference": "https://github.com/muvich3n/ComfyUI-Crop-Border", + "files": [ + "https://github.com/muvich3n/ComfyUI-Crop-Border" + ], + "install_type": "git-clone", + "description": "NODES: Crop Image Borders" + }, + { + "author": "masmullin2000", + "title": "ComfyUI-MMYolo", + "reference": "https://github.com/masmullin2000/ComfyUI-MMYolo", + "files": [ + "https://github.com/masmullin2000/ComfyUI-MMYolo" + ], + "install_type": "git-clone", + "description": "A comfy node to find faces and output a mask" + }, + { + "author": "Yeonri", + "title": "ComfyUI_LLM_Are_You_Listening [WIP]", + "reference": "https://github.com/Yeonri/ComfyUI_LLM_Are_You_Listening", + "files": [ + "https://github.com/Yeonri/ComfyUI_LLM_Are_You_Listening" + ], + "install_type": "git-clone", + "description": "NODES: AYL_Node, AYL_GGUF_Node, AYL_API_Node\nNOTE: The files in the repo are not organized." + }, + { + "author": "altkeyproject", + "title": "Dream Painter [WIP]", + "reference": "https://github.com/alt-key-project/comfyui-dream-painter", + "files": [ + "https://github.com/alt-key-project/comfyui-dream-painter" + ], + "install_type": "git-clone", + "description": "Provide utilities for 2D image generation and processing." + }, + { + "author": "kimara-ai", + "title": "ComfyUI-Kimara-AI-Image-From-URL [WIP]", + "reference": "https://github.com/kimara-ai/ComfyUI-Kimara-AI-Image-From-URL", + "files": [ + "https://github.com/kimara-ai/ComfyUI-Kimara-AI-Image-From-URL" + ], + "install_type": "git-clone", + "description": "Load image from URL and downscale to desired megapixels. Set megapixels to 0 for no downscaling." + }, + { + "author": "krisshen2021", + "title": "comfyui_OpenRouterNodes [WIP]", + "reference": "https://github.com/krisshen2021/comfyui_OpenRouterNodes", + "files": [ + "https://github.com/krisshen2021/comfyui_OpenRouterNodes" + ], + "install_type": "git-clone", + "description": "LLM custom nodes for comfyui\nNOTE: The files in the repo are not organized." + }, + { + "author": "Velour-Fog", + "title": "comfy-latent-nodes [UNSAFE]", + "reference": "https://github.com/Velour-Fog/comfy-latent-nodes", + "files": [ + "https://github.com/Velour-Fog/comfy-latent-nodes" + ], + "install_type": "git-clone", + "description": "ComfyUI nodes to save and load a latent to a specified directory. Saves time for doing operations on a latent such as upscaling without having to re-trigger the creation of the original latent.[w/This node can write files to an arbitrary path.]" + }, + { + "author": "jgbyte", + "title": "ComfyUI-RandomCube [WIP]", + "reference": "https://github.com/jgbyte/ComfyUI-RandomCube", + "files": [ + "https://github.com/jgbyte/ComfyUI-RandomCube" + ], + "install_type": "git-clone", + "description": "NODES: RandomCubeGrid" + }, + { + "author": "thot-experiment", + "title": "comfy-live-preview [WIP]", + "reference": "https://github.com/thot-experiment/comfy-live-preview", + "files": [ + "https://github.com/thot-experiment/comfy-live-preview" + ], + "install_type": "git-clone", + "description": "external live preview plugin for ComfyUI" + }, + { + "author": "AhBumm", + "title": "ComfyUI-Upscayl", + "reference": "https://github.com/AhBumm/ComfyUI-Upscayl", + "files": [ + "https://github.com/AhBumm/ComfyUI-Upscayl" + ], + "nodename_pattern": "\\(BillBum\\)$", + "install_type": "git-clone", + "description": "NODES: Upscayl Upscaler" + }, + { + "author": "NEZHA625", + "title": "ComfyUI-tools-by-dong [UNSAFE]", + "reference": "https://github.com/NEZHA625/ComfyUI-tools-by-dong", + "files": [ + "https://github.com/NEZHA625/ComfyUI-tools-by-dong" + ], + "install_type": "git-clone", + "description": "NODES: HuggingFaceUploadNode, ImageDownloader, LoraIterator, FileMoveNode, InputDetectionNode, ...\nNOTE: The files in the repo are not organized.[w/This node pack includes nodes that can modify arbitrary files.]" + }, + { + "author": "if-ai", + "title": "ComfyUI-IF_Zonos [WIP]", + "reference": "https://github.com/if-ai/ComfyUI-IF_Zonos", + "files": [ + "https://github.com/if-ai/ComfyUI-IF_Zonos" + ], + "install_type": "git-clone", + "description": "Zonos for ComfyUI" + }, + { + "author": "grinlau18", + "title": "Xiser_Nodes [WIP]", + "reference": "https://github.com/grinlau18/ComfyUI_XISER_Nodes", + "files": [ + "https://github.com/grinlau18/ComfyUI_XISER_Nodes" + ], + "install_type": "git-clone", + "description": "Custom nodes for customizing workflows\nNOTE: The files in the repo are not organized." + }, + { + "author": "LAOGOU-666", + "title": "Comfyui_StartPatch [UNSAFE]", + "reference": "https://github.com/LAOGOU-666/Comfyui_StartPatch", + "files": [ + "https://github.com/LAOGOU-666/Comfyui_StartPatch" + ], + "install_type": "git-clone", + "description": "This patch plugin optimizes the node information processing mechanism of the ComfyUI server, significantly improving server performance and response speed. It greatly reduces the browser page initialization waiting time. [w/Since this patch modifies key functions of ComfyUI, it is highly likely to cause compatibility issues.]" + }, + { + "author": "badmike", + "title": "Prompt Factory [CONFLICT]", + "reference": "https://github.com/badmike/comfyui-prompt-factory", + "files": [ + "https://github.com/badmike/comfyui-prompt-factory" + ], + "install_type": "git-clone", + "description": "A modular system that adds randomness to prompt generation [w/This node pack is causing a name conflict with https://github.com/satche/comfyui-prompt-factory]" + }, + { + "author": "owengillett", + "title": "ComfyUI-tilefusion", + "reference": "https://github.com/owengillett/ComfyUI-tilefusion", + "files": [ + "https://github.com/owengillett/ComfyUI-tilefusion" + ], + "install_type": "git-clone", + "description": "Helper nodes for generating seamless tiles." + }, + { + "author": "Scaryplasmon", + "title": "ComfTrellis [WIP]", + "reference": "https://github.com/Scaryplasmon/ComfTrellis", + "files": [ + "https://github.com/Scaryplasmon/ComfTrellis" + ], + "install_type": "git-clone", + "description": "1 click install to run Trellis in ComfyUI\nNOTE: The files in the repo are not organized." + }, + { + "author": "fangziheng2321", + "title": "comfyuinode_chopmask [WIP]", + "reference": "https://github.com/fangziheng2321/comfyuinode_chopmask", + "files": [ + "https://github.com/fangziheng2321/comfyuinode_chopmask" + ], + "install_type": "git-clone", + "description": "a custom comfyui node for '/fooocusinpaint_upload'\nNOTE: The files in the repo are not organized." + }, + { + "author": "D1-3105", + "title": "ComfyUI-VideoStream", + "reference": "https://github.com/D1-3105/ComfyUI-VideoStream", + "files": [ + "https://github.com/D1-3105/ComfyUI-VideoStream" + ], + "install_type": "git-clone", + "description": "NODES: FloWWeaverExportSingleFrameGRPC" + }, + { + "author": "gmorks", + "title": "ComfyUI Animagine prompt [WIP]", + "reference": "https://github.com/gmorks/ComfyUI-Animagine-Prompt", + "files": [ + "https://github.com/gmorks/ComfyUI-Animagine-Prompt" + ], + "install_type": "git-clone", + "description": "Comfy UI node to prompt build for [a/https://huggingface.co/cagliostrolab/animagine-xl-4.0](https://huggingface.co/cagliostrolab/animagine-xl-4.0) model\nNOTE: The files in the repo are not organized." + }, + { + "author": "wirytiox", + "title": "ComfyUI-Qwen [CONFLICT]", + "reference": "https://github.com/mr-krak3n/ComfyUI-Qwen", + "files": [ + "https://github.com/mr-krak3n/ComfyUI-Qwen" + ], + "install_type": "git-clone", + "description": "This repository contains custom nodes for ComfyUI, designed to facilitate working with language models such as Qwen2.5 and DeepSeek. [w/This node pack is causing a name conflict with https://github.com/ZHO-ZHO-ZHO/ComfyUI-Qwen]" + }, + { + "author": "hiusdev", + "title": "ComfyUI_Lah_Toffee", + "reference": "https://github.com/hiusdev/ComfyUI_Lah_Toffee", + "files": [ + "https://github.com/hiusdev/ComfyUI_Lah_Toffee" + ], + "install_type": "git-clone", + "description": "NODES: Lah LoadVideoRandom" + }, + { + "author": "hdfhssg", + "title": "ComfyUI_pxtool [WIP]", + "reference": "https://github.com/hdfhssg/ComfyUI_pxtool", + "files": [ + "https://github.com/hdfhssg/ComfyUI_pxtool" + ], + "install_type": "git-clone", + "description": "This is a custom plugin node for ComfyUI that modifies and extends some features from existing projects. The main implementations include:\n* Reproducing some features of the [a/Stable-Diffusion-Webui-Civitai-Helper](https://github.com/zixaphir/Stable-Diffusion-Webui-Civitai-Helper) project within ComfyUI\n* Implementing a feature to randomly generate related prompt words by referencing the [a/noob-wiki dataset](https://huggingface.co/datasets/Laxhar/noob-wiki/tree/main)\nNOTE: The files in the repo are not organized." + }, + { + "author": "franky519", + "title": "comfyui-redux-style", + "reference": "https://github.com/franky519/comfyui-redux-style", + "files": [ + "https://github.com/franky519/comfyui-redux-style" + ], + "install_type": "git-clone", + "description": "NODES: Style Model Grid, Style Model Apply, Style Model Advanced" + }, + { + "author": "rishipandey125", + "title": "ComfyUI-FramePacking [WIP]", + "reference": "https://github.com/rishipandey125/ComfyUI-FramePacking", + "files": [ + "https://github.com/rishipandey125/ComfyUI-FramePacking" + ], + "install_type": "git-clone", + "description": "NODES: Add Grid Boundaries, Pack Frames, Unpack Frames, Resize Frame" + }, + { + "author": "Northerner1", + "title": "ComfyUI_North_Noise [WIP]", + "reference": "https://github.com/Northerner1/ComfyUI_North_Noise", + "files": [ + "https://github.com/Northerner1/ComfyUI_North_Noise" + ], + "install_type": "git-clone", + "description": "NODES: Unsampler" + }, + { + "author": "kimara-ai", + "title": "ComfyUI-Kimara-AI-Image-From-URL [WIP]", + "reference": "https://github.com/kimara-ai/ComfyUI-Kimara-AI-Image-From-URL", + "files": [ + "https://github.com/kimara-ai/ComfyUI-Kimara-AI-Image-From-URL" + ], + "install_type": "git-clone", + "description": "Load image from URL and downscale to desired megapixels. Set megapixels to 0 for no downscaling." + }, + { + "author": "tc8M4lF3s88", + "title": "comfy-tif-support", + "reference": "https://github.com/M4lF3s/comfy-tif-support", + "files": [ + "https://github.com/M4lF3s/comfy-tif-support" + ], + "install_type": "git-clone", + "description": "NODES: Load TIFF" + }, + { + "author": "tc888", + "title": "ComfyUI_Save_Flux_Image", + "reference": "https://github.com/tc888/ComfyUI_Save_Flux_Image", + "files": [ + "https://github.com/tc888/ComfyUI_Save_Flux_Image" + ], + "install_type": "git-clone", + "description": "Customized version of comfyui-image-save tailored for saving Flux images" + }, + { + "author": "var1ableX", + "title": "ComfyUI_Accessories", + "reference": "https://github.com/var1ableX/ComfyUI_Accessories", + "files": [ + "https://github.com/var1ableX/ComfyUI_Accessories" + ], + "install_type": "git-clone", + "description": "NODES: Get Mask Dimensions, Get Random Dimensions, Is Mask Empty/Image, Any Cast, Make List From Text" + }, + { + "author": "xinyiSS", + "title": "CombineMasksNode", + "reference": "https://github.com/xinyiSS/CombineMasksNode", + "files": [ + "https://github.com/xinyiSS/CombineMasksNode" + ], + "install_type": "git-clone", + "description": "NODES: Combine Masks Node" + }, + { + "author": "osuiso-depot", + "title": "comfyui-keshigom_custom", + "reference": "https://github.com/osuiso-depot/comfyui-keshigom_custom", + "files": [ + "https://github.com/osuiso-depot/comfyui-keshigom_custom" + ], + "install_type": "git-clone", + "description": "NODES: RegexpChopper, FLIP-W/H Selector, FLIP-W/H SelectorConst, TextFind, ckpt_Loader_Simple, True-or-False, myStringNode" + }, + { + "author": "LucipherDev", + "title": "ComfyUI-Sentinel [WIP]", + "reference": "https://github.com/LucipherDev/ComfyUI-Sentinel", + "files": [ + "https://github.com/LucipherDev/ComfyUI-Sentinel" + ], + "install_type": "git-clone", + "description": "ComfyUI Extension for Advanced Security. Implements login, multi-user registration, IP filtering, and user-specific input/output directories.[w/WARN:While ComfyUI Sentinel enhances security for ComfyUI, it does not guarantee absolute protection. Security is about risk mitigation, not elimination. Users are responsible for implementing their own security measures.]" + }, + { + "author": "threadedblue", + "title": "MLXnodes [WIP]", + "reference": "https://github.com/threadedblue/MLXnodes", + "files": [ + "https://github.com/threadedblue/MLXnodes" + ], + "install_type": "git-clone", + "description": "A port of MLX Examples to ComfyUI custom_nodes. These are intended to run on a macOS M1.\nNOTE: The files in the repo are not organized." + }, + { + "author": "jschoormans", + "title": "Comfy-InterestingPixels [WIP]", + "reference": "https://github.com/jschoormans/Comfy-InterestingPixels", + "files": [ + "https://github.com/jschoormans/Comfy-InterestingPixels" + ], + "install_type": "git-clone", + "description": "NODES: Shareable Image Slider, Random Palette\nNOTE: The files in the repo are not organized." + }, + { + "author": "jschoormans", + "title": "ComfyUI-TexturePacker [WIP]", + "reference": "https://github.com/kijai/ComfyUI-TexturePacker", + "files": [ + "https://github.com/jschoormans/Comfy-InterestingPixels" + ], + "install_type": "git-clone", + "description": "ComfyUI node to use PyTexturePacker\nNOTE: The files in the repo are not organized." + }, + { + "author": "lum3on", + "title": "comfyui_LLM_Polymath [WIP]", + "reference": "https://github.com/lum3on/comfyui_LLM_Polymath", + "files": [ + "https://github.com/lum3on/comfyui_LLM_Polymath" + ], + "install_type": "git-clone", + "description": "An advanced chat node, that integrates large language models to automate data processes and enhance prompt responses through real-time web search and image handling. It supports both OpenAI's GPT-like models and a local Ollama API. Custom node finder and smart assistant tools provide tailored workflow recommendations for efficient integration. Additionally, the node dynamically augments prompts and offers flexible output compression options.\nNOTE: The files in the repo are not organized." + }, + { + "author": "MickeyJ", + "title": "ComfyUI_mickster_nodes [WIP]", + "reference": "https://github.com/MickeyJ/ComfyUI_mickster_nodes", + "files": [ + "https://github.com/MickeyJ/ComfyUI_mickster_nodes" + ], + "install_type": "git-clone", + "description": "A collection of custom nodes for ComfyUI, focusing on image handling and LoRA training." + }, + { + "author": "gold24park", + "title": "loki-comfyui-node", + "reference": "https://github.com/gold24park/loki-comfyui-node", + "files": [ + "https://github.com/gold24park/loki-comfyui-node" + ], + "install_type": "git-clone", + "description": "NODES: Get Image Luminance, Get Dominant Color, Overlay Text" + }, + { + "author": "hayden-fr", + "title": "ComfyUI-Image-Browsing [USAFE]", + "id": "image-browsing", + "reference": "https://github.com/hayden-fr/ComfyUI-Image-Browsing", + "files": [ + "https://github.com/hayden-fr/ComfyUI-Image-Browsing" + ], + "install_type": "git-clone", + "description": "Image Browsing: browsing, download and delete." + }, + { + "author": "molbal", + "title": "comfy-url-fetcher [WIP]", + "reference": "https://github.com/molbal/comfy-url-fetcher", + "files": [ + "https://github.com/molbal/comfy-url-fetcher" + ], + "install_type": "git-clone", + "description": "Fetches URLs" + }, + { + "author": "neverbiasu", + "title": "ComfyUI_Output_as_Input", + "reference": "https://github.com/a-und-b/ComfyUI_Output_as_Input", + "files": [ + "https://github.com/a-und-b/ComfyUI_Output_as_Input" + ], + "install_type": "git-clone", + "description": "This is a simple custom ComfyUI node that allows you to easily use recent output images as input in your workflows. It does not allow image uploads on purpose and does not require any additional dependencies.\nNOTE: The files in the repo are not organized." + }, + { + "author": "neverbiasu", + "title": "ComfyUI-DeepSeek", + "reference": "https://github.com/neverbiasu/ComfyUI-DeepSeek", + "files": [ + "https://github.com/neverbiasu/ComfyUI-DeepSeek" + ], + "install_type": "git-clone", + "description": "NODES: DeepSeek Caller" + }, + { + "author": "Krish-701", + "title": "RK_Comfyui", + "reference": "https://github.com/Krish-701/RK_Comfyui", + "files": [ + "https://github.com/Krish-701/RK_Comfyui" + ], + "install_type": "git-clone", + "description": "NODES: RK Excel File State Looper, RK Accumulate Text, RK Advanced Script Finder, RK CSV File State Looper, RK Read Excel Row, RK Sequential Image Viewer, RK Concatenate Text, RK Write Text, RK Save Image, RK Seed Loop" + }, + { + "author": "mikebilly", + "title": "transparent-background-comfyui", + "reference": "https://github.com/mikebilly/Transparent-background-comfyUI", + "files": [ + "https://github.com/mikebilly/Transparent-background-comfyUI" + ], + "install_type": "git-clone", + "description": "NODES: Transparentbackground RemBg" + }, + { + "author": "Kayarte", + "title": "Time Series Nodes for ComfyUI [Experimental]", + "reference": "https://github.com/Kayarte/Time-Series-Nodes-for-ComfyUI", + "files": [ + "https://github.com/Kayarte/Time-Series-Nodes-for-ComfyUI" + ], + "install_type": "git-clone", + "description": "Basic nodes for time series analysis in ComfyUI. Currently in early development." + }, + { + "author": "HuangYuChuh", + "title": "ComfyUI-LLMs-Toolkit [WIP]", + "reference": "https://github.com/HuangYuChuh/ComfyUI-LLMs-Toolkit", + "files": [ + "https://github.com/HuangYuChuh/ComfyUI-LLMs-Toolkit" + ], + "install_type": "git-clone", + "description": "Enhance your ComfyUI workflows with powerful LLMs! This custom node suite integrates DeepSeek, Qwen, and other leading Chinese LLMs directly into your ComfyUI environment. Create innovative AI-powered applications with a range of useful nodes designed to leverage the advanced capabilities of these LLMs for image generation, understanding, and more.\nNOTE: The files in the repo are not organized." + }, + { + "author": "comfyuiblog", + "title": "deepseek_prompt_generator_comfyui [WIP]", + "reference": "https://github.com/comfyuiblog/deepseek_prompt_generator_comfyui", + "files": [ + "https://github.com/comfyuiblog/deepseek_prompt_generator_comfyui" + ], + "install_type": "git-clone", + "description": "Prompt Expansion for Stable Diffusion, using Deepseek API.\nNOTE: The files in the repo are not organized." + }, + { + "author": "risunobushi", + "title": "ComfyUI_HEXtoRGB", + "reference": "https://github.com/risunobushi/ComfyUI_HEXtoRGB", + "files": [ + "https://github.com/risunobushi/ComfyUI_HEXtoRGB" + ], + "install_type": "git-clone", + "description": "NODES: Hex to RGB Converter" + }, + { + "author": "EmanueleUniroma2", + "title": "ComfyUI-FLAC-to-WAV [WIP]", + "reference": "https://github.com/EmanueleUniroma2/ComfyUI-FLAC-to-WAV", + "files": [ + "https://github.com/EmanueleUniroma2/ComfyUI-FLAC-to-WAV" + ], + "install_type": "git-clone", + "description": "A custom node to convert flac files to wav inside comfy UI\nComfyUI Custom Node: FLAC to WAV Converter Welcome to the ComfyUI Custom Node: FLAC to WAV Converter repository! This project provides a custom node for ComfyUI that allows you to easily convert .flac audio files to .wav format, making it simpler to work with a variety of audio tools and applications.\nNOTE: The files in the repo are not organized." + }, + { + "author": "eyekayem", + "title": "comfyui_runway_gen3", + "reference": "https://github.com/eyekayem/comfyui_runway_gen3", + "files": [ + "https://github.com/eyekayem/comfyui_runway_gen3" + ], + "install_type": "git-clone", + "description": "NODES: Runway Video Gen, Runway Video Preview" + }, + { + "author": "StartHua", + "title": "Comfyui_CXH_joy_caption [SECURITY SCREENING]", + "reference": "https://github.com/StartHua/Comfyui_CXH_joy_caption", + "files": [ + "https://github.com/StartHua/Comfyui_CXH_joy_caption" + ], + "install_type": "git-clone", + "description": "Nodes:Joy_caption_load, Joy_caption\nNOTE:This node pack has been transitioned to a security screening status due to policy." + }, + { + "author": "kijai", + "title": "ComfyUI-ComfyUI-Hunyuan3DWrapper [WIP]", + "reference": "https://github.com/kijai/ComfyUI-Hunyuan3DWrapper", + "files": [ + "https://github.com/kijai/ComfyUI-Hunyuan3DWrapper" + ], + "install_type": "git-clone", + "description": "Wrapper nodes for https://github.com/Tencent/Hunyuan3D-2, additional installation steps needed, please check the github repository" + }, + { + "author": "7BEII", + "title": "comfyui-promptbymood [WIP]", + "reference": "https://github.com/7BEII/Comfyui_PDuse", + "files": [ + "https://github.com/7BEII/Comfyui_PDuse" + ], + "install_type": "git-clone", + "description": "NODES: PD_json_group_fontsize, PD_Incremental_JSON, PD_removeword, PD_Image Crop Location, PD_ImageConcanate, PD_FileName_refixer\nNOTE: The files in the repo are not organized." + }, + { + "author": "RLW-Chars", + "title": "comfyui-promptbymood [WIP]", + "reference": "https://github.com/RLW-Chars/comfyui-promptbymood", + "files": [ + "https://github.com/RLW-Chars/comfyui-promptbymood" + ], + "install_type": "git-clone", + "description": "A plugin for ComfyUI to create random prompts.\nNOTE: The files in the repo are not organized." + }, + { + "author": "mohamedsobhi777", + "title": "ComfyUI-FramerComfy [WIP]", + "reference": "https://github.com/mohamedsobhi777/ComfyUI-FramerComfy", + "files": [ + "https://github.com/mohamedsobhi777/ComfyUI-FramerComfy" + ], + "install_type": "git-clone", + "description": "NODES: FramerComfy Input String/Number/Image/Float/Boolean/Image, ...\nNOTE: The files in the repo are not organized." + }, + { + "author": "naderzare", + "title": "comfyui-inodes", + "reference": "https://github.com/naderzare/comfyui-inodes", + "files": [ + "https://github.com/naderzare/comfyui-inodes" + ], + "install_type": "git-clone", + "description": "NODES: If-Else, Multiline Split, Azure AI API" + }, + { + "author": "sizzlebop", + "title": "ComfyUI LLM Prompt Enhancer [WIP]", + "reference": "https://github.com/pinkpixel-dev/comfyui-llm-prompt-enhancer", + "files": [ + "https://github.com/pinkpixel-dev/comfyui-llm-prompt-enhancer" + ], + "install_type": "git-clone", + "description": "A ComfyUI node for enhancing prompts using various LLM providers\nNOTE: The files in the repo are not organized." + }, + { + "author": "a-One-Fan", + "title": "ComfyUI-Blenderesque-Nodes [WIP]", + "reference": "https://github.com/a-One-Fan/ComfyUI-Blenderesque-Nodes", + "files": [ + "https://github.com/a-One-Fan/ComfyUI-Blenderesque-Nodes" + ], + "install_type": "git-clone", + "description": "Blender-like nodes for ComfyUI." + }, + { + "author": "yanhuifair", + "title": "comfyui-deepseek [WIP]", + "reference": "https://github.com/yanhuifair/comfyui-deepseek", + "files": [ + "https://github.com/yanhuifair/comfyui-deepseek" + ], + "install_type": "git-clone", + "description": "nodes for deepseek api\nNOTE: The files in the repo are not organized." + }, + { + "author": "IfnotFr", + "title": "ComfyUI-Ifnot-Pack", + "reference": "https://github.com/IfnotFr/ComfyUI-Ifnot-Pack", + "files": [ + "https://github.com/IfnotFr/ComfyUI-Ifnot-Pack" + ], + "install_type": "git-clone", + "description": "NODES: Face Crop, [w/A pack of custom nodes used in my projects. Not intended to be used by other persons as the usage is not documented. But if something interests you in this repository, go for it !]" + }, + { + "author": "KihongK", + "title": "ComfyUI-RoysNodes [WIP]", + "reference": "https://github.com/KihongK/comfyui-roysnodes", + "files": [ + "https://github.com/KihongK/comfyui-roysnodes" + ], + "install_type": "git-clone", + "description": "WIP custom nodes for Creation of AI images & videos" + }, + { + "author": "catboxanon", + "title": "ComfyUI-Pixelsmith [WIP]", + "reference": "https://github.com/catboxanon/ComfyUI-Pixelsmith", + "files": [ + "https://github.com/catboxanon/ComfyUI-Pixelsmith" + ], + "install_type": "git-clone", + "description": "NODES: Pixelsmith" + }, + { + "author": "smthemex", + "title": "ComfyUI_MangaNinjia [WIP]", + "reference": "https://github.com/smthemex/ComfyUI_MangaNinjia", + "files": [ + "https://github.com/smthemex/ComfyUI_MangaNinjia" + ], + "install_type": "git-clone", + "description": "ComfyUI_MangaNinjia is a ComfyUI node of MangaNinja which is a Line Art Colorization with Precise Reference Following method.\nNOTE: invalid pyproject.toml file." + }, + { + "author": "hunterssl", + "title": "ComfyUI_SSLNodes", + "reference": "https://github.com/hunterssl/ComfyUI_SSLNodes", + "files": [ + "https://github.com/hunterssl/ComfyUI_SSLNodes" + ], + "install_type": "git-clone", + "description": "NODES: SSL Load Json, SSL Get Json Keys Count, SSL Load Checkpoint By Name, SSL Random Num In Loop, SSL Save Image Outside" + }, + { + "author": "ammahmoudi", + "title": "ComfyUI-Legendary-Nodes", + "reference": "https://github.com/ammahmoudi/ComfyUI-Legendary-Nodes", + "files": [ + "https://github.com/ammahmoudi/ComfyUI-Legendary-Nodes" + ], + "install_type": "git-clone", + "description": "NODES: Legendary Lora URL Loader, Legendary Lora URL Loader" + }, + { + "author": "yichengup", + "title": "Comfyui-NodeSpark", + "reference": "https://github.com/yichengup/Comfyui-NodeSpark", + "files": [ + "https://github.com/yichengup/Comfyui-NodeSpark" + ], + "install_type": "git-clone", + "description": "NODES: Image Circle Warp, Image Stretch, Image Wave Warp, Liquify Effect" + }, + { + "author": "kijai", + "title": "ComfyUI-VideoNoiseWarp [WIP]", + "reference": "https://github.com/kijai/ComfyUI-VideoNoiseWarp", + "files": [ + "https://github.com/kijai/ComfyUI-VideoNoiseWarp" + ], + "install_type": "git-clone", + "description": "Nodes to generate noise from video for [a/https://github.com/Eyeline-Research/Go-with-the-Flow](https://github.com/Eyeline-Research/Go-with-the-Flow)" + }, + { + "author": "muvich3n", + "title": "ComfyUI-Claude-I2T", + "reference": "https://github.com/muvich3n/ComfyUI-Claude-I2T", + "files": [ + "https://github.com/muvich3n/ComfyUI-Claude-I2T" + ], + "install_type": "git-clone", + "description": "NODES: Claude Image to Prompt Generator" + }, + { + "author": "maekawataiki", + "title": "ComfyUI-ALB-Login", + "reference": "https://github.com/maekawataiki/ComfyUI-ALB-Login", + "files": [ + "https://github.com/maekawataiki/ComfyUI-ALB-Login" + ], + "install_type": "git-clone", + "description": "Auth library to inspect token provided by ALB to protect ComfyUI." + }, + { + "author": "ArmandAlbert", + "title": "Kwai_font_comfyui", + "reference": "https://github.com/ArmandAlbert/Kwai_font_comfyui", + "files": [ + "https://github.com/ArmandAlbert/Kwai_font_comfyui" + ], + "install_type": "git-clone", + "description": "NODES: Kwaifont_Resnet50_Runner, Kwaifont_Resnet50_Loader, Kwaifont_Resnet101_Runner, Kwaifont_Resnet101_Loader, Kwaifont_Image_Cropper" + }, + { + "author": "PATATAJEC", + "title": "Patatajec-Nodes [WIP]", + "reference": "https://github.com/PATATAJEC/ComfyUI-PatatajecNodes", + "files": [ + "https://github.com/PATATAJEC/ComfyUI-PatatajecNodes" + ], + "install_type": "git-clone", + "description": "NODES: HyVid Switcher\nNOTE: The files in the repo are not organized." + }, + { + "author": "sourceful-official", + "title": "comfyui-sourceful-official", + "reference": "https://github.com/sourceful-official/comfyui-sourceful-official", + "files": [ + "https://github.com/sourceful-official/comfyui-sourceful-official" + ], + "description": "NODES: SourcefulOfficialComfyuiIncontextThreePanels, FalFluxLoraSourcefulOfficial, FalIcLightV2SourcefulOfficial", + "install_type": "git-clone" + }, + { + "author": "Alvaroeai", + "title": "ComfyUI-SunoAI-Mds", + "reference": "https://github.com/Alvaroeai/ComfyUI-SunoAI-Mds", + "files": [ + "https://github.com/Alvaroeai/ComfyUI-SunoAI-Mds" + ], + "install_type": "git-clone", + "description": "NODES: Suno Generate, Suno Download, Suno Proxy Generate, Suno Proxy Download" + }, + { + "author": "parmarjh", + "title": "ComfyUI-MochiWrapper-I2V [WIP]", + "reference": "https://github.com/parmarjh/ComfyUI-MochiWrapper-I2V", + "files": [ + "https://github.com/parmarjh/ComfyUI-MochiWrapper-I2V" + ], + "install_type": "git-clone", + "description": "ComfyUI wrapper nodes for [a/Mochi](https://github.com/genmoai/models) video generator" + }, + { + "author": "Symbiomatrix", + "title": "Comfyui-Sort-Files", + "reference": "https://github.com/Symbiomatrix/Comfyui-Sort-Files", + "files": [ + "https://github.com/Symbiomatrix/Comfyui-Sort-Files" + ], + "install_type": "git-clone", + "description": "Monkeypatch file sort to date modified or custom instead of lexicographic." + }, + { + "author": "x3bits", + "title": "ComfyUI-Power-Flow [UNSAFE]", + "reference": "https://github.com/x3bits/ComfyUI-Power-Flow", + "files": [ + "https://github.com/x3bits/ComfyUI-Power-Flow" + ], + "install_type": "git-clone", + "description": "A ComfyUI node package that introduces common programming logic to enhance the flexibility of ComfyUI workflows. It supports features such as function definition and execution, 'for' loops, 'while' loops, and Python code execution.\n[w/This extension allows the execution of arbitrary Python code from a workflow.]" + }, + { + "author": "EmilioPlumed", + "title": "ComfyUI-Math [WIP]", + "reference": "https://github.com/EmilioPlumed/ComfyUI-Math", + "files": [ + "https://github.com/EmilioPlumed/ComfyUI-Math" + ], + "install_type": "git-clone", + "description": "Custom nodes that take 2 float inputs and calculates greatest common denominator and least common multiple, returning them as ints.\nNOTE: The files in the repo are not organized." + }, + { + "author": "mliand", + "title": "ComfyUI-Calendar-Node [WIP]", + "reference": "https://github.com/mliand/ComfyUI-Calendar-Node", + "files": [ + "https://github.com/mliand/ComfyUI-Calendar-Node" + ], + "install_type": "git-clone", + "description": "A custom node for Comfyui to create a Calendar like grid\nNOTE: The files in the repo are not organized." + }, + { + "author": "phamngoctukts", + "title": "ComyUI-Tupham", + "reference": "https://github.com/phamngoctukts/ComyUI-Tupham", + "files": [ + "https://github.com/phamngoctukts/ComyUI-Tupham" + ], + "install_type": "git-clone", + "description": "NODES: Ghép Ảnh, Multi Prompt v2.0, Condition Upscale, Multi sampler, Run node selected" + }, + { + "author": "5x00", + "title": "ComfyUI-Prompt-Plus [WIP]", + "reference": "https://github.com/5x00/ComfyUI-Prompt-Plus", + "files": [ + "https://github.com/5x00/ComfyUI-Prompt-Plus" + ], + "install_type": "git-clone", + "description": "Prompt Plus is a collection of LLM and VLM nodes that make prompting easier for image and video generation.\nNOTE: The files in the repo are not organized." + }, + { + "author": "aria1th", + "title": "ComfyUI-CairoSVG", + "reference": "https://github.com/aria1th/ComfyUI-CairoSVG", + "files": [ + "https://github.com/aria1th/ComfyUI-CairoSVG" + ], + "install_type": "git-clone", + "description": "NODES: VectorizedUpscaleScaling, VectorizedUpscaleSize" + }, + { + "author": "gitmylo", + "title": "FlowNodes [WIP]", + "reference": "https://github.com/gitmylo/FlowNodes", + "files": [ + "https://github.com/gitmylo/FlowNodes" + ], + "install_type": "git-clone", + "description": "A ComfyUI node pack containing nodes for basic programming logic." + }, + { + "author": "chengzeyi", + "title": "Comfy-WaveSpeed [WIP]", + "reference": "https://github.com/chengzeyi/Comfy-WaveSpeed", + "files": [ + "https://github.com/chengzeyi/Comfy-WaveSpeed" + ], + "install_type": "git-clone", + "description": "The all in one inference optimization solution for ComfyUI, universal, flexible, and fast." + }, + { + "author": "zyd232", + "title": "ComfyUI-zyd232-Nodes", + "reference": "https://github.com/zyd232/ComfyUI-zyd232-Nodes", + "files": [ + "https://github.com/zyd232/ComfyUI-zyd232-Nodes" + ], + "install_type": "git-clone", + "description": "NODES: Image Pixels Compare, Save Preview Images" + }, + { + "author": "yanhuifair", + "title": "ComfyUI-FairLab", + "reference": "https://github.com/yanhuifair/ComfyUI-FairLab", + "files": [ + "https://github.com/yanhuifair/ComfyUI-FairLab" + ], + "install_type": "git-clone", + "description": "NODES: CLIP Text Encode Translated, Translate String, Load Image From Folder, Save String To Folder, Fix UTF-8 String, String Combine, String Field, Download Image, Save Images To Folder, Save Image To Folder, Image Resize, ..." + }, + { + "author": "nomcycle", + "title": "ComfyUI_Cluster [WIP]", + "reference": "https://github.com/nomcycle/ComfyUI_Cluster", + "files": [ + "https://github.com/nomcycle/ComfyUI_Cluster" + ], + "install_type": "git-clone", + "description": "Very early W.I.P of clustered ComfyUI inference." + }, + { + "author": "waynepimpzhang", + "title": "FindBrightestSpot [WIP]", + "reference": "https://github.com/waynepimpzhang/comfyui-opencv-brightestspot", + "files": [ + "https://github.com/waynepimpzhang/comfyui-opencv-brightestspot" + ], + "install_type": "git-clone", + "description": "Analyze the image to find the x and y coordinates of the brightest point.\nNOTE: The files in the repo are not organized." + }, + { + "author": "power88", + "title": "ComfyUI-PDiD-Nodes [WIP]", + "reference": "https://github.com/power88/ComfyUI-PDiD-Nodes", + "files": [ + "https://github.com/power88/ComfyUI-PDiD-Nodes" + ], + "install_type": "git-clone", + "description": "NODES: Get Image Size, Check Character Tag, Nearest SDXL Resolution divided by 64, Get Image Main Color, Blend Images, List Operations, Make Image Gray.\nNOTE: not working" + }, + { + "author": "FinetunersAI", + "title": "ComfyUI Finetuners [WIP]", + "reference": "https://github.com/FinetunersAI/finetuners", + "files": [ + "https://github.com/FinetunersAI/finetuners" + ], + "install_type": "git-clone", + "description": "A collection of utility nodes for ComfyUI to enhance your workflow.\nNOTE: The files in the repo are not organized." + }, + { + "author": "sourceful-official", + "title": "ComfyUI_InstructPixToPixConditioningLatent [WIP]", + "reference": "https://github.com/sourceful-official/ComfyUI_InstructPixToPixConditioningLatent", + "files": [ + "https://github.com/sourceful-official/ComfyUI_InstructPixToPixConditioningLatent" + ], + "description": "ComfyUI-ComfyUI_InstructPixToPixConditioningLatent\nNOTE:invalid pyproject.toml", + "install_type": "git-clone" + }, + { + "author": "fritzprix", + "title": "ComfyUI-LLM-Utils [WIP]", + "reference": "https://github.com/fritzprix/ComfyUI-LLM-Utils", + "files": [ + "https://github.com/fritzprix/ComfyUI-LLM-Utils" + ], + "install_type": "git-clone", + "description": "A collection of utility nodes for ComfyUI focused on text and LLM-related operations\nNOTE: The files in the repo are not organized." + }, + { + "author": "ciga2011", + "title": "ComfyUI-AppGen [UNSAFE]", + "reference": "https://github.com/ciga2011/ComfyUI-AppGen", + "files": [ + "https://github.com/ciga2011/ComfyUI-AppGen" + ], + "install_type": "git-clone", + "description": "A ComfyUI node pack designed to generate and edit Single Page Applications (SPAs) using natural language.[w/This extension allows arbitrary JavaScript code to be executed through the execution of workflows.]" + }, + { + "author": "DraconicDragon", + "title": "ComfyUI e621 booru Toolkit", + "reference": "https://github.com/DraconicDragon/ComfyUI_e621_booru_toolkit", + "files": [ + "https://github.com/DraconicDragon/ComfyUI_e621_booru_toolkit" + ], + "install_type": "git-clone", + "description": "WIP. Nodes: Fetch e621/danbooru image and/or tags etc from a given URL; Get the Wiki entry for a tag through a button press." + }, + { + "author": "Grey3016", + "title": "Save2Icon", + "reference": "https://github.com/Grey3016/Save2Icon", + "files": [ + "https://github.com/Grey3016/Save2Icon" + ], + "install_type": "git-clone", + "description": "NODES: Save2Icon" + }, + { + "author": "Chargeuk", + "title": "ComfyUI-vts-nodes [WIP]", + "reference": "https://github.com/Chargeuk/ComfyUI-vts-nodes", + "files": [ + "https://github.com/Chargeuk/ComfyUI-vts-nodes" + ], + "install_type": "git-clone", + "description": "NODES: Clean Text, Color Mask To Mask, Conditioning Set Batch Mask, Merge Delimited Text, Reduce Batch Size, Text To Batch Prompt, To Text, " + }, + { + "author": "ryanontheinside", + "title": "ComfyUI_YoloNasObjectDetection_Tensorrt [WIP]", + "reference": "https://github.com/ryanontheinside/ComfyUI_YoloNasObjectDetection_Tensorrt", + "files": [ + "https://github.com/ryanontheinside/ComfyUI_YoloNasObjectDetection_Tensorrt" + ], + "install_type": "git-clone", + "description": "ComfyUI YOLO NAS Object Detection with TensorRT" + }, + { + "author": "steelan9199", + "title": "ComfyUI-Teeth [UNSAFE]", + "reference": "https://github.com/steelan9199/ComfyUI-Teeth", + "files": [ + "https://github.com/steelan9199/ComfyUI-Teeth" + ], + "install_type": "git-clone", + "description": "Run Python code, Outline, List, Four-quadrant grid, Nine-square grid[w/This extension poses a risk of executing arbitrary commands through workflow execution. Please be cautious.]" + }, + { + "author": "aiden1020", + "title": "ComfyUI_Artcoder [WIP]", + "reference": "https://github.com/aiden1020/ComfyUI_Artcoder", + "files": [ + "https://github.com/aiden1020/ComfyUI_Artcoder" + ], + "install_type": "git-clone", + "description": "This project is a custom node for ComfyUI that uses [a/ArtCoder](https://arxiv.org/abs/2011.07815) [CVPR 2021] to refine videos generated by [a/AnimateDiff](https://arxiv.org/abs/2307.04725) [ICLR2024 Spotlight] or the other video. The node is to transform these videos into functional QR code videos that can be scanned.\nNOTE: The files in the repo are not organized." + }, + { + "author": "A4P7J1N7M05OT", + "title": "ComfyUI-ManualSigma", + "reference": "https://github.com/A4P7J1N7M05OT/ComfyUI-ManualSigma", + "files": [ + "https://github.com/A4P7J1N7M05OT/ComfyUI-ManualSigma" + ], + "install_type": "git-clone", + "description": "NODES: Manual Sigma" + }, + { + "author": "neverbiasu", + "title": "ComfyUI-StereoCrafter [WIP]", + "reference": "https://github.com/neverbiasu/ComfyUI-StereoCrafter", + "files": [ + "https://github.com/neverbiasu/ComfyUI-StereoCrafter" + ], + "install_type": "git-clone", + "description": "NODES: Depth Splatting Model Loader, Depth Splatting Node, Inpainting Inference Node" + }, + { + "author": "watarika", + "title": "ComfyUI-exit [UNSAFE]", + "reference": "https://github.com/watarika/ComfyUI-exit", + "files": [ + "https://github.com/watarika/ComfyUI-exit" + ], + "install_type": "git-clone", + "description": "Custom node to handle text.[w/This custom node includes a custom node that can terminate ComfyUI.]" + }, + { + "author": "watarika", + "title": "ComfyUI-Text-Utility [UNSAFE]", + "reference": "https://github.com/watarika/ComfyUI-Text-Utility", + "files": [ + "https://github.com/watarika/ComfyUI-Text-Utility" + ], + "install_type": "git-clone", + "description": "Custom node to handle text.[w/This node pack contains a custom node that poses a security risk by providing the ability to read text from arbitrary paths.]" + }, + { + "author": "mehbebe", + "title": "ComfyLoraGallery [WIP]", + "reference": "https://github.com/mehbebe/ComfyLoraGallery", + "files": [ + "https://github.com/mehbebe/ComfyLoraGallery" + ], + "install_type": "git-clone", + "description": "A custom node for ComfyUI that will provide a gallery style lora selector similar to the 'lora' tab in Automatic1111." + }, + { + "author": "karthikg-09", + "title": "ComfyUI-KG09 [WIP]", + "reference": "https://github.com/karthikg-09/ComfyUI-3ncrypt", + "files": [ + "https://github.com/karthikg-09/ComfyUI-3ncrypt" + ], + "install_type": "git-clone", + "description": "NODES: Save Image+[w/The web extension of this node pack modifies part of ComfyUI's asset files.]" + }, + { + "author": "AustinMroz", + "title": "ComfyUI-MinCache", + "id": "comfyui-mincache", + "reference": "https://github.com/AustinMroz/ComfyUI-MinCache", + "files": [ + "https://github.com/AustinMroz/ComfyUI-MinCache" + ], + "install_type": "git-clone", + "description": "Modifies execution to minimize RAM at the cost of performance" + }, + { + "author": "glamorfleet0i", + "title": "ComfyUI Firewall", + "reference": "https://github.com/glamorfleet0i/ComfyUI-Firewall", + "files": [ + "https://github.com/glamorfleet0i/ComfyUI-Firewall" + ], + "install_type": "git-clone", + "description": "A very basic firewall-like middleware that restricts access to your ComfyUI server based on a list of specified IP addresses. As this is configured as middleware, the firewall will restrict both the web UI and any API endpoints." + }, + { + "author": "warshanks", + "title": "Shank-Tools", + "reference": "https://github.com/warshanks/Shank-Tools", + "files": [ + "https://github.com/warshanks/Shank-Tools" + ], + "install_type": "git-clone", + "description": "NODES: Tile Calculator, Resolution Divider, Height & Width" + }, + { + "author": "BaronVonBoolean", + "title": "ComfyUI-FileOps [UNSAFE]", + "reference": "https://github.com/BaronVonBoolean/ComfyUI-FileOps", + "files": [ + "https://github.com/BaronVonBoolean/ComfyUI-FileOps" + ], + "install_type": "git-clone", + "description": "NODES: File Mv, File Path, File Dir.\n[w/This is dangerous as it provides the ability to manipulate arbitrary user files.]" + }, + { + "author": "JissiChoi", + "title": "ComfyUI-Jissi-List [WIP]", + "reference": "https://github.com/JissiChoi/ComfyUI-Jissi-List", + "files": [ + "https://github.com/JissiChoi/ComfyUI-Jissi-List" + ], + "install_type": "git-clone", + "description": "Data List Management for ComfyUI\nNOTE: The files in the repo are not organized." + }, + { + "author": "Maxim-Dey", + "title": "ComfyUI-MS_Tools [WIP]", + "reference": "https://github.com/Maxim-Dey/ComfyUI-MaksiTools", + "files": [ + "https://github.com/Maxim-Dey/ComfyUI-MaksiTools" + ], + "install_type": "git-clone", + "description": "NODES: MS Time Measure NodeMaksiTools" + }, + { + "author": "jammyfu", + "title": "ComfyUI PaintingCoderUtils Nodes [WIP]", + "reference": "https://github.com/jammyfu/ComfyUI_PaintingCoderUtils", + "files": [ + "https://github.com/jammyfu/ComfyUI_PaintingCoderUtils" + ], + "install_type": "git-clone", + "description": "A collection of utility nodes designed for ComfyUI, offering convenient image processing tools.\nNOTE: The files in the repo are not organized.\nNOTE: The files in the repo are not organized." + }, + { + "author": "krich-cto", + "title": "ComfyUI Flow Control [UNSTABLE]", + "reference": "https://github.com/krich-cto/ComfyUI-Flow-Control", + "files": [ + "https://github.com/krich-cto/ComfyUI-Flow-Control" + ], + "install_type": "git-clone", + "description": "This is an Extension for ComfyUI. This project will help you control the flow logic via many controls.[w/Installing this custom node currently causes a conflict with the UnetLoaderGGUF of ComfyUI-GGUF.]" + }, + { + "author": "dihan", + "title": "ComfyUI Random Keypoints for InstantID [WIP]", + "reference": "https://github.com/dihan/comfyui-random-kps", + "files": [ + "https://github.com/dihan/comfyui-random-kps" + ], + "install_type": "git-clone", + "description": "A custom node for ComfyUI that generates random facial keypoints compatible with InstantID.\nNOTE: The files in the repo are not organized." + }, + { + "author": "emranemran", + "title": "ComfyUI-FasterLivePortrait", + "reference": "https://github.com/emranemran/ComfyUI-FasterLivePortrait", + "files": [ + "https://github.com/emranemran/ComfyUI-FasterLivePortrait" + ], + "install_type": "git-clone", + "description": "Improve mouth tracking with live AI Video" + }, + { + "author": "kandy", + "title": "ComfyUI-KAndy", + "reference": "https://github.com/kandy/ComfyUI-KAndy", + "files": [ + "https://github.com/kandy/ComfyUI-KAndy" + ], + "install_type": "git-clone", + "description": "NODES: Civit Prompt API, Load Image From Url, Civit Images API, KAndyNoiseCondition, KAndyImagesByCss" + }, + { + "author": "StartHua", + "title": "Comfyui_leffa", + "reference": "https://github.com/StartHua/Comfyui_leffa", + "files": [ + "https://github.com/StartHua/Comfyui_leffa" + ], + "install_type": "git-clone", + "description": "NODES: CXH_Leffa_Viton_Load, CXH_Leffa_Viton_Run" + }, + { + "author": "logtd", + "title": "ComfyUI-HunyuanLoom [WIP]", + "id": "comfyui-42lux", + "reference": "https://github.com/logtd/ComfyUI-HunyuanLoom", + "files": [ + "https://github.com/logtd/ComfyUI-HunyuanLoom" + ], + "install_type": "git-clone", + "description": "A set of nodes to edit videos using the Hunyuan Video model" + }, + { + "author": "watarika", + "title": "ComfyUI-exit [UNSAFE]", + "reference": "https://github.com/watarika/ComfyUI-exit", + "files": [ + "https://github.com/watarika/ComfyUI-exit" + ], + "install_type": "git-clone", + "description": "A custom node that terminates ComfyUI after a specified number of seconds. Use this node if you want Google Colab to automatically terminate after mass generation. It is necessary to disconnect and delete the Google Colab runtime on the Notebook side." + }, + { + "author": "Eagle-CN", + "title": "ComfyUI-Addoor [UNSAFE]", + "reference": "https://github.com/Eagle-CN/ComfyUI-Addoor", + "files": [ + "https://github.com/Eagle-CN/ComfyUI-Addoor" + ], + "install_type": "git-clone", + "description": "NODES: AD_BatchImageLoadFromDir, AD_DeleteLocalAny, AD_TextListToString, AD_AnyFileList, AD_ZipSave, AD_ImageSaver, AD_FluxTrainStepMath, AD_TextSaver, AD_PromptReplace.\nNOTE: This node pack includes nodes that can delete arbitrary files." + }, + { + "author": "backearth1", + "title": "Comfyui-MiniMax-Video [WIP]", + "reference": "https://github.com/backearth1/Comfyui-MiniMax-Video", + "files": [ + "https://github.com/backearth1/Comfyui-MiniMax-Video" + ], + "install_type": "git-clone", + "description": "A ComfyUI extension that integrates MiniMax AI's image-to-video and text-to-video generation capabilities, allowing users to easily convert static images into dynamic videos.\nNOTE: The files in the repo are not organized." + }, + { + "author": "FinetunersAI", + "title": "Fast Group Link [WIP]", + "id": "fast-group-link", + "reference": "https://github.com/FinetunersAI/comfyui-fast-group-link", + "files": [ + "https://github.com/FinetunersAI/comfyui-fast-group-link" + ], + "install_type": "git-clone", + "description": "Link and control ComfyUI groups with a simple ON/OFF toggle. Control multiple groups at once with an easy-to-use interface.\nNOTE: The files in the repo are not organized." + }, + { + "author": "kijai", + "title": "ComfyUI-MMAudio [WIP]", + "reference": "https://github.com/kijai/ComfyUI-MMAudio", + "files": [ + "https://github.com/kijai/ComfyUI-MMAudio" + ], + "install_type": "git-clone", + "description": "ComfyUI nodes to use [a/MMAudio](https://github.com/hkchengrex/MMAudio)" + }, + { + "author": "kuschanow", + "title": "ComfyUI-SD-Slicer", + "reference": "https://github.com/kuschanow/ComfyUI-SD-Slicer", + "files": [ + "https://github.com/kuschanow/ComfyUI-SD-Slicer" + ], + "install_type": "git-clone", + "description": "NODES: Slicer" + }, + { + "author": "ralonsobeas", + "title": "ComfyUI-HDRConversion [WIP]", + "reference": "https://github.com/ralonsobeas/ComfyUI-HDRConversion", + "files": [ + "https://github.com/ralonsobeas/ComfyUI-HDRConversion" + ], + "install_type": "git-clone", + "description": "NODES: Generate HDR image" + }, + { + "author": "Matrix-King-Studio", + "title": "ComfyUI-MoviePy", + "reference": "https://github.com/Matrix-King-Studio/ComfyUI-MoviePy", + "files": [ + "https://github.com/Matrix-King-Studio/ComfyUI-MoviePy" + ], + "install_type": "git-clone", + "description": "NODES: Image Clip Node, Audio Duration Node, Save Video Node" + }, + { + "author": "oxysoft", + "title": "ComfyUI-uiapi", + "reference": "https://github.com/oxysoft/ComfyUI-uiapi", + "files": [ + "https://github.com/oxysoft/ComfyUI-uiapi" + ], + "install_type": "git-clone", + "description": "UIAPI is an intermediate and frontend plugin which allow communicating with the Comfy webui through server connection. This saves the need to export a workflow.json and instead directly sending a queue command to the frontend. This way, the user can experiment in realtime as they are running some professional industry or rendering software which uses UIAPI / ComfyUI as a backend. There is no way to switch seamlessly between UIAPI and regular server connection - though as of late summer 2023 it was inferior to use the server connection because the server would constantly unload models and start from scratch, and the schema of the workfow json was completely different and much less convenient, losing crucial information for efficient querying of nodes and assigning data dynamically." + }, + { + "author": "esciron", + "title": "ComfyUI-HunyuanVideoWrapper-Extended [WIP]", + "reference": "https://github.com/esciron/ComfyUI-HunyuanVideoWrapper-Extended", + "files": [ + "https://github.com/esciron/ComfyUI-HunyuanVideoWrapper-Extended" + ], + "install_type": "git-clone", + "description": "Extended ComfyUI wrapper nodes for [a/HunyuanVideo](https://github.com/Tencent/HunyuanVideo)" + }, + { + "author": "hotpot-killer", + "title": "ComfyUI_AlexNodes", + "reference": "https://github.com/hotpot-killer/ComfyUI_AlexNodes", + "files": [ + "https://github.com/hotpot-killer/ComfyUI_AlexNodes" + ], + "install_type": "git-clone", + "description": "NODES: InstructPG - editing images with text prompt, ...\nNOTE: The files in the repo are not organized." + }, + { + "author": "pschroedl", + "title": "ComfyUI-StreamDiffusion", + "reference": "https://github.com/pschroedl/ComfyUI-StreamDiffusion", + "files": [ + "https://github.com/pschroedl/ComfyUI-StreamDiffusion" + ], + "install_type": "git-clone", + "description": "NODES: StreamDiffusionConfig, StreamDiffusionAccelerationSampler, StreamDiffusionLoraLoader, StreamDiffusionAccelerationConfig, StreamDiffusionSimilarityFilterConfig, StreamDiffusionModelLoader, ..." + }, + { + "author": "c0ffymachyne", + "title": "ComfyUI Signal Processing [WIP]", + "reference": "https://github.com/c0ffymachyne/ComfyUI_SignalProcessing", + "files": [ + "https://github.com/c0ffymachyne/ComfyUI_SignalProcessing" + ], + "install_type": "git-clone", + "description": "This repo contains signal processing nodes for ComfyUI allowing for audio manipulation." + }, + { + "author": "Junst", + "title": "ComfyUI-PNG2SVG2PNG", + "reference": "https://github.com/Junst/ComfyUI-PNG2SVG2PNG", + "files": [ + "https://github.com/Junst/ComfyUI-PNG2SVG2PNG" + ], + "description": "NODES:PNG2SVG2PNG", + "install_type": "git-clone" + }, + { + "author": "animEEEmpire", + "title": "ComfyUI-Animemory-Loader", + "reference": "https://github.com/animEEEmpire/ComfyUI-Animemory-Loader", + "files": [ + "https://github.com/animEEEmpire/ComfyUI-Animemory-Loader" + ], + "install_type": "git-clone", + "description": "AniMemory-alpha Custom Node for ComfyUI" + }, + { + "author": "ShahFaisalWani", + "title": "ComfyUI-Mojen-Nodeset", + "reference": "https://github.com/ShahFaisalWani/ComfyUI-Mojen-Nodeset", + "files": [ + "https://github.com/ShahFaisalWani/ComfyUI-Mojen-Nodeset" + ], + "install_type": "git-clone", + "description": "A collection of powerful, versatile, and community-driven custom nodes for ComfyUI, designed to elevate AI workflows!" + }, + { + "author": "kijai", + "title": "ComfyUI-HunyuanVideoWrapper [WIP]", + "reference": "https://github.com/kijai/ComfyUI-HunyuanVideoWrapper", + "files": [ + "https://github.com/kijai/ComfyUI-HunyuanVideoWrapper" + ], + "install_type": "git-clone", + "description": "ComfyUI wrapper nodes for [a/HunyuanVideo](https://github.com/Tencent/HunyuanVideo)" + }, + { + "author": "grimli333", + "title": "ComfyUI_Grim", + "reference": "https://github.com/grimli333/ComfyUI_Grim", + "files": [ + "https://github.com/grimli333/ComfyUI_Grim" + ], + "install_type": "git-clone", + "description": "NODES: Generate a unique filename and folder name, Format Strings with Two Inputs" + }, + { + "author": "risunobushi", + "title": "ComfyUI_FocusMask", + "reference": "https://github.com/risunobushi/ComfyUI_FocusMask", + "files": [ + "https://github.com/risunobushi/ComfyUI_FocusMask" + ], + "install_type": "git-clone", + "description": "NODES: Extract Focus Mask" + }, + { + "author": "RicherdLee", + "title": "comfyui-oss-image-save [WIP]", + "reference": "https://github.com/RicherdLee/comfyui-oss-image-save", + "files": [ + "https://github.com/RicherdLee/comfyui-oss-image-save" + ], + "install_type": "git-clone", + "description": "NODES: SaveImageOSS." + }, + { + "author": "Matrix-King-Studio", + "title": "ComfyUI-MoviePy", + "reference": "https://github.com/Matrix-King-Studio/ComfyUI-MoviePy", + "files": [ + "https://github.com/Matrix-King-Studio/ComfyUI-MoviePy" + ], + "install_type": "git-clone", + "description": "NODES: Image Clip Node, Audio Duration Node, Save Video Node,..." + }, + { + "author": "Big Idea Technology", + "title": "ComfyUI-Movie-Tools [WIP]", + "reference": "https://github.com/Big-Idea-Technology/ComfyUI-Movie-Tools", + "files": [ + "https://github.com/Big-Idea-Technology/ComfyUI-Movie-Tools" + ], + "install_type": "git-clone", + "description": "Movie Tools is a set of custom nodes, designed to simplify saving and loading batches of images with enhanced functionality like subfolder management and batch image handling." + }, + { + "author": "ArthusLiang", + "title": "comfyui-face-remap [WIP]", + "reference": "https://github.com/ArthusLiang/comfyui-face-remap", + "files": [ + "https://github.com/ArthusLiang/comfyui-face-remap" + ], + "install_type": "git-clone", + "description": "NODES: FaceRemap\nNOTE: The files in the repo are not organized." + }, + { + "author": "trithemius", + "title": "ComfyUI-SmolVLM [WIP]", + "reference": "https://github.com/mamorett/ComfyUI-SmolVLM", + "files": [ + "https://github.com/mamorett/ComfyUI-SmolVLM" + ], + "install_type": "git-clone", + "description": "Nodes to use SmolVLM for image tagging and captioning.\nNOTE: The files in the repo are not organized." + }, + { + "author": "anze", + "title": "ComfyUI-OIDN [WIP]", + "reference": "https://github.com/Anze-/ComfyUI-OIDN", + "files": [ + "https://github.com/Anze-/ComfyUI-OIDN" + ], + "install_type": "git-clone", + "description": "ComyUI wrapper for Intel OIDN image denoising\nWARNING! : this is a development repo, usage in production environments is not advised! Bugs are to be expected." + }, + { + "author": "techzuhaib", + "title": "ComfyUI-CacheImageNode", + "reference": "https://github.com/techzuhaib/ComfyUI-CacheImageNode", + "files": [ + "https://github.com/techzuhaib/ComfyUI-CacheImageNode" + ], + "install_type": "git-clone", + "description": "NODES: CacheImageNode" + }, + { + "author": "hay86", + "title": "ComfyUI AceNodes [UNSAFE]", + "reference": "https://github.com/hay86/ComfyUI_AceNodes", + "files": [ + "https://github.com/hay86/ComfyUI_AceNodes" + ], + "install_type": "git-clone", + "description": "Some useful custom nodes that are not included in ComfyUI core yet.\nNOTE: Vulnerability discovered. Not being managed." + }, + { + "author": "dowands", + "title": "AddMaskForICLora", + "reference": "https://github.com/dowands/ComfyUI-AddMaskForICLora", + "files": [ + "https://github.com/dowands/ComfyUI-AddMaskForICLora" + ], + "install_type": "git-clone", + "description": "NODES: Add Mask For IC Lora x" + }, + { + "author": "exectails", + "title": "Scripting", + "id": "et_scripting [UNSAFE]", + "reference": "https://github.com/exectails/comfyui-et_scripting", + "files": [ + "https://github.com/exectails/comfyui-et_scripting" + ], + "install_type": "git-clone", + "description": "Nodes that can be used to write Python scripts directly on a node. Useful for quick prototyping and testing, at the cost of security.[w/This extension allows the execution of arbitrary Python code from a workflow.]" + }, + { + "author": "AIFSH", + "title": "UltralightDigitalHuman-ComfyUI", + "reference": "https://github.com/AIFSH/UltralightDigitalHuman-ComfyUI", + "files": [ + "https://github.com/AIFSH/UltralightDigitalHuman-ComfyUI" + ], + "install_type": "git-clone", + "description": "a custom node for [a/Ultralight-Digital-Human](https://github.com/anliyuan/Ultralight-Digital-Human)\nNOTE: The files in the repo are not organized." + }, + { + "author": "StartHua", + "title": "Comfyui_Flux_Style_Ctr [WIP]", + "reference": "https://github.com/StartHua/Comfyui_Flux_Style_Ctr", + "files": [ + "https://github.com/StartHua/Comfyui_Flux_Style_Ctr" + ], + "install_type": "git-clone", + "description": "NODES:CXH_StyleModelApply\nNOTE: The files in the repo are not organized." + }, + { + "author": "miragecoa", + "title": "ComfyUI-LLM-Evaluation [WIP]", + "reference": "https://github.com/miragecoa/ComfyUI-LLM-Evaluation", + "files": [ + "https://github.com/miragecoa/ComfyUI-LLM-Evaluation" + ], + "install_type": "git-clone", + "description": "NODES:Load File, Select Item by Index, Select Item by Key, JSONToListNode, MathOperationNode, F1ScoreNode, AccuracyNode, ..." + }, + { + "author": "WASasquatch", + "title": "ASTERR [UNSAFE]", + "id": "asterr", + "reference": "https://github.com/WASasquatch/ASTERR", + "files": [ + "https://github.com/WASasquatch/ASTERR" + ], + "install_type": "git-clone", + "description": "Abstract Syntax Trees Evaluated Restricted Run (ASTERR) is a Python Script executor for ComfyUI. [w/Warning:ASTERR runs Python Code from a Web Interface! It is highly recommended to run this in a closed-off environment, as it could have potential security risks.]" + }, + { + "author": "BenjaMITM", + "title": "ComfyUI_On_The_Fly_Wildcards [WIP]", + "reference": "https://github.com/BenjaMITM/ComfyUI_On_The_Fly_Wildcards", + "files": [ + "https://github.com/BenjaMITM/ComfyUI_On_The_Fly_Wildcards" + ], + "install_type": "git-clone", + "description": "NODES:Wildcard Creator, Wildcard Loader, Wildcard Selector, Display String.\nNOTE: The files in the repo are not organized." + }, + { + "author": "celll1", + "title": "cel_sampler [WIP]", + "reference": "https://github.com/celll1/cel_sampler", + "files": [ + "https://github.com/celll1/cel_sampler" + ], + "install_type": "git-clone", + "description": "NODES:Latent Value Tracker\nNOTE: The files in the repo are not organized." + }, + { + "author": "DataCTE", + "title": "ComfyUI-DataVoid-nodes [WIP]", + "reference": "https://github.com/DataCTE/ComfyUI-DataVoid-nodes", + "files": [ + "https://github.com/DataCTE/ComfyUI-DataVoid-nodes" + ], + "install_type": "git-clone", + "description": "A collection of custom nodes for ComfyUI focused on model merging and style adaptation.[w/It may cause a lot of node conflicts with comfyui_ipadapter_plus.]" + }, + { + "author": "minhtuannhn", + "title": "comfyui-gemini-studio [WIP]", + "reference": "https://github.com/minhtuannhn/comfyui-gemini-studio", + "files": [ + "https://github.com/minhtuannhn/comfyui-gemini-studio" + ], + "install_type": "git-clone", + "description": "comfyui-gemini-studio[w/This extension uses the legacy method of copying JS.]" + }, + { + "author": "artem-konevskikh", + "title": "ComfyUI Video Processing Nodes [WIP]", + "reference": "https://github.com/artem-konevskikh/comfyui-split-merge-video", + "files": [ + "https://github.com/artem-konevskikh/comfyui-split-merge-video" + ], + "install_type": "git-clone", + "description": "Custom nodes for ComfyUI that add video splitting and merging capabilities with crossfade transitions." + }, + { + "author": "Poseidon-fan", + "title": "ComfyUI-fileCleaner [UNSAFE]", + "reference": "https://github.com/Poseidon-fan/ComfyUI-fileCleaner", + "files": [ + "https://github.com/Poseidon-fan/ComfyUI-fileCleaner" + ], + "install_type": "git-clone", + "description": "In production environments, images are usually saved on storage servers such as S3, rather than local folders. So the method of placing images in local folders using ComfyUI's native LoadImage and SaveImage nodes cannot be used as a deployment service method, but can only be used as a temporary storage place for images. This requires a way to delete images from the input and output folders.\nThis node is used to delete images from the input and output folders. It is recommended to use this node through api call in the backend after the image processing is completed.[w/Users can use the file deletion feature through the workflow.]" + }, + { + "author": "yorkane", + "title": "Comfy UI Robe Nodes [UNSAFE]", + "reference": "https://github.com/RobeSantoro/ComfyUI-RobeNodes", + "files": [ + "https://github.com/RobeSantoro/ComfyUI-RobeNodes" + ], + "install_type": "git-clone", + "description": "NODES: List Video Path Node, List Image Path Node\nThis is a collection of utility nodes for the ComfyUI stable diffusion client that provides enhanced file path handling capabilities.[w/Users will be able to access images from arbitrary paths through the workflow.]" + }, + { + "author": "Kimara.ai", + "title": "Advanced Watermarking Tools [WIP]", + "reference": "https://github.com/kimara-ai/ComfyUI-Kimara-AI-Advanced-Watermarks", + "files": [ + "https://github.com/kimara-ai/ComfyUI-Kimara-AI-Advanced-Watermarks" + ], + "install_type": "git-clone", + "description": "The KimaraAIWatermarker custom node allows you to apply watermark text and logo overlays to images (or a batch of images). It provides features like customizable watermark movement, rotation, and opacity. You can also apply both text and logo watermarks simultaneously, with fine-tuned control over positioning and scaling." + }, + { + "author": "Clybius", + "title": "ComfyUI-FluxDeCLIP", + "reference": "https://github.com/Clybius/ComfyUI-FluxDeCLIP", + "files": [ + "https://github.com/Clybius/ComfyUI-FluxDeCLIP" + ], + "install_type": "git-clone", + "description": "NODES:FluxDeCLIPCheckpointLoader" + }, + { + "author": "ZHO-ZHO-ZHO", + "title": "ComfyUI-BiRefNet-ZHO [BROKEN]", + "id": "birefnet", + "reference": "https://github.com/ZHO-ZHO-ZHO/ComfyUI-BiRefNet-ZHO", + "files": [ + "https://github.com/ZHO-ZHO-ZHO/ComfyUI-BiRefNet-ZHO" + ], + "install_type": "git-clone", + "description": "Better version for [a/BiRefNet](https://github.com/zhengpeng7/birefnet) in ComfyUI | Both img and video.\nNOTE: You need to do [a/manual patch](https://github.com/ZHO-ZHO-ZHO/ComfyUI-BiRefNet-ZHO/issues/20)" + }, + { + "author": "trashgraphicard", + "title": "Albedo-Sampler-for-ComfyUI", + "reference": "https://github.com/trashgraphicard/Albedo-Sampler-for-ComfyUI", + "files": [ + "https://github.com/trashgraphicard/Albedo-Sampler-for-ComfyUI" + ], + "install_type": "git-clone", + "description": "NODES:Sample Image, Make Seamless Tile" + }, + { + "author": "Anze-", + "title": "ComfyUI_deepDeband [WIP]", + "reference": "https://github.com/Anze-/ComfyUI_deepDeband", + "files": [ + "https://github.com/Anze-/ComfyUI_deepDeband" + ], + "install_type": "git-clone", + "description": "ComyUI wrapper for RaymondLZhou/deepDeband image and video debanding\nNOTE: The files in the repo are not organized." + }, + { + "author": "bmad4ever", + "title": "Bmad Nodes [UNSAFE]", + "id": "bmad", + "reference": "https://github.com/bmad4ever/comfyui_bmad_nodes", + "files": [ + "https://github.com/bmad4ever/comfyui_bmad_nodes" + ], + "install_type": "git-clone", + "description": "This custom node offers the following functionalities: API support for setting up API requests, computer vision primarily for masking or collages, and general utility to streamline workflow setup or implement essential missing features.\nNOTE: Vulnerability discovered. Not being managed." + }, + { + "author": "suncat2ps", + "title": "ComfyUI-SaveImgNextcloud", + "reference": "https://github.com/suncat2ps/ComfyUI-SaveImgNextcloud", + "files": [ + "https://github.com/suncat2ps/ComfyUI-SaveImgNextcloud" + ], + "install_type": "git-clone", + "description": "NODES: Save Image to Nextcloud" + }, + { + "author": "KoreTeknology", + "title": "ComfyUI Production Nodes Pack [WIP]", + "reference": "https://github.com/KoreTeknology/ComfyUI-Nai-Production-Nodes-Pack", + "files": [ + "https://github.com/KoreTeknology/ComfyUI-Nai-Production-Nodes-Pack" + ], + "description": "This is set of custom nodes for your ComfyUI1 production setup. It offers the very basic nodes that are missing in the official 'Vanilla' package. It is a research Node based project on Artificial Intelligence using ComfyUI visual editor. This repository also includes a set of workflows to test the nodes.", + "install_type": "git-clone" + }, + { + "author": "DoctorDiffusion", + "title": "ComfyUI-Flashback", + "reference": "https://github.com/DoctorDiffusion/ComfyUI-Flashback", + "files": [ + "https://github.com/DoctorDiffusion/ComfyUI-Flashback" + ], + "description": "NODES:Latent Export, Latent Import, Latent Loop", + "install_type": "git-clone" + }, + { + "author": "sswink", + "title": "comfyui-lingshang", + "reference": "https://github.com/sswink/comfyui-lingshang", + "files": [ + "https://github.com/sswink/comfyui-lingshang" + ], + "description": "NODES:LS_SaveImageToOss, LS_LoadMaskFromUrl, LS_DigImageByMask, LS_ALY_Seg_Utils, LS_ALY_UploadToOssAndGetUrl, LS_GrowMaskWithBlur, LS_ALY_Seg_Body_Utils, LS_ALY_Seg_Common_Utils, LS_ALY_Seg_Clothes_Utils, LS_ALY_Seg_Body_Utils_Return_crop, ...", + "install_type": "git-clone" + }, + { + "author": "AICodeFactory", + "title": "ComfyUI-Viva", + "reference": "https://github.com/AICodeFactory/ComfyUI-Viva", + "files": [ + "https://github.com/AICodeFactory/ComfyUI-Viva" + ], + "description": "NODES:HttpTrigger (Viva), HttpTrigger (Image), HttpTrigger (Common)", + "install_type": "git-clone" + }, + { + "author": "LogicAI", + "title": "ComfyUI-MagicAI [UNSAFE]", + "reference": "https://github.com/lcolok/ComfyUI-MagicAI", + "files": [ + "https://github.com/lcolok/ComfyUI-MagicAI" + ], + "install_type": "git-clone", + "description": "NODES:Mask Size Calculator (MagicAI), Universal Mask Converter (MagicAI), Python Execution (MagicAI), Extract JSON From Text Node(MagicAI)\n[w/This extension allows the execution of arbitrary Python code from a workflow.]" + }, + { + "author": "Laser-one", + "title": "ComfyUI-align-pose", + "reference": "https://github.com/Laser-one/ComfyUI-align-pose", + "files": [ + "https://github.com/Laser-one/ComfyUI-align-pose" + ], + "install_type": "git-clone", + "description": "NODES:align pose" + }, + { + "author": "chenbaiyujason", + "title": "ComfyUI_StepFun", + "reference": "https://github.com/chenbaiyujason/ComfyUI_StepFun", + "files": [ + "https://github.com/chenbaiyujason/ComfyUI_StepFun" + ], + "install_type": "git-clone", + "description": "To use stepfun's library, you need an official api that supports multimodal inputs such as video and pictures [a/https://platform.stepfun.com/request-restriction](https://platform.stepfun.com/request-restriction)" + }, + { + "author": "aria1th", + "title": "ComfyUI-SkipCFGSigmas", + "reference": "https://github.com/aria1th/ComfyUI-SkipCFGSigmas", + "files": [ + "https://github.com/aria1th/ComfyUI-SkipCFGSigmas" + ], + "install_type": "git-clone", + "description": "NODES: CFGControl_SKIPCFG" + }, + { + "author": "Clelstyn", + "title": "ComfyUI-Inpaint_with_Detailer", + "reference": "https://github.com/Clelstyn/ComfyUI-Inpaint_with_Detailer", + "files": [ + "https://github.com/Clelstyn/ComfyUI-Inpaint_with_Detailer" + ], + "install_type": "git-clone", + "description": "NODES:Masked Resize Image, Paste Masked Image, Filter And Blur Mask" + }, + { + "author": "Looking-Glass", + "title": "LKG-ComfyUI", + "reference": "https://github.com/Looking-Glass/LKG-ComfyUI", + "files": [ + "https://github.com/Looking-Glass/LKG-ComfyUI" + ], + "install_type": "git-clone", + "description": "NODES:Side by Side Node, Bridge Preview Node, Load Folder, Scale Maintain Aspect Ratio Node, " + }, + { + "author": "xiaoyumu", + "title": "ComfyUI-XYNodes", + "reference": "https://github.com/xiaoyumu/ComfyUI-XYNodes", + "files": [ + "https://github.com/xiaoyumu/ComfyUI-XYNodes" + ], + "install_type": "git-clone", + "description": "Nodes:PrimitiveBBOX." + }, + { + "author": "ainanoha", + "title": "etm_comfyui_nodes", + "reference": "https://github.com/ainanoha/etm_comfyui_nodes", + "files": [ + "https://github.com/ainanoha/etm_comfyui_nodes" + ], + "install_type": "git-clone", + "description": "NODES:LETM Save Image, ETM Load Image From Local" + }, + { + "author": "m-ai-studio", + "title": "mai-prompt-progress", + "reference": "https://github.com/m-ai-studio/mai-prompt-progress", + "files": [ + "https://github.com/m-ai-studio/mai-prompt-progress" + ], + "install_type": "git-clone", + "description": "ComfyUI extensions for sending prompt progress to webhook" + }, + { + "author": "neeltheninja", + "title": "ComfyUI-TempFileDeleter [UNSAFE]", + "reference": "https://github.com/neeltheninja/ComfyUI-TempFileDeleter", + "files": [ + "https://github.com/neeltheninja/ComfyUI-TempFileDeleter" + ], + "install_type": "git-clone", + "description": "This node is designed to streamline your workflow in ComfyUI by efficiently cleaning up temporary files on each run. This node takes no input. You can specify 'on' or 'off' in the node itself, or just bypass to not use use it.[w/This node can delete any files in the folder mentioned in 'folder_path' in the node. Be aware of this and change the folder path correctly before running any workflow with this node. I will NOT be responsible for wrongly deleted files because you didn't read this beforehand.]" + }, + { + "author": "kylegrover", + "title": "comfyui-python-cowboy [UNSAFE]", + "reference": "https://github.com/kylegrover/comfyui-python-cowboy", + "files": [ + "https://github.com/kylegrover/comfyui-python-cowboy" + ], + "install_type": "git-clone", + "description": "run python code in comfyui\nuses codemirror for nice syntax highlighting\nNOTE: based on ComfyUI-nidefawl[w/This node is an unsafe node that includes the capability to execute arbitrary python script.]" + }, + { + "author": "kijai", + "title": "ComfyUI-MochiWrapper [WIP]", + "reference": "https://github.com/kijai/ComfyUI-MochiWrapper", + "files": [ + "https://github.com/kijai/ComfyUI-MochiWrapper" + ], + "install_type": "git-clone", + "description": "ComfyUI wrapper nodes for [a/Mochi](https://github.com/genmoai/models) video generator" + }, + { + "author": "kk8bit", + "title": "KayTool", + "reference": "https://github.com/kk8bit/KayTool", + "files": [ + "https://github.com/kk8bit/KayTool" + ], + "install_type": "git-clone", + "description": "KayTool is a custom node utility package developed for ComfyUI. I plan to add more features in the future." + }, + { + "author": "leadbreak", + "title": "Face Aging [WIP]", + "reference": "https://github.com/leadbreak/comfyui-faceaging", + "files": [ + "https://github.com/leadbreak/comfyui-faceaging" + ], + "install_type": "git-clone", + "description": "This is a comfyui custom node version of [a/Age Transformation](https://github.com/yuval-alaluf/SAM).\nNOTE: The files in the repo are not organized." + }, + { + "author": "downlifted", + "title": "ComfyUI_BWiZ_Nodes [WIP]", + "reference": "https://github.com/downlifted/ComfyUI_BWiZ_Nodes", + "files": [ + "https://github.com/downlifted/ComfyUI_BWiZ_Nodes" + ], + "install_type": "git-clone", + "description": "NODES:CaptainWebhook, CaptainWebhook-Email, CaptainWebhook-Push, BWIZ_AdvancedLoadImageBatch, BWIZ_ErrorDetector, BWIZ_HFRepoBatchLoader, BWIZ_NotificationSound.\nNOTE: The files in the repo are not organized." + }, + { + "author": "Poukpalaova", + "title": "ComfyUI-FRED-Nodes [WIP]", + "reference": "https://github.com/Poukpalaova/ComfyUI-FRED-Nodes", + "files": [ + "https://github.com/Poukpalaova/ComfyUI-FRED-Nodes" + ], + "install_type": "git-clone", + "description": "Multiple nodes that ease the process.\nNOTE: The files in the repo are not organized." + }, + { + "author": "blurymind", + "title": "cozy-fireplace [WIP]", + "reference": "https://github.com/blurymind/cozy-fireplace", + "files": [ + "https://github.com/blurymind/cozy-fireplace" + ], + "install_type": "git-clone", + "description": "Cozy fireplace is a ComfyUI workflow prompter that brings a localhost server frontend for existing workflows created in ComfyUi. Just place your favorite or lovingly crafted workflows in a folder and cozy fireplace will let you select and run any of them (export them as API type in comfyui) It's a cozy UI that scales all the way down to mobile phone devices - to let you prompt your beefy pc at home with your smartphone." + }, + { + "author": "lordwedggie", + "title": "xcpNodes [WIP]", + "reference": "https://github.com/lordwedggie/xcpNodes", + "files": [ + "https://github.com/lordwedggie/xcpNodes" + ], + "install_type": "git-clone", + "description": "Slider nodes based on Smirnov75's codes [a/https://github.com/Smirnov75/ComfyUI-mxToolkit](https://github.com/Smirnov75/ComfyUI-mxToolkit)\nNOTE: The files in the repo are not organized." + }, + { + "author": "kxh", + "title": "ComfyUI-ImageUpscaleWithModelMultipleTimes", + "reference": "https://github.com/kxh/ComfyUI-ImageUpscaleWithModelMultipleTimes", + "files": [ + "https://github.com/kxh/ComfyUI-ImageUpscaleWithModelMultipleTimes" + ], + "install_type": "git-clone", + "description": "Upscale image with model multiple times !" + }, + { + "author": "rouxianmantou", + "title": "comfyui-rxmt-nodes", + "reference": "https://github.com/rouxianmantou/comfyui-rxmt-nodes", + "files": [ + "https://github.com/rouxianmantou/comfyui-rxmt-nodes" + ], + "install_type": "git-clone", + "description": "NODES: Check Value Type, Why Prompt Text" + }, + { + "author": "SirVeggie", + "title": "SirVeggie/Custom nodes for ComfyUI", + "reference": "https://github.com/SirVeggie/comfyui-sv-nodes", + "files": [ + "https://github.com/SirVeggie/comfyui-sv-nodes" + ], + "install_type": "git-clone", + "description": "NODES:SV-SimpleText, SV-PromptProcessing, SV-PromptProcessingRecursive, SV-PromptProcessingAdvanced, SV-PromptProcessingEncode,..." + }, + { + "author": "artisanalcomputing", + "title": "artcpu-custom-nodes", + "reference": "https://github.com/artisanalcomputing/ComfyUI-Custom-Nodes", + "files": [ + "https://github.com/artisanalcomputing/ComfyUI-Custom-Nodes" + ], + "install_type": "git-clone", + "description": "NODES:Random Video Mixer, Spotify Canvas Generator, Video Writer\ncustom comfyui nodes for audio/visual purposes# ComfyUI-Custom-Nodes" + }, + { + "author": "kxh", + "title": "ComfyUI-sam2", + "reference": "https://github.com/kxh/ComfyUI-sam2", + "files": [ + "https://github.com/kxh/ComfyUI-sam2" + ], + "install_type": "git-clone", + "description": "use semantic tag to segment any element in an image, output a mask.\nNOTE: Repo name is conflicting with neverbiasu/ComfyUI-SAM2" + }, + { + "author": "AIFSH", + "title": "UtilNodes-ComfyUI [WIP]", + "reference": "https://github.com/AIFSH/UtilNodes-ComfyUI", + "files": [ + "https://github.com/AIFSH/UtilNodes-ComfyUI" + ], + "install_type": "git-clone", + "description": "here put custom input nodes such as text,video...\nNOTE: The files in the repo are not organized." + }, + { + "author": "monate0615", + "title": "ComfyUI-Simple-Image-Tools [WIP]", + "reference": "https://github.com/gondar-software/ComfyUI-Simple-Image-Tools", + "files": [ + "https://github.com/gondar-software/ComfyUI-Simple-Image-Tools" + ], + "install_type": "git-clone", + "description": "Get mask from image based on alpha (Get Mask From Alpha)\nNOTE: The files in the repo are not organized." + }, + { + "author": "galoreware", + "title": "ComfyUI-GaloreNodes [WIP]", + "reference": "https://github.com/galoreware/ComfyUI-GaloreNodes", + "files": [ + "https://github.com/galoreware/ComfyUI-GaloreNodes" + ], + "install_type": "git-clone", + "description": "Color and Image related nodes for ComfyUI." + }, + { + "author": "lgldlk", + "title": "ComfyUI-img-tiler", + "reference": "https://github.com/lgldlk/ComfyUI-img-tiler", + "files": [ + "https://github.com/lgldlk/ComfyUI-img-tiler" + ], + "install_type": "git-clone", + "description": "NODES:TilerImage, TilerSelect, TileMaker, ImageListTileMaker" + }, + { + "author": "SSsnap", + "title": "Snap Processing for Comfyui", + "reference": "https://github.com/SS-snap/ComfyUI-Snap_Processing", + "files": [ + "https://github.com/SS-snap/ComfyUI-Snap_Processing" + ], + "install_type": "git-clone", + "description": "for preprocessing images, presented in a visual way. It also calculates the corresponding image area." + }, + { + "author": "cwebbi1", + "title": "VoidCustomNodes", + "reference": "https://github.com/cwebbi1/VoidCustomNodes", + "files": [ + "https://github.com/cwebbi1/VoidCustomNodes" + ], + "install_type": "git-clone", + "description": "NODES:Prompt Parser, String Combiner" + }, + { + "author": "wilzamguerrero", + "title": "Comfyui-zZzZz [UNSAFE]", + "reference": "https://github.com/wilzamguerrero/Comfyui-zZzZz", + "files": [ + "https://github.com/wilzamguerrero/Comfyui-zZzZz" + ], + "install_type": "git-clone", + "description": "NODES:Download Z, Compress Z, Move Z, Delete Z, Rename Z, Create Z, Infinite Z, Share Screen Z" + }, + { + "author": "monate0615", + "title": "Affine Transform ComfyUI Node [WIP]", + "reference": "https://github.com/gondar-software/ComfyUI-Affine-Transform", + "files": [ + "https://github.com/gondar-software/ComfyUI-Affine-Transform" + ], + "install_type": "git-clone", + "description": "This node output the image that are transfromed by affine matrix what is made according to 4 points of output.\nNOTE: The files in the repo are not organized." + }, + { + "author": "ruka-game", + "title": "ComfyUI RukaLib [WIP]", + "reference": "https://github.com/ruka-game/rukalib_comfyui", + "files": [ + "https://github.com/ruka-game/rukalib_comfyui" + ], + "install_type": "git-clone", + "description": "NODES: Ruka Prompt Enhancer, Ruka Debug Probe.\nMy utilities for comfy (WIP / ollama is required for LLM nodes)" + }, + { + "author": "MythicalChu", + "title": "ComfyUI-APG_ImYourCFGNow", + "reference": "https://github.com/MythicalChu/ComfyUI-APG_ImYourCFGNow", + "files": [ + "https://github.com/MythicalChu/ComfyUI-APG_ImYourCFGNow" + ], + "install_type": "git-clone", + "description": "Use this node like a RescaleCFG node, ... modelIn -> ThisNode -> ModelOut ... -> KSampler\n'scale' acts like your CFG, your CFG doesn't do anything anymore white this node is active. See paper [a/https://arxiv.org/pdf/2410.02416](https://arxiv.org/pdf/2410.02416) for instructions about the other parameters. (Pages 20-21)" + }, + { + "author": "okg21", + "title": "VLLMVisionChatNode", + "reference": "https://github.com/okg21/VLLMVisionChatNode", + "files": [ + "https://raw.githubusercontent.com/okg21/VLLMVisionChatNode/refs/heads/main/VLLMVisionChatNode.py" + ], + "pip": ["openai", "numpy"], + "install_type": "copy", + "description": "This platform extension provides ZhipuAI nodes, enabling you to configure a workflow for online video generation." + }, + { + "author": "netanelben", + "title": "comfyui-photobooth-customnode", + "reference": "https://github.com/netanelben/comfyui-photobooth-customnode", + "files": [ + "https://github.com/netanelben/comfyui-photobooth-customnode" + ], + "install_type": "git-clone", + "description": "comfyui-photobooth-customnode" + }, + { + "author": "netanelben", + "title": "comfyui-text2image-customnode", + "reference": "https://github.com/netanelben/comfyui-text2image-customnode", + "files": [ + "https://github.com/netanelben/comfyui-text2image-customnode" + ], + "install_type": "git-clone", + "description": "comfyui-text2image-customnode" + }, + { + "author": "netanelben", + "title": "comfyui-camera2image-customnode", + "reference": "https://github.com/netanelben/comfyui-camera2image-customnode", + "files": [ + "https://github.com/netanelben/comfyui-camera2image-customnode" + ], + "install_type": "git-clone", + "description": "comfyui-camera2image-customnode" + }, + { + "author": "netanelben", + "title": "comfyui-image2image-customnode", + "reference": "https://github.com/netanelben/comfyui-image2image-customnode", + "files": [ + "https://github.com/netanelben/comfyui-image2image-customnode" + ], + "install_type": "git-clone", + "description": "comfyui-image2image-customnode" + }, + { + "author": "JayLyu", + "title": "ComfyUI_BaiKong_Node", + "id": "baikong", + "reference": "https://github.com/JayLyu/ComfyUI_BaiKong_Node", + "files": [ + "https://github.com/JayLyu/ComfyUI_BaiKong_Node" + ], + "install_type": "git-clone", + "description": "Nodes for advanced color manipulation and image processing: BK Img To Color, BK Color Selector, BK Color Contrast, BK Color Limit, BK Color Luminance, BK Gradient Image, and BK Image Aspect Filter.\n[w/requirements.txt is broken.]" + }, + { + "author": "ShmuelRonen", + "title": "ComfyUI-FreeMemory", + "reference": "https://github.com/ShmuelRonen/ComfyUI-FreeMemory", + "files": [ + "https://github.com/ShmuelRonen/ComfyUI-FreeMemory" + ], + "install_type": "git-clone", + "description": "ComfyUI-FreeMemory is a custom node extension for ComfyUI that provides advanced memory management capabilities within your image generation workflows." + }, + { + "author": "ZHO-ZHO-ZHO", + "title": "ComfyUI Llama 3.1 [WIP]", + "reference": "https://github.com/ZHO-ZHO-ZHO/ComfyUI-Llama-3-2", + "files": [ + "https://github.com/ZHO-ZHO-ZHO/ComfyUI-Llama-3-2" + ], + "install_type": "git-clone", + "description": "Using Llama-3-1 in ComfyUI" + }, + { + "author": "netanelben", + "title": "comfyui-text2image-customnode [WIP]", + "reference": "https://github.com/netanelben/comfyui-text2image-customnode", + "files": [ + "https://github.com/netanelben/comfyui-text2image-customnode" + ], + "install_type": "git-clone", + "description": "text2image web extension" + }, + { + "author": "leeguandong", + "title": "ComfyUI_AliControlnetInpainting [WIP]", + "reference": "https://github.com/leeguandong/ComfyUI_AliControlnetInpainting", + "files": [ + "https://github.com/leeguandong/ComfyUI_AliControlnetInpainting" + ], + "install_type": "git-clone", + "description": "ComfyUI nodes to use AliControlnetInpainting" + }, + { + "author": "jordancoult", + "title": "ComfyUI_HelpfulNodes", + "reference": "https://github.com/jordancoult/ComfyUI_HelpfulNodes", + "files": [ + "https://github.com/jordancoult/ComfyUI_HelpfulNodes" + ], + "install_type": "git-clone", + "description": "NODES: Prep Crop Around Keypoints" + }, + { + "author": "ashishsaini", + "title": "comfyui_segformer_b2_sleeves", + "reference": "https://github.com/ashishsaini/comfyui-segment-clothing-sleeves", + "files": [ + "https://github.com/ashishsaini/comfyui-segment-clothing-sleeves" + ], + "install_type": "git-clone", + "description": "NODES:segformer_b2_sleeves" + }, + { + "author": "io-club", + "title": "ComfyUI-LuminaNext [WIP]", + "reference": "https://github.com/io-club/ComfyUI-LuminaNext", + "files": [ + "https://github.com/io-club/ComfyUI-LuminaNext" + ], + "install_type": "git-clone", + "description": "NODES: GemmaClipLoader" + }, + { + "author": "shadowcz007", + "title": "Comfyui-EzAudio [WIP]", + "reference": "https://github.com/shadowcz007/Comfyui-EzAudio", + "files": [ + "https://github.com/shadowcz007/Comfyui-EzAudio" + ], + "install_type": "git-clone", + "description": "NODES: EZ Generate Audio, EZ Load Model\nNOTE: The files in the repo are not organized." + }, + { + "author": "neo0801", + "title": "my-comfy-node", + "reference": "https://github.com/neo0801/my-comfy-node", + "files": [ + "https://github.com/neo0801/my-comfy-node" + ], + "install_type": "git-clone", + "description": "NODES:Deep Mosaic Get Image Mosaic Mask, Deep Mosaic Get Video Mosaic Mask, Deep Mosaic Remove Image Mosaic, Deep Mosaic Remove Video Mosaic" + }, + { + "author": "nikkuexe", + "title": "List Data Helper Nodes", + "reference": "https://github.com/paulhoux/Smart-Prompting", + "files": [ + "https://github.com/paulhoux/Smart-Prompting" + ], + "install_type": "git-clone", + "description": "Custom nodes for ComfyUI, allowing you to more easily manipulate text and create good prompts.[w/The use of outdated front extension techniques results in remnants being left behind during uninstallation.]" + }, + { + "author": "nikkuexe", + "title": "List Data Helper Nodes", + "reference": "https://github.com/nikkuexe/ComfyUI-ListDataHelpers", + "files": [ + "https://github.com/nikkuexe/ComfyUI-ListDataHelpers" + ], + "install_type": "git-clone", + "description": "A set of custom nodes for handling lists in ComfyUI." + }, + { + "author": "Fannovel16", + "title": "ComfyUI-AppIO", + "reference": "https://github.com/Fannovel16/ComfyUI-AppIO", + "files": [ + "https://github.com/Fannovel16/ComfyUI-AppIO" + ], + "install_type": "git-clone", + "description": "NODES:AppIO_StringInput, AppIO_ImageInput, AppIO_StringOutput, AppIO_ImageOutput" + }, + { + "author": "SoftMeng", + "title": "ComfyUI-PIL", + "reference": "https://github.com/SoftMeng/ComfyUI-PIL", + "files": [ + "https://github.com/SoftMeng/ComfyUI-PIL" + ], + "install_type": "git-clone", + "description": "PIL Nodes" + }, + { + "author": "seancheung", + "title": "comfyui-creative-nodes", + "reference": "https://github.com/seancheung/comfyui-creative-nodes", + "files": [ + "https://github.com/seancheung/comfyui-creative-nodes" + ], + "install_type": "git-clone", + "description": "NODES:Stop Flow, Skip From Flow, Skip To Flow, Resolution Selector, ResolutionXL Selector" + }, + { + "author": "AlexXi19", + "title": "ComfyUI-OpenAINode", + "reference": "https://github.com/AlexXi19/ComfyUI-OpenAINode", + "files": [ + "https://github.com/AlexXi19/ComfyUI-OpenAINode" + ], + "install_type": "git-clone", + "description": "ComfyUI-OpenAINode is a user-friendly node that serves as an interface to the OpenAI Models.[w/Repo name conflict with Electrofried/ComfyUI-OpenAINode]" + }, + { + "author": "IgPoly", + "title": "ComfyUI-igTools", + "reference": "https://github.com/IgPoly/ComfyUI-igTools", + "files": [ + "https://github.com/IgPoly/ComfyUI-igTools" + ], + "install_type": "git-clone", + "description": "NODES:IGT Simple Tiles Calc" + }, + { + "author": "Ryota", + "title": "Ryota's Nodes", + "reference": "https://github.com/lichenhao/Comfyui_Ryota", + "files": [ + "https://github.com/lichenhao/Comfyui_Ryota" + ], + "install_type": "git-clone", + "description": "NODES:CombineTexts, FontLoader, DrawText, TxtFileLoader, SaveTxtFile, SwitchModelClip, SwitchAnyInputs, Reroute2, Reroute3" + }, + { + "author": "Soppatorsk", + "title": "comfyui_img_to_ascii [WIP]", + "reference": "https://github.com/Soppatorsk/comfyui_img_to_ascii", + "files": [ + "https://github.com/Soppatorsk/comfyui_img_to_ascii" + ], + "install_type": "git-clone", + "description": "Basic functionality for converting an image to ASCII art returned as a png image based on [a/ascii_magic](https://github.com/LeandroBarone/python-ascii_magic)" + }, + { + "author": "AIFSH", + "title": "HivisionIDPhotos-ComfyUI", + "reference": "https://github.com/AIFSH/HivisionIDPhotos-ComfyUI", + "files": [ + "https://github.com/AIFSH/HivisionIDPhotos-ComfyUI" + ], + "install_type": "git-clone", + "description": "a custom node for [a/HivisionIDPhotos](https://github.com/Zeyi-Lin/HivisionIDPhotos).\nNOTE: Unsuitable for international users" + }, + { + "author": "lu64k", + "title": "SK-Nodes", + "reference": "https://github.com/lu64k/SK-Nodes", + "files": [ + "https://github.com/lu64k/SK-Nodes" + ], + "install_type": "git-clone", + "description": "NODES:image select, Load AnyLLM, Ask LLM, OpenAI DAlle Node, SK Text_String, SK Random File Name" + }, + { + "author": "Lilien86", + "title": "Comfyui_Latent_Interpolation [WIP]", + "reference": "https://github.com/Lilien86/Comfyui_Latent_Interpolation", + "files": [ + "https://github.com/Lilien86/Comfyui_Latent_Interpolation" + ], + "install_type": "git-clone", + "description": "Hey everyone it's my Custom ComfyUI Nodes Pack repository! This project contains a collection of custom nodes designed to extend the functionality of ComfyUI. These nodes offer capabilities and new creative possibilities, especially in the realms of latent space manipulation and interpolation.\nNOTE: The files in the repo are not organized." + }, + { + "author": "haodman", + "title": "ComfyUI_Rain", + "reference": "https://github.com/haodman/ComfyUI_Rain", + "files": [ + "https://github.com/haodman/ComfyUI_Rain" + ], + "install_type": "git-clone", + "description": "NODES:Rain_ValueSwitch, Rain_Math, Rain_IntToFloat, Rain_ImageSize." + }, + { + "author": "bananasss00", + "title": "Comfyui-PyExec [UNSAFE]", + "reference": "https://github.com/bananasss00/Comfyui-PyExec", + "files": [ + "https://github.com/bananasss00/Comfyui-PyExec" + ], + "install_type": "git-clone", + "description": "Nodes:PyExec.[w/This node allows access to arbitrary files through the workflow, which could pose a security threat.]" + }, + { + "author": "jgbrblmd", + "title": "ComfyUI-ComfyFluxSize [WIP]", + "reference": "https://github.com/jgbrblmd/ComfyUI-ComfyFluxSize", + "files": [ + "https://github.com/jgbrblmd/ComfyUI-ComfyFluxSize" + ], + "install_type": "git-clone", + "description": "Nodes:ComfyFlux Size\nNOTE: The files in the repo are not organized." + }, + { + "author": "yojimbodayne", + "title": "ComfyUI-Dropbox-API [WIP]", + "reference": "https://github.com/yojimbodayne/ComfyUI-Dropbox-API", + "files": [ + "https://github.com/yojimbodayne/ComfyUI-Dropbox-API" + ], + "install_type": "git-clone", + "description": "This custom node package for ComfyUI allows users to interact with Dropbox API, enabling image, text, and video uploads, downloads, and management directly from ComfyUI workflows.\nNOTE: The files in the repo are not organized." + }, + { + "author": "ilovejohnwhite", + "title": "Kolors Awesome Prompts [WIP]", + "reference": "https://github.com/ilovejohnwhite/Tracer", + "files": [ + "https://github.com/ilovejohnwhite/Tracer" + ], + "install_type": "git-clone", + "description": "Nodes:Image Load TTK, SuckerPunch, LinkMasterNode, PixelPerfectResolution, ImageGenResolutionFromImage, ImageGenResolutionFromLatent, HintImageEnchance\nNOTE: The files in the repo are not organized." + }, + { + "author": "shuanshtalon468uan", + "title": "ComfyUI-Rpg-Architect [WIP]", + "reference": "https://github.com/talon468/ComfyUI-Rpg-Architect", + "files": [ + "https://github.com/talon468/ComfyUI-Rpg-Architect" + ], + "install_type": "git-clone", + "description": "Custom Node for ComfyUI to create RPG Characters\nNOTE: The files in the repo are not organized." + }, + { + "author": "shuanshuan", + "title": "ComfyUI_CheckPointLoader_Ext [WIP]", + "reference": "https://github.com/shuanshuan/ComfyUI_CheckPointLoader_Ext", + "files": [ + "https://github.com/shuanshuan/ComfyUI_CheckPointLoader_Ext" + ], + "install_type": "git-clone", + "description": "NODES:Checkpoint Loader Ext" + }, + { + "author": "123jimin", + "title": "ComfyUI MobileForm [WIP]", + "reference": "https://github.com/123jimin/ComfyUI-MobileForm", + "files": [ + "https://github.com/123jimin/ComfyUI-MobileForm" + ], + "install_type": "git-clone", + "description": "MobileForm is an extension for ComfyUI, providing simple form for any workflows, suitable for use on mobile phones.[w/Currently MobileForm is in a PoC state; expect bugs and breaking changes.]" + }, + { + "author": "go-package-lab", + "title": "ComfyUI-Tools-Video-Combine [WIP]", + "reference": "https://github.com/go-package-lab/ComfyUI-Tools-Video-Combine", + "files": [ + "https://github.com/go-package-lab/ComfyUI-Tools-Video-Combine" + ], + "install_type": "git-clone", + "description": "NODES:LoadAudioUrl, VideoWatermark" + }, + { + "author": "zhongpei", + "title": "Comfyui_image2prompt", + "id": "img2prompt", + "reference": "https://github.com/zhongpei/Comfyui_image2prompt", + "files": [ + "https://github.com/zhongpei/Comfyui_image2prompt" + ], + "install_type": "git-clone", + "description": "Nodes:Image to Text, Loader Image to Text Model.[w/This custom node may break dependencies by reinstalling the torch package.]" + }, + { + "author": "APZmedia", + "title": "comfyui-textools [WIP]", + "reference": "https://github.com/APZmedia/comfyui-textools", + "files": [ + "https://github.com/APZmedia/comfyui-textools" + ], + "install_type": "git-clone", + "description": "ComfyUI-textools is a collection of custom nodes designed for use with ComfyUI. These nodes enhance text processing capabilities, including applying rich text overlays on images and cleaning file names for safe and consistent file management.\nNOTE: The files in the repo are not organized." + }, + { + "author": "Comfy Org", + "title": "ComfyUI_devtools [WIP]", + "reference": "https://github.com/Comfy-Org/ComfyUI_devtools", + "files": [ + "https://github.com/Comfy-Org/ComfyUI_devtools" + ], + "install_type": "git-clone", + "description": "ComfyUI developer tools (Custom Node)" + }, + { + "author": "Sakura-nee", + "title": "ComfyUI_Save2Discord", + "reference": "https://github.com/Sakura-nee/ComfyUI_Save2Discord", + "files": [ + "https://github.com/Sakura-nee/ComfyUI_Save2Discord" + ], + "install_type": "git-clone", + "description": "Nodes:Send Generated Image To Discord Webhook.\nNOTE: The files in the repo are not organized." + }, + { + "author": "ThisModernDay", + "title": "ComfyUI Instructor Ollama", + "reference": "https://github.com/ThisModernDay/ComfyUI-InstructorOllama", + "files": [ + "https://github.com/ThisModernDay/ComfyUI-InstructorOllama" + ], + "install_type": "git-clone", + "description": "Custom ComfyUI Nodes for interacting with Ollama using the Instructor. Library to provide structured output from your LLM. To use this properly, you would need a running Ollama server reachable from the host that is running ComfyUI.\nNOTE: The files in the repo are not organized, which may lead to update issues." + }, + { + "author": "gioferreira", + "title": "ComfyUI-Molde-Utils", + "reference": "https://github.com/gioferreira/ComfyUI-Molde-Utils", + "files": [ + "https://github.com/gioferreira/ComfyUI-Molde-Utils" + ], + "install_type": "git-clone", + "description": "ComfyUI-Molde-Utils is a utility library designed to provide various helper functions for working with UI elements. This project includes modules for handling bezier curves and color conversions.\nNOTE: The files in the repo are not organized, which may lead to update issues." + }, + { + "author": "kijai", + "title": "ComfyUI nodes for VEnhancer [WIP]", + "reference": "https://github.com/kijai/ComfyUI-VEnhancer", + "files": [ + "https://github.com/kijai/ComfyUI-VEnhancer" + ], + "install_type": "git-clone", + "description": "Original repo: [a/https://github.com/Vchitect/VEnhancer](https://github.com/Vchitect/VEnhancer)" + }, + { + "author": "jimstudt", + "title": "Jim's ComfyUI Nodes [WIP]", + "reference": "https://github.com/jimstudt/ComfyUI-Jims-Nodes", + "files": [ + "https://github.com/jimstudt/ComfyUI-Jims-Nodes" + ], + "install_type": "git-clone", + "description": "NODES: Zoom and Enhance Nodes, Text To String List, Choose String, Define Word, Lookup Word, Substitute Words, Dictionary to JSON, JSON file to Dictionary, JSON to Dictionary, Load Image And Info From Path, CubbyHack, Image to Solid Background" + }, + { + "author": "hananbeer", + "title": "node_dev - ComfyUI Node Development Helper", + "reference": "https://github.com/hananbeer/node_dev", + "files": [ + "https://github.com/hananbeer/node_dev" + ], + "install_type": "git-clone", + "description": "Browse to this endpoint to reload custom nodes for more streamlined development:\nhttp://127.0.0.1:8188/node_dev/reload/" + }, + { + "author": "ChrisColeTech", + "title": "ComfyUI-Get-Random-File [UNSAFE]", + "reference": "https://github.com/ChrisColeTech/ComfyUI-Get-Random-File", + "files": [ + "https://github.com/ChrisColeTech/ComfyUI-Get-Random-File" + ], + "install_type": "git-clone", + "description": "Gets a random file from a directory. Returns the filpath as a STRING. [w/This node allows access to arbitrary files through the workflow, which could pose a security threat.]" + }, + { + "author": "logtd", + "title": "ComfyUI-Fluxtapoz [WIP]", + "reference": "https://github.com/logtd/ComfyUI-Fluxtapoz", + "files": [ + "https://github.com/logtd/ComfyUI-Fluxtapoz" + ], + "install_type": "git-clone", + "description": "A set of nodes for editing images using Flux in ComfyUI" + }, + { + "author": "neeltheninja", + "title": "ComfyUI-ControlNeXt [WIP]", + "reference": "https://github.com/neverbiasu/ComfyUI-ControlNeXt", + "files": [ + "https://github.com/neverbiasu/ComfyUI-ControlNeXt" + ], + "install_type": "git-clone", + "description": "In progress🚧" + }, + { + "author": "neeltheninja", + "title": "ComfyUI-TextOverlay", + "reference": "https://github.com/neeltheninja/ComfyUI-TextOverlay", + "files": [ + "https://github.com/neeltheninja/ComfyUI-TextOverlay" + ], + "install_type": "git-clone", + "description": "A custom node for ComfyUI that adds text overlay to images, with options for text and background color, opacity, vertical positioning, and custom font selection. [w/Name conflict with munkyfoot/ComfyUI-TextOverlay. Cannot install simulatenously.]" + }, + { + "author": "comfyanonymous", + "title": "ComfyUI_bitsandbytes_NF4 [EXPERIMENTAL]", + "reference": "https://github.com/comfyanonymous/ComfyUI_bitsandbytes_NF4", + "files": [ + "https://github.com/comfyanonymous/ComfyUI_bitsandbytes_NF4" + ], + "install_type": "git-clone", + "description": "A quickly written custom node that uses code from Forge to support the nf4 flux dev checkpoint and nf4 flux schnell checkpoint.\nRequires installing bitsandbytes.\nMake sure your ComfyUI is updated.\nThe node is: CheckpointLoaderNF4, just plug it in your flux workflow instead of the regular one.[w/NF4 checckpoint doesn't support LoRA.]" + }, + { + "author": "kijai", + "title": "ComfyUI-EasyAnimateWrapper [WIP]", + "reference": "https://github.com/kijai/ComfyUI-EasyAnimateWrapper", + "files": [ + "https://github.com/kijai/ComfyUI-EasyAnimateWrapper" + ], + "install_type": "git-clone", + "description": "EasyAnimateWrapper for ComfyUI" + }, + { + "author": "logtd", + "title": "ComfyUI-Veevee [WIP]", + "reference": "https://github.com/logtd/ComfyUI-Veevee", + "files": [ + "https://github.com/logtd/ComfyUI-Veevee" + ], + "install_type": "git-clone", + "description": "A Video2Video framework for text2image models in ComfyUI. Supports SD1.5 and SDXL." + }, + { + "author": "IuvenisSapiens", + "title": "ComfyUI_MiniCPM-V-2_6-int4", + "id": "minicpm-v-2_6-int4", + "reference": "https://github.com/IuvenisSapiens/ComfyUI_MiniCPM-V-2_6-int4", + "files": [ + "https://github.com/IuvenisSapiens/ComfyUI_MiniCPM-V-2_6-int4" + ], + "install_type": "git-clone", + "description": "This is an implementation of [a/MiniCPM-V-2_6-int4](https://github.com/OpenBMB/MiniCPM-V) by [a/ComfyUI](https://github.com/comfyanonymous/ComfyUI), including support for text-based queries, video queries, single-image queries, and multi-image queries to generate captions or responses." + }, + { + "author": "chrisdreid", + "title": "ComfyUI_EnvAutopsyAPI Debugger [UNSAFE]", + "id": "chrisdreid", + "reference": "https://github.com/chrisdreid/ComfyUI_EnvAutopsyAPI", + "files": [ + "https://github.com/chrisdreid/ComfyUI_EnvAutopsyAPI" + ], + "install_type": "git-clone", + "description": "A powerful debugging tool designed to provide in-depth analysis of your environment and dependencies by exposing API endpoints. This tool allows you to inspect environment variables, pip packages, python info and dependency trees, making it easier to diagnose and resolve issues in your ComfyUI setup.[w/This tool may expose sensitive system information if used on a public server]" + }, + { + "author": "Futureversecom", + "title": "ComfyUI-JEN", + "reference": "https://github.com/futureversecom/ComfyUI-JEN", + "files": [ + "https://github.com/futureversecom/ComfyUI-JEN" + ], + "install_type": "git-clone", + "description": "Comfy UI custom nodes for JEN music generation powered by Futureverse" + }, + { + "author": "denislov", + "title": "Comfyui_AutoSurvey", + "reference": "https://github.com/denislov/Comfyui_AutoSurvey", + "files": [ + "https://github.com/denislov/Comfyui_AutoSurvey" + ], + "install_type": "git-clone", + "description": "Nodes:AutoSurvey, WriteOutline, WriteSection, ChatModel, QueryKnowledge, ManageDatabase, AddDoc2Knowledge" + }, + { + "author": "leoleelxh", + "title": "ComfyUI-MidjourneyNode-leoleexh", + "reference": "https://github.com/leoleelxh/ComfyUI-MidjourneyNode-leoleexh", + "files": [ + "https://github.com/leoleelxh/ComfyUI-MidjourneyNode-leoleexh" + ], + "install_type": "git-clone", + "description": "This node allows ComfyUI to easily integrate with Midjourney, utilizing the ultra-high quality of Midjourney and the powerful control of SD to provide more convenient capabilities for AIGC.\nNOTE: This node relies on the midjourney proxy project and requires API deployment in advance. For detailed installation, please refer to the instructions of the project. https://github.com/novicezk/midjourney-proxy" + }, + { + "author": "kijai", + "title": "ComfyUI-FollowYourEmojiWrapper [WIP]", + "reference": "https://github.com/kijai/ComfyUI-FollowYourEmojiWrapper", + "files": [ + "https://github.com/kijai/ComfyUI-FollowYourEmojiWrapper" + ], + "install_type": "git-clone", + "description": "Original repo: [a/https://github.com/mayuelala/FollowYourEmoji](https://github.com/mayuelala/FollowYourEmoji)" + }, + { + "author": "haomole", + "title": "Comfyui-SadTalker", + "reference": "https://github.com/haomole/Comfyui-SadTalker", + "files": [ + "https://github.com/haomole/Comfyui-SadTalker" + ], + "install_type": "git-clone", + "description": "[a/SadTalker](https://github.com/OpenTalker/SadTalker) for ComfyUI" + }, + { + "author": "hotpizzatactics", + "title": "ComfyUI-WaterMark-Detector", + "id": "watermark-detector", + "reference": "https://github.com/hotpizzatactics/ComfyUI-WaterMark-Detector", + "files": [ + "https://github.com/hotpizzatactics/ComfyUI-WaterMark-Detector" + ], + "install_type": "git-clone", + "description": "Nodes:CLAHE Enhancement, High Pass Filter, Edge Detection, Combine Enhancements, Adaptive Thresholding, Morphological Operations, Gray Color Enhancement, Improved Gray Color Enhancement, Texture Enhancement, Denoising Filter, Flexible Combine Enhancements." + }, + { + "author": "AIFSH", + "title": "IMAGDressing-ComfyUI", + "id": "imagdressing", + "reference": "https://github.com/AIFSH/IMAGDressing-ComfyUI", + "files": [ + "https://github.com/AIFSH/IMAGDressing-ComfyUI" + ], + "install_type": "git-clone", + "description": "Nodes:IMAGDressingNode, TextNode" + }, + { + "author": "BetaDoggo", + "title": "ComfyUI-LogicGates", + "id": "logicgates", + "reference": "https://github.com/BetaDoggo/ComfyUI-LogicGates", + "files": [ + "https://github.com/BetaDoggo/ComfyUI-LogicGates" + ], + "install_type": "git-clone", + "description": "Binary Nodes, Byte Nodes, ..." + }, + { + "author": "shadowcz007", + "title": "comfyui-hydit", + "reference": "https://github.com/shadowcz007/comfyui-hydit-lowvram", + "files": [ + "https://github.com/shadowcz007/comfyui-hydit-lowvram" + ], + "install_type": "git-clone", + "description": "HunYuan Diffusers Nodes" + }, + { + "author": "walterFeng", + "title": "ComfyUI-Image-Utils", + "reference": "https://github.com/walterFeng/ComfyUI-Image-Utils", + "files":[ + "https://github.com/walterFeng/ComfyUI-Image-Utils" + ], + "install_type":"git-clone", + "description":"Nodes: Calculate Image Brightness" + }, + { + "author": "zml-ai", + "title": "comfyui-hydit", + "reference": "https://github.com/zml-ai/comfyui-hydit", + "files":[ + "https://github.com/zml-ai/comfyui-hydit" + ], + "install_type":"git-clone", + "description":"The ComfyUI code is under review in the official repository. Meanwhile, a temporary version is available below for immediate community use. We welcome users to try our workflow and appreciate any inquiries or suggestions." + }, + { + "author": "melMass", + "title": "ComfyUI-Lygia", + "id": "lygia", + "reference": "https://github.com/melMass/ComfyUI-Lygia", + "files": [ + "https://github.com/melMass/ComfyUI-Lygia" + ], + "install_type": "git-clone", + "description": "NODES: LygiaProgram, LygiaUniforms" + }, + { + "author": "SpaceWarpStudio", + "title": "ComfyUI_Remaker_FaceSwap", + "reference": "https://github.com/SpaceWarpStudio/ComfyUI_Remaker_FaceSwap", + "files": [ + "https://github.com/SpaceWarpStudio/ComfyUI_Remaker_FaceSwap" + ], + "install_type": "git-clone", + "description": "Nodes:Remaker Face Swap" + }, + { + "author": "VisionExp", + "title": "ve_custom_comfyui_nodes", + "reference": "https://github.com/VisionExp/ve_custom_comfyui_nodes", + "files": [ + "https://github.com/VisionExp/ve_custom_comfyui_nodes" + ], + "install_type": "git-clone", + "description": "Nodes:LoadImgFromInputUrl" + }, + { + "author": "StartHua", + "title": "Comfyui_CXH_CRM", + "id": "csdmt-cxh", + "reference": "https://github.com/StartHua/Comfyui_CSDMT_CXH", + "files": [ + "https://github.com/StartHua/Comfyui_CSDMT_CXH" + ], + "install_type": "git-clone", + "description": "Node:CSD_Makeup\nNOTE:You need to download [a/pre-trained model file](https://github.com/StartHua/Comfyui_CSDMT_CXH)." + }, + { + "author": "ZHO-ZHO-ZHO", + "title": "ComfyUI-AuraSR-ZHO", + "reference": "https://github.com/ZHO-ZHO-ZHO/ComfyUI-AuraSR-ZHO", + "files": [ + "https://github.com/ZHO-ZHO-ZHO/ComfyUI-AuraSR-ZHO" + ], + "install_type": "git-clone", + "description": "AuraSR in ComfyUI for img & video\n[w/If the custom_nodes path is not under ComfyUI, be careful as it may not install properly.]" + }, + { + "author": "tom-doerr", + "title": "DSPy Nodes [WIP]", + "reference": "https://github.com/tom-doerr/dspy_nodes", + "files": [ + "https://github.com/tom-doerr/dspy_nodes" + ], + "install_type": "git-clone", + "description": "This is an attempt to make all DSPy features available in ComfyUI. Using an UI to devlop DSPy programs should be way faster since it makes it easier to see what is happening and allows to quickly iterate on the DSPy program structure." + }, + { + "author": "Grant-CP", + "title": "ComfyUI-LivePortraitKJ-MPS", + "reference": "https://github.com/Grant-CP/ComfyUI-LivePortraitKJ-MPS", + "files": [ + "https://github.com/Grant-CP/ComfyUI-LivePortraitKJ-MPS" + ], + "install_type": "git-clone", + "description": "If you wish to incorporate these changes into your repo, feel free to open an issue and ask. The commits should be pretty clear, and I also label almost all changes with #HACK so a full text search will work too.\nPlease let me know if you decide to incorporate any of these changes into your LivePortrait implementation so I can direct people to you repository. I do not intend to maintain this repo.\nSome operations are simply not supported on MPS and I didn't rewrite them. Most of my changes are just to .cuda calls and that sort of thing. Many operations are still done on CPU, so don't expect awesome performance." + }, + { + "author": "thderoo", + "title": "_topfun_s_nodes", + "reference": "https://github.com/thderoo/ComfyUI-_topfun_s_nodes", + "files": [ + "https://github.com/thderoo/ComfyUI-_topfun_s_nodes" + ], + "install_type": "git-clone", + "description": "Nodes:Conditioning Perturbation" + }, + { + "author": "willblaschko", + "title": "ComfyUI-Unload-Models", + "reference": "https://github.com/willblaschko/ComfyUI-Unload-Models", + "files": [ + "https://github.com/willblaschko/ComfyUI-Unload-Models" + ], + "install_type": "git-clone", + "description": "This repository provides developers with a way to better manage their ComfyUI model memory. It includes nodes that allow developers to either unload all models or unload one model at a time. These nodes are designed as pass-through nodes, so they can be used anywhere in the flow. The nodes can be found in the 'Unload Model' section.[w/These are massive hammers, and it could be possible to break things, please don't use them if you need finesse.]" + }, + { + "author": "AIFSH", + "title": "ComfyUI-OpenDIT [WIP]", + "id": "opendit", + "reference": "https://github.com/AIFSH/ComfyUI-OpenDIT", + "files": [ + "https://github.com/AIFSH/ComfyUI-OpenDIT" + ], + "install_type": "git-clone", + "description": "make [a/OpenDIT](https://github.com/NUS-HPC-AI-Lab/OpenDiT) avaliable in ComfyUI" + }, + { + "author": "alexisrolland", + "title": "alexisrolland/ComfyUI-AuraSR", + "id": "aurasr-alexisrolland", + "reference": "https://github.com/alexisrolland/ComfyUI-AuraSR", + "files": [ + "https://github.com/alexisrolland/ComfyUI-AuraSR" + ], + "install_type": "git-clone", + "description": "Custom ComfyUI nodes to run [a/fal-ai/AuraSR](https://huggingface.co/fal-ai/AuraSR) model.[w/This node cannot be installed simultaneously with AIFSH/ComfyUI-AuraSR due to overlapping repository names.]" + }, + { + "author": "linhusyung", + "title": "ComfyUI Build and Train Your Network [WIP]", + "id": "cfgpp", + "reference": "https://github.com/linhusyung/comfyui-Build-and-train-your-network", + "files": [ + "https://github.com/linhusyung/comfyui-Build-and-train-your-network" + ], + "install_type": "git-clone", + "description": "Stable Diffusion is an image generation technique based on diffusion models. Its core idea involves simulating diffusion processes by iteratively adding noise and using neural networks to predict and remove the noise, thereby generating high-quality images. This approach is not limited to image generation; with appropriate network architecture and training data, it can be adapted for various other tasks. The application of neural networks extends beyond image generation. By adjusting network structures and loss functions, neural networks can also perform tasks such as classification and regression. This flexibility makes neural networks a powerful tool for handling a wide range of machine learning tasks. This project aims to expand custom neural network layers (such as linear layers, convolutional layers, etc.) within ComfyUI and provide simplified task training functionalities. Through this project, users can easily construct custom neural network layers and perform training in ComfyUI using a graphical interface." + }, + { + "author": "Fucci-Mateo", + "title": "ComfyUI-Airtable [WIP]", + "id": "airtable", + "reference": "https://github.com/Fucci-Mateo/ComfyUI-Airtable", + "files": [ + "https://github.com/Fucci-Mateo/ComfyUI-Airtable" + ], + "install_type": "git-clone", + "description": "A simple node to load image from local path or http url. You can find this node from 'image' category." + }, + { + "author": "majorsauce", + "title": "comfyui_indieTools [WIP]", + "id": "indie-tools", + "reference": "https://github.com/majorsauce/comfyui_indieTools", + "files": [ + "https://github.com/majorsauce/comfyui_indieTools" + ], + "install_type": "git-clone", + "description": "Nodes:[Indie] Cut by Mask, [Indie] Paste Image, [Indie] Local Scale, [Indie] Solidify, [Indie] Yolo Detector.[w/Install may fail due to invliad requirements.txt file]" + }, + { + "author": "AIFSH", + "title": "ComfyUI-ViViD", + "id": "vivid", + "reference": "https://github.com/AIFSH/ComfyUI-ViViD", + "files": [ + "https://github.com/AIFSH/ComfyUI-ViViD" + ], + "install_type": "git-clone", + "description": "a comfyui custom node for ViViD" + }, + { + "author": "NeuralNotW0rk", + "title": "ComfyUI-Waveform-Extensions", + "reference": "https://github.com/NeuralNotW0rk/ComfyUI-Waveform-Extensions", + "files": [ + "https://raw.githubusercontent.com/NeuralNotW0rk/ComfyUI-Waveform-Extensions/main/EXT_VariationUtils.py", + "https://raw.githubusercontent.com/NeuralNotW0rk/ComfyUI-Waveform-Extensions/main/EXT_AudioManipulation.py" + ], + "install_type": "copy", + "description": "Some additional audio utilites for use on top of Sample Diffusion ComfyUI Extension" + }, + { + "author": "nat-chan", + "title": "comfyui-paint", + "reference": "https://github.com/nat-chan/comfyui-paint", + "files": [ + "https://github.com/nat-chan/comfyui-paint" + ], + "install_type": "git-clone", + "description": "comfyui-paint\n[w/You need to clone submodule manually after clone. There is permission issue.]" + }, + { + "author": "prabinpebam", + "title": "anyPython [UNSAFE]", + "reference": "https://github.com/prabinpebam/anyPython", + "files": [ + "https://github.com/prabinpebam/anyPython" + ], + "install_type": "git-clone", + "description": "This node was inspired by AnyNode. I wanted to have a node where I can paste any python script and execute it. That way I can use this node in combination with a Custom node like the Ollama node that can generate the python code and feed into this node. This also makes it much easier to debug or modify the code iteratively. As of the current version, I've created separate nodes for no input, 1 input and 2 inputs. The input also currently takes only sting as input. Let me know in the discussion how you would use this node.\n[w/This extension allows the execution of arbitrary Python code from a workflow.]" + }, + { + "author": "kijai", + "title": "ComfyUI DiffSynth wrapper nodes", + "id": "diffsynth-wrapper", + "reference": "https://github.com/kijai/ComfyUI-DiffSynthWrapper", + "files": [ + "https://github.com/kijai/ComfyUI-DiffSynthWrapper" + ], + "install_type": "git-clone", + "description": "Currently only the new extended SVD model 'ExVideo' is supported.\nOriginal repo:[a/https://github.com/modelscope/DiffSynth-Studio](https://github.com/modelscope/DiffSynth-Studio)" + }, + { + "author": "AllenEdgarPoe", + "title": "ComfyUI-Xorbis-nodes [WIP]", + "id": "xorbis", + "reference": "https://github.com/AllenEdgarPoe/ComfyUI-Xorbis-nodes", + "files": [ + "https://github.com/AllenEdgarPoe/ComfyUI-Xorbis-nodes" + ], + "install_type": "git-clone", + "description": "This repository is for MuseumX Update. We use ComfyUI as our framework, and the nodes are built for my comfort.\nNOTE: The files in the repo are not organized." + }, + { + "author": "mikeymcfish", + "title": "LaserCutterFull and Deptherize Nodes", + "id": "fishtools", + "reference": "https://github.com/mikeymcfish/FishTools", + "files": [ + "https://github.com/mikeymcfish/FishTools" + ], + "install_type": "git-clone", + "description": "This repository contains two custom nodes, LaserCutterFull and Deptherize, designed for use in image processing workflows. The LaserCutterFull node processes input images to generate layers for laser cutting, while the Deptherize node converts SVG data into a depth map image." + }, + { + "author": "pzzmyc", + "title": "comfyui-sd3-simple-simpletuner", + "id": "simpletuner", + "reference": "https://github.com/pzzmyc/comfyui-sd3-simple-simpletuner", + "files": [ + "https://github.com/pzzmyc/comfyui-sd3-simple-simpletuner" + ], + "install_type": "git-clone", + "description": "Nodes:sd3 simple simpletuner by hhy." + }, + { + "author": "horidream", + "title": "ComfyUI-Horidream", + "id": "horidream", + "reference": "https://github.com/horidream/ComfyUI-Horidream", + "files": [ + "https://github.com/horidream/ComfyUI-Horidream" + ], + "install_type": "git-clone", + "description": "Nodes:Pass Through With Sound." + }, + { + "author": "kijai", + "title": "ComfyUI-DiffusersSD3Wrapper", + "id": "diffusers-sd3-wrapper", + "reference": "https://github.com/kijai/ComfyUI-DiffusersSD3Wrapper", + "files": [ + "https://github.com/kijai/ComfyUI-DiffusersSD3Wrapper" + ], + "install_type": "git-clone", + "description": "Nodes:Load SD3DiffusersPipeline, SD3 ControlNet Sampler" + }, + { + "author": "AustinMroz", + "title": "ComfyUI-SD3-Medium-CN-Diffusers [WIP]", + "reference": "https://github.com/ZHO-ZHO-ZHO/ComfyUI-SD3-Medium-CN-Diffusers", + "files": [ + "https://github.com/AustinMroz/ComfyUI-WorkflowCheckpointing" + ], + "install_type": "git-clone", + "description": "ComfyUI SD3-Medium ControlNet (Diffusers)" + }, + { + "author": "redhottensors", + "title": "ComfyUI-ODE", + "id": "ode", + "reference": "https://github.com/redhottensors/ComfyUI-ODE", + "files": [ + "https://github.com/redhottensors/ComfyUI-ODE" + ], + "install_type": "git-clone", + "description": "ODE Solvers for ComfyUI\nThis node enables use of torchdiffeq ODE solvers with models. Intended for use with Stable Diffusion 3 and similar flow models." + }, + { + "author": "maruhidd", + "title": "Transparent Background for ComfyUI", + "id": "transparent-bg", + "reference": "https://github.com/maruhidd/ComfyUI_Transparent-Background", + "files": [ + "https://github.com/maruhidd/ComfyUI_Transparent-Background" + ], + "install_type": "git-clone", + "description": "Nodes:Remove Background, Fill Transparent" + }, + { + "author": "baicai99", + "title": "ComfyUI-FrameSkipping", + "id": "frame-skipping", + "reference": "https://github.com/baicai99/ComfyUI-FrameSkipping", + "files": [ + "https://github.com/baicai99/ComfyUI-FrameSkipping" + ], + "install_type": "git-clone", + "description": "This plugin can precisely control the rendering between frames, completing the synthesis of multiple frames in a single load. My homepage includes my attached workflow." + }, + { + "author": "ejektaflex", + "title": "ComfyUI - Ty", + "id": "ty-nodes", + "reference": "https://github.com/ejektaflex/ComfyUI-Ty", + "files": [ + "https://github.com/ejektaflex/ComfyUI-Ty" + ], + "install_type": "git-clone", + "description": "Nodes:Lora Block Weight Regex Loader" + }, + { + "author": "jtydhr88", + "title": "ComfyUI-Unique3D [WIP]", + "id": "unique3d", + "reference": "https://github.com/jtydhr88/ComfyUI-Unique3D", + "files": [ + "https://github.com/jtydhr88/ComfyUI-Unique3D" + ], + "install_type": "git-clone", + "description": "ComfyUI Unique3D is custom nodes that running [a/AiuniAI/Unique3D](https://github.com/AiuniAI/Unique3D) into ComfyUI." + }, + { + "author": "kycg", + "title": "comfyui-Kwtoolset", + "id": "kwtoolset", + "reference": "https://github.com/kycg/comfyui-Kwtoolset", + "files": [ + "https://github.com/kycg/comfyui-Kwtoolset" + ], + "install_type": "git-clone", + "description": "Nodes:KwtoolsetLoraLoaderwithpreview, KwtoolsetCheckpointLoaderwithpreview, KwtoolsetLoadCheckpointsBatch, KwtoolsetGrowMaskPlus, KwtoolsetGetHipMask, KwtoolsetGetHipMasktest, KwtoolsetGetImageSize, KWPositiveString, KWNagetiveString, KWanywhereString, KwtoolsetChangeOpenpose, ..." + }, + { + "author": "mashb1t", + "title": "ComfyUI mashb1t nodes", + "id": "mashb1t", + "reference": "https://github.com/mashb1t/comfyui-nodes-mashb1t", + "files": [ + "https://github.com/mashb1t/comfyui-nodes-mashb1t" + ], + "install_type": "git-clone", + "description": "This Python script is an optional add-on to the Comfy UI stable diffusion client." + }, + { + "author": "immersiveexperience", + "title": "ie-comfyui-color-nodes", + "reference": "https://github.com/immersiveexperience/ie-comfyui-color-nodes", + "files": [ + "https://github.com/immersiveexperience/ie-comfyui-color-nodes" + ], + "install_type": "git-clone", + "description": "Custom ComfyUI nodes for simple color correction." + }, + { + "author": "LZpenguin", + "title": "ComfyUI-Text", + "id": "comfy-text", + "reference": "https://github.com/LZpenguin/ComfyUI-Text", + "files": [ + "https://github.com/LZpenguin/ComfyUI-Text" + ], + "install_type": "git-clone", + "description": "Nodes:Add_text_by_mask.[w/This custom node cannot be installed simultaneously as it has the same repository name as MarkoCa1/ComfyUI-Text.]" + }, + { + "author": "norgeous", + "title": "UI Builder [WIP]", + "id": "norgeous", + "reference": "https://github.com/norgeous/ComfyUI-UI-Builder", + "files": [ + "https://github.com/norgeous/ComfyUI-UI-Builder" + ], + "install_type": "git-clone", + "description": "Alternative configurable React UI overlay for Comfy UI." + }, + { + "author": "Shinsplat", + "title": "ComfyUI-Shinsplat [UNSAFE]", + "id": "shinsplat", + "reference": "https://github.com/Shinsplat/ComfyUI-Shinsplat", + "files": [ + "https://github.com/Shinsplat/ComfyUI-Shinsplat" + ], + "install_type": "git-clone", + "description": "Nodes: Clip Text Encode (Shinsplat), Clip Text Encode SDXL (Shinsplat), Lora Loader (Shinsplat).\n[w/This extension poses a risk of executing arbitrary commands through workflow execution. Please be cautious.]" + }, + { + "author": "hy134300", + "title": "comfyui-hydit", + "reference": "https://github.com/hy134300/comfyui-hydit", + "files": [ + "https://github.com/hy134300/comfyui-hydit" + ], + "install_type": "git-clone", + "description": "This repository contains a customized node and workflow designed specifically for HunYuan DIT. The official tests conducted on DDPM, DDIM, and DPMMS have consistently yielded results that align with those obtained through the Diffusers library. However, it's important to note that we cannot assure the consistency of results from other ComfyUI native samplers with the Diffusers inference. We cordially invite users to explore our workflow and are open to receiving any inquiries or suggestions you may have." + }, + { + "author": "corbin-hayden13", + "title": "ComfyUI-Better-Dimensions", + "id": "better-dim", + "reference": "https://github.com/corbin-hayden13/ComfyUI-Better-Dimensions", + "files": [ + "https://github.com/corbin-hayden13/ComfyUI-Better-Dimensions" + ], + "install_type": "git-clone", + "description": "Nodes:BetterImageDimensions, SDXLDimensions, PureRatio" + }, + { + "author": "endman100", + "title": "ComfyUI-augmentation", + "id": "augmentation", + "reference": "https://github.com/endman100/ComfyUI-augmentation", + "files": [ + "https://github.com/endman100/ComfyUI-augmentation" + ], + "install_type": "git-clone", + "description": "Nodes:RamdomFlipImage (endman100)" + }, + { + "author": "endman100", + "title": "ComfyUI Nodes: SaveConditioning and LoadConditioning", + "id": "save-load-conditioning", + "reference": "https://github.com/endman100/ComfyUI-SaveAndLoadPromptCondition", + "files": [ + "https://github.com/endman100/ComfyUI-SaveAndLoadPromptCondition" + ], + "install_type": "git-clone", + "description": "The SaveConditioning node is designed to save conditioning data to binary files. This is useful for storing and reusing conditioning information across different sessions or applications.\n[w/This node can only handle very limited conditioning at the text prompt level.]" + }, + { + "author": "marduk191", + "title": "comfyui-marnodes", + "id": "marnodes", + "reference": "https://github.com/marduk191/comfyui-marnodes", + "files": [ + "https://github.com/marduk191/comfyui-marnodes" + ], + "install_type": "git-clone", + "description": "Nodes:marduk191_workflow_settings" + }, + { + "author": "kijai", + "title": "ComfyUI-CV-VAE", + "id": "cv-vae", + "reference": "https://github.com/kijai/ComfyUI-CV-VAE", + "files": [ + "https://github.com/kijai/ComfyUI-CV-VAE" + ], + "install_type": "git-clone", + "description": "Nodes:CV_VAE_Load, CV_VAE_Encode, CV_VAE_Decode" + }, + { + "author": "GentlemanHu", + "title": "ComfyUI Notifier", + "id": "notifier", + "reference": "https://github.com/GentlemanHu/ComfyUI-Notifier", + "files": [ + "https://github.com/GentlemanHu/ComfyUI-Notifier" + ], + "install_type": "git-clone", + "description": "Nodes:GentlemanHu_Notifier" + }, + { + "author": "jimmm-ai", + "title": "TimeUi a ComfyUI Timeline Node System [WIP]", + "id": "timeline", + "reference": "https://github.com/jimmm-ai/TimeUi-a-ComfyUi-Timeline-Node", + "files": [ + "https://github.com/jimmm-ai/TimeUi-a-ComfyUi-Timeline-Node" + ], + "install_type": "git-clone", + "description": "I've been working on the UX/UI of a timeline custom node system for ComfyUI over the past two weeks. The goal is to create a timeline similar to video/animation editing tools, without relying on traditional timeframe code. You can effortlessly add, delete, or rearrange rows, providing a streamlined user experience." + }, + { + "author": "StartHua", + "title": "Comfyui_CXH_CRM", + "id": "cxh-crm", + "reference": "https://github.com/StartHua/Comfyui_CXH_CRM", + "files": [ + "https://github.com/StartHua/Comfyui_CXH_CRM" + ], + "install_type": "git-clone", + "description": "Nodes:CRM" + }, + { + "author": "comfypod", + "title": "ComfyUI-Comflow", + "id": "comflow", + "reference": "https://github.com/comfypod/ComfyUI-Comflow", + "files": [ + "https://github.com/comfypod/ComfyUI-Comflow" + ], + "install_type": "git-clone", + "description": "ComfyUI-Comflow." + }, + { + "author": "pamparamm", + "title": "ComfyUI-ppm", + "id": "comfyui-ppm", + "reference": "https://github.com/pamparamm/ComfyUI-ppm", + "files": [ + "https://github.com/pamparamm/ComfyUI-ppm" + ], + "install_type": "git-clone", + "description": "Fixed AttentionCouple/NegPip(negative weights in prompts), more CFG++ samplers, etc." + }, + { + "author": "FoundD-oka", + "title": "ComfyUI KISEKAE-OOTD", + "id": "kisekae-ootd", + "reference": "https://github.com/FoundD-oka/ComfyUI-kisekae-OOTD", + "files": [ + "https://github.com/FoundD-oka/ComfyUI-kisekae-OOTD" + ], + "install_type": "git-clone", + "description": "Nodes:LoadOOTDPipeline, LoadOOTDPipelineHub, LoadOOTDPipelineHub." + }, + { + "author": "bruce007lee", + "title": "comfyui-tiny-utils", + "id": "tiny-utils", + "reference": "https://github.com/bruce007lee/comfyui-tiny-utils", + "files": [ + "https://github.com/bruce007lee/comfyui-tiny-utils" + ], + "install_type": "git-clone", + "description": "Nodes:FaceAlign, FaceAlignImageProcess, FaceAlignMaskProcess, ImageFillColorByMask, CropImageByMask, LoadImageAdvance, ImageTransposeAdvance, ImageSAMMask" + }, + { + "author": "brycegoh", + "title": "brycegoh/comfyui-custom-nodes", + "reference": "https://github.com/brycegoh/comfyui-custom-nodes", + "files": [ + "https://github.com/brycegoh/comfyui-custom-nodes" + ], + "install_type": "git-clone", + "description": "Nodes:MaskAreaComparisonSegment, FillMaskedArea, OCRAndMask, CombineTwoImageIntoOne" + }, + { + "author": "LykosAI", + "title": "ComfyUI Nodes for Inference.Core", + "id": "inference-core", + "reference": "https://github.com/LykosAI/ComfyUI-Inference-Core-Nodes", + "files": [ + "https://github.com/LykosAI/ComfyUI-Inference-Core-Nodes" + ], + "install_type": "git-clone", + "description": "Primary Nodes for Inference.Core and Stability Matrix. With a focus on not impacting startup performance and using fully qualified Node names. [w/This custom node is likely to conflict with many other nodes.]" + }, + { + "author": "tracerstar", + "title": "comfyui-p5js-node", + "id": "p5js", + "reference": "https://github.com/tracerstar/comfyui-p5js-node", + "files": [ + "https://github.com/tracerstar/comfyui-p5js-node" + ], + "install_type": "git-clone", + "description": "A simple proof of concept node to pass a p5js canvas through ComfyUI for img2img generation use." + }, + { + "author": "chaojie", + "title": "ComfyUI-mobvoi-openapi", + "id": "mobvoi-openapi", + "reference": "https://github.com/chaojie/ComfyUI-mobvoi-openapi", + "files": [ + "https://github.com/chaojie/ComfyUI-mobvoi-openapi" + ], + "install_type": "git-clone", + "description": "Nodes:MobvoiOpenapiMetamanText, MobvoiOpenapiMetamanAudio, MobvoiOpenapiTts, HtmlViewer, OssUploadImage, OssUploadAudio" + }, + { + "author": "immersiveexperience", + "title": "ie-comfyui-color-nodes", + "id": "ie-color-nodes", + "reference": "https://github.com/immersiveexperience/ie-comfyui-color-nodes", + "files": [ + "https://github.com/immersiveexperience/ie-comfyui-color-nodes" + ], + "install_type": "git-clone", + "description": "Custom ComfyUI nodes for simple color correction." + }, + { + "author": "beyastard", + "title": "ComfyUI_BeySoft", + "reference": "https://github.com/beyastard/ComfyUI_BeySoft", + "files": [ + "https://github.com/beyastard/ComfyUI_BeySoft" + ], + "install_type": "git-clone", + "description": "Nodes:BeySoft" + }, + { + "author": "christian-byrne", + "title": "🌌 Infinite Parallax Nodes [WIP]", + "reference": "https://github.com/christian-byrne/infinite-zoom-parallax-nodes", + "files": [ + "https://github.com/christian-byrne/infinite-zoom-parallax-nodes" + ], + "install_type": "git-clone", + "description": "Nodes:Parallax Config, Load Parallax Frame, Save Parallax Object Layers, Layer Shifter for Parallax Outpainting, Save Parallax Frame, Shrink and Pad for Outpainting, Create Infinite Zoom Video" + }, + { + "author": "flyingdogsoftware", + "title": "Gyre for ComfyUI", + "id": "gyre", + "reference": "https://github.com/flyingdogsoftware/gyre_for_comfyui", + "files": [ + "https://github.com/flyingdogsoftware/gyre_for_comfyui" + ], + "install_type": "git-clone", + "description": "Nodes:BackgroundRemoval, GyreLoopStart, GyreLoopEnd, GyreIfElse" + }, + { + "author": "githubYiheng", + "title": "comfyui_median_filter", + "id": "median-filter", + "reference": "https://github.com/githubYiheng/comfyui_median_filter", + "files": [ + "https://github.com/githubYiheng/comfyui_median_filter" + ], + "install_type": "git-clone", + "description": "Nodes:Apply Median Filter. [w/This has been updated to be equivalent to the comfyui_kmeans_filter node. Mistake?]" + }, + { + "author": "nat-chan", + "title": "comfyui-exec [UNSAFE]", + "id": "evalnode", + "reference": "https://github.com/nat-chan/comfyui-exec", + "files": [ + "https://github.com/nat-chan/comfyui-exec" + ], + "install_type": "git-clone", + "description": "Nodes:EvalNode [w/Please do not use the node that executes arbitrary code and outputs in any type, as it is dangerous.]" + }, + { + "author": "haofanwang", + "title": "ComfyUI-InstantStyle", + "id": "instantstyle", + "reference": "https://github.com/haofanwang/ComfyUI-InstantStyle", + "files": [ + "https://github.com/haofanwang/ComfyUI-InstantStyle" + ], + "install_type": "git-clone", + "description": "Nodes:PromptLoader, BaseModelLoader, InstantStyleLoader, InstantStyleGenerationNode" + }, + { + "author": "jp0215", + "title": "comfyUI_padding-resize_node", + "reference": "https://github.com/jp0215/comfyUI_padding-resize_node", + "files": [ + "https://raw.githubusercontent.com/jp0215/comfyUI_padding-resize_node/main/PaddingNode.py", + "https://raw.githubusercontent.com/jp0215/comfyUI_padding-resize_node/main/ResizeNode.py" + ], + "install_type": "copy", + "description": "Padding image to 8x: input image and mask, if the side length is not an integer multiple of 8, expand the side length to the smallest multiple of 8 greater than the original side length. Output padding image and mask. Resize to the origin: input the generated image and the original image, crop the generated image to the size of the original image, output the cropped image." + }, + { + "author": "Quasimondo", + "title": "ComfyUI-QuasimondoNodes [WIP]", + "id": "quasimondo-nodes", + "reference": "https://github.com/Quasimondo/ComfyUI-QuasimondoNodes", + "files": [ + "https://github.com/Quasimondo/ComfyUI-QuasimondoNodes" + ], + "install_type": "git-clone", + "description": "Nodes:Custom Shader, Spring Mesh" + }, + { + "author": "TSFSean", + "title": "ComfyUI-TSFNodes", + "id": "tsfnodes", + "reference": "https://github.com/TSFSean/ComfyUI-TSFNodes", + "files": [ + "https://github.com/TSFSean/ComfyUI-TSFNodes" + ], + "install_type": "git-clone", + "description": "Nodes:GyroOSC" + }, + { + "author": "blib-la", + "title": "ComfyUI-Captain-Extensions", + "id": "captain", + "reference": "https://github.com/blib-la/ComfyUI-Captain-Extensions", + "files": [ + "https://github.com/blib-la/ComfyUI-Captain-Extensions" + ], + "install_type": "git-clone", + "description": "ComfyUI extensions for better [a/Captain](https://github.com/blib-la/captain) integration." + }, + { + "author": "ejektaflex", + "title": "ComfyUI-Ty", + "reference": "https://github.com/ejektaflex/ComfyUI-Ty", + "files": [ + "https://github.com/ejektaflex/ComfyUI-Ty" + ], + "install_type": "git-clone", + "description": "Nodes:Lora Block Weight Regex Loader" + }, + { + "author": "christian-byrne", + "title": "Python Interpreter ComfyUI Node [UNSAFE]", + "reference": "https://github.com/christian-byrne/python-interpreter-node", + "files": [ + "https://github.com/christian-byrne/python-interpreter-node" + ], + "install_type": "git-clone", + "description": "For debugging, parsing data, generating random values, converting types, testing custom nodes faster.\nReference and use variables in the code using the same names as the inputs in the UI\nWrapper class around tensors with operator overloading for doing common image manipulation tasks.I might remove this aspect\n[w/This extension allows you to run programs through Python code in your workflow, which may not be secure. Use with caution.]" + }, + { + "author": "sofakid", + "title": "dandy [UNSAFE]", + "reference": "https://github.com/sofakid/dandy", + "files": [ + "https://github.com/sofakid/dandy" + ], + "install_type": "git-clone", + "description": "Dandy is a JavaScript bridge for ComfyUI. It includes everything you need to make JavaScript enabled extensions, or just load and code in little editor nodes right in ComfyUI.[w/This code can cause security issues because it allows for the execution of arbitrary JavaScript input.]" + }, + { + "author": "shadowcz007", + "title": "ComfyUI-PuLID [TEST]", + "reference": "https://github.com/shadowcz007/ComfyUI-PuLID-Test", + "files": [ + "https://github.com/shadowcz007/ComfyUI-PuLID-Test" + ], + "install_type": "git-clone", + "description": "[a/PuLID](https://github.com/ToTheBeginning/PuLID) ComfyUI native implementation." + }, + { + "author": "sangeet", + "title": "Simple Frontend For ComfyUI workflow [TEST]", + "reference": "https://github.com/sangeet/testui", + "files": [ + "https://github.com/sangeet/testui" + ], + "install_type": "git-clone", + "description": "A simple base front-end for text-to-image workflow in ComfyUI. Meant to serve as a base to be modified for future complex workflows" + }, + { + "author": "Elawphant", + "title": "ComfyUI-MusicGen [WIP]", + "id": "musicgen", + "reference": "https://github.com/Elawphant/ComfyUI-MusicGen", + "files": [ + "https://github.com/Elawphant/ComfyUI-MusicGen" + ], + "install_type": "git-clone", + "description": "ComfyUI for Meta MusicGen." + }, + { + "author": "jtscmw01", + "title": "ComfyUI-DiffBIR", + "id": "diffbir", + "reference": "https://github.com/jtscmw01/ComfyUI-DiffBIR", + "files": [ + "https://github.com/jtscmw01/ComfyUI-DiffBIR" + ], + "install_type": "git-clone", + "description": "This extension provides [a/DiffBIR](https://github.com/XPixelGroup/DiffBIR) feature." + }, + { + "author": "ericbeyer", + "title": "guidance_interval", + "reference": "https://github.com/ericbeyer/guidance_interval", + "files": [ + "https://github.com/ericbeyer/guidance_interval" + ], + "install_type": "git-clone", + "description": "Nodes:Guidance Interval\nNOTE: Because the sampling function is replaced, you must restart after executing this custom node to restore the original state." + }, + { + "author": "oztrkoguz", + "title": "Kosmos2_BBox_Cutter Models", + "reference": "https://github.com/oztrkoguz/ComfyUI_Kosmos2_BBox_Cutter", + "files": [ + "https://github.com/oztrkoguz/ComfyUI_Kosmos2_BBox_Cutter" + ], + "install_type": "git-clone", + "description": "Nodes:KosmosLoader, Kosmos2SamplerSimple, Write" + }, + { + "author": "ZHO-ZHO-ZHO", + "title": "ComfyUI-PuLID-ZHO [WIP]", + "reference": "https://github.com/ZHO-ZHO-ZHO/ComfyUI-PuLID-ZHO", + "files": [ + "https://github.com/ZHO-ZHO-ZHO/ComfyUI-PuLID-ZHO" + ], + "install_type": "git-clone", + "description": "Unofficial implementation of [a/PuLID](https://github.com/ToTheBeginning/PuLID)(diffusers) for ComfyUI" + }, + { + "author": "longgui0318", + "title": "comfyui-one-more-step [WIP]", + "reference": "https://github.com/longgui0318/comfyui-one-more-step", + "files": [ + "https://github.com/longgui0318/comfyui-one-more-step" + ], + "install_type": "git-clone", + "description": "[a/(OMS)mhh0318/OneMoreStep](https://github.com/mhh0318/OneMoreStep) comfyui support ." + }, + { + "author": "unknown", + "title": "CLIPTextEncodeAndEnhancev4 (shirazdesigner)", + "reference": "https://github.com/shirazdesigner/CLIPTextEncodeAndEnhancev4", + "files": [ + "https://github.com/shirazdesigner/CLIPTextEncodeAndEnhancev4" + ], + "install_type": "git-clone", + "description": "Nodes:CLIPTextEncodeAndEnhance.\nNOTE:Translation:This is a wrapper that simply makes it easy to install an existing node via git." + }, + { + "author": "umisetokikaze", + "title": "comfyui_mergekit [WIP]", + "reference": "https://github.com/umisetokikaze/comfyui_mergekit", + "files": [ + "https://github.com/umisetokikaze/comfyui_mergekit" + ], + "install_type": "git-clone", + "description": "Nodes:DefineSaveName, SetModels, get_skip, LoadLR, LoadTarget, SetTokenizer, Merge, SetLayer, SetModels" + }, + { + "author": "Video3DGenResearch", + "title": "ComfyUI Batch Input Node", + "reference": "https://github.com/Video3DGenResearch/comfyui-batch-input-node", + "files": [ + "https://github.com/Video3DGenResearch/comfyui-batch-input-node" + ], + "install_type": "git-clone", + "description": "Nodes:BatchInputText" + }, + { + "author": "kijai", + "title": "ComfyUI nodes to use DeepSeek-VL", + "reference": "https://github.com/kijai/ComfyUI-DeepSeek-VL", + "files": [ + "https://github.com/kijai/ComfyUI-DeepSeek-VL" + ], + "install_type": "git-clone", + "description": "[a/https://huggingface.co/deepseek-ai](https://huggingface.co/deepseek-ai)" + }, + { + "author": "GentlemanHu", + "title": "ComfyUI-Notifier", + "reference": "https://github.com/GentlemanHu/ComfyUI-Notifier", + "files": [ + "https://github.com/GentlemanHu/ComfyUI-Notifier" + ], + "install_type": "git-clone", + "description": "Nodes:GentlemanHu_Notifier" + }, + { + "author": "nat-chan", + "title": "Transceiver📡", + "reference": "https://github.com/nat-chan/transceiver", + "files": [ + "https://github.com/nat-chan/transceiver" + ], + "install_type": "git-clone", + "description": "Why? When processing a large number of requests, the SaveImage and LoadImage nodes may be IO-limited, and using shared memory improves performance by passing images only through memory access, not through IO." + }, + { + "author": "DrMWeigand", + "title": "ComfyUI_LineBreakInserter", + "reference": "https://github.com/DrMWeigand/ComfyUI_LineBreakInserter", + "files": [ + "https://github.com/DrMWeigand/ComfyUI_LineBreakInserter" + ], + "install_type": "git-clone", + "description": "Nodes:Line Break Inserter" + }, + { + "author": "WilliamStanford", + "title": "visuallabs_comfyui_nodes", + "reference": "https://github.com/WilliamStanford/ComfyUI-VisualLabs", + "files": [ + "https://github.com/WilliamStanford/ComfyUI-VisualLabs" + ], + "install_type": "git-clone", + "description": "Nodes:PointStringFromFloatArray" + }, + { + "author": "bruce007lee", + "title": "comfyui-cleaner", + "reference": "https://github.com/bruce007lee/comfyui-cleaner", + "files": [ + "https://github.com/bruce007lee/comfyui-cleaner" + ], + "install_type": "git-clone", + "description": "Nodes:cleaner" + }, + { + "author": "ExponentialML", + "title": "ComfyUI_LiveDirector (WIP)", + "reference": "https://github.com/ExponentialML/ComfyUI_LiveDirector", + "files": [ + "https://github.com/ExponentialML/ComfyUI_LiveDirector" + ], + "install_type": "git-clone", + "description": "Experimental method to use reference video to drive motion in generations without training in ComfyUI." + }, + { + "author": "hy134300", + "title": "comfyui-hb-node", + "reference": "https://github.com/hy134300/comfyui-hb-node", + "files": [ + "https://github.com/hy134300/comfyui-hb-node" + ], + "install_type": "git-clone", + "description": "Nodes:sound voice, text concat, latent to list, movie generate, movie batch, hy save image, generate story" + }, + { + "author": "gameltb", + "title": "io_comfyui", + "reference": "https://github.com/gameltb/io_comfyui", + "files": [ + "https://github.com/gameltb/io_comfyui" + ], + "install_type": "git-clone", + "description": "Let Blender work with ComfyUI by ComfyScript. This addon is still in development." + }, + { + "author": "ALatentPlace", + "title": "YANC- Yet Another Node Collection", + "reference": "https://github.com/ALatentPlace/ComfyUI_yanc", + "files": [ + "https://github.com/ALatentPlace/ComfyUI_yanc" + ], + "install_type": "git-clone", + "description": "This is another node collection for ComfyUI. It includes some basic nodes that I find useful, and I've also created them to meet my personal needs." + }, + { + "author": "Jiffies-64", + "title": "ComfyUI-SaveImagePlus", + "reference": "https://github.com/Jiffies-64/ComfyUI-SaveImagePlus", + "files": [ + "https://github.com/Jiffies-64/ComfyUI-SaveImagePlus" + ], + "install_type": "git-clone", + "description": "Nodes:SaveImagePlus" + }, + { + "author": "kadirnar", + "title": "ComfyUI-Adapter [WIP]", + "reference": "https://github.com/kadirnar/ComfyUI-Adapter", + "files": [ + "https://github.com/kadirnar/ComfyUI-Adapter" + ], + "install_type": "git-clone", + "description": "WIP" + }, + { + "author": "Beinsezii", + "title": "comfyui-amd-go-fast", + "reference": "https://github.com/Beinsezii/comfyui-amd-go-fast", + "files": [ + "https://github.com/Beinsezii/comfyui-amd-go-fast" + ], + "install_type": "git-clone", + "description": "This contains all-in-one 'principled' nodes for T2I, I2I, refining, and scaling. Additionally it has many tools for directly manipulating the color of latents, high res fix math, and scripted image post-processing." + }, + { + "author": "sugarkwork", + "title": "comfyui_psd [WIP]", + "reference": "https://github.com/sugarkwork/comfyui_psd", + "files": [ + "https://github.com/sugarkwork/comfyui_psd" + ], + "install_type": "git-clone", + "description": "Not working yet." + }, + { + "author": "SadaleNet", + "title": "ComfyUI Port for Google's Prompt-to-Prompt", + "reference": "https://github.com/SadaleNet/ComfyUI-Prompt-To-Prompt", + "files": [ + "https://github.com/SadaleNet/ComfyUI-Prompt-To-Prompt" + ], + "install_type": "git-clone", + "description": "This is a PoC port of [a/Google's Prompt-to-Prompt](https://github.com/google/prompt-to-prompt/) to ComfyUI. It isn't feature complete. But it's good enough for evaluating if prompt-to-prompt is of any good." + }, + { + "author": "stavsap", + "title": "ComfyUI Ollama [WIP]", + "reference": "https://github.com/stavsap/ComfyUI-React-SDK", + "files": [ + "https://github.com/stavsap/ComfyUI-React-SDK" + ], + "install_type": "git-clone", + "description": "This project is for building React application as an overlay upon ComfyUI.\nProviding and ability to provide desired UI with ComfyUI API and workflows.\nInspired by: [a/https://github.com/cubiq/Comfy_Dungeon](https://github.com/cubiq/Comfy_Dungeon)" + }, + { + "author": "chaojie", + "title": "ComfyUI DynamiCrafter", + "reference": "https://github.com/chaojie/ComfyUI-DynamiCrafter", + "files": [ + "https://github.com/chaojie/ComfyUI-DynamiCrafter" + ], + "install_type": "git-clone", + "description": "ComfyUI [a/DynamiCrafter](https://github.com/Doubiiu/DynamiCrafter)" + }, + { + "author": "cubiq", + "title": "Comfy Dungeon [WIP]", + "reference": "https://github.com/cubiq/Comfy_Dungeon", + "files": [ + "https://github.com/cubiq/Comfy_Dungeon" + ], + "install_type": "git-clone", + "description": "Build D&D Character Portraits with ComfyUI.\nIMPORTANT: At the moment this is mostly a tech demo to show how to build a web app on top of ComfyUI. The code is very messy and the application doesn't guaratee consistent results." + }, + { + "author": "dfl", + "title": "comfyui-stylegan", + "reference": "https://github.com/dfl/comfyui-stylegan", + "files": [ + "https://github.com/dfl/comfyui-stylegan" + ], + "install_type": "git-clone", + "description": "Generator for StyleGAN 3" + }, + { + "author": "A719689614", + "title": "ComfyUI_AC_FUNV8Beta1", + "reference": "https://github.com/A719689614/ComfyUI_AC_FUNV8Beta1", + "files": [ + "https://github.com/A719689614/ComfyUI_AC_FUNV8Beta1" + ], + "install_type": "git-clone", + "description": "Nodes:AC_Super_Controlnet/Checkpoint/Loras/Lora&LCM/KSampler/UpKSampler/SaveImage/PreviewImage/CKPT&LCM/CLIPEN/EmptLatent, AC_FUN_SUPER_LARGE, AC_Super_Come_Ckpt, AC_Super_Come_Lora" + }, + { + "author": "houdinii", + "title": "comfy-magick [WIP]", + "reference": "https://github.com/houdinii/comfy-magick", + "files": [ + "https://github.com/houdinii/comfy-magick" + ], + "install_type": "git-clone", + "description": "This is a way to implement ImageMagick functionality in ComfyUI, which is generally PIL (pillow) based. I'm not sure the best way to handle this, as batch images make it a lot more complex, but the general idea will be two nodes to translate the IMAGE type, a torch.tensor of shape [batch, height, width, channels], or [1, 600, 800, 3] for a single 800x600 image, into/from a wand Image object." + }, + { + "author": "tjorbogarden", + "title": "my-useful-comfyui-custom-nodes", + "reference": "https://github.com/tjorbogarden/my-useful-comfyui-custom-nodes", + "files": [ + "https://github.com/tjorbogarden/my-useful-comfyui-custom-nodes" + ], + "install_type": "git-clone", + "description": "Nodes:My-Image Sizer, KSamplerSDXLAdvanced." + }, + { + "author": "DeTK", + "title": "ComfyUI Node Switcher", + "reference": "https://github.com/DeTK/ComfyUI-Switch", + "files": [ + "https://github.com/DeTK/ComfyUI-Switch" + ], + "install_type": "git-clone", + "description": "Nodes:NodeSwitch." + }, + { + "author": "GrindHouse66", + "title": "GH Tools for ComfyUI", + "reference": "https://github.com/GrindHouse66/ComfyUI-GH_Tools", + "files": [ + "https://github.com/GrindHouse66/ComfyUI-GH_Tools" + ], + "install_type": "git-clone", + "description": "Nodes:GH Tools Image Sizer, GH Tools Simple Scale. Simple quality of life Tools for ComfyUI. Basically, If it makes my life easier, it will be here. The list will grow over time." + }, + { + "author": "sdfxai", + "title": "SDFXBridgeForComfyUI - ComfyUI Custom Node for SDFX Integration", + "reference": "https://github.com/sdfxai/SDFXBridgeForComfyUI", + "files": [ + "https://github.com/sdfxai/SDFXBridgeForComfyUI" + ], + "install_type": "git-clone", + "description": "SDFXBridgeForComfyUI is a custom node designed for seamless integration between ComfyUI and the SDFX solution. This custom node allows users to make ComfyUI compatible with SDFX when running the ComfyUI instance on their local machines." + }, + { + "author": "Beinsezii", + "title": "comfyui-amd-go-fast", + "reference": "https://github.com/Beinsezii/comfyui-amd-go-fast", + "files": [ + "https://github.com/Beinsezii/comfyui-amd-go-fast" + ], + "install_type": "git-clone", + "description": "See details: [a/link](https://github.com/Beinsezii/comfyui-amd-go-fast?tab=readme-ov-file)" + }, + { + "author": "SeedV", + "title": "ComfyUI-SeedV-Nodes [UNSAFE]", + "reference": "https://github.com/SeedV/ComfyUI-SeedV-Nodes", + "files": [ + "https://github.com/SeedV/ComfyUI-SeedV-Nodes" + ], + "install_type": "git-clone", + "description": "Nodes:Script.\n[w/This extension poses a risk of executing arbitrary commands through workflow execution. Please be cautious.]" + }, + { + "author": "mut-ex", + "title": "ComfyUI GLIGEN GUI Node", + "reference": "https://github.com/mut-ex/comfyui-gligengui-node", + "files": [ + "https://github.com/mut-ex/comfyui-gligengui-node" + ], + "install_type": "git-clone", + "description": "This is a simple, straightforward ComfyUI node to be used along with the [a/GLIGEN GUI](https://github.com/mut-ex/gligen-gui) I developed.\nNOTE:[a/Make sure you have the GLIGEN GUI up and running](https://github.com/mut-ex/gligen-gui/tree/main)" + }, + { + "author": "unanan", + "title": "ComfyUI-Dist [WIP]", + "reference": "https://github.com/unanan/ComfyUI-Dist", + "files": [ + "https://github.com/unanan/ComfyUI-Dist" + ], + "install_type": "git-clone", + "description": "For distributed processing ComfyUI workflows within a local area network.\nNot Finished Yet." + }, + { + "author": "NicholasKao1029", + "title": "comfyui-hook", + "reference": "https://github.com/NicholasKao1029/comfyui-hook", + "files": [ + "https://github.com/NicholasKao1029/comfyui-hook" + ], + "install_type": "git-clone", + "description": "This extension provides additional API" + }, + { + "author": "Extraltodeus", + "title": "Conditioning-token-experiments-for-ComfyUI", + "reference": "https://github.com/Extraltodeus/Conditioning-token-experiments-for-ComfyUI", + "files": [ + "https://github.com/Extraltodeus/Conditioning-token-experiments-for-ComfyUI" + ], + "install_type": "git-clone", + "description": "I made these nodes for experimenting so it's far from perfect but at least it is entertaining!\nIt uses cosine similarities or smallest euclidean distances to find the closest tokens." + }, + { + "author": "shadowcz007", + "title": "comfyui-llamafile [WIP]", + "reference": "https://github.com/shadowcz007/comfyui-sd-prompt-mixlab", + "files": [ + "https://github.com/shadowcz007/comfyui-sd-prompt-mixlab" + ], + "install_type": "git-clone", + "description": "This node is an experimental node aimed at exploring the collaborative way of human-machine creation." + }, + { + "author": "gameltb", + "title": "ComfyUI paper playground", + "reference": "https://github.com/gameltb/ComfyUI_paper_playground", + "files": [ + "https://github.com/gameltb/ComfyUI_paper_playground" + ], + "install_type": "git-clone", + "description": "Evaluate some papers in ComfyUI, just playground.\nNOTE: Various models need to be installed, so please visit the repository to check." + }, + { + "author": "huizhang0110", + "title": "ComfyUI_Easy_Nodes_hui", + "reference": "https://github.com/huizhang0110/ComfyUI_Easy_Nodes_hui", + "files": [ + "https://github.com/huizhang0110/ComfyUI_Easy_Nodes_hui" + ], + "install_type": "git-clone", + "description": "Nodes:EasyEmptyLatentImage" + }, + { + "author": "tuckerdarby", + "title": "ComfyUI-TDNodes [WIP]", + "reference": "https://github.com/tuckerdarby/ComfyUI-TDNodes", + "files": [ + "https://github.com/tuckerdarby/ComfyUI-TDNodes" + ], + "install_type": "git-clone", + "description": "Nodes:KSampler (RAVE), KSampler (TF), Object Tracker, KSampler Batched, Video Tracker Prompt, TemporalNet Preprocessor, Instance Tracker Prompt, Instance Diffusion Loader, Hand Tracker Node" + }, + { + "author": "shadowcz007", + "title": "comfyui-CLIPSeg", + "reference": "https://github.com/shadowcz007/comfyui-CLIPSeg", + "files": [ + "https://github.com/shadowcz007/comfyui-CLIPSeg" + ], + "install_type": "git-clone", + "description": "Download [a/CLIPSeg](https://huggingface.co/CIDAS/clipseg-rd64-refined/tree/main), move to : models/clipseg" + }, + { + "author": "stutya", + "title": "ComfyUI-Terminal [UNSAFE]", + "reference": "https://github.com/stutya/ComfyUI-Terminal", + "files": [ + "https://github.com/stutya/ComfyUI-Terminal" + ], + "install_type": "git-clone", + "description": "Run Terminal Commands from ComfyUI.\n[w/This extension poses a risk of executing arbitrary commands through workflow execution. Please be cautious.]" + }, + { + "author": "marcueberall", + "title": "ComfyUI-BuildPath", + "reference": "https://github.com/marcueberall/ComfyUI-BuildPath", + "files": [ + "https://github.com/marcueberall/ComfyUI-BuildPath" + ], + "install_type": "git-clone", + "description": "Nodes: Build Path Adv." + }, + { + "author": "LotzF", + "title": "ComfyUI simple ChatGPT completion [UNSAFE]", + "reference": "https://github.com/LotzF/ComfyUI-Simple-Chat-GPT-completion", + "files": [ + "https://github.com/LotzF/ComfyUI-Simple-Chat-GPT-completion" + ], + "install_type": "git-clone", + "description": "A simple node to request ChatGPT completions. [w/Do not share your workflows including the API key! I'll take no responsibility for your leaked keys.]" + }, + { + "author": "kappa54m", + "title": "ComfyUI_Usability (WIP)", + "reference": "https://github.com/kappa54m/ComfyUI_Usability", + "files": [ + "https://github.com/kappa54m/ComfyUI_Usability" + ], + "install_type": "git-clone", + "description": "Nodes: Load Image Dedup, Load Image By Path." + }, + { + "author": "17Retoucher", + "title": "ComfyUI_Fooocus", + "reference": "https://github.com/17Retoucher/ComfyUI_Fooocus", + "files": [ + "https://github.com/17Retoucher/ComfyUI_Fooocus" + ], + "install_type": "git-clone", + "description": "Custom nodes that help reproduce image generation in Fooocus." + }, + { + "author": "nkchocoai", + "title": "ComfyUI-PromptUtilities", + "reference": "https://github.com/nkchocoai/ComfyUI-PromptUtilities", + "files": [ + "https://github.com/nkchocoai/ComfyUI-PromptUtilities" + ], + "install_type": "git-clone", + "description": "Nodes: Format String, Join String List, Load Preset, Load Preset (Advanced), Const String, Const String (multi line). Add useful nodes related to prompt." + }, + { + "author": "BadCafeCode", + "title": "execution-inversion-demo-comfyui", + "reference": "https://github.com/BadCafeCode/execution-inversion-demo-comfyui", + "files": [ + "https://github.com/BadCafeCode/execution-inversion-demo-comfyui" + ], + "install_type": "git-clone", + "description": "execution-inversion-demo-comfyui" + }, + { + "author": "prodogape", + "title": "ComfyUI-clip-interrogator [WIP]", + "reference": "https://github.com/prodogape/ComfyUI-clip-interrogator", + "files": [ + "https://github.com/prodogape/ComfyUI-clip-interrogator" + ], + "install_type": "git-clone", + "description": "Unofficial ComfyUI extension of clip-interrogator" + }, + { + "author": "poisenbery", + "title": "NudeNet-Detector-Provider [WIP]", + "reference": "https://github.com/poisenbery/NudeNet-Detector-Provider", + "files": [ + "https://github.com/poisenbery/NudeNet-Detector-Provider" + ], + "install_type": "git-clone", + "description": "BBOX Detector Provider for NudeNet. Bethesda version of NudeNet V3 detector provider to work with Impact Pack ComfyUI." + }, + { + "author": "LarryJane491", + "title": "ComfyUI-ModelUnloader", + "reference": "https://github.com/LarryJane491/ComfyUI-ModelUnloader", + "files": [ + "https://github.com/LarryJane491/ComfyUI-ModelUnloader" + ], + "install_type": "git-clone", + "description": "A simple custom node that unloads all models. Useful for developers or users who want to free some memory." + }, + { + "author": "MrAdamBlack", + "title": "CheckProgress [WIP]", + "reference": "https://github.com/MrAdamBlack/CheckProgress", + "files": [ + "https://github.com/MrAdamBlack/CheckProgress" + ], + "install_type": "git-clone", + "description": "I was looking for a node to put in place to ensure my prompt etc where going as expected before the rest of the flow executed. To end the session, I just return the input image as None (see expected error). Recommend using it alongside PreviewImage, then output to the rest of the flow and Save Image." + }, + { + "author": "birnam", + "title": "Gen Data Tester [WIP]", + "reference": "https://github.com/birnam/ComfyUI-GenData-Pack", + "files": [ + "https://github.com/birnam/ComfyUI-GenData-Pack" + ], + "install_type": "git-clone", + "description": "This answers the itch for being able to easily paste [a/CivitAI.com](https://civitai.com/) generated data (or other simple metadata) into Comfy in a way that makes it easy to test with multiple checkpoints." + }, + { + "author": "nidefawl", + "title": "ComfyUI-nidefawl [UNSAFE]", + "reference": "https://github.com/nidefawl/ComfyUI-nidefawl", + "files": [ + "https://github.com/nidefawl/ComfyUI-nidefawl" + ], + "install_type": "git-clone", + "description": "Nodes:PythonScript, BlendImagesWithBoundedMasks, CropImagesWithMasks, VAELoaderDataType, ModelSamplerTonemapNoiseTest, gcLatentTunnel, ReferenceOnlySimple, EmptyImageWithColor, MaskFromColor, SetLatentCustomNoise, LatentToImage, ImageToLatent, LatentScaledNoise, DisplayAnyType, SamplerCustomCallback, CustomCallback, SplitCustomSigmas, SamplerDPMPP_2M_SDE_nidefawl, LatentPerlinNoise.
    [w/This node is an unsafe node that includes the capability to execute arbitrary python script.]" + }, + { + "author": "foglerek", + "title": "comfyui-cem-tools", + "reference": "https://github.com/foglerek/comfyui-cem-tools", + "files": [ + "https://github.com/foglerek/comfyui-cem-tools" + ], + "install_type": "git-clone", + "description": "Nodes:ProcessImageBatch" + }, + { + "author": "komojini", + "title": "ComfyUI_Prompt_Template_CustomNodes", + "reference": "https://github.com/komojini/ComfyUI_Prompt_Template_CustomNodes", + "files": [ + "https://raw.githubusercontent.com/komojini/ComfyUI_Prompt_Template_CustomNodes/main/prompt_with_template.py" + ], + "install_type": "copy", + "description": "Nodes:Prompt with Template" + }, + { + "author": "talesofai", + "title": "comfyui-supersave [WIP]", + "reference": "https://github.com/talesofai/comfyui-supersave", + "files": [ + "https://github.com/talesofai/comfyui-supersave" + ], + "install_type": "git-clone", + "description": "WIP" + }, + { + "author": "Sai-ComfyUI", + "title": "ComfyUI-MS-Nodes [WIP]", + "reference": "https://github.com/Sai-ComfyUI/ComfyUI-MS-Nodes", + "files": [ + "https://github.com/Sai-ComfyUI/ComfyUI-MS-Nodes" + ], + "install_type": "git-clone", + "description": "WIP" + }, + { + "author": "eigenpunk", + "title": "ComfyUI-audio", + "reference": "https://github.com/eigenpunk/ComfyUI-audio", + "files": [ + "https://github.com/eigenpunk/ComfyUI-audio" + ], + "install_type": "git-clone", + "description": "generative audio tools for ComfyUI. highly experimental-expect things to break." + }, + { + "author": "Jaxkr", + "title": "comfyui-terminal-command [UNSAFE]", + "reference": "https://github.com/Jaxkr/comfyui-terminal-command", + "files": [ + "https://github.com/Jaxkr/comfyui-terminal-command" + ], + "install_type": "git-clone", + "description": "Nodes: Run Terminal Command. [w/This node is an unsafe node that includes the capability to execute terminal commands.]" + }, + { + "author": "BlueDangerX", + "title": "ComfyUI-BDXNodes [WIP]", + "reference": "https://github.com/BlueDangerX/ComfyUI-BDXNodes", + "files": [ + "https://github.com/BlueDangerX/ComfyUI-BDXNodes" + ], + "install_type": "git-clone", + "description": "Nodes: Node Jumper. Various quality of life testing nodes" + }, + { + "author": "IvanZhd", + "title": "comfyui-codeformer [WIP]", + "reference": "https://github.com/IvanZhd/comfyui-codeformer", + "files": [ + "https://github.com/IvanZhd/comfyui-codeformer" + ], + "install_type": "git-clone", + "description": "Nodes:Image Inverter" + }, + { + "author": "alt-key-project", + "title": "Dream Project Video Batches [WIP]", + "reference": "https://github.com/alt-key-project/comfyui-dream-video-batches", + "files": [ + "https://github.com/alt-key-project/comfyui-dream-video-batches" + ], + "install_type": "git-clone", + "description": "NOTE: This is currently work in progress. Expect nodes to break (or be broken) until 1.0 release." + }, + { + "author": "oyvindg", + "title": "ComfyUI-TrollSuite", + "reference": "https://github.com/oyvindg/ComfyUI-TrollSuite", + "files": [ + "https://github.com/oyvindg/ComfyUI-TrollSuite" + ], + "install_type": "git-clone", + "description": "Nodes: BinaryImageMask, ImagePadding, LoadLastCreatedImage, RandomMask, TransparentImage." + }, + { + "author": "romeobuilderotti", + "title": "ComfyUI-EZ-Pipes", + "reference": "https://github.com/romeobuilderotti/ComfyUI-EZ-Pipes", + "files": [ + "https://github.com/romeobuilderotti/ComfyUI-EZ-Pipes" + ], + "install_type": "git-clone", + "description": "ComfyUI-EZ-Pipes is a set of custom pipe nodes for ComfyUI. It provides a set of Input/Edit/Output nodes for each pipe type." + }, + { + "author": "wormley", + "title": "comfyui-wormley-nodes", + "reference": "https://github.com/wormley/comfyui-wormley-nodes", + "files": [ + "https://github.com/wormley/comfyui-wormley-nodes" + ], + "install_type": "git-clone", + "description": "Nodes: CheckpointVAELoaderSimpleText, CheckpointVAESelectorText, LoRA_Tag_To_Stack" + }, + { + "author": "Brandelan", + "title": "ComfyUI_bd_customNodes", + "reference": "https://github.com/Brandelan/ComfyUI_bd_customNodes", + "files": [ + "https://github.com/Brandelan/ComfyUI_bd_customNodes" + ], + "install_type": "git-clone", + "description": "Nodes: BD Random Range, BD Settings, BD Sequencer." + }, + { + "author": "Jordach", + "title": "comfy-consistency-vae", + "reference": "https://github.com/Jordach/comfy-consistency-vae", + "files": [ + "https://github.com/Jordach/comfy-consistency-vae" + ], + "install_type": "git-clone", + "description": "Nodes: Comfy_ConsistencyVAE" + }, + { + "author": "gameltb", + "title": "ComfyUI_stable_fast", + "reference": "https://github.com/gameltb/ComfyUI_stable_fast", + "files": [ + "https://github.com/gameltb/ComfyUI_stable_fast" + ], + "install_type": "git-clone", + "description": "Nodes:ApplyStableFastUnet. Experimental usage of stable-fast." + }, + { + "author": "jn-jairo", + "title": "jn_node_suite_comfyui [WIP]", + "reference": "https://github.com/jn-jairo/jn_node_suite_comfyui", + "files": [ + "https://github.com/jn-jairo/jn_node_suite_comfyui" + ], + "install_type": "git-clone", + "description": "Image manipulation nodes, Temperature control nodes, Tiling nodes, Primitive and operation nodes, ..." + }, + { + "author": "laksjdjf", + "title": "ssd-1b-comfyui", + "reference": "https://github.com/laksjdjf/ssd-1b-comfyui", + "files": [ + "https://github.com/laksjdjf/ssd-1b-comfyui" + ], + "install_type": "git-clone", + "description": "Experimental node for SSD-1B. This node is not need for latest comfyui." + }, + { + "author": "flowtyone", + "title": "comfyui-flowty-lcm", + "reference": "https://github.com/flowtyone/comfyui-flowty-lcm", + "files": [ + "https://github.com/flowtyone/comfyui-flowty-lcm" + ], + "install_type": "git-clone", + "description": "This is a comfyui early testing node for LCM, adapted from [a/https://github.com/0xbitches/sd-webui-lcm](https://github.com/0xbitches/sd-webui-lcm). It uses the diffusers backend unfortunately and not comfy's model loading mechanism. But the intention here is just to be able to execute lcm inside comfy.\nNOTE: 0xbitches's 'Latent Consistency Model for ComfyUI' is original implementation." + }, + { + "author": "doucx", + "title": "ComfyUI_WcpD_Utility_Kit", + "reference": "https://github.com/doucx/ComfyUI_WcpD_Utility_Kit", + "files": [ + "https://github.com/doucx/ComfyUI_WcpD_Utility_Kit" + ], + "install_type": "git-clone", + "description": "Nodes: MergeStrings, ExecStrAsCode, RandnLatentImage. [w/NOTE: This extension includes the ability to execute code as a string in nodes. Be cautious during installation, as it can pose a security risk.]" + }, + { + "author": "WSJUSA", + "title": "pre-comfyui-stablsr", + "reference": "https://github.com/WSJUSA/Comfyui-StableSR", + "files": [ + "https://github.com/WSJUSA/Comfyui-StableSR" + ], + "install_type": "git-clone", + "description": "This is a development respository for debugging migration of StableSR to Comfyui" + }, + { + "author": "Dr.Lt.Data", + "title": "ComfyUI-Workflow-Component [WIP]", + "reference": "https://github.com/ltdrdata/ComfyUI-Workflow-Component", + "files": [ + "https://github.com/ltdrdata/ComfyUI-Workflow-Component" + ], + "install_type": "git-clone", + "description": "This extension provides the capability to use ComfyUI Workflow as a component and the ability to use the Image Refiner functionality based on components. NOTE: This is an experimental extension feature with no consideration for backward compatibility and can be highly unstable." + } + ] +} diff --git a/ComfyUI/custom_nodes/ComfyUI-Manager/node_db/dev/extension-node-map.json b/ComfyUI/custom_nodes/ComfyUI-Manager/node_db/dev/extension-node-map.json new file mode 100644 index 0000000000000000000000000000000000000000..98fb7757976ebc61933f2c9883d3ab24a2527715 --- /dev/null +++ b/ComfyUI/custom_nodes/ComfyUI-Manager/node_db/dev/extension-node-map.json @@ -0,0 +1,10013 @@ +{ + "https://github.com/17Retoucher/ComfyUI_Fooocus": [ + [ + "BasicScheduler", + "CLIPLoader", + "CLIPMergeSimple", + "CLIPSave", + "CLIPSetLastLayer", + "CLIPTextEncode", + "CLIPTextEncodeSDXL", + "CLIPTextEncodeSDXLRefiner", + "CLIPVisionEncode", + "CLIPVisionLoader", + "Canny", + "CheckpointLoader", + "CheckpointLoaderSimple", + "CheckpointSave", + "ConditioningAverage", + "ConditioningCombine", + "ConditioningConcat", + "ConditioningSetArea", + "ConditioningSetAreaPercentage", + "ConditioningSetMask", + "ConditioningSetTimestepRange", + "ConditioningZeroOut", + "ControlNetApply", + "ControlNetApplyAdvanced", + "ControlNetLoader", + "CropMask", + "DiffControlNetLoader", + "DiffusersLoader", + "DualCLIPLoader", + "EmptyImage", + "EmptyLatentImage", + "ExponentialScheduler", + "FeatherMask", + "FlipSigmas", + "Fooocus Controlnet", + "Fooocus Hirefix", + "Fooocus KSampler", + "Fooocus Loader", + "Fooocus LoraStack", + "Fooocus PreKSampler", + "Fooocus negative", + "Fooocus positive", + "Fooocus stylesSelector", + "FreeU", + "FreeU_V2", + "GLIGENLoader", + "GLIGENTextBoxApply", + "GrowMask", + "HyperTile", + "HypernetworkLoader", + "ImageBatch", + "ImageBlend", + "ImageBlur", + "ImageColorToMask", + "ImageCompositeMasked", + "ImageCrop", + "ImageInvert", + "ImageOnlyCheckpointLoader", + "ImagePadForOutpaint", + "ImageQuantize", + "ImageScale", + "ImageScaleBy", + "ImageScaleToTotalPixels", + "ImageSharpen", + "ImageToMask", + "ImageUpscaleWithModel", + "InvertMask", + "JoinImageWithAlpha", + "KSampler", + "KSamplerAdvanced", + "KSamplerSelect", + "KarrasScheduler", + "LatentAdd", + "LatentBatch", + "LatentBlend", + "LatentComposite", + "LatentCompositeMasked", + "LatentCrop", + "LatentFlip", + "LatentFromBatch", + "LatentInterpolate", + "LatentMultiply", + "LatentRotate", + "LatentSubtract", + "LatentUpscale", + "LatentUpscaleBy", + "LoadImage", + "LoadImageMask", + "LoadLatent", + "LoraLoader", + "LoraLoaderModelOnly", + "MaskComposite", + "MaskToImage", + "ModelMergeAdd", + "ModelMergeBlocks", + "ModelMergeSimple", + "ModelMergeSubtract", + "ModelSamplingContinuousEDM", + "ModelSamplingDiscrete", + "PatchModelAddDownscale", + "PerpNeg", + "PolyexponentialScheduler", + "PorterDuffImageComposite", + "PreviewImage", + "RebatchImages", + "RebatchLatents", + "RepeatImageBatch", + "RepeatLatentBatch", + "RescaleCFG", + "SDTurboScheduler", + "SVD_img2vid_Conditioning", + "SamplerCustom", + "SamplerDPMPP_2M_SDE", + "SamplerDPMPP_SDE", + "SaveAnimatedPNG", + "SaveAnimatedWEBP", + "SaveImage", + "SaveLatent", + "SelfAttentionGuidance", + "SetLatentNoiseMask", + "SolidMask", + "SplitImageWithAlpha", + "SplitSigmas", + "StableZero123_Conditioning", + "StyleModelApply", + "StyleModelLoader", + "TomePatchModel", + "UNETLoader", + "UpscaleModelLoader", + "VAEDecode", + "VAEDecodeTiled", + "VAEEncode", + "VAEEncodeForInpaint", + "VAEEncodeTiled", + "VAELoader", + "VAESave", + "VPScheduler", + "VideoLinearCFGGuidance", + "unCLIPCheckpointLoader", + "unCLIPConditioning" + ], + { + "title_aux": "ComfyUI_Fooocus" + } + ], + "https://github.com/1H-hobit/ComfyUI_InternVL3": [ + [ + "DynamicPreprocess", + "InternVLHFInference", + "InternVLModelLoader" + ], + { + "title_aux": "ComfyComfyUI_InternVL3 [WIP]" + } + ], + "https://github.com/1hew/ComfyUI-1hewNodes": [ + [ + "ImageAddLabel", + "ImageBBoxOverlayByMask", + "ImageBatchToList", + "ImageBlendModesByAlpha", + "ImageBlendModesByCSS", + "ImageCropByMaskAlpha", + "ImageCropSquare", + "ImageCropWithBBoxMask", + "ImageEdgeCropPad", + "ImageEditStitch", + "ImageGetSize", + "ImageHLFreqCombine", + "ImageHLFreqSeparate", + "ImageHLFreqTransform", + "ImageListAppend", + "ImageListToBatch", + "ImageLumaMatte", + "ImagePasteByBBoxMask", + "ImagePlot", + "ImageResizeFluxKontext", + "ImageResizeUniversal", + "ImageSolid", + "ImageTileMerge", + "ImageTileSplit", + "ListCustomFloat", + "ListCustomInt", + "ListCustomSeed", + "ListCustomString", + "MaskBatchMathOps", + "MaskBatchToList", + "MaskCropByBBoxMask", + "MaskFillHole", + "MaskListToBatch", + "MaskMathOps", + "PathBuild", + "RangeMapping", + "StringCoordinateToBBoxMask", + "StringCoordinateToBBoxes", + "TextCustomExtract", + "TextFormat", + "TextJoinMulti", + "TextLoadLocal" + ], + { + "title_aux": "ComfyUI-1hewNodes [WIP]" + } + ], + "https://github.com/206811/ComfyUI_ZhipuAIO": [ + [ + "ZhipuAIOConfigNode", + "ZhipuAIO_VisionNode", + "ZhipuTranslateNode" + ], + { + "title_aux": "ComfyUI_ZhipuAIO" + } + ], + "https://github.com/3dmindscapper/ComfyUI-PartField": [ + [ + "PartFieldClustering", + "PartFieldExportParts", + "PartFieldInference", + "PartFieldModelDownLoader", + "PartFieldSplitter", + "PartFieldViewer" + ], + { + "title_aux": "ComfyUI-PartField [WIP]" + } + ], + "https://github.com/3dmindscapper/ComfyUI-Sam-Mesh": [ + [ + "SamMeshExporter", + "SamMeshExporter+SamMesh", + "SamMeshLoader", + "SamMeshLoader+SamMesh", + "SamMeshRenderer", + "SamMeshRenderer+SamMesh", + "SamMeshSegmenter", + "SamMeshSegmenter+SamMesh", + "SamModelDownloader", + "SamModelDownloader+SamMesh" + ], + { + "title_aux": "ComfyUI-Sam-Mesh [WIP]" + } + ], + "https://github.com/438443467/ComfyUI-SanMian-Nodes": [ + [ + "FaceAlignPro", + "FaceAlignProRestore", + "SANMIN Adapt Coordinates", + "SanmiKSampler", + "sanmi AddTextToImage", + "sanmi Adjust Transparency By Mask", + "sanmi AdjustHexBrightness", + "sanmi Align Images with Mask", + "sanmi BinarizeMask", + "sanmi BlendICLight", + "sanmi Chinese To Character", + "sanmi ColorOverlayOnMask", + "sanmi Compare", + "sanmi CompareV2", + "sanmi Counter", + "sanmi CreateTxtForImages", + "sanmi Filter Prompt Words", + "sanmi Float", + "sanmi Florence2toCoordinates", + "sanmi Get Content From Excel", + "sanmi Get LastPathComponent", + "sanmi Get Mask White Region Size", + "sanmi GetFilePath", + "sanmi GetMostCommonColor", + "sanmi ImageBatchSplitter", + "sanmi Image_Rotate", + "sanmi Int90", + "sanmi IntToBOOLEAN", + "sanmi Load Image Batch", + "sanmi LoadImageFromPath", + "sanmi LoadImagesanmi", + "sanmi Mask To Box", + "sanmi MaskToBboxes", + "sanmi MaskWhiteRatioAnalyzer", + "sanmi Path Captioner", + "sanmi Path Change", + "sanmi Read Image Prompt", + "sanmi RectMaskAnalyzer", + "sanmi Reduce Mask", + "sanmi RestoreJson", + "sanmi Sanmi_Text_Concatenate", + "sanmi Save Image To Local", + "sanmi SimpleWildcards", + "sanmi SortTheMasksLeftRight", + "sanmi SortTheMasksSize", + "sanmi Special Counter", + "sanmi StrToPinYin", + "sanmi String Counter", + "sanmi String Counter V2", + "sanmi StringToBox", + "sanmi Time", + "sanmi Upscale And Keep Original Size" + ], + { + "title_aux": "ComfyUI-SanMian-Nodes" + } + ], + "https://github.com/5x00/ComfyUI-Prompt-Plus": [ + [ + "LoadAPI", + "LoadCustomModel", + "LoadFlorenceModel", + "Prompt", + "RunAPIVLM", + "RunCustomVLM", + "TriggerToPromptAPI", + "TriggerToPromptCustom", + "TriggerToPromptSimple" + ], + { + "title_aux": "ComfyUI-Prompt-Plus [WIP]" + } + ], + "https://github.com/7BEII/Comfyui_PDuse": [ + [ + "Empty_Line", + "ImageBlendText", + "ImageBlendV1", + "ImageRatioCrop", + "Load_Images", + "Load_Images_V1", + "PDFile_name_fix", + "PDIMAGE_ImageCombine", + "PDIMAGE_LongerSize", + "PDIMAGE_Rename", + "PDIMAGE_SAVE_PATH_V2", + "PDIMAGE_SAVE_PATH_V3", + "PDImageConcante", + "PDImageResize", + "PDImageResizeV2", + "PDJSON_BatchJsonIncremental", + "PDJSON_Group", + "PDStringConcate", + "PDStringInput", + "PDTEXT_SAVE_PATH_V1", + "PDTEXT_SAVE_PATH_V2", + "PDTEXT_SAVE_PATH_V3", + "PD_BatchCropBlackBorder", + "PD_CropBorder", + "PD_GetImageRatio", + "PD_GetImageSize", + "PD_Image_Crop_Location", + "PD_Image_Rotate_v1", + "PD_Image_centerCrop", + "PD_MASK_SELECTION", + "PD_RemoveBlackBackground", + "PD_RemoveColorWords", + "PD_ShowText", + "PD_Text Overlay Node", + "PD_imagesave_path", + "PD_number_start", + "PD_random_prompt", + "PD_rename_batch_v1", + "PD_replace_word", + "PDimage_corp_v1", + "PDimage_corp_v2", + "mask_edge_selector" + ], + { + "title_aux": "comfyui-promptbymood [WIP]" + } + ], + "https://github.com/A4P7J1N7M05OT/ComfyUI-ManualSigma": [ + [ + "ManualSigma" + ], + { + "title_aux": "ComfyUI-ManualSigma" + } + ], + "https://github.com/A4P7J1N7M05OT/ComfyUI-VAELoaderSDXLmod": [ + [ + "EmptyLatentImageVariable", + "ModifiedSDXLVAELoader" + ], + { + "title_aux": "ComfyUI-VAELoaderSDXLmod" + } + ], + "https://github.com/A719689614/ComfyUI_AC_FUNV7-FLUX-": [ + [ + "AC_Super_CLIP(FLUX)", + "AC_Super_UNET(FLUX)" + ], + { + "title_aux": "ComfyUI_AC_FUNV7-FLUX- [WIP]" + } + ], + "https://github.com/A719689614/ComfyUI_AC_FUNV8Beta1": [ + [ + "AC_FUN_SUPER_DESIGN_LARGE", + "AC_FUN_SUPER_LARGE", + "AC_Super_CKPT&LCM", + "AC_Super_CLIPEN", + "AC_Super_Checkpoint", + "AC_Super_Controlnet", + "AC_Super_EmptLatent", + "AC_Super_KSampler", + "AC_Super_Lora&LCM", + "AC_Super_Loras", + "AC_Super_MaskScale", + "AC_Super_MaskScaleBy", + "AC_Super_PreviewImage", + "AC_Super_PreviewMask", + "AC_Super_SaveImage", + "AC_Super_UpKSampler" + ], + { + "title_aux": "ComfyUI_AC_FUNV8Beta1" + } + ], + "https://github.com/AICodeFactory/ComfyUI-Viva": [ + [ + "HttpTrigger_Common", + "HttpTrigger_Image", + "HttpTrigger_Viva" + ], + { + "title_aux": "ComfyUI-Viva" + } + ], + "https://github.com/AIFSH/ComfyUI-OpenDIT": [ + [ + "DITModelLoader", + "DITPromptNode", + "DiffVAELoader", + "LattePipeLineNode", + "OpenSoraNode", + "OpenSoraPlanPipeLineNode", + "PABConfigNode", + "PreViewVideo", + "SchedulerLoader", + "T5EncoderLoader", + "T5TokenizerLoader" + ], + { + "title_aux": "ComfyUI-OpenDIT [WIP]" + } + ], + "https://github.com/AIFSH/ComfyUI-ViViD": [ + [ + "LoadImagePath", + "LoadVideo", + "PreViewVideo", + "ViViD_Node" + ], + { + "title_aux": "ComfyUI-ViViD" + } + ], + "https://github.com/AIFSH/HivisionIDPhotos-ComfyUI": [ + [ + "AddBackgroundNode", + "AddWaterMarkNode", + "ENHivisionParamsNode", + "HivisionLayOutNode", + "HivisionNode", + "LaterProcessNode", + "ZHHivisionParamsNode" + ], + { + "author": "cuny", + "description": "", + "title_aux": "HivisionIDPhotos-ComfyUI" + } + ], + "https://github.com/AIFSH/IMAGDressing-ComfyUI": [ + [ + "IMAGDressingNode", + "TextNode" + ], + { + "title_aux": "IMAGDressing-ComfyUI" + } + ], + "https://github.com/AIFSH/UltralightDigitalHuman-ComfyUI": [ + [ + "InferUltralightDigitalHumanNode", + "TrainUltralightDigitalHumanNode" + ], + { + "title_aux": "UltralightDigitalHuman-ComfyUI" + } + ], + "https://github.com/AIFSH/UtilNodes-ComfyUI": [ + [ + "GetRGBEmptyImgae", + "LoadVideo", + "PreViewVideo", + "PromptTextNode" + ], + { + "title_aux": "UtilNodes-ComfyUI [WIP]" + } + ], + "https://github.com/ALatentPlace/ComfyUI_yanc": [ + [ + "> Bloom", + "> Blur", + "> Brightness", + "> Clear Text", + "> Combine Channels", + "> Contrast", + "> Divide Channels", + "> Edge Enhance", + "> Film Grain", + "> Float to Int", + "> Fog", + "> Get Mean Color", + "> HUE", + "> Int", + "> Int to Text", + "> Layer Weights (for IPAMS)", + "> Lens Distortion", + "> Light Source Mask", + "> Load Image", + "> Load Image From Folder", + "> Mask Curves", + "> NIKSampler", + "> Noise From Image", + "> Normal Map Lighting", + "> RGB Color", + "> RGB Shift", + "> Resolution by Aspect Ratio", + "> Rotate Image", + "> Saturation", + "> Save Image", + "> Save Text", + "> Scale Image to Side", + "> Scanlines", + "> Sharpen", + "> Text", + "> Text Combine", + "> Text Count", + "> Text Pick Line by Index", + "> Text Pick Random Line", + "> Text Random Weights", + "> Text Replace", + "> Vignette" + ], + { + "title_aux": "YANC- Yet Another Node Collection" + } + ], + "https://github.com/APZmedia/comfyui-textools": [ + [ + "APZmediaImageMarkdownTextOverlay", + "APZmediaImageRichTextOverlay", + "APZmediaImageRichTextOverlayV2" + ], + { + "author": "Pablo Apiolazza", + "description": "This extension provides rich text overlay functionalities, color management, and text parsing utilities for ComfyUI.", + "nickname": "ComfyUI Text Tools", + "title": "ComfyUI APZmedia Text Tools", + "title_aux": "comfyui-textools [WIP]" + } + ], + "https://github.com/Aero-Ex/comfyui_diffswap": [ + [ + "DiffSwapNode" + ], + { + "title_aux": "comfyui_diffswap" + } + ], + "https://github.com/AhBumm/ComfyUI-Upscayl": [ + [ + "Upscayl Upscaler" + ], + { + "nodename_pattern": "\\(BillBum\\)$", + "title_aux": "ComfyUI-Upscayl" + } + ], + "https://github.com/AhBumm/ComfyUI_MangaLineExtraction-hf": [ + [ + "MangaLineExtraction-hf" + ], + { + "title_aux": "ComfyUI_MangaLineExtraction" + } + ], + "https://github.com/AkiEvansDev/ComfyUI-Tools": [ + [ + "AE.AnySwitch", + "AE.AnyTypeSwitch", + "AE.BRIARemBG", + "AE.BRIARemBGAdvanced", + "AE.ChangeSamplerConfig", + "AE.CheckpointList", + "AE.CheckpointLoader", + "AE.CompareFloat", + "AE.CompareInt", + "AE.ControlNetApplyWithConfig", + "AE.ControlNetConfig", + "AE.DisplayAny", + "AE.ExtractControlNetConfig", + "AE.ExtractHiresFixConfig", + "AE.ExtractImg2ImgConfig", + "AE.ExtractOutpaintConfig", + "AE.ExtractSamplerConfig", + "AE.Float", + "AE.FloatList", + "AE.FloatSwitch", + "AE.FloatToInt", + "AE.GaussianBlurMask", + "AE.GetImageSize", + "AE.GetLatentSize", + "AE.GroupsMuter", + "AE.HiresFixConfig", + "AE.ImageAdjustment", + "AE.ImageBlank", + "AE.ImageBlendMask", + "AE.ImageBlendMode", + "AE.ImageCannyFilter", + "AE.ImageDragonFilter", + "AE.ImageHighPassFilter", + "AE.ImageLevels", + "AE.ImageLucySharpen", + "AE.ImagePixelate", + "AE.ImagePowerNoise", + "AE.ImageStyleFilter", + "AE.Img2ImgConfig", + "AE.InpaintWithModel", + "AE.Int", + "AE.IntList", + "AE.IntSwitch", + "AE.IntToFloat", + "AE.KSamplerHiresFixWithConfig", + "AE.KSamplerImg2ImgWithConfig", + "AE.KSamplerInpaintWithConfig", + "AE.KSamplerInpaintWithConfigAndImage", + "AE.KSamplerOutpaintWithConfig", + "AE.KSamplerOutpaintWithConfigAndImage", + "AE.KSamplerWithConfig", + "AE.LoadImageFromPath", + "AE.LoadInpaintModel", + "AE.LoraLoader", + "AE.LorasList", + "AE.LorasLoader", + "AE.MathFloat", + "AE.MathInt", + "AE.OutpaintConfig", + "AE.OutpaintWithModel", + "AE.OutpaintWithModelAndConfig", + "AE.Range", + "AE.RangeList", + "AE.SDXLConfig", + "AE.SDXLPrompt", + "AE.SDXLPromptWithHires", + "AE.SDXLRegionalPrompt", + "AE.SDXLRegionalPromptWithHires", + "AE.SamplerConfig", + "AE.SamplerList", + "AE.SaveImage", + "AE.SchedulerList", + "AE.Seed", + "AE.String", + "AE.StringConcat", + "AE.StringEquals", + "AE.StringLength", + "AE.StringList", + "AE.StringReplace", + "AE.StringSwitch", + "AE.Text", + "AE.ToString", + "AE.ToStringConcat", + "AE.UpscaleLatentBy", + "AE.VAEEncodeInpaintConditioning", + "AE.XYRange" + ], + { + "title_aux": "ComfyUI-Tools" + } + ], + "https://github.com/Alazuaka/comfyui-lora-stack-node": [ + [ + "AlazukaCheckpoint", + "EsLoraSet" + ], + { + "title_aux": "ES_nodes for ComfyUI by Alazuka [WIP]" + } + ], + "https://github.com/AlejandroTuzzi/TUZZI-ByPass": [ + [ + "LinkSuppressor", + "SequentialTextReaderAuto", + "TUZZI-Bypasser", + "TUZZI-DataloungeScraper", + "TUZZI-DirectoryImagePromptReader", + "TUZZI-GeminiFlash25", + "TUZZI-GroqNode", + "TUZZI-ImageAudioToVideo", + "TUZZI-ImageExtractorSaver", + "TUZZI-LineCounter", + "TUZZI-LinkSuppressor", + "TUZZI-NumberLines", + "TUZZI-PlosArticleScraper", + "TUZZI-RangedSelectorText5", + "TUZZI-RangedSelectorTitleURL10", + "TUZZI-RangedSelectorTitleURL5", + "TUZZI-RedditPostExtractor", + "TUZZI-SaveVideo", + "TUZZI-SequentialTextReader", + "TUZZI-SequentialTextReaderAuto", + "TUZZI-SmartAudioVisualComposer", + "TUZZI-TVTropesScraper", + "TUZZI-TextFormatter", + "TUZZI-TextFormatterPlus", + "TUZZI-TextTranslatorExporter", + "TUZZI-TextTruncatorPlus", + "TUZZI-YouTubeCommentExtractor", + "TUZZI-YouTubeSubtitleExtractor" + ], + { + "title_aux": "TUZZI-ByPass [WIP]" + } + ], + "https://github.com/AlexXi19/ComfyUI-OpenAINode": [ + [ + "ImageWithPrompt", + "TextWithPrompt" + ], + { + "title_aux": "ComfyUI-OpenAINode" + } + ], + "https://github.com/AlexYez/comfyui-timesaver": [ + [ + "TS Cube to Equirectangular", + "TS Equirectangular to Cube", + "TS Files Downloader", + "TS Qwen2.5", + "TS Youtube Chapters", + "TSCropToMask", + "TSRestoreFromCrop", + "TSWhisper", + "TS_DeflickerNode", + "TS_FilePathLoader", + "TS_FilmEmulation", + "TS_FilmGrain", + "TS_Free_Video_Memory", + "TS_ImageResize", + "TS_MarianTranslator", + "TS_Qwen3", + "TS_VideoDepthNode", + "TS_Video_Upscale_With_Model" + ], + { + "title_aux": "ComfyUI Timesaver Nodes" + } + ], + "https://github.com/AllenEdgarPoe/ComfyUI-Xorbis-nodes": [ + [ + "Add Human Styler", + "ConcaveHullImage", + "Convert Monochrome", + "ImageBWPostprocessor", + "ImageBWPreprocessor", + "Inpaint Crop Xo", + "LoadData", + "Mask Aligned bbox for ConcaveHull", + "Mask Aligned bbox for Inpainting", + "Mask Aligned bbox for Inpainting2", + "Mask Square bbox for Inpainting", + "One Image Compare", + "RT4KSR Loader", + "RandomPromptStyler", + "Save Log Info", + "Three Image Compare", + "Upscale RT4SR" + ], + { + "title_aux": "ComfyUI-Xorbis-nodes [WIP]" + } + ], + "https://github.com/Alvaroeai/ComfyUI-SunoAI-Mds": [ + [ + "Mideas_SunoAI_AudioManager", + "Mideas_SunoAI_Generator", + "Mideas_SunoAI_ProxyDownloadNode", + "Mideas_SunoAI_ProxyNode" + ], + { + "title_aux": "ComfyUI-SunoAI-Mds" + } + ], + "https://github.com/Anonymzx/ComfyUI-Indonesia-TTS": [ + [ + "Facebook MMS-TTS-IND Variants FX" + ], + { + "title_aux": "ComfyUI-Indonesia-TTS [WIP]" + } + ], + "https://github.com/Anze-/ComfyUI-OIDN": [ + [ + "OIDN Denoise" + ], + { + "title_aux": "ComfyUI-OIDN [WIP]" + } + ], + "https://github.com/Anze-/ComfyUI_deepDeband": [ + [ + "deepDeband Inference" + ], + { + "title_aux": "ComfyUI_deepDeband [WIP]" + } + ], + "https://github.com/ArmandAlbert/Kwai_font_comfyui": [ + [ + "Kwaifont_Image_Cropper", + "Kwaifont_Resnet101_Loader", + "Kwaifont_Resnet101_Runner", + "Kwaifont_Resnet50_Loader", + "Kwaifont_Resnet50_Runner" + ], + { + "title_aux": "Kwai_font_comfyui" + } + ], + "https://github.com/ArthusLiang/comfyui-face-remap": [ + [ + "FaceRemap" + ], + { + "title_aux": "comfyui-face-remap [WIP]" + } + ], + "https://github.com/BAIS1C/ComfyUI_BASICDancePoser": [ + [ + "ComfyUI_BASICDancePoser" + ], + { + "title_aux": "ComfyUI-AudioDuration [WIP]" + } + ], + "https://github.com/BadCafeCode/execution-inversion-demo-comfyui": [ + [ + "AccumulateNode", + "AccumulationGetItemNode", + "AccumulationGetLengthNode", + "AccumulationHeadNode", + "AccumulationSetItemNode", + "AccumulationTailNode", + "AccumulationToListNode", + "BoolOperationNode", + "ComponentInput", + "ComponentMetadata", + "ComponentOutput", + "DebugPrint", + "ExecutionBlocker", + "FloatConditions", + "ForLoopClose", + "ForLoopOpen", + "IntConditions", + "IntMathOperation", + "InversionDemoAdvancedPromptNode", + "InversionDemoLazyConditional", + "InversionDemoLazyIndexSwitch", + "InversionDemoLazyMixImages", + "InversionDemoLazySwitch", + "ListToAccumulationNode", + "MakeListNode", + "StringConditions", + "ToBoolNode", + "WhileLoopClose", + "WhileLoopOpen" + ], + { + "title_aux": "execution-inversion-demo-comfyui" + } + ], + "https://github.com/BaronVonBoolean/ComfyUI-FileOps": [ + [ + "File Mv", + "File Path", + "Make Dir" + ], + { + "title_aux": "ComfyUI-FileOps [UNSAFE]" + } + ], + "https://github.com/Baverne/comfyUI-TiledWan": [ + [ + "TileAndStitchBack", + "TiledWanImageStatistics", + "TiledWanImageToMask", + "TiledWanInpaintCropImproved", + "TiledWanInpaintStitchImproved", + "TiledWanMaskStatistics", + "TiledWanVideoSLGSimple", + "TiledWanVideoSamplerSimple", + "TiledWanVideoVACEpipe", + "WanVideoVACEpipe" + ], + { + "title_aux": "TiledWan ComfyUI Node Set [WIP]" + } + ], + "https://github.com/BenjaMITM/ComfyUI_On_The_Fly_Wildcards": [ + [ + "Display String", + "Wildcard Creator", + "Wildcard Loader", + "Wildcard Selector" + ], + { + "title_aux": "ComfyUI_On_The_Fly_Wildcards [WIP]" + } + ], + "https://github.com/BetaDoggo/ComfyUI-LogicGates": [ + [ + "AND", + "BitMemory", + "BoolToString", + "ByteMemory", + "ByteToBits", + "CreateByte", + "NAND", + "NOR", + "NOT", + "ON", + "OR", + "SWITCH", + "XNOR", + "XOR" + ], + { + "title_aux": "ComfyUI-LogicGates" + } + ], + "https://github.com/Big-Idea-Technology/ComfyUI-Movie-Tools": [ + [ + "LoadImagesFromSubdirsBatch", + "SaveImagesWithSubfolder" + ], + { + "title_aux": "ComfyUI-Movie-Tools [WIP]" + } + ], + "https://github.com/BigStationW/flowmatch_scheduler-comfyui": [ + [ + "FlowMatchSigmas" + ], + { + "title_aux": "flowmatch_scheduler-comfyui" + } + ], + "https://github.com/BinglongLi/ComfyUI_ToolsForAutomask": [ + [ + "Closing Mask", + "Conditional Mask Selector", + "Directional Mask Expansion", + "Mask Fill Gaps Convex Hull", + "Opening Mask", + "Precise Add Mask", + "Precise Subtract Mask", + "Prune Thin Branches Mask", + "Remove Small Regions Mask" + ], + { + "title_aux": "ComfyUI_ToolsForAutomask" + } + ], + "https://github.com/BlueDangerX/ComfyUI-BDXNodes": [ + [ + "BDXTestInt", + "ColorMatch", + "ColorToMask", + "ConditioningMultiCombine", + "ConditioningSetMaskAndCombine", + "ConditioningSetMaskAndCombine3", + "ConditioningSetMaskAndCombine4", + "ConditioningSetMaskAndCombine5", + "CreateAudioMask", + "CreateFadeMask", + "CreateFluidMask", + "CreateGradientMask", + "CreateTextMask", + "CrossFadeImages", + "EmptyLatentImagePresets", + "GrowMaskWithBlur", + "SomethingToString", + "VRAM_Debug" + ], + { + "author": "BlueDangerX", + "title": "BDXNodes", + "title_aux": "ComfyUI-BDXNodes [WIP]" + } + ], + "https://github.com/BobRandomNumber/ComfyUI-DiaTTS": [ + [ + "DiaGenerate", + "DiaLoader" + ], + { + "title_aux": "ComfyUI DiaTest TTS Node [WIP]" + } + ], + "https://github.com/Brandelan/ComfyUI_bd_customNodes": [ + [ + "BD Random Range", + "BD Random Settings", + "BD Sequencer", + "BD Settings" + ], + { + "title_aux": "ComfyUI_bd_customNodes" + } + ], + "https://github.com/BrettMedia/comfyui-bhtools": [ + [ + "CinematicSceneDirectorTools", + "EndOfWorkflowClearingBHTools", + "PromptInferenceBHTools", + "SaveImageVideoBHTools" + ], + { + "title_aux": "comfyui-bhtools [WIP]" + } + ], + "https://github.com/BuffMcBigHuge/ComfyUI-Buff-Nodes": [ + [ + "ConsoleOutput", + "FilePathSelectorFromDirectory", + "MostRecentFileSelector", + "RaftOpticalFlowNode", + "StringProcessor", + "TwoImageConcatenator" + ], + { + "title_aux": "ComfyUI-Buff-Nodes [WIP]" + } + ], + "https://github.com/Burgstall-labs/ComfyUI-BS_FalAi-API-Video": [ + [ + "FalAILipSyncNode", + "FalAPIOmniProNode", + "FalAPIVideoGeneratorI2V", + "FalAPIVideoGeneratorT2V" + ], + { + "title_aux": "ComfyUI-BS_FalAi-API-Video [WIP]" + } + ], + "https://github.com/Bwebbfx/ComfyUI_FaceParsing": [ + [ + "FaceParsingInfer", + "FaceParsingLoader", + "FacePartMask" + ], + { + "title_aux": "ComfyUI Face Parsing Nodes [WIP]" + } + ], + "https://github.com/COcisuts/CObot-ComfyUI-WhisperToTranscription": [ + [ + "CobotWhisperToTransciption" + ], + { + "title_aux": "CObot-ComfyUI-WhisperToTranscription [WIP]" + } + ], + "https://github.com/CY-CHENYUE/ComfyUI-FramePack-HY": [ + [ + "CreateKeyframes_HY", + "FramePackBucketResize_HY", + "FramePackDiffusersSampler_HY", + "LoadFramePackDiffusersPipeline_HY" + ], + { + "title_aux": "ComfyUI-FramePack-HY" + } + ], + "https://github.com/CeeVeeR/ComfyUi-Text-Tiler": [ + [ + "Text Tiler" + ], + { + "title_aux": "ComfyUi-Text-Tiler" + } + ], + "https://github.com/Chargeuk/ComfyUI-vts-nodes": [ + [ + "VTS Add Text To list", + "VTS Calculate Upscale Amount", + "VTS Clean Text", + "VTS Clean Text List", + "VTS Clear Ram", + "VTS Clip Text Encode", + "VTS Color Mask To Mask", + "VTS Colour Match", + "VTS Conditioning Set Batch Mask", + "VTS Count Characters", + "VTS Create Character Mask", + "VTS Fix Image Tags", + "VTS Image Composite Masked", + "VTS Image Upscale With Model", + "VTS Images Crop From Masks", + "VTS Images Scale", + "VTS Images Scale To Min", + "VTS Merge Delimited Text", + "VTS Merge Text", + "VTS Merge Text Lists", + "VTS Reduce Batch Size", + "VTS Render People Kps", + "VTS Repeat Text As List", + "VTS Replace Text In List", + "VTS Sharpen", + "VTS To Text", + "VTS_Load_Pose_Keypoints", + "Vts Text To Batch Prompt" + ], + { + "title_aux": "ComfyUI-vts-nodes [WIP]" + } + ], + "https://github.com/Charonartist/ComfyUI-send-eagle-pro_2": [ + [ + "Batch Send Media to Eagle", + "Send Audio to Eagle", + "Send Eagle with text", + "Send Media to Eagle", + "Send Media to Eagle (Advanced)", + "Send Video to Eagle", + "Send Webp Image to Eagle" + ], + { + "title_aux": "ComfyUI-send-eagle-pro" + } + ], + "https://github.com/ChrisColeTech/ComfyUI-Get-Random-File": [ + [ + "Get Image File By Index", + "Get Video File By Index", + "Random File Path", + "Random Image Path", + "Random Video Path" + ], + { + "title_aux": "ComfyUI-Get-Random-File [UNSAFE]" + } + ], + "https://github.com/Clelstyn/ComfyUI-Inpaint_with_Detailer": [ + [ + "FilterAndBlurMask", + "MaskedResizeImage", + "PasteMaskedImage" + ], + { + "title_aux": "ComfyUI-Inpaint_with_Detailer" + } + ], + "https://github.com/Clybius/ComfyUI-FluxDeCLIP": [ + [ + "FluxDeCLIPCheckpointLoader" + ], + { + "title_aux": "ComfyUI-FluxDeCLIP" + } + ], + "https://github.com/Comfy-Org/ComfyUI_devtools": [ + [ + "DevToolsDeprecatedNode", + "DevToolsErrorRaiseNode", + "DevToolsErrorRaiseNodeWithMessage", + "DevToolsExperimentalNode", + "DevToolsLoadAnimatedImageTest", + "DevToolsLongComboDropdown", + "DevToolsMultiSelectNode", + "DevToolsNodeWithBooleanInput", + "DevToolsNodeWithDefaultInput", + "DevToolsNodeWithForceInput", + "DevToolsNodeWithOnlyOptionalInput", + "DevToolsNodeWithOptionalComboInput", + "DevToolsNodeWithOptionalInput", + "DevToolsNodeWithOutputCombo", + "DevToolsNodeWithOutputList", + "DevToolsNodeWithSeedInput", + "DevToolsNodeWithStringInput", + "DevToolsNodeWithUnionInput", + "DevToolsNodeWithV2ComboInput", + "DevToolsNodeWithValidation", + "DevToolsObjectPatchNode", + "DevToolsRemoteWidgetNode", + "DevToolsRemoteWidgetNodeWithControlAfterRefresh", + "DevToolsRemoteWidgetNodeWithParams", + "DevToolsRemoteWidgetNodeWithRefresh", + "DevToolsRemoteWidgetNodeWithRefreshButton", + "DevToolsSimpleSlider" + ], + { + "title_aux": "ComfyUI_devtools [WIP]" + } + ], + "https://github.com/D1-3105/ComfyUI-VideoStream": [ + [ + "FloWWeaverExportSingleFrameGRPC" + ], + { + "title_aux": "ComfyUI-VideoStream" + } + ], + "https://github.com/DataCTE/ComfyUI-DataVoid-nodes": [ + [ + "IPAAdapterFaceIDBatch", + "IPAdapter", + "IPAdapterAdvanced", + "IPAdapterBatch", + "IPAdapterClipVisionEnhancer", + "IPAdapterClipVisionEnhancerBatch", + "IPAdapterCombineEmbeds", + "IPAdapterCombineParams", + "IPAdapterCombineWeights", + "IPAdapterEmbeds", + "IPAdapterEmbedsBatch", + "IPAdapterEncoder", + "IPAdapterFaceID", + "IPAdapterFaceIDKolors", + "IPAdapterFromParams", + "IPAdapterInsightFaceLoader", + "IPAdapterLoadEmbeds", + "IPAdapterMS", + "IPAdapterModelLoader", + "IPAdapterNoise", + "IPAdapterPreciseComposition", + "IPAdapterPreciseCompositionBatch", + "IPAdapterPreciseStyleTransfer", + "IPAdapterPreciseStyleTransferBatch", + "IPAdapterPromptScheduleFromWeightsStrategy", + "IPAdapterRegionalConditioning", + "IPAdapterSameEnergy", + "IPAdapterSaveEmbeds", + "IPAdapterStyleComposition", + "IPAdapterStyleCompositionBatch", + "IPAdapterTiled", + "IPAdapterTiledBatch", + "IPAdapterUnifiedLoader", + "IPAdapterUnifiedLoaderCommunity", + "IPAdapterUnifiedLoaderFaceID", + "IPAdapterWeights", + "IPAdapterWeightsFromStrategy", + "MegaMergeSDXL", + "PrepImageForClipVision" + ], + { + "title_aux": "ComfyUI-DataVoid-nodes [WIP]" + } + ], + "https://github.com/DeTK/ComfyUI-Switch": [ + [ + "NodeSwitch" + ], + { + "title_aux": "ComfyUI Node Switcher" + } + ], + "https://github.com/DenRakEiw/DenRakEiw_Nodes": [ + [ + "ColorGeneratorNode" + ], + { + "title_aux": "Denrakeiw Nodes [WIP]" + } + ], + "https://github.com/DiffusionWave-YT/DiffusionWave_PickResolution": [ + [ + "Blacklist_String_DiffusionWave \ud83c\udf0a", + "ImageBatchMulti_DiffusionWave", + "ImageSimpleSaver_DiffusionWave", + "Int_PickResolution_DiffusionWave \ud83c\udf0a", + "LoadImagesFromFolder_DiffusionWave", + "MergeImages_DiffusionWave", + "Order_String_Tags_DiffusionWave \ud83c\udf0a", + "OverlayImages_DiffusionWave", + "PickResolution_DiffusionWave \ud83c\udf0a", + "PromptExpression_DiffusionWave \ud83c\udf0a", + "RemoveBackgroundByColor_DiffusionWave", + "ResizeLongestSide_DiffusionWave", + "Seed__DiffusionWave \ud83c\udf0a" + ], + { + "title_aux": "DiffusionWave_PickResolution [WIP]" + } + ], + "https://github.com/DoctorDiffusion/ComfyUI-Flashback": [ + [ + "LatentExport", + "LatentImport", + "LatentLoop" + ], + { + "title_aux": "ComfyUI-Flashback" + } + ], + "https://github.com/DonutsDelivery/ComfyUI-DonutNodes": [ + [ + "ApplyLBW //Inspire", + "Donut Block Calibration", + "Donut Detailer", + "Donut Detailer 2", + "Donut Detailer 4", + "Donut Detailer LoRA 5", + "Donut Detailer XL Blocks", + "Donut Frequency Analysis", + "Donut Sharpener", + "Donut Sharpener (from reference)", + "Donut Simple Calibration", + "DonutApplyLoRAStack", + "DonutClipEncode", + "DonutFillerClip", + "DonutFillerModel", + "DonutHotReload", + "DonutLoRAStack", + "DonutMultiModelSampler", + "DonutSDXLTeaCache", + "DonutSDXLTeaCacheStats", + "DonutSampler", + "DonutSampler (Advanced)", + "DonutWidenMergeCLIP", + "DonutWidenMergeUNet", + "LoadLBW //Inspire", + "LoraBlockInfo //Inspire", + "LoraLoaderBlockWeight //Inspire", + "MakeLBW //Inspire", + "SaveLBW //Inspire", + "XY Input: Lora Block Weight //Inspire" + ], + { + "title_aux": "ComfyUI-DonutDetailer" + } + ], + "https://github.com/DrMWeigand/ComfyUI_LineBreakInserter": [ + [ + "LineBreakInserter" + ], + { + "title_aux": "ComfyUI_LineBreakInserter" + } + ], + "https://github.com/DraconicDragon/ComfyUI_e621_booru_toolkit": [ + [ + "GetAnyBooruPostAdv", + "GetBooruPost", + "TagWikiFetch" + ], + { + "title_aux": "ComfyUI e621 booru Toolkit" + } + ], + "https://github.com/Dream-Pixels-Forge/ComfyUI-Mzikart-Player": [ + [ + "MzikartPlayerNode" + ], + { + "title_aux": "ComfyUI-Mzikart-Player [WIP]" + } + ], + "https://github.com/DreamsInAutumn/ComfyUI-Autumn-LLM-Nodes": [ + [ + "GeminiImageToPrompt", + "GeminiPromptBuilder", + "LLMPromptBuilder" + ], + { + "title_aux": "ComfyUI-Autumn-LLM-Nodes" + } + ], + "https://github.com/Dreamshot-io/ComfyUI-Extend-Resolution": [ + [ + "ResolutionPadding" + ], + { + "title_aux": "ComfyUI-Extend-Resolution" + } + ], + "https://github.com/ELiZswe/ComfyUI-ELiZTools": [ + [ + "ELiZMeshUVWrap" + ], + { + "title_aux": "ComfyUI-ELiZTools" + } + ], + "https://github.com/EQXai/ComfyUI_EQX": [ + [ + "CountFaces_EQX", + "Extract Filename - EQX", + "Extract LORA name - EQX", + "FaceDetectOut", + "File Image Selector", + "Load Prompt From File - EQX", + "LoadRetinaFace_EQX", + "LoraStackEQX_random", + "NSFW Detector EQX", + "SaveImage_EQX", + "WorkFlow Check" + ], + { + "title_aux": "ComfyUI_EQX" + } + ], + "https://github.com/Eagle-CN/ComfyUI-Addoor": [ + [ + "AD_AnyFileList", + "AD_BatchImageLoadFromDir", + "AD_CSVPromptStyler", + "AD_CSVReader", + "AD_CSVTranslator", + "AD_DeleteLocalAny", + "AD_FluxTrainStepMath", + "AD_HFDownload", + "AD_ImageDrawRectangleSimple", + "AD_ImageIndexer", + "AD_ImageSaver", + "AD_LoadImageAdvanced", + "AD_PromptReplace", + "AD_TextIndexer", + "AD_TextListToString", + "AD_TextSaver", + "AD_TxtToCSVCombiner", + "AD_ZipSave", + "AD_advanced-padding", + "AD_color-image", + "AD_image-concat", + "AD_image-resize", + "AD_mockup-maker", + "AD_poster-maker", + "AD_prompt-saver", + "ImageCaptioner", + "ImageResize", + "Incrementer \ud83e\udeb4", + "TextAppendNode", + "Width and height for scaling image to ideal resolution \ud83e\udeb4", + "Width and height from aspect ratio \ud83e\udeb4", + "YANC.MultilineString", + "comfyui-easy-padding", + "image concat mask" + ], + { + "author": "ComfyUI Addoor", + "description": "Save prompts to CSV file with customizable naming pattern", + "title": "ComfyUI-PromptSaver", + "title_aux": "ComfyUI-Addoor [UNSAFE]" + } + ], + "https://github.com/Elawphant/ComfyUI-MusicGen": [ + [ + "AudioLoader", + "MusicGen" + ], + { + "title_aux": "ComfyUI-MusicGen [WIP]" + } + ], + "https://github.com/ElyZeng/ComfyUI-Translator": [ + [ + "TextTranslatorNode" + ], + { + "title_aux": "ComfyUI-Translator [NAME CONFLICT]" + } + ], + "https://github.com/Elypha/ComfyUI-Prompt-Helper": [ + [ + "PromptHelper_CombineConditioning", + "PromptHelper_ConcatConditioning", + "PromptHelper_ConcatString", + "PromptHelper_EncodeMultiStringCombine", + "PromptHelper_FormatString", + "PromptHelper_LoadPreset", + "PromptHelper_LoadPresetAdvanced", + "PromptHelper_String", + "PromptHelper_StringMultiLine", + "PromptHelper_WeightedPrompt" + ], + { + "title_aux": "ComfyUI-Prompt-Helper [WIP]" + } + ], + "https://github.com/EmanueleUniroma2/ComfyUI-FLAC-to-WAV": [ + [ + "AudioToWavConverter" + ], + { + "title_aux": "ComfyUI-FLAC-to-WAV [WIP]" + } + ], + "https://github.com/EmilioPlumed/ComfyUI-Math": [ + [ + "GreatestCommonDenominator", + "LowestCommonMultiple" + ], + { + "title_aux": "ComfyUI-Math [WIP]" + } + ], + "https://github.com/EricRollei/Comfy-Metadata-System": [ + [ + "EnhancedMetadataFilterNode_V2", + "Eric_Duplicate_Image_Finder_v021", + "Eric_Image_Sorter_V13", + "Eric_Keyword_Sorter_V6", + "Eric_Metadata_Debugger_V2", + "Eric_Metadata_Entry_V2", + "Eric_Metadata_Filter_V2", + "Eric_Metadata_Query_V3", + "MetadataAwareSaveImage_v099", + "MetadataConsolidatorNode_V2", + "PngInfoDiagnosticV3", + "PngMetadataExtractorV3", + "TextOverlayNode_v04" + ], + { + "title_aux": "Comfy-Metadata-System [WIP]" + } + ], + "https://github.com/Estanislao-Oviedo/ComfyUI-CustomNodes": [ + [ + "Attention couple", + "AttentionCouple", + "LoadImageFolder", + "MakeBatchFromSingleImage", + "RegionConditionMerge", + "RegionConditionSpecPct", + "RegionConditionSpecPx" + ], + { + "title_aux": "ComfyUI-CustomNodes [NAME CONFLICT]" + } + ], + "https://github.com/ExponentialML/ComfyUI_LiveDirector": [ + [ + "LiveDirector" + ], + { + "title_aux": "ComfyUI_LiveDirector (WIP)" + } + ], + "https://github.com/Extraltodeus/Conditioning-token-experiments-for-ComfyUI": [ + [ + "Automatic wildcards", + "Conditioning (Cosine similarities)", + "Conditioning (Maximum absolute)", + "Conditioning (Maximum absolute) text inputs", + "Conditioning (Scale by absolute sum)", + "Conditioning merge clip g/l", + "Conditioning similar tokens recombine", + "Conditioning to text", + "Quick and dirty text encode", + "encode_all_tokens_SDXL" + ], + { + "title_aux": "Conditioning-token-experiments-for-ComfyUI" + } + ], + "https://github.com/FaberVS/MultiModel": [ + [ + "ActiveModel", + "DenoiseSelector", + "KSamplerPipe", + "ListSelector", + "ModelParamsPipe", + "MySwitchIndex", + "ParamsPipeUnpack", + "PromptBuilder" + ], + { + "title_aux": "MultiModel" + } + ], + "https://github.com/Fannovel16/ComfyUI-AppIO": [ + [ + "AppIO_FitResizeImage", + "AppIO_ImageInput", + "AppIO_ImageInputFromID", + "AppIO_ImageOutput", + "AppIO_IntegerInput", + "AppIO_ResizeInstanceAndPaste", + "AppIO_ResizeInstanceImageMask", + "AppIO_StringInput", + "AppIO_StringOutput" + ], + { + "title_aux": "ComfyUI-AppIO" + } + ], + "https://github.com/Filexor/File_x_dynamic_prompt2": [ + [ + "File_x_DynamicPrompt2", + "File_x_DynamicPrompt2 with States IO" + ], + { + "title_aux": "File_x_dynamic_prompt2" + } + ], + "https://github.com/FinetunersAI/comfyui-fast-group-link": [ + [ + "FastGroupLink" + ], + { + "title_aux": "Fast Group Link [WIP]" + } + ], + "https://github.com/FinetunersAI/finetuners": [ + [ + "AutoImageResize", + "GroupLink", + "VariablesInjector" + ], + { + "title_aux": "ComfyUI Finetuners [WIP]" + } + ], + "https://github.com/Fucci-Mateo/ComfyUI-Airtable": [ + [ + "Push pose to Airtable" + ], + { + "title_aux": "ComfyUI-Airtable [WIP]" + } + ], + "https://github.com/GalactusX31/ComfyUI-FileBrowserAPI": [ + [ + "PathSelectorNode" + ], + { + "title_aux": "ComfyUI-FileBrowserAPI [UNSAFE]" + } + ], + "https://github.com/GentlemanHu/ComfyUI-Notifier": [ + [ + "GentlemanHu_Notifier" + ], + { + "title_aux": "ComfyUI-Notifier" + } + ], + "https://github.com/George0726/ComfyUI-video-accessory": [ + [ + "VideoAcc_CameraTrajectoryAdvance", + "VideoAcc_CameraTrajectoryRecam", + "VideoAcc_ImageResizeAdvanced", + "VideoAcc_ImageUpscaleVideo", + "VideoAcc_LoadImage", + "VideoAcc_LoadVideo", + "VideoAcc_SaveMP4", + "VideoAcc_imageSize" + ], + { + "title_aux": "ComfyUI-video-accessory [WIP]" + } + ], + "https://github.com/Grant-CP/ComfyUI-LivePortraitKJ-MPS": [ + [ + "DownloadAndLoadLivePortraitModels", + "LivePortraitProcess" + ], + { + "title_aux": "ComfyUI-LivePortraitKJ-MPS" + } + ], + "https://github.com/Grey3016/Save2Icon": [ + [ + "ConvertToIconNode" + ], + { + "title_aux": "Save2Icon" + } + ], + "https://github.com/GrindHouse66/ComfyUI-GH_Tools": [ + [ + "GHImg_Sizer", + "GHSimple_Scale" + ], + { + "title_aux": "GH Tools for ComfyUI" + } + ], + "https://github.com/Hapseleg/ComfyUI-This-n-That": [ + [ + "Show Prompt (Hapse)", + "Show Prompt TnT", + "Simple Ratio Selector (Hapse)", + "Simple Ratio Selector TnT", + "Simple Seed Selector TnT" + ], + { + "title_aux": "This n that (Hapse)" + } + ], + "https://github.com/HuangYuChuh/ComfyUI-LLMs-Toolkit": [ + [ + "DeepSeekImageAnalyst", + "DeepSeekImageGeneration", + "DeepSeekModelLoader", + "ImagePreprocessor", + "LLM_Loader", + "OpenAICompatibleLoader", + "VideoFileUploader" + ], + { + "title_aux": "ComfyUI-LLMs-Toolkit [WIP]" + } + ], + "https://github.com/Huangcj2005/comfyui-HandDetect": [ + [ + "HandMaskGenerator" + ], + { + "title_aux": "comfyui-HandDetect" + } + ], + "https://github.com/IfnotFr/ComfyUI-Ifnot-Pack": [ + [ + "Face Crop", + "Face Crop Mouth", + "Get Beard Mask" + ], + { + "title_aux": "ComfyUI-Ifnot-Pack" + } + ], + "https://github.com/IgPoly/ComfyUI-igTools": [ + [ + "IGT_SimpleTilesCalc" + ], + { + "title_aux": "ComfyUI-igTools" + } + ], + "https://github.com/IsItDanOrAi/ComfyUI-exLoadout": [ + [ + "dropdowns", + "exCheckpointLoader", + "exLoadoutCheckpointLoader", + "exLoadoutEditCell", + "exLoadoutReadColumn", + "exLoadoutSeg", + "exLoadoutSeg2", + "exLoadoutSelector", + "exSeg", + "exSeg2" + ], + { + "title_aux": "ComfyUI-exLoadout [WIP]" + } + ], + "https://github.com/IuvenisSapiens/ComfyUI_MiniCPM-V-2_6-int4": [ + [ + "DisplayText", + "LoadVideo", + "MiniCPM_VQA", + "MiniCPM_VQA_Polished", + "MultipleImagesInput", + "PreviewVideo" + ], + { + "title_aux": "ComfyUI_MiniCPM-V-2_6-int4" + } + ], + "https://github.com/IvanZhd/comfyui-codeformer": [ + [ + "RedBeanie_CustomImageInverter" + ], + { + "title_aux": "comfyui-codeformer [WIP]" + } + ], + "https://github.com/Jaxkr/comfyui-terminal-command": [ + [ + "Terminal" + ], + { + "title_aux": "comfyui-terminal-command [UNSAFE]" + } + ], + "https://github.com/JiSenHua/ComfyUI-yolov5-face": [ + [ + "FaceDetect" + ], + { + "title_aux": "ComfyUI-yolov5-face [WIP]" + } + ], + "https://github.com/Jiffies-64/ComfyUI-SaveImagePlus": [ + [ + "SaveImagePlus" + ], + { + "title_aux": "ComfyUI-SaveImagePlus" + } + ], + "https://github.com/Jingwen-genies/comfyui-genies-nodes": [ + [ + "GeniesPoseEstimation", + "GeniesRGBToHSV", + "GeniesScaleFaceByKeypoints", + "GeniesSelectRGBByMask" + ], + { + "title_aux": "comfyui-genies-nodes" + } + ], + "https://github.com/JioJe/comfyui_video_BC": [ + [ + "TextBatchIndexer", + "TextBatchLoader", + "TextBatchReplace", + "TextBatchSaver", + "VideoCombine", + "VideoSequenceProcessor" + ], + { + "title_aux": "comfyui_video_BC [WIP]" + } + ], + "https://github.com/JissiChoi/ComfyUI-Jissi-List": [ + [ + "JissiFloatList", + "JissiList", + "JissiMatching", + "JissiMultiplePrompts", + "JissiText", + "JissiTextFileToListDisplay", + "JissiTextTemplate", + "JissiView" + ], + { + "title_aux": "ComfyUI-Jissi-List [WIP]" + } + ], + "https://github.com/Jordach/comfy-consistency-vae": [ + [ + "Comfy_ConsistencyVAE" + ], + { + "title_aux": "comfy-consistency-vae" + } + ], + "https://github.com/Jpzz/comfyui-ixiworks": [ + [ + "BuildCharacterPromptNode", + "BuildPromptNode", + "JsonParserNode", + "MergeStringsNode", + "SelectIndexNode" + ], + { + "title_aux": "IxiWorks StoryBoard Nodes [WIP]" + } + ], + "https://github.com/Junst/ComfyUI-PNG2SVG2PNG": [ + [ + "PNG2SVG2PNG" + ], + { + "title_aux": "ComfyUI-PNG2SVG2PNG" + } + ], + "https://github.com/KERRY-YUAN/ComfyUI_Python_Executor": [ + [ + "NodePython" + ], + { + "title_aux": "Python_Executor [UNSAFE]" + } + ], + "https://github.com/Kayarte/Time-Series-Nodes-for-ComfyUI": [ + [ + "DomainTimeSeriesPrep", + "TimeSeriesLoader", + "TimeSeriesPredictor" + ], + { + "title_aux": "Time Series Nodes for ComfyUI [Experimental]" + } + ], + "https://github.com/KihongK/comfyui-roysnodes": [ + [ + "CLIPMultiTextEncode", + "Create_ConditionLoRA", + "Create_ConditionLoRA_MainPrompt", + "Load_ConditionLoRA", + "OpenAI_Summarize", + "Unzip_ConditionLoRA" + ], + { + "title_aux": "ComfyUI-RoysNodes [WIP]" + } + ], + "https://github.com/KoreTeknology/ComfyUI-Nai-Production-Nodes-Pack": [ + [ + "Brightness Image", + "ColorMatch2", + "Contrast Image", + "Get Text", + "Image Difference", + "ImageConcatenate", + "ImageDesaturate", + "ImageExtend", + "ImageFlip", + "ImageRotate", + "LoadImageNai", + "Math Operation", + "NoteAdvanced", + "Set Text" + ], + { + "title_aux": "ComfyUI Production Nodes Pack [WIP]" + } + ], + "https://github.com/Krish-701/RK_Comfyui": [ + [ + "RK_Accumulate_Text_Multiline", + "RK_Accumulate_Text_Multiline_Numbered", + "RK_Advanced_Script_Finder", + "RK_CSV_File_State_Looper_v01", + "RK_CSV_File_State_Looper_v02", + "RK_Calc", + "RK_Concatenate_Text", + "RK_Excel_File_State_Looper", + "RK_ImageViewer", + "RK_Read_Excel_Row", + "RK_Write_Text", + "RK_seed", + "rk_save_image", + "rk_save_image_v01" + ], + { + "title_aux": "RK_Comfyui" + } + ], + "https://github.com/KurtHokke/ComfyUI_KurtHokke_Nodes": [ + [ + "AIO_Tuner_Pipe", + "ApplyCondsExtraOpts", + "BashScriptNode", + "BooleanFromPipe", + "BooleanToPipe", + "COND_ExtraOpts", + "COND_ExtraOpts_2", + "COND_SET_STRENGTH_ExtraOpts", + "ChainTextEncode", + "CkptPipe", + "CompareTorch", + "DynamicThresholding", + "DynamicThresholdingBasic", + "EmptyLatentSize", + "EmptyLatentSize64", + "ExecutePythonNode", + "ExpMath", + "ExpMathDual", + "ExpMathQuad", + "InspectNode", + "LoadUnetAndClip", + "LoraFluxParams", + "MergeExtraOpts", + "ModelPipe1", + "ModelPipe2", + "NoModel_CkptLoader", + "NoNegExtraOpts", + "Node_BOOL", + "Node_Float", + "Node_INT", + "Node_RandomRange", + "Node_String", + "Node_StringMultiline", + "SEED_ExtraOpts", + "SamplerCustomAdvanced_Pipe", + "SamplerSel", + "SchedulerSel", + "SedOnString", + "UnetClipLoraLoader", + "UnetClipLoraLoaderBasic", + "VAE_ExtraOpts", + "ViewExtraOpts", + "batchsize_ExtraOpts", + "get_lora_metadata", + "mycombine", + "re_sub_str", + "splitcond", + "str_str", + "str_str_str_str" + ], + { + "title_aux": "ComfyUI-VLMStudio" + } + ], + "https://github.com/LK-168/comfyui_LK_selfuse": [ + [ + "Adv Scheduler", + "Artist Add Prefix", + "CaompareDebug", + "EasyUse_XYPlot_Lora_Folder", + "Efficient_XYplot_LoRA_Batch", + "InspectModelArchitecture", + "LK_MaskToSEGS", + "LK_SegsAdjust", + "Mask Connected Remove", + "Mask Diff", + "Mask Filter with Rate", + "Mask Get Max", + "Pick From String", + "Print Sigma", + "Random Choice", + "Read Text File", + "SaveImageTensor", + "SaveMaskTensor", + "String Filter", + "String Filter v2", + "String Modify", + "String Modify Simple", + "String Remove Duplicate", + "String Replace 10", + "String Switch", + "String To Save", + "XYplot_Lora_lk" + ], + { + "title_aux": "comfyui_LK_selfuse" + } + ], + "https://github.com/LZpenguin/ComfyUI-Text": [ + [ + "Add_text_by_mask" + ], + { + "title_aux": "ComfyUI-Text" + } + ], + "https://github.com/LarryJane491/ComfyUI-ModelUnloader": [ + [ + "Model Unloader" + ], + { + "title_aux": "ComfyUI-ModelUnloader" + } + ], + "https://github.com/Laser-one/ComfyUI-align-pose": [ + [ + "Align_Pose" + ], + { + "title_aux": "ComfyUI-align-pose" + } + ], + "https://github.com/Letz-AI/ComfyUI-LetzAI": [ + [ + "LetzAI Generator" + ], + { + "title_aux": "ComfyUI-LetzAI [UNSAFE]" + } + ], + "https://github.com/Lilien86/Comfyui_Latent_Interpolation": [ + [ + "Latent Interpolator Multi" + ], + { + "title_aux": "Comfyui_Latent_Interpolation [WIP]" + } + ], + "https://github.com/Linsoo/ComfyUI-Linsoo-Custom-Nodes": [ + [ + "LinsooEmptyLatentImage", + "LinsooLoadImage", + "LinsooMultiInputs", + "LinsooMultiOutputs", + "LinsooSaveImage" + ], + { + "title_aux": "ComfyUI-Linsoo-Custom-Nodes" + } + ], + "https://github.com/Looking-Glass/LKG-ComfyUI": [ + [ + "BridgePreview", + "LoadFolder", + "ScaleAndMaintainAspect", + "SideBySide" + ], + { + "title_aux": "LKG-ComfyUI" + } + ], + "https://github.com/LotzF/ComfyUI-Simple-Chat-GPT-completion": [ + [ + "AzureChatGptCompletion", + "ChatGPTCompletion" + ], + { + "title_aux": "ComfyUI simple ChatGPT completion [UNSAFE]" + } + ], + "https://github.com/LucianGnn/ComfyUI-Lucian": [ + [ + "AudioDurationCalculator" + ], + { + "title_aux": "ComfyUI-Lucian [WIP]" + } + ], + "https://github.com/LucianoCirino/ComfyUI-invAIder-Nodes": [ + [ + "\ud83d\udc7e Any Switch", + "\ud83d\udc7e Any Switch Large", + "\ud83d\udc7e Any Switch Medium", + "\ud83d\udc7e Any to Any", + "\ud83d\udc7e Debug Tensor Structure", + "\ud83d\udc7e Evaluate Anything", + "\ud83d\udc7e Image Crop", + "\ud83d\udc7e Image Grid", + "\ud83d\udc7e Image Overlay", + "\ud83d\udc7e Img to Gif", + "\ud83d\udc7e Int to Bits", + "\ud83d\udc7e Is Image Fully Transparent", + "\ud83d\udc7e Load Any", + "\ud83d\udc7e Load Image Batch", + "\ud83d\udc7e Number Counter", + "\ud83d\udc7e Preview Image if True", + "\ud83d\udc7e Save AnimPNG If True", + "\ud83d\udc7e Save Any", + "\ud83d\udc7e Save Image If True", + "\ud83d\udc7e Seed Controller", + "\ud83d\udc7e Signed Integer" + ], + { + "title_aux": "ComfyUI-invAIder-Nodes" + } + ], + "https://github.com/LyazS/ComfyUI-aznodes": [ + [ + "CrossFadeImageSequence", + "ImageGrayscaleAZ", + "SaveImageAZ" + ], + { + "title_aux": "ComfyUI-aznodes" + } + ], + "https://github.com/LykosAI/ComfyUI-Inference-Core-Nodes": [ + [ + "AIO_Preprocessor", + "AnimalPosePreprocessor", + "AnimeFace_SemSegPreprocessor", + "AnimeLineArtPreprocessor", + "AnyLineArtPreprocessor_aux", + "BAE-NormalMapPreprocessor", + "BinaryPreprocessor", + "CannyEdgePreprocessor", + "ColorPreprocessor", + "ControlNetAuxSimpleAddText", + "ControlNetPreprocessorSelector", + "DSINE-NormalMapPreprocessor", + "DWPreprocessor", + "DensePosePreprocessor", + "DepthAnythingPreprocessor", + "DepthAnythingV2Preprocessor", + "DiffusionEdge_Preprocessor", + "ExecuteAllControlNetPreprocessors", + "FacialPartColoringFromPoseKps", + "FakeScribblePreprocessor", + "HEDPreprocessor", + "HintImageEnchance", + "ImageGenResolutionFromImage", + "ImageGenResolutionFromLatent", + "ImageIntensityDetector", + "ImageLuminanceDetector", + "InpaintPreprocessor", + "LayeredDiffusionApply", + "LayeredDiffusionCondApply", + "LayeredDiffusionCondJointApply", + "LayeredDiffusionDecode", + "LayeredDiffusionDecodeRGBA", + "LayeredDiffusionDecodeSplit", + "LayeredDiffusionDiffApply", + "LayeredDiffusionJointApply", + "LeReS-DepthMapPreprocessor", + "LineArtPreprocessor", + "LineartStandardPreprocessor", + "M-LSDPreprocessor", + "Manga2Anime_LineArt_Preprocessor", + "MaskOptFlow", + "MediaPipe-FaceMeshPreprocessor", + "MeshGraphormer+ImpactDetector-DepthMapPreprocessor", + "MeshGraphormer-DepthMapPreprocessor", + "Metric3D-DepthMapPreprocessor", + "Metric3D-NormalMapPreprocessor", + "Metric_DepthAnythingV2Preprocessor", + "MiDaS-DepthMapPreprocessor", + "MiDaS-NormalMapPreprocessor", + "ModelMergeBlockNumber", + "ModelMergeSDXL", + "ModelMergeSDXLDetailedTransformers", + "ModelMergeSDXLTransformers", + "ModelSamplerTonemapNoiseTest", + "OneFormer-ADE20K-SemSegPreprocessor", + "OneFormer-COCO-SemSegPreprocessor", + "OpenposePreprocessor", + "PiDiNetPreprocessor", + "PixelPerfectResolution", + "PromptExpansion", + "PyraCannyPreprocessor", + "ReferenceOnlySimple", + "RenderAnimalKps", + "RenderPeopleKps", + "RescaleClassifierFreeGuidanceTest", + "SAMPreprocessor", + "SavePoseKpsAsJsonFile", + "ScribblePreprocessor", + "Scribble_PiDiNet_Preprocessor", + "Scribble_XDoG_Preprocessor", + "SemSegPreprocessor", + "ShufflePreprocessor", + "TEEDPreprocessor", + "TTPlanet_TileGF_Preprocessor", + "TTPlanet_TileSimple_Preprocessor", + "TilePreprocessor", + "TonemapNoiseWithRescaleCFG", + "UniFormer-SemSegPreprocessor", + "Unimatch_OptFlowPreprocessor", + "UpperBodyTrackingFromPoseKps", + "Zoe-DepthMapPreprocessor", + "Zoe_DepthAnythingPreprocessor" + ], + { + "author": "tstandley", + "title_aux": "ComfyUI Nodes for Inference.Core" + } + ], + "https://github.com/M4lF3s/comfy-tif-support": [ + [ + "Load TIFF", + "Save TIFF" + ], + { + "title_aux": "comfy-tif-support" + } + ], + "https://github.com/MakkiShizu/ComfyUI-MakkiTools": [ + [ + "AnyImageStitch", + "AnyImagetoConditioning_flux_kontext", + "AutoLoop_create_pseudo_loop_video", + "Environment_INFO", + "GetImageNthCount", + "ImageChannelSeparate", + "ImageCountConcatenate", + "ImageHeigthStitch", + "ImageWidthStitch", + "Image_Resize", + "MergeImageChannels", + "random_any", + "show_type", + "timer", + "translator_m2m100", + "translators" + ], + { + "title_aux": "ComfyUI-MakkiTools" + } + ], + "https://github.com/Malloc-pix/comfyui-QwenVL": [ + [ + "Qwen2.5", + "Qwen2.5VL" + ], + { + "title_aux": "comfyui-QwenVL" + } + ], + "https://github.com/ManuShamil/ComfyUI_BodyEstimation_Nodes": [ + [ + "CogitareLabsPoseIDExtractor" + ], + { + "title_aux": "ComfyUI_BodyEstimation_Nodes" + } + ], + "https://github.com/MarkFreeDom168/ComfyUI-image-load-url": [ + [ + "LoadImageAndMaskFromURLOrBase64", + "LoadImageFromURLOrBase64", + "LoadMaskFromURLOrBase64" + ], + { + "title_aux": "ComfyUI-image-load-url [WIP]" + } + ], + "https://github.com/Matrix-King-Studio/ComfyUI-MoviePy": [ + [ + "AudioDurationNode", + "ImageClipNode", + "SaveVideoNode" + ], + { + "title_aux": "ComfyUI-MoviePy" + } + ], + "https://github.com/Maxim-Dey/ComfyUI-MaksiTools": [ + [ + "\ud83d\udd22 Return Boolean", + "\ud83d\udd22 Return Float", + "\ud83d\udd22 Return Integer", + "\ud83d\udd22 Return Multiline String", + "\ud83d\udd27 Time Measure Node" + ], + { + "title_aux": "ComfyUI-MS_Tools [WIP]" + } + ], + "https://github.com/Mervent/comfyui-telegram-send": [ + [ + "TelegramReply", + "TelegramSend" + ], + { + "title_aux": "comfyui-telegram-send" + } + ], + "https://github.com/Mervent/comfyui-yaml-prompt": [ + [ + "YAMLPromptParser" + ], + { + "title_aux": "comfyui-yaml-prompt" + } + ], + "https://github.com/MickeyJ/ComfyUI_mickster_nodes": [ + [ + "Image Size Scaled", + "ImageSwitchSelect" + ], + { + "title_aux": "ComfyUI_mickster_nodes [WIP]" + } + ], + "https://github.com/MockbaTheBorg/ComfyUI-Mockba": [ + [ + "mb Barcode", + "mb CLIP Text Encoder", + "mb Debug", + "mb Demux", + "mb Empty Latent Image", + "mb Eval", + "mb Exec", + "mb File to Image", + "mb File to Text", + "mb Hash Generator", + "mb Image Batch", + "mb Image Dimensions", + "mb Image Dither", + "mb Image Flip", + "mb Image Load", + "mb Image Load from URL", + "mb Image Preview", + "mb Image Rotate", + "mb Image Size", + "mb Image Subtract", + "mb Image to File", + "mb KSampler", + "mb Select", + "mb String", + "mb Text", + "mb Text or File", + "mb Text to File", + "mb Textbox" + ], + { + "title_aux": "ComfyUI-Mockba" + } + ], + "https://github.com/MrAdamBlack/CheckProgress": [ + [ + "CHECK_PROGRESS" + ], + { + "title_aux": "CheckProgress [WIP]" + } + ], + "https://github.com/MuAIGC/ComfyUI-DMXAPI_mmx": [ + [ + "DMXAPIClient", + "ImageEdit", + "ImageMerge", + "PreviewImageFromUrl", + "TextToImage" + ], + { + "title_aux": "DMXAPI Nodes [WIP]" + } + ], + "https://github.com/MythicalChu/ComfyUI-APG_ImYourCFGNow": [ + [ + "APG_ImYourCFGNow" + ], + { + "title_aux": "ComfyUI-APG_ImYourCFGNow" + } + ], + "https://github.com/NEZHA625/ComfyUI-tools-by-dong": [ + [ + "A1111_FLUX_DATA_NODE", + "AudioDurationNode", + "AudioPathToAudioNode", + "CategorizeNode", + "CountFilesFromFolderNode", + "Data_handle_Node", + "DeepSeek_Node", + "Delay_node", + "Delete_folder_Node", + "DongShowTextNode", + "Dong_Pixelate_Node", + "Dong_Text_Node", + "DownloadNode", + "Downloader", + "FileMoveNode", + "FolderIteratorNODE", + "GetImageListFromFloderNode", + "Get_cookies_Node", + "Get_json_value_Node", + "Get_video_Node", + "HashCalculationsNode", + "HuggingFaceUploadNode", + "IMG2URLNode", + "INTNODE", + "Image2GIFNode", + "ImageDownloader", + "ImageResizeNode", + "LibLib_upload_Node", + "LogicToolsNode", + "LoraIterator", + "Notice_Node", + "PromptConcatNode", + "RandomNumbersNode", + "RenameNode", + "ResolutionNode", + "SaveTXTNode", + "SetAppidNode", + "TextToJsonNode", + "TranslateAPINode", + "Wan21_get_Node", + "Wan21_post_Node", + "ZIPwith7zNode", + "bailian_model_select_Node", + "cogvideox_flash_get_Node", + "cogvideox_flash_post_Node", + "cogview_3_flash_Node", + "doubaoNode", + "douyin_remove_watermark_Node", + "file_analysis_Node", + "find_files_by_extension_Node", + "get_video_from_url_Node", + "img2url_v2_Node", + "img_understanding_Node", + "klingai_video_Node", + "path_join_Node", + "save_img_NODE", + "set_api_Node", + "text_replace_node" + ], + { + "title_aux": "ComfyUI-tools-by-dong [UNSAFE]" + } + ], + "https://github.com/Nambi24/ComfyUI-Save_Image": [ + [ + "ExtractLastPathComponent", + "ListSubfoldersNode", + "SaveImageNode" + ], + { + "title_aux": "ComfyUI-Save_Image" + } + ], + "https://github.com/No-22-Github/ComfyUI_SaveImageCustom": [ + [ + "SaveUtility: SaveImageCustom" + ], + { + "title_aux": "ComfyUI_SaveImageCustom" + } + ], + "https://github.com/Northerner1/ComfyUI_North_Noise": [ + [ + "North_Noise" + ], + { + "title_aux": "ComfyUI_North_Noise [WIP]" + } + ], + "https://github.com/Novavision0313/ComfyUI-NVVS": [ + [ + "AllBlackMaskValidator", + "DirectionSelector", + "FullBodyDetection", + "HighlightIndexSelector", + "MaskCoverageAnalysis", + "StringSplit", + "StringStrip" + ], + { + "title_aux": "ComfyUI-NVVS [WIP]" + } + ], + "https://github.com/OSAnimate/ComfyUI-SpriteSheetMaker": [ + [ + "SpriteSheetMaker" + ], + { + "title_aux": "ComfyUI-SpriteSheetMaker [WIP]" + } + ], + "https://github.com/Oct7/ComfyUI-LaplaMask": [ + [ + "BlurMask" + ], + { + "title_aux": "ComfyUI-LaplaMask" + } + ], + "https://github.com/PATATAJEC/ComfyUI-PatatajecNodes": [ + [ + "ColorMatchFalloff", + "PathTool" + ], + { + "title_aux": "Patatajec-Nodes [WIP]" + } + ], + "https://github.com/Pablerdo/ComfyUI-Sa2VAWrapper": [ + [ + "GetCaptionFromImages" + ], + { + "title_aux": "ComfyUI-Sa2VAWrapper [WIP]" + } + ], + "https://github.com/PabloGrant/comfyui-giraffe-test-panel": [ + [ + "DebugHelloNode", + "GiraffeTestPanel" + ], + { + "title_aux": "comfyui-giraffe-test-panel" + } + ], + "https://github.com/PaleBloodq/ComfyUI-HFTransformers": [ + [ + "HFTCaptioner", + "HFTClassificationSelector", + "HFTClassifier", + "HFTDepthEstimator", + "HFTLoader", + "HFTObjectDetector" + ], + { + "title_aux": "ComfyUI-HFTransformers" + } + ], + "https://github.com/PeterMikhai/Doom_Flux_NodePack": [ + [ + "DoomFluxInpaintSampler", + "DoomFluxLoader", + "DoomFluxSampler", + "DoomFluxSamplerAdvanced" + ], + { + "title_aux": "DoomFLUX Nodes [WIP]" + } + ], + "https://github.com/Poseidon-fan/ComfyUI-fileCleaner": [ + [ + "Clean input and output file" + ], + { + "title_aux": "ComfyUI-fileCleaner [UNSAFE]" + } + ], + "https://github.com/Poukpalaova/ComfyUI-FRED-Nodes": [ + [ + "FRED_AutoCropImage_Native_Ratio_v5", + "FRED_AutoCropImage_SDXL_Ratio_V3", + "FRED_AutoCropImage_SDXL_Ratio_V4", + "FRED_AutoImageTile_from_Mask_v1", + "FRED_CropFace", + "FRED_FolderSelector", + "FRED_ImageBrowser_Dress", + "FRED_ImageBrowser_Eyes_Color", + "FRED_ImageBrowser_Generic", + "FRED_ImageBrowser_Hair_Color", + "FRED_ImageBrowser_Hair_Style", + "FRED_ImageBrowser_Top", + "FRED_JoinImages", + "FRED_LoadImage_V2", + "FRED_LoadImage_V3", + "FRED_LoadImage_V4", + "FRED_LoadImage_V5", + "FRED_LoadImage_V6", + "FRED_LoadPathImagesPreview", + "FRED_LoadPathImagesPreview_v2", + "FRED_LoadRetinaFace", + "FRED_LoraInfos", + "FRED_PreviewOnly", + "FRED_TextMultiline", + "FRED_Text_to_XMP", + "FRED_photo_prompt" + ], + { + "title_aux": "ComfyUI-FRED-Nodes [WIP]" + } + ], + "https://github.com/QingLuanWithoutHeart/comfyui-file-image-utils": [ + [ + "FileManagerV2", + "IfTextEquals", + "LoadImageFromPath" + ], + { + "title_aux": "ComfyUI File/Image Utils Nodes [UNSAFE]" + } + ], + "https://github.com/Quasimondo/ComfyUI-QuasimondoNodes": [ + [ + "CPPN Generator", + "Color Match", + "Coordinates From Mask", + "Custom Shader", + "Distance Map", + "Folder Queue Manager", + "Image Blend by Mask (Batch)", + "Image Noise Generator", + "Image to Optical Flow", + "Perlin Noise Generator", + "Preview Mask", + "Random Image Generator", + "Shift Mask", + "Slit Scan", + "Spring Mesh", + "Temporal Blur", + "Video Queue Manager" + ], + { + "title_aux": "ComfyUI-QuasimondoNodes [WIP]" + } + ], + "https://github.com/QuietNoise/ComfyUI-Queue-Manager": [ + [ + "Workflow Name" + ], + { + "title_aux": "ComfyUI Queue Manager [WIP]" + } + ], + "https://github.com/RLW-Chars/comfyui-promptbymood": [ + [ + "Prompt By Mood" + ], + { + "title_aux": "comfyui-promptbymood [WIP]" + } + ], + "https://github.com/RUFFY-369/ComfyUI-FeatureBank": [ + [ + "FeatureBankAttentionProcessor" + ], + { + "title_aux": "ComfyUI-FeatureBank" + } + ], + "https://github.com/Raidez/comfyui-kuniklo-collection": [ + [ + "ApplySVG2Image", + "Properties" + ], + { + "title_aux": "Kuniklo Collection" + } + ], + "https://github.com/RamonGuthrie/ComfyUI-RBG-LoraConverter": [ + [ + "RBGLoraKeyConverterNode" + ], + { + "title_aux": "ComfyUI-RBG-LoRA-Converter [UNSAFE]" + } + ], + "https://github.com/RicherdLee/comfyui-oss-image-save": [ + [ + "SaveImageOSS" + ], + { + "title_aux": "comfyui-oss-image-save [WIP]" + } + ], + "https://github.com/RobeSantoro/ComfyUI-RobeNodes": [ + [ + "AudioWeights to FadeMask \ud83d\udc24", + "Boolean Primitive \ud83d\udc24", + "Image Input Switch \ud83d\udc24", + "Indices Generator \ud83d\udc24", + "List Image Path \ud83d\udc24", + "List Model Path \ud83d\udc24", + "List Video Path \ud83d\udc24", + "Peaks Weights Generator \ud83d\udc24" + ], + { + "title_aux": "Comfy UI Robe Nodes [UNSAFE]" + } + ], + "https://github.com/Rocky-Lee-001/ComfyUI_SZtools": [ + [ + "RSZImageTaggerSave", + "ShanZhuDepthVAEEncode", + "ShanZhuLoadImagesFromDirList", + "ShanZhuTextJoin", + "ShanZhuTextSaver", + "ShanzhuDepthVAEDecoder", + "ShanzhuLujingTIFSaver", + "ShanzhuTifSaver", + "ShanzhuVAEDecodeSmooth", + "TSZImageTaggerSave" + ], + { + "title_aux": "ComfyUI_SZtools" + } + ], + "https://github.com/RoyKillington/miscomfy-nodes": [ + [ + "VeniceUpscale" + ], + { + "title_aux": "Miscomfy Nodes [WIP]" + } + ], + "https://github.com/SKBv0/ComfyUI-RetroEngine": [ + [ + "RetroEngineNode" + ], + { + "title_aux": "Retro Engine Node for ComfyUI" + } + ], + "https://github.com/SS-snap/ComfyUI-Snap_Processing": [ + [ + "AreaCalculator", + "PyQtCanvasNode", + "Snapload" + ], + { + "title_aux": "Snap Processing for Comfyui" + } + ], + "https://github.com/SS-snap/Comfyui_SSsnap_pose-Remapping": [ + [ + "ApplyPoseScalesToFrames", + "CalculatePoseScales", + "ConvertPoseToStandardFormat", + "RenderKps" + ], + { + "title_aux": "Comfyui_SSsnap_pose-Remapping" + } + ], + "https://github.com/SXQBW/ComfyUI-Qwen3": [ + [ + "QwenVisionParser" + ], + { + "title_aux": "ComfyUI-Qwen-VLM [WIP]" + } + ], + "https://github.com/SadaleNet/ComfyUI-Prompt-To-Prompt": [ + [ + "CLIPTextEncodePromptToPrompt", + "KSamplerPromptToPrompt", + "KSamplerPromptToPromptAttentionMapLogger", + "LocalBlendLayerPresetPromptToPrompt" + ], + { + "title_aux": "ComfyUI Port for Google's Prompt-to-Prompt" + } + ], + "https://github.com/Sai-ComfyUI/ComfyUI-MS-Nodes": [ + [ + "FloatMath", + "MS_Boolean", + "MS_Float", + "MS_GenerateSeed", + "MS_NP_Vector3", + "PowerFractalCrossHatchNode", + "PowerFractalNoiseNode", + "VectorMath" + ], + { + "title_aux": "ComfyUI-MS-Nodes [WIP]" + } + ], + "https://github.com/Sakura-nee/ComfyUI_Save2Discord": [ + [ + "SendToWebhook" + ], + { + "title_aux": "ComfyUI_Save2Discord" + } + ], + "https://github.com/SanDiegoDude/ComfyUI-HiDream-Sampler": [ + [ + "HiDreamImg2Img", + "HiDreamResolutionSelect", + "HiDreamSampler", + "HiDreamSamplerAdvanced" + ], + { + "title_aux": "HiDreamSampler for ComfyUI [WIP]" + } + ], + "https://github.com/SaulQcy/comfy_saul_plugin": [ + [ + "Blend Images", + "Compute Keypoints Similarity", + "Cutting Video", + "End Node", + "Extract .webp from Folder", + "Extract the First Frame", + "Find the most similar webp", + "Fuse People and Cigarette", + "Get Pose", + "Patch Pose to People", + "Smoking Auto Label" + ], + { + "title_aux": "comfyui-saul-plugin [WIP]" + } + ], + "https://github.com/Scaryplasmon/ComfTrellis": [ + [ + "LoadTrellisModel", + "RembgSquare", + "SaveGLBFile", + "TrellisGrid", + "TrellisInference" + ], + { + "title_aux": "ComfTrellis [WIP]" + } + ], + "https://github.com/SeedV/ComfyUI-SeedV-Nodes": [ + [ + "ALL_Model_UnLoader(SEEDV)", + "AdvancedScript", + "CheckpointLoaderSimpleShared //SeedV", + "ControlNetLoaderAdvancedShared", + "LoraLoader //SeedV", + "Script", + "Switch_Any(SEEDV)", + "TCD_Sampler(SEEDV)", + "nunchakuLoraAdapter(SEEDV)" + ], + { + "title_aux": "ComfyUI-SeedV-Nodes [UNSAFE]" + } + ], + "https://github.com/Sephrael/comfyui_caption-around-image": [ + [ + "CaptionAroundImageSmart", + "PrintPromptValues", + "PromptProvider", + "TextboxReferenceNode" + ], + { + "title_aux": "comfyui_caption-around-image" + } + ], + "https://github.com/ShahFaisalWani/ComfyUI-Mojen-Nodeset": [ + [ + "MojenAnalyzeProcessor", + "MojenAspectRatio", + "MojenImageLoader", + "MojenNSFWClassifier", + "MojenNSFWClassifierSave", + "MojenStringLength", + "MojenStyleExtractor", + "MojenTagProcessor", + "MojenTransparentBg" + ], + { + "title_aux": "ComfyUI-Mojen-Nodeset" + } + ], + "https://github.com/Shinsplat/ComfyUI-Shinsplat": [ + [ + "Clip Text Encode (Shinsplat)", + "Clip Text Encode ALT (Shinsplat)", + "Clip Text Encode SD3 (Shinsplat)", + "Clip Text Encode SDXL (Shinsplat)", + "Clip Text Encode T5 (Shinsplat)", + "Clip Tokens Encode (Shinsplat)", + "Green Box (Shinsplat)", + "Hex To Other (Shinsplat)", + "KSampler (Shinsplat)", + "Lora Loader (Shinsplat)", + "Nupoma (Shinsplat)", + "Seed (Shinsplat)", + "Shinsplat_CLIPTextEncodeFlux", + "String Interpolated (Shinsplat)", + "Sum Wrap (Shinsplat)", + "Tensor Toys (Shinsplat)", + "Test Node (Shinsplat)", + "Text To Tokens (Shinsplat)", + "Text To Tokens SD3 (Shinsplat)", + "Variables (Shinsplat)" + ], + { + "author": "Shinsplat", + "description": "", + "nickname": "shinsplat", + "title": "Shinsplat", + "title_aux": "ComfyUI-Shinsplat [UNSAFE]" + } + ], + "https://github.com/ShmuelRonen/ComfyUI-FreeMemory": [ + [ + "FreeMemoryCLIP", + "FreeMemoryImage", + "FreeMemoryLatent", + "FreeMemoryModel", + "FreeMemoryString" + ], + { + "title_aux": "ComfyUI-FreeMemory" + } + ], + "https://github.com/Simlym/comfyui-prompt-helper": [ + [ + "PromptProcessor" + ], + { + "title_aux": "Simlym/comfyui-prompt-helper [WIP]" + } + ], + "https://github.com/Slix-M-Lestragg/comfyui-enhanced": [ + [ + "Range Iterator" + ], + { + "title_aux": "comfyui-enhanced [WIP]" + } + ], + "https://github.com/SoftMeng/ComfyUI-PIL": [ + [ + "PIL Effects (Mexx)", + "PIL Merge Image (Mexx)", + "PIL Remove Black Dots (Mexx)", + "PIL TITLE (Mexx)" + ], + { + "title_aux": "ComfyUI-PIL" + } + ], + "https://github.com/Solankimayursinh/PMSnodes": [ + [ + "InputAnalyzer", + "LoadBase64Audio", + "LoadImageBase64", + "LoadMaskBase64", + "PMSLoadText", + "PMSSendAudio", + "PMSSendImage" + ], + { + "title_aux": "PMSnodes [WIP]" + } + ], + "https://github.com/Soliton80/ComfyUI-Watermark-Detection-YOLO": [ + [ + "WatermarkDetector", + "WatermarkDetectorLoader" + ], + { + "title_aux": "Watermark Detection YOLO Custom Node [WIP]" + } + ], + "https://github.com/Sophylax/ComfyUI-ReferenceMerge": [ + [ + "InpaintRegionRestitcher", + "ReferenceInpaintComposite" + ], + { + "title_aux": "ComfyUI-ReferenceMerge" + } + ], + "https://github.com/Soppatorsk/comfyui_img_to_ascii": [ + [ + "Img_to_ASCII" + ], + { + "title_aux": "comfyui_img_to_ascii [WIP]" + } + ], + "https://github.com/SpaceWarpStudio/ComfyUI_Remaker_FaceSwap": [ + [ + "RemakerFaceSwap" + ], + { + "title_aux": "ComfyUI_Remaker_FaceSwap" + } + ], + "https://github.com/Stable-X/ComfyUI-Hi3DGen": [ + [ + "DifferenceExtractorNode", + "DownloadAndLoadStableXModel", + "IF_TrellisCheckpointLoader", + "IF_TrellisImageTo3D", + "StableXProcessImage" + ], + { + "title_aux": "ComfyUI-Hi3DGen" + } + ], + "https://github.com/StableDiffusionVN/SDVN_Comfy_node": [ + [ + "SDVM Image List Repeat", + "SDVN API chatbot", + "SDVN Any From List", + "SDVN Any Input Type", + "SDVN Any List", + "SDVN Any Repeat", + "SDVN Any Show", + "SDVN AnyDownload List", + "SDVN Apply Kontext Reference", + "SDVN Apply Style Model", + "SDVN Auto Generate", + "SDVN AutoSwitch", + "SDVN Boolean", + "SDVN CLIP Download", + "SDVN CLIP Text Encode", + "SDVN CLIPVision Download", + "SDVN Checkpoint Download", + "SDVN Checkpoint Download List", + "SDVN ControlNet Download", + "SDVN Controlnet Apply", + "SDVN Crop By Ratio", + "SDVN DALL-E Generate Image", + "SDVN Dall-E Generate Image 2", + "SDVN Dic Convert", + "SDVN DualCLIP Download", + "SDVN Easy IPAdapter weight", + "SDVN Empty Latent Ratio", + "SDVN Exif check", + "SDVN Fill Background", + "SDVN Filter List", + "SDVN Flip Image", + "SDVN GPT Image", + "SDVN Gemini Flash 2 Image", + "SDVN Get Mask Size", + "SDVN Google Imagen", + "SDVN IC Lora Layout", + "SDVN IC Lora Layout Crop", + "SDVN IC-Light v2", + "SDVN IPAdapterModel Download", + "SDVN Image Adjust", + "SDVN Image Film Grain", + "SDVN Image HSL", + "SDVN Image Info", + "SDVN Image Layout", + "SDVN Image Repeat", + "SDVN Image Scraper", + "SDVN Image Size", + "SDVN Image White Balance", + "SDVN Inpaint", + "SDVN Inpaint Crop", + "SDVN InstantIDModel Download", + "SDVN Join Parameter", + "SDVN Joy Caption", + "SDVN KSampler", + "SDVN Load Checkpoint", + "SDVN Load Google Sheet", + "SDVN Load Image", + "SDVN Load Image Folder", + "SDVN Load Image From List", + "SDVN Load Image Ultimate", + "SDVN Load Image Url", + "SDVN Load Lora", + "SDVN Load Model", + "SDVN Load Text", + "SDVN LoadPinterest", + "SDVN Logic", + "SDVN Loop Inpaint Stitch", + "SDVN Lora Download", + "SDVN Lora info", + "SDVN Mask Regions", + "SDVN Mask To Transparent Color", + "SDVN Menu Option", + "SDVN Merge Flux", + "SDVN Merge SD1", + "SDVN Merge SDXL", + "SDVN Metadata Check", + "SDVN Model Merge", + "SDVN Model info editor", + "SDVN Overlay Images", + "SDVN Overlay Mask Color Image", + "SDVN Pipe In", + "SDVN Pipe Out", + "SDVN Pipe Out All", + "SDVN QuadrupleCLIP Download", + "SDVN Quick Menu", + "SDVN RGBA to RGB", + "SDVN Run Python Code", + "SDVN Run Test", + "SDVN Save Text", + "SDVN Seed", + "SDVN Simple Any Input", + "SDVN Slider 1", + "SDVN Slider 100", + "SDVN StyleModel Download", + "SDVN Styles", + "SDVN Switch", + "SDVN Translate", + "SDVN UNET Download", + "SDVN UPscale Latent", + "SDVN Upscale Image", + "SDVN UpscaleModel Download", + "SDVN VAE Download", + "SDVN Yolo8 Seg" + ], + { + "title_aux": "SDVN Comfy node [UNSAFE]" + } + ], + "https://github.com/StaffsGull/comfyui_scene_builder": [ + [ + "CharacterBuilderNode", + "ClothingItemNode", + "ClothingMergerNode", + "EnvironmentBuilderNode", + "MergeCharactersNode", + "PhotoStyleBuilderNode", + "SceneCombinerNode" + ], + { + "title_aux": "comfyui_scene_builder [WIP]" + } + ], + "https://github.com/StartHua/Comfyui_CSDMT_CXH": [ + [ + "CSD" + ], + { + "title_aux": "Comfyui_CXH_CRM" + } + ], + "https://github.com/StartHua/Comfyui_CXH_CRM": [ + [ + "CRM" + ], + { + "title_aux": "Comfyui_CXH_CRM" + } + ], + "https://github.com/StartHua/Comfyui_CXH_joy_caption": [ + [ + "CXH_DownloadAndLoadFlorence2Model", + "CXH_Florence2Run", + "CXH_HG_Model_Load", + "CXH_IC_Lora_Florence2Run", + "CXH_IC_lora_reversal", + "CXH_Ic_lora_Joy_batch", + "CXH_Min2_6_classifiy", + "CXH_Min2_6_prompt_Run", + "CXH_MinCP3_4B_Chat", + "CXH_MinCP3_4B_Load", + "CXH_SmolVlm_Load", + "CXH_SmolVlm_Run", + "Joy_caption", + "Joy_caption_alpha_batch", + "Joy_caption_alpha_batch_Dirs", + "Joy_caption_alpha_load", + "Joy_caption_alpha_prompt", + "Joy_caption_alpha_run", + "Joy_caption_load" + ], + { + "title_aux": "Comfyui_CXH_joy_caption [SECURITY SCREENING]" + } + ], + "https://github.com/StartHua/Comfyui_Flux_Style_Ctr": [ + [ + "CXH_StyleModelApply" + ], + { + "title_aux": "Comfyui_Flux_Style_Ctr [WIP]" + } + ], + "https://github.com/StartHua/Comfyui_leffa": [ + [ + "CXH_Leffa_Viton_Load", + "CXH_Leffa_Viton_Run" + ], + { + "title_aux": "Comfyui_leffa" + } + ], + "https://github.com/StoryWalker/comfyui_flux_collection_advanced": [ + [ + "Example", + "FluxControlNetApply", + "FluxControlNetApplyPreview", + "FluxControlNetLoader", + "FluxImagePreview", + "FluxImageUpscaler", + "FluxLoader", + "FluxSamplerParameters", + "FluxTextPrompt" + ], + { + "title_aux": "comfyui_flux_collection_advanced [WIP]" + } + ], + "https://github.com/Symbiomatrix/Comfyui-Sort-Files": [ + [ + "ImageSaverSBM", + "SortControlSBM", + "StringToFloatSBM", + "VideoSeriesMergerSBM" + ], + { + "author": "SBM", + "title_aux": "Comfyui-Sort-Files" + } + ], + "https://github.com/TSFSean/ComfyUI-TSFNodes": [ + [ + "GyroOSC" + ], + { + "title_aux": "ComfyUI-TSFNodes" + } + ], + "https://github.com/Tawbaware/ComfyUI-Tawbaware": [ + [ + "Example", + "LatentBlendGradient", + "ReverseLatentBatch", + "WanVideoReCamMasterGenerateOrbitCameraEx" + ], + { + "title_aux": "ComfyUI-Tawbaware [WIP]" + } + ], + "https://github.com/Temult/TWanSigmaSampler": [ + [ + "TWanVideoSigmaSampler" + ], + { + "title_aux": "TWanVideoSigmaSampler: EXPERIMENTAL [WIP]" + } + ], + "https://github.com/ThatGlennD/ComfyUI-Image-Analysis-Tools": [ + [ + "Blur Detection", + "Clipping Analysis", + "Color Cast Detector", + "Color Harmony Analyzer", + "Color Temperature Estimator", + "ColorTemperatureEstimator", + "Contrast Analysis", + "ContrastAnalysis", + "Defocus Analysis", + "Edge Density Analysis", + "Entropy Analysis", + "Noise Estimation", + "RGB Histogram Renderer", + "Sharpness / Focus Score" + ], + { + "title_aux": "ComfyUI Image Analysis Toolkit [WIP]" + } + ], + "https://github.com/TheJorseman/IntrinsicCompositingClean-ComfyUI": [ + [ + "AlbedoHarmonizer", + "AlbedoModelLoader", + "CompleteRelighting", + "CompositeNormalsCalculator", + "DepthEstimator", + "DepthModelLoader", + "ExtractSmallBgShd", + "HarmonizedImageCreator", + "ImageResizer", + "ImageResizerNP", + "ImageResizerNPMASK", + "IntrinsicDecomposer", + "IntrinsicModelLoader", + "LightCoeffExtractor", + "LoadImagePIL", + "MaskApplier", + "MaskGenerator", + "NormalsExtractor", + "NormalsModelLoader", + "ReshadingModelLoader", + "ReshadingProcessor" + ], + { + "title_aux": "IntrinsicCompositingClean-ComfyUI" + } + ], + "https://github.com/ThisModernDay/ComfyUI-InstructorOllama": [ + [ + "OllamaInstructorNode" + ], + { + "title_aux": "ComfyUI Instructor Ollama" + } + ], + "https://github.com/TinyBeeman/ComfyUI-TinyBee": [ + [ + "Get File List", + "Incrementer", + "Indexed Entry", + "List Count", + "Process Path Name", + "Random Entry", + "Randomize List" + ], + { + "title_aux": "ComfyUI-TinyBee" + } + ], + "https://github.com/Tr1dae/ComfyUI-CustomNodes-MVM": [ + [ + "LoadImageFromFolderMVM", + "LoadImagesFromFolderRandomMVM", + "LoadImagesFromRelativePathRandomMVM", + "LoadTextFromFolderMVM" + ], + { + "title_aux": "ComfyUI-CustomNodes-MVM" + } + ], + "https://github.com/UmutGuzel/tryvariantai-comfyui": [ + [ + "DebugMaskNode", + "FillTransparencyNode", + "MaskExpandBorder", + "MaskExpandBorderAdvanced", + "MaskFromContoursOpenCV", + "MaskToTransparentNode", + "SimpleWhiteDetectorNode", + "WhiteToTransparentNode" + ], + { + "title_aux": "ComfyUI-yolov5-face [WIP]" + } + ], + "https://github.com/Velour-Fog/comfy-latent-nodes": [ + [ + "CustomLoadLatent", + "CustomSaveLatent" + ], + { + "title_aux": "comfy-latent-nodes [UNSAFE]" + } + ], + "https://github.com/VictorLopes643/ComfyUI-Video-Dataset-Tools": [ + [ + "VideoFrameExtractor", + "VideoFrameSaver" + ], + { + "title_aux": "ComfyUI-Video-Dataset-Tools [WIP]" + } + ], + "https://github.com/Video3DGenResearch/comfyui-batch-input-node": [ + [ + "BatchImageAndPrompt", + "BatchInputCSV", + "BatchInputText" + ], + { + "title_aux": "ComfyUI Batch Input Node" + } + ], + "https://github.com/VisionExp/ve_custom_comfyui_nodes": [ + [ + "LoadImgFromInputUrl", + "assets/Asset Image", + "render3d/Render Node" + ], + { + "title_aux": "ve_custom_comfyui_nodes" + } + ], + "https://github.com/Vkabuto23/comfyui_openrouter_ollama": [ + [ + "OllamaNode", + "OllamaNodeExperimental", + "OllamaVisionNode", + "OllamaVisionNodeExperimental", + "OpenRouterNode", + "OpenRouterNodeExperimental", + "OpenRouterVisionNode", + "OpenRouterVisionNodeExperimental" + ], + { + "title_aux": "ComfyUI Custom Nodes: OpenRouter & Ollama [UNSAFE]" + } + ], + "https://github.com/WASasquatch/ASTERR": [ + [ + "ASTERR", + "SaveASTERR" + ], + { + "title_aux": "ASTERR [UNSAFE]" + } + ], + "https://github.com/WSJUSA/Comfyui-StableSR": [ + [ + "ColorFix", + "StableSRUpscalerPipe" + ], + { + "author": "WSJUSA", + "description": "This module enables StableSR in Comgfyui. Ported work of sd-webui-stablesr. Original work for Auotmaatic1111 version of this module and StableSR credit to LIightChaser and Jianyi Wang.", + "nickname": "StableSR", + "title": "StableSR", + "title_aux": "pre-comfyui-stablsr" + } + ], + "https://github.com/WaiyanLing/ComfyUI-Tracking": [ + [ + "WorkflowStats" + ], + { + "title_aux": "ComfyUI-Tracking [WIP]" + } + ], + "https://github.com/WilliamStanford/ComfyUI-VisualLabs": [ + [ + "CreateFadeMaskAdvancedVL", + "PointStringFromFloatArray", + "RescaleFloatArray", + "StringFromFloatArray" + ], + { + "title_aux": "visuallabs_comfyui_nodes" + } + ], + "https://github.com/WozStudios/ComfyUI-WozNodes": [ + [ + "CreateImageBatch", + "ImageBatchSelectByMask", + "ImageBatchTrim", + "ImageBatcherByIndexProV2" + ], + { + "title_aux": "ComfyUI-WozNodes" + } + ], + "https://github.com/XiaoHeiziGGG/ComfyUI-Gemini-Kontext": [ + [ + "GeminiBatchTranslator", + "GeminiImageAnalyzer", + "GeminiKontextOptimizer", + "GeminiTranslator" + ], + { + "title_aux": "ComfyUI-Gemini-Kontext [WIP]" + } + ], + "https://github.com/XiaoHeiziGGG/ComfyUI-GeminiTranslator": [ + [ + "GeminiBatchTranslator", + "GeminiTranslator" + ], + { + "title_aux": "ComfyUI Gemini Translator [WIP]" + } + ], + "https://github.com/Yeonri/ComfyUI_LLM_Are_You_Listening": [ + [ + "AYL_API_Node", + "AYL_GGUF_Node", + "AYL_Node" + ], + { + "title_aux": "ComfyUI_LLM_Are_You_Listening [WIP]" + } + ], + "https://github.com/Yukinoshita-Yukinoe/ComfyUI-KontextOfficialNode": [ + [ + "KontextImageEditingOfficialAPI_Max", + "KontextTextToImageOfficialAPI_Max" + ], + { + "title_aux": "ComfyUI-KontextOfficialNode" + } + ], + "https://github.com/ZHO-ZHO-ZHO/ComfyUI-AuraSR-ZHO": [ + [ + "AuraSR_Lterative_Zho", + "AuraSR_ModelLoader_Zho", + "AuraSR_Zho" + ], + { + "title_aux": "ComfyUI-AuraSR-ZHO" + } + ], + "https://github.com/ZHO-ZHO-ZHO/ComfyUI-BiRefNet-ZHO": [ + [ + "BiRefNet_ModelLoader_Zho", + "BiRefNet_Zho" + ], + { + "title_aux": "ComfyUI-BiRefNet-ZHO [BROKEN]" + } + ], + "https://github.com/ZHO-ZHO-ZHO/ComfyUI-Gemini": [ + [ + "ConcatText_Zho", + "DisplayText_Zho", + "Gemini_15P_API_S_Advance_Zho", + "Gemini_15P_API_S_Chat_Advance_Zho", + "Gemini_API_Chat_Zho", + "Gemini_API_S_Chat_Zho", + "Gemini_API_S_Vsion_ImgURL_Zho", + "Gemini_API_S_Zho", + "Gemini_API_Vsion_ImgURL_Zho", + "Gemini_API_Zho", + "Gemini_FileUpload_API_S_Zho", + "Gemini_File_API_S_Zho" + ], + { + "title_aux": "ComfyUI-Gemini [NAME CONFLICT]" + } + ], + "https://github.com/ZHO-ZHO-ZHO/ComfyUI-PuLID-ZHO": [ + [ + "PuLID_Zho" + ], + { + "title_aux": "ComfyUI-PuLID-ZHO [WIP]" + } + ], + "https://github.com/ZHO-ZHO-ZHO/ComfyUI-Wan-ZHO": [ + [ + "WanT2V_Generation_Zho", + "WanT2V_ModelLoader_Zho" + ], + { + "title_aux": "ComfyUI Wan2.1 [WIP]" + } + ], + "https://github.com/ZenAI-Vietnam/ComfyUI-gemini-IG": [ + [ + "Gemini Image Generation", + "Gemini Text Generation" + ], + { + "title_aux": "ComfyUI-gemini-IG" + } + ], + "https://github.com/ZenAI-Vietnam/ComfyUI_InfiniteYou": [ + [ + "FaceCombine", + "FaceSwap_InfiniteYou", + "InfiniteYouApply" + ], + { + "title_aux": "ComfyUI_InfiniteYou [NAME CONFLICT]" + } + ], + "https://github.com/a-One-Fan/ComfyUI-Blenderesque-Nodes": [ + [ + "BlenderAlphaConvert", + "BlenderAlphaOver", + "BlenderBlackbody", + "BlenderBokehImage", + "BlenderBrickTexture", + "BlenderBrightnessContrast", + "BlenderCheckerTexture", + "BlenderClamp", + "BlenderCombineColor", + "BlenderCombineXYZ", + "BlenderConvertColorspace", + "BlenderCornerPin", + "BlenderCrop", + "BlenderDisplace", + "BlenderExposure", + "BlenderFlip", + "BlenderGaborTexture", + "BlenderGamma", + "BlenderGradientTexture", + "BlenderHueSaturationValue", + "BlenderInvertColor", + "BlenderLensDistortion", + "BlenderMagicTexture", + "BlenderMapRange", + "BlenderMapUV", + "BlenderMath", + "BlenderMix", + "BlenderMovieDistortion", + "BlenderNoiseTexture", + "BlenderRGB", + "BlenderRGBtoBW", + "BlenderRotate", + "BlenderScale", + "BlenderSeparateColor", + "BlenderSeparateXYZ", + "BlenderSetAlpha", + "BlenderTonemap", + "BlenderTransform", + "BlenderTranslate", + "BlenderUV", + "BlenderValue", + "BlenderVectorMath", + "BlenderVoronoiTexture", + "BlenderWaveTexture", + "BlenderWavelength", + "BlenderWhiteNoiseTexture", + "BlenderZCombine" + ], + { + "title_aux": "ComfyUI-Blenderesque-Nodes [WIP]" + } + ], + "https://github.com/a-und-b/ComfyUI_Output_as_Input": [ + [ + "OutputAsInput" + ], + { + "title_aux": "ComfyUI_Output_as_Input" + } + ], + "https://github.com/aa-parky/pipemind-comfyui": [ + [ + "BatchImageLoadInput", + "BatchImageLoadOutput", + "BooleanSwitchAny", + "EnhancedKeywordPromptComposer", + "KeywordPromptComposer", + "LoadTxtFile", + "PipemindDisplayAny", + "PipemindFlux2MAspectRatio", + "PipemindLoraLoader", + "PipemindMultilineTextInput", + "PipemindSDXL15AspectRatio", + "PipemindSaveImageWTxt", + "PipemindShowText", + "PipemindShowTextFind", + "PipemindTokenCounter", + "RandomLineFromDropdown", + "SelectLineFromDropdown", + "SimplePromptCombiner" + ], + { + "title_aux": "pipemind-comfyui" + } + ], + "https://github.com/abuzreq/ComfyUI-Model-Bending": [ + [ + "Add Noise Module (Bending)", + "Add Scalar Module (Bending)", + "Compute PCA", + "ConditioningApplyOperation", + "Custom Code Module", + "Dilation Module (Bending)", + "Erosion Module (Bending)", + "Gradient Module (Bending)", + "HSpace Bending", + "Latent Operation (Add Noise)", + "Latent Operation (Add Scalar)", + "Latent Operation (Custom)", + "Latent Operation (Multiply Scalar)", + "Latent Operation (Rotate)", + "Latent Operation (Threshold)", + "Latent Operation To Module", + "LatentApplyOperationCFGToStep", + "LoRA Bending", + "Model Bending", + "Model Bending (SD Layers)", + "Model Inspector", + "Model VAE Bending", + "Model VAE Inspector", + "Multiply Scalar Module (Bending)", + "NoiseVariations", + "Rotate Module (Bending)", + "Scale Module (Bending)", + "Sobel Module (Bending)", + "Threshold Module (Bending)", + "Visualize Feature Map" + ], + { + "title_aux": "ComfyUI Model Bending [UNSAFE]" + } + ], + "https://github.com/ahmedbana/File-Rename": [ + [ + "AdvancedFileRenameNode", + "FileRenameNode" + ], + { + "title_aux": "File-Rename [UNSAFE]" + } + ], + "https://github.com/ahmedbana/json-creator": [ + [ + "JsonCreator" + ], + { + "title_aux": "json-creator [WIP]" + } + ], + "https://github.com/ahmedbana/upload-to-azure": [ + [ + "AzureBlobUploader" + ], + { + "title_aux": "upload-to-azure" + } + ], + "https://github.com/aiden1020/ComfyUI_Artcoder": [ + [ + "ArtCoder" + ], + { + "title_aux": "ComfyUI_Artcoder [WIP]" + } + ], + "https://github.com/ainanoha/etm_comfyui_nodes": [ + [ + "ETM_LoadImageFromLocal", + "ETM_SaveImage" + ], + { + "title_aux": "etm_comfyui_nodes" + } + ], + "https://github.com/akatz-ai/ComfyUI-Execution-Inversion": [ + [ + "AccumulateNode", + "AccumulationGetItemNode", + "AccumulationGetLengthNode", + "AccumulationHeadNode", + "AccumulationSetItemNode", + "AccumulationTailNode", + "AccumulationToListNode", + "DebugPrint", + "ExecutionBlocker", + "ForLoopClose", + "ForLoopOpen", + "GetFloatFromList", + "GetIntFromList", + "IntegerListGeneratorNode", + "LazyConditional", + "LazyIndexSwitch", + "LazyMixImages", + "LazySwitch", + "ListToAccumulationNode", + "MakeListNode", + "WhileLoopClose", + "WhileLoopOpen", + "_ForLoopCounter" + ], + { + "title_aux": "ComfyUI-Execution-Inversion" + } + ], + "https://github.com/aklevecz/ComfyUI-AutoPrompt": [ + [ + "OllamaChat", + "OllamaModelLister", + "OllamaPromptGenerator", + "TextDisplay" + ], + { + "title_aux": "ComfyUI-AutoPrompt [WIP]" + } + ], + "https://github.com/alexgenovese/ComfyUI-Diffusion-4k": [ + [ + "FluxImageGenerator" + ], + { + "title_aux": "ComfyUI-Diffusion-4k [WIP]" + } + ], + "https://github.com/alexgenovese/ComfyUI-Reica": [ + [ + "ReicaGCPReadImageNode", + "ReicaGCPWriteImageNode", + "ReicaHTTPCustomRequest", + "ReicaHTTPNotification", + "ReicaInsertAnythingNode", + "ReicaLoadLoopImagesFromURLs", + "ReicaLoadLoopImagesFromURLsSkipErrors", + "ReicaSmartResizer", + "ReicaTextImageDisplay", + "ReicaTryOffDiffGenerator", + "ReicaTryOffDiffLoader", + "ReicaURLImageLoader" + ], + { + "title_aux": "ComfyUI-Reica" + } + ], + "https://github.com/alexisrolland/ComfyUI-AuraSR": [ + [ + "LoadAuraSR", + "RunAuraSR" + ], + { + "title_aux": "alexisrolland/ComfyUI-AuraSR" + } + ], + "https://github.com/alt-key-project/comfyui-dream-painter": [ + [ + "Bitmap AND [DPaint]", + "Bitmap Crop Center [DPaint]", + "Bitmap Dimensions [DPaint]", + "Bitmap Edge Detect [DPaint]", + "Bitmap Expand Canvas [DPaint]", + "Bitmap Invert [DPaint]", + "Bitmap OR [DPaint]", + "Bitmap Resize [DPaint]", + "Bitmap Rotate [DPaint]", + "Bitmap To Image & Mask [DPaint]", + "Bitmap XOR [DPaint]", + "Draw Shape As Bitmap [DPaint]", + "Image To Bitmap [DPaint]", + "Random Number Generator [DPaint]", + "Shape Center & Fit [DPaint]", + "Shape Combiner [DPaint]", + "Shape Copycat Tool [DPaint]", + "Shape Find Bounds [DPaint]", + "Shape Flip [DPaint]", + "Shape Grid [DPaint]", + "Shape Resize [DPaint]", + "Shape Rotate [DPaint]", + "Shape of Circular Rays [DPaint]", + "Shape of N-Polygon [DPaint]", + "Shape of Rectangle [DPaint]", + "Shape of Star [DPaint]" + ], + { + "title_aux": "Dream Painter [WIP]" + } + ], + "https://github.com/alt-key-project/comfyui-dream-video-batches": [ + [ + "Blended Transition [DVB]", + "Calculation [DVB]", + "Create Frame Set [DVB]", + "Divide [DVB]", + "Fade From Black [DVB]", + "Fade To Black [DVB]", + "Float Input [DVB]", + "For Each Done [DVB]", + "For Each Filename [DVB]", + "Frame Set Append [DVB]", + "Frame Set Frame Dimensions Scaled [DVB]", + "Frame Set Index Offset [DVB]", + "Frame Set Merger [DVB]", + "Frame Set Reindex [DVB]", + "Frame Set Repeat [DVB]", + "Frame Set Reverse [DVB]", + "Frame Set Split Beginning [DVB]", + "Frame Set Split End [DVB]", + "Frame Set Splitter [DVB]", + "Generate Inbetween Frames [DVB]", + "Int Input [DVB]", + "Linear Camera Pan [DVB]", + "Linear Camera Roll [DVB]", + "Linear Camera Zoom [DVB]", + "Load Image From Path [DVB]", + "Multiply [DVB]", + "Sine Camera Pan [DVB]", + "Sine Camera Roll [DVB]", + "Sine Camera Zoom [DVB]", + "String Input [DVB]", + "Text Input [DVB]", + "Trace Memory Allocation [DVB]", + "Unwrap Frame Set [DVB]" + ], + { + "title_aux": "Dream Project Video Batches [WIP]" + } + ], + "https://github.com/amamisonlyuser/MixvtonComfyui": [ + [ + "CXH_Leffa_Viton_Load", + "CXH_Leffa_Viton_Run" + ], + { + "title_aux": "MixvtonComfyui [WIP]" + } + ], + "https://github.com/ammahmoudi/ComfyUI-Legendary-Nodes": [ + [ + "Legendary Dataset Saver", + "Legendary Image URL Loader", + "Legendary Lora URL Loader" + ], + { + "title_aux": "ComfyUI-Legendary-Nodes" + } + ], + "https://github.com/animEEEmpire/ComfyUI-Animemory-Loader": [ + [ + "AnimemoryNode" + ], + { + "title_aux": "ComfyUI-Animemory-Loader" + } + ], + "https://github.com/apetitbois/nova_utils": [ + [ + "floatList2Float", + "jsonParser" + ], + { + "title_aux": "nova_utils" + } + ], + "https://github.com/aria1th/ComfyUI-SkipCFGSigmas": [ + [ + "CFGControl_SKIPCFG" + ], + { + "title_aux": "ComfyUI-SkipCFGSigmas" + } + ], + "https://github.com/aria1th/ComfyUI-camietagger-onnx": [ + [ + "CamieTagger" + ], + { + "title_aux": "ComfyUI-camietagger-onnx" + } + ], + "https://github.com/artem-konevskikh/comfyui-split-merge-video": [ + [ + "VideoMerger", + "VideoSplitter" + ], + { + "title_aux": "ComfyUI Video Processing Nodes [WIP]" + } + ], + "https://github.com/artifyfun/ComfyUI-JS": [ + [ + "JavascriptExecutor", + "JavascriptExecutorMultiOutput" + ], + { + "title_aux": "ComfyUI-JS [UNSAFE]" + } + ], + "https://github.com/artisanalcomputing/ComfyUI-Custom-Nodes": [ + [ + "RandomVideoMixer", + "SpotifyCanvasGenerator", + "VideoWriter" + ], + { + "title_aux": "artcpu-custom-nodes" + } + ], + "https://github.com/ashishsaini/comfyui-segment-clothing-sleeves": [ + [ + "segformer_b2_sleeves" + ], + { + "title_aux": "comfyui_segformer_b2_sleeves" + } + ], + "https://github.com/ashllay/ComfyUI_MoreComfy": [ + [ + "MC Alter Seed", + "MC Get Image Min Max", + "MC Get Image Size", + "MC Multi Concat", + "MC Multi Concat(Advanced)", + "MC Noise", + "MC Set Tile Size", + "MC Switch Image", + "MC Switch Latent", + "MC Switch Model", + "MC Switch Seed", + "MC Switch String" + ], + { + "title_aux": "ComfyUI_MoreComfy" + } + ], + "https://github.com/avocadori/ComfyUI-AudioAmplitudeConverter": [ + [ + "NormalizeAmpToFloatNode" + ], + { + "title_aux": "ComfyUI Audio Amplitude Converter [WIP]" + } + ], + "https://github.com/ayaoayaoayaoaya/ComfyUI-KLUT-DeepSeek-API": [ + [ + "KLUTDeepSeekAPI" + ], + { + "title_aux": "ComfyUI-KLUT-DeepSeek-API [WIP]" + } + ], + "https://github.com/babydjac/comfyui-grok-ponyxl": [ + [ + "GrokPonyXLPrompter" + ], + { + "title_aux": "comfyui-grok-ponyxl [WIP]" + } + ], + "https://github.com/backearth1/Comfyui-MiniMax-Video": [ + [ + "ImageToPrompt", + "MiniMaxAIAPIClient", + "MiniMaxImage2Video", + "MiniMaxImageGenerator", + "MiniMaxPreviewVideo" + ], + { + "title_aux": "Comfyui-MiniMax-Video [WIP]" + } + ], + "https://github.com/baicai99/ComfyUI-FrameSkipping": [ + [ + "FrameSelector", + "FrameSkipping", + "FrameTruncating", + "IntOperationsNode", + "MaskFrameSkipping", + "MaskGenerator", + "MaskSelector" + ], + { + "title_aux": "ComfyUI-FrameSkipping" + } + ], + "https://github.com/bananasss00/Comfyui-PyExec": [ + [ + "PyExec", + "PyExec_Output", + "PyExec_OutputIsList", + "PyExec_OutputIsValue" + ], + { + "author": "SeniorPioner", + "description": "Comfyui runtime python code execution", + "nickname": "PyExec", + "title": "PyExec", + "title_aux": "Comfyui-PyExec [UNSAFE]" + } + ], + "https://github.com/bandido37/comfyui-kaggle-local-save": [ + [ + "KaggleLocalSaveNode" + ], + { + "title_aux": "Kaggle ComfyUI Local Save Node [WIP]" + } + ], + "https://github.com/benda1989/WaterMarkRemover_ComfyUI": [ + [ + "Remover", + "VideoRemover" + ], + { + "title_aux": "Comfyui lama remover [WIP]" + } + ], + "https://github.com/benmizrahi/ComfyGCS": [ + [ + "LoadImageGCS", + "SaveImageGCS" + ], + { + "title_aux": "ComfyGCS [WIP]" + } + ], + "https://github.com/beyastard/ComfyUI_BeySoft": [ + [ + "BeySoft" + ], + { + "title_aux": "ComfyUI_BeySoft" + } + ], + "https://github.com/bheins/ComfyUI-glb-to-stl": [ + [ + "GLBToSTLNode" + ], + { + "title_aux": "ComfyUI-glb-to-stl [WIP]" + } + ], + "https://github.com/bikiam/ComfyUi_WhisperGTranslate": [ + [ + "GoogleTranslateNode", + "WhisperAudioTranslateNode" + ], + { + "title_aux": "ComfyUi_WhisperGTranslate" + } + ], + "https://github.com/bikiam/Comfyui_AudioRecoder": [ + [ + "BikiAudioRecorderNode" + ], + { + "title_aux": "Comfyui_AudioRecoder" + } + ], + "https://github.com/birnam/ComfyUI-GenData-Pack": [ + [ + "Checkpoint From String \ud83d\udc69\u200d\ud83d\udcbb", + "Checkpoint Rerouter \ud83d\udc69\u200d\ud83d\udcbb", + "Checkpoint Selector Stacker \ud83d\udc69\u200d\ud83d\udcbb", + "Checkpoint Selector \ud83d\udc69\u200d\ud83d\udcbb", + "Checkpoint to String \ud83d\udc69\u200d\ud83d\udcbb", + "Crop Recombine \ud83d\udc69\u200d\ud83d\udcbb", + "Crop|IP|Inpaint \ud83d\udc69\u200d\ud83d\udcbb", + "Crop|IP|Inpaint|SDXL \ud83d\udc69\u200d\ud83d\udcbb", + "Decode GenData \ud83d\udc69\u200d\ud83d\udcbb", + "Encode GenData \ud83d\udc69\u200d\ud83d\udcbb", + "GenData Stacker \ud83d\udc69\u200d\ud83d\udcbb", + "IPAdapterApply", + "IPAdapterApplyEncoded", + "IPAdapterApplyFaceID", + "IPAdapterBatchEmbeds", + "IPAdapterEncoder", + "IPAdapterLoadEmbeds", + "IPAdapterModelLoader", + "IPAdapterSaveEmbeds", + "IPAdapterTilesMasked", + "InsightFaceLoader", + "LoRA Stack to String \ud83d\udc69\u200d\ud83d\udcbb", + "LoRA Stacker From Prompt \ud83d\udc69\u200d\ud83d\udcbb", + "Load Checkpoints From File \ud83d\udc69\u200d\ud83d\udcbb", + "Load GenData From Dir \ud83d\udc69\u200d\ud83d\udcbb", + "Parse GenData \ud83d\udc69\u200d\ud83d\udcbb", + "PrepImageForClipVision", + "PrepImageForInsightFace", + "Provide GenData \ud83d\udc69\u200d\ud83d\udcbb", + "Save Image From GenData \ud83d\udc69\u200d\ud83d\udcbb", + "VAE From String \ud83d\udc69\u200d\ud83d\udcbb", + "VAE to String \ud83d\udc69\u200d\ud83d\udcbb", + "\u00d7 Product CheckpointXGenDatas \ud83d\udc69\u200d\ud83d\udcbb" + ], + { + "title_aux": "Gen Data Tester [WIP]" + } + ], + "https://github.com/bleash-dev/Comfyui-FileSytem-Manager": [ + [ + "custom_nodes", + "web_extensions" + ], + { + "title_aux": "Comfyui-FileSytem-Manager" + } + ], + "https://github.com/blepping/comfyui_dum_samplers": [ + [ + "BatchMergeSampler", + "CacheAwareEulerSampler", + "CyclePaddingSampler", + "HistorySampler", + "PingPongSampler", + "RestlessScheduler", + "SimilarityAncestralEulerSampler", + "SimilarityClampEulerSampler" + ], + { + "title_aux": "ComfyUI 'dum' samplers [WIP]" + } + ], + "https://github.com/blueraincoatli/ComfyUI-Model-Cleaner": [ + [ + "InteractiveModelCleanerNode", + "ModelScannerNode" + ], + { + "title_aux": "ComfyModelCleaner [WIP]" + } + ], + "https://github.com/bmad4ever/comfyui_bmad_nodes": [ + [ + "AdaptiveThresholding", + "Add String To Many", + "AddAlpha", + "AdjustRect", + "AnyToAny", + "BoundingRect (contours)", + "BuildColorRangeAdvanced (hsv)", + "BuildColorRangeHSV (hsv)", + "CLAHE", + "CLIPEncodeMultiple", + "CLIPEncodeMultipleAdvanced", + "ChameleonMask", + "CheckpointLoader (dirty)", + "CheckpointLoaderSimple (dirty)", + "Color (RGB)", + "Color (hexadecimal)", + "Color Clip", + "Color Clip (advanced)", + "Color Clip ADE20k", + "ColorDictionary", + "ColorDictionary (custom)", + "Conditioning (combine multiple)", + "Conditioning (combine selective)", + "Conditioning Grid (cond)", + "Conditioning Grid (string)", + "Conditioning Grid (string) Advanced", + "Contour To Mask", + "Contours", + "ControlNetHadamard", + "ControlNetHadamard (manual)", + "ConvertImg", + "CopyMakeBorder", + "CreateRequestMetadata", + "DistanceTransform", + "Draw Contour(s)", + "EqualizeHistogram", + "ExtendColorList", + "ExtendCondList", + "ExtendFloatList", + "ExtendImageList", + "ExtendIntList", + "ExtendLatentList", + "ExtendMaskList", + "ExtendModelList", + "ExtendStringList", + "FadeMaskEdges", + "Filter Contour", + "FindComplementaryColor", + "FindThreshold", + "FlatLatentsIntoSingleGrid", + "Framed Mask Grab Cut", + "Framed Mask Grab Cut 2", + "FromListGet1Color", + "FromListGet1Cond", + "FromListGet1Float", + "FromListGet1Image", + "FromListGet1Int", + "FromListGet1Latent", + "FromListGet1Mask", + "FromListGet1Model", + "FromListGet1String", + "FromListGetColors", + "FromListGetConds", + "FromListGetFloats", + "FromListGetImages", + "FromListGetInts", + "FromListGetLatents", + "FromListGetMasks", + "FromListGetModels", + "FromListGetStrings", + "Get Contour from list", + "Get Models", + "Get Prompt", + "Hue Mode (InRange hsv)", + "HypernetworkLoader (dirty)", + "ImageBatchToList", + "InRange (hsv)", + "Inpaint", + "Input/String to Int Array", + "KMeansColor", + "Load 64 Encoded Image", + "LoraLoader (dirty)", + "MaskGrid N KSamplers Advanced", + "MaskOuterBlur", + "Merge Latent Batch Gridwise", + "MonoMerge", + "MorphologicOperation", + "MorphologicSkeletoning", + "NaiveAutoKMeansColor", + "OtsuThreshold", + "RGB to HSV", + "Rect Grab Cut", + "Remap", + "RemapBarrelDistortion", + "RemapFromInsideParabolas", + "RemapFromQuadrilateral (homography)", + "RemapInsideParabolas", + "RemapInsideParabolasAdvanced", + "RemapPinch", + "RemapReverseBarrelDistortion", + "RemapStretch", + "RemapToInnerCylinder", + "RemapToOuterCylinder", + "RemapToQuadrilateral", + "RemapWarpPolar", + "Repeat Into Grid (image)", + "Repeat Into Grid (latent)", + "RequestInputs", + "SampleColorHSV", + "Save Image (api)", + "SeamlessClone", + "SeamlessClone (simple)", + "SetRequestStateToComplete", + "String", + "String to Float", + "String to Integer", + "ToColorList", + "ToCondList", + "ToFloatList", + "ToImageList", + "ToIntList", + "ToLatentList", + "ToMaskList", + "ToModelList", + "ToStringList", + "UnGridify (image)", + "VAEEncodeBatch" + ], + { + "title_aux": "Bmad Nodes [UNSAFE]" + } + ], + "https://github.com/boricuapab/ComfyUI-Bori-KontextPresets": [ + [ + "Bori Kontext Presets" + ], + { + "title_aux": "ComfyUI-Bori-KontextPresets [WIP]" + } + ], + "https://github.com/brace-great/comfyui-eim": [ + [ + "EncryptImage" + ], + { + "title_aux": "comfyui-eim" + } + ], + "https://github.com/brace-great/comfyui-mc": [ + [ + "IncrementCounterOnMatch" + ], + { + "title_aux": "comfyui-mc [WIP]" + } + ], + "https://github.com/broumbroum/comfyui-time-system": [ + [ + "DayTimeNode", + "HourTimeNode", + "MinuteTimeNode", + "MonthTimeNode", + "SecondTimeNode", + "YearTimeNode" + ], + { + "title_aux": "comfyui-time-system [WIP]" + } + ], + "https://github.com/bruce007lee/comfyui-cleaner": [ + [ + "cleaner" + ], + { + "title_aux": "comfyui-cleaner" + } + ], + "https://github.com/bruce007lee/comfyui-tiny-utils": [ + [ + "CropImageByMask", + "FaceAlign", + "FaceAlignImageProcess", + "FaceAlignMaskProcess", + "ImageFillColorByMask", + "ImageSAMMask", + "ImageTransposeAdvance", + "LoadImageAdvance" + ], + { + "title_aux": "comfyui-tiny-utils" + } + ], + "https://github.com/brycegoh/comfyui-custom-nodes": [ + [ + "CombineTwoImageIntoOne", + "FillMaskedArea", + "MaskAreaComparisonSegment", + "OCRAndMask" + ], + { + "title_aux": "brycegoh/comfyui-custom-nodes" + } + ], + "https://github.com/bulldog68/ComfyUI_FMJ": [ + [ + "FMJCreaPrompt", + "FMJKontext" + ], + { + "title_aux": "ComfyUI_FMJ [WIP]" + } + ], + "https://github.com/c0ffymachyne/ComfyUI_SignalProcessing": [ + [ + "SignalProcessingBaxandall3BandEQ", + "SignalProcessingBaxandallEQ", + "SignalProcessingCompressor", + "SignalProcessingConvolutionReverb", + "SignalProcessingFilter", + "SignalProcessingHarmonicsEnhancer", + "SignalProcessingLimiter", + "SignalProcessingLoadAudio", + "SignalProcessingLoudness", + "SignalProcessingMixdown", + "SignalProcessingNormalizer", + "SignalProcessingPadSynth", + "SignalProcessingPadSynthChoir", + "SignalProcessingPaulStretch", + "SignalProcessingPitchShifter", + "SignalProcessingSaturation", + "SignalProcessingSpectrogram", + "SignalProcessingStereoWidening", + "SignalProcessingWaveform" + ], + { + "title_aux": "ComfyUI Signal Processing [WIP]" + } + ], + "https://github.com/casterpollux/MiniMax-bmo": [ + [ + "MinimaxRemoverBMO" + ], + { + "title_aux": "MiniMax-bmo" + } + ], + "https://github.com/catboxanon/ComfyUI-Pixelsmith": [ + [ + "Pixelsmith" + ], + { + "title_aux": "ComfyUI-Pixelsmith [WIP]" + } + ], + "https://github.com/celll1/cel_sampler": [ + [ + "latent_tracker" + ], + { + "title_aux": "cel_sampler [WIP]" + } + ], + "https://github.com/cesilk10/cesilk-comfyui-nodes": [ + [ + "CESILK_OpenAIChat", + "CESILK_OpenAIImageBatchGenerator", + "CESILK_OpenAIImageDescriptionToTextfile", + "CESILK_SaveAndUploadToS3", + "CESILK_SdxlImageSizes" + ], + { + "title_aux": "cesilk-comfyui-nodes" + } + ], + "https://github.com/chaojie/ComfyUI-DynamiCrafter": [ + [ + "DynamiCrafter Simple", + "DynamiCrafterInterp Simple", + "DynamiCrafterInterpLoader", + "DynamiCrafterLoader" + ], + { + "title_aux": "ComfyUI DynamiCrafter" + } + ], + "https://github.com/chaojie/ComfyUI-mobvoi-openapi": [ + [ + "HtmlViewer", + "MobvoiOpenapiMetamanAudio", + "MobvoiOpenapiMetamanText", + "MobvoiOpenapiTts", + "OssUploadAudio", + "OssUploadImage" + ], + { + "title_aux": "ComfyUI-mobvoi-openapi" + } + ], + "https://github.com/chenbaiyujason/ComfyUI_StepFun": [ + [ + "CombineStrings", + "JSONParser", + "StepFunClient", + "TextImageChat", + "VideoChat", + "VideoFileUploader" + ], + { + "title_aux": "ComfyUI_StepFun" + } + ], + "https://github.com/chengzeyi/Comfy-WaveSpeed": [ + [ + "ApplyFBCacheOnModel", + "EnhancedCompileModel", + "EnhancedLoadDiffusionModel", + "VelocatorCompileModel", + "VelocatorLoadAndQuantizeClip", + "VelocatorLoadAndQuantizeDiffusionModel", + "VelocatorQuantizeModel" + ], + { + "title_aux": "Comfy-WaveSpeed [WIP]" + } + ], + "https://github.com/chetusangolgi/Comfyui-supabase": [ + [ + "SupabaseAudioUploader", + "SupabaseImageUploader", + "SupabaseTableWatcherNode" + ], + { + "title_aux": "Comfyui-supabase" + } + ], + "https://github.com/christian-byrne/infinite-zoom-parallax-nodes": [ + [ + "Create Parallax Video", + "Layer Shifter for Parallax Outpainting", + "Load Parallax Frame", + "Parallax Config", + "Save Parallax Frame", + "Shrink and Pad for Outpainting" + ], + { + "title_aux": "\ud83c\udf0c Infinite Parallax Nodes [WIP]" + } + ], + "https://github.com/christian-byrne/python-interpreter-node": [ + [ + "Exec Python Code Script" + ], + { + "title_aux": "Python Interpreter ComfyUI Node [UNSAFE]" + } + ], + "https://github.com/chuge26/ComfyUI_seal_migration": [ + [ + "PDFLoader", + "PDFSaver", + "SealMigration" + ], + { + "title_aux": "ComfyUI_seal_migration [WIP]" + } + ], + "https://github.com/cidiro/cid-node-pack": [ + [ + "CidAnyBuffer", + "CidAnySync", + "CidLoadImageFromDir", + "CidSaveImage", + "CidWildcardProcessor" + ], + { + "title_aux": "cid-node-pack" + } + ], + "https://github.com/ciga2011/ComfyUI-AppGen": [ + [ + "AG_APP_EDIT", + "AG_APP_GEN", + "AG_APP_SANDBOX", + "AG_CODER_LLM" + ], + { + "title_aux": "ComfyUI-AppGen [UNSAFE]" + } + ], + "https://github.com/comfyanonymous/ComfyUI": [ + [ + "APG", + "AddNoise", + "AlignYourStepsScheduler", + "BasicGuider", + "BasicScheduler", + "BetaSamplingScheduler", + "CFGGuider", + "CFGNorm", + "CFGZeroStar", + "CLIPAttentionMultiply", + "CLIPLoader", + "CLIPMergeAdd", + "CLIPMergeSimple", + "CLIPMergeSubtract", + "CLIPSave", + "CLIPSetLastLayer", + "CLIPTextEncode", + "CLIPTextEncodeControlnet", + "CLIPTextEncodeFlux", + "CLIPTextEncodeHiDream", + "CLIPTextEncodeHunyuanDiT", + "CLIPTextEncodeLumina2", + "CLIPTextEncodePixArtAlpha", + "CLIPTextEncodeSD3", + "CLIPTextEncodeSDXL", + "CLIPTextEncodeSDXLRefiner", + "CLIPVisionEncode", + "CLIPVisionLoader", + "Canny", + "CaseConverter", + "CheckpointLoader", + "CheckpointLoaderSimple", + "CheckpointSave", + "ConditioningAverage", + "ConditioningCombine", + "ConditioningConcat", + "ConditioningSetArea", + "ConditioningSetAreaPercentage", + "ConditioningSetAreaPercentageVideo", + "ConditioningSetAreaStrength", + "ConditioningSetMask", + "ConditioningSetTimestepRange", + "ConditioningStableAudio", + "ConditioningZeroOut", + "ControlNetApply", + "ControlNetApplyAdvanced", + "ControlNetApplySD3", + "ControlNetInpaintingAliMamaApply", + "ControlNetLoader", + "CosmosImageToVideoLatent", + "CosmosPredict2ImageToVideoLatent", + "CreateVideo", + "CropMask", + "DiffControlNetLoader", + "DifferentialDiffusion", + "DiffusersLoader", + "DisableNoise", + "DualCFGGuider", + "DualCLIPLoader", + "EmptyAceStepLatentAudio", + "EmptyCosmosLatentVideo", + "EmptyHunyuanLatentVideo", + "EmptyImage", + "EmptyLTXVLatentVideo", + "EmptyLatentAudio", + "EmptyLatentHunyuan3Dv2", + "EmptyLatentImage", + "EmptyMochiLatentVideo", + "EmptySD3LatentImage", + "ExponentialScheduler", + "ExtendIntermediateSigmas", + "FeatherMask", + "FlipSigmas", + "FluxDisableGuidance", + "FluxGuidance", + "FluxKontextImageScale", + "FluxKontextMaxImageNode", + "FluxKontextProImageNode", + "FluxProCannyNode", + "FluxProDepthNode", + "FluxProExpandNode", + "FluxProFillNode", + "FluxProImageNode", + "FluxProUltraImageNode", + "FreSca", + "FreeU", + "FreeU_V2", + "GITSScheduler", + "GLIGENLoader", + "GLIGENTextBoxApply", + "GeminiInputFiles", + "GeminiNode", + "GetImageSize", + "GetVideoComponents", + "GrowMask", + "Hunyuan3Dv2Conditioning", + "Hunyuan3Dv2ConditioningMultiView", + "HunyuanImageToVideo", + "HyperTile", + "HypernetworkLoader", + "IdeogramV1", + "IdeogramV2", + "IdeogramV3", + "ImageAddNoise", + "ImageBatch", + "ImageBlend", + "ImageBlur", + "ImageColorToMask", + "ImageCompositeMasked", + "ImageCrop", + "ImageFlip", + "ImageFromBatch", + "ImageInvert", + "ImageOnlyCheckpointLoader", + "ImageOnlyCheckpointSave", + "ImagePadForOutpaint", + "ImageQuantize", + "ImageRGBToYUV", + "ImageRotate", + "ImageScale", + "ImageScaleBy", + "ImageScaleToTotalPixels", + "ImageSharpen", + "ImageStitch", + "ImageToMask", + "ImageUpscaleWithModel", + "ImageYUVToRGB", + "InpaintModelConditioning", + "InstructPixToPixConditioning", + "InvertMask", + "JoinImageWithAlpha", + "KSampler", + "KSamplerAdvanced", + "KSamplerSelect", + "KarrasScheduler", + "KlingCameraControlI2VNode", + "KlingCameraControlT2VNode", + "KlingCameraControls", + "KlingDualCharacterVideoEffectNode", + "KlingImage2VideoNode", + "KlingImageGenerationNode", + "KlingLipSyncAudioToVideoNode", + "KlingLipSyncTextToVideoNode", + "KlingSingleImageVideoEffectNode", + "KlingStartEndFrameNode", + "KlingTextToVideoNode", + "KlingVideoExtendNode", + "KlingVirtualTryOnNode", + "LTXVAddGuide", + "LTXVConditioning", + "LTXVCropGuides", + "LTXVImgToVideo", + "LTXVPreprocess", + "LTXVScheduler", + "LaplaceScheduler", + "LatentAdd", + "LatentApplyOperation", + "LatentApplyOperationCFG", + "LatentBatch", + "LatentBatchSeedBehavior", + "LatentBlend", + "LatentComposite", + "LatentCompositeMasked", + "LatentCrop", + "LatentFlip", + "LatentFromBatch", + "LatentInterpolate", + "LatentMultiply", + "LatentOperationSharpen", + "LatentOperationTonemapReinhard", + "LatentRotate", + "LatentSubtract", + "LatentUpscale", + "LatentUpscaleBy", + "Load3D", + "Load3DAnimation", + "LoadAudio", + "LoadImage", + "LoadImageMask", + "LoadImageOutput", + "LoadImageSetFromFolderNode", + "LoadImageTextSetFromFolderNode", + "LoadLatent", + "LoadVideo", + "LoraLoader", + "LoraLoaderModelOnly", + "LoraModelLoader", + "LoraSave", + "LossGraphNode", + "LotusConditioning", + "LumaConceptsNode", + "LumaImageModifyNode", + "LumaImageNode", + "LumaImageToVideoNode", + "LumaReferenceNode", + "LumaVideoNode", + "Mahiro", + "MaskComposite", + "MaskPreview", + "MaskToImage", + "MinimaxImageToVideoNode", + "MinimaxSubjectToVideoNode", + "MinimaxTextToVideoNode", + "ModelComputeDtype", + "ModelMergeAdd", + "ModelMergeAuraflow", + "ModelMergeBlocks", + "ModelMergeCosmos14B", + "ModelMergeCosmos7B", + "ModelMergeCosmosPredict2_14B", + "ModelMergeCosmosPredict2_2B", + "ModelMergeFlux1", + "ModelMergeLTXV", + "ModelMergeMochiPreview", + "ModelMergeSD1", + "ModelMergeSD2", + "ModelMergeSD35_Large", + "ModelMergeSD3_2B", + "ModelMergeSDXL", + "ModelMergeSimple", + "ModelMergeSubtract", + "ModelMergeWAN2_1", + "ModelSamplingAuraFlow", + "ModelSamplingContinuousEDM", + "ModelSamplingContinuousV", + "ModelSamplingDiscrete", + "ModelSamplingFlux", + "ModelSamplingLTXV", + "ModelSamplingSD3", + "ModelSamplingStableCascade", + "ModelSave", + "MoonvalleyImg2VideoNode", + "MoonvalleyTxt2VideoNode", + "MoonvalleyVideo2VideoNode", + "Morphology", + "OpenAIChatConfig", + "OpenAIChatNode", + "OpenAIDalle2", + "OpenAIDalle3", + "OpenAIGPTImage1", + "OpenAIInputFiles", + "OptimalStepsScheduler", + "PatchModelAddDownscale", + "PerpNeg", + "PerpNegGuider", + "PerturbedAttentionGuidance", + "PhotoMakerEncode", + "PhotoMakerLoader", + "PikaImageToVideoNode2_2", + "PikaScenesV2_2", + "PikaStartEndFrameNode2_2", + "PikaTextToVideoNode2_2", + "Pikadditions", + "Pikaffects", + "Pikaswaps", + "PixverseImageToVideoNode", + "PixverseTemplateNode", + "PixverseTextToVideoNode", + "PixverseTransitionVideoNode", + "PolyexponentialScheduler", + "PorterDuffImageComposite", + "Preview3D", + "Preview3DAnimation", + "PreviewAny", + "PreviewAudio", + "PreviewImage", + "PrimitiveBoolean", + "PrimitiveFloat", + "PrimitiveInt", + "PrimitiveString", + "PrimitiveStringMultiline", + "QuadrupleCLIPLoader", + "RandomNoise", + "RebatchImages", + "RebatchLatents", + "RecraftColorRGB", + "RecraftControls", + "RecraftCreativeUpscaleNode", + "RecraftCrispUpscaleNode", + "RecraftImageInpaintingNode", + "RecraftImageToImageNode", + "RecraftRemoveBackgroundNode", + "RecraftReplaceBackgroundNode", + "RecraftStyleV3DigitalIllustration", + "RecraftStyleV3InfiniteStyleLibrary", + "RecraftStyleV3LogoRaster", + "RecraftStyleV3RealisticImage", + "RecraftTextToImageNode", + "RecraftTextToVectorNode", + "RecraftVectorizeImageNode", + "ReferenceLatent", + "RegexExtract", + "RegexMatch", + "RegexReplace", + "RenormCFG", + "RepeatImageBatch", + "RepeatLatentBatch", + "RescaleCFG", + "ResizeAndPadImage", + "Rodin3D_Detail", + "Rodin3D_Regular", + "Rodin3D_Sketch", + "Rodin3D_Smooth", + "RunwayFirstLastFrameNode", + "RunwayImageToVideoNodeGen3a", + "RunwayImageToVideoNodeGen4", + "RunwayTextToImageNode", + "SDTurboScheduler", + "SD_4XUpscale_Conditioning", + "SV3D_Conditioning", + "SVD_img2vid_Conditioning", + "SamplerCustom", + "SamplerCustomAdvanced", + "SamplerDPMAdaptative", + "SamplerDPMPP_2M_SDE", + "SamplerDPMPP_2S_Ancestral", + "SamplerDPMPP_3M_SDE", + "SamplerDPMPP_SDE", + "SamplerER_SDE", + "SamplerEulerAncestral", + "SamplerEulerAncestralCFGPP", + "SamplerEulerCFGpp", + "SamplerLCMUpscale", + "SamplerLMS", + "SamplerSASolver", + "SamplingPercentToSigma", + "SaveAnimatedPNG", + "SaveAnimatedWEBP", + "SaveAudio", + "SaveAudioMP3", + "SaveAudioOpus", + "SaveGLB", + "SaveImage", + "SaveImageWebsocket", + "SaveLatent", + "SaveLoRANode", + "SaveSVGNode", + "SaveVideo", + "SaveWEBM", + "SelfAttentionGuidance", + "SetFirstSigma", + "SetLatentNoiseMask", + "SetUnionControlNetType", + "SkipLayerGuidanceDiT", + "SkipLayerGuidanceDiTSimple", + "SkipLayerGuidanceSD3", + "SolidMask", + "SplitImageWithAlpha", + "SplitSigmas", + "SplitSigmasDenoise", + "StabilityStableImageSD_3_5Node", + "StabilityStableImageUltraNode", + "StabilityUpscaleConservativeNode", + "StabilityUpscaleCreativeNode", + "StabilityUpscaleFastNode", + "StableCascade_EmptyLatentImage", + "StableCascade_StageB_Conditioning", + "StableCascade_StageC_VAEEncode", + "StableCascade_SuperResolutionControlnet", + "StableZero123_Conditioning", + "StableZero123_Conditioning_Batched", + "StringCompare", + "StringConcatenate", + "StringContains", + "StringLength", + "StringReplace", + "StringSubstring", + "StringTrim", + "StubConstantImage", + "StubFloat", + "StubImage", + "StubInt", + "StubMask", + "StyleModelApply", + "StyleModelLoader", + "T5TokenizerOptions", + "TCFG", + "TestAccumulateNode", + "TestAccumulationGetItemNode", + "TestAccumulationGetLengthNode", + "TestAccumulationHeadNode", + "TestAccumulationSetItemNode", + "TestAccumulationTailNode", + "TestAccumulationToListNode", + "TestAsyncBatchProcessing", + "TestAsyncConcurrentLimit", + "TestAsyncError", + "TestAsyncLazyCheck", + "TestAsyncProgressUpdate", + "TestAsyncResourceUser", + "TestAsyncTimeout", + "TestAsyncValidation", + "TestAsyncValidationError", + "TestBoolOperationNode", + "TestCustomIsChanged", + "TestCustomValidation1", + "TestCustomValidation2", + "TestCustomValidation3", + "TestCustomValidation4", + "TestCustomValidation5", + "TestDynamicAsyncGeneration", + "TestDynamicDependencyCycle", + "TestExecutionBlocker", + "TestFloatConditions", + "TestForLoopClose", + "TestForLoopOpen", + "TestIntConditions", + "TestIntMathOperation", + "TestIsChangedWithConstants", + "TestLazyMixImages", + "TestListToAccumulationNode", + "TestMakeListNode", + "TestMixedExpansionReturns", + "TestOutputNodeWithSocketOutput", + "TestParallelSleep", + "TestSamplingInExpansion", + "TestSleep", + "TestStringConditions", + "TestSyncError", + "TestSyncProgressUpdate", + "TestToBoolNode", + "TestVariadicAverage", + "TestWhileLoopClose", + "TestWhileLoopOpen", + "TextEncodeAceStepAudio", + "TextEncodeHunyuanVideo_ImageToVideo", + "ThresholdMask", + "TomePatchModel", + "TorchCompileModel", + "TrainLoraNode", + "TrimVideoLatent", + "TripleCLIPLoader", + "TripoConversionNode", + "TripoImageToModelNode", + "TripoMultiviewToModelNode", + "TripoRefineNode", + "TripoRetargetNode", + "TripoRigNode", + "TripoTextToModelNode", + "TripoTextureNode", + "UNETLoader", + "UNetCrossAttentionMultiply", + "UNetSelfAttentionMultiply", + "UNetTemporalAttentionMultiply", + "UpscaleModelLoader", + "VAEDecode", + "VAEDecodeAudio", + "VAEDecodeHunyuan3D", + "VAEDecodeTiled", + "VAEEncode", + "VAEEncodeAudio", + "VAEEncodeForInpaint", + "VAEEncodeTiled", + "VAELoader", + "VAESave", + "VPScheduler", + "VeoVideoGenerationNode", + "VideoLinearCFGGuidance", + "VideoTriangleCFGGuidance", + "VoxelToMesh", + "VoxelToMeshBasic", + "Wan22ImageToVideoLatent", + "WanCameraEmbedding", + "WanCameraImageToVideo", + "WanFirstLastFrameToVideo", + "WanFunControlToVideo", + "WanFunInpaintToVideo", + "WanImageToVideo", + "WanPhantomSubjectToVideo", + "WanTrackToVideo", + "WanVaceToVideo", + "WebcamCapture", + "unCLIPCheckpointLoader", + "unCLIPConditioning" + ], + { + "title_aux": "ComfyUI" + } + ], + "https://github.com/comfyanonymous/ComfyUI_bitsandbytes_NF4": [ + [ + "CheckpointLoaderNF4" + ], + { + "title_aux": "ComfyUI_bitsandbytes_NF4 [EXPERIMENTAL]" + } + ], + "https://github.com/comfypod/ComfyUI-Comflow": [ + [ + "ComflowInputBoolean", + "ComflowInputCheckpoint", + "ComflowInputImage", + "ComflowInputImageAlpha", + "ComflowInputImageBatch", + "ComflowInputLora", + "ComflowInputNumber", + "ComflowInputNumberInt", + "ComflowInputNumberSlider", + "ComflowInputText", + "ComflowInputVid", + "ComflowInputVideo", + "ComflowWebsocketImageInput", + "ComflowWebsocketImageOutput" + ], + { + "description": "", + "nickname": "Comflow", + "title": "comflow", + "title_aux": "ComfyUI-Comflow" + } + ], + "https://github.com/comfyuiblog/deepseek_prompt_generator_comfyui": [ + [ + "DeepSeek_Prompt_Generator" + ], + { + "title_aux": "deepseek_prompt_generator_comfyui [WIP]" + } + ], + "https://github.com/corbin-hayden13/ComfyUI-Better-Dimensions": [ + [ + "BetterImageDimensions", + "PureRatio", + "SDXLDimensions" + ], + { + "title_aux": "ComfyUI-Better-Dimensions" + } + ], + "https://github.com/crimro-se/ComfyUI-CascadedGaze": [ + [ + "CGDenoiseImage", + "CGDenoiseModelLoader" + ], + { + "title_aux": "ComfyUI-CascadedGaze" + } + ], + "https://github.com/ctf05/ComfyUI-AudioDuration": [ + [ + "SimpleAudioDuration", + "SimpleAudioOverlay" + ], + { + "title_aux": "ComfyUI-AudioDuration" + } + ], + "https://github.com/cwebbi1/VoidCustomNodes": [ + [ + "Prompt Parser", + "String Combiner" + ], + { + "title_aux": "VoidCustomNodes" + } + ], + "https://github.com/cyberhirsch/seb_nodes": [ + [ + "AspectRatioSeb", + "DepthInpaintSeb", + "SaveImageSeb", + "SwitchMasksSeb", + "SwitchSeb", + "UnifiedPrompterSeb" + ], + { + "title_aux": "seb_nodes [WIP]" + } + ], + "https://github.com/daracazamea/comfyUI-DCNodes": [ + [ + "FluxResolutionPicker", + "GetGenerationTime", + "ManualTrigger", + "SDXLResolutionPicker", + "StartTimerPassThrough" + ], + { + "title_aux": "DCNodess [WIP]" + } + ], + "https://github.com/denislov/Comfyui_AutoSurvey": [ + [ + "AddDoc2Knowledge", + "AutoSurvey", + "ChatModel", + "ComfyMilvus", + "ComfyWeaviate", + "ManageDatabase", + "MilvusScheme", + "MsField", + "QueryKnowledge", + "WcProperty", + "WcPropertyComb", + "WriteOutline", + "WriteSection" + ], + { + "title_aux": "Comfyui_AutoSurvey" + } + ], + "https://github.com/dexintenebri/comfyui_voxel_nodes": [ + [ + "AutoVoxelScaler", + "DepthEstimationNode", + "DepthToVox", + "MultiViewVoxelFusion", + "OptimizedVoxelizationNode", + "ProceduralChunkedWFC3DTerrain", + "ShapeCompletionNode", + "VoxelExportNode", + "VoxelModelLoader", + "VoxelPreview", + "VoxelPreviewNode", + "VoxelToGLB", + "WFC3DTerrainGenerator" + ], + { + "title_aux": "comfyui_voxel_nodes [WIP]" + } + ], + "https://github.com/dfl/comfyui-stylegan": [ + [ + "BatchAverageStyleGANLatents", + "BlendStyleGANLatents", + "GenerateStyleGANLatent", + "LoadStyleGAN", + "LoadStyleGANLatentImg", + "SaveStyleGANLatentImg", + "StyleGANInversion", + "StyleGANLatentFromBatch", + "StyleGANSampler" + ], + { + "title_aux": "comfyui-stylegan" + } + ], + "https://github.com/dhpdong/ComfyUI-IPAdapter-Flux-Repair": [ + [ + "SamplerCustomAdvancedPlus", + "SeedPlus", + "UnetLoaderGGUFPlus" + ], + { + "title_aux": "ComfyUI-IPAdapter-Flux-Repair" + } + ], + "https://github.com/dihan/comfyui-random-kps": [ + [ + "RandomFaceKeypoints" + ], + { + "title_aux": "ComfyUI Random Keypoints for InstantID [WIP]" + } + ], + "https://github.com/dogcomplex/ComfyUI-LOKI": [ + [ + "EvaluateRelevanceLLM", + "FilterNodesLLM", + "LLMQueryAPI", + "LLMQueryAPIBatch", + "LokiGlamour", + "LokiListAvailableNodes", + "LokiListInstalledNodes", + "LokiTweetScraper", + "LokiTweetThreadScraper", + "LokiTweetUserScraper", + "TwitterScraper", + "TwitterThreadScraper", + "TwitterUserScraper", + "\u270d\ufe0fScribe (LOKI)" + ], + { + "title_aux": "ComfyUI-LOKI [WIP]" + } + ], + "https://github.com/doucx/ComfyUI_WcpD_Utility_Kit": [ + [ + "BlackImage", + "CopyImage(Wayland)", + "ExecStrAsCode", + "MergeStrings", + "YamlToPrompt" + ], + { + "title_aux": "ComfyUI_WcpD_Utility_Kit" + } + ], + "https://github.com/dowands/ComfyUI-AddMaskForICLora": [ + [ + "AddMaskForICLora" + ], + { + "title_aux": "AddMaskForICLora" + } + ], + "https://github.com/downlifted/ComfyUI_BWiZ_Nodes": [ + [ + "BWIZInteractiveLogMonitor", + "BWIZ_AdvancedLoadImageBatch", + "BWIZ_CaptainWebhook", + "BWIZ_ComfyEmail", + "BWIZ_ErrorDetector", + "BWIZ_HFRepoBatchLoader", + "BWIZ_NotificationSound" + ], + { + "title_aux": "ComfyUI_BWiZ_Nodes [WIP]" + } + ], + "https://github.com/eggsbenedicto/DiffusionRenderer-ComfyUI": [ + [ + "Cosmos1ForwardRenderer", + "Cosmos1InverseRenderer", + "LoadDiffusionRendererModel" + ], + { + "title_aux": "DiffusionRenderer-ComfyUI [WIP]" + } + ], + "https://github.com/eigenpunk/ComfyUI-audio": [ + [ + "ApplyVoiceFixer", + "BatchAudio", + "BlendAudio", + "ClipAudioRegion", + "CombineImageWithAudio", + "ConcatAudio", + "ConvertAudio", + "FilterAudio", + "FlattenAudioBatch", + "HifiGANApply", + "HifiGANLoader", + "HifiGANModelParams", + "InvertAudioPhase", + "LoadAudio", + "MusicgenGenerate", + "MusicgenHFGenerate", + "MusicgenHFLoader", + "MusicgenLoader", + "NormalizeAudio", + "PreviewAudio", + "ResampleAudio", + "SaveAudio", + "SpectrogramImage", + "Tacotron2Generate", + "Tacotron2Loader", + "ToMelSpectrogram", + "TortoiseTTSGenerate", + "TortoiseTTSLoader", + "TrimAudio", + "TrimAudioSamples", + "TrimSilence", + "VALLEXGenerator", + "VALLEXLoader", + "VALLEXVoicePromptFromAudio", + "VALLEXVoicePromptLoader", + "WaveGlowApply", + "WaveGlowLoader" + ], + { + "title_aux": "ComfyUI-audio" + } + ], + "https://github.com/ejektaflex/ComfyUI-Ty": [ + [ + "Lora Block Weight Regex Loader // Ty" + ], + { + "title_aux": "ComfyUI-Ty" + } + ], + "https://github.com/emranemran/ComfyUI-FasterLivePortrait": [ + [ + "FasterLivePortraitProcess", + "LoadFasterLivePortraitModels" + ], + { + "title_aux": "ComfyUI-FasterLivePortrait" + } + ], + "https://github.com/endman100/ComfyUI-SaveAndLoadPromptCondition": [ + [ + "LoadContditioning", + "SaveConditioning" + ], + { + "title_aux": "ComfyUI Nodes: SaveConditioning and LoadConditioning" + } + ], + "https://github.com/endman100/ComfyUI-augmentation": [ + [ + "RamdomFlipImage (endman100)" + ], + { + "title_aux": "ComfyUI-augmentation" + } + ], + "https://github.com/enlo/ComfyUI-CheckpointSettings": [ + [ + "CheckPointSettingsListMerger", + "CheckPointSettingsPack", + "CheckPointSettingsRandomSelector", + "CheckPointSettingsTie", + "CheckPointSettingsToFilename", + "CheckPointSettingsUnpack", + "RandomChoiceNumber" + ], + { + "title_aux": "ComfyUI-CheckpointSettings" + } + ], + "https://github.com/ericbeyer/guidance_interval": [ + [ + "Guidance Interval" + ], + { + "title_aux": "guidance_interval" + } + ], + "https://github.com/erosDiffusion/ComfyUI-enricos-json-file-load-and-value-selector": [ + [ + "SelectorNode" + ], + { + "title_aux": "Select key from JSON (Alpha) [UNSAFE]" + } + ], + "https://github.com/esciron/ComfyUI-HunyuanVideoWrapper-Extended": [ + [ + "DownloadAndLoadHyVideoTextEncoder", + "HyVideoBlockSwap", + "HyVideoDecode", + "HyVideoEncode", + "HyVideoModelLoader", + "HyVideoSTG", + "HyVideoSampler", + "HyVideoTextEncode", + "HyVideoTorchCompileSettings", + "HyVideoVAELoader" + ], + { + "title_aux": "ComfyUI-HunyuanVideoWrapper-Extended [WIP]" + } + ], + "https://github.com/exectails/comfyui-et_scripting": [ + [ + "ETPythonTextScript3Node" + ], + { + "title_aux": "Scripting" + } + ], + "https://github.com/eyekayem/comfyui_runway_gen3": [ + [ + "RunwayVideoGenerator", + "RunwayVideoPreview" + ], + { + "title_aux": "comfyui_runway_gen3" + } + ], + "https://github.com/facok/ComfyUI-FokToolset": [ + [ + "Fok_PreprocessRefImage" + ], + { + "title_aux": "ComfyUI-FokToolset" + } + ], + "https://github.com/fangg2000/ComfyUI-SenseVoice": [ + [ + "STTNode", + "ShowTextNode", + "VoiceRecorderNode" + ], + { + "title_aux": "ComfyUI-SenseVoice [WIP]" + } + ], + "https://github.com/fangg2000/ComfyUI-StableAudioFG": [ + [ + "LoadStableAudioModel", + "StableAudioFG" + ], + { + "author": "lks-ai", + "description": "A Simple integration of Stable Audio Diffusion with knobs and stuff!", + "nickname": "stableaudio", + "title": "StableAudioSampler", + "title_aux": "ComfyUI-StableAudioFG [WIP]" + } + ], + "https://github.com/fangziheng2321/comfyuinode_chopmask": [ + [ + "cus_chopmask" + ], + { + "title_aux": "comfyuinode_chopmask [WIP]" + } + ], + "https://github.com/filipemeneses/ComfyUI_html": [ + [ + "HtmlDownload", + "HtmlPreview", + "LoadHtml", + "SaveHtml", + "SingleImageToBase64", + "SingleImageToBase64KeepMetadata" + ], + { + "title_aux": "ComfyUI_html [UNSAFE]" + } + ], + "https://github.com/flowtyone/comfyui-flowty-lcm": [ + [ + "LCMSampler" + ], + { + "title_aux": "comfyui-flowty-lcm" + } + ], + "https://github.com/flyingdogsoftware/gyre_for_comfyui": [ + [ + "BackgroundRemoval", + "GyreIfElse", + "GyreLoopEnd", + "GyreLoopStart" + ], + { + "title_aux": "Gyre for ComfyUI" + } + ], + "https://github.com/foglerek/comfyui-cem-tools": [ + [ + "ProcessImageBatch" + ], + { + "title_aux": "comfyui-cem-tools" + } + ], + "https://github.com/franky519/comfyui-redux-style": [ + [ + "StyleModelAdvanced", + "StyleModelApplySimple", + "StyleModelConditioner", + "StyleModelGridVisualizer" + ], + { + "title_aux": "comfyui-redux-style" + } + ], + "https://github.com/franky519/comfyui_fnckc_Face_analysis": [ + [ + "FaceAnalysisModels", + "FaceFourImageMatcher" + ], + { + "title_aux": "ComfyUI Face Four Image Matcher [WIP]" + } + ], + "https://github.com/fritzprix/ComfyUI-LLM-Utils": [ + [ + "WeightedDict", + "WeightedDictConcat", + "WeightedDictInput", + "WeightedDictSelect", + "WeightedDictSelectGroup", + "WeightedDictToPrompt" + ], + { + "title_aux": "ComfyUI-LLM-Utils [WIP]" + } + ], + "https://github.com/ftechmax/ComfyUI-NovaKit-Pack": [ + [ + "CountTokens" + ], + { + "title_aux": "ComfyUI-NovaKit-Pack" + } + ], + "https://github.com/ftf001-tech/ComfyUI-ExternalLLMDetector": [ + [ + "ExternalLLMDetectorBboxesConvert", + "ExternalLLMDetectorMainProcess", + "ExternalLLMDetectorSettings" + ], + { + "title_aux": "ComfyUI-Lucian [WIP]" + } + ], + "https://github.com/fylrid2/comfyui_lock_previous_value": [ + [ + "Lock" + ], + { + "title_aux": "lockValue" + } + ], + "https://github.com/gabe-init/ComfyUI-LM-Studio": [ + [ + "LMStudioNode" + ], + { + "title_aux": "ComfyUI LM Studio Node [WIP]" + } + ], + "https://github.com/gabe-init/ComfyUI-Repo-Eater": [ + [ + "RepoEaterNode" + ], + { + "title_aux": "gabe-init [WIP]" + } + ], + "https://github.com/gabe-init/comfyui_ui_render": [ + [ + "HTMLRendererNode" + ], + { + "title_aux": "comfyui_ui_render [UNSAFE]" + } + ], + "https://github.com/gagaprince/ComfyUI_gaga_utils": [ + [ + "GagaAddStringArray", + "GagaBatchStringReplace", + "GagaGetDirList", + "GagaGetFileList", + "GagaGetImageInfoByUpload", + "GagaGetImageInfoWithUrl", + "GagaGetImageWithPath", + "GagaGetStringArrayByIndex", + "GagaGetStringArraySize", + "GagaGetStringListSize", + "GagaPythonScript", + "GagaSaveImageToPath", + "GagaSaveImageWithInfo", + "GagaSaveImagesToGif", + "GagaSplitStringToList", + "GagaStringListToArray", + "GagaTest" + ], + { + "title_aux": "ComfyUI_gaga_utils" + } + ], + "https://github.com/galoreware/ComfyUI-GaloreNodes": [ + [ + "GNI_HEX_TO_COLOR", + "GNI_RGB_TO_COLOR", + "GN_COLOR_TO_INT", + "GN_IO_GET_FILENAME", + "GN_MASK_TO_IMAGE", + "GN_SNAP_RESIZE" + ], + { + "title_aux": "ComfyUI-GaloreNodes [WIP]" + } + ], + "https://github.com/gameltb/ComfyUI_stable_fast": [ + [ + "ApplyStableFastUnet", + "ApplyTensorRTControlNet", + "ApplyTensorRTUnet", + "ApplyTensorRTVaeDecoder" + ], + { + "title_aux": "ComfyUI_stable_fast" + } + ], + "https://github.com/gamtruliar/ComfyUI-N_SwapInput": [ + [ + "N_SwapInput" + ], + { + "title_aux": "ComfyUI-N_SwapInput [UNSAFE]" + } + ], + "https://github.com/gaowei-space/ComfyUI-Doubao-LLM": [ + [ + "DoubaoAPI", + "DoubaoConfig", + "DoubaoTextChat", + "DoubaoVisionChat" + ], + { + "title_aux": "ComfyUI Doubao LLM [WIP]" + } + ], + "https://github.com/gilons/ComfyUI-GoogleDrive-Downloader": [ + [ + "custom_nodes" + ], + { + "title_aux": "ComfyUI-GoogleDrive-Downloader [UNSAFE]" + } + ], + "https://github.com/gitadmini/comfyui_extractstoryboards": [ + [ + "Example", + "ExtractStoryboards_xuhuan1024", + "IntBatchSize_xuhuan1024", + "IntBatch_xuhuan1024" + ], + { + "title_aux": "ExtractStoryboards [WIP]" + } + ], + "https://github.com/githubYiheng/comfyui_median_filter": [ + [ + "ImageMedianFilter" + ], + { + "title_aux": "comfyui_median_filter" + } + ], + "https://github.com/gmorks/ComfyUI-Animagine-Prompt": [ + [ + "AnimaginePrompt", + "MultiWildcardLoader", + "MultilineTextInput", + "TextFileLoader" + ], + { + "title_aux": "ComfyUI Animagine prompt [WIP]" + } + ], + "https://github.com/go-package-lab/ComfyUI-Tools-Video-Combine": [ + [ + "Tools:CopyFile", + "Tools:Image2video", + "Tools:LoadAudioUrl", + "Tools:PreviewVideo", + "Tools:SetString", + "Tools:SetValue", + "Tools:VideoWatermark" + ], + { + "title_aux": "ComfyUI-Tools-Video-Combine [WIP]" + } + ], + "https://github.com/godric8/ComfyUI_Step1X-Edit": [ + [ + "Step1XEdit" + ], + { + "title_aux": "ComfyUI_Step1X-Edit [NAME CONFLICT]" + } + ], + "https://github.com/gold24park/loki-comfyui-node": [ + [ + "Base64ToImage", + "DominantColor", + "ImageLuminance", + "ImageToBase64", + "OverlayText" + ], + { + "title_aux": "loki-comfyui-node" + } + ], + "https://github.com/gondar-software/ComfyUI-Affine-Transform": [ + [ + "AffineTransform" + ], + { + "title_aux": "Affine Transform ComfyUI Node [WIP]" + } + ], + "https://github.com/gondar-software/ComfyUI-Simple-Image-Tools": [ + [ + "GetMaskFromAlpha", + "GetQuadrilateralOutfit" + ], + { + "title_aux": "ComfyUI-Simple-Image-Tools [WIP]" + } + ], + "https://github.com/gordon123/ComfyUI_DreamBoard": [ + [ + "PromptExtraNode", + "StoryboardNode" + ], + { + "title_aux": "ComfyUI_DreamBoard [WIP]" + } + ], + "https://github.com/gordon123/ComfyUI_srt2speech": [ + [ + "GetSubtitleByIndex", + "MergeAllWave", + "SaveWavNode" + ], + { + "title_aux": "ComfyUI_srt2speech [WIP]" + } + ], + "https://github.com/gorillaframeai/GF_pixtral_node": [ + [ + "GF_MistralPixtralNode" + ], + { + "title_aux": "GF_pixtral_node [WIP]" + } + ], + "https://github.com/grimli333/ComfyUI_Grim": [ + [ + "GenerateFileName", + "TwoStringsFormat" + ], + { + "title_aux": "ComfyUI_Grim" + } + ], + "https://github.com/grinlau18/ComfyUI_XISER_Nodes": [ + [ + "CreatePointsString", + "XISER_Canvas", + "XIS_CanvasMaskProcessor", + "XIS_CompositorProcessor", + "XIS_CropImage", + "XIS_DynamicBatchKSampler", + "XIS_Float_Slider", + "XIS_FromListGet1Color", + "XIS_FromListGet1Cond", + "XIS_FromListGet1Float", + "XIS_FromListGet1Image", + "XIS_FromListGet1Int", + "XIS_FromListGet1Latent", + "XIS_FromListGet1Mask", + "XIS_FromListGet1Model", + "XIS_FromListGet1String", + "XIS_INT_Slider", + "XIS_IPAStyleSettings", + "XIS_IfDataIsNone", + "XIS_ImageManager", + "XIS_ImageMaskMirror", + "XIS_ImageStitcher", + "XIS_InvertMask", + "XIS_IsThereAnyData", + "XIS_KSamplerSettingsNode", + "XIS_KSamplerSettingsUnpackNode", + "XIS_Label", + "XIS_LatentBlendNode", + "XIS_LoadImage", + "XIS_MaskBatchProcessor", + "XIS_MaskCompositeOperation", + "XIS_MergePackImages", + "XIS_MultiPromptSwitch", + "XIS_PSDLayerExtractor", + "XIS_PackImages", + "XIS_PromptProcessor", + "XIS_PromptsWithSwitches", + "XIS_ReorderImageMaskGroups", + "XIS_ReorderImages", + "XIS_ResizeImageOrMask", + "XIS_ResizeToDivisible", + "XIS_ResolutionSelector" + ], + { + "title_aux": "Xiser_Nodes [WIP]" + } + ], + "https://github.com/grokuku/ComfyUI-Holaf": [ + [ + "HolafBenchmarkLoader", + "HolafBenchmarkPlotter", + "HolafBenchmarkRunner", + "HolafImageComparer", + "HolafInstagramResize", + "HolafInternalSampler", + "HolafKSampler", + "HolafLutGenerator", + "HolafLutSaver", + "HolafMaskToBoolean", + "HolafOrchestratorConfig", + "HolafOverlayNode", + "HolafRatioCalculator", + "HolafResolutionPreset", + "HolafSaveImage", + "HolafSliceCalculator", + "HolafTileCalculator", + "HolafTiledKSampler", + "UpscaleImageHolaf" + ], + { + "title_aux": "Holaf Custom Nodes for ComfyUI" + } + ], + "https://github.com/haodman/ComfyUI_Rain": [ + [ + "Rain_ImageSize", + "Rain_IntToFloat", + "Rain_Math", + "Rain_ValueSwitch" + ], + { + "title_aux": "ComfyUI_Rain" + } + ], + "https://github.com/haofanwang/ComfyUI-InstantStyle": [ + [ + "BaseModelLoader", + "InstantStyleGenerationNode", + "InstantStyleLoader", + "PromptLoader" + ], + { + "title_aux": "ComfyUI-InstantStyle" + } + ], + "https://github.com/haomole/Comfyui-SadTalker": [ + [ + "LoadRefVideo", + "SadTalker", + "ShowAudio", + "ShowText", + "ShowVideo" + ], + { + "title_aux": "Comfyui-SadTalker" + } + ], + "https://github.com/hay86/ComfyUI_AceNodes": [ + [ + "ACE_AnyInputSwitchBool", + "ACE_AnyInputToAny", + "ACE_AudioCrop", + "ACE_AudioLoad", + "ACE_AudioPlay", + "ACE_AudioSave", + "ACE_Expression_Eval", + "ACE_Float", + "ACE_ImageColorFix", + "ACE_ImageConstrain", + "ACE_ImageFaceCrop", + "ACE_ImageGetSize", + "ACE_ImageLoadFromCloud", + "ACE_ImageMakeSlideshow", + "ACE_ImagePixelate", + "ACE_ImageQA", + "ACE_ImageRemoveBackground", + "ACE_ImageSaveToCloud", + "ACE_Integer", + "ACE_MaskBlur", + "ACE_OpenAI_GPT_Chat", + "ACE_OpenAI_GPT_IMAGE", + "ACE_OpenAI_GPT_TTS", + "ACE_Seed", + "ACE_Text", + "ACE_TextConcatenate", + "ACE_TextGoogleTranslate", + "ACE_TextInputSwitch2Way", + "ACE_TextInputSwitch4Way", + "ACE_TextInputSwitch8Way", + "ACE_TextList", + "ACE_TextLoad", + "ACE_TextPreview", + "ACE_TextSave", + "ACE_TextSelector", + "ACE_TextToResolution", + "ACE_TextTranslate", + "ACE_VideoConcat", + "ACE_VideoLoad", + "ACE_VideoPreview" + ], + { + "title_aux": "ComfyUI AceNodes [UNSAFE]" + } + ], + "https://github.com/hdfhssg/ComfyUI_pxtool": [ + [ + "ArtistLoader", + "CivitaiHelper", + "DanbooruCharacterTag", + "E621CharacterTag", + "NegativeTag", + "PX_Seed", + "QualityTag", + "RandomArtists", + "RandomArtistsAdvanced", + "RandomTag" + ], + { + "title_aux": "ComfyUI_pxtool [WIP]" + } + ], + "https://github.com/hdfhssg/comfyui_EvoSearch": [ + [ + "EvoSearch_FLUX", + "EvoSearch_SD21", + "EvoSearch_WAN", + "EvolutionScheduleGenerator", + "GuidanceRewardsGenerator" + ], + { + "title_aux": "comfyui_EvoSearch [WIP]" + } + ], + "https://github.com/hiusdev/ComfyUI_Lah_Toffee": [ + [ + "LoadVideoRandom" + ], + { + "title_aux": "ComfyUI_Lah_Toffee" + } + ], + "https://github.com/hnmr293/ComfyUI-SamOne": [ + [ + "Latent", + "SamplerOne" + ], + { + "title_aux": "ComfyUI-SamOne - one-step sampling" + } + ], + "https://github.com/horidream/ComfyUI-Horidream": [ + [ + "PassThroughWithSound" + ], + { + "title_aux": "ComfyUI-Horidream" + } + ], + "https://github.com/hotpizzatactics/ComfyUI-WaterMark-Detector": [ + [ + "AdaptiveThresholding", + "AdvancedWatermarkEnhancement", + "AdvancedWaveletWatermarkEnhancement", + "CLAHEEnhancement", + "CombineEnhancements", + "ComprehensiveImageEnhancement", + "DenoisingFilter", + "EdgeDetection", + "FlexibleCombineEnhancements", + "HighPassFilter", + "ImprovedGrayColorEnhancement", + "MorphologicalOperations", + "TextureEnhancement", + "WatermarkEnhancement" + ], + { + "title_aux": "ComfyUI-WaterMark-Detector" + } + ], + "https://github.com/hotpot-killer/ComfyUI_AlexNodes": [ + [ + "InstructPG" + ], + { + "title_aux": "ComfyUI_AlexNodes" + } + ], + "https://github.com/houdinii/comfy-magick": [ + [ + "AdaptiveBlur", + "AdaptiveSharpen", + "AddNoise", + "BlueShift", + "Blur", + "Charcoal", + "Colorize", + "CropByAspectRatio", + "Despeckle", + "Edge", + "Emboss", + "FX", + "GaussianBlur", + "Implode", + "Kuwahara", + "MotionBlur", + "RotationalBlur", + "SelectiveBlur", + "Sepia", + "Shade", + "Sharpen", + "Sketch", + "Solarize", + "Spread", + "Stereogram", + "Swirl", + "Tint", + "UnsharpMask", + "Vignette", + "WaveletDenoise" + ], + { + "title_aux": "comfy-magick [WIP]" + } + ], + "https://github.com/huizhang0110/ComfyUI_Easy_Nodes_hui": [ + [ + "EasyBgRemover", + "EasyBgRemover_ModelLoader", + "EasyControlNetApply", + "EasyControlNetLoader", + "EasyEmptyLatentImage", + "EasyLatentToCondition", + "EasyLoadImage" + ], + { + "title_aux": "ComfyUI_Easy_Nodes_hui" + } + ], + "https://github.com/hulipanpan/Comfyui_tuteng": [ + [ + "TutengChatGPTApi", + "TutengGeminiAPI", + "TutengJimengApi", + "TutengJimengVideoApi", + "TutengSeededit", + "Tuteng_Flux_Kontext", + "Tuteng_Flux_Kontext_Edit", + "Tuteng_Flux_Kontext_bfl", + "Tuteng_Google_Veo3", + "Tuteng_Mj", + "Tuteng_Mju", + "Tuteng_Mjv", + "Tuteng_gpt_image_1", + "Tuteng_gpt_image_1_edit", + "Tuteng_kling_image2video", + "Tuteng_kling_text2video", + "Tuteng_kling_videoPreview", + "Tuteng_lip_sync", + "Tuteng_mjstyle", + "Tuteng_upload", + "Tuteng_video_extend" + ], + { + "title_aux": "Comfyui_tuteng [WIP]" + } + ], + "https://github.com/hunterssl/ComfyUI_SSLNodes": [ + [ + "SSLGetJsonKeysCount", + "SSLLoadCheckpointByName", + "SSLLoadJson", + "SSLRandomNumInLoop", + "SSLRandomSeedInLoop", + "SSLSaveImageOutside" + ], + { + "title_aux": "ComfyUI_SSLNodes" + } + ], + "https://github.com/hunzmusic/ComfyUI-Hunyuan3DTools": [ + [ + "Hy3DTools_BackProjectInpaint", + "Hy3DTools_RenderSpecificView" + ], + { + "title_aux": "ComfyUI-Hunyuan3DTools [WIP]" + } + ], + "https://github.com/hunzmusic/Comfyui-CraftsMan3DWrapper": [ + [ + "CraftsManDoraVAEGenerator", + "DecodeCraftsManLatents", + "LoadCraftsManPipeline", + "PreprocessImageCraftsMan", + "SampleCraftsManLatents", + "SaveCraftsManMesh" + ], + { + "title_aux": "Comfyui-CraftsMan3DWrapper [WIP]" + } + ], + "https://github.com/hunzmusic/comfyui-hnznodes": [ + [ + "CombineChannelsGrayscale", + "ImageBatchReorder", + "MaleCharacterPromptGenerator" + ], + { + "title_aux": "comfyui-hnznodes" + } + ], + "https://github.com/hy134300/comfyui-hb-node": [ + [ + "generate story", + "hy save image", + "latent to list", + "movie batch", + "movie generate", + "sound voice", + "text concat" + ], + { + "title_aux": "comfyui-hb-node" + } + ], + "https://github.com/hy134300/comfyui-hydit": [ + [ + "DiffusersCLIPLoader", + "DiffusersCheckpointLoader", + "DiffusersClipTextEncode", + "DiffusersControlNetLoader", + "DiffusersLoraLoader", + "DiffusersModelMakeup", + "DiffusersPipelineLoader", + "DiffusersSampler", + "DiffusersSchedulerLoader", + "DiffusersVAELoader" + ], + { + "title_aux": "comfyui-hydit" + } + ], + "https://github.com/hylarucoder/comfyui-copilot": [ + [ + "EagleImageNode", + "SDXLPromptStyler", + "SDXLPromptStylerAdvanced", + "SDXLResolutionPresets" + ], + { + "title_aux": "comfyui-copilot" + } + ], + "https://github.com/iacoposk8/xor_pickle_nodes": [ + [ + "Load XOR Pickle From File", + "Save XOR Pickle To File" + ], + { + "title_aux": "ComfyUI XOR Pickle Nodes" + } + ], + "https://github.com/if-ai/ComfyUI-IF_Zonos": [ + [ + "IF_ZonosTTS" + ], + { + "title_aux": "ComfyUI-IF_Zonos [WIP]" + } + ], + "https://github.com/ilovejohnwhite/Tracer": [ + [ + "BillyGoatNode", + "EcstaticNode", + "HintImageEnchance", + "Image Load TTK", + "ImageGenResolutionFromImage", + "ImageGenResolutionFromLatent", + "KillMeNode", + "LinkMasterNode", + "OkayBuddyNode", + "OutlineRealNode", + "OutlineStandardNode", + "PixelPerfectResolution", + "SuckerPunch", + "UWU_Preprocessor", + "VooDooNode" + ], + { + "title_aux": "Kolors Awesome Prompts [WIP]" + } + ], + "https://github.com/immersiveexperience/ie-comfyui-color-nodes": [ + [ + "Average Color", + "Complementary Color", + "Hex Color to Image", + "Hex to Color Name", + "Random String" + ], + { + "title_aux": "ie-comfyui-color-nodes" + } + ], + "https://github.com/io-club/ComfyUI-LuminaNext": [ + [ + "GemmaClipLoader" + ], + { + "title_aux": "ComfyUI-LuminaNext [WIP]" + } + ], + "https://github.com/jammyfu/ComfyUI_PaintingCoderUtils": [ + [ + "PaintingCoder::DynamicImageCombiner", + "PaintingCoder::DynamicMaskCombiner", + "PaintingCoder::ImageLatentCreator", + "PaintingCoder::ImageLatentCreatorPlus", + "PaintingCoder::ImageResolutionAdjuster", + "PaintingCoder::ImageSizeCreator", + "PaintingCoder::ImageSizeCreatorPlus", + "PaintingCoder::ImageSwitch", + "PaintingCoder::ImageToBase64", + "PaintingCoder::LatentSwitch", + "PaintingCoder::MaskPreview", + "PaintingCoder::MaskSwitch", + "PaintingCoder::MultilineTextInput", + "PaintingCoder::OutputToTextConverter", + "PaintingCoder::RemoveEmptyLinesAndLeadingSpaces", + "PaintingCoder::ShowTextPlus", + "PaintingCoder::SimpleTextInput", + "PaintingCoder::TextCombiner", + "PaintingCoder::TextSwitch", + "PaintingCoder::WebImageLoader" + ], + { + "title_aux": "ComfyUI PaintingCoderUtils Nodes [WIP]" + } + ], + "https://github.com/jax-explorer/ComfyUI-DreamO": [ + [ + "BgRmModelLoad", + "DreamOGenerate", + "DreamOLoadModel", + "DreamOLoadModelFromLocal", + "FaceModelLoad" + ], + { + "title_aux": "ComfyUI-DreamO" + } + ], + "https://github.com/jcomeme/ComfyUI-AsunaroTools": [ + [ + "AsunaroAnd", + "AsunaroAutomaticSexPrompter", + "AsunaroBatchImageLoader", + "AsunaroIfBiggerThanZero", + "AsunaroIfContain", + "AsunaroIfSame", + "AsunaroImageLoader", + "AsunaroIntToStr", + "AsunaroOr", + "AsunaroPromptStripper", + "AsunaroRandomDice", + "AsunaroResolutions", + "AsunaroSave", + "AsunaroTextConcatenator", + "AsunaroWildCard" + ], + { + "title_aux": "AsunaroTools" + } + ], + "https://github.com/jerryname2022/ComfyUI-Real-ESRGAN": [ + [ + "GFPGANImageGenerator", + "GFPGANModelLoader", + "RealESRGANImageGenerator", + "RealESRGANModelLoader" + ], + { + "title_aux": "ComfyUI-Real-ESRGAN [WIP]" + } + ], + "https://github.com/jgbrblmd/ComfyUI-ComfyFluxSize": [ + [ + "ComfyFluxSize" + ], + { + "title_aux": "ComfyUI-ComfyFluxSize [WIP]" + } + ], + "https://github.com/jgbyte/ComfyUI-RandomCube": [ + [ + "RandomCubeGrid" + ], + { + "title_aux": "ComfyUI-RandomCube [WIP]" + } + ], + "https://github.com/jiafuzeng/comfyui-fishSpeech": [ + [ + "FishSpeechAudioDurationNode", + "FishSpeechAudioPreview", + "FishSpeechLoader", + "FishSpeechTTS" + ], + { + "title_aux": "comfyui-fishSpeech" + } + ], + "https://github.com/jimmm-ai/TimeUi-a-ComfyUi-Timeline-Node": [ + [ + "jimmm.ai.TimelineUI" + ], + { + "title_aux": "TimeUi a ComfyUI Timeline Node System [WIP]" + } + ], + "https://github.com/jimstudt/ComfyUI-Jims-Nodes": [ + [ + "ChooseFromStringList", + "Cubby", + "DefineWord", + "DictFromJSON", + "DictionaryToJSON", + "ImageToSolidBackground", + "JSONToDictionary", + "LiftFromBackground", + "LoadImageAndInfoFromPath", + "LookupWord", + "ReplaceWords", + "TextToStringList", + "ZoomFocus" + ], + { + "title_aux": "Jim's ComfyUI Nodes [WIP]" + } + ], + "https://github.com/jinchanz/ComfyUI-AliCloud-Bailian": [ + [ + "BailianAPI", + "BailianAPIPoll", + "BailianAPISubmit", + "MaletteJSONExtractor", + "MaletteJSONModifier", + "MaletteVirtualTryOn" + ], + { + "title_aux": "ComfyUI-AliCloud-Bailian [WIP]" + } + ], + "https://github.com/jn-jairo/jn_node_suite_comfyui": [ + [ + "JN_AreaInfo", + "JN_AreaNormalize", + "JN_AreaWidthHeight", + "JN_AreaXY", + "JN_Blip", + "JN_BlipLoader", + "JN_BooleanOperation", + "JN_Condition", + "JN_CoolDown", + "JN_CoolDownOutput", + "JN_CropFace", + "JN_DatetimeFormat", + "JN_DatetimeInfo", + "JN_DatetimeNow", + "JN_Dump", + "JN_DumpOutput", + "JN_FaceRestoreModelLoader", + "JN_FaceRestoreWithModel", + "JN_FirstActive", + "JN_ImageAddMask", + "JN_ImageBatch", + "JN_ImageCenterArea", + "JN_ImageCrop", + "JN_ImageGrid", + "JN_ImageInfo", + "JN_ImageSharpness", + "JN_ImageSquare", + "JN_ImageUncrop", + "JN_KSampler", + "JN_KSamplerAdvancedParams", + "JN_KSamplerFaceRestoreParams", + "JN_KSamplerResizeInputParams", + "JN_KSamplerResizeMaskAreaParams", + "JN_KSamplerResizeOutputParams", + "JN_KSamplerSeamlessParams", + "JN_KSamplerTileParams", + "JN_LoadImageDirectory", + "JN_LogicOperation", + "JN_MaskInfo", + "JN_MathOperation", + "JN_MathOperationArray", + "JN_PrimitiveArrayInfo", + "JN_PrimitiveBatchToArray", + "JN_PrimitiveBoolean", + "JN_PrimitiveFloat", + "JN_PrimitiveInt", + "JN_PrimitivePrompt", + "JN_PrimitiveString", + "JN_PrimitiveStringMultiline", + "JN_PrimitiveStringToArray", + "JN_PrimitiveToArray", + "JN_PrimitiveToBoolean", + "JN_PrimitiveToFloat", + "JN_PrimitiveToInt", + "JN_PrimitiveToString", + "JN_RemoveBackground", + "JN_Seamless", + "JN_SeamlessBorder", + "JN_SeamlessBorderCrop", + "JN_SelectItem", + "JN_Sleep", + "JN_SleepOutput", + "JN_SliceOperation", + "JN_StopIf", + "JN_StopIfOutput", + "JN_TextConcatenation", + "JN_TextReplace", + "JN_TimedeltaFormat", + "JN_TimedeltaInfo", + "JN_VAEPatch" + ], + { + "title_aux": "jn_node_suite_comfyui [WIP]" + } + ], + "https://github.com/jordancoult/ComfyUI_HelpfulNodes": [ + [ + "JCo_CropAroundKPS" + ], + { + "title_aux": "ComfyUI_HelpfulNodes" + } + ], + "https://github.com/jschoormans/Comfy-InterestingPixels": [ + [ + "Random Palette", + "Shareable Image Slider" + ], + { + "title_aux": "ComfyUI-TexturePacker [WIP]" + } + ], + "https://github.com/jtscmw01/ComfyUI-DiffBIR": [ + [ + "DiffBIR_sample", + "DiffBIR_sample_advanced", + "Simple_load", + "Stage1_load", + "Stage2_load" + ], + { + "title_aux": "ComfyUI-DiffBIR" + } + ], + "https://github.com/jtydhr88/ComfyUI-Unique3D": [ + [ + "Unique3DLoadPipeline", + "Unique3DRun - Geo Reconstruct", + "Unique3DRun - MVPrediction" + ], + { + "title_aux": "ComfyUI-Unique3D [WIP]" + } + ], + "https://github.com/jtydhr88/ComfyUI_frontend_vue_basic": [ + [ + "vue-basic" + ], + { + "title_aux": "ComfyUI Frontend Vue Basic [WIP]" + } + ], + "https://github.com/junhe421/comfyui_batch_image_processor": [ + [ + "BatchImageDifferenceToPromptFiles", + "custom_nodes" + ], + { + "title_aux": "comfyui_batch_image_processor [WIP]" + } + ], + "https://github.com/kadirnar/ComfyUI-Adapter": [ + [ + "GarmentSegLoader" + ], + { + "title_aux": "ComfyUI-Adapter [WIP]" + } + ], + "https://github.com/kandy/ComfyUI-KAndy": [ + [ + "KAndyLoadImageFromUrl", + "KAndyTaggerModelLoader", + "KAndyWD14Tagger", + "KPromtGen", + "KandySimplePrompt" + ], + { + "title_aux": "ComfyUI-KAndy" + } + ], + "https://github.com/kappa54m/ComfyUI_Usability": [ + [ + "KLoadImageByPath", + "KLoadImageByPathAdvanced", + "KLoadImageDedup" + ], + { + "title_aux": "ComfyUI_Usability (WIP)" + } + ], + "https://github.com/karthikg-09/ComfyUI-3ncrypt": [ + [ + "Enhanced Save Image", + "Markdown Editor" + ], + { + "title_aux": "ComfyUI-KG09 [WIP]" + } + ], + "https://github.com/kevin314/ComfyUI-FastVideo": [ + [ + "DITConfig", + "InferenceArgs", + "LoadImagePath", + "TextEncoderConfig", + "VAEConfig", + "VideoGenerator" + ], + { + "title_aux": "ComfyUI-FastVideo" + } + ], + "https://github.com/kijai/ComfyUI-CV-VAE": [ + [ + "CV_VAE_Decode", + "CV_VAE_Encode", + "CV_VAE_Load" + ], + { + "title_aux": "ComfyUI-CV-VAE" + } + ], + "https://github.com/kijai/ComfyUI-DeepSeek-VL": [ + [ + "deepseek_vl_inference", + "deepseek_vl_model_loader" + ], + { + "title_aux": "ComfyUI nodes to use DeepSeek-VL" + } + ], + "https://github.com/kijai/ComfyUI-DiffSynthWrapper": [ + [ + "DiffSynthSampler", + "DownloadAndLoadDiffSynthExVideoSVD" + ], + { + "title_aux": "ComfyUI DiffSynth wrapper nodes" + } + ], + "https://github.com/kijai/ComfyUI-DiffusersSD3Wrapper": [ + [ + "LoadSD3DiffusersPipeline", + "SD3ControlNetSampler" + ], + { + "title_aux": "ComfyUI-DiffusersSD3Wrapper" + } + ], + "https://github.com/kijai/ComfyUI-EasyAnimateWrapper": [ + [ + "DownloadAndLoadEasyAnimateModel", + "EasyAnimateDecode", + "EasyAnimateImageEncoder", + "EasyAnimateResize", + "EasyAnimateSampler", + "EasyAnimateTextEncode" + ], + { + "title_aux": "ComfyUI-EasyAnimateWrapper [WIP]" + } + ], + "https://github.com/kijai/ComfyUI-FollowYourEmojiWrapper": [ + [ + "DownloadAndLoadFYEModel", + "FYECLIPEncode", + "FYEClipEmbedToComfy", + "FYELandmarkEncode", + "FYELandmarkToComfy", + "FYEMediaPipe", + "FYESampler", + "FYESamplerLong" + ], + { + "title_aux": "ComfyUI-FollowYourEmojiWrapper [WIP]" + } + ], + "https://github.com/kijai/ComfyUI-FramePackWrapper": [ + [ + "DownloadAndLoadFramePackModel", + "FramePackFindNearestBucket", + "FramePackLoraSelect", + "FramePackSampler", + "FramePackSingleFrameSampler", + "FramePackTorchCompileSettings", + "LoadFramePackModel" + ], + { + "title_aux": "ComfyUI-FramePackWrapper [WIP]" + } + ], + "https://github.com/kijai/ComfyUI-Hunyuan3DWrapper": [ + [ + "CV2InpaintTexture", + "DownloadAndLoadHy3DDelightModel", + "DownloadAndLoadHy3DPaintModel", + "Hy3DApplyTexture", + "Hy3DBPT", + "Hy3DBakeFromMultiview", + "Hy3DCameraConfig", + "Hy3DDelightImage", + "Hy3DDiffusersSchedulerConfig", + "Hy3DExportMesh", + "Hy3DFastSimplifyMesh", + "Hy3DGenerateMesh", + "Hy3DGenerateMeshMultiView", + "Hy3DGetMeshPBRTextures", + "Hy3DIMRemesh", + "Hy3DLoadMesh", + "Hy3DMeshInfo", + "Hy3DMeshUVWrap", + "Hy3DMeshVerticeInpaintTexture", + "Hy3DModelLoader", + "Hy3DNvdiffrastRenderer", + "Hy3DPostprocessMesh", + "Hy3DRenderMultiView", + "Hy3DRenderMultiViewDepth", + "Hy3DRenderSingleView", + "Hy3DSampleMultiView", + "Hy3DSetMeshPBRAttributes", + "Hy3DSetMeshPBRTextures", + "Hy3DTorchCompileSettings", + "Hy3DUploadMesh", + "Hy3DVAEDecode", + "Hy3DVAELoader", + "Hy3D_2_1SimpleMeshGen", + "MESHToTrimesh", + "TrimeshToMESH" + ], + { + "title_aux": "ComfyUI-ComfyUI-Hunyuan3DWrapper [WIP]" + } + ], + "https://github.com/kijai/ComfyUI-HunyuanVideoWrapper": [ + [ + "DownloadAndLoadHyVideoTextEncoder", + "HunyuanVideoFresca", + "HunyuanVideoSLG", + "HyVideoBlockSwap", + "HyVideoCFG", + "HyVideoContextOptions", + "HyVideoCustomPromptTemplate", + "HyVideoDecode", + "HyVideoEmptyTextEmbeds", + "HyVideoEncode", + "HyVideoEncodeKeyframes", + "HyVideoEnhanceAVideo", + "HyVideoGetClosestBucketSize", + "HyVideoI2VEncode", + "HyVideoInverseSampler", + "HyVideoLatentPreview", + "HyVideoLoopArgs", + "HyVideoLoraBlockEdit", + "HyVideoLoraSelect", + "HyVideoModelLoader", + "HyVideoPromptMixSampler", + "HyVideoReSampler", + "HyVideoSTG", + "HyVideoSampler", + "HyVideoTeaCache", + "HyVideoTextEmbedBridge", + "HyVideoTextEmbedsLoad", + "HyVideoTextEmbedsSave", + "HyVideoTextEncode", + "HyVideoTextImageEncode", + "HyVideoTorchCompileSettings", + "HyVideoVAELoader" + ], + { + "title_aux": "ComfyUI-HunyuanVideoWrapper [WIP]" + } + ], + "https://github.com/kijai/ComfyUI-MMAudio": [ + [ + "MMAudioFeatureUtilsLoader", + "MMAudioModelLoader", + "MMAudioSampler", + "MMAudioVoCoderLoader" + ], + { + "title_aux": "ComfyUI-MMAudio [WIP]" + } + ], + "https://github.com/kijai/ComfyUI-MochiWrapper": [ + [ + "DownloadAndLoadMochiModel", + "MochiDecode", + "MochiDecodeSpatialTiling", + "MochiFasterCache", + "MochiImageEncode", + "MochiLatentPreview", + "MochiModelLoader", + "MochiSampler", + "MochiSigmaSchedule", + "MochiTextEncode", + "MochiTorchCompileSettings", + "MochiVAEEncoderLoader", + "MochiVAELoader" + ], + { + "title_aux": "ComfyUI-MochiWrapper [WIP]" + } + ], + "https://github.com/kijai/ComfyUI-VEnhancer": [ + [ + "DownloadAndLoadVEnhancerModel", + "VEnhancerSampler", + "VEnhancerUnpad" + ], + { + "title_aux": "ComfyUI nodes for VEnhancer [WIP]" + } + ], + "https://github.com/kijai/ComfyUI-VideoNoiseWarp": [ + [ + "GetWarpedNoiseFromVideo", + "GetWarpedNoiseFromVideoAnimateDiff", + "GetWarpedNoiseFromVideoCogVideoX", + "GetWarpedNoiseFromVideoHunyuan" + ], + { + "title_aux": "ComfyUI-VideoNoiseWarp [WIP]" + } + ], + "https://github.com/kimara-ai/ComfyUI-Kimara-AI-Advanced-Watermarks": [ + [ + "KimaraAIBatchImages", + "KimaraAIWatermarker" + ], + { + "title_aux": "Advanced Watermarking Tools [WIP]" + } + ], + "https://github.com/kimara-ai/ComfyUI-Kimara-AI-Image-From-URL": [ + [ + "KimaraAIImageFromURL" + ], + { + "title_aux": "ComfyUI-Kimara-AI-Image-From-URL [WIP]" + } + ], + "https://github.com/kk8bit/KayTool": [ + [ + "AB_Images", + "AIO_Translater", + "Abc_Math", + "Baidu_Translater", + "Color_Adjustment", + "Custom_Save_Image", + "Display_Any", + "Image_Composer", + "Image_Cropper", + "Image_Mask_Composer", + "Image_Resizer", + "Image_Size_Extractor", + "Kay_BiRefNet_Loader", + "Load_Image_Folder", + "Mask_Blur_Plus", + "Mask_Filler", + "Preview_Mask", + "Preview_Mask_Plus", + "RemBG_Loader", + "Remove_BG", + "Slider_10", + "Slider_100", + "Slider_1000", + "Strong_Prompt", + "Tencent_Translater", + "Text", + "To_Int" + ], + { + "title_aux": "KayTool" + } + ], + "https://github.com/kongds1999/ComfyUI_was_image": [ + [ + "ConvertGrayToImage", + "Generate Color Palette", + "Replace Color By Palette" + ], + { + "title_aux": "ComfyUI_was_image" + } + ], + "https://github.com/krich-cto/ComfyUI-Flow-Control": [ + [ + "CLIPLoaderGGUF", + "DualCLIPLoaderGGUF", + "FlowCheckpointPresetLoader", + "FlowClipCondition", + "FlowClipTextEncode", + "FlowConditioningAutoSwitch", + "FlowFluxPresetLoader", + "FlowGate", + "FlowImageAutoBatch", + "FlowImageCondition", + "FlowKSampler", + "FlowLatentAutoBatch", + "FlowLatentCondition", + "FlowLoraLoader", + "FlowLoraLoaderModelOnly", + "FlowModelManager", + "FlowSaveImage", + "QuadrupleCLIPLoaderGGUF", + "TripleCLIPLoaderGGUF", + "UnetLoaderGGUF", + "UnetLoaderGGUFAdvanced" + ], + { + "title_aux": "ComfyUI Flow Control [UNSTABLE]" + } + ], + "https://github.com/krisshen2021/comfyui_OpenRouterNodes": [ + [ + "OpenRouterOAINode_Infer", + "OpenRouterOAINode_Models", + "OpenRouterOAINode_hunyuanPrompt", + "OpenRouterOAINode_txt2imgPrompt" + ], + { + "title_aux": "comfyui_OpenRouterNodes [WIP]" + } + ], + "https://github.com/kuschanow/ComfyUI-SD-Slicer": [ + [ + "SdSlicer" + ], + { + "title_aux": "ComfyUI-SD-Slicer" + } + ], + "https://github.com/kxh/ComfyUI-ImageUpscaleWithModelMultipleTimes": [ + [ + "ImageUpscaleWithModelMultipleTimes" + ], + { + "title_aux": "ComfyUI-ImageUpscaleWithModelMultipleTimes" + } + ], + "https://github.com/kxh/ComfyUI-sam2": [ + [ + "Segment" + ], + { + "title_aux": "ComfyUI-sam2" + } + ], + "https://github.com/kycg/comfyui-Kwtoolset": [ + [ + "KWImageResizeByLongerSide", + "KWNagetiveString", + "KWPositiveString", + "KWShowAnything", + "KWanywhereString", + "KwtoolsetChangeOpenpose", + "KwtoolsetCheckpointLoaderwithpreview", + "KwtoolsetConditioningSelect", + "KwtoolsetGetHipMask", + "KwtoolsetGetHipMasktest", + "KwtoolsetGetImageSize", + "KwtoolsetGrowMaskPlus", + "KwtoolsetImageSelect", + "KwtoolsetLoadCheckpointsBatch", + "KwtoolsetLoraLoaderwithpreview", + "KwtoolsetMaskAdd", + "KwtoolsetModelSelect", + "LatentMatch" + ], + { + "title_aux": "comfyui-Kwtoolset" + } + ], + "https://github.com/kylegrover/comfyui-python-cowboy": [ + [ + "PythonScript" + ], + { + "title_aux": "comfyui-python-cowboy [UNSAFE]" + } + ], + "https://github.com/laksjdjf/ssd-1b-comfyui": [ + [ + "SSD-1B-Loader" + ], + { + "title_aux": "ssd-1b-comfyui" + } + ], + "https://github.com/lazybuttalented/ComfyUI_LBT": [ + [ + "LBT_CombineImagesFromBatch", + "LBT_CombineImagesFromList", + "LBT_CropByMask", + "LBT_GetFolderInfo", + "LBT_ListInfo", + "LBT_LoadImagesFromFolder", + "LBT_LoadImagesFromList", + "LBT_LoadTextFromFolder", + "LBT_LoadVideoFromFolder", + "LBT_SaveImage", + "LBT_StringToList", + "LBT_TextImageLibraryComparison" + ], + { + "title_aux": "ComfyUI_LBT [WIP]" + } + ], + "https://github.com/leadbreak/comfyui-faceaging": [ + [ + "AgeTransformationNode" + ], + { + "title_aux": "Face Aging [WIP]" + } + ], + "https://github.com/leeguandong/ComfyUI_AliControlnetInpainting": [ + [ + "AliInpaintingsampler", + "EcomXL_AddFG", + "EcomXL_Condition", + "EcomXL_Controlnet_ModelLoader", + "EcomXL_LoadImage", + "EcomXL_SDXL_Inpaint_ModelLoader", + "Flux_Controlnet_ModelLoader", + "Flux_Inpainting_ModelLoader", + "SD3_Controlnet_ModelLoader", + "SD3_Inpainting_ModelLoader" + ], + { + "title_aux": "ComfyUI_AliControlnetInpainting [WIP]" + } + ], + "https://github.com/leoleelxh/ComfyUI-MidjourneyNode-leoleexh": [ + [ + "MidjourneyGenerateNode", + "MidjourneyUpscaleNode" + ], + { + "title_aux": "ComfyUI-MidjourneyNode-leoleexh" + } + ], + "https://github.com/leon-etienne/ComfyUI_Scoring-Nodes": [ + [ + "AestheticScore", + "ImageSimilarity", + "MultiAestheticScore", + "MultiImageToTextSimilarity", + "MultiTextToImageSimilarity", + "TextSimilarity" + ], + { + "title_aux": "ComfyUI_Scoring-Nodes" + } + ], + "https://github.com/lggcfx2020/ComfyUI-LGGCFX-Tools": [ + [ + "LGGCFX_resolution", + "LGGCFX_time_frame" + ], + { + "title_aux": "ComfyUI-LGGCFX-Tools" + } + ], + "https://github.com/lgldlk/ComfyUI-img-tiler": [ + [ + "PC ImageListTileMaker", + "PC TileMaker", + "PC TilerImage", + "PC TilerSelect" + ], + { + "title_aux": "ComfyUI-img-tiler" + } + ], + "https://github.com/linhusyung/comfyui-Build-and-train-your-network": [ + [ + "Conv_layer", + "Normalization_layer", + "activation_function", + "create_dataset", + "create_intput", + "create_model", + "create_training_task", + "forward_test", + "linear_layer", + "pooling_layer", + "pre_train_layer", + "res_connect", + "show_dimensions", + "view_layer" + ], + { + "title_aux": "ComfyUI Build and Train Your Network [WIP]" + } + ], + "https://github.com/littleowl/ComfyUI-MV-HECV": [ + [ + "CombineSideBySide", + "DepthResize", + "StereoShift_Fast" + ], + { + "title_aux": "ComfyUI-MV-HECV" + } + ], + "https://github.com/logtd/ComfyUI-Fluxtapoz": [ + [ + "AddFluxFlow", + "ApplyFluxRaveAttention", + "ApplyRefFlux", + "ApplyRegionalConds", + "ConfigureModifiedFlux", + "CreateRegionalCond", + "FlowEditForwardSampler", + "FlowEditGuider", + "FlowEditReverseSampler", + "FlowEditSampler", + "FluxAttnOverride", + "FluxDeGuidance", + "FluxForwardODESampler", + "FluxInverseSampler", + "FluxNoiseMixer", + "FluxReverseODESampler", + "InFluxFlipSigmas", + "InFluxModelSamplingPred", + "OutFluxModelSamplingPred", + "PAGAttention", + "PrepareAttnBank", + "RFDoubleBlocksOverride", + "RFSingleBlocksOverride", + "RegionalStyleModelApply", + "SEGAttention" + ], + { + "title_aux": "ComfyUI-Fluxtapoz [WIP]" + } + ], + "https://github.com/logtd/ComfyUI-HunyuanLoom": [ + [ + "ConfigureModifiedHY", + "HYApplyRegionalConds", + "HYAttnOverride", + "HYCreateRegionalCond", + "HYFetaEnhance", + "HYFlowEditGuider", + "HYFlowEditGuiderAdv", + "HYFlowEditGuiderCFG", + "HYFlowEditGuiderCFGAdv", + "HYFlowEditSampler", + "HYForwardODESampler", + "HYInverseModelSamplingPred", + "HYReverseModelSamplingPred", + "HYReverseODESampler", + "HyVideoFlowEditSamplerWrapper" + ], + { + "title_aux": "ComfyUI-HunyuanLoom [WIP]" + } + ], + "https://github.com/logtd/ComfyUI-Veevee": [ + [ + "ApplyVVModel", + "FlowConfig", + "FlowGetFlow", + "GetRaftFlow", + "InjectionConfig", + "PivotConfig", + "RaveConfig", + "SCAConfig", + "TemporalConfig", + "VVSamplerSampler", + "VVUnsamplerSampler" + ], + { + "title_aux": "ComfyUI-Veevee [WIP]" + } + ], + "https://github.com/longgui0318/comfyui-one-more-step": [ + [ + "Calculate More Step Latent", + "Load More Step Model" + ], + { + "title_aux": "comfyui-one-more-step [WIP]" + } + ], + "https://github.com/longzoho/ComfyUI-Qdrant-Saver": [ + [ + "QDrantSaver" + ], + { + "title_aux": "ComfyUI-Qdrant-Saver" + } + ], + "https://github.com/lordwedggie/xcpNodes": [ + [ + "derpBaseAlpha", + "derpSlider", + "xcpDerpBool", + "xcpDerpFloat", + "xcpDerpINT", + "xcpDerpSeed" + ], + { + "title_aux": "xcpNodes [WIP]" + } + ], + "https://github.com/love2hina-net/ComfyUI-Local-Translator": [ + [ + "LocalTranslator" + ], + { + "title_aux": "ComfyUI-Local-Translator" + } + ], + "https://github.com/lrzjason/Comfyui-Condition-Utils": [ + [ + "LoadCondition", + "LoadConditionFromLoras", + "SaveCondition" + ], + { + "title_aux": "Comfyui-Condition-Utils [WIP]" + } + ], + "https://github.com/ltdrdata/ComfyUI-Workflow-Component": [ + [ + "ComboToString", + "ExecutionBlocker", + "ExecutionControlString", + "ExecutionOneOf", + "ExecutionSwitch", + "InputUnzip", + "InputZip", + "LoopControl", + "LoopCounterCondition", + "OptionalTest", + "TensorToCPU" + ], + { + "title_aux": "ComfyUI-Workflow-Component [WIP]" + } + ], + "https://github.com/lu64k/SK-Nodes": [ + [ + "Ask LLM", + "Color Transfer", + "Image Tracing Node", + "Load LLM", + "Load_Nemotron", + "Natural Saturation", + "OpenAI DAlle Node", + "OpenAI Text Node", + "SK Random File Name", + "SK Save Text", + "SK Text_String", + "SK load text", + "Tone Layer Quantize", + "grey_scale blend" + ], + { + "title_aux": "SK-Nodes" + } + ], + "https://github.com/lucafoscili/lf-nodes": [ + [ + "LF_Blend", + "LF_BlobToImage", + "LF_Bloom", + "LF_BlurImages", + "LF_Boolean", + "LF_Brightness", + "LF_CaptionImageWD14", + "LF_CharacterImpersonator", + "LF_CheckpointSelector", + "LF_CivitAIMetadataSetup", + "LF_Clarity", + "LF_ColorAnalysis", + "LF_CompareImages", + "LF_Contrast", + "LF_ControlPanel", + "LF_CreateMask", + "LF_Desaturation", + "LF_DiffusionModelSelector", + "LF_DisplayBoolean", + "LF_DisplayFloat", + "LF_DisplayInteger", + "LF_DisplayJSON", + "LF_DisplayPrimitiveAsJSON", + "LF_DisplayString", + "LF_EmbeddingSelector", + "LF_EmptyImage", + "LF_ExtractPromptFromLoraTag", + "LF_ExtractString", + "LF_FilmGrain", + "LF_Float", + "LF_GaussianBlur", + "LF_GetRandomKeyFromJSON", + "LF_GetValueFromJSON", + "LF_ImageClassifier", + "LF_ImageHistogram", + "LF_ImageListFromJSON", + "LF_ImageToSVG", + "LF_ImagesEditingBreakpoint", + "LF_ImagesSlideshow", + "LF_Integer", + "LF_IsLandscape", + "LF_KeywordCounter", + "LF_KeywordToggleFromJSON", + "LF_LLMChat", + "LF_LLMMessenger", + "LF_LUTApplication", + "LF_LUTGeneration", + "LF_Line", + "LF_LoadAndEditImages", + "LF_LoadCLIPSegModel", + "LF_LoadFileOnce", + "LF_LoadImages", + "LF_LoadLocalJSON", + "LF_LoadLoraTags", + "LF_LoadMetadata", + "LF_LoadWD14Model", + "LF_LoraAndEmbeddingSelector", + "LF_LoraSelector", + "LF_MarkdownDocGenerator", + "LF_MathOperation", + "LF_MultipleImageResizeForWeb", + "LF_Notify", + "LF_ParsePromptWithLoraTags", + "LF_RandomBoolean", + "LF_RegexReplace", + "LF_RegionExtractor", + "LF_ResizeImageByEdge", + "LF_ResizeImageToDimension", + "LF_ResizeImageToSquare", + "LF_ResolutionSwitcher", + "LF_SamplerSelector", + "LF_Saturation", + "LF_SaveImageForCivitAI", + "LF_SaveJSON", + "LF_SaveMarkdown", + "LF_SaveText", + "LF_SchedulerSelector", + "LF_Sepia", + "LF_SequentialSeedsGenerator", + "LF_SetValueInJSON", + "LF_ShuffleJSONKeys", + "LF_Something2Number", + "LF_Something2String", + "LF_SortJSONKeys", + "LF_SortTags", + "LF_SplitTone", + "LF_String", + "LF_StringReplace", + "LF_StringTemplate", + "LF_StringToJSON", + "LF_SwitchFloat", + "LF_SwitchImage", + "LF_SwitchInteger", + "LF_SwitchJSON", + "LF_SwitchString", + "LF_TiltShift", + "LF_UpdateUsageStatistics", + "LF_UpscaleModelSelector", + "LF_UrandomSeedGenerator", + "LF_UsageStatistics", + "LF_VAESelector", + "LF_Vibrance", + "LF_ViewImages", + "LF_Vignette", + "LF_WallOfText", + "LF_WriteJSON" + ], + { + "title_aux": "LF Nodes [UNSAFE]" + } + ], + "https://github.com/lum3on/comfyui_LLM_Polymath": [ + [ + "ConceptEraserNode", + "flux_context_preset", + "polymath_SaveAbsolute", + "polymath_StringListPicker", + "polymath_TextSplitter", + "polymath_chat", + "polymath_helper", + "polymath_scraper", + "polymath_settings", + "polymath_text_mask" + ], + { + "title_aux": "comfyui_LLM_Polymath [WIP]" + } + ], + "https://github.com/lum3on/comfyui_RollingDepth": [ + [ + "RollingDepthNode" + ], + { + "title_aux": "comfyui_RollingDepth [WIP]" + } + ], + "https://github.com/machinesarenotpeople/comfyui-energycost": [ + [ + "TimeCostEndNode", + "TimeStartNode" + ], + { + "title_aux": "comfyui-energycost" + } + ], + "https://github.com/maizerrr/comfyui-code-nodes": [ + [ + "BBoxDrawNode", + "BBoxParseNode", + "DummyNode", + "ImageBatchNode", + "MaskEditorNode", + "OpenAIGPTImageNode", + "OpenAIQueryNode" + ], + { + "title_aux": null + } + ], + "https://github.com/majorsauce/comfyui_indieTools": [ + [ + "IndCutByMask", + "IndLocalScale", + "IndPastImage", + "IndSolidify", + "IndYoloDetector" + ], + { + "title_aux": "comfyui_indieTools [WIP]" + } + ], + "https://github.com/mamorett/ComfyUI-SmolVLM": [ + [ + "Smolvlm_Caption_Analyzer", + "Smolvlm_Flux_CLIPTextEncode", + "Smolvlm_SaveTags", + "Smolvlm_Tagger" + ], + { + "title_aux": "ComfyUI-SmolVLM [WIP]" + } + ], + "https://github.com/mamorett/comfyui_minicpm_vision": [ + [ + "MiniCPMVisionGGUF" + ], + { + "title_aux": "comfyui_minicpm_vision" + } + ], + "https://github.com/marcueberall/ComfyUI-BuildPath": [ + [ + "Build Path Adv" + ], + { + "title_aux": "ComfyUI-BuildPath" + } + ], + "https://github.com/marduk191/comfyui-marnodes": [ + [ + "ImageToDevice", + "marduk191_5_text_string", + "marduk191_5way_text_switch", + "marduk191_s_random_latent", + "marduk191_workflow_settings" + ], + { + "author": "\u02f6marduk191", + "description": "marduk191s nodes.", + "nickname": "marduk191 workflow settings", + "title": "marduk191 workflow settings", + "title_aux": "comfyui-marnodes" + } + ], + "https://github.com/maruhidd/ComfyUI_Transparent-Background": [ + [ + "FillTransparentNode", + "RemoveBackgroundNode" + ], + { + "title_aux": "Transparent Background for ComfyUI" + } + ], + "https://github.com/mashb1t/comfyui-nodes-mashb1t": [ + [ + "mashb1t: LoadImage" + ], + { + "title_aux": "ComfyUI mashb1t nodes" + } + ], + "https://github.com/masmullin2000/ComfyUI-MMYolo": [ + [ + "MMFace_Finder" + ], + { + "title_aux": "ComfyUI-MMYolo" + } + ], + "https://github.com/matDobek/ComfyUI_duck": [ + [ + "Combine Images (duck)" + ], + { + "title_aux": "ComfyUI_duck" + } + ], + "https://github.com/maurorilla/ComfyUI-MisterMR-Nodes": [ + [ + "AddLogo", + "AddSingleObject", + "AddSingleText", + "ColorNode" + ], + { + "title_aux": "ComfyUI-glb-to-stl [WIP]" + } + ], + "https://github.com/mehbebe/ComfyLoraGallery": [ + [ + "LoraGallery" + ], + { + "title_aux": "ComfyLoraGallery [WIP]" + } + ], + "https://github.com/melMass/ComfyUI-Lygia": [ + [ + "LygiaProgram", + "LygiaUniforms" + ], + { + "title_aux": "ComfyUI-Lygia" + } + ], + "https://github.com/mikebilly/Transparent-background-comfyUI": [ + [ + "Transparentbackground RemBg" + ], + { + "title_aux": "transparent-background-comfyui" + } + ], + "https://github.com/mikeymcfish/FishTools": [ + [ + "AnaglyphCreator", + "AnaglyphCreatorPro", + "Deptherize", + "LaserCutterFull", + "ShadowMap" + ], + { + "author": "Fish", + "description": "This extension provides tools for generating laser cutter ready files and other fun stuff", + "nickname": "FishTools", + "title": "FishTools", + "title_aux": "LaserCutterFull and Deptherize Nodes" + } + ], + "https://github.com/mikheys/ComfyUI-mikheys": [ + [ + "WanImageDimensions", + "WanOptimalResolution", + "\u0418\u043c\u044f\u0414\u043b\u044fComfyUI" + ], + { + "title_aux": "ComfyUI-mikheys" + } + ], + "https://github.com/minhtuannhn/comfyui-gemini-studio": [ + [ + "GetFileNameFromURL" + ], + { + "title_aux": "comfyui-gemini-studio [WIP]" + } + ], + "https://github.com/miragecoa/ComfyUI-LLM-Evaluation": [ + [ + "AccuracyNode", + "ClearVRAM", + "DeleteFile", + "DownloadHuggingFaceModel", + "F1ScoreNode", + "JSONToListNode", + "JsonResultGenerator", + "LLMLocalLoader", + "LoadFileNode", + "MathOperationNode", + "MyNode", + "PullOllamaModel", + "SelectItemByIndexNode", + "SelectItemByKeyNode", + "StringCombiner", + "StringPatternEnforcer", + "StringScraper", + "UpdateLLMResultToJson", + "WriteToJson" + ], + { + "title_aux": "ComfyUI-LLM-Evaluation [WIP]" + } + ], + "https://github.com/mliand/ComfyUI-Calendar-Node": [ + [ + "Comfy Calendar Node" + ], + { + "title_aux": "ComfyUI-Calendar-Node [WIP]" + } + ], + "https://github.com/mm-akhtar/comfyui-mask-selector-node": [ + [ + "Mask Selector" + ], + { + "title_aux": "comfyui-mask-selector-node" + } + ], + "https://github.com/mohamedsobhi777/ComfyUI-FramerComfy": [ + [ + "FramerComfyBooleanInputNode", + "FramerComfyFloatInputNode", + "FramerComfyInputImageNode", + "FramerComfyInputNumberNode", + "FramerComfyInputStringNode", + "FramerComfySaveImageNode" + ], + { + "title_aux": "ComfyUI-FramerComfy [WIP]" + } + ], + "https://github.com/molbal/comfy-url-fetcher": [ + [ + "URL Fetcher" + ], + { + "title_aux": "comfy-url-fetcher [WIP]" + } + ], + "https://github.com/moonwhaler/comfyui-moonpack": [ + [ + "DynamicLoraStack", + "DynamicStringConcat", + "ProportionalDimension", + "RegexStringReplace", + "SimpleStringReplace", + "VACELooperFrameMaskCreator" + ], + { + "title_aux": "comfyui-moonpack" + } + ], + "https://github.com/mr-krak3n/ComfyUI-Qwen": [ + [ + "Qwen2_Chat_Zho", + "Qwen2_ModelLoader_Zho", + "Qwen2_Zho" + ], + { + "title_aux": "ComfyUI-Qwen [CONFLICT]" + } + ], + "https://github.com/mut-ex/comfyui-gligengui-node": [ + [ + "GLIGEN_GUI" + ], + { + "title_aux": "ComfyUI GLIGEN GUI Node" + } + ], + "https://github.com/muvich3n/ComfyUI-Claude-I2T": [ + [ + "ClaudeImageToPrompt" + ], + { + "title_aux": "ComfyUI-Claude-I2T" + } + ], + "https://github.com/muvich3n/ComfyUI-Crop-Border": [ + [ + "CropImageBorder" + ], + { + "title_aux": "ComfyUI-Crop-Border" + } + ], + "https://github.com/naderzare/comfyui-inodes": [ + [ + "IAzureAiApi", + "ICutStrings", + "IFinalizeProject", + "IIfElse", + "ILLMExecute", + "ILLMExecute2", + "ILoadAzureAiApi", + "ILoadOllamaApi", + "IMergeImages", + "IMultilineSplitToStrings", + "IPassImage", + "IPostProcessLLMResponse", + "IPromptGenerator", + "IRandomChoiceToStrings", + "ISaveImage", + "ISaveText", + "IStringsCounter", + "IStringsToFile", + "IStringsToString", + "ITimesToStrings", + "IUploadToGoogleDrive", + "IZipImages" + ], + { + "title_aux": "comfyui-inodes" + } + ], + "https://github.com/neeltheninja/ComfyUI-TempFileDeleter": [ + [ + "TempCleaner" + ], + { + "title_aux": "ComfyUI-TempFileDeleter [UNSAFE]" + } + ], + "https://github.com/neeltheninja/ComfyUI-TextOverlay": [ + [ + "TextOverlay" + ], + { + "title_aux": "ComfyUI-TextOverlay" + } + ], + "https://github.com/neo0801/my-comfy-node": [ + [ + "DeepMosaicGetImageMosaicMask", + "DeepMosaicGetVideoMosaicMask", + "DeepMosaicRemoveImageMosaic", + "DeepMosaicRemoveVideoMosaic" + ], + { + "title_aux": "my-comfy-node" + } + ], + "https://github.com/neverbiasu/ComfyUI-ControlNeXt": [ + [ + "ControlNextPipelineConfig", + "ControlNextSDXL" + ], + { + "title_aux": "ComfyUI-ControlNeXt [WIP]" + } + ], + "https://github.com/neverbiasu/ComfyUI-DeepSeek": [ + [ + "DeepSeekCaller" + ], + { + "title_aux": "ComfyUI-DeepSeek" + } + ], + "https://github.com/neverbiasu/ComfyUI-Show-o": [ + [ + "ShowoImageCaptioning", + "ShowoImageInpainting", + "ShowoModelLoader", + "ShowoTextToImage" + ], + { + "title_aux": "ComfyUI-Show-o [WIP]" + } + ], + "https://github.com/neverbiasu/ComfyUI-StereoCrafter": [ + [ + "DepthSplattingModelLoader", + "DepthSplattingNode", + "InpaintingInferenceNode" + ], + { + "title_aux": "ComfyUI-StereoCrafter [WIP]" + } + ], + "https://github.com/newraina/ComfyUI-Remote-Save-Image": [ + [ + "RemoteImageSaver" + ], + { + "title_aux": "ComfyUI-Remote-Save-Image [UNSAFE]" + } + ], + "https://github.com/nidefawl/ComfyUI-nidefawl": [ + [ + "BlendImagesWithBoundedMasks", + "CropImagesWithMasks", + "CustomCallback", + "DisplayAnyType", + "EmptyImageWithColor", + "ImageToLatent", + "LatentPerlinNoise", + "LatentScaledNoise", + "LatentToImage", + "MaskFromColor", + "ModelSamplerTonemapNoiseTest", + "PythonScript", + "ReferenceOnlySimple", + "SamplerCustomCallback", + "SamplerDPMPP_2M_SDE_nidefawl", + "SetLatentCustomNoise", + "SplitCustomSigmas", + "VAELoaderDataType", + "gcLatentTunnel" + ], + { + "title_aux": "ComfyUI-nidefawl [UNSAFE]" + } + ], + "https://github.com/nikkuexe/ComfyUI-ListDataHelpers": [ + [ + "List Difference", + "VHS Output Filter", + "VHS_VideoOutputFilter" + ], + { + "title_aux": "List Data Helper Nodes" + } + ], + "https://github.com/nkchocoai/ComfyUI-PromptUtilities": [ + [ + "PromptUtilitiesConstString", + "PromptUtilitiesConstStringMultiLine", + "PromptUtilitiesFormatString", + "PromptUtilitiesJoinStringList", + "PromptUtilitiesLoadPreset", + "PromptUtilitiesLoadPresetAdvanced", + "PromptUtilitiesPromptWeight", + "PromptUtilitiesRandomPreset", + "PromptUtilitiesRandomPresetAdvanced", + "PromptUtilitiesReplaceOrInsertTag", + "PromptUtilitiesRoundPromptWeight", + "PromptUtilitiesSampleTags", + "PromptUtilitiesSampleTagsWithWeight" + ], + { + "title_aux": "ComfyUI-PromptUtilities" + } + ], + "https://github.com/nobandegani/comfyui_ino_nodes": [ + [ + "Ino_BranchImage", + "Ino_CalculateLoraConfig", + "Ino_CountFiles", + "Ino_DateTimeAsString", + "Ino_GetFolderBatchID", + "Ino_GetParentID", + "Ino_IntEqual", + "Ino_NotBoolean", + "Ino_ParseFilePath", + "Ino_RandomCharacterPrompt", + "Ino_SaveFile", + "Ino_SaveImage", + "Ino_StringToggleCase", + "Ino_VideoConvert" + ], + { + "title_aux": "Ino Custom Nodes" + } + ], + "https://github.com/nomcycle/ComfyUI_Cluster": [ + [ + "ClusterBroadcastLoadedImage", + "ClusterBroadcastTensor", + "ClusterEndSubgraph", + "ClusterExecuteCurrentWorkflow", + "ClusterExecuteWorkflow", + "ClusterFanInImages", + "ClusterFanOutImage", + "ClusterFanOutLatent", + "ClusterFanOutMask", + "ClusterFinallyFree", + "ClusterFlattenBatchedImageList", + "ClusterFreeNow", + "ClusterGatherImages", + "ClusterGatherLatents", + "ClusterGatherMasks", + "ClusterGetInstanceWorkItemFromBatch", + "ClusterInfo", + "ClusterInsertAtIndex", + "ClusterListenTensorBroadcast", + "ClusterSplitBatchToList", + "ClusterStartSubgraph", + "ClusterStridedReorder", + "ClusterUseSubgraph" + ], + { + "title_aux": "ComfyUI_Cluster [WIP]" + } + ], + "https://github.com/orion4d/ComfyUI_unified_list_selector": [ + [ + "UnifiedListSelector" + ], + { + "title_aux": "Unified List Selector for ComfyUI [UNSAFE]" + } + ], + "https://github.com/osuiso-depot/comfyui-keshigom_custom": [ + [ + "KANI_Checkpoint_Loader_From_String", + "KANI_MathExpression", + "KANI_Multiplexer", + "KANI_ShowAnything", + "KANI_TextFind", + "KANI_TrueorFalse", + "RegExTextChopper", + "ResolutionSelector", + "ResolutionSelectorConst", + "StringNodeClass" + ], + { + "title_aux": "comfyui-keshigom_custom" + } + ], + "https://github.com/owengillett/ComfyUI-tilefusion": [ + [ + "VideoGridCombine" + ], + { + "title_aux": "ComfyUI-tilefusion" + } + ], + "https://github.com/oxysoft/Comfy-Compel": [ + [ + "CLIPEmbedCompel" + ], + { + "title_aux": "Comfy-Compel" + } + ], + "https://github.com/oyvindg/ComfyUI-TrollSuite": [ + [ + "BinaryImageMask", + "ImagePadding", + "LoadLastImage", + "RandomMask", + "TransparentImage" + ], + { + "title_aux": "ComfyUI-TrollSuite" + } + ], + "https://github.com/oztrkoguz/ComfyUI_Kosmos2_BBox_Cutter": [ + [ + "Kosmos2SamplerSimple", + "KosmosLoader", + "Write" + ], + { + "title_aux": "Kosmos2_BBox_Cutter Models" + } + ], + "https://github.com/p1atdev/comfyui-aesthetic-predictor": [ + [ + "LoadAestheticPredictorNode", + "PredictAestheticScore" + ], + { + "title_aux": "comfyui-aesthetic-predictor" + } + ], + "https://github.com/pacchikAI/ImagePromptBatch": [ + [ + "LoadImageandPrompt" + ], + { + "title_aux": "ImagePromptBatch [UNSAFE]" + } + ], + "https://github.com/pamparamm/ComfyUI-ppm": [ + [ + "AttentionCouplePPM", + "CFGLimiterGuider", + "CFGPPSamplerSelect", + "CLIPMicroConditioning", + "CLIPNegPip", + "CLIPTextEncodeBREAK", + "CLIPTextEncodeInvertWeights", + "CLIPTokenCounter", + "ConditioningZeroOutCombine", + "ConvertTimestepToSigma", + "DynSamplerSelect", + "DynamicThresholdingPost", + "DynamicThresholdingSimplePost", + "EmptyLatentImageAR", + "FreeU2PPM", + "Guidance Limiter", + "LatentOperationTonemapLuminance", + "LatentToMaskBB", + "LatentToWidthHeight", + "MaskCompositePPM", + "PPMSamplerSelect", + "RenormCFGPost", + "RescaleCFGPost" + ], + { + "title_aux": "ComfyUI-ppm" + } + ], + "https://github.com/papcorns/ComfyUI-Papcorns-Node-UploadToGCS": [ + [ + "UploadImageToGCS" + ], + { + "title_aux": "ComfyUI-Papcorns-Node-UploadToGCS" + } + ], + "https://github.com/parmarjh/ComfyUI-MochiWrapper-I2V": [ + [ + "DownloadAndLoadMochiModel", + "MochiDecode", + "MochiDecodeSpatialTiling", + "MochiFasterCache", + "MochiImageEncode", + "MochiLatentPreview", + "MochiModelLoader", + "MochiSampler", + "MochiSigmaSchedule", + "MochiTextEncode", + "MochiTorchCompileSettings", + "MochiVAEEncoderLoader", + "MochiVAELoader" + ], + { + "title_aux": "ComfyUI-MochiWrapper-I2V [WIP]" + } + ], + "https://github.com/paulhoux/Smart-Prompting": [ + [ + "SaveImageWithPrefix", + "TextAppend", + "TextCharacterSelector", + "TextEncodeReusable", + "TextFile", + "TextNegatives", + "TextSearchReplace", + "TextString", + "TextStyleSelector" + ], + { + "title_aux": "List Data Helper Nodes" + } + ], + "https://github.com/phamngoctukts/ComyUI-Tupham": [ + [ + "AreaCondition_v2", + "ConditionUpscale", + "MultiLatent", + "Runnodeselected", + "ghepanh" + ], + { + "title_aux": "ComyUI-Tupham" + } + ], + "https://github.com/pictorialink/ComfyUI-static-resource": [ + [ + "StaticResource" + ], + { + "title_aux": "comfyui-static-resource[UNSAFE]" + } + ], + "https://github.com/pinkpixel-dev/comfyui-llm-prompt-enhancer": [ + [ + "PromptEnhancer" + ], + { + "title_aux": "ComfyUI LLM Prompt Enhancer [WIP]" + } + ], + "https://github.com/pixixai/ComfyUI_Pixix-Tools": [ + [ + "BaiduTranslateNode", + "ChatGLM4TranslateTextNode", + "ColorPicker", + "LoadTextFromFolderNode" + ], + { + "title_aux": "ComfyUI_Pixix-Tools [UNSAFE/WIP]" + } + ], + "https://github.com/pixuai/ComfyUI-PixuAI": [ + [ + "PromptSearch" + ], + { + "title_aux": "ComfyUI-PixuAI" + } + ], + "https://github.com/pmarmotte2/Comfyui-VibeVoiceSelector": [ + [ + "VibeVoiceSelector" + ], + { + "title_aux": "VibeVoiceSelector [WIP]" + } + ], + "https://github.com/poisenbery/NudeNet-Detector-Provider": [ + [ + "NudeNetDetectorProvider" + ], + { + "title_aux": "NudeNet-Detector-Provider [WIP]" + } + ], + "https://github.com/pomelyu/cy-prompt-tools": [ + [ + "CY_LLM", + "CY_LoadPrompt", + "CY_LoadPrompt4", + "CY_LoadPromptPro", + "CY_PromptComposer", + "CY_TextBox" + ], + { + "title_aux": "cy-prompt-tools" + } + ], + "https://github.com/power88/ComfyUI-PDiD-Nodes": [ + [ + "Blend Images", + "Check Character Tag", + "Get Image Colors", + "Get image size", + "List Operations", + "Make Image Gray", + "Nearest SDXL Resolution divided by 64" + ], + { + "title_aux": "ComfyUI-PDiD-Nodes [WIP]" + } + ], + "https://github.com/prabinpebam/anyPython": [ + [ + "Any Python" + ], + { + "author": "prabinpebam", + "description": "This node can execute Python operations with user-confirmed risk management", + "nickname": "anyPython", + "title": "anyPython v0.0.3", + "title_aux": "anyPython [UNSAFE]" + } + ], + "https://github.com/prodogape/ComfyUI-clip-interrogator": [ + [ + "ComfyUIClipInterrogator", + "ShowText" + ], + { + "title_aux": "ComfyUI-clip-interrogator [WIP]" + } + ], + "https://github.com/przewodo/ComfyUI-Przewodo-Utils": [ + [ + "przewodo AppendToAnyList", + "przewodo BatchImagesFromPath", + "przewodo CompareNumbersToCombo", + "przewodo DebugLatentShapes", + "przewodo FloatIfElse", + "przewodo HasInputvalue", + "przewodo ImageScaleFactor", + "przewodo ImageSizer", + "przewodo IsInputDisabled", + "przewodo SwapAnyComparison", + "przewodo SwapAnyCondition", + "przewodo SwapImageComparison", + "przewodo WanFirstLastFirstFrameToVideo", + "przewodo WanGetMaxImageResolutionByAspectRatio", + "przewodo WanImageToVideoAdvancedSampler", + "przewodo WanModelTypeSelector", + "przewodo WanPromptChunkStacker", + "przewodo WanVideoEnhanceAVideo", + "przewodo WanVideoGenerationModeSelector", + "przewodo WanVideoLoraStack", + "przewodo WanVideoVaeDecode" + ], + { + "title_aux": "ComfyUI-Przewodo-Utils [WIP]" + } + ], + "https://github.com/pschroedl/ComfyUI-StreamDiffusion": [ + [ + "StreamDiffusionAdvancedConfig", + "StreamDiffusionCheckpointLoader", + "StreamDiffusionConfig", + "StreamDiffusionLPCheckpointLoader", + "StreamDiffusionLoraLoader", + "StreamDiffusionPrebuiltConfig", + "StreamDiffusionSampler", + "StreamDiffusionTensorRTEngineLoader" + ], + { + "title_aux": "ComfyUI-StreamDiffusion" + } + ], + "https://github.com/pzzmyc/comfyui-sd3-simple-simpletuner": [ + [ + "sd not very simple simpletuner by hhy" + ], + { + "title_aux": "comfyui-sd3-simple-simpletuner" + } + ], + "https://github.com/qlikpetersen/ComfyUI-AI_Tools": [ + [ + "CreateListJSON", + "CreateListString", + "DoLogin", + "FixLinksAndRevLinks", + "HttpRequest", + "Image_Attachment", + "IncludeInSpiderData", + "JSON_Attachment", + "Json2String", + "LoadSpiderData", + "PNGtoImage", + "Query_OpenAI", + "RemoveCircularReferences", + "RunPython", + "RunPythonGriptapeTool", + "SaveSpiderData", + "SpiderCrawl", + "SpiderSplit", + "String2Json", + "String_Attachment", + "TextMultiSave" + ], + { + "author": "kierdran", + "description": "Tools for agentic testing", + "nickname": "ai_tools", + "title": "AI_Tools", + "title_aux": "ComfyUI-AI_Tools [UNSAFE]" + } + ], + "https://github.com/rakete/comfyui-rakete": [ + [ + "rakete.BuildString", + "rakete.GetWidgetValue", + "rakete.GpuGarbageCollector", + "rakete.JoinStrings" + ], + { + "author": "Rakete", + "description": "Rakete Comfy Custom Nodes", + "nickname": "Rakete Nodes", + "title": "Rakete Nodes", + "title_aux": "comfyui-rakete" + } + ], + "https://github.com/rakki194/ComfyUI_WolfSigmas": [ + [ + "GetImageSize", + "LatentVisualizeDirect", + "ListModelBlocks", + "ModifyActivationsSVD", + "VisualizeActivation", + "WolfDCTNoise", + "WolfPlotSamplerStatsNode", + "WolfProbeGetData", + "WolfProbeSetup", + "WolfSamplerCustomAdvancedPlotter", + "WolfSamplerScriptEvaluator", + "WolfScriptableEmptyLatent", + "WolfScriptableLatentAnalyzer", + "WolfScriptableNoise", + "WolfSigmaAddNoise", + "WolfSigmaClampT0", + "WolfSigmaClipValues", + "WolfSigmaGeometricProgression", + "WolfSigmaInsertValue", + "WolfSigmaNormalizeRange", + "WolfSigmaPolynomial", + "WolfSigmaPowerTransform", + "WolfSigmaQuantize", + "WolfSigmaRespaceLogCosine", + "WolfSigmaReverse", + "WolfSigmaReverseAndRescale", + "WolfSigmaScriptEvaluator", + "WolfSigmaShiftAndScale", + "WolfSigmaSlice", + "WolfSigmaTanhGenerator", + "WolfSigmasGet", + "WolfSigmasSet", + "WolfSigmasToJSON", + "WolfSimpleSamplerScriptEvaluator", + "WolfSimpleScriptableEmptyLatent" + ], + { + "title_aux": "ComfyUI_WolfSigmas [UNSAFE]" + } + ], + "https://github.com/ralonsobeas/ComfyUI-HDRConversion": [ + [ + "HDRConversion" + ], + { + "title_aux": "ComfyUI-HDRConversion [WIP]" + } + ], + "https://github.com/realm-weaver/ComfyUI-tile-seamstress-360": [ + [ + "RW_EquirectangularMask", + "RW_EquirectangularSphericalRotate", + "RW_PostSeamlessUpscale", + "RW_PreSeamlessUpscale", + "RW_ResizeToProperRatio", + "RW_SegmentBlend" + ], + { + "author": "realm-weaver", + "description": "Tools for touching up seams and poles on 360\u00b0 panoramic images and flat tilings.", + "nickname": "ts360", + "title": "Tile Seamstress 360", + "title_aux": "Tile Seamstress 360\u00b0 [WIP]" + } + ], + "https://github.com/redhottensors/ComfyUI-ODE": [ + [ + "ODESamplerSelect" + ], + { + "author": "RedHotTensors", + "description": "Adaptive ODE Solvers for ComfyUI", + "nickname": "ComfyUI-ODE", + "title": "ComfyUI-ODE", + "title_aux": "ComfyUI-ODE" + } + ], + "https://github.com/retech995/Save_Florence2_Bulk_Prompts": [ + [ + "SaveTextFlorence" + ], + { + "title_aux": "ComfyUI_SaveImageBulk [UNSAFE]" + } + ], + "https://github.com/rhinoflavored/comfyui_QT": [ + [ + "CSVDataMatcher", + "QTAutoCropByNPS", + "QTExcelImageReader", + "QTExcelReader", + "QTRandomSelectString", + "QTStringWrappingByNumber", + "QT_Alpha_Yaxis_Node", + "QT_AntiAliasing_Node", + "QT_Batch_Anything_Node", + "QT_Center_Rotation", + "QT_Character_Height_Difference", + "QT_Character_Size_Node", + "QT_Color_Image_Loop", + "QT_Content_Location_Node", + "QT_Crop_Alpha", + "QT_Crop_Alpha_V2", + "QT_Curves_Node", + "QT_Dictionary_Node", + "QT_Elements_Into_List_Node", + "QT_Float_To_Int", + "QT_Image_Array", + "QT_Image_Array_Circle", + "QT_Image_Array_Rectangle", + "QT_Image_Overlay", + "QT_Image_Overlay_BOOLEAN", + "QT_Image_Overlay_Rotation", + "QT_Image_Overlay_V2", + "QT_Image_Overlay_V3", + "QT_Image_Sorting_Node", + "QT_Image_Upscale_And_Crop_Node", + "QT_Image_Upscale_And_Crop_Node_V2", + "QT_Image_Upscale_And_Crop_Node_V3", + "QT_Image_Upscale_Node", + "QT_Image_Vision_Center_Node", + "QT_Join_Image_List_Node", + "QT_Line_Break", + "QT_Line_Break_V2", + "QT_List_Length", + "QT_List_Picker", + "QT_List_To_String", + "QT_Mask_Mix_Node", + "QT_Merge_Into_List_Node", + "QT_Pageturn_Node", + "QT_Pattern_Fill", + "QT_Piecewise_Function_Node", + "QT_Polar_Coordinate_Conversion_Node", + "QT_Rounded_Corner", + "QT_SUPIR_Upscale", + "QT_Simple_Text_Image_V2", + "QT_Sorting_Node", + "QT_Split_List_Node", + "QT_Split_List_Node_V2", + "QT_Split_Mask_Node", + "QT_Split_String", + "QT_String_Horizontal_To_Vertical", + "QT_String_To_List", + "QT_Text_Input_Switch_Node", + "QT_Text_Overlay_V2", + "QT_Text_To_Bool_Node", + "QT_Tilt_Transform", + "QT_Translucent_Node", + "QT_Vertical_Text_Overlay", + "QT_Video_Combine_Node" + ], + { + "title_aux": "comfyui_QT" + } + ], + "https://github.com/ricklove/ComfyUI-AutoSeg-SAM2": [ + [ + "AutoSegSAM2Node" + ], + { + "title_aux": "ComfyUI-AutoSeg-SAM2" + } + ], + "https://github.com/rickyars/sd-cn-animation": [ + [ + "SDCNAnimation", + "SDCNAnimationAdvanced" + ], + { + "title_aux": "sd-cn-animation" + } + ], + "https://github.com/rishipandey125/ComfyUI-FramePacking": [ + [ + "Batch Keyframes", + "Get Image Dimensions", + "Image Mix RGB", + "Pad Batch to 4n+1", + "Resize Frame", + "Slot Frame", + "Threshold Image", + "Trim Padded Batch" + ], + { + "title_aux": "ComfyUI-FramePacking [WIP]" + } + ], + "https://github.com/risunobushi/ComfyUI_FaceMesh_Eyewear_Mask": [ + [ + "FaceMeshEyewearMask", + "MaskFromFacialKeypoints", + "OpenPoseEyewearMask" + ], + { + "title_aux": "ComfyUI_FaceMesh_Eyewear_Mask" + } + ], + "https://github.com/risunobushi/ComfyUI_FocusMask": [ + [ + "FocusMaskExtractor", + "FocusOutlineExtractor" + ], + { + "title_aux": "ComfyUI_FocusMask" + } + ], + "https://github.com/risunobushi/ComfyUI_HEXtoRGB": [ + [ + "HexToRGB" + ], + { + "title_aux": "ComfyUI_HEXtoRGB" + } + ], + "https://github.com/ritikvirus/comfyui-terminal-modal-node": [ + [ + "terminal_node" + ], + { + "title_aux": "ComfyUI Terminal Command Node [UNSAFE]" + } + ], + "https://github.com/ronalds-eu/comfyui-plus-integrations": [ + [ + "ImagePassThrough", + "ImageToS3" + ], + { + "title_aux": "comfyui-plus-integrations [WIP]" + } + ], + "https://github.com/rouxianmantou/comfyui-rxmt-nodes": [ + [ + "CheckValueTypeNode", + "ConvertStrToIntNode", + "TextCombineWithCommaNode", + "WhyPromptTextNode" + ], + { + "title_aux": "comfyui-rxmt-nodes" + } + ], + "https://github.com/rphmeier/comfyui-videodepthanything": [ + [ + "VideoDepthAnythingLoader", + "VideoDepthAnythingProcess" + ], + { + "title_aux": "comfyui-videodepthanything" + } + ], + "https://github.com/ruka-game/rukalib_comfyui": [ + [ + "RukaDebugProbe", + "RukaPromptEnhancer" + ], + { + "title_aux": "ComfyUI RukaLib [WIP]" + } + ], + "https://github.com/ryanontheinside/ComfyUI-Livepeer": [ + [ + "BatchInfo", + "BatchIterator", + "LivepeerA2T", + "LivepeerAudioJobGetter", + "LivepeerI2I", + "LivepeerI2T", + "LivepeerI2V", + "LivepeerImageJobGetter", + "LivepeerLLM", + "LivepeerLive2Video", + "LivepeerSegment", + "LivepeerT2I", + "LivepeerT2S", + "LivepeerTextJobGetter", + "LivepeerUpscale", + "LivepeerVideoJobGetter" + ], + { + "title_aux": "ComfyUI-Livepeer [WIP]" + } + ], + "https://github.com/ryanontheinside/ComfyUI-MineWorld": [ + [ + "MineWorldGenerateFrame", + "MineWorldGenerateSequence", + "MineWorldInitialState", + "MineWorldModelLoader" + ], + { + "title_aux": "ComfyUI MineWorld Nodes [WIP]" + } + ], + "https://github.com/ryanontheinside/ComfyUI_YoloNasObjectDetection_Tensorrt": [ + [ + "YoloNasDetectionTensorrt" + ], + { + "title_aux": "ComfyUI_YoloNasObjectDetection_Tensorrt [WIP]" + } + ], + "https://github.com/sdfxai/SDFXBridgeForComfyUI": [ + [ + "SDFXClipTextEncode" + ], + { + "title_aux": "SDFXBridgeForComfyUI - ComfyUI Custom Node for SDFX Integration" + } + ], + "https://github.com/seancheung/comfyui-creative-nodes": [ + [ + "CreativeSkipFromFlow", + "CreativeSkipToFlow", + "CreativeStopFlow", + "ResolutionSelector", + "ResolutionXLSelector" + ], + { + "title_aux": "comfyui-creative-nodes" + } + ], + "https://github.com/sh570655308/Comfyui-RayNodes": [ + [ + "BorderMask", + "BracketedTagIndexMerger", + "Florence2TagProcessor", + "GrabberTagProcessor", + "ImageListConverter", + "ImageResizer", + "ImageSelector", + "MaskApplierAndCombiner", + "MaskBlackener", + "MaskProcessor", + "SaturationAdjuster", + "SaveImageWebsocket", + "TagArrayToLines", + "TagIndexMerger" + ], + { + "title_aux": "Comfyui-RayNodes [WIP]" + } + ], + "https://github.com/shadowcz007/ComfyUI-PuLID-Test": [ + [ + "ApplyPulid", + "PulidEvaClipLoader", + "PulidInsightFaceLoader", + "PulidModelLoader" + ], + { + "title_aux": "ComfyUI-PuLID [TEST]" + } + ], + "https://github.com/shadowcz007/Comfyui-EzAudio": [ + [ + "EZGenerateAudioNode", + "EZLoadModelNode" + ], + { + "title_aux": "Comfyui-EzAudio [WIP]" + } + ], + "https://github.com/shadowcz007/comfyui-CLIPSeg": [ + [ + "CLIPSeg_", + "CombineMasks_" + ], + { + "title_aux": "comfyui-CLIPSeg" + } + ], + "https://github.com/shadowcz007/comfyui-hydit-lowvram": [ + [ + "DiffusersCLIPLoader", + "DiffusersCheckpointLoader", + "DiffusersClipTextEncode", + "DiffusersControlNetLoader", + "DiffusersLoraLoader", + "DiffusersModelMakeup", + "DiffusersPipelineLoader", + "DiffusersSampler", + "DiffusersSchedulerLoader", + "DiffusersVAELoader" + ], + { + "title_aux": "comfyui-hydit" + } + ], + "https://github.com/shinich39/comfyui-nothing-happened": [ + [ + "NothingHappened" + ], + { + "author": "shinich39", + "description": "Save image and keep metadata.", + "nickname": "comfyui-nothing-happened", + "title": "comfyui-nothing-happened", + "title_aux": "comfyui-nothing-happened" + } + ], + "https://github.com/shinich39/comfyui-run-js": [ + [ + "RunJS" + ], + { + "author": "shinich39", + "description": "Manipulate workflow via javascript on node.", + "nickname": "comfyui-run-js", + "title": "comfyui-run-js", + "title_aux": "comfyui-run-js [UNSAFE]" + } + ], + "https://github.com/shirazdesigner/CLIPTextEncodeAndEnhancev4": [ + [ + "CLIPTextEncodeAndEnhance" + ], + { + "title_aux": "CLIPTextEncodeAndEnhancev4 (shirazdesigner)" + } + ], + "https://github.com/shuanshuan/ComfyUI_CheckPointLoader_Ext": [ + [ + "CheckpointLoaderExt" + ], + { + "title_aux": "ComfyUI_CheckPointLoader_Ext [WIP]" + } + ], + "https://github.com/silent-rain/ComfyUI-SilentRain": [ + [ + "Example" + ], + { + "title_aux": "ComfyUI-SilentRain" + } + ], + "https://github.com/silveroxides/ComfyUI_ReduxEmbedToolkit": [ + [ + "LoadReduxEmb", + "LoadT5XXLConds", + "LoadT5XXLEmb", + "SaveCondsEmb", + "SaveReduxEmb" + ], + { + "title_aux": "ComfyUI_ReduxEmbedToolkit" + } + ], + "https://github.com/simonjaq/ComfyUI-sjnodes": [ + [ + "CrossFadeVideo", + "InpaintCropImprovedGPU", + "InpaintStitchImprovedGPU", + "LoadStitcherFromFile", + "LogCRec709Convert", + "SaveStitcherToFile", + "SmoothTemporalMask", + "WanVideoVACEExtend" + ], + { + "title_aux": "ComfyUI-sjnodes" + } + ], + "https://github.com/siyonomicon/ComfyUI-Pin": [ + [ + "PinGridNode" + ], + { + "title_aux": "ComfyUI-Pin" + } + ], + "https://github.com/smthemex/ComfyUI_GPT_SoVITS_Lite": [ + [ + "GPT_SoVITS_LoadModel", + "GPT_SoVITS_Sampler" + ], + { + "title_aux": "ComfyUI_GPT_SoVITS_Lite" + } + ], + "https://github.com/smthemex/ComfyUI_MangaNinjia": [ + [ + "MangaNinjiaLoader", + "MangaNinjiaSampler", + "MarkImageNode" + ], + { + "title_aux": "ComfyUI_MangaNinjia [WIP]" + } + ], + "https://github.com/sofakid/dandy": [ + [ + "DandyBooleanCollector", + "DandyBooleanPreview", + "DandyBooleanSplitter", + "DandyCss", + "DandyCssLoader", + "DandyEditorSettings", + "DandyFloatCollector", + "DandyFloatPreview", + "DandyFloatSplitter", + "DandyGradient", + "DandyHtml", + "DandyHtmlLoader", + "DandyImageCollector", + "DandyIntCollector", + "DandyIntPreview", + "DandyIntSplitter", + "DandyJs", + "DandyJsLoader", + "DandyJson", + "DandyJsonLoader", + "DandyLand", + "DandyMaskCollector", + "DandyP5JsDraw", + "DandyP5JsLoader", + "DandyP5JsSetup", + "DandyPixelsJs", + "DandyPixiJs", + "DandyPrompt", + "DandyString", + "DandyStringArrayCollector", + "DandyStringArraySplitter", + "DandyStringCatCollector", + "DandyStringPreview", + "DandyUrlLoader", + "DandyWasmLoader", + "DandyYaml", + "DandyYamlLoader" + ], + { + "title_aux": "dandy [UNSAFE]" + } + ], + "https://github.com/songtianhui/ComfyUI-DMM": [ + [ + "DMMApply", + "DMMLoader" + ], + { + "title_aux": "ComfyUI-DMM [WIP]" + } + ], + "https://github.com/sourceful-official/ComfyUI_InstructPixToPixConditioningLatent": [ + [ + "InstructPixToPixConditioningLatent" + ], + { + "title_aux": "ComfyUI_InstructPixToPixConditioningLatent [WIP]" + } + ], + "https://github.com/sourceful-official/comfyui-sourceful-official": [ + [ + "FalFluxLoraSourcefulOfficial", + "FalIcLightV2SourcefulOfficial", + "SourcefulOfficialComfyuiIncontextThreePanels" + ], + { + "title_aux": "comfyui-sourceful-official" + } + ], + "https://github.com/sswink/comfyui-lingshang": [ + [ + "LS_ALY_Seg_Body_Utils", + "LS_ALY_Seg_Body_Utils_Return_crop", + "LS_ALY_Seg_Clothes_Utils", + "LS_ALY_Seg_Common_Utils", + "LS_ALY_Seg_Utils", + "LS_ALY_UploadToOssAndGetUrl", + "LS_DigImageByMask", + "LS_GrowMaskWithBlur", + "LS_LoadMaskFromUrl", + "LS_SaveImageToOss" + ], + { + "title_aux": "comfyui-lingshang" + } + ], + "https://github.com/stalkervr/comfyui-custom-path-nodes": [ + [ + "BatchImageCrop", + "ContextPipeIn", + "ContextPipeOut", + "ContextPipeReroute", + "DataFileLoader", + "ImageGridCropper", + "PathPipeIn", + "PathPipeOut", + "PathPipeReroute", + "PromptPartConcatenation", + "PromptPartJoin", + "SavePath", + "StringConcatenation" + ], + { + "title_aux": "comfyui-custom-path-nodes [UNSAFE]" + } + ], + "https://github.com/steelan9199/ComfyUI-Teeth": [ + [ + "teeth FindContours", + "teeth Gemini2", + "teeth GetFirstSeg", + "teeth GetValueByIndexFromList", + "teeth ImageGridLines", + "teeth LoadTextFile", + "teeth RunPythonCode", + "teeth SaveTextFile", + "teeth SplitGridImage", + "teeth TextSplitByDelimiter" + ], + { + "title_aux": "ComfyUI-Teeth [UNSAFE]" + } + ], + "https://github.com/strhwste/comfyui_csv_utils": [ + [ + "ExtractFromJSON", + "SearchCSVByRow", + "WriteCSVByRow" + ], + { + "title_aux": "CSV Utils [WIP]" + } + ], + "https://github.com/stutya/ComfyUI-Terminal": [ + [ + "Terminal" + ], + { + "title_aux": "ComfyUI-Terminal [UNSAFE]" + } + ], + "https://github.com/subnet99/ComfyUI-URLLoader": [ + [ + "URLAudioDownloader", + "URLImageDownloader" + ], + { + "title_aux": "ComfyUI-URLLoader" + } + ], + "https://github.com/sugarkwork/comfyui_image_crop": [ + [ + "CropReapply", + "CropTransparent", + "ExpandMultiple", + "RestoreCrop" + ], + { + "title_aux": "comfyui_image_crop" + } + ], + "https://github.com/sugarkwork/comfyui_my_img_util": [ + [ + "Auto Image Selector", + "Simple Image Rotate" + ], + { + "title_aux": "comfyui_my_img_util" + } + ], + "https://github.com/sugarkwork/comfyui_psd": [ + [ + "Convert PSD to Image", + "PSDLayer", + "Save PSD" + ], + { + "title_aux": "comfyui_psd [WIP]" + } + ], + "https://github.com/suncat2ps/ComfyUI-SaveImgNextcloud": [ + [ + "SaveImageNextcloud" + ], + { + "title_aux": "ComfyUI-SaveImgNextcloud" + } + ], + "https://github.com/system-out-cho/displayHistory_ComfyUI": [ + [ + "Client Proxy", + "DisplayHistory", + "Example", + "Image Selector" + ], + { + "title_aux": "displayHistory [WIP]" + } + ], + "https://github.com/takoyaki1118/ComfyUI_PromptExtractor": [ + [ + "CustomLoadImageWithPathNode", + "PromptExtractorNode" + ], + { + "title_aux": "ComfyUI_PromptExtractor" + } + ], + "https://github.com/talon468/ComfyUI-Rpg-Architect": [ + [ + "ComfyUI Rpg Architect \ud83e\ude84" + ], + { + "title_aux": "ComfyUI-Rpg-Architect [WIP]" + } + ], + "https://github.com/tankenyuen-ola/comfyui-env-variable-reader": [ + [ + "EnvironmentVariableNode" + ], + { + "title_aux": "comfyui-env-variable-reader [UNSAFE]" + } + ], + "https://github.com/tankenyuen-ola/comfyui-wanvideo-scheduler-loop": [ + [ + "FloatRangeLoop", + "ParametersRangeLoop", + "WanVideoAllParametersLoop", + "WanVideoSchedulerInfo", + "WanVideoSchedulerLoop", + "WanVideoSchedulerSelector" + ], + { + "title_aux": "comfyui-wanvideo-scheduler-loop" + } + ], + "https://github.com/tanmoy-it/comfyuiCustomNode": [ + [ + "DownloadImageDataUrl" + ], + { + "title_aux": "comfyuiCustomNode" + } + ], + "https://github.com/tc888/ComfyUI_Save_Flux_Image": [ + [ + "Cfg Literal", + "Int Literal", + "Sampler Select", + "Save Flux Image with Metadata", + "Scheduler Select", + "Seed Gen", + "String Literal", + "Unet Select", + "Width/Height Literal" + ], + { + "title_aux": "ComfyUI_Save_Flux_Image" + } + ], + "https://github.com/techidsk/comfyui_molook_nodes": [ + [ + "ImageOutpaintPadding(Molook)", + "MaskExpand(Molook)", + "OpenAIProvider(Molook)" + ], + { + "title_aux": "comfyui_molook_nodes [WIP]" + } + ], + "https://github.com/techtruth/ComfyUI-Dreambooth": [ + [ + "DreamboothNode" + ], + { + "title_aux": "ComfyUI-Dreambooth" + } + ], + "https://github.com/techzuhaib/ComfyUI-CacheImageNode": [ + [ + "CacheImageNode" + ], + { + "title_aux": "ComfyUI-CacheImageNode" + } + ], + "https://github.com/thavocado/comfyui-danbooru-lookup": [ + [ + "DanbooruFAISSLookup", + "DanbooruFAISSLookupAdvanced", + "WD14ToConditioning" + ], + { + "author": "ComfyUI Danbooru Lookup", + "description": "ComfyUI node that performs FAISS cosine similarity lookup on Danbooru embeddings using CLIP conditioning inputs.", + "nickname": "Danbooru Lookup", + "title": "Danbooru FAISS Lookup", + "title_aux": "comfyui-danbooru-lookup" + } + ], + "https://github.com/thderoo/ComfyUI-_topfun_s_nodes": [ + [ + "ConditioningPerturbation", + "TextGenerator" + ], + { + "title_aux": "_topfun_s_nodes" + } + ], + "https://github.com/threadedblue/MLXnodes": [ + [ + "MLXImg2Img", + "MLXText2Image" + ], + { + "title_aux": "MLXnodes [WIP]" + } + ], + "https://github.com/tjorbogarden/my-useful-comfyui-custom-nodes": [ + [ + "ImageSizer", + "KSamplerSDXLAdvanced" + ], + { + "title_aux": "my-useful-comfyui-custom-nodes" + } + ], + "https://github.com/tom-doerr/dspy_nodes": [ + [ + "Accepted Examples Viewer", + "Dataset Reader", + "DynamicOptionsNode", + "Few Shot CoT", + "Few Shot Control", + "Few Shot Review", + "FewShotReview", + "FewShotReviewServer", + "Model", + "Predict", + "Print Hello World", + "Show Text", + "ShowText|pysssss", + "String List Viewer", + "String Splitter", + "StringReverser", + "StringSplitter", + "Text Field", + "Text Output" + ], + { + "title_aux": "DSPy Nodes [WIP]" + } + ], + "https://github.com/tracerstar/comfyui-p5js-node": [ + [ + "HYPE_P5JSImage" + ], + { + "title_aux": "comfyui-p5js-node" + } + ], + "https://github.com/trampolin/comfy-ui-scryfall": [ + [ + "ScryfallCardInfoNode", + "ScryfallDecklistParserNode", + "ScryfallImageExtractorNode", + "ScryfallSearchNode" + ], + { + "title_aux": "comfy-ui-scryfall" + } + ], + "https://github.com/trashgraphicard/Albedo-Sampler-for-ComfyUI": [ + [ + "Make Seamless Tile", + "Sample Image" + ], + { + "title_aux": "Albedo-Sampler-for-ComfyUI" + } + ], + "https://github.com/truebillyblue/lC.ComfyUI_epistemic_nodes": [ + [ + "AddApplicationNode", + "AddInterpretationNode", + "AddObservationNode", + "CreatePbiNode", + "CreateRDSOTMComponentNode", + "GetMadaObjectNode", + "InitiateOiaNode", + "InitiateRDSOTMCycleNode", + "LcADKConfigNode", + "LcADKGuiInteractionNode", + "LcAddCommentToPbiNode", + "LcAnchorClickNode", + "LcApiLlmAgentNode", + "LcApplyDoneNode", + "LcEpistemicPipelineNode", + "LcFieldClickNode", + "LcFrameClickNode", + "LcGetPbiDetailsNode", + "LcKeymapClickNode", + "LcLinkPbiNode", + "LcMemWriteNode", + "LcReflectBoomNode", + "LcStartleNode", + "LcWebLlmAgentNode", + "QueryPbisNode", + "ShowTextNode", + "StoreMadaObjectNode", + "UpdatePbiNode", + "ViewOiaCycleNode", + "ViewRDSOTMCycleDetailsNode" + ], + { + "title_aux": "lC.ComfyUI_epistemic_nodes [WIP]" + } + ], + "https://github.com/tuckerdarby/ComfyUI-TDNodes": [ + [ + "HandTrackerNode", + "InstanceDiffusionLoader", + "InstanceTrackerPrompt", + "KSamplerBatchedNode", + "KSamplerRAVE", + "KSamplerTF", + "TemporalNetPreprocessor", + "TrackerNode", + "VideoTrackerPromptNode" + ], + { + "title_aux": "ComfyUI-TDNodes [WIP]" + } + ], + "https://github.com/turskeli/comfyui-SetWallpaper": [ + [ + "SetWallpaper" + ], + { + "title_aux": "comfyui-SetWallpaper" + } + ], + "https://github.com/tzsoulcap/ComfyUI-SaveImg-W-MetaData": [ + [ + "CAP Cfg Literal", + "CAP Checkpoint Selector", + "CAP Int Literal", + "CAP Load Image with Metadata", + "CAP Sampler Selector", + "CAP Save Image w/Metadata", + "CAP Scheduler Selector", + "CAP Seed Generator", + "CAP String Literal", + "CAP Tag Image", + "CAP Width/Height Literal" + ], + { + "title_aux": "ComfyUI-SaveImg-W-MetaData" + } + ], + "https://github.com/umisetokikaze/comfyui_mergekit": [ + [ + "DefineSaveName", + "LoadLR", + "LoadTarget", + "Merge", + "SetLayer", + "SetModels", + "SetTokenizer", + "get_skip" + ], + { + "title_aux": "comfyui_mergekit [WIP]" + } + ], + "https://github.com/unanan/ComfyUI-Dist": [ + [ + "LoadCheckpointFromLAN", + "LoadCheckpointFromURL", + "LoadImageFromLAN", + "LoadImageFromURL", + "LoadWorkflowFromLAN", + "LoadWorkflowFromURL" + ], + { + "title_aux": "ComfyUI-Dist [WIP]" + } + ], + "https://github.com/usman2003/ComfyUI-Classifiers": [ + [ + "GenderClassificationNode" + ], + { + "title_aux": "ComfyUI-Classifiers" + } + ], + "https://github.com/usman2003/ComfyUI-RaceDetect": [ + [ + "RaceDetectionNodeV2" + ], + { + "title_aux": "ComfyUI-RaceDetect" + } + ], + "https://github.com/usrname0/ComfyUI-AllergicPack": [ + [ + "FolderFileCounter_Allergic", + "IncrementorPlus", + "WanVideoVACEContextManager" + ], + { + "title_aux": "ComfyUI-AllergicPack [WIP]" + } + ], + "https://github.com/var1ableX/ComfyUI_Accessories": [ + [ + "ACC_AnyCast", + "AccMakeListNode", + "GetMaskDimensions", + "GetRandomDimensions", + "isImageEmpty", + "isMaskEmpty" + ], + { + "title_aux": "ComfyUI_Accessories" + } + ], + "https://github.com/vchopine/ComfyUI_Toolbox": [ + [ + "ModelAspectRatioSelector" + ], + { + "title_aux": "ComfyUI_Toolbox" + } + ], + "https://github.com/virallover/comfyui-virallover": [ + [ + "BrightnessCorrectionNode", + "ConcatHorizontalWithMask", + "DeHaloAlphaWithMaskTorch", + "DepthFitter", + "DownloadAndLoadLoraModelOnly", + "EdgeNoise", + "FeatheredSharpen", + "IterativeDeHaloAlphaWithMaskTorch" + ], + { + "title_aux": "comfyui-virallover" + } + ], + "https://github.com/visualbruno/ComfyUI-QRemeshify": [ + [ + "QRemeshify" + ], + { + "title_aux": "ComfyUI-QRemeshify" + } + ], + "https://github.com/vladp0727/Comfyui-with-Furniture": [ + [ + "GetMaskFromAlpha", + "GetQuadrilateralOutfit" + ], + { + "title_aux": "ComfyUI Simple Image Tools [WIP]" + } + ], + "https://github.com/vovler/ComfyUI-vovlerTools": [ + [ + "WD14BlackListLoader", + "WD14TaggerAndImageFilterer", + "WD14TensorRTModelLoader", + "WDTaggerONNXtoTENSORRT" + ], + { + "title_aux": "comfyui-vovlertools" + } + ], + "https://github.com/wTechArtist/ComfyUI_VVL_Segmentation": [ + [ + "Mask2FormerPanoptic", + "OneFormerPanoptic" + ], + { + "title_aux": "ComfyUI_VVL_Segmentation [WIP]" + } + ], + "https://github.com/wTechArtist/ComfyUI_VVL_VideoCamera": [ + [ + "ImageSequenceCameraEstimator", + "VVLColmapMVSDepthNode" + ], + { + "title_aux": "ComfyUI_VVL_VideoCamera" + } + ], + "https://github.com/wTechArtist/ComfyUI_vvl_BBOX": [ + [ + "vvl_BBoxInput" + ], + { + "title_aux": "ComfyUI_vvl_BBOX" + } + ], + "https://github.com/walterFeng/ComfyUI-Image-Utils": [ + [ + "Calculate Image Brightness", + "Calculate Image Contrast", + "Calculate Image Saturation", + "Color Similarity Checker", + "Crop Mask Util", + "Displace Filter", + "Image Fix (tensor shape convert)", + "Load Image (By Url)", + "Mask Refine (Aliyun)" + ], + { + "title_aux": "ComfyUI-Image-Utils" + } + ], + "https://github.com/warshanks/Shank-Tools": [ + [ + "HeightWidth", + "ResolutionDivider", + "TileCalculator" + ], + { + "title_aux": "Shank-Tools" + } + ], + "https://github.com/wasilone11/comfyui-sync-translate-node": [ + [ + "SyncTranslateInputNode", + "SyncTranslateNode" + ], + { + "title_aux": "comfyui-sync-translate-node" + } + ], + "https://github.com/watarika/ComfyUI-Text-Utility": [ + [ + "LoadTextFile", + "PromptsFromTextbox", + "RemoveComments", + "ReplaceVariables", + "SaveTextFile", + "StringsFromTextbox" + ], + { + "title_aux": "ComfyUI-Text-Utility [UNSAFE]" + } + ], + "https://github.com/watarika/ComfyUI-exit": [ + [ + "ExitComfyUI", + "FetchApi" + ], + { + "title_aux": "ComfyUI-exit [UNSAFE]" + } + ], + "https://github.com/waynepimpzhang/comfyui-opencv-brightestspot": [ + [ + "FindBrightestSpot" + ], + { + "title_aux": "FindBrightestSpot [WIP]" + } + ], + "https://github.com/whmc76/ComfyUI-AudioSuiteAdvanced": [ + [ + "AudioConcatenateFree", + "AudioSeparation", + "CharacterVocalExtractor", + "CharacterVocalExtractorMultiTrack", + "CombineAudioFromList", + "IndexSelectFromList", + "ListLength", + "LongTextSplitter", + "MakeAudioBatch", + "MultiSpeakerSpeechToText", + "SubtitleFileLoader" + ], + { + "title_aux": "ComfyUI-AudioSuiteAdvanced [WIP]" + } + ], + "https://github.com/wildminder/ComfyUI-MagCache": [ + [ + "MagCache" + ], + { + "title_aux": "ComfyUI-MagCache [NAME CONFLICT|WIP]" + } + ], + "https://github.com/willblaschko/ComfyUI-Unload-Models": [ + [ + "DeleteAnyObject", + "UnloadAllModels", + "UnloadOneModel" + ], + { + "title_aux": "ComfyUI-Unload-Models" + } + ], + "https://github.com/wilzamguerrero/Comfyui-zZzZz": [ + [ + "CaptureZNode", + "CompressFolderNode", + "CreateZNode", + "DeleteZNode", + "DownloadFileNode", + "InfiniteZNode", + "MoveZNode", + "RenameZNode", + "VideoZNode", + "ZFShareScreen" + ], + { + "title_aux": "Comfyui-zZzZz [UNSAFE]" + } + ], + "https://github.com/wordbrew/comfyui-wan-control-nodes": [ + [ + "WanWeightedControlToVideo" + ], + { + "title_aux": "WAN Control Nodes for ComfyUI [WIP]" + } + ], + "https://github.com/wormley/comfyui-wormley-nodes": [ + [ + "CheckpointVAELoaderSimpleText", + "CheckpointVAESelectorText", + "LoRA_Tag_To_Stack" + ], + { + "title_aux": "comfyui-wormley-nodes" + } + ], + "https://github.com/xgfone/ComfyUI_PromptLogoCleaner": [ + [ + "PromptLogoCleaner" + ], + { + "title_aux": "ComfyUI_PromptLogoCleaner" + } + ], + "https://github.com/xiaoyumu/ComfyUI-XYNodes": [ + [ + "AdjustImageColor", + "AppyColorToImage", + "PrimitiveBBOX", + "StringToBBOX" + ], + { + "title_aux": "ComfyUI-XYNodes" + } + ], + "https://github.com/xinyiSS/CombineMasksNode": [ + [ + "CombineMasksNode" + ], + { + "title_aux": "CombineMasksNode" + } + ], + "https://github.com/xl0/q_tools": [ + [ + "PreviewModelMetadata", + "QBlendLatent", + "QConcatLatentBatch", + "QGaussianLatent", + "QImageSizeSetter", + "QKSampler", + "QLatentOp", + "QLatentToShape", + "QLinearScheduler", + "QLoadLatent", + "QLoadLatentTimeline", + "QParamRandomizerRange", + "QParamaRandomizerList", + "QPreviewLatent", + "QReshapeLatent", + "QSamplerCustom", + "QSamplerEulerAncestral", + "QUniformLatent" + ], + { + "title_aux": "q_tools" + } + ], + "https://github.com/xmarked-ai/ComfyUI_misc": [ + [ + "BLIPMatcherX", + "BlendLatentsX", + "ColorCorrectionX", + "ColorSpaceConversionX", + "ColorTransferNodeX", + "CommonSourcesX", + "ConstantColorX", + "ConvexHullByMaskX", + "CropBorderX", + "DepthDisplaceX", + "EmptyLatentX", + "ExpressionsX", + "FourCornerPinMaskX", + "GaussianBlurX", + "GaussianMaskBlurX", + "IfConditionX", + "ImageCompositionX", + "ImageResizeX", + "ImageTileSquare", + "ImageUntileSquare", + "KSamplerComboX", + "LoopCloseX", + "LoopOpenX", + "LoraBatchSamplerX", + "RegionTesterNodeX", + "RegionalPromptSamplerX", + "RelightX", + "RemoveBackgroundX", + "SamplersTestX", + "SaveImageX", + "SelectiveDepthLoraBlocksX", + "SimpleBlockerX", + "SplineImageMask", + "TextConcatX", + "TextX", + "WhiteBalanceX" + ], + { + "title_aux": "ComfyUI_misc" + } + ], + "https://github.com/xqqe/honey_nodes": [ + [ + "ExtractLoRAName", + "Honey Lora Loader", + "HoneyBatchAspectRatio", + "HoneyLoraStackTags", + "HoneyTextConcat", + "Honey_LoRAStackRandom", + "Honey_LoRATags", + "Small Lora Loader", + "TagAdder" + ], + { + "title_aux": "honey_nodes [WIP]" + } + ], + "https://github.com/xzuyn/ComfyUI-xzuynodes": [ + [ + "CLIPLoaderXZ", + "CLIPTextEncodeXZ", + "DualCLIPLoaderXZ", + "FirstLastFrameXZ", + "ImageResizeKJ", + "ImageResizeXZ", + "TripleCLIPLoaderXZ", + "WanImageToVideoXZ" + ], + { + "title_aux": "xzuynodes-ComfyUI" + } + ], + "https://github.com/y4my4my4m/ComfyUI_Direct3DS2": [ + [ + "Direct3DS2ModelDownloader", + "Direct3DS2Node" + ], + { + "title_aux": "ComfyUI-Direct3DS2 [WIP]" + } + ], + "https://github.com/yamanacn/comfyui_qwen_object": [ + [ + "BBoxToSAM", + "DetectObject", + "LoadQwenModel", + "SortBBox" + ], + { + "title_aux": "comfyui_qwen_object [WIP]" + } + ], + "https://github.com/yamanacn/comfyui_qwenbbox": [ + [ + "BBoxToSAM_v2", + "LoadQwenModel_v2", + "QwenBbox" + ], + { + "title_aux": "comfyui_qwenbbox" + } + ], + "https://github.com/yanhuifair/ComfyUI-FairLab": [ + [ + "AppendTagsNode", + "Base64ToImageNode", + "BlacklistTagsNode", + "CLIPTranslatedNode", + "DownloadImageNode", + "FillAlphaNode", + "FixUTF8StringNode", + "FloatNode", + "ImageResizeNode", + "ImageToBase64Node", + "ImageToVideoNode", + "IntNode", + "LoadImageFromDirectoryNode", + "LoadImageFromURLNode", + "LoadStringFromDirectoryNode", + "LoadStringNode", + "OllamaClientNode", + "OllamaNode", + "PrependTagsNode", + "PrintAnyNode", + "PrintImageNode", + "RemoveDuplicateTagsNode", + "SaveImageToDirectoryNode", + "SaveStringToDirectoryNode", + "SequenceStringListNode", + "ShowStringNode", + "StringCombineNode", + "StringNode", + "TranslateStringNode", + "VideoToImageNode" + ], + { + "title_aux": "ComfyUI-FairLab" + } + ], + "https://github.com/yanhuifair/comfyui-deepseek": [ + [ + "DeepSeekChatNode", + "DeepSeekChatProNode", + "DeepSeekReasonerNode" + ], + { + "title_aux": "comfyui-deepseek [WIP]" + } + ], + "https://github.com/yanlang0123/ComfyUI_Lam": [ + [ + "AppParams", + "AspectRatio", + "AutioInfo", + "AutioPath", + "DoWhileEnd", + "DoWhileStart", + "EasyPromptSelecto", + "FaceFusion", + "ForEnd", + "ForInnerEnd", + "ForInnerStart", + "ForStart", + "GLM3Prompt", + "IdentifyingQR", + "IfInnerExecute", + "Image2Video", + "ImageAddMask", + "ImageBlank", + "ImageClone", + "ImageCropFaces", + "ImageLama", + "ImageToMasks", + "JyAnimationGroup", + "JyAnimationIn", + "JyAnimationOut", + "JyAudio2CaptionsGroup", + "JyAudioNative", + "JyCaptionsNative", + "JyEffectNative", + "JyMediaAnimation", + "JyMediaNative", + "JyMultiAudioGroup", + "JyMultiCaptionsGroup", + "JyMultiEffectGroup", + "JyMultiMediaGroup", + "JySaveDraft", + "JySaveOutDraft", + "JyTransition", + "LAM.OpenPoseEditorPlus", + "LamCommonHidden", + "LamCommonNames", + "LamCommonPrint", + "LamCommonPrintNoOutput", + "LamFaceAnalysisModels", + "LamGetPngInfo", + "LamHeyGemNode", + "LamHeyGemQueryNode", + "LamLoadImageBase64", + "LamLoadPathImage", + "LamLoadVideo", + "LamReadFileList", + "LamSamplerName", + "LamSaveAudio", + "LamSaveOnly", + "LamSaveVideo", + "LamScheduler", + "LamSwitcherCase", + "LamViewVideo", + "LamViewVideoOut", + "LoadDirImgPaths", + "LoadReplaceImage", + "LongTextToList", + "MultiControlNetApply", + "MultiGLIGENTextBoxApply", + "MultiIPAdapterRegional", + "MultiIntFormula", + "MultiMergeAudio", + "MultiMergeVideos", + "MultiParamFormula", + "MultiTextConcatenate", + "MultiTextEncode", + "MultiTextEncodeAdvanced", + "MultiTextSelelct", + "MultiTextSetArea", + "MultiTextSetGligen", + "MultiTextSetMask", + "OutDoWhileEnd", + "OutDoWhileStart", + "PreviewImageLam", + "PromptTranslator", + "QRCode", + "SaveImageLam", + "SaveImgOutputLam", + "SectionEnd", + "SectionStart", + "StyleSelecto", + "Text2AutioEdgeTts", + "TextListSelelct", + "VideoAddAudio", + "VideoExtractAudio", + "VideoFaceFusion", + "VideoPath", + "WaitImagSelector", + "ZhPromptTranslator" + ], + { + "title_aux": "ComfyUI_Lam" + } + ], + "https://github.com/yichengup/ComfyUI-Transition": [ + [ + "CircularSequenceTransition", + "CircularTransition", + "DualLineTransition", + "GradientTransition", + "LinearTransition", + "SequenceTransition" + ], + { + "title_aux": "ComfyUI-Transition" + } + ], + "https://github.com/yichengup/ComfyUI-YCNodes_Advance": [ + [ + "FaceDetectorSelector", + "HumanPartsUltra", + "YC Color Match", + "YCFaceAlignToCanvas", + "YCFaceAlignToCanvasV2", + "YCFaceAnalysisModels" + ], + { + "title_aux": "ComfyUI-YCNodes_Advance" + } + ], + "https://github.com/yichengup/Comfyui-NodeSpark": [ + [ + "ImageCircleWarp", + "ImageStretch", + "ImageWaveWarp", + "LiquifyNode" + ], + { + "title_aux": "Comfyui-NodeSpark" + } + ], + "https://github.com/yincangshiwei/ComfyUI-SEQLToolNode": [ + [ + "CanvasFusionNode", + "ImageCropByAlpha" + ], + { + "title_aux": "ComfyUI-SEQLToolNode" + } + ], + "https://github.com/yojimbodayne/ComfyUI-Dropbox-API": [ + [ + "FetchTokenFromDropbox", + "PostImagesToDropboxAPI", + "PostPromptsToDropboxAPI", + "PullImagesFromDropboxAPI", + "PullTextFromDropboxAPI", + "PullVideosFromDropboxAPI", + "VideoCombineAndExportToDropboxAPI" + ], + { + "title_aux": "ComfyUI-Dropbox-API [WIP]" + } + ], + "https://github.com/zackabrams/ComfyUI-KeySyncWrapper": [ + [ + "KeySyncAdvanced", + "KeySyncWrapper" + ], + { + "title_aux": "ComfyUI-KeySyncWrapper [WIP]" + } + ], + "https://github.com/zhaorishuai/ComfyUI-StoryboardDistributor": [ + [ + "StoryboardDistributor" + ], + { + "title_aux": "ComfyUI-StoryboardDistributor" + } + ], + "https://github.com/zhengxyz123/ComfyUI-CLIPSeg": [ + [ + "CLIPSegImage", + "CLIPSegText" + ], + { + "title_aux": "zhengxyz123/ComfyUI-CLIPSeg [NAME CONFLICT]" + } + ], + "https://github.com/zhongpei/Comfyui_image2prompt": [ + [ + "CLIP AdvancedTextEncode|fofo", + "CLIP PromptConditioning|fofo", + "Image2Text", + "Image2TextWithTags", + "ImageBatchToList|fofo", + "ImageRewardScore|fofo", + "LoadImage2TextModel", + "LoadImageRewardScoreModel|fofo", + "LoadT5Model|fofo", + "LoadText2PromptModel", + "ShowText|fofo", + "T5QuantizationConfig|fofo", + "T5Text2Prompt|fofo", + "Text2GPTPrompt", + "Text2Prompt", + "TextBox|fofo", + "Translate2Chinese|fofo" + ], + { + "title_aux": "Comfyui_image2prompt" + } + ], + "https://github.com/zhuanvi/ComfyUI-ZVNodes": [ + [ + "ImageCounterNodeZV", + "JoinListZV", + "JsonListIndexerZV", + "JsonListLengthZV", + "JsonListNodeZV", + "JsonListSlicerZV", + "JsonListToMaskZV", + "JsonReaderZV", + "LoadImageFromDirZV", + "LoadTxtFromDirZV", + "RandomSelectListZV", + "SaveImageToPathZV", + "SaveTxtToPathZV", + "TriangleCharacterLayoutZV", + "VideoSceneDetectorZV", + "VideoSpeedZV" + ], + { + "title_aux": "ComfyUI-ZVNodes [WIP]" + } + ], + "https://github.com/zjkhurry/comfyui_MetalFX": [ + [ + "metalFXImg" + ], + { + "title_aux": "comfyui_MetalFX [WIP]" + } + ], + "https://github.com/zl9739379/comfyui-qwen-vl-api": [ + [ + "VL_QwenDescribeImage" + ], + { + "title_aux": "ComfyUI Qwen Vision Language API Node [NAME CONFLICT]" + } + ], + "https://github.com/zopieux/ComfyUI-zopi": [ + [ + "EvalPython", + "LoadTensortRTAndCheckpoint" + ], + { + "title_aux": "ComfyUI-zopi [UNSAFE]" + } + ], + "https://github.com/zyd232/ComfyUI-zyd232-Nodes": [ + [ + "zyd232 ImagesPixelsCompare", + "zyd232_SavePreviewImages" + ], + { + "title_aux": "ComfyUI-zyd232-Nodes" + } + ], + "https://raw.githubusercontent.com/NeuralNotW0rk/ComfyUI-Waveform-Extensions/main/EXT_VariationUtils.py": [ + [ + "BatchToList", + "ConcatAudioList", + "SequenceVariation", + "SliceAudio" + ], + { + "title_aux": "ComfyUI-Waveform-Extensions" + } + ], + "https://raw.githubusercontent.com/jp0215/comfyUI_padding-resize_node/main/PaddingNode.py": [ + [ + "function" + ], + { + "title_aux": "comfyUI_padding-resize_node" + } + ], + "https://raw.githubusercontent.com/komojini/ComfyUI_Prompt_Template_CustomNodes/main/prompt_with_template.py": [ + [ + "ObjectPromptWithTemplate", + "PromptWithTemplate" + ], + { + "title_aux": "ComfyUI_Prompt_Template_CustomNodes" + } + ], + "https://raw.githubusercontent.com/okg21/VLLMVisionChatNode/refs/heads/main/VLLMVisionChatNode.py": [ + [ + "VLLMVisionChatNode" + ], + { + "title_aux": "VLLMVisionChatNode" + } + ], + "https://raw.githubusercontent.com/olyyarm/ComfyUI-VLMStudio/refs/heads/master/vlm_visionary_node_v3_.py": [ + [ + "GemmaMultimodalAnalyzer" + ], + { + "title_aux": "ComfyUI-VLMStudio" + } + ] +} \ No newline at end of file diff --git a/ComfyUI/custom_nodes/ComfyUI-Manager/node_db/dev/github-stats.json b/ComfyUI/custom_nodes/ComfyUI-Manager/node_db/dev/github-stats.json new file mode 100644 index 0000000000000000000000000000000000000000..b470b02f8df08cdd54a14dacbe8327138aacb538 --- /dev/null +++ b/ComfyUI/custom_nodes/ComfyUI-Manager/node_db/dev/github-stats.json @@ -0,0 +1,3677 @@ +{ + "https://github.com/123jimin/ComfyUI-MobileForm": { + "stars": 9, + "last_update": "2025-04-06 13:36:29", + "author_account_age_days": 5167 + }, + "https://github.com/17Retoucher/ComfyUI_Fooocus": { + "stars": 57, + "last_update": "2024-02-24 07:33:29", + "author_account_age_days": 573 + }, + "https://github.com/1H-hobit/ComfyUI_InternVL3": { + "stars": 0, + "last_update": "2025-07-28 06:21:53", + "author_account_age_days": 329 + }, + "https://github.com/1hew/ComfyUI-1hewNodes": { + "stars": 4, + "last_update": "2025-07-28 04:13:57", + "author_account_age_days": 849 + }, + "https://github.com/206811/ComfyUI_ZhipuAIO": { + "stars": 0, + "last_update": "2025-07-29 10:11:45", + "author_account_age_days": 1909 + }, + "https://github.com/3dmindscapper/ComfyUI-PartField": { + "stars": 30, + "last_update": "2025-05-01 02:50:39", + "author_account_age_days": 805 + }, + "https://github.com/3dmindscapper/ComfyUI-Sam-Mesh": { + "stars": 33, + "last_update": "2025-05-07 12:42:13", + "author_account_age_days": 805 + }, + "https://github.com/438443467/ComfyUI-SanMian-Nodes": { + "stars": 29, + "last_update": "2025-04-29 10:29:07", + "author_account_age_days": 815 + }, + "https://github.com/5x00/ComfyUI-Prompt-Plus": { + "stars": 1, + "last_update": "2025-01-08 15:54:08", + "author_account_age_days": 1373 + }, + "https://github.com/7BEII/Comfyui_PDuse": { + "stars": 9, + "last_update": "2025-07-30 13:18:10", + "author_account_age_days": 214 + }, + "https://github.com/A4P7J1N7M05OT/ComfyUI-ManualSigma": { + "stars": 1, + "last_update": "2024-12-30 10:45:23", + "author_account_age_days": 874 + }, + "https://github.com/A4P7J1N7M05OT/ComfyUI-VAELoaderSDXLmod": { + "stars": 0, + "last_update": "2025-06-23 23:42:45", + "author_account_age_days": 874 + }, + "https://github.com/A719689614/ComfyUI_AC_FUNV7-FLUX-": { + "stars": 0, + "last_update": "2025-07-25 12:51:58", + "author_account_age_days": 716 + }, + "https://github.com/A719689614/ComfyUI_AC_FUNV8Beta1": { + "stars": 13, + "last_update": "2025-07-24 09:42:22", + "author_account_age_days": 716 + }, + "https://github.com/AICodeFactory/ComfyUI-Viva": { + "stars": 1, + "last_update": "2025-05-15 08:07:12", + "author_account_age_days": 470 + }, + "https://github.com/AIFSH/ComfyUI-OpenDIT": { + "stars": 0, + "last_update": "2024-06-30 09:33:55", + "author_account_age_days": 633 + }, + "https://github.com/AIFSH/ComfyUI-ViViD": { + "stars": 5, + "last_update": "2024-06-25 08:16:53", + "author_account_age_days": 633 + }, + "https://github.com/AIFSH/HivisionIDPhotos-ComfyUI": { + "stars": 156, + "last_update": "2024-09-16 14:16:06", + "author_account_age_days": 633 + }, + "https://github.com/AIFSH/IMAGDressing-ComfyUI": { + "stars": 63, + "last_update": "2024-11-14 01:44:02", + "author_account_age_days": 633 + }, + "https://github.com/AIFSH/UltralightDigitalHuman-ComfyUI": { + "stars": 129, + "last_update": "2024-11-25 11:39:23", + "author_account_age_days": 633 + }, + "https://github.com/AIFSH/UtilNodes-ComfyUI": { + "stars": 14, + "last_update": "2024-12-19 06:44:25", + "author_account_age_days": 633 + }, + "https://github.com/AIWarper/ComfyUI-DAViD": { + "stars": 2, + "last_update": "2025-07-25 17:07:43", + "author_account_age_days": 279 + }, + "https://github.com/ALatentPlace/ComfyUI_yanc": { + "stars": 64, + "last_update": "2025-01-22 14:44:17", + "author_account_age_days": 1861 + }, + "https://github.com/APZmedia/comfyui-textools": { + "stars": 5, + "last_update": "2025-07-13 18:44:11", + "author_account_age_days": 2878 + }, + "https://github.com/Aero-Ex/comfyui_diffswap": { + "stars": 0, + "last_update": "2025-07-29 11:43:43", + "author_account_age_days": 1152 + }, + "https://github.com/AhBumm/ComfyUI-Upscayl": { + "stars": 0, + "last_update": "2025-02-19 09:41:02", + "author_account_age_days": 1210 + }, + "https://github.com/AhBumm/ComfyUI_MangaLineExtraction-hf": { + "stars": 0, + "last_update": "2025-05-02 18:47:09", + "author_account_age_days": 1210 + }, + "https://github.com/AkiEvansDev/ComfyUI-Tools": { + "stars": 0, + "last_update": "2025-06-28 14:48:29", + "author_account_age_days": 2719 + }, + "https://github.com/Alazuaka/comfyui-lora-stack-node": { + "stars": 0, + "last_update": "2025-06-12 23:14:31", + "author_account_age_days": 1188 + }, + "https://github.com/AlejandroTuzzi/TUZZI-ByPass": { + "stars": 5, + "last_update": "2025-05-13 14:04:56", + "author_account_age_days": 1646 + }, + "https://github.com/AlexXi19/ComfyUI-OpenAINode": { + "stars": 1, + "last_update": "2025-01-13 18:43:22", + "author_account_age_days": 1832 + }, + "https://github.com/AlexYez/comfyui-timesaver": { + "stars": 0, + "last_update": "2025-06-30 14:57:46", + "author_account_age_days": 1556 + }, + "https://github.com/AllenEdgarPoe/ComfyUI-Xorbis-nodes": { + "stars": 3, + "last_update": "2025-06-12 23:48:01", + "author_account_age_days": 2506 + }, + "https://github.com/Alvaroeai/ComfyUI-SunoAI-Mds": { + "stars": 0, + "last_update": "2025-01-11 21:13:41", + "author_account_age_days": 4103 + }, + "https://github.com/Anonymzx/ComfyUI-Indonesia-TTS": { + "stars": 0, + "last_update": "2025-05-07 14:33:50", + "author_account_age_days": 2230 + }, + "https://github.com/Anze-/ComfyUI-OIDN": { + "stars": 8, + "last_update": "2024-11-27 18:05:41", + "author_account_age_days": 4356 + }, + "https://github.com/Anze-/ComfyUI_deepDeband": { + "stars": 3, + "last_update": "2024-11-12 19:13:59", + "author_account_age_days": 4356 + }, + "https://github.com/ArmandAlbert/Kwai_font_comfyui": { + "stars": 1, + "last_update": "2025-01-14 04:02:21", + "author_account_age_days": 2393 + }, + "https://github.com/ArthusLiang/comfyui-face-remap": { + "stars": 4, + "last_update": "2024-11-30 12:34:28", + "author_account_age_days": 4417 + }, + "https://github.com/AustinMroz/ComfyUI-MinCache": { + "stars": 2, + "last_update": "2024-12-25 18:52:07", + "author_account_age_days": 4476 + }, + "https://github.com/AustinMroz/ComfyUI-WorkflowCheckpointing": { + "stars": 11, + "last_update": "2024-10-17 19:59:40", + "author_account_age_days": 4476 + }, + "https://github.com/BAIS1C/ComfyUI_BASICDancePoser": { + "stars": 0, + "last_update": "2025-07-22 17:13:31", + "author_account_age_days": 891 + }, + "https://github.com/BadCafeCode/execution-inversion-demo-comfyui": { + "stars": 75, + "last_update": "2025-03-09 00:44:37", + "author_account_age_days": 832 + }, + "https://github.com/BaronVonBoolean/ComfyUI-FileOps": { + "stars": 0, + "last_update": "2024-12-22 18:04:20", + "author_account_age_days": 240 + }, + "https://github.com/Baverne/comfyUI-TiledWan": { + "stars": 0, + "last_update": "2025-07-30 15:38:12", + "author_account_age_days": 872 + }, + "https://github.com/Beinsezii/comfyui-amd-go-fast": { + "stars": 43, + "last_update": "2025-04-21 19:37:22", + "author_account_age_days": 2627 + }, + "https://github.com/BenjaMITM/ComfyUI_On_The_Fly_Wildcards": { + "stars": 0, + "last_update": "2024-11-20 06:17:53", + "author_account_age_days": 349 + }, + "https://github.com/BetaDoggo/ComfyUI-LogicGates": { + "stars": 3, + "last_update": "2024-07-21 06:31:25", + "author_account_age_days": 1199 + }, + "https://github.com/Big-Idea-Technology/ComfyUI-Movie-Tools": { + "stars": 3, + "last_update": "2024-11-29 11:13:57", + "author_account_age_days": 1273 + }, + "https://github.com/BigStationW/flowmatch_scheduler-comfyui": { + "stars": 15, + "last_update": "2025-06-17 13:31:03", + "author_account_age_days": 87 + }, + "https://github.com/BinglongLi/ComfyUI_ToolsForAutomask": { + "stars": 1, + "last_update": "2025-06-04 11:56:53", + "author_account_age_days": 2086 + }, + "https://github.com/BlueDangerX/ComfyUI-BDXNodes": { + "stars": 1, + "last_update": "2023-12-10 04:01:19", + "author_account_age_days": 648 + }, + "https://github.com/BobRandomNumber/ComfyUI-DiaTTS": { + "stars": 8, + "last_update": "2025-06-02 03:02:19", + "author_account_age_days": 245 + }, + "https://github.com/Brandelan/ComfyUI_bd_customNodes": { + "stars": 2, + "last_update": "2024-09-08 01:04:38", + "author_account_age_days": 4553 + }, + "https://github.com/BrettMedia/comfyui-bhtools": { + "stars": 1, + "last_update": "2025-07-24 21:32:37", + "author_account_age_days": 61 + }, + "https://github.com/BuffMcBigHuge/ComfyUI-Buff-Nodes": { + "stars": 2, + "last_update": "2025-05-21 02:59:22", + "author_account_age_days": 3314 + }, + "https://github.com/Burgstall-labs/ComfyUI-BS_FalAi-API-Video": { + "stars": 3, + "last_update": "2025-06-19 06:47:25", + "author_account_age_days": 192 + }, + "https://github.com/Bwebbfx/ComfyUI_FaceParsing": { + "stars": 13, + "last_update": "2025-07-02 20:41:55", + "author_account_age_days": 628 + }, + "https://github.com/COcisuts/CObot-ComfyUI-WhisperToTranscription": { + "stars": 0, + "last_update": "2025-06-08 13:32:25", + "author_account_age_days": 3017 + }, + "https://github.com/CY-CHENYUE/ComfyUI-FramePack-HY": { + "stars": 18, + "last_update": "2025-05-08 09:38:09", + "author_account_age_days": 599 + }, + "https://github.com/CeeVeeR/ComfyUi-Text-Tiler": { + "stars": 0, + "last_update": "2025-03-25 20:26:18", + "author_account_age_days": 1475 + }, + "https://github.com/Chargeuk/ComfyUI-vts-nodes": { + "stars": 0, + "last_update": "2025-07-24 14:51:32", + "author_account_age_days": 4509 + }, + "https://github.com/Charonartist/ComfyUI-send-eagle-pro_2": { + "stars": 0, + "last_update": "2025-05-26 12:12:47", + "author_account_age_days": 395 + }, + "https://github.com/ChrisColeTech/ComfyUI-Get-Random-File": { + "stars": 3, + "last_update": "2025-06-19 03:10:17", + "author_account_age_days": 2814 + }, + "https://github.com/Clelstyn/ComfyUI-Inpaint_with_Detailer": { + "stars": 1, + "last_update": "2024-11-02 12:04:53", + "author_account_age_days": 719 + }, + "https://github.com/Clybius/ComfyUI-FluxDeCLIP": { + "stars": 1, + "last_update": "2024-11-17 20:06:29", + "author_account_age_days": 2135 + }, + "https://github.com/Comfy-Org/ComfyUI_devtools": { + "stars": 20, + "last_update": "2025-05-10 16:23:35", + "author_account_age_days": 476 + }, + "https://github.com/D1-3105/ComfyUI-VideoStream": { + "stars": 0, + "last_update": "2025-02-17 04:02:01", + "author_account_age_days": 1904 + }, + "https://github.com/DataCTE/ComfyUI-DataVoid-nodes": { + "stars": 0, + "last_update": "2024-11-20 14:20:31", + "author_account_age_days": 1179 + }, + "https://github.com/DeTK/ComfyUI-Switch": { + "stars": 0, + "last_update": "2024-03-04 11:52:04", + "author_account_age_days": 2432 + }, + "https://github.com/DenRakEiw/DenRakEiw_Nodes": { + "stars": 0, + "last_update": "2025-07-25 15:55:52", + "author_account_age_days": 1432 + }, + "https://github.com/DiffusionWave-YT/DiffusionWave_PickResolution": { + "stars": 0, + "last_update": "2025-06-29 23:55:17", + "author_account_age_days": 51 + }, + "https://github.com/DoctorDiffusion/ComfyUI-Flashback": { + "stars": 0, + "last_update": "2024-11-11 01:37:43", + "author_account_age_days": 743 + }, + "https://github.com/DonutsDelivery/ComfyUI-DonutNodes": { + "stars": 7, + "last_update": "2025-07-26 02:15:08", + "author_account_age_days": 127 + }, + "https://github.com/DrMWeigand/ComfyUI_LineBreakInserter": { + "stars": 0, + "last_update": "2024-04-19 11:37:19", + "author_account_age_days": 1433 + }, + "https://github.com/DraconicDragon/ComfyUI_e621_booru_toolkit": { + "stars": 5, + "last_update": "2025-06-09 19:31:11", + "author_account_age_days": 1775 + }, + "https://github.com/Dream-Pixels-Forge/ComfyUI-Mzikart-Player": { + "stars": 0, + "last_update": "2025-07-18 15:11:25", + "author_account_age_days": 2257 + }, + "https://github.com/DreamsInAutumn/ComfyUI-Autumn-LLM-Nodes": { + "stars": 0, + "last_update": "2025-06-14 06:14:05", + "author_account_age_days": 1268 + }, + "https://github.com/Dreamshot-io/ComfyUI-Extend-Resolution": { + "stars": 0, + "last_update": "2025-06-02 07:15:00", + "author_account_age_days": 254 + }, + "https://github.com/ELiZswe/ComfyUI-ELiZTools": { + "stars": 0, + "last_update": "2025-06-24 06:14:44", + "author_account_age_days": 2225 + }, + "https://github.com/EQXai/ComfyUI_EQX": { + "stars": 0, + "last_update": "2025-06-27 21:55:53", + "author_account_age_days": 429 + }, + "https://github.com/Eagle-CN/ComfyUI-Addoor": { + "stars": 51, + "last_update": "2025-04-25 01:03:58", + "author_account_age_days": 3028 + }, + "https://github.com/Elawphant/ComfyUI-MusicGen": { + "stars": 6, + "last_update": "2024-05-11 13:33:24", + "author_account_age_days": 2989 + }, + "https://github.com/ElyZeng/ComfyUI-Translator": { + "stars": 0, + "last_update": "2025-07-31 03:12:40", + "author_account_age_days": 1156 + }, + "https://github.com/Elypha/ComfyUI-Prompt-Helper": { + "stars": 0, + "last_update": "2025-03-03 21:42:14", + "author_account_age_days": 2933 + }, + "https://github.com/EmanueleUniroma2/ComfyUI-FLAC-to-WAV": { + "stars": 0, + "last_update": "2025-01-26 11:25:43", + "author_account_age_days": 3047 + }, + "https://github.com/EmilioPlumed/ComfyUI-Math": { + "stars": 1, + "last_update": "2025-01-11 14:28:42", + "author_account_age_days": 2379 + }, + "https://github.com/EricRollei/Comfy-Metadata-System": { + "stars": 2, + "last_update": "2025-04-28 23:42:26", + "author_account_age_days": 1296 + }, + "https://github.com/Estanislao-Oviedo/ComfyUI-CustomNodes": { + "stars": 0, + "last_update": "2025-07-23 18:10:07", + "author_account_age_days": 2582 + }, + "https://github.com/ExponentialML/ComfyUI_LiveDirector": { + "stars": 37, + "last_update": "2024-04-09 19:01:49", + "author_account_age_days": 2025 + }, + "https://github.com/Extraltodeus/Conditioning-token-experiments-for-ComfyUI": { + "stars": 18, + "last_update": "2024-03-10 01:04:02", + "author_account_age_days": 3551 + }, + "https://github.com/FaberVS/MultiModel": { + "stars": 1, + "last_update": "2025-05-06 14:27:08", + "author_account_age_days": 2172 + }, + "https://github.com/Fannovel16/ComfyUI-AppIO": { + "stars": 0, + "last_update": "2024-12-01 16:37:19", + "author_account_age_days": 3533 + }, + "https://github.com/Filexor/File_x_dynamic_prompt2": { + "stars": 0, + "last_update": "2025-07-29 16:19:34", + "author_account_age_days": 4349 + }, + "https://github.com/FinetunersAI/comfyui-fast-group-link": { + "stars": 0, + "last_update": "2024-12-09 17:35:50", + "author_account_age_days": 422 + }, + "https://github.com/FinetunersAI/finetuners": { + "stars": 1, + "last_update": "2025-01-06 16:29:33", + "author_account_age_days": 422 + }, + "https://github.com/FoundD-oka/ComfyUI-kisekae-OOTD": { + "stars": 0, + "last_update": "2024-06-02 06:13:42", + "author_account_age_days": 839 + }, + "https://github.com/Fucci-Mateo/ComfyUI-Airtable": { + "stars": 1, + "last_update": "2024-06-25 13:35:18", + "author_account_age_days": 1280 + }, + "https://github.com/GalactusX31/ComfyUI-FileBrowserAPI": { + "stars": 4, + "last_update": "2025-06-13 20:53:11", + "author_account_age_days": 2728 + }, + "https://github.com/GentlemanHu/ComfyUI-Notifier": { + "stars": 4, + "last_update": "2024-07-14 15:38:44", + "author_account_age_days": 2785 + }, + "https://github.com/George0726/ComfyUI-video-accessory": { + "stars": 1, + "last_update": "2025-05-19 14:18:22", + "author_account_age_days": 2653 + }, + "https://github.com/Grant-CP/ComfyUI-LivePortraitKJ-MPS": { + "stars": 12, + "last_update": "2024-07-11 22:04:16", + "author_account_age_days": 1575 + }, + "https://github.com/Grey3016/Save2Icon": { + "stars": 2, + "last_update": "2025-01-06 15:18:57", + "author_account_age_days": 730 + }, + "https://github.com/GrindHouse66/ComfyUI-GH_Tools": { + "stars": 0, + "last_update": "2024-03-10 13:27:14", + "author_account_age_days": 1025 + }, + "https://github.com/Haoming02/comfyui-resharpen": { + "stars": 49, + "last_update": "2025-07-16 01:45:14", + "author_account_age_days": 1732 + }, + "https://github.com/Hapseleg/ComfyUI-This-n-That": { + "stars": 0, + "last_update": "2025-06-03 20:26:27", + "author_account_age_days": 3697 + }, + "https://github.com/HuangYuChuh/ComfyUI-LLMs-Toolkit": { + "stars": 13, + "last_update": "2025-06-23 09:42:46", + "author_account_age_days": 466 + }, + "https://github.com/Huangcj2005/comfyui-HandDetect": { + "stars": 0, + "last_update": "2025-07-29 11:41:58", + "author_account_age_days": 630 + }, + "https://github.com/IfnotFr/ComfyUI-Ifnot-Pack": { + "stars": 0, + "last_update": "2025-02-05 08:51:23", + "author_account_age_days": 4986 + }, + "https://github.com/IgPoly/ComfyUI-igTools": { + "stars": 0, + "last_update": "2024-09-11 08:48:57", + "author_account_age_days": 329 + }, + "https://github.com/IsItDanOrAi/ComfyUI-exLoadout": { + "stars": 6, + "last_update": "2025-07-11 02:36:28", + "author_account_age_days": 513 + }, + "https://github.com/IuvenisSapiens/ComfyUI_MiniCPM-V-2_6-int4": { + "stars": 187, + "last_update": "2025-04-02 16:32:54", + "author_account_age_days": 812 + }, + "https://github.com/IvanZhd/comfyui-codeformer": { + "stars": 0, + "last_update": "2023-12-02 20:51:52", + "author_account_age_days": 2980 + }, + "https://github.com/Jaxkr/comfyui-terminal-command": { + "stars": 1, + "last_update": "2023-12-03 10:31:40", + "author_account_age_days": 5029 + }, + "https://github.com/JayLyu/ComfyUI_BaiKong_Node": { + "stars": 8, + "last_update": "2025-07-12 15:27:51", + "author_account_age_days": 3670 + }, + "https://github.com/JiSenHua/ComfyUI-yolov5-face": { + "stars": 1, + "last_update": "2025-07-14 17:02:39", + "author_account_age_days": 1145 + }, + "https://github.com/Jiffies-64/ComfyUI-SaveImagePlus": { + "stars": 0, + "last_update": "2024-04-01 10:52:59", + "author_account_age_days": 1293 + }, + "https://github.com/Jingwen-genies/comfyui-genies-nodes": { + "stars": 0, + "last_update": "2025-05-13 19:36:45", + "author_account_age_days": 735 + }, + "https://github.com/JioJe/comfyui_video_BC": { + "stars": 9, + "last_update": "2025-07-21 07:41:37", + "author_account_age_days": 425 + }, + "https://github.com/JissiChoi/ComfyUI-Jissi-List": { + "stars": 0, + "last_update": "2024-12-24 08:24:27", + "author_account_age_days": 2620 + }, + "https://github.com/JoeAu/ComfyUI-PythonNode": { + "stars": 3, + "last_update": "2025-03-16 13:05:38", + "author_account_age_days": 4587 + }, + "https://github.com/Jordach/comfy-consistency-vae": { + "stars": 69, + "last_update": "2023-11-06 20:50:40", + "author_account_age_days": 4914 + }, + "https://github.com/Jpzz/comfyui-ixiworks": { + "stars": 0, + "last_update": "2025-07-21 08:09:52", + "author_account_age_days": 2724 + }, + "https://github.com/Junst/ComfyUI-PNG2SVG2PNG": { + "stars": 0, + "last_update": "2024-12-04 02:25:04", + "author_account_age_days": 2940 + }, + "https://github.com/KERRY-YUAN/ComfyUI_Python_Executor": { + "stars": 1, + "last_update": "2025-04-07 07:49:03", + "author_account_age_days": 1655 + }, + "https://github.com/Kayarte/Time-Series-Nodes-for-ComfyUI": { + "stars": 1, + "last_update": "2025-01-29 02:33:25", + "author_account_age_days": 455 + }, + "https://github.com/KihongK/comfyui-roysnodes": { + "stars": 0, + "last_update": "2025-01-23 09:11:02", + "author_account_age_days": 1963 + }, + "https://github.com/KoreTeknology/ComfyUI-Nai-Production-Nodes-Pack": { + "stars": 11, + "last_update": "2024-11-24 15:55:30", + "author_account_age_days": 3591 + }, + "https://github.com/Krish-701/RK_Comfyui": { + "stars": 0, + "last_update": "2025-04-17 17:18:52", + "author_account_age_days": 257 + }, + "https://github.com/Kur0butiMegane/Comfyui-StringUtils2": { + "stars": 0, + "last_update": "2025-05-04 16:34:13", + "author_account_age_days": 2054 + }, + "https://github.com/KurtHokke/ComfyUI_KurtHokke_Nodes": { + "stars": 1, + "last_update": "2025-03-27 19:04:42", + "author_account_age_days": 226 + }, + "https://github.com/LAOGOU-666/Comfyui_StartPatch": { + "stars": 49, + "last_update": "2025-02-24 17:22:34", + "author_account_age_days": 496 + }, + "https://github.com/LK-168/comfyui_LK_selfuse": { + "stars": 0, + "last_update": "2025-07-10 09:55:05", + "author_account_age_days": 31 + }, + "https://github.com/LZpenguin/ComfyUI-Text": { + "stars": 23, + "last_update": "2024-06-20 13:38:16", + "author_account_age_days": 2380 + }, + "https://github.com/LarryJane491/ComfyUI-ModelUnloader": { + "stars": 4, + "last_update": "2024-01-14 08:22:39", + "author_account_age_days": 565 + }, + "https://github.com/Laser-one/ComfyUI-align-pose": { + "stars": 0, + "last_update": "2024-11-01 09:34:31", + "author_account_age_days": 1234 + }, + "https://github.com/Letz-AI/ComfyUI-LetzAI": { + "stars": 0, + "last_update": "2025-07-10 13:46:04", + "author_account_age_days": 739 + }, + "https://github.com/Lilien86/Comfyui_Latent_Interpolation": { + "stars": 1, + "last_update": "2024-09-03 21:00:49", + "author_account_age_days": 895 + }, + "https://github.com/Linsoo/ComfyUI-Linsoo-Custom-Nodes": { + "stars": 0, + "last_update": "2025-04-25 09:23:18", + "author_account_age_days": 4493 + }, + "https://github.com/Looking-Glass/LKG-ComfyUI": { + "stars": 5, + "last_update": "2024-10-30 17:02:54", + "author_account_age_days": 3386 + }, + "https://github.com/LotzF/ComfyUI-Simple-Chat-GPT-completion": { + "stars": 0, + "last_update": "2025-02-27 15:07:36", + "author_account_age_days": 1331 + }, + "https://github.com/LucianGnn/ComfyUI-Lucian": { + "stars": 0, + "last_update": "2025-06-18 06:47:37", + "author_account_age_days": 2280 + }, + "https://github.com/LucianoCirino/ComfyUI-invAIder-Nodes": { + "stars": 0, + "last_update": "2025-07-27 22:04:59", + "author_account_age_days": 1065 + }, + "https://github.com/LucipherDev/ComfyUI-Sentinel": { + "stars": 34, + "last_update": "2025-04-07 14:53:13", + "author_account_age_days": 1898 + }, + "https://github.com/LyazS/ComfyUI-aznodes": { + "stars": 0, + "last_update": "2025-06-03 14:57:29", + "author_account_age_days": 3258 + }, + "https://github.com/LykosAI/ComfyUI-Inference-Core-Nodes": { + "stars": 35, + "last_update": "2025-04-05 22:22:31", + "author_account_age_days": 780 + }, + "https://github.com/M4lF3s/comfy-tif-support": { + "stars": 0, + "last_update": "2025-02-12 09:29:11", + "author_account_age_days": 3625 + }, + "https://github.com/MakkiShizu/ComfyUI-MakkiTools": { + "stars": 4, + "last_update": "2025-07-27 22:50:19", + "author_account_age_days": 714 + }, + "https://github.com/Malloc-pix/comfyui-QwenVL": { + "stars": 0, + "last_update": "2025-06-24 09:35:32", + "author_account_age_days": 50 + }, + "https://github.com/ManuShamil/ComfyUI_BodyEstimation_Nodes": { + "stars": 0, + "last_update": "2025-02-28 19:23:24", + "author_account_age_days": 2554 + }, + "https://github.com/MarkFreeDom168/ComfyUI-image-load-url": { + "stars": 0, + "last_update": "2025-07-17 02:47:42", + "author_account_age_days": 1747 + }, + "https://github.com/Matrix-King-Studio/ComfyUI-MoviePy": { + "stars": 0, + "last_update": "2024-12-10 01:50:42", + "author_account_age_days": 1865 + }, + "https://github.com/Maxim-Dey/ComfyUI-MaksiTools": { + "stars": 3, + "last_update": "2025-02-08 08:04:03", + "author_account_age_days": 814 + }, + "https://github.com/Mervent/comfyui-telegram-send": { + "stars": 0, + "last_update": "2025-07-12 07:22:09", + "author_account_age_days": 3221 + }, + "https://github.com/Mervent/comfyui-yaml-prompt": { + "stars": 0, + "last_update": "2025-06-01 06:55:16", + "author_account_age_days": 3221 + }, + "https://github.com/MickeyJ/ComfyUI_mickster_nodes": { + "stars": 0, + "last_update": "2025-02-07 02:29:12", + "author_account_age_days": 3615 + }, + "https://github.com/MockbaTheBorg/ComfyUI-Mockba": { + "stars": 0, + "last_update": "2025-05-20 17:39:21", + "author_account_age_days": 3481 + }, + "https://github.com/MrAdamBlack/CheckProgress": { + "stars": 1, + "last_update": "2024-01-10 08:02:18", + "author_account_age_days": 3121 + }, + "https://github.com/MuAIGC/ComfyUI-DMXAPI_mmx": { + "stars": 4, + "last_update": "2025-05-26 06:58:45", + "author_account_age_days": 324 + }, + "https://github.com/MythicalChu/ComfyUI-APG_ImYourCFGNow": { + "stars": 32, + "last_update": "2024-11-29 17:45:03", + "author_account_age_days": 1896 + }, + "https://github.com/NEZHA625/ComfyUI-tools-by-dong": { + "stars": 1, + "last_update": "2025-07-30 18:32:39", + "author_account_age_days": 859 + }, + "https://github.com/Nambi24/ComfyUI-Save_Image": { + "stars": 0, + "last_update": "2025-05-05 15:05:27", + "author_account_age_days": 1286 + }, + "https://github.com/NicholasKao1029/comfyui-hook": { + "stars": 0, + "last_update": "2024-03-07 05:50:56", + "author_account_age_days": 2432 + }, + "https://github.com/No-22-Github/ComfyUI_SaveImageCustom": { + "stars": 0, + "last_update": "2025-06-26 06:33:38", + "author_account_age_days": 821 + }, + "https://github.com/Northerner1/ComfyUI_North_Noise": { + "stars": 1, + "last_update": "2025-03-01 12:32:29", + "author_account_age_days": 843 + }, + "https://github.com/Novavision0313/ComfyUI-NVVS": { + "stars": 1, + "last_update": "2025-06-12 03:27:13", + "author_account_age_days": 69 + }, + "https://github.com/OSAnimate/ComfyUI-SpriteSheetMaker": { + "stars": 1, + "last_update": "2025-03-12 04:22:34", + "author_account_age_days": 848 + }, + "https://github.com/Oct7/ComfyUI-LaplaMask": { + "stars": 0, + "last_update": "2025-06-03 07:45:26", + "author_account_age_days": 2003 + }, + "https://github.com/OgreLemonSoup/ComfyUI-Notes-manager": { + "stars": 2, + "last_update": "2025-06-25 07:24:16", + "author_account_age_days": 354 + }, + "https://github.com/PATATAJEC/ComfyUI-PatatajecNodes": { + "stars": 2, + "last_update": "2025-07-19 20:53:01", + "author_account_age_days": 2336 + }, + "https://github.com/Pablerdo/ComfyUI-Sa2VAWrapper": { + "stars": 3, + "last_update": "2025-03-27 22:58:39", + "author_account_age_days": 3202 + }, + "https://github.com/PabloGrant/comfyui-giraffe-test-panel": { + "stars": 0, + "last_update": "2025-05-18 16:38:09", + "author_account_age_days": 688 + }, + "https://github.com/PaleBloodq/ComfyUI-HFTransformers": { + "stars": 0, + "last_update": "2025-07-11 12:01:43", + "author_account_age_days": 1231 + }, + "https://github.com/PeterMikhai/Doom_Flux_NodePack": { + "stars": 1, + "last_update": "2025-06-30 20:41:45", + "author_account_age_days": 634 + }, + "https://github.com/Poseidon-fan/ComfyUI-fileCleaner": { + "stars": 1, + "last_update": "2024-11-19 02:42:29", + "author_account_age_days": 983 + }, + "https://github.com/Poukpalaova/ComfyUI-FRED-Nodes": { + "stars": 4, + "last_update": "2025-06-08 17:29:57", + "author_account_age_days": 723 + }, + "https://github.com/QingLuanWithoutHeart/comfyui-file-image-utils": { + "stars": 1, + "last_update": "2025-04-08 11:13:50", + "author_account_age_days": 2426 + }, + "https://github.com/Quasimondo/ComfyUI-QuasimondoNodes": { + "stars": 14, + "last_update": "2025-06-09 08:58:42", + "author_account_age_days": 5676 + }, + "https://github.com/QuietNoise/ComfyUI-Queue-Manager": { + "stars": 12, + "last_update": "2025-07-01 02:08:55", + "author_account_age_days": 4607 + }, + "https://github.com/RLW-Chars/comfyui-promptbymood": { + "stars": 1, + "last_update": "2025-01-25 11:21:59", + "author_account_age_days": 186 + }, + "https://github.com/RUFFY-369/ComfyUI-FeatureBank": { + "stars": 0, + "last_update": "2025-03-07 19:30:55", + "author_account_age_days": 1878 + }, + "https://github.com/Raidez/comfyui-kuniklo-collection": { + "stars": 0, + "last_update": "2025-05-02 19:44:45", + "author_account_age_days": 4075 + }, + "https://github.com/RamonGuthrie/ComfyUI-RBG-LoraConverter": { + "stars": 15, + "last_update": "2025-07-29 16:10:27", + "author_account_age_days": 562 + }, + "https://github.com/RicherdLee/comfyui-oss-image-save": { + "stars": 0, + "last_update": "2024-12-10 09:08:39", + "author_account_age_days": 4045 + }, + "https://github.com/RobeSantoro/ComfyUI-RobeNodes": { + "stars": 0, + "last_update": "2025-06-14 10:29:07", + "author_account_age_days": 5018 + }, + "https://github.com/Rocky-Lee-001/ComfyUI_SZtools": { + "stars": 1, + "last_update": "2025-07-17 02:14:52", + "author_account_age_days": 857 + }, + "https://github.com/RoyKillington/miscomfy-nodes": { + "stars": 0, + "last_update": "2025-03-06 19:36:33", + "author_account_age_days": 2807 + }, + "https://github.com/SKBv0/ComfyUI-RetroEngine": { + "stars": 4, + "last_update": "2025-05-10 14:29:43", + "author_account_age_days": 1957 + }, + "https://github.com/SS-snap/ComfyUI-Snap_Processing": { + "stars": 62, + "last_update": "2025-04-25 04:54:44", + "author_account_age_days": 701 + }, + "https://github.com/SS-snap/Comfyui_SSsnap_pose-Remapping": { + "stars": 30, + "last_update": "2025-07-25 09:49:47", + "author_account_age_days": 701 + }, + "https://github.com/SXQBW/ComfyUI-Qwen3": { + "stars": 0, + "last_update": "2025-04-18 06:06:49", + "author_account_age_days": 3193 + }, + "https://github.com/SadaleNet/ComfyUI-Prompt-To-Prompt": { + "stars": 24, + "last_update": "2024-03-17 04:30:01", + "author_account_age_days": 4441 + }, + "https://github.com/Sai-ComfyUI/ComfyUI-MS-Nodes": { + "stars": 2, + "last_update": "2024-02-22 08:34:44", + "author_account_age_days": 610 + }, + "https://github.com/Sakura-nee/ComfyUI_Save2Discord": { + "stars": 0, + "last_update": "2024-08-27 19:01:46", + "author_account_age_days": 1713 + }, + "https://github.com/SanDiegoDude/ComfyUI-HiDream-Sampler": { + "stars": 98, + "last_update": "2025-05-09 15:17:23", + "author_account_age_days": 1033 + }, + "https://github.com/SaulQcy/comfy_saul_plugin": { + "stars": 0, + "last_update": "2025-07-28 08:54:50", + "author_account_age_days": 663 + }, + "https://github.com/Scaryplasmon/ComfTrellis": { + "stars": 7, + "last_update": "2025-02-18 11:34:33", + "author_account_age_days": 1427 + }, + "https://github.com/SeedV/ComfyUI-SeedV-Nodes": { + "stars": 1, + "last_update": "2025-04-25 07:37:36", + "author_account_age_days": 1527 + }, + "https://github.com/Sephrael/comfyui_caption-around-image": { + "stars": 0, + "last_update": "2025-06-02 19:16:34", + "author_account_age_days": 865 + }, + "https://github.com/ShahFaisalWani/ComfyUI-Mojen-Nodeset": { + "stars": 0, + "last_update": "2025-05-03 08:29:40", + "author_account_age_days": 812 + }, + "https://github.com/Shinsplat/ComfyUI-Shinsplat": { + "stars": 46, + "last_update": "2025-03-15 00:02:11", + "author_account_age_days": 1426 + }, + "https://github.com/ShmuelRonen/ComfyUI-FreeMemory": { + "stars": 109, + "last_update": "2025-03-20 11:25:12", + "author_account_age_days": 1606 + }, + "https://github.com/Simlym/comfyui-prompt-helper": { + "stars": 2, + "last_update": "2025-07-30 16:06:49", + "author_account_age_days": 2584 + }, + "https://github.com/SirVeggie/comfyui-sv-nodes": { + "stars": 5, + "last_update": "2025-05-03 19:46:49", + "author_account_age_days": 2862 + }, + "https://github.com/Slix-M-Lestragg/comfyui-enhanced": { + "stars": 0, + "last_update": "2025-04-11 21:32:23", + "author_account_age_days": 1716 + }, + "https://github.com/SoftMeng/ComfyUI-PIL": { + "stars": 7, + "last_update": "2024-10-13 10:02:17", + "author_account_age_days": 3925 + }, + "https://github.com/Solankimayursinh/PMSnodes": { + "stars": 0, + "last_update": "2025-04-26 07:47:15", + "author_account_age_days": 266 + }, + "https://github.com/Soliton80/ComfyUI-Watermark-Detection-YOLO": { + "stars": 3, + "last_update": "2025-07-22 08:55:26", + "author_account_age_days": 1365 + }, + "https://github.com/Sophylax/ComfyUI-ReferenceMerge": { + "stars": 0, + "last_update": "2025-04-30 21:48:18", + "author_account_age_days": 4347 + }, + "https://github.com/Soppatorsk/comfyui_img_to_ascii": { + "stars": 0, + "last_update": "2024-09-07 15:39:28", + "author_account_age_days": 1541 + }, + "https://github.com/SpaceWarpStudio/ComfyUI_Remaker_FaceSwap": { + "stars": 0, + "last_update": "2024-07-15 11:57:20", + "author_account_age_days": 3357 + }, + "https://github.com/Stable-X/ComfyUI-Hi3DGen": { + "stars": 166, + "last_update": "2025-04-04 03:48:36", + "author_account_age_days": 424 + }, + "https://github.com/StableDiffusionVN/SDVN_Comfy_node": { + "stars": 58, + "last_update": "2025-07-30 07:58:27", + "author_account_age_days": 359 + }, + "https://github.com/StaffsGull/comfyui_scene_builder": { + "stars": 0, + "last_update": "2025-04-27 12:40:57", + "author_account_age_days": 3341 + }, + "https://github.com/StartHua/Comfyui_CSDMT_CXH": { + "stars": 20, + "last_update": "2024-07-11 15:36:03", + "author_account_age_days": 3237 + }, + "https://github.com/StartHua/Comfyui_CXH_CRM": { + "stars": 45, + "last_update": "2024-06-06 14:15:14", + "author_account_age_days": 3237 + }, + "https://github.com/StartHua/Comfyui_CXH_joy_caption": { + "stars": 591, + "last_update": "2025-02-06 02:35:10", + "author_account_age_days": 3237 + }, + "https://github.com/StartHua/Comfyui_Flux_Style_Ctr": { + "stars": 97, + "last_update": "2024-11-22 09:25:11", + "author_account_age_days": 3237 + }, + "https://github.com/StartHua/Comfyui_leffa": { + "stars": 231, + "last_update": "2024-12-18 03:04:54", + "author_account_age_days": 3237 + }, + "https://github.com/StoryWalker/comfyui_flux_collection_advanced": { + "stars": 0, + "last_update": "2025-04-28 02:49:48", + "author_account_age_days": 212 + }, + "https://github.com/Symbiomatrix/Comfyui-Sort-Files": { + "stars": 1, + "last_update": "2025-04-22 22:24:00", + "author_account_age_days": 2576 + }, + "https://github.com/TSFSean/ComfyUI-TSFNodes": { + "stars": 6, + "last_update": "2024-05-18 00:59:06", + "author_account_age_days": 3874 + }, + "https://github.com/Tawbaware/ComfyUI-Tawbaware": { + "stars": 1, + "last_update": "2025-04-20 22:23:11", + "author_account_age_days": 1673 + }, + "https://github.com/Temult/TWanSigmaSampler": { + "stars": 2, + "last_update": "2025-04-17 08:53:41", + "author_account_age_days": 668 + }, + "https://github.com/ThatGlennD/ComfyUI-Image-Analysis-Tools": { + "stars": 13, + "last_update": "2025-05-27 11:49:48", + "author_account_age_days": 3247 + }, + "https://github.com/TheJorseman/IntrinsicCompositingClean-ComfyUI": { + "stars": 0, + "last_update": "2025-05-07 17:07:51", + "author_account_age_days": 3684 + }, + "https://github.com/ThisModernDay/ComfyUI-InstructorOllama": { + "stars": 8, + "last_update": "2024-08-20 00:30:24", + "author_account_age_days": 4131 + }, + "https://github.com/TinyBeeman/ComfyUI-TinyBee": { + "stars": 0, + "last_update": "2025-07-11 08:43:08", + "author_account_age_days": 2183 + }, + "https://github.com/Tr1dae/ComfyUI-CustomNodes-MVM": { + "stars": 0, + "last_update": "2025-07-09 17:19:56", + "author_account_age_days": 940 + }, + "https://github.com/UmutGuzel/tryvariantai-comfyui": { + "stars": 0, + "last_update": "2025-07-15 12:05:03", + "author_account_age_days": 2172 + }, + "https://github.com/V-woodpecker-V/comfyui-stiffy-nodes": { + "stars": 1, + "last_update": "2025-04-12 22:36:51", + "author_account_age_days": 1658 + }, + "https://github.com/Velour-Fog/comfy-latent-nodes": { + "stars": 7, + "last_update": "2025-02-24 00:34:41", + "author_account_age_days": 1362 + }, + "https://github.com/VictorLopes643/ComfyUI-Video-Dataset-Tools": { + "stars": 1, + "last_update": "2025-05-09 22:47:52", + "author_account_age_days": 2711 + }, + "https://github.com/Video3DGenResearch/comfyui-batch-input-node": { + "stars": 1, + "last_update": "2024-04-28 15:21:17", + "author_account_age_days": 508 + }, + "https://github.com/VisionExp/ve_custom_comfyui_nodes": { + "stars": 0, + "last_update": "2024-07-17 11:51:54", + "author_account_age_days": 406 + }, + "https://github.com/Vkabuto23/comfyui_openrouter_ollama": { + "stars": 2, + "last_update": "2025-07-16 09:03:47", + "author_account_age_days": 413 + }, + "https://github.com/WASasquatch/ASTERR": { + "stars": 30, + "last_update": "2024-10-27 01:48:56", + "author_account_age_days": 5027 + }, + "https://github.com/WSJUSA/Comfyui-StableSR": { + "stars": 53, + "last_update": "2023-10-18 12:40:30", + "author_account_age_days": 1826 + }, + "https://github.com/WaiyanLing/ComfyUI-Tracking": { + "stars": 1, + "last_update": "2025-04-18 04:36:33", + "author_account_age_days": 4523 + }, + "https://github.com/WilliamStanford/ComfyUI-VisualLabs": { + "stars": 1, + "last_update": "2024-04-16 21:53:02", + "author_account_age_days": 2171 + }, + "https://github.com/WozStudios/ComfyUI-WozNodes": { + "stars": 0, + "last_update": "2025-06-25 14:29:29", + "author_account_age_days": 4510 + }, + "https://github.com/XiaoHeiziGGG/ComfyUI-Gemini-Kontext": { + "stars": 7, + "last_update": "2025-07-02 21:15:07", + "author_account_age_days": 455 + }, + "https://github.com/XiaoHeiziGGG/ComfyUI-GeminiTranslator": { + "stars": 0, + "last_update": "2025-06-30 05:43:17", + "author_account_age_days": 455 + }, + "https://github.com/Yeonri/ComfyUI_LLM_Are_You_Listening": { + "stars": 0, + "last_update": "2025-02-21 00:35:03", + "author_account_age_days": 938 + }, + "https://github.com/Yukinoshita-Yukinoe/ComfyUI-KontextOfficialNode": { + "stars": 2, + "last_update": "2025-06-06 09:23:19", + "author_account_age_days": 1808 + }, + "https://github.com/ZHO-ZHO-ZHO/ComfyUI-AuraSR-ZHO": { + "stars": 94, + "last_update": "2024-07-11 07:33:30", + "author_account_age_days": 741 + }, + "https://github.com/ZHO-ZHO-ZHO/ComfyUI-BiRefNet-ZHO": { + "stars": 358, + "last_update": "2024-07-30 23:24:24", + "author_account_age_days": 741 + }, + "https://github.com/ZHO-ZHO-ZHO/ComfyUI-Gemini": { + "stars": 767, + "last_update": "2024-05-22 14:15:11", + "author_account_age_days": 741 + }, + "https://github.com/ZHO-ZHO-ZHO/ComfyUI-Llama-3-2": { + "stars": 18, + "last_update": "2024-09-26 18:08:01", + "author_account_age_days": 741 + }, + "https://github.com/ZHO-ZHO-ZHO/ComfyUI-PuLID-ZHO": { + "stars": 235, + "last_update": "2024-05-22 13:38:23", + "author_account_age_days": 741 + }, + "https://github.com/ZHO-ZHO-ZHO/ComfyUI-Qwen": { + "stars": 110, + "last_update": "2024-09-20 21:27:47", + "author_account_age_days": 741 + }, + "https://github.com/ZHO-ZHO-ZHO/ComfyUI-Wan-ZHO": { + "stars": 10, + "last_update": "2025-02-26 05:46:42", + "author_account_age_days": 741 + }, + "https://github.com/ZenAI-Vietnam/ComfyUI-gemini-IG": { + "stars": 1, + "last_update": "2025-03-26 14:49:13", + "author_account_age_days": 584 + }, + "https://github.com/ZenAI-Vietnam/ComfyUI_InfiniteYou": { + "stars": 240, + "last_update": "2025-03-31 07:56:02", + "author_account_age_days": 584 + }, + "https://github.com/a-One-Fan/ComfyUI-Blenderesque-Nodes": { + "stars": 5, + "last_update": "2025-07-04 23:14:56", + "author_account_age_days": 1256 + }, + "https://github.com/a-und-b/ComfyUI_Output_as_Input": { + "stars": 2, + "last_update": "2025-05-08 08:35:02", + "author_account_age_days": 841 + }, + "https://github.com/aa-parky/pipemind-comfyui": { + "stars": 0, + "last_update": "2025-07-09 08:45:49", + "author_account_age_days": 2245 + }, + "https://github.com/abuzreq/ComfyUI-Model-Bending": { + "stars": 13, + "last_update": "2025-06-20 04:31:48", + "author_account_age_days": 4257 + }, + "https://github.com/ahmedbana/File-Rename": { + "stars": 0, + "last_update": "2025-07-25 10:48:49", + "author_account_age_days": 3334 + }, + "https://github.com/ahmedbana/json-creator": { + "stars": 0, + "last_update": "2025-07-29 10:59:12", + "author_account_age_days": 3334 + }, + "https://github.com/ahmedbana/upload-to-azure": { + "stars": 0, + "last_update": "2025-07-29 10:58:26", + "author_account_age_days": 3334 + }, + "https://github.com/aiden1020/ComfyUI_Artcoder": { + "stars": 2, + "last_update": "2025-01-11 08:31:32", + "author_account_age_days": 871 + }, + "https://github.com/ainanoha/etm_comfyui_nodes": { + "stars": 0, + "last_update": "2024-10-31 05:45:59", + "author_account_age_days": 4655 + }, + "https://github.com/akatz-ai/ComfyUI-Execution-Inversion": { + "stars": 2, + "last_update": "2025-06-18 04:06:55", + "author_account_age_days": 437 + }, + "https://github.com/aklevecz/ComfyUI-AutoPrompt": { + "stars": 0, + "last_update": "2025-05-26 18:36:34", + "author_account_age_days": 2674 + }, + "https://github.com/alexgenovese/ComfyUI-Diffusion-4k": { + "stars": 6, + "last_update": "2025-05-22 20:48:23", + "author_account_age_days": 5416 + }, + "https://github.com/alexgenovese/ComfyUI-Reica": { + "stars": 0, + "last_update": "2025-07-17 08:21:13", + "author_account_age_days": 5416 + }, + "https://github.com/alexisrolland/ComfyUI-AuraSR": { + "stars": 28, + "last_update": "2025-04-01 14:20:42", + "author_account_age_days": 3687 + }, + "https://github.com/alt-key-project/comfyui-dream-painter": { + "stars": 2, + "last_update": "2025-02-23 10:19:26", + "author_account_age_days": 1062 + }, + "https://github.com/alt-key-project/comfyui-dream-video-batches": { + "stars": 73, + "last_update": "2025-02-23 10:28:40", + "author_account_age_days": 1062 + }, + "https://github.com/amamisonlyuser/MixvtonComfyui": { + "stars": 0, + "last_update": "2025-05-31 14:14:10", + "author_account_age_days": 837 + }, + "https://github.com/ammahmoudi/ComfyUI-Legendary-Nodes": { + "stars": 0, + "last_update": "2025-03-15 07:26:17", + "author_account_age_days": 1343 + }, + "https://github.com/animEEEmpire/ComfyUI-Animemory-Loader": { + "stars": 2, + "last_update": "2025-01-20 08:02:58", + "author_account_age_days": 247 + }, + "https://github.com/apetitbois/nova_utils": { + "stars": 0, + "last_update": "2025-04-02 20:01:49", + "author_account_age_days": 3499 + }, + "https://github.com/aria1th/ComfyUI-CairoSVG": { + "stars": 0, + "last_update": "2025-01-07 19:40:19", + "author_account_age_days": 2746 + }, + "https://github.com/aria1th/ComfyUI-SkipCFGSigmas": { + "stars": 3, + "last_update": "2025-03-05 07:50:45", + "author_account_age_days": 2746 + }, + "https://github.com/aria1th/ComfyUI-camietagger-onnx": { + "stars": 0, + "last_update": "2025-03-06 01:55:51", + "author_account_age_days": 2746 + }, + "https://github.com/artem-konevskikh/comfyui-split-merge-video": { + "stars": 3, + "last_update": "2024-11-19 00:11:17", + "author_account_age_days": 4773 + }, + "https://github.com/artifyfun/ComfyUI-JS": { + "stars": 2, + "last_update": "2025-07-02 10:45:12", + "author_account_age_days": 492 + }, + "https://github.com/artisanalcomputing/ComfyUI-Custom-Nodes": { + "stars": 0, + "last_update": "2024-10-13 05:55:33", + "author_account_age_days": 2677 + }, + "https://github.com/ashishsaini/comfyui-segment-clothing-sleeves": { + "stars": 2, + "last_update": "2024-09-23 19:09:15", + "author_account_age_days": 4350 + }, + "https://github.com/ashllay/ComfyUI_MoreComfy": { + "stars": 0, + "last_update": "2025-07-11 19:06:00", + "author_account_age_days": 4371 + }, + "https://github.com/avocadori/ComfyUI-AudioAmplitudeConverter": { + "stars": 0, + "last_update": "2025-05-29 07:57:22", + "author_account_age_days": 475 + }, + "https://github.com/ayaoayaoayaoaya/ComfyUI-KLUT-DeepSeek-API": { + "stars": 0, + "last_update": "2025-03-27 15:38:59", + "author_account_age_days": 421 + }, + "https://github.com/babydjac/comfyui-grok-ponyxl": { + "stars": 0, + "last_update": "2025-07-18 19:10:49", + "author_account_age_days": 807 + }, + "https://github.com/backearth1/Comfyui-MiniMax-Video": { + "stars": 19, + "last_update": "2025-03-12 15:26:35", + "author_account_age_days": 653 + }, + "https://github.com/badmike/comfyui-prompt-factory": { + "stars": 0, + "last_update": "2025-02-18 09:28:53", + "author_account_age_days": 5081 + }, + "https://github.com/baicai99/ComfyUI-FrameSkipping": { + "stars": 12, + "last_update": "2025-06-23 02:50:12", + "author_account_age_days": 1232 + }, + "https://github.com/bananasss00/Comfyui-PyExec": { + "stars": 1, + "last_update": "2025-02-26 12:01:18", + "author_account_age_days": 2935 + }, + "https://github.com/bandido37/comfyui-kaggle-local-save": { + "stars": 0, + "last_update": "2025-04-23 16:20:30", + "author_account_age_days": 2144 + }, + "https://github.com/barakapa/barakapa-nodes": { + "stars": 0, + "last_update": "2025-05-13 20:47:52", + "author_account_age_days": 81 + }, + "https://github.com/benda1989/WaterMarkRemover_ComfyUI": { + "stars": 1, + "last_update": "2025-05-01 22:31:19", + "author_account_age_days": 2515 + }, + "https://github.com/benmizrahi/ComfyGCS": { + "stars": 1, + "last_update": "2025-05-05 15:18:40", + "author_account_age_days": 3643 + }, + "https://github.com/beyastard/ComfyUI_BeySoft": { + "stars": 0, + "last_update": "2024-05-26 22:44:55", + "author_account_age_days": 4688 + }, + "https://github.com/bheins/ComfyUI-glb-to-stl": { + "stars": 0, + "last_update": "2025-05-31 17:41:31", + "author_account_age_days": 4065 + }, + "https://github.com/bikiam/ComfyUi_WhisperGTranslate": { + "stars": 0, + "last_update": "2025-07-01 19:41:11", + "author_account_age_days": 551 + }, + "https://github.com/bikiam/Comfyui_AudioRecoder": { + "stars": 0, + "last_update": "2025-07-08 05:35:33", + "author_account_age_days": 551 + }, + "https://github.com/birnam/ComfyUI-GenData-Pack": { + "stars": 0, + "last_update": "2024-03-25 01:25:23", + "author_account_age_days": 5411 + }, + "https://github.com/bleash-dev/ComfyUI-Auth-Manager": { + "stars": 0, + "last_update": "2025-07-08 11:07:44", + "author_account_age_days": 1465 + }, + "https://github.com/bleash-dev/Comfyui-FileSytem-Manager": { + "stars": 0, + "last_update": "2025-07-26 23:57:01", + "author_account_age_days": 1465 + }, + "https://github.com/bleash-dev/Comfyui-Idle-Checker": { + "stars": 0, + "last_update": "2025-07-15 15:33:31", + "author_account_age_days": 1465 + }, + "https://github.com/blepping/comfyui_dum_samplers": { + "stars": 5, + "last_update": "2025-07-28 22:42:05", + "author_account_age_days": 555 + }, + "https://github.com/blib-la/ComfyUI-Captain-Extensions": { + "stars": 0, + "last_update": "2024-05-17 23:27:25", + "author_account_age_days": 675 + }, + "https://github.com/blueraincoatli/ComfyUI-Model-Cleaner": { + "stars": 1, + "last_update": "2025-05-29 08:55:38", + "author_account_age_days": 714 + }, + "https://github.com/blurymind/cozy-fireplace": { + "stars": 4, + "last_update": "2024-11-08 19:42:20", + "author_account_age_days": 4205 + }, + "https://github.com/bmad4ever/comfyui_bmad_nodes": { + "stars": 64, + "last_update": "2025-03-17 14:50:46", + "author_account_age_days": 3938 + }, + "https://github.com/boricuapab/ComfyUI-Bori-KontextPresets": { + "stars": 3, + "last_update": "2025-07-21 05:49:40", + "author_account_age_days": 1971 + }, + "https://github.com/brace-great/comfyui-eim": { + "stars": 0, + "last_update": "2025-05-14 06:09:18", + "author_account_age_days": 1490 + }, + "https://github.com/brace-great/comfyui-mc": { + "stars": 0, + "last_update": "2025-07-06 23:58:45", + "author_account_age_days": 1490 + }, + "https://github.com/broumbroum/comfyui-time-system": { + "stars": 0, + "last_update": "2025-07-25 18:50:32", + "author_account_age_days": 1170 + }, + "https://github.com/bruce007lee/comfyui-cleaner": { + "stars": 3, + "last_update": "2024-04-20 15:36:03", + "author_account_age_days": 4914 + }, + "https://github.com/bruce007lee/comfyui-tiny-utils": { + "stars": 1, + "last_update": "2024-08-31 13:34:57", + "author_account_age_days": 4914 + }, + "https://github.com/brycegoh/comfyui-custom-nodes": { + "stars": 0, + "last_update": "2024-06-05 09:30:06", + "author_account_age_days": 3510 + }, + "https://github.com/bulldog68/ComfyUI_FMJ": { + "stars": 4, + "last_update": "2025-07-16 16:04:06", + "author_account_age_days": 505 + }, + "https://github.com/c0ffymachyne/ComfyUI_SignalProcessing": { + "stars": 13, + "last_update": "2025-05-14 01:41:00", + "author_account_age_days": 4916 + }, + "https://github.com/casterpollux/MiniMax-bmo": { + "stars": 44, + "last_update": "2025-06-24 19:22:18", + "author_account_age_days": 75 + }, + "https://github.com/catboxanon/ComfyUI-Pixelsmith": { + "stars": 4, + "last_update": "2025-01-22 03:02:05", + "author_account_age_days": 933 + }, + "https://github.com/celll1/cel_sampler": { + "stars": 1, + "last_update": "2024-11-20 13:04:54", + "author_account_age_days": 631 + }, + "https://github.com/cesilk10/cesilk-comfyui-nodes": { + "stars": 0, + "last_update": "2025-07-20 06:06:24", + "author_account_age_days": 85 + }, + "https://github.com/chaojie/ComfyUI-DynamiCrafter": { + "stars": 130, + "last_update": "2024-06-14 10:23:59", + "author_account_age_days": 5235 + }, + "https://github.com/chaojie/ComfyUI-mobvoi-openapi": { + "stars": 2, + "last_update": "2024-05-29 09:02:52", + "author_account_age_days": 5235 + }, + "https://github.com/chenbaiyujason/ComfyUI_StepFun": { + "stars": 6, + "last_update": "2024-12-05 14:45:27", + "author_account_age_days": 2136 + }, + "https://github.com/chengzeyi/Comfy-WaveSpeed": { + "stars": 1103, + "last_update": "2025-03-27 08:10:29", + "author_account_age_days": 3178 + }, + "https://github.com/chetusangolgi/Comfyui-supabase": { + "stars": 0, + "last_update": "2025-07-17 11:50:31", + "author_account_age_days": 794 + }, + "https://github.com/chrisdreid/ComfyUI_EnvAutopsyAPI": { + "stars": 4, + "last_update": "2024-08-29 03:54:28", + "author_account_age_days": 3520 + }, + "https://github.com/christian-byrne/infinite-zoom-parallax-nodes": { + "stars": 5, + "last_update": "2024-07-08 15:07:05", + "author_account_age_days": 1750 + }, + "https://github.com/christian-byrne/python-interpreter-node": { + "stars": 60, + "last_update": "2025-04-02 02:06:27", + "author_account_age_days": 1750 + }, + "https://github.com/chuge26/ComfyUI_seal_migration": { + "stars": 0, + "last_update": "2025-04-21 07:23:45", + "author_account_age_days": 2767 + }, + "https://github.com/cidiro/cid-node-pack": { + "stars": 0, + "last_update": "2025-03-23 23:26:00", + "author_account_age_days": 2031 + }, + "https://github.com/ciga2011/ComfyUI-AppGen": { + "stars": 2, + "last_update": "2025-01-02 17:00:32", + "author_account_age_days": 4600 + }, + "https://github.com/comfyanonymous/ComfyUI": { + "stars": 83953, + "last_update": "2025-07-31 03:02:04", + "author_account_age_days": 950 + }, + "https://github.com/comfyanonymous/ComfyUI_bitsandbytes_NF4": { + "stars": 410, + "last_update": "2024-08-16 18:06:10", + "author_account_age_days": 950 + }, + "https://github.com/comfypod/ComfyUI-Comflow": { + "stars": 0, + "last_update": "2024-06-17 08:44:08", + "author_account_age_days": 424 + }, + "https://github.com/comfyuiblog/deepseek_prompt_generator_comfyui": { + "stars": 2, + "last_update": "2025-01-28 21:28:11", + "author_account_age_days": 292 + }, + "https://github.com/concarne000/ComfyUI-Stacker": { + "stars": 5, + "last_update": "2025-07-26 12:44:46", + "author_account_age_days": 2302 + }, + "https://github.com/corbin-hayden13/ComfyUI-Better-Dimensions": { + "stars": 7, + "last_update": "2024-06-12 17:45:21", + "author_account_age_days": 2217 + }, + "https://github.com/crimro-se/ComfyUI-CascadedGaze": { + "stars": 1, + "last_update": "2025-07-30 14:41:30", + "author_account_age_days": 279 + }, + "https://github.com/ctf05/ComfyUI-AudioDuration": { + "stars": 0, + "last_update": "2025-07-25 13:35:03", + "author_account_age_days": 2203 + }, + "https://github.com/cubiq/Comfy_Dungeon": { + "stars": 262, + "last_update": "2024-04-26 11:00:58", + "author_account_age_days": 5412 + }, + "https://github.com/cwebbi1/VoidCustomNodes": { + "stars": 0, + "last_update": "2024-10-07 02:23:02", + "author_account_age_days": 407 + }, + "https://github.com/cyberhirsch/seb_nodes": { + "stars": 1, + "last_update": "2025-07-12 12:45:36", + "author_account_age_days": 2268 + }, + "https://github.com/daracazamea/comfyUI-DCNodes": { + "stars": 0, + "last_update": "2025-04-03 14:38:27", + "author_account_age_days": 2360 + }, + "https://github.com/denislov/Comfyui_AutoSurvey": { + "stars": 1, + "last_update": "2024-08-03 06:50:57", + "author_account_age_days": 2389 + }, + "https://github.com/dexintenebri/comfyui_voxel_nodes": { + "stars": 1, + "last_update": "2025-07-29 11:46:03", + "author_account_age_days": 1014 + }, + "https://github.com/dfl/comfyui-stylegan": { + "stars": 0, + "last_update": "2024-12-29 18:35:27", + "author_account_age_days": 6376 + }, + "https://github.com/dhpdong/ComfyUI-IPAdapter-Flux-Repair": { + "stars": 4, + "last_update": "2025-05-23 08:51:34", + "author_account_age_days": 2311 + }, + "https://github.com/dihan/comfyui-random-kps": { + "stars": 3, + "last_update": "2025-01-01 22:48:11", + "author_account_age_days": 4698 + }, + "https://github.com/diodiogod/Comfy-Inpainting-Works": { + "stars": 46, + "last_update": "2025-07-08 01:15:58", + "author_account_age_days": 529 + }, + "https://github.com/dogcomplex/ComfyUI-LOKI": { + "stars": 1, + "last_update": "2025-05-07 08:10:12", + "author_account_age_days": 4468 + }, + "https://github.com/doucx/ComfyUI_WcpD_Utility_Kit": { + "stars": 1, + "last_update": "2024-01-06 19:07:45", + "author_account_age_days": 2724 + }, + "https://github.com/dowands/ComfyUI-AddMaskForICLora": { + "stars": 1, + "last_update": "2024-11-26 09:40:06", + "author_account_age_days": 2940 + }, + "https://github.com/downlifted/ComfyUI_BWiZ_Nodes": { + "stars": 1, + "last_update": "2024-12-27 17:03:52", + "author_account_age_days": 2648 + }, + "https://github.com/eggsbenedicto/DiffusionRenderer-ComfyUI": { + "stars": 1, + "last_update": "2025-07-29 01:33:24", + "author_account_age_days": 250 + }, + "https://github.com/eigenpunk/ComfyUI-audio": { + "stars": 88, + "last_update": "2024-03-03 21:14:14", + "author_account_age_days": 1327 + }, + "https://github.com/ejektaflex/ComfyUI-Ty": { + "stars": 0, + "last_update": "2024-06-12 16:08:16", + "author_account_age_days": 3173 + }, + "https://github.com/emranemran/ComfyUI-FasterLivePortrait": { + "stars": 0, + "last_update": "2024-12-18 20:03:19", + "author_account_age_days": 4586 + }, + "https://github.com/endman100/ComfyUI-SaveAndLoadPromptCondition": { + "stars": 1, + "last_update": "2024-07-03 09:35:02", + "author_account_age_days": 2875 + }, + "https://github.com/endman100/ComfyUI-augmentation": { + "stars": 0, + "last_update": "2024-07-23 09:06:24", + "author_account_age_days": 2875 + }, + "https://github.com/enlo/ComfyUI-CheckpointSettings": { + "stars": 0, + "last_update": "2025-07-20 04:27:46", + "author_account_age_days": 3871 + }, + "https://github.com/ericbeyer/guidance_interval": { + "stars": 2, + "last_update": "2024-04-16 03:24:01", + "author_account_age_days": 2996 + }, + "https://github.com/erosDiffusion/ComfyUI-enricos-json-file-load-and-value-selector": { + "stars": 2, + "last_update": "2025-06-04 16:32:17", + "author_account_age_days": 398 + }, + "https://github.com/esciron/ComfyUI-HunyuanVideoWrapper-Extended": { + "stars": 4, + "last_update": "2025-01-04 22:27:09", + "author_account_age_days": 3395 + }, + "https://github.com/etng/ComfyUI-Heartbeat": { + "stars": 2, + "last_update": "2025-06-03 09:32:40", + "author_account_age_days": 1465 + }, + "https://github.com/exectails/comfyui-et_scripting": { + "stars": 1, + "last_update": "2024-11-29 17:23:07", + "author_account_age_days": 4319 + }, + "https://github.com/eyekayem/comfyui_runway_gen3": { + "stars": 0, + "last_update": "2025-01-27 06:59:45", + "author_account_age_days": 1014 + }, + "https://github.com/facok/ComfyUI-FokToolset": { + "stars": 5, + "last_update": "2025-04-24 19:29:57", + "author_account_age_days": 860 + }, + "https://github.com/fangg2000/ComfyUI-SenseVoice": { + "stars": 0, + "last_update": "2025-05-06 06:42:52", + "author_account_age_days": 829 + }, + "https://github.com/fangg2000/ComfyUI-StableAudioFG": { + "stars": 0, + "last_update": "2025-06-15 11:49:34", + "author_account_age_days": 829 + }, + "https://github.com/fangziheng2321/comfyuinode_chopmask": { + "stars": 0, + "last_update": "2025-02-17 03:16:50", + "author_account_age_days": 1573 + }, + "https://github.com/filipemeneses/ComfyUI_html": { + "stars": 1, + "last_update": "2025-06-10 10:53:55", + "author_account_age_days": 3878 + }, + "https://github.com/filliptm/ComfyUI_Fill-Node-Loader": { + "stars": 5, + "last_update": "2025-06-25 01:25:38", + "author_account_age_days": 2138 + }, + "https://github.com/flowtyone/comfyui-flowty-lcm": { + "stars": 63, + "last_update": "2023-10-23 12:08:55", + "author_account_age_days": 675 + }, + "https://github.com/flyingdogsoftware/gyre_for_comfyui": { + "stars": 1, + "last_update": "2024-11-18 22:35:37", + "author_account_age_days": 2414 + }, + "https://github.com/foglerek/comfyui-cem-tools": { + "stars": 1, + "last_update": "2024-01-13 23:22:07", + "author_account_age_days": 4440 + }, + "https://github.com/franky519/comfyui-redux-style": { + "stars": 0, + "last_update": "2025-02-13 10:04:45", + "author_account_age_days": 678 + }, + "https://github.com/franky519/comfyui_fnckc_Face_analysis": { + "stars": 0, + "last_update": "2025-06-16 02:09:00", + "author_account_age_days": 678 + }, + "https://github.com/fritzprix/ComfyUI-LLM-Utils": { + "stars": 1, + "last_update": "2025-01-04 23:25:38", + "author_account_age_days": 5129 + }, + "https://github.com/ftechmax/ComfyUI-NovaKit-Pack": { + "stars": 0, + "last_update": "2025-04-26 13:27:06", + "author_account_age_days": 2979 + }, + "https://github.com/ftf001-tech/ComfyUI-ExternalLLMDetector": { + "stars": 1, + "last_update": "2025-06-22 03:43:09", + "author_account_age_days": 2097 + }, + "https://github.com/futureversecom/ComfyUI-JEN": { + "stars": 1, + "last_update": "2024-08-06 00:24:56", + "author_account_age_days": 1121 + }, + "https://github.com/fuzr0dah/comfyui-sceneassembly": { + "stars": 0, + "last_update": "2025-05-18 12:27:05", + "author_account_age_days": 3505 + }, + "https://github.com/fylrid2/comfyui_lock_previous_value": { + "stars": 0, + "last_update": "2025-06-30 22:05:07", + "author_account_age_days": 414 + }, + "https://github.com/gabe-init/ComfyUI-LM-Studio": { + "stars": 1, + "last_update": "2025-05-26 22:10:36", + "author_account_age_days": 66 + }, + "https://github.com/gabe-init/ComfyUI-Repo-Eater": { + "stars": 0, + "last_update": "2025-05-27 01:09:24", + "author_account_age_days": 66 + }, + "https://github.com/gabe-init/comfyui_ui_render": { + "stars": 2, + "last_update": "2025-05-27 00:27:32", + "author_account_age_days": 66 + }, + "https://github.com/gagaprince/ComfyUI_gaga_utils": { + "stars": 0, + "last_update": "2025-05-12 09:54:34", + "author_account_age_days": 4262 + }, + "https://github.com/galoreware/ComfyUI-GaloreNodes": { + "stars": 0, + "last_update": "2024-10-24 05:47:23", + "author_account_age_days": 1826 + }, + "https://github.com/gameltb/ComfyUI_paper_playground": { + "stars": 10, + "last_update": "2025-05-14 16:18:43", + "author_account_age_days": 4456 + }, + "https://github.com/gameltb/ComfyUI_stable_fast": { + "stars": 208, + "last_update": "2024-08-04 09:25:33", + "author_account_age_days": 4456 + }, + "https://github.com/gameltb/io_comfyui": { + "stars": 6, + "last_update": "2025-02-04 15:14:01", + "author_account_age_days": 4456 + }, + "https://github.com/gamtruliar/ComfyUI-N_SwapInput": { + "stars": 0, + "last_update": "2025-05-08 19:08:30", + "author_account_age_days": 4526 + }, + "https://github.com/gaowei-space/ComfyUI-Doubao-LLM": { + "stars": 2, + "last_update": "2025-07-03 07:19:49", + "author_account_age_days": 3879 + }, + "https://github.com/gilons/ComfyUI-GoogleDrive-Downloader": { + "stars": 0, + "last_update": "2025-06-13 20:43:59", + "author_account_age_days": 2949 + }, + "https://github.com/gioferreira/ComfyUI-Molde-Utils": { + "stars": 0, + "last_update": "2025-02-27 20:53:33", + "author_account_age_days": 3362 + }, + "https://github.com/gitadmini/comfyui_extractstoryboards": { + "stars": 9, + "last_update": "2025-06-11 02:01:24", + "author_account_age_days": 3443 + }, + "https://github.com/githubYiheng/comfyui_median_filter": { + "stars": 0, + "last_update": "2024-07-03 11:38:39", + "author_account_age_days": 4304 + }, + "https://github.com/gitmylo/FlowNodes": { + "stars": 11, + "last_update": "2025-04-03 08:17:47", + "author_account_age_days": 2709 + }, + "https://github.com/glamorfleet0i/ComfyUI-Firewall": { + "stars": 0, + "last_update": "2024-12-30 02:14:57", + "author_account_age_days": 219 + }, + "https://github.com/gmorks/ComfyUI-Animagine-Prompt": { + "stars": 11, + "last_update": "2025-07-20 03:42:06", + "author_account_age_days": 2696 + }, + "https://github.com/go-package-lab/ComfyUI-Tools-Video-Combine": { + "stars": 2, + "last_update": "2024-09-24 03:54:00", + "author_account_age_days": 1785 + }, + "https://github.com/godric8/ComfyUI_Step1X-Edit": { + "stars": 0, + "last_update": "2025-06-02 12:14:14", + "author_account_age_days": 1845 + }, + "https://github.com/gold24park/loki-comfyui-node": { + "stars": 0, + "last_update": "2025-02-07 01:55:07", + "author_account_age_days": 3692 + }, + "https://github.com/gondar-software/ComfyUI-Affine-Transform": { + "stars": 3, + "last_update": "2024-10-05 17:42:40", + "author_account_age_days": 361 + }, + "https://github.com/gondar-software/ComfyUI-Simple-Image-Tools": { + "stars": 0, + "last_update": "2024-10-12 18:29:58", + "author_account_age_days": 361 + }, + "https://github.com/gordon123/ComfyUI_DreamBoard": { + "stars": 2, + "last_update": "2025-05-18 09:53:50", + "author_account_age_days": 5496 + }, + "https://github.com/gordon123/ComfyUI_srt2speech": { + "stars": 3, + "last_update": "2025-04-27 13:00:13", + "author_account_age_days": 5496 + }, + "https://github.com/gorillaframeai/GF_pixtral_node": { + "stars": 0, + "last_update": "2025-07-27 13:00:23", + "author_account_age_days": 635 + }, + "https://github.com/grimli333/ComfyUI_Grim": { + "stars": 0, + "last_update": "2024-12-01 18:10:07", + "author_account_age_days": 5166 + }, + "https://github.com/grinlau18/ComfyUI_XISER_Nodes": { + "stars": 19, + "last_update": "2025-06-30 08:09:38", + "author_account_age_days": 705 + }, + "https://github.com/grokuku/ComfyUI-Holaf": { + "stars": 1, + "last_update": "2025-06-28 21:14:43", + "author_account_age_days": 2858 + }, + "https://github.com/grokuku/ComfyUI-Holaf-Utilities": { + "stars": 3, + "last_update": "2025-07-22 17:00:48", + "author_account_age_days": 2858 + }, + "https://github.com/hananbeer/node_dev": { + "stars": 6, + "last_update": "2024-08-19 08:08:39", + "author_account_age_days": 1945 + }, + "https://github.com/haodman/ComfyUI_Rain": { + "stars": 1, + "last_update": "2024-09-01 10:41:20", + "author_account_age_days": 2541 + }, + "https://github.com/haofanwang/ComfyUI-InstantStyle": { + "stars": 8, + "last_update": "2024-05-23 16:11:13", + "author_account_age_days": 3379 + }, + "https://github.com/haomole/Comfyui-SadTalker": { + "stars": 20, + "last_update": "2025-07-03 03:47:14", + "author_account_age_days": 702 + }, + "https://github.com/hay86/ComfyUI_AceNodes": { + "stars": 64, + "last_update": "2025-05-01 03:08:58", + "author_account_age_days": 5068 + }, + "https://github.com/hayden-fr/ComfyUI-Image-Browsing": { + "stars": 16, + "last_update": "2025-04-21 02:35:46", + "author_account_age_days": 2339 + }, + "https://github.com/hdfhssg/ComfyUI_pxtool": { + "stars": 4, + "last_update": "2025-03-02 06:23:44", + "author_account_age_days": 1645 + }, + "https://github.com/hdfhssg/comfyui_EvoSearch": { + "stars": 6, + "last_update": "2025-06-15 11:05:48", + "author_account_age_days": 1645 + }, + "https://github.com/hiusdev/ComfyUI_Lah_Toffee": { + "stars": 0, + "last_update": "2025-02-14 12:40:14", + "author_account_age_days": 1745 + }, + "https://github.com/hnmr293/ComfyUI-SamOne": { + "stars": 0, + "last_update": "2025-04-16 08:07:42", + "author_account_age_days": 956 + }, + "https://github.com/horidream/ComfyUI-Horidream": { + "stars": 0, + "last_update": "2024-09-08 08:57:57", + "author_account_age_days": 5445 + }, + "https://github.com/hotpizzatactics/ComfyUI-WaterMark-Detector": { + "stars": 0, + "last_update": "2024-07-23 14:36:35", + "author_account_age_days": 378 + }, + "https://github.com/hotpot-killer/ComfyUI_AlexNodes": { + "stars": 0, + "last_update": "2024-12-06 09:09:03", + "author_account_age_days": 2627 + }, + "https://github.com/houdinii/comfy-magick": { + "stars": 5, + "last_update": "2024-03-11 06:40:54", + "author_account_age_days": 3921 + }, + "https://github.com/huizhang0110/ComfyUI_Easy_Nodes_hui": { + "stars": 2, + "last_update": "2024-02-27 08:22:49", + "author_account_age_days": 2856 + }, + "https://github.com/hulipanpan/Comfyui_tuteng": { + "stars": 0, + "last_update": "2025-07-14 08:33:39", + "author_account_age_days": 806 + }, + "https://github.com/hunterssl/ComfyUI_SSLNodes": { + "stars": 0, + "last_update": "2025-01-20 07:23:52", + "author_account_age_days": 3237 + }, + "https://github.com/hunzmusic/ComfyUI-Hunyuan3DTools": { + "stars": 4, + "last_update": "2025-06-19 18:11:36", + "author_account_age_days": 130 + }, + "https://github.com/hunzmusic/Comfyui-CraftsMan3DWrapper": { + "stars": 14, + "last_update": "2025-05-09 10:46:59", + "author_account_age_days": 130 + }, + "https://github.com/hunzmusic/comfyui-hnznodes": { + "stars": 1, + "last_update": "2025-03-24 21:53:50", + "author_account_age_days": 130 + }, + "https://github.com/hy134300/comfyui-hb-node": { + "stars": 0, + "last_update": "2024-04-09 09:56:22", + "author_account_age_days": 2162 + }, + "https://github.com/hy134300/comfyui-hydit": { + "stars": 9, + "last_update": "2024-06-07 09:52:15", + "author_account_age_days": 2162 + }, + "https://github.com/hylarucoder/comfyui-copilot": { + "stars": 26, + "last_update": "2024-06-28 04:43:18", + "author_account_age_days": 4315 + }, + "https://github.com/iacoposk8/xor_pickle_nodes": { + "stars": 1, + "last_update": "2025-06-24 17:46:56", + "author_account_age_days": 4555 + }, + "https://github.com/if-ai/ComfyUI-IF_Zonos": { + "stars": 1, + "last_update": "2025-02-18 01:28:04", + "author_account_age_days": 3264 + }, + "https://github.com/ilovejohnwhite/Tracer": { + "stars": 0, + "last_update": "2024-11-26 03:39:33", + "author_account_age_days": 1275 + }, + "https://github.com/immersiveexperience/ie-comfyui-color-nodes": { + "stars": 2, + "last_update": "2024-06-18 10:54:55", + "author_account_age_days": 670 + }, + "https://github.com/io-club/ComfyUI-LuminaNext": { + "stars": 0, + "last_update": "2024-09-23 12:02:22", + "author_account_age_days": 1040 + }, + "https://github.com/jammyfu/ComfyUI_PaintingCoderUtils": { + "stars": 12, + "last_update": "2025-02-26 05:03:05", + "author_account_age_days": 4879 + }, + "https://github.com/jax-explorer/ComfyUI-DreamO": { + "stars": 67, + "last_update": "2025-05-22 08:07:02", + "author_account_age_days": 978 + }, + "https://github.com/jcomeme/ComfyUI-AsunaroTools": { + "stars": 1, + "last_update": "2025-03-21 03:57:39", + "author_account_age_days": 5251 + }, + "https://github.com/jerryname2022/ComfyUI-Real-ESRGAN": { + "stars": 0, + "last_update": "2025-04-19 10:54:34", + "author_account_age_days": 3681 + }, + "https://github.com/jgbrblmd/ComfyUI-ComfyFluxSize": { + "stars": 0, + "last_update": "2024-08-30 06:42:39", + "author_account_age_days": 857 + }, + "https://github.com/jgbyte/ComfyUI-RandomCube": { + "stars": 0, + "last_update": "2025-07-25 23:32:58", + "author_account_age_days": 357 + }, + "https://github.com/jiafuzeng/comfyui-fishSpeech": { + "stars": 0, + "last_update": "2025-07-23 08:29:43", + "author_account_age_days": 2614 + }, + "https://github.com/jimmm-ai/TimeUi-a-ComfyUi-Timeline-Node": { + "stars": 227, + "last_update": "2024-07-04 11:44:03", + "author_account_age_days": 422 + }, + "https://github.com/jimstudt/ComfyUI-Jims-Nodes": { + "stars": 0, + "last_update": "2025-01-21 17:36:29", + "author_account_age_days": 5349 + }, + "https://github.com/jinchanz/ComfyUI-AliCloud-Bailian": { + "stars": 1, + "last_update": "2025-06-29 07:08:03", + "author_account_age_days": 2467 + }, + "https://github.com/jn-jairo/jn_node_suite_comfyui": { + "stars": 6, + "last_update": "2024-06-08 05:15:33", + "author_account_age_days": 4386 + }, + "https://github.com/jordancoult/ComfyUI_HelpfulNodes": { + "stars": 0, + "last_update": "2025-05-17 01:04:37", + "author_account_age_days": 2827 + }, + "https://github.com/jschoormans/Comfy-InterestingPixels": { + "stars": 1, + "last_update": "2025-02-05 08:34:17", + "author_account_age_days": 3941 + }, + "https://github.com/jtscmw01/ComfyUI-DiffBIR": { + "stars": 297, + "last_update": "2024-05-21 05:28:34", + "author_account_age_days": 903 + }, + "https://github.com/jtydhr88/ComfyUI-Unique3D": { + "stars": 213, + "last_update": "2024-10-18 10:37:10", + "author_account_age_days": 5154 + }, + "https://github.com/jtydhr88/ComfyUI_frontend_vue_basic": { + "stars": 6, + "last_update": "2025-07-25 11:59:16", + "author_account_age_days": 5154 + }, + "https://github.com/junhe421/comfyui_batch_image_processor": { + "stars": 7, + "last_update": "2025-07-11 01:09:12", + "author_account_age_days": 536 + }, + "https://github.com/kadirnar/ComfyUI-Adapter": { + "stars": 3, + "last_update": "2024-04-03 12:05:39", + "author_account_age_days": 2731 + }, + "https://github.com/kandy/ComfyUI-KAndy": { + "stars": 0, + "last_update": "2025-04-08 01:42:33", + "author_account_age_days": 5874 + }, + "https://github.com/kappa54m/ComfyUI_Usability": { + "stars": 0, + "last_update": "2024-08-08 15:31:47", + "author_account_age_days": 1912 + }, + "https://github.com/karthikg-09/ComfyUI-3ncrypt": { + "stars": 0, + "last_update": "2024-12-27 09:09:07", + "author_account_age_days": 597 + }, + "https://github.com/kevin314/ComfyUI-FastVideo": { + "stars": 3, + "last_update": "2025-07-03 05:21:54", + "author_account_age_days": 2534 + }, + "https://github.com/kijai/ComfyUI-CV-VAE": { + "stars": 11, + "last_update": "2024-06-03 21:46:49", + "author_account_age_days": 2584 + }, + "https://github.com/kijai/ComfyUI-DeepSeek-VL": { + "stars": 48, + "last_update": "2024-05-21 16:43:40", + "author_account_age_days": 2584 + }, + "https://github.com/kijai/ComfyUI-DiffSynthWrapper": { + "stars": 61, + "last_update": "2024-06-22 00:16:46", + "author_account_age_days": 2584 + }, + "https://github.com/kijai/ComfyUI-DiffusersSD3Wrapper": { + "stars": 10, + "last_update": "2024-06-17 13:03:43", + "author_account_age_days": 2584 + }, + "https://github.com/kijai/ComfyUI-EasyAnimateWrapper": { + "stars": 85, + "last_update": "2024-08-14 02:20:18", + "author_account_age_days": 2584 + }, + "https://github.com/kijai/ComfyUI-FollowYourEmojiWrapper": { + "stars": 63, + "last_update": "2025-04-18 10:50:26", + "author_account_age_days": 2584 + }, + "https://github.com/kijai/ComfyUI-FramePackWrapper": { + "stars": 1454, + "last_update": "2025-06-03 21:48:59", + "author_account_age_days": 2584 + }, + "https://github.com/kijai/ComfyUI-Hunyuan3DWrapper": { + "stars": 812, + "last_update": "2025-06-15 09:52:41", + "author_account_age_days": 2584 + }, + "https://github.com/kijai/ComfyUI-HunyuanVideoWrapper": { + "stars": 2512, + "last_update": "2025-05-12 13:31:36", + "author_account_age_days": 2584 + }, + "https://github.com/kijai/ComfyUI-MMAudio": { + "stars": 393, + "last_update": "2025-06-30 09:33:55", + "author_account_age_days": 2584 + }, + "https://github.com/kijai/ComfyUI-MochiWrapper": { + "stars": 789, + "last_update": "2024-11-11 13:54:57", + "author_account_age_days": 2584 + }, + "https://github.com/kijai/ComfyUI-VEnhancer": { + "stars": 72, + "last_update": "2024-11-02 00:24:36", + "author_account_age_days": 2584 + }, + "https://github.com/kijai/ComfyUI-VideoNoiseWarp": { + "stars": 157, + "last_update": "2025-03-30 13:39:03", + "author_account_age_days": 2584 + }, + "https://github.com/kimara-ai/ComfyUI-Kimara-AI-Advanced-Watermarks": { + "stars": 17, + "last_update": "2025-04-03 17:22:59", + "author_account_age_days": 258 + }, + "https://github.com/kimara-ai/ComfyUI-Kimara-AI-Image-From-URL": { + "stars": 0, + "last_update": "2025-05-06 07:50:34", + "author_account_age_days": 258 + }, + "https://github.com/kk8bit/KayTool": { + "stars": 175, + "last_update": "2025-07-01 03:47:14", + "author_account_age_days": 758 + }, + "https://github.com/kongds1999/ComfyUI_was_image": { + "stars": 1, + "last_update": "2025-07-30 09:58:09", + "author_account_age_days": 1099 + }, + "https://github.com/krich-cto/ComfyUI-Flow-Control": { + "stars": 1, + "last_update": "2025-06-01 03:38:17", + "author_account_age_days": 1666 + }, + "https://github.com/krisshen2021/comfyui_OpenRouterNodes": { + "stars": 0, + "last_update": "2025-02-22 02:29:36", + "author_account_age_days": 1589 + }, + "https://github.com/kuschanow/ComfyUI-SD-Slicer": { + "stars": 0, + "last_update": "2024-12-08 16:59:31", + "author_account_age_days": 1783 + }, + "https://github.com/kxh/ComfyUI-ImageUpscaleWithModelMultipleTimes": { + "stars": 0, + "last_update": "2024-10-16 13:53:50", + "author_account_age_days": 4948 + }, + "https://github.com/kxh/ComfyUI-sam2": { + "stars": 1, + "last_update": "2024-10-10 18:06:11", + "author_account_age_days": 4948 + }, + "https://github.com/kycg/comfyui-Kwtoolset": { + "stars": 0, + "last_update": "2024-11-04 21:14:07", + "author_account_age_days": 1342 + }, + "https://github.com/kylegrover/comfyui-python-cowboy": { + "stars": 1, + "last_update": "2024-11-04 18:37:04", + "author_account_age_days": 3054 + }, + "https://github.com/l1yongch1/ComfyUI-YcNodes": { + "stars": 1, + "last_update": "2025-05-05 04:00:28", + "author_account_age_days": 1177 + }, + "https://github.com/laksjdjf/ssd-1b-comfyui": { + "stars": 1, + "last_update": "2023-10-27 20:05:06", + "author_account_age_days": 3232 + }, + "https://github.com/laubsauger/comfyui-storyboard": { + "stars": 9, + "last_update": "2025-06-14 23:33:25", + "author_account_age_days": 4957 + }, + "https://github.com/lazybuttalented/ComfyUI_LBT": { + "stars": 0, + "last_update": "2025-07-30 00:59:41", + "author_account_age_days": 2153 + }, + "https://github.com/lcolok/ComfyUI-MagicAI": { + "stars": 7, + "last_update": "2024-11-14 08:21:40", + "author_account_age_days": 2815 + }, + "https://github.com/leadbreak/comfyui-faceaging": { + "stars": 85, + "last_update": "2024-10-31 08:25:21", + "author_account_age_days": 1774 + }, + "https://github.com/leeguandong/ComfyUI_AliControlnetInpainting": { + "stars": 3, + "last_update": "2024-09-25 10:44:58", + "author_account_age_days": 3196 + }, + "https://github.com/leoleelxh/ComfyUI-MidjourneyNode-leoleexh": { + "stars": 23, + "last_update": "2024-08-01 03:37:17", + "author_account_age_days": 4477 + }, + "https://github.com/leon-etienne/ComfyUI_Scoring-Nodes": { + "stars": 0, + "last_update": "2025-04-21 11:48:26", + "author_account_age_days": 754 + }, + "https://github.com/lggcfx2020/ComfyUI-LGGCFX-Tools": { + "stars": 1, + "last_update": "2025-07-30 08:35:42", + "author_account_age_days": 1867 + }, + "https://github.com/lgldlk/ComfyUI-img-tiler": { + "stars": 1, + "last_update": "2024-10-17 07:56:42", + "author_account_age_days": 2091 + }, + "https://github.com/lichenhao/Comfyui_Ryota": { + "stars": 0, + "last_update": "2024-09-07 08:25:54", + "author_account_age_days": 4768 + }, + "https://github.com/linhusyung/comfyui-Build-and-train-your-network": { + "stars": 106, + "last_update": "2024-06-26 05:44:43", + "author_account_age_days": 1079 + }, + "https://github.com/littleowl/ComfyUI-MV-HECV": { + "stars": 1, + "last_update": "2025-06-04 12:42:47", + "author_account_age_days": 5063 + }, + "https://github.com/logtd/ComfyUI-Fluxtapoz": { + "stars": 1357, + "last_update": "2025-01-09 02:38:40", + "author_account_age_days": 524 + }, + "https://github.com/logtd/ComfyUI-HunyuanLoom": { + "stars": 484, + "last_update": "2025-02-21 21:01:57", + "author_account_age_days": 524 + }, + "https://github.com/logtd/ComfyUI-Veevee": { + "stars": 63, + "last_update": "2024-08-12 03:04:12", + "author_account_age_days": 524 + }, + "https://github.com/longgui0318/comfyui-one-more-step": { + "stars": 1, + "last_update": "2024-05-07 08:40:56", + "author_account_age_days": 4566 + }, + "https://github.com/longzoho/ComfyUI-Qdrant-Saver": { + "stars": 0, + "last_update": "2025-03-07 13:44:52", + "author_account_age_days": 1920 + }, + "https://github.com/lordwedggie/xcpNodes": { + "stars": 0, + "last_update": "2024-11-15 06:24:48", + "author_account_age_days": 954 + }, + "https://github.com/love2hina-net/ComfyUI-Local-Translator": { + "stars": 0, + "last_update": "2025-07-29 14:09:56", + "author_account_age_days": 1453 + }, + "https://github.com/lrzjason/Comfyui-Condition-Utils": { + "stars": 6, + "last_update": "2025-05-18 17:09:17", + "author_account_age_days": 4064 + }, + "https://github.com/ltdrdata/ComfyUI-Workflow-Component": { + "stars": 243, + "last_update": "2024-07-30 08:08:28", + "author_account_age_days": 864 + }, + "https://github.com/ltdrdata/comfyui-unsafe-torch": { + "stars": 29, + "last_update": "2025-06-10 22:31:29", + "author_account_age_days": 864 + }, + "https://github.com/lu64k/SK-Nodes": { + "stars": 0, + "last_update": "2024-11-18 03:47:34", + "author_account_age_days": 793 + }, + "https://github.com/lucafoscili/lf-nodes": { + "stars": 14, + "last_update": "2025-07-22 05:43:11", + "author_account_age_days": 2436 + }, + "https://github.com/lum3on/comfyui_LLM_Polymath": { + "stars": 65, + "last_update": "2025-07-17 10:22:58", + "author_account_age_days": 176 + }, + "https://github.com/lum3on/comfyui_RollingDepth": { + "stars": 1, + "last_update": "2025-06-01 18:46:56", + "author_account_age_days": 176 + }, + "https://github.com/m-ai-studio/mai-prompt-progress": { + "stars": 0, + "last_update": "2025-04-14 19:13:55", + "author_account_age_days": 450 + }, + "https://github.com/machinesarenotpeople/comfyui-energycost": { + "stars": 0, + "last_update": "2025-05-03 21:22:23", + "author_account_age_days": 1935 + }, + "https://github.com/maekawataiki/ComfyUI-ALB-Login": { + "stars": 3, + "last_update": "2025-01-17 02:10:49", + "author_account_age_days": 3065 + }, + "https://github.com/maizerrr/comfyui-code-nodes": { + "stars": 0, + "last_update": "2025-06-30 03:36:19", + "author_account_age_days": 3471 + }, + "https://github.com/majorsauce/comfyui_indieTools": { + "stars": 0, + "last_update": "2024-06-25 08:59:57", + "author_account_age_days": 2207 + }, + "https://github.com/mamorett/ComfyUI-SmolVLM": { + "stars": 5, + "last_update": "2024-11-30 14:31:14", + "author_account_age_days": 1153 + }, + "https://github.com/mamorett/comfyui_minicpm_vision": { + "stars": 0, + "last_update": "2025-06-17 13:25:18", + "author_account_age_days": 1153 + }, + "https://github.com/marcueberall/ComfyUI-BuildPath": { + "stars": 0, + "last_update": "2024-02-06 07:57:33", + "author_account_age_days": 2184 + }, + "https://github.com/marduk191/comfyui-marnodes": { + "stars": 3, + "last_update": "2025-03-27 13:26:45", + "author_account_age_days": 4816 + }, + "https://github.com/maruhidd/ComfyUI_Transparent-Background": { + "stars": 4, + "last_update": "2024-06-14 07:02:56", + "author_account_age_days": 2657 + }, + "https://github.com/mashb1t/comfyui-nodes-mashb1t": { + "stars": 0, + "last_update": "2024-06-11 15:55:53", + "author_account_age_days": 3937 + }, + "https://github.com/masmullin2000/ComfyUI-MMYolo": { + "stars": 0, + "last_update": "2025-02-22 22:23:02", + "author_account_age_days": 4480 + }, + "https://github.com/matDobek/ComfyUI_duck": { + "stars": 0, + "last_update": "2025-05-21 13:12:40", + "author_account_age_days": 4479 + }, + "https://github.com/maurorilla/ComfyUI-MisterMR-Nodes": { + "stars": 0, + "last_update": "2025-05-09 13:18:07", + "author_account_age_days": 4418 + }, + "https://github.com/mehbebe/ComfyLoraGallery": { + "stars": 1, + "last_update": "2024-12-29 12:44:29", + "author_account_age_days": 760 + }, + "https://github.com/melMass/ComfyUI-Lygia": { + "stars": 0, + "last_update": "2024-07-14 09:59:10", + "author_account_age_days": 4147 + }, + "https://github.com/mikebilly/Transparent-background-comfyUI": { + "stars": 2, + "last_update": "2025-01-29 16:29:23", + "author_account_age_days": 2965 + }, + "https://github.com/mikeymcfish/FishTools": { + "stars": 27, + "last_update": "2024-07-13 20:51:17", + "author_account_age_days": 3805 + }, + "https://github.com/mikheys/ComfyUI-mikheys": { + "stars": 0, + "last_update": "2025-06-21 15:35:56", + "author_account_age_days": 2803 + }, + "https://github.com/minhtuannhn/comfyui-gemini-studio": { + "stars": 0, + "last_update": "2024-11-19 16:05:05", + "author_account_age_days": 1582 + }, + "https://github.com/miragecoa/ComfyUI-LLM-Evaluation": { + "stars": 1, + "last_update": "2024-11-21 01:29:48", + "author_account_age_days": 967 + }, + "https://github.com/mliand/ComfyUI-Calendar-Node": { + "stars": 0, + "last_update": "2025-01-10 07:33:40", + "author_account_age_days": 787 + }, + "https://github.com/mm-akhtar/comfyui-mask-selector-node": { + "stars": 0, + "last_update": "2025-04-18 10:06:17", + "author_account_age_days": 1894 + }, + "https://github.com/mohamedsobhi777/ComfyUI-FramerComfy": { + "stars": 0, + "last_update": "2025-01-25 14:39:17", + "author_account_age_days": 2821 + }, + "https://github.com/molbal/comfy-url-fetcher": { + "stars": 0, + "last_update": "2025-02-02 13:37:48", + "author_account_age_days": 4298 + }, + "https://github.com/moonwhaler/comfyui-moonpack": { + "stars": 0, + "last_update": "2025-06-20 16:22:16", + "author_account_age_days": 4825 + }, + "https://github.com/mr-krak3n/ComfyUI-Qwen": { + "stars": 22, + "last_update": "2025-03-08 12:12:29", + "author_account_age_days": 2443 + }, + "https://github.com/mut-ex/comfyui-gligengui-node": { + "stars": 51, + "last_update": "2024-02-28 02:46:05", + "author_account_age_days": 3260 + }, + "https://github.com/muvich3n/ComfyUI-Claude-I2T": { + "stars": 0, + "last_update": "2025-01-15 07:50:46", + "author_account_age_days": 1700 + }, + "https://github.com/muvich3n/ComfyUI-Crop-Border": { + "stars": 0, + "last_update": "2025-02-24 10:01:53", + "author_account_age_days": 1700 + }, + "https://github.com/naderzare/comfyui-inodes": { + "stars": 0, + "last_update": "2025-02-05 04:32:29", + "author_account_age_days": 3092 + }, + "https://github.com/nat-chan/comfyui-exec": { + "stars": 3, + "last_update": "2024-05-28 11:56:37", + "author_account_age_days": 3395 + }, + "https://github.com/nat-chan/comfyui-paint": { + "stars": 3, + "last_update": "2024-06-14 11:01:38", + "author_account_age_days": 3395 + }, + "https://github.com/nat-chan/transceiver": { + "stars": 1, + "last_update": "2024-05-01 10:03:01", + "author_account_age_days": 3395 + }, + "https://github.com/neeltheninja/ComfyUI-TempFileDeleter": { + "stars": 0, + "last_update": "2024-10-26 19:25:43", + "author_account_age_days": 2280 + }, + "https://github.com/neeltheninja/ComfyUI-TextOverlay": { + "stars": 0, + "last_update": "2024-07-31 18:40:19", + "author_account_age_days": 2280 + }, + "https://github.com/neo0801/my-comfy-node": { + "stars": 0, + "last_update": "2024-09-20 07:49:04", + "author_account_age_days": 4181 + }, + "https://github.com/netanelben/comfyui-camera2image-customnode": { + "stars": 1, + "last_update": "2024-09-29 15:14:57", + "author_account_age_days": 4283 + }, + "https://github.com/netanelben/comfyui-image2image-customnode": { + "stars": 1, + "last_update": "2024-09-29 12:50:53", + "author_account_age_days": 4283 + }, + "https://github.com/netanelben/comfyui-photobooth-customnode": { + "stars": 0, + "last_update": "2024-10-02 08:00:05", + "author_account_age_days": 4283 + }, + "https://github.com/netanelben/comfyui-text2image-customnode": { + "stars": 4, + "last_update": "2024-09-29 15:19:37", + "author_account_age_days": 4283 + }, + "https://github.com/neverbiasu/ComfyUI-ControlNeXt": { + "stars": 3, + "last_update": "2024-08-15 08:15:43", + "author_account_age_days": 1420 + }, + "https://github.com/neverbiasu/ComfyUI-DeepSeek": { + "stars": 0, + "last_update": "2025-02-01 04:17:59", + "author_account_age_days": 1420 + }, + "https://github.com/neverbiasu/ComfyUI-Show-o": { + "stars": 1, + "last_update": "2025-06-24 06:33:20", + "author_account_age_days": 1420 + }, + "https://github.com/neverbiasu/ComfyUI-StereoCrafter": { + "stars": 4, + "last_update": "2024-12-30 13:32:43", + "author_account_age_days": 1420 + }, + "https://github.com/newraina/ComfyUI-Remote-Save-Image": { + "stars": 0, + "last_update": "2025-04-18 10:50:44", + "author_account_age_days": 3838 + }, + "https://github.com/nidefawl/ComfyUI-nidefawl": { + "stars": 0, + "last_update": "2024-01-16 18:16:41", + "author_account_age_days": 5269 + }, + "https://github.com/nikkuexe/ComfyUI-ListDataHelpers": { + "stars": 0, + "last_update": "2024-09-21 16:15:57", + "author_account_age_days": 4950 + }, + "https://github.com/nkchocoai/ComfyUI-PromptUtilities": { + "stars": 19, + "last_update": "2025-03-30 08:19:25", + "author_account_age_days": 561 + }, + "https://github.com/nobandegani/comfyui_ino_nodes": { + "stars": 2, + "last_update": "2025-07-26 14:20:24", + "author_account_age_days": 1688 + }, + "https://github.com/nomcycle/ComfyUI_Cluster": { + "stars": 3, + "last_update": "2025-05-28 03:57:01", + "author_account_age_days": 4740 + }, + "https://github.com/norgeous/ComfyUI-UI-Builder": { + "stars": 9, + "last_update": "2024-08-11 22:22:04", + "author_account_age_days": 4431 + }, + "https://github.com/orion4d/ComfyUI_unified_list_selector": { + "stars": 1, + "last_update": "2025-07-03 08:57:21", + "author_account_age_days": 983 + }, + "https://github.com/osuiso-depot/comfyui-keshigom_custom": { + "stars": 0, + "last_update": "2025-02-27 10:01:17", + "author_account_age_days": 1511 + }, + "https://github.com/owengillett/ComfyUI-tilefusion": { + "stars": 0, + "last_update": "2025-02-19 11:05:53", + "author_account_age_days": 2134 + }, + "https://github.com/oxysoft/Comfy-Compel": { + "stars": 0, + "last_update": "2025-04-08 13:12:20", + "author_account_age_days": 4513 + }, + "https://github.com/oxysoft/ComfyUI-uiapi": { + "stars": 0, + "last_update": "2025-01-27 18:29:08", + "author_account_age_days": 4513 + }, + "https://github.com/oyvindg/ComfyUI-TrollSuite": { + "stars": 4, + "last_update": "2024-08-15 10:37:43", + "author_account_age_days": 2731 + }, + "https://github.com/oztrkoguz/ComfyUI_Kosmos2_BBox_Cutter": { + "stars": 16, + "last_update": "2024-07-25 05:50:01", + "author_account_age_days": 1243 + }, + "https://github.com/p1atdev/comfyui-aesthetic-predictor": { + "stars": 0, + "last_update": "2025-05-10 08:03:13", + "author_account_age_days": 2016 + }, + "https://github.com/pacchikAI/ImagePromptBatch": { + "stars": 0, + "last_update": "2025-05-26 12:48:05", + "author_account_age_days": 80 + }, + "https://github.com/pamparamm/ComfyUI-ppm": { + "stars": 209, + "last_update": "2025-06-13 16:23:47", + "author_account_age_days": 2532 + }, + "https://github.com/papcorns/ComfyUI-Papcorns-Node-UploadToGCS": { + "stars": 0, + "last_update": "2025-05-28 09:31:23", + "author_account_age_days": 1914 + }, + "https://github.com/parmarjh/ComfyUI-MochiWrapper-I2V": { + "stars": 0, + "last_update": "2025-01-10 14:28:51", + "author_account_age_days": 1962 + }, + "https://github.com/paulhoux/Smart-Prompting": { + "stars": 0, + "last_update": "2025-03-10 09:16:44", + "author_account_age_days": 5525 + }, + "https://github.com/phamngoctukts/ComyUI-Tupham": { + "stars": 1, + "last_update": "2025-01-09 04:02:54", + "author_account_age_days": 4289 + }, + "https://github.com/pictorialink/ComfyUI-static-resource": { + "stars": 0, + "last_update": "2025-07-15 07:50:28", + "author_account_age_days": 77 + }, + "https://github.com/pinkpixel-dev/comfyui-llm-prompt-enhancer": { + "stars": 8, + "last_update": "2025-01-28 12:43:25", + "author_account_age_days": 190 + }, + "https://github.com/pixixai/ComfyUI_Pixix-Tools": { + "stars": 0, + "last_update": "2025-07-22 04:43:42", + "author_account_age_days": 204 + }, + "https://github.com/pixuai/ComfyUI-PixuAI": { + "stars": 0, + "last_update": "2025-03-01 13:56:56", + "author_account_age_days": 151 + }, + "https://github.com/pmarmotte2/Comfyui-VibeVoiceSelector": { + "stars": 1, + "last_update": "2025-04-08 11:18:55", + "author_account_age_days": 465 + }, + "https://github.com/poisenbery/NudeNet-Detector-Provider": { + "stars": 1, + "last_update": "2024-02-26 02:11:27", + "author_account_age_days": 1638 + }, + "https://github.com/pomelyu/cy-prompt-tools": { + "stars": 0, + "last_update": "2025-06-13 15:09:26", + "author_account_age_days": 4657 + }, + "https://github.com/power88/ComfyUI-PDiD-Nodes": { + "stars": 0, + "last_update": "2025-01-04 11:21:29", + "author_account_age_days": 3132 + }, + "https://github.com/prabinpebam/anyPython": { + "stars": 15, + "last_update": "2025-02-15 06:56:01", + "author_account_age_days": 4644 + }, + "https://github.com/prodogape/ComfyUI-clip-interrogator": { + "stars": 60, + "last_update": "2024-07-27 18:33:22", + "author_account_age_days": 1431 + }, + "https://github.com/przewodo/ComfyUI-Przewodo-Utils": { + "stars": 2, + "last_update": "2025-07-30 08:20:40", + "author_account_age_days": 3712 + }, + "https://github.com/pschroedl/ComfyUI-StreamDiffusion": { + "stars": 6, + "last_update": "2025-05-21 01:33:15", + "author_account_age_days": 4389 + }, + "https://github.com/pzzmyc/comfyui-sd3-simple-simpletuner": { + "stars": 1, + "last_update": "2024-06-19 12:48:18", + "author_account_age_days": 2500 + }, + "https://github.com/qlikpetersen/ComfyUI-AI_Tools": { + "stars": 0, + "last_update": "2025-07-23 19:53:54", + "author_account_age_days": 1430 + }, + "https://github.com/rakete/comfyui-rakete": { + "stars": 0, + "last_update": "2025-07-22 21:48:34", + "author_account_age_days": 5980 + }, + "https://github.com/rakki194/ComfyUI_WolfSigmas": { + "stars": 5, + "last_update": "2025-05-21 13:02:21", + "author_account_age_days": 179 + }, + "https://github.com/ralonsobeas/ComfyUI-HDRConversion": { + "stars": 5, + "last_update": "2024-12-12 20:21:26", + "author_account_age_days": 2460 + }, + "https://github.com/realm-weaver/ComfyUI-tile-seamstress-360": { + "stars": 0, + "last_update": "2025-07-16 15:36:03", + "author_account_age_days": 608 + }, + "https://github.com/redhottensors/ComfyUI-ODE": { + "stars": 51, + "last_update": "2024-08-01 06:57:05", + "author_account_age_days": 541 + }, + "https://github.com/retech995/Save_Florence2_Bulk_Prompts": { + "stars": 0, + "last_update": "2025-06-03 18:27:37", + "author_account_age_days": 2381 + }, + "https://github.com/rhinoflavored/comfyui_QT": { + "stars": 0, + "last_update": "2025-03-18 08:35:59", + "author_account_age_days": 398 + }, + "https://github.com/ricklove/ComfyUI-AutoSeg-SAM2": { + "stars": 0, + "last_update": "2025-03-15 20:46:14", + "author_account_age_days": 5234 + }, + "https://github.com/rickyars/sd-cn-animation": { + "stars": 0, + "last_update": "2025-05-18 22:33:04", + "author_account_age_days": 4605 + }, + "https://github.com/rishipandey125/ComfyUI-FramePacking": { + "stars": 9, + "last_update": "2025-06-09 21:51:46", + "author_account_age_days": 2752 + }, + "https://github.com/risunobushi/ComfyUI_FaceMesh_Eyewear_Mask": { + "stars": 0, + "last_update": "2025-05-14 07:13:26", + "author_account_age_days": 1048 + }, + "https://github.com/risunobushi/ComfyUI_FocusMask": { + "stars": 4, + "last_update": "2024-12-09 11:52:53", + "author_account_age_days": 1048 + }, + "https://github.com/risunobushi/ComfyUI_HEXtoRGB": { + "stars": 1, + "last_update": "2025-01-28 14:37:42", + "author_account_age_days": 1048 + }, + "https://github.com/ritikvirus/comfyui-terminal-modal-node": { + "stars": 0, + "last_update": "2025-03-01 20:03:57", + "author_account_age_days": 2581 + }, + "https://github.com/rodpl/comfyui-asset-manager": { + "stars": 0, + "last_update": "2025-07-29 22:15:01", + "author_account_age_days": 5855 + }, + "https://github.com/romeobuilderotti/ComfyUI-EZ-Pipes": { + "stars": 3, + "last_update": "2023-11-15 22:00:49", + "author_account_age_days": 692 + }, + "https://github.com/ronalds-eu/comfyui-plus-integrations": { + "stars": 0, + "last_update": "2025-05-02 17:38:19", + "author_account_age_days": 4183 + }, + "https://github.com/rouxianmantou/comfyui-rxmt-nodes": { + "stars": 0, + "last_update": "2025-07-01 12:11:18", + "author_account_age_days": 3578 + }, + "https://github.com/rphmeier/comfyui-videodepthanything": { + "stars": 1, + "last_update": "2025-04-14 18:53:06", + "author_account_age_days": 3887 + }, + "https://github.com/ruka-game/rukalib_comfyui": { + "stars": 0, + "last_update": "2024-10-03 23:59:55", + "author_account_age_days": 306 + }, + "https://github.com/ryanontheinside/ComfyUI-Livepeer": { + "stars": 1, + "last_update": "2025-04-21 22:53:14", + "author_account_age_days": 4091 + }, + "https://github.com/ryanontheinside/ComfyUI-MineWorld": { + "stars": 2, + "last_update": "2025-04-16 18:59:09", + "author_account_age_days": 4091 + }, + "https://github.com/ryanontheinside/ComfyUI_YoloNasObjectDetection_Tensorrt": { + "stars": 1, + "last_update": "2024-12-31 17:43:33", + "author_account_age_days": 4091 + }, + "https://github.com/sangeet/testui": { + "stars": 2, + "last_update": "2024-05-15 00:55:17", + "author_account_age_days": 5481 + }, + "https://github.com/sdfxai/SDFXBridgeForComfyUI": { + "stars": 11, + "last_update": "2024-06-14 10:26:56", + "author_account_age_days": 636 + }, + "https://github.com/seancheung/comfyui-creative-nodes": { + "stars": 0, + "last_update": "2024-09-13 06:22:45", + "author_account_age_days": 4339 + }, + "https://github.com/sh570655308/Comfyui-RayNodes": { + "stars": 2, + "last_update": "2025-07-19 11:32:53", + "author_account_age_days": 2903 + }, + "https://github.com/shadowcz007/ComfyUI-PuLID-Test": { + "stars": 9, + "last_update": "2024-05-12 14:37:28", + "author_account_age_days": 3716 + }, + "https://github.com/shadowcz007/Comfyui-EzAudio": { + "stars": 1, + "last_update": "2024-09-22 03:17:40", + "author_account_age_days": 3716 + }, + "https://github.com/shadowcz007/comfyui-CLIPSeg": { + "stars": 3, + "last_update": "2024-02-08 02:16:24", + "author_account_age_days": 3716 + }, + "https://github.com/shadowcz007/comfyui-hydit-lowvram": { + "stars": 1, + "last_update": "2024-07-31 10:04:09", + "author_account_age_days": 3716 + }, + "https://github.com/shadowcz007/comfyui-sd-prompt-mixlab": { + "stars": 16, + "last_update": "2024-05-21 19:47:56", + "author_account_age_days": 3716 + }, + "https://github.com/shinich39/comfyui-nothing-happened": { + "stars": 0, + "last_update": "2025-05-25 10:18:24", + "author_account_age_days": 714 + }, + "https://github.com/shinich39/comfyui-run-js": { + "stars": 0, + "last_update": "2025-06-05 13:34:15", + "author_account_age_days": 714 + }, + "https://github.com/shinich39/comfyui-textarea-is-shit": { + "stars": 0, + "last_update": "2025-06-03 11:52:52", + "author_account_age_days": 714 + }, + "https://github.com/shirazdesigner/CLIPTextEncodeAndEnhancev4": { + "stars": 1, + "last_update": "2024-04-27 13:25:08", + "author_account_age_days": 4363 + }, + "https://github.com/shuanshuan/ComfyUI_CheckPointLoader_Ext": { + "stars": 0, + "last_update": "2024-08-27 02:24:05", + "author_account_age_days": 4519 + }, + "https://github.com/silent-rain/ComfyUI-SilentRain": { + "stars": 2, + "last_update": "2025-07-30 10:08:33", + "author_account_age_days": 2544 + }, + "https://github.com/silveroxides/ComfyUI_ReduxEmbedToolkit": { + "stars": 1, + "last_update": "2025-07-16 04:31:05", + "author_account_age_days": 1903 + }, + "https://github.com/simonjaq/ComfyUI-sjnodes": { + "stars": 0, + "last_update": "2025-06-20 08:23:01", + "author_account_age_days": 2936 + }, + "https://github.com/siyonomicon/ComfyUI-Pin": { + "stars": 0, + "last_update": "2025-07-21 18:06:00", + "author_account_age_days": 12 + }, + "https://github.com/smthemex/ComfyUI_GPT_SoVITS_Lite": { + "stars": 5, + "last_update": "2025-03-17 06:45:58", + "author_account_age_days": 756 + }, + "https://github.com/smthemex/ComfyUI_MangaNinjia": { + "stars": 55, + "last_update": "2025-04-09 14:21:57", + "author_account_age_days": 756 + }, + "https://github.com/sofakid/dandy": { + "stars": 51, + "last_update": "2024-05-27 21:46:18", + "author_account_age_days": 4455 + }, + "https://github.com/songtianhui/ComfyUI-DMM": { + "stars": 2, + "last_update": "2025-04-27 12:38:20", + "author_account_age_days": 1647 + }, + "https://github.com/sorption-dev/mycraft-comfyui": { + "stars": 4, + "last_update": "2025-04-12 18:08:12", + "author_account_age_days": 124 + }, + "https://github.com/sourceful-official/ComfyUI_InstructPixToPixConditioningLatent": { + "stars": 3, + "last_update": "2025-01-03 13:20:33", + "author_account_age_days": 1884 + }, + "https://github.com/sourceful-official/comfyui-sourceful-official": { + "stars": 0, + "last_update": "2025-01-27 14:58:03", + "author_account_age_days": 1884 + }, + "https://github.com/spawner1145/comfyui-spawner-nodes": { + "stars": 2, + "last_update": "2025-07-27 14:54:54", + "author_account_age_days": 339 + }, + "https://github.com/springjk/ComfyUI-Psutil-Container-Memory-Patch": { + "stars": 0, + "last_update": "2025-04-23 15:12:34", + "author_account_age_days": 4062 + }, + "https://github.com/sswink/comfyui-lingshang": { + "stars": 0, + "last_update": "2024-11-06 15:04:22", + "author_account_age_days": 2937 + }, + "https://github.com/stalkervr/comfyui-custom-path-nodes": { + "stars": 1, + "last_update": "2025-07-19 08:30:33", + "author_account_age_days": 2768 + }, + "https://github.com/stavsap/ComfyUI-React-SDK": { + "stars": 13, + "last_update": "2024-03-17 21:54:21", + "author_account_age_days": 4485 + }, + "https://github.com/steelan9199/ComfyUI-Teeth": { + "stars": 10, + "last_update": "2025-03-03 01:44:23", + "author_account_age_days": 1255 + }, + "https://github.com/strhwste/comfyui_csv_utils": { + "stars": 0, + "last_update": "2025-06-03 23:04:43", + "author_account_age_days": 874 + }, + "https://github.com/stutya/ComfyUI-Terminal": { + "stars": 0, + "last_update": "2025-05-11 21:29:49", + "author_account_age_days": 4239 + }, + "https://github.com/subnet99/ComfyUI-URLLoader": { + "stars": 2, + "last_update": "2025-07-14 07:57:56", + "author_account_age_days": 24 + }, + "https://github.com/sugarkwork/comfyui_image_crop": { + "stars": 0, + "last_update": "2025-03-14 01:43:03", + "author_account_age_days": 1277 + }, + "https://github.com/sugarkwork/comfyui_my_img_util": { + "stars": 0, + "last_update": "2025-04-04 15:51:26", + "author_account_age_days": 1277 + }, + "https://github.com/sugarkwork/comfyui_psd": { + "stars": 6, + "last_update": "2025-01-14 04:33:37", + "author_account_age_days": 1277 + }, + "https://github.com/suncat2ps/ComfyUI-SaveImgNextcloud": { + "stars": 0, + "last_update": "2025-06-23 04:03:38", + "author_account_age_days": 4526 + }, + "https://github.com/system-out-cho/displayHistory_ComfyUI": { + "stars": 1, + "last_update": "2025-07-30 02:16:22", + "author_account_age_days": 1630 + }, + "https://github.com/takoyaki1118/ComfyUI_PromptExtractor": { + "stars": 0, + "last_update": "2025-06-05 07:01:24", + "author_account_age_days": 2501 + }, + "https://github.com/talesofai/comfyui-supersave": { + "stars": 2, + "last_update": "2023-12-27 02:05:53", + "author_account_age_days": 957 + }, + "https://github.com/talon468/ComfyUI-Rpg-Architect": { + "stars": 4, + "last_update": "2024-08-31 14:47:47", + "author_account_age_days": 819 + }, + "https://github.com/tankenyuen-ola/comfyui-env-variable-reader": { + "stars": 0, + "last_update": "2025-06-19 06:21:23", + "author_account_age_days": 205 + }, + "https://github.com/tankenyuen-ola/comfyui-wanvideo-scheduler-loop": { + "stars": 0, + "last_update": "2025-07-31 02:58:44", + "author_account_age_days": 205 + }, + "https://github.com/tanmoy-it/comfyuiCustomNode": { + "stars": 0, + "last_update": "2025-05-10 07:45:32", + "author_account_age_days": 333 + }, + "https://github.com/tc888/ComfyUI_Save_Flux_Image": { + "stars": 0, + "last_update": "2025-02-09 17:21:22", + "author_account_age_days": 2661 + }, + "https://github.com/techidsk/comfyui_molook_nodes": { + "stars": 0, + "last_update": "2025-03-31 02:17:02", + "author_account_age_days": 2590 + }, + "https://github.com/techtruth/ComfyUI-Dreambooth": { + "stars": 0, + "last_update": "2025-04-06 02:57:20", + "author_account_age_days": 3033 + }, + "https://github.com/techzuhaib/ComfyUI-CacheImageNode": { + "stars": 0, + "last_update": "2024-11-29 07:31:49", + "author_account_age_days": 571 + }, + "https://github.com/thavocado/comfyui-danbooru-lookup": { + "stars": 0, + "last_update": "2025-07-26 16:00:54", + "author_account_age_days": 976 + }, + "https://github.com/thderoo/ComfyUI-_topfun_s_nodes": { + "stars": 6, + "last_update": "2024-07-03 14:39:28", + "author_account_age_days": 3268 + }, + "https://github.com/thot-experiment/comfy-live-preview": { + "stars": 2, + "last_update": "2025-02-19 20:30:13", + "author_account_age_days": 1353 + }, + "https://github.com/threadedblue/MLXnodes": { + "stars": 2, + "last_update": "2025-02-15 13:41:14", + "author_account_age_days": 4366 + }, + "https://github.com/tjorbogarden/my-useful-comfyui-custom-nodes": { + "stars": 0, + "last_update": "2024-03-05 13:31:31", + "author_account_age_days": 514 + }, + "https://github.com/tom-doerr/dspy_nodes": { + "stars": 191, + "last_update": "2024-12-01 20:14:37", + "author_account_age_days": 3181 + }, + "https://github.com/tracerstar/comfyui-p5js-node": { + "stars": 38, + "last_update": "2024-07-05 23:47:57", + "author_account_age_days": 5600 + }, + "https://github.com/trampolin/comfy-ui-scryfall": { + "stars": 0, + "last_update": "2025-05-20 11:46:54", + "author_account_age_days": 4646 + }, + "https://github.com/trashgraphicard/Albedo-Sampler-for-ComfyUI": { + "stars": 4, + "last_update": "2024-12-04 23:50:38", + "author_account_age_days": 1077 + }, + "https://github.com/truebillyblue/lC.ComfyUI_epistemic_nodes": { + "stars": 0, + "last_update": "2025-05-29 14:43:38", + "author_account_age_days": 157 + }, + "https://github.com/tuckerdarby/ComfyUI-TDNodes": { + "stars": 3, + "last_update": "2024-02-19 17:00:55", + "author_account_age_days": 3340 + }, + "https://github.com/turskeli/comfyui-SetWallpaper": { + "stars": 0, + "last_update": "2025-04-23 22:46:46", + "author_account_age_days": 5019 + }, + "https://github.com/tzsoulcap/ComfyUI-SaveImg-W-MetaData": { + "stars": 0, + "last_update": "2025-04-11 15:28:03", + "author_account_age_days": 2223 + }, + "https://github.com/umisetokikaze/comfyui_mergekit": { + "stars": 0, + "last_update": "2024-04-28 07:21:00", + "author_account_age_days": 2224 + }, + "https://github.com/unanan/ComfyUI-Dist": { + "stars": 7, + "last_update": "2024-02-28 10:03:50", + "author_account_age_days": 3299 + }, + "https://github.com/usman2003/ComfyUI-Classifiers": { + "stars": 0, + "last_update": "2025-05-21 12:44:01", + "author_account_age_days": 1938 + }, + "https://github.com/usman2003/ComfyUI-RaceDetect": { + "stars": 0, + "last_update": "2025-05-23 12:23:39", + "author_account_age_days": 1938 + }, + "https://github.com/usrname0/ComfyUI-AllergicPack": { + "stars": 0, + "last_update": "2025-06-29 19:13:04", + "author_account_age_days": 2815 + }, + "https://github.com/var1ableX/ComfyUI_Accessories": { + "stars": 1, + "last_update": "2025-02-09 14:31:19", + "author_account_age_days": 5166 + }, + "https://github.com/vchopine/ComfyUI_Toolbox": { + "stars": 2, + "last_update": "2025-03-18 16:12:09", + "author_account_age_days": 3992 + }, + "https://github.com/virallover/comfyui-virallover": { + "stars": 0, + "last_update": "2025-06-02 13:49:38", + "author_account_age_days": 94 + }, + "https://github.com/visualbruno/ComfyUI-Hunyuan3d-2-1": { + "stars": 120, + "last_update": "2025-07-29 19:43:57", + "author_account_age_days": 5441 + }, + "https://github.com/visualbruno/ComfyUI-QRemeshify": { + "stars": 2, + "last_update": "2025-07-14 19:03:15", + "author_account_age_days": 5441 + }, + "https://github.com/vladp0727/Comfyui-with-Furniture": { + "stars": 0, + "last_update": "2025-04-18 08:58:04", + "author_account_age_days": 131 + }, + "https://github.com/vovler/ComfyUI-vovlerTools": { + "stars": 0, + "last_update": "2025-06-25 17:36:07", + "author_account_age_days": 2129 + }, + "https://github.com/wTechArtist/ComfyUI_VVL_Segmentation": { + "stars": 0, + "last_update": "2025-05-29 05:25:00", + "author_account_age_days": 1762 + }, + "https://github.com/wTechArtist/ComfyUI_VVL_VideoCamera": { + "stars": 0, + "last_update": "2025-06-12 02:11:03", + "author_account_age_days": 1762 + }, + "https://github.com/wTechArtist/ComfyUI_vvl_BBOX": { + "stars": 0, + "last_update": "2025-05-21 12:14:07", + "author_account_age_days": 1762 + }, + "https://github.com/walterFeng/ComfyUI-Image-Utils": { + "stars": 3, + "last_update": "2025-03-25 14:36:37", + "author_account_age_days": 3177 + }, + "https://github.com/warshanks/Shank-Tools": { + "stars": 0, + "last_update": "2025-01-26 03:39:09", + "author_account_age_days": 3885 + }, + "https://github.com/wasilone11/comfyui-sync-translate-node": { + "stars": 0, + "last_update": "2025-07-19 03:26:55", + "author_account_age_days": 2607 + }, + "https://github.com/watarika/ComfyUI-Text-Utility": { + "stars": 1, + "last_update": "2025-04-22 14:16:27", + "author_account_age_days": 2134 + }, + "https://github.com/watarika/ComfyUI-exit": { + "stars": 0, + "last_update": "2025-01-05 03:24:05", + "author_account_age_days": 2134 + }, + "https://github.com/waynepimpzhang/comfyui-opencv-brightestspot": { + "stars": 0, + "last_update": "2025-01-05 06:04:53", + "author_account_age_days": 4192 + }, + "https://github.com/whmc76/ComfyUI-AudioSuiteAdvanced": { + "stars": 8, + "last_update": "2025-07-18 11:33:21", + "author_account_age_days": 853 + }, + "https://github.com/wildminder/ComfyUI-MagCache": { + "stars": 6, + "last_update": "2025-06-13 20:56:49", + "author_account_age_days": 4634 + }, + "https://github.com/willblaschko/ComfyUI-Unload-Models": { + "stars": 21, + "last_update": "2024-06-30 10:07:40", + "author_account_age_days": 4993 + }, + "https://github.com/wilzamguerrero/Comfyui-zZzZz": { + "stars": 2, + "last_update": "2025-01-02 00:35:50", + "author_account_age_days": 1089 + }, + "https://github.com/wordbrew/comfyui-wan-control-nodes": { + "stars": 6, + "last_update": "2025-06-19 23:37:04", + "author_account_age_days": 1014 + }, + "https://github.com/wormley/comfyui-wormley-nodes": { + "stars": 0, + "last_update": "2023-11-12 19:05:11", + "author_account_age_days": 2875 + }, + "https://github.com/x3bits/ComfyUI-Power-Flow": { + "stars": 2, + "last_update": "2025-01-14 14:20:35", + "author_account_age_days": 3784 + }, + "https://github.com/xgfone/ComfyUI_PromptLogoCleaner": { + "stars": 0, + "last_update": "2025-07-28 05:28:42", + "author_account_age_days": 4659 + }, + "https://github.com/xiaoyumu/ComfyUI-XYNodes": { + "stars": 0, + "last_update": "2024-12-05 07:07:30", + "author_account_age_days": 4418 + }, + "https://github.com/xinyiSS/CombineMasksNode": { + "stars": 0, + "last_update": "2025-02-08 04:35:18", + "author_account_age_days": 853 + }, + "https://github.com/xl0/q_tools": { + "stars": 0, + "last_update": "2025-05-28 06:09:00", + "author_account_age_days": 5403 + }, + "https://github.com/xmarked-ai/ComfyUI_misc": { + "stars": 2, + "last_update": "2025-06-07 16:26:56", + "author_account_age_days": 275 + }, + "https://github.com/xqqe/honey_nodes": { + "stars": 0, + "last_update": "2025-05-03 20:59:53", + "author_account_age_days": 2103 + }, + "https://github.com/xzuyn/ComfyUI-xzuynodes": { + "stars": 0, + "last_update": "2025-07-23 20:07:16", + "author_account_age_days": 3522 + }, + "https://github.com/y4my4my4m/ComfyUI_Direct3DS2": { + "stars": 6, + "last_update": "2025-06-01 04:29:47", + "author_account_age_days": 4036 + }, + "https://github.com/yamanacn/comfyui_qwen_object": { + "stars": 0, + "last_update": "2025-06-20 12:24:28", + "author_account_age_days": 1721 + }, + "https://github.com/yamanacn/comfyui_qwenbbox": { + "stars": 0, + "last_update": "2025-06-21 03:00:01", + "author_account_age_days": 1721 + }, + "https://github.com/yanhuifair/ComfyUI-FairLab": { + "stars": 1, + "last_update": "2025-07-30 08:23:25", + "author_account_age_days": 3963 + }, + "https://github.com/yanhuifair/comfyui-deepseek": { + "stars": 4, + "last_update": "2025-04-08 09:14:25", + "author_account_age_days": 3963 + }, + "https://github.com/yanlang0123/ComfyUI_Lam": { + "stars": 48, + "last_update": "2025-07-20 10:45:04", + "author_account_age_days": 3210 + }, + "https://github.com/yichengup/ComfyUI-Transition": { + "stars": 3, + "last_update": "2025-07-12 07:15:38", + "author_account_age_days": 526 + }, + "https://github.com/yichengup/ComfyUI-YCNodes_Advance": { + "stars": 5, + "last_update": "2025-06-25 15:08:18", + "author_account_age_days": 526 + }, + "https://github.com/yichengup/Comfyui-NodeSpark": { + "stars": 5, + "last_update": "2025-01-20 14:20:36", + "author_account_age_days": 526 + }, + "https://github.com/yincangshiwei/ComfyUI-SEQLToolNode": { + "stars": 0, + "last_update": "2025-05-28 10:06:17", + "author_account_age_days": 4032 + }, + "https://github.com/yojimbodayne/ComfyUI-Dropbox-API": { + "stars": 0, + "last_update": "2024-08-30 05:29:07", + "author_account_age_days": 352 + }, + "https://github.com/zackabrams/ComfyUI-KeySyncWrapper": { + "stars": 3, + "last_update": "2025-06-21 17:46:04", + "author_account_age_days": 2731 + }, + "https://github.com/zhaorishuai/ComfyUI-StoryboardDistributor": { + "stars": 2, + "last_update": "2025-04-01 08:10:16", + "author_account_age_days": 2640 + }, + "https://github.com/zhengxyz123/ComfyUI-CLIPSeg": { + "stars": 2, + "last_update": "2025-05-20 12:40:03", + "author_account_age_days": 2056 + }, + "https://github.com/zhongpei/Comfyui_image2prompt": { + "stars": 350, + "last_update": "2025-06-06 23:41:46", + "author_account_age_days": 3860 + }, + "https://github.com/zhuanvi/ComfyUI-ZVNodes": { + "stars": 0, + "last_update": "2025-07-27 05:13:32", + "author_account_age_days": 3558 + }, + "https://github.com/zjkhurry/comfyui_MetalFX": { + "stars": 1, + "last_update": "2025-03-05 07:07:17", + "author_account_age_days": 3382 + }, + "https://github.com/zl9739379/comfyui-qwen-vl-api": { + "stars": 0, + "last_update": "2025-07-02 12:53:51", + "author_account_age_days": 982 + }, + "https://github.com/zml-ai/comfyui-hydit": { + "stars": 3, + "last_update": "2024-08-07 09:37:09", + "author_account_age_days": 2384 + }, + "https://github.com/zopieux/ComfyUI-zopi": { + "stars": 0, + "last_update": "2025-07-27 02:50:44", + "author_account_age_days": 5930 + }, + "https://github.com/zyd232/ComfyUI-zyd232-Nodes": { + "stars": 0, + "last_update": "2025-04-12 01:13:21", + "author_account_age_days": 4025 + }, + "https://github.com/zyquon/ComfyUI-Stash": { + "stars": 0, + "last_update": "2025-06-21 03:32:52", + "author_account_age_days": 121 + } +} \ No newline at end of file diff --git a/ComfyUI/custom_nodes/ComfyUI-Manager/node_db/dev/model-list.json b/ComfyUI/custom_nodes/ComfyUI-Manager/node_db/dev/model-list.json new file mode 100644 index 0000000000000000000000000000000000000000..8e3e1dc4858a08aa46190aa53ba320d565206cf4 --- /dev/null +++ b/ComfyUI/custom_nodes/ComfyUI-Manager/node_db/dev/model-list.json @@ -0,0 +1,3 @@ +{ + "models": [] +} diff --git a/ComfyUI/custom_nodes/ComfyUI-Manager/node_db/dev/scan.sh b/ComfyUI/custom_nodes/ComfyUI-Manager/node_db/dev/scan.sh new file mode 100644 index 0000000000000000000000000000000000000000..ac8c4255b608064f5a362e01532f97a733fbc67b --- /dev/null +++ b/ComfyUI/custom_nodes/ComfyUI-Manager/node_db/dev/scan.sh @@ -0,0 +1,3 @@ +#!/bin/bash +rm ~/.tmp/dev/*.py > /dev/null 2>&1 +python ../../scanner.py ~/.tmp/dev $* \ No newline at end of file diff --git a/ComfyUI/custom_nodes/ComfyUI-Manager/node_db/forked/custom-node-list.json b/ComfyUI/custom_nodes/ComfyUI-Manager/node_db/forked/custom-node-list.json new file mode 100644 index 0000000000000000000000000000000000000000..9f0e8980a40a33190b410d92fefb53f0e2d5a5a0 --- /dev/null +++ b/ComfyUI/custom_nodes/ComfyUI-Manager/node_db/forked/custom-node-list.json @@ -0,0 +1,164 @@ +{ + "custom_nodes": [ + { + "author": "joaomede", + "title": "ComfyUI-Unload-Model-Fork", + "reference": "https://github.com/joaomede/ComfyUI-Unload-Model-Fork", + "files": [ + "https://github.com/joaomede/ComfyUI-Unload-Model-Fork" + ], + "install_type": "git-clone", + "description": "For unloading a model or all models, using the memory management that is already present in ComfyUI. Copied from [a/https://github.com/willblaschko/ComfyUI-Unload-Models](https://github.com/willblaschko/ComfyUI-Unload-Models) but without the unnecessary extra stuff." + }, + { + "author": "SanDiegoDude", + "title": "ComfyUI-HiDream-Sampler [WIP]", + "reference": "https://github.com/SanDiegoDude/ComfyUI-HiDream-Sampler", + "files": [ + "https://github.com/SanDiegoDude/ComfyUI-HiDream-Sampler" + ], + "install_type": "git-clone", + "description": "A collection of enhanced nodes for ComfyUI that provide powerful additional functionality to your workflows.\nNOTE: The files in the repo are not organized." + }, + { + "author": "PramaLLC", + "title": "ComfyUI BEN - Background Erase Network", + "reference": "https://github.com/PramaLLC/BEN2_ComfyUI", + "files": [ + "https://github.com/PramaLLC/BEN2_ComfyUI" + ], + "install_type": "git-clone", + "description": "Remove backgrounds from images with [a/BEN2](https://huggingface.co/PramaLLC/BEN2) in ComfyUI\nOriginal repo: [a/https://github.com/DoctorDiffusion/ComfyUI-BEN](https://github.com/DoctorDiffusion/ComfyUI-BEN)" + }, + { + "author": "BlenderNeko", + "title": "ltdrdata/ComfyUI_TiledKSampler", + "reference": "https://github.com/ltdrdata/ComfyUI_TiledKSampler", + "files": [ + "https://github.com/ltdrdata/ComfyUI_TiledKSampler" + ], + "install_type": "git-clone", + "description": "PR for [a/https://github.com/BlenderNeko/ComfyUI_TiledKSampler/pull/59](https://github.com/BlenderNeko/ComfyUI_TiledKSampler/pull/59)" + }, + { + "author": "leeooo001", + "title": "ComfyUI-leo-Hamer", + "reference": "https://github.com/leeooo001/ComfyUI-leo-Hamer", + "files": [ + "https://github.com/leeooo001/ComfyUI-leo-Hamer" + ], + "install_type": "git-clone", + "description": "Unoffice Hamer-ComfyUI by leo\nNOTE:base on [a/hamer](https://github.com/geopavlakos/hamer)" + }, + { + "author": "leeooo001", + "title": "ComfyUI-leo-GVHMR", + "reference": "https://github.com/leeooo001/ComfyUI-leo-GVHMR", + "files": [ + "https://github.com/leeooo001/ComfyUI-leo-GVHMR" + ], + "install_type": "git-clone", + "description": "Unoffice Hamer-ComfyUI by leo\nNOTE:base on [a/GVHMR](https://github.com/zju3dv/GVHMR)" + }, + { + "author": "leeooo001", + "title": "RealisDance-ComfyUI", + "reference": "https://github.com/leeooo001/ComfyUI-leo-RealisDance", + "files": [ + "https://github.com/leeooo001/ComfyUI-leo-RealisDance" + ], + "install_type": "git-clone", + "description": "Unoffice RealisDance-ComfyUI by leo\nNOTE:base on [a/RealisDance](https://github.com/damo-cv/RealisDance), modified on [a/RealisDanceComfyui](https://github.com/AIFSH/RealisDance-ComfyUI)" + }, + { + "author": "jags111", + "title": "NyaamZ/efficiency-nodes-ED", + "reference": "https://github.com/NyaamZ/efficiency-nodes-ED", + "files": [ + "https://github.com/NyaamZ/efficiency-nodes-ED" + ], + "install_type": "git-clone", + "description": "This forked repo supports efficiency-nodes-comfyui. Additional features." + }, + { + "author": "SeaArtLab", + "title": "zer0int/ComfyUI-Long-CLIP", + "reference": "https://github.com/zer0int/ComfyUI-Long-CLIP", + "files": [ + "https://github.com/zer0int/ComfyUI-Long-CLIP" + ], + "install_type": "git-clone", + "description": "This forked repo supports FLUX.1 not only SD1.5, SDXL." + }, + { + "author": "meimeilook", + "title": "ComfyUI_IPAdapter_plus.old [backward compatbility]", + "reference": "https://github.com/meimeilook/ComfyUI_IPAdapter_plus.old", + "files": [ + "https://github.com/meimeilook/ComfyUI_IPAdapter_plus.old" + ], + "install_type": "git-clone", + "description": "This repo is created to provide backward compatibility for workflows configured with the old IPAdapter." + }, + { + "author": "ZHO-ZHO-ZHO", + "title": "Dr.Lt.Data/ComfyUI-YoloWorld-EfficientSAM", + "reference": "https://github.com/ltdrdata/ComfyUI-YoloWorld-EfficientSAM", + "files": [ + "https://github.com/ltdrdata/ComfyUI-YoloWorld-EfficientSAM" + ], + "install_type": "git-clone", + "description": "This fork includes [a/PR32](https://github.com/ZHO-ZHO-ZHO/ComfyUI-YoloWorld-EfficientSAM/pull/32)" + }, + { + "author": "ertu110", + "title": "sdxl_prompt_style", + "reference": "https://github.com/ertu110/sdxl_prompt_style", + "files": [ + "https://github.com/ertu110/sdxl_prompt_style" + ], + "install_type": "git-clone", + "description": "This project is a complete benchmark [a/https://github.com/twri/sdxl_prompt_styler](https://github.com/twri/sdxl_prompt_styler) A large amount of code inside comes from https://github.com/twri/sdxl_prompt_styler Project and [a/https://www.nodecafe.org/package/pythongosssss_ComfyUI-Custom-Scripts](https://www.nodecafe.org/package/pythongosssss_ComfyUI-Custom-Scripts) project\nThe functionality of this project is related to https://github.com/twri/sdxl_prompt_styler Highly overlapping, the only purpose of creating this project is because there are too many styles when selecting, resulting in a long and inconvenient dropdown box. Therefore, To address this issue, this project has added a secondary menu to the style." + }, + { + "author": "gustproof", + "title": "ComfyUI_IPAdapter_plus_Style_Components", + "reference": "https://github.com/gustproof/ComfyUI_IPAdapter_plus_Style_Components", + "files": [ + "https://github.com/gustproof/ComfyUI_IPAdapter_plus_Style_Components" + ], + "install_type": "git-clone", + "description": "Style Components is an IP-Adapter model conditioned on anime styles. The style embeddings can either be extracted from images or created manually. This repo currently only supports the SDXL model trained on AutismmixPony." + }, + { + "author": "gameltb", + "title": "comfyui-stablsr", + "reference": "https://github.com/gameltb/Comfyui-StableSR", + "files": [ + "https://github.com/gameltb/Comfyui-StableSR" + ], + "install_type": "git-clone", + "description": "This is a development respository for debugging migration of StableSR to ComfyUI\n\nNOTE:Forked from [https://github.com/gameltb/Comfyui-StableSR]\nPut the StableSR [a/webui_786v_139.ckpt](https://huggingface.co/Iceclear/StableSR/resolve/main/webui_768v_139.ckpt) model into Comyfui/models/stablesr/, Put the StableSR [a/stablesr_768v_000139.ckpt](https://huggingface.co/Iceclear/StableSR/resolve/main/stablesr_768v_000139.ckpt) model into Comyfui/models/checkpoints/" + }, + { + "author": "city96", + "title": "Efficient-Large-Model/Extra Models for ComfyUI", + "reference": "https://github.com/Efficient-Large-Model/ComfyUI_ExtraModels", + "files": [ + "https://github.com/Efficient-Large-Model/ComfyUI_ExtraModels" + ], + "install_type": "git-clone", + "description": "A forked version of ComfyUI_ExtraModels. (modified by Efficient-Large-Model)" + }, + { + "author": "Pablerdo", + "title": "ComfyUI-PSNodes", + "reference": "https://github.com/Pablerdo/ComfyUI-PSNodes", + "files": [ + "https://github.com/Pablerdo/ComfyUI-PSNodes" + ], + "install_type": "git-clone", + "description": "A fork of KJNodes for ComfyUI.\nVarious quality of life -nodes for ComfyUI, mostly just visual stuff to improve usability" + } + ] +} \ No newline at end of file diff --git a/ComfyUI/custom_nodes/ComfyUI-Manager/node_db/forked/extension-node-map.json b/ComfyUI/custom_nodes/ComfyUI-Manager/node_db/forked/extension-node-map.json new file mode 100644 index 0000000000000000000000000000000000000000..9e26dfeeb6e641a33dae4961196235bdb965b21b --- /dev/null +++ b/ComfyUI/custom_nodes/ComfyUI-Manager/node_db/forked/extension-node-map.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/ComfyUI/custom_nodes/ComfyUI-Manager/node_db/forked/model-list.json b/ComfyUI/custom_nodes/ComfyUI-Manager/node_db/forked/model-list.json new file mode 100644 index 0000000000000000000000000000000000000000..8e3e1dc4858a08aa46190aa53ba320d565206cf4 --- /dev/null +++ b/ComfyUI/custom_nodes/ComfyUI-Manager/node_db/forked/model-list.json @@ -0,0 +1,3 @@ +{ + "models": [] +} diff --git a/ComfyUI/custom_nodes/ComfyUI-Manager/node_db/forked/scan.sh b/ComfyUI/custom_nodes/ComfyUI-Manager/node_db/forked/scan.sh new file mode 100644 index 0000000000000000000000000000000000000000..5d8d8c48b6e3f48dc1491738c1226f574909c05d --- /dev/null +++ b/ComfyUI/custom_nodes/ComfyUI-Manager/node_db/forked/scan.sh @@ -0,0 +1,4 @@ +#!/bin/bash +source ../../../../venv/bin/activate +rm .tmp/*.py > /dev/null +python ../../scanner.py diff --git a/ComfyUI/custom_nodes/ComfyUI-Manager/node_db/legacy/alter-list.json b/ComfyUI/custom_nodes/ComfyUI-Manager/node_db/legacy/alter-list.json new file mode 100644 index 0000000000000000000000000000000000000000..9e26dfeeb6e641a33dae4961196235bdb965b21b --- /dev/null +++ b/ComfyUI/custom_nodes/ComfyUI-Manager/node_db/legacy/alter-list.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/ComfyUI/custom_nodes/ComfyUI-Manager/node_db/legacy/custom-node-list.json b/ComfyUI/custom_nodes/ComfyUI-Manager/node_db/legacy/custom-node-list.json new file mode 100644 index 0000000000000000000000000000000000000000..6c3e891d9267b9733b840c0c40197105c6c2dda5 --- /dev/null +++ b/ComfyUI/custom_nodes/ComfyUI-Manager/node_db/legacy/custom-node-list.json @@ -0,0 +1,2668 @@ +{ + "custom_nodes": [ + { + "author": "fablestudio", + "title": "ComfyUI-Showrunner-Utils [REMOVED]", + "reference": "https://github.com/fablestudio/ComfyUI-Showrunner-Utils", + "files": [ + "https://github.com/fablestudio/ComfyUI-Showrunner-Utils" + ], + "install_type": "git-clone", + "description": "NODES: Align Face, Generate Timestamp, GetMostCommonColors, Alpha Crop and Position Image, Shrink Image" + }, + { + "author": "skayka", + "title": "ComfyUI-DreamFit [REMOVED]", + "reference": "https://github.com/skayka/ComfyUI-DreamFit", + "files": [ + "https://github.com/skayka/ComfyUI-DreamFit" + ], + "install_type": "git-clone", + "description": "Garment-centric human generation nodes for ComfyUI using DreamFit with Flux.\nDreamFit is a powerful adapter system that enhances Flux models with garment-aware generation capabilities, enabling high-quality fashion and clothing generation." + }, + { + "author": "domenecmiralles", + "title": "obobo_nodes [REMOVED]", + "reference": "https://github.com/domenecmiralles/obobo_nodes", + "files": [ + "https://github.com/domenecmiralles/obobo_nodes" + ], + "install_type": "git-clone", + "description": "A collection of custom nodes for ComfyUI that provide various input and output capabilities." + }, + { + "author": "NicholasKao1029", + "title": "comfyui-pixxio [REMOVED]", + "reference": "https://github.com/NicholasKao1029/comfyui-pixxio", + "files": [ + "https://github.com/NicholasKao1029/comfyui-pixxio" + ], + "install_type": "git-clone", + "description": "NODES: Auto-Upload Image to Pixxio Collection, Load Image from Pixx.io" + }, + { + "author": "ComfyUI-Workflow", + "title": "ComfyUI OpenAI Nodes [REMOVED]", + "reference": "https://github.com/ComfyUI-Workflow/ComfyUI-OpenAI", + "files": [ + "https://github.com/ComfyUI-Workflow/ComfyUI-OpenAI" + ], + "install_type": "git-clone", + "description": "By utilizing OpenAI's powerful vision models, this node enables you to incorporate state-of-the-art image understanding into your ComfyUI projects with minimal setup." + }, + { + "author": "dionren", + "title": "Export Workflow With Cyuai Api Available Nodes [REMOVED]", + "id": "comfyUI-Pro-Export-Tool", + "reference": "https://github.com/dionren/ComfyUI-Pro-Export-Tool", + "files": [ + "https://github.com/dionren/ComfyUI-Pro-Export-Tool" + ], + "install_type": "git-clone", + "description": "This is a node to convert workflows to cyuai api available nodes." + }, + { + "author": "1H-hobit", + "title": "ComfyUI_InternVL3 [REMOVED]", + "reference": "https://github.com/1H-hobit/ComfyUI_InternVL3", + "files": [ + "https://github.com/1H-hobit/ComfyUI_InternVL3" + ], + "install_type": "git-clone", + "description": "ComfyUI for [a/InternVL](https://github.com/OpenGVLab/InternVL)" + }, + { + "author": "spacepxl", + "title": "ComfyUI-Florence-2 [DEPRECATED]", + "id": "florence2-spacepxl", + "reference": "https://github.com/spacepxl/ComfyUI-Florence-2", + "files": [ + "https://github.com/spacepxl/ComfyUI-Florence-2" + ], + "install_type": "git-clone", + "description": "[a/https://huggingface.co/microsoft/Florence-2-large-ft](https://huggingface.co/microsoft/Florence-2-large-ft)\nLarge or base model, support for captioning and bbox task modes, more coming soon." + }, + { + "author": "xxxxxxxxxxxc", + "title": "flux-kontext-diff-merge [REMOVED]", + "reference": "https://github.com/xxxxxxxxxxxc/flux-kontext-diff-merge", + "files": [ + "https://github.com/xxxxxxxxxxxc/flux-kontext-diff-merge" + ], + "install_type": "git-clone", + "description": "Preserve image quality with flux-kontext-diff-merge. This ComfyUI node merges only changed areas from AI edits, ensuring clarity and detail." + }, + { + "author": "TechnoByteJS", + "title": "TechNodes [REMOVED]", + "id": "technodes", + "reference": "https://github.com/TechnoByteJS/ComfyUI-TechNodes", + "files": [ + "https://github.com/TechnoByteJS/ComfyUI-TechNodes" + ], + "install_type": "git-clone", + "description": "ComfyUI nodes for merging, testing and more.\nNOTE: SDNext Merge, VAE Merge, MBW Layers, Repeat VAE, Quantization." + }, + { + "author": "DDDDEEP", + "title": "ComfyUI-DDDDEEP [REMOVED]", + "reference": "https://github.com/DDDDEEP/ComfyUI-DDDDEEP", + "files": [ + "https://github.com/DDDDEEP/ComfyUI-DDDDEEP" + ], + "install_type": "git-clone", + "description": "NODES: AutoWidthHeight, ReturnIntSeed, OppositeBool, PromptItemCollection" + }, + { + "author": "manifestations", + "title": "ComfyUI Ethnic Outfits Custom Nodes [REMOVED]", + "reference": "https://github.com/manifestations/comfyui-outfits", + "files": [ + "https://github.com/manifestations/comfyui-outfits" + ], + "install_type": "git-clone", + "description": "Custom ComfyUI nodes for generating outfit prompts representing diverse ethnicities, cultures, and regions. Uses extensible JSON data for clothing, accessories, and poses, with “random/disabled” dropdowns for flexibility. Advanced prompt engineering is supported via Ollama LLM integration. Easily add new regions, ethnicities, or cultures by updating data files and creating lightweight node wrappers. Designed for artists, researchers, and developers seeking culturally rich, customizable prompt generation in ComfyUI workflows." + }, + { + "author": "MitoshiroPJ", + "title": "ComfyUI Slothful Attention [REMOVED]", + "reference": "https://github.com/MitoshiroPJ/comfyui_slothful_attention", + "files": [ + "https://github.com/MitoshiroPJ/comfyui_slothful_attention" + ], + "install_type": "git-clone", + "description": "This custom node allow controlling output without training. The reducing method is similar to [a/Spatial-Reduction Attention](https://paperswithcode.com/method/spatial-reduction-attention)." + }, + { + "author": "MitoshiroPJ", + "title": "comfyui_focal_sampler [REMOVED]", + "reference": "https://github.com/MitoshiroPJ/comfyui_focal_sampler", + "files": [ + "https://github.com/MitoshiroPJ/comfyui_focal_sampler" + ], + "install_type": "git-clone", + "description": "Apply additional sampling to specific area" + }, + { + "author": "manifestations", + "title": "ComfyUI Ethnic Outfit & Prompt Enhancer Nodes [REMOVED]", + "reference": "https://github.com/manifestations/comfyui-indian-outfit", + "files": [ + "https://github.com/manifestations/comfyui-indian-outfit" + ], + "install_type": "git-clone", + "description": "Features:\n* Extensive options for Indian, Indonesian, and international clothing, jewelry, accessories, and styles\n* Multiple jewelry and accessory fields (with material support: gold, diamond, silver, leather, beads, etc.)\n* Support for tattoos, henna, hair styles, poses, shot types, lighting, and photography genres\n* Seamless prompt expansion using your own Ollama LLM instance\n* Modular, extensible JSON data files for easy customization" + }, + { + "author": "coVISIONSld", + "title": "ComfyUI-OmniGen2 [REMOVED]", + "reference": "https://github.com/coVISIONSld/ComfyUI-OmniGen2", + "files": [ + "https://github.com/coVISIONSld/ComfyUI-OmniGen2" + ], + "install_type": "git-clone", + "description": "ComfyUI-OmniGen2 is a custom node package for the OmniGen2 model, enabling advanced text-to-image generation and visual understanding." + }, + { + "author": "S4MUEL-404", + "title": "ComfyUI-S4Tool-Image-Overlay [REMOVED]", + "reference": "https://github.com/S4MUEL-404/ComfyUI-S4Tool-Image-Overlay", + "files": [ + "https://github.com/S4MUEL-404/ComfyUI-S4Tool-Image-Overlay" + ], + "install_type": "git-clone", + "description": "Quickly set up image overlay effects" + }, + { + "author": "akspa0", + "title": "ComfyUI-FapMixPlus [REMOVED]", + "reference": "https://github.com/akspa0/ComfyUI-FapMixPlus", + "files": [ + "https://github.com/akspa0/ComfyUI-FapMixPlus" + ], + "install_type": "git-clone", + "description": "This is an audio processing script that applies soft limiting, optional loudness normalization, and optional slicing for transcription. It can also produce stereo-mixed outputs with optional audio appended to the end. The script organizes processed files into structured folders with sanitized filenames and retains original timestamps for continuity." + }, + { + "author": "RedmondAI", + "title": "comfyui-tools [UNSAFE]", + "reference": "https://github.com/RedmondAI/comfyui-tools", + "files": [ + "https://github.com/RedmondAI/comfyui-tools" + ], + "install_type": "git-clone", + "description": "Custom extensions for ComfyUI used by the Redmond3D VFX team.[w/This node pack has a vulnerability that allows it to create files at arbitrary paths.]" + }, + { + "author": "S4MUEL-404", + "title": "Image Position Blend [REMOVED]", + "id": "ComfyUI-Image-Position-Blend", + "version": "1.1", + "reference": "https://github.com/S4MUEL-404/ComfyUI-Image-Position-Blend", + "files": [ + "https://github.com/S4MUEL-404/ComfyUI-Image-Position-Blend" + ], + "install_type": "git-clone", + "description": "A custom node for conveniently adjusting the overlay position of two images." + }, + { + "author": "S4MUEL-404", + "title": "ComfyUI-Text-On-Image [REMOVED]", + "id": "ComfyUI-Text-On-Image", + "reference": "https://github.com/S4MUEL-404/ComfyUI-Text-On-Image", + "files": [ + "https://github.com/S4MUEL-404/ComfyUI-Text-On-Image" + ], + "install_type": "git-clone", + "description": "A custom node for ComfyUI that allows users to add text overlays to images with customizable size, font, position, and shadow." + }, + { + "author": "S4MUEL-404", + "title": "ComfyUI-Prompts-Selector [REMOVED]", + "reference": "https://github.com/S4MUEL-404/ComfyUI-Prompts-Selector", + "files": [ + "https://github.com/S4MUEL-404/ComfyUI-Prompts-Selector" + ], + "install_type": "git-clone", + "description": "Quickly select preset prompts and merge them" + }, + { + "author": "juntaosun", + "title": "ComfyUI_open_nodes [REMOVED]", + "reference": "https://github.com/juntaosun/ComfyUI_open_nodes", + "files": [ + "https://github.com/juntaosun/ComfyUI_open_nodes" + ], + "install_type": "git-clone", + "description": "ComfyUI open nodes by juntaosun." + }, + { + "author": "perilli", + "title": "apw_nodes [DEPRECATED]", + "reference": "https://github.com/alessandroperilli/apw_nodes", + "files": [ + "https://github.com/alessandroperilli/apw_nodes" + ], + "install_type": "git-clone", + "description": "A custom node suite to augment the capabilities of the [a/AP Workflows for ComfyUI](https://perilli.com/ai/comfyui/)[w/'APW_Nodes' has been newly added in place of 'apw_nodes'.]" + }, + { + "author": "markuryy", + "title": "ComfyUI Spiritparticle Nodes [REMOVED]", + "reference": "https://github.com/markuryy/comfyui-spiritparticle", + "files": [ + "https://github.com/markuryy/comfyui-spiritparticle" + ], + "install_type": "git-clone", + "description": "A node pack by spiritparticle." + }, + { + "author": "SpaceKendo", + "title": "Text to video for Stable Video Diffusion in ComfyUI [REMOVED]", + "id": "svd-txt2vid", + "reference": "https://github.com/SpaceKendo/ComfyUI-svd_txt2vid", + "files": [ + "https://github.com/SpaceKendo/ComfyUI-svd_txt2vid" + ], + "install_type": "git-clone", + "description": "This is node replaces the init_image conditioning for the [a/Stable Video Diffusion](https://github.com/Stability-AI/generative-models) image to video model with text embeds, together with a conditioning frame. The conditioning frame is a set of latents." + }, + { + "author": "vovler", + "title": "ComfyUI Civitai Helper Extension [REMOVED]", + "reference": "https://github.com/vovler/comfyui-civitaihelper", + "files": [ + "https://github.com/vovler/comfyui-civitaihelper" + ], + "install_type": "git-clone", + "description": "ComfyUI extension for parsing Civitai PNG workflows and automatically downloading missing models" + }, + { + "author": "DriftJohnson", + "title": "DJZ-Nodes [REMOVED]", + "id": "DJZ-Nodes", + "reference": "https://github.com/MushroomFleet/DJZ-Nodes", + "files": [ + "https://github.com/MushroomFleet/DJZ-Nodes" + ], + "install_type": "git-clone", + "description": "AspectSize and other nodes" + }, + { + "author": "DriftJohnson", + "title": "KokoroTTS Node [REMOVED]", + "reference": "https://github.com/MushroomFleet/DJZ-KokoroTTS", + "files": [ + "https://github.com/MushroomFleet/DJZ-KokoroTTS" + ], + "install_type": "git-clone", + "description": "This node provides advanced text-to-speech functionality powered by KokoroTTS. Follow the instructions below to install, configure, and use the node within your portable ComfyUI installation." + }, + { + "author": "MushroomFleet", + "title": "DJZ-Pedalboard [REMOVED]", + "reference": "https://github.com/MushroomFleet/DJZ-Pedalboard", + "files": [ + "https://github.com/MushroomFleet/DJZ-Pedalboard" + ], + "install_type": "git-clone", + "description": "This project provides a collection of custom nodes designed for enhanced audio effects in ComfyUI. With an intuitive pedalboard interface, users can easily integrate and manipulate various audio effects within their workflows." + }, + { + "author": "MushroomFleet", + "title": "SVG Suite for ComfyUI [REMOVED]", + "reference": "https://github.com/MushroomFleet/svg-suite", + "files": [ + "https://github.com/MushroomFleet/svg-suite" + ], + "install_type": "git-clone", + "description": "SVG Suite is an advanced set of nodes for converting images to SVG in ComfyUI, expanding upon the functionality of ComfyUI-ToSVG." + }, + { + "author": "joeriben", + "title": "AI4ArtsEd Ollama Prompt Node [DEPRECATED]", + "reference": "https://github.com/joeriben/ai4artsed_comfyui", + "files": [ + "https://github.com/joeriben/ai4artsed_comfyui" + ], + "install_type": "git-clone", + "description": "Experimental nodes for ComfyUI. For more, see [a/https://kubi-meta.de/ai4artsed](https://kubi-meta.de/ai4artsed) A custom ComfyUI node for stylistic and cultural transformation of input text using local LLMs served via Ollama. This node allows you to combine a free-form prompt (e.g. translation, poetic recoding, genre shift) with externally supplied text in the ComfyUI graph. The result is processed via an Ollama-hosted model and returned as plain text." + }, + { + "author": "bento234", + "title": "ComfyUI-bento-toolbox [REMOVED]", + "reference": "https://github.com/bento234/ComfyUI-bento-toolbox", + "files": [ + "https://github.com/bento234/ComfyUI-bento-toolbox" + ], + "install_type": "git-clone", + "description": "NODES: Tile Prompt Distributor" + }, + { + "author": "yichengup", + "title": "ComfyUI-VideoBlender [REMOVED]", + "reference": "https://github.com/yichengup/ComfyUI-VideoBlender", + "files": [ + "https://github.com/yichengup/ComfyUI-VideoBlender" + ], + "install_type": "git-clone", + "description": "Video clip mixing" + }, + { + "author": "xl0", + "title": "latent-tools [REMOVED]", + "reference": "https://github.com/xl0/latent-tools", + "files": [ + "https://github.com/xl0/latent-tools" + ], + "install_type": "git-clone", + "description": "Visualize and manipulate the latent space in ComfyUI" + }, + { + "author": "Conor-Collins", + "title": "ComfyUI-CoCoTools [REMOVED]", + "reference": "https://github.com/Conor-Collins/coco_tools", + "files": [ + "https://github.com/Conor-Collins/coco_tools" + ], + "install_type": "git-clone", + "description": "A set of custom nodes for ComfyUI providing advanced image processing, file handling, and utility functions." + }, + { + "author": "theUpsider", + "title": "ComfyUI-Logic [DEPRECATED]", + "id": "comfy-logic", + "reference": "https://github.com/theUpsider/ComfyUI-Logic", + "files": [ + "https://github.com/theUpsider/ComfyUI-Logic" + ], + "install_type": "git-clone", + "description": "An extension to ComfyUI that introduces logic nodes and conditional rendering capabilities." + }, + { + "author": "Malloc-pix", + "title": "comfyui_qwen2.4_vl_node [REMOVED]", + "reference": "https://github.com/Malloc-pix/comfyui_qwen2.4_vl_node", + "files": [ + "https://github.com/Malloc-pix/comfyui_qwen2.4_vl_node" + ], + "install_type": "git-clone", + "description": "NODES: CogVLM2 Captioner, CLIP Dynamic Text Encode(cy)" + }, + { + "author": "inyourdreams-studio", + "title": "ComfyUI-RBLM [REMOVED]", + "reference": "https://github.com/inyourdreams-studio/comfyui-rblm", + "files": [ + "https://github.com/inyourdreams-studio/comfyui-rblm" + ], + "install_type": "git-clone", + "description": "A custom node pack for ComfyUI that provides text manipulation nodes." + }, + { + "author": "dream-computing", + "title": "SyntaxNodes - Image Processing Effects for ComfyUI [REMOVED]", + "reference": "https://github.com/dream-computing/syntax-nodes", + "files": [ + "https://github.com/dream-computing/syntax-nodes" + ], + "install_type": "git-clone", + "description": "A collection of custom nodes for ComfyUI designed to apply various image processing effects, stylizations, and analyses." + }, + { + "author": "UD1sto", + "title": "plugin-utils-nodes [DEPRECATED]", + "reference": "https://github.com/its-DeFine/plugin-utils-nodes", + "files": [ + "https://github.com/its-DeFine/plugin-utils-nodes" + ], + "install_type": "git-clone", + "description": "NODES: Compare Images (SimHash), Image Selector, Temporal Consistency, Update Image Reference, Frame Blend." + }, + { + "author": "hanyingcho", + "title": "ComfyUI LLM Promp [REMOVED]", + "reference": "https://github.com/hanyingcho/comfyui-llmprompt", + "files": [ + "https://github.com/hanyingcho/comfyui-llmprompt" + ], + "install_type": "git-clone", + "description": "NODES: Load llm, Generate Text with LLM, Inference Qwen2VL, Inference Qwen2" + }, + { + "author": "WASasquatch", + "title": "WAS Node Suite [DEPRECATED]", + "id": "was", + "reference": "https://github.com/WASasquatch/was-node-suite-comfyui", + "pip": ["numba"], + "files": [ + "https://github.com/WASasquatch/was-node-suite-comfyui" + ], + "install_type": "git-clone", + "description": "A node suite for ComfyUI with many new nodes, such as image processing, text processing, and more." + }, + { + "author": "TOM1063", + "title": "ComfyUI-SamuraiTools [REMOVED]", + "reference": "https://github.com/TOM1063/ComfyUI-SamuraiTools", + "files": [ + "https://github.com/TOM1063/ComfyUI-SamuraiTools" + ], + "install_type": "git-clone", + "description": "ComfyUI custom node for switching integer values based on boolean conditions" + }, + { + "author": "whitemoney293", + "title": "ComfyUI-MediaUtilities [REMOVED]", + "reference": "https://github.com/ThanaritKanjanametawatAU/ComfyUI-MediaUtilities", + "files": [ + "https://github.com/ThanaritKanjanametawatAU/ComfyUI-MediaUtilities" + ], + "install_type": "git-clone", + "description": "Custom nodes for loading and previewing media from URLs in ComfyUI." + }, + { + "author": "pureexe", + "title": "DiffusionLight-ComfyUI [REMOVED]", + "reference": "https://github.com/pureexe/DiffusionLight-ComfyUI", + "files": [ + "https://github.com/pureexe/DiffusionLight-ComfyUI" + ], + "install_type": "git-clone", + "description": "DiffusionLight (Turbo) implemented in ComfyUI" + }, + { + "author": "gondar-software", + "title": "comfyui-custom-padding [REMOVED]", + "reference": "https://github.com/gondar-software/comfyui-custom-padding", + "files": [ + "https://github.com/gondar-software/comfyui-custom-padding" + ], + "install_type": "git-clone", + "description": "NODES: Adaptive image padding, Adaptive image unpadding" + }, + { + "author": "Charonartist", + "title": "ComfyUI-EagleExporter [REMOVED]", + "reference": "https://github.com/Charonartist/ComfyUI-EagleExporter", + "files": [ + "https://github.com/Charonartist/ComfyUI-EagleExporter" + ], + "install_type": "git-clone", + "description": "This is an extension that automatically saves video files generated with ComfyUI's 'video combine' extension to the Eagle library." + }, + { + "author": "pomePLaszlo-collablyu", + "title": "comfyui_ejam [REMOVED]", + "reference": "https://github.com/PLaszlo-collab/comfyui_ejam", + "files": [ + "https://github.com/PLaszlo-collab/comfyui_ejam" + ], + "install_type": "git-clone", + "description": "Ejam nodes for comfyui" + }, + { + "author": "jonnydolake", + "title": "ComfyUI-AIR-Nodes [REMOVED]", + "reference": "https://github.com/jonnydolake/ComfyUI-AIR-Nodes", + "files": [ + "https://github.com/jonnydolake/ComfyUI-AIR-Nodes" + ], + "install_type": "git-clone", + "description": "NODES: String List To Prompt Schedule, Force Minimum Batch Size, Target Location (Crop), Target Location (Paste), Image Composite Chained, Match Image Count To Mask Count, Random Character Prompts, Parallax Test, Easy Parallax, Parallax GPU Test" + }, + { + "author": "solution9th", + "title": "Comfyui_mobilesam [REMOVED]", + "reference": "https://github.com/solution9th/Comfyui_mobilesam", + "files": [ + "https://github.com/solution9th/Comfyui_mobilesam" + ], + "install_type": "git-clone", + "description": "NODES: Mobile SAM Model Loader, Mobile SAM Detector, Mobile SAM Predictor" + }, + { + "author": "syaofox", + "title": "ComfyUI_fnodes [REMOVED]", + "reference": "https://github.com/syaofox/ComfyUI_fnodes", + "files": [ + "https://github.com/syaofox/ComfyUI_fnodes" + ], + "install_type": "git-clone", + "description": "ComfyUI_fnodes is a collection of custom nodes designed for ComfyUI. These nodes provide additional functionality that can enhance your ComfyUI workflows.\nFile manipulation tools, Image resizing tools, IPAdapter tools, Image processing tools, Mask tools, Face analysis tools, Sampler tools, Miscellaneous tools" + }, + { + "author": "Hangover3832", + "title": "ComfyUI-Hangover-Moondream [DEPRECATED]", + "reference": "https://github.com/Hangover3832/ComfyUI-Hangover-Moondream", + "files": [ + "https://github.com/Hangover3832/ComfyUI-Hangover-Moondream" + ], + "install_type": "git-clone", + "description": "Moondream is a lightweight multimodal large language model.\n[w/WARN:Additional python code will be downloaded from huggingface and executed. You have to trust this creator if you want to use this node!]" + }, + { + "author": "Hangover3832", + "title": "Recognize Anything Model (RAM) for ComfyUI [DEPRECATED]", + "reference": "https://github.com/Hangover3832/ComfyUI-Hangover-Recognize_Anything", + "files": [ + "https://github.com/Hangover3832/ComfyUI-Hangover-Recognize_Anything" + ], + "install_type": "git-clone", + "description": "This is an image recognition node for ComfyUI based on the RAM++ model from [a/xinyu1205](https://huggingface.co/xinyu1205).\nThis node outputs a string of tags with all the recognized objects and elements in the image in English or Chinese language.\nFor image tagging and captioning." + }, + { + "author": "Hangover3832", + "title": "ComfyUI-Hangover-Nodes [DEPRECATED]", + "reference": "https://github.com/Hangover3832/ComfyUI-Hangover-Nodes", + "files": [ + "https://github.com/Hangover3832/ComfyUI-Hangover-Nodes" + ], + "install_type": "git-clone", + "description": "Nodes: MS kosmos-2 Interrogator, Save Image w/o Metadata, Image Scale Bounding Box. An implementation of Microsoft [a/kosmos-2](https://huggingface.co/microsoft/kosmos-2-patch14-224) image to text transformer." + }, + { + "author": "SirLatore", + "title": "ComfyUI-IPAdapterWAN [REMOVED]", + "reference": "https://github.com/SirLatore/ComfyUI-IPAdapterWAN", + "files": [ + "https://github.com/SirLatore/ComfyUI-IPAdapterWAN" + ], + "install_type": "git-clone", + "description": "This extension adapts the [a/InstantX IP-Adapter for SD3.5-Large](https://huggingface.co/InstantX/SD3.5-Large-IP-Adapter) to work with Wan 2.1 and other UNet-based video/image models in ComfyUI.\nUnlike the original SD3 version (which depends on joint_blocks from MMDiT), this version performs sampling-time identity conditioning by dynamically injecting into attention layers — making it compatible with models like Wan 2.1, AnimateDiff, and other non-SD3 pipelines." + }, + { + "author": "Jpzz", + "title": "ComfyUI-VirtualInteraction [UNSAFE/REMOVED]", + "reference": "https://github.com/Jpzz/ComfyUI-VirtualInteraction", + "files": [ + "https://github.com/Jpzz/ComfyUI-VirtualInteraction" + ], + "install_type": "git-clone", + "description": "NODES: virtual interaction custom node when using generative movie\n[w/This nodepack contains a node which is reading arbitrary excel file.]" + }, + { + "author": "satche", + "title": "Prompt Factory [REMOVED]", + "reference": "https://github.com/satche/comfyui-prompt-factory", + "files": [ + "https://github.com/satche/comfyui-prompt-factory" + ], + "install_type": "git-clone", + "description": "A modular system that adds randomness to prompt generation" + }, + { + "author": "MITCAP", + "title": "ComfyUI OpenAI DALL-E 3 Node [REMOVED]", + "reference": "https://github.com/MITCAP/OpenAI-ComfyUI", + "files": [ + "https://github.com/MITCAP/OpenAI-ComfyUI" + ], + "install_type": "git-clone", + "description": "This project provides custom nodes for ComfyUI that integrate with OpenAI's DALL-E 3 and GPT-4o models. The nodes allow users to generate images and describe images using OpenAI's API.\nNOTE: The files in the repo are not organized." + }, + { + "author": "raspie10032", + "title": "ComfyUI NAI Prompt Converter [REMOVED]", + "reference": "https://github.com/raspie10032/ComfyUI_RS_NAI_Local_Prompt_converter", + "files": [ + "https://github.com/raspie10032/ComfyUI_RS_NAI_Local_Prompt_converter" + ], + "install_type": "git-clone", + "description": "A custom node extension for ComfyUI that enables conversion between different prompt formats: NovelAI V4, ComfyUI, and old NovelAI." + }, + { + "author": "holchan", + "title": "ComfyUI-ModelDownloader [REMOVED]", + "reference": "https://github.com/holchan/ComfyUI-ModelDownloader", + "files": [ + "https://github.com/holchan/ComfyUI-ModelDownloader" + ], + "install_type": "git-clone", + "description": "A ComfyUI node to download models(Checkpoints and LoRA) from external links and act as an output standalone node." + }, + { + "author": "Kur0butiMegane", + "title": "Comfyui-StringUtils [DEPRECATED]", + "reference": "https://github.com/Kur0butiMegane/Comfyui-StringUtils", + "files": [ + "https://github.com/Kur0butiMegane/Comfyui-StringUtils" + ], + "install_type": "git-clone", + "description": "NODES: Prompt Normalizer, String Splitter, String Line Selector, Extract Markup Value" + }, + { + "author": "Apache0ne", + "title": "ComfyUI-LantentCompose [REMOVED]", + "reference": "https://github.com/Apache0ne/ComfyUI-LantentCompose", + "files": [ + "https://github.com/Apache0ne/ComfyUI-LantentCompose" + ], + "install_type": "git-clone", + "description": "Interpolate sdxl latents using slerp with and without a mask. use with unsample nodes for best effect.\nNOTE: The files in the repo are not organized." + }, + { + "author": "jax-explorer", + "title": "ComfyUI-H-flow [REMOVED]", + "reference": "https://github.com/jax-explorer/ComfyUI-H-flow", + "files": [ + "https://github.com/jax-explorer/ComfyUI-H-flow" + ], + "install_type": "git-clone", + "description": "NODES: Wan2-1 Image To Video, LLM Task, Save Image, Save Video, Show Text, FluxPro Ultra, IdeogramV2 Turbo, Runway Image To Video, Kling Image To Video, Replace Text, Join Text, Test Image, Test Text" + }, + { + "author": "Apache0ne", + "title": "SambaNova [REMOVED]", + "id": "SambaNovaAPI", + "reference": "https://github.com/Apache0ne/SambaNova", + "files": [ + "https://github.com/Apache0ne/SambaNova" + ], + "install_type": "git-clone", + "description": "Super Fast LLM's llama3.1-405B,70B,8B and more" + }, + { + "author": "Apache0ne", + "title": "ComfyUI-EasyUrlLoader [REMOVED]", + "id": "easy-url-loader", + "reference": "https://github.com/Apache0ne/ComfyUI-EasyUrlLoader", + "files": [ + "https://github.com/Apache0ne/ComfyUI-EasyUrlLoader" + ], + "install_type": "git-clone", + "description": "A simple YT downloader node for ComfyUI using video Urls. Can be used with VHS nodes etc." + }, + { + "author": "nxt5656", + "title": "ComfyUI-Image2OSS [REMOVED]", + "reference": "https://github.com/nxt5656/ComfyUI-Image2OSS", + "files": [ + "https://github.com/nxt5656/ComfyUI-Image2OSS" + ], + "install_type": "git-clone", + "description": "Upload the image to Alibaba Cloud OSS." + }, + { + "author": "ainewsto", + "title": "Comfyui_Comfly", + "reference": "https://github.com/ainewsto/Comfyui_Comfly", + "files": [ + "https://github.com/ainewsto/Comfyui_Comfly" + ], + "install_type": "git-clone", + "description": "NODES: Comfly_Mj, Comfly_mjstyle, Comfly_upload, Comfly_Mju, Comfly_Mjv, Comfly_kling_videoPreview\nNOTE: Comfyui_Comfly_v2 is introduced." + }, + { + "author": "shinich39", + "title": "comfyui-to-inpaint", + "reference": "https://github.com/shinich39/comfyui-to-inpaint", + "files": [ + "https://github.com/shinich39/comfyui-to-inpaint" + ], + "install_type": "git-clone", + "description": "Send preview image to inpaint workflow." + }, + { + "author": "magic-quill", + "title": "ComfyUI_MagicQuill [NOT MAINTAINED]", + "id": "MagicQuill", + "reference": "https://github.com/magic-quill/ComfyUI_MagicQuill", + "files": [ + "https://github.com/magic-quill/ComfyUI_MagicQuill" + ], + "install_type": "git-clone", + "description": "Towards GPT-4 like large language and visual assistant.\nNOTE: The current version has not been maintained for a long time and does not work. Please use https://github.com/brantje/ComfyUI_MagicQuill instead." + }, + { + "author": "shinich39", + "title": "comfyui-event-handler [USAFE/REMOVED]", + "reference": "https://github.com/shinich39/comfyui-event-handler", + "files": [ + "https://github.com/shinich39/comfyui-event-handler" + ], + "install_type": "git-clone", + "description": "Javascript code will run when an event fires. [w/This node allows you to execute arbitrary JavaScript code as input for the workflow.]" + }, + { + "author": "Moooonet", + "title": "ComfyUI-ArteMoon [REMOVED]", + "reference": "https://github.com/Moooonet/ComfyUI-ArteMoon", + "files": [ + "https://github.com/Moooonet/ComfyUI-ArteMoon" + ], + "install_type": "git-clone", + "description": "This plugin works with [a/IF_AI_Tools](https://github.com/if-ai/ComfyUI-IF_AI_tools) to build a workflow in ComfyUI that uses AI to assist in generating prompts." + }, + { + "author": "ryanontheinside", + "title": "ComfyUI-MediaPipe-Vision [REMOVED]", + "reference": "https://github.com/ryanontheinside/ComfyUI-MediaPipe-Vision", + "files": [ + "https://github.com/ryanontheinside/ComfyUI-MediaPipe-Vision" + ], + "install_type": "git-clone", + "description": "A centralized wrapper of all MediaPipe vision tasks for ComfyUI." + }, + { + "author": "shinich39", + "title": "comfyui-textarea-command [REMOVED]", + "reference": "https://github.com/shinich39/comfyui-textarea-command", + "files": [ + "https://github.com/shinich39/comfyui-textarea-command" + ], + "install_type": "git-clone", + "description": "Add command and comment in textarea. (e.g. // Disabled line)" + }, + { + "author": "shinich39", + "title": "comfyui-parse-image [REMOVED]", + "reference": "https://github.com/shinich39/comfyui-parse-image", + "files": [ + "https://github.com/shinich39/comfyui-parse-image" + ], + "install_type": "git-clone", + "description": "Extract metadata from image." + }, + { + "author": "shinich39", + "title": "comfyui-put-image [REMOVED]", + "reference": "https://github.com/shinich39/comfyui-put-image", + "files": [ + "https://github.com/shinich39/comfyui-put-image" + ], + "install_type": "git-clone", + "description": "Load image from directory." + }, + { + "author": "fredconex", + "title": "TripoSG Nodes for ComfyUI [REMOVED]", + "reference": "https://github.com/fredconex/ComfyUI-TripoSG", + "files": [ + "https://github.com/fredconex/ComfyUI-TripoSG" + ], + "install_type": "git-clone", + "description": "Created by Alfredo Fernandes inspired by Hunyuan3D nodes by Kijai. This extension adds TripoSG 3D mesh generation capabilities to ComfyUI, allowing you to generate 3D meshes from a single image using the TripoSG model." + }, + { + "author": "fredconex", + "title": "ComfyUI-PaintTurbo [REMOVED]", + "reference": "https://github.com/fredconex/ComfyUI-PaintTurbo", + "files": [ + "https://github.com/fredconex/ComfyUI-PaintTurbo" + ], + "install_type": "git-clone", + "description": "NODES: Hunyuan3D Texture Mesh" + }, + { + "author": "zhuanqianfish", + "title": "TaesdDecoder [REMOVED]", + "reference": "https://github.com/zhuanqianfish/TaesdDecoder", + "files": [ + "https://github.com/zhuanqianfish/TaesdDecoder" + ], + "install_type": "git-clone", + "description": "use TAESD decoded image.you need donwload taesd_decoder.pth and taesdxl_decoder.pth to vae_approx folder first.\n It will result in a slight loss of image quality and a significant decrease in peak video memory during decoding." + }, + { + "author": "myAiLemon", + "title": "MagicAutomaticPicture [REMOVED]", + "reference": "https://github.com/myAiLemon/MagicAutomaticPicture", + "files": [ + "https://github.com/myAiLemon/MagicAutomaticPicture" + ], + "install_type": "git-clone", + "description": "A comfyui node package that can generate pictures and automatically save positive prompts and eliminate unwanted prompts" + }, + { + "author": "thisiseddy-ab", + "title": "ComfyUI-Edins-Ultimate-Pack [REMOVED]", + "reference": "https://github.com/thisiseddy-ab/ComfyUI-Edins-Ultimate-Pack", + "files": [ + "https://github.com/thisiseddy-ab/ComfyUI-Edins-Ultimate-Pack" + ], + "install_type": "git-clone", + "description": "Well i needet a Tiled Ksampler that still works for Comfy UI there were none so i made one, in this Package i will put all Nodes i will develop for Comfy Ui still in beta alot will change.." + }, + { + "author": "Davros666", + "title": "safetriggers [REMOVED]", + "reference": "https://github.com/Davros666/safetriggers", + "files": [ + "https://github.com/Davros666/safetriggers" + ], + "install_type": "git-clone", + "description": "ComfyUI Nodes for READING TRIGGERS, TRIGGER-WORDS, TRIGGER-PHRASES FROM LoRAs" + }, + { + "author": "cubiq", + "title": "Simple Math [REMOVED]", + "id": "simplemath", + "reference": "https://github.com/cubiq/ComfyUI_SimpleMath", + "files": [ + "https://github.com/cubiq/ComfyUI_SimpleMath" + ], + "install_type": "git-clone", + "description": "custom node for ComfyUI to perform simple math operations" + }, + { + "author": "lucafoscili", + "title": "LF Nodes [DEPRECATED]", + "reference": "https://github.com/lucafoscili/comfyui-lf", + "files": [ + "https://github.com/lucafoscili/comfyui-lf" + ], + "install_type": "git-clone", + "description": "Custom nodes with a touch of extra UX, including: history for primitives, JSON manipulation, logic switches with visual feedback, LLM chat... and more!" + }, + { + "author": "AI2lab", + "title": "comfyUI-tool-2lab [REMOVED]", + "id": "tool-2lab", + "reference": "https://github.com/AI2lab/comfyUI-tool-2lab", + "files": [ + "https://github.com/AI2lab/comfyUI-tool-2lab" + ], + "install_type": "git-clone", + "description": "tool set for developing workflow and publish to web api server" + }, + { + "author": "AI2lab", + "title": "comfyUI-DeepSeek-2lab [REMOVED]", + "id": "deepseek", + "reference": "https://github.com/AI2lab/comfyUI-DeepSeek-2lab", + "files": [ + "https://github.com/AI2lab/comfyUI-DeepSeek-2lab" + ], + "install_type": "git-clone", + "description": "Unofficial implementation of DeepSeek for ComfyUI" + }, + { + "author": "AI2lab", + "title": "comfyUI-kling-api-2lab [REMOVED]", + "reference": "https://github.com/AI2lab/comfyUI-kling-api-2lab", + "files": [ + "https://github.com/AI2lab/comfyUI-kling-api-2lab" + ], + "install_type": "git-clone", + "description": "Unofficial implementation of KLing for ComfyUI" + }, + { + "author": "ZhiHui6", + "title": "comfyui_zhihui_nodes [REMOVED]", + "reference": "https://github.com/ZhiHui6/comfyui_zhihui_nodes", + "files": [ + "https://github.com/ZhiHui6/comfyui_zhihui_nodes" + ], + "install_type": "git-clone", + "description": "NODES: Prompt Preset, Video Batch Loader, Video Combiner" + }, + { + "author": "ImagineerNL", + "title": "comfyui_potrace_svg [REMOVED]", + "reference": "https://github.com/ImagineerNL/comfyui_potrace_svg", + "files": [ + "https://github.com/ImagineerNL/comfyui_potrace_svg" + ], + "install_type": "git-clone", + "description": "This project converts raster images into SVG format using the Potrace library." + }, + { + "author": "kayselmecnun", + "title": "ComfyUI-Qwen-25-VL [REMOVED]", + "reference": "https://github.com/kayselmecnun/ComfyUI-Qwen-25-VL", + "files": [ + "https://github.com/kayselmecnun/ComfyUI-Qwen-25-VL" + ], + "install_type": "git-clone", + "description": "A custom Comfy UI node for using Qwen2.5-VL-3B/7B-Instruct models" + }, + { + "author": "IfnotFr", + "title": "⚡ ComfyUI Connect [REMOVED]", + "reference": "https://github.com/IfnotFr/ComfyUI-Connect", + "files": [ + "https://github.com/IfnotFr/ComfyUI-Connect" + ], + "install_type": "git-clone", + "description": "Transform your ComfyUI into a powerful API, exposing all your saved workflows as ready-to-use HTTP endpoints." + }, + { + "author": "ginlov", + "title": "segment_to_mask_comfyui [REMOVED]", + "reference": "https://github.com/ginlov/segment_to_mask_comfyui", + "files": [ + "https://github.com/ginlov/segment_to_mask_comfyui" + ], + "install_type": "git-clone", + "description": "Nodes:SegToMask" + }, + { + "author": "TGu-97", + "title": "TGu Utilities [REMOVED]", + "id": "tgu", + "reference": "https://github.com/TGu-97/ComfyUI-TGu-utils", + "files": [ + "https://github.com/TGu-97/ComfyUI-TGu-utils" + ], + "install_type": "git-clone", + "description": "Nodes: MPN Switch, MPN Reroute, PN Switch. This is a set of custom nodes for ComfyUI. Mainly focus on control switches." + }, + { + "author": "IfnotFr", + "title": "ComfyUI-Connect [REMOVED]", + "reference": "https://github.com/IfnotFr/ComfyUI-Connect", + "files": [ + "https://github.com/IfnotFr/ComfyUI-Connect" + ], + "install_type": "git-clone", + "description": "Transform your ComfyUI into a powerful API, exposing all your saved workflows as ready-to-use HTTP endpoints." + }, + { + "author": "KurtHokke", + "title": "ComfyUI_KurtHokke-Nodes [REMOVED]", + "reference": "https://github.com/KurtHokke/ComfyUI_KurtHokke-Nodes", + "files": [ + "https://github.com/KurtHokke/ComfyUI_KurtHokke-Nodes" + ], + "install_type": "git-clone", + "description": "ComfyUI_KurtHokke-Nodes" + }, + { + "author": "SpatialDeploy", + "title": "ComfyUI-Voxels [REMOVED]", + "reference": "https://github.com/SpatialDeploy/ComfyUI-Voxels", + "files": [ + "https://github.com/SpatialDeploy/ComfyUI-Voxels" + ], + "install_type": "git-clone", + "description": "Tools for creating voxel based videos" + }, + { + "author": "shinich39", + "title": "comfyui-group-selection [REMOVED]", + "reference": "https://github.com/shinich39/comfyui-group-selection", + "files": [ + "https://github.com/shinich39/comfyui-group-selection" + ], + "install_type": "git-clone", + "description": "Create a new group of nodes." + }, + { + "author": "shinich39", + "title": "connect-from-afar [REMOVED]", + "reference": "https://github.com/shinich39/comfyui-connect-from-afar", + "files": [ + "https://github.com/shinich39/comfyui-connect-from-afar" + ], + "install_type": "git-clone", + "description": "Connect a new link from out of screen." + }, + { + "author": "shinich39", + "title": "comfyui-local-db [REMOVED]", + "reference": "https://github.com/shinich39/comfyui-local-db", + "files": [ + "https://github.com/shinich39/comfyui-local-db" + ], + "install_type": "git-clone", + "description": "Store text to Key-Values pair json." + }, + { + "author": "shinich39", + "title": "comfyui-model-db [REMOVED]", + "reference": "https://github.com/shinich39/comfyui-model-db", + "files": [ + "https://github.com/shinich39/comfyui-model-db" + ], + "install_type": "git-clone", + "description": "Store settings by model." + }, + { + "author": "shinich39", + "title": "comfyui-target-search [REMOVED]", + "reference": "https://github.com/shinich39/comfyui-target-search", + "files": [ + "https://github.com/shinich39/comfyui-target-search" + ], + "install_type": "git-clone", + "description": "Move canvas to target on dragging connection." + }, + { + "author": "chrisgoringe", + "title": "Image chooser [DEPRECATED]", + "id": "image-chooser", + "reference": "https://github.com/chrisgoringe/cg-image-picker", + "files": [ + "https://github.com/chrisgoringe/cg-image-picker" + ], + "install_type": "git-clone", + "description": "A custom node that pauses the flow while you choose which image (or latent) to pass on to the rest of the workflow." + }, + { + "author": "weilin9999", + "title": "WeiLin-ComfyUI-prompt-all-in-one [DEPRECATED]", + "id": "prompt-all-in-one", + "reference": "https://github.com/weilin9999/WeiLin-ComfyUI-prompt-all-in-one", + "files": [ + "https://github.com/weilin9999/WeiLin-ComfyUI-prompt-all-in-one" + ], + "install_type": "git-clone", + "description": "Write prompt words like WebUI" + }, + { + "author": "svetozarov", + "title": "AS_GeminiCaptioning Node [REMOVED]", + "reference": "https://github.com/svetozarov/AS_GeminiCaptioning", + "files": [ + "https://github.com/svetozarov/AS_GeminiCaptioning" + ], + "install_type": "git-clone", + "description": "A ComfyUI node that combines an image with simple text parameters to create a prompt, sends it to the Google Gemini API via the google-generativeai SDK, and returns the generated text response along with the original prompt and an execution log" + }, + { + "author": "shinich39", + "title": "comfyui-load-image-in-seq [REMOVED]", + "reference": "https://github.com/shinich39/comfyui-load-image-in-seq", + "files": [ + "https://github.com/shinich39/comfyui-load-image-in-seq" + ], + "install_type": "git-clone", + "description": "This node is load png image sequentially with metadata. Only supported for PNG format that has been created by ComfyUI.[w/renamed from comfyui-load-image-39. You need to remove previous one and reinstall to this.]" + }, + { + "author": "shinich39", + "title": "comfyui-model-metadata [REMOVED]", + "reference": "https://github.com/shinich39/comfyui-model-metadata", + "files": [ + "https://github.com/shinich39/comfyui-model-metadata" + ], + "install_type": "git-clone", + "description": "Print model metadata on note node" + }, + { + "author": "shinich39", + "title": "comfyui-view-recommendations [REMOVED]", + "reference": "https://github.com/shinich39/comfyui-view-recommendations", + "files": [ + "https://github.com/shinich39/comfyui-view-recommendations" + ], + "install_type": "git-clone", + "description": "Load model generation data from civitai." + }, + { + "author": "jonstreeter", + "title": "Comfyui-PySceneDetect [REMOVED]", + "reference": "https://github.com/jonstreeter/Comfyui-PySceneDetect", + "files": [ + "https://github.com/jonstreeter/Comfyui-PySceneDetect" + ], + "install_type": "git-clone", + "description": "NODES: PySceneDetect Video Processor" + }, + { + "author": "muxueChen", + "title": "ComfyUI-NTQwen25-VL [REMOVED]", + "reference": "https://github.com/muxueChen/ComfyUI-NTQwen25-VL", + "files": [ + "https://github.com/muxueChen/ComfyUI-NTQwen25-VL" + ], + "install_type": "git-clone", + "description": "Qwen25-VL is a plugin for ComfyU" + }, + { + "author": "Makki_Shizu", + "title": "ComfyUI-SaveAnimatedGIF [DEPRECATED]", + "id": "SaveAnimatedGIF", + "reference": "https://github.com/MakkiShizu/ComfyUI-SaveAnimatedGIF", + "files": [ + "https://github.com/MakkiShizu/ComfyUI-SaveAnimatedGIF" + ], + "install_type": "git-clone", + "description": "Save animated GIF format nodes in ComfyUI" + }, + { + "author": "l1yongch1", + "title": "ComfyUI_PhiCaption [REMOVED]", + "reference": "https://github.com/l1yongch1/ComfyUI_PhiCaption", + "files": [ + "https://github.com/l1yongch1/ComfyUI_PhiCaption" + ], + "install_type": "git-clone", + "description": "In addition to achieving conventional single-image, single-round reverse engineering, it can also achieve single-image multi-round and multi-image single-round reverse engineering. Moreover, the Phi model has a better understanding of prompts." + }, + { + "author": "nova-florealis", + "title": "comfyui-alien [REMOVED]", + "reference": "https://github.com/nova-florealis/comfyui-alien", + "files": [ + "https://github.com/nova-florealis/comfyui-alien" + ], + "install_type": "git-clone", + "description": "NODES: Text to Text (LLM), Text Output, Convert to Markdown, List Display (Debug)" + }, + { + "author": "PluMaZero", + "title": "ComfyUI-SpaceFlower [REMOVED]", + "reference": "https://github.com/PluMaZero/ComfyUI-SpaceFlower", + "files": [ + "https://github.com/PluMaZero/ComfyUI-SpaceFlower" + ], + "install_type": "git-clone", + "description": "Nodes: SpaceFlower_Prompt, SpaceFlower_HangulPrompt, ..." + }, + { + "author": "vahidzxc", + "title": "ComfyUI-My-Handy-Nodes [REMOVED]", + "reference": "https://github.com/vahidzxc/ComfyUI-My-Handy-Nodes", + "files": [ + "https://github.com/vahidzxc/ComfyUI-My-Handy-Nodes" + ], + "install_type": "git-clone", + "description": "NODES:VahCropImage" + }, + { + "author": "Samulebotin", + "title": "ComfyUI-FreeVC_wrapper [REMOVED]", + "reference": "https://github.com/Samulebotin/ComfyUI-FreeVC_wrapper", + "files": [ + "https://github.com/Samulebotin/ComfyUI-FreeVC_wrapper" + ], + "install_type": "git-clone", + "description": "A voice conversion extension node for ComfyUI based on FreeVC, enabling high-quality voice conversion capabilities within the ComfyUI framework." + }, + { + "author": "GoingAI1998", + "title": "ComfyUI Web Canvas Node [REMOVED]", + "reference": "https://github.com/GoingAI1998/Comfyui_imgcanvas", + "files": [ + "https://github.com/GoingAI1998/Comfyui_imgcanvas" + ], + "install_type": "git-clone", + "description": "ComfyUI_imgcanvas At present, I have not used the useful comfyui custom node about layer mixing, and I have written a comfyui runtime automatic pop-up window for layer editing node" + }, + { + "author": "807502278", + "title": "ComfyUI_TensorRT_Merge [REMOVED]", + "reference": "https://github.com/807502278/ComfyUI_TensorRT_Merge", + "files": [ + "https://github.com/807502278/ComfyUI_TensorRT_Merge" + ], + "install_type": "git-clone", + "description": "Non diffusion models supported by TensorRT, merged Comfyui plugin, added onnx automatic download and trt model conversion nodes." + }, + { + "author": "logtd", + "title": "ComfyUI-LTXTricks [DEPRECATED]", + "reference": "https://github.com/logtd/ComfyUI-LTXTricks", + "files": [ + "https://github.com/logtd/ComfyUI-LTXTricks" + ], + "install_type": "git-clone", + "description": "A set of nodes that provide additional controls for the LTX Video model" + }, + { + "author": "JichaoLiang", + "title": "Immortal_comfyUI [REMOVED]", + "reference": "https://github.com/JichaoLiang/Immortal_comfyUI", + "files": [ + "https://github.com/JichaoLiang/Immortal_comfyUI" + ], + "install_type": "git-clone", + "description": "NODES:ImNewNode, ImAppendNode, MergeNode, SetProperties, SaveToDirectory, batchNodes, redirectToNode, SetEvent, ..." + }, + { + "author": "Rvage0815", + "title": "ComfyUI-RvTools [REMOVED]", + "reference": "https://github.com/Rvage0815/ComfyUI-RvTools", + "files": [ + "https://github.com/Rvage0815/ComfyUI-RvTools" + ], + "install_type": "git-clone", + "description": "this node contains a lot of small little helpers like switches, passers and selectors that i use a lot to build my workflows." + }, + { + "author": "Rvage0815", + "title": "RvTComfyUI-RvTools_v2 [REMOVED]", + "reference": "https://github.com/Rvage0815/ComfyUI-RvTools_v2", + "files": [ + "https://github.com/Rvage0815/ComfyUI-RvTools_v2" + ], + "install_type": "git-clone", + "description": "this node contains a lot of small little helpers like switches, passers and selectors that i use a lot to build my workflows." + }, + { + "author": "scottmudge", + "title": "ComfyUI_BiscuitNodes [REMOVED]", + "reference": "https://github.com/scottmudge/ComfyUI_BiscuitNodes", + "files": [ + "https://github.com/scottmudge/ComfyUI_BiscuitNodes" + ], + "install_type": "git-clone", + "description": "Load Image From Path Using File Selector" + }, + { + "author": "thanhduong0213929", + "title": "ComfyUI-DeepUnlock [REMOVED]", + "reference": "https://github.com/thanhduong0213929/ComfyUI-DeepUnlock", + "files": [ + "https://github.com/thanhduong0213929/ComfyUI-DeepUnlock" + ], + "install_type": "git-clone", + "description": "DeepFuze is a state-of-the-art deep learning tool that seamlessly integrates with ComfyUI to revolutionize facial transformations, lipsyncing, video generation, voice cloning, face swapping, and lipsync translation. Leveraging advanced algorithms, DeepFuze enables users to combine audio and video with unparalleled realism, ensuring perfectly synchronized facial movements. This innovative solution is ideal for content creators, animators, developers, and anyone seeking to elevate their video editing projects with sophisticated AI-driven features." + }, + { + "author": "pathway8-sudo", + "title": "RMBG [REMOVED]", + "reference": "https://github.com/pathway8-sudo/RMBG", + "files": [ + "https://github.com/pathway8-sudo/RMBG" + ], + "install_type": "git-clone", + "description": "This repository provides a custom node for ComfyUI, leveraging the BriaRMBG model to remove backgrounds from images and output a transparent PNG." + }, + { + "author": "iris-Neko", + "title": "ComfyUI_ascii_art [REMOVED]", + "reference": "https://github.com/iris-Neko/ComfyUI_ascii_art", + "files": [ + "https://github.com/iris-Neko/ComfyUI_ascii_art" + ], + "install_type": "git-clone", + "description": "ComfyUI node for [a/ASCII art controlnet](https://civitai.com/models/986392)" + }, + { + "author": "apesplat", + "title": "ezXY scripts and nodes [NOT MAINTAINED]", + "id": "ezxy", + "reference": "https://github.com/GMapeSplat/ComfyUI_ezXY", + "files": [ + "https://github.com/GMapeSplat/ComfyUI_ezXY" + ], + "install_type": "git-clone", + "description": "Extensions/Patches: Enables linking float and integer inputs and ouputs. Values are automatically cast to the correct type and clamped to the correct range. Works with both builtin and custom nodes.[w/NOTE: This repo patches ComfyUI's validate_inputs and map_node_over_list functions while running. May break depending on your version of ComfyUI. Can be deactivated in config.yaml.]Nodes: A collection of nodes for facilitating the generation of XY plots. Capable of plotting changes over most primitive values.[w/Does not work with current version of Comfyui]" + }, + { + "author": "mie", + "title": "ComfyUI_JanusProCaption [REMOVED]", + "reference": "https://github.com/MieMieeeee/ComfyUI-JanusProCaption", + "files": [ + "https://github.com/MieMieeeee/ComfyUI-JanusProCaption" + ], + "install_type": "git-clone", + "description": "Describe image or create caption files using Janus Pro Model" + }, + { + "author": "Njbx", + "title": "ComfyUI-blockswap [REMOVED]", + "reference": "https://github.com/Njbx/ComfyUI-blockswap", + "files": [ + "https://github.com/Njbx/ComfyUI-blockswap" + ], + "install_type": "git-clone", + "description": "NODES: Block Swap" + }, + { + "author": "T8star1984", + "title": "comfyui-purgevram [REMOVED]", + "reference": "https://github.com/T8star1984/comfyui-purgevram", + "files": [ + "https://github.com/T8star1984/comfyui-purgevram" + ], + "install_type": "git-clone", + "description": "NODES:PurgeVRAM.\nCan be added after any node to clean up vram and memory" + }, + { + "author": "zmwv823", + "title": "ComfyUI-VideoDiffusion [REMOVED]", + "reference": "https://github.com/zmwv823/ComfyUI-VideoDiffusion", + "files": [ + "https://github.com/zmwv823/ComfyUI-VideoDiffusion" + ], + "install_type": "git-clone", + "description": "[a/LatentSync](https://github.com/bytedance/LatentSync) and [a/Sonic](https://github.com/jixiaozhong/Sonic). [w/Just for study purpose. It's not for directly use, u should know how to fix issues.]" + }, + { + "author": "NyaamZ", + "title": "Get Booru Tag ExtendeD [REMOVED]", + "reference": "https://github.com/NyaamZ/ComfyUI-GetBooruTag-ED", + "files": [ + "https://github.com/NyaamZ/ComfyUI-GetBooruTag-ED" + ], + "description": "Get tag from Booru site.", + "install_type": "git-clone" + }, + { + "author": "lingha", + "title": "comfyui_kj [REMOVED]", + "id": "comfyui_kj", + "reference": "https://github.com/XieChengYuan/comfyui_kj", + "files": [ + "https://github.com/XieChengYuan/comfyui_kj" + ], + "install_type": "git-clone", + "description": "comfyui_kj, A tool that can package workflows into projects and publish them to a WeChat Mini Program named Kaji, allowing charges to be collected from users." + }, + { + "author": "myAiLemon", + "title": "MagicGetPromptAutomatically [REMOVED]", + "reference": "https://github.com/myAiLemon/MagicGetPromptAutomatically", + "files": [ + "https://github.com/myAiLemon/MagicGetPromptAutomatically" + ], + "install_type": "git-clone", + "description": "A plug-in that can automatically generate pictures and save txt files in comfyui" + }, + { + "author": "ryanontheinside", + "title": "ComfyUI_ScavengerHunt [REMOVED]", + "reference": "https://github.com/ryanontheinside/ComfyUI_ScavengerHunt", + "files": [ + "https://github.com/ryanontheinside/ComfyUI_ScavengerHunt" + ], + "install_type": "git-clone", + "description": "NODES: Compare Image Similarity (ResNet), Compare Image Similarity (CLIP), Compare Image Types\nNOTE: The files in the repo are not organized." + }, + { + "author": "vpakarinen", + "title": "ComfyUI-GenerationTimer [REMOVED]", + "reference": "https://github.com/vpakarinen/ComfyUI-GenerationTimer", + "files": [ + "https://github.com/vpakarinen/ComfyUI-GenerationTimer" + ], + "install_type": "git-clone", + "description": "NODES: Generation Timer, Image Timer, Timer Display" + }, + { + "author": "RedRayz", + "title": "ComfyUI-Danbooru-To-WD [REMOVED]", + "id": "danbooru2wd", + "reference": "https://github.com/RedRayz/ComfyUI-Danbooru-To-WD", + "files": [ + "https://github.com/RedRayz/ComfyUI-Danbooru-To-WD" + ], + "install_type": "git-clone", + "description": "Converts booru tags to a format suitable for Waifu Diffusion(or Danbooru based models)." + }, + { + "author": "alexgenovese", + "title": "comfyui_CfgPlusPlus [REMOVED]", + "id": "cfgpp", + "reference": "https://gitea.com/NotEvilGirl/cfgpp", + "files": [ + "https://gitea.com/NotEvilGirl/cfgpp" + ], + "install_type": "git-clone", + "description": "CFG++ implemented according to [a/https://cfgpp-diffusion.github.io](https://cfgpp-diffusion.github.io). Basically modified DDIM sampler that makes sampling work at low CFG values (0 ~ 2). Read the CFG++ paper for more details" + }, + { + "author": "hosterosi", + "title": "ComfyUI OpenAI Node", + "reference": "https://github.com/hosterosi/ComfyUI_OpenAI [REMOVED]", + "files": [ + "https://github.com/hosterosi/ComfyUI_OpenAI" + ], + "install_type": "git-clone", + "description": "This custom node for ComfyUI allows users to input multiline text and select a specific line by its number. The node processes the input and returns the selected line along with its index." + }, + { + "author": "Gourieff", + "title": "ReActor Node for ComfyUI [DISABLED]", + "id": "reactor", + "reference": "https://github.com/Gourieff/comfyui-reactor-node", + "files": [ + "https://github.com/Gourieff/comfyui-reactor-node" + ], + "install_type": "git-clone", + "description": "The Fast and Simple 'roop-like' Face Swap Extension Node for ComfyUI, based on ReActor (ex Roop-GE) SD-WebUI Face Swap Extension" + }, + { + "author": "prismwastaken", + "title": "prism-tools [REMOVED]", + "reference": "https://github.com/prismwastaken/prism-comfyui-tools", + "files": [ + "https://github.com/prismwastaken/prism-comfyui-tools" + ], + "install_type": "git-clone", + "description": "prism-tools" + }, + { + "author": "42lux", + "title": "ComfyUI-safety-checker [DEPRECATED]", + "reference": "https://github.com/42lux/ComfyUI-safety-checker", + "files": [ + "https://github.com/42lux/ComfyUI-safety-checker" + ], + "install_type": "git-clone", + "description": "A NSFW/Safety Checker Node for ComfyUI." + }, + { + "author": "riverolls", + "title": "ComfyUI-FJDH", + "reference": "https://github.com/riverolls/ComfyUI-FJDH [REMOVED]", + "files": [ + "https://github.com/riverolls/ComfyUI-FJDH" + ], + "install_type": "git-clone", + "description": "bbox tools, image tools, mask generators, point tools" + }, + { + "author": "jetchopper", + "title": "ComfyUI-GeneraNodes", + "id": "genera", + "reference": "https://github.com/evolox/ComfyUI-GeneraNodes [REMOVED]", + "files": [ + "https://github.com/evolox/ComfyUI-GeneraNodes" + ], + "install_type": "git-clone", + "description": "Genera custom nodes and extensions" + }, + { + "author": "Pos13", + "title": "Cyclist [DEPRECATED]", + "id": "cyclist", + "reference": "https://github.com/Pos13/comfyui-cyclist", + "files": [ + "https://github.com/Pos13/comfyui-cyclist" + ], + "install_type": "git-clone", + "description": "This extension provides tools to iterate generation results between runs. In general, it's for cycles." + }, + { + "author": "leiweiqiang", + "title": "ComfyUI-TRA", + "id": "tra", + "reference": "https://github.com/leiweiqiang/ComfyUI-TRA", + "files": [ + "https://github.com/leiweiqiang/ComfyUI-TRA" + ], + "install_type": "git-clone", + "description": "Nodes:TCL EbSynth, TCL Extract Frames (From File), TCL Extract Frames (From Video), TCL Combine Frames, TCL Save Video (From Frames)" + }, + { + "author": "ai-business-hql", + "title": "comfyUIAgent [REMOVED]", + "reference": "https://github.com/ai-business-hql/comfyUIAgent", + "files": [ + "https://github.com/ai-business-hql/comfyUIAgent" + ], + "install_type": "git-clone", + "description": "test" + }, + { + "author": "daqingliu", + "title": "ComfyUI-SaveImageOSS [REMOVED]", + "reference": "https://github.com/daqingliu/ComfyUI-SaveImageOSS", + "files": [ + "https://github.com/daqingliu/ComfyUI-SaveImageOSS" + ], + "install_type": "git-clone", + "description": "Save images directly to URL, e.g., OSS. Just input the url in the text box!" + }, + { + "author": "shinich39", + "title": "comfyui-textarea-keybindings [REMOVED]", + "reference": "https://github.com/shinich39/comfyui-textarea-keybindings", + "files": [ + "https://github.com/shinich39/comfyui-textarea-keybindings" + ], + "install_type": "git-clone", + "description": "Add keybindings to textarea." + }, + { + "author": "shinich39", + "title": "comfyui-load-image-with-cmd [REMOVED]", + "reference": "https://github.com/shinich39/comfyui-load-image-with-cmd", + "files": [ + "https://github.com/shinich39/comfyui-load-image-with-cmd" + ], + "install_type": "git-clone", + "description": "Load image and partially workflow with javascript." + }, + { + "author": "neuratech-ai", + "title": "ComfyUI-MultiGPU [NOT MAINTAINED]", + "reference": "https://github.com/neuratech-ai/ComfyUI-MultiGPU", + "files": [ + "https://github.com/neuratech-ai/ComfyUI-MultiGPU" + ], + "install_type": "git-clone", + "description": "Experimental nodes for using multiple GPUs in a single ComfyUI workflow.\nThis extension adds new nodes for model loading that allow you to specify the GPU to use for each model. It monkey patches the memory management of ComfyUI in a hacky way and is neither a comprehensive solution nor a well-tested one. Use at your own risk.\nNote that this does not add parallelism. The workflow steps are still executed sequentially just on different GPUs. Any potential speedup comes from not having to constantly load and unload models from VRAM." + }, + { + "author": "jefferyharrell", + "title": "ComfyUI-JHXMP [REMOVED]", + "reference": "https://github.com/jefferyharrell/ComfyUI-JHXMP", + "files": [ + "https://github.com/jefferyharrell/ComfyUI-JHXMP" + ], + "install_type": "git-clone", + "description": "NODES: Save Image With XMP Metadata" + }, + { + "author": "viperyl", + "title": "ComfyUI-BiRefNet [NOT MAINTAINED]", + "id": "comfyui-birefnet", + "reference": "https://github.com/viperyl/ComfyUI-BiRefNet", + "files": [ + "https://github.com/viperyl/ComfyUI-BiRefNet" + ], + "install_type": "git-clone", + "description": "Bilateral Reference Network achieves SOTA result in multi Salient Object Segmentation dataset, this repo pack BiRefNet as ComfyUI nodes, and make this SOTA model easier use for everyone." + }, + { + "author": "asagi4", + "title": "ComfyUI prompt control (LEGACY VERSION)", + "reference": "https://github.com/asagi4/comfyui-prompt-control-legacy", + "files": [ + "https://github.com/asagi4/comfyui-prompt-control-legacy" + ], + "install_type": "git-clone", + "description": "WARNING: These nodes exist only to reproduce old workflows. They are unmaintained See https://github.com/asagi4/comfyui-prompt-control for the revised, current version of prompt control." + }, + { + "author": "doomy23", + "title": "ComfyUI-D00MYsNodes [REMOVED]", + "reference": "https://github.com/doomy23/ComfyUI-D00MYsNodes", + "files": [ + "https://github.com/doomy23/ComfyUI-D00MYsNodes" + ], + "install_type": "git-clone", + "description": "Nodes: Images_Converter, Show_Text, Strings_From_List, Save_Text, Random_Images, Load_Images_From_Paths, JSPaint." + }, + { + "author": "kadirnar", + "title": "comfyui_hub [REMOVED]", + "reference": "https://github.com/kadirnar/comfyui_hub", + "files": [ + "https://github.com/kadirnar/comfyui_hub" + ], + "install_type": "git-clone", + "description": "A collection of nodes randomly selected and gathered, related to noise. NOTE: SD-Advanced-Noise, noise_latent_perlinpinpin, comfy-plasma" + }, + { + "author": "SaltAI", + "title": "SaltAI_AudioViz [REMOVED]", + "id": "saltai-audioviz", + "reference": "https://github.com/get-salt-AI/SaltAI_AudioViz", + "files": [ + "https://github.com/get-salt-AI/SaltAI_AudioViz" + ], + "install_type": "git-clone", + "description": "SaltAI AudioViz contains ComfyUI nodes for generating complex audio reactive visualizations" + }, + { + "author": "SaltAI", + "title": "SaltAI-Open-Resources [REMOVED]", + "id": "saltai-open-resource", + "reference": "https://github.com/get-salt-AI/SaltAI", + "pip": ["numba"], + "files": [ + "https://github.com/get-salt-AI/SaltAI" + ], + "install_type": "git-clone", + "description": "This repository is a collection of open-source nodes and workflows for ComfyUI, a dev tool that allows users to create node-based workflows often powered by various AI models to do pretty much anything.\nOur mission is to seamlessly connect people and organizations with the world’s foremost AI innovations, anywhere, anytime. Our vision is to foster a flourishing AI ecosystem where the world’s best developers can build and share their work, thereby redefining how software is made, pushing innovation forward, and ensuring as many people as possible can benefit from the positive promise of AI technologies.\nWe believe that ComfyUI is a powerful tool that can help us achieve our mission and vision, by enabling anyone to explore the possibilities and limitations of AI models in a visual and interactive way, without coding if desired.\nWe hope that by sharing our nodes and workflows, we can inspire and empower more people to create amazing AI-powered content with ComfyUI." + }, + { + "author": "SaltAI", + "title": "SaltAI_Language_Toolkit [REMOVED]", + "id": "saltai_language_toolkit", + "reference": "https://github.com/get-salt-AI/SaltAI_Language_Toolkit", + "files": [ + "https://github.com/get-salt-AI/SaltAI_Language_Toolkit" + ], + "install_type": "git-clone", + "description": "The project integrates the Retrieval Augmented Generation (RAG) tool [a/Llama-Index](https://www.llamaindex.ai/), [a/Microsoft's AutoGen](https://microsoft.github.io/autogen/), and [a/LlaVA-Next](https://github.com/LLaVA-VL/LLaVA-NeXT) with ComfyUI's adaptable node interface, enhancing the functionality and user experience of the platform." + }, + { + "author": "zmwv823", + "title": "ComfyUI-Sana [DEPRECATED]", + "reference": "https://github.com/zmwv823/ComfyUI-Sana", + "files": [ + "https://github.com/zmwv823/ComfyUI-Sana" + ], + "install_type": "git-clone", + "description": "Unofficial custom-node for [a/SANA: Efficient High-Resolution Image Synthesis with Linear Diffusion Transformer](https://github.com/NVlabs/Sana)\n[w/A init node with lots of bugs, do not try unless interested.]" + }, + { + "author": "ACE-innovate", + "title": "seg-node [REMOVED]", + "reference": "https://github.com/ACE-innovate/seg-node", + "files": [ + "https://github.com/ACE-innovate/seg-node" + ], + "install_type": "git-clone", + "description": "hf cloth seg custom node for comfyui\nNOTE: The files in the repo are not organized." + }, + { + "author": "zefu-lu", + "title": "ComfyUI_InstantX_SD35_Large_IPAdapter [REMOVED]", + "id": "comfyui-instantx-sd3-5-large-ipadapter", + "reference": "https://github.com/zefu-lu/ComfyUI-InstantX-SD3_5-Large-IPAdapter", + "files": [ + "https://github.com/zefu-lu/ComfyUI-InstantX-SD3_5-Large-IPAdapter" + ], + "install_type": "git-clone", + "description": "Custom ComfyUI node for using InstantX SD3.5-Large IPAdapter" + }, + { + "author": "HentaiGirlfriendDotCom", + "title": "comfyui-highlight-connections [REMOVED]", + "reference": "https://github.com/HentaiGirlfriendDotCom/comfyui-highlight-connections", + "files": [ + "https://github.com/HentaiGirlfriendDotCom/comfyui-highlight-connections" + ], + "install_type": "git-clone", + "description": "A node that can be dropped into a group. When a node is then clicked within that group, all nodes and connections in that group get greyed out and the connections from the clicked node go bright red." + }, + { + "author": "huangyangke", + "title": "ComfyUI-Kolors-IpadapterFaceId [DEPRECATED]", + "reference": "https://github.com/huangyangke/ComfyUI-Kolors-IpadapterFaceId", + "files": [ + "https://github.com/huangyangke/ComfyUI-Kolors-IpadapterFaceId" + ], + "install_type": "git-clone", + "description": "NODES:kolors_ipadapter_faceid\nNOTE: The files in the repo are not organized." + }, + { + "author": "zmwv823", + "title": "ComfyUI_Ctrlora [DEPRECATED]", + "reference": "https://github.com/zmwv823/ComfyUI_Ctrlora", + "files": [ + "https://github.com/zmwv823/ComfyUI_Ctrlora" + ], + "install_type": "git-clone", + "description": "Unofficial custom_node for [a/xyfJASON/ctrlora](https://github.com/xyfJASON/ctrlora)." + }, + { + "author": "Fannovel16", + "title": "ComfyUI Loopchain [DEPRECATED]", + "id": "loopchain", + "reference": "https://github.com/Fannovel16/ComfyUI-Loopchain", + "files": [ + "https://github.com/Fannovel16/ComfyUI-Loopchain" + ], + "install_type": "git-clone", + "description": "A collection of nodes which can be useful for animation in ComfyUI. The main focus of this extension is implementing a mechanism called loopchain. A loopchain in this case is the chain of nodes only executed repeatly in the workflow. If a node chain contains a loop node from this extension, it will become a loop chain." + }, + { + "author": "DonBaronFactory", + "title": "ComfyUI-Cre8it-Nodes [DEPRECATED]", + "reference": "https://github.com/DonBaronFactory/ComfyUI-Cre8it-Nodes", + "files": [ + "https://github.com/DonBaronFactory/ComfyUI-Cre8it-Nodes" + ], + "install_type": "git-clone", + "description": "Nodes:CRE8IT Serial Prompter, CRE8IT Apply Serial Prompter, CRE8IT Image Sizer. A few simple nodes to facilitate working wiht ComfyUI Workflows" + }, + { + "author": "thecooltechguy", + "title": "ComfyUI-ComfyRun [DEPRECATED/UNSAFE]", + "reference": "https://github.com/thecooltechguy/ComfyUI-ComfyRun", + "files": [ + "https://github.com/thecooltechguy/ComfyUI-ComfyRun" + ], + "install_type": "git-clone", + "description": "The easiest way to run & share any ComfyUI workflow [a/https://comfyrun.com](https://comfyrun.com)\nNOTE: Vulnerability discovered. Not being managed." + }, + { + "author": "Cardoso-topdev", + "title": "comfyui_meshanything_v1 [REMOVED]", + "reference": "https://github.com/Cardoso-topdev/comfyui_meshanything_v1", + "files": [ + "https://github.com/Cardoso-topdev/comfyui_meshanything_v1" + ], + "install_type": "git-clone", + "description": "MeshAnything V2: Artist-Created Mesh Generation With Adjacent Mesh Tokenization" + }, + { + "author": "palant", + "title": "Extended Save Image for ComfyUI [DEPRECATED]", + "reference": "https://github.com/palant/extended-saveimage-comfyui", + "files": [ + "https://github.com/palant/extended-saveimage-comfyui" + ], + "install_type": "git-clone", + "description": "This custom node is largely identical to the usual Save Image but allows saving images also in JPEG and WEBP formats, the latter with both lossless and lossy compression. Metadata is embedded in the images as usual, and the resulting images can be used to load a workflow." + }, + { + "author": "1038lab", + "title": "ComfyUI-GPT2P [REMOVED]", + "id": "gpt2p", + "reference": "https://github.com/1038lab/ComfyUI-GPT2P", + "files": [ + "https://github.com/1038lab/ComfyUI-GPT2P" + ], + "install_type": "git-clone", + "description": "ComfyUI Node - Hugging Face repositories GTP2 Prompt" + }, + { + "author": "yushan777", + "title": "Y7 Nodes for ComfyUI [REMOVED]", + "id": "y7nodes", + "reference": "https://github.com/yushan777/ComfyUI-Y7Nodes", + "files": [ + "https://github.com/yushan777/ComfyUI-Y7Nodes" + ], + "install_type": "git-clone", + "description": "Nodes:Count_Tokens_(Y7)" + }, + { + "author": "city96", + "title": "SD-Advanced-Noise [DEPRECATED]", + "id": "adv-noise", + "reference": "https://github.com/city96/SD-Advanced-Noise", + "files": [ + "https://github.com/city96/SD-Advanced-Noise" + ], + "install_type": "git-clone", + "description": "Nodes: LatentGaussianNoise, MathEncode. An experimental custom node that generates latent noise directly by utilizing the linear characteristics of the latent space." + }, + { + "author": "shockz0rz", + "title": "InterpolateEverything [DEPRECATED]", + "id": "interpolate-everything", + "reference": "https://github.com/shockz0rz/ComfyUI_InterpolateEverything", + "files": [ + "https://github.com/shockz0rz/ComfyUI_InterpolateEverything" + ], + "install_type": "git-clone", + "description": "Nodes: Interpolate Poses, Interpolate Lineart, ... Custom nodes for interpolating between, well, everything in the Stable Diffusion ComfyUI." + }, + { + "author": "svdC1", + "title": "LoRa Dataset Tools [REMOVED]", + "reference": "https://github.com/svdC1/comfy-ui-lora-dataset-tools", + "files": [ + "https://github.com/svdC1/comfy-ui-lora-dataset-tools" + ], + "install_type": "git-clone", + "description": "NODES:Directory Loader, Filter Images Without Faces, Detect Faces and Draw Detection Box" + }, + { + "author": "MiddleKD", + "title": "ComfyUI-default-workflow-setter [REMOVED]", + "reference": "https://github.com/MiddleKD/ComfyUI-default-workflow-setter", + "files": [ + "https://github.com/MiddleKD/ComfyUI-default-workflow-setter" + ], + "install_type": "git-clone", + "description": "Default workflow setter" + }, + { + "author": "Firetheft", + "title": "ComfyUI-Flux-Prompt-Tools [REMOVED]", + "reference": "https://github.com/Firetheft/ComfyUI-Flux-Prompt-Tools", + "files": [ + "https://github.com/Firetheft/ComfyUI-Flux-Prompt-Tools" + ], + "install_type": "git-clone", + "description": "NODES:Flux Prompt Enhance, Flux Prompt Gemini Flash, Flux Prompt Generator, MiniCPM V2.6 Int4" + }, + { + "author": "jtydhr88", + "title": "ComfyUI Unique3D [DEPRECATED]", + "id": "unique3d", + "reference": "https://github.com/jtydhr88/ComfyUI-Unique3D", + "files": [ + "https://github.com/jtydhr88/ComfyUI-Unique3D" + ], + "install_type": "git-clone", + "description": "ComfyUI Unique3D is custom nodes that running AiuniAI/Unique3D into ComfyUI[w/Please follow readme to install with ComfyUI embedded python.]" + }, + { + "author": "mpiquero7164", + "title": "SaveImgPrompt [DEPRECATED]", + "id": "save-imgprompt", + "reference": "https://github.com/mpiquero7164/ComfyUI-SaveImgPrompt", + "files": [ + "https://github.com/mpiquero7164/ComfyUI-SaveImgPrompt" + ], + "install_type": "git-clone", + "description": "Save a png or jpeg and option to save prompt/workflow in a text or json file for each image in Comfy + Workflow loading." + }, + { + "author": "guoyk93", + "title": "y.k.'s ComfyUI node suite [DEPRECATED]", + "id": "yks", + "reference": "https://github.com/yankeguo-deprecated/yk-node-suite-comfyui", + "files": [ + "https://github.com/yankeguo-deprecated/yk-node-suite-comfyui" + ], + "install_type": "git-clone", + "description": "Nodes: YKImagePadForOutpaint, YKMaskToImage" + }, + { + "author": "adityathiru", + "title": "ComfyUI LLMs [REMOVED]", + "reference": "https://github.com/adityathiru/ComfyUI-LLMs", + "files": [ + "https://github.com/adityathiru/ComfyUI-LLMs" + ], + "install_type": "git-clone", + "description": "Goal: To enable folks to rapidly build complex workflows with LLMs\nNOTE:☠️ This is experimental and not recommended to use in a production environment (yet!)" + }, + { + "author": "DannyStone1999", + "title": "ComfyUI-Depth2Mask [REMOVED]", + "reference": "https://github.com/DannyStone1999/ComfyUI-Depth2Mask", + "files": [ + "https://github.com/DannyStone1999/ComfyUI-Depth2Mask/raw/main/Depth2Mask.py" + ], + "install_type": "copy", + "description": "Nodes:Depth2Mask" + }, + { + "author": "syaofox", + "title": "ComfyUI_FoxTools [REMOVED]", + "reference": "https://github.com/syaofox/ComfyUI_FoxTools", + "files": [ + "https://github.com/syaofox/ComfyUI_FoxTools" + ], + "install_type": "git-clone", + "description": "Nodes:BatchImageFromList, Load Face Occlusion Model, Create Face Mask, Simple FaceAlign, Cacul FaceAlign, Gen Blurbord, Face Align, Face Rotate, ImageAdd, LoadImageList, SaveImage Plus, RegTextFind" + }, + { + "author": "AIFSH", + "title": "SeedVC-ComfyUI [REMOVED]", + "reference": "https://github.com/AIFSH/SeedVC-ComfyUI", + "files": [ + "https://github.com/AIFSH/SeedVC-ComfyUI" + ], + "install_type": "git-clone", + "description": "a custom node for [a/seed-vc](https://github.com/Plachtaa/seed-vc)" + }, + { + "author": "jazhang00", + "title": "ComfyUI Node for Slicedit [REMOVED]", + "reference": "https://github.com/jazhang00/ComfyUI-Slicedit", + "files": [ + "https://github.com/jazhang00/ComfyUI-Slicedit" + ], + "install_type": "git-clone", + "description": "Slicedit main page: [a/https://matankleiner.github.io/slicedit/](https://matankleiner.github.io/slicedit/). Use Slicedit with ComfyUI." + }, + { + "author": "rklaffehn", + "title": "rk-comfy-nodes [REMOVED]", + "id": "rknodes", + "reference": "https://github.com/rklaffehn/rk-comfy-nodes", + "files": [ + "https://github.com/rklaffehn/rk-comfy-nodes" + ], + "install_type": "git-clone", + "description": "Nodes: RK_CivitAIMetaChecker, RK_CivitAIAddHashes." + }, + { + "author": "Extraltodeus", + "title": "CLIP-Token-Injection [REMOVED]", + "reference": "https://github.com/Extraltodeus/CLIP-Token-Injection", + "files": [ + "https://github.com/Extraltodeus/CLIP-Token-Injection" + ], + "install_type": "git-clone", + "description": "These nodes are to edit the text vectors of CLIP models, so to customize how the prompts will be interpreted. You could see it as either customisation, 'one token prompt' up to some limitation and a way to mess with how the text will be interpreted. The edited CLIP can then be saved, or as well the edited tokens themselves. The shared example weights does not contain any image-knowledge but the text vector of the words affected." + }, + { + "author": "openart", + "title": "openart-comfyui-deploy [REMOVED]", + "id": "openart-comfyui-deploy", + "reference": "https://github.com/kulsisme/openart-comfyui-deploy", + "files": [ + "https://github.com/kulsisme/openart-comfyui-deploy" + ], + "install_type": "git-clone", + "description": "NODES: External Boolean (ComfyUI Deploy), External Checkpoint (ComfyUI Deploy), External Image (ComfyUI Deploy), External Video (ComfyUI Deploy x VHS), OpenArt Text, Image Websocket Output (ComfyDeploy), ..." + }, + { + "author": "mittimi", + "title": "ComfyUI_mittimiLoadPreset [DEPRECATED]", + "id": "comfyui-mittimi-load-preset", + "reference": "https://github.com/mittimi/ComfyUI_mittimiLoadPreset", + "files": [ + "https://github.com/mittimi/ComfyUI_mittimiLoadPreset" + ], + "install_type": "git-clone", + "description": "The system selects and loads preset." + }, + { + "author": "jinljin", + "title": "ComfyUI-Talking-Head [REMOVED]", + "reference": "https://github.com/jinljin/ComfyUI-ElevenlabsAndDID-Combine", + "files": [ + "https://github.com/jinljin/ComfyUI-ElevenlabsAndDID-Combine" + ], + "install_type": "git-clone", + "description": "ComfyUI-Talking-Head" + }, + { + "author": "jh-leon-kim", + "title": "ComfyUI-JHK-utils [REMOVED]", + "id": "jhk", + "reference": "https://github.com/jh-leon-kim/ComfyUI-JHK-utils", + "files": [ + "https://github.com/jh-leon-kim/ComfyUI-JHK-utils" + ], + "install_type": "git-clone", + "description": "Nodes:JHK_Utils_LoadEmbed, JHK_Utils_string_merge, JHK_Utils_ImageRemoveBackground" + }, + { + "author": "ilovejohnwhite", + "title": "TatToolkit [REMOVED]", + "reference": "https://github.com/ilovejohnwhite/UncleBillyGoncho", + "files": [ + "https://github.com/ilovejohnwhite/UncleBillyGoncho" + ], + "install_type": "git-clone", + "description": "Nodes:UWU TTK Preprocessor, Pixel Perfect Resolution, Generation Resolution From Image, Generation Resolution From Latent, Enchance And Resize Hint Images, ..." + }, + { + "author": "hzchet", + "title": "ComfyUI_QueueGeneration [REMOVED]", + "reference": "https://github.com/hzchet/ComfyUI_QueueGeneration", + "files": [ + "https://github.com/hzchet/ComfyUI_QueueGeneration" + ], + "install_type": "git-clone", + "description": "NODES:Queue Img2Vid Generation" + }, + { + "author": "ader47", + "title": "ComfyUI-MeshHamer [REMOVED]", + "reference": "https://github.com/ader47/comfyui_meshhamer", + "files": [ + "https://github.com/ader47/comfyui_meshhamer" + ], + "install_type": "git-clone", + "description": "Nodes:MeshHamer Hand Refiner. See also: [a/HaMeR: Hand Mesh Recovery](https://github.com/geopavlakos/hamer/tree/main)" + }, + { + "author": "SEkINVR", + "title": "ComfyUI-Animator", + "reference": "https://github.com/SEkINVR/ComfyUI-Animator [REMOVED]", + "files": [ + "https://github.com/SEkINVR/ComfyUI-Animator" + ], + "install_type": "git-clone", + "description": "This custom node for ComfyUI provides full-body animation capabilities, including facial rigging, various lighting styles, and green screen output." + }, + { + "author": "ZHO-ZHO-ZHO", + "title": "ComfyUI-AnyText [NOT MAINTAINED]", + "reference": "https://github.com/ZHO-ZHO-ZHO/ComfyUI-AnyText", + "files": [ + "https://github.com/ZHO-ZHO-ZHO/ComfyUI-AnyText" + ], + "install_type": "git-clone", + "description": "Unofficial implementation of [a/AnyText](https://github.com/tyxsspa/AnyText/tree/825bcc54687206b15bd7e28ba1a8b095989d58e3) for ComfyUI(EXP)" + }, + { + "author": "shinich39", + "title": "comfyui-pkg39 [DEPRECATED]", + "reference": "https://github.com/shinich39/comfyui-pkg39", + "files": [ + "https://github.com/shinich39/comfyui-pkg39" + ], + "install_type": "git-clone", + "description": "This package has created for generate image from generated image and embedded workflow." + }, + { + "author": "dnl13", + "title": "ComfyUI-dnl13-seg [DEPRECATED]", + "reference": "https://github.com/dnl13/ComfyUI-dnl13-seg", + "files": [ + "https://github.com/dnl13/ComfyUI-dnl13-seg" + ], + "install_type": "git-clone", + "description": "After discovering @storyicon implementation here of Segment Anything, I realized its potential as a powerful tool for ComfyUI if implemented correctly. I delved into the SAM and Dino models. The following is my own adaptation of sam_hq for ComfyUI." + }, + { + "author": "1038lab", + "title": "ComfyUI-latentSizeSelector [REMOVED]", + "id": "ComfyUI-latentSizeSelector", + "reference": "https://github.com/1038lab/ComfyUI_LatentSizeSelector", + "files": [ + "https://github.com/1038lab/ComfyUI_LatentSizeSelector" + ], + "install_type": "git-clone", + "description": "You'll get a new node Latent Size Selector, you can pick the x and y sizes from a list." + }, + { + "author": "hy134300", + "title": "ComfyUI-PhotoMaker-V2 [REMOVED]", + "reference": "https://github.com/hy134300/ComfyUI-PhotoMaker-V2", + "files": [ + "https://github.com/hy134300/ComfyUI-PhotoMaker-V2" + ], + "install_type": "git-clone", + "description": "Nodes for PhotoMaker-V2" + }, + { + "author": "neverbiasu", + "title": "ComfyUI ImageCaptioner [REMOVED]", + "reference": "https://github.com/neverbiasu/ComfyUI-ImageCaptioner", + "files": [ + "https://github.com/neverbiasu/ComfyUI-ImageCaptioner" + ], + "install_type": "git-clone", + "description": "A ComfyUI extension for generating captions for your images. Runs on your own system, no external services used, no filter." + }, + { + "author": "mingqizhang", + "title": "ComfyUI_InSPyResNet_zmq [REMOVED]", + "id": "inspy", + "reference": "https://github.com/mingqizhang/ComfyUI_InSPyResNet_zmq", + "files": [ + "https://github.com/mingqizhang/ComfyUI_InSPyResNet_zmq" + ], + "install_type": "git-clone", + "description": "Nodes:INSPY removebg ModelLoader, INSPY RMBG" + }, + { + "author": "mingqizhang", + "title": "ComfyUI_AEMatter_zmq [REMOVED]", + "id": "aematter", + "reference": "https://github.com/mingqizhang/ComfyUI_AEMatter_zmq", + "files": [ + "https://github.com/mingqizhang/ComfyUI_AEMatter_zmq" + ], + "install_type": "git-clone", + "description": "Nodes:AEMatter_ModelLoader, Create_Trimap, AEMatter_Apply, Mask_Transfor, Replace_Background, Gaussian_Filter, Guide_Filter, Improved_Aplha_Composite" + }, + { + "author": "bradsec", + "title": "ComfyUI_StringTools [REMOVED]", + "id": "stringtools", + "reference": "https://github.com/bradsec/ComfyUI_StringTools", + "files": [ + "https://github.com/bradsec/ComfyUI_StringTools" + ], + "install_type": "git-clone", + "description": "Some simple string tools to modify text and strings in ComfyUI." + }, + { + "author": "Millyarde", + "title": "Pomfy - Photoshop and ComfyUI 2-way sync [REMOVED]", + "reference": "https://github.com/Millyarde/Pomfy", + "files": [ + "https://github.com/Millyarde/Pomfy" + ], + "install_type": "git-clone", + "description": "Photoshop custom nodes inside of ComfyUi, send and get data via Photoshop UXP plugin for cross platform support" + }, + { + "author": "turkyden", + "title": "ComfyUI-Sticker [REMOVED]", + "reference": "https://github.com/turkyden/ComfyUI-Sticker", + "files": [ + "https://github.com/turkyden/ComfyUI-Sticker" + ], + "install_type": "git-clone", + "description": "image to sticker" + }, + { + "author": "turkyden", + "title": "ComfyUI-Comic [REMOVED]", + "id": "comic", + "reference": "https://github.com/turkyden/ComfyUI-Comic", + "files": [ + "https://github.com/turkyden/ComfyUI-Comic" + ], + "install_type": "git-clone", + "description": "a comfyui plugin for image to comic" + }, + { + "author": "turkyden", + "title": "ComfyUI-Avatar [REMOVED]", + "id": "avatar", + "reference": "https://github.com/turkyden/ComfyUI-Avatar", + "files": [ + "https://github.com/turkyden/ComfyUI-Avatar" + ], + "install_type": "git-clone", + "description": "a comfyui plugin for image to avatar" + }, + { + "author": "bvhari", + "title": "LatentToRGB [DEPRECATED]", + "id": "latent2rgb", + "reference": "https://github.com/bvhari/ComfyUI_LatentToRGB", + "files": [ + "https://github.com/bvhari/ComfyUI_LatentToRGB" + ], + "install_type": "git-clone", + "description": "ComfyUI custom node to convert latent to RGB.\nNOTE:This repo has been archived because ComfyUI natively has similar functionality now" + }, + { + "author": "Kaharos94", + "title": "ComfyUI-Saveaswebp [DEPRECATED]", + "id": "save-webp", + "reference": "https://github.com/Kaharos94/ComfyUI-Saveaswebp", + "files": [ + "https://github.com/Kaharos94/ComfyUI-Saveaswebp" + ], + "install_type": "git-clone", + "description": "Save a picture as Webp file in Comfy + Workflow loading" + }, + { + "author": "udi0510", + "title": "comfyui-slicer [REMOVED]", + "id": "slicer", + "reference": "https://github.com/udi0510/comfyui-slicer", + "files": [ + "https://github.com/udi0510/comfyui-slicer" + ], + "install_type": "git-clone", + "description": "Nodes:SlicerNode" + }, + { + "author": "logtd", + "title": "ComfyUI-FLATTEN [REMOVED]", + "id": "flatten", + "reference": "https://github.com/logtd/ComfyUI-FlattenFlow", + "files": [ + "https://github.com/logtd/ComfyUI-FlattenFlow" + ], + "install_type": "git-clone", + "description": "An alternate trajectory processor for ComfyUI-FLATTEN\nNOTE:When using this trajectory type FLATTEN will use roughly 1/4 VRAM and be ~20% faster at the cost of some consistency (especially when injection_steps are low)." + }, + { + "author": "MackinationsAi", + "title": "ComfyUi_Stuctured-Outputs [REMOVED]", + "id": "struct-output", + "reference": "https://github.com/MackinationsAi/ComfyUi_Stuctured-Outputs", + "files": [ + "https://github.com/MackinationsAi/ComfyUi_Stuctured-Outputs" + ], + "install_type": "git-clone", + "description": "This repository contains a custom node for ComfyUI that allows users to save generative image outputs with custom filenames and folder structures. The filenames are padded to four digits, and the positive and negative prompts are embedded in the image metadata." + }, + { + "author": "laksjdjf", + "title": "attention-couple-ComfyUI [DEPRECATED]", + "id": "attention-couple", + "reference": "https://github.com/laksjdjf/attention-couple-ComfyUI", + "files": [ + "https://github.com/laksjdjf/attention-couple-ComfyUI" + ], + "install_type": "git-clone", + "description": "Nodes:Attention couple. This is a custom node that manipulates region-specific prompts. While vanilla ComfyUI employs an area specification method based on latent couples, this node divides regions using attention layers within UNet.\nNOTE: This has been integrated with cgem156-ComfyUI." + }, + { + "author": "phineas-pta", + "title": "comfy-trt-test [DEPRECATED]", + "reference": "https://github.com/phineas-pta/comfy-trt-test", + "files": [ + "https://github.com/phineas-pta/comfy-trt-test" + ], + "install_type": "git-clone", + "description": "Test project for ComfyUI TensorRT Support.\nNOT WORKING YET.\nnot automatic yet, do not use ComfyUI-Manager to install !!!.\nnot beginner-friendly yet, still intended to technical users\nNOTE: The reason for registration in the Manager is for guidance, and for detailed installation instructions, please visit the repository.\nNOTE: Use 'TensorRT Node for ComfyUI' instead of this." + }, + { + "author": "dezi-ai", + "title": "ComfyUI Animate LCM [NOT MAINTAINED]", + "reference": "https://github.com/dezi-ai/ComfyUI-AnimateLCM", + "files": [ + "https://github.com/dezi-ai/ComfyUI-AnimateLCM" + ], + "install_type": "git-clone", + "description": "ComfyUI implementation for [a/AnimateLCM](https://animatelcm.github.io/) [[a/paper](https://arxiv.org/abs/2402.00769)].\b[w/This extension includes a large number of nodes imported from the existing custom nodes, increasing the likelihood of conflicts.]" + }, + { + "author": "christian-byrne", + "title": "elimination-nodes [REMOVED]", + "reference": "https://github.com/christian-byrne/elimination-nodes", + "files": [ + "https://github.com/christian-byrne/elimination-nodes" + ], + "install_type": "git-clone", + "description": "Nodes:Paste Cutout on Base Image" + }, + { + "author": "Levy1417", + "title": "Universal-Data-Processing-Kit [UNSAFE] [REMOVED]", + "reference": "https://github.com/Levy1417/Universal-Data-Processing-Kit", + "files": [ + "https://github.com/Levy1417/Universal-Data-Processing-Kit" + ], + "install_type": "git-clone", + "description": "Nodes:DPK - Any Eval, DPK - Extract Array, DPK - Run External Program, DPK - Any Literals, DPK - Set Node States, DPK - Realtime Text Preview, DPK - Dynamic Action, DPK - Object To Json, DPK - Json To Object\n[w/This extension includes the ability to execute arbitrary code and programs.]" + }, + { + "author": "liusida", + "title": "ComfyUI-Sida-Remove-Image [UNSAFE] [REMOVED]", + "reference": "https://github.com/liusida/ComfyUI-Sida-Remove-Image", + "files": [ + "https://github.com/liusida/ComfyUI-Sida-Remove-Image" + ], + "install_type": "git-clone", + "description": "Nodes: LoadImageWithPrivacy, RemoveImage.[w/This extension is not secure because it provides the capability to delete files from arbitrary paths.]" + }, + { + "author": "88IO", + "title": "ComfyUI Image Reordering Plugins [REMOVED]", + "reference": "https://github.com/88IO/ComfyUI-ImageReorder", + "files": [ + "https://github.com/88IO/ComfyUI-ImageReorder" + ], + "install_type": "git-clone", + "description": "A custom node reorder multiple image frames based on indexes or curves." + }, + { + "author": "jtydhr88", + "title": "ComfyUI-InstantMesh [DEPRECATED]", + "id": "instant-mesh", + "reference": "https://github.com/jtydhr88/ComfyUI-InstantMesh", + "files": [ + "https://github.com/jtydhr88/ComfyUI-InstantMesh" + ], + "install_type": "git-clone", + "description": "ComfyUI InstantMesh is custom nodes that running TencentARC/InstantMesh into ComfyUI, this extension depends on ComfyUI-3D-Pack. Please refer to Readme carefully to install.\nNOTE: This repo is archived due to ComfyUI-3D-Pack supports InstantMesh, please check 3D-Pack directly if you need it" + }, + { + "author": "biegert", + "title": "CLIPSeg [NOT MAINTAINED]", + "id": "clipseg", + "reference": "https://github.com/biegert/ComfyUI-CLIPSeg", + "files": [ + "https://github.com/biegert/ComfyUI-CLIPSeg/raw/main/custom_nodes/clipseg.py" + ], + "install_type": "copy", + "description": "The CLIPSeg node generates a binary mask for a given input image and text prompt." + }, + { + "author": "tankucc1no", + "title": "ComfyUI-Dragdiffusion [REMOVED]", + "id": "dragdiffusion", + "reference": "https://github.com/tankucc1no/ComfyUI-Dragdiffusion", + "files": [ + "https://github.com/tankucc1no/ComfyUI-Dragdiffusion" + ], + "install_type": "git-clone", + "description": "Implementation of [a/Dragdiffusion](https://github.com/Yujun-Shi/DragDiffusion) in ComfyUI." + }, + { + "author": "wibur0620", + "title": "ComfyUI Ollama (wibur) [REMOVED]", + "id": "ollama-wibur", + "reference": "https://github.com/wibur0620/comfyui-ollama-wibur", + "files": [ + "https://github.com/wibur0620/comfyui-ollama-wibur" + ], + "install_type": "git-clone", + "description": "Custom ComfyUI Nodes for interacting with [a/Ollama](https://ollama.com/) using the ollama python client.\nIntegrate the power of LLMs into ComfyUI workflows easily or just experiment with GPT.\nNOTE: To use this properly, you would need a running Ollama server reachable from the host that is running ComfyUI." + }, + { + "author": "IKHOR", + "title": "ikhor-nodes [REMOVED]", + "reference": "https://github.com/IKHOR/ComfyUI-IKHOR-Jam-Nodes", + "files": [ + "https://github.com/IKHOR/ComfyUI-IKHOR-Jam-Nodes" + ], + "install_type": "git-clone", + "description": "Nodes:LoadFromS3, LoadBatchFromS3, SaveToS3, SaveBatchToS3" + }, + { + "author": "kijai", + "title": "ComfyUI wrapper nodes for IC-light [DEPRECATED]", + "reference": "https://github.com/kijai/ComfyUI-IC-Light-Wrapper", + "files": [ + "https://github.com/kijai/ComfyUI-IC-Light-Wrapper" + ], + "install_type": "git-clone", + "description": "Stopped. Original repo: [a/https://github.com/lllyasviel/IC-Light](https://github.com/lllyasviel/IC-Light)" + }, + { + "author": "thedyze", + "title": "Save Image Extended for ComfyUI", + "reference": "https://github.com/thedyze/save-image-extended-comfyui", + "files": [ + "https://github.com/thedyze/save-image-extended-comfyui" + ], + "install_type": "git-clone", + "description": "Customize the information saved in file- and folder names. Use the values of sampler parameters as part of file or folder names. Save your positive & negative prompt as entries in a JSON (text) file, in each folder.\n[w/This custom node has not been maintained for a long time. Please use an alternative node from the default channel.]" + }, + { + "author": "ExponentialML", + "title": "ComfyUI_ELLA [DEPRECATED]", + "reference": "https://github.com/ExponentialML/ComfyUI_ELLA", + "files": [ + "https://github.com/ExponentialML/ComfyUI_ELLA" + ], + "install_type": "git-clone", + "description": "ComfyUI Implementaion of ELLA: Equip Diffusion Models with LLM for Enhanced Semantic Alignment.[w/Officially implemented here: [a/https://github.com/TencentQQGYLab/ComfyUI-ELLA](https://github.com/TencentQQGYLab/ComfyUI-ELLA)]" + }, + { + "author": "shinich39", + "title": "comfyui-text-pipe-39 [DEPRECATED]", + "reference": "https://github.com/shinich39/comfyui-text-pipe-39", + "files": [ + "https://github.com/shinich39/comfyui-text-pipe-39" + ], + "install_type": "git-clone", + "description": "Modify text by condition." + }, + { + "author": "Big Idea Technology", + "title": "Image Text Overlay Node for ComfyUI [DEPRECATED]", + "reference": "https://github.com/Big-Idea-Technology/ComfyUI_Image_Text_Overlay", + "files": [ + "https://github.com/Big-Idea-Technology/ComfyUI_Image_Text_Overlay" + ], + "install_type": "git-clone", + "description": "Please note that the ImageTextOverlay project is no longer supported and has been moved to a new repository. For ongoing developments, contributions, and issues, please refer to the new repository at: [a/https://github.com/Big-Idea-Technology/ComfyUI-Book-Tools](https://github.com/Big-Idea-Technology/ComfyUI-Book-Tools)" + }, + { + "author": "mlinmg", + "title": "LaMa Preprocessor [DEPRECATED]", + "reference": "https://github.com/mlinmg/ComfyUI-LaMA-Preprocessor", + "files": [ + "https://github.com/mlinmg/ComfyUI-LaMA-Preprocessor" + ], + "install_type": "git-clone", + "description": "A LaMa prerocessor for ComfyUI. This preprocessor finally enable users to generate coherent inpaint and outpaint prompt-free. The best results are given on landscapes, not so much in drawings/animation." + }, + { + "author": "CapsAdmin", + "title": "ComfyUI-Euler-Smea-Dyn-Sampler [DEPRECATED]", + "reference": "https://github.com/CapsAdmin/ComfyUI-Euler-Smea-Dyn-Sampler", + "files": [ + "https://github.com/CapsAdmin/ComfyUI-Euler-Smea-Dyn-Sampler" + ], + "install_type": "git-clone", + "description": "Just a comfyui version of [a/Euler Smea Dyn Sampler](https://github.com/Koishi-Star/Euler-Smea-Dyn-Sampler). It adds the sampler directly to existing samplers." + }, + { + "author": "BlakeOne", + "title": "ComfyUI FastImageListToImageBatch [REMOVED]", + "reference": "https://github.com/BlakeOne/ComfyUI-FastImageListToImageBatch", + "files": [ + "https://github.com/BlakeOne/ComfyUI-FastImageListToImageBatch" + ], + "install_type": "git-clone", + "description": "Quickly convert a list of images to a batch of images. All images must be the same size. Great for long videos." + }, + { + "author": "ggpid", + "title": "idpark_custom_node [REMOVED]", + "reference": "https://github.com/ggpid/idpark_custom_node", + "files": [ + "https://github.com/ggpid/idpark_custom_node" + ], + "install_type": "git-clone", + "description": "Nodes:Load Image from S3, Save Image to S3, Generate SAM, Generate FastSAM, Cut by Mask fixed" + }, + { + "author": "Davemane42", + "title": "Visual Area Conditioning / Latent composition [DEPRECATED]", + "reference": "https://github.com/Davemane42/ComfyUI_Dave_CustomNode", + "files": [ + "https://github.com/Davemane42/ComfyUI_Dave_CustomNode" + ], + "install_type": "git-clone", + "description": "This tool provides custom nodes that allow visualization and configuration of area conditioning and latent composite." + }, + { + "author": "laksjdjf", + "title": "LoRA-Merger-ComfyUI [DEPRECATED]", + "reference": "https://github.com/laksjdjf/LoRA-Merger-ComfyUI", + "files": [ + "https://github.com/laksjdjf/LoRA-Merger-ComfyUI" + ], + "install_type": "git-clone", + "description": "Nodes:Load LoRA Weight Only, Load LoRA from Weight, Merge LoRA, Save LoRA. This extension provides nodes for merging LoRA." + }, + { + "author": "kinfolk0117", + "title": "TiledIPAdapter [DEPRECATED]", + "reference": "https://github.com/kinfolk0117/ComfyUI_TiledIPAdapter", + "files": [ + "https://github.com/kinfolk0117/ComfyUI_TiledIPAdapter" + ], + "install_type": "git-clone", + "description": "Proof of concent on how to use IPAdapter to control tiled upscaling. NOTE: You need to have 'ComfyUI_IPAdapter_plus' installed." + }, + { + "author": "XINZHANG-ops", + "title": "comfyui-xin-nodes [REMOVED]", + "reference": "https://github.com/XINZHANG-ops/comfyui-xin-nodes", + "files": [ + "https://github.com/XINZHANG-ops/comfyui-xin-nodes" + ], + "install_type": "git-clone", + "description": "Nodes:ImageSizeClassifer, RandomInt, ShowValue" + }, + { + "author": "ssitu", + "title": "NestedNodeBuilder [DEPRECATED]", + "reference": "https://github.com/ssitu/ComfyUI_NestedNodeBuilder", + "files": [ + "https://github.com/ssitu/ComfyUI_NestedNodeBuilder" + ], + "install_type": "git-clone", + "description": "This extension provides the ability to combine multiple nodes into a single node.\nNOTE:An identical feature now exists in ComfyUI. Additionally, this extension is largely broken with the recent versions of the codebase, so please use the built-in feature for group nodes." + }, + { + "author": "ccvv804", + "title": "ComfyUI StableCascade using diffusers for Low VRAM [DEPRECATED]", + "reference": "https://github.com/ccvv804/ComfyUI-DiffusersStableCascade-LowVRAM", + "files": [ + "https://github.com/ccvv804/ComfyUI-DiffusersStableCascade-LowVRAM" + ], + "install_type": "git-clone", + "description": "Works with RTX 4070ti 12GB.\nSimple quick wrapper for [a/https://huggingface.co/stabilityai/stable-cascade](https://huggingface.co/stabilityai/stable-cascade)\nComfy is going to implement this properly soon, this repo is just for quick testing for the impatient!" + }, + { + "author": "kijai", + "title": "ComfyUI StableCascade using diffusers [DEPRECATED]", + "reference": "https://github.com/kijai/ComfyUI-DiffusersStableCascade", + "files": [ + "https://github.com/kijai/ComfyUI-DiffusersStableCascade" + ], + "install_type": "git-clone", + "description": "Simple quick wrapper for [a/https://huggingface.co/stabilityai/stable-cascade](https://huggingface.co/stabilityai/stable-cascade)\nComfy is going to implement this properly soon, this repo is just for quick testing for the impatient!" + }, + { + "author": "solarpush", + "title": "comfyui_sendimage_node [REMOVED]", + "reference": "https://github.com/solarpush/comfyui_sendimage_node", + "files": [ + "https://github.com/solarpush/comfyui_sendimage_node" + ], + "install_type": "git-clone", + "description": "Send images to the pod." + }, + { + "author": "azazeal04", + "title": "ComfyUI-Styles", + "reference": "https://github.com/azazeal04/ComfyUI-Styles", + "files": [ + "https://github.com/azazeal04/ComfyUI-Styles" + ], + "install_type": "git-clone", + "description": "Nodes:Anime_Styler, Fantasy_Styler, Gothic_Styler, Line_Art_Styler, Movie_Poster_Styler, Punk_Styler, Travel_Poster_Styler. This extension offers 8 art style nodes, each of which includes approximately 50 individual style variations.\n\nNOTE: Due to the dynamic nature of node name definitions, ComfyUI-Manager cannot recognize the node list from this extension. The Missing nodes and Badge features are not available for this extension.\nNOTE: This extension is removed. Users who were previously using this node should install ComfyUI-styles-all instead." + }, + { + "author": "hnmr293", + "title": "ComfyUI-nodes-hnmr", + "reference": "https://github.com/hnmr293/ComfyUI-nodes-hnmr", + "files": [ + "https://github.com/hnmr293/ComfyUI-nodes-hnmr" + ], + "install_type": "git-clone", + "description": "Provide various custom nodes for Latent, Sampling, Model, Loader, Image, Text" + }, + { + "author": "bvhari", + "title": "ComfyUI_PerpNeg [WIP]", + "reference": "https://github.com/bvhari/ComfyUI_PerpNeg", + "files": [ + "https://github.com/bvhari/ComfyUI_PerpNeg" + ], + "install_type": "git-clone", + "description": "Nodes: KSampler (Advanced + Perp-Neg). Implementation of [a/Perp-Neg](https://perp-neg.github.io/)\nIncludes Tonemap and CFG Rescale optionsComfyUI custom node to convert latent to RGB.[w/WARNING: Experimental code, might have incompatibilities and edge cases.]\nNOTE: In the latest version of ComfyUI, this extension is included as built-in." + }, + { + "author": "laksjdjf", + "title": "IPAdapter-ComfyUI", + "reference": "https://github.com/laksjdjf/IPAdapter-ComfyUI", + "files": [ + "https://github.com/laksjdjf/IPAdapter-ComfyUI" + ], + "install_type": "git-clone", + "description": "This custom nodes provides loader of the IP-Adapter model.[w/NOTE: To use this extension node, you need to download the [a/ip-adapter_sd15.bin](https://huggingface.co/h94/IP-Adapter/resolve/main/models/ip-adapter_sd15.bin) file and place it in the %%**custom_nodes/IPAdapter-ComfyUI/models**%% directory. Additionally, you need to download the 'Clip vision model' from the 'Install models' menu as well.]\nNOTE: Use ComfyUI_IPAdapter_plus instead of this." + }, + { + "author": "RockOfFire", + "title": "CR Animation Nodes", + "reference": "https://github.com/RockOfFire/CR_Animation_Nodes", + "files": [ + "https://github.com/RockOfFire/CR_Animation_Nodes" + ], + "install_type": "git-clone", + "description": "A comprehensive suite of nodes to enhance your animations. These nodes include some features similar to Deforum, and also some new ideas.
    NOTE: This node is merged into Comfyroll Custom Nodes." + }, + { + "author": "tkoenig89", + "title": "Load Image with metadata", + "reference": "https://github.com/tkoenig89/ComfyUI_Load_Image_With_Metadata", + "files": [ + "https://github.com/tkoenig89/ComfyUI_Load_Image_With_Metadata" + ], + "install_type": "git-clone", + "description": "A custom node for comfy ui to read generation data from images (prompt, seed, size...). This could be used when upscaling generated images to use the original prompt and seed." + }, + { + "author": "LucianoCirino", + "title": "Efficiency Nodes for ComfyUI [LEGACY]", + "reference": "https://github.com/LucianoCirino/efficiency-nodes-comfyui", + "files": [ + "https://github.com/LucianoCirino/efficiency-nodes-comfyui" + ], + "install_type": "git-clone", + "description": "A collection of ComfyUI custom nodes to help streamline workflows and reduce total node count.
    NOTE: This repository is the original repository but is no longer maintained. Please use the forked version by jags." + }, + { + "author": "GeLi1989", + "title": "roop nodes for ComfyUI", + "reference": "https://github.com/GeLi1989/GK-beifen-ComfyUI_roop", + "files": [ + "https://github.com/GeLi1989/GK-beifen-ComfyUI_roop" + ], + "install_type": "git-clone", + "description": "ComfyUI nodes for the roop A1111 webui script. NOTE: Need to download model to use this node. NOTE: This is removed." + }, + { + "author": "ProDALOR", + "title": "comfyui_u2net", + "reference": "https://github.com/ProDALOR/comfyui_u2net", + "files": [ + "https://github.com/ProDALOR/comfyui_u2net" + ], + "install_type": "git-clone", + "description": "Nodes: Load U2Net model, U2Net segmentation, To mask, Segmentation to mask, U2NetBaseNormalization, U2NetMaxNormalization. NOTE: This is removed." + }, + { + "author": "FizzleDorf", + "title": "AIT", + "reference": "https://github.com/FizzleDorf/AIT", + "files": [ + "https://github.com/FizzleDorf/AIT" + ], + "install_type": "git-clone", + "description": "Nodes: Load AITemplate, Load AITemplate (ControlNet), VAE Decode (AITemplate), VAE Encode (AITemplate), VAE Encode (AITemplate, Inpaint). Experimental usage of AITemplate. NOTE: This is deprecated extension. Use ComfyUI-AIT instead of this." + }, + { + "author": "chenbaiyujason", + "title": "sc-node-comfyui", + "reference": "https://github.com/chenbaiyujason/sc-node-comfyui", + "files": [ + "https://github.com/chenbaiyujason/sc-node-comfyui" + ], + "install_type": "git-clone", + "description": "Nodes for GPT interaction and text manipulation" + }, + { + "author": "asd417", + "title": "CheckpointTomeLoader", + "reference": "https://github.com/asd417/tomeSD_for_Comfy", + "files": [ + "https://github.com/ltdrdata/ComfyUI-tomeSD-installer" + ], + "install_type": "git-clone", + "description": "tomeSD(https://github.com/dbolya/tomesd) applied to ComfyUI stable diffusion UI using custom node. Note:In vanilla ComfyUI, the TomePatchModel node is provided as a built-in feature." + }, + { + "author": "gamert", + "title": "ComfyUI_tagger", + "reference": "https://github.com/gamert/ComfyUI_tagger", + "pip": ["gradio"], + "files": [ + "https://github.com/gamert/ComfyUI_tagger" + ], + "install_type": "git-clone", + "description": "Nodes: CLIPTextEncodeTaggerDD, ImageTaggerDD.

    WARNING: Installing the current version is causing an issue where ComfyUI fails to start.

    " + }, + { + "author": "Fannovel16", + "title": "ControlNet Preprocessors", + "reference": "https://github.com/Fannovel16/comfy_controlnet_preprocessors", + "files": [ + "https://github.com/Fannovel16/comfy_controlnet_preprocessors" + ], + "install_type": "git-clone", + "description": "ControlNet Preprocessors. (To use this extension, you need to download the required model file from Install Models)

    NOTE: Please uninstall this custom node and instead install 'ComfyUI's ControlNet Auxiliary Preprocessors' from the default channel.
    To use nodes belonging to controlnet v1 such as Canny_Edge_Preprocessor, MIDAS_Depth_Map_Preprocessor, Uniformer_SemSegPreprocessor, etc., you need to copy the config.yaml.example file to config.yaml and change skip_v1: True to skip_v1: False.

    " + }, + { + "author": "comfyanonymous", + "title": "ComfyUI_experiments/sampler_tonemap", + "reference": "https://github.com/comfyanonymous/ComfyUI_experiments", + "files": [ + "https://github.com/comfyanonymous/ComfyUI_experiments/raw/master/sampler_tonemap.py" + ], + "install_type": "copy", + "description": "ModelSamplerTonemapNoiseTest a node that makes the sampler use a simple tonemapping algorithm to tonemap the noise. It will let you use higher CFG without breaking the image. To using higher CFG lower the multiplier value. Similar to Dynamic Thresholding extension of A1111. " + }, + { + "author": "comfyanonymous", + "title": "ComfyUI_experiments/sampler_rescalecfg", + "reference": "https://github.com/comfyanonymous/ComfyUI_experiments", + "files": [ + "https://github.com/comfyanonymous/ComfyUI_experiments/raw/master/sampler_rescalecfg.py" + ], + "install_type": "copy", + "description": "RescaleClassifierFreeGuidance improves the problem of images being degraded by high CFG.To using higher CFG lower the multiplier value. Similar to Dynamic Thresholding extension of A1111. (reference paper)

    It is recommended to use the integrated custom nodes in the default channel for update support rather than installing individual nodes.

    " + }, + { + "author": "comfyanonymous", + "title": "ComfyUI_experiments/advanced_model_merging", + "reference": "https://github.com/comfyanonymous/ComfyUI_experiments", + "files": [ + "https://github.com/comfyanonymous/ComfyUI_experiments/raw/master/advanced_model_merging.py" + ], + "install_type": "copy", + "description": "This provides a detailed model merge feature based on block weight. ModelMergeBlock, in vanilla ComfyUI, allows for adjusting the ratios of input/middle/output layers, but this node provides ratio adjustments for all blocks within each layer.

    It is recommended to use the integrated custom nodes in the default channel for update support rather than installing individual nodes.

    " + }, + { + "author": "comfyanonymous", + "title": "ComfyUI_experiments/sdxl_model_merging", + "reference": "https://github.com/comfyanonymous/ComfyUI_experiments", + "files": [ + "https://github.com/comfyanonymous/ComfyUI_experiments/raw/master/sdxl_model_merging.py" + ], + "install_type": "copy", + "description": "These nodes provide the capability to merge SDXL base models.

    It is recommended to use the integrated custom nodes in the default channel for update support rather than installing individual nodes.

    " + }, + { + "author": "comfyanonymous", + "title": "ComfyUI_experiments/reference_only", + "reference": "https://github.com/comfyanonymous/ComfyUI_experiments", + "files": [ + "https://github.com/comfyanonymous/ComfyUI_experiments/raw/master/reference_only.py" + ], + "install_type": "copy", + "description": "This node provides functionality corresponding to Reference only in Controlnet.

    It is recommended to use the integrated custom nodes in the default channel for update support rather than installing individual nodes.

    " + } + ] +} \ No newline at end of file diff --git a/ComfyUI/custom_nodes/ComfyUI-Manager/node_db/legacy/extension-node-map.json b/ComfyUI/custom_nodes/ComfyUI-Manager/node_db/legacy/extension-node-map.json new file mode 100644 index 0000000000000000000000000000000000000000..9e26dfeeb6e641a33dae4961196235bdb965b21b --- /dev/null +++ b/ComfyUI/custom_nodes/ComfyUI-Manager/node_db/legacy/extension-node-map.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/ComfyUI/custom_nodes/ComfyUI-Manager/node_db/legacy/model-list.json b/ComfyUI/custom_nodes/ComfyUI-Manager/node_db/legacy/model-list.json new file mode 100644 index 0000000000000000000000000000000000000000..1f870f11f31e0ea8162e386a9e61ad0f6ddb64d4 --- /dev/null +++ b/ComfyUI/custom_nodes/ComfyUI-Manager/node_db/legacy/model-list.json @@ -0,0 +1,148 @@ +{ + "models": [ + { + "name": "Inswapper-fp16 (face swap) [REMOVED]", + "type": "insightface", + "base": "inswapper", + "save_path": "insightface", + "description": "Checkpoint of the insightface swapper model\n(used by ComfyUI-FaceSwap, comfyui-reactor-node, CharacterFaceSwap,\nComfyUI roop and comfy_mtb)", + "reference": "https://github.com/facefusion/facefusion-assets", + "filename": "inswapper_128_fp16.onnx", + "url": "https://github.com/facefusion/facefusion-assets/releases/download/models/inswapper_128_fp16.onnx", + "size": "277.7MB" + }, + { + "name": "Inswapper (face swap) [REMOVED]", + "type": "insightface", + "base": "inswapper", + "save_path": "insightface", + "description": "Checkpoint of the insightface swapper model\n(used by ComfyUI-FaceSwap, comfyui-reactor-node, CharacterFaceSwap,\nComfyUI roop and comfy_mtb)", + "reference": "https://github.com/facefusion/facefusion-assets", + "filename": "inswapper_128.onnx", + "url": "https://github.com/facefusion/facefusion-assets/releases/download/models/inswapper_128.onnx", + "size": "555.3MB" + }, + { + "name": "pfg-novel-n10.pt", + "type": "PFG", + "base": "SD1.5", + "save_path": "custom_nodes/pfg-ComfyUI/models", + "description": "Pressing 'install' directly downloads the model from the pfg-ComfyUI/models extension node. (Note: Requires ComfyUI-Manager V0.24 or above)", + "reference": "https://huggingface.co/furusu/PFG", + "filename": "pfg-novel-n10.pt", + "url": "https://huggingface.co/furusu/PFG/resolve/main/pfg-novel-n10.pt", + "size": "23.6MB" + }, + { + "name": "pfg-wd14-n10.pt", + "type": "PFG", + "base": "SD1.5", + "save_path": "custom_nodes/pfg-ComfyUI/models", + "description": "Pressing 'install' directly downloads the model from the pfg-ComfyUI/models extension node. (Note: Requires ComfyUI-Manager V0.24 or above)", + "reference": "https://huggingface.co/furusu/PFG", + "filename": "pfg-wd14-n10.pt", + "url": "https://huggingface.co/furusu/PFG/resolve/main/pfg-wd14-n10.pt", + "size": "31.5MB" + }, + { + "name": "pfg-wd15beta2-n10.pt", + "type": "PFG", + "base": "SD1.5", + "save_path": "custom_nodes/pfg-ComfyUI/models", + "description": "Pressing 'install' directly downloads the model from the pfg-ComfyUI/models extension node. (Note: Requires ComfyUI-Manager V0.24 or above)", + "reference": "https://huggingface.co/furusu/PFG", + "filename": "pfg-wd15beta2-n10.pt", + "url": "https://huggingface.co/furusu/PFG/resolve/main/pfg-wd15beta2-n10.pt", + "size": "31.5MB" + }, + { + "name": "shape_predictor_68_face_landmarks.dat [Face Analysis]", + "type": "Shape Predictor", + "base": "DLIB", + "save_path": "custom_nodes/comfyui_faceanalysis/dlib", + "description": "To use the Face Analysis for ComfyUI custom node, installation of this model is needed.", + "reference": "https://huggingface.co/matt3ounstable/dlib_predictor_recognition/tree/main", + "filename": "shape_predictor_68_face_landmarks.dat", + "url": "https://huggingface.co/matt3ounstable/dlib_predictor_recognition/resolve/main/shape_predictor_68_face_landmarks.dat", + "size": "99.7MB" + }, + { + "name": "dlib_face_recognition_resnet_model_v1.dat [Face Analysis]", + "type": "Face Recognition", + "base": "DLIB", + "save_path": "custom_nodes/comfyui_faceanalysis/dlib", + "description": "To use the Face Analysis for ComfyUI custom node, installation of this model is needed.", + "reference": "https://huggingface.co/matt3ounstable/dlib_predictor_recognition/tree/main", + "filename": "dlib_face_recognition_resnet_model_v1.dat", + "url": "https://huggingface.co/matt3ounstable/dlib_predictor_recognition/resolve/main/dlib_face_recognition_resnet_model_v1.dat", + "size": "22.5MB" + }, + { + "name": "ID-Animator/animator.ckpt", + "type": "ID-Animator", + "base": "SD1.5", + "save_path": "custom_nodes/comfyui_id_animator/models", + "description": "ID-Animator checkpoint", + "reference": "https://huggingface.co/spaces/ID-Animator/ID-Animator", + "filename": "animator.ckpt", + "url": "https://huggingface.co/spaces/ID-Animator/ID-Animator/resolve/main/animator.ckpt", + "size": "247.3MB" + }, + { + "name": "ID-Animator/mm_sd_v15_v2.ckpt", + "type": "ID-Animator", + "base": "SD1.5", + "save_path": "custom_nodes/comfyui_id_animator/models/animatediff_models", + "description": "AnimateDiff checkpoint for ID-Animator", + "reference": "https://huggingface.co/spaces/ID-Animator/ID-Animator", + "filename": "mm_sd_v15_v2.ckpt", + "url": "https://huggingface.co/spaces/ID-Animator/ID-Animator/resolve/main/mm_sd_v15_v2.ckpt", + "size": "1.82GB" + }, + { + "name": "ID-Animator/image_encoder", + "type": "ID-Animator", + "base": "SD1.5", + "save_path": "custom_nodes/comfyui_id_animator/models/image_encoder", + "description": "CLIP Image encoder for ID-Animator", + "reference": "https://huggingface.co/spaces/ID-Animator/ID-Animator", + "filename": "model.safetensors", + "url": "https://huggingface.co/spaces/ID-Animator/ID-Animator/resolve/main/image_encoder/model.safetensors", + "size": "2.53GB" + }, + { + "name": "Doubiiu/ToonCrafter model checkpoint", + "type": "checkpoint", + "base": "ToonCrafter", + "save_path": "custom_nodes/comfyui-tooncrafter/ToonCrafter/checkpoints/tooncrafter_512_interp_v1", + "description": "ToonCrafter checkpoint model for ComfyUI-ToonCrafter", + "reference": "https://huggingface.co/Doubiiu/ToonCrafter/tree/main", + "filename": "model.ckpt", + "url": "https://huggingface.co/Doubiiu/ToonCrafter/resolve/main/model.ckpt", + "size": "10.5GB" + }, + + { + "name": "BAAI/SegGPT", + "type": "SegGPT", + "base": "SegGPT", + "save_path": "custom_nodes/comfyui-seggpt", + "description": "SegGPT", + "reference": "https://huggingface.co/BAAI/SegGPT", + "filename": "seggpt_vit_large.pth", + "url": "https://huggingface.co/BAAI/SegGPT/resolve/main/seggpt_vit_large.pth", + "size": "1.48GB" + }, + { + "name": "kohya-ss/ControlNet-LLLite: SDXL Canny Anime", + "type": "controlnet", + "base": "SDXL", + "save_path": "custom_nodes/ControlNet-LLLite-ComfyUI/models", + "description": "An extremely compactly designed controlnet model (a.k.a. ControlNet-LLLite). Note: The model structure is highly experimental and may be subject to change in the future.", + "reference": "https://huggingface.co/kohya-ss/controlnet-lllite", + "filename": "controllllite_v01032064e_sdxl_canny_anime.safetensors", + "url": "https://huggingface.co/kohya-ss/controlnet-lllite/resolve/main/controllllite_v01032064e_sdxl_canny_anime.safetensors", + "size": "46.2MB" + } + ] +} diff --git a/ComfyUI/custom_nodes/ComfyUI-Manager/node_db/new/alter-list.json b/ComfyUI/custom_nodes/ComfyUI-Manager/node_db/new/alter-list.json new file mode 100644 index 0000000000000000000000000000000000000000..072c3bb5e8bd05b6f14f6df25386dc1e1010a137 --- /dev/null +++ b/ComfyUI/custom_nodes/ComfyUI-Manager/node_db/new/alter-list.json @@ -0,0 +1,4 @@ +{ + "items": [ + ] +} \ No newline at end of file diff --git a/ComfyUI/custom_nodes/ComfyUI-Manager/node_db/new/custom-node-list.json b/ComfyUI/custom_nodes/ComfyUI-Manager/node_db/new/custom-node-list.json new file mode 100644 index 0000000000000000000000000000000000000000..92e79fc70db1d71f1be1451168d05e91d658123a --- /dev/null +++ b/ComfyUI/custom_nodes/ComfyUI-Manager/node_db/new/custom-node-list.json @@ -0,0 +1,696 @@ +{ + "custom_nodes": [ + { + "author": "Simlym", + "title": "ComfyUI Prompt Helper", + "id": "prompt-helper", + "reference": "https://github.com/Simlym/comfyui-prompt-helper", + "files": [ + "https://github.com/Simlym/comfyui-prompt-helper" + ], + "install_type": "git-clone", + "description": "A simple and intuitive prompt management tool for ComfyUI." + }, + { + "author": "What-a-stupid-username", + "title": "comfyui_InversedSampler", + "reference": "https://github.com/What-a-stupid-username/comfyui-InversedSampler", + "files": [ + "https://github.com/What-a-stupid-username/comfyui-InversedSampler" + ], + "install_type": "git-clone", + "description": "A inversed euler sampler to revert image to noisy latent. Can be used to improve content consistency when perform I2I." + }, + { + "author": "woct0rdho", + "title": "ComfyUI-RadialAttn", + "reference": "https://github.com/woct0rdho/ComfyUI-RadialAttn", + "files": [ + "https://github.com/woct0rdho/ComfyUI-RadialAttn" + ], + "install_type": "git-clone", + "description": "RadialAttention in ComfyUI native workflow" + }, + { + "author": "11dogzi", + "title": "HYPIR ComfyUI Plugin", + "reference": "https://github.com/11dogzi/Comfyui-HYPIR", + "files": [ + "https://github.com/11dogzi/Comfyui-HYPIR" + ], + "install_type": "git-clone", + "description": "This is a ComfyUI plugin for [a/HYPIR (Harnessing Diffusion-Yielded Score Priors for Image Restoration)](https://github.com/XPixelGroup/HYPIR), a state-of-the-art image restoration model based on Stable Diffusion 2.1." + }, + { + "author": "jiafuzeng", + "title": "LatentSync", + "reference": "https://github.com/jiafuzeng/comfyui-LatentSync", + "files": [ + "https://github.com/jiafuzeng/comfyui-LatentSync" + ], + "install_type": "git-clone", + "description": "We present LatentSync, an end-to-end lip-sync method based on audio-conditioned latent diffusion models without any intermediate motion representation, diverging from previous diffusion-based lip-sync methods based on pixel-space diffusion or two-stage generation. Our framework can leverage the powerful capabilities of Stable Diffusion to directly model complex audio-visual correlations." + }, + { + "author": "Starnodes2024", + "title": "ComfyUI_StarBetaNodes", + "reference": "https://github.com/Starnodes2024/ComfyUI_StarBetaNodes", + "files": [ + "https://github.com/Starnodes2024/ComfyUI_StarBetaNodes" + ], + "install_type": "git-clone", + "description": "Welcome to Star Beta Nodes - a collection of experimental custom nodes for ComfyUI designed for beta testing and feedback. These nodes provide enhanced functionality for image processing, video handling, and workflow automation." + }, + { + "author": "Franklyc", + "title": "ComfyUI LoRA adaLN Patcher Node", + "reference": "https://github.com/Franklyc/comfyui-lora-adain-patcher-node", + "files": [ + "https://github.com/Franklyc/comfyui-lora-adain-patcher-node" + ], + "install_type": "git-clone", + "description": "A simple but powerful custom node for ComfyUI that patches LoRA models by adding dummy adaLN_modulation_1 weights. This solves compatibility errors when using LoRAs with newer model architectures that expect these keys to be present in the final_layer." + }, + { + "author": "YaserJaradeh", + "title": "Yaser-nodes for ComfyUI", + "reference": "https://github.com/YaserJaradeh/comfyui-yaser-nodes", + "files": [ + "https://github.com/YaserJaradeh/comfyui-yaser-nodes" + ], + "install_type": "git-clone", + "description": "A collection of custom nodes for ComfyUI that provide dynamic input selection and intelligent upscaling functionality." + }, + { + "author": "gvfarns", + "title": "comfyui_gvf", + "reference": "https://github.com/gvfarns/comfyui_gvf", + "files": [ + "https://github.com/gvfarns/comfyui_gvf" + ], + "install_type": "git-clone", + "description": "ComfyUI custom convenience nodes: Cropping images to a given aspect ratio, Cropping images to a max/min aspect ratio, If/else logic with provided float (rather than using a float node)" + }, + { + "author": "mikeshuangyan", + "title": "ComfyUI_MqUtils", + "reference": "https://github.com/mikeshuangyan/ComfyUI_MqUtils", + "files": [ + "https://github.com/mikeshuangyan/ComfyUI_MqUtils" + ], + "install_type": "git-clone", + "description": "MQ util nodes for ComfyUI" + }, + { + "author": "Yuan-ManX", + "title": "ComfyUI-SkyworkUniPic", + "reference": "https://github.com/Yuan-ManX/ComfyUI-SkyworkUniPic", + "files": [ + "https://github.com/Yuan-ManX/ComfyUI-SkyworkUniPic" + ], + "install_type": "git-clone", + "description": "ComfyUI-SkyworkUniPic is now available in ComfyUI, Skywork-UniPic is a unified autoregressive multimodal model with 1.5 billion parameters that natively integrates image understanding, text-to-image generation, and image editing capabilities within a single architecture." + }, + { + "author": "zl9739379", + "title": "ComfyUI-ArkVideoGenerate", + "reference": "https://github.com/zl9739379/ComfyUI-ArkVideoGenerate", + "files": [ + "https://github.com/zl9739379/ComfyUI-ArkVideoGenerate" + ], + "install_type": "git-clone", + "description": "A custom node for ComfyUI that integrates ByteDance Volcano Engine's video generation AI model, supporting both text-to-video and image-to-video generation." + }, + { + "author": "AlfredClark", + "title": "ComfyUI-ModelSpec", + "reference": "https://github.com/AlfredClark/ComfyUI-ModelSpec", + "files": [ + "https://github.com/AlfredClark/ComfyUI-ModelSpec" + ], + "install_type": "git-clone", + "description": "ComfyUI model metadata editing nodes." + }, + { + "author": "rainlizard", + "title": "Whirlpool Upscaler", + "reference": "https://github.com/rainlizard/ComfyUI-WhirlpoolUpscaler", + "files": [ + "https://github.com/rainlizard/ComfyUI-WhirlpoolUpscaler" + ], + "install_type": "git-clone", + "description": "This is a modified implementation of impact-pack's iterative upscaler. It leans in on the idea that giving too much attention to computation at high resolutions isn't a good idea." + }, + { + "author": "Android zhang", + "title": "ComfyUI-MoGe2", + "reference": "https://github.com/zade23/Comfyui-MoGe2", + "files": [ + "https://github.com/zade23/Comfyui-MoGe2" + ], + "install_type": "git-clone", + "description": "Runs the MoGe2 model on the input image. \n v1: Ruicheng/moge-vitl \n v2: Ruicheng/moge-2-vitl-normal" + }, + { + "author": "mcDandy", + "title": "More Math", + "reference": "https://github.com/mcDandy/more_math", + "files": [ + "https://github.com/mcDandy/more_math" + ], + "install_type": "git-clone", + "description": "Adds math nodes for numbers and types which do not need it." + }, + { + "author": "ebrinz", + "title": "ComfyUI-MusicGen-HF", + "reference": "https://github.com/ebrinz/ComfyUI-MusicGen-HF", + "files": [ + "https://github.com/ebrinz/ComfyUI-MusicGen-HF" + ], + "install_type": "git-clone", + "description": "A standalone ComfyUI custom node package for Facebook's MusicGen using Hugging Face Transformers. Generate high-quality music from text prompts with full support for CUDA, MPS (Apple Silicon), and CPU." + }, + { + "author": "joosthel", + "title": "ComfyUI-CVOverlay", + "reference": "https://github.com/joosthel/ComfyUI-CVOverlay", + "files": [ + "https://github.com/joosthel/ComfyUI-CVOverlay" + ], + "install_type": "git-clone", + "description": "TouchDesigner-style blob tracking and computer vision effects for ComfyUI. Simple nodes for bright spot detection, plexus connections, and technical aesthetics in video workflows." + }, + { + "author": "kmlbdh", + "title": "ComfyUI_LocalLLMNodes", + "reference": "https://github.com/kmlbdh/ComfyUI_LocalLLMNodes", + "files": [ + "https://github.com/kmlbdh/ComfyUI_LocalLLMNodes" + ], + "install_type": "git-clone", + "description": "A custom node pack for ComfyUI that allows you to run Large Language Models (LLMs) locally and use them for prompt generation and other text tasks directly within your ComfyUI workflows." + }, + { + "author": "fcanfora", + "title": "comfyui-camera-tools", + "reference": "https://github.com/fcanfora/comfyui-camera-tools", + "files": [ + "https://github.com/fcanfora/comfyui-camera-tools" + ], + "install_type": "git-clone", + "description": "NODES: Load Camera From File, Load 3D, Load 3D - Animation, Preview 3D, Preview 3D - Animation" + }, + { + "author": "lokinou", + "title": "ComfyUI-Offload-Models", + "reference": "https://github.com/lokinou/comfyui-offload-models", + "files": [ + "https://github.com/lokinou/comfyui-offload-models" + ], + "install_type": "git-clone", + "description": "Custom nodes to offload and rapatriate models from cpu." + }, + { + "author": "CallMe1101", + "title": "ComfyUI_OmniAvatar", + "reference": "https://github.com/CallMe1101/ComfyUI_OmniAvatar", + "files": [ + "https://github.com/CallMe1101/ComfyUI_OmniAvatar" + ], + "install_type": "git-clone", + "description": "A ComfyUI custom node developed based on OmniAvatar, capable of generating video sequences with synchronized lip movements and facial expressions by inputting a portrait image, audio, and text prompt. The node parameters and invocation method are fully consistent with the official OmniAvatar inference." + }, + { + "author": "cnnmmd", + "title": "cnnmmd: comfyui_xoxxox_cnnmmd", + "reference": "https://github.com/cnnmmd/comfyui_xoxxox_cnnmmd", + "files": [ + "https://github.com/cnnmmd/comfyui_xoxxox_cnnmmd" + ], + "install_type": "git-clone", + "description": "This is a set of custom nodes for ComfyUI, designed for the following application: [a/https://github.com/cnnmmd/cnnmmd](https://github.com/cnnmmd/cnnmmd)" + }, + { + "author": "ShmuelRonen", + "title": "ComfyUI-HiggsAudio_Wrapper", + "id": "higgs-audio-wrapper", + "reference": "https://github.com/ShmuelRonen/ComfyUI-HiggsAudio_Wrapper", + "files": [ + "https://github.com/ShmuelRonen/ComfyUI-HiggsAudio_Wrapper" + ], + "install_type": "git-clone", + "description": "A comprehensive ComfyUI wrapper for HiggsAudio v2, enabling high-quality text-to-speech generation with advanced voice cloning capabilities. Supports multiple voice presets and custom reference audio for voice cloning. Requires transformers==4.45.2 for compatibility." + }, + { + "author": "RndNanthu", + "title": "ComfyUI-RndNanthu", + "id": "ComfyUI-RndNanthu", + "reference": "https://github.com/rndnanthu/ComfyUI-RndNanthu", + "files": [ + "https://github.com/rndnanthu/ComfyUI-RndNanthu" + ], + "install_type": "git-clone", + "description": "Film Grain simulation, Log Color Conversions, Color Scopes (RGB Parade, Vectorscope, Gamut Warnings), False Color, and more." + }, + { + "author": "keit", + "title": "ComfyUI-musubi-tuner", + "reference": "https://github.com/keit0728/ComfyUI-musubi-tuner", + "files": [ + "https://github.com/keit0728/ComfyUI-musubi-tuner" + ], + "install_type": "git-clone", + "description": "This is a custom node that allows you to run musubi-tuner from ComfyUI." + }, + { + "author": "guill", + "title": "ComfyUI Droopy Noodles", + "reference": "https://github.com/guill/comfyui-droopy-noodles", + "files": [ + "https://github.com/guill/comfyui-droopy-noodles" + ], + "install_type": "git-clone", + "description": "A ComfyUI extension that makes your node connections delightfully droopy. (Disclaimer: despite what it may look like, this extension will not make your monitor taste like spaghetti.)" + }, + { + "author": "AIWarper", + "title": "ComfyUI-DAViD", + "reference": "https://github.com/AIWarper/ComfyUI-DAViD", + "files": [ + "https://github.com/AIWarper/ComfyUI-DAViD" + ], + "install_type": "git-clone", + "description": "An implementation of the DAViD tooling, a method for extracting depth, normals, and masks from an input image." + }, + { + "author": "ComfyUI Studio", + "title": "ComfyUI-Studio-nodes", + "reference": "https://github.com/comfyuistudio/ComfyUI-Studio-nodes", + "files": [ + "https://github.com/comfyuistudio/ComfyUI-Studio-nodes" + ], + "install_type": "git-clone", + "description": "🧩 Aspect Ratio Image Size Calculator, 🖼️ Aspect Ratio Resizer, and 📄 Markdown Link Generator for ComfyUI.", + "tags": ["image", "resize", "aspect-ratio", "markdown", "utils"] + }, + { + "author": "HJH-AILab", + "title": "ComfyUI_Facefusion", + "reference": "https://github.com/HJH-AILab/ComfyUI_Facefusion", + "files": [ + "https://github.com/HJH-AILab/ComfyUI_Facefusion" + ], + "install_type": "git-clone", + "description": "a [a/Facefusion](https://github.com/facefusion/facefusion)'s wrapper for ComfyUI custom node." + }, + { + "author": "visualbruno", + "title": "ComfyUI-Hunyuan3d-2-1", + "reference": "https://github.com/visualbruno/ComfyUI-Hunyuan3d-2-1", + "files": [ + "https://github.com/visualbruno/ComfyUI-Hunyuan3d-2-1" + ], + "install_type": "git-clone", + "description": "ComfyUI Wrapper for [a/Hunyuan3D v2.1](https://github.com/Tencent-Hunyuan/Hunyuan3D-2.1) - From Images to High-Fidelity 3D Assets with Production-Ready PBR Material" + }, + { + "author": "synchronicity-labs", + "title": "ComfyUI Sync Lipsync Node", + "reference": "https://github.com/synchronicity-labs/sync-comfyui", + "files": [ + "https://github.com/synchronicity-labs/sync-comfyui" + ], + "install_type": "git-clone", + "description": "This custom node allows you to perform audio-video lip synchronization inside ComfyUI using a simple interface." + }, + { + "author": "brayevalerien", + "title": "ComfyUI-splitstring", + "reference": "https://github.com/brayevalerien/ComfyUI-SplitString", + "files": [ + "https://github.com/brayevalerien/ComfyUI-SplitString" + ], + "install_type": "git-clone", + "description": "Very specific node for spliting a string with 12 lines into 12 individual strings.k" + }, + { + "author": "jenn", + "title": "BookCoverFinder", + "reference": "https://github.com/weberjc/book-cover-finder-comfy", + "files": [ + "https://github.com/weberjc/book-cover-finder-comfy" + ], + "install_type": "git-clone", + "description": "Book Cover Finder tool that wraps openlibrary.org" + }, + { + "author": "GACLove", + "title": "ComfyUI-VFI", + "reference": "https://github.com/GACLove/ComfyUI-VFI", + "files": [ + "https://github.com/GACLove/ComfyUI-VFI" + ], + "install_type": "git-clone", + "description": "ComfyUI-RIFE is an inference wrapper for RIFE designed for use with ComfyUI." + }, + { + "author": "g0kuvonlange", + "title": "ComfyUI Load From URL", + "reference": "https://github.com/g0kuvonlange/ComfyUI-Load-From-URL", + "files": [ + "https://github.com/g0kuvonlange/ComfyUI-Load-From-URL" + ], + "install_type": "git-clone", + "description": "A simple custom node for ComfyUI to load LoRAs and videos directly from a URL. Ideal for users hosting files on a server with publicly accessible URLs." + }, + { + "author": "concarne000", + "title": "ComfyUI-Stacker", + "reference": "https://github.com/concarne000/ComfyUI-Stacker", + "files": [ + "https://github.com/concarne000/ComfyUI-Stacker" + ], + "install_type": "git-clone", + "description": "Simple stack push/pop style nodes for images, strings, integers and generic objects (image batches, latents, face models etc)" + }, + { + "author": "GeraldWie", + "title": "ComfyUI-I2I-slim", + "reference": "https://github.com/GeraldWie/ComfyUI-I2I-slim", + "files": [ + "https://github.com/GeraldWie/ComfyUI-I2I-slim" + ], + "install_type": "git-clone", + "description": "A lightweight version of the custom nodes originally developed by [a/ManglerFTW](https://github.com/ManglerFTW/ComfyI2I) for performing image-to-image tasks in ComfyUI." + }, + { + "author": "tauraloke", + "title": "ComfyUI-Unfake-Pixels", + "reference": "https://github.com/tauraloke/ComfyUI-Unfake-Pixels", + "files": [ + "https://github.com/tauraloke/ComfyUI-Unfake-Pixels" + ], + "install_type": "git-clone", + "description": "A ComfyUI node for pixel art scaling. Automatically detects the pixel scale using an edge-aware method (Sobel filter + voting on tiles) and downscales the image to that pixel size, reducing color palette." + }, + { + "author": "adrianschubek", + "title": "comfyui-zeug", + "reference": "https://github.com/adrianschubek/comfyui-zeug", + "files": [ + "https://github.com/adrianschubek/comfyui-zeug" + ], + "install_type": "git-clone", + "description": "comfyui-zeug (German for 'gear' or 'stuff') is a collection of custom nodes for ComfyUI, designed to enhance functionality and provide additional features." + }, + { + "author": "smthemex", + "title": "ComfyUI_ObjectClear", + "reference": "https://github.com/smthemex/ComfyUI_ObjectClear", + "files": [ + "https://github.com/smthemex/ComfyUI_ObjectClear" + ], + "install_type": "git-clone", + "description": "ObjectClear:Complete Object Removal via Object-Effect Attention,you can try it in ComfyUI" + }, + { + "author": "Cyrostar", + "title": "ComfyUI-Artha-Gemini", + "id": "comfyui-artha-gemini", + "reference": "https://github.com/Cyrostar/ComfyUI-Artha-Gemini", + "files": [ + "https://github.com/Cyrostar/ComfyUI-Artha-Gemini" + ], + "install_type": "git-clone", + "description": "ComfyUI custom nodes for interacting with the Gemini api for image and video generation prompting." + }, + { + "author": "builmenlabo", + "title": "ComfyUI builmenlabo - Unified Package", + "id": "builmenlabo", + "reference": "https://github.com/comnote-max/builmenlabo", + "files": [ + "https://github.com/comnote-max/builmenlabo" + ], + "install_type": "git-clone", + "description": "Comprehensive collection of ComfyUI custom nodes: 🦙 Advanced LLM text generation with Llama-CPP (CPU/GPU acceleration), 🌐 Smart multi-language prompt translation (Google/DeepL/Yandex/Baidu), 🌍 20-language interface toggle, 📸 AI-powered Gemini pose analysis, 🎛️ Smart ControlNet management. Perfect unified package for AI artists and creators. Blog: https://note.com/hirodream44", + "nodename_pattern": "builmenlabo", + "tags": [ + "LLM", + "translation", + "multilingual", + "pose-analysis", + "controlnet", + "text-generation", + "gemini", + "llama-cpp", + "AI" + ] + }, + { + "author": "o-l-l-i", + "title": "Olm Channel Mixer for ComfyUI", + "reference": "https://github.com/o-l-l-i/ComfyUI-Olm-ChannelMixer", + "files": [ + "https://github.com/o-l-l-i/ComfyUI-Olm-ChannelMixer" + ], + "install_type": "git-clone", + "description": "An interactive, classic channel mixer color adjustment node for ComfyUI, with realtime preview and a responsive editing interface." + }, + { + "author": "cuban044", + "title": "[Unofficial] ComfyUI-Veo3-Experimental", + "reference": "https://github.com/cuban044/ComfyUI-Veo3-Experimental", + "files": [ + "https://github.com/cuban044/ComfyUI-Veo3-Experimental" + ], + "install_type": "git-clone", + "description": "A custom node extension for ComfyUI that integrates Google's Veo 3 text-to-video generation capabilities." + }, + { + "author": "shahkoorosh", + "title": "ComfyUI SeedXPro Translation Node", + "reference": "https://github.com/HM-RunningHub/ComfyUI_RH_SeedXPro", + "files": [ + "https://github.com/HM-RunningHub/ComfyUI_RH_SeedXPro" + ], + "install_type": "git-clone", + "description": "This is a Seed-X-PPO-7B ComfyUI plugin. Easy to use" + }, + { + "author": "cedarconnor", + "title": "ComfyUI Batch Name Loop", + "reference": "https://github.com/cedarconnor/comfyui-BatchNameLoop", + "files": [ + "https://github.com/cedarconnor/comfyui-BatchNameLoop" + ], + "install_type": "git-clone", + "description": "A ComfyUI custom node package for batch image processing with filename preservation." + }, + { + "author": "SamTyurenkov", + "title": "comfyui_vace_preprocessors", + "reference": "https://github.com/SamTyurenkov/comfyui-vace-preprocessors", + "files": [ + "https://github.com/SamTyurenkov/comfyui-vace-preprocessors" + ], + "install_type": "git-clone", + "description": "Some nodes to create a preprocessed videos" + }, + { + "author": "einhorn13", + "title": "ComfyUI-ImageProcessUtilities", + "reference": "https://github.com/einhorn13/ComfyUI-ImageProcessUtilities", + "files": [ + "https://github.com/einhorn13/ComfyUI-ImageProcessUtilities" + ], + "install_type": "git-clone", + "description": "A collection of custom nodes for ComfyUI designed to enhance image processing workflows. Especially useful for high-resolution rendering, complex inpainting, tiling, and batch manipulation. This allows you to perform processing that would otherwise exceed your VRAM limits." + }, + { + "author": "Azornes", + "title": "Comfyui-LayerForge", + "id": "layerforge", + "reference": "https://github.com/Azornes/Comfyui-LayerForge", + "files": [ + "https://github.com/Azornes/Comfyui-LayerForge" + ], + "install_type": "git-clone", + "description": "Photoshop-like layered canvas editor to your ComfyUI workflow. This node is perfect for complex compositing, inpainting, and outpainting, featuring multi-layer support, masking, blend modes, and precise transformations. Includes optional AI-powered background removal for streamlined image editing." + }, + { + "author": "BAIS1C", + "title": "ComfyUI_BASICSAdvancedDancePoser", + "reference": "https://github.com/BAIS1C/ComfyUI_BASICSAdvancedDancePoser", + "files": [ + "https://github.com/BAIS1C/ComfyUI_BASICSAdvancedDancePoser" + ], + "install_type": "git-clone", + "description": "Professional COCO-WholeBody 133-keypoint dance animation system for ComfyUI" + }, + { + "author": "khanhlvg", + "title": "[Unofficial] Vertex AI Custom Nodes for ComfyUI", + "reference": "https://github.com/khanhlvg/vertex-ai-comfyui-nodes", + "files": [ + "https://github.com/khanhlvg/vertex-ai-comfyui-nodes" + ], + "install_type": "git-clone", + "description": "Vertex AI Custom Nodes for ComfyUI" + }, + { + "author": "alexopus", + "title": "ComfyUI Notes Sidebar", + "reference": "https://github.com/alexopus/ComfyUI-Notes-Sidebar", + "files": [ + "https://github.com/alexopus/ComfyUI-Notes-Sidebar" + ], + "install_type": "git-clone", + "description": "A ComfyUI extension that adds a notes sidebar for managing notes" + }, + { + "author": "cedarconnor", + "title": "ComfyUI Upsampler Nodes", + "reference": "https://github.com/cedarconnor/upsampler", + "files": [ + "https://github.com/cedarconnor/upsampler" + ], + "install_type": "git-clone", + "description": "ComfyUI custom nodes for integrating with the Upsampler API to enhance and upscale images using AI." + }, + { + "author": "set-soft", + "title": "Image Misc", + "reference": "https://github.com/set-soft/ComfyUI-ImageMisc", + "files": [ + "https://github.com/set-soft/ComfyUI-ImageMisc" + ], + "install_type": "git-clone", + "description": "Miscellaneous nodes for image manipulation.\nCurrently just download image with bypass, so you can create workflows including image examples.\nNo extra dependencies, just an internal module." + }, + { + "author": "tritant", + "title": "Layers System", + "reference": "https://github.com/tritant/ComfyUI_Layers_Utility", + "files": [ + "https://github.com/tritant/ComfyUI_Layers_Utility" + ], + "install_type": "git-clone", + "description": "This custom node for ComfyUI provides a powerful and flexible dynamic layering system, similar to what you would find in image editing software like Photoshop." + }, + { + "author": "lex-drl", + "title": "String Constructor (Text-Formatting)", + "reference": "https://github.com/Lex-DRL/ComfyUI-StringConstructor", + "files": [ + "https://github.com/Lex-DRL/ComfyUI-StringConstructor" + ], + "install_type": "git-clone", + "description": "Composing prompt variants from the same text pieces with ease:\n• Build your \"library\" (dictionary) of named text chunks (sub-strings) to use it across the entire workflow.\n• Compile these snippets into different prompts in-place - with just one string formatting node.\n• Freely update the dictionary down the line - get different prompts.\n• Reference text chunks within each other to build dependent hierarchies of less/more detailed descriptions.\n• A real life-saver for regional prompting (aka area composition)." + }, + { + "author": "vaishnav-vn", + "title": "va1", + "name": "Pad Image by Aspect", + "reference": "https://github.com/vaishnav-vn/va1", + "files": [ + "https://github.com/vaishnav-vn/va1" + ], + "install_type": "git-clone", + "description": "repo has Custon node designed to expand, pad, and mask images to fixed or randomized aspect ratios with precise spatial and scale control — engineered for outpainting, compositional layout, and creative canvas expansion. " + }, + { + "author": "wawahuy", + "title": "ComfyUI HTTP - REST API Nodes", + "reference": "https://github.com/wawahuy/ComfyUI-HTTP", + "files": [ + "https://github.com/wawahuy/ComfyUI-HTTP" + ], + "install_type": "git-clone", + "description": "Powerful REST API nodes for ComfyUI that enable seamless HTTP/REST integration into your workflows." + }, + { + "author": "cedarconnor", + "title": "ComfyUI LatLong - Equirectangular Image Processing Nodes", + "reference": "https://github.com/cedarconnor/comfyui-LatLong", + "files": [ + "https://github.com/cedarconnor/comfyui-LatLong" + ], + "install_type": "git-clone", + "description": "Advanced equirectangular (360°) image processing nodes for ComfyUI, enabling precise rotation, horizon adjustment, and specialized cropping operations for panoramic images." + }, + { + "author": "BiffMunky", + "title": "Endless 🌊✨ Buttons", + "reference": "https://github.com/tusharbhutt/Endless-Buttons", + "files": [ + "https://github.com/tusharbhutt/Endless-Buttons" + ], + "install_type": "git-clone", + "description": "A small set of JavaScript files I created for myself. The scripts provide Quality of Life enhancements to the ComfyUI interface, such as changing fonts and font sizes." + }, + { + "author": "thalismind", + "title": "ComfyUI LoadImageWithFilename", + "reference": "https://github.com/thalismind/ComfyUI-LoadImageWithFilename", + "files": [ + "https://github.com/thalismind/ComfyUI-LoadImageWithFilename" + ], + "install_type": "git-clone", + "description": "This custom node extends ComfyUI's image loading functionality with filename output and folder loading capabilities." + }, + { + "author": "Edoardo Carmignani", + "title": "ComfyUI-ExtraLinks", + "id": "extralinks", + "reference": "https://github.com/edoardocarmignani/extralinks", + "files": [ + "https://github.com/edoardocarmignani/extralinks" + ], + "install_type": "git-clone", + "description": "A one-click collection of alternate connection styles for ComfyUI." + }, + { + "author": "WASasquatch", + "title": "FUSE Face Enhancer", + "reference": "https://github.com/WASasquatch/face-upscaling-and-seamless-embedding", + "files": [ + "https://github.com/WASasquatch/face-upscaling-and-seamless-embedding" + ], + "install_type": "git-clone", + "description": "All-in-One Face Fix KSampler for ComfyUI with YOLO detection and SAM segmentation" + }, + { + "author": "brucew4yn3rp", + "title": "Save Image with Selective Metadata", + "id": "SaveImageSelectiveMetadata", + "reference": "https://github.com/brucew4yn3rp/ComfyUI_SelectiveMetadata", + "files": [ + "https://github.com/brucew4yn3rp/ComfyUI_SelectiveMetadata" + ], + "install_type": "copy", + "description": "This custom node allows users to selectively choose what to add to the generated image's metadata." + }, + { + "author": "silveroxides", + "title": "ComfyUI_FDGuidance", + "reference": "https://github.com/silveroxides/ComfyUI_FDGuidance", + "files": [ + "https://github.com/silveroxides/ComfyUI_FDGuidance" + ], + "install_type": "git-clone", + "description": "An implementation of Frequency-Decoupled Guidance (FDG) in pure Pytorch." + }, + { + "author": "Edoardo Carmignani", + "title": "VAE Decode Switch for ComfyUI", + "reference": "https://github.com/MasterDenis/VAE-Decode-Switch", + "files": [ + "https://github.com/MasterDenis/VAE-Decode-Switch" + ], + "install_type": "git-clone", + "description": "Node for ComfyUI designed for more neatly switching between tiled and default VAE Decode Nodes." + } + ] +} diff --git a/ComfyUI/custom_nodes/ComfyUI-Manager/node_db/new/extension-node-map.json b/ComfyUI/custom_nodes/ComfyUI-Manager/node_db/new/extension-node-map.json new file mode 100644 index 0000000000000000000000000000000000000000..d653a1827f63cbcb78002b49b99207b42899ae6d --- /dev/null +++ b/ComfyUI/custom_nodes/ComfyUI-Manager/node_db/new/extension-node-map.json @@ -0,0 +1,40854 @@ +{ + "https://gist.githubusercontent.com/alkemann/7361b8eb966f29c8238fd323409efb68/raw/f9605be0b38d38d3e3a2988f89248ff557010076/alkemann.py": [ + [ + "Int to Text", + "Save A1 Image", + "Seed With Text" + ], + { + "title_aux": "alkemann nodes" + } + ], + "https://git.mmaker.moe/mmaker/sd-webui-color-enhance": [ + [ + "MMakerColorBlend", + "MMakerColorEnhance" + ], + { + "title_aux": "mmaker/Color Enhance" + } + ], + "https://gitee.com/yyh915/jkha-load-img": [ + [ + "JkhaLoadImage" + ], + { + "title_aux": "ImageLoadFromLocalOrUrl Node for ComfyUI" + } + ], + "https://github.com/0x-jerry/comfyui-rembg": [ + [ + "Load Rembg Model", + "Rembg Remove background" + ], + { + "title_aux": "0x-jerry/Rembg Background Removal Node for ComfyUI" + } + ], + "https://github.com/0xRavenBlack/ComfyUI-OOP": [ + [ + "OOPAnimalNode", + "OOPClothingNode", + "OOPEnvironmentNode", + "OOPEyesNode", + "OOPHairNode", + "OOPLocationNode", + "OOPMouthNode", + "OOPNode", + "OOPPersonNode", + "OOPPoseNode", + "OOPStyleNode", + "OOPViewNode" + ], + { + "title_aux": "ComfyUI-OOP" + } + ], + "https://github.com/0xbitches/ComfyUI-LCM": [ + [ + "LCM_Sampler", + "LCM_Sampler_Advanced", + "LCM_img2img_Sampler", + "LCM_img2img_Sampler_Advanced" + ], + { + "title_aux": "Latent Consistency Model for ComfyUI" + } + ], + "https://github.com/1038lab/ComfyUI-EdgeTTS": [ + [ + "EdgeTTS", + "Save_Audio", + "WhisperSTT" + ], + { + "title_aux": "ComfyUI-EdgeTTS" + } + ], + "https://github.com/1038lab/ComfyUI-JoyCaption": [ + [ + "CaptionSaver", + "ImageBatchPath", + "JC", + "JC_ExtraOptions", + "JC_adv" + ], + { + "title_aux": "ComfyUI-JoyCaption" + } + ], + "https://github.com/1038lab/ComfyUI-LBM": [ + [ + "LBM_DepthNormal", + "LBM_Relighting" + ], + { + "title_aux": "ComfyUI-LBM" + } + ], + "https://github.com/1038lab/ComfyUI-MegaTTS": [ + [ + "MegaTTS3", + "MegaTTS3S", + "MegaTTS_VoiceMaker" + ], + { + "title_aux": "ComfyUI-MegaTTS" + } + ], + "https://github.com/1038lab/ComfyUI-MiniMax-Remover": [ + [ + "ImageSizeAdjuster", + "MinimaxImageRemover", + "MinimaxModelLoader", + "MinimaxVideoLoader", + "MinimaxVideoRemover" + ], + { + "title_aux": "ComfyUI-MiniMax-Remover" + } + ], + "https://github.com/1038lab/ComfyUI-Mosaic": [ + [ + "MosaicCreator", + "MosaicDetector" + ], + { + "title_aux": "ComfyUI-Mosaic" + } + ], + "https://github.com/1038lab/ComfyUI-OmniGen": [ + [ + "ailab_OmniGen" + ], + { + "title_aux": "ComfyUI-OmniGen" + } + ], + "https://github.com/1038lab/ComfyUI-RMBG": [ + [ + "AILab_ColorInput", + "AILab_CropObject", + "AILab_ICLoRAConcat", + "AILab_ImageCombiner", + "AILab_ImageCompare", + "AILab_ImageCrop", + "AILab_ImageMaskConvert", + "AILab_ImageMaskResize", + "AILab_ImagePreview", + "AILab_ImageStitch", + "AILab_LamaRemover", + "AILab_LoadImage", + "AILab_LoadImageAdvanced", + "AILab_LoadImageSimple", + "AILab_MaskCombiner", + "AILab_MaskEnhancer", + "AILab_MaskExtractor", + "AILab_MaskOverlay", + "AILab_MaskPreview", + "AILab_Preview", + "AILab_ReferenceLatentMask", + "BiRefNetRMBG", + "BodySegment", + "ClothesSegment", + "FaceSegment", + "FashionSegmentAccessories", + "FashionSegmentClothing", + "RMBG", + "Segment", + "SegmentV2" + ], + { + "title_aux": "ComfyUI-RMBG" + } + ], + "https://github.com/1038lab/ComfyUI-ReduxFineTune": [ + [ + "ClipVisionStyleLoader", + "ReduxFineTune", + "ReduxFineTuneAdvanced" + ], + { + "title_aux": "ComfyUI-ReduxFineTune" + } + ], + "https://github.com/1038lab/ComfyUI-SparkTTS": [ + [ + "SparkTTS_AdvVoiceClone", + "SparkTTS_AudioRecorder", + "SparkTTS_VoiceClone", + "SparkTTS_VoiceCreator" + ], + { + "title_aux": "Comfyui-Spark-TTS" + } + ], + "https://github.com/1038lab/ComfyUI-WildPromptor": [ + [ + "AllInOneList", + "KeywordPicker", + "PromptBuilder", + "PromptConcat", + "WildPromptorAllInOne", + "WildPromptorGenerator", + "WildPromptor_DataToPromptList", + "WildPromptor_Enhancer" + ], + { + "title_aux": "ComfyUI-WildPromptor" + } + ], + "https://github.com/111496583yzy/comfyui-PuzzleCrack-Effect": [ + [ + "MyJigsawPuzzleEffect", + "MyRegionBoundaryEffect" + ], + { + "title_aux": "Jigsaw Puzzle Effect Plugin" + } + ], + "https://github.com/11dogzi/CYBERPUNK-STYLE-DIY": [ + [ + "CYBERPUNKHT" + ], + { + "title_aux": "CYBERPUNK-STYLE-DIY" + } + ], + "https://github.com/11dogzi/ComfUI-EGAdapterMadAssistant": [ + [ + "EGIPAdapter_Mad_Assistant", + "EGIPAdapter_Mad_AssistantV1", + "EGIPAdapter_Mad_AssistantV2", + "EGIPAdapter_Mad_AssistantV3", + "EGIPAdapter_Mad_AssistantV4", + "EGIPAdapter_Mad_AssistantV5", + "EGIPAdapter_Mad_AssistantV6" + ], + { + "title_aux": "ComfUI-EGAdapterMadAssistant" + } + ], + "https://github.com/11dogzi/Comfyui-HYPIR": [ + [ + "HYPIRAdvancedRestoration" + ], + { + "title_aux": "HYPIR ComfyUI Plugin" + } + ], + "https://github.com/11dogzi/Comfyui-ergouzi-Nodes": [ + [ + "EG-YSZT-ZT", + "EG_CPSYTJ", + "EG_FX_BDAPI", + "EG_HT_YSTZ", + "EG_JF_ZZSC", + "EG_JXFZ_node", + "EG_K_LATENT", + "EG_RY_HT", + "EG_SCQY_BHDQY", + "EG_SCQY_QBQY", + "EG_SCQY_SXQY", + "EG_SJ", + "EG_SJPJ_Node", + "EG_SS_RYZH", + "EG_SZ_JDYS", + "EG_TC_Node", + "EG_TSCDS_CJ", + "EG_TSCDS_DG", + "EG_TSCDS_FG", + "EG_TSCDS_JT", + "EG_TSCDS_QT", + "EG_TSCDS_RW", + "EG_TSCDS_WP", + "EG_TSCDS_ZL", + "EG_TSCMB_GL", + "EG_TXZZ_ZH", + "EG_TX_CCHQ", + "EG_TX_CJPJ", + "EG_TX_JZRY", + "EG_TX_LJ", + "EG_TX_LJBC", + "EG_TX_SFBLS", + "EG_TX_WHLJ", + "EG_WB_KSH", + "EG_WXZ_QH", + "EG_XZ_QH", + "EG_YSQY_BBLLD", + "EG_YSQY_BLLD", + "EG_ZY_WBK", + "EG_ZZHBCJ", + "EG_ZZKZ_HT_node", + "EG_ZZ_BSYH", + "EG_ZZ_BYYH", + "EG_ZZ_HSYH", + "EG_ZZ_MHHT", + "EG_ZZ_SSKZ", + "ER_JBCH", + "ER_TX_ZZCJ" + ], + { + "title_aux": "Comfyui-ergouzi-Nodes" + } + ], + "https://github.com/11dogzi/Comfyui-ergouzi-kaiguan": [ + [ + "ALLty", + "EGRWGL", + "EGRYDZQHNode", + "EGSEED", + "GroupSwitchNode", + "GroupSwitchNodee", + "GroupSwitchNodeee", + "GroupSwitchNodeeee", + "GroupSwitchNodi", + "hulue", + "jinyong" + ], + { + "title_aux": "Comfyui-ergouzi-kaiguan" + } + ], + "https://github.com/11dogzi/Comfyui-ergouzi-samplers": [ + [ + "EGBYZZCYQ", + "EGCYQJB", + "EGCYQJBCJ" + ], + { + "title_aux": "Comfyui-ergouzi-samplers" + } + ], + "https://github.com/1hew/ComfyUI-1hewNodes": [ + [ + "ImageAddLabel", + "ImageBBoxOverlayByMask", + "ImageBatchToList", + "ImageBlendModesByAlpha", + "ImageBlendModesByCSS", + "ImageCropByMaskAlpha", + "ImageCropSquare", + "ImageCropWithBBoxMask", + "ImageEdgeCropPad", + "ImageEditStitch", + "ImageGetSize", + "ImageHLFreqCombine", + "ImageHLFreqSeparate", + "ImageHLFreqTransform", + "ImageListAppend", + "ImageListToBatch", + "ImageLumaMatte", + "ImagePasteByBBoxMask", + "ImagePlot", + "ImageResizeFluxKontext", + "ImageResizeUniversal", + "ImageSolid", + "ImageTileMerge", + "ImageTileSplit", + "ListCustomFloat", + "ListCustomInt", + "ListCustomSeed", + "ListCustomString", + "MaskBatchMathOps", + "MaskBatchToList", + "MaskCropByBBoxMask", + "MaskFillHole", + "MaskListToBatch", + "MaskMathOps", + "PathBuild", + "RangeMapping", + "StringCoordinateToBBoxMask", + "StringCoordinateToBBoxes", + "TextCustomExtract", + "TextFormat", + "TextJoinMulti", + "TextLoadLocal" + ], + { + "title_aux": "ComfyUI 1hewNodes" + } + ], + "https://github.com/1mckw/Comfyui-Gelbooru": [ + [ + "Gelbooru (ID)", + "Gelbooru (Random)", + "UrlsToImage" + ], + { + "title_aux": "Comfyui-Gelbooru" + } + ], + "https://github.com/1zhangyy1/comfyui-vidu-nodes": [ + [ + "Character2Video", + "Image2Video", + "StartEnd2Video", + "Text2Video", + "UpscaleVideo", + "VideoDownloader" + ], + { + "title_aux": "ComfyUI VIDU" + } + ], + "https://github.com/2frames/ComfyUI-AQnodes": [ + [ + "AQ_BatchAverageImage", + "AQ_BlendImages", + "AQ_CLIPSetLastLayer", + "AQ_ColorMatchImage", + "AQ_Gemini", + "AQ_ImageMaskSwitch", + "AQ_Image_DetailTransfer", + "AQ_Image_Pad", + "AQ_Increment", + "AQ_LoadImageBase64", + "AQ_MasksAndImagesAsList", + "AQ_Qwen", + "AQ_QwenLoader", + "AQ_Random", + "AQ_SaveImageWebpReturnBase64", + "AQ_SendImageToAPI", + "AQ_multiface_ApplyPulidFlux" + ], + { + "title_aux": "AQnodes for ComfyUI" + } + ], + "https://github.com/2kpr/ComfyUI-PMRF": [ + [ + "PMRF" + ], + { + "title_aux": "ComfyUI-PMRF" + } + ], + "https://github.com/2kpr/ComfyUI-UltraPixel": [ + [ + "UltraPixelLoad", + "UltraPixelProcess" + ], + { + "author": "italo", + "title_aux": "ComfyUI-UltraPixel" + } + ], + "https://github.com/311-code/ComfyUI-MagicClip_Strength": [ + [ + "CLIPTextEncodeSDXL_311_code" + ], + { + "title_aux": "ComfyUI MagicClip_Strength for SDXL" + } + ], + "https://github.com/31702160136/ComfyUI-GrsAI": [ + [ + "GPTImage_ImageToImage", + "GPTImage_TextToImage", + "GrsaiFluxKontext_ImageToImage", + "GrsaiFluxKontext_MultiImageToImage", + "GrsaiFluxKontext_TextToImage" + ], + { + "title_aux": "GrsAI api in ComfyUI" + } + ], + "https://github.com/42lux/ComfyUI-42lux": [ + [ + "FluxEmptyLatentSizePicker", + "HighResFixApply", + "HighResFixConditioningDuplicator", + "HighResFixModelInjection", + "ModelSamplingFluxNormalized", + "SoulSampler", + "SoulSamplerAdvanced", + "SoulSamplerDPM", + "SoulSamplerDPMAdvanced", + "SoulSamplerHybrid", + "SoulSamplerHybridAdvanced" + ], + { + "title_aux": "ComfyUI-42lux" + } + ], + "https://github.com/438443467/ComfyUI-GPT4V-Image-Captioner": [ + [ + "GPT4VCaptioner" + ], + { + "title_aux": "ComfyUI-GPT4V-Image-Captioner" + } + ], + "https://github.com/45uee/ComfyUI-Color_Transfer": [ + [ + "ColorPalette", + "ColorTransferReinhard", + "PaletteOptimalTransportTransfer", + "PaletteRbfTransfer", + "PaletteTransfer", + "PaletteTransferReinhard", + "PalleteTransferClustering", + "PalletteSoftTransfer" + ], + { + "title_aux": "ComfyUI-Color_Transfer" + } + ], + "https://github.com/54rt1n/ComfyUI-DareMerge": [ + [ + "DM_AdvancedDareModelMerger", + "DM_AdvancedModelMerger", + "DM_AttentionGradient", + "DM_BlockGradient", + "DM_BlockModelMerger", + "DM_DareClipMerger", + "DM_DareModelMergerBlock", + "DM_DareModelMergerElement", + "DM_DareModelMergerMBW", + "DM_GradientEdit", + "DM_GradientOperations", + "DM_GradientReporting", + "DM_InjectNoise", + "DM_LoRALoaderTags", + "DM_LoRAReporting", + "DM_MBWGradient", + "DM_MagnitudeMasker", + "DM_MaskEdit", + "DM_MaskOperations", + "DM_MaskReporting", + "DM_ModelReporting", + "DM_NormalizeModel", + "DM_QuadMasker", + "DM_ShellGradient", + "DM_SimpleMasker" + ], + { + "title_aux": "ComfyUI-DareMerge" + } + ], + "https://github.com/5x00/ComfyUI-PiAPI-Faceswap": [ + [ + "Face Swapper" + ], + { + "title_aux": "ComfyUI-PiAPI-Faceswap" + } + ], + "https://github.com/5x00/ComfyUI-VLM-Captions": [ + [ + "Image To Caption" + ], + { + "title_aux": "ComfyUI-VLM_Captions" + } + ], + "https://github.com/6174/comflowy-nodes": [ + [ + "Comflowy_Clarity_Upscale", + "Comflowy_Extract_JSON", + "Comflowy_Flux", + "Comflowy_Flux_Dev_Lora", + "Comflowy_Flux_Pro_Ultra", + "Comflowy_Hailuo", + "Comflowy_Http_Request", + "Comflowy_Ideogram", + "Comflowy_Kling", + "Comflowy_LLM", + "Comflowy_Load_JSON", + "Comflowy_Luma", + "Comflowy_Omost_LLM", + "Comflowy_Omost_Load_Canvas_Conditioning", + "Comflowy_Omost_Load_Canvas_Python_Code", + "Comflowy_Omost_Preview", + "Comflowy_Omost_To_Conditioning", + "Comflowy_Preview_JSON", + "Comflowy_Preview_Video", + "Comflowy_Recraft", + "Comflowy_Set_API_Key" + ], + { + "title_aux": "Comflowy's Custom Nodes" + } + ], + "https://github.com/807502278/ComfyUI-3D-MeshTool": [ + [ + "Auto_Normal", + "List_to_Tensor", + "Load_OBJ", + "Mesh_Clean_Data", + "Mesh_Cleanup", + "Mesh_Data_Get", + "Mesh_Data_Set", + "Mesh_Data_Statistics", + "Mesh_Optimization", + "Mesh_Subdivide", + "RT-to-camposes", + "Tensor_to_List", + "UV_options", + "UnwrapUV_Auto_xatlas", + "UnwrapUV_xatlas", + "array-append", + "array-attribute", + "array-convert", + "array-end-increment", + "array-end-step", + "array-is-null", + "array-number-to-angle", + "array-select-element", + "array-step", + "array-step-increment", + "array-t", + "array-to-camposes", + "cam-pos-bus", + "get-tensor-shape", + "img-bath-rotationZ", + "img-to-tensor", + "json-to-campos", + "mesh_data_bus", + "mesh_remap_cubvh", + "ply_load", + "ply_normalize", + "ply_save", + "show_any", + "string-to-array", + "tensor-new", + "tensor-shape", + "tensor-to-img", + "vc_to_texture" + ], + { + "author": "807502278", + "description": "A simple 3D model processing tool within ComfyUI", + "nickname": "3D Mesh Tool", + "title": "3D Mesh Tool", + "title_aux": "ComfyUI-3D-MeshTool" + } + ], + "https://github.com/807502278/ComfyUI-WJNodes": [ + [ + "Accurate_mask_clipping", + "Any_Pipe", + "ApplyEasyOCR_batch", + "AudioDuration_wan", + "Batch_Average", + "Bilateral_Filter", + "ColorData_HSV_Capture", + "Color_Data_Break", + "Color_check_Name", + "ComfyUI_Path_Out", + "Composite_Application_pro", + "Composite_Basic", + "Composite_Batch", + "Composite_Canvas_adv", + "Composite_Coordinate", + "Composite_Global_adv", + "Composite_Mask", + "Composite_Merge_pro", + "Composite_Other", + "Composite_Scale", + "Cutting_video", + "Detecting_videos_mask", + "Determine_Type", + "Folder_Operations_CH", + "Graphics_Detection_Reference", + "ImageCompositeMask_Adv", + "ListMerger", + "Load_Image_Adv", + "Load_Image_From_Path", + "Mask_Detection", + "PrimitiveNode", + "Random_Select_Prompt", + "Robust_Imager_Merge", + "Run_BEN_v2", + "Run_torchvision_model", + "Sam2AutoSegmentation_data", + "SaveMP4", + "SaveMP4_batch", + "Save_Image_Out", + "Save_Image_To_Path", + "SegmDetectorCombined_batch", + "SelectBatch_paragraph", + "Select_Batch_v2", + "Select_Images_Batch", + "Split_Path", + "Str_Append", + "Video_Fade", + "Video_MaskBasedSplit", + "Video_OverlappingSeparation_test", + "WAS_Mask_Fill_Region_batch", + "adv_crop", + "any_math", + "any_math_v2", + "audio_resample", + "audio_scale", + "bbox_restore_mask", + "color_segmentation", + "color_segmentation_v2", + "coords_select_mask", + "crop_by_bboxs", + "crop_data_CoordinateSmooth", + "crop_data_edit", + "filter_DensePose_color", + "get_image_data", + "image_math", + "image_math_value", + "invert_channel_adv", + "load_BEN_model", + "load_ColorName_config", + "load_EasyOCR_model", + "load_color_config", + "load_model_value", + "load_torchvision_model", + "maskCompositeMask_Adv", + "mask_and_mask_math", + "mask_crop_option_Basic", + "mask_crop_option_SmoothCrop", + "mask_crop_square", + "mask_line_mapping", + "mask_select_mask", + "run_yolo_bboxs", + "run_yolo_bboxs_v2", + "sort_images_batch" + ], + { + "title_aux": "ComfyUI-WJNodes" + } + ], + "https://github.com/807502278/ComfyUI_MaskGCT": [ + [ + "audio_capture_percentage", + "audio_resample", + "audio_scale", + "from_path_load_audio", + "get_audio_data", + "get_text_data", + "load_maskgct_model", + "load_w2vbert_model", + "maskgct_pipeline", + "maskgct_run_v2", + "maskgct_setting", + "multilingual_slice", + "remove_blank_space", + "whisper_large_v3" + ], + { + "title_aux": "ComfyUI_MaskGCT" + } + ], + "https://github.com/80sVectorz/ComfyUI-Static-Primitives": [ + [ + "FloatStaticPrimitive", + "IntStaticPrimitive", + "StringMlStaticPrimitive", + "StringStaticPrimitive" + ], + { + "title_aux": "ComfyUI-Static-Primitives" + } + ], + "https://github.com/834t/ComfyUI_834t_scene_composer": [ + [ + "B34tSceneComposerNode" + ], + { + "title_aux": "Scene Composer for ComfyUI" + } + ], + "https://github.com/852wa/ComfyUI-AAP": [ + [ + "AdvancedAlphaProcessor" + ], + { + "title_aux": "ComfyUI-AdvancedAlphaProcessor" + } + ], + "https://github.com/852wa/ComfyUI-ColorshiftColor": [ + [ + "ColorshiftColor", + "CsCFill", + "CsCPaletteEditor" + ], + { + "title_aux": "ComfyUI-ColorshiftColor" + } + ], + "https://github.com/A043-studios/ComfyUI-ASDF-Pixel-Sort-Nodes": [ + [ + "PixelSortAdvancedNode", + "PixelSortNode" + ], + { + "title_aux": "ComfyUI ASDF Pixel Sort Nodes" + } + ], + "https://github.com/A043-studios/Comfyui-ascii-generator": [ + [ + "ASCIIGeneratorNode" + ], + { + "title_aux": "ComfyUI ASCII Generator Node" + } + ], + "https://github.com/A043-studios/comfyui-deforum-x-flux-nodes": [ + [ + "DeforumAnimParamsNode", + "DeforumBaseParamsNode", + "DeforumCacheLatentNode", + "DeforumConditioningBlendNode", + "DeforumDepthWarping", + "DeforumDiffusionParamsNode", + "DeforumFluxSampler", + "DeforumGetCachedLatentNode", + "DeforumInterpolation", + "DeforumIteratorNode", + "DeforumPromptNode", + "DeforumRenderer", + "DeforumTranslationParamsNode", + "DeforumVideoInput", + "DeforumVideoOutput" + ], + { + "title_aux": "ComfyUI Deforum-X-Flux Nodes" + } + ], + "https://github.com/A043-studios/comfyui-pixel3dmm": [ + [ + "FLAMEOptimizer", + "FaceReconstructor3D", + "MeshExporter", + "NormalPredictor", + "Pixel3DMMLoader", + "UVPredictor" + ], + { + "title_aux": "Pixel3DMM ComfyUI Nodes" + } + ], + "https://github.com/A4P7J1N7M05OT/ComfyUI-AutoColorGimp": [ + [ + "AutoColorGimp" + ], + { + "title_aux": "ComfyUI-AutoColorGimp" + } + ], + "https://github.com/A4P7J1N7M05OT/ComfyUI-PixelOE-Wrapper": [ + [ + "PixelOE", + "PixelOETorch" + ], + { + "title_aux": "ComfyUI-PixelOE-Wrapper" + } + ], + "https://github.com/AARG-FAN/Image-Vector-for-ComfyUI": [ + [ + "AIraster" + ], + { + "title_aux": "Image-vector-for-ComfyUI" + } + ], + "https://github.com/AEmotionStudio/ComfyUI-DiscordSend": [ + [ + "DiscordSendSaveImage", + "DiscordSendSaveVideo" + ], + { + "title_aux": "ComfyUI-DiscordSend" + } + ], + "https://github.com/AEmotionStudio/ComfyUI-ShaderNoiseKSampler": [ + [ + "AdvancedImageComparer", + "ShaderNoiseKSampler", + "ShaderNoiseKSamplerDirect", + "Video Comparer", + "VideoComparer" + ], + { + "title_aux": "ComfyUI-ShaderNoiseKSampler" + } + ], + "https://github.com/AIExplorer25/ComfyUI_AutoDownloadModels": [ + [ + "ALIMAMAFUNCONTROLWANModelsAutoDownload", + "AutoDownloadALLModels", + "AutoDownloadModels", + "AutoInstallRequirements_txt", + "GetModelsFromWorkflow", + "SetModelPath", + "ShowModelsAndFolderMappings", + "WANALMAMAModelsAutoDownload", + "WANModelsAutoDownload" + ], + { + "title_aux": "ComfyUI_AutoDownloadModels" + } + ], + "https://github.com/AIExplorer25/ComfyUI_ChatGptHelper": [ + [ + "ChatGptHelper" + ], + { + "title_aux": "ComfyUI_ChatGptHelper" + } + ], + "https://github.com/AIExplorer25/ComfyUI_ImageCaptioner": [ + [ + "CheckImageCaptionsData", + "ImageCaptioner", + "ImageCaptionerPostProcessing", + "Quen3Helper", + "Quen3HelperGGUF" + ], + { + "title_aux": "ComfyUI_ImageCaptioner" + } + ], + "https://github.com/AIFSH/AniTalker-ComfyUI": [ + [ + "AniTalkerNode", + "PreViewVideo" + ], + { + "title_aux": "AniTalker-ComfyUI" + } + ], + "https://github.com/AIFSH/ComfyUI-3d-photo-inpainting": [ + [ + "LoadImagePath", + "PreViewVideo", + "TreeDNode" + ], + { + "title_aux": "ComfyUI-3d-photo-inpainting" + } + ], + "https://github.com/AIFSH/ComfyUI-AuraSR": [ + [ + "AuraSRNode" + ], + { + "title_aux": "AIFSH/ComfyUI-AuraSR" + } + ], + "https://github.com/AIFSH/ComfyUI-DiffSynth-Studio": [ + [ + "ControlNetPathLoader", + "DiffTextNode", + "DiffutoonNode", + "ExVideoNode", + "LoadVideo", + "PreViewVideo", + "SDPathLoader" + ], + { + "title_aux": "ComfyUI-DiffSynth-Studio" + } + ], + "https://github.com/AIFSH/ComfyUI-FishSpeech": [ + [ + "FishSpeech_INFER", + "FishSpeech_INFER_SRT", + "LoadAudio", + "LoadSRT", + "PreViewAudio" + ], + { + "title_aux": "ComfyUI-FishSpeech" + } + ], + "https://github.com/AIFSH/ComfyUI-GPT_SoVITS": [ + [ + "GPT_SOVITS_FT", + "GPT_SOVITS_INFER", + "GPT_SOVITS_TTS", + "LoadAudio", + "LoadSRT", + "PreViewAudio" + ], + { + "title_aux": "ComfyUI-GPT_SoVITS" + } + ], + "https://github.com/AIFSH/ComfyUI-Hallo": [ + [ + "HalloNode", + "LoadAudioPath", + "LoadImagePath", + "PreViewVideo" + ], + { + "title_aux": "ComfyUI-Hallo" + } + ], + "https://github.com/AIFSH/ComfyUI-I2V-Adapter": [ + [ + "I2V_AdapterNode", + "LoraPathLoader", + "MotionLoraLoader", + "PreViewVideo", + "PromptNode" + ], + { + "title_aux": "ComfyUI-I2V-Adapter" + } + ], + "https://github.com/AIFSH/ComfyUI-IP_LAP": [ + [ + "CombineAudioVideo", + "IP_LAP", + "LoadVideo", + "PreViewVideo" + ], + { + "title_aux": "ComfyUI-IP_LAP" + } + ], + "https://github.com/AIFSH/ComfyUI-Live2DViewer": [ + [ + "Live2DViewer", + "LoadAudio" + ], + { + "title_aux": "ComfyUI-Live2DViewer" + } + ], + "https://github.com/AIFSH/ComfyUI-MARS5-TTS": [ + [ + "LoadAudioPath", + "MARS5TTS_Node", + "PreViewAudio", + "TTSTextEncode" + ], + { + "title_aux": "ComfyUI-MARS5-TTS" + } + ], + "https://github.com/AIFSH/ComfyUI-MimicBrush": [ + [ + "MimicBrushNode" + ], + { + "title_aux": "ComfyUI-MimicBrush" + } + ], + "https://github.com/AIFSH/ComfyUI-MimicMotion": [ + [ + "LoadVideo", + "MimicMotionNode", + "PreViewVideo" + ], + { + "title_aux": "ComfyUI-MimicMotion" + } + ], + "https://github.com/AIFSH/ComfyUI-MuseTalk_FSH": [ + [ + "CombineAudioVideo", + "LoadVideo", + "MuseTalk", + "MuseTalkRealTime", + "PreViewVideo" + ], + { + "title_aux": "ComfyUI-MuseTalk_FSH" + } + ], + "https://github.com/AIFSH/ComfyUI-RVC": [ + [ + "CombineAudio", + "LoadAudio", + "PreViewAudio", + "RVC_Infer", + "RVC_Train" + ], + { + "title_aux": "ComfyUI-RVC" + } + ], + "https://github.com/AIFSH/ComfyUI-UVR5": [ + [ + "LoadAudioPath", + "PreViewAudio", + "UVR5_Node" + ], + { + "title_aux": "ComfyUI-UVR5" + } + ], + "https://github.com/AIFSH/ComfyUI-UniAnimate": [ + [ + "LoadImagePath", + "LoadVideo", + "PoseAlignNode", + "PreViewVideo", + "UniAnimateNode" + ], + { + "title_aux": "ComfyUI-UniAnimate" + } + ], + "https://github.com/AIFSH/ComfyUI-WhisperX": [ + [ + "LoadAudioVideoPath", + "PreViewSRT", + "SRTToString", + "WhisperX" + ], + { + "title_aux": "ComfyUI-WhisperX" + } + ], + "https://github.com/AIFSH/ComfyUI-XTTS": [ + [ + "LoadAudioPath", + "LoadSRT", + "PreViewAudio", + "XTTS_INFER", + "XTTS_INFER_SRT" + ], + { + "title_aux": "ComfyUI-XTTS" + } + ], + "https://github.com/AIFSH/ComfyUI_V-Express": [ + [ + "LoadAudioPath", + "LoadImagePath", + "LoadVideo", + "PreViewVideo", + "VExpress" + ], + { + "title_aux": "ComfyUI_V-Express" + } + ], + "https://github.com/AIFSH/CosyVoice-ComfyUI": [ + [ + "CosyVoiceDubbingNode", + "CosyVoiceNode", + "LoadSRT", + "TextNode" + ], + { + "title_aux": "CosyVoice-ComfyUI" + } + ], + "https://github.com/AIFSH/DHLive-ComfyUI": [ + [ + "CombineVideo", + "DHLIVELoadVideo", + "DHLiveNode", + "PreViewVideo", + "StaticVideo" + ], + { + "title_aux": "DHLive-ComfyUI" + } + ], + "https://github.com/AIFSH/DiffMorpher-ComfyUI": [ + [ + "DiffMorpherNode", + "PreViewGIF", + "TextNode" + ], + { + "title_aux": "DiffMorpher-ComfyUI" + } + ], + "https://github.com/AIFSH/DiffSynth-ComfyUI": [ + [ + "CogVideoNode", + "DownloadModelsNode", + "LoadVideo", + "PreViewVideo", + "TextEncode" + ], + { + "title_aux": "DiffSynth-ComfyUI" + } + ], + "https://github.com/AIFSH/EchoMimicV2-ComfyUI": [ + [ + "EchoMimicV2Node", + "EchoMimicV2PoseNode" + ], + { + "title_aux": "EchoMimicV2-ComfyUI" + } + ], + "https://github.com/AIFSH/EzAudio-ComfyUI": [ + [ + "EzAudioControlNetNode", + "EzAudioEditNode", + "EzAudioNode", + "TextPromptNode" + ], + { + "title_aux": "EzAudio-ComfyUI" + } + ], + "https://github.com/AIFSH/F5-TTS-ComfyUI": [ + [ + "F5TTSNode" + ], + { + "title_aux": "F5-TTS-ComfyUI" + } + ], + "https://github.com/AIFSH/FancyVideo-ComfyUI": [ + [ + "FancyVideoI2VNode", + "FancyVideoV2VNode" + ], + { + "title_aux": "FancyVideo-ComfyUI" + } + ], + "https://github.com/AIFSH/FireRedTTS-ComfyUI": [ + [ + "FireRedTTSNode" + ], + { + "title_aux": "FireRedTTS-ComfyUI" + } + ], + "https://github.com/AIFSH/GSTTS-ComfyUI": [ + [ + "ASRNode", + "AudioSlicerNode", + "ConfigGPTNode", + "ConfigSoVITSNode", + "DatasetNode", + "ExperienceNode", + "GSFinetuneNone", + "GSVTTSNode", + "LoadSRT", + "PreViewSRT", + "TSCY_Node", + "TextDictNode" + ], + { + "title_aux": "GSTTS-ComfyUI" + } + ], + "https://github.com/AIFSH/HivisionIDPhotos-ComfyUI": [ + [ + "AddBackgroundNode", + "AddWaterMarkNode", + "ENHivisionParamsNode", + "HivisionLayOutNode", + "HivisionNode", + "LaterProcessNode", + "ZHHivisionParamsNode" + ], + { + "author": "cuny", + "description": "", + "title_aux": "HivisionIDPhotos-ComfyUI" + } + ], + "https://github.com/AIFSH/IMAGDressing-ComfyUI": [ + [ + "IMAGDressingNode", + "TextNode" + ], + { + "title_aux": "IMAGDressing-ComfyUI" + } + ], + "https://github.com/AIFSH/JoyHallo-ComfyUI": [ + [ + "JoyHalloNode" + ], + { + "title_aux": "JoyHallo-ComfyUI" + } + ], + "https://github.com/AIFSH/MaskGCT-ComfyUI": [ + [ + "MaskGCTNode" + ], + { + "title_aux": "MaskGCT-ComfyUI" + } + ], + "https://github.com/AIFSH/MiniMates-ComfyUI": [ + [ + "MiniMatesNode" + ], + { + "title_aux": "MiniMates-ComfyUI" + } + ], + "https://github.com/AIFSH/OmniGen-ComfyUI": [ + [ + "OmniGenLoader", + "OmniGenNode" + ], + { + "title_aux": "OmniGen-ComfyUI" + } + ], + "https://github.com/AIFSH/PyramidFlow-ComfyUI": [ + [ + "PyramidFlowNode" + ], + { + "title_aux": "PyramidFlow-ComfyUI" + } + ], + "https://github.com/AIFSH/RealisDance-ComfyUI": [ + [ + "LoadFile", + "LoadVideo", + "PreViewVideo", + "RealisDanceNode" + ], + { + "title_aux": "RealisDance-ComfyUI" + } + ], + "https://github.com/AIFSH/SenseVoice-ComfyUI": [ + [ + "SenseVoiceNode", + "ShowTextNode" + ], + { + "title_aux": "SenseVoice-ComfyUI" + } + ], + "https://github.com/AIFSH/StyleShot-ComfyUI": [ + [ + "StyleShotNode", + "TextNode" + ], + { + "title_aux": "StyleShot-ComfyUI" + } + ], + "https://github.com/AIFSH/VideoSys-ComfyUI": [ + [ + "PreViewVideo", + "TextNode", + "VideoSysNode" + ], + { + "title_aux": "VideoSys-ComfyUI" + } + ], + "https://github.com/AIFSH/ViewCrafter-ComfyUI": [ + [ + "LoadVideo", + "PreViewVideo", + "ViewCrafterTxTNode" + ], + { + "title_aux": "ViewCrafter-ComfyUI" + } + ], + "https://github.com/AIFSH/VocalSeparation-ComfyUI": [ + [ + "CombineAudioNode", + "VocalSeparationNode" + ], + { + "title_aux": "VocalSeparation-ComfyUI" + } + ], + "https://github.com/AIGCTeam/ComfyUI_kkTranslator_nodes": [ + [ + "LoadMarianMTCheckPoint", + "PromptBaiduFanyiToText", + "PromptTranslateToText" + ], + { + "title_aux": "ComfyUI_kkTranslator_nodes" + } + ], + "https://github.com/AIGODLIKE/ComfyUI-CUP": [ + [ + "Mask", + "OpenPose", + "OpenPoseCanny", + "OpenPoseDepth", + "OpenPoseFace", + "OpenPoseFull", + "OpenPoseFullExtraLimb", + "OpenPoseHand", + "OpenPoseKeyPose", + "OpenPoseLineart", + "OpenPoseMediaPipeFace", + "PreviewAudio", + "SaveAudioBL", + "SaveModel", + "\u5b58\u50a8", + "\u5bfc\u5165", + "\u622a\u56fe", + "\u6750\u8d28\u56fe", + "\u8f93\u5165\u56fe\u50cf", + "\u9884\u89c8" + ], + { + "title_aux": "ComfyUI-CUP" + } + ], + "https://github.com/AIGODLIKE/ComfyUI-ToonCrafter": [ + [ + "ToonCrafterNode", + "ToonCrafterWithSketch" + ], + { + "title_aux": "ComfyUI-ToonCrafter" + } + ], + "https://github.com/AIPOQUE/ComfyUI-APQNodes": [ + [ + "ColorPalette|AIPOQUE" + ], + { + "title_aux": "ComfyUI-APQNodes" + } + ], + "https://github.com/AIToldMeTo/comfyui-cache-cleaner": [ + [ + "CacheCleaner" + ], + { + "title_aux": "ComfyUI Cache Cleaner Node" + } + ], + "https://github.com/AIWarper/ComfyUI-DAViD": [ + [ + "DAViDDepthVisualizer", + "DAViDMultiTask", + "DAViDNormalToLight" + ], + { + "title_aux": "ComfyUI-DAViD" + } + ], + "https://github.com/AIWarper/ComfyUI-NormalCrafterWrapper": [ + [ + "DetailTransfer", + "NormalCrafterNode" + ], + { + "title_aux": "NormalCrafterWrapper" + } + ], + "https://github.com/AIWarper/ComfyUI-WarperNodes": [ + [ + "DWPoseScalerNode_Warper", + "FacialPartMaskFromPose_Warper", + "FlowConfig_Warper", + "FlowGetFlow_Warper", + "FlowVisualizerNode_Warper", + "GetBatchByIndex_Warper", + "GetRaftFlow_Warper", + "MouthMaskFromPose_Warper", + "SmartOverlappingBatcher_Warper", + "SmartVideoBatcher_Warper" + ], + { + "title_aux": "ComfyUI-WarperNodes" + } + ], + "https://github.com/AInseven/ComfyUI-fastblend": [ + [ + "FillDarkMask", + "InterpolateKeyFrame", + "MaskListcaptoBatch", + "Merge_Image_List", + "MyOpenPoseNode", + "SmoothVideo", + "alert_when_finished", + "reBatchImage" + ], + { + "title_aux": "ComfyUI-fastblend" + } + ], + "https://github.com/AIrjen/OneButtonPrompt": [ + [ + "AutoNegativePrompt", + "CreatePromptVariant", + "OneButtonArtify", + "OneButtonFlufferize", + "OneButtonPreset", + "OneButtonPrompt", + "OneButtonSuperPrompt", + "SavePromptToFile" + ], + { + "title_aux": "One Button Prompt" + } + ], + "https://github.com/AJO-reading/ComfyUI-AjoNodes": [ + [ + "AJO_AudioCollectAndConcat", + "AJO_PreviewAudio", + "AJO_SaveAudio", + "AJO_VfiSkipListCalculator" + ], + { + "title_aux": "ComfyUI-AjoNodes" + } + ], + "https://github.com/AKharytonchyk/ComfyUI-telegram-bot-node": [ + [ + "SaveToTelegram", + "TelegramListener", + "author", + "description", + "files", + "install_type", + "keywords", + "license", + "name", + "nodename_pattern", + "pip", + "reference", + "version" + ], + { + "title_aux": "ComfyUI-telegram-bot-node" + } + ], + "https://github.com/ALatentPlace/ComfyUI_yanc": [ + [ + "> Bloom", + "> Blur", + "> Brightness", + "> Clear Text", + "> Combine Channels", + "> Contrast", + "> Divide Channels", + "> Edge Enhance", + "> Film Grain", + "> Float to Int", + "> Fog", + "> Get Mean Color", + "> HUE", + "> Int", + "> Int to Text", + "> Layer Weights (for IPAMS)", + "> Lens Distortion", + "> Light Source Mask", + "> Load Image", + "> Load Image From Folder", + "> Mask Curves", + "> NIKSampler", + "> Noise From Image", + "> Normal Map Lighting", + "> RGB Color", + "> RGB Shift", + "> Resolution by Aspect Ratio", + "> Rotate Image", + "> Saturation", + "> Save Image", + "> Save Text", + "> Scale Image to Side", + "> Scanlines", + "> Sharpen", + "> Text", + "> Text Combine", + "> Text Count", + "> Text Pick Line by Index", + "> Text Pick Random Line", + "> Text Random Weights", + "> Text Replace", + "> Vignette" + ], + { + "title_aux": "ComfyUI_yanc" + } + ], + "https://github.com/ALatentPlace/YANC_LMStudio": [ + [ + "> LMStudio", + "> Select LMS Model" + ], + { + "title_aux": "YANC_LMStudio" + } + ], + "https://github.com/APZmedia/APZmedia-comfy-together-lora": [ + [ + "TogetherImageGenerator", + "TogetherImageGeneratorLoRA" + ], + { + "title_aux": "APZmedia Together Image Generator for ComfyUI" + } + ], + "https://github.com/APZmedia/APZmedia-comfyui-fast-image-save": [ + [ + "APZmedia Fast image save" + ], + { + "title_aux": "APZmedia Fast Image Save Node" + } + ], + "https://github.com/APZmedia/ComfyUI-APZmedia-cleanName-from-string": [ + [ + "APZmediaStandardFilenameBuilder", + "CleanFileNameNode", + "GenerateFilePathNode" + ], + { + "title_aux": "APZmedia Clean Name" + } + ], + "https://github.com/ARZUMATA/ComfyUI-ARZUMATA": [ + [ + "CachingCLIPTextEncodeFlux|ARZUMATA", + "CachingCLIPTextEncode|ARZUMATA", + "ImageCacher", + "ImageLoaderWithPath|ARZUMATA", + "JDC_ImageLoader", + "Sampler Selector|ARZUMATA", + "Scheduler Selector|ARZUMATA" + ], + { + "title_aux": "ComfyUI-ARZUMATA" + } + ], + "https://github.com/ARZUMATA/ComfyUI-ARZUMATA-PixelIt": [ + [ + "PixelIt" + ], + { + "title_aux": "ComfyUI-ARZUMATA-PixelIt" + } + ], + "https://github.com/ARZUMATA/ComfyUI-ARZUMATA-Qwen2": [ + [ + "Image2Base64", + "Qwen2ModelLoader", + "Qwen2ModelRunInference" + ], + { + "title_aux": "ComfyUI-Qwen2" + } + ], + "https://github.com/Aaron-CHM/ComfyUI-z-a1111-sd-webui-DanTagGen": [ + [ + "DanTagGen" + ], + { + "title_aux": "z-a1111-sd-webui-DanTagGen" + } + ], + "https://github.com/AbdullahAlfaraj/Comfy-Photoshop-SD": [ + [ + "APS_LatentBatch", + "APS_Seed", + "ContentMaskLatent", + "ControlNetScript", + "ControlnetUnit", + "GaussianLatentImage", + "GetConfig", + "LoadImageBase64", + "LoadImageWithMetaData", + "LoadLorasFromPrompt", + "MaskExpansion" + ], + { + "title_aux": "Comfy-Photoshop-SD" + } + ], + "https://github.com/AbstractEyes/comfyui-lycoris": [ + [ + "ABS_ConcatPrompts", + "ABS_PromptNode", + "LycorisLoaderNode" + ], + { + "title_aux": "comfyui-lycoris" + } + ], + "https://github.com/AbyssBadger0/ComfyUI_BadgerTools": [ + [ + "ApplyMaskToImage-badger", + "CropImageByMask-badger", + "ExpandImageWithColor-badger", + "FindThickLinesFromCanny-badger", + "Find_closest_factors-badger", + "FloatToInt-badger", + "FloatToString-badger", + "FrameToVideo-badger", + "GETRequset-badger", + "GarbageCollect-badger", + "GetColorFromBorder-badger", + "GetDirName-badger", + "GetUUID-badger", + "IdentifyBorderColorToMask-badger", + "IdentifyColorToMask-badger", + "ImageNormalization-badger", + "ImageOverlap-badger", + "ImageScaleToSide-badger", + "IntToString-badger", + "IntToStringAdvanced-badger", + "LoadImageAdvanced-badger", + "LoadImagesFromDirListAdvanced-badger", + "MapColorsToPalette-badger", + "NormalizationNumber-badger", + "ReduceColors-badger", + "RotateImageWithPadding-badger", + "SegmentToMaskByPoint-badger", + "SimpleBoolean-badger", + "StringToFizz-badger", + "TextListToString-badger", + "ToPixel-badger", + "ToPixelV2-badger", + "TrimTransparentEdges-badger", + "VideoCutFromDir-badger", + "VideoToFrame-badger", + "deleteDir-badger", + "findCenterOfMask-badger", + "getImageSide-badger", + "getParentDir-badger", + "mkdir-badger" + ], + { + "title_aux": "ComfyUI_BadgerTools" + } + ], + "https://github.com/AbyssBadger0/ComfyUI_Kolors_awesome_prompts": [ + [ + "KolorsAwesomePrompts" + ], + { + "title_aux": "Kolors Awesome Prompts" + } + ], + "https://github.com/Acly/comfyui-inpaint-nodes": [ + [ + "INPAINT_ApplyFooocusInpaint", + "INPAINT_DenoiseToCompositingMask", + "INPAINT_ExpandMask", + "INPAINT_InpaintWithModel", + "INPAINT_LoadFooocusInpaint", + "INPAINT_LoadInpaintModel", + "INPAINT_MaskedBlur", + "INPAINT_MaskedFill", + "INPAINT_VAEEncodeInpaintConditioning" + ], + { + "title_aux": "ComfyUI Inpaint Nodes" + } + ], + "https://github.com/Acly/comfyui-tooling-nodes": [ + [ + "ETN_ApplyMaskToImage", + "ETN_ApplyReferenceImages", + "ETN_AttentionMask", + "ETN_BackgroundRegion", + "ETN_CropImage", + "ETN_DefineRegion", + "ETN_ExtractImageTile", + "ETN_ExtractMaskTile", + "ETN_GenerateTileMask", + "ETN_KritaCanvas", + "ETN_KritaImageLayer", + "ETN_KritaMaskLayer", + "ETN_KritaOutput", + "ETN_KritaSelection", + "ETN_KritaSendText", + "ETN_KritaStyle", + "ETN_ListRegionMasks", + "ETN_LoadImageBase64", + "ETN_LoadMaskBase64", + "ETN_MergeImageTile", + "ETN_NSFWFilter", + "ETN_Parameter", + "ETN_ReferenceImage", + "ETN_SendImageWebSocket", + "ETN_TileLayout", + "ETN_Translate" + ], + { + "title_aux": "ComfyUI Nodes for External Tooling" + } + ], + "https://github.com/AconexOfficial/ComfyUI_GOAT_Nodes": [ + [ + "Advanced_Upscale_Image_Using_Model", + "Capped_Float_Positive", + "Capped_Int_Positive", + "Embedding_Selector", + "Fast_Color_Match", + "Fast_Film_Grain", + "Get_Side_Length_Of_Image", + "Image_Crop", + "Image_Dimensions", + "Image_Stitch", + "Image_Tiler", + "Image_Untiler", + "Int_Divide_Rounded", + "Sampler_Settings", + "Smart_Seed", + "Triple_Prompt" + ], + { + "title_aux": "ComfyUI GOAT Nodes" + } + ], + "https://github.com/Aero-Ex/ComfyUI-Vision-LLM-Analyzer": [ + [ + "VisionLLMNode" + ], + { + "title_aux": "ComfyUI Vision LLM Analyzer Node" + } + ], + "https://github.com/Aerse/ComfyUI-Seed-Nodes": [ + [ + "Seed-Nodes: ImagePixelator", + "Seed-Nodes: ImageTo3D", + "Seed-Nodes: ImageTransparencyCrop", + "Seed-Nodes: LoadImage", + "Seed-Nodes: LoadMultipleImages", + "Seed-Nodes: QwenVLAPI", + "Seed-Nodes: SLICPixelator", + "Seed-Nodes: ScreenModeRemoveBlack", + "Seed-Nodes: SeedSaveAudio", + "Seed-Nodes: SiliconFlowVLAPI" + ], + { + "title_aux": "ComfyUI-Seed-Nodes" + } + ], + "https://github.com/AgencyMind/ComfyUI-Satori": [ + [ + "TemporalInvestigator", + "WhyDidItBreak" + ], + { + "title_aux": "ComfyUI-Satori" + } + ], + "https://github.com/AhBumm/ComfyUI_BillBum_APIset_Nodes": [ + [ + "BillBum_Modified_Base64_Url2Data_Node", + "BillBum_Modified_Base64_Url2Img_Node", + "BillBum_Modified_Dalle_API_Node", + "BillBum_Modified_DropoutToken_Node", + "BillBum_Modified_Flux_API_Node", + "BillBum_Modified_Flux_API_with_imgInput", + "BillBum_Modified_GPTImage1_API_Node", + "BillBum_Modified_Ideogram_API_Node", + "BillBum_Modified_ImageSplit_Node", + "BillBum_Modified_Image_API_Call_Node", + "BillBum_Modified_LLM_API_Node", + "BillBum_Modified_LLM_ForceStream_Mode", + "BillBum_Modified_Recraft_API_Node", + "BillBum_Modified_RegText_Node", + "BillBum_Modified_SD3_API_Node", + "BillBum_Modified_Structured_LLM_Node(Imperfect)", + "BillBum_Modified_VisionLM_API_Node", + "BillBum_Modified_img2b64_url_Node", + "BillBum_NonSysPrompt_VLM_API_Node", + "Input_Text", + "Text_Concat" + ], + { + "nodename_pattern": "\\(BillBum\\)$", + "title_aux": "Customizable API Call Nodes by BillBum" + } + ], + "https://github.com/AiMiDi/ComfyUI-Aimidi-nodes": [ + [ + "Add Tag", + "Clear Tag", + "Load Images Pair Batch", + "Merge Tag", + "Move Tag To Top", + "Reserve Tag", + "Save Images Pair" + ], + { + "title_aux": "ComfyUI-Aimidi-nodes" + } + ], + "https://github.com/AkashKarnatak/ComfyUI_faishme": [ + [ + "Faishme Debug", + "Faishme Load Image from Glob", + "Faishme Mannequin to Model Loader", + "Faishme Memory Debug", + "Faishme Moondream", + "Faishme Repeat BBOX", + "Faishme Repeat Image Batch", + "Faishme Repeat Latent Batch", + "Faishme Repeat Tensor Batch", + "Faishme Save Image", + "Faishme Stack Images", + "Faishme Stack Latents", + "Faishme Unstack Images", + "Faishme Unstack Latents", + "Load Fashion Model" + ], + { + "title_aux": "ComfyUI_faishme" + } + ], + "https://github.com/Aksaz/comfyui-seamless-clone": [ + [ + "Seamless Clone" + ], + { + "title_aux": "seamless-clone-comfyui" + } + ], + "https://github.com/Alexankharin/camera-comfyUI": [ + [ + "CameraInterpolationNode", + "CameraMotionNode", + "CameraTrajectoryNode", + "CombineDepthsNode", + "DepthEstimatorNode", + "DepthFramesToVideo", + "DepthRenormalizer", + "DepthToImageNode", + "DepthToPointCloud", + "FisheyeDepthEstimator", + "LoadPointCloud", + "LoadTrajectory", + "OutpaintAnyProjection", + "PointCloudCleaner", + "PointCloudUnion", + "PointcloudTrajectoryEnricher", + "ProjectAndClean", + "ProjectPointCloud", + "ReprojectDepth", + "ReprojectImage", + "SavePointCloud", + "SaveTrajectory", + "TransformPointCloud", + "TransformToMatrix", + "TransformToMatrixManual", + "VideoCameraMotionSequence", + "VideoMetricDepthEstimate", + "ZDepthToRayDepthNode" + ], + { + "title_aux": "camera-comfyUI" + } + ], + "https://github.com/AlfredClark/ComfyUI-ModelSpec": [ + [ + "ModelMetadataNode", + "ModelSpecEditNode", + "SelectModelNode" + ], + { + "title_aux": "ComfyUI-ModelSpec" + } + ], + "https://github.com/Aljnk/ComfyUI-JNK-Tiny-Nodes": [ + [ + "Add Layer Overlay JNK", + "Ask Google Gemini JNK", + "Bridge All JNK", + "Create Folder JNK", + "Create RGBA Image JNK", + "Get All Alpha Layers JNK", + "Get Gemini Keys JNK", + "Get Gemini Models JNK", + "Get Models JNK", + "Get One Alpha Layer JNK", + "Get Substring JNK", + "Get Text From List by Index JNK", + "Get Timestamp JNK", + "Image Filter Loader JNK", + "Join Strings JNK", + "Load Checkpoint Model with Name JNK", + "Load Image if Exist JNK", + "Load LoRA with Name JNK", + "Load UNet Model with Name JNK", + "Prepare Image for AI JNK", + "Queue Stop JNK", + "Save Frame JNK", + "Save Static Image JNK", + "Save Video Images JNK", + "Split String JNK", + "Stroke RGBA Image JNK", + "Switch Index JNK", + "Switch Integer JNK", + "Text Saver JNK", + "Text to Key JNK", + "Text to MD5 JNK", + "Topaz Photo Upscaler (Autopilot) JNK" + ], + { + "title_aux": "ComfyUI-JNK-Tiny-Nodes" + } + ], + "https://github.com/Altair200333/ComfyUI_Flux_1.1_PRO": [ + [ + "FluxGenerate", + "FluxProInpaint", + "FluxProOutpaint" + ], + { + "title_aux": "Flux Pro Nodes for ComfyUI" + } + ], + "https://github.com/Alvaroeai/ComfyUI-Text2Json": [ + [ + "TextToJson" + ], + { + "title_aux": "ComfyUI-Text2Json" + } + ], + "https://github.com/Amorano/Jovi_Capture": [ + [ + "CAMERA (JOV_CAPTURE)", + "MONITOR (JOV_CAPTURE)", + "REMOTE (JOV_CAPTURE)", + "WINDOW (JOV_CAPTURE)" + ], + { + "title_aux": "Jovi_Capture" + } + ], + "https://github.com/Amorano/Jovi_GLSL": [ + [ + "BLEND LINEAR (JOV_GL)", + "BLOOM (JOV_GL)", + "CIRCULAR GRADIENT (JOV_GL)", + "COLOR CONVERSION (JOV_GL)", + "COLOR PALETTE (JOV_GL)", + "CONICAL GRADIENT (JOV_GL)", + "DIRECTIONAL WARP (JOV_GL)", + "FILTER RANGE (JOV_GL)", + "GRAYSCALE (JOV_GL)", + "HSV ADJUST (JOV_GL)", + "INVERT (JOV_GL)", + "LINEAR GRADIENT (JOV_GL)", + "MIN MAX (JOV_GL)", + "NOISE PERLIN (JOV_GL)", + "NOISE SIMPLEX (JOV_GL)", + "NOISE WORLEY (JOV_GL)", + "NORMAL (JOV_GL)", + "NORMAL BLEND (JOV_GL)", + "PIXELATE (JOV_GL)", + "POSTERIZE (JOV_GL)", + "SOBEL (JOV_GL)", + "TRANSFORM (JOV_GL)" + ], + { + "title_aux": "Jovi_GLSL" + } + ], + "https://github.com/Amorano/Jovi_MIDI": [ + [ + "MIDI FILTER (JOV_MIDI)", + "MIDI FILTER EZ (JOV_MIDI)", + "MIDI LOADER (JOV_MIDI)", + "MIDI MESSAGE (JOV_MIDI)", + "MIDI READER (JOV_MIDI)" + ], + { + "title_aux": "Jovi_MIDI" + } + ], + "https://github.com/Amorano/Jovi_Measure": [ + [ + "BLUR EFFECT (JOV_MEASURE)", + "SHANNON ENTROPY (JOV_MEASURE)" + ], + { + "title_aux": "Jovi_Measure" + } + ], + "https://github.com/Amorano/Jovi_Spout": [ + [ + "SPOUT READER (JOV_SPOUT)", + "SPOUT WRITER (JOV_SPOUT)" + ], + { + "title_aux": "Jovi_Spout" + } + ], + "https://github.com/Amorano/Jovimetrix": [ + [ + "ADJUST: BLUR (JOV)", + "ADJUST: COLOR (JOV)", + "ADJUST: EDGE (JOV)", + "ADJUST: EMBOSS (JOV)", + "ADJUST: LEVELS (JOV)", + "ADJUST: LIGHT (JOV)", + "ADJUST: MORPHOLOGY (JOV)", + "ADJUST: PIXEL (JOV)", + "ADJUST: SHARPEN (JOV)", + "AKASHIC (JOV) \ud83d\udcd3", + "ARRAY (JOV) \ud83d\udcda", + "BATCH TO LIST (JOV)", + "BIT SPLIT (JOV) \u2b44", + "BLEND (JOV) \u2697\ufe0f", + "COLOR BLIND (JOV) \ud83d\udc41\u200d\ud83d\udde8", + "COLOR MATCH (JOV) \ud83d\udc9e", + "COLOR MEANS (JOV) \u3030\ufe0f", + "COLOR THEORY (JOV) \ud83d\udede", + "COMPARISON (JOV) \ud83d\udd75\ud83c\udffd", + "CONSTANT (JOV) \ud83d\udfea", + "CROP (JOV) \u2702\ufe0f", + "DELAY (JOV) \u270b\ud83c\udffd", + "EXPORT (JOV) \ud83d\udcfd", + "FILTER MASK (JOV) \ud83e\udd3f", + "FLATTEN (JOV) \u2b07\ufe0f", + "GRADIENT MAP (JOV) \ud83c\uddf2\ud83c\uddfa", + "GRAPH (JOV) \ud83d\udcc8", + "IMAGE INFO (JOV) \ud83d\udcda", + "LERP (JOV) \ud83d\udd30", + "OP BINARY (JOV) \ud83c\udf1f", + "OP UNARY (JOV) \ud83c\udfb2", + "PIXEL MERGE (JOV) \ud83e\udec2", + "PIXEL SPLIT (JOV) \ud83d\udc94", + "PIXEL SWAP (JOV) \ud83d\udd03", + "QUEUE (JOV) \ud83d\uddc3", + "QUEUE TOO (JOV) \ud83d\uddc3", + "ROUTE (JOV) \ud83d\ude8c", + "SAVE OUTPUT (JOV) \ud83d\udcbe", + "SHAPE GEN (JOV) \u2728", + "SPLIT (JOV) \ud83c\udfad", + "STACK (JOV) \u2795", + "STRINGER (JOV) \ud83e\ude80", + "SWIZZLE (JOV) \ud83d\ude35", + "TEXT GEN (JOV) \ud83d\udcdd", + "THRESHOLD (JOV) \ud83d\udcc9", + "TICK (JOV) \u23f1", + "TRANSFORM (JOV) \ud83c\udfdd\ufe0f", + "VALUE (JOV) \ud83e\uddec", + "VECTOR2 (JOV)", + "VECTOR3 (JOV)", + "VECTOR4 (JOV)", + "WAVE GEN (JOV) \ud83c\udf0a" + ], + { + "title_aux": "Jovimetrix" + } + ], + "https://github.com/Andro-Meta/ComfyUI-Ovis2": [ + [ + "Ovis2ImageCaption", + "Ovis2ModelLoader", + "Ovis2MultiImageInput", + "Ovis2VideoFramesDescription" + ], + { + "title_aux": "ComfyUI-Ovis2" + } + ], + "https://github.com/AngelCookies/ComfyUI-Seed-Tracker": [ + [ + "GlobalSeedTracker", + "SeedExporter", + "SeedTracker" + ], + { + "title_aux": "ComfyUI-Seed-Tracker" + } + ], + "https://github.com/Anibaaal/ComfyUI-UX-Nodes": [ + [ + "AdvancedCompositeImageMasked", + "BlockLayerStringGenerator", + "BlurNode", + "ColorGeneratorNode", + "DropShadowNode", + "EasyResolutionPicker", + "LerpNode", + "RemoveJSONMarkdownFormatting" + ], + { + "title_aux": "ComfyUI UX Nodes" + } + ], + "https://github.com/AonekoSS/ComfyUI-LoRA-Tuner": [ + [ + "LoraTuner" + ], + { + "title_aux": "ComfyUI-LoRA-Tuner" + } + ], + "https://github.com/AonekoSS/ComfyUI-SimpleCounter": [ + [ + "Simple Counter" + ], + { + "title_aux": "ComfyUI-SimpleCounter" + } + ], + "https://github.com/ArcherFMY/Diffusion360_ComfyUI": [ + [ + "Diffusion360LoaderImage2Pano", + "Diffusion360LoaderText2Pano", + "Diffusion360Sampler", + "Diffusion360SamplerImage2Pano", + "Diffusion360SamplerText2Pano", + "InputImage", + "InputText", + "VAEDecodeTiledBlended" + ], + { + "title_aux": "Diffusion360_ComfyUI" + } + ], + "https://github.com/ArdeniusAI/ComfyUI-Ardenius": [ + [ + "ARD 4crop Latent", + "ARD 4img Combine", + "ARD 4latent Upscale", + "ARD 4vae Decode", + "ARD Basic Load Image", + "ARD Control Box", + "ARD Counter", + "ARD Dual Prompt", + "ARD Empty Latent Image", + "ARD Float", + "ARD Float To Integer", + "ARD Integer", + "ARD Integer To Float", + "ARD Load Image", + "ARD Math", + "ARD Position", + "ARD Prompt Travel", + "ARD Remainder", + "ARD Resize", + "ARD Save Image", + "ARD Seed", + "ARD Text Box", + "ARD Text Box Counter", + "ARD XY Scripts" + ], + { + "title_aux": "ComfyUI-Ardenius" + } + ], + "https://github.com/Arkanun/ReadCSV_ComfyUI": [ + [ + "ReadCSVRowNode" + ], + { + "title_aux": "ReadCSV_ComfyUI" + } + ], + "https://github.com/ArtBot2023/CharacterFaceSwap": [ + [ + "Color Blend", + "Crop Face", + "Exclude Facial Feature", + "Generation Parameter Input", + "Generation Parameter Output", + "Image Full BBox", + "Load BiseNet", + "Load RetinaFace", + "Mask Contour", + "Segment Face", + "Uncrop Face" + ], + { + "title_aux": "Character Face Swap" + } + ], + "https://github.com/ArtHommage/HommageTools": [ + [ + "HTBaseShiftNode", + "HTConsoleLoggerNode", + "HTConversionNode", + "HTDWPoseConstraintNode", + "HTDetectionBatchProcessor", + "HTDiffusionLoaderMulti", + "HTDimensionAnalyzerNode", + "HTDimensionFormatterNode", + "HTDownsampleNode", + "HTDynamicPromptNode", + "HTDynamicSwitchNode", + "HTFlexibleNode", + "HTGeminiImageNode", + "HTGeminiNode", + "HTImageAdjusterNode", + "HTInspectorNode", + "HTLayerCollectorNode", + "HTLayerExportNode", + "HTLevelsNode", + "HTMaskDilationNode", + "HTMaskValidatorNode", + "HTMoireRemovalNode", + "HTMultiMaskDilationNode", + "HTNodeStateController", + "HTNodeUnmuteAll", + "HTNullNode", + "HTParameterExtractorNode", + "HTPhotoshopBlurNode", + "HTRegexNode", + "HTResizeNode", + "HTResolutionDownsampleNode", + "HTResolutionNode", + "HTSamplerBridgeNode", + "HTSaveImagePlus", + "HTScaleByNode", + "HTSchedulerBridgeNode", + "HTSeedAdvancedNode", + "HTSeedNode", + "HTSplitterNode", + "HTStatusIndicatorNode", + "HTSurfaceBlurNode", + "HTSwitchNode", + "HTTensorInfoNode", + "HTTextCleanupNode", + "HTTrainingSizeNode", + "HTValueMapperNode", + "HTWidgetControlNode" + ], + { + "title_aux": "HommageTools for ComfyUI" + } + ], + "https://github.com/Aryan185/ComfyUI-ExternalAPI-Helpers": [ + [ + "FluxKontextMaxNode", + "FluxKontextProNode", + "GPTImageEditNode", + "GeminiChatNode" + ], + { + "title_aux": "ComfyUI-ExternalAPI-Helpers" + } + ], + "https://github.com/AshMartian/ComfyUI-DirGir": [ + [ + "Dir_Gir_Looper", + "Dir_Gir_Picker", + "Gir_Image_Nabber" + ], + { + "title_aux": "Dir Gir" + } + ], + "https://github.com/AstroCorp/ComfyUI-AstroCorp-Nodes": [ + [ + "InstructNode", + "MergeTextsNode", + "TextareaNode" + ], + { + "title_aux": "ComfyUI AstroCorp Nodes" + } + ], + "https://github.com/AuroBit/ComfyUI-AnimateAnyone-reproduction": [ + [ + "AnimateAnyone" + ], + { + "title_aux": "ComfyUI-AnimateAnyone-reproduction" + } + ], + "https://github.com/AustinMroz/ComfyUI-DynamicOversampling": [ + [ + "DynamicSampler", + "MeasuredSampler", + "ResolveMaskPromise" + ], + { + "title_aux": "DynamicOversampling" + } + ], + "https://github.com/AustinMroz/ComfyUI-SpliceTools": [ + [ + "LogSigmas", + "RerangeSigmas", + "SpliceDenoised", + "SpliceLatents", + "TemporalSplice" + ], + { + "title_aux": "SpliceTools" + } + ], + "https://github.com/Auttasak-L/ComfyUI-ImageCropper": [ + [ + "ImageCropper" + ], + { + "title_aux": "ComfyUI-ImageCropper" + } + ], + "https://github.com/Azornes/Comfyui-LayerForge": [ + [ + "CanvasNode" + ], + { + "title_aux": "Comfyui-LayerForge" + } + ], + "https://github.com/BAIS1C/ComfyUI_BASICSAdvancedDancePoser": [ + [ + "ComfyUI_BASICSAdvancedDancePoser" + ], + { + "title_aux": "ComfyUI_BASICSAdvancedDancePoser" + } + ], + "https://github.com/BAIS1C/ComfyUI_RSS_Feed_Reader": [ + [ + "RSSFeedNode" + ], + { + "title_aux": "ComfyUI_RSS_Feed_Reader" + } + ], + "https://github.com/BIMer-99/ComfyUI_FishSpeech_EX": [ + [ + "AudioToPrompt", + "LoadVQGAN", + "Prompt2Semantic", + "SaveAudioToMp3", + "Semantic2Audio" + ], + { + "title_aux": "ComfyUI_FishSpeech_EX" + } + ], + "https://github.com/BIMer-99/Comfyui_Hunyuan3D_EX": [ + [ + "GenerateSixViews", + "Hunyuan3DNode", + "RemoveBackground", + "SquareImage", + "TriMeshViewer" + ], + { + "title_aux": "Comfyui_Hunyuan3D_EX" + } + ], + "https://github.com/BNP1111/comfyui_flux_corrector": [ + [ + "FLUXCorrector" + ], + { + "title_aux": "comfyui_flux_corrector" + } + ], + "https://github.com/BXYMartin/ComfyUI-InstantIDUtils": [ + [ + "ListOfImages", + "MultiControlNetConverter", + "NHWC2NCHWTensor", + "NHWCTensor2PIL", + "PIL2NHWCTensor" + ], + { + "title_aux": "ComfyUI-InstantIDUtils" + } + ], + "https://github.com/BZcreativ/ComfyUI-FLUX-TOGETHER-API": [ + [ + "FluxDev_TOGETHER", + "FluxPro11_TOGETHER", + "FluxPro_TOGETHER" + ], + { + "title_aux": "ComfyUI-FLUX-TOGETHER-API" + } + ], + "https://github.com/BadCafeCode/masquerade-nodes-comfyui": [ + [ + "Blur", + "Change Channel Count", + "Combine Masks", + "Constant Mask", + "Convert Color Space", + "Create QR Code", + "Create Rect Mask", + "Cut By Mask", + "Get Image Size", + "Image To Mask", + "Make Image Batch", + "Mask By Text", + "Mask Morphology", + "Mask To Region", + "MasqueradeIncrementer", + "Mix Color By Mask", + "Mix Images By Mask", + "Paste By Mask", + "Prune By Mask", + "Separate Mask Components", + "Unary Image Op", + "Unary Mask Op" + ], + { + "title_aux": "Masquerade Nodes" + } + ], + "https://github.com/BahaC/ComfyUI-ZonosTTS": [ + [ + "ZonosTextToSpeech" + ], + { + "title_aux": "ComfyUI Zonos TTS Node" + } + ], + "https://github.com/Beinsezii/bsz-cui-extras": [ + [ + "BSZAbsoluteHires", + "BSZAspectHires", + "BSZColoredLatentImageXL", + "BSZCombinedHires", + "BSZHueChromaXL", + "BSZInjectionKSampler", + "BSZLatentDebug", + "BSZLatentFill", + "BSZLatentGradient", + "BSZLatentHSVAImage", + "BSZLatentOffsetXL", + "BSZLatentRGBAImage", + "BSZLatentbuster", + "BSZPixelbuster", + "BSZPixelbusterHelp", + "BSZPrincipledConditioning", + "BSZPrincipledSampler", + "BSZPrincipledScale", + "BSZStrangeResample" + ], + { + "title_aux": "bsz-cui-extras" + } + ], + "https://github.com/Bellzs/ComfyUI-LoRA-Assistant": [ + [ + "LoRATriggerLocal" + ], + { + "title_aux": "ComfyUI-LoRA-Assistant" + } + ], + "https://github.com/BenNarum/ComfyUI_CAS": [ + [ + "AttentionToSigmas", + "AttenuatorNode", + "BasicCFGGuider", + "CELU", + "CustomAdvancedSampler", + "CustomLCMCFGPP", + "CustomModelSamplingDiscreteDistilledAncestralCFGPP", + "CustomModelSamplingDiscreteDistilledCFGPP", + "CustomX0AncestralCFGPP", + "CustomX0CFGPP", + "ELU", + "EPSCFGPPScheduler", + "GELU", + "GLU", + "HardTanhScheduler", + "Hardshrink", + "Hardsigmoid", + "Hardswish", + "Hardtanh", + "LatentActivation", + "LatentChannelPresets", + "LatentConvolution", + "LatentFFT", + "LatentFrequencyPresets", + "LatentGlitch", + "LatentMath", + "LatentMathFormulaBuilder", + "LatentMosaic", + "LatentNoisePresets", + "LatentPixelSort", + "LatentSelfAttention", + "LatentTwist", + "LatentValuePresets", + "LatentWarpPresets", + "LeakyReLU", + "LogSigmoid", + "LogSoftmax", + "Mish", + "PReLU", + "RReLU", + "ReLU", + "ReLU6", + "SELU", + "SamplerCustomLCMCFGPP", + "SamplerCustomModelSamplingDiscreteDistilledAncestralCFGPP", + "SamplerCustomModelSamplingDiscreteDistilledCFGPP", + "SamplerCustomX0AncestralCFGPP", + "SamplerCustomX0CFGPP", + "SamplerDPMCFGPP", + "SamplerDynamicCFGPP", + "SamplerEulerAttnCFGPP", + "SamplerEulerStepControlAncestralCFGPP", + "SamplerHeunCFGPP", + "SamplerLCMUpscaleW", + "SamplerLCMUpscaleWCFGPP", + "SamplerLCMUpscaleWGPU", + "SamplerStepSizeControlCFGPP", + "SamplerWeightedCFGPP", + "SiLU", + "Softmax", + "Softmax2D", + "SoftmaxScheduler", + "Softmin", + "Softplus", + "Softshrink", + "Softsign", + "Tanh", + "Tanhshrink", + "Threshold", + "tcd_euler_a_w", + "tcd_w" + ], + { + "title_aux": "ComfyUI_CAS" + } + ], + "https://github.com/BenNarum/SigmaWaveFormNode": [ + [ + "AttenuatorNode", + "FourierFilterNode", + "PhaseLockedLoopNode", + "SigmaWaveFormNode", + "SigmaWaveFormNodeAdvanced", + "SigmaWaveFormNodeSimple" + ], + { + "title_aux": "SigmaWaveFormNodes" + } + ], + "https://github.com/BennyKok/comfyui-deploy": [ + [ + "ComfyDeployOutputEXR", + "ComfyDeployOutputImage", + "ComfyDeployOutputText", + "ComfyDeployWebscoketImageInput", + "ComfyDeployWebscoketImageOutput", + "ComfyUIDeployExternalAudio", + "ComfyUIDeployExternalBoolean", + "ComfyUIDeployExternalCheckpoint", + "ComfyUIDeployExternalEXR", + "ComfyUIDeployExternalFaceModel", + "ComfyUIDeployExternalImage", + "ComfyUIDeployExternalImageAlpha", + "ComfyUIDeployExternalImageBatch", + "ComfyUIDeployExternalLora", + "ComfyUIDeployExternalNumber", + "ComfyUIDeployExternalNumberInt", + "ComfyUIDeployExternalNumberSlider", + "ComfyUIDeployExternalNumberSliderInt", + "ComfyUIDeployExternalSeed", + "ComfyUIDeployExternalText", + "ComfyUIDeployExternalTextAny", + "ComfyUIDeployExternalVid", + "ComfyUIDeployExternalVideo", + "ComfyUIDeployStringCombine" + ], + { + "author": "BennyKok", + "description": "", + "nickname": "Comfy Deploy", + "title": "comfyui-deploy", + "title_aux": "ComfyUI Deploy" + } + ], + "https://github.com/BetaDoggo/ComfyUI-Cloud-APIs": [ + [ + "FalAddLora", + "FalAuraFlowAPI", + "FalFluxAPI", + "FalFluxI2IAPI", + "FalFluxLoraAPI", + "FalLLaVAAPI", + "FalSoteDiffusionAPI", + "FalStableCascadeAPI", + "FalVeo2ImagetoVideo", + "FluxResolutionPresets", + "LoadVideoFromURL", + "ReplicateFluxAPI", + "RunWareAPI", + "RunwareAddLora", + "SplitImages" + ], + { + "title_aux": "ComfyUI-Cloud-APIs" + } + ], + "https://github.com/BetaDoggo/ComfyUI-FastSDCPU": [ + [ + "fastsdcpu", + "fastsdcpu_lcm_models", + "fastsdcpu_loadModel", + "fastsdcpu_vino_models" + ], + { + "title_aux": "ComfyUI-FastSDCPU" + } + ], + "https://github.com/BetaDoggo/ComfyUI-Gatcha-Embedding": [ + [ + "GatchaEmbedding" + ], + { + "title_aux": "Gatcha Embeddings" + } + ], + "https://github.com/BetaDoggo/ComfyUI-VideoPlayer": [ + [ + "AllInOnePlayer", + "ImageToEmoji", + "LoadFrame", + "LoadJPGFrame", + "LoadVideoFrame" + ], + { + "title_aux": "ComfyUI Video Player" + } + ], + "https://github.com/BetaDoggo/ComfyUI-WDV-Nodes": [ + [ + "ModelSamplingWaifuDiffusionV" + ], + { + "title_aux": "neggles/ComfyUI-WDV-Nodes [gist-wrapper]" + } + ], + "https://github.com/BetaDoggo/ComfyUI-YetAnotherSafetyChecker": [ + [ + "YetAnotherSafetyChecker" + ], + { + "title_aux": "ComfyUI YetAnotherSafetyChecker" + } + ], + "https://github.com/Big-Idea-Technology/ComfyUI-Book-Tools": [ + [ + "BTDownloadFont", + "BTEndQueue", + "BTImageTextOverlay", + "BTLoop", + "BTLoopEnd", + "BTLoopStart", + "BTPromptSchedule", + "BTPromptSelector", + "BTRandomTextOverlay", + "BTTextGrowth", + "BTTextToImage" + ], + { + "title_aux": "ComfyUI-Book-Tools Nodes for ComfyUI" + } + ], + "https://github.com/Big-Idea-Technology/ComfyUI_LLM_Node": [ + [ + "AdvOptions_Node", + "CodingOptionsNode", + "LLM_Node", + "Output_Node", + "QuantizationConfig_Node" + ], + { + "title_aux": "LLM Node for ComfyUI" + } + ], + "https://github.com/BigStationW/ComfyUi-Load-Image-And-Display-Prompt-Metadata": [ + [ + "LoadImageX" + ], + { + "title_aux": "ComfyUi-Load-Image-And-Display-Prompt-Metadata" + } + ], + "https://github.com/BigStationW/ComfyUi-RescaleCFGAdvanced": [ + [ + "RescaleCFGAdvanced" + ], + { + "title_aux": "ComfyUi-RescaleCFGAdvanced" + } + ], + "https://github.com/BigWhiteFly/ComfyUI-ImageConcat": [ + [ + "ImageConcatenateBatchWithTxt" + ], + { + "title_aux": "ComfyUI-ImageConcat" + } + ], + "https://github.com/Billius-AI/ComfyUI-Path-Helper": [ + [ + "Add File Name Prefix", + "Add File Name Prefix Advanced", + "Add Folder", + "Add Folder Advanced", + "Create Project Root", + "Join Variables", + "Show Path", + "Show String" + ], + { + "title_aux": "ComfyUI-Path-Helper" + } + ], + "https://github.com/Bin-sam/DynamicPose-ComfyUI": [ + [ + "DynamicPose_Sampler", + "Load_Pose_Guider", + "Load_denoising_unet", + "Load_reference_unet", + "Pose_Guider_Encode", + "align", + "load_pose_model", + "pose_extraction" + ], + { + "title_aux": "DynamicPose-ComfyUI" + } + ], + "https://github.com/Black-Lioness/ComfyUI-PromptUtils": [ + [ + "FilenameGenerator", + "KeywordGenerator" + ], + { + "title_aux": "ComfyUI-PromptUtils" + } + ], + "https://github.com/BlackVortexAI/ComfyUI-BVortexNodes": [ + [ + "BV Conditional ImagePipe Splitter", + "BV Image Caption Saver", + "BV Image Difference Heatmap", + "BV Image Size with Math", + "BV ImagePipe Junction", + "BV ImagePipe Loader", + "BV ImagePipe Merger", + "BV Show LoRA Blocks", + "BV String to Combo", + "BV Upscale Config", + "BV Vector Edit", + "BV Vector Edit Dropdown FLUX", + "BV Vector Edit Range", + "BV Vector Edit Range Dropdown FLUX", + "BV Vector Edit Selector FLUX", + "BV Vector Permutation", + "BV Vector of Length-n", + "BV Vector to String", + "BV Vector to String List" + ], + { + "title_aux": "BV Nodes" + } + ], + "https://github.com/BlakeOne/ComfyUI-CustomScheduler": [ + [ + "CustomScheduler" + ], + { + "title_aux": "ComfyUI CustomScheduler" + } + ], + "https://github.com/BlakeOne/ComfyUI-SchedulerMixer": [ + [ + "SchedulerMixer" + ], + { + "title_aux": "ComfyUI SchedulerMixer" + } + ], + "https://github.com/BlenderNeko/ComfyUI_ADV_CLIP_emb": [ + [ + "BNK_AddCLIPSDXLParams", + "BNK_AddCLIPSDXLRParams", + "BNK_CLIPTextEncodeAdvanced", + "BNK_CLIPTextEncodeSDXLAdvanced" + ], + { + "title_aux": "Advanced CLIP Text Encode" + } + ], + "https://github.com/BlenderNeko/ComfyUI_Cutoff": [ + [ + "BNK_CutoffBasePrompt", + "BNK_CutoffRegionsToConditioning", + "BNK_CutoffRegionsToConditioning_ADV", + "BNK_CutoffSetRegions" + ], + { + "title_aux": "ComfyUI Cutoff" + } + ], + "https://github.com/BlenderNeko/ComfyUI_Noise": [ + [ + "BNK_DuplicateBatchIndex", + "BNK_GetSigma", + "BNK_InjectNoise", + "BNK_NoisyLatentImage", + "BNK_SlerpLatent", + "BNK_Unsampler" + ], + { + "title_aux": "ComfyUI Noise" + } + ], + "https://github.com/BlenderNeko/ComfyUI_SeeCoder": [ + [ + "ConcatConditioning", + "SEECoderImageEncode" + ], + { + "title_aux": "SeeCoder [WIP]" + } + ], + "https://github.com/BlenderNeko/ComfyUI_TiledKSampler": [ + [ + "BNK_TiledKSampler", + "BNK_TiledKSamplerAdvanced" + ], + { + "title_aux": "Tiled sampling for ComfyUI" + } + ], + "https://github.com/Blonicx/ComfyUI-X-Rework": [ + [ + "ClearNode", + "LoadImageURL", + "StopNode", + "UploadImage", + "XSampler", + "XSave" + ], + { + "title_aux": "ComfyUI-Rework-X" + } + ], + "https://github.com/BlueprintCoding/ComfyUI_AIDocsClinicalTools": [ + [ + "Multi Float", + "Multi Int", + "Multi Text", + "MultiFloatNodeAID", + "MultiInt", + "MultiText" + ], + { + "title_aux": "The AI Doctors Clinical Tools" + } + ], + "https://github.com/BobRandomNumber/ComfyUI-DiaTTS": [ + [ + "DiaGenerate", + "DiaLoader" + ], + { + "title_aux": "ComfyUI-DiaTTS" + } + ], + "https://github.com/BobRandomNumber/ComfyUI-KyutaiTTS": [ + [ + "KyutaiTTS" + ], + { + "title_aux": "ComfyUI-KyutaiTTS" + } + ], + "https://github.com/BobsBlazed/Bobs-Lora-Loader": [ + [ + "BobsLoraLoaderFlux", + "BobsLoraLoaderSdxl" + ], + { + "title_aux": "Bobs_LoRA_Loader" + } + ], + "https://github.com/BobsBlazed/Bobs_Latent_Optimizer": [ + [ + "BobsLatentNode", + "BobsLatentNodeAdvanced" + ], + { + "title_aux": "Bobs_Latent_Optimizer" + } + ], + "https://github.com/BoyuanJiang/FitDiT-ComfyUI": [ + [ + "FitDiTLoader", + "FitDiTMaskGenerator", + "FitDiTTryOn" + ], + { + "title_aux": "FitDiT[official] - High-fidelity Virtual Try-on" + } + ], + "https://github.com/Bria-AI/ComfyUI-BRIA-API": [ + [ + "BriaEraser", + "BriaGenFill", + "BriaTailoredGen", + "ImageExpansionNode", + "ReimagineNode", + "RemoveForegroundNode", + "ReplaceBgNode", + "RmbgNode", + "ShotByImageNode", + "ShotByTextNode", + "TailoredModelInfoNode", + "TailoredPortraitNode", + "Text2ImageBaseNode", + "Text2ImageFastNode", + "Text2ImageHDNode" + ], + { + "title_aux": "BRIA AI API nodes" + } + ], + "https://github.com/BuffMcBigHuge/ComfyUI-Google-AI-Studio": [ + [ + "GoogleAIStudioImageGen", + "GoogleAIStudioMultiSpeakerTTS", + "GoogleAIStudioTTS", + "GoogleAIStudioTextGen" + ], + { + "title_aux": "ComfyUI-Google-AI-Studio" + } + ], + "https://github.com/BuffMcBigHuge/ComfyUI-Zonos": [ + [ + "ZonosEmotion", + "ZonosGenerate" + ], + { + "title_aux": "ComfyUI-Zonos" + } + ], + "https://github.com/Burgstall-labs/ComfyUI-BETA-Cropnodes": [ + [ + "BETACrop", + "BETAStitch", + "IndexedLoRALoader_BETA", + "LoadTextFromIndex", + "SaveAudioAdvanced_BETA", + "SelectSharpestFrames", + "SharpestFrameClipper", + "TextLineCount", + "WANResolutionCalculator" + ], + { + "title_aux": "ComfyUI-BETA-Cropnodes" + } + ], + "https://github.com/Burgstall-labs/ComfyUI-BETA-Helpernodes": [ + [ + "BETACrop", + "BETAStitch", + "IndexedLoRALoader_BETA", + "LoadTextFromIndex", + "SaveAudioAdvanced_BETA", + "SelectSharpestFrames", + "SharpestFrameClipper", + "TextLineCount", + "WANResolutionCalculator" + ], + { + "title_aux": "ComfyUI-BETA-Helpernodes" + } + ], + "https://github.com/Burgstall-labs/ComfyUI-BS-Textchop": [ + [ + "BSTextChop" + ], + { + "title_aux": "ComfyUI-BS-Textchop" + } + ], + "https://github.com/Burgstall-labs/ComfyUI-BS_Kokoro-onnx": [ + [ + "Kokoro TTS" + ], + { + "title_aux": "ComfyUI-BS_Kokoro-onnx" + } + ], + "https://github.com/CC-BryanOttho/ComfyUI_API_Manager": [ + [ + "APIRequestNode", + "PostImageToAPI", + "TextPromptCombinerNode" + ], + { + "title_aux": "ComfyUI_API_Manager" + } + ], + "https://github.com/CC-SUN6/ccsun_node": [ + [ + "Image Editing", + "Single Image", + "resize to 8", + "several images" + ], + { + "title_aux": "ccsun_node" + } + ], + "https://github.com/CHAOSEA/ComfyUI_FaceAlignPaste": [ + [ + "FaceAlignDouble", + "FaceAlignSingle", + "FaceAutoFitSingle" + ], + { + "title_aux": "ComfyUI_FaceAlignPaste" + } + ], + "https://github.com/CY-CHENYUE/ComfyUI-FramePack-HY": [ + [ + "CreateKeyframes_HY", + "FramePackBucketResize_HY", + "FramePackDiffusersSampler_HY", + "LoadFramePackDiffusersPipeline_HY" + ], + { + "title_aux": "ComfyUI-FramePack-HY" + } + ], + "https://github.com/CY-CHENYUE/ComfyUI-Free-GPU": [ + [ + "FreeGPUMemory" + ], + { + "title_aux": "ComfyUI-Free-GPU" + } + ], + "https://github.com/CY-CHENYUE/ComfyUI-GPT-API": [ + [ + "GPT-ImageGenerator" + ], + { + "title_aux": "ComfyUI-GPT-API" + } + ], + "https://github.com/CY-CHENYUE/ComfyUI-Gemini-API": [ + [ + "Google-Gemini" + ], + { + "title_aux": "ComfyUI-Gemini-API" + } + ], + "https://github.com/CY-CHENYUE/ComfyUI-InpaintEasy": [ + [ + "CropByMask", + "ImageAndMaskResizeNode", + "ImageCropMerge", + "InpaintEasyModel" + ], + { + "title_aux": "ComfyUI-InpaintEasy" + } + ], + "https://github.com/CY-CHENYUE/ComfyUI-Janus-Pro": [ + [ + "JanusImageGeneration", + "JanusImageUnderstanding", + "JanusModelLoader" + ], + { + "title_aux": "ComfyUI-Janus-Pro" + } + ], + "https://github.com/CY-CHENYUE/ComfyUI-MiniCPM-Plus": [ + [ + "MiniCPM3_4B", + "MiniCPM3_4B_GPTQ_Int4", + "MiniCPM_V_2_6", + "MiniCPM_V_2_6_Int4", + "TextDisplay" + ], + { + "author": "CY-CHENYUE", + "description": "Custom nodes for MiniCPM language models in ComfyUI", + "nickname": "MiniCPM-Plus", + "title": "MiniCPM-Plus", + "title_aux": "ComfyUI-MiniCPM-Plus" + } + ], + "https://github.com/CY-CHENYUE/ComfyUI-MiniCPM-o": [ + [ + "Load MiniCPM Model", + "MiniCPM Image Chat", + "MiniCPMImageAnalyzer" + ], + { + "title_aux": "ComfyUI-MiniCPM-o" + } + ], + "https://github.com/CY-CHENYUE/ComfyUI-Molmo": [ + [ + "Molmo7BDbnb" + ], + { + "title_aux": "ComfyUI-Molmo" + } + ], + "https://github.com/CY-CHENYUE/ComfyUI-OmniGenX": [ + [ + "LoadOmniGen" + ], + { + "title_aux": "ComfyUI-OmniGenX" + } + ], + "https://github.com/CY-CHENYUE/ComfyUI-Redux-Prompt": [ + [ + "ReduxPromptStyler" + ], + { + "title_aux": "ComfyUI-Redux-Prompt" + } + ], + "https://github.com/CYBERLOOM-INC/ComfyUI-nodes-hnmr": [ + [ + "CLIPIter", + "Dict2Model", + "GridImage", + "ImageBlend2", + "KSamplerOverrided", + "KSamplerSetting", + "KSamplerXYZ", + "LatentToHist", + "LatentToImage", + "ModelIter", + "RandomLatentImage", + "SaveStateDict", + "SaveText", + "StateDictLoader", + "StateDictMerger", + "StateDictMergerBlockWeighted", + "StateDictMergerBlockWeightedMulti", + "VAEDecodeBatched", + "VAEEncodeBatched", + "VAEIter" + ], + { + "title_aux": "ComfyUI-nodes-hnmr" + } + ], + "https://github.com/CallMe1101/ComfyUI_OmniAvatar": [ + [ + "OmniAvatar All-in-One (14B)" + ], + { + "title_aux": "ComfyUI_OmniAvatar" + } + ], + "https://github.com/Chan-0312/ComfyUI-EasyDeforum": [ + [ + "Easy2DDeforum" + ], + { + "title_aux": "ComfyUI-EasyDeforum" + } + ], + "https://github.com/Chan-0312/ComfyUI-IPAnimate": [ + [ + "IPAdapterAnimate" + ], + { + "title_aux": "ComfyUI-IPAnimate" + } + ], + "https://github.com/Chan-0312/ComfyUI-Prompt-Preview": [ + [ + "SDXLPromptStylerAdvancedPreview", + "SDXLPromptStylerPreview" + ], + { + "title_aux": "ComfyUI-Prompt-Preview" + } + ], + "https://github.com/Chaoses-Ib/ComfyUI_Ib_CustomNodes": [ + [ + "ImageToPIL", + "LoadImageFromPath", + "PILToImage", + "PILToMask" + ], + { + "title_aux": "ComfyUI_Ib_CustomNodes" + } + ], + "https://github.com/Charlweed/image_transceiver": [ + [ + "ImageTransceiver" + ], + { + "title_aux": "ImageTransceiver - ComfyUI" + } + ], + "https://github.com/Charonartist/Comfyui_gemini_tts_node": [ + [ + "GeminiTTSFinal" + ], + { + "title_aux": "Comfyui_gemini_tts_node" + } + ], + "https://github.com/Charonartist/comfyui-auto-lora-v2": [ + [ + "AutoLoRANode", + "LoRABrowserNode", + "LoRAManagerNode" + ], + { + "title_aux": "ComfyUI Auto LoRA" + } + ], + "https://github.com/CheNing233/ComfyUI_Image_Pin": [ + [ + "ImagePin" + ], + { + "title_aux": "ComfyUI_Image_Pin" + } + ], + "https://github.com/ChenDarYen/ComfyUI-NAG": [ + [ + "KSamplerWithNAG", + "KSamplerWithNAG (Advanced)", + "NAGCFGGuider", + "NAGGuider", + "SamplerCustomWithNAG" + ], + { + "title_aux": "ComfyUI-NAG" + } + ], + "https://github.com/ChenDarYen/ComfyUI-TimestepShiftModel": [ + [ + "Timestep Shift Model" + ], + { + "title_aux": "ComfyUI-TimestepShiftModel" + } + ], + "https://github.com/Chengym2023/ComfyUI-DeepSeek_Online": [ + [ + "DeepSeekOnline", + "SiliconCloud" + ], + { + "title_aux": "ComfyUI-DeepSeek_Online" + } + ], + "https://github.com/ChrisColeTech/ComfyUI-Elegant-Resource-Monitor": [ + [ + "Resource Monitor" + ], + { + "title_aux": "ComfyUI-Elegant-Resource-Monitor" + } + ], + "https://github.com/ChrisColeTech/ComfyUI-Line-counter": [ + [ + "Directory File Counter", + "Simple Number Counter", + "Text File Line Counter", + "Text File Line Reader" + ], + { + "title_aux": "ComfyUI-Line-counter" + } + ], + "https://github.com/Chrisvenator/ComfyUI-Painting-by-colors-generator": [ + [ + "EnhancedPaintByNumbersNode", + "HexStackNode", + "NumbersOverlayAdvancedNode", + "NumbersOverlayNode", + "PaintByNumbersNode", + "PaintByNumbersTemplateNode" + ], + { + "title_aux": "painting-by-colors-generator" + } + ], + "https://github.com/ClownsharkBatwing/RES4LYF": [ + [ + "AdvancedNoise", + "Base64ToConditioning", + "CLIPTextEncodeFluxUnguided", + "ClownModelLoader", + "ClownRegionalConditioning", + "ClownRegionalConditioning2", + "ClownRegionalConditioning3", + "ClownRegionalConditioning_AB", + "ClownRegionalConditioning_ABC", + "ClownRegionalConditionings", + "ClownScheduler", + "ClownpileModelWanVideo", + "Conditioning Recast FP64", + "ConditioningAdd", + "ConditioningAverageScheduler", + "ConditioningBatch4", + "ConditioningBatch8", + "ConditioningDownsample (T5)", + "ConditioningMultiply", + "ConditioningOrthoCollin", + "ConditioningToBase64", + "ConditioningTruncate", + "ConditioningZeroAndTruncate", + "Constant Scheduler", + "CrossAttn_EraseReplace_HiDream", + "EmptyLatentImage64", + "EmptyLatentImageCustom", + "Film Grain", + "FluxGuidanceDisable", + "FluxLoader", + "FluxOrthoCFGPatcher", + "Frame Select", + "Frame Select Latent", + "Frame Select Latent Raw", + "Frames Concat", + "Frames Concat Latent", + "Frames Concat Latent Raw", + "Frames Concat Masks", + "Frames Latent ReverseOrder", + "Frames Masks Uninterpolate", + "Frames Masks ZeroOut", + "Frames Slice", + "Frames Slice Latent", + "Frames Slice Latent Raw", + "Frequency Separation Hard Light", + "Frequency Separation Hard Light LAB", + "Frequency Separation Linear Light", + "Image Channels LAB", + "Image Crop Location Exact", + "Image Gaussian Blur", + "Image Get Color Swatches", + "Image Grain Add", + "Image Median Blur", + "Image Pair Split", + "Image Repeat Tile To Size", + "Image Sharpen FS", + "Latent Batcher", + "Latent Channels From To", + "Latent Clear State Info", + "Latent Display State Info", + "Latent Get Channel Means", + "Latent Match Channelwise", + "Latent Normalize Channels", + "Latent Replace State Info", + "Latent Transfer State Info", + "Latent to Cuda", + "Latent to RawX", + "LatentBatch_channels", + "LatentBatch_channels_16", + "LatentNoiseBatch_fractal", + "LatentNoiseBatch_gaussian", + "LatentNoiseBatch_gaussian_channels", + "LatentNoiseBatch_perlin", + "LatentNoiseList", + "LatentNoised", + "LatentPhaseMagnitude", + "LatentPhaseMagnitudeMultiply", + "LatentPhaseMagnitudeOffset", + "LatentPhaseMagnitudePower", + "LatentUpscaleWithVAE", + "LayerPatcher", + "Linear Quadratic Advanced", + "Mask Bounding Box Aspect Ratio", + "Mask Sketch", + "MaskEdge", + "MaskEdgeRatio", + "MaskFloatToBoolean", + "MaskToggle", + "Masks From Color Swatches", + "Masks From Colors", + "Masks Unpack 16", + "Masks Unpack 4", + "Masks Unpack 8", + "ModelSamplingAdvanced", + "ModelSamplingAdvancedResolution", + "ModelTimestepPatcher", + "PrepForUnsampling", + "ReAuraPatcher", + "ReAuraPatcherAdvanced", + "ReChromaPatcher", + "ReChromaPatcherAdvanced", + "ReFluxPatcher", + "ReFluxPatcherAdvanced", + "ReHiDreamPatcher", + "ReHiDreamPatcherAdvanced", + "ReLTXVPatcher", + "ReLTXVPatcherAdvanced", + "ReReduxPatcher", + "ReSD35Patcher", + "ReSD35PatcherAdvanced", + "ReSDPatcher", + "ReWanPatcher", + "ReWanPatcherAdvanced", + "SD35Loader", + "SeedGenerator", + "Set Precision", + "Set Precision Advanced", + "Set Precision Universal", + "SetImageSize", + "SetImageSizeWithScale", + "Sigmas Abs", + "Sigmas AdaptiveNoiseFloor", + "Sigmas AdaptiveStep", + "Sigmas Add", + "Sigmas Append", + "Sigmas ArcCosine", + "Sigmas ArcSine", + "Sigmas ArcTangent", + "Sigmas Attractor", + "Sigmas CNFInverse", + "Sigmas CatmullRom", + "Sigmas Chaos", + "Sigmas Cleanup", + "Sigmas CollatzIteration", + "Sigmas Concat", + "Sigmas ConwaySequence", + "Sigmas Count", + "Sigmas CrossProduct", + "Sigmas DeleteBelowFloor", + "Sigmas DeleteDuplicates", + "Sigmas DotProduct", + "Sigmas Easing", + "Sigmas Fmod", + "Sigmas Frac", + "Sigmas From Text", + "Sigmas GammaBeta", + "Sigmas Gaussian", + "Sigmas GaussianCDF", + "Sigmas GilbreathSequence", + "Sigmas HarmonicDecay", + "Sigmas Hyperbolic", + "Sigmas If", + "Sigmas InvLerp", + "Sigmas Iteration Karras", + "Sigmas Iteration Polyexp", + "Sigmas KernelSmooth", + "Sigmas LambertW", + "Sigmas LangevinDynamics", + "Sigmas Lerp", + "Sigmas LinearSine", + "Sigmas Logarithm2", + "Sigmas Math1", + "Sigmas Math3", + "Sigmas Modulus", + "Sigmas Mult", + "Sigmas Noise Inversion", + "Sigmas NormalizingFlows", + "Sigmas Pad", + "Sigmas Percentile", + "Sigmas PersistentHomology", + "Sigmas Power", + "Sigmas QuantileNorm", + "Sigmas Quotient", + "Sigmas ReactionDiffusion", + "Sigmas Recast", + "Sigmas Rescale", + "Sigmas RiemannianFlow", + "Sigmas SetFloor", + "Sigmas Sigmoid", + "Sigmas SmoothStep", + "Sigmas Split", + "Sigmas SquareRoot", + "Sigmas Start", + "Sigmas StepwiseMultirate", + "Sigmas TimeStep", + "Sigmas Truncate", + "Sigmas Unpad", + "Sigmas Variance Floor", + "Sigmas ZetaEta", + "Sigmas2 Add", + "Sigmas2 Mult", + "SigmasPreview", + "SigmasSchedulePreview", + "StableCascade_StageB_Conditioning64", + "StableCascade_StageC_VAEEncode_Exact", + "StyleModelApplyStyle", + "Tan Scheduler", + "Tan Scheduler 2", + "Tan Scheduler 2 Simple", + "TemporalCrossAttnMask", + "TemporalMaskGenerator", + "TemporalSplitAttnMask", + "TemporalSplitAttnMask (Midframe)", + "TextBox1", + "TextBox2", + "TextBox3", + "TextBoxConcatenate", + "TextConcatenate", + "TextLoadFile", + "TextShuffle", + "TextShuffleAndTruncate", + "TextTruncateTokens", + "TorchCompileModelAura", + "TorchCompileModelFluxAdv", + "TorchCompileModelSD35", + "TorchCompileModels", + "UNetSave", + "VAEEncodeAdvanced", + "VAEStyleTransferLatent" + ], + { + "title_aux": "RES4LYF" + } + ], + "https://github.com/Clybius/ComfyUI-ClybsChromaNodes": [ + [ + "ClybGuidance", + "InverseSquaredScheduler", + "PrintSigmas", + "SamplerClyb_BDF" + ], + { + "title_aux": "ComfyUI-ClybsChromaNodes" + } + ], + "https://github.com/Clybius/ComfyUI-Extra-Samplers": [ + [ + "GeometricCFGGuider", + "ImageAssistedCFGGuider", + "MegaCFGGuider", + "SamplerCLYB_4M_SDE_Momentumized", + "SamplerCustomModelMixtureDuo", + "SamplerCustomNoise", + "SamplerCustomNoiseDuo", + "SamplerDPMPP_3M_SDE_DynETA", + "SamplerDPMPP_DualSDE_Momentumized", + "SamplerEulerAncestralDancing_Experimental", + "SamplerLCMCustom", + "SamplerRES_Momentumized", + "SamplerSupreme", + "SamplerTTM", + "ScaledCFGGuider", + "SimpleExponentialScheduler", + "WarmupDecayCFGGuider" + ], + { + "title_aux": "ComfyUI Extra Samplers" + } + ], + "https://github.com/Clybius/ComfyUI-Latent-Modifiers": [ + [ + "Latent Diffusion Mega Modifier" + ], + { + "title_aux": "ComfyUI-Latent-Modifiers" + } + ], + "https://github.com/CoiiChan/ComfyUI-Depth-Visualization-Advanced": [ + [ + "DepthViewerAndQuilts" + ], + { + "title_aux": "ComfyUI-Depth-Visualization-advanced" + } + ], + "https://github.com/CoiiChan/ComfyUI-FuncAsTexture-CoiiNode": [ + [ + "Add", + "Ceil", + "Chroma_Key_Alpha", + "Clamp", + "Contant3Vector", + "CustomScriptNumpy", + "DDX", + "Desaturation", + "Distance", + "Divided", + "Dot", + "HueShift", + "InverseUVMapGenerator", + "Lerp", + "Max", + "Min", + "Multiply", + "Oneminus", + "Outline", + "Panner", + "Power", + "Rotator", + "Sine", + "Subtraction", + "TextureSampler", + "UVCoordinateGen", + "ifFunction" + ], + { + "title_aux": "ComfyUI-FuncAsTexture-CoiiNode" + } + ], + "https://github.com/ComfyAssets/ComfyUI-KikoStats": [ + [ + "ResourceMonitor" + ], + { + "title_aux": "ComfyUI-KikoStats" + } + ], + "https://github.com/ComfyAssets/ComfyUI-KikoTools": [ + [ + "EmptyLatentBatch", + "KikoSaveImage", + "ResolutionCalculator", + "SamplerCombo", + "SamplerComboCompact", + "SeedHistory", + "WidthHeightSelector" + ], + { + "title_aux": "ComfyUI-KikoTools" + } + ], + "https://github.com/ComfyAssets/ComfyUI_PromptManager": [ + [ + "PromptManager", + "PromptManagerText" + ], + { + "title_aux": "ComfyUI_PromptManager" + } + ], + "https://github.com/ComfyAssets/ComfyUI_Selectors": [ + [ + "HeightNode", + "SamplerSelector", + "SchedulerSelector", + "SeedHistory", + "WidthHeightNode", + "WidthNode" + ], + { + "title_aux": "ComfyUI_Selectors" + } + ], + "https://github.com/ComfyUI-JH/ComfyUI-JH-Misc-Nodes": [ + [ + "JHDaisyChainableStringConstantNode", + "JHPreviewImage", + "JHThreeWaySwitchNode", + "JHTwoWaySwitchNode" + ], + { + "title_aux": "JH Misc. Nodes" + } + ], + "https://github.com/ComplexRobot/ComfyUI-Simple-VFI": [ + [ + "Simple_Frame_Interpolation" + ], + { + "title_aux": "ComfyUI-Simple-VFI" + } + ], + "https://github.com/Conor-Collins/ComfyUI-CoCoTools_IO": [ + [ + "ColorspaceNode", + "CryptomatteLayer", + "ImageLoader", + "LoadExr", + "LoadExrLayerByName", + "LoadExrSequence", + "SaverNode", + "ZNormalizeNode" + ], + { + "title_aux": "ComfyUI-CoCoTools_IO" + } + ], + "https://github.com/CosmicLaca/ComfyUI_Primere_Nodes": [ + [ + "DebugToFile", + "PrimereAestheticCKPTScorer", + "PrimereAnyDetailer", + "PrimereAnyOutput", + "PrimereCKPT", + "PrimereCKPTLoader", + "PrimereCLIPEncoder", + "PrimereClearNetworkTagsPrompt", + "PrimereConceptDataTuple", + "PrimereDiTPurifyPrompt", + "PrimereDynamicParser", + "PrimereEmbedding", + "PrimereEmbeddingHandler", + "PrimereEmbeddingKeywordMerger", + "PrimereEmotionsStyles", + "PrimereFaceAnalyzer", + "PrimereFastSeed", + "PrimereHypernetwork", + "PrimereImageSegments", + "PrimereImgToPrompt", + "PrimereKSampler", + "PrimereLLMEnhancer", + "PrimereLLMEnhancerOptions", + "PrimereLORA", + "PrimereLYCORIS", + "PrimereLatentNoise", + "PrimereLensStyles", + "PrimereLoraKeywordMerger", + "PrimereLoraStackMerger", + "PrimereLycorisKeywordMerger", + "PrimereLycorisStackMerger", + "PrimereMetaCollector", + "PrimereMetaDistributor", + "PrimereMetaDistributorStage2", + "PrimereMetaHandler", + "PrimereMetaSave", + "PrimereMetaTupleCollector", + "PrimereMidjourneyStyles", + "PrimereModelConceptSelector", + "PrimereModelKeyword", + "PrimereNetworkDataCollector", + "PrimereNetworkTagLoader", + "PrimerePreviewImage", + "PrimerePrompt", + "PrimerePromptOrganizer", + "PrimerePromptOrganizerCSV", + "PrimerePromptSwitch", + "PrimereRefinerPrompt", + "PrimereResolution", + "PrimereResolutionCoordinatorMPX", + "PrimereResolutionMultiplierMPX", + "PrimereSamplersSteps", + "PrimereSeed", + "PrimereStyleLoader", + "PrimereStylePile", + "PrimereTextOutput", + "PrimereUpscaleModel", + "PrimereVAE", + "PrimereVAELoader", + "PrimereVisualCKPT", + "PrimereVisualEmbedding", + "PrimereVisualHypernetwork", + "PrimereVisualLORA", + "PrimereVisualLYCORIS", + "PrimereVisualPromptOrganizerCSV", + "PrimereVisualStyle" + ], + { + "title_aux": "Primere nodes for ComfyUI" + } + ], + "https://github.com/CpreForEver/CFE_comfyui": [ + [ + "CFE Aspect Ratio", + "CFE FLUX Guidance", + "CFE FLUX Sampler", + "CFE FLUX Sampler (Pipe)", + "CFE Flux In Pipe", + "CFE Flux Out Pipe", + "CFE Lora Params", + "CFE Scheduler", + "CFE Sigma Sampler", + "CFE Sigma Sampler Strings" + ], + { + "title_aux": "CFE_comfyui" + } + ], + "https://github.com/Creeper-MZ/comfyui_nai_api": [ + [ + "NovelAI", + "NovelAI_Declutter_Preprocessor", + "NovelAI_Lineart_Processor", + "NovelAI_Prompt", + "NovelAI_Sketch_Processor", + "NovelAI_VIBE" + ], + { + "title_aux": "comfyui_nai_api" + } + ], + "https://github.com/Creepybits/ComfyUI-Creepy_nodes": [ + [ + "AudioKeywordExtractor", + "CLIPSwitch", + "Categorizer", + "CollectAndDistributeText", + "Coloring", + "ConditionalLoRAApplierCreepybits", + "CustomNodeManager", + "DelayNode", + "DelayTextNode", + "DynamicClipswitch", + "DynamicConditioning", + "DynamicDelayText", + "DynamicImageSwitch", + "DynamicLatentSwitch", + "DynamicModelswitch", + "DynamicVAESwitch", + "EvaluaterNode", + "FilterImages", + "GeminiAPI", + "GeminiAudioAnalyzer", + "GeminiTokenCounter", + "IMGToIMGConditioning", + "KeywordExtractor", + "LoadBatchImagesDir", + "MasterKey", + "Modelswitch", + "PeopleEvaluationNode", + "PromptGenerator", + "RandomAudioSegment", + "SanitizeFilename", + "SummaryWriter", + "SystemPromp", + "Textswitch", + "VAESwitch" + ], + { + "title_aux": "ComfyUI-Creepy_nodes" + } + ], + "https://github.com/Creepybits/ComfyUI-Save_To_GDrive": [ + [ + "SaveImageToGoogleDrive" + ], + { + "title_aux": "Save Image To Google Drive" + } + ], + "https://github.com/Creepybits/ComfyUI-Save_To_OneDrive": [ + [ + "SaveImageToOneDrive_CreepyBits" + ], + { + "title_aux": "Comfyui-Save_To_OneDrive" + } + ], + "https://github.com/Cryptyox/anaglyphTool-Comfyui": [ + [ + "AnaglyphTool", + "CrossEyeTool", + "StereogramTool" + ], + { + "author": "Timon", + "description": "Provides CUDA GPU accelerated nodes for creating 3D images (Anaglyph, Cross-Eye, Stereogram).", + "nickname": "StereoTools", + "title": "Stereo Tools (CUDA)", + "title_aux": "anaglyphTool-Comfyui" + } + ], + "https://github.com/Curt-Park/human-parser-comfyui-node-in-pure-python": [ + [ + "Cozy Human Parser ATR", + "Cozy Human Parser LIP", + "Cozy Human Parser Pascal" + ], + { + "title_aux": "Cozy Human Parser in pure Python" + } + ], + "https://github.com/CyanAutumn/ComfyUi_Random_Manage_Cyan": [ + [ + "Random Prompt Cyan", + "Remove Prompt Cyan" + ], + { + "title_aux": "ComfyUi Random Manage Cyan" + } + ], + "https://github.com/Cyber-BlackCat/ComfyUI-Image-Vector": [ + [ + "Vector" + ], + { + "title_aux": "ComfyUI-Image-Vector" + } + ], + "https://github.com/Cyber-BlackCat/ComfyUI-MoneyMaker": [ + [ + ", and the value is the function name in the right of the", + "Black and white", + "Image Judgment", + "Image Resize MM", + "ImageMinusMask", + "Light or Dark", + "Load Random Images", + "Mask Preprocess Morphology", + "Mask To Gray", + "Number", + "PhotoShop Transfer", + "SomethingShow", + "TensorShow", + "a fake Nod" + ], + { + "title_aux": "ComfyUI-Yuan" + } + ], + "https://github.com/Cyber-BlackCat/ComfyUI_Auto_Caption": [ + [ + "Auto Caption", + "Auto_Caption2", + "ExtraOptionsSet", + "Joy Model load", + "Joy_Model2_load", + "LoadManyImages" + ], + { + "title_aux": "ComfyUI_Auto_Caption" + } + ], + "https://github.com/Cyberschorsch/ComfyUI-checkpoint-config-loader": [ + [ + "Checkpoint Loader Config" + ], + { + "title_aux": "ComfyUI Checkpoint Loader Config" + } + ], + "https://github.com/Cyrostar/ComfyUI-Artha-Gemini": [ + [ + "Gemini Backdrop", + "Gemini Body", + "Gemini Cloth", + "Gemini Condense", + "Gemini Face", + "Gemini Form", + "Gemini Instruct", + "Gemini Makeup", + "Gemini Markdown", + "Gemini Motion", + "Gemini Operation", + "Gemini Portrait", + "Gemini Prompter", + "Gemini Question", + "Gemini Response", + "Gemini Scenery", + "Gemini Translate", + "Gemini Vision" + ], + { + "title_aux": "ComfyUI-Artha-Gemini" + } + ], + "https://github.com/DJ-Tribefull/Comfyui_FOCUS_nodes": [ + [ + "Control Pipe (Focus Nodes)", + "FOCUS Upscale (Focus Nodes)", + "Global Seed Controller (Focus Nodes)", + "KSampler Settings (Focus Nodes)", + "Model Unloader (Focus Nodes)", + "Prompt Box (Focus Nodes)", + "SDXL All-In-One (Focus Nodes)", + "SDXL Control Module (Focus Nodes)", + "SDXL Preprocess (Focus Nodes)", + "Style Injector (Focus Nodes)", + "Style Selector (Focus Nodes)", + "Text Display (Focus Nodes)", + "Wildcard Processor (Focus Nodes)" + ], + { + "title_aux": "Comfyui FOCUS nodes" + } + ], + "https://github.com/Danand/ComfyUI-ComfyCouple": [ + [ + "Attention couple", + "Comfy Couple" + ], + { + "author": "Rei D.", + "description": "If you want to draw two different characters together without blending their features, so you could try to check out this custom node.", + "nickname": "Danand", + "title": "Comfy Couple", + "title_aux": "Comfy Couple" + } + ], + "https://github.com/DanielHabib/ComfyUI-Voxels": [ + [ + "ImageBatchToImageList", + "MaskBatchToMaskList", + "MeshToVoxel", + "VoxelBlockLoader", + "VoxelBlockSaver", + "VoxelBlocksIntoVoxelVideo", + "VoxelVideoAPIInputNode", + "VoxelVideoLoader", + "VoxelVideoPreview", + "VoxelVideoViewer", + "VoxelViewer", + "VoxelizeMesh" + ], + { + "title_aux": "ComfyUI-Voxels" + } + ], + "https://github.com/DareFail/ComfyUI-Roboflow": [ + [ + "CustomWorkflow_1image", + "LabelEmotions", + "RemoveBackground" + ], + { + "title_aux": "ComfyUI-Roboflow" + } + ], + "https://github.com/DarioFT/ComfyUI-VideoDirCombiner": [ + [ + "VideoDirCombiner" + ], + { + "title_aux": "ComfyUI-VideoDirCombiner" + } + ], + "https://github.com/DataCTE/prompt_injection": [ + [ + "AdvancedPromptInjection", + "PromptInjection", + "SVDPromptInjection", + "SimplePromptInjection" + ], + { + "title_aux": "Prompt Injection Node for ComfyUI" + } + ], + "https://github.com/DavidPiazza/network_bending": [ + [ + "AudioFeatureExtractor", + "AudioLatentBlend", + "AudioLatentGuidance", + "AudioLatentInterpolate", + "AudioLatentManipulator", + "AudioReferenceEncoder", + "AudioStyleTransfer", + "AudioVAEDecode", + "AudioVAEEncode", + "LatentFormatConverter", + "ModelMixer", + "NetworkBending", + "NetworkBendingAdvanced", + "VAEChannelManipulator", + "VAELatentBending", + "VAEMixer", + "VAENetworkBending" + ], + { + "title_aux": "Network Bending for ComfyUI" + } + ], + "https://github.com/Dayuppy/ComfyUI-DiscordWebhook": [ + [ + "DiscordPostViaWebhook", + "DiscordSetWebhook", + "Set Discord Webhook", + "Use Discord Webhook" + ], + { + "author": "Dayuppy", + "description": "A very simple Discord webhook integration node for ComfyUI that lets you post images and text.", + "nickname": "DiscordWebhook", + "title": "Discord Webhook", + "title_aux": "Discord Webhook" + } + ], + "https://github.com/De-Zoomer/ComfyUI-DeZoomer-Nodes": [ + [ + "CaptionRefinement", + "VideoCaptioning" + ], + { + "title_aux": "ComfyUI-DeZoomer-Nodes" + } + ], + "https://github.com/DeJoker/pipeline-parallel-comfy": [ + [ + "PipelineParallel" + ], + { + "title_aux": "Pipeline Parallel ComfyUI" + } + ], + "https://github.com/DebugPadawan/DebugPadawans-ComfyUI-Essentials": [ + [ + "DebugPadawan_ConditionalString", + "DebugPadawan_DebugPrint", + "DebugPadawan_ListInfo", + "DebugPadawan_TextJoiner", + "DebugPadawan_TextSplitter", + "DebugPadawan_TextToJSON", + "DebugPadawan_WaitNode" + ], + { + "title_aux": "DebugPadawan's ComfyUI Essentials" + } + ], + "https://github.com/Deep-Neko/ComfyUI_ascii_art": [ + [ + "AsciiGenerator" + ], + { + "author": "DeepNeko ", + "title_aux": "ascii-art-comfyui" + } + ], + "https://github.com/Derfuu/Derfuu_ComfyUI_ModdedNodes": [ + [], + { + "author": "Derfuu", + "description": "Pack of simple (or not) and modded nodes for scaling images/latents, editing numbers or text. Automate calculations depending on image sizes or any other thing you want. Or randomize any number in your workflow. Debug node included.", + "nickname": "Derfuu simple/modded Nodes", + "nodename_pattern": "^DF_", + "title": "Derfuu simple/modded Nodes", + "title_aux": "Derfuu_ComfyUI_ModdedNodes" + } + ], + "https://github.com/DesertPixelAi/ComfyUI-Desert-Pixel-Nodes": [ + [ + "DP 10 Images Switch Or Batch", + "DP 10 String Switch Or Connect", + "DP 2 String Switch", + "DP 3 Images Switch Or Batch", + "DP 3 String Switch Or Connect", + "DP 5 Find And Replace", + "DP 5 Image And Mask Switch", + "DP 5 Images Switch Or Batch", + "DP 5 String Switch Or Connect", + "DP Add Background To Png", + "DP Add Weight To String Sdxl", + "DP Advanced Sampler", + "DP Advanced Weight String Sdxl", + "DP Animation Calculator 10 Inputs", + "DP Animation Calculator 5 Inputs", + "DP Art Style Generator", + "DP Aspect Ratio Picker", + "DP Big Letters", + "DP Broken Token", + "DP Clean Prompt", + "DP Clean Prompt Travel", + "DP Condition Switch", + "DP ControlNet Apply Advanced", + "DP Crazy Prompt Mixer", + "DP Create Json File", + "DP Custom Aspect Ratio", + "DP Diff Int 8step Selector", + "DP Draggable Floats 1", + "DP Draggable Floats 2", + "DP Draggable Floats 3", + "DP Draggable Int 1step", + "DP Draggable Int 4step", + "DP Draggable Int 8step", + "DP Extract Mask", + "DP Fast Slow Motion", + "DP Five Lora", + "DP Five Lora Random", + "DP Float Stepper", + "DP Get Seed From Image", + "DP IF Int Condition", + "DP Image And String Pairs Switch", + "DP Image Color Analyzer", + "DP Image Color Analyzer Small", + "DP Image Color Effect", + "DP Image Effect Processor", + "DP Image Effect Processor Small", + "DP Image Empty Latent Switch Flux", + "DP Image Empty Latent Switch SDXL", + "DP Image Grid To Image", + "DP Image Slice To Grid", + "DP Image Slide Show", + "DP Image Strip", + "DP Image To Pixelgrid", + "DP Int 0 1000", + "DP Latent Split", + "DP Line Cycler", + "DP Load Checkpoint With Info", + "DP Load Controlnet Model With Name", + "DP Load Dual CLIP With Info", + "DP Load Image Effects", + "DP Load Image Effects Small", + "DP Load Image Folder", + "DP Load Image Minimal", + "DP Load Image V2", + "DP Load Image With Seed", + "DP Load UNET With Info", + "DP Logo Animator", + "DP Lora Random Strength Controller", + "DP Lora Strength Controller", + "DP Mask Settings", + "DP Place Image", + "DP Prompt Inverter", + "DP Prompt Manager Small", + "DP Prompt Mode Controller", + "DP Prompt Styler", + "DP Prompt Token Compressor", + "DP Prompt Travel Prompt", + "DP Quick Model Link", + "DP Random Character", + "DP Random Crazy Prompt Generator", + "DP Random Logo Style Generator", + "DP Random Min Max", + "DP Random Mode Controller", + "DP Random Mode Switch", + "DP Random Psychedelic Punk Generator", + "DP Random Superhero Prompt Generator", + "DP Random Vehicle Generator", + "DP Resize Image And Mask", + "DP Sampler With Info", + "DP Save Image V2", + "DP Save Preview Image", + "DP Stitch 2 Images", + "DP String Text", + "DP String Text With Sdxl Weight", + "DP Strip Edge Masks", + "DP Switch Controller", + "DP Text Preview", + "DP Transition Frames Selector", + "DP Versatile Prompt Subjects Generator", + "DP Video Effect Receiver", + "DP Video Effect Sender", + "DP Video Flicker", + "DP Video Looper", + "DP Video Transition", + "DP Words", + "DP_Crazy_Prompt_Mixer", + "DP_Float_Stepper", + "DP_Image_To_Pixelgrid", + "DP_Prompt_Inverter" + ], + { + "title_aux": "ComfyUI-Desert-Pixel-Nodes" + } + ], + "https://github.com/DesertPixelAi/comfyui-dp-them-styler": [ + [ + "DP_Add_Logo_Banner", + "DP_Advanced_Sampler_Modified", + "DP_Dynamic_Random_Styler", + "DP_Gender_Age_Detector" + ], + { + "title_aux": "ComfyUI DP Dynamic Random Styler" + } + ], + "https://github.com/DiaoDaiaChan/ComfyUI_API_Request": [ + [ + "Character_Prompt_Select", + "NovelAI_Request", + "NovelAI_Request_Payload", + "SDWebUI_Request", + "SDWebUI_Request_Payload", + "SDWebUI_Request_PayloadExtend" + ], + { + "title_aux": "Comfyui SDAPI Request / NovelAI" + } + ], + "https://github.com/DiffusionLight/DiffusionLight-ComfyUI": [ + [ + "DiffusionLightBall2Envmap", + "DiffusionLightChromeballMask", + "DiffusionLightExposure2HDR", + "DiffusionLightExposureBracket", + "DiffusionLightPadBlackBorder", + "DiffusionLightPercentileToPixelValueTonemap", + "DiffusionLightSaveHDR" + ], + { + "title_aux": "DiffusionLight-ComfyUI" + } + ], + "https://github.com/Diohim/ComfyUI-Unusual-Tools": [ + [ + "AdjustCrop", + "AutoImageResize", + "BatchLoadLatentImage", + "BatchSaveLatentImage", + "FillMaskWithColor" + ], + { + "title_aux": "ComfyUI Unusual Tools" + } + ], + "https://github.com/Dobidop/ComfyStereo": [ + [ + "DeoVRViewNode", + "StereoImageNode" + ], + { + "title_aux": "Dobidop ComfyStereo" + } + ], + "https://github.com/DoctorDiffusion/ComfyUI-BEN": [ + [ + "BackgroundEraseNetwork" + ], + { + "title_aux": "ComfyUI BEN - Background Erase Network" + } + ], + "https://github.com/DoctorDiffusion/ComfyUI-MediaMixer": [ + [ + "FinalFrameSelector", + "FirstFrameSelector", + "PromptJournal", + "ReverseFrameSequence", + "VideoMerge", + "YouTubeVideoDownloader" + ], + { + "title_aux": "MediaMixer" + } + ], + "https://github.com/DoctorDiffusion/ComfyUI-Schedulizer": [ + [ + "prompt_schedule_converter", + "whisper_node" + ], + { + "title_aux": "Schedulizer" + } + ], + "https://github.com/DoctorDiffusion/ComfyUI-SnakeOil": [ + [ + "NegativeLoRALoader" + ], + { + "title_aux": "ComfyUI-SnakeOil" + } + ], + "https://github.com/DoctorDiffusion/ComfyUI-basic-pitch": [ + [ + "AudioToMidi", + "SaveMidi" + ], + { + "title_aux": "ComfyUI-basic-pitch" + } + ], + "https://github.com/Dontdrunk/ComfyUI-DD-Nodes": [ + [ + "DD-AdvancedFusion", + "DD-ConditionSwitcher", + "DD-DimensionCalculator", + "DD-ImageSizeLimiter", + "DD-ImageStroke", + "DD-ImageToVideo", + "DD-ImageUniformSize", + "DD-LatentSwitcher", + "DD-MaskUniformSize", + "DD-ModelOptimizer", + "DD-ModelSwitcher", + "DD-QwenMTTranslator", + "DD-SamplingOptimizer", + "DD-SimpleLatent", + "DD-VideoFrameExtractor" + ], + { + "title_aux": "ComfyUI-DD-Nodes" + } + ], + "https://github.com/DrMWeigand/ComfyUI-StereoVision": [ + [ + "AutostereogramGenerator", + "StereoscopicGenerator" + ], + { + "title_aux": "StereoVision Plugin for ComfyUI" + } + ], + "https://github.com/DrMWeigand/ComfyUI_ColorImageDetection": [ + [ + "LABColorDetection", + "RGBColorDetection" + ], + { + "title_aux": "ComfyUI Color Detection Nodes" + } + ], + "https://github.com/DrStone71/ComfyUI-Prompt-Translator": [ + [ + "CLIP Text Encode (Translate)", + "CLIP Text Translate Advanced", + "Combine Conditioning", + "Conditional Translate", + "Language Package Manager", + "Prompt Text (Translate)", + "Text Translate", + "Universal Text Translate" + ], + { + "title_aux": "ComfyUI-Prompt-Translator" + } + ], + "https://github.com/DraconicDragon/ComfyUI-RyuuNoodles": [ + [ + "Ryuu_CleanStringAdvanced", + "Ryuu_ColorMatch", + "Ryuu_ExtractAndSaveLora", + "Ryuu_FallbackPassthrough", + "Ryuu_FallbackSwitchAny", + "Ryuu_FallbackSwitchImage", + "Ryuu_FallbackSwitchLatent", + "Ryuu_FloatPlain", + "Ryuu_FloatPlainLarger", + "Ryuu_FloatSlider", + "Ryuu_IntSlider", + "Ryuu_IsMultipleOf", + "Ryuu_ScaleToMultiple", + "Ryuu_ScaleToMultipleAdvanced", + "Ryuu_ScaleToMultipleLatentSizePicker", + "Ryuu_TestNode", + "Ryuu_TextEncoderDiffCheck", + "Ryuu_TokenCountTextBox" + ], + { + "title_aux": "ComfyUI-RyuuNoodles" + } + ], + "https://github.com/DraconicDragon/ComfyUI-Venice-API": [ + [ + "CharCountTextBox", + "GenerateImage_VENICE", + "GenerateSpeech_VENICE", + "GenerateTextAdvanced_VENICE", + "GenerateTextVeniceParameters_VENICE", + "GenerateText_VENICE", + "I2IEnhanceUpscale_VENICE" + ], + { + "title_aux": "ComfyUI-Venice-API" + } + ], + "https://github.com/DragonDiffusionbyBoyo/BoyoSupercoolWrapper": [ + [ + "BoyoSuperCoolWrapper" + ], + { + "title_aux": "BoyoSupercoolWrapper" + } + ], + "https://github.com/DragonDiffusionbyBoyo/Boyonodes": [ + [ + "BoyoAudioEval", + "BoyoFramePackLoRA", + "BoyoLoadImageList", + "BoyoSaver", + "BoyoVAEDecode", + "Boyolatent", + "MandelbrotVideo" + ], + { + "title_aux": "Boyonodes" + } + ], + "https://github.com/Dream-Pixels-Forge/ComfyUI-Mzikart-Mixer": [ + [ + "ArrangementEnforcer", + "AudioPostProcessor", + "CompressorNode", + "LimiterNode", + "MasteringEffects" + ], + { + "title_aux": "ComfyUI Mzikart Mixer" + } + ], + "https://github.com/Duanyll/duanyll_nodepack": [ + [ + "CreateBoundingBoxesMaskQwen", + "DrawBoundingBoxesQwen", + "FluxKontextTrue3DPE", + "FluxTextLoraLoader", + "HfCheckpointLoader", + "HfDiffusionModelLoader", + "HfDualClipLoader", + "HfLoraLoader", + "HfLoraLoaderModelOnly", + "HfQuadrupleClipLoader", + "HfTripleClipLoader", + "HfVaeLoader", + "ImageDifferenceCmap", + "PhotoDoddleConditioning" + ], + { + "title_aux": "Duanyll Nodepack" + } + ], + "https://github.com/Eagle-CN/ComfyUI-Addoor": [ + [ + "AD_AnyFileList", + "AD_BatchImageLoadFromDir", + "AD_CSVPromptStyler", + "AD_CSVReader", + "AD_CSVTranslator", + "AD_DeleteLocalAny", + "AD_FluxTrainStepMath", + "AD_HFDownload", + "AD_ImageDrawRectangleSimple", + "AD_ImageIndexer", + "AD_ImageSaver", + "AD_LoadImageAdvanced", + "AD_PromptReplace", + "AD_TextIndexer", + "AD_TextListToString", + "AD_TextSaver", + "AD_TxtToCSVCombiner", + "AD_ZipSave", + "AD_advanced-padding", + "AD_color-image", + "AD_image-concat", + "AD_image-resize", + "AD_mockup-maker", + "AD_poster-maker", + "AD_prompt-saver", + "ImageCaptioner", + "ImageResize", + "Incrementer \ud83e\udeb4", + "TextAppendNode", + "Width and height for scaling image to ideal resolution \ud83e\udeb4", + "Width and height from aspect ratio \ud83e\udeb4", + "YANC.MultilineString", + "comfyui-easy-padding", + "image concat mask" + ], + { + "author": "ComfyUI Addoor", + "description": "Save prompts to CSV file with customizable naming pattern", + "title": "ComfyUI-PromptSaver", + "title_aux": "ComfyUI-Addoor" + } + ], + "https://github.com/Easymode-ai/ComfyUI-BPT": [ + [ + "TrimeshBPT", + "TrimeshLoad", + "TrimeshPreview", + "TrimeshSave" + ], + { + "title_aux": "ComfyUI-BPT" + } + ], + "https://github.com/Easymode-ai/ComfyUI-ShadowR": [ + [ + "ShadowRModelLoader", + "ShadowRShadowRemover" + ], + { + "title_aux": "ComfyUI-ShadowR" + } + ], + "https://github.com/EeroHeikkinen/ComfyUI-eesahesNodes": [ + [ + "InstantX Flux Union ControlNet Loader" + ], + { + "author": "eesahe", + "description": "InstantX's Flux union ControlNet loader and implementation", + "nickname": "eesahesNodes", + "title": "eesahe's Nodes", + "title_aux": "ComfyUI-eesahesNodes" + } + ], + "https://github.com/Elaine-chennn/comfyui-overlay-media": [ + [ + "OverlayMediaNode", + "VideoUpload" + ], + { + "title_aux": "ComfyUI Overlay Media Node" + } + ], + "https://github.com/Electrofried/ComfyUI-OpenAINode": [ + [ + "OpenAINode" + ], + { + "title_aux": "OpenAINode" + } + ], + "https://github.com/EllangoK/ComfyUI-post-processing-nodes": [ + [ + "ArithmeticBlend", + "AsciiArt", + "Blend", + "Blur", + "CannyEdgeMask", + "ChromaticAberration", + "ColorCorrect", + "ColorTint", + "Dissolve", + "Dither", + "DodgeAndBurn", + "FilmGrain", + "Glow", + "HSVThresholdMask", + "KMeansQuantize", + "KuwaharaBlur", + "Parabolize", + "PencilSketch", + "PixelSort", + "Pixelize", + "Quantize", + "Sharpen", + "SineWave", + "Solarize", + "Vignette" + ], + { + "title_aux": "ComfyUI-post-processing-nodes" + } + ], + "https://github.com/EmAySee/ComfyUI_EmAySee_CustomNodes": [ + [ + "EmAySee_AnyPassthrough", + "EmAySee_CheckboxFloatNode", + "EmAySee_DateTimeStringNode", + "EmAySee_DynamicStringSelectorNode", + "EmAySee_GreaterThanNode", + "EmAySee_HostPinger", + "EmAySee_ImagePassthrough", + "EmAySee_IntegerStringSelectorNode", + "EmAySee_IntegerStringSelectorNodeDynamic", + "EmAySee_MultiplierNode", + "EmAySee_ProbabilityStringSelectorNode", + "EmAySee_RandomIntFromList", + "EmAySee_RandomIntegerFromListNode", + "EmAySee_RandomIntegerFromTogglesNode_PremadeLabels", + "EmAySee_RandomStringSelectorNode", + "EmAySee_RandomStringSelectorNodeFourChoice", + "EmAySee_RandomStringSelectorNodeThreeChoice", + "EmAySee_RemoveDuplicateCSV", + "EmAySee_RepaintKSampler", + "EmAySee_SaveImage", + "EmAySee_SaveTextToFile", + "EmAySee_StringPoseSelectorNode", + "EmAySee_StringTupleInputNode", + "EmAySee_SubmitToOobaboogaAPI", + "EmAySee_SubmitToOobaboogaAPIWithKey", + "EmAySee_ToggleIntNode", + "EmAySee_VarTextReplacer", + "EmAySee_VeryUniqueStringSelectorNode" + ], + { + "title_aux": "ComfyUI_EmAySee_CustomNodes" + } + ], + "https://github.com/EnragedAntelope/ComfyUI-ConstrainResolution": [ + [ + "ConstrainResolution" + ], + { + "title_aux": "ComfyUI-ConstrainResolution" + } + ], + "https://github.com/EnragedAntelope/ComfyUI-Doubutsu-Describer": [ + [ + "DoubutsuDescriber" + ], + { + "title_aux": "ComfyUI-Doubutsu-Describer" + } + ], + "https://github.com/EnragedAntelope/ComfyUI-EACloudNodes": [ + [ + "GroqNode", + "OpenRouterModels", + "OpenrouterNode" + ], + { + "title_aux": "ComfyUI-EACloudNodes" + } + ], + "https://github.com/EnragedAntelope/comfyui-relight": [ + [ + "ReLight" + ], + { + "title_aux": "ComfyUI-ReLight" + } + ], + "https://github.com/Erehr/ComfyUI-EreNodes": [ + [ + "ErePromptCloud", + "ErePromptFilter", + "ErePromptGallery", + "ErePromptLoraStack", + "ErePromptMultiSelect", + "ErePromptMultiline", + "ErePromptRandomizer", + "ErePromptToggle" + ], + { + "title_aux": "ComfyUI-EreNodes" + } + ], + "https://github.com/EvilBT/ComfyUI_SLK_joy_caption_two": [ + [ + "Batch_joy_caption_two", + "Batch_joy_caption_two_advanced", + "Joy_caption_two", + "Joy_caption_two_advanced", + "Joy_caption_two_load", + "Joy_extra_options" + ], + { + "title_aux": "JoyCaptionAlpha Two for ComfyUI" + } + ], + "https://github.com/Excidos/ComfyUI-Documents": [ + [ + "ChunkRouter", + "DocumentLoader", + "ImageSelector", + "PDFPageSplitter", + "PDFToImage", + "TextChunker" + ], + { + "title_aux": "ComfyUI-Documents" + } + ], + "https://github.com/Excidos/ComfyUI-Lumina-Next-SFT-DiffusersWrapper": [ + [ + "LuminaDiffusersNode" + ], + { + "title_aux": "ComfyUI-Lumina-Next-SFT-DiffusersWrapper" + } + ], + "https://github.com/ExponentialML/ComfyUI_ModelScopeT2V": [ + [ + "ModelScopeT2VLoader" + ], + { + "title_aux": "ComfyUI_ModelScopeT2V" + } + ], + "https://github.com/ExponentialML/ComfyUI_Native_DynamiCrafter": [ + [ + "DynamiCrafterLoader", + "DynamiCrafterProcessor" + ], + { + "title_aux": "ComfyUI - Native DynamiCrafter" + } + ], + "https://github.com/ExponentialML/ComfyUI_VisualStylePrompting": [ + [ + "ApplyVisualStyle" + ], + { + "title_aux": "ComfyUI_VisualStylePrompting" + } + ], + "https://github.com/ExterminanzHS/Gecco-Discord-Autosend": [ + [ + "GeccoAutosend", + "GeccoImageSave", + "GeccoSelectchannel" + ], + { + "title_aux": "Gecco Discord Autosend" + } + ], + "https://github.com/Extraltodeus/ComfyUI-AutomaticCFG": [ + [ + "Automatic CFG", + "Automatic CFG - Advanced", + "Automatic CFG - Attention modifiers", + "Automatic CFG - Attention modifiers tester", + "Automatic CFG - Custom attentions", + "Automatic CFG - Excellent attention", + "Automatic CFG - Negative", + "Automatic CFG - Post rescale only", + "Automatic CFG - Preset Loader", + "Automatic CFG - Unpatch function", + "Automatic CFG - Warp Drive", + "SAG delayed activation", + "Temperature separate settings CLIP SDXL", + "Temperature settings CLIP", + "Temperature settings SD 1.5", + "Temperature settings SDXL", + "Zero Uncond CFG - standalone patch (incompatible with the others)" + ], + { + "title_aux": "ComfyUI-AutomaticCFG" + } + ], + "https://github.com/Extraltodeus/DistanceSampler": [ + [ + "SamplerDistance", + "SamplerDistanceAdvanced" + ], + { + "title_aux": "DistanceSampler" + } + ], + "https://github.com/Extraltodeus/LoadLoraWithTags": [ + [ + "LoraLoaderTagsQuery" + ], + { + "title_aux": "LoadLoraWithTags" + } + ], + "https://github.com/Extraltodeus/Negative-attention-for-ComfyUI-": [ + [ + "Negative cross attention", + "Negative cross attention concatenate" + ], + { + "title_aux": "Negative-attention-for-ComfyUI-" + } + ], + "https://github.com/Extraltodeus/Skimmed_CFG": [ + [ + "Skimmed CFG", + "Skimmed CFG - Clean Skim", + "Skimmed CFG - Difference CFG", + "Skimmed CFG - Timed flip", + "Skimmed CFG - linear interpolation", + "Skimmed CFG - linear interpolation dual scales", + "Skimmed CFG - replace" + ], + { + "title_aux": "Skimmed_CFG" + } + ], + "https://github.com/Extraltodeus/Stable-Diffusion-temperature-settings": [ + [ + "CLIP Temperature", + "Unet Temperature" + ], + { + "title_aux": "Stable-Diffusion-temperature-settings" + } + ], + "https://github.com/Extraltodeus/Uncond-Zero-for-ComfyUI": [ + [ + "Conditioning combine positive and negative", + "Conditioning crop or fill", + "Uncond Zero", + "interrupt on NaN" + ], + { + "title_aux": "Uncond-Zero-for-ComfyUI" + } + ], + "https://github.com/Extraltodeus/Vector_Sculptor_ComfyUI": [ + [ + "CLIP Vector Sculptor text encode", + "Conditioning (Average keep magnitude)", + "Conditioning (Slerp)", + "Conditioning SDXL merge clip_g / clip_l", + "Conditioning normalize magnitude to empty" + ], + { + "title_aux": "Vector_Sculptor_ComfyUI" + } + ], + "https://github.com/Extraltodeus/noise_latent_perlinpinpin": [ + [ + "NoisyLatentPerlin", + "NoisyLatentPerlin16ch" + ], + { + "title_aux": "noise latent perlinpinpin" + } + ], + "https://github.com/Extraltodeus/sigmas_tools_and_the_golden_scheduler": [ + [ + "Aligned Scheduler", + "Gaussian Tail Scheduler", + "Get sigmas as float", + "Graph sigmas", + "Manual scheduler", + "Merge many sigmas by average", + "Merge sigmas by average", + "Merge sigmas gradually", + "Multiply sigmas", + "Output min/max sigmas", + "Split and concatenate sigmas", + "The Golden Scheduler" + ], + { + "title_aux": "sigmas_tools_and_the_golden_scheduler" + } + ], + "https://github.com/FaberVS/MultiModel": [ + [ + "ActiveModel", + "DenoiseSelector", + "KSamplerPipe", + "ListSelector", + "ModelParamsPipe", + "MySwitchIndex", + "ParamsPipeUnpack", + "PromptBuilder" + ], + { + "title_aux": "MultiModel" + } + ], + "https://github.com/Fannovel16/ComfyUI-Frame-Interpolation": [ + [ + "AMT VFI", + "CAIN VFI", + "FILM VFI", + "FLAVR VFI", + "GMFSS Fortuna VFI", + "IFRNet VFI", + "IFUnet VFI", + "KSampler Gradually Adding More Denoise (efficient)", + "M2M VFI", + "Make Interpolation State List", + "RIFE VFI", + "STMFNet VFI", + "Sepconv VFI", + "VFI FloatToInt" + ], + { + "title_aux": "ComfyUI Frame Interpolation" + } + ], + "https://github.com/Fannovel16/ComfyUI-MagickWand": [ + [ + "ImageMagick Adaptive Blur", + "ImageMagick Adaptive Resize", + "ImageMagick Adaptive Sharpen", + "ImageMagick Adaptive Threshold", + "ImageMagick Auto Gamma", + "ImageMagick Auto Level", + "ImageMagick Auto Orient", + "ImageMagick Auto Threshold", + "ImageMagick Blue Shift", + "ImageMagick Blur", + "ImageMagick Brightness Contrast", + "ImageMagick Canny", + "ImageMagick Charcoal", + "ImageMagick Chop", + "ImageMagick Clahe", + "ImageMagick Clamp", + "ImageMagick Coalesce", + "ImageMagick Color Decision List", + "ImageMagick Color Matrix", + "ImageMagick Combine", + "ImageMagick Concat", + "ImageMagick Contrast", + "ImageMagick Contrast Stretch", + "ImageMagick Crop", + "ImageMagick Cycle Color Map", + "ImageMagick Decipher", + "ImageMagick Despeckle", + "ImageMagick Distort", + "ImageMagick Edge", + "ImageMagick Emboss", + "ImageMagick Encipher", + "ImageMagick Enhance", + "ImageMagick Equalize", + "ImageMagick Evaluate", + "ImageMagick Extent", + "ImageMagick Flip", + "ImageMagick Flop", + "ImageMagick Forward Fourier Transform", + "ImageMagick Function", + "ImageMagick Gamma", + "ImageMagick Gaussian Blur", + "ImageMagick Hough Lines", + "ImageMagick Implode", + "ImageMagick Kmeans", + "ImageMagick Kuwahara", + "ImageMagick Level", + "ImageMagick Levelize", + "ImageMagick Linear Stretch", + "ImageMagick Liquid Rescale", + "ImageMagick Local Contrast", + "ImageMagick Magnify", + "ImageMagick Mean Shift", + "ImageMagick Merge Layers", + "ImageMagick Mode", + "ImageMagick Modulate", + "ImageMagick Morphology", + "ImageMagick Motion Blur", + "ImageMagick Negate", + "ImageMagick Noise", + "ImageMagick Normalize", + "ImageMagick Oil Paint", + "ImageMagick Ordered Dither", + "ImageMagick Polynomial", + "ImageMagick Posterize", + "ImageMagick Quantize", + "ImageMagick Random Threshold", + "ImageMagick Range Threshold", + "ImageMagick Resample", + "ImageMagick Resize", + "ImageMagick Roll", + "ImageMagick Rotational Blur", + "ImageMagick Sample", + "ImageMagick Scale", + "ImageMagick Selective Blur", + "ImageMagick Sepia Tone", + "ImageMagick Shade", + "ImageMagick Shadow", + "ImageMagick Sharpen", + "ImageMagick Shave", + "ImageMagick Sigmoidal Contrast", + "ImageMagick Sketch", + "ImageMagick Smush", + "ImageMagick Solarize", + "ImageMagick Splice", + "ImageMagick Spread", + "ImageMagick Statistic", + "ImageMagick Swirl", + "ImageMagick Threshold", + "ImageMagick Thumbnail", + "ImageMagick Transform", + "ImageMagick Transform Colorspace", + "ImageMagick Transparentize", + "ImageMagick Transpose", + "ImageMagick Transverse", + "ImageMagick Unsharp Mask", + "ImageMagick Vignette", + "ImageMagick Wave", + "ImageMagick Wavelet Denoise", + "ImageMagick White Balance" + ], + { + "title_aux": "ComfyUI-MagickWand" + } + ], + "https://github.com/Fannovel16/ComfyUI-MotionDiff": [ + [ + "EmptyMotionData", + "ExportSMPLTo3DSoftware", + "Export_SMPLMultipleSubjects_To_3DSoftware", + "Human4D_Img2SMPL", + "Humans4DLoader", + "MotionCLIPTextEncode", + "MotionDataVisualizer", + "MotionDiffLoader", + "MotionDiffSimpleSampler", + "RenderMultipleSubjectsSMPLMesh", + "RenderSMPLMesh", + "Render_OpenPose_From_SMPL_Mesh_Multiple_Subjects", + "SMPLLoader", + "SMPLShapeParameters", + "SaveSMPL", + "SmplifyMotionData", + "SpectreFaceReconLoader", + "SpectreImg2SMPL", + "mgpt_model_loader", + "mgpt_t2m" + ], + { + "title_aux": "ComfyUI MotionDiff" + } + ], + "https://github.com/Fannovel16/ComfyUI-Video-Matting": [ + [ + "BRIAAI Matting", + "Robust Video Matting" + ], + { + "title_aux": "ComfyUI-Video-Matting" + } + ], + "https://github.com/Fannovel16/comfyui_controlnet_aux": [ + [ + "AIO_Preprocessor", + "AnimalPosePreprocessor", + "AnimeFace_SemSegPreprocessor", + "AnimeLineArtPreprocessor", + "AnyLineArtPreprocessor_aux", + "BAE-NormalMapPreprocessor", + "BinaryPreprocessor", + "CannyEdgePreprocessor", + "ColorPreprocessor", + "ControlNetAuxSimpleAddText", + "ControlNetPreprocessorSelector", + "DSINE-NormalMapPreprocessor", + "DWPreprocessor", + "DensePosePreprocessor", + "DepthAnythingPreprocessor", + "DepthAnythingV2Preprocessor", + "DiffusionEdge_Preprocessor", + "ExecuteAllControlNetPreprocessors", + "FacialPartColoringFromPoseKps", + "FakeScribblePreprocessor", + "HEDPreprocessor", + "HintImageEnchance", + "ImageGenResolutionFromImage", + "ImageGenResolutionFromLatent", + "ImageIntensityDetector", + "ImageLuminanceDetector", + "InpaintPreprocessor", + "LeReS-DepthMapPreprocessor", + "LineArtPreprocessor", + "LineartStandardPreprocessor", + "M-LSDPreprocessor", + "Manga2Anime_LineArt_Preprocessor", + "MaskOptFlow", + "MediaPipe-FaceMeshPreprocessor", + "MeshGraphormer+ImpactDetector-DepthMapPreprocessor", + "MeshGraphormer-DepthMapPreprocessor", + "Metric3D-DepthMapPreprocessor", + "Metric3D-NormalMapPreprocessor", + "Metric_DepthAnythingV2Preprocessor", + "MiDaS-DepthMapPreprocessor", + "MiDaS-NormalMapPreprocessor", + "OneFormer-ADE20K-SemSegPreprocessor", + "OneFormer-COCO-SemSegPreprocessor", + "OpenposePreprocessor", + "PiDiNetPreprocessor", + "PixelPerfectResolution", + "PyraCannyPreprocessor", + "RenderAnimalKps", + "RenderPeopleKps", + "SAMPreprocessor", + "SavePoseKpsAsJsonFile", + "ScribblePreprocessor", + "Scribble_PiDiNet_Preprocessor", + "Scribble_XDoG_Preprocessor", + "SemSegPreprocessor", + "ShufflePreprocessor", + "TEEDPreprocessor", + "TTPlanet_TileGF_Preprocessor", + "TTPlanet_TileSimple_Preprocessor", + "TilePreprocessor", + "UniFormer-SemSegPreprocessor", + "Unimatch_OptFlowPreprocessor", + "UpperBodyTrackingFromPoseKps", + "Zoe-DepthMapPreprocessor", + "Zoe_DepthAnythingPreprocessor" + ], + { + "preemptions": [ + "AIO_Preprocessor", + "AnimalPosePreprocessor", + "AnimeFace_SemSegPreprocessor", + "AnimeLineArtPreprocessor", + "BAE-NormalMapPreprocessor", + "BinaryPreprocessor", + "CannyEdgePreprocessor", + "ColorPreprocessor", + "DSINE-NormalMapPreprocessor", + "DWPreprocessor", + "DensePosePreprocessor", + "DepthAnythingPreprocessor", + "DiffusionEdge_Preprocessor", + "FacialPartColoringFromPoseKps", + "FakeScribblePreprocessor", + "HEDPreprocessor", + "HintImageEnchance", + "ImageGenResolutionFromImage", + "ImageGenResolutionFromLatent", + "ImageIntensityDetector", + "ImageLuminanceDetector", + "InpaintPreprocessor", + "LeReS-DepthMapPreprocessor", + "LineArtPreprocessor", + "LineartStandardPreprocessor", + "M-LSDPreprocessor", + "Manga2Anime_LineArt_Preprocessor", + "MaskOptFlow", + "MediaPipe-FaceMeshPreprocessor", + "MeshGraphormer-DepthMapPreprocessor", + "MiDaS-DepthMapPreprocessor", + "MiDaS-NormalMapPreprocessor", + "OneFormer-ADE20K-SemSegPreprocessor", + "OneFormer-COCO-SemSegPreprocessor", + "OpenposePreprocessor", + "PiDiNetPreprocessor", + "PixelPerfectResolution", + "SAMPreprocessor", + "SavePoseKpsAsJsonFile", + "ScribblePreprocessor", + "Scribble_XDoG_Preprocessor", + "SemSegPreprocessor", + "ShufflePreprocessor", + "TEEDPreprocessor", + "TilePreprocessor", + "UniFormer-SemSegPreprocessor", + "Unimatch_OptFlowPreprocessor", + "Zoe-DepthMapPreprocessor", + "Zoe_DepthAnythingPreprocessor" + ], + "title_aux": "ComfyUI's ControlNet Auxiliary Preprocessors" + } + ], + "https://github.com/Fantaxico/ComfyUI-GCP-Storage": [ + [ + "GCPStorageNode" + ], + { + "title_aux": "ComfyUI-GCP-Storage" + } + ], + "https://github.com/FaraamFide/ComfyUI-ParamNodes": [ + [ + "HelperModelSwitch", + "ParamBoolean", + "ParamFloat", + "ParamImage", + "ParamInt", + "ParamString", + "ParamUniversal" + ], + { + "title_aux": "ComfyUI-ParamNodes" + } + ], + "https://github.com/Feidorian/feidorian-ComfyNodes": [ + [], + { + "nodename_pattern": "^Feidorian_", + "title_aux": "feidorian-ComfyNodes" + } + ], + "https://github.com/FewBox/fewbox-outfit-comfyui": [ + [ + "FewBoxInContextLora", + "FewBoxLab", + "FewBoxSaveImage", + "FewBoxWatermark", + "FewBoxWebDAV" + ], + { + "title_aux": "fewbox-outfit-comfyui" + } + ], + "https://github.com/Fictiverse/ComfyUI_Fictiverse": [ + [ + "Add Margin With Color", + "Essential Params", + "If Image Valid", + "Image Params", + "Is Image Valid ?", + "None if same Image", + "Resize To Megapixels", + "Video Params" + ], + { + "title_aux": "ComfyUI Fictiverse Nodes" + } + ], + "https://github.com/Fihade/IC-Light-ComfyUI-Node": [ + [ + "LoadICLightUnetDiffusers", + "diffusers_model_loader", + "iclight_diffusers_sampler" + ], + { + "title_aux": "IC-Light-ComfyUI-Node" + } + ], + "https://github.com/FinetunersAI/ComfyUI_Finetuners_Suite": [ + [ + "AutoImageResize", + "GroupLink", + "ModelListNode", + "VariablesInjector", + "VariablesLogicNode" + ], + { + "title_aux": "ComfyUI_Finetuners_Suite" + } + ], + "https://github.com/FizzleDorf/ComfyUI-AIT": [ + [ + "AIT_Unet_Loader", + "AIT_VAE_Encode_Loader" + ], + { + "title_aux": "ComfyUI-AIT" + } + ], + "https://github.com/FizzleDorf/ComfyUI_FizzNodes": [ + [ + "AbsCosWave", + "AbsSinWave", + "BatchGLIGENSchedule", + "BatchPromptSchedule", + "BatchPromptScheduleEncodeSDXL", + "BatchPromptScheduleLatentInput", + "BatchPromptScheduleNodeFlowEnd", + "BatchPromptScheduleSDXLLatentInput", + "BatchStringSchedule", + "BatchValueSchedule", + "BatchValueScheduleLatentInput", + "CalculateFrameOffset", + "ConcatStringSingle", + "CosWave", + "FizzFrame", + "FizzFrameConcatenate", + "ImagesFromBatchSchedule", + "Init FizzFrame", + "InvCosWave", + "InvSinWave", + "Lerp", + "PromptSchedule", + "PromptScheduleEncodeSDXL", + "PromptScheduleNodeFlow", + "PromptScheduleNodeFlowEnd", + "SawtoothWave", + "SinWave", + "SquareWave", + "StringConcatenate", + "StringSchedule", + "TriangleWave", + "ValueSchedule", + "convertKeyframeKeysToBatchKeys" + ], + { + "title_aux": "FizzNodes" + } + ], + "https://github.com/Flow-two/ComfyUI-WanStartEndFramesNative": [ + [ + "GetImagesFromBatchRanged_F2", + "WanImageToVideo_F2", + "WanSkipEndFrameImages_F2" + ], + { + "title_aux": "ComfyUI-WanStartEndFramesNative" + } + ], + "https://github.com/FlyingFireCo/tiled_ksampler": [ + [ + "Asymmetric Tiled KSampler", + "Circular VAEDecode", + "Tiled KSampler" + ], + { + "title_aux": "tiled_ksampler" + } + ], + "https://github.com/ForeignGods/ComfyUI-Mana-Nodes": [ + [ + "Canvas Properties", + "Combine Video", + "Font Properties", + "Generate Audio", + "Preset Color Animations", + "Save/Preview Text", + "Scheduled Values", + "Speech Recognition", + "Split Video", + "Text to Image Generator" + ], + { + "title_aux": "ComfyUI-Mana-Nodes" + } + ], + "https://github.com/FortunaCournot/comfyui_stereoscopic": [ + [ + "GetResolutionForDepth", + "ImageSBSConverter" + ], + { + "title_aux": "Stereoscopic" + } + ], + "https://github.com/Franck-Demongin/NX_HuggingFace_Flux": [ + [ + "HFFlux" + ], + { + "title_aux": "NX_HuggingFace_Flux" + } + ], + "https://github.com/Franck-Demongin/NX_PromptStyler": [ + [ + "NX_PromptStyler" + ], + { + "title_aux": "NX_PromptStyler" + } + ], + "https://github.com/Franck-Demongin/NX_Translator": [ + [ + "Nx_Translator" + ], + { + "title_aux": "NX_Translator" + } + ], + "https://github.com/Franklyc/comfyui-lora-adain-patcher-node": [ + [ + "LoraAdaLNPatcher" + ], + { + "title_aux": "ComfyUI LoRA adaLN Patcher Node" + } + ], + "https://github.com/FunnyFinger/ComfyUi-RadarWeightNode": [ + [ + "RadarWeightsNode" + ], + { + "title_aux": "Radar Weights Node" + } + ], + "https://github.com/FuouM/ComfyUI-EbSynth": [ + [ + "ES_Guides7", + "ES_Translate", + "ES_VideoTransfer", + "ES_VideoTransferExtra" + ], + { + "author": "Fuou Marinas", + "description": "Run EbSynth in ComfyUI.", + "nickname": "EbSynth", + "title": "ComfyUI-EbSynth", + "title_aux": "ComfyUI-EbSynth" + } + ], + "https://github.com/FuouM/ComfyUI-FirstOrderMM": [ + [ + "Articulate_Runner", + "FOMM_Partswap", + "FOMM_Runner", + "FOMM_Seg10Chooser", + "FOMM_Seg15Chooser", + "FOMM_Seg5Chooser", + "FSRT_Runner", + "MRFA_Runner", + "Spline_Runner" + ], + { + "author": "Fuou Marinas", + "description": "ComfyUI-native nodes to run First Order Motion Model for Image Animation and its non-diffusion-based successors.", + "nickname": "FOMM", + "title": "ComfyUI-FirstOrderMM", + "title_aux": "ComfyUI-FirstOrderMM" + } + ], + "https://github.com/FuouM/ComfyUI-MatAnyone": [ + [ + "MatAnyone", + "SolidColorBatched" + ], + { + "author": "Fuou Marinas", + "description": "A collection of nodes.", + "nickname": "FM_nodes", + "title": "FM Nodes", + "title_aux": "ComfyUI-MatAnyone" + } + ], + "https://github.com/FuouM/ComfyUI-StyleTransferPlus": [ + [ + "AESFA", + "AesFAStyleBlend", + "AesPA", + "CAST", + "CoralColorTransfer", + "EFDM", + "MicroAST", + "NeuralNeighbor", + "TSSAT", + "UniST", + "UniST_Video" + ], + { + "author": "ZJU", + "description": "A collection of style transfer nodes.", + "nickname": "StyleTransferPlus", + "title": "ComfyUI-StyleTransferPlus", + "title_aux": "ComfyUI-StyleTransferPlus" + } + ], + "https://github.com/FuouM/FM_nodes": [ + [ + "CoLIE_LowLight_Enhance", + "ConvIR_DeHaze", + "ConvIR_DeRain", + "ConvIR_DeSnow", + "ConvIR_DefocusDeblur", + "ConvIR_MotionDeBlur", + "ProPIH_Harmonizer", + "RealViFormerSR", + "StabStitch", + "StabStitch_Crop_Resize", + "StabStitch_Stabilize", + "WFEN" + ], + { + "author": "Fuou Marinas", + "description": "A collection of nodes.", + "nickname": "FM_nodes", + "title": "FM Nodes", + "title_aux": "FM_nodes" + } + ], + "https://github.com/Fuwuffyi/ComfyUI-VisualArea-Nodes": [ + [ + "VisualAreaPrompt", + "VisualAreaPromptAdvanced" + ], + { + "author": "Fuwuffy", + "description": "This is a collection of nodes created to aid when managing area conditionings.", + "nickname": "comfy-visual-area", + "title": "ComfyUI Visual Area Nodes", + "title_aux": "ComfyUI-VisualArea-Nodes" + } + ], + "https://github.com/G-370/ComfyUI-SD3-Powerlab": [ + [ + "G370SD3PowerLab_AttentionToImage", + "G370SD3PowerLab_ImageIntoAttention", + "G370SD3PowerLab_ImageIntoLayer", + "G370SD3PowerLab_LayerToImage", + "G370SD3PowerLab_RenderAttention" + ], + { + "title_aux": "ComfyUI-SD3-Powerlab" + } + ], + "https://github.com/GACLove/ComfyUI-Lightx2vWrapper": [ + [ + "LightX2VConfigCombiner", + "LightX2VInferenceConfig", + "LightX2VLightweightVAE", + "LightX2VLoRALoader", + "LightX2VMemoryOptimization", + "LightX2VModularInference", + "LightX2VQuantization", + "LightX2VTeaCache" + ], + { + "title_aux": "ComfyUI-Lightx2vWrapper" + } + ], + "https://github.com/GACLove/ComfyUI-VFI": [ + [ + "RIFEInterpolation" + ], + { + "title_aux": "ComfyUI-VFI" + } + ], + "https://github.com/GHOSTLXH/ComfyUI-Counternodes": [ + [ + "AlternatingOutput", + "AlternatingOutputB", + "ImageCounter", + "IntervalCounter", + "IntervalCounterB", + "LoadPromptFromTXT" + ], + { + "title_aux": "ComfyUI-Counternodes" + } + ], + "https://github.com/GTSuya-Studio/ComfyUI-Gtsuya-Nodes": [ + [ + "Danbooru (ID)", + "Danbooru (Random)", + "Random File From Path", + "Replace Strings", + "Simple Wildcards", + "Simple Wildcards (Dir.)", + "Wildcards Nodes" + ], + { + "title_aux": "ComfyUI-GTSuya-Nodes" + } + ], + "https://github.com/GadzoinksOfficial/comfyui_gprompts": [ + [ + "GPrompts" + ], + { + "author": "gadzoinksofficial", + "description": "Another dynamic prompt node, designed to be easy to use and support wildcards", + "nickname": "Gprompts", + "title": "Gprompts", + "title_aux": "Gprompts" + } + ], + "https://github.com/GadzoinksOfficial/gadzoinks_ComfyUI": [ + [ + "Gadzoinks" + ], + { + "author": "gadzoinksofficial", + "description": "Custom node for integrating with gadzoinks iPhone app", + "nickname": "Gadzoinks", + "title": "Gadzoinks", + "title_aux": "Gadzoinks" + } + ], + "https://github.com/GamingDaveUk/daves_nodes": [ + [ + "davesTextToList" + ], + { + "title_aux": "Daves Nodes" + } + ], + "https://github.com/Gary-yeh/ComfyUI-WebPrompter": [ + [ + "ContentFetcher (WebPrompter)", + "LLMNewsScriptGenerator (WebPrompter)" + ], + { + "title_aux": "ComfyUI-WebPrompter" + } + ], + "https://github.com/Gary-yeh/comfyui-super-captioner": [ + [ + "SuperCaptioner" + ], + { + "title_aux": "comfyui-super-captioner" + } + ], + "https://github.com/GavChap/ComfyUI-SD3LatentSelectRes": [ + [ + "SD3LatentSelectRes" + ], + { + "title_aux": "ComfyUI-SD3LatentSelectRes" + } + ], + "https://github.com/GeekyGhost/ComfyUI-Geeky-Kokoro-TTS": [ + [ + "GeekyKokoroAdvancedVoice", + "GeekyKokoroTTS" + ], + { + "title_aux": "ComfyUI-Geeky-Kokoro-TTS" + } + ], + "https://github.com/GeekyGhost/ComfyUI-GeekyRemB": [ + [ + "GeekyRemB" + ], + { + "title_aux": "ComfyUI-GeekyRemB" + } + ], + "https://github.com/GentlemanHu/ComfyUI-SunoAI": [ + [ + "GentlemanHu_SunoAI", + "GentlemanHu_SunoAI_NotSafe" + ], + { + "title_aux": "ComfyUI Suno API" + } + ], + "https://github.com/GeraldWie/ComfyUI-I2I-slim": [ + [ + "Color Transfer Slim", + "Combine and Paste Slim", + "Inpaint Segments Slim", + "Mask Ops Slim" + ], + { + "author": "GeraldWie", + "title": "ComfyI2I-lite", + "title_aux": "ComfyUI-I2I-slim" + } + ], + "https://github.com/GiusTex/ComfyUI-DiffusersImageOutpaint": [ + [ + "DiffusersImageOutpaint", + "EncodeDiffusersOutpaintPrompt", + "LoadDiffuserControlnet", + "LoadDiffuserModel", + "PadImageForDiffusersOutpaint" + ], + { + "title_aux": "ComfyUI-DiffusersImageOutpaint" + } + ], + "https://github.com/Goktug/comfyui-saveimage-plus": [ + [ + "SaveImagePlus" + ], + { + "title_aux": "Save Image Plus for ComfyUI" + } + ], + "https://github.com/Goshe-nite/comfyui-gps-supplements": [ + [ + "KSampler to Image Saver", + "Lora Prompt Concatenation", + "Lora to String", + "Model to String", + "gpsdebugger" + ], + { + "title_aux": "GPS' Supplements for ComfyUI" + } + ], + "https://github.com/Gourieff/ComfyUI-ReActor": [ + [ + "ImageRGBA2RGB", + "ReActorBuildFaceModel", + "ReActorFaceBoost", + "ReActorFaceSwap", + "ReActorFaceSwapOpt", + "ReActorImageDublicator", + "ReActorLoadFaceModel", + "ReActorMakeFaceModelBatch", + "ReActorMaskHelper", + "ReActorOptions", + "ReActorRestoreFace", + "ReActorSaveFaceModel", + "ReActorSetWeight", + "ReActorUnload" + ], + { + "title_aux": "comfyui-reactor-node" + } + ], + "https://github.com/GraftingRayman/ComfyUI-PuLID-Flux-GR": [ + [ + "GRApplyPulidFlux", + "GRPulidFluxEvaClipLoader", + "GRPulidFluxInsightFaceLoader", + "GRPulidFluxModelLoader" + ], + { + "title_aux": "ComfyUI-PuLID-Flux-GR" + } + ], + "https://github.com/GraftingRayman/ComfyUI_GraftingRayman": [ + [ + "GR BLIP 2 Caption Generator", + "GR BLIP 2 Text Expander", + "GR Background Remover REMBG", + "GR Checkered Board", + "GR Counter", + "GR Flip Tile Random Inverted", + "GR Flip Tile Random Red Ring", + "GR Florence 2 Caption Generator", + "GR INT Incremetor", + "GR Image Details Displayer", + "GR Image Details Saver", + "GR Image Multiplier", + "GR Image Paste", + "GR Image Paste With Mask", + "GR Image Resize", + "GR Image Resize Methods", + "GR Image Size", + "GR Image/Depth Mask", + "GR Lora Randomizer", + "GR Mask", + "GR Mask Create", + "GR Mask Create Random", + "GR Mask Create Random Multi", + "GR Mask Resize", + "GR Multi Mask Create", + "GR Onomatopoeia", + "GR Pan Or Zoom", + "GR Prompt Generator", + "GR Prompt Generator Extended", + "GR Prompt HUB", + "GR Prompt Selector", + "GR Prompt Selector Multi", + "GR Prompty", + "GR Scroller", + "GR Sigmas", + "GR Stack Image", + "GR Text Overlay", + "GR Tile and Border Image", + "GR Tile and Border Image Random Flip" + ], + { + "title_aux": "GraftingRayman" + } + ], + "https://github.com/GraftingRayman/ComfyUI_QueueTube": [ + [ + "GR QueueTube" + ], + { + "title_aux": "ComfyUI QueueTube" + } + ], + "https://github.com/GrailGreg/images_base64": [ + [ + "SaveImage64", + "ShowText64" + ], + { + "title_aux": "Image Saving and Base64 Encoding Script" + } + ], + "https://github.com/GreenLandisaLie/AuraSR-ComfyUI": [ + [ + "AuraSR.AuraSRUpscaler" + ], + { + "title_aux": "AuraSR-ComfyUI" + } + ], + "https://github.com/GrenKain/PixelArt-Processing-Nodes-for-ComfyUI": [ + [ + "PixelArtDownscaleNode", + "PixelArtNode" + ], + { + "title_aux": "PixelArt Processing Nodes" + } + ], + "https://github.com/GroxicTinch/EasyUI-ComfyUI": [ + [ + "UINode" + ], + { + "title_aux": "EasyUI" + } + ], + "https://github.com/GrvBdgr/comfyui-negativewildcardsprocessor": [ + [ + "custom_token_processor", + "neg_wildcard_processor" + ], + { + "title_aux": "Negative Wildcard Processor Node for ComfyUI" + } + ], + "https://github.com/Gue-e/ComfyUI-PanoCard": [ + [ + "PanoCardViewer", + "PanoCondAllBatch", + "PanoCondFaceBatch", + "PanoCondFaceClamp", + "PanoCondFaceDetailerHook", + "PanoCondFaceUnPack", + "PanoImage2FaceSplit", + "PanoImageAdjust", + "PanoImageEqu2Equ", + "PanoImageEqu2Face", + "PanoImageEqu2Pic", + "PanoImageFace2Equ", + "PanoImageFaceClamp", + "PanoImageFaceToLong", + "PanoImageHeightPad", + "PanoImagePad", + "PanoImagePic2Equ", + "PanoImageRoll", + "PanoImageUnPack", + "PanoImageWidthPad", + "PanoLongMaskSplit", + "PanoMaskUnPack", + "PanoPipe", + "PanoPromptSplit", + "PanoRegionalPrompt" + ], + { + "title_aux": "ComfyUI-PanoCard" + } + ], + "https://github.com/Guillaume-Fgt/ComfyUI_StableCascadeLatentRatio": [ + [ + "StableCascadeLatentRatio" + ], + { + "title_aux": "ComfyUI_StableCascadeLatentRatio" + } + ], + "https://github.com/HAL41/ComfyUI-aichemy-nodes": [ + [ + "aichemyYOLOv8Segmentation" + ], + { + "title_aux": "ComfyUI aichemy nodes" + } + ], + "https://github.com/HECer/ComfyUI-FilePathCreator": [ + [ + "FilePathCreator", + "FilePathExtractor" + ], + { + "title_aux": "ComfyUI-FilePathCreator" + } + ], + "https://github.com/HJH-AILab/ComfyUI_CosyVoice2": [ + [ + "CosyVoiceModel", + "CosyVoiceNode", + "HJHCosyVoiceSaveAudio" + ], + { + "title_aux": "ComfyUI_CosyVoice2" + } + ], + "https://github.com/HJH-AILab/ComfyUI_Facefusion": [ + [ + "FacefusionFaceEnhancerProcessor", + "FacefusionFaceSwapperProcessor", + "FacefusionFrameEnhancerProcessor", + "FacefusionLipSyncerProcessor", + "FacefusionOptionsNode", + "FacefusionProcesserOptionsNode", + "FacefusionProcessingNode" + ], + { + "title_aux": "ComfyUI_Facefusion" + } + ], + "https://github.com/HJH-AILab/ComfyUI_StableAnimator": [ + [ + "StableAnimatorDWPoseDetectorAlignedModels", + "StableAnimatorLoadFramesFromFolderNode", + "StableAnimatorModels", + "StableAnimatorNode", + "StableAnimatorSkeletonNode" + ], + { + "title_aux": "ComfyUI_StableAnimator" + } + ], + "https://github.com/HM-RunningHub/ComfyUI_RH_APICall": [ + [ + "RH_AudioUploader", + "RH_BatchImages", + "RH_ExecuteNode", + "RH_ExtractImage", + "RH_ImageUploaderNode", + "RH_NodeInfoListNode", + "RH_SettingsNode", + "RH_Utils", + "RH_VideoUploader" + ], + { + "title_aux": "ComfyUI_RH_APICall" + } + ], + "https://github.com/HM-RunningHub/ComfyUI_RH_FramePack": [ + [ + "RunningHub_FramePack", + "RunningHub_FramePack_F1" + ], + { + "title_aux": "ComfyUI_RH_FramePack" + } + ], + "https://github.com/HM-RunningHub/ComfyUI_RH_OminiControl": [ + [ + "RunningHub_Omini_Fill", + "RunningHub_Omini_Spatial", + "RunningHub_Omini_Subject" + ], + { + "title_aux": "ComfyUI_RH_OminiControl" + } + ], + "https://github.com/HM-RunningHub/ComfyUI_RH_SeedXPro": [ + [ + "RunningHub SeedXPro Translator" + ], + { + "title_aux": "ComfyUI SeedXPro Translation Node" + } + ], + "https://github.com/HM-RunningHub/ComfyUI_RH_Step1XEdit": [ + [ + "RunningHub_Step1XEdit" + ], + { + "title_aux": "ComfyUI_RH_Step1XEdit" + } + ], + "https://github.com/HM-RunningHub/ComfyUI_RH_UNO": [ + [ + "RunningHub_UNO_Loadmodel", + "RunningHub_UNO_Sampler" + ], + { + "title_aux": "ComfyUI_RH_UNO" + } + ], + "https://github.com/HMG-Fiverr/ComfyUI-RandomNumberButton": [ + [ + "RandomNumberButton" + ], + { + "title_aux": "Random Number Button" + } + ], + "https://github.com/Haiper-ai/ComfyUI-HaiperAI-API": [ + [ + "HaiperImage2Video", + "HaiperKeyframeConditioning", + "HaiperText2Image", + "HaiperText2Video" + ], + { + "title_aux": "ComfyUI-HaiperAI-API" + } + ], + "https://github.com/HannibalP/comfyui-HannibalPack": [ + [ + "HannibalLoraLoader" + ], + { + "title_aux": "comfyui-HannibalPack" + } + ], + "https://github.com/Haoming02/comfyui-diffusion-cg": [ + [ + "Normalization", + "Recenter", + "Recenter XL" + ], + { + "title_aux": "Diffusion CG" + } + ], + "https://github.com/Haoming02/comfyui-floodgate": [ + [ + "FloodGate" + ], + { + "title_aux": "Floodgate" + } + ], + "https://github.com/Haoming02/comfyui-resharpen": [ + [ + "Resharpen" + ], + { + "title_aux": "ComfyUI ReSharpen" + } + ], + "https://github.com/HappyXY/ComfyUI-AmazonBedrock": [ + [ + "Amazon Bedrock - Nova Canvas Background Prompt Replace", + "Amazon Bedrock - Nova Canvas Generate Image", + "Amazon Bedrock - Nova Canvas Generate Variations", + "Amazon Bedrock - Nova Reel Video", + "Bedrock - Claude", + "Bedrock - Claude Multimodal", + "Bedrock - Nova", + "Bedrock - SDXL" + ], + { + "title_aux": "ComfyUI-AmazonBedrock" + } + ], + "https://github.com/HavocsCall/comfyui_HavocsCall_Custom_Nodes": [ + [ + "Clip Switch", + "Combine String", + "Conditioning Switch", + "Float Selector", + "Float to Integer", + "Float to String", + "Image Switch", + "Integer Selector", + "Integer to Float", + "Integer to String", + "Latent Switch", + "Load Image", + "Logic Compare", + "Math Operation", + "Model Switch", + "Prompt Combiner", + "Prompt Styler", + "Sampler Config", + "Save Image", + "String Switch", + "String to Float", + "String to Integer", + "Text Box", + "VAE Switch" + ], + { + "title_aux": "HavocsCall's Custom ComfyUI Nodes" + } + ], + "https://github.com/HaydenReeve/ComfyUI-Better-Strings": [ + [ + "BetterString" + ], + { + "title_aux": "ComfyUI Better Strings" + } + ], + "https://github.com/Hazukiaoi/ComfyUI-LM_Studio_Tools": [ + [ + "LMS_APIConfig", + "LMS_GetAssistantMessage", + "LMS_Request", + "LMS_SelectModel", + "LMS_SystemPrompt", + "LMS_UnloadModel", + "LMS_UserPrompt" + ], + { + "title_aux": "LM Studio Tools for ComfyUI" + } + ], + "https://github.com/HeadshotPro/ComfyUI-HeadshotPro": [ + [ + "[HSP] Download Dreambooth Checkpoint", + "[HSP] Download Flux Lora", + "[HSP] Get Random Value From List", + "[HSP] Load Canny Pose Face", + "[HSP] Transparent to White Background" + ], + { + "title_aux": "ComfyUI-HeadshotPro" + } + ], + "https://github.com/HebelHuber/comfyui-enhanced-save-node": [ + [ + "EnhancedSaveNode" + ], + { + "title_aux": "comfyui-enhanced-save-node" + } + ], + "https://github.com/HellerCommaA/ComfyUI-VideoResolutions": [ + [ + "HunyuanResolutions" + ], + { + "title_aux": "Hunyuan Video Resolutions" + } + ], + "https://github.com/Hellfiredragon/comfyui-image-manipulation": [ + [ + "AlphaApplyMaskToImage", + "CreateMaskFromColorsNode" + ], + { + "title_aux": "comfyui-image-manipulation" + } + ], + "https://github.com/HelloVision/ComfyUI_HelloMeme": [ + [ + "GetExpression", + "GetExpression2", + "GetFaceLandmarks", + "GetHeadPose", + "HMFaceToolkitsLoader", + "HMImagePipelineLoader", + "HMPipelineImage", + "HMPipelineVideo", + "HMVideoPipelineLoader" + ], + { + "title_aux": "ComfyUI_HelloMeme" + } + ], + "https://github.com/Hellrunner2k/ComfyUI-HellrunnersMagicalNodes": [ + [ + "AdjustMojo", + "BufferedEncoder", + "LoRABox", + "LoadMaskMap", + "MagicalSaveNode", + "MaskMapPrompt", + "MaskMapPromptMix", + "MojoLoader", + "MojoMaker", + "SaveMojo", + "TEAce", + "ThermalLatenator" + ], + { + "title_aux": "Hellrunner's Magical Nodes" + } + ], + "https://github.com/Hiero207/ComfyUI-Hiero-Nodes": [ + [ + "Load Prompt Travel file", + "Post to Discord w/ Webhook", + "Save Prompt Travel file" + ], + { + "author": "Hiero", + "description": "Just some nodes that I wanted/needed, so I made them.", + "nickname": "HNodes", + "title": "Hiero-Nodes", + "title_aux": "Hiero-Nodes" + } + ], + "https://github.com/HighDoping/ComfyUI_ASSSSA": [ + [ + "ASSSubtitleReader", + "ASSSubtitleSave", + "FFMpegSettings", + "MultilineTextInput", + "SubtitleEmbedding", + "SubtitleExtraction", + "VideoTranscoding" + ], + { + "title_aux": "ComfyUI-ASSSSA" + } + ], + "https://github.com/Holasyb918/Ghost2_Comfyui": [ + [ + "AlignPipeline", + "BlenderPipeline", + "FaceAnalysisePipeline", + "FaceParsingPipeline", + "LoadAlignerModel", + "LoadBlenderModel", + "LoadFaceAnalysisModel", + "LoadFaceParsingModel", + "LoadInpainterModel", + "LoadStyleMatteModel" + ], + { + "title_aux": "Ghost2_Comfyui" + } + ], + "https://github.com/Hopping-Mad-Games/ComfyUI_LiteLLM": [ + [ + "AgentMemoryProvider", + "AgentNode", + "BasicRecursionFilterNode", + "DocumentChunkRecursionFilterNode", + "DocumentProcessor", + "LinuxMemoryDirectory", + "QueryNode" + ], + { + "description": "Nodes for interfacing with LiteLLM", + "nickname": "Tasha", + "title": "ComfyUI_LiteLLM", + "title_aux": "ComfyUI_LiteLLM" + } + ], + "https://github.com/Hullabalo/ComfyUI-Loop": [ + [ + "ImageCropLoop", + "ImageCutLoop", + "ImagePasteLoop", + "LoadImageSimple", + "LoopImageSimple", + "SaveImageSimple" + ], + { + "title_aux": "ComfyUI-Loop" + } + ], + "https://github.com/IDGallagher/ComfyUI-IG-Motion-I2V": [ + [ + "MI2V Flow Animator", + "MI2V Flow Predictor", + "MI2V PauseNode", + "MotionPainter" + ], + { + "author": "IDGallagher", + "description": "Custom nodes to aid in the exploration of Latent Space", + "nickname": "IG Interpolation Nodes", + "title": "IG Interpolation Nodes", + "title_aux": "ComfyUI-IG-Motion-I2V" + } + ], + "https://github.com/IDGallagher/ComfyUI-IG-Nodes": [ + [ + "IG Analyze SSIM", + "IG Cross Fade Images", + "IG Explorer", + "IG Float", + "IG Float List", + "IG Folder", + "IG Image Crop", + "IG Int", + "IG Interpolate", + "IG Load Image", + "IG Load Images", + "IG MotionPredictor", + "IG Multiply", + "IG Path Join", + "IG PointCloud From Cylindrical", + "IG PointCloud From Depth", + "IG Save PLY PointCloud", + "IG Simple Translate Stitcher", + "IG Stitch Depth Tiles", + "IG Stitch Images CV2", + "IG String", + "IG String List", + "IG Tile Image", + "IG ZFill", + "SM Video Base", + "SM Video Base Control" + ], + { + "author": "IDGallagher", + "description": "Custom nodes to aid in the exploration of Latent Space", + "nickname": "IG Interpolation Nodes", + "title": "IG Interpolation Nodes", + "title_aux": "IG Interpolation Nodes" + } + ], + "https://github.com/IDGallagher/MotionVideoSearch": [ + [ + "IG Motion Video Frame", + "IG Motion Video Search" + ], + { + "author": "IDGallagher", + "description": "Search an index of videos by motion image", + "nickname": "IG Motion Video Search", + "title": "IG Motion Video Search", + "title_aux": "IG-Motion-Search" + } + ], + "https://github.com/IIEleven11/ComfyUI-FairyTaler": [ + [ + "FairyTalerStoryboard", + "SceneParser", + "SceneToConditioning", + "StoryboardCompositor", + "ThreeSceneGenerator" + ], + { + "author": "Eleven", + "description": "Turn your AI roleplay into AI generated scenes from every response. Visualize what you read!", + "nickname": "ComfyUI FairyTaler", + "title": "ComfyUI FairyTaler Storyboard Nodes", + "title_aux": "ComfyUI-FairyTaler" + } + ], + "https://github.com/IIs-fanta/ComfyUI-FANTA-GameBox": [ + [ + "BilliardsGameNode", + "BrickBreakerNode", + "FlappyBirdNode", + "SnakeGameNode" + ], + { + "title_aux": "ComfyUI-FANTA-GameBox" + } + ], + "https://github.com/INuBq8/ComfyUI-NotificationBridge": [ + [ + "DiscordNotifyNode", + "WhatsAppNotifyNodeTwilio" + ], + { + "title_aux": "Notification Bridge" + } + ], + "https://github.com/ITurchenko/ComfyUI-SizeFromArray": [ + [ + "SizeFromArray" + ], + { + "title_aux": "ComfyUI-SizeFromArray" + } + ], + "https://github.com/IamCreateAI/Ruyi-Models": [ + [ + "Ruyi_EnhanceAVideo", + "Ruyi_I2VSampler", + "Ruyi_LoadLora", + "Ruyi_LoadModel", + "Ruyi_TeaCache" + ], + { + "title_aux": "ComfyUI-Ruyi" + } + ], + "https://github.com/IcelandicCenterArtificialIntelligence/ComfyUI-SamplerSchedulerMetricsTester": [ + [ + "SamplerSchedulerMetricsTester" + ], + { + "title_aux": "Sampler Scheduler Metrics Tester for ComfyUI" + } + ], + "https://github.com/Icyman86/ComfyUI_AnimeCharacterSelect": [ + [ + "ActionPromptNode", + "CharacterPromptNode", + "CombinePromptStringsNode", + "EnhancedCharacterPromptNode", + "MinimalCharacterActionPrompt" + ], + { + "title_aux": "ComfyUI_AnimeCharacterSelect" + } + ], + "https://github.com/IgalOgonov/ComfyUI_Simple_String_Repository": [ + [ + "SimpleStringRepository", + "SimpleStringRepositoryCompact", + "SimpleStringRepositoryLarge", + "SimpleStringRepositoryLargeCompact", + "SimpleStringRepositorySmall", + "SimpleStringRepositorySmallCompact" + ], + { + "title_aux": "Simple String Repository" + } + ], + "https://github.com/ImagineerNL/ComfyUI-IMGNR-Utils": [ + [ + "CatchEditTextNode", + "PreviewImageBase64Node" + ], + { + "title_aux": "ComfyUI-IMGNR-Utils" + } + ], + "https://github.com/ImagineerNL/ComfyUI-ToSVG-Potracer": [ + [ + "PotracerVectorize", + "SaveAsSVG Potracer (Temporary Fix)" + ], + { + "title_aux": "ComfyUI-ToSVG-Potracer" + } + ], + "https://github.com/Immac/ComfyUI-CoreVideoMocks": [ + [ + "CoreVideoMocks:AV1Codec", + "CoreVideoMocks:BatchAudioStreams", + "CoreVideoMocks:BatchSubtitlesStreams", + "CoreVideoMocks:BatchVideoStreams", + "CoreVideoMocks:CodecFromVideoStream", + "CoreVideoMocks:CombineVideo", + "CoreVideoMocks:DecodeVideoStream", + "CoreVideoMocks:EncodeVideoStream", + "CoreVideoMocks:GetAudioStream", + "CoreVideoMocks:GetSubtitleStream", + "CoreVideoMocks:GetVideoStream", + "CoreVideoMocks:LoadVideo", + "CoreVideoMocks:PreviewVideo", + "CoreVideoMocks:SaveVideo", + "CoreVideoMocks:SplitVideo", + "CoreVideoMocks:VP9Codec", + "CoreVideoMocks:VideoStreamData" + ], + { + "title_aux": "ComfyUI Core Video Nodes" + } + ], + "https://github.com/ImmortalPie/ComfyUI-PonySwitch": [ + [ + "PonySwitch" + ], + { + "title_aux": "PonySwitch Node" + } + ], + "https://github.com/InstantStudioAI/ComfyUI-InstantStudio": [ + [ + "HuggingFace Classify", + "Moondream", + "UploadImagesToInstantStudio" + ], + { + "title_aux": "ComfyUI-InstantStudio" + } + ], + "https://github.com/Intersection98/ComfyUI_MX_post_processing-nodes": [ + [ + "MX_AlphaBlend", + "MX_Blend", + "MX_Blur", + "MX_Canny", + "MX_ChromaticAberration", + "MX_ColorCorrect", + "MX_ColorMatch", + "MX_ColorTint", + "MX_Glow", + "MX_HSVThresholdMask", + "MX_KuwaharaBlur(Cartoon)", + "MX_LUT", + "MX_LensBokeh", + "MX_LensOpticAxis", + "MX_LensZoomBurst", + "MX_Mosaic", + "MX_Noise", + "MX_Posterize", + "MX_SineWave", + "MX_Solarize", + "MX_Vignette" + ], + { + "title_aux": "ComfyUI-MX-post-processing-nodes" + } + ], + "https://github.com/Inzaniak/comfyui-ranbooru": [ + [ + "LockSeed", + "PromptBackground", + "PromptLimit", + "PromptMix", + "PromptRandomWeight", + "PromptRemove", + "Ranbooru", + "RanbooruURL", + "RandomPicturePath", + "TimestampFileName" + ], + { + "title_aux": "Ranbooru for ComfyUI" + } + ], + "https://github.com/Irsalistic/comfyui-dam-object-extractor": [ + [ + "DAMObjectNameNode", + "DAMVisualizeNode" + ], + { + "title_aux": "ComfyUI DAM Object Extractor" + } + ], + "https://github.com/IsItDanOrAi/ComfyUI-Stereopsis": [ + [ + "Dan_FrameDelay", + "Dan_Stereopsis" + ], + { + "title_aux": "ComfyUI-Stereopsis" + } + ], + "https://github.com/IsItDanOrAi/ComfyUI-exLoadout": [ + [ + "dropdowns", + "exCheckpointLoader", + "exLoadoutCheckpointLoader", + "exLoadoutEditCell", + "exLoadoutReadColumn", + "exLoadoutSeg", + "exLoadoutSeg2", + "exLoadoutSelector", + "exSeg", + "exSeg2" + ], + { + "title_aux": "exLoadout: Excel-Based Model & Settings Loader" + } + ], + "https://github.com/Isi-dev/ComfyUI-Animation_Nodes_and_Workflows": [ + [ + "CLIPTextEncodeStyles", + "JoinVideos", + "MakeDrivingVideoForLivePortrait", + "MakePortraitWalk", + "MoveInOrOut", + "MoveLeftOrRight", + "MoveUpOrDown", + "Replace_Img_or_Vid_Bg_Assistant" + ], + { + "title_aux": "ComfyUI-Animation_Nodes_and_Workflows" + } + ], + "https://github.com/Isi-dev/ComfyUI-Img2DrawingAssistants": [ + [ + "LineArt_Assistant", + "LineArt_Assistant_2", + "Sketch_Assistant", + "Sketch_Assistant_grayScale" + ], + { + "title_aux": "ComfyUI-Img2DrawingAssistants" + } + ], + "https://github.com/Isi-dev/ComfyUI-Img2PaintingAssistant": [ + [ + "Painting", + "ProcessInspyrenetRembg" + ], + { + "title_aux": "Image to Painting and Inspyrenet Assistant Nodes" + } + ], + "https://github.com/Isi-dev/ComfyUI-UniAnimate-W": [ + [ + "Animate_X_Image", + "Animate_X_Image_Long", + "Animate_X_Image_v2", + "Animate_X_ReposeImage", + "Animate_X_ReposeImage_v2", + "Gen_align_pose", + "Gen_align_pose2", + "ReposeImage", + "UniAnimateImage", + "UniAnimateImageLong" + ], + { + "title_aux": "ComfyUI-UniAnimate-W" + } + ], + "https://github.com/Isulion/ComfyUI_Isulion": [ + [ + "CustomTextNode", + "CustomTextNode \u2328\ufe0f", + "DisplayImageFromURL", + "IsuCollage_Node", + "Isulion Civitai Image Display", + "Isulion Civitai Model Explorer", + "Isulion Civitai Trending", + "IsulionActionGenerator", + "IsulionAlienWorldGenerator", + "IsulionAnimalBehaviorGenerator", + "IsulionAnimalRandom", + "IsulionArtStyleGenerator", + "IsulionArtifactGenerator", + "IsulionCivitaiImageDisplay", + "IsulionCivitaiModelExplorer", + "IsulionCivitaiTrending", + "IsulionClothingGenerator", + "IsulionCuteAnimalRandom", + "IsulionEmotionGenerator", + "IsulionEpochGenerator", + "IsulionFantasyRaceGenerator", + "IsulionHabitatGenerator", + "IsulionLoadImagesNode", + "IsulionMagicalEffectGenerator", + "IsulionMegaPromptV3", + "IsulionMultiplePromptGenerator", + "IsulionMythicalLocationGenerator", + "IsulionNegativePromptGenerator", + "IsulionProfessionGenerator", + "IsulionPromptEnhancer", + "IsulionSceneComposition", + "IsulionSpacecraftGenerator", + "IsulionStyleMixer", + "IsulionTechGenerator", + "IsulionTimeOfDayGenerator", + "IsulionVideoPromptGenerator", + "IsulionWeatherGenerator", + "\u29c9 IsulionOverlay", + "\ud83d\udca4 IsulionShutdown", + "\ud83e\udde9 IsulionQRCode" + ], + { + "title_aux": "ComfyUI_Isulion Random Prompt Generator" + } + ], + "https://github.com/IuvenisSapiens/ComfyUI_MiniCPM-V-2_6-int4": [ + [ + "DisplayText", + "LoadVideo", + "MiniCPM_VQA", + "MiniCPM_VQA_Polished", + "MultipleImagesInput", + "PreviewVideo" + ], + { + "title_aux": "ComfyUI_MiniCPM-V-2_6-int4" + } + ], + "https://github.com/IuvenisSapiens/ComfyUI_Qwen2-Audio-7B-Instruct-Int4": [ + [ + "AudioLoader", + "AudioPreviewer", + "Qwen2_AQA" + ], + { + "title_aux": "ComfyUI_Qwen2-Audio-7B-Instruct-Int4" + } + ], + "https://github.com/IuvenisSapiens/ComfyUI_Qwen2-VL-Instruct": [ + [ + "ImageLoader", + "MultiplePathsInput", + "Qwen2_VQA" + ], + { + "title_aux": "ComfyUI_Qwen2-VL-Instruct" + } + ], + "https://github.com/JEONG-JIWOO/ComfyUI_Eugene_Nodes": [ + [ + "DictBus", + "DictBusEdit", + "DictBusUnpack", + "DictMultilineSelect", + "DictTemplate", + "DictUpdate1", + "DictUpdate10", + "DictUpdate5", + "LoraPresetListLoader", + "LoraPresetLoader", + "LoraPresetSaver", + "LoraPresetSelector" + ], + { + "title_aux": "ComfyUI_Eugene_Nodes" + } + ], + "https://github.com/JPS-GER/ComfyUI_JPS-Nodes": [ + [ + "CLIPTextEncode SDXL Plus (JPS)", + "Conditioning Switch (JPS)", + "ControlNet Switch (JPS)", + "Crop Image Pipe (JPS)", + "Crop Image Settings (JPS)", + "Crop Image Square (JPS)", + "Crop Image TargetSize (JPS)", + "CtrlNet CannyEdge Pipe (JPS)", + "CtrlNet CannyEdge Settings (JPS)", + "CtrlNet MiDaS Pipe (JPS)", + "CtrlNet MiDaS Settings (JPS)", + "CtrlNet OpenPose Pipe (JPS)", + "CtrlNet OpenPose Settings (JPS)", + "CtrlNet ZoeDepth Pipe (JPS)", + "CtrlNet ZoeDepth Settings (JPS)", + "Disable Enable Switch (JPS)", + "Enable Disable Switch (JPS)", + "Generation TXT IMG Settings (JPS)", + "Get Date Time String (JPS)", + "Get Image Size (JPS)", + "IP Adapter Settings (JPS)", + "IP Adapter Settings Pipe (JPS)", + "IP Adapter Tiled Settings (JPS)", + "IP Adapter Tiled Settings Pipe (JPS)", + "IPA Switch (JPS)", + "Image Prepare Pipe (JPS)", + "Image Prepare Settings (JPS)", + "Image Switch (JPS)", + "ImageToImage Pipe (JPS)", + "ImageToImage Settings (JPS)", + "Images Masks MultiPipe (JPS)", + "InstantID Mask Prepare Pipe (JPS)", + "InstantID Mask Prepare Settings (JPS)", + "InstantID Pipe (JPS)", + "InstantID Pose Prepare Pipe (JPS)", + "InstantID Pose Prepare Settings (JPS)", + "InstantID Settings (JPS)", + "InstantID Source Prepare Pipe (JPS)", + "InstantID Source Prepare Settings (JPS)", + "Integer Switch (JPS)", + "Largest Int (JPS)", + "Latent Switch (JPS)", + "Lora Loader (JPS)", + "Mask Switch (JPS)", + "Model Switch (JPS)", + "Multiply Float Float (JPS)", + "Multiply Int Float (JPS)", + "Multiply Int Int (JPS)", + "Prepare Image (JPS)", + "Prepare Image Plus (JPS)", + "Prepare Image Tiled IPA (JPS)", + "Resolution Multiply (JPS)", + "Revision Settings (JPS)", + "Revision Settings Pipe (JPS)", + "SDXL Basic Settings (JPS)", + "SDXL Basic Settings Pipe (JPS)", + "SDXL Fundamentals MultiPipe (JPS)", + "SDXL Prompt Handling (JPS)", + "SDXL Prompt Handling Plus (JPS)", + "SDXL Prompt Styler (JPS)", + "SDXL Recommended Resolution Calc (JPS)", + "SDXL Resolutions (JPS)", + "SDXL Settings (JPS)", + "SDXL Settings Pipe (JPS)", + "Sampler Scheduler Settings (JPS)", + "Save Images Plus (JPS)", + "Substract Int Int (JPS)", + "Text Concatenate (JPS)", + "Text Prompt (JPS)", + "Text Prompt Combo (JPS)", + "Time Seed (JPS)", + "VAE Switch (JPS)" + ], + { + "author": "JPS", + "description": "Various nodes to handle SDXL Resolutions, SDXL Basic Settings, IP Adapter Settings, Revision Settings, SDXL Prompt Styler, Crop Image to Square, Crop Image to Target Size, Get Date-Time String, Resolution Multiply, Largest Integer, 5-to-1 Switches for Integer, Images, Latents, Conditioning, Model, VAE, ControlNet", + "nickname": "JPS Custom Nodes", + "title": "JPS Custom Nodes for ComfyUI", + "title_aux": "JPS Custom Nodes for ComfyUI" + } + ], + "https://github.com/JPrevots/ComfyUI-PhyCV": [ + [ + "PAGE", + "PST", + "VEVID" + ], + { + "title_aux": "ComfyUI-PhyCV" + } + ], + "https://github.com/JTriggerFish/ComfyLatentTools": [ + [ + "DownsampledAttentionGuidance", + "DownsampledLatentGuidance", + "GenericAttentionGuidance", + "LatentNormalizedLanczosResize" + ], + { + "title_aux": "Comfy Latent Tools" + } + ], + "https://github.com/JackEllie/ComfyUI_AI_Assistant": [ + [ + "apply_lighting_effects", + "clean_prompt_tags", + "noline_process", + "prompt_blacklist", + "prompt_sorting", + "resize_image_sdxl_ratio" + ], + { + "title_aux": "ComfyUI-AI-Assistant" + } + ], + "https://github.com/Jacky-MYQ/comfyui-DataCleaning": [ + [ + "CleanData" + ], + { + "title_aux": "comfyui-DataCleaning" + } + ], + "https://github.com/Jacky-MYQ/comfyui-rgb2cmyk": [ + [ + "RGB2CMYK" + ], + { + "title_aux": "RGB to CMYK for ComfyUI (Save as tif)" + } + ], + "https://github.com/Jaminanim/ComfyUI-Random-Int-Divisor-Node": [ + [ + "RandomIntegerNodeEfficient", + "RandomIntegerNodeEfficientAdvanced", + "RandomIntegerNodeList" + ], + { + "title_aux": "ComfyUI-Random-Int-Divisor-Node" + } + ], + "https://github.com/Jannchie/ComfyUI-J": [ + [ + "DiffusersCompelPromptEmbedding", + "DiffusersControlnetLoader", + "DiffusersControlnetUnit", + "DiffusersControlnetUnitStack", + "DiffusersDecoder", + "DiffusersGenerator", + "DiffusersPipeline", + "DiffusersPrepareLatents", + "DiffusersTextureInversionLoader", + "DiffusersXLPipeline", + "GetAverageColorFromImage", + "GetFilledColorImage" + ], + { + "title_aux": "ComfyUI-J" + } + ], + "https://github.com/Jannled/owl-vit-comfyui": [ + [ + "OWL_BBox_Visualizer", + "OWL_Load_Model", + "OWL_Objectness_Inference" + ], + { + "title_aux": "OWL-ViT ComfyUI" + } + ], + "https://github.com/JaredTherriault/ComfyUI-JNodes": [ + [ + "JNodes_AddOrSetMetaDataKey", + "JNodes_AnyToString", + "JNodes_AppendReversedFrames", + "JNodes_AudioInputOptions", + "JNodes_BooleanSelectorWithString", + "JNodes_BreakMediaInfo", + "JNodes_CheckpointSelectorWithString", + "JNodes_ConditioningInOut", + "JNodes_CreateStereoscopicImageFromDepth", + "JNodes_DiffusionModelSelector", + "JNodes_FloatLiteral", + "JNodes_GetCleanFilename", + "JNodes_GetComfyDirectory", + "JNodes_GetLeafDirectory", + "JNodes_GetOutputDirectory", + "JNodes_GetParameterFromList", + "JNodes_GetParameterGlobal", + "JNodes_GetTempDirectory", + "JNodes_ImageFormatSelector", + "JNodes_ImageSizeSelector", + "JNodes_IntLiteral", + "JNodes_JoinVideosInDirectory", + "JNodes_LoadVideo", + "JNodes_LoadVisualMediaFromPath", + "JNodes_LoadVisualMediaFromPath_Batch", + "JNodes_LoadVisualMediaFromPath_List", + "JNodes_LoraExtractor", + "JNodes_MediaInfoToString", + "JNodes_ModelInOut", + "JNodes_OutVideoInfo", + "JNodes_ParseDynamicPrompts", + "JNodes_ParseParametersToGlobalList", + "JNodes_ParseWildcards", + "JNodes_PromptBuilderSingleSubject", + "JNodes_RemoveCommentedText", + "JNodes_RemoveMetaDataKey", + "JNodes_RemoveParseableDataForInference", + "JNodes_SamplerSelectorWithString", + "JNodes_SaveImageWithOutput", + "JNodes_SaveVideo", + "JNodes_SaveVideoWithOptions", + "JNodes_SchedulerSelectorWithString", + "JNodes_SearchAndReplace", + "JNodes_SearchAndReplaceFromFile", + "JNodes_SearchAndReplaceFromList", + "JNodes_SelectRandomFileFromDirectory", + "JNodes_SeparateStringByDelimiters", + "JNodes_SetMetadataA1111", + "JNodes_SetNegativePromptInMetaData", + "JNodes_SetPositivePromptInMetaData", + "JNodes_SplitAndJoin", + "JNodes_StringLiteral", + "JNodes_SubdirectorySelector", + "JNodes_SyncedStringLiteral", + "JNodes_TokenCounter", + "JNodes_TrimAndStrip", + "JNodes_UploadVideo", + "JNodes_UploadVisualMedia", + "JNodes_VaeSelectorWithString" + ], + { + "title_aux": "ComfyUI-JNodes" + } + ], + "https://github.com/Jash-Vora/ComfyUI-GarmentDiT": [ + [ + "GarmentEnhancementNode" + ], + { + "title_aux": "FitDiT" + } + ], + "https://github.com/JcandZero/ComfyUI_GLM4Node": [ + [ + "GLM3_turbo_CHAT", + "GLM4_CHAT", + "GLM4_Vsion_IMGURL" + ], + { + "title_aux": "ComfyUI_GLM4Node" + } + ], + "https://github.com/Jcd1230/rembg-comfyui-node": [ + [ + "Image Remove Background (rembg)" + ], + { + "title_aux": "Rembg Background Removal Node for ComfyUI" + } + ], + "https://github.com/JerryOrbachJr/ComfyUI-RandomSize": [ + [ + "JOJR_RandomSize" + ], + { + "author": "JerryOrbachJr", + "description": "A ComfyUI custom node that randomly selects a height and width pair from a list in a config file", + "nickname": "Random Size", + "title": "Random Size", + "title_aux": "Random Size" + } + ], + "https://github.com/JettHu/ComfyUI-TCD": [ + [ + "TCDModelSamplingDiscrete" + ], + { + "title_aux": "ComfyUI-TCD" + } + ], + "https://github.com/JettHu/ComfyUI_TGate": [ + [ + "TGateApply", + "TGateApplyAdvanced", + "TGateApplySimple" + ], + { + "title_aux": "ComfyUI_TGate" + } + ], + "https://github.com/JiSenHua/ComfyUI-TD": [ + [ + "Comfy3DPacktoTD", + "Hy3DtoTD", + "ImagetoTD", + "ImagetoTD(JPEG)", + "LoadTDImage", + "Tripo3DtoTD", + "TripoSRtoTD", + "VideotoTD" + ], + { + "title_aux": "ComfyUI-TD" + } + ], + "https://github.com/Jint8888/Comfyui_JTnodes": [ + [ + "JT Find Text From Excel", + "JT Read From Excel", + "JTBrightness", + "JTImagesavetopath", + "JTSaveTextToExcel", + "JTSaveTextToFile", + "JTcounter", + "SiliconflowFree" + ], + { + "title_aux": "Comfyui_JTnodes" + } + ], + "https://github.com/JoeNavark/comfyui_custom_sigma_editor": [ + [ + "CustomSplineSigma", + "SigmaJoiner" + ], + { + "title_aux": "Custom Graph Sigma for ComfyUI" + } + ], + "https://github.com/JohanK66/ComfyUI-WebhookImage": [ + [ + "Notif-Webhook" + ], + { + "title_aux": "ComfyUI WebhookImage" + } + ], + "https://github.com/JohnDoeSmithee/ComfyUI-SoX-Mixdown": [ + [ + "SoxMixNode" + ], + { + "title_aux": "ComfyUI-SoX-Mixdown" + } + ], + "https://github.com/Jokimbe/ComfyUI-DrawThings-gRPC": [ + [ + "DrawThingsControlNet", + "DrawThingsLoRA", + "DrawThingsNegative", + "DrawThingsPositive", + "DrawThingsPrompt", + "DrawThingsRefiner", + "DrawThingsSampler", + "DrawThingsUpscaler" + ], + { + "title_aux": "ComfyUI-DrawThings-gRPC" + } + ], + "https://github.com/Jonseed/ComfyUI-Detail-Daemon": [ + [ + "DetailDaemonGraphSigmasNode", + "DetailDaemonSamplerNode", + "LyingSigmaSampler", + "MultiplySigmas" + ], + { + "title_aux": "ComfyUI-Detail-Daemon" + } + ], + "https://github.com/Jordach/comfy-plasma": [ + [ + "JDC_AutoContrast", + "JDC_BlendImages", + "JDC_BrownNoise", + "JDC_Contrast", + "JDC_EqualizeGrey", + "JDC_GaussianBlur", + "JDC_GreyNoise", + "JDC_Greyscale", + "JDC_ImageLoader", + "JDC_ImageLoaderMeta", + "JDC_PinkNoise", + "JDC_Plasma", + "JDC_PlasmaSampler", + "JDC_PowerImage", + "JDC_RandNoise", + "JDC_ResizeFactor" + ], + { + "title_aux": "comfy-plasma" + } + ], + "https://github.com/JosefKuchar/ComfyUI-AdvancedTiling": [ + [ + "AdvancedTiling", + "AdvancedTilingSettings", + "AdvancedTilingVAEDecode" + ], + { + "title_aux": "ComfyUI-AdvancedTiling" + } + ], + "https://github.com/JosephThomasParker/ComfyUI-DrawThingsWrapper": [ + [ + "DrawThingsGenerateFromPipeline", + "DrawThingsImg2Img", + "DrawThingsImg2ImgPipeline", + "DrawThingsPipelineAddControl", + "DrawThingsPipelineAddCustom", + "DrawThingsPipelineAddLora", + "DrawThingsTxt2Img", + "DrawThingsTxt2ImgPipeline" + ], + { + "title_aux": "ComfyUI-DrawThingsWrapper" + } + ], + "https://github.com/Julian-adv/WildDivide": [ + [ + "Attention couple wild divide", + "Comfy Divide", + "WildPromptGenerator", + "WildcardDivide", + "WildcardEncode" + ], + { + "author": "Julian Adventurer.", + "description": "This node is used to encode a wildcard string.", + "nickname": "WildDivide", + "title": "Wild Divide", + "title_aux": "Wild Divide" + } + ], + "https://github.com/JustLateNightAI/KeywordImageBlocker": [ + [ + "TagKeywordBlocker" + ], + { + "title_aux": "KeywordImageBlocker" + } + ], + "https://github.com/JustinMatters/comfyUI-JMNodes": [ + [ + "JMBinaryNot", + "JMIntegerToBooleans", + "JMNumberList", + "JMSWitchablePrompt" + ], + { + "title_aux": "ComfyUI JMNodes" + } + ], + "https://github.com/KAVVATARE/ComfyUI-Light-N-Color": [ + [ + "ControlNetSwitch", + "FluxLightingAndColor", + "FluxSamplerPuLID", + "ImageSwitch", + "LatentSwitch", + "LoadInputOutputImage" + ], + { + "title_aux": " ComfyUI-Light-N-Color" + } + ], + "https://github.com/KAVVATARE/ComfyUI_RightEyeDisparity": [ + [ + "RightEyeImageNode", + "VideoRightEyeNode" + ], + { + "title_aux": "RightEyeDisparity" + } + ], + "https://github.com/KERRY-YUAN/ComfyUI_Float_Animator": [ + [ + "Float_Animator" + ], + { + "title_aux": "ComfyUI_Float_Animator" + } + ], + "https://github.com/KERRY-YUAN/ComfyUI_Simple_Executor": [ + [ + "NodeAutoSampler", + "NodeImagePre", + "NodeImageResize" + ], + { + "title_aux": "NodeSimpleExecutor" + } + ], + "https://github.com/KERRY-YUAN/ComfyUI_Spark_TTS": [ + [ + "Spark_TTS_Clone", + "Spark_TTS_Creation" + ], + { + "title_aux": "ComfyUI_Spark_TTS" + } + ], + "https://github.com/KLL535/ComfyUI_SimpleButcher": [ + [ + "Simple Auto Bypass", + "Simple Extract Lora From Text", + "Simple Image Saver (as Forge)", + "Simple Load Image With Metadata", + "Simple Load Images from Dir", + "Simple Load Line From Text File", + "Simple Lora Loader", + "Simple Remove Think" + ], + { + "title_aux": "ComfyUI_SimpleButcher" + } + ], + "https://github.com/Kangkang625/ComfyUI-paint-by-example": [ + [ + "PaintbyExamplePipeLoader", + "PaintbyExampleSampler" + ], + { + "title_aux": "ComfyUI-Paint-by-Example" + } + ], + "https://github.com/KarmaSwint/ComfyUI-KarmaNodes": [ + [ + "Karma-Film-Grain", + "Karma-KSampler-Cycle", + "Karma-Kolors", + "Karma_Film_Grain", + "Karma_Kolors" + ], + { + "title_aux": "KarmaNodes" + } + ], + "https://github.com/Kayarte/AudioDriven-Latent-Space-Tools-for-ComfyUI": [ + [ + "AdvancedNoisePatterns", + "AudioNoiseMapper", + "LibrosaAnalysisNode", + "NoiseToLatentConverter" + ], + { + "title_aux": "AudioDriven-Latent-Space-Tools-for-ComfyUI" + } + ], + "https://github.com/Kayarte/GeoNodes/raw/refs/heads/main/GISDetectionNode.py": [ + [ + "GISDetectionNode" + ], + { + "title_aux": "GeoNodes" + } + ], + "https://github.com/Kesin11/ComfyUI-list-filter": [ + [ + "list_filter_FilterImageListByIndexList", + "list_filter_FilterStringListByIndexList", + "list_filter_FindAnyStrings", + "list_filter_FindNotAnyStrings", + "list_filter_StringToIndex", + "random_normal_dist" + ], + { + "title_aux": "ComfyUI-list-filter" + } + ], + "https://github.com/KewkLW/ComfyUI-kewky_tools": [ + [ + "CLIPInterrogator", + "FormattedPromptNode", + "ImageBatcher", + "LoadImagePlus", + "LoadVideoPlus", + "TensorDebugPlus", + "TextAppendNode", + "TextSearchNode", + "VRAM_Debug_Plus" + ], + { + "title_aux": "ComfyUI-kewky_tools" + } + ], + "https://github.com/Kidev/ComfyUI-Fisheye-effects": [ + [ + "Defisheye", + "Fisheye" + ], + { + "title_aux": "ComfyUI Fisheye Effects Nodes" + } + ], + "https://github.com/KohakuBlueleaf/z-tipo-extension": [ + [ + "TIPO", + "TIPOFormat", + "TIPOOperation" + ], + { + "title_aux": "TIPO-extension" + } + ], + "https://github.com/KoreTeknology/ComfyUI-Nai-Production-Nodes-Pack": [ + [ + "Brightness Image", + "ColorMatch2", + "Contrast Image", + "Get Text", + "Image Difference", + "ImageConcatenate", + "ImageDesaturate", + "ImageExtend", + "ImageFlip", + "ImageRotate", + "LoadImageNai", + "Math Operation", + "NoteAdvanced", + "Set Text" + ], + { + "title_aux": "ComfyUI Production Nodes Pack" + } + ], + "https://github.com/KoreTeknology/ComfyUI-Universal-Styler": [ + [ + "\ud83d\udee1\ufe0f Load Scripts from Database", + "\ud83d\udee1\ufe0f Save Script to Database (In progress)", + "\ud83d\udee1\ufe0f Set Main Channel" + ], + { + "title_aux": "ComfyUI Universal Styler" + } + ], + "https://github.com/Kosinkadink/ComfyUI-Advanced-ControlNet": [ + [ + "ACN_AdvancedControlNetApply", + "ACN_AdvancedControlNetApplySingle", + "ACN_AdvancedControlNetApplySingle_v2", + "ACN_AdvancedControlNetApply_v2", + "ACN_ControlNet++InputNode", + "ACN_ControlNet++LoaderAdvanced", + "ACN_ControlNet++LoaderSingle", + "ACN_ControlNetLoaderAdvanced", + "ACN_ControlNetLoaderWithLoraAdvanced", + "ACN_CtrLoRALoader", + "ACN_CustomControlNetWeightsFlux", + "ACN_CustomControlNetWeightsSD15", + "ACN_CustomT2IAdapterWeights", + "ACN_DefaultUniversalWeights", + "ACN_DiffControlNetLoaderAdvanced", + "ACN_ExtrasMiddleMult", + "ACN_ReferenceControlNet", + "ACN_ReferenceControlNetFinetune", + "ACN_ReferencePreprocessor", + "ACN_ScaledSoftControlNetWeights", + "ACN_SoftControlNetWeightsSD15", + "ACN_SoftT2IAdapterWeights", + "ACN_SparseCtrlIndexMethodNode", + "ACN_SparseCtrlLoaderAdvanced", + "ACN_SparseCtrlMergedLoaderAdvanced", + "ACN_SparseCtrlRGBPreprocessor", + "ACN_SparseCtrlSpreadMethodNode", + "ACN_SparseCtrlWeightExtras", + "ACN_TimestepKeyframeFromStrengthList", + "ACN_TimestepKeyframeInterpolation", + "ControlNetLoaderAdvanced", + "CustomControlNetWeights", + "CustomT2IAdapterWeights", + "DiffControlNetLoaderAdvanced", + "LatentKeyframe", + "LatentKeyframeBatchedGroup", + "LatentKeyframeGroup", + "LatentKeyframeTiming", + "LoadImagesFromDirectory", + "ScaledSoftControlNetWeights", + "ScaledSoftMaskedUniversalWeights", + "SoftControlNetWeights", + "SoftT2IAdapterWeights", + "TimestepKeyframe" + ], + { + "title_aux": "ComfyUI-Advanced-ControlNet" + } + ], + "https://github.com/Kosinkadink/ComfyUI-AnimateDiff-Evolved": [ + [ + "ADE_AdjustPEFullStretch", + "ADE_AdjustPEManual", + "ADE_AdjustPESweetspotStretch", + "ADE_AdjustWeightAllAdd", + "ADE_AdjustWeightAllMult", + "ADE_AdjustWeightIndivAdd", + "ADE_AdjustWeightIndivAttnAdd", + "ADE_AdjustWeightIndivAttnMult", + "ADE_AdjustWeightIndivMult", + "ADE_AncestralOptions", + "ADE_AnimateDiffCombine", + "ADE_AnimateDiffKeyframe", + "ADE_AnimateDiffLoRALoader", + "ADE_AnimateDiffLoaderGen1", + "ADE_AnimateDiffLoaderV1Advanced", + "ADE_AnimateDiffLoaderWithContext", + "ADE_AnimateDiffModelSettings", + "ADE_AnimateDiffModelSettingsAdvancedAttnStrengths", + "ADE_AnimateDiffModelSettingsSimple", + "ADE_AnimateDiffModelSettings_Release", + "ADE_AnimateDiffSamplingSettings", + "ADE_AnimateDiffSettings", + "ADE_AnimateDiffUniformContextOptions", + "ADE_AnimateDiffUnload", + "ADE_ApplyAnimateDiffModel", + "ADE_ApplyAnimateDiffModelSimple", + "ADE_ApplyAnimateDiffModelWithCameraCtrl", + "ADE_ApplyAnimateDiffModelWithPIA", + "ADE_ApplyAnimateLCMI2VModel", + "ADE_AttachLoraHookToCLIP", + "ADE_AttachLoraHookToConditioning", + "ADE_BatchedContextOptions", + "ADE_CFGExtrasPAG", + "ADE_CFGExtrasPAGSimple", + "ADE_CFGExtrasRescaleCFG", + "ADE_CFGExtrasRescaleCFGSimple", + "ADE_CameraCtrlAnimateDiffKeyframe", + "ADE_CameraManualPoseAppend", + "ADE_CameraPoseAdvanced", + "ADE_CameraPoseBasic", + "ADE_CameraPoseCombo", + "ADE_CombineLoraHooks", + "ADE_CombineLoraHooksEight", + "ADE_CombineLoraHooksFour", + "ADE_ConditioningCombine", + "ADE_ConditioningSetMask", + "ADE_ConditioningSetMaskAndCombine", + "ADE_ConditioningSetUnmaskedAndCombine", + "ADE_ContextExtras_ContextRef", + "ADE_ContextExtras_ContextRef_Keyframe", + "ADE_ContextExtras_ContextRef_KeyframeFromList", + "ADE_ContextExtras_ContextRef_KeyframeInterpolation", + "ADE_ContextExtras_ContextRef_ModeFirst", + "ADE_ContextExtras_ContextRef_ModeIndexes", + "ADE_ContextExtras_ContextRef_ModeSliding", + "ADE_ContextExtras_ContextRef_TuneAttn", + "ADE_ContextExtras_ContextRef_TuneAttnAdain", + "ADE_ContextExtras_NaiveReuse", + "ADE_ContextExtras_NaiveReuse_Keyframe", + "ADE_ContextExtras_NaiveReuse_KeyframeFromList", + "ADE_ContextExtras_NaiveReuse_KeyframeInterpolation", + "ADE_ContextExtras_Set", + "ADE_CustomCFG", + "ADE_CustomCFGKeyframe", + "ADE_CustomCFGKeyframeFromList", + "ADE_CustomCFGKeyframeInterpolation", + "ADE_CustomCFGKeyframeSimple", + "ADE_CustomCFGSimple", + "ADE_EmptyLatentImageLarge", + "ADE_InjectI2VIntoAnimateDiffModel", + "ADE_InjectPIAIntoAnimateDiffModel", + "ADE_InputPIA_Multival", + "ADE_InputPIA_PaperPresets", + "ADE_IterationOptsDefault", + "ADE_IterationOptsFreeInit", + "ADE_LoadAnimateDiffModel", + "ADE_LoadAnimateDiffModelWithCameraCtrl", + "ADE_LoadAnimateLCMI2VModel", + "ADE_LoadCameraPoses", + "ADE_LoadCameraPosesFromPath", + "ADE_LoopedUniformContextOptions", + "ADE_LoopedUniformViewOptions", + "ADE_LoraHookKeyframe", + "ADE_LoraHookKeyframeFromStrengthList", + "ADE_LoraHookKeyframeInterpolation", + "ADE_MultivalConvertToMask", + "ADE_MultivalDynamic", + "ADE_MultivalDynamicFloatInput", + "ADE_MultivalDynamicFloats", + "ADE_MultivalScaledMask", + "ADE_NoiseCalibration", + "ADE_NoiseLayerAdd", + "ADE_NoiseLayerAddWeighted", + "ADE_NoiseLayerNormalizedSum", + "ADE_NoiseLayerReplace", + "ADE_NoisedImageInjectOptions", + "ADE_NoisedImageInjection", + "ADE_PIA_AnimateDiffKeyframe", + "ADE_PairedConditioningCombine", + "ADE_PairedConditioningSetMask", + "ADE_PairedConditioningSetMaskAndCombine", + "ADE_PairedConditioningSetUnmaskedAndCombine", + "ADE_PerturbedAttentionGuidanceMultival", + "ADE_RawSigmaSchedule", + "ADE_RegisterLoraHook", + "ADE_RegisterLoraHookModelOnly", + "ADE_RegisterModelAsLoraHook", + "ADE_RegisterModelAsLoraHookModelOnly", + "ADE_ReplaceCameraParameters", + "ADE_ReplaceOriginalPoseAspectRatio", + "ADE_RescaleCFGMultival", + "ADE_SetLoraHookKeyframe", + "ADE_SigmaSchedule", + "ADE_SigmaScheduleSplitAndCombine", + "ADE_SigmaScheduleToSigmas", + "ADE_SigmaScheduleWeightedAverage", + "ADE_SigmaScheduleWeightedAverageInterp", + "ADE_StandardStaticContextOptions", + "ADE_StandardStaticViewOptions", + "ADE_StandardUniformContextOptions", + "ADE_StandardUniformViewOptions", + "ADE_TimestepsConditioning", + "ADE_UpscaleAndVAEEncode", + "ADE_UseEvolvedSampling", + "ADE_ViewsOnlyContextOptions", + "ADE_VisualizeContextOptionsK", + "ADE_VisualizeContextOptionsKAdv", + "ADE_VisualizeContextOptionsSCustom", + "AnimateDiffLoaderV1", + "CheckpointLoaderSimpleWithNoiseSelect" + ], + { + "title_aux": "AnimateDiff Evolved" + } + ], + "https://github.com/Kosinkadink/ComfyUI-VideoHelperSuite": [ + [ + "VHS_AudioToVHSAudio", + "VHS_BatchManager", + "VHS_DuplicateImages", + "VHS_DuplicateLatents", + "VHS_DuplicateMasks", + "VHS_GetImageCount", + "VHS_GetLatentCount", + "VHS_GetMaskCount", + "VHS_LoadAudio", + "VHS_LoadAudioUpload", + "VHS_LoadImagePath", + "VHS_LoadImages", + "VHS_LoadImagesPath", + "VHS_LoadVideo", + "VHS_LoadVideoFFmpeg", + "VHS_LoadVideoFFmpegPath", + "VHS_LoadVideoPath", + "VHS_MergeImages", + "VHS_MergeLatents", + "VHS_MergeMasks", + "VHS_PruneOutputs", + "VHS_SelectEveryNthImage", + "VHS_SelectEveryNthLatent", + "VHS_SelectEveryNthMask", + "VHS_SelectFilename", + "VHS_SelectImages", + "VHS_SelectLatents", + "VHS_SelectLatest", + "VHS_SelectMasks", + "VHS_SplitImages", + "VHS_SplitLatents", + "VHS_SplitMasks", + "VHS_Unbatch", + "VHS_VAEDecodeBatched", + "VHS_VAEEncodeBatched", + "VHS_VHSAudioToAudio", + "VHS_VideoCombine", + "VHS_VideoInfo", + "VHS_VideoInfoLoaded", + "VHS_VideoInfoSource" + ], + { + "title_aux": "ComfyUI-VideoHelperSuite" + } + ], + "https://github.com/Koushakur/ComfyUI-DenoiseChooser": [ + [ + "DenoiseChooser|Koushakur" + ], + { + "title_aux": "ComfyUI-DenoiseChooser" + } + ], + "https://github.com/KunmyonChoi/ComfyUI_S3_direct": [ + [ + "Direct Load Image From S3", + "Direct Save Image To S3", + "Save VHS Video to S3" + ], + { + "title_aux": "ComfyUI_S3_direct" + } + ], + "https://github.com/Kurdknight/Kurdknight_comfycheck": [ + [ + "SystemCheck", + "SystemViz" + ], + { + "title_aux": "KurdKnight ComfyUI System Check Node" + } + ], + "https://github.com/KwaiVGI/ComfyUI-KLingAI-API": [ + [ + "Client", + "Effects", + "Image Generator", + "Image2Video", + "KLingAI Preview Video", + "Lip Sync", + "Lip Sync Audio Input", + "Lip Sync Text Input", + "Text2Video", + "Video Extend", + "Virtual Try On" + ], + { + "title_aux": "ComfyUI-KLingAI-API" + } + ], + "https://github.com/Ky11le/draw_tools": [ + [ + "DetectInnerBox", + "PasteIntoFrame", + "TextBoxAutoWrap" + ], + { + "title_aux": "draw_tools" + } + ], + "https://github.com/Ky11le/ygo_tools": [ + [ + "DetectInnerBox", + "PasteIntoFrame", + "TextBoxAutoWrap" + ], + { + "title_aux": "ygo_tools" + } + ], + "https://github.com/KytraScript/ComfyUI_KytraWebhookHTTP": [ + [ + "SendToDiscordWebhook" + ], + { + "title_aux": "ComfyUI_KytraWebhookHTTP" + } + ], + "https://github.com/KytraScript/ComfyUI_MatAnyone_Kytra": [ + [ + "Kytra_Images_To_RGB", + "MatAnyoneVideoMatting" + ], + { + "title_aux": "ComfyUI_MatAnyone_Kytra" + } + ], + "https://github.com/LAOGOU-666/ComfyUI-LG_HotReload": [ + [ + "HotReload_Terminal" + ], + { + "title_aux": "ComfyUI-LG_HotReload" + } + ], + "https://github.com/LAOGOU-666/ComfyUI_LG_FFT": [ + [ + "LG_FFTNode", + "LG_IFFTNode" + ], + { + "title_aux": "ComfyUI_LG_FFT" + } + ], + "https://github.com/LAOGOU-666/Comfyui-LG_GroupExecutor": [ + [ + "GroupExecutorRepeater", + "GroupExecutorSender", + "GroupExecutorSingle", + "ImageListRepeater", + "ImageListSplitter", + "LG_AccumulatePreview", + "LG_FastPreview", + "LG_ImageReceiver", + "LG_ImageSender", + "MaskListRepeater", + "MaskListSplitter" + ], + { + "title_aux": "Comfyui-LG_GroupExecutor" + } + ], + "https://github.com/LAOGOU-666/Comfyui-LG_Relight": [ + [ + "LG_Relight", + "LG_Relight_Basic", + "LG_Relight_Ultra" + ], + { + "title_aux": "Comfyui-LG_Relight" + } + ], + "https://github.com/LAOGOU-666/Comfyui-Memory_Cleanup": [ + [ + "RAMCleanup", + "VRAMCleanup" + ], + { + "title_aux": "Comfyui-Memory_Cleanup" + } + ], + "https://github.com/LAOGOU-666/Comfyui_LG_Tools": [ + [ + "BridgePreviewNode", + "CachePreviewBridge", + "ColorAdjustment", + "FastCanvas", + "FastCanvasComposite", + "FastCanvasTool", + "GroupSwitcher", + "IPAdapterWeightTypes", + "ImageCropper", + "ImageSelector", + "ImageSizeAdjustment", + "InspyrenetRembgLoader", + "InspyrenetRembgProcess", + "LG_FloatRange", + "LG_InstallDependencies", + "LG_LatentBatchToList", + "LG_LoadImage", + "LG_Noise", + "LG_PipManager", + "LG_SaveImage", + "LazySwitch1way", + "LazySwitch2way", + "MuterSwitcher" + ], + { + "title_aux": "Comfyui_LG_Tools" + } + ], + "https://github.com/LEv145/images-grid-comfy-plugin": [ + [ + "GridAnnotation", + "ImageCombine", + "ImagesGridByColumns", + "ImagesGridByRows", + "LatentCombine" + ], + { + "title_aux": "ImagesGrid" + } + ], + "https://github.com/LK-168/comfyui_imgutils": [ + [ + "BBoxFilter", + "BBoxToMaskNode", + "CensorWithMask", + "ImgutilsAutoSegmenter", + "ImgutilsBBoxSegmenter", + "ImgutilsGenericDetector", + "MaskAttributeNodeLK", + "MaskCombineNodeLK", + "MaskEdgeNodeLK", + "MaskHelperLK", + "MaskInfoNodeLK", + "MaskMorphologyNodeLK", + "MaskToBBoxNode", + "SAMLoaderLK", + "SAMPredictorNode" + ], + { + "title_aux": "comfyui_imgutils" + } + ], + "https://github.com/LKbaba/ComfyUI-TuZi-Flux-Kontext": [ + [ + "FluxKontext_ImageToImage", + "FluxKontext_MultiImageToImage", + "FluxKontext_TextToImage" + ], + { + "title_aux": "ComfyUI-TuZi-Flux-Kontext" + } + ], + "https://github.com/LamEmil/ComfyUI_ASCIIArtNode": [ + [ + "ASCIIAnimationGenerator", + "ASCIIArtGenerator", + "ColorASCIIAnimationGenerator", + "RealisticColorASCIIAnimationGenerator", + "SequentialTwoPassTypingColorASCIIAnimation", + "TwoPassTypingColorASCIIAnimation", + "VideoToColorStaticASCIIArt", + "VideoToDynamicColorASCIIArt" + ], + { + "title_aux": "ComfyUI ASCII Art Nodes" + } + ], + "https://github.com/LaoMaoBoss/ComfyUI-WBLESS": [ + [ + "Inversed Switch", + "Switch" + ], + { + "title_aux": "ComfyUI-WBLESS" + } + ], + "https://github.com/LargeModGames/comfyui-smart-lora-downloader": [ + [ + "LoRAAutoDownloader", + "WorkflowLoRAScanner" + ], + { + "title_aux": "ComfyUI LoRA Auto Downloader" + } + ], + "https://github.com/LarryJane491/Image-Captioning-in-ComfyUI": [ + [ + "LoRA Caption Load", + "LoRA Caption Save" + ], + { + "title_aux": "Image-Captioning-in-ComfyUI" + } + ], + "https://github.com/LarryJane491/Lora-Training-in-Comfy": [ + [ + "Lora Training in Comfy (Advanced)", + "Lora Training in ComfyUI", + "Tensorboard Access" + ], + { + "title_aux": "Lora-Training-in-Comfy" + } + ], + "https://github.com/LatentSpaceDirective/ComfyUI-Texturaizer": [ + [ + "Texturaizer_ApplyControlNets", + "Texturaizer_ApplyStyleToPrompt", + "Texturaizer_CachedCNLoader", + "Texturaizer_CachedCheckpointLoader", + "Texturaizer_ClipEncodeSwitchVersion", + "Texturaizer_CombinedConditioningFromColors", + "Texturaizer_ExtractCNData", + "Texturaizer_GenerateNoise", + "Texturaizer_GetCNData", + "Texturaizer_GetClipModelName", + "Texturaizer_GetFluxGuidance", + "Texturaizer_GetIPAdapterData", + "Texturaizer_GetImageData", + "Texturaizer_GetJsonData", + "Texturaizer_GetLoraData", + "Texturaizer_GetMaterialTileData", + "Texturaizer_GetModelName", + "Texturaizer_GetPromptData", + "Texturaizer_GetRenderData", + "Texturaizer_GetSamplerData", + "Texturaizer_GetSegData", + "Texturaizer_GetStyleData", + "Texturaizer_GetVAEName", + "Texturaizer_IPAdapterEmbeds", + "Texturaizer_KSamplerAdvanced", + "Texturaizer_Placeholder", + "Texturaizer_PowerLoraLoader", + "Texturaizer_SendImage", + "Texturaizer_SetGlobalDir", + "Texturaizer_SigmasSelector", + "Texturaizer_SwitchAny", + "Texturaizer_SwitchLazy", + "Texturaizer_UseSDXL" + ], + { + "title_aux": "ComfyUI-Texturaizer" + } + ], + "https://github.com/Layer-norm/comfyui-lama-remover": [ + [ + "LamaRemover", + "LamaRemoverIMG" + ], + { + "title_aux": "Comfyui lama remover" + } + ], + "https://github.com/Legorobotdude/ComfyUI-VariationLab": [ + [ + "CFGExplorer", + "CheckpointExplorer", + "StepExplorer" + ], + { + "title_aux": "ComfyUI-VariationLab" + } + ], + "https://github.com/Lerc/canvas_tab": [ + [ + "Canvas_Tab", + "Send_To_Editor" + ], + { + "author": "Lerc", + "description": "This extension provides a full page image editor with mask support. There are two nodes, one to receive images from the editor and one to send images to the editor.", + "nickname": "Canvas Tab", + "title": "Canvas Tab", + "title_aux": "Canvas Tab" + } + ], + "https://github.com/LevelPixel/ComfyUI-LevelPixel": [ + [ + "AnyToText|LP", + "BoolToInt|LP", + "BoolToString|LP", + "CLIP Text Encode Translate [LP]", + "CLIPTextEncodeTranslate|LP", + "Calculate Target Size By Mask [LP]", + "CalculateTargetSizeByMask|LP", + "ComboToText|LP", + "Convert Any To Text [LP]", + "Convert Bool To Int [LP]", + "Convert Bool To String [LP]", + "Convert Combo To Text [LP]", + "Convert Float To Int [LP]", + "Convert Float To String [LP]", + "Convert Int To Bool [LP]", + "Convert Int To Float [LP]", + "Convert Int To String [LP]", + "Convert String To Bool [LP]", + "Convert String To Combo [LP]", + "Convert String To Float [LP]", + "Convert String To Int [LP]", + "Convert String To Number [LP]", + "Count Objects [LP]", + "CountObjects|LP", + "Cropped Aspect Size Parameters [LP]", + "Cropped Forsed Size Parameters [LP]", + "Cropped Free Size Parameters [LP]", + "Cropped Ranged Size Parameters [LP]", + "CroppedAspectSizeParameters|LP", + "CroppedForsedSizeParameters|LP", + "CroppedFreeSizeParameters|LP", + "CroppedRangedSizeParameters|LP", + "Delay [LP]", + "Delay|LP", + "Extend Factor Parameters [LP]", + "ExtendFactorParameters|LP", + "Fast Checker Pattern [LP]", + "FastCheckerPattern|LP", + "File Counter [LP]", + "FileCounter|LP", + "Find Value From File [LP]", + "FindValueFromFile|LP", + "FloatToInt|LP", + "FloatToString|LP", + "Get ComfyUI Folder Path [LP]", + "Get ComfyUI HTTP Folder Path [LP]", + "Get Filename By Index In Folder [LP]", + "Get Iterator Data From Image Folders [LP]", + "Get Iterator Data From Videos [LP]", + "GetComfyUIFolderPath|LP", + "GetComfyUIHttpFolderPath|LP", + "GetFilenameByIndexInFolder|LP", + "GetIteratorDataImageFolders|LP", + "GetIteratorDataVideos|LP", + "Hard Model Unloader [LP]", + "HardModelUnloader|LP", + "HundredthsSimpleFloatSlider|LP", + "Image Data Iterator [LP]", + "Image Loader From Path [LP]", + "Image Overlay [LP]", + "ImageDataIterator|LP", + "ImageLoaderFromPath|LP", + "ImageOverlay|LP", + "Inpaint Crop [LP]", + "Inpaint Stitch [LP]", + "InpaintCrop|LP", + "InpaintStitch|LP", + "IntToBool|LP", + "IntToFloat|LP", + "IntToString|LP", + "Iterator [LP]", + "Iterator|LP", + "Keep Only English Tags [LP]", + "Keep Only English Words [LP]", + "KeepOnlyEnglishTags|LP", + "KeepOnlyEnglishWords|LP", + "Load Image [LP]", + "Load LoRA Tag [LP]", + "LoadImage|LP", + "LoraTagLoader|LP", + "Model Unloader [LP]", + "ModelUnloader|LP", + "Override CLIP Device [LP]", + "Override VAE Device [LP]", + "OverrideCLIPDevice|LP", + "OverrideVAEDevice|LP", + "Pipe In [LP]", + "Pipe Out [LP]", + "Pipe [LP]", + "PipeIn|LP", + "PipeOut|LP", + "Pipe|LP", + "Preview Image Bridge [LP]", + "PreviewImageForConditions|LP", + "Remove Banned Tags From Tags [LP]", + "Remove Banned Tags From Text [LP]", + "Remove Duplicate Tags [LP]", + "RemoveBannedTagsFromTags|LP", + "RemoveBannedTagsFromText|LP", + "RemoveDuplicateTags|LP", + "Resize Image To Target Size [LP]", + "Resize Image and Masks [LP]", + "ResizeImageAndMasks|LP", + "ResizeImageToTargetSize|LP", + "Resorting Tags [LP]", + "ResortingTags|LP", + "Seed [LP]", + "Seed|LP", + "Show Text Bridge [LP]", + "Show Text [LP]", + "ShowTextBridge|LP", + "ShowText|LP", + "Simple Float Slider - Hundredths Step [LP]", + "Simple Float Slider - Tenths Step [LP]", + "Simple Float Slider [LP]", + "SimpleFloatSlider|LP", + "Soft Model Unloader [LP]", + "SoftModelUnloader|LP", + "Split Compound Text [LP]", + "SplitCompoundText|LP", + "String Cycler [LP]", + "String [LP]", + "StringCycler|LP", + "StringToBool|LP", + "StringToCombo|LP", + "StringToFloat|LP", + "StringToInt|LP", + "StringToNumber|LP", + "String|LP", + "Tag Category Filter [LP]", + "Tag Category Keeper [LP]", + "Tag Category Remover [LP]", + "Tag Category [LP]", + "Tag Merger [LP]", + "Tag Remover [LP]", + "Tag Replace [LP]", + "Tag Switcher [LP]", + "TagCategoryFilter|LP", + "TagCategoryKeeper|LP", + "TagCategoryRemover|LP", + "TagCategory|LP", + "TagMerger|LP", + "TagRemover|LP", + "TagReplace|LP", + "TagSwitcher|LP", + "TenthsSimpleFloatSlider|LP", + "Text Choice Parser [LP]", + "Text Replace [LP]", + "Text To List [LP]", + "Text Translate [LP]", + "Text [LP]", + "TextChoiceParser|LP", + "TextReplace|LP", + "TextToList|LP", + "TextTranslateManualAll|LP", + "TextTranslateManual|LP", + "TextTranslate|LP", + "Text|LP" + ], + { + "title_aux": "ComfyUI Level Pixel" + } + ], + "https://github.com/LevelPixel/ComfyUI-LevelPixel-Advanced": [ + [ + "Autotagger [LP]", + "Autotagger|LP", + "Color Input [LP]", + "ColorInput|LP", + "Image Remove Background (BiRefNet) [LP]", + "Image Remove Background (RMBG) [LP]", + "Image Remove Background (rembg) [LP]", + "ImageRemoveBackgroundBiRefNet|LP", + "ImageRemoveBackgroundRMBG|LP", + "ImageRemoveBackground|LP", + "LLM Advanced [LP]", + "LLM Loader [LP]", + "LLM Sampler [LP]", + "LLMAdvanced|LP", + "LLMLoader|LP", + "LLMSampler|LP", + "LLava Advanced [LP]", + "LLava Clip Loader [LP]", + "LLava Loader [LP]", + "LLava Sampler Advanced [LP]", + "LLava Sampler Simple [LP]", + "LLava Simple [LP]", + "LLavaAdvanced|LP", + "LLavaClipLoader|LP", + "LLavaLoader|LP", + "LLavaSamplerAdvanced|LP", + "LLavaSamplerSimple|LP", + "LLavaSimple|LP", + "Multimodal Generator Advanced [LP]", + "MultimodalGeneratorAdvanced|LP", + "Recognize Anything (RAM) [LP]", + "RecognizeAnything(RAM)|LP" + ], + { + "title_aux": "ComfyUI Level Pixel Advanced" + } + ], + "https://github.com/Lhyejin/ComfyUI-Fill-Image-for-Outpainting": [ + [ + "FillImageForOutpainting" + ], + { + "title_aux": "ComfyUI-Fill-Image-for-Outpainting" + } + ], + "https://github.com/LiJT/ComfyUI-Gemini-Prompt-Generator-JT": [ + [ + "GeminiPromptGeneratorJT" + ], + { + "title_aux": "Gemini prompt generator JT version" + } + ], + "https://github.com/Light-x02/ComfyUI-FluxSettingsNode": [ + [ + "DisableNoise", + "FluxSettingsNode" + ], + { + "title_aux": "Flux Settings Node" + } + ], + "https://github.com/Light-x02/ComfyUI-Image-Metadata-Nodes": [ + [ + "ImageMetadataLoader", + "ImageMetadataSaver" + ], + { + "title_aux": "Image Metadata Nodes" + } + ], + "https://github.com/LightSketch-ai/ComfyUI-LivePortraitNode": [ + [ + "LightSketch Live Portrait", + "Preview Video" + ], + { + "title_aux": "ComfyUI-LivePortraitNode (Replicate API)" + } + ], + "https://github.com/Lightricks/ComfyUI-LTXVideo": [ + [ + "AddLatentGuide", + "ImageToCPU", + "LTXAttentioOverride", + "LTXAttentionBank", + "LTXAttnOverride", + "LTXFetaEnhance", + "LTXFlowEditCFGGuider", + "LTXFlowEditSampler", + "LTXForwardModelSamplingPred", + "LTXPerturbedAttention", + "LTXPrepareAttnInjections", + "LTXQ8Patch", + "LTXRFForwardODESampler", + "LTXRFReverseODESampler", + "LTXReverseModelSamplingPred", + "LTXVAdainLatent", + "LTXVAddGuideAdvanced", + "LTXVApplySTG", + "LTXVBaseSampler", + "LTXVExtendSampler", + "LTXVFilmGrain", + "LTXVInContextSampler", + "LTXVLatentUpsampler", + "LTXVLinearOverlapLatentTransition", + "LTXVLoopingSampler", + "LTXVMultiPromptProvider", + "LTXVPatcherVAE", + "LTXVPreprocessMasks", + "LTXVPromptEnhancer", + "LTXVPromptEnhancerLoader", + "LTXVQ8LoraModelLoader", + "LTXVSelectLatents", + "LTXVSetVideoLatentNoiseMasks", + "LTXVTiledSampler", + "LTXVTiledVAEDecode", + "ModifyLTXModel", + "STGAdvancedPresets", + "STGGuiderAdvanced", + "STGGuiderNode", + "Set VAE Decoder Noise" + ], + { + "title_aux": "ComfyUI-LTXVideo" + } + ], + "https://github.com/Limbicnation/ComfyUI-TransparencyBackgroundRemover": [ + [ + "TransparencyBackgroundRemover", + "TransparencyBackgroundRemoverBatch" + ], + { + "title_aux": "Transparency Background Remover" + } + ], + "https://github.com/Limbicnation/ComfyUIDepthEstimation": [ + [ + "DepthEstimationNode" + ], + { + "title_aux": "Depth Estimation Node" + } + ], + "https://github.com/Limbicnation/ComfyUI_FaceDetectionNode": [ + [ + "FaceDetectionNode", + "custom_nodes" + ], + { + "nodename_pattern": "FaceDetectionNode", + "title_aux": "ComfyUI Face Detection Node" + } + ], + "https://github.com/Limitex/ComfyUI-Calculation": [ + [ + "CenterCalculation", + "CreateQRCode" + ], + { + "title_aux": "ComfyUI-Calculation" + } + ], + "https://github.com/Limitex/ComfyUI-Diffusers": [ + [ + "CreateIntListNode", + "DiffusersClipTextEncode", + "DiffusersModelMakeup", + "DiffusersPipelineLoader", + "DiffusersSampler", + "DiffusersSchedulerLoader", + "DiffusersVaeLoader", + "LcmLoraLoader", + "StreamDiffusionCreateStream", + "StreamDiffusionFastSampler", + "StreamDiffusionSampler", + "StreamDiffusionWarmup" + ], + { + "title_aux": "ComfyUI-Diffusers" + } + ], + "https://github.com/Ling-APE/ComfyUI-PixelResolutionCalculator": [ + [ + "LatentSizeToPixelSize", + "PixelResolutionCalculator" + ], + { + "title_aux": "ComfyUI-PixelResolutionCalculator" + } + ], + "https://github.com/LingSss9/comfyui-merge": [ + [ + "MergeLoRAsKohyaSSLike", + "OnlyLoadLoRAsModel", + "SaveLoRAModels" + ], + { + "author": "cyberblackcat", + "description": "This extension provides some nodes to support merge lora, adjust Lora Block Weight.", + "nickname": "CBC", + "title": "merge", + "title_aux": "Comfyui-Merge-LoRA" + } + ], + "https://github.com/Loewen-Hob/rembg-comfyui-node-better": [ + [ + "Image Remove Background (rembg)" + ], + { + "title_aux": "Rembg Background Removal Node for ComfyUI (Better)" + } + ], + "https://github.com/LonicaMewinsky/ComfyUI-MakeFrame": [ + [ + "BreakFrames", + "BreakGrid", + "GetKeyFrames", + "MakeGrid", + "RandomImageFromDir" + ], + { + "title_aux": "ComfyBreakAnim" + } + ], + "https://github.com/LonicaMewinsky/ComfyUI-RawSaver": [ + [ + "SaveTifImage" + ], + { + "title_aux": "ComfyUI-RawSaver" + } + ], + "https://github.com/LoveEatCandy/COMFYUI-ReplacePartOfImage": [ + [ + "ReplacePartOfImage" + ], + { + "title_aux": "COMFYUI-ReplacePartOfImage" + } + ], + "https://github.com/Ltamann/ComfyUI-TBG-ETUR": [ + [ + "AIO_Preprocessor", + "AddFluxFlow", + "AnimeFace_SemSegPreprocessor", + "AnimeLineArtPreprocessor", + "AnyLineArtPreprocessor_aux", + "ApplyFluxRaveAttention", + "ApplyRefFlux", + "ApplyRegionalConds", + "BAE-NormalMapPreprocessor", + "BinaryPreprocessor", + "CannyEdgePreprocessor", + "ColorPreprocessor", + "ConfigureModifiedFlux", + "ControlNetAuxSimpleAddText", + "ControlNetPreprocessorSelector", + "CreateRegionalCond", + "DSINE-NormalMapPreprocessor", + "DepthAnythingPreprocessor", + "DepthAnythingV2Preprocessor", + "DiffusionEdge_Preprocessor", + "EdgePadNode", + "ExecuteAllControlNetPreprocessors", + "FakeScribblePreprocessor", + "FlowEditForwardSampler", + "FlowEditGuider", + "FlowEditReverseSampler", + "FlowEditSampler", + "FluxAttnOverride", + "FluxDeGuidance", + "FluxForwardODESampler", + "FluxInverseSampler", + "FluxNoiseMixer", + "FluxReverseODESampler", + "HEDPreprocessor", + "HintImageEnchance", + "ImageGenResolutionFromImage", + "ImageGenResolutionFromLatent", + "ImageIntensityDetector", + "ImageLuminanceDetector", + "InFluxFlipSigmas", + "InFluxModelSamplingPred", + "InpaintPreprocessor", + "JanusImageGeneration", + "JanusImageUnderstanding", + "JanusModelLoader", + "LeReS-DepthMapPreprocessor", + "LineArtPreprocessor", + "LineartStandardPreprocessor", + "M-LSDPreprocessor", + "Manga2Anime_LineArt_Preprocessor", + "MaskOptFlow", + "MediaPipe-FaceMeshPreprocessor", + "MeshGraphormer+ImpactDetector-DepthMapPreprocessor", + "MeshGraphormer-DepthMapPreprocessor", + "Metric3D-DepthMapPreprocessor", + "Metric3D-NormalMapPreprocessor", + "Metric_DepthAnythingV2Preprocessor", + "MiDaS-DepthMapPreprocessor", + "MiDaS-NormalMapPreprocessor", + "OneFormer-ADE20K-SemSegPreprocessor", + "OneFormer-COCO-SemSegPreprocessor", + "OpenposePreprocessor", + "OutFluxModelSamplingPred", + "PAGAttention", + "PatreonStatusCheck", + "PiDiNetPreprocessor", + "PixelPerfectResolution", + "PrepareAttnBank", + "PyraCannyPreprocessor", + "RFDoubleBlocksOverride", + "RFSingleBlocksOverride", + "RegionalStyleModelApply", + "SAMPreprocessor", + "SEGAttention", + "ScribblePreprocessor", + "Scribble_PiDiNet_Preprocessor", + "Scribble_XDoG_Preprocessor", + "SemSegPreprocessor", + "ShufflePreprocessor", + "TBG_masked_attention", + "TEEDPreprocessor", + "TTPlanet_TileGF_Preprocessor", + "TTPlanet_TileSimple_Preprocessor", + "TilePreprocessor", + "UniFormer-SemSegPreprocessor", + "Unimatch_OptFlowPreprocessor", + "UnloadOneModel", + "Zoe-DepthMapPreprocessor", + "Zoe_DepthAnythingPreprocessor" + ], + { + "title_aux": "TBG_Enhanced Tiled Upscaler & Refiner FLUX PRO" + } + ], + "https://github.com/Ltamann/ComfyUI-TBG-Takeaways": [ + [ + "BasicSchedulerNormalized", + "LogSigmaSamplerNode", + "LogSigmaStepSamplerNode", + "ModelSamplingFluxGradual", + "PolyExponentialSigmaAdder", + "TBG_FluxKontextStabilizer" + ], + { + "title_aux": "TBG\u2019s ComfyUI Development Takeaways" + } + ], + "https://github.com/LucipherDev/ComfyUI-AniDoc": [ + [ + "AniDocLoader", + "AniDocSampler", + "GetAniDocControlnetImages", + "LoadCoTracker" + ], + { + "title_aux": "ComfyUI-AniDoc" + } + ], + "https://github.com/LucipherDev/ComfyUI-Golden-Noise": [ + [ + "GoldenNoise" + ], + { + "title_aux": "ComfyUI-Golden-Noise" + } + ], + "https://github.com/LucipherDev/ComfyUI-TangoFlux": [ + [ + "TangoFluxLoader", + "TangoFluxSampler", + "TangoFluxVAEDecodeAndPlay" + ], + { + "title_aux": "ComfyUI-TangoFlux" + } + ], + "https://github.com/Ludobico/ComfyUI-ScenarioPrompt": [ + [ + "ScenarioPrompt" + ], + { + "title_aux": "ComfyUI-ScenarioPrompt" + } + ], + "https://github.com/LyazS/comfyui-anime-seg": [ + [ + "Anime Character Seg" + ], + { + "title_aux": "Anime Character Segmentation node for comfyui" + } + ], + "https://github.com/LyazS/comfyui-nettools": [ + [ + "NTL_LoadImagesBase64", + "NTL_SendImagesWebSocket" + ], + { + "title_aux": "net tool node for comfyui" + } + ], + "https://github.com/M1kep/ComfyLiterals": [ + [ + "Checkpoint", + "Float", + "Int", + "KepStringLiteral", + "Lora", + "Operation", + "String" + ], + { + "title_aux": "ComfyLiterals" + } + ], + "https://github.com/M1kep/ComfyUI-KepOpenAI": [ + [ + "KepOpenAI_ImageWithPrompt" + ], + { + "title_aux": "ComfyUI-KepOpenAI" + } + ], + "https://github.com/M1kep/ComfyUI-OtherVAEs": [ + [ + "OtherVAE_Taesd" + ], + { + "title_aux": "ComfyUI-OtherVAEs" + } + ], + "https://github.com/M1kep/Comfy_KepKitchenSink": [ + [ + "KepRotateImage" + ], + { + "title_aux": "Comfy_KepKitchenSink" + } + ], + "https://github.com/M1kep/Comfy_KepListStuff": [ + [ + "Empty Images", + "Image Overlay", + "ImageListLoader", + "Join Float Lists", + "Join Image Lists", + "KepStringList", + "KepStringListFromNewline", + "Kep_JoinListAny", + "Kep_RepeatList", + "Kep_ReverseList", + "Kep_VariableImageBuilder", + "List Length", + "Range(Num Steps) - Float", + "Range(Num Steps) - Int", + "Range(Step) - Float", + "Range(Step) - Int", + "Stack Images", + "XYAny", + "XYImage" + ], + { + "title_aux": "Comfy_KepListStuff" + } + ], + "https://github.com/M1kep/Comfy_KepMatteAnything": [ + [ + "MatteAnything_DinoBoxes", + "MatteAnything_GenerateVITMatte", + "MatteAnything_InitSamPredictor", + "MatteAnything_LoadDINO", + "MatteAnything_LoadVITMatteModel", + "MatteAnything_SAMLoader", + "MatteAnything_SAMMaskFromBoxes", + "MatteAnything_ToTrimap" + ], + { + "title_aux": "Comfy_KepMatteAnything" + } + ], + "https://github.com/M1kep/KepPromptLang": [ + [ + "Build Gif", + "Special CLIP Loader" + ], + { + "title_aux": "KepPromptLang" + } + ], + "https://github.com/MDMAchine/ComfyUI_MD_Nodes": [ + [ + "ACE_LatentVisualizer", + "APGGuiderForked", + "AdvancedAudioPreviewAndSave", + "HybridAdaptiveSigmas", + "MasteringChainNode", + "NoiseDecayScheduler_Custom", + "PingPongSampler_Custom", + "PingPongSampler_Custom_FBG", + "SceneGeniusAutocreator", + "SeedSaver" + ], + { + "title_aux": "MD Nodes" + } + ], + "https://github.com/MNeMoNiCuZ/ComfyUI-mnemic-nodes": [ + [ + "LoraTagLoader", + "ResolutionSelector", + "StringCleaning", + "StringTextExtractor", + "StringTextSplitter", + "TiktokenTokenizer", + "WildcardProcessor", + "\u26d4 Generate Negative Prompt", + "\u2702\ufe0f String Text Extractor", + "\u2702\ufe0f String Text Splitter", + "\u2728\ud83c\udf10 Groq ALM API - Translate [EN only]", + "\u2728\ud83d\udcac Groq LLM API", + "\u2728\ud83d\udcdd Groq ALM API - Transcribe", + "\u2728\ud83d\udcf7 Groq VLM API", + "\ud83c\udff7\ufe0f LoRA Loader Prompt Tags", + "\ud83d\udcbe Save Text File With Path", + "\ud83d\udcc1 Get File Path", + "\ud83d\udcd0 Resolution Image Size Selector", + "\ud83d\udcdd Wildcard Processor", + "\ud83d\udd20 Tiktoken Tokenizer Info", + "\ud83d\uddbc\ufe0f Download Image from URL", + "\ud83e\uddf9 String Cleaning" + ], + { + "title_aux": "ComfyUI-mnemic-nodes" + } + ], + "https://github.com/Makeezi/ComfyUI-promptLAB": [ + [ + "PromptLAB" + ], + { + "title_aux": "ComfyUI-promptLAB" + } + ], + "https://github.com/MakkiShizu/ComfyUI-Prompt-Wildcards": [ + [ + "makitextwildcards", + "makiwildcards", + "textconcatenate", + "textconcatenate_v2" + ], + { + "title_aux": "ComfyUI-Prompt-Wildcards" + } + ], + "https://github.com/MakkiShizu/ComfyUI-Qwen2_5-VL": [ + [ + "BatchImageLoaderToLocalFiles", + "DownloadAndLoadQwen2_5_VLModel", + "Qwen2_5_VL_Run", + "Qwen2_5_VL_Run_Advanced" + ], + { + "title_aux": "ComfyUI-Qwen2_5-VL" + } + ], + "https://github.com/MakkiShizu/comfyui_reimgsize": [ + [ + "Cropimg", + "Reimgsize", + "Resizebyratio" + ], + { + "title_aux": "comfyui_reimgsize" + } + ], + "https://github.com/Mamaaaamooooo/batchImg-rembg-ComfyUI-nodes": [ + [ + "Image Remove Background (rembg)" + ], + { + "title_aux": "Batch Rembg for ComfyUI" + } + ], + "https://github.com/ManglerFTW/ComfyI2I": [ + [ + "Color Transfer", + "Combine and Paste", + "Inpaint Segments", + "Mask Ops" + ], + { + "author": "ManglerFTW", + "title": "ComfyI2I", + "title_aux": "ComfyI2I" + } + ], + "https://github.com/MarcusNyne/m9-prompts-comfyui": [ + [ + "ScramblePrompts_m9", + "TweakWeights_m9" + ], + { + "title_aux": "m9-prompts-comfyui" + } + ], + "https://github.com/MariusKM/ComfyUI-BadmanNodes": [ + [ + "BadmanBrightness", + "BadmanCLIPTextEncodeSDXLRegion", + "BadmanDesaturate", + "BadmanDilateErodeMask", + "BadmanIO", + "BadmanIntUtil", + "BadmanMaskBlur", + "BadmanStringSelect", + "BadmanStringToInteger", + "BadmanWildCardProcessor", + "Badman_Blend", + "Badman_ColorTransferLab", + "Badman_Concat_String", + "Badman_HexGenerator", + "Badman_PalletteGenerator", + "Badman_Print", + "Badman_String" + ], + { + "title_aux": "ComfyUI-BadmanNodes" + } + ], + "https://github.com/MarkoCa1/ComfyUI-Text": [ + [ + "CombinationText", + "PlaceholderText", + "ReplaceText", + "ShowText" + ], + { + "title_aux": "ComfyUI-Text" + } + ], + "https://github.com/MarkoCa1/ComfyUI_Segment_Mask": [ + [ + "AutomaticMask(segment anything)" + ], + { + "title_aux": "ComfyUI_Segment_Mask" + } + ], + "https://github.com/Marksusu/ComfyUI_MTCLIPEncode": [ + [ + "MTCLIPEncode" + ], + { + "title_aux": "ComfyUI_MTCLIPEncode" + } + ], + "https://github.com/MartinDeanMoriarty/ComfyUI-DeanLogic": [ + [ + "ImageCount", + "ImageInputSwitch", + "ImageOutputSwitch", + "Int Compare" + ], + { + "title_aux": "ComfyUI-DeanLogic" + } + ], + "https://github.com/MaruPelkar/comfyui-conditioning-resizer": [ + [ + "ConditioningResizer" + ], + { + "title_aux": "ComfyUI Conditioning Resizer" + } + ], + "https://github.com/Mason-McGough/ComfyUI-Mosaica": [ + [ + "ApplyLUTToLabelImage", + "KMeans", + "LoadLUTFromMatplotlib", + "MeanShift", + "RandomLUT", + "Watershed" + ], + { + "title_aux": "Mosaica" + } + ], + "https://github.com/MasterDenis/VAE-Decode-Switch": [ + [ + "VAEDecodeSwitcher" + ], + { + "title_aux": "VAE Decode Switch for ComfyUI" + } + ], + "https://github.com/Mattabyte/ComfyUI-SecureApiCall": [ + [ + "SaveLatentToS3", + "SaveVideoFilesS3", + "SecureAPI-SecureAPI", + "SecureAPI-SecureAPI-AWS" + ], + { + "title_aux": "ComfyUI Secure API Call" + } + ], + "https://github.com/Maxed-Out-99/ComfyUI-MaxedOut": [ + [ + "Crop Image By Mask", + "Flux Empty Latent Image", + "Flux Image Scale To Total Pixels (Flux Safe)", + "Flux Resolution Selector", + "FluxResolutionMatcher", + "Image Scale To Total Pixels (SDXL Safe)", + "LatentHalfMasks", + "Place Image By Mask", + "Prompt With Guidance (Flux)", + "Sdxl Empty Latent Image", + "Sdxl Resolution Selector" + ], + { + "title_aux": "ComfyUI-MaxedOut" + } + ], + "https://github.com/McKlinton2/comfyui-mcklinton-pack": [ + [ + "ColormaskNode", + "LoadFilteredImageBatch", + "MultiLayerComposeNode", + "SaveTextArrayToFiles" + ], + { + "title_aux": "ComfyUI McKlinton Pack \u2014 Mask Node" + } + ], + "https://github.com/Mcmillian/ComfyUI-SimpleToolsNodes": [ + [ + "GetModelStep", + "GlmPromptNode" + ], + { + "title_aux": "SimpleToolsNodes" + } + ], + "https://github.com/MeeeyoAI/ComfyUI_StringOps": [ + [ + "AddPrefixSuffix", + "AddPrefixSuffixToLines", + "BatchReplaceStrings", + "CheckSubstringPresence", + "CompareInt", + "ConditionalTextOutput", + "CountOccurrences", + "CustomCrop", + "DecodePreview", + "ExtractAndCombineLines", + "ExtractBeforeAfter", + "ExtractLinesByIndex", + "ExtractSpecificData", + "ExtractSpecificLines", + "ExtractSubstring", + "ExtractSubstringByIndices", + "FileCopyCutNode", + "FileDeleteNode", + "FileListAndSuffix", + "FileNameReplacer", + "FilterLinesBySubstrings", + "FilterLinesByWordCount", + "FindExcelData", + "FindFirstLineContent", + "FloatToInteger", + "GenerateNumbers", + "GenerateVideoPrompt", + "GenericImageLoader", + "GetCurrentTime", + "GetFloatParam", + "GetIntParam", + "GetRandomIntegerInRange", + "ImageAdjuster", + "ImageOverlayAlignment", + "LoadAndAdjustImage", + "MultiParamInputNode", + "NumberExtractor", + "ProcessString", + "RandomLineFromText", + "ReadExcelData", + "ReadExcelRowOrColumnDiff", + "ReadWebNode", + "RemoveContentBetweenChars", + "ReplaceMultiple", + "ReplaceNthOccurrence", + "SaveImagEX", + "SelectionParameter", + "ShuffleTextLines", + "SimpleRandomSeed", + "SimpleTextReplacer", + "SingleTextInput", + "SplitAndExtractText", + "SplitStringByDelimiter", + "TextConcatenation", + "TextConcatenator", + "TextConditionCheck", + "TextToImage", + "TextToList", + "WriteExcelData", + "WriteExcelImage", + "WriteToTxtFile" + ], + { + "title_aux": "ComfyUI_StringOps" + } + ], + "https://github.com/Meettya/ComfyUI-OneForOne": [ + [ + "OFO Image Fit" + ], + { + "title_aux": "ComfyUI-OneForOne" + } + ], + "https://github.com/MetaGLM/ComfyUI-ZhipuAI-Platform": [ + [ + "VideoReportData", + "VideoReportGenerate", + "VideoReportPull" + ], + { + "title_aux": "ComfyUI ZhipuAI Platform" + } + ], + "https://github.com/MicheleGuidi/ComfyUI-Contextual-SAM2": [ + [ + "Sam2ContextSegmentation", + "Sam2TiledSegmentation" + ], + { + "title_aux": "ComfyUI-Computer-Vision" + } + ], + "https://github.com/MiddleKD/ComfyUI-denoise-mask-scheduler": [ + [ + "ApplyDenoiseMaskSchedulerWithSigma", + "ApplyDenoiseMaskSchedulerWithStep", + "DynamicImageResize" + ], + { + "title_aux": "ComfyUI-denoise-mask-scheduler" + } + ], + "https://github.com/MiddleKD/ComfyUI-mem-safe-wrapper": [ + [ + "MakeModelMemorySafe-safewrapper", + "ResetModelPatcher-safewrapper", + "SimpleDummyModel-safewrapper", + "SimpleDummyRun-safewrapper" + ], + { + "title_aux": "ComfyUI-mem-safe-wrapper" + } + ], + "https://github.com/MiddleKD/ComfyUI-productfix": [ + [ + "ApplyLatentInjection", + "DetailTransferAdd", + "DetailTransferLatentAdd", + "DynamicImageResize", + "GetTextMask", + "ResetModelPatcherCalculateWeight", + "VQDecoder", + "VQEncoder", + "VQLoader" + ], + { + "title_aux": "ComfyUI-productfix" + } + ], + "https://github.com/MijnSpam/ComfyUI_SwapAndScale": [ + [ + "SwapAndScale" + ], + { + "title_aux": "Comfy swap and scale" + } + ], + "https://github.com/MijnSpam/UploadToPushOver": [ + [ + "UploadToPushOver" + ], + { + "title_aux": "Upload to PushOver" + } + ], + "https://github.com/MilitantHitchhiker/MilitantHitchhiker-SwitchbladePack": [ + [ + "FluxModelSave_v2", + "IntegratedRandomPromptGenerator", + "ModelAnalyserNode", + "TextAppender_v2" + ], + { + "author": "Militant Hitchhiker", + "description": "Militant Hitchhiker's multi-function nodes.", + "nickname": "Switchblade", + "title": "Switchblade Pack", + "title_aux": "MilitantHitchhiker-SwitchbladePack" + } + ], + "https://github.com/Mintbeer96/ComfyUI-KerasOCR": [ + [ + "KerasOCR" + ], + { + "title_aux": "ComfyUI-KerasOCR" + } + ], + "https://github.com/Miosp/ComfyUI-FBCNN": [ + [ + "JPEG artifacts removal FBCNN" + ], + { + "title_aux": "ComfyUI-FBCNN" + } + ], + "https://github.com/MitoshiroPJ/ComfyUI_save_image_sdli": [ + [ + "PreviewSdlImage", + "SaveSdlImage" + ], + { + "title_aux": "ComfyUI SaveImage SDLI" + } + ], + "https://github.com/MitoshiroPJ/comfyui_nearsighted_attention": [ + [ + "NearSightedAttention", + "NearSightedAttentionSimple", + "NearSightedTile", + "SlothfulAttention" + ], + { + "title_aux": "ComfyUI Nearsighted Attention" + } + ], + "https://github.com/Miyuutsu/comfyui-save-vpred": [ + [ + "CheckpointSaveVpred" + ], + { + "author": "miyuu", + "description": "Used to save SDXL V-Prediction models directly with correct tensors.", + "nickname": "vpred-save", + "title": "vpred-save", + "title_aux": "comfyui-save-vpred" + } + ], + "https://github.com/MohammadAboulEla/ComfyUI-iTools": [ + [ + "iToolsAddOverlay", + "iToolsCheckerBoard", + "iToolsCompareImage", + "iToolsGridFiller", + "iToolsKSampler", + "iToolsLineLoader", + "iToolsLoadImagePlus", + "iToolsLoadImages", + "iToolsLoadRandomImage", + "iToolsPreviewImage", + "iToolsPreviewText", + "iToolsPromptLoader", + "iToolsPromptRecord", + "iToolsPromptSaver", + "iToolsPromptStyler", + "iToolsPromptStylerExtra", + "iToolsRegexNode", + "iToolsTextReplacer", + "iToolsVaePreview" + ], + { + "title_aux": "ComfyUI-iTools" + } + ], + "https://github.com/MokkaBoss1/ComfyUI_Mokkaboss1": [ + [ + "AnimeCosplayDir", + "AspectRatioCondition", + "ChooseImage", + "Colors", + "CombinedCrop", + "ConnectFloat", + "ConnectImage", + "ConnectInteger", + "ConnectInteger2", + "ConnectLatent", + "ConnectString", + "CycleInteger", + "DirSelector", + "DoubleClipTextEncode", + "DoubleConditioningMixer", + "EmbeddingLoader", + "FilmCharDir", + "FlexEmptyLatent", + "FloatEvaluate", + "FuseImages", + "FuseImages2", + "HashText", + "HueSatLum", + "HueShift", + "ImageDimensions", + "ImageDimensionsBatch", + "ImageOverlayResized", + "ImageResizeLong", + "ImageZigzag", + "IndoorBackgrounds", + "IndoorDir", + "IntEvaluate", + "IntFloatDict", + "IntStringDict", + "JsonSearch", + "KillWorkflow", + "LandscapeBackgrounds", + "LandscapeDir", + "LinEqEval", + "MakeupStylesDir", + "Mbsampler", + "OptimalCrop", + "Overlay", + "PhotomontageA", + "PhotomontageB", + "PhotomontageC", + "PostSamplerCrop", + "PresetLoad", + "PresetRemove", + "PresetSave", + "PromptSwitcher", + "QuadClipTextEncode", + "RandomString", + "SDXLEmptyLatent", + "SavePrompt", + "SaveWithMetaData", + "SaveWithMetaData2", + "SearchReplace", + "SimplePrompts", + "SpecificStylesDir", + "SplitImages", + "StringJoin", + "TimeStamp", + "TintnShift", + "TricolorComposition", + "WorkflowSettings", + "WrapText", + "X_In_a_Dress", + "X_In_a_Suit", + "X_In_a_Suit)", + "ZoomCrop", + "imageborder" + ], + { + "title_aux": "Node Pack mostly for manipulating strings and integers" + } + ], + "https://github.com/MontagenAI/ComfyUI-Montagen": [ + [ + "MontagenAudioAdapter", + "MontagenAudioConvertResourceAdapter", + "MontagenAudioListAdapter", + "MontagenCreateTimeline", + "MontagenEdgeTTSNode", + "MontagenFishAudioCloneNode", + "MontagenFishAudioTTSNode", + "MontagenImageAdapter", + "MontagenImageListAdapter", + "MontagenRenderTimeline", + "MontagenResourceConvertAudioAdapter", + "MontagenSRTListParser", + "MontagenStickerAdapter", + "MontagenStickerListAdapter", + "MontagenTextAdapter", + "MontagenTextListAdapter", + "MontagenVideoAdapter", + "MontagenVideoListAdapter" + ], + { + "title_aux": "ComfyUI-Montagen" + } + ], + "https://github.com/MoonHugo/ComfyUI-BAGEL-Hugo": [ + [ + "BagelByHugo" + ], + { + "title_aux": "ComfyUI-BAGEL-Hugo" + } + ], + "https://github.com/MoonHugo/ComfyUI-BiRefNet-Hugo": [ + [ + "BiRefNet_Hugo" + ], + { + "title_aux": "ComfyUI-BiRefNet-Hugo" + } + ], + "https://github.com/MoonHugo/ComfyUI-FFmpeg": [ + [ + "AddAudio", + "AddImgWatermark", + "AddTextWatermark", + "ExtractAudio", + "Frames2Video", + "ImageCopy", + "ImagePath2Tensor", + "ImagesSave", + "LoadImageFromDir", + "MergingVideoByPlenty", + "MergingVideoByTwo", + "MultiCuttingVideo", + "PipVideo", + "SingleCuttingVideo", + "StitchingVideo", + "Video2Frames", + "VideoFlip", + "VideoTransition" + ], + { + "title_aux": "ComfyUI-FFmpeg" + } + ], + "https://github.com/MoonHugo/ComfyUI-StableAudioOpen": [ + [ + "Text2Audio" + ], + { + "title_aux": "ComfyUI-StableAudioOpen" + } + ], + "https://github.com/MovieLabs/comfyui-movielabs-util": [ + [ + "PublishAsset", + "PublishBlender" + ], + { + "title_aux": "MovieLabs ComfyUI Nodes for Publishing Workflow" + } + ], + "https://github.com/MrForExample/ComfyUI-3D-Pack": [ + [], + { + "nodename_pattern": "^\\[Comfy3D\\]", + "title_aux": "ComfyUI-3D-Pack" + } + ], + "https://github.com/MrForExample/ComfyUI-AnimateAnyone-Evolved": [ + [], + { + "nodename_pattern": "^\\[AnimateAnyone\\]", + "title_aux": "ComfyUI-AnimateAnyone-Evolved" + } + ], + "https://github.com/MrSamSeen/ComfyUI_SSBeforeAfterNode": [ + [ + "SSBeforeAndAfterVideo", + "SSBeforeAndAfterVideoWithDepthMap" + ], + { + "title_aux": "ComfyUI_SSBeforeAfterNode" + } + ], + "https://github.com/MrSamSeen/ComfyUI_SSStereoscope": [ + [ + "SBS_External_Depthmap_by_SamSeen", + "SBS_Image_Uploader", + "SBS_V2_by_SamSeen", + "SBS_Video_Combiner", + "SBS_Video_Uploader" + ], + { + "title_aux": "SideBySide_Stereoscope" + } + ], + "https://github.com/Munkyfoot/ComfyUI-TextOverlay": [ + [ + "Text Overlay" + ], + { + "title_aux": "ComfyUI-TextOverlay" + } + ], + "https://github.com/MuziekMagie/ComfyUI-Matchering": [ + [ + "Matchering", + "MatcheringAdvanced", + "MatcheringLimiterConfig" + ], + { + "title_aux": "ComfyUI-Matchering" + } + ], + "https://github.com/MzMaXaM/ComfyUi-MzMaXaM": [ + [ + "KSamplerWithVAE", + "SelectLatentSize1MP", + "SelectLatentSize2MP", + "TextEncode3in1", + "UpscaleImageBy1_5x", + "UpscaleLatentBy1_5x", + "selectLatentSizePlus" + ], + { + "title_aux": "ComfyUi-MzMaXaM" + } + ], + "https://github.com/N3rd00d/ComfyUI-Paint3D-Nodes": [ + [ + "3D_GenerateDepthImage", + "3D_GenerateInpaintMask", + "3D_GenerateInpaintUVMapMask", + "3D_GeneratePreviewVideo", + "3D_LoadMeshModel", + "3D_Projection", + "3D_SaveUVMapImage", + "3D_TrainConfig", + "3D_TrainConfigPipe" + ], + { + "title_aux": "ComfyUI-Paint3D-Nodes" + } + ], + "https://github.com/NMWave/ComfyUI-Nader-Tagging": [ + [ + "Load Text List", + "Split Sentences", + "Split Tags", + "Tag Alternating Combiner", + "Tag Duplicate Remover", + "Token Counter" + ], + { + "title_aux": "Image Captioning and Tagging Assistor Nodes" + } + ], + "https://github.com/NVIDIAGameWorks/ComfyUI-RTX-Remix": [ + [ + "RTXRemixCloseProject", + "RTXRemixCreateLayer", + "RTXRemixDefineLayerId", + "RTXRemixDeleteFile", + "RTXRemixEndContext", + "RTXRemixGetDefaultDirectory", + "RTXRemixGetEditTarget", + "RTXRemixGetLayers", + "RTXRemixGetLoadedProject", + "RTXRemixGetTextures", + "RTXRemixIngestTexture", + "RTXRemixInvertBool", + "RTXRemixLayerType", + "RTXRemixLayerTypes", + "RTXRemixMuteLayer", + "RTXRemixOpenProject", + "RTXRemixRemoveLayer", + "RTXRemixRestAPIDetails", + "RTXRemixSaveLayer", + "RTXRemixSetEditTarget", + "RTXRemixSetTexture", + "RTXRemixStartContext", + "RTXRemixStrToList", + "RTXRemixStringConcatenate", + "RTXRemixStringConstant", + "RTXRemixSwitch", + "RTXRemixTextureTypeToUSDAttribute", + "RTXRemixTexturesType", + "RTXRemixTexturesTypes" + ], + { + "title_aux": "ComfyUI-RTX-Remix" + } + ], + "https://github.com/NakamuraShippo/ComfyUI-NS-ManySliders": [ + [ + "NS_ManySliders" + ], + { + "title_aux": "ComfyUI-NS-ManySliders" + } + ], + "https://github.com/NakamuraShippo/ComfyUI-NS-PromptList": [ + [ + "NS-PromptList" + ], + { + "title_aux": "ComfyUI-PromptList" + } + ], + "https://github.com/NakamuraShippo/ComfyUI-NS-Util": [ + [ + "NS-FlexPreset" + ], + { + "title_aux": "ComfyUI-NS-Util" + } + ], + "https://github.com/NeoDroleDeGueule/comfyui-image-mixer": [ + [ + "ImageLatentMixer" + ], + { + "title_aux": "comfyui-image-mixer" + } + ], + "https://github.com/NeoGriever/ComfyUI-NeoGriever": [ + [ + "NGs_BetterCLIPTextEncode", + "NGs_Checkerboard_Generator", + "NGs_Create_Solid_Color", + "NGs_Discord_Webhook", + "NGs_Fill_with_Color", + "NGs_Image_Progress_Bar", + "NGs_Multimask_Read", + "NGs_Multimask_Write", + "NGs_ResolutionProvider", + "NGs_Sliders_FLOAT", + "NGs_Sliders_INT", + "NGs_Sliders_PERCENTAGECUT", + "NGs_String_Operator", + "NGs_String_Squisher", + "NGs_Tag_Source", + "NGs_TextBox_JOIN", + "NGs_TextBox_SIMPLE", + "NGs_TextBox_x2", + "NGs_TextBox_x3", + "NGs_Text_Cut_String" + ], + { + "title_aux": "ComfyUI - NeoGriever" + } + ], + "https://github.com/NeonLightning/neonllama": [ + [ + "OllamaPromptFromIdea" + ], + { + "title_aux": "neonllama" + } + ], + "https://github.com/NeuralSamurAI/ComfyUI-Dimensional-Latent-Perlin": [ + [ + "NoisyLatentPerlinD" + ], + { + "title_aux": "Dimensional Latent Perlin for ComfyUI" + } + ], + "https://github.com/NeuralSamurAI/ComfyUI-FluxPseudoNegativePrompt": [ + [ + "FluxPseudoNegativeNode" + ], + { + "title_aux": "FluxPseudoNegative" + } + ], + "https://github.com/NeuralSamurAI/ComfyUI-PromptJSON": [ + [ + "PromptJSON" + ], + { + "title_aux": "PromptJSON Node for ComfyUI" + } + ], + "https://github.com/NeuralSamurAI/Comfyui-Superprompt-Unofficial": [ + [ + "SuperPrompterNode" + ], + { + "title_aux": "SuperPrompter Node for ComfyUI" + } + ], + "https://github.com/NeuroSenko/ComfyUI_LLM_SDXL_Adapter": [ + [ + "ApplyLLMToSDXLAdapter", + "LLMAdapterLoader", + "LLMModelLoader", + "LLMTextEncoder" + ], + { + "title_aux": "ComfyUI LLM SDXL Adapter" + } + ], + "https://github.com/NguynHungNguyen/Segment-Bedroom-Interior": [ + [ + "BedroomFurnitureMask" + ], + { + "title_aux": "Segment Any Bedroom Interior" + } + ], + "https://github.com/NicholasMcCarthy/ComfyUI_TravelSuite": [ + [ + "LatentTravel" + ], + { + "title_aux": "ComfyUI_TravelSuite" + } + ], + "https://github.com/Nikosis/ComfyUI-Nikosis-Nodes": [ + [ + "AspectRatioNikosis", + "PromptCameraAngleSelectorNikosis", + "PromptMultipleStylesSelectorNikosis", + "TextConcatenateNikosis" + ], + { + "title_aux": "ComfyUI-Nikosis-Nodes" + } + ], + "https://github.com/Nikosis/ComfyUI-Nikosis-Preprocessors": [ + [ + "DepthAnythingV2Nikosis", + "EdgePreprocessorNikosis", + "LaplacianPreprocessorNikosis", + "LineArtPreprocessorNikosis", + "LineArtSketchPreprocessorNikosis" + ], + { + "title_aux": "ComfyUI-Nikosis-Preprocessors" + } + ], + "https://github.com/NimaNzrii/comfyui-photoshop": [ + [ + "\ud83d\udd39 Photoshop RemoteConnection", + "\ud83d\udd39ClipPass", + "\ud83d\udd39Photoshop ComfyUI Plugin", + "\ud83d\udd39SendTo Photoshop Plugin", + "\ud83d\udd39modelPass" + ], + { + "title_aux": "comfyui-photoshop" + } + ], + "https://github.com/NimaNzrii/comfyui-popup_preview": [ + [ + "PreviewPopup" + ], + { + "title_aux": "comfyui-popup_preview" + } + ], + "https://github.com/Niutonian/ComfyUi-NoodleWebcam": [ + [ + "WebcamNode" + ], + { + "title_aux": "ComfyUi-NoodleWebcam" + } + ], + "https://github.com/Njbx/ComfyUI-LTX13B-Blockswap": [ + [ + "LTXBlockswap" + ], + { + "title_aux": "ComfyUI-LTX13B-Blockswap" + } + ], + "https://github.com/Nlar/ComfyUI_CartoonSegmentation": [ + [ + "AnimeSegmentation", + "KenBurnsConfigLoader", + "KenBurns_Processor", + "LoadImageFilename" + ], + { + "author": "Nels Larsen", + "description": "This extension offers a front end to the Cartoon Segmentation Project (https://github.com/CartoonSegmentation/CartoonSegmentation)", + "nickname": "CfyCS", + "title": "ComfyUI_CartoonSegmentation", + "title_aux": "ComfyUI_CartoonSegmentation" + } + ], + "https://github.com/Nojahhh/ComfyUI_GLM4_Wrapper": [ + [ + "GLM-4 Inferencing", + "GLM-4 Model Loader", + "GLM-4 Prompt Enhancer" + ], + { + "title_aux": "ComfyUI GLM-4 Wrapper" + } + ], + "https://github.com/NotHarroweD/Harronode": [ + [ + "Harronode" + ], + { + "author": "HarroweD and quadmoon (https://github.com/traugdor)", + "description": "This extension to ComfyUI will build a prompt for the Harrlogos LoRA for SDXL.", + "nickname": "Harronode", + "nodename_pattern": "Harronode", + "title": "Harrlogos Prompt Builder Node", + "title_aux": "Harrlogos Prompt Builder Node" + } + ], + "https://github.com/Nourepide/ComfyUI-Allor": [ + [ + "AlphaChanelAdd", + "AlphaChanelAddByMask", + "AlphaChanelAsMask", + "AlphaChanelRemove", + "AlphaChanelRestore", + "ClipClamp", + "ClipVisionClamp", + "ClipVisionOutputClamp", + "ConditioningClamp", + "ControlNetClamp", + "GligenClamp", + "ImageBatchCopy", + "ImageBatchFork", + "ImageBatchGet", + "ImageBatchJoin", + "ImageBatchPermute", + "ImageBatchRemove", + "ImageClamp", + "ImageCompositeAbsolute", + "ImageCompositeAbsoluteByContainer", + "ImageCompositeRelative", + "ImageCompositeRelativeByContainer", + "ImageContainer", + "ImageContainerInheritanceAdd", + "ImageContainerInheritanceMax", + "ImageContainerInheritanceScale", + "ImageContainerInheritanceSum", + "ImageDrawArc", + "ImageDrawArcByContainer", + "ImageDrawChord", + "ImageDrawChordByContainer", + "ImageDrawEllipse", + "ImageDrawEllipseByContainer", + "ImageDrawLine", + "ImageDrawLineByContainer", + "ImageDrawPieslice", + "ImageDrawPiesliceByContainer", + "ImageDrawPolygon", + "ImageDrawRectangle", + "ImageDrawRectangleByContainer", + "ImageDrawRectangleRounded", + "ImageDrawRectangleRoundedByContainer", + "ImageEffectsAdjustment", + "ImageEffectsGrayscale", + "ImageEffectsLensBokeh", + "ImageEffectsLensChromaticAberration", + "ImageEffectsLensOpticAxis", + "ImageEffectsLensVignette", + "ImageEffectsLensZoomBurst", + "ImageEffectsNegative", + "ImageEffectsSepia", + "ImageFilterBilateralBlur", + "ImageFilterBlur", + "ImageFilterBoxBlur", + "ImageFilterContour", + "ImageFilterDetail", + "ImageFilterEdgeEnhance", + "ImageFilterEdgeEnhanceMore", + "ImageFilterEmboss", + "ImageFilterFindEdges", + "ImageFilterGaussianBlur", + "ImageFilterGaussianBlurAdvanced", + "ImageFilterMax", + "ImageFilterMedianBlur", + "ImageFilterMin", + "ImageFilterMode", + "ImageFilterRank", + "ImageFilterSharpen", + "ImageFilterSmooth", + "ImageFilterSmoothMore", + "ImageFilterStackBlur", + "ImageNoiseBeta", + "ImageNoiseBinomial", + "ImageNoiseBytes", + "ImageNoiseGaussian", + "ImageSegmentation", + "ImageSegmentationCustom", + "ImageSegmentationCustomAdvanced", + "ImageText", + "ImageTextMultiline", + "ImageTextMultilineOutlined", + "ImageTextOutlined", + "ImageTransformCropAbsolute", + "ImageTransformCropCorners", + "ImageTransformCropRelative", + "ImageTransformPaddingAbsolute", + "ImageTransformPaddingRelative", + "ImageTransformResizeAbsolute", + "ImageTransformResizeClip", + "ImageTransformResizeRelative", + "ImageTransformRotate", + "ImageTransformTranspose", + "LatentClamp", + "MaskClamp", + "ModelClamp", + "StyleModelClamp", + "UpscaleModelClamp", + "VaeClamp" + ], + { + "title_aux": "Allor Plugin" + } + ], + "https://github.com/Nuked88/ComfyUI-N-Nodes": [ + [ + "CLIPTextEncodeAdvancedNSuite [n-suite]", + "DynamicPrompt [n-suite]", + "Float Variable [n-suite]", + "FrameInterpolator [n-suite]", + "GPT Loader Simple [n-suite]", + "GPT Sampler [n-suite]", + "ImagePadForOutpaintAdvanced [n-suite]", + "Integer Variable [n-suite]", + "Llava Clip Loader [n-suite]", + "LoadFramesFromFolder [n-suite]", + "LoadImageFromFolder [n-suite]", + "LoadVideo [n-suite]", + "SaveVideo [n-suite]", + "SetMetadataForSaveVideo [n-suite]", + "String Variable [n-suite]" + ], + { + "title_aux": "ComfyUI-N-Nodes" + } + ], + "https://github.com/NyaFuP/ComfyUI_Preview_Selector": [ + [ + "NFPreviewSelector" + ], + { + "title_aux": "NF Preview Selector" + } + ], + "https://github.com/NyaamZ/efficiency-nodes-ED": [ + [ + "Context To BasicPipe", + "Context To DetailerPipe", + "Control Net Script \ud83d\udcacED", + "Detailer (SEGS) \ud83d\udcacED", + "Efficient Loader \ud83d\udcacED", + "Embedding Stacker \ud83d\udcacED", + "FaceDetailer \ud83d\udcacED", + "Get Booru Tag \ud83d\udcacED", + "Int Holder \ud83d\udcacED", + "KSampler (Efficient) \ud83d\udcacED", + "KSampler Text \ud83d\udcacED", + "LoRA Stacker \ud83d\udcacED", + "Load Image \ud83d\udcacED", + "MaskDetailer \ud83d\udcacED", + "Refiner Script \ud83d\udcacED", + "Regional Processor \ud83d\udcacED", + "Regional Script \ud83d\udcacED", + "Regional Stacker \ud83d\udcacED", + "SUPIR Model Loader \ud83d\udcacED", + "SUPIR Sampler \ud83d\udcacED", + "Save Image \ud83d\udd14ED", + "Simple Text \ud83d\udcacED", + "TIPO Script \ud83d\udcacED", + "Ultimate SD Upscale \ud83d\udcacED", + "Wildcard Encode \ud83d\udcacED" + ], + { + "author": "NyaamZ", + "description": "Expansion of Efficiency Nodes for ComfyUI. Significant UX improvements.", + "nickname": "Efficiency Nodes ED", + "title": "Efficiency Nodes ExtendeD", + "title_aux": "Efficiency Nodes ExtendeD" + } + ], + "https://github.com/Off-Live/ComfyUI-off-suite": [ + [ + "Apply CLAHE", + "Cached Image Load From URL", + "CalcMaskBound", + "Crop Center wigh SEGS", + "Crop Center with SEGS", + "Dilate Mask for Each Face", + "GW Number Formatting", + "Grid Image from batch (OFF)", + "Image Crop Fit", + "Image Resize Fit", + "OFF SEGS to Image", + "Paste Face Segment to Image", + "Query Gender and Age", + "RandomSeedfromList", + "SEGS to Face Crop Data", + "Safe Mask to Image", + "VAE Encode For Inpaint V2", + "Watermarking" + ], + { + "title_aux": "ComfyUI-off-suite" + } + ], + "https://github.com/OneThingAI/ComfyUI_Onething_CV": [ + [ + "OneThingAI ImageToText" + ], + { + "title_aux": "ComfyUI OneThing CV Node" + } + ], + "https://github.com/OneThingAI/ComfyUI_Onething_Image": [ + [ + "OneThingAILoader" + ], + { + "title_aux": "ComfyUI OneThing AI Node" + } + ], + "https://github.com/OpalSky-AI/OpalSky_Nodes": [ + [ + "PromptAssistantOpalSky", + "StringSwitchOpalSky", + "string_switch_opalsky" + ], + { + "title_aux": "OpalSky Nodes" + } + ], + "https://github.com/OuticNZ/ComfyUI-Simple-Of-Complex": [ + [ + "Pipe From Parameters", + "Pipe To Parameters", + "Prompt Tidy", + "Text Switch 2 Way", + "Text With Context" + ], + { + "title_aux": "ComfyUI-Simple-Of-Complex" + } + ], + "https://github.com/PCMonsterx/ComfyUI-CSV-Loader": [ + [ + "Load Artists CSV", + "Load Artmovements CSV", + "Load Characters CSV", + "Load Colors CSV", + "Load Composition CSV", + "Load Lighting CSV", + "Load Negative CSV", + "Load Positive CSV", + "Load Settings CSV", + "Load Styles CSV" + ], + { + "title_aux": "ComfyUI-CSV-Loader" + } + ], + "https://github.com/Pablerdo/ComfyUI-MultiCutAndDrag": [ + [ + "BatchImageToMask", + "LoadImageFromBase64", + "LoadImagesFromBase64Array", + "MapTrajectoriesToSegmentedMasks", + "MultiCutAndDragOnPath" + ], + { + "title_aux": "ComfyUI-MultiCutAndDrag" + } + ], + "https://github.com/Pablerdo/ComfyUI-ResizeZeptaPayload": [ + [ + "ResizeImageBatch", + "ResizeTrajectories" + ], + { + "title_aux": "ComfyUI-ResizeZeptaPayload" + } + ], + "https://github.com/Pablerdo/ComfyUI-StableVirtualCameraWrapper": [ + [ + "SVCFly", + "SVCFly_Bash" + ], + { + "title_aux": "Stable Virtual Camera" + } + ], + "https://github.com/Pablerdo/ComfyUI-ZeptaframePromptMerger": [ + [ + "MergePrompts" + ], + { + "title_aux": "ComfyUI-ZeptaframePromptMerger" + } + ], + "https://github.com/PanicTitan/ComfyUI-Fooocus-V2-Expansion": [ + [ + "FooocusV2Expansion" + ], + { + "title_aux": "ComfyUI-Fooocus-V2-Expansion" + } + ], + "https://github.com/PanicTitan/ComfyUI-Gallery": [ + [ + "GalleryNode" + ], + { + "title_aux": "ComfyUI-Gallery" + } + ], + "https://github.com/Parameshvadivel/ComfyUI-SVGview": [ + [ + "SVGPreview" + ], + { + "title_aux": "ComfyUI-SVGview" + } + ], + "https://github.com/ParisNeo/lollms_nodes_suite": [ + [ + "Artbot", + "Lollms_Text_Gen", + "Lollms_Text_Saver", + "RandomizeVideo" + ], + { + "title_aux": "lollms_nodes_suite" + } + ], + "https://github.com/ParmanBabra/ComfyUI-Malefish-Custom-Scripts": [ + [ + "CSVPromptsLoader", + "CombinePrompt", + "MultiLoraLoader", + "RandomPrompt" + ], + { + "title_aux": "ComfyUI-Malefish-Custom-Scripts" + } + ], + "https://github.com/PauldeLavallaz/comfyui_claude_prompt_generator": [ + [ + "ClaudeCustomPrompt" + ], + { + "title_aux": "Claude Prompt Generator" + } + ], + "https://github.com/PenguinTeo/Comfyui-TextEditor-Penguin": [ + [ + "PenguinTextOnImage" + ], + { + "title_aux": "Comfyui-TextEditor-Penguin" + } + ], + "https://github.com/Pfaeff/pfaeff-comfyui": [ + [ + "AstropulsePixelDetector", + "BackgroundRemover", + "ImagePadForBetterOutpaint", + "Inpainting", + "InpaintingPipelineLoader" + ], + { + "title_aux": "pfaeff-comfyui" + } + ], + "https://github.com/Phando/ComfyUI-PhandoNodes": [ + [ + "SearchBySubstring", + "TextConcatenateDynamic" + ], + { + "title_aux": "ComfyUI-PhandoNodes" + } + ], + "https://github.com/Pheat-AI/Remade_nodes": [ + [ + "Batch Enlarged Overlay", + "Batch Image Blend by Mask", + "Batch Image Overlay", + "CannyImageCropper", + "MaskBoundingBox", + "Place Canny On Canvas", + "REMADE Batch Color Blend", + "REMADE Batch Image Blend", + "REMADE Batch Image Composite Masked", + "REMADE Batch Image Select Channel", + "Remove Black To Transparent", + "Shrink Canny Image" + ], + { + "title_aux": "Remade_nodes" + } + ], + "https://github.com/PiggyDance/ComfyUI_OpenCV": [ + [ + "OpenCV_CvtColor", + "OpenCV_GaussianBlur", + "OpenCV_MedianBlur" + ], + { + "title_aux": "ComfyUI_OpenCV" + } + ], + "https://github.com/Pigidiy/ComfyUI-LikeSpiderAI-SaveMP3": [ + [ + "SaveAudioAsMP3" + ], + { + "title_aux": "ComfyUI-LikeSpiderAI-SaveMP3" + } + ], + "https://github.com/Pigidiy/ComfyUI-LikeSpiderAI-UI": [ + [ + "AudioExport" + ], + { + "title_aux": "ComfyUI-LikeSpiderAI-UI" + } + ], + "https://github.com/PixelFunAI/ComfyUI_PixelFun": [ + [ + "HunyuanLoadAndEditLoraBlocks", + "HunyuanLoadFromBlockCache", + "HunyuanLoraFromJson", + "HunyuanLoraFromPrompt" + ], + { + "title_aux": "Hunyuan LoRA Loader Nodes" + } + ], + "https://github.com/PixelML/ComfyUI-PixelML-CustomNodes": [ + [ + "AgenticflowAIVariable", + "BooleanInput_PixelML", + "FloatInput_PixelML", + "IntegerInput_PixelML", + "LoadImageFromURL_PixelML", + "SaveImage_PixelML", + "StringInput_PixelML" + ], + { + "title_aux": "PixelML ComfyUI Nodes" + } + ], + "https://github.com/PnthrLeo/comfyUI-PL-data-tools": [ + [ + "AreasGenerator", + "BatchImageGetter", + "CloseImagesSearcher" + ], + { + "title_aux": "comfyUI-PL-data-tools" + } + ], + "https://github.com/Poseidon-fan/ComfyUI-RabbitMQ-Publisher": [ + [ + "Publish Image To RabbitMQ" + ], + { + "title_aux": "ComfyUI-RabbitMQ-Publisher" + } + ], + "https://github.com/Positliver/comfyui-zegr": [ + [ + "ZEGR_ALI_UF", + "ZEGR_LF", + "ZEGR_WD" + ], + { + "title_aux": "comfyui-zegr" + } + ], + "https://github.com/PowerHouseMan/ComfyUI-AdvancedLivePortrait": [ + [ + "AdvancedLivePortrait", + "ExpData", + "ExpressionEditor", + "LoadExpData", + "PrintExpData:", + "SaveExpData" + ], + { + "title_aux": "ComfyUI-AdvancedLivePortrait" + } + ], + "https://github.com/PressWagon/ComfyUI-StringsAndThings": [ + [ + "DebugString", + "FormatConcatStrings", + "FormattingSingle", + "FourierAnalysisNode", + "ImageDifference", + "MosaicEffectNode", + "PWLoraNameCollector", + "PWLoraSelector", + "TextEmbeddingsInterrogator" + ], + { + "title_aux": "ComfyUI-StringsAndThings" + } + ], + "https://github.com/ProGamerGov/ComfyUI_preview360panorama": [ + [ + "PanoramaVideoViewerNode", + "PanoramaViewerNode" + ], + { + "title_aux": "Preview 360 Panorama for ComfyUI" + } + ], + "https://github.com/ProGamerGov/ComfyUI_pytorch360convert": [ + [ + "Crop 360 to 180 Equirectangular", + "Crop Image with Coords", + "Crop Stereo to Monoscopic", + "Cubemap to Equirectangular", + "Equirectangular Rotation", + "Equirectangular to Cubemap", + "Equirectangular to Perspective", + "Masked Diff C2E", + "Merge Monoscopic into Stereo", + "Pad 180 to 360 Equirectangular", + "Paste Image with Coords", + "Roll Image Axes", + "Split Cubemap Faces", + "Stack Cubemap Faces" + ], + { + "title_aux": "PyTorch 360\u00b0 Image Conversion Toolkit for ComfyUI" + } + ], + "https://github.com/PrunaAI/ComfyUI_pruna": [ + [ + "CacheModelAdaptive", + "CacheModelAuto", + "CacheModelPeriodic", + "PrunaCompileModel" + ], + { + "title_aux": "Pruna nodes for ComfyUI" + } + ], + "https://github.com/Pseudotools/Pseudocomfy": [ + [ + "PseudoApplyDenseDiffusionSDXL", + "PseudoApplyIPAdaperSDXL", + "PseudoConcatStrings", + "PseudoFloatToInt", + "PseudoIPAdapterUnifiedLoaderClone", + "PseudoLoadModelSnapshot", + "PseudoMaskAggregate", + "PseudoMaskBlur", + "PseudoMaskClamp", + "PseudoMaskInvert", + "PseudoMaskRemap", + "PseudoMaskReshape", + "PseudoPreviewStrings", + "PseudoProcessEnvironmentalPrompts", + "PseudoProcessImagePrompt", + "PseudoProcessMaterialPrompts", + "PseudoRemapNormalizedFloat", + "PseudoSaveImageWithEmbeddedMasks", + "PseudoUnpackModelSnapshot" + ], + { + "title_aux": "Pseudocomfy" + } + ], + "https://github.com/Pun0110/ComfyUI-CSV-Styler": [ + [ + "PT.CSV Styler" + ], + { + "title_aux": "CSV Styler" + } + ], + "https://github.com/Q-Bug4/Comfyui-Qb-DateNodes": [ + [ + "DateTimeFormatterNode" + ], + { + "title_aux": "Comfyui-Qb-Date-Nodes" + } + ], + "https://github.com/Q-Bug4/Comfyui-Simple-Json-Node": [ + [ + "JSONArrayIteratorNode", + "JSONGeneratorNode", + "JSONKeyCheckerNode", + "JSONLengthNode", + "JSONMergeNode", + "JSONModifierNode", + "JSONObjectIteratorNode", + "JSONParserNode", + "JSONStringifierNode", + "RandomJSONValueNode" + ], + { + "title_aux": "Simple JSON Parser Node for ComfyUI" + } + ], + "https://github.com/Q-Bug4/comfyui-qbug-batch": [ + [ + "CrossJoinSelector", + "ListFiles", + "NoPreviewSaveImage" + ], + { + "title_aux": "comfyui-qbug-batch" + } + ], + "https://github.com/QaisMalkawi/ComfyUI-QaisHelper": [ + [ + "Bool Binary Operation", + "Bool Unary Operation", + "Item Debugger", + "Item Switch", + "Nearest SDXL Resolution", + "SDXL Resolution", + "Size Swapper" + ], + { + "title_aux": "ComfyUI-Qais-Helper" + } + ], + "https://github.com/QijiTec/ComfyUI-RED-UNO": [ + [ + "REDUNOGenerate", + "REDUNOModelLoader" + ], + { + "title_aux": "ComfyUI-RED-UNO" + } + ], + "https://github.com/R5-Revo/llm-node-comfyui": [ + [ + "UniversalLLMNode" + ], + { + "title_aux": "Universal LLM Node for ComfyUI" + } + ], + "https://github.com/Raapys/ComfyUI-LatentGC_Aggressive": [ + [ + "LatentGC" + ], + { + "title_aux": "LatentGC Aggressive" + } + ], + "https://github.com/RamonGuthrie/ComfyUI-RBG-ImageStitchPlus": [ + [ + "RBGImageStitchPlus", + "RBGPadPro" + ], + { + "title_aux": "ComfyUI-RBG-ImageStitchPlus" + } + ], + "https://github.com/Ravenmelt/ComfyUI-Rodin": [ + [ + "LoadRodinAPIKEY", + "Preview_3DMesh", + "PromptForRodin", + "RodinImage3D", + "RodinMultipleImage3D", + "RodinText3D" + ], + { + "title_aux": "ComfyUI-Rodin" + } + ], + "https://github.com/Raykosan/ComfyUI_RS-SaturationNode": [ + [ + "RS_SaturationSwitch" + ], + { + "title_aux": "ComfyUI_RS-SaturationNode" + } + ], + "https://github.com/Raykosan/ComfyUI_RaykoStudio": [ + [ + "RS_RusTextOverlay" + ], + { + "title_aux": "ComfyUI_RaykoStudio" + } + ], + "https://github.com/RaymondProduction/comfyui-zerna-pack": [ + [ + "CLIPDynamicPromptEncoder //Zerna Pack", + "DynamicPromptInjector //Zerna Pack", + "LastImage //Zerna Pack", + "UnzipPrompt //Zerna Pack" + ], + { + "author": "Raymond", + "description": "A set of nodes for batch processing of text and images.", + "nickname": "Zerna Pack", + "title": "Zerna Pack", + "title_aux": "Zerna Pack" + } + ], + "https://github.com/ReBeating/ComfyUI-Artist-Selector": [ + [ + "LoadArtistTag" + ], + { + "title_aux": "ComfyUI-Artist-Selector" + } + ], + "https://github.com/Reithan/negative_rejection_steering": [ + [ + "NRS" + ], + { + "title_aux": "Negative Rejection Steering" + } + ], + "https://github.com/RenderRift/ComfyUI-RenderRiftNodes": [ + [ + "AnalyseMetadata", + "DateIntegerNode", + "DisplayMetaOptions", + "LoadImageWithMeta", + "MetadataOverlayNode", + "VideoPathMetaExtraction" + ], + { + "title_aux": "ComfyUI-RenderRiftNodes" + } + ], + "https://github.com/RhizoNymph/ComfyUI-CLIPSlider": [ + [ + "CLIPSlider" + ], + { + "title_aux": "ComfyUI-CLIPSlider" + } + ], + "https://github.com/RhizoNymph/ComfyUI-ColorWheel": [ + [ + "AccurateColorWheelNode" + ], + { + "title_aux": "ComfyUI-ColorWheel" + } + ], + "https://github.com/RhizoNymph/ComfyUI-Latte": [ + [ + "LatteVideoGenerator" + ], + { + "title_aux": "ComfyUI-Latte" + } + ], + "https://github.com/RiceRound/ComfyUI_CryptoCat": [ + [ + "CryptoCatImage", + "DecodeCryptoNode", + "ExcuteCryptoNode", + "RandomSeedNode", + "SaveCryptoBridgeNode", + "SaveCryptoNode" + ], + { + "title_aux": "ComfyUI Compression and Encryption Node" + } + ], + "https://github.com/RiceRound/ComfyUI_RiceRound": [ + [ + "RiceRoundAdvancedChoiceNode", + "RiceRoundBooleanNode", + "RiceRoundDecryptNode", + "RiceRoundDownloadImageAndMaskNode", + "RiceRoundDownloadImageNode", + "RiceRoundDownloadMaskNode", + "RiceRoundEncryptNode", + "RiceRoundFloatNode", + "RiceRoundImageBridgeNode", + "RiceRoundImageNode", + "RiceRoundImageUrlNode", + "RiceRoundInputTextNode", + "RiceRoundIntNode", + "RiceRoundMaskBridgeNode", + "RiceRoundOutputBooleanNode", + "RiceRoundOutputFloatNode", + "RiceRoundOutputImageBridgeNode", + "RiceRoundOutputImageNode", + "RiceRoundOutputIntNode", + "RiceRoundOutputMaskBridgeNode", + "RiceRoundOutputTextNode", + "RiceRoundRandomSeedNode", + "RiceRoundSimpleChoiceNode", + "RiceRoundSimpleImageNode", + "RiceRoundStrToBooleanNode", + "RiceRoundStrToFloatNode", + "RiceRoundStrToIntNode", + "RiceRoundUploadImageNode" + ], + { + "title_aux": "RiceRound Cloud Node" + } + ], + "https://github.com/Rinsanga1/comfyui-florence2xy": [ + [ + "Florence2toCoordinatesButxy", + "LoadImageWithName" + ], + { + "title_aux": "comfyui-florence2xy" + } + ], + "https://github.com/RodrigoSKohl/ComfyUI-Panoramic-ImgStitcher": [ + [ + "Image Stitching Node" + ], + { + "title_aux": "Panoramic Image Stitcher" + } + ], + "https://github.com/RodrigoSKohl/InteriorDesign-for-ComfyUI": [ + [ + "Control Items", + "Image Normalize", + "Interior Design Segmentator" + ], + { + "title_aux": "Interior Design for Comfyui" + } + ], + "https://github.com/RodrigoSKohl/comfyui-tryoff-anyone": [ + [ + "TryOffAnyoneNode" + ], + { + "title_aux": "TryOff Anyone" + } + ], + "https://github.com/RomanKuschanow/ComfyUI-Advanced-Latent-Control": [ + [ + "LatentAddTransform", + "LatentInterpolateTransform", + "LatentMirror", + "LatentNormalize", + "LatentShift", + "MirrorTransform", + "MultiplyTransform", + "OffsetCombine", + "OneTimeLatentAddTransform", + "OneTimeLatentInterpolateTransform", + "OneTimeMirrorTransform", + "OneTimeMultiplyTransform", + "OneTimeShiftTransform", + "ShiftTransform", + "TransformHijack", + "TransformOffset", + "TransformSampler", + "TransformSamplerAdvanced", + "TransformsCombine" + ], + { + "title_aux": "Advanced Latent Control" + } + ], + "https://github.com/Ron-Digital/ComfyUI-SceneGenerator": [ + [ + "Scene Generator" + ], + { + "title_aux": "ComfyUI-SceneGenerator" + } + ], + "https://github.com/Runware/ComfyUI-Runware": [ + [ + "Runware API Manager", + "Runware Background Removal", + "Runware ControlNet", + "Runware ControlNet Combine", + "Runware ControlNet PreProcessor", + "Runware DeepCache", + "Runware Embedding Search", + "Runware Embeddings Combine", + "Runware IPAdapter", + "Runware IPAdapters Combine", + "Runware Image Caption", + "Runware Image Inference", + "Runware Image Masking", + "Runware Image Upscaler", + "Runware Imagen Inference", + "Runware Kontext Inference", + "Runware Lora Combine", + "Runware Lora Search", + "Runware Model Search", + "Runware Multi Inference", + "Runware Outpaint", + "Runware PhotoMaker V2", + "Runware Reference Images", + "Runware Refiner", + "Runware TeaCache", + "Runware VAE Search" + ], + { + "title_aux": "Runware.ai ComfyUI Inference API Integration" + } + ], + "https://github.com/Ryuukeisyou/comfyui_face_parsing": [ + [ + "BBoxDecompose(FaceParsing)", + "BBoxDetect(FaceParsing)", + "BBoxDetectorLoader(FaceParsing)", + "BBoxListItemSelect(FaceParsing)", + "BBoxResize(FaceParsing)", + "ColorAdjust(FaceParsing)", + "FaceParse(FaceParsing)", + "FaceParsingModelLoader(FaceParsing)", + "FaceParsingProcessorLoader(FaceParsing)", + "FaceParsingResultsParser(FaceParsing)", + "GuidedFilter(FaceParsing)", + "ImageCropWithBBox(FaceParsing)", + "ImageCropWithBBoxList(FaceParsing)", + "ImageInsertWithBBox(FaceParsing)", + "ImageListSelect(FaceParsing)", + "ImagePadWithBBox(FaceParsing)", + "ImageResizeCalculator(FaceParsing)", + "ImageResizeWithBBox(FaceParsing)", + "ImageSize(FaceParsing)", + "LatentCropWithBBox(FaceParsing)", + "LatentInsertWithBBox(FaceParsing)", + "LatentSize(FaceParsing)", + "MaskBatchComposite(FaceParsing)", + "MaskBlackOut(FaceParsing)", + "MaskBorderDissolve(FaceParsing)", + "MaskBorderDissolveAdvanced(FaceParsing)", + "MaskComposite(FaceParsing)", + "MaskCropWithBBox(FaceParsing)", + "MaskInsertWithBBox(FaceParsing)", + "MaskListSelect(FaceParsing)", + "MaskToBBoxList(FaceParsing)", + "SkinDetectTraditional(FaceParsing)" + ], + { + "title_aux": "comfyui_face_parsing" + } + ], + "https://github.com/Ryuukeisyou/comfyui_io_helpers": [ + [ + "ImageLoadAsMaskByPath(IOHelpers)", + "ImageLoadByPath(IOHelpers)", + "ImageLoadFromBase64(IOHelpers)", + "ImageSaveAsBase64(IOHelpers)", + "ImageSaveToPath(IOHelpers)", + "TypeConversion(IOHelpers)", + "VHSFileNamesToStrings(IOHelpers)" + ], + { + "title_aux": "comfyui_io_helpers" + } + ], + "https://github.com/S4MUEL-404/ComfyUI-S4Tool-Image": [ + [ + "ImageBlendWithAlpha", + "ImageColor", + "ImageCombine", + "ImageCropToFit", + "ImageFromBase64", + "ImageMaskExpand", + "ImageOverlay", + "ImagePalette", + "ImagePalette631", + "ImagePrimaryColor", + "ImageTilingPattern", + "ImageToBase64" + ], + { + "title_aux": "ComfyUI S4Tool Image" + } + ], + "https://github.com/SEkINVR/ComfyUI-SaveAs": [ + [ + "ComfyUISaveAs" + ], + { + "title_aux": "ComfyUI SaveAS" + } + ], + "https://github.com/SKBv0/ComfyUI_SKBundle": [ + [ + "AspectRatioAdvanced", + "DisplayEverything", + "ImageComparer", + "LensFlare", + "MultiFloat", + "MultiTextNode", + "PaintPro", + "SKB_AnySwitch", + "SeamlessTexture", + "TextBox", + "TitlePlus" + ], + { + "title_aux": "ComfyUI SKBundle" + } + ], + "https://github.com/SLAPaper/ComfyUI-Image-Selector": [ + [ + "ImageDuplicator", + "ImageSelector", + "LatentDuplicator", + "LatentSelector" + ], + { + "title_aux": "ComfyUI-Image-Selector" + } + ], + "https://github.com/SOELexicon/ComfyUI-LexMSDBNodes": [ + [ + "MSSqlSelectNode", + "MSSqlTableNode" + ], + { + "title_aux": "LexMSDBNodes" + } + ], + "https://github.com/SOELexicon/ComfyUI-LexTools": [ + [ + "AesthetlcScoreSorter", + "AgeClassifierNode", + "ArtOrHumanClassifierNode", + "CalculateAestheticScore", + "DocumentClassificationNode", + "FoodCategoryClassifierNode", + "ImageAspectPadNode", + "ImageCaptioning", + "ImageFilterByFloatScoreNode", + "ImageFilterByIntScoreNode", + "ImageQualityScoreNode", + "ImageRankingNode", + "ImageScaleToMin", + "LoadAesteticModel", + "MD5ImageHashNode", + "NSFWClassifierNode", + "SamplerPropertiesNode", + "ScoreConverterNode", + "SeedIncrementerNode", + "SegformerNode", + "SegformerNodeMasks", + "SegformerNodeMergeSegments", + "StepCfgIncrementNode", + "WatermarkDetectionNode" + ], + { + "title_aux": "ComfyUI-LexTools" + } + ], + "https://github.com/SS-snap/ComfyUI-Ad_scheduler": [ + [ + "AD_Scheduler" + ], + { + "title_aux": "ComfyUI-Ad-scheduler" + } + ], + "https://github.com/SS-snap/ComfyUI-LBW_flux": [ + [ + "LoraBlockWeight_Flux" + ], + { + "title_aux": "ComfyUI-LBW_flux" + } + ], + "https://github.com/SS-snap/ComfyUI-Snap_Processing": [ + [ + "AreaCalculator", + "PyQtCanvasNode", + "Snapload" + ], + { + "title_aux": "Snap Processing for Comfyui" + } + ], + "https://github.com/SS-snap/Comfyui_SSsnap_pose-Remapping": [ + [ + "ApplyPoseScalesToFrames", + "CalculatePoseScales", + "ConvertPoseToStandardFormat", + "RenderKps" + ], + { + "title_aux": "Comfyui_SSsnap_pose-Remapping" + } + ], + "https://github.com/SXQBW/ComfyUI-Qwen": [ + [ + "Qwen2_Chat_Zho", + "Qwen2_ModelLoader_Zho", + "Qwen2_Zho" + ], + { + "title_aux": "ComfyUI-Qwen" + } + ], + "https://github.com/SXQBW/ComfyUI-Qwen-Omni": [ + [ + "QwenOmniCombined", + "VideoUploader" + ], + { + "title_aux": "ComfyUI-Qwen-Omni" + } + ], + "https://github.com/SXQBW/ComfyUI-Qwen-VL": [ + [ + "QwenVisionParser" + ], + { + "title_aux": "ComfyUI-Qwen-VL" + } + ], + "https://github.com/SamKhoze/ComfyUI-DeepFuze": [ + [ + "DeepFuze Save", + "DeepFuzeAdavance", + "DeepFuzeFaceSwap", + "DeepfuzePreview", + "LLM_node", + "PlayBackAudio", + "TTS_generation" + ], + { + "title_aux": "DeepFuze" + } + ], + "https://github.com/SamTyurenkov/comfyui-vace-preprocessors": [ + [ + "CombineLayoutTracksNode", + "VideoLayoutTrackAnnotatorNode" + ], + { + "title_aux": "comfyui_vace_preprocessors" + } + ], + "https://github.com/SamTyurenkov/comfyui_chatgpt": [ + [ + "ChatGPTImageEditNode", + "ChatGPTImageGenerationNode", + "ImageToBase64" + ], + { + "title_aux": "comfyui_chatgpt" + } + ], + "https://github.com/San4itos/ComfyUI-Save-Images-as-Video": [ + [ + "SaveFramesToVideoFFmpeg_san4itos" + ], + { + "title_aux": "Save Images to Video (FFmpeg) for ComfyUI" + } + ], + "https://github.com/SanDiegoDude/ComfyUI-DeepStereo": [ + [ + "ColorPickerNode", + "DepthMapProcessor", + "ImageEffectsTransformer", + "ImageResizeAndTransform", + "MiDaSDepthEstimator", + "ProceduralTextureGenerator", + "RandomDotStereogramGenerator", + "RandomNoiseGenerator", + "StereogramGenerator", + "TextureTransformer" + ], + { + "title_aux": "ComfyUI-DeepStereo" + } + ], + "https://github.com/SanDiegoDude/ComfyUI-Kontext-API": [ + [ + "FalKontextMaxMultiImageNode", + "KontextAPINode" + ], + { + "title_aux": "ComfyUI-Kontext-API" + } + ], + "https://github.com/SanDiegoDude/ComfyUI-SaveAudioMP3": [ + [ + "SaveAudioMP3" + ], + { + "title_aux": "ComfyUI-SaveAudioMP3" + } + ], + "https://github.com/Santodan/santodan-custom-nodes-comfyui": [ + [ + "LoRACachePreloader", + "RandomLoRACustom", + "RandomLoRAFolder" + ], + { + "title_aux": "Santodan Random LoRA Node" + } + ], + "https://github.com/SayanoAI/Comfy-RVC": [ + [ + "Any2ListNode", + "AudioBatchValueNode", + "AudioInfoNode", + "AudioTranscriptionNode", + "BatchedTranscriptionEncoderNode", + "DownloadAudio", + "ImageRepeatInterleavedNode", + "LatentRepeatInterleavedNode", + "List2AnyNode", + "LoadHubertModel", + "LoadPitchExtractionParams", + "LoadRVCModelNode", + "LoadWhisperModelNode", + "MergeAudioNode", + "MergeImageBatches", + "MergeLatentBatches", + "MuseAudioFeatureExtractionNode", + "MuseImageFeatureExtractionNode", + "MuseTalkNode", + "ProcessAudioNode", + "RVC-Studio.LoadAudio", + "RVC-Studio.PreviewAudio", + "RVCNode", + "RVCProcessDatasetNode", + "RVCTrainModelNode", + "RVCTrainParamsNode", + "SimpleMathNode", + "SliceNode", + "SortImagesNode", + "UVR5Node", + "ZipNode" + ], + { + "title_aux": "Comfy-RVC" + } + ], + "https://github.com/Sayene/comfyui-base64-to-image-size": [ + [ + "LoadImageFromBase64Size" + ], + { + "title_aux": "comfyui-base64-to-image-size" + } + ], + "https://github.com/Scholar01/ComfyUI-Keyframe": [ + [ + "KeyframeApply", + "KeyframeInterpolationPart", + "KeyframePart" + ], + { + "title_aux": "SComfyUI-Keyframe" + } + ], + "https://github.com/Scorpinaus/ComfyUI-DiffusersLoader": [ + [ + "CombinedDiffusersLoader", + "DiffusersClipLoader", + "DiffusersUNETLoader", + "DiffusersVAELoader" + ], + { + "title_aux": "ComfyUI-DiffusersLoader" + } + ], + "https://github.com/ScreamingHawk/comfyui-ollama-prompt-encode": [ + [ + "OllamaCLIPTextEncode", + "OllamaPromptGenerator" + ], + { + "author": "Michael Standen", + "description": "Use AI to generate prompts and perform CLIP text encoding", + "nickname": "Ollama Prompt Encode", + "title": "Ollama Prompt Encode", + "title_aux": "Ollama Prompt Encode" + } + ], + "https://github.com/SeaArtLab/ComfyUI-Long-CLIP": [ + [ + "LongCLIPTextEncodeFlux", + "SeaArtLongClip", + "SeaArtLongXLClipMerge" + ], + { + "title_aux": "ComfyUI-Long-CLIP" + } + ], + "https://github.com/SeanScripts/ComfyUI-PixtralLlamaMolmoVision": [ + [ + "AutoVisionModelLoader", + "JoinString", + "LlamaVisionGenerateText", + "LlamaVisionModelLoader", + "MolmoGenerateText", + "MolmoModelLoader", + "ParseBoundingBoxes", + "ParsePoints", + "PixtralGenerateText", + "PixtralImageEncode", + "PixtralModelLoader", + "PixtralTextEncode", + "PlotPoints", + "RegexFindAll", + "RegexSearch", + "RegexSplitString", + "RegexSubstitution", + "SelectIndex", + "SliceList" + ], + { + "title_aux": "ComfyUI-PixtralLlamaMolmoVision" + } + ], + "https://github.com/SeanScripts/ComfyUI-Unload-Model": [ + [ + "UnloadAllModels", + "UnloadModel" + ], + { + "title_aux": "ComfyUI-Unload-Model" + } + ], + "https://github.com/SeargeDP/ComfyUI_Searge_LLM": [ + [ + "Searge_AdvOptionsNode", + "Searge_LLM_Node", + "Searge_Output_Node" + ], + { + "title_aux": "Searge-LLM for ComfyUI v1.0" + } + ], + "https://github.com/SeargeDP/SeargeSDXL": [ + [ + "SeargeAdvancedParameters", + "SeargeCheckpointLoader", + "SeargeConditionMixing", + "SeargeConditioningMuxer2", + "SeargeConditioningMuxer5", + "SeargeConditioningParameters", + "SeargeControlnetAdapterV2", + "SeargeControlnetModels", + "SeargeCustomAfterUpscaling", + "SeargeCustomAfterVaeDecode", + "SeargeCustomPromptMode", + "SeargeDebugPrinter", + "SeargeEnablerInputs", + "SeargeFloatConstant", + "SeargeFloatMath", + "SeargeFloatPair", + "SeargeFreeU", + "SeargeGenerated1", + "SeargeGenerationParameters", + "SeargeHighResolution", + "SeargeImage2ImageAndInpainting", + "SeargeImageAdapterV2", + "SeargeImageSave", + "SeargeImageSaving", + "SeargeInput1", + "SeargeInput2", + "SeargeInput3", + "SeargeInput4", + "SeargeInput5", + "SeargeInput6", + "SeargeInput7", + "SeargeIntegerConstant", + "SeargeIntegerMath", + "SeargeIntegerPair", + "SeargeIntegerScaler", + "SeargeLatentMuxer3", + "SeargeLoraLoader", + "SeargeLoras", + "SeargeMagicBox", + "SeargeModelSelector", + "SeargeOperatingMode", + "SeargeOutput1", + "SeargeOutput2", + "SeargeOutput3", + "SeargeOutput4", + "SeargeOutput5", + "SeargeOutput6", + "SeargeOutput7", + "SeargeParameterProcessor", + "SeargePipelineStart", + "SeargePipelineTerminator", + "SeargePreviewImage", + "SeargePromptAdapterV2", + "SeargePromptCombiner", + "SeargePromptStyles", + "SeargePromptText", + "SeargeSDXLBasePromptEncoder", + "SeargeSDXLImage2ImageSampler", + "SeargeSDXLImage2ImageSampler2", + "SeargeSDXLPromptEncoder", + "SeargeSDXLRefinerPromptEncoder", + "SeargeSDXLSampler", + "SeargeSDXLSampler2", + "SeargeSDXLSamplerV3", + "SeargeSamplerAdvanced", + "SeargeSamplerInputs", + "SeargeSaveFolderInputs", + "SeargeSeparator", + "SeargeStylePreprocessor", + "SeargeTextInputV2", + "SeargeUpscaleModelLoader", + "SeargeUpscaleModels", + "SeargeVAELoader" + ], + { + "title_aux": "SeargeSDXL" + } + ], + "https://github.com/Seedsa/Fooocus_Nodes": [ + [ + "BasicScheduler", + "CLIPLoader", + "CLIPMergeSimple", + "CLIPSave", + "CLIPSetLastLayer", + "CLIPTextEncode", + "CLIPTextEncodeSDXL", + "CLIPTextEncodeSDXLRefiner", + "CLIPVisionEncode", + "CLIPVisionLoader", + "Canny", + "CheckpointLoader", + "CheckpointLoaderSimple", + "CheckpointSave", + "ConditioningAverage", + "ConditioningCombine", + "ConditioningConcat", + "ConditioningSetArea", + "ConditioningSetAreaPercentage", + "ConditioningSetMask", + "ConditioningSetTimestepRange", + "ConditioningZeroOut", + "ControlNetApply", + "ControlNetApplyAdvanced", + "ControlNetLoader", + "CropMask", + "DiffControlNetLoader", + "DiffusersLoader", + "DualCLIPLoader", + "EmptyImage", + "EmptyLatentImage", + "ExponentialScheduler", + "FeatherMask", + "FlipSigmas", + "Fooocus ApplyImagePrompt", + "Fooocus Controlnet", + "Fooocus Describe", + "Fooocus Expansion", + "Fooocus ImagePrompt", + "Fooocus Inpaint", + "Fooocus KSampler", + "Fooocus Loader", + "Fooocus LoraStack", + "Fooocus PipeOut", + "Fooocus PreKSampler", + "Fooocus Styles", + "Fooocus Upscale", + "Fooocus detailerFix", + "Fooocus negative", + "Fooocus positive", + "Fooocus preDetailerFix", + "Fooocus samLoaderPipe", + "Fooocus ultralyticsDetectorPipe", + "FreeU", + "FreeU_V2", + "GLIGENLoader", + "GLIGENTextBoxApply", + "GrowMask", + "HyperTile", + "HypernetworkLoader", + "ImageBatch", + "ImageBlend", + "ImageBlur", + "ImageColorToMask", + "ImageCompositeMasked", + "ImageCrop", + "ImageInvert", + "ImageOnlyCheckpointLoader", + "ImageOnlyCheckpointSave", + "ImagePadForOutpaint", + "ImageQuantize", + "ImageScale", + "ImageScaleBy", + "ImageScaleToTotalPixels", + "ImageSharpen", + "ImageToMask", + "ImageUpscaleWithModel", + "InpaintModelConditioning", + "InvertMask", + "JoinImageWithAlpha", + "KSampler", + "KSamplerAdvanced", + "KSamplerSelect", + "KarrasScheduler", + "LatentAdd", + "LatentBatch", + "LatentBatchSeedBehavior", + "LatentBlend", + "LatentComposite", + "LatentCompositeMasked", + "LatentCrop", + "LatentFlip", + "LatentFromBatch", + "LatentInterpolate", + "LatentMultiply", + "LatentRotate", + "LatentSubtract", + "LatentUpscale", + "LatentUpscaleBy", + "LoadImage", + "LoadImageMask", + "LoadLatent", + "LoraLoader", + "LoraLoaderModelOnly", + "MaskComposite", + "MaskToImage", + "ModelMergeAdd", + "ModelMergeBlocks", + "ModelMergeSimple", + "ModelMergeSubtract", + "ModelSamplingContinuousEDM", + "ModelSamplingDiscrete", + "PatchModelAddDownscale", + "PerpNeg", + "PhotoMakerEncode", + "PhotoMakerLoader", + "PolyexponentialScheduler", + "PorterDuffImageComposite", + "PreviewImage", + "RebatchImages", + "RebatchLatents", + "RepeatImageBatch", + "RepeatLatentBatch", + "RescaleCFG", + "SDTurboScheduler", + "SD_4XUpscale_Conditioning", + "SVD_img2vid_Conditioning", + "SamplerCustom", + "SamplerDPMPP_2M_SDE", + "SamplerDPMPP_SDE", + "SaveAnimatedPNG", + "SaveAnimatedWEBP", + "SaveImage", + "SaveLatent", + "SelfAttentionGuidance", + "SetLatentNoiseMask", + "SolidMask", + "SplitImageWithAlpha", + "SplitSigmas", + "StableZero123_Conditioning", + "StableZero123_Conditioning_Batched", + "StyleModelApply", + "StyleModelLoader", + "TomePatchModel", + "UNETLoader", + "UpscaleModelLoader", + "VAEDecode", + "VAEDecodeTiled", + "VAEEncode", + "VAEEncodeForInpaint", + "VAEEncodeTiled", + "VAELoader", + "VAESave", + "VPScheduler", + "VideoLinearCFGGuidance", + "unCLIPCheckpointLoader", + "unCLIPConditioning" + ], + { + "title_aux": "ComfyUI Fooocus Nodes" + } + ], + "https://github.com/Sekiun/ComfyUI-WebpToPNGSequence": [ + [ + "WebpToPngSequence" + ], + { + "title_aux": "ComfyUI-WebpToPNGSequence" + } + ], + "https://github.com/Semper-Sursum/HF-Flux-ComfyUI": [ + [ + "HuggingFaceTextToImage" + ], + { + "title_aux": "HF-Flux-ComfyUI" + } + ], + "https://github.com/ServiceStack/comfy-asset-downloader": [ + [ + "AssetDownloader" + ], + { + "title_aux": "ComfyUI Asset Downloader" + } + ], + "https://github.com/Shadetail/ComfyUI_Eagleshadow": [ + [ + "Batch 12 Images", + "Detect Transparency", + "Fix Checkpoint Name", + "ImageLinearGammaCompositeMasked", + "KSampler Same Noise", + "MaskGlow", + "OffsetImage", + "Round Float to String", + "SaveImageToFolder", + "Select ControlNet", + "Select Model 20", + "Simple Load Image Batch" + ], + { + "title_aux": "Eagleshadow Custom Nodes" + } + ], + "https://github.com/Shakker-Labs/ComfyUI-IPAdapter-Flux": [ + [ + "ApplyIPAdapterFlux", + "ApplyIPAdapterFluxAdvanced", + "IPAdapterFluxLoader", + "IPAdapterFluxLoaderAdvanced" + ], + { + "title_aux": "ComfyUI-IPAdapter-Flux" + } + ], + "https://github.com/Shannooty/ComfyUI-Timer-Nodes": [ + [ + "TimerStart", + "TimerStringConcat" + ], + { + "title_aux": "ComfyUI Timer Nodes" + } + ], + "https://github.com/SherryXieYuchen/ComfyUI-Image-Inpainting": [ + [ + "CropImageByRect", + "INPAINT_ColorCorrection", + "INPAINT_CropImage", + "INPAINT_InpaintingWithModel", + "INPAINT_LoadModel", + "INPAINT_PasteBackCropImage", + "INPAINT_VAEDecode", + "INPAINT_VAEEncode", + "ImagePostprocess", + "ImagePreprocess" + ], + { + "title_aux": "ComfyUI-Image-Inpainting" + } + ], + "https://github.com/Shiba-2-shiba/ComfyUI-Magcache-for-SDXL": [ + [ + "MagCacheSDXL", + "MagCacheSDXLCalibration" + ], + { + "title_aux": "ComfyUI-Magcache-for-SDXL" + } + ], + "https://github.com/Shiba-2-shiba/ComfyUI_DiffusionModel_fp8_converter": [ + [ + "ClipFP8ConverterNode", + "ModelFP8ConverterNode" + ], + { + "title_aux": "ComfyUI_DiffusionModel_fp8_converter" + } + ], + "https://github.com/Shiba-2-shiba/ComfyUI_FreeU_V2_timestepadd": [ + [ + "FreeU_V2_timestepadd" + ], + { + "title_aux": "ComfyUI_FreeU_V2_timestepadd" + } + ], + "https://github.com/Shiba-2-shiba/comfyui-color-ascii-art-node": [ + [ + "ASCIIArtNodeV3" + ], + { + "title_aux": "ComfyUI-color-ascii-art-node" + } + ], + "https://github.com/Shibiko-AI/ShibikoAI-ComfyUI-Tools": [ + [ + "BboxInsertImage", + "BboxSplit", + "Cascade", + "ImageFilters", + "Luts", + "LutsAdvanced", + "RemoveAreaByMask", + "RemoveBackground", + "SeedGenerator", + "Waifu2x" + ], + { + "title_aux": "Shibiko AI ComfyUI Tools" + } + ], + "https://github.com/ShinChven/sc-comfy-nodes": [ + [ + "Rescale Node", + "Width & Height" + ], + { + "title_aux": "ShinChven's Custom Nodes Package" + } + ], + "https://github.com/ShmuelRonen/ComfyUI-Apply_Style_Model_Adjust": [ + [ + "ApplyStyleModelAdjust" + ], + { + "title_aux": "Apply Style Model Adjust for ComfyUI" + } + ], + "https://github.com/ShmuelRonen/ComfyUI-AstralAnimator": [ + [ + "AstralAnimator" + ], + { + "title_aux": "ComfyUI-AstralAnimator" + } + ], + "https://github.com/ShmuelRonen/ComfyUI-Audio_Quality_Enhancer": [ + [ + "AudioQualityEffects", + "AudioQualityEnhancer" + ], + { + "title_aux": "ComfyUI-Audio_Quality_Enhancer" + } + ], + "https://github.com/ShmuelRonen/ComfyUI-CohernetVideoSampler": [ + [ + "CohernetVideoSampler" + ], + { + "title_aux": "ComfyUI Coherent Video Sampler Node" + } + ], + "https://github.com/ShmuelRonen/ComfyUI-DeepSeek_R1-Chat": [ + [ + "ComfyUIDeepSeekChat" + ], + { + "title_aux": "ComfyUI DeepSeek_R1 Chat Node" + } + ], + "https://github.com/ShmuelRonen/ComfyUI-EmptyHunyuanLatent": [ + [ + "EmptyHunyuanLatentForImage", + "EmptyHunyuanLatentForVideo" + ], + { + "title_aux": "ComfyUI-EmptyHunyuanLatent" + } + ], + "https://github.com/ShmuelRonen/ComfyUI-FramePackWrapper_Plus": [ + [ + "DownloadAndLoadFramePackModel", + "FramePackFindNearestBucket", + "FramePackLoraSelect", + "FramePackSampler", + "FramePackSampler_F1", + "FramePackTimestampedTextEncode", + "FramePackTorchCompileSettings", + "LoadFramePackModel" + ], + { + "title_aux": "ComfyUI-FramePackWrapper_Plus" + } + ], + "https://github.com/ShmuelRonen/ComfyUI-FreeMemory": [ + [ + "FreeMemoryCLIP", + "FreeMemoryImage", + "FreeMemoryLatent", + "FreeMemoryModel", + "FreeMemoryString" + ], + { + "title_aux": "ComfyUI-FreeMemory" + } + ], + "https://github.com/ShmuelRonen/ComfyUI-FreeVC_wrapper": [ + [ + "FreeVC Voice Conversion" + ], + { + "title_aux": "ComfyUI-FreeVC_wrapper" + } + ], + "https://github.com/ShmuelRonen/ComfyUI-Gemini_Flash_2.0_Exp": [ + [ + "AudioRecorder", + "GeminiFlash" + ], + { + "title_aux": "ComfyUI-Gemini_Flash_2.0_Exp" + } + ], + "https://github.com/ShmuelRonen/ComfyUI-Gemini_TTS": [ + [ + "GeminiTTS" + ], + { + "title_aux": "ComfyUI-Gemini_TTS" + } + ], + "https://github.com/ShmuelRonen/ComfyUI-HiggsAudio_Wrapper": [ + [ + "HiggsAudio", + "LoadHiggsAudioModel", + "LoadHiggsAudioPrompt", + "LoadHiggsAudioSystemPrompt", + "LoadHiggsAudioTokenizer" + ], + { + "title_aux": "ComfyUI-HiggsAudio_Wrapper" + } + ], + "https://github.com/ShmuelRonen/ComfyUI-HunyuanVideoSamplerSave": [ + [ + "EmptyVideoLatentForHunyuan", + "HunyuanVideoSamplerSave", + "ImageMotionInfluance", + "ResizeImageForHunyuan" + ], + { + "title_aux": "ComfyUI-HunyuanVideoSamplerSave" + } + ], + "https://github.com/ShmuelRonen/ComfyUI-HunyuanVideoStyler": [ + [ + "HunyuanVideoStyler" + ], + { + "title_aux": "ComfyUI-HunyuanVideoStyler" + } + ], + "https://github.com/ShmuelRonen/ComfyUI-ImageMotionGuider": [ + [ + "ImageMotionGuider" + ], + { + "title_aux": "ComfyUI-ImageMotionGuider" + } + ], + "https://github.com/ShmuelRonen/ComfyUI-Janus_pro_vision": [ + [ + "UnifiedVisionAnalyzer", + "VisionModelLoader" + ], + { + "title_aux": "ComfyUI Janus Pro Vision" + } + ], + "https://github.com/ShmuelRonen/ComfyUI-JoyHallo_wrapper": [ + [ + "JoyHallo_wrapper" + ], + { + "title_aux": "ComfyUI-JoyHallo_wrapper" + } + ], + "https://github.com/ShmuelRonen/ComfyUI-LatentSyncWrapper": [ + [ + "LatentSyncNode", + "VideoLengthAdjuster" + ], + { + "title_aux": "ComfyUI-LatentSyncWrapper" + } + ], + "https://github.com/ShmuelRonen/ComfyUI-Orpheus-TTS": [ + [ + "OrpheusAudioEffects", + "OrpheusGenerate", + "OrpheusModelLoader" + ], + { + "title_aux": "ComfyUI-Orpheus-TTS" + } + ], + "https://github.com/ShmuelRonen/ComfyUI-PS_Flatten_Image": [ + [ + "FlattenImage" + ], + { + "title_aux": "ComfyUI-PS_Flatten_Image" + } + ], + "https://github.com/ShmuelRonen/ComfyUI-PixArt_XL": [ + [ + "PA_BaseModelLoader_fromhub", + "PA_Generation" + ], + { + "title_aux": "ComfyUI-PixArt_XL" + } + ], + "https://github.com/ShmuelRonen/ComfyUI-SVDResizer": [ + [ + "SVDRsizer" + ], + { + "title_aux": "ComfyUI-SVDResizer" + } + ], + "https://github.com/ShmuelRonen/ComfyUI-ThinkSound_Wrapper": [ + [ + "ThinkSoundFeatureUtilsLoader", + "ThinkSoundModelLoader", + "ThinkSoundSampler" + ], + { + "title_aux": "ComfyUI-ThinkSound_Wrapper" + } + ], + "https://github.com/ShmuelRonen/ComfyUI-Veo2-Experimental": [ + [ + "VeoTextToVideo", + "VeoToVHS", + "VeoVideoPreview" + ], + { + "title_aux": "ComfyUI-Veo2-Experimental" + } + ], + "https://github.com/ShmuelRonen/ComfyUI-VideoUpscale_WithModel": [ + [ + "Free_Video_Memory", + "Video_Upscale_With_Model" + ], + { + "title_aux": "ComfyUI-VideoUpscale_WithModel" + } + ], + "https://github.com/ShmuelRonen/ComfyUI-WanVideoKsampler": [ + [ + "WanVideoKsampler" + ], + { + "title_aux": "ComfyUI-WanVideoKsampler" + } + ], + "https://github.com/ShmuelRonen/ComfyUI_ChatterBox_Voice": [ + [ + "ChatterBoxVoiceCapture", + "ChatterBoxVoiceTTS", + "ChatterBoxVoiceVC" + ], + { + "title_aux": "ComfyUI_ChatterBox_Voice" + } + ], + "https://github.com/ShmuelRonen/ComfyUI_Flux_1.1_RAW_API": [ + [ + "FluxPro11WithFinetune" + ], + { + "title_aux": "ComfyUI Flux 1.1 Ultra & Raw Node" + } + ], + "https://github.com/ShmuelRonen/ComfyUI_Gemini_Flash": [ + [ + "Gemini_Flash_002" + ], + { + "title_aux": "ComfyUI_Gemini_Flash" + } + ], + "https://github.com/ShmuelRonen/ComfyUI_Hedra": [ + [ + "HedraImageToVideo" + ], + { + "title_aux": "ComfyUI Hedra Node" + } + ], + "https://github.com/ShmuelRonen/ComfyUI_pixtral_large": [ + [ + "ComfyUIPixtralLarge", + "MultiImagesInput", + "preview_text" + ], + { + "title_aux": "ComfyUI Pixtral Large Extension" + } + ], + "https://github.com/ShmuelRonen/ComfyUI_pixtral_vision": [ + [ + "ComfyUIPixtralVision", + "MultiImagesInput", + "preview_text" + ], + { + "title_aux": "ComfyUI_pixtral_vision" + } + ], + "https://github.com/ShmuelRonen/ComfyUI_wav2lip": [ + [ + "LoadAudio", + "Wav2Lip" + ], + { + "title_aux": "Wav2Lip Node for ComfyUI" + } + ], + "https://github.com/ShmuelRonen/DJ_VideoAudioMixer": [ + [ + "DJ_VideoAudioMixer" + ], + { + "title_aux": "DJ_VideoAudioMixer" + } + ], + "https://github.com/ShmuelRonen/FluxKontextCreator": [ + [ + "FluxKontextCreator", + "FluxKontextCreatorExperimental" + ], + { + "title_aux": "Flux Kontext Creator for ComfyUI" + } + ], + "https://github.com/ShmuelRonen/comfyui-openai_fm": [ + [ + "OpenAIFMNode" + ], + { + "title_aux": "comfyui-openai_fm" + } + ], + "https://github.com/ShmuelRonen/google_moogle": [ + [ + "googletrans" + ], + { + "title_aux": "Google Moogle" + } + ], + "https://github.com/Shraknard/ComfyUI-Remover": [ + [ + "Remover" + ], + { + "title_aux": "ComfyUI-Remover" + } + ], + "https://github.com/Siberpone/lazy-pony-prompter": [ + [ + "LPP_Danbooru", + "LPP_Deleter", + "LPP_Derpibooru", + "LPP_E621", + "LPP_Loader_Danbooru", + "LPP_Loader_Derpibooru", + "LPP_Loader_E621", + "LPP_Saver" + ], + { + "title_aux": "Lazy Pony Prompter" + } + ], + "https://github.com/Siempreflaco/ComfyUI-NCNodes": [ + [ + "Load3DMesh", + "NCAudioRecorderNode", + "NCImageProcessor", + "NCIncrementINT", + "NCLineCounter" + ], + { + "title_aux": "ComfyUI-NCNodes" + } + ], + "https://github.com/Sieyalixnet/ComfyUI_Textarea_Loaders": [ + [ + "CheckPointLoader_Text", + "EmptyLatentImage_Text", + "LoRALoader_Text", + "LoadImage_Text" + ], + { + "title_aux": "ComfyUI_Textarea_Loaders" + } + ], + "https://github.com/SignalCha1n/comfyui-ComfySnap": [ + [ + "FaceAvoidRandomY", + "LowQualityDigitalLook", + "SnapBasicFilters", + "SnapTextOverlay" + ], + { + "title_aux": "Snap Style Nodes for ComfyUI" + } + ], + "https://github.com/SijieMei/ComfyUI-promptHistory": [ + [ + "PromptHistory" + ], + { + "title_aux": "ComfyUI-Prompt-History" + } + ], + "https://github.com/Simlym/comfyui-prompt-helper": [ + [ + "PromptProcessor" + ], + { + "title_aux": "ComfyUI Prompt Helper" + } + ], + "https://github.com/SimonHeese/ComfyUI_AnimationNodes/raw/refs/heads/main/animated_offset_pad.py": [ + [ + "AnimatedOffsetPadding" + ], + { + "title_aux": "ComfyUI_AnimationNodes" + } + ], + "https://github.com/Sinphaltimus/comfyui_fedcoms_node_pack": [ + [ + "EnhancedModelMetadataReader", + "ModelDataExtractor", + "ModelMetadataReader" + ], + { + "title_aux": "comfyui_fedcoms_node_pack" + } + ], + "https://github.com/SipherAGI/comfyui-animatediff": [ + [ + "AnimateDiffCombine", + "AnimateDiffLoraLoader", + "AnimateDiffModuleLoader", + "AnimateDiffSampler", + "AnimateDiffSlidingWindowOptions", + "ImageSizeAndBatchSize", + "LoadVideo" + ], + { + "title_aux": "AnimateDiff" + } + ], + "https://github.com/SlackinJack/asyncdiff_comfyui": [ + [ + "ADADSampler", + "ADControlNetLoader", + "ADIPAdapterLoader", + "ADLoraLoader", + "ADModelLoader", + "ADMultiLoraCombiner", + "ADPipelineConfig", + "ADSDSampler", + "ADSDUpscaleSampler", + "ADSVDSampler", + "ADSchedulerSelector" + ], + { + "title_aux": "asyncdiff_comfyui" + } + ], + "https://github.com/SlackinJack/distrifuser_comfyui": [ + [ + "DFPipelineConfig", + "DFSampler" + ], + { + "title_aux": "distrifuser_comfyui" + } + ], + "https://github.com/SleeeepyZhou/ComfyUI-CNtranslator": [ + [ + "CNtranslator", + "TextShow" + ], + { + "title_aux": "CNtranslator" + } + ], + "https://github.com/Slickytail/ComfyUI-InstantX-IPAdapter-SD3": [ + [ + "ApplyIPAdapterSD3", + "IPAdapterSD3Loader" + ], + { + "title_aux": "ComfyUI-InstantX-IPAdapter-SD3" + } + ], + "https://github.com/Slickytail/ComfyUI-RegionalAdaptiveSampling": [ + [ + "RegionalAdaptiveSampling" + ], + { + "title_aux": "ComfyUI-RegionalAdaptiveSampling" + } + ], + "https://github.com/Smirnov75/ComfyUI-mxToolkit": [ + [ + "mxSeed", + "mxSlider", + "mxSlider2D", + "mxStop" + ], + { + "title_aux": "ComfyUI-mxToolkit" + } + ], + "https://github.com/Smuzzies/comfyui_meme_maker": [ + [ + "MemeMaker" + ], + { + "title_aux": "comfyui_meme_maker" + } + ], + "https://github.com/SoftMeng/ComfyUI-DeepCache-Fix": [ + [ + "DeepCache_Fix" + ], + { + "title_aux": "ComfyUI-DeepCache-Fix" + } + ], + "https://github.com/SoftMeng/ComfyUI-PIL": [ + [ + "PIL Effects (Mexx)", + "PIL Merge Image (Mexx)", + "PIL Remove Black Dots (Mexx)", + "PIL TITLE (Mexx)" + ], + { + "title_aux": "ComfyUI-PIL" + } + ], + "https://github.com/SoftMeng/ComfyUI_ImageToText": [ + [ + "ComfyUI_ImageToText" + ], + { + "title_aux": "ComfyUI_ImageToText" + } + ], + "https://github.com/SoftMeng/ComfyUI_Mexx_Poster": [ + [ + "ComfyUI_Mexx_Poster" + ], + { + "title_aux": "ComfyUI_Mexx_Poster" + } + ], + "https://github.com/SoftMeng/ComfyUI_Mexx_Styler": [ + [ + "MexxSDXLPromptStyler", + "MexxSDXLPromptStylerAdvanced" + ], + { + "title_aux": "ComfyUI_Mexx_Styler" + } + ], + "https://github.com/SongGuo11/ComfyUI-SaveAnything-SG11": [ + [ + "SG11_SaveAnything" + ], + { + "title_aux": "ComfyUI SaveAnything Node (SG11)" + } + ], + "https://github.com/Sorcerio/MBM-Music-Visualizer": [ + [ + "id", + "mbmAudioFeatureCalculator", + "mbmAudioLoader", + "mbmImageConcat", + "mbmPromptSequenceBuilder", + "mbmPromptSequenceBuilderAdv", + "mbmPromptSequenceInterpolator", + "mbmPromptSequenceLoader", + "mbmPromptSequenceRenderer" + ], + { + "title_aux": "MBM's Music Visualizer" + } + ], + "https://github.com/SozeInc/ComfyUI-Mobile": [ + [ + "Mobile_Settings_Launcher_Data", + "Send Notification (Mobile)", + "Settings Launcher (Mobile)", + "Ultimate Concat (Mobile)" + ], + { + "title_aux": "ComfyUI-Mobile" + } + ], + "https://github.com/SozeInc/ComfyUI_Soze": [ + [ + "Alpha Crop and Position Image", + "CSV Reader", + "CSV Reader X Checkpoint", + "CSV Reader X Lora", + "CSV Writer", + "Checkpoint File Loader", + "ComfyDeploy API Node Image-Prompt 2 Image", + "Empty Images", + "Get Most Common Image Colors", + "Image Batch Process Switch", + "Image List Loader", + "Image Overlay", + "Is Input In List", + "Load Image", + "Load Image From URL", + "Load Images From Folder", + "Lora File Loader", + "Multiline Concatenate Strings", + "Output Filename", + "Pad Mask", + "Prompt Cache", + "Range(Num Steps) - Float", + "Range(Num Steps) - Int", + "Range(Step) - Float", + "Range(Step) - Int", + "Shrink Image", + "Special Character Replacer", + "Text Contains (Return Bool)", + "Text Contains (Return String)", + "Variable Image Builder", + "XY Any", + "XY Image" + ], + { + "title_aux": "Quality of Life Nodes for ComfyUI" + } + ], + "https://github.com/SparknightLLC/ComfyUI-ConditionalInterrupt": [ + [ + "Conditional Interrupt" + ], + { + "title_aux": "ComfyUI-ConditionalInterrupt" + } + ], + "https://github.com/SparknightLLC/ComfyUI-GPENO": [ + [ + "GPENO Face Restoration" + ], + { + "author": "yangxy (yangtao9009@gmail.com)", + "title_aux": "ComfyUI-GPENO" + } + ], + "https://github.com/SparknightLLC/ComfyUI-ImageAutosize": [ + [ + "ImageAutosize" + ], + { + "title_aux": "ComfyUI-ImageAutosize" + } + ], + "https://github.com/SparknightLLC/ComfyUI-ImageAutotone": [ + [ + "ImageAutotone" + ], + { + "title_aux": "ComfyUI-ImageAutotone" + } + ], + "https://github.com/SparknightLLC/ComfyUI-LatentClamp": [ + [ + "LatentClamp" + ], + { + "title_aux": "ComfyUI-LatentClamp" + } + ], + "https://github.com/SparknightLLC/ComfyUI-MaskArbiter": [ + [ + "GroundingDinoSAM2SegmentList", + "MaskArbiter" + ], + { + "title_aux": "ComfyUI-MaskArbiter" + } + ], + "https://github.com/SparknightLLC/ComfyUI-WeightedRandomChoice": [ + [ + "WeightedRandomChoice" + ], + { + "title_aux": "ComfyUI-WeightedRandomChoice" + } + ], + "https://github.com/SpenserCai/ComfyUI-FunAudioLLM": [ + [ + "CosyVoiceCrossLingualNode", + "CosyVoiceInstructNode", + "CosyVoiceLoadSpeakerModelFromUrlNode", + "CosyVoiceLoadSpeakerModelNode", + "CosyVoiceSFTNode", + "CosyVoiceSaveSpeakerModelNode", + "CosyVoiceZeroShotNode", + "SenseVoiceNode" + ], + { + "title_aux": "ComfyUI-FunAudioLLM" + } + ], + "https://github.com/Stability-AI/ComfyUI-SAI_API": [ + [ + "Stability Conservative Upscale", + "Stability Control Sketch", + "Stability Control Structure", + "Stability Control Style", + "Stability Creative Upscale", + "Stability Erase", + "Stability Fast Upscale", + "Stability Image Core", + "Stability Image Ultra", + "Stability Inpainting", + "Stability Outpainting", + "Stability Remove Background", + "Stability Replace Background and Relight", + "Stability SD3", + "Stability Search And Recolor", + "Stability Search and Replace" + ], + { + "title_aux": "Stability API nodes for ComfyUI" + } + ], + "https://github.com/Stability-AI/stability-ComfyUI-nodes": [ + [ + "ColorBlend", + "ControlLoraSave", + "GetImageSize" + ], + { + "title_aux": "stability-ComfyUI-nodes" + } + ], + "https://github.com/StableLlama/ComfyUI-basic_data_handling": [ + [ + "Basic data handling: Boolean And", + "Basic data handling: Boolean Nand", + "Basic data handling: Boolean Nor", + "Basic data handling: Boolean Not", + "Basic data handling: Boolean Or", + "Basic data handling: Boolean Xor", + "Basic data handling: CastToBoolean", + "Basic data handling: CastToDict", + "Basic data handling: CastToFloat", + "Basic data handling: CastToInt", + "Basic data handling: CastToList", + "Basic data handling: CastToSet", + "Basic data handling: CastToString", + "Basic data handling: CompareLength", + "Basic data handling: ContinueFlow", + "Basic data handling: DataListAll", + "Basic data handling: DataListAny", + "Basic data handling: DataListAppend", + "Basic data handling: DataListContains", + "Basic data handling: DataListCount", + "Basic data handling: DataListCreate", + "Basic data handling: DataListCreateFromBoolean", + "Basic data handling: DataListCreateFromFloat", + "Basic data handling: DataListCreateFromInt", + "Basic data handling: DataListCreateFromString", + "Basic data handling: DataListEnumerate", + "Basic data handling: DataListExtend", + "Basic data handling: DataListFilter", + "Basic data handling: DataListFilterSelect", + "Basic data handling: DataListFirst", + "Basic data handling: DataListGetItem", + "Basic data handling: DataListIndex", + "Basic data handling: DataListInsert", + "Basic data handling: DataListLast", + "Basic data handling: DataListLength", + "Basic data handling: DataListMax", + "Basic data handling: DataListMin", + "Basic data handling: DataListPop", + "Basic data handling: DataListPopRandom", + "Basic data handling: DataListRange", + "Basic data handling: DataListRemove", + "Basic data handling: DataListReverse", + "Basic data handling: DataListSetItem", + "Basic data handling: DataListSlice", + "Basic data handling: DataListSort", + "Basic data handling: DataListSum", + "Basic data handling: DataListToList", + "Basic data handling: DataListToSet", + "Basic data handling: DataListZip", + "Basic data handling: DictCompare", + "Basic data handling: DictContainsKey", + "Basic data handling: DictCreate", + "Basic data handling: DictCreateFromBoolean", + "Basic data handling: DictCreateFromFloat", + "Basic data handling: DictCreateFromInt", + "Basic data handling: DictCreateFromItemsDataList", + "Basic data handling: DictCreateFromItemsList", + "Basic data handling: DictCreateFromLists", + "Basic data handling: DictCreateFromString", + "Basic data handling: DictExcludeKeys", + "Basic data handling: DictFilterByKeys", + "Basic data handling: DictFromKeys", + "Basic data handling: DictGet", + "Basic data handling: DictGetKeysValues", + "Basic data handling: DictGetMultiple", + "Basic data handling: DictInvert", + "Basic data handling: DictItems", + "Basic data handling: DictKeys", + "Basic data handling: DictLength", + "Basic data handling: DictMerge", + "Basic data handling: DictPop", + "Basic data handling: DictPopItem", + "Basic data handling: DictPopRandom", + "Basic data handling: DictRemove", + "Basic data handling: DictSet", + "Basic data handling: DictSetDefault", + "Basic data handling: DictUpdate", + "Basic data handling: DictValues", + "Basic data handling: Equal", + "Basic data handling: ExecutionOrder", + "Basic data handling: FloatAdd", + "Basic data handling: FloatAsIntegerRatio", + "Basic data handling: FloatCreate", + "Basic data handling: FloatDivide", + "Basic data handling: FloatDivideSafe", + "Basic data handling: FloatFromHex", + "Basic data handling: FloatHex", + "Basic data handling: FloatIsInteger", + "Basic data handling: FloatMultiply", + "Basic data handling: FloatPower", + "Basic data handling: FloatRound", + "Basic data handling: FloatSubtract", + "Basic data handling: FlowSelect", + "Basic data handling: ForceCalculation", + "Basic data handling: GreaterThan", + "Basic data handling: GreaterThanOrEqual", + "Basic data handling: IfElifElse", + "Basic data handling: IfElse", + "Basic data handling: IntAdd", + "Basic data handling: IntBitCount", + "Basic data handling: IntBitLength", + "Basic data handling: IntCreate", + "Basic data handling: IntCreateWithBase", + "Basic data handling: IntDivide", + "Basic data handling: IntDivideSafe", + "Basic data handling: IntFromBytes", + "Basic data handling: IntModulus", + "Basic data handling: IntMultiply", + "Basic data handling: IntPower", + "Basic data handling: IntSubtract", + "Basic data handling: IntToBytes", + "Basic data handling: IsNull", + "Basic data handling: LessThan", + "Basic data handling: LessThanOrEqual", + "Basic data handling: ListAll", + "Basic data handling: ListAny", + "Basic data handling: ListAppend", + "Basic data handling: ListContains", + "Basic data handling: ListCount", + "Basic data handling: ListCreate", + "Basic data handling: ListCreateFromBoolean", + "Basic data handling: ListCreateFromFloat", + "Basic data handling: ListCreateFromInt", + "Basic data handling: ListCreateFromString", + "Basic data handling: ListEnumerate", + "Basic data handling: ListExtend", + "Basic data handling: ListFirst", + "Basic data handling: ListGetItem", + "Basic data handling: ListIndex", + "Basic data handling: ListInsert", + "Basic data handling: ListLast", + "Basic data handling: ListLength", + "Basic data handling: ListMax", + "Basic data handling: ListMin", + "Basic data handling: ListPop", + "Basic data handling: ListPopRandom", + "Basic data handling: ListRange", + "Basic data handling: ListRemove", + "Basic data handling: ListReverse", + "Basic data handling: ListSetItem", + "Basic data handling: ListSlice", + "Basic data handling: ListSort", + "Basic data handling: ListSum", + "Basic data handling: ListToDataList", + "Basic data handling: ListToSet", + "Basic data handling: MathAbs", + "Basic data handling: MathAcos", + "Basic data handling: MathAsin", + "Basic data handling: MathAtan", + "Basic data handling: MathAtan2", + "Basic data handling: MathCeil", + "Basic data handling: MathCos", + "Basic data handling: MathDegrees", + "Basic data handling: MathE", + "Basic data handling: MathExp", + "Basic data handling: MathFloor", + "Basic data handling: MathFormula", + "Basic data handling: MathLog", + "Basic data handling: MathLog10", + "Basic data handling: MathMax", + "Basic data handling: MathMin", + "Basic data handling: MathPi", + "Basic data handling: MathRadians", + "Basic data handling: MathSin", + "Basic data handling: MathSqrt", + "Basic data handling: MathTan", + "Basic data handling: NotEqual", + "Basic data handling: NumberInRange", + "Basic data handling: PathAbspath", + "Basic data handling: PathBasename", + "Basic data handling: PathCommonPrefix", + "Basic data handling: PathDirname", + "Basic data handling: PathExists", + "Basic data handling: PathExpandVars", + "Basic data handling: PathGetCwd", + "Basic data handling: PathGetExtension", + "Basic data handling: PathGetSize", + "Basic data handling: PathGlob", + "Basic data handling: PathIsAbsolute", + "Basic data handling: PathIsDir", + "Basic data handling: PathIsFile", + "Basic data handling: PathJoin", + "Basic data handling: PathListDir", + "Basic data handling: PathLoadImageRGB", + "Basic data handling: PathLoadImageRGBA", + "Basic data handling: PathLoadMaskFromAlpha", + "Basic data handling: PathLoadMaskFromGreyscale", + "Basic data handling: PathLoadStringFile", + "Basic data handling: PathNormalize", + "Basic data handling: PathRelative", + "Basic data handling: PathSaveImageRGB", + "Basic data handling: PathSaveImageRGBA", + "Basic data handling: PathSaveStringFile", + "Basic data handling: PathSetExtension", + "Basic data handling: PathSplit", + "Basic data handling: PathSplitExt", + "Basic data handling: RegexFindallDataList", + "Basic data handling: RegexFindallList", + "Basic data handling: RegexGroupDict", + "Basic data handling: RegexSearchGroupsDataList", + "Basic data handling: RegexSearchGroupsList", + "Basic data handling: RegexSplitDataList", + "Basic data handling: RegexSplitList", + "Basic data handling: RegexSub", + "Basic data handling: RegexTest", + "Basic data handling: SetAdd", + "Basic data handling: SetAll", + "Basic data handling: SetAny", + "Basic data handling: SetContains", + "Basic data handling: SetCreate", + "Basic data handling: SetCreateFromBoolean", + "Basic data handling: SetCreateFromFloat", + "Basic data handling: SetCreateFromInt", + "Basic data handling: SetCreateFromString", + "Basic data handling: SetDifference", + "Basic data handling: SetDiscard", + "Basic data handling: SetEnumerate", + "Basic data handling: SetIntersection", + "Basic data handling: SetIsDisjoint", + "Basic data handling: SetIsSubset", + "Basic data handling: SetIsSuperset", + "Basic data handling: SetLength", + "Basic data handling: SetPop", + "Basic data handling: SetPopRandom", + "Basic data handling: SetRemove", + "Basic data handling: SetSum", + "Basic data handling: SetSymmetricDifference", + "Basic data handling: SetToDataList", + "Basic data handling: SetToList", + "Basic data handling: SetUnion", + "Basic data handling: StringCapitalize", + "Basic data handling: StringCasefold", + "Basic data handling: StringCenter", + "Basic data handling: StringComparison", + "Basic data handling: StringConcat", + "Basic data handling: StringCount", + "Basic data handling: StringDataListJoin", + "Basic data handling: StringDecode", + "Basic data handling: StringEncode", + "Basic data handling: StringEndswith", + "Basic data handling: StringEscape", + "Basic data handling: StringExpandtabs", + "Basic data handling: StringFind", + "Basic data handling: StringFormatMap", + "Basic data handling: StringIn", + "Basic data handling: StringIsAlnum", + "Basic data handling: StringIsAlpha", + "Basic data handling: StringIsAscii", + "Basic data handling: StringIsDecimal", + "Basic data handling: StringIsDigit", + "Basic data handling: StringIsIdentifier", + "Basic data handling: StringIsLower", + "Basic data handling: StringIsNumeric", + "Basic data handling: StringIsPrintable", + "Basic data handling: StringIsSpace", + "Basic data handling: StringIsTitle", + "Basic data handling: StringIsUpper", + "Basic data handling: StringLength", + "Basic data handling: StringListJoin", + "Basic data handling: StringLjust", + "Basic data handling: StringLower", + "Basic data handling: StringLstrip", + "Basic data handling: StringRemoveprefix", + "Basic data handling: StringRemovesuffix", + "Basic data handling: StringReplace", + "Basic data handling: StringRfind", + "Basic data handling: StringRjust", + "Basic data handling: StringRsplitDataList", + "Basic data handling: StringRsplitList", + "Basic data handling: StringRstrip", + "Basic data handling: StringSplitDataList", + "Basic data handling: StringSplitList", + "Basic data handling: StringSplitlinesDataList", + "Basic data handling: StringSplitlinesList", + "Basic data handling: StringStartswith", + "Basic data handling: StringStrip", + "Basic data handling: StringSwapcase", + "Basic data handling: StringTitle", + "Basic data handling: StringUnescape", + "Basic data handling: StringUpper", + "Basic data handling: StringZfill", + "Basic data handling: SwitchCase", + "Basic data handling: TimeAddDelta", + "Basic data handling: TimeDelta", + "Basic data handling: TimeDifference", + "Basic data handling: TimeExtract", + "Basic data handling: TimeFormat", + "Basic data handling: TimeNow", + "Basic data handling: TimeParse", + "Basic data handling: TimeSubtractDelta", + "Basic data handling: TimeToUnix", + "Basic data handling: UnixToTime" + ], + { + "title_aux": "Basic data handling" + } + ], + "https://github.com/StarAsh042/ComfyUI_RollingArtist": [ + [ + "RollingArtist" + ], + { + "title_aux": "ComfyUI_RollingArtist" + } + ], + "https://github.com/StarMagicAI/comfyui_tagger": [ + [ + "DownloadAndLoadFlorence2Lora_jsonL", + "DownloadAndLoadFlorence2Model_jsonL", + "Florence2ModelLoader_jsonL", + "Florence2Run_jsonL", + "batch_text_save_jsonL" + ], + { + "title_aux": "ComfyUI-tagger" + } + ], + "https://github.com/Starnodes2024/ComfyUI_StarBetaNodes": [ + [ + "StarFrameFromVideo", + "StarImageLoader1by1", + "StarRandomImageLoader", + "StarSavePanoramaJPEG" + ], + { + "title_aux": "ComfyUI_StarBetaNodes" + } + ], + "https://github.com/Starnodes2024/ComfyUI_StarNodes": [ + [ + "AdaptiveDetailEnhancement", + "DetailStarDaemon", + "FluxFillSampler", + "FluxStartSettings", + "Fluxstarsampler", + "OllamaModelChooser", + "SD35StartSettings", + "SDXLStartSettings", + "SDstarsampler", + "Star Face Loader", + "Star3LoRAs", + "StarConditioningLoader", + "StarConditioningSaver", + "StarDeleteSamplerSettings", + "StarDenoiseSlider", + "StarDivisibleDimension", + "StarEasyTextStorage", + "StarFiveWildcards", + "StarGridCaptionsBatcher", + "StarGridComposer", + "StarGridImageBatcher", + "StarImageSwitch", + "StarImageSwitch2", + "StarInfiniteYouAdvancedPatchMaker", + "StarInfiniteYouApply", + "StarInfiniteYouFaceSwapMod", + "StarInfiniteYouPatch", + "StarInfiniteYouPatchCombine", + "StarInfiniteYouSaver", + "StarLatentSwitch", + "StarLoadSamplerSettings", + "StarNewsScraper", + "StarPSDSaver", + "StarPSDSaver2", + "StarPaletteExtractor", + "StarSaveSamplerSettings", + "StarTextFilter", + "StarTextInput", + "StarWildcardsAdvanced", + "Star_Image2Latent", + "Star_Show_Last_Frame", + "Starnodes_Aspect_Ratio", + "Starnodes_Aspect_Ratio_Advanced", + "Starnodes_Aspect_Video_Ratio", + "Starupscale" + ], + { + "title_aux": "ComfyUI_StarNodes" + } + ], + "https://github.com/StartHua/ComfyUI_OOTDiffusion_CXH": [ + [ + "Ood_CXH" + ], + { + "title_aux": "ComfyUI_OOTDiffusion_CXH" + } + ], + "https://github.com/StartHua/ComfyUI_PCDMs": [ + [ + "PCDMS_CXH" + ], + { + "title_aux": "ComfyUI_PCDMs" + } + ], + "https://github.com/StartHua/ComfyUI_Seg_VITON": [ + [ + "segformer_agnostic", + "segformer_clothes", + "segformer_remove_bg", + "stabel_vition" + ], + { + "title_aux": "ComfyUI_Seg_VITON" + } + ], + "https://github.com/StartHua/Comfyui_CXH_DeepLX": [ + [ + "CXH_DeepLX_Free", + "CXH_DeepLX_translate" + ], + { + "title_aux": "Comfyui_CXH_DeepLX" + } + ], + "https://github.com/StartHua/Comfyui_CXH_FluxLoraMerge": [ + [ + "CXH_Lora_Merge" + ], + { + "title_aux": "Comfyui_CXH_FluxLoraMerge" + } + ], + "https://github.com/StartHua/Comfyui_CXH_Phi_3.5": [ + [ + "CXH_Phi_Run", + "CXH_Phi_chat_load", + "CXH_Phi_chat_min", + "CXH_Phi_load" + ], + { + "title_aux": "Comfyui_CXH_Phi_3.5" + } + ], + "https://github.com/StartHua/Comfyui_Gemini2": [ + [ + "CXH_Gemini2_TX", + "CXH_Gemini2_Vision", + "CXH_Local_Prompt" + ], + { + "title_aux": "Comfyui_Gemini2" + } + ], + "https://github.com/StartHua/Comfyui_joytag": [ + [ + "CXH_JoyTag" + ], + { + "title_aux": "Comfyui_joytag" + } + ], + "https://github.com/StartHua/Comfyui_segformer_b2_clothes": [ + [ + "segformer_b2_clothes", + "segformer_b3_fashion" + ], + { + "title_aux": "comfyui_segformer_b2_clothes" + } + ], + "https://github.com/Steudio/ComfyUI_Steudio": [ + [ + "Combine Tiles", + "Display UI", + "Divide Image and Select Tile", + "Divide and Conquer Algorithm", + "Load Images into List", + "Ratio Calculator", + "Ratio to Size", + "Seed Shifter", + "Sequence Generator", + "Simple Config" + ], + { + "title_aux": "ComfyUI Steudio" + } + ], + "https://github.com/Style-Mosaic/dino-x-comfyui-node": [ + [ + "DinoxDetector" + ], + { + "title_aux": "ComfyUI DINO-X Detector Node" + } + ], + "https://github.com/SuperBeastsAI/ComfyUI-SuperBeasts": [ + [ + "Deflicker - Experimental (SuperBeasts.AI)", + "HDR Effects (SuperBeasts.AI)", + "Image Batch Manager (SuperBeasts.AI)", + "Make Resized Mask Batch (SuperBeasts.AI)", + "Mask Batch Manager (SuperBeasts.AI)", + "Pixel Deflicker - Experimental (SuperBeasts.AI)", + "SB Load Model (SuperBeasts.AI)", + "String List Manager (SuperBeasts.AI)", + "Super Pop Color Adjustment (SuperBeasts.AI)", + "Super Pop Residual Blend (SuperBeasts.AI)" + ], + { + "title_aux": "ComfyUI-SuperBeasts" + } + ], + "https://github.com/SuperMasterBlasterLaser/ComfyUI_YOLO_Classifiers": [ + [ + "YOLO Classifier Model Loader", + "YOLO Classify" + ], + { + "title_aux": "ComfyUI_YOLO_Classifiers" + } + ], + "https://github.com/Suzie1/ComfyUI_Comfyroll_CustomNodes": [ + [ + "CR 8 Channel In", + "CR 8 Channel Out", + "CR Apply ControlNet", + "CR Apply LoRA Stack", + "CR Apply Model Merge", + "CR Apply Multi Upscale", + "CR Apply Multi-ControlNet", + "CR Arabic Text RTL", + "CR Aspect Ratio", + "CR Aspect Ratio Banners", + "CR Aspect Ratio SDXL", + "CR Aspect Ratio Social Media", + "CR Batch Images From List", + "CR Batch Process Switch", + "CR Binary Pattern", + "CR Binary To Bit List", + "CR Bit Schedule", + "CR Central Schedule", + "CR Checker Pattern", + "CR Clamp Value", + "CR Clip Input Switch", + "CR Color Bars", + "CR Color Gradient", + "CR Color Panel", + "CR Color Tint", + "CR Combine Prompt", + "CR Combine Schedules", + "CR Comic Panel Templates", + "CR Composite Text", + "CR Conditioning Input Switch", + "CR Conditioning Mixer", + "CR ControlNet Input Switch", + "CR Current Frame", + "CR Cycle Images", + "CR Cycle Images Simple", + "CR Cycle LoRAs", + "CR Cycle Models", + "CR Cycle Text", + "CR Cycle Text Simple", + "CR Data Bus In", + "CR Data Bus Out", + "CR Debatch Frames", + "CR Diamond Panel", + "CR Draw Perspective Text", + "CR Draw Pie", + "CR Draw Shape", + "CR Draw Text", + "CR Encode Scheduled Prompts", + "CR Feathered Border", + "CR Float Range List", + "CR Float To Integer", + "CR Float To String", + "CR Font File List", + "CR Get Parameter From Prompt", + "CR Gradient Float", + "CR Gradient Integer", + "CR Half Drop Panel", + "CR Halftone Filter", + "CR Halftone Grid", + "CR Hires Fix Process Switch", + "CR Image Border", + "CR Image Grid Panel", + "CR Image Input Switch", + "CR Image Input Switch (4 way)", + "CR Image List", + "CR Image List Simple", + "CR Image Output", + "CR Image Panel", + "CR Image Pipe Edit", + "CR Image Pipe In", + "CR Image Pipe Out", + "CR Image Size", + "CR Img2Img Process Switch", + "CR Increment Float", + "CR Increment Integer", + "CR Index", + "CR Index Increment", + "CR Index Multiply", + "CR Index Reset", + "CR Input Text List", + "CR Integer Multiple", + "CR Integer Range List", + "CR Integer To String", + "CR Interpolate Latents", + "CR Intertwine Lists", + "CR Keyframe List", + "CR Latent Batch Size", + "CR Latent Input Switch", + "CR LoRA List", + "CR LoRA Stack", + "CR Load Animation Frames", + "CR Load Flow Frames", + "CR Load GIF As List", + "CR Load Image List", + "CR Load Image List Plus", + "CR Load LoRA", + "CR Load Prompt Style", + "CR Load Schedule From File", + "CR Load Scheduled ControlNets", + "CR Load Scheduled LoRAs", + "CR Load Scheduled Models", + "CR Load Text List", + "CR Mask Text", + "CR Math Operation", + "CR Model Input Switch", + "CR Model List", + "CR Model Merge Stack", + "CR Module Input", + "CR Module Output", + "CR Module Pipe Loader", + "CR Multi Upscale Stack", + "CR Multi-ControlNet Stack", + "CR Multiline Text", + "CR Output Flow Frames", + "CR Output Schedule To File", + "CR Overlay Text", + "CR Overlay Transparent Image", + "CR Page Layout", + "CR Pipe Switch", + "CR Polygons", + "CR Prompt List", + "CR Prompt List Keyframes", + "CR Prompt Scheduler", + "CR Prompt Text", + "CR Radial Gradient", + "CR Random Hex Color", + "CR Random LoRA Stack", + "CR Random Multiline Colors", + "CR Random Multiline Values", + "CR Random Panel Codes", + "CR Random RGB", + "CR Random RGB Gradient", + "CR Random Shape Pattern", + "CR Random Weight LoRA", + "CR Repeater", + "CR SD1.5 Aspect Ratio", + "CR SDXL Aspect Ratio", + "CR SDXL Base Prompt Encoder", + "CR SDXL Prompt Mix Presets", + "CR SDXL Prompt Mixer", + "CR SDXL Style Text", + "CR Save Text To File", + "CR Schedule Input Switch", + "CR Schedule To ScheduleList", + "CR Seamless Checker", + "CR Seed", + "CR Seed to Int", + "CR Select Font", + "CR Select ISO Size", + "CR Select Model", + "CR Select Resize Method", + "CR Set Switch From String", + "CR Set Value On Binary", + "CR Set Value On Boolean", + "CR Set Value on String", + "CR Simple Banner", + "CR Simple Binary Pattern", + "CR Simple Binary Pattern Simple", + "CR Simple Image Compare", + "CR Simple List", + "CR Simple Meme Template", + "CR Simple Prompt List", + "CR Simple Prompt List Keyframes", + "CR Simple Prompt Scheduler", + "CR Simple Schedule", + "CR Simple Text Panel", + "CR Simple Text Scheduler", + "CR Simple Text Watermark", + "CR Simple Titles", + "CR Simple Value Scheduler", + "CR Split String", + "CR Starburst Colors", + "CR Starburst Lines", + "CR String To Boolean", + "CR String To Combo", + "CR String To Number", + "CR Style Bars", + "CR Switch Model and CLIP", + "CR Text", + "CR Text Blacklist", + "CR Text Concatenate", + "CR Text Cycler", + "CR Text Input Switch", + "CR Text Input Switch (4 way)", + "CR Text Length", + "CR Text List", + "CR Text List Simple", + "CR Text List To String", + "CR Text Operation", + "CR Text Replace", + "CR Text Scheduler", + "CR Thumbnail Preview", + "CR Trigger", + "CR Upscale Image", + "CR VAE Decode", + "CR VAE Input Switch", + "CR Value", + "CR Value Cycler", + "CR Value Scheduler", + "CR Vignette Filter", + "CR XY From Folder", + "CR XY Index", + "CR XY Interpolate", + "CR XY List", + "CR XY Product", + "CR XY Save Grid Image", + "CR XYZ Index", + "CR_Aspect Ratio For Print" + ], + { + "author": "Suzie1", + "description": "175 custom nodes for artists, designers and animators.", + "nickname": "Comfyroll Studio", + "title": "Comfyroll Studio", + "title_aux": "Comfyroll Studio" + } + ], + "https://github.com/Sxela/ComfyWarp": [ + [ + "ApplyMask", + "ApplyMaskConditional", + "ApplyMaskLatent", + "ApplyMaskLatentConditional", + "ExtractFlowAndMixConsistencyMaps", + "ExtractOpticalFlow", + "FixedQueue", + "KeyframedFlowApplication", + "LoadFrame", + "LoadFrameFromDataset", + "LoadFrameFromFolder", + "LoadFramePairFromDataset", + "LoadFrameSequence", + "MakeFrameDataset", + "MakePaths", + "MixConsistencyMaps", + "OffsetNumber", + "RenderVideo", + "ResizeToFit", + "SaveFrame", + "SchedulerFloat", + "SchedulerInt", + "SchedulerString", + "WarpFrame" + ], + { + "title_aux": "ComfyWarp" + } + ], + "https://github.com/SykkoAtHome/ComfyUI_FaceProcessor": [ + [ + "FaceFitAndRestore", + "FaceTracker", + "FaceWrapper", + "HighPassFilter", + "ImageFeeder" + ], + { + "title_aux": "Face Processor for ComfyUI" + } + ], + "https://github.com/T-Ph525/ComfyUI-Underage-Filter": [ + [ + "AgeCheckerNode", + "MultiTypeGateNode", + "UnderageFilterNode" + ], + { + "title_aux": "Underage Filter" + } + ], + "https://github.com/TFL-TFL/ComfyUI_Text_Translation": [ + [ + "Get_Translator", + "Text", + "Text_Concatenate", + "Text_Switch", + "Text_Translation", + "Text_Translation_V2", + "Text_Translation_V2_Full" + ], + { + "title_aux": "ComfyUI_Text_Translation" + } + ], + "https://github.com/THtianhao/ComfyUI-FaceChain": [ + [ + "FC CropAndPaste", + "FC CropBottom", + "FC CropToOrigin", + "FC FaceDetectCrop", + "FC FaceFusion", + "FC FaceSegAndReplace", + "FC FaceSegment", + "FC MaskOP", + "FC RemoveCannyFace", + "FC ReplaceByMask", + "FC StyleLoraLoad" + ], + { + "title_aux": "ComfyUI-FaceChain" + } + ], + "https://github.com/THtianhao/ComfyUI-Portrait-Maker": [ + [ + "PM_BoxCropImage", + "PM_ColorTransfer", + "PM_ExpandMaskBox", + "PM_FaceFusion", + "PM_FaceShapMatch", + "PM_FaceSkin", + "PM_GetImageInfo", + "PM_ImageResizeTarget", + "PM_ImageScaleShort", + "PM_MakeUpTransfer", + "PM_MaskDilateErode", + "PM_MaskMerge2Image", + "PM_PortraitEnhancement", + "PM_RatioMerge2Image", + "PM_ReplaceBoxImg", + "PM_RetinaFace", + "PM_Similarity", + "PM_SkinRetouching", + "PM_SuperColorTransfer", + "PM_SuperMakeUpTransfer" + ], + { + "title_aux": "ComfyUI-Portrait-Maker" + } + ], + "https://github.com/TJ16th/comfyUI_TJ_NormalLighting": [ + [ + "EulerLightingNode" + ], + { + "title_aux": "comfyUI_TJ_NormalLighting" + } + ], + "https://github.com/TKRLAB/ComfyUI_Prompt_List_JSON": [ + [ + "ComfyUI_Prompt_JSON" + ], + { + "author": "TKRLAB", + "description": "ComfyUI JSON-based prompt management tool.", + "title": "ComfyUI_Prompt_List_JSON", + "title_aux": "Prompt List JSON" + } + ], + "https://github.com/TMElyralab/Comfyui-MusePose": [ + [ + "filenamestring", + "musepose", + "museposealign" + ], + { + "title_aux": "Comfyui-MusePose" + } + ], + "https://github.com/TRI3D-LC/ComfyUI-MiroBoard": [ + [ + "add-image-miro-board" + ], + { + "title_aux": "ComfyUI-MiroBoard" + } + ], + "https://github.com/TRI3D-LC/tri3d-comfyui-nodes": [ + [ + "TRI3D_CutByMaskAspectRatio", + "get_histogram_limits", + "main_light_layer", + "main_scaled_paste", + "simple_rescale_histogram", + "tri3d-HistogramEqualization", + "tri3d-LAB_2_RGB", + "tri3d-RGB_2_LAB", + "tri3d-adjust-neck", + "tri3d-atr-parse", + "tri3d-atr-parse-batch", + "tri3d-bgremove-mega", + "tri3d-clean_mask", + "tri3d-clear-memory", + "tri3d-clipdrop-bgremove-api", + "tri3d-clipdrop-bgreplace-api", + "tri3d-composite-image-splitter", + "tri3d-dwpose", + "tri3d-extract-hand", + "tri3d-extract-masks-batch", + "tri3d-extract-parts-batch", + "tri3d-extract-parts-batch2", + "tri3d-extract-parts-mask-batch", + "tri3d-extract-pascal-parts-batch", + "tri3d-extract_pose_part", + "tri3d-face-recognise", + "tri3d-flexible_color_extract", + "tri3d-float-to-image", + "tri3d-fuzzification", + "tri3d-get_histogram_limits", + "tri3d-get_mean_and_standard_deviation", + "tri3d-get_threshold_for_bg_swap", + "tri3d-image-mask-2-box", + "tri3d-image-mask-box-2-image", + "tri3d-interaction-canny", + "tri3d-levindabhi-cloth-seg", + "tri3d-load-pose-json", + "tri3d-load_AEMatter_Model", + "tri3d-luminosity-match", + "tri3d-main_transparent_background", + "tri3d-photoroom-bgremove-api", + "tri3d-pose-adaption", + "tri3d-pose-to-image", + "tri3d-position-hands", + "tri3d-position-parts-batch", + "tri3d-position-pascal-parts-batch", + "tri3d-recolor-mask", + "tri3d-recolor-mask-LAB_space", + "tri3d-recolor-mask-LAB_space_manual", + "tri3d-recolor-mask-RGB_space", + "tri3d-renormalize_array", + "tri3d-run_AEMatter_inference", + "tri3d-scaled-paste", + "tri3d-scaled-paste_unsafe", + "tri3d-simple_bg_swap", + "tri3d-simple_rescale_histogram", + "tri3d-skin-feathered-padded-mask", + "tri3d-swap-pixels", + "tri3d_CutByMaskAspectRatio", + "tri3d_H_Stack_Images", + "tri3d_Image_extend", + "tri3d_MaskAreaPercentage", + "tri3d_NSFWFilter", + "tri3d_NarrowfyImage", + "tri3d_Remove_Small_Mask_Islands", + "tri3d_SaveFlattenedPoseKpsAsJsonFile", + "tri3d_SaveImage_absolute", + "tri3d_SaveText_absolute", + "tri3d_Skip_HeadMask", + "tri3d_Skip_HeadMask_AddNeck", + "tri3d_Skip_LipMask", + "tri3d_SmartBox", + "tri3d_Smart_Depth", + "tri3d_StringContains", + "tri3d_Wait_And_Read_File", + "tri3d_extract_facer_mask", + "tri3d_fill_mask", + "tri3d_is_only_trouser", + "tri3d_position_pose_part" + ], + { + "title_aux": "tri3d-comfyui-nodes" + } + ], + "https://github.com/TTPlanetPig/Comfyui_Hunyuan3D": [ + [ + "GifImageViewerNode", + "Hunyuan3DNode", + "SquareImage" + ], + { + "title_aux": "Comfyui_Hunyuan3D" + } + ], + "https://github.com/TTPlanetPig/Comfyui_JC2": [ + [ + "ExtraOptionsNode", + "ExtraOptionsNode_Beta", + "JoyCaption2", + "JoyCaption2_simple", + "JoyCaptionBetaOne_Full", + "JoyCaptionBetaOne_Simple" + ], + { + "title_aux": "Comfyui_JC2" + } + ], + "https://github.com/TTPlanetPig/Comfyui_Object_Detect_QWen_VL": [ + [ + "BBoxesToSAM2", + "DownloadAndLoadQwenModel", + "QwenVLDetection" + ], + { + "title_aux": "ComfyUI Qwen2.5-VL Object Detection Node" + } + ], + "https://github.com/TTPlanetPig/Comfyui_Object_Migration": [ + [ + "TTP_Expand_And_Mask", + "TTP_text_mix" + ], + { + "title_aux": "Clothing Migration Kit" + } + ], + "https://github.com/TTPlanetPig/Comfyui_TTP_CN_Preprocessor": [ + [ + "TTPlanet_Tile_Preprocessor_GF", + "TTPlanet_Tile_Preprocessor_Simple", + "TTPlanet_Tile_Preprocessor_cufoff", + "TTPlanet_inpainting_Preprecessor" + ], + { + "title_aux": "for comfyui image proprocessor" + } + ], + "https://github.com/TTPlanetPig/Comfyui_TTP_Toolset": [ + [ + "TTP_CoordinateSplitter", + "TTP_Expand_And_Mask", + "TTP_Image_Assy", + "TTP_Image_Tile_Batch", + "TTP_Tile_image_size", + "TTP_condsetarea_merge", + "TTP_condsetarea_merge_test", + "TTP_condtobatch", + "TTP_text_mix", + "TTPlanet_Tile_Preprocessor_Simple", + "TeaCacheHunyuanVideoSampler" + ], + { + "title_aux": "Comfyui_TTP_Toolset" + } + ], + "https://github.com/TTPlanetPig/TTP_Comfyui_FramePack_SE": [ + [ + "TTPlanet_FramePack" + ], + { + "title_aux": "TTP_Comfyui_FramePack_SE" + } + ], + "https://github.com/TW-CUI/TW-CUI-Util": [ + [ + "TWCUI_Util_CommonSDXLResolutions", + "TWCUI_Util_FloatLiteral", + "TWCUI_Util_GenerationParameters", + "TWCUI_Util_GenerationPrompts", + "TWCUI_Util_IntLiteral", + "TWCUI_Util_ModelVAELORALoader", + "TWCUI_Util_ModelVAELoader", + "TWCUI_Util_MultilineStringLiteral", + "TWCUI_Util_SaveImage", + "TWCUI_Util_SaveImageAdvanced", + "TWCUI_Util_StringLiteral" + ], + { + "title_aux": "TW-CUI-Util" + } + ], + "https://github.com/TZOOTZ/ComfyUI-TZOOTZ_VHS": [ + [ + "TZOOTZ_VHSNode" + ], + { + "title_aux": "TZOOTZ VHS Effect Node" + } + ], + "https://github.com/TaiTair/comfyui-simswap": [ + [ + "Simswap", + "SimswapBuildFaceModel", + "SimswapFaceSwapOpt", + "SimswapImageDublicator", + "SimswapLoadFaceModel", + "SimswapMaskHelper", + "SimswapOptions", + "SimswapRestoreFace", + "SimswapSaveFaceModel" + ], + { + "title_aux": "Simswap Node for ComfyUI" + } + ], + "https://github.com/Taithrah/ComfyUI_Fens_Simple_Nodes": [ + [ + "FensTokenCounter", + "OptiEmptyLatent" + ], + { + "title_aux": "Fens-Simple-Nodes" + } + ], + "https://github.com/Taremin/comfyui-prompt-config": [ + [ + "PromptEdit", + "PromptGenerationConfig" + ], + { + "title_aux": "comfyui-prompt-config" + } + ], + "https://github.com/Taremin/comfyui-prompt-extranetworks": [ + [ + "PromptControlNetApply", + "PromptControlNetPrepare", + "PromptExtraNetworks" + ], + { + "title_aux": "ComfyUI Prompt ExtraNetworks" + } + ], + "https://github.com/Taremin/comfyui-string-tools": [ + [ + "StringToolsBalancedChoice", + "StringToolsConcat", + "StringToolsRandomChoice", + "StringToolsString", + "StringToolsText" + ], + { + "title_aux": "ComfyUI String Tools" + } + ], + "https://github.com/Taremin/webui-monaco-prompt": [ + [ + "WebuiMonacoPromptFind", + "WebuiMonacoPromptReplace" + ], + { + "title_aux": "WebUI Monaco Prompt" + } + ], + "https://github.com/TeaCrab/ComfyUI-TeaNodes": [ + [ + "TC_ColorFill", + "TC_CropTo", + "TC_EqualizeCLAHE", + "TC_ImageResize", + "TC_ImageScale", + "TC_KorniaGamma", + "TC_RandomColorFill", + "TC_SizeApproximation" + ], + { + "title_aux": "ComfyUI-TeaNodes" + } + ], + "https://github.com/TemryL/ComfyS3": [ + [ + "DownloadFileS3", + "LoadImageS3", + "SaveImageS3", + "SaveVideoFilesS3", + "UploadFileS3" + ], + { + "title_aux": "ComfyS3" + } + ], + "https://github.com/TemryL/ComfyUI-IDM-VTON": [ + [ + "IDM-VTON", + "PipelineLoader" + ], + { + "title_aux": "ComfyUI-IDM-VTON [WIP]" + } + ], + "https://github.com/Temult/TWanSigmaGraph": [ + [ + "TWanSigmaGraph" + ], + { + "title_aux": "TWanSigmaGraph" + } + ], + "https://github.com/TencentQQGYLab/ComfyUI-ELLA": [ + [ + "CombineClipEllaEmbeds", + "ConcatConditionEllaEmbeds", + "ConditionToEllaEmbeds", + "ELLALoader", + "EllaApply", + "EllaCombineEmbeds", + "EllaEncode", + "EllaTextEncode", + "SetEllaTimesteps", + "T5TextEncode #ELLA", + "T5TextEncoderLoader #ELLA" + ], + { + "title_aux": "ComfyUI-ELLA" + } + ], + "https://github.com/Tensor-Art/ComfyUI_TENSOR_ART": [ + [ + "TA_AIToolsNode", + "TA_ExecuteNode", + "TA_UploadImageNode" + ], + { + "title_aux": "ComfyUI_TENSOR_ART" + } + ], + "https://github.com/TensorKaze/ComfyUI-TkNodes": [ + [ + "FluxAdvancedSampler", + "FluxLatentSampler", + "LoadImageAndScaleToTotalPixels", + "LoadModelAndUpscaleImage", + "MultiLatentSelector", + "MultiModelLoader", + "RepeatLatentBatchOptional", + "VAEEncodeOptional" + ], + { + "title_aux": "ComfyUI-TkNodes" + } + ], + "https://github.com/TheBarret/ZSuite": [ + [ + "ZSuite: Prompter", + "ZSuite: RF Noise", + "ZSuite: SeedMod" + ], + { + "title_aux": "ZSuite" + } + ], + "https://github.com/TheBill2001/comfyui-upscale-by-model": [ + [ + "UpscaleImageByUsingModel" + ], + { + "author": "Tr\u1ea7n Nam Tu\u1ea5n", + "description": "This custom node allow upscaling an image by a factor using a model.", + "nickname": "Upscale Image By (Using Model)", + "title": "Upscale Image By (Using Model)", + "title_aux": "comfyui-upscale-by-model" + } + ], + "https://github.com/TheLustriVA/ComfyUI-Image-Size-Tools": [ + [ + "FluxResolutionNode", + "ImageSizeDetectorNode", + "SD15ResolutionNode", + "SDXLResolutionNode", + "WAN21AdvancedResolutionNode", + "WAN21ResolutionNode" + ], + { + "title_aux": "ComfyUI Image Size Tool" + } + ], + "https://github.com/TheMistoAI/ComfyUI-Anyline": [ + [ + "AnyLinePreprocessor" + ], + { + "title_aux": "Anyline" + } + ], + "https://github.com/TheWhykiki/Whykiki-ComfyUIToolset": [ + [ + "SequentialImageLoaderV8" + ], + { + "title_aux": "Whykiki ComfyUI Toolset" + } + ], + "https://github.com/ThepExcel/aiangelgallery-comfyui": [ + [ + "ThepExcel_AiAngel_MultilineTextChoiceNode" + ], + { + "title_aux": "Multiline Text Choice Node for ComfyUI" + } + ], + "https://github.com/ThereforeGames/ComfyUI-Unprompted": [ + [ + "Unprompted", + "UnpromptedSetRack" + ], + { + "title_aux": "ComfyUI-Unprompted" + } + ], + "https://github.com/TiamaTiramisu/risutools": [ + [ + "CheckFileNamePrefixExists", + "LoadImageFromText", + "LoadLastFileNamePrefix", + "UUIDGenerator" + ], + { + "title_aux": "RisuTools" + } + ], + "https://github.com/TinyTerra/ComfyUI_tinyterraNodes": [ + [ + "ttN KSampler_v2", + "ttN advPlot combo", + "ttN advPlot images", + "ttN advPlot merge", + "ttN advPlot range", + "ttN advPlot string", + "ttN advanced xyPlot", + "ttN compareInput", + "ttN concat", + "ttN conditioning", + "ttN debugInput", + "ttN float", + "ttN hiresfixScale", + "ttN imageOutput", + "ttN imageREMBG", + "ttN int", + "ttN multiModelMerge", + "ttN pipe2BASIC", + "ttN pipe2DETAILER", + "ttN pipeEDIT", + "ttN pipeEncodeConcat", + "ttN pipeIN", + "ttN pipeKSampler", + "ttN pipeKSamplerAdvanced", + "ttN pipeKSamplerAdvanced_v2", + "ttN pipeKSamplerSDXL", + "ttN pipeKSamplerSDXL_v2", + "ttN pipeKSampler_v2", + "ttN pipeLoader", + "ttN pipeLoaderSDXL", + "ttN pipeLoaderSDXL_v2", + "ttN pipeLoader_v2", + "ttN pipeLoraStack", + "ttN pipeOUT", + "ttN seed", + "ttN text", + "ttN text3BOX_3WAYconcat", + "ttN text7BOX_concat", + "ttN textCycleLine", + "ttN textDebug", + "ttN textOutput", + "ttN tinyLoader", + "ttN xyPlot" + ], + { + "author": "tinyterra", + "description": "This extension offers extensive xyPlot, various pipe nodes, fullscreen image viewer based on node history, dynamic widgets, interface customization, and more.", + "nickname": "\ud83c\udf0f", + "nodename_pattern": "^ttN ", + "title": "tinyterraNodes", + "title_aux": "ComfyUI_tinyterraNodes" + } + ], + "https://github.com/Tlant/ComfyUI-OllamaPromptsGeneratorTlant": [ + [ + "LoadImageAndExtractMetadataTlant", + "LoadRandomTxtFileTlant", + "LoadRandomTxtFileTlantV2", + "LoadRandomTxtFileTlantV3", + "LoadSequencedTxtFileTlant", + "LoadSpecificTxtFileTlant", + "OllamaPromptsGeneratorTlant", + "OllamaSimpleTextGeneratorTlant", + "RandomImageLoaderTlant", + "ReasoningLLMOutputCleaner", + "SaveImagePairForKontext", + "StringFormatterTlant" + ], + { + "title_aux": "ComfyUI-OllamaPromptsGeneratorTlant" + } + ], + "https://github.com/ToTheBeginning/ComfyUI-DreamO": [ + [ + "ApplyDreamO", + "DreamOProcessorLoader", + "DreamORefEncode" + ], + { + "title_aux": "DreamO Comfyui" + } + ], + "https://github.com/Tr1dae/ComfyUI-Dequality": [ + [ + "Dequality" + ], + { + "title_aux": "ComfyUI-Dequality" + } + ], + "https://github.com/Trgtuan10/ComfyUI_YoloSegment_Mask": [ + [ + "Object Mask" + ], + { + "title_aux": "ComfyUI_YoloSegment_Mask" + } + ], + "https://github.com/Tropfchen/ComfyUI-Embedding_Picker": [ + [ + "EmbeddingPicker" + ], + { + "title_aux": "Embedding Picker" + } + ], + "https://github.com/Tropfchen/ComfyUI-yaResolutionSelector": [ + [ + "YARS", + "YARSAdv" + ], + { + "title_aux": "YARS: Yet Another Resolution Selector" + } + ], + "https://github.com/TrophiHunter/ComfyUI_Photography_Nodes": [ + [ + "Bloom", + "Bloom Lens Flares", + "Chromatic Aberration", + "Contrast Adaptive Sharpening", + "Contrast Brightness", + "Depth of Field", + "Get Watermark", + "Halation", + "Lens Dirt", + "Lens Distortion", + "Levels Adjustment", + "Lut", + "Manga Toner", + "Monitor Filter", + "Multi Scale Contrast", + "NTSC Filter", + "Noise", + "Physically Accurate Lens Dirt", + "Pixel Art", + "Saturation Vibrance", + "Sensor Dust", + "Sharpen Simple", + "Sharpen Unsharp Mask", + "Tint", + "VHS Chroma Smear", + "VHS Degrade", + "Vignette Effect", + "Watermark" + ], + { + "title_aux": "Photography Nodes" + } + ], + "https://github.com/Trung0246/ComfyUI-0246": [ + [ + "0246.Beautify", + "0246.BoxRange", + "0246.CastReroute", + "0246.Cloud", + "0246.Count", + "0246.Highway", + "0246.HighwayBatch", + "0246.Hold", + "0246.Hub", + "0246.Junction", + "0246.JunctionBatch", + "0246.Loop", + "0246.Merge", + "0246.Meta", + "0246.RandomInt", + "0246.Script", + "0246.ScriptNode", + "0246.ScriptPile", + "0246.ScriptRule", + "0246.Stringify", + "0246.Switch", + "0246.Tag" + ], + { + "author": "Trung0246", + "description": "Random nodes for ComfyUI I made to solve my struggle with ComfyUI (ex: pipe, process). Have varying quality.", + "nickname": "ComfyUI-0246", + "title": "ComfyUI-0246", + "title_aux": "ComfyUI-0246" + } + ], + "https://github.com/Ttl/ComfyUi_NNLatentUpscale": [ + [ + "NNLatentUpscale" + ], + { + "preemptions": [ + "NNLatentUpscale" + ], + "title_aux": "ComfyUI Neural Network Latent Upscale" + } + ], + "https://github.com/TylerZoro/SD3-Scaling": [ + [ + "SD3ImageScaleToTotalPixels" + ], + { + "title_aux": "SD3-Scaling" + } + ], + "https://github.com/Umikaze-job/select_folder_path_easy": [ + [ + "SelectFolderPathEasy" + ], + { + "title_aux": "select_folder_path_easy" + } + ], + "https://github.com/VAST-AI-Research/ComfyUI-Tripo": [ + [ + "TripoAPIDraft", + "TripoAnimateRetargetNode", + "TripoAnimateRigNode", + "TripoConvertNode", + "TripoMeshCompletion", + "TripoMeshSegmentation", + "TripoRefineModel", + "TripoSmartLowPoly", + "TripoStylizeModel", + "TripoTextureModel" + ], + { + "title_aux": "Tripo for ComfyUI" + } + ], + "https://github.com/VK/vk-nodes": [ + [ + "PrepareJobs", + "SketchyText", + "SketchyThumbnail", + "TiledConfigNode", + "TiledCropNode", + "TiledRenderNode", + "TiledSetupNode", + "VKLoadAudio" + ], + { + "title_aux": "VK Nodes" + } + ], + "https://github.com/Vaibhavs10/ComfyUI-DDUF": [ + [ + "DDUFLoader", + "DiffusersModelMakeup", + "DiffusersPipelineLoader", + "DiffusersSchedulerLoader", + "DiffusersSimpleSampler" + ], + { + "title_aux": "ComfyUI-DDUF" + } + ], + "https://github.com/VangengLab/ComfyUI-LivePortrait_v2": [ + [ + "LivePortraitProcess_animal" + ], + { + "title_aux": "ComfyUI-LivePortrait_v2" + } + ], + "https://github.com/VangengLab/ComfyUI-LivePortrait_v3": [ + [ + "LivePortraitp2p" + ], + { + "title_aux": "ComfyUI-LivePortrait_v3" + } + ], + "https://github.com/Vaporbook/ComfyUI-SaveImage-PP": [ + [ + "SaveImagePP" + ], + { + "title_aux": "ComfyUI-SaveImage-PP" + } + ], + "https://github.com/VertexAnomaly/ComfyUI_ImageSentinel": [ + [ + "ImageSentinel" + ], + { + "title_aux": "ComfyUI_ImageSentinel" + } + ], + "https://github.com/VertexStudio/roblox-comfyui-nodes": [ + [ + "FirstLetterNode", + "FlowNodes", + "MirrorEffectNode", + "SaveImageNode", + "ScaleImageNode", + "SwitchImageNode", + "SwitchTextNode", + "TextToImageNode" + ], + { + "title_aux": "roblox-comfyui-nodes" + } + ], + "https://github.com/VikramxD/VEnhancer-ComfyUI-Wrapper": [ + [ + "MultiGPUInference", + "MultiGPUVEnhancerLoader", + "SingleGPUInference", + "SingleGPUVEnhancerLoader", + "VideoLoader", + "VideoSaver" + ], + { + "title_aux": "VEnhancer ComfyUI Extension" + } + ], + "https://github.com/Visionatrix/ComfyUI-Gemini": [ + [ + "ConcatText_Zho", + "DisplayText_Zho", + "Gemini_15P_API_S_Advance_Zho", + "Gemini_15P_API_S_Chat_Advance_Zho", + "Gemini_API_Chat_Zho", + "Gemini_API_S_Chat_Zho", + "Gemini_API_S_Vsion_ImgURL_Zho", + "Gemini_API_S_Zho", + "Gemini_API_Vsion_ImgURL_Zho", + "Gemini_API_Zho", + "Gemini_FileUpload_API_S_Zho", + "Gemini_File_API_S_Zho" + ], + { + "title_aux": "ComfyUI-Gemini" + } + ], + "https://github.com/Visionatrix/ComfyUI-RemoteVAE": [ + [ + "RemoteVAEDecode" + ], + { + "title_aux": "ComfyUI-RemoteVAE" + } + ], + "https://github.com/Visionatrix/ComfyUI-Visionatrix": [ + [ + "StyleAlignedBatchAlign", + "VixCheckboxLogic", + "VixDictionaryConvert", + "VixDictionaryGet", + "VixDictionaryNew", + "VixDictionaryUpdate", + "VixDynamicLoraDefinition", + "VixImageFilters", + "VixMultilineText", + "VixTextConcatenate", + "VixTextReplace", + "VixUiAspectRatioSelector", + "VixUiCheckbox", + "VixUiCheckboxLogic", + "VixUiList", + "VixUiListLogic", + "VixUiPrompt", + "VixUiRangeFloat", + "VixUiRangeInt", + "VixUiRangeScaleFloat", + "VixUiWorkflowMetadata" + ], + { + "title_aux": "ComfyUI-Visionatrix" + } + ], + "https://github.com/VrchStudio/comfyui-web-viewer": [ + [ + "VrchAnyOSCControlNode", + "VrchAudioChannelLoaderNode", + "VrchAudioConcatNode", + "VrchAudioGenresNode", + "VrchAudioRecorderNode", + "VrchAudioSaverNode", + "VrchAudioVisualizerNode", + "VrchAudioWebViewerNode", + "VrchBPMDetectorNode", + "VrchBooleanKeyControlNode", + "VrchChannelOSCControlNode", + "VrchChannelX4OSCControlNode", + "VrchDelayNode", + "VrchDelayOSCControlNode", + "VrchFloatKeyControlNode", + "VrchFloatOSCControlNode", + "VrchFloatRemapNode", + "VrchGamepadLoaderNode", + "VrchImageChannelLoaderNode", + "VrchImageFlipBookWebViewerNode", + "VrchImagePreviewBackgroundNewNode", + "VrchImagePreviewBackgroundNode", + "VrchImageSaverNode", + "VrchImageSwitchOSCControlNode", + "VrchImageWebSocketChannelLoaderNode", + "VrchImageWebSocketSettingsNode", + "VrchImageWebSocketSimpleWebViewerNode", + "VrchImageWebSocketWebViewerNode", + "VrchImageWebViewerNode", + "VrchInstantQueueKeyControlNode", + "VrchIntKeyControlNode", + "VrchIntOSCControlNode", + "VrchIntRemapNode", + "VrchJsonUrlLoaderNode", + "VrchJsonWebSocketChannelLoaderNode", + "VrchJsonWebSocketSenderNode", + "VrchMicLoaderNode", + "VrchMidiDeviceLoaderNode", + "VrchModelWebViewerNode", + "VrchOSCControlSettingsNode", + "VrchQRCodeNode", + "VrchSwitchOSCControlNode", + "VrchTextConcatOSCControlNode", + "VrchTextKeyControlNode", + "VrchTextSrtPlayerNode", + "VrchTextSwitchOSCControlNode", + "VrchTriggerToggleNode", + "VrchTriggerToggleX4Node", + "VrchTriggerToggleX8Node", + "VrchVideoWebViewerNode", + "VrchWebSocketServerNode", + "VrchWebViewerNode", + "VrchXYOSCControlNode", + "VrchXYZOSCControlNode", + "VrchXboxControllerNode" + ], + { + "title_aux": "ComfyUI Web Viewer" + } + ], + "https://github.com/VykosX/ControlFlowUtils": [ + [ + "Cycle", + "CycleContinue", + "CycleEnd", + "DataMonitor", + "FallbackAnyBatch", + "FallbackImagePreviewer", + "FolderSearch", + "GarbageCollector", + "HaltExecution", + "IfConditionSelector", + "ImageResolutionAdjust", + "InvertCondition", + "LoopClose", + "LoopOpen", + "LoraSelector", + "MemoryStorage", + "ModelSelector", + "NullInput", + "NullOutput", + "ReadTextFile", + "SaveTextFile", + "SimpleToggle", + "StringOperation", + "UniversalSwitch", + "UnloadModels", + "VAESelector", + "Wait" + ], + { + "title_aux": "ControlFlowUtils" + } + ], + "https://github.com/WASasquatch/ComfyUI_Preset_Merger": [ + [ + "Preset_Model_Merge" + ], + { + "title_aux": "ComfyUI Preset Merger" + } + ], + "https://github.com/WASasquatch/FreeU_Advanced": [ + [ + "FreeU (Advanced)", + "FreeU_V2 (Advanced)" + ], + { + "title_aux": "FreeU_Advanced" + } + ], + "https://github.com/WASasquatch/PPF_Noise_ComfyUI": [ + [ + "Blend Latents (PPF Noise)", + "Cross-Hatch Power Fractal (PPF Noise)", + "Images as Latents (PPF Noise)", + "Perlin Power Fractal Latent (PPF Noise)" + ], + { + "title_aux": "PPF_Noise_ComfyUI" + } + ], + "https://github.com/WASasquatch/PowerNoiseSuite": [ + [ + "Blend Latents (PPF Noise)", + "Cross-Hatch Power Fractal (PPF Noise)", + "Cross-Hatch Power Fractal Settings (PPF Noise)", + "Images as Latents (PPF Noise)", + "Latent Adjustment (PPF Noise)", + "Latents to CPU (PPF Noise)", + "Linear Cross-Hatch Power Fractal (PPF Noise)", + "Perlin Power Fractal Latent (PPF Noise)", + "Perlin Power Fractal Settings (PPF Noise)", + "Power KSampler Advanced (PPF Noise)", + "Power-Law Noise (PPF Noise)" + ], + { + "title_aux": "Power Noise Suite for ComfyUI" + } + ], + "https://github.com/WASasquatch/WAS_Extras": [ + [ + "BLVAEEncode", + "CLIPTextEncodeList", + "CLIPTextEncodeSequence2", + "ConditioningBlend", + "DebugInput", + "KSamplerSeq", + "KSamplerSeq2", + "VAEEncodeForInpaint (WAS)", + "VividSharpen", + "VividSharpenV2" + ], + { + "title_aux": "WAS_Extras" + } + ], + "https://github.com/WASasquatch/face-upscaling-and-seamless-embedding": [ + [ + "FUSEGenericKSampler", + "FUSEKSampler", + "FUSESamplerMaskOptions", + "FUSEYOLOSettings" + ], + { + "title_aux": "FUSE Face Enhancer" + } + ], + "https://github.com/WUYUDING2583/ComfyUI-Save-Image-Callback": [ + [ + "Save Image With Callback" + ], + { + "title_aux": "Save Image With Callback" + } + ], + "https://github.com/WX-NPS1598/ComfyUI-Auto_Crop_By_NPS": [ + [ + "AutoCropByNPS" + ], + { + "title_aux": "Auto Crop By NPS" + } + ], + "https://github.com/WaddingtonHoldings/ComfyUI-InstaSD": [ + [ + "GPTImage1Generate", + "InstaCBoolean", + "InstaCFloat", + "InstaCInteger", + "InstaCLoadImageFromS3", + "InstaCLoraLoader", + "InstaCSaveImageToS3", + "InstaCSeed", + "InstaCText", + "InstaCTextML", + "InstaLoadImageLocal", + "InstaLoadImageWithMask", + "InstaPromptMultipleStyleSelector", + "InstaPromptStyleSelector", + "LoadVideo", + "PreViewVideo" + ], + { + "title_aux": "InstaSD nodes for ComfyUI" + } + ], + "https://github.com/WainWong/ComfyUI-Loop-image": [ + [ + "CyberEve_BatchImageLoopClose", + "CyberEve_BatchImageLoopOpen", + "CyberEve_LoopIndexSwitch", + "CyberEve_MaskMerge", + "CyberEve_MaskSegmentation", + "CyberEve_SingleImageLoopClose", + "CyberEve_SingleImageLoopOpen" + ], + { + "title_aux": "ComfyUI-Loop-image" + } + ], + "https://github.com/Wakfull33/ComfyUI-SaveImageCivitAI": [ + [ + "SaveCivitai" + ], + { + "title_aux": "ComfyUI-SaveImageCivitAI" + } + ], + "https://github.com/WangPengxing/ComfyUI_WPX_Node": [ + [ + "AnimalContour", + "AnimalContourSilhouette", + "CircleContour", + "DetermineRowsAndCols", + "PenetrateStyle", + "RectangleContour", + "SplitMaskElements", + "SplitStickers" + ], + { + "title_aux": "ComfyUI WPX Nodes" + } + ], + "https://github.com/WarpedAnimation/ComfyUI-WarpedToolset": [ + [ + "ClipLoaderGGUF", + "DualClipLoaderGGUF", + "GGUFRun", + "GGUFSave", + "GGUFUndo", + "LoaderGGUF", + "LoaderGGUFAdvanced", + "QuadrupleClipLoaderGGUF", + "TENSORBoost", + "TENSORCut", + "TripleClipLoaderGGUF", + "VaeGGUF", + "WarpedBasicGuider", + "WarpedBundleAllVideoImages", + "WarpedBundleVideoImages", + "WarpedCLIPLoader", + "WarpedCLIPVisionLoader", + "WarpedCheckpointLoader", + "WarpedClipLoaderGGUF", + "WarpedCreateEmptyImageBatch", + "WarpedCreateEmptyLatentBatch", + "WarpedCreateSpecialImageBatch", + "WarpedDualCLIPLoader", + "WarpedDualClipLoaderGGUF", + "WarpedDualEncoder", + "WarpedDualGuider", + "WarpedFramepackLoraSelectBatch", + "WarpedFramepackMultiLoraSelect", + "WarpedFramepackMultiLoraSelectExt", + "WarpedFramepackSampler", + "WarpedGetImageFromVideo", + "WarpedGetTwoImagesFromVideo", + "WarpedHunyuanImageToVideo", + "WarpedHunyuanLoraAvgMerge", + "WarpedHunyuanLoraBatchMerge", + "WarpedHunyuanLoraConvert", + "WarpedHunyuanLoraConvertKeys", + "WarpedHunyuanLoraMerge", + "WarpedHunyuanMultiLoraAvgMerge", + "WarpedHunyuanMultiLoraLoader", + "WarpedHunyuanMultiLoraMerge", + "WarpedHunyuanMultiLoraMixer", + "WarpedHunyuanMultiLoraMixerExt", + "WarpedHunyuanVideoLoraLoader", + "WarpedImageNoiseAugmentation", + "WarpedImageScaleToSide", + "WarpedLeapfusionHunyuanI2V", + "WarpedLoadFramePackModel", + "WarpedLoadLorasBatchByPrefix", + "WarpedLoadVideosBatch", + "WarpedLoaderGGUF", + "WarpedLoraKeysAndMetadataReader", + "WarpedLoraReSave", + "WarpedMultiLoraLoader", + "WarpedNumericalConversion", + "WarpedReverseImageBatch", + "WarpedSamplerCustomAdv", + "WarpedSamplerCustomAdvLatent", + "WarpedSamplerCustomBatch", + "WarpedSamplerCustomScripted", + "WarpedSaveAnimatedPng", + "WarpedUpscaleWithModel", + "WarpedVAELoader", + "WarpedWanImageToVideo", + "WarpedWanLoadAndEditLoraBlocks", + "WarpedWanLoraMerge" + ], + { + "title_aux": "ComfyUI-WarpedToolset" + } + ], + "https://github.com/WaveSpeedAI/wavespeed-comfyui": [ + [ + "WaveSpeedAI BytedanceSeedanceLiteI2VNode", + "WaveSpeedAI BytedanceSeedanceLiteT2VNode", + "WaveSpeedAI BytedanceSeedanceProI2VNode", + "WaveSpeedAI BytedanceSeedanceProT2VNode", + "WaveSpeedAI Client", + "WaveSpeedAI DiaTTSNode", + "WaveSpeedAI Flux Image2Image", + "WaveSpeedAI Flux Loras", + "WaveSpeedAI Flux Text2Image", + "WaveSpeedAI FluxControlLoraCannyNode", + "WaveSpeedAI FluxControlLoraDepthNode", + "WaveSpeedAI FluxControlnetUnionPro2_0Node", + "WaveSpeedAI FluxDevFillNode", + "WaveSpeedAI FluxDevLoraNode", + "WaveSpeedAI FluxDevLoraUltraFastNode", + "WaveSpeedAI FluxDevNode", + "WaveSpeedAI FluxDevUltraFastNode", + "WaveSpeedAI FluxProReduxNode", + "WaveSpeedAI FluxReduxDevNode", + "WaveSpeedAI FluxSchnellLoraNode", + "WaveSpeedAI FluxSchnellNode", + "WaveSpeedAI FramepackNode", + "WaveSpeedAI GhibliNode", + "WaveSpeedAI GoogleVeo3FastNode", + "WaveSpeedAI GoogleVeo3Node", + "WaveSpeedAI HidreamE1FullNode", + "WaveSpeedAI HidreamI1DevNode", + "WaveSpeedAI HidreamI1FullNode", + "WaveSpeedAI Hunyuan3DV2MultiViewNode", + "WaveSpeedAI HunyuanCustomRef2V480pNode", + "WaveSpeedAI HunyuanCustomRef2V720pNode", + "WaveSpeedAI HunyuanVideoI2VNode", + "WaveSpeedAI HunyuanVideoT2VNode", + "WaveSpeedAI InstantCharacterNode", + "WaveSpeedAI KwaivgiKlingV16I2VProNode", + "WaveSpeedAI KwaivgiKlingV16I2VStandardNode", + "WaveSpeedAI KwaivgiKlingV16T2VStandardNode", + "WaveSpeedAI KwaivgiKlingV21I2vMasterNode", + "WaveSpeedAI KwaivgiKlingV21I2vProNode", + "WaveSpeedAI KwaivgiKlingV21I2vStandardNode", + "WaveSpeedAI KwaivgiKlingV21T2vMasterNode", + "WaveSpeedAI LtxVideoV097I2V480pNode", + "WaveSpeedAI LtxVideoV097I2V720pNode", + "WaveSpeedAI MMAudioV2Node", + "WaveSpeedAI Magi124bNode", + "WaveSpeedAI Minimax Image2Video", + "WaveSpeedAI MinimaxVideo01Node", + "WaveSpeedAI NightmareAIRealESRGANNode", + "WaveSpeedAI Preview Video", + "WaveSpeedAI SDXLLoraNode", + "WaveSpeedAI SDXLNode", + "WaveSpeedAI Save Audio", + "WaveSpeedAI SeedEditV3Node", + "WaveSpeedAI SeedreamV3Node", + "WaveSpeedAI SkyReelsV1Node", + "WaveSpeedAI Step1xEditNode", + "WaveSpeedAI UnoNode", + "WaveSpeedAI Upload Image", + "WaveSpeedAI Veo2I2vNode", + "WaveSpeedAI Veo2T2vNode", + "WaveSpeedAI ViduImageToVideo20Node", + "WaveSpeedAI ViduReferenceToVideo20Node", + "WaveSpeedAI ViduStartEndToVideo20Node", + "WaveSpeedAI Wan Image2Video", + "WaveSpeedAI Wan Loras", + "WaveSpeedAI Wan Text2Video", + "WaveSpeedAI Wan2114BVaceNode", + "WaveSpeedAI Wan21I2V480pLoraNode", + "WaveSpeedAI Wan21I2V480pLoraUltraFastNode", + "WaveSpeedAI Wan21I2V480pNode", + "WaveSpeedAI Wan21I2V480pUltraFastNode", + "WaveSpeedAI Wan21I2V720pLoraNode", + "WaveSpeedAI Wan21I2V720pLoraUltraFastNode", + "WaveSpeedAI Wan21I2V720pNode", + "WaveSpeedAI Wan21I2V720pUltraFastNode", + "WaveSpeedAI Wan21T2V480pLoraNode", + "WaveSpeedAI Wan21T2V480pLoraUltraFastNode", + "WaveSpeedAI Wan21T2V480pUltraFastNode", + "WaveSpeedAI Wan21T2V720pLoraNode", + "WaveSpeedAI Wan21T2V720pLoraUltraFastNode", + "WaveSpeedAI Wan21T2V720pNode", + "WaveSpeedAI Wan21T2V720pUltraFastNode" + ], + { + "title_aux": "wavespeed-comfyui" + } + ], + "https://github.com/WebDev9000/WebDev9000-Nodes": [ + [ + "IgnoreBraces", + "SettingsSwitch" + ], + { + "title_aux": "WebDev9000-Nodes" + } + ], + "https://github.com/Wenaka2004/ComfyUI-TagClassifier": [ + [ + "LLMProcessingNode" + ], + { + "title_aux": "ComfyUI-TagClassifier" + } + ], + "https://github.com/What-a-stupid-username/comfyui-InversedSampler": [ + [ + "SamplerInversedEulerNode" + ], + { + "title_aux": "comfyui_InversedSampler" + } + ], + "https://github.com/Wicloz/ComfyUI-Simply-Nodes": [ + [ + "WF_ConditionalLoraLoader", + "WF_FixupPixelArt", + "WF_MultilineText", + "WF_RandomStyle", + "WF_ResolutionSDXL", + "WF_TextFlow" + ], + { + "title_aux": "ComfyUI Simply Nodes" + } + ], + "https://github.com/Windecay/ComfyUI-ReservedVRAM": [ + [ + "ReservedVRAMSetter" + ], + { + "title_aux": "ComfyUI-ReservedVRAM" + } + ], + "https://github.com/X-School-Academy/X-FluxAgent": [ + [ + "X-FluxAgent.AICodeGenNode", + "X-FluxAgent.OpenAIChatnNode", + "X-FluxAgent.RichTextNode", + "X-FluxAgent.SaveTextNode" + ], + { + "title_aux": "X-FluxAgent" + } + ], + "https://github.com/X-T-E-R/ComfyUI-EasyCivitai-XTNodes": [ + [ + "CheckpointLoaderSimpleWithPreviews", + "CivitaiCheckpointLoaderSimple", + "CivitaiLoraLoader", + "CivitaiLoraLoaderStacked", + "CivitaiLoraLoaderStackedAdvanced", + "LoraLoaderStackedAdvancedWithPreviews", + "LoraLoaderStackedWithPreviews", + "LoraLoaderWithPreviews", + "XTNodesCleanPrompt", + "XTNodesPromptConcatenate" + ], + { + "title_aux": "ComfyUI Easy Civitai (XTNodes)" + } + ], + "https://github.com/XLabs-AI/x-flux-comfyui": [ + [ + "ApplyAdvancedFluxControlNet", + "ApplyAdvancedFluxIPAdapter", + "ApplyFluxControlNet", + "ApplyFluxIPAdapter", + "FluxLoraLoader", + "LoadFluxControlNet", + "LoadFluxIPAdapter", + "XlabsSampler" + ], + { + "title_aux": "x-flux-comfyui" + } + ], + "https://github.com/XWAVEart/comfyui-xwave-xlitch-nodes": [ + [ + "XWAVECellularNoiseNode", + "XWAVEPixelate", + "XWaveChromaticAberration", + "XWaveColorChannelManipulation", + "XWaveColorFilter", + "XWaveColorShiftExpansion", + "XWaveCurvedHueShift", + "XWaveGaussianBlur", + "XWaveHistogramGlitch", + "XWaveJPEGArtifacts", + "XWaveNoiseEffect", + "XWavePosterize", + "XWaveRGBChannelShift", + "XWaveSharpen" + ], + { + "title_aux": "ComfyUI XWAVE Nodes" + } + ], + "https://github.com/XchanBik/ComfyUI_SimpleBridgeNode": [ + [ + "LoraTextLoader", + "SimpleBridgeLoadNode", + "SimpleBridgeStoreNode" + ], + { + "description": "A custom node for ComfyUI to store and retrieve data dynamically.", + "nickname": "SimpleBridgeNode", + "title": "SimpleBridgeNode", + "title_aux": "ComfyUI_SimpleBridgeNode" + } + ], + "https://github.com/Xclbr7/ComfyUI-Merlin": [ + [ + "GeminiPromptExpander", + "Magic Photo Prompter \ud83e\ude84" + ], + { + "title_aux": "ComfyUI-Merlin: Magic Photo Prompter" + } + ], + "https://github.com/Xiangyu-CAS/HandFixer": [ + [ + "MediapipeHandNode" + ], + { + "title_aux": "HandFixer" + } + ], + "https://github.com/XieJunchen/comfyUI_LLM": [ + [ + "AppendImagesToBatch", + "CloudImageUploadNode", + "CloudImagesToVideoAndUpload", + "CloudVideoUploadNode", + "ComfyUI_LLM_Ollama", + "CreateEmptyImageBatch", + "DeepSeek_Online", + "GetFirstImageFromBatch", + "GetVideoClipByIndex", + "LoadGifFromLocal", + "LoadImgFromUrl", + "RemoveFirstOrLastImageFromBatch", + "SplitVideoByFrames", + "StringArrayFormatter", + "StringArrayIndexer" + ], + { + "title_aux": "comfyUI_LLM" + } + ], + "https://github.com/Xkipper/ComfyUI_SkipperNodes": [ + [ + "Embedding Stack", + "Simple Box" + ], + { + "title_aux": "ComfyUI_SkipperNodes" + } + ], + "https://github.com/XmYx/ComfyUI-SmolLM3": [ + [ + "SmolLM3ModelLoader", + "SmolLM3Sampler", + "SmolLM3SimpleGenerate" + ], + { + "title_aux": "ComfyUI-SmolLM3" + } + ], + "https://github.com/XmYx/deforum-comfy-nodes": [ + [ + "DeforumAddNoiseNode", + "DeforumAnimParamsNode", + "DeforumAreaPromptNode", + "DeforumBaseParamsNode", + "DeforumCacheLatentNode", + "DeforumCadenceNode", + "DeforumCadenceParamsNode", + "DeforumColorMatchNode", + "DeforumColorParamsNode", + "DeforumConditioningBlendNode", + "DeforumDepthParamsNode", + "DeforumDiffusionParamsNode", + "DeforumFILMInterpolationNode", + "DeforumFrameWarpNode", + "DeforumGetCachedLatentNode", + "DeforumHybridMotionNode", + "DeforumHybridParamsNode", + "DeforumHybridScheduleNode", + "DeforumIteratorNode", + "DeforumKSampler", + "DeforumLoadVideo", + "DeforumNoiseParamsNode", + "DeforumPromptNode", + "DeforumSeedNode", + "DeforumSetVAEDownscaleRatioNode", + "DeforumSimpleInterpolationNode", + "DeforumSingleSampleNode", + "DeforumTranslationParamsNode", + "DeforumVideoSaveNode" + ], + { + "title_aux": "Deforum Nodes" + } + ], + "https://github.com/Xyem/Xycuno-Oobabooga": [ + [ + "Oobabooga" + ], + { + "title_aux": "Xycuno Oobabooga" + } + ], + "https://github.com/YOUR-WORST-TACO/ComfyUI-TacoNodes": [ + [ + "Example", + "TacoAnimatedLoader", + "TacoGifMaker", + "TacoImg2ImgAnimatedLoader", + "TacoImg2ImgAnimatedProcessor", + "TacoLatent" + ], + { + "title_aux": "ComfyUI-TacoNodes" + } + ], + "https://github.com/YRIKKA/ComfyUI-InferenceTimeScaling": [ + [ + "InferenceTimeScaler", + "LoadCLIPScoreVerifier", + "LoadImageRewardVerifier", + "LoadQwenVLMVerifier" + ], + { + "title_aux": "ComfyUI-InferenceTimeScaling" + } + ], + "https://github.com/Yahweasel/ComfyUI-MinDalle": [ + [ + "MinDalleNode" + ], + { + "title_aux": "ComfyUI-MinDalle" + } + ], + "https://github.com/Yanick112/ComfyUI-ToSVG": [ + [ + "TS_ImageQuantize", + "TS_ImageToSVGStringBW_Potracer", + "TS_ImageToSVGStringBW_Vtracer", + "TS_ImageToSVGStringColor_Vtracer", + "TS_SVGBytesIOToString", + "TS_SVGPathSimplify", + "TS_SVGStringPreview", + "TS_SVGStringToImage", + "TS_SVGStringToSVGBytesIO", + "TS_SaveSVGString" + ], + { + "title_aux": "ComfyUI-ToSVG" + } + ], + "https://github.com/YaroslavIv/comfyui_swd": [ + [ + "SwDSelector" + ], + { + "title_aux": "SwD Preset Selector for ComfyUI" + } + ], + "https://github.com/YarvixPA/ComfyUI-NeuralMedia": [ + [ + "ApplyStyleModelEnhanced", + "ApplyStyleModelSimple", + "BatchImagesNode", + "FrameCalculator", + "InpaintConditioningNode", + "LoadImagesFromFolderNode", + "Prepimg2Vid", + "RemoveBackgroundNode", + "SaveCaptionsImages", + "SaveImageAdvance", + "StitchImages", + "StitchImagesAndMask", + "TextFieldNode", + "TileCheckpointPatchNode", + "UnstitchImages", + "UnstitchImagesAndMask", + "UpscaleImageWithModel", + "VTracerImageVectorizerNode", + "VacePatchLoader" + ], + { + "title_aux": "ComfyUI-NeuralMedia" + } + ], + "https://github.com/YaserJaradeh/comfyui-yaser-nodes": [ + [ + "ConditionalSelectionNode", + "IterativeUpscaleWithModelsNode" + ], + { + "title_aux": "Yaser-nodes for ComfyUI" + } + ], + "https://github.com/YinBailiang/MergeBlockWeighted_fo_ComfyUI": [ + [ + "MergeBlockWeighted" + ], + { + "title_aux": "MergeBlockWeighted_fo_ComfyUI" + } + ], + "https://github.com/Yo1up/Diffusion-Model-Detailer": [ + [ + "DetailModelUnwrapperNode", + "DetailModelWrapperNode" + ], + { + "title_aux": "Diffusion-Model-Detailer" + } + ], + "https://github.com/Yuan-ManX/ComfyUI-AniSora": [ + [ + "AniSora", + "AniSoraPrompt", + "LoadAniSoraModel", + "SaveAniSora" + ], + { + "title_aux": "ComfyUI-AniSora" + } + ], + "https://github.com/Yuan-ManX/ComfyUI-AudioX": [ + [ + "AudioXPrompt", + "Condition", + "Generate", + "LoadAudioXAudio", + "LoadAudioXModel", + "LoadAudioXVideo", + "SaveAudioXAudio" + ], + { + "title_aux": "ComfyUI-AudioX" + } + ], + "https://github.com/Yuan-ManX/ComfyUI-Bagel": [ + [ + "BagelPrompt", + "ImageEditing", + "ImageGeneration", + "ImageThinkEditing", + "ImageThinkGeneration", + "ImageUnderstanding", + "LoadBAGELModel", + "LoadEditImage" + ], + { + "title_aux": "ComfyUI-Bagel" + } + ], + "https://github.com/Yuan-ManX/ComfyUI-ChatterboxTTS": [ + [ + "ChatterboxAudioPrompt", + "ChatterboxPrompt", + "ChatterboxTTS", + "ChatterboxVC", + "LoadChatterboxAudio", + "LoadChatterboxTTSModel", + "LoadChatterboxTargetAudio", + "LoadChatterboxVCModel", + "SaveChatterboxAudio" + ], + { + "title_aux": "ComfyUI-ChatterboxTTS" + } + ], + "https://github.com/Yuan-ManX/ComfyUI-Cobra": [ + [ + "ColorizeImage", + "DrawColorHint", + "ExtractLineArt", + "GetColorValue", + "LoadCobraModel" + ], + { + "title_aux": "ComfyUI-Cobra" + } + ], + "https://github.com/Yuan-ManX/ComfyUI-Dia": [ + [ + "DiaTTS", + "InputDiaText", + "LoadDiaAudio", + "LoadDiaModel", + "SaveDiaAudio" + ], + { + "title_aux": "ComfyUI-Dia" + } + ], + "https://github.com/Yuan-ManX/ComfyUI-Direct3D-S2": [ + [ + "Direct3DS2", + "LoadDirect3DS2Image", + "LoadDirect3DS2Model", + "SaveDirect3DS2Mesh" + ], + { + "title_aux": "ComfyUI-Direct3D-S2" + } + ], + "https://github.com/Yuan-ManX/ComfyUI-HiDream-I1": [ + [ + "GenerateHiDreamImage", + "LoadHiDreamModel", + "SaveHiDreamImage" + ], + { + "title_aux": "ComfyUI-HiDream-I1" + } + ], + "https://github.com/Yuan-ManX/ComfyUI-HiggsAudio": [ + [ + "HiggsAudio", + "LoadHiggsAudioModel", + "LoadHiggsAudioPrompt", + "LoadHiggsAudioSystemPrompt", + "LoadHiggsAudioTokenizer", + "SaveHiggsAudio" + ], + { + "title_aux": "ComfyUI-HiggsAudio" + } + ], + "https://github.com/Yuan-ManX/ComfyUI-Hunyuan3D-2.1": [ + [ + "Hunyuan3DShapeGeneration", + "Hunyuan3DTexureSynthsis", + "LoadHunyuan3DImage", + "LoadHunyuan3DModel" + ], + { + "title_aux": "ComfyUI-Hunyuan3D-2.1" + } + ], + "https://github.com/Yuan-ManX/ComfyUI-HunyuanPortrait": [ + [ + "HunyuanPortrait", + "LoadHunyuanPortraitConfig", + "LoadHunyuanPortraitImage", + "LoadHunyuanPortraitVideo" + ], + { + "title_aux": "ComfyUI-HunyuanPortrait" + } + ], + "https://github.com/Yuan-ManX/ComfyUI-Kimi-VL": [ + [ + "KimiVL", + "LoadKimiVLImage", + "LoadKimiVLModel", + "SaveKimiVLText" + ], + { + "title_aux": "ComfyUI-Kimi-VL" + } + ], + "https://github.com/Yuan-ManX/ComfyUI-LLaMA-Mesh": [ + [ + "Apply Gradient Color", + "Chat LLaMa Mesh", + "Visualize Mesh" + ], + { + "title_aux": "ComfyUI-LLaMA-Mesh" + } + ], + "https://github.com/Yuan-ManX/ComfyUI-LayerAnimate": [ + [ + "LayerAnimateNode", + "LoadImages", + "LoadPretrainedModel" + ], + { + "title_aux": "ComfyUI-LayerAnimate" + } + ], + "https://github.com/Yuan-ManX/ComfyUI-LiveCC": [ + [ + "LiveCC", + "LiveCCPrompt", + "LoadLiveCCModel", + "LoadLiveCCVideo", + "SaveLiveCCText" + ], + { + "title_aux": "ComfyUI-LiveCC" + } + ], + "https://github.com/Yuan-ManX/ComfyUI-Matrix-Game": [ + [ + "GameVideoGenerator", + "LoadDiTModel", + "LoadGameImage", + "LoadMouseIcon", + "LoadTextEncoderModel", + "LoadVAEModel", + "MatrixGameOutput" + ], + { + "title_aux": "ComfyUI-Matrix-Game" + } + ], + "https://github.com/Yuan-ManX/ComfyUI-MoviiGen": [ + [ + "LoadMoviiGenModel", + "MoviiGen", + "MoviiGenPrompt", + "SaveMoviiGen" + ], + { + "title_aux": "ComfyUI-MoviiGen" + } + ], + "https://github.com/Yuan-ManX/ComfyUI-Multiverse": [ + [ + "PlayGame" + ], + { + "title_aux": "ComfyUI-Multiverse" + } + ], + "https://github.com/Yuan-ManX/ComfyUI-Muyan-TTS": [ + [ + "Generate", + "InputText", + "LoadMuyanTTSModel", + "LoadRefAudio", + "PromptText", + "SaveMuyanTTSAudio" + ], + { + "title_aux": "ComfyUI-Muyan-TTS" + } + ], + "https://github.com/Yuan-ManX/ComfyUI-OmniGen2": [ + [ + "LoadOmniGen2Image", + "LoadOmniGen2Model", + "OmniGen2" + ], + { + "title_aux": "ComfyUI-OmniGen2" + } + ], + "https://github.com/Yuan-ManX/ComfyUI-OrpheusTTS": [ + [ + "Long Text Generation", + "Single Text Generation" + ], + { + "title_aux": "ComfyUI-OrpheusTTS" + } + ], + "https://github.com/Yuan-ManX/ComfyUI-PhotoDoodle": [ + [ + "PhotoDoodle Gen" + ], + { + "title_aux": "ComfyUI-PhotoDoodle" + } + ], + "https://github.com/Yuan-ManX/ComfyUI-PosterCraft": [ + [ + "LoadCustomTransformer", + "LoadPipeline", + "LoadPosterCraftPrompt", + "LoadQwenModel", + "PosterCraft", + "SavePosterCraft" + ], + { + "title_aux": "ComfyUI-PosterCraft" + } + ], + "https://github.com/Yuan-ManX/ComfyUI-SkyReels-A2": [ + [ + "A2Prompt", + "A2VideoGenerator", + "CombineImages", + "LoadA2Model", + "NegativePrompt", + "ReferenceImages", + "SaveVideo" + ], + { + "title_aux": "ComfyUI-SkyReels-A2" + } + ], + "https://github.com/Yuan-ManX/ComfyUI-SkyworkUniPic": [ + [ + "Image2Text", + "ImageEditing", + "LoadSkyworkUniPicCheckpoint", + "LoadSkyworkUniPicConfig", + "LoadSkyworkUniPicImage", + "LoadSkyworkUniPicPrompt", + "SaveSkyworkUniPicEditImage", + "SaveSkyworkUniPicImage", + "Text2Image" + ], + { + "title_aux": "ComfyUI-SkyworkUniPic" + } + ], + "https://github.com/Yuan-ManX/ComfyUI-SoundHub": [ + [ + "Load Audio", + "Preview Audio", + "Save Audio" + ], + { + "title_aux": "ComfyUI-SoundHub" + } + ], + "https://github.com/Yuan-ManX/ComfyUI-Step1X-3D": [ + [ + "GeometryGeneration", + "GeometryLabelGeneration", + "LoadInputImage", + "LoadStep1X3DGeometryLabelModel", + "LoadStep1X3DGeometryModel", + "LoadStep1X3DTextureModel", + "LoadUntexturedMesh", + "SaveTexturedMesh", + "SaveUntexturedMesh", + "TexureSynthsis" + ], + { + "title_aux": "ComfyUI-Step1X-3D" + } + ], + "https://github.com/Yuan-ManX/ComfyUI-StyleStudio": [ + [ + "StyleStudio Image Stylization" + ], + { + "title_aux": "ComfyUI-StyleStudio" + } + ], + "https://github.com/Yuan-ManX/ComfyUI-ThinkSound": [ + [ + "LoadCaption", + "LoadCoTDescription", + "LoadOThinkSoundVideo", + "ThinkSound" + ], + { + "title_aux": "ComfyUI-ThinkSound" + } + ], + "https://github.com/Yuan-ManX/ComfyUI-UNO": [ + [ + "ConfigSave", + "ImageConcat", + "ImagePathLoader", + "ImageSave", + "UNOGenerator", + "UNOParams" + ], + { + "title_aux": "ComfyUI-UNO" + } + ], + "https://github.com/Yuan-ManX/ComfyUI-Vui": [ + [ + "LoadVuiModel", + "LoadVuiPrompt", + "SaveVui", + "Vui" + ], + { + "title_aux": "ComfyUI-Vui" + } + ], + "https://github.com/Yukinoshita-Yukinoe/ComfyUI-Qwen-Node": [ + [ + "QwenAPILLMNode" + ], + { + "title_aux": "ComfyUI-Qwen-Node" + } + ], + "https://github.com/ZHO-ZHO-ZHO/ComfyUI-APISR": [ + [ + "APISR_Lterative_Zho", + "APISR_ModelLoader_Zho", + "APISR_Zho" + ], + { + "title_aux": "APISR IN COMFYUI" + } + ], + "https://github.com/ZHO-ZHO-ZHO/ComfyUI-Animated-optical-illusions": [ + [ + "AOI_Processing_Zho" + ], + { + "title_aux": "ComfyUI-Animated-optical-illusions" + } + ], + "https://github.com/ZHO-ZHO-ZHO/ComfyUI-ArtGallery": [ + [ + "ArtGallery_Zho", + "ArtistsImage_Zho", + "CamerasImage_Zho", + "FilmsImage_Zho", + "MovementsImage_Zho", + "StylesImage_Zho" + ], + { + "title_aux": "ComfyUI-ArtGallery" + } + ], + "https://github.com/ZHO-ZHO-ZHO/ComfyUI-BRIA_AI-RMBG": [ + [ + "BRIA_RMBG_ModelLoader_Zho", + "BRIA_RMBG_Zho" + ], + { + "title_aux": "ComfyUI-BRIA_AI-RMBG" + } + ], + "https://github.com/ZHO-ZHO-ZHO/ComfyUI-DeepSeek-JanusPro": [ + [ + "Janus_ImageGeneration", + "Janus_ModelLoader", + "Janus_MultimodalUnderstanding" + ], + { + "title_aux": "ComfyUI-DeepSeek-JanusPro" + } + ], + "https://github.com/ZHO-ZHO-ZHO/ComfyUI-DepthFM": [ + [ + "DepthFM_Literative_Zho", + "DepthFM_ModelLoader_Zho", + "DepthFM_Zho" + ], + { + "title_aux": "DepthFM IN COMFYUI" + } + ], + "https://github.com/ZHO-ZHO-ZHO/ComfyUI-InstantID": [ + [ + "IDBaseModelLoader_fromhub", + "IDBaseModelLoader_local", + "IDControlNetLoader", + "IDGenerationNode", + "ID_Prompt_Styler", + "InsightFaceLoader_Zho", + "Ipadapter_instantidLoader" + ], + { + "title_aux": "ComfyUI-InstantID" + } + ], + "https://github.com/ZHO-ZHO-ZHO/ComfyUI-Phi-3-mini": [ + [ + "Phi3mini_4k_Chat_Zho", + "Phi3mini_4k_ModelLoader_Zho", + "Phi3mini_4k_Zho" + ], + { + "title_aux": "Phi-3-mini in ComfyUI" + } + ], + "https://github.com/ZHO-ZHO-ZHO/ComfyUI-PhotoMaker-ZHO": [ + [ + "BaseModel_Loader_fromhub", + "BaseModel_Loader_local", + "LoRALoader", + "NEW_PhotoMaker_Generation", + "PhotoMakerAdapter_Loader_fromhub", + "PhotoMakerAdapter_Loader_local", + "PhotoMaker_Generation", + "Prompt_Styler", + "Ref_Image_Preprocessing" + ], + { + "title_aux": "ComfyUI PhotoMaker (ZHO)" + } + ], + "https://github.com/ZHO-ZHO-ZHO/ComfyUI-PixArt-alpha-Diffusers": [ + [ + "PA_BaseModelLoader_fromhub_Zho", + "PA_Generation_Zho", + "PA_Styler_Zho" + ], + { + "title_aux": "ComfyUI-PixArt-alpha-Diffusers" + } + ], + "https://github.com/ZHO-ZHO-ZHO/ComfyUI-Q-Align": [ + [ + "QAlign_Zho" + ], + { + "title_aux": "ComfyUI-Q-Align" + } + ], + "https://github.com/ZHO-ZHO-ZHO/ComfyUI-Qwen-VL-API": [ + [ + "QWenVL_API_S_Multi_Zho", + "QWenVL_API_S_Zho" + ], + { + "title_aux": "ComfyUI-Qwen-VL-API" + } + ], + "https://github.com/ZHO-ZHO-ZHO/ComfyUI-SVD-ZHO": [ + [ + "SVD_Aspect_Ratio_Zho", + "SVD_Steps_MotionStrength_Seed_Zho", + "SVD_Styler_Zho" + ], + { + "title_aux": "ComfyUI-SVD-ZHO (WIP)" + } + ], + "https://github.com/ZHO-ZHO-ZHO/ComfyUI-SegMoE": [ + [ + "SMoE_Generation_Zho", + "SMoE_ModelLoader_Zho" + ], + { + "title_aux": "ComfyUI SegMoE" + } + ], + "https://github.com/ZHO-ZHO-ZHO/ComfyUI-Text_Image-Composite": [ + [ + "AlphaChanelAddByMask", + "ImageCompositeBy_BG_Zho", + "ImageCompositeBy_Zho", + "ImageComposite_BG_Zho", + "ImageComposite_Zho", + "RGB_Image_Zho", + "Text_Image_Frame_Zho", + "Text_Image_Multiline_Zho", + "Text_Image_Zho" + ], + { + "title_aux": "ComfyUI-Text_Image-Composite [WIP]" + } + ], + "https://github.com/ZHO-ZHO-ZHO/ComfyUI-UltraEdit-ZHO": [ + [ + "UltraEdit_Generation_Zho", + "UltraEdit_ModelLoader_Zho", + "UltraEdit_ModelLoader_local_Zho" + ], + { + "title_aux": "ComfyUI-UltraEdit-ZHO" + } + ], + "https://github.com/ZHO-ZHO-ZHO/ComfyUI-YoloWorld-EfficientSAM": [ + [ + "ESAM_ModelLoader_Zho", + "Yoloworld_ESAM_DetectorProvider_Zho", + "Yoloworld_ESAM_Zho", + "Yoloworld_ModelLoader_Zho" + ], + { + "title_aux": "ComfyUI YoloWorld-EfficientSAM" + } + ], + "https://github.com/ZHO-ZHO-ZHO/comfyui-portrait-master-zh-cn": [ + [ + "PortraitMaster_\u4e2d\u6587\u7248" + ], + { + "title_aux": "comfyui-portrait-master-zh-cn" + } + ], + "https://github.com/ZXL-Xinram/ComfyUI-AutoFlow": [ + [ + "AutoFlowPathJoiner", + "AutoFlowPathParser", + "AutoFlowPathValidator", + "AutoFlowStringCase", + "AutoFlowStringConcat", + "AutoFlowStringFormat", + "AutoFlowStringMultiConcat", + "AutoFlowStringReplace", + "AutoFlowStringSplit", + "AutoFlowTimestampFormatter", + "AutoFlowTimestampGenerator" + ], + { + "title_aux": "ComfyUI-AutoFlow" + } + ], + "https://github.com/ZZXYWQ/ComfyUI-ZZXYWQ": [ + [ + "StreamRecorder", + "VideoFormatConverter", + "ZZX_PaintsUndo" + ], + { + "title_aux": "ZZX Nodes" + } + ], + "https://github.com/Zachary116699/ComfyUI-LoadImageWithMetaDataEx": [ + [ + "ZLoadImageWithMetaDataFP" + ], + { + "title_aux": "ComfyUI_LoadImageWithMetaDataEx" + } + ], + "https://github.com/ZaneA/ComfyUI-ImageReward": [ + [ + "ImageRewardLoader", + "ImageRewardScore" + ], + { + "title_aux": "ImageReward" + } + ], + "https://github.com/Zar4X/ComfyUI-Batch-Process": [ + [ + "ImageBatchLoader", + "ImageBatchSaver", + "LoraBatchLoader", + "SimpleImageTagger", + "TXTBatchLoader", + "TextModifyTool" + ], + { + "title_aux": "ComfyUI-Batch-Process" + } + ], + "https://github.com/Zar4X/ComfyUI-Image-Resizing": [ + [ + "CalculateAspectRatioExtension", + "CalculateUpscaleFactor", + "CalculateUpscaleRounds", + "ExtendCanvasByPercentage", + "ExtendCanvasByPercentage (ZX)", + "ImageAspectRatioExtractor", + "ImageCropByPercentage", + "ImageResolutionExtractor", + "MaskCropByPercentage", + "ResizeToMultiple" + ], + { + "title_aux": "ComfyUI-Image-Resizing" + } + ], + "https://github.com/Zch6111/AI_Text_Comfyui": [ + [ + "AutoPromptGeneratorNode", + "GeminiImageToPrompt", + "SmartAutoPromptNode" + ], + { + "title_aux": "AI_Text_Comfyui" + } + ], + "https://github.com/ZeDarkAdam/ComfyUI-Embeddings-Tools": [ + [ + "EmbeddingsNameLoader", + "EmbendingList" + ], + { + "title_aux": "ComfyUI-Embeddings-Tools" + } + ], + "https://github.com/Zehong-Ma/ComfyUI-MagCache": [ + [ + "CompileModel", + "MagCache", + "MagCacheCalibration" + ], + { + "title_aux": "ComfyUI-MagCache" + } + ], + "https://github.com/Zeks/comfyui-rapidfire": [ + [ + "BracketEscaper", + "CachedCheckpoint", + "CsvWriterNode", + "ImmatureImageCounter", + "ImmatureImageDataLoader", + "MultiModelAdvancedKsampler", + "MultiModelCheckpointIterator", + "MultiModelPromptSaver", + "MultiModelPromptSaverIterative", + "Ranbooru", + "RandomCharacterSelector", + "RapidSchedulerCombo", + "RapidSchedulerSelector" + ], + { + "title_aux": "comfyui-rapidfire" + } + ], + "https://github.com/a-und-b/ComfyUI_Delay": [ + [ + "Add Delay" + ], + { + "title_aux": "ComfyUI_Delay" + } + ], + "https://github.com/a-und-b/ComfyUI_IC-Light-v2_fal": [ + [ + "ICLightV2" + ], + { + "title_aux": "IC-Light V2 (fal.ai)" + } + ], + "https://github.com/a-und-b/ComfyUI_JSON_Helper": [ + [ + "JSONStringToObjectNode" + ], + { + "title_aux": "ComfyUI_JSON_Helper" + } + ], + "https://github.com/a-und-b/ComfyUI_LoRA_from_URL": [ + [ + "Load LoRA From URL" + ], + { + "title_aux": "ComfyUI_LoRA_from_URL" + } + ], + "https://github.com/a-und-b/ComfyUI_MaskAreaCondition": [ + [ + "MaskAreaCondition", + "SelectData" + ], + { + "title_aux": "ComfyUI Mask Area Condition" + } + ], + "https://github.com/a1lazydog/ComfyUI-AudioScheduler": [ + [ + "AmplitudeToGraph", + "AmplitudeToNumber", + "AudioToAmplitudeGraph", + "AudioToAudioData", + "AudioToFFTs", + "BatchAmplitudeSchedule", + "ClipAmplitude", + "FloatArrayToGraph", + "GateNormalizedAmplitude", + "NormalizeAmplitude", + "NormalizedAmplitudeDrivenString", + "NormalizedAmplitudeToGraph", + "NormalizedAmplitudeToNumber", + "TransientAmplitudeBasic" + ], + { + "title_aux": "ComfyUI-AudioScheduler" + } + ], + "https://github.com/abdozmantar/ComfyUI-DeepExtract": [ + [ + "VocalAndSoundRemoverNode" + ], + { + "title_aux": "DeepExtract" + } + ], + "https://github.com/aburahamu/ComfyUI-IsNiceParts": [ + [ + "NiceHand" + ], + { + "title_aux": "ComfyUI-IsNiceParts" + } + ], + "https://github.com/aburahamu/ComfyUI-RequestsPoster": [ + [ + "GetImageFromSD3byI2I", + "GetImageFromSD3byT2I", + "PostImage2Discord", + "PostImage2X", + "PostText" + ], + { + "title_aux": "ComfyUI-RequestPoster" + } + ], + "https://github.com/abyz22/image_control": [ + [ + "abyz22_AddPrompt", + "abyz22_Convertpipe", + "abyz22_Editpipe", + "abyz22_FirstNonNull", + "abyz22_FromBasicPipe_v2", + "abyz22_Frompipe", + "abyz22_ImpactWildcardEncode", + "abyz22_ImpactWildcardEncode_GetPrompt", + "abyz22_Ksampler", + "abyz22_Padding Image", + "abyz22_RandomMask", + "abyz22_RemoveControlnet", + "abyz22_ResizeOpenpose", + "abyz22_SaveImage", + "abyz22_SetQueue", + "abyz22_ToBasicPipe", + "abyz22_Topipe", + "abyz22_blend_onecolor", + "abyz22_blendimages", + "abyz22_bypass", + "abyz22_censoring", + "abyz22_drawmask", + "abyz22_lamaInpaint", + "abyz22_lamaPreprocessor", + "abyz22_makecircles", + "abyz22_path_generator", + "abyz22_setimageinfo", + "abyz22_smallhead" + ], + { + "title_aux": "image_control" + } + ], + "https://github.com/acorderob/sd-webui-prompt-postprocessor": [ + [ + "ACBPPPSelectVariable", + "ACBPromptPostProcessor" + ], + { + "author": "ACB", + "description": "Node for processing prompts. Includes the following options: send to negative prompt, set variables, if/elif/else command for conditional content, wildcards and choices.", + "nickname": "ACB PPP", + "title": "Prompt Post Processor", + "title_aux": "Prompt PostProcessor" + } + ], + "https://github.com/adbrasi/ComfyUI-TrashNodes-DownloadHuggingface": [ + [ + "DownloadLinkChecker", + "ShowFileNames" + ], + { + "title_aux": "ComfyUI-TrashNodes-DownloadHuggingface" + } + ], + "https://github.com/adieyal/comfyui-dynamicprompts": [ + [ + "DPCombinatorialGenerator", + "DPFeelingLucky", + "DPJinja", + "DPMagicPrompt", + "DPOutput", + "DPRandomGenerator" + ], + { + "title_aux": "DynamicPrompts Custom Nodes" + } + ], + "https://github.com/adigayung/ComfyUI-Translator": [ + [ + "CLIP Text Encode (Auto Translate)", + "Prompt Text (Auto Translate)" + ], + { + "title_aux": "ComfyUI-Translator" + } + ], + "https://github.com/adrianschubek/comfyui-zeug": [ + [ + "ZeugBool", + "ZeugCleanGpuPass", + "ZeugFloat", + "ZeugFloatToStr", + "ZeugInt", + "ZeugIntToStr", + "ZeugIntToWxH", + "ZeugJoinStr", + "ZeugJoinStrList", + "ZeugPrintPass", + "ZeugSplitStrList", + "ZeugStr", + "ZeugStrToFloat", + "ZeugStrToInt", + "ZeugWxHToInt" + ], + { + "title_aux": "comfyui-zeug" + } + ], + "https://github.com/adriflex/ComfyUI_Blender_Texdiff": [ + [ + "ViewportColor", + "ViewportDepth" + ], + { + "title_aux": "ComfyUI_Blender_Texdiff" + } + ], + "https://github.com/aegis72/aegisflow_utility_nodes": [ + [ + "Add Text To Image", + "Aegisflow CLIP Pass", + "Aegisflow Conditioning Pass", + "Aegisflow Image Pass", + "Aegisflow Latent Pass", + "Aegisflow Mask Pass", + "Aegisflow Model Pass", + "Aegisflow Pos/Neg Pass", + "Aegisflow SDXL Tuple Pass", + "Aegisflow VAE Pass", + "Aegisflow controlnet preprocessor bus", + "Apply Instagram Filter", + "Binary INT Switch", + "Brightness_Contrast_Ally", + "Flatten Colors", + "Gaussian Blur_Ally", + "GlitchThis Effect", + "Hue Rotation", + "Image Flip_ally", + "Placeholder Tuple", + "Swap Color Mode", + "aegisflow Multi_Pass", + "aegisflow Multi_Pass XL", + "af_pipe_in_15", + "af_pipe_in_xl", + "af_pipe_out_15", + "af_pipe_out_xl" + ], + { + "title_aux": "AegisFlow Utility Nodes" + } + ], + "https://github.com/aegis72/comfyui-styles-all": [ + [ + "menus" + ], + { + "title_aux": "ComfyUI-styles-all" + } + ], + "https://github.com/agilly1989/ComfyUI_agilly1989_motorway": [ + [ + "MotorwayFloat", + "MotorwayInt", + "MotorwaySeed", + "MotorwayStr", + "MotorwayStrMulti" + ], + { + "title_aux": "ComfyUI_agilly1989_motorway" + } + ], + "https://github.com/ahernandezmiro/ComfyUI-GCP_Storage_tools": [ + [ + "GCPReadImageNode", + "GCPWriteImageNode" + ], + { + "title_aux": "ComfyUI-GCP_Storage_tools" + } + ], + "https://github.com/ai-liam/comfyui-liam": [ + [ + "AiStoreAzureGPTLiam", + "GetBetterDepthImage", + "LiamLibDisplayText", + "LiamLibFillImage", + "LiamLibImageToGray", + "LiamLibLoadImage", + "LiamLibMergeText", + "LiamLibSaveImg", + "LiamLibSaveText", + "OllamaApiTNodeLiam", + "PreviewReliefImage", + "SpeechRecognitionLiam", + "SpeechSynthesisLiam" + ], + { + "title_aux": "LiamUtil" + } + ], + "https://github.com/ai-liam/comfyui_liam_util": [ + [ + "LiamLoadImage" + ], + { + "title_aux": "LiamUtil (single node)" + } + ], + "https://github.com/ai-shizuka/ComfyUI-tbox": [ + [ + "AnimalPosePreprocessor", + "BatchManager", + "CannyPreprocessor", + "ConstrainImageNode", + "DWPosePreprocessor", + "DWPreprocessor", + "DensePosePreprocessor", + "GFPGANNode", + "ImageLoader", + "ImageResize", + "ImageSaver", + "ImageSize", + "ImagesSaver", + "LineArtPreprocessor", + "LineartStandardPreprocessor", + "MaskAddNode", + "MiDaSDepthPreprocessor", + "PurgeVRAMNode", + "VideoInfo", + "VideoLoader", + "VideoSaver", + "WatermarkNode" + ], + { + "author": "tstandley", + "title_aux": "ComfyUI-tbox" + } + ], + "https://github.com/aiaiaikkk/ComfyUI-Curve": [ + [ + "CameraRawEnhanceNode", + "CameraRawToneCurveNode", + "ColorGradingNode", + "CurvePresetNode", + "GaussianBlurNode", + "HistogramAnalysisNode", + "PhotoshopCurveNode", + "PhotoshopHSLNode", + "PhotoshopLevelsNode" + ], + { + "title_aux": "ComfyUI-Curve" + } + ], + "https://github.com/aiaiaikkk/kontext-super-prompt": [ + [ + "APIFluxKontextEnhancer", + "AnnotationDataNode", + "OllamaFluxKontextEnhancerV2", + "TextGenWebUIFluxKontextEnhancer", + "VisualPromptEditor" + ], + { + "title_aux": "kontext-super-prompt" + } + ], + "https://github.com/aianimation55/ComfyUI-FatLabels": [ + [ + "FatLabels" + ], + { + "title_aux": "Comfy UI FatLabels" + } + ], + "https://github.com/aiartvn/A2V_Multi_Image_Composite": [ + [ + "A2V_Multi_Image_Composite" + ], + { + "title_aux": "A2V Multi Image Composite" + } + ], + "https://github.com/aicuai/aicu-comfyui-stability-ai-api": [ + [ + "Preview3DModel", + "Save3DModel", + "StabilityControlSketch", + "StabilityControlStructure", + "StabilityControlStyle", + "StabilityEdit", + "StabilityImageCore", + "StabilityImageSD3", + "StabilityImageToVideo", + "StabilityImageUltra", + "StabilityUpscaleConservative", + "StabilityUpscaleCreative", + "StabilityUpscaleFast", + "StableFast3D", + "StablePointAware3D" + ], + { + "title_aux": "aicu-comfyui-stability-ai-api" + } + ], + "https://github.com/aidec/Comfyui_TextBatch_aidec": [ + [ + "ImageFilenameProcessor", + "ImageInfoExtractor", + "ImageQueueProcessor", + "LoadImagesFromDirBatch", + "PathParser", + "TextBatch", + "TextQueueProcessor", + "TextSplitCounter" + ], + { + "title_aux": "Comfyui_TextBatch_aidec" + } + ], + "https://github.com/aidenli/ComfyUI_NYJY": [ + [ + "BailianChat", + "BailianChatOption", + "BailianVL", + "BailianVLOption", + "CivitaiPrompt", + "CommonLLMChat", + "ConvertAnyToString", + "ConvertStringToNumber", + "CustomLatentImage-NYJY", + "CustomLatentImageSimple", + "FloatSlider-NYJY", + "FluxProOnline", + "GetItemFromList", + "JoyCaption", + "JoyCaptionAlpha1Online", + "JoyCaptionAlpha2Online", + "JoyTag", + "JsonDumps", + "JsonGetValueByKeys", + "JsonLoads", + "ReadFileToString", + "SplitString", + "Translate" + ], + { + "title_aux": "ComfyUI_NYJY" + } + ], + "https://github.com/aigc-apps/EasyAnimate": [ + [ + "CameraBasicFromChaoJie", + "CameraCombineFromChaoJie", + "CameraJoinFromChaoJie", + "CameraTrajectoryFromChaoJie", + "CreateTrajectoryBasedOnKJNodes", + "EasyAnimateI2VSampler", + "EasyAnimateT2VSampler", + "EasyAnimateV2VSampler", + "EasyAnimateV5_I2VSampler", + "EasyAnimateV5_T2VSampler", + "EasyAnimateV5_V2VSampler", + "EasyAnimate_TextBox", + "ImageMaximumNode", + "LoadEasyAnimateLora", + "LoadEasyAnimateModel", + "TextBox" + ], + { + "title_aux": "Video Generation Nodes for EasyAnimate" + } + ], + "https://github.com/aigc-apps/VideoX-Fun": [ + [ + "CameraBasicFromChaoJie", + "CameraCombineFromChaoJie", + "CameraJoinFromChaoJie", + "CameraTrajectoryFromChaoJie", + "CogVideoXFunInpaintSampler", + "CogVideoXFunT2VSampler", + "CogVideoXFunV2VSampler", + "CreateTrajectoryBasedOnKJNodes", + "FunCompile", + "FunRiflex", + "FunTextBox", + "ImageMaximumNode", + "LoadCogVideoXFunLora", + "LoadCogVideoXFunModel", + "LoadWanFunLora", + "LoadWanFunModel", + "LoadWanLora", + "LoadWanModel", + "VideoToCanny", + "VideoToDepth", + "VideoToOpenpose", + "WanFunInpaintSampler", + "WanFunT2VSampler", + "WanFunV2VSampler", + "WanI2VSampler", + "WanT2VSampler" + ], + { + "title_aux": "VideoX-Fun" + } + ], + "https://github.com/aimerib/ComfyUI_HigherBitDepthSaveImage": [ + [ + "SaveImageHigherBitDepth" + ], + { + "title_aux": "ComfyUI-HigherBitDepthSaveImage" + } + ], + "https://github.com/ainewsto/Comfyui-chatgpt-api": [ + [ + "ComfyuiChatGPTApi", + "Comfyui_gpt_image_1", + "Comfyui_gpt_image_1_edit" + ], + { + "title_aux": "Comfyui-chatgpt-api" + } + ], + "https://github.com/ainewsto/Comfyui-google-veo2-api": [ + [ + "ComfyuiGoogleVeo2" + ], + { + "title_aux": "Comfyui-google-veo2-api" + } + ], + "https://github.com/ainewsto/Comfyui_Comfly_v2": [ + [ + "ComflyChatGPTApi", + "ComflyGeminiAPI", + "ComflyJimengApi", + "ComflyJimengVideoApi", + "ComflySeededit", + "Comfly_Flux_Kontext", + "Comfly_Flux_Kontext_Edit", + "Comfly_Flux_Kontext_bfl", + "Comfly_Googel_Veo3", + "Comfly_Mj", + "Comfly_Mj_swap_face", + "Comfly_Mju", + "Comfly_Mjv", + "Comfly_gpt_image_1", + "Comfly_gpt_image_1_edit", + "Comfly_kling_image2video", + "Comfly_kling_multi_image2video", + "Comfly_kling_text2video", + "Comfly_lip_sync", + "Comfly_mj_video", + "Comfly_mj_video_extend", + "Comfly_mjstyle", + "Comfly_upload", + "Comfly_video_extend" + ], + { + "title_aux": "Comfyui_Comfly_v2" + } + ], + "https://github.com/ainewsto/comfyui-labs-google": [ + [ + "ComfyUI-ImageFx", + "ComfyUI-Whisk", + "ComfyUI-Whisk-Prompts" + ], + { + "title_aux": "comfyui-labs-google" + } + ], + "https://github.com/aisabervisionlab/ComfyUI_merge_ASVL": [ + [ + "ASVL" + ], + { + "title_aux": "ComfyUI_merge_ASVL" + } + ], + "https://github.com/ajbergh/comfyui-ethnicity_hairstyle_clip_encoder": [ + [ + "CLIPTextEncodeWithExtras" + ], + { + "title_aux": "comfyui-ethnicity_hairstyle_clip_encoder" + } + ], + "https://github.com/akatz-ai/ComfyUI-AKatz-Nodes": [ + [ + "AK_AdjustDepthmapBrightness", + "AK_AdjustListSize", + "AK_AnimatedDilationMaskLinear", + "AK_AudioFramesyncSchedule", + "AK_AudioreactiveDilateMaskInfinite", + "AK_AudioreactiveDilationMask", + "AK_AudioreactiveDynamicDilationMask", + "AK_BinaryAmplitudeGate", + "AK_BlobTrack", + "AK_BrightnessToFloatList", + "AK_ConvertListToFloatList", + "AK_DilateMaskLinearInfinite", + "AK_FadeBetweenBatches", + "AK_FlexFeatureToFloatList", + "AK_FloatListToDilateMaskSchedule", + "AK_FloatListToFlexFeature", + "AK_IPAdapterCustomWeights", + "AK_KeyframeScheduler", + "AK_LagChop", + "AK_ListToNumpyFloatArray", + "AK_MakeDepthmapSeamless", + "AK_NormalizeMaskImage", + "AK_RescaleFloatList", + "AK_ScaleMask", + "AK_ScheduledBinaryComparison", + "AK_ShrinkNumSequence", + "AK_SplitImageBatch", + "AK_VideoSpeedAdjust", + "Scale Mask Node" + ], + { + "author": "akatz", + "description": "Custom node pack for nodes I use in my workflows.", + "nickname": "Akatz Custom Nodes", + "title": "Akatz Custom Nodes", + "title_aux": "Akatz Custom Nodes" + } + ], + "https://github.com/akatz-ai/ComfyUI-Basic-Math": [ + [ + "BasicMath", + "BooleanInput", + "BooleanLogic", + "BooleanUnary", + "FloatComparison", + "FloatInput", + "IntMath", + "IntegerComparison", + "IntegerInput", + "MathConstants", + "NumberClamp", + "NumberComparison", + "NumberInRange", + "NumberLerp", + "NumberRound", + "PreciseFloatInput", + "StringComparison", + "StringInput", + "ToBool", + "ToFloat", + "ToInt", + "ToString", + "UnaryMath" + ], + { + "title_aux": "ComfyUI-Basic-Math" + } + ], + "https://github.com/akatz-ai/ComfyUI-DepthCrafter-Nodes": [ + [ + "DepthCrafter", + "DownloadAndLoadDepthCrafterModel" + ], + { + "author": "akatz", + "description": "Custom nodes for use with DepthCrafter. Create consistent depth maps for your videos.", + "nickname": "DepthCrafter Nodes", + "title": "DepthCrafter Nodes", + "title_aux": "DepthCrafter Nodes" + } + ], + "https://github.com/akatz-ai/ComfyUI-Depthflow-Nodes": [ + [ + "Depthflow", + "DepthflowEffectColor", + "DepthflowEffectDOF", + "DepthflowEffectInpaint", + "DepthflowEffectVignette", + "DepthflowMotionArc", + "DepthflowMotionCosine", + "DepthflowMotionLinear", + "DepthflowMotionPresetCircle", + "DepthflowMotionPresetDolly", + "DepthflowMotionPresetHorizontal", + "DepthflowMotionPresetOrbital", + "DepthflowMotionPresetVertical", + "DepthflowMotionPresetZoom", + "DepthflowMotionSetTarget", + "DepthflowMotionSine", + "DepthflowMotionTriangle" + ], + { + "author": "akatz", + "description": "Custom nodes for use with Tremeschin's Depthflow library.", + "nickname": "Depthflow Nodes", + "title": "Depthflow Nodes", + "title_aux": "\ud83c\udf0a Depthflow Nodes" + } + ], + "https://github.com/akatz-ai/ComfyUI-X-Portrait-Nodes": [ + [ + "DownloadXPortraitModel", + "XPortrait" + ], + { + "author": "akatz", + "description": "Custom nodes for use with X-Portrait. Animate portraits with an input video and a reference image.", + "nickname": "X-Portrait Nodes", + "title": "X-Portrait Nodes", + "title_aux": "ComfyUI-X-Portrait-Nodes" + } + ], + "https://github.com/akierson/ComfyUI-textnodes": [ + [ + "Prompt Truncate", + "Tidy Tags" + ], + { + "title_aux": "ComfyUI-textnodes" + } + ], + "https://github.com/akierson/comfyui-colornodes": [ + [ + "Color Picker", + "Color to Hex", + "Color to RGB", + "Image Replace Color", + "Invert Color" + ], + { + "title_aux": "comfyui-colornodes" + } + ], + "https://github.com/al-swaiti/All-IN-ONE-style": [ + [ + "ComfyUIStyler", + "menus" + ], + { + "title_aux": "All-IN-ONE-style" + } + ], + "https://github.com/al-swaiti/ComfyUI-CascadeResolutions": [ + [ + "CascadeResolutions" + ], + { + "title_aux": "ComfyUI-CascadeResolutions" + } + ], + "https://github.com/al-swaiti/ComfyUI-OllamaGemini": [ + [ + "ClaudeAPI", + "GeminiAPI", + "GeminiBRIA_RMBG", + "GeminiCLIPSeg", + "GeminiCombineSegMasks", + "GeminiComfyUIStyler", + "GeminiConvertRasterToVector", + "GeminiFLUXResolutions", + "GeminiImageGenerator", + "GeminiSVGPreview", + "GeminiSaveSVG", + "GeminiSaveText", + "GeminiSmartPromptGenerator", + "GeminiTextSplitter", + "ListAvailableModels", + "OllamaAPI", + "OpenAIAPI", + "QwenAPI", + "style_menus" + ], + { + "title_aux": "GeminiOllama ComfyUI Extension" + } + ], + "https://github.com/alanhuang67/ComfyUI-FAI-Node": [ + [ + "FAIDynamicMask", + "FAIScaleScheduler", + "FAI_Voronoi_Generator" + ], + { + "title_aux": "FAI-Node" + } + ], + "https://github.com/alastor-666-1933/caching_to_not_waste": [ + [ + "caching_condition", + "caching_controlnet", + "caching_from_combined_images", + "caching_image", + "caching_mask", + "caching_text", + "caching_wildcard_list" + ], + { + "title_aux": "Caching to not Waste" + } + ], + "https://github.com/alchemine/comfyui-alchemine-pack": [ + [ + "DanbooruPopularPostsTagsRetriever", + "DanbooruPostTagsRetriever", + "DanbooruRelatedTagsRetriever", + "FilterSubtags", + "FilterTags", + "FixBreakAfterTIPO", + "GeminiInference", + "OllamaInference", + "ProcessTags", + "ReplaceUnderscores", + "TextEditingInference", + "TokenAnalyzer", + "WidthHeight" + ], + { + "title_aux": "ComfyUI-Alchemine-Pack" + } + ], + "https://github.com/aleolidev/comfy_kaizen_package": [ + [ + "KaizenImageComposite" + ], + { + "title_aux": "Kaizen Package" + } + ], + "https://github.com/alessandroperilli/APW_Nodes": [ + [ + "APW_CloudImageSize", + "APW_ImageListFilter", + "APW_ImageSaver", + "APW_LocalImageSize", + "APW_LocalVideoSize" + ], + { + "title_aux": "apw_nodes" + } + ], + "https://github.com/alessandroperilli/OCS_Nodes": [ + [ + "OCS_CloudImageSize", + "OCS_ImageListFilter", + "OCS_ImageSaver", + "OCS_LocalImageSize", + "OCS_LocalVideoSize" + ], + { + "title_aux": "Open Creative Studio Nodes" + } + ], + "https://github.com/alessandrozonta/ComfyUI-CenterNode": [ + [ + "BBoxCrop" + ], + { + "title_aux": "ComfyUI-CenterNode" + } + ], + "https://github.com/alessandrozonta/ComfyUI-Layers": [ + [ + "LayersSaver - Save Layer", + "LayersSaver - Save Layer From Images" + ], + { + "title_aux": "Save Layers Node for ComfyUI" + } + ], + "https://github.com/alessandrozonta/ComfyUI-OpenPose": [ + [ + "OpenPose - Get poses" + ], + { + "author": "joe", + "title_aux": "OpenPose Node" + } + ], + "https://github.com/alessandrozonta/ComfyUI-PoseDirection": [ + [ + "OpenPose - Get direction" + ], + { + "title_aux": "ComfyUI-PoseDirection" + } + ], + "https://github.com/alessandrozonta/Comfyui-LoopLoader": [ + [ + "LoadLoopImagesFromDir" + ], + { + "title_aux": "Comfyui-LoopLoader" + } + ], + "https://github.com/alexcong/ComfyUI_QwenVL": [ + [ + "Qwen2.5", + "Qwen2.5VL" + ], + { + "title_aux": "Qwen2-VL wrapper for ComfyUI" + } + ], + "https://github.com/alexgenovese/ComfyUI-UNO-Flux": [ + [ + "UNOGenerate", + "UNOModelLoader" + ], + { + "title_aux": "ComfyUI UNO Nodes" + } + ], + "https://github.com/alexgenovese/ComfyUI_HF_Servelress_Inference": [ + [ + "HF_QuestionAnswer", + "Job_Caption", + "Joy_caption", + "Joy_caption_load" + ], + { + "author": "Alex Genovese", + "description": "Huggingface Api Serverless request", + "nickname": "alexgenovese", + "title": "Huggingface Api Serverless", + "title_aux": "Huggingface Api Serverless" + } + ], + "https://github.com/alexisrolland/ComfyUI-Blender": [ + [ + "BlenderInputBoolean", + "BlenderInputCombo", + "BlenderInputFloat", + "BlenderInputInt", + "BlenderInputLoad3D", + "BlenderInputLoadImage", + "BlenderInputSeed", + "BlenderInputString", + "BlenderInputStringMultiline", + "BlenderOutputDownload3D", + "BlenderOutputSaveImage" + ], + { + "title_aux": "ComfyUI-Blender" + } + ], + "https://github.com/alexisrolland/ComfyUI-Phi": [ + [ + "LoadPhi", + "LoadPhiMultimodal", + "LoadPhiVision", + "RunPhi", + "RunPhiMultimodal", + "RunPhiVision" + ], + { + "title_aux": "ComfyUI-Phi" + } + ], + "https://github.com/alisson-anjos/ComfyUI-Ollama-Describer": [ + [ + "InputText", + "JsonPropertyExtractorNode", + "OllamaCaptionerExtraOptions", + "OllamaImageCaptioner", + "OllamaImageDescriber", + "OllamaTextDescriber", + "ShowText", + "TextTransformer" + ], + { + "title_aux": "ComfyUI-Ollama-Describer" + } + ], + "https://github.com/alpertunga-bile/image-caption-comfyui": [ + [ + "Image Caption Node", + "Insert Prompt Node" + ], + { + "title_aux": "image-caption-comfyui" + } + ], + "https://github.com/alpertunga-bile/prompt-generator-comfyui": [ + [ + "Prompt Generator" + ], + { + "title_aux": "prompt-generator" + } + ], + "https://github.com/alsritter/asymmetric-tiling-comfyui": [ + [ + "Asymmetric_Tiling_KSampler" + ], + { + "title_aux": "asymmetric-tiling-comfyui" + } + ], + "https://github.com/alt-key-project/comfyui-dream-project": [ + [ + "Analyze Palette [Dream]", + "Beat Curve [Dream]", + "Big Float Switch [Dream]", + "Big Image Switch [Dream]", + "Big Int Switch [Dream]", + "Big Latent Switch [Dream]", + "Big Palette Switch [Dream]", + "Big Text Switch [Dream]", + "Boolean To Float [Dream]", + "Boolean To Int [Dream]", + "Build Prompt [Dream]", + "CSV Curve [Dream]", + "CSV Generator [Dream]", + "Calculation [Dream]", + "Common Frame Dimensions [Dream]", + "Compare Palettes [Dream]", + "FFMPEG Video Encoder [Dream]", + "File Count [Dream]", + "Finalize Prompt [Dream]", + "Float Input [Dream]", + "Float to Log Entry [Dream]", + "Frame Count Calculator [Dream]", + "Frame Counter (Directory) [Dream]", + "Frame Counter (Simple) [Dream]", + "Frame Counter Info [Dream]", + "Frame Counter Offset [Dream]", + "Frame Counter Time Offset [Dream]", + "Image Brightness Adjustment [Dream]", + "Image Color Shift [Dream]", + "Image Contrast Adjustment [Dream]", + "Image Motion [Dream]", + "Image Sequence Blend [Dream]", + "Image Sequence Loader [Dream]", + "Image Sequence Saver [Dream]", + "Image Sequence Tweening [Dream]", + "Int Input [Dream]", + "Int to Log Entry [Dream]", + "Laboratory [Dream]", + "Linear Curve [Dream]", + "Log Entry Joiner [Dream]", + "Log File [Dream]", + "Noise from Area Palettes [Dream]", + "Noise from Palette [Dream]", + "Palette Color Align [Dream]", + "Palette Color Shift [Dream]", + "Random Prompt Words [Dream]", + "Sample Image Area as Palette [Dream]", + "Sample Image as Palette [Dream]", + "Saw Curve [Dream]", + "Sine Curve [Dream]", + "Smooth Event Curve [Dream]", + "String Input [Dream]", + "String Tokenizer [Dream]", + "String to Log Entry [Dream]", + "Text Input [Dream]", + "Triangle Curve [Dream]", + "Triangle Event Curve [Dream]", + "WAV Curve [Dream]" + ], + { + "title_aux": "Dream Project Animation Nodes" + } + ], + "https://github.com/alt-key-project/comfyui-dream-video-batches": [ + [ + "Blended Transition [DVB]", + "Calculation [DVB]", + "Create Frame Set [DVB]", + "Divide [DVB]", + "Fade From Black [DVB]", + "Fade To Black [DVB]", + "Float Input [DVB]", + "For Each Done [DVB]", + "For Each Filename [DVB]", + "Frame Set Append [DVB]", + "Frame Set Frame Dimensions Scaled [DVB]", + "Frame Set Index Offset [DVB]", + "Frame Set Merger [DVB]", + "Frame Set Reindex [DVB]", + "Frame Set Repeat [DVB]", + "Frame Set Reverse [DVB]", + "Frame Set Split Beginning [DVB]", + "Frame Set Split End [DVB]", + "Frame Set Splitter [DVB]", + "Generate Inbetween Frames [DVB]", + "Int Input [DVB]", + "Linear Camera Pan [DVB]", + "Linear Camera Roll [DVB]", + "Linear Camera Zoom [DVB]", + "Load Image From Path [DVB]", + "Multiply [DVB]", + "Sine Camera Pan [DVB]", + "Sine Camera Roll [DVB]", + "Sine Camera Zoom [DVB]", + "String Input [DVB]", + "Text Input [DVB]", + "Trace Memory Allocation [DVB]", + "Unwrap Frame Set [DVB]" + ], + { + "title_aux": "Dream Video Batches" + } + ], + "https://github.com/an90ray/ComfyUI_RErouter_CustomNodes": [ + [ + "CLIPTextEncode (RE)", + "CLIPTextEncodeSDXL (RE)", + "CLIPTextEncodeSDXLRefiner (RE)", + "Int (RE)", + "RErouter <=", + "RErouter =>", + "String (RE)" + ], + { + "title_aux": "ComfyUI_RErouter_CustomNodes" + } + ], + "https://github.com/andersxa/comfyui-PromptAttention": [ + [ + "CLIPAttentionMaskEncode" + ], + { + "title_aux": "CLIP Directional Prompt Attention" + } + ], + "https://github.com/andygill/comfyui-sunflower-nodes": [ + [ + "DepthViewToIsometric", + "DisparityToDepthView", + "EquirectangularToRectilinear", + "ImageChannelSelect", + "MaskChannelSelect", + "ResizeDown" + ], + { + "title_aux": "comfyui-sunflower-nodes" + } + ], + "https://github.com/angeloshredder/StableCascadeResizer": [ + [ + "CascadeResize" + ], + { + "title_aux": "StableCascadeResizer" + } + ], + "https://github.com/angree/ComfyUI-Q_GLB_Material_Modifier": [ + [ + "QManualGLBMaterialModifier", + "QPresetGLBMaterialModifier" + ], + { + "title_aux": "Q GLB Material Modifier" + } + ], + "https://github.com/angree/ComfyUI-Q_find-mask-size": [ + [ + "QImageCropCalculator" + ], + { + "title_aux": "Q Find Mask Size" + } + ], + "https://github.com/anhkhoatranle30/Handy-Nodes-ComfyUI": [ + [ + "Custom Save Image //Handy" + ], + { + "author": "Khoa Tran", + "description": "This extension offers various handy nodes.", + "nickname": "Handy-Nodes-ComfyUI", + "title": "Handy-Nodes-ComfyUI", + "title_aux": "Handy Node ComfyUI" + } + ], + "https://github.com/arcum42/ComfyUI_SageUtils": [ + [ + "SageSetWildcardText", + "Sage_AdvSamplerInfo", + "Sage_CLIPLoaderFromInfo", + "Sage_CLIPSelector", + "Sage_CacheMaintenance", + "Sage_CheckLorasForUpdates", + "Sage_CheckpointLoaderRecent", + "Sage_CheckpointLoaderSimple", + "Sage_CheckpointSelector", + "Sage_CleanText", + "Sage_CollectKeywordsFromLoraStack", + "Sage_ConditioningZeroOut", + "Sage_ConstructLLMPrompt", + "Sage_ConstructLLMPromptExtra", + "Sage_ConstructMetadata", + "Sage_ConstructMetadataLite", + "Sage_CropImage", + "Sage_CubiqImageResize", + "Sage_DualCLIPSelector", + "Sage_DualCLIPTextEncode", + "Sage_DualCLIPTextEncodeLumina2", + "Sage_EmptyLatentImagePassthrough", + "Sage_FreeMemory", + "Sage_GetFileHash", + "Sage_GuessResolutionByRatio", + "Sage_Halt", + "Sage_HiDreamE1_Instruction", + "Sage_JoinText", + "Sage_KSampler", + "Sage_KSamplerAudioDecoder", + "Sage_KSamplerDecoder", + "Sage_KSamplerTiledDecoder", + "Sage_LMStudioLLMPromptText", + "Sage_LMStudioLLMPromptVision", + "Sage_LMStudioLLMPromptVisionRefine", + "Sage_LastLoraInfo", + "Sage_LoadImage", + "Sage_LoadImageTextSetFromFolderNode", + "Sage_LoadModelFromInfo", + "Sage_Load_Dataset_From_Folder", + "Sage_LogicalSwitch", + "Sage_LoraStack", + "Sage_LoraStackInfoDisplay", + "Sage_LoraStackLoader", + "Sage_LoraStackRecent", + "Sage_ModelInfo", + "Sage_ModelInfoDisplay", + "Sage_ModelLoraStackLoader", + "Sage_ModelReport", + "Sage_ModelShifts", + "Sage_MultiModelPicker", + "Sage_OllamaLLMPromptText", + "Sage_OllamaLLMPromptVision", + "Sage_OllamaLLMPromptVisionRefine", + "Sage_PonyPrefix", + "Sage_PonyStyle", + "Sage_QuadCLIPSelector", + "Sage_QuickLoraStack", + "Sage_QuickNineLoraStack", + "Sage_QuickResPicker", + "Sage_QuickSixLoraStack", + "Sage_ReferenceImage", + "Sage_SamplerInfo", + "Sage_SaveImageWithMetadata", + "Sage_SaveText", + "Sage_SetText", + "Sage_SixLoraStack", + "Sage_TextRandomLine", + "Sage_TextSelectLine", + "Sage_TextSubstitution", + "Sage_TextSwitch", + "Sage_TextWeight", + "Sage_TilingInfo", + "Sage_TrainingCaptionsToConditioning", + "Sage_TripleCLIPSelector", + "Sage_TripleJoinText", + "Sage_TripleLoraStack", + "Sage_TripleQuickLoraStack", + "Sage_UNETLoader", + "Sage_UNETLoaderFromInfo", + "Sage_UNETSelector", + "Sage_UnetClipVaeToModelInfo", + "Sage_VAELoaderFromInfo", + "Sage_VAESelector", + "Sage_ViewAnything", + "Sage_ViewNotes" + ], + { + "title_aux": "Sage Utils" + } + ], + "https://github.com/asaddi/ComfyUI-YALLM-node": [ + [ + "LLMChat", + "LLMMinP", + "LLMModel", + "LLMPrependAppend", + "LLMProvider", + "LLMTemperature", + "LLMTextLatch", + "LLMTopK", + "LLMTopP" + ], + { + "title_aux": "ComfyUI-YALLM-node" + } + ], + "https://github.com/asaddi/YALLM-LlamaVision": [ + [ + "LLMSamplerSettings", + "LlamaVisionChat", + "LlamaVisionModel" + ], + { + "title_aux": "YALLM-LlamaVision" + } + ], + "https://github.com/asagi4/ComfyUI-Adaptive-Guidance": [ + [ + "AdaptiveGuidance", + "AdaptiveProjectedGuidance", + "PerpNegAdaptiveGuidanceGuider" + ], + { + "title_aux": "Adaptive Guidance for ComfyUI" + } + ], + "https://github.com/asagi4/ComfyUI-CADS": [ + [ + "CADS" + ], + { + "title_aux": "ComfyUI-CADS" + } + ], + "https://github.com/asagi4/ComfyUI-NPNet": [ + [ + "NPNetGoldenNoise" + ], + { + "title_aux": "ComfyUI NPNet (Golden Noise)" + } + ], + "https://github.com/asagi4/comfyui-prompt-control": [ + [ + "PCAddMaskToCLIP", + "PCAddMaskToCLIPMany", + "PCAttentionCoupleBatchNegative", + "PCExtractScheduledPrompt", + "PCLazyLoraLoader", + "PCLazyLoraLoaderAdvanced", + "PCLazyTextEncode", + "PCLazyTextEncodeAdvanced", + "PCLoraHooksFromText", + "PCMacroExpand", + "PCSaveExpandedWorkflow", + "PCSetLogLevel", + "PCSetPCTextEncodeSettings", + "PCTextEncode", + "PCTextEncodeWithRange" + ], + { + "author": "asagi4", + "description": "Control LoRA and prompt scheduling, advanced text encoding, regional prompting, and much more, through your text prompt. Generates dynamic graphs that are literally identical to handcrafted noodle soup.", + "nickname": "ComfyUI Prompt Control", + "title": "ComfyUI Prompt Control", + "title_aux": "ComfyUI Prompt Control" + } + ], + "https://github.com/asagi4/comfyui-utility-nodes": [ + [ + "MUConditioningCutoff", + "MUForceCacheClear", + "MUJinjaRender", + "MURemoveControlNet", + "MUReplaceModelWeights", + "MUSimpleWildcard" + ], + { + "title_aux": "asagi4/comfyui-utility-nodes" + } + ], + "https://github.com/asdrabael/Hunyuan-Multi-Lora-Loader": [ + [ + "HunyuanMultiLoraLoader", + "HunyuanMultiLoraLoaderWrapper" + ], + { + "title_aux": "Hunyuan-Multi-Lora-Loader" + } + ], + "https://github.com/asutermo/ComfyUI-Flux-TryOff": [ + [ + "TryOffFluxFillModelNode", + "TryOffFluxFillPipelineNode", + "TryOffModelNode", + "TryOffQuantizerNode", + "TryOffRunNode", + "TryOnOffModelNode", + "TryOnOffRunNode", + "TryOnRunNode" + ], + { + "title_aux": "ComfyUI-Flux-TryOff" + } + ], + "https://github.com/aszc-dev/ComfyUI-CoreMLSuite": [ + [ + "Core ML Converter", + "Core ML LCM Converter", + "Core ML LoRA Loader", + "CoreMLModelAdapter", + "CoreMLSampler", + "CoreMLSamplerAdvanced", + "CoreMLUNetLoader" + ], + { + "title_aux": "Core ML Suite for ComfyUI" + } + ], + "https://github.com/atluslin/comfyui_arcane_style_trans": [ + [ + "Arcane_style_trans" + ], + { + "title_aux": "comfyui_arcane_style_trans" + } + ], + "https://github.com/attashe/ComfyUI-FluxRegionAttention": [ + [ + "BBoxToMaskNode", + "BoundingBoxNode", + "CLIPDebug", + "FluxRegionBBOX", + "FluxRegionMask", + "RegionAttention", + "VisualizeBBoxesNode" + ], + { + "title_aux": "ComfyUI-FluxRegionAttention" + } + ], + "https://github.com/audioscavenger/ComfyUI-Thumbnails": [ + [ + "LoadImage" + ], + { + "author": "AudioscavengeR", + "description": "Load Image thumbnails and show input subfolders.", + "nickname": "LoadImageThumbnails", + "title": "LoadImageThumbnails", + "title_aux": "ComfyUI-Thumbnails" + } + ], + "https://github.com/audioscavenger/save-image-extended-comfyui": [ + [ + "SaveImageExtended" + ], + { + "author": "AudioscavengeR", + "description": "1 custom node to save your pictures in various folders and formats.", + "nickname": "Save Image Extended", + "title": "Save Image Extended", + "title_aux": "Save Image Extended for ComfyUI" + } + ], + "https://github.com/austinbrown34/ComfyUI-IO-Helpers": [ + [ + "EncodedPromptFromFile", + "EncodedPromptToFile", + "IO_LoadImage", + "SampledLatentsFromFile", + "SampledLatentsToFile" + ], + { + "title_aux": "ComfyUI-IO-Helpers" + } + ], + "https://github.com/avatechai/avatar-graph-comfyui": [ + [ + "ApplyMeshTransformAsShapeKey", + "B_ENUM", + "B_VECTOR3", + "B_VECTOR4", + "Combine Points", + "CreateShapeFlow", + "ExportBlendshapes", + "ExportGLTF", + "Extract Boundary Points", + "Image Alpha Mask Merge", + "ImageBridge", + "LoadImageFromRequest", + "LoadImageWithAlpha", + "LoadValueFromRequest", + "SAM MultiLayer", + "Save Image With Workflow" + ], + { + "author": "Avatech Limited", + "description": "Include nodes for sam + bpy operation, that allows workflow creations for generative 2d character rig.", + "nickname": "Avatar Graph", + "title": "Avatar Graph", + "title_aux": "Avatar Graph" + } + ], + "https://github.com/avenstack/ComfyUI-AV-FunASR": [ + [ + "AVASRTimestamp", + "AVFormat2Subtitle", + "AVSaveSubtitles", + "AVSpeechTimestamp" + ], + { + "title_aux": "ComfyUI-AV-FunASR" + } + ], + "https://github.com/avenstack/ComfyUI-AV-LatentSync": [ + [ + "AVLatentSync", + "AVVideoLengthAdjuster" + ], + { + "title_aux": "ComfyUI-AV-LatentSync" + } + ], + "https://github.com/avenstack/ComfyUI-AV-MegaTTS3": [ + [ + "AVMegaTTS3", + "AVPromptInit" + ], + { + "title_aux": "ComfyUI-AV-MegaTTS3" + } + ], + "https://github.com/avocadori/ComfyUI-load-image-prompt-lora": [ + [ + "YAMLImageCycler", + "YAMLImageCyclerSimple", + "YAMLLoRAExtractor", + "YAMLLoRALoader", + "YAMLLoRASelector" + ], + { + "title_aux": "ComfyUI-load-image-prompt-lora" + } + ], + "https://github.com/aws-samples/comfyui-llm-node-for-amazon-bedrock": [ + [ + "Amazon Bedrock - Luma AI Ray Video", + "Amazon Bedrock - Nova Canvas Background Prompt Replace", + "Amazon Bedrock - Nova Canvas Generate Image", + "Amazon Bedrock - Nova Canvas Generate Variations", + "Amazon Bedrock - Nova Reel Video", + "Amazon Bedrock - SD3 & SD3.5 Large | Image to Image", + "Amazon Bedrock - Stability AI Models | Text to Image", + "Bedrock - Claude", + "Bedrock - Claude Multimodal", + "Bedrock - Nova", + "Bedrock - SDXL", + "Bedrock - Titan Inpainting", + "Bedrock - Titan Outpainting", + "Bedrock - Titan Text to Image", + "Bedrock - Titan Variation", + "Image From S3", + "Image From URL", + "Image OCR By Textract", + "Image OCR By Textract V2", + "Image OCR By Textract V3", + "Image OCR by PaddleOCR", + "Image To S3", + "JSON Text Extraction", + "Prompt Regex Remove", + "Prompt Template", + "Prompt Template with Two Inputs" + ], + { + "title_aux": "Amazon Bedrock nodes for ComfyUI" + } + ], + "https://github.com/azure-dragon-ai/ComfyUI-ClipScore-Nodes": [ + [ + "HaojihuiClipScoreFakeImageProcessor", + "HaojihuiClipScoreImageProcessor", + "HaojihuiClipScoreImageScore", + "HaojihuiClipScoreLoader", + "HaojihuiClipScoreRealImageProcessor", + "HaojihuiClipScoreTextProcessor" + ], + { + "title_aux": "ComfyUI-ClipScore-Nodes" + } + ], + "https://github.com/azure-dragon-ai/ComfyUI-HPSv2-Nodes": [ + [ + "GetImageSize", + "HaojihuiHPSv2ImageProcessor", + "HaojihuiHPSv2ImageScore", + "HaojihuiHPSv2ImageScores", + "HaojihuiHPSv2Loader", + "HaojihuiHPSv2SaveAnimatedWEBP", + "HaojihuiHPSv2SaveImage", + "HaojihuiHPSv2SaveWEBP", + "HaojihuiHPSv2SaveWebpImage", + "HaojihuiHPSv2TextProcessor", + "SaveImageWebp", + "ScaleShort" + ], + { + "title_aux": "ComfyUI-HPSv2-Nodes" + } + ], + "https://github.com/babe-and-spencer-enterprises/base-comfyui-node": [ + [ + "UploadToBaseNode" + ], + { + "title_aux": "ComfyUI Upload to BASE Node" + } + ], + "https://github.com/bablueza/ComfyUI-Vaja-Ai4thai": [ + [ + "ShowText", + "Vaja Synthesis Api" + ], + { + "title_aux": "Vaja TextToSpeech Node for ComfyUI" + } + ], + "https://github.com/babydjac/comfyui-grok-prompts": [ + [ + "Flux", + "PonyXL" + ], + { + "title_aux": "ComfyUI Grok Prompts" + } + ], + "https://github.com/babydjac/comfyui-smart-scaler": [ + [ + "AspectRatioAdjuster", + "BatchFrameProcessor", + "DynamicResolutionSelector", + "ImageMetadataExtractor", + "SizeParser", + "SmartAspectScaler", + "WanVideoFrameScaler" + ], + { + "title_aux": "ComfyUI Smart Scaler" + } + ], + "https://github.com/badayvedat/ComfyUI-fal-Connector": [ + [ + "RemoteCheckpointLoader_fal", + "RemoteLoraLoader_fal" + ], + { + "title_aux": "ComfyUI-fal-Connector" + } + ], + "https://github.com/badjeff/comfyui_lora_tag_loader": [ + [ + "LoraTagLoader" + ], + { + "title_aux": "LoRA Tag Loader for ComfyUI" + } + ], + "https://github.com/badxprogramm/ComfyUI-GradientBlur": [ + [ + "GradientBlur" + ], + { + "title_aux": "GradientBlurNode for ComfyUI" + } + ], + "https://github.com/baicai99/ComfyUI-FrameSkipping": [ + [ + "FrameSelector", + "FrameSkipping", + "FrameTruncating", + "IntOperationsNode", + "MaskFrameSkipping", + "MaskGenerator", + "MaskSelector" + ], + { + "title_aux": "ComfyUI-FrameSkipping" + } + ], + "https://github.com/bananasss00/ComfyUI-SP-Nodes": [ + [ + "BoolSwitchOutStr", + "CivitaiPrompts", + "ComfyuiRuntimeArgs", + "FaceScatter", + "FaceScatter2", + "FluxInspireLbw_Batch", + "FluxInspireLbw_BlockVectorPreset", + "GodnessMerger_Apply", + "GodnessMerger_InputBlocks", + "GodnessMerger_InputBlocksExperimental", + "GodnessMerger_LabelEmb", + "GodnessMerger_MiddleBlock", + "GodnessMerger_MiddleBlockExperimental", + "GodnessMerger_NoiseInjection", + "GodnessMerger_Out", + "GodnessMerger_OutputBlocks", + "GodnessMerger_OutputBlocksExperimental", + "GodnessMerger_RAW_Apply", + "GodnessMerger_TimeEmbed", + "ImageMonitor", + "ImgMetaValueExtractor", + "LoraLoaderByPath", + "LoraLoaderFromFolder", + "LoraLoaderOnlyModelByPath", + "NoiseInjectionEssentialsHookProvider", + "PreviewImageWEBP", + "PromptChecker", + "RandomPromptFromBook", + "Random_Model_Merge", + "SD3BlocksMultiply", + "SD3Multiply", + "SP-CheckpointSave", + "SP-UnetSave", + "SP_DDInpaint_Pipe", + "SP_DictValue", + "SP_DynamicCombo", + "SP_FlorenceCaption", + "SP_FluxFastMergePatchFP8 [Experimental]", + "SP_FluxLoader", + "SP_FluxUnsampler", + "SP_FluxUnsampler_ForwardODESampler", + "SP_FluxUnsampler_InverseSampler", + "SP_HiresGen", + "SP_HiresGen_Dynamic", + "SP_HiresGen_HiresCfg", + "SP_HiresGen_Sharpen", + "SP_HunyuanLoader", + "SP_ImpactSwitchCombo", + "SP_KSampler", + "SP_KSamplerSelect", + "SP_KoboldCpp", + "SP_KoboldCppWithContext", + "SP_KoboldCpp_BannedTokens", + "SP_KoboldCpp_OverrideCfg", + "SP_ListAny", + "SP_ModelLoader", + "SP_Pass", + "SP_Pipe", + "SP_Pipe_ToBasicPipe", + "SP_SD3Loader", + "SP_SDLoader", + "SP_SetPipeModelType", + "SP_Supir", + "SP_SupirSampler", + "SP_SupirSampler_DPMPP2M", + "SP_SupirSampler_EDM", + "SP_SwitchBooleanAny", + "SP_UnlistValues", + "SP_WebsocketSendImage", + "SP_XYGrid", + "SP_XYValues", + "ScatterParams", + "ScatterParamsBatch", + "SendTelegramChatBot", + "StrToCombo", + "TextSplitJoinByDelimiter" + ], + { + "author": "SeniorPioner", + "description": "Node Pack: PromptChecker for token toggling, KoboldCPP API, ModelMerging, Telegram-Bot-API, and more", + "nickname": "SP-Nodes", + "title": "SP-Nodes", + "title_aux": "SP-Nodes" + } + ], + "https://github.com/bananasss00/ComfyUI-flux_fill_patcher": [ + [ + "ApplyFluxFillPatch" + ], + { + "title_aux": "ComfyUI-flux_fill_patcher" + } + ], + "https://github.com/banodoco/steerable-motion": [ + [ + "BatchCreativeInterpolation", + "IpaConfiguration", + "RemoveAndInterpolateFrames", + "VideoContinuationGenerator", + "VideoFrameExtractorAndMaskGenerator", + "WanInputFrameNumber", + "WanVideoBlender" + ], + { + "title_aux": "Steerable Motion" + } + ], + "https://github.com/banqingyuan/ComfyUI-text-replace": [ + [ + "ChatOverlayNode", + "ExtractJsonNode", + "ImageEraseNode", + "OCRLocNode" + ], + { + "title_aux": "ComfyUI-text-replace" + } + ], + "https://github.com/bartly/Comfyui_babel_removebg_api": [ + [ + "BabelRemovebg" + ], + { + "title_aux": "Babel Removebg Api Node for ComfyUI" + } + ], + "https://github.com/bash-j/mikey_nodes": [ + [ + "AddMetaData", + "Batch Crop Image", + "Batch Crop Resize Inplace", + "Batch Load Images", + "Batch Resize Image for SDXL", + "Checkpoint Loader Simple Mikey", + "CheckpointHash", + "CheckpointSaveModelOnly", + "CinematicLook", + "Empty Latent Ratio Custom SDXL", + "Empty Latent Ratio Select SDXL", + "EvalFloats", + "FaceFixerOpenCV", + "FileNamePrefix", + "FileNamePrefixDateDirFirst", + "Float to String", + "GetSubdirectories", + "HaldCLUT", + "Image Caption", + "ImageBorder", + "ImageOverlay", + "ImagePaste", + "Int to String", + "LMStudioPrompt", + "Load Image Based on Number", + "LoraSyntaxProcessor", + "Mikey Sampler", + "Mikey Sampler Base Only", + "Mikey Sampler Base Only Advanced", + "Mikey Sampler Tiled", + "Mikey Sampler Tiled Base Only", + "MikeyLatentTileSampler", + "MikeyLatentTileSamplerCustom", + "MikeySamplerTiledAdvanced", + "MikeySamplerTiledAdvancedBaseOnly", + "ModelMergePixArtSigmaXL2_1024MS", + "ModelMergeTrainDiff", + "ModelMergeTrainDiffPixartSigmaXL2_1024MS", + "MosaicExpandImage", + "OobaPrompt", + "PresetRatioSelector", + "Prompt With SDXL", + "Prompt With Style", + "Prompt With Style V2", + "Prompt With Style V3", + "Range Float", + "Range Integer", + "Ratio Advanced", + "RemoveTextBetween", + "Resize Image for SDXL", + "SD3TextConditioningWithOptionsOnePrompt", + "SRFloatPromptInput", + "SRIntPromptInput", + "SRStringPromptInput", + "Save Image If True", + "Save Image With Prompt Data", + "Save Images Mikey", + "Save Images No Display", + "SaveMetaData", + "SearchAndReplace", + "Seed String", + "Style Conditioner", + "Style Conditioner Base Only", + "Text2InputOr3rdOption", + "TextCombinations", + "TextCombinations3", + "TextConcat", + "TextPadderMikey", + "TextPreserve", + "Upscale Tile Calculator", + "Wildcard Processor", + "WildcardAndLoraSyntaxProcessor", + "WildcardOobaPrompt" + ], + { + "title_aux": "Mikey Nodes" + } + ], + "https://github.com/bbaudio-2025/ComfyUI-SuperUltimateVaceTools": [ + [ + "CustomCropArea", + "CustomRefineOption", + "RegionalBatchPrompt", + "SuperUltimateVACEUpscale", + "VACEControlImageCombine", + "VACEPromptCheckTotalFrame", + "VACEPromptCombine", + "VaceLongVideo" + ], + { + "title_aux": "ComfyUI-SuperUltimateVaceTools" + } + ], + "https://github.com/bbtaivi/ComfyUI-Aiv-Param": [ + [ + "AivParam" + ], + { + "title_aux": "AIV ComfyUI Node" + } + ], + "https://github.com/bear2b/comfyui-argo-nodes": [ + [ + "ColorMatrixGPU", + "LoadGridFromURL", + "SaveGridToS3" + ], + { + "title_aux": "ColorMatrixGPU Node for ComfyUI" + } + ], + "https://github.com/bedovyy/ComfyUI_NAIDGenerator": [ + [ + "ColorizeNAID", + "DeclutterNAID", + "EmotionNAID", + "GenerateNAID", + "Img2ImgOptionNAID", + "InpaintingOptionNAID", + "LineArtNAID", + "MaskImageToNAID", + "ModelOptionNAID", + "NetworkOptionNAID", + "PromptToNAID", + "RemoveBGNAID", + "SketchNAID", + "V4BasePrompt", + "V4NegativePrompt", + "VibeTransferOptionNAID" + ], + { + "title_aux": "ComfyUI_NAIDGenerator" + } + ], + "https://github.com/bemoregt/ComfyUI_CustomNode_Image2Spectrum": [ + [ + "Image_Spectrum" + ], + { + "title_aux": "ComfyUI_CustomNode_Image2Spectrum" + } + ], + "https://github.com/benda1989/CosyVoice2_ComfyUI": [ + [ + "CosyVoice3s", + "CosyVoiceCrossLingual", + "CosyVoiceLoader", + "CosyVoiceNLControl", + "CosyVoiceSonic", + "Text2" + ], + { + "title_aux": "GKK\u00b7CosyVoice" + } + ], + "https://github.com/benda1989/Sonic_ComfyUI": [ + [ + "SonicLoader", + "SonicSimper", + "SonicSpeechs" + ], + { + "title_aux": "GKK\u00b7Sonic" + } + ], + "https://github.com/benjamin-bertram/Comfyui_OIDN_Denoiser": [ + [ + "OIDNDenoiser" + ], + { + "title_aux": "ComfyUI OIDN Denoiser" + } + ], + "https://github.com/benjiyaya/ComfyUI-HunyuanVideoImagesGuider": [ + [ + "Hunyuan Video Image To Guider" + ], + { + "title_aux": "ComfyUI-HunyuanVideoImagesGuider" + } + ], + "https://github.com/benjiyaya/ComfyUI-KokoroTTS": [ + [ + "Kokoro TextToSpeech" + ], + { + "title_aux": "ComfyUI-KokoroTTS" + } + ], + "https://github.com/benstaniford/comfy-contact-sheet-image-loader": [ + [ + "ContactSheetImageLoader" + ], + { + "title_aux": "Comfy Contact Sheet Image Loader" + } + ], + "https://github.com/benstaniford/comfy-load-last-image": [ + [ + "LoadMostRecentImage" + ], + { + "title_aux": "ComfyUI Load Most Recent Image Node" + } + ], + "https://github.com/benstaniford/comfy-lora-loader-with-triggerdb": [ + [ + "LoRaLoaderWithTriggerDB" + ], + { + "title_aux": "LoRa Loader with Trigger Database" + } + ], + "https://github.com/benstaniford/comfy-prompt-db": [ + [ + "PromptDB", + "PromptStack" + ], + { + "title_aux": "Prompt Database for ComfyUI" + } + ], + "https://github.com/bentoml/comfy-pack": [ + [ + "CPackInputAny", + "CPackInputFile", + "CPackInputImage", + "CPackInputInt", + "CPackInputString", + "CPackOutputAudio", + "CPackOutputFile", + "CPackOutputImage", + "CPackOutputTextFile", + "CPackOutputVideo", + "CPackOutputZip", + "CPackOutputZipSwitch" + ], + { + "title_aux": "Comfy-Pack" + } + ], + "https://github.com/big-mon/ComfyUI-ResolutionPresets": [ + [ + "ResolutionPresetsSDXL" + ], + { + "title_aux": "ComfyUI-ResolutionPresets" + } + ], + "https://github.com/bikiam/ComfyUI_WhisperSRT": [ + [ + "WhisperAudioToSRTText" + ], + { + "title_aux": "ComfyUI_WhisperSRT" + } + ], + "https://github.com/bilal-arikan/ComfyUI_TextAssets": [ + [ + "LoadTextAsset" + ], + { + "title_aux": "ComfyUI_TextAssets" + } + ], + "https://github.com/billwuhao/ComfyUI_ACE-Step": [ + [ + "ACELoRALoader", + "ACEModelLoader", + "ACEStepEdit", + "ACEStepExtend", + "ACEStepGen", + "ACEStepRepainting", + "GenerationParameters", + "LyricsLangSwitch", + "MultiLineLyrics", + "MultiLinePromptACES" + ], + { + "title_aux": "ComfyUI_ACE-Step" + } + ], + "https://github.com/billwuhao/ComfyUI_AudioTools": [ + [ + "AddSubtitlesToVideo", + "AdjustAudio", + "AudioAddWatermark", + "AudioConcatenate", + "AudioDenoising", + "AudioRecorderAT", + "ClearVoiceRun", + "LoadAudioMW", + "MergeAudioMW", + "MinimalPauseNode", + "MultiLinePromptAT", + "MusicSeparation", + "RemoveSilence", + "SpeechSeparation", + "StringEditNode", + "TrimAudio" + ], + { + "title_aux": "ComfyUI_AudioTools" + } + ], + "https://github.com/billwuhao/ComfyUI_CSM": [ + [ + "CSMDialogRun", + "CSMSpeakersPreview", + "MultiLineText" + ], + { + "title_aux": "ComfyUI_CSM" + } + ], + "https://github.com/billwuhao/ComfyUI_DiffRhythm": [ + [ + "DiffRhythmRun", + "MultiLineLyricsDR" + ], + { + "title_aux": "ComfyUI_DiffRhythm_MW" + } + ], + "https://github.com/billwuhao/ComfyUI_EraX-WoW-Turbo": [ + [ + "EraXWoWRUN", + "WhisperTurboRun" + ], + { + "title_aux": "MW-ComfyUI_EraX-WoW-Turbo" + } + ], + "https://github.com/billwuhao/ComfyUI_IndexTTS": [ + [ + "IndexSpeakersPreview", + "IndexTTSRun", + "MultiLinePromptIndex" + ], + { + "title_aux": "ComfyUI_IndexTTS" + } + ], + "https://github.com/billwuhao/ComfyUI_KokoroTTS_MW": [ + [ + "KokoroRun", + "KokoroZHRun", + "MultiLinePromptKK" + ], + { + "title_aux": "ComfyUI_KokoroTTS_MW" + } + ], + "https://github.com/billwuhao/ComfyUI_MegaTTS3": [ + [ + "MegaTTS3Run", + "MegaTTS3SpeakersPreview", + "MultiLinePromptMG" + ], + { + "title_aux": "MW-ComfyUI_MegaTTS3" + } + ], + "https://github.com/billwuhao/ComfyUI_NotaGen": [ + [ + "NotaGenRun" + ], + { + "title_aux": "ComfyUI_NotaGen" + } + ], + "https://github.com/billwuhao/ComfyUI_OneButtonPrompt": [ + [ + "LoadImageAndPromptFromURL", + "LoadImageFromURL", + "LoadPrompt", + "StringEditNodeOBP" + ], + { + "title_aux": "MW-ComfyUI_OneButtonPrompt" + } + ], + "https://github.com/billwuhao/ComfyUI_OuteTTS": [ + [ + "OuteTTSRun" + ], + { + "title_aux": "MW-ComfyUI_OuteTTS" + } + ], + "https://github.com/billwuhao/ComfyUI_PortraitTools": [ + [ + "AlignFace", + "BeautifyPhoto", + "DetectCropFace", + "IDPhotos", + "ImageWatermark", + "LoadImageMW" + ], + { + "title_aux": "MW-ComfyUI_PortraitTools" + } + ], + "https://github.com/billwuhao/ComfyUI_SOME": [ + [ + "SomeSing2Midi" + ], + { + "title_aux": "ComfyUI_SOME" + } + ], + "https://github.com/billwuhao/ComfyUI_SparkTTS": [ + [ + "AudioRecorderSpark", + "SparkTTSClone", + "SparkTTSRun" + ], + { + "title_aux": "ComfyUI_SparkTTS" + } + ], + "https://github.com/billwuhao/ComfyUI_StepAudioTTS": [ + [ + "AudioRecorder", + "StepAudioClone", + "StepAudioRun" + ], + { + "title_aux": "ComfyUI_StepAudioTTS" + } + ], + "https://github.com/billwuhao/ComfyUI_gemmax": [ + [ + "GemmaxRun", + "QuickMTRun" + ], + { + "title_aux": "MW-ComfyUI_gemmax" + } + ], + "https://github.com/billwuhao/ComfyUI_parakeet-tdt": [ + [ + "ParakeetASRRun" + ], + { + "title_aux": "ComfyUI_parakeet-tdt" + } + ], + "https://github.com/billwuhao/Comfyui_HeyGem": [ + [ + "HeyGemRun" + ], + { + "title_aux": "Comfyui_HeyGem" + } + ], + "https://github.com/bitaffinity/ComfyUI_HF_Inference": [ + [ + "Classification", + "FeatureExtraction", + "Generation", + "ObjectDetection", + "QuestionAnswering", + "Segmentation", + "TextToImage", + "Translation" + ], + { + "title_aux": "ComfyUI_HF_Inference" + } + ], + "https://github.com/black-forest-labs/bfl-comfy-nodes": [ + [ + "FLUX 1.0 [canny]", + "FLUX 1.0 [canny] Finetuned", + "FLUX 1.0 [depth]", + "FLUX 1.0 [depth] Finetuned", + "FLUX 1.0 [dev]", + "FLUX 1.0 [fill]", + "FLUX 1.0 [fill] Finetuned", + "FLUX 1.0 [pro]", + "FLUX 1.0 [pro] Finetuned", + "FLUX 1.1 [pro]", + "FLUX 1.1 [ultra]", + "FLUX 1.1 [ultra] Finetuned" + ], + { + "title_aux": "Black Forest Labs API Nodes" + } + ], + "https://github.com/blackcodetavern/ComfyUI-Benripack": [ + [ + "AnimationExtractor", + "CharacterPipe", + "Load3DModel" + ], + { + "title_aux": "ComfyUI-Benripack" + } + ], + "https://github.com/blepping/ComfyUI-ApplyResAdapterUnet": [ + [ + "ApplyResAdapterUnet" + ], + { + "title_aux": "ComfyUI-ApplyResAdapterUnet" + } + ], + "https://github.com/blepping/ComfyUI-bleh": [ + [ + "BlehBlockCFG", + "BlehBlockOps", + "BlehCast", + "BlehDeepShrink", + "BlehDisableNoise", + "BlehDiscardPenultimateSigma", + "BlehEnsurePreviewer", + "BlehForceSeedSampler", + "BlehGlobalSageAttention", + "BlehHyperTile", + "BlehInsaneChainSampler", + "BlehLatentBlend", + "BlehLatentOps", + "BlehLatentScaleBy", + "BlehModelPatchConditional", + "BlehPlug", + "BlehRefinerAfter", + "BlehSageAttentionSampler", + "BlehSetSamplerPreset", + "BlehSetSigmas", + "BlehTAEVideoDecode", + "BlehTAEVideoEncode" + ], + { + "title_aux": "ComfyUI-bleh" + } + ], + "https://github.com/blepping/ComfyUI-sonar": [ + [ + "FreeUExtreme", + "FreeUExtremeConfig", + "NoisyLatentLike", + "SONAR_CUSTOM_NOISE to NOISE", + "SamplerConfigOverride", + "SamplerSonarDPMPPSDE", + "SamplerSonarEuler", + "SamplerSonarEulerA", + "SonarAdvanced1fNoise", + "SonarAdvancedCollatzNoise", + "SonarAdvancedDistroNoise", + "SonarAdvancedPowerLawNoise", + "SonarAdvancedPyramidNoise", + "SonarApplyLatentOperationCFG", + "SonarBlendedNoise", + "SonarChannelNoise", + "SonarCompositeNoise", + "SonarCustomNoise", + "SonarCustomNoiseAdv", + "SonarGuidanceConfig", + "SonarGuidedNoise", + "SonarLatentOperationAdvanced", + "SonarLatentOperationFilteredNoise", + "SonarLatentOperationNoise", + "SonarLatentOperationQuantileFilter", + "SonarLatentOperationSetSeed", + "SonarModulatedNoise", + "SonarNoiseImage", + "SonarNormalizeNoiseToScale", + "SonarPatternBreakNoise", + "SonarPerDimNoise", + "SonarPowerFilter", + "SonarPowerFilterNoise", + "SonarPowerNoise", + "SonarPreviewFilter", + "SonarQuantileFilteredNoise", + "SonarRandomNoise", + "SonarRepeatedNoise", + "SonarResizedNoise", + "SonarRippleFilteredNoise", + "SonarScatternetFilteredNoise", + "SonarScheduledNoise", + "SonarShuffledNoise", + "SonarSplitNoiseChain", + "SonarWaveletFilteredNoise", + "SonarWaveletNoise" + ], + { + "title_aux": "ComfyUI-sonar" + } + ], + "https://github.com/blepping/comfyui_jankdiffusehigh": [ + [ + "DiffuseHighParam", + "DiffuseHighSampler" + ], + { + "title_aux": "comfyui_jankdiffusehigh" + } + ], + "https://github.com/blepping/comfyui_jankhidiffusion": [ + [ + "ApplyMSWMSAAttention", + "ApplyMSWMSAAttentionSimple", + "ApplyRAUNet", + "ApplyRAUNetSimple" + ], + { + "title_aux": "comfyui_jankhidiffusion" + } + ], + "https://github.com/blepping/comfyui_overly_complicated_sampling": [ + [ + "OCS Group", + "OCS ModelSetMaxSigma", + "OCS MultiParam", + "OCS Param", + "OCS Sampler", + "OCS SimpleRestartSchedule", + "OCS Substeps", + "OCSNoise PerlinAdvanced", + "OCSNoise PerlinSimple", + "OCSNoise to SONAR_CUSTOM_NOISE" + ], + { + "title_aux": "comfyui_overly_complicated_sampling" + } + ], + "https://github.com/blird/ComfyUI-Wanify": [ + [ + "AdaptiveImageResize" + ], + { + "title_aux": "ComfyUI-Wanify: Adaptive Image Resize Node" + } + ], + "https://github.com/blob8/ComfyUI_sloppy-comic": [ + [ + "Generate Comic", + "LLM API Request" + ], + { + "title_aux": "ComfyUI_sloppy-comic" + } + ], + "https://github.com/blovett80/ComfyUI-PixelDojo": [ + [ + "PixelDojoAPI" + ], + { + "title_aux": "ComfyUI-PixelDojo" + } + ], + "https://github.com/blueraincoatli/comfyUI_SillyNodes": [ + [ + "BooleanJumper|SillyNode", + "CloseErrorWindowNode|SillyNode", + "QueueSequence|SillyNode", + "Screenshots|SillyNode", + "dummyInput|SillyNode", + "dummyInput|blueraincoat" + ], + { + "title_aux": "comfyUI_SillyNodes" + } + ], + "https://github.com/bluevisor/ComfyUI_PS_Blend_Node": [ + [ + "PSBlendNode" + ], + { + "title_aux": "ComfyUI_PS_Blend_Node" + } + ], + "https://github.com/bmad4ever/comfyui_ab_samplercustom": [ + [ + "AB SamplerCustom (experimental)" + ], + { + "title_aux": "comfyui_ab_sampler" + } + ], + "https://github.com/bmad4ever/comfyui_lists_cartesian_product": [ + [ + "AnyListCartesianProduct" + ], + { + "title_aux": "Lists Cartesian Product" + } + ], + "https://github.com/bmad4ever/comfyui_quilting": [ + [ + "GuessQuiltingBlockSize_Bmad", + "ImageQuiltingSeamlessMB_Bmad", + "ImageQuiltingSeamlessSB_Bmad", + "ImageQuilting_Bmad", + "LatentQuiltingSeamlessMB_Bmad", + "LatentQuiltingSeamlessSB_Bmad", + "LatentQuilting_Bmad" + ], + { + "title_aux": "comfyui_quilting" + } + ], + "https://github.com/bmad4ever/comfyui_wfc_like": [ + [ + "WFC_CustomTemperature_Bmad", + "WFC_CustomValueWeights_Bmad", + "WFC_Decode_BMad", + "WFC_EmptyState_Bmad", + "WFC_Encode_BMad", + "WFC_Filter_Bmad", + "WFC_GenParallel_Bmad", + "WFC_Generate_BMad", + "WFC_SampleNode_BMad" + ], + { + "title_aux": "comfyui_wfc_like" + } + ], + "https://github.com/bobmagicii/comfykit-custom-nodes": [ + [ + "LoraStackFiveSimple", + "LoraThree", + "LoraWithMeta", + "TypecasterClip", + "TypecasterCond", + "TypecasterImage", + "TypecasterLatent", + "TypecasterModel", + "TypecasterVae" + ], + { + "title_aux": "ComfyKit Custom Nodes" + } + ], + "https://github.com/bollerdominik/ComfyUI-load-lora-from-url": [ + [ + "LoadLoraFromUrlOrPath", + "LoadVideoLoraFromUrlOrPath" + ], + { + "title_aux": "ComfyUI-load-lora-from-url" + } + ], + "https://github.com/bombax-xiaoice/ComfyUI-Allegro": [ + [ + "AllegroDecoder", + "AllegroEncoder", + "AllegroSampler", + "AllegroTI2VEncoder", + "AllegroTI2VSampler", + "AllegroTextEncoder", + "LoadAllegroModel", + "LoadAllegroTI2VModel" + ], + { + "title_aux": "ComfyUI-Allegro" + } + ], + "https://github.com/bombax-xiaoice/ComfyUI-DisPose": [ + [ + "DisPoseDecoder", + "DisPoseLoader", + "DisPoseSampler" + ], + { + "title_aux": "ComfyUI-DisPose" + } + ], + "https://github.com/bombax-xiaoice/ComfyUI-MagicDance": [ + [ + "LoadMagicDanceModel", + "MagicDanceDecoder", + "MagicDanceEncoder", + "MagicDanceSampler" + ], + { + "title_aux": "ComfyUI-MagicDance" + } + ], + "https://github.com/bombax-xiaoice/ComfyUI-Open-Sora-I2V": [ + [ + "OpenSoraDecoder", + "OpenSoraEncoder", + "OpenSoraLoader", + "OpenSoraSampler", + "OpenSoraTextEncoder" + ], + { + "title_aux": "ComfyUI-Open-Sora-I2V" + } + ], + "https://github.com/bombax-xiaoice/ComfyUI-OpenSoraPlan": [ + [ + "OpenSoraPlan0LoaderT2V", + "OpenSoraPlan1LoaderT2V", + "OpenSoraPlan2LoaderI2V", + "OpenSoraPlan2LoaderT2V", + "OpenSoraPlan2SamplerI2V", + "OpenSoraPlan3LoaderI2V", + "OpenSoraPlan3LoaderT2V", + "OpenSoraPlan3SamplerI2V", + "OpenSoraPlanDecoder", + "OpenSoraPlanPromptRefiner", + "OpenSoraPlanSamplerT2V" + ], + { + "title_aux": "ComfyUI-OpenSoraPlan" + } + ], + "https://github.com/bombless/comfyUI-RememberingUtils": [ + [ + "RememberLastSeed", + "ShowLastSeed", + "ShowLastText" + ], + { + "title_aux": "Remembering utils" + } + ], + "https://github.com/bongsang/ComfyUI-Bongsang": [ + [ + "AnyInfo", + "RgbChannel" + ], + { + "title_aux": "ComfyUI-Bongsang" + } + ], + "https://github.com/boredofnames/ComfyUI-ntfy": [ + [ + "Ntfy", + "SaveImageAndNtfy" + ], + { + "title_aux": "ComfyUI-ntfy" + } + ], + "https://github.com/boricuapab/ComfyUI-Bori-JsonSetGetConverter": [ + [ + "Bori Json Get Set Convert" + ], + { + "title_aux": "ComfyUI-Bori-JsonSetGetConverter" + } + ], + "https://github.com/bradsec/ComfyUI_ResolutionSelector": [ + [ + "ResolutionSelector" + ], + { + "title_aux": "ResolutionSelector for ComfyUI" + } + ], + "https://github.com/bradsec/ComfyUI_StringEssentials": [ + [ + "StringMultiReplace", + "StringPreview", + "StringStrip", + "StringTextbox" + ], + { + "title_aux": "ComfyUI_StringEssentials" + } + ], + "https://github.com/braintacles/braintacles-comfyui-nodes": [ + [ + "CLIPTextEncodeSDXL-Multi-IO", + "CLIPTextEncodeSDXL-Pipe", + "Empty Latent Image from Aspect-Ratio", + "Interval Sampler", + "Random Find and Replace" + ], + { + "title_aux": "braintacles-nodes" + } + ], + "https://github.com/brantje/ComfyUI-api-tools": [ + [ + "SimpleGenImageInterface" + ], + { + "title_aux": "ComfyUI-api-tools" + } + ], + "https://github.com/brantje/ComfyUI_MagicQuill": [ + [ + "MagicQuill" + ], + { + "author": "Zichen LIU (https://zliucz.github.io/) and Yue YU (https://bruceyyu.github.io/)", + "description": "Official ComfyUI Implementations for Paper - MagicQuill: An Intelligent Interactive Image Editing System", + "nickname": "MagicQuill nodes", + "title": "MagicQuill", + "title_aux": "ComfyUI-MagicQuill" + } + ], + "https://github.com/brayevalerien/ComfyUI-SplitString": [ + [ + "Split String" + ], + { + "title_aux": "ComfyUI-splitstring" + } + ], + "https://github.com/brayevalerien/ComfyUI-resynthesizer": [ + [ + "Resynthesize" + ], + { + "title_aux": "ComfyUI Resynthesizer" + } + ], + "https://github.com/brianfitzgerald/style_aligned_comfy": [ + [ + "StyleAlignedBatchAlign", + "StyleAlignedReferenceSampler", + "StyleAlignedSampleReferenceLatents" + ], + { + "title_aux": "StyleAligned for ComfyUI" + } + ], + "https://github.com/bronkula/comfyui-fitsize": [ + [ + "FS: Crop Image Into Even Pieces", + "FS: Fit Image And Resize", + "FS: Fit Size From Image", + "FS: Fit Size From Int", + "FS: Image Region To Mask", + "FS: Load Image And Resize To Fit", + "FS: Pick Image From Batch", + "FS: Pick Image From Batches", + "FS: Pick Image From List" + ], + { + "title_aux": "comfyui-fitsize" + } + ], + "https://github.com/brucew4yn3rp/ComfyUI_SelectiveMetadata": [ + [ + "Multiline String", + "Save Image (Selective Metadata)", + "SaveImage" + ], + { + "title_aux": "Save Image with Selective Metadata" + } + ], + "https://github.com/bruefire/ComfyUI-SeqImageLoader": [ + [ + "VFrame Loader With Mask Editor", + "Video Loader With Mask Editor" + ], + { + "title_aux": "ComfyUI Sequential Image Loader" + } + ], + "https://github.com/budihartono/comfyui-aspect-ratio-presets": [ + [ + "CAS Empty Latent Aspect Ratio Axis", + "CAS Empty Latent Aspect Ratio Preset" + ], + { + "title_aux": "CAS Aspect Ratio Presets Node for ComfyUI" + } + ], + "https://github.com/budihartono/comfyui_otonx_nodes": [ + [ + "OTX Integer Multiple Inputs 4", + "OTX Integer Multiple Inputs 5", + "OTX Integer Multiple Inputs 6", + "OTX KSampler Feeder", + "OTX Versatile Multiple Inputs 4", + "OTX Versatile Multiple Inputs 5", + "OTX Versatile Multiple Inputs 6" + ], + { + "title_aux": "Otonx's Custom Nodes" + } + ], + "https://github.com/bugltd/ComfyLab-Pack": [ + [ + "Convert to Any (lab)", + "File Queue (lab)", + "Format: Multiline (lab)", + "Format: String (lab)", + "Generic Queue (lab)", + "Image Queue (lab)", + "Image: Downscale to Total Pixels (lab)", + "Input: Boolean (lab)", + "Input: Float (lab)", + "Input: Folder (lab)", + "Input: Integer (lab)", + "Input: Multiline (lab)", + "Input: String (lab)", + "List: Checkpoints (lab)", + "List: Limit (lab)", + "List: LoRAs (lab)", + "List: Merge (lab)", + "List: Random Seeds (lab)", + "List: Samplers (lab)", + "List: Schedulers (lab)", + "List: from Elements (lab)", + "List: from File (backend) (lab)", + "List: from Multiline (lab)", + "List: from String (lab)", + "Load Image (RGBA) (lab)", + "Output Config: Load (lab)", + "Output Config: Retrieve (backend) (lab)", + "Plot Config: Grid (lab)", + "Plot Config: Header/Footer (lab)", + "Resolution to Dimensions (lab)", + "Save Text File (lab)", + "Sleep (lab)", + "XY Plot: Queue (lab)", + "XY Plot: Render (lab)", + "XY Plot: Split Data (lab)" + ], + { + "nodename_pattern": " \\(lab\\)$", + "title_aux": "ComfyLab Pack" + } + ], + "https://github.com/burnsbert/ComfyUI-EBU-LMStudio": [ + [ + "EbuLMStudioBrainstormer", + "EbuLMStudioLoadModel", + "EbuLMStudioMakeRequest", + "EbuLMStudioUnload", + "EbuLMStudioUnloadGuider" + ], + { + "title_aux": "EBU LMStudio LLM Integration" + } + ], + "https://github.com/burnsbert/ComfyUI-EBU-PromptHelper": [ + [ + "EbuPromptHelperCharacterDescriberFemale", + "EbuPromptHelperCharacterDescriberMale", + "EbuPromptHelperCombineTwoStrings", + "EbuPromptHelperConsumeListItem", + "EbuPromptHelperCurrentDateTime", + "EbuPromptHelperListSampler", + "EbuPromptHelperLoadFileAsString", + "EbuPromptHelperRandomColorPalette", + "EbuPromptHelperRandomize", + "EbuPromptHelperReplace", + "EbuPromptHelperSeasonWeatherTimeOfDay", + "EbuPromptHelperTruncate" + ], + { + "title_aux": "EBU PromptHelper" + } + ], + "https://github.com/burnsbert/ComfyUI-EBU-Workflow": [ + [ + "EbuAppendToFile", + "EbuDecodeNewLines", + "EbuEncodeNewLines", + "EbuFileListCache", + "EbuGetImageAspectRatio", + "EbuReadFromFile", + "EbuScalingResolution", + "EbuScalingTile", + "EbuUniqueFileName" + ], + { + "title_aux": "EBU Workflow" + } + ], + "https://github.com/bvhari/ComfyUI_CFGStar": [ + [ + "CFGStar" + ], + { + "title_aux": "ComfyUI_CFGStar" + } + ], + "https://github.com/bvhari/ComfyUI_ImageProcessing": [ + [ + "BilateralFilter", + "Brightness", + "Gamma", + "Hue", + "Saturation", + "SigmoidCorrection", + "UnsharpMask" + ], + { + "title_aux": "ImageProcessing" + } + ], + "https://github.com/bvhari/ComfyUI_PerpCFG": [ + [ + "PerpCFG" + ], + { + "title_aux": "ComfyUI_PerpCFG" + } + ], + "https://github.com/bvhari/ComfyUI_PerpWeight": [ + [ + "CLIPTextEncodePerpWeight" + ], + { + "title_aux": "ComfyUI_PerpWeight" + } + ], + "https://github.com/bvhari/ComfyUI_SUNoise": [ + [ + "SUNoiseLatent", + "SamplersSUNoise", + "SamplersSUNoiseAdvanced" + ], + { + "title_aux": "ComfyUI_SUNoise" + } + ], + "https://github.com/bytedance/ComfyUI-HyperLoRA": [ + [ + "HyperLoRAApplyLoRA", + "HyperLoRABaseCond", + "HyperLoRAConfig", + "HyperLoRAFaceAttr", + "HyperLoRAGenerateBaseLoRA", + "HyperLoRAGenerateIDLoRA", + "HyperLoRAIDCond", + "HyperLoRALoader", + "HyperLoRASaveLoRA", + "HyperLoRAUniGenerateIDLoRA", + "HyperLoRAUniLoader" + ], + { + "title_aux": "ComfyUI-HyperLoRA" + } + ], + "https://github.com/bytedance/ComfyUI_InfiniteYou": [ + [ + "FaceCombine", + "FaceSwap_InfiniteYou", + "InfiniteYouApply" + ], + { + "title_aux": "ComfyUI_InfiniteYou" + } + ], + "https://github.com/c0ffymachyne/ComfyUI_BeatByte": [ + [ + "BytebeatSynth" + ], + { + "title_aux": "Bytebeat Synthesizer: Composing with Operators" + } + ], + "https://github.com/c0ffymachyne/ComfyUI_SignalProcessing": [ + [ + "SignalProcessingBaxandall3BandEQ", + "SignalProcessingBaxandallEQ", + "SignalProcessingCompressor", + "SignalProcessingConvolutionReverb", + "SignalProcessingFilter", + "SignalProcessingHarmonicsEnhancer", + "SignalProcessingLimiter", + "SignalProcessingLoadAudio", + "SignalProcessingLoudness", + "SignalProcessingMixdown", + "SignalProcessingNormalizer", + "SignalProcessingPadSynth", + "SignalProcessingPadSynthChoir", + "SignalProcessingPaulStretch", + "SignalProcessingPitchShifter", + "SignalProcessingSaturation", + "SignalProcessingSpectrogram", + "SignalProcessingStereoWidening", + "SignalProcessingWaveform" + ], + { + "title_aux": "ComfyUI Signal Processing" + } + ], + "https://github.com/cake-ml/tiny-sana-preview": [ + [ + "TinySanaPreview" + ], + { + "title_aux": "TinySanaPreview" + } + ], + "https://github.com/calcuis/gguf": [ + [ + "ClipLoaderGGUF", + "DualClipLoaderGGUF", + "GGUFRun", + "GGUFSave", + "GGUFUndo", + "LoaderGGUF", + "LoaderGGUFAdvanced", + "QuadrupleClipLoaderGGUF", + "TENSORBoost", + "TENSORCut", + "TripleClipLoaderGGUF", + "VaeGGUF" + ], + { + "preemptions": [ + "LoaderGGUF", + "ClipLoaderGGUF", + "DualClipLoaderGGUF", + "TripleClipLoaderGGUF", + "LoaderGGUFAdvanced", + "GGUFSave" + ], + "title_aux": "gguf" + } + ], + "https://github.com/caleboleary/ComfyUI-Arc2Face": [ + [ + "Arc2FaceEncoderLoader", + "Arc2FaceFaceExtractor", + "Arc2FaceGenerator", + "Arc2FaceImageGridGenerator", + "Arc2FaceImg2ImgGenerator", + "Arc2FaceUNetLoader" + ], + { + "title_aux": "Arc2Face ComfyUI Node Library" + } + ], + "https://github.com/camenduru/ComfyUI-TostAI": [ + [ + "SendToTostAI" + ], + { + "title_aux": "ComfyUI-TostAI" + } + ], + "https://github.com/cardenluo/ComfyUI-Apt_Preset": [ + [ + "AD_DrawSchedule", + "AD_ImageExpandBatch", + "AD_MaskExpandBatch", + "AD_batch_replace", + "AD_font2img", + "AD_pingpong_vedio", + "AD_sch_IPA", + "AD_sch_image_merge", + "AD_sch_latent", + "AD_sch_mask", + "AD_sch_prompt_adv", + "AD_sch_prompt_basic", + "AD_sch_prompt_stack", + "AD_sch_value", + "AD_slice_Condi", + "Amp_audio_Normalized", + "Amp_drive_String", + "Amp_drive_mask", + "Amp_drive_value", + "Apply_CN_union", + "Apply_ControlNetStack", + "Apply_IPA", + "Apply_IPA_SD3", + "Apply_LoRAStack", + "Apply_Redux", + "Apply_adv_CN", + "Apply_condiStack", + "Apply_latent", + "Apply_textStack", + "CN_preset1_Unpack", + "CN_preset1_pack", + "Data_Highway", + "Data_basic", + "Data_bus_chx", + "Data_chx_Merge", + "Data_presetData", + "Data_preset_save", + "Data_sampleData", + "Data_select", + "IO_adjust_image", + "IO_clear_cache", + "IO_input_any", + "IO_inputbasic", + "IO_load_anyimage", + "IO_save_image", + "IO_video_encode", + "IPA_XL_PromptInjection", + "IPA_clip_vision", + "IPA_dapterSD3LOAD", + "Image_Channel_Apply", + "Image_Channel_Extract", + "Image_Channel_RemoveAlpha", + "Image_Pair_Merge", + "Image_Pair_crop", + "Image_Resize2", + "Image_Resize_sum", + "Image_Upscaletile", + "Image_batch_composite", + "Image_batch_select", + "Image_pad_outfill", + "Image_solo_crop", + "Image_solo_stitch", + "Image_transform_layer", + "Image_transform_solo", + "Mask_Detect_label", + "Mask_Remove_bg", + "Mask_face_detect", + "Mask_image2mask", + "Mask_math", + "Mask_splitMask", + "Mask_splitMask_by_color", + "Mask_split_mulMask", + "Mask_transform_sum", + "Model_Preset_Unpack", + "Model_Preset_pack", + "Stack_CN_union", + "Stack_ControlNet", + "Stack_ControlNet1", + "Stack_IPA", + "Stack_IPA_SD3", + "Stack_LoRA", + "Stack_Redux", + "Stack_WanCameralToVideo", + "Stack_WanFirstLastFrameToVideo", + "Stack_WanFunControlToVideo", + "Stack_WanFunInpaintToVideo", + "Stack_WanImageToVideo", + "Stack_WanVaceToVideo_mul", + "Stack_adv_CN", + "Stack_condi", + "Stack_latent", + "Stack_pre_Mark", + "Stack_text", + "basicIn_Sampler", + "basicIn_Scheduler", + "basicIn_Seed", + "basicIn_color", + "basicIn_float", + "basicIn_int", + "basicIn_string", + "basic_Ksampler_adv", + "basic_Ksampler_custom", + "basic_Ksampler_full", + "basic_Ksampler_mid", + "basic_Ksampler_simple", + "batch_BatchGetByIndex", + "batch_BatchSlice", + "batch_MergeBatch", + "chx_IPA_XL", + "chx_IPA_adv", + "chx_IPA_apply_combine", + "chx_IPA_basic", + "chx_IPA_faceID", + "chx_IPA_faceID_adv", + "chx_IPA_region_combine", + "chx_Ksampler_Kontext", + "chx_Ksampler_Kontext_adv", + "chx_Ksampler_Kontext_inpaint", + "chx_Ksampler_VisualStyle", + "chx_Ksampler_dual_area", + "chx_Ksampler_dual_paint", + "chx_Ksampler_inpaint", + "chx_Ksampler_mix", + "chx_Ksampler_refine", + "chx_Ksampler_texture", + "chx_StyleModelApply", + "chx_Style_Redux", + "chx_YC_LG_Redux", + "chx_ksampler_Deforum_sch", + "chx_ksampler_tile", + "chx_latent_adjust", + "color_Local_Gray", + "color_OneColor_keep", + "color_OneColor_replace", + "color_adjust_HDR", + "color_adjust_HSL", + "color_adjust_WB_balance", + "color_adjust_light", + "color_match_adv", + "color_tool", + "creat_any_List", + "creat_any_batch", + "creat_image_batch", + "creat_image_batch_input", + "creat_mask_batch", + "creat_mask_batch_input", + "create_AD_mask", + "create_Mask_Rectangles", + "create_Mask_lay_X", + "create_Mask_lay_Y", + "create_Mask_match_shape", + "create_Mask_visual_tag", + "create_RadialGradient", + "create_lineGradient", + "create_mask_solo", + "create_mulcolor_img", + "excel_Prompter", + "excel_column_diff", + "excel_insert_image", + "excel_read", + "excel_row_diff", + "excel_search_data", + "excel_write_data", + "img_effect_CircleWarp", + "img_effect_Liquify", + "img_effect_Load", + "img_effect_Stretch", + "img_effect_WaveWarp", + "latent_Image2Noise", + "latent_chx_noise", + "latent_ratio", + "lay_ImageGrid", + "lay_MaskGrid", + "lay_compare_img", + "lay_edge_cut", + "lay_fill_inpaint", + "lay_image_grid_note", + "lay_image_match_W_and_H", + "lay_images_free_layout", + "lay_imgCanvas", + "lay_text_sum", + "lay_texture_Offset", + "list_ListGetByIndex", + "list_ListSlice", + "list_MergeList", + "list_num_range", + "list_sch_Value", + "load_FLUX", + "load_GGUF", + "load_SD35", + "load_basic", + "math_Remap_data", + "math_calculate", + "model_Regional", + "model_Style_Align", + "model_adjust_color", + "model_diff_inpaint", + "pack_Pack", + "pack_Unpack", + "param_preset_Unpack", + "param_preset_pack", + "photoshop_preset_Unpack", + "photoshop_preset_pack", + "pre_Flex2", + "pre_Kontext", + "pre_Kontext_mul", + "pre_controlnet", + "pre_controlnet_union", + "pre_ic_light_sd15", + "pre_latent_light", + "pre_mul_Mulcondi", + "pre_sample_data", + "sampler_DynamicTileMerge", + "sampler_DynamicTileSplit", + "sampler_enhance", + "sch_Prompt", + "sch_Value", + "sch_image", + "sch_mask", + "sch_split_text", + "sch_text", + "stack_Mask2color", + "stack_sum_pack", + "sum_create_chx", + "sum_editor", + "sum_latent", + "sum_load_adv", + "sum_lora", + "sum_stack_AD", + "sum_stack_Wan", + "sum_stack_all", + "sum_stack_image", + "text_CSV_load", + "text_SuperPrompter", + "text_free_wildcards", + "text_mul_Join", + "text_mul_Split", + "text_mul_remove", + "text_mul_replace", + "text_stack_wildcards", + "text_sum", + "type_AnyCast", + "type_Anyswitch", + "type_BasiPIPE", + "type_BatchToList", + "type_Image_Batch2List", + "type_Image_List2Batch", + "type_ListToBatch", + "type_Mask_Batch2List", + "type_Mask_List2Batch", + "type_text_list2batch", + "unpack_box2", + "view_Data", + "view_GetLength", + "view_GetShape", + "view_GetWidgetsValues", + "view_Mask_And_Img", + "view_bridge_Text", + "view_bridge_image", + "view_combo", + "view_latent", + "view_mask", + "view_node_Script" + ], + { + "title_aux": "ComfyUI-Apt_Preset" + } + ], + "https://github.com/casterpollux/MiniMax-bmo": [ + [ + "MinimaxRemoverBMO" + ], + { + "nodename_pattern": "MiniMax.*BMO|BMO.*MiniMax", + "title_aux": "MiniMax Video Object Remover Suite" + } + ], + "https://github.com/catboxanon/comfyui_stealth_pnginfo": [ + [ + "CatboxAnonSaveImageStealth" + ], + { + "title_aux": "comfyui_stealth_pnginfo" + } + ], + "https://github.com/cdb-boop/ComfyUI-Bringing-Old-Photos-Back-to-Life": [ + [ + "BOPBTL_BlendFaces", + "BOPBTL_DetectEnhanceBlendFaces", + "BOPBTL_DetectFaces", + "BOPBTL_EnhanceFaces", + "BOPBTL_EnhanceFacesAdvanced", + "BOPBTL_LoadFaceDetectorModel", + "BOPBTL_LoadFaceEnhancerModel", + "BOPBTL_LoadRestoreOldPhotosModel", + "BOPBTL_LoadScratchMaskModel", + "BOPBTL_RestoreOldPhotos", + "BOPBTL_ScratchMask" + ], + { + "title_aux": "ComfyUI Bringing Old Photos Back to Life" + } + ], + "https://github.com/cdb-boop/comfyui-image-round": [ + [ + "ComfyUI_Image_Round__CircularCrop", + "ComfyUI_Image_Round__ImageCropAdvanced", + "ComfyUI_Image_Round__ImageRound", + "ComfyUI_Image_Round__ImageRoundAdvanced" + ], + { + "title_aux": "comfyui-image-round" + } + ], + "https://github.com/cdxOo/comfyui-text-node-with-comments": [ + [ + "text-node-with-comments" + ], + { + "title_aux": "Text Node With Comments (@cdxoo)" + } + ], + "https://github.com/cedarconnor/comfyui-BatchNameLoop": [ + [ + "Batch Image Iterator", + "Batch Image Loader", + "Batch Image Saver", + "Batch Image Single Saver" + ], + { + "title_aux": "ComfyUI Batch Name Loop" + } + ], + "https://github.com/cedarconnor/comfyui-LatLong": [ + [ + "Equirectangular Crop 180", + "Equirectangular Crop Square", + "Equirectangular Processor", + "Equirectangular Rotate" + ], + { + "title_aux": "ComfyUI LatLong - Equirectangular Image Processing Nodes" + } + ], + "https://github.com/cedarconnor/upsampler": [ + [ + "Upsampler Dynamic Upscale", + "Upsampler Precise Upscale", + "Upsampler Smart Upscale" + ], + { + "title_aux": "ComfyUI Upsampler Nodes" + } + ], + "https://github.com/celoron/ComfyUI-VisualQueryTemplate": [ + [ + "VisualQueryTemplateNode" + ], + { + "title_aux": "ComfyUI-VisualQueryTemplate" + } + ], + "https://github.com/celsojr2013/comfyui_jamworks_client": [ + [ + "Jamworks_Download", + "Jamworks_Login", + "Shell_Command" + ], + { + "title_aux": "comfyui_jamworks_client" + } + ], + "https://github.com/celsojr2013/comfyui_simpletools": [ + [ + "GoogleTranslator", + "Parameters", + "ResolutionSolver" + ], + { + "title_aux": "ComfyUI SimpleTools Suit" + } + ], + "https://github.com/cenzijing/ComfyUI-Markmap": [ + [ + "MarkmapNode", + "ReadHtmlNode" + ], + { + "title_aux": "ComfyUI-Markmap" + } + ], + "https://github.com/cerspense/ComfyUI_cspnodes": [ + [ + "DepthToNormalMap", + "GetMP4Prompt", + "ImageDirIterator", + "IncrementEveryN", + "Modelscopet2v", + "Modelscopev2v", + "RemapRange", + "ResizeByImage", + "SplitImageChannels", + "VidDirIterator" + ], + { + "title_aux": "cspnodes" + } + ], + "https://github.com/ceruleandeep/ComfyUI-LLaVA-Captioner": [ + [ + "LlavaCaptioner" + ], + { + "title_aux": "ComfyUI LLaVA Captioner" + } + ], + "https://github.com/cganimitta/ComfyUI_CGAnimittaTools": [ + [ + "CGA_BlackBorderCrop", + "CGA_BlenderBridge", + "CGA_ColorToGrayscale", + "CGA_ExtractFromList", + "CGA_FrameExtraction\ud83c\udf9e\ufe0f", + "CGA_ListSubfolders", + "CGA_NegativeSelector", + "CGA_TxtReaderNode" + ], + { + "title_aux": "ComfyUI_CGAnimittaTools" + } + ], + "https://github.com/chakib-belgaid/ComfyUI-autosize": [ + [ + "CustomAutoSize", + "SDXLAutoSize" + ], + { + "title_aux": "ComfyUI-autosize" + } + ], + "https://github.com/chakib-belgaid/Comfyui_Prompt_styler": [ + [ + "Prompt_Styler" + ], + { + "title_aux": "ComfyUI Style Plugin" + } + ], + "https://github.com/chandlergis/ComfyUI-IMG_Query": [ + [ + "ImageRequestNode" + ], + { + "title_aux": "ComfyUI-IMG_Query" + } + ], + "https://github.com/chandlergis/ComfyUI_EmojiOverlay": [ + [ + "Image Emoji Overlay" + ], + { + "title_aux": "ComfyUI_EmojiOverlay" + } + ], + "https://github.com/changwook987/ComfyUI-Small-Utility": [ + [ + "Eval", + "RandomEmptyLatent" + ], + { + "title_aux": "ComfyUI-Small-Utility" + } + ], + "https://github.com/chaojie/ComfyUI-AniPortrait": [ + [ + "AniPortraitLoader", + "AniPortraitRun", + "Box2Video", + "CoverVideo", + "MaskList2Video" + ], + { + "title_aux": "ComfyUI-AniPortrait" + } + ], + "https://github.com/chaojie/ComfyUI-CameraCtrl-Wrapper": [ + [ + "CameraBasic", + "CameraCombine", + "CameraCtrlLoader", + "CameraCtrlRun", + "CameraJoin", + "CameraTrajectory" + ], + { + "title_aux": "ComfyUI-CameraCtrl-Wrapper" + } + ], + "https://github.com/chaojie/ComfyUI-Champ": [ + [ + "ChampLoader", + "ChampRun", + "ImageCombineOneColumn", + "ImageCombineOneRow" + ], + { + "title_aux": "ComfyUI-Champ" + } + ], + "https://github.com/chaojie/ComfyUI-DragAnything": [ + [ + "DragAnythingLoader", + "DragAnythingPipelineRun", + "DragAnythingPipelineRunRandom", + "DragAnythingRun", + "DragAnythingRunRandom", + "LoadText", + "SaveText", + "VHS_FILENAMES_STRING" + ], + { + "title_aux": "ComfyUI-DragAnything" + } + ], + "https://github.com/chaojie/ComfyUI-DragNUWA": [ + [ + "BrushMotion", + "CompositeMotionBrush", + "CompositeMotionBrushWithoutModel", + "DragNUWA Run", + "DragNUWA Run MotionBrush", + "Get First Image", + "Get Last Image", + "InstantCameraMotionBrush", + "InstantObjectMotionBrush", + "Load CheckPoint DragNUWA", + "Load MotionBrush From Optical Flow", + "Load MotionBrush From Optical Flow Directory", + "Load MotionBrush From Optical Flow Without Model", + "Load MotionBrush From Tracking Points", + "Load MotionBrush From Tracking Points Without Model", + "Load Pose KeyPoints", + "Loop", + "LoopEnd_IMAGE", + "LoopStart_IMAGE", + "Split Tracking Points" + ], + { + "title_aux": "ComfyUI-DragNUWA" + } + ], + "https://github.com/chaojie/ComfyUI-DynamiCrafter": [ + [ + "DynamiCrafter Simple", + "DynamiCrafterInterp Simple", + "DynamiCrafterInterpLoader", + "DynamiCrafterLoader" + ], + { + "title_aux": "ComfyUI-DynamiCrafter" + } + ], + "https://github.com/chaojie/ComfyUI-EasyAnimate": [ + [ + "EasyAnimateLoader", + "EasyAnimateRun" + ], + { + "title_aux": "ComfyUI-EasyAnimate" + } + ], + "https://github.com/chaojie/ComfyUI-Gemma": [ + [ + "GemmaLoader", + "GemmaRun" + ], + { + "title_aux": "ComfyUI-Gemma" + } + ], + "https://github.com/chaojie/ComfyUI-I2VGEN-XL": [ + [ + "I2VGEN-XL Simple", + "Modelscope Pipeline Loader" + ], + { + "title_aux": "ComfyUI-I2VGEN-XL" + } + ], + "https://github.com/chaojie/ComfyUI-Img2Img-Turbo": [ + [ + "Img2ImgTurboEdgeLoader", + "Img2ImgTurboEdgeRun", + "Img2ImgTurboSketchLoader", + "Img2ImgTurboSketchRun" + ], + { + "title_aux": "ComfyUI-Img2Img-Turbo" + } + ], + "https://github.com/chaojie/ComfyUI-LaVIT": [ + [ + "VHS_FILENAMES_STRING_LaVIT", + "VideoLaVITI2I", + "VideoLaVITI2V", + "VideoLaVITI2VLong", + "VideoLaVITLoader", + "VideoLaVITT2V", + "VideoLaVITT2VLong", + "VideoLaVITUnderstandingImage", + "VideoLaVITUnderstandingLoader", + "VideoLaVITUnderstandingVideo", + "VideoLaVITVideoDetokenizerLoader", + "VideoLaVITVideoReconstruction" + ], + { + "title_aux": "ComfyUI-LaVIT" + } + ], + "https://github.com/chaojie/ComfyUI-LightGlue": [ + [ + "LightGlue Loader", + "LightGlue Simple", + "LightGlue Simple Multi" + ], + { + "title_aux": "ComfyUI-LightGlue" + } + ], + "https://github.com/chaojie/ComfyUI-Moore-AnimateAnyone": [ + [ + "Moore-AnimateAnyone Denoising Unet", + "Moore-AnimateAnyone Image Encoder", + "Moore-AnimateAnyone Pipeline Loader", + "Moore-AnimateAnyone Pose Guider", + "Moore-AnimateAnyone Reference Unet", + "Moore-AnimateAnyone Simple", + "Moore-AnimateAnyone VAE" + ], + { + "title_aux": "ComfyUI-Moore-AnimateAnyone" + } + ], + "https://github.com/chaojie/ComfyUI-Motion-Vector-Extractor": [ + [ + "Motion Vector Extractor", + "VideoCombineThenPath" + ], + { + "title_aux": "ComfyUI-Motion-Vector-Extractor" + } + ], + "https://github.com/chaojie/ComfyUI-MotionCtrl": [ + [ + "Load Motion Camera Preset", + "Load Motion Traj Preset", + "Load Motionctrl Checkpoint", + "Motionctrl Cond", + "Motionctrl Sample", + "Motionctrl Sample Simple", + "Select Image Indices" + ], + { + "title_aux": "ComfyUI-MotionCtrl" + } + ], + "https://github.com/chaojie/ComfyUI-MotionCtrl-SVD": [ + [ + "Load Motionctrl-SVD Camera Preset", + "Load Motionctrl-SVD Checkpoint", + "Motionctrl-SVD Sample Simple" + ], + { + "title_aux": "ComfyUI-MotionCtrl-SVD" + } + ], + "https://github.com/chaojie/ComfyUI-MuseTalk": [ + [ + "MuseTalkCupAudio", + "MuseTalkRun", + "VHS_FILENAMES_STRING_MuseTalk" + ], + { + "title_aux": "ComfyUI-MuseTalk" + } + ], + "https://github.com/chaojie/ComfyUI-MuseV": [ + [ + "MuseVRun", + "MuseVRunVid2Vid", + "VHS_FILENAMES_STRING_MuseV" + ], + { + "author": "infguo", + "title_aux": "ComfyUI-MuseV" + } + ], + "https://github.com/chaojie/ComfyUI-Open-Sora": [ + [ + "OpenSoraLoader", + "OpenSoraRun", + "OpenSoraSampler" + ], + { + "title_aux": "ComfyUI-Open-Sora" + } + ], + "https://github.com/chaojie/ComfyUI-Open-Sora-Plan": [ + [ + "OpenSoraPlanDecode", + "OpenSoraPlanLoader", + "OpenSoraPlanRun", + "OpenSoraPlanSample" + ], + { + "title_aux": "ComfyUI-Open-Sora-Plan" + } + ], + "https://github.com/chaojie/ComfyUI-Panda3d": [ + [ + "Panda3dAmbientLight", + "Panda3dAttachNewNode", + "Panda3dBase", + "Panda3dDirectionalLight", + "Panda3dLoadDepthModel", + "Panda3dLoadModel", + "Panda3dLoadTexture", + "Panda3dModelMerge", + "Panda3dTest", + "Panda3dTextureMerge" + ], + { + "title_aux": "ComfyUI-Panda3d" + } + ], + "https://github.com/chaojie/ComfyUI-Pymunk": [ + [ + "PygameRun", + "PygameSurface", + "PymunkDynamicBox", + "PymunkDynamicCircle", + "PymunkRun", + "PymunkShapeMerge", + "PymunkSpace", + "PymunkStaticLine" + ], + { + "title_aux": "ComfyUI-Pymunk" + } + ], + "https://github.com/chaojie/ComfyUI-RAFT": [ + [ + "Load MotionBrush", + "RAFT Run", + "Save MotionBrush", + "VizMotionBrush" + ], + { + "title_aux": "ComfyUI-RAFT" + } + ], + "https://github.com/chaojie/ComfyUI-SimDA": [ + [ + "SimDALoader", + "SimDARun", + "SimDATrain", + "VHS_FILENAMES_STRING_SimDA" + ], + { + "title_aux": "ComfyUI-SimDA" + } + ], + "https://github.com/chaojie/ComfyUI-Trajectory": [ + [ + "Trajectory_Canvas_Tab" + ], + { + "author": "Lerc", + "description": "This extension provides a full page image editor with mask support. There are two nodes, one to receive images from the editor and one to send images to the editor.", + "nickname": "Canvas Tab", + "title": "Canvas Tab", + "title_aux": "ComfyUI-Trajectory" + } + ], + "https://github.com/chaojie/ComfyUI-Video-Editing-X-Attention": [ + [ + "StringList", + "VEXAGuidance", + "VEXALoader", + "VEXARun" + ], + { + "title_aux": "ComfyUI-Video-Editing-X-Attention" + } + ], + "https://github.com/chaojie/ComfyUI-dust3r": [ + [ + "CameraPoseVideo", + "Dust3rLoader", + "Dust3rRun" + ], + { + "title_aux": "ComfyUI-dust3r" + } + ], + "https://github.com/chaojie/ComfyUI_StreamingT2V": [ + [ + "LoadText_StreamingT2V", + "PromptTravelIndex", + "SaveText_StreamingT2V", + "StreamingT2VLoaderAnimateDiff", + "StreamingT2VLoaderAnimateDiffModel", + "StreamingT2VLoaderEnhanceModel", + "StreamingT2VLoaderModelscopeModel", + "StreamingT2VLoaderModelscopeT2V", + "StreamingT2VLoaderSVD", + "StreamingT2VLoaderSVDModel", + "StreamingT2VLoaderStreamModel", + "StreamingT2VLoaderVidXTendModel", + "StreamingT2VRunEnhanceStep", + "StreamingT2VRunI2V", + "StreamingT2VRunLongStep", + "StreamingT2VRunLongStepVidXTendPipeline", + "StreamingT2VRunLongStepVidXTendPipelineCustomRef", + "StreamingT2VRunLongStepVidXTendPipelineCustomRefOutExtendOnly", + "StreamingT2VRunLongStepVidXTendPipelinePromptTravel", + "StreamingT2VRunShortStepAnimateDiff", + "StreamingT2VRunShortStepModelscopeT2V", + "StreamingT2VRunShortStepSVD", + "StreamingT2VRunT2V", + "VHS_FILENAMES_STRING_StreamingT2V" + ], + { + "title_aux": "ComfyUI_StreamingT2V" + } + ], + "https://github.com/chaosaiart/Chaosaiart-Nodes": [ + [ + "chaosaiart_Any_Switch", + "chaosaiart_Any_Switch_Big_Number", + "chaosaiart_Any_Switch_small", + "chaosaiart_AutoNone_Switch_small", + "chaosaiart_CheckpointLoader", + "chaosaiart_CheckpointPrompt", + "chaosaiart_CheckpointPrompt2", + "chaosaiart_CheckpointPrompt_Frame", + "chaosaiart_CheckpointPrompt_FrameMixer", + "chaosaiart_ControlNetApply", + "chaosaiart_ControlNetApply2", + "chaosaiart_ControlNetApply3", + "chaosaiart_Denoising_Switch", + "chaosaiart_EmptyLatentImage", + "chaosaiart_FramePromptCLIPEncode", + "chaosaiart_Frame_Switch", + "chaosaiart_KSampler1", + "chaosaiart_KSampler2", + "chaosaiart_KSampler3", + "chaosaiart_KSampler4", + "chaosaiart_KSampler5", + "chaosaiart_KSampler7", + "chaosaiart_KSampler_a1", + "chaosaiart_KSampler_a1a", + "chaosaiart_KSampler_a2", + "chaosaiart_KSampler_expert_0", + "chaosaiart_KSampler_expert_1", + "chaosaiart_Ksampler_attribut", + "chaosaiart_Load_Image_Batch", + "chaosaiart_Load_Image_Batch_2img", + "chaosaiart_MainPromptCLIPEncode", + "chaosaiart_Number", + "chaosaiart_Number2", + "chaosaiart_Number_Counter", + "chaosaiart_Number_Switch", + "chaosaiart_Prompt", + "chaosaiart_Prompt_Frame", + "chaosaiart_Prompt_mixer_byFrame", + "chaosaiart_SaveImage", + "chaosaiart_Show_Info", + "chaosaiart_Simple_Prompt", + "chaosaiart_Style_Node", + "chaosaiart_TextCLIPEncode", + "chaosaiart_TextCLIPEncode_lora", + "chaosaiart_adjust_color", + "chaosaiart_any_array2input_1Input", + "chaosaiart_any_array2input_all_big", + "chaosaiart_any_array2input_all_small", + "chaosaiart_any_input2array_big", + "chaosaiart_any_input2array_small", + "chaosaiart_controlnet_weidgth", + "chaosaiart_convert", + "chaosaiart_convert_Prompt", + "chaosaiart_deepseek_fix", + "chaosaiart_forPreview", + "chaosaiart_image_loop", + "chaosaiart_img2gif", + "chaosaiart_img2video", + "chaosaiart_lora", + "chaosaiart_lora_advanced", + "chaosaiart_merge_Folders", + "chaosaiart_oneNode", + "chaosaiart_reloadAny_Load", + "chaosaiart_reloadAny_Save", + "chaosaiart_reloadIMG_Load", + "chaosaiart_reloadIMG_Save", + "chaosaiart_reloadLatent_Load", + "chaosaiart_reloadLatent_Save", + "chaosaiart_restarter", + "chaosaiart_restarter_advanced", + "chaosaiart_video2img1", + "chaosaiart_zoom_frame" + ], + { + "title_aux": "Chaosaiart-Nodes" + } + ], + "https://github.com/charlyad142/ComfyUI_bfl_api_pro_nodes": [ + [ + "BFL Canny Control", + "BFL Depth Control", + "BFL Flux Kontext", + "BFL Flux Ultra", + "BFL Image Expander", + "BFL Image Generator", + "BFL Inpainting" + ], + { + "title_aux": "ComfyUI BFL API Pro Nodes" + } + ], + "https://github.com/chaunceyyann/comfyui-image-processing-nodes": [ + [ + "CharacterLoaderNode", + "ImagePreviewCompare", + "ImageSizeProcessor", + "LoraAndTextCombiner", + "RandomPersonPhoto", + "ToggleLoraStackNode", + "ToggleTextNode", + "VideoThumbnailExtractor", + "YouTubeThumbnailExtractor" + ], + { + "title_aux": "ComfyUI Image Processing Nodes" + } + ], + "https://github.com/checkbins/checkbin-comfy": [ + [ + "Checkbin Get Image Bin", + "Checkbin Get String Bin", + "Checkbin Save Image Bin", + "Checkbin Save String Bin", + "Checkbin Start Run", + "Checkbin Submit Bin" + ], + { + "title_aux": "checkbin-comfy" + } + ], + "https://github.com/chenbaiyujason/ComfyUI_StepFun": [ + [ + "CombineStrings", + "JSONParser", + "StepFunClient", + "TextImageChat", + "VideoChat", + "VideoFileUploader" + ], + { + "title_aux": "ComfyUI-SCStepFun" + } + ], + "https://github.com/chenlongming/ComfyUI_Spectral": [ + [ + "Calculate", + "KMeans", + "LoadEnvi", + "LoadSpectral", + "Plot" + ], + { + "title_aux": "ComfyUI_Spectral" + } + ], + "https://github.com/chenpipi0807/ComfyUI-Index-TTS": [ + [ + "AudioCleanupNode", + "IndexTTSNode", + "IndexTTSProNode", + "NovelTextStructureNode", + "TimbreAudioLoader" + ], + { + "author": "ComfyUI-Index-TTS", + "description": "ComfyUI\u63a5\u53e3\u7684\u5de5\u4e1a\u7ea7\u96f6\u6837\u672c\u6587\u672c\u5230\u8bed\u97f3\u5408\u6210\u7cfb\u7edf", + "title": "IndexTTS for ComfyUI", + "title_aux": "ComfyUI-Index-TTS" + } + ], + "https://github.com/chenpipi0807/ComfyUI_NSFW_Godie": [ + [ + "NSFWFilterNode" + ], + { + "title_aux": "ComfyUI NSFW Filter" + } + ], + "https://github.com/chenpipi0807/PIP_ArtisticWords": [ + [ + "PIP Artistic Text Generator", + "PIP ArtisticWords Fusion", + "PIP ColorPicker", + "PIP SVG Recorder", + "PIP Text Preview", + "PIPAdvancedColorAnalyzer", + "PIPColorPicker", + "PIPColorWheel" + ], + { + "title_aux": "PIP Artistic Words for ComfyUI" + } + ], + "https://github.com/cherninlab/logo-generator-comfyui": [ + [ + "GoogleFontsLogo" + ], + { + "title_aux": "Logo Generator Node for ComfyUI" + } + ], + "https://github.com/chesnokovivan/ComfyUI-Novakid": [ + [ + "Novakid Styler" + ], + { + "title_aux": "ComfyUI-Novakid" + } + ], + "https://github.com/chflame163/ComfyUI_CatVTON_Wrapper": [ + [ + "CatVTONWrapper" + ], + { + "author": "chflame", + "description": "CatVTON warpper for ComfyUI", + "nickname": "CatVTON_Wrapper", + "title": "CatVTON_Wrapper", + "title_aux": "ComfyUI_CatVTON_Wrapper" + } + ], + "https://github.com/chflame163/ComfyUI_CogView4_Wrapper": [ + [ + "CogView4" + ], + { + "title_aux": "ComfyUI_CogView4_Wrapper" + } + ], + "https://github.com/chflame163/ComfyUI_FaceSimilarity": [ + [ + "Face Similarity" + ], + { + "title_aux": "ComfyUI Face Similarity" + } + ], + "https://github.com/chflame163/ComfyUI_Janus_Wrapper": [ + [ + "JanusImage2Text", + "JanusTextToImage", + "LoadJanusModel" + ], + { + "title_aux": "ComfyUI_Janus_Wrapper" + } + ], + "https://github.com/chflame163/ComfyUI_LayerStyle": [ + [ + "LayerColor: AutoAdjust", + "LayerColor: AutoAdjustV2", + "LayerColor: AutoBrightness", + "LayerColor: Brightness & Contrast", + "LayerColor: BrightnessContrastV2", + "LayerColor: Color of Shadow & Highlight", + "LayerColor: ColorAdapter", + "LayerColor: ColorBalance", + "LayerColor: ColorTemperature", + "LayerColor: ColorofShadowHighlightV2", + "LayerColor: Exposure", + "LayerColor: Gamma", + "LayerColor: HSV", + "LayerColor: LAB", + "LayerColor: LUT Apply", + "LayerColor: Levels", + "LayerColor: RGB", + "LayerColor: YUV", + "LayerFilter: AddGrain", + "LayerFilter: ChannelShake", + "LayerFilter: ColorMap", + "LayerFilter: Film", + "LayerFilter: FilmV2", + "LayerFilter: GaussianBlur", + "LayerFilter: GaussianBlurV2", + "LayerFilter: HDREffects", + "LayerFilter: HalfTone", + "LayerFilter: LightLeak", + "LayerFilter: MotionBlur", + "LayerFilter: Sharp & Soft", + "LayerFilter: SkinBeauty", + "LayerFilter: SoftLight", + "LayerFilter: WaterColor", + "LayerMask: BlendIf Mask", + "LayerMask: CreateGradientMask", + "LayerMask: ImageToMask", + "LayerMask: LoadSegformerModel", + "LayerMask: MaskBoxDetect", + "LayerMask: MaskByColor", + "LayerMask: MaskEdgeShrink", + "LayerMask: MaskEdgeUltraDetail", + "LayerMask: MaskEdgeUltraDetail V2", + "LayerMask: MaskGradient", + "LayerMask: MaskGrain", + "LayerMask: MaskGrow", + "LayerMask: MaskInvert", + "LayerMask: MaskMotionBlur", + "LayerMask: MaskPreview", + "LayerMask: MaskStroke", + "LayerMask: PixelSpread", + "LayerMask: RemBgUltra", + "LayerMask: RmBgUltra V2", + "LayerMask: SegformerB2ClothesUltra", + "LayerMask: SegformerClothesPipelineLoader", + "LayerMask: SegformerClothesSetting", + "LayerMask: SegformerFashionPipelineLoader", + "LayerMask: SegformerFashionSetting", + "LayerMask: SegformerUltraV2", + "LayerMask: SegformerUltraV3", + "LayerMask: Shadow & Highlight Mask", + "LayerMask: ShadowHighlightMaskV2", + "LayerStyle: ColorOverlay", + "LayerStyle: ColorOverlay V2", + "LayerStyle: DropShadow", + "LayerStyle: DropShadow V2", + "LayerStyle: DropShadow V3", + "LayerStyle: Gradient Map", + "LayerStyle: GradientOverlay", + "LayerStyle: GradientOverlay V2", + "LayerStyle: InnerGlow", + "LayerStyle: InnerGlow V2", + "LayerStyle: InnerShadow", + "LayerStyle: InnerShadow V2", + "LayerStyle: OuterGlow", + "LayerStyle: OuterGlow V2", + "LayerStyle: Stroke", + "LayerStyle: Stroke V2", + "LayerUtility: AnyRerouter", + "LayerUtility: BatchSelector", + "LayerUtility: Boolean", + "LayerUtility: BooleanOperator", + "LayerUtility: BooleanOperatorV2", + "LayerUtility: CheckMask", + "LayerUtility: CheckMaskV2", + "LayerUtility: ChoiceTextPreset", + "LayerUtility: ColorImage", + "LayerUtility: ColorImage V2", + "LayerUtility: ColorName", + "LayerUtility: ColorPicker", + "LayerUtility: CropBoxResolve", + "LayerUtility: CropByMask", + "LayerUtility: CropByMask V2", + "LayerUtility: CropByMask V3", + "LayerUtility: ExtendCanvas", + "LayerUtility: ExtendCanvasV2", + "LayerUtility: Float", + "LayerUtility: GetImageSize", + "LayerUtility: GetMainColors", + "LayerUtility: GetMainColorsV2", + "LayerUtility: GradientImage", + "LayerUtility: GradientImage V2", + "LayerUtility: GrayValue", + "LayerUtility: HLFrequencyDetailRestore", + "LayerUtility: HSV Value", + "LayerUtility: ICMask", + "LayerUtility: ICMaskCropBack", + "LayerUtility: If", + "LayerUtility: ImageBlend", + "LayerUtility: ImageBlend V2", + "LayerUtility: ImageBlendAdvance", + "LayerUtility: ImageBlendAdvance V2", + "LayerUtility: ImageBlendAdvance V3", + "LayerUtility: ImageChannelMerge", + "LayerUtility: ImageChannelSplit", + "LayerUtility: ImageCombineAlpha", + "LayerUtility: ImageHub", + "LayerUtility: ImageMaskScaleAs", + "LayerUtility: ImageMaskScaleAsV2", + "LayerUtility: ImageOpacity", + "LayerUtility: ImageReel", + "LayerUtility: ImageReelComposit", + "LayerUtility: ImageRemoveAlpha", + "LayerUtility: ImageScaleByAspectRatio", + "LayerUtility: ImageScaleByAspectRatio V2", + "LayerUtility: ImageScaleRestore", + "LayerUtility: ImageScaleRestore V2", + "LayerUtility: ImageShift", + "LayerUtility: ImageTaggerSave", + "LayerUtility: Integer", + "LayerUtility: LayerImageTransform", + "LayerUtility: LayerMaskTransform", + "LayerUtility: LoadVQAModel", + "LayerUtility: NameToColor", + "LayerUtility: NumberCalculator", + "LayerUtility: NumberCalculatorV2", + "LayerUtility: PrintInfo", + "LayerUtility: PurgeVRAM", + "LayerUtility: PurgeVRAM V2", + "LayerUtility: QueueStop", + "LayerUtility: RGB Value", + "LayerUtility: RandomGenerator", + "LayerUtility: RandomGeneratorV2", + "LayerUtility: RestoreCropBox", + "LayerUtility: RoundedRectangle", + "LayerUtility: Seed", + "LayerUtility: SimpleTextImage", + "LayerUtility: String", + "LayerUtility: StringCondition", + "LayerUtility: SwitchCase", + "LayerUtility: TextBox", + "LayerUtility: TextImage", + "LayerUtility: TextImage V2", + "LayerUtility: TextJoin", + "LayerUtility: TextJoinV2", + "LayerUtility: TextPreseter", + "LayerUtility: VQAPrompt", + "LayerUtility: XY to Percent" + ], + { + "author": "chflame", + "description": "A set of nodes for ComfyUI that can composite layer and mask to achieve Photoshop like functionality.", + "nickname": "LayerStyle", + "title": "LayerStyle", + "title_aux": "ComfyUI Layer Style" + } + ], + "https://github.com/chflame163/ComfyUI_LayerStyle_Advance": [ + [ + "LayerMask: BBoxJoin", + "LayerMask: BenUltra", + "LayerMask: BiRefNetUltra", + "LayerMask: BiRefNetUltraV2", + "LayerMask: DrawBBoxMask", + "LayerMask: DrawBBoxMaskV2", + "LayerMask: EVFSAMUltra", + "LayerMask: Florence2Ultra", + "LayerMask: HumanPartsUltra", + "LayerMask: LoadBenModel", + "LayerMask: LoadBiRefNetModel", + "LayerMask: LoadBiRefNetModelV2", + "LayerMask: LoadFlorence2Model", + "LayerMask: LoadSAM2Model", + "LayerMask: LoadSegmentAnythingModels", + "LayerMask: MaskByDifferent", + "LayerMask: MediapipeFacialSegment", + "LayerMask: ObjectDetectorFL2", + "LayerMask: ObjectDetectorGemini", + "LayerMask: ObjectDetectorGeminiV2", + "LayerMask: ObjectDetectorMask", + "LayerMask: ObjectDetectorYOLO8", + "LayerMask: ObjectDetectorYOLOWorld", + "LayerMask: PersonMaskUltra", + "LayerMask: PersonMaskUltra V2", + "LayerMask: SAM2Ultra", + "LayerMask: SAM2UltraV2", + "LayerMask: SAM2VideoUltra", + "LayerMask: SegmentAnythingUltra", + "LayerMask: SegmentAnythingUltra V2", + "LayerMask: SegmentAnythingUltra V3", + "LayerMask: TransparentBackgroundUltra", + "LayerMask: YoloV8Detect", + "LayerUtility: AddBlindWaterMark", + "LayerUtility: Collage", + "LayerUtility: CreateQRCode", + "LayerUtility: DecodeQRCode", + "LayerUtility: DeepSeekAPI", + "LayerUtility: DeepSeekAPIV2", + "LayerUtility: Florence2Image2Prompt", + "LayerUtility: Gemini", + "LayerUtility: GeminiImageEdit", + "LayerUtility: GeminiV2", + "LayerUtility: GetColorTone", + "LayerUtility: GetColorToneV2", + "LayerUtility: ImageAutoCrop", + "LayerUtility: ImageAutoCrop V2", + "LayerUtility: ImageAutoCrop V3", + "LayerUtility: ImageRewardFilter", + "LayerUtility: JoyCaption2", + "LayerUtility: JoyCaption2ExtraOptions", + "LayerUtility: JoyCaption2Split", + "LayerUtility: JoyCaptionBeta1", + "LayerUtility: JoyCaptionBeta1ExtraOptions", + "LayerUtility: LaMa", + "LayerUtility: LlamaVision", + "LayerUtility: LoadJoyCaption2Model", + "LayerUtility: LoadJoyCaptionBeta1Model", + "LayerUtility: LoadPSD", + "LayerUtility: LoadSmolLM2Model", + "LayerUtility: LoadSmolVLMModel", + "LayerUtility: PhiPrompt", + "LayerUtility: PromptEmbellish", + "LayerUtility: PromptTagger", + "LayerUtility: QWenImage2Prompt", + "LayerUtility: SD3NegativeConditioning", + "LayerUtility: SaveImagePlus", + "LayerUtility: SaveImagePlusV2", + "LayerUtility: ShowBlindWaterMark", + "LayerUtility: SmolLM2", + "LayerUtility: SmolVLM", + "LayerUtility: UserPromptGeneratorReplaceWord", + "LayerUtility: UserPromptGeneratorTxt2ImgPrompt", + "LayerUtility: UserPromptGeneratorTxt2ImgPromptWithReference", + "LayerUtility: ZhipuGLM4", + "LayerUtility: ZhipuGLM4V" + ], + { + "author": "chflame", + "description": "A set of nodes for ComfyUI that can composite layer and mask to achieve Photoshop like functionality.", + "nickname": "LayerStyle", + "title": "LayerStyle", + "title_aux": "ComfyUI_LayerStyle_Advance" + } + ], + "https://github.com/chflame163/ComfyUI_MSSpeech_TTS": [ + [ + "Input Trigger", + "MicrosoftSpeech_TTS", + "Play Sound", + "Play Sound (loop)" + ], + { + "title_aux": "ComfyUI_MSSpeech_TTS" + } + ], + "https://github.com/chflame163/ComfyUI_OmniGen_Wrapper": [ + [ + "dzOmniGenWrapper" + ], + { + "title_aux": "ComfyUI_OmniGen_Wrapper" + } + ], + "https://github.com/chflame163/ComfyUI_WordCloud": [ + [ + "ComfyWordCloud", + "LoadTextFile", + "RGB_Picker" + ], + { + "title_aux": "ComfyUI_WordCloud" + } + ], + "https://github.com/chibiace/ComfyUI-Chibi-Nodes": [ + [ + "ConditionText", + "ConditionTextMulti", + "ConditionTextPrompts", + "ImageAddText", + "ImageSimpleResize", + "ImageSizeInfo", + "ImageTool", + "Int2String", + "LoadEmbedding", + "LoadImageExtended", + "Loader", + "Prompts", + "RandomResolutionLatent", + "SaveImages", + "SeedGenerator", + "SimpleSampler", + "TextSplit", + "Textbox", + "Wildcards" + ], + { + "title_aux": "ComfyUI-Chibi-Nodes" + } + ], + "https://github.com/choey/Comfy-Topaz": [ + [ + "TopazPhotoAI", + "TopazSharpenSettings", + "TopazUpscaleSettings" + ], + { + "title_aux": "Comfy-Topaz" + } + ], + "https://github.com/chou18194766xx/comfyui-EncryptSave": [ + [ + "EncryptSaveAES" + ], + { + "title_aux": "comfyui-EncryptSave" + } + ], + "https://github.com/chou18194766xx/comfyui_EncryptPreview": [ + [ + "EncryptPreviewImage" + ], + { + "title_aux": "comfyui_EncryptPreview" + } + ], + "https://github.com/chri002/ComfyUI_depthMapOperation": [ + [ + "CleanPoints (KDTree)", + "CloudPointsInfo", + "CubeLimit", + "Export to PLY", + "ImageToPoints", + "ImageToPoints (Legacy)", + "ImageToPoints (Torch)", + "Import PLY", + "InterpolatePoints (KDTree)", + "PointsToImage (Orthographic)", + "PointsToImage (Projection)", + "PointsToImage advance (DEBUG)", + "PointsToImage advance (Orthographic)", + "PointsToImage advance (Projection)", + "TransformPoints" + ], + { + "title_aux": "ComfyUI_depthMapOperation" + } + ], + "https://github.com/chris-arsenault/ComfyUI-AharaNodes": [ + [ + "FrameSegmenter", + "FrameSegmenterIndexer", + "RepeatSampler", + "RepeatSamplerConfigNode", + "RepeatSamplerConfigPatchLatent", + "RepeatSamplerConfigPatchModel" + ], + { + "title_aux": "ComfyUI-AharaNodes" + } + ], + "https://github.com/chris-the-wiz/EmbeddingsCurveEditor_ComfyUI": [ + [ + "Embeddings Curve Editor" + ], + { + "title_aux": "EmbeddingsCurveEditor_ComfyUI" + } + ], + "https://github.com/chrisfreilich/virtuoso-nodes": [ + [ + "BlackAndWhite", + "BlendIf", + "BlendModes", + "ColorBalance", + "ColorBalanceAdvanced", + "GaussianBlur", + "GaussianBlurDepth", + "HueSat", + "HueSatAdvanced", + "LensBlur", + "LensBlurDepth", + "Levels", + "MergeRGB", + "MotionBlur", + "MotionBlurDepth", + "SelectiveColor", + "SolidColor", + "SolidColorHSV", + "SolidColorRGB", + "SplitRGB" + ], + { + "author": "Chris Freilich", + "description": "This extension provides a \"Levels\" node.", + "nickname": "Virtuoso Pack - Contrast", + "title": "Virtuoso Pack - Contrast", + "title_aux": "Virtuoso Nodes for ComfyUI" + } + ], + "https://github.com/chrisgoringe/cg-image-filter": [ + [ + "Batch from Image List", + "Image Filter", + "Image List From Batch", + "Mask Image Filter", + "Masked Section", + "Pick from List", + "Split String by Commas", + "String to Float", + "String to Int", + "Text Image Filter", + "Text Image Filter with Extras" + ], + { + "author": "chrisgoringe", + "description": "A custom node that pauses the flow while you choose which image or images to pass on to the rest of the workflow. Simplified and improved version of cg-image-picker.", + "nickname": "Image Filter", + "title": "Image Filter", + "title_aux": "Image Filter" + } + ], + "https://github.com/chrisgoringe/cg-noisetools": [ + [ + "Batch Noise Simulate", + "Mix Noise", + "Seperable Batch Noise", + "Shape Noise", + "Split Sigmas with Rewind" + ], + { + "title_aux": "Noise variation and batch noise tools" + } + ], + "https://github.com/chrisgoringe/cg-use-everywhere": [ + [ + "Seed Everywhere" + ], + { + "nodename_pattern": "(^(Prompts|Anything) Everywhere|Simple String)", + "title_aux": "Use Everywhere (UE Nodes)" + } + ], + "https://github.com/chrissy0/chris-comfyui-nodes": [ + [ + "PadImageSquare" + ], + { + "title_aux": "chris-comfyui-nodes" + } + ], + "https://github.com/christian-byrne/audio-separation-nodes-comfyui": [ + [ + "AudioCombine", + "AudioCrop", + "AudioGetTempo", + "AudioSeparation", + "AudioSpeedShift", + "AudioTempoMatch", + "AudioVideoCombine" + ], + { + "title_aux": "audio-separation-nodes-comfyui" + } + ], + "https://github.com/christian-byrne/claude-code-comfyui-nodes": [ + [ + "ClaudeCodeArguments", + "ClaudeCodeContext", + "ClaudeCodeExecute", + "ClaudeCodeMCP", + "ClaudeCodeMemory", + "ClaudeCodeReader", + "ClaudeCodeTools", + "ClaudeRedditScraper" + ], + { + "title_aux": "Claude Code ComfyUI Nodes" + } + ], + "https://github.com/christian-byrne/img2colors-comfyui-node": [ + [ + "bmy_Img2ColorNode" + ], + { + "author": "christian-byrne", + "description": "", + "nickname": "img2color", + "title": "Img2Color Node - Detect and describe color palettes in images", + "title_aux": "Img2color - Extract Colors from Image" + } + ], + "https://github.com/christian-byrne/img2txt-comfyui-nodes": [ + [ + "img2txt BLIP/Llava Multimodel Tagger" + ], + { + "author": "christian-byrne", + "title": "Img2Txt auto captioning", + "title_aux": "img2txt-comfyui-nodes" + } + ], + "https://github.com/christian-byrne/size-match-compositing-nodes": [ + [ + "Composite Alpha Layer", + "Size Match Images/Masks" + ], + { + "title_aux": "Node - Size Matcher" + } + ], + "https://github.com/christian-byrne/youtube-dl-comfyui": [ + [ + "YoutubeDL" + ], + { + "title_aux": "youtube-dl-comfyui" + } + ], + "https://github.com/chuchu114514/comfyui_proportion_solver": [ + [ + "ProportionSolver", + "ProportionSolverAdvanced" + ], + { + "title_aux": "comfyui_proportion_solver" + } + ], + "https://github.com/ciga2011/ComfyUI-MarkItDown": [ + [ + "WIZ_AUDIO2MARKDOWN", + "WIZ_EXCEL2MARKDOWN", + "WIZ_HTML2MARKDOWN", + "WIZ_IMAGE2MARKDOWN", + "WIZ_IPYNB2MARKDOWN", + "WIZ_LLM_CLIENT", + "WIZ_MARKITDOWN", + "WIZ_PDF2MARKDOWN", + "WIZ_POWERPOINT2MARKDOWN", + "WIZ_WORD2MARKDOWN" + ], + { + "title_aux": "ComfyUI MarkItDown" + } + ], + "https://github.com/ciga2011/ComfyUI-Pollinations": [ + [ + "PollinationsNode" + ], + { + "title_aux": "ComfyUI Pollinations" + } + ], + "https://github.com/ciga2011/ComfyUI-PromptOptimizer": [ + [ + "PromptOptimizer" + ], + { + "title_aux": "ComfyUI Prompt Optimizer" + } + ], + "https://github.com/ciri/comfyui-model-downloader": [ + [ + "Auto Model Downloader", + "CivitAI Downloader", + "HF Downloader" + ], + { + "title_aux": "ComfyUI Model Downloader" + } + ], + "https://github.com/city96/ComfyUI-GGUF": [ + [ + "CLIPLoaderGGUF", + "DualCLIPLoaderGGUF", + "QuadrupleCLIPLoaderGGUF", + "TripleCLIPLoaderGGUF", + "UnetLoaderGGUF", + "UnetLoaderGGUFAdvanced" + ], + { + "preemptions": [ + "CLIPLoaderGGUF", + "DualCLIPLoaderGGUF", + "TripleCLIPLoaderGGUF", + "UnetLoaderGGUF", + "UnetLoaderGGUFAdvanced" + ], + "title_aux": "ComfyUI-GGUF" + } + ], + "https://github.com/city96/ComfyUI_ColorMod": [ + [ + "CV2Tonemap", + "CV2TonemapDrago", + "CV2TonemapDurand", + "CV2TonemapMantiuk", + "CV2TonemapReinhard", + "ColorModCompress", + "ColorModEdges", + "ColorModMove", + "ColorModPivot", + "ColorspaceConvert", + "HDRCreate", + "HDRExposureFusion", + "LoadImageHDR", + "LoadImageHighPrec", + "PreviewImageHighPrec", + "SaveImageHDR", + "SaveImageHighPrec" + ], + { + "title_aux": "ComfyUI_ColorMod" + } + ], + "https://github.com/city96/ComfyUI_DiT": [ + [ + "DiTCheckpointLoader", + "DiTCheckpointLoaderSimple", + "DiTLabelCombine", + "DiTLabelSelect", + "DiTSampler" + ], + { + "title_aux": "ComfyUI_DiT [WIP]" + } + ], + "https://github.com/city96/ComfyUI_ExtraModels": [ + [ + "DiTCondLabelEmpty", + "DiTCondLabelSelect", + "DitCheckpointLoader", + "EmptyDCAELatentImage", + "EmptySanaLatentImage", + "ExtraVAELoader", + "GemmaLoader", + "GemmaTextEncode", + "HYDiTCheckpointLoader", + "HYDiTSrcSizeCond", + "HYDiTTextEncode", + "HYDiTTextEncodeSimple", + "HYDiTTextEncoderLoader", + "MiaoBiCLIPLoader", + "MiaoBiDiffusersLoader", + "OverrideCLIPDevice", + "OverrideVAEDevice", + "PixArtCheckpointLoader", + "PixArtCheckpointLoaderSimple", + "PixArtControlNetCond", + "PixArtLoraLoader", + "PixArtResolutionCond", + "PixArtResolutionSelect", + "PixArtT5FromSD3CLIP", + "PixArtT5TextEncode", + "SanaCheckpointLoader", + "SanaResolutionCond", + "SanaResolutionSelect", + "SanaTextEncode", + "T5TextEncode", + "T5v11Loader" + ], + { + "title_aux": "Extra Models for ComfyUI" + } + ], + "https://github.com/city96/ComfyUI_NetDist": [ + [ + "CombineImageBatch", + "FetchRemote", + "LoadCurrentWorkflowJSON", + "LoadDiskWorkflowJSON", + "LoadImageUrl", + "LoadLatentNumpy", + "LoadLatentUrl", + "RemoteChainEnd", + "RemoteChainStart", + "RemoteQueueSimple", + "RemoteQueueWorker", + "SaveDiskWorkflowJSON", + "SaveImageUrl", + "SaveLatentNumpy" + ], + { + "title_aux": "ComfyUI_NetDist" + } + ], + "https://github.com/city96/SD-Latent-Interposer": [ + [ + "LatentInterposer" + ], + { + "title_aux": "Latent-Interposer" + } + ], + "https://github.com/city96/SD-Latent-Upscaler": [ + [ + "LatentUpscaler" + ], + { + "title_aux": "SD-Latent-Upscaler" + } + ], + "https://github.com/civen-cn/ComfyUI-PaddleOcr": [ + [ + "OcrBlur", + "OcrBoxMask", + "OcrImageText" + ], + { + "title_aux": "ComfyUI-PaddleOcr" + } + ], + "https://github.com/civen-cn/ComfyUI-Whisper-Translator": [ + [ + "Add Subtitles To FramesX", + "Apply WhisperX" + ], + { + "title_aux": "ComfyUI Whisper Translator" + } + ], + "https://github.com/civitai/civitai_comfy_nodes": [ + [ + "CivitAI_Checkpoint_Loader", + "CivitAI_Lora_Loader" + ], + { + "title_aux": "Civitai Comfy Nodes" + } + ], + "https://github.com/cjj198909/comfy_openai_image_api_azure": [ + [ + "OpenAI Image API" + ], + { + "title_aux": "OpenAI/Azure OpenAI Image API" + } + ], + "https://github.com/claptrap0/ComfyUI_LLM_Hub": [ + [ + "Generated_Output", + "LLM_Hub", + "LLM_Settings" + ], + { + "title_aux": "ComfyUI_LLM_Hub" + } + ], + "https://github.com/claussteinmassl/ComfyUI-CS-CustomNodes": [ + [ + "CS Transform" + ], + { + "title_aux": "CS Transform Node for ComfyUI" + } + ], + "https://github.com/cleanlii/comfyui-dalle-integration": [ + [ + "DalleImageEdit", + "DalleImageGeneration", + "DalleImageVariation" + ], + { + "title_aux": "DalleImageNodes - OpenAI DALL\u00b7E Nodes for ComfyUI" + } + ], + "https://github.com/clhui/ComfyUi-clh-Tool": [ + [ + "EchartGraph_clh", + "EchartOptionByPath_clh", + "EchartOption_clh", + "INTConstant_clh", + "JavaScript_clh", + "JoinStringMulti_clh", + "MathExpression_clh", + "SetRedis|clh", + "ShowText_clh", + "SomethingToString_clh", + "String2FatLabels_clh", + "String2Image_clh", + "StringConstant_clh" + ], + { + "author": "Dr.Lt.Data", + "description": "This extension offers various detector nodes and detailer nodes that allow you to configure a workflow that automatically enhances facial details. And provide iterative upscaler.", + "nickname": "CLH Simple Tool", + "title": "CLH simple Tool", + "title_aux": "Clh Tool for ComfyUI" + } + ], + "https://github.com/clouddreamfly/ComfyUI-PromptWrapper": [ + [ + "CombinePrompt", + "CustomPrompt", + "DrawStylePrompt", + "GeneratePrompt", + "InputPrompt", + "LightPrompt", + "MultiCombinePrompt", + "MultiReplacePrompt", + "NegativePrompt", + "PortraitCosmeticPrompt", + "PortraitFashionPrompt", + "PortraitPosePrompt", + "PortraitPrompt", + "PortraitSkinPrompt", + "PreviewPrompt", + "PromptTranslation", + "RandomLinePrompt", + "RandomsPrompt", + "RandomsWeightPrompt", + "ReplacePrompt", + "SavePrompt", + "SceneryPrompt" + ], + { + "title_aux": "ComfyUI-PromptWrapper" + } + ], + "https://github.com/cloudkoala/comfyui-koala": [ + [ + "AspectRatioLatentNode", + "SaveImageAnywhere", + "SaveMeshAnywhere" + ], + { + "title_aux": "comfyui-koala" + } + ], + "https://github.com/cluny85/ComfyUI-Scripting-Tools": [ + [ + "EnhancedUUIDGeneratorNode", + "UUIDGeneratorNode" + ], + { + "title_aux": "ComfyUI-Scripting-Tools" + } + ], + "https://github.com/cmdicely/simple_image_to_palette": [ + [ + "Example" + ], + { + "title_aux": "Simple Image To Palette" + } + ], + "https://github.com/cnnmmd/comfyui_xoxxox_cnnmmd": [ + [ + "Xoxxox_CnvDat", + "Xoxxox_CnvSen", + "Xoxxox_CnvTxt", + "Xoxxox_CnvVce", + "Xoxxox_DlyGet", + "Xoxxox_DlySet", + "Xoxxox_GenImg", + "Xoxxox_GenTxt", + "Xoxxox_GetAud", + "Xoxxox_GetDir", + "Xoxxox_GetDis", + "Xoxxox_GetImg", + "Xoxxox_GetMem", + "Xoxxox_GetTxt", + "Xoxxox_IniFlw", + "Xoxxox_LogNum", + "Xoxxox_LogTxt", + "Xoxxox_PutTxt", + "Xoxxox_RcvVce", + "Xoxxox_RepTxt", + "Xoxxox_RunFlw", + "Xoxxox_SenTxt", + "Xoxxox_SetAud", + "Xoxxox_SetDir", + "Xoxxox_SetDis", + "Xoxxox_SetImg", + "Xoxxox_SetMem", + "Xoxxox_SetNil", + "Xoxxox_SetTxt", + "Xoxxox_SndVce", + "Xoxxox_SwtImg", + "Xoxxox_TrnBak" + ], + { + "title_aux": "cnnmmd: comfyui_xoxxox_cnnmmd" + } + ], + "https://github.com/codeprimate/ComfyUI-MaskContourProcessor": [ + [ + "MaskContourProcessor" + ], + { + "title_aux": "ComfyUI Mask Contour Processor" + } + ], + "https://github.com/comfy-deploy/comfyui-llm-toolkit": [ + [ + "AudioDurationFrames", + "BFLProviderNode", + "BlankImage", + "CheckImageEmpty", + "ConfigGenerateImage", + "ConfigGenerateImageBFL", + "ConfigGenerateImageFluxDev", + "ConfigGenerateImageGemini", + "ConfigGenerateImageOpenAI", + "ConfigGenerateImagePortrait", + "ConfigGenerateImageSeedanceEditV3", + "ConfigGenerateMusic", + "ConfigGenerateSpeech", + "ConfigGenerateVideo", + "ConfigGenerateVideoHailuoI2VPro", + "ConfigGenerateVideoHailuoI2VStandard", + "ConfigGenerateVideoHailuoT2VPro", + "ConfigGenerateVideoHailuoT2VStandard", + "ConfigGenerateVideoKlingI2VMaster", + "ConfigGenerateVideoKlingI2VPro", + "ConfigGenerateVideoKlingI2VStandard", + "ConfigGenerateVideoSeedanceProI2V", + "ConfigGenerateVideoSeedanceProT2V", + "ConfigGenerateVideoVeo2I2V", + "ConfigGenerateVideoVeo2T2V", + "ConfigGenerateVideoVeo3", + "ConfigGenerateVideoVeo3Fast", + "Display_Text", + "FramesToSeconds", + "GeminiProviderNode", + "GenerateImage", + "GenerateLyrics", + "GenerateMusic", + "GenerateSpeech", + "GenerateVideo", + "GroqProviderNode", + "JoinStringsMulti", + "LLMToolkitProviderSelector", + "LLMToolkitTextGenerator", + "LLMToolkitTextGeneratorStream", + "LoadAudioFromPath", + "LoadVideoFromPath", + "LocalTransformersProviderNode", + "LocalVLLMProviderNode", + "OpenAIProviderNode", + "PlayRandomSound", + "PreviewImageLogic", + "PreviewOutputs", + "PreviewVideo", + "PromptManager", + "ResolutionSelector", + "StylePromptGenerator", + "SunoProviderSelector", + "SwitchAny", + "SwitchAnyRoute", + "SwitchAnyRoute_wANY", + "SwitchAny_wANY", + "SystemPromptTaskGenerator", + "UpscaleVideo", + "WaveSpeedProviderNode" + ], + { + "author": "ComfyDeploy", + "description": "llm toolkit", + "nickname": "llm_toolkit", + "title": "llm toolkit", + "title_aux": "ComfyUI LLM Toolkit" + } + ], + "https://github.com/comfyanonymous/ComfyUI": [ + [ + "APG", + "AddNoise", + "AlignYourStepsScheduler", + "BasicGuider", + "BasicScheduler", + "BetaSamplingScheduler", + "CFGGuider", + "CFGNorm", + "CFGZeroStar", + "CLIPAttentionMultiply", + "CLIPLoader", + "CLIPMergeAdd", + "CLIPMergeSimple", + "CLIPMergeSubtract", + "CLIPSave", + "CLIPSetLastLayer", + "CLIPTextEncode", + "CLIPTextEncodeControlnet", + "CLIPTextEncodeFlux", + "CLIPTextEncodeHiDream", + "CLIPTextEncodeHunyuanDiT", + "CLIPTextEncodeLumina2", + "CLIPTextEncodePixArtAlpha", + "CLIPTextEncodeSD3", + "CLIPTextEncodeSDXL", + "CLIPTextEncodeSDXLRefiner", + "CLIPVisionEncode", + "CLIPVisionLoader", + "Canny", + "CaseConverter", + "CheckpointLoader", + "CheckpointLoaderSimple", + "CheckpointSave", + "ConditioningAverage", + "ConditioningCombine", + "ConditioningConcat", + "ConditioningSetArea", + "ConditioningSetAreaPercentage", + "ConditioningSetAreaPercentageVideo", + "ConditioningSetAreaStrength", + "ConditioningSetMask", + "ConditioningSetTimestepRange", + "ConditioningStableAudio", + "ConditioningZeroOut", + "ControlNetApply", + "ControlNetApplyAdvanced", + "ControlNetApplySD3", + "ControlNetInpaintingAliMamaApply", + "ControlNetLoader", + "CosmosImageToVideoLatent", + "CosmosPredict2ImageToVideoLatent", + "CreateVideo", + "CropMask", + "DiffControlNetLoader", + "DifferentialDiffusion", + "DiffusersLoader", + "DisableNoise", + "DualCFGGuider", + "DualCLIPLoader", + "EmptyAceStepLatentAudio", + "EmptyCosmosLatentVideo", + "EmptyHunyuanLatentVideo", + "EmptyImage", + "EmptyLTXVLatentVideo", + "EmptyLatentAudio", + "EmptyLatentHunyuan3Dv2", + "EmptyLatentImage", + "EmptyMochiLatentVideo", + "EmptySD3LatentImage", + "ExponentialScheduler", + "ExtendIntermediateSigmas", + "FeatherMask", + "FlipSigmas", + "FluxDisableGuidance", + "FluxGuidance", + "FluxKontextImageScale", + "FluxKontextMaxImageNode", + "FluxKontextProImageNode", + "FluxProCannyNode", + "FluxProDepthNode", + "FluxProExpandNode", + "FluxProFillNode", + "FluxProImageNode", + "FluxProUltraImageNode", + "FreSca", + "FreeU", + "FreeU_V2", + "GITSScheduler", + "GLIGENLoader", + "GLIGENTextBoxApply", + "GeminiInputFiles", + "GeminiNode", + "GetImageSize", + "GetVideoComponents", + "GrowMask", + "Hunyuan3Dv2Conditioning", + "Hunyuan3Dv2ConditioningMultiView", + "HunyuanImageToVideo", + "HyperTile", + "HypernetworkLoader", + "IdeogramV1", + "IdeogramV2", + "IdeogramV3", + "ImageAddNoise", + "ImageBatch", + "ImageBlend", + "ImageBlur", + "ImageColorToMask", + "ImageCompositeMasked", + "ImageCrop", + "ImageFlip", + "ImageFromBatch", + "ImageInvert", + "ImageOnlyCheckpointLoader", + "ImageOnlyCheckpointSave", + "ImagePadForOutpaint", + "ImageQuantize", + "ImageRGBToYUV", + "ImageRotate", + "ImageScale", + "ImageScaleBy", + "ImageScaleToTotalPixels", + "ImageSharpen", + "ImageStitch", + "ImageToMask", + "ImageUpscaleWithModel", + "ImageYUVToRGB", + "InpaintModelConditioning", + "InstructPixToPixConditioning", + "InvertMask", + "JoinImageWithAlpha", + "KSampler", + "KSamplerAdvanced", + "KSamplerSelect", + "KarrasScheduler", + "KlingCameraControlI2VNode", + "KlingCameraControlT2VNode", + "KlingCameraControls", + "KlingDualCharacterVideoEffectNode", + "KlingImage2VideoNode", + "KlingImageGenerationNode", + "KlingLipSyncAudioToVideoNode", + "KlingLipSyncTextToVideoNode", + "KlingSingleImageVideoEffectNode", + "KlingStartEndFrameNode", + "KlingTextToVideoNode", + "KlingVideoExtendNode", + "KlingVirtualTryOnNode", + "LTXVAddGuide", + "LTXVConditioning", + "LTXVCropGuides", + "LTXVImgToVideo", + "LTXVPreprocess", + "LTXVScheduler", + "LaplaceScheduler", + "LatentAdd", + "LatentApplyOperation", + "LatentApplyOperationCFG", + "LatentBatch", + "LatentBatchSeedBehavior", + "LatentBlend", + "LatentComposite", + "LatentCompositeMasked", + "LatentCrop", + "LatentFlip", + "LatentFromBatch", + "LatentInterpolate", + "LatentMultiply", + "LatentOperationSharpen", + "LatentOperationTonemapReinhard", + "LatentRotate", + "LatentSubtract", + "LatentUpscale", + "LatentUpscaleBy", + "Load3D", + "Load3DAnimation", + "LoadAudio", + "LoadImage", + "LoadImageMask", + "LoadImageOutput", + "LoadImageSetFromFolderNode", + "LoadImageTextSetFromFolderNode", + "LoadLatent", + "LoadVideo", + "LoraLoader", + "LoraLoaderModelOnly", + "LoraModelLoader", + "LoraSave", + "LossGraphNode", + "LotusConditioning", + "LumaConceptsNode", + "LumaImageModifyNode", + "LumaImageNode", + "LumaImageToVideoNode", + "LumaReferenceNode", + "LumaVideoNode", + "Mahiro", + "MaskComposite", + "MaskPreview", + "MaskToImage", + "MinimaxImageToVideoNode", + "MinimaxSubjectToVideoNode", + "MinimaxTextToVideoNode", + "ModelComputeDtype", + "ModelMergeAdd", + "ModelMergeAuraflow", + "ModelMergeBlocks", + "ModelMergeCosmos14B", + "ModelMergeCosmos7B", + "ModelMergeCosmosPredict2_14B", + "ModelMergeCosmosPredict2_2B", + "ModelMergeFlux1", + "ModelMergeLTXV", + "ModelMergeMochiPreview", + "ModelMergeSD1", + "ModelMergeSD2", + "ModelMergeSD35_Large", + "ModelMergeSD3_2B", + "ModelMergeSDXL", + "ModelMergeSimple", + "ModelMergeSubtract", + "ModelMergeWAN2_1", + "ModelSamplingAuraFlow", + "ModelSamplingContinuousEDM", + "ModelSamplingContinuousV", + "ModelSamplingDiscrete", + "ModelSamplingFlux", + "ModelSamplingLTXV", + "ModelSamplingSD3", + "ModelSamplingStableCascade", + "ModelSave", + "MoonvalleyImg2VideoNode", + "MoonvalleyTxt2VideoNode", + "MoonvalleyVideo2VideoNode", + "Morphology", + "OpenAIChatConfig", + "OpenAIChatNode", + "OpenAIDalle2", + "OpenAIDalle3", + "OpenAIGPTImage1", + "OpenAIInputFiles", + "OptimalStepsScheduler", + "PatchModelAddDownscale", + "PerpNeg", + "PerpNegGuider", + "PerturbedAttentionGuidance", + "PhotoMakerEncode", + "PhotoMakerLoader", + "PikaImageToVideoNode2_2", + "PikaScenesV2_2", + "PikaStartEndFrameNode2_2", + "PikaTextToVideoNode2_2", + "Pikadditions", + "Pikaffects", + "Pikaswaps", + "PixverseImageToVideoNode", + "PixverseTemplateNode", + "PixverseTextToVideoNode", + "PixverseTransitionVideoNode", + "PolyexponentialScheduler", + "PorterDuffImageComposite", + "Preview3D", + "Preview3DAnimation", + "PreviewAny", + "PreviewAudio", + "PreviewImage", + "PrimitiveBoolean", + "PrimitiveFloat", + "PrimitiveInt", + "PrimitiveString", + "PrimitiveStringMultiline", + "QuadrupleCLIPLoader", + "RandomNoise", + "RebatchImages", + "RebatchLatents", + "RecraftColorRGB", + "RecraftControls", + "RecraftCreativeUpscaleNode", + "RecraftCrispUpscaleNode", + "RecraftImageInpaintingNode", + "RecraftImageToImageNode", + "RecraftRemoveBackgroundNode", + "RecraftReplaceBackgroundNode", + "RecraftStyleV3DigitalIllustration", + "RecraftStyleV3InfiniteStyleLibrary", + "RecraftStyleV3LogoRaster", + "RecraftStyleV3RealisticImage", + "RecraftTextToImageNode", + "RecraftTextToVectorNode", + "RecraftVectorizeImageNode", + "ReferenceLatent", + "RegexExtract", + "RegexMatch", + "RegexReplace", + "RenormCFG", + "RepeatImageBatch", + "RepeatLatentBatch", + "RescaleCFG", + "ResizeAndPadImage", + "Rodin3D_Detail", + "Rodin3D_Regular", + "Rodin3D_Sketch", + "Rodin3D_Smooth", + "RunwayFirstLastFrameNode", + "RunwayImageToVideoNodeGen3a", + "RunwayImageToVideoNodeGen4", + "RunwayTextToImageNode", + "SDTurboScheduler", + "SD_4XUpscale_Conditioning", + "SV3D_Conditioning", + "SVD_img2vid_Conditioning", + "SamplerCustom", + "SamplerCustomAdvanced", + "SamplerDPMAdaptative", + "SamplerDPMPP_2M_SDE", + "SamplerDPMPP_2S_Ancestral", + "SamplerDPMPP_3M_SDE", + "SamplerDPMPP_SDE", + "SamplerER_SDE", + "SamplerEulerAncestral", + "SamplerEulerAncestralCFGPP", + "SamplerEulerCFGpp", + "SamplerLCMUpscale", + "SamplerLMS", + "SamplerSASolver", + "SamplingPercentToSigma", + "SaveAnimatedPNG", + "SaveAnimatedWEBP", + "SaveAudio", + "SaveAudioMP3", + "SaveAudioOpus", + "SaveGLB", + "SaveImage", + "SaveImageWebsocket", + "SaveLatent", + "SaveLoRANode", + "SaveSVGNode", + "SaveVideo", + "SaveWEBM", + "SelfAttentionGuidance", + "SetFirstSigma", + "SetLatentNoiseMask", + "SetUnionControlNetType", + "SkipLayerGuidanceDiT", + "SkipLayerGuidanceDiTSimple", + "SkipLayerGuidanceSD3", + "SolidMask", + "SplitImageWithAlpha", + "SplitSigmas", + "SplitSigmasDenoise", + "StabilityStableImageSD_3_5Node", + "StabilityStableImageUltraNode", + "StabilityUpscaleConservativeNode", + "StabilityUpscaleCreativeNode", + "StabilityUpscaleFastNode", + "StableCascade_EmptyLatentImage", + "StableCascade_StageB_Conditioning", + "StableCascade_StageC_VAEEncode", + "StableCascade_SuperResolutionControlnet", + "StableZero123_Conditioning", + "StableZero123_Conditioning_Batched", + "StringCompare", + "StringConcatenate", + "StringContains", + "StringLength", + "StringReplace", + "StringSubstring", + "StringTrim", + "StubConstantImage", + "StubFloat", + "StubImage", + "StubInt", + "StubMask", + "StyleModelApply", + "StyleModelLoader", + "T5TokenizerOptions", + "TCFG", + "TestAccumulateNode", + "TestAccumulationGetItemNode", + "TestAccumulationGetLengthNode", + "TestAccumulationHeadNode", + "TestAccumulationSetItemNode", + "TestAccumulationTailNode", + "TestAccumulationToListNode", + "TestAsyncBatchProcessing", + "TestAsyncConcurrentLimit", + "TestAsyncError", + "TestAsyncLazyCheck", + "TestAsyncProgressUpdate", + "TestAsyncResourceUser", + "TestAsyncTimeout", + "TestAsyncValidation", + "TestAsyncValidationError", + "TestBoolOperationNode", + "TestCustomIsChanged", + "TestCustomValidation1", + "TestCustomValidation2", + "TestCustomValidation3", + "TestCustomValidation4", + "TestCustomValidation5", + "TestDynamicAsyncGeneration", + "TestDynamicDependencyCycle", + "TestExecutionBlocker", + "TestFloatConditions", + "TestForLoopClose", + "TestForLoopOpen", + "TestIntConditions", + "TestIntMathOperation", + "TestIsChangedWithConstants", + "TestLazyMixImages", + "TestListToAccumulationNode", + "TestMakeListNode", + "TestMixedExpansionReturns", + "TestOutputNodeWithSocketOutput", + "TestParallelSleep", + "TestSamplingInExpansion", + "TestSleep", + "TestStringConditions", + "TestSyncError", + "TestSyncProgressUpdate", + "TestToBoolNode", + "TestVariadicAverage", + "TestWhileLoopClose", + "TestWhileLoopOpen", + "TextEncodeAceStepAudio", + "TextEncodeHunyuanVideo_ImageToVideo", + "ThresholdMask", + "TomePatchModel", + "TorchCompileModel", + "TrainLoraNode", + "TrimVideoLatent", + "TripleCLIPLoader", + "TripoConversionNode", + "TripoImageToModelNode", + "TripoMultiviewToModelNode", + "TripoRefineNode", + "TripoRetargetNode", + "TripoRigNode", + "TripoTextToModelNode", + "TripoTextureNode", + "UNETLoader", + "UNetCrossAttentionMultiply", + "UNetSelfAttentionMultiply", + "UNetTemporalAttentionMultiply", + "UpscaleModelLoader", + "VAEDecode", + "VAEDecodeAudio", + "VAEDecodeHunyuan3D", + "VAEDecodeTiled", + "VAEEncode", + "VAEEncodeAudio", + "VAEEncodeForInpaint", + "VAEEncodeTiled", + "VAELoader", + "VAESave", + "VPScheduler", + "VeoVideoGenerationNode", + "VideoLinearCFGGuidance", + "VideoTriangleCFGGuidance", + "VoxelToMesh", + "VoxelToMeshBasic", + "Wan22ImageToVideoLatent", + "WanCameraEmbedding", + "WanCameraImageToVideo", + "WanFirstLastFrameToVideo", + "WanFunControlToVideo", + "WanFunInpaintToVideo", + "WanImageToVideo", + "WanPhantomSubjectToVideo", + "WanTrackToVideo", + "WanVaceToVideo", + "WebcamCapture", + "unCLIPCheckpointLoader", + "unCLIPConditioning" + ], + { + "title_aux": "ComfyUI" + } + ], + "https://github.com/comfyanonymous/ComfyUI_TensorRT": [ + [ + "DYNAMIC_TRT_MODEL_CONVERSION", + "STATIC_TRT_MODEL_CONVERSION", + "TensorRTLoader" + ], + { + "title_aux": "TensorRT Node for ComfyUI" + } + ], + "https://github.com/comfyanonymous/ComfyUI_experiments": [ + [ + "ModelMergeBlockNumber", + "ModelMergeSDXL", + "ModelMergeSDXLDetailedTransformers", + "ModelMergeSDXLTransformers", + "ModelSamplerTonemapNoiseTest", + "ReferenceOnlySimple", + "RescaleClassifierFreeGuidanceTest", + "TonemapNoiseWithRescaleCFG" + ], + { + "title_aux": "ComfyUI_experiments" + } + ], + "https://github.com/comfyuistudio/ComfyUI-Studio-nodes": [ + [ + "AspectRatioImageSize", + "AspectRatioResizeImage", + "MarkdownModelNote" + ], + { + "title_aux": "ComfyUI-Studio-nodes" + } + ], + "https://github.com/comnote-max/builmenlabo": [ + [ + "GeminiPoseAnalyzer", + "LlamaCppAIO", + "LlamaCppCompleteUnload", + "LlamaCppGenerate", + "LlamaCppLoader", + "LlamaCppMemoryInfo", + "LlamaCppSafeUnload", + "LlamaCppUnload", + "MultiControlNetLoader", + "PromptTranslator" + ], + { + "nodename_pattern": "builmenlabo", + "title_aux": "ComfyUI builmenlabo - Unified Package" + } + ], + "https://github.com/concarne000/ComfyUI-Stacker": [ + [ + "StackPopFloat", + "StackPopImage", + "StackPopInt", + "StackPopObject", + "StackPopString", + "StackPushFloat", + "StackPushImage", + "StackPushInt", + "StackPushObject", + "StackPushString" + ], + { + "title_aux": "ComfyUI-Stacker" + } + ], + "https://github.com/concarne000/ConCarneNode": [ + [ + "BingImageGrabber", + "Hermes", + "Zephyr" + ], + { + "title_aux": "ConCarneNode" + } + ], + "https://github.com/conquestace/ComfyUI-ImageUploader": [ + [ + "ImageUploader" + ], + { + "author": "ConquestAce", + "description": "Upload to temporary websites with API.", + "nickname": "Image Uploader", + "title": "Image Uploader", + "title_aux": "Image Uploader" + } + ], + "https://github.com/coreyryanhanson/ComfyQR": [ + [ + "comfy-qr-by-image-size", + "comfy-qr-by-module-size", + "comfy-qr-by-module-split", + "comfy-qr-mask_errors" + ], + { + "title_aux": "ComfyQR" + } + ], + "https://github.com/coreyryanhanson/ComfyQR-scanning-nodes": [ + [ + "comfy-qr-read", + "comfy-qr-validate" + ], + { + "title_aux": "ComfyQR-scanning-nodes" + } + ], + "https://github.com/coulterj/comfyui-svg-visual-normalize": [ + [ + "SVGVisualBoundsNormalize" + ], + { + "title_aux": "ComfyUI SVG Visual Normalize & Margin Node" + } + ], + "https://github.com/cozy-comfyui/cozy_comm": [ + [ + "CozyDiscordPost" + ], + { + "nodename_pattern": " \\(cozy\\)", + "title_aux": "Cozy Communication" + } + ], + "https://github.com/cozymantis/cozy-utils-comfyui-nodes": [ + [ + "Cozy Sampler Options" + ], + { + "title_aux": "Cozy Utils" + } + ], + "https://github.com/cozymantis/human-parser-comfyui-node": [ + [ + "Cozy Human Parser ATR", + "Cozy Human Parser LIP", + "Cozy Human Parser Pascal" + ], + { + "title_aux": "Cozy Human Parser" + } + ], + "https://github.com/cozymantis/pose-generator-comfyui-node": [ + [ + "Cozy Pose Body Reference", + "Cozy Pose Face Reference" + ], + { + "title_aux": "Cozy Reference Pose Generator" + } + ], + "https://github.com/cr7Por/ComfyUI_DepthFlow": [ + [ + "DepthFlowSimple" + ], + { + "title_aux": "ComfyUI_DepthFlow" + } + ], + "https://github.com/craig-tanaka/comfyui_animeseg": [ + [ + "AdvancedAnimeSeg", + "SimpleAnimeSeg" + ], + { + "title_aux": "ComfyUI Anime Segmentation Nodes v1.1.0" + } + ], + "https://github.com/crave33/RenesStuffDanbooruTagGet": [ + [ + "DanbooruTagFetcher" + ], + { + "title_aux": "RenesStuffDanboruTagGet" + } + ], + "https://github.com/crystian/ComfyUI-Crystools": [ + [], + { + "author": "Crystian", + "description": "Plugins for multiples uses, mainly for debugging, you need them! IG: https://www.instagram.com/crystian.ia", + "nickname": "Crystools", + "nodename_pattern": " \\[Crystools\\]$", + "title": "Crystools", + "title_aux": "Crystools" + } + ], + "https://github.com/cuban044/ComfyUI-Veo3-Experimental": [ + [ + "Veo3TextToVideo", + "Veo3ToVHS", + "Veo3VideoPreview" + ], + { + "title_aux": "[Unofficial] ComfyUI-Veo3-Experimental" + } + ], + "https://github.com/cubiq/Block_Patcher_ComfyUI": [ + [ + "FluxBlockPatcherSampler", + "FluxBlockShareKV", + "PlotBlockParams" + ], + { + "title_aux": "Flux blocks patcher sampler" + } + ], + "https://github.com/cubiq/ComfyUI_FaceAnalysis": [ + [ + "FaceAlign", + "FaceAnalysisModels", + "FaceBoundingBox", + "FaceEmbedDistance", + "FaceSegmentation", + "FaceWarp" + ], + { + "title_aux": "Face Analysis for ComfyUI" + } + ], + "https://github.com/cubiq/ComfyUI_IPAdapter_plus": [ + [ + "IPAAdapterFaceIDBatch", + "IPAdapter", + "IPAdapterAdvanced", + "IPAdapterBatch", + "IPAdapterClipVisionEnhancer", + "IPAdapterClipVisionEnhancerBatch", + "IPAdapterCombineEmbeds", + "IPAdapterCombineParams", + "IPAdapterCombineWeights", + "IPAdapterEmbeds", + "IPAdapterEmbedsBatch", + "IPAdapterEncoder", + "IPAdapterFaceID", + "IPAdapterFaceIDKolors", + "IPAdapterFromParams", + "IPAdapterInsightFaceLoader", + "IPAdapterLoadEmbeds", + "IPAdapterMS", + "IPAdapterModelLoader", + "IPAdapterNoise", + "IPAdapterPreciseComposition", + "IPAdapterPreciseCompositionBatch", + "IPAdapterPreciseStyleTransfer", + "IPAdapterPreciseStyleTransferBatch", + "IPAdapterPromptScheduleFromWeightsStrategy", + "IPAdapterRegionalConditioning", + "IPAdapterSaveEmbeds", + "IPAdapterStyleComposition", + "IPAdapterStyleCompositionBatch", + "IPAdapterTiled", + "IPAdapterTiledBatch", + "IPAdapterUnifiedLoader", + "IPAdapterUnifiedLoaderCommunity", + "IPAdapterUnifiedLoaderFaceID", + "IPAdapterWeights", + "IPAdapterWeightsFromStrategy", + "PrepImageForClipVision" + ], + { + "preemptions": [ + "IPAAdapterFaceIDBatch", + "IPAdapter", + "IPAdapterAdvanced", + "IPAdapterBatch", + "IPAdapterClipVisionEnhancer", + "IPAdapterClipVisionEnhancerBatch", + "IPAdapterCombineEmbeds", + "IPAdapterCombineParams", + "IPAdapterCombineWeights", + "IPAdapterEmbeds", + "IPAdapterEmbedsBatch", + "IPAdapterEncoder", + "IPAdapterFaceID", + "IPAdapterFromParams", + "IPAdapterInsightFaceLoader", + "IPAdapterLoadEmbeds", + "IPAdapterMS", + "IPAdapterModelLoader", + "IPAdapterNoise", + "IPAdapterPreciseComposition", + "IPAdapterPreciseCompositionBatch", + "IPAdapterPreciseStyleTransfer", + "IPAdapterPreciseStyleTransferBatch", + "IPAdapterPromptScheduleFromWeightsStrategy", + "IPAdapterRegionalConditioning", + "IPAdapterSaveEmbeds", + "IPAdapterStyleComposition", + "IPAdapterStyleCompositionBatch", + "IPAdapterTiled", + "IPAdapterTiledBatch", + "IPAdapterUnifiedLoader", + "IPAdapterUnifiedLoaderCommunity", + "IPAdapterUnifiedLoaderFaceID", + "IPAdapterWeights", + "IPAdapterWeightsFromStrategy", + "PrepImageForClipVision" + ], + "title_aux": "ComfyUI_IPAdapter_plus" + } + ], + "https://github.com/cubiq/ComfyUI_InstantID": [ + [ + "ApplyInstantID", + "ApplyInstantIDAdvanced", + "ApplyInstantIDControlNet", + "FaceKeypointsPreprocessor", + "InstantIDAttentionPatch", + "InstantIDFaceAnalysis", + "InstantIDModelLoader" + ], + { + "title_aux": "ComfyUI InstantID (Native Support)" + } + ], + "https://github.com/cubiq/ComfyUI_essentials": [ + [ + "ApplyCLIPSeg+", + "BatchCount+", + "CLIPTextEncodeSDXL+", + "ConditioningCombineMultiple+", + "ConsoleDebug+", + "DebugTensorShape+", + "DisplayAny", + "DrawText+", + "ExtractKeyframes+", + "FluxAttentionSeeker+", + "FluxBlocksBuster+", + "FluxSamplerParams+", + "GetImageSize+", + "GuidanceTimestepping+", + "ImageApplyLUT+", + "ImageBatchMultiple+", + "ImageBatchToList+", + "ImageCASharpening+", + "ImageColorMatch+", + "ImageColorMatchAdobe+", + "ImageComposite+", + "ImageCompositeFromMaskBatch+", + "ImageCrop+", + "ImageDesaturate+", + "ImageEnhanceDifference+", + "ImageExpandBatch+", + "ImageFlip+", + "ImageFromBatch+", + "ImageHistogramMatch+", + "ImageListToBatch+", + "ImagePosterize+", + "ImagePreviewFromLatent+", + "ImageRandomTransform+", + "ImageRemoveAlpha+", + "ImageRemoveBackground+", + "ImageResize+", + "ImageSeamCarving+", + "ImageSmartSharpen+", + "ImageTile+", + "ImageToDevice+", + "ImageUntile+", + "InjectLatentNoise+", + "KSamplerVariationsStochastic+", + "KSamplerVariationsWithNoise+", + "LoadCLIPSegModels+", + "LorasForFluxParams+", + "MaskBatch+", + "MaskBlur+", + "MaskBoundingBox+", + "MaskExpandBatch+", + "MaskFix+", + "MaskFlip+", + "MaskFromBatch+", + "MaskFromColor+", + "MaskFromList+", + "MaskFromRGBCMYBW+", + "MaskFromSegmentation+", + "MaskPreview+", + "MaskSmooth+", + "ModelCompile+", + "ModelSamplingSD3Advanced+", + "NoiseFromImage+", + "PixelOEPixelize+", + "PlotParameters+", + "RemBGSession+", + "RemoveLatentMask+", + "SD3AttentionSeekerLG+", + "SD3AttentionSeekerT5+", + "SD3NegativeConditioning+", + "SDXLEmptyLatentSizePicker+", + "SamplerSelectHelper+", + "SchedulerSelectHelper+", + "SimpleComparison+", + "SimpleCondition+", + "SimpleMath+", + "SimpleMathBoolean+", + "SimpleMathCondition+", + "SimpleMathDual+", + "SimpleMathFloat+", + "SimpleMathInt+", + "SimpleMathPercent+", + "SimpleMathSlider+", + "SimpleMathSliderLowRes+", + "TextEncodeForSamplerParams+", + "TransitionMask+", + "TransparentBGSession+" + ], + { + "title_aux": "ComfyUI Essentials" + } + ], + "https://github.com/cubiq/PuLID_ComfyUI": [ + [ + "ApplyPulid", + "ApplyPulidAdvanced", + "PulidEvaClipLoader", + "PulidInsightFaceLoader", + "PulidModelLoader" + ], + { + "title_aux": "PuLID_ComfyUI" + } + ], + "https://github.com/cuongloveit/comfy_http_request": [ + [ + "Send Http Request", + "Send Http request" + ], + { + "title_aux": "comfy_http_request" + } + ], + "https://github.com/curiousjp/ComfyUI-MaskBatchPermutations": [ + [ + "CombinatorialDetailer", + "FlattenAgainstOriginal", + "PermuteMaskBatch" + ], + { + "title_aux": "ComfyUI-MaskBatchPermutations" + } + ], + "https://github.com/cyberhirsch/seb_nodes": [ + [ + "AspectRatioSeb", + "DepthInpaintSeb", + "SaveImageSeb", + "SwitchMasksSeb", + "SwitchSeb", + "UnifiedPrompterSeb" + ], + { + "title_aux": "Seb Nodes" + } + ], + "https://github.com/czcz1024/Comfyui-FaceCompare": [ + [ + "FaceCompare" + ], + { + "author": "czcz1024", + "description": "Face Compare", + "nickname": "Face Compare", + "title": "Face Compare", + "title_aux": "Face Compare" + } + ], + "https://github.com/da2el-ai/ComfyUI-d2-send-eagle": [ + [ + "D2 Send Eagle" + ], + { + "author": "da2el", + "description": "Send images to Eagle, an image management application", + "title": "D2 Send Eagle", + "title_aux": "D2 Send Eagle" + } + ], + "https://github.com/da2el-ai/ComfyUI-d2-size-selector": [ + [ + "D2_SizeSelector" + ], + { + "author": "da2el", + "description": "Easy select image size", + "title": "D2 Size Selector", + "title_aux": "D2 Size Selector" + } + ], + "https://github.com/da2el-ai/ComfyUI-d2-steps": [ + [ + "D2 Refiner Steps", + "D2 Refiner Steps A1111", + "D2 Refiner Steps Tester" + ], + { + "author": "da2el", + "description": "Calculate the steps for the refiner", + "title": "D2 Steps", + "title_aux": "D2 Steps" + } + ], + "https://github.com/da2el-ai/ComfyUI-d2-xyplot-utils": [ + [ + "D2 Checkpoint List", + "D2 Checkpoint Loader", + "D2 Multi Output", + "D2 Prompt SR", + "D2 Regex Switcher" + ], + { + "author": "da2el", + "description": "A parameter output node compatible with qq-nodes-comfyui. It outputs parameters such as Prompt S/R and seed.", + "title": "D2 XYPlot Utils", + "title_aux": "D2 XYPlot Utils" + } + ], + "https://github.com/da2el-ai/D2-SavePSD-ComfyUI": [ + [ + "D2 Apply Alpha Channel", + "D2 Extract Alpha", + "D2 Save PSD" + ], + { + "author": "da2el", + "description": "", + "title": "D2 Save PSD", + "title_aux": "D2-SavePSD-ComfyUI" + } + ], + "https://github.com/da2el-ai/D2-nodes-ComfyUI": [ + [ + "D2 Checkpoint Loader", + "D2 Controlnet Loader", + "D2 Cut By Mask", + "D2 EmptyImage Alpha", + "D2 Filename Template", + "D2 Filename Template2", + "D2 Folder Image Queue", + "D2 Get Image Size", + "D2 Grid Image", + "D2 Image Mask Stack", + "D2 Image Resize", + "D2 Image Stack", + "D2 KSampler", + "D2 KSampler(Advanced)", + "D2 List To String", + "D2 Load Folder Images", + "D2 Load Image", + "D2 Load Lora", + "D2 Model and CLIP Merge SDXL", + "D2 Mosaic Filter", + "D2 Multi Output", + "D2 Paste By Mask", + "D2 Pipe", + "D2 Preview Image", + "D2 Prompt", + "D2 Refiner Steps", + "D2 Refiner Steps A1111", + "D2 Refiner Steps Tester", + "D2 Regex Replace", + "D2 Regex Switcher", + "D2 Resize Calculator", + "D2 Save Image", + "D2 Size Slector", + "D2 Token Counter", + "D2 XY Annotation", + "D2 XY Folder Images", + "D2 XY Grid Image", + "D2 XY List To Plot", + "D2 XY Model List", + "D2 XY Plot", + "D2 XY Plot Easy", + "D2 XY Plot Easy Mini", + "D2 XY Prompt SR", + "D2 XY Prompt SR2", + "D2 XY Seed", + "D2 XY Seed2", + "D2 XY String To Plot", + "D2 XY Upload Image" + ], + { + "author": "da2el", + "description": "A Collection of Handy Custom Nodes for ComfyUI", + "title": "D2 Nodes", + "title_aux": "D2 Nodes ComfyUI" + } + ], + "https://github.com/dadoirie/ComfyUI_Dados_Nodes": [ + [ + "DN_JoyTaggerNode", + "DN_MiaoshouAITaggerNode", + "DN_MultilineString", + "DN_SmolVLMNode", + "DN_TextConcatenateNode", + "DN_TextDropDownNode", + "DN_WildcardPromptEditorNode", + "DN_WildcardsProcessor", + "PinterestFetch", + "inactivePinterestImageNode" + ], + { + "author": "Dado", + "description": "Node with dynamic text inputs for concatenation", + "title": "Text Concatenator", + "title_aux": "ComfyUI_Dados_Nodes" + } + ], + "https://github.com/dafeng012/comfyui-imgmake": [ + [ + "LoadImageListPlus", + "LoadImagesFromPath_lp", + "SaveImage_lp", + "SelectImageName", + "VideoKeyFramesExtractor", + "ebsynth_hecheng", + "ebsynth_main", + "ebsynth_process", + "image2mask", + "video2image" + ], + { + "title_aux": "comfyui-imgmake" + } + ], + "https://github.com/dagthomas/comfyui_dagthomas": [ + [ + "APNLatent", + "CustomPromptLoader", + "DynamicStringCombinerNode", + "FileReaderNode", + "FlexibleStringMergerNode", + "GPT4MiniNode", + "GPT4VisionNode", + "GeminiCustomVision", + "GeminiTextOnly", + "Gpt4CustomVision", + "Gpt4VisionCloner", + "OllamaNode", + "OllamaVisionNode", + "PGSD3LatentGenerator", + "PhiCustomModelInference", + "PhiModelInference", + "PhiModelLoader", + "PromptGenerator", + "RandomIntegerNode", + "SentenceMixerNode", + "StringMergerNode" + ], + { + "title_aux": "SDXL Auto Prompter" + } + ], + "https://github.com/danTheMonk/comfyui-int-and-float": [ + [ + "FloatToInt", + "IntToFloat" + ], + { + "title_aux": "ComfyUI Int and Float Conversion Nodes" + } + ], + "https://github.com/danger-electrodes/ComfyUI_Fawfluencer_Nodes": [ + [ + "FawfaceModelSpreadsheetRealismNode", + "FawfakeAuthenticImageSaveNode", + "FawfluxencerNode", + "FawfulizedAddImagesToImageList", + "FawfulizedEmptyImageList", + "FawfulizedHunyuanAddNoise", + "FawfulizedHunyuanBasicGuider", + "FawfulizedHunyuanBasicScheduler", + "FawfulizedHunyuanBetaSamplingScheduler", + "FawfulizedHunyuanCFGGuider", + "FawfulizedHunyuanControlNetApply", + "FawfulizedHunyuanControlNetApplyAdvanced", + "FawfulizedHunyuanControlNetLoader", + "FawfulizedHunyuanDiffControlNetLoader", + "FawfulizedHunyuanDisableNoise", + "FawfulizedHunyuanDualCFGGuider", + "FawfulizedHunyuanExponentialScheduler", + "FawfulizedHunyuanFlipSigmas", + "FawfulizedHunyuanKSamplerSelect", + "FawfulizedHunyuanKarrasScheduler", + "FawfulizedHunyuanLaplaceScheduler", + "FawfulizedHunyuanLatentVideo", + "FawfulizedHunyuanPolyexponentialScheduler", + "FawfulizedHunyuanRandomNoise", + "FawfulizedHunyuanSDTurboScheduler", + "FawfulizedHunyuanSamplerCustom", + "FawfulizedHunyuanSamplerCustomAdvanced", + "FawfulizedHunyuanSamplerDPMAdaptative", + "FawfulizedHunyuanSamplerDPMPP_2M_SDE", + "FawfulizedHunyuanSamplerDPMPP_2S_Ancestral", + "FawfulizedHunyuanSamplerDPMPP_3M_SDE", + "FawfulizedHunyuanSamplerDPMPP_SDE", + "FawfulizedHunyuanSamplerEulerAncestral", + "FawfulizedHunyuanSamplerEulerAncestralCFGPP", + "FawfulizedHunyuanSamplerLMS", + "FawfulizedHunyuanSetFirstSigma", + "FawfulizedHunyuanSetLatentNoiseMask", + "FawfulizedHunyuanSplitSigmas", + "FawfulizedHunyuanSplitSigmasDenoise", + "FawfulizedHunyuanVPScheduler", + "Img2ImgFawfluencerNodeSDXL" + ], + { + "title_aux": "ComfyUI_Fawfluencer_Nodes" + } + ], + "https://github.com/daniabib/ComfyUI_ProPainter_Nodes": [ + [ + "ProPainterInpaint", + "ProPainterOutpaint" + ], + { + "title_aux": "ComfyUI ProPainter Nodes" + } + ], + "https://github.com/daniel-lewis-ab/ComfyUI-Llama": [ + [ + "Call LLM Advanced", + "Call LLM Basic", + "LLM_Create_Completion Advanced", + "LLM_Detokenize", + "LLM_Embed", + "LLM_Eval", + "LLM_Load_State", + "LLM_Reset", + "LLM_Sample", + "LLM_Save_State", + "LLM_Token_BOS", + "LLM_Token_EOS", + "LLM_Tokenize", + "Load LLM Model Advanced", + "Load LLM Model Basic" + ], + { + "title_aux": "ComfyUI-Llama" + } + ], + "https://github.com/daniel-lewis-ab/ComfyUI-TTS": [ + [ + "Load_Piper_Model", + "Piper_Speak_Text" + ], + { + "title_aux": "ComfyUI-TTS" + } + ], + "https://github.com/darkpixel/darkprompts": [ + [ + "DarkAnyToString", + "DarkCheckpointRandomizer", + "DarkCheckpointSwitcher", + "DarkCombine", + "DarkFaceIndexGenerator", + "DarkFaceIndexShuffle", + "DarkFolders", + "DarkLoRALoader", + "DarkLoraStackFromString", + "DarkPopLoraFromStack", + "DarkPrompt" + ], + { + "title_aux": "DarkPrompts" + } + ], + "https://github.com/darth-veitcher/comfydv": [ + [ + "CircuitBreaker", + "FormatString", + "ModelUnloader", + "RandomChoice" + ], + { + "author": "Darth Veitcher", + "description": "This collection of nodes provides string formatting, random choices, model memory management, and other quality of life improvements.", + "nickname": "DV Nodes", + "title": "Comfy DV Nodes", + "title_aux": "Comfy DV" + } + ], + "https://github.com/daryltucker/ComfyUI-LoadFiles": [ + [ + "CountLines", + "ListFilenames", + "LoadImages" + ], + { + "title_aux": "ComfyUI-LoadFiles" + } + ], + "https://github.com/dasilva333/ComfyUI_ContrastingColor": [ + [ + "ContrastingComplementaryColor|pysssss" + ], + { + "title_aux": "ComfyUI_ContrastingColor" + } + ], + "https://github.com/dasilva333/ComfyUI_MarkdownImage": [ + [ + "CreateDialogImage", + "CreateDialogImageV2", + "CreateMarkdownImage", + "CreateMarkdownImageV2" + ], + { + "title_aux": "ComfyUI_MarkdownImage" + } + ], + "https://github.com/dave-palt/comfyui_DSP_imagehelpers": [ + [ + "dsp-imagehelpers-concat" + ], + { + "title_aux": "comfyui_DSP_imagehelpers" + } + ], + "https://github.com/davidgressett/comfyui-systemlevel": [ + [ + "CartesianCSVNode" + ], + { + "title_aux": "CartesianCSVNode for ComfyUI" + } + ], + "https://github.com/daxcay/ComfyUI-DataSet": [ + [ + "DataSet_ClaudeAIChat", + "DataSet_ClaudeAIChatImage", + "DataSet_ConceptManager", + "DataSet_CopyFiles", + "DataSet_FindAndReplace", + "DataSet_GroqChat", + "DataSet_GroqChatImage", + "DataSet_LoadImage", + "DataSet_OpenAIChat", + "DataSet_OpenAIChatImage", + "DataSet_OpenAIChatImageBatch", + "DataSet_PathSelector", + "DataSet_SaveImage", + "DataSet_SaveImagePro", + "DataSet_TextFilesLoad", + "DataSet_TextFilesLoadFromList", + "DataSet_TextFilesSave", + "DataSet_TriggerWords", + "DataSet_Visualizer" + ], + { + "author": "Daxton Caylor", + "description": "Data Research, Preparation, and Manipulation Nodes for Model Trainers, Artists, Designers, and Animators.", + "nickname": "ComfyUI-DataSet", + "title": "ComfyUI-DataSet", + "title_aux": "ComfyUI-DataSet" + } + ], + "https://github.com/daxcay/ComfyUI-JDCN": [ + [ + "JDCN_AnyCheckpointLoader", + "JDCN_AnyFileList", + "JDCN_AnyFileListHelper", + "JDCN_AnyFileListRandom", + "JDCN_AnyFileSelector", + "JDCN_BatchCounter", + "JDCN_BatchCounterAdvance", + "JDCN_BatchImageLoadFromDir", + "JDCN_BatchImageLoadFromList", + "JDCN_BatchLatentLoadFromDir", + "JDCN_BatchLatentLoadFromList", + "JDCN_BatchSaveLatent", + "JDCN_BoolInt", + "JDCN_EnableDisable", + "JDCN_FileMover", + "JDCN_ImageSaver", + "JDCN_ListToString", + "JDCN_LoadImage", + "JDCN_ReBatch", + "JDCN_SeamlessExperience", + "JDCN_ShowAny", + "JDCN_SplitString", + "JDCN_StringManipulator", + "JDCN_StringToList", + "JDCN_SwapInputs", + "JDCN_TXTFileSaver", + "JDCN_VHSFileMover" + ], + { + "author": "Daxton Caylor & Jerry Davos", + "description": "Custom Utility Nodes for Artists, Designers and Animators.", + "nickname": "ComfyUI-JDCN", + "title": "ComfyUI-JDCN", + "title_aux": "ComfyUI-JDCN" + } + ], + "https://github.com/daxcay/ComfyUI-TG": [ + [ + "TG_ImageSaver" + ], + { + "author": "Daxton Caylor", + "description": "This node enables someone to run comfyui in telegram.", + "nickname": "ComfyUI-TG", + "title": "ComfyUI-TG", + "title_aux": "ComfyUI-TG" + } + ], + "https://github.com/daxcay/ComfyUI-WA": [ + [ + "WA_ImageSaver" + ], + { + "author": "Daxton Caylor", + "description": "This node enables someone to run comfyui in whatsapp.", + "nickname": "ComfyUI-WA", + "title": "ComfyUI-WA", + "title_aux": "ComfyUI-WA" + } + ], + "https://github.com/daxcay/ComfyUI-YouTubeVideoPlayer": [ + [ + "YouTubeVideoPlayer" + ], + { + "author": "Daxton Caylor & Jerry Davos", + "description": "YouTube Video Player in Comfy.", + "nickname": "ComfyUI-YouTubeVideoPlayer", + "title": "ComfyUI-YouTubeVideoPlayer", + "title_aux": "ComfyUI-YouTubeVideoPlayer" + } + ], + "https://github.com/dchatel/comfyui_davcha": [ + [ + "ApplyMask", + "ConditioningCompress", + "DStack", + "DavchaCLIPMergeSimple", + "DavchaCLIPTextEncode", + "DavchaConditioningConcat", + "DavchaEmptyLatentImage", + "DavchaLLM", + "DavchaLLMAdvanced", + "DavchaLoadLLM", + "DavchaLoadVideo", + "DavchaMaskImage", + "DavchaModelMergeSD1", + "DavchaModelMergeSDXL", + "DavchaModelMergeSimple", + "DavchaPop", + "PadAndResize", + "PercentPadding", + "ResizeCropFit", + "SmartMask", + "SoftErosion", + "StringScheduleHelper" + ], + { + "title_aux": "comfyui_davcha" + } + ], + "https://github.com/dchatel/comfyui_facetools": [ + [ + "BiSeNetMask", + "CropFaces", + "DetectFaces", + "GenderFaceFilter", + "JonathandinuMask", + "MergeWarps", + "OrderedFaceFilter", + "WarpFacesBack" + ], + { + "title_aux": "comfyui_facetools" + } + ], + "https://github.com/denfrost/Den_ComfyUI_Workflow": [ + [ + "Den_BatchIndex_AS", + "Den_CropImage_AS", + "Den_Eval_AS", + "Den_FaceRestoreCFWithModel", + "Den_GPTLoaderSimple_llama", + "Den_GPTSampler_llama", + "Den_ImageMixMasked_As", + "Den_ImageToLatentSpace", + "Den_ImageToMask_AS", + "Den_Int2Any_AS", + "Den_LatentAdd_AS", + "Den_LatentMixMasked_As", + "Den_LatentMix_AS", + "Den_LatentToImages_AS", + "Den_LoadLatent_AS", + "Den_MapRange_AS", + "Den_MaskToImage_AS", + "Den_Math_AS", + "Den_NoiseImage_AS", + "Den_Number2Float_AS", + "Den_Number2Int_AS", + "Den_Number_AS", + "Den_SVD_img2vid", + "Den_SaveLatent_AS", + "Den_TextToImage_AS", + "Den_TextWildcardList_AS", + "Increment_AS" + ], + { + "title_aux": "Den_ComfyUI_Workflows" + } + ], + "https://github.com/deroberon/StableZero123-comfyui": [ + [ + "SDZero ImageSplit", + "Stablezero123", + "Stablezero123WithDepth" + ], + { + "title_aux": "StableZero123-comfyui" + } + ], + "https://github.com/deroberon/demofusion-comfyui": [ + [ + "Batch Unsampler", + "Demofusion", + "Demofusion From Single File", + "Iterative Mixing KSampler" + ], + { + "title_aux": "demofusion-comfyui" + } + ], + "https://github.com/dfghsdh/ComfyUI_FluxPromptGen": [ + [ + "FluxImageCaptionNode", + "FluxPromptGeneratorNode" + ], + { + "title_aux": "ComfyUI_FluxPromptGen" + } + ], + "https://github.com/dfl/comfyui-clip-with-break": [ + [ + "AdvancedCLIPTextEncodeWithBreak", + "CLIPTextEncodeWithBreak" + ], + { + "author": "dfl", + "description": "CLIP text encoder that does BREAK prompting like A1111", + "nickname": "CLIP with BREAK", + "title": "CLIP with BREAK syntax", + "title_aux": "comfyui-clip-with-break" + } + ], + "https://github.com/dfl/comfyui-tcd-scheduler": [ + [ + "SamplerTCD", + "SamplerTCD EulerA", + "TCDScheduler" + ], + { + "title_aux": "ComfyUI-TCD-scheduler" + } + ], + "https://github.com/diStyApps/ComfyUI-disty-Flow": [ + [ + "Flow" + ], + { + "title_aux": "Flow - Streamlined Way to ComfyUI" + } + ], + "https://github.com/diStyApps/ComfyUI_FrameMaker": [ + [ + "FrameMaker", + "FrameMakerBatch" + ], + { + "title_aux": "ComfyUI Frame Maker" + } + ], + "https://github.com/dicksensei69/comfyui_loops": [ + [ + "LoopImageNode" + ], + { + "title_aux": "ComfyUI Loops" + } + ], + "https://github.com/dicksondickson/ComfyUI-Dickson-Nodes": [ + [ + "DicksonColorMatch", + "DicksonLoadImage", + "Dickson_TTP_Preprocessor_Simple", + "Dickson_TTP_Preprocessor_cufoff", + "Dickson_TTP_Tile_Preprocessor_GF" + ], + { + "description": "This is a set of custom nodes that I've either written myself or adapted from other authors for my own convenience. Currently includes color matching node forked from StableSR and TTPlanet's controlnet preprocessor. https://github.com/dicksondickson", + "nickname": "Dickson Nodes", + "title": "Dickson Nodes", + "title_aux": "ComfyUI-Dickson-Nodes" + } + ], + "https://github.com/digitaljohn/comfyui-propost": [ + [ + "ProPostApplyLUT", + "ProPostDepthMapBlur", + "ProPostFilmGrain", + "ProPostRadialBlur", + "ProPostVignette" + ], + { + "title_aux": "ComfyUI-ProPost" + } + ], + "https://github.com/dimtion/comfyui-raw-image": [ + [ + "Load Raw Image" + ], + { + "title_aux": "ComfyUI-Raw-Image" + } + ], + "https://github.com/dimtoneff/ComfyUI-PixelArt-Detector": [ + [ + "PixelArtAddDitherPattern", + "PixelArtDetectorConverter", + "PixelArtDetectorSave", + "PixelArtDetectorToImage", + "PixelArtLoadPalettes" + ], + { + "title_aux": "ComfyUI PixelArt Detector" + } + ], + "https://github.com/diontimmer/ComfyUI-Vextra-Nodes": [ + [ + "Add Text To Image", + "Apply Instagram Filter", + "Create Solid Color", + "Flatten Colors", + "Generate Noise Image", + "GlitchThis Effect", + "Hue Rotation", + "Load Picture Index", + "Pixel Sort", + "Play Sound At Execution", + "Prettify Prompt Using distilgpt2", + "Swap Color Mode" + ], + { + "title_aux": "ComfyUI-Vextra-Nodes" + } + ], + "https://github.com/discopixel-studio/comfyui-discopixel": [ + [ + "PhotoroomRemoveBG" + ], + { + "author": "Anson Kao", + "description": "A small collection of custom nodes for use with ComfyUI, by Discopixel", + "nickname": "ComfyUI Discopixel", + "title": "ComfyUI Discopixel", + "title_aux": "PhotoRoom Nodes by Discopixel" + } + ], + "https://github.com/discus0434/comfyui-caching-embeddings": [ + [ + "CachingCLIPTextEncode" + ], + { + "title_aux": "ComfyUI Caching Embeddings" + } + ], + "https://github.com/discus0434/comfyui-flux-accelerator": [ + [ + "\ud83c\udf6dFluxAccelerator" + ], + { + "title_aux": "ComfyUI Flux Accelerator" + } + ], + "https://github.com/djbielejeski/a-person-mask-generator": [ + [ + "APersonFaceLandmarkMaskGenerator", + "APersonMaskGenerator" + ], + { + "title_aux": "a-person-mask-generator" + } + ], + "https://github.com/dmMaze/sketch2manga": [ + [ + "BlendScreentone", + "EmptyLatentImageAdvanced" + ], + { + "title_aux": "Sketch2Manga" + } + ], + "https://github.com/dmarx/ComfyUI-AudioReactive": [ + [ + "OpAbs", + "OpBandpass", + "OpClamp", + "OpHarmonic", + "OpModulo", + "OpNormalize", + "OpNovelty", + "OpPercussive", + "OpPow", + "OpPow2", + "OpPredominant_pulse", + "OpQuantize", + "OpRms", + "OpSmoosh", + "OpSmooth", + "OpSqrt", + "OpStretch", + "OpSustain", + "OpThreshold" + ], + { + "title_aux": "ComfyUI-AudioReactive" + } + ], + "https://github.com/dmarx/ComfyUI-Keyframed": [ + [ + "Example", + "KfAddCurveToPGroup", + "KfAddCurveToPGroupx10", + "KfApplyCurveToCond", + "KfConditioningAdd", + "KfConditioningAddx10", + "KfCurveConstant", + "KfCurveDraw", + "KfCurveFromString", + "KfCurveFromYAML", + "KfCurveInverse", + "KfCurveToAcnLatentKeyframe", + "KfCurvesAdd", + "KfCurvesAddx10", + "KfCurvesDivide", + "KfCurvesMultiply", + "KfCurvesMultiplyx10", + "KfCurvesSubtract", + "KfDebug_Clip", + "KfDebug_Cond", + "KfDebug_Curve", + "KfDebug_Float", + "KfDebug_Image", + "KfDebug_Int", + "KfDebug_Latent", + "KfDebug_Model", + "KfDebug_Passthrough", + "KfDebug_Segs", + "KfDebug_String", + "KfDebug_Vae", + "KfDrawSchedule", + "KfEvaluateCurveAtT", + "KfGetCurveFromPGroup", + "KfGetScheduleConditionAtTime", + "KfGetScheduleConditionSlice", + "KfKeyframedCondition", + "KfKeyframedConditionWithText", + "KfPGroupCurveAdd", + "KfPGroupCurveMultiply", + "KfPGroupDraw", + "KfPGroupProd", + "KfPGroupSum", + "KfSetCurveLabel", + "KfSetKeyframe", + "KfSinusoidalAdjustAmplitude", + "KfSinusoidalAdjustFrequency", + "KfSinusoidalAdjustPhase", + "KfSinusoidalAdjustWavelength", + "KfSinusoidalEntangledZeroOneFromFrequencyx2", + "KfSinusoidalEntangledZeroOneFromFrequencyx3", + "KfSinusoidalEntangledZeroOneFromFrequencyx4", + "KfSinusoidalEntangledZeroOneFromFrequencyx5", + "KfSinusoidalEntangledZeroOneFromFrequencyx6", + "KfSinusoidalEntangledZeroOneFromFrequencyx7", + "KfSinusoidalEntangledZeroOneFromFrequencyx8", + "KfSinusoidalEntangledZeroOneFromFrequencyx9", + "KfSinusoidalEntangledZeroOneFromWavelengthx2", + "KfSinusoidalEntangledZeroOneFromWavelengthx3", + "KfSinusoidalEntangledZeroOneFromWavelengthx4", + "KfSinusoidalEntangledZeroOneFromWavelengthx5", + "KfSinusoidalEntangledZeroOneFromWavelengthx6", + "KfSinusoidalEntangledZeroOneFromWavelengthx7", + "KfSinusoidalEntangledZeroOneFromWavelengthx8", + "KfSinusoidalEntangledZeroOneFromWavelengthx9", + "KfSinusoidalGetAmplitude", + "KfSinusoidalGetFrequency", + "KfSinusoidalGetPhase", + "KfSinusoidalGetWavelength", + "KfSinusoidalWithFrequency", + "KfSinusoidalWithWavelength" + ], + { + "title_aux": "ComfyUI-Keyframed" + } + ], + "https://github.com/dorpxam/ComfyUI-FramePack-F1-T2V": [ + [ + "FramePackF1T2VLoraStack", + "FramePackF1T2VSampler", + "FramePackF1T2VSamplerSettings", + "FramePackF1T2VTextEncode", + "FramePackF1T2VUserSettings" + ], + { + "title_aux": "ComfyUI-FramePack-F1-T2V" + } + ], + "https://github.com/dorpxam/ComfyUI-LTXVideoLoRA": [ + [ + "LTXVLoRABlockEdit", + "LTXVLoRALoader", + "LTXVLoRASelector" + ], + { + "title_aux": "ComfyUI-LTXVideoLoRA" + } + ], + "https://github.com/doubletwisted/ComfyUI-Deadline-Plugin": [ + [ + "DeadlineSubmit" + ], + { + "nodename_pattern": "DeadlineSubmitNode", + "title_aux": "ComfyUI Deadline Submission" + } + ], + "https://github.com/drago87/ComfyUI_Dragos_Nodes": [ + [ + "file_padding", + "image_info", + "lora_loader", + "vae_loader" + ], + { + "title_aux": "ComfyUI_Dragos_Nodes" + } + ], + "https://github.com/dreamhartley/ComfyUI_show_seed": [ + [ + "Show Seed" + ], + { + "title_aux": "ComfyUI_show_seed" + } + ], + "https://github.com/drmbt/comfyui-dreambait-nodes": [ + [ + "AudioInfoPlus", + "BoolPlusPlus", + "CompareImageSimilarity", + "DRMBT_AspectPadImageForOutpainting", + "DRMBT_LoadMedia", + "DRMBT_MultiMinMax", + "DRMBT_String_Item_Menu", + "DictToOutputs", + "DownloadAndLoadMiniCPMV", + "DrawMana", + "DrawText", + "DreambaitFolderOpener", + "DynamicDictionary", + "DynamicStringConcatenate", + "ImageFrameBlend", + "ImageResizeFaceAware", + "ListItemExtract", + "ListItemSelector", + "LoadAudioPlus", + "MiniCPMVNode", + "MusicGen", + "NormalizeAudio", + "NumberPlusPlus", + "NumberRemap", + "Qwen2AudioInstruct", + "ShotHistory", + "StringToDict", + "SwitchDuo", + "TextBoxStyle", + "TextLineSelect", + "TextLinesToList", + "TextMargins", + "TextPlusPlus", + "TextShadow" + ], + { + "title_aux": "comfyui-dreambait-nodes" + } + ], + "https://github.com/drphero/comfyui_prompttester": [ + [ + "PromptTester" + ], + { + "title_aux": "ComfyUI-PromptTester" + } + ], + "https://github.com/drustan-hawk/primitive-types": [ + [ + "float", + "int", + "string", + "string_multiline" + ], + { + "title_aux": "primitive-types" + } + ], + "https://github.com/dseditor/ComfyUI-ListHelper": [ + [ + "AudioListCombine", + "AudioListGenerator", + "AudioToFrameCount", + "CeilDivide", + "FrameMatch", + "LoadVideoPath", + "MergeVideoFilename", + "NumberListGenerator", + "PromptListGenerator", + "SaveVideoPath" + ], + { + "title_aux": "ComfyUI-ListHelper" + } + ], + "https://github.com/dseditor/ComfyUI-ScheduledTask": [ + [ + "DailyPromptScheduler", + "ShutdownNode", + "TimeToSeedList" + ], + { + "title_aux": "ComfyUI-ScheduledTask" + } + ], + "https://github.com/dseditor/ComfyUI-Thread": [ + [ + "PublishThread", + "StartWithLongLiveToken", + "ThreadPublishVideo", + "ThreadsHistory" + ], + { + "title_aux": "ComfyUI-Thread" + } + ], + "https://github.com/duchamps0305/comfyui-white-extractor": [ + [ + "WhitePercentage" + ], + { + "title_aux": "comfyui-white-extractor" + } + ], + "https://github.com/ducido/ObjectFusion_ComfyUI_nodes": [ + [ + "Custom ESAM_ModelLoader_Zho", + "Custom Generate Stable Diffsution Prompt With LLM", + "Custom Yoloworld_ESAM_Zho", + "Custom Yoloworld_ModelLoader_Zho", + "ObjectCrop" + ], + { + "title_aux": "ObjectFusion_ComfyUI_nodes" + } + ], + "https://github.com/duskfallcrew/Comfyui_EmbeddingMerge_Node/raw/refs/heads/main/merge_embed.py": [ + [ + "EmbeddingMerger" + ], + { + "title_aux": "Embedding Merge for ComfyUI" + } + ], + "https://github.com/dymokomi/comfyui_dygen": [ + [ + "AdaptiveColorCircles", + "AdaptiveColorLines", + "AdaptiveColorRectangles", + "BinaryPatternStamper", + "DYImageCluster", + "DYImageMasks", + "DYImagePalette", + "DYImageQuantize", + "ImageListToGrid", + "ImageScaler", + "RandomLines" + ], + { + "title_aux": "comfyui_dygen" + } + ], + "https://github.com/dzqdzq/ComfyUI-crop-alpha": [ + [ + "FastAlphaCropper", + "ShrinkImage" + ], + { + "title_aux": "ComfyUI-crop-alpha" + } + ], + "https://github.com/e-tier-newbie/ComfyUI-E-Tier-TextSaver": [ + [ + "E_TierTextSaver" + ], + { + "title_aux": "ComfyUI-E-Tier-TextSaver" + } + ], + "https://github.com/e7mac/ComfyUI-ShadertoyGL": [ + [ + "ColorChannelOffset", + "Shader", + "Shadertoy" + ], + { + "title_aux": "ComfyUI-ShadertoyGL" + } + ], + "https://github.com/ealkanat/comfyui-easy-padding": [ + [ + "comfyui-easy-padding" + ], + { + "title_aux": "ComfyUI Easy Padding" + } + ], + "https://github.com/eastoc/ComfyUI_SemanticSAM": [ + [ + "PointPrompt", + "SemanticSAMLoader", + "SemanticSAMSegment" + ], + { + "title_aux": "Semantic-SAM" + } + ], + "https://github.com/ebrinz/ComfyUI-MusicGen-HF": [ + [ + "AudioOutputToConditioningQueue", + "BPMDurationInput", + "ConditioningQueueManager", + "HuggingFaceMusicGen", + "LoadAudioStandalone", + "LoopingAudioPreview", + "MusicGenAudioToFile", + "ProfessionalLoopTransition", + "SaveAudioStandalone", + "SmoothAudioQueue", + "custom_nodes" + ], + { + "title_aux": "ComfyUI-MusicGen-HF" + } + ], + "https://github.com/edelvarden/ComfyUI-Display-Value": [ + [ + "DisplayValue" + ], + { + "title_aux": "ComfyUI-Display-Value" + } + ], + "https://github.com/edenartlab/eden_comfy_pipelines": [ + [ + "AnimatedShapeMaskNode", + "Animation_RGB_Mask", + "AspectPadImageForOutpainting", + "CLIP_Interrogator", + "ConvertToGrayscale", + "DepthSlicer", + "Eden_AllMediaLoader", + "Eden_Bool", + "Eden_BoolBinaryOperation", + "Eden_Compare", + "Eden_Debug_Anything", + "Eden_DepthSlice_MaskVideo", + "Eden_DetermineFrameCount", + "Eden_FaceToMask", + "Eden_Face_Crop", + "Eden_Float", + "Eden_FloatToInt", + "Eden_GPTPromptEnhancer", + "Eden_GPTStructuredOutput", + "Eden_IMG_padder", + "Eden_IMG_unpadder", + "Eden_ImageMaskComposite", + "Eden_Image_Math", + "Eden_Int", + "Eden_IntToFloat", + "Eden_MaskBoundingBox", + "Eden_MaskCombiner", + "Eden_Math", + "Eden_RGBA_to_RGB", + "Eden_RandomFilepathSampler", + "Eden_RandomNumberSampler", + "Eden_RandomPromptFromFile", + "Eden_Regex_Replace", + "Eden_RepeatLatentBatch", + "Eden_Save_Param_Dict", + "Eden_Seed", + "Eden_String", + "Eden_StringHash", + "Eden_StringReplace", + "Eden_gpt4_node", + "Eden_randbool", + "Extend_Sequence", + "FolderScanner", + "GetRandomFile", + "Get_Prefixed_Imgs", + "HistogramMatching", + "IMG_blender", + "IMG_resolution_multiple_of", + "IMG_scaler", + "IP_Adapter_Settings_Distribution", + "If ANY execute A else B", + "ImageDescriptionNode", + "ImageFolderIterator", + "KeyframeBlender", + "LatentTypeConversion", + "Linear_Combine_IP_Embeds", + "LoadImagesByFilename", + "LoadRandomImage", + "Load_Embeddings_From_Folder", + "MaskFromRGB_KMeans", + "MaskedRegionVideoExport", + "OrganicFillNode", + "ParallaxZoom", + "Random_Style_Mixture", + "SDAnyConverter", + "SDTypeConverter", + "SaveImageAdvanced", + "SavePosEmbeds", + "VAEDecode_to_folder", + "VideoFrameSelector", + "WidthHeightPicker" + ], + { + "title_aux": "Eden.art nodesuite" + } + ], + "https://github.com/edenartlab/sd-lora-trainer": [ + [ + "Eden_LoRa_trainer" + ], + { + "title_aux": "Eden.art LoRa Trainer" + } + ], + "https://github.com/educator-art/ComfyUI-Load-DirectoryFiles": [ + [ + "Load Images and Prompts from Directory", + "Load Images and Prompts from Directory(Advanced)" + ], + { + "title_aux": "ComfyUI-Load-DirectoryFiles" + } + ], + "https://github.com/eg0pr0xy/comfyui_noisegen": [ + [ + "AudioAnalyzer", + "AudioMixer", + "AudioSave", + "BandLimitedNoise", + "ChaosNoiseMix", + "ConvolutionReverb", + "FeedbackProcessor", + "GranularProcessor", + "GranularSequencer", + "HarshFilter", + "MicrosoundSculptor", + "ModulationMatrix", + "MultiDistortion", + "NoiseGenerator", + "PerlinNoise", + "SpectralProcessor", + "SpectrumAnalyzer", + "TrueChaos" + ], + { + "title_aux": "ComfyUI-NoiseGen" + } + ], + "https://github.com/einhorn13/ComfyUI-ImageProcessUtilities": [ + [ + "CombineCoords", + "CropByCoords", + "ImageTiler", + "ImageUntiler", + "PasteByCoords", + "ReorderBatch", + "SplitCoords", + "StringToIntegers" + ], + { + "title_aux": "ComfyUI-ImageProcessUtilities" + } + ], + "https://github.com/emojiiii/ComfyUI_Emojiiii_Custom_Nodes": [ + [ + "BatchImageProcessor", + "Caption", + "CaptionDownload", + "KolorsMultiTextEncode", + "MultiTextEncode" + ], + { + "title_aux": "ComfyUI_Emojiiii_Custom_Nodes" + } + ], + "https://github.com/envy-ai/ComfyUI-ConDelta": [ + [ + "ApplyConDelta", + "ApplyConDeltaAutoScale", + "CFGlessNegativePrompt", + "ClampConDelta", + "ConditioningAddConDelta", + "ConditioningAddConDeltaAutoScale", + "ConditioningAverageMultiple", + "ConditioningGetNoise", + "ConditioningGetRandom", + "ConditioningScale", + "ConditioningSubtract", + "ExtendedConditioningAverage", + "GetConDeltaFromPrompt", + "HardClampConDelta", + "LoadConditioningDelta", + "MaskConDelta", + "QuickConDelta", + "SaveConditioningDelta", + "ThresholdConditioning" + ], + { + "title_aux": "ComfyUI-ConDelta" + } + ], + "https://github.com/erosDiffusion/ComfyUI-enricos-nodes": [ + [ + "Compositor3", + "CompositorColorPicker", + "CompositorConfig3", + "CompositorMasksOutputV3", + "CompositorTools3", + "CompositorTransformsOutV3", + "ImageColorSampler" + ], + { + "title_aux": "ComfyUI-enricos-nodes" + } + ], + "https://github.com/evanspearman/ComfyMath": [ + [ + "CM_BoolBinaryOperation", + "CM_BoolToInt", + "CM_BoolUnaryOperation", + "CM_BreakoutVec2", + "CM_BreakoutVec3", + "CM_BreakoutVec4", + "CM_ComposeVec2", + "CM_ComposeVec3", + "CM_ComposeVec4", + "CM_FloatBinaryCondition", + "CM_FloatBinaryOperation", + "CM_FloatToInt", + "CM_FloatToNumber", + "CM_FloatUnaryCondition", + "CM_FloatUnaryOperation", + "CM_IntBinaryCondition", + "CM_IntBinaryOperation", + "CM_IntToBool", + "CM_IntToFloat", + "CM_IntToNumber", + "CM_IntUnaryCondition", + "CM_IntUnaryOperation", + "CM_NearestSDXLExtendedResolution", + "CM_NearestSDXLResolution", + "CM_NumberBinaryCondition", + "CM_NumberBinaryOperation", + "CM_NumberToFloat", + "CM_NumberToInt", + "CM_NumberUnaryCondition", + "CM_NumberUnaryOperation", + "CM_SDXLExtendedResolution", + "CM_SDXLResolution", + "CM_Vec2BinaryCondition", + "CM_Vec2BinaryOperation", + "CM_Vec2ScalarOperation", + "CM_Vec2ToScalarBinaryOperation", + "CM_Vec2ToScalarUnaryOperation", + "CM_Vec2UnaryCondition", + "CM_Vec2UnaryOperation", + "CM_Vec3BinaryCondition", + "CM_Vec3BinaryOperation", + "CM_Vec3ScalarOperation", + "CM_Vec3ToScalarBinaryOperation", + "CM_Vec3ToScalarUnaryOperation", + "CM_Vec3UnaryCondition", + "CM_Vec3UnaryOperation", + "CM_Vec4BinaryCondition", + "CM_Vec4BinaryOperation", + "CM_Vec4ScalarOperation", + "CM_Vec4ToScalarBinaryOperation", + "CM_Vec4ToScalarUnaryOperation", + "CM_Vec4UnaryCondition", + "CM_Vec4UnaryOperation" + ], + { + "title_aux": "ComfyMath" + } + ], + "https://github.com/excelwong/ComfyUI-PromptComposer": [ + [ + "PromptComposer" + ], + { + "title_aux": "ComfyUI Prompt Composer" + } + ], + "https://github.com/exdysa/comfyui-selector": [ + [ + "RecourseAny", + "RecourseCkpt", + "RecourseImage", + "RecoursePolar", + "RecourseStrings", + "SelInClip", + "SelInFloat", + "SelInGuider", + "SelInInt", + "SelInLatent", + "SelInModel", + "SelInPolar", + "SelInSampler", + "SelInSigmas", + "SelInVae", + "SelOutCLIP", + "SelOutModel", + "SelOutPolar", + "Selector", + "Selector Advanced", + "Selector Hub" + ], + { + "author": "\"\u02f6\ud835\udfa2\u292c\u2ad2\u2d56s\u143c\u02f6\"", + "description": "\"EXDYSA. Selector and Recourse. Presets & failsafes. Work flow.\"", + "nickname": "\"Selector\"", + "title": "\"Selector\"", + "title_aux": "comfyui-selector" + } + ], + "https://github.com/exectails/comfyui-et_dynamicprompts": [ + [ + "ETDynamicPrompt" + ], + { + "title_aux": "Dynamic Prompts" + } + ], + "https://github.com/exectails/comfyui-et_infoutils": [ + [ + "ETInspectTextNode", + "ETIntBoxNode", + "ETPresentImageNode", + "ETShowDataNode", + "ETStringBoxNode", + "ETTextBoxNode", + "ETTokenCountNode" + ], + { + "title_aux": "Info Utils" + } + ], + "https://github.com/exectails/comfyui-et_stringutils": [ + [ + "ETATOI", + "ETITOA", + "ETJoinTextNode", + "ETReplaceTextNode", + "ETSplitTextNode", + "ETSwitchTextNode", + "ETTextFormatter10Node", + "ETTextFormatter2Node", + "ETTextFormatter5Node" + ], + { + "title_aux": "String Utils" + } + ], + "https://github.com/ez-af/ComfyUI-EZ-AF-Nodes": [ + [ + "EZ_CSV_Loader", + "EZ_Extract_Prompt", + "EZ_Find_Replace", + "EZ_Input", + "EZ_Prompt_Loader", + "EZ_Switch", + "EZ_Tag_Loader", + "EZ_Test", + "EZ_Text_Concat", + "EZ_Text_to_Size" + ], + { + "title_aux": "ComfyUI-EZ-AF-Nodes" + } + ], + "https://github.com/facok/ComfyUI-HunyuanVideoMultiLora": [ + [ + "HunyuanVideoLoraLoader" + ], + { + "title_aux": "ComfyUI-HunyuanVideoMultiLora" + } + ], + "https://github.com/facok/ComfyUI-TeaCacheHunyuanVideo": [ + [ + "TeaCacheHunyuanVideoSampler_FOK" + ], + { + "title_aux": "ComfyUI-TeaCacheHunyuanVideo" + } + ], + "https://github.com/fairy-root/ComfyUI-GLHF": [ + [ + "glhf_chat" + ], + { + "title_aux": "ComfyUI-GLHF" + } + ], + "https://github.com/fairy-root/ComfyUI-OpenAI-FM": [ + [ + "OpenAIFMNode" + ], + { + "title_aux": "ComfyUI-OpenAI-FM" + } + ], + "https://github.com/fairy-root/ComfyUI-Show-Text": [ + [ + "ShowText" + ], + { + "title_aux": "ComfyUI-Show-Text" + } + ], + "https://github.com/fairy-root/Flux-Prompt-Generator": [ + [ + "FluxPromptGenerator" + ], + { + "title_aux": "Flux Prompt Generator for ComfyUI" + } + ], + "https://github.com/fairy-root/comfyui-ollama-llms": [ + [ + "ConcatenateText", + "llava", + "ollama" + ], + { + "title_aux": "Ollama and Llava Vision integration for ComfyUI" + } + ], + "https://github.com/fallingmeteorite/nsfw-image-check-comfyui": [ + [ + "NsfwAreaCoverNode", + "NsfwCheckNode" + ], + { + "title_aux": "nsfw-image-check-comfyui" + } + ], + "https://github.com/fashn-AI/ComfyUI-FASHN": [ + [ + "FASHN" + ], + { + "title_aux": "FASHN Virtual Try-On" + } + ], + "https://github.com/fat-tire/comfyui-unified-media-suite": [ + [ + "MediaLoad", + "MediaMerge", + "MediaSave" + ], + { + "title_aux": "ComfyUI Unified Media Suite" + } + ], + "https://github.com/fblissjr/ComfyUI-DatasetHelper": [ + [ + "DatasetBatchNode" + ], + { + "title_aux": "ComfyUI Dataset Helper & Batch Node" + } + ], + "https://github.com/fblissjr/ComfyUI-EmbeddingPipelineAnalytics": [ + [ + "EmbeddingAnalyzer", + "EmbeddingPipelineCapture" + ], + { + "title_aux": "ComfyUI-EmbeddingPipelineAnalytics" + } + ], + "https://github.com/fblissjr/ComfyUI-WanActivationEditor": [ + [ + "WanVideoActivationEditor", + "WanVideoAdvancedActivationEditor", + "WanVideoBlockActivationBuilder", + "WanVideoBlockActivationViewer", + "WanVideoBlockStrengthBuilder", + "WanVideoDirectInjector", + "WanVideoEmbeddingAmplifier", + "WanVideoEmbeddingAnalyzer", + "WanVideoEmbeddingDatabase", + "WanVideoGuidanceController", + "WanVideoInjectionTester", + "WanVideoLatentEncoder", + "WanVideoLatentInjector", + "WanVideoNoiseController", + "WanVideoProjectionBooster", + "WanVideoSequentialMixer", + "WanVideoStrengthVisualizer", + "WanVideoVectorArithmetic", + "WanVideoVectorDifference", + "WanVideoVectorInterpolation" + ], + { + "title_aux": "ComfyUI-WanActivationEditor" + } + ], + "https://github.com/fblissjr/ComfyUI-WanSeamlessFlow": [ + [ + "WanAdaptiveFlow", + "WanBlendVisualize", + "WanEmbeddingPrevizCanvas", + "WanMinimalCanvasTest", + "WanSmartBlend" + ], + { + "title_aux": "wanvideo - seamless flow" + } + ], + "https://github.com/fblissjr/shrug-prompter": [ + [ + "AccumulationNodeCompat", + "AdvancedVLMSampler", + "AnyTypePassthrough", + "AutoMemoryManager", + "GlobalMemoryCleanup", + "ImageToAny", + "LoopAwareResponseIterator", + "LoopAwareVLMAccumulator", + "LoopSafeAccumulator", + "PromptTemplateLoader", + "RobustImageRangeExtractor", + "ShrugPrompter", + "SmartImageRangeExtractor", + "TextCleanup", + "TextListCleanup", + "TextListIndexer", + "TextListToString", + "VLMImagePassthrough", + "VLMImageProcessor", + "VLMImageResizer", + "VLMPrompterFast", + "VLMProviderConfig", + "VLMResponseExtractor", + "VLMResultCollector", + "VLMResultIterator", + "VLMResultsToGeneric", + "VideoFramePairExtractor", + "VideoSegmentAssembler" + ], + { + "title_aux": "Shrug-Prompter: Unified VLM Integration for ComfyUI" + } + ], + "https://github.com/fcanfora/comfyui-camera-tools": [ + [ + "Load3DAnimation_Adv", + "Load3D_Adv", + "LoadCameraFromFile", + "Preview3D_Adv", + "Preview3D_AdvAnimation_Adv" + ], + { + "title_aux": "comfyui-camera-tools" + } + ], + "https://github.com/fchangjun/Comfyui_MultiSaveImage": [ + [ + "MultiSaveImage", + "SimpleLLMNode" + ], + { + "title_aux": "MultiSaveImage Node" + } + ], + "https://github.com/fearnworks/ComfyUI_FearnworksNodes": [ + [ + "Count Files in Directory (FW)", + "Count Tokens (FW)", + "CountTokens", + "FileCountInDirectory", + "Token Count Ranker(FW)", + "TokenCountRanker", + "Trim To Tokens (FW)", + "TrimToTokens" + ], + { + "title_aux": "Fearnworks Nodes" + } + ], + "https://github.com/feixuetuba/Spleeter": [ + [ + "Spleeter" + ], + { + "title_aux": "Spleeter" + } + ], + "https://github.com/felixszeto/ComfyUI-RequestNodes": [ + [ + "Get Request Node", + "GetRequestNode", + "Key/Value Node", + "KeyValueNode", + "Post Request Node", + "PostRequestNode", + "Rest Api Node", + "RestApiNode", + "Retry Settings Node", + "RetrySettingNode", + "String Replace Node", + "StringReplaceNode" + ], + { + "title_aux": "ComfyUI-RequestNodes" + } + ], + "https://github.com/fexli/fexli-util-node-comfyui": [ + [ + "FEAnyToDict", + "FEAnyToString", + "FEBCPrompt", + "FEBatchGenStringBCDocker", + "FEColor2Image", + "FEColorOut", + "FEDataInsertor", + "FEDataPacker", + "FEDataUnpacker", + "FEDeepClone", + "FEDictCombine", + "FEDictPacker", + "FEDictUnpacker", + "FEEncLoraAutoLoader", + "FEEncLoraAutoLoaderStack", + "FEEncLoraLoader", + "FEExtraInfoAdd", + "FEGenStringBCDocker", + "FEGenStringGPT", + "FEGenStringNBus", + "FEImageNoiseGenerate", + "FEImagePadForOutpaint", + "FEImagePadForOutpaintByImage", + "FEInterruptCondition", + "FELoadImageQQUrl", + "FEOperatorIf", + "FEPythonStrOp", + "FERandomBool", + "FERandomLoraSelect", + "FERandomPrompt", + "FERandomizedColor2Image", + "FERandomizedColorOut", + "FERerouteWithName", + "FESaveEncryptImage", + "FETextCombine", + "FETextCombine2Any", + "FETextInput" + ], + { + "title_aux": "fexli-util-node-comfyui" + } + ], + "https://github.com/fexploit/ComfyUI-AutoLabel": [ + [ + "AutoLabel" + ], + { + "title_aux": "ComfyUI-AutoLabel" + } + ], + "https://github.com/fexploit/ComfyUI-AutoTrimBG": [ + [ + "RonLayers/TrimBg: RonLayersTrimBgUltraV2" + ], + { + "title_aux": "ComfyUI-AutoTrimBG" + } + ], + "https://github.com/fexploit/ComfyUI-Classifier": [ + [ + "ClassifierNode" + ], + { + "title_aux": "ComfyUI-Classifier" + } + ], + "https://github.com/filipemeneses/comfy_pixelization": [ + [ + "Pixelization" + ], + { + "title_aux": "Pixelization" + } + ], + "https://github.com/filliptm/ComfyUI_FL-Trainer": [ + [ + "FL_ImageCaptionSaver", + "FL_KohyaSSAdvConfig", + "FL_KohyaSSDatasetConfig", + "FL_KohyaSSInitWorkspace", + "FL_KohyaSSTrain", + "FL_Kohya_EasyTrain", + "FL_LoadImagesFromDirectoryPath", + "FL_SliderLoraAdvConfig", + "FL_SliderLoraDatasetConfig", + "FL_SliderLoraInitWorkspace", + "FL_SliderLoraTrain" + ], + { + "title_aux": "ComfyUI_FL-Trainer" + } + ], + "https://github.com/filliptm/ComfyUI_Fill-ChatterBox": [ + [ + "FL_ChatterboxDialogTTS", + "FL_ChatterboxTTS", + "FL_ChatterboxVC" + ], + { + "title_aux": "ComfyUI_Fill-ChatterBox" + } + ], + "https://github.com/filliptm/ComfyUI_Fill-Nodes": [ + [ + "FL_API_Base64_ImageLoader", + "FL_API_ImageSaver", + "FL_AnimeLineExtractor", + "FL_ApplyMask", + "FL_Ascii", + "FL_BatchAlign", + "FL_BlackFrameReject", + "FL_BulkPDFLoader", + "FL_BulletHellGame", + "FL_CaptionSaver_V2", + "FL_CaptionToCSV", + "FL_ClipScanner", + "FL_CodeNode", + "FL_ColorPicker", + "FL_Dalle3", + "FL_DirectoryCrawl", + "FL_Dither", + "FL_Fal_Kontext", + "FL_Fal_Pixverse", + "FL_Float", + "FL_FractalKSampler", + "FL_GPT_Image1", + "FL_GPT_Image1_ADV", + "FL_GPT_Text", + "FL_GPT_Vision", + "FL_GeminiImageEditor", + "FL_GeminiImageGenADV", + "FL_GeminiTextAPI", + "FL_GeminiVideoCaptioner", + "FL_Glitch", + "FL_GoogleCloudStorage", + "FL_GoogleDriveDownloader", + "FL_GoogleDriveImageDownloader", + "FL_GradGenerator", + "FL_HFDatasetDownloader", + "FL_HFHubModelUploader", + "FL_HF_Character", + "FL_HF_UploaderAbsolute", + "FL_HalftonePattern", + "FL_Hedra_API", + "FL_HexagonalPattern", + "FL_HunyuanDelight", + "FL_ImageAddToBatch", + "FL_ImageAdjuster", + "FL_ImageAspectCropper", + "FL_ImageBatch", + "FL_ImageBatchToGrid", + "FL_ImageBatchToImageList", + "FL_ImageBlank", + "FL_ImageCaptionLayout", + "FL_ImageCaptionLayoutPDF", + "FL_ImageCaptionSaver", + "FL_ImageCollage", + "FL_ImageDimensionDisplay", + "FL_ImageListToImageBatch", + "FL_ImageNotes", + "FL_ImagePixelator", + "FL_ImageRandomizer", + "FL_ImageSelector", + "FL_ImageSlicer", + "FL_ImagesToPDF", + "FL_InfiniteZoom", + "FL_InpaintCrop", + "FL_Inpaint_Stitch", + "FL_JS", + "FL_KSamplerXYZPlot", + "FL_KsamplerBasic", + "FL_KsamplerPlus", + "FL_KsamplerPlusV2", + "FL_KsamplerSettings", + "FL_LoadImage", + "FL_MadLibGenerator", + "FL_Math", + "FL_MirrorAndAppendCaptions", + "FL_ModelInspector", + "FL_NFTGenerator", + "FL_NodeLoader", + "FL_NodePackLoader", + "FL_OllamaCaptioner", + "FL_PDFEncryptor", + "FL_PDFImageExtractor", + "FL_PDFLoader", + "FL_PDFMerger", + "FL_PDFSaver", + "FL_PDFTextExtractor", + "FL_PDFToImages", + "FL_Padding", + "FL_PaddingRemover", + "FL_PaperDrawn", + "FL_PasteByMask", + "FL_PasteOnCanvas", + "FL_PathTypeChecker", + "FL_PixVerseAPI", + "FL_PixelArtShader", + "FL_PixelSort", + "FL_ProResVideo", + "FL_PromptBasic", + "FL_PromptMulti", + "FL_PromptSelector", + "FL_RandomNumber", + "FL_RetroEffect", + "FL_Ripple", + "FL_RunwayImageAPI", + "FL_SDUltimate_Slices", + "FL_SamplerStrings", + "FL_SaveAndDisplayImage", + "FL_SaveCSV", + "FL_SaveImages", + "FL_SaveWebM", + "FL_SaveWebPImage(SaveImage)", + "FL_SchedulerStrings", + "FL_SendToDiscordWebhook", + "FL_SeparateMaskComponents", + "FL_Shadertoy", + "FL_SimpleGPTVision", + "FL_Switch", + "FL_Switch_Big", + "FL_SystemCheck", + "FL_TetrisGame", + "FL_TextOverlayNode", + "FL_TextToPDF", + "FL_TimeLine", + "FL_UnloadAllModels", + "FL_UnloadModel", + "FL_UpscaleModel", + "FL_VideoCadence", + "FL_VideoCadenceCompile", + "FL_VideoCaptionSaver", + "FL_VideoCropMask", + "FL_VideoCut", + "FL_VideoRecompose", + "FL_VideoTrim", + "FL_WF_Agent", + "FL_ZipDirectory", + "FL_ZipSave", + "GradientImageGenerator", + "SaveWebPImage" + ], + { + "title_aux": "ComfyUI_Fill-Nodes" + } + ], + "https://github.com/flamacore/ComfyUI-YouTubeUploader": [ + [ + "YouTubeAuthNode", + "YouTubeUploaderNode" + ], + { + "title_aux": "ComfyUI YouTube Uploader" + } + ], + "https://github.com/florestefano1975/ComfyUI-Advanced-Sequence-Seed": [ + [ + "AdvancedSequenceSeedNode" + ], + { + "title_aux": "Advanced Sequence Seed Generator" + } + ], + "https://github.com/florestefano1975/ComfyUI-CogVideoX": [ + [ + "CogVideoX Image-2-Video Extended", + "CogVideoX Save Video" + ], + { + "title_aux": "ComfyUI-CogVideoX" + } + ], + "https://github.com/florestefano1975/ComfyUI-HiDiffusion": [ + [ + "HiDiffusionSD15", + "HiDiffusionSD21", + "HiDiffusionSDXL", + "HiDiffusionSDXLTurbo" + ], + { + "title_aux": "ComfyUI HiDiffusion" + } + ], + "https://github.com/florestefano1975/ComfyUI-StabilityAI-Suite": [ + [ + "StabilityAI Suite - Creative Upscale", + "StabilityAI Suite - Creative Upscale Recover File", + "StabilityAI Suite - Image Core + Style Preset", + "StabilityAI Suite - Inpainting", + "StabilityAI Suite - Outpainting", + "StabilityAI Suite - Remove Background", + "StabilityAI Suite - SD3", + "StabilityAI Suite - Search and Replace" + ], + { + "title_aux": "ComfyUI StabilityAI Suite" + } + ], + "https://github.com/florestefano1975/comfyui-portrait-master": [ + [ + "PortraitMaster", + "PortraitMasterBaseCharacter", + "PortraitMasterMakeup", + "PortraitMasterPromptStyler", + "PortraitMasterSkinDetails", + "PortraitMasterStylePose" + ], + { + "title_aux": "comfyui-portrait-master" + } + ], + "https://github.com/florestefano1975/comfyui-prompt-composer": [ + [ + "PromptComposerCustomLists", + "PromptComposerEffect", + "PromptComposerGrouping", + "PromptComposerMerge", + "PromptComposerStyler", + "PromptComposerTextSingle", + "promptComposerTextMultiple" + ], + { + "title_aux": "comfyui-prompt-composer" + } + ], + "https://github.com/flowtyone/ComfyUI-Flowty-CRM": [ + [ + "CCMSampler", + "CRMModelLoader", + "CRMModeler", + "CRMModelerCuda", + "CRMPoseSampler", + "CRMPoserConfig", + "CRMPreprocessForPoser", + "CRMViewer" + ], + { + "title_aux": "ComfyUI-Flowty-CRM" + } + ], + "https://github.com/flowtyone/ComfyUI-Flowty-LDSR": [ + [ + "LDSRModelLoader", + "LDSRUpscale", + "LDSRUpscaler" + ], + { + "title_aux": "ComfyUI-Flowty-LDSR" + } + ], + "https://github.com/flowtyone/ComfyUI-Flowty-TripoSR": [ + [ + "TripoSRModelLoader", + "TripoSRSampler", + "TripoSRViewer" + ], + { + "title_aux": "ComfyUI-Flowty-TripoSR" + } + ], + "https://github.com/fluffydiveX/ComfyUI-hvBlockswap": [ + [ + "hvBlockSwap" + ], + { + "title_aux": "ComfyUI-hvBlockswap" + } + ], + "https://github.com/flycarl/ComfyUI-Pixelate": [ + [ + "ComfyUIPixelate" + ], + { + "title_aux": "ComfyUI-Pixelate" + } + ], + "https://github.com/flyingshutter/As_ComfyUI_CustomNodes": [ + [ + "BatchIndex_AS", + "CropImage_AS", + "Eval_AS", + "ImageMixMasked_As", + "ImageToMask_AS", + "Increment_AS", + "Int2Any_AS", + "LatentAdd_AS", + "LatentMixMasked_As", + "LatentMix_AS", + "LatentToImages_AS", + "LoadLatent_AS", + "MapRange_AS", + "MaskToImage_AS", + "Math_AS", + "NoiseImage_AS", + "Number2Float_AS", + "Number2Int_AS", + "Number_AS", + "SaveLatent_AS", + "TextToImage_AS", + "TextWildcardList_AS" + ], + { + "title_aux": "As_ComfyUI_CustomNodes" + } + ], + "https://github.com/fmatray/ComfyUI_BattlemapGrid": [ + [ + "Battlemap Grid", + "Compass", + "Map Generator", + "Map Generator(Outdoors)" + ], + { + "title_aux": "ComfyUI_BattlemapGrid" + } + ], + "https://github.com/fofr/ComfyUI-HyperSDXL1StepUnetScheduler": [ + [ + "HyperSDXL1StepUnetScheduler" + ], + { + "title_aux": "ComfyUI-HyperSDXL1StepUnetScheduler (ByteDance)" + } + ], + "https://github.com/fofr/ComfyUI-Prompter-fofrAI": [ + [ + "List sampler \ud83e\udeb4", + "Prompt from template \ud83e\udeb4" + ], + { + "title_aux": "ComfyUI-Prompter-fofrAI" + } + ], + "https://github.com/fofr/comfyui-basic-auth": [ + [ + "BasicAuthSetup" + ], + { + "title_aux": "ComfyUI-Basic-Auth" + } + ], + "https://github.com/fofr/comfyui-fofr-toolkit": [ + [ + "Incrementer \ud83e\udeb4", + "Width and height for scaling image to ideal resolution \ud83e\udeb4", + "Width and height from aspect ratio \ud83e\udeb4" + ], + { + "title_aux": "comfyui-fofr-toolkit" + } + ], + "https://github.com/forever22777/comfyui-self-guidance": [ + [ + "CLIPConditioning", + "CheckpointLoaderMixWithDiffusers", + "SelfGuidanceSampler" + ], + { + "title_aux": "Self-Guidance nodes" + } + ], + "https://github.com/fotobudka-team/comfyui-ai-faces": [ + [ + "PhotoVerification" + ], + { + "title_aux": "ComfyUI AI Faces - Photo Verification Node" + } + ], + "https://github.com/foxtrot-roger/comfyui-rf-nodes": [ + [ + "LogBool", + "LogFloat", + "LogInt", + "LogNumber", + "LogString", + "LogVec2", + "LogVec3", + "RF_AtIndexString", + "RF_BoolToString", + "RF_FloatToString", + "RF_IntToString", + "RF_JsonStyleLoader", + "RF_MergeLines", + "RF_NumberToString", + "RF_OptionsString", + "RF_RangeFloat", + "RF_RangeInt", + "RF_RangeNumber", + "RF_SavePromptInfo", + "RF_SplitLines", + "RF_TextConcatenate", + "RF_TextInput", + "RF_TextReplace", + "RF_Timestamp", + "RF_ToString", + "RF_Vec2ToString", + "RF_Vec3ToString", + "TextLine" + ], + { + "title_aux": "RF Nodes" + } + ], + "https://github.com/fpgaminer/joycaption_comfyui": [ + [ + "JJC_JoyCaption", + "JJC_JoyCaption_Custom" + ], + { + "title_aux": "JoyCaption Nodes" + } + ], + "https://github.com/fplu/comfyui_lama_with_refiner": [ + [ + "INPAINT_InpaintWithLaMaRefinerModel", + "INPAINT_LoadInpaintLaMaModel" + ], + { + "title_aux": "lama_with_refiner" + } + ], + "https://github.com/frankchieng/ComfyUI_Aniportrait": [ + [ + "AniPortrait_Audio2Video", + "AniPortrait_Audio_Path", + "AniPortrait_LoadVideoPath", + "AniPortrait_Pose_Gen_Video", + "AniPortrait_Ref_Image_Path", + "AniPortrait_Video_Gen_Pose" + ], + { + "title_aux": "ComfyUI_Aniportrait" + } + ], + "https://github.com/frankchieng/ComfyUI_MagicClothing": [ + [ + "MagicClothing_Animatediff", + "MagicClothing_Generate", + "MagicClothing_Inpainting" + ], + { + "title_aux": "ComfyUI_MagicClothing" + } + ], + "https://github.com/frankchieng/ComfyUI_llm_easyanimiate": [ + [], + { + "nodename_pattern": "^FrankChiengEasyAnimate", + "title_aux": "ComfyUI_llm_easyanimiate" + } + ], + "https://github.com/fredconex/ComfyUI-SongBloom": [ + [ + "SongBloomGenerate", + "SongBloomModelLoader" + ], + { + "title_aux": "SongBloom" + } + ], + "https://github.com/fredconex/ComfyUI-SoundFlow": [ + [ + "SoundFlow_Concatenator", + "SoundFlow_DuckCompressor", + "SoundFlow_Equalizer", + "SoundFlow_Fade", + "SoundFlow_GainPitchControl", + "SoundFlow_GetLength", + "SoundFlow_Mixer", + "SoundFlow_PreviewAudio", + "SoundFlow_SetLength", + "SoundFlow_SilenceTrimmer", + "SoundFlow_SimpleCompressor", + "SoundFlow_TrimAudio" + ], + { + "title_aux": "ComfyUI-SoundFlow" + } + ], + "https://github.com/fredconex/ComfyUI-SyncEdit": [ + [ + "SyncTextEditor" + ], + { + "title_aux": "Sync Edit" + } + ], + "https://github.com/freelifehacker/ComfyUI-ImgMask2PNG": [ + [ + "ImageMask2PNG" + ], + { + "title_aux": "ComfyUI-ImgMask2PNG" + } + ], + "https://github.com/fsdymy1024/ComfyUI_fsdymy": [ + [ + "IPAdapterLayerWeight", + "Preview Image Without Metadata", + "PreviewImageWithoutMetadata", + "Save Image Without Metadata", + "SaveImageWithoutMetadata", + "ShowText", + "ZhiPuAiNode" + ], + { + "title_aux": "ComfyUI_fsdymy" + } + ], + "https://github.com/fssorc/ComfyUI_FFT": [ + [ + "FFTNode", + "FindFFTSpot", + "InvertFFTNode", + "InvertFFTWithMask" + ], + { + "title_aux": "ComfyUI_FFT" + } + ], + "https://github.com/fssorc/ComfyUI_FaceShaper": [ + [ + "FaceAlignmentCropper", + "FaceShaper", + "FaceShaperComposite", + "FaceShaperCropper", + "FaceShaperFaceMask", + "FaceShaperLoadInsightFaceCropper", + "FaceShaperLoadMediaPipeCropper", + "FaceShaperMatchV2", + "FaceShaperModels", + "FaceShaperShowLandMarks" + ], + { + "title_aux": "ComfyUI_FaceShaper" + } + ], + "https://github.com/fssorc/ComfyUI_RopeWrapper": [ + [ + "RopeVideoCombine", + "RopeWrapper_DetectNode", + "RopeWrapper_FaceRestore", + "RopeWrapper_LoadModels", + "RopeWrapper_LoadSwapInfo", + "RopeWrapper_OptionNode", + "RopeWrapper_SaveSwapInfo", + "RopeWrapper_SwapNode", + "RopeWrapper_SwapNodeTEST" + ], + { + "title_aux": "ComfyUI_RopeWrapper" + } + ], + "https://github.com/fssorc/ComfyUI_pose_inter": [ + [ + "GenTPose", + "PoseModify", + "Pose_Inter", + "Pose_Inter_V2" + ], + { + "title_aux": "ComfyUI_pose_inter" + } + ], + "https://github.com/fuselayer/comfyui-mosaic-blur": [ + [ + "ImageMosaic" + ], + { + "title_aux": "comfyui-mosaic-blur" + } + ], + "https://github.com/g0kuvonlange/ComfyUI-Load-From-URL": [ + [ + "Load LoRA From URL", + "Load Video From URL" + ], + { + "title_aux": "ComfyUI Load From URL" + } + ], + "https://github.com/gabe-init/ComfyUI-11labs": [ + [ + "ElevenLabsNode" + ], + { + "title_aux": "ComfyUI-11labs" + } + ], + "https://github.com/gabe-init/ComfyUI-Google-Image-Search": [ + [ + "GoogleImageSearchNode" + ], + { + "title_aux": "ComfyUI-Google-Image-Search" + } + ], + "https://github.com/gabe-init/ComfyUI-Openrouter_node": [ + [ + "OpenRouterNode" + ], + { + "title_aux": "ComfyUI OpenRouter Node" + } + ], + "https://github.com/gabe-init/ComfyUI-String-Similarity": [ + [ + "StringSimilarity" + ], + { + "title_aux": "ComfyUI-String-Similarity" + } + ], + "https://github.com/game4d/ComfyUI-BDsInfiniteYou": [ + [ + "InfiniteYou_Image", + "InfiniteYou_Load" + ], + { + "title_aux": "ComfyUI-BDsInfiniteYou" + } + ], + "https://github.com/gasparuff/CustomSelector": [ + [ + "CustomSelector" + ], + { + "title_aux": "comfyui-customselector" + } + ], + "https://github.com/gelasdev/ComfyUI-FLUX-BFL-API": [ + [ + "FluxDeleteFinetune_BFL", + "FluxDevRedux_BFL", + "FluxDev_BFL", + "FluxFinetuneDetails_BFL", + "FluxFinetuneStatus_BFL", + "FluxFinetune_BFL", + "FluxKontextMax_BFL", + "FluxKontextPro_BFL", + "FluxMyFinetunes_BFL", + "FluxPro11Redux_BFL", + "FluxPro11UltraFinetune_BFL", + "FluxPro11UltraRedux_BFL", + "FluxPro11Ultra_BFL", + "FluxPro11_BFL", + "FluxProCannyFinetune_BFL", + "FluxProCanny_BFL", + "FluxProDepthFinetune_BFL", + "FluxProDepth_BFL", + "FluxProFillFinetune_BFL", + "FluxProFill_BFL", + "FluxProFinetune_BFL", + "FluxPro_BFL" + ], + { + "title_aux": "ComfyUI-FLUX-BFL-API" + } + ], + "https://github.com/gemell1/ComfyUI_GMIC": [ + [ + "GmicCliWrapper", + "GmicQtWrapper" + ], + { + "title_aux": "ComfyUI_GMIC" + } + ], + "https://github.com/geocine/geocine-comfyui": [ + [ + "Image Scale", + "Image Selector", + "LoRA Name List", + "Prompt Text", + "Seed to Noise", + "ShowTextNode", + "Text Replace" + ], + { + "title_aux": "geocine-comfyui" + } + ], + "https://github.com/georgitsenov/ComfyUI-R2": [ + [ + "S3SaveNode" + ], + { + "title_aux": "ComfyUI S3 Save Node" + } + ], + "https://github.com/ggarra13/ComfyUI-mrv2": [ + [ + "mrv2AnnotationsImageNode", + "mrv2SaveEXRImage" + ], + { + "title_aux": "ComfyUI-mrv2" + } + ], + "https://github.com/giriss/comfy-image-saver": [ + [ + "Cfg Literal", + "Checkpoint Selector", + "Int Literal", + "Sampler Selector", + "Save Image w/Metadata", + "Scheduler Selector", + "Seed Generator", + "String Literal", + "Width/Height Literal" + ], + { + "title_aux": "Save Image with Generation Metadata" + } + ], + "https://github.com/gisu/comfyui-foxpack": [ + [ + "Add_To_List", + "BaseSamplerSetup", + "Big_Prompter", + "Change_Entries_In_A_List", + "Change_Entry_From_List", + "CheckpointMetaExtractor", + "CheckpointSelector", + "Complete_Setup", + "Convert_Into", + "Negate_Boolean", + "Optional_Value_Override", + "OverrideSamplerSetup", + "Override_Value_If_Unset", + "Pick_Value_From_Dict", + "Pick_Values_From_List", + "Refine_Prompt", + "Refine_Setup", + "Remap_Values", + "Remove_Values_From_List", + "Select_By_Index", + "Select_Line_By_Index", + "Select_String_By_Index", + "SetupSelector", + "Show_Type", + "Split_Entry_In_2Chunks", + "Split_Entry_In_4Chunks", + "Split_Entry_In_6Chunks", + "Split_Entry_In_8Chunks", + "Step_Denoise", + "UniversalLatentHelper", + "Universal_VAE_Loader" + ], + { + "title_aux": "foxpack" + } + ], + "https://github.com/gitadmini/comfyui_extractstoryboards": [ + [ + "Example", + "ExtractStoryboards_xuhuan1024", + "IntBatchSize_xuhuan1024", + "IntBatch_xuhuan1024" + ], + { + "title_aux": "ExtractStoryboards" + } + ], + "https://github.com/githubYiheng/ComfyUI_Change_IMAGE_BOREDER": [ + [ + "ChangeImageBorder" + ], + { + "title_aux": "ComfyUI_Change_IMAGE_BOREDER" + } + ], + "https://github.com/githubYiheng/ComfyUI_GetFileNameFromURL": [ + [ + "GetFileNameFromURL" + ], + { + "title_aux": "ComfyUI_GetFileNameFromURL" + } + ], + "https://github.com/githubYiheng/comfyui_kmeans_filter": [ + [ + "ImageKmeansFilter" + ], + { + "title_aux": "comfyui_kmeans_filter" + } + ], + "https://github.com/githubYiheng/comfyui_meanshift_filter": [ + [ + "ImageMeanshiftFilter" + ], + { + "title_aux": "comfyui_meanshift_filter" + } + ], + "https://github.com/githubYiheng/comfyui_private_postprocessor": [ + [ + "ImageCPostprocessor", + "PrivateImageMask" + ], + { + "title_aux": "comfyui_private_postprocessor" + } + ], + "https://github.com/glibsonoran/Plush-for-ComfyUI": [ + [ + "AI Chooser", + "Add Parameters", + "AdvPromptEnhancer", + "Custom API Key", + "DalleImage", + "Enhancer", + "GPT Image", + "Gemini Image", + "Image Mixer", + "Imagen Image", + "ImgTextSwitch", + "Load Remote Models", + "LoadText|plush", + "Model-CLIP Output Switch", + "ParseJSON", + "Plush-Exif Wrangler", + "Random Image Output", + "Random Mixer", + "Random Output", + "Remove Text", + "SaveText|plush", + "Tagger", + "Text (Any)", + "Type Converter", + "mulTextSwitch" + ], + { + "title_aux": "Plush-for-ComfyUI" + } + ], + "https://github.com/glifxyz/ComfyUI-GlifNodes": [ + [ + "FilmGrain", + "FluxReduxFloatRamp", + "GlifConsistencyDecoder", + "GlifPatchConsistencyDecoderTiled", + "GlifVariable", + "HFHubEmbeddingLoader", + "HFHubLoraLoader", + "ImagePaddingAdvanced", + "ImageToMultipleOf", + "LoraLoaderFromURL", + "SDXLAspectRatio" + ], + { + "title_aux": "ComfyUI-GlifNodes" + } + ], + "https://github.com/glitchinthemetrix16/ComfyUI-Roop": [ + [ + "RoopBatchFaceSwap", + "RoopFaceSwap", + "RoopFaceSwapVideo", + "RoopFaceSwapWithEnhancer", + "RoopSendWebhookFile", + "RoopSendWebhookImage" + ], + { + "title_aux": "ComfyUI Roop Custom Nodes" + } + ], + "https://github.com/glowcone/comfyui-base64-to-image": [ + [ + "LoadImageFromBase64" + ], + { + "title_aux": "Load Image From Base64 URI" + } + ], + "https://github.com/glowcone/comfyui-string-converter": [ + [ + "StringToFloat", + "StringToInt" + ], + { + "title_aux": "String Converter" + } + ], + "https://github.com/gmorks/ComfyUI-Animagine-Prompt": [ + [ + "AnimaginePrompt", + "MultiWildcardLoader", + "MultilineTextInput", + "TextFileLoader" + ], + { + "title_aux": "ComfyUI-Animagine-Prompt" + } + ], + "https://github.com/gmorks/ComfyUI-SendToDiscord": [ + [ + "PreviewImageWithDiscord" + ], + { + "title_aux": "ComfyUI-SendToDiscord" + } + ], + "https://github.com/goburiin/nsfwrecog-comfyui": [ + [ + "NSFWDetectorNode" + ], + { + "title_aux": "nsfwrecog-comfyui" + } + ], + "https://github.com/godmt/ComfyUI-IP-Composer": [ + [ + "IPCompConceptMerge", + "IPCompConceptSubspace", + "IPCompLoadOpenCLIP", + "IPLoadConceptSubspace", + "IPSaveConceptSubspace" + ], + { + "title_aux": "ComfyUI-IP-Composer" + } + ], + "https://github.com/godmt/ComfyUI-List-Utils": [ + [ + "GODMT_AnyCast", + "GODMT_AnyToDict", + "GODMT_BatchGetByIndex", + "GODMT_BatchItemCast", + "GODMT_BatchSlice", + "GODMT_BatchToList", + "GODMT_CreateArange", + "GODMT_CreateBatch", + "GODMT_CreateLinspace", + "GODMT_CreateList", + "GODMT_CreateRange", + "GODMT_Exec", + "GODMT_GetLength", + "GODMT_GetShape", + "GODMT_GetWidgetsValues", + "GODMT_ListDir", + "GODMT_ListGetByIndex", + "GODMT_ListSlice", + "GODMT_ListToBatch", + "GODMT_MergeBatch", + "GODMT_MergeList", + "GODMT_Pack", + "GODMT_SplitString", + "GODMT_Unpack" + ], + { + "title_aux": "ComfyUI-List-Utils" + } + ], + "https://github.com/godspede/ComfyUI_Substring": [ + [ + "SubstringTheory" + ], + { + "title_aux": "ComfyUI Substring" + } + ], + "https://github.com/gokayfem/ComfyUI-Depth-Visualization": [ + [ + "DepthViewer" + ], + { + "title_aux": "ComfyUI-Depth-Visualization" + } + ], + "https://github.com/gokayfem/ComfyUI-Dream-Interpreter": [ + [ + "DreamViewer" + ], + { + "title_aux": "ComfyUI-Dream-Interpreter" + } + ], + "https://github.com/gokayfem/ComfyUI-Texture-Simple": [ + [ + "TextureViewer" + ], + { + "title_aux": "ComfyUI-Texture-Simple" + } + ], + "https://github.com/gokayfem/ComfyUI-fal-API": [ + [ + "CombinedVideoGeneration_fal", + "FluxDev_fal", + "FluxGeneral_fal", + "FluxLoraTrainer_fal", + "FluxLora_fal", + "FluxPro11_fal", + "FluxProKontextMulti_fal", + "FluxProKontextTextToImage_fal", + "FluxProKontext_fal", + "FluxPro_fal", + "FluxSchnell_fal", + "FluxUltra_fal", + "Hidreamfull_fal", + "HunyuanVideoLoraTrainer_fal", + "Ideogramv3_fal", + "Imagen4Preview_fal", + "KlingMaster_fal", + "KlingPro10_fal", + "KlingPro16_fal", + "Kling_fal", + "LLM_fal", + "LoadVideoURL", + "LtxVideoTrainer_fal", + "LumaDreamMachine_fal", + "MiniMaxSubjectReference_fal", + "MiniMaxTextToVideo_fal", + "MiniMax_fal", + "Recraft_fal", + "RunwayGen3_fal", + "Sana_fal", + "SeedEditV3_fal", + "SeedanceImageToVideo_fal", + "SeedanceTextToVideo_fal", + "Upscaler_fal", + "VLM_fal", + "Veo2ImageToVideo_fal", + "Veo3_fal", + "VideoUpscaler_fal", + "WanLoraTrainer_fal", + "WanPro_fal" + ], + { + "title_aux": "ComfyUI-fal-API" + } + ], + "https://github.com/gokayfem/ComfyUI_VLM_nodes": [ + [ + "AudioLDM2Node", + "ChatMusician", + "CreativeArtPromptGenerator", + "Joytag", + "JsonToText", + "KeywordExtraction", + "Kosmos2model", + "LLMLoader", + "LLMOptionalMemoryFreeAdvanced", + "LLMOptionalMemoryFreeSimple", + "LLMPromptGenerator", + "LLMSampler", + "LLava Loader Simple", + "LLavaOptionalMemoryFreeAdvanced", + "LLavaOptionalMemoryFreeSimple", + "LLavaPromptGenerator", + "LLavaSamplerAdvanced", + "LLavaSamplerSimple", + "LlavaClipLoader", + "MCLLaVAModel", + "MiniCPMNode", + "MolmoNode", + "MoonDream", + "Moondream2model", + "Paligemma", + "PlayMusic", + "PromptGenerateAPI", + "Qwen2VLNode", + "SaveAudioNode", + "SimpleText", + "StructuredOutput", + "Suggester", + "UformGen2QwenNode", + "ViewText" + ], + { + "title_aux": "VLM_nodes" + } + ], + "https://github.com/goldwins520/Comfyui_saveimg2webdav": [ + [ + "SaveFileToWebDAV", + "SaveImageToWebDAV" + ], + { + "title_aux": "Save Image To Webdav" + } + ], + "https://github.com/gonzalu/ComfyUI_YFG_Comical": [ + [ + "Image10Switcher_node", + "Image15Switcher_node", + "Image20Switcher_node", + "Image3Switcher_node", + "Image5Switcher_node", + "MonoClip_node", + "PixelArt_node", + "RandomOrgTrueRandomNumber_node", + "VAEDecodePreview_node", + "image2contrastMask_node", + "image2imbgg_node", + "image_halftone", + "image_histograms_node", + "image_histograms_node_compact", + "images_side_by_side", + "imgbbLoader_node", + "smartCheckpointLoader_node", + "storeURL_node", + "textMaskOverlay_node" + ], + { + "author": "Manny Gonzalez", + "description": "Utility custom nodes for special effects, image manipulation and quality of life tools.", + "nickname": "\ud83d\udc2f YFG Comical Nodes", + "title": "\ud83d\udc2f YFG Comical Nodes", + "title_aux": "\ud83d\ude38 YFG Comical Nodes" + } + ], + "https://github.com/gorillaframeai/GF_nodes": [ + [ + "GFrbmg2", + "GFrbmg2Plus" + ], + { + "title_aux": "GFrbmg2" + } + ], + "https://github.com/gorillaframeai/GF_translate": [ + [ + "GFDeepTranslate", + "GFJsonTranslate" + ], + { + "title_aux": "GF_translate" + } + ], + "https://github.com/greengerong/ComfyUI-JanusPro-PL": [ + [ + "JanusProImageGenerator", + "JanusProImageUnderstanding", + "JanusProModelLoader" + ], + { + "title_aux": "Janus-Pro ComfyUI Plugin" + } + ], + "https://github.com/greengerong/ComfyUI-Lumina-Video": [ + [ + "LuminaVideoModelLoader", + "LuminaVideoSampler", + "LuminaVideoVAEDecode" + ], + { + "title_aux": "ComfyUI-Lumina-Video" + } + ], + "https://github.com/gremlation/ComfyUI-ImageLabel": [ + [ + "gremlation:ComfyUI-ImageLabel:ImageLabel" + ], + { + "title_aux": "ComfyUI-ImageLabel" + } + ], + "https://github.com/gremlation/ComfyUI-JMESPath": [ + [ + "gremlation:ComfyUI-JMESPath" + ], + { + "title_aux": "ComfyUI-JMESPath" + } + ], + "https://github.com/gremlation/ComfyUI-ViewData": [ + [ + "gremlation:ComfyUI-ViewData:ViewData" + ], + { + "title_aux": "ComfyUI-ViewData" + } + ], + "https://github.com/gremlation/ComfyUI-jq": [ + [ + "gremlation:ComfyUI-jq" + ], + { + "title_aux": "ComfyUI-jq" + } + ], + "https://github.com/griptape-ai/ComfyUI-Griptape": [ + [ + "Griptape Agent Config: Amazon Bedrock Drivers", + "Griptape Agent Config: Amazon Bedrock [DEPRECATED]", + "Griptape Agent Config: Anthropic Drivers", + "Griptape Agent Config: Anthropic [DEPRECATED]", + "Griptape Agent Config: Azure OpenAI Drivers", + "Griptape Agent Config: Azure OpenAI [DEPRECATED]", + "Griptape Agent Config: Cohere Drivers", + "Griptape Agent Config: Custom Structure", + "Griptape Agent Config: Environment Variables", + "Griptape Agent Config: Expand", + "Griptape Agent Config: Google Drivers", + "Griptape Agent Config: Google [DEPRECATED]", + "Griptape Agent Config: Griptape Cloud", + "Griptape Agent Config: Grok Drivers", + "Griptape Agent Config: Groq Drivers", + "Griptape Agent Config: HuggingFace Drivers", + "Griptape Agent Config: HuggingFace [DEPRECATED]", + "Griptape Agent Config: LM Studio Drivers", + "Griptape Agent Config: LM Studio [DEPRECATED]", + "Griptape Agent Config: Ollama Drivers", + "Griptape Agent Config: Ollama [DEPRECATED]", + "Griptape Agent Config: OpenAI Compatible Drivers", + "Griptape Agent Config: OpenAI Compatible [DEPRECATED]", + "Griptape Agent Config: OpenAI Drivers", + "Griptape Agent Config: OpenAI [DEPRECATED]", + "Griptape Audio Transcription Driver: Groq", + "Griptape Audio Transcription Driver: OpenAI", + "Griptape Code: Run Griptape Cloud Structure", + "Griptape Code: Run Python [DEPRECATED]", + "Griptape Combine: Merge Dictionary", + "Griptape Combine: Merge Inputs", + "Griptape Combine: Merge Texts", + "Griptape Combine: RAG Module List", + "Griptape Combine: Rules List", + "Griptape Combine: String List", + "Griptape Combine: Tool List", + "Griptape Config: Environment Variables", + "Griptape Convert: Agent to Tool", + "Griptape Convert: Text to CLIP Encode", + "Griptape Convert: Text to Combo", + "Griptape Create: Agent", + "Griptape Create: Agent from Config", + "Griptape Create: CLIP Text Encode", + "Griptape Create: Image Inpainting Variation", + "Griptape Create: Image Variation", + "Griptape Create: Image from Text", + "Griptape Create: Key Value Pair", + "Griptape Create: Rules", + "Griptape Create: Text", + "Griptape Display: Artifact", + "Griptape Display: Data as Text", + "Griptape Display: Dictionary", + "Griptape Display: Image", + "Griptape Display: Text", + "Griptape Display: Text as Markdown", + "Griptape Driver: Amazon Bedrock Stable Diffusion", + "Griptape Driver: Amazon Bedrock Titan", + "Griptape Driver: Azure OpenAI Image Generation", + "Griptape Driver: Black Forest Labs Image Generation", + "Griptape Driver: Leonardo.AI", + "Griptape Driver: OpenAI Compatible Image Generation", + "Griptape Driver: OpenAI Image Generation", + "Griptape Embedding Driver: Amazon Bedrock Titan", + "Griptape Embedding Driver: Amazon SageMaker Jumpstart", + "Griptape Embedding Driver: Azure OpenAI", + "Griptape Embedding Driver: Cohere", + "Griptape Embedding Driver: Google", + "Griptape Embedding Driver: HuggingFace", + "Griptape Embedding Driver: LM Studio", + "Griptape Embedding Driver: Ollama", + "Griptape Embedding Driver: OpenAI", + "Griptape Embedding Driver: OpenAI Compatible", + "Griptape Embedding Driver: Voyage AI", + "Griptape End Workflow", + "Griptape Expand: Agent Nodes", + "Griptape Load: Audio", + "Griptape Load: Image From URL", + "Griptape Load: Text", + "Griptape Prompt Driver: Amazon Bedrock", + "Griptape Prompt Driver: Amazon SageMaker Jumpstart", + "Griptape Prompt Driver: Anthropic", + "Griptape Prompt Driver: Azure OpenAI", + "Griptape Prompt Driver: Cohere", + "Griptape Prompt Driver: Google", + "Griptape Prompt Driver: Griptape Cloud", + "Griptape Prompt Driver: Grok", + "Griptape Prompt Driver: Groq", + "Griptape Prompt Driver: HuggingFace", + "Griptape Prompt Driver: LM Studio", + "Griptape Prompt Driver: Ollama", + "Griptape Prompt Driver: OpenAI", + "Griptape Prompt Driver: OpenAI Compatible", + "Griptape RAG Query: Translate Module", + "Griptape RAG Rerank: Text Chunks Module", + "Griptape RAG Response: Footnote Prompt Module", + "Griptape RAG Response: Prompt Module", + "Griptape RAG Response: Text Chunks Module", + "Griptape RAG Retrieve: Text Loader Module", + "Griptape RAG Retrieve: Vector Store Module", + "Griptape RAG: Engine", + "Griptape Replace: Rulesets on Agent", + "Griptape Replace: Tools on Agent", + "Griptape Rerank Driver: Cohere", + "Griptape Rerank Driver: Local", + "Griptape Retrieve: Cloud Ruleset", + "Griptape Run: Agent", + "Griptape Run: Audio Transcription", + "Griptape Run: Cloud Assistant", + "Griptape Run: Image Description", + "Griptape Run: Parallel Image Description", + "Griptape Run: Parallel Prompt Task", + "Griptape Run: Prompt Task", + "Griptape Run: Task", + "Griptape Run: Text Extraction", + "Griptape Run: Text Summary", + "Griptape Run: Text to Speech", + "Griptape Run: Tool Task", + "Griptape Run: Toolkit Task", + "Griptape Save: Text", + "Griptape Set: Default Agent", + "Griptape Start Workflow", + "Griptape Text To Speech Driver: ElevenLabs", + "Griptape Text To Speech Driver: OpenAI", + "Griptape Tool: Audio Transcription", + "Griptape Tool: Calculator", + "Griptape Tool: DateTime", + "Griptape Tool: Extraction", + "Griptape Tool: FileManager", + "Griptape Tool: Griptape Cloud KnowledgeBase", + "Griptape Tool: Prompt Summary", + "Griptape Tool: Query", + "Griptape Tool: RAG", + "Griptape Tool: Text to Speech", + "Griptape Tool: VectorStore", + "Griptape Tool: WebScraper", + "Griptape Tool: WebSearch", + "Griptape Util: Create Agent Modelfile", + "Griptape Util: Create Model from Modelfile", + "Griptape Util: Remove Ollama Model", + "Griptape Util: Switch Node", + "Griptape Vector Store Driver: Amazon OpenSearch", + "Griptape Vector Store Driver: Azure MongoDB", + "Griptape Vector Store Driver: Griptape Cloud", + "Griptape Vector Store Driver: Local", + "Griptape Vector Store Driver: Marqo", + "Griptape Vector Store Driver: MongoDB Atlas", + "Griptape Vector Store Driver: PGVector", + "Griptape Vector Store Driver: Pinecone", + "Griptape Vector Store Driver: Qdrant", + "Griptape Vector Store Driver: Redis", + "Griptape Vector Store: Add Text", + "Griptape Vector Store: Query", + "Griptape WebSearch Driver: DuckDuckGo", + "Griptape WebSearch Driver: Exa", + "Griptape WebSearch Driver: Google", + "Griptape WebSearch Driver: Serper", + "Griptape WebSearch Driver: Tavily" + ], + { + "author": "Jason Schleifer", + "description": "This extension offers various nodes that allow you to work with LLMs using the Griptape Python Framework (https://griptape.ai)", + "nickname": "ComfyUI-Griptape", + "title": "ComfyUI Griptape Nodes", + "title_aux": "ComfyUI Griptape Nodes" + } + ], + "https://github.com/gseth/ControlAltAI-Nodes": [ + [ + "BooleanBasic", + "BooleanReverse", + "ChooseUpscaleModel", + "FluxAttentionCleanup", + "FluxAttentionControl", + "FluxControlNetApply", + "FluxResolutionNode", + "FluxSampler", + "FluxUnionControlNetApply", + "GetImageSizeRatio", + "HiDreamResolutionNode", + "IntegerSettings", + "IntegerSettingsAdvanced", + "NoisePlusBlend", + "PerturbationTexture", + "RegionMaskConditioning", + "RegionMaskGenerator", + "RegionMaskProcessor", + "RegionMaskValidator", + "RegionOverlayVisualizer", + "TextBridge", + "ThreeWaySwitch", + "TwoWaySwitch" + ], + { + "title_aux": "ControlAltAI Nodes" + } + ], + "https://github.com/gt732/ComfyUI-DreamWaltz-G": [ + [ + "DreamWaltzGStageOneTrainer", + "DreamWaltzGStageTwoTrainer" + ], + { + "title_aux": "ComfyUI-DreamWaltz-G" + } + ], + "https://github.com/guerreiro/comfyg-switch": [ + [ + "ComfygSwitch" + ], + { + "title_aux": "Comfyg Switch" + } + ], + "https://github.com/guill/abracadabra-comfyui": [ + [ + "AbracadabraNode", + "AbracadabraNodeDefSummary" + ], + { + "title_aux": "abracadabra-comfyui" + } + ], + "https://github.com/guyaton/guy-nodes-comfyui": [ + [ + "GuyRecommendedLatentResCalc" + ], + { + "title_aux": "guy-nodes-comfyui" + } + ], + "https://github.com/gvfarns/comfyui_gvf": [ + [ + "CheckpointLoaderWithName", + "CropToAspectRatio", + "CropToAspectRatioMinMax", + "IfElseFloat", + "IfElseInt", + "StringContains" + ], + { + "title_aux": "comfyui_gvf" + } + ], + "https://github.com/hackkhai/ComfyUI-Image-Matting": [ + [ + "ApplyMatting", + "CreateTrimap", + "MattingModelLoader" + ], + { + "title_aux": "ComfyUI-Image-Matting" + } + ], + "https://github.com/hanoixan/ComfyUI-DataBeast": [ + [ + "DBConvertToBoolean //DataBeast", + "DBConvertToFloat //DataBeast", + "DBConvertToInt //DataBeast", + "DBConvertToString //DataBeast", + "DBFloatExpression //DataBeast", + "DBGetBatchList //DataBeast", + "DBGetItem //DataBeast", + "DBLoadData //DataBeast", + "DBStringExpression //DataBeast" + ], + { + "author": "hanoixan", + "description": "This extension provides nodes for controlling data-driven processing in Comfy-UI", + "nickname": "DataBeast", + "title": "DataBeast", + "title_aux": "ComfyUI DataBeast" + } + ], + "https://github.com/hao-ai-lab/FastVideo": [ + [ + "DITConfig", + "InferenceArgs", + "LoadImagePath", + "TextEncoderConfig", + "VAEConfig", + "VideoGenerator" + ], + { + "title_aux": "ComfyUI-FastVideo" + } + ], + "https://github.com/haohaocreates/ComfyUI-HH-Image-Selector": [ + [ + "Image Selector" + ], + { + "title_aux": "ComfyUI-HH-Image-Selector" + } + ], + "https://github.com/hassan-sd/comfyui-image-prompt-loader": [ + [ + "ImagePromptLoader", + "apt", + "author", + "category", + "description", + "files", + "install_type", + "js_path", + "license", + "name", + "nodename_pattern", + "pip", + "preemptions", + "reference", + "repository", + "tags", + "title_aux", + "version" + ], + { + "title_aux": "ComfyUI Image & Prompt Loader" + } + ], + "https://github.com/havvk/ComfyUI_AIIA": [ + [ + "AIIA_E2E_Speaker_Diarization", + "AIIA_FloatProcess_InMemory", + "AIIA_FloatProcess_ToDisk", + "AIIA_GenerateSpeakerSegments", + "AIIA_Utils_Image_Concanate", + "AIIA_VideoCombine" + ], + { + "title_aux": "ComfyUI_AIIA" + } + ], + "https://github.com/hay86/ComfyUI_DDColor": [ + [ + "D_DDColor" + ], + { + "title_aux": "ComfyUI DDColor" + } + ], + "https://github.com/hay86/ComfyUI_Dreamtalk": [ + [ + "D_DreamTalk" + ], + { + "title_aux": "ComfyUI Dreamtalk" + } + ], + "https://github.com/hay86/ComfyUI_Hallo": [ + [ + "D_HalloNode" + ], + { + "title_aux": "ComfyUI Hallo" + } + ], + "https://github.com/hay86/ComfyUI_LatentSync": [ + [ + "D_LatentSyncNode" + ], + { + "title_aux": "ComfyUI LatentSync" + } + ], + "https://github.com/hay86/ComfyUI_MiniCPM-V": [ + [ + "D_MiniCPM_VQA" + ], + { + "title_aux": "ComfyUI MiniCPM-V" + } + ], + "https://github.com/hay86/ComfyUI_OpenVoice": [ + [ + "D_OpenVoice_STS", + "D_OpenVoice_TTS", + "D_OpenVoice_TTS_V2" + ], + { + "title_aux": "ComfyUI OpenVoice" + } + ], + "https://github.com/hayd-zju/ICEdit-ComfyUI-official": [ + [ + "SaveImageWebsocket" + ], + { + "title_aux": "ICEdit-ComfyUI-official" + } + ], + "https://github.com/hayde0096/Comfyui-EasySettingpipes": [ + [ + "ConvertAny", + "SamplerSetup", + "SamplerSetupUnpack" + ], + { + "title_aux": "EasySettingpipes" + } + ], + "https://github.com/hben35096/ComfyUI-ReplenishNodes": [ + [ + "Batch Image Blend", + "FLOAT Output", + "Fill Alpha", + "Get Batch Count", + "Image Align", + "Image Blend BG", + "Integer Output", + "Load CLIP Name", + "Load Ckpt Name", + "Load Lora Name", + "Load Sampler Name", + "Load Scheduler Name", + "Load UNET Name", + "Mask Levels Adjust", + "Multi Line Text", + "Multiple Image Blend", + "Multiple Image Blend 2", + "Preview Image-JPEG", + "Reference Resize", + "Seed Output", + "To RGB" + ], + { + "title_aux": "ComfyUI-ReplenishNodes" + } + ], + "https://github.com/heheok/comfyui_wan2.1_vace_infinite_helpers": [ + [ + "CyclicCharacterAndBackgroundPrompt", + "LatestVideoFromFolder", + "PrepareControlVideo" + ], + { + "title_aux": "comfyui_wan2.1_vace_infinite_helpers" + } + ], + "https://github.com/hekmon/comfyui-checkpoint-extract": [ + [ + "CLIPModelSaver", + "VAEModelSaver" + ], + { + "title_aux": "comfyui-checkpoint-extract" + } + ], + "https://github.com/hekmon/comfyui-openai-api": [ + [ + "OAIAPIChatCompletion", + "OAIAPIClient", + "OAIAPIDebug", + "OAIAPIDeveloperRole", + "OAIAPIExtraBody", + "OAIAPIFrequencyPenalty", + "OAIAPIMaxTokens", + "OAIAPIPresencePenalty", + "OAIAPISeed", + "OAIAPITemperature", + "OAIAPITopP" + ], + { + "title_aux": "ComfyUI OpenAI API" + } + ], + "https://github.com/heshengtao/comfyui_LLM_party": [ + [ + "About_us", + "AmapRegeoTool", + "AmapWeatherTool", + "Browser_display", + "CLIPTextEncode_party", + "Combine_Videos_party", + "Dingding", + "Dingding_tool", + "EasyOCR_advance", + "EasyOCR_choose", + "FeishuDownloadAudio", + "FeishuDownloadImage", + "FeishuGetHistory", + "FeishuSendMsg", + "FileOnlineDelete_gitee", + "FileOnlineStorage_gitee", + "FilePathExists", + "FolderCleaner", + "GGUFLoader", + "GeocodeTool", + "Image2Video_party", + "Images2Image", + "KG_csv_toolkit_developer", + "KG_csv_toolkit_user", + "KG_json_toolkit_developer", + "KG_json_toolkit_user", + "KG_neo_toolkit_developer", + "KG_neo_toolkit_user", + "KSampler_party", + "LLM", + "LLM_api_loader", + "LLM_local", + "LLM_local_loader", + "LLavaLoader", + "LorapathLoader", + "Lorebook", + "Mcp_tool", + "RSS_loader", + "RSS_tool", + "SpeedChange", + "URL2IMG", + "VAEDecode_party", + "accuweather_tool", + "advance_ebd_tool", + "aisuite_loader", + "any2str", + "any_switcher", + "api_function", + "api_tool", + "arxiv_tool", + "bing_loader", + "bing_tool", + "bool_logic", + "browser_use_tool", + "check_text", + "check_web_tool", + "classify_function", + "classify_function_plus", + "classify_persona", + "classify_persona_plus", + "clear_file", + "clear_model", + "custom_persona", + "custom_string_format", + "dall_e_tool", + "discord_bot", + "discord_file_monitor", + "discord_send", + "duckduckgo_loader", + "duckduckgo_tool", + "easy_GGUFLoader", + "easy_LLM_api_loader", + "easy_LLM_local_loader", + "easy_LLavaLoader", + "easy_load_llm_lora", + "easy_vlmLoader", + "ebd_tool", + "embeddings_function", + "end_anything", + "end_dialog", + "end_workflow", + "extra_parameters", + "feishu", + "feishu_tool", + "file_combine", + "file_combine_plus", + "file_path_iterator", + "files_read_tool", + "fish_tts", + "fish_whisper", + "flux_persona", + "genai_api_loader", + "get_string", + "github_tool", + "google_loader", + "google_tool", + "got_ocr", + "gpt_sovits", + "graph_md_to_html", + "html2img_function", + "ic_lora_persona", + "image_iterator", + "img2path", + "img_hosting", + "interpreter_function", + "interpreter_tool", + "interrupt_loop", + "json2text", + "json_extractor", + "json_get_value", + "json_iterator", + "json_parser", + "json_writing", + "keyword_tool", + "list_append", + "list_append_plus", + "list_extend", + "list_extend_plus", + "listen_audio", + "load_SQL_memo", + "load_bool", + "load_ebd", + "load_excel", + "load_file", + "load_file_folder", + "load_float", + "load_img_path", + "load_int", + "load_keyword", + "load_llm_lora", + "load_memo", + "load_name", + "load_openai_ebd", + "load_persona", + "load_redis_memo", + "load_url", + "load_wikipedia", + "md_to_excel", + "md_to_html", + "mini_error_correction", + "mini_flux_prompt", + "mini_flux_tag", + "mini_intent_recognition", + "mini_ocr", + "mini_party", + "mini_sd_prompt", + "mini_sd_tag", + "mini_story", + "mini_summary", + "mini_translate", + "none2false", + "omost_decode", + "omost_json2py", + "omost_setting", + "open_url_function", + "open_url_tool", + "openai_dall_e", + "openai_ebd_tool", + "openai_tts", + "openai_whisper", + "parameter_combine", + "parameter_combine_plus", + "parameter_function", + "path2img_tool", + "red_book_text_persona", + "replace_string", + "save_SQL_memo", + "save_ebd_database", + "save_memo", + "save_openai_ebd", + "save_redis_memo", + "savepersona", + "searxng_tool", + "send_to_wechat_official", + "show_text_party", + "sql_tool", + "srt2txt", + "start_anything", + "start_dialog", + "start_workflow", + "story_json_tool", + "str2float", + "str2int", + "string_combine", + "string_combine_plus", + "string_logic", + "substring", + "svg2html", + "svg2img_function", + "text2json", + "text2parameters", + "text_iterator", + "text_writing", + "time_sleep", + "time_tool", + "tool_combine", + "tool_combine_plus", + "translate_persona", + "txt2srt", + "url2img_tool", + "vlmLoader", + "weekday_tool", + "whisper_local", + "wikipedia_tool", + "work_wechat", + "work_wechat_tool", + "workflow_tool", + "workflow_transfer", + "workflow_transfer_v2" + ], + { + "title_aux": "comfyui_LLM_party" + } + ], + "https://github.com/heshengtao/comfyui_LLM_schools": [ + [ + "CausalLM_trainer", + "IA3_Arguments", + "LLM_Arguments", + "Lora_or_adapter_Arguments", + "P_or_Prompt_Arguments", + "Prefix_Arguments", + "download_dataset", + "get_dataset_name", + "split_dataset" + ], + { + "title_aux": "comfyui_LLM_schools" + } + ], + "https://github.com/hexxacubic/ComfyUI-Prompt_Library": [ + [ + "Double_Prompt_Encode", + "Multi_Wildcard_Loader", + "Prompt_Extender", + "Prompt_Library", + "Simple_Prompt_Library" + ], + { + "title_aux": "ComfyUI-Prompt_Library" + } + ], + "https://github.com/hgabha/WWAA-CustomNodes": [ + [ + "WWAA-BuildString", + "WWAA-LineCount", + "WWAA_AdvancedTextFileReader", + "WWAA_DitherNode", + "WWAA_GBCamera", + "WWAA_ImageLoader", + "WWAA_ImageToTextFile", + "WWAA_NestedLoopCounter", + "WWAA_PromptWriter", + "WWAA_SearchReplaceText", + "WWAA_Switch_Int" + ], + { + "title_aux": "WWAA-CustomNodes" + } + ], + "https://github.com/hhhzzyang/Comfyui_Lama": [ + [ + "LamaApply", + "LamaModelLoader", + "YamlConfigLoader" + ], + { + "title_aux": "Comfyui-Lama" + } + ], + "https://github.com/hiderminer/ComfyUI-HM-Utilities": [ + [ + "AutoCropImage", + "NormalizeImageWithRectangle" + ], + { + "title_aux": "ComfyUI-HM-Tools" + } + ], + "https://github.com/hieuck/ComfyUI-BiRefNet": [ + [ + "BiRefNet" + ], + { + "title_aux": "ComfyUI-BiRefNet-Fix utils" + } + ], + "https://github.com/hiforce/comfyui-hiforce-plugin": [ + [ + "HfBoolSwitchKSampleStatus", + "HfImageAutoExpansionSquare", + "HfImageToRGB", + "HfImageToRGBA", + "HfInitImageWithMaxSize", + "HfIterativeLatentUpscale", + "HfLoadImageWithCropper", + "HfLookbackSamplerLoader", + "HfLoopback", + "HfResizeImage", + "HfSampler", + "HfSamplerLoader", + "HfSamplerLoopback", + "HfSaveImage", + "HfSwitchKSampleStatus", + "HfTwoSamplersForMask", + "HfTwoStepSamplers", + "LoadImageFromURL" + ], + { + "title_aux": "Comfyui HiFORCE Plugin" + } + ], + "https://github.com/hinablue/ComfyUI_3dPoseEditor": [ + [ + "Hina.PoseEditor3D" + ], + { + "title_aux": "ComfyUI 3D Pose Editor" + } + ], + "https://github.com/hmwl/ComfyUI_zip": [ + [ + "CleanFolders", + "CompressImages", + "UnzipToInput" + ], + { + "title_aux": "ComfyUI_zip" + } + ], + "https://github.com/hnmr293/comfyui-savemem": [ + [ + "SaveImagesMemory", + "SaveLatentsMemory" + ], + { + "title_aux": "ComfyUI-SaveMem" + } + ], + "https://github.com/hodanajan/optimal-crop-resolution": [ + [ + "AspectRatioCalculator", + "ResolutionMatcher" + ], + { + "title_aux": "optimal-crop-resolution" + } + ], + "https://github.com/hoveychen/ComfyUI-MusePose-Remaster": [ + [ + "musepose_getposes", + "musepose_inference" + ], + { + "title_aux": "ComfyUI-MusePose-Remaster" + } + ], + "https://github.com/huagetai/ComfyUI-Gaffer": [ + [ + "ApplyICLight", + "CalculateNormalMap", + "GrayScaler", + "ICLightModelLoader", + "LightSource" + ], + { + "title_aux": "comfyui's gaffer(ComfyUI native implementation of IC-Light. )" + } + ], + "https://github.com/huagetai/ComfyUI_LightGradient": [ + [ + "ImageGradient", + "MaskGradient" + ], + { + "title_aux": "Light Gradient for ComfyUI" + } + ], + "https://github.com/huanngzh/ComfyUI-MVAdapter": [ + [ + "BiRefNet", + "ControlImagePreprocessor", + "ControlNetModelLoader", + "CustomLoraModelLoader", + "DiffusersMVModelMakeup", + "DiffusersMVPipelineLoader", + "DiffusersMVSampler", + "DiffusersMVSchedulerLoader", + "DiffusersMVVaeLoader", + "ImagePreprocessor", + "LdmPipelineLoader", + "LdmVaeLoader", + "ViewSelector" + ], + { + "title_aux": "ComfyUI-MVAdapter" + } + ], + "https://github.com/hubentu/ComfyUI-loras-loader": [ + [ + "DynamicLoRALoader", + "LoRAStringAdapter", + "MultiLoRAnameLoader", + "MultiLoraLoader", + "MultiTriggerLoader" + ], + { + "title_aux": "Multiple LoRA Loader for ComfyUI" + } + ], + "https://github.com/huchenlei/ComfyUI-IC-Light-Native": [ + [ + "ICLightApplyMaskGrey", + "ICLightAppply", + "VAEEncodeArgMax" + ], + { + "title_aux": "ComfyUI-IC-Light-Native" + } + ], + "https://github.com/huchenlei/ComfyUI-layerdiffuse": [ + [ + "LayeredDiffusionApply", + "LayeredDiffusionCondApply", + "LayeredDiffusionCondJointApply", + "LayeredDiffusionDecode", + "LayeredDiffusionDecodeRGBA", + "LayeredDiffusionDecodeSplit", + "LayeredDiffusionDiffApply", + "LayeredDiffusionJointApply" + ], + { + "title_aux": "ComfyUI-layerdiffuse (layerdiffusion)" + } + ], + "https://github.com/huchenlei/ComfyUI-openpose-editor": [ + [ + "huchenlei.LoadOpenposeJSON" + ], + { + "title_aux": "ComfyUI-openpose-editor" + } + ], + "https://github.com/huchenlei/ComfyUI_DanTagGen": [ + [ + "PromptDanTagGen" + ], + { + "title_aux": "ComfyUI_DanTagGen" + } + ], + "https://github.com/huchenlei/ComfyUI_densediffusion": [ + [ + "DenseDiffusionAddCondNode", + "DenseDiffusionApplyNode" + ], + { + "title_aux": "ComfyUI DenseDiffusion" + } + ], + "https://github.com/huchenlei/ComfyUI_omost": [ + [ + "OmostDenseDiffusionLayoutNode", + "OmostGreedyBagsTextEmbeddingNode", + "OmostLLMChatNode", + "OmostLLMHTTPServerNode", + "OmostLLMLoaderNode", + "OmostLayoutCondNode", + "OmostLoadCanvasConditioningNode", + "OmostLoadCanvasPythonCodeNode", + "OmostRenderCanvasConditioningNode" + ], + { + "title_aux": "ComfyUI_omost" + } + ], + "https://github.com/hughescr/ComfyUI-OpenPose-Keypoint-Extractor": [ + [ + "Openpose Keypoint Extractor" + ], + { + "title_aux": "OpenPose Keypoint Extractor" + } + ], + "https://github.com/hugobb/FastGAN-ComfyUI-Node": [ + [ + "GenerateImages", + "LoadFastGAN", + "LoadLatent", + "SampleLatent", + "SaveLatent" + ], + { + "title_aux": "fastgan-comfyui" + } + ], + "https://github.com/huixingyun/ComfyUI-HX-Captioner": [ + [ + "HXOllamaCaptioner" + ], + { + "title_aux": "ComfyUI-HX-Captioner" + } + ], + "https://github.com/huixingyun/ComfyUI-HX-Pimg": [ + [ + "SaveImageWithPromptsWebsocket" + ], + { + "title_aux": "ComfyUI-HX-Pimg" + } + ], + "https://github.com/hunzmusic/ComfyUI-IG2MV": [ + [ + "DiffusersIGMVModelMakeup", + "DiffusersIGMVSampler" + ], + { + "title_aux": "ComfyUI-IG2MV" + } + ], + "https://github.com/hustille/ComfyUI_Fooocus_KSampler": [ + [ + "KSampler With Refiner (Fooocus)" + ], + { + "title_aux": "ComfyUI_Fooocus_KSampler" + } + ], + "https://github.com/hustille/ComfyUI_hus_utils": [ + [ + "3way Prompt Styler", + "Batch State", + "Date Time Format", + "Debug Extra", + "Fetch widget value", + "Text Hash" + ], + { + "title_aux": "hus' utils for ComfyUI" + } + ], + "https://github.com/hvppycoding/comfyui-random-sampler-scheduler-steps": [ + [ + "RandomSamplerSchedulerSteps" + ], + { + "title_aux": "RandomSamplerSchedulerSteps for ComfyUI" + } + ], + "https://github.com/hwhaocool/ComfyUI-Select-Any": [ + [ + "SelectAnyValues" + ], + { + "title_aux": "ComfyUI-Select-Any" + } + ], + "https://github.com/hybskgks28275/ComfyUI-hybs-nodes": [ + [ + "Random Resolution Selector", + "Resolution Selector", + "Seed List Generator" + ], + { + "title_aux": "ComfyUI-hybs-nodes" + } + ], + "https://github.com/hyunamy/comfy-ui-on-complete-email-me": [ + [ + "OnCompleteEmailMe", + "OnCompletePlaySound", + "OnCompleteWebhook" + ], + { + "title_aux": "Comfy-UI on-complete-email-me" + } + ], + "https://github.com/iDAPPA/ComfyUI-AMDGPUMonitor": [ + [ + "AMDGPUMonitor" + ], + { + "title_aux": "AMD GPU Monitor for ComfyUI" + } + ], + "https://github.com/iFREEGROUP/comfyui-undistort": [ + [ + "IG_LoadCheckerboardImageForCalibrateCamera", + "IG_MatrixAndDistCoefToText", + "IG_Undistort" + ], + { + "title_aux": "comfyui-undistort" + } + ], + "https://github.com/iacoposk8/ComfyUI-Fooocus-Inpaint-Wrapper": [ + [ + "AlignYourStepsScheduler", + "BasicScheduler", + "CLIPLoader", + "CLIPMergeSimple", + "CLIPSave", + "CLIPSetLastLayer", + "CLIPTextEncode", + "CLIPTextEncodeSDXL", + "CLIPTextEncodeSDXLRefiner", + "CLIPVisionEncode", + "CLIPVisionLoader", + "Canny", + "CheckpointLoader", + "CheckpointLoaderSimple", + "CheckpointSave", + "ConditioningAverage", + "ConditioningCombine", + "ConditioningConcat", + "ConditioningSetArea", + "ConditioningSetAreaPercentage", + "ConditioningSetMask", + "ConditioningSetTimestepRange", + "ConditioningZeroOut", + "ControlNetApply", + "ControlNetApplyAdvanced", + "ControlNetLoader", + "CropMask", + "DiffControlNetLoader", + "DiffusersLoader", + "DualCLIPLoader", + "EmptyImage", + "EmptyLatentImage", + "ExponentialScheduler", + "FeatherMask", + "FlipSigmas", + "FooocusInpaintWrapper", + "FreeU", + "FreeU_V2", + "GLIGENLoader", + "GLIGENTextBoxApply", + "GrowMask", + "HyperTile", + "HypernetworkLoader", + "ImageBatch", + "ImageBlend", + "ImageBlur", + "ImageColorToMask", + "ImageCompositeMasked", + "ImageCrop", + "ImageInvert", + "ImageOnlyCheckpointLoader", + "ImageOnlyCheckpointSave", + "ImagePadForOutpaint", + "ImageQuantize", + "ImageScale", + "ImageScaleBy", + "ImageScaleToTotalPixels", + "ImageSharpen", + "ImageToMask", + "ImageUpscaleWithModel", + "InpaintModelConditioning", + "InvertMask", + "JoinImageWithAlpha", + "KSampler", + "KSamplerAdvanced", + "KSamplerSelect", + "KarrasScheduler", + "LatentAdd", + "LatentBatch", + "LatentBatchSeedBehavior", + "LatentBlend", + "LatentComposite", + "LatentCompositeMasked", + "LatentCrop", + "LatentFlip", + "LatentFromBatch", + "LatentInterpolate", + "LatentMultiply", + "LatentRotate", + "LatentSubtract", + "LatentUpscale", + "LatentUpscaleBy", + "LoadImage", + "LoadImageMask", + "LoadLatent", + "LoraLoader", + "LoraLoaderModelOnly", + "MaskComposite", + "MaskToImage", + "ModelMergeAdd", + "ModelMergeBlocks", + "ModelMergeSimple", + "ModelMergeSubtract", + "ModelSamplingContinuousEDM", + "ModelSamplingDiscrete", + "PatchModelAddDownscale", + "PerpNeg", + "PhotoMakerEncode", + "PhotoMakerLoader", + "PolyexponentialScheduler", + "PorterDuffImageComposite", + "PreviewImage", + "RebatchImages", + "RebatchLatents", + "RepeatImageBatch", + "RepeatLatentBatch", + "RescaleCFG", + "SDTurboScheduler", + "SD_4XUpscale_Conditioning", + "SVD_img2vid_Conditioning", + "SamplerCustom", + "SamplerDPMPP_2M_SDE", + "SamplerDPMPP_SDE", + "SamplerTCD", + "SaveAnimatedPNG", + "SaveAnimatedWEBP", + "SaveImage", + "SaveLatent", + "SelfAttentionGuidance", + "SetLatentNoiseMask", + "SolidMask", + "SplitImageWithAlpha", + "SplitSigmas", + "StableZero123_Conditioning", + "StableZero123_Conditioning_Batched", + "StyleModelApply", + "StyleModelLoader", + "TomePatchModel", + "UNETLoader", + "UpscaleModelLoader", + "VAEDecode", + "VAEDecodeTiled", + "VAEEncode", + "VAEEncodeForInpaint", + "VAEEncodeTiled", + "VAELoader", + "VAESave", + "VPScheduler", + "VideoLinearCFGGuidance", + "unCLIPCheckpointLoader", + "unCLIPConditioning" + ], + { + "title_aux": "ComfyUI Fooocus Inpaint Wrapper" + } + ], + "https://github.com/ialhabbal/OcclusionMask": [ + [ + "BatchLoadImages", + "ImageOcclusion" + ], + { + "title_aux": "OcclusionMask" + } + ], + "https://github.com/iamandeepsandhu/ComfyUI-NSFW-Check": [ + [ + "NSFWScore" + ], + { + "title_aux": "NSFW Check for ComfyUI" + } + ], + "https://github.com/icesun963/ComfyUI_HFDownLoad": [ + [ + "Apply EasyOCR V2", + "HFDownLoad_Tool", + "LayerMask: SegmentAnythingUltra V2.1", + "LayerUtility: LaMa V2" + ], + { + "author": "chflame", + "description": "A set of nodes for ComfyUI that can composite layer and mask to achieve Photoshop like functionality.", + "nickname": "LayerStyle", + "title": "LayerStyle", + "title_aux": "HFDownLoad Node for ComfyUI" + } + ], + "https://github.com/ichabodcole/ComfyUI-Ichis-Pack": [ + [ + "ICHIS_Aspect_Ratio_Plus", + "ICHIS_Extract_Tags", + "ICHIS_Text_Selector" + ], + { + "title_aux": "ComfyUI-Ichis-Pack" + } + ], + "https://github.com/idrirap/ComfyUI-Lora-Auto-Trigger-Words": [ + [ + "FusionText", + "LoraListNames", + "LoraLoaderAdvanced", + "LoraLoaderStackedAdvanced", + "LoraLoaderStackedVanilla", + "LoraLoaderVanilla", + "LoraTagsOnly", + "Randomizer", + "TagsFormater", + "TagsSelector", + "TextInputBasic" + ], + { + "title_aux": "ComfyUI-Lora-Auto-Trigger-Words" + } + ], + "https://github.com/iemesowum/ComfyUI_IsaacNodes": [ + [ + "I_AmplitudeToWeights", + "I_BinaryAmplitudeGate", + "I_UnmixAudio", + "I_WeightsListToWeights" + ], + { + "author": "Isaac Emesowum", + "description": "This extension offers automatic drums extraction from audio files, as well as a few helper nodes to support my audio synchronization AnimateDiff workflows.", + "nickname": "Isaac's Nodes", + "title": "Isaac's Nodes", + "title_aux": "Isaac's Nodes" + } + ], + "https://github.com/if-ai/ComfyUI-IF_AI_Dreamtalk": [ + [ + "IF_DreamTalk" + ], + { + "title_aux": "IF_Dreamtalk" + } + ], + "https://github.com/if-ai/ComfyUI-IF_AI_HFDownloaderNode": [ + [ + "IF_HFDownload", + "IF_HFDownloadNode" + ], + { + "title_aux": "IF_AI_HFDownloaderNode" + } + ], + "https://github.com/if-ai/ComfyUI-IF_AI_ParlerTTSNode": [ + [ + "IF_ParlerTTS" + ], + { + "title_aux": "IF_ParlerTTSNode" + } + ], + "https://github.com/if-ai/ComfyUI-IF_AI_WishperSpeechNode": [ + [ + "IF_WhisperSpeech" + ], + { + "title_aux": "IF_AI_WishperSpeechNode" + } + ], + "https://github.com/if-ai/ComfyUI-IF_AI_tools": [ + [ + "IF_ChatPrompt", + "IF_DisplayOmni", + "IF_DisplayText", + "IF_DisplayTextWildcard", + "IF_ImagePrompt", + "IF_JoinText", + "IF_LoadImagesS", + "IF_PromptMkr", + "IF_SaveText", + "IF_StepCounter", + "IF_TextTyper", + "IF_VisualizeGraph", + "IF_tools_LoadImagesS" + ], + { + "title_aux": "IF_AI_tools" + } + ], + "https://github.com/if-ai/ComfyUI-IF_DatasetMkr": [ + [ + "IF_DatasetMkr", + "IF_HyDatasetMkr" + ], + { + "title_aux": "IF_DatasetMkr" + } + ], + "https://github.com/if-ai/ComfyUI-IF_Gemini": [ + [ + "IFGeminiNode" + ], + { + "title_aux": "IF_Gemini" + } + ], + "https://github.com/if-ai/ComfyUI-IF_LLM": [ + [ + "IF_DisplayText", + "IF_JoinText", + "IF_LLM", + "IF_LLM_DisplayOmni", + "IF_LLM_DisplayText", + "IF_LLM_DisplayTextWildcard", + "IF_LLM_JoinText", + "IF_LLM_ListModels", + "IF_LLM_LoadImagesS", + "IF_LLM_SaveText", + "IF_LLM_TextTyper", + "IF_LoadImagesS", + "IF_TextTyper", + "IF_saveText", + "ListModelsNode" + ], + { + "title_aux": "IF_LLM" + } + ], + "https://github.com/if-ai/ComfyUI-IF_MemoAvatar": [ + [ + "IF_MemoAvatar", + "IF_MemoCheckpointLoader" + ], + { + "title_aux": "IF_MemoAvatar" + } + ], + "https://github.com/if-ai/ComfyUI-IF_Trellis": [ + [ + "IF_TrellisCheckpointLoader", + "IF_TrellisImageTo3D" + ], + { + "title_aux": "IF_Trellis" + } + ], + "https://github.com/if-ai/ComfyUI-IF_VideoPrompts": [ + [ + "VideoPromptNode" + ], + { + "title_aux": "IF_VideoPrompts" + } + ], + "https://github.com/if-ai/ComfyUI-WanResolutionSelector": [ + [ + "VideoResolutionSelector" + ], + { + "title_aux": "ComfyUI-WanResolutionSelector" + } + ], + "https://github.com/if-ai/ComfyUI_IF_AI_LoadImages": [ + [ + "IF_LoadImagesS" + ], + { + "title_aux": "IF_AI_LoadImages" + } + ], + "https://github.com/ifmylove2011/comfyui-missed-tool": [ + [ + "ImageQueueLoader", + "LoadImageA", + "LoraLoad", + "LoraMerge", + "LoraSaver", + "ScaleMultilplePixels", + "TrimBG", + "TrimBGAdvanced", + "TxtSave" + ], + { + "title_aux": "comfyui-missed-tool" + } + ], + "https://github.com/ihmily/ComfyUI-Light-Tool": [ + [ + "Light-Tool: AddBackground", + "Light-Tool: AddBackgroundV2", + "Light-Tool: BoundingBoxCropping", + "Light-Tool: Calculate", + "Light-Tool: ConvertNumType", + "Light-Tool: CropImage", + "Light-Tool: DeserializeJsonString", + "Light-Tool: GetImageSize", + "Light-Tool: GetImagesCount", + "Light-Tool: Hex2RGB", + "Light-Tool: ImageConcat", + "Light-Tool: ImageMaskApply", + "Light-Tool: ImageOverlay", + "Light-Tool: ImageToMask", + "Light-Tool: InputText", + "Light-Tool: InputTextList", + "Light-Tool: InvertMask", + "Light-Tool: IsTransparent", + "Light-Tool: KeyValue", + "Light-Tool: LoadImage", + "Light-Tool: LoadImageFromURL", + "Light-Tool: LoadImagesFromDir", + "Light-Tool: LoadMetadataFromURL", + "Light-Tool: LoadVideo", + "Light-Tool: MaskBoundingBoxCropping", + "Light-Tool: MaskContourExtractor", + "Light-Tool: MaskImageToTransparent", + "Light-Tool: MaskToImage", + "Light-Tool: MorphologicalTF", + "Light-Tool: PhantomTankEffect", + "Light-Tool: PreviewVideo", + "Light-Tool: RGB2Hex", + "Light-Tool: RGB2RGBA", + "Light-Tool: RGBA2RGB", + "Light-Tool: ResizeImage", + "Light-Tool: ResizeImageByMaxSize", + "Light-Tool: ResizeImageByRatio", + "Light-Tool: ResizeImageV2", + "Light-Tool: SaveMetadata", + "Light-Tool: SaveToAliyunOSS", + "Light-Tool: SaveVideo", + "Light-Tool: ScaleImage", + "Light-Tool: SerializeJsonObject", + "Light-Tool: ShowText", + "Light-Tool: SimpleImageOverlay", + "Light-Tool: SimpleTextConnect", + "Light-Tool: SolidColorBackground", + "Light-Tool: TextConnect", + "Light-Tool: UpscaleImage" + ], + { + "author": "Hmily", + "description": "An awesome light tool nodes for ComfyUI.", + "nickname": "ComfyUI-Light-Tool", + "title": "ComfyUI-Light-Tool", + "title_aux": "ComfyUI-Light-Tool" + } + ], + "https://github.com/illuminatianon/comfyui-csvwildcards": [ + [ + "CSVWildcardNode", + "DisplayTextNode" + ], + { + "title_aux": "CSV Wildcard Node for ComfyUI" + } + ], + "https://github.com/imb101/ComfyUI-FaceSwap": [ + [ + "FaceSwapNode" + ], + { + "title_aux": "FaceSwap" + } + ], + "https://github.com/inflamously/comfyui-prompt-enhancer": [ + [ + "PROMPT_ENHANCER", + "PROMPT_ENHANCER_CHAIN_CONTROL", + "PROMPT_ENHANCER_CHAIN_RANDOM", + "PROMPT_ENHANCER_REPROMPT" + ], + { + "title_aux": "comfyui-prompt-enhancer" + } + ], + "https://github.com/injet-zhou/comfyui_extra_api": [ + [ + "SimpleGenImageInterface" + ], + { + "title_aux": "comfyui_extra_api" + } + ], + "https://github.com/inventorado/ComfyUI_NNT": [ + [ + "NntAnalyzeInferenceMetrics", + "NntAnalyzeModel", + "NntCompileModel", + "NntDatasetToImageTensor", + "NntDatasetToTargetTensor", + "NntDatasetToTensor", + "NntDatasetToTextTensor", + "NntDefineActivationLayer", + "NntDefineAlibiPositionalBias", + "NntDefineConvLayer", + "NntDefineDenseLayer", + "NntDefineFlattenLayer", + "NntDefineGRULayer", + "NntDefineLSTMLayer", + "NntDefineLinearAttention", + "NntDefineLocalAttention", + "NntDefineMultiheadAttention", + "NntDefineNormLayer", + "NntDefinePoolingLayer", + "NntDefinePositionalEncoding", + "NntDefineRNNLayer", + "NntDefineReformerAttention", + "NntDefineRelativePositionBias", + "NntDefineReshapeLayer", + "NntDefineRotaryPositionalEmbedding", + "NntDefineTransformerEncoderLayer", + "NntDefineTransformerXLAttention", + "NntDefineVanillaAttention", + "NntEditModelLayers", + "NntEvaluatePredictions", + "NntFileLoader", + "NntFineTuneModel", + "NntHuggingFaceDataLoader", + "NntImageToTensor", + "NntInference", + "NntInputLayer", + "NntLoadModel", + "NntMergeExtendModel", + "NntPlotTensors", + "NntRandomTensorGenerator", + "NntSHAPSummaryNode", + "NntSaveModel", + "NntShowLayerStack", + "NntShowModelInfo", + "NntTensorElementToImage", + "NntTensorOperations", + "NntTensorSlice", + "NntTensorToText", + "NntTextBatchProcessor", + "NntTextToTensor", + "NntTimeSeriesDataLoader", + "NntTorchvisionDataLoader", + "NntTorchvisionDatasets", + "NntTrainModel", + "NntTrainingHyperparameters", + "NntVisualizeConfidenceScores", + "NntVisualizeGraph", + "NntVisualizePredictionMetrics", + "NntVisualizeTrainingMetrics" + ], + { + "title_aux": "ComfyUI Neural Network Toolkit NNT " + } + ], + "https://github.com/irreveloper/ComfyUI-DSD": [ + [ + "DSDGeminiPromptEnhancer", + "DSDImageGenerator", + "DSDModelDownloader", + "DSDModelLoader", + "DSDModelSelector", + "DSDResizeSelector" + ], + { + "title_aux": "ComfyUI-DSD" + } + ], + "https://github.com/iwanders/ComfyUI_nodes": [ + [ + "IW_JsonPickItem", + "IW_ModelHook", + "IW_StringConcat", + "IW_StringFromInt", + "IW_StringNode", + "IW_StringPrint", + "IW_StringReplace", + "IW_StringSave", + "IW_TokenizerVocab" + ], + { + "title_aux": "iwanders/ComfyUI_nodes" + } + ], + "https://github.com/jacklukai/ComfyUI_DeployCash": [ + [ + "DeployCash", + "DeployCash_saveImage", + "DeployCash_textInput" + ], + { + "title_aux": "ComfyUI_DeployCash" + } + ], + "https://github.com/jags111/ComfyUI_Jags_Audiotools": [ + [ + "BatchJoinAudio", + "BatchToList", + "BitCrushAudioFX", + "BulkVariation", + "ChorusAudioFX", + "ClippingAudioFX", + "CompressorAudioFX", + "ConcatAudioList", + "ConvolutionAudioFX", + "CutAudio", + "DelayAudioFX", + "DistortionAudioFX", + "DuplicateAudio", + "GainAudioFX", + "GenerateAudioSample", + "GenerateAudioWave", + "GetAudioFromFolderIndex", + "GetSingle", + "GetStringByIndex", + "HighShelfFilter", + "HighpassFilter", + "ImageToSpectral", + "InvertAudioFX", + "JoinAudio", + "LadderFilter", + "LimiterAudioFX", + "ListToBatch", + "LoadAudioDir", + "LoadAudioFile", + "LoadAudioModel (DD)", + "LoadVST3", + "LowShelfFilter", + "LowpassFilter", + "MP3CompressorAudioFX", + "MixAudioTensors", + "NoiseGateAudioFX", + "OTTAudioFX", + "PeakFilter", + "PhaserEffectAudioFX", + "PitchShiftAudioFX", + "PlotSpectrogram", + "PreviewAudioFile", + "PreviewAudioTensor", + "ResampleAudio", + "ReverbAudioFX", + "ReverseAudio", + "SaveAudioTensor", + "SequenceVariation", + "SliceAudio", + "SoundPlayer", + "StretchAudio", + "samplerate" + ], + { + "author": "jags111", + "description": "This extension offers various audio generation tools", + "nickname": "Audiotools", + "title": "Jags_Audiotools", + "title_aux": "Jags_Audiotools" + } + ], + "https://github.com/jags111/ComfyUI_Jags_VectorMagic": [ + [ + "CircularVAEDecode", + "JagsCLIPSeg", + "JagsClipseg", + "JagsCombineMasks", + "SVG", + "YoloSEGdetectionNode", + "YoloSegNode", + "color_drop", + "xy_Tiling_KSampler" + ], + { + "author": "jags111", + "description": "This extension offers various vector manipulation and generation tools", + "nickname": "Jags_VectorMagic", + "title": "Jags_VectorMagic", + "title_aux": "Jags_VectorMagic" + } + ], + "https://github.com/jags111/efficiency-nodes-comfyui": [ + [ + "AnimateDiff Script", + "Apply ControlNet Stack", + "Control Net Stacker", + "Eff. Loader SDXL", + "Efficient Loader", + "HighRes-Fix Script", + "Image Overlay", + "Join XY Inputs of Same Type", + "KSampler (Efficient)", + "KSampler Adv. (Efficient)", + "KSampler SDXL (Eff.)", + "LatentUpscaler", + "LoRA Stack to String converter", + "LoRA Stacker", + "Manual XY Entry Info", + "NNLatentUpscale", + "Noise Control Script", + "Pack SDXL Tuple", + "Tiled Upscaler Script", + "Unpack SDXL Tuple", + "XY Input: Add/Return Noise", + "XY Input: Aesthetic Score", + "XY Input: CFG Scale", + "XY Input: Checkpoint", + "XY Input: Clip Skip", + "XY Input: Control Net", + "XY Input: Control Net Plot", + "XY Input: Denoise", + "XY Input: LoRA", + "XY Input: LoRA Plot", + "XY Input: LoRA Stacks", + "XY Input: Manual XY Entry", + "XY Input: Prompt S/R", + "XY Input: Refiner On/Off", + "XY Input: Sampler/Scheduler", + "XY Input: Seeds++ Batch", + "XY Input: Steps", + "XY Input: VAE", + "XY Plot" + ], + { + "title_aux": "Efficiency Nodes for ComfyUI Version 2.0+" + } + ], + "https://github.com/jaimitoes/ComfyUI_Wan2_1_lora_trainer": [ + [ + "MusubiCompileSettings", + "MusubiMemorySettings", + "MusubiSamplingSettings", + "WanCacheLatents", + "WanCacheTextEncoder", + "WanDatasetConfig", + "WanLoRATrainer" + ], + { + "title_aux": "ComfyUI_Wan2_1_lora_trainer" + } + ], + "https://github.com/jakechai/ComfyUI-JakeUpgrade": [ + [ + "Animation Prompt JK", + "Animation Value JK", + "Base Image Parameters Extract JK", + "Base Model Parameters Extract JK", + "Base Model Parameters JK", + "Base Model Parameters SD3API JK", + "Base Model Pipe Extract JK", + "Base Model Pipe JK", + "Bool Binary And JK", + "Bool Binary OR JK", + "CM_BoolBinaryOperation JK", + "CM_BoolToInt JK", + "CM_BoolUnaryOperation JK", + "CM_BreakoutVec2 JK", + "CM_BreakoutVec3 JK", + "CM_BreakoutVec4 JK", + "CM_ComposeVec2 JK", + "CM_ComposeVec3 JK", + "CM_ComposeVec4 JK", + "CM_FillVec2 JK", + "CM_FillVec3 JK", + "CM_FillVec4 JK", + "CM_FloatBinaryCondition JK", + "CM_FloatBinaryOperation JK", + "CM_FloatToInt JK", + "CM_FloatToNumber JK", + "CM_FloatUnaryCondition JK", + "CM_FloatUnaryOperation JK", + "CM_IntBinaryCondition JK", + "CM_IntBinaryOperation JK", + "CM_IntToBool JK", + "CM_IntToFloat JK", + "CM_IntToNumber JK", + "CM_IntUnaryCondition JK", + "CM_IntUnaryOperation JK", + "CM_NumberBinaryCondition JK", + "CM_NumberBinaryOperation JK", + "CM_NumberToFloat JK", + "CM_NumberToInt JK", + "CM_NumberUnaryCondition JK", + "CM_NumberUnaryOperation JK", + "CM_PromptCombine_JK", + "CM_StringBinaryCondition_JK", + "CM_Vec2BinaryCondition JK", + "CM_Vec2BinaryOperation JK", + "CM_Vec2FloatOperation_JK", + "CM_Vec2ToFloatBinaryOperation JK", + "CM_Vec2ToFloatUnaryOperation JK", + "CM_Vec2UnaryCondition JK", + "CM_Vec2UnaryOperation JK", + "CM_Vec3BinaryCondition JK", + "CM_Vec3BinaryOperation JK", + "CM_Vec3FloatOperation_JK", + "CM_Vec3ToFloatBinaryOperation JK", + "CM_Vec3ToFloatUnaryOperation JK", + "CM_Vec3UnaryCondition JK", + "CM_Vec3UnaryOperation JK", + "CM_Vec4BinaryCondition JK", + "CM_Vec4BinaryOperation JK", + "CM_Vec4FloatOperation_JK", + "CM_Vec4ToFloatBinaryOperation JK", + "CM_Vec4ToFloatUnaryOperation JK", + "CM_Vec4UnaryCondition JK", + "CM_Vec4UnaryOperation JK", + "CR Apply ControlNet JK", + "CR Apply LoRA Stack JK", + "CR Apply Multi-ControlNet Adv JK", + "CR Apply Multi-ControlNet JK", + "CR Aspect Ratio JK", + "CR Boolean JK", + "CR Clip Input Switch JK", + "CR Conditioning Input Switch JK", + "CR ControlNet Input Switch JK", + "CR ControlNet Loader JK", + "CR ControlNet Stack Input Switch JK", + "CR Float Input Switch JK", + "CR Guider Input Switch JK", + "CR Image Input Switch JK", + "CR Impact Pipe Input Switch JK", + "CR Int Input Switch JK", + "CR Latent Input Switch JK", + "CR LoRA Stack JK", + "CR Load LoRA JK", + "CR Mask Input Switch JK", + "CR Mesh Input Switch JK", + "CR Model Input Switch JK", + "CR Multi-ControlNet Param Stack JK", + "CR Multi-ControlNet Stack JK", + "CR Noise Input Switch JK", + "CR Orbit Pose Input Switch JK", + "CR Pipe Input Switch JK", + "CR Ply Input Switch JK", + "CR SD1.5 Aspect Ratio JK", + "CR SD3 Aspect Ratio JK", + "CR SDXL Aspect Ratio JK", + "CR Sampler Input Switch JK", + "CR Sigmas Input Switch JK", + "CR Text Input Switch JK", + "CR TriMesh Input Switch JK", + "CR VAE Input Switch JK", + "Ckpt Loader JK", + "Color Grading JK", + "Detailer Parameters JK", + "Embedding Picker JK", + "Embedding Picker Multi JK", + "Empty Latent Color JK", + "Evaluate Examples JK", + "Evaluate Floats JK", + "Evaluate Ints JK", + "Evaluate Strings JK", + "Get OrbitPoses From List JK", + "Get Size JK", + "Guidance Default JK", + "HintImageEnchance JK", + "Hy3D Cam Config 20to21 JK", + "IPAAdapterFaceIDBatch", + "IPAdapter", + "IPAdapterAdvanced", + "IPAdapterBatch", + "IPAdapterClipVisionEnhancer", + "IPAdapterClipVisionEnhancerBatch", + "IPAdapterCombineEmbeds", + "IPAdapterCombineParams", + "IPAdapterCombineWeights", + "IPAdapterEmbeds", + "IPAdapterEmbedsBatch", + "IPAdapterEncoder", + "IPAdapterFaceID", + "IPAdapterFaceIDKolors", + "IPAdapterFromParams", + "IPAdapterInsightFaceLoader", + "IPAdapterLoadEmbeds", + "IPAdapterMS", + "IPAdapterModelLoader", + "IPAdapterNoise", + "IPAdapterPreciseComposition", + "IPAdapterPreciseCompositionBatch", + "IPAdapterPreciseStyleTransfer", + "IPAdapterPreciseStyleTransferBatch", + "IPAdapterPromptScheduleFromWeightsStrategy", + "IPAdapterRegionalConditioning", + "IPAdapterSaveEmbeds", + "IPAdapterStyleComposition", + "IPAdapterStyleCompositionBatch", + "IPAdapterTiled", + "IPAdapterTiledBatch", + "IPAdapterUnifiedLoader", + "IPAdapterUnifiedLoaderCommunity", + "IPAdapterUnifiedLoaderFaceID", + "IPAdapterWeights", + "IPAdapterWeightsFromStrategy", + "Image Crop By Mask Resolution Grp JK", + "Image Crop by Mask Params JK", + "Image Crop by Mask Resolution JK", + "Image Remove Alpha JK", + "Image Resize Mode JK", + "Image Upscale Parameters Extract JK", + "Inject Noise Params JK", + "Is Mask Empty JK", + "Ksampler Parameters Default JK", + "Ksampler Parameters JK", + "Latent Crop Offset JK", + "Latent Upscale Parameters Extract JK", + "Load Image With Alpha JK", + "Load Image With Metadata JK", + "Load String List From JSON JK", + "Make Image Grid JK", + "Metadata Pipe Extract JK", + "Metadata Pipe JK", + "NodesState JK", + "Noise Injection Parameters JK", + "Noise Injection Pipe Extract JK", + "OpenDWPose_JK", + "Orbit Poses JK", + "OrbitLists to OrbitPoses JK", + "OrbitPoses to OrbitLists JK", + "Pipe End JK", + "PrepImageForClipVision", + "Project Setting JK", + "Random Beats JK", + "Refine 1 Parameters Extract JK", + "Refine 2 Parameters Extract JK", + "Refine Model Parameters JK", + "Refine Pipe Extract JK", + "Refine Pipe JK", + "Remove Input JK", + "Reroute Ckpt JK", + "Reroute List JK", + "Reroute Resize JK", + "Reroute Sampler JK", + "Reroute String JK", + "Reroute Upscale JK", + "Reroute Vae JK", + "Rough Outline JK", + "SD3 Prompts Switch JK", + "SDXL Target Res JK", + "SDXLPromptStylerAll", + "SDXLPromptStylerHorror", + "SDXLPromptStylerMisc", + "SDXLPromptStylerbyArtist", + "SDXLPromptStylerbyCamera", + "SDXLPromptStylerbyComposition", + "SDXLPromptStylerbyCyberpunkSurrealism", + "SDXLPromptStylerbyDepth", + "SDXLPromptStylerbyDiva", + "SDXLPromptStylerbyEnvironment", + "SDXLPromptStylerbyFantasySetting", + "SDXLPromptStylerbyFilter", + "SDXLPromptStylerbyFocus", + "SDXLPromptStylerbyFooocus", + "SDXLPromptStylerbyImpressionism", + "SDXLPromptStylerbyLighting", + "SDXLPromptStylerbyMarc", + "SDXLPromptStylerbyMileHigh", + "SDXLPromptStylerbyMood", + "SDXLPromptStylerbyMre", + "SDXLPromptStylerbyMythicalCreature", + "SDXLPromptStylerbyOriginal", + "SDXLPromptStylerbyQuantumRealism", + "SDXLPromptStylerbySai", + "SDXLPromptStylerbySteamPunkRealism", + "SDXLPromptStylerbySubject", + "SDXLPromptStylerbySurrealism", + "SDXLPromptStylerbyTheme", + "SDXLPromptStylerbyTimeofDay", + "SDXLPromptStylerbyTwri", + "SDXLPromptStylerbyWyvern", + "SDXLPromptbyCelticArt", + "SDXLPromptbyContemporaryNordicArt", + "SDXLPromptbyFashionArt", + "SDXLPromptbyGothicRevival", + "SDXLPromptbyIrishFolkArt", + "SDXLPromptbyRomanticNationalismArt", + "SDXLPromptbySportsArt", + "SDXLPromptbyStreetArt", + "SDXLPromptbyVikingArt", + "SDXLPromptbyWildlifeArt", + "Sampler Loader JK", + "Save Image with Metadata Flow JK", + "Save Image with Metadata JK", + "Save String List To JSON JK", + "Scale To Resolution JK", + "Split Image Grid JK", + "String To Combo JK", + "Tiling Mode JK", + "Upscale Method JK", + "Upscale Model Loader JK", + "Upscale Model Parameters Extract JK", + "Upscale Model Parameters JK", + "Vae Loader JK" + ], + { + "title_aux": "ComfyUI-JakeUpgrade" + } + ], + "https://github.com/jamal-alkharrat/ComfyUI_rotate_image": [ + [ + "RotateImage" + ], + { + "title_aux": "ComfyUI_rotate_image" + } + ], + "https://github.com/jamesWalker55/comfyui-p2ldgan": [ + [ + "P2LDGAN" + ], + { + "title_aux": "ComfyUI - P2LDGAN Node" + } + ], + "https://github.com/jamesWalker55/comfyui-various": [ + [ + "BatchLoadImage", + "BatchSaveImage", + "GroupInfoExtractFloat", + "GroupInfoExtractInt", + "GroupLoadBatchImages", + "GroupLoadImage", + "JWAudioBlend", + "JWAudioSaveToPath", + "JWDatetimeString", + "JWImageBatchCount", + "JWImageContrast", + "JWImageExtractFromBatch", + "JWImageFlip", + "JWImageLevels", + "JWImageLoadRGB", + "JWImageLoadRGBA", + "JWImageLoadRGBA From Clipboard", + "JWImageLoadRGBFromClipboard", + "JWImageLoadRGBIfExists", + "JWImageMix", + "JWImageResize", + "JWImageResizeByFactor", + "JWImageResizeByLongerSide", + "JWImageResizeByShorterSide", + "JWImageResizeToSquare", + "JWImageSaturation", + "JWImageSaveToPath", + "JWImageSequenceExtractFromBatch", + "JWImageStackChannels", + "JWInfoHashExtractFloat", + "JWInfoHashExtractInteger", + "JWInfoHashExtractString", + "JWInfoHashFromInfoHashList", + "JWInfoHashFromRangedInfo", + "JWInfoHashListExtractStringList", + "JWInfoHashListFromRangedInfo", + "JWInfoHashPrint", + "JWLoadAudio", + "JWLoadImageSequence", + "JWLoadImagesFromString", + "JWLoopImageSequence", + "JWMaskLikeImageSize", + "JWMaskResize", + "JWMaskSequenceApplyToLatent", + "JWMaskSequenceFromMask", + "JWMaskSequenceJoin", + "JWPrintFloat", + "JWPrintImage", + "JWPrintInteger", + "JWPrintLatent", + "JWPrintMask", + "JWPrintString", + "JWRangedInfoCalculateSubBatch", + "JWReferenceOnly", + "JWSaveImageSequence", + "JWStringListCLIPEncode", + "JWStringListFromString", + "JWStringListFromStrings", + "JWStringListJoin", + "JWStringListRepeat", + "JWStringListToFormatedString", + "JWStringListToString", + "JWUncropCrop", + "JWUncropNewRect", + "JWUncropUncrop", + "JamesLoadImageGroup", + "RAFTEstimate", + "RAFTFlowToImage", + "RAFTLoadFlowFromEXRChannels", + "RCReceiveFloat", + "RCReceiveFloatList", + "RCReceiveInt", + "RCReceiveIntList", + "RCReceiveLatent", + "RCSendLatent" + ], + { + "nodename_pattern": "^JW", + "title_aux": "Various ComfyUI Nodes by Type" + } + ], + "https://github.com/jammyfu/ComfyUI_PaintingCoderUtils": [ + [ + "PaintingCoder::DynamicImageCombiner", + "PaintingCoder::DynamicMaskCombiner", + "PaintingCoder::ImageLatentCreator", + "PaintingCoder::ImageLatentCreatorPlus", + "PaintingCoder::ImageResolutionAdjuster", + "PaintingCoder::ImageSizeCreator", + "PaintingCoder::ImageSizeCreatorPlus", + "PaintingCoder::ImageSwitch", + "PaintingCoder::ImageToBase64", + "PaintingCoder::LatentSwitch", + "PaintingCoder::MaskPreview", + "PaintingCoder::MaskSwitch", + "PaintingCoder::MultilineTextInput", + "PaintingCoder::OutputToTextConverter", + "PaintingCoder::RemoveEmptyLinesAndLeadingSpaces", + "PaintingCoder::ShowTextPlus", + "PaintingCoder::SimpleTextInput", + "PaintingCoder::TextCombiner", + "PaintingCoder::TextSwitch", + "PaintingCoder::WebImageLoader" + ], + { + "title_aux": "Painting Coder Utils" + } + ], + "https://github.com/jasonjgardner/comfui-substance-designer-integration": [ + [ + "SubstanceBatchProcessor", + "SubstanceCooker", + "SubstanceInfoExtractor", + "SubstanceParameterController", + "SubstanceRenderer" + ], + { + "title_aux": "ComfyUI Substance Designer Integration Plugin" + } + ], + "https://github.com/jax-explorer/ComfyUI-InstantCharacter": [ + [ + "InstantCharacterGenerate", + "InstantCharacterLoadModel", + "InstantCharacterLoadModelFromLocal" + ], + { + "title_aux": "ComfyUI-InstantCharacter" + } + ], + "https://github.com/jax-explorer/ComfyUI-VideoBasic": [ + [ + "VideoBasicLoadVideo", + "VideoBasicMergeVideo", + "VideoBasicVideoSave", + "VideoBasicVideoUpscaleWithModel" + ], + { + "title_aux": "ComfyUI-VideoBasic" + } + ], + "https://github.com/jax-explorer/ComfyUI-VideoBasicLatentSync": [ + [ + "VideoBasicLatentSyncLengthAdjuster", + "VideoBasicLatentSyncNode" + ], + { + "title_aux": "ComfyUI-VideoBasicLatentSync" + } + ], + "https://github.com/jax-explorer/ComfyUI-easycontrol": [ + [ + "EasyControlGenerate", + "EasyControlLoadFlux", + "EasyControlLoadLora", + "EasyControlLoadMultiLora", + "EasyControlLoadStyleLora", + "EasyControlLoadStyleLoraFromCivitai" + ], + { + "title_aux": "ComfyUI-easycontrol" + } + ], + "https://github.com/jax-explorer/comfyui-model-dynamic-loader": [ + [ + "ComfyOnlineSaveFile", + "ComfyOnlineUploadAnything", + "EmbeddingLoader", + "LoadCheckpointFromCivitAI", + "LoadHunyuanLoraFromCivitAI", + "LoadHunyuanLoraFromComfyOnline", + "LoadHunyuanLoraFromHF", + "LoadImageFromURL", + "LoadLoraFromCivitAI", + "LoadLoraFromComfyOnline", + "LoadLoraFromHF", + "LoadLoraFromHFWithToken", + "LoadWanVideoLoraFromCivitAI", + "LoadWanVideoLoraFromComfyOnline", + "LoadWanVideoLoraFromHF", + "SaveAudioAsWav", + "SaveText" + ], + { + "title_aux": "comfyui-model-dynamic-loader" + } + ], + "https://github.com/jax-explorer/fast_video_comfyui": [ + [ + "FastImageListToImageBatch" + ], + { + "title_aux": "fast_video_comfyui" + } + ], + "https://github.com/jeffrey2212/ComfyUI-PonyCharacterPrompt": [ + [ + "Pony Character Prompt Picker" + ], + { + "title_aux": "Pony Character Prompt Picker for ComfyUI" + } + ], + "https://github.com/jeffy5/comfyui-faceless-node": [ + [ + "FacelessFaceRestore", + "FacelessFaceSwap", + "FacelessLoadImageUrl", + "FacelessLoadVideo", + "FacelessLoadVideoImages", + "FacelessLoadVideoUrl", + "FacelessMergeVideos", + "FacelessRemoveBackground", + "FacelessSaveVideo", + "FacelessUploadVideo", + "FacelessVideoFaceRestore", + "FacelessVideoFaceSwap", + "FacelessVideoRemoveBackground" + ], + { + "title_aux": "Faceless Node for ComfyUI" + } + ], + "https://github.com/jerrylongyan/ComfyUI-My-Mask": [ + [ + "MaskToBottonHalfConvexMask", + "MaskToConvexMask" + ], + { + "title_aux": "ComfyUI-My-Mask" + } + ], + "https://github.com/jerrywap/ComfyUI_LoadImageFromHttpURL": [ + [ + "LoadImageFromHttpURL" + ], + { + "title_aux": "ComfyUI_LoadImageFromHttpURL" + } + ], + "https://github.com/jerrywap/ComfyUI_UploadToWebhookHTTP": [ + [ + "UploadToWebHookHTTP" + ], + { + "title_aux": "ComfyUI_UploadToWebhookHTTP" + } + ], + "https://github.com/jesenzhang/ComfyUI_StreamDiffusion": [ + [ + "StreamDiffusion_Loader", + "StreamDiffusion_Sampler" + ], + { + "title_aux": "ComfyUI_StreamDiffusion" + } + ], + "https://github.com/jhj0517/ComfyUI-Moondream-Gaze-Detection": [ + [ + "(Down)Load Moondream Model", + "Gaze Detection", + "Gaze Detection Video" + ], + { + "title_aux": "ComfyUI-Moondream-Gaze-Detection" + } + ], + "https://github.com/jhj0517/ComfyUI-jhj-Kokoro-Onnx": [ + [ + "(Down)Load Kokoro Model", + "Kokoro Audio Generator" + ], + { + "title_aux": "ComfyUI jhj Kokoro Onnx" + } + ], + "https://github.com/jiafuzeng/comfyui-LatentSync": [ + [ + "LatentSyncNode" + ], + { + "title_aux": "LatentSync" + } + ], + "https://github.com/jiaqianjing/ComfyUI-MidjourneyHub": [ + [ + "GPTImageEditNode", + "GPTImageGenerateNode", + "MidjourneyActionNode", + "MidjourneyBatchActionNode", + "MidjourneyBlendNode", + "MidjourneyImagineNode" + ], + { + "title_aux": "ComfyUI-MidjourneyHub" + } + ], + "https://github.com/jiaxiangc/ComfyUI-ResAdapter": [ + [ + "ResAdapterLoader" + ], + { + "title_aux": "ResAdapter for ComfyUI" + } + ], + "https://github.com/jinanlongen/ComfyUI-Prompt-Expander": [ + [ + "PromptExpanderNode" + ], + { + "title_aux": "ComfyUI Prompt Expander Node" + } + ], + "https://github.com/jinchanz/ComfyUI-ADIC": [ + [ + "ADIC_COMMON_API", + "AliCloudOSSUpload", + "ImageTranslateAPI", + "ImageTranslateParamsBuilder", + "ImageTranslateResultExtractor", + "LoadImagesFromUrls", + "MaletteFluxKontextImageScale", + "MaletteImageConcatFromBatch", + "MaletteImageStitch", + "MaletteReferenceLatent", + "MarketImageGenerateWithPolling", + "PythonCodeExecutor", + "StringToJsonArray" + ], + { + "title_aux": "ComfyUI-ADIC" + } + ], + "https://github.com/jitcoder/lora-info": [ + [ + "ImageFromURL", + "LoraInfo" + ], + { + "title_aux": "LoraInfo" + } + ], + "https://github.com/jjkramhoeft/ComfyUI-Jjk-Nodes": [ + [ + "JjkConcat", + "JjkShowText", + "JjkText", + "SDXLRecommendedImageSize" + ], + { + "title_aux": "ComfyUI-Jjk-Nodes" + } + ], + "https://github.com/jkrauss82/ultools-comfyui": [ + [ + "CLIPTextEncodeWithStats", + "OpenPoseEditorAdv", + "SaveImgAdv", + "SolidMaskAdv" + ], + { + "title_aux": "ULTools for ComfyUI" + } + ], + "https://github.com/jn-jairo/jn_comfyui": [ + [ + "JN_AreaAround", + "JN_AreaInfo", + "JN_AreaNormalize", + "JN_AreaToMask", + "JN_AreaWidthHeight", + "JN_AreaXY", + "JN_AudioArrayToBatch", + "JN_AudioAutoTune", + "JN_AudioBatchToArray", + "JN_AudioCompare", + "JN_AudioConcatenation", + "JN_AudioGetChannels", + "JN_AudioInfo", + "JN_AudioNoiseReduction", + "JN_AudioNormalize", + "JN_AudioPitch", + "JN_AudioPlot", + "JN_AudioReverberation", + "JN_AudioSampleRate", + "JN_AudioSetChannels", + "JN_AudioSlice", + "JN_AudioSpeed", + "JN_AudioSplitChannels", + "JN_AudioStackChannels", + "JN_AudioTempo", + "JN_AudioTrimSilence", + "JN_AudioVolume", + "JN_Blip", + "JN_BlipLoader", + "JN_BooleanOperation", + "JN_Condition", + "JN_CoolDown", + "JN_CoolDownOutput", + "JN_DatetimeFormat", + "JN_DatetimeInfo", + "JN_DatetimeNow", + "JN_Dump", + "JN_DumpOutput", + "JN_Exec", + "JN_ExecOutput", + "JN_FaceCrop", + "JN_FaceRestoreModelLoader", + "JN_FaceRestoreWithModel", + "JN_FirstActive", + "JN_Flow", + "JN_FlowOutput", + "JN_ImageAddBackground", + "JN_ImageAddMask", + "JN_ImageBatch", + "JN_ImageCenterArea", + "JN_ImageCrop", + "JN_ImageGrid", + "JN_ImageInfo", + "JN_ImageRemoveBackground", + "JN_ImageSharpness", + "JN_ImageSquare", + "JN_ImageToMask", + "JN_ImageUncrop", + "JN_KSampler", + "JN_KSamplerAdvancedParams", + "JN_KSamplerFaceRestoreParams", + "JN_KSamplerResizeInputParams", + "JN_KSamplerResizeMaskAreaParams", + "JN_KSamplerResizeOutputParams", + "JN_KSamplerSeamlessParams", + "JN_KSamplerTileParams", + "JN_KeyValue", + "JN_LoadAudioDirectory", + "JN_LoadImageDirectory", + "JN_LogicOperation", + "JN_MaskBatch", + "JN_MaskInfo", + "JN_MaskToArea", + "JN_MaskToImage", + "JN_MathOperation", + "JN_MathOperationArray", + "JN_MeowHrtfAudio3d", + "JN_MeowHrtfModel", + "JN_MeowHrtfPosition", + "JN_MeowLoadVoice", + "JN_MeowSaveVoice", + "JN_MeowSentenceSplit", + "JN_MeowTts", + "JN_MeowTtsAudioToContext", + "JN_MeowTtsCoarse", + "JN_MeowTtsDecode", + "JN_MeowTtsFine", + "JN_MeowTtsLoadContext", + "JN_MeowTtsModel", + "JN_MeowTtsModelCoarse", + "JN_MeowTtsModelEncodec", + "JN_MeowTtsModelFine", + "JN_MeowTtsModelHubert", + "JN_MeowTtsModelSemantic", + "JN_MeowTtsSaveContext", + "JN_MeowTtsSemantic", + "JN_MeowTtsTokenizerHubert", + "JN_MeowVc", + "JN_MeowVcConvertVoice", + "JN_MeowVcEncodeSource", + "JN_MeowVcEncodeTarget", + "JN_MeowVcLoadSpeaker", + "JN_MeowVcModelFreeVC", + "JN_MeowVcModelWavLM", + "JN_MeowVcSaveSpeaker", + "JN_PreviewAudio", + "JN_PreviewImage", + "JN_PreviewMask", + "JN_PrimitiveArrayInfo", + "JN_PrimitiveBatchToArray", + "JN_PrimitiveBoolean", + "JN_PrimitiveFloat", + "JN_PrimitiveInt", + "JN_PrimitivePrompt", + "JN_PrimitiveString", + "JN_PrimitiveStringMultiline", + "JN_PrimitiveStringToArray", + "JN_PrimitiveToArray", + "JN_PrimitiveToBoolean", + "JN_PrimitiveToFloat", + "JN_PrimitiveToInt", + "JN_PrimitiveToString", + "JN_RemBGSession", + "JN_SaveAudio", + "JN_SaveImage", + "JN_Seamless", + "JN_SeamlessBorder", + "JN_SeamlessBorderCrop", + "JN_SelectItem", + "JN_Sleep", + "JN_SleepOutput", + "JN_SliceOperation", + "JN_StopIf", + "JN_StopIfOutput", + "JN_TensorInfo", + "JN_TextConcatenation", + "JN_TextReplace", + "JN_TimedeltaFormat", + "JN_TimedeltaInfo" + ], + { + "title_aux": "JNComfy" + } + ], + "https://github.com/jnxmx/ComfyUI_HuggingFace_Downloader": [ + [ + "Hugging Face Download Folder", + "Hugging Face Download Model" + ], + { + "title_aux": "ComfyUI_HuggingFace_Downloader" + } + ], + "https://github.com/joeriben/ai4artsed_comfyui_nodes": [ + [ + "ai4artsed_conditioning_fusion", + "ai4artsed_image_analysis", + "ai4artsed_openrouter_key", + "ai4artsed_prompt_interception", + "ai4artsed_random_artform_generator", + "ai4artsed_random_instruction_generator", + "ai4artsed_random_language_selector", + "ai4artsed_stabilitai_key", + "ai4artsed_switch_promptsafety", + "ai4artsed_t5_clip_fusion", + "ai4artsed_text_remix", + "ai4artsed_vector_dimension_eliminator" + ], + { + "title_aux": "AI4ArtsEd Nodes" + } + ], + "https://github.com/john-mnz/ComfyUI-Inspyrenet-Rembg": [ + [ + "InspyrenetRembg", + "InspyrenetRembgAdvanced" + ], + { + "title_aux": "ComfyUI-Inspyrenet-Rembg" + } + ], + "https://github.com/jojkaart/ComfyUI-sampler-lcm-alternative": [ + [ + "LCMScheduler", + "SamplerLCMAlternative", + "SamplerLCMCycle", + "SamplerLCMDualNoise", + "SamplerLCMDuoFusion", + "SamplerLCMParallel" + ], + { + "title_aux": "ComfyUI-sampler-lcm-alternative" + } + ], + "https://github.com/joosthel/ComfyUI-CVOverlay": [ + [ + "CV_AestheticOverlay", + "CV_BlobTracker", + "CV_ModelLoader", + "CV_ObjectDetector" + ], + { + "title_aux": "ComfyUI-CVOverlay" + } + ], + "https://github.com/jordoh/ComfyUI-Deepface": [ + [ + "DeepfaceAnalyze", + "DeepfaceExtractFaces", + "DeepfaceVerify" + ], + { + "title_aux": "ComfyUI Deepface" + } + ], + "https://github.com/joreyaesh/comfyui_scroll_over_textarea": [ + [ + "ScrollOverTextareaDummyNode" + ], + { + "title_aux": "ComfyUI Scroll Over Textarea" + } + ], + "https://github.com/joreyaesh/comfyui_touchpad_scroll_controller.enableTouchpadScroll": [ + [ + "TouchpadScrollControllerDummyNode" + ], + { + "title_aux": "ComfyUI Touchpad Scroll Controller" + } + ], + "https://github.com/jqy-yo/Comfyui-BBoxLowerMask2": [ + [ + "BBoxLowerMask2" + ], + { + "title_aux": "BBoxLowerMask2" + } + ], + "https://github.com/jroc22/ComfyUI-CSV-prompt-builder": [ + [ + "BuildPromptFromCSV" + ], + { + "title_aux": "ComfyUI-CSV-prompt-builder" + } + ], + "https://github.com/jstit/comfyui_custom_node_image": [ + [ + "ImageCropCircle" + ], + { + "title_aux": "comfyui_custom_node_image" + } + ], + "https://github.com/jtrue/ComfyUI-JaRue": [ + [ + "Text2Image_jru", + "YouTube2Prompt_jru" + ], + { + "nodename_pattern": "_jru$", + "title_aux": "ComfyUI-JaRue" + } + ], + "https://github.com/jtydhr88/ComfyUI-Hunyuan3D-1-wrapper": [ + [ + "Hunyuan3D V1 - Image Loader", + "Hunyuan3D V1 - Image2Views", + "Hunyuan3D V1 - Image2Views Pipeline Load", + "Hunyuan3D V1 - Text2Image", + "Hunyuan3D V1 - Text2Image Pipeline Load", + "Hunyuan3D V1 - Views2Mesh", + "Hunyuan3D V1 - Views2Mesh Pipeline Load" + ], + { + "title_aux": "ComfyUI-Hunyuan3D-1-wrapper" + } + ], + "https://github.com/jtydhr88/ComfyUI-LayerDivider": [ + [ + "LayerDivider - Color Base", + "LayerDivider - Divide Layer", + "LayerDivider - Load SAM Mask Generator", + "LayerDivider - Segment Mask" + ], + { + "title_aux": "ComfyUI LayerDivider" + } + ], + "https://github.com/judian17/ComfyUI-Extract_Flux_Lora": [ + [ + "ExtractFluxLoRA" + ], + { + "title_aux": "ComfyUI-Extract_Flux_Lora" + } + ], + "https://github.com/judian17/ComfyUI-UniWorld-jd17": [ + [ + "UniWorldEncoderNode", + "UniWorldScheduler", + "UniWorldSiglipEncoder", + "UniWorld_T5_CLIP_Encoder" + ], + { + "title_aux": "ComfyUI-UniWorld-jd17" + } + ], + "https://github.com/judian17/ComfyUI-joycaption-beta-one-GGUF": [ + [ + "JJC_JoyCaption_Custom_GGUF", + "JJC_JoyCaption_GGUF", + "JJC_JoyCaption_GGUF_ExtraOptions" + ], + { + "title_aux": "ComfyUI JoyCaption-Beta-GGUF Node" + } + ], + "https://github.com/judian17/ComfyUI_ZIM": [ + [ + "MaskToBbox_ZIM", + "MaskToPoints_ZIM", + "ZimSegment" + ], + { + "title_aux": "ComfyUI_ZIM" + } + ], + "https://github.com/juehackr/comfyui_fk_server": [ + [ + "FK_3dpose", + "FK_Node", + "FK_ShowBaseNode" + ], + { + "title_aux": "comfyui_fk_server" + } + ], + "https://github.com/jurdnf/ComfyUI-JurdnsIterativeNoiseKSampler": [ + [ + "KSamplerIterativeNoise" + ], + { + "title_aux": "ComfyUI-JurdnsIterativeNoiseKsampler" + } + ], + "https://github.com/jurdnf/ComfyUI-JurdnsModelSculptor": [ + [ + "ModelSculptorFlux", + "ModelSculptorSD3", + "ModelSculptorSDXL" + ], + { + "title_aux": "ComfyUI-JurdnsModelSculptor" + } + ], + "https://github.com/jurdnisglobby/ComfyUI-Jurdns-Groq-Node": [ + [ + "JurdnsGroqAPIPromptEnhancer" + ], + { + "title_aux": "Jurdns Groq API Node" + } + ], + "https://github.com/justUmen/Bjornulf_custom_nodes": [ + [ + "Bjornulf_APIGenerateCivitAI", + "Bjornulf_APIGenerateCivitAIAddLORA", + "Bjornulf_APIGenerateFalAI", + "Bjornulf_APIGenerateFlux", + "Bjornulf_APIGenerateGPT4o", + "Bjornulf_APIGenerateStability", + "Bjornulf_AddLineNumbers", + "Bjornulf_AllLoraSelector", + "Bjornulf_AnythingToFloat", + "Bjornulf_AnythingToInt", + "Bjornulf_AnythingToText", + "Bjornulf_ApiDynamicTextInputs", + "Bjornulf_AudioPreview", + "Bjornulf_AudioVideoSync", + "Bjornulf_BoundingRectangleMask", + "Bjornulf_BoundingRectangleMaskBlur", + "Bjornulf_CharacterDescriptionGenerator", + "Bjornulf_CivitAILoraSelector", + "Bjornulf_CivitAILoraSelectorHunyuan", + "Bjornulf_CivitAILoraSelectorPONY", + "Bjornulf_CivitAILoraSelectorSD15", + "Bjornulf_CivitAILoraSelectorSDXL", + "Bjornulf_CivitAIModelSelectorFLUX_D", + "Bjornulf_CivitAIModelSelectorFLUX_S", + "Bjornulf_CivitAIModelSelectorPony", + "Bjornulf_CivitAIModelSelectorSD15", + "Bjornulf_CivitAIModelSelectorSDXL", + "Bjornulf_CombineBackgroundOverlay", + "Bjornulf_CombineImages", + "Bjornulf_CombineTexts", + "Bjornulf_CombineTextsByLines", + "Bjornulf_CombineVideoAudio", + "Bjornulf_ConcatVideos", + "Bjornulf_ConcatVideosFromList", + "Bjornulf_ConditionalSwitch", + "Bjornulf_ConvertVideo", + "Bjornulf_DisplayNote", + "Bjornulf_EmptyVideoLatentWithSingle", + "Bjornulf_ExecuteWorkflowNode", + "Bjornulf_FFmpegConfig", + "Bjornulf_FaceSettings", + "Bjornulf_FixFace", + "Bjornulf_FourImageViewer", + "Bjornulf_FreeVRAM", + "Bjornulf_GlobalSeedManager", + "Bjornulf_GrayscaleTransform", + "Bjornulf_GreenScreenToTransparency", + "Bjornulf_HiResFix", + "Bjornulf_HorizontalCutAndShift", + "Bjornulf_HuggingFaceDownloader", + "Bjornulf_IfElse", + "Bjornulf_ImageBlend", + "Bjornulf_ImageDetails", + "Bjornulf_ImageMaskCutter", + "Bjornulf_ImageNote", + "Bjornulf_ImageNoteLoadImage", + "Bjornulf_ImageUpscaleWithModelTransparency", + "Bjornulf_ImagesListToVideo", + "Bjornulf_JSONImagePromptExtractor", + "Bjornulf_KokoroTTS", + "Bjornulf_LargestMaskOnly", + "Bjornulf_LatentResolutionSelector", + "Bjornulf_LineSelector", + "Bjornulf_ListLooper", + "Bjornulf_ListLooperCharacter", + "Bjornulf_ListLooperOutfitFemale", + "Bjornulf_ListLooperOutfitMale", + "Bjornulf_ListLooperScene", + "Bjornulf_ListLooperStyle", + "Bjornulf_ListSelector", + "Bjornulf_LoadCivitAILinks", + "Bjornulf_LoadFromBase64", + "Bjornulf_LoadGlobalVariables", + "Bjornulf_LoadImageWithTransparency", + "Bjornulf_LoadImageWithTransparencyFromPath", + "Bjornulf_LoadImagesFromSelectedFolder", + "Bjornulf_LoadTensor", + "Bjornulf_LoadTextFromFolder", + "Bjornulf_LoadTextFromPath", + "Bjornulf_LoadTextPickMeGlobal", + "Bjornulf_LoaderLoraWithPath", + "Bjornulf_LoopAllLines", + "Bjornulf_LoopBasicBatch", + "Bjornulf_LoopCombosSamplersSchedulers", + "Bjornulf_LoopFloat", + "Bjornulf_LoopImages", + "Bjornulf_LoopInteger", + "Bjornulf_LoopIntegerSequential", + "Bjornulf_LoopLinesSequential", + "Bjornulf_LoopLoraSelector", + "Bjornulf_LoopModelClipVae", + "Bjornulf_LoopModelSelector", + "Bjornulf_LoopRandomSeed", + "Bjornulf_LoopSamplers", + "Bjornulf_LoopSchedulers", + "Bjornulf_LoopTexts", + "Bjornulf_LoopWriteText", + "Bjornulf_LoraSelectorHunyuan", + "Bjornulf_LoraSelectorWanVideo", + "Bjornulf_MatchTextToInput", + "Bjornulf_MathNode", + "Bjornulf_MergeImagesHorizontally", + "Bjornulf_MergeImagesVertically", + "Bjornulf_ModelClipVaeSelector", + "Bjornulf_MultiOpenAIVisionNode", + "Bjornulf_OllamaConfig", + "Bjornulf_OllamaImageVision", + "Bjornulf_OllamaSystemJobSelector", + "Bjornulf_OllamaSystemPersonaSelector", + "Bjornulf_OllamaTalk", + "Bjornulf_OllamaVisionPromptSelector", + "Bjornulf_OpenAIVisionNode", + "Bjornulf_PassPreviewImage", + "Bjornulf_PauseResume", + "Bjornulf_PickInput", + "Bjornulf_PickMe", + "Bjornulf_PlayAudio", + "Bjornulf_PreviewFirstImage", + "Bjornulf_PurgeCLIPNode", + "Bjornulf_RandomFloatNode", + "Bjornulf_RandomImage", + "Bjornulf_RandomIntNode", + "Bjornulf_RandomLineFromInput", + "Bjornulf_RandomLoraSelector", + "Bjornulf_RandomModelClipVae", + "Bjornulf_RandomModelSelector", + "Bjornulf_RandomTexts", + "Bjornulf_ReassembleImageGrid", + "Bjornulf_RemoteTextEncodingWithCLIPs", + "Bjornulf_RemoteVAEDecoderNode", + "Bjornulf_RemoteVAEDecoderNodeTiled", + "Bjornulf_RemoveTransparency", + "Bjornulf_ResizeImage", + "Bjornulf_ResizeImagePercentage", + "Bjornulf_SaveBjornulfLobeChat", + "Bjornulf_SaveGlobalVariables", + "Bjornulf_SaveImagePath", + "Bjornulf_SaveImageToFolder", + "Bjornulf_SaveTensors", + "Bjornulf_SaveText", + "Bjornulf_SaveTmpAudio", + "Bjornulf_SaveTmpImage", + "Bjornulf_SaveTmpVideo", + "Bjornulf_ScramblerCharacter", + "Bjornulf_SelectImageFromList", + "Bjornulf_ShowFloat", + "Bjornulf_ShowInt", + "Bjornulf_ShowJson", + "Bjornulf_ShowStringText", + "Bjornulf_ShowText", + "Bjornulf_SpeechToText", + "Bjornulf_SplitImageGrid", + "Bjornulf_StyleSelector", + "Bjornulf_SwitchAnything", + "Bjornulf_SwitchText", + "Bjornulf_TextAnalyzer", + "Bjornulf_TextGenerator", + "Bjornulf_TextGeneratorCharacterCreature", + "Bjornulf_TextGeneratorCharacterFemale", + "Bjornulf_TextGeneratorCharacterMale", + "Bjornulf_TextGeneratorCharacterObject", + "Bjornulf_TextGeneratorCharacterPose", + "Bjornulf_TextGeneratorOutfitFemale", + "Bjornulf_TextGeneratorOutfitMale", + "Bjornulf_TextGeneratorScene", + "Bjornulf_TextGeneratorStyle", + "Bjornulf_TextGeneratorText2Video", + "Bjornulf_TextReplace", + "Bjornulf_TextSplitin10", + "Bjornulf_TextSplitin5", + "Bjornulf_TextToAnything", + "Bjornulf_TextToSpeech", + "Bjornulf_TextToStringAndSeed", + "Bjornulf_TextToVariable", + "Bjornulf_ToDoList", + "Bjornulf_VideoDetails", + "Bjornulf_VideoLatentResolutionSelector", + "Bjornulf_VideoPingPong", + "Bjornulf_VideoPreview", + "Bjornulf_VideoTextGenerator", + "Bjornulf_VideoToImagesList", + "Bjornulf_WriteText", + "Bjornulf_WriteTextAdvanced", + "Bjornulf_WriteTextPickMe", + "Bjornulf_WriteTextPickMeChain", + "Bjornulf_WriteTextPickMeGlobal", + "Bjornulf_XTTSConfig", + "Bjornulf_imagesToVideo", + "Bjornulf_loadImageBase64Transparency", + "Bjornulf_ollamaLoader" + ], + { + "title_aux": "Bjornulf_custom_nodes" + } + ], + "https://github.com/justin-vt/ComfyUI-brushstrokes": [ + [ + "OpenCVBrushStrokesNode", + "PILBrushStrokesNode", + "WandBrushStrokesNode" + ], + { + "title_aux": "ComfyUI-brushstrokes" + } + ], + "https://github.com/k-komarov/comfyui-bunny-cdn-storage": [ + [ + "Save Image to BunnyStorage" + ], + { + "title_aux": "comfyui-bunny-cdn-storage" + } + ], + "https://github.com/ka-puna/comfyui-yanc": [ + [ + "YANC.ConcatStrings", + "YANC.FormatDatetimeString", + "YANC.GetWidgetValueString", + "YANC.IntegerCaster", + "YANC.MultilineString", + "YANC.SaveImageWEBP", + "YANC.TruncateString" + ], + { + "title_aux": "comfyui-yanc" + } + ], + "https://github.com/kaanyalova/ComfyUI_ExtendedImageFormats": [ + [ + "DDSSaveImage", + "ExtendedSaveImage" + ], + { + "title_aux": "Extended Image Formats for ComfyUI" + } + ], + "https://github.com/kaaskoek232/ComfyUI-MemoryManagement": [ + [ + "AutoMemoryCleanup", + "MemoryCleanup", + "MemoryLeakDetector", + "MemoryMonitor", + "SmartMemoryManager", + "VRAMOptimizer", + "VRAMUnload" + ], + { + "title_aux": "ComfyUI-MemoryManagement" + } + ], + "https://github.com/kadirnar/ComfyUI-Transformers": [ + [ + "DepthEstimationInference", + "ImageClassificationPipeline", + "ImageSegmentationPipeline", + "LoadDepthModel", + "ObjectDetectionPipeline" + ], + { + "title_aux": "ComfyUI-Transformers" + } + ], + "https://github.com/kadirnar/ComfyUI-YOLO": [ + [ + "BBoxToCoco", + "BBoxToXYWH", + "BBoxVisNode", + "CocoToNumber", + "ConvertToDict", + "CustomUltralyticsModelLoader", + "GetImageSize", + "ImageResizeAdvanced", + "UltralyticsInference", + "UltralyticsModelLoader", + "UltralyticsVisualization", + "ViewText" + ], + { + "title_aux": "ComfyUI-YOLO" + } + ], + "https://github.com/kael558/ComfyUI-GGUF-FantasyTalking": [ + [ + "CLIPLoaderGGUF", + "DownloadAndLoadWav2VecModel", + "FantasyTalkingModelLoader", + "FantasyTalkingWav2VecEmbeds", + "LoadWanVideoT5TextEncoderGGUF", + "ReCamMasterPoseVisualizer", + "UnetLoaderGGUF", + "UnetLoaderGGUF_LowVRAM", + "WanVideoATITracks", + "WanVideoATITracksVisualize", + "WanVideoATI_comfy", + "WanVideoControlnet", + "WanVideoControlnetLoader", + "WanVideoDiffusionForcingSampler", + "WanVideoFunCameraEmbeds", + "WanVideoReCamMasterCameraEmbed", + "WanVideoReCamMasterDefaultCamera", + "WanVideoReCamMasterGenerateOrbitCamera", + "WanVideoUni3C_ControlnetLoader", + "WanVideoUni3C_embeds", + "WanVideoUniAnimateDWPoseDetector", + "WanVideoUniAnimatePoseInput" + ], + { + "title_aux": "ComfyUI-GGUF-FantasyTalking" + } + ], + "https://github.com/kaibioinfo/ComfyUI_AdvancedRefluxControl": [ + [ + "ReduxAdvanced", + "StyleModelApplySimple" + ], + { + "title_aux": "Advanced Reflux control" + } + ], + "https://github.com/kaipard/comfyui-auto-latent-size": [ + [ + "AutoAspectLatent" + ], + { + "title_aux": "Auto Aspect Latent Generator" + } + ], + "https://github.com/kale4eat/ComfyUI-path-util": [ + [ + "path_util_PathAbspath", + "path_util_PathBasename", + "path_util_PathDirname", + "path_util_PathExists", + "path_util_PathIsdir", + "path_util_PathIsfile", + "path_util_PathJoin", + "path_util_PathRelpath", + "path_util_PathSplitext" + ], + { + "title_aux": "ComfyUI_demucus" + } + ], + "https://github.com/kale4eat/ComfyUI-speech-dataset-toolkit": [ + [ + "SDT_AudioProperty", + "SDT_BSRoFormerApply", + "SDT_BSRoFormerLoader", + "SDT_ConcatAudio", + "SDT_CutAudio", + "SDT_DemucsApply", + "SDT_DemucsLoader", + "SDT_FasterWhisperListSegments", + "SDT_FasterWhisperLoader", + "SDT_FasterWhisperSegmentProperty", + "SDT_FasterWhisperTextFromSegments", + "SDT_FasterWhisperTranscribe", + "SDT_GriffinLim", + "SDT_HighpassBiquad", + "SDT_JoinAudio", + "SDT_KotobaWhisperListSegments", + "SDT_KotobaWhisperLoaderLong", + "SDT_KotobaWhisperLoaderShort", + "SDT_KotobaWhisperSegmentProperty", + "SDT_KotobaWhisperTranscribeLong", + "SDT_KotobaWhisperTranscribeShort", + "SDT_LFCC", + "SDT_LoadAudio", + "SDT_LoadAudios", + "SDT_LowpassBiquad", + "SDT_MFCC", + "SDT_MakeSilenceAudio", + "SDT_MelBandRoformerLoader", + "SDT_MelSpectrogram", + "SDT_NemoAsrLoader", + "SDT_NemoAsrTranscribe", + "SDT_NueAsrLoader", + "SDT_NueAsrTranscribe", + "SDT_PlotMelFilterBank", + "SDT_PlotPitch", + "SDT_PlotSpecgram", + "SDT_PlotSpectrogram", + "SDT_PlotWaveForm", + "SDT_ResampleAudio", + "SDT_SaveAudio", + "SDT_SilenceAudio", + "SDT_SileroVADApply", + "SDT_SileroVADCollectChunks", + "SDT_SileroVADListTimestamps", + "SDT_SileroVADLoader", + "SDT_SileroVADTimestampProperty", + "SDT_Spectrogram", + "SDT_SpeechMOSLoader", + "SDT_SpeechMOSScore", + "SDT_SplitAudio", + "SDT_TrimAudio", + "SDT_TrimAudioBySample" + ], + { + "title_aux": "ComfyUI-speech-dataset-toolkit" + } + ], + "https://github.com/kale4eat/ComfyUI-string-util": [ + [ + "string_util_Str", + "string_util_StrConcat", + "string_util_StrCount", + "string_util_StrEndsWith", + "string_util_StrEqual", + "string_util_StrFind", + "string_util_StrFormat", + "string_util_StrJoin", + "string_util_StrLen", + "string_util_StrLower", + "string_util_StrLstrip", + "string_util_StrNotEqual", + "string_util_StrReplace", + "string_util_StrRstrip", + "string_util_StrSlice", + "string_util_StrSplit", + "string_util_StrStartsWith", + "string_util_StrStrip", + "string_util_StrUpper" + ], + { + "title_aux": "ComfyUI-string-util" + } + ], + "https://github.com/kale4eat/ComfyUI-text-file-util": [ + [ + "text_file_util_ReadAllLines", + "text_file_util_ReadAllText", + "text_file_util_WriteText", + "text_file_util_WriteTextLines", + "text_file_util_WriteTextWithSequentialNumbering" + ], + { + "title_aux": "ComfyUI-text-file-util" + } + ], + "https://github.com/kambara/ComfyUI-PromptPalette": [ + [ + "PromptPalette" + ], + { + "title_aux": "ComfyUI-PromptPalette" + } + ], + "https://github.com/kantsche/ComfyUI-MixMod": [ + [ + "MixModBandFFTGuiderNode", + "MixModDepthGuiderNode", + "MixModDynamicMaskAlternativeGuiderNode", + "MixModDynamicMaskGuiderNode", + "MixModFFTGuiderNode", + "MixModGuiderComponentNode", + "MixModGuiderComponentPipelineNode", + "MixModGuiderNode", + "MixModHighResGuiderNode", + "MixModOptionsMaskNode", + "MixModOptionsScaleNode", + "MixModOptionsSchedulerNode", + "MixModPipelineNode" + ], + { + "author": "Kantsche", + "description": "Model Mixture Guider", + "nickname": "MixMod", + "title": "ComfyUI-MixMod", + "title_aux": "ComfyUI-MixMod" + } + ], + "https://github.com/kappa54m/ComfyUI_Usability": [ + [ + "KLoadImageByPath", + "KLoadImageByPathAdvanced", + "KLoadImageDedup" + ], + { + "title_aux": "ComfyUI Usability" + } + ], + "https://github.com/karthikg-09/ComfyUI-Vton-Mask": [ + [ + "ComfyUIVtonMaskGenerator", + "ComfyUIVtonMaskLoader" + ], + { + "title_aux": "ComfyUI-Vton-Mask" + } + ], + "https://github.com/kasukanra/ComfyUI_StringToHex": [ + [ + "ColorNameToHex" + ], + { + "title_aux": "ComfyUI_StringToHex" + } + ], + "https://github.com/katalist-ai/comfyUI-nsfw-detection": [ + [ + "NudenetDetector" + ], + { + "title_aux": "comfyUI-nsfw-detection" + } + ], + "https://github.com/kazeyori/ComfyUI-QuickImageSequenceProcess": [ + [ + "QuickImageSequenceProcess" + ], + { + "author": "kazeyori", + "description": "A ComfyUI plugin for efficient image sequence processing. Features frame insertion, duplication, and removal with intuitive controls.", + "nickname": "QuickSeq", + "title": "Quick Image Sequence Process", + "title_aux": "ComfyUI-QuickImageSequenceProcess" + } + ], + "https://github.com/kealiu/ComfyUI-S3-Tools": [ + [ + "Load Image From S3", + "Save Image To S3" + ], + { + "title_aux": "ComfyUI Load and Save file to S3" + } + ], + "https://github.com/kealiu/ComfyUI-Zero123-Porting": [ + [ + "Zero123: Image Preprocess", + "Zero123: Image Rotate in 3D" + ], + { + "title_aux": "ComfyUI-Zero123-Porting" + } + ], + "https://github.com/kealiu/ComfyUI-ZeroShot-MTrans": [ + [ + "ZeST: Grayout Subject" + ], + { + "title_aux": "ComfyUI-ZeroShot-MTrans" + } + ], + "https://github.com/keit0728/ComfyUI-Image-Toolkit": [ + [ + "AlphaFlatten", + "AlphaToGrayscale", + "AntialiasingImage", + "BinarizeImage", + "BinarizeImageUsingOtsu", + "BrightnessTransparency", + "GrayscaleImage", + "RemoveWhiteBackgroundNoise" + ], + { + "title_aux": "ComfyUI-Image-Toolkit" + } + ], + "https://github.com/keit0728/ComfyUI-keitNodes": [ + [ + "AspectRatioResolutionFinder", + "M2MTranslator", + "PixelLimitResizer", + "WanVideoOptimalResizer", + "WanVideoResolutionFinder" + ], + { + "title_aux": "ComfyUI-keitNodes" + } + ], + "https://github.com/keit0728/ComfyUI-musubi-tuner": [ + [ + "MusubiTunerWanGenerateVideo" + ], + { + "title_aux": "ComfyUI-musubi-tuner" + } + ], + "https://github.com/kenjiqq/qq-nodes-comfyui": [ + [ + "Any List", + "Any List Iterator", + "Any To Any", + "Axis Pack", + "Axis To Any", + "Axis Unpack", + "Load Lines From Text File", + "Slice List", + "Text Splitter", + "XY Grid Accumulator", + "XY Grid Helper" + ], + { + "title_aux": "qq-nodes-comfyui" + } + ], + "https://github.com/kevinmcmahondev/comfyui-kmcdev-image-filter-adjustments": [ + [ + "ImageBlankAlpha", + "ImageBlendMask", + "ImageFilterAdjustments", + "ImageMixColorByMask" + ], + { + "title_aux": "KMCDev Nodes" + } + ], + "https://github.com/kevinmcmahondev/comfyui-skin-tone-detector": [ + [ + "SkinToneDetector" + ], + { + "title_aux": "Skin Tone Detector for ComfyUI" + } + ], + "https://github.com/kft334/Knodes": [ + [ + "Image(s) To Websocket (Base64)", + "ImageOutput", + "Load Image (Base64)", + "Load Images (Base64)" + ], + { + "title_aux": "Knodes" + } + ], + "https://github.com/khanhlvg/vertex-ai-comfyui-nodes": [ + [ + "Chirp", + "Gemini", + "ImagenComputedMaskConfig", + "ImagenMaskEditing", + "Imagen_Product_Recontext", + "Imagen_T2I", + "Lyria", + "PreviewVideo", + "Veo2", + "Veo2Extend", + "Veo3", + "Veo_Prompt_Writer", + "Virtual_Try_On" + ], + { + "title_aux": "[Unofficial] Vertex AI Custom Nodes for ComfyUI" + } + ], + "https://github.com/kijai/ComfyUI-ADMotionDirector": [ + [ + "ADMD_AdditionalModelSelect", + "ADMD_CheckpointLoader", + "ADMD_ComfyModelLoader", + "ADMD_DiffusersLoader", + "ADMD_InitializeTraining", + "ADMD_LoadLora", + "ADMD_MakeBatchList", + "ADMD_SaveLora", + "ADMD_TrainLora", + "ADMD_ValidationSampler", + "ADMD_ValidationSettings" + ], + { + "title_aux": "Animatediff MotionLoRA Trainer" + } + ], + "https://github.com/kijai/ComfyUI-APISR-KJ": [ + [ + "APISR_upscale" + ], + { + "title_aux": "ComfyUI-APISR" + } + ], + "https://github.com/kijai/ComfyUI-BrushNet-Wrapper": [ + [ + "brushnet_ella_loader", + "brushnet_ipadapter_matteo", + "brushnet_model_loader", + "brushnet_sampler", + "brushnet_sampler_ella", + "powerpaint_brushnet_sampler" + ], + { + "title_aux": "ComfyUI-BrushNet-Wrapper" + } + ], + "https://github.com/kijai/ComfyUI-CCSR": [ + [ + "CCSR_Model_Select", + "CCSR_Upscale", + "DownloadAndLoadCCSRModel" + ], + { + "title_aux": "ComfyUI-CCSR" + } + ], + "https://github.com/kijai/ComfyUI-CogVideoXWrapper": [ + [ + "CogVideoContextOptions", + "CogVideoControlNet", + "CogVideoDecode", + "CogVideoEnhanceAVideo", + "CogVideoImageEncode", + "CogVideoImageEncodeFunInP", + "CogVideoLatentPreview", + "CogVideoLoraSelect", + "CogVideoLoraSelectComfy", + "CogVideoSampler", + "CogVideoTextEncode", + "CogVideoTextEncodeCombine", + "CogVideoTransformerEdit", + "CogVideoXFasterCache", + "CogVideoXFunResizeToClosestBucket", + "CogVideoXModelLoader", + "CogVideoXTeaCache", + "CogVideoXTorchCompileSettings", + "CogVideoXVAELoader", + "DownloadAndLoadCogVideoControlNet", + "DownloadAndLoadCogVideoGGUFModel", + "DownloadAndLoadCogVideoModel", + "DownloadAndLoadToraModel", + "ToraEncodeOpticalFlow", + "ToraEncodeTrajectory" + ], + { + "title_aux": "ComfyUI CogVideoX Wrapper" + } + ], + "https://github.com/kijai/ComfyUI-ControlNeXt-SVD": [ + [ + "ControlNextDecode", + "ControlNextDiffusersScheduler", + "ControlNextGetPoses", + "ControlNextSVDApply", + "ControlNextSampler", + "DownloadAndLoadControlNeXt" + ], + { + "title_aux": "ComfyUI nodes for ControlNext-SVD v2" + } + ], + "https://github.com/kijai/ComfyUI-DDColor": [ + [ + "DDColor_Colorize" + ], + { + "title_aux": "ComfyUI-DDColor" + } + ], + "https://github.com/kijai/ComfyUI-DepthAnythingV2": [ + [ + "DepthAnything_V2", + "DownloadAndLoadDepthAnythingV2Model" + ], + { + "title_aux": "ComfyUI-DepthAnythingV2" + } + ], + "https://github.com/kijai/ComfyUI-DiffusionLight": [ + [ + "chrome_ball_to_envmap", + "exposure_to_hdr" + ], + { + "title_aux": "DiffusionLight implementation for ComfyUI" + } + ], + "https://github.com/kijai/ComfyUI-DynamiCrafterWrapper": [ + [ + "DownloadAndLoadCLIPModel", + "DownloadAndLoadCLIPVisionModel", + "DownloadAndLoadDynamiCrafterCNModel", + "DownloadAndLoadDynamiCrafterModel", + "DynamiCrafterBatchInterpolation", + "DynamiCrafterCNLoader", + "DynamiCrafterControlnetApply", + "DynamiCrafterI2V", + "DynamiCrafterLoadInitNoise", + "DynamiCrafterModelLoader", + "ToonCrafterDecode", + "ToonCrafterInterpolation" + ], + { + "title_aux": "ComfyUI-DynamiCrafterWrapper" + } + ], + "https://github.com/kijai/ComfyUI-ELLA-wrapper": [ + [ + "diffusers_model_loader", + "diffusers_sampler", + "ella_model_loader", + "ella_sampler", + "ella_t5_embeds" + ], + { + "title_aux": "ComfyUI-ELLA-wrapper" + } + ], + "https://github.com/kijai/ComfyUI-Florence2": [ + [ + "DownloadAndLoadFlorence2Lora", + "DownloadAndLoadFlorence2Model", + "Florence2ModelLoader", + "Florence2Run" + ], + { + "preemptions": [ + "DownloadAndLoadFlorence2Lora", + "DownloadAndLoadFlorence2Model", + "Florence2ModelLoader", + "Florence2Run" + ], + "title_aux": "ComfyUI-Florence2" + } + ], + "https://github.com/kijai/ComfyUI-FluxTrainer": [ + [ + "ExtractFluxLoRA", + "FluxKohyaInferenceSampler", + "FluxTrainAndValidateLoop", + "FluxTrainBlockSelect", + "FluxTrainEnd", + "FluxTrainLoop", + "FluxTrainModelSelect", + "FluxTrainResume", + "FluxTrainSave", + "FluxTrainSaveModel", + "FluxTrainValidate", + "FluxTrainValidationSettings", + "FluxTrainerLossConfig", + "InitFluxLoRATraining", + "InitFluxTraining", + "InitSD3LoRATraining", + "InitSDXLLoRATraining", + "OptimizerConfig", + "OptimizerConfigAdafactor", + "OptimizerConfigProdigy", + "OptimizerConfigProdigyPlusScheduleFree", + "SD3ModelSelect", + "SD3TrainValidationSettings", + "SDXLModelSelect", + "SDXLTrainValidate", + "SDXLTrainValidationSettings", + "TrainDatasetAdd", + "TrainDatasetGeneralConfig", + "TrainDatasetRegularization", + "TrainNetworkConfig", + "UploadToHuggingFace", + "VisualizeLoss" + ], + { + "title_aux": "ComfyUI Flux Trainer" + } + ], + "https://github.com/kijai/ComfyUI-GIMM-VFI": [ + [ + "DownloadAndLoadGIMMVFIModel", + "GIMMVFI_interpolate" + ], + { + "title_aux": "ComfyUI-GIMM-VFI" + } + ], + "https://github.com/kijai/ComfyUI-Geowizard": [ + [ + "geowizard_model_loader", + "geowizard_sampler" + ], + { + "title_aux": "Geowizard depth and normal estimation in ComfyUI" + } + ], + "https://github.com/kijai/ComfyUI-HFRemoteVae": [ + [ + "HFRemoteVAE", + "HFRemoteVAEDecode" + ], + { + "title_aux": "ComfyUI-HFRemoteVae" + } + ], + "https://github.com/kijai/ComfyUI-HunyuanVideoWrapper": [ + [ + "DownloadAndLoadHyVideoTextEncoder", + "HunyuanVideoFresca", + "HunyuanVideoSLG", + "HyVideoBlockSwap", + "HyVideoCFG", + "HyVideoContextOptions", + "HyVideoCustomPromptTemplate", + "HyVideoDecode", + "HyVideoEmptyTextEmbeds", + "HyVideoEncode", + "HyVideoEncodeKeyframes", + "HyVideoEnhanceAVideo", + "HyVideoGetClosestBucketSize", + "HyVideoI2VEncode", + "HyVideoInverseSampler", + "HyVideoLatentPreview", + "HyVideoLoopArgs", + "HyVideoLoraBlockEdit", + "HyVideoLoraSelect", + "HyVideoModelLoader", + "HyVideoPromptMixSampler", + "HyVideoReSampler", + "HyVideoSTG", + "HyVideoSampler", + "HyVideoTeaCache", + "HyVideoTextEmbedBridge", + "HyVideoTextEmbedsLoad", + "HyVideoTextEmbedsSave", + "HyVideoTextEncode", + "HyVideoTextImageEncode", + "HyVideoTorchCompileSettings", + "HyVideoVAELoader" + ], + { + "title_aux": "ComfyUI-HunyuanVideoWrapper" + } + ], + "https://github.com/kijai/ComfyUI-IC-Light": [ + [ + "BackgroundScaler", + "CalculateNormalsFromImages", + "DetailTransfer", + "ICLightConditioning", + "LightSource", + "LoadAndApplyICLightUnet", + "LoadHDRImage" + ], + { + "title_aux": "ComfyUI-IC-Light" + } + ], + "https://github.com/kijai/ComfyUI-KJNodes": [ + [ + "AddLabel", + "AppendInstanceDiffusionTracking", + "AppendStringsToList", + "ApplyRifleXRoPE_HunuyanVideo", + "ApplyRifleXRoPE_WanVideo", + "AudioConcatenate", + "BOOLConstant", + "BatchCLIPSeg", + "BatchCropFromMask", + "BatchCropFromMaskAdvanced", + "BatchUncrop", + "BatchUncropAdvanced", + "BboxToInt", + "BboxVisualize", + "CFGZeroStarAndInit", + "CameraPoseVisualizer", + "CheckpointLoaderKJ", + "CheckpointPerturbWeights", + "ColorMatch", + "ColorToMask", + "CondPassThrough", + "ConditioningMultiCombine", + "ConditioningSetMaskAndCombine", + "ConditioningSetMaskAndCombine3", + "ConditioningSetMaskAndCombine4", + "ConditioningSetMaskAndCombine5", + "CreateAudioMask", + "CreateFadeMask", + "CreateFadeMaskAdvanced", + "CreateFluidMask", + "CreateGradientFromCoords", + "CreateGradientMask", + "CreateInstanceDiffusionTracking", + "CreateMagicMask", + "CreateShapeImageOnPath", + "CreateShapeMask", + "CreateShapeMaskOnPath", + "CreateTextMask", + "CreateTextOnPath", + "CreateVoronoiMask", + "CrossFadeImages", + "CrossFadeImagesMulti", + "CustomControlNetWeightsFluxFromList", + "CustomSigmas", + "CutAndDragOnPath", + "DiTBlockLoraLoader", + "DifferentialDiffusionAdvanced", + "DiffusionModelLoaderKJ", + "DownloadAndLoadCLIPSeg", + "DrawInstanceDiffusionTracking", + "DummyOut", + "EmptyLatentImageCustomPresets", + "EmptyLatentImagePresets", + "FastPreview", + "FilterZeroMasksAndCorrespondingImages", + "FlipSigmasAdjusted", + "FloatConstant", + "FloatToMask", + "FloatToSigmas", + "FluxBlockLoraSelect", + "GLIGENTextBoxApplyBatchCoords", + "GenerateNoise", + "GetImageRangeFromBatch", + "GetImageSizeAndCount", + "GetImagesFromBatchIndexed", + "GetLatentRangeFromBatch", + "GetLatentsFromBatchIndexed", + "GetMaskSizeAndCount", + "GradientToFloat", + "GrowMaskWithBlur", + "HunyuanVideoBlockLoraSelect", + "HunyuanVideoEncodeKeyframesToCond", + "INTConstant", + "ImageAddMulti", + "ImageAndMaskPreview", + "ImageBatchFilter", + "ImageBatchJoinWithTransition", + "ImageBatchMulti", + "ImageBatchRepeatInterleaving", + "ImageBatchTestPattern", + "ImageConcanate", + "ImageConcatFromBatch", + "ImageConcatMulti", + "ImageCropByMask", + "ImageCropByMaskAndResize", + "ImageCropByMaskBatch", + "ImageGrabPIL", + "ImageGridComposite2x2", + "ImageGridComposite3x3", + "ImageGridtoBatch", + "ImageNoiseAugmentation", + "ImageNormalize_Neg1_To_1", + "ImagePadForOutpaintMasked", + "ImagePadForOutpaintTargetSize", + "ImagePadKJ", + "ImagePass", + "ImagePrepForICLora", + "ImageResizeKJ", + "ImageResizeKJv2", + "ImageTensorList", + "ImageTransformByNormalizedAmplitude", + "ImageUncropByMask", + "ImageUpscaleWithModelBatched", + "InjectNoiseToLatent", + "InsertImageBatchByIndexes", + "InsertImagesToBatchIndexed", + "InsertLatentToIndexed", + "InterpolateCoords", + "Intrinsic_lora_sampling", + "JoinStringMulti", + "JoinStrings", + "LeapfusionHunyuanI2VPatcher", + "LoadAndResizeImage", + "LoadImagesFromFolderKJ", + "LoadResAdapterNormalization", + "LoraExtractKJ", + "MaskBatchMulti", + "MaskOrImageToWeight", + "MergeImageChannels", + "ModelPassThrough", + "ModelPatchTorchSettings", + "ModelSaveKJ", + "NormalizedAmplitudeToFloatList", + "NormalizedAmplitudeToMask", + "OffsetMask", + "OffsetMaskByNormalizedAmplitude", + "PadImageBatchInterleaved", + "PatchModelPatcherOrder", + "PathchSageAttentionKJ", + "PlotCoordinates", + "PointsEditor", + "PreviewAnimation", + "RemapImageRange", + "RemapMaskRange", + "ReplaceImagesInBatch", + "ResizeMask", + "ReverseImageBatch", + "RoundMask", + "SV3D_BatchSchedule", + "SaveImageKJ", + "SaveImageWithAlpha", + "SaveStringKJ", + "ScaleBatchPromptSchedule", + "ScheduledCFGGuidance", + "Screencap_mss", + "SeparateMasks", + "SetShakkerLabsUnionControlNetType", + "ShuffleImageBatch", + "SigmasToFloat", + "SkipLayerGuidanceWanVideo", + "Sleep", + "SomethingToString", + "SoundReactive", + "SplineEditor", + "SplitBboxes", + "SplitImageChannels", + "StableZero123_BatchSchedule", + "StringConstant", + "StringConstantMultiline", + "StringToFloatList", + "StyleModelApplyAdvanced", + "Superprompt", + "TimerNodeKJ", + "TorchCompileControlNet", + "TorchCompileCosmosModel", + "TorchCompileLTXModel", + "TorchCompileModelFluxAdvanced", + "TorchCompileModelFluxAdvancedV2", + "TorchCompileModelHyVideo", + "TorchCompileModelWanVideo", + "TorchCompileModelWanVideoV2", + "TorchCompileVAE", + "TransitionImagesInBatch", + "TransitionImagesMulti", + "VAELoaderKJ", + "VRAM_Debug", + "Wan21BlockLoraSelect", + "WanVideoEnhanceAVideoKJ", + "WanVideoNAG", + "WanVideoTeaCacheKJ", + "WebcamCaptureCV2", + "WeightScheduleConvert", + "WeightScheduleExtend", + "WidgetToString" + ], + { + "title_aux": "KJNodes for ComfyUI" + } + ], + "https://github.com/kijai/ComfyUI-KwaiKolorsWrapper": [ + [ + "DownloadAndLoadChatGLM3", + "DownloadAndLoadKolorsModel", + "KolorsSampler", + "KolorsTextEncode", + "LoadChatGLM3" + ], + { + "title_aux": "ComfyUI-KwaiKolorsWrapper" + } + ], + "https://github.com/kijai/ComfyUI-LBMWrapper": [ + [ + "LBMSampler", + "LoadLBMModel" + ], + { + "title_aux": "ComfyUI-LBMWrapper" + } + ], + "https://github.com/kijai/ComfyUI-LLaVA-OneVision": [ + [ + "DownloadAndLoadLLaVAOneVisionModel", + "LLaVA_OneVision_Run", + "OneVisionCaptionFolder", + "SaveCaptionToTextFile" + ], + { + "title_aux": "ComfyUI Llava-OneVision" + } + ], + "https://github.com/kijai/ComfyUI-LVCDWrapper": [ + [ + "LVCDDecoder", + "LVCDSampler", + "LoadLVCDModel" + ], + { + "title_aux": "ComfyUI wrapper nodes for LVCD" + } + ], + "https://github.com/kijai/ComfyUI-LaVi-Bridge-Wrapper": [ + [ + "lavi_bridge_llama_encoder", + "lavi_bridge_t5_encoder", + "lavibridge_model_loader", + "lavibridge_sampler" + ], + { + "title_aux": "ComfyUI-LaVi-Bridge-Wrapper" + } + ], + "https://github.com/kijai/ComfyUI-LivePortraitKJ": [ + [ + "DownloadAndLoadLivePortraitModels", + "KeypointScaler", + "KeypointsToImage", + "LivePortraitComposite", + "LivePortraitCropper", + "LivePortraitLoadCropper", + "LivePortraitLoadFaceAlignmentCropper", + "LivePortraitLoadMediaPipeCropper", + "LivePortraitProcess", + "LivePortraitRetargeting" + ], + { + "title_aux": "ComfyUI-LivePortraitKJ" + } + ], + "https://github.com/kijai/ComfyUI-Lotus": [ + [ + "LoadLotusModel", + "LotusSampler" + ], + { + "title_aux": "ComfyUI-Lotus" + } + ], + "https://github.com/kijai/ComfyUI-LuminaWrapper": [ + [ + "DownloadAndLoadGemmaModel", + "DownloadAndLoadLuminaModel", + "GemmaSampler", + "LuminaGemmaTextEncode", + "LuminaGemmaTextEncodeArea", + "LuminaT2ISampler", + "LuminaTextAreaAppend" + ], + { + "title_aux": "ComfyUI-LuminaWrapper" + } + ], + "https://github.com/kijai/ComfyUI-Marigold": [ + [ + "ColorizeDepthmap", + "MarigoldDepthEstimation", + "MarigoldDepthEstimationVideo", + "MarigoldDepthEstimation_v2", + "MarigoldDepthEstimation_v2_video", + "MarigoldModelLoader", + "RemapDepth", + "SaveImageOpenEXR" + ], + { + "title_aux": "Marigold depth estimation in ComfyUI" + } + ], + "https://github.com/kijai/ComfyUI-MimicMotionWrapper": [ + [ + "DiffusersScheduler", + "DownloadAndLoadMimicMotionModel", + "MimicMotionDecode", + "MimicMotionGetPoses", + "MimicMotionSampler" + ], + { + "title_aux": "ComfyUI-MimicMotionWrapper" + } + ], + "https://github.com/kijai/ComfyUI-MoGe": [ + [ + "DownloadAndLoadMoGeModel", + "MoGeProcess" + ], + { + "title_aux": "ComfyUI-MoGe" + } + ], + "https://github.com/kijai/ComfyUI-OpenDiTWrapper": [ + [ + "DownloadAndLoadOpenDiTT5Model", + "DownloadAndLoadOpenSoraModel", + "DownloadAndLoadOpenSoraVAE", + "OpenDiTConditioning", + "OpenDiTSampler", + "OpenSoraDecode", + "OpenSoraEncodeReference" + ], + { + "title_aux": "ComfyUI-OpenDiTWrapper" + } + ], + "https://github.com/kijai/ComfyUI-PyramidFlowWrapper": [ + [ + "PyramidFlowLatentPreview", + "PyramidFlowSampler", + "PyramidFlowTextEncode", + "PyramidFlowTorchCompileSettings", + "PyramidFlowTransformerLoader", + "PyramidFlowVAEDecode", + "PyramidFlowVAEEncode", + "PyramidFlowVAELoader" + ], + { + "title_aux": "ComfyUI PyramidFlow Wrapper" + } + ], + "https://github.com/kijai/ComfyUI-SUPIR": [ + [ + "SUPIR_Upscale", + "SUPIR_conditioner", + "SUPIR_decode", + "SUPIR_encode", + "SUPIR_first_stage", + "SUPIR_model_loader", + "SUPIR_model_loader_v2", + "SUPIR_model_loader_v2_clip", + "SUPIR_sample", + "SUPIR_tiles" + ], + { + "title_aux": "ComfyUI-SUPIR" + } + ], + "https://github.com/kijai/ComfyUI-StableXWrapper": [ + [ + "DifferenceExtractorNode", + "DownloadAndLoadStableXModel", + "StableXProcessImage" + ], + { + "title_aux": "ComfyUI-StableXWrapper" + } + ], + "https://github.com/kijai/ComfyUI-WanVideoWrapper": [ + [ + "CreateCFGScheduleFloatList", + "DownloadAndLoadWav2VecModel", + "DummyComfyWanModelObject", + "ExtractStartFramesForContinuations", + "FantasyTalkingModelLoader", + "FantasyTalkingWav2VecEmbeds", + "LoadWanVideoClipTextEncoder", + "LoadWanVideoT5TextEncoder", + "MultiTalkModelLoader", + "MultiTalkWav2VecEmbeds", + "ReCamMasterPoseVisualizer", + "WanVideoATITracks", + "WanVideoATITracksVisualize", + "WanVideoATI_comfy", + "WanVideoApplyNAG", + "WanVideoBlockList", + "WanVideoBlockSwap", + "WanVideoClipVisionEncode", + "WanVideoContextOptions", + "WanVideoControlEmbeds", + "WanVideoControlnet", + "WanVideoControlnetLoader", + "WanVideoDecode", + "WanVideoDiffusionForcingSampler", + "WanVideoEasyCache", + "WanVideoEmptyEmbeds", + "WanVideoEncode", + "WanVideoEnhanceAVideo", + "WanVideoExperimentalArgs", + "WanVideoFlowEdit", + "WanVideoFreeInitArgs", + "WanVideoFunCameraEmbeds", + "WanVideoImageClipEncode", + "WanVideoImageResizeToClosest", + "WanVideoImageToVideoEncode", + "WanVideoImageToVideoMultiTalk", + "WanVideoLoopArgs", + "WanVideoLoraBlockEdit", + "WanVideoLoraSelect", + "WanVideoLoraSelectMulti", + "WanVideoMagCache", + "WanVideoMiniMaxRemoverEmbeds", + "WanVideoModelLoader", + "WanVideoPhantomEmbeds", + "WanVideoReCamMasterCameraEmbed", + "WanVideoReCamMasterDefaultCamera", + "WanVideoReCamMasterGenerateOrbitCamera", + "WanVideoRealisDanceLatents", + "WanVideoSLG", + "WanVideoSampler", + "WanVideoSetBlockSwap", + "WanVideoSetLoRAs", + "WanVideoSetRadialAttention", + "WanVideoTeaCache", + "WanVideoTextEmbedBridge", + "WanVideoTextEncode", + "WanVideoTextEncodeSingle", + "WanVideoTinyVAELoader", + "WanVideoTorchCompileSettings", + "WanVideoUni3C_ControlnetLoader", + "WanVideoUni3C_embeds", + "WanVideoUniAnimateDWPoseDetector", + "WanVideoUniAnimatePoseInput", + "WanVideoVACEEncode", + "WanVideoVACEModelSelect", + "WanVideoVACEStartToEndFrame", + "WanVideoVAELoader", + "WanVideoVRAMManagement" + ], + { + "title_aux": "ComfyUI-WanVideoWrapper" + } + ], + "https://github.com/kijai/ComfyUI-depth-fm": [ + [ + "Depth_fm" + ], + { + "title_aux": "ComfyUI-depth-fm" + } + ], + "https://github.com/kijai/ComfyUI-moondream": [ + [ + "MoondreamQuery", + "MoondreamQueryCaptions" + ], + { + "title_aux": "ComfyUI-moondream" + } + ], + "https://github.com/kijai/ComfyUI-segment-anything-2": [ + [ + "DownloadAndLoadSAM2Model", + "Florence2toCoordinates", + "Sam2AutoSegmentation", + "Sam2Segmentation", + "Sam2VideoSegmentation", + "Sam2VideoSegmentationAddPoints" + ], + { + "preemptions": [ + "DownloadAndLoadSAM2Model", + "Florence2toCoordinates", + "Sam2AutoSegmentation", + "Sam2Segmentation", + "Sam2VideoSegmentation", + "Sam2VideoSegmentationAddPoints" + ], + "title_aux": "ComfyUI-segment-anything-2" + } + ], + "https://github.com/kimara-ai/ComfyUI-Kimara-AI-Advanced-Watermarks": [ + [ + "KimaraAIBatchImages", + "KimaraAIWatermarker" + ], + { + "title_aux": "Kimara.ai's Advanced Watermarking Tools" + } + ], + "https://github.com/kinfolk0117/ComfyUI_GradientDeepShrink": [ + [ + "GradientPatchModelAddDownscale", + "GradientPatchModelAddDownscaleAdvanced" + ], + { + "title_aux": "ComfyUI_GradientDeepShrink" + } + ], + "https://github.com/kinfolk0117/ComfyUI_GridSwapper": [ + [ + "GridSwapper" + ], + { + "title_aux": "Gridswapper" + } + ], + "https://github.com/kinfolk0117/ComfyUI_Pilgram": [ + [ + "Pilgram" + ], + { + "title_aux": "ComfyUI_Pilgram" + } + ], + "https://github.com/kinfolk0117/ComfyUI_SimpleTiles": [ + [ + "DynamicTileMerge", + "DynamicTileSplit", + "TileCalc", + "TileMerge", + "TileSplit" + ], + { + "title_aux": "SimpleTiles" + } + ], + "https://github.com/kk8bit/KayTool": [ + [ + "AB_Images", + "AIO_Translater", + "Abc_Math", + "Baidu_Translater", + "Color_Adjustment", + "Custom_Save_Image", + "Display_Any", + "Image_Composer", + "Image_Cropper", + "Image_Mask_Composer", + "Image_Resizer", + "Image_Size_Extractor", + "Kay_BiRefNet_Loader", + "Load_Image_Folder", + "Mask_Blur_Plus", + "Mask_Filler", + "Preview_Mask", + "Preview_Mask_Plus", + "RemBG_Loader", + "Remove_BG", + "Slider_10", + "Slider_100", + "Slider_1000", + "Strong_Prompt", + "Tencent_Translater", + "Text", + "To_Int" + ], + { + "title_aux": "KayTool" + } + ], + "https://github.com/klinter007/klinter_nodes": [ + [ + "AspectSelector", + "BBoxCropper", + "FolderLoader", + "Json Extractor - klinter", + "LoadImagePlus", + "LoadVideoForExtendingKlinter", + "OutpaintPadding", + "PrepVideoForExtendKlinter", + "SaveAudioPlus", + "SizeSelector", + "SpeedRamp", + "YellowBus", + "ZoomOutComposer", + "concat", + "filter", + "nodevalue2stringmulti", + "string_contact_multi" + ], + { + "title_aux": "Klinter_nodes" + } + ], + "https://github.com/kmlbdh/ComfyUI_LocalLLMNodes": [ + [ + "AddUserLocalKontextPreset", + "LocalKontextPromptGenerator", + "RemoveUserLocalKontextPreset", + "SetLocalLLMServiceConnector" + ], + { + "title_aux": "ComfyUI_LocalLLMNodes" + } + ], + "https://github.com/knuknX/ComfyUI-Image-Tools": [ + [ + "BatchImagePathLoader", + "ImageBgRemoveProcessor", + "ImageCheveretoUploader", + "ImageStandardResizeProcessor", + "JSONMessageNotifyTool", + "PreviewJSONNode", + "SingleImagePathLoader", + "SingleImageUrlLoader" + ], + { + "title_aux": "ComfyUI-Image-Tools" + } + ], + "https://github.com/kohs100/comfyui-ppwc": [ + [ + "PPWCReplace" + ], + { + "author": "Phospholipids", + "description": "This extension offers wildcard prompting works solely in workflow.", + "nickname": "PPWC", + "title": "PPWildCard", + "title_aux": "PPWildCard" + } + ], + "https://github.com/kohya-ss/ControlNet-LLLite-ComfyUI": [ + [ + "LLLiteLoader" + ], + { + "title_aux": "ControlNet-LLLite-ComfyUI" + } + ], + "https://github.com/komojini/ComfyUI_SDXL_DreamBooth_LoRA_CustomNodes": [ + [ + "S3 Bucket LoRA", + "S3Bucket_Load_LoRA", + "XL DreamBooth LoRA", + "XLDB_LoRA" + ], + { + "title_aux": "ComfyUI_SDXL_DreamBooth_LoRA_CustomNodes" + } + ], + "https://github.com/komojini/komojini-comfyui-nodes": [ + [ + "BatchCreativeInterpolationNodeDynamicSettings", + "CachedGetter", + "DragNUWAImageCanvas", + "FlowBuilder", + "FlowBuilder (adv)", + "FlowBuilder (advanced)", + "FlowBuilder (advanced) Setter", + "FlowBuilderSetter", + "FlowBuilderSetter (adv)", + "Getter", + "ImageCropByRatio", + "ImageCropByRatioAndResize", + "ImageGetter", + "ImageMerger", + "ImagesCropByRatioAndResizeBatch", + "KSamplerAdvancedCacheable", + "KSamplerCacheable", + "Setter", + "UltimateVideoLoader", + "UltimateVideoLoader (simple)", + "YouTubeVideoLoader" + ], + { + "title_aux": "komojini-comfyui-nodes" + } + ], + "https://github.com/kostenickj/jk-comfyui-helpers": [ + [ + "EasyHRFix", + "EasyHRFix_Context", + "JKAnythingToString", + "JKBigContext", + "JKDynamicThresholdingMultiModel", + "JKEasyCheckpointLoader", + "JKEasyDetailer", + "JKEasyDetailer_Context", + "JKEasyKSampler_Context", + "JKEasyUpscaleImage", + "JKEasyWatermark", + "JKInspireSchedulerAdapter", + "JKLilContext", + "JKMultiModelSamplerUnpatch", + "JKStringEmpty", + "JKStringEquals", + "JKStringNotEmpty", + "JKStringNotEquals", + "JKStringToSamplerAdapter" + ], + { + "title_aux": "comfyui-jk-easy-nodes" + } + ], + "https://github.com/kpsss34/ComfyUI-kpsss34": [ + [ + "SD35PlusImageEnhancer", + "SD35PlusLoader" + ], + { + "title_aux": "ComfyUI kpsss34 Custom Node" + } + ], + "https://github.com/krmahil/comfyui-hollow-preserve": [ + [ + "RemoveEnclosedMaskedAreas" + ], + { + "title_aux": "Hollow Preserve" + } + ], + "https://github.com/kukuo6666/ComfyUI-Equirect": [ + [ + "CubemapToEquirect", + "EquirectToCubemap" + ], + { + "title_aux": "ComfyUI Equirectangular Tools" + } + ], + "https://github.com/kungful/ComfyUI_to_webui": [ + [ + "BarcodeGeneratorNode", + "Barcode_seed", + "DeepseekNode", + "Go_to_image", + "GradioInputImage", + "GradioTextBad", + "GradioTextOk", + "HuaFloatNode", + "HuaIntNode", + "Hua_CheckpointLoaderSimple", + "Hua_LoraLoader", + "Hua_LoraLoaderModelOnly", + "Hua_Output", + "Hua_UNETLoader", + "Hua_Video_Output", + "Hua_gradio_Seed", + "Hua_gradio_jsonsave", + "Hua_gradio_resolution", + "Huaword", + "Modelhua", + "brucelee", + "\u5c0f\u5b57\u4f53\u8bf4\u660e\uff1a\u6211\u662fcomfyui_hua_boy\u7684model" + ], + { + "title_aux": "ComfyUI_to_webui" + } + ], + "https://github.com/kunieone/ComfyUI_alkaid": [ + [ + "A_EmptyLatentImageLongside", + "A_Face3DSwapper", + "A_FaceCrop", + "A_FacePaste", + "A_GetImageSize", + "A_OpenPosePreprocessor", + "AdapterFace", + "AdapterFaceLoader", + "AdapterStyle", + "AdapterStyleLoader", + "AlkaidLoader", + "ApplyAdapter", + "ApplyControlNet_KPS", + "CombineAdapterPatch", + "KSamplerHires" + ], + { + "title_aux": "ComfyUI_alkaid" + } + ], + "https://github.com/kwaroran/abg-comfyui": [ + [ + "Remove Image Background (abg)" + ], + { + "title_aux": "abg-comfyui" + } + ], + "https://github.com/kycg/comfyui-Lora-auto-downloader": [ + [ + "Kw_JsonLoraLoader", + "Kw_Json_Lora_CivitAIDownloader" + ], + { + "title_aux": "Kw_Json_Lora_CivitAIDownloader" + } + ], + "https://github.com/l-comm/WatermarkRemoval": [ + [ + "FindWatermarkNode", + "RemoveWatermarkNode" + ], + { + "author": "l-comm", + "description": "Remove watermark", + "nickname": "Watermark Removal", + "title": "Watermark Removal", + "title_aux": "WatermarkRemoval" + } + ], + "https://github.com/l20richo/ComfyUI-Azure-Blob-Storage": [ + [ + "DownloadFileBLOB", + "LoadImageBLOB", + "SaveImageBLOB", + "SaveVideoFilesBLOB", + "UploadFileBLOB" + ], + { + "title_aux": "ComfyUI-Azure-Blob-Storage" + } + ], + "https://github.com/l3ony2k/comfyui-leon-nodes": [ + [ + "Leon_ByteDance_Image_API_Node", + "Leon_DALLE_Image_API_Node", + "Leon_Flux_Image_API_Node", + "Leon_Flux_Kontext_API_Node", + "Leon_GPT_Image_API_Node", + "Leon_Google_Image_API_Node", + "Leon_Hypr_Upload_Node", + "Leon_Ideogram_Image_API_Node", + "Leon_Image_Split_4Grid_Node", + "Leon_ImgBB_Upload_Node", + "Leon_LLM_Chat_API_Node", + "Leon_LLM_JSON_API_Node", + "Leon_Luma_AI_Image_API_Node", + "Leon_Midjourney_Describe_API_Node", + "Leon_Midjourney_Proxy_API_Node", + "Leon_Midjourney_Upload_API_Node", + "Leon_Model_Selector_Node", + "Leon_Recraft_Image_API_Node", + "Leon_StableDiffusion_35_API_Node", + "Leon_StableDiffusion_3_Ultra_API_Node", + "Leon_StableDiffusion_XL_API_Node", + "Leon_String_Combine_Node" + ], + { + "nodename_pattern": "^\ud83e\udd16 Leon", + "title_aux": "Leon's Utility and API Integration Nodes" + } + ], + "https://github.com/laksjdjf/Batch-Condition-ComfyUI": [ + [ + "Batch String", + "CLIP Text Encode (Batch)", + "String Input" + ], + { + "title_aux": "Batch-Condition-ComfyUI" + } + ], + "https://github.com/laksjdjf/ComfyUI-Imatrix": [ + [ + "ImatrixUNETLoader", + "LoRAdiff", + "SaveImatrix" + ], + { + "title_aux": "ComfyUI-Imatrix" + } + ], + "https://github.com/laksjdjf/LCMSampler-ComfyUI": [ + [ + "SamplerLCM", + "TAESDLoader" + ], + { + "title_aux": "LCMSampler-ComfyUI" + } + ], + "https://github.com/laksjdjf/LoRTnoC-ComfyUI": [ + [ + "LortnocLoader" + ], + { + "title_aux": "LoRTnoC-ComfyUI" + } + ], + "https://github.com/laksjdjf/cd-tuner_negpip-ComfyUI": [ + [ + "CDTuner", + "Negapip", + "Negpip" + ], + { + "title_aux": "cd-tuner_negpip-ComfyUI" + } + ], + "https://github.com/laksjdjf/cgem156-ComfyUI": [ + [ + "GradualLatentSampler", + "LCMSamplerRCFG", + "LoadAestheticShadow", + "PredictAesthetic", + "TCDSampler", + "TextScheduler" + ], + { + "title_aux": "cgem156-ComfyUI\ud83c\udf4c" + } + ], + "https://github.com/laksjdjf/pfg-ComfyUI": [ + [ + "PFG" + ], + { + "title_aux": "pfg-ComfyUI" + } + ], + "https://github.com/larsupb/LoRA-Merger-ComfyUI": [ + [ + "PM LoRA Apply", + "PM LoRA Loader", + "PM LoRA Merger", + "PM LoRA Resizer", + "PM LoRA SVD Merger", + "PM LoRA Save", + "XY: PM LoRA Modes", + "XY: PM LoRA SVD Rank", + "XY: PM LoRA Strengths" + ], + { + "title_aux": "LoRA Power-Merger ComfyUI" + } + ], + "https://github.com/latenightlabs/ComfyUI-LNL": [ + [ + "LNL_FrameSelectorV3", + "LNL_FrameSelectorV4" + ], + { + "title_aux": "LNL Frame Selector" + } + ], + "https://github.com/lazniak/Head-Orientation-Node-for-ComfyUI---by-PabloGFX": [ + [ + "HeadOrientationNode" + ], + { + "title_aux": "Head-Orientation-Node - by PabloGFX" + } + ], + "https://github.com/lazniak/LiquidTime-Interpolation": [ + [ + "LiquidTime" + ], + { + "title_aux": "LiquidTime - by PabloGFX" + } + ], + "https://github.com/lazniak/comfyui-google-photos-loader": [ + [ + "ContentFilter", + "DatePicker", + "Google Photos Album Lister", + "Google Photos Album Loader", + "Google Photos Album Selector", + "Google Photos Cache Manager", + "Google Photos Clear Cache", + "Google Photos Images Loader", + "Google Photos Login/Logout" + ], + { + "title_aux": "Google Photos Loader - by PabloGFX" + } + ], + "https://github.com/lc03lc/Comfyui_OmniConsistency": [ + [ + "Comfyui_OmniConsistency" + ], + { + "title_aux": "ComfyUI OmniConsistency Nodes" + } + ], + "https://github.com/lceric/comfyui-gpt-image": [ + [ + "GPTImage1Generate" + ], + { + "title_aux": "comfyui-gpt-image" + } + ], + "https://github.com/lebrosoft/ComfyUI-VideoChatWrapper": [ + [ + "AudioCombine", + "ConcatAudio", + "ConvertAudioChannels", + "JoinAudio", + "ResampleAudio", + "SplitAudio", + "VCW_LoadVideo", + "VCW_ModelLoader", + "VCW_VideoSummary" + ], + { + "title_aux": "ComfyUI-VideoChatWrapper" + } + ], + "https://github.com/leeguandong/ComfyUI_1Prompt1Story": [ + [ + "GenerateStoryImage", + "PromptStoryModelLoader" + ], + { + "title_aux": "ComfyUI_1Prompt1Story" + } + ], + "https://github.com/leeguandong/ComfyUI_ChatGen": [ + [ + "ChatGenGenerate", + "ChatGenImageProcessor", + "ChatGenModelLoader" + ], + { + "title_aux": "ComfyUI_ChatGen" + } + ], + "https://github.com/leeguandong/ComfyUI_Cogview4": [ + [ + "CogView4ImageGenerator", + "CogView4ModelLoader" + ], + { + "title_aux": "ComfyUI_Cogview4" + } + ], + "https://github.com/leeguandong/ComfyUI_CompareModelWeights": [ + [ + "CheckPointLoader_Compare", + "CompareModelWeightsDiff", + "CompareModelWeightsDiffNormalized", + "PreviewImageCompareModelWeights" + ], + { + "title_aux": "ComfyUI_CompareModelWeights" + } + ], + "https://github.com/leeguandong/ComfyUI_CrossImageAttention": [ + [ + "AppearanceTransferInference", + "AppearanceTransferModelModelLoader", + "CIAConfig", + "LoadImagePath", + "LoadLatents" + ], + { + "title_aux": "ComfyUI nodes to use CrossImageAttention" + } + ], + "https://github.com/leeguandong/ComfyUI_DeepSeekVL2": [ + [ + "deepseek_vl2_inference", + "deepseek_vl2_model_loader" + ], + { + "title_aux": "ComfyUI_DeepSeekVL2" + } + ], + "https://github.com/leeguandong/ComfyUI_FluxAttentionMask": [ + [ + "AMModelLoader", + "AMSample", + "AttentionMask" + ], + { + "title_aux": "ComfyUI nodes to use AttentionMask" + } + ], + "https://github.com/leeguandong/ComfyUI_FluxClipWeight": [ + [ + "CLIPTextEncodeFluxWeight" + ], + { + "title_aux": "ComfyUI nodes to use FluxClipWeight" + } + ], + "https://github.com/leeguandong/ComfyUI_FluxCustomId": [ + [ + "ApplyCustomIDFlux", + "CustomIDModelLoader" + ], + { + "title_aux": "ComfyUI_FluxCustomId" + } + ], + "https://github.com/leeguandong/ComfyUI_FluxLayerDiffuse": [ + [ + "FluxTransparentI2I", + "FluxTransparentModelLoader", + "FluxTransparentT2I" + ], + { + "title_aux": "ComfyUI_FluxLayerDiffuse" + } + ], + "https://github.com/leeguandong/ComfyUI_Gemma3": [ + [ + "ApplyGemma3", + "Gemma3ModelLoader" + ], + { + "title_aux": "ComfyUI_Gemma3" + } + ], + "https://github.com/leeguandong/ComfyUI_InternVL2": [ + [ + "DynamicPreprocess", + "InternVLHFInference", + "InternVLLMDEPLOYInference", + "InternVLModelLoader" + ], + { + "title_aux": "ComfyUI_InternVL2" + } + ], + "https://github.com/leeguandong/ComfyUI_LLaSM": [ + [ + "LLaSM2Interface", + "LLaSM2ModelLoader", + "LLaSMLoadAudio" + ], + { + "title_aux": "ComfyUI_LLaSM" + } + ], + "https://github.com/leeguandong/ComfyUI_M3Net": [ + [ + "M3Net_Interface", + "M3Net_ModelLoader" + ], + { + "title_aux": "ComfyUI_M3Net" + } + ], + "https://github.com/leeguandong/ComfyUI_MasaCtrl": [ + [ + "DirectSampler", + "MasaCtrlConcatImage", + "MasaCtrlInversion", + "MasaCtrlLoadImage", + "MasaCtrlModelLoader", + "MutualSelfAttentionControlMaskAutoSampler", + "MutualSelfAttentionControlSampler" + ], + { + "title_aux": "ComfyUI nodes to use MasaCtrl" + } + ], + "https://github.com/leeguandong/ComfyUI_QWQ32B": [ + [ + "QwQModelLoader", + "QwQTextGenerator" + ], + { + "title_aux": "ComfyUI_QWQ32B" + } + ], + "https://github.com/leeguandong/ComfyUI_Style_Aligned": [ + [ + "SAControlnet_ModelLoader", + "SADepth", + "SAHandler", + "SAInversion", + "SASDXLControlnetKsampler", + "SASDXLKampler", + "SASDXLTransferKsampler", + "SASDXL_ModelLoader", + "SchedulerLoader" + ], + { + "title_aux": "ComfyUI nodes to use Style-Aligned" + } + ], + "https://github.com/leeguandong/ComfyUI_VideoEditing": [ + [ + "LoadVideo2Images", + "VEdit_ControlNet_ModelLoader", + "VEdit_ModelLoader", + "VEdit_Sampler", + "VEdit_image2canny" + ], + { + "title_aux": "ComfyUI nodes to use VideoEditing" + } + ], + "https://github.com/leeguandong/ComfyUI_VisualAttentionMap": [ + [ + "DecodeLatent", + "HFModelLoader", + "ShowCrossAttn", + "ShowImages", + "ShowSelfAttn", + "Text2ImageInference" + ], + { + "title_aux": "ComfyUI_VisualAttentionMap" + } + ], + "https://github.com/leestuartx/ComfyUI-GG": [ + [ + "AddPaddingToImage", + "ForLoopNode", + "ImageAndTextDescriptionBySeed", + "ImageMetadataExtractor", + "InputNode", + "MetadataExtractBySeed", + "MetadataExtractorBySeed", + "OutputNode", + "ResizeImageProportionally", + "WorkspaceNode" + ], + { + "title_aux": "ComfyUI-GG" + } + ], + "https://github.com/lenskikh/ComfyUI-Prompt-Worker": [ + [ + "Clip and Text -> Encode", + "Prompt Body", + "Prompt Clothes", + "Prompt Merger", + "Prompt Worker", + "Prompt \u0421onstructor" + ], + { + "title_aux": "Propmt Worker" + } + ], + "https://github.com/leoleelxh/Comfy-Topaz-Photo": [ + [ + "ComfyTopazPhoto", + "ComfyTopazPhotoTestAndClean" + ], + { + "title_aux": "Comfy-Topaz-Photo" + } + ], + "https://github.com/leoleelxh/ComfyUI-LLMs": [ + [ + "LLMs Chat", + "LLMs Vision Unified", + "LLMs_Vision_Unified" + ], + { + "title_aux": "ComfyUI-LLMs" + } + ], + "https://github.com/leonardomiramondi/flux-context-comfyui": [ + [ + "FluxKontextNode" + ], + { + "title_aux": "Flux Context ComfyUI Node" + } + ], + "https://github.com/lepiai/ComfyUI-Minitools": [ + [ + "LP-CropTransparentEdges", + "LP-ImageToMaskWithAlpha", + "LP-TranslateToEN", + "LP-color2RGB", + "LP-hex2dec", + "NumericSlider" + ], + { + "title_aux": "ComfyUI-Minitools" + } + ], + "https://github.com/lerignoux/ComfyUI-PechaKucha": [ + [ + "GeneratePowerpoint", + "SplitPrompt" + ], + { + "title_aux": "ComfyUI-PechaKucha" + } + ], + "https://github.com/lgldlk/ComfyUI-PC-ding-dong": [ + [ + "pc ding dong", + "pc ding dong text", + "pc time sleep" + ], + { + "title_aux": "ComfyUI-PC-ding-dong" + } + ], + "https://github.com/lgldlk/ComfyUI-PSD-Replace": [ + [ + "psd replace" + ], + { + "title_aux": "ComfyUI-PSD-Replace" + } + ], + "https://github.com/liangt/comfyui-loadimagewithsubfolder": [ + [ + "LoadImageWithSubfolder" + ], + { + "title_aux": "comfyui-loadimagewithsubfolder" + } + ], + "https://github.com/licyk/ComfyUI-HakuImg": [ + [ + "BlendImage", + "Blur", + "Chromatic", + "Color", + "Curve", + "CustomExif", + "Flip", + "Glow", + "InOutPaint", + "LenDistortion", + "OutlineExpansion", + "PixelOE", + "Pixelize", + "PreResize", + "SaveImageWithCustomExif", + "Sketch", + "TiltShift" + ], + { + "title_aux": "ComfyUI-HakuImg" + } + ], + "https://github.com/licyk/ComfyUI-TCD-Sampler": [ + [ + "TCDScheduler" + ], + { + "title_aux": "ComfyUI-TCD-Sampler" + } + ], + "https://github.com/lihaoyun6/ComfyUI-BlindWatermark": [ + [ + "ApplyBlindWatermark", + "ApplyBlindWatermarkAdvanced", + "DecodeBlindWatermark", + "DecodeBlindWatermarkAdvanced" + ], + { + "title_aux": "ComfyUI-BlindWatermark" + } + ], + "https://github.com/lihaoyun6/ComfyUI-CSV-Random-Picker": [ + [ + "CSVRandomPicker" + ], + { + "title_aux": "ComfyUI-CSV-Random-Picker" + } + ], + "https://github.com/lingha0h/comfyui_kj": [ + [ + "cpm_textInput" + ], + { + "title_aux": "comfyui_kj" + } + ], + "https://github.com/linjian-ufo/ComfyUI_GLM4V_voltspark": [ + [ + "Glm4vBatchNode", + "Glm4vNode" + ], + { + "title_aux": "GLM-4V Image Descriptor" + } + ], + "https://github.com/linjian-ufo/comfyui_deepseek_lj257_update": [ + [ + "DeepSeekChatNode" + ], + { + "title_aux": "DeepSeek Chat Node for ComfyUI" + } + ], + "https://github.com/linksluckytime/comfyui_snacknodes": [ + [ + "ImageInfo", + "ImageScaler", + "TextBox", + "TextProcessor" + ], + { + "title_aux": "comfyui_snacknodes" + } + ], + "https://github.com/linshier/comfyui-remote-tools": [ + [ + "LoadBase64(js)", + "LoadBase64FromRemote", + "SendBase64ToRemote" + ], + { + "title_aux": "comfyui-remote-tools" + } + ], + "https://github.com/lisaks/comfyui-panelforge": [ + [ + "FrameNode", + "PageNode", + "RowNode" + ], + { + "title_aux": "Pixstri ComfyUI Comics" + } + ], + "https://github.com/liuqianhonga/ComfyUI-Html2Image": [ + [ + "CameraWatermark", + "TemplateToImage", + "WebpageScreenshot" + ], + { + "title_aux": "ComfyUI-Html2Image" + } + ], + "https://github.com/liuqianhonga/ComfyUI-Image-Compressor": [ + [ + "BatchImageCompressor", + "ImageCompressor" + ], + { + "title_aux": "ComfyUI-Image-Compressor" + } + ], + "https://github.com/liuqianhonga/ComfyUI-QHNodes": [ + [ + "BatchImageCompressor", + "CameraWatermark", + "DownloadCheckpoint", + "DownloadControlNet", + "DownloadLora", + "DownloadUNET", + "DownloadVAE", + "DynamicExpression", + "FileSave", + "Gemini", + "ImageCompressor", + "ImageCountFromFolder", + "JsonToCSV", + "JsonUnpack", + "LoadImageFromFolder", + "LoadLoraFromFolder", + "LoadPromptsFromFolder", + "PresetSizeLatent", + "SamplerSettings", + "ShowTranslateString", + "StringConverter", + "StringFormatter", + "StringList", + "StringListFromCSV", + "StringListToCSV", + "StringMatcher", + "StringTranslate", + "TemplateToImage", + "TimeFormatter", + "UnifiedPromptGenerator", + "WebpageScreenshot" + ], + { + "title_aux": "ComfyUI-QHNodes" + } + ], + "https://github.com/liuqianhonga/ComfyUI-String-Helper": [ + [ + "JsonToCSV", + "ShowTranslateString", + "StringConverter", + "StringFormatter", + "StringList", + "StringListFromCSV", + "StringListToCSV", + "StringMatcher", + "StringTranslate", + "TimeFormatter" + ], + { + "title_aux": "ComfyUI-String-Helper" + } + ], + "https://github.com/liushuchun/ComfyUI_Lora_List_With_Url_Loader": [ + [ + "LoraListUrlLoader" + ], + { + "title_aux": "ComfyUI_Lora_List_With_Url_Loader" + } + ], + "https://github.com/liusida/ComfyUI-AutoCropFaces": [ + [ + "AutoCropFaces" + ], + { + "title_aux": "ComfyUI-AutoCropFaces" + } + ], + "https://github.com/liusida/ComfyUI-B-LoRA": [ + [ + "LoadBLoRA" + ], + { + "title_aux": "ComfyUI-B-LoRA" + } + ], + "https://github.com/liusida/ComfyUI-Debug": [ + [ + "DebugInspectorNode", + "DebugModelInspectorNode", + "DebugModelPrintOutNode" + ], + { + "title_aux": "ComfyUI-Debug" + } + ], + "https://github.com/liusida/ComfyUI-Login": [ + [ + "LoadImageIncognito" + ], + { + "title_aux": "ComfyUI-Login" + } + ], + "https://github.com/liusida/ComfyUI-SD3-nodes": [ + [ + "SD3EmptyLatent", + "SD3LoadCLIPs", + "SD3LoadCheckpoint" + ], + { + "title_aux": "ComfyUI-SD3-nodes" + } + ], + "https://github.com/livepeer/ComfyUI-Stream-Pack": [ + [ + "FaceMeshDrawNode", + "FaceMeshMaskNode", + "FaceMeshNode", + "FeatureBankAttentionProcessor", + "SuperResolutionModelLoader", + "SuperResolutionUpscale" + ], + { + "title_aux": "ComfyUI-Stream-Pack" + } + ], + "https://github.com/ljleb/comfy-mecha": [ + [ + "Already Loaded Model Mecha Recipe", + "Any Model Mecha Recipe", + "Blocks Mecha Hyper", + "Bool Mecha Hyper", + "Float Mecha Hyper", + "Int Mecha Hyper", + "Lora Mecha Recipe", + "Mecha Converter", + "Mecha Deserializer", + "Mecha Merge Method Cache Unit", + "Mecha Merger", + "Mecha Recipe List", + "Mecha Regex Weights", + "Mecha Serializer", + "Model Mecha Recipe", + "SD1-LDM Mecha Blocks Parameters", + "SDXL-SGM Mecha Blocks Parameters", + "String Mecha Hyper" + ], + { + "title_aux": "Mecha Merge Node Pack" + } + ], + "https://github.com/lks-ai/ComfyUI-StableAudioSampler": [ + [ + "StableAudioConditioning", + "StableAudioLoadModel", + "StableAudioPrompt", + "StableAudioSampler" + ], + { + "author": "lks-ai", + "description": "A Simple integration of Stable Audio Diffusion with knobs and stuff!", + "nickname": "stableaudio", + "title": "StableAudioSampler", + "title_aux": "ComfyUI Stable Audio Open 1.0 Sampler" + } + ], + "https://github.com/lks-ai/anynode": [ + [ + "AnyNode", + "AnyNodeAnthropic", + "AnyNodeCodeViewer", + "AnyNodeExport", + "AnyNodeGemini", + "AnyNodeLocal" + ], + { + "author": "newsbubbles", + "description": "This single node uses an LLM to generate a functionality based on your request. You can make the node do anything.", + "nickname": "AnyNode", + "title": "AnyNode v0.1.1", + "title_aux": "ComfyUI AnyNode: Any Node you ask for" + } + ], + "https://github.com/lldacing/ComfyUI_BEN_ll": [ + [ + "BlurFusionForegroundEstimationForBen", + "GetMaskByBen", + "LoadRembgByBenModel", + "RembgByBen", + "RembgByBenAdvanced" + ], + { + "title_aux": "ComfyUI_BEN_ll" + } + ], + "https://github.com/lldacing/ComfyUI_BiRefNet_ll": [ + [ + "AutoDownloadBiRefNetModel", + "BlurFusionForegroundEstimation", + "GetMaskByBiRefNet", + "LoadRembgByBiRefNetModel", + "RembgByBiRefNet", + "RembgByBiRefNetAdvanced" + ], + { + "title_aux": "ComfyUI_BiRefNet_ll" + } + ], + "https://github.com/lldacing/ComfyUI_Patches_ll": [ + [ + "ApplyFirstBlockCachePatch", + "ApplyFirstBlockCachePatchAdvanced", + "ApplyTeaCachePatch", + "ApplyTeaCachePatchAdvanced", + "DitForwardOverrider", + "FluxForwardOverrider", + "VideoForwardOverrider" + ], + { + "title_aux": "ComfyUI_Patches_ll" + } + ], + "https://github.com/lldacing/ComfyUI_PuLID_Flux_ll": [ + [ + "ApplyPulidFlux", + "FixPulidFluxPatch", + "PulidFluxEvaClipLoader", + "PulidFluxFaceDetector", + "PulidFluxInsightFaceLoader", + "PulidFluxModelLoader", + "PulidFluxOptions" + ], + { + "title_aux": "ComfyUI_PuLID_Flux_ll" + } + ], + "https://github.com/lldacing/ComfyUI_StableDelight_ll": [ + [ + "ApplyStableDelight", + "LoadStableDelightModel" + ], + { + "title_aux": "ComfyUI_StableDelight_ll" + } + ], + "https://github.com/lldacing/ComfyUI_StableHair_ll": [ + [ + "ApplyHairRemover", + "ApplyHairTransfer", + "LoadStableHairRemoverModel", + "LoadStableHairTransferModel" + ], + { + "title_aux": "ComfyUI_StableHair_ll" + } + ], + "https://github.com/lldacing/comfyui-easyapi-nodes": [ + [ + "Base64ToImage", + "Base64ToMask", + "BboxToBbox", + "BboxToCropData", + "BboxesToBboxes", + "ColorPicker", + "ConvertToJsonStr", + "ConvertTypeToAny", + "CopyAndRenameFiles", + "CropImageByBbox", + "CropTargetSizeImageByBbox", + "EmptyOutputNode", + "FilterSortDependSubGraphs", + "FilterValueForList", + "ForEachClose", + "ForEachOpen", + "GetImageBatchSize", + "GetValueFromJsonObj", + "IfElseForEmptyObject", + "ImageEqual", + "ImageSizeGetter", + "ImageToBase64", + "ImageToBase64Advanced", + "IndexOfList", + "IndexesOfList", + "InnerIntCompare", + "InnerIntMathOperation", + "InnerLoopClose", + "InsightFaceBBOXDetect", + "IntToList", + "IntToNumber", + "IsNoneOrEmpty", + "IsNoneOrEmptyOptional", + "JoinList", + "ListMerge", + "ListUnWrapper", + "ListWrapper", + "LoadImageFromLocalPath", + "LoadImageFromURL", + "LoadImageToBase64", + "LoadJsonStrToList", + "LoadLocalFilePath", + "LoadMaskFromLocalPath", + "LoadMaskFromURL", + "MaskByBboxes", + "MaskImageToBase64", + "MaskToBase64", + "MaskToBase64Image", + "MaskToRle", + "NodeListToList", + "NoneNode", + "ReadTextFromLocalFile", + "RleToMask", + "SDBaseVerNumber", + "SamAutoMaskSEGS", + "SamAutoMaskSEGSAdvanced", + "SaveImagesWithoutOutput", + "SaveSingleImageWithoutOutput", + "SaveTextToFileByImagePath", + "SaveTextToLocalFile", + "SelectBbox", + "SelectBboxes", + "ShowBoolean", + "ShowFloat", + "ShowInt", + "ShowNumber", + "ShowString", + "SliceList", + "SortDependSubGraphs", + "SplitStringToList", + "StringArea", + "StringToList", + "TryFreeMemory" + ], + { + "title_aux": "comfyui-easyapi-nodes" + } + ], + "https://github.com/lo-th/Comfyui_three_js": [ + [ + "ThreeView" + ], + { + "title_aux": "Comfyui_three_js" + } + ], + "https://github.com/logtd/ComfyUI-4DHumans": [ + [ + "LoadDetectron", + "LoadHMR", + "ProcessHumans", + "SelectHuman" + ], + { + "title_aux": "ComfyUI-4DHumans" + } + ], + "https://github.com/logtd/ComfyUI-APGScaling": [ + [ + "APGFunction" + ], + { + "title_aux": "ComfyUI-APGScaling" + } + ], + "https://github.com/logtd/ComfyUI-DiLightNet": [ + [ + "LoadDiLightControlNet", + "PrepareDiLightCond" + ], + { + "title_aux": "ComfyUI-DiLightNet" + } + ], + "https://github.com/logtd/ComfyUI-FLATTEN": [ + [ + "ApplyFlattenAttentionNode", + "CreateFlowNoiseNode", + "FlattenCheckpointLoaderNode", + "KSamplerFlattenNode", + "TrajectoryNode", + "UnsamplerFlattenNode" + ], + { + "title_aux": "ComfyUI-FLATTEN" + } + ], + "https://github.com/logtd/ComfyUI-Fluxtapoz": [ + [ + "AddFluxFlow", + "ApplyFluxRaveAttention", + "ApplyRefFlux", + "ApplyRegionalConds", + "ConfigureModifiedFlux", + "CreateRegionalCond", + "FlowEditForwardSampler", + "FlowEditGuider", + "FlowEditReverseSampler", + "FlowEditSampler", + "FluxAttnOverride", + "FluxDeGuidance", + "FluxForwardODESampler", + "FluxInverseSampler", + "FluxNoiseMixer", + "FluxReverseODESampler", + "InFluxFlipSigmas", + "InFluxModelSamplingPred", + "OutFluxModelSamplingPred", + "PAGAttention", + "PrepareAttnBank", + "RFDoubleBlocksOverride", + "RFSingleBlocksOverride", + "RegionalStyleModelApply", + "SEGAttention" + ], + { + "title_aux": "ComfyUI-Fluxtapoz" + } + ], + "https://github.com/logtd/ComfyUI-InstanceDiffusion": [ + [ + "ApplyScaleUModelNode", + "DownloadInstanceDiffusionModels", + "InstanceDiffusionTrackingPrompt", + "LoadInstanceFusersNode", + "LoadInstancePositionNetModel", + "LoadInstanceScaleUNode" + ], + { + "title_aux": "InstanceDiffusion Nodes" + } + ], + "https://github.com/logtd/ComfyUI-InversedNoise": [ + [ + "CombineNoiseLatentNode", + "MixNoiseNode", + "SamplerInversedEulerNode" + ], + { + "title_aux": "ComfyUI-InversedNoise" + } + ], + "https://github.com/logtd/ComfyUI-MochiEdit": [ + [ + "MochiPrepareSigmas", + "MochiResampler", + "MochiUnsampler", + "MochiWrapperResampler", + "MochiWrapperSamplerCustom", + "MochiWrapperUnsampler" + ], + { + "title_aux": "ComfyUI-MochiEdit" + } + ], + "https://github.com/logtd/ComfyUI-MotionThiefExperiment": [ + [ + "ApplyRefMotionNode", + "MotionRefSettingsCustomNode", + "MotionRefSettingsDefaultNode" + ], + { + "title_aux": "ComfyUI-MotionThiefExperiment" + } + ], + "https://github.com/logtd/ComfyUI-RAVE_ATTN": [ + [ + "ApplyRaveAttentionNode", + "AttentionOverrideSD15Node", + "AttentionOverrideSDXLNode" + ], + { + "title_aux": "ComfyUI-RAVE Attention" + } + ], + "https://github.com/logtd/ComfyUI-ReNoise": [ + [ + "ReNoiseModelSamplingPred", + "ReNoiseSampler" + ], + { + "title_aux": "ComfyUI-ReNoise" + } + ], + "https://github.com/logtd/ComfyUI-RefSampling": [ + [ + "ApplyRefContentNode", + "ApplyRefStyleNode", + "ApplyRefUNetNode" + ], + { + "title_aux": "ComfyUI-RefSampling" + } + ], + "https://github.com/logtd/ComfyUI-RefUNet": [ + [ + "ConfigRefMapAdv", + "ConfigureRefNet", + "CreateRefBank", + "CustomRefMapSD1", + "PrepareRefLatents", + "ReadSampler", + "RefModelSamplingPred", + "VisionClipEncode", + "WriteSampler" + ], + { + "title_aux": "ComfyUI-RefUNet" + } + ], + "https://github.com/logtd/ComfyUI-SEGAttention": [ + [ + "SEGAttention" + ], + { + "title_aux": "ComfyUI-SEGAttention" + } + ], + "https://github.com/logtd/ComfyUI-SSREncoder": [ + [ + "ApplySSR", + "EncodeSSRQuery", + "LoadSSRAligner", + "LoadSSRAttention" + ], + { + "title_aux": "ComfyUI-SSREncoder" + } + ], + "https://github.com/logtd/ComfyUI-SeeCoder": [ + [ + "LoadSeeCoder", + "LoadSeeCoderUncond", + "SeecoderEncode" + ], + { + "title_aux": "ComfyUI-SeeCoder" + } + ], + "https://github.com/logtd/ComfyUI-TrackingNodes": [ + [ + "OpenPoseTrackerNode", + "YOLOTrackerNode" + ], + { + "title_aux": "Tracking Nodes for Videos" + } + ], + "https://github.com/logtd/ComfyUI-ViewCrafter": [ + [ + "ApplyViewCrafter", + "LoadViewCrafter", + "ScaleImages" + ], + { + "title_aux": "ComfyUI-ViewCrafter" + } + ], + "https://github.com/lokinou/comfyui-offload-models": [ + [ + "OffloadModel", + "RecallModel" + ], + { + "title_aux": "ComfyUI-Offload-Models" + } + ], + "https://github.com/lonelyowl13/artist_randomizer": [ + [ + "AddRandomArtists", + "TextInput" + ], + { + "title_aux": "Artist tag randomizer for comfyui" + } + ], + "https://github.com/longgui0318/comfyui-common-util": [ + [ + "Added Layer Info To Array", + "Enhanced Random Light Source", + "Float Relay", + "HLFrequencyDetailRestore", + "Hex to Color", + "Image Add Alpha", + "Image Frequency Analyzer", + "Image Relay", + "Image Remove Alpha", + "Image Resize With Padding", + "Init Layer Info Array", + "Int Relay", + "Layer Image Seleted", + "Layer Images IPAdapter Advanced", + "Layer Info Array Fuse", + "Mask Relay", + "String Relay" + ], + { + "title_aux": "comfyui-common-util" + } + ], + "https://github.com/longgui0318/comfyui-llm-assistant": [ + [ + "Chat With LLM", + "Generate Stable Diffsution Prompt With LLM", + "Translate Text With LLM" + ], + { + "title_aux": "comfyui-llm-assistant" + } + ], + "https://github.com/longgui0318/comfyui-magic-clothing": [ + [ + "Add Magic Clothing Attention", + "Change Pipeline Dtype And Device", + "Change Pixel Value Normalization", + "Diffusers Model Makeup &MC", + "Diffusers Scheduler Loader &MC", + "Load Magic Clothing Adapter", + "Load Magic Clothing Model", + "Load Magic Clothing Pipeline", + "Load Magic Clothing Pipeline With Path", + "RUN Magic Clothing Diffusers Model" + ], + { + "title_aux": "comfyui-magic-clothing" + } + ], + "https://github.com/longgui0318/comfyui-mask-util": [ + [ + "Image Adaptive Crop M&R", + "Image Adaptive Crop With Mask", + "Image Change DType", + "Image Change Device", + "Image Resolution Adaptive With X", + "Image Resolution Limit With 8K", + "Load Image With Name", + "Mask Change DType", + "Mask Change Device", + "Mask Selection Of Masks", + "Model Change Device", + "Model Change Device Repeaters", + "Output Image To Input", + "Split Masks" + ], + { + "title_aux": "comfyui-mask-util" + } + ], + "https://github.com/lord-lethris/ComfyUI-RPG-Characters": [ + [ + "ModelLikenessSwitch", + "PromptConcatenatorNode", + "PromptConditioningConverter", + "PromptSelectorNode", + "RPGArtStyleSelector", + "RPGCharacterSelector" + ], + { + "title_aux": "ComfyUI-RPG-Characters" + } + ], + "https://github.com/lordgasmic/comfyui_save_image_with_options": [ + [ + "SaveImageWithOptions" + ], + { + "title_aux": "comfyui_save_image_with_options" + } + ], + "https://github.com/lordgasmic/comfyui_wildcards": [ + [ + "CLIPTextEncodeWithWildcards" + ], + { + "title_aux": "comfyui_wildcards" + } + ], + "https://github.com/lquesada/ComfyUI-Inpaint-CropAndStitch": [ + [ + "InpaintCrop", + "InpaintCropImproved", + "InpaintExtendOutpaint", + "InpaintResize", + "InpaintStitch", + "InpaintStitchImproved" + ], + { + "title_aux": "ComfyUI-Inpaint-CropAndStitch" + } + ], + "https://github.com/lquesada/ComfyUI-Interactive": [ + [ + "InteractiveFloat", + "InteractiveInteger", + "InteractiveReset", + "InteractiveSave", + "InteractiveSeed", + "InteractiveSelector", + "InteractiveSelectorWithParameters", + "InteractiveString", + "InteractiveStringAppend", + "InteractiveStringMultiline", + "InteractiveSwitch", + "InteractiveSwitchWithParameters" + ], + { + "title_aux": "ComfyUI-Interactive" + } + ], + "https://github.com/lquesada/ComfyUI-Prompt-Combinator": [ + [ + "PromptCombinator", + "PromptCombinatorExportGallery", + "PromptCombinatorMerger", + "PromptCombinatorRandomPrompt" + ], + { + "title_aux": "ComfyUI-Prompt-Combinator" + } + ], + "https://github.com/lrzjason/ComfyUI-Watermark-Detection": [ + [ + "WatermarkDetector", + "WatermarkDetectorLoader" + ], + { + "title_aux": "ComfyUI Watermark Detection Node" + } + ], + "https://github.com/lrzjason/Comfyui-In-Context-Lora-Utils": [ + [ + "AddMaskForICLora", + "AutoPatch", + "ConcatContextWindow", + "CreateContextWindow" + ], + { + "title_aux": "Comfyui-In-Context-Lora-Utils" + } + ], + "https://github.com/lrzjason/Comfyui-Kolors-Utils": [ + [ + "SaveKolors", + "SaveWeightAsKolorsUnet" + ], + { + "title_aux": "Comfyui Kolors Utils" + } + ], + "https://github.com/lrzjason/Comfyui-ThinkRemover": [ + [ + "ThinkRemover" + ], + { + "title_aux": "Comfyui-ThinkRemover" + } + ], + "https://github.com/ltdrdata/ComfyUI-Impact-Pack": [ + [ + "AddMask", + "AnyPipeToBasic", + "BasicPipeToDetailerPipe", + "BasicPipeToDetailerPipeSDXL", + "BboxDetectorCombined_v2", + "BboxDetectorSEGS", + "BitwiseAndMask", + "BitwiseAndMaskForEach", + "BlackPatchRetryHookProvider", + "CLIPSegDetectorProvider", + "CfgScheduleHookProvider", + "CombineRegionalPrompts", + "CoreMLDetailerHookProvider", + "CustomNoiseDetailerHookProvider", + "CustomSamplerDetailerHookProvider", + "DenoiseScheduleHookProvider", + "DenoiseSchedulerDetailerHookProvider", + "DetailerForEach", + "DetailerForEachAutoRetry", + "DetailerForEachDebug", + "DetailerForEachDebugPipe", + "DetailerForEachPipe", + "DetailerForEachPipeForAnimateDiff", + "DetailerHookCombine", + "DetailerPipeToBasicPipe", + "EditBasicPipe", + "EditDetailerPipe", + "EditDetailerPipeSDXL", + "EmptySegs", + "FaceDetailer", + "FaceDetailerPipe", + "FromBasicPipe", + "FromBasicPipe_v2", + "FromDetailerPipe", + "FromDetailerPipeSDXL", + "FromDetailerPipe_v2", + "GITSSchedulerFuncProvider", + "ImageListToImageBatch", + "ImageMaskSwitch", + "ImageReceiver", + "ImageSender", + "ImpactAssembleSEGS", + "ImpactBoolean", + "ImpactCombineConditionings", + "ImpactCompare", + "ImpactConcatConditionings", + "ImpactConditionalBranch", + "ImpactConditionalBranchSelMode", + "ImpactConditionalStopIteration", + "ImpactControlBridge", + "ImpactControlNetApplyAdvancedSEGS", + "ImpactControlNetApplySEGS", + "ImpactControlNetClearSEGS", + "ImpactConvertDataType", + "ImpactCount_Elts_in_SEGS", + "ImpactDecomposeSEGS", + "ImpactDilateMask", + "ImpactDilateMaskInSEGS", + "ImpactDilate_Mask_SEG_ELT", + "ImpactDummyInput", + "ImpactEdit_SEG_ELT", + "ImpactExecutionOrderController", + "ImpactFlattenMask", + "ImpactFloat", + "ImpactFrom_SEG_ELT", + "ImpactFrom_SEG_ELT_bbox", + "ImpactFrom_SEG_ELT_crop_region", + "ImpactGaussianBlurMask", + "ImpactGaussianBlurMaskInSEGS", + "ImpactHFTransformersClassifierProvider", + "ImpactIPAdapterApplySEGS", + "ImpactIfNone", + "ImpactImageBatchToImageList", + "ImpactImageInfo", + "ImpactInt", + "ImpactInversedSwitch", + "ImpactIsNotEmptySEGS", + "ImpactKSamplerAdvancedBasicPipe", + "ImpactKSamplerBasicPipe", + "ImpactLatentInfo", + "ImpactListBridge", + "ImpactLogger", + "ImpactLogicalOperators", + "ImpactMakeAnyList", + "ImpactMakeImageBatch", + "ImpactMakeImageList", + "ImpactMakeMaskBatch", + "ImpactMakeMaskList", + "ImpactMakeTileSEGS", + "ImpactMinMax", + "ImpactNeg", + "ImpactNegativeConditioningPlaceholder", + "ImpactNodeSetMuteState", + "ImpactQueueTrigger", + "ImpactQueueTriggerCountdown", + "ImpactRemoteBoolean", + "ImpactRemoteInt", + "ImpactSAM2VideoDetectorSEGS", + "ImpactSEGSClassify", + "ImpactSEGSConcat", + "ImpactSEGSIntersectionFilter", + "ImpactSEGSLabelAssign", + "ImpactSEGSLabelFilter", + "ImpactSEGSMerge", + "ImpactSEGSNMSFilter", + "ImpactSEGSOrderedFilter", + "ImpactSEGSPicker", + "ImpactSEGSRangeFilter", + "ImpactSEGSToMaskBatch", + "ImpactSEGSToMaskList", + "ImpactScaleBy_BBOX_SEG_ELT", + "ImpactSchedulerAdapter", + "ImpactSegsAndMask", + "ImpactSegsAndMaskForEach", + "ImpactSelectNthItemOfAnyList", + "ImpactSetWidgetValue", + "ImpactSimpleDetectorSEGS", + "ImpactSimpleDetectorSEGSPipe", + "ImpactSimpleDetectorSEGS_for_AD", + "ImpactSleep", + "ImpactStringSelector", + "ImpactSwitch", + "ImpactValueReceiver", + "ImpactValueSender", + "ImpactWildcardEncode", + "ImpactWildcardProcessor", + "IterativeImageUpscale", + "IterativeLatentUpscale", + "KSamplerAdvancedProvider", + "KSamplerProvider", + "LamaRemoverDetailerHookProvider", + "LatentPixelScale", + "LatentReceiver", + "LatentSender", + "LatentSwitch", + "MaskDetailerPipe", + "MaskListToMaskBatch", + "MaskRectArea", + "MaskRectAreaAdvanced", + "MaskToSEGS", + "MaskToSEGS_for_AnimateDiff", + "MasksToMaskList", + "MediaPipeFaceMeshToSEGS", + "NoiseInjectionDetailerHookProvider", + "NoiseInjectionHookProvider", + "ONNXDetectorProvider", + "ONNXDetectorSEGS", + "PixelKSampleHookCombine", + "PixelKSampleUpscalerProvider", + "PixelKSampleUpscalerProviderPipe", + "PixelTiledKSampleUpscalerProvider", + "PixelTiledKSampleUpscalerProviderPipe", + "PreviewBridge", + "PreviewBridgeLatent", + "PreviewDetailerHookProvider", + "ReencodeLatent", + "ReencodeLatentPipe", + "RegionalPrompt", + "RegionalSampler", + "RegionalSamplerAdvanced", + "RemoveImageFromSEGS", + "RemoveNoiseMask", + "SAMDetectorCombined", + "SAMDetectorSegmented", + "SAMLoader", + "SEGSDetailer", + "SEGSDetailerForAnimateDiff", + "SEGSLabelFilterDetailerHookProvider", + "SEGSOrderedFilterDetailerHookProvider", + "SEGSPaste", + "SEGSPreview", + "SEGSPreviewCNet", + "SEGSRangeFilterDetailerHookProvider", + "SEGSSwitch", + "SEGSToImageList", + "SEGSUpscaler", + "SEGSUpscalerPipe", + "SegmDetectorCombined_v2", + "SegmDetectorSEGS", + "Segs Mask", + "Segs Mask ForEach", + "SegsToCombinedMask", + "SetDefaultImageForSEGS", + "StepsScheduleHookProvider", + "StringListToString", + "SubtractMask", + "SubtractMaskForEach", + "TiledKSamplerProvider", + "ToBasicPipe", + "ToBinaryMask", + "ToDetailerPipe", + "ToDetailerPipeSDXL", + "TwoAdvancedSamplersForMask", + "TwoSamplersForMask", + "TwoSamplersForMaskUpscalerProvider", + "TwoSamplersForMaskUpscalerProviderPipe", + "UnsamplerDetailerHookProvider", + "UnsamplerHookProvider", + "VariationNoiseDetailerHookProvider", + "WildcardPromptFromString" + ], + { + "author": "Dr.Lt.Data", + "description": "This extension offers various detector nodes and detailer nodes that allow you to configure a workflow that automatically enhances facial details. And provide iterative upscaler.", + "nickname": "Impact Pack", + "preemptions": [ + "SAMLoader" + ], + "title": "Impact Pack", + "title_aux": "ComfyUI Impact Pack" + } + ], + "https://github.com/ltdrdata/ComfyUI-Impact-Subpack": [ + [ + "UltralyticsDetectorProvider" + ], + { + "author": "Dr.Lt.Data", + "description": "This extension provides UltralyticsDetectorProvider node", + "nickname": "Impact Subpack", + "title": "Impact Subpack", + "title_aux": "ComfyUI Impact Subpack" + } + ], + "https://github.com/ltdrdata/ComfyUI-Inspire-Pack": [ + [ + "AnimeLineArt_Preprocessor_Provider_for_SEGS //Inspire", + "ApplyLBW //Inspire", + "ApplyRegionalIPAdapters //Inspire", + "BindImageListPromptList //Inspire", + "CLIPTextEncodeWithWeight //Inspire", + "CacheBackendData //Inspire", + "CacheBackendDataList //Inspire", + "CacheBackendDataNumberKey //Inspire", + "CacheBackendDataNumberKeyList //Inspire", + "CacheBridge //Inspire", + "Canny_Preprocessor_Provider_for_SEGS //Inspire", + "ChangeImageBatchSize //Inspire", + "ChangeLatentBatchSize //Inspire", + "CheckpointLoaderSimpleShared //Inspire", + "ColorMapToMasks //Inspire", + "ColorMaskToDepthMask //Inspire", + "Color_Preprocessor_Provider_for_SEGS //Inspire", + "CompositeNoise //Inspire", + "ConcatConditioningsWithMultiplier //Inspire", + "ConditioningStretch //Inspire", + "ConditioningUpscale //Inspire", + "DWPreprocessor_Provider_for_SEGS //Inspire", + "DropItems //Inspire", + "FakeScribblePreprocessor_Provider_for_SEGS //Inspire", + "FloatRange //Inspire", + "ForeachListBegin //Inspire", + "ForeachListEnd //Inspire", + "FromIPAdapterPipe //Inspire", + "GlobalSampler //Inspire", + "GlobalSeed //Inspire", + "HEDPreprocessor_Provider_for_SEGS //Inspire", + "HyperTile //Inspire", + "IPAdapterModelHelper //Inspire", + "ImageBatchSplitter //Inspire", + "InpaintPreprocessor_Provider_for_SEGS //Inspire", + "IsCached //Inspire", + "KSampler //Inspire", + "KSamplerAdvanced //Inspire", + "KSamplerAdvancedPipe //Inspire", + "KSamplerAdvancedProgress //Inspire", + "KSamplerPipe //Inspire", + "KSamplerProgress //Inspire", + "LatentBatchSplitter //Inspire", + "LeRes_DepthMap_Preprocessor_Provider_for_SEGS //Inspire", + "LineArt_Preprocessor_Provider_for_SEGS //Inspire", + "ListCounter //Inspire", + "LoadDiffusionModelShared //Inspire", + "LoadImage //Inspire", + "LoadImageListFromDir //Inspire", + "LoadImagesFromDir //Inspire", + "LoadLBW //Inspire", + "LoadPromptsFromDir //Inspire", + "LoadPromptsFromFile //Inspire", + "LoadSinglePromptFromFile //Inspire", + "LoadTextEncoderShared //Inspire", + "LoraBlockInfo //Inspire", + "LoraLoaderBlockWeight //Inspire", + "MakeBasicPipe //Inspire", + "MakeLBW //Inspire", + "Manga2Anime_LineArt_Preprocessor_Provider_for_SEGS //Inspire", + "MediaPipeFaceMeshDetectorProvider //Inspire", + "MediaPipe_FaceMesh_Preprocessor_Provider_for_SEGS //Inspire", + "MeshGraphormerDepthMapPreprocessorProvider_for_SEGS //Inspire", + "MiDaS_DepthMap_Preprocessor_Provider_for_SEGS //Inspire", + "OpenPose_Preprocessor_Provider_for_SEGS //Inspire", + "PromptBuilder //Inspire", + "PromptExtractor //Inspire", + "RGB_HexToHSV //Inspire", + "RandomGeneratorForList //Inspire", + "RandomNoise //Inspire", + "RegionalCFG //Inspire", + "RegionalConditioningColorMask //Inspire", + "RegionalConditioningSimple //Inspire", + "RegionalIPAdapterColorMask //Inspire", + "RegionalIPAdapterEncodedColorMask //Inspire", + "RegionalIPAdapterEncodedMask //Inspire", + "RegionalIPAdapterMask //Inspire", + "RegionalPromptColorMask //Inspire", + "RegionalPromptSimple //Inspire", + "RegionalSeedExplorerColorMask //Inspire", + "RegionalSeedExplorerMask //Inspire", + "RemoveBackendData //Inspire", + "RemoveBackendDataNumberKey //Inspire", + "RemoveControlNet //Inspire", + "RemoveControlNetFromRegionalPrompts //Inspire", + "RetrieveBackendData //Inspire", + "RetrieveBackendDataNumberKey //Inspire", + "SaveLBW //Inspire", + "ScheduledCFGGuider //Inspire", + "ScheduledPerpNegCFGGuider //Inspire", + "SeedExplorer //Inspire", + "SeedLogger //Inspire", + "SelectNthMask //Inspire", + "ShowCachedInfo //Inspire", + "StableCascade_CheckpointLoader //Inspire", + "TilePreprocessor_Provider_for_SEGS //Inspire", + "ToIPAdapterPipe //Inspire", + "UnzipPrompt //Inspire", + "WildcardEncode //Inspire", + "WorklistToItemList //Inspire", + "XY Input: Lora Block Weight //Inspire", + "ZipPrompt //Inspire", + "Zoe_DepthMap_Preprocessor_Provider_for_SEGS //Inspire" + ], + { + "author": "Dr.Lt.Data", + "description": "This extension provides various nodes to support Lora Block Weight, Regional Nodes, Backend Cache, Prompt Utils, List Utils and the Impact Pack.", + "nickname": "Inspire Pack", + "nodename_pattern": "Inspire$", + "title": "Inspire Pack", + "title_aux": "ComfyUI Inspire Pack" + } + ], + "https://github.com/ltdrdata/comfyui-connection-helper": [ + [], + { + "author": "Dr.Lt.Data", + "description": "Helper", + "nickname": "Connection Helper", + "nodename_pattern": "Inspire$", + "title": "ComfyUI Connection Helper", + "title_aux": "ComfyUI Connection Helper" + } + ], + "https://github.com/ltdrdata/was-node-suite-comfyui": [ + [ + "BLIP Analyze Image", + "BLIP Model Loader", + "Blend Latents", + "Boolean To Text", + "Bounded Image Blend", + "Bounded Image Blend with Mask", + "Bounded Image Crop", + "Bounded Image Crop with Mask", + "Bus Node", + "CLIP Input Switch", + "CLIP Vision Input Switch", + "CLIPSEG2", + "CLIPSeg Batch Masking", + "CLIPSeg Masking", + "CLIPSeg Model Loader", + "CLIPTextEncode (BlenderNeko Advanced + NSP)", + "CLIPTextEncode (NSP)", + "Cache Node", + "Checkpoint Loader", + "Checkpoint Loader (Simple)", + "Conditioning Input Switch", + "Constant Number", + "Control Net Model Input Switch", + "Convert Masks to Images", + "Create Grid Image", + "Create Grid Image from Batch", + "Create Morph Image", + "Create Morph Image from Path", + "Create Video from Path", + "Debug Number to Console", + "Dictionary to Console", + "Diffusers Hub Model Down-Loader", + "Diffusers Model Loader", + "Export API", + "HSL to Hex", + "Hex to HSL", + "Image Analyze", + "Image Aspect Ratio", + "Image Batch", + "Image Blank", + "Image Blend", + "Image Blend by Mask", + "Image Blending Mode", + "Image Bloom Filter", + "Image Bounds", + "Image Bounds to Console", + "Image Canny Filter", + "Image Chromatic Aberration", + "Image Color Palette", + "Image Crop Face", + "Image Crop Location", + "Image Crop Square Location", + "Image Displacement Warp", + "Image Dragan Photography Filter", + "Image Edge Detection Filter", + "Image Film Grain", + "Image Filter Adjustments", + "Image Flip", + "Image Generate Gradient", + "Image Gradient Map", + "Image High Pass Filter", + "Image History Loader", + "Image Input Switch", + "Image Levels Adjustment", + "Image Load", + "Image Lucy Sharpen", + "Image Median Filter", + "Image Mix RGB Channels", + "Image Monitor Effects Filter", + "Image Nova Filter", + "Image Padding", + "Image Paste Crop", + "Image Paste Crop by Location", + "Image Paste Face", + "Image Perlin Noise", + "Image Perlin Power Fractal", + "Image Pixelate", + "Image Power Noise", + "Image Rembg (Remove Background)", + "Image Remove Background (Alpha)", + "Image Remove Color", + "Image Resize", + "Image Rotate", + "Image Rotate Hue", + "Image SSAO (Ambient Occlusion)", + "Image SSDO (Direct Occlusion)", + "Image Save", + "Image Seamless Texture", + "Image Select Channel", + "Image Select Color", + "Image Send HTTP", + "Image Shadows and Highlights", + "Image Size to Number", + "Image Stitch", + "Image Style Filter", + "Image Threshold", + "Image Tiled", + "Image Transpose", + "Image Voronoi Noise Filter", + "Image fDOF Filter", + "Image to Latent Mask", + "Image to Noise", + "Image to Seed", + "Images to Linear", + "Images to RGB", + "Inset Image Bounds", + "Integer place counter", + "KSampler (WAS)", + "KSampler Cycle", + "Latent Batch", + "Latent Input Switch", + "Latent Noise Injection", + "Latent Size to Number", + "Latent Upscale by Factor (WAS)", + "Load Cache", + "Load Image Batch", + "Load Lora", + "Load Text File", + "Logic Boolean", + "Logic Boolean Primitive", + "Logic Comparison AND", + "Logic Comparison OR", + "Logic Comparison XOR", + "Logic NOT", + "Lora Input Switch", + "Lora Loader", + "Mask Arbitrary Region", + "Mask Batch", + "Mask Batch to Mask", + "Mask Ceiling Region", + "Mask Crop Dominant Region", + "Mask Crop Minority Region", + "Mask Crop Region", + "Mask Dilate Region", + "Mask Dominant Region", + "Mask Erode Region", + "Mask Fill Holes", + "Mask Floor Region", + "Mask Gaussian Region", + "Mask Invert", + "Mask Minority Region", + "Mask Paste Region", + "Mask Rect Area", + "Mask Rect Area (Advanced)", + "Mask Smooth Region", + "Mask Threshold Region", + "Masks Add", + "Masks Combine Batch", + "Masks Combine Regions", + "Masks Subtract", + "MiDaS Depth Approximation", + "MiDaS Mask Image", + "MiDaS Model Loader", + "Model Input Switch", + "Number Counter", + "Number Input Condition", + "Number Input Switch", + "Number Multiple Of", + "Number Operation", + "Number PI", + "Number to Float", + "Number to Int", + "Number to Seed", + "Number to String", + "Number to Text", + "Prompt Multiple Styles Selector", + "Prompt Styles Selector", + "Random Number", + "SAM Image Mask", + "SAM Model Loader", + "SAM Parameters", + "SAM Parameters Combine", + "Samples Passthrough (Stat System)", + "Save Text File", + "Seed", + "String to Text", + "Tensor Batch to Image", + "Text Add Token by Input", + "Text Add Tokens", + "Text Compare", + "Text Concatenate", + "Text Contains", + "Text Dictionary Convert", + "Text Dictionary Get", + "Text Dictionary Keys", + "Text Dictionary New", + "Text Dictionary To Text", + "Text Dictionary Update", + "Text File History Loader", + "Text Find", + "Text Find and Replace", + "Text Find and Replace Input", + "Text Find and Replace by Dictionary", + "Text Input Switch", + "Text List", + "Text List Concatenate", + "Text List to Text", + "Text Load Line From File", + "Text Multiline", + "Text Multiline (Code Compatible)", + "Text Parse A1111 Embeddings", + "Text Parse Noodle Soup Prompts", + "Text Parse Tokens", + "Text Random Line", + "Text Random Prompt", + "Text Shuffle", + "Text Sort", + "Text String", + "Text String Truncate", + "Text to Conditioning", + "Text to Console", + "Text to Number", + "Text to String", + "True Random.org Number Generator", + "Upscale Model Loader", + "Upscale Model Switch", + "VAE Input Switch", + "Video Dump Frames", + "Write to GIF", + "Write to Video", + "unCLIP Checkpoint Loader" + ], + { + "title_aux": "WAS Node Suite (Revised)" + } + ], + "https://github.com/lthero-big/ComfyUI-GaussianShadingWatermark": [ + [ + "DPR_Extractor", + "DPR_GS_Latent", + "DPR_KSamplerAdvanced" + ], + { + "title_aux": "ComfyUI-GaussianShadingWatermark" + } + ], + "https://github.com/luandev/ComfyUI-CrewAI": [ + [ + "DisplayText", + "\ud83d\udcceCrewAI Agent", + "\ud83d\udcceCrewAI Agent List", + "\ud83d\udcceCrewAI Crew", + "\ud83d\udcceCrewAI LLM Chat GPT", + "\ud83d\udcceCrewAI LLM Hugging Face", + "\ud83d\udcceCrewAI LLM Ollama", + "\ud83d\udcceCrewAI LLM OpenAI", + "\ud83d\udcceCrewAI Task", + "\ud83d\udcceCrewAI Task List", + "\ud83d\udcceCrewAI Text" + ], + { + "title_aux": "ComfyUI CrewAI" + } + ], + "https://github.com/lucak5s/comfyui_gfpgan": [ + [ + "GFPGANRestorer" + ], + { + "title_aux": "ComfyUI GFPGAN" + } + ], + "https://github.com/lujiazho/ComfyUI-CatvtonFluxWrapper": [ + [ + "CatvtonFluxSampler", + "LoadCatvtonFlux", + "LoadCatvtonFluxLoRA", + "ModelPrinter" + ], + { + "title_aux": "ComfyUI-CatvtonFluxWrapper" + } + ], + "https://github.com/lum3on/ComfyUI-FrameUtilitys": [ + [ + "FrameClipper", + "FrameExtender", + "FrameExtenderAdvanced", + "FrameRepeater", + "FrameReplacer" + ], + { + "title_aux": "ComfyUI-FrameUtilitys" + } + ], + "https://github.com/lum3on/ComfyUI-ModelQuantizer": [ + [ + "ControlNetFP8QuantizeNode", + "ControlNetMetadataViewerNode", + "ModelToStateDict", + "QuantizeFP8Format", + "QuantizeModel", + "SaveAsSafeTensor" + ], + { + "title_aux": "ComfyUI-ModelQuantizer" + } + ], + "https://github.com/lum3on/ComfyUI-StableAudioX": [ + [ + "AudioXAdvancedVolumeControl", + "AudioXAudioProcessor", + "AudioXEnhancedTextToAudio", + "AudioXEnhancedTextToMusic", + "AudioXEnhancedVideoToAudio", + "AudioXModelLoader", + "AudioXMultiModalGeneration", + "AudioXPromptHelper", + "AudioXTextToAudio", + "AudioXTextToMusic", + "AudioXVideoAudioCombiner", + "AudioXVideoMuter", + "AudioXVideoToAudio", + "AudioXVideoToMusic", + "AudioXVolumeControl" + ], + { + "title_aux": "ComfyUI-AudioX" + } + ], + "https://github.com/lum3on/ComfyUI_MJ-Scraper": [ + [ + "MJScraper" + ], + { + "title_aux": "ComfyUI Midjourney Scraper Node" + } + ], + "https://github.com/lum3on/comfyui_EdgeTAM": [ + [ + "EdgeTAMVideoTracker", + "InteractiveMaskEditor" + ], + { + "title_aux": "comfyui_EdgeTAM" + } + ], + "https://github.com/lum3on/comfyui_LLM_Polymath": [ + [ + "ConceptEraserNode", + "flux_context_preset", + "polymath_SaveAbsolute", + "polymath_StringListPicker", + "polymath_TextSplitter", + "polymath_chat", + "polymath_helper", + "polymath_scraper", + "polymath_settings", + "polymath_text_mask" + ], + { + "title_aux": "comfyui_LLM_Polymath" + } + ], + "https://github.com/lumalabs/ComfyUI-LumaAI-API": [ + [ + "CharacterReference", + "ConcatReferences", + "ImgBBUpload", + "LumaAIClient", + "LumaAddAudio2Video", + "LumaExtendGeneration", + "LumaImage2Video", + "LumaImageGeneration", + "LumaInterpolateGenerations", + "LumaModifyImage", + "LumaPreviewVideo", + "LumaText2Video", + "LumaUpscaleGeneration", + "Reference" + ], + { + "title_aux": "ComfyUI-LumaAI-API" + } + ], + "https://github.com/lxe/ComfyUI-OpenAI-Compat-LLM-Node": [ + [ + "OpenAILLMNode" + ], + { + "title_aux": "ComfyUI OpenAI Compatible LLM Node" + } + ], + "https://github.com/m-sokes/ComfyUI-Sokes-Nodes": [ + [ + "ComfyUI Folder Paths | sokes \ud83e\uddac", + "Current Date & Time | sokes \ud83e\uddac", + "Generate Random Background | sokes \ud83e\uddac", + "Hex Color Swatch | sokes \ud83e\uddac", + "Hex to Color Name | sokes \ud83e\uddac", + "Image Picker | sokes \ud83e\uddac", + "Latent Switch x9 | sokes \ud83e\uddac", + "Load Random Image | sokes \ud83e\uddac", + "Random Hex Color | sokes \ud83e\uddac", + "Random Number | sokes \ud83e\uddac", + "Replace Text with RegEx | sokes \ud83e\uddac", + "Street View Loader | sokes \ud83e\uddac" + ], + { + "title_aux": "ComfyUI Sokes Nodes \ud83e\uddac" + } + ], + "https://github.com/maepopi/Diffusers-in-ComfyUI": [ + [ + "BLoRALoader", + "GenerateImg2Image", + "GenerateInpaintImage", + "GenerateTxt2Image", + "Img2ImgStableDiffusionPipeline", + "InpaintingStableDiffusionPipeline", + "LoRALoader", + "MakeCanny", + "Text2ImgStableDiffusionPipeline" + ], + { + "title_aux": "Diffusers-in-ComfyUI" + } + ], + "https://github.com/magekinnarus/ComfyUI-V-Prediction-Node": [ + [ + "AddParam" + ], + { + "title_aux": "ComfyUI-V-Prediction-Node" + } + ], + "https://github.com/magic-eraser-org/ComfyUI-Unwatermark": [ + [ + "Remove Watermark" + ], + { + "title_aux": "ComfyUI-Unwatermark" + } + ], + "https://github.com/mang01010/MangoNodePack": [ + [ + "CompositeMangoLoader", + "FluxGuidanceMango", + "FluxSamplerMango", + "ImageSaverMango", + "KSamplerMango", + "LatentImageMango", + "LoraStackMango", + "MangoImageLoader", + "MangoLoader", + "MangoLoader10Loras", + "MangoModelData", + "MangoPromptLoad", + "MangoTriggerExporter", + "PromptEmbedMango", + "PromptMango", + "PromptSave" + ], + { + "title_aux": "Mango Node Pack" + } + ], + "https://github.com/mango-rgb/ComfyUI-Mango-Random-node": [ + [ + "RandomFilePathNode", + "RandomImageNode", + "RandomImagePathNode", + "RandomTextNode", + "RandomVideoPathNode" + ], + { + "title_aux": "ComfyUI-Mango-Random" + } + ], + "https://github.com/manifestations/comfyui-globetrotter": [ + [ + "LoRATrainerNode", + "OllamaLLMNode", + "OllamaVisionNode", + "TextCombinerNode" + ], + { + "title_aux": "ComfyUI Globetrotter Nodes" + } + ], + "https://github.com/manifestations/comfyui-outfit": [ + [ + "OllamaLLMNode", + "SimpleOllamaNode" + ], + { + "title_aux": "ComfyUI Outfit Nodes" + } + ], + "https://github.com/mape/ComfyUI-mape-Helpers": [ + [ + "mape Variable" + ], + { + "author": "mape", + "description": "Various QoL improvements like prompt tweaking, variable assignment, image preview, fuzzy search, error reporting, organizing and node navigation.", + "nickname": "\ud83d\udfe1 mape's helpers", + "title": "mape's helpers", + "title_aux": "mape's helpers" + } + ], + "https://github.com/maracman/ComfyUI-SubjectStyle-CSV": [ + [ + "CSVPromptProcessor" + ], + { + "title_aux": "ComfyUI-SubjectStyle-CSV" + } + ], + "https://github.com/marawan206/ComfyUI-FaceCropper": [ + [ + "NodoFaceCropping" + ], + { + "title_aux": "Face Cropper Node (2:3 Ratio)" + } + ], + "https://github.com/marcoc2/ComfyUI-AnotherUtils": [ + [ + "CustomCrop", + "LoadImagesOriginal", + "NearestUpscale", + "PixelArtNormalizer", + "SmartResize" + ], + { + "title_aux": "Image Processing Suite for ComfyUI" + } + ], + "https://github.com/marcoc2/ComfyUI_CogView4-6B_diffusers": [ + [ + "CogView4Generator" + ], + { + "title_aux": "ComfyUI-Cog" + } + ], + "https://github.com/marduk191/ComfyUI-Fluxpromptenhancer": [ + [ + "FluxPromptEnhance" + ], + { + "title_aux": "Flux Prompt Enhance Node for ComfyUI" + } + ], + "https://github.com/marduk191/comfyui-marnodes": [ + [ + "ImageToDevice", + "marduk191_5_text_string", + "marduk191_5way_text_switch", + "marduk191_s_random_latent", + "marduk191_workflow_settings" + ], + { + "author": "\u02f6marduk191", + "description": "marduk191s nodes.", + "nickname": "marduk191 workflow settings", + "title": "marduk191 workflow settings", + "title_aux": "marduk191 workflow settings" + } + ], + "https://github.com/marhensa/sdxl-recommended-res-calc": [ + [ + "RecommendedResCalc" + ], + { + "title_aux": "Recommended Resolution Calculator" + } + ], + "https://github.com/marklieberman/ComfyUI-Liebs-Picker": [ + [ + "LiebsPicker" + ], + { + "title_aux": "ComfyUI-Liebs-Picker" + } + ], + "https://github.com/marklieberman/ComfyUI-Liebs-Title": [ + [ + "LiebsTitleVar" + ], + { + "title_aux": "ComfyUI-Liebs-Title" + } + ], + "https://github.com/marklieberman/ComfyUI-Liebs-Toast": [ + [ + "LiebsToast" + ], + { + "title_aux": "ComfyUI-Liebs-Toast" + } + ], + "https://github.com/markuryy/ComfyUI-Flux-Prompt-Saver": [ + [ + "FluxPromptSaver", + "FluxTextSampler", + "ModelName" + ], + { + "title_aux": "ComfyUI Flux Prompt Saver" + } + ], + "https://github.com/markuryy/ComfyUI-Simple-Video-XY-Plot": [ + [ + "VideoXYPlotSampler" + ], + { + "title_aux": "Video XY Plot" + } + ], + "https://github.com/markuryy/ComfyUI-SuperLoader": [ + [ + "Display String", + "Display String Multiline", + "LoRA Metadata" + ], + { + "title_aux": "Super Loader" + } + ], + "https://github.com/martijnat/comfyui-previewlatent": [ + [ + "PreviewLatent", + "PreviewLatentAdvanced", + "PreviewLatentXL" + ], + { + "title_aux": "comfyui-previewlatent" + } + ], + "https://github.com/massao000/ComfyUI_aspect_ratios": [ + [ + "Aspect Ratios Node" + ], + { + "title_aux": "ComfyUI_aspect_ratios" + } + ], + "https://github.com/matan1905/ComfyUI-Serving-Toolkit": [ + [ + "AlwaysExecute", + "CommandPickerServing", + "DiscordServing", + "ServingInputImage", + "ServingInputImageAsLatent", + "ServingInputNumber", + "ServingInputText", + "ServingInputTextImage", + "ServingMultiImageOutput", + "ServingOutput", + "ServingTextOutput", + "TelegramServing", + "WebSocketServing" + ], + { + "title_aux": "ComfyUI Serving toolkit" + } + ], + "https://github.com/matorzhin/milan-nodes-comfyui": [ + [ + "LoadMultipleImagesExtended", + "LoadOneImageExtended" + ], + { + "title_aux": "milan-nodes-comfyui" + } + ], + "https://github.com/mattjohnpowell/comfyui-lmstudio-image-to-text-node": [ + [ + "Expo Lmstudio Image To Text", + "Expo Lmstudio Text Generation", + "Expo Lmstudio Unified", + "ExpoLmstudioImageToText", + "ExpoLmstudioTextGeneration", + "ExpoLmstudioUnified" + ], + { + "author": "Matt John Powell", + "description": "This extension provides three custom nodes for ComfyUI that integrate LM Studio's capabilities:", + "nickname": "LM Studio Nodes", + "title": "LM Studio Nodes for ComfyUI", + "title_aux": "LM Studio Image to Text Node for ComfyUI" + } + ], + "https://github.com/mav-rik/facerestore_cf": [ + [ + "CropFace", + "FaceRestoreCFWithModel", + "FaceRestoreModelLoader" + ], + { + "title_aux": "Facerestore CF (Code Former)" + } + ], + "https://github.com/mbrostami/ComfyUI-HF": [ + [ + "GPT2Node" + ], + { + "title_aux": "ComfyUI-HF" + } + ], + "https://github.com/mbrostami/ComfyUI-TITrain": [ + [ + "TextualInversionTraining", + "TextualInversionTrainingSDXL" + ], + { + "title_aux": "ComfyUI-TITrain" + } + ], + "https://github.com/mcDandy/more_math": [ + [ + "mrmth_ConditioningMathNode", + "mrmth_FloatMathNode", + "mrmth_FloatToInt", + "mrmth_ImageMathNode", + "mrmth_IntToFloat", + "mrmth_LatentMathNode" + ], + { + "title_aux": "More Math" + } + ], + "https://github.com/mcmonkeyprojects/sd-dynamic-thresholding": [ + [ + "DynamicThresholdingFull", + "DynamicThresholdingSimple" + ], + { + "title_aux": "Dynamic Thresholding" + } + ], + "https://github.com/meanin2/comfyui-MGnodes": [ + [ + "ImageWatermarkNode", + "TextExtractorNode" + ], + { + "title_aux": "comfyui-MGnodes" + } + ], + "https://github.com/meap158/ComfyUI-Background-Replacement": [ + [ + "BackgroundReplacement", + "ImageComposite" + ], + { + "title_aux": "ComfyUI-Background-Replacement" + } + ], + "https://github.com/meap158/ComfyUI-GPU-temperature-protection": [ + [ + "GPUTemperatureProtection" + ], + { + "title_aux": "GPU temperature protection" + } + ], + "https://github.com/meap158/ComfyUI-Prompt-Expansion": [ + [ + "PromptExpansion" + ], + { + "title_aux": "ComfyUI-Prompt-Expansion" + } + ], + "https://github.com/mech-tools/comfyui-checkpoint-automatic-config": [ + [ + "CheckpointAutomaticConfig", + "ConfigPipe" + ], + { + "title_aux": "ComfyUI Checkpoint Automatic Config" + } + ], + "https://github.com/mediocreatmybest/ComfyUI-Transformers-Pipeline": [ + [ + "BatchProcessorTpl", + "CaptionExportTpl", + "CaptionGeneratorTpl", + "DebugModelNodeTpl", + "DebugNodeTpl", + "ExifMetadataExtractorTpl", + "Florence2NodeTpl", + "ImageLoaderTpl", + "ModelLoaderTpl", + "PresetModelListTpl", + "TaskListTpl" + ], + { + "title_aux": "ComfyUI-Transformers-Pipeline" + } + ], + "https://github.com/melMass/comfy_mtb": [ + [ + "Animation Builder (mtb)", + "Any To String (mtb)", + "Batch Float (mtb)", + "Batch Float Assemble (mtb)", + "Batch Float Fill (mtb)", + "Batch Make (mtb)", + "Batch Merge (mtb)", + "Batch Shake (mtb)", + "Batch Shape (mtb)", + "Batch Transform (mtb)", + "Bbox (mtb)", + "Bbox From Mask (mtb)", + "Blur (mtb)", + "Color Correct (mtb)", + "Colored Image (mtb)", + "Concat Images (mtb)", + "Crop (mtb)", + "Debug (mtb)", + "Deep Bump (mtb)", + "Export With Ffmpeg (mtb)", + "Face Swap (mtb)", + "Film Interpolation (mtb)", + "Fit Number (mtb)", + "Float To Number (mtb)", + "Get Batch From History (mtb)", + "Image Compare (mtb)", + "Image Premultiply (mtb)", + "Image Remove Background Rembg (mtb)", + "Image Resize Factor (mtb)", + "Image Tile Offset (mtb)", + "Int To Bool (mtb)", + "Int To Number (mtb)", + "Interpolate Clip Sequential (mtb)", + "Latent Lerp (mtb)", + "Load Face Analysis Model (mtb)", + "Load Face Enhance Model (mtb)", + "Load Face Swap Model (mtb)", + "Load Film Model (mtb)", + "Load Image From Url (mtb)", + "Load Image Sequence (mtb)", + "Mask To Image (mtb)", + "Math Expression (mtb)", + "Model Patch Seamless (mtb)", + "Pick From Batch (mtb)", + "Qr Code (mtb)", + "Restore Face (mtb)", + "Save Gif (mtb)", + "Save Image Grid (mtb)", + "Save Image Sequence (mtb)", + "Save Tensors (mtb)", + "Sharpen (mtb)", + "Smart Step (mtb)", + "Stack Images (mtb)", + "String Replace (mtb)", + "Styles Loader (mtb)", + "Text To Image (mtb)", + "Transform Image (mtb)", + "Uncrop (mtb)", + "Unsplash Image (mtb)", + "Vae Decode (mtb)" + ], + { + "nodename_pattern": "\\(mtb\\)$", + "title_aux": "MTB Nodes" + } + ], + "https://github.com/melMass/comfy_oiio": [ + [ + "OIIO_ColorspaceConvert", + "OIIO_ColorspaceMatchFinder", + "OIIO_LoadImage", + "OIIO_SaveImage" + ], + { + "title_aux": "comfy-oiio" + } + ], + "https://github.com/mephisto83/petty-paint-comfyui-node": [ + [ + "ConvertWhiteToAlpha", + "PPGenerateRandomFloat", + "PPGenerateRandomNumber", + "PPKSamplerAdvanced", + "PPSelectRandomValue", + "PettyImageImageColorToMask", + "PettyPaintAppend", + "PettyPaintApplyLoRAStack", + "PettyPaintArguments", + "PettyPaintBlurs", + "PettyPaintCheckpointLoaderSimple", + "PettyPaintComponent", + "PettyPaintConditioningSetMaskAndCombine", + "PettyPaintControlNetToMasking", + "PettyPaintConvert", + "PettyPaintCountFiles", + "PettyPaintEnsureDirectory", + "PettyPaintExec", + "PettyPaintFakeConvert", + "PettyPaintFileExists", + "PettyPaintImageColorsToMasks", + "PettyPaintImageCompositeMasked", + "PettyPaintImageDims", + "PettyPaintImageMaskCropper", + "PettyPaintImagePlacement", + "PettyPaintImageSave", + "PettyPaintImageStore", + "PettyPaintImageToMask", + "PettyPaintImagesToMasks", + "PettyPaintJsonMap", + "PettyPaintJsonRead", + "PettyPaintJsonReadArray", + "PettyPaintKSampler", + "PettyPaintKSamplerAdvanced", + "PettyPaintLoRAStack", + "PettyPaintLoadImage", + "PettyPaintLoadImageMasks", + "PettyPaintLoadImages", + "PettyPaintMap", + "PettyPaintMasksToImages", + "PettyPaintNot", + "PettyPaintPassThroughNode", + "PettyPaintProcessor", + "PettyPaintRemoveAddText", + "PettyPaintSDTurboScheduler", + "PettyPaintStoryImage", + "PettyPaintText", + "PettyPaintTexts_to_Conditioning", + "PettyPaintToJson", + "PettyPaintVAEDecode", + "SkippableVAEEncode" + ], + { + "title_aux": "petty-paint-comfyui-node" + } + ], + "https://github.com/meshmesh-io/ComfyUI-MeshMesh": [ + [ + "ColorPicker", + "MasksToColoredMasks" + ], + { + "title_aux": "ComfyUI-MeshMesh" + } + ], + "https://github.com/meshmesh-io/mm-comfyui-loopback": [ + [ + "Loop", + "LoopEnd", + "LoopEnd_SEGIMAGE", + "LoopStart", + "LoopStart_SEGIMAGE" + ], + { + "title_aux": "mm-comfyui-loopback" + } + ], + "https://github.com/meshmesh-io/mm-comfyui-megamask": [ + [ + "ColorListMaskToImage", + "FlattenAndCombineMaskImages" + ], + { + "title_aux": "mm-comfyui-megamask" + } + ], + "https://github.com/metal3d/ComfyUI_Human_Parts": [ + [ + "HumanParts" + ], + { + "title_aux": "Human Parts Detector" + } + ], + "https://github.com/metal3d/ComfyUI_M3D_photo_effects": [ + [ + "Bleach Bypass", + "RGB Curve" + ], + { + "title_aux": "M3D photo effects" + } + ], + "https://github.com/metncelik/comfyui_met_suite": [ + [ + "BBOXPadding", + "BBOXResize", + "ImageResizeKeepRatio", + "PrimitiveBBOX", + "RaiseError" + ], + { + "title_aux": "comfyui_met_suite" + } + ], + "https://github.com/mfg637/ComfyUI-ScheduledGuider-Ext": [ + [ + "ArctanScheduler", + "ConcatSigmas", + "CosineScheduler", + "CustomBaseLogarithm", + "CustomExponent", + "GaussianScheduler", + "InvertSigmas", + "LogNormal Scheduler", + "OffsetSigmas", + "Parametric Peak #1", + "PerpNegScheduledCFGGuider", + "PredefinedExponent", + "PredefinedLogarithm", + "ScaleToRange", + "ScheduledCFGGuider", + "SigmasToPower", + "SplitSigmasByValue", + "k/x scheduler" + ], + { + "title_aux": "ComfyUI-ScheduledGuider-Ext" + } + ], + "https://github.com/mgfxer/ComfyUI-FrameFX": [ + [ + "DynamicAnimatedWeightsHelper", + "EdgeFXSourceImages", + "FlorencePromptTravelHelper", + "LivePromptInterpolation", + "MaskSequenceHelper", + "PromptStackManager", + "PromptTravelHelper" + ], + { + "author": "mgfxer", + "description": "This extension provides various frame and mask sequence manipulation tools for animation workflows.", + "nickname": "FrameFX \ud83d\udcab", + "title": "FrameFX", + "title_aux": "ComfyUI-FrameFX" + } + ], + "https://github.com/miaoshouai/ComfyUI-Miaoshouai-Tagger": [ + [ + "Miaoshouai_Caption_Analyzer", + "Miaoshouai_Flux_CLIPTextEncode", + "Miaoshouai_SaveTags", + "Miaoshouai_Tagger" + ], + { + "title_aux": "ComfyUI-Miaoshouai-Tagger" + } + ], + "https://github.com/michaelgold/ComfyUI-HF-Model-Downloader": [ + [ + "DownloadModel", + "ModelDownloader" + ], + { + "title_aux": "ComfyUI-HF-Model-Downloader" + } + ], + "https://github.com/microbote/ComfyUI-StyledCLIPTextEncode": [ + [ + "StyledCLIPTextEncode" + ], + { + "title_aux": "StyledCLIPTextEncode" + } + ], + "https://github.com/mihaiiancu/ComfyUI_Inpaint": [ + [ + "InpaintMediapipe" + ], + { + "title_aux": "mihaiiancu/Inpaint" + } + ], + "https://github.com/mikebilly/Transparent-background-comfyUI": [ + [ + "Transparentbackground RemBg" + ], + { + "title_aux": "Transparent-background-comfyUI" + } + ], + "https://github.com/mikeshuangyan/ComfyUI_MqUtils": [ + [ + "MqCheckFP4Support", + "MqIntSwitch", + "MqIntToString", + "MqTextSplitter" + ], + { + "title_aux": "ComfyUI_MqUtils" + } + ], + "https://github.com/mikkel/ComfyUI-text-overlay": [ + [ + "Image Text Overlay" + ], + { + "title_aux": "ComfyUI - Text Overlay Plugin" + } + ], + "https://github.com/mikkel/comfyui-mask-boundingbox": [ + [ + "Mask Bounding Box" + ], + { + "title_aux": "ComfyUI - Mask Bounding Box" + } + ], + "https://github.com/mingsky-ai/ComfyUI-MingNodes": [ + [ + "AddWaterMarkNode", + "AdjustBrightnessContrastSaturationNode", + "BaiduTranslateNode", + "ColorBalanceNode", + "ConvertGrayChannelNode", + "HSLColorNode", + "HighlightShadowBrightnessNode", + "ImitationHueNode", + "LightShapeNode", + "RemoveWatermarkNode" + ], + { + "title_aux": "ComfyUI-MingNodes" + } + ], + "https://github.com/mira-6/comfyui-sasolver": [ + [ + "SamplerSASolver", + "SamplerSASolverExperimental" + ], + { + "title_aux": "comfyui-sasolver" + } + ], + "https://github.com/mirabarukaso/ComfyUI_Mira": [ + [ + "BooleanListInterpreter1", + "BooleanListInterpreter4", + "BooleanListInterpreter8", + "CanvasCreatorAdvanced", + "CanvasCreatorBasic", + "CanvasCreatorSimple", + "CheckpointLoaderSimpleMira", + "CreateMaskWithCanvas", + "CreateNestedPNGMask", + "CreateSimpleMask", + "CreateTillingPNGMask", + "CreateWatermarkRemovalMask", + "EightBooleanTrigger", + "EightFloats", + "EvenOrOdd", + "EvenOrOddList", + "FloatListInterpreter1", + "FloatListInterpreter4", + "FloatListInterpreter8", + "FloatMultiplication", + "FourBooleanTrigger", + "FourFloats", + "FunctionSelectAuto", + "FunctionSwap", + "ImageBrightness", + "ImageColorTransferMira", + "ImageContrast", + "ImageGamma", + "ImageGrayscale", + "ImageHUE", + "ImageRGBChannel", + "ImageSaturation", + "ImageSaverMira", + "ImageSharpness", + "ImageToneCurve", + "IntMultiplication", + "IntSubtraction", + "IntToFloatMultiplication", + "LoRALoaderWithNameStacker", + "LoRAfromText", + "LogicNot", + "NoneToZero", + "NumeralToString", + "OneFloat", + "PngColorMasksToMaskList", + "PngColorMasksToRGB", + "PngColorMasksToString", + "PngColorMasksToStringList", + "PngRectanglesToMask", + "PngRectanglesToMaskList", + "RandomNestedLayouts", + "RandomTillingLayouts", + "SN74HC1G86", + "SN74HC86", + "SN74LVC1G125", + "SeedGeneratorMira", + "SingleBooleanTrigger", + "SixBooleanTrigger", + "StepsAndCfg", + "TextBoxMira", + "TextCombinerSix", + "TextCombinerTwo", + "TextLoopCombiner", + "TextSwitcherThreeWays", + "TextSwitcherTwoWays", + "TextWildcardSeprator", + "TextWithBooleanSwitchAndCommonTextInput", + "TwoBooleanTrigger", + "TwoFloats", + "UpscaleImageByModelThenResize", + "illustrious_character_select", + "illustrious_character_select_en", + "llm_prompt_gen_node", + "local_llm_prompt_gen" + ], + { + "title_aux": "ComfyUI_Mira" + } + ], + "https://github.com/misterjoessef/MLTask_ComfyUI": [ + [ + "FacebookPosterData", + "InstagramPosterData", + "LinkedinPosterData", + "MLTaskUtilsTextImageGenerator", + "PinterestPosterData", + "SocialManMediaToPoster", + "SocialManPostData", + "SocialManPoster", + "TiktokPosterData", + "TwitterPosterData", + "YoutubePosterData" + ], + { + "title_aux": "MLTask_ComfyUI" + } + ], + "https://github.com/mittimi/ComfyUI_mittimiLoadPreset2": [ + [ + "CombineParamDataMittimi", + "LoadImageParamMittimi", + "LoadSetParamMittimi", + "SaveImageParamMittimi", + "SaveParamToPresetMittimi" + ], + { + "author": "mittimi", + "description": "This node can easily switch between models and prompts by saving presets.", + "nickname": "mittimiLoadPreset2", + "title": "mittimiLoadPreset2", + "title_aux": "ComfyUI_mittimiLoadPreset2" + } + ], + "https://github.com/mittimi/ComfyUI_mittimiRecalculateSize": [ + [ + "RecalculateSizeMittimi01" + ], + { + "author": "mittimi", + "description": "Switch between vertical and horizontal values with a single button.", + "nickname": "mittimiWidthHeight", + "title": "mittimiWidthHeight", + "title_aux": "ComfyUI_mittimiRecalculateSize" + } + ], + "https://github.com/mittimi/ComfyUI_mittimiWidthHeight": [ + [ + "WidthHeightMittimi01" + ], + { + "author": "mittimi", + "description": "Switch between vertical and horizontal values with a single button.", + "nickname": "mittimiWidthHeight", + "title": "mittimiWidthHeight", + "title_aux": "ComfyUI_mittimiDaisyChainText" + } + ], + "https://github.com/mo230761/InsertAnything-ComfyUI-official": [ + [ + "CropBack", + "CropBackNoScaling", + "FillProcess", + "FillProcessNoScaling", + "MaskOption", + "ReduxProcess" + ], + { + "title_aux": "InsertAnything-ComfyUI-official" + } + ], + "https://github.com/mobilehacker/ComfyUI_format-lora-stack": [ + [ + "FormatLoraStack" + ], + { + "title_aux": "ComfyUI_format-lora-stack" + } + ], + "https://github.com/modelscope/comfyscope": [ + [ + "DashScopeFLUXAPI" + ], + { + "title_aux": "Dashscope FLUX API for ComfyUI" + } + ], + "https://github.com/modusCell/ComfyUI-dimension-node-modusCell": [ + [ + "DimensionProviderFree modusCell", + "DimensionProviderRatio modusCell", + "String Concat modusCell" + ], + { + "title_aux": "Preset Dimensions" + } + ], + "https://github.com/mohseni-mr/ComfyUI-Mohseni-Kit": [ + [ + "FloatPreview" + ], + { + "title_aux": "ComfyUI Mohseni Kit" + } + ], + "https://github.com/mohsensd1373/comfyui_wordpress": [ + [ + "SaveToWordPressNode" + ], + { + "title_aux": "comfyui_wordpress" + } + ], + "https://github.com/monkeyWie/ComfyUI-FormInput": [ + [ + "BooleanInput_FormInput", + "DisplayText_FormInput", + "TextInput_FormInput" + ], + { + "title_aux": "ComfyUI-FormInput" + } + ], + "https://github.com/moon7star9/ComfyUI_BiRefNet_Universal": [ + [ + "BiRefNet_Loader", + "BiRefNet_Remove_Background" + ], + { + "title_aux": "ComfyUI_BiRefNet_Universal" + } + ], + "https://github.com/morino-kumasan/comfyui-toml-prompt": [ + [ + "CheckPointLoaderSimpleFromString", + "IntSelector", + "JsonExtractFloat", + "JsonExtractInt", + "JsonExtractString", + "KSamplerFromJsonInfo", + "LatentSelector", + "MultipartCLIPTextEncode", + "MultipleLoraTagLoader", + "PromptLoader", + "SeedGenerator", + "StringConcat", + "StringConcatInt", + "StringPicker", + "StringSelector", + "StringViewer", + "SummaryReader", + "TomlPromptDecode" + ], + { + "title_aux": "comfyui-toml-prompt" + } + ], + "https://github.com/moustafa-nasr/ComfyUI-SimpleLogger": [ + [ + "Log Image", + "LogImageNode" + ], + { + "title_aux": "ComfyUI-SimpleLogger" + } + ], + "https://github.com/moyi7712/ComfyUI_Seamless_Patten": [ + [ + "SeamlessApply", + "SeamlessKSampler", + "SeamlessKSamplerAdvanced", + "SeamlessVae" + ], + { + "title_aux": "ComfyUI_Seamless_Patten" + } + ], + "https://github.com/mr7thing/circle_pattern_processor": [ + [ + "CirclePatternProcessor", + "CirclePatternSVGExporter", + "ImageBinarizer" + ], + { + "title_aux": "Circle Pattern Processor for ComfyUI" + } + ], + "https://github.com/mrchipset/ComfyUI-SaveImageS3": [ + [ + "SaveImageS3" + ], + { + "author": "Mr.Chip", + "description": "This extension offers a custom node to save image to S3-compatible oss.", + "nickname": "SaveImageS3", + "title": "SaveImageS3", + "title_aux": "ComfyUI-SaveImageS3" + } + ], + "https://github.com/mrhan1993/ComfyUI-Fooocus": [ + [ + "AlignYourStepsScheduler", + "BasicScheduler", + "CLIPLoader", + "CLIPMergeSimple", + "CLIPSave", + "CLIPSetLastLayer", + "CLIPTextEncode", + "CLIPTextEncodeSDXL", + "CLIPTextEncodeSDXLRefiner", + "CLIPVisionEncode", + "CLIPVisionLoader", + "Canny", + "CheckpointLoader", + "CheckpointLoaderSimple", + "CheckpointSave", + "ClearVram", + "ConditioningAverage", + "ConditioningCombine", + "ConditioningConcat", + "ConditioningSetArea", + "ConditioningSetAreaPercentage", + "ConditioningSetMask", + "ConditioningSetTimestepRange", + "ConditioningZeroOut", + "ControlNetApply", + "ControlNetApplyAdvanced", + "ControlNetLoader", + "CropMask", + "DiffControlNetLoader", + "DiffusersLoader", + "DualCLIPLoader", + "EmptyImage", + "EmptyLatentImage", + "EnhanceControl", + "EnhanceControls", + "ExponentialScheduler", + "FeatherMask", + "FlipSigmas", + "FooocusSampler", + "FooocusSettings", + "FreeU", + "FreeU_V2", + "GLIGENLoader", + "GLIGENTextBoxApply", + "GrowMask", + "HyperTile", + "HypernetworkLoader", + "ImageBatch", + "ImageBlend", + "ImageBlur", + "ImageColorToMask", + "ImageCompositeMasked", + "ImageCrop", + "ImageInvert", + "ImageOnlyCheckpointLoader", + "ImageOnlyCheckpointSave", + "ImagePadForOutpaint", + "ImagePrompts", + "ImageQuantize", + "ImageScale", + "ImageScaleBy", + "ImageScaleToTotalPixels", + "ImageSharpen", + "ImageToMask", + "ImageUpscaleWithModel", + "InpaintModelConditioning", + "InpaintOutpaint", + "InvertMask", + "JoinImageWithAlpha", + "KSampler", + "KSamplerAdvanced", + "KSamplerSelect", + "KarrasScheduler", + "LatentAdd", + "LatentBatch", + "LatentBatchSeedBehavior", + "LatentBlend", + "LatentComposite", + "LatentCompositeMasked", + "LatentCrop", + "LatentFlip", + "LatentFromBatch", + "LatentInterpolate", + "LatentMultiply", + "LatentRotate", + "LatentSubtract", + "LatentUpscale", + "LatentUpscaleBy", + "LoadImage", + "LoadImageMask", + "LoadLatent", + "LoraLoader", + "LoraLoaderModelOnly", + "LoraStacks", + "MaskComposite", + "MaskToImage", + "ModelMergeAdd", + "ModelMergeBlocks", + "ModelMergeSimple", + "ModelMergeSubtract", + "ModelSamplingContinuousEDM", + "ModelSamplingDiscrete", + "PatchModelAddDownscale", + "PerpNeg", + "PhotoMakerEncode", + "PhotoMakerLoader", + "PolyexponentialScheduler", + "PorterDuffImageComposite", + "PreviewImage", + "RebatchImages", + "RebatchLatents", + "RepeatImageBatch", + "RepeatLatentBatch", + "RescaleCFG", + "SDTurboScheduler", + "SD_4XUpscale_Conditioning", + "SVD_img2vid_Conditioning", + "SamplerCustom", + "SamplerDPMPP_2M_SDE", + "SamplerDPMPP_SDE", + "SamplerTCD", + "SaveAnimatedPNG", + "SaveAnimatedWEBP", + "SaveImage", + "SaveLatent", + "SelfAttentionGuidance", + "SetLatentNoiseMask", + "SolidMask", + "SplitImageWithAlpha", + "SplitSigmas", + "StableZero123_Conditioning", + "StableZero123_Conditioning_Batched", + "StyleModelApply", + "StyleModelLoader", + "TomePatchModel", + "UNETLoader", + "UpscaleModelLoader", + "UpscaleVary", + "VAEDecode", + "VAEDecodeTiled", + "VAEEncode", + "VAEEncodeForInpaint", + "VAEEncodeTiled", + "VAELoader", + "VAESave", + "VPScheduler", + "VideoLinearCFGGuidance", + "unCLIPCheckpointLoader", + "unCLIPConditioning" + ], + { + "author": "Konie", + "title_aux": "ComfyUI-Fooocus" + } + ], + "https://github.com/muhammederem/blip-comfyui": [ + [ + "Blip Processor Node", + "List to Text Node" + ], + { + "title_aux": "BLIP Vision-Language Model Integration" + } + ], + "https://github.com/mullakhmetov/comfyui_dynamic_util_nodes": [ + [ + "ConcatStrings", + "FormatString", + "GetFiles", + "LoadImageByPath", + "StringOutput" + ], + { + "title_aux": "comfyui_dynamic_util_nodes" + } + ], + "https://github.com/muxueChen/ComfyUI_NTCosyVoice": [ + [ + "NTCosyVoiceCrossLingualSampler", + "NTCosyVoiceInstruct2Sampler", + "NTCosyVoiceZeroShotSampler" + ], + { + "title_aux": "CosyVoice2 for ComfyUI" + } + ], + "https://github.com/muzi12888/ComfyUI-PoseKeypoint-Mask": [ + [ + "Image Brightness", + "Openpose Keypoint Mask" + ], + { + "title_aux": "PoseKeypoint Mask" + } + ], + "https://github.com/my-opencode/ComfyUI_IndustrialMagick": [ + [ + "IndustrialMagick", + "IndustrialMagickImageIngest" + ], + { + "title_aux": "ComfyUI_IndustrialMagick" + } + ], + "https://github.com/my-opencode/ComfyUI_KSamplerTimer": [ + [ + "KSamplerTimer" + ], + { + "author": "Ludovic Anterieur", + "description": "This extension provides a wrapper of the native KSampler which outputs generation time.", + "nickname": "\u23f1", + "title": "KSampler (timer)", + "title_aux": "ComfyUI_KSamplerTimer" + } + ], + "https://github.com/myshell-ai/ComfyUI-ShellAgent-Plugin": [ + [ + "ShellAgentPluginInputAudio", + "ShellAgentPluginInputBoolean", + "ShellAgentPluginInputFloat", + "ShellAgentPluginInputImage", + "ShellAgentPluginInputInteger", + "ShellAgentPluginInputText", + "ShellAgentPluginInputVideo", + "ShellAgentPluginOutputBoolean", + "ShellAgentPluginOutputFloat", + "ShellAgentPluginOutputInteger", + "ShellAgentPluginOutputText", + "ShellAgentPluginSaveAudio", + "ShellAgentPluginSaveAudios", + "ShellAgentPluginSaveImage", + "ShellAgentPluginSaveImages", + "ShellAgentPluginSaveVideoVHS" + ], + { + "author": "MyShell", + "description": "", + "title": "comfyui-shellagent-plugin", + "title_aux": "ComfyUI-ShellAgent-Plugin" + } + ], + "https://github.com/n0neye/A3D-comfyui-integration": [ + [ + "A3DListener", + "UniqueNodeName" + ], + { + "title_aux": "A3D ComfyUI Integration" + } + ], + "https://github.com/nagolinc/ComfyUI_FastVAEDecorder_SDXL": [ + [ + "FastLatentToImage" + ], + { + "title_aux": "ComfyUI_FastVAEDecorder_SDXL" + } + ], + "https://github.com/nagolinc/comfyui_openai_node": [ + [ + "OpenAINode" + ], + { + "title_aux": "comfyui_openai_node" + } + ], + "https://github.com/nako-nakoko/ComfyUI_Mel_Nodes": [ + [ + "AddFileNameonly", + "ResolutionSwitcher", + "Split Image Batch", + "Unet Selector_gguf", + "mel_RandomIntNode", + "mel_TextFilterNode", + "mel_TextSplitNode", + "mel_TextSplitNode2" + ], + { + "title_aux": "ComfyUI_Mel_Nodes" + } + ], + "https://github.com/namtb96/OmniGen2-Simple-Node": [ + [ + "OmniGen2ModelLoader", + "OmniGen2Sampler" + ], + { + "title_aux": "OmniGen2 Simple Node" + } + ], + "https://github.com/narusas/Comfyui-Logic-Support": [ + [ + "BooleanIndexAdder", + "NumberConditionChecker", + "NumberRangeIndex", + "NumberSequenceGenerator", + "StringConcatenator", + "StringSwitchByNumber" + ], + { + "title_aux": "ComfyUI Logic Support" + } + ], + "https://github.com/natto-maki/ComfyUI-NegiTools": [ + [ + "NegiTools_CompositeImages", + "NegiTools_DepthEstimationByMarigold", + "NegiTools_DetectFaceRotationForInpainting", + "NegiTools_ImageProperties", + "NegiTools_LatentProperties", + "NegiTools_NoiseImageGenerator", + "NegiTools_OpenAiDalle3", + "NegiTools_OpenAiGpt", + "NegiTools_OpenAiGpt4v", + "NegiTools_OpenAiTranslate", + "NegiTools_OpenPoseToPointList", + "NegiTools_PointListToMask", + "NegiTools_RandomImageLoader", + "NegiTools_SaveImageToDirectory", + "NegiTools_SeedGenerator", + "NegiTools_StereoImageGenerator", + "NegiTools_StringFunction" + ], + { + "title_aux": "ComfyUI-NegiTools" + } + ], + "https://github.com/nchenevey1/comfyui-gimp-nodes": [ + [ + "NC_LoadImageGIMP", + "NC_LoadMaskGIMP", + "NC_SendImageDimsWebSocketGIMP", + "NC_SendImageWebSocketGIMP" + ], + { + "title_aux": "comfyui-gimp-nodes" + } + ], + "https://github.com/negaga53/comfyui-imgloader": [ + [ + "ImageLoader" + ], + { + "title_aux": "ComfyUI Universal Image Loader" + } + ], + "https://github.com/neggo/comfyui-sambanova": [ + [ + "SambaNova API Node", + "SambaNovaNode" + ], + { + "title_aux": "comfyui-sambanova" + } + ], + "https://github.com/neocrz/comfyui-usetaesd": [ + [ + "DecodeTAESD", + "DecodeTAESDTiled", + "EncodeTAESD", + "EncodeTAESDTiled" + ], + { + "title_aux": "comfyui-usetaesd" + } + ], + "https://github.com/neph1/comfyui-smooth-step-lora-loader": [ + [ + "Smooth_Step_Lora_Loader" + ], + { + "title_aux": "comfyui-smooth-step-lora-loader" + } + ], + "https://github.com/neverbiasu/ComfyUI-BAGEL": [ + [ + "BagelImageEdit", + "BagelImageUnderstanding", + "BagelModelLoader", + "BagelTextToImage" + ], + { + "title_aux": "ComfyUI-BAGEL" + } + ], + "https://github.com/neverbiasu/ComfyUI-ChatTTS": [ + [ + "ChatTTSLoader", + "ChatTTS_ExtractSpeaker", + "ChatTTS_LoadSpeakerProfile", + "ChatTTS_Sampler", + "ChatTTS_SaveSpeakerProfile", + "ChatTTS_SeedBasedSpeaker", + "ChatTTS_TextNormalizer", + "ChatTTS_TextSplitter" + ], + { + "title_aux": "ComfyUI-ChatTTS" + } + ], + "https://github.com/neverbiasu/ComfyUI-Dashscope": [ + [ + "DashscopeEmoCaller", + "DashscopeLLMLoader", + "DashscopeModelCaller", + "DashscopeOCRCaller", + "DashscopeVLMLoader" + ], + { + "title_aux": "ComfyUI-Dashscope" + } + ], + "https://github.com/neverbiasu/ComfyUI-Image-Captioner": [ + [ + "ImageCaptioner" + ], + { + "title_aux": "ComfyUI-Image-Captioner" + } + ], + "https://github.com/neverbiasu/ComfyUI-SAM2": [ + [ + "GroundingDinoModelLoader (segment anything2)", + "GroundingDinoSAM2Segment (segment anything2)", + "InvertMask (segment anything)", + "IsMaskEmpty", + "SAM2ModelLoader (segment anything2)" + ], + { + "title_aux": "ComfyUI SAM2(Segment Anything 2)" + } + ], + "https://github.com/neverbiasu/ComfyUI-StyleShot": [ + [ + "StyleShotApply" + ], + { + "title_aux": "ComfyUI-StyleShot" + } + ], + "https://github.com/ngosset/ComfyUI-ImageSimilarity": [ + [ + "Image Similarity" + ], + { + "title_aux": "ImageSimilarity" + } + ], + "https://github.com/nicehero/comfyui-SegGPT": [ + [ + "SegGPT" + ], + { + "title_aux": "comfyui-SegGPT" + } + ], + "https://github.com/nickve28/ComfyUI-Nich-Utils": [ + [ + "Image from Dir Selector (Nich)", + "Select Text with Regular Expression (Nich)" + ], + { + "title_aux": "ComfyUI Nich Utils" + } + ], + "https://github.com/nicofdga/DZ-FaceDetailer": [ + [ + "DZ_Face_Detailer" + ], + { + "title_aux": "DZ-FaceDetailer" + } + ], + "https://github.com/niknah/ComfyUI-F5-TTS": [ + [ + "F5TTSAudio", + "F5TTSAudioAdvanced", + "F5TTSAudioInputs" + ], + { + "title_aux": "ComfyUI F5-TTS" + } + ], + "https://github.com/niknah/ComfyUI-Hunyuan-3D-2": [ + [ + "Hunyuan3D2ImageTo3D" + ], + { + "title_aux": "ComfyUI Hunyuan-3D-2" + } + ], + "https://github.com/niknah/ComfyUI-InfiniteYou": [ + [ + "InfiniteYouSampler" + ], + { + "title_aux": "ComfyUI-InfiniteYou" + } + ], + "https://github.com/niknah/audio-general-ComfyUI": [ + [ + "AudioBassTreble", + "AudioConcat", + "AudioInfo", + "AudioMix", + "AudioPitch", + "AudioSampleRate", + "AudioSpeed", + "AudioTrimSilenceRosa", + "AudioTrimSilenceVAD" + ], + { + "title_aux": "Audio General" + } + ], + "https://github.com/nilor-corp/nilor-nodes": [ + [ + "Nilor Blur Analysis", + "Nilor Categorize String", + "Nilor Count Images In Directory", + "Nilor Extract Filename from Path", + "Nilor Int To List Of Bools", + "Nilor Interpolated Float List", + "Nilor Inverse Map Float List", + "Nilor List of Ints", + "Nilor Load Image By Index", + "Nilor One Minus Float List", + "Nilor Output Filename String", + "Nilor Random String", + "Nilor Remap Float List", + "Nilor Remap Float List Auto Input", + "Nilor Repeat & Trim Image Batch", + "Nilor Repeat, Shuffle, & Trim Image Batch", + "Nilor Save EXR Arbitrary", + "Nilor Save Image To HF Dataset", + "Nilor Save Video To HF Dataset", + "Nilor Select Index From List", + "Nilor Shuffle Image Batch", + "Nilor To Sparse Index Method", + "Nilor n Fractions of Int" + ], + { + "title_aux": "Nilor Nodes by Nilor Corp" + } + ], + "https://github.com/ningxiaoxiao/comfyui-NDI": [ + [ + "NDI_LoadImage", + "NDI_SendImage" + ], + { + "title_aux": "comfyui-NDI" + } + ], + "https://github.com/nirbhay-faaya/ImgProcessing_ComfyUI": [ + [ + "ImageConcat", + "ImageCropMultEight", + "ImageCut", + "LightingPreprocessor" + ], + { + "title_aux": "ImgProcessing_ComfyUI" + } + ], + "https://github.com/nirex0/ComfyUI_pytorch_openpose": [ + [ + "pytorch_openpose" + ], + { + "title_aux": "ComfyUI_pytorch_openpose" + } + ], + "https://github.com/nisaruj/comfyui-daam": [ + [ + "CLIPTextEncodeWithTokens", + "DAAMAnalyzer", + "KSamplerDAAM" + ], + { + "title_aux": "ComfyUI-DAAM" + } + ], + "https://github.com/nisimjoseph/ComfyUI_OpenAI-Prompter": [ + [ + "OpenAI Prompt Generator" + ], + { + "title_aux": "ComfyUI OpenAI Prompter" + } + ], + "https://github.com/nkchocoai/ComfyUI-DanbooruPromptQuiz": [ + [ + "DanbooruPromptComparison", + "DanbooruPromptQuiz" + ], + { + "title_aux": "ComfyUI-DanbooruPromptQuiz" + } + ], + "https://github.com/nkchocoai/ComfyUI-Dart": [ + [ + "DanbooruTagsTransformerBanTagsFromRegex", + "DanbooruTagsTransformerComposePrompt", + "DanbooruTagsTransformerComposePromptV2", + "DanbooruTagsTransformerDecode", + "DanbooruTagsTransformerDecodeBySplitedParts", + "DanbooruTagsTransformerGenerate", + "DanbooruTagsTransformerGenerateAdvanced", + "DanbooruTagsTransformerGenerationConfig", + "DanbooruTagsTransformerGetAspectRatio", + "DanbooruTagsTransformerLoader", + "DanbooruTagsTransformerRearrangedByAnimagine", + "DanbooruTagsTransformerRemoveTagToken" + ], + { + "title_aux": "ComfyUI-Dart" + } + ], + "https://github.com/nkchocoai/ComfyUI-PromptUtilities": [ + [ + "PromptUtilitiesConstString", + "PromptUtilitiesConstStringMultiLine", + "PromptUtilitiesFormatString", + "PromptUtilitiesJoinStringList", + "PromptUtilitiesLoadPreset", + "PromptUtilitiesLoadPresetAdvanced", + "PromptUtilitiesPromptWeight", + "PromptUtilitiesRandomPreset", + "PromptUtilitiesRandomPresetAdvanced", + "PromptUtilitiesReplaceOrInsertTag", + "PromptUtilitiesRoundPromptWeight", + "PromptUtilitiesSampleTags", + "PromptUtilitiesSampleTagsWithWeight" + ], + { + "title_aux": "ComfyUI-PromptUtilities" + } + ], + "https://github.com/nkchocoai/ComfyUI-SaveImageWithMetaData": [ + [ + "CreateExtraMetaData", + "SaveImageWithMetaData" + ], + { + "title_aux": "ComfyUI-SaveImageWithMetaData" + } + ], + "https://github.com/nkchocoai/ComfyUI-SizeFromPresets": [ + [ + "EmptyLatentImageFromPresetsSD15", + "EmptyLatentImageFromPresetsSDXL", + "GetSimilarResolution", + "GetSimilarResolutionEmptyLatent", + "RandomEmptyLatentImageFromPresetsSD15", + "RandomEmptyLatentImageFromPresetsSDXL", + "RandomSizeFromPresetsSD15", + "RandomSizeFromPresetsSDXL", + "SizeFromPresetsSD15", + "SizeFromPresetsSDXL" + ], + { + "title_aux": "ComfyUI-SizeFromPresets" + } + ], + "https://github.com/nkchocoai/ComfyUI-TextOnSegs": [ + [ + "CalcMaxFontSize", + "ExtractDominantColor", + "GetComplementaryColor", + "SegsToRegion", + "TextOnSegsFloodFill" + ], + { + "title_aux": "ComfyUI-TextOnSegs" + } + ], + "https://github.com/nobrainX2/comfyUI-customDia": [ + [ + "Audio retimer", + "Dia text to speech" + ], + { + "title_aux": "ComfyUI Custom Dia" + } + ], + "https://github.com/noembryo/ComfyUI-noEmbryo": [ + [ + "PromptTermList1", + "PromptTermList2", + "PromptTermList3", + "PromptTermList4", + "PromptTermList5", + "PromptTermList6" + ], + { + "author": "noEmbryo", + "description": "Some useful nodes for ComfyUI", + "nickname": "noEmbryo", + "title": "noEmbryo nodes", + "title_aux": "noEmbryo nodes" + } + ], + "https://github.com/nofunstudio/Node_Fun_ComfyUI": [ + [ + "DynamicQueueCounter", + "FalAPI_kling_video", + "FalAPI_recraft_upscale", + "FluxKontextInpaintingConditioning", + "Fun KSampler", + "IframeView", + "IndexedStringSelector", + "Kontext Inpainting Conditioning", + "LayeredInfiniteZoom", + "MultiAlphaComposite", + "Replicate flux 1.1 pro ultra", + "ReplicateAPI_flux_1_1_pro_ultra", + "ReplicateAPI_flux_fill_pro", + "StringLower" + ], + { + "title_aux": "Node_Fun_ComfyUI" + } + ], + "https://github.com/northumber/ComfyUI-northTools": [ + [ + "BooleanIndexesToString", + "ConcatHistoryString", + "ExtractMetadataByKey", + "ImageToTrue", + "LoadImagesFromDirByIndexBatch", + "LoadImagesFromDirByIndexList", + "LoadImagesFromDirList", + "SumIntegers" + ], + { + "title_aux": "ComfyUI-northTools" + } + ], + "https://github.com/nosiu/comfyui-instantId-faceswap": [ + [ + "AngleFromFace", + "AngleFromKps", + "ComposeRotated", + "ControlNetInstantIdApply", + "FaceEmbed", + "FaceEmbedCombine", + "InstantIdAdapterApply", + "InstantIdAndControlnetApply", + "Kps2dRandomizer", + "Kps3dFromImage", + "Kps3dRandomizer", + "KpsCrop", + "KpsDraw", + "KpsMaker", + "KpsRotate", + "KpsScale", + "KpsScaleBy", + "LoadInsightface", + "LoadInstantIdAdapter", + "MaskFromKps", + "PreprocessImage", + "PreprocessImageAdvanced", + "RotateImage" + ], + { + "title_aux": "comfyui-instantId-faceswap" + } + ], + "https://github.com/nosiu/comfyui-text-randomizer": [ + [ + "ConcatText", + "RandomTextChoice", + "RandomizeText", + "RandomizeTextWithCheck", + "ShowText" + ], + { + "title_aux": "comfyui-text-randomizer" + } + ], + "https://github.com/noxinias/ComfyUI_NoxinNodes": [ + [ + "NoxinChime", + "NoxinPromptLoad", + "NoxinPromptSave", + "NoxinScaledResolution", + "NoxinSimpleMath", + "NoxinSplitPrompt" + ], + { + "title_aux": "ComfyUI_NoxinNodes" + } + ], + "https://github.com/nsdtcloud3d/ComfyUI-3D-Convert": [ + [ + "ConvertTo3DFormat", + "Load3DConvertAPIKEY", + "Load3DFile" + ], + { + "title_aux": "ComfyUI-3D-Convert" + } + ], + "https://github.com/ntc-ai/ComfyUI-DARE-LoRA-Merge": [ + [ + "Apply LoRA", + "DARE Merge LoRA Stack", + "Save LoRA" + ], + { + "title_aux": "ComfyUI - Apply LoRA Stacker with DARE" + } + ], + "https://github.com/nuanarchy/ComfyUI-NuA-BIRD": [ + [ + "Bird_Deblurring_NuA", + "Bird_Denoising_NuA", + "Bird_Inpainting_NuA", + "Bird_Loader_NuA", + "Bird_Non_Uniform_Deblurring_NuA", + "Bird_Super_Resolution_NuA" + ], + { + "title_aux": "ComfyUI-NuA-BIRD" + } + ], + "https://github.com/nuanarchy/ComfyUI-NuA-FlashFace": [ + [ + "FlashFace_Loader_NuA", + "FlashFace_Sampler_NuA" + ], + { + "title_aux": "ComfyUI-NuA-FlashFace" + } + ], + "https://github.com/nullquant/ComfyUI-BrushNet": [ + [ + "BlendInpaint", + "BrushNet", + "BrushNetLoader", + "CutForInpaint", + "PowerPaint", + "PowerPaintCLIPLoader", + "RAUNet", + "Terminal" + ], + { + "author": "nullquant", + "description": "These are custom nodes for ComfyUI native implementation of BrushNet, PowerPaint and RAUNet models", + "nickname": "BrushName nodes", + "title": "BrushNet", + "title_aux": "BrushNet" + } + ], + "https://github.com/numz/ComfyUI-FlowChain": [ + [ + "WorkflowLipSync" + ], + { + "title_aux": "ComfyUI-FlowChain" + } + ], + "https://github.com/numz/ComfyUI-SeedVR2_VideoUpscaler": [ + [ + "SeedVR2", + "SeedVR2BlockSwap" + ], + { + "title_aux": "ComfyUI-SeedVR2_VideoUpscaler" + } + ], + "https://github.com/numz/Comfyui-Orpheus": [ + [ + "orpheus", + "orpheusAdvanced" + ], + { + "title_aux": "ComfyUI-Orpheus" + } + ], + "https://github.com/nux1111/ComfyUI_NetDist_Plus": [ + [ + "CombineImageBatch", + "ConditioningFromBase64(Nux)", + "ConditioningToBase64(Nux)", + "ExtractBase64FromImage(Nux)", + "ExtractBase64FromImageUpload(Nux)", + "FetchRemote", + "FetchRemoteWithExtras(Nux)", + "LatentToBase64(Nux)", + "LoadCurrentWorkflowJSON", + "LoadDiskWorkflowJSON", + "LoadImageUrl", + "LoadLatentFromBase64(Nux)", + "LoadLatentNumpy", + "LoadLatentUrl", + "LoadWorkflowJSON", + "RemoteApplyValues(Nux)", + "RemoteApplyValuesMulti(Nux)", + "RemoteChainEnd", + "RemoteChainStart", + "RemoteChainStart(Nux)", + "RemoteQueueSimple", + "RemoteQueueSimple(Nux)", + "RemoteQueueWorker", + "SaveDiskWorkflowJSON", + "SaveImageUrl", + "SaveImageWithBase64(Nux)", + "SaveLatentNumpy" + ], + { + "title_aux": "ComfyUI_NetDist_Plus" + } + ], + "https://github.com/o-l-l-i/ComfyUI-Olm-ChannelMixer": [ + [ + "OlmChannelMixer" + ], + { + "title_aux": "Olm Channel Mixer for ComfyUI" + } + ], + "https://github.com/o-l-l-i/ComfyUI-Olm-ColorBalance": [ + [ + "OlmColorBalance" + ], + { + "title_aux": "Olm Color Balance for ComfyUI" + } + ], + "https://github.com/o-l-l-i/ComfyUI-Olm-CurveEditor": [ + [ + "OlmCurveEditor" + ], + { + "title_aux": "Olm Curve Editor for ComfyUI" + } + ], + "https://github.com/o-l-l-i/ComfyUI-Olm-DragCrop": [ + [ + "OlmDragCrop" + ], + { + "title_aux": "Olm DragCrop for ComfyUI" + } + ], + "https://github.com/o-l-l-i/ComfyUI-Olm-ImageAdjust": [ + [ + "OlmImageAdjust" + ], + { + "title_aux": "Olm Image Adjust for ComfyUI" + } + ], + "https://github.com/o-l-l-i/ComfyUI-Olm-Resolution-Picker": [ + [ + "OlmResolutionPicker" + ], + { + "title_aux": "Olm Resolution Picker for ComfyUI" + } + ], + "https://github.com/o-l-l-i/ComfyUI-Olm-Sketch": [ + [ + "OlmSketch" + ], + { + "title_aux": "Olm Sketch for ComfyUI" + } + ], + "https://github.com/o-l-l-i/ComfyUI-OlmLUT": [ + [ + "OlmLUT" + ], + { + "title_aux": "Olm LUT Node for ComfyUI" + } + ], + "https://github.com/okgo4/ComfyUI-Mosaic-Mask": [ + [ + "MosaicMask" + ], + { + "title_aux": "ComfyUI-Mosaic-Mask" + } + ], + "https://github.com/olduvai-jp/ComfyUI-HfLoader": [ + [ + "ControlNet Loader From HF", + "Lora Loader From HF" + ], + { + "title_aux": "ComfyUI-HfLoader" + } + ], + "https://github.com/oleksandr612/ComfyUI-Counter": [ + [ + "Simple Counter" + ], + { + "title_aux": "ComfyUI-Counter" + } + ], + "https://github.com/olivv-cs/ComfyUI-FunPack": [ + [ + "FunPackCLIPLoader", + "FunPackContinueVideo", + "FunPackImg2LatentInterpolation", + "FunPackPromptEnhancer", + "FunPackVideoStitch" + ], + { + "title_aux": "ComfyUI-FunPack" + } + ], + "https://github.com/omar92/ComfyUI-QualityOfLifeSuit_Omar92": [ + [ + "CLIPStringEncode _O", + "Chat completion _O", + "ChatGPT Simple _O", + "ChatGPT _O", + "ChatGPT compact _O", + "Chat_Completion _O", + "Chat_Message _O", + "Chat_Message_fromString _O", + "Concat Text _O", + "ConcatRandomNSP_O", + "Debug String _O", + "Debug Text _O", + "Debug Text route _O", + "Edit_image _O", + "Equation1param _O", + "Equation2params _O", + "GetImage_(Width&Height) _O", + "GetLatent_(Width&Height) _O", + "ImageScaleFactor _O", + "ImageScaleFactorSimple _O", + "LatentUpscaleFactor _O", + "LatentUpscaleFactorSimple _O", + "LatentUpscaleMultiply", + "Note _O", + "QOL Split String", + "RandomNSP _O", + "Replace Text _O", + "String _O", + "Text _O", + "Text2Image _O", + "Trim Text _O", + "VAEDecodeParallel _O", + "combine_chat_messages _O", + "compine_chat_messages _O", + "concat Strings _O", + "create image _O", + "create_image _O", + "debug Completeion _O", + "debug messages_O", + "float _O", + "floatToInt _O", + "floatToText _O", + "int _O", + "intToFloat _O", + "load_openAI _O", + "replace String _O", + "replace String advanced _O", + "saveTextToFile _O", + "seed _O", + "selectLatentFromBatch _O", + "string2Image _O", + "trim String _O", + "variation_image _O" + ], + { + "title_aux": "Quality of life Suit:V2" + } + ], + "https://github.com/openvino-dev-samples/comfyui_openvino": [ + [ + "OpenVINO_TorchCompileModel" + ], + { + "title_aux": "ComfyUI-OpenVINO" + } + ], + "https://github.com/opvelll/ComfyUI_TextListProduct": [ + [ + "ProductedString", + "PromptPairConcat", + "TextListProduct", + "TextListProductWithSingleA", + "TextListProductWithSingleB", + "TextListProductWithSingleBoth" + ], + { + "title_aux": "Comfy UI Text List Product" + } + ], + "https://github.com/orange90/ComfyUI-Regex-Runner": [ + [ + "RegexNode" + ], + { + "title_aux": " ComfyUI-Regex-Runner" + } + ], + "https://github.com/orex2121/comfyui-OreX": [ + [ + "IoNetVision", + "orex IoNet Chat", + "orex IoNet Vision", + "orex IoNet Vision Url", + "orex Load Image", + "orex Load Image Batch", + "orex Save Image" + ], + { + "title_aux": "comfyui-OreX" + } + ], + "https://github.com/orion4d/Calculator_Pro": [ + [ + "DataConverter", + "LengthConverter", + "ManualRateConverter", + "MassConverter", + "ScientificCalculatorTri", + "TimeConverter", + "UniversalConverter", + "VolumeConverter" + ], + { + "title_aux": "CalculatorPro - Node Suite for ComfyUI" + } + ], + "https://github.com/orion4d/ComfyUI-Image-Effects": [ + [ + "AsciiArtNode", + "AsciiTextNode", + "AuroraNode", + "BarrelDistortionNode", + "CSSFiltersNode", + "ChannelMixerNode", + "ColorBalanceNode", + "CrystallizeNode", + "CurvesNode", + "FilmGrainNode", + "FisheyeNode", + "GodRaysNode", + "HexagonalPixelateNode", + "HolographicNode", + "KaleidoscopeAdvancedNode", + "KaleidoscopeNode", + "LensFlareNode", + "LevelsNode", + "LightLeaksNode", + "NeonGlowNode", + "PinchNode", + "PolaroidNode", + "PolygonNode", + "RippleNode", + "SaverPlusNode", + "ShadowHighlightNode", + "SpherizeNode", + "TriangulateNode", + "VHSGlitchNode", + "VibranceNode", + "VintageTVNode", + "VoronoiNode" + ], + { + "title_aux": "ComfyUI-Image-Effects" + } + ], + "https://github.com/orion4d/ComfyUI_colormaster": [ + [ + "AnnotateHexLines", + "ColorPaletteExtractor", + "HexColorToImage", + "ImageCollageNode", + "SelectHexLine" + ], + { + "title_aux": "ComfyUI Colormaster Nodes" + } + ], + "https://github.com/orion4d/ComfyUI_extract_imag": [ + [ + "ExtractAndSaveImagesFromDocument" + ], + { + "title_aux": "ComfyUI_extract_imag" + } + ], + "https://github.com/orion4d/ComfyUI_image-display": [ + [ + "DisplayImageWithMask" + ], + { + "title_aux": "Display Image with Mask for ComfyUI" + } + ], + "https://github.com/orion4d/ComfyUI_pdf_nodes": [ + [ + "PDFExtractTextFromPages", + "PDFGetPageCount", + "PDFLoad", + "PDFMerge", + "PDFRotatePages", + "PDFSave", + "PDFSelectPageAndExtractText" + ], + { + "title_aux": "ComfyUI PDF Nodes" + } + ], + "https://github.com/orion4d/illusion_node": [ + [ + "AdvancedAutostereogramNode", + "AutostereogramNode", + "CheckerboardNode", + "ColorImageNode", + "PatternGeneratorNode", + "TessellationNode", + "TileImageRepeaterNode" + ], + { + "title_aux": "ComfyUI Illusion & Pattern Nodes" + } + ], + "https://github.com/orssorbit/ComfyUI-wanBlockswap": [ + [ + "wanBlockSwap" + ], + { + "title_aux": "ComfyUI-wanBlockswap" + } + ], + "https://github.com/oshtz/ComfyUI-oshtz-nodes": [ + [ + "EasyAspectRatioNode", + "GPTImage1", + "LLMAIONode", + "LoRASwitcherNode", + "LoRASwitcherNode20", + "LoRASwitcherNode40", + "LoraSwitcherDynamic", + "StringSplitterNode" + ], + { + "title_aux": "oshtz Nodes" + } + ], + "https://github.com/osi1880vr/prompt_quill_comfyui": [ + [ + "PromptQuillGenerate", + "PromptQuillGenerateConditioning", + "PromptQuillSail", + "PromptQuillSailConditioning" + ], + { + "title_aux": "ComfyUI_Prompt-Quill" + } + ], + "https://github.com/ostris/ComfyUI-FlexTools": [ + [ + "Flex2Conditioner", + "FlexGuidance", + "FlexLoraLoader", + "FlexLoraLoaderModelOnly" + ], + { + "nodename_pattern": "- Ostris$", + "title_aux": "Flex.1 tools" + } + ], + "https://github.com/ostris/ostris_nodes_comfyui": [ + [ + "Batch Image Loader - Ostris", + "LLM Pipe Loader - Ostris", + "LLM Prompt Upsampling - Ostris", + "One Seed - Ostris", + "Save Image Direct - Ostris", + "Text Box - Ostris" + ], + { + "nodename_pattern": "- Ostris$", + "title_aux": "Ostris Nodes ComfyUI" + } + ], + "https://github.com/ownimage/ComfyUI-ownimage": [ + [ + "Caching Image Loader" + ], + { + "title_aux": "ComfyUI-ownimage" + } + ], + "https://github.com/oxysoft/ComfyUI-gowiththeflow": [ + [ + "KSamplerNoiseless", + "NoiseWarperNode" + ], + { + "title_aux": "ComfyUI-gowiththeflow" + } + ], + "https://github.com/oyvindg/ComfyUI-TrollSuite": [ + [ + "BinaryImageMask", + "ImagePadding", + "LoadLastImage", + "RandomMask", + "TransparentImage" + ], + { + "title_aux": "ComfyUI-TrollSuite" + } + ], + "https://github.com/oztrkoguz/ComfyUI_StoryCreator": [ + [ + "Kosmos2SamplerSimple2", + "KosmosLoader2", + "StoryLoader", + "StorySamplerSimple", + "Write2" + ], + { + "title_aux": "ComfyUI StoryCreater" + } + ], + "https://github.com/p1atdev/comfyui-timm-backbone": [ + [ + "TimmBackboneImageEncode", + "TimmBackboneImageNormalize", + "TimmBackboneLoader", + "TimmBackboneRGB2BGR", + "TimmEmbedsPrint" + ], + { + "title_aux": "comfyui-timm-backbone" + } + ], + "https://github.com/palant/image-resize-comfyui": [ + [ + "ImageResize" + ], + { + "title_aux": "Image Resize for ComfyUI" + } + ], + "https://github.com/pamparamm/ComfyUI-ppm": [ + [ + "AttentionCouplePPM", + "CFGLimiterGuider", + "CFGPPSamplerSelect", + "CLIPMicroConditioning", + "CLIPNegPip", + "CLIPTextEncodeBREAK", + "CLIPTextEncodeInvertWeights", + "CLIPTokenCounter", + "ConditioningZeroOutCombine", + "ConvertTimestepToSigma", + "DynSamplerSelect", + "DynamicThresholdingPost", + "DynamicThresholdingSimplePost", + "EmptyLatentImageAR", + "FreeU2PPM", + "Guidance Limiter", + "LatentOperationTonemapLuminance", + "LatentToMaskBB", + "LatentToWidthHeight", + "MaskCompositePPM", + "PPMSamplerSelect", + "RenormCFGPost", + "RescaleCFGPost" + ], + { + "title_aux": "ComfyUI-ppm" + } + ], + "https://github.com/pamparamm/ComfyUI-vectorscope-cc": [ + [ + "DiffusionCG", + "NormalizeLatent", + "VectorscopeCC" + ], + { + "title_aux": "ComfyUI Vectorscope CC" + } + ], + "https://github.com/pamparamm/sd-perturbed-attention": [ + [ + "NormalizedAttentionGuidance", + "PerturbedAttention", + "Pladis", + "SlidingWindowGuidanceAdvanced", + "SmoothedEnergyGuidanceAdvanced", + "TRTAttachPag", + "TRTPerturbedAttention", + "TokenPerturbationGuidance" + ], + { + "title_aux": "sd-perturbed-attention" + } + ], + "https://github.com/pants007/comfy-pants": [ + [ + "CLIPTextEncodeAIO", + "Image Make Square" + ], + { + "title_aux": "pants" + } + ], + "https://github.com/papcorns/ComfyUI-Papcorns-Node-LoadImageFromUrl": [ + [ + "LoadImageFromUrlOrPath" + ], + { + "title_aux": "ComfyUI Load Image From URL" + } + ], + "https://github.com/papcorns/Papcorns-Comfyui-Custom-Nodes": [ + [ + "PapcornsAspectResize", + "PapcornsAudioTrimAndSave", + "PapcornsAudioTrimmer", + "UploadImageToGCS" + ], + { + "title_aux": "Papcorns ComfyUI Custom Nodes" + } + ], + "https://github.com/pathway8-sudo/ComfyUI-Pathway-CutPNG-Node": [ + [ + "CutPNGNode" + ], + { + "title_aux": "ComfyUI-Pathway-CutPNG-Node" + } + ], + "https://github.com/patriciogonzalezvivo/comfyui_glslnodes": [ + [ + "float", + "glslBuffers", + "glslEditor", + "glslEditorPro", + "glslUniforms", + "glslViewer", + "int", + "vec2", + "vec2 (pos)", + "vec3", + "vec3 (pos)", + "vec4", + "vec4 (color)" + ], + { + "author": "Patricio Gonzalez Vivo", + "description": "A set of nodes to work with GLSL shaders", + "nickname": "GLSL Nodes", + "title": "GLSL Nodes", + "title_aux": "GLSL Nodes" + } + ], + "https://github.com/paulh4x/ComfyUI_PHRenderFormerWrapper": [ + [ + "RenderFormerCamera", + "RenderFormerCameraTarget", + "RenderFormerExampleScene", + "RenderFormerFromJSON", + "RenderFormerGenerator", + "RenderFormerLighting", + "RenderFormerLightingCombine", + "RenderFormerLightingTarget", + "RenderFormerLoadMesh", + "RenderFormerMeshCombine", + "RenderFormerMeshTarget", + "RenderFormerModelLoader", + "RenderFormerRandomizeColors", + "RenderFormerRemeshMesh", + "RenderFormerSceneBuilder" + ], + { + "title_aux": "ComfyUI_PHRenderFormerWrapper" + } + ], + "https://github.com/paulo-coronado/comfy_clip_blip_node": [ + [ + "CLIPTextEncodeBLIP", + "CLIPTextEncodeBLIP-2", + "Example" + ], + { + "title_aux": "comfy_clip_blip_node" + } + ], + "https://github.com/pawelmal0101/ComfyUI-Webhook": [ + [ + "Webhook" + ], + { + "title_aux": "ComfyUI Webhook Notifier" + } + ], + "https://github.com/pbpbpb2705/ComfyUI-LyraVSIH": [ + [ + "MultiObjectMask" + ], + { + "title_aux": "ComfyUI-LyraVSIH" + } + ], + "https://github.com/penposs/ComfyUI_Gemini_Pro": [ + [ + "Gemini File Processing", + "Gemini File Upload", + "Gemini Pro", + "Gemini-Pro-Chat", + "Gemini-Pro-Editimage" + ], + { + "title_aux": "ComfyUI Gemini Pro Node" + } + ], + "https://github.com/penposs/Comfyui_wan_api": [ + [ + "WanAPIImageToVideo", + "WanAPIImageUploader", + "WanAPI_Image2Video", + "WanAPI_ImageUploader" + ], + { + "title_aux": "Comfyui_wan_api" + } + ], + "https://github.com/pharmapsychotic/comfy-cliption": [ + [ + "CLIPtionBeamSearch", + "CLIPtionGenerate", + "CLIPtionLoader" + ], + { + "title_aux": "comfy-cliption" + } + ], + "https://github.com/phazei/ComfyUI-Prompt-Stash": [ + [ + "PromptStashManager", + "PromptStashPassthrough", + "PromptStashSaver" + ], + { + "title_aux": "Prompt Stash" + } + ], + "https://github.com/philiprodriguez/ComfyUI-HunyuanImageLatentToVideoLatent": [ + [ + "HunyuanImageLatentToVideoLatent" + ], + { + "title_aux": "ComfyUI-HunyuanImageLatentToVideoLatent" + } + ], + "https://github.com/philipy1219/ComfyUI-CloudStorage": [ + [ + "LoadImageFromCloud", + "LoadMaskFromCloud", + "LoadVideoFromCloud", + "SaveImageToCloud", + "UploadFileToCloud" + ], + { + "title_aux": "ComfyUI-CloudStorage" + } + ], + "https://github.com/philipy1219/ComfyUI-TaylorSeer": [ + [ + "FluxBlockSwap", + "HidreamBlockSwap", + "TaylorSeer" + ], + { + "title_aux": "ComfyUI-TaylorSeer" + } + ], + "https://github.com/philz1337x/ComfyUI-ClarityAI": [ + [ + "Clarity AI Upscaler" + ], + { + "title_aux": "\u2728 Clarity AI - Creative Image Upscaler and Enhancer for ComfyUI" + } + ], + "https://github.com/phuvinh010701/ComfyUI-Nudenet": [ + [ + "ApplyNudenet", + "FilterdLabel", + "NudenetModelLoader" + ], + { + "title_aux": "ComfyUI-Nudenet" + } + ], + "https://github.com/phyblas/paint-by-example_comfyui": [ + [ + "PaintbyExampleAdvanced", + "PaintbyExampleGen", + "PaintbyExampleSimple", + "PaintbyIchimatsu", + "PaintbySingleColor" + ], + { + "title_aux": "paint-by-example @ ComfyUI" + } + ], + "https://github.com/pictorialink/ComfyUI-Custom-Node-Config": [ + [ + "FormSubmitNode" + ], + { + "title_aux": "ComfyUI-Custom-Node-Config" + } + ], + "https://github.com/pictorialink/ComfyUI-Qwen3-llama.cpp": [ + [ + "Qwen25_VL", + "Qwen3" + ], + { + "title_aux": "ComfyUI-Qwen3-llama.cpp" + } + ], + "https://github.com/pictorialink/ComfyUI-Text-Translation": [ + [ + "Get_Translator", + "Text", + "Text_Concatenate", + "Text_Switch", + "Text_Translation", + "Text_Translation_V2", + "Text_Translation_V2_Full" + ], + { + "title_aux": "ComfyUI-Text-Translation" + } + ], + "https://github.com/picturesonpictures/comfy_PoP": [ + [ + "AdaptiveCannyDetector_PoP", + "AnyAspectRatio", + "ConditioningMultiplier_PoP", + "ConditioningNormalizer_PoP", + "DallE3_PoP", + "EfficientAttention", + "LoadImageResizer_PoP", + "LoraStackLoader10_PoP", + "LoraStackLoader_PoP", + "VAEDecoderPoP", + "VAEEncoderPoP" + ], + { + "title_aux": "comfy_PoP" + } + ], + "https://github.com/pikenrover/ComfyUI_PRNodes": [ + [ + "CheckpointLoaderSimpleExtended", + "EmptyLatentImageScaleBy", + "ImageScaleTo", + "LoadRandomImage", + "LoraLoaderExtended", + "RandomPrompt", + "RandomPromptMixed", + "Save Image w/Metadata" + ], + { + "title_aux": "ComfyUI_PRNodes" + } + ], + "https://github.com/pixelworldai/ComfyUI-AlphaFlatten": [ + [ + "FlattenByAlpha" + ], + { + "title_aux": "ComfyUI-AlphaFlatten" + } + ], + "https://github.com/pkpkTech/ComfyUI-SaveAVIF": [ + [ + "SaveAvif" + ], + { + "title_aux": "ComfyUI-SaveAVIF" + } + ], + "https://github.com/pkpkTech/ComfyUI-TemporaryLoader": [ + [ + "LoadTempCheckpoint", + "LoadTempLoRA", + "LoadTempMultiLoRA" + ], + { + "title_aux": "ComfyUI-TemporaryLoader" + } + ], + "https://github.com/playbook3d/playbook3d-comfyui-nodes": [ + [ + "Beauty Pass Sequence", + "Depth Pass Sequence", + "Mask Pass Sequence", + "Outline Pass Sequence", + "Playbook Aspect Ratio Select", + "Playbook Beauty", + "Playbook Beauty Sequence", + "Playbook Boolean", + "Playbook Depth", + "Playbook Depth Sequence", + "Playbook Float", + "Playbook Image", + "Playbook LoRA Select", + "Playbook LoRA Selection", + "Playbook Mask", + "Playbook Mask Sequence", + "Playbook Number", + "Playbook Outline", + "Playbook Outline Sequence", + "Playbook Render Result", + "Playbook Seed", + "Playbook Text", + "Playbook Video" + ], + { + "title_aux": "Playbook Nodes" + } + ], + "https://github.com/plugcrypt/CRT-Nodes": [ + [ + "AdvancedStringReplace", + "AudioCompressor", + "AudioPreviewer", + "AutopromptProcessor", + "Boolean Transform", + "CLIPTextEncodeFluxMerged", + "CRT Post-Process Suite", + "CRTChromaKeyOverlay", + "CRTFirstLastFrameSelector", + "CRTLoadLastMedia", + "CRTLoadLastVideo", + "CRTPctCropCalculator", + "CRTPostProcess", + "CRT_UpscaleModelAdv", + "ClearStyleModelDualCache", + "FaceEnhancementPipeline", + "FaceEnhancementPipelineWithInjection", + "FancyNoteNode", + "FileLoaderCrawl", + "FluxAIO_CRT", + "FluxControlnetSampler", + "FluxControlnetSamplerWithInjection", + "FluxLoraBlocksPatcher", + "FluxSemanticEncoder", + "FluxTiledSamplerCustomAdvanced", + "ImageLoaderCrawl", + "LatentNoiseInjectionSampler", + "LoadImageResize", + "LoadVideoForVCaptioning", + "Lora Loader Str", + "MaskEmptyFloatNode", + "MaskPassOrPlaceholder", + "ParametricEQNode", + "PonyFaceEnhancementPipelineWithInjection", + "PonyUpscaleSamplerWithInjection", + "Remove Trailing Comma", + "Resolution", + "SamplerSchedulerSelector", + "SaveImageWithPath", + "SaveTextWithPath", + "SaveVideoWithPath", + "SeamlessLoopBlender", + "SimpleFluxShiftNode", + "SimpleKnobNode", + "SimpleToggleNode", + "SmartControlNetApply", + "SmartPreprocessor", + "SmartStyleModelApplyDual", + "Toggle Lora Unet Blocks L1", + "Toggle Lora Unet Blocks L2", + "Video Duration Calculator", + "VideoLoaderCrawl" + ], + { + "author": "chflame", + "description": "A set of nodes for ComfyUI that can composite layer and mask to achieve Photoshop like functionality.", + "nickname": "LayerStyle", + "title": "LayerStyle", + "title_aux": "CRT-Nodes" + } + ], + "https://github.com/pmarmotte2/ComfyUI-Speaker-Isolation": [ + [ + "SpeakerDiarizer" + ], + { + "title_aux": "ComfyUI-Speaker-Isolation" + } + ], + "https://github.com/pnikolic-amd/ComfyUI_MIGraphX": [ + [ + "CompileDiffusersMIGraphX" + ], + { + "title_aux": "MIGraphX Node for ComfyUI" + } + ], + "https://github.com/pollockjj/ComfyUI-MultiGPU": [ + [ + "DeviceSelectorMultiGPU", + "HunyuanVideoEmbeddingsAdapter" + ], + { + "title_aux": "ComfyUI-MultiGPU" + } + ], + "https://github.com/portu-sim/comfyui_bmab": [ + [ + "BMAB Alpha Composit", + "BMAB Base64 Image", + "BMAB Basic", + "BMAB Black And White", + "BMAB Blend", + "BMAB Clip Text Encoder SDXL", + "BMAB Conditioning To Bind", + "BMAB Context", + "BMAB ControlNet", + "BMAB ControlNet IPAdapter", + "BMAB ControlNet Openpose", + "BMAB Crop", + "BMAB Detail Anything", + "BMAB Detect And Mask", + "BMAB Detect And Paste", + "BMAB Detection Crop", + "BMAB Detector", + "BMAB Dummy", + "BMAB Edge", + "BMAB Extractor", + "BMAB Face Detailer", + "BMAB Flux ControlNet", + "BMAB Flux Integrator", + "BMAB Google Gemini Prompt", + "BMAB Image Storage", + "BMAB Import Integrator", + "BMAB Inpaint", + "BMAB Integrator", + "BMAB KSampler", + "BMAB KSamplerHiresFix", + "BMAB KSamplerHiresFixWithUpscaler", + "BMAB KSamplerKohyaDeepShrink", + "BMAB Lama Inpaint", + "BMAB LoRA Loader", + "BMAB Load Image", + "BMAB Load Output Image", + "BMAB Masks To Images", + "BMAB Model To Bind", + "BMAB Noise Generator", + "BMAB Normalize Size", + "BMAB Openpose Hand Detailer", + "BMAB Outpaint By Ratio", + "BMAB Person Detailer", + "BMAB Preview Text", + "BMAB Prompt", + "BMAB Reframe", + "BMAB Remote Access And Save", + "BMAB Remove Background", + "BMAB Resize By Person", + "BMAB Resize By Ratio", + "BMAB Resize and Fill", + "BMAB SD-WebUI API BMAB Extension", + "BMAB SD-WebUI API ControlNet", + "BMAB SD-WebUI API I2I", + "BMAB SD-WebUI API Server", + "BMAB SD-WebUI API T2I", + "BMAB SD-WebUI API T2I Hires.Fix", + "BMAB Save Image", + "BMAB SeedGenerator", + "BMAB Segment Anything", + "BMAB Simple Hand Detailer", + "BMAB Square", + "BMAB Subframe Hand Detailer", + "BMAB Text", + "BMAB ToBind", + "BMAB Upscale With Model", + "BMAB Upscaler", + "BMAB Watermark", + "BMAB Zoom Out" + ], + { + "title_aux": "comfyui_bmab" + } + ], + "https://github.com/prodogape/ComfyUI-EasyOCR": [ + [ + "Apply EasyOCR" + ], + { + "title_aux": "ComfyUI-EasyOCR" + } + ], + "https://github.com/prodogape/ComfyUI-Minio": [ + [ + "Load Image From Minio", + "Save Image To Minio", + "Set Minio Config" + ], + { + "title_aux": "Comfyui-Minio" + } + ], + "https://github.com/prodogape/ComfyUI-OmDet": [ + [ + "Apply OmDet" + ], + { + "title_aux": "ComfyUI-OmDet" + } + ], + "https://github.com/prodogape/Comfyui-Yolov8-JSON": [ + [ + "Apply Yolov8 Model", + "Apply Yolov8 Model Seg", + "Draw Labelme Json", + "Load Yolov8 Model", + "Load Yolov8 Model From Path", + "Save Labelme Json" + ], + { + "title_aux": "Comfyui-Yolov8-JSON" + } + ], + "https://github.com/pschroedl/ComfyUI-SAM2-Realtime": [ + [ + "DownloadAndLoadSAM2RealtimeModel", + "Sam2RealtimeSegmentation" + ], + { + "title_aux": "ComfyUI-SAM2-Realtime" + } + ], + "https://github.com/ptmaster/ComfyUI-Load-Diffusion-Model-to-Muti-GPUs/raw/refs/heads/main/Load%20Diffusion%20Model%20into%20Muti%20GPUs.py": [ + [ + "OverrideLoadedDiffusionDevice" + ], + { + "title_aux": "ComfyUI-Load-Diffusion-Model-to-Muti-GPUs" + } + ], + "https://github.com/ptmaster/Comfyui-PT-Keyframe-Camera": [ + [ + "PT_KeyframeCamera" + ], + { + "title_aux": "Comfyui-PT-Keyframe-Camera" + } + ], + "https://github.com/ptmaster/comfyui-audio-speed": [ + [ + "PT48KHZ", + "PTAudioSpeed", + "PTEnsureStereo" + ], + { + "title_aux": "ComfyUI-audio-speed" + } + ], + "https://github.com/pupba/Comfy_ForEach": [ + [ + "EventBridgeTriggerNode", + "FolderImageLoaderNode", + "IndexedImageSelectorNode", + "IndexedNameSelectorNode", + "IsLastIndexNode", + "LoadPreCheckpointModel", + "LoadPreControlNetModel", + "SaveExactNameImageNode", + "StringViewer", + "TaskIDStorageNode" + ], + { + "title_aux": "ComfyForEach" + } + ], + "https://github.com/purewater2011/comfyui_color_detection": [ + [ + "IsYellowish", + "YellowHeatmap" + ], + { + "title_aux": "comfyui_color_detection" + } + ], + "https://github.com/purpen/ComfyUI-AIRedoon": [ + [ + "AIRedoonApplyLoRAStack", + "AIRedoonCheckLoraFile", + "AIRedoonCheckModelFile", + "AIRedoonConcatText", + "AIRedoonImageCaptioning", + "AIRedoonImageRGBA2RGB", + "AIRedoonLoRAStack", + "AIRedoonPreviewText", + "AIRedoonQwenModelLoader", + "AIRedoonSaveText", + "AIRedoonTranslator" + ], + { + "title_aux": "AIRedoon" + } + ], + "https://github.com/purpen/ComfyUI-ImageTagger": [ + [ + "AIRedoonImageCaptioning" + ], + { + "title_aux": "ComfyUI-ImageTagger" + } + ], + "https://github.com/pvlprk/comfyui-pvl-api-nodes": [ + [ + "PVL Call OpenAI Assistant", + "PVL ComfyDeploy API Caller", + "PVL KONTEXT MAX", + "PVLCheckIfConnected", + "PvlKontextMax" + ], + { + "title_aux": "ComfyUI Assistant Node" + } + ], + "https://github.com/pxl-pshr/GlitchNodes": [ + [ + "ASCII", + "Corruptor", + "DataBend", + "DitherMe", + "FrequencyModulation", + "GlitchIT", + "LineScreen", + "LuminousFlow", + "OrderedDithering", + "PixelFloat", + "PixelRedistribution", + "Rekked", + "Scanz", + "TvGlitch", + "VHSonAcid", + "VaporWave", + "VideoModulation", + "interference" + ], + { + "title_aux": "GlitchNodes" + } + ], + "https://github.com/pythongosssss/ComfyUI-Custom-Scripts": [ + [ + "CheckpointLoader|pysssss", + "ConstrainImageforVideo|pysssss", + "ConstrainImage|pysssss", + "LoadText|pysssss", + "LoraLoader|pysssss", + "MathExpression|pysssss", + "MultiPrimitive|pysssss", + "PlaySound|pysssss", + "Repeater|pysssss", + "ReroutePrimitive|pysssss", + "SaveText|pysssss", + "ShowText|pysssss", + "StringFunction|pysssss", + "SystemNotification|pysssss" + ], + { + "title_aux": "ComfyUI-Custom-Scripts" + } + ], + "https://github.com/pythongosssss/ComfyUI-WD14-Tagger": [ + [ + "WD14Tagger|pysssss" + ], + { + "title_aux": "ComfyUI WD 1.4 Tagger" + } + ], + "https://github.com/pzc163/Comfyui_MiniCPMv2_6-prompt-generator": [ + [ + "Prompt_Generator", + "Save_Prompts" + ], + { + "title_aux": "Comfyui_MiniCPMv2_6-prompt-generator" + } + ], + "https://github.com/quank123wip/ComfyUI-Step1X-Edit": [ + [ + "Step-1XEditNode" + ], + { + "title_aux": "ComfyUI-Step1X-Edit" + } + ], + "https://github.com/quasiblob/ComfyUI-EsesCompositionGuides": [ + [ + "EsesCompositionGuides" + ], + { + "title_aux": "ComfyUI-EsesCompositionGuides" + } + ], + "https://github.com/quasiblob/ComfyUI-EsesImageAdjustments": [ + [ + "EsesImageAdjustments2" + ], + { + "title_aux": "ComfyUI-EsesImageAdjustments" + } + ], + "https://github.com/quasiblob/ComfyUI-EsesImageCompare": [ + [ + "EsesImageCompare" + ], + { + "title_aux": "ComfyUI-EsesImageCompare" + } + ], + "https://github.com/quasiblob/ComfyUI-EsesImageEffectBloom": [ + [ + "EsesImageEffectBloom" + ], + { + "title_aux": "ComfyUI-EsesImageEffectBloom" + } + ], + "https://github.com/quasiblob/ComfyUI-EsesImageEffectCurves": [ + [ + "EsesImageEffectCurves" + ], + { + "title_aux": "ComfyUI-EsesImageEffectCurves" + } + ], + "https://github.com/quasiblob/ComfyUI-EsesImageEffectLevels": [ + [ + "EsesImageEffectLevels" + ], + { + "title_aux": "ComfyUI-EsesImageEffectLevels" + } + ], + "https://github.com/quasiblob/ComfyUI-EsesImageLensEffects": [ + [ + "EsesImageLensEffects" + ], + { + "title_aux": "ComfyUI-EsesImageLensEffects" + } + ], + "https://github.com/quasiblob/ComfyUI-EsesImageOffset": [ + [ + "EsesImageOffset" + ], + { + "title_aux": "ComfyUI-EsesImageOffset" + } + ], + "https://github.com/quasiblob/ComfyUI-EsesImageResize": [ + [ + "EsesImageResize" + ], + { + "title_aux": "EsesImageResize" + } + ], + "https://github.com/quasiblob/ComfyUI-EsesImageTransform": [ + [ + "EsesImageTransform" + ], + { + "title_aux": "ComfyUI-EsesImageTransform" + } + ], + "https://github.com/quasiblob/EsesCompositionGoldenRatio": [ + [ + "EsesCompositionGoldenRatio" + ], + { + "title_aux": "EsesCompositionGoldenRatio" + } + ], + "https://github.com/qwixiwp/queuetools": [ + [ + "load images (queue tools)" + ], + { + "title_aux": "queuetools" + } + ], + "https://github.com/r-vage/ComfyUI-RvTools_v2": [ + [ + "Combine Video Clips", + "WanVideo Vace Seamless Join" + ], + { + "title_aux": "ComfyUI-RvTools_v2" + } + ], + "https://github.com/r3dial/redial-discomphy": [ + [ + "DiscordMessage" + ], + { + "title_aux": "Redial Discomphy - Discord Integration for ComfyUI" + } + ], + "https://github.com/r3dsd/comfyui-template-loader": [ + [ + "TemplateLoader" + ], + { + "title_aux": "Comfyui-Template-Loader" + } + ], + "https://github.com/raindrop313/ComfyUI-WanVideoStartEndFrames": [ + [ + "WanVideoSEDecode", + "WanVideoSEImageClipEncode", + "WanVideoSEModelLoader", + "WanVideoSESampler", + "WanVideoSEVAELoader" + ], + { + "title_aux": "ComfyUI-WanVideoStartEndFrames" + } + ], + "https://github.com/raindrop313/ComfyUI_SD3_Flowedit": [ + [ + "FlowEditCFGGuider", + "FlowEditSampler", + "OutSD3ModelSamplingPred" + ], + { + "title_aux": "ComfyUI_SD3_Flowedit" + } + ], + "https://github.com/rainlizard/ComfyUI-Raffle": [ + [ + "PreviewHistory", + "Raffle", + "TagCategoryStrength" + ], + { + "title_aux": "Raffle" + } + ], + "https://github.com/rainlizard/ComfyUI-WhirlpoolUpscaler": [ + [ + "WhirlpoolUpscaler" + ], + { + "title_aux": "Whirlpool Upscaler" + } + ], + "https://github.com/ramesh-x90/ComfyUI_pyannote": [ + [ + "Speaker Diarization", + "Whisper Segments to Speaker" + ], + { + "title_aux": "ComfyUI_pyannote" + } + ], + "https://github.com/ramyma/A8R8_ComfyUI_nodes": [ + [ + "AttentionCouple", + "AttentionCoupleRegion", + "AttentionCoupleRegions", + "Base64ImageInput", + "Base64ImageOutput" + ], + { + "title_aux": "A8R8 ComfyUI Nodes" + } + ], + "https://github.com/randjtw/advance-aesthetic-score": [ + [ + "Adv_Scoring" + ], + { + "title_aux": "advance-aesthetic-score" + } + ], + "https://github.com/randomnoner11/ComfyUI-MistralAI-API": [ + [ + "InvokeMistralEndpoint", + "LoadFewShotPrompt" + ], + { + "title_aux": "ComfyUI-MistralAI-API" + } + ], + "https://github.com/ratulrafsan/Comfyui-SAL-VTON": [ + [ + "SALVTON_Apply", + "SV_random" + ], + { + "title_aux": "Comfyui-SAL-VTON" + } + ], + "https://github.com/raykindle/ComfyUI_Step1X-Edit": [ + [ + "Step1XEditGenerate", + "Step1XEditModelLoader", + "Step1XEditTeaCacheGenerate", + "Step1XEditTeaCacheModelLoader" + ], + { + "title_aux": "ComfyUI_Step1X-Edit" + } + ], + "https://github.com/raysers/Mflux-ComfyUI": [ + [ + "MfluxControlNetLoader", + "MfluxCustomModels", + "MfluxImg2Img", + "MfluxLorasLoader", + "MfluxModelsDownloader", + "MfluxModelsLoader", + "QuickMfluxNode" + ], + { + "title_aux": "Mflux-ComfyUI" + } + ], + "https://github.com/rcfcu2000/zhihuige-nodes-comfyui": [ + [ + "Combine ZHGMasks", + "Cover ZHGMasks", + "From ZHG pip", + "GroundingDinoModelLoader (zhihuige)", + "GroundingDinoPIPESegment (zhihuige)", + "GroundingDinoSAMSegment (zhihuige)", + "InvertMask (zhihuige)", + "SAMModelLoader (zhihuige)", + "To ZHG pip", + "ZHG FaceIndex", + "ZHG GetMaskArea", + "ZHG Image Levels", + "ZHG SaveImage", + "ZHG SmoothEdge", + "ZHG UltimateSDUpscale" + ], + { + "title_aux": "zhihuige-nodes-comfyui" + } + ], + "https://github.com/rcsaquino/comfyui-custom-nodes": [ + [ + "BackgroundRemover | rcsaquino", + "VAELoader | rcsaquino", + "VAEProcessor | rcsaquino" + ], + { + "title_aux": "rcsaquino/comfyui-custom-nodes" + } + ], + "https://github.com/rdancer/ComfyUI_Florence2SAM2": [ + [ + "RdancerFlorence2SAM2GenerateMask" + ], + { + "title_aux": "ComfyUI_Florence2SAM2" + } + ], + "https://github.com/rdomunky/comfyui-subfolderimageloader": [ + [ + "SubfolderImageLoader" + ], + { + "title_aux": "comfyui-subfolderimageloader" + } + ], + "https://github.com/reallusion/ComfyUI-Reallusion": [ + [ + "additional_image", + "control_net", + "core", + "upscale_data" + ], + { + "title_aux": "Reallusion ComfyUI Custom Nodes" + } + ], + "https://github.com/receyuki/comfyui-prompt-reader-node": [ + [ + "SDAnyConverter", + "SDBatchLoader", + "SDLoraLoader", + "SDLoraSelector", + "SDParameterExtractor", + "SDParameterGenerator", + "SDPromptMerger", + "SDPromptReader", + "SDPromptSaver", + "SDTypeConverter" + ], + { + "author": "receyuki", + "description": "The ultimate solution for managing image metadata and multi-tool compatibility. ComfyUI node version of the SD Prompt Reader", + "nickname": "SD Prompt Reader", + "title": "SD Prompt Reader", + "title_aux": "SD Prompt Reader" + } + ], + "https://github.com/recraft-ai/ComfyUI-RecraftAI": [ + [ + "RecraftBackgroundRemover", + "RecraftBackgroundReplacer", + "RecraftClarityUpscaler", + "RecraftClient", + "RecraftGenerativeUpscaler", + "RecraftImageGenerator", + "RecraftImageToImageTransformer", + "RecraftInpainter" + ], + { + "title_aux": "ComfyUI-RecraftAI" + } + ], + "https://github.com/redhottensors/ComfyUI-Prediction": [ + [ + "AvoidErasePrediction", + "CFGPrediction", + "CharacteristicGuidancePrediction", + "CombinePredictions", + "ConditionedPrediction", + "EarlyMiddleLatePrediction", + "InterpolatePredictions", + "LogSigmas", + "PerpNegPrediction", + "SamplerCustomPrediction", + "ScalePrediction", + "ScaledGuidancePrediction", + "SelectSigmas", + "SplitAtSigma", + "SwitchPredictions" + ], + { + "author": "RedHotTensors", + "description": "Fully customizable Classifer Free Guidance for ComfyUI", + "nickname": "ComfyUI-Prediction", + "title": "ComfyUI-Prediction", + "title_aux": "ComfyUI-Prediction" + } + ], + "https://github.com/regiellis/ComfyUI-EasyColorCorrector": [ + [ + "BatchColorCorrection", + "ColorCorrectionViewer", + "ColorPaletteExtractor", + "EasyColorCorrection", + "FilmEmulation", + "RawImageProcessor", + "VAEColorCorrector" + ], + { + "title_aux": "Easy Color Correction" + } + ], + "https://github.com/regiellis/ComfyUI-EasyNoobai": [ + [ + "EasyNoobai", + "EasyNoobaiMasterModel", + "NoobaiArtists", + "NoobaiCharacters", + "NoobaiClothing", + "NoobaiE621Artists", + "NoobaiE621Characters", + "NoobaiHairstyles", + "NoobaiPony", + "NoobaiPoses" + ], + { + "title_aux": "ComfyUI-EasyNoobai" + } + ], + "https://github.com/regiellis/ComfyUI-EasyPony": [ + [ + "EasyPony" + ], + { + "title_aux": "ComfyUI-EasyPony" + } + ], + "https://github.com/revirevy/Comfyui_saveimage_imgbb": [ + [ + "ImgBBUploader", + "LLM_prompt_generator" + ], + { + "author": "N.RHEVI", + "description": "This custom node allow save image to imgbb.", + "nickname": "save image to imgbb", + "title": "save image to imgbb", + "title_aux": "Comfyui_saveimage_imgbb" + } + ], + "https://github.com/rgthree/rgthree-comfy": [ + [], + { + "author": "rgthree", + "description": "A bunch of nodes I created that I also find useful.", + "nickname": "rgthree", + "nodename_pattern": " \\(rgthree\\)$", + "title": "Comfy Nodes", + "title_aux": "rgthree's ComfyUI Nodes" + } + ], + "https://github.com/rhdunn/comfyui-audio-processing": [ + [ + "ComfyAudio.ApplyFilterBank", + "ComfyAudio.GriffinLim", + "ComfyAudio.InverseSpectrogram", + "ComfyAudio.LinearFilterBank", + "ComfyAudio.LoadAudio", + "ComfyAudio.MelScaleFilterBank", + "ComfyAudio.PlotFilterBank", + "ComfyAudio.PlotSpectrogram", + "ComfyAudio.PlotWaveform", + "ComfyAudio.Spectrogram" + ], + { + "title_aux": "comfyui-audio-processing" + } + ], + "https://github.com/rhdunn/comfyui-bus-plugin": [ + [ + "ComfyBus.CLIPConditioningBusNode", + "ComfyBus.CLIPEncodedPromptBusNode", + "ComfyBus.CheckpointBusNode", + "ComfyBus.ImageBusNode", + "ComfyBus.ImageParameterBusNode", + "ComfyBus.ImageScaleToSideParameterBusNode", + "ComfyBus.ImageSizeBusNode", + "ComfyBus.LatentImageBusNode", + "ComfyBus.LatentImageParameterBusNode", + "ComfyBus.PromptBusNode", + "ComfyBus.PromptSDXLBusNode" + ], + { + "title_aux": "comfyui-bus-plugin" + } + ], + "https://github.com/rhplus0831/ComfyMepi": [ + [ + "MepiCheckpoint", + "MepiImageSize", + "MepiNegativePrompt", + "MepiPositivePrompt", + "MepiSaveImage", + "MepiStepsAndCfg" + ], + { + "title_aux": "ComfyMepi" + } + ], + "https://github.com/richinsley/Comfy-LFO": [ + [ + "LFO_Pulse", + "LFO_Sawtooth", + "LFO_Sine", + "LFO_Square", + "LFO_Triangle" + ], + { + "title_aux": "Comfy-LFO" + } + ], + "https://github.com/ricklove/comfyui-ricklove": [ + [ + "RL_BBox", + "RL_CacheImageSequence", + "RL_CacheMaskSequence", + "RL_CivitaiTopImagePrompts", + "RL_Crop_Resize", + "RL_Crop_Resize_Batch", + "RL_Finetune_Analyze", + "RL_Finetune_Analyze_Batch", + "RL_Finetune_Variable", + "RL_ForceDependencyOrder", + "RL_ForceDependencyOrder_ImageString", + "RL_ForceDependencyOrder_Images", + "RL_ForceDependencyOrder_Latents", + "RL_ForceDependencyOrder_String", + "RL_ForceDependencyOrder_Strings", + "RL_IfFileExists", + "RL_Image_Shadow", + "RL_Image_Threshold_Channels", + "RL_Internet_Search", + "RL_LoadImageSequence", + "RL_Load_Flow", + "RL_LoraTextExtractTags", + "RL_Optical_Flow_Dip", + "RL_RebootComfyIfLeaky", + "RL_SaveImageSequence", + "RL_Save_Flow", + "RL_SequenceContext", + "RL_Sequence_ToFilePathList", + "RL_StopIfBlack", + "RL_Uncrop", + "RL_Warp_Image", + "RL_Zoe_Depth_Map_Preprocessor", + "RL_Zoe_Depth_Map_Preprocessor_Raw_Infer", + "RL_Zoe_Depth_Map_Preprocessor_Raw_Process" + ], + { + "title_aux": "comfyui-ricklove" + } + ], + "https://github.com/rickyars/comfyui-llm-tile": [ + [ + "TiledImageGenerator", + "TiledImageGeneratorAdvanced" + ], + { + "title_aux": "Tiled Image Generator for ComfyUI" + } + ], + "https://github.com/risunobushi/ComfyUI-Similarity-Score": [ + [ + "ImageSimilarityScores" + ], + { + "title_aux": "ComfyUI-Similarity-Score" + } + ], + "https://github.com/risunobushi/ComfyUI_DisplacementMapTools": [ + [ + "DisplaceLogo", + "ExtractDisplacementMap" + ], + { + "title_aux": "ComfyUI_DisplacementMapTools" + } + ], + "https://github.com/risunobushi/ComfyUI_sm4ll-Wrapper": [ + [ + "VTONAPINode", + "VTONAPIPaidNode" + ], + { + "title_aux": "ComfyUI_sm4ll-Wrapper" + } + ], + "https://github.com/risunobushi/comfyUI_FrequencySeparation_RGB-HSV": [ + [ + "FrequencyCombination", + "FrequencyCombinationHSV", + "FrequencySeparation", + "FrequencySeparationHSV" + ], + { + "title_aux": "comfyUI_FrequencySeparation_RGB-HSV" + } + ], + "https://github.com/rkfg/ComfyUI-Dia_tts": [ + [ + "DiaModelLoader", + "DiaSampler" + ], + { + "title_aux": "Dia realistic TTS" + } + ], + "https://github.com/rndnanthu/ComfyUI-RndNanthu": [ + [ + "AutoGradePro", + "ColorAnalysisPlotNode", + "ColorSpaceSim", + "ConvertToLogImage", + "FilmGrain", + "ProColorGrading", + "PromptGenerator" + ], + { + "title_aux": "ComfyUI-RndNanthu" + } + ], + "https://github.com/robertvoy/ComfyUI-Distributed": [ + [ + "DistributedCollector", + "DistributedSeed", + "ImageBatchDivider", + "UltimateSDUpscaleDistributed" + ], + { + "title_aux": "ComfyUI-Distributed" + } + ], + "https://github.com/robertvoy/ComfyUI-Flux-Continuum": [ + [ + "BatchSlider", + "BooleanToEnabled", + "CannySlider", + "ConfigurableDrawText", + "ConfigurableModelRouter", + "ControlNetSlider", + "DenoiseSlider", + "DrawTextConfig", + "FluxContinuumModelRouter", + "GPUSlider", + "GuidanceSlider", + "IPAdapterSlider", + "ImageBatchBoolean", + "ImpactControlBridgeFix", + "IntPass", + "LatentPass", + "MaxShiftSlider", + "OutputGetString", + "PipePass", + "ResolutionMultiplySlider", + "ResolutionPicker", + "SEGSPass", + "SamplerParameterPacker", + "SamplerParameterUnpacker", + "SelectFromBatch", + "SimpleTextTruncate", + "SplitVec2", + "SplitVec3", + "StepSlider", + "TextVersions" + ], + { + "title_aux": "ComfyUI Flux Continuum: Modular Interface" + } + ], + "https://github.com/robtl2/ComfyUI-ComfyBridge": [ + [ + "CB_ImageReceiver", + "CB_ImageSender" + ], + { + "title_aux": "ComfyUI-ComfyBridge" + } + ], + "https://github.com/rohitsainier/ComfyUI-InstagramDownloader": [ + [ + "InstagramDownloader", + "MediaOrganizer" + ], + { + "title_aux": "ComfyUI-InstagramDownloader" + } + ], + "https://github.com/romeobuilderotti/ComfyUI-PNG-Metadata": [ + [ + "SetMetadataAll", + "SetMetadataString" + ], + { + "title_aux": "ComfyUI PNG Metadata" + } + ], + "https://github.com/ronaldzgithub/ComfyUI_Appstore": [ + [ + "ComfyUIAppstoreHost", + "ComfyUIAppstoreParam", + "sdAppstore_saveImage" + ], + { + "title_aux": "ComfyUI_Appstore" + } + ], + "https://github.com/ronniebasak/ComfyUI-Tara-LLM-Integration": [ + [ + "TaraAdvancedComposition", + "TaraApiKeyLoader", + "TaraApiKeySaver", + "TaraDaisyChainNode", + "TaraLLMConfig", + "TaraPresetLLMConfig", + "TaraPrompter", + "TaraPrompterAdvanced" + ], + { + "title_aux": "ComfyUI-Tara-LLM-Integration" + } + ], + "https://github.com/ronsantash/Comfyui-flexi-lora-loader": [ + [ + "ComfyUIFlexiLoRALoader" + ], + { + "title_aux": "ComfyUIFlexiLoRALoader" + } + ], + "https://github.com/rookiepsi/comfypsi_blur_mask": [ + [ + "comfypsi_blur_mask" + ], + { + "title_aux": "Blur Mask" + } + ], + "https://github.com/rookiepsi/comfyui-extended": [ + [ + "ImageLiquify", + "ImageSwitch", + "MaskSwitch", + "PreviewBoolean", + "PreviewFloat", + "PreviewInteger", + "PreviewMask", + "PreviewText", + "PrimitiveBoolean", + "PrimitiveDimensions", + "PrimitiveFloat", + "PrimitiveInteger", + "PrimitiveText", + "UtilityExpression", + "UtilityImageDimensions", + "UtilitySwitch", + "rookiepsi_BlurMask", + "rookiepsi_ConstructMask", + "rookiepsi_CropImageToMask", + "rookiepsi_ResizeMask" + ], + { + "title_aux": "ComfyUI Extended" + } + ], + "https://github.com/roundyyy/ComfyUI-mesh-simplifier": [ + [ + "MeshSimplifierNode" + ], + { + "title_aux": "Mesh Simplifier for ComfyUI" + } + ], + "https://github.com/royceschultz/ComfyUI-Notifications": [ + [ + "Notif-PlaySound", + "Notif-SystemNotification", + "Notif-UnifiedNotification", + "Notif-Webhook" + ], + { + "title_aux": "ComfyUI-Notifications" + } + ], + "https://github.com/royceschultz/ComfyUI-TranscriptionTools": [ + [ + "TT-AudioSink", + "TT-ConvertVhsAudioToAudio", + "TT-LoadAudio", + "TT-LoadBatch", + "TT-LoadVideoAudio", + "TT-LoadWhisperModel", + "TT-WhisperTranscription", + "TT-WhisperTranscriptionBatch" + ], + { + "title_aux": "ComfyUI-TranscriptionTools" + } + ], + "https://github.com/rubi-du/ComfyUI-BiRefNet-Super": [ + [ + "BiRefNet_Lite", + "BiRefNet_Super", + "BiRefNet_onnx" + ], + { + "title_aux": "ComfyUI-BiRefNet-lite" + } + ], + "https://github.com/rubi-du/ComfyUI-Flux-Inpainting": [ + [ + "Flux Inpainting", + "FluxGuffInpainting", + "FluxInpainting", + "FluxSimpleInpainting", + "FluxTransformerInpainting", + "FluxVAELoader" + ], + { + "title_aux": "ComfyUI-Flux-Inpainting" + } + ], + "https://github.com/rubi-du/ComfyUI-ICC-nodes": [ + [ + "LoadImageICC", + "PreviewImageICC", + "SaveImageICC" + ], + { + "title_aux": "ComfyUI-ICC-nodes" + } + ], + "https://github.com/rui40000/RUI-Nodes": [ + [ + "ABCondition", + "CharacterCount" + ], + { + "title_aux": "RUI-Nodes" + } + ], + "https://github.com/ruiqutech/ComfyUI-RuiquNodes": [ + [ + "EvaluateListMultiple1", + "EvaluateListMultiple3", + "EvaluateListMultiple6", + "EvaluateListMultiple9", + "EvaluateMultiple1", + "EvaluateMultiple3", + "EvaluateMultiple6", + "EvaluateMultiple9", + "ImageDilate", + "ImageErode", + "ListPath", + "MaskDilate", + "MaskErode", + "PreviewMask", + "RangeSplit", + "SaveMask", + "StringAsAny", + "StringConcat1", + "StringConcat3", + "StringConcat6", + "StringConcat9", + "StringPathStem", + "TermsToList", + "VAEDecodeSave" + ], + { + "title_aux": "RuiquNodes for ComfyUI" + } + ], + "https://github.com/runtime44/comfyui_r44_nodes": [ + [ + "Runtime44ColorMatch", + "Runtime44DynamicKSampler", + "Runtime44FilmGrain", + "Runtime44ImageEnhance", + "Runtime44ImageOverlay", + "Runtime44ImageResizer", + "Runtime44ImageToNoise", + "Runtime44IterativeUpscaleFactor", + "Runtime44MaskSampler", + "Runtime44TiledMaskSampler", + "Runtime44Upscaler" + ], + { + "title_aux": "Runtime44 ComfyUI Nodes" + } + ], + "https://github.com/ruucm/ruucm-comfy": [ + [ + "BatchAverageImage", + "LoadExternalLoraModelOnly", + "RuucmShareScreen" + ], + { + "nodename_pattern": " \\(ruucm\\)$", + "title_aux": "Ruucm's ComfyUI Nodes" + } + ], + "https://github.com/ryanontheinside/ComfyUI-DeepLiveCam": [ + [ + "DeepLiveCamNode" + ], + { + "title_aux": "Deep Live Cam for ComfyUI" + } + ], + "https://github.com/ryanontheinside/ComfyUI_ControlFreak": [ + [ + "ControlFreak" + ], + { + "title_aux": "Control Freak for ComfyUI" + } + ], + "https://github.com/ryanontheinside/ComfyUI_Doom": [ + [ + "Doom" + ], + { + "title_aux": "Doom" + } + ], + "https://github.com/ryanontheinside/ComfyUI_EfficientTAM": [ + [ + "EfficientTAMLoader", + "EfficientTAMPredictor" + ], + { + "title_aux": "ComfyUI-EfficientTAM" + } + ], + "https://github.com/ryanontheinside/ComfyUI_ProfilerX": [ + [ + "CATEGORY", + "ExecutionTracker", + "FUNCTION", + "INPUT_TYPES", + "OUTPUT_NODE", + "ProfilerX", + "RETURN_TYPES" + ], + { + "title_aux": "ComfyUI_ProfilerX" + } + ], + "https://github.com/ryanontheinside/ComfyUI_RealtimeNodes": [ + [ + "BlendshapeControlFloat", + "BlendshapeControlInt", + "BlendshapeTrigger", + "CompareMediaPipeEmbeddings", + "CoordinateConverter", + "FaceDetectionToMask", + "FaceLandmarkPosition", + "FaceTextureWarp", + "HandLandmarkPosition", + "HeadPoseControlFloat", + "HeadPoseControlInt", + "HeadPoseTrigger", + "MaskFromFaceLandmarks", + "MaskFromHandLandmarks", + "MaskFromPoseLandmarks", + "MediaPipeFaceDetector", + "MediaPipeFaceDetectorModelLoader", + "MediaPipeFaceLandmarker", + "MediaPipeFaceLandmarkerModelLoader", + "MediaPipeFaceStylizer", + "MediaPipeFaceStylizerModelLoader", + "MediaPipeGestureRecognizer", + "MediaPipeGestureRecognizerModelLoader", + "MediaPipeHandLandmarker", + "MediaPipeHandLandmarkerModelLoader", + "MediaPipeHolisticLandmarker", + "MediaPipeHolisticVisualizer", + "MediaPipeImageEmbedder", + "MediaPipeImageEmbedderModelLoader", + "MediaPipeImageSegmenter", + "MediaPipeImageSegmenterModelLoader", + "MediaPipeInteractiveSegmenter", + "MediaPipeInteractiveSegmenterModelLoader", + "MediaPipeObjectDetector", + "MediaPipeObjectDetectorModelLoader", + "MediaPipePoseLandmarker", + "MediaPipePoseLandmarkerModelLoader", + "Point2D", + "PointList", + "PoseLandmarkPosition", + "RTCoordinateConverter", + "RTDrawLines", + "RTDrawPoints", + "RTDrawPolygon", + "ReshapeMediaPipeEmbedding", + "SelectMediaPipeSegment", + "VisualizeFaceDetections", + "VisualizeFaceLandmarks", + "VisualizeGestureRecognitions", + "VisualizeHandLandmarks", + "VisualizeObjectDetections", + "VisualizePoseLandmarks" + ], + { + "title_aux": "Nodes for use with real-time applications of ComfyUI" + } + ], + "https://github.com/ryanontheinside/ComfyUI_RyanOnTheInside": [ + [ + "ACEStepAnalyzeLatent", + "ACEStepAudioPostProcessor", + "ACEStepExtendGuider", + "ACEStepHybridGuider", + "ACEStepMaskVisualizer", + "ACEStepRepaintGuider", + "ACEStepTimeRange", + "AdvancedLuminanceMask", + "AnimatedFeaturePreview", + "AreaFeatureNode", + "AudioChannelMerge", + "AudioChannelSplit", + "AudioDither", + "AudioFade", + "AudioFeatureExtractor", + "AudioFeatureVisualizer", + "AudioFilter", + "AudioGain", + "AudioInfo", + "AudioLatentBlend", + "AudioLatentInfo", + "AudioMaskAnalyzer", + "AudioPad", + "AudioPitchShift", + "AudioRegionMask", + "AudioResample", + "AudioSeparatorSimple", + "AudioSubtract", + "AudioTemporalMask", + "AudioTimeStretch", + "AudioTrim", + "AudioVolumeNormalization", + "Audio_Combine", + "Audio_Concatenate", + "BrightnessFeatureNode", + "ColorFeatureNode", + "Color_Picker", + "ContextModifier", + "DepthBlender", + "DepthFeatureNode", + "DepthInjection", + "DepthMapProtrusion", + "DepthRippleEffect", + "DepthShapeModifier", + "DepthShapeModifierPrecise", + "Doom_", + "DownloadCREPEModel", + "DownloadOpenUnmixModel", + "DrawableFeatureNode", + "DyeImage", + "EffectVisualizer", + "EmbeddingGuidedLatentInterpolate", + "EmitterEmissionRateModulation", + "EmitterMovement", + "EmptyImageAndMaskFromAudio", + "EmptyImageFromAudio", + "EmptyMaskFromAudio", + "FeatureAccumulate", + "FeatureCombine", + "FeatureContiguousInterpolate", + "FeatureFade", + "FeatureInfoNode", + "FeatureInterpolateMulti", + "FeatureInterpolator", + "FeatureMath", + "FeatureMixer", + "FeatureOscillator", + "FeaturePeakDetector", + "FeatureRebase", + "FeatureRenormalize", + "FeatureScaler", + "FeatureSmoothing", + "FeatureToFilteredList", + "FeatureToFlexFloatParam", + "FeatureToFlexIntParam", + "FeatureToFloat", + "FeatureToLatentKeyframe", + "FeatureToMask", + "FeatureToSplineData", + "FeatureToTimestepKeyframe", + "FeatureToWeightsStrategy", + "FeatureTruncateOrExtend", + "FlexAudioPitchShift", + "FlexAudioTimeStretch", + "FlexAudioVisualizerCircular", + "FlexAudioVisualizerContour", + "FlexAudioVisualizerLine", + "FlexFeatureAttentionControl", + "FlexImageBloom", + "FlexImageChromaticAberration", + "FlexImageColorGrade", + "FlexImageContrast", + "FlexImageDepthWarp", + "FlexImageEdgeDetect", + "FlexImageGlitch", + "FlexImageHorizontalToVertical", + "FlexImageHueShift", + "FlexImageKaleidoscope", + "FlexImageParallax", + "FlexImagePixelate", + "FlexImagePosterize", + "FlexImageTiltShift", + "FlexImageTransform", + "FlexImageVignette", + "FlexImageWarp", + "FlexLatentBlend", + "FlexLatentInterpolate", + "FlexLatentNoise", + "FlexMaskBinary", + "FlexMaskDepthChamber", + "FlexMaskEmanatingRings", + "FlexMaskInterpolate", + "FlexMaskMath", + "FlexMaskMorph", + "FlexMaskOpacity", + "FlexMaskRandomShapes", + "FlexMaskTransform", + "FlexMaskVoronoiScheduled", + "FlexMaskWarp", + "FlexMaskWavePropagation", + "FlexVideoDirection", + "FlexVideoFrameBlend", + "FlexVideoSeek", + "FlexVideoSpeed", + "FlexlatentAudioBlend", + "FloatFeatureNode", + "FrequencyFilterCustom", + "FrequencyFilterPreset", + "FrequencyRange", + "GravityWell", + "ImageCASBatch", + "ImageChunk", + "ImageDifference", + "ImageIndexSelect", + "ImageInterval", + "ImageIntervalSelectPercentage", + "ImageScaleToTarget", + "Image_Shuffle", + "Knob", + "LatentFrequencyBlender", + "LocationFromMask", + "LocationFromPoint", + "LocationTransform", + "MIDIFeatureExtractor", + "MIDILoader", + "MIDIToAudio", + "ManualFeatureFromPipe", + "ManualFeatureNode", + "ManualFeaturePipe", + "ManualWhisperAlignmentData", + "MaskCompositePlus", + "MaskMath", + "MaskMorph", + "MaskRings", + "MaskToAudioMask", + "MaskTransform", + "MaskWarp", + "MotionFeatureNode", + "MovingShape", + "OpticalFlowDirectionMask", + "OpticalFlowMaskModulation", + "OpticalFlowParticleSystem", + "ParticleColorModulation", + "ParticleEmissionMask", + "ParticleEmitter", + "ParticleSizeModulation", + "ParticleSpeedModulation", + "PitchFeatureExtractor", + "PitchRange", + "PitchRangeByNoteNode", + "PitchRangePreset", + "PitchVisualizer", + "PoseInterpolator", + "PreviewFeature", + "ProximityFeatureNode", + "ProximityVisualizer", + "RhythmFeatureExtractor", + "SplineFeatureModulator", + "SplineRhythmModulator", + "SpringJointSetting", + "StaticBody", + "SwapDevice", + "TextMaskNode", + "TimeFeatureNode", + "TranslucentComposite", + "TriggerBuilder", + "VideoChunk", + "Vortex", + "WhisperAutoAdjust", + "WhisperFeature", + "WhisperTextRenderer", + "WhisperTimeAdjuster", + "WhisperToPromptTravel", + "_mfc" + ], + { + "title_aux": "RyanOnTheInside" + } + ], + "https://github.com/ryanontheinside/ComfyUI_SuperResolution": [ + [ + "SuperResolutionModelLoader", + "SuperResolutionUpscale" + ], + { + "title_aux": "ComfyUI_SuperResolution" + } + ], + "https://github.com/s9roll7/comfyui_cotracker_node": [ + [ + "CoTrackerNode", + "GridPointGeneratorNode", + "PerlinCoordinateRandomizerNode", + "XYMotionAmplifierNode" + ], + { + "title_aux": "Comfyui CoTracker Node" + } + ], + "https://github.com/saftle/uber_comfy_nodes": [ + [ + "ControlNet Selector", + "ControlNetOptionalLoader", + "DiffusersSelector", + "MultiInputVariableRewrite", + "SaveImageJPGNoMeta" + ], + { + "title_aux": "Suplex Misc ComfyUI Nodes" + } + ], + "https://github.com/sakura1bgx/ComfyUI_FlipStreamViewer": [ + [ + "FlipStreamBatchPrompt", + "FlipStreamChat", + "FlipStreamFileSelect_AnimateDiffModel", + "FlipStreamFileSelect_Checkpoints", + "FlipStreamFileSelect_ControlNetModel", + "FlipStreamFileSelect_Input", + "FlipStreamFileSelect_Output", + "FlipStreamFileSelect_TensorRT", + "FlipStreamFileSelect_VAE", + "FlipStreamFilmVfi", + "FlipStreamGate", + "FlipStreamGetParam", + "FlipStreamGetPreviewRoi", + "FlipStreamImageSize", + "FlipStreamInputBox", + "FlipStreamLogBox", + "FlipStreamParseJson", + "FlipStreamPreviewBox", + "FlipStreamRembg", + "FlipStreamScreenGrabber", + "FlipStreamSection", + "FlipStreamSegMask", + "FlipStreamSelectBox_Samplers", + "FlipStreamSelectBox_Scheduler", + "FlipStreamSetMessage", + "FlipStreamSetParam", + "FlipStreamSetUpdateAndReload", + "FlipStreamSlider", + "FlipStreamSource", + "FlipStreamSwitch", + "FlipStreamSwitchImage", + "FlipStreamSwitchLatent", + "FlipStreamTextBox", + "FlipStreamTextReplace", + "FlipStreamVideoInput", + "FlipStreamViewer" + ], + { + "title_aux": "ComfyUI_FlipStreamViewer" + } + ], + "https://github.com/sanbuphy/ComfyUI-AudioLDM": [ + [ + "AudioLDM", + "PreviewAudioLDM", + "SaveAudioLDM" + ], + { + "title_aux": "ComfyUI-AudioLDM" + } + ], + "https://github.com/santiagosamuel3455/ComfyUI-GeminiImageToPrompt": [ + [ + "DeepseekR1KlingAINode", + "GeminiImageToPromptNode", + "GeminiTextToCinematicPromptNode", + "ShowGeneratedText", + "ShowTextNode" + ], + { + "title_aux": "ComfyUI-GeminiImageToPrompt" + } + ], + "https://github.com/scraed/LanPaint": [ + [ + "LanPaint_KSampler", + "LanPaint_KSamplerAdvanced", + "LanPaint_MaskBlend", + "LanPaint_SamplerCustom", + "LanPaint_SamplerCustomAdvanced" + ], + { + "title_aux": "LanPaint" + } + ], + "https://github.com/sdfxai/SDFXBridgeForComfyUI": [ + [ + "SDFXClipTextEncode" + ], + { + "title_aux": "SDFXBridgeForComfyUI - ComfyUI Custom Node for SDFX Integration" + } + ], + "https://github.com/sdtana/ComfyUI-FDG": [ + [ + "FDGNode" + ], + { + "title_aux": "ComfyUI-FDG" + } + ], + "https://github.com/seanjang990/comfyui-document-auto-crop": [ + [ + "CropRotateNode" + ], + { + "title_aux": "ComfyUI Document Auto Crop Node" + } + ], + "https://github.com/seanlynch/comfyui-optical-flow": [ + [ + "Apply optical flow", + "Compute optical flow", + "Visualize optical flow" + ], + { + "title_aux": "ComfyUI Optical Flow" + } + ], + "https://github.com/seanlynch/srl-nodes": [ + [ + "SRL Conditional Interrrupt", + "SRL Eval", + "SRL Filter Image List", + "SRL Format String" + ], + { + "title_aux": "SRL's nodes" + } + ], + "https://github.com/sebord/ComfyUI-LMCQ": [ + [ + "LmcqCodeDecryptionLoader", + "LmcqCodeEncryption", + "LmcqDeepGen", + "LmcqDeepLoader", + "LmcqGetMachineCode", + "LmcqImageSaver", + "LmcqImageSaverTransit", + "LmcqImageSaverWeb", + "LmcqInputValidator", + "LmcqLoadFluxNF4Checkpoint", + "LmcqRuntimeLoraDecryption", + "LmcqRuntimeLoraEncryption", + "LmcqRuntimeModelDecryption", + "LmcqRuntimeModelEncryption", + "LmcqRuntimeWorkflowDecryption", + "LmcqRuntimeWorkflowEncryption" + ], + { + "title_aux": "ComfyUI-LMCQ" + } + ], + "https://github.com/sergekatzmann/ComfyUI_Nimbus-Pack": [ + [ + "AdjustAndRoundDimensions", + "AspectRatioMobileDevices", + "ImageResizeAndCropNode", + "ImageSquareAdapterNode", + "PopularScreenResolutions" + ], + { + "title_aux": "ComfyUI_Nimbus-Pack" + } + ], + "https://github.com/sh570655308/ComfyUI-GigapixelAI": [ + [ + "GigapixelAI", + "GigapixelModelSettings", + "GigapixelUpscaleSettings" + ], + { + "title_aux": "ComfyUI-GigapixelAI" + } + ], + "https://github.com/sh570655308/ComfyUI-TopazVideoAI": [ + [ + "TopazUpscaleParams", + "TopazVideoAI" + ], + { + "title_aux": "ComfyUI-TopazVideoAI" + } + ], + "https://github.com/shabri-arrahim/ComfyUI-Safety-Checker": [ + [ + "CompVisSafetyChecker", + "FalconsAISafetyChecker", + "loadImageBase64" + ], + { + "title_aux": "ComfyUI Safety Checker" + } + ], + "https://github.com/shadowcz007/comfyui-Image-reward": [ + [ + "ImageBatchToList_", + "ImageRewardScore_" + ], + { + "title_aux": "comfyui-Image-reward" + } + ], + "https://github.com/shadowcz007/comfyui-consistency-decoder": [ + [ + "VAEDecodeConsistencyDecoder", + "VAELoaderConsistencyDecoder" + ], + { + "title_aux": "Consistency Decoder" + } + ], + "https://github.com/shadowcz007/comfyui-edit-mask": [ + [ + "EditMask" + ], + { + "title_aux": "comfyui-edit-mask" + } + ], + "https://github.com/shadowcz007/comfyui-liveportrait": [ + [ + "ExpressionEditor_", + "ExpressionVideo2VideoNode", + "ExpressionVideoNode", + "FaceCropInfo", + "LivePortraitNode", + "LivePortraitVideoNode", + "Retargeting" + ], + { + "title_aux": "comfyui-liveportrait" + } + ], + "https://github.com/shadowcz007/comfyui-mixlab-nodes": [ + [ + "3DImage", + "AnalyzeAudio", + "AppInfo", + "ApplyVisualStylePrompting_", + "AreaToMask", + "AudioPlay", + "CenterImage", + "CkptNames_", + "Color", + "ComparingTwoFrames_", + "CompositeImages_", + "CreateJsonNode", + "DepthViewer", + "DynamicDelayProcessor", + "EmbeddingPrompt", + "EnhanceImage", + "FaceToMask", + "FeatheredMask", + "FloatSlider", + "FloatingVideo", + "Font", + "GLIGENTextBoxApply_Advanced", + "GetImageSize_", + "GradientImage", + "GridDisplayAndSave", + "GridInput", + "GridOutput", + "ImageBatchToList_", + "ImageColorTransfer", + "ImageCropByAlpha", + "ImageListToBatch_", + "ImagesPrompt_", + "IncrementingListNode_", + "IntNumber", + "JoinWithDelimiter", + "KeyInput", + "LimitNumber", + "ListSplit_", + "LoadImagesFromPath", + "LoadImagesFromURL", + "LoadImagesToBatch", + "LoraNames_", + "LoraPrompt", + "MaskListMerge_", + "MaskListReplace_", + "MergeLayers", + "MirroredImage", + "MultiplicationNode", + "NewLayer", + "NoiseImage", + "OutlineMask", + "P5Input", + "PreviewMask_", + "PromptImage", + "PromptSimplification", + "PromptSlide", + "RandomPrompt", + "ResizeImageMixlab", + "SamplerNames_", + "SaveImageAndMetadata_", + "SaveImageToLocal", + "ScreenShare", + "Seed_", + "ShowLayer", + "SmoothMask", + "SpeechRecognition", + "SpeechSynthesis", + "SplitImage", + "SplitLongMask", + "StyleAlignedBatchAlign_", + "StyleAlignedReferenceSampler_", + "StyleAlignedSampleReferenceLatents_", + "SvgImage", + "SwitchByIndex", + "TESTNODE_", + "TESTNODE_TOKEN", + "TextImage", + "TextInput_", + "TextToNumber", + "TransparentImage", + "VAEDecodeConsistencyDecoder", + "VAELoaderConsistencyDecoder" + ], + { + "title_aux": "comfyui-mixlab-nodes" + } + ], + "https://github.com/shadowcz007/comfyui-sound-lab": [ + [ + "AudioPlay", + "Musicgen_", + "StableAudio_" + ], + { + "title_aux": "comfyui-sound-lab" + } + ], + "https://github.com/shadowcz007/comfyui-try-on": [ + [ + "CatVTONNode", + "FashionClothMask", + "FashionClothMask2" + ], + { + "author": "chflame", + "description": "CatVTON warpper for ComfyUI", + "nickname": "CatVTON_Wrapper", + "title": "CatVTON_Wrapper", + "title_aux": "comfyui-try-on" + } + ], + "https://github.com/shadowcz007/comfyui-ultralytics-yolo": [ + [ + "DetectByLabel" + ], + { + "title_aux": "comfyui-ultralytics-yolo" + } + ], + "https://github.com/shahkoorosh/ComfyUI-KGnodes": [ + [ + "CustomResolutionLatentNode", + "FaceDetectorAndCropper", + "ImageScaleToSide", + "OverlayRGBAonRGB", + "StyleSelector", + "TextBehindImage" + ], + { + "author": "ShahKoorosh", + "description": "This Custom node pack offers various nodes to make it easier to use ComfyUI.", + "nickname": "KGnodes", + "title": "ComfyUI-KGnodes", + "title_aux": "ComfyUI-KGnodes" + } + ], + "https://github.com/shahkoorosh/ComfyUI-PersianText": [ + [ + "PersianText" + ], + { + "author": "shahkoorosh", + "description": "A powerful ComfyUI node for rendering text with advanced styling options, including full support for Persian/Farsi and Arabic scripts.", + "nickname": "PersianText", + "title": "ComfyUI-PersianText", + "title_aux": "ComfyUI-PersianText" + } + ], + "https://github.com/shenduldh/ComfyUI-Lightning": [ + [ + "ApplyFBCacheAndSkipBlocks", + "ApplyMBCacheAndSkipBlocks", + "ApplyMBCacheAndSkipBlocksForSana", + "ApplySageAttention", + "ApplySpargeAttn", + "ApplyTeaCacheAndSkipBlocks", + "ApplyToCa", + "ApplyTokenMerging", + "CompileAndQuantizeModel", + "SanaCLIPLoader", + "SanaDiffusionLoader", + "SanaEmptyLatentImage", + "SanaTextEncode", + "SanaVAELoader", + "SaveSpargeAttnHyperparams" + ], + { + "title_aux": "ComfyUI-Lightning" + } + ], + "https://github.com/shi3z/ComfyUI_Memeplex_DALLE": [ + [ + "DallERender", + "GPT", + "MemeplexCustomSDXLRender", + "MemeplexRender", + "TextInput", + "TextSend" + ], + { + "title_aux": "ComfyUI_Memeplex_DALLE" + } + ], + "https://github.com/shiertier/ComfyUI-TeaCache-lumina2": [ + [ + "TeaCacheForLumina2", + "TeaCacheForLuminaAuto", + "TeaCacheForLuminaNext" + ], + { + "title_aux": "ComfyUI-TeaCache-Lumina" + } + ], + "https://github.com/shiimizu/ComfyUI-PhotoMaker-Plus": [ + [ + "PhotoMakerEncodePlus", + "PhotoMakerInsightFaceLoader", + "PhotoMakerLoaderPlus", + "PhotoMakerLoraLoaderPlus", + "PhotoMakerStyles", + "PrepImagesForClipVisionFromPath" + ], + { + "title_aux": "ComfyUI PhotoMaker Plus" + } + ], + "https://github.com/shiimizu/ComfyUI-TiledDiffusion": [ + [ + "NoiseInversion", + "SpotDiffusionParams_TiledDiffusion", + "TiledDiffusion", + "VAEDecodeTiled_TiledDiffusion", + "VAEEncodeTiled_TiledDiffusion" + ], + { + "title_aux": "Tiled Diffusion & VAE for ComfyUI" + } + ], + "https://github.com/shiimizu/ComfyUI-semantic-aware-guidance": [ + [ + "SemanticAwareGuidance" + ], + { + "title_aux": "Semantic-aware Guidance (S-CFG)" + } + ], + "https://github.com/shiimizu/ComfyUI_smZNodes": [ + [ + "smZ CLIPTextEncode", + "smZ Settings" + ], + { + "title_aux": "smZNodes" + } + ], + "https://github.com/shingo1228/ComfyUI-SDXL-EmptyLatentImage": [ + [ + "SDXL Empty Latent Image" + ], + { + "title_aux": "ComfyUI-SDXL-EmptyLatentImage" + } + ], + "https://github.com/shingo1228/ComfyUI-send-eagle-slim": [ + [ + "Send Image to Eagle" + ], + { + "title_aux": "ComfyUI-send-Eagle(slim)" + } + ], + "https://github.com/shinich39/comfyui-break-workflow": [ + [ + "BreakWorkflow" + ], + { + "author": "shinich39", + "description": "Break the execution, save the incompleted image then continue later.", + "nickname": "comfyui-break-workflow", + "title": "comfyui-break-workflow", + "title_aux": "comfyui-break-workflow" + } + ], + "https://github.com/shinich39/comfyui-dynamic-routes": [ + [ + "DynamicRoutes" + ], + { + "author": "shinich39", + "description": "Shuffle nodes after queue execution.", + "nickname": "comfyui-dynamic-routes", + "title": "comfyui-dynamic-routes", + "title_aux": "comfyui-dynamic-routes" + } + ], + "https://github.com/shinich39/comfyui-get-meta": [ + [ + "GetBooleanFromImage", + "GetComboFromImage", + "GetFloatFromImage", + "GetIntFromImage", + "GetNodesFromImage", + "GetPromptFromImage", + "GetStringFromImage", + "GetWorkflowFromImage" + ], + { + "author": "shinich39", + "description": "Get metadata from image.", + "nickname": "comfyui-get-meta", + "title": "comfyui-get-meta", + "title_aux": "comfyui-get-meta" + } + ], + "https://github.com/shinich39/comfyui-no-one-above-me": [ + [ + "NoOneAboveMe" + ], + { + "author": "shinich39", + "description": "Fix node to top.", + "nickname": "comfyui-no-one-above-me", + "title": "comfyui-no-one-above-me", + "title_aux": "comfyui-no-one-above-me" + } + ], + "https://github.com/shobhitic/ComfyUI-PlusMinusTextClip": [ + [ + "PlusMinusTextClip" + ], + { + "title_aux": "PlusMinusTextClip - Single node for Positive and Negative Prompts" + } + ], + "https://github.com/shockz0rz/comfy-easy-grids": [ + [ + "FloatToText", + "GridFloatList", + "GridFloats", + "GridIntList", + "GridInts", + "GridLoras", + "GridStringList", + "GridStrings", + "ImageGridCommander", + "IntToText", + "SaveImageGrid", + "TextConcatenator" + ], + { + "title_aux": "comfy-easy-grids" + } + ], + "https://github.com/silveroxides/ComfyUI-ModelUtils": [ + [ + "CLIPMetaKeys", + "CheckpointMetaKeys", + "LoRAMetaKeys", + "UNetMetaKeys" + ], + { + "title_aux": "Model Utility Toolkit" + } + ], + "https://github.com/silveroxides/ComfyUI_EmbeddingToolkit": [ + [ + "SaveA1111WeightedEmbeddings", + "SaveTokenEmbeddings", + "SaveWeightedEmbeddings", + "SliceExistingEmbedding" + ], + { + "title_aux": "ComfyUI_EmbeddingToolkit" + } + ], + "https://github.com/silveroxides/ComfyUI_FDGuidance": [ + [ + "FrequencyDecoupledGuidance" + ], + { + "title_aux": "ComfyUI_FDGuidance" + } + ], + "https://github.com/silveroxides/ComfyUI_PowerShiftScheduler": [ + [ + "PowerShiftScheduler" + ], + { + "title_aux": "ComfyUI Power Shift Scheduler" + } + ], + "https://github.com/silveroxides/ComfyUI_SigmoidOffsetScheduler": [ + [ + "SigmoidOffsetScheduler" + ], + { + "title_aux": "ComfyUI Sigmoid Offset Scheduler" + } + ], + "https://github.com/sipherxyz/comfyui-art-venture": [ + [ + "AV_AwsBedrockClaudeApi", + "AV_AwsBedrockMistralApi", + "AV_CheckpointMerge", + "AV_CheckpointModelsToParametersPipe", + "AV_CheckpointSave", + "AV_ClaudeApi", + "AV_ControlNetEfficientLoader", + "AV_ControlNetEfficientLoaderAdvanced", + "AV_ControlNetEfficientStacker", + "AV_ControlNetEfficientStackerSimple", + "AV_ControlNetLoader", + "AV_ControlNetPreprocessor", + "AV_LLMApiConfig", + "AV_LLMChat", + "AV_LLMCompletion", + "AV_LLMMessage", + "AV_LoraListLoader", + "AV_LoraListStacker", + "AV_LoraLoader", + "AV_OpenAIApi", + "AV_ParametersPipeToCheckpointModels", + "AV_ParametersPipeToPrompts", + "AV_PromptsToParametersPipe", + "AV_SAMLoader", + "AV_VAELoader", + "AspectRatioSelector", + "BLIPCaption", + "BLIPLoader", + "BooleanPrimitive", + "CheckpointNameSelector", + "ColorBlend", + "ColorCorrect", + "DeepDanbooruCaption", + "DependenciesEdit", + "DownloadAndLoadBlip", + "DownloadISNetModel", + "Fooocus_KSampler", + "Fooocus_KSamplerAdvanced", + "GetBoolFromJson", + "GetFloatFromJson", + "GetIntFromJson", + "GetObjectFromJson", + "GetSAMEmbedding", + "GetTextFromJson", + "ISNetLoader", + "ISNetSegment", + "ImageAlphaComposite", + "ImageApplyChannel", + "ImageExtractChannel", + "ImageGaussianBlur", + "ImageMuxer", + "ImageRepeat", + "ImageScaleDown", + "ImageScaleDownBy", + "ImageScaleDownToSize", + "ImageScaleToMegapixels", + "LaMaInpaint", + "LoadImageAsMaskFromUrl", + "LoadImageFromUrl", + "LoadJsonFromText", + "LoadJsonFromUrl", + "LoadLaMaModel", + "MergeModels", + "NumberScaler", + "OverlayInpaintedImage", + "OverlayInpaintedLatent", + "PrepareImageAndMaskForInpaint", + "QRCodeGenerator", + "RandomFloat", + "RandomInt", + "SAMEmbeddingToImage", + "SDXLAspectRatioSelector", + "SDXLPromptStyler", + "SeedSelector", + "StringToInt", + "StringToNumber", + "TextRandomMultiline", + "TextSwitchCase" + ], + { + "title_aux": "comfyui-art-venture" + } + ], + "https://github.com/sipie800/ComfyUI-PuLID-Flux-Enhanced": [ + [ + "ApplyPulidFlux", + "PulidFluxEvaClipLoader", + "PulidFluxInsightFaceLoader", + "PulidFluxModelLoader" + ], + { + "title_aux": "ComfyUI-PuLID-Flux-Enhanced" + } + ], + "https://github.com/sittere/ComfyUI-YK_Line-loading": [ + [ + "MultiTextLoader" + ], + { + "title_aux": "ComfyUI-YK Line loading" + } + ], + "https://github.com/sjh00/ComfyUI-LoadImageWithInfo": [ + [ + "LoadImageWithInfo", + "SaveImageWithInfo" + ], + { + "title_aux": "ComfyUI LoadImageWithInfo" + } + ], + "https://github.com/skfoo/ComfyUI-Coziness": [ + [ + "LoraTextExtractor-b1f83aa2", + "MultiLoraLoader-70bf3d77" + ], + { + "title_aux": "ComfyUI-Coziness" + } + ], + "https://github.com/skycoder182/comfyui-filename-tools": [ + [ + "ExtractAndTrimFilename", + "LoadImageWithFilename" + ], + { + "title_aux": "Filename Tools" + } + ], + "https://github.com/skycoder182/comfyui-skycoder-tools": [ + [ + "Aspect_Ratio_and_Tile_size_calculator", + "BLIP2Captioning", + "BooleanToggle", + "ConcatenateAndTestIfEmpty", + "DirectoryImageInfo", + "DirectoryImageLoader", + "ImageBasicNode" + ], + { + "title_aux": "Skycoder Tools" + } + ], + "https://github.com/slvslvslv/ComfyUI-SmartHelperNodes": [ + [ + "SmartFormatString", + "SmartFormatString10", + "SmartHVLoraSelect", + "SmartHVLoraStack", + "SmartLoadLoRA", + "SmartModelOrLoraToString", + "SmartPrompt", + "SmartRemoveComments", + "SmartSaveText", + "SmartShowAnything" + ], + { + "title_aux": "ComfyUI Smart Helper Nodes" + } + ], + "https://github.com/slvslvslv/ComfyUI-SmartImageTools": [ + [ + "SmartBackgroundRemove", + "SmartGenerateImage", + "SmartImagePaletteConvert", + "SmartImagePaletteExtract", + "SmartImagePoint", + "SmartImagePreviewScaled", + "SmartImageRegion", + "SmartImagesProcessor", + "SmartPoint", + "SmartPointSet", + "SmartPointSetMerge", + "SmartPreviewPalette", + "SmartSaveAnimatedPNG", + "SmartSavePNG", + "SmartSemiTransparenceRemove", + "SmartVideoPreviewScaled" + ], + { + "title_aux": "ComfyUI-SmartImageTools" + } + ], + "https://github.com/slyt/comfyui-ollama-nodes": [ + [ + "BooleanToString", + "DownloadHuggingfaceModel", + "FloatToString", + "GenerateOllama", + "IntToString", + "ListModels", + "ListToString", + "PullModel" + ], + { + "title_aux": "comfyui-ollama-nodes" + } + ], + "https://github.com/sm079/ComfyUI-Face-Detection": [ + [ + "FaceCombine", + "FaceDetection" + ], + { + "title_aux": "ComfyUI-Face-Detection" + } + ], + "https://github.com/smagnetize/kb-comfyui-nodes": [ + [ + "SingleImageDataUrlLoader" + ], + { + "title_aux": "kb-comfyui-nodes" + } + ], + "https://github.com/smlbiobot/ComfyUI-Flux-Replicate-API": [ + [ + "SML_FluxProUltra_Replicate_Standalone", + "SML_FluxPro_Replicate_Standalone" + ], + { + "title_aux": "ComfyUI-Flux-Replicate-API" + } + ], + "https://github.com/smlbiobot/sml-comfyui-prompt-expansion": [ + [ + "SML_Prompt_Generator" + ], + { + "title_aux": "sml-comfyui-prompt-expansion" + } + ], + "https://github.com/smthemex/ComfyUI_AniCrafter": [ + [ + "AniCrafterLoader", + "AniCrafterPreImage", + "AniCrafterPreText", + "AniCrafterPreVideo", + "AniCrafterSampler" + ], + { + "title_aux": "ComfyUI_AniCrafter" + } + ], + "https://github.com/smthemex/ComfyUI_AnyDoor": [ + [ + "AnyDoor_LoadModel", + "AnyDoor_img2img" + ], + { + "title_aux": "ComfyUI_AnyDoor" + } + ], + "https://github.com/smthemex/ComfyUI_CSD_MT": [ + [ + "CSDMTLoader", + "CSDMTSampler" + ], + { + "title_aux": "ComfyUI_CSD_MT" + } + ], + "https://github.com/smthemex/ComfyUI_CSGO_Wrapper": [ + [ + "Blip_Loader", + "CSGO_Loader", + "CSGO_Sampler" + ], + { + "title_aux": "ComfyUI_CSGO_Wrapper" + } + ], + "https://github.com/smthemex/ComfyUI_ChatGLM_API": [ + [ + "Glm_4_9b_Chat", + "Glm_4v_9b", + "Glm_Lcoal_Or_Repo", + "ZhipuaiApi_Character", + "ZhipuaiApi_Txt", + "ZhipuaiApi_img" + ], + { + "title_aux": "ComfyUI_ChatGLM_API" + } + ], + "https://github.com/smthemex/ComfyUI_CustomNet": [ + [ + "CustomNet_LoadModel", + "CustomNet_Sampler" + ], + { + "title_aux": "ComfyUI_CustomNet" + } + ], + "https://github.com/smthemex/ComfyUI_DICE_Talk": [ + [ + "Dice_Talk_Loader", + "Dice_Talk_PreData", + "Dice_Talk_Sampler" + ], + { + "title_aux": "ComfyUI_DICE_Talk" + } + ], + "https://github.com/smthemex/ComfyUI_DeepFakeDefenders": [ + [ + "DeepFakeDefender_Loader", + "DeepFakeDefender_Sampler" + ], + { + "title_aux": "ComfyUI_DeepFakeDefenders" + } + ], + "https://github.com/smthemex/ComfyUI_Demucs": [ + [ + "Demucs_Loader", + "Demucs_Sampler" + ], + { + "title_aux": "ComfyUI_Demucs" + } + ], + "https://github.com/smthemex/ComfyUI_Diffree": [ + [ + "Diffree_Model_Loader", + "Diffree_Sampler" + ], + { + "title_aux": "ComfyUI_Diffree" + } + ], + "https://github.com/smthemex/ComfyUI_DiffuEraser": [ + [ + "DiffuEraserLoader", + "DiffuEraserSampler" + ], + { + "title_aux": "ComfyUI_DiffuEraser" + } + ], + "https://github.com/smthemex/ComfyUI_EchoMimic": [ + [ + "Echo_LoadModel", + "Echo_Sampler" + ], + { + "title_aux": "ComfyUI_EchoMimic" + } + ], + "https://github.com/smthemex/ComfyUI_Face_Anon_Simple": [ + [ + "Face_Anon_Simple_Align", + "Face_Anon_Simple_LoadModel", + "Face_Anon_Simple_Sampler" + ], + { + "title_aux": "ComfyUI_Face_Anon_Simple" + } + ], + "https://github.com/smthemex/ComfyUI_FoleyCrafter": [ + [ + "FoleyCrafter_LoadModel", + "FoleyCrafter_Sampler" + ], + { + "title_aux": "ComfyUI_FoleyCrafter" + } + ], + "https://github.com/smthemex/ComfyUI_FollowYourEmoji": [ + [ + "Emoji_Make_Temple", + "FollowYouEmoji_LoadModel", + "FollowYouEmoji_Sampler" + ], + { + "title_aux": "ComfyUI_FollowYourEmoji" + } + ], + "https://github.com/smthemex/ComfyUI_Hallo2": [ + [ + "HalloLoader", + "HalloPreImgAndAudio", + "HallosSampler", + "HallosUpscaleloader", + "HallosVideoUpscale" + ], + { + "title_aux": "ComfyUI_Hallo2" + } + ], + "https://github.com/smthemex/ComfyUI_HiDiffusion_Pro": [ + [ + "HI_Diffusers_Model_Loader", + "Hi_Sampler" + ], + { + "title_aux": "ComfyUI_HiDiffusion_Pro" + } + ], + "https://github.com/smthemex/ComfyUI_HunyuanAvatar_Sm": [ + [ + "HY_Avatar_Loader", + "HY_Avatar_PreData", + "HY_Avatar_Sampler" + ], + { + "title_aux": "ComfyUI_HunyuanAvatar_Sm" + } + ], + "https://github.com/smthemex/ComfyUI_ID_Animator": [ + [ + "ID_Animator", + "ID_Repo_Choice" + ], + { + "title_aux": "ComfyUI_ID_Animator" + } + ], + "https://github.com/smthemex/ComfyUI_InstantIR_Wrapper": [ + [ + "InstantIR_Loader", + "InstantIR_Sampler" + ], + { + "author": "zhaoyafei", + "title_aux": "ComfyUI_InstantIR_Wrapper" + } + ], + "https://github.com/smthemex/ComfyUI_KV_Edit": [ + [ + "KV_Edit_Load", + "KV_Edit_PreData", + "KV_Edit_Sampler" + ], + { + "title_aux": "ComfyUI_KV_Edit" + } + ], + "https://github.com/smthemex/ComfyUI_Light_A_Video": [ + [ + "Light_A_Video_Loader", + "Light_A_Video_Sampler" + ], + { + "title_aux": "ComfyUI_Light_A_Video" + } + ], + "https://github.com/smthemex/ComfyUI_Llama3_8B": [ + [ + "ChatQA_1p5_8b", + "Local_Or_Repo_Choice", + "Meta_Llama3_8B", + "MiniCPM_Llama3_V25" + ], + { + "title_aux": "ComfyUI_Llama3_8B" + } + ], + "https://github.com/smthemex/ComfyUI_MS_Diffusion": [ + [ + "MS_Object_img_Batch", + "MSdiffusion_Model_Loader", + "MSdiffusion_Sampler" + ], + { + "title_aux": "ComfyUI_MS_Diffusion" + } + ], + "https://github.com/smthemex/ComfyUI_MangaNinjia": [ + [ + "MangaNinjiaLoader", + "MangaNinjiaSampler", + "MarkImageNode" + ], + { + "title_aux": "ComfyUI_MangaNinjia" + } + ], + "https://github.com/smthemex/ComfyUI_MooER": [ + [ + "MooER_LoadModel", + "MooER_Sampler" + ], + { + "title_aux": "ComfyUI_MooER" + } + ], + "https://github.com/smthemex/ComfyUI_ObjectClear": [ + [ + "ObjectClearBatch", + "ObjectClearLoader", + "ObjectClearSampler", + "ObjectClearVision" + ], + { + "title_aux": "ComfyUI_ObjectClear" + } + ], + "https://github.com/smthemex/ComfyUI_OmniParser": [ + [ + "OmniParser_Loader", + "OmniParser_Sampler" + ], + { + "title_aux": "ComfyUI_OmniParser" + } + ], + "https://github.com/smthemex/ComfyUI_OmniSVG": [ + [ + "OmniSVGLoader", + "OmniSVGSampler" + ], + { + "title_aux": "ComfyUI_OmniSVG" + } + ], + "https://github.com/smthemex/ComfyUI_PBR_Maker": [ + [ + "Load_MatForger", + "MatForger_Sampler" + ], + { + "title_aux": "ComfyUI_PBR_Maker" + } + ], + "https://github.com/smthemex/ComfyUI_ParlerTTS": [ + [ + "ParlerTTS_LoadModel", + "ParlerTTS_Sampler" + ], + { + "title_aux": "ComfyUI_ParlerTTS" + } + ], + "https://github.com/smthemex/ComfyUI_PartPacker": [ + [ + "PartPacker_Loader", + "PartPacker_Sampler" + ], + { + "title_aux": "ComfyUI_PartPacker" + } + ], + "https://github.com/smthemex/ComfyUI_Personalize_Anything": [ + [ + "Personalize_Anything_Load", + "Personalize_Anything_Sampler" + ], + { + "title_aux": "ComfyUI_Personalize_Anything" + } + ], + "https://github.com/smthemex/ComfyUI_PhotoDoodle": [ + [ + "PhotoDoodle_Loader", + "PhotoDoodle_Sampler" + ], + { + "title_aux": "ComfyUI_PhotoDoodle" + } + ], + "https://github.com/smthemex/ComfyUI_Pic2Story": [ + [ + "Pic2Story_Loader", + "Pic2Story_Sampler" + ], + { + "title_aux": "ComfyUI_Pic2Story" + } + ], + "https://github.com/smthemex/ComfyUI_Pipeline_Tool": [ + [ + "Pipeline_Tool" + ], + { + "title_aux": "ComfyUI_Pipeline_Tool" + } + ], + "https://github.com/smthemex/ComfyUI_Pops": [ + [ + "Pops_Decoder", + "Pops_Repo_Loader", + "Pops_Sampler" + ], + { + "title_aux": "ComfyUI_Pops" + } + ], + "https://github.com/smthemex/ComfyUI_SVFR": [ + [ + "SVFR_LoadModel", + "SVFR_Sampler", + "SVFR_img2mask" + ], + { + "title_aux": "ComfyUI_SVFR" + } + ], + "https://github.com/smthemex/ComfyUI_Sapiens": [ + [ + "SapiensLoader", + "SapiensSampler" + ], + { + "title_aux": "ComfyUI_Sapiens" + } + ], + "https://github.com/smthemex/ComfyUI_SongGeneration": [ + [ + "SongGeneration_Sampler", + "SongGeneration_Stage1", + "SongGeneration_Stage2" + ], + { + "title_aux": "ComfyUI_SongGeneration" + } + ], + "https://github.com/smthemex/ComfyUI_Sonic": [ + [ + "SONICSampler", + "SONICTLoader", + "SONIC_PreData" + ], + { + "title_aux": "ComfyUI_Sonic" + } + ], + "https://github.com/smthemex/ComfyUI_StableAudio_Open": [ + [ + "StableAudio_ModelLoader", + "StableAudio_Sampler" + ], + { + "title_aux": "ComfyUI_StableAudio_Open" + } + ], + "https://github.com/smthemex/ComfyUI_Stable_Makeup": [ + [ + "StableMakeup_LoadModel", + "StableMakeup_Sampler" + ], + { + "author": "Sajjad Ayobbi", + "title_aux": "ComfyUI_Stable_Makeup" + } + ], + "https://github.com/smthemex/ComfyUI_StoryDiffusion": [ + [ + "Comic_Type", + "EasyFunction_Lite", + "Pre_Translate_prompt", + "StoryDiffusion_Apply", + "StoryDiffusion_CLIPTextEncode", + "StoryDiffusion_KSampler", + "StoryDiffusion_Lora_Control" + ], + { + "title_aux": "ComfyUI_StoryDiffusion" + } + ], + "https://github.com/smthemex/ComfyUI_Streamv2v_Plus": [ + [ + "Stream_Lora_Loader", + "Stream_Model_Loader", + "Stream_Sampler" + ], + { + "title_aux": "ComfyUI_Streamv2v_Plus" + } + ], + "https://github.com/smthemex/ComfyUI_TRELLIS": [ + [ + "Trellis_LoadModel", + "Trellis_Sampler", + "Trellis_multiimage_loader" + ], + { + "title_aux": "ComfyUI_TRELLIS" + } + ], + "https://github.com/smthemex/ComfyUI_VisualCloze": [ + [ + "Img_Quadruple", + "VisualCloze_Aplly", + "VisualCloze_CLIPText", + "VisualCloze_KSampler" + ], + { + "title_aux": "ComfyUI_VisualCloze" + } + ], + "https://github.com/smthemex/ComfyUI_YuE": [ + [ + "YUE_Stage_A_Loader", + "YUE_Stage_A_Sampler", + "YUE_Stage_B_Loader", + "YUE_Stage_B_Sampler" + ], + { + "title_aux": "ComfyUI_YuE" + } + ], + "https://github.com/sn0w12/ComfyUI-Sn0w-Scripts": [ + [ + "Character Selector", + "Copy/Paste Textbox", + "Filter Tags", + "Generate All Characters", + "Get Font Size", + "Load Lora Folder", + "Load Lora Sn0w", + "Lora Selector", + "Lora Tester", + "Prompt Combine", + "Prompt Selector", + "Sn0w KSampler", + "Sn0w Lora Stacker", + "TaggedTiledUpscaler", + "Upscale Image With Model By" + ], + { + "title_aux": "ComfyUI-Sn0w-Scripts" + } + ], + "https://github.com/sneccc/comfyui-snek-nodes": [ + [ + "Aesthetics", + "Aesthetics V2", + "Load AI Toolkit Latent Flux", + "Save_Image_And_Caption", + "Send_to_Eagle", + "\ud83d\udc0d Random Prompt From JSON" + ], + { + "title_aux": "comfyui-snek-nodes" + } + ], + "https://github.com/souki202/ComfyUI-LoadImage-Advanced": [ + [ + "ColorAdjustment", + "LoadImageUpscale", + "LoadImageUpscaleBy" + ], + { + "title_aux": "ComfyUI-LoadImage-Advanced" + } + ], + "https://github.com/sourceful-official/LoadLoraModelOnlyWithUrl": [ + [ + "LoadLoraModelOnlyWithUrl" + ], + { + "title_aux": "LoadLoraModelOnlyWithUrl" + } + ], + "https://github.com/sousakujikken/ComfyUI-PixydustQuantizer": [ + [ + "CRTLikeEffectNode", + "Quantizer", + "XYBlurNode" + ], + { + "title_aux": "ComfyUI-PixydustQuantizer" + } + ], + "https://github.com/space-nuko/ComfyUI-Disco-Diffusion": [ + [ + "DiscoDiffusion_DiscoDiffusion", + "DiscoDiffusion_DiscoDiffusionExtraSettings", + "DiscoDiffusion_GuidedDiffusionLoader", + "DiscoDiffusion_OpenAICLIPLoader" + ], + { + "title_aux": "Disco Diffusion" + } + ], + "https://github.com/space-nuko/ComfyUI-OpenPose-Editor": [ + [ + "Nui.OpenPoseEditor" + ], + { + "title_aux": "OpenPose Editor" + } + ], + "https://github.com/space-nuko/nui-suite": [ + [ + "Nui.DynamicPromptsTextGen", + "Nui.FeelingLuckyTextGen", + "Nui.OutputString" + ], + { + "title_aux": "nui suite" + } + ], + "https://github.com/spacepxl/ComfyUI-Depth-Pro": [ + [ + "DepthPro", + "FocalFromList", + "FocalMMtoPX", + "FocalPXtoMM", + "LoadDepthPro", + "MetricDepthToInverse", + "MetricDepthToRelative" + ], + { + "title_aux": "ComfyUI-Depth-Pro" + } + ], + "https://github.com/spacepxl/ComfyUI-HQ-Image-Save": [ + [ + "LoadEXR", + "LoadEXRFrames", + "LoadImageAndPrompt", + "LoadLatentEXR", + "SaveEXR", + "SaveEXRFrames", + "SaveImageAndPromptExact", + "SaveImageAndPromptIncremental", + "SaveLatentEXR", + "SaveTiff" + ], + { + "title_aux": "ComfyUI-HQ-Image-Save" + } + ], + "https://github.com/spacepxl/ComfyUI-LossTesting": [ + [ + "Measure Timestep Loss" + ], + { + "title_aux": "ComfyUI-LossTesting" + } + ], + "https://github.com/spacepxl/ComfyUI-RAVE": [ + [ + "ConditioningDebug", + "ImageGridCompose", + "ImageGridDecompose", + "KSamplerRAVE", + "LatentGridCompose", + "LatentGridDecompose" + ], + { + "title_aux": "ComfyUI-RAVE" + } + ], + "https://github.com/spacepxl/ComfyUI-StyleGan": [ + [ + "BatchAverageStyleGANLatents", + "BlendStyleGANLatents", + "GenerateStyleGANLatent", + "LoadStyleGAN", + "StyleGANInversion", + "StyleGANLatentFromBatch", + "StyleGANSampler" + ], + { + "title_aux": "ComfyUI-StyleGan" + } + ], + "https://github.com/spawner1145/CUI-Lumina2-TeaCache": [ + [ + "LPIPS_Model_Loader", + "Store_Baseline_Image", + "TeaCache_LPIPS_Evaluator", + "TeaCache_Lumina2", + "TeaCache_Patcher", + "TeaCache_Result_Collector" + ], + { + "title_aux": "CUI-Lumina2-TeaCache" + } + ], + "https://github.com/spawner1145/comfyui-aichat": [ + [ + "GeminiApiLoader", + "GeminiChat", + "GeminiFileUploader", + "GeminiImageEncoder", + "GeminiTextBlock", + "OpenAIApiLoader", + "OpenAIChat", + "OpenAIFileUploader", + "OpenAIImageEncoder", + "OpenAITextBlock" + ], + { + "title_aux": "comfyui-aichat" + } + ], + "https://github.com/spinagon/ComfyUI-seam-carving": [ + [ + "SeamCarving" + ], + { + "title_aux": "ComfyUI-seam-carving" + } + ], + "https://github.com/spinagon/ComfyUI-seamless-tiling": [ + [ + "CircularVAEDecode", + "MakeCircularVAE", + "OffsetImage", + "SeamlessTile" + ], + { + "title_aux": "Seamless tiling Node for ComfyUI" + } + ], + "https://github.com/spro/comfyui-mirror": [ + [ + "LatentMirror" + ], + { + "title_aux": "Latent Mirror node for ComfyUI" + } + ], + "https://github.com/ssitu/ComfyUI_UltimateSDUpscale": [ + [ + "UltimateSDUpscale", + "UltimateSDUpscaleCustomSample", + "UltimateSDUpscaleNoUpscale" + ], + { + "title_aux": "UltimateSDUpscale" + } + ], + "https://github.com/ssitu/ComfyUI_fabric": [ + [ + "FABRICPatchModel", + "FABRICPatchModelAdv", + "KSamplerAdvFABRICAdv", + "KSamplerFABRIC", + "KSamplerFABRICAdv" + ], + { + "title_aux": "ComfyUI fabric" + } + ], + "https://github.com/ssitu/ComfyUI_restart_sampling": [ + [ + "KRestartSampler", + "KRestartSamplerAdv", + "KRestartSamplerCustom", + "KRestartSamplerSimple", + "RestartSampler", + "RestartScheduler" + ], + { + "title_aux": "Restart Sampling" + } + ], + "https://github.com/ssitu/ComfyUI_roop": [ + [ + "RoopImproved", + "roop" + ], + { + "title_aux": "ComfyUI roop" + } + ], + "https://github.com/stavsap/comfyui-downloader": [ + [ + "DownloadSummaryParser", + "DownloadTokenLoader", + "Downloader" + ], + { + "title_aux": "comfyui-downloader" + } + ], + "https://github.com/stavsap/comfyui-kokoro": [ + [ + "KokoroGenerator", + "KokoroSpeaker", + "KokoroSpeakerCombiner" + ], + { + "title_aux": "comfyui-kokoro" + } + ], + "https://github.com/stavsap/comfyui-ollama": [ + [ + "OllamaConnectivityV2", + "OllamaGenerate", + "OllamaGenerateAdvance", + "OllamaGenerateV2", + "OllamaLoadContext", + "OllamaOptionsV2", + "OllamaSaveContext", + "OllamaVision" + ], + { + "title_aux": "ComfyUI Ollama" + } + ], + "https://github.com/stepfun-ai/ComfyUI-StepVideo": [ + [ + "TI2V", + "TI2V_API" + ], + { + "title_aux": "ComfyUI-StepVideo" + } + ], + "https://github.com/stevenwg/ComfyUI-VideoGrid": [ + [ + "VideosConcateHorizontal:", + "VideosConcateVertical" + ], + { + "title_aux": "ComfyUI-VideoGrid" + } + ], + "https://github.com/stormcenter/ComfyUI-AutoSplitGridImage": [ + [ + "EvenImageResizer", + "GridImageSplitter" + ], + { + "title_aux": "ComfyUI-AutoSplitGridImage" + } + ], + "https://github.com/stormcenter/ComfyUI-LivePhotoCreator": [ + [ + "ImageCompareTransition", + "LivePhotoCreator", + "LivePhotoPreview" + ], + { + "title_aux": "ComfyUI LivePhoto Creator" + } + ], + "https://github.com/stormcenter/ComfyUI-SVGFullfill": [ + [ + "SVGEditor", + "SVGUploader" + ], + { + "title_aux": "ComfyUI-SVGFullfill" + } + ], + "https://github.com/storyicon/comfyui_musev_evolved": [ + [ + "AnimationZoom (comfyui_musev_evolved)", + "ImageSelector (comfyui_musev_evolved)", + "MuseVImg2Vid V1 (comfyui_musev_evolved)", + "MuseVPredictor V1 (comfyui_musev_evolved)" + ], + { + "author": "infguo", + "title_aux": "ComfyUI MuseV Evolved" + } + ], + "https://github.com/storyicon/comfyui_segment_anything": [ + [ + "GroundingDinoModelLoader (segment anything)", + "GroundingDinoSAMSegment (segment anything)", + "InvertMask (segment anything)", + "IsMaskEmpty", + "SAMModelLoader (segment anything)" + ], + { + "title_aux": "segment anything" + } + ], + "https://github.com/strand1/ComfyUI-Autogen": [ + [ + "AutogenAssistantAgent", + "AutogenCodeExecutor", + "AutogenGroupChat", + "AutogenModel" + ], + { + "title_aux": "ComfyUI-Autogen" + } + ], + "https://github.com/strawberryPunch/vram_optimizer": [ + [ + "StrawberryGPUMonitor", + "StrawberryVramOptimizer", + "custom_nodes" + ], + { + "nodename_pattern": "StFist", + "title_aux": "StrawberryFist VRAM Optimizer" + } + ], + "https://github.com/strimmlarn/ComfyUI-Strimmlarns-Aesthetic-Score": [ + [ + "AesthetlcScoreSorter", + "CalculateAestheticScore", + "LoadAesteticModel", + "ScoreToNumber" + ], + { + "title_aux": "ComfyUI_Strimmlarns_aesthetic_score" + } + ], + "https://github.com/styler00dollar/ComfyUI-deepcache": [ + [ + "DeepCache" + ], + { + "title_aux": "ComfyUI-deepcache" + } + ], + "https://github.com/styler00dollar/ComfyUI-sudo-latent-upscale": [ + [ + "SudoLatentUpscale" + ], + { + "title_aux": "ComfyUI-sudo-latent-upscale" + } + ], + "https://github.com/sugarkwork/ComfyUI_AspectRatioToSize": [ + [ + "AspectRatio", + "AspectRatioToSize", + "CalcFactorWidthHeight", + "CalculateImagePadding", + "MatchImageToAspectRatio", + "SizeToWidthHeight" + ], + { + "title_aux": "ComfyUI_AspectRatioToSize" + } + ], + "https://github.com/sugarkwork/comfyui-trtupscaler": [ + [ + "TRTUpscaler" + ], + { + "title_aux": "comfyui-trtupscaler" + } + ], + "https://github.com/sugarkwork/comfyui_cohere": [ + [ + "SimpleCohereNode" + ], + { + "title_aux": "comfyui_cohere" + } + ], + "https://github.com/sugarkwork/comfyui_tag_fillter": [ + [ + "TagCategory", + "TagCategoryEnhance", + "TagComparator", + "TagEnhance", + "TagFilter", + "TagFlag", + "TagFlagImage", + "TagIf", + "TagMerger", + "TagMerger4", + "TagMerger6", + "TagRemover", + "TagReplace", + "TagSelector", + "TagSwitcher", + "TagWildcardFilter" + ], + { + "title_aux": "comfyui_tag_filter" + } + ], + "https://github.com/superyoman/comfyui_lumaAPI": [ + [ + "LUMA_API_YoC", + "LUMA_API_result_YoC" + ], + { + "title_aux": "comfyui_lumaAPI" + } + ], + "https://github.com/surinder83singh/ComfyUI-compare-videos": [ + [ + "CompareVideos" + ], + { + "title_aux": "Compare Videos" + } + ], + "https://github.com/svetozarov/AS_LLM_nodes": [ + [ + "AS_ComfyGPT", + "AS_GeminiCaptioning", + "AS_MultimodalGemini" + ], + { + "title_aux": "AS_LLM_nodes" + } + ], + "https://github.com/sweetndata/ComfyUI-Image-Harmonizer": [ + [ + "harmonizer" + ], + { + "title_aux": "ComfyUI-Image-Harmonizer" + } + ], + "https://github.com/sweetndata/ComfyUI-googletrans": [ + [ + "googletrans" + ], + { + "title_aux": "ComfyUI-googletrans" + } + ], + "https://github.com/sweetndata/ComfyUI_Sticker_Compositer": [ + [ + "Sticker_Compositer" + ], + { + "title_aux": "ComfyUI_Sticker_Compositer" + } + ], + "https://github.com/syllebra/bilbox-comfyui": [ + [ + "BilboXLut", + "BilboXPhotoPrompt", + "BilboXVignette" + ], + { + "title_aux": "BilboX's ComfyUI Custom Nodes" + } + ], + "https://github.com/sylym/comfy_vid2vid": [ + [ + "CheckpointLoaderSimpleSequence", + "DdimInversionSequence", + "KSamplerSequence", + "LoadImageMaskSequence", + "LoadImageSequence", + "LoraLoaderSequence", + "SetLatentNoiseSequence", + "TrainUnetSequence", + "VAEEncodeForInpaintSequence" + ], + { + "title_aux": "Vid2vid" + } + ], + "https://github.com/synchronicity-labs/sync-comfyui": [ + [ + "SyncLipsyncInputNode", + "SyncLipsyncMainNode", + "SyncLipsyncOutputNode" + ], + { + "title_aux": "ComfyUI Sync Lipsync Node" + } + ], + "https://github.com/synthetai/ComfyUI-JM-KLing-API": [ + [ + "JM-KLingAI-API/api-key", + "JM-KLingAI-API/hybrid-video", + "JM-KLingAI-API/image-downloader", + "JM-KLingAI-API/image-generation", + "JM-KLingAI-API/image2video", + "JM-KLingAI-API/lip-sync", + "JM-KLingAI-API/lip-sync-async", + "JM-KLingAI-API/multi-image2video", + "JM-KLingAI-API/query-status", + "JM-KLingAI-API/text2video", + "JM-KLingAI-API/video-downloader" + ], + { + "title_aux": "ComfyUI-JM-KLing-API" + } + ], + "https://github.com/synthetai/ComfyUI-JM-MiniMax-API": [ + [ + "JM-MiniMax-API/check-video-status", + "JM-MiniMax-API/download-video", + "JM-MiniMax-API/load-audio", + "JM-MiniMax-API/text-to-speech", + "JM-MiniMax-API/video-generation", + "JM-MiniMax-API/voice-cloning", + "JM-MiniMax-API/voice-design" + ], + { + "title_aux": "ComfyUI-JM-MiniMax-API" + } + ], + "https://github.com/synthetai/ComfyUI-JM-Volcengine-API": [ + [ + "VolcengineI2VS2Pro", + "VolcengineImgEditV3", + "volcengine-doubao-seedance", + "volcengine-i2v-s2pro", + "volcengine-img-edit-v3", + "volcengine-seedream-v3" + ], + { + "title_aux": "ComfyUI-JM-Volcengine-API" + } + ], + "https://github.com/synthetai/ComfyUI-ToolBox": [ + [ + "AutoDLDownload", + "CreatePaths", + "FolderDeleter", + "FolderViewe", + "PathOutput" + ], + { + "title_aux": "ComfyUI-ToolBox" + } + ], + "https://github.com/synthetai/ComfyUI_FaceEnhancer": [ + [ + "GFPGANFaceEnhancer", + "GFPGANFolderProcessor" + ], + { + "title_aux": "ComfyUI_FaceEnhancer" + } + ], + "https://github.com/synthetai/ComfyUI_PromptBatcher": [ + [ + "LoadPromptsFromDir", + "SaveTextToFiles" + ], + { + "title_aux": "ComfyUI_PromptBatcher" + } + ], + "https://github.com/sysL-padawan/comfyui-elevenlabs-integration": [ + [ + "ElevenlabsTextToEffect", + "ElevenlabsTextToVoice" + ], + { + "title_aux": "ComfyUI ElevenLabs API integration" + } + ], + "https://github.com/szhublox/ambw_comfyui": [ + [ + "Auto Merge Block Weighted", + "CLIPMergeSimple", + "CheckpointSave", + "ModelMergeBlocks", + "ModelMergeSimple" + ], + { + "title_aux": "Auto-MBW" + } + ], + "https://github.com/taabata/LCM_Inpaint_Outpaint_Comfy": [ + [ + "ComfyNodesToSaveCanvas", + "FloatNumber", + "FreeU_LCM", + "ImageDims", + "ImageOutputToComfyNodes", + "ImageResize", + "ImageShuffle", + "ImageSwitch", + "LCMGenerate", + "LCMGenerate_ReferenceOnly", + "LCMGenerate_SDTurbo", + "LCMGenerate_img2img", + "LCMGenerate_img2img_IPAdapter", + "LCMGenerate_img2img_controlnet", + "LCMGenerate_inpaintv2", + "LCMGenerate_inpaintv3", + "LCMLoader", + "LCMLoader_RefInpaint", + "LCMLoader_ReferenceOnly", + "LCMLoader_SDTurbo", + "LCMLoader_controlnet", + "LCMLoader_controlnet_inpaint", + "LCMLoader_img2img", + "LCMLoraLoader_inpaint", + "LCMLoraLoader_ipadapter", + "LCMLora_inpaint", + "LCMLora_inpaintV2", + "LCMLora_ipadapter", + "LCMT2IAdapter", + "LCM_IPAdapter", + "LCM_IPAdapter_inpaint", + "LCM_outpaint_prep", + "LoadImageNode_LCM", + "Loader_SegmindVega", + "OutpaintCanvasTool", + "SaveImage_Canvas", + "SaveImage_LCM", + "SaveImage_Puzzle", + "SaveImage_PuzzleV2", + "SegmindVega", + "SettingsSwitch", + "stitch" + ], + { + "title_aux": "LCM_Inpaint-Outpaint_Comfy" + } + ], + "https://github.com/taabata/SANA_LOWVRAM": [ + [ + "SANADiffuse", + "SANATextEncode" + ], + { + "title_aux": "SANA_LOWVRAM" + } + ], + "https://github.com/takemetosiberia/ComfyUI-SAMURAI--SAM2-": [ + [ + "SAMURAIBoxInputNode", + "SAMURAIPointsInputNode", + "SAMURAIRefineNode" + ], + { + "title_aux": "SAMURAI Nodes for ComfyUI" + } + ], + "https://github.com/talesofai/comfyui-browser": [ + [ + "DifyTextGenerator //Browser", + "LoadImageByUrl //Browser", + "SelectInputs //Browser", + "UploadToRemote //Browser", + "XyzPlot //Browser" + ], + { + "title_aux": "ComfyUI Browser" + } + ], + "https://github.com/tanglaoya321/ComfyUI-StoryMaker": [ + [ + "StoryMakerSinglePortraitNode", + "StoryMakerSwapClothNode", + "StoryMakerTwoPortraitNode" + ], + { + "title_aux": "ComfyUI-StoryMaker" + } + ], + "https://github.com/tatookan/comfyui_ssl_gemini_EXP": [ + [ + "SSL_GeminiAPIKeyConfig", + "SSL_GeminiTextPrompt" + ], + { + "title_aux": "comfyui_ssl_gemini_EXP" + } + ], + "https://github.com/tauraloke/ComfyUI-Unfake-Pixels": [ + [ + "PixelArtScaler" + ], + { + "title_aux": "ComfyUI-Unfake-Pixels" + } + ], + "https://github.com/tavyra/ComfyUI_Curves": [ + [ + "Curve Visualizer", + "RGB Curve Editor", + "RGBCurvesAdvanced" + ], + { + "title_aux": "ComfyUI_Curves" + } + ], + "https://github.com/tetsuoo-online/comfyui-too-xmp-metadata": [ + [ + "ReadXMPMetadata", + "WriteXMPMetadataLossless", + "WriteXMPMetadataTensor" + ], + { + "title_aux": "comfyui-too-xmp-metadata" + } + ], + "https://github.com/teward/ComfyUI-Helper-Nodes": [ + [ + "HelperNodes_CfgScale", + "HelperNodes_CheckpointSelector", + "HelperNodes_MultilineStringLiteral", + "HelperNodes_Prompt", + "HelperNodes_SDXLCommonResolutions", + "HelperNodes_SamplerSelector", + "HelperNodes_SaveImage", + "HelperNodes_SchedulerSelector", + "HelperNodes_SeedSelector", + "HelperNodes_Steps", + "HelperNodes_StringLiteral", + "HelperNodes_VAESelector", + "HelperNodes_WidthHeight" + ], + { + "title_aux": "ComfyUI-Helper-Nodes" + } + ], + "https://github.com/thalismind/ComfyUI-Blend-Nodes": [ + [ + "BlendImageNode" + ], + { + "title_aux": "ComfyUI Blend Image Nodes" + } + ], + "https://github.com/thalismind/ComfyUI-LoadImageWithFilename": [ + [ + "CropImageByMask", + "LoadImageFolder", + "LoadImageWithFilename", + "SaveImageWithFilename" + ], + { + "title_aux": "ComfyUI LoadImageWithFilename" + } + ], + "https://github.com/theAdamColton/ComfyUI-texflow-extension": [ + [ + "Load Texflow Depth Image", + "Save Texflow Image" + ], + { + "title_aux": "ComfyUI-texflow-extension" + } + ], + "https://github.com/theUpsider/ComfyUI-Styles_CSV_Loader": [ + [ + "Load Styles CSV" + ], + { + "title_aux": "Styles CSV Loader Extension for ComfyUI" + } + ], + "https://github.com/thecooltechguy/ComfyUI-MagicAnimate": [ + [ + "MagicAnimate", + "MagicAnimateModelLoader" + ], + { + "title_aux": "ComfyUI-MagicAnimate" + } + ], + "https://github.com/thecooltechguy/ComfyUI-Stable-Video-Diffusion": [ + [ + "SVDDecoder", + "SVDModelLoader", + "SVDSampler", + "SVDSimpleImg2Vid" + ], + { + "title_aux": "ComfyUI Stable Video Diffusion" + } + ], + "https://github.com/thedivergentai/divergent_nodes": [ + [ + "CLIPTokenCounter", + "DivergentGeminiNode", + "KoboldCppApiNode", + "LoraStrengthXYPlot", + "MusiQNode", + "SaveImageEnhancedNode" + ], + { + "title_aux": "Divergent Nodes" + } + ], + "https://github.com/theshubzworld/ComfyUI-FaceCalloutNode": [ + [ + "FaceCalloutEffect", + "IntegratedFaceComposite", + "IsolatedFaceCallout" + ], + { + "title_aux": "ComfyUI-FaceCalloutNode" + } + ], + "https://github.com/theshubzworld/ComfyUI-SD3.5-Latent-Size-Picker": [ + [ + "FluxEmptyLatent", + "SD3_5EmptyLatent" + ], + { + "title_aux": "SD3.5 Empty Latent Size Picker" + } + ], + "https://github.com/theshubzworld/ComfyUI-TogetherVision": [ + [ + "Together Image \ud83c\udfa8", + "TogetherVisionBatchNode", + "TogetherVisionNode", + "TogetherVisionNode \ud83d\udd0d (Enhanced)" + ], + { + "title_aux": "Together Vision Node" + } + ], + "https://github.com/theshubzworld/ComfyUI-ollama_killer": [ + [ + "OllamaKiller" + ], + { + "title_aux": "ComfyUI-ollama_killer" + } + ], + "https://github.com/thezveroboy/ComfyUI-CSM-Nodes": [ + [ + "CSMTextToSpeech", + "LoadCSMCheckpoint", + "LoadCSMTokenizer" + ], + { + "title_aux": "ComfyUI-CSM-Nodes" + } + ], + "https://github.com/thezveroboy/ComfyUI-WAN-ClipSkip": [ + [ + "CLIPSkip" + ], + { + "title_aux": "ComfyUI-WAN-ClipSkip" + } + ], + "https://github.com/thezveroboy/ComfyUI-lut": [ + [ + "ImageToLUT" + ], + { + "title_aux": "ComfyUI-LUT" + } + ], + "https://github.com/thezveroboy/ComfyUI_ACE-Step-zveroboy": [ + [ + "ACEModelLoaderZveroboy", + "ACEStepEditZveroboy", + "ACEStepExtendZveroboy", + "ACEStepGenerateZveroboy", + "ACEStepRepaintZveroboy" + ], + { + "title_aux": "ComfyUI_ACE-Step-zveroboy" + } + ], + "https://github.com/thezveroboy/comfyui-random-image-loader": [ + [ + "LoadRandomImage" + ], + { + "title_aux": "ComfyUI Random Image Loader" + } + ], + "https://github.com/thoddnn/ComfyUI-MLX": [ + [ + "MLXClipTextEncoder", + "MLXDecoder", + "MLXLoadFlux", + "MLXSampler" + ], + { + "title_aux": "ComfyUI MLX Nodes" + } + ], + "https://github.com/tianguanggliu/Utools": [ + [ + "UTools" + ], + { + "title_aux": "comfyui-utools" + } + ], + "https://github.com/tiankuan93/ComfyUI-V-Express": [ + [ + "Load_Audio_Path", + "Load_Audio_Path_From_Video", + "Load_Image_Path", + "Load_Kps_Path", + "Load_Kps_Path_From_Video", + "Load_Video_Path", + "VEINTConstant", + "VEPreview_Video", + "VEStringConstant", + "V_Express_Loader", + "V_Express_Sampler" + ], + { + "title_aux": "V-Express: Conditional Dropout for Progressive Training of Portrait Video Generation" + } + ], + "https://github.com/tianlang0704/ComfyUI-StableProjectorzBridge": [ + [ + "ProjectorzControlnetInput", + "ProjectorzControlnetParameter", + "ProjectorzInitInput", + "ProjectorzOutput", + "ProjectorzParameter", + "ProjectorzStringToFloat", + "ProjectorzStringToInt" + ], + { + "title_aux": "Stable Projectorz Bridge" + } + ], + "https://github.com/tianyuw/ComfyUI-LLM-API": [ + [ + "PromptWithImage" + ], + { + "title_aux": "Custom nodes for llm chat with optional image input" + } + ], + "https://github.com/tighug/comfyui-eagle-feeder": [ + [ + "EagleFeederAnimatedWebp", + "EagleFeederMp4", + "EagleFeederPng" + ], + { + "title_aux": "ComfyUI Eagle Feeder" + } + ], + "https://github.com/tighug/comfyui-rating-checker": [ + [ + "RatingCheckerGantMan", + "RatingCheckerMarqo", + "RatingCheckerNudeNet" + ], + { + "title_aux": "ComfyUI Rating Checker" + } + ], + "https://github.com/tkreuziger/comfyui-claude": [ + [ + "Combine Texts", + "Describe Image", + "Transform Text" + ], + { + "title_aux": "ComfyUI and Claude" + } + ], + "https://github.com/tmagara/ComfyUI-Prediction-Boost": [ + [ + "PredictionBoost" + ], + { + "title_aux": "ComfyUI-Prediction-Boost" + } + ], + "https://github.com/tocubed/ComfyUI-AudioReactor": [ + [ + "AudioFrameTransformBeats", + "AudioFrameTransformShadertoy", + "AudioLoadPath", + "Shadertoy" + ], + { + "title_aux": "ComfyUI-AudioReactor" + } + ], + "https://github.com/tocubed/ComfyUI-EvTexture": [ + [ + "EVTEventsToImage", + "EVTLoadEvTextureModel", + "EVTTextureUpscaleVideo", + "EVTVideoToEvents" + ], + { + "title_aux": "ComfyUI-EvTexture" + } + ], + "https://github.com/tomudo/ComfyUI-ascii-art": [ + [ + "ImageToAscii" + ], + { + "author": "dfl", + "description": "CLIP text encoder that does BREAK prompting like A1111", + "nickname": "CLIP with BREAK", + "title": "CLIP with BREAK syntax", + "title_aux": "ComfyUI-ascii-art" + } + ], + "https://github.com/tooldigital/ComfyUI-Yolo-Cropper": [ + [ + "ToolYoloCropper" + ], + { + "title_aux": "Easy automatic (square) image cropper using Yolo" + } + ], + "https://github.com/toxicwind/ComfyUI-TTools": [ + [ + "TTools Extract JSON", + "TTools SD3 Resolution Solver" + ], + { + "title_aux": "TTools for ComfyUI" + } + ], + "https://github.com/toyxyz/ComfyUI_rgbx_Wrapper": [ + [ + "rgb2x" + ], + { + "title_aux": "ComfyUI_rgbx_Wrapper" + } + ], + "https://github.com/toyxyz/ComfyUI_toyxyz_test_nodes": [ + [ + "CaptureWebcam", + "Depth to normal", + "Direct Screen Capture", + "Export glb", + "ImageResize_Padding", + "LatentDelay", + "Load Random Text From File", + "LoadWebcamImage", + "Remove noise", + "SaveImagetoPath", + "VisualAreaMask" + ], + { + "title_aux": "ComfyUI_toyxyz_test_nodes" + } + ], + "https://github.com/traugdor/ComfyUI-Riffusion": [ + [ + "RiffusionNode", + "RiffusionToBatchNode" + ], + { + "title_aux": "ComfyUI-Riffusion" + } + ], + "https://github.com/traugdor/ComfyUI-UltimateSDUpscale-GGUF": [ + [ + "UltimateSDUpscaleGGUF" + ], + { + "title_aux": "ComfyUI-UltimateSDUpscale-GGUF" + } + ], + "https://github.com/traugdor/ComfyUI-quadMoons-nodes": [ + [ + "AnimateDiff Script", + "Apply ControlNet Stack", + "Control Net Stacker", + "Eff. Loader SDXL", + "Efficient Loader", + "HighRes-Fix Script", + "Image Overlay", + "Join XY Inputs of Same Type", + "KSampler (Efficient)", + "KSampler Adv. (Efficient)", + "KSampler SDXL (Eff.)", + "LatentUpscaler", + "LoRA Stack to String converter", + "LoRA Stacker", + "Manual XY Entry Info", + "NNLatentUpscale", + "Noise Control Script", + "Pack SDXL Tuple", + "Tiled Upscaler Script", + "Unpack SDXL Tuple", + "XY Input: Add/Return Noise", + "XY Input: Aesthetic Score", + "XY Input: CFG Scale", + "XY Input: Checkpoint", + "XY Input: Clip Skip", + "XY Input: Control Net", + "XY Input: Control Net Plot", + "XY Input: Denoise", + "XY Input: LoRA", + "XY Input: LoRA Plot", + "XY Input: LoRA Stacks", + "XY Input: Manual XY Entry", + "XY Input: Prompt S/R", + "XY Input: Refiner On/Off", + "XY Input: Sampler/Scheduler", + "XY Input: Seeds++ Batch", + "XY Input: Steps", + "XY Input: VAE", + "XY Plot", + "quadmoonBatchFromLatent", + "quadmoonCLIPTextEncode", + "quadmoonCLIPTextEncode2", + "quadmoonChangeBackground", + "quadmoonConvertBoolToString", + "quadmoonConvertFloatToString", + "quadmoonConvertImageToPrompt", + "quadmoonConvertIntToString", + "quadmoonConvertNormalizeHW", + "quadmoonConvertNumberToString", + "quadmoonINTConditionalOperation", + "quadmoonKSampler", + "quadmoonKSamplerAdvanced", + "quadmoonKSamplerBatched", + "quadmoonLatentImage", + "quadmoonLoadConfigs", + "quadmoonModelLoader", + "quadmoonRotationalSampler", + "quadmoonSaveNeg", + "quadmoonSavePrompt", + "quadmoonSmartNeg", + "quadmoonSmartPrompt", + "quadmoonThebutton" + ], + { + "author": "quadmoon (https://github.com/traugdor)", + "description": "These are just some nodes I wanted and couldn't find where anyone else had made them yet.", + "nickname": "quadmoon's Nodes", + "title": "quadmoon's ComfyUI nodes", + "title_aux": "quadmoon's ComfyUI nodes" + } + ], + "https://github.com/tritant/ComfyUI-Advanced-Photo-Grain": [ + [ + "PhotoFilmGrain" + ], + { + "title_aux": "Advanced Photo Grain" + } + ], + "https://github.com/tritant/ComfyUI_CreaPrompt": [ + [ + "CreaPrompt", + "CreaPrompt List", + "CreaPrompt_0", + "CreaPrompt_1", + "CreaPrompt_2", + "CreaPrompt_3", + "CreaPrompt_4" + ], + { + "title_aux": "ComfyUI-CreaPrompt" + } + ], + "https://github.com/tritant/ComfyUI_Flux_Block_Lora_Merger": [ + [ + "FluxBlockLoraMerger" + ], + { + "title_aux": "Flux Block LoRA Merger" + } + ], + "https://github.com/tritant/ComfyUI_Flux_Lora_Merger": [ + [ + "FluxLoraMerger" + ], + { + "title_aux": "Flux LoRA Merger" + } + ], + "https://github.com/tritant/ComfyUI_Layers_Utility": [ + [ + "LayerSystem" + ], + { + "title_aux": "Layers System" + } + ], + "https://github.com/tritant/ComfyUI_Remove_Banding_Artifacts": [ + [ + "ResampleBandingFix" + ], + { + "title_aux": "Remove Banding Artifacts" + } + ], + "https://github.com/trojblue/trNodes": [ + [ + "trColorCorrection", + "trLayering", + "trRouter", + "trRouterLonger" + ], + { + "title_aux": "trNodes" + } + ], + "https://github.com/troyxmccall/ComfyUI-ScaleToTargetMegapixels": [ + [ + "ScaleToTargetMegapixels" + ], + { + "title_aux": "ComfyUI-ScaleToTargetMegapixels" + } + ], + "https://github.com/trumanwong/ComfyUI-NSFW-Detection": [ + [ + "NSFWDetection" + ], + { + "title_aux": "ComfyUI-NSFW-Detection" + } + ], + "https://github.com/tsogzark/ComfyUI-load-image-from-url": [ + [ + "LoadImageFromUrlOrPath" + ], + { + "title_aux": "ComfyUI-load-image-from-url" + } + ], + "https://github.com/ttulttul/ComfyUI-Iterative-Mixer": [ + [ + "Batch Unsampler", + "Iterative Mixing KSampler", + "Iterative Mixing KSampler Advanced", + "IterativeMixingSampler", + "IterativeMixingScheduler", + "IterativeMixingSchedulerAdvanced", + "Latent Batch Comparison Plot", + "Latent Batch Statistics Plot", + "MixingMaskGenerator" + ], + { + "title_aux": "ComfyUI Iterative Mixing Nodes" + } + ], + "https://github.com/ttulttul/ComfyUI-Tensor-Operations": [ + [ + "Fast Image to Noise", + "Image Match Normalize", + "Latent Match Normalize" + ], + { + "title_aux": "ComfyUI-Tensor-Operations" + } + ], + "https://github.com/tungdop2/Comfyui_face_restorer": [ + [ + "FaceRestorer", + "FaceRestorerLoader" + ], + { + "title_aux": "Face Restorer for ComfyUI" + } + ], + "https://github.com/tungdop2/Comfyui_joy-caption-alpha-two": [ + [ + "JoyCaptioner" + ], + { + "title_aux": "Joy Caption Alpha Two for ComfyUI" + } + ], + "https://github.com/turkyden/ComfyUI-SmartCrop": [ + [ + "ImageSmartCrop" + ], + { + "title_aux": "ComfyUI-SmartCrop" + } + ], + "https://github.com/tusharbhutt/Endless-Nodes": [ + [ + "BatchNegativePrompts", + "Eight_Input_Int_Switch", + "Eight_Input_Int_Switch_Widget", + "Eight_Input_Text_Switch", + "EndlessReplicateLatents", + "FluxBatchPrompts", + "FluxKontextBatchPrompts", + "Four_Input_Int_Switch", + "Four_Input_Int_Switch_Widget", + "Four_Input_Text_Switch", + "ImageComplexityScorer", + "ImageNoveltyScorer", + "Image_saver", + "LatentReplicator", + "LatentReplicatorPrompts", + "PromptCounter", + "Random_Prompt_Multipicker", + "Random_Prompt_Selector", + "Randomizer_Chaos", + "Randomizer_Mayhem", + "Randomizer_Pandemonium", + "SDXLBatchPrompts", + "SimpleBatchPrompts", + "Six_Input_Int_Switch", + "Six_Input_Int_Switch_Widget", + "Six_Input_Text_Switch" + ], + { + "title_aux": "Endless \ufe0f\ud83c\udf0a\u2728 Nodes" + } + ], + "https://github.com/twri/sdxl_prompt_styler": [ + [ + "SDXLPromptStyler", + "SDXLPromptStylerAdvanced" + ], + { + "title_aux": "SDXL Prompt Styler" + } + ], + "https://github.com/ty0x2333/ComfyUI-Dev-Utils": [ + [ + "TY_ExecutionTime", + "TY_UploadAnything", + "TY_UrlDownload" + ], + { + "title_aux": "ComfyUI-Dev-Utils" + } + ], + "https://github.com/uarefans/ComfyUI-Fans": [ + [ + "Fans Prompt Styler Negative", + "Fans Prompt Styler Positive", + "Fans Styler", + "Fans Text Concatenate" + ], + { + "title_aux": "ComfyUI-Fans" + } + ], + "https://github.com/uetuluk/comfyui-webcam-node": [ + [ + "webcam_capture_node" + ], + { + "title_aux": "comfyui-webcam-node" + } + ], + "https://github.com/uihp/ComfyUI-String-Chain": [ + [ + "String Chain", + "String Concat", + "String Toggle", + "String Toggle (Multiline)" + ], + { + "title_aux": "ComfyUI-String-Chain" + } + ], + "https://github.com/umiyuki/comfyui-pad-to-eight": [ + [ + "Pad To Eight" + ], + { + "title_aux": "ComfyUI Pad To Eight" + } + ], + "https://github.com/un-seen/comfyui-tensorops": [ + [ + "BackgroundSelect", + "ChannelSelector", + "DownloadAndLoadFlorence2Model", + "DownloadAndLoadSAM2Model", + "FaceSwap", + "FalDifferentialDiffusion", + "FalDiffusion", + "FetchFromRedis", + "FetchJsonFromSurreal", + "Florence2Run", + "Florence2toCoordinates", + "ForegroundMask", + "GetLayerMask", + "MaskImage", + "Sam2AutoSegmentation", + "Sam2Segmentation", + "Sam2VideoSegmentation", + "Sam2VideoSegmentationAddPoints", + "SaveImageToS3", + "SaveJsonToSurreal", + "SaveTextToSurreal", + "SaveToRedis", + "SendImageOnWebSocket", + "SendJsonOnWebSocket", + "SeparateMask" + ], + { + "title_aux": "comfyui-tensorop" + } + ], + "https://github.com/un-seen/comfyui_segment_anything_plus": [ + [ + "GroundingDinoModelLoader (segment anything plus)", + "GroundingDinoSAMSegment (segment anything plus)", + "InvertMask (segment anything plus)", + "IsMaskEmpty (segment anything plus)", + "SAMModelLoader (segment anything plus)" + ], + { + "title_aux": "ComfyUI Segment Anything" + } + ], + "https://github.com/unicough/comfy_openai_image_api": [ + [ + "OpenAI Image API" + ], + { + "title_aux": "OpenAI Image API with gpt-image-1" + } + ], + "https://github.com/unwdef/unwdef-nodes-comfyui": [ + [ + "RandomTextFromMultiline", + "RandomizeLoras", + "RandomizeLorasStack", + "TextMultilineWithVariables" + ], + { + "title_aux": "unwdef-nodes" + } + ], + "https://github.com/upseem/comfyui_sun_nodes": [ + [ + "SunxAI_BatchImageLoopCloseChen", + "SunxAI_BatchImageLoopOpenChen" + ], + { + "title_aux": "SunxAI Custom Nodes for ComfyUI" + } + ], + "https://github.com/usrname0/comfyui-holdup": [ + [ + "HoldUp" + ], + { + "title_aux": "comfyui-holdup" + } + ], + "https://github.com/vadimcro/VKRiez-Edge": [ + [ + "VKriezEnhancedEdgePreprocessor", + "VKriezHybridEdgePreprocessor" + ], + { + "title_aux": "VKRiez-Edge" + } + ], + "https://github.com/vahidzxc/va-nodes": [ + [ + "VA_Seed" + ], + { + "title_aux": "va-nodes" + } + ], + "https://github.com/vahlok-alunmid/ComfyUI-ExtendIPAdapterClipVision": [ + [ + "EXTEND_CLIP_VISION_INPUT_SIZE", + "IPAdapterAdvancedSizeAware" + ], + { + "title_aux": "ComfyUI-ExtendIPAdapterClipVision" + } + ], + "https://github.com/vaishnav-vn/va1": [ + [ + "RandomAspectRatioMask" + ], + { + "title_aux": "va1" + } + ], + "https://github.com/valofey/Openrouter-Node": [ + [ + "OpenrouterNode" + ], + { + "title_aux": "OpenRouter Node" + } + ], + "https://github.com/vanche1212/ComfyUI-ZMG-Nodes": [ + [ + "VC_Load_Video_Path_Unified_Output", + "VC_Load_Video_Upload_Unified_Output", + "VC_Video_Combine_Unified_Output", + "Waveform2Audio", + "\ud83d\ude0bAPI Request Node", + "\ud83d\ude0bJSON Parser Node", + "\ud83d\ude0bOld Photo Colorization Node", + "\ud83d\ude0bOllama Request Node", + "\ud83d\ude0bSave Image Unified Output" + ], + { + "title_aux": "ZMG PLUGIN" + } + ], + "https://github.com/vanillacode314/SimpleWildcardsComfyUI": [ + [ + "SimpleConcat", + "SimpleWildcard" + ], + { + "author": "VanillaCode314", + "description": "A simple wildcard node for ComfyUI. Can also be used a style prompt node.", + "nickname": "Simple Wildcard", + "title": "Simple Wildcard", + "title_aux": "Simple Wildcard" + } + ], + "https://github.com/var1ableX/ComfyUI_Accessories": [ + [ + "ACC_AnyCast", + "AccMakeListNode", + "GetMaskDimensions", + "GetRandomDimensions", + "isImageEmpty", + "isMaskEmpty" + ], + { + "title_aux": "ComfyUI_Accessories" + } + ], + "https://github.com/vault-developer/comfyui-image-blender": [ + [ + "ImageBlender" + ], + { + "title_aux": "ImageBlender" + } + ], + "https://github.com/veighnsche/comfyui_gr85": [ + [ + "GR85_CTGPhrases", + "GR85_CTGPhrasesSimple", + "GR85_ContainsWord", + "GR85_FilterAndCombineMasks", + "GR85_Florence2RunCTPG", + "GR85_Florence2toCoordinatesGR85", + "GR85_FluxAttentionSeeker2", + "GR85_FluxAttentionSeeker3", + "GR85_FluxAttentionSeekerGenerator", + "GR85_FluxModelMergeParameters", + "GR85_ImageDimensionResizer", + "GR85_ImageSizer", + "GR85_ImageSizerAll", + "GR85_IntToString", + "GR85_IntegerSequenceModifier", + "GR85_IslandMaskGenerator", + "GR85_MaskBatchToSEGS", + "GR85_MaskConnectMST", + "GR85_MaskSplitter", + "GR85_NextSeed", + "GR85_PasteByMaskGr85", + "GR85_RandomFloat", + "GR85_RandomInt", + "GR85_RandomRatio", + "GR85_RandomizedMaskTransform", + "GR85_Sam2Segmentation", + "GR85_SaveImageFile", + "GR85_SaveTextFile", + "GR85_SeedBasedOutputSelector", + "GR85_ShowText", + "GR85_SimpleWildcardPicker", + "GR85_StrSafe", + "GR85_TagInjector", + "GR85_TagInjectorDuo", + "GR85_TagInjectorLarge", + "GR85_TagInjectorSingle", + "GR85_VerticalWildcardPicker" + ], + { + "title_aux": "comfyui_gr85" + } + ], + "https://github.com/vekitan55/SimpleFlux1Merger": [ + [ + "ExpertFlux1Merge", + "SimplifiedFlux1Merge" + ], + { + "title_aux": "Simple Flux.1 Merger for ComfyUI" + } + ], + "https://github.com/victorchall/comfyui_webcamcapture": [ + [ + "WebcamCapture" + ], + { + "title_aux": "Comfyui Webcam capture node" + } + ], + "https://github.com/vienteck/ComfyUI-Chat-GPT-Integration": [ + [ + "ChatGptPrompt" + ], + { + "title_aux": "ComfyUI-Chat-GPT-Integration" + } + ], + "https://github.com/violet-chen/comfyui-psd2png": [ + [ + "Psd2Png", + "StringInsert" + ], + { + "title_aux": "comfyui-psd2png" + } + ], + "https://github.com/violet0927/ComfyUI-HuggingFaceLoraUploader": [ + [ + "HuggingFaceLoraUploader", + "ModelScopeLoraUploader" + ], + { + "title_aux": "Hugging Face LoRA Uploader" + } + ], + "https://github.com/viperyl/ComfyUI-RGT": [ + [ + "RGT_Upscale" + ], + { + "title_aux": "ComfyUI-RGT" + } + ], + "https://github.com/visualbruno/ComfyUI-Hunyuan3d-2-1": [ + [ + "Hy3D21CameraConfig", + "Hy3D21ExportMesh", + "Hy3D21GenerateMultiViewsBatch", + "Hy3D21IMRemesh", + "Hy3D21LoadImageWithTransparency", + "Hy3D21LoadMesh", + "Hy3D21MeshGenerationBatch", + "Hy3D21MeshUVWrap", + "Hy3D21MeshlibDecimate", + "Hy3D21MultiViewsGeneratorWithMetaData", + "Hy3D21MultiViewsMeshGenerator", + "Hy3D21PostprocessMesh", + "Hy3D21ResizeImages", + "Hy3D21SimpleMeshlibDecimate", + "Hy3D21UseMultiViews", + "Hy3D21UseMultiViewsFromMetaData", + "Hy3D21VAEConfig", + "Hy3D21VAEDecode", + "Hy3D21VAELoader", + "Hy3DBakeMultiViews", + "Hy3DBakeMultiViewsWithMetaData", + "Hy3DHighPolyToLowPolyBakeMultiViewsWithMetaData", + "Hy3DInPaint", + "Hy3DMeshGenerator", + "Hy3DMultiViewsGenerator" + ], + { + "title_aux": "ComfyUI-Hunyuan3d-2-1" + } + ], + "https://github.com/vivax3794/ComfyUI-Sub-Nodes": [ + [ + "VIV_Default", + "VIV_Subgraph", + "VIV_Subgraph_Inputs", + "VIV_Subgraph_Outputs" + ], + { + "title_aux": "ComfyUI-Sub-Nodes" + } + ], + "https://github.com/vivax3794/ComfyUI-Vivax-Nodes": [ + [ + "Any String", + "Chunk Up", + "Get Chunk", + "Inspect", + "Join Chunks", + "Model From URL" + ], + { + "title_aux": "ComfyUI-Vivax-Nodes" + } + ], + "https://github.com/vkff5833/ComfyUI-MobileClient": [ + [ + "MobileClient" + ], + { + "title_aux": "ComfyUI-MobileClient" + } + ], + "https://github.com/vkff5833/ComfyUI-PromptConverter": [ + [ + "PromptConverter", + "PromptConverterWithFilter" + ], + { + "title_aux": "ComfyUI-PromptConverter" + } + ], + "https://github.com/vladpro3/ComfyUI_BishaNodes": [ + [ + "CreatePromptsWithTextFromFile", + "EmptyLatentSizePicker", + "LoadDataFromFiles", + "SimpleSizePicker", + "WildcardReplace", + "WildcardReplaceFromFile" + ], + { + "title_aux": "ComfyUI_BishaNodes" + } + ], + "https://github.com/vrgamegirl19/comfyui-vrgamedevgirl": [ + [ + "ColorMatchToReference", + "FastFilmGrain", + "FastLaplacianSharpen", + "FastSobelSharpen", + "FastUnsharpSharpen" + ], + { + "title_aux": "VRGameDevGirl Video Enhancement Nodes" + } + ], + "https://github.com/vsevolod-oparin/comfyui-kandinsky22": [ + [ + "comfy-kandinsky22-decoder-loader", + "comfy-kandinsky22-hint-combiner", + "comfy-kandinsky22-image-encoder", + "comfy-kandinsky22-img-latents", + "comfy-kandinsky22-latents", + "comfy-kandinsky22-movq-decoder", + "comfy-kandinsky22-positive-text-encoder", + "comfy-kandinsky22-prior-averaging-2", + "comfy-kandinsky22-prior-averaging-3", + "comfy-kandinsky22-prior-averaging-4", + "comfy-kandinsky22-prior-loader", + "comfy-kandinsky22-text-encoder", + "comfy-kandinsky22-unet-decoder" + ], + { + "title_aux": "Kandinsky 2.2 ComfyUI Plugin" + } + ], + "https://github.com/vuongminh1907/ComfyUI_ZenID": [ + [ + "ApplyZenID", + "InstantIDFaceAnalysis", + "InstantIDModelLoader", + "ZenIDCombineFace" + ], + { + "title_aux": "ComfyUI_ZenID" + } + ], + "https://github.com/wTechArtist/ComfyUI-CustomNodes": [ + [ + "GPT4 WWL", + "IPAdapter FaceID With Bool", + "IPAdapter Mad Scientist Weight_Type", + "Image Blending Mode Mask", + "Load Image With Bool", + "Load Lora With Shared" + ], + { + "title_aux": "ComfyUI-CustomNodes" + } + ], + "https://github.com/wTechArtist/ComfyUI-StableDelight-weiweiliang": [ + [ + "WWL_StableDelight" + ], + { + "title_aux": "ComfyUI-StableDelight-weiweiliang" + } + ], + "https://github.com/wTechArtist/ComfyUI_VVL_VideoCamera_Advanced": [ + [ + "VGGTVideoCameraNode" + ], + { + "title_aux": "ComfyUI VVL Video Camera Advanced" + } + ], + "https://github.com/wakattac/ComfyUI-AbstractImaGen": [ + [ + "AbstractImageBackground", + "AbstractImageFilledShapes", + "AbstractImageGenerator", + "AbstractImageLines", + "AbstractImageNoise", + "AbstractImagePattern", + "AbstractImagePostprocessing" + ], + { + "title_aux": "ComfyUI-AbstractImaGen" + } + ], + "https://github.com/wallish77/wlsh_nodes": [ + [ + "Alternating KSampler (WLSH)", + "Build Filename String (WLSH)", + "CLIP +/- w/Text Unified (WLSH)", + "CLIP Positive-Negative (WLSH)", + "CLIP Positive-Negative XL (WLSH)", + "CLIP Positive-Negative XL w/Text (WLSH)", + "CLIP Positive-Negative w/Text (WLSH)", + "Checkpoint Loader w/Name (WLSH)", + "Empty Latent by Pixels (WLSH)", + "Empty Latent by Ratio (WLSH)", + "Empty Latent by Size (WLSH)", + "Generate Border Mask (WLSH)", + "Grayscale Image (WLSH)", + "Image Load with Metadata (WLSH)", + "Image Save with Prompt (WLSH)", + "Image Save with Prompt File (WLSH)", + "Image Save with Prompt/Info (WLSH)", + "Image Save with Prompt/Info File (WLSH)", + "Image Scale By Factor (WLSH)", + "Image Scale by Shortside (WLSH)", + "KSamplerAdvanced (WLSH)", + "Multiply Integer (WLSH)", + "Outpaint to Image (WLSH)", + "Prompt Weight (WLSH)", + "Quick Resolution Multiply (WLSH)", + "Resolutions by Ratio (WLSH)", + "SDXL Quick Empty Latent (WLSH)", + "SDXL Quick Image Scale (WLSH)", + "SDXL Resolutions (WLSH)", + "SDXL Steps (WLSH)", + "Save Positive Prompt(WLSH)", + "Save Prompt (WLSH)", + "Save Prompt/Info (WLSH)", + "Seed and Int (WLSH)", + "Seed to Number (WLSH)", + "Simple Pattern Replace (WLSH)", + "Simple String Combine (WLSH)", + "Time String (WLSH)", + "Upscale by Factor with Model (WLSH)", + "VAE Encode for Inpaint w/Padding (WLSH)" + ], + { + "title_aux": "wlsh_nodes" + } + ], + "https://github.com/wasilone11/comfyui-pvm-node": [ + [ + "SyncPVMessengerNode" + ], + { + "title_aux": "ComfyUI Sync PVM Node" + } + ], + "https://github.com/wasilone11/comfyui-sync-lipsync-node": [ + [ + "SyncLipsyncInputNode", + "SyncLipsyncMainNode", + "SyncLipsyncOutputNode" + ], + { + "title_aux": "ComfyUI Sync Lipsync Node" + } + ], + "https://github.com/watarika/ComfyUI-SendToEagle-w-Metadata": [ + [ + "CreateExtraMetadata", + "SendToEagleWithMetadata", + "SendToEagleWithMetadataSimple" + ], + { + "title_aux": "ComfyUI-SendToEagle-w-Metadata" + } + ], + "https://github.com/wawahuy/ComfyUI-HTTP": [ + [ + "Base64ToImageNode", + "HTTPFormDataConcatNode", + "HTTPFormDataNode", + "HTTPFormFileItemNode", + "HTTPFormImageItemNode", + "HTTPFormTextItemNode", + "HTTPGetJSONFieldNode", + "HTTPGetNode", + "HTTPPostFormDataNode", + "HTTPPostJSONNode", + "HTTPPostRawNode", + "ImageToBase64Node" + ], + { + "title_aux": "ComfyUI HTTP - REST API Nodes" + } + ], + "https://github.com/web3nomad/ComfyUI_Invisible_Watermark": [ + [ + "InvisibleWatermarkEncode" + ], + { + "title_aux": "ComfyUI Invisible Watermark" + } + ], + "https://github.com/weberjc/book-cover-finder-comfy": [ + [ + "BookCoverFinder" + ], + { + "title_aux": "BookCoverFinder" + } + ], + "https://github.com/webfiltered/DebugNode-ComfyUI": [ + [ + "WTFDebugNode" + ], + { + "title_aux": "WTF? - a debug node for ComfyUI" + } + ], + "https://github.com/webuilder/WB-ComfyUI-Utils": [ + [ + "WB_AudioDuration" + ], + { + "title_aux": "ComfyUI WB Utils" + } + ], + "https://github.com/weekii/comfyui-save-image-pro": [ + [ + "SaveImageAdvanced", + "SaveImageSimple" + ], + { + "author": "weekii", + "description": "\u4e13\u4e1a\u7ea7\u56fe\u50cf\u4fdd\u5b58\u63d2\u4ef6\uff0c\u652f\u6301\u591a\u683c\u5f0f\u3001\u81ea\u5b9a\u4e49\u547d\u540d\u548c\u9ad8\u7ea7\u529f\u80fd", + "nickname": "Save Image Pro", + "title": "ComfyUI Save Image Pro", + "title_aux": "comfyui-save-image-pro" + } + ], + "https://github.com/weilin9999/WeiLin-Comfyui-Tools": [ + [ + "WeiLinPromptUI", + "WeiLinPromptUIOnlyLoraStack", + "WeiLinPromptUIWithoutLora" + ], + { + "title_aux": "WeiLin-Comfyui-Tools" + } + ], + "https://github.com/welltop-cn/ComfyUI-TeaCache": [ + [ + "CompileModel", + "TeaCache", + "TeaCacheForCogVideoX" + ], + { + "title_aux": "ComfyUI-TeaCache" + } + ], + "https://github.com/wentao-uw/ComfyUI-template-matching": [ + [ + "IsMaskEmptyNode (template matching)", + "TemplateMatching (template matching)" + ], + { + "title_aux": "ComfyUI template matching" + } + ], + "https://github.com/westNeighbor/ComfyUI-ultimate-openpose-editor": [ + [ + "AppendageEditorNode", + "OpenposeEditorNode" + ], + { + "title_aux": "ComfyUI-ultimate-openpose-editor" + } + ], + "https://github.com/westNeighbor/ComfyUI-ultimate-openpose-estimator": [ + [ + "OpenposeEstimatorNode" + ], + { + "title_aux": "ComfyUI-ultimate-openpose-estimator" + } + ], + "https://github.com/westNeighbor/ComfyUI-ultimate-openpose-render": [ + [ + "OpenposeRenderNode" + ], + { + "title_aux": "ComfyUI-ultimate-openpose-render" + } + ], + "https://github.com/whatbirdisthat/cyberdolphin": [ + [ + "\ud83d\udc2c Gradio ChatInterface", + "\ud83d\udc2c OpenAI Advanced", + "\ud83d\udc2c OpenAI Compatible", + "\ud83d\udc2c OpenAI DALL\u00b7E", + "\ud83d\udc2c OpenAI Simple" + ], + { + "title_aux": "cyberdolphin" + } + ], + "https://github.com/whmc76/ComfyUI-Openpose-Editor-Plus": [ + [ + "CDL.OpenPoseEditorPlus" + ], + { + "title_aux": "ComfyUI-Openpose-Editor-Plus" + } + ], + "https://github.com/whmc76/ComfyUI-RemoveBackgroundSuite": [ + [ + "BiRefNetUltra_RBS", + "MaskProcessDetails_RBS", + "TransparentBackgroundUltra_RBS" + ], + { + "title_aux": "ComfyUI-RemoveBackgroundSuite" + } + ], + "https://github.com/whmc76/ComfyUI-UniversalToolkit": [ + [ + "AudioCropProcessUTK", + "CheckMask_UTK", + "CropByMask_UTK", + "DepthMapBlur_UTK", + "EmptyUnitGenerator_UTK", + "FillMaskedArea_UTK", + "ImageAndMaskPreview_UTK", + "ImageCombineAlpha_UTK", + "ImageConcatenateMulti_UTK", + "ImageConcatenate_UTK", + "ImageMaskScaleAs_UTK", + "ImagePadForOutpaintMasked_UTK", + "ImageRatioDetector_UTK", + "ImageRemoveAlpha_UTK", + "ImageScaleByAspectRatio_UTK", + "ImageScaleRestore_UTK", + "ImitationHueNode_UTK", + "LoadAudioPlusFromPath_UTK", + "LoadKontextPresets_UTK", + "LoraInfo_UTK", + "MaskAdd_UTK", + "MaskAnd_UTK", + "MaskSub_UTK", + "MathExpression_UTK", + "PurgeVRAM_UTK", + "RestoreCropBox_UTK", + "TextBoxNode_UTK", + "TextConcatenate_UTK", + "ThinkRemover_UTK", + "Video_Prompt_Helper" + ], + { + "title_aux": "ComfyUI-UniversalToolkit" + } + ], + "https://github.com/wildminder/ComfyUI-Chatterbox": [ + [ + "ChatterboxTTS", + "ChatterboxVC" + ], + { + "title_aux": "ComfyUI-Chatterbox" + } + ], + "https://github.com/wildminder/ComfyUI-KEEP": [ + [ + "KEEP_FaceUpscaleImage", + "KEEP_ModelLoader", + "KEEP_ProcessImageSequence" + ], + { + "title_aux": "ComfyUI-KEEP" + } + ], + "https://github.com/willchil/ComfyUI-Environment-Visualizer": [ + [ + "EnvironmentVisualizer", + "InterpolateEdges", + "MapEquirectangular" + ], + { + "title_aux": "ComfyUI-Environment-Visualizer" + } + ], + "https://github.com/windfancy/zsq_prompt": [ + [ + "BatchPromptJson", + "BatchPromptSelector", + "ConnectionString", + "DoubleCLIPEncode", + "FloatMathOperation", + "ImageAddText", + "ImageEmpty", + "IndexString", + "IntMathOperation", + "JoinImageBatch", + "LLMImage", + "LLMText", + "OptionString", + "PortraitStyler", + "SaveJpgImage", + "StringInput", + "ZSQPixelLatent", + "ZSQRatioLatent", + "ZSQShowINT", + "ZSQShowText", + "checkpoint_sampler", + "controlnetStack", + "controlnetStack_2", + "imageConcat", + "imageCount", + "imageCrop", + "imageDetailTransfer", + "imageFilter", + "imageFlip", + "imageGaussianBlur", + "imageHug", + "imageRGB", + "imageRatio", + "imageResize", + "imageRotate", + "imageSaveSimple", + "imageScaleDown", + "imageScaleDownBy", + "imageSharpen", + "imageSize", + "imageTilesFromBatch", + "imagesSplitImage", + "loraStack", + "loraStack_2", + "stylesSelector", + "zsqcheckpoint", + "zsqcontrolnet", + "zsqsampler" + ], + { + "title_aux": "zsq_prompt" + } + ], + "https://github.com/wings6407/ComfyUI_HBH-image_overlay": [ + [ + "HBH_ImageCoordinatePicker", + "HBH_ImageCoordinatePreview", + "HBH_ImageInteractivePicker", + "HBH_ImageOverlay", + "HBH_ImageOverlayPreview", + "HBH_ImagePreview" + ], + { + "title_aux": "ComfyUI_HBH-image_overlay" + } + ], + "https://github.com/wirytiox/ComfyUI-SelectStringFromListWithIndex": [ + [ + "StringFromList" + ], + { + "title_aux": "ComfyUI-SelectStringFromListWithIndex" + } + ], + "https://github.com/withmpx/mpx-comfyui-nodes": [ + [ + "Agent_PickBestImageFromList", + "Agent_ReflectionOnImageList", + "ImagesTo3DModels", + "LoadImageData", + "ObjectListToImageList", + "PickFromList", + "SaveModelsToDisk", + "ShowList", + "ShowString", + "StringListToStringList", + "StringListToText", + "TextToImage", + "TextToList", + "TextToObjectList", + "TextToScriptBreakdown", + "TextToStory", + "TextToText", + "TransformObjectList", + "TwoTextToText" + ], + { + "title_aux": "mpx-comfyui-nodes" + } + ], + "https://github.com/wjl0313/ComfyUI_KimNodes": [ + [ + "Add_ImageMetadata", + "BoundingBox_Cropper", + "Crop_Paste", + "Distribute_Icons", + "Edge_Element_Cropper", + "ExtractDifferenceLora", + "IconDistributeByGrid", + "Icon_Position_Cropper", + "Image_Classification", + "Image_PixelFilter", + "Image_Resize", + "KimFilter", + "KimHDR", + "LoRA_Metadata_Reader", + "LoadImage_Metadata", + "Manual_MetadataInput", + "Mask_Noise_Cleaner", + "Mask_White_Area_Ratio", + "MaxLength_ImageListSelector", + "Percentage_Cropper", + "Pixelate_Filter", + "Prompt_Text", + "Save_Image", + "Seamless_Icon_Generator", + "Split_Mask", + "Text_Match", + "Text_Processor", + "Transparent_Area_Cropper", + "Transparent_Image_Filter", + "Whitening_Node", + "YOLOWorld_Match", + "YOLO_Crop", + "YOLO_Multi_Crop" + ], + { + "title_aux": "ComfyUI_KimNodes" + } + ], + "https://github.com/wmatson/easy-comfy-nodes": [ + [ + "EZAssocDictNode", + "EZAssocImgNode", + "EZAssocStrNode", + "EZEmptyDictNode", + "EZHttpPostNode", + "EZLoadImgBatchFromUrlsNode", + "EZLoadImgFromUrlNode", + "EZRemoveImgBackground", + "EZS3Uploader" + ], + { + "title_aux": "easy-comfy-nodes" + } + ], + "https://github.com/wmpmiles/comfyui-some-image-processing-stuff": [ + [ + "Blur Mask", + "Color Grading", + "Latent Zero Mask", + "Mask-Crop Inpaint | Post", + "Mask-Crop Inpaint | Pre", + "Mask-Crop | Post", + "Mask-Crop | Pre", + "Resample Image", + "Resample Latent", + "Resample Mask", + "Resampler | Area", + "Resampler | Jinc-Lanczos", + "Resampler | Lanczos", + "Resampler | Mitchell-Netravali", + "Resampler | Nearest-Neighbor", + "Resampler | Triangle", + "Scaler | Area", + "Scaler | Fixed", + "Scaler | Megapixels", + "Scaler | Pixel Deltas", + "Scaler | Side", + "Scaler | Sides Unlinked" + ], + { + "title_aux": "comfyui-some-image-processing-stuff" + } + ], + "https://github.com/woct0rdho/ComfyUI-RadialAttn": [ + [ + "PatchRadialAttn" + ], + { + "title_aux": "ComfyUI-RadialAttn" + } + ], + "https://github.com/wolfden/ComfyUi_PromptStylers": [ + [ + "SDXLPromptStylerAll", + "SDXLPromptStylerHorror", + "SDXLPromptStylerMisc", + "SDXLPromptStylerbyArtist", + "SDXLPromptStylerbyCamera", + "SDXLPromptStylerbyComposition", + "SDXLPromptStylerbyCyberpunkSurrealism", + "SDXLPromptStylerbyDepth", + "SDXLPromptStylerbyEnvironment", + "SDXLPromptStylerbyFantasySetting", + "SDXLPromptStylerbyFilter", + "SDXLPromptStylerbyFocus", + "SDXLPromptStylerbyImpressionism", + "SDXLPromptStylerbyLighting", + "SDXLPromptStylerbyMileHigh", + "SDXLPromptStylerbyMood", + "SDXLPromptStylerbyMythicalCreature", + "SDXLPromptStylerbyOriginal", + "SDXLPromptStylerbyQuantumRealism", + "SDXLPromptStylerbySteamPunkRealism", + "SDXLPromptStylerbySubject", + "SDXLPromptStylerbySurrealism", + "SDXLPromptStylerbyTheme", + "SDXLPromptStylerbyTimeofDay", + "SDXLPromptStylerbyWyvern", + "SDXLPromptbyCelticArt", + "SDXLPromptbyContemporaryNordicArt", + "SDXLPromptbyFashionArt", + "SDXLPromptbyGothicRevival", + "SDXLPromptbyIrishFolkArt", + "SDXLPromptbyRomanticNationalismArt", + "SDXLPromptbySportsArt", + "SDXLPromptbyStreetArt", + "SDXLPromptbyVikingArt", + "SDXLPromptbyWildlifeArt" + ], + { + "title_aux": "SDXL Prompt Styler (customized version by wolfden)" + } + ], + "https://github.com/wolfden/ComfyUi_String_Function_Tree": [ + [ + "StringFunction" + ], + { + "title_aux": "ComfyUi_String_Function_Tree" + } + ], + "https://github.com/wootwootwootwoot/ComfyUI-RK-Sampler": [ + [ + "RungeKuttaSampler" + ], + { + "author": "wootwootwootwoot", + "description": "Batched Runge-Kutta Samplers for ComfyUI", + "nickname": "ComfyUI-RK-Sampler", + "title": "ComfyUI-RK-Sampler", + "title_aux": "ComfyUI-RK-Sampler" + } + ], + "https://github.com/wqjuser/ComfyUI-Chat-Image": [ + [ + "LLMImageDescription" + ], + { + "title_aux": "ComfyUI-Chat-Image" + } + ], + "https://github.com/wu12023/ComfyUI-Image-Evaluation": [ + [ + "Clip_Score-\ud83d\udd2c", + "Dino_Score-\ud83d\udd2c" + ], + { + "title_aux": "ComfyUI-Image-Evaluation" + } + ], + "https://github.com/wujm424606/ComfyUi-Ollama-YN": [ + [ + "MyOllamaDeleteModel", + "MyOllamaGenerate", + "MyOllamaGenerateAdvance", + "MyOllamaLoadContext", + "MyOllamaSaveContext", + "MyOllamaSpecialGenerateAdvance", + "MyOllamaVision" + ], + { + "title_aux": "ComfyUi-Ollama-YN" + } + ], + "https://github.com/wutipong/ComfyUI-TextUtils": [ + [ + "Text Utils - Join N-Elements of String List", + "Text Utils - Join String List", + "Text Utils - Join Strings", + "Text Utils - Split String to List" + ], + { + "title_aux": "ComfyUI-TextUtils" + } + ], + "https://github.com/wwwins/ComfyUI-Simple-Aspect-Ratio": [ + [ + "SimpleAspectRatio" + ], + { + "title_aux": "ComfyUI-Simple-Aspect-Ratio" + } + ], + "https://github.com/wywywywy/ComfyUI-pause": [ + [ + "PauseWorkflowNode" + ], + { + "title_aux": "ComfyUI Pause Workflow Node" + } + ], + "https://github.com/xXAdonesXx/NodeGPT": [ + [ + "AppendAgent", + "Assistant", + "Chat", + "ChatGPT", + "CombineInput", + "Conditioning", + "CostumeAgent_1", + "CostumeAgent_2", + "CostumeMaster_1", + "Critic", + "DisplayString", + "DisplayTextAsImage", + "EVAL", + "Engineer", + "Executor", + "GroupChat", + "Image_generation_Conditioning", + "LM_Studio", + "LoadAPIconfig", + "LoadTXT", + "MemGPT", + "Memory_Excel", + "Model_1", + "Ollama", + "Output2String", + "Planner", + "Scientist", + "TextCombine", + "TextGeneration", + "TextGenerator", + "TextInput", + "TextOutput", + "UserProxy", + "llama-cpp", + "llava", + "oobaboogaOpenAI" + ], + { + "title_aux": "NodeGPT" + } + ], + "https://github.com/xfgexo/EXO-Custom-ComfyUI-Nodes": [ + [ + "ComfyUI_EXO_Clip_Text_Encode", + "ComfyUI_EXO_DisplayText", + "ComfyUI_EXO_FluxSampler", + "ComfyUI_EXO_FluxSamplerMini", + "ComfyUI_EXO_ImageRescale", + "ComfyUI_EXO_LatentImageSize", + "ComfyUI_EXO_LatentImageSizeX", + "ComfyUI_EXO_Notes", + "ComfyUI_EXO_NumericValue", + "ComfyUI_EXO_PromptBuilderDeluxe", + "ComfyUI_EXO_SaveText", + "ComfyUI_EXO_TranslateText" + ], + { + "title_aux": "EXO Custom ComfyUI Nodes" + } + ], + "https://github.com/xhiroga/ComfyUI-FramePackWrapper_PlusOne": [ + [ + "DownloadAndLoadFramePackModel", + "FramePackFindNearestBucket", + "FramePackLoraSelect", + "FramePackSampler", + "FramePackSampler_F1", + "FramePackSingleFrameResizeSampler", + "FramePackSingleFrameSampler", + "FramePackTimestampedTextEncode", + "FramePackTorchCompileSettings", + "LoadFramePackModel" + ], + { + "title_aux": "ComfyUI-FramePackWrapper_PlusOne" + } + ], + "https://github.com/xiaogui8dangjia/Comfyui-imagetoSTL": [ + [ + "ImageToSTLNode" + ], + { + "title_aux": "Comfyui-imagetoSTL" + } + ], + "https://github.com/xiaowc-lib/comfyui-dynamic-params": [ + [ + "DynamicParams" + ], + { + "title_aux": "Comfyui-Dynamic-Params" + } + ], + "https://github.com/xiaoxiaodesha/hd_node": [ + [ + "Combine HDMasks", + "Cover HDMasks", + "HD FaceIndex", + "HD GetMaskArea", + "HD Image Levels", + "HD SmoothEdge", + "HD UltimateSDUpscale" + ], + { + "title_aux": "hd-nodes-comfyui" + } + ], + "https://github.com/xingBaGan/ComfyUI-connect-ui": [ + [ + "SaveImageByWebsocket", + "reciveImageByWebsocket" + ], + { + "title_aux": "ComfyUI-connect-ui" + } + ], + "https://github.com/xlinx/ComfyUI-decadetw-auto-messaging-realtime": [ + [ + "Auto-MSG-ALL", + "Auto-MSG-Discord-Bot", + "Auto-MSG-Line-Notify", + "Auto-MSG-Telegram-Bot" + ], + { + "title_aux": "ComfyUI-decadetw-auto-messaging-realtime" + } + ], + "https://github.com/xlinx/ComfyUI-decadetw-auto-prompt-llm": [ + [ + "Auto-LLM-Chat", + "Auto-LLM-Text", + "Auto-LLM-Text-Vision", + "Auto-LLM-Vision" + ], + { + "title_aux": "ComfyUI-decadetw-auto-prompt-llm" + } + ], + "https://github.com/xlinx/ComfyUI-decadetw-spout-syphon-im-vj": [ + [ + "Im-SD-VJ-SPOUT", + "Im-SD-VJ-SYPHON" + ], + { + "title_aux": "ComfyUI-decadetw-spout-syphon-im-vj" + } + ], + "https://github.com/xliry/ComfyUI_SendDiscord": [ + [ + "SendDiscord" + ], + { + "title_aux": "ComfyUI_SendDiscord" + } + ], + "https://github.com/xmarre/TorchCompileModel_LoRASafe": [ + [ + "TorchCompileModel_LoRASafe" + ], + { + "title_aux": "LoRA-Safe TorchCompile" + } + ], + "https://github.com/xobiomesh/ComfyUI_xObiomesh": [ + [ + "OllamaModelSelect", + "OllamaTextGen", + "ShowText_xO", + "xO_ComfyUIPortRunner", + "xO_GetImageFilename", + "xO_LoadRecentFile", + "xO_TestScriptRunner", + "xO_WorkflowRunner" + ], + { + "title_aux": "ComfyUI Neural Nodes" + } + ], + "https://github.com/xs315431/Comfyui_Get_promptId": [ + [ + "GetPromptId", + "SuccessCallback" + ], + { + "title_aux": "Comfyui_Get_promptId" + } + ], + "https://github.com/xuhongming251/ComfyUI-GPEN": [ + [ + "FaceEnhancement" + ], + { + "title_aux": "ComfyUI-GPEN" + } + ], + "https://github.com/xuhongming251/ComfyUI-Jimeng": [ + [ + "JimengAPIClient", + "JimengFirstLastFrame2Video", + "JimengImage2Video", + "PreviewVideoFromUrl" + ], + { + "title_aux": "ComfyUI-Jimeng" + } + ], + "https://github.com/xuhongming251/ComfyUI-MuseTalkUtils": [ + [ + "MuseTalkPostprocess", + "MuseTalkPreprocess", + "MuseTalkTrain", + "MuseTalkTrainPreprocess", + "MuseTalkUncropMask" + ], + { + "title_aux": "ComfyUI-MuseTalkUtils" + } + ], + "https://github.com/xuhongming251/ComfyUI_Camera": [ + [ + "Load Image From Local Camera", + "Save Image To Local Camera" + ], + { + "title_aux": "ComfyUI_Camera" + } + ], + "https://github.com/yamanacn/comfyui_kontext_Analyze": [ + [ + "KontextDuoImageAnalyzer" + ], + { + "title_aux": "ComfyUI Kontext Duo Image Analyzer" + } + ], + "https://github.com/yanhuifair/comfyui-janus": [ + [ + "JanusProImageGenerationNode", + "JanusProModelLoaderNode", + "JanusProMultimodalUnderstandingNode" + ], + { + "title_aux": "comfyui-janus" + } + ], + "https://github.com/yanlang0123/ComfyUI_Lam": [ + [ + "AppParams", + "AspectRatio", + "AutioInfo", + "AutioPath", + "DoWhileEnd", + "DoWhileStart", + "EasyPromptSelecto", + "FaceFusion", + "ForEnd", + "ForInnerEnd", + "ForInnerStart", + "ForStart", + "GLM3Prompt", + "IdentifyingQR", + "IfInnerExecute", + "Image2Video", + "ImageAddMask", + "ImageBlank", + "ImageClone", + "ImageCropFaces", + "ImageLama", + "ImageToMasks", + "JyAnimationGroup", + "JyAnimationIn", + "JyAnimationOut", + "JyAudio2CaptionsGroup", + "JyAudioNative", + "JyCaptionsNative", + "JyEffectNative", + "JyMediaAnimation", + "JyMediaNative", + "JyMultiAudioGroup", + "JyMultiCaptionsGroup", + "JyMultiEffectGroup", + "JyMultiMediaGroup", + "JySaveDraft", + "JySaveOutDraft", + "JyTransition", + "LAM.OpenPoseEditorPlus", + "LamCommonHidden", + "LamCommonNames", + "LamCommonPrint", + "LamCommonPrintNoOutput", + "LamFaceAnalysisModels", + "LamGetPngInfo", + "LamHeyGemNode", + "LamHeyGemQueryNode", + "LamLoadImageBase64", + "LamLoadPathImage", + "LamLoadVideo", + "LamReadFileList", + "LamSamplerName", + "LamSaveAudio", + "LamSaveOnly", + "LamSaveVideo", + "LamScheduler", + "LamSwitcherCase", + "LamViewVideo", + "LamViewVideoOut", + "LoadDirImgPaths", + "LoadReplaceImage", + "LongTextToList", + "MultiControlNetApply", + "MultiGLIGENTextBoxApply", + "MultiIPAdapterRegional", + "MultiIntFormula", + "MultiMergeAudio", + "MultiMergeVideos", + "MultiParamFormula", + "MultiTextConcatenate", + "MultiTextEncode", + "MultiTextEncodeAdvanced", + "MultiTextSelelct", + "MultiTextSetArea", + "MultiTextSetGligen", + "MultiTextSetMask", + "OutDoWhileEnd", + "OutDoWhileStart", + "PreviewImageLam", + "PromptTranslator", + "QRCode", + "SaveImageLam", + "SaveImgOutputLam", + "SectionEnd", + "SectionStart", + "StyleSelecto", + "Text2AutioEdgeTts", + "TextListSelelct", + "VideoAddAudio", + "VideoExtractAudio", + "VideoFaceFusion", + "VideoPath", + "WaitImagSelector", + "ZhPromptTranslator" + ], + { + "title_aux": "ComfyUI_Lam" + } + ], + "https://github.com/yasser-baalla/comfyUI-SemanticImageFetch": [ + [ + "ColorGradeSampler", + "ColorGrading", + "SemanticImageFetch" + ], + { + "title_aux": "comfyUI-SemanticImageFetch" + } + ], + "https://github.com/ycchanau/ComfyUI_Preview_Magnifier": [ + [ + "YC.ImageComparerMagnifier", + "YC.PreviewImageMagnifier", + "YC.XYPreviewImageMagnifier" + ], + { + "title_aux": "ComfyUI Preview Magnifier" + } + ], + "https://github.com/ycyy/ComfyUI-YCYY-LoraInfo": [ + [ + "LoraInfo" + ], + { + "title_aux": "ComfyUI-YCYY-LoraInfo" + } + ], + "https://github.com/yffyhk/comfyui_auto_danbooru": [ + [ + "GetDanbooru", + "TagEncode" + ], + { + "title_aux": "comfyui_auto_danbooru" + } + ], + "https://github.com/yhayano-ponotech/ComfyUI-Fal-API-Flux": [ + [ + "FalAPIFluxControlNetConfigNode", + "FalAPIFluxControlNetUnionConfigNode", + "FalAPIFluxDevCannyWithLoraNode", + "FalAPIFluxDevImageToImageNode", + "FalAPIFluxDevNode", + "FalAPIFluxDevWithLoraAndControlNetImageToImageNode", + "FalAPIFluxDevWithLoraAndControlNetInpaintNode", + "FalAPIFluxDevWithLoraAndControlNetNode", + "FalAPIFluxDevWithLoraImageToImageNode", + "FalAPIFluxDevWithLoraInpaintNode", + "FalAPIFluxDevWithLoraNode", + "FalAPIFluxLoraConfigNode", + "FalAPIFluxProCannyNode", + "FalAPIFluxProDepthNode", + "FalAPIFluxProFillNode", + "FalAPIFluxProNode", + "FalAPIFluxProReduxNode", + "FalAPIFluxProV11Node", + "FalAPIFluxProV11UltraNode" + ], + { + "title_aux": "ComfyUI-Fal-API-Flux" + } + ], + "https://github.com/yhayano-ponotech/comfyui-save-image-local": [ + [ + "Local Save" + ], + { + "title_aux": "ComfyUI Local Save Node" + } + ], + "https://github.com/yhayano-ponotech/comfyui-stability-ai-api": [ + [ + "Preview3DModel", + "Save3DModel", + "StabilityControlSketch", + "StabilityControlStructure", + "StabilityControlStyle", + "StabilityEdit", + "StabilityImageCore", + "StabilityImageSD3", + "StabilityImageToVideo", + "StabilityImageUltra", + "StabilityUpscaleConservative", + "StabilityUpscaleCreative", + "StabilityUpscaleFast", + "StableFast3D", + "StablePointAware3D" + ], + { + "title_aux": "ComfyUI-Stability-AI-API" + } + ], + "https://github.com/yichengup/ComfyUI-LinearTransition": [ + [ + "GradientTransition", + "LinearTransition" + ], + { + "title_aux": "ComfyUI-LinearTransition" + } + ], + "https://github.com/yichengup/ComfyUI-YCNodes": [ + [ + "AdvancedImageSelector", + "DynamicThreshold", + "ImageBatchSelector", + "ImageBlendResize", + "ImageIC", + "ImageICAdvanced", + "ImageLoaderAdvanced", + "ImageMirror", + "ImageMosaic", + "ImageRotate", + "ImageSelector", + "ImageUpscaleTiled", + "IrregularToEllipseMask", + "LoadImagesFromFolder", + "MaskBatchComposite", + "MaskBatchCopy", + "MaskContourFillNode", + "MaskCropRestore_YC", + "MaskCrop_YC", + "MaskFilterBySolidity", + "MaskFromBatch", + "MaskPreviewNode", + "MaskRepeatBatch", + "MaskSmartValleySplit", + "MaskSplitFilter", + "MaskTopNFilter", + "TextKeyword", + "YC Extract Number", + "YC Mask Condition Switch", + "YC Seed List", + "YC Super Selector", + "YC Text Condition Switch", + "YC Text Index Switch", + "YC Universal Gate", + "YCMaskComposite", + "YCRemapMaskRange", + "YCTextImageGenerator", + "YC_FiveTextCombineNode", + "YC_Image_Save", + "YC_SingleTextNode", + "YC_textReplaceNode" + ], + { + "title_aux": "ComfyUI-YCNodes" + } + ], + "https://github.com/yichengup/ComfyUI_Yc_JanusPro": [ + [ + "ImageAnalyzer", + "JanusChatAnalyzer", + "JanusImageGenerator", + "JanusProLoader" + ], + { + "title_aux": "ComfyUI_Yc_JanusPro" + } + ], + "https://github.com/yichengup/Comfyui-Deepseek": [ + [ + "DeepseekAdvancedNode", + "DeepseekNode", + "DeepseekReasonerNode", + "SiliconDeepseekChat", + "SiliconDeepseekReasoner" + ], + { + "title_aux": "Comfyui-Deepseek" + } + ], + "https://github.com/yichengup/Comfyui-Ycanvas": [ + [ + "CanvasNode" + ], + { + "title_aux": "Comfyui-Ycanvas" + } + ], + "https://github.com/yichengup/Comfyui_Flux_Style_Adjust": [ + [ + "StyleModelAdvancedApply" + ], + { + "title_aux": "Comfyui_Flux_Style_Adjust (Redux)" + } + ], + "https://github.com/yichengup/Comfyui_Redux_Advanced": [ + [ + "StyleAdvancedApply", + "YC_LG_Redux" + ], + { + "title_aux": "Comfyui_Redux_Advanced" + } + ], + "https://github.com/yichengup/comfyui-face-liquify": [ + [ + "FaceLiquifyNode" + ], + { + "title_aux": "comfyui-face-liquify" + } + ], + "https://github.com/yiwangsimple/ComfyUI_DW_Chat": [ + [ + "DeepSeekChatNode", + "DeepSeekTranslator", + "ErrorLogNode", + "ExecutionTime", + "FileBasedChatNode", + "FluxPromptEngineeringNode", + "Gemini1_5Text", + "Gemini1_5Vision", + "GeminiFluxPrompt", + "Gemma2PromptNode", + "GemmaDialogueNode", + "GithubLinkNode", + "GroqChatNode", + "MoonshotMultiChatNode", + "MoonshotSingleChatNode", + "OllamaImageToText", + "OllamaPromptExtractor", + "OllamaTextToText", + "PaliGemma3bCaptioner", + "PromptEngineeringNode", + "PromptExtractorNode", + "Qwen2VLCaption", + "Qwen2VLLocalCaption", + "SD3LongCaptionerV2", + "SDPromptAgent", + "dwimage2" + ], + { + "title_aux": "ComfyUI_DW_Chat" + } + ], + "https://github.com/yiwangsimple/florence_dw": [ + [ + "Florence2", + "Florence2Postprocess", + "Florence2PostprocessAll", + "LoadFlorence2Model" + ], + { + "title_aux": "florence_dw" + } + ], + "https://github.com/yolain/ComfyUI-Easy-Use": [ + [ + "dynamicThresholdingFull", + "easy LLLiteLoader", + "easy XYInputs: CFG Scale", + "easy XYInputs: Checkpoint", + "easy XYInputs: ControlNet", + "easy XYInputs: Denoise", + "easy XYInputs: FluxGuidance", + "easy XYInputs: Lora", + "easy XYInputs: ModelMergeBlocks", + "easy XYInputs: NegativeCond", + "easy XYInputs: NegativeCondList", + "easy XYInputs: PositiveCond", + "easy XYInputs: PositiveCondList", + "easy XYInputs: PromptSR", + "easy XYInputs: Sampler/Scheduler", + "easy XYInputs: Seeds++ Batch", + "easy XYInputs: Steps", + "easy XYPlot", + "easy XYPlotAdvanced", + "easy a1111Loader", + "easy ab", + "easy anythingIndexSwitch", + "easy anythingInversedSwitch", + "easy applyBrushNet", + "easy applyFooocusInpaint", + "easy applyInpaint", + "easy applyPowerPaint", + "easy batchAnything", + "easy blocker", + "easy boolean", + "easy cascadeKSampler", + "easy cascadeLoader", + "easy ckptNames", + "easy cleanGpuUsed", + "easy clearCacheAll", + "easy clearCacheKey", + "easy comfyLoader", + "easy compare", + "easy conditioningIndexSwitch", + "easy controlnetLoader", + "easy controlnetLoader++", + "easy controlnetLoaderADV", + "easy controlnetNames", + "easy controlnetStack", + "easy controlnetStackApply", + "easy convertAnything", + "easy detailerFix", + "easy float", + "easy fluxLoader", + "easy forLoopEnd", + "easy forLoopStart", + "easy fullCascadeKSampler", + "easy fullLoader", + "easy fullkSampler", + "easy globalSeed", + "easy hiresFix", + "easy humanSegmentation", + "easy hunyuanDiTLoader", + "easy icLightApply", + "easy if", + "easy ifElse", + "easy imageBatchToImageList", + "easy imageChooser", + "easy imageColorMatch", + "easy imageConcat", + "easy imageCount", + "easy imageCropFromMask", + "easy imageDetailTransfer", + "easy imageIndexSwitch", + "easy imageInsetCrop", + "easy imageInterrogator", + "easy imageListToImageBatch", + "easy imagePixelPerfect", + "easy imageRatio", + "easy imageRemBg", + "easy imageSave", + "easy imageScaleDown", + "easy imageScaleDownBy", + "easy imageScaleDownToSize", + "easy imageScaleToNormPixels", + "easy imageSize", + "easy imageSizeByLongerSide", + "easy imageSizeBySide", + "easy imageSplitGrid", + "easy imageSplitList", + "easy imageSplitTiles", + "easy imageSwitch", + "easy imageTilesFromBatch", + "easy imageToBase64", + "easy imageToMask", + "easy imageUncropFromBBOX", + "easy imagesCountInDirectory", + "easy imagesSplitImage", + "easy indexAnything", + "easy injectNoiseToLatent", + "easy instantIDApply", + "easy instantIDApplyADV", + "easy int", + "easy ipadapterApply", + "easy ipadapterApplyADV", + "easy ipadapterApplyEmbeds", + "easy ipadapterApplyEncoder", + "easy ipadapterApplyFaceIDKolors", + "easy ipadapterApplyFromParams", + "easy ipadapterApplyRegional", + "easy ipadapterStyleComposition", + "easy isFileExist", + "easy isMaskEmpty", + "easy isNone", + "easy isSDXL", + "easy joinImageBatch", + "easy joyCaption2API", + "easy joyCaption3API", + "easy kSampler", + "easy kSamplerCustom", + "easy kSamplerDownscaleUnet", + "easy kSamplerInpainting", + "easy kSamplerLayerDiffusion", + "easy kSamplerSDTurbo", + "easy kSamplerTiled", + "easy kolorsLoader", + "easy latentCompositeMaskedWithCond", + "easy latentNoisy", + "easy lengthAnything", + "easy loadImageBase64", + "easy loadImagesForLoop", + "easy loraNames", + "easy loraPromptApply", + "easy loraStack", + "easy loraStackApply", + "easy loraSwitcher", + "easy makeImageForICLora", + "easy mathFloat", + "easy mathInt", + "easy mathString", + "easy mochiLoader", + "easy negative", + "easy outputToList", + "easy pipeBatchIndex", + "easy pipeEdit", + "easy pipeEditPrompt", + "easy pipeIn", + "easy pipeOut", + "easy pipeToBasicPipe", + "easy pixArtLoader", + "easy pixels", + "easy portraitMaster", + "easy poseEditor", + "easy positive", + "easy preDetailerFix", + "easy preMaskDetailerFix", + "easy preSampling", + "easy preSamplingAdvanced", + "easy preSamplingCascade", + "easy preSamplingCustom", + "easy preSamplingDynamicCFG", + "easy preSamplingLayerDiffusion", + "easy preSamplingLayerDiffusionADDTL", + "easy preSamplingNoiseIn", + "easy preSamplingSdTurbo", + "easy prompt", + "easy promptAwait", + "easy promptConcat", + "easy promptLine", + "easy promptList", + "easy promptReplace", + "easy pulIDApply", + "easy pulIDApplyADV", + "easy rangeFloat", + "easy rangeInt", + "easy removeLocalImage", + "easy samLoaderPipe", + "easy saveImageLazy", + "easy saveText", + "easy saveTextLazy", + "easy seed", + "easy seedList", + "easy showAnything", + "easy showAnythingLazy", + "easy showLoaderSettingsNames", + "easy showSpentTime", + "easy showTensorShape", + "easy sleep", + "easy sliderControl", + "easy stableDiffusion3API", + "easy string", + "easy styleAlignedBatchAlign", + "easy stylesSelector", + "easy sv3dLoader", + "easy svdLoader", + "easy textIndexSwitch", + "easy textSwitch", + "easy ultralyticsDetectorPipe", + "easy unSampler", + "easy whileLoopEnd", + "easy whileLoopStart", + "easy wildcards", + "easy wildcardsMatrix", + "easy xyAny", + "easy zero123Loader" + ], + { + "title_aux": "ComfyUI Easy Use" + } + ], + "https://github.com/yolanother/ComfyUI-Save16bitPng": [ + [ + "SaveImageARGB16PNG" + ], + { + "title_aux": "Save Uncompressed 16 Bit PNG" + } + ], + "https://github.com/yolanother/DTAIComfyImageSubmit": [ + [ + "DTSimpleSubmitImage", + "DTSubmitImage" + ], + { + "title_aux": "Comfy AI DoubTech.ai Image Sumission Node" + } + ], + "https://github.com/yolanother/DTAIComfyLoaders": [ + [ + "DTCLIPLoader", + "DTCLIPVisionLoader", + "DTCheckpointLoader", + "DTCheckpointLoaderSimple", + "DTControlNetLoader", + "DTDiffControlNetLoader", + "DTDiffusersLoader", + "DTGLIGENLoader", + "DTLoadImage", + "DTLoadImageMask", + "DTLoadLatent", + "DTLoraLoader", + "DTLorasLoader", + "DTStyleModelLoader", + "DTUpscaleModelLoader", + "DTVAELoader", + "DTunCLIPCheckpointLoader" + ], + { + "title_aux": "Comfy UI Online Loaders" + } + ], + "https://github.com/yolanother/DTAIComfyPromptAgent": [ + [ + "DTPromptAgent", + "DTPromptAgentString" + ], + { + "title_aux": "Comfy UI Prompt Agent" + } + ], + "https://github.com/yolanother/DTAIComfyQRCodes": [ + [ + "QRCode" + ], + { + "title_aux": "Comfy UI QR Codes" + } + ], + "https://github.com/yolanother/DTAIComfyVariables": [ + [ + "DTCLIPTextEncode", + "DTSingleLineStringVariable", + "DTSingleLineStringVariableNoClip", + "FloatVariable", + "IntVariable", + "StringFormat", + "StringFormatSingleLine", + "StringVariable" + ], + { + "title_aux": "Variables for Comfy UI" + } + ], + "https://github.com/yolanother/DTAIImageToTextNode": [ + [ + "DTAIImageToTextNode", + "DTAIImageUrlToTextNode" + ], + { + "title_aux": "Image to Text Node" + } + ], + "https://github.com/yondonfu/ComfyUI-Background-Edit": [ + [ + "BackgroundColor", + "Composite", + "GaussianBlur" + ], + { + "title_aux": "ComfyUI-Background-Edit" + } + ], + "https://github.com/yondonfu/ComfyUI-Torch-Compile": [ + [ + "TorchCompileLoadControlNet", + "TorchCompileLoadVAE" + ], + { + "title_aux": "ComfyUI-Torch-Compile" + } + ], + "https://github.com/yorkane/ComfyUI-KYNode": [ + [ + "KY_AnyByIndex", + "KY_AnyToList", + "KY_FilePathAnalyzer-", + "KY_FileSequenceAnalyzer", + "KY_JoinToString", + "KY_LoadImageFrom", + "KY_LoadImagesFromFolder", + "KY_MathExpression", + "KY_OpenAICaptionImage", + "KY_OpenAICaptionImages", + "KY_OpenAIChat", + "KY_ReadImage", + "KY_RegexExtractor", + "KY_RegexReplace", + "KY_SaveImageToPath" + ], + { + "title_aux": "ComfyUI-KYNode" + } + ], + "https://github.com/younyokel/comfyui_prompt_formatter": [ + [ + "CLIPTextEncodeFormatter", + "TextAppendFormatter", + "TextOnlyFormatter" + ], + { + "title_aux": "ComfyUI Prompt Formatter" + } + ], + "https://github.com/youyegit/tdxh_node_comfyui": [ + [ + "TdxhBoolNumber", + "TdxhClipVison", + "TdxhControlNetApply", + "TdxhControlNetProcessor", + "TdxhFloatInput", + "TdxhImageToSize", + "TdxhImageToSizeAdvanced", + "TdxhImg2ImgLatent", + "TdxhIntInput", + "TdxhLoraLoader", + "TdxhOnOrOff", + "TdxhReference", + "TdxhStringInput", + "TdxhStringInputTranslator", + "TdxhToggleGuest", + "TdxhToggleMaster" + ], + { + "title_aux": "tdxh_node_comfyui" + } + ], + "https://github.com/yuan199696/add_text_2_img": [ + [ + "AddText" + ], + { + "title_aux": "add_text_2_img" + } + ], + "https://github.com/yuan199696/chinese_clip_encode": [ + [ + "ChineseCLIPEncode" + ], + { + "title_aux": "chinese_clip_encode" + } + ], + "https://github.com/yushan777/ComfyUI-Y7-SBS-2Dto3D": [ + [ + "Y7_SideBySide", + "Y7_VideoSideBySide" + ], + { + "title_aux": "ComfyUI-Y7-SBS-2Dto3D" + } + ], + "https://github.com/yushan777/ComfyUI-Y7Nodes": [ + [ + "Y7Nodes_CLIP_TokenCounter", + "Y7Nodes_CatchEditTextNodeDual", + "Y7Nodes_Grid2Batch", + "Y7Nodes_ImageRow", + "Y7Nodes_ImageSizePresets", + "Y7Nodes_PromptEnhancerFlux", + "Y7Nodes_ShowAnything", + "Y7Nodes_SmolVLM", + "Y7Nodes_T5_TokenCounter", + "Y7Nodes_Text" + ], + { + "title_aux": "Y7Nodes for ComfyUI" + } + ], + "https://github.com/yuvraj108c/ComfyUI-Depth-Anything-Tensorrt": [ + [ + "DepthAnythingEngineBuilder", + "DepthAnythingTensorrt" + ], + { + "title_aux": "ComfyUI Depth Anything TensorRT" + } + ], + "https://github.com/yuvraj108c/ComfyUI-Dwpose-Tensorrt": [ + [ + "DwposeTensorrt", + "LoadDwposeTensorrtModels" + ], + { + "title_aux": "ComfyUI Dwpose TensorRT" + } + ], + "https://github.com/yuvraj108c/ComfyUI-FLOAT": [ + [ + "FloatProcess", + "LoadFloatModels" + ], + { + "title_aux": "ComfyUI FLOAT" + } + ], + "https://github.com/yuvraj108c/ComfyUI-Facerestore-Tensorrt": [ + [ + "FaceRestoreTensorrt" + ], + { + "title_aux": "ComfyUI Facerestore TensorRT" + } + ], + "https://github.com/yuvraj108c/ComfyUI-PiperTTS": [ + [ + "PiperTTS" + ], + { + "title_aux": "ComfyUI PiperTTS" + } + ], + "https://github.com/yuvraj108c/ComfyUI-Pronodes": [ + [ + "ImagesSeekerNode", + "ImagesShufflerNode", + "LoadImageFromOutputDirectoryNode", + "LoadYoutubeVideoNode", + "PreviewVHSAudioNode", + "SaveAndOverwriteImageNode", + "VHSFilenamesToPathNode" + ], + { + "title_aux": "ComfyUI-Pronodes" + } + ], + "https://github.com/yuvraj108c/ComfyUI-Rife-Tensorrt": [ + [ + "RifeTensorrt" + ], + { + "title_aux": "ComfyUI Rife TensorRT" + } + ], + "https://github.com/yuvraj108c/ComfyUI-Thera": [ + [ + "LoadTheraModel", + "TheraProcess" + ], + { + "title_aux": "ComfyUI Thera" + } + ], + "https://github.com/yuvraj108c/ComfyUI-Upscaler-Tensorrt": [ + [ + "LoadUpscalerTensorrtModel", + "UpscalerTensorrt" + ], + { + "title_aux": "ComfyUI Upscaler TensorRT" + } + ], + "https://github.com/yuvraj108c/ComfyUI-Video-Depth-Anything": [ + [ + "LoadVideoDepthAnythingModel", + "VideoDepthAnythingProcess" + ], + { + "title_aux": "ComfyUI Video Depth Anything" + } + ], + "https://github.com/yuvraj108c/ComfyUI-Vsgan": [ + [ + "DepthAnythingTrtNode", + "TTSCapcutNode", + "UpscaleVideoTrtNode" + ], + { + "title_aux": "ComfyUI-Vsgan" + } + ], + "https://github.com/yuvraj108c/ComfyUI-Whisper": [ + [ + "Add Subtitles To Background", + "Add Subtitles To Frames", + "Apply Whisper", + "Resize Cropped Subtitles" + ], + { + "title_aux": "ComfyUI Whisper" + } + ], + "https://github.com/yuvraj108c/ComfyUI-YoloNasPose-Tensorrt": [ + [ + "YoloNasPoseEngineBuilder", + "YoloNasPoseTensorrt" + ], + { + "title_aux": "ComfyUI YoloNasPose Tensorrt" + } + ], + "https://github.com/yuvraj108c/ComfyUI_InvSR": [ + [ + "InvSRSampler", + "LoadInvSRModels" + ], + { + "title_aux": "ComfyUI InvSR" + } + ], + "https://github.com/yvann-ba/ComfyUI_Yvann-Nodes": [ + [ + "Audio Analysis", + "Audio IPAdapter Transitions", + "Audio Peaks Detection", + "Audio Prompt Schedule", + "Audio Remixer", + "Edit Audio Weights", + "Float to Int", + "Floats To Weights Strategy", + "Floats Visualizer", + "Invert Floats", + "Load Audio Separation Model", + "Mask To Float", + "Repeat Image To Count" + ], + { + "title_aux": "ComfyUI_Yvann-Nodes" + } + ], + "https://github.com/za-wa-n-go/ComfyUI_Zwng_Nodes": [ + [ + "ZwngLoadImagePathOrURL", + "ZwngPreviewImageAndMask", + "ZwngSimpleGoogleTranslater", + "ZwngSimplePhotoshopConnector" + ], + { + "title_aux": "ComfyUI_Zwng_Nodes" + } + ], + "https://github.com/zade23/Comfyui-Distill-Any-Depth": [ + [ + "DistillAnyDepthProcessImage", + "DownloadDistillAnyDepthModel" + ], + { + "title_aux": "Comfyui-Distill-Any-Depth" + } + ], + "https://github.com/zade23/Comfyui-MoGe2": [ + [ + "RunMoGe2Process" + ], + { + "title_aux": "ComfyUI-MoGe2" + } + ], + "https://github.com/zaheenrahman/ComfyUI-ColorCorrection": [ + [ + "ClothingColorCorrection" + ], + { + "title_aux": "ComfyUI-ColorCorrection" + } + ], + "https://github.com/zccrs/comfyui-dci": [ + [ + "Base64Decoder", + "Base64Encoder", + "BinaryFileLoader", + "BinaryFileSaver", + "DCIAnalysis", + "DCIFileNode", + "DCIFileSaver", + "DCIImage", + "DCIImagePreview", + "DCIPreviewNode", + "DCISampleImage", + "DCI_Analysis", + "DCI_Base64Decoder", + "DCI_Base64Encoder", + "DCI_BinaryFileLoader", + "DCI_BinaryFileSaver", + "DCI_DebLoader", + "DCI_DebPackager", + "DCI_DirectoryLoader", + "DCI_FileNode", + "DCI_FileSaver", + "DCI_Image", + "DCI_ImagePreview", + "DCI_PreviewNode", + "DCI_SampleImage", + "DebLoader", + "DebPackager", + "DirectoryLoader" + ], + { + "title_aux": "ComfyUI DCI" + } + ], + "https://github.com/zcfrank1st/Comfyui-Toolbox": [ + [ + "PreviewJson", + "PreviewVideo", + "SaveJson", + "TestJsonPreview" + ], + { + "title_aux": "Comfyui-Toolbox" + } + ], + "https://github.com/zcfrank1st/Comfyui-Yolov8": [ + [ + "Yolov8Detection", + "Yolov8Segmentation" + ], + { + "title_aux": "ComfyUI Yolov8" + } + ], + "https://github.com/zcfrank1st/comfyui_visual_anagrams": [ + [ + "VisualAnagramsAnimate", + "VisualAnagramsSample" + ], + { + "title_aux": "comfyui_visual_anagram" + } + ], + "https://github.com/zeeoale/PromptCreatorNode": [ + [ + "PromptCreatorNode" + ], + { + "title_aux": "PromptCreatorNodetraumakom Prompt Generator" + } + ], + "https://github.com/zentrocdot/ComfyUI-RealESRGAN_Upscaler": [ + [ + "\ud83d\ude80 Universal RealESRGAN Upscaler", + "\ud83e\uddf3 Show Data" + ], + { + "title_aux": "ComfyUI-RealESRGAN_Upscaler" + } + ], + "https://github.com/zentrocdot/ComfyUI-Simple_Image_To_Prompt": [ + [ + "\ud83d\udc41\ufe0f Image To Prompt", + "\ud83d\udc41\ufe0f Image To Prompt (NO UPDATE)", + "\ud83e\uddf3 Show Data" + ], + { + "title_aux": "ComfyUI-Simple_Image_To_Prompt" + } + ], + "https://github.com/zentrocdot/ComfyUI_Circle_Detection": [ + [ + "\u270f\ufe0f Input Data", + "\ud83d\udcc4 Show Data", + "\ud83d\udd2c Circle Detection (Hough)", + "\ud83d\udd2c Ellipse Detection (Simple)" + ], + { + "title_aux": "ComfyUI_Circle_Detection" + } + ], + "https://github.com/zer0TF/cute-comfy": [ + [ + "Cute.Placeholder" + ], + { + "title_aux": "Cute Comfy" + } + ], + "https://github.com/zer0thgear/zer0-comfy-utils": [ + [ + "List Combine Node (zer0)", + "Multiline String Node (zer0)", + "Prompt Minimizer And Splitter Node (zer0)", + "Quality Tag Prepend Node (zer0)", + "Tavern Card Creation Node (zer0)", + "Tavern Card Info Node (zer0)" + ], + { + "author": "zer0gear", + "description": "Dubiously useful nodes that I've made for my own use.", + "nickname": "zer0gear Comfy Utils", + "title": "zer0gear's Comfy Utilities", + "title_aux": "zer0 Comfy Utilities" + } + ], + "https://github.com/zeroxoxo/ComfyUI-Fast-Style-Transfer": [ + [ + "FastStyleTransfer", + "NeuralStyleTransfer", + "TrainFastStyleTransfer" + ], + { + "title_aux": "ComfyUI-Fast-Style-Transfer" + } + ], + "https://github.com/zfkun/ComfyUI_zfkun": [ + [ + "ZFLoadImagePath", + "ZFPreviewText", + "ZFPreviewTextMultiline", + "ZFShareScreen", + "ZFTextTranslation" + ], + { + "title_aux": "ComfyUI_zfkun" + } + ], + "https://github.com/zhangp365/ComfyUI-utils-nodes": [ + [ + "BooleanControlOutput", + "CheckpointLoaderSimpleWithSwitch", + "ColorCorrectOfUtils", + "ConcatTextOfUtils", + "CropByMaskToSpecificSize", + "DeepfaceAnalyzeFaceAttributes", + "DetectorForNSFW", + "EmptyConditioning", + "FloatMultipleAddLiteral", + "FrameAdjuster", + "GeminiPromptEnhance", + "GenderControlOutput", + "ImageAutoSelector", + "ImageBatchOneOrMore", + "ImageCompositeMaskedOneByOne", + "ImageCompositeMaskedWithSwitch", + "ImageCompositeWatermark", + "ImageConcanateOfUtils", + "ImageMaskColorAverage", + "ImageResizeTo8x", + "ImageTransition", + "ImageTransitionBottomToTop", + "ImageTransitionLeftToRight", + "ImageTransitionRightToLeft", + "ImageTransitionTopToBottom", + "ImagesConcanateToGrid", + "IntMultipleAddLiteral", + "LoadImageMaskWithSwitch", + "LoadImageMaskWithoutListDir", + "LoadImageWithSwitch", + "LoadImageWithoutListDir", + "MaskAreaComparison", + "MaskAutoSelector", + "MaskCoverFourCorners", + "MaskFastGrow", + "MaskFromFaceModel", + "MaskofCenter", + "MatchImageRatioToPreset", + "ModifyTextGender", + "NeedImageSizeAndCount", + "ReplicateRequstNode", + "SplitMask", + "TextInputAutoSelector", + "TextPreview", + "TorchCompileModelAdvanced", + "UpscaleImageWithModelIfNeed", + "VolcanoImageEditNode", + "VolcanoOutpaintingNode" + ], + { + "title_aux": "zhangp365/ComfyUI-utils-nodes" + } + ], + "https://github.com/zhangp365/ComfyUI_photomakerV2_native": [ + [ + "PhotoMakerEncodeV2", + "PhotoMakerLoaderV2" + ], + { + "title_aux": "ComfyUI_photomakerV2_native" + } + ], + "https://github.com/zhilemann/ComfyUI-moondream2": [ + [ + "moondream2_Caption", + "moondream2_DownLoad", + "moondream2_Encode", + "moondream2_Query" + ], + { + "title_aux": "ComfyUI-moondream2" + } + ], + "https://github.com/zhiselfly/ComfyUI-Alimama-ControlNet-compatible": [ + [ + "SD3AlimamaInpaintControlNetApplyAdvanced", + "SD3AlimamaInpaintControlNetLoader" + ], + { + "title_aux": "ComfyUI-Alimama-ControlNet-compatible" + } + ], + "https://github.com/zhongpei/ComfyUI-InstructIR": [ + [ + "InstructIRProcess", + "LoadInstructIRModel" + ], + { + "title_aux": "ComfyUI for InstructIR" + } + ], + "https://github.com/zhuanqianfish/ComfyUI-EasyNode": [ + [ + "EasyCaptureNode", + "EasyVideoOutputNode", + "SendImageWebSocket" + ], + { + "title_aux": "EasyCaptureNode for ComfyUI" + } + ], + "https://github.com/zhulu111/ComfyUI_Bxb": [ + [ + "bxbSwitch", + "sdBxb", + "sdBxb_saveImage", + "sdBxb_textInput" + ], + { + "title_aux": "ComfyUI_Bxb" + } + ], + "https://github.com/zichongc/ComfyUI-Attention-Distillation": [ + [ + "ADOptimizer", + "ADSampler", + "LoadDistiller", + "LoadPILImage", + "PureText", + "ResizeImage" + ], + { + "title_aux": "ComfyUI-Attention-Distillation" + } + ], + "https://github.com/ziwang-com/comfyui-deepseek-r1": [ + [ + "deep_gen", + "deep_load" + ], + { + "title_aux": "comfyui-deepseek-r1" + } + ], + "https://github.com/zl9739379/ComfyUI-ArkVideoGenerate": [ + [ + "ArkVideoGenerate" + ], + { + "title_aux": "ComfyUI-ArkVideoGenerate" + } + ], + "https://github.com/zmwv823/ComfyUI_Anytext": [ + [ + "UL_AnyText2Fonts", + "UL_AnyTextComposer", + "UL_AnyTextEncoder", + "UL_AnyTextFontImg", + "UL_AnyTextFormatter", + "UL_AnyTextLoader", + "UL_AnyTextSampler", + "UL_DiffusersCheckpointLoader", + "UL_DiffusersControlNetApplyAdvanced", + "UL_DiffusersControlNetLoader", + "UL_Image_Generation_Diffusers_Sampler", + "UL_Image_Generation_Glyph_ByT5", + "UL_Image_Generation_Glyph_ByT5_Checkponits_Loader", + "UL_Image_Generation_Glyph_ByT5_Font", + "UL_Image_Generation_JoyType_Font_Img", + "UL_Image_Generation_JoyType_Render_List", + "UL_Image_Process_Common_Cv2_Canny", + "UL_Translator", + "UL_TranslatorLoader", + "U_LoRAS" + ], + { + "title_aux": "ComfyUI_Anytext" + } + ], + "https://github.com/zohac/ComfyUI_ZC_DrawShape": [ + [ + "ZcDrawShape" + ], + { + "author": "Zohac", + "description": "nodes for artists, designers and animators.", + "nickname": "Zc DrawShape", + "title": "Zc DrawShape", + "title_aux": "ComfyUI_ZC_DrawShape" + } + ], + "https://github.com/zombieyang/sd-ppp": [ + [ + "CLIP Text Encode PS Regional", + "Get Image From Photoshop Layer", + "Send Images To Photoshop" + ], + { + "title_aux": "SD-PPP" + } + ], + "https://github.com/zubenelakrab/ComfyUI-ASV-Nodes": [ + [ + "ASVPromptGenerator" + ], + { + "title_aux": "ComfyUI-ASV-Nodes Node" + } + ], + "https://github.com/zygion/comfyui-zygion-util-nodes": [ + [ + "ItemListNode", + "SceneQueueNode", + "TemplateInputNode", + "TemplateProcessorNode", + "TriggerPassthroughNode" + ], + { + "title_aux": "i-zygion-util-nodes" + } + ], + "https://github.com/zzubnik/TT_TextTools": [ + [ + "TT_StoryCombiner", + "TT_TextFileSelectorNode", + "TT_TextInput", + "TT_TextReplacer" + ], + { + "title_aux": "TT_TextTools" + } + ], + "https://github.com/zzw5516/ComfyUI-zw-tools": [ + [ + "ZwPrompt", + "ZwPromptText" + ], + { + "title_aux": "ComfyUI-zw-tools" + } + ], + "https://raw.githubusercontent.com/1shadow1/hayo_comfyui_nodes/main/LZCNodes.py": [ + [ + "LoadPILImages", + "MergeImages", + "make_transparentmask", + "tensor_trans_pil", + "words_generatee" + ], + { + "title_aux": "Hayo comfyui nodes" + } + ], + "https://raw.githubusercontent.com/CaptainGrock/ComfyUIInvisibleWatermark/main/Invisible%20Watermark.py": [ + [ + "Apply Invisible Watermark", + "Extract Watermark" + ], + { + "title_aux": "ComfyUIInvisibleWatermark" + } + ], + "https://raw.githubusercontent.com/NeuralNotW0rk/ComfyUI-Waveform-Extensions/main/EXT_AudioManipulation.py": [ + [ + "BatchJoinAudio", + "CutAudio", + "DuplicateAudio", + "JoinAudio", + "ResampleAudio", + "ReverseAudio", + "StretchAudio" + ], + { + "title_aux": "Waveform Extensions" + } + ], + "https://raw.githubusercontent.com/Onierous/QRNG_Node_ComfyUI/main/qrng_node.py": [ + [ + "QRNG_Node_CSV" + ], + { + "title_aux": "QRNG_Node_ComfyUI" + } + ], + "https://raw.githubusercontent.com/SadaleNet/CLIPTextEncodeA1111-ComfyUI/master/custom_nodes/clip_text_encoder_a1111.py": [ + [ + "CLIPTextEncodeA1111", + "RerouteTextForCLIPTextEncodeA1111" + ], + { + "title_aux": "ComfyUI A1111-like Prompt Custom Node Solution" + } + ], + "https://raw.githubusercontent.com/Ser-Hilary/SDXL_sizing/main/conditioning_sizing_for_SDXL.py": [ + [ + "get_aspect_from_image", + "get_aspect_from_ints", + "sizing_node", + "sizing_node_basic", + "sizing_node_unparsed" + ], + { + "title_aux": "SDXL_sizing" + } + ], + "https://raw.githubusercontent.com/barckley75/comfyUI_DaVinciResolve/main/custom_nodes/node_text_to_speech.py": [ + [ + "TextToSpeech" + ], + { + "title_aux": "comfyUI_DaVinciResolve" + } + ], + "https://raw.githubusercontent.com/bkunbargi/BrevImage/main/BrevLoadImage.py": [ + [ + "BrevImage" + ], + { + "title_aux": "BrevImage" + } + ], + "https://raw.githubusercontent.com/catscandrive/comfyui-imagesubfolders/main/loadImageWithSubfolders.py": [ + [ + "LoadImagewithSubfolders" + ], + { + "title_aux": "Image loader with subfolders" + } + ], + "https://raw.githubusercontent.com/dawangraoming/ComfyUI_ksampler_gpu/main/ksampler_gpu.py": [ + [ + "KSamplerAdvancedGPU", + "KSamplerGPU" + ], + { + "title_aux": "KSampler GPU" + } + ], + "https://raw.githubusercontent.com/fitCorder/fcSuite/main/fcSuite.py": [ + [ + "fcFloat", + "fcFloatMatic", + "fcHex", + "fcInteger" + ], + { + "title_aux": "fcSuite" + } + ], + "https://raw.githubusercontent.com/folkghost/comfyui_search_csv/main/search_csv_node.py": [ + [ + "Search CSV" + ], + { + "title_aux": "CSV Search Node" + } + ], + "https://raw.githubusercontent.com/huimengshiguang/AspectAwareTiling/refs/heads/main/hmsg-quanjing.py": [ + [ + "AspectAwareTiling" + ], + { + "title_aux": "AspectAwareTiling" + } + ], + "https://raw.githubusercontent.com/lordgasmic/comfyui_wildcards/master/wildcards.py": [ + [ + "CLIPTextEncodeWithWildcards" + ], + { + "title_aux": "Wildcards" + } + ], + "https://raw.githubusercontent.com/lrzjason/ComfyUIJasonNode/main/SDXLMixSampler.py": [ + [ + "SDXLMixSampler" + ], + { + "title_aux": "ComfyUIJasonNode" + } + ], + "https://raw.githubusercontent.com/m957ymj75urz/ComfyUI-Custom-Nodes/main/clip-text-encode-split/clip_text_encode_split.py": [ + [ + "RawText", + "RawTextCombine", + "RawTextEncode", + "RawTextReplace" + ], + { + "title_aux": "m957ymj75urz/ComfyUI-Custom-Nodes" + } + ], + "https://raw.githubusercontent.com/nicolai256/comfyUI_Nodes_nicolai256/main/yugioh-presets.py": [ + [ + "yugioh_Presets" + ], + { + "title_aux": "comfyUI_Nodes_nicolai256" + } + ], + "https://raw.githubusercontent.com/ntdviet/comfyui-ext/main/custom_nodes/gcLatentTunnel/gcLatentTunnel.py": [ + [ + "gcLatentTunnel" + ], + { + "title_aux": "ntdviet/comfyui-ext" + } + ], + "https://raw.githubusercontent.com/s1dlx/comfy_meh/main/meh.py": [ + [ + "MergingExecutionHelper" + ], + { + "title_aux": "comfy_meh" + } + ], + "https://raw.githubusercontent.com/seghier/ComfyUI_LibreTranslate/main/translate_node.py": [ + [ + "LibreTranslateLocally", + "LibreTranslateOnline" + ], + { + "title_aux": "ComfyUI_LibreTranslate" + } + ], + "https://raw.githubusercontent.com/taabata/Comfy_Syrian_Falcon_Nodes/main/SyrianFalconNodes.py": [ + [ + "CompositeImage", + "KSamplerAlternate", + "KSamplerPromptEdit", + "KSamplerPromptEditAndAlternate", + "LoopBack", + "QRGenerate", + "WordAsImage" + ], + { + "title_aux": "Syrian Falcon Nodes" + } + ], + "https://raw.githubusercontent.com/throttlekitty/SDXLCustomAspectRatio/main/SDXLAspectRatio.py": [ + [ + "SDXLAspectRatio" + ], + { + "preemptions": [ + "SAMLoader" + ], + "title_aux": "SDXLCustomAspectRatio" + } + ], + "https://raw.githubusercontent.com/time-river/ComfyUI-CLIPSeg/main/custom_nodes/clipseg.py": [ + [ + "CLIPSeg", + "CombineSegMasks" + ], + { + "title_aux": "CLIPSeg" + } + ], + "https://raw.githubusercontent.com/tudal/Hakkun-ComfyUI-nodes/main/hakkun_nodes.py": [ + [ + "Any Converter", + "Calculate Upscale", + "Image Resize To Height", + "Image Resize To Width", + "Image size to string", + "Load Random Image", + "Load Text", + "Multi Text Merge", + "Prompt Parser", + "Random Line", + "Random Line 4" + ], + { + "title_aux": "Hakkun-ComfyUI-nodes" + } + ], + "https://raw.githubusercontent.com/ultimatech-cn/FaceSimilarity/main/faceSimilarity.py": [ + [ + "Face-analyze", + "Face-similarity" + ], + { + "title_aux": "FaceSimilarity" + } + ], + "https://raw.githubusercontent.com/vxinhao/color2rgb/main/color2rgb.py": [ + [ + "color2RGB" + ], + { + "title_aux": "color2rgb" + } + ], + "https://raw.githubusercontent.com/wsippel/comfyui_ws/main/sdxl_utility.py": [ + [ + "SDXLResolutionPresets" + ], + { + "title_aux": "SDXLResolutionPresets" + } + ] +} \ No newline at end of file diff --git a/ComfyUI/custom_nodes/ComfyUI-Manager/node_db/new/model-list.json b/ComfyUI/custom_nodes/ComfyUI-Manager/node_db/new/model-list.json new file mode 100644 index 0000000000000000000000000000000000000000..4c1db6339370d255e7bca6dfc412f6c1eae62556 --- /dev/null +++ b/ComfyUI/custom_nodes/ComfyUI-Manager/node_db/new/model-list.json @@ -0,0 +1,694 @@ +{ + "models": [ + { + "name": "sam2.1_hiera_tiny.pt", + "type": "sam2.1", + "base": "SAM", + "save_path": "sams", + "description": "Segmenty Anything SAM 2.1 hiera model (tiny)", + "reference": "https://github.com/facebookresearch/sam2#model-description", + "filename": "sam2.1_hiera_tiny.pt", + "url": "https://dl.fbaipublicfiles.com/segment_anything_2/092824/sam2.1_hiera_tiny.pt", + "size": "149.0MB" + }, + { + "name": "sam2.1_hiera_small.pt", + "type": "sam2.1", + "base": "SAM", + "save_path": "sams", + "description": "Segmenty Anything SAM 2.1 hiera model (small)", + "reference": "https://github.com/facebookresearch/sam2#model-description", + "filename": "sam2.1_hiera_small.pt", + "url": "https://dl.fbaipublicfiles.com/segment_anything_2/092824/sam2.1_hiera_small.pt", + "size": "176.0MB" + }, + { + "name": "sam2.1_hiera_base_plus.pt", + "type": "sam2.1", + "base": "SAM", + "save_path": "sams", + "description": "Segmenty Anything SAM 2.1 hiera model (base+)", + "reference": "https://github.com/facebookresearch/sam2#model-description", + "filename": "sam2.1_hiera_base_plus.pt", + "url": "https://dl.fbaipublicfiles.com/segment_anything_2/092824/sam2.1_hiera_base_plus.pt", + "size": "309.0MB" + }, + { + "name": "sam2.1_hiera_large.pt", + "type": "sam2.1", + "base": "SAM", + "save_path": "sams", + "description": "Segmenty Anything SAM 2.1 hiera model (large)", + "reference": "https://github.com/facebookresearch/sam2#model-description", + "filename": "sam2.1_hiera_large.pt", + "url": "https://dl.fbaipublicfiles.com/segment_anything_2/092824/sam2.1_hiera_large.pt", + "size": "857.0MB" + }, + + { + "name": "sam2_hiera_tiny.pt", + "type": "sam2", + "base": "SAM", + "save_path": "sams", + "description": "Segmenty Anything SAM 2 hiera model (tiny)", + "reference": "https://github.com/facebookresearch/sam2#model-description", + "filename": "sam2_hiera_tiny.pt", + "url": "https://dl.fbaipublicfiles.com/segment_anything_2/072824/sam2_hiera_tiny.pt", + "size": "149.0MB" + }, + { + "name": "sam2_hiera_small.pt", + "type": "sam2", + "base": "SAM", + "save_path": "sams", + "description": "Segmenty Anything SAM 2 hiera model (small)", + "reference": "https://github.com/facebookresearch/sam2#model-description", + "filename": "sam2_hiera_small.pt", + "url": "https://dl.fbaipublicfiles.com/segment_anything_2/072824/sam2_hiera_small.pt", + "size": "176.0MB" + }, + { + "name": "sam2_hiera_base_plus.pt", + "type": "sam2", + "base": "SAM", + "save_path": "sams", + "description": "Segmenty Anything SAM 2 hiera model (base+)", + "reference": "https://github.com/facebookresearch/sam2#model-description", + "filename": "sam2_hiera_base_plus.pt", + "url": "https://dl.fbaipublicfiles.com/segment_anything_2/072824/sam2_hiera_base_plus.pt", + "size": "309.0MB" + }, + { + "name": "sam2_hiera_large.pt", + "type": "sam2", + "base": "SAM", + "save_path": "sams", + "description": "Segmenty Anything SAM 2 hiera model (large)", + "reference": "https://github.com/facebookresearch/sam2#model-description", + "filename": "sam2_hiera_large.pt", + "url": "https://dl.fbaipublicfiles.com/segment_anything_2/072824/sam2_hiera_large.pt", + "size": "857.0MB" + }, + + { + "name": "Comfy-Org/omnigen2_fp16.safetensors", + "type": "diffusion_model", + "base": "OmniGen2", + "save_path": "default", + "description": "OmniGen2 diffusion model. This is required for using OmniGen2.", + "reference": "https://huggingface.co/Comfy-Org/Omnigen2_ComfyUI_repackaged", + "filename": "omnigen2_fp16.safetensors", + "url": "https://huggingface.co/Comfy-Org/Omnigen2_ComfyUI_repackaged/resolve/main/split_files/diffusion_models/omnigen2_fp16.safetensors", + "size": "7.93GB" + }, + { + "name": "Comfy-Org/qwen_2.5_vl_fp16.safetensors", + "type": "clip", + "base": "qwen-2.5", + "save_path": "default", + "description": "text encoder for OmniGen2", + "reference": "https://huggingface.co/Comfy-Org/Omnigen2_ComfyUI_repackaged", + "filename": "qwen_2.5_vl_fp16.safetensors", + "url": "https://huggingface.co/Comfy-Org/Omnigen2_ComfyUI_repackaged/resolve/main/split_files/text_encoders/qwen_2.5_vl_fp16.safetensors", + "size": "7.51GB" + }, + + { + "name": "Latent Bridge Matching for Image Relighting", + "type": "diffusion_model", + "base": "LBM", + "save_path": "diffusion_models/LBM", + "description": "Latent Bridge Matching (LBM) Relighting model", + "reference": "https://huggingface.co/jasperai/LBM_relighting", + "filename": "LBM_relighting.safetensors", + "url": "https://huggingface.co/jasperai/LBM_relighting/resolve/main/model.safetensors", + "size": "5.02GB" + }, + + { + "name": "LTX-Video 13B Distilled v0.9.7", + "type": "checkpoint", + "base": "LTX-Video", + "save_path": "checkpoints/LTXV", + "description": "Distilled version of the LTX-Video 13B model, providing improved efficiency while maintaining high-resolution quality.", + "reference": "https://huggingface.co/Lightricks/LTX-Video", + "filename": "ltxv-13b-0.9.7-distilled.safetensors", + "url": "https://huggingface.co/Lightricks/LTX-Video/resolve/main/ltxv-13b-0.9.7-distilled.safetensors", + "size": "28.6GB" + }, + { + "name": "LTX-Video 13B Distilled FP8 v0.9.7", + "type": "checkpoint", + "base": "LTX-Video", + "save_path": "checkpoints/LTXV", + "description": "Quantized distilled version of the LTX-Video 13B model, optimized for even lower VRAM usage while maintaining quality.", + "reference": "https://huggingface.co/Lightricks/LTX-Video", + "filename": "ltxv-13b-0.9.7-distilled-fp8.safetensors", + "url": "https://huggingface.co/Lightricks/LTX-Video/resolve/main/ltxv-13b-0.9.7-distilled-fp8.safetensors", + "size": "15.7GB" + }, + { + "name": "LTX-Video 13B Distilled LoRA v0.9.7", + "type": "lora", + "base": "LTX-Video", + "save_path": "loras", + "description": "A LoRA adapter that transforms the standard LTX-Video 13B model into a distilled version when loaded.", + "reference": "https://huggingface.co/Lightricks/LTX-Video", + "filename": "ltxv-13b-0.9.7-distilled-lora128.safetensors", + "url": "https://huggingface.co/Lightricks/LTX-Video/resolve/main/ltxv-13b-0.9.7-distilled-lora128.safetensors", + "size": "1.33GB" + }, + { + "name": "lllyasviel/FramePackI2V_HY", + "type": "FramePackI2V", + "base": "FramePackI2V", + "save_path": "diffusers/lllyasviel", + "description": "[SNAPSHOT] This is the f1k1_x_g9_f1k1f2k2f16k4_td FramePack for HY. [w/You cannot download this item on ComfyUI-Manager versions below V3.18]", + "reference": "https://huggingface.co/lllyasviel/FramePackI2V_HY", + "filename": "", + "url": "lllyasviel/FramePackI2V_HY", + "size": "25.75GB" + }, + + { + "name": "LTX-Video Spatial Upscaler v0.9.7", + "type": "checkpoint", + "base": "LTX-Video", + "save_path": "checkpoints/LTXV", + "description": "Spatial upscaler model for LTX-Video. This model enhances the spatial resolution of generated videos.", + "reference": "https://huggingface.co/Lightricks/LTX-Video", + "filename": "ltxv-spatial-upscaler-0.9.7.safetensors", + "url": "https://huggingface.co/Lightricks/LTX-Video/resolve/main/ltxv-spatial-upscaler-0.9.7.safetensors", + "size": "505MB" + }, + { + "name": "LTX-Video Temporal Upscaler v0.9.7", + "type": "checkpoint", + "base": "LTX-Video", + "save_path": "checkpoints/LTXV", + "description": "Temporal upscaler model for LTX-Video. This model enhances the temporal resolution and smoothness of generated videos.", + "reference": "https://huggingface.co/Lightricks/LTX-Video", + "filename": "ltxv-temporal-upscaler-0.9.7.safetensors", + "url": "https://huggingface.co/Lightricks/LTX-Video/resolve/main/ltxv-temporal-upscaler-0.9.7.safetensors", + "size": "524MB" + }, + { + "name": "LTX-Video 13B v0.9.7", + "type": "checkpoint", + "base": "LTX-Video", + "save_path": "checkpoints/LTXV", + "description": "High-resolution quality LTX-Video 13B model.", + "reference": "https://huggingface.co/Lightricks/LTX-Video", + "filename": "ltxv-13b-0.9.7-dev.safetensors", + "url": "https://huggingface.co/Lightricks/LTX-Video/resolve/main/ltxv-13b-0.9.7-dev.safetensors", + "size": "28.6GB" + }, + { + "name": "LTX-Video 13B FP8 v0.9.7", + "type": "checkpoint", + "base": "LTX-Video", + "save_path": "checkpoints/LTXV", + "description": "Quantized version of the LTX-Video 13B model, optimized for lower VRAM usage while maintaining high quality.", + "reference": "https://huggingface.co/Lightricks/LTX-Video", + "filename": "ltxv-13b-0.9.7-dev-fp8.safetensors", + "url": "https://huggingface.co/Lightricks/LTX-Video/resolve/main/ltxv-13b-0.9.7-dev-fp8.safetensors", + "size": "15.7GB" + }, + { + "name": "Comfy-Org/Wan2.1 i2v 480p 14B (bf16)", + "type": "diffusion_model", + "base": "Wan2.1", + "save_path": "diffusion_models/Wan2.1", + "description": "Wan2.1 difussion model for i2v 480p 14B (bf16)", + "reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged", + "filename": "wan2.1_i2v_480p_14B_bf16.safetensors", + "url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/diffusion_models/wan2.1_i2v_480p_14B_bf16.safetensors", + "size": "32.8GB" + }, + { + "name": "Comfy-Org/Wan2.1 i2v 480p 14B (fp16)", + "type": "diffusion_model", + "base": "Wan2.1", + "save_path": "diffusion_models/Wan2.1", + "description": "Wan2.1 difussion model for i2v 480p 14B (fp16)", + "reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged", + "filename": "wan2.1_i2v_480p_14B_fp16.safetensors", + "url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/diffusion_models/wan2.1_i2v_480p_14B_fp16.safetensors", + "size": "32.8GB" + }, + { + "name": "Comfy-Org/Wan2.1 i2v 480p 14B (fp8_e4m3fn)", + "type": "diffusion_model", + "base": "Wan2.1", + "save_path": "diffusion_models/Wan2.1", + "description": "Wan2.1 difussion model for i2v 480p 14B (fp8_e4m3fn)", + "reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged", + "filename": "wan2.1_i2v_480p_14B_fp8_e4m3fn.safetensors", + "url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/diffusion_models/wan2.1_i2v_480p_14B_fp8_e4m3fn.safetensors", + "size": "16.4GB" + }, + { + "name": "Comfy-Org/Wan2.1 i2v 480p 14B (fp8_scaled)", + "type": "diffusion_model", + "base": "Wan2.1", + "save_path": "diffusion_models/Wan2.1", + "description": "Wan2.1 difussion model for i2v 480p 14B (fp8_scaled)", + "reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged", + "filename": "wan2.1_i2v_480p_14B_fp8_scaled.safetensors", + "url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/diffusion_models/wan2.1_i2v_480p_14B_fp8_scaled.safetensors", + "size": "16.4GB" + }, + { + "name": "Comfy-Org/Wan2.1 i2v 720p 14B (bf16)", + "type": "diffusion_model", + "base": "Wan2.1", + "save_path": "diffusion_models/Wan2.1", + "description": "Wan2.1 difussion model for i2v 720p 14B (bf16)", + "reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged", + "filename": "wan2.1_i2v_720p_14B_bf16.safetensors", + "url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/diffusion_models/wan2.1_i2v_720p_14B_bf16.safetensors", + "size": "32.8GB" + }, + { + "name": "Comfy-Org/Wan2.1 i2v 720p 14B (fp16)", + "type": "diffusion_model", + "base": "Wan2.1", + "save_path": "diffusion_models/Wan2.1", + "description": "Wan2.1 difussion model for i2v 720p 14B (fp16)", + "reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged", + "filename": "wan2.1_i2v_720p_14B_fp16.safetensors", + "url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/diffusion_models/wan2.1_i2v_720p_14B_fp16.safetensors", + "size": "32.8GB" + }, + { + "name": "Comfy-Org/Wan2.1 i2v 720p 14B (fp8_e4m3fn)", + "type": "diffusion_model", + "base": "Wan2.1", + "save_path": "diffusion_models/Wan2.1", + "description": "Wan2.1 difussion model for i2v 720p 14B (fp8_e4m3fn)", + "reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged", + "filename": "wan2.1_i2v_720p_14B_fp8_e4m3fn.safetensors", + "url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/diffusion_models/wan2.1_i2v_720p_14B_fp8_e4m3fn.safetensors", + "size": "16.4GB" + }, + { + "name": "Comfy-Org/Wan2.1 i2v 720p 14B (fp8_scaled)", + "type": "diffusion_model", + "base": "Wan2.1", + "save_path": "diffusion_models/Wan2.1", + "description": "Wan2.1 difussion model for i2v 720p 14B (fp8_scaled)", + "reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged", + "filename": "wan2.1_i2v_720p_14B_fp8_scaled.safetensors", + "url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/diffusion_models/wan2.1_i2v_720p_14B_fp8_scaled.safetensors", + "size": "16.4GB" + }, + { + "name": "Comfy-Org/clip_vision_h.safetensors", + "type": "clip_vision", + "base": "clip_vision_h", + "save_path": "clip_vision", + "description": "clip_vision_h model for Wan2.1", + "reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged", + "filename": "clip_vision_h.safetensors", + "url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/clip_vision/clip_vision_h.safetensors", + "size": "1.26GB" + }, + + { + "name": "Comfy-Org/Wan2.1 t2v 1.3B (bf16)", + "type": "diffusion_model", + "base": "Wan2.1", + "save_path": "diffusion_models/Wan2.1", + "description": "Wan2.1 difussion model for t2v 1.3B (bf16)", + "reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged", + "filename": "wan2.1_t2v_1.3B_bf16.safetensors", + "url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/diffusion_models/wan2.1_t2v_1.3B_bf16.safetensors", + "size": "2.84GB" + }, + { + "name": "Comfy-Org/Wan2.1 t2v 1.3B (fp16)", + "type": "diffusion_model", + "base": "Wan2.1", + "save_path": "diffusion_models/Wan2.1", + "description": "Wan2.1 difussion model for t2v 1.3B (fp16)", + "reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged", + "filename": "wan2.1_t2v_1.3B_fp16.safetensors", + "url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/diffusion_models/wan2.1_t2v_1.3B_fp16.safetensors", + "size": "2.84GB" + }, + + { + "name": "Comfy-Org/Wan2.1 t2v 14B (bf16)", + "type": "diffusion_model", + "base": "Wan2.1", + "save_path": "diffusion_models/Wan2.1", + "description": "Wan2.1 difussion model for t2v 14B (bf16)", + "reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged", + "filename": "wan2.1_t2v_14B_bf16.safetensors", + "url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/diffusion_models/wan2.1_t2v_14B_bf16.safetensors", + "size": "28.6GB" + }, + { + "name": "Comfy-Org/Wan2.1 t2v 14B (fp16)", + "type": "diffusion_model", + "base": "Wan2.1", + "save_path": "diffusion_models/Wan2.1", + "description": "Wan2.1 difussion model for t2v 14B (fp16)", + "reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged", + "filename": "wan2.1_t2v_14B_fp16.safetensors", + "url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/diffusion_models/wan2.1_t2v_14B_fp16.safetensors", + "size": "28.6GB" + }, + { + "name": "Comfy-Org/Wan2.1 t2v 14B (fp8_e4m3fn)", + "type": "diffusion_model", + "base": "Wan2.1", + "save_path": "diffusion_models/Wan2.1", + "description": "Wan2.1 difussion model for t2v 14B (fp8_e4m3fn)", + "reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged", + "filename": "wan2.1_t2v_14B_fp8_e4m3fn.safetensors", + "url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/diffusion_models/wan2.1_t2v_14B_fp8_e4m3fn.safetensors", + "size": "14.3GB" + }, + { + "name": "Comfy-Org/Wan2.1 t2v 14B (fp8_scaled)", + "type": "diffusion_model", + "base": "Wan2.1", + "save_path": "diffusion_models/Wan2.1", + "description": "Wan2.1 difussion model for t2v 14B (fp8_scaled)", + "reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged", + "filename": "wan2.1_t2v_14B_fp8_scaled.safetensors", + "url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/diffusion_models/wan2.1_t2v_14B_fp8_scaled.safetensors", + "size": "14.3GB" + }, + { + "name": "Comfy-Org/Wan2.1 VAE", + "type": "vae", + "base": "Wan2.1", + "save_path": "vae", + "description": "Wan2.1 VAE model", + "reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged", + "filename": "wan_2.1_vae.safetensors", + "url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/vae/wan_2.1_vae.safetensors", + "size": "254MB" + }, + + + { + "name": "Comfy-Org/umt5_xxl_fp16.safetensors", + "type": "clip", + "base": "umt5_xxl", + "save_path": "text_encoders", + "description": "umt5_xxl_fp16 text encoder for Wan2.1", + "reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged", + "filename": "umt5_xxl_fp16.safetensors", + "url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/text_encoders/umt5_xxl_fp16.safetensors", + "size": "11.4GB" + }, + { + "name": "Comfy-Org/umt5_xxl_fp8_e4m3fn_scaled.safetensors", + "type": "clip", + "base": "umt5_xxl", + "save_path": "text_encoders", + "description": "umt5_xxl_fp8_e4m3fn_scaled text encoder for Wan2.1", + "reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged", + "filename": "umt5_xxl_fp8_e4m3fn_scaled.safetensors", + "url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/text_encoders/umt5_xxl_fp8_e4m3fn_scaled.safetensors", + "size": "6.74GB" + }, + + { + "name": "Comfy-Org/hunyuan_video_image_to_video_720p_bf16.safetensors", + "type": "diffusion_model", + "base": "Hunyuan Video", + "save_path": "diffusion_models/hunyuan_video", + "description": "Huyuan Video Image2Video diffusion model. repackaged version.", + "reference": "https://huggingface.co/Comfy-Org/HunyuanVideo_repackaged", + "filename": "hunyuan_video_image_to_video_720p_bf16.safetensors", + "url": "https://huggingface.co/Comfy-Org/HunyuanVideo_repackaged/resolve/main/split_files/diffusion_models/hunyuan_video_image_to_video_720p_bf16.safetensors", + "size": "25.6GB" + }, + { + "name": "Comfy-Org/llava_llama3_vision.safetensors", + "type": "clip_vision", + "base": "LLaVA-Llama-3", + "save_path": "text_encoders", + "description": "llava_llama3_vision clip vison model. This is required for using Hunyuan Video Image2Video.", + "reference": "https://huggingface.co/Comfy-Org/HunyuanVideo_repackaged", + "filename": "llava_llama3_vision.safetensors", + "url": "https://huggingface.co/Comfy-Org/HunyuanVideo_repackaged/resolve/main/split_files/clip_vision/llava_llama3_vision.safetensors", + "size": "649MB" + }, + + { + "name": "LTX-Video 2B v0.9.5 Checkpoint", + "type": "checkpoint", + "base": "LTX-Video", + "save_path": "checkpoints/LTXV", + "description": "LTX-Video is the first DiT-based video generation model capable of generating high-quality videos in real-time. It produces 24 FPS videos at a 768x512 resolution faster than they can be watched. Trained on a large-scale dataset of diverse videos, the model generates high-resolution videos with realistic and varied content.", + "reference": "https://huggingface.co/Lightricks/LTX-Video", + "filename": "ltx-video-2b-v0.9.5.safetensors", + "url": "https://huggingface.co/Lightricks/LTX-Video/resolve/main/ltx-video-2b-v0.9.5.safetensors", + "size": "6.34GB" + }, + { + "name": "kolors/vae/diffusion_pytorch_model.fp16.safetensors", + "type": "VAE", + "base": "Kolors", + "save_path": "vae/kolors", + "description": "Kolors VAE", + "reference": "https://huggingface.co/Kwai-Kolors/Kolors", + "filename": "diffusion_pytorch_model.fp16.safetensors", + "url": "https://huggingface.co/Kwai-Kolors/Kolors/resolve/main/vae/diffusion_pytorch_model.fp16.safetensors", + "size": "167MB" + }, + { + "name": "kolors/vae/diffusion_pytorch_model.safetensors", + "type": "VAE", + "base": "Kolors", + "save_path": "vae/kolors", + "description": "Kolors VAE", + "reference": "https://huggingface.co/Kwai-Kolors/Kolors", + "filename": "diffusion_pytorch_model.safetensors", + "url": "https://huggingface.co/Kwai-Kolors/Kolors/resolve/main/vae/diffusion_pytorch_model.safetensors", + "size": "335MB" + }, + + { + "name": "deepseek-ai/Janus-Pro-1B", + "type": "Janus-Pro", + "base": "Janus-Pro", + "save_path": "Janus-Pro", + "description": "[SNAPSHOT] Janus-Pro-1B model.[w/You cannot download this item on ComfyUI-Manager versions below V3.18]", + "reference": "https://huggingface.co/deepseek-ai/Janus-Pro-1B", + "filename": "", + "url": "deepseek-ai/Janus-Pro-1B", + "size": "7.8GB" + }, + { + "name": "deepseek-ai/Janus-Pro-7B", + "type": "Janus-Pro", + "base": "Janus-Pro", + "save_path": "Janus-Pro", + "description": "[SNAPSHOT] Janus-Pro-7B model.[w/You cannot download this item on ComfyUI-Manager versions below V3.18]", + "reference": "https://huggingface.co/deepseek-ai/Janus-Pro-7B", + "filename": "", + "url": "deepseek-ai/Janus-Pro-7B", + "size": "14.85GB" + }, + + { + "name": "Leoxing/pia.ckpt", + "type": "animatediff-pia", + "base": "SD1.x", + "save_path": "animatediff_models", + "description": "AnimateDiff-PIA Model", + "reference": "https://huggingface.co/Leoxing/PIA/tree/main", + "filename": "pia.ckpt", + "url": "https://huggingface.co/Leoxing/PIA/resolve/main/pia.ckpt", + "size": "1.67GB" + }, + + { + "name": "comfyanonymous/cosmos_cv8x8x8_1.0.safetensors", + "type": "VAE", + "base": "Cosmos-1.0", + "save_path": "default", + "description": "VAE model for Cosmos 1.0", + "reference": "https://huggingface.co/comfyanonymous/cosmos_1.0_text_encoder_and_VAE_ComfyUI/tree/main", + "filename": "cosmos_cv8x8x8_1.0.safetensors", + "url": "https://huggingface.co/comfyanonymous/cosmos_1.0_text_encoder_and_VAE_ComfyUI/resolve/main/vae/cosmos_cv8x8x8_1.0.safetensors", + "size": "211MB" + }, + { + "name": "mcmonkey/Cosmos-1_0-Diffusion-7B-Text2World.safetensors", + "type": "diffusion_model", + "base": "Cosmos-1.0", + "save_path": "diffusion_models/cosmos-1.0", + "description": "Cosmos 1.0 Text2World Diffusion Model (7B)", + "reference": "https://huggingface.co/mcmonkey/cosmos-1.0", + "filename": "Cosmos-1_0-Diffusion-7B-Text2World.safetensors", + "url": "https://huggingface.co/mcmonkey/cosmos-1.0/resolve/main/Cosmos-1_0-Diffusion-7B-Text2World.safetensors", + "size": "14.5GB" + }, + { + "name": "mcmonkey/Cosmos-1_0-Diffusion-7B-Video2World.safetensors", + "type": "diffusion_model", + "base": "Cosmos-1.0", + "save_path": "diffusion_models/cosmos-1.0", + "description": "Cosmos 1.0 Video2World Diffusion Model (7B)", + "reference": "https://huggingface.co/mcmonkey/cosmos-1.0", + "filename": "Cosmos-1_0-Diffusion-7B-Video2World.safetensors", + "url": "https://huggingface.co/mcmonkey/cosmos-1.0/resolve/main/Cosmos-1_0-Diffusion-7B-Video2World.safetensors", + "size": "14.5GB" + }, + { + "name": "mcmonkey/Cosmos-1_0-Diffusion-14B-Text2World.safetensors", + "type": "diffusion_model", + "base": "Cosmos-1.0", + "save_path": "diffusion_models/cosmos-1.0", + "description": "Cosmos 1.0 Text2World Diffusion Model (14B)", + "reference": "https://huggingface.co/mcmonkey/cosmos-1.0", + "filename": "Cosmos-1_0-Diffusion-14B-Text2World.safetensors", + "url": "https://huggingface.co/mcmonkey/cosmos-1.0/resolve/main/Cosmos-1_0-Diffusion-14B-Text2World.safetensors", + "size": "28.5GB" + }, + { + "name": "mcmonkey/Cosmos-1_0-Diffusion-14B-Video2World.safetensors", + "type": "diffusion_model", + "base": "Cosmos-1.0", + "save_path": "diffusion_models/cosmos-1.0", + "description": "Cosmos 1.0 Video2World Diffusion Model (14B)", + "reference": "https://huggingface.co/mcmonkey/cosmos-1.0", + "filename": "Cosmos-1_0-Diffusion-14B-Video2World.safetensors", + "url": "https://huggingface.co/mcmonkey/cosmos-1.0/resolve/main/Cosmos-1_0-Diffusion-14B-Video2World.safetensors", + "size": "28.5GB" + }, + + { + "name": "Comfy-Org/llava_llama3_fp8_scaled.safetensors", + "type": "clip", + "base": "LLaVA-Llama-3", + "save_path": "text_encoders", + "description": "llava_llama3_fp8_scaled text encoder model. This is required for using Hunyuan Video.", + "reference": "https://huggingface.co/Comfy-Org/HunyuanVideo_repackaged", + "filename": "llava_llama3_fp8_scaled.safetensors", + "url": "https://huggingface.co/Comfy-Org/HunyuanVideo_repackaged/resolve/main/split_files/text_encoders/llava_llama3_fp8_scaled.safetensors", + "size": "9.09GB" + }, + { + "name": "Comfy-Org/llava_llama3_fp16.safetensors", + "type": "clip", + "base": "LLaVA-Llama-3", + "save_path": "text_encoders", + "description": "llava_llama3_fp16 text encoder model. This is required for using Hunyuan Video.", + "reference": "https://huggingface.co/Comfy-Org/HunyuanVideo_repackaged", + "filename": "llava_llama3_fp16.safetensors", + "url": "https://huggingface.co/Comfy-Org/HunyuanVideo_repackaged/resolve/main/split_files/text_encoders/llava_llama3_fp16.safetensors", + "size": "16.1GB" + }, + + { + "name": "PixArt-Sigma-XL-2-512-MS.safetensors (diffusion)", + "type": "diffusion_model", + "base": "pixart-sigma", + "save_path": "diffusion_models/PixArt-Sigma", + "description": "PixArt-Sigma Diffusion model", + "reference": "https://huggingface.co/PixArt-alpha/PixArt-Sigma-XL-2-512-MS", + "filename": "PixArt-Sigma-XL-2-512-MS.safetensors", + "url": "https://huggingface.co/PixArt-alpha/PixArt-Sigma-XL-2-512-MS/resolve/main/transformer/diffusion_pytorch_model.safetensors", + "size": "2.44GB" + }, + { + "name": "PixArt-Sigma-XL-2-1024-MS.safetensors (diffusion)", + "type": "diffusion_model", + "base": "pixart-sigma", + "save_path": "diffusion_models/PixArt-Sigma", + "description": "PixArt-Sigma Diffusion model", + "reference": "https://huggingface.co/PixArt-alpha/PixArt-Sigma-XL-2-1024-MS", + "filename": "PixArt-Sigma-XL-2-1024-MS.safetensors", + "url": "https://huggingface.co/PixArt-alpha/PixArt-Sigma-XL-2-1024-MS/resolve/main/transformer/diffusion_pytorch_model.safetensors", + "size": "2.44GB" + }, + { + "name": "PixArt-XL-2-1024-MS.safetensors (diffusion)", + "type": "diffusion_model", + "base": "pixart-alpha", + "save_path": "diffusion_models/PixArt-Alpha", + "description": "PixArt-Alpha Diffusion model", + "reference": "https://huggingface.co/PixArt-alpha/PixArt-XL-2-1024-MS", + "filename": "PixArt-XL-2-1024-MS.safetensors", + "url": "https://huggingface.co/PixArt-alpha/PixArt-XL-2-1024-MS/resolve/main/transformer/diffusion_pytorch_model.safetensors", + "size": "2.45GB" + }, + + { + "name": "Comfy-Org/hunyuan_video_t2v_720p_bf16.safetensors", + "type": "diffusion_model", + "base": "Hunyuan Video", + "save_path": "diffusion_models/hunyuan_video", + "description": "Huyuan Video diffusion model. repackaged version.", + "reference": "https://huggingface.co/Comfy-Org/HunyuanVideo_repackaged", + "filename": "hunyuan_video_t2v_720p_bf16.safetensors", + "url": "https://huggingface.co/Comfy-Org/HunyuanVideo_repackaged/resolve/main/split_files/diffusion_models/hunyuan_video_t2v_720p_bf16.safetensors", + "size": "25.6GB" + }, + { + "name": "Comfy-Org/hunyuan_video_vae_bf16.safetensors", + "type": "VAE", + "base": "Hunyuan Video", + "save_path": "VAE", + "description": "Huyuan Video VAE model. repackaged version.", + "reference": "https://huggingface.co/Comfy-Org/HunyuanVideo_repackaged", + "filename": "hunyuan_video_vae_bf16.safetensors", + "url": "https://huggingface.co/Comfy-Org/HunyuanVideo_repackaged/resolve/main/split_files/vae/hunyuan_video_vae_bf16.safetensors", + "size": "493MB" + }, + + { + "name": "LTX-Video 2B v0.9.1 Checkpoint", + "type": "checkpoint", + "base": "LTX-Video", + "save_path": "checkpoints/LTXV", + "description": "LTX-Video is the first DiT-based video generation model capable of generating high-quality videos in real-time. It produces 24 FPS videos at a 768x512 resolution faster than they can be watched. Trained on a large-scale dataset of diverse videos, the model generates high-resolution videos with realistic and varied content.", + "reference": "https://huggingface.co/Lightricks/LTX-Video", + "filename": "ltx-video-2b-v0.9.1.safetensors", + "url": "https://huggingface.co/Lightricks/LTX-Video/resolve/main/ltx-video-2b-v0.9.1.safetensors", + "size": "5.72GB" + }, + + { + "name": "XLabs-AI/flux-canny-controlnet-v3.safetensors", + "type": "controlnet", + "base": "FLUX.1", + "save_path": "xlabs/controlnets", + "description": "ControlNet checkpoints for FLUX.1-dev model by Black Forest Labs.", + "reference": "https://huggingface.co/XLabs-AI/flux-controlnet-collections", + "filename": "flux-canny-controlnet-v3.safetensors", + "url": "https://huggingface.co/XLabs-AI/flux-controlnet-collections/resolve/main/flux-canny-controlnet-v3.safetensors", + "size": "1.49GB" + }, + { + "name": "XLabs-AI/flux-depth-controlnet-v3.safetensors", + "type": "controlnet", + "base": "FLUX.1", + "save_path": "xlabs/controlnets", + "description": "ControlNet checkpoints for FLUX.1-dev model by Black Forest Labs.", + "reference": "https://huggingface.co/XLabs-AI/flux-controlnet-collections", + "filename": "flux-depth-controlnet-v3.safetensors", + "url": "https://huggingface.co/XLabs-AI/flux-controlnet-collections/resolve/main/flux-depth-controlnet-v3.safetensors", + "size": "1.49GB" + }, + { + "name": "XLabs-AI/flux-hed-controlnet-v3.safetensors", + "type": "controlnet", + "base": "FLUX.1", + "save_path": "xlabs/controlnets", + "description": "ControlNet checkpoints for FLUX.1-dev model by Black Forest Labs.", + "reference": "https://huggingface.co/XLabs-AI/flux-controlnet-collections", + "filename": "flux-hed-controlnet-v3.safetensors", + "url": "https://huggingface.co/XLabs-AI/flux-controlnet-collections/resolve/main/flux-hed-controlnet-v3.safetensors", + "size": "1.49GB" + } + ] +} diff --git a/ComfyUI/custom_nodes/ComfyUI-Manager/node_db/tutorial/custom-node-list.json b/ComfyUI/custom_nodes/ComfyUI-Manager/node_db/tutorial/custom-node-list.json new file mode 100644 index 0000000000000000000000000000000000000000..9789e512da30e57e1ed15a9238e8c02255540130 --- /dev/null +++ b/ComfyUI/custom_nodes/ComfyUI-Manager/node_db/tutorial/custom-node-list.json @@ -0,0 +1,346 @@ +{ + "custom_nodes": [ + { + "author": "Comfy-Org", + "title": "ComfyUI React Extension Template", + "reference": "https://github.com/Comfy-Org/ComfyUI-React-Extension-Template", + "files": [ + "https://github.com/Comfy-Org/ComfyUI-React-Extension-Template" + ], + "install_type": "git-clone", + "description": "A minimal template for creating React/TypeScript frontend extensions for ComfyUI, with complete boilerplate setup including internationalization and unit testing." + }, + { + "author": "Suzie1", + "title": "Guide To Making Custom Nodes in ComfyUI", + "reference": "https://github.com/Suzie1/ComfyUI_Guide_To_Making_Custom_Nodes", + "files": [ + "https://github.com/Suzie1/ComfyUI_Guide_To_Making_Custom_Nodes" + ], + "install_type": "git-clone", + "description": "There is a small node pack attached to this guide. This includes the init file and 3 nodes associated with the tutorials." + }, + { + "author": "bamboodia", + "title": "BAM Nodes", + "reference": "https://github.com/bamboodia/BAM_Nodes", + "files": [ + "https://github.com/bamboodia/BAM_Nodes" + ], + "install_type": "git-clone", + "description": "A collection of comfyui nodes that I have made for nothing more than educational purposes." + }, + { + "author": "BadCafeCode", + "title": "execution-inversion-demo-comfyui", + "reference": "https://github.com/BadCafeCode/execution-inversion-demo-comfyui", + "files": [ + "https://github.com/BadCafeCode/execution-inversion-demo-comfyui" + ], + "install_type": "git-clone", + "description": "These are demo nodes for [a/PR2666](https://github.com/comfyanonymous/ComfyUI/pull/2666)" + }, + { + "author": "ecjojo", + "title": "ecjojo_example_nodes", + "reference": "https://github.com/ecjojo/ecjojo-example-nodes", + "files": [ + "https://github.com/ecjojo/ecjojo-example-nodes" + ], + "install_type": "git-clone", + "description": "Welcome to ecjojo_example_nodes! This example is specifically designed for beginners who want to learn how to write a simple custom node.\nFeel free to modify this example and make it your own. Experiment with different features and functionalities to enhance your understanding of ComfyUI custom nodes. Don't be afraid to explore and customize the code to suit your needs.\nBy diving into this example and making it your own, you'll gain valuable hands-on experience in creating custom nodes in ComfyUI. Enjoy the process of learning and have fun with your custom node development journey!" + }, + { + "author": "dynamixar", + "title": "Atluris", + "reference": "https://github.com/dynamixar/Atluris", + "files": [ + "https://github.com/dynamixar/Atluris" + ], + "install_type": "git-clone", + "description": "Nodes:Random Line" + }, + { + "author": "et118", + "title": "ComfyUI-ElGogh-Nodes", + "reference": "https://github.com/et118/ComfyUI-ElGogh-Nodes", + "files": [ + "https://github.com/et118/ComfyUI-ElGogh-Nodes" + ], + "install_type": "git-clone", + "description": "Nodes:ElGogh Positive Prompt, ElGogh NEGATIVE Prompt, ElGogh Empty Latent Image, ElGogh Checkpoint Loader Simple" + }, + { + "author": "LarryJane491", + "title": "Custom-Node-Base", + "reference": "https://github.com/LarryJane491/Custom-Node-Base", + "files": [ + "https://github.com/LarryJane491/Custom-Node-Base" + ], + "install_type": "git-clone", + "description": "This project is an `empty` custom node that is already in its own folder. It serves as a base to build any custom node. Whenever you want to create a custom node, you can download that, put it in custom_nodes, then you just have to change the names and fill it with code!" + }, + { + "author": "foxtrot-roger", + "title": "comfyui-custom-nodes", + "reference": "https://github.com/foxtrot-roger/comfyui-custom-nodes", + "files": [ + "https://github.com/foxtrot-roger/comfyui-custom-nodes" + ], + "install_type": "git-clone", + "description": "Tutorial nodes" + }, + { + "author": "wailovet", + "title": "ComfyUI-WW", + "reference": "https://github.com/wailovet/ComfyUI-WW", + "files": [ + "https://github.com/wailovet/ComfyUI-WW" + ], + "install_type": "git-clone", + "description": "Nodes:WW_ImageResize" + }, + { + "author": "azure-dragon-ai", + "title": "ComfyUI-HPSv2-Nodes", + "reference": "https://github.com/azure-dragon-ai/ComfyUI-HPSv2-Nodes", + "files": [ + "https://github.com/azure-dragon-ai/ComfyUI-HPSv2-Nodes" + ], + "install_type": "git-clone", + "description": "Nodes:Loader, Image Processor, Text Processor, ImageScore" + }, + { + "author": "kappa54m", + "title": "ComfyUI-HPSv2-Nodes", + "reference": "https://github.com/kappa54m/ComfyUI_Usability", + "files": [ + "https://github.com/kappa54m/ComfyUI_Usability" + ], + "install_type": "git-clone", + "description": "Nodes:Load Image Dedup" + }, + { + "author": "IvanRybakov", + "title": "comfyui-node-int-to-string-convertor", + "reference": "https://github.com/IvanRybakov/comfyui-node-int-to-string-convertor", + "files": [ + "https://github.com/IvanRybakov/comfyui-node-int-to-string-convertor" + ], + "install_type": "git-clone", + "description": "Nodes:Int To String Convertor" + }, + { + "author": "yowipr", + "title": "ComfyUI-Manual", + "reference": "https://github.com/yowipr/ComfyUI-Manual", + "files": [ + "https://github.com/yowipr/ComfyUI-Manual" + ], + "install_type": "git-clone", + "description": "Nodes:M_Layer, M_Output" + }, + { + "author": "andrewharp", + "title": "ComfyUI Function Annotator", + "reference": "https://github.com/andrewharp/ComfyUI-Annotations", + "files": [ + "https://github.com/andrewharp/ComfyUI-Annotations" + ], + "install_type": "git-clone", + "description": "This module provides an annotation @ComfyFunc to streamline adding custom node types in ComfyUI. It processes your function's signature to create a wrapped function and custom node definition required for ComfyUI, eliminating all the boilerplate code. In most cases you can just add a @ComfyFunc(\"category\") annotation to your existing function." + }, + { + "author": "OuticNZ", + "title": "ComfyUI-Simple-Of-Complex", + "reference": "https://github.com/OuticNZ/ComfyUI-Simple-Of-Complex", + "files": [ + "https://github.com/OuticNZ/ComfyUI-Simple-Of-Complex" + ], + "install_type": "git-clone", + "description": "Keeping it simple for starting. Single branch for now and will add development branch later." + }, + { + "author": "jtong", + "title": "comfyui-jtong-workflow", + "reference": "https://github.com/jtong/comfyui-jtong-workflow", + "files": [ + "https://github.com/jtong/comfyui-jtong-workflow" + ], + "install_type": "git-clone", + "description": "Nodes:jtong.Highway, Example" + }, + { + "author": "thinkthinking", + "title": "ComfyUI-Ye", + "reference": "https://github.com/thinkthinking/ComfyUI-Ye", + "files": [ + "https://github.com/thinkthinking/ComfyUI-Ye" + ], + "install_type": "git-clone", + "description": "Nodes:Signature|Ye, CheckpointLoader|Ye, PrintHelloWorld|Ye." + }, + { + "author": "BoosterCore", + "title": "ComfyUI-BC-Experimental", + "reference": "https://github.com/BoosterCore/ComfyUI-BC-Experimental", + "files": [ + "https://github.com/BoosterCore/ComfyUI-BC-Experimental" + ], + "install_type": "git-clone", + "description": "Nodes:ClipTextEncodeBC, SaveAnyText, SimpleText" + }, + { + "author": "sonyeon-sj", + "title": "ComfyUI-easy_ImageSize_Selecter", + "reference": "https://github.com/sonyeon-sj/ComfyUI-easy_ImageSize_Selecter", + "files": [ + "https://github.com/sonyeon-sj/ComfyUI-easy_ImageSize_Selecter" + ], + "install_type": "git-clone", + "description": "Custom node for ComfyUI Select the image size from the preset and select Vertical and Horizontal to output Width and Height." + }, + { + "author": "boricuapab", + "title": "ComfyUI_BoricuapabWFNodePack", + "reference": "https://github.com/boricuapab/ComfyUI_BoricuapabWFNodePack", + "files": [ + "https://github.com/boricuapab/ComfyUI_BoricuapabWFNodePack" + ], + "install_type": "git-clone", + "description": "Learning how to make my own comfy ui custom nodes" + }, + { + "author": "mira-6", + "title": "mira-wildcard-node", + "reference": "https://github.com/mira-6/mira-wildcard-node", + "files": [ + "https://github.com/mira-6/mira-wildcard-node" + ], + "install_type": "git-clone", + "description": "Mira's Simple Wildcard Node" + }, + { + "author": "BetaDoggo", + "title": "ComfyUI Tetris", + "id": "tetris", + "reference": "https://github.com/BetaDoggo/ComfyUI-Tetris", + "files": [ + "https://github.com/BetaDoggo/ComfyUI-Tetris" + ], + "install_type": "git-clone", + "description": "The primitive node and dummy input are required because comfy doesn't accept requests with identical graphs. You'll also need a show text node. I like the one from ComfyUI-Custom-Scripts. I got the generic tetris remake from claude so it may or may not be ripped from somewhere else." + }, + { + "author": "FlyMyAI", + "title": "ComfyUI-ExampleNode", + "reference": "https://github.com/FlyMyAI/ComfyUI-ExampleNode", + "files": [ + "https://github.com/FlyMyAI/ComfyUI-ExampleNode" + ], + "install_type": "git-clone", + "description": "Node to provide convenient ComfyUI standard, supported by flymy_comfyui." + }, + { + "author": "Wanghanying", + "title": "ComfyUI_RAGDemo", + "reference": "https://github.com/Wanghanying/ComfyUI_RAGDemo", + "files": [ + "https://github.com/Wanghanying/ComfyUI_RAGDemo" + ], + "install_type": "git-clone", + "description": "RAG Demo for LLM" + }, + { + "author": "FelixTeutsch", + "title": "BachelorThesis", + "reference": "https://github.com/FelixTeutsch/BachelorThesis", + "files": [ + "https://github.com/FelixTeutsch/BachelorThesis" + ], + "install_type": "git-clone", + "description": "This is a ComfyUi custom node, that build a new UI on top of the already existing AI, to enable the use of custom controllers" + }, + { + "author": "jhj0517", + "title": "ComfyUI-CustomNodes-Template", + "reference": "https://github.com/jhj0517/ComfyUI-CustomNodes-Template", + "files": [ + "https://github.com/jhj0517/ComfyUI-CustomNodes-Template" + ], + "install_type": "git-clone", + "description": "This is the ComfyUI custom node template repository that anyone can use to create their own custom nodes." + }, + { + "author": "laogou666", + "title": "Comfyui_LG_Advertisement", + "reference": "https://github.com/LAOGOU-666/Comfyui_LG_Advertisement", + "files": [ + "https://github.com/LAOGOU-666/Comfyui_LG_Advertisement" + ], + "install_type": "git-clone", + "description": "A node for demonstration." + }, + { + "author": "amorano", + "title": "cozy_spoke", + "reference": "https://github.com/cozy-comfyui/cozy_spoke", + "files": [ + "https://github.com/cozy-comfyui/cozy_spoke" + ], + "install_type": "git-clone", + "description": "Example node communicating between ComfyUI Javascript and Python." + }, + { + "author": "amorano", + "title": "Cozy Link Toggle", + "id": "cozyLinkToggle", + "reference": "https://github.com/cozy-comfyui/cozy_link_toggle", + "files": [ + "https://github.com/cozy-comfyui/cozy_link_toggle" + ], + "install_type": "git-clone", + "description": "Example of using ComfyUI Toolbar to Toggle ComfyUI links on/off" + }, + { + "author": "xhiroga", + "title": "ComfyUI-TypeScript-CustomNode", + "reference": "https://github.com/xhiroga/ComfyUI-TypeScript-CustomNode", + "files": [ + "https://github.com/xhiroga/ComfyUI-TypeScript-CustomNode" + ], + "install_type": "git-clone", + "description": "This project is generated from xhiroga/ComfyUI-TypeScript-CustomNode" + }, + { + "author": "zentrocdot", + "title": "ComfyUI-Turtle_Graphics_Demos", + "reference": "https://github.com/zentrocdot/ComfyUI-Turtle_Graphics_Demo", + "files": [ + "https://github.com/zentrocdot/ComfyUI-Turtle_Graphics_Demo" + ], + "description": "ComfyUI node for creating some Turtle Graphic demos.", + "install_type": "git-clone" + }, + { + "author": "cozy-comfyui", + "title": "cozy_ex_dynamic", + "reference": "https://github.com/cozy-comfyui/cozy_ex_dynamic", + "files": [ + "https://github.com/cozy-comfyui/cozy_ex_dynamic" + ], + "description": "Dynamic Node examples for ComfyUI", + "install_type": "git-clone" + }, + { + "author": "Jonathon-Doran", + "title": "remote-combo-demo", + "reference": "https://github.com/Jonathon-Doran/remote-combo-demo", + "files": [ + "https://github.com/Jonathon-Doran/remote-combo-demo" + ], + "install_type": "git-clone", + "description": "A minimal test suite demonstrating how remote COMBO inputs behave in ComfyUI, with and without force_input" + } + ] +} \ No newline at end of file diff --git a/ComfyUI/custom_nodes/ComfyUI-Manager/node_db/tutorial/extension-node-map.json b/ComfyUI/custom_nodes/ComfyUI-Manager/node_db/tutorial/extension-node-map.json new file mode 100644 index 0000000000000000000000000000000000000000..d5b0c984d2dd1e04e64c8a5d76d0b7af1be1b3b6 --- /dev/null +++ b/ComfyUI/custom_nodes/ComfyUI-Manager/node_db/tutorial/extension-node-map.json @@ -0,0 +1,573 @@ +{ + "https://github.com/BadCafeCode/execution-inversion-demo-comfyui": [ + [ + "AccumulateNode", + "AccumulationGetItemNode", + "AccumulationGetLengthNode", + "AccumulationHeadNode", + "AccumulationSetItemNode", + "AccumulationTailNode", + "AccumulationToListNode", + "BoolOperationNode", + "ComponentInput", + "ComponentMetadata", + "ComponentOutput", + "DebugPrint", + "ExecutionBlocker", + "FloatConditions", + "ForLoopClose", + "ForLoopOpen", + "IntConditions", + "IntMathOperation", + "InversionDemoAdvancedPromptNode", + "InversionDemoLazyConditional", + "InversionDemoLazyIndexSwitch", + "InversionDemoLazyMixImages", + "InversionDemoLazySwitch", + "ListToAccumulationNode", + "MakeListNode", + "StringConditions", + "ToBoolNode", + "WhileLoopClose", + "WhileLoopOpen" + ], + { + "title_aux": "execution-inversion-demo-comfyui" + } + ], + "https://github.com/BetaDoggo/ComfyUI-Tetris": [ + [ + "Tetris" + ], + { + "title_aux": "ComfyUI Tetris" + } + ], + "https://github.com/BoosterCore/ComfyUI-BC-Experimental": [ + [ + "ClipTextEncodeBC", + "ClipTextEncodeBCA", + "FluxEmptyLatentSize", + "LoraWithTriggerWord", + "SaveAnyText", + "SimpleText" + ], + { + "title_aux": "ComfyUI-BC-Experimental" + } + ], + "https://github.com/FlyMyAI/ComfyUI-ExampleNode": [ + [ + "ExampleT2IFMANode" + ], + { + "title_aux": "ComfyUI-ExampleNode" + } + ], + "https://github.com/IvanRybakov/comfyui-node-int-to-string-convertor": [ + [ + "Int To String" + ], + { + "title_aux": "comfyui-node-int-to-string-convertor" + } + ], + "https://github.com/LarryJane491/Custom-Node-Base": [ + [ + "My First Node" + ], + { + "title_aux": "Custom-Node-Base" + } + ], + "https://github.com/OuticNZ/ComfyUI-Simple-Of-Complex": [ + [ + "Pipe From Parameters", + "Pipe To Parameters", + "Prompt Tidy", + "Text Switch 2 Way", + "Text With Context" + ], + { + "title_aux": "ComfyUI-Simple-Of-Complex" + } + ], + "https://github.com/Suzie1/ComfyUI_Guide_To_Making_Custom_Nodes": [ + [ + "Concatenate Hello World", + "Hello World Overlay Text", + "Print Hello World" + ], + { + "title_aux": "Guide To Making Custom Nodes in ComfyUI" + } + ], + "https://github.com/Wanghanying/ComfyUI_RAGDemo": [ + [ + "testRAG" + ], + { + "title_aux": "ComfyUI_RAGDemo" + } + ], + "https://github.com/azure-dragon-ai/ComfyUI-HPSv2-Nodes": [ + [ + "GetImageSize", + "HaojihuiHPSv2ImageProcessor", + "HaojihuiHPSv2ImageScore", + "HaojihuiHPSv2ImageScores", + "HaojihuiHPSv2Loader", + "HaojihuiHPSv2SaveAnimatedWEBP", + "HaojihuiHPSv2SaveImage", + "HaojihuiHPSv2SaveWEBP", + "HaojihuiHPSv2SaveWebpImage", + "HaojihuiHPSv2TextProcessor", + "SaveImageWebp", + "ScaleShort" + ], + { + "title_aux": "ComfyUI-HPSv2-Nodes" + } + ], + "https://github.com/bamboodia/BAM_Nodes": [ + [ + "BAM Crop To Ratio", + "BAM Empty Latent By Ratio", + "BAM Get Shortest Side", + "BAM OnOff INT", + "BAM Random Float", + "BAM Random Image From Folder" + ], + { + "title_aux": "BAM Nodes" + } + ], + "https://github.com/boricuapab/ComfyUI_BoricuapabWFNodePack": [ + [ + "BoricuapabWF Concatenate Hello World", + "BoricuapabWF Integer", + "BoricuapabWF Print Hello Puerto Rican World", + "BoricuapabWF Print Puerto Rican" + ], + { + "title_aux": "ComfyUI_BoricuapabWFNodePack" + } + ], + "https://github.com/comfyanonymous/ComfyUI": [ + [ + "AddNoise", + "AlignYourStepsScheduler", + "BasicGuider", + "BasicScheduler", + "BetaSamplingScheduler", + "CFGGuider", + "CLIPAttentionMultiply", + "CLIPLoader", + "CLIPMergeAdd", + "CLIPMergeSimple", + "CLIPMergeSubtract", + "CLIPSave", + "CLIPSetLastLayer", + "CLIPTextEncode", + "CLIPTextEncodeControlnet", + "CLIPTextEncodeFlux", + "CLIPTextEncodeHunyuanDiT", + "CLIPTextEncodePixArtAlpha", + "CLIPTextEncodeSD3", + "CLIPTextEncodeSDXL", + "CLIPTextEncodeSDXLRefiner", + "CLIPVisionEncode", + "CLIPVisionLoader", + "Canny", + "CheckpointLoader", + "CheckpointLoaderSimple", + "CheckpointSave", + "ConditioningAverage", + "ConditioningCombine", + "ConditioningConcat", + "ConditioningSetArea", + "ConditioningSetAreaPercentage", + "ConditioningSetAreaStrength", + "ConditioningSetMask", + "ConditioningSetTimestepRange", + "ConditioningStableAudio", + "ConditioningZeroOut", + "ControlNetApply", + "ControlNetApplyAdvanced", + "ControlNetApplySD3", + "ControlNetInpaintingAliMamaApply", + "ControlNetLoader", + "CropMask", + "DiffControlNetLoader", + "DifferentialDiffusion", + "DiffusersLoader", + "DisableNoise", + "DualCFGGuider", + "DualCLIPLoader", + "EmptyHunyuanLatentVideo", + "EmptyImage", + "EmptyLTXVLatentVideo", + "EmptyLatentAudio", + "EmptyLatentImage", + "EmptyMochiLatentVideo", + "EmptySD3LatentImage", + "ExponentialScheduler", + "FeatherMask", + "FlipSigmas", + "FluxGuidance", + "FreeU", + "FreeU_V2", + "GITSScheduler", + "GLIGENLoader", + "GLIGENTextBoxApply", + "GrowMask", + "HyperTile", + "HypernetworkLoader", + "ImageBatch", + "ImageBlend", + "ImageBlur", + "ImageColorToMask", + "ImageCompositeMasked", + "ImageCrop", + "ImageFromBatch", + "ImageInvert", + "ImageOnlyCheckpointLoader", + "ImageOnlyCheckpointSave", + "ImagePadForOutpaint", + "ImageQuantize", + "ImageScale", + "ImageScaleBy", + "ImageScaleToTotalPixels", + "ImageSharpen", + "ImageToMask", + "ImageUpscaleWithModel", + "InpaintModelConditioning", + "InstructPixToPixConditioning", + "InvertMask", + "JoinImageWithAlpha", + "KSampler", + "KSamplerAdvanced", + "KSamplerSelect", + "KarrasScheduler", + "LTXVConditioning", + "LTXVImgToVideo", + "LTXVScheduler", + "LaplaceScheduler", + "LatentAdd", + "LatentApplyOperation", + "LatentApplyOperationCFG", + "LatentBatch", + "LatentBatchSeedBehavior", + "LatentBlend", + "LatentComposite", + "LatentCompositeMasked", + "LatentCrop", + "LatentFlip", + "LatentFromBatch", + "LatentInterpolate", + "LatentMultiply", + "LatentOperationSharpen", + "LatentOperationTonemapReinhard", + "LatentRotate", + "LatentSubtract", + "LatentUpscale", + "LatentUpscaleBy", + "Load3D", + "Load3DAnimation", + "LoadAudio", + "LoadImage", + "LoadImageMask", + "LoadLatent", + "LoraLoader", + "LoraLoaderModelOnly", + "LoraSave", + "Mahiro", + "MaskComposite", + "MaskToImage", + "ModelMergeAdd", + "ModelMergeAuraflow", + "ModelMergeBlocks", + "ModelMergeFlux1", + "ModelMergeLTXV", + "ModelMergeMochiPreview", + "ModelMergeSD1", + "ModelMergeSD2", + "ModelMergeSD35_Large", + "ModelMergeSD3_2B", + "ModelMergeSDXL", + "ModelMergeSimple", + "ModelMergeSubtract", + "ModelSamplingAuraFlow", + "ModelSamplingContinuousEDM", + "ModelSamplingContinuousV", + "ModelSamplingDiscrete", + "ModelSamplingFlux", + "ModelSamplingLTXV", + "ModelSamplingSD3", + "ModelSamplingStableCascade", + "ModelSave", + "Morphology", + "PatchModelAddDownscale", + "PerpNeg", + "PerpNegGuider", + "PerturbedAttentionGuidance", + "PhotoMakerEncode", + "PhotoMakerLoader", + "PolyexponentialScheduler", + "PorterDuffImageComposite", + "Preview3D", + "PreviewAudio", + "PreviewImage", + "RandomNoise", + "RebatchImages", + "RebatchLatents", + "RepeatImageBatch", + "RepeatLatentBatch", + "RescaleCFG", + "SDTurboScheduler", + "SD_4XUpscale_Conditioning", + "SV3D_Conditioning", + "SVD_img2vid_Conditioning", + "SamplerCustom", + "SamplerCustomAdvanced", + "SamplerDPMAdaptative", + "SamplerDPMPP_2M_SDE", + "SamplerDPMPP_2S_Ancestral", + "SamplerDPMPP_3M_SDE", + "SamplerDPMPP_SDE", + "SamplerEulerAncestral", + "SamplerEulerAncestralCFGPP", + "SamplerEulerCFGpp", + "SamplerLCMUpscale", + "SamplerLMS", + "SaveAnimatedPNG", + "SaveAnimatedWEBP", + "SaveAudio", + "SaveImage", + "SaveImageWebsocket", + "SaveLatent", + "SelfAttentionGuidance", + "SetLatentNoiseMask", + "SetUnionControlNetType", + "SkipLayerGuidanceDiT", + "SkipLayerGuidanceSD3", + "SolidMask", + "SplitImageWithAlpha", + "SplitSigmas", + "SplitSigmasDenoise", + "StableCascade_EmptyLatentImage", + "StableCascade_StageB_Conditioning", + "StableCascade_StageC_VAEEncode", + "StableCascade_SuperResolutionControlnet", + "StableZero123_Conditioning", + "StableZero123_Conditioning_Batched", + "StubConstantImage", + "StubFloat", + "StubImage", + "StubInt", + "StubMask", + "StyleModelApply", + "StyleModelLoader", + "TestAccumulateNode", + "TestAccumulationGetItemNode", + "TestAccumulationGetLengthNode", + "TestAccumulationHeadNode", + "TestAccumulationSetItemNode", + "TestAccumulationTailNode", + "TestAccumulationToListNode", + "TestBoolOperationNode", + "TestCustomIsChanged", + "TestCustomValidation1", + "TestCustomValidation2", + "TestCustomValidation3", + "TestCustomValidation4", + "TestCustomValidation5", + "TestDynamicDependencyCycle", + "TestExecutionBlocker", + "TestFloatConditions", + "TestForLoopClose", + "TestForLoopOpen", + "TestIntConditions", + "TestIntMathOperation", + "TestIsChangedWithConstants", + "TestLazyMixImages", + "TestListToAccumulationNode", + "TestMakeListNode", + "TestMixedExpansionReturns", + "TestStringConditions", + "TestToBoolNode", + "TestVariadicAverage", + "TestWhileLoopClose", + "TestWhileLoopOpen", + "ThresholdMask", + "TomePatchModel", + "TorchCompileModel", + "TripleCLIPLoader", + "UNETLoader", + "UNetCrossAttentionMultiply", + "UNetSelfAttentionMultiply", + "UNetTemporalAttentionMultiply", + "UpscaleModelLoader", + "VAEDecode", + "VAEDecodeAudio", + "VAEDecodeTiled", + "VAEEncode", + "VAEEncodeAudio", + "VAEEncodeForInpaint", + "VAEEncodeTiled", + "VAELoader", + "VAESave", + "VPScheduler", + "VideoLinearCFGGuidance", + "VideoTriangleCFGGuidance", + "WebcamCapture", + "unCLIPCheckpointLoader", + "unCLIPConditioning" + ], + { + "title_aux": "ComfyUI" + } + ], + "https://github.com/dynamixar/Atluris": [ + [ + "RandomLine" + ], + { + "title_aux": "Atluris" + } + ], + "https://github.com/ecjojo/ecjojo-example-nodes": [ + [ + "BiggerNote_Example", + "DisplayTextNode_Example", + "EmptyNode_Example", + "ExampleNode_Example", + "FilePrefixNode_Example", + "HelloWorldNode_Example", + "RandomSizeNode_Example", + "StringNode_Example", + "TextOverlayNode_Example" + ], + { + "title_aux": "ecjojo_example_nodes" + } + ], + "https://github.com/et118/ComfyUI-ElGogh-Nodes": [ + [ + "ElGoghCLIPSetLastLayer", + "ElGoghCheckpointLoaderSimple", + "ElGoghEmptyLatentImage", + "ElGoghKSamplerAdvanced", + "ElGoghNegativePrompt", + "ElGoghPositivePrompt", + "ElGoghPrimaryLoraLoader", + "ElGoghSecondaryLoraLoader", + "ElGoghSendWebsocketNSFWBool", + "ElGoghTertiaryLoraLoader", + "ElGoghVAELoader" + ], + { + "title_aux": "ComfyUI-ElGogh-Nodes" + } + ], + "https://github.com/foxtrot-roger/comfyui-custom-nodes": [ + [ + "RF_Tutorial" + ], + { + "title_aux": "comfyui-custom-nodes" + } + ], + "https://github.com/jhj0517/ComfyUI-CustomNodes-Template": [ + [ + "(Down)Load My Model", + "Calculate Minus", + "Calculate Plus", + "Example Output Node" + ], + { + "title_aux": "ComfyUI-CustomNodes-Template" + } + ], + "https://github.com/jtong/comfyui-jtong-workflow": [ + [ + "Example", + "high_workflow_caller", + "jtong.Highend", + "jtong.Highway" + ], + { + "author": "Trung0246", + "description": "Random nodes for ComfyUI I made to solve my struggle with ComfyUI (ex: pipe, process). Have varying quality.", + "nickname": "ComfyUI-0246", + "title": "ComfyUI-0246", + "title_aux": "comfyui-jtong-workflow" + } + ], + "https://github.com/kappa54m/ComfyUI_Usability": [ + [ + "KLoadImageByPath", + "KLoadImageByPathAdvanced", + "KLoadImageDedup" + ], + { + "title_aux": "ComfyUI-HPSv2-Nodes" + } + ], + "https://github.com/mira-6/mira-wildcard-node": [ + [ + "MiraWildcard" + ], + { + "author": "mira-6", + "description": "Single-node wildcard implementation.", + "nickname": "mira-wildcard-node", + "title": "mira-wildcard-node", + "title_aux": "mira-wildcard-node" + } + ], + "https://github.com/sonyeon-sj/ComfyUI-easy_ImageSize_Selecter": [ + [ + "ImageSizer", + "promptSelecter" + ], + { + "title_aux": "ComfyUI-easy_ImageSize_Selecter" + } + ], + "https://github.com/thinkthinking/ComfyUI-Ye": [ + [ + "CheckpointLoader|Ye", + "OllamaVision|Ye", + "PrintHelloWorld|Ye", + "Signature|Ye" + ], + { + "title_aux": "ComfyUI-Ye" + } + ], + "https://github.com/wailovet/ComfyUI-WW": [ + [ + "WW_AccumulationPreviewImages", + "WW_AppendString", + "WW_CurrentPreviewImages", + "WW_ImageResize", + "WW_PreviewTextNode", + "WW_RandString" + ], + { + "title_aux": "ComfyUI-WW" + } + ], + "https://github.com/yowipr/ComfyUI-Manual": [ + [ + "EXAMPLE", + "M_Layer", + "M_Output", + "M_RenderArea" + ], + { + "title_aux": "ComfyUI-Manual" + } + ] +} \ No newline at end of file diff --git a/ComfyUI/custom_nodes/ComfyUI-Manager/node_db/tutorial/model-list.json b/ComfyUI/custom_nodes/ComfyUI-Manager/node_db/tutorial/model-list.json new file mode 100644 index 0000000000000000000000000000000000000000..8e3e1dc4858a08aa46190aa53ba320d565206cf4 --- /dev/null +++ b/ComfyUI/custom_nodes/ComfyUI-Manager/node_db/tutorial/model-list.json @@ -0,0 +1,3 @@ +{ + "models": [] +} diff --git a/ComfyUI/custom_nodes/ComfyUI-Manager/node_db/tutorial/scan.sh b/ComfyUI/custom_nodes/ComfyUI-Manager/node_db/tutorial/scan.sh new file mode 100644 index 0000000000000000000000000000000000000000..6e3e6dafd1f031eeee2ed170b3ba1b9cf731f924 --- /dev/null +++ b/ComfyUI/custom_nodes/ComfyUI-Manager/node_db/tutorial/scan.sh @@ -0,0 +1,3 @@ +#!/bin/bash +rm ~/.tmp/dev/*.py > /dev/null 2>&1 +python ../../scanner.py ~/.tmp/tutorial diff --git a/ComfyUI/custom_nodes/ComfyUI-Manager/notebooks/comfyui_colab_with_manager.ipynb b/ComfyUI/custom_nodes/ComfyUI-Manager/notebooks/comfyui_colab_with_manager.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..3cfa484b9d2e9bd87624528dd713e83afd4b77ee --- /dev/null +++ b/ComfyUI/custom_nodes/ComfyUI-Manager/notebooks/comfyui_colab_with_manager.ipynb @@ -0,0 +1,373 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "aaaaaaaaaa" + }, + "source": [ + "Git clone the repo and install the requirements. (ignore the pip errors about protobuf)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "bbbbbbbbbb" + }, + "outputs": [], + "source": [ + "# #@title Environment Setup\n", + "\n", + "from pathlib import Path\n", + "\n", + "OPTIONS = {}\n", + "\n", + "USE_GOOGLE_DRIVE = True #@param {type:\"boolean\"}\n", + "UPDATE_COMFY_UI = True #@param {type:\"boolean\"}\n", + "USE_COMFYUI_MANAGER = True #@param {type:\"boolean\"}\n", + "INSTALL_CUSTOM_NODES_DEPENDENCIES = True #@param {type:\"boolean\"}\n", + "OPTIONS['USE_GOOGLE_DRIVE'] = USE_GOOGLE_DRIVE\n", + "OPTIONS['UPDATE_COMFY_UI'] = UPDATE_COMFY_UI\n", + "OPTIONS['USE_COMFYUI_MANAGER'] = USE_COMFYUI_MANAGER\n", + "OPTIONS['INSTALL_CUSTOM_NODES_DEPENDENCIES'] = INSTALL_CUSTOM_NODES_DEPENDENCIES\n", + "\n", + "current_dir = !pwd\n", + "WORKSPACE = f\"{current_dir[0]}/ComfyUI\"\n", + "\n", + "if OPTIONS['USE_GOOGLE_DRIVE']:\n", + " !echo \"Mounting Google Drive...\"\n", + " %cd /\n", + "\n", + " from google.colab import drive\n", + " drive.mount('/content/drive')\n", + "\n", + " WORKSPACE = \"/content/drive/MyDrive/ComfyUI\"\n", + " %cd /content/drive/MyDrive\n", + "\n", + "![ ! -d $WORKSPACE ] && echo -= Initial setup ComfyUI =- && git clone https://github.com/comfyanonymous/ComfyUI\n", + "%cd $WORKSPACE\n", + "\n", + "if OPTIONS['UPDATE_COMFY_UI']:\n", + " !echo -= Updating ComfyUI =-\n", + "\n", + " # Correction of the issue of permissions being deleted on Google Drive.\n", + " ![ -f \".ci/nightly/update_windows/update_comfyui_and_python_dependencies.bat\" ] && chmod 755 .ci/nightly/update_windows/update_comfyui_and_python_dependencies.bat\n", + " ![ -f \".ci/nightly/windows_base_files/run_nvidia_gpu.bat\" ] && chmod 755 .ci/nightly/windows_base_files/run_nvidia_gpu.bat\n", + " ![ -f \".ci/update_windows/update_comfyui_and_python_dependencies.bat\" ] && chmod 755 .ci/update_windows/update_comfyui_and_python_dependencies.bat\n", + " ![ -f \".ci/update_windows_cu118/update_comfyui_and_python_dependencies.bat\" ] && chmod 755 .ci/update_windows_cu118/update_comfyui_and_python_dependencies.bat\n", + " ![ -f \".ci/update_windows/update.py\" ] && chmod 755 .ci/update_windows/update.py\n", + " ![ -f \".ci/update_windows/update_comfyui.bat\" ] && chmod 755 .ci/update_windows/update_comfyui.bat\n", + " ![ -f \".ci/update_windows/README_VERY_IMPORTANT.txt\" ] && chmod 755 .ci/update_windows/README_VERY_IMPORTANT.txt\n", + " ![ -f \".ci/update_windows/run_cpu.bat\" ] && chmod 755 .ci/update_windows/run_cpu.bat\n", + " ![ -f \".ci/update_windows/run_nvidia_gpu.bat\" ] && chmod 755 .ci/update_windows/run_nvidia_gpu.bat\n", + "\n", + " !git pull\n", + "\n", + "!echo -= Install dependencies =-\n", + "!pip3 install accelerate\n", + "!pip3 install einops transformers>=4.28.1 safetensors>=0.4.2 aiohttp pyyaml Pillow scipy tqdm psutil tokenizers>=0.13.3\n", + "!pip3 install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121\n", + "!pip3 install torchsde\n", + "!pip3 install kornia>=0.7.1 spandrel soundfile sentencepiece\n", + "\n", + "if OPTIONS['USE_COMFYUI_MANAGER']:\n", + " %cd custom_nodes\n", + "\n", + " # Correction of the issue of permissions being deleted on Google Drive.\n", + " ![ -f \"ComfyUI-Manager/check.sh\" ] && chmod 755 ComfyUI-Manager/check.sh\n", + " ![ -f \"ComfyUI-Manager/scan.sh\" ] && chmod 755 ComfyUI-Manager/scan.sh\n", + " ![ -f \"ComfyUI-Manager/node_db/dev/scan.sh\" ] && chmod 755 ComfyUI-Manager/node_db/dev/scan.sh\n", + " ![ -f \"ComfyUI-Manager/node_db/tutorial/scan.sh\" ] && chmod 755 ComfyUI-Manager/node_db/tutorial/scan.sh\n", + " ![ -f \"ComfyUI-Manager/scripts/install-comfyui-venv-linux.sh\" ] && chmod 755 ComfyUI-Manager/scripts/install-comfyui-venv-linux.sh\n", + " ![ -f \"ComfyUI-Manager/scripts/install-comfyui-venv-win.bat\" ] && chmod 755 ComfyUI-Manager/scripts/install-comfyui-venv-win.bat\n", + "\n", + " ![ ! -d ComfyUI-Manager ] && echo -= Initial setup ComfyUI-Manager =- && git clone https://github.com/ltdrdata/ComfyUI-Manager\n", + " %cd ComfyUI-Manager\n", + " !git pull\n", + "\n", + "%cd $WORKSPACE\n", + "\n", + "if OPTIONS['INSTALL_CUSTOM_NODES_DEPENDENCIES']:\n", + " !echo -= Install custom nodes dependencies =-\n", + " !pip install GitPython\n", + " !python custom_nodes/ComfyUI-Manager/cm-cli.py restore-dependencies\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "cccccccccc" + }, + "source": [ + "Download some models/checkpoints/vae or custom comfyui nodes (uncomment the commands for the ones you want)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "dddddddddd" + }, + "outputs": [], + "source": [ + "# Checkpoints\n", + "\n", + "### SDXL\n", + "### I recommend these workflow examples: https://comfyanonymous.github.io/ComfyUI_examples/sdxl/\n", + "\n", + "#!wget -c https://huggingface.co/stabilityai/stable-diffusion-xl-base-1.0/resolve/main/sd_xl_base_1.0.safetensors -P ./models/checkpoints/\n", + "#!wget -c https://huggingface.co/stabilityai/stable-diffusion-xl-refiner-1.0/resolve/main/sd_xl_refiner_1.0.safetensors -P ./models/checkpoints/\n", + "\n", + "# SDXL ReVision\n", + "#!wget -c https://huggingface.co/comfyanonymous/clip_vision_g/resolve/main/clip_vision_g.safetensors -P ./models/clip_vision/\n", + "\n", + "# SD1.5\n", + "!wget -c https://huggingface.co/runwayml/stable-diffusion-v1-5/resolve/main/v1-5-pruned-emaonly.ckpt -P ./models/checkpoints/\n", + "\n", + "# SD2\n", + "#!wget -c https://huggingface.co/stabilityai/stable-diffusion-2-1-base/resolve/main/v2-1_512-ema-pruned.safetensors -P ./models/checkpoints/\n", + "#!wget -c https://huggingface.co/stabilityai/stable-diffusion-2-1/resolve/main/v2-1_768-ema-pruned.safetensors -P ./models/checkpoints/\n", + "\n", + "# Some SD1.5 anime style\n", + "#!wget -c https://huggingface.co/WarriorMama777/OrangeMixs/resolve/main/Models/AbyssOrangeMix2/AbyssOrangeMix2_hard.safetensors -P ./models/checkpoints/\n", + "#!wget -c https://huggingface.co/WarriorMama777/OrangeMixs/resolve/main/Models/AbyssOrangeMix3/AOM3A1_orangemixs.safetensors -P ./models/checkpoints/\n", + "#!wget -c https://huggingface.co/WarriorMama777/OrangeMixs/resolve/main/Models/AbyssOrangeMix3/AOM3A3_orangemixs.safetensors -P ./models/checkpoints/\n", + "#!wget -c https://huggingface.co/Linaqruf/anything-v3.0/resolve/main/anything-v3-fp16-pruned.safetensors -P ./models/checkpoints/\n", + "\n", + "# Waifu Diffusion 1.5 (anime style SD2.x 768-v)\n", + "#!wget -c https://huggingface.co/waifu-diffusion/wd-1-5-beta3/resolve/main/wd-illusion-fp16.safetensors -P ./models/checkpoints/\n", + "\n", + "\n", + "# unCLIP models\n", + "#!wget -c https://huggingface.co/comfyanonymous/illuminatiDiffusionV1_v11_unCLIP/resolve/main/illuminatiDiffusionV1_v11-unclip-h-fp16.safetensors -P ./models/checkpoints/\n", + "#!wget -c https://huggingface.co/comfyanonymous/wd-1.5-beta2_unCLIP/resolve/main/wd-1-5-beta2-aesthetic-unclip-h-fp16.safetensors -P ./models/checkpoints/\n", + "\n", + "\n", + "# VAE\n", + "!wget -c https://huggingface.co/stabilityai/sd-vae-ft-mse-original/resolve/main/vae-ft-mse-840000-ema-pruned.safetensors -P ./models/vae/\n", + "#!wget -c https://huggingface.co/WarriorMama777/OrangeMixs/resolve/main/VAEs/orangemix.vae.pt -P ./models/vae/\n", + "#!wget -c https://huggingface.co/hakurei/waifu-diffusion-v1-4/resolve/main/vae/kl-f8-anime2.ckpt -P ./models/vae/\n", + "\n", + "\n", + "# Loras\n", + "#!wget -c https://civitai.com/api/download/models/10350 -O ./models/loras/theovercomer8sContrastFix_sd21768.safetensors #theovercomer8sContrastFix SD2.x 768-v\n", + "#!wget -c https://civitai.com/api/download/models/10638 -O ./models/loras/theovercomer8sContrastFix_sd15.safetensors #theovercomer8sContrastFix SD1.x\n", + "#!wget -c https://huggingface.co/stabilityai/stable-diffusion-xl-base-1.0/resolve/main/sd_xl_offset_example-lora_1.0.safetensors -P ./models/loras/ #SDXL offset noise lora\n", + "\n", + "\n", + "# T2I-Adapter\n", + "#!wget -c https://huggingface.co/TencentARC/T2I-Adapter/resolve/main/models/t2iadapter_depth_sd14v1.pth -P ./models/controlnet/\n", + "#!wget -c https://huggingface.co/TencentARC/T2I-Adapter/resolve/main/models/t2iadapter_seg_sd14v1.pth -P ./models/controlnet/\n", + "#!wget -c https://huggingface.co/TencentARC/T2I-Adapter/resolve/main/models/t2iadapter_sketch_sd14v1.pth -P ./models/controlnet/\n", + "#!wget -c https://huggingface.co/TencentARC/T2I-Adapter/resolve/main/models/t2iadapter_keypose_sd14v1.pth -P ./models/controlnet/\n", + "#!wget -c https://huggingface.co/TencentARC/T2I-Adapter/resolve/main/models/t2iadapter_openpose_sd14v1.pth -P ./models/controlnet/\n", + "#!wget -c https://huggingface.co/TencentARC/T2I-Adapter/resolve/main/models/t2iadapter_color_sd14v1.pth -P ./models/controlnet/\n", + "#!wget -c https://huggingface.co/TencentARC/T2I-Adapter/resolve/main/models/t2iadapter_canny_sd14v1.pth -P ./models/controlnet/\n", + "\n", + "# T2I Styles Model\n", + "#!wget -c https://huggingface.co/TencentARC/T2I-Adapter/resolve/main/models/t2iadapter_style_sd14v1.pth -P ./models/style_models/\n", + "\n", + "# CLIPVision model (needed for styles model)\n", + "#!wget -c https://huggingface.co/openai/clip-vit-large-patch14/resolve/main/pytorch_model.bin -O ./models/clip_vision/clip_vit14.bin\n", + "\n", + "\n", + "# ControlNet\n", + "#!wget -c https://huggingface.co/comfyanonymous/ControlNet-v1-1_fp16_safetensors/resolve/main/control_v11e_sd15_ip2p_fp16.safetensors -P ./models/controlnet/\n", + "#!wget -c https://huggingface.co/comfyanonymous/ControlNet-v1-1_fp16_safetensors/resolve/main/control_v11e_sd15_shuffle_fp16.safetensors -P ./models/controlnet/\n", + "#!wget -c https://huggingface.co/comfyanonymous/ControlNet-v1-1_fp16_safetensors/resolve/main/control_v11p_sd15_canny_fp16.safetensors -P ./models/controlnet/\n", + "#!wget -c https://huggingface.co/comfyanonymous/ControlNet-v1-1_fp16_safetensors/resolve/main/control_v11f1p_sd15_depth_fp16.safetensors -P ./models/controlnet/\n", + "#!wget -c https://huggingface.co/comfyanonymous/ControlNet-v1-1_fp16_safetensors/resolve/main/control_v11p_sd15_inpaint_fp16.safetensors -P ./models/controlnet/\n", + "#!wget -c https://huggingface.co/comfyanonymous/ControlNet-v1-1_fp16_safetensors/resolve/main/control_v11p_sd15_lineart_fp16.safetensors -P ./models/controlnet/\n", + "#!wget -c https://huggingface.co/comfyanonymous/ControlNet-v1-1_fp16_safetensors/resolve/main/control_v11p_sd15_mlsd_fp16.safetensors -P ./models/controlnet/\n", + "#!wget -c https://huggingface.co/comfyanonymous/ControlNet-v1-1_fp16_safetensors/resolve/main/control_v11p_sd15_normalbae_fp16.safetensors -P ./models/controlnet/\n", + "#!wget -c https://huggingface.co/comfyanonymous/ControlNet-v1-1_fp16_safetensors/resolve/main/control_v11p_sd15_openpose_fp16.safetensors -P ./models/controlnet/\n", + "#!wget -c https://huggingface.co/comfyanonymous/ControlNet-v1-1_fp16_safetensors/resolve/main/control_v11p_sd15_scribble_fp16.safetensors -P ./models/controlnet/\n", + "#!wget -c https://huggingface.co/comfyanonymous/ControlNet-v1-1_fp16_safetensors/resolve/main/control_v11p_sd15_seg_fp16.safetensors -P ./models/controlnet/\n", + "#!wget -c https://huggingface.co/comfyanonymous/ControlNet-v1-1_fp16_safetensors/resolve/main/control_v11p_sd15_softedge_fp16.safetensors -P ./models/controlnet/\n", + "#!wget -c https://huggingface.co/comfyanonymous/ControlNet-v1-1_fp16_safetensors/resolve/main/control_v11p_sd15s2_lineart_anime_fp16.safetensors -P ./models/controlnet/\n", + "#!wget -c https://huggingface.co/comfyanonymous/ControlNet-v1-1_fp16_safetensors/resolve/main/control_v11u_sd15_tile_fp16.safetensors -P ./models/controlnet/\n", + "\n", + "# ControlNet SDXL\n", + "#!wget -c https://huggingface.co/stabilityai/control-lora/resolve/main/control-LoRAs-rank256/control-lora-canny-rank256.safetensors -P ./models/controlnet/\n", + "#!wget -c https://huggingface.co/stabilityai/control-lora/resolve/main/control-LoRAs-rank256/control-lora-depth-rank256.safetensors -P ./models/controlnet/\n", + "#!wget -c https://huggingface.co/stabilityai/control-lora/resolve/main/control-LoRAs-rank256/control-lora-recolor-rank256.safetensors -P ./models/controlnet/\n", + "#!wget -c https://huggingface.co/stabilityai/control-lora/resolve/main/control-LoRAs-rank256/control-lora-sketch-rank256.safetensors -P ./models/controlnet/\n", + "\n", + "# Controlnet Preprocessor nodes by Fannovel16\n", + "#!cd custom_nodes && git clone https://github.com/Fannovel16/comfy_controlnet_preprocessors; cd comfy_controlnet_preprocessors && python install.py\n", + "\n", + "\n", + "# GLIGEN\n", + "#!wget -c https://huggingface.co/comfyanonymous/GLIGEN_pruned_safetensors/resolve/main/gligen_sd14_textbox_pruned_fp16.safetensors -P ./models/gligen/\n", + "\n", + "\n", + "# ESRGAN upscale model\n", + "#!wget -c https://github.com/xinntao/Real-ESRGAN/releases/download/v0.1.0/RealESRGAN_x4plus.pth -P ./models/upscale_models/\n", + "#!wget -c https://huggingface.co/sberbank-ai/Real-ESRGAN/resolve/main/RealESRGAN_x2.pth -P ./models/upscale_models/\n", + "#!wget -c https://huggingface.co/sberbank-ai/Real-ESRGAN/resolve/main/RealESRGAN_x4.pth -P ./models/upscale_models/\n", + "\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "kkkkkkkkkkkkkkk" + }, + "source": [ + "### Run ComfyUI with cloudflared (Recommended Way)\n", + "\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "jjjjjjjjjjjjjj" + }, + "outputs": [], + "source": [ + "!wget -P ~ https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb\n", + "!dpkg -i ~/cloudflared-linux-amd64.deb\n", + "\n", + "import subprocess\n", + "import threading\n", + "import time\n", + "import socket\n", + "import urllib.request\n", + "\n", + "def iframe_thread(port):\n", + " while True:\n", + " time.sleep(0.5)\n", + " sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n", + " result = sock.connect_ex(('127.0.0.1', port))\n", + " if result == 0:\n", + " break\n", + " sock.close()\n", + " print(\"\\nComfyUI finished loading, trying to launch cloudflared (if it gets stuck here cloudflared is having issues)\\n\")\n", + "\n", + " p = subprocess.Popen([\"cloudflared\", \"tunnel\", \"--url\", \"http://127.0.0.1:{}\".format(port)], stdout=subprocess.PIPE, stderr=subprocess.PIPE)\n", + " for line in p.stderr:\n", + " l = line.decode()\n", + " if \"trycloudflare.com \" in l:\n", + " print(\"This is the URL to access ComfyUI:\", l[l.find(\"http\"):], end='')\n", + " #print(l, end='')\n", + "\n", + "\n", + "threading.Thread(target=iframe_thread, daemon=True, args=(8188,)).start()\n", + "\n", + "!python main.py --dont-print-server" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "kkkkkkkkkkkkkk" + }, + "source": [ + "### Run ComfyUI with localtunnel\n", + "\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "jjjjjjjjjjjjj" + }, + "outputs": [], + "source": [ + "!npm install -g localtunnel\n", + "\n", + "import subprocess\n", + "import threading\n", + "import time\n", + "import socket\n", + "import urllib.request\n", + "\n", + "def iframe_thread(port):\n", + " while True:\n", + " time.sleep(0.5)\n", + " sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n", + " result = sock.connect_ex(('127.0.0.1', port))\n", + " if result == 0:\n", + " break\n", + " sock.close()\n", + " print(\"\\nComfyUI finished loading, trying to launch localtunnel (if it gets stuck here localtunnel is having issues)\\n\")\n", + "\n", + " print(\"The password/enpoint ip for localtunnel is:\", urllib.request.urlopen('https://ipv4.icanhazip.com').read().decode('utf8').strip(\"\\n\"))\n", + " p = subprocess.Popen([\"lt\", \"--port\", \"{}\".format(port)], stdout=subprocess.PIPE)\n", + " for line in p.stdout:\n", + " print(line.decode(), end='')\n", + "\n", + "\n", + "threading.Thread(target=iframe_thread, daemon=True, args=(8188,)).start()\n", + "\n", + "!python main.py --dont-print-server" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "gggggggggg" + }, + "source": [ + "### Run ComfyUI with colab iframe (use only in case the previous way with localtunnel doesn't work)\n", + "\n", + "You should see the ui appear in an iframe. If you get a 403 error, it's your firefox settings or an extension that's messing things up.\n", + "\n", + "If you want to open it in another window use the link.\n", + "\n", + "Note that some UI features like live image previews won't work because the colab iframe blocks websockets." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "hhhhhhhhhh" + }, + "outputs": [], + "source": [ + "import threading\n", + "import time\n", + "import socket\n", + "def iframe_thread(port):\n", + " while True:\n", + " time.sleep(0.5)\n", + " sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n", + " result = sock.connect_ex(('127.0.0.1', port))\n", + " if result == 0:\n", + " break\n", + " sock.close()\n", + " from google.colab import output\n", + " output.serve_kernel_port_as_iframe(port, height=1024)\n", + " print(\"to open it in a window you can open this link here:\")\n", + " output.serve_kernel_port_as_window(port)\n", + "\n", + "threading.Thread(target=iframe_thread, daemon=True, args=(8188,)).start()\n", + "\n", + "!python main.py --dont-print-server" + ] + } + ], + "metadata": { + "accelerator": "GPU", + "colab": { + "provenance": [] + }, + "gpuClass": "standard", + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + }, + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/ComfyUI/custom_nodes/ComfyUI-Manager/scripts/colab-dependencies.py b/ComfyUI/custom_nodes/ComfyUI-Manager/scripts/colab-dependencies.py new file mode 100644 index 0000000000000000000000000000000000000000..d5a70ed6dd92ba90e8084e07fbb9097fe3096ea5 --- /dev/null +++ b/ComfyUI/custom_nodes/ComfyUI-Manager/scripts/colab-dependencies.py @@ -0,0 +1,39 @@ +import os +import subprocess + + +def get_enabled_subdirectories_with_files(base_directory): + subdirs_with_files = [] + for subdir in os.listdir(base_directory): + try: + full_path = os.path.join(base_directory, subdir) + if os.path.isdir(full_path) and not subdir.endswith(".disabled") and not subdir.startswith('.') and subdir != '__pycache__': + print(f"## Install dependencies for '{subdir}'") + requirements_file = os.path.join(full_path, "requirements.txt") + install_script = os.path.join(full_path, "install.py") + + if os.path.exists(requirements_file) or os.path.exists(install_script): + subdirs_with_files.append((full_path, requirements_file, install_script)) + except Exception as e: + print(f"EXCEPTION During Dependencies INSTALL on '{subdir}':\n{e}") + + return subdirs_with_files + + +def install_requirements(requirements_file_path): + if os.path.exists(requirements_file_path): + subprocess.run(["pip", "install", "-r", requirements_file_path]) + + +def run_install_script(install_script_path): + if os.path.exists(install_script_path): + subprocess.run(["python", install_script_path]) + + +custom_nodes_directory = "custom_nodes" +subdirs_with_files = get_enabled_subdirectories_with_files(custom_nodes_directory) + + +for subdir, requirements_file, install_script in subdirs_with_files: + install_requirements(requirements_file) + run_install_script(install_script) diff --git a/ComfyUI/custom_nodes/ComfyUI-Manager/scripts/install-comfyui-venv-linux.sh b/ComfyUI/custom_nodes/ComfyUI-Manager/scripts/install-comfyui-venv-linux.sh new file mode 100644 index 0000000000000000000000000000000000000000..5a736ef1be2757c9b0307132506916411a65b46b --- /dev/null +++ b/ComfyUI/custom_nodes/ComfyUI-Manager/scripts/install-comfyui-venv-linux.sh @@ -0,0 +1,21 @@ +git clone https://github.com/comfyanonymous/ComfyUI +cd ComfyUI/custom_nodes +git clone https://github.com/ltdrdata/ComfyUI-Manager comfyui-manager +cd .. +python -m venv venv +source venv/bin/activate +python -m pip install torch torchvision torchaudio --extra-index-url https://download.pytorch.org/whl/cu121 +python -m pip install -r requirements.txt +python -m pip install -r custom_nodes/comfyui-manager/requirements.txt +cd .. +echo "#!/bin/bash" > run_gpu.sh +echo "cd ComfyUI" >> run_gpu.sh +echo "source venv/bin/activate" >> run_gpu.sh +echo "python main.py --preview-method auto" >> run_gpu.sh +chmod +x run_gpu.sh + +echo "#!/bin/bash" > run_cpu.sh +echo "cd ComfyUI" >> run_cpu.sh +echo "source venv/bin/activate" >> run_cpu.sh +echo "python main.py --preview-method auto --cpu" >> run_cpu.sh +chmod +x run_cpu.sh diff --git a/ComfyUI/custom_nodes/ComfyUI-Manager/scripts/install-comfyui-venv-win.bat b/ComfyUI/custom_nodes/ComfyUI-Manager/scripts/install-comfyui-venv-win.bat new file mode 100644 index 0000000000000000000000000000000000000000..47470098bb09af02abb0a7e0021a4da49df47213 --- /dev/null +++ b/ComfyUI/custom_nodes/ComfyUI-Manager/scripts/install-comfyui-venv-win.bat @@ -0,0 +1,17 @@ +git clone https://github.com/comfyanonymous/ComfyUI +cd ComfyUI/custom_nodes +git clone https://github.com/ltdrdata/ComfyUI-Manager comfyui-manager +cd .. +python -m venv venv +call venv/Scripts/activate +python -m pip install torch torchvision torchaudio --extra-index-url https://download.pytorch.org/whl/cu121 +python -m pip install -r requirements.txt +python -m pip install -r custom_nodes/comfyui-manager/requirements.txt +cd .. +echo "cd ComfyUI" >> run_gpu.bat +echo "call venv/Scripts/activate" >> run_gpu.bat +echo "python main.py" >> run_gpu.bat + +echo "cd ComfyUI" >> run_cpu.bat +echo "call venv/Scripts/activate" >> run_cpu.bat +echo "python main.py --cpu" >> run_cpu.bat diff --git a/ComfyUI/custom_nodes/ComfyUI-Manager/scripts/install-manager-for-portable-version.bat b/ComfyUI/custom_nodes/ComfyUI-Manager/scripts/install-manager-for-portable-version.bat new file mode 100644 index 0000000000000000000000000000000000000000..6eb58b741b09be2c74aa85e235825a5859b84978 --- /dev/null +++ b/ComfyUI/custom_nodes/ComfyUI-Manager/scripts/install-manager-for-portable-version.bat @@ -0,0 +1,3 @@ +.\python_embeded\python.exe -s -m pip install gitpython +.\python_embeded\python.exe -c "import git; git.Repo.clone_from('https://github.com/ltdrdata/ComfyUI-Manager', './ComfyUI/custom_nodes/comfyui-manager')" +.\python_embeded\python.exe -m pip install -r ./ComfyUI/custom_nodes/comfyui-manager/requirements.txt diff --git a/ComfyUI/custom_nodes/ComfyUI-Manager/snapshots/the_snapshot_files_are_located_here b/ComfyUI/custom_nodes/ComfyUI-Manager/snapshots/the_snapshot_files_are_located_here new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/ComfyUI/custom_nodes/ComfyUI_essentials/.github/workflows/publish.yml b/ComfyUI/custom_nodes/ComfyUI_essentials/.github/workflows/publish.yml new file mode 100644 index 0000000000000000000000000000000000000000..a4fd89247c103447a257600ca36e31ad5bf8fdf3 --- /dev/null +++ b/ComfyUI/custom_nodes/ComfyUI_essentials/.github/workflows/publish.yml @@ -0,0 +1,22 @@ +name: Publish to Comfy registry +on: + workflow_dispatch: + push: + branches: + - main + - master + paths: + - "pyproject.toml" + +jobs: + publish-node: + name: Publish Custom Node to registry + runs-on: ubuntu-latest + steps: + - name: Check out code + uses: actions/checkout@v4 + - name: Publish Custom Node + uses: Comfy-Org/publish-node-action@main + with: + ## Add your own personal access token to your Github Repository secrets and reference it here. + personal_access_token: ${{ secrets.REGISTRY_ACCESS_TOKEN }} diff --git a/ComfyUI/custom_nodes/ComfyUI_essentials/fonts/ShareTechMono-Regular.ttf b/ComfyUI/custom_nodes/ComfyUI_essentials/fonts/ShareTechMono-Regular.ttf new file mode 100644 index 0000000000000000000000000000000000000000..0ae0b19750c51a751bc45f54622443d55d643999 Binary files /dev/null and b/ComfyUI/custom_nodes/ComfyUI_essentials/fonts/ShareTechMono-Regular.ttf differ diff --git a/ComfyUI/custom_nodes/ComfyUI_essentials/fonts/put_font_files_here.txt b/ComfyUI/custom_nodes/ComfyUI_essentials/fonts/put_font_files_here.txt new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/ComfyUI/custom_nodes/ComfyUI_essentials/js/DisplayAny.js b/ComfyUI/custom_nodes/ComfyUI_essentials/js/DisplayAny.js new file mode 100644 index 0000000000000000000000000000000000000000..ccfa68c138d8fdc87e57132aed8a6a74f94c2dd1 --- /dev/null +++ b/ComfyUI/custom_nodes/ComfyUI_essentials/js/DisplayAny.js @@ -0,0 +1,36 @@ +import { app } from "../../scripts/app.js"; +import { ComfyWidgets } from "../../scripts/widgets.js"; + +app.registerExtension({ + name: "essentials.DisplayAny", + async beforeRegisterNodeDef(nodeType, nodeData, app) { + if (!nodeData?.category?.startsWith("essentials")) { + return; + } + + if (nodeData.name === "DisplayAny") { + const onExecuted = nodeType.prototype.onExecuted; + + nodeType.prototype.onExecuted = function (message) { + onExecuted?.apply(this, arguments); + + if (this.widgets) { + for (let i = 1; i < this.widgets.length; i++) { + this.widgets[i].onRemove?.(); + } + this.widgets.length = 1; + } + + // Check if the "text" widget already exists. + let textWidget = this.widgets && this.widgets.find(w => w.name === "displaytext"); + if (!textWidget) { + textWidget = ComfyWidgets["STRING"](this, "displaytext", ["STRING", { multiline: true }], app).widget; + textWidget.inputEl.readOnly = true; + textWidget.inputEl.style.border = "none"; + textWidget.inputEl.style.backgroundColor = "transparent"; + } + textWidget.value = message["text"].join(""); + }; + } + }, +}); \ No newline at end of file diff --git a/ComfyUI/custom_nodes/ComfyUI_essentials/js/FluxAttentionSeeker.js b/ComfyUI/custom_nodes/ComfyUI_essentials/js/FluxAttentionSeeker.js new file mode 100644 index 0000000000000000000000000000000000000000..b31989c51ebcabd0b87ffd0b9f71f1a6ccb702a7 --- /dev/null +++ b/ComfyUI/custom_nodes/ComfyUI_essentials/js/FluxAttentionSeeker.js @@ -0,0 +1,133 @@ +import { app } from "../../scripts/app.js"; + +app.registerExtension({ + name: "essentials.FluxAttentionSeeker", + async beforeRegisterNodeDef(nodeType, nodeData, app) { + if (!nodeData?.category?.startsWith("essentials")) { + return; + } + + if (nodeData.name === "FluxAttentionSeeker+") { + const onCreated = nodeType.prototype.onNodeCreated; + + nodeType.prototype.onNodeCreated = function () { + this.addWidget("button", "RESET ALL", null, () => { + this.widgets.forEach(w => { + if (w.type === "slider") { + w.value = 1.0; + } + }); + }); + + this.addWidget("button", "ZERO ALL", null, () => { + this.widgets.forEach(w => { + if (w.type === "slider") { + w.value = 0.0; + } + }); + }); + + this.addWidget("button", "REPEAT FIRST", null, () => { + var clip_value = undefined; + var t5_value = undefined; + this.widgets.forEach(w => { + if (w.name.startsWith('clip_l')) { + if (clip_value === undefined) { + clip_value = w.value; + } + w.value = clip_value; + } else if (w.name.startsWith('t5')) { + if (t5_value === undefined) { + t5_value = w.value; + } + w.value = t5_value; + } + }); + }); + }; + } + }, +}); + +app.registerExtension({ + name: "essentials.SD3AttentionSeekerLG", + async beforeRegisterNodeDef(nodeType, nodeData, app) { + if (!nodeData?.category?.startsWith("essentials")) { + return; + } + + if (nodeData.name === "SD3AttentionSeekerLG+") { + const onCreated = nodeType.prototype.onNodeCreated; + + nodeType.prototype.onNodeCreated = function () { + this.addWidget("button", "RESET L", null, () => { + this.widgets.forEach(w => { + if (w.type === "slider" && w.name.startsWith('clip_l')) { + w.value = 1.0; + } + }); + }); + this.addWidget("button", "RESET G", null, () => { + this.widgets.forEach(w => { + if (w.type === "slider" && w.name.startsWith('clip_g')) { + w.value = 1.0; + } + }); + }); + + this.addWidget("button", "REPEAT FIRST", null, () => { + var clip_l_value = undefined; + var clip_g_value = undefined; + this.widgets.forEach(w => { + if (w.name.startsWith('clip_l')) { + if (clip_l_value === undefined) { + clip_l_value = w.value; + } + w.value = clip_l_value; + } else if (w.name.startsWith('clip_g')) { + if (clip_g_value === undefined) { + clip_g_value = w.value; + } + w.value = clip_g_value; + } + }); + }); + }; + } + }, +}); + +app.registerExtension({ + name: "essentials.SD3AttentionSeekerT5", + async beforeRegisterNodeDef(nodeType, nodeData, app) { + if (!nodeData?.category?.startsWith("essentials")) { + return; + } + + if (nodeData.name === "SD3AttentionSeekerT5+") { + const onCreated = nodeType.prototype.onNodeCreated; + + nodeType.prototype.onNodeCreated = function () { + this.addWidget("button", "RESET ALL", null, () => { + this.widgets.forEach(w => { + if (w.type === "slider") { + w.value = 1.0; + } + }); + }); + + this.addWidget("button", "REPEAT FIRST", null, () => { + var t5_value = undefined; + this.widgets.forEach(w => { + if (w.name.startsWith('t5')) { + if (t5_value === undefined) { + t5_value = w.value; + } + w.value = t5_value; + } + }); + }); + }; + } + }, +}); \ No newline at end of file diff --git a/ComfyUI/custom_nodes/ComfyUI_essentials/luts/put_luts_files_here.txt b/ComfyUI/custom_nodes/ComfyUI_essentials/luts/put_luts_files_here.txt new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/ComfyUI/custom_nodes/RES4LYF/aura/mmdit.py b/ComfyUI/custom_nodes/RES4LYF/aura/mmdit.py new file mode 100644 index 0000000000000000000000000000000000000000..9d086adb755de014168a3d49d927080dd2e162c0 --- /dev/null +++ b/ComfyUI/custom_nodes/RES4LYF/aura/mmdit.py @@ -0,0 +1,901 @@ +#AuraFlow MMDiT +#Originally written by the AuraFlow Authors + +import math + +import torch +import torch.nn as nn +import torch.nn.functional as F + +#from comfy.ldm.modules.attention import optimized_attention +from comfy.ldm.modules.attention import attention_pytorch + +import comfy.ops +import comfy.ldm.common_dit + +from ..helper import ExtraOptions + +from typing import Dict, Optional, Tuple, List +from ..latents import slerp_tensor, interpolate_spd, tile_latent, untile_latent, gaussian_blur_2d, median_blur_2d +from ..style_transfer import apply_scattersort_masked, apply_scattersort_tiled, adain_seq_inplace, adain_patchwise_row_batch_med, adain_patchwise_row_batch +from einops import rearrange +def modulate(x, shift, scale): + return x * (1 + scale.unsqueeze(1)) + shift.unsqueeze(1) + + +def find_multiple(n: int, k: int) -> int: + if n % k == 0: + return n + return n + k - (n % k) + + +class MLP(nn.Module): # not executed directly with ReAura? + def __init__(self, dim, hidden_dim=None, dtype=None, device=None, operations=None) -> None: + super().__init__() + if hidden_dim is None: + hidden_dim = 4 * dim + + n_hidden = int(2 * hidden_dim / 3) + n_hidden = find_multiple(n_hidden, 256) + + self.c_fc1 = operations.Linear(dim, n_hidden, bias=False, dtype=dtype, device=device) + self.c_fc2 = operations.Linear(dim, n_hidden, bias=False, dtype=dtype, device=device) + self.c_proj = operations.Linear(n_hidden, dim, bias=False, dtype=dtype, device=device) + + #@torch.compile(mode="default", dynamic=False, fullgraph=False, backend="inductor") + def forward(self, x: torch.Tensor) -> torch.Tensor: + x = F.silu(self.c_fc1(x)) * self.c_fc2(x) + x = self.c_proj(x) + return x + + +class MultiHeadLayerNorm(nn.Module): + def __init__(self, hidden_size=None, eps=1e-5, dtype=None, device=None): + # Copy pasta from https://github.com/huggingface/transformers/blob/e5f71ecaae50ea476d1e12351003790273c4b2ed/src/transformers/models/cohere/modeling_cohere.py#L78 + + super().__init__() + self.weight = nn.Parameter(torch.empty(hidden_size, dtype=dtype, device=device)) + self.variance_epsilon = eps + + #@torch.compile(mode="default", dynamic=False, fullgraph=False, backend="inductor") + def forward(self, hidden_states): + input_dtype = hidden_states.dtype + hidden_states = hidden_states.to(torch.float32) + mean = hidden_states.mean(-1, keepdim=True) + variance = (hidden_states - mean).pow(2).mean(-1, keepdim=True) + hidden_states = (hidden_states - mean) * torch.rsqrt( + variance + self.variance_epsilon + ) + hidden_states = self.weight.to(torch.float32) * hidden_states + return hidden_states.to(input_dtype) + +class ReSingleAttention(nn.Module): + def __init__(self, dim, n_heads, mh_qknorm=False, dtype=None, device=None, operations=None): + super().__init__() + + self.n_heads = n_heads + self.head_dim = dim // n_heads + + # this is for cond + self.w1q = operations.Linear(dim, dim, bias=False, dtype=dtype, device=device) + self.w1k = operations.Linear(dim, dim, bias=False, dtype=dtype, device=device) + self.w1v = operations.Linear(dim, dim, bias=False, dtype=dtype, device=device) + self.w1o = operations.Linear(dim, dim, bias=False, dtype=dtype, device=device) + + self.q_norm1 = ( + MultiHeadLayerNorm((self.n_heads, self.head_dim), dtype=dtype, device=device) + if mh_qknorm + else operations.LayerNorm(self.head_dim, elementwise_affine=False, dtype=dtype, device=device) + ) + self.k_norm1 = ( + MultiHeadLayerNorm((self.n_heads, self.head_dim), dtype=dtype, device=device) + if mh_qknorm + else operations.LayerNorm(self.head_dim, elementwise_affine=False, dtype=dtype, device=device) + ) + + #@torch.compile(mode="default", dynamic=False, fullgraph=False, backend="inductor") # c = 1,4552,3072 #operations.Linear = torch.nn.Linear with recast + def forward(self, c, mask=None): + + bsz, seqlen1, _ = c.shape + + q, k, v = self.w1q(c), self.w1k(c), self.w1v(c) + q = q.view(bsz, seqlen1, self.n_heads, self.head_dim) + k = k.view(bsz, seqlen1, self.n_heads, self.head_dim) + v = v.view(bsz, seqlen1, self.n_heads, self.head_dim) + q, k = self.q_norm1(q), self.k_norm1(k) + + output = attention_pytorch(q.permute(0, 2, 1, 3), k.permute(0, 2, 1, 3), v.permute(0, 2, 1, 3), self.n_heads, skip_reshape=True, mask=mask) + c = self.w1o(output) + return c + + + +class ReDoubleAttention(nn.Module): + def __init__(self, dim, n_heads, mh_qknorm=False, dtype=None, device=None, operations=None): + super().__init__() + + self.n_heads = n_heads + self.head_dim = dim // n_heads + + # this is for cond 1 (one) not l (L) + self.w1q = operations.Linear(dim, dim, bias=False, dtype=dtype, device=device) + self.w1k = operations.Linear(dim, dim, bias=False, dtype=dtype, device=device) + self.w1v = operations.Linear(dim, dim, bias=False, dtype=dtype, device=device) + self.w1o = operations.Linear(dim, dim, bias=False, dtype=dtype, device=device) + + # this is for x + self.w2q = operations.Linear(dim, dim, bias=False, dtype=dtype, device=device) + self.w2k = operations.Linear(dim, dim, bias=False, dtype=dtype, device=device) + self.w2v = operations.Linear(dim, dim, bias=False, dtype=dtype, device=device) + self.w2o = operations.Linear(dim, dim, bias=False, dtype=dtype, device=device) + + self.q_norm1 = ( + MultiHeadLayerNorm((self.n_heads, self.head_dim), dtype=dtype, device=device) + if mh_qknorm + else operations.LayerNorm(self.head_dim, elementwise_affine=False, dtype=dtype, device=device) + ) + self.k_norm1 = ( + MultiHeadLayerNorm((self.n_heads, self.head_dim), dtype=dtype, device=device) + if mh_qknorm + else operations.LayerNorm(self.head_dim, elementwise_affine=False, dtype=dtype, device=device) + ) + + self.q_norm2 = ( + MultiHeadLayerNorm((self.n_heads, self.head_dim), dtype=dtype, device=device) + if mh_qknorm + else operations.LayerNorm(self.head_dim, elementwise_affine=False, dtype=dtype, device=device) + ) + self.k_norm2 = ( + MultiHeadLayerNorm((self.n_heads, self.head_dim), dtype=dtype, device=device) + if mh_qknorm + else operations.LayerNorm(self.head_dim, elementwise_affine=False, dtype=dtype, device=device) + ) + + + #@torch.compile(mode="default", dynamic=False, fullgraph=False, backend="inductor") # c.shape 1,264,3072 x.shape 1,4032,3072 + def forward(self, c, x, mask=None): + + bsz, seqlen1, _ = c.shape + bsz, seqlen2, _ = x.shape + + cq, ck, cv = self.w1q(c), self.w1k(c), self.w1v(c) + cq = cq.view(bsz, seqlen1, self.n_heads, self.head_dim) + ck = ck.view(bsz, seqlen1, self.n_heads, self.head_dim) + cv = cv.view(bsz, seqlen1, self.n_heads, self.head_dim) + cq, ck = self.q_norm1(cq), self.k_norm1(ck) + + xq, xk, xv = self.w2q(x), self.w2k(x), self.w2v(x) + xq = xq.view(bsz, seqlen2, self.n_heads, self.head_dim) + xk = xk.view(bsz, seqlen2, self.n_heads, self.head_dim) + xv = xv.view(bsz, seqlen2, self.n_heads, self.head_dim) + xq, xk = self.q_norm2(xq), self.k_norm2(xk) + + # concat all q,k,v.shape 1,4299,12,256 cq 1,267,12,256 xq 1,4032,12,256 self.n_heads 12 + q, k, v = ( + torch.cat([cq, xq], dim=1), + torch.cat([ck, xk], dim=1), + torch.cat([cv, xv], dim=1), + ) + # attn mask would be 4299,4299 + if mask is not None: + pass + + output = attention_pytorch(q.permute(0, 2, 1, 3), k.permute(0, 2, 1, 3), v.permute(0, 2, 1, 3), self.n_heads, skip_reshape=True, mask=mask) + + c, x = output.split([seqlen1, seqlen2], dim=1) + c = self.w1o(c) + x = self.w2o(x) + + return c, x + + +class ReMMDiTBlock(nn.Module): + def __init__(self, dim, heads=8, global_conddim=1024, is_last=False, dtype=None, device=None, operations=None): + super().__init__() + + self.normC1 = operations.LayerNorm(dim, elementwise_affine=False, dtype=dtype, device=device) + self.normC2 = operations.LayerNorm(dim, elementwise_affine=False, dtype=dtype, device=device) + if not is_last: + self.mlpC = MLP(dim, hidden_dim=dim * 4, dtype=dtype, device=device, operations=operations) + self.modC = nn.Sequential( + nn.SiLU(), + operations.Linear(global_conddim, 6 * dim, bias=False, dtype=dtype, device=device), + ) + else: + self.modC = nn.Sequential( + nn.SiLU(), + operations.Linear(global_conddim, 2 * dim, bias=False, dtype=dtype, device=device), + ) + + self.normX1 = operations.LayerNorm(dim, elementwise_affine=False, dtype=dtype, device=device) + self.normX2 = operations.LayerNorm(dim, elementwise_affine=False, dtype=dtype, device=device) + self.mlpX = MLP(dim, hidden_dim=dim * 4, dtype=dtype, device=device, operations=operations) + self.modX = nn.Sequential( + nn.SiLU(), + operations.Linear(global_conddim, 6 * dim, bias=False, dtype=dtype, device=device), + ) + + self.attn = ReDoubleAttention(dim, heads, dtype=dtype, device=device, operations=operations) + self.is_last = is_last + + #@torch.compile(mode="default", dynamic=False, fullgraph=False, backend="inductor") # MAIN BLOCK + def forward(self, c, x, global_cond, mask=None, **kwargs): + + cres, xres = c, x + + cshift_msa, cscale_msa, cgate_msa, cshift_mlp, cscale_mlp, cgate_mlp = ( + self.modC(global_cond).chunk(6, dim=1) + ) + + c = modulate(self.normC1(c), cshift_msa, cscale_msa) + + # xpath + xshift_msa, xscale_msa, xgate_msa, xshift_mlp, xscale_mlp, xgate_mlp = ( + self.modX(global_cond).chunk(6, dim=1) + ) + + x = modulate(self.normX1(x), xshift_msa, xscale_msa) + + # attention c.shape 1,520,3072 x.shape 1,6144,3072 + c, x = self.attn(c, x, mask=mask) + + c = self.normC2(cres + cgate_msa.unsqueeze(1) * c) + c = cgate_mlp.unsqueeze(1) * self.mlpC(modulate(c, cshift_mlp, cscale_mlp)) + c = cres + c + + x = self.normX2(xres + xgate_msa.unsqueeze(1) * x) + x = xgate_mlp.unsqueeze(1) * self.mlpX(modulate(x, xshift_mlp, xscale_mlp)) + x = xres + x + + return c, x + +class ReDiTBlock(nn.Module): + # like MMDiTBlock, but it only has X + def __init__(self, dim, heads=8, global_conddim=1024, dtype=None, device=None, operations=None): + super().__init__() + + self.norm1 = operations.LayerNorm(dim, elementwise_affine=False, dtype=dtype, device=device) + self.norm2 = operations.LayerNorm(dim, elementwise_affine=False, dtype=dtype, device=device) + + self.modCX = nn.Sequential( + nn.SiLU(), + operations.Linear(global_conddim, 6 * dim, bias=False, dtype=dtype, device=device), + ) + + self.attn = ReSingleAttention(dim, heads, dtype=dtype, device=device, operations=operations) + self.mlp = MLP(dim, hidden_dim=dim * 4, dtype=dtype, device=device, operations=operations) + + #@torch.compile(mode="default", dynamic=False, fullgraph=False, backend="inductor") # cx.shape 1,6664,3072 global_cond.shape 1,3072 mlpout.shape 1,6664,3072 float16 + def forward(self, cx, global_cond, mask=None, **kwargs): + cxres = cx + shift_msa, scale_msa, gate_msa, shift_mlp, scale_mlp, gate_mlp = self.modCX( + global_cond + ).chunk(6, dim=1) + cx = modulate(self.norm1(cx), shift_msa, scale_msa) + cx = self.attn(cx, mask=mask) + cx = self.norm2(cxres + gate_msa.unsqueeze(1) * cx) + mlpout = self.mlp(modulate(cx, shift_mlp, scale_mlp)) + cx = gate_mlp.unsqueeze(1) * mlpout + + cx = cxres + cx # residual connection + + return cx + + + +class TimestepEmbedder(nn.Module): + def __init__(self, hidden_size, frequency_embedding_size=256, dtype=None, device=None, operations=None): + super().__init__() + self.mlp = nn.Sequential( + operations.Linear(frequency_embedding_size, hidden_size, dtype=dtype, device=device), + nn.SiLU(), + operations.Linear(hidden_size, hidden_size, dtype=dtype, device=device), + ) + self.frequency_embedding_size = frequency_embedding_size + + @staticmethod + def timestep_embedding(t, dim, max_period=10000): + half = dim // 2 + freqs = 1000 * torch.exp( + -math.log(max_period) * torch.arange(start=0, end=half) / half + ).to(t.device) + args = t[:, None] * freqs[None] + embedding = torch.cat([torch.cos(args), torch.sin(args)], dim=-1) + if dim % 2: + embedding = torch.cat( + [embedding, torch.zeros_like(embedding[:, :1])], dim=-1 + ) + return embedding + + #@torch.compile(mode="default", dynamic=False, fullgraph=False, backend="inductor") + def forward(self, t, dtype): + t_freq = self.timestep_embedding(t, self.frequency_embedding_size).to(dtype) + t_emb = self.mlp(t_freq) + return t_emb + + +class ReMMDiT(nn.Module): + def __init__( + self, + in_channels=4, + out_channels=4, + patch_size=2, + dim=3072, + n_layers=36, + n_double_layers=4, + n_heads=12, + global_conddim=3072, + cond_seq_dim=2048, + max_seq=32 * 32, + device=None, + dtype=None, + operations=None, + ): + super().__init__() + self.dtype = dtype + + self.t_embedder = TimestepEmbedder(global_conddim, dtype=dtype, device=device, operations=operations) + + self.cond_seq_linear = operations.Linear( + cond_seq_dim, dim, bias=False, dtype=dtype, device=device + ) # linear for something like text sequence. + self.init_x_linear = operations.Linear( + patch_size * patch_size * in_channels, dim, dtype=dtype, device=device + ) # init linear for patchified image. + + self.positional_encoding = nn.Parameter(torch.empty(1, max_seq, dim, dtype=dtype, device=device)) + self.register_tokens = nn.Parameter(torch.empty(1, 8, dim, dtype=dtype, device=device)) + + self.double_layers = nn.ModuleList([]) + self.single_layers = nn.ModuleList([]) + + + for idx in range(n_double_layers): + self.double_layers.append( + ReMMDiTBlock(dim, n_heads, global_conddim, is_last=(idx == n_layers - 1), dtype=dtype, device=device, operations=operations) + ) + + for idx in range(n_double_layers, n_layers): + self.single_layers.append( + ReDiTBlock(dim, n_heads, global_conddim, dtype=dtype, device=device, operations=operations) + ) + + + self.final_linear = operations.Linear( + dim, patch_size * patch_size * out_channels, bias=False, dtype=dtype, device=device + ) + + self.modF = nn.Sequential( + nn.SiLU(), + operations.Linear(global_conddim, 2 * dim, bias=False, dtype=dtype, device=device), + ) + + self.out_channels = out_channels + self.patch_size = patch_size + self.n_double_layers = n_double_layers + self.n_layers = n_layers + + self.h_max = round(max_seq**0.5) + self.w_max = round(max_seq**0.5) + + @torch.no_grad() + def extend_pe(self, init_dim=(16, 16), target_dim=(64, 64)): + # extend pe + pe_data = self.positional_encoding.data.squeeze(0)[: init_dim[0] * init_dim[1]] + + pe_as_2d = pe_data.view(init_dim[0], init_dim[1], -1).permute(2, 0, 1) + + # now we need to extend this to target_dim. for this we will use interpolation. + # we will use torch.nn.functional.interpolate + pe_as_2d = F.interpolate( + pe_as_2d.unsqueeze(0), size=target_dim, mode="bilinear" + ) + pe_new = pe_as_2d.squeeze(0).permute(1, 2, 0).flatten(0, 1) + self.positional_encoding.data = pe_new.unsqueeze(0).contiguous() + self.h_max, self.w_max = target_dim + + def pe_selection_index_based_on_dim(self, h, w): + h_p, w_p = h // self.patch_size, w // self.patch_size + original_pe_indexes = torch.arange(self.positional_encoding.shape[1]) + original_pe_indexes = original_pe_indexes.view(self.h_max, self.w_max) + starth = self.h_max // 2 - h_p // 2 + endh = starth + h_p + startw = self.w_max // 2 - w_p // 2 + endw = startw + w_p + original_pe_indexes = original_pe_indexes[ + starth:endh, startw:endw + ] + return original_pe_indexes.flatten() + + def unpatchify(self, x, h, w): + c = self.out_channels + p = self.patch_size + + x = x.reshape(shape=(x.shape[0], h, w, p, p, c)) + x = torch.einsum("nhwpqc->nchpwq", x) + imgs = x.reshape(shape=(x.shape[0], c, h * p, w * p)) + return imgs + + def patchify(self, x): + B, C, H, W = x.size() + x = comfy.ldm.common_dit.pad_to_patch_size(x, (self.patch_size, self.patch_size)) + x = x.view( + B, + C, + (H + 1) // self.patch_size, + self.patch_size, + (W + 1) // self.patch_size, + self.patch_size, + ) + x = x.permute(0, 2, 4, 1, 3, 5).flatten(-3).flatten(1, 2) + return x + + def apply_pos_embeds(self, x, h, w): + h = (h + 1) // self.patch_size + w = (w + 1) // self.patch_size + max_dim = max(h, w) + + cur_dim = self.h_max + pos_encoding = comfy.ops.cast_to_input(self.positional_encoding.reshape(1, cur_dim, cur_dim, -1), x) + + if max_dim > cur_dim: + pos_encoding = F.interpolate(pos_encoding.movedim(-1, 1), (max_dim, max_dim), mode="bilinear").movedim(1, -1) + cur_dim = max_dim + + from_h = (cur_dim - h) // 2 + from_w = (cur_dim - w) // 2 + pos_encoding = pos_encoding[:,from_h:from_h+h,from_w:from_w+w] + return x + pos_encoding.reshape(1, -1, self.positional_encoding.shape[-1]) + + def forward(self, x, timestep, context, transformer_options={}, **kwargs): + + x_orig = x.clone() + context_orig = context.clone() + + SIGMA = timestep[0].unsqueeze(0) #/ 1000 + EO = transformer_options.get("ExtraOptions", ExtraOptions("")) + if EO is not None: + EO.mute = True + + y0_style_pos = transformer_options.get("y0_style_pos") + y0_style_neg = transformer_options.get("y0_style_neg") + + y0_style_pos_weight = transformer_options.get("y0_style_pos_weight", 0.0) + y0_style_pos_synweight = transformer_options.get("y0_style_pos_synweight", 0.0) + y0_style_pos_synweight *= y0_style_pos_weight + + y0_style_neg_weight = transformer_options.get("y0_style_neg_weight", 0.0) + y0_style_neg_synweight = transformer_options.get("y0_style_neg_synweight", 0.0) + y0_style_neg_synweight *= y0_style_neg_weight + + + out_list = [] + for i in range(len(transformer_options['cond_or_uncond'])): + UNCOND = transformer_options['cond_or_uncond'][i] == 1 + + x = x_orig[i][None,...].clone() + context = context_orig.clone() + + patches_replace = transformer_options.get("patches_replace", {}) + # patchify x, add PE + b, c, h, w = x.shape + + h_len = ((h + (self.patch_size // 2)) // self.patch_size) # h_len 96 + w_len = ((w + (self.patch_size // 2)) // self.patch_size) # w_len 96 + + + x = self.init_x_linear(self.patchify(x)) # B, T_x, D + x = self.apply_pos_embeds(x, h, w) + + if UNCOND: + + transformer_options['reg_cond_weight'] = transformer_options.get("regional_conditioning_weight", 0.0) + transformer_options['reg_cond_floor'] = transformer_options.get("regional_conditioning_floor", 0.0) + transformer_options['reg_cond_mask_orig'] = transformer_options.get('regional_conditioning_mask_orig') + + AttnMask = transformer_options.get('AttnMask', None) + RegContext = transformer_options.get('RegContext', None) + + if AttnMask is not None and transformer_options['reg_cond_weight'] > 0.0: + AttnMask.attn_mask_recast(x.dtype) + context_tmp = RegContext.get().to(context.dtype) + #context_tmp = 0 * context_tmp.clone() + + # If it's not a perfect factor, repeat and slice: + A = context[i][None,...].clone() + B = context_tmp + context_tmp = A.repeat(1, (B.shape[1] // A.shape[1]) + 1, 1)[:, :B.shape[1], :] + + else: + context_tmp = context[i][None,...].clone() + + elif UNCOND == False: + transformer_options['reg_cond_weight'] = transformer_options.get("regional_conditioning_weight", 0.0) + transformer_options['reg_cond_floor'] = transformer_options.get("regional_conditioning_floor", 0.0) + transformer_options['reg_cond_mask_orig'] = transformer_options.get('regional_conditioning_mask_orig') + + AttnMask = transformer_options.get('AttnMask', None) + RegContext = transformer_options.get('RegContext', None) + + if AttnMask is not None and transformer_options['reg_cond_weight'] > 0.0: + AttnMask.attn_mask_recast(x.dtype) + context_tmp = RegContext.get().to(context.dtype) + else: + context_tmp = context[i][None,...].clone() + + if context_tmp is None: + context_tmp = context[i][None,...].clone() + + + + + # process conditions for MMDiT Blocks + #c_seq = context # B, T_c, D_c + c_seq = context_tmp # B, T_c, D_c + + t = timestep + + c = self.cond_seq_linear(c_seq) # B, T_c, D # 1,256,2048 -> + c = torch.cat([comfy.ops.cast_to_input(self.register_tokens, c).repeat(c.size(0), 1, 1), c], dim=1) #1,256,3072 -> 1,264,3072 + + global_cond = self.t_embedder(t, x.dtype) # B, D + + global_cond = global_cond[i][None] + + + + weight = transformer_options['reg_cond_weight'] if 'reg_cond_weight' in transformer_options else 0.0 + floor = transformer_options['reg_cond_floor'] if 'reg_cond_floor' in transformer_options else 0.0 + + floor = min(floor, weight) + + reg_cond_mask_expanded = transformer_options.get('reg_cond_mask_expanded') + reg_cond_mask_expanded = reg_cond_mask_expanded.to(img.dtype).to(img.device) if reg_cond_mask_expanded is not None else None + reg_cond_mask = None + + + + AttnMask = transformer_options.get('AttnMask') + mask = None + if AttnMask is not None and weight > 0: + mask = AttnMask.get(weight=weight) #mask_obj[0](transformer_options, weight.item()) + + mask_type_bool = type(mask[0][0].item()) == bool if mask is not None else False + if not mask_type_bool: + mask = mask.to(x.dtype) + + if mask_type_bool: + mask = F.pad(mask, (8, 0, 8, 0), value=True) + #mask = F.pad(mask, (0, 8, 0, 8), value=True) + else: + mask = F.pad(mask, (8, 0, 8, 0), value=1.0) + + text_len = context.shape[1] # mask_obj[0].text_len + + mask[text_len:,text_len:] = torch.clamp(mask[text_len:,text_len:], min=floor.to(mask.device)) #ORIGINAL SELF-ATTN REGION BLEED + reg_cond_mask = reg_cond_mask_expanded.unsqueeze(0).clone() if reg_cond_mask_expanded is not None else None + + mask_type_bool = type(mask[0][0].item()) == bool if mask is not None else False + + total_layers = len(self.double_layers) + len(self.single_layers) + + blocks_replace = patches_replace.get("dit", {}) # context 1,259,2048 x 1,4032,3072 + if len(self.double_layers) > 0: + for i, layer in enumerate(self.double_layers): + if mask_type_bool and weight < (i / (total_layers-1)) and mask is not None: + mask = mask.to(x.dtype) + + if ("double_block", i) in blocks_replace: + def block_wrap(args): + out = {} + out["txt"], out["img"] = layer( args["txt"], + args["img"], + args["vec"]) + return out + out = blocks_replace[("double_block", i)]({"img": x, "txt": c, "vec": global_cond}, {"original_block": block_wrap}) + c = out["txt"] + x = out["img"] + else: + c, x = layer(c, x, global_cond, mask=mask, **kwargs) + + if len(self.single_layers) > 0: + c_len = c.size(1) + cx = torch.cat([c, x], dim=1) + for i, layer in enumerate(self.single_layers): + if mask_type_bool and weight < ((len(self.double_layers) + i) / (total_layers-1)) and mask is not None: + mask = mask.to(x.dtype) + + if ("single_block", i) in blocks_replace: + def block_wrap(args): + out = {} + out["img"] = layer(args["img"], args["vec"]) + return out + + out = blocks_replace[("single_block", i)]({"img": cx, "vec": global_cond}, {"original_block": block_wrap}) + cx = out["img"] + else: + cx = layer(cx, global_cond, mask=mask, **kwargs) + + x = cx[:, c_len:] + + fshift, fscale = self.modF(global_cond).chunk(2, dim=1) + + x = modulate(x, fshift, fscale) + x = self.final_linear(x) + x = self.unpatchify(x, (h + 1) // self.patch_size, (w + 1) // self.patch_size)[:,:,:h,:w] + + out_list.append(x) + + eps = torch.stack(out_list, dim=0).squeeze(dim=1) + + + + + + + freqsep_lowpass_method = transformer_options.get("freqsep_lowpass_method") + freqsep_sigma = transformer_options.get("freqsep_sigma") + freqsep_kernel_size = transformer_options.get("freqsep_kernel_size") + freqsep_inner_kernel_size = transformer_options.get("freqsep_inner_kernel_size") + freqsep_stride = transformer_options.get("freqsep_stride") + + freqsep_lowpass_weight = transformer_options.get("freqsep_lowpass_weight") + freqsep_highpass_weight= transformer_options.get("freqsep_highpass_weight") + freqsep_mask = transformer_options.get("freqsep_mask") + + dtype = eps.dtype if self.style_dtype is None else self.style_dtype + + if y0_style_pos is not None: + y0_style_pos_weight = transformer_options.get("y0_style_pos_weight") + y0_style_pos_synweight = transformer_options.get("y0_style_pos_synweight") + y0_style_pos_synweight *= y0_style_pos_weight + y0_style_pos_mask = transformer_options.get("y0_style_pos_mask") + y0_style_pos_mask_edge = transformer_options.get("y0_style_pos_mask_edge") + + y0_style_pos = y0_style_pos.to(dtype) + x = x_orig.clone().to(dtype) + #x = x.to(dtype) + eps = eps.to(dtype) + eps_orig = eps.clone() + + sigma = SIGMA #t_orig[0].to(torch.float32) / 1000 + denoised = x - sigma * eps + + denoised_embed = self.Retrojector.embed(denoised) + y0_adain_embed = self.Retrojector.embed(y0_style_pos) + + if transformer_options['y0_style_method'] == "scattersort": + tile_h, tile_w = transformer_options.get('y0_style_tile_height'), transformer_options.get('y0_style_tile_width') + pad = transformer_options.get('y0_style_tile_padding') + if pad is not None and tile_h is not None and tile_w is not None: + + denoised_spatial = rearrange(denoised_embed, "b (h w) c -> b c h w", h=h_len, w=w_len) + y0_adain_spatial = rearrange(y0_adain_embed, "b (h w) c -> b c h w", h=h_len, w=w_len) + + if EO("scattersort_median_LP"): + denoised_spatial_LP = median_blur_2d(denoised_spatial, kernel_size=EO("scattersort_median_LP",7)) + y0_adain_spatial_LP = median_blur_2d(y0_adain_spatial, kernel_size=EO("scattersort_median_LP",7)) + + denoised_spatial_HP = denoised_spatial - denoised_spatial_LP + y0_adain_spatial_HP = y0_adain_spatial - y0_adain_spatial_LP + + denoised_spatial_LP = apply_scattersort_tiled(denoised_spatial_LP, y0_adain_spatial_LP, tile_h, tile_w, pad) + + denoised_spatial = denoised_spatial_LP + denoised_spatial_HP + denoised_embed = rearrange(denoised_spatial, "b c h w -> b (h w) c") + else: + denoised_spatial = apply_scattersort_tiled(denoised_spatial, y0_adain_spatial, tile_h, tile_w, pad) + + denoised_embed = rearrange(denoised_spatial, "b c h w -> b (h w) c") + + else: + denoised_embed = apply_scattersort_masked(denoised_embed, y0_adain_embed, y0_style_pos_mask, y0_style_pos_mask_edge, h_len, w_len) + + + + elif transformer_options['y0_style_method'] == "AdaIN": + if freqsep_mask is not None: + freqsep_mask = freqsep_mask.view(1, 1, *freqsep_mask.shape[-2:]).float() + freqsep_mask = F.interpolate(freqsep_mask.float(), size=(h_len, w_len), mode='nearest-exact') + + if hasattr(self, "adain_tile"): + tile_h, tile_w = self.adain_tile + + denoised_pretile = rearrange(denoised_embed, "b (h w) c -> b c h w", h=h_len, w=w_len) + y0_adain_pretile = rearrange(y0_adain_embed, "b (h w) c -> b c h w", h=h_len, w=w_len) + + if self.adain_flag: + h_off = tile_h // 2 + w_off = tile_w // 2 + denoised_pretile = denoised_pretile[:,:,h_off:-h_off, w_off:-w_off] + self.adain_flag = False + else: + h_off = 0 + w_off = 0 + self.adain_flag = True + + tiles, orig_shape, grid, strides = tile_latent(denoised_pretile, tile_size=(tile_h,tile_w)) + y0_tiles, orig_shape, grid, strides = tile_latent(y0_adain_pretile, tile_size=(tile_h,tile_w)) + + tiles_out = [] + for i in range(tiles.shape[0]): + tile = tiles[i].unsqueeze(0) + y0_tile = y0_tiles[i].unsqueeze(0) + + tile = rearrange(tile, "b c h w -> b (h w) c", h=tile_h, w=tile_w) + y0_tile = rearrange(y0_tile, "b c h w -> b (h w) c", h=tile_h, w=tile_w) + + tile = adain_seq_inplace(tile, y0_tile) + tiles_out.append(rearrange(tile, "b (h w) c -> b c h w", h=tile_h, w=tile_w)) + + tiles_out_tensor = torch.cat(tiles_out, dim=0) + tiles_out_tensor = untile_latent(tiles_out_tensor, orig_shape, grid, strides) + + if h_off == 0: + denoised_pretile = tiles_out_tensor + else: + denoised_pretile[:,:,h_off:-h_off, w_off:-w_off] = tiles_out_tensor + denoised_embed = rearrange(denoised_pretile, "b c h w -> b (h w) c", h=h_len, w=w_len) + + elif freqsep_lowpass_method is not None and freqsep_lowpass_method.endswith("pw"): #EO("adain_pw"): + + denoised_spatial = rearrange(denoised_embed, "b (h w) c -> b c h w", h=h_len, w=w_len) + y0_adain_spatial = rearrange(y0_adain_embed, "b (h w) c -> b c h w", h=h_len, w=w_len) + + if freqsep_lowpass_method == "median_pw": + denoised_spatial_new = adain_patchwise_row_batch_med(denoised_spatial.clone(), y0_adain_spatial.clone().repeat(denoised_spatial.shape[0],1,1,1), sigma=freqsep_sigma, kernel_size=freqsep_kernel_size, use_median_blur=True, lowpass_weight=freqsep_lowpass_weight, highpass_weight=freqsep_highpass_weight) + elif freqsep_lowpass_method == "gaussian_pw": + denoised_spatial_new = adain_patchwise_row_batch(denoised_spatial.clone(), y0_adain_spatial.clone().repeat(denoised_spatial.shape[0],1,1,1), sigma=freqsep_sigma, kernel_size=freqsep_kernel_size) + + denoised_embed = rearrange(denoised_spatial_new, "b c h w -> b (h w) c", h=h_len, w=w_len) + + elif freqsep_lowpass_method is not None: + denoised_spatial = rearrange(denoised_embed, "b (h w) c -> b c h w", h=h_len, w=w_len) + y0_adain_spatial = rearrange(y0_adain_embed, "b (h w) c -> b c h w", h=h_len, w=w_len) + + if freqsep_lowpass_method == "median": + denoised_spatial_LP = median_blur_2d(denoised_spatial, kernel_size=freqsep_kernel_size) + y0_adain_spatial_LP = median_blur_2d(y0_adain_spatial, kernel_size=freqsep_kernel_size) + elif freqsep_lowpass_method == "gaussian": + denoised_spatial_LP = gaussian_blur_2d(denoised_spatial, sigma=freqsep_sigma, kernel_size=freqsep_kernel_size) + y0_adain_spatial_LP = gaussian_blur_2d(y0_adain_spatial, sigma=freqsep_sigma, kernel_size=freqsep_kernel_size) + + denoised_spatial_HP = denoised_spatial - denoised_spatial_LP + + if EO("adain_fs_uhp"): + y0_adain_spatial_HP = y0_adain_spatial - y0_adain_spatial_LP + + denoised_spatial_ULP = gaussian_blur_2d(denoised_spatial, sigma=EO("adain_fs_uhp_sigma", 1.0), kernel_size=EO("adain_fs_uhp_kernel_size", 3)) + y0_adain_spatial_ULP = gaussian_blur_2d(y0_adain_spatial, sigma=EO("adain_fs_uhp_sigma", 1.0), kernel_size=EO("adain_fs_uhp_kernel_size", 3)) + + denoised_spatial_UHP = denoised_spatial_HP - denoised_spatial_ULP + y0_adain_spatial_UHP = y0_adain_spatial_HP - y0_adain_spatial_ULP + + #denoised_spatial_HP = y0_adain_spatial_ULP + denoised_spatial_UHP + denoised_spatial_HP = denoised_spatial_ULP + y0_adain_spatial_UHP + + denoised_spatial_new = freqsep_lowpass_weight * y0_adain_spatial_LP + freqsep_highpass_weight * denoised_spatial_HP + denoised_embed = rearrange(denoised_spatial_new, "b c h w -> b (h w) c", h=h_len, w=w_len) + + else: + denoised_embed = adain_seq_inplace(denoised_embed, y0_adain_embed) + + for adain_iter in range(EO("style_iter", 0)): + denoised_embed = adain_seq_inplace(denoised_embed, y0_adain_embed) + denoised_embed = self.Retrojector.embed(self.Retrojector.unembed(denoised_embed)) + denoised_embed = adain_seq_inplace(denoised_embed, y0_adain_embed) + + elif transformer_options['y0_style_method'] == "WCT": + self.StyleWCT.set(y0_adain_embed) + denoised_embed = self.StyleWCT.get(denoised_embed) + + if transformer_options.get('y0_standard_guide') is not None: + y0_standard_guide = transformer_options.get('y0_standard_guide') + + y0_standard_guide_embed = self.Retrojector.embed(y0_standard_guide) + f_cs = self.StyleWCT.get(y0_standard_guide_embed) + self.y0_standard_guide = self.Retrojector.unembed(f_cs) + + if transformer_options.get('y0_inv_standard_guide') is not None: + y0_inv_standard_guide = transformer_options.get('y0_inv_standard_guide') + + y0_inv_standard_guide_embed = self.Retrojector.embed(y0_inv_standard_guide) + f_cs = self.StyleWCT.get(y0_inv_standard_guide_embed) + self.y0_inv_standard_guide = self.Retrojector.unembed(f_cs) + + denoised_approx = self.Retrojector.unembed(denoised_embed) + + eps = (x - denoised_approx) / sigma + + if not UNCOND: + if eps.shape[0] == 2: + eps[1] = eps_orig[1] + y0_style_pos_weight * (eps[1] - eps_orig[1]) + eps[0] = eps_orig[0] + y0_style_pos_synweight * (eps[0] - eps_orig[0]) + else: + eps[0] = eps_orig[0] + y0_style_pos_weight * (eps[0] - eps_orig[0]) + elif eps.shape[0] == 1 and UNCOND: + eps[0] = eps_orig[0] + y0_style_pos_synweight * (eps[0] - eps_orig[0]) + + eps = eps.float() + + if y0_style_neg is not None: + y0_style_neg_weight = transformer_options.get("y0_style_neg_weight") + y0_style_neg_synweight = transformer_options.get("y0_style_neg_synweight") + y0_style_neg_synweight *= y0_style_neg_weight + y0_style_neg_mask = transformer_options.get("y0_style_neg_mask") + y0_style_neg_mask_edge = transformer_options.get("y0_style_neg_mask_edge") + + y0_style_neg = y0_style_neg.to(dtype) + x = x.to(dtype) + eps = eps.to(dtype) + eps_orig = eps.clone() + + sigma = SIGMA #t_orig[0].to(torch.float32) / 1000 + denoised = x - sigma * eps + + denoised_embed = self.Retrojector.embed(denoised) + y0_adain_embed = self.Retrojector.embed(y0_style_neg) + + if transformer_options['y0_style_method'] == "scattersort": + tile_h, tile_w = transformer_options.get('y0_style_tile_height'), transformer_options.get('y0_style_tile_width') + pad = transformer_options.get('y0_style_tile_padding') + if pad is not None and tile_h is not None and tile_w is not None: + + denoised_spatial = rearrange(denoised_embed, "b (h w) c -> b c h w", h=h_len, w=w_len) + y0_adain_spatial = rearrange(y0_adain_embed, "b (h w) c -> b c h w", h=h_len, w=w_len) + + denoised_spatial = apply_scattersort_tiled(denoised_spatial, y0_adain_spatial, tile_h, tile_w, pad) + + denoised_embed = rearrange(denoised_spatial, "b c h w -> b (h w) c") + + else: + denoised_embed = apply_scattersort_masked(denoised_embed, y0_adain_embed, y0_style_neg_mask, y0_style_neg_mask_edge, h_len, w_len) + + + elif transformer_options['y0_style_method'] == "AdaIN": + denoised_embed = adain_seq_inplace(denoised_embed, y0_adain_embed) + for adain_iter in range(EO("style_iter", 0)): + denoised_embed = adain_seq_inplace(denoised_embed, y0_adain_embed) + denoised_embed = self.Retrojector.embed(self.Retrojector.unembed(denoised_embed)) + denoised_embed = adain_seq_inplace(denoised_embed, y0_adain_embed) + + elif transformer_options['y0_style_method'] == "WCT": + self.StyleWCT.set(y0_adain_embed) + denoised_embed = self.StyleWCT.get(denoised_embed) + + denoised_approx = self.Retrojector.unembed(denoised_embed) + + if UNCOND: + eps = (x - denoised_approx) / sigma + eps[0] = eps_orig[0] + y0_style_neg_weight * (eps[0] - eps_orig[0]) + if eps.shape[0] == 2: + eps[1] = eps_orig[1] + y0_style_neg_synweight * (eps[1] - eps_orig[1]) + elif eps.shape[0] == 1 and not UNCOND: + eps[0] = eps_orig[0] + y0_style_neg_synweight * (eps[0] - eps_orig[0]) + + eps = eps.float() + + return eps + + + + +def unpatchify2(x: torch.Tensor, H: int, W: int, patch_size: int) -> torch.Tensor: + """ + Invert patchify: + x: (B, N, C*p*p) + returns: (B, C, H, W), slicing off any padding + """ + B, N, CPP = x.shape + p = patch_size + Hp = math.ceil(H / p) + Wp = math.ceil(W / p) + C = CPP // (p * p) + assert N == Hp * Wp, f"Expected N={Hp*Wp} patches, got {N}" + + x = x.view(B, Hp, Wp, CPP) + x = x.view(B, Hp, Wp, C, p, p) + x = x.permute(0, 3, 1, 4, 2, 5) + imgs = x.reshape(B, C, Hp * p, Wp * p) + return imgs[:, :, :H, :W] + diff --git a/ComfyUI/custom_nodes/RES4LYF/beta/__init__.py b/ComfyUI/custom_nodes/RES4LYF/beta/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..4d105f208440636dede161d52d7b86bbe1c52f8e --- /dev/null +++ b/ComfyUI/custom_nodes/RES4LYF/beta/__init__.py @@ -0,0 +1,197 @@ + +from . import rk_sampler_beta +from . import samplers +from . import samplers_extensions + + +def add_beta(NODE_CLASS_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS, extra_samplers): + + NODE_CLASS_MAPPINGS.update({ + #"SharkSampler" : samplers.SharkSampler, + #"SharkSamplerAdvanced_Beta" : samplers.SharkSampler, #SharkSamplerAdvanced_Beta, + "SharkOptions_Beta" : samplers_extensions.SharkOptions_Beta, + "ClownOptions_SDE_Beta" : samplers_extensions.ClownOptions_SDE_Beta, + "ClownOptions_DetailBoost_Beta" : samplers_extensions.ClownOptions_DetailBoost_Beta, + "ClownGuide_Style_Beta" : samplers_extensions.ClownGuide_Style_Beta, + "ClownGuide_Style_EdgeWidth" : samplers_extensions.ClownGuide_Style_EdgeWidth, + "ClownGuide_Style_TileSize" : samplers_extensions.ClownGuide_Style_TileSize, + + "ClownGuide_Beta" : samplers_extensions.ClownGuide_Beta, + "ClownGuides_Beta" : samplers_extensions.ClownGuides_Beta, + "ClownGuidesAB_Beta" : samplers_extensions.ClownGuidesAB_Beta, + + "ClownGuides_Sync" : samplers_extensions.ClownGuides_Sync, + "ClownGuides_Sync_Advanced" : samplers_extensions.ClownGuides_Sync_Advanced, + "ClownGuide_FrequencySeparation" : samplers_extensions.ClownGuide_FrequencySeparation, + + + "SharkOptions_GuiderInput" : samplers_extensions.SharkOptions_GuiderInput, + "ClownOptions_ImplicitSteps_Beta" : samplers_extensions.ClownOptions_ImplicitSteps_Beta, + "ClownOptions_Cycles_Beta" : samplers_extensions.ClownOptions_Cycles_Beta, + + "SharkOptions_GuideCond_Beta" : samplers_extensions.SharkOptions_GuideCond_Beta, + "SharkOptions_GuideConds_Beta" : samplers_extensions.SharkOptions_GuideConds_Beta, + + "ClownOptions_Tile_Beta" : samplers_extensions.ClownOptions_Tile_Beta, + "ClownOptions_Tile_Advanced_Beta" : samplers_extensions.ClownOptions_Tile_Advanced_Beta, + + + "ClownGuide_Mean_Beta" : samplers_extensions.ClownGuide_Mean_Beta, + "ClownGuide_AdaIN_MMDiT_Beta" : samplers_extensions.ClownGuide_AdaIN_MMDiT_Beta, + "ClownGuide_AttnInj_MMDiT_Beta" : samplers_extensions.ClownGuide_AttnInj_MMDiT_Beta, + "ClownGuide_StyleNorm_Advanced_HiDream" : samplers_extensions.ClownGuide_StyleNorm_Advanced_HiDream, + + "ClownOptions_SDE_Mask_Beta" : samplers_extensions.ClownOptions_SDE_Mask_Beta, + + "ClownOptions_StepSize_Beta" : samplers_extensions.ClownOptions_StepSize_Beta, + "ClownOptions_SigmaScaling_Beta" : samplers_extensions.ClownOptions_SigmaScaling_Beta, + + "ClownOptions_Momentum_Beta" : samplers_extensions.ClownOptions_Momentum_Beta, + "ClownOptions_SwapSampler_Beta" : samplers_extensions.ClownOptions_SwapSampler_Beta, + "ClownOptions_ExtraOptions_Beta" : samplers_extensions.ClownOptions_ExtraOptions_Beta, + "ClownOptions_Automation_Beta" : samplers_extensions.ClownOptions_Automation_Beta, + + "SharkOptions_UltraCascade_Latent_Beta" : samplers_extensions.SharkOptions_UltraCascade_Latent_Beta, + "SharkOptions_StartStep_Beta" : samplers_extensions.SharkOptions_StartStep_Beta, + + "ClownOptions_Combine" : samplers_extensions.ClownOptions_Combine, + "ClownOptions_Frameweights" : samplers_extensions.ClownOptions_Frameweights, + "ClownOptions_FlowGuide" : samplers_extensions.ClownOptions_FlowGuide, + + "ClownStyle_Block_MMDiT" : samplers_extensions.ClownStyle_Block_MMDiT, + "ClownStyle_MMDiT" : samplers_extensions.ClownStyle_MMDiT, + "ClownStyle_Attn_MMDiT" : samplers_extensions.ClownStyle_Attn_MMDiT, + "ClownStyle_Boost" : samplers_extensions.ClownStyle_Boost, + + "ClownStyle_UNet" : samplers_extensions.ClownStyle_UNet, + "ClownStyle_Block_UNet" : samplers_extensions.ClownStyle_Block_UNet, + "ClownStyle_Attn_UNet" : samplers_extensions.ClownStyle_Attn_UNet, + "ClownStyle_ResBlock_UNet" : samplers_extensions.ClownStyle_ResBlock_UNet, + "ClownStyle_SpatialBlock_UNet" : samplers_extensions.ClownStyle_SpatialBlock_UNet, + "ClownStyle_TransformerBlock_UNet": samplers_extensions.ClownStyle_TransformerBlock_UNet, + + + "ClownSamplerSelector_Beta" : samplers_extensions.ClownSamplerSelector_Beta, + + "SharkSampler_Beta" : samplers.SharkSampler_Beta, + + "SharkChainsampler_Beta" : samplers.SharkChainsampler_Beta, + + "ClownsharKSampler_Beta" : samplers.ClownsharKSampler_Beta, + "ClownsharkChainsampler_Beta" : samplers.ClownsharkChainsampler_Beta, + + "ClownSampler_Beta" : samplers.ClownSampler_Beta, + "ClownSamplerAdvanced_Beta" : samplers.ClownSamplerAdvanced_Beta, + + "BongSampler" : samplers.BongSampler, + + }) + + extra_samplers.update({ + "res_2m" : sample_res_2m, + "res_3m" : sample_res_3m, + "res_2s" : sample_res_2s, + "res_3s" : sample_res_3s, + "res_5s" : sample_res_5s, + "res_6s" : sample_res_6s, + "res_2m_ode" : sample_res_2m_ode, + "res_3m_ode" : sample_res_3m_ode, + "res_2s_ode" : sample_res_2s_ode, + "res_3s_ode" : sample_res_3s_ode, + "res_5s_ode" : sample_res_5s_ode, + "res_6s_ode" : sample_res_6s_ode, + + "deis_2m" : sample_deis_2m, + "deis_3m" : sample_deis_3m, + "deis_2m_ode": sample_deis_2m_ode, + "deis_3m_ode": sample_deis_3m_ode, + "rk_beta": rk_sampler_beta.sample_rk_beta, + }) + + NODE_DISPLAY_NAME_MAPPINGS.update({ + #"SharkSampler" : "SharkSampler", + #"SharkSamplerAdvanced_Beta" : "SharkSamplerAdvanced", + "SharkSampler_Beta" : "SharkSampler", + "SharkChainsampler_Beta" : "SharkChainsampler", + "BongSampler" : "BongSampler", + "ClownsharKSampler_Beta" : "ClownsharKSampler", + "ClownsharkChainsampler_Beta" : "ClownsharkChainsampler", + "ClownSampler_Beta" : "ClownSampler", + "ClownSamplerAdvanced_Beta" : "ClownSamplerAdvanced", + "ClownGuide_Mean_Beta" : "ClownGuide Mean", + "ClownGuide_AdaIN_MMDiT_Beta" : "ClownGuide AdaIN (HiDream)", + "ClownGuide_AttnInj_MMDiT_Beta" : "ClownGuide AttnInj (HiDream)", + "ClownGuide_StyleNorm_Advanced_HiDream" : "ClownGuide_StyleNorm_Advanced_HiDream", + "ClownGuide_Style_Beta" : "ClownGuide Style", + "ClownGuide_Beta" : "ClownGuide", + "ClownGuides_Beta" : "ClownGuides", + "ClownGuides_Sync" : "ClownGuides Sync", + "ClownGuides_Sync_Advanced" : "ClownGuides Sync_Advanced", + + + "ClownGuidesAB_Beta" : "ClownGuidesAB", + "ClownSamplerSelector_Beta" : "ClownSamplerSelector", + "ClownOptions_SDE_Mask_Beta" : "ClownOptions SDE Mask", + "ClownOptions_SDE_Beta" : "ClownOptions SDE", + "ClownOptions_StepSize_Beta" : "ClownOptions Step Size", + "ClownOptions_DetailBoost_Beta" : "ClownOptions Detail Boost", + "ClownOptions_SigmaScaling_Beta" : "ClownOptions Sigma Scaling", + "ClownOptions_Momentum_Beta" : "ClownOptions Momentum", + "ClownOptions_ImplicitSteps_Beta" : "ClownOptions Implicit Steps", + "ClownOptions_Cycles_Beta" : "ClownOptions Cycles", + "ClownOptions_SwapSampler_Beta" : "ClownOptions Swap Sampler", + "ClownOptions_ExtraOptions_Beta" : "ClownOptions Extra Options", + "ClownOptions_Automation_Beta" : "ClownOptions Automation", + "SharkOptions_GuideCond_Beta" : "SharkOptions Guide Cond", + "SharkOptions_GuideConds_Beta" : "SharkOptions Guide Conds", + "SharkOptions_Beta" : "SharkOptions", + "SharkOptions_StartStep_Beta" : "SharkOptions Start Step", + "SharkOptions_UltraCascade_Latent_Beta" : "SharkOptions UltraCascade Latent", + "ClownOptions_Combine" : "ClownOptions Combine", + "ClownOptions_Frameweights" : "ClownOptions Frameweights", + "SharkOptions_GuiderInput" : "SharkOptions Guider Input", + "ClownOptions_Tile_Beta" : "ClownOptions Tile", + "ClownOptions_Tile_Advanced_Beta" : "ClownOptions Tile Advanced", + + }) + + return NODE_CLASS_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS, extra_samplers + + + +def sample_res_2m(model, x, sigmas, extra_args=None, callback=None, disable=None): + return rk_sampler_beta.sample_rk_beta(model, x, sigmas, None, extra_args, callback, disable, rk_type="res_2m",) +def sample_res_3m(model, x, sigmas, extra_args=None, callback=None, disable=None): + return rk_sampler_beta.sample_rk_beta(model, x, sigmas, None, extra_args, callback, disable, rk_type="res_3m",) +def sample_res_2s(model, x, sigmas, extra_args=None, callback=None, disable=None): + return rk_sampler_beta.sample_rk_beta(model, x, sigmas, None, extra_args, callback, disable, rk_type="res_2s",) +def sample_res_3s(model, x, sigmas, extra_args=None, callback=None, disable=None): + return rk_sampler_beta.sample_rk_beta(model, x, sigmas, None, extra_args, callback, disable, rk_type="res_3s",) +def sample_res_5s(model, x, sigmas, extra_args=None, callback=None, disable=None): + return rk_sampler_beta.sample_rk_beta(model, x, sigmas, None, extra_args, callback, disable, rk_type="res_5s",) +def sample_res_6s(model, x, sigmas, extra_args=None, callback=None, disable=None): + return rk_sampler_beta.sample_rk_beta(model, x, sigmas, None, extra_args, callback, disable, rk_type="res_6s",) + +def sample_res_2m_ode(model, x, sigmas, extra_args=None, callback=None, disable=None): + return rk_sampler_beta.sample_rk_beta(model, x, sigmas, None, extra_args, callback, disable, rk_type="res_2m", eta=0.0, eta_substep=0.0, ) +def sample_res_3m_ode(model, x, sigmas, extra_args=None, callback=None, disable=None): + return rk_sampler_beta.sample_rk_beta(model, x, sigmas, None, extra_args, callback, disable, rk_type="res_3m", eta=0.0, eta_substep=0.0, ) +def sample_res_2s_ode(model, x, sigmas, extra_args=None, callback=None, disable=None): + return rk_sampler_beta.sample_rk_beta(model, x, sigmas, None, extra_args, callback, disable, rk_type="res_2s", eta=0.0, eta_substep=0.0, ) +def sample_res_3s_ode(model, x, sigmas, extra_args=None, callback=None, disable=None): + return rk_sampler_beta.sample_rk_beta(model, x, sigmas, None, extra_args, callback, disable, rk_type="res_3s", eta=0.0, eta_substep=0.0, ) +def sample_res_5s_ode(model, x, sigmas, extra_args=None, callback=None, disable=None): + return rk_sampler_beta.sample_rk_beta(model, x, sigmas, None, extra_args, callback, disable, rk_type="res_5s", eta=0.0, eta_substep=0.0, ) +def sample_res_6s_ode(model, x, sigmas, extra_args=None, callback=None, disable=None): + return rk_sampler_beta.sample_rk_beta(model, x, sigmas, None, extra_args, callback, disable, rk_type="res_6s", eta=0.0, eta_substep=0.0, ) + +def sample_deis_2m(model, x, sigmas, extra_args=None, callback=None, disable=None): + return rk_sampler_beta.sample_rk_beta(model, x, sigmas, None, extra_args, callback, disable, rk_type="deis_2m",) +def sample_deis_3m(model, x, sigmas, extra_args=None, callback=None, disable=None): + return rk_sampler_beta.sample_rk_beta(model, x, sigmas, None, extra_args, callback, disable, rk_type="deis_3m",) + +def sample_deis_2m_ode(model, x, sigmas, extra_args=None, callback=None, disable=None): + return rk_sampler_beta.sample_rk_beta(model, x, sigmas, None, extra_args, callback, disable, rk_type="deis_2m", eta=0.0, eta_substep=0.0, ) +def sample_deis_3m_ode(model, x, sigmas, extra_args=None, callback=None, disable=None): + return rk_sampler_beta.sample_rk_beta(model, x, sigmas, None, extra_args, callback, disable, rk_type="deis_3m", eta=0.0, eta_substep=0.0, ) + diff --git a/ComfyUI/custom_nodes/RES4LYF/beta/constants.py b/ComfyUI/custom_nodes/RES4LYF/beta/constants.py new file mode 100644 index 0000000000000000000000000000000000000000..4523001e63bc29d809478fa6a3980c7de05b075d --- /dev/null +++ b/ComfyUI/custom_nodes/RES4LYF/beta/constants.py @@ -0,0 +1,62 @@ +MAX_STEPS = 10000 + + +IMPLICIT_TYPE_NAMES = [ + "rebound", + "retro-eta", + "bongmath", + "predictor-corrector", +] + + + + +GUIDE_MODE_NAMES_BETA_SIMPLE = [ + "flow", + "sync", + "lure", + "data", + "epsilon", + "inversion", + "pseudoimplicit", + "fully_pseudoimplicit", + "none", +] + +FRAME_WEIGHTS_CONFIG_NAMES = [ + "frame_weights", + "frame_weights_inv", + "frame_targets" +] + +FRAME_WEIGHTS_DYNAMICS_NAMES = [ + "constant", + "linear", + "ease_out", + "ease_in", + "middle", + "trough", +] + + +FRAME_WEIGHTS_SCHEDULE_NAMES = [ + "moderate_early", + "moderate_late", + "fast_early", + "fast_late", + "slow_early", + "slow_late", +] + + + +GUIDE_MODE_NAMES_PSEUDOIMPLICIT = [ + "pseudoimplicit", + "pseudoimplicit_cw", + "pseudoimplicit_projection", + "pseudoimplicit_projection_cw", + "fully_pseudoimplicit", + "fully_pseudoimplicit_projection", + "fully_pseudoimplicit_cw", + "fully_pseudoimplicit_projection_cw" +] diff --git a/ComfyUI/custom_nodes/RES4LYF/beta/deis_coefficients.py b/ComfyUI/custom_nodes/RES4LYF/beta/deis_coefficients.py new file mode 100644 index 0000000000000000000000000000000000000000..8e2e4c0053ac639dcb06cbca0c240c1d3b1c472b --- /dev/null +++ b/ComfyUI/custom_nodes/RES4LYF/beta/deis_coefficients.py @@ -0,0 +1,123 @@ +# Adapted from: https://github.com/zju-pi/diff-sampler/blob/main/gits-main/solver_utils.py +# fixed the calcs for "rhoab" which suffered from an off-by-one error and made some other minor corrections + +import torch +import numpy as np + +# A pytorch reimplementation of DEIS (https://github.com/qsh-zh/deis). +############################# +### Utils for DEIS solver ### +############################# +#---------------------------------------------------------------------------- +# Transfer from the input time (sigma) used in EDM to that (t) used in DEIS. + +def edm2t(edm_steps, epsilon_s=1e-3, sigma_min=0.002, sigma_max=80): + vp_sigma = lambda beta_d, beta_min: lambda t: (np.e ** (0.5 * beta_d * (t ** 2) + beta_min * t) - 1) ** 0.5 + vp_sigma_inv = lambda beta_d, beta_min: lambda sigma: ((beta_min ** 2 + 2 * beta_d * (sigma ** 2 + 1).log()).sqrt() - beta_min) / beta_d + vp_beta_d = 2 * (np.log(torch.tensor(sigma_min).cpu() ** 2 + 1) / epsilon_s - np.log(torch.tensor(sigma_max).cpu() ** 2 + 1)) / (epsilon_s - 1) + vp_beta_min = np.log(torch.tensor(sigma_max).cpu() ** 2 + 1) - 0.5 * vp_beta_d + t_steps = vp_sigma_inv(vp_beta_d.clone().detach().cpu(), vp_beta_min.clone().detach().cpu())(edm_steps.clone().detach().cpu()) + return t_steps, vp_beta_min, vp_beta_d + vp_beta_min + +#---------------------------------------------------------------------------- + +def cal_poly(prev_t, j, taus): + poly = 1 + for k in range(prev_t.shape[0]): + if k == j: + continue + poly *= (taus - prev_t[k]) / (prev_t[j] - prev_t[k]) + return poly + +#---------------------------------------------------------------------------- +# Transfer from t to alpha_t. + +def t2alpha_fn(beta_0, beta_1, t): + return torch.exp(-0.5 * t ** 2 * (beta_1 - beta_0) - t * beta_0) + +#---------------------------------------------------------------------------- + +def cal_integrand(beta_0, beta_1, taus): + with torch.inference_mode(mode=False): + taus = taus.clone() + beta_0 = beta_0.clone() + beta_1 = beta_1.clone() + with torch.enable_grad(): + taus.requires_grad_(True) + alpha = t2alpha_fn(beta_0, beta_1, taus) + log_alpha = alpha.log() + log_alpha.sum().backward() + d_log_alpha_dtau = taus.grad + integrand = -0.5 * d_log_alpha_dtau / torch.sqrt(alpha * (1 - alpha)) + return integrand + +#---------------------------------------------------------------------------- + +def get_deis_coeff_list(t_steps, max_order, N=10000, deis_mode='tab'): + """ + Get the coefficient list for DEIS sampling. + + Args: + t_steps: A pytorch tensor. The time steps for sampling. + max_order: A `int`. Maximum order of the solver. 1 <= max_order <= 4 + N: A `int`. Use how many points to perform the numerical integration when deis_mode=='tab'. + deis_mode: A `str`. Select between 'tab' and 'rhoab'. Type of DEIS. + Returns: + A pytorch tensor. A batch of generated samples or sampling trajectories if return_inters=True. + """ + if deis_mode == 'tab': + t_steps, beta_0, beta_1 = edm2t(t_steps) + C = [] + for i, (t_cur, t_next) in enumerate(zip(t_steps[:-1], t_steps[1:])): + order = min(i+1, max_order) + if order == 1: + C.append([]) + else: + taus = torch.linspace(t_cur, t_next, N) # split the interval for integral approximation + dtau = (t_next - t_cur) / N + prev_t = t_steps[[i - k for k in range(order)]] + coeff_temp = [] + integrand = cal_integrand(beta_0, beta_1, taus) + for j in range(order): + poly = cal_poly(prev_t, j, taus) + coeff_temp.append(torch.sum(integrand * poly) * dtau) + C.append(coeff_temp) + + elif deis_mode == 'rhoab': + # Analytical solution, second order + def get_def_integral_2(a, b, start, end, c): + coeff = (end**3 - start**3) / 3 - (end**2 - start**2) * (a + b) / 2 + (end - start) * a * b + return coeff / ((c - a) * (c - b)) + + # Analytical solution, third order + def get_def_integral_3(a, b, c, start, end, d): + coeff = (end**4 - start**4) / 4 - (end**3 - start**3) * (a + b + c) / 3 \ + + (end**2 - start**2) * (a*b + a*c + b*c) / 2 - (end - start) * a * b * c + return coeff / ((d - a) * (d - b) * (d - c)) + + C = [] + for i, (t_cur, t_next) in enumerate(zip(t_steps[:-1], t_steps[1:])): + order = min(i+1, max_order) #fixed order calcs + if order == 1: + C.append([]) + else: + prev_t = t_steps[[i - k for k in range(order+1)]] + if order == 2: + coeff_cur = ((t_next - prev_t[1])**2 - (t_cur - prev_t[1])**2) / (2 * (t_cur - prev_t[1])) + coeff_prev1 = (t_next - t_cur)**2 / (2 * (prev_t[1] - t_cur)) + coeff_temp = [coeff_cur, coeff_prev1] + elif order == 3: + coeff_cur = get_def_integral_2(prev_t[1], prev_t[2], t_cur, t_next, t_cur) + coeff_prev1 = get_def_integral_2(t_cur, prev_t[2], t_cur, t_next, prev_t[1]) + coeff_prev2 = get_def_integral_2(t_cur, prev_t[1], t_cur, t_next, prev_t[2]) + coeff_temp = [coeff_cur, coeff_prev1, coeff_prev2] + elif order == 4: + coeff_cur = get_def_integral_3(prev_t[1], prev_t[2], prev_t[3], t_cur, t_next, t_cur) + coeff_prev1 = get_def_integral_3(t_cur, prev_t[2], prev_t[3], t_cur, t_next, prev_t[1]) + coeff_prev2 = get_def_integral_3(t_cur, prev_t[1], prev_t[3], t_cur, t_next, prev_t[2]) + coeff_prev3 = get_def_integral_3(t_cur, prev_t[1], prev_t[2], t_cur, t_next, prev_t[3]) + coeff_temp = [coeff_cur, coeff_prev1, coeff_prev2, coeff_prev3] + C.append(coeff_temp) + + return C + diff --git a/ComfyUI/custom_nodes/RES4LYF/beta/noise_classes.py b/ComfyUI/custom_nodes/RES4LYF/beta/noise_classes.py new file mode 100644 index 0000000000000000000000000000000000000000..781f0f6635bc8aeed2e0fb0839a821adf9cc154f --- /dev/null +++ b/ComfyUI/custom_nodes/RES4LYF/beta/noise_classes.py @@ -0,0 +1,707 @@ +import torch +import torch.nn.functional as F + +from torch import nn, Tensor, Generator, lerp +from torch.nn.functional import unfold +from torch.distributions import StudentT, Laplace + +import numpy as np +import pywt +import functools + +from typing import Callable, Tuple +from math import pi + +from comfy.k_diffusion.sampling import BrownianTreeNoiseSampler + +from ..res4lyf import RESplain + +# Set this to "True" if you have installed OpenSimplex. Recommended to install without dependencies due to conflicting packages: pip3 install opensimplex --no-deps +OPENSIMPLEX_ENABLE = False + +if OPENSIMPLEX_ENABLE: + from opensimplex import OpenSimplex + +class PrecisionTool: + def __init__(self, cast_type='fp64'): + self.cast_type = cast_type + + def cast_tensor(self, func): + @functools.wraps(func) + def wrapper(*args, **kwargs): + if self.cast_type not in ['fp64', 'fp32', 'fp16']: + return func(*args, **kwargs) + + target_device = None + for arg in args: + if torch.is_tensor(arg): + target_device = arg.device + break + if target_device is None: + for v in kwargs.values(): + if torch.is_tensor(v): + target_device = v.device + break + + # recursively zs_recast tensors in nested dictionaries + def cast_and_move_to_device(data): + if torch.is_tensor(data): + if self.cast_type == 'fp64': + return data.to(torch.float64).to(target_device) + elif self.cast_type == 'fp32': + return data.to(torch.float32).to(target_device) + elif self.cast_type == 'fp16': + return data.to(torch.float16).to(target_device) + elif isinstance(data, dict): + return {k: cast_and_move_to_device(v) for k, v in data.items()} + return data + + new_args = [cast_and_move_to_device(arg) for arg in args] + new_kwargs = {k: cast_and_move_to_device(v) for k, v in kwargs.items()} + + return func(*new_args, **new_kwargs) + return wrapper + + def set_cast_type(self, new_value): + if new_value in ['fp64', 'fp32', 'fp16']: + self.cast_type = new_value + else: + self.cast_type = 'fp64' + +precision_tool = PrecisionTool(cast_type='fp64') + + +def noise_generator_factory(cls, **fixed_params): + def create_instance(**kwargs): + params = {**fixed_params, **kwargs} + return cls(**params) + return create_instance + +def like(x): + return {'size': x.shape, 'dtype': x.dtype, 'layout': x.layout, 'device': x.device} + +def scale_to_range(x, scaled_min = -1.73, scaled_max = 1.73): #1.73 is roughly the square root of 3 + return scaled_min + (x - x.min()) * (scaled_max - scaled_min) / (x.max() - x.min()) + +def normalize(x): + return (x - x.mean())/ x.std() + +class NoiseGenerator: + def __init__(self, x=None, size=None, dtype=None, layout=None, device=None, seed=42, generator=None, sigma_min=None, sigma_max=None): + self.seed = seed + + if x is not None: + self.x = x + self.size = x.shape + self.dtype = x.dtype + self.layout = x.layout + self.device = x.device + else: + self.x = torch.zeros(size, dtype, layout, device) + + # allow overriding parameters imported from latent 'x' if specified + if size is not None: + self.size = size + if dtype is not None: + self.dtype = dtype + if layout is not None: + self.layout = layout + if device is not None: + self.device = device + + self.sigma_max = sigma_max.to(device) if isinstance(sigma_max, torch.Tensor) else sigma_max + self.sigma_min = sigma_min.to(device) if isinstance(sigma_min, torch.Tensor) else sigma_min + + self.last_seed = seed #- 1 #adapt for update being called during initialization, which increments last_seed + + if generator is None: + self.generator = torch.Generator(device=self.device).manual_seed(seed) + else: + self.generator = generator + + def __call__(self): + raise NotImplementedError("This method got clownsharked!") + + def update(self, **kwargs): + + #if not isinstance(self, BrownianNoiseGenerator): + # self.last_seed += 1 + + updated_values = [] + for attribute_name, value in kwargs.items(): + if value is not None: + setattr(self, attribute_name, value) + updated_values.append(getattr(self, attribute_name)) + return tuple(updated_values) + + + +class BrownianNoiseGenerator(NoiseGenerator): + def __call__(self, *, sigma=None, sigma_next=None, **kwargs): + return BrownianTreeNoiseSampler(self.x, self.sigma_min, self.sigma_max, seed=self.seed, cpu = self.device.type=='cpu')(sigma, sigma_next) + + + +class FractalNoiseGenerator(NoiseGenerator): + def __init__(self, x=None, size=None, dtype=None, layout=None, device=None, seed=42, generator=None, sigma_min=None, sigma_max=None, + alpha=0.0, k=1.0, scale=0.1): + super().__init__(x, size, dtype, layout, device, seed, generator, sigma_min, sigma_max) + self.update(alpha=alpha, k=k, scale=scale) + + def __call__(self, *, alpha=None, k=None, scale=None, **kwargs): + self.update(alpha=alpha, k=k, scale=scale) + self.last_seed += 1 + + if len(self.size) == 5: + b, c, t, h, w = self.size + else: + b, c, h, w = self.size + + noise = torch.normal(mean=0.0, std=1.0, size=self.size, dtype=self.dtype, layout=self.layout, device=self.device, generator=self.generator) + + y_freq = torch.fft.fftfreq(h, 1/h, device=self.device) + x_freq = torch.fft.fftfreq(w, 1/w, device=self.device) + + if len(self.size) == 5: + t_freq = torch.fft.fftfreq(t, 1/t, device=self.device) + freq = torch.sqrt(t_freq[:, None, None]**2 + y_freq[None, :, None]**2 + x_freq[None, None, :]**2).clamp(min=1e-10) + else: + freq = torch.sqrt(y_freq[:, None]**2 + x_freq[None, :]**2).clamp(min=1e-10) + + spectral_density = self.k / torch.pow(freq, self.alpha * self.scale) + spectral_density[0, 0] = 0 + + noise_fft = torch.fft.fftn(noise) + modified_fft = noise_fft * spectral_density + noise = torch.fft.ifftn(modified_fft).real + + return noise / torch.std(noise) + + + +class SimplexNoiseGenerator(NoiseGenerator): + def __init__(self, x=None, size=None, dtype=None, layout=None, device=None, seed=42, generator=None, sigma_min=None, sigma_max=None, + scale=0.01): + super().__init__(x, size, dtype, layout, device, seed, generator, sigma_min, sigma_max) + self.noise = OpenSimplex(seed=seed) + self.scale = scale + + def __call__(self, *, scale=None, **kwargs): + self.update(scale=scale) + self.last_seed += 1 + + if len(self.size) == 5: + b, c, t, h, w = self.size + else: + b, c, h, w = self.size + + noise_array = self.noise.noise3array(np.arange(w),np.arange(h),np.arange(c)) + self.noise = OpenSimplex(seed=self.noise.get_seed()+1) + + noise_tensor = torch.from_numpy(noise_array).to(self.device) + noise_tensor = torch.unsqueeze(noise_tensor, dim=0) + if len(self.size) == 5: + noise_tensor = torch.unsqueeze(noise_tensor, dim=0) + + return noise_tensor / noise_tensor.std() + #return normalize(scale_to_range(noise_tensor)) + + + +class HiresPyramidNoiseGenerator(NoiseGenerator): + def __init__(self, x=None, size=None, dtype=None, layout=None, device=None, seed=42, generator=None, sigma_min=None, sigma_max=None, + discount=0.7, mode='nearest-exact'): + super().__init__(x, size, dtype, layout, device, seed, generator, sigma_min, sigma_max) + self.update(discount=discount, mode=mode) + + def __call__(self, *, discount=None, mode=None, **kwargs): + self.update(discount=discount, mode=mode) + self.last_seed += 1 + + if len(self.size) == 5: + b, c, t, h, w = self.size + orig_h, orig_w, orig_t = h, w, t + u = nn.Upsample(size=(orig_h, orig_w, orig_t), mode=self.mode).to(self.device) + else: + b, c, h, w = self.size + orig_h, orig_w = h, w + orig_t = t = 1 + u = nn.Upsample(size=(orig_h, orig_w), mode=self.mode).to(self.device) + + noise = ((torch.rand(size=self.size, dtype=self.dtype, layout=self.layout, device=self.device, generator=self.generator) - 0.5) * 2 * 1.73) + + for i in range(4): + r = torch.rand(1, device=self.device, generator=self.generator).item() * 2 + 2 + h, w = min(orig_h * 15, int(h * (r ** i))), min(orig_w * 15, int(w * (r ** i))) + if len(self.size) == 5: + t = min(orig_t * 15, int(t * (r ** i))) + new_noise = torch.randn((b, c, t, h, w), dtype=self.dtype, layout=self.layout, device=self.device, generator=self.generator) + else: + new_noise = torch.randn((b, c, h, w), dtype=self.dtype, layout=self.layout, device=self.device, generator=self.generator) + + upsampled_noise = u(new_noise) + noise += upsampled_noise * self.discount ** i + + if h >= orig_h * 15 or w >= orig_w * 15 or t >= orig_t * 15: + break # if resolution is too high + + return noise / noise.std() + + + +class PyramidNoiseGenerator(NoiseGenerator): + def __init__(self, x=None, size=None, dtype=None, layout=None, device=None, seed=42, generator=None, sigma_min=None, sigma_max=None, + discount=0.8, mode='nearest-exact'): + super().__init__(x, size, dtype, layout, device, seed, generator, sigma_min, sigma_max) + self.update(discount=discount, mode=mode) + + def __call__(self, *, discount=None, mode=None, **kwargs): + self.update(discount=discount, mode=mode) + self.last_seed += 1 + + x = torch.zeros(self.size, dtype=self.dtype, layout=self.layout, device=self.device) + + if len(self.size) == 5: + b, c, t, h, w = self.size + orig_h, orig_w, orig_t = h, w, t + else: + b, c, h, w = self.size + orig_h, orig_w = h, w + + r = 1 + for i in range(5): + r *= 2 + + if len(self.size) == 5: + scaledSize = (b, c, t * r, h * r, w * r) + origSize = (orig_h, orig_w, orig_t) + else: + scaledSize = (b, c, h * r, w * r) + origSize = (orig_h, orig_w) + + x += torch.nn.functional.interpolate( + torch.normal(mean=0, std=0.5 ** i, size=scaledSize, dtype=self.dtype, layout=self.layout, device=self.device, generator=self.generator), + size=origSize, mode=self.mode + ) * self.discount ** i + return x / x.std() + + + +class InterpolatedPyramidNoiseGenerator(NoiseGenerator): + def __init__(self, x=None, size=None, dtype=None, layout=None, device=None, seed=42, generator=None, sigma_min=None, sigma_max=None, + discount=0.7, mode='nearest-exact'): + super().__init__(x, size, dtype, layout, device, seed, generator, sigma_min, sigma_max) + self.update(discount=discount, mode=mode) + + def __call__(self, *, discount=None, mode=None, **kwargs): + self.update(discount=discount, mode=mode) + self.last_seed += 1 + + if len(self.size) == 5: + b, c, t, h, w = self.size + orig_t, orig_h, orig_w = t, h, w + else: + b, c, h, w = self.size + orig_h, orig_w = h, w + t = orig_t = 1 + + noise = ((torch.rand(size=self.size, dtype=self.dtype, layout=self.layout, device=self.device, generator=self.generator) - 0.5) * 2 * 1.73) + multipliers = [1] + + for i in range(4): + r = torch.rand(1, device=self.device, generator=self.generator).item() * 2 + 2 + h, w = min(orig_h * 15, int(h * (r ** i))), min(orig_w * 15, int(w * (r ** i))) + + if len(self.size) == 5: + t = min(orig_t * 15, int(t * (r ** i))) + new_noise = torch.randn((b, c, t, h, w), dtype=self.dtype, layout=self.layout, device=self.device, generator=self.generator) + upsampled_noise = nn.functional.interpolate(new_noise, size=(orig_t, orig_h, orig_w), mode=self.mode) + else: + new_noise = torch.randn((b, c, h, w), dtype=self.dtype, layout=self.layout, device=self.device, generator=self.generator) + upsampled_noise = nn.functional.interpolate(new_noise, size=(orig_h, orig_w), mode=self.mode) + + noise += upsampled_noise * self.discount ** i + multipliers.append( self.discount ** i) + + if h >= orig_h * 15 or w >= orig_w * 15 or (len(self.size) == 5 and t >= orig_t * 15): + break # if resolution is too high + + noise = noise / sum([m ** 2 for m in multipliers]) ** 0.5 + return noise / noise.std() + + + +class CascadeBPyramidNoiseGenerator(NoiseGenerator): + def __init__(self, x=None, size=None, dtype=None, layout=None, device=None, seed=42, generator=None, sigma_min=None, sigma_max=None, + levels=10, mode='nearest', size_range=[1,16]): + super().__init__(x, size, dtype, layout, device, seed, generator, sigma_min, sigma_max) + self.update(epsilon=x, levels=levels, mode=mode, size_range=size_range) + + def __call__(self, *, levels=10, mode='nearest', size_range=[1,16], **kwargs): + self.update(levels=levels, mode=mode) + if len(self.size) == 5: + raise NotImplementedError("CascadeBPyramidNoiseGenerator is not implemented for 5D tensors (eg. video).") + self.last_seed += 1 + + b, c, h, w = self.size + + epsilon = torch.randn(self.size, dtype=self.dtype, layout=self.layout, device=self.device, generator=self.generator) + multipliers = [1] + for i in range(1, levels): + m = 0.75 ** i + + h, w = int(epsilon.size(-2) // (2 ** i)), int(epsilon.size(-2) // (2 ** i)) + if size_range is None or (size_range[0] <= h <= size_range[1] or size_range[0] <= w <= size_range[1]): + offset = torch.randn(epsilon.size(0), epsilon.size(1), h, w, device=self.device, generator=self.generator) + epsilon = epsilon + torch.nn.functional.interpolate(offset, size=epsilon.shape[-2:], mode=self.mode) * m + multipliers.append(m) + + if h <= 1 or w <= 1: + break + epsilon = epsilon / sum([m ** 2 for m in multipliers]) ** 0.5 #divides the epsilon tensor by the square root of the sum of the squared multipliers. + + return epsilon + + +class UniformNoiseGenerator(NoiseGenerator): + def __init__(self, x=None, size=None, dtype=None, layout=None, device=None, seed=42, generator=None, sigma_min=None, sigma_max=None, + mean=0.0, scale=1.73): + super().__init__(x, size, dtype, layout, device, seed, generator, sigma_min, sigma_max) + self.update(mean=mean, scale=scale) + + def __call__(self, *, mean=None, scale=None, **kwargs): + self.update(mean=mean, scale=scale) + self.last_seed += 1 + + noise = torch.rand(self.size, dtype=self.dtype, layout=self.layout, device=self.device, generator=self.generator) + + return self.scale * 2 * (noise - 0.5) + self.mean + +class GaussianNoiseGenerator(NoiseGenerator): + def __init__(self, x=None, size=None, dtype=None, layout=None, device=None, seed=42, generator=None, sigma_min=None, sigma_max=None, + mean=0.0, std=1.0): + super().__init__(x, size, dtype, layout, device, seed, generator, sigma_min, sigma_max) + self.update(mean=mean, std=std) + + def __call__(self, *, mean=None, std=None, **kwargs): + self.update(mean=mean, std=std) + self.last_seed += 1 + + noise = torch.randn(self.size, dtype=self.dtype, layout=self.layout, device=self.device, generator=self.generator) + + return (noise - noise.mean()) / noise.std() + +class GaussianBackwardsNoiseGenerator(NoiseGenerator): + def __init__(self, x=None, size=None, dtype=None, layout=None, device=None, seed=42, generator=None, sigma_min=None, sigma_max=None, + mean=0.0, std=1.0): + super().__init__(x, size, dtype, layout, device, seed, generator, sigma_min, sigma_max) + self.update(mean=mean, std=std) + + def __call__(self, *, mean=None, std=None, **kwargs): + self.update(mean=mean, std=std) + self.last_seed += 1 + RESplain("GaussianBackwards last seed:", self.generator.initial_seed()) + self.generator.manual_seed(self.generator.initial_seed() - 1) + noise = torch.randn(self.size, dtype=self.dtype, layout=self.layout, device=self.device, generator=self.generator) + + return (noise - noise.mean()) / noise.std() + +class LaplacianNoiseGenerator(NoiseGenerator): + def __init__(self, x=None, size=None, dtype=None, layout=None, device=None, seed=42, generator=None, sigma_min=None, sigma_max=None, + loc=0, scale=1.0): + super().__init__(x, size, dtype, layout, device, seed, generator, sigma_min, sigma_max) + self.update(loc=loc, scale=scale) + + def __call__(self, *, loc=None, scale=None, **kwargs): + self.update(loc=loc, scale=scale) + self.last_seed += 1 + + # b, c, h, w = self.size + # orig_h, orig_w = h, w + + noise = torch.randn(self.size, dtype=self.dtype, layout=self.layout, device=self.device, generator=self.generator) / 4.0 + + rng_state = torch.random.get_rng_state() + torch.manual_seed(self.generator.initial_seed()) + laplacian_noise = Laplace(loc=self.loc, scale=self.scale).rsample(self.size).to(self.device) + self.generator.manual_seed(self.generator.initial_seed() + 1) + torch.random.set_rng_state(rng_state) + + noise += laplacian_noise + return noise / noise.std() + +class StudentTNoiseGenerator(NoiseGenerator): + def __init__(self, x=None, size=None, dtype=None, layout=None, device=None, seed=42, generator=None, sigma_min=None, sigma_max=None, + loc=0, scale=0.2, df=1): + super().__init__(x, size, dtype, layout, device, seed, generator, sigma_min, sigma_max) + self.update(loc=loc, scale=scale, df=df) + + def __call__(self, *, loc=None, scale=None, df=None, **kwargs): + self.update(loc=loc, scale=scale, df=df) + self.last_seed += 1 + + # b, c, h, w = self.size + # orig_h, orig_w = h, w + + rng_state = torch.random.get_rng_state() + torch.manual_seed(self.generator.initial_seed()) + + noise = StudentT(loc=self.loc, scale=self.scale, df=self.df).rsample(self.size) + if not isinstance(self, BrownianNoiseGenerator): + self.last_seed += 1 + + s = torch.quantile(noise.flatten(start_dim=1).abs(), 0.75, dim=-1) + + if len(self.size) == 5: + s = s.reshape(*s.shape, 1, 1, 1, 1) + else: + s = s.reshape(*s.shape, 1, 1, 1) + + noise = noise.clamp(-s, s) + + noise_latent = torch.copysign(torch.pow(torch.abs(noise), 0.5), noise).to(self.device) + + self.generator.manual_seed(self.generator.initial_seed() + 1) + torch.random.set_rng_state(rng_state) + return (noise_latent - noise_latent.mean()) / noise_latent.std() + +class WaveletNoiseGenerator(NoiseGenerator): + def __init__(self, x=None, size=None, dtype=None, layout=None, device=None, seed=42, generator=None, sigma_min=None, sigma_max=None, + wavelet='haar'): + super().__init__(x, size, dtype, layout, device, seed, generator, sigma_min, sigma_max) + self.update(wavelet=wavelet) + + def __call__(self, *, wavelet=None, **kwargs): + self.update(wavelet=wavelet) + self.last_seed += 1 + + # b, c, h, w = self.size + # orig_h, orig_w = h, w + + # noise for spatial dimensions only + coeffs = pywt.wavedecn(torch.randn(self.size, dtype=self.dtype, layout=self.layout, device=self.device, generator=self.generator).to('cpu'), wavelet=self.wavelet, mode='periodization') + noise = pywt.waverecn(coeffs, wavelet=self.wavelet, mode='periodization') + noise_tensor = torch.tensor(noise, dtype=self.dtype, device=self.device) + + noise_tensor = (noise_tensor - noise_tensor.mean()) / noise_tensor.std() + return noise_tensor + +class PerlinNoiseGenerator(NoiseGenerator): + def __init__(self, x=None, size=None, dtype=None, layout=None, device=None, seed=42, generator=None, sigma_min=None, sigma_max=None, + detail=0.0): + super().__init__(x, size, dtype, layout, device, seed, generator, sigma_min, sigma_max) + self.update(detail=detail) + + @staticmethod + def get_positions(block_shape: Tuple[int, int]) -> Tensor: + bh, bw = block_shape + positions = torch.stack( + torch.meshgrid( + [(torch.arange(b) + 0.5) / b for b in (bw, bh)], + indexing="xy", + ), + -1, + ).view(1, bh, bw, 1, 1, 2) + return positions + + @staticmethod + def unfold_grid(vectors: Tensor) -> Tensor: + batch_size, _, gpy, gpx = vectors.shape + return ( + unfold(vectors, (2, 2)) + .view(batch_size, 2, 4, -1) + .permute(0, 2, 3, 1) + .view(batch_size, 4, gpy - 1, gpx - 1, 2) + ) + + @staticmethod + def smooth_step(t: Tensor) -> Tensor: + return t * t * (3.0 - 2.0 * t) + + @staticmethod + def perlin_noise_tensor( + self, + vectors: Tensor, positions: Tensor, step: Callable = None + ) -> Tensor: + if step is None: + step = self.smooth_step + + batch_size = vectors.shape[0] + # grid height, grid width + gh, gw = vectors.shape[2:4] + # block height, block width + bh, bw = positions.shape[1:3] + + for i in range(2): + if positions.shape[i + 3] not in (1, vectors.shape[i + 2]): + raise Exception( + f"Blocks shapes do not match: vectors ({vectors.shape[1]}, {vectors.shape[2]}), positions {gh}, {gw})" + ) + + if positions.shape[0] not in (1, batch_size): + raise Exception( + f"Batch sizes do not match: vectors ({vectors.shape[0]}), positions ({positions.shape[0]})" + ) + + vectors = vectors.view(batch_size, 4, 1, gh * gw, 2) + positions = positions.view(positions.shape[0], bh * bw, -1, 2) + + step_x = step(positions[..., 0]) + step_y = step(positions[..., 1]) + + row0 = lerp( + (vectors[:, 0] * positions).sum(dim=-1), + (vectors[:, 1] * (positions - positions.new_tensor((1, 0)))).sum(dim=-1), + step_x, + ) + row1 = lerp( + (vectors[:, 2] * (positions - positions.new_tensor((0, 1)))).sum(dim=-1), + (vectors[:, 3] * (positions - positions.new_tensor((1, 1)))).sum(dim=-1), + step_x, + ) + noise = lerp(row0, row1, step_y) + return ( + noise.view( + batch_size, + bh, + bw, + gh, + gw, + ) + .permute(0, 3, 1, 4, 2) + .reshape(batch_size, gh * bh, gw * bw) + ) + + def perlin_noise( + self, + grid_shape: Tuple[int, int], + out_shape: Tuple[int, int], + batch_size: int = 1, + generator: Generator = None, + *args, + **kwargs, + ) -> Tensor: + gh, gw = grid_shape # grid height and width + oh, ow = out_shape # output height and width + bh, bw = oh // gh, ow // gw # block height and width + + if oh != bh * gh: + raise Exception(f"Output height {oh} must be divisible by grid height {gh}") + if ow != bw * gw != 0: + raise Exception(f"Output width {ow} must be divisible by grid width {gw}") + + angle = torch.empty( + [batch_size] + [s + 1 for s in grid_shape], device=self.device, *args, **kwargs + ).uniform_(to=2.0 * pi, generator=self.generator) + # random vectors on grid points + vectors = self.unfold_grid(torch.stack((torch.cos(angle), torch.sin(angle)), dim=1)) + # positions inside grid cells [0, 1) + positions = self.get_positions((bh, bw)).to(vectors) + return self.perlin_noise_tensor(self, vectors, positions).squeeze(0) + + def __call__(self, *, detail=None, **kwargs): + self.update(detail=detail) #currently unused + self.last_seed += 1 + if len(self.size) == 5: + b, c, t, h, w = self.size + noise = torch.randn(self.size, dtype=self.dtype, layout=self.layout, device=self.device, generator=self.generator) / 2.0 + + for tt in range(t): + for i in range(2): + perlin_slice = self.perlin_noise((h, w), (h, w), batch_size=c, generator=self.generator).to(self.device) + perlin_expanded = perlin_slice.unsqueeze(0).unsqueeze(2) + time_slice = noise[:, :, tt:tt+1, :, :] + noise[:, :, tt:tt+1, :, :] += perlin_expanded + else: + b, c, h, w = self.size + #orig_h, orig_w = h, w + + noise = torch.randn(self.size, dtype=self.dtype, layout=self.layout, device=self.device, generator=self.generator) / 2.0 + for i in range(2): + noise += self.perlin_noise((h, w), (h, w), batch_size=c, generator=self.generator).to(self.device) + + return noise / noise.std() + +from functools import partial + +NOISE_GENERATOR_CLASSES = { + "fractal" : FractalNoiseGenerator, + "gaussian" : GaussianNoiseGenerator, + "gaussian_backwards" : GaussianBackwardsNoiseGenerator, + "uniform" : UniformNoiseGenerator, + "pyramid-cascade_B" : CascadeBPyramidNoiseGenerator, + "pyramid-interpolated" : InterpolatedPyramidNoiseGenerator, + "pyramid-bilinear" : noise_generator_factory(PyramidNoiseGenerator, mode='bilinear'), + "pyramid-bicubic" : noise_generator_factory(PyramidNoiseGenerator, mode='bicubic'), + "pyramid-nearest" : noise_generator_factory(PyramidNoiseGenerator, mode='nearest'), + "hires-pyramid-bilinear": noise_generator_factory(HiresPyramidNoiseGenerator, mode='bilinear'), + "hires-pyramid-bicubic" : noise_generator_factory(HiresPyramidNoiseGenerator, mode='bicubic'), + "hires-pyramid-nearest" : noise_generator_factory(HiresPyramidNoiseGenerator, mode='nearest'), + "brownian" : BrownianNoiseGenerator, + "laplacian" : LaplacianNoiseGenerator, + "studentt" : StudentTNoiseGenerator, + "wavelet" : WaveletNoiseGenerator, + "perlin" : PerlinNoiseGenerator, +} + + +NOISE_GENERATOR_CLASSES_SIMPLE = { + "none" : GaussianNoiseGenerator, + "brownian" : BrownianNoiseGenerator, + "gaussian" : GaussianNoiseGenerator, + "gaussian_backwards" : GaussianBackwardsNoiseGenerator, + "laplacian" : LaplacianNoiseGenerator, + "perlin" : PerlinNoiseGenerator, + "studentt" : StudentTNoiseGenerator, + "uniform" : UniformNoiseGenerator, + "wavelet" : WaveletNoiseGenerator, + "brown" : noise_generator_factory(FractalNoiseGenerator, alpha=2.0), + "pink" : noise_generator_factory(FractalNoiseGenerator, alpha=1.0), + "white" : noise_generator_factory(FractalNoiseGenerator, alpha=0.0), + "blue" : noise_generator_factory(FractalNoiseGenerator, alpha=-1.0), + "violet" : noise_generator_factory(FractalNoiseGenerator, alpha=-2.0), + "ultraviolet_A" : noise_generator_factory(FractalNoiseGenerator, alpha=-3.0), + "ultraviolet_B" : noise_generator_factory(FractalNoiseGenerator, alpha=-4.0), + "ultraviolet_C" : noise_generator_factory(FractalNoiseGenerator, alpha=-5.0), + + "hires-pyramid-bicubic" : noise_generator_factory(HiresPyramidNoiseGenerator, mode='bicubic'), + "hires-pyramid-bilinear": noise_generator_factory(HiresPyramidNoiseGenerator, mode='bilinear'), + "hires-pyramid-nearest" : noise_generator_factory(HiresPyramidNoiseGenerator, mode='nearest'), + "pyramid-bicubic" : noise_generator_factory(PyramidNoiseGenerator, mode='bicubic'), + "pyramid-bilinear" : noise_generator_factory(PyramidNoiseGenerator, mode='bilinear'), + "pyramid-nearest" : noise_generator_factory(PyramidNoiseGenerator, mode='nearest'), + "pyramid-interpolated" : InterpolatedPyramidNoiseGenerator, + "pyramid-cascade_B" : CascadeBPyramidNoiseGenerator, +} + +if OPENSIMPLEX_ENABLE: + NOISE_GENERATOR_CLASSES.update({ + "simplex": SimplexNoiseGenerator, + }) + +NOISE_GENERATOR_NAMES = tuple(NOISE_GENERATOR_CLASSES.keys()) +NOISE_GENERATOR_NAMES_SIMPLE = tuple(NOISE_GENERATOR_CLASSES_SIMPLE.keys()) + + +@precision_tool.cast_tensor +def prepare_noise(latent_image, seed, noise_type, noise_inds=None, alpha=1.0, k=1.0): # adapted from comfy/sample.py: https://github.com/comfyanonymous/ComfyUI + #optional arg skip can be used to skip and discard x number of noise generations for a given seed + noise_func = NOISE_GENERATOR_CLASSES.get(noise_type)(x=latent_image, seed=seed, sigma_min=0.0291675, sigma_max=14.614642) # WARNING: HARDCODED SDXL SIGMA RANGE! + + if noise_type == "fractal": + noise_func.alpha = alpha + noise_func.k = k + + # from here until return is very similar to comfy/sample.py + if noise_inds is None: + return noise_func(sigma=14.614642, sigma_next=0.0291675) + + unique_inds, inverse = np.unique(noise_inds, return_inverse=True) + noises = [] + for i in range(unique_inds[-1]+1): + noise = noise_func(size = [1] + list(latent_image.size())[1:], dtype=latent_image.dtype, layout=latent_image.layout, device=latent_image.device) + if i in unique_inds: + noises.append(noise) + noises = [noises[i] for i in inverse] + noises = torch.cat(noises, axis=0) + return noises diff --git a/ComfyUI/custom_nodes/RES4LYF/beta/phi_functions.py b/ComfyUI/custom_nodes/RES4LYF/beta/phi_functions.py new file mode 100644 index 0000000000000000000000000000000000000000..66986f0488c8c1fd45f046757750c68bd84ab7fd --- /dev/null +++ b/ComfyUI/custom_nodes/RES4LYF/beta/phi_functions.py @@ -0,0 +1,140 @@ +import torch +import math +from typing import Optional + + +# Remainder solution +def _phi(j, neg_h): + remainder = torch.zeros_like(neg_h) + + for k in range(j): + remainder += (neg_h)**k / math.factorial(k) + phi_j_h = ((neg_h).exp() - remainder) / (neg_h)**j + + return phi_j_h + +def calculate_gamma(c2, c3): + return (3*(c3**3) - 2*c3) / (c2*(2 - 3*c2)) + +# Exact analytic solution originally calculated by Clybius. https://github.com/Clybius/ComfyUI-Extra-Samplers/tree/main +def _gamma(n: int,) -> int: + """ + https://en.wikipedia.org/wiki/Gamma_function + for every positive integer n, + Γ(n) = (n-1)! + """ + return math.factorial(n-1) + +def _incomplete_gamma(s: int, x: float, gamma_s: Optional[int] = None) -> float: + """ + https://en.wikipedia.org/wiki/Incomplete_gamma_function#Special_values + if s is a positive integer, + Γ(s, x) = (s-1)!*∑{k=0..s-1}(x^k/k!) + """ + if gamma_s is None: + gamma_s = _gamma(s) + + sum_: float = 0 + # {k=0..s-1} inclusive + for k in range(s): + numerator: float = x**k + denom: int = math.factorial(k) + quotient: float = numerator/denom + sum_ += quotient + incomplete_gamma_: float = sum_ * math.exp(-x) * gamma_s + return incomplete_gamma_ + +def phi(j: int, neg_h: float, ): + """ + For j={1,2,3}: you could alternatively use Kat's phi_1, phi_2, phi_3 which perform fewer steps + + Lemma 1 + https://arxiv.org/abs/2308.02157 + ϕj(-h) = 1/h^j*∫{0..h}(e^(τ-h)*(τ^(j-1))/((j-1)!)dτ) + + https://www.wolframalpha.com/input?i=integrate+e%5E%28%CF%84-h%29*%28%CF%84%5E%28j-1%29%2F%28j-1%29%21%29d%CF%84 + = 1/h^j*[(e^(-h)*(-τ)^(-j)*τ(j))/((j-1)!)]{0..h} + https://www.wolframalpha.com/input?i=integrate+e%5E%28%CF%84-h%29*%28%CF%84%5E%28j-1%29%2F%28j-1%29%21%29d%CF%84+between+0+and+h + = 1/h^j*((e^(-h)*(-h)^(-j)*h^j*(Γ(j)-Γ(j,-h)))/(j-1)!) + = (e^(-h)*(-h)^(-j)*h^j*(Γ(j)-Γ(j,-h))/((j-1)!*h^j) + = (e^(-h)*(-h)^(-j)*(Γ(j)-Γ(j,-h))/(j-1)! + = (e^(-h)*(-h)^(-j)*(Γ(j)-Γ(j,-h))/Γ(j) + = (e^(-h)*(-h)^(-j)*(1-Γ(j,-h)/Γ(j)) + + requires j>0 + """ + assert j > 0 + gamma_: float = _gamma(j) + incomp_gamma_: float = _incomplete_gamma(j, neg_h, gamma_s=gamma_) + phi_: float = math.exp(neg_h) * neg_h**-j * (1-incomp_gamma_/gamma_) + return phi_ + + + +from mpmath import mp, mpf, factorial, exp + + +mp.dps = 80 # e.g. 80 decimal digits (~ float256) + +def phi_mpmath_series(j: int, neg_h: float) -> float: + """ + Arbitrary‐precision phi_j(-h) via the remainder‐series definition, + using mpmath’s mpf and factorial. + """ + j = int(j) + z = mpf(float(neg_h)) + S = mp.mpf('0') # S = sum_{k=0..j-1} z^k / k! + for k in range(j): + S += (z**k) / factorial(k) + phi_val = (exp(z) - S) / (z**j) + return float(phi_val) + + + +class Phi: + def __init__(self, h, c, analytic_solution=False): + self.h = h + self.c = c + self.cache = {} + if analytic_solution: + #self.phi_f = superphi + self.phi_f = phi_mpmath_series + self.h = mpf(float(h)) + self.c = [mpf(c_val) for c_val in c] + #self.c = c + #self.phi_f = phi + else: + self.phi_f = phi + #self.phi_f = _phi # remainder method + + def __call__(self, j, i=-1): + if (j, i) in self.cache: + return self.cache[(j, i)] + + if i < 0: + c = 1 + else: + c = self.c[i - 1] + if c == 0: + self.cache[(j, i)] = 0 + return 0 + + if j == 0 and type(c) in {float, torch.Tensor}: + result = math.exp(float(-self.h * c)) + else: + result = self.phi_f(j, -self.h * c) + + self.cache[(j, i)] = result + + return result + + + +from mpmath import mp, mpf, gamma, gammainc + +def superphi(j: int, neg_h: float, ): + gamma_: float = gamma(j) + incomp_gamma_: float = gamma_ - gammainc(j, 0, float(neg_h)) + phi_: float = float(math.exp(float(neg_h)) * neg_h**-j) * (1-incomp_gamma_/gamma_) + return float(phi_) + diff --git a/ComfyUI/custom_nodes/RES4LYF/beta/rk_coefficients_beta.py b/ComfyUI/custom_nodes/RES4LYF/beta/rk_coefficients_beta.py new file mode 100644 index 0000000000000000000000000000000000000000..a804cfaac1205bfeb51a332596465f8ce605a79b --- /dev/null +++ b/ComfyUI/custom_nodes/RES4LYF/beta/rk_coefficients_beta.py @@ -0,0 +1,3307 @@ +import torch +from torch import Tensor + +import copy +import math +from mpmath import mp, mpf, factorial, exp +mp.dps = 80 +from typing import Optional, Callable, Tuple, Dict, Any, Union, TYPE_CHECKING, TypeVar + +from .deis_coefficients import get_deis_coeff_list +from .phi_functions import phi, Phi, calculate_gamma + +from ..helper import ExtraOptions, get_extra_options_kv, extra_options_flag + + +from itertools import permutations, combinations +import random + +from einops import rearrange, einsum +from ..res4lyf import get_display_sampler_category + +# Samplers with free parameters (c1, c2, c3) +# 1 2 3 +# X res_2s +# X X res_3s +# X res_3s_alt +# X res_3s_strehmel_weiner +# X dpmpp_2s (dpmpp_sde_2s has c2=1.0) +# X X dpmpp_3s +# X X irk_exp_diag_2s + +RK_EXPONENTIAL_PREFIXES = ( + "res", + "dpmpp", + "ddim", + "pec", + "etdrk", + "lawson", + "abnorsett", + ) + +def is_exponential(rk_type:str) -> bool: + return rk_type.startswith(RK_EXPONENTIAL_PREFIXES) + +RK_SAMPLER_NAMES_BETA_FOLDERS = ["none", + "multistep/res_2m", + "multistep/res_3m", + + "multistep/dpmpp_2m", + "multistep/dpmpp_3m", + + "multistep/abnorsett_2m", + "multistep/abnorsett_3m", + "multistep/abnorsett_4m", + + "multistep/deis_2m", + "multistep/deis_3m", + "multistep/deis_4m", + + "exponential/res_2s_rkmk2e", + "exponential/res_2s", + "exponential/res_2s_stable", + "exponential/res_3s", + "exponential/res_3s_non-monotonic", + "exponential/res_3s_alt", + "exponential/res_3s_cox_matthews", + "exponential/res_3s_lie", + "exponential/res_3s_sunstar", + + "exponential/res_3s_strehmel_weiner", + "exponential/res_4s_krogstad", + "exponential/res_4s_krogstad_alt", + "exponential/res_4s_strehmel_weiner", + "exponential/res_4s_strehmel_weiner_alt", + "exponential/res_4s_cox_matthews", + "exponential/res_4s_cfree4", + "exponential/res_4s_friedli", + "exponential/res_4s_minchev", + "exponential/res_4s_munthe-kaas", + + "exponential/res_5s", + "exponential/res_5s_hochbruck-ostermann", + "exponential/res_6s", + "exponential/res_8s", + "exponential/res_8s_alt", + + "exponential/res_10s", + "exponential/res_15s", + "exponential/res_16s", + + "exponential/etdrk2_2s", + "exponential/etdrk3_a_3s", + "exponential/etdrk3_b_3s", + "exponential/etdrk4_4s", + "exponential/etdrk4_4s_alt", + + "exponential/dpmpp_2s", + "exponential/dpmpp_sde_2s", + "exponential/dpmpp_3s", + + "exponential/lawson2a_2s", + "exponential/lawson2b_2s", + + "exponential/lawson4_4s", + "exponential/lawson41-gen_4s", + "exponential/lawson41-gen-mod_4s", + + "exponential/ddim", + + "hybrid/pec423_2h2s", + "hybrid/pec433_2h3s", + + "hybrid/abnorsett2_1h2s", + "hybrid/abnorsett3_2h2s", + "hybrid/abnorsett4_3h2s", + + + "hybrid/lawson42-gen-mod_1h4s", + "hybrid/lawson43-gen-mod_2h4s", + "hybrid/lawson44-gen-mod_3h4s", + "hybrid/lawson45-gen-mod_4h4s", + + "linear/ralston_2s", + "linear/ralston_3s", + "linear/ralston_4s", + + + + "linear/midpoint_2s", + "linear/heun_2s", + "linear/heun_3s", + + "linear/houwen-wray_3s", + "linear/kutta_3s", + "linear/ssprk3_3s", + "linear/ssprk4_4s", + + "linear/rk38_4s", + "linear/rk4_4s", + "linear/rk5_7s", + "linear/rk6_7s", + + "linear/bogacki-shampine_4s", + "linear/bogacki-shampine_7s", + + "linear/dormand-prince_6s", + "linear/dormand-prince_13s", + + "linear/tsi_7s", + #"verner_robust_16s", + + "linear/euler", + + "diag_implicit/irk_exp_diag_2s", + + "diag_implicit/kraaijevanger_spijker_2s", + "diag_implicit/qin_zhang_2s", + + "diag_implicit/pareschi_russo_2s", + "diag_implicit/pareschi_russo_alt_2s", + + "diag_implicit/crouzeix_2s", + "diag_implicit/crouzeix_3s", + "diag_implicit/crouzeix_3s_alt", + + "fully_implicit/gauss-legendre_2s", + "fully_implicit/gauss-legendre_3s", + "fully_implicit/gauss-legendre_4s", + "fully_implicit/gauss-legendre_4s_alternating_a", + "fully_implicit/gauss-legendre_4s_ascending_a", + "fully_implicit/gauss-legendre_4s_alt", + "fully_implicit/gauss-legendre_5s", + "fully_implicit/gauss-legendre_5s_ascending", + #"gauss-legendre_diag_8s", + + + "fully_implicit/radau_ia_2s", + "fully_implicit/radau_ia_3s", + + "fully_implicit/radau_iia_2s", + "fully_implicit/radau_iia_3s", + "fully_implicit/radau_iia_3s_alt", + "fully_implicit/radau_iia_5s", + "fully_implicit/radau_iia_7s", + "fully_implicit/radau_iia_9s", + "fully_implicit/radau_iia_11s", + + "fully_implicit/lobatto_iiia_2s", + "fully_implicit/lobatto_iiia_3s", + "fully_implicit/lobatto_iiia_4s", + "fully_implicit/lobatto_iiib_2s", + "fully_implicit/lobatto_iiib_3s", + "fully_implicit/lobatto_iiib_4s", + + "fully_implicit/lobatto_iiic_2s", + "fully_implicit/lobatto_iiic_3s", + "fully_implicit/lobatto_iiic_4s", + + "fully_implicit/lobatto_iiic_star_2s", + "fully_implicit/lobatto_iiic_star_3s", + "fully_implicit/lobatto_iiid_2s", + "fully_implicit/lobatto_iiid_3s", + + ] + + + +RK_SAMPLER_NAMES_BETA_NO_FOLDERS = [] +for orig_sampler_name in RK_SAMPLER_NAMES_BETA_FOLDERS[1:]: + sampler_name = orig_sampler_name.split("/")[-1] if "/" in orig_sampler_name else orig_sampler_name + RK_SAMPLER_NAMES_BETA_NO_FOLDERS.append(sampler_name) + +IRK_SAMPLER_NAMES_BETA_FOLDERS = ["none", "use_explicit"] +for orig_sampler_name in RK_SAMPLER_NAMES_BETA_FOLDERS[1:]: + if "implicit" in orig_sampler_name and "/" in orig_sampler_name: + IRK_SAMPLER_NAMES_BETA_FOLDERS.append(orig_sampler_name) + +IRK_SAMPLER_NAMES_BETA_NO_FOLDERS = [] +for orig_sampler_name in IRK_SAMPLER_NAMES_BETA_FOLDERS[1:]: + sampler_name = orig_sampler_name.split("/")[-1] if "/" in orig_sampler_name else orig_sampler_name + IRK_SAMPLER_NAMES_BETA_NO_FOLDERS.append(sampler_name) + +RK_SAMPLER_FOLDER_MAP = {} +for orig_sampler_name in RK_SAMPLER_NAMES_BETA_FOLDERS: + if "/" in orig_sampler_name: + folder, sampler_name = orig_sampler_name.rsplit("/", 1) + else: + folder = "" + sampler_name = orig_sampler_name + RK_SAMPLER_FOLDER_MAP[sampler_name] = folder + +IRK_SAMPLER_FOLDER_MAP = {} +for orig_sampler_name in IRK_SAMPLER_NAMES_BETA_FOLDERS: + if "/" in orig_sampler_name: + folder, sampler_name = orig_sampler_name.rsplit("/", 1) + else: + folder = "" + sampler_name = orig_sampler_name + IRK_SAMPLER_FOLDER_MAP[sampler_name] = folder + +class DualFormatList(list): + """list that can match items with or without category prefixes.""" + def __contains__(self, item): + if super().__contains__(item): + return True + + if isinstance(item, str) and "/" in item: + base_name = item.split("/")[-1] + return any(name.endswith(base_name) for name in self) + + return any(isinstance(opt, str) and opt.endswith("/" + item) for opt in self) + +def get_sampler_name_list(nameOnly = False) -> list: + sampler_name_list = [] + for sampler_name in RK_SAMPLER_FOLDER_MAP: + if get_display_sampler_category() and not nameOnly: + folder_name = RK_SAMPLER_FOLDER_MAP[sampler_name] + full_sampler_name = f"{folder_name}/{sampler_name}" + else: + full_sampler_name = sampler_name + if full_sampler_name[0] == "/": + full_sampler_name = full_sampler_name[1:] + sampler_name_list.append(full_sampler_name) + return DualFormatList(sampler_name_list) + +def get_default_sampler_name(nameOnly = False) -> str: + default_sampler_name = "res_2m" + #find the key associated with the default value + for sampler_name in RK_SAMPLER_FOLDER_MAP: + if sampler_name == default_sampler_name: + if get_display_sampler_category() and not nameOnly: + folder_name = RK_SAMPLER_FOLDER_MAP[sampler_name] + return f"{folder_name}/{default_sampler_name}" + else: + return default_sampler_name + return default_sampler_name + +def get_implicit_sampler_name_list(nameOnly = False) -> list: + implicit_sampler_name_list = [] + for sampler_name in IRK_SAMPLER_FOLDER_MAP: + if get_display_sampler_category() and not nameOnly: + folder_name = IRK_SAMPLER_FOLDER_MAP[sampler_name] + full_sampler_name = f"{folder_name}/{sampler_name}" + else: + full_sampler_name = sampler_name + if full_sampler_name[0] == "/": + full_sampler_name = full_sampler_name[1:] + implicit_sampler_name_list.append(full_sampler_name) + return DualFormatList(implicit_sampler_name_list) + +def get_default_implicit_sampler_name(nameOnly = False) -> str: + default_sampler_value = "explicit_diagonal" + #find the key associated with the default value + for sampler_name in IRK_SAMPLER_FOLDER_MAP: + if sampler_name == default_sampler_value: + if get_display_sampler_category() and not nameOnly: + folder_name = IRK_SAMPLER_FOLDER_MAP[sampler_name] + return f"{folder_name}/{default_sampler_value}" + else: + return default_sampler_value + return default_sampler_value + +def get_full_sampler_name(sampler_name_in: str) -> str: + if "/" in sampler_name_in and sampler_name_in[0] != "/": + return sampler_name_in + for sampler_name in RK_SAMPLER_FOLDER_MAP: + if sampler_name == sampler_name_in: + folder_name = RK_SAMPLER_FOLDER_MAP[sampler_name] + return f"{folder_name}/{sampler_name}" + return sampler_name + +def process_sampler_name(sampler_name_in): + processed_name = sampler_name_in.split("/")[-1] if "/" in sampler_name_in else sampler_name_in + full_sampler_name = get_full_sampler_name(sampler_name_in) + + if sampler_name_in.startswith("fully_implicit") or sampler_name_in.startswith("diag_implicit"): + implicit_sampler_name = processed_name + sampler_name = "euler" + else: + sampler_name = processed_name + implicit_sampler_name = "use_explicit" + + return sampler_name, implicit_sampler_name + + + + +alpha_crouzeix = (2/(3**0.5)) * math.cos(math.pi / 18) +gamma_crouzeix = (1/(3**0.5)) * math.cos(math.pi / 18) + 1/2 # Crouzeix & Raviart 1980; A-stable; pg 100 in Solving Ordinary Differential Equations II +delta_crouzeix = 1 / (6 * (2 * gamma_crouzeix - 1)**2) # Crouzeix & Raviart 1980; A-stable; pg 100 in Solving Ordinary Differential Equations II + +rk_coeff = { + "gauss-legendre_diag_8s": ( # https://github.com/SciML/IRKGaussLegendre.jl/blob/master/src/IRKCoefficients.jl Antoñana, M., Makazaga, J., Murua, Ander. "Reducing and monitoring round-off error propagation for symplectic implicit Runge-Kutta schemes." Numerical Algorithms. 2017. + [ + [ + 0.5, + 0,0,0,0,0,0,0, + ], + [ + 1.0818949631055814971365081647359309e00, + 0.5, + 0,0,0,0,0,0, + ], + [ + 9.5995729622205494766003095439844678e-01, + 1.0869589243008327233290709646162480e00, + 0.5, + 0,0,0,0,0, + ], + [ + 1.0247213458032003748680445816450829e00, + 9.5505887369737431186016905653386876e-01, + 1.0880938387323083134422138713913203e00, + 0.5, + 0,0,0,0, + ], + [ + 9.8302382676362890697311829123888390e-01, + 1.0287597754747493109782305570410685e00, + 9.5383453518519996588326911440754302e-01, + 1.0883471611098277842507073806008045e00, + 0.5, + 0,0,0, + ], + [ + 1.0122259141132982060539425317219435e00, + 9.7998287236359129082628958290257329e-01, + 1.0296038730649779374630125982121223e00, + 9.5383453518519996588326911440754302e-01, + 1.0880938387323083134422138713913203e00, + 0.5, + 0,0, + ], + [ + 9.9125143323080263118822334698608777e-01, + 1.0140743558891669291459735166525994e00, + 9.7998287236359129082628958290257329e-01, + 1.0287597754747493109782305570410685e00, + 9.5505887369737431186016905653386876e-01, + 1.0869589243008327233290709646162480e00, + 0.5, + 0, + ], + [ + 1.0054828082532158826793409353214951e00, + 9.9125143323080263118822334698608777e-01, + 1.0122259141132982060539425317219435e00, + 9.8302382676362890697311829123888390e-01, + 1.0247213458032003748680445816450829e00, + 9.5995729622205494766003095439844678e-01, + 1.0818949631055814971365081647359309e00, + 0.5, + ], + ], + [ + [ + 5.0614268145188129576265677154981094e-02, + 1.1119051722668723527217799721312045e-01, + 1.5685332293894364366898110099330067e-01, + 1.8134189168918099148257522463859781e-01, + 1.8134189168918099148257522463859781e-01, + 1.5685332293894364366898110099330067e-01, + 1.1119051722668723527217799721312045e-01, + 5.0614268145188129576265677154981094e-02,] + ], + [ + 1.9855071751231884158219565715263505e-02, # 0.019855071751231884158219565715263505 + 1.0166676129318663020422303176208480e-01, + 2.3723379504183550709113047540537686e-01, + 4.0828267875217509753026192881990801e-01, + 5.9171732124782490246973807118009203e-01, + 7.6276620495816449290886952459462321e-01, + 8.9833323870681336979577696823791522e-01, + 9.8014492824876811584178043428473653e-01, + ] + ), + + + "gauss-legendre_5s": ( + [ + [4563950663 / 32115191526, + (310937500000000 / 2597974476091533 + 45156250000 * (739**0.5) / 8747388808389), + (310937500000000 / 2597974476091533 - 45156250000 * (739**0.5) / 8747388808389), + (5236016175 / 88357462711 + 709703235 * (739**0.5) / 353429850844), + (5236016175 / 88357462711 - 709703235 * (739**0.5) / 353429850844)], + + [(4563950663 / 32115191526 - 38339103 * (739**0.5) / 6250000000), + (310937500000000 / 2597974476091533 + 9557056475401 * (739**0.5) / 3498955523355600000), + (310937500000000 / 2597974476091533 - 14074198220719489 * (739**0.5) / 3498955523355600000), + (5236016175 / 88357462711 + 5601362553163918341 * (739**0.5) / 2208936567775000000000), + (5236016175 / 88357462711 - 5040458465159165409 * (739**0.5) / 2208936567775000000000)], + + [(4563950663 / 32115191526 + 38339103 * (739**0.5) / 6250000000), + (310937500000000 / 2597974476091533 + 14074198220719489 * (739**0.5) / 3498955523355600000), + (310937500000000 / 2597974476091533 - 9557056475401 * (739**0.5) / 3498955523355600000), + (5236016175 / 88357462711 + 5040458465159165409 * (739**0.5) / 2208936567775000000000), + (5236016175 / 88357462711 - 5601362553163918341 * (739**0.5) / 2208936567775000000000)], + + [(4563950663 / 32115191526 - 38209 * (739**0.5) / 7938810), + (310937500000000 / 2597974476091533 - 359369071093750 * (739**0.5) / 70145310854471391), + (310937500000000 / 2597974476091533 - 323282178906250 * (739**0.5) / 70145310854471391), + (5236016175 / 88357462711 - 470139 * (739**0.5) / 1413719403376), + (5236016175 / 88357462711 - 44986764863 * (739**0.5) / 21205791050640)], + + [(4563950663 / 32115191526 + 38209 * (739**0.5) / 7938810), + (310937500000000 / 2597974476091533 + 359369071093750 * (739**0.5) / 70145310854471391), + (310937500000000 / 2597974476091533 + 323282178906250 * (739**0.5) / 70145310854471391), + (5236016175 / 88357462711 + 44986764863 * (739**0.5) / 21205791050640), + (5236016175 / 88357462711 + 470139 * (739**0.5) / 1413719403376)], + ], + [ + + [ + 4563950663 / 16057595763, + 621875000000000 / 2597974476091533, + 621875000000000 / 2597974476091533, + 10472032350 / 88357462711, + 10472032350 / 88357462711] + ], + [ + 1 / 2, + 1 / 2 - 99 * (739**0.5) / 10000, # smallest # 0.06941899716778028758987101075583196 + 1 / 2 + 99 * (739**0.5) / 10000, # largest + 1 / 2 - (739**0.5) / 60, + 1 / 2 + (739**0.5) / 60 + ] + ), + + "gauss-legendre_5s_ascending": ( + [ + [(4563950663 / 32115191526 - 38339103 * (739**0.5) / 6250000000), + (310937500000000 / 2597974476091533 + 9557056475401 * (739**0.5) / 3498955523355600000), + (310937500000000 / 2597974476091533 - 14074198220719489 * (739**0.5) / 3498955523355600000), + (5236016175 / 88357462711 + 5601362553163918341 * (739**0.5) / 2208936567775000000000), + (5236016175 / 88357462711 - 5040458465159165409 * (739**0.5) / 2208936567775000000000)], + + + [(4563950663 / 32115191526 - 38209 * (739**0.5) / 7938810), + (310937500000000 / 2597974476091533 - 359369071093750 * (739**0.5) / 70145310854471391), + (310937500000000 / 2597974476091533 - 323282178906250 * (739**0.5) / 70145310854471391), + (5236016175 / 88357462711 - 470139 * (739**0.5) / 1413719403376), + (5236016175 / 88357462711 - 44986764863 * (739**0.5) / 21205791050640)], + + [4563950663 / 32115191526, + (310937500000000 / 2597974476091533 + 45156250000 * (739**0.5) / 8747388808389), + (310937500000000 / 2597974476091533 - 45156250000 * (739**0.5) / 8747388808389), + (5236016175 / 88357462711 + 709703235 * (739**0.5) / 353429850844), + (5236016175 / 88357462711 - 709703235 * (739**0.5) / 353429850844)], + + + [(4563950663 / 32115191526 + 38209 * (739**0.5) / 7938810), + (310937500000000 / 2597974476091533 + 359369071093750 * (739**0.5) / 70145310854471391), + (310937500000000 / 2597974476091533 + 323282178906250 * (739**0.5) / 70145310854471391), + (5236016175 / 88357462711 + 44986764863 * (739**0.5) / 21205791050640), + (5236016175 / 88357462711 + 470139 * (739**0.5) / 1413719403376)], + + + [(4563950663 / 32115191526 + 38339103 * (739**0.5) / 6250000000), + (310937500000000 / 2597974476091533 + 14074198220719489 * (739**0.5) / 3498955523355600000), + (310937500000000 / 2597974476091533 - 9557056475401 * (739**0.5) / 3498955523355600000), + (5236016175 / 88357462711 + 5040458465159165409 * (739**0.5) / 2208936567775000000000), + (5236016175 / 88357462711 - 5601362553163918341 * (739**0.5) / 2208936567775000000000)], + ], + [ + + [621875000000000 / 2597974476091533, + 10472032350 / 88357462711, + + 4563950663 / 16057595763, + + 10472032350 / 88357462711, + 621875000000000 / 2597974476091533,] + ], + [ + 1 / 2 - 99 * (739**0.5) / 10000, # smallest # 0.06941899716778028758987101075583196 + 1 / 2 - (739**0.5) / 60, + 1 / 2, + + + 1 / 2 + (739**0.5) / 60, + + 1 / 2 + 99 * (739**0.5) / 10000, # largest + ] + ), + "gauss-legendre_4s_alt": ( # https://ijstre.com/Publish/072016/371428231.pdf Four Point Gauss Quadrature Runge – Kuta Method Of Order 8 For Ordinary Differential Equations + [ + [1633/18780 - 71*206**0.5/96717000, + 134689/939000 - 927*206**0.5/78250, + 171511/939000 - 927*206**0.5/78250, + 1633/18780 - 121979*206**0.5/19343400,], + [7623/78250 - 1629507*206**0.5/257912000, + 347013/21284000, + -118701/4256800, + 7623/78250 + 1629507*206**0.5/257912000,], + [8978/117375 + 1629507*206**0.5/257912000, + 4520423/12770400, + 10410661/63852000, + 8978/117375 + 1629507*206**0.5/257912000,], + [1633/18780 + 121979*206**0.5/19343400, + 134689/939000 + 927*206**0.5/78250, + 171511/939000 + 927*206**0.5/78250, + 1633/18780 + 71*206**0.5/96717000,], + ], + [ + [1633/9390, + 1531/4695, + 1531/4695, + 1633/9390,] + ], + [ + 1/2 - 3*206**0.5 / 100, # 0.06941899716778028758987101075583196 + 33/100, + 67/100, + 1/2 + 3*206**0.5 / 100, + ] + ), + "gauss-legendre_4s": ( + [ + [1/4, 1/4 - 15**0.5 / 6, 1/4 + 15**0.5 / 6, 1/4], + [1/4 + 15**0.5 / 6, 1/4, 1/4 - 15**0.5 / 6, 1/4], + [1/4, 1/4 + 15**0.5 / 6, 1/4, 1/4 - 15**0.5 / 6], + [1/4 - 15**0.5 / 6, 1/4, 1/4 + 15**0.5 / 6, 1/4], + ], + [ + [ + 1/8, + 3/8, + 3/8, + 1/8,] + ], + [ + 1/2 - 15**0.5 / 10, # 0.11270166537925831148207346002176004 + 1/2 + 15**0.5 / 10, + 1/2 + 15**0.5 / 10, + 1/2 - 15**0.5 / 10 + ] + ), + "gauss-legendre_4s_alternating_a": ( + [ + [1/4, 1/4 - 15**0.5 / 6, 1/4 + 15**0.5 / 6, 1/4], + [1/4 + 15**0.5 / 6, 1/4, 1/4 - 15**0.5 / 6, 1/4], + [1/4 - 15**0.5 / 6, 1/4, 1/4 + 15**0.5 / 6, 1/4], + [1/4, 1/4 + 15**0.5 / 6, 1/4, 1/4 - 15**0.5 / 6], + ], + [ + [ + 1/8, + 3/8, + 1/8, + 3/8,] + ], + [ + 1/2 - 15**0.5 / 10, # 0.11270166537925831148207346002176004 + 1/2 + 15**0.5 / 10, + 1/2 - 15**0.5 / 10, + 1/2 + 15**0.5 / 10, + ] + ), + "gauss-legendre_4s_ascending_a": ( + [ + [1/4 - 15**0.5 / 6, 1/4, 1/4 + 15**0.5 / 6, 1/4], + [1/4, 1/4 - 15**0.5 / 6, 1/4 + 15**0.5 / 6, 1/4], + [1/4, 1/4 + 15**0.5 / 6, 1/4, 1/4 - 15**0.5 / 6], + [1/4 + 15**0.5 / 6, 1/4, 1/4 - 15**0.5 / 6, 1/4], + + ], + [ + [ + 1/8, + 3/8, + 1/8, + 3/8,] + ], + [ + 1/2 - 15**0.5 / 10, + 1/2 - 15**0.5 / 10, + 1/2 + 15**0.5 / 10, + 1/2 + 15**0.5 / 10, + ] + ), + + "gauss-legendre_3s": ( # Kunzmann-Butcher, IRK, order 6 https://www.math.umd.edu/~mariakc/SymplecticMethods.pdf + [ + [5/36, 2/9 - 15**0.5 / 15, 5/36 - 15**0.5 / 30], + [5/36 + 15**0.5 / 24, 2/9, 5/36 - 15**0.5 / 24], + [5/36 + 15**0.5 / 30, 2/9 + 15**0.5 / 15, 5/36], + ], + [ + [5/18, 4/9, 5/18] + ], + [1/2 - 15**0.5 / 10, 1/2, 1/2 + 15**0.5 / 10] # 0.11270166537925831148207346002176004 + ), + "gauss-legendre_2s": ( # Hammer-Hollingsworth, IRK, order 4 https://www.math.umd.edu/~mariakc/SymplecticMethods.pdf + [ + [1/4, 1/4 - 3**0.5 / 6], + [1/4 + 3**0.5 / 6, 1/4], + ], + [ + [1/2, 1/2], + ], + [1/2 - 3**0.5 / 6, 1/2 + 3**0.5 / 6] # 0.21132486540518711774542560974902127 # 1/2 - (1/2)*(1/3**0.5) 1/2 + (1/2)*(1/3**0.5) + ), + + "radau_iia_4s": ( + [ + [], + [], + [], + [], + ], + [ + [1/4, 1/4, 1/4, 1/4], + ], + [(1/11)*(4-6**0.5), (1/11)*(4+6**0.5), 1/2, 1] + ), + + "radau_iia_11s": ( # https://github.com/ryanelandt/Radau.jl + [ + [0.015280520789530369, -0.0057824996781311875, 0.00438010324638053, -0.0036210375473319026, 0.003092977042211754, -0.0026728314041491816, 0.0023050911672361017, -0.001955651803123845, 0.001593873849612843, -0.0011728625554916522, 0.00046993032567176855], + [0.03288397668119629, 0.03451351173940448, -0.009285420023734383, 0.00641324617083941, -0.005095455838865143, 0.0042460913690415955, -0.0035876743372353984, 0.003006834900018004, -0.0024326697483255453, 0.0017827773828584467, -0.0007131464180496306], + [0.029332502147155125, 0.0741624250777296, 0.0511486756872502, -0.012005023334430185, 0.00777794727524923, -0.005944695307870806, 0.004802655736401176, -0.003923600687657003, 0.003127328539609814, -0.0022731432208609507, 0.0009063777304940358], + [0.03111455337650569, 0.06578995121943092, 0.10929962691877611, 0.06381051663919307, -0.013853591907177828, 0.008557435524870741, -0.0063076358492939275, 0.004913357548166058, -0.0038139969541068734, 0.0027334306074068546, -0.0010839711153145738], + [0.03005269275666326, 0.07011284530154153, 0.09714692306747527, 0.1353916024839275, 0.07147107644479529, -0.014710238851905252, 0.008733191499420551, -0.00619941303527863, 0.004591640852897801, -0.003213330884490774, 0.001262857250740274], + [0.030728073929609766, 0.06751925856657341, 0.10334060375222286, 0.12083525997663601, 0.1503267876654705, 0.07350931976920085, -0.014512880052768446, 0.008296645645701008, -0.0056128275038367864, 0.003766229774466616, -0.001457705807615146], + [0.030292022376401242, 0.06914472100762357, 0.09972096441656238, 0.12801064060853223, 0.13493180383303127, 0.15289670039157693, 0.06975993047996924, -0.013274545709987746, 0.007258767272883859, -0.0044843888202694155, 0.0016878458203415244], + [0.03056654381836576, 0.06813851028407998, 0.10188107030389015, 0.12403361149690655, 0.14211431622263265, 0.13829395377418516, 0.14289135336320447, 0.06052636121446275, -0.011077739682117822, 0.005598667203856668, -0.0019877269625674446], + [0.030406629901865028, 0.06871880785022819, 0.10066095698900927, 0.12619527453091425, 0.13848875677027936, 0.14450773783254642, 0.13065188915037962, 0.1211140113707743, 0.046555483263607714, -0.008026200095719123, 0.002437640226261747], + [0.030484119381553945, 0.06843924691254653, 0.10124184869598654, 0.1251873187759311, 0.14011843430039864, 0.14190386755377057, 0.13500342651951197, 0.11262869537051934, 0.08930604389562254, 0.028969664972192485, -0.0033116985395201413], + [0.03046254890606557, 0.06851684106660112, 0.10108155427001221, 0.1254626888485642, 0.13968066655169153, 0.14258278197050367, 0.1339335430948421, 0.11443306192448831, 0.08565880960332992, 0.04992304095398403, 0.008264462809917356], + ], + [ + [0.03046254890606557, 0.06851684106660112, 0.10108155427001221, 0.1254626888485642, 0.13968066655169153, 0.14258278197050367, 0.1339335430948421, 0.11443306192448831, 0.08565880960332992, 0.04992304095398403, 0.008264462809917356], + ], + [0.011917613432415597, 0.061732071877148124, 0.14711144964307024, 0.26115967600845624, 0.39463984688578685, 0.5367387657156606, 0.6759444616766651, 0.8009789210368988, 0.9017109877901468, 0.9699709678385136, 1.0] + ), + + "radau_iia_9s": ( # https://github.com/ryanelandt/Radau.jl + [ + [0.022788378793458776, -0.008589639752938945, 0.0064510291769951465, -0.00525752869975012, 0.004388833809361376, -0.0036512155536904674, 0.0029404882137526148, -0.002149274163882554, 0.0008588433240576261], + [0.04890795244749932, 0.05070205048082808, -0.013523807196021316, 0.009209373774305071, -0.0071557133175369604, 0.005747246699432309, -0.004542582976394536, 0.003288161681791406, -0.0013090736941094112], + [0.04374276009157137, 0.10830189290274023, 0.07291956593742897, -0.016879877210016055, 0.010704551844802781, -0.007901946479238777, 0.005991406942179993, -0.0042480244399873135, 0.0016781498061495626], + [0.04624923745394712, 0.09656073072680009, 0.1542987697900386, 0.0867193693031384, -0.018451639643617873, 0.011036658729835513, -0.007673280940281649, 0.005228224999889903, -0.00203590583647778], + [0.044834436586910234, 0.10230684968594175, 0.13821763419236816, 0.18126393468214014, 0.09043360059943564, -0.018085063366782478, 0.010193387903855565, -0.006405265418866323, 0.0024271699384239612], + [0.045658755719323395, 0.09914547048938806, 0.14574704049699233, 0.16364828123387398, 0.18594458734451902, 0.08361326023153276, -0.015809936146309538, 0.00813825269404473, -0.002910469207795258], + [0.045200600187797244, 0.10085370671832047, 0.1419422367945749, 0.17118947183876332, 0.1697833861700019, 0.16776829117327952, 0.06707903432249304, -0.011792230536025322, 0.0036092462886493657], + [0.045416516657427734, 0.10006040244594375, 0.143652840987038, 0.16801908098069296, 0.17556076841841367, 0.15588627045003361, 0.12889391351650395, 0.04281082602522101, -0.004934574771244536], + [0.04535725246164146, 0.10027664901227598, 0.1431933481786156, 0.16884698348796479, 0.1741365013864833, 0.158421887835219, 0.12359468910229653, 0.0738270095231577, 0.012345679012345678], + ], + [ + [0.04535725246164146, 0.10027664901227598, 0.1431933481786156, 0.16884698348796479, 0.1741365013864833, 0.158421887835219, 0.12359468910229653, 0.0738270095231577, 0.012345679012345678], + ], + [0.01777991514736345, 0.09132360789979396, 0.21430847939563075, 0.37193216458327233, 0.5451866848034267, 0.7131752428555694, 0.8556337429578544, 0.9553660447100302, 1.0] + ), + + "radau_iia_7s": ( # https://github.com/ryanelandt/Radau.jl + [ + [0.03754626499392133, -0.0140393345564604, 0.0103527896007423, -0.008158322540275011, 0.006388413879534685, -0.004602326779148656, 0.0018289425614706437], + [0.08014759651561897, 0.08106206398589154, -0.021237992120711036, 0.014000291238817119, -0.010234185730090163, 0.0071534651513645905, -0.0028126393724067235], + [0.0720638469418819, 0.17106835498388662, 0.10961456404007211, -0.024619871728984055, 0.014760377043950817, -0.009575259396791401, 0.0036726783971383057], + [0.07570512581982441, 0.15409015514217114, 0.2271077366732024, 0.11747818703702478, -0.023810827153044174, 0.012709985533661206, -0.004608844281289633], + [0.07391234216319184, 0.16135560761594242, 0.2068672415521042, 0.23700711534269422, 0.10308679353381345, -0.018854139152580447, 0.0058589009748887914], + [0.07470556205979623, 0.1583072238724687, 0.21415342326720002, 0.21987784703186003, 0.19875212168063527, 0.06926550160550914, -0.00811600819772829], + [0.07449423555601031, 0.15910211573365074, 0.21235188950297781, 0.22355491450728324, 0.19047493682211558, 0.1196137446126562, 0.02040816326530612], + ], + [ + [0.07449423555601031, 0.15910211573365074, 0.21235188950297781, 0.22355491450728324, 0.19047493682211558, 0.1196137446126562, 0.02040816326530612], + ], + [0.029316427159784893, 0.1480785996684843, 0.3369846902811543, 0.5586715187715501, 0.7692338620300545, 0.9269456713197411, 1.0] + ), + + "radau_iia_5s": ( # https://github.com/ryanelandt/Radau.jl + [ + [0.07299886431790333, -0.02673533110794557, 0.018676929763984353, -0.01287910609330644, 0.005042839233882015], + [0.15377523147918246, 0.14621486784749352, -0.03644456890512809, 0.02123306311930472, -0.007935579902728777], + [0.14006304568480987, 0.29896712949128346, 0.16758507013524895, -0.03396910168661774, 0.010944288744192253], + [0.14489430810953477, 0.2765000687601592, 0.32579792291042103, 0.12875675325490976, -0.015708917378805327], + [0.14371356079122594, 0.28135601514946207, 0.31182652297574126, 0.22310390108357075, 0.04], + ], + [ + [0.14371356079122594, 0.28135601514946207, 0.31182652297574126, 0.22310390108357075, 0.04], + ], + [0.05710419611451768, 0.2768430136381238, 0.5835904323689168, 0.8602401356562195, 1.0] + ), + "radau_iia_3s": ( + [ + [11/45 - 7*6**0.5 / 360, 37/225 - 169*6**0.5 / 1800, -2/225 + 6**0.5 / 75], + [37/225 + 169*6**0.5 / 1800, 11/45 + 7*6**0.5 / 360, -2/225 - 6**0.5 / 75], + [4/9 - 6**0.5 / 36, 4/9 + 6**0.5 / 36, 1/9], + ], + [ + [4/9 - 6**0.5 / 36, 4/9 + 6**0.5 / 36, 1/9], + ], + [2/5 - 6**0.5 / 10, 2/5 + 6**0.5 / 10, 1.] + ), + "radau_iia_3s_alt": ( # https://www.unige.ch/~hairer/preprints/coimbra.pdf (page 7) Ehle [Eh69] and Axelsson [Ax69] + [ + [(88 - 7*6**0.5) / 360, (296 - 169*6**0.5) / 1800, (-2 + 3 * 6**0.5) / 225], + [(296 + 169*6**0.5) / 1800, (88 + 7*6**0.5) / 360, (-2 - 3*6**0.5) / 225], + [(16 - 6**0.5) / 36, (16 + 6**0.5) / 36, 1/9], + ], + [ + [ + (16 - 6**0.5) / 36, + (16 + 6**0.5) / 36, + 1/9], + ], + [ + (4 - 6**0.5) / 10, + (4 + 6**0.5) / 10, + 1.] + ), + "radau_iia_2s": ( + [ + [5/12, -1/12], + [3/4, 1/4], + ], + [ + [3/4, 1/4], + ], + [1/3, 1] + ), + "radau_ia_3s": ( + [ + [1/9, (-1-6**0.5)/18, (-1+6**0.5)/18], + [1/9, 11/45 + 7*6**0.5/360, 11/45-43*6**0.5/360], + [1/9, 11/45-43*6**0.5/360, 11/45 + 7*6**0.5/360], + ], + [ + [1/9, 4/9 + 6**0.5/36, 4/9 - 6**0.5/36], + ], + [0, 3/5-6**0.5/10, 3/5+6**0.5/10] + ), + "radau_ia_2s": ( + [ + [1/4, -1/4], + [1/4, 5/12], + ], + [ + [1/4, 3/4], + ], + [0, 2/3] + ), + "lobatto_iiia_4s": ( #6th order + [ + [0, 0, 0, 0], + [(11+5**0.5)/120, (25-5**0.5)/120, (25-13*5**0.5)/120, (-1+5**0.5)/120], + [(11-5**0.5)/120, (25+13*5**0.5)/120, (25+5**0.5)/120, (-1-5**0.5)/120], + [1/12, 5/12, 5/12, 1/12], + ], + [ + [1/12, 5/12, 5/12, 1/12], + ], + [0, (5-5**0.5)/10, (5+5**0.5)/10, 1] + ), + "lobatto_iiib_4s": ( #6th order + [ + [1/12, (-1-5**0.5)/24, (-1+5**0.5)/24, 0], + [1/12, (25+5**0.5)/120, (25-13*5**0.5)/120, 0], + [1/12, (25+13*5**0.5)/120, (25-5**0.5)/120, 0], + [1/12, (11-5**0.5)/24, (11+5**0.5)/24, 0], + ], + [ + [1/12, 5/12, 5/12, 1/12], + ], + [0, (5-5**0.5)/10, (5+5**0.5)/10, 1] + ), + "lobatto_iiic_4s": ( #6th order + [ + [1/12, (-5**0.5)/12, (5**0.5)/12, -1/12], + [1/12, 1/4, (10-7*5**0.5)/60, (5**0.5)/60], + [1/12, (10+7*5**0.5)/60, 1/4, (-5**0.5)/60], + [1/12, 5/12, 5/12, 1/12], + ], + [ + [1/12, 5/12, 5/12, 1/12], + ], + [0, (5-5**0.5)/10, (5+5**0.5)/10, 1] + ), + "lobatto_iiia_3s": ( + [ + [0, 0, 0], + [5/24, 1/3, -1/24], + [1/6, 2/3, 1/6], + ], + [ + [1/6, 2/3, 1/6], + ], + [0, 1/2, 1] + ), + "lobatto_iiia_2s": ( + [ + [0, 0], + [1/2, 1/2], + ], + [ + [1/2, 1/2], + ], + [0, 1] + ), + + + + "lobatto_iiib_3s": ( + [ + [1/6, -1/6, 0], + [1/6, 1/3, 0], + [1/6, 5/6, 0], + ], + [ + [1/6, 2/3, 1/6], + ], + [0, 1/2, 1] + ), + "lobatto_iiib_2s": ( + [ + [1/2, 0], + [1/2, 0], + ], + [ + [1/2, 1/2], + ], + [0, 1] + ), + + "lobatto_iiic_3s": ( + [ + [1/6, -1/3, 1/6], + [1/6, 5/12, -1/12], + [1/6, 2/3, 1/6], + ], + [ + [1/6, 2/3, 1/6], + ], + [0, 1/2, 1] + ), + "lobatto_iiic_2s": ( + [ + [1/2, -1/2], + [1/2, 1/2], + ], + [ + [1/2, 1/2], + ], + [0, 1] + ), + + + "lobatto_iiic_star_3s": ( + [ + [0, 0, 0], + [1/4, 1/4, 0], + [0, 1, 0], + ], + [ + [1/6, 2/3, 1/6], + ], + [0, 1/2, 1] + ), + "lobatto_iiic_star_2s": ( + [ + [0, 0], + [1, 0], + ], + [ + [1/2, 1/2], + ], + [0, 1] + ), + + "lobatto_iiid_3s": ( + [ + [1/6, 0, -1/6], + [1/12, 5/12, 0], + [1/2, 1/3, 1/6], + ], + [ + [1/6, 2/3, 1/6], + ], + [0, 1/2, 1] + ), + "lobatto_iiid_2s": ( + [ + [1/2, 1/2], + [-1/2, 1/2], + ], + [ + [1/2, 1/2], + ], + [0, 1] + ), + + "kraaijevanger_spijker_2s": ( #overshoots step + [ + [1/2, 0], + [-1/2, 2], + ], + [ + [-1/2, 3/2], + ], + [1/2, 3/2] + ), + + "qin_zhang_2s": ( + [ + [1/4, 0], + [1/2, 1/4], + ], + [ + [1/2, 1/2], + ], + [1/4, 3/4] + ), + + "pareschi_russo_2s": ( + [ + [(1-2**0.5/2), 0], + [1-2*(1-2**0.5/2), (1-2**0.5/2)], + ], + [ + [1/2, 1/2], + ], + [(1-2**0.5/2), 1-(1-2**0.5/2)] + ), + + "pareschi_russo_alt_2s": ( + [ + [(1-2**0.5/2), 0], + [1-(1-2**0.5/2), (1-2**0.5/2)], + ], + [ + [1-(1-2**0.5/2), (1-2**0.5/2)], + ], + [(1-2**0.5/2), 1] + ), + + "crouzeix_3s_alt": ( # Crouzeix & Raviart 1980; A-stable; pg 100 in Solving Ordinary Differential Equations II + [ + [gamma_crouzeix, 0, 0], + [1/2 - gamma_crouzeix, gamma_crouzeix, 0], + [2*gamma_crouzeix, 1-4*gamma_crouzeix, gamma_crouzeix], + ], + [ + [delta_crouzeix, 1-2*delta_crouzeix, delta_crouzeix], + ], + [gamma_crouzeix, 1/2, 1-gamma_crouzeix], + ), + + "crouzeix_3s": ( + [ + [(1+alpha_crouzeix)/2, 0, 0], + [-alpha_crouzeix/2, (1+alpha_crouzeix)/2, 0], + [1+alpha_crouzeix, -(1+2*alpha_crouzeix), (1+alpha_crouzeix)/2], + ], + [ + [1/(6*alpha_crouzeix**2), 1-(1/(3*alpha_crouzeix**2)), 1/(6*alpha_crouzeix**2)], + ], + [(1+alpha_crouzeix)/2, 1/2, (1-alpha_crouzeix)/2], + ), + + "crouzeix_2s": ( + [ + [1/2 + 3**0.5 / 6, 0], + [-(3**0.5 / 3), 1/2 + 3**0.5 / 6] + ], + [ + [1/2, 1/2], + ], + [1/2 + 3**0.5 / 6, 1/2 - 3**0.5 / 6], + ), + "verner_13s": ( #verner9. some values are missing, need to revise + [ + [], + ], + [ + [], + ], + [ + 0.03462, + 0.09702435063878045, + 0.14553652595817068, + 0.561, + 0.22900791159048503, + 0.544992088409515, + 0.645, + 0.48375, + 0.06757, + 0.25, + 0.6590650618730999, + 0.8206, + 0.9012, + ] + ), + "verner_robust_16s": ( + [ + [], + [0.04], + [-0.01988527319182291, 0.11637263332969652], + [0.0361827600517026, 0, 0.10854828015510781], + [2.272114264290177, 0, -8.526886447976398, 6.830772183686221], + [0.050943855353893744, 0, 0, 0.1755865049809071, 0.007022961270757467], + [0.1424783668683285, 0, 0, -0.3541799434668684, 0.07595315450295101, 0.6765157656337123], + [0.07111111111111111, 0, 0, 0, 0, 0.3279909287605898, 0.24089796012829906], + [0.07125, 0, 0, 0, 0, 0.32688424515752457, 0.11561575484247544, -0.03375], + [0.0482267732246581, 0, 0, 0, 0, 0.039485599804954, 0.10588511619346581, -0.021520063204743093, -0.10453742601833482], + [-0.026091134357549235, 0, 0, 0, 0, 0.03333333333333333, -0.1652504006638105, 0.03434664118368617, 0.1595758283215209, 0.21408573218281934], + [-0.03628423396255658, 0, 0, 0, 0, -1.0961675974272087, 0.1826035504321331, 0.07082254444170683, -0.02313647018482431, 0.2711204726320933, 1.3081337494229808], + [-0.5074635056416975, 0, 0, 0, 0, -6.631342198657237, -0.2527480100908801, -0.49526123800360955, 0.2932525545253887, 1.440108693768281, 6.237934498647056, 0.7270192054526988], + [0.6130118256955932, 0, 0, 0, 0, 9.088803891640463, -0.40737881562934486, 1.7907333894903747, 0.714927166761755, -1.4385808578417227, -8.26332931206474, -1.537570570808865, 0.34538328275648716], + [-1.2116979103438739, 0, 0, 0, 0, -19.055818715595954, 1.263060675389875, -6.913916969178458, -0.6764622665094981, 3.367860445026608, 18.00675164312591, 6.83882892679428, -1.0315164519219504, 0.4129106232130623], + [2.1573890074940536, 0, 0, 0, 0, 23.807122198095804, 0.8862779249216555, 13.139130397598764, -2.604415709287715, -5.193859949783872, -20.412340711541507, -12.300856252505723, 1.5215530950085394], + ], + [ + 0.014588852784055396, 0, 0, 0, 0, 0, 0, 0.0020241978878893325, 0.21780470845697167, + 0.12748953408543898, 0.2244617745463132, 0.1787254491259903, 0.07594344758096556, + 0.12948458791975614, 0.029477447612619417, 0 + ], + [ + 0, 0.04, 0.09648736013787361, 0.1447310402068104, 0.576, 0.2272326564618766, + 0.5407673435381234, 0.64, 0.48, 0.06754, 0.25, 0.6770920153543243, 0.8115, + 0.906, 1, 1 + ], + ), + + "dormand-prince_13s": ( #non-monotonic + [ + [], + [1/18], + [1/48, 1/16], + [1/32, 0, 3/32], + [5/16, 0, -75/64, 75/64], + [3/80, 0, 0, 3/16, 3/20], + [29443841/614563906, 0, 0, 77736538/692538347, -28693883/1125000000, 23124283/1800000000], + [16016141/946692911, 0, 0, 61564180/158732637, 22789713/633445777, 545815736/2771057229, -180193667/1043307555], + [39632708/573591083, 0, 0, -433636366/683701615, -421739975/2616292301, 100302831/723423059, 790204164/839813087, 800635310/3783071287], + [246121993/1340847787, 0, 0, -37695042795/15268766246, -309121744/1061227803, -12992083/490766935, 6005943493/2108947869, 393006217/1396673457, 123872331/1001029789], + [-1028468189/846180014, 0, 0, 8478235783/508512852, 1311729495/1432422823, -10304129995/1701304382, -48777925059/3047939560, 15336726248/1032824649, -45442868181/3398467696, 3065993473/597172653], + [185892177/718116043, 0, 0, -3185094517/667107341, -477755414/1098053517, -703635378/230739211, 5731566787/1027545527, 5232866602/850066563, -4093664535/808688257, 3962137247/1805957418, 65686358/487910083], + [403863854/491063109, 0, 0, -5068492393/434740067, -411421997/543043805, 652783627/914296604, 11173962825/925320556, -13158990841/6184727034, 3936647629/1978049680, -160528059/685178525, 248638103/1413531060], + ], + [ + [14005451/335480064, 0, 0, 0, 0, -59238493/1068277825, 181606767/758867731, 561292985/797845732, -1041891430/1371343529, 760417239/1151165299, 118820643/751138087, -528747749/2220607170, 1/4], + ], + [0, 1/18, 1/12, 1/8, 5/16, 3/8, 59/400, 93/200, 5490023248 / 9719169821, 13/20, 1201146811 / 1299019798, 1, 1], + ), + "dormand-prince_6s": ( + [ + [], + [1/5], + [3/40, 9/40], + [44/45, -56/15, 32/9], + [19372/6561, -25360/2187, 64448/6561, -212/729], + [9017/3168, -355/33, 46732/5247, 49/176, -5103/18656], + ], + [ + [35/384, 0, 500/1113, 125/192, -2187/6784, 11/84], + ], + [0, 1/5, 3/10, 4/5, 8/9, 1], + ), + "bogacki-shampine_7s": ( #5th order + [ + [], + [1/6], + [2/27, 4/27], + [183/1372, -162/343, 1053/1372], + [68/297, -4/11, 42/143, 1960/3861], + [597/22528, 81/352, 63099/585728, 58653/366080, 4617/20480], + [174197/959244, -30942/79937, 8152137/19744439, 666106/1039181, -29421/29068, 482048/414219], + ], + [ + [587/8064, 0, 4440339/15491840, 24353/124800, 387/44800, 2152/5985, 7267/94080], + ], + [0, 1/6, 2/9, 3/7, 2/3, 3/4, 1] + ), + "bogacki-shampine_4s": ( #5th order + [ + [], + [1/2], + [0, 3/4], + [2/9, 1/3, 4/9], + ], + [ + [2/9, 1/3, 4/9, 0], + ], + [0, 1/2, 3/4, 1] + ), + "tsi_7s": ( #5th order + [ + [], + [0.161], + [-0.008480655492356989, 0.335480655492357], + [2.8971530571054935, -6.359448489975075, 4.3622954328695815], + [5.325864828439257, -11.748883564062828, 7.4955393428898365, -0.09249506636175525], + [5.86145544294642, -12.92096931784711, 8.159367898576159, -0.071584973281401, -0.02826905039406838], + [0.09646076681806523, 0.01, 0.4798896504144996, 1.379008574103742, -3.290069515436081, 2.324710524099774], + ], + [ + [0.09646076681806523, 0.01, 0.4798896504144996, 1.379008574103742, -3.290069515436081, 2.324710524099774, 0.0], + ], + [0.0, 0.161, 0.327, 0.9, 0.9800255409045097, 1.0, 1.0], + ), + "rk6_7s": ( #non-monotonic #5th order + [ + [], + [1/3], + [0, 2/3], + [1/12, 1/3, -1/12], + [-1/16, 9/8, -3/16, -3/8], + [0, 9/8, -3/8, -3/4, 1/2], + [9/44, -9/11, 63/44, 18/11, 0, -16/11], + ], + [ + [11/120, 0, 27/40, 27/40, -4/15, -4/15, 11/120], + ], + [0, 1/3, 2/3, 1/3, 1/2, 1/2, 1], + ), + "rk5_7s": ( #5th order + [ + [], + [1/5], + [3/40, 9/40], + [44/45, -56/15, 32/9], + [19372/6561, -25360/2187, 64448/6561, 212/729], #flipped 212 sign + [-9017/3168, -355/33, 46732/5247, 49/176, -5103/18656], + [35/384, 0, 500/1113, 125/192, -2187/6784, 11/84], + ], + [ + [5179/57600, 0, 7571/16695, 393/640, -92097/339200, 187/2100, 1/40], + ], + [0, 1/5, 3/10, 4/5, 8/9, 1, 1], + ), + "ssprk4_4s": ( #non-monotonic #https://link.springer.com/article/10.1007/s41980-022-00731-x + [ + [], + [1/2], + [1/2, 1/2], + [1/6, 1/6, 1/6], + ], + [ + [1/6, 1/6, 1/6, 1/2], + ], + [0, 1/2, 1, 1/2], + ), + "rk4_4s": ( + [ + [], + [1/2], + [0, 1/2], + [0, 0, 1], + ], + [ + [1/6, 1/3, 1/3, 1/6], + ], + [0, 1/2, 1/2, 1], + ), + "rk38_4s": ( + [ + [], + [1/3], + [-1/3, 1], + [1, -1, 1], + ], + [ + [1/8, 3/8, 3/8, 1/8], + ], + [0, 1/3, 2/3, 1], + ), + "ralston_4s": ( + [ + [], + [2/5], + [(-2889+1428 * 5**0.5)/1024, (3785-1620 * 5**0.5)/1024], + [(-3365+2094 * 5**0.5)/6040, (-975-3046 * 5**0.5)/2552, (467040+203968*5**0.5)/240845], + ], + [ + [(263+24*5**0.5)/1812, (125-1000*5**0.5)/3828, (3426304+1661952*5**0.5)/5924787, (30-4*5**0.5)/123], + ], + [0, 2/5, (14-3 * 5**0.5)/16, 1], + ), + "heun_3s": ( + [ + [], + [1/3], + [0, 2/3], + ], + [ + [1/4, 0, 3/4], + ], + [0, 1/3, 2/3], + ), + "kutta_3s": ( + [ + [], + [1/2], + [-1, 2], + ], + [ + [1/6, 2/3, 1/6], + ], + [0, 1/2, 1], + ), + "ralston_3s": ( + [ + [], + [1/2], + [0, 3/4], + ], + [ + [2/9, 1/3, 4/9], + ], + [0, 1/2, 3/4], + ), + "houwen-wray_3s": ( + [ + [], + [8/15], + [1/4, 5/12], + ], + [ + [1/4, 0, 3/4], + ], + [0, 8/15, 2/3], + ), + "ssprk3_3s": ( #non-monotonic + [ + [], + [1], + [1/4, 1/4], + ], + [ + [1/6, 1/6, 2/3], + ], + [0, 1, 1/2], + ), + "midpoint_2s": ( + [ + [], + [1/2], + ], + [ + [0, 1], + ], + [0, 1/2], + ), + "heun_2s": ( + [ + [], + [1], + ], + [ + [1/2, 1/2], + ], + [0, 1], + ), + "ralston_2s": ( + [ + [], + [2/3], + ], + [ + [1/4, 3/4], + ], + [0, 2/3], + ), + "euler": ( + [ + [], + ], + [ + [1], + ], + [0], + ), +} + + + +def get_rk_methods_beta(rk_type : str, + h : Tensor, + c1 : float = 0.0, + c2 : float = 0.5, + c3 : float = 1.0, + h_prev : Optional[Tensor] = None, + step : int = 0, + sigmas : Optional[Tensor] = None, + sigma : Optional[Tensor] = None, + sigma_next : Optional[Tensor] = None, + sigma_down : Optional[Tensor] = None, + extra_options : Optional[str] = None + ): + + FSAL = False + multistep_stages = 0 + hybrid_stages = 0 + u = None + v = None + + EO = ExtraOptions(extra_options) + use_analytic_solution = not EO("disable_analytic_solution") + multistep_initial_sampler = EO("multistep_initial_sampler", "", debugMode=1) + multistep_fallback_sampler = EO("multistep_fallback_sampler", "") + multistep_extra_initial_steps = EO("multistep_extra_initial_steps", 1) + + #if RK_Method_Beta.is_exponential(rk_type): + if rk_type.startswith(("res", "dpmpp", "ddim", "pec", "etdrk", "lawson")): + h_no_eta = -torch.log(sigma_next/sigma) + h_prev1_no_eta = -torch.log(sigmas[step]/sigmas[step-1]) if step >= 1 else None + h_prev2_no_eta = -torch.log(sigmas[step]/sigmas[step-2]) if step >= 2 else None + h_prev3_no_eta = -torch.log(sigmas[step]/sigmas[step-3]) if step >= 3 else None + h_prev4_no_eta = -torch.log(sigmas[step]/sigmas[step-4]) if step >= 4 else None + + else: + h_no_eta = sigma_next - sigma + h_prev1_no_eta = sigmas[step] - sigmas[step-1] if step >= 1 else None + h_prev2_no_eta = sigmas[step] - sigmas[step-2] if step >= 2 else None + h_prev3_no_eta = sigmas[step] - sigmas[step-3] if step >= 3 else None + h_prev4_no_eta = sigmas[step] - sigmas[step-4] if step >= 4 else None + + if type(c1) == torch.Tensor: + c1 = c1.item() + if type(c2) == torch.Tensor: + c2 = c2.item() + if type(c3) == torch.Tensor: + c3 = c3.item() + + if c1 == -1: + c1 = random.uniform(0, 1) + if c2 == -1: + c2 = random.uniform(0, 1) + if c3 == -1: + c3 = random.uniform(0, 1) + + if rk_type[:4] == "deis": + order = int(rk_type[-2]) + if step < order + multistep_extra_initial_steps: + if order == 4: + #rk_type = "res_4s_strehmel_weiner" + rk_type = "ralston_4s" + rk_type = multistep_initial_sampler if multistep_initial_sampler else rk_type + order = 3 + elif order == 3: + #rk_type = "res_3s" + rk_type = "ralston_3s" + rk_type = multistep_initial_sampler if multistep_initial_sampler else rk_type + elif order == 2: + #rk_type = "res_2s" + rk_type = "ralston_2s" + rk_type = multistep_initial_sampler if multistep_initial_sampler else rk_type + else: + rk_type = "deis" + multistep_stages = order-1 + + if rk_type[-2:] == "2m": #multistep method + rk_type = rk_type[:-2] + "2s" + #if h_prev is not None and step >= 1: + if h_no_eta < 1.0: + if step >= 1 + multistep_extra_initial_steps: + multistep_stages = 1 + c2 = (-h_prev1_no_eta / h_no_eta).item() + else: + rk_type = multistep_initial_sampler if multistep_initial_sampler else rk_type + if rk_type.startswith("abnorsett"): + rk_type = "res_2s" + rk_type = multistep_initial_sampler if multistep_initial_sampler else rk_type + else: + #rk_type = "res_2s" + rk_type = "euler" if sigma < 0.1 else "res_2s" + rk_type = multistep_fallback_sampler if multistep_fallback_sampler else rk_type + + if rk_type[-2:] == "3m": #multistep method + rk_type = rk_type[:-2] + "3s" + #if h_prev2 is not None and step >= 2: + if h_no_eta < 1.0: + if step >= 2 + multistep_extra_initial_steps: + multistep_stages = 2 + + c2 = (-h_prev1_no_eta / h_no_eta).item() + c3 = (-h_prev2_no_eta / h_no_eta).item() + else: + rk_type = multistep_initial_sampler if multistep_initial_sampler else rk_type + if rk_type.startswith("abnorsett"): + rk_type = "res_3s" + rk_type = multistep_initial_sampler if multistep_initial_sampler else rk_type + else: + #rk_type = "res_3s" + rk_type = "euler" if sigma < 0.1 else "res_3s" + rk_type = multistep_fallback_sampler if multistep_fallback_sampler else rk_type + + if rk_type[-2:] == "4m": #multistep method + rk_type = rk_type[:-2] + "4s" + #if h_prev2 is not None and step >= 2: + if h_no_eta < 1.0: + if step >= 3 + multistep_extra_initial_steps: + multistep_stages = 3 + + c2 = (-h_prev1_no_eta / h_no_eta).item() + c3 = (-h_prev2_no_eta / h_no_eta).item() + # WOULD NEED A C4 (POW) TO IMPLEMENT RES_4M IF IT EXISTED + else: + rk_type = multistep_initial_sampler if multistep_initial_sampler else rk_type + if rk_type == "res_4s": + rk_type = "res_4s_strehmel_weiner" + rk_type = multistep_initial_sampler if multistep_initial_sampler else rk_type + if rk_type.startswith("abnorsett"): + rk_type = "res_4s_strehmel_weiner" + rk_type = multistep_initial_sampler if multistep_initial_sampler else rk_type + else: + #rk_type = "res_4s_strehmel_weiner" + rk_type = "euler" if sigma < 0.1 else "res_4s_strehmel_weiner" + rk_type = multistep_fallback_sampler if multistep_fallback_sampler else rk_type + + if rk_type[-3] == "h" and rk_type[-1] == "s": #hybrid method + if step < int(rk_type[-4]) + multistep_extra_initial_steps: + rk_type = "res_" + rk_type[-2:] + rk_type = multistep_initial_sampler if multistep_initial_sampler else rk_type + else: + hybrid_stages = int(rk_type[-4]) #+1 adjustment needed? + if rk_type == "res_4s": + rk_type = "res_4s_strehmel_weiner" + rk_type = multistep_initial_sampler if multistep_initial_sampler else rk_type + if rk_type == "res_1s": + rk_type = "res_2s" + rk_type = multistep_initial_sampler if multistep_initial_sampler else rk_type + + if rk_type in rk_coeff: + a, b, ci = copy.deepcopy(rk_coeff[rk_type]) + + a = [row + [0] * (len(ci) - len(row)) for row in a] + + match rk_type: + case "deis": + coeff_list = get_deis_coeff_list(sigmas, multistep_stages+1, deis_mode="rhoab") + coeff_list = [[elem / h for elem in inner_list] for inner_list in coeff_list] + if multistep_stages == 1: + b1, b2 = coeff_list[step] + a = [ + [0, 0], + [0, 0], + ] + b = [ + [b1, b2], + ] + ci = [0, 0] + if multistep_stages == 2: + b1, b2, b3 = coeff_list[step] + a = [ + [0, 0, 0], + [0, 0, 0], + [0, 0, 0], + ] + b = [ + [b1, b2, b3], + ] + ci = [0, 0, 0] + if multistep_stages == 3: + b1, b2, b3, b4 = coeff_list[step] + a = [ + [0, 0, 0, 0], + [0, 0, 0, 0], + [0, 0, 0, 0], + [0, 0, 0, 0], + ] + b = [ + [b1, b2, b3, b4], + ] + ci = [0, 0, 0, 0] + if multistep_stages > 0: + for i in range(len(b[0])): + b[0][i] *= ((sigma_down - sigma) / (sigma_next - sigma)) + + case "dormand-prince_6s": + FSAL = True + + case "ddim": + b1 = phi(1, -h) + a = [ + [0], + ] + b = [ + [b1], + ] + ci = [0] + + case "res_2s": + c2 = float(get_extra_options_kv("c2", str(c2), extra_options)) + + ci = [0, c2] + φ = Phi(h, ci, use_analytic_solution) + + a2_1 = c2 * φ(1,2) + b2 = φ(2)/c2 + b1 = φ(1) - b2 + + a = [ + [0,0], + [a2_1, 0], + ] + b = [ + [b1, b2], + ] + + case "res_2s_stable": + c2 = 1.0 #float(get_extra_options_kv("c2", str(c2), extra_options)) + + ci = [0, c2] + φ = Phi(h, ci, use_analytic_solution) + + a2_1 = c2 * φ(1,2) + b2 = φ(2)/c2 + b1 = φ(1) - b2 + + a = [ + [0,0], + [a2_1, 0], + ] + b = [ + [b1, b2], + ] + + case "res_2s_rkmk2e": + + ci = [0, 1] + φ = Phi(h, ci, use_analytic_solution) + + b2 = φ(2) + + a = [ + [0,0], + [0, 0], + ] + b = [ + [0, b2], + ] + + gen_first_col_exp(a, b, ci, φ) + + + + case "abnorsett2_1h2s": + + c1, c2 = 0, 1 + ci = [c1, c2] + φ = Phi(h, ci, use_analytic_solution) + + b1 = φ(1) #+ φ(2) + + a = [ + [0, 0], + [0, 0], + ] + b = [ + [0, 0], + ] + + if extra_options_flag("h_prev_h_h_no_eta", extra_options): + φ1 = Phi(h_prev1_no_eta * h/h_no_eta, ci) + elif extra_options_flag("h_only", extra_options): + φ1 = Phi(h, ci, use_analytic_solution) + else: + φ1 = Phi(h_prev1_no_eta, ci) + + u1 = -φ1(2) + v1 = -φ1(2) + + u = [ + [0, 0], + [u1, 0], + ] + v = [ + [v1, 0], + ] + + gen_first_col_exp_uv(a, b, ci, u, v, φ) + + + + case "abnorsett_2m": + + c1, c2 = 0, 1 + ci = [c1, c2] + φ = Phi(h, ci, use_analytic_solution) + + a = [ + [0, 0], + [0, 0], + ] + b = [ + [0, -φ(2)], + ] + + gen_first_col_exp(a, b, ci, φ) + + + case "abnorsett_3m": + + c1, c2, c3 = 0, 0, 1 + ci = [c1, c2, c3] + φ = Phi(h, ci, use_analytic_solution) + + a = [ + [0, 0, 0], + [0, 0, 0], + [0, 0, 0], + ] + b = [ + [0, -2*φ(2) - 2*φ(3), (1/2)*φ(2) + φ(3)], + ] + + gen_first_col_exp(a, b, ci, φ) + + + + case "abnorsett_4m": + + c1, c2, c3, c4 = 0, 0, 0, 1 + ci = [c1, c2, c3, c4] + φ = Phi(h, ci, use_analytic_solution) + + a = [ + [0, 0, 0, 0], + [0, 0, 0, 0], + [0, 0, 0, 0], + [0, 0, 0, 0], + ] + b = [ + [0, + -3*φ(2) - 5*φ(3) - 3*φ(4), + (3/2)*φ(2) + 4*φ(3) + 3*φ(4), + (-1/3)*φ(2) - φ(3) - φ(4), + ], + ] + + gen_first_col_exp(a, b, ci, φ) + + + case "abnorsett3_2h2s": + + c1,c2 = 0,1 + ci = [c1, c2] + φ = Phi(h, ci, use_analytic_solution) + + b2 = 0 + + a = [ + [0, 0], + [0, 0], + ] + b = [ + [0, 0], + ] + + if extra_options_flag("h_prev_h_h_no_eta", extra_options): + φ1 = Phi(h_prev1_no_eta * h/h_no_eta, ci) + φ2 = Phi(h_prev2_no_eta * h/h_no_eta, ci) + elif extra_options_flag("h_only", extra_options): + φ1 = Phi(h, ci, use_analytic_solution) + φ2 = Phi(h, ci, use_analytic_solution) + else: + φ1 = Phi(h_prev1_no_eta, ci) + φ2 = Phi(h_prev2_no_eta, ci) + + u2_1 = -2*φ1(2) - 2*φ1(3) + u2_2 = (1/2)*φ2(2) + φ2(3) + + v1 = u2_1 # -φ1(2) + φ1(3) + 3*φ1(4) + v2 = u2_2 # (1/6)*φ2(2) - φ2(4) + + u = [ + [ 0, 0], + [u2_1, u2_2], + ] + v = [ + [v1, v2], + ] + + gen_first_col_exp_uv(a, b, ci, u, v, φ) + + + + case "pec423_2h2s": #https://ora.ox.ac.uk/objects/uuid:cc001282-4285-4ca2-ad06-31787b540c61/files/m611df1a355ca243beb09824b70e5e774 + + c1,c2 = 0,1 + ci = [c1, c2] + φ = Phi(h, ci, use_analytic_solution) + + b2 = (1/3)*φ(2) + φ(3) + φ(4) + + a = [ + [0, 0], + [0, 0], + ] + b = [ + [0, b2], + ] + + if extra_options_flag("h_prev_h_h_no_eta", extra_options): + φ1 = Phi(h_prev1_no_eta * h/h_no_eta, ci) + φ2 = Phi(h_prev2_no_eta * h/h_no_eta, ci) + elif extra_options_flag("h_only", extra_options): + φ1 = Phi(h, ci, use_analytic_solution) + φ2 = Phi(h, ci, use_analytic_solution) + else: + φ1 = Phi(h_prev1_no_eta, ci) + φ2 = Phi(h_prev2_no_eta, ci) + + u2_1 = -2*φ1(2) - 2*φ1(3) + u2_2 = (1/2)*φ2(2) + φ2(3) + + v1 = -φ1(2) + φ1(3) + 3*φ1(4) + v2 = (1/6)*φ2(2) - φ2(4) + + u = [ + [ 0, 0], + [u2_1, u2_2], + ] + v = [ + [v1, v2], + ] + + gen_first_col_exp_uv(a, b, ci, u, v, φ) + + + + + case "pec433_2h3s": #https://ora.ox.ac.uk/objects/uuid:cc001282-4285-4ca2-ad06-31787b540c61/files/m611df1a355ca243beb09824b70e5e774 + + c1,c2,c3 = 0, 1, 1 + ci = [c1,c2,c3] + φ = Phi(h, ci, use_analytic_solution) + + a3_2 = (1/3)*φ(2) + φ(3) + φ(4) + + b2 = 0 + b3 = (1/3)*φ(2) + φ(3) + φ(4) + + a = [ + [0, 0, 0], + [0, 0, 0], + [0, a3_2, 0], + ] + b = [ + [0, b2, b3], + ] + + if extra_options_flag("h_prev_h_h_no_eta", extra_options): + φ1 = Phi(h_prev1_no_eta * h/h_no_eta, ci) + φ2 = Phi(h_prev2_no_eta * h/h_no_eta, ci) + elif extra_options_flag("h_only", extra_options): + φ1 = Phi(h, ci, use_analytic_solution) + φ2 = Phi(h, ci, use_analytic_solution) + else: + φ1 = Phi(h_prev1_no_eta, ci) + φ2 = Phi(h_prev2_no_eta, ci) + + u2_1 = -2*φ1(2) - 2*φ1(3) + u3_1 = -φ1(2) + φ1(3) + 3*φ1(4) + v1 = -φ1(2) + φ1(3) + 3*φ1(4) + + u2_2 = (1/2)*φ2(2) + φ2(3) + u3_2 = (1/6)*φ2(2) - φ2(4) + v2 = (1/6)*φ2(2) - φ2(4) + + + u = [ + [ 0, 0, 0], + [u2_1, u2_2, 0], + [u3_1, u3_2, 0], + ] + v = [ + [v1, v2, 0], + ] + + gen_first_col_exp_uv(a, b, ci, u, v, φ) + + + + case "res_3s": + c2 = float(get_extra_options_kv("c2", str(c2), extra_options)) + c3 = float(get_extra_options_kv("c3", str(c3), extra_options)) + + ci = [0,c2,c3] + φ = Phi(h, ci, use_analytic_solution) + + gamma = calculate_gamma(c2, c3) + + a3_2 = gamma * c2 * φ(2,2) + (c3 ** 2 / c2) * φ(2, 3) + + b3 = (1 / (gamma * c2 + c3)) * φ(2) + b2 = gamma * b3 #simplified version of: b2 = (gamma / (gamma * c2 + c3)) * phi_2_h + + a = [ + [0, 0, 0], + [0, 0, 0], + [0, a3_2, 0], + ] + b = [ + [0, b2, b3], + ] + + a, b = gen_first_col_exp(a,b,ci,φ) + + case "res_3s_non-monotonic": + c2 = float(get_extra_options_kv("c2", "1.0", extra_options)) + c3 = float(get_extra_options_kv("c3", "0.5", extra_options)) + + ci = [0,c2,c3] + φ = Phi(h, ci, use_analytic_solution) + + gamma = calculate_gamma(c2, c3) + + a3_2 = gamma * c2 * φ(2,2) + (c3 ** 2 / c2) * φ(2, 3) + + b3 = (1 / (gamma * c2 + c3)) * φ(2) + b2 = gamma * b3 #simplified version of: b2 = (gamma / (gamma * c2 + c3)) * phi_2_h + + a = [ + [0, 0, 0], + [0, 0, 0], + [0, a3_2, 0], + ] + b = [ + [0, b2, b3], + ] + + a, b = gen_first_col_exp(a,b,ci,φ) + + + case "res_3s_alt": + c2 = 1/3 + c2 = float(get_extra_options_kv("c2", str(c2), extra_options)) + + c1,c2,c3 = 0, c2, 2/3 + ci = [c1,c2,c3] + φ = Phi(h, ci, use_analytic_solution) + + a = [ + [0, 0, 0], + [0, 0, 0], + [0, (4/(9*c2)) * φ(2,3), 0], + ] + b = [ + [0, 0, (1/c3)*φ(2)], + ] + + a, b = gen_first_col_exp(a,b,ci,φ) + + case "res_3s_strehmel_weiner": # + c2 = 1/2 + c2 = float(get_extra_options_kv("c2", str(c2), extra_options)) + + ci = [0,c2,1] + φ = Phi(h, ci, use_analytic_solution) + + a = [ + [0, 0, 0], + [0, 0, 0], + [0, (1/c2) * φ(2,3), 0], + ] + b = [ + [0, 0, φ(2)], + ] + + a, b = gen_first_col_exp(a,b,ci,φ) + + + case "res_3s_cox_matthews": # Cox & Matthews; known as ETD3RK + c2 = 1/2 # must be 1/2 + ci = [0,c2,1] + φ = Phi(h, ci, use_analytic_solution) + + a = [ + [0, 0, 0], + [0, 0, 0], + [0, (1/c2) * φ(1,3), 0], # paper said 2 * φ(1,3), but this is the same and more consistent with res_3s_strehmel_weiner + ] + b = [ + [0, + -8*φ(3) + 4*φ(2), + 4*φ(3) - φ(2)], + ] + + a, b = gen_first_col_exp(a,b,ci,φ) + + case "res_3s_lie": # Lie; known as ETD2CF3 + c1,c2,c3 = 0, 1/3, 2/3 + ci = [c1,c2,c3] + φ = Phi(h, ci, use_analytic_solution) + + a = [ + [0, 0, 0], + [0, 0, 0], + [0, (4/3)*φ(2,3), 0], # paper said 2 * φ(1,3), but this is the same and more consistent with res_3s_strehmel_weiner + ] + b = [ + [0, + 6*φ(2) - 18*φ(3), + (-3/2)*φ(2) + 9*φ(3)], + ] + + a, b = gen_first_col_exp(a,b,ci,φ) + + case "res_3s_sunstar": # https://arxiv.org/pdf/2410.00498 pg 5 (tableau 2.7) + c1,c2,c3 = 0, 1/3, 2/3 + ci = [c1,c2,c3] + φ = Phi(h, ci, use_analytic_solution) + + a = [ + [0, 0, 0], + [0, 0, 0], + [0, (8/9)*φ(2,3), 0], # paper said 2 * φ(1,3), but this is the same and more consistent with res_3s_strehmel_weiner + ] + b = [ + [0, + 0, + (3/2)*φ(2)], + ] + + a, b = gen_first_col_exp(a,b,ci,φ) + + + case "res_4s_cox_matthews": # weak 4th order, Cox & Matthews; unresolved issue, see below + c1,c2,c3,c4 = 0, 1/2, 1/2, 1 + ci = [c1,c2,c3,c4] + φ = Phi(h, ci, use_analytic_solution) + + a2_1 = c2 * φ(1,2) + a3_2 = c3 * φ(1,3) + a4_1 = (1/2) * φ(1,3) * (φ(0,3) - 1) # φ(0,3) == torch.exp(-h*c3) + a4_3 = φ(1,3) + + b1 = φ(1) - 3*φ(2) + 4*φ(3) + + b2 = 2*φ(2) - 4*φ(3) + b3 = 2*φ(2) - 4*φ(3) + b4 = 4*φ(3) - φ(2) + + a = [ + [0, 0,0,0], + [a2_1, 0,0,0], + [0, a3_2,0,0], + [a4_1, 0, a4_3,0], + ] + b = [ + [b1, b2, b3, b4], + ] + + + case "res_4s_cfree4": # weak 4th order, Cox & Matthews; unresolved issue, see below + c1,c2,c3,c4 = 0, 1/2, 1/2, 1 + ci = [c1,c2,c3,c4] + φ = Phi(h, ci, use_analytic_solution) + + a2_1 = c2 * φ(1,2) + a3_2 = c3 * φ(1,2) + a4_1 = (1/2) * φ(1,2) * (φ(0,2) - 1) # φ(0,3) == torch.exp(-h*c3) + a4_3 = φ(1,2) + + b1 = (1/2)*φ(1) - (1/3)*φ(1,2) + + b2 = (1/3)*φ(1) + b3 = (1/3)*φ(1) + b4 = -(1/6)*φ(1) + (1/3)*φ(1,2) + + a = [ + [0, 0,0,0], + [a2_1, 0,0,0], + [0, a3_2,0,0], + [a4_1, 0, a4_3,0], + ] + b = [ + [b1, b2, b3, b4], + ] + + case "res_4s_friedli": # https://ora.ox.ac.uk/objects/uuid:cc001282-4285-4ca2-ad06-31787b540c61/files/m611df1a355ca243beb09824b70e5e774 + c1,c2,c3,c4 = 0, 1/2, 1/2, 1 + ci = [c1,c2,c3,c4] + φ = Phi(h, ci, use_analytic_solution) + + a3_2 = 2*φ(2,2) + a4_2 = -(26/25)*φ(1) + (2/25)*φ(2) + a4_3 = (26/25)*φ(1) + (48/25)*φ(2) + + + b2 = 0 + b3 = 4*φ(2) - 8*φ(3) + b4 = -φ(2) + 4*φ(3) + + a = [ + [0, 0,0,0], + [0, 0,0,0], + [0, a3_2,0,0], + [0, a4_2, a4_3,0], + ] + b = [ + [0, b2, b3, b4], + ] + + a, b = gen_first_col_exp(a,b,ci,φ) + + case "res_4s_munthe-kaas": # unstable RKMK4t + c1,c2,c3,c4 = 0, 1/2, 1/2, 1 + ci = [c1,c2,c3,c4] + φ = Phi(h, ci, use_analytic_solution) + + a = [ + [0, 0, 0, 0], + [c2*φ(1,2), 0, 0, 0], + [(h/8)*φ(1,2), (1/2)*(1-h/4)*φ(1,2), 0, 0], + [0, 0, φ(1), 0], + ] + b = [ + [ + (1/6)*φ(1)*(1+h/2), + (1/3)*φ(1), + (1/3)*φ(1), + (1/6)*φ(1)*(1-h/2) + ], + ] + + case "res_4s_krogstad": # weak 4th order, Krogstad + c1,c2,c3,c4 = 0, 1/2, 1/2, 1 + ci = [c1,c2,c3,c4] + φ = Phi(h, ci, use_analytic_solution) + + a = [ + [0, 0, 0, 0], + [0, 0, 0, 0], + [0, φ(2,3), 0, 0], + [0, 0, 2*φ(2,4), 0], + ] + b = [ + [ + 0, + 2*φ(2) - 4*φ(3), + 2*φ(2) - 4*φ(3), + -φ(2) + 4*φ(3) + ], + ] + + #a = [row + [0] * (len(ci) - len(row)) for row in a] + a, b = gen_first_col_exp(a,b,ci,φ) + + case "res_4s_krogstad_alt": # weak 4th order, Krogstad https://ora.ox.ac.uk/objects/uuid:cc001282-4285-4ca2-ad06-31787b540c61/files/m611df1a355ca243beb09824b70e5e774 + c1,c2,c3,c4 = 0, 1/2, 1/2, 1 + ci = [c1,c2,c3,c4] + φ = Phi(h, ci, use_analytic_solution) + + a = [ + [0, 0, 0, 0], + [0, 0, 0, 0], + [0, 4*φ(2,2), 0, 0], + [0, 0, 2*φ(2), 0], + ] + b = [ + [ + 0, + 2*φ(2) - 4*φ(3), + 2*φ(2) - 4*φ(3), + -φ(2) + 4*φ(3) + ], + ] + + #a = [row + [0] * (len(ci) - len(row)) for row in a] + a, b = gen_first_col_exp(a,b,ci,φ) + + case "res_4s_minchev": # https://ora.ox.ac.uk/objects/uuid:cc001282-4285-4ca2-ad06-31787b540c61/files/m611df1a355ca243beb09824b70e5e774 + c1,c2,c3,c4 = 0, 1/2, 1/2, 1 + ci = [c1,c2,c3,c4] + φ = Phi(h, ci, use_analytic_solution) + + a3_2 = (4/25)*φ(1,2) + (24/25)*φ(2,2) + a4_2 = (21/5)*φ(2) - (108/5)*φ(3) + a4_3 = (1/20)*φ(1) - (33/10)*φ(2) + (123/5)*φ(3) + + + b2 = -(1/10)*φ(1) + (1/5)*φ(2) - 4*φ(3) + 12*φ(4) + b3 = (1/30)*φ(1) + (23/5)*φ(2) - 8*φ(3) - 4*φ(4) + b4 = (1/30)*φ(1) - (7/5)*φ(2) + 6*φ(3) - 4*φ(4) + + a = [ + [0, 0,0,0], + [0, 0,0,0], + [0, a3_2,0,0], + [0, 0, a4_3,0], + ] + b = [ + [0, b2, b3, b4], + ] + + a, b = gen_first_col_exp(a,b,ci,φ) + + case "res_4s_strehmel_weiner": # weak 4th order, Strehmel & Weiner + c1,c2,c3,c4 = 0, 1/2, 1/2, 1 + ci = [c1,c2,c3,c4] + φ = Phi(h, ci, use_analytic_solution) + + a = [ + [0, 0, 0, 0], + [0, 0, 0, 0], + [0, c3*φ(2,3), 0, 0], + [0, -2*φ(2,4), 4*φ(2,4), 0], + ] + b = [ + [ + 0, + 0, + 4*φ(2) - 8*φ(3), + -φ(2) + 4*φ(3) + ], + ] + + a, b = gen_first_col_exp(a,b,ci,φ) + + case "res_4s_strehmel_weiner_alt": # weak 4th order, Strehmel & Weiner https://ora.ox.ac.uk/objects/uuid:cc001282-4285-4ca2-ad06-31787b540c61/files/m611df1a355ca243beb09824b70e5e774 + c1,c2,c3,c4 = 0, 1/2, 1/2, 1 + ci = [c1,c2,c3,c4] + φ = Phi(h, ci, use_analytic_solution) + + a = [ + [0, 0, 0, 0], + [0, 0, 0, 0], + [0, 2*φ(2,2), 0, 0], + [0, -2*φ(2), 4*φ(2), 0], + ] + b = [ + [ + 0, + 0, + 4*φ(2) - 8*φ(3), + -φ(2) + 4*φ(3) + ], + ] + + a, b = gen_first_col_exp(a,b,ci,φ) + + + case "lawson2a_2s": # based on midpoint rule, stiff order 1 https://cds.cern.ch/record/848126/files/cer-002531460.pdf + c1,c2 = 0,1/2 + ci = [c1, c2] + φ = Phi(h, ci, use_analytic_solution) + + a2_1 = c2 * φ(0,2) + b2 = φ(0,2) + b1 = 0 + + a = [ + [0,0], + [a2_1, 0], + ] + b = [ + [b1, b2], + ] + + case "lawson2b_2s": # based on trapezoidal rule, stiff order 1 https://cds.cern.ch/record/848126/files/cer-002531460.pdf + c1,c2 = 0,1 + ci = [c1, c2] + φ = Phi(h, ci, use_analytic_solution) + + a2_1 = φ(0) + b2 = 1/2 + b1 = (1/2)*φ(0) + + a = [ + [0,0], + [a2_1, 0], + ] + b = [ + [b1, b2], + ] + + + case "lawson4_4s": + c1,c2,c3,c4 = 0, 1/2, 1/2, 1 + ci = [c1,c2,c3,c4] + φ = Phi(h, ci, use_analytic_solution) + + a2_1 = c2 * φ(0,2) + a3_2 = 1/2 + a4_3 = φ(0,2) + + b1 = (1/6) * φ(0) + b2 = (1/3) * φ(0,2) + b3 = (1/3) * φ(0,2) + b4 = 1/6 + + a = [ + [0, 0, 0, 0], + [a2_1, 0, 0, 0], + [0, a3_2, 0, 0], + [0, 0, a4_3, 0], + ] + b = [ + [b1,b2,b3,b4], + ] + + case "lawson41-gen_4s": # GenLawson4 https://ora.ox.ac.uk/objects/uuid:cc001282-4285-4ca2-ad06-31787b540c61/files/m611df1a355ca243beb09824b70e5e774 + c1,c2,c3,c4 = 0, 1/2, 1/2, 1 + ci = [c1,c2,c3,c4] + φ = Phi(h, ci, use_analytic_solution) + + + a3_2 = 1/2 + a4_3 = φ(0,2) + + b2 = (1/3) * φ(0,2) + b3 = (1/3) * φ(0,2) + b4 = 1/6 + + a = [ + [0, 0, 0, 0], + [0, 0, 0, 0], + [0, a3_2, 0, 0], + [0, 0, a4_3, 0], + ] + b = [ + [0, + b2, + b3, + b4,], + ] + + a, b = gen_first_col_exp(a,b,ci,φ) + + case "lawson41-gen-mod_4s": # GenLawson4 https://ora.ox.ac.uk/objects/uuid:cc001282-4285-4ca2-ad06-31787b540c61/files/m611df1a355ca243beb09824b70e5e774 + c1,c2,c3,c4 = 0, 1/2, 1/2, 1 + ci = [c1,c2,c3,c4] + φ = Phi(h, ci, use_analytic_solution) + + + a3_2 = 1/2 + a4_3 = φ(0,2) + + b2 = (1/3) * φ(0,2) + b3 = (1/3) * φ(0,2) + b4 = φ(2) - (1/3)*φ(0,2) + + a = [ + [0, 0, 0, 0], + [0, 0, 0, 0], + [0, a3_2, 0, 0], + [0, 0, a4_3, 0], + ] + b = [ + [0, + b2, + b3, + b4,], + ] + + a, b = gen_first_col_exp(a,b,ci,φ) + + + + case "lawson42-gen-mod_1h4s": # GenLawson4 https://ora.ox.ac.uk/objects/uuid:cc001282-4285-4ca2-ad06-31787b540c61/files/m611df1a355ca243beb09824b70e5e774 + c1,c2,c3,c4 = 0, 1/2, 1/2, 1 + ci = [c1,c2,c3,c4] + φ = Phi(h, ci, use_analytic_solution) + + a3_2 = 1/2 + a4_3 = φ(0,2) + + b2 = (1/3) * φ(0,2) + b3 = (1/3) * φ(0,2) + b4 = (1/2)*φ(2) + φ(3) - (1/4)*φ(0,2) + + a = [ + [0, 0, 0, 0], + [0, 0, 0, 0], + [0, a3_2, 0, 0], + [0, 0, a4_3, 0], + ] + b = [ + [0, b2, b3, b4,], + ] + + if extra_options_flag("h_prev_h_h_no_eta", extra_options): + φ1 = Phi(h_prev1_no_eta * h/h_no_eta, ci, use_analytic_solution) + elif extra_options_flag("h_only", extra_options): + φ1 = Phi(h, ci, use_analytic_solution) + else: + φ1 = Phi(h_prev1_no_eta, ci, use_analytic_solution) + + u2_1 = -φ1(2,2) + u3_1 = -φ1(2,2) + 1/4 + u4_1 = -φ1(2) + (1/2)*φ1(0,2) + v1 = -(1/2)*φ1(2) + φ1(3) + (1/12)*φ1(0,2) + + u = [ + [ 0, 0, 0, 0], + [u2_1, 0, 0, 0], + [u3_1, 0, 0, 0], + [u4_1, 0, 0, 0], + ] + v = [ + [v1, 0, 0, 0,], + ] + + a, b = gen_first_col_exp_uv(a,b,ci,u,v,φ) + + + + case "lawson43-gen-mod_2h4s": # GenLawson4 https://ora.ox.ac.uk/objects/uuid:cc001282-4285-4ca2-ad06-31787b540c61/files/m611df1a355ca243beb09824b70e5e774 + c1,c2,c3,c4 = 0, 1/2, 1/2, 1 + ci = [c1,c2,c3,c4] + φ = Phi(h, ci, use_analytic_solution) + + a3_2 = 1/2 + a4_3 = φ(0,2) + + b3 = b2 = (1/3) * a4_3 + b4 = (1/3)*φ(2) + φ(3) + φ(4) - (5/24)*φ(0,2) + + a = [ + [0, 0, 0, 0], + [0, 0, 0, 0], + [0, a3_2, 0, 0], + [0, 0, a4_3, 0], + ] + b = [ + [0, b2, b3, b4,], + ] + + if extra_options_flag("h_prev_h_h_no_eta", extra_options): + φ1 = Phi(h_prev1_no_eta * h/h_no_eta, ci, use_analytic_solution) + φ2 = Phi(h_prev2_no_eta * h/h_no_eta, ci, use_analytic_solution) + elif extra_options_flag("h_only", extra_options): + φ1 = Phi(h, ci, use_analytic_solution) + φ2 = Phi(h, ci, use_analytic_solution) + else: + φ1 = Phi(h_prev1_no_eta, ci, use_analytic_solution) + φ2 = Phi(h_prev2_no_eta, ci, use_analytic_solution) + + u2_1 = -2*φ1(2,2) - 2*φ1(3,2) + u3_1 = -2*φ1(2,2) - 2*φ1(3,2) + 5/8 + u4_1 = -2*φ1(2) - 2*φ1(3) + (5/4)*φ1(0,2) + v1 = -φ1(2) + φ1(3) + 3*φ1(4) + (5/24)*φ1(0,2) + + u2_2 = -(1/2)*φ2(2,2) + φ2(3,2) + u3_2 = (1/2)*φ2(2,2) + φ2(3,2) - 3/16 + u4_2 = (1/2)*φ2(2) + φ2(3) - (3/8)*φ2(0,2) + v2 = (1/6)*φ2(2) - φ2(4) - (1/24)*φ2(0,2) + + u = [ + [ 0, 0, 0, 0], + [u2_1, u2_2, 0, 0], + [u3_1, u3_2, 0, 0], + [u4_1, u4_2, 0, 0], + ] + v = [ + [v1, v2, 0, 0,], + ] + + a, b = gen_first_col_exp_uv(a,b,ci,u,v,φ) + + + case "lawson44-gen-mod_3h4s": # GenLawson4 https://ora.ox.ac.uk/objects/uuid:cc001282-4285-4ca2-ad06-31787b540c61/files/m611df1a355ca243beb09824b70e5e774 + c1,c2,c3,c4 = 0, 1/2, 1/2, 1 + ci = [c1,c2,c3,c4] + φ = Phi(h, ci, use_analytic_solution) + + a3_2 = 1/2 + a4_3 = φ(0,2) + + b3 = b2 = (1/3) * a4_3 + b4 = (1/4)*φ(2) + (11/12)*φ(3) + (3/2)*φ(4) + φ(5) - (35/192)*φ(0,2) + + a = [ + [0, 0, 0, 0], + [0, 0, 0, 0], + [0, a3_2, 0, 0], + [0, 0, a4_3, 0], + ] + b = [ + [0, b2, b3, b4,], + ] + + if extra_options_flag("h_prev_h_h_no_eta", extra_options): + φ1 = Phi(h_prev1_no_eta * h/h_no_eta, ci, use_analytic_solution) + φ2 = Phi(h_prev2_no_eta * h/h_no_eta, ci, use_analytic_solution) + φ3 = Phi(h_prev3_no_eta * h/h_no_eta, ci, use_analytic_solution) + elif extra_options_flag("h_only", extra_options): + φ1 = Phi(h, ci, use_analytic_solution) + φ2 = Phi(h, ci, use_analytic_solution) + φ3 = Phi(h, ci, use_analytic_solution) + else: + φ1 = Phi(h_prev1_no_eta, ci, use_analytic_solution) + φ2 = Phi(h_prev2_no_eta, ci, use_analytic_solution) + φ3 = Phi(h_prev3_no_eta, ci, use_analytic_solution) + + u2_1 = -3*φ1(2,2) - 5*φ1(3,2) - 3*φ1(4,2) + u3_1 = u2_1 + 35/32 + u4_1 = -3*φ1(2) - 5*φ1(3) - 3*φ1(4) + (35/16)*φ1(0,2) + v1 = -(3/2)*φ1(2) + (1/2)*φ1(3) + 6*φ1(4) + 6*φ1(5) + (35/96)*φ1(0,2) + + u2_2 = (3/2)*φ2(2,2) + 4*φ2(3,2) + 3*φ2(4,2) + u3_2 = u2_2 - 21/32 + u4_2 = (3/2)*φ2(2) + 4*φ2(3) + 3*φ2(4) - (21/16)*φ2(0,2) + v2 = (1/2)*φ2(2) + (1/3)*φ2(3) - 3*φ2(4) - 4*φ2(5) - (7/48)*φ2(0,2) + + u2_3 = (-1/3)*φ3(2,2) - φ3(3,2) - φ3(4,2) + u3_3 = u2_3 + 5/32 + u4_3 = -(1/3)*φ3(2) - φ3(3) - φ3(4) + (5/16)*φ3(0,2) + v3 = -(1/12)*φ3(2) - (1/12)*φ3(3) + (1/2)*φ3(4) + φ3(5) + (5/192)*φ3(0,2) + + u = [ + [ 0, 0, 0, 0], + [u2_1, u2_2, u2_3, 0], + [u3_1, u3_2, u3_3, 0], + [u4_1, u4_2, u4_3, 0], + ] + v = [ + [v1, v2, v3, 0,], + ] + + a, b = gen_first_col_exp_uv(a,b,ci,u,v,φ) + + + + case "lawson45-gen-mod_4h4s": # GenLawson4 https://ora.ox.ac.uk/objects/uuid:cc001282-4285-4ca2-ad06-31787b540c61/files/m611df1a355ca243beb09824b70e5e774 + c1,c2,c3,c4 = 0, 1/2, 1/2, 1 + ci = [c1,c2,c3,c4] + φ = Phi(h, ci, use_analytic_solution) + + a3_2 = 1/2 + a4_3 = φ(0,2) + + b2 = (1/3) * φ(0,2) + b3 = (1/3) * φ(0,2) + b4 = (12/59)*φ(2) + (50/59)*φ(3) + (105/59)*φ(4) + (120/59)*φ(5) - (60/59)*φ(6) - (157/944)*φ(0,2) + + a = [ + [0, 0, 0, 0], + [0, 0, 0, 0], + [0, a3_2, 0, 0], + [0, 0, a4_3, 0], + ] + b = [ + [0, b2, b3, b4,], + ] + + if extra_options_flag("h_prev_h_h_no_eta", extra_options): + φ1 = Phi(h_prev1_no_eta * h/h_no_eta, ci, use_analytic_solution) + φ2 = Phi(h_prev2_no_eta * h/h_no_eta, ci, use_analytic_solution) + φ3 = Phi(h_prev3_no_eta * h/h_no_eta, ci, use_analytic_solution) + φ4 = Phi(h_prev4_no_eta * h/h_no_eta, ci, use_analytic_solution) + elif extra_options_flag("h_only", extra_options): + φ1 = Phi(h, ci, use_analytic_solution) + φ2 = Phi(h, ci, use_analytic_solution) + φ3 = Phi(h, ci, use_analytic_solution) + φ4 = Phi(h, ci, use_analytic_solution) + else: + φ1 = Phi(h_prev1_no_eta, ci, use_analytic_solution) + φ2 = Phi(h_prev2_no_eta, ci, use_analytic_solution) + φ3 = Phi(h_prev3_no_eta, ci, use_analytic_solution) + φ4 = Phi(h_prev4_no_eta, ci, use_analytic_solution) + + u2_1 = -4*φ1(2,2) - (26/3)*φ1(3,2) - 9*φ1(4,2) - 4*φ1(5,2) + u3_1 = u2_1 + 105/64 + u4_1 = -4*φ1(2) - (26/3)*φ1(3) - 9*φ1(4) - 4*φ1(5) + (105/32)*φ1(0,2) + v1 = -(116/59)*φ1(2) - (34/177)*φ1(3) + (519/59)*φ1(4) + (964/59)*φ1(5) - (600/59)*φ1(6) + (495/944)*φ1(0,2) + + u2_2 = 3*φ2(2,2) + (19/2)*φ2(3,2) + 12*φ2(4,2) + 6*φ2(5,2) + u3_2 = u2_2 - 189/128 + u4_2 = 3*φ2(2) + (19/2)*φ2(3) + 12*φ2(4) + 6*φ2(5) - (189/64)*φ2(0,2) + v2 = (57/59)*φ2(2) + (121/118)*φ2(3) - (342/59)*φ2(4) - (846/59)*φ2(5) + (600/59)*φ2(6) - (577/1888)*φ2(0,2) + + u2_3 = -(4/3)*φ3(2,2) - (14/3)*φ3(3,2) - 7*φ3(4,2) - 4*φ3(5,2) + u3_3 = u2_3 + 45/64 + u4_3 = -(4/3)*φ3(2) - (14/3)*φ3(3) - 7*φ3(4) - 4*φ3(5) +(45/32)*φ3(0,2) + v3 = -(56/177)*φ3(2) - (76/177)*φ3(3) + (112/59)*φ3(4) + (364/59)*φ3(5) - (300/59)*φ3(6) + (25/236)*φ3(0,2) + + u2_4 = (1/4)*φ4(2,2) + (88/96)*φ4(3,2) + (3/2)*φ4(4,2) + φ4(5,2) + u3_4 = u2_4 - 35/256 + u4_4 = (1/4)*φ4(2) + (11/12)*φ4(3) + (3/2)*φ4(4) + φ4(5) - (35/128)*φ4(0,2) + v4 = (11/236)*φ4(2) + (49/708)*φ4(3) - (33/118)*φ4(4) - (61/59)*φ4(5) + ( 60/59)*φ4(6) - (181/11328)*φ4(0,2) + + u = [ + [ 0, 0, 0, 0], + [u2_1, u2_2, u2_3, u2_4], + [u3_1, u3_2, u3_3, u3_4], + [u4_1, u4_2, u4_3, u4_4], + ] + v = [ + [v1, v2, v3, v4,], + ] + + a, b = gen_first_col_exp_uv(a,b,ci,u,v,φ) + + + + case "etdrk2_2s": # https://arxiv.org/pdf/2402.15142v1 + c1,c2 = 0, 1 + ci = [c1,c2] + φ = Phi(h, ci, use_analytic_solution) + + a = [ + [0, 0], + [φ(1), 0], + ] + b = [ + [φ(1)-φ(2), φ(2)], + ] + + case "etdrk3_a_3s": #non-monotonic # https://arxiv.org/pdf/2402.15142v1 + c1,c2,c3 = 0, 1, 2/3 + ci = [c1,c2,c3] + φ = Phi(h, ci, use_analytic_solution) + + a2_1 = c2*φ(1) + a3_2 = (4/9)*φ(2,3) + a3_1 = c3*φ(1,3) - a3_2 + + b2 = φ(2) - (1/2)*φ(1) + b3 = (3/4) * φ(1) + b1 = φ(1) - b2 - b3 + + a = [ + [0, 0, 0], + [a2_1, 0, 0], + [a3_1, a3_2, 0 ] + ] + b = [ + [b1, b2, b3], + ] + + case "etdrk3_b_3s": # https://arxiv.org/pdf/2402.15142v1 + c1,c2,c3 = 0, 4/9, 2/3 + ci = [c1,c2,c3] + φ = Phi(h, ci, use_analytic_solution) + + a2_1 = c2*φ(1,2) + a3_2 = φ(2,3) + a3_1 = c3*φ(1,3) - a3_2 + + b2 = 0 + b3 = (3/2) * φ(2) + b1 = φ(1) - b2 - b3 + + a = [ + [0, 0, 0], + [a2_1, 0, 0], + [a3_1, a3_2, 0 ] + ] + b = [ + [b1, b2, b3], + ] + + case "etdrk4_4s": # https://ora.ox.ac.uk/objects/uuid:cc001282-4285-4ca2-ad06-31787b540c61/files/m611df1a355ca243beb09824b70e5e774 + c1,c2,c3,c4 = 0, 1/2, 1/2, 1 + ci = [c1,c2,c3,c4] + φ = Phi(h, ci, use_analytic_solution) + + a3_2 = φ(1,2) + a4_3 = 2*φ(1,2) + + b2 = 2*φ(2) - 4*φ(3) + b3 = 2*φ(2) - 4*φ(3) + b4 = -φ(2) + 4*φ(3) + + a = [ + [0, 0,0,0], + [0, 0,0,0], + [0, a3_2,0,0], + [0, 0, a4_3,0], + ] + b = [ + [0, b2, b3, b4], + ] + + a, b = gen_first_col_exp(a,b,ci,φ) + + + case "etdrk4_4s_alt": # pg 70 col 1 computed with (4.9) https://ora.ox.ac.uk/objects/uuid:cc001282-4285-4ca2-ad06-31787b540c61/files/m611df1a355ca243beb09824b70e5e774 + c1,c2,c3,c4 = 0, 1/2, 1/2, 1 + ci = [c1,c2,c3,c4] + φ = Phi(h, ci, use_analytic_solution) + + a2_1 = φ(1,2) #unsure about this, looks bad and is pretty different from col #1 implementations for everything else except the other 4s alt and 5s ostermann??? from the link + a3_1 = 0 + a4_1 = φ(1) - 2*φ(1,2) + + a3_2 = φ(1,2) + a4_3 = 2*φ(1,2) + + b1 = φ(1) - 3*φ(2) + 4*φ(3) + b2 = 2*φ(2) - 4*φ(3) + b3 = 2*φ(2) - 4*φ(3) + b4 = -φ(2) + 4*φ(3) + + a = [ + [ 0, 0, 0,0], + [a2_1, 0, 0,0], + [a3_1, a3_2, 0,0], + [a4_1, 0, a4_3,0], + ] + b = [ + [0, b2, b3, b4], + ] + + #a, b = gen_first_col_exp(a,b,ci,φ) + + + case "dpmpp_2s": + c2 = float(get_extra_options_kv("c2", str(c2), extra_options)) + + ci = [0,c2] + φ = Phi(h, ci, use_analytic_solution) + + b2 = (1/(2*c2)) * φ(1) + + a = [ + [0, 0], + [0, 0], + ] + b = [ + [0, b2], + ] + + a, b = gen_first_col_exp(a,b,ci,φ) + + case "dpmpp_sde_2s": + c2 = 1.0 #hardcoded to 1.0 to more closely emulate the configuration for k-diffusion's implementation + + ci = [0,c2] + φ = Phi(h, ci, use_analytic_solution) + + b2 = (1/(2*c2)) * φ(1) + + a = [ + [0, 0], + [0, 0], + ] + b = [ + [0, b2], + ] + + a, b = gen_first_col_exp(a,b,ci,φ) + + case "dpmpp_3s": + c2 = float(get_extra_options_kv("c2", str(c2), extra_options)) + c3 = float(get_extra_options_kv("c3", str(c3), extra_options)) + + ci = [0,c2,c3] + φ = Phi(h, ci, use_analytic_solution) + + a3_2 = (c3**2 / c2) * φ(2,3) + b3 = (1/c3) * φ(2) + + a = [ + [0, 0, 0], + [0, 0, 0], + [0, a3_2, 0], + ] + b = [ + [0, 0, b3], + ] + + a, b = gen_first_col_exp(a,b,ci,φ) + + case "res_5s": #non-monotonic #4th order + + c1, c2, c3, c4, c5 = 0, 1/2, 1/2, 1, 1/2 + ci = [c1,c2,c3,c4,c5] + φ = Phi(h, ci, use_analytic_solution) + + a3_2 = φ(2,3) + a4_2 = φ(2,4) + a5_2 = (1/2)*φ(2,5) - φ(3,4) + (1/4)*φ(2,4) - (1/2)*φ(3,5) + + a4_3 = a4_2 + a5_3 = a5_2 + + a5_4 = (1/4)*φ(2,5) - a5_2 + + b4 = -φ(2) + 4*φ(3) + b5 = 4*φ(2) - 8*φ(3) + + a = [ + [0, 0, 0, 0, 0], + [0, 0, 0, 0, 0], + [0, a3_2, 0, 0, 0], + [0, a4_2, a4_3, 0, 0], + [0, a5_2, a5_3, a5_4, 0], + ] + b = [ + [0, 0, 0, b4, b5], + ] + + a, b = gen_first_col_exp(a,b,ci,φ) + + case "res_5s_hochbruck-ostermann": #non-monotonic #4th order + + c1, c2, c3, c4, c5 = 0, 1/2, 1/2, 1, 1/2 + ci = [c1,c2,c3,c4,c5] + φ = Phi(h, ci, use_analytic_solution) + + a3_2 = 4*φ(2,2) + a4_2 = φ(2) + a5_2 = (1/4)*φ(2) - φ(3) + 2*φ(2,2) - 4*φ(3,2) + + a4_3 = φ(2) + a5_3 = a5_2 + + a5_4 = φ(2,2) - a5_2 + + b4 = -φ(2) + 4*φ(3) + b5 = 4*φ(2) - 8*φ(3) + + a = [ + [0, 0 , 0 , 0 , 0], + [0, 0 , 0 , 0 , 0], + [0, a3_2, 0 , 0 , 0], + [0, a4_2, a4_3, 0 , 0], + [0, a5_2, a5_3, a5_4, 0], + ] + b = [ + [0, 0, 0, b4, b5], + ] + + a, b = gen_first_col_exp(a,b,ci,φ) + + + case "res_6s": #non-monotonic #4th order + + c1, c2, c3, c4, c5, c6 = 0, 1/2, 1/2, 1/3, 1/3, 5/6 + ci = [c1, c2, c3, c4, c5, c6] + φ = Phi(h, ci, use_analytic_solution) + + a2_1 = c2 * φ(1,2) + + a3_1 = 0 + a3_2 = (c3**2 / c2) * φ(2,3) + + a4_1 = 0 + a4_2 = (c4**2 / c2) * φ(2,4) + a4_3 = (c4**2 * φ(2,4) - a4_2 * c2) / c3 + + a5_1 = 0 + a5_2 = 0 #zero + a5_3 = (-c4 * c5**2 * φ(2,5) + 2*c5**3 * φ(3,5)) / (c3 * (c3 - c4)) + a5_4 = (-c3 * c5**2 * φ(2,5) + 2*c5**3 * φ(3,5)) / (c4 * (c4 - c3)) + + a6_1 = 0 + a6_2 = 0 #zero + a6_3 = (-c4 * c6**2 * φ(2,6) + 2*c6**3 * φ(3,6)) / (c3 * (c3 - c4)) + a6_4 = (-c3 * c6**2 * φ(2,6) + 2*c6**3 * φ(3,6)) / (c4 * (c4 - c3)) + a6_5 = (c6**2 * φ(2,6) - a6_3*c3 - a6_4*c4) / c5 + #a6_5_alt = (2*c6**3 * φ(3,6) - a6_3*c3**2 - a6_4*c4**2) / c5**2 + + b1 = 0 + b2 = 0 + b3 = 0 + b4 = 0 + b5 = (-c6*φ(2) + 2*φ(3)) / (c5 * (c5 - c6)) + b6 = (-c5*φ(2) + 2*φ(3)) / (c6 * (c6 - c5)) + + a = [ + [0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0], + [0, a3_2, 0, 0, 0, 0], + [0, a4_2, a4_3, 0, 0, 0], + [0, a5_2, a5_3, a5_4, 0, 0], + [0, a6_2, a6_3, a6_4, a6_5, 0], + ] + b = [ + [0, b2, b3, b4, b5, b6], + ] + + a, b = gen_first_col_exp(a,b,ci,φ) + + case "res_8s": #non-monotonic # this is not EXPRK5S8 https://ora.ox.ac.uk/objects/uuid:cc001282-4285-4ca2-ad06-31787b540c61/files/m611df1a355ca243beb09824b70e5e774 + + c1, c2, c3, c4, c5, c6, c7, c8 = 0, 1/2, 1/2, 1/4, 1/2, 1/5, 2/3, 1 + ci = [c1, c2, c3, c4, c5, c6, c7, c8] + #φ = Phi(h, ci, analytic_solution=use_analytic_solution) + + ci = [mpf(c_val) for c_val in ci] + c1, c2, c3, c4, c5, c6, c7, c8 = [c_val for c_val in ci] + + φ = Phi(mpf(h.item()), ci, analytic_solution=use_analytic_solution) + + a3_2 = (1/2) * φ(2,3) + + a4_3 = (1/8) * φ(2,4) + + a5_3 = (-1/2) * φ(2,5) + 2 * φ(3,5) + a5_4 = 2 * φ(2,5) - 4 * φ(3,5) + + a6_4 = (8/25) * φ(2,6) - (32/125) * φ(3,6) + a6_5 = (2/25) * φ(2,6) - (1/2) * a6_4 + + a7_4 = (-125/162) * a6_4 + a7_5 = (125/1944) * a6_4 - (16/27) * φ(2,7) + (320/81) * φ(3,7) + a7_6 = (3125/3888) * a6_4 + (100/27) * φ(2,7) - (800/81) * φ(3,7) + + Φ = (5/32)*a6_4 - (1/28)*φ(2,6) + (36/175)*φ(2,7) - (48/25)*φ(3,7) + (6/175)*φ(4,6) + (192/35)*φ(4,7) + 6*φ(4,8) + + a8_5 = (208/3)*φ(3,8) - (16/3) *φ(2,8) - 40*Φ + a8_6 = (-250/3)*φ(3,8) + (250/21)*φ(2,8) + (250/7)*Φ + a8_7 = -27*φ(3,8) + (27/14)*φ(2,8) + (135/7)*Φ + + b6 = (125/14)*φ(2) - (625/14)*φ(3) + (1125/14)*φ(4) + b7 = (-27/14)*φ(2) + (162/7) *φ(3) - (405/7) *φ(4) + b8 = (1/2) *φ(2) - (13/2) *φ(3) + (45/2) *φ(4) + + b1 = φ(1) - b6 - b7 - b8 + + a = [ + [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0], + [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0], + + [0 , a3_2, 0 , 0 , 0 , 0 , 0 , 0], + [0 , 0 , a4_3, 0 , 0 , 0 , 0 , 0], + + [0 , 0 , a5_3, a5_4, 0 , 0 , 0 , 0], + [0 , 0 , 0 , a6_4, a6_5, 0 , 0 , 0], + + [0 , 0 , 0 , a7_4, a7_5, a7_6, 0 , 0], + [0 , 0 , 0 , 0 , a8_5, a8_6, a8_7, 0], + ] + b = [ + [0, 0, 0, 0, 0, b6, b7, b8], + ] + + a, b = gen_first_col_exp(a,b,ci,φ) + + a = [[float(val) for val in row] for row in a] + b = [[float(val) for val in row] for row in b] + ci = [c1, c2, c3, c4, c5, c6, c7, c8] + + + + case "res_8s_alt": # this is EXPRK5S8 https://ora.ox.ac.uk/objects/uuid:cc001282-4285-4ca2-ad06-31787b540c61/files/m611df1a355ca243beb09824b70e5e774 + + c1, c2, c3, c4, c5, c6, c7, c8 = 0, 1/2, 1/2, 1/4, 1/2, 1/5, 2/3, 1 + #ci = [c1, c2, c3, c4, c5, c6, c7, c8] + #φ = Phi(h, ci, analytic_solution=use_analytic_solution) + + ci = [mpf(c_val) for c_val in ci] + c1, c2, c3, c4, c5, c6, c7, c8 = [c_val for c_val in ci] + + φ = Phi(mpf(h.item()), ci, analytic_solution=use_analytic_solution) + + a3_2 = 2*φ(2,2) + + a4_3 = 2*φ(2,4) + + a5_3 = -2*φ(2,2) + 16*φ(3,2) + a5_4 = 8*φ(2,2) - 32*φ(3,2) + + a6_4 = 8*φ(2,6) - 32*φ(3,6) + a6_5 = -2*φ(2,6) + 16*φ(3,6) + + a7_4 = (-125/162) * a6_4 + a7_5 = (125/1944) * a6_4 - (4/3) * φ(2,7) + (40/3)*φ(3,7) + a7_6 = (3125/3888) * a6_4 + (25/3) * φ(2,7) - (100/3)*φ(3,7) + + Φ = (5/32)*a6_4 - (25/28)*φ(2,6) + (81/175)*φ(2,7) - (162/25)*φ(3,7) + (150/7)*φ(4,6) + (972/35)*φ(4,7) + 6*φ(4) + + a8_5 = -(16/3)*φ(2) + (208/3)*φ(3) - 40*Φ + a8_6 = (250/21)*φ(2) - (250/3)*φ(3) + (250/7)*Φ + a8_7 = (27/14)*φ(2) - 27*φ(3) + (135/7)*Φ + + b6 = (125/14)*φ(2) - (625/14)*φ(3) + (1125/14)*φ(4) + b7 = (-27/14)*φ(2) + (162/7) *φ(3) - (405/7) *φ(4) + b8 = (1/2) *φ(2) - (13/2) *φ(3) + (45/2) *φ(4) + + a = [ + [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0], + [0 , 0 , 0 , 0 , 0 , 0 , 0 , 0], + + [0 , a3_2, 0 , 0 , 0 , 0 , 0 , 0], + [0 , 0 , a4_3, 0 , 0 , 0 , 0 , 0], + + [0 , 0 , a5_3, a5_4, 0 , 0 , 0 , 0], + [0 , 0 , 0 , a6_4, a6_5, 0 , 0 , 0], + + [0 , 0 , 0 , a7_4, a7_5, a7_6, 0 , 0], + [0 , 0 , 0 , 0 , a8_5, a8_6, a8_7, 0], + ] + b = [ + [0, 0, 0, 0, 0, b6, b7, b8], + ] + + a, b = gen_first_col_exp(a,b,ci,φ) + + a = [[float(val) for val in row] for row in a] + b = [[float(val) for val in row] for row in b] + ci = [c1, c2, c3, c4, c5, c6, c7, c8] + + + case "res_10s": + + c1, c2, c3, c4, c5, c6, c7, c8, c9, c10 = 0, 1/2, 1/2, 1/3, 1/2, 1/3, 1/4, 3/10, 3/4, 1 + ci = [c1, c2, c3, c4, c5, c6, c7, c8, c9, c10] + #φ = Phi(h, ci, analytic_solution=use_analytic_solution) + + ci = [mpf(c_val) for c_val in ci] + c1, c2, c3, c4, c5, c6, c7, c8, c9, c10 = [c_val for c_val in ci] + + φ = Phi(mpf(h.item()), ci, analytic_solution=use_analytic_solution) + + a3_2 = (c3**2 / c2) * φ(2,3) + a4_2 = (c4**2 / c2) * φ(2,4) + + b8 = (c9*c10*φ(2) - 2*(c9+c10)*φ(3) + 6*φ(4)) / (c8 * (c8-c9) * (c8-c10)) + b9 = (c8*c10*φ(2) - 2*(c8+c10)*φ(3) + 6*φ(4)) / (c9 * (c9-c8) * (c9-c10)) + + b10 = (c8*c9*φ(2) - 2*(c8+c9) *φ(3) + 6*φ(4)) / (c10 * (c10-c8) * (c10-c9)) + + a = [ + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, a3_2, 0, 0, 0, 0, 0, 0, 0, 0], + [0, a4_2, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + ] + b = [ + [0, 0, 0, 0, 0, 0, 0, b8, b9, b10], + ] + + # a5_3, a5_4 + # a6_3, a6_4 + # a7_3, a7_4 + for i in range(5, 8): # i=5,6,7 j,k ∈ {3, 4}, j != k + jk = [(3, 4), (4, 3)] + jk = list(permutations([3, 4], 2)) + for j,k in jk: + a[i-1][j-1] = (-ci[i-1]**2 * ci[k-1] * φ(2,i) + 2*ci[i-1]**3 * φ(3,i)) / (ci[j-1] * (ci[j-1] - ci[k-1])) + + for i in range(8, 11): # i=8,9,10 j,k,l ∈ {5, 6, 7}, j != k != l [ (5, 6, 7), (5, 7, 6), (6, 5, 7), (6, 7, 5), (7, 5, 6), (7, 6, 5)] 6 total coeff + jkl = list(permutations([5, 6, 7], 3)) + for j,k,l in jkl: + a[i-1][j-1] = (ci[i-1]**2 * ci[k-1] * ci[l-1] * φ(2,i) - 2*ci[i-1]**3 * (ci[k-1] + ci[l-1]) * φ(3,i) + 6*ci[i-1]**4 * φ(4,i)) / (ci[j-1] * (ci[j-1] - ci[k-1]) * (ci[j-1] - ci[l-1])) + + gen_first_col_exp(a, b, ci, φ) + + a = [[float(val) for val in row] for row in a] + b = [[float(val) for val in row] for row in b] + c1, c2, c3, c4, c5, c6, c7, c8, c9, c10 = 0, 1/2, 1/2, 1/3, 1/2, 1/3, 1/4, 3/10, 3/4, 1 + ci = [c1, c2, c3, c4, c5, c6, c7, c8, c9, c10] + + + case "res_15s": + + c1,c2,c3,c4,c5,c6,c7,c8,c9,c10,c11,c12,c13,c14,c15 = 0, 1/2, 1/2, 1/3, 1/2, 1/5, 1/4, 18/25, 1/3, 3/10, 1/6, 90/103, 1/3, 3/10, 1/5 + c1 = 0 + c2 = c3 = c5 = 1/2 + c4 = c9 = c13 = 1/3 + c6 = c15 = 1/5 + c7 = 1/4 + c8 = 18/25 + c10 = c14 = 3/10 + c11 = 1/6 + c12 = 90/103 + c15 = 1/5 + ci = [c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15] + ci = [mpf(c_val) for c_val in ci] + + φ = Phi(mpf(h.item()), ci, analytic_solution=use_analytic_solution) + + a = [[mpf(0) for _ in range(15)] for _ in range(15)] + b = [[mpf(0) for _ in range(15)]] + + for i in range(3, 5): # i=3,4 j=2 + j=2 + a[i-1][j-1] = (ci[i-1]**2 / ci[j-1]) * φ(j,i) + + + for i in range(5, 8): # i=5,6,7 j,k ∈ {3, 4}, j != k + jk = list(permutations([3, 4], 2)) + for j,k in jk: + a[i-1][j-1] = (-ci[i-1]**2 * ci[k-1] * φ(2,i) + 2*ci[i-1]**3 * φ(3,i)) / prod_diff(ci[j-1], ci[k-1]) + + for i in range(8, 12): # i=8,9,10,11 j,k,l ∈ {5, 6, 7}, j != k != l [ (5, 6, 7), (5, 7, 6), (6, 5, 7), (6, 7, 5), (7, 5, 6), (7, 6, 5)] 6 total coeff + jkl = list(permutations([5, 6, 7], 3)) + for j,k,l in jkl: + a[i-1][j-1] = (ci[i-1]**2 * ci[k-1] * ci[l-1] * φ(2,i) - 2*ci[i-1]**3 * (ci[k-1] + ci[l-1]) * φ(3,i) + 6*ci[i-1]**4 * φ(4,i)) / (ci[j-1] * (ci[j-1] - ci[k-1]) * (ci[j-1] - ci[l-1])) + + for i in range(12,16): # i=12,13,14,15 + jkld = list(permutations([8,9,10,11], 4)) + for j,k,l,d in jkld: + numerator = -ci[i-1]**2 * ci[d-1]*ci[k-1]*ci[l-1] * φ(2,i) + 2*ci[i-1]**3 * (ci[d-1]*ci[k-1] + ci[d-1]*ci[l-1] + ci[k-1]*ci[l-1]) * φ(3,i) - 6*ci[i-1]**4 * (ci[d-1] + ci[k-1] + ci[l-1]) * φ(4,i) + 24*ci[i-1]**5 * φ(5,i) + a[i-1][j-1] = numerator / prod_diff(ci[j-1], ci[k-1], ci[l-1], ci[d-1]) + + """ijkl = list(permutations([12,13,14,15], 4)) + for i,j,k,l in ijkl: + #numerator = -ci[j-1]*ci[k-1]*ci[l-1]*φ(2) + 2*(ci[j-1]*ci[k-1] + ci[j-1]*ci[l-1] + ci[k-1]*ci[l-1])*φ(3) - 6*(ci[j-1] + ci[k-1] + ci[l-1])*φ(4) + 24*φ(5) + #b[0][i-1] = numerator / prod_diff(ci[i-1], ci[j-1], ci[k-1], ci[l-1]) + for jjj in range (2, 6): # 2,3,4,5 + b[0][i-1] += mu_numerator(jjj, ci[j-1], ci[i-1], ci[k-1], ci[l-1]) * φ(jjj) + b[0][i-1] /= prod_diff(ci[i-1], ci[j-1], ci[k-1], ci[l-1])""" + + ijkl = list(permutations([12,13,14,15], 4)) + for i,j,k,l in ijkl: + numerator = 0 + for jjj in range(2, 6): # 2, 3, 4, 5 + numerator += mu_numerator(jjj, ci[j-1], ci[i-1], ci[k-1], ci[l-1]) * φ(jjj) + #print(i,j,k,l) + + b[0][i-1] = numerator / prod_diff(ci[i-1], ci[j-1], ci[k-1], ci[l-1]) + + + ijkl = list(permutations([12, 13, 14, 15], 4)) + selected_permutations = {} + sign = 1 + + for i in range(12, 16): + results = [] + for j, k, l, d in ijkl: + if i != j and i != k and i != l and i != d: + numerator = 0 + for jjj in range(2, 6): # 2, 3, 4, 5 + numerator += mu_numerator(jjj, ci[j-1], ci[i-1], ci[k-1], ci[l-1]) * φ(jjj) + theta_value = numerator / prod_diff(ci[i-1], ci[j-1], ci[k-1], ci[l-1]) + results.append((theta_value, (i, j, k, l, d))) + + results.sort(key=lambda x: abs(x[0])) + + for theta_value, permutation in results: + if sign == 1 and theta_value > 0: + selected_permutations[i] = (theta_value, permutation) + sign *= -1 + break + elif sign == -1 and theta_value < 0: + selected_permutations[i] = (theta_value, permutation) + sign *= -1 + break + + for i in range(12, 16): + if i in selected_permutations: + theta_value, (i, j, k, l, d) = selected_permutations[i] + b[0][i-1] = theta_value + + for i in selected_permutations: + theta_value, permutation = selected_permutations[i] + print(f"i={i}") + print(f" Selected Theta: {theta_value:.6f}, Permutation: {permutation}") + + + gen_first_col_exp(a, b, ci, φ) + + a = [[float(val) for val in row] for row in a] + b = [[float(val) for val in row] for row in b] + ci = [c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15] + + + case "res_16s": # 6th order without weakened order conditions + + c1 = 0 + c2 = c3 = c5 = c8 = c12 = 1/2 + c4 = c11 = c15 = 1/3 + c6 = c9 = c13 = 1/5 + c7 = c10 = c14 = 1/4 + c16 = 1 + ci = [c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, c16] + ci = [mpf(c_val) for c_val in ci] + φ = Phi(mpf(h.item()), ci, analytic_solution=use_analytic_solution) + + a3_2 = (1/2) * φ(2,3) + + a = [[mpf(0) for _ in range(16)] for _ in range(16)] + b = [[mpf(0) for _ in range(16)]] + + for i in range(3, 5): # i=3,4 j=2 + j=2 + a[i-1][j-1] = (ci[i-1]**2 / ci[j-1]) * φ(j,i) + + for i in range(5, 8): # i=5,6,7 j,k ∈ {3, 4}, j != k + jk = list(permutations([3, 4], 2)) + for j,k in jk: + a[i-1][j-1] = (-ci[i-1]**2 * ci[k-1] * φ(2,i) + 2*ci[i-1]**3 * φ(3,i)) / prod_diff(ci[j-1], ci[k-1]) + + for i in range(8, 12): # i=8,9,10,11 j,k,l ∈ {5, 6, 7}, j != k != l [ (5, 6, 7), (5, 7, 6), (6, 5, 7), (6, 7, 5), (7, 5, 6), (7, 6, 5)] 6 total coeff + jkl = list(permutations([5, 6, 7], 3)) + for j,k,l in jkl: + a[i-1][j-1] = (ci[i-1]**2 * ci[k-1] * ci[l-1] * φ(2,i) - 2*ci[i-1]**3 * (ci[k-1] + ci[l-1]) * φ(3,i) + 6*ci[i-1]**4 * φ(4,i)) / (ci[j-1] * (ci[j-1] - ci[k-1]) * (ci[j-1] - ci[l-1])) + + for i in range(12,17): # i=12,13,14,15,16 + jkld = list(permutations([8,9,10,11], 4)) + for j,k,l,d in jkld: + numerator = -ci[i-1]**2 * ci[d-1]*ci[k-1]*ci[l-1] * φ(2,i) + 2*ci[i-1]**3 * (ci[d-1]*ci[k-1] + ci[d-1]*ci[l-1] + ci[k-1]*ci[l-1]) * φ(3,i) - 6*ci[i-1]**4 * (ci[d-1] + ci[k-1] + ci[l-1]) * φ(4,i) + 24*ci[i-1]**5 * φ(5,i) + a[i-1][j-1] = numerator / prod_diff(ci[j-1], ci[k-1], ci[l-1], ci[d-1]) + + """ijdkl = list(permutations([12,13,14,15,16], 5)) + for i,j,d,k,l in ijdkl: + #numerator = -ci[j-1]*ci[k-1]*ci[l-1]*φ(2) + 2*(ci[j-1]*ci[k-1] + ci[j-1]*ci[l-1] + ci[k-1]*ci[l-1])*φ(3) - 6*(ci[j-1] + ci[k-1] + ci[l-1])*φ(4) + 24*φ(5) + b[0][i-1] = theta(2, ci[d-1], ci[i-1], ci[k-1], ci[j-1], ci[l-1]) * φ(2) + theta(3, ci[d-1], ci[i-1], ci[k-1], ci[j-1], ci[l-1])*φ(3) + theta(4, ci[d-1], ci[i-1], ci[k-1], ci[j-1], ci[l-1])*φ(4) + theta(5, ci[d-1], ci[i-1], ci[k-1], ci[j-1], ci[l-1])*φ(5) + theta(6, ci[d-1], ci[i-1], ci[k-1], ci[j-1], ci[l-1]) * φ(6) + #b[0][i-1] = numerator / prod_diff(ci[i-1], ci[j-1], ci[k-1], ci[l-1])""" + + + ijdkl = list(permutations([12,13,14,15,16], 5)) + for i,j,d,k,l in ijdkl: + #numerator = -ci[j-1]*ci[k-1]*ci[l-1]*φ(2) + 2*(ci[j-1]*ci[k-1] + ci[j-1]*ci[l-1] + ci[k-1]*ci[l-1])*φ(3) - 6*(ci[j-1] + ci[k-1] + ci[l-1])*φ(4) + 24*φ(5) + #numerator = theta_numerator(2, ci[d-1], ci[i-1], ci[k-1], ci[j-1], ci[l-1]) * φ(2) + theta_numerator(3, ci[d-1], ci[i-1], ci[k-1], ci[j-1], ci[l-1])*φ(3) + theta_numerator(4, ci[d-1], ci[i-1], ci[k-1], ci[j-1], ci[l-1])*φ(4) + theta_numerator(5, ci[d-1], ci[i-1], ci[k-1], ci[j-1], ci[l-1])*φ(5) + theta_numerator(6, ci[d-1], ci[i-1], ci[k-1], ci[j-1], ci[l-1]) * φ(6) + #b[0][i-1] = numerator / (ci[i-1] *, ci[d-1], ci[j-1], ci[k-1], ci[l-1]) + #b[0][i-1] = numerator / denominator(ci[i-1], ci[d-1], ci[j-1], ci[k-1], ci[l-1]) + b[0][i-1] = theta(2, ci[d-1], ci[i-1], ci[k-1], ci[j-1], ci[l-1]) * φ(2) + theta(3, ci[d-1], ci[i-1], ci[k-1], ci[j-1], ci[l-1])*φ(3) + theta(4, ci[d-1], ci[i-1], ci[k-1], ci[j-1], ci[l-1])*φ(4) + theta(5, ci[d-1], ci[i-1], ci[k-1], ci[j-1], ci[l-1])*φ(5) + theta(6, ci[d-1], ci[i-1], ci[k-1], ci[j-1], ci[l-1]) * φ(6) + + + ijdkl = list(permutations([12,13,14,15,16], 5)) + for i,j,d,k,l in ijdkl: + numerator = 0 + for jjj in range(2, 7): # 2, 3, 4, 5, 6 + numerator += theta_numerator(jjj, ci[d-1], ci[i-1], ci[k-1], ci[j-1], ci[l-1]) * φ(jjj) + #print(i,j,d,k,l) + b[0][i-1] = numerator / (ci[i-1] * (ci[i-1] - ci[k-1]) * (ci[i-1] - ci[j-1] * (ci[i-1] - ci[d-1]) * (ci[i-1] - ci[l-1]))) + + gen_first_col_exp(a, b, ci, φ) + + a = [[float(val) for val in row] for row in a] + b = [[float(val) for val in row] for row in b] + ci = [c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, c16] + + case "irk_exp_diag_2s": + c1 = 1/3 + c2 = 2/3 + c1 = float(get_extra_options_kv("c1", str(c1), extra_options)) + c2 = float(get_extra_options_kv("c2", str(c2), extra_options)) + + lam = (1 - torch.exp(-c1 * h)) / h + a2_1 = ( torch.exp(c2*h) - torch.exp(c1*h)) / (h * torch.exp(2*c1*h)) + b1 = (1 + c2*h + torch.exp(h) * (-1 + h - c2*h)) / ((c1-c2) * h**2 * torch.exp(c1*h)) + b2 = -(1 + c1*h - torch.exp(h) * ( 1 - h + c1*h)) / ((c1-c2) * h**2 * torch.exp(c2*h)) + + a = [ + [lam, 0], + [a2_1, lam], + ] + b = [ + [b1, b2], + ] + ci = [c1, c2] + + ci = ci[:] + #if rk_type.startswith("lob") == False: + ci.append(1) + + if EO("exp2lin_override_coeff") and is_exponential(rk_type): + a = scale_all(a, -sigma.item()) + b = scale_all(b, -sigma.item()) + + return a, b, u, v, ci, multistep_stages, hybrid_stages, FSAL + + +def scale_all(data, scalar): + if isinstance(data, torch.Tensor): + return data * scalar + elif isinstance(data, list): + return [scale_all(x, scalar) for x in data] + elif isinstance(data, (float, int)): + return data * scalar + else: + return data # passthrough unscaled if unknown type... or None, etc + + +def gen_first_col_exp(a, b, c, φ): + for i in range(len(c)): + a[i][0] = c[i] * φ(1,i+1) - sum(a[i]) + for i in range(len(b)): + b[i][0] = φ(1) - sum(b[i]) + return a, b + +def gen_first_col_exp_uv(a, b, c, u, v, φ): + for i in range(len(c)): + a[i][0] = c[i] * φ(1,i+1) - sum(a[i]) - sum(u[i]) + for i in range(len(b)): + b[i][0] = φ(1) - sum(b[i]) - sum(v[i]) + return a, b + +def rho(j, ci, ck, cl): + if j == 2: + numerator = ck*cl + if j == 3: + numerator = (-2 * (ck + cl)) + if j == 4: + numerator = 6 + return numerator / denominator(ci, ck, cl) + + +def mu(j, cd, ci, ck, cl): + if j == 2: + numerator = -cd * ck * cl + if j == 3: + numerator = 2 * (cd * ck + cd * cl + ck * cl) + if j == 4: + numerator = -6 * (cd + ck + cl) + if j == 5: + numerator = 24 + return numerator / denominator(ci, cd, ck, cl) + +def mu_numerator(j, cd, ci, ck, cl): + if j == 2: + numerator = -cd * ck * cl + if j == 3: + numerator = 2 * (cd * ck + cd * cl + ck * cl) + if j == 4: + numerator = -6 * (cd + ck + cl) + if j == 5: + numerator = 24 + return numerator #/ denominator(ci, cd, ck, cl) + + + +def theta_numerator(j, cd, ci, ck, cj, cl): + if j == 2: + numerator = -cj * cd * ck * cl + if j == 3: + numerator = 2 * (cj * ck * cd + cj*ck*cl + ck*cd*cl + cd*cl*cj) + if j == 4: + numerator = -6*(cj*ck + cj*cd + cj*cl + ck*cd + ck*cl + cd*cl) + if j == 5: + numerator = 24 * (cj + ck + cl + cd) + if j == 6: + numerator = -120 + return numerator # / denominator(ci, cj, ck, cl, cd) + + +def theta(j, cd, ci, ck, cj, cl): + if j == 2: + numerator = -cj * cd * ck * cl + if j == 3: + numerator = 2 * (cj * ck * cd + cj*ck*cl + ck*cd*cl + cd*cl*cj) + if j == 4: + numerator = -6*(cj*ck + cj*cd + cj*cl + ck*cd + ck*cl + cd*cl) + if j == 5: + numerator = 24 * (cj + ck + cl + cd) + if j == 6: + numerator = -120 + return numerator / ( ci * (ci - cj) * (ci - ck) * (ci - cl) * (ci - cd)) + return numerator / denominator(ci, cj, ck, cl, cd) + + +def prod_diff(cj, ck, cl=None, cd=None): + if cl is None and cd is None: + return cj * (cj - ck) + if cd is None: + return cj * (cj - ck) * (cj - cl) + else: + return cj * (cj - ck) * (cj - cl) * (cj - cd) + +def denominator(ci, *args): + result = ci + for arg in args: + result *= (ci - arg) + return result + + + +def check_condition_4_2(nodes): + + c12, c13, c14, c15 = nodes + + term_1 = (1 / 5) * (c12 + c13 + c14 + c15) + term_2 = (1 / 4) * (c12 * c13 + c12 * c14 + c12 * c15 + c13 * c14 + c13 * c15 + c14 * c15) + term_3 = (1 / 3) * (c12 * c13 * c14 + c12 * c13 * c15 + c12 * c14 * c15 + c13 * c14 * c15) + term_4 = (1 / 2) * (c12 * c13 * c14 * c15) + + result = term_1 - term_2 + term_3 - term_4 + + return abs(result - (1 / 6)) < 1e-6 + diff --git a/ComfyUI/custom_nodes/RES4LYF/beta/rk_guide_func_beta.py b/ComfyUI/custom_nodes/RES4LYF/beta/rk_guide_func_beta.py new file mode 100644 index 0000000000000000000000000000000000000000..573af28811ad503202597a5e84662dc12d2b0a9e --- /dev/null +++ b/ComfyUI/custom_nodes/RES4LYF/beta/rk_guide_func_beta.py @@ -0,0 +1,2352 @@ +import torch +import torch.nn.functional as F +from torch import Tensor + +import itertools +import copy + +from typing import Optional, Callable, Tuple, Dict, Any, Union, TYPE_CHECKING, TypeVar + +if TYPE_CHECKING: + from .noise_classes import NoiseGenerator + NoiseGeneratorSubclass = TypeVar("NoiseGeneratorSubclass", bound="NoiseGenerator") + +from einops import rearrange + +from ..sigmas import get_sigmas +from ..helper import ExtraOptions, FrameWeightsManager, initialize_or_scale, is_video_model +from ..latents import normalize_zscore, get_collinear, get_orthogonal, get_cosine_similarity, get_pearson_similarity, \ + get_slerp_weight_for_cossim, normalize_latent, hard_light_blend, slerp_tensor, get_orthogonal_noise_from_channelwise, get_edge_mask + +from .rk_method_beta import RK_Method_Beta +from .constants import MAX_STEPS + +from ..models import PRED + + +#from ..latents import hard_light_blend, normalize_latent + + + +class LatentGuide: + def __init__(self, + model, + sigmas : Tensor, + UNSAMPLE : bool, + VE_MODEL : bool, + LGW_MASK_RESCALE_MIN : bool, + extra_options : str, + device : str = 'cpu', + dtype : torch.dtype = torch.float64, + frame_weights_mgr : FrameWeightsManager = None, + ): + + self.dtype = dtype + self.device = device + self.model = model + + if hasattr(model, "model"): + model_sampling = model.model.model_sampling + elif hasattr(model, "inner_model"): + model_sampling = model.inner_model.inner_model.model_sampling + + self.sigma_min = model_sampling.sigma_min.to(dtype=dtype, device=device) + self.sigma_max = model_sampling.sigma_max.to(dtype=dtype, device=device) + self.sigmas = sigmas .to(dtype=dtype, device=device) + self.UNSAMPLE = UNSAMPLE + self.VE_MODEL = VE_MODEL + self.VIDEO = is_video_model(model) + self.SAMPLE = (sigmas[0] > sigmas[1]) # type torch.bool + self.y0 = None + self.y0_inv = None + self.y0_mean = None + self.y0_adain = None + self.y0_attninj = None + self.y0_style_pos = None + self.y0_style_neg = None + + self.guide_mode = "" + self.max_steps = MAX_STEPS + self.mask = None + self.mask_inv = None + self.mask_sync = None + self.mask_drift_x = None + self.mask_drift_y = None + self.mask_lure_x = None + self.mask_lure_y = None + self.mask_mean = None + self.mask_adain = None + self.mask_attninj = None + self.mask_style_pos = None + self.mask_style_neg = None + self.x_lying_ = None + self.s_lying_ = None + + self.LGW_MASK_RESCALE_MIN = LGW_MASK_RESCALE_MIN + self.HAS_LATENT_GUIDE = False + self.HAS_LATENT_GUIDE_INV = False + self.HAS_LATENT_GUIDE_MEAN = False + self.HAS_LATENT_GUIDE_ADAIN = False + self.HAS_LATENT_GUIDE_ATTNINJ = False + self.HAS_LATENT_GUIDE_STYLE_POS= False + self.HAS_LATENT_GUIDE_STYLE_NEG= False + + self.lgw = torch.full_like(sigmas, 0., dtype=dtype) + self.lgw_inv = torch.full_like(sigmas, 0., dtype=dtype) + self.lgw_mean = torch.full_like(sigmas, 0., dtype=dtype) + self.lgw_adain = torch.full_like(sigmas, 0., dtype=dtype) + self.lgw_attninj = torch.full_like(sigmas, 0., dtype=dtype) + self.lgw_style_pos = torch.full_like(sigmas, 0., dtype=dtype) + self.lgw_style_neg = torch.full_like(sigmas, 0., dtype=dtype) + + self.cossim_tgt = torch.full_like(sigmas, 0., dtype=dtype) + self.cossim_tgt_inv = torch.full_like(sigmas, 0., dtype=dtype) + + self.guide_cossim_cutoff_ = 1.0 + self.guide_bkg_cossim_cutoff_ = 1.0 + self.guide_mean_cossim_cutoff_ = 1.0 + self.guide_adain_cossim_cutoff_ = 1.0 + self.guide_attninj_cossim_cutoff_ = 1.0 + self.guide_style_pos_cossim_cutoff_= 1.0 + self.guide_style_neg_cossim_cutoff_= 1.0 + + self.frame_weights_mgr = frame_weights_mgr + self.frame_weights = None + self.frame_weights_inv = None + + #self.freqsep_lowpass_method = "none" + #self.freqsep_sigma = 0. + #self.freqsep_kernel_size = 0 + + self.extra_options = extra_options + self.EO = ExtraOptions(extra_options) + + + def init_guides(self, + x : Tensor, + RK_IMPLICIT : bool, + guides : Optional[Tensor] = None, + noise_sampler : Optional["NoiseGeneratorSubclass"] = None, + batch_num : int = 0, + sigma_init = None, + guide_inversion_y0 = None, + guide_inversion_y0_inv = None, + ) -> Tensor: + + latent_guide_weight = 0.0 + latent_guide_weight_inv = 0.0 + latent_guide_weight_sync = 0.0 + latent_guide_weight_sync_inv = 0.0 + latent_guide_weight_drift_x = 0.0 + latent_guide_weight_drift_x_inv = 0.0 + latent_guide_weight_drift_y = 0.0 + latent_guide_weight_drift_y_inv = 0.0 + latent_guide_weight_lure_x = 0.0 + latent_guide_weight_lure_x_inv = 0.0 + latent_guide_weight_lure_y = 0.0 + latent_guide_weight_lure_y_inv = 0.0 + + latent_guide_weight_mean = 0.0 + latent_guide_weight_adain = 0.0 + latent_guide_weight_attninj = 0.0 + latent_guide_weight_style_pos = 0.0 + latent_guide_weight_style_neg = 0.0 + + latent_guide_weights = torch.zeros_like(self.sigmas, dtype=self.dtype, device=self.device) + latent_guide_weights_inv = torch.zeros_like(self.sigmas, dtype=self.dtype, device=self.device) + latent_guide_weights_sync = torch.zeros_like(self.sigmas, dtype=self.dtype, device=self.device) + latent_guide_weights_sync_inv = torch.zeros_like(self.sigmas, dtype=self.dtype, device=self.device) + latent_guide_weights_drift_x = torch.zeros_like(self.sigmas, dtype=self.dtype, device=self.device) + latent_guide_weights_drift_x_inv = torch.zeros_like(self.sigmas, dtype=self.dtype, device=self.device) + latent_guide_weights_drift_y = torch.zeros_like(self.sigmas, dtype=self.dtype, device=self.device) + latent_guide_weights_drift_y_inv = torch.zeros_like(self.sigmas, dtype=self.dtype, device=self.device) + latent_guide_weights_lure_x = torch.zeros_like(self.sigmas, dtype=self.dtype, device=self.device) + latent_guide_weights_lure_x_inv = torch.zeros_like(self.sigmas, dtype=self.dtype, device=self.device) + latent_guide_weights_lure_y = torch.zeros_like(self.sigmas, dtype=self.dtype, device=self.device) + latent_guide_weights_lure_y_inv = torch.zeros_like(self.sigmas, dtype=self.dtype, device=self.device) + latent_guide_weights_mean = torch.zeros_like(self.sigmas, dtype=self.dtype, device=self.device) + latent_guide_weights_adain = torch.zeros_like(self.sigmas, dtype=self.dtype, device=self.device) + latent_guide_weights_attninj = torch.zeros_like(self.sigmas, dtype=self.dtype, device=self.device) + latent_guide_weights_style_pos = torch.zeros_like(self.sigmas, dtype=self.dtype, device=self.device) + latent_guide_weights_style_neg = torch.zeros_like(self.sigmas, dtype=self.dtype, device=self.device) + + latent_guide = None + latent_guide_inv = None + latent_guide_mean = None + latent_guide_adain = None + latent_guide_attninj = None + latent_guide_style_pos = None + latent_guide_style_neg = None + + self.drift_x_data = 0.0 + self.drift_x_sync = 0.0 + self.drift_y_data = 0.0 + self.drift_y_sync = 0.0 + self.drift_y_guide = 0.0 + + if guides is not None: + self.guide_mode = guides.get("guide_mode", "none") + + if self.guide_mode.startswith("inversion"): + self.guide_mode = self.guide_mode.replace("inversion", "epsilon", 1) + else: + self.SAMPLE = True + self.UNSAMPLE = False + + latent_guide_weight = guides.get("weight_masked", 0.) + latent_guide_weight_inv = guides.get("weight_unmasked", 0.) + latent_guide_weight_sync = guides.get("weight_masked_sync", 0.) + latent_guide_weight_sync_inv = guides.get("weight_unmasked_sync", 0.) + latent_guide_weight_drift_x = guides.get("weight_masked_drift_x", 0.) + latent_guide_weight_drift_x_inv = guides.get("weight_unmasked_drift_x", 0.) + latent_guide_weight_drift_y = guides.get("weight_masked_drift_y", 0.) + latent_guide_weight_drift_y_inv = guides.get("weight_unmasked_drift_y", 0.) + latent_guide_weight_lure_x = guides.get("weight_masked_lure_x", 0.) + latent_guide_weight_lure_x_inv = guides.get("weight_unmasked_lure_x", 0.) + latent_guide_weight_lure_y = guides.get("weight_masked_lure_y", 0.) + latent_guide_weight_lure_y_inv = guides.get("weight_unmasked_lure_y", 0.) + latent_guide_weight_mean = guides.get("weight_mean", 0.) + latent_guide_weight_adain = guides.get("weight_adain", 0.) + latent_guide_weight_attninj = guides.get("weight_attninj", 0.) + latent_guide_weight_style_pos = guides.get("weight_style_pos", 0.) + latent_guide_weight_style_neg = guides.get("weight_style_neg", 0.) + #latent_guide_synweight_style_pos = guides.get("synweight_style_pos", 0.) + #latent_guide_synweight_style_neg = guides.get("synweight_style_neg", 0.) + + self.drift_x_data = guides.get("drift_x_data", 0.) + self.drift_x_sync = guides.get("drift_x_sync", 0.) + self.drift_y_data = guides.get("drift_y_data", 0.) + self.drift_y_sync = guides.get("drift_y_sync", 0.) + self.drift_y_guide = guides.get("drift_y_guide", 0.) + + latent_guide_weights = guides.get("weights_masked") + latent_guide_weights_inv = guides.get("weights_unmasked") + latent_guide_weights_sync = guides.get("weights_masked_sync") + latent_guide_weights_sync_inv = guides.get("weights_unmasked_sync") + latent_guide_weights_drift_x = guides.get("weights_masked_drift_x") + latent_guide_weights_drift_x_inv = guides.get("weights_unmasked_drift_x") + latent_guide_weights_drift_y = guides.get("weights_masked_drift_y") + latent_guide_weights_drift_y_inv = guides.get("weights_unmasked_drift_y") + latent_guide_weights_lure_x = guides.get("weights_masked_lure_x") + latent_guide_weights_lure_x_inv = guides.get("weights_unmasked_lure_x") + latent_guide_weights_lure_y = guides.get("weights_masked_lure_y") + latent_guide_weights_lure_y_inv = guides.get("weights_unmasked_lure_y") + latent_guide_weights_mean = guides.get("weights_mean") + latent_guide_weights_adain = guides.get("weights_adain") + latent_guide_weights_attninj = guides.get("weights_attninj") + latent_guide_weights_style_pos = guides.get("weights_style_pos") + latent_guide_weights_style_neg = guides.get("weights_style_neg") + #latent_guide_synweights_style_p os = guides.get("synweights_style_pos") + #latent_guide_synweights_style_neg = guides.get("synweights_style_neg") + + latent_guide = guides.get("guide_masked") + latent_guide_inv = guides.get("guide_unmasked") + latent_guide_mean = guides.get("guide_mean") + latent_guide_adain = guides.get("guide_adain") + latent_guide_attninj = guides.get("guide_attninj") + latent_guide_style_pos = guides.get("guide_style_pos") + latent_guide_style_neg = guides.get("guide_style_neg") + + self.mask = guides.get("mask") + self.mask_inv = guides.get("unmask") + self.mask_sync = guides.get("mask_sync") + self.mask_drift_x = guides.get("mask_drift_x") + self.mask_drift_y = guides.get("mask_drift_y") + self.mask_lure_x = guides.get("mask_lure_x") + self.mask_lure_y = guides.get("mask_lure_y") + self.mask_mean = guides.get("mask_mean") + self.mask_adain = guides.get("mask_adain") + self.mask_attninj = guides.get("mask_attninj") + self.mask_style_pos = guides.get("mask_style_pos") + self.mask_style_neg = guides.get("mask_style_neg") + + scheduler_ = guides.get("weight_scheduler_masked") + scheduler_inv_ = guides.get("weight_scheduler_unmasked") + scheduler_sync_ = guides.get("weight_scheduler_masked_sync") + scheduler_sync_inv_ = guides.get("weight_scheduler_unmasked_sync") + scheduler_drift_x_ = guides.get("weight_scheduler_masked_drift_x") + scheduler_drift_x_inv_ = guides.get("weight_scheduler_unmasked_drift_x") + scheduler_drift_y_ = guides.get("weight_scheduler_masked_drift_y") + scheduler_drift_y_inv_ = guides.get("weight_scheduler_unmasked_drift_y") + scheduler_lure_x_ = guides.get("weight_scheduler_masked_lure_x") + scheduler_lure_x_inv_ = guides.get("weight_scheduler_unmasked_lure_x") + scheduler_lure_y_ = guides.get("weight_scheduler_masked_lure_y") + scheduler_lure_y_inv_ = guides.get("weight_scheduler_unmasked_lure_y") + scheduler_mean_ = guides.get("weight_scheduler_mean") + scheduler_adain_ = guides.get("weight_scheduler_adain") + scheduler_attninj_ = guides.get("weight_scheduler_attninj") + scheduler_style_pos_ = guides.get("weight_scheduler_style_pos") + scheduler_style_neg_ = guides.get("weight_scheduler_style_neg") + + start_steps_ = guides.get("start_step_masked", 0) + start_steps_inv_ = guides.get("start_step_unmasked", 0) + start_steps_sync_ = guides.get("start_step_masked_sync", 0) + start_steps_sync_inv_ = guides.get("start_step_unmasked_sync", 0) + start_steps_drift_x_ = guides.get("start_step_masked_drift_x", 0) + start_steps_drift_x_inv_ = guides.get("start_step_unmasked_drift_x", 0) + start_steps_drift_y_ = guides.get("start_step_masked_drift_y", 0) + start_steps_drift_y_inv_ = guides.get("start_step_unmasked_drift_y", 0) + start_steps_lure_x_ = guides.get("start_step_masked_lure_x", 0) + start_steps_lure_x_inv_ = guides.get("start_step_unmasked_lure_x", 0) + start_steps_lure_y_ = guides.get("start_step_masked_lure_y", 0) + start_steps_lure_y_inv_ = guides.get("start_step_unmasked_lure_y", 0) + start_steps_mean_ = guides.get("start_step_mean", 0) + start_steps_adain_ = guides.get("start_step_adain", 0) + start_steps_attninj_ = guides.get("start_step_attninj", 0) + start_steps_style_pos_ = guides.get("start_step_style_pos", 0) + start_steps_style_neg_ = guides.get("start_step_style_neg", 0) + + steps_ = guides.get("end_step_masked", 1) + steps_inv_ = guides.get("end_step_unmasked", 1) + steps_sync_ = guides.get("end_step_masked_sync", 1) + steps_sync_inv_ = guides.get("end_step_unmasked_sync", 1) + steps_drift_x_ = guides.get("end_step_masked_drift_x", 1) + steps_drift_x_inv_ = guides.get("end_step_unmasked_drift_x", 1) + steps_drift_y_ = guides.get("end_step_masked_drift_y", 1) + steps_drift_y_inv_ = guides.get("end_step_unmasked_drift_y", 1) + steps_lure_x_ = guides.get("end_step_masked_lure_x", 1) + steps_lure_x_inv_ = guides.get("end_step_unmasked_lure_x", 1) + steps_lure_y_ = guides.get("end_step_masked_lure_y", 1) + steps_lure_y_inv_ = guides.get("end_step_unmasked_lure_y", 1) + + steps_mean_ = guides.get("end_step_mean", 1) + steps_adain_ = guides.get("end_step_adain", 1) + steps_attninj_ = guides.get("end_step_attninj", 1) + steps_style_pos_ = guides.get("end_step_style_pos", 1) + steps_style_neg_ = guides.get("end_step_style_neg", 1) + + self.guide_cossim_cutoff_ = guides.get("cutoff_masked", 1.) + self.guide_bkg_cossim_cutoff_ = guides.get("cutoff_unmasked", 1.) + self.guide_mean_cossim_cutoff_ = guides.get("cutoff_mean", 1.) + self.guide_adain_cossim_cutoff_ = guides.get("cutoff_adain", 1.) + self.guide_attninj_cossim_cutoff_ = guides.get("cutoff_attninj", 1.) + self.guide_style_pos_cossim_cutoff_ = guides.get("cutoff_style_pos", 1.) + self.guide_style_neg_cossim_cutoff_ = guides.get("cutoff_style_neg", 1.) + + self.sync_lure_iter = guides.get("sync_lure_iter", 0) + self.sync_lure_sequence = guides.get("sync_lure_sequence") + + #self.SYNC_SEPARATE = False + #if scheduler_sync_ is not None: + # self.SYNC_SEPARATE = True + self.SYNC_SEPARATE = True + if scheduler_sync_ is None and scheduler_ is not None: + + latent_guide_weight_sync = latent_guide_weight + latent_guide_weight_sync_inv = latent_guide_weight_inv + latent_guide_weights_sync = latent_guide_weights + latent_guide_weights_sync_inv = latent_guide_weights_inv + + scheduler_sync_ = scheduler_ + scheduler_sync_inv_ = scheduler_inv_ + + start_steps_sync_ = start_steps_ + start_steps_sync_inv_ = start_steps_inv_ + + steps_sync_ = steps_ + steps_sync_inv_ = steps_inv_ + + self.SYNC_drift_X = True + if scheduler_drift_x_ is None and scheduler_ is not None: + self.SYNC_drift_X = False + + latent_guide_weight_drift_x = latent_guide_weight + latent_guide_weight_drift_x_inv = latent_guide_weight_inv + latent_guide_weights_drift_x = latent_guide_weights + latent_guide_weights_drift_x_inv = latent_guide_weights_inv + + scheduler_drift_x_ = scheduler_ + scheduler_drift_x_inv_ = scheduler_inv_ + + start_steps_drift_x_ = start_steps_ + start_steps_drift_x_inv_ = start_steps_inv_ + + steps_drift_x_ = steps_ + steps_drift_x_inv_ = steps_inv_ + + self.SYNC_drift_Y = True + if scheduler_drift_y_ is None and scheduler_ is not None: + self.SYNC_drift_Y = False + + latent_guide_weight_drift_y = latent_guide_weight + latent_guide_weight_drift_y_inv = latent_guide_weight_inv + latent_guide_weights_drift_y = latent_guide_weights + latent_guide_weights_drift_y_inv = latent_guide_weights_inv + + scheduler_drift_y_ = scheduler_ + scheduler_drift_y_inv_ = scheduler_inv_ + + start_steps_drift_y_ = start_steps_ + start_steps_drift_y_inv_ = start_steps_inv_ + + steps_drift_y_ = steps_ + steps_drift_y_inv_ = steps_inv_ + + self.SYNC_LURE_X = True + if scheduler_lure_x_ is None and scheduler_ is not None: + self.SYNC_LURE_X = False + + latent_guide_weight_lure_x = latent_guide_weight + latent_guide_weight_lure_x_inv = latent_guide_weight_inv + latent_guide_weights_lure_x = latent_guide_weights + latent_guide_weights_lure_x_inv = latent_guide_weights_inv + + scheduler_lure_x_ = scheduler_ + scheduler_lure_x_inv_ = scheduler_inv_ + + start_steps_lure_x_ = start_steps_ + start_steps_lure_x_inv_ = start_steps_inv_ + + steps_lure_x_ = steps_ + steps_lure_x_inv_ = steps_inv_ + + self.SYNC_LURE_Y = True + if scheduler_lure_y_ is None and scheduler_ is not None: + self.SYNC_LURE_Y = False + + latent_guide_weight_lure_y = latent_guide_weight + latent_guide_weight_lure_y_inv = latent_guide_weight_inv + latent_guide_weights_lure_y = latent_guide_weights + latent_guide_weights_lure_y_inv = latent_guide_weights_inv + + scheduler_lure_y_ = scheduler_ + scheduler_lure_y_inv_ = scheduler_inv_ + + start_steps_lure_y_ = start_steps_ + start_steps_lure_y_inv_ = start_steps_inv_ + + steps_lure_y_ = steps_ + steps_lure_y_inv_ = steps_inv_ + + if self.mask is not None and self.mask.shape [0] > 1 and self.VIDEO is False: + self.mask = self.mask [batch_num].unsqueeze(0) + if self.mask_inv is not None and self.mask_inv.shape[0] > 1 and self.VIDEO is False: + self.mask_inv = self.mask_inv[batch_num].unsqueeze(0) + if self.mask_sync is not None and self.mask_sync.shape[0] > 1 and self.VIDEO is False: + self.mask_sync = self.mask_sync[batch_num].unsqueeze(0) + if self.mask_drift_x is not None and self.mask_drift_x.shape[0] > 1 and self.VIDEO is False: + self.mask_drift_x = self.mask_drift_x[batch_num].unsqueeze(0) + if self.mask_drift_y is not None and self.mask_drift_y.shape[0] > 1 and self.VIDEO is False: + self.mask_drift_y = self.mask_drift_y[batch_num].unsqueeze(0) + if self.mask_lure_x is not None and self.mask_lure_x.shape[0] > 1 and self.VIDEO is False: + self.mask_lure_x = self.mask_lure_x[batch_num].unsqueeze(0) + if self.mask_lure_y is not None and self.mask_lure_y.shape[0] > 1 and self.VIDEO is False: + self.mask_lure_y = self.mask_lure_y[batch_num].unsqueeze(0) + + if self.guide_mode.startswith("fully_") and not RK_IMPLICIT: + self.guide_mode = self.guide_mode[6:] # fully_pseudoimplicit is only supported for implicit samplers, default back to pseudoimplicit + + guide_sigma_shift = self.EO("guide_sigma_shift", 0.0) # effectively hardcoding shift to 0 !!!!!! + + if latent_guide_weights is None and scheduler_ is not None: + total_steps = steps_ - start_steps_ + latent_guide_weights = get_sigmas(self.model, scheduler_, total_steps, 1.0, shift=guide_sigma_shift).to(dtype=self.dtype, device=self.device) / self.sigma_max + prepend = torch.zeros(start_steps_, dtype=self.dtype, device=self.device) + latent_guide_weights = torch.cat((prepend, latent_guide_weights.to(self.device)), dim=0) + + if latent_guide_weights_inv is None and scheduler_inv_ is not None: + total_steps = steps_inv_ - start_steps_inv_ + latent_guide_weights_inv = get_sigmas(self.model, scheduler_inv_, total_steps, 1.0, shift=guide_sigma_shift).to(dtype=self.dtype, device=self.device) / self.sigma_max + prepend = torch.zeros(start_steps_inv_, dtype=self.dtype, device=self.device) + latent_guide_weights_inv = torch.cat((prepend, latent_guide_weights_inv.to(self.device)), dim=0) + + if latent_guide_weights_sync is None and scheduler_sync_ is not None: + total_steps = steps_sync_ - start_steps_sync_ + latent_guide_weights_sync = get_sigmas(self.model, scheduler_sync_, total_steps, 1.0, shift=guide_sigma_shift).to(dtype=self.dtype, device=self.device) / self.sigma_max + prepend = torch.zeros(start_steps_sync_, dtype=self.dtype, device=self.device) + latent_guide_weights_sync = torch.cat((prepend, latent_guide_weights_sync.to(self.device)), dim=0) + + if latent_guide_weights_sync_inv is None and scheduler_sync_inv_ is not None: + total_steps = steps_sync_inv_ - start_steps_sync_inv_ + latent_guide_weights_sync_inv = get_sigmas(self.model, scheduler_sync_inv_, total_steps, 1.0, shift=guide_sigma_shift).to(dtype=self.dtype, device=self.device) / self.sigma_max + prepend = torch.zeros(start_steps_sync_inv_, dtype=self.dtype, device=self.device) + latent_guide_weights_sync_inv = torch.cat((prepend, latent_guide_weights_sync_inv.to(self.device)), dim=0) + + if latent_guide_weights_drift_x is None and scheduler_drift_x_ is not None: + total_steps = steps_drift_x_ - start_steps_drift_x_ + latent_guide_weights_drift_x = get_sigmas(self.model, scheduler_drift_x_, total_steps, 1.0, shift=guide_sigma_shift).to(dtype=self.dtype, device=self.device) / self.sigma_max + prepend = torch.zeros(start_steps_drift_x_, dtype=self.dtype, device=self.device) + latent_guide_weights_drift_x = torch.cat((prepend, latent_guide_weights_drift_x.to(self.device)), dim=0) + + if latent_guide_weights_drift_x_inv is None and scheduler_drift_x_inv_ is not None: + total_steps = steps_drift_x_inv_ - start_steps_drift_x_inv_ + latent_guide_weights_drift_x_inv = get_sigmas(self.model, scheduler_drift_x_inv_, total_steps, 1.0, shift=guide_sigma_shift).to(dtype=self.dtype, device=self.device) / self.sigma_max + prepend = torch.zeros(start_steps_drift_x_inv_, dtype=self.dtype, device=self.device) + latent_guide_weights_drift_x_inv = torch.cat((prepend, latent_guide_weights_drift_x_inv.to(self.device)), dim=0) + + if latent_guide_weights_drift_y is None and scheduler_drift_y_ is not None: + total_steps = steps_drift_y_ - start_steps_drift_y_ + latent_guide_weights_drift_y = get_sigmas(self.model, scheduler_drift_y_, total_steps, 1.0, shift=guide_sigma_shift).to(dtype=self.dtype, device=self.device) / self.sigma_max + prepend = torch.zeros(start_steps_drift_y_, dtype=self.dtype, device=self.device) + latent_guide_weights_drift_y = torch.cat((prepend, latent_guide_weights_drift_y.to(self.device)), dim=0) + + if latent_guide_weights_drift_y_inv is None and scheduler_drift_y_inv_ is not None: + total_steps = steps_drift_y_inv_ - start_steps_drift_y_inv_ + latent_guide_weights_drift_y_inv = get_sigmas(self.model, scheduler_drift_y_inv_, total_steps, 1.0, shift=guide_sigma_shift).to(dtype=self.dtype, device=self.device) / self.sigma_max + prepend = torch.zeros(start_steps_drift_y_inv_, dtype=self.dtype, device=self.device) + latent_guide_weights_drift_y_inv = torch.cat((prepend, latent_guide_weights_drift_y_inv.to(self.device)), dim=0) + + if latent_guide_weights_lure_x is None and scheduler_lure_x_ is not None: + total_steps = steps_lure_x_ - start_steps_lure_x_ + latent_guide_weights_lure_x = get_sigmas(self.model, scheduler_lure_x_, total_steps, 1.0, shift=guide_sigma_shift).to(dtype=self.dtype, device=self.device) / self.sigma_max + prepend = torch.zeros(start_steps_lure_x_, dtype=self.dtype, device=self.device) + latent_guide_weights_lure_x = torch.cat((prepend, latent_guide_weights_lure_x.to(self.device)), dim=0) + + if latent_guide_weights_lure_x_inv is None and scheduler_lure_x_inv_ is not None: + total_steps = steps_lure_x_inv_ - start_steps_lure_x_inv_ + latent_guide_weights_lure_x_inv = get_sigmas(self.model, scheduler_lure_x_inv_, total_steps, 1.0, shift=guide_sigma_shift).to(dtype=self.dtype, device=self.device) / self.sigma_max + prepend = torch.zeros(start_steps_lure_x_inv_, dtype=self.dtype, device=self.device) + latent_guide_weights_lure_x_inv = torch.cat((prepend, latent_guide_weights_lure_x_inv.to(self.device)), dim=0) + + if latent_guide_weights_lure_y is None and scheduler_lure_y_ is not None: + total_steps = steps_lure_y_ - start_steps_lure_y_ + latent_guide_weights_lure_y = get_sigmas(self.model, scheduler_lure_y_, total_steps, 1.0, shift=guide_sigma_shift).to(dtype=self.dtype, device=self.device) / self.sigma_max + prepend = torch.zeros(start_steps_lure_y_, dtype=self.dtype, device=self.device) + latent_guide_weights_lure_y = torch.cat((prepend, latent_guide_weights_lure_y.to(self.device)), dim=0) + + if latent_guide_weights_lure_y_inv is None and scheduler_lure_y_inv_ is not None: + total_steps = steps_lure_y_inv_ - start_steps_lure_y_inv_ + latent_guide_weights_lure_y_inv = get_sigmas(self.model, scheduler_lure_y_inv_, total_steps, 1.0, shift=guide_sigma_shift).to(dtype=self.dtype, device=self.device) / self.sigma_max + prepend = torch.zeros(start_steps_lure_y_inv_, dtype=self.dtype, device=self.device) + latent_guide_weights_lure_y_inv = torch.cat((prepend, latent_guide_weights_lure_y_inv.to(self.device)), dim=0) + + + if latent_guide_weights_mean is None and scheduler_mean_ is not None: + total_steps = steps_mean_ - start_steps_mean_ + latent_guide_weights_mean = get_sigmas(self.model, scheduler_mean_, total_steps, 1.0, shift=guide_sigma_shift).to(dtype=self.dtype, device=self.device) / self.sigma_max + prepend = torch.zeros(start_steps_mean_, dtype=self.dtype, device=self.device) + latent_guide_weights_mean = torch.cat((prepend, latent_guide_weights_mean.to(self.device)), dim=0) + + if latent_guide_weights_adain is None and scheduler_adain_ is not None: + total_steps = steps_adain_ - start_steps_adain_ + latent_guide_weights_adain = get_sigmas(self.model, scheduler_adain_, total_steps, 1.0, shift=guide_sigma_shift).to(dtype=self.dtype, device=self.device) / self.sigma_max + prepend = torch.zeros(start_steps_adain_, dtype=self.dtype, device=self.device) + latent_guide_weights_adain = torch.cat((prepend, latent_guide_weights_adain.to(self.device)), dim=0) + + if latent_guide_weights_attninj is None and scheduler_attninj_ is not None: + total_steps = steps_attninj_ - start_steps_attninj_ + latent_guide_weights_attninj = get_sigmas(self.model, scheduler_attninj_, total_steps, 1.0, shift=guide_sigma_shift).to(dtype=self.dtype, device=self.device) / self.sigma_max + prepend = torch.zeros(start_steps_attninj_, dtype=self.dtype, device=self.device) + latent_guide_weights_attninj = torch.cat((prepend, latent_guide_weights_attninj.to(self.device)), dim=0) + + if latent_guide_weights_style_pos is None and scheduler_style_pos_ is not None: + total_steps = steps_style_pos_ - start_steps_style_pos_ + latent_guide_weights_style_pos = get_sigmas(self.model, scheduler_style_pos_, total_steps, 1.0, shift=guide_sigma_shift).to(dtype=self.dtype, device=self.device) / self.sigma_max + prepend = torch.zeros(start_steps_style_pos_, dtype=self.dtype, device=self.device) + latent_guide_weights_style_pos = torch.cat((prepend, latent_guide_weights_style_pos.to(self.device)), dim=0) + + if latent_guide_weights_style_neg is None and scheduler_style_neg_ is not None: + total_steps = steps_style_neg_ - start_steps_style_neg_ + latent_guide_weights_style_neg = get_sigmas(self.model, scheduler_style_neg_, total_steps, 1.0, shift=guide_sigma_shift).to(dtype=self.dtype, device=self.device) / self.sigma_max + prepend = torch.zeros(start_steps_style_neg_, dtype=self.dtype, device=self.device) + latent_guide_weights_style_neg = torch.cat((prepend, latent_guide_weights_style_neg.to(self.device)), dim=0) + + if scheduler_ != "constant": + latent_guide_weights = initialize_or_scale(latent_guide_weights, latent_guide_weight, self.max_steps) + if scheduler_inv_ != "constant": + latent_guide_weights_inv = initialize_or_scale(latent_guide_weights_inv, latent_guide_weight_inv, self.max_steps) + if scheduler_sync_ != "constant": + latent_guide_weights_sync = initialize_or_scale(latent_guide_weights_sync, latent_guide_weight_sync, self.max_steps) + if scheduler_sync_inv_ != "constant": + latent_guide_weights_sync_inv = initialize_or_scale(latent_guide_weights_sync_inv, latent_guide_weight_sync_inv, self.max_steps) + + latent_guide_weights_sync = 1 - latent_guide_weights_sync if latent_guide_weights_sync is not None else latent_guide_weights + latent_guide_weights_sync_inv = 1 - latent_guide_weights_sync_inv if latent_guide_weights_sync_inv is not None else latent_guide_weights_inv + latent_guide_weight_sync = 1 - latent_guide_weight_sync + latent_guide_weight_sync_inv = 1 - latent_guide_weight_sync_inv# these are more intuitive to use if these are reversed... so that sync weight = 1.0 means "maximum guide strength" + + + if scheduler_drift_x_ != "constant": + latent_guide_weights_drift_x = initialize_or_scale(latent_guide_weights_drift_x, latent_guide_weight_drift_x, self.max_steps) + if scheduler_drift_x_inv_ != "constant": + latent_guide_weights_drift_x_inv = initialize_or_scale(latent_guide_weights_drift_x_inv, latent_guide_weight_drift_x_inv, self.max_steps) + if scheduler_drift_y_ != "constant": + latent_guide_weights_drift_y = initialize_or_scale(latent_guide_weights_drift_y, latent_guide_weight_drift_y, self.max_steps) + if scheduler_drift_y_inv_ != "constant": + latent_guide_weights_drift_y_inv = initialize_or_scale(latent_guide_weights_drift_y_inv, latent_guide_weight_drift_y_inv, self.max_steps) + if scheduler_lure_x_ != "constant": + latent_guide_weights_lure_x = initialize_or_scale(latent_guide_weights_lure_x, latent_guide_weight_lure_x, self.max_steps) + if scheduler_lure_x_inv_ != "constant": + latent_guide_weights_lure_x_inv = initialize_or_scale(latent_guide_weights_lure_x_inv, latent_guide_weight_lure_x_inv, self.max_steps) + if scheduler_lure_y_ != "constant": + latent_guide_weights_lure_y = initialize_or_scale(latent_guide_weights_lure_y, latent_guide_weight_lure_y, self.max_steps) + if scheduler_lure_y_inv_ != "constant": + latent_guide_weights_lure_y_inv = initialize_or_scale(latent_guide_weights_lure_y_inv, latent_guide_weight_lure_y_inv, self.max_steps) + if scheduler_mean_ != "constant": + latent_guide_weights_mean = initialize_or_scale(latent_guide_weights_mean, latent_guide_weight_mean, self.max_steps) + if scheduler_adain_ != "constant": + latent_guide_weights_adain = initialize_or_scale(latent_guide_weights_adain, latent_guide_weight_adain, self.max_steps) + if scheduler_attninj_ != "constant": + latent_guide_weights_attninj = initialize_or_scale(latent_guide_weights_attninj, latent_guide_weight_attninj, self.max_steps) + if scheduler_style_pos_ != "constant": + latent_guide_weights_style_pos = initialize_or_scale(latent_guide_weights_style_pos, latent_guide_weight_style_pos, self.max_steps) + if scheduler_style_neg_ != "constant": + latent_guide_weights_style_neg = initialize_or_scale(latent_guide_weights_style_neg, latent_guide_weight_style_neg, self.max_steps) + + latent_guide_weights [steps_ :] = 0 + latent_guide_weights_inv [steps_inv_ :] = 0 + latent_guide_weights_sync [steps_sync_ :] = 1 #one + latent_guide_weights_sync_inv [steps_sync_inv_ :] = 1 #one + latent_guide_weights_drift_x [steps_drift_x_ :] = 0 + latent_guide_weights_drift_x_inv[steps_drift_x_inv_:] = 0 + latent_guide_weights_drift_y [steps_drift_y_ :] = 0 + latent_guide_weights_drift_y_inv[steps_drift_y_inv_:] = 0 + latent_guide_weights_lure_x [steps_lure_x_ :] = 0 + latent_guide_weights_lure_x_inv [steps_lure_x_inv_ :] = 0 + latent_guide_weights_lure_y [steps_lure_y_ :] = 0 + latent_guide_weights_lure_y_inv [steps_lure_y_inv_ :] = 0 + latent_guide_weights_mean [steps_mean_ :] = 0 + latent_guide_weights_adain [steps_adain_ :] = 0 + latent_guide_weights_attninj [steps_attninj_ :] = 0 + latent_guide_weights_style_pos [steps_style_pos_ :] = 0 + latent_guide_weights_style_neg [steps_style_neg_ :] = 0 + + self.lgw = F.pad(latent_guide_weights, (0, self.max_steps), value=0.0) + self.lgw_inv = F.pad(latent_guide_weights_inv, (0, self.max_steps), value=0.0) + self.lgw_sync = F.pad(latent_guide_weights_sync, (0, self.max_steps), value=1.0) #one + self.lgw_sync_inv = F.pad(latent_guide_weights_sync_inv, (0, self.max_steps), value=1.0) #one + self.lgw_drift_x = F.pad(latent_guide_weights_drift_x, (0, self.max_steps), value=0.0) + self.lgw_drift_x_inv = F.pad(latent_guide_weights_drift_x_inv, (0, self.max_steps), value=0.0) + self.lgw_drift_y = F.pad(latent_guide_weights_drift_y, (0, self.max_steps), value=0.0) + self.lgw_drift_y_inv = F.pad(latent_guide_weights_drift_y_inv, (0, self.max_steps), value=0.0) + self.lgw_lure_x = F.pad(latent_guide_weights_lure_x, (0, self.max_steps), value=0.0) + self.lgw_lure_x_inv = F.pad(latent_guide_weights_lure_x_inv, (0, self.max_steps), value=0.0) + self.lgw_lure_y = F.pad(latent_guide_weights_lure_y, (0, self.max_steps), value=0.0) + self.lgw_lure_y_inv = F.pad(latent_guide_weights_lure_y_inv, (0, self.max_steps), value=0.0) + self.lgw_mean = F.pad(latent_guide_weights_mean, (0, self.max_steps), value=0.0) + self.lgw_adain = F.pad(latent_guide_weights_adain, (0, self.max_steps), value=0.0) + self.lgw_attninj = F.pad(latent_guide_weights_attninj, (0, self.max_steps), value=0.0) + self.lgw_style_pos = F.pad(latent_guide_weights_style_pos, (0, self.max_steps), value=0.0) + self.lgw_style_neg = F.pad(latent_guide_weights_style_neg, (0, self.max_steps), value=0.0) + + mask, self.LGW_MASK_RESCALE_MIN = prepare_mask(x, self.mask, self.LGW_MASK_RESCALE_MIN) + self.mask = mask.to(dtype=self.dtype, device=self.device) + + if self.mask_inv is not None: + mask_inv, self.LGW_MASK_RESCALE_MIN = prepare_mask(x, self.mask_inv, self.LGW_MASK_RESCALE_MIN) + self.mask_inv = mask_inv.to(dtype=self.dtype, device=self.device) + else: + self.mask_inv = (1-self.mask) + + if self.mask_sync is not None: + mask_sync, self.LGW_MASK_RESCALE_MIN = prepare_mask(x, self.mask_sync, self.LGW_MASK_RESCALE_MIN) + self.mask_sync = mask_sync.to(dtype=self.dtype, device=self.device) + else: + self.mask_sync = self.mask + + if self.mask_drift_x is not None: + mask_drift_x, self.LGW_MASK_RESCALE_MIN = prepare_mask(x, self.mask_drift_x, self.LGW_MASK_RESCALE_MIN) + self.mask_drift_x = mask_drift_x.to(dtype=self.dtype, device=self.device) + else: + self.mask_drift_x = self.mask + + if self.mask_drift_y is not None: + mask_drift_y, self.LGW_MASK_RESCALE_MIN = prepare_mask(x, self.mask_drift_y, self.LGW_MASK_RESCALE_MIN) + self.mask_drift_y = mask_drift_y.to(dtype=self.dtype, device=self.device) + else: + self.mask_drift_y = self.mask + + if self.mask_lure_x is not None: + mask_lure_x, self.LGW_MASK_RESCALE_MIN = prepare_mask(x, self.mask_lure_x, self.LGW_MASK_RESCALE_MIN) + self.mask_lure_x = mask_lure_x.to(dtype=self.dtype, device=self.device) + else: + self.mask_lure_x = self.mask + + if self.mask_lure_y is not None: + mask_lure_y, self.LGW_MASK_RESCALE_MIN = prepare_mask(x, self.mask_lure_y, self.LGW_MASK_RESCALE_MIN) + self.mask_lure_y = mask_lure_y.to(dtype=self.dtype, device=self.device) + else: + self.mask_lure_y = self.mask + + mask_style_pos, self.LGW_MASK_RESCALE_MIN = prepare_mask(x, self.mask_style_pos, self.LGW_MASK_RESCALE_MIN) + self.mask_style_pos = mask_style_pos.to(dtype=self.dtype, device=self.device) + + + mask_style_neg, self.LGW_MASK_RESCALE_MIN = prepare_mask(x, self.mask_style_neg, self.LGW_MASK_RESCALE_MIN) + self.mask_style_neg = mask_style_neg.to(dtype=self.dtype, device=self.device) + + if latent_guide is not None: + self.HAS_LATENT_GUIDE = True + if type(latent_guide) is dict: + if latent_guide ['samples'].shape[0] > 1: + latent_guide['samples'] = latent_guide ['samples'][batch_num].unsqueeze(0) + latent_guide_samples = self.model.inner_model.inner_model.process_latent_in(latent_guide['samples']).clone().to(dtype=self.dtype, device=self.device) + elif type(latent_guide) is torch.Tensor: + latent_guide_samples = latent_guide.to(dtype=self.dtype, device=self.device) + else: + raise ValueError(f"Invalid latent type: {type(latent_guide)}") + + if self.VIDEO and latent_guide_samples.shape[2] == 1: + latent_guide_samples = latent_guide_samples.repeat(1, 1, x.shape[2], 1, 1) + + if self.SAMPLE: + self.y0 = latent_guide_samples + elif sigma_init != 0.0: + pass + elif self.UNSAMPLE: # and self.mask is not None: + mask = self.mask.to(x.device) + x = (1-mask) * x + mask * latent_guide_samples.to(x.device) + else: + x = latent_guide_samples.to(x.device) + else: + self.y0 = torch.zeros_like(x, dtype=self.dtype, device=self.device) + + if latent_guide_inv is not None: + self.HAS_LATENT_GUIDE_INV = True + if type(latent_guide_inv) is dict: + if latent_guide_inv['samples'].shape[0] > 1: + latent_guide_inv['samples'] = latent_guide_inv['samples'][batch_num].unsqueeze(0) + latent_guide_inv_samples = self.model.inner_model.inner_model.process_latent_in(latent_guide_inv['samples']).clone().to(dtype=self.dtype, device=self.device) + elif type(latent_guide_inv) is torch.Tensor: + latent_guide_inv_samples = latent_guide_inv.to(dtype=self.dtype, device=self.device) + else: + raise ValueError(f"Invalid latent type: {type(latent_guide_inv)}") + + if self.VIDEO and latent_guide_inv_samples.shape[2] == 1: + latent_guide_inv_samples = latent_guide_inv_samples.repeat(1, 1, x.shape[2], 1, 1) + + if self.SAMPLE: + self.y0_inv = latent_guide_inv_samples + elif sigma_init != 0.0: + pass + elif self.UNSAMPLE: # and self.mask is not None: + mask_inv = self.mask_inv.to(x.device) + x = (1-mask_inv) * x + mask_inv * latent_guide_inv_samples.to(x.device) #fixed old approach, which was mask, (1-mask) + else: + x = latent_guide_inv_samples.to(x.device) #THIS COULD LEAD TO WEIRD BEHAVIOR! OVERWRITING X WITH LG_INV AFTER SETTING TO LG above! + else: + self.y0_inv = torch.zeros_like(x, dtype=self.dtype, device=self.device) + + if latent_guide_mean is not None: + self.HAS_LATENT_GUIDE_MEAN = True + if type(latent_guide_mean) is dict: + if latent_guide_mean['samples'].shape[0] > 1: + latent_guide_mean['samples'] = latent_guide_mean['samples'][batch_num].unsqueeze(0) + latent_guide_mean_samples = self.model.inner_model.inner_model.process_latent_in(latent_guide_mean['samples']).clone().to(dtype=self.dtype, device=self.device) + elif type(latent_guide_mean) is torch.Tensor: + latent_guide_mean_samples = latent_guide_mean.to(dtype=self.dtype, device=self.device) + else: + raise ValueError(f"Invalid latent type: {type(latent_guide_mean)}") + + if self.VIDEO and latent_guide_mean_samples.shape[2] == 1: + latent_guide_mean_samples = latent_guide_mean_samples.repeat(1, 1, x.shape[2], 1, 1) + + self.y0_mean = latent_guide_mean_samples + """if self.SAMPLE: + self.y0_mean = latent_guide_mean_samples + elif self.UNSAMPLE: # and self.mask is not None: + mask_mean = self.mask_mean.to(x.device) + x = (1-mask_mean) * x + mask_mean * latent_guide_mean_samples.to(x.device) #fixed old approach, which was mask, (1-mask) # NECESSARY? + else: + x = latent_guide_mean_samples.to(x.device) #THIS COULD LEAD TO WEIRD BEHAVIOR! OVERWRITING X WITH LG_MEAN AFTER SETTING TO LG above!""" + else: + self.y0_mean = torch.zeros_like(x, dtype=self.dtype, device=self.device) + + if latent_guide_adain is not None: + self.HAS_LATENT_GUIDE_ADAIN = True + if type(latent_guide_adain) is dict: + if latent_guide_adain['samples'].shape[0] > 1: + latent_guide_adain['samples'] = latent_guide_adain['samples'][batch_num].unsqueeze(0) + latent_guide_adain_samples = self.model.inner_model.inner_model.process_latent_in(latent_guide_adain['samples']).clone().to(dtype=self.dtype, device=self.device) + elif type(latent_guide_adain) is torch.Tensor: + latent_guide_adain_samples = latent_guide_adain.to(dtype=self.dtype, device=self.device) + else: + raise ValueError(f"Invalid latent type: {type(latent_guide_adain)}") + + if self.VIDEO and latent_guide_adain_samples.shape[2] == 1: + latent_guide_adain_samples = latent_guide_adain_samples.repeat(1, 1, x.shape[2], 1, 1) + + self.y0_adain = latent_guide_adain_samples + """if self.SAMPLE: + self.y0_adain = latent_guide_adain_samples + elif self.UNSAMPLE: # and self.mask is not None: + if self.mask_adain is not None: + mask_adain = self.mask_adain.to(x.device) + x = (1-mask_adain) * x + mask_adain * latent_guide_adain_samples.to(x.device) #fixed old approach, which was mask, (1-mask) # NECESSARY? + else: + x = latent_guide_adain_samples.to(x.device) + else: + x = latent_guide_adain_samples.to(x.device) #THIS COULD LEAD TO WEIRD BEHAVIOR! OVERWRITING X WITH LG_ADAIN AFTER SETTING TO LG above!""" + else: + self.y0_adain = torch.zeros_like(x, dtype=self.dtype, device=self.device) + + if latent_guide_attninj is not None: + self.HAS_LATENT_GUIDE_ATTNINJ = True + if type(latent_guide_attninj) is dict: + if latent_guide_attninj['samples'].shape[0] > 1: + latent_guide_attninj['samples'] = latent_guide_attninj['samples'][batch_num].unsqueeze(0) + latent_guide_attninj_samples = self.model.inner_model.inner_model.process_latent_in(latent_guide_attninj['samples']).clone().to(dtype=self.dtype, device=self.device) + elif type(latent_guide_attninj) is torch.Tensor: + latent_guide_attninj_samples = latent_guide_attninj.to(dtype=self.dtype, device=self.device) + else: + raise ValueError(f"Invalid latent type: {type(latent_guide_attninj)}") + + if self.VIDEO and latent_guide_attninj_samples.shape[2] == 1: + latent_guide_attninj_samples = latent_guide_attninj_samples.repeat(1, 1, x.shape[2], 1, 1) + + self.y0_attninj = latent_guide_attninj_samples + """if self.SAMPLE: + self.y0_attninj = latent_guide_attninj_samples + elif self.UNSAMPLE: # and self.mask is not None: + if self.mask_attninj is not None: + mask_attninj = self.mask_attninj.to(x.device) + x = (1-mask_attninj) * x + mask_attninj * latent_guide_attninj_samples.to(x.device) #fixed old approach, which was mask, (1-mask) # NECESSARY? + else: + x = latent_guide_attninj_samples.to(x.device) + else: + x = latent_guide_attninj_samples.to(x.device) #THIS COULD LEAD TO WEIRD BEHAVIOR! OVERWRITING X WITH LG_ADAIN AFTER SETTING TO LG above!""" + else: + self.y0_attninj = torch.zeros_like(x, dtype=self.dtype, device=self.device) + + + if latent_guide_style_pos is not None: + self.HAS_LATENT_GUIDE_STYLE_POS = True + if type(latent_guide_style_pos) is dict: + if latent_guide_style_pos['samples'].shape[0] > 1: + latent_guide_style_pos['samples'] = latent_guide_style_pos['samples'][batch_num].unsqueeze(0) + latent_guide_style_pos_samples = self.model.inner_model.inner_model.process_latent_in(latent_guide_style_pos['samples']).clone().to(dtype=self.dtype, device=self.device) + elif type(latent_guide_style_pos) is torch.Tensor: + latent_guide_style_pos_samples = latent_guide_style_pos.to(dtype=self.dtype, device=self.device) + else: + raise ValueError(f"Invalid latent type: {type(latent_guide_style_pos)}") + + if self.VIDEO and latent_guide_style_pos_samples.shape[2] == 1: + latent_guide_style_pos_samples = latent_guide_style_pos_samples.repeat(1, 1, x.shape[2], 1, 1) + + self.y0_style_pos = latent_guide_style_pos_samples + """if self.SAMPLE: + self.y0_style_pos = latent_guide_style_pos_samples + elif self.UNSAMPLE: # and self.mask is not None: + if self.mask_style_pos is not None: + mask_style_pos = self.mask_style_pos.to(x.device) + x = (1-mask_style_pos) * x + mask_style_pos * latent_guide_style_pos_samples.to(x.device) #fixed old approach, which was mask, (1-mask) # NECESSARY? + else: + x = latent_guide_style_pos_samples.to(x.device) + else: + x = latent_guide_style_pos_samples.to(x.device) #THIS COULD LEAD TO WEIRD BEHAVIOR! OVERWRITING X WITH LG_ADAIN AFTER SETTING TO LG above!""" + else: + self.y0_style_pos = torch.zeros_like(x, dtype=self.dtype, device=self.device) + + + if latent_guide_style_neg is not None: + self.HAS_LATENT_GUIDE_STYLE_NEG = True + if type(latent_guide_style_neg) is dict: + if latent_guide_style_neg['samples'].shape[0] > 1: + latent_guide_style_neg['samples'] = latent_guide_style_neg['samples'][batch_num].unsqueeze(0) + latent_guide_style_neg_samples = self.model.inner_model.inner_model.process_latent_in(latent_guide_style_neg['samples']).clone().to(dtype=self.dtype, device=self.device) + elif type(latent_guide_style_neg) is torch.Tensor: + latent_guide_style_neg_samples = latent_guide_style_neg.to(dtype=self.dtype, device=self.device) + else: + raise ValueError(f"Invalid latent type: {type(latent_guide_style_neg)}") + + if self.VIDEO and latent_guide_style_neg_samples.shape[2] == 1: + latent_guide_style_neg_samples = latent_guide_style_neg_samples.repeat(1, 1, x.shape[2], 1, 1) + + self.y0_style_neg = latent_guide_style_neg_samples + """if self.SAMPLE: + self.y0_style_neg = latent_guide_style_neg_samples + elif self.UNSAMPLE: # and self.mask is not None: + if self.mask_style_neg is not None: + mask_style_neg = self.mask_style_neg.to(x.device) + x = (1-mask_style_neg) * x + mask_style_neg * latent_guide_style_neg_samples.to(x.device) #fixed old approach, which was mask, (1-mask) # NECESSARY? + else: + x = latent_guide_style_neg_samples.to(x.device) + else: + x = latent_guide_style_neg_samples.to(x.device) #THIS COULD LEAD TO WEIRD BEHAVIOR! OVERWRITING X WITH LG_ADAIN AFTER SETTING TO LG above!""" + else: + self.y0_style_neg = torch.zeros_like(x, dtype=self.dtype, device=self.device) + + if self.UNSAMPLE and not self.SAMPLE: #sigma_next > sigma: # TODO: VERIFY APPROACH FOR INVERSION + if guide_inversion_y0 is not None: + self.y0 = guide_inversion_y0 + else: + self.y0 = noise_sampler(sigma=self.sigma_max, sigma_next=self.sigma_min).to(dtype=self.dtype, device=self.device) + self.y0 = normalize_zscore(self.y0, channelwise=True, inplace=True) + self.y0 *= self.sigma_max + + if guide_inversion_y0_inv is not None: + self.y0_inv = guide_inversion_y0_inv + else: + self.y0_inv = noise_sampler(sigma=self.sigma_max, sigma_next=self.sigma_min).to(dtype=self.dtype, device=self.device) + self.y0_inv = normalize_zscore(self.y0_inv, channelwise=True, inplace=True) + self.y0_inv*= self.sigma_max + + + if self.VIDEO and self.frame_weights_mgr is not None: + num_frames = x.shape[2] + self.frame_weights = self.frame_weights_mgr.get_frame_weights_by_name('frame_weights', num_frames) + self.frame_weights_inv = self.frame_weights_mgr.get_frame_weights_by_name('frame_weights_inv', num_frames) + + x, self.y0, self.y0_inv = self.normalize_inputs(x, self.y0, self.y0_inv) # ??? + + return x + + def prepare_weighted_masks(self, step:int, lgw_type="default") -> Tuple[Tensor, Tensor]: + if lgw_type == "sync": + lgw_ = self.lgw_sync [step] + lgw_inv_ = self.lgw_sync_inv[step] + mask = torch.ones_like (self.y0) if self.mask_sync is None else self.mask_sync + mask_inv = torch.zeros_like(self.y0) if self.mask_sync is None else 1-self.mask_sync + elif lgw_type == "drift_x": + lgw_ = self.lgw_drift_x [step] + lgw_inv_ = self.lgw_drift_x_inv[step] + mask = torch.ones_like (self.y0) if self.mask_drift_x is None else self.mask_drift_x + mask_inv = torch.zeros_like(self.y0) if self.mask_drift_x is None else 1-self.mask_drift_x + elif lgw_type == "drift_y": + lgw_ = self.lgw_drift_y [step] + lgw_inv_ = self.lgw_drift_y_inv[step] + mask = torch.ones_like (self.y0) if self.mask_drift_y is None else self.mask_drift_y + mask_inv = torch.zeros_like(self.y0) if self.mask_drift_y is None else 1-self.mask_drift_y + elif lgw_type == "lure_x": + lgw_ = self.lgw_lure_x [step] + lgw_inv_ = self.lgw_lure_x_inv[step] + mask = torch.ones_like (self.y0) if self.mask_lure_x is None else self.mask_lure_x + mask_inv = torch.zeros_like(self.y0) if self.mask_lure_x is None else 1-self.mask_lure_x + elif lgw_type == "lure_y": + lgw_ = self.lgw_lure_y [step] + lgw_inv_ = self.lgw_lure_y_inv[step] + mask = torch.ones_like (self.y0) if self.mask_lure_y is None else self.mask_lure_y + mask_inv = torch.zeros_like(self.y0) if self.mask_lure_y is None else 1-self.mask_lure_y + else: + lgw_ = self.lgw [step] + lgw_inv_ = self.lgw_inv[step] + mask = torch.ones_like (self.y0) if self.mask is None else self.mask + mask_inv = torch.zeros_like(self.y0) if self.mask_inv is None else self.mask_inv + + if self.LGW_MASK_RESCALE_MIN: + lgw_mask = mask * (1-lgw_) + lgw_ + lgw_mask_inv = (1-mask) * (1-lgw_inv_) + lgw_inv_ + else: + if self.HAS_LATENT_GUIDE: + lgw_mask = mask * lgw_ + else: + lgw_mask = torch.zeros_like(mask) + + if self.HAS_LATENT_GUIDE_INV: + if mask_inv is not None: + lgw_mask_inv = torch.minimum(mask_inv, (1-mask) * lgw_inv_) + #lgw_mask_inv = torch.minimum(1-mask_inv, (1-mask) * lgw_inv_) + else: + lgw_mask_inv = (1-mask) * lgw_inv_ + else: + lgw_mask_inv = torch.zeros_like(mask) + + return lgw_mask, lgw_mask_inv + + + def get_masks_for_step(self, step:int, lgw_type="default") -> Tuple[Tensor, Tensor]: + lgw_mask, lgw_mask_inv = self.prepare_weighted_masks(step, lgw_type=lgw_type) + normalize_frame_weights_per_step = self.EO("normalize_frame_weights_per_step") + normalize_frame_weights_per_step_inv = self.EO("normalize_frame_weights_per_step_inv") + + if self.VIDEO and self.frame_weights_mgr: + num_frames = lgw_mask.shape[2] + if self.HAS_LATENT_GUIDE: + frame_weights = self.frame_weights_mgr.get_frame_weights_by_name('frame_weights', num_frames, step) + apply_frame_weights(lgw_mask, frame_weights, normalize_frame_weights_per_step) + if self.HAS_LATENT_GUIDE_INV: + frame_weights_inv = self.frame_weights_mgr.get_frame_weights_by_name('frame_weights_inv', num_frames, step) + apply_frame_weights(lgw_mask_inv, frame_weights_inv, normalize_frame_weights_per_step_inv) + + return lgw_mask.to(self.device), lgw_mask_inv.to(self.device) + + + + def get_cossim_adjusted_lgw_masks(self, data:Tensor, step:int) -> Tuple[Tensor, Tensor, Tensor, Tensor]: + + if self.HAS_LATENT_GUIDE: + y0 = self.y0.clone() + else: + y0 = torch.zeros_like(data) + + if self.HAS_LATENT_GUIDE_INV: + y0_inv = self.y0_inv.clone() + else: + y0_inv = torch.zeros_like(data) + + if y0.shape[0] > 1: # this is for changing the guide on a per-step basis + y0 = y0[min(step, y0.shape[0]-1)].unsqueeze(0) + + lgw_mask, lgw_mask_inv = self.get_masks_for_step(step) + + y0_cossim, y0_cossim_inv = 1.0, 1.0 + if self.HAS_LATENT_GUIDE: + y0_cossim = get_pearson_similarity(data, y0, mask=lgw_mask) + if self.HAS_LATENT_GUIDE_INV: + y0_cossim_inv = get_pearson_similarity(data, y0_inv, mask=lgw_mask_inv) + + #if y0_cossim < self.guide_cossim_cutoff_ or y0_cossim_inv < self.guide_bkg_cossim_cutoff_: + if y0_cossim >= self.guide_cossim_cutoff_: + lgw_mask *= 0 + if y0_cossim_inv >= self.guide_bkg_cossim_cutoff_: + lgw_mask_inv *= 0 + + return y0, y0_inv, lgw_mask, lgw_mask_inv + + + + + + + + + + + + + @torch.no_grad + def process_pseudoimplicit_guides_substep(self, + x_0 : Tensor, + x_ : Tensor, + eps_ : Tensor, + eps_prev_ : Tensor, + data_ : Tensor, + denoised_prev : Tensor, + row : int, + step : int, + step_sched : int, + sigmas : Tensor, + NS , + RK , + pseudoimplicit_row_weights : Tensor, + pseudoimplicit_step_weights : Tensor, + full_iter : int, + BONGMATH : bool, + ): + + if "pseudoimplicit" not in self.guide_mode or (self.lgw[step_sched] == 0 and self.lgw_inv[step_sched] == 0): + return x_0, x_, eps_, None, None + + sigma = sigmas[step] + + if self.s_lying_ is not None: + if row >= len(self.s_lying_): + return x_0, x_, eps_, None, None + + if self.guide_mode.startswith("fully_"): + data_cossim_test = denoised_prev + else: + data_cossim_test = data_[row] + + y0, y0_inv, lgw_mask, lgw_mask_inv = self.get_cossim_adjusted_lgw_masks(data_cossim_test, step_sched) + + if not (lgw_mask.any() != 0 or lgw_mask_inv.any() != 0): # cossim score too similar! deactivate guide for this step + return x_0, x_, eps_, None, None + + + if "fully_pseudoimplicit" in self.guide_mode: + if self.x_lying_ is None: + return x_0, x_, eps_, None, None + else: + x_row_pseudoimplicit = self.x_lying_[row] + sub_sigma_pseudoimplicit = self.s_lying_[row] + + + + if RK.IMPLICIT: + x_ = RK.update_substep(x_0, + x_, + eps_, + eps_prev_, + row, + RK.row_offset, + NS.h_new, + NS.h_new_orig, + ) + + x_[row] = NS.rebound_overshoot_substep(x_0, x_[row]) + + if row > 0: + x_[row] = NS.swap_noise_substep(x_0, x_[row]) + if BONGMATH and step < sigmas.shape[0]-1 and not self.EO("disable_pseudoimplicit_bongmath"): + x_0, x_, eps_ = RK.bong_iter(x_0, + x_, + eps_, + eps_prev_, + data_, + sigma, + NS.s_, + row, + RK.row_offset, + NS.h, + step, + step_sched, + ) + else: + eps_[row] = RK.get_epsilon(x_0, x_[row], denoised_prev, sigma, NS.s_[row]) + + if self.EO("pseudoimplicit_denoised_prev"): + eps_[row] = RK.get_epsilon(x_0, x_[row], denoised_prev, sigma, NS.s_[row]) + + eps_substep_guide = torch.zeros_like(x_0) + eps_substep_guide_inv = torch.zeros_like(x_0) + + if self.HAS_LATENT_GUIDE: + eps_substep_guide = RK.get_guide_epsilon(x_0, x_[row], y0, sigma, NS.s_[row], NS.sigma_down, None) + if self.HAS_LATENT_GUIDE_INV: + eps_substep_guide_inv = RK.get_guide_epsilon(x_0, x_[row], y0_inv, sigma, NS.s_[row], NS.sigma_down, None) + + + + if self.guide_mode in {"pseudoimplicit", "pseudoimplicit_cw", "pseudoimplicit_projection", "pseudoimplicit_projection_cw"}: + maxmin_ratio = (NS.sub_sigma - RK.sigma_min) / NS.sub_sigma + + if self.EO("guide_pseudoimplicit_power_substep_flip_maxmin_scaling"): + maxmin_ratio *= (RK.rows-row) / RK.rows + elif self.EO("guide_pseudoimplicit_power_substep_maxmin_scaling"): + maxmin_ratio *= row / RK.rows + + sub_sigma_2 = NS.sub_sigma - maxmin_ratio * (NS.sub_sigma * pseudoimplicit_row_weights[row] * pseudoimplicit_step_weights[full_iter] * self.lgw[step_sched]) + + eps_tmp_ = eps_.clone() + + eps_ = self.process_channelwise(x_0, + eps_, + data_, + row, + eps_substep_guide, + eps_substep_guide_inv, + y0, + y0_inv, + lgw_mask, + lgw_mask_inv, + use_projection = self.guide_mode in {"pseudoimplicit_projection", "pseudoimplicit_projection_cw"}, + channelwise = self.guide_mode in {"pseudoimplicit_cw", "pseudoimplicit_projection_cw"}, + ) + + x_row_tmp = x_[row] + RK.h_fn(sub_sigma_2, NS.sub_sigma) * eps_[row] + + eps_ = eps_tmp_ + x_row_pseudoimplicit = x_row_tmp + sub_sigma_pseudoimplicit = sub_sigma_2 + + + if RK.IMPLICIT and BONGMATH and step < sigmas.shape[0]-1 and not self.EO("disable_pseudobongmath"): + x_[row] = NS.sigma_from_to(x_0, x_row_pseudoimplicit, sigma, sub_sigma_pseudoimplicit, NS.s_[row]) + + x_0, x_, eps_ = RK.bong_iter(x_0, + x_, + eps_, + eps_prev_, + data_, + sigma, + NS.s_, + row, + RK.row_offset, + NS.h, + step, + step_sched, + ) + + return x_0, x_, eps_, x_row_pseudoimplicit, sub_sigma_pseudoimplicit + + + + @torch.no_grad + def prepare_fully_pseudoimplicit_guides_substep(self, + x_0, + x_, + eps_, + eps_prev_, + data_, + denoised_prev, + row, + step, + step_sched, + sigmas, + eta_substep, + overshoot_substep, + s_noise_substep, + NS, + RK, + pseudoimplicit_row_weights, + pseudoimplicit_step_weights, + full_iter, + BONGMATH, + ): + + if "fully_pseudoimplicit" not in self.guide_mode or (self.lgw[step_sched] == 0 and self.lgw_inv[step_sched] == 0): + return x_0, x_, eps_ + + sigma = sigmas[step] + + y0, y0_inv, lgw_mask, lgw_mask_inv = self.get_cossim_adjusted_lgw_masks(denoised_prev, step_sched) + + if not (lgw_mask.any() != 0 or lgw_mask_inv.any() != 0): # cossim score too similar! deactivate guide for this step + return x_0, x_, eps_ + + + # PREPARE FULLY PSEUDOIMPLICIT GUIDES + if self.guide_mode in {"fully_pseudoimplicit", "fully_pseudoimplicit_cw", "fully_pseudoimplicit_projection", "fully_pseudoimplicit_projection_cw"} and (self.lgw[step_sched] > 0 or self.lgw_inv[step_sched] > 0): + x_lying_ = x_.clone() + eps_lying_ = eps_.clone() + s_lying_ = [] + + for r in range(RK.rows): + + NS.set_sde_substep(r, RK.multistep_stages, eta_substep, overshoot_substep, s_noise_substep) + + maxmin_ratio = (NS.sub_sigma - RK.sigma_min) / NS.sub_sigma + fully_sub_sigma_2 = NS.sub_sigma - maxmin_ratio * (NS.sub_sigma * pseudoimplicit_row_weights[r] * pseudoimplicit_step_weights[full_iter] * self.lgw[step_sched]) + + s_lying_.append(fully_sub_sigma_2) + + if RK.IMPLICIT: + x_ = RK.update_substep(x_0, + x_, + eps_, + eps_prev_, + r, + RK.row_offset, + NS.h_new, + NS.h_new_orig, + ) + + x_[r] = NS.rebound_overshoot_substep(x_0, x_[r]) + + if r > 0: + x_[r] = NS.swap_noise_substep(x_0, x_[r]) + if BONGMATH and step < sigmas.shape[0]-1 and not self.EO("disable_fully_pseudoimplicit_bongmath"): + x_0, x_, eps_ = RK.bong_iter(x_0, + x_, + eps_, + eps_prev_, + data_, + sigma, + NS.s_, + r, + RK.row_offset, + NS.h, + step, + step_sched, + ) + + if self.EO("fully_pseudoimplicit_denoised_prev"): + eps_[r] = RK.get_epsilon(x_0, x_[r], denoised_prev, sigma, NS.s_[r]) + + eps_substep_guide = torch.zeros_like(x_0) + eps_substep_guide_inv = torch.zeros_like(x_0) + + if self.HAS_LATENT_GUIDE: + eps_substep_guide = RK.get_guide_epsilon(x_0, x_[r], y0, sigma, NS.s_[r], NS.sigma_down, None) + if self.HAS_LATENT_GUIDE_INV: + eps_substep_guide_inv = RK.get_guide_epsilon(x_0, x_[r], y0_inv, sigma, NS.s_[r], NS.sigma_down, None) + + eps_ = self.process_channelwise(x_0, + eps_, + data_, + row, + eps_substep_guide, + eps_substep_guide_inv, + y0, + y0_inv, + lgw_mask, + lgw_mask_inv, + use_projection = self.guide_mode in {"fully_pseudoimplicit_projection", "fully_pseudoimplicit_projection_cw"}, + channelwise = self.guide_mode in {"fully_pseudoimplicit_cw", "fully_pseudoimplicit_projection_cw"}, + ) + + x_lying_[r] = x_[r] + RK.h_fn(fully_sub_sigma_2, NS.sub_sigma) * eps_[r] + data_lying = x_[r] + RK.h_fn(0, NS.s_[r]) * eps_[r] + + eps_lying_[r] = RK.get_epsilon(x_0, x_[r], data_lying, sigma, NS.s_[r]) + + if not self.EO("pseudoimplicit_disable_eps_lying"): + eps_ = eps_lying_ + + if not self.EO("pseudoimplicit_disable_newton_iter"): + x_, eps_ = RK.newton_iter(x_0, + x_, + eps_, + eps_prev_, + data_, + NS.s_, + 0, + NS.h, + sigmas, + step, + "lying", + ) + + self.x_lying_ = x_lying_ + self.s_lying_ = s_lying_ + + return x_0, x_, eps_ + + + + @torch.no_grad + def process_guides_data_substep(self, + x_row : Tensor, + data_row : Tensor, + step : int, + sigma_row : Tensor, + frame_targets : Optional[Tensor] = None, + ): + if not self.HAS_LATENT_GUIDE and not self.HAS_LATENT_GUIDE_INV: + return x_row + + y0, y0_inv, lgw_mask, lgw_mask_inv = self.get_cossim_adjusted_lgw_masks(data_row, step) + + if not (lgw_mask.any() != 0 or lgw_mask_inv.any() != 0): # cossim score too similar! deactivate guide for this step + return x_row + + if self.VIDEO and self.frame_weights_mgr is not None and frame_targets is None: + num_frames = data_row.shape[2] + frame_targets = self.frame_weights_mgr.get_frame_weights_by_name('frame_targets', num_frames, step) + if frame_targets is None: + frame_targets = torch.tensor(self.EO("frame_targets", [1.0])) + frame_targets = torch.clamp(frame_targets, 0.0, 1.0).to(self.device) + + if self.guide_mode in {"data", "data_projection", "lure", "lure_projection"}: + if frame_targets is None: + x_row = self.get_data_substep(x_row, data_row, y0, y0_inv, lgw_mask, lgw_mask_inv, step, sigma_row) + else: + t_dim = x_row.shape[-3] + for t in range(t_dim): #temporal dimension + frame_target = float(frame_targets[t] if len(frame_targets) > t else frame_targets[-1]) + x_row[...,t:t+1,:,:] = self.get_data_substep( + x_row [...,t:t+1,:,:], + data_row [...,t:t+1,:,:], + y0 [...,t:t+1,:,:], + y0_inv [...,t:t+1,:,:], + lgw_mask [...,t:t+1,:,:], + lgw_mask_inv[...,t:t+1,:,:], + step, + sigma_row, + frame_target) + + return x_row + + + + + @torch.no_grad + def get_data_substep(self, + x_row : Tensor, + data_row : Tensor, + y0 : Tensor, + y0_inv : Tensor, + lgw_mask : Tensor, + lgw_mask_inv : Tensor, + step : int, + sigma_row : Tensor, + frame_target : float = 1.0, + ): + + if not self.HAS_LATENT_GUIDE and not self.HAS_LATENT_GUIDE_INV: + return x_row + + if self.guide_mode in {"data", "data_projection", "lure", "lure_projection"}: + data_targets = self.EO("data_targets", [1.0]) + step_target = step if len(data_targets) > step else len(data_targets)-1 + + cossim_target = frame_target * data_targets[step_target] + + if self.HAS_LATENT_GUIDE: + if self.guide_mode.endswith("projection"): + d_collinear_d_lerp = get_collinear(data_row, y0) + d_lerp_ortho_d = get_orthogonal(y0, data_row) + y0 = d_collinear_d_lerp + d_lerp_ortho_d + + if cossim_target == 1.0: + d_slerped = y0 + elif cossim_target == 0.0: + d_slerped = data_row + else: + y0_pearsim = get_pearson_similarity(data_row, y0, mask=self.mask) + slerp_weight = get_slerp_weight_for_cossim(y0_pearsim.item(), cossim_target) + d_slerped = slerp_tensor(slerp_weight, data_row, y0) # lgw_mask * slerp_weight same as using mask below + + """if self.guide_mode == "data_projection": + d_collinear_d_lerp = get_collinear(data_row, d_slerped) + d_lerp_ortho_d = get_orthogonal(d_slerped, data_row) + d_slerped = d_collinear_d_lerp + d_lerp_ortho_d""" + + if self.VE_MODEL: + x_row = x_row + lgw_mask * (d_slerped - data_row) + else: + x_row = x_row + lgw_mask * (self.sigma_max - sigma_row) * (d_slerped - data_row) + + + if self.HAS_LATENT_GUIDE_INV: + if self.guide_mode.endswith("projection"): + d_collinear_d_lerp = get_collinear(data_row, y0_inv) + d_lerp_ortho_d = get_orthogonal(y0_inv, data_row) + y0_inv = d_collinear_d_lerp + d_lerp_ortho_d + + if cossim_target == 1.0: + d_slerped_inv = y0_inv + elif cossim_target == 0.0: + d_slerped_inv = data_row + else: + y0_pearsim = get_pearson_similarity(data_row, y0_inv, mask=self.mask_inv) + slerp_weight = get_slerp_weight_for_cossim(y0_pearsim.item(), cossim_target) + d_slerped_inv = slerp_tensor(slerp_weight, data_row, y0_inv) + + """if self.guide_mode == "data_projection": + d_collinear_d_lerp = get_collinear(data_row, d_slerped_inv) + d_lerp_ortho_d = get_orthogonal(d_slerped_inv, data_row) + d_slerped_inv = d_collinear_d_lerp + d_lerp_ortho_d""" + + if self.VE_MODEL: + x_row = x_row + lgw_mask_inv * (d_slerped_inv - data_row) + else: + x_row = x_row + lgw_mask_inv * (self.sigma_max - sigma_row) * (d_slerped_inv - data_row) + + + return x_row + + @torch.no_grad + def swap_data(self, + x : Tensor, + data : Tensor, + y : Tensor, + sigma : Tensor, + mask : Optional[Tensor] = None, + ): + mask = 1.0 if mask is None else mask + if self.VE_MODEL: + return x + mask * (y - data) + else: + return x + mask * (self.sigma_max - sigma) * (y - data) + + @torch.no_grad + def process_guides_eps_substep(self, + x_0 : Tensor, + x_row : Tensor, + data_row : Tensor, + eps_row : Tensor, + step : int, + sigma : Tensor, + sigma_down : Tensor, + sigma_row : Tensor, + frame_targets : Optional[Tensor] = None, + RK=None, + ): + + if not self.HAS_LATENT_GUIDE and not self.HAS_LATENT_GUIDE_INV: + return eps_row + + y0, y0_inv, lgw_mask, lgw_mask_inv = self.get_cossim_adjusted_lgw_masks(data_row, step) + + if not (lgw_mask.any() != 0 or lgw_mask_inv.any() != 0): # cossim score too similar! deactivate guide for this step + return eps_row + + if self.VIDEO and data_row.ndim == 5 and frame_targets is None: + num_frames = data_row.shape[2] + frame_targets = self.frame_weights_mgr.get_frame_weights_by_name('frame_targets', num_frames, step) + if frame_targets is None: + frame_targets = self.EO("frame_targets", [1.0]) + frame_targets = torch.clamp(frame_targets, 0.0, 1.0) + + eps_y0 = torch.zeros_like(x_0) + eps_y0_inv = torch.zeros_like(x_0) + + if self.HAS_LATENT_GUIDE: + eps_y0 = RK.get_guide_epsilon(x_0, x_row, y0, sigma, sigma_row, sigma_down, None) + + if self.HAS_LATENT_GUIDE_INV: + eps_y0_inv = RK.get_guide_epsilon(x_0, x_row, y0_inv, sigma, sigma_row, sigma_down, None) + + if self.guide_mode in {"epsilon", "epsilon_projection"}: + if frame_targets is None: + eps_row = self.get_eps_substep(eps_row, eps_y0, eps_y0_inv, lgw_mask, lgw_mask_inv, step, sigma_row) + else: + t_dim = x_row.shape[-3] + for t in range(t_dim): #temporal dimension + frame_target = float(frame_targets[t] if len(frame_targets) > t else frame_targets[-1]) + eps_row[...,t:t+1,:,:] = self.get_eps_substep( + eps_row [...,t:t+1,:,:], + eps_y0 [...,t:t+1,:,:], + eps_y0_inv [...,t:t+1,:,:], + lgw_mask [...,t:t+1,:,:], + lgw_mask_inv[...,t:t+1,:,:], + step, + sigma_row, + frame_target) + + return eps_row + + + + @torch.no_grad + def get_eps_substep(self, + eps_row : Tensor, + eps_y0 : Tensor, + eps_y0_inv : Tensor, + lgw_mask : Tensor, + lgw_mask_inv : Tensor, + step : int, + sigma_row : Tensor, + frame_target : float = 1.0, + ): + + if not self.HAS_LATENT_GUIDE and not self.HAS_LATENT_GUIDE_INV: + return eps_row + + if self.guide_mode in {"epsilon", "epsilon_projection"}: + eps_targets = self.EO("eps_targets", [1.0]) + step_target = step if len(eps_targets) > step else len(eps_targets)-1 + + cossim_target = frame_target * eps_targets[step_target] + + if self.HAS_LATENT_GUIDE: + if self.guide_mode == "epsilon_projection": + d_collinear_d_lerp = get_collinear(eps_row, eps_y0) + d_lerp_ortho_d = get_orthogonal(eps_y0, eps_row) + eps_y0 = d_collinear_d_lerp + d_lerp_ortho_d + + if cossim_target == 1.0: + d_slerped = eps_y0 + elif cossim_target == 0.0: + d_slerped = eps_row + else: + y0_pearsim = get_pearson_similarity(eps_row, eps_y0, mask=self.mask) + slerp_weight = get_slerp_weight_for_cossim(y0_pearsim.item(), cossim_target) + d_slerped = slerp_tensor(slerp_weight, eps_row, eps_y0) # lgw_mask * slerp_weight same as using mask below + + """if self.guide_mode == "data_projection": + d_collinear_d_lerp = get_collinear(data_row, d_slerped) + d_lerp_ortho_d = get_orthogonal(d_slerped, data_row) + d_slerped = d_collinear_d_lerp + d_lerp_ortho_d""" + + eps_row = eps_row + lgw_mask * (d_slerped - eps_row) + + + if self.HAS_LATENT_GUIDE_INV: + if self.guide_mode == "epsilon_projection": + d_collinear_d_lerp = get_collinear(eps_row, eps_y0_inv) + d_lerp_ortho_d = get_orthogonal(eps_y0_inv, eps_row) + eps_y0_inv = d_collinear_d_lerp + d_lerp_ortho_d + + if cossim_target == 1.0: + d_slerped_inv = eps_y0_inv + elif cossim_target == 0.0: + d_slerped_inv = eps_row + else: + y0_pearsim = get_pearson_similarity(eps_row, eps_y0_inv, mask=self.mask_inv) + slerp_weight = get_slerp_weight_for_cossim(y0_pearsim.item(), cossim_target) + d_slerped_inv = slerp_tensor(slerp_weight, eps_row, eps_y0_inv) + + """if self.guide_mode == "data_projection": + d_collinear_d_lerp = get_collinear(data_row, d_slerped_inv) + d_lerp_ortho_d = get_orthogonal(d_slerped_inv, data_row) + d_slerped_inv = d_collinear_d_lerp + d_lerp_ortho_d""" + + eps_row = eps_row + lgw_mask_inv * (d_slerped_inv - eps_row) + + return eps_row + + + + + + @torch.no_grad + def process_guides_substep(self, + x_0 : Tensor, + x_ : Tensor, + eps_ : Tensor, + data_ : Tensor, + row : int, + step_sched : int, + sigma : Tensor, + sigma_next : Tensor, + sigma_down : Tensor, + s_ : Tensor, + epsilon_scale : float, + RK, + ): + + if not self.HAS_LATENT_GUIDE and not self.HAS_LATENT_GUIDE_INV: + return eps_, x_ + + y0, y0_inv, lgw_mask, lgw_mask_inv = self.get_cossim_adjusted_lgw_masks(data_[row], step_sched) + + if not (lgw_mask.any() != 0 or lgw_mask_inv.any() != 0): # cossim score too similar! deactivate guide for this step + return eps_, x_ + + if self.EO(["substep_eps_ch_mean_std", "substep_eps_ch_mean", "substep_eps_ch_std", "substep_eps_mean_std", "substep_eps_mean", "substep_eps_std"]): + eps_orig = eps_.clone() + + if self.EO("dynamic_guides_mean_std"): + y_shift, y_inv_shift = normalize_latent([y0, y0_inv], [data_, data_]) + y0 = y_shift + if self.EO("dynamic_guides_inv"): + y0_inv = y_inv_shift + + if self.EO("dynamic_guides_mean"): + y_shift, y_inv_shift = normalize_latent([y0, y0_inv], [data_, data_], std=False) + y0 = y_shift + if self.EO("dynamic_guides_inv"): + y0_inv = y_inv_shift + + + + if "data_old" == self.guide_mode: + y0_tmp = y0.clone() + if self.HAS_LATENT_GUIDE: + y0_tmp = (1-lgw_mask) * data_[row] + lgw_mask * y0 + y0_tmp = (1-lgw_mask_inv) * y0_tmp + lgw_mask_inv * y0_inv + x_[row+1] = y0_tmp + eps_[row] + + if self.guide_mode == "data_old_projection": + + d_lerp = data_[row] + lgw_mask * (y0-data_[row]) + lgw_mask_inv * (y0_inv-data_[row]) + + d_collinear_d_lerp = get_collinear(data_[row], d_lerp) + d_lerp_ortho_d = get_orthogonal(d_lerp, data_[row]) + + data_[row] = d_collinear_d_lerp + d_lerp_ortho_d + + x_[row+1] = data_[row] + eps_[row] * sigma + + + + #elif (self.UNSAMPLE or self.guide_mode in {"epsilon", "epsilon_cw", "epsilon_projection", "epsilon_projection_cw"}) and (self.lgw[step] > 0 or self.lgw_inv[step] > 0): + elif self.guide_mode in {"epsilon", "epsilon_cw", "epsilon_projection", "epsilon_projection_cw"} and (self.lgw[step_sched] > 0 or self.lgw_inv[step_sched] > 0): + if sigma_down < sigma or s_[row] < RK.sigma_max: + + eps_substep_guide = torch.zeros_like(x_0) + eps_substep_guide_inv = torch.zeros_like(x_0) + + if self.HAS_LATENT_GUIDE: + eps_substep_guide = RK.get_guide_epsilon(x_0, x_[row], y0, sigma, s_[row], sigma_down, epsilon_scale) + + if self.HAS_LATENT_GUIDE_INV: + eps_substep_guide_inv = RK.get_guide_epsilon(x_0, x_[row], y0_inv, sigma, s_[row], sigma_down, epsilon_scale) + + tol_value = self.EO("tol", -1.0) + if tol_value >= 0: + for b, c in itertools.product(range(x_0.shape[0]), range(x_0.shape[1])): + current_diff = torch.norm(data_[row][b][c] - y0 [b][c]) + current_diff_inv = torch.norm(data_[row][b][c] - y0_inv[b][c]) + + lgw_scaled = torch.nan_to_num(1-(tol_value/current_diff), 0) + lgw_scaled_inv = torch.nan_to_num(1-(tol_value/current_diff_inv), 0) + + lgw_tmp = min(self.lgw[step_sched] , lgw_scaled) + lgw_tmp_inv = min(self.lgw_inv[step_sched], lgw_scaled_inv) + + lgw_mask_clamp = torch.clamp(lgw_mask, max=lgw_tmp) + lgw_mask_clamp_inv = torch.clamp(lgw_mask_inv, max=lgw_tmp_inv) + + eps_[row][b][c] = eps_[row][b][c] + lgw_mask_clamp[b][0] * (eps_substep_guide[b][c] - eps_[row][b][c]) + lgw_mask_clamp_inv[b][0] * (eps_substep_guide_inv[b][c] - eps_[row][b][c]) + + elif self.guide_mode in {"epsilon"}: + #eps_[row] = slerp(lgw_mask.mean().item(), eps_[row], eps_substep_guide) + if self.EO("slerp_epsilon_guide"): + if eps_substep_guide.sum() != 0: + eps_[row] = slerp_tensor(lgw_mask, eps_[row], eps_substep_guide) + if eps_substep_guide_inv.sum() != 0: + eps_[row] = slerp_tensor(lgw_mask_inv, eps_[row], eps_substep_guide_inv) + else: + eps_[row] = eps_[row] + lgw_mask * (eps_substep_guide - eps_[row]) + lgw_mask_inv * (eps_substep_guide_inv - eps_[row]) + + #eps_[row] = slerp_barycentric(eps_[row].norm(), eps_substep_guide.norm(), eps_substep_guide_inv.norm(), 1-lgw_mask-lgw_mask_inv, lgw_mask, lgw_mask_inv) + + elif self.guide_mode in {"epsilon_projection"}: + if self.EO("slerp_epsilon_guide"): + if eps_substep_guide.sum() != 0: + eps_row_slerp = slerp_tensor(self.mask, eps_[row], eps_substep_guide) + if eps_substep_guide_inv.sum() != 0: + eps_row_slerp = slerp_tensor((1-self.mask), eps_row_slerp, eps_substep_guide_inv) + + eps_collinear_eps_slerp = get_collinear(eps_[row], eps_row_slerp) + eps_slerp_ortho_eps = get_orthogonal(eps_row_slerp, eps_[row]) + + eps_sum = eps_collinear_eps_slerp + eps_slerp_ortho_eps + + eps_[row] = slerp_tensor(lgw_mask, eps_[row] , eps_sum) + eps_[row] = slerp_tensor(lgw_mask_inv, eps_[row], eps_sum) + else: + eps_row_lerp = eps_[row] + self.mask * (eps_substep_guide-eps_[row]) + (1-self.mask) * (eps_substep_guide_inv-eps_[row]) + + eps_collinear_eps_lerp = get_collinear(eps_[row], eps_row_lerp) + eps_lerp_ortho_eps = get_orthogonal(eps_row_lerp, eps_[row]) + + eps_sum = eps_collinear_eps_lerp + eps_lerp_ortho_eps + + eps_[row] = eps_[row] + lgw_mask * (eps_sum - eps_[row]) + lgw_mask_inv * (eps_sum - eps_[row]) + + + #eps_row_slerp = eps_[row] + self.mask * (eps_substep_guide-eps_[row]) + (1-self.mask) * (eps_substep_guide_inv-eps_[row]) + + + elif self.guide_mode in {"epsilon_cw", "epsilon_projection_cw"}: + eps_ = self.process_channelwise(x_0, + eps_, + data_, + row, + eps_substep_guide, + eps_substep_guide_inv, + y0, + y0_inv, + lgw_mask, + lgw_mask_inv, + use_projection = self.guide_mode == "epsilon_projection_cw", + channelwise = True + ) + + temporal_smoothing = self.EO("temporal_smoothing", 0.0) + if temporal_smoothing > 0: + eps_[row] = apply_temporal_smoothing(eps_[row], temporal_smoothing) + + if self.EO("substep_eps_ch_mean_std"): + eps_[row] = normalize_latent(eps_[row], eps_orig[row]) + if self.EO("substep_eps_ch_mean"): + eps_[row] = normalize_latent(eps_[row], eps_orig[row], std=False) + if self.EO("substep_eps_ch_std"): + eps_[row] = normalize_latent(eps_[row], eps_orig[row], mean=False) + if self.EO("substep_eps_mean_std"): + eps_[row] = normalize_latent(eps_[row], eps_orig[row], channelwise=False) + if self.EO("substep_eps_mean"): + eps_[row] = normalize_latent(eps_[row], eps_orig[row], std=False, channelwise=False) + if self.EO("substep_eps_std"): + eps_[row] = normalize_latent(eps_[row], eps_orig[row], mean=False, channelwise=False) + return eps_, x_ + + + def process_channelwise(self, + x_0 : Tensor, + eps_ : Tensor, + data_ : Tensor, + row : int, + eps_substep_guide : Tensor, + eps_substep_guide_inv : Tensor, + y0 : Tensor, + y0_inv : Tensor, + lgw_mask : Tensor, + lgw_mask_inv : Tensor, + use_projection : bool = False, + channelwise : bool = False + ): + + avg, avg_inv = 0, 0 + for b, c in itertools.product(range(x_0.shape[0]), range(x_0.shape[1])): + avg += torch.norm(lgw_mask [b][0] * data_[row][b][c] - lgw_mask [b][0] * y0 [b][c]) + avg_inv += torch.norm(lgw_mask_inv[b][0] * data_[row][b][c] - lgw_mask_inv[b][0] * y0_inv[b][c]) + + avg /= x_0.shape[1] + avg_inv /= x_0.shape[1] + + for b, c in itertools.product(range(x_0.shape[0]), range(x_0.shape[1])): + if channelwise: + ratio = torch.nan_to_num(torch.norm(lgw_mask [b][0] * data_[row][b][c] - lgw_mask [b][0] * y0 [b][c]) / avg, 0) + ratio_inv = torch.nan_to_num(torch.norm(lgw_mask_inv[b][0] * data_[row][b][c] - lgw_mask_inv[b][0] * y0_inv[b][c]) / avg_inv, 0) + else: + ratio = 1. + ratio_inv = 1. + + if self.EO("slerp_epsilon_guide"): + if eps_substep_guide[b][c].sum() != 0: + eps_[row][b][c] = slerp_tensor(ratio * lgw_mask[b][0], eps_[row][b][c], eps_substep_guide[b][c]) + if eps_substep_guide_inv[b][c].sum() != 0: + eps_[row][b][c] = slerp_tensor(ratio_inv * lgw_mask_inv[b][0], eps_[row][b][c], eps_substep_guide_inv[b][c]) + else: + eps_[row][b][c] = eps_[row][b][c] + ratio * lgw_mask[b][0] * (eps_substep_guide[b][c] - eps_[row][b][c]) + ratio_inv * lgw_mask_inv[b][0] * (eps_substep_guide_inv[b][c] - eps_[row][b][c]) + + if use_projection: + if self.EO("slerp_epsilon_guide"): + if eps_substep_guide[b][c].sum() != 0: + eps_row_lerp = slerp_tensor(self.mask[b][0], eps_[row][b][c], eps_substep_guide[b][c]) + if eps_substep_guide_inv[b][c].sum() != 0: + eps_row_lerp = slerp_tensor((1-self.mask[b][0]), eps_[row][b][c], eps_substep_guide_inv[b][c]) + else: + eps_row_lerp = eps_[row][b][c] + self.mask[b][0] * (eps_substep_guide[b][c] - eps_[row][b][c]) + (1-self.mask[b][0]) * (eps_substep_guide_inv[b][c] - eps_[row][b][c]) # should this ever be self.mask_inv? + + eps_collinear_eps_lerp = get_collinear (eps_[row][b][c], eps_row_lerp) + eps_lerp_ortho_eps = get_orthogonal(eps_row_lerp , eps_[row][b][c]) + + eps_sum = eps_collinear_eps_lerp + eps_lerp_ortho_eps + + + if self.EO("slerp_epsilon_guide"): + if eps_substep_guide[b][c].sum() != 0: + eps_[row][b][c] = slerp_tensor(ratio * lgw_mask[b][0], eps_[row][b][c], eps_sum) + if eps_substep_guide_inv[b][c].sum() != 0: + eps_[row][b][c] = slerp_tensor(ratio_inv * lgw_mask_inv[b][0], eps_[row][b][c], eps_sum) + else: + eps_[row][b][c] = eps_[row][b][c] + ratio * lgw_mask[b][0] * (eps_sum - eps_[row][b][c]) + ratio_inv * lgw_mask_inv[b][0] * (eps_sum - eps_[row][b][c]) + else: + if self.EO("slerp_epsilon_guide"): + if eps_substep_guide[b][c].sum() != 0: + eps_[row][b][c] = slerp_tensor(ratio * lgw_mask[b][0], eps_[row][b][c], eps_substep_guide[b][c]) + if eps_substep_guide_inv[b][c].sum() != 0: + eps_[row][b][c] = slerp_tensor(ratio_inv * lgw_mask_inv[b][0], eps_[row][b][c], eps_substep_guide_inv[b][c]) + else: + eps_[row][b][c] = eps_[row][b][c] + ratio * lgw_mask[b][0] * (eps_substep_guide[b][c] - eps_[row][b][c]) + ratio_inv * lgw_mask_inv[b][0] * (eps_substep_guide_inv[b][c] - eps_[row][b][c]) + + return eps_ + + + def normalize_inputs(self, x:Tensor, y0:Tensor, y0_inv:Tensor): + """ + Modifies and returns 'x' by matching its mean and/or std to y0 and/or y0_inv. + Controlled by extra_options. + + Returns: + - x (modified) + - y0 (may be modified to match mean and std from y0_inv) + - y0_inv (unchanged) + """ + if self.guide_mode == "epsilon_guide_mean_std_from_bkg": + y0 = normalize_latent(y0, y0_inv) + + input_norm = self.EO("input_norm", "") + input_std = self.EO("input_std", 1.0) + + if input_norm == "input_ch_mean_set_std_to": + x = normalize_latent(x, set_std=input_std) + + if input_norm == "input_ch_set_std_to": + x = normalize_latent(x, set_std=input_std, mean=False) + + if input_norm == "input_mean_set_std_to": + x = normalize_latent(x, set_std=input_std, channelwise=False) + + if input_norm == "input_std_set_std_to": + x = normalize_latent(x, set_std=input_std, mean=False, channelwise=False) + + return x, y0, y0_inv + + + +def apply_frame_weights(mask, frame_weights, normalize=False): + original_mask_mean = mask.mean() + if frame_weights is not None: + for f in range(mask.shape[2]): + frame_weight = frame_weights[f] + mask[..., f:f+1, :, :] *= frame_weight + if normalize: + mask_mean = mask.mean() + mask *= (original_mask_mean / mask_mean) + + + +def prepare_mask(x, mask, LGW_MASK_RESCALE_MIN) -> tuple[torch.Tensor, bool]: + if mask is None: + mask = torch.ones_like(x[:,0:1,...]) + LGW_MASK_RESCALE_MIN = False + return mask, LGW_MASK_RESCALE_MIN + + target_height = x.shape[-2] + target_width = x.shape[-1] + + spatial_mask = None + if x.ndim == 5 and mask.shape[0] > 1 and mask.ndim < 4: + target_frames = x.shape[-3] + spatial_mask = mask.unsqueeze(0).unsqueeze(0) # [B, H, W] -> [1, 1, B, H, W] + spatial_mask = F.interpolate(spatial_mask, + size=(target_frames, target_height, target_width), + mode='trilinear', + align_corners=False) # [1, 1, F, H, W] + repeat_shape = [1] # batch + for i in range(1, x.ndim - 3): + repeat_shape.append(x.shape[i]) + repeat_shape.extend([1, 1, 1]) # frames, height, width + elif mask.ndim == 4: #temporal mask batch + mask = F.interpolate(mask, size=(target_height, target_width), mode='bilinear', align_corners=False) + mask = mask.repeat(x.shape[-4],1,1,1) + mask.unsqueeze_(0) + + else: + spatial_mask = mask.unsqueeze(1) + spatial_mask = F.interpolate(spatial_mask, size=(target_height, target_width), mode='bilinear', align_corners=False) + + while spatial_mask.ndim < x.ndim: + spatial_mask = spatial_mask.unsqueeze(2) + + repeat_shape = [1] # batch + for i in range(1, x.ndim - 2): + repeat_shape.append(x.shape[i]) + repeat_shape.extend([1, 1]) # height and width + repeat_shape[1] = 1 # only need one channel for masks + + if spatial_mask is not None: + mask = spatial_mask.repeat(*repeat_shape).to(x.dtype) + + del spatial_mask + return mask, LGW_MASK_RESCALE_MIN + +def apply_temporal_smoothing(tensor, temporal_smoothing): + if temporal_smoothing <= 0 or tensor.ndim != 5: + return tensor + + kernel_size = 5 + padding = kernel_size // 2 + temporal_kernel = torch.tensor( + [0.1, 0.2, 0.4, 0.2, 0.1], + device=tensor.device, dtype=tensor.dtype + ) * temporal_smoothing + temporal_kernel[kernel_size//2] += (1 - temporal_smoothing) + temporal_kernel = temporal_kernel / temporal_kernel.sum() + + # resahpe for conv1d + b, c, f, h, w = tensor.shape + data_flat = tensor.permute(0, 1, 3, 4, 2).reshape(-1, f) + + # apply smoohting + data_smooth = F.conv1d( + data_flat.unsqueeze(1), + temporal_kernel.view(1, 1, -1), + padding=padding + ).squeeze(1) + + return data_smooth.view(b, c, h, w, f).permute(0, 1, 4, 2, 3) + +def get_guide_epsilon_substep(x_0, x_, y0, y0_inv, s_, row, row_offset, rk_type, b=None, c=None): + s_in = x_0.new_ones([x_0.shape[0]]) + + if b is not None and c is not None: + index = (b, c) + elif b is not None: + index = (b,) + else: + index = () + + if RK_Method_Beta.is_exponential(rk_type): + eps_row = y0 [index] - x_0[index] + eps_row_inv = y0_inv[index] - x_0[index] + else: + eps_row = (x_[row][index] - y0 [index]) / (s_[row] * s_in) # was row+row_offset before for x_!! not right... also? potential issues here with x_[row+1] being RK.rows+2 with gauss-legendre_2s 1 imp step 1 imp substep + eps_row_inv = (x_[row][index] - y0_inv[index]) / (s_[row] * s_in) + + return eps_row, eps_row_inv + +def get_guide_epsilon(x_0, x_, y0, sigma, rk_type, b=None, c=None): + s_in = x_0.new_ones([x_0.shape[0]]) + + if b is not None and c is not None: + index = (b, c) + elif b is not None: + index = (b,) + else: + index = () + + if RK_Method_Beta.is_exponential(rk_type): + eps = y0 [index] - x_0[index] + else: + eps = (x_[index] - y0 [index]) / (sigma * s_in) + + return eps + + + +@torch.no_grad +def noise_cossim_guide_tiled(x_list, guide, cossim_mode="forward", tile_size=2, step=0): + + guide_tiled = rearrange(guide, "b c (h t1) (w t2) -> b (t1 t2) c h w", t1=tile_size, t2=tile_size) + + x_tiled_list = [ + rearrange(x, "b c (h t1) (w t2) -> b (t1 t2) c h w", t1=tile_size, t2=tile_size) + for x in x_list + ] + x_tiled_stack = torch.stack([x_tiled[0] for x_tiled in x_tiled_list]) # [n_x, n_tiles, c, h, w] + + guide_flat = guide_tiled[0].view(guide_tiled.shape[1], -1).unsqueeze(0) # [1, n_tiles, c*h*w] + x_flat = x_tiled_stack.view(x_tiled_stack.size(0), x_tiled_stack.size(1), -1) # [n_x, n_tiles, c*h*w] + + cossim_tmp_all = F.cosine_similarity(x_flat, guide_flat, dim=-1) # [n_x, n_tiles] + + if cossim_mode == "forward": + indices = cossim_tmp_all.argmax(dim=0) + elif cossim_mode == "reverse": + indices = cossim_tmp_all.argmin(dim=0) + elif cossim_mode == "orthogonal": + indices = torch.abs(cossim_tmp_all).argmin(dim=0) + elif cossim_mode == "forward_reverse": + if step % 2 == 0: + indices = cossim_tmp_all.argmax(dim=0) + else: + indices = cossim_tmp_all.argmin(dim=0) + elif cossim_mode == "reverse_forward": + if step % 2 == 1: + indices = cossim_tmp_all.argmax(dim=0) + else: + indices = cossim_tmp_all.argmin(dim=0) + elif cossim_mode == "orthogonal_reverse": + if step % 2 == 0: + indices = torch.abs(cossim_tmp_all).argmin(dim=0) + else: + indices = cossim_tmp_all.argmin(dim=0) + elif cossim_mode == "reverse_orthogonal": + if step % 2 == 1: + indices = torch.abs(cossim_tmp_all).argmin(dim=0) + else: + indices = cossim_tmp_all.argmin(dim=0) + else: + target_value = float(cossim_mode) + indices = torch.abs(cossim_tmp_all - target_value).argmin(dim=0) + + x_tiled_out = x_tiled_stack[indices, torch.arange(indices.size(0))] # [n_tiles, c, h, w] + + x_tiled_out = x_tiled_out.unsqueeze(0) + x_detiled = rearrange(x_tiled_out, "b (t1 t2) c h w -> b c (h t1) (w t2)", t1=tile_size, t2=tile_size) + + return x_detiled + + +@torch.no_grad +def noise_cossim_eps_tiled(x_list, eps, noise_list, cossim_mode="forward", tile_size=2, step=0): + + eps_tiled = rearrange(eps, "b c (h t1) (w t2) -> b (t1 t2) c h w", t1=tile_size, t2=tile_size) + x_tiled_list = [ + rearrange(x, "b c (h t1) (w t2) -> b (t1 t2) c h w", t1=tile_size, t2=tile_size) + for x in x_list + ] + noise_tiled_list = [ + rearrange(noise, "b c (h t1) (w t2) -> b (t1 t2) c h w", t1=tile_size, t2=tile_size) + for noise in noise_list + ] + + noise_tiled_stack = torch.stack([noise_tiled[0] for noise_tiled in noise_tiled_list]) # [n_x, n_tiles, c, h, w] + eps_expanded = eps_tiled[0].view(eps_tiled.shape[1], -1).unsqueeze(0) # [1, n_tiles, c*h*w] + noise_flat = noise_tiled_stack.view(noise_tiled_stack.size(0), noise_tiled_stack.size(1), -1) # [n_x, n_tiles, c*h*w] + cossim_tmp_all = F.cosine_similarity(noise_flat, eps_expanded, dim=-1) # [n_x, n_tiles] + + if cossim_mode == "forward": + indices = cossim_tmp_all.argmax(dim=0) + elif cossim_mode == "reverse": + indices = cossim_tmp_all.argmin(dim=0) + elif cossim_mode == "orthogonal": + indices = torch.abs(cossim_tmp_all).argmin(dim=0) + elif cossim_mode == "orthogonal_pos": + positive_mask = cossim_tmp_all > 0 + positive_tmp = torch.where(positive_mask, cossim_tmp_all, torch.full_like(cossim_tmp_all, float('inf'))) + indices = positive_tmp.argmin(dim=0) + elif cossim_mode == "orthogonal_neg": + negative_mask = cossim_tmp_all < 0 + negative_tmp = torch.where(negative_mask, cossim_tmp_all, torch.full_like(cossim_tmp_all, float('-inf'))) + indices = negative_tmp.argmax(dim=0) + elif cossim_mode == "orthogonal_posneg": + if step % 2 == 0: + positive_mask = cossim_tmp_all > 0 + positive_tmp = torch.where(positive_mask, cossim_tmp_all, torch.full_like(cossim_tmp_all, float('inf'))) + indices = positive_tmp.argmin(dim=0) + else: + negative_mask = cossim_tmp_all < 0 + negative_tmp = torch.where(negative_mask, cossim_tmp_all, torch.full_like(cossim_tmp_all, float('-inf'))) + indices = negative_tmp.argmax(dim=0) + elif cossim_mode == "orthogonal_negpos": + if step % 2 == 1: + positive_mask = cossim_tmp_all > 0 + positive_tmp = torch.where(positive_mask, cossim_tmp_all, torch.full_like(cossim_tmp_all, float('inf'))) + indices = positive_tmp.argmin(dim=0) + else: + negative_mask = cossim_tmp_all < 0 + negative_tmp = torch.where(negative_mask, cossim_tmp_all, torch.full_like(cossim_tmp_all, float('-inf'))) + indices = negative_tmp.argmax(dim=0) + elif cossim_mode == "forward_reverse": + if step % 2 == 0: + indices = cossim_tmp_all.argmax(dim=0) + else: + indices = cossim_tmp_all.argmin(dim=0) + elif cossim_mode == "reverse_forward": + if step % 2 == 1: + indices = cossim_tmp_all.argmax(dim=0) + else: + indices = cossim_tmp_all.argmin(dim=0) + elif cossim_mode == "orthogonal_reverse": + if step % 2 == 0: + indices = torch.abs(cossim_tmp_all).argmin(dim=0) + else: + indices = cossim_tmp_all.argmin(dim=0) + elif cossim_mode == "reverse_orthogonal": + if step % 2 == 1: + indices = torch.abs(cossim_tmp_all).argmin(dim=0) + else: + indices = cossim_tmp_all.argmin(dim=0) + else: + target_value = float(cossim_mode) + indices = torch.abs(cossim_tmp_all - target_value).argmin(dim=0) + #else: + # raise ValueError(f"Unknown cossim_mode: {cossim_mode}") + + x_tiled_stack = torch.stack([x_tiled[0] for x_tiled in x_tiled_list]) # [n_x, n_tiles, c, h, w] + x_tiled_out = x_tiled_stack[indices, torch.arange(indices.size(0))] # [n_tiles, c, h, w] + + x_tiled_out = x_tiled_out.unsqueeze(0) # restore batch dim + x_detiled = rearrange(x_tiled_out, "b (t1 t2) c h w -> b c (h t1) (w t2)", t1=tile_size, t2=tile_size) + return x_detiled + + + +@torch.no_grad +def noise_cossim_guide_eps_tiled(x_0, x_list, y0, noise_list, cossim_mode="forward", tile_size=2, step=0, sigma=None, rk_type=None): + + x_tiled_stack = torch.stack([ + rearrange(x, "b c (h t1) (w t2) -> b (t1 t2) c h w", t1=tile_size, t2=tile_size)[0] + for x in x_list + ]) # [n_x, n_tiles, c, h, w] + eps_guide_stack = torch.stack([ + rearrange(x - y0, "b c (h t1) (w t2) -> b (t1 t2) c h w", t1=tile_size, t2=tile_size)[0] + for x in x_list + ]) # [n_x, n_tiles, c, h, w] + del x_list + + noise_tiled_stack = torch.stack([ + rearrange(noise, "b c (h t1) (w t2) -> b (t1 t2) c h w", t1=tile_size, t2=tile_size)[0] + for noise in noise_list + ]) # [n_x, n_tiles, c, h, w] + del noise_list + + noise_flat = noise_tiled_stack.view(noise_tiled_stack.size(0), noise_tiled_stack.size(1), -1) # [n_x, n_tiles, c*h*w] + eps_guide_flat = eps_guide_stack.view(eps_guide_stack.size(0), eps_guide_stack.size(1), -1) # [n_x, n_tiles, c*h*w] + + cossim_tmp_all = F.cosine_similarity(noise_flat, eps_guide_flat, dim=-1) # [n_x, n_tiles] + del noise_tiled_stack, noise_flat, eps_guide_stack, eps_guide_flat + + if cossim_mode == "forward": + indices = cossim_tmp_all.argmax(dim=0) + elif cossim_mode == "reverse": + indices = cossim_tmp_all.argmin(dim=0) + elif cossim_mode == "orthogonal": + indices = torch.abs(cossim_tmp_all).argmin(dim=0) + elif cossim_mode == "orthogonal_pos": + positive_mask = cossim_tmp_all > 0 + positive_tmp = torch.where(positive_mask, cossim_tmp_all, torch.full_like(cossim_tmp_all, float('inf'))) + indices = positive_tmp.argmin(dim=0) + elif cossim_mode == "orthogonal_neg": + negative_mask = cossim_tmp_all < 0 + negative_tmp = torch.where(negative_mask, cossim_tmp_all, torch.full_like(cossim_tmp_all, float('-inf'))) + indices = negative_tmp.argmax(dim=0) + elif cossim_mode == "orthogonal_posneg": + if step % 2 == 0: + positive_mask = cossim_tmp_all > 0 + positive_tmp = torch.where(positive_mask, cossim_tmp_all, torch.full_like(cossim_tmp_all, float('inf'))) + indices = positive_tmp.argmin(dim=0) + else: + negative_mask = cossim_tmp_all < 0 + negative_tmp = torch.where(negative_mask, cossim_tmp_all, torch.full_like(cossim_tmp_all, float('-inf'))) + indices = negative_tmp.argmax(dim=0) + elif cossim_mode == "orthogonal_negpos": + if step % 2 == 1: + positive_mask = cossim_tmp_all > 0 + positive_tmp = torch.where(positive_mask, cossim_tmp_all, torch.full_like(cossim_tmp_all, float('inf'))) + indices = positive_tmp.argmin(dim=0) + else: + negative_mask = cossim_tmp_all < 0 + negative_tmp = torch.where(negative_mask, cossim_tmp_all, torch.full_like(cossim_tmp_all, float('-inf'))) + indices = negative_tmp.argmax(dim=0) + elif cossim_mode == "forward_reverse": + if step % 2 == 0: + indices = cossim_tmp_all.argmax(dim=0) + else: + indices = cossim_tmp_all.argmin(dim=0) + elif cossim_mode == "reverse_forward": + if step % 2 == 1: + indices = cossim_tmp_all.argmax(dim=0) + else: + indices = cossim_tmp_all.argmin(dim=0) + elif cossim_mode == "orthogonal_reverse": + if step % 2 == 0: + indices = torch.abs(cossim_tmp_all).argmin(dim=0) + else: + indices = cossim_tmp_all.argmin(dim=0) + elif cossim_mode == "reverse_orthogonal": + if step % 2 == 1: + indices = torch.abs(cossim_tmp_all).argmin(dim=0) + else: + indices = cossim_tmp_all.argmin(dim=0) + else: + target_value = float(cossim_mode) + indices = torch.abs(cossim_tmp_all - target_value).argmin(dim=0) + + x_tiled_out = x_tiled_stack[indices, torch.arange(indices.size(0))] # [n_tiles, c, h, w] + del x_tiled_stack + + x_tiled_out = x_tiled_out.unsqueeze(0) + x_detiled = rearrange(x_tiled_out, "b (t1 t2) c h w -> b c (h t1) (w t2)", t1=tile_size, t2=tile_size) + + return x_detiled + + + + + + + +class NoiseStepHandlerOSDE: + def __init__(self, x, eps=None, data=None, x_init=None, guide=None, guide_bkg=None): + self.noise = None + self.x = x + self.eps = eps + self.data = data + self.x_init = x_init + self.guide = guide + self.guide_bkg = guide_bkg + + self.eps_list = None + + self.noise_cossim_map = { + "eps_orthogonal": [self.noise, self.eps], + "eps_data_orthogonal": [self.noise, self.eps, self.data], + + "data_orthogonal": [self.noise, self.data], + "xinit_orthogonal": [self.noise, self.x_init], + + "x_orthogonal": [self.noise, self.x], + "x_data_orthogonal": [self.noise, self.x, self.data], + "x_eps_orthogonal": [self.noise, self.x, self.eps], + + "x_eps_data_orthogonal": [self.noise, self.x, self.eps, self.data], + "x_eps_data_xinit_orthogonal": [self.noise, self.x, self.eps, self.data, self.x_init], + + "x_eps_guide_orthogonal": [self.noise, self.x, self.eps, self.guide], + "x_eps_guide_bkg_orthogonal": [self.noise, self.x, self.eps, self.guide_bkg], + + "noise_orthogonal": [self.noise, self.x_init], + + "guide_orthogonal": [self.noise, self.guide], + "guide_bkg_orthogonal": [self.noise, self.guide_bkg], + } + + def check_cossim_source(self, source): + return source in self.noise_cossim_map + + def get_ortho_noise(self, noise, prev_noises=None, max_iter=100, max_score=1e-7, NOISE_COSSIM_SOURCE="eps_orthogonal"): + + if NOISE_COSSIM_SOURCE not in self.noise_cossim_map: + raise ValueError(f"Invalid NOISE_COSSIM_SOURCE: {NOISE_COSSIM_SOURCE}") + + self.noise_cossim_map[NOISE_COSSIM_SOURCE][0] = noise + + params = self.noise_cossim_map[NOISE_COSSIM_SOURCE] + + noise = get_orthogonal_noise_from_channelwise(*params, max_iter=max_iter, max_score=max_score) + + return noise + + + + + +# NOTE: NS AND SUBSTEP ADDED! +def handle_tiled_etc_noise_steps( + x_0, + x, + x_prenoise, + x_init, + eps, + denoised, + y0, + y0_inv, + step, + rk_type, + RK, + NS, + SUBSTEP, + sigma_up, + sigma, + sigma_next, + alpha_ratio, + s_noise, + noise_mode, + SDE_NOISE_EXTERNAL, + sde_noise_t, + NOISE_COSSIM_SOURCE, + NOISE_COSSIM_MODE, + noise_cossim_tile_size, + noise_cossim_iterations, + extra_options): + + EO = ExtraOptions(extra_options) + + x_tmp = [] + cossim_tmp = [] + noise_tmp_list = [] + + if step > EO("noise_cossim_end_step", MAX_STEPS): + NOISE_COSSIM_SOURCE = EO("noise_cossim_takeover_source" , "eps") + NOISE_COSSIM_MODE = EO("noise_cossim_takeover_mode" , "forward" ) + noise_cossim_tile_size = EO("noise_cossim_takeover_tile" , noise_cossim_tile_size ) + noise_cossim_iterations = EO("noise_cossim_takeover_iterations", noise_cossim_iterations) + + for i in range(noise_cossim_iterations): + #x_tmp.append(NS.swap_noise(x_0, x, sigma, sigma, sigma_next, )) + x_tmp.append(NS.add_noise_post(x, sigma_up, sigma, sigma_next, alpha_ratio, s_noise, noise_mode, SDE_NOISE_EXTERNAL, sde_noise_t) )#y0, lgw, sigma_down are currently unused + noise_tmp = x_tmp[i] - x + if EO("noise_noise_zscore_norm"): + noise_tmp = normalize_zscore(noise_tmp, channelwise=False, inplace=True) + if EO("noise_noise_zscore_norm_cw"): + noise_tmp = normalize_zscore(noise_tmp, channelwise=True, inplace=True) + if EO("noise_eps_zscore_norm"): + eps = normalize_zscore(eps, channelwise=False, inplace=True) + if EO("noise_eps_zscore_norm_cw"): + eps = normalize_zscore(eps, channelwise=True, inplace=True) + + if NOISE_COSSIM_SOURCE in ("eps_tiled", "guide_epsilon_tiled", "guide_bkg_epsilon_tiled", "iig_tiled"): + noise_tmp_list.append(noise_tmp) + if NOISE_COSSIM_SOURCE == "eps": + cossim_tmp.append(get_cosine_similarity(eps, noise_tmp)) + if NOISE_COSSIM_SOURCE == "eps_ch": + cossim_total = torch.zeros_like(eps[0][0][0][0]) + for ch in range(eps.shape[1]): + cossim_total += get_cosine_similarity(eps[0][ch], noise_tmp[0][ch]) + cossim_tmp.append(cossim_total) + elif NOISE_COSSIM_SOURCE == "data": + cossim_tmp.append(get_cosine_similarity(denoised, noise_tmp)) + elif NOISE_COSSIM_SOURCE == "latent": + cossim_tmp.append(get_cosine_similarity(x_prenoise, noise_tmp)) + elif NOISE_COSSIM_SOURCE == "x_prenoise": + cossim_tmp.append(get_cosine_similarity(x_prenoise, x_tmp[i])) + elif NOISE_COSSIM_SOURCE == "x": + cossim_tmp.append(get_cosine_similarity(x, x_tmp[i])) + elif NOISE_COSSIM_SOURCE == "x_data": + cossim_tmp.append(get_cosine_similarity(denoised, x_tmp[i])) + elif NOISE_COSSIM_SOURCE == "x_init_vs_noise": + cossim_tmp.append(get_cosine_similarity(x_init, noise_tmp)) + elif NOISE_COSSIM_SOURCE == "mom": + cossim_tmp.append(get_cosine_similarity(denoised, x + sigma_next*noise_tmp)) + elif NOISE_COSSIM_SOURCE == "guide": + cossim_tmp.append(get_cosine_similarity(y0, x_tmp[i])) + elif NOISE_COSSIM_SOURCE == "guide_bkg": + cossim_tmp.append(get_cosine_similarity(y0_inv, x_tmp[i])) + + if step < EO("noise_cossim_start_step", 0): + x = x_tmp[0] + + elif (NOISE_COSSIM_SOURCE == "eps_tiled"): + x = noise_cossim_eps_tiled(x_tmp, eps, noise_tmp_list, cossim_mode=NOISE_COSSIM_MODE, tile_size=noise_cossim_tile_size, step=step) + elif (NOISE_COSSIM_SOURCE == "guide_epsilon_tiled"): + x = noise_cossim_guide_eps_tiled(x_0, x_tmp, y0, noise_tmp_list, cossim_mode=NOISE_COSSIM_MODE, tile_size=noise_cossim_tile_size, step=step, sigma=sigma, rk_type=rk_type) + elif (NOISE_COSSIM_SOURCE == "guide_bkg_epsilon_tiled"): + x = noise_cossim_guide_eps_tiled(x_0, x_tmp, y0_inv, noise_tmp_list, cossim_mode=NOISE_COSSIM_MODE, tile_size=noise_cossim_tile_size, step=step, sigma=sigma, rk_type=rk_type) + elif (NOISE_COSSIM_SOURCE == "guide_tiled"): + x = noise_cossim_guide_tiled(x_tmp, y0, cossim_mode=NOISE_COSSIM_MODE, tile_size=noise_cossim_tile_size, step=step) + elif (NOISE_COSSIM_SOURCE == "guide_bkg_tiled"): + x = noise_cossim_guide_tiled(x_tmp, y0_inv, cossim_mode=NOISE_COSSIM_MODE, tile_size=noise_cossim_tile_size) + else: + for i in range(len(x_tmp)): + if (NOISE_COSSIM_MODE == "forward") and (cossim_tmp[i] == max(cossim_tmp)): + x = x_tmp[i] + break + elif (NOISE_COSSIM_MODE == "reverse") and (cossim_tmp[i] == min(cossim_tmp)): + x = x_tmp[i] + break + elif (NOISE_COSSIM_MODE == "orthogonal") and (abs(cossim_tmp[i]) == min(abs(val) for val in cossim_tmp)): + x = x_tmp[i] + break + elif (NOISE_COSSIM_MODE != "forward") and (NOISE_COSSIM_MODE != "reverse") and (NOISE_COSSIM_MODE != "orthogonal"): + x = x_tmp[0] + break + return x + + + + + +def get_masked_epsilon_projection(x_0, x_, eps_, y0, y0_inv, s_, row, row_offset, rk_type, LG, step): + + eps_row, eps_row_inv = get_guide_epsilon_substep(x_0, x_, y0, y0_inv, s_, row, row_offset, rk_type) + eps_row_lerp = eps_[row] + LG.mask * (eps_row-eps_[row]) + (1-LG.mask) * (eps_row_inv-eps_[row]) + eps_collinear_eps_lerp = get_collinear(eps_[row], eps_row_lerp) + eps_lerp_ortho_eps = get_orthogonal(eps_row_lerp, eps_[row]) + eps_sum = eps_collinear_eps_lerp + eps_lerp_ortho_eps + lgw_mask, lgw_mask_inv = LG.get_masks_for_step(step) + eps_substep_guide = eps_[row] + lgw_mask * (eps_sum - eps_[row]) + lgw_mask_inv * (eps_sum - eps_[row]) + return eps_substep_guide + + + diff --git a/ComfyUI/custom_nodes/RES4LYF/beta/rk_method_beta.py b/ComfyUI/custom_nodes/RES4LYF/beta/rk_method_beta.py new file mode 100644 index 0000000000000000000000000000000000000000..89746d8142c77ae233980c78481ebf6c3eda6fdb --- /dev/null +++ b/ComfyUI/custom_nodes/RES4LYF/beta/rk_method_beta.py @@ -0,0 +1,1170 @@ +import torch +from torch import Tensor +from typing import Optional, Callable, Tuple, List, Dict, Any, Union + +import comfy.model_patcher +import comfy.supported_models + +import itertools + +from .phi_functions import Phi +from .rk_coefficients_beta import get_implicit_sampler_name_list, get_rk_methods_beta +from ..helper import ExtraOptions +from ..latents import get_orthogonal, get_collinear, get_cosine_similarity, tile_latent, untile_latent + +from ..res4lyf import RESplain + +MAX_STEPS = 10000 + + +def get_data_from_step (x:Tensor, x_next:Tensor, sigma:Tensor, sigma_next:Tensor) -> Tensor: + h = sigma_next - sigma + return (sigma_next * x - sigma * x_next) / h + +def get_epsilon_from_step(x:Tensor, x_next:Tensor, sigma:Tensor, sigma_next:Tensor) -> Tensor: + h = sigma_next - sigma + return (x - x_next) / h + + + +class RK_Method_Beta: + def __init__(self, + model, + rk_type : str, + VE_MODEL : bool, + noise_anchor : float, + noise_boost_normalize : bool = True, + model_device : str = 'cuda', + work_device : str = 'cpu', + dtype : torch.dtype = torch.float64, + extra_options : str = "" + ): + + self.work_device = work_device + self.model_device = model_device + self.dtype : torch.dtype = dtype + + self.model = model + + if hasattr(model, "model"): + model_sampling = model.model.model_sampling + elif hasattr(model, "inner_model"): + model_sampling = model.inner_model.inner_model.model_sampling + + self.sigma_min : Tensor = model_sampling.sigma_min.to(dtype=dtype, device=work_device) + self.sigma_max : Tensor = model_sampling.sigma_max.to(dtype=dtype, device=work_device) + + self.rk_type : str = rk_type + + self.IMPLICIT : str = rk_type in get_implicit_sampler_name_list(nameOnly=True) + self.EXPONENTIAL : bool = RK_Method_Beta.is_exponential(rk_type) + self.VE_MODEL : bool = VE_MODEL + + self.SYNC_SUBSTEP_MEAN_CW : bool = noise_boost_normalize + + self.A : Optional[Tensor] = None + self.B : Optional[Tensor] = None + self.U : Optional[Tensor] = None + self.V : Optional[Tensor] = None + + self.rows : int = 0 + self.cols : int = 0 + + self.denoised : Optional[Tensor] = None + self.uncond : Optional[Tensor] = None + + self.y0 : Optional[Tensor] = None + self.y0_inv : Optional[Tensor] = None + + self.multistep_stages : int = 0 + self.row_offset : Optional[int] = None + + self.cfg_cw : float = 1.0 + self.extra_args : Optional[Dict[str, Any]] = None + + self.extra_options : str = extra_options + self.EO : ExtraOptions = ExtraOptions(extra_options) + + self.reorder_tableau_indices : list[int] = self.EO("reorder_tableau_indices", [-1]) + + self.LINEAR_ANCHOR_X_0 : float = noise_anchor + + self.tile_sizes : Optional[List[Tuple[int,int]]] = None + self.tile_cnt : int = 0 + self.latent_compression_ratio : int = 8 + + @staticmethod + def is_exponential(rk_type:str) -> bool: + if rk_type.startswith(( "res", + "dpmpp", + "ddim", + "pec", + "etdrk", + "lawson", + "abnorsett", + )): + return True + else: + return False + + @staticmethod + def create(model, + rk_type : str, + VE_MODEL : bool, + noise_anchor : float = 1.0, + noise_boost_normalize : bool = True, + model_device : str = 'cuda', + work_device : str = 'cpu', + dtype : torch.dtype = torch.float64, + extra_options : str = "" + ) -> "Union[RK_Method_Exponential, RK_Method_Linear]": + + if RK_Method_Beta.is_exponential(rk_type): + return RK_Method_Exponential(model, rk_type, VE_MODEL, noise_anchor, noise_boost_normalize, model_device, work_device, dtype, extra_options) + else: + return RK_Method_Linear (model, rk_type, VE_MODEL, noise_anchor, noise_boost_normalize, model_device, work_device, dtype, extra_options) + + def __call__(self): + raise NotImplementedError("This method got clownsharked!") + + def model_epsilon(self, x:Tensor, sigma:Tensor, **extra_args) -> Tuple[Tensor, Tensor]: + s_in = x.new_ones([x.shape[0]]) + denoised = self.model(x, sigma * s_in, **extra_args) + denoised = self.calc_cfg_channelwise(denoised) + eps = (x - denoised) / (sigma * s_in).view(x.shape[0], 1, 1, 1) #return x0 ###################################THIS WORKS ONLY WITH THE MODEL SAMPLING PATCH + return eps, denoised + + def model_denoised(self, x:Tensor, sigma:Tensor, **extra_args) -> Tensor: + s_in = x.new_ones([x.shape[0]]) + control_tiles = None + y0_style_pos = self.extra_args['model_options']['transformer_options'].get("y0_style_pos") + y0_style_neg = self.extra_args['model_options']['transformer_options'].get("y0_style_neg") + y0_style_pos_tile, sy0_style_neg_tiles = None, None + + if self.EO("tile_model_calls"): + tile_h = self.EO("tile_h", 128) + tile_w = self.EO("tile_w", 128) + + denoised_tiles = [] + + tiles, orig_shape, grid, strides = tile_latent(x, tile_size=(tile_h,tile_w)) + + for i in range(tiles.shape[0]): + tile = tiles[i].unsqueeze(0) + + denoised_tile = self.model(tile, sigma * s_in, **extra_args) + + denoised_tiles.append(denoised_tile) + + denoised_tiles = torch.cat(denoised_tiles, dim=0) + + denoised = untile_latent(denoised_tiles, orig_shape, grid, strides) + + elif self.tile_sizes is not None: + tile_h_full = self.tile_sizes[self.tile_cnt % len(self.tile_sizes)][0] + tile_w_full = self.tile_sizes[self.tile_cnt % len(self.tile_sizes)][1] + + if tile_h_full == -1: + tile_h = x.shape[-2] + tile_h_full = tile_h * self.latent_compression_ratio + else: + tile_h = tile_h_full // self.latent_compression_ratio + + if tile_w_full == -1: + tile_w = x.shape[-1] + tile_w_full = tile_w * self.latent_compression_ratio + else: + tile_w = tile_w_full // self.latent_compression_ratio + + #tile_h = tile_h_full // self.latent_compression_ratio + #tile_w = tile_w_full // self.latent_compression_ratio + + self.tile_cnt += 1 + + #if len(self.tile_sizes) == 1 and self.tile_cnt % 2 == 1: + # tile_h, tile_w = tile_w, tile_h + # tile_h_full, tile_w_full = tile_w_full, tile_h_full + + if (self.tile_cnt // len(self.tile_sizes)) % 2 == 1 and self.EO("tiles_autorotate"): + tile_h, tile_w = tile_w, tile_h + tile_h_full, tile_w_full = tile_w_full, tile_h_full + + xt_negative = self.model.inner_model.conds.get('xt_negative', self.model.inner_model.conds.get('negative')) + negative_control = xt_negative[0].get('control') + + if negative_control is not None and hasattr(negative_control, 'cond_hint_original'): + negative_cond_hint_init = negative_control.cond_hint.clone() if negative_control.cond_hint is not None else None + + xt_positive = self.model.inner_model.conds.get('xt_positive', self.model.inner_model.conds.get('positive')) + positive_control = xt_positive[0].get('control') + + if positive_control is not None and hasattr(positive_control, 'cond_hint_original'): + positive_cond_hint_init = positive_control.cond_hint.clone() if positive_control.cond_hint is not None else None + if positive_control.cond_hint_original.shape[-1] != x.shape[-2] * self.latent_compression_ratio or positive_control.cond_hint_original.shape[-2] != x.shape[-1] * self.latent_compression_ratio: + positive_control_pretile = comfy.utils.bislerp(positive_control.cond_hint_original.clone().to(torch.float16).to('cuda'), x.shape[-1] * self.latent_compression_ratio, x.shape[-2] * self.latent_compression_ratio) + positive_control.cond_hint_original = positive_control_pretile.to(positive_control.cond_hint_original) + positive_control_pretile = positive_control.cond_hint_original.clone().to(torch.float16).to('cuda') + control_tiles, control_orig_shape, control_grid, control_strides = tile_latent(positive_control_pretile, tile_size=(tile_h_full,tile_w_full)) + control_tiles = control_tiles + + denoised_tiles = [] + + tiles, orig_shape, grid, strides = tile_latent(x, tile_size=(tile_h,tile_w)) + + if y0_style_pos is not None: + y0_style_pos_tiles, _, _, _ = tile_latent(y0_style_pos, tile_size=(tile_h,tile_w)) + if y0_style_neg is not None: + y0_style_neg_tiles, _, _, _ = tile_latent(y0_style_neg, tile_size=(tile_h,tile_w)) + + for i in range(tiles.shape[0]): + tile = tiles[i].unsqueeze(0) + self.extra_args['model_options']['transformer_options']['x_tmp'] = tile + if control_tiles is not None: + positive_control.cond_hint = control_tiles[i].unsqueeze(0).to(positive_control.cond_hint) + if negative_control is not None: + negative_control.cond_hint = control_tiles[i].unsqueeze(0).to(positive_control.cond_hint) + + if y0_style_pos is not None: + self.extra_args['model_options']['transformer_options']['y0_style_pos'] = y0_style_pos_tiles[i].unsqueeze(0) + if y0_style_neg is not None: + self.extra_args['model_options']['transformer_options']['y0_style_neg'] = y0_style_neg_tiles[i].unsqueeze(0) + + denoised_tile = self.model(tile, sigma * s_in, **extra_args) + + denoised_tiles.append(denoised_tile) + + denoised_tiles = torch.cat(denoised_tiles, dim=0) + + denoised = untile_latent(denoised_tiles, orig_shape, grid, strides) + + else: + denoised = self.model(x, sigma * s_in, **extra_args) + + if control_tiles is not None: + positive_control.cond_hint = positive_cond_hint_init + if negative_control is not None: + negative_control.cond_hint = negative_cond_hint_init + + if y0_style_pos is not None: + self.extra_args['model_options']['transformer_options']['y0_style_pos'] = y0_style_pos + if y0_style_neg is not None: + self.extra_args['model_options']['transformer_options']['y0_style_neg'] = y0_style_neg + + denoised = self.calc_cfg_channelwise(denoised) + return denoised + + def update_transformer_options(self, + transformer_options : Optional[dict] = None, + ): + + self.extra_args.setdefault("model_options", {}).setdefault("transformer_options", {}).update(transformer_options) + return + + def set_coeff(self, + rk_type : str, + h : Tensor, + c1 : float = 0.0, + c2 : float = 0.5, + c3 : float = 1.0, + step : int = 0, + sigmas : Optional[Tensor] = None, + sigma_down : Optional[Tensor] = None, + ) -> None: + + self.rk_type = rk_type + self.IMPLICIT = rk_type in get_implicit_sampler_name_list(nameOnly=True) + self.EXPONENTIAL = RK_Method_Beta.is_exponential(rk_type) + + sigma = sigmas[step] + sigma_next = sigmas[step+1] + + h_prev = [] + a, b, u, v, ci, multistep_stages, hybrid_stages, FSAL = get_rk_methods_beta(rk_type, + h, + c1, + c2, + c3, + h_prev, + step, + sigmas, + sigma, + sigma_next, + sigma_down, + self.extra_options, + ) + + self.multistep_stages = multistep_stages + self.hybrid_stages = hybrid_stages + + self.A = torch.tensor(a, dtype=h.dtype, device=h.device) + self.B = torch.tensor(b, dtype=h.dtype, device=h.device) + self.C = torch.tensor(ci, dtype=h.dtype, device=h.device) + + self.U = torch.tensor(u, dtype=h.dtype, device=h.device) if u is not None else None + self.V = torch.tensor(v, dtype=h.dtype, device=h.device) if v is not None else None + + self.rows = self.A.shape[0] + self.cols = self.A.shape[1] + + self.row_offset = 1 if not self.IMPLICIT and self.A[0].sum() == 0 else 0 + + if self.IMPLICIT and self.reorder_tableau_indices[0] != -1: + self.reorder_tableau(self.reorder_tableau_indices) + + + + def reorder_tableau(self, indices:list[int]) -> None: + #if indices[0]: + self.A = self.A [indices] + self.B[0] = self.B[0][indices] + self.C = self.C [indices] + self.C = torch.cat((self.C, self.C[-1:])) + return + + + + def update_substep(self, + x_0 : Tensor, + x_ : Tensor, + eps_ : Tensor, + eps_prev_ : Tensor, + row : int, + row_offset : int, + h_new : Tensor, + h_new_orig : Tensor, + lying_eps_row_factor : float = 1.0, + sigma : Optional[Tensor] = None, + ) -> Tensor: + + if row < self.rows - row_offset and self.multistep_stages == 0: + row_tmp_offset = row + row_offset + + else: + row_tmp_offset = row + 1 + + #zr_base = self.zum(row+row_offset+self.multistep_stages, eps_, eps_prev_) # TODO: why unused? + + if self.SYNC_SUBSTEP_MEAN_CW and lying_eps_row_factor != 1.0: + zr_orig = self.zum(row+row_offset+self.multistep_stages, eps_, eps_prev_) + x_orig_row = x_0 + h_new * zr_orig + + #eps_row = eps_ [row].clone() + #eps_prev_row = eps_prev_[row].clone() + + eps_ [row] *= lying_eps_row_factor + eps_prev_[row] *= lying_eps_row_factor + + if self.EO("exp2lin_override"): + zr = self.zum2(row+row_offset+self.multistep_stages, eps_, eps_prev_, h_new, sigma) + x_[row_tmp_offset] = x_0 + zr + else: + zr = self.zum(row+row_offset+self.multistep_stages, eps_, eps_prev_) + + x_[row_tmp_offset] = x_0 + h_new * zr + + if self.SYNC_SUBSTEP_MEAN_CW and lying_eps_row_factor != 1.0: + x_[row_tmp_offset] = x_[row_tmp_offset] - x_[row_tmp_offset].mean(dim=(-2,-1), keepdim=True) + x_orig_row.mean(dim=(-2,-1), keepdim=True) + + #eps_ [row] = eps_row + #eps_prev_[row] = eps_prev_row + + if (self.SYNC_SUBSTEP_MEAN_CW and h_new != h_new_orig) or self.EO("sync_mean_noise"): + if not self.EO("disable_sync_mean_noise"): + x_row_down = x_0 + h_new_orig * zr + x_[row_tmp_offset] = x_[row_tmp_offset] - x_[row_tmp_offset].mean(dim=(-2,-1), keepdim=True) + x_row_down.mean(dim=(-2,-1), keepdim=True) + + return x_ + + + + def zum2(self, row:int, k:Tensor, k_prev:Tensor=None, h_new:Tensor=None, sigma:Tensor=None) -> Tensor: + if row < self.rows: + return self.a_k_einsum2(row, k, h_new, sigma) + else: + row = row - self.rows + return self.b_k_einsum2(row, k, h_new, sigma) + + def a_k_einsum2(self, row:int, k:Tensor, h:Tensor, sigma:Tensor) -> Tensor: + return torch.einsum('i,j,k,i... -> ...', self.A[row], h.unsqueeze(0), -sigma.unsqueeze(0), k[:self.cols]) + + def b_k_einsum2(self, row:int, k:Tensor, h:Tensor, sigma:Tensor) -> Tensor: + return torch.einsum('i,j,k,i... -> ...', self.B[row], h.unsqueeze(0), -sigma.unsqueeze(0), k[:self.cols]) + + + def a_k_einsum(self, row:int, k :Tensor) -> Tensor: + return torch.einsum('i, i... -> ...', self.A[row], k[:self.cols]) + + def b_k_einsum(self, row:int, k :Tensor) -> Tensor: + return torch.einsum('i, i... -> ...', self.B[row], k[:self.cols]) + + def u_k_einsum(self, row:int, k_prev:Tensor) -> Tensor: + return torch.einsum('i, i... -> ...', self.U[row], k_prev[:self.cols]) if (self.U is not None and k_prev is not None) else 0 + + def v_k_einsum(self, row:int, k_prev:Tensor) -> Tensor: + return torch.einsum('i, i... -> ...', self.V[row], k_prev[:self.cols]) if (self.V is not None and k_prev is not None) else 0 + + + + def zum(self, row:int, k:Tensor, k_prev:Tensor=None,) -> Tensor: + if row < self.rows: + return self.a_k_einsum(row, k) + self.u_k_einsum(row, k_prev) + else: + row = row - self.rows + return self.b_k_einsum(row, k) + self.v_k_einsum(row, k_prev) + + def zum_tableau(self, k:Tensor, k_prev:Tensor=None,) -> Tensor: + a_k_sum = torch.einsum('ij, j... -> i...', self.A, k[:self.cols]) + u_k_sum = torch.einsum('ij, j... -> i...', self.U, k_prev[:self.cols]) if (self.U is not None and k_prev is not None) else 0 + return a_k_sum + u_k_sum + + def get_x(self, data:Tensor, noise:Tensor, sigma:Tensor): + if self.VE_MODEL: + return data + sigma * noise + else: + return (self.sigma_max - sigma) * data + sigma * noise + + def init_cfg_channelwise(self, x:Tensor, cfg_cw:float=1.0, **extra_args) -> Dict[str, Any]: + self.uncond = [torch.full_like(x, 0.0)] + self.cfg_cw = cfg_cw + if cfg_cw != 1.0: + def post_cfg_function(args): + self.uncond[0] = args["uncond_denoised"] + return args["denoised"] + model_options = extra_args.get("model_options", {}).copy() + extra_args["model_options"] = comfy.model_patcher.set_model_options_post_cfg_function(model_options, post_cfg_function, disable_cfg1_optimization=True) + return extra_args + + + def calc_cfg_channelwise(self, denoised:Tensor) -> Tensor: + if self.cfg_cw != 1.0: + avg = 0 + for b, c in itertools.product(range(denoised.shape[0]), range(denoised.shape[1])): + avg += torch.norm(denoised[b][c] - self.uncond[0][b][c]) + avg /= denoised.shape[1] + + for b, c in itertools.product(range(denoised.shape[0]), range(denoised.shape[1])): + ratio = torch.nan_to_num(torch.norm(denoised[b][c] - self.uncond[0][b][c]) / avg, 0) + denoised_new = self.uncond[0] + ratio * self.cfg_cw * (denoised - self.uncond[0]) + return denoised_new + else: + return denoised + + + @staticmethod + def calculate_res_2m_step( + x_0 : Tensor, + denoised_ : Tensor, + sigma_down : Tensor, + sigmas : Tensor, + step : int, + ) -> Tuple[Tensor, Tensor]: + + if denoised_[2].sum() == 0: + return None, None + + sigma = sigmas[step] + sigma_prev = sigmas[step-1] + + h_prev = -torch.log(sigma/sigma_prev) + h = -torch.log(sigma_down/sigma) + + c1 = 0 + c2 = (-h_prev / h).item() + + ci = [c1,c2] + φ = Phi(h, ci, analytic_solution=True) + + b2 = φ(2)/c2 + b1 = φ(1) - b2 + + eps_2 = denoised_[1] - x_0 + eps_1 = denoised_[0] - x_0 + + h_a_k_sum = h * (b1 * eps_1 + b2 * eps_2) + + x = torch.exp(-h) * x_0 + h_a_k_sum + + denoised = x_0 + (sigma / (sigma - sigma_down)) * h_a_k_sum + + return x, denoised + + + @staticmethod + def calculate_res_3m_step( + x_0 : Tensor, + denoised_ : Tensor, + sigma_down : Tensor, + sigmas : Tensor, + step : int, + ) -> Tuple[Tensor, Tensor]: + + if denoised_[3].sum() == 0: + return None, None + + sigma = sigmas[step] + sigma_prev = sigmas[step-1] + sigma_prev2 = sigmas[step-2] + + h = -torch.log(sigma_down/sigma) + h_prev = -torch.log(sigma/sigma_prev) + h_prev2 = -torch.log(sigma/sigma_prev2) + + c1 = 0 + c2 = (-h_prev / h).item() + c3 = (-h_prev2 / h).item() + + ci = [c1,c2,c3] + φ = Phi(h, ci, analytic_solution=True) + + gamma = (3*(c3**3) - 2*c3) / (c2*(2 - 3*c2)) + + b3 = (1 / (gamma * c2 + c3)) * φ(2, -h) + b2 = gamma * b3 + b1 = φ(1, -h) - b2 - b3 + + eps_3 = denoised_[2] - x_0 + eps_2 = denoised_[1] - x_0 + eps_1 = denoised_[0] - x_0 + + h_a_k_sum = h * (b1 * eps_1 + b2 * eps_2 + b3 * eps_3) + + x = torch.exp(-h) * x_0 + h_a_k_sum + + denoised = x_0 + (sigma / (sigma - sigma_down)) * h_a_k_sum + + return x, denoised + + def swap_rk_type_at_step_or_threshold(self, + x_0 : Tensor, + data_prev_ : Tensor, + NS, + sigmas : Tensor, + step : Tensor, + rk_swap_step : int, + rk_swap_threshold : float, + rk_swap_type : str, + rk_swap_print : bool, + ) -> str: + if rk_swap_type == "": + if self.EXPONENTIAL: + rk_swap_type = "res_3m" + else: + rk_swap_type = "deis_3m" + + if step > rk_swap_step and self.rk_type != rk_swap_type: + RESplain("Switching rk_type to:", rk_swap_type) + self.rk_type = rk_swap_type + + if RK_Method_Beta.is_exponential(rk_swap_type): + self.__class__ = RK_Method_Exponential + else: + self.__class__ = RK_Method_Linear + + if rk_swap_type in get_implicit_sampler_name_list(nameOnly=True): + self.IMPLICIT = True + self.row_offset = 0 + NS.row_offset = 0 + else: + self.IMPLICIT = False + self.row_offset = 1 + NS.row_offset = 1 + NS.h_fn = self.h_fn + NS.t_fn = self.t_fn + NS.sigma_fn = self.sigma_fn + + + + if step > 2 and sigmas[step+1] > 0 and self.rk_type != rk_swap_type and rk_swap_threshold > 0: + x_res_2m, denoised_res_2m = self.calculate_res_2m_step(x_0, data_prev_, NS.sigma_down, sigmas, step) + x_res_3m, denoised_res_3m = self.calculate_res_3m_step(x_0, data_prev_, NS.sigma_down, sigmas, step) + if denoised_res_2m is not None: + if rk_swap_print: + RESplain("res_3m - res_2m:", torch.norm(denoised_res_3m - denoised_res_2m).item()) + if rk_swap_threshold > torch.norm(denoised_res_2m - denoised_res_3m): + RESplain("Switching rk_type to:", rk_swap_type, "at step:", step) + self.rk_type = rk_swap_type + + if RK_Method_Beta.is_exponential(rk_swap_type): + self.__class__ = RK_Method_Exponential + else: + self.__class__ = RK_Method_Linear + + if rk_swap_type in get_implicit_sampler_name_list(nameOnly=True): + self.IMPLICIT = True + self.row_offset = 0 + NS.row_offset = 0 + else: + self.IMPLICIT = False + self.row_offset = 1 + NS.row_offset = 1 + NS.h_fn = self.h_fn + NS.t_fn = self.t_fn + NS.sigma_fn = self.sigma_fn + + return self.rk_type + + + def bong_iter(self, + x_0 : Tensor, + x_ : Tensor, + eps_ : Tensor, + eps_prev_ : Tensor, + data_ : Tensor, + sigma : Tensor, + s_ : Tensor, + row : int, + row_offset: int, + h : Tensor, + step : int, + step_sched: int, + BONGMATH_Y : bool = False, + y0_bongflow : Optional[Tensor] = None, + noise_sync: Optional[Tensor] = None, + eps_x_ : Optional[Tensor] = None, + eps_y_ : Optional[Tensor] = None, + #eps_x2y_ : Optional[Tensor] = None, + data_x_ : Optional[Tensor] = None, + data_y_ : Optional[Tensor] = None, + #yt_ : Optional[Tensor] = None, + #yt_0 : Optional[Tensor] = None, + LG = None, + ) -> Tuple[Tensor, Tensor, Tensor]: + + if x_0.ndim == 4: + norm_dim = (-2,-1) + elif x_0.ndim == 5: + norm_dim = (-4,-2,-1) + + if BONGMATH_Y: + lgw_mask_, lgw_mask_inv_ = LG.get_masks_for_step(step_sched) + lgw_mask_sync_, lgw_mask_sync_inv_ = LG.get_masks_for_step(step_sched, lgw_type="sync") + + weight_mask = lgw_mask_+lgw_mask_inv_ + if LG.SYNC_SEPARATE: + sync_mask = lgw_mask_sync_+lgw_mask_sync_inv_ + else: + sync_mask = 1. + + + if self.EO("bong_start_step", 0) > step or step > self.EO("bong_stop_step", 10000) or (self.unsample_bongmath == False and s_[-1] > s_[0]): + return x_0, x_, eps_ + + bong_iter_max_row = self.rows - row_offset + if self.EO("bong_iter_max_row_full"): + bong_iter_max_row = self.rows + + if self.EO("bong_iter_lock_x_0_ch_means"): + x_0_ch_means = x_0.mean(dim=norm_dim, keepdim=True) + + if self.EO("bong_iter_lock_x_row_ch_means"): + x_row_means = [] + for rr in range(row+row_offset): + x_row_mean = x_[rr].mean(dim=norm_dim, keepdim=True) + x_row_means.append(x_row_mean) + + if row < bong_iter_max_row and self.multistep_stages == 0: + bong_strength = self.EO("bong_strength", 1.0) + + if bong_strength != 1.0: + x_0_tmp = x_0 .clone() + x_tmp_ = x_ .clone() + eps_tmp_ = eps_.clone() + + for i in range(100): #bongmath for eps_prev_ not implemented? + x_0 = x_[row+row_offset] - h * self.zum(row+row_offset, eps_, eps_prev_) + + if self.EO("bong_iter_lock_x_0_ch_means"): + x_0 = x_0 - x_0.mean(dim=norm_dim, keepdim=True) + x_0_ch_means + + for rr in range(row+row_offset): + x_[rr] = x_0 + h * self.zum(rr, eps_, eps_prev_) + + if self.EO("bong_iter_lock_x_row_ch_means"): + for rr in range(row+row_offset): + x_[rr] = x_[rr] - x_[rr].mean(dim=norm_dim, keepdim=True) + x_row_means[rr] + + for rr in range(row+row_offset): + if self.EO("zonkytar"): + #eps_[rr] = self.get_unsample_epsilon(x_[rr], x_0, data_[rr], sigma, s_[rr]) + eps_[rr] = self.get_epsilon(x_[rr], x_0, data_[rr], sigma, s_[rr]) + else: + if BONGMATH_Y and not self.EO("disable_bongmath_y"): + if self.EXPONENTIAL: + eps_x_ = data_x_ - x_0 + eps_x2y_ = data_y_ - x_0 + if self.VE_MODEL: + eps_ = sync_mask * eps_x_ + (1-sync_mask) * eps_x2y_ + weight_mask * (-eps_y_+sigma*(-noise_sync)) + if self.EO("sync_x2y"): + eps_ = sync_mask * eps_x_ + (1-sync_mask) * eps_x2y_ + weight_mask * (-eps_x2y_+sigma*(-noise_sync)) + else: + eps_ = sync_mask * eps_x_ + (1-sync_mask) * eps_x2y_ + weight_mask * (-eps_y_+sigma*(y0_bongflow-noise_sync)) + if self.EO("sync_x2y"): + eps_ = sync_mask * eps_x_ + (1-sync_mask) * eps_x2y_ + weight_mask * (-eps_x2y_+sigma*(y0_bongflow-noise_sync)) + else: + eps_x_ [:s_.shape[0]] = (x_[:s_.shape[0]] - data_x_[:s_.shape[0]]) / s_.view(-1,1,1,1,1) # or should it be vs x_0??? + eps_x2y_ = torch.zeros_like(eps_x_) + eps_x2y_[:s_.shape[0]] = (x_[:s_.shape[0]] - data_y_[:s_.shape[0]]) / s_.view(-1,1,1,1,1) # or should it be vs x_0??? + + if self.VE_MODEL: + eps_ = sync_mask * eps_x_ + (1-sync_mask) * eps_x2y_ + weight_mask * (noise_sync-eps_y_) + if self.EO("sync_x2y"): + eps_ = sync_mask * eps_x_ + (1-sync_mask) * eps_x2y_ + weight_mask * (noise_sync-eps_x2y_) + else: + eps_ = sync_mask * eps_x_ + (1-sync_mask) * eps_x2y_ + weight_mask * (noise_sync-eps_y_-y0_bongflow) + if self.EO("sync_x2y"): + eps_ = sync_mask * eps_x_ + (1-sync_mask) * eps_x2y_ + weight_mask * (noise_sync-eps_x2y_-y0_bongflow) + + else: + eps_[rr] = self.get_epsilon(x_0, x_[rr], data_[rr], sigma, s_[rr]) + + if bong_strength != 1.0: + x_0 = x_0_tmp + bong_strength * (x_0 - x_0_tmp) + x_ = x_tmp_ + bong_strength * (x_ - x_tmp_) + eps_ = eps_tmp_ + bong_strength * (eps_ - eps_tmp_) + + return x_0, x_, eps_ #, yt_0, yt_ + + + def newton_iter(self, + x_0 : Tensor, + x_ : Tensor, + eps_ : Tensor, + eps_prev_ : Tensor, + data_ : Tensor, + s_ : Tensor, + row : int, + h : Tensor, + sigmas : Tensor, + step : int, + newton_name: str, + SYNC_GUIDE_ACTIVE: bool, + ) -> Tuple[Tensor, Tensor]: + if SYNC_GUIDE_ACTIVE: + return x_, eps_ + newton_iter_name = "newton_iter_" + newton_name + + default_anchor_x_all = False + if newton_name == "lying": + default_anchor_x_all = True + + newton_iter = self.EO(newton_iter_name, 100) + newton_iter_skip_last_steps = self.EO(newton_iter_name + "_skip_last_steps", 0) + newton_iter_mixing_rate = self.EO(newton_iter_name + "_mixing_rate", 1.0) + + newton_iter_anchor = self.EO(newton_iter_name + "_anchor", 0) + newton_iter_anchor_x_all = self.EO(newton_iter_name + "_anchor_x_all", default_anchor_x_all) + newton_iter_type = self.EO(newton_iter_name + "_type", "from_epsilon") + newton_iter_sequence = self.EO(newton_iter_name + "_sequence", "double") + + row_b_offset = 0 + if self.EO(newton_iter_name + "_include_row_b"): + row_b_offset = 1 + + if step >= len(sigmas)-1-newton_iter_skip_last_steps or sigmas[step+1] == 0 or not self.IMPLICIT: + return x_, eps_ + + sigma = sigmas[step] + + start, stop = 0, self.rows+row_b_offset + if newton_name == "pre": + start = row + elif newton_name == "post": + start = row + 1 + + if newton_iter_anchor >= 0: + eps_anchor = eps_[newton_iter_anchor].clone() + + if newton_iter_anchor_x_all: + x_orig_ = x_.clone() + + for n_iter in range(newton_iter): + for r in range(start, stop): + if newton_iter_anchor >= 0: + eps_[newton_iter_anchor] = eps_anchor.clone() + if newton_iter_anchor_x_all: + x_ = x_orig_.clone() + x_tmp, eps_tmp = x_[r].clone(), eps_[r].clone() + + seq_start, seq_stop = r, r+1 + + if newton_iter_sequence == "double": + seq_start, seq_stop = start, stop + + for r_ in range(seq_start, seq_stop): + x_[r_] = x_0 + h * self.zum(r_, eps_, eps_prev_) + + for r_ in range(seq_start, seq_stop): + if newton_iter_type == "from_data": + data_[r_] = get_data_from_step(x_0, x_[r_], sigma, s_[r_]) + eps_ [r_] = self.get_epsilon(x_0, x_[r_], data_[r_], sigma, s_[r_]) + elif newton_iter_type == "from_step": + eps_ [r_] = get_epsilon_from_step(x_0, x_[r_], sigma, s_[r_]) + elif newton_iter_type == "from_alt": + eps_ [r_] = x_0/sigma - x_[r_]/s_[r_] + elif newton_iter_type == "from_epsilon": + eps_ [r_] = self.get_epsilon(x_0, x_[r_], data_[r_], sigma, s_[r_]) + + if self.EO(newton_iter_name + "_opt"): + opt_timing, opt_type, opt_subtype = self.EO(newton_iter_name+"_opt", [str]) + + opt_start, opt_stop = 0, self.rows+row_b_offset + if opt_timing == "early": + opt_stop = row + 1 + elif opt_timing == "late": + opt_start = row + 1 + + for r2 in range(opt_start, opt_stop): + if r_ != r2: + if opt_subtype == "a": + eps_a = eps_[r2] + eps_b = eps_[r_] + elif opt_subtype == "b": + eps_a = eps_[r_] + eps_b = eps_[r2] + + if opt_type == "ortho": + eps_ [r_] = get_orthogonal(eps_a, eps_b) + elif opt_type == "collin": + eps_ [r_] = get_collinear (eps_a, eps_b) + elif opt_type == "proj": + eps_ [r_] = get_collinear (eps_a, eps_b) + get_orthogonal(eps_b, eps_a) + + x_ [r_] = x_tmp + newton_iter_mixing_rate * (x_ [r_] - x_tmp) + eps_[r_] = eps_tmp + newton_iter_mixing_rate * (eps_[r_] - eps_tmp) + + if newton_iter_sequence == "double": + break + + return x_, eps_ + + + + +class RK_Method_Exponential(RK_Method_Beta): + def __init__(self, + model, + rk_type : str, + VE_MODEL : bool, + noise_anchor : float, + noise_boost_normalize : bool, + + model_device : str = 'cuda', + work_device : str = 'cpu', + dtype : torch.dtype = torch.float64, + extra_options : str = "", + ): + + super().__init__(model, + rk_type, + VE_MODEL, + noise_anchor, + noise_boost_normalize, + model_device = model_device, + work_device = work_device, + dtype = dtype, + extra_options = extra_options, + ) + + @staticmethod + def alpha_fn(neg_h:Tensor) -> Tensor: + return torch.exp(neg_h) + + @staticmethod + def sigma_fn(t:Tensor) -> Tensor: + #return 1/(torch.exp(-t)+1) + return t.neg().exp() + + @staticmethod + def t_fn(sigma:Tensor) -> Tensor: + #return -torch.log((1.-sigma)/sigma) + return sigma.log().neg() + + @staticmethod + def h_fn(sigma_down:Tensor, sigma:Tensor) -> Tensor: + #return (-torch.log((1.-sigma_down)/sigma_down)) - (-torch.log((1.-sigma)/sigma)) + return -torch.log(sigma_down/sigma) + + def __call__(self, + x : Tensor, + sub_sigma : Tensor, + x_0 : Optional[Tensor] = None, + sigma : Optional[Tensor] = None, + transformer_options : Optional[dict] = None, + ) -> Tuple[Tensor, Tensor]: + + x_0 = x if x_0 is None else x_0 + sigma = sub_sigma if sigma is None else sigma + + if transformer_options is not None: + self.extra_args.setdefault("model_options", {}).setdefault("transformer_options", {}).update(transformer_options) + + denoised = self.model_denoised(x.to(self.model_device), sub_sigma.to(self.model_device), **self.extra_args).to(sigma.device) + + eps_anchored = (x_0 - denoised) / sigma + eps_unmoored = (x - denoised) / sub_sigma + + eps = eps_unmoored + self.LINEAR_ANCHOR_X_0 * (eps_anchored - eps_unmoored) + + denoised = x_0 - sigma * eps + + epsilon = denoised - x_0 + + #epsilon = denoised - x + + if self.EO("exp2lin_override"): + epsilon = (x_0 - denoised) / sigma + + return epsilon, denoised + + def get_eps(self, *args): + if len(args) == 3: + x, denoised, sigma = args + return denoised - x + elif len(args) == 5: + x_0, x, denoised, sigma, sub_sigma = args + eps_anchored = (x_0 - denoised) / sigma + eps_unmoored = (x - denoised) / sub_sigma + eps = eps_unmoored + self.LINEAR_ANCHOR_X_0 * (eps_anchored - eps_unmoored) + denoised = x_0 - sigma * eps + eps_out = denoised - x_0 + if self.EO("exp2lin_override"): + eps_out = (x_0 - denoised) / sigma + return eps_out + + else: + raise ValueError(f"get_eps expected 3 or 5 arguments, got {len(args)}") + + def get_epsilon(self, + x_0 : Tensor, + x : Tensor, + denoised : Tensor, + sigma : Tensor, + sub_sigma : Tensor, + ) -> Tensor: + + eps_anchored = (x_0 - denoised) / sigma + eps_unmoored = (x - denoised) / sub_sigma + + eps = eps_unmoored + self.LINEAR_ANCHOR_X_0 * (eps_anchored - eps_unmoored) + + denoised = x_0 - sigma * eps + if self.EO("exp2lin_override"): + return (x_0 - denoised) / sigma + else: + return denoised - x_0 + + + + def get_epsilon_anchored(self, x_0:Tensor, denoised:Tensor, sigma:Tensor) -> Tensor: + return denoised - x_0 + + + + def get_guide_epsilon(self, + x_0 : Tensor, + x : Tensor, + y : Tensor, + sigma : Tensor, + sigma_cur : Tensor, + sigma_down : Optional[Tensor] = None, + epsilon_scale : Optional[Tensor] = None, + ) -> Tensor: + + sigma_cur = epsilon_scale if epsilon_scale is not None else sigma_cur + + if sigma_down > sigma: + eps_unmoored = (sigma_cur/(self.sigma_max - sigma_cur)) * (x - y) + else: + eps_unmoored = y - x + + if self.EO("manually_anchor_unsampler"): + if sigma_down > sigma: + eps_anchored = (sigma /(self.sigma_max - sigma)) * (x_0 - y) + else: + eps_anchored = y - x_0 + eps_guide = eps_unmoored + self.LINEAR_ANCHOR_X_0 * (eps_anchored - eps_unmoored) + else: + eps_guide = eps_unmoored + + return eps_guide + + + +class RK_Method_Linear(RK_Method_Beta): + def __init__(self, + model, + rk_type : str, + VE_MODEL : bool, + noise_anchor : float, + noise_boost_normalize : bool, + model_device : str = 'cuda', + work_device : str = 'cpu', + dtype : torch.dtype = torch.float64, + extra_options : str = "", + ): + + super().__init__(model, + rk_type, + VE_MODEL, + noise_anchor, + noise_boost_normalize, + model_device = model_device, + work_device = work_device, + dtype = dtype, + extra_options = extra_options, + ) + + @staticmethod + def alpha_fn(neg_h:Tensor) -> Tensor: + return torch.ones_like(neg_h) + + @staticmethod + def sigma_fn(t:Tensor) -> Tensor: + return t + + @staticmethod + def t_fn(sigma:Tensor) -> Tensor: + return sigma + + @staticmethod + def h_fn(sigma_down:Tensor, sigma:Tensor) -> Tensor: + return sigma_down - sigma + + def __call__(self, + x : Tensor, + sub_sigma : Tensor, + x_0 : Optional[Tensor] = None, + sigma : Optional[Tensor] = None, + transformer_options : Optional[dict] = None, + ) -> Tuple[Tensor, Tensor]: + + x_0 = x if x_0 is None else x_0 + sigma = sub_sigma if sigma is None else sigma + + if transformer_options is not None: + self.extra_args.setdefault("model_options", {}).setdefault("transformer_options", {}).update(transformer_options) + + denoised = self.model_denoised(x.to(self.model_device), sub_sigma.to(self.model_device), **self.extra_args).to(sigma.device) + + epsilon_anchor = (x_0 - denoised) / sigma + epsilon_unmoored = (x - denoised) / sub_sigma + + epsilon = epsilon_unmoored + self.LINEAR_ANCHOR_X_0 * (epsilon_anchor - epsilon_unmoored) + + return epsilon, denoised + + def get_eps(self, *args): + if len(args) == 3: + x, denoised, sigma = args + return (x - denoised) / sigma + elif len(args == 5): + x_0, x, denoised, sigma, sub_sigma = args + eps_anchor = (x_0 - denoised) / sigma + eps_unmoored = (x - denoised) / sub_sigma + return eps_unmoored + self.LINEAR_ANCHOR_X_0 * (eps_anchor - eps_unmoored) + else: + raise ValueError(f"get_eps expected 3 or 5 arguments, got {len(args)}") + + def get_epsilon(self, + x_0 : Tensor, + x : Tensor, + denoised : Tensor, + sigma : Tensor, + sub_sigma : Tensor, + ) -> Tensor: + + eps_anchor = (x_0 - denoised) / sigma + eps_unmoored = (x - denoised) / sub_sigma + + return eps_unmoored + self.LINEAR_ANCHOR_X_0 * (eps_anchor - eps_unmoored) + + + + def get_epsilon_anchored(self, x_0:Tensor, denoised:Tensor, sigma:Tensor) -> Tensor: + return (x_0 - denoised) / sigma + + + + def get_guide_epsilon(self, + x_0 : Tensor, + x : Tensor, + y : Tensor, + sigma : Tensor, + sigma_cur : Tensor, + sigma_down : Optional[Tensor] = None, + epsilon_scale : Optional[Tensor] = None, + ) -> Tensor: + + if sigma_down > sigma: + sigma_ratio = self.sigma_max - sigma_cur.clone() + else: + sigma_ratio = sigma_cur.clone() + sigma_ratio = epsilon_scale if epsilon_scale is not None else sigma_ratio + + if sigma_down is None: + return (x - y) / sigma_ratio + else: + if sigma_down > sigma: + return (y - x) / sigma_ratio + else: + return (x - y) / sigma_ratio + + + + +""" + + + + +if EO("bong2m") and RK.multistep_stages > 0 and step < len(sigmas)-4: + h_no_eta = -torch.log(sigmas[step+1]/sigmas[step]) + h_prev1_no_eta = -torch.log(sigmas[step] /sigmas[step-1]) + c2_prev = (-h_prev1_no_eta / h_no_eta).item() + eps_prev = denoised_data_prev - x_0 + + φ = Phi(h_prev, [0.,c2_prev]) + a2_1 = c2_prev * φ(1,2) + for i in range(100): + x_prev = x_0 - h_prev * (a2_1 * eps_prev) + eps_prev = denoised_data_prev - x_prev + + eps_[1] = eps_prev + +if EO("bong3m") and RK.multistep_stages > 0 and step < len(sigmas)-10: + h_no_eta = -torch.log(sigmas[step+1]/sigmas[step]) + h_prev1_no_eta = -torch.log(sigmas[step] /sigmas[step-1]) + h_prev2_no_eta = -torch.log(sigmas[step] /sigmas[step-2]) + c2_prev = (-h_prev1_no_eta / h_no_eta).item() + c3_prev = (-h_prev2_no_eta / h_no_eta).item() + + eps_prev2 = denoised_data_prev2 - x_0 + eps_prev = denoised_data_prev - x_0 + + φ = Phi(h_prev1_no_eta, [0.,c2_prev, c3_prev]) + a2_1 = c2_prev * φ(1,2) + for i in range(100): + x_prev = x_0 - h_prev1_no_eta * (a2_1 * eps_prev) + eps_prev = denoised_data_prev2 - x_prev + + eps_[1] = eps_prev + + φ = Phi(h_prev2_no_eta, [0.,c3_prev, c3_prev]) + + def calculate_gamma(c2_prev, c3_prev): + return (3*(c3_prev**3) - 2*c3_prev) / (c2_prev*(2 - 3*c2_prev)) + gamma = calculate_gamma(c2_prev, c3_prev) + + a2_1 = c2_prev * φ(1,2) + a3_2 = gamma * c2_prev * φ(2,2) + (c3_prev ** 2 / c2_prev) * φ(2, 3) + a3_1 = c3_prev * φ(1,3) - a3_2 + + for i in range(100): + x_prev2 = x_0 - h_prev2_no_eta * (a3_1 * eps_prev + a3_2 * eps_prev2) + x_prev = x_prev2 + h_prev2_no_eta * (a2_1 * eps_prev) + + eps_prev2 = denoised_data_prev - x_prev2 + eps_prev = denoised_data_prev2 - x_prev + + eps_[2] = eps_prev2 +""" \ No newline at end of file diff --git a/ComfyUI/custom_nodes/RES4LYF/beta/rk_noise_sampler_beta.py b/ComfyUI/custom_nodes/RES4LYF/beta/rk_noise_sampler_beta.py new file mode 100644 index 0000000000000000000000000000000000000000..43c52efb2af879196218f9bbd4f25ec415dcb2ae --- /dev/null +++ b/ComfyUI/custom_nodes/RES4LYF/beta/rk_noise_sampler_beta.py @@ -0,0 +1,849 @@ +import torch + +from torch import Tensor +from typing import Optional, Callable, Tuple, Dict, Any, Union, TYPE_CHECKING, TypeVar + +if TYPE_CHECKING: + from .rk_method_beta import RK_Method_Exponential, RK_Method_Linear + +import comfy.model_patcher +import comfy.supported_models + +from .noise_classes import NOISE_GENERATOR_CLASSES, NOISE_GENERATOR_CLASSES_SIMPLE +from .constants import MAX_STEPS + +from ..helper import ExtraOptions, has_nested_attr +from ..latents import normalize_zscore, get_orthogonal, get_collinear +from ..res4lyf import RESplain + + + + +NOISE_MODE_NAMES = ["none", + #"hard_sq", + "hard", + "lorentzian", + "soft", + "soft-linear", + "softer", + "eps", + "sinusoidal", + "exp", + "vpsde", + "er4", + "hard_var", + ] + + + +def get_data_from_step(x, x_next, sigma, sigma_next): # assumes 100% linear trajectory + h = sigma_next - sigma + return (sigma_next * x - sigma * x_next) / h + +def get_epsilon_from_step(x, x_next, sigma, sigma_next): + h = sigma_next - sigma + return (x - x_next) / h + + + +class RK_NoiseSampler: + def __init__(self, + RK : Union["RK_Method_Exponential", "RK_Method_Linear"], + model, + step : int=0, + device : str='cuda', + dtype : torch.dtype=torch.float64, + extra_options : str="" + ): + + self.device = device + self.dtype = dtype + + self.model = model + + if has_nested_attr(model, "inner_model.inner_model.model_sampling"): + model_sampling = model.inner_model.inner_model.model_sampling + elif has_nested_attr(model, "model.model_sampling"): + model_sampling = model.model.model_sampling + + self.sigma_max = model_sampling.sigma_max.to(dtype=self.dtype, device=self.device) + self.sigma_min = model_sampling.sigma_min.to(dtype=self.dtype, device=self.device) + + + self.sigma_fn = RK.sigma_fn + self.t_fn = RK.t_fn + self.h_fn = RK.h_fn + + self.row_offset = 1 if not RK.IMPLICIT else 0 + + self.step = step + + self.noise_sampler = None + self.noise_sampler2 = None + + self.noise_mode_sde = None + self.noise_mode_sde_substep = None + + self.LOCK_H_SCALE = True + + self.CONST = isinstance(model_sampling, comfy.model_sampling.CONST) + self.VARIANCE_PRESERVING = isinstance(model_sampling, comfy.model_sampling.CONST) + + self.extra_options = extra_options + self.EO = ExtraOptions(extra_options) + + self.DOWN_SUBSTEP = self.EO("down_substep") + self.DOWN_STEP = self.EO("down_step") + + self.init_noise = None + + + + + def init_noise_samplers(self, + x : Tensor, + noise_seed : int, + noise_seed_substep : int, + noise_sampler_type : str, + noise_sampler_type2 : str, + noise_mode_sde : str, + noise_mode_sde_substep : str, + overshoot_mode : str, + overshoot_mode_substep : str, + noise_boost_step : float, + noise_boost_substep : float, + alpha : float, + alpha2 : float, + k : float = 1.0, + k2 : float = 1.0, + scale : float = 0.1, + scale2 : float = 0.1, + last_rng = None, + last_rng_substep = None, + ) -> None: + + self.noise_sampler_type = noise_sampler_type + self.noise_sampler_type2 = noise_sampler_type2 + self.noise_mode_sde = noise_mode_sde + self.noise_mode_sde_substep = noise_mode_sde_substep + self.overshoot_mode = overshoot_mode + self.overshoot_mode_substep = overshoot_mode_substep + self.noise_boost_step = noise_boost_step + self.noise_boost_substep = noise_boost_substep + self.s_in = x.new_ones([1], dtype=self.dtype, device=self.device) + + if noise_seed < 0 and last_rng is None: + seed = torch.initial_seed()+1 + RESplain("SDE noise seed: ", seed, " (set via torch.initial_seed()+1)", debug=True) + if noise_seed < 0 and last_rng is not None: + seed = torch.initial_seed() + RESplain("SDE noise seed: ", seed, " (set via torch.initial_seed())", debug=True) + else: + seed = noise_seed + RESplain("SDE noise seed: ", seed, debug=True) + + + #seed2 = seed + MAX_STEPS #for substep noise generation. offset needed to ensure seeds are not reused + + if noise_sampler_type == "fractal": + self.noise_sampler = NOISE_GENERATOR_CLASSES.get(noise_sampler_type )(x=x, seed=seed, sigma_min=self.sigma_min, sigma_max=self.sigma_max) + self.noise_sampler.alpha = alpha + self.noise_sampler.k = k + self.noise_sampler.scale = scale + if noise_sampler_type2 == "fractal": + self.noise_sampler2 = NOISE_GENERATOR_CLASSES.get(noise_sampler_type2)(x=x, seed=noise_seed_substep, sigma_min=self.sigma_min, sigma_max=self.sigma_max) + self.noise_sampler2.alpha = alpha2 + self.noise_sampler2.k = k2 + self.noise_sampler2.scale = scale2 + else: + self.noise_sampler = NOISE_GENERATOR_CLASSES_SIMPLE.get(noise_sampler_type )(x=x, seed=seed, sigma_min=self.sigma_min, sigma_max=self.sigma_max) + self.noise_sampler2 = NOISE_GENERATOR_CLASSES_SIMPLE.get(noise_sampler_type2)(x=x, seed=noise_seed_substep, sigma_min=self.sigma_min, sigma_max=self.sigma_max) + + if last_rng is not None: + self.noise_sampler .generator.set_state(last_rng) + self.noise_sampler2.generator.set_state(last_rng_substep) + + + def set_substep_list(self, RK:Union["RK_Method_Exponential", "RK_Method_Linear"]) -> None: + + self.multistep_stages = RK.multistep_stages + self.rows = RK.rows + self.C = RK.C + self.s_ = self.sigma_fn(self.t_fn(self.sigma) + self.h * self.C) + + + def get_substep_list(self, RK:Union["RK_Method_Exponential", "RK_Method_Linear"], sigma, h) -> None: + s_ = RK.sigma_fn(RK.t_fn(sigma) + h * RK.C) + return s_ + + + def get_sde_coeff(self, sigma_next:Tensor, sigma_down:Tensor=None, sigma_up:Tensor=None, eta:float=0.0, VP_OVERRIDE=None) -> Tuple[Tensor,Tensor,Tensor]: + VARIANCE_PRESERVING = VP_OVERRIDE if VP_OVERRIDE is not None else self.VARIANCE_PRESERVING + + if VARIANCE_PRESERVING: + if sigma_down is not None: + alpha_ratio = (1 - sigma_next) / (1 - sigma_down) + sigma_up = (sigma_next ** 2 - sigma_down ** 2 * alpha_ratio ** 2) ** 0.5 + + elif sigma_up is not None: + if sigma_up >= sigma_next: + RESplain("Maximum VPSDE noise level exceeded: falling back to hard noise mode.", debug=True) + if eta >= 1: + sigma_up = sigma_next * 0.9999 #avoid sqrt(neg_num) later + else: + sigma_up = sigma_next * eta + + if VP_OVERRIDE is not None: + sigma_signal = 1 - sigma_next + else: + sigma_signal = self.sigma_max - sigma_next + sigma_residual = (sigma_next ** 2 - sigma_up ** 2) ** .5 + alpha_ratio = sigma_signal + sigma_residual + sigma_down = sigma_residual / alpha_ratio + + else: + alpha_ratio = torch.ones_like(sigma_next) + + if sigma_down is not None: + sigma_up = (sigma_next ** 2 - sigma_down ** 2) ** .5 # not sure this is correct #TODO: CHECK THIS + elif sigma_up is not None: + sigma_down = (sigma_next ** 2 - sigma_up ** 2) ** .5 + + return alpha_ratio, sigma_down, sigma_up + + + + def set_sde_step(self, sigma:Tensor, sigma_next:Tensor, eta:float, overshoot:float, s_noise:float) -> None: + self.sigma_0 = sigma + self.sigma_next = sigma_next + + self.s_noise = s_noise + self.eta = eta + self.overshoot = overshoot + + self.sigma_up_eta, self.sigma_eta, self.sigma_down_eta, self.alpha_ratio_eta \ + = self.get_sde_step(sigma, sigma_next, eta, self.noise_mode_sde, self.DOWN_STEP, SUBSTEP=False) + + self.sigma_up, self.sigma, self.sigma_down, self.alpha_ratio \ + = self.get_sde_step(sigma, sigma_next, overshoot, self.overshoot_mode, self.DOWN_STEP, SUBSTEP=False) + + self.h = self.h_fn(self.sigma_down, self.sigma) + self.h_no_eta = self.h_fn(self.sigma_next, self.sigma) + self.h = self.h + self.noise_boost_step * (self.h_no_eta - self.h) + + + + + + def set_sde_substep(self, + row : int, + multistep_stages : int, + eta_substep : float, + overshoot_substep : float, + s_noise_substep : float, + full_iter : int = 0, + diag_iter : int = 0, + implicit_steps_full : int = 0, + implicit_steps_diag : int = 0 + ) -> None: + + # start with stepsizes for no overshoot/noise addition/noise swapping + self.sub_sigma_up_eta = self.sub_sigma_up = 0.0 + self.sub_sigma_eta = self.sub_sigma = self.s_[row] + self.sub_sigma_down_eta = self.sub_sigma_down = self.sub_sigma_next = self.s_[row+self.row_offset+multistep_stages] + self.sub_alpha_ratio_eta = self.sub_alpha_ratio = 1.0 + + self.s_noise_substep = s_noise_substep + self.eta_substep = eta_substep + self.overshoot_substep = overshoot_substep + + + if row < self.rows and self.s_[row+self.row_offset+multistep_stages] > 0: + if diag_iter > 0 and diag_iter == implicit_steps_diag and self.EO("implicit_substep_skip_final_eta"): + pass + elif diag_iter > 0 and self.EO("implicit_substep_only_first_eta"): + pass + elif full_iter > 0 and full_iter == implicit_steps_full and self.EO("implicit_step_skip_final_eta"): + pass + elif full_iter > 0 and self.EO("implicit_step_only_first_eta"): + pass + elif (full_iter > 0 or diag_iter > 0) and self.noise_sampler_type2 == "brownian": + pass # brownian noise does not increment its seed when generated, deactivate on implicit repeats to avoid burn + elif full_iter > 0 and self.EO("implicit_step_only_first_all_eta"): + self.sigma_down_eta = self.sigma_next + self.sigma_up_eta *= 0 + self.alpha_ratio_eta /= self.alpha_ratio_eta + + self.sigma_down = self.sigma_next + self.sigma_up *= 0 + self.alpha_ratio /= self.alpha_ratio + + self.h_new = self.h = self.h_no_eta + + elif (row < self.rows-self.row_offset-multistep_stages or diag_iter < implicit_steps_diag) or self.EO("substep_eta_use_final"): + self.sub_sigma_up, self.sub_sigma, self.sub_sigma_down, self.sub_alpha_ratio = self.get_sde_substep(sigma = self.s_[row], + sigma_next = self.s_[row+self.row_offset+multistep_stages], + eta = overshoot_substep, + noise_mode_override = self.overshoot_mode_substep, + DOWN = self.DOWN_SUBSTEP) + + self.sub_sigma_up_eta, self.sub_sigma_eta, self.sub_sigma_down_eta, self.sub_alpha_ratio_eta = self.get_sde_substep(sigma = self.s_[row], + sigma_next = self.s_[row+self.row_offset+multistep_stages], + eta = eta_substep, + noise_mode_override = self.noise_mode_sde_substep, + DOWN = self.DOWN_SUBSTEP) + + if self.h_fn(self.sub_sigma_next, self.sigma) != 0: + self.h_new = self.h * self.h_fn(self.sub_sigma_down, self.sigma) / self.h_fn(self.sub_sigma_next, self.sigma) + self.h_eta = self.h * self.h_fn(self.sub_sigma_down_eta, self.sigma) / self.h_fn(self.sub_sigma_next, self.sigma) + self.h_new_orig = self.h_new.clone() + self.h_new = self.h_new + self.noise_boost_substep * (self.h - self.h_eta) + else: + self.h_new = self.h_eta = self.h + self.h_new_orig = self.h_new.clone() + + + + + def get_sde_substep(self, + sigma :Tensor, + sigma_next :Tensor, + eta :float = 0.0 , + noise_mode_override :Optional[str] = None , + DOWN :bool = False, + ) -> Tuple[Tensor,Tensor,Tensor,Tensor]: + + return self.get_sde_step(sigma=sigma, sigma_next=sigma_next, eta=eta, noise_mode_override=noise_mode_override, DOWN=DOWN, SUBSTEP=True,) + + def get_sde_step(self, + sigma :Tensor, + sigma_next :Tensor, + eta :float = 0.0 , + noise_mode_override :Optional[str] = None , + DOWN :bool = False, + SUBSTEP :bool = False, + VP_OVERRIDE = None, + ) -> Tuple[Tensor,Tensor,Tensor,Tensor]: + + VARIANCE_PRESERVING = VP_OVERRIDE if VP_OVERRIDE is not None else self.VARIANCE_PRESERVING + + if noise_mode_override is not None: + noise_mode = noise_mode_override + elif SUBSTEP: + noise_mode = self.noise_mode_sde_substep + else: + noise_mode = self.noise_mode_sde + + if DOWN: #calculates noise level by first scaling sigma_down from sigma_next, instead of sigma_up from sigma_next + eta_fn = lambda eta_scale: 1-eta_scale + sud_fn = lambda sd: (sd, None) + else: + eta_fn = lambda eta_scale: eta_scale + sud_fn = lambda su: (None, su) + + su, sd, sud = None, None, None + eta_ratio = None + sigma_base = sigma_next + + sigmax = self.sigma_max if VP_OVERRIDE is None else 1 + + match noise_mode: + case "hard": + eta_ratio = eta + case "exp": + h = -(sigma_next/sigma).log() + eta_ratio = (1 - (-2*eta*h).exp())**.5 + case "soft": + eta_ratio = 1-(1 - eta) + eta * ((sigma_next) / sigma) + case "softer": + eta_ratio = 1-torch.sqrt(1 - (eta**2 * (sigma**2 - sigma_next**2)) / sigma**2) + case "soft-linear": + eta_ratio = 1-eta * (sigma_next - sigma) + case "sinusoidal": + eta_ratio = eta * torch.sin(torch.pi * (sigma_next / sigmax)) ** 2 + case "eps": + eta_ratio = eta * torch.sqrt((sigma_next/sigma) ** 2 * (sigma ** 2 - sigma_next ** 2) ) + + case "lorentzian": + eta_ratio = eta + alpha = 1 / ((sigma_next.to(sigma.dtype))**2 + 1) + sigma_base = ((1 - alpha) ** 0.5).to(sigma.dtype) + + case "hard_var": + sigma_var = (-1 + torch.sqrt(1 + 4 * sigma)) / 2 + if sigma_next > sigma_var: + eta_ratio = 0 + sigma_base = sigma_next + else: + eta_ratio = eta + sigma_base = torch.sqrt((sigma - sigma_next).abs() + 1e-10) + + case "hard_sq": + sigma_hat = sigma * (1 + eta) + su = (sigma_hat ** 2 - sigma ** 2) ** .5 #su + + if VARIANCE_PRESERVING: + alpha_ratio, sd, su = self.get_sde_coeff(sigma_next, None, su, eta, VARIANCE_PRESERVING) + else: + sd = sigma_next + sigma = sigma_hat + alpha_ratio = torch.ones_like(sigma) + + case "vpsde": + alpha_ratio, sd, su = self.get_vpsde_step_RF(sigma, sigma_next, eta) + + case "er4": + #def noise_scaler(sigma): + # return sigma * ((sigma ** 0.3).exp() + 10.0) + noise_scaler = lambda sigma: sigma * ((sigma ** eta).exp() + 10.0) + alpha_ratio = noise_scaler(sigma_next) / noise_scaler(sigma) + sigma_up = (sigma_next ** 2 - sigma ** 2 * alpha_ratio ** 2) ** 0.5 + eta_ratio = sigma_up / sigma_next + + + if eta_ratio is not None: + sud = sigma_base * eta_fn(eta_ratio) + alpha_ratio, sd, su = self.get_sde_coeff(sigma_next, *sud_fn(sud), eta, VARIANCE_PRESERVING) + + su = torch.nan_to_num(su, 0.0) + sd = torch.nan_to_num(sd, float(sigma_next)) + alpha_ratio = torch.nan_to_num(alpha_ratio, 1.0) + + return su, sigma, sd, alpha_ratio + + def get_vpsde_step_RF(self, sigma:Tensor, sigma_next:Tensor, eta:float) -> Tuple[Tensor,Tensor,Tensor]: + dt = sigma - sigma_next + sigma_up = eta * sigma * dt**0.5 + alpha_ratio = 1 - dt * (eta**2/4) * (1 + sigma) + sigma_down = sigma_next - (eta/4)*sigma*(1-sigma)*(sigma - sigma_next) + return sigma_up, sigma_down, alpha_ratio + + def linear_noise_init(self, y:Tensor, sigma_curr:Tensor, x_base:Optional[Tensor]=None, x_curr:Optional[Tensor]=None, mask:Optional[Tensor]=None) -> Tensor: + + y_noised = (self.sigma_max - sigma_curr) * y + sigma_curr * self.init_noise + + if x_curr is not None: + x_curr = x_curr + sigma_curr * (self.init_noise - y) + x_base = x_base + self.sigma * (self.init_noise - y) + return y_noised, x_base, x_curr + + if mask is not None: + y_noised = mask * y_noised + (1-mask) * y + + return y_noised + + def linear_noise_step(self, y:Tensor, sigma_curr:Optional[Tensor]=None, x_base:Optional[Tensor]=None, x_curr:Optional[Tensor]=None, brownian_sigma:Optional[Tensor]=None, brownian_sigma_next:Optional[Tensor]=None, mask:Optional[Tensor]=None) -> Tensor: + if self.sigma_up_eta == 0 or self.sigma_next == 0: + return y, x_base, x_curr + + sigma_curr = self.sub_sigma if sigma_curr is None else sigma_curr + + brownian_sigma = sigma_curr if brownian_sigma is None else brownian_sigma + brownian_sigma_next = self.sigma_next.clone() if brownian_sigma_next is None else brownian_sigma_next + + if brownian_sigma == brownian_sigma_next: + brownian_sigma_next *= 0.999 + + if brownian_sigma_next > brownian_sigma and not self.EO("disable_brownian_swap"): # should this really be done? + brownian_sigma, brownian_sigma_next = brownian_sigma_next, brownian_sigma + + noise = self.noise_sampler(sigma=brownian_sigma, sigma_next=brownian_sigma_next) + noise = normalize_zscore(noise, channelwise=True, inplace=True) + + y_noised = (self.sigma_max - sigma_curr) * y + sigma_curr * noise + + if x_curr is not None: + x_curr = x_curr + sigma_curr * (noise - y) + x_base = x_base + self.sigma * (noise - y) + return y_noised, x_base, x_curr + + if mask is not None: + y_noised = mask * y_noised + (1-mask) * y + + return y_noised + + + def linear_noise_substep(self, y:Tensor, sigma_curr:Optional[Tensor]=None, x_base:Optional[Tensor]=None, x_curr:Optional[Tensor]=None, brownian_sigma:Optional[Tensor]=None, brownian_sigma_next:Optional[Tensor]=None, mask:Optional[Tensor]=None) -> Tensor: + if self.sub_sigma_up_eta == 0 or self.sub_sigma_next == 0: + return y, x_base, x_curr + + sigma_curr = self.sub_sigma if sigma_curr is None else sigma_curr + + brownian_sigma = sigma_curr if brownian_sigma is None else brownian_sigma + brownian_sigma_next = self.sub_sigma_next.clone() if brownian_sigma_next is None else brownian_sigma_next + + if brownian_sigma == brownian_sigma_next: + brownian_sigma_next *= 0.999 + + if brownian_sigma_next > brownian_sigma and not self.EO("disable_brownian_swap"): # should this really be done? + brownian_sigma, brownian_sigma_next = brownian_sigma_next, brownian_sigma + + noise = self.noise_sampler2(sigma=brownian_sigma, sigma_next=brownian_sigma_next) + noise = normalize_zscore(noise, channelwise=True, inplace=True) + + y_noised = (self.sigma_max - sigma_curr) * y + sigma_curr * noise + + if x_curr is not None: + x_curr = x_curr + sigma_curr * (noise - y) + x_base = x_base + self.sigma * (noise - y) + return y_noised, x_base, x_curr + + if mask is not None: + y_noised = mask * y_noised + (1-mask) * y + + return y_noised + + + def swap_noise_step(self, x_0:Tensor, x_next:Tensor, brownian_sigma:Optional[Tensor]=None, brownian_sigma_next:Optional[Tensor]=None, mask:Optional[Tensor]=None) -> Tensor: + if self.sigma_up_eta == 0 or self.sigma_next == 0: + return x_next + + brownian_sigma = self.sigma.clone() if brownian_sigma is None else brownian_sigma + brownian_sigma_next = self.sigma_next.clone() if brownian_sigma_next is None else brownian_sigma_next + + if brownian_sigma == brownian_sigma_next: + brownian_sigma_next *= 0.999 + + eps_next = (x_0 - x_next) / (self.sigma - self.sigma_next) + denoised_next = x_0 - self.sigma * eps_next + + if brownian_sigma_next > brownian_sigma and not self.EO("disable_brownian_swap"): # should this really be done? + brownian_sigma, brownian_sigma_next = brownian_sigma_next, brownian_sigma + + noise = self.noise_sampler(sigma=brownian_sigma, sigma_next=brownian_sigma_next) + noise = normalize_zscore(noise, channelwise=True, inplace=True) + + x_noised = self.alpha_ratio_eta * (denoised_next + self.sigma_down_eta * eps_next) + self.sigma_up_eta * noise * self.s_noise + + if mask is not None: + x = mask * x_noised + (1-mask) * x_next + else: + x = x_noised + + return x + + + def swap_noise_substep(self, x_0:Tensor, x_next:Tensor, brownian_sigma:Optional[Tensor]=None, brownian_sigma_next:Optional[Tensor]=None, mask:Optional[Tensor]=None, guide:Optional[Tensor]=None) -> Tensor: + if self.sub_sigma_up_eta == 0 or self.sub_sigma_next == 0: + return x_next + + brownian_sigma = self.sub_sigma.clone() if brownian_sigma is None else brownian_sigma + brownian_sigma_next = self.sub_sigma_next.clone() if brownian_sigma_next is None else brownian_sigma_next + + if brownian_sigma == brownian_sigma_next: + brownian_sigma_next *= 0.999 + + eps_next = (x_0 - x_next) / (self.sigma - self.sub_sigma_next) + denoised_next = x_0 - self.sigma * eps_next + + if brownian_sigma_next > brownian_sigma and not self.EO("disable_brownian_swap"): # should this really be done? + brownian_sigma, brownian_sigma_next = brownian_sigma_next, brownian_sigma + + noise = self.noise_sampler2(sigma=brownian_sigma, sigma_next=brownian_sigma_next) + noise = normalize_zscore(noise, channelwise=True, inplace=True) + + x_noised = self.sub_alpha_ratio_eta * (denoised_next + self.sub_sigma_down_eta * eps_next) + self.sub_sigma_up_eta * noise * self.s_noise_substep + + if mask is not None: + x = mask * x_noised + (1-mask) * x_next + else: + x = x_noised + + return x + + + + + def swap_noise_inv_substep(self, x_0:Tensor, x_next:Tensor, eta_substep:float, row:int, row_offset_multistep_stages:int, brownian_sigma:Optional[Tensor]=None, brownian_sigma_next:Optional[Tensor]=None, mask:Optional[Tensor]=None, guide:Optional[Tensor]=None) -> Tensor: + if self.sub_sigma_up_eta == 0 or self.sub_sigma_next == 0: + return x_next + + brownian_sigma = self.sub_sigma.clone() if brownian_sigma is None else brownian_sigma + brownian_sigma_next = self.sub_sigma_next.clone() if brownian_sigma_next is None else brownian_sigma_next + + if brownian_sigma == brownian_sigma_next: + brownian_sigma_next *= 0.999 + + eps_next = (x_0 - x_next) / ((1-self.sigma) - (1-self.sub_sigma_next)) + denoised_next = x_0 - (1-self.sigma) * eps_next + + if brownian_sigma_next > brownian_sigma and not self.EO("disable_brownian_swap"): # should this really be done? + brownian_sigma, brownian_sigma_next = brownian_sigma_next, brownian_sigma + + noise = self.noise_sampler2(sigma=brownian_sigma, sigma_next=brownian_sigma_next) + noise = normalize_zscore(noise, channelwise=True, inplace=True) + + sub_sigma_up, sub_sigma, sub_sigma_down, sub_alpha_ratio = self.get_sde_substep(sigma = 1-self.s_[row], + sigma_next = 1-self.s_[row_offset_multistep_stages], + eta = eta_substep, + noise_mode_override = self.noise_mode_sde_substep, + DOWN = self.DOWN_SUBSTEP) + + x_noised = sub_alpha_ratio * (denoised_next + sub_sigma_down * eps_next) + sub_sigma_up * noise * self.s_noise_substep + + if mask is not None: + x = mask * x_noised + (1-mask) * x_next + else: + x = x_noised + + return x + + + def swap_noise(self, + x_0 :Tensor, + x_next :Tensor, + sigma_0 :Tensor, + sigma :Tensor, + sigma_next :Tensor, + sigma_down :Tensor, + sigma_up :Tensor, + alpha_ratio :Tensor, + s_noise :float, + SUBSTEP :bool = False, + brownian_sigma :Optional[Tensor] = None, + brownian_sigma_next :Optional[Tensor] = None, + ) -> Tensor: + + if sigma_up == 0: + return x_next + + if brownian_sigma is None: + brownian_sigma = sigma.clone() + if brownian_sigma_next is None: + brownian_sigma_next = sigma_next.clone() + if sigma_next == 0: + return x_next + if brownian_sigma == brownian_sigma_next: + brownian_sigma_next *= 0.999 + eps_next = (x_0 - x_next) / (sigma_0 - sigma_next) + denoised_next = x_0 - sigma_0 * eps_next + + if brownian_sigma_next > brownian_sigma: + s_tmp = brownian_sigma + brownian_sigma = brownian_sigma_next + brownian_sigma_next = s_tmp + + if not SUBSTEP: + noise = self.noise_sampler(sigma=brownian_sigma, sigma_next=brownian_sigma_next) + else: + noise = self.noise_sampler2(sigma=brownian_sigma, sigma_next=brownian_sigma_next) + + noise = normalize_zscore(noise, channelwise=True, inplace=True) + + x = alpha_ratio * (denoised_next + sigma_down * eps_next) + sigma_up * noise * s_noise + return x + + # not used. WARNING: some parameters have a different order than swap_noise! + def add_noise_pre(self, + x_0 :Tensor, + x :Tensor, + sigma_up :Tensor, + sigma_0 :Tensor, + sigma :Tensor, + sigma_next :Tensor, + real_sigma_down :Tensor, + alpha_ratio :Tensor, + s_noise :float, + noise_mode :str, + SDE_NOISE_EXTERNAL :bool = False, + sde_noise_t :Optional[Tensor] = None, + SUBSTEP :bool = False, + ) -> Tensor: + + if not self.CONST and noise_mode == "hard_sq": + if self.LOCK_H_SCALE: + x = self.swap_noise(x_0 = x_0, + x = x, + sigma = sigma, + sigma_0 = sigma_0, + sigma_next = sigma_next, + real_sigma_down = real_sigma_down, + sigma_up = sigma_up, + alpha_ratio = alpha_ratio, + s_noise = s_noise, + SUBSTEP = SUBSTEP, + ) + else: + x = self.add_noise( x = x, + sigma_up = sigma_up, + sigma = sigma, + sigma_next = sigma_next, + alpha_ratio = alpha_ratio, + s_noise = s_noise, + SDE_NOISE_EXTERNAL = SDE_NOISE_EXTERNAL, + sde_noise_t = sde_noise_t, + SUBSTEP = SUBSTEP, + ) + + return x + + # only used for handle_tiled_etc_noise_steps() in rk_guide_func_beta.py + def add_noise_post(self, + x_0 :Tensor, + x :Tensor, + sigma_up :Tensor, + sigma_0 :Tensor, + sigma :Tensor, + sigma_next :Tensor, + real_sigma_down :Tensor, + alpha_ratio :Tensor, + s_noise :float, + noise_mode :str, + SDE_NOISE_EXTERNAL :bool = False, + sde_noise_t :Optional[Tensor] = None, + SUBSTEP :bool = False, + ) -> Tensor: + + if self.CONST or (not self.CONST and noise_mode != "hard_sq"): + if self.LOCK_H_SCALE: + x = self.swap_noise(x_0 = x_0, + x = x, + sigma = sigma, + sigma_0 = sigma_0, + sigma_next = sigma_next, + real_sigma_down = real_sigma_down, + sigma_up = sigma_up, + alpha_ratio = alpha_ratio, + s_noise = s_noise, + SUBSTEP = SUBSTEP, + ) + else: + x = self.add_noise( x = x, + sigma_up = sigma_up, + sigma = sigma, + sigma_next = sigma_next, + alpha_ratio = alpha_ratio, + s_noise = s_noise, + SDE_NOISE_EXTERNAL = SDE_NOISE_EXTERNAL, + sde_noise_t = sde_noise_t, + SUBSTEP = SUBSTEP, + ) + return x + + def add_noise(self, + x :Tensor, + sigma_up :Tensor, + sigma :Tensor, + sigma_next :Tensor, + alpha_ratio :Tensor, + s_noise :float, + SDE_NOISE_EXTERNAL :bool = False, + sde_noise_t :Optional[Tensor] = None, + SUBSTEP :bool = False, + ) -> Tensor: + + if sigma_next > 0.0 and sigma_up > 0.0: + if sigma_next > sigma: + sigma, sigma_next = sigma_next, sigma + + if sigma == sigma_next: + sigma_next = sigma * 0.9999 + if not SUBSTEP: + noise = self.noise_sampler (sigma=sigma, sigma_next=sigma_next) + else: + noise = self.noise_sampler2(sigma=sigma, sigma_next=sigma_next) + + #noise_ortho = get_orthogonal(noise, x) + #noise_ortho = noise_ortho / noise_ortho.std()model, + noise = normalize_zscore(noise, channelwise=True, inplace=True) + + if SDE_NOISE_EXTERNAL: + noise = (1-s_noise) * noise + s_noise * sde_noise_t + + x_next = alpha_ratio * x + noise * sigma_up * s_noise + + return x_next + + else: + return x + + def sigma_from_to(self, + x_0 : Tensor, + x_down : Tensor, + sigma : Tensor, + sigma_down : Tensor, + sigma_next : Tensor) -> Tensor: #sigma, sigma_from, sigma_to + + eps = (x_0 - x_down) / (sigma - sigma_down) + denoised = x_0 - sigma * eps + x_next = denoised + sigma_next * eps # VESDE vs VPSDE equiv.? + return x_next + + def rebound_overshoot_step(self, x_0:Tensor, x:Tensor) -> Tensor: + eps = (x_0 - x) / (self.sigma - self.sigma_down) + denoised = x_0 - self.sigma * eps + x = denoised + self.sigma_next * eps + return x + + def rebound_overshoot_substep(self, x_0:Tensor, x:Tensor) -> Tensor: + if self.sigma - self.sub_sigma_down > 0: + sub_eps = (x_0 - x) / (self.sigma - self.sub_sigma_down) + sub_denoised = x_0 - self.sigma * sub_eps + x = sub_denoised + self.sub_sigma_next * sub_eps + return x + + def prepare_sigmas(self, + sigmas : Tensor, + sigmas_override : Tensor, + d_noise : float, + d_noise_start_step : int, + sampler_mode : str) -> Tuple[Tensor,bool]: + #SIGMA_MIN = torch.full_like(self.sigma_min, 0.00227896) if self.sigma_min < 0.00227896 else self.sigma_min # prevent black image with unsampling flux, which has a sigma_min of 0.0002 + SIGMA_MIN = self.sigma_min #torch.full_like(self.sigma_min, max(0.01, self.sigma_min.item())) + if sigmas_override is not None: + sigmas = sigmas_override.clone().to(sigmas.device).to(sigmas.dtype) + + if d_noise_start_step == 0: + sigmas = sigmas.clone() * d_noise + + UNSAMPLE_FROM_ZERO = False + if sigmas[0] == 0.0: #remove padding used to prevent comfy from adding noise to the latent (for unsampling, etc.) + UNSAMPLE = True + if sigmas[-1] == 0.0: + UNSAMPLE_FROM_ZERO = True + #sigmas = sigmas[1:-1] # was cleaving off 1.0 at the end when restart looping + sigmas = sigmas[1:] + if sigmas[-1] == 0.0: + sigmas = sigmas[:-1] + else: + UNSAMPLE = False + + if hasattr(self.model, "sigmas"): + self.model.sigmas = sigmas + + if sampler_mode == "standard": + UNSAMPLE = False + + consecutive_duplicate_mask = torch.cat((torch.tensor([True], device=sigmas.device), torch.diff(sigmas) != 0)) + sigmas = sigmas[consecutive_duplicate_mask] + + if sigmas[-1] == 0: + if sigmas[-2] < SIGMA_MIN: + sigmas[-2] = SIGMA_MIN + elif (sigmas[-2] - SIGMA_MIN).abs() > 1e-4: + sigmas = torch.cat((sigmas[:-1], SIGMA_MIN.unsqueeze(0), sigmas[-1:])) + + elif UNSAMPLE_FROM_ZERO and not torch.isclose(sigmas[0], SIGMA_MIN): + sigmas = torch.cat([SIGMA_MIN.unsqueeze(0), sigmas]) + + self.sigmas = sigmas + self.UNSAMPLE = UNSAMPLE + self.d_noise = d_noise + self.sampler_mode = sampler_mode + + return sigmas, UNSAMPLE + + + + + + +def extract_latent_swap_noise(self, x:Tensor, x_noise_swapped:Tensor, sigma:Tensor, old_noise:Tensor) -> Tensor: + return (x - x_noise_swapped) / sigma + old_noise + +def update_latent_swap_noise(self, x:Tensor, sigma:Tensor, old_noise:Tensor, new_noise:Tensor) -> Tensor: + return x + sigma * (new_noise - old_noise) + + + + diff --git a/ComfyUI/custom_nodes/RES4LYF/beta/rk_sampler_beta.py b/ComfyUI/custom_nodes/RES4LYF/beta/rk_sampler_beta.py new file mode 100644 index 0000000000000000000000000000000000000000..0238d7a316fe37cb85cbb8cdf4971aa5c7d02820 --- /dev/null +++ b/ComfyUI/custom_nodes/RES4LYF/beta/rk_sampler_beta.py @@ -0,0 +1,2232 @@ +import torch +from torch import Tensor +import torch.nn.functional as F +from tqdm.auto import trange +import gc +from typing import Optional, Callable, Tuple, List, Dict, Any, Union +import math +import copy + +from comfy.model_sampling import EPS +import comfy + +from ..res4lyf import RESplain +from ..helper import ExtraOptions, FrameWeightsManager +from ..latents import lagrange_interpolation, get_collinear, get_orthogonal, get_cosine_similarity, get_pearson_similarity, get_slerp_weight_for_cossim, get_slerp_ratio, slerp_tensor, get_edge_mask, normalize_zscore, compute_slerp_ratio_for_target, find_slerp_ratio_grid +from ..style_transfer import apply_scattersort_spatial, apply_adain_spatial + +from .rk_method_beta import RK_Method_Beta +from .rk_noise_sampler_beta import RK_NoiseSampler +from .rk_guide_func_beta import LatentGuide +from .phi_functions import Phi +from .constants import MAX_STEPS, GUIDE_MODE_NAMES_PSEUDOIMPLICIT + +def init_implicit_sampling( + RK : RK_Method_Beta, + x_0 : Tensor, + x_ : Tensor, + eps_ : Tensor, + eps_prev_ : Tensor, + data_ : Tensor, + eps : Tensor, + denoised : Tensor, + denoised_prev2 : Tensor, + step : int, + sigmas : Tensor, + h : Tensor, + s_ : Tensor, + EO : ExtraOptions, + SYNC_GUIDE_ACTIVE, + ): + + sigma = sigmas[step] + if EO("implicit_skip_model_call_at_start") and denoised.sum() + eps.sum() != 0: + if denoised_prev2.sum() == 0: + eps_ [0] = eps.clone() + data_[0] = denoised.clone() + eps_ [0] = RK.get_epsilon_anchored(x_0, denoised, sigma) + else: + sratio = sigma - s_[0] + data_[0] = denoised + sratio * (denoised - denoised_prev2) + + elif EO("implicit_full_skip_model_call_at_start") and denoised.sum() + eps.sum() != 0: + if denoised_prev2.sum() == 0: + eps_ [0] = eps.clone() + data_[0] = denoised.clone() + eps_ [0] = RK.get_epsilon_anchored(x_0, denoised, sigma) + else: + for r in range(RK.rows): + sratio = sigma - s_[r] + data_[r] = denoised + sratio * (denoised - denoised_prev2) + eps_ [r] = RK.get_epsilon_anchored(x_0, data_[r], s_[r]) + + elif EO("implicit_lagrange_skip_model_call_at_start") and denoised.sum() + eps.sum() != 0: + if denoised_prev2.sum() == 0: + eps_ [0] = eps.clone() + data_[0] = denoised.clone() + eps_ [0] = RK.get_epsilon_anchored(x_0, denoised, sigma) + else: + sigma_prev = sigmas[step-1] + h_prev = sigma - sigma_prev + w = h / h_prev + substeps_prev = len(RK.C[:-1]) + + for r in range(RK.rows): + sratio = sigma - s_[r] + data_[r] = lagrange_interpolation([0,1], [denoised_prev2, denoised], 1 + w*RK.C[r]).squeeze(0) + denoised_prev2 - denoised + eps_ [r] = RK.get_epsilon_anchored(x_0, data_[r], s_[r]) + + if EO("implicit_lagrange_skip_model_call_at_start_0_only"): + for r in range(RK.rows): + eps_ [r] = eps_ [0].clone() * s_[0] / s_[r] + data_[r] = denoised.clone() + + + elif EO("implicit_lagrange_init") and denoised.sum() + eps.sum() != 0: + sigma_prev = sigmas[step-1] + h_prev = sigma - sigma_prev + w = h / h_prev + substeps_prev = len(RK.C[:-1]) + + z_prev_ = eps_.clone() + for r in range (substeps_prev): + z_prev_[r] = h * RK.zum(r, eps_) # u,v not implemented for lagrange guess for implicit + zi_1 = lagrange_interpolation(RK.C[:-1], z_prev_[:substeps_prev], RK.C[0]).squeeze(0) # + x_prev - x_0""" + x_[0] = x_0 + zi_1 + + else: + + eps_[0], data_[0] = RK(x_[0], sigma, x_0, sigma) + + if not EO(("implicit_lagrange_init", "radaucycle", "implicit_full_skip_model_call_at_start", "implicit_lagrange_skip_model_call_at_start")): + for r in range(RK.rows): + eps_ [r] = eps_ [0].clone() * sigma / s_[r] + data_[r] = data_[0].clone() + + x_, eps_ = RK.newton_iter(x_0, x_, eps_, eps_prev_, data_, s_, 0, h, sigmas, step, "init", SYNC_GUIDE_ACTIVE) + return x_, eps_, data_ + + +@torch.no_grad() +def sample_rk_beta( + model, + x : Tensor, + sigmas : Tensor, + sigmas_override : Optional[Tensor] = None, + + extra_args : Optional[Tensor] = None, + callback : Optional[Callable] = None, + disable : bool = None, + + sampler_mode : str = "standard", + + rk_type : str = "res_2m", + implicit_sampler_name : str = "use_explicit", + + c1 : float = 0.0, + c2 : float = 0.5, + c3 : float = 1.0, + + noise_sampler_type : str = "gaussian", + noise_sampler_type_substep : str = "gaussian", + noise_mode_sde : str = "hard", + noise_mode_sde_substep : str = "hard", + + eta : float = 0.5, + eta_substep : float = 0.5, + + + + + noise_scaling_weight : float = 0.0, + noise_scaling_type : str = "sampler", + noise_scaling_mode : str = "linear", + noise_scaling_eta : float = 0.0, + noise_scaling_cycles : int = 1, + + noise_scaling_weights : Optional[Tensor] = None, + noise_scaling_etas : Optional[Tensor] = None, + + noise_boost_step : float = 0.0, + noise_boost_substep : float = 0.0, + noise_boost_normalize : bool = True, + noise_anchor : float = 1.0, + + s_noise : float = 1.0, + s_noise_substep : float = 1.0, + d_noise : float = 1.0, + d_noise_start_step : int = 0, + d_noise_inv : float = 1.0, + d_noise_inv_start_step : int = 0, + + + + alpha : float = -1.0, + alpha_substep : float = -1.0, + k : float = 1.0, + k_substep : float = 1.0, + + momentum : float = 0.0, + + + overshoot_mode : str = "hard", + overshoot_mode_substep : str = "hard", + overshoot : float = 0.0, + overshoot_substep : float = 0.0, + + implicit_type : str = "predictor-corrector", + implicit_type_substeps : str = "predictor-corrector", + + implicit_steps_diag : int = 0, + implicit_steps_full : int = 0, + + etas : Optional[Tensor] = None, + etas_substep : Optional[Tensor] = None, + s_noises : Optional[Tensor] = None, + s_noises_substep : Optional[Tensor] = None, + + momentums : Optional[Tensor] = None, + + regional_conditioning_weights : Optional[Tensor] = None, + regional_conditioning_floors : Optional[Tensor] = None, + narcissism_start_step : int = 0, + narcissism_end_step : int = 5, + + LGW_MASK_RESCALE_MIN : bool = True, + guides : Optional[Tuple[Any, ...]] = None, + epsilon_scales : Optional[Tensor] = None, + frame_weights_mgr : Optional[FrameWeightsManager] = None, + + sde_noise : list [Tensor] = [], + + noise_seed : int = -1, + noise_initial : Optional[Tensor] = None, + image_initial : Optional[Tensor] = None, + + cfgpp : float = 0.0, + cfg_cw : float = 1.0, + + BONGMATH : bool = True, + unsample_bongmath = None, + + state_info : Optional[dict[str, Any]] = None, + state_info_out : Optional[dict[str, Any]] = None, + + rk_swap_type : str = "", + rk_swap_step : int = MAX_STEPS, + rk_swap_threshold : float = 0.0, + rk_swap_print : bool = False, + + steps_to_run : int = -1, + start_at_step : int = -1, + tile_sizes : Optional[List[Tuple[int,int]]] = None, + + flow_sync_eps : float = 0.0, + + sde_mask : Optional[Tensor] = None, + + batch_num : int = 0, + + extra_options : str = "", + + AttnMask = None, + RegContext = None, + RegParam = None, + + AttnMask_neg = None, + RegContext_neg = None, + RegParam_neg = None, + ): + + if sampler_mode == "NULL": + return x + + EO = ExtraOptions(extra_options) + default_dtype = EO("default_dtype", torch.float64) + + extra_args = {} if extra_args is None else extra_args + model_device = model.inner_model.inner_model.device #x.device + work_device = 'cpu' if EO("work_device_cpu") else model_device + + state_info = {} if state_info is None else state_info + state_info_out = {} if state_info_out is None else state_info_out + + VE_MODEL = isinstance(model.inner_model.inner_model.model_sampling, EPS) + + RENOISE = False + if 'raw_x' in state_info and sampler_mode in {"resample", "unsample"}: + if x.shape == state_info['raw_x'].shape: + x = state_info['raw_x'].to(work_device) #clone() + else: + denoised = comfy.utils.bislerp(state_info['denoised'], x.shape[-1], x.shape[-2]) + x = denoised.to(x) + RENOISE = True + RESplain("Continuing from raw latent from previous sampler.", debug=False) + + + + start_step = 0 + if 'end_step' in state_info and (sampler_mode == "resample" or sampler_mode == "unsample"): + + if state_info['completed'] != True and state_info['end_step'] != 0 and state_info['end_step'] != -1 and state_info['end_step'] < len(state_info['sigmas'])-1 : #incomplete run in previous sampler node + + if state_info['sampler_mode'] in {"standard","resample"} and sampler_mode == "unsample" and sigmas[2] < sigmas[1]: + sigmas = torch.flip(state_info['sigmas'], dims=[0]) + start_step = (len(sigmas)-1) - (state_info['end_step']) #-1) #removed -1 at the end here. correct? + + if state_info['sampler_mode'] == "unsample" and sampler_mode == "resample" and sigmas[2] > sigmas[1]: + sigmas = torch.flip(state_info['sigmas'], dims=[0]) + start_step = (len(sigmas)-1) - state_info['end_step'] #-1) + elif state_info['sampler_mode'] == "unsample" and sampler_mode == "resample": + start_step = 0 + + if state_info['sampler_mode'] in {"standard", "resample"} and sampler_mode == "resample": + start_step = state_info['end_step'] if state_info['end_step'] != -1 else 0 + if start_step > 0: + sigmas = state_info['sigmas'].clone() + + + + if sde_mask is not None: + from .rk_guide_func_beta import prepare_mask + sde_mask, _ = prepare_mask(x, sde_mask, LGW_MASK_RESCALE_MIN) + sde_mask = sde_mask.to(x.device).to(x.dtype) + + + + x = x .to(dtype=default_dtype, device=work_device) + sigmas = sigmas.to(dtype=default_dtype, device=work_device) + + c1 = EO("c1" , c1) + c2 = EO("c2" , c2) + c3 = EO("c3" , c3) + + cfg_cw = EO("cfg_cw" , cfg_cw) + + noise_seed = EO("noise_seed" , noise_seed) + noise_seed_substep = EO("noise_seed_substep" , noise_seed + MAX_STEPS) + + pseudoimplicit_row_weights = EO("pseudoimplicit_row_weights" , [1. for _ in range(100)]) + pseudoimplicit_step_weights = EO("pseudoimplicit_step_weights", [1. for _ in range(max(implicit_steps_diag, implicit_steps_full)+1)]) + + noise_scaling_cycles = EO("noise_scaling_cycles", 1) + noise_boost_step = EO("noise_boost_step", 0.0) + noise_boost_substep = EO("noise_boost_substep", 0.0) + + # SETUP SAMPLER + if implicit_sampler_name not in ("use_explicit", "none"): + rk_type = implicit_sampler_name + RESplain("rk_type:", rk_type) + if implicit_sampler_name == "none": + implicit_steps_diag = implicit_steps_full = 0 + + RK = RK_Method_Beta.create(model, rk_type, VE_MODEL, noise_anchor, noise_boost_normalize, model_device=model_device, work_device=work_device, dtype=default_dtype, extra_options=extra_options) + RK.extra_args = RK.init_cfg_channelwise(x, cfg_cw, **extra_args) + RK.tile_sizes = tile_sizes + RK.extra_args['model_options']['transformer_options']['regional_conditioning_weight'] = 0.0 + RK.extra_args['model_options']['transformer_options']['regional_conditioning_floor'] = 0.0 + + RK.unsample_bongmath = BONGMATH if unsample_bongmath is None else unsample_bongmath # allow turning off bongmath for unsampling with cycles + + + # SETUP SIGMAS + sigmas_orig = sigmas.clone() + NS = RK_NoiseSampler(RK, model, device=work_device, dtype=default_dtype, extra_options=extra_options) + sigmas, UNSAMPLE = NS.prepare_sigmas(sigmas, sigmas_override, d_noise, d_noise_start_step, sampler_mode) + if UNSAMPLE and sigmas_orig[0] == 0.0 and sigmas_orig[0] != sigmas[0] and sigmas[1] < sigmas[2]: + sigmas = torch.cat([torch.full_like(sigmas[0], 0.0).unsqueeze(0), sigmas]) + if start_step == 0: + start_step = 1 + else: + start_step -= 1 + + if sampler_mode in {"resample", "unsample"}: + state_info_sigma_next = state_info.get('sigma_next', -1) + state_info_start_step = (sigmas == state_info_sigma_next).nonzero().flatten() + if state_info_start_step.shape[0] > 0: + start_step = state_info_start_step.item() + + start_step = start_at_step if start_at_step >= 0 else start_step + + + SDE_NOISE_EXTERNAL = False + if sde_noise is not None: + if len(sde_noise) > 0 and sigmas[1] > sigmas[2]: + SDE_NOISE_EXTERNAL = True + sigma_up_total = torch.zeros_like(sigmas[0]) + for i in range(len(sde_noise)-1): + sigma_up_total += sigmas[i+1] + etas = torch.full_like(sigmas, eta / sigma_up_total) + + if 'last_rng' in state_info and sampler_mode in {"resample", "unsample"} and noise_seed < 0: + last_rng = state_info['last_rng'].clone() + last_rng_substep = state_info['last_rng_substep'].clone() + else: + last_rng = None + last_rng_substep = None + + NS.init_noise_samplers(x, noise_seed, noise_seed_substep, noise_sampler_type, noise_sampler_type_substep, noise_mode_sde, noise_mode_sde_substep, \ + overshoot_mode, overshoot_mode_substep, noise_boost_step, noise_boost_substep, alpha, alpha_substep, k, k_substep, \ + last_rng=last_rng, last_rng_substep=last_rng_substep,) + + data_ = None + eps_ = None + eps = torch.zeros_like(x, dtype=default_dtype, device=work_device) + denoised = torch.zeros_like(x, dtype=default_dtype, device=work_device) + denoised_prev = torch.zeros_like(x, dtype=default_dtype, device=work_device) + denoised_prev2 = torch.zeros_like(x, dtype=default_dtype, device=work_device) + x_ = None + eps_prev_ = None + denoised_data_prev = None + denoised_data_prev2 = None + h_prev = None + eps_y2x_ = None + eps_x2y_ = None + eps_y_ = None + eps_prev_y_ = None + data_y_ = None + yt_ = None + yt_0 = None + eps_yt_ = None + eps_x_ = None + data_y_ = None + data_x_ = None + z_ = None # for tracking residual noise for model scattersort/synchronized diffusion + + y0_bongflow = state_info.get('y0_bongflow') + y0_bongflow_orig = state_info.get('y0_bongflow_orig') + noise_bongflow = state_info.get('noise_bongflow') + y0_standard_guide = state_info.get('y0_standard_guide') + y0_inv_standard_guide = state_info.get('y0_inv_standard_guide') + + data_prev_y_ = state_info.get('data_prev_y_') + data_prev_x_ = state_info.get('data_prev_x_') + data_prev_x2y_ = state_info.get('data_prev_x2y_') + + # BEGIN SAMPLING LOOP + num_steps = len(sigmas[start_step:])-2 if sigmas[-1] == 0 else len(sigmas[start_step:])-1 + + if steps_to_run >= 0: + current_steps = min(num_steps, steps_to_run) + num_steps = start_step + min(num_steps, steps_to_run) + else: + current_steps = num_steps + num_steps = start_step + num_steps + #current_steps = current_steps + 1 if sigmas[-1] == 0 and steps_to_run < 0 and UNSAMPLE else current_steps + + INIT_SAMPLE_LOOP = True + step = start_step + sigma, sigma_next, data_prev_ = None, None, None + + if (num_steps-1) == len(sigmas)-2 and sigmas[-1] == 0 and sigmas[-2] == NS.sigma_min: + progress_bar = trange(current_steps+1, disable=disable) + else: + progress_bar = trange(current_steps, disable=disable) + + + # SETUP GUIDES + LG = LatentGuide(model, sigmas, UNSAMPLE, VE_MODEL, LGW_MASK_RESCALE_MIN, extra_options, device=work_device, dtype=default_dtype, frame_weights_mgr=frame_weights_mgr) + + guide_inversion_y0 = state_info.get('guide_inversion_y0') + guide_inversion_y0_inv = state_info.get('guide_inversion_y0_inv') + + x = LG.init_guides(x, RK.IMPLICIT, guides, NS.noise_sampler, batch_num, sigmas[step], guide_inversion_y0, guide_inversion_y0_inv) + LG.y0 = y0_standard_guide if y0_standard_guide is not None else LG.y0 + LG.y0_inv = y0_inv_standard_guide if y0_inv_standard_guide is not None else LG.y0_inv + if (LG.mask != 1.0).any() and ((LG.y0 == 0).all() or (LG.y0_inv == 0).all()) : # and not LG.guide_mode.startswith("flow"): # (LG.y0.sum() == 0 or LG.y0_inv.sum() == 0): + SKIP_PSEUDO = True + RESplain("skipping pseudo...") + if LG.y0 .sum() == 0: + SKIP_PSEUDO_Y = "y0" + elif LG.y0_inv.sum() == 0: + SKIP_PSEUDO_Y = "y0_inv" + else: + SKIP_PSEUDO = False + if guides is not None and guides.get('guide_mode', '') != "inversion" or sampler_mode != "unsample": #do not set denoised_prev to noise guide with inversion! + if LG.y0.sum() != 0 and LG.y0_inv.sum() != 0: + denoised_prev = LG.mask * LG.y0 + (1-LG.mask) * LG.y0_inv + elif LG.y0.sum() != 0: + denoised_prev = LG.y0 + elif LG.y0_inv.sum() != 0: + denoised_prev = LG.y0_inv + data_cached = None + + if EO("pseudo_mix_strength"): + orig_y0 = LG.y0.clone() + orig_y0_inv = LG.y0_inv.clone() + + #gc.collect() + BASE_STARTED = False + INV_STARTED = False + FLOW_STARTED = False + FLOW_STOPPED = False + noise_xt, noise_yt = None, None + FLOW_RESUMED = False + if state_info.get('FLOW_STARTED', False) and not state_info.get('FLOW_STOPPED', False): + FLOW_RESUMED = True + y0 = state_info['y0'].to(work_device) + data_cached = state_info['data_cached'].to(work_device) + data_x_prev_ = state_info['data_x_prev_'].to(work_device) + + if noise_initial is not None: + x_init = noise_initial.to(x) + RK.update_transformer_options({'x_init': x_init.clone()}) + + #progress_bar = trange(len(sigmas)-1-start_step, disable=disable) + + #if EO("eps_adain") or EO("x_init_to_model"): + + if AttnMask is not None: + RK.update_transformer_options({'AttnMask' : AttnMask}) + RK.update_transformer_options({'RegContext': RegContext}) + + if AttnMask_neg is not None: + RK.update_transformer_options({'AttnMask_neg' : AttnMask_neg}) + RK.update_transformer_options({'RegContext_neg': RegContext_neg}) + + if EO("y0_to_transformer_options"): + RK.update_transformer_options({'y0': LG.y0.clone()}) + + if EO("y0_inv_to_transformer_options"): + RK.update_transformer_options({'y0_inv': LG.y0_inv.clone()}) + for block in model.inner_model.inner_model.diffusion_model.double_stream_blocks: + for attr in ["txt_q_cache", "txt_k_cache", "txt_v_cache", "img_q_cache", "img_k_cache", "img_v_cache"]: + if hasattr(block.block.attn1, attr): + delattr(block.block.attn1, attr) + + for block in model.inner_model.inner_model.diffusion_model.single_stream_blocks: + block.block.attn1.EO = EO + for attr in ["txt_q_cache", "txt_k_cache", "txt_v_cache", "img_q_cache", "img_k_cache", "img_v_cache"]: + if hasattr(block.block.attn1, attr): + delattr(block.block.attn1, attr) + + RK.update_transformer_options({'ExtraOptions': copy.deepcopy(EO)}) + if EO("update_cross_attn"): + update_cross_attn = { + 'src_llama_start': EO('src_llama_start', 0), + 'src_llama_end': EO('src_llama_end', 0), + 'src_t5_start': EO('src_t5_start', 0), + 'src_t5_end': EO('src_t5_end', 0), + + 'tgt_llama_start': EO('tgt_llama_start', 0), + 'tgt_llama_end': EO('tgt_llama_end', 0), + 'tgt_t5_start': EO('tgt_t5_start', 0), + 'tgt_t5_end': EO('tgt_t5_end', 0), + 'skip_cross_attn': EO('skip_cross_attn', False), + + 'update_q': EO('update_q', False), + 'update_k': EO('update_k', True), + 'update_v': EO('update_v', True), + + + 'lamb': EO('lamb', 0.01), + 'erase': EO('erase', 10.0), + } + RK.update_transformer_options({'update_cross_attn': update_cross_attn}) + else: + RK.update_transformer_options({'update_cross_attn': None}) + + if LG.HAS_LATENT_GUIDE_ADAIN: + RK.update_transformer_options({'blocks_adain_cache': []}) + if LG.HAS_LATENT_GUIDE_ATTNINJ: + RK.update_transformer_options({'blocks_attninj_cache': []}) + if LG.HAS_LATENT_GUIDE_STYLE_POS: + if LG.HAS_LATENT_GUIDE and y0_standard_guide is None: + y0_cache = LG.y0.clone().cpu() + RK.update_transformer_options({'y0_standard_guide': LG.y0}) + + sigmas_scheduled = sigmas.clone() # store for return in state_info_out + + if EO("sigma_restarts"): + sigma_restarts = 1 + EO("sigma_restarts", 0) + sigmas = sigmas[step:num_steps+1].repeat(sigma_restarts) + step = 0 + num_steps = 2 * sigma_restarts - 1 + + if RENOISE: # TODO: adapt for noise inversion somehow + if VE_MODEL: + x = x + sigmas[step] * NS.noise_sampler(sigma=sigmas[step], sigma_next=sigmas[step+1]) + else: + x = (1 - sigmas[step]) * x + sigmas[step] * NS.noise_sampler(sigma=sigmas[step], sigma_next=sigmas[step+1]) + LG.ADAIN_NOISE_MODE = "" + StyleMMDiT = None + if guides is not None: + RK.update_transformer_options({"freqsep_lowpass_method": guides.get("freqsep_lowpass_method")}) + RK.update_transformer_options({"freqsep_sigma": guides.get("freqsep_sigma")}) + RK.update_transformer_options({"freqsep_kernel_size": guides.get("freqsep_kernel_size")}) + RK.update_transformer_options({"freqsep_inner_kernel_size": guides.get("freqsep_inner_kernel_size")}) + RK.update_transformer_options({"freqsep_stride": guides.get("freqsep_stride")}) + + + RK.update_transformer_options({"freqsep_lowpass_weight": guides.get("freqsep_lowpass_weight")}) + RK.update_transformer_options({"freqsep_highpass_weight":guides.get("freqsep_highpass_weight")}) + RK.update_transformer_options({"freqsep_mask": guides.get("freqsep_mask")}) + + StyleMMDiT = guides.get('StyleMMDiT') + if StyleMMDiT is not None: + StyleMMDiT.init_guides(model) + LG.ADAIN_NOISE_MODE = StyleMMDiT.noise_mode + + if EO("mycoshock"): + StyleMMDiT.Retrojector = model.inner_model.inner_model.diffusion_model.Retrojector + image_initial_shock = StyleMMDiT.apply_data_shock(image_initial.to(x)) + if VE_MODEL: + x = image_initial_shock.to(x) + sigmas[0] * noise_initial.to(x) + else: + x = (1 - sigmas[0]) * image_initial_shock.to(x) + sigmas[0] * noise_initial.to(x) + + RK.update_transformer_options({"model_sampling": model.inner_model.inner_model.model_sampling}) + # BEGIN SAMPLING LOOP + + while step < num_steps: + sigma, sigma_next = sigmas[step], sigmas[step+1] + if sigma_next > sigma: + step_sched = torch.where(torch.flip(sigmas, dims=[0]) == sigma)[0][0].item() + else: + step_sched = step + + SYNC_GUIDE_ACTIVE = LG.guide_mode.startswith("sync") and (LG.lgw[step_sched] != 0 or LG.lgw_inv[step_sched] != 0 or LG.lgw_sync[step_sched] != 0 or LG.lgw_sync_inv[step_sched] != 0) + + if StyleMMDiT is not None: + RK.update_transformer_options({'StyleMMDiT': StyleMMDiT}) + else: + if LG.HAS_LATENT_GUIDE_ADAIN: + if LG.lgw_adain[step_sched] == 0.0: + RK.update_transformer_options({'y0_adain': None}) + RK.update_transformer_options({'blocks_adain': {}}) + RK.update_transformer_options({'sort_and_scatter': {}}) + else: + RK.update_transformer_options({'y0_adain': LG.y0_adain.clone()}) + if 'blocks_adain_mmdit' in guides: + blocks_adain = { + "double_weights": [val * LG.lgw_adain[step_sched] for val in guides['blocks_adain_mmdit']['double_weights']], + "single_weights": [val * LG.lgw_adain[step_sched] for val in guides['blocks_adain_mmdit']['single_weights']], + "double_blocks" : guides['blocks_adain_mmdit']['double_blocks'], + "single_blocks" : guides['blocks_adain_mmdit']['single_blocks'], + } + RK.update_transformer_options({'blocks_adain': blocks_adain}) + RK.update_transformer_options({'sort_and_scatter': guides['sort_and_scatter']}) + RK.update_transformer_options({'noise_mode_adain': guides['sort_and_scatter']['noise_mode']}) + + + if LG.HAS_LATENT_GUIDE_ATTNINJ: + if LG.lgw_attninj[step_sched] == 0.0: + RK.update_transformer_options({'y0_attninj': None}) + RK.update_transformer_options({'blocks_attninj' : {}}) + RK.update_transformer_options({'blocks_attninj_qkv': {}}) + else: + RK.update_transformer_options({'y0_attninj': LG.y0_attninj.clone()}) + if 'blocks_attninj_mmdit' in guides: + blocks_attninj = { + "double_weights": [val * LG.lgw_attninj[step_sched] for val in guides['blocks_attninj_mmdit']['double_weights']], + "single_weights": [val * LG.lgw_attninj[step_sched] for val in guides['blocks_attninj_mmdit']['single_weights']], + "double_blocks" : guides['blocks_attninj_mmdit']['double_blocks'], + "single_blocks" : guides['blocks_attninj_mmdit']['single_blocks'], + } + RK.update_transformer_options({'blocks_attninj' : blocks_attninj}) + RK.update_transformer_options({'blocks_attninj_qkv': guides['blocks_attninj_qkv']}) + + if LG.HAS_LATENT_GUIDE_STYLE_POS: + if LG.lgw_style_pos[step_sched] == 0.0: + RK.update_transformer_options({'y0_style_pos': None}) + RK.update_transformer_options({'y0_style_pos_weight': 0.0}) + RK.update_transformer_options({'y0_style_pos_synweight': 0.0}) + RK.update_transformer_options({'y0_style_pos_mask': None}) + else: + RK.update_transformer_options({'y0_style_pos': LG.y0_style_pos.clone()}) + RK.update_transformer_options({'y0_style_pos_weight': LG.lgw_style_pos[step_sched]}) + RK.update_transformer_options({'y0_style_pos_synweight': guides['synweight_style_pos']}) + RK.update_transformer_options({'y0_style_pos_mask': LG.mask_style_pos}) + RK.update_transformer_options({'y0_style_pos_mask_edge': guides.get('mask_edge_style_pos')}) + RK.update_transformer_options({'y0_style_method': guides['style_method']}) + RK.update_transformer_options({'y0_style_tile_height': guides.get('style_tile_height')}) + RK.update_transformer_options({'y0_style_tile_width': guides.get('style_tile_width')}) + RK.update_transformer_options({'y0_style_tile_padding': guides.get('style_tile_padding')}) + + if EO("style_edge_width"): + RK.update_transformer + + #if LG.HAS_LATENT_GUIDE: + # y0_cache = LG.y0.clone().cpu() + # RK.update_transformer_options({'y0_standard_guide': LG.y0}) + + if LG.HAS_LATENT_GUIDE_INV and y0_inv_standard_guide is None: + y0_inv_cache = LG.y0_inv.clone().cpu() + RK.update_transformer_options({'y0_inv_standard_guide': LG.y0_inv}) + + + if LG.HAS_LATENT_GUIDE_STYLE_NEG: + if LG.lgw_style_neg[step_sched] == 0.0: + RK.update_transformer_options({'y0_style_neg': None}) + RK.update_transformer_options({'y0_style_neg_weight': 0.0}) + RK.update_transformer_options({'y0_style_neg_synweight': 0.0}) + RK.update_transformer_options({'y0_style_neg_mask': None}) + else: + RK.update_transformer_options({'y0_style_neg': LG.y0_style_neg.clone()}) + RK.update_transformer_options({'y0_style_neg_weight': LG.lgw_style_neg[step_sched]}) + RK.update_transformer_options({'y0_style_neg_synweight': guides['synweight_style_neg']}) + RK.update_transformer_options({'y0_style_neg_mask': LG.mask_style_neg}) + RK.update_transformer_options({'y0_style_neg_mask_edge': guides.get('mask_edge_style_neg')}) + RK.update_transformer_options({'y0_style_method': guides['style_method']}) + RK.update_transformer_options({'y0_style_tile_height': guides.get('style_tile_height')}) + RK.update_transformer_options({'y0_style_tile_width': guides.get('style_tile_width')}) + RK.update_transformer_options({'y0_style_tile_padding': guides.get('style_tile_padding')}) + + if AttnMask_neg is not None: + RK.update_transformer_options({'regional_conditioning_weight_neg': RegParam_neg.weights[step_sched]}) + RK.update_transformer_options({'regional_conditioning_floor_neg': RegParam_neg.floors[step_sched]}) + + if AttnMask is not None: + RK.update_transformer_options({'regional_conditioning_weight': RegParam.weights[step_sched]}) + RK.update_transformer_options({'regional_conditioning_floor': RegParam.floors[step_sched]}) + + elif regional_conditioning_weights is not None: + RK.extra_args['model_options']['transformer_options']['regional_conditioning_weight'] = regional_conditioning_weights[step_sched] + RK.extra_args['model_options']['transformer_options']['regional_conditioning_floor'] = regional_conditioning_floors [step_sched] + + epsilon_scale = float(epsilon_scales [step_sched]) if epsilon_scales is not None else None + eta = etas [step_sched].to(x) if etas is not None else eta + eta_substep = etas_substep [step_sched].to(x) if etas_substep is not None else eta_substep + s_noise = s_noises [step_sched].to(x) if s_noises is not None else s_noise + s_noise_substep = s_noises_substep [step_sched].to(x) if s_noises_substep is not None else s_noise_substep + noise_scaling_eta = noise_scaling_etas [step_sched].to(x) if noise_scaling_etas is not None else noise_scaling_eta + noise_scaling_weight = noise_scaling_weights[step_sched].to(x) if noise_scaling_weights is not None else noise_scaling_weight + + NS.set_sde_step(sigma, sigma_next, eta, overshoot, s_noise) + RK.set_coeff(rk_type, NS.h, c1, c2, c3, step, sigmas, NS.sigma_down) + NS.set_substep_list(RK) + + if (noise_scaling_eta > 0 or noise_scaling_weight != 0) and noise_scaling_type != "model_d": + if noise_scaling_type == "model_alpha": + VP_OVERRIDE=True + else: + VP_OVERRIDE=None + if noise_scaling_type in {"sampler", "model", "model_alpha"}: + if noise_scaling_type == "model_alpha": + sigma_divisor = NS.sigma_max + else: + sigma_divisor = 1.0 + + if RK.multistep_stages > 0: # hardcoded s_[1] for multistep samplers, which are never multistage + lying_su, lying_sigma, lying_sd, lying_alpha_ratio = NS.get_sde_step(NS.s_[1]/sigma_divisor, NS.s_[0]/sigma_divisor, noise_scaling_eta, noise_scaling_mode, VP_OVERRIDE=VP_OVERRIDE) + + else: + lying_su, lying_sigma, lying_sd, lying_alpha_ratio = NS.get_sde_step(sigma/sigma_divisor, NS.sigma_down/sigma_divisor, noise_scaling_eta, noise_scaling_mode, VP_OVERRIDE=VP_OVERRIDE) + for _ in range(noise_scaling_cycles-1): + lying_su, lying_sigma, lying_sd, lying_alpha_ratio = NS.get_sde_step(sigma/sigma_divisor, lying_sd/sigma_divisor, noise_scaling_eta, noise_scaling_mode, VP_OVERRIDE=VP_OVERRIDE) + lying_s_ = NS.get_substep_list(RK, sigma, RK.h_fn(lying_sd, lying_sigma)) + lying_s_ = NS.s_ + noise_scaling_weight * (lying_s_ - NS.s_) + else: + lying_s_ = NS.s_.clone() + + + rk_swap_stages = 3 if rk_swap_type != "" else 0 + data_prev_len = len(data_prev_)-1 if data_prev_ is not None else 3 + recycled_stages = max(rk_swap_stages, RK.multistep_stages, RK.hybrid_stages, data_prev_len) + + if INIT_SAMPLE_LOOP: + INIT_SAMPLE_LOOP = False + x_, data_, eps_, eps_prev_ = (torch.zeros(RK.rows+2, *x.shape, dtype=default_dtype, device=work_device) for _ in range(4)) + if LG.ADAIN_NOISE_MODE == "smart": + z_ = torch.zeros(RK.rows+2, *x.shape, dtype=default_dtype, device=work_device) + z_[0] = noise_initial.clone() + RK.update_transformer_options({'z_' : z_}) + + if sampler_mode in {"unsample", "resample"}: + data_prev_ = state_info.get('data_prev_') + if data_prev_ is not None: + if x.shape == state_info['raw_x'].shape: + data_prev_ = state_info['data_prev_'].clone().to(dtype=default_dtype, device=work_device) + else: + data_prev_ = torch.stack([comfy.utils.bislerp(data_prev_item, x.shape[-1], x.shape[-2]) for data_prev_item in state_info['data_prev_']]) + data_prev_ = data_prev_.to(x) + else: + data_prev_ = torch.zeros(4, *x.shape, dtype=default_dtype, device=work_device) # multistep max is 4m... so 4 needed + else: + data_prev_ = torch.zeros(4, *x.shape, dtype=default_dtype, device=work_device) # multistep max is 4m... so 4 needed + + recycled_stages = len(data_prev_)-1 + + if RK.rows+2 > x_.shape[0]: + row_gap = RK.rows+2 - x_.shape[0] + x_gap_, data_gap_, eps_gap_, eps_prev_gap_ = (torch.zeros(row_gap, *x.shape, dtype=default_dtype, device=work_device) for _ in range(4)) + x_ = torch.cat((x_ ,x_gap_) , dim=0) + data_ = torch.cat((data_ ,data_gap_) , dim=0) + eps_ = torch.cat((eps_ ,eps_gap_) , dim=0) + eps_prev_ = torch.cat((eps_prev_,eps_prev_gap_), dim=0) + + if LG.ADAIN_NOISE_MODE == "smart": + z_gap_ = torch.zeros(row_gap, *x.shape, dtype=default_dtype, device=work_device) + z_ = torch.cat((z_ ,z_gap_) , dim=0) + RK.update_transformer_options({'z_' : z_}) + + sde_noise_t = None + if SDE_NOISE_EXTERNAL: + if step >= len(sde_noise): + SDE_NOISE_EXTERNAL=False + else: + sde_noise_t = sde_noise[step] + + x_[0] = x.clone() + # PRENOISE METHOD HERE! + x_0 = x_[0].clone() + if EO("guide_step_cutoff") or EO("guide_step_min"): + x_0_orig = x_0.clone() + + # RECYCLE STAGES FOR MULTISTEP + if RK.multistep_stages > 0 or RK.hybrid_stages > 0: + if SYNC_GUIDE_ACTIVE: + lgw_mask_, lgw_mask_inv_ = LG.get_masks_for_step(step) + lgw_mask_sync_, lgw_mask_sync_inv_ = LG.get_masks_for_step(step, lgw_type="sync") + + weight_mask = lgw_mask_+lgw_mask_inv_ + if LG.SYNC_SEPARATE: + sync_mask = lgw_mask_sync_+lgw_mask_sync_inv_ + else: + sync_mask = 1. + + if VE_MODEL: + yt_0 = y0_bongflow + sigma * noise_bongflow + else: + yt_0 = (1-sigma) * y0_bongflow + sigma * noise_bongflow + for ms in range(min(len(data_prev_), len(eps_))): + eps_x = RK.get_epsilon_anchored(x_0, data_prev_x_[ms], sigma) + eps_y = RK.get_epsilon_anchored(yt_0, data_prev_y_[ms], sigma) + eps_x2y = RK.get_epsilon_anchored(yt_0, data_prev_y_[ms], sigma) + + if RK.EXPONENTIAL: + if VE_MODEL: + eps_[ms] = sync_mask * eps_x + (1-sync_mask) * eps_x2y + weight_mask * (-eps_y + sigma*(-noise_bongflow)) + if EO("sync_x2y"): + eps_[ms] = sync_mask * eps_x + (1-sync_mask) * eps_x2y + weight_mask * (-eps_x2y + sigma*(-noise_bongflow)) + else: + eps_[ms] = sync_mask * eps_x + (1-sync_mask) * eps_x2y + weight_mask * (-eps_y + sigma*(y0_bongflow-noise_bongflow)) + if EO("sync_x2y"): + eps_[ms] = sync_mask * eps_x + (1-sync_mask) * eps_x2y + weight_mask * (-eps_x2y + sigma*(y0_bongflow-noise_bongflow)) + else: + if VE_MODEL: + eps_[ms] = sync_mask * eps_x + (1-sync_mask) * eps_x2y + weight_mask * (-eps_y + (noise_bongflow)) + if EO("sync_x2y"): + eps_[ms] = sync_mask * eps_x + (1-sync_mask) * eps_x2y + weight_mask * (-eps_x2y + (noise_bongflow)) + else: + eps_[ms] = sync_mask * eps_x + (1-sync_mask) * eps_x2y + weight_mask * (-eps_y + (noise_bongflow-y0_bongflow)) + if EO("sync_x2y"): + eps_[ms] = sync_mask * eps_x + (1-sync_mask) * eps_x2y + weight_mask * (-eps_x2y + (noise_bongflow-y0_bongflow)) + + #if RK.EXPONENTIAL: + # if VE_MODEL: + # eps_[ms] = sync_mask * weight_mask_inv * (eps_x - weight_mask * eps_y) + weight_mask * sigma*(-noise_bongflow) + # else: + # #eps_[ms] = (lgw_mask_sync_+lgw_mask_sync_inv_) * (1-(lgw_mask_+lgw_mask_inv_)) * (eps_x - (lgw_mask_+lgw_mask_inv_) * eps_y) + (lgw_mask_+lgw_mask_inv_) * sigma*(y0_bongflow-noise_bongflow) + # eps_[ms] = sync_mask * weight_mask_inv * (eps_x - weight_mask * eps_y) + weight_mask * sigma*(y0_bongflow-noise_bongflow) + #else: + # if VE_MODEL: + # eps_[ms] = sync_mask * weight_mask_inv * (eps_x - weight_mask * eps_y) + weight_mask * (noise_bongflow) + # else: + # #eps_[ms] = (lgw_mask_sync_+lgw_mask_sync_inv_) * (1-(lgw_mask_+lgw_mask_inv_)) * (eps_x - (lgw_mask_+lgw_mask_inv_) * eps_y) + (lgw_mask_+lgw_mask_inv_) * (noise_bongflow-y0_bongflow) + # eps_[ms] = sync_mask * weight_mask_inv * (eps_x - weight_mask * eps_y) + weight_mask * (noise_bongflow-y0_bongflow) + eps_prev_ = eps_.clone() + + else: + for ms in range(min(len(data_prev_), len(eps_))): + eps_[ms] = RK.get_epsilon_anchored(x_0, data_prev_[ms], sigma) + eps_prev_ = eps_.clone() + + + + # INITIALIZE IMPLICIT SAMPLING + if RK.IMPLICIT: + x_, eps_, data_ = init_implicit_sampling(RK, x_0, x_, eps_, eps_prev_, data_, eps, denoised, denoised_prev2, step, sigmas, NS.h, NS.s_, EO, SYNC_GUIDE_ACTIVE) + + implicit_steps_total = (implicit_steps_full + 1) * (implicit_steps_diag + 1) + + # BEGIN FULLY IMPLICIT LOOP + cossim_counter = 0 + adaptive_lgw = LG.lgw.clone() + full_iter = 0 + while full_iter < implicit_steps_full+1: + + if RK.IMPLICIT: + x_, eps_ = RK.newton_iter(x_0, x_, eps_, eps_prev_, data_, NS.s_, 0, NS.h, sigmas, step, "init", SYNC_GUIDE_ACTIVE) + + # PREPARE FULLY PSEUDOIMPLICIT GUIDES + if step > 0 or not SKIP_PSEUDO: + if full_iter > 0 and EO("fully_implicit_reupdate_x"): + x_[0] = NS.sigma_from_to(x_0, x, sigma, sigma_next, NS.s_[0]) + x_0 = NS.sigma_from_to(x_0, x, sigma, sigma_next, sigma) + + if EO("fully_pseudo_init") and full_iter == 0: + guide_mode_tmp = LG.guide_mode + LG.guide_mode = "fully_" + LG.guide_mode + x_0, x_, eps_ = LG.prepare_fully_pseudoimplicit_guides_substep(x_0, x_, eps_, eps_prev_, data_, denoised_prev, 0, step, step_sched, sigmas, eta_substep, overshoot_substep, s_noise_substep, \ + NS, RK, pseudoimplicit_row_weights, pseudoimplicit_step_weights, full_iter, BONGMATH) + if EO("fully_pseudo_init") and full_iter == 0: + LG.guide_mode = guide_mode_tmp + + # TABLEAU LOOP + for row in range(RK.rows - RK.multistep_stages - RK.row_offset + 1): + diag_iter = 0 + while diag_iter < implicit_steps_diag+1: + + + if noise_sampler_type_substep == "brownian" and (full_iter > 0 or diag_iter > 0): + eta_substep = 0. + + NS.set_sde_substep(row, RK.multistep_stages, eta_substep, overshoot_substep, s_noise_substep, full_iter, diag_iter, implicit_steps_full, implicit_steps_diag) + + # PRENOISE METHOD HERE! + + # A-TABLEAU + if row < RK.rows: + + # PREPARE PSEUDOIMPLICIT GUIDES + if step > 0 or not SKIP_PSEUDO: + x_0, x_, eps_, x_row_pseudoimplicit, sub_sigma_pseudoimplicit = LG.process_pseudoimplicit_guides_substep(x_0, x_, eps_, eps_prev_, data_, denoised_prev, row, step, step_sched, sigmas, NS, RK, \ + pseudoimplicit_row_weights, pseudoimplicit_step_weights, full_iter, BONGMATH) + + # PREPARE MODEL CALL + if LG.guide_mode in GUIDE_MODE_NAMES_PSEUDOIMPLICIT and (step > 0 or not SKIP_PSEUDO) and (LG.lgw[step_sched] > 0 or LG.lgw_inv[step_sched] > 0) and x_row_pseudoimplicit is not None: + + x_tmp = x_row_pseudoimplicit + s_tmp = sub_sigma_pseudoimplicit + + # Fully implicit iteration (explicit only) # or... Fully implicit iteration (implicit only... not standard) + elif (full_iter > 0 and RK.row_offset == 1 and row == 0) or (full_iter > 0 and RK.row_offset == 0 and row == 0 and EO("fully_implicit_update_x")): + if EO("fully_explicit_pogostick_eta"): + super_alpha_ratio, super_sigma_down, super_sigma_up = NS.get_sde_coeff(sigma, sigma_next, None, eta) + x = super_alpha_ratio * x + super_sigma_up * NS.noise_sampler(sigma=sigma_next, sigma_next=sigma) + + x_tmp = x + s_tmp = sigma + elif EO("enable_fully_explicit_lagrange_rebound1"): + substeps_prev = len(RK.C[:-1]) + x_tmp = lagrange_interpolation(RK.C[1:-1], x_[1:substeps_prev], RK.C[0]).squeeze(0) + + elif EO("enable_fully_explicit_lagrange_rebound2"): + substeps_prev = len(RK.C[:-1]) + x_tmp = lagrange_interpolation(RK.C[1:], x_[1:substeps_prev+1], RK.C[0]).squeeze(0) + + elif EO("enable_fully_explicit_rebound1"): # 17630, faded dots, just crap + eps_tmp, denoised_tmp = RK(x, sigma_next, x, sigma_next) + eps_tmp = (x - denoised_tmp) / sigma_next + x_[0] = denoised_tmp + sigma * eps_tmp + + x_0 = x_[0] + x_tmp = x_[0] + s_tmp = sigma + + elif implicit_type == "rebound": # TODO: ADAPT REBOUND IMPLICIT TO WORK WITH FLOW GUIDE MODE + eps_tmp, denoised_tmp = RK(x, sigma_next, x_0, sigma) + eps_tmp = (x - denoised_tmp) / sigma_next + x = denoised_tmp + sigma * eps_tmp + + x_tmp = x + s_tmp = sigma + + elif implicit_type == "retro-eta" and (NS.sub_sigma_up > 0 or NS.sub_sigma_up_eta > 0): + x_tmp = NS.sigma_from_to(x_0, x, sigma, sigma_next, sigma) + s_tmp = sigma + + elif implicit_type == "bongmath" and (NS.sub_sigma_up > 0 or NS.sub_sigma_up_eta > 0): + if BONGMATH: + x_tmp = x_[row] + s_tmp = NS.s_[row] + else: + x_tmp = NS.sigma_from_to(x_0, x, sigma, sigma_next, sigma) + s_tmp = sigma + + else: + x_tmp = x + s_tmp = sigma_next + + + + # All others + else: + # three potential toggle options: force rebound/model call, force PC style, force pogostick style + if diag_iter > 0: # Diagonally implicit iteration (explicit or implicit) + if EO("diag_explicit_pogostick_eta"): + super_alpha_ratio, super_sigma_down, super_sigma_up = NS.get_sde_coeff(NS.s_[row], NS.s_[row+RK.row_offset+RK.multistep_stages], None, eta) + x_[row+RK.row_offset] = super_alpha_ratio * x_[row+RK.row_offset] + super_sigma_up * NS.noise_sampler(sigma=NS.s_[row+RK.row_offset+RK.multistep_stages], sigma_next=NS.s_[row]) + + x_tmp = x_[row+RK.row_offset] + s_tmp = sigma + + elif implicit_type_substeps == "rebound": + eps_[row], data_[row] = RK(x_[row+RK.row_offset], NS.s_[row+RK.row_offset+RK.multistep_stages], x_0, sigma) + + x_ = RK.update_substep(x_0, x_, eps_, eps_prev_, row, RK.row_offset, NS.h_new, NS.h_new_orig) + x_[row+RK.row_offset] = NS.rebound_overshoot_substep(x_0, x_[row+RK.row_offset]) + + x_[row+RK.row_offset] = NS.sigma_from_to(x_0, x_[row+RK.row_offset], sigma, NS.s_[row+RK.row_offset+RK.multistep_stages], NS.s_[row]) + x_tmp = x_[row+RK.row_offset] + s_tmp = NS.s_[row] + + elif implicit_type_substeps == "retro-eta" and (NS.sub_sigma_up > 0 or NS.sub_sigma_up_eta > 0): + x_tmp = NS.sigma_from_to(x_0, x_[row+RK.row_offset], sigma, NS.s_[row+RK.row_offset+RK.multistep_stages], NS.s_[row]) + s_tmp = NS.s_[row] + + elif implicit_type_substeps == "bongmath" and (NS.sub_sigma_up > 0 or NS.sub_sigma_up_eta > 0) and not EO("disable_diag_explicit_bongmath_rebound"): + if BONGMATH: + x_tmp = x_[row] + s_tmp = NS.s_[row] + else: + x_tmp = NS.sigma_from_to(x_0, x_[row+RK.row_offset], sigma, NS.s_[row+RK.row_offset+RK.multistep_stages], NS.s_[row]) + s_tmp = NS.s_[row] + + else: + x_tmp = x_[row+RK.row_offset] + s_tmp = NS.s_[row+RK.row_offset+RK.multistep_stages] + else: + x_tmp = x_[row] + s_tmp = NS.sub_sigma + + + + if RK.IMPLICIT: + if not EO("disable_implicit_guide_preproc"): + eps_, x_ = LG.process_guides_substep(x_0, x_, eps_, data_, row, step_sched, sigma, sigma_next, NS.sigma_down, NS.s_, epsilon_scale, RK) + eps_prev_, x_ = LG.process_guides_substep(x_0, x_, eps_prev_, data_, row, step_sched, sigma, sigma_next, NS.sigma_down, NS.s_, epsilon_scale, RK) + if row == 0 and (EO("implicit_lagrange_init") or EO("radaucycle")): + pass + else: + x_[row+RK.row_offset] = x_0 + NS.h_new * RK.zum(row+RK.row_offset, eps_, eps_prev_) + x_[row+RK.row_offset] = NS.rebound_overshoot_substep(x_0, x_[row+RK.row_offset]) + if row > 0: + if not LG.guide_mode.startswith("flow") or (LG.lgw[step_sched] == 0 and LG.lgw[step+1] == 0 and LG.lgw_inv[step_sched] == 0 and LG.lgw_inv[step+1] == 0): + x_row_tmp = NS.swap_noise_substep(x_0, x_[row+RK.row_offset], mask=sde_mask, guide=LG.y0) + + if LG.ADAIN_NOISE_MODE == "smart": #_smartnoise_implicit"): + data_next = denoised + NS.h_new * RK.zum(row+RK.row_offset+RK.multistep_stages, data_, data_prev_) + if VE_MODEL: + z_[row+RK.row_offset] = (x_row_tmp - data_next) / s_tmp + else: + z_[row+RK.row_offset] = (x_row_tmp - (NS.sigma_max-s_tmp)*data_next) / s_tmp + RK.update_transformer_options({'z_' : z_}) + + if SYNC_GUIDE_ACTIVE: + noise_bongflow_new = (x_row_tmp - x_[row+RK.row_offset]) / s_tmp + noise_bongflow + yt_[row+RK.row_offset] += s_tmp * (noise_bongflow_new - noise_bongflow) + x_0 += sigma * (noise_bongflow_new - noise_bongflow) + if not EO("disable_i_bong"): + for i_bong in range(len(NS.s_)): + x_[i_bong] += NS.s_[i_bong] * (noise_bongflow_new - noise_bongflow) + noise_bongflow = noise_bongflow_new + + x_[row+RK.row_offset] = x_row_tmp + + if SYNC_GUIDE_ACTIVE: + if VE_MODEL: + yt_[:NS.s_.shape[0], 0] = y0_bongflow + NS.s_.view(-1, *[1]*(x.ndim-1)) * (noise_bongflow) + yt_0 = y0_bongflow + sigma * (noise_bongflow) + else: + yt_[:NS.s_.shape[0], 0] = y0_bongflow + NS.s_.view(-1, *[1]*(x.ndim-1)) * (noise_bongflow - y0_bongflow) + yt_0 = y0_bongflow + sigma * (noise_bongflow - y0_bongflow) + + if RK.EXPONENTIAL: + eps_y_ = data_y_ - yt_0 # yt_ # watch out for fuckery with size of tableau being smaller later in a chained sampler + else: + if BONGMATH: + eps_y_[:NS.s_.shape[0]] = (yt_[:NS.s_.shape[0]] - data_y_[:NS.s_.shape[0]]) / NS.s_.view(-1,*[1]*(x_.ndim-1)) + else: + eps_y_[:NS.s_.shape[0]] = (yt_0.repeat(NS.s_.shape[0], *[1]*(x_.ndim-1)) - data_y_[:NS.s_.shape[0]]) / sigma # calc exact to c0 node + if not BONGMATH: + if RK.EXPONENTIAL: + eps_x_ = data_x_ - x_0 + else: + eps_x_ = (x_0 - data_x_) / sigma + + weight_mask = lgw_mask_+lgw_mask_inv_ + if LG.SYNC_SEPARATE: + sync_mask = lgw_mask_sync_+lgw_mask_sync_inv_ + else: + sync_mask = 1. + + for ms in range(len(eps_)): + if RK.EXPONENTIAL: + if VE_MODEL: # ZERO IS THIS # ONE IS THIS + eps_[ms] = sync_mask * eps_x_[ms] + (1-sync_mask) * eps_x2y_[ms] + weight_mask * (-eps_y_[ms] + sigma*(-noise_bongflow)) + if EO("sync_x2y"): + eps_[ms] = sync_mask * eps_x_[ms] + (1-sync_mask) * eps_x2y_[ms] + weight_mask * (-eps_x2y_[ms] + sigma*(-noise_bongflow)) + else: + eps_[ms] = sync_mask * eps_x_[ms] + (1-sync_mask) * eps_x2y_[ms] + weight_mask * (-eps_y_[ms] + sigma*(y0_bongflow-noise_bongflow)) + if EO("sync_x2y"): + eps_[ms] = sync_mask * eps_x_[ms] + (1-sync_mask) * eps_x2y_[ms] + weight_mask * (-eps_x2y_[ms] + sigma*(y0_bongflow-noise_bongflow)) + else: + if VE_MODEL: + eps_[ms] = sync_mask * eps_x_[ms] + (1-sync_mask) * eps_x2y_[ms] + weight_mask * (-eps_y_[ms] + (noise_bongflow)) + if EO("sync_x2y"): + eps_[ms] = sync_mask * eps_x_[ms] + (1-sync_mask) * eps_x2y_[ms] + weight_mask * (-eps_x2y_[ms] + (noise_bongflow)) + else: + eps_[ms] = sync_mask * eps_x_[ms] + (1-sync_mask) * eps_x2y_[ms] + weight_mask * (-eps_y_[ms] + (noise_bongflow-y0_bongflow)) + if EO("sync_x2y"): + eps_[ms] = sync_mask * eps_x_[ms] + (1-sync_mask) * eps_x2y_[ms] + weight_mask * (-eps_x2y_[ms] + (noise_bongflow-y0_bongflow)) + + + if BONGMATH and step < sigmas.shape[0]-1 and sigma > 0.03 and not EO("disable_implicit_prebong"): + BONGMATH_Y = SYNC_GUIDE_ACTIVE + + x_0, x_, eps_ = RK.bong_iter(x_0, x_, eps_, eps_prev_, data_, sigma, NS.s_, row, RK.row_offset, NS.h, step, step_sched, + BONGMATH_Y, y0_bongflow, noise_bongflow, eps_x_, eps_y_, data_x_, data_y_, LG) # TRY WITH h_new ?? + # BONGMATH_Y, y0_bongflow, noise_bongflow, eps_x_, eps_y_, eps_x2y_, data_x_, LG) # TRY WITH h_new ?? + + #if EO("eps_adain_smartnoise_bongmath"): + if LG.ADAIN_NOISE_MODE == "smart": + if VE_MODEL: + z_[:NS.s_.shape[0], ...] = (x_ - data_)[:NS.s_.shape[0], ...] / NS.s_.view(-1,*[1]*(x_.ndim-1)) + else: + z_[:NS.s_.shape[0], ...] = (x_[:NS.s_.shape[0], ...] - (NS.sigma_max - NS.s_.view(-1,*[1]*(x_.ndim-1)))*data_[:NS.s_.shape[0], ...])[:NS.s_.shape[0], ...] / NS.s_.view(-1,*[1]*(x_.ndim-1)) + RK.update_transformer_options({'z_' : z_}) + + x_tmp = x_[row+RK.row_offset] + + lying_eps_row_factor = 1.0 + # MODEL CALL MODEL CALL MODEL CALL MODEL CALL MODEL CALL MODEL CALL MODEL CALL MODEL CALL MODEL CALL MODEL CALL MODEL CALL MODEL CALL MODEL CALL MODEL CALL MODEL CALL MODEL CALL MODEL CALL MODEL CALL + if RK.IMPLICIT and row == 0 and (EO("implicit_lazy_recycle_first_model_call_at_start") or EO("radaucycle") or RK.C[0] == 0.0): + pass + else: + if s_tmp == 0: + break + x_, eps_ = RK.newton_iter(x_0, x_, eps_, eps_prev_, data_, NS.s_, row, NS.h, sigmas, step, "pre", SYNC_GUIDE_ACTIVE) # will this do anything? not x_tmp + + # DETAIL BOOST + if noise_scaling_type == "model_alpha" and noise_scaling_weight != 0 and noise_scaling_eta > 0: + s_tmp = s_tmp + noise_scaling_weight * (s_tmp * lying_alpha_ratio - s_tmp) + if noise_scaling_type == "model" and noise_scaling_weight != 0 and noise_scaling_eta > 0: + s_tmp = lying_s_[row] + if RK.multistep_stages > 0: + s_tmp = lying_sd + + # SYNC GUIDE --------------------------- + if LG.guide_mode.startswith("sync") and (LG.lgw[step_sched] == 0 and LG.lgw_inv[step_sched] == 0 and LG.lgw_sync[step_sched] == 0 and LG.lgw_sync_inv[step_sched] == 0): + data_cached = None + elif SYNC_GUIDE_ACTIVE: + lgw_mask_, lgw_mask_inv_ = LG.get_masks_for_step(step_sched) + lgw_mask_sync_, lgw_mask_sync_inv_ = LG.get_masks_for_step(step_sched, lgw_type="sync") + lgw_mask_drift_x_, lgw_mask_drift_x_inv_ = LG.get_masks_for_step(step_sched, lgw_type="drift_x") + lgw_mask_drift_y_, lgw_mask_drift_y_inv_ = LG.get_masks_for_step(step_sched, lgw_type="drift_y") + lgw_mask_lure_x_, lgw_mask_lure_x_inv_ = LG.get_masks_for_step(step_sched, lgw_type="lure_x") + lgw_mask_lure_y_, lgw_mask_lure_y_inv_ = LG.get_masks_for_step(step_sched, lgw_type="lure_y") + + weight_mask = lgw_mask_ + lgw_mask_inv_ + sync_mask = lgw_mask_sync_ + lgw_mask_sync_inv_ + + + drift_x_mask = lgw_mask_drift_x_ + lgw_mask_drift_x_inv_ + drift_y_mask = lgw_mask_drift_y_ + lgw_mask_drift_y_inv_ + lure_x_mask = lgw_mask_lure_x_ + lgw_mask_lure_x_inv_ + lure_y_mask = lgw_mask_lure_y_ + lgw_mask_lure_y_inv_ + + if eps_x_ is None: + eps_x_ = torch.zeros(RK.rows+2, *x.shape, dtype=default_dtype, device=work_device) + data_x_ = torch.zeros(RK.rows+2, *x.shape, dtype=default_dtype, device=work_device) + eps_y2x_ = torch.zeros(RK.rows+2, *x.shape, dtype=default_dtype, device=work_device) + eps_x2y_ = torch.zeros(RK.rows+2, *x.shape, dtype=default_dtype, device=work_device) + eps_yt_ = torch.zeros(RK.rows+2, *x.shape, dtype=default_dtype, device=work_device) + eps_y_ = torch.zeros(RK.rows+2, *x.shape, dtype=default_dtype, device=work_device) + eps_prev_y_ = torch.zeros(RK.rows+2, *x.shape, dtype=default_dtype, device=work_device) + data_y_ = torch.zeros(RK.rows+2, *x.shape, dtype=default_dtype, device=work_device) + yt_ = torch.zeros(RK.rows+2, *x.shape, dtype=default_dtype, device=work_device) + + RUN_X_0_COPY = False + if noise_bongflow is None: + RUN_X_0_COPY = True + data_prev_x_ = torch.zeros(4, *x.shape, dtype=default_dtype, device=work_device) + data_prev_y_ = torch.zeros(4, *x.shape, dtype=default_dtype, device=work_device) + + noise_bongflow = normalize_zscore(NS.noise_sampler(sigma=sigma, sigma_next=NS.sigma_min), channelwise=True, inplace=True) + + _, _ = RK(noise_bongflow, s_tmp/s_tmp, noise_bongflow, sigma/sigma, transformer_options={'latent_type': 'xt'}) + + if RK.extra_args['model_options']['transformer_options'].get('y0_standard_guide') is not None: + if hasattr(model.inner_model.inner_model.diffusion_model, "y0_standard_guide"): + LG.y0 = y0_standard_guide = model.inner_model.inner_model.diffusion_model.y0_standard_guide.clone() + del model.inner_model.inner_model.diffusion_model.y0_standard_guide + RK.extra_args['model_options']['transformer_options']['y0_standard_guide'] = None + + if RK.extra_args['model_options']['transformer_options'].get('y0_inv_standard_guide') is not None: + if hasattr(model.inner_model.inner_model.diffusion_model, "y0_inv_standard_guide"): + LG.y0_inv = y0_inv_standard_guide = model.inner_model.inner_model.diffusion_model.y0_inv_standard_guide.clone() # RK.extra_args['model_options']['transformer_options'].get('y0_standard_guide') + del model.inner_model.inner_model.diffusion_model.y0_inv_standard_guide + RK.extra_args['model_options']['transformer_options']['y0_inv_standard_guide'] = None + + y0_bongflow = LG.HAS_LATENT_GUIDE * LG.mask * LG.y0 + LG.HAS_LATENT_GUIDE_INV * LG.mask_inv * LG.y0_inv #LG.y0.clone() + + if VE_MODEL: + yt_0 = y0_bongflow + sigma * noise_bongflow + yt = y0_bongflow + s_tmp * noise_bongflow + else: + yt_0 = (1-sigma) * y0_bongflow + sigma * noise_bongflow + yt = (1-s_tmp) * y0_bongflow + s_tmp * noise_bongflow + + yt_[row] = yt + + if RUN_X_0_COPY: + x_0 = yt_0.clone() + x_tmp = x_[row] = yt.clone() + else: + y0_bongflow_orig = y0_bongflow.clone() if y0_bongflow_orig is None else y0_bongflow_orig + y0_bongflow = y0_bongflow + LG.drift_x_data * drift_x_mask * (data_x - y0_bongflow) \ + + LG.drift_x_sync * drift_x_mask * (data_barf - y0_bongflow) \ + + LG.drift_y_data * drift_y_mask * (data_y - y0_bongflow) \ + + LG.drift_y_sync * drift_y_mask * (data_barf_y - y0_bongflow) \ + + LG.drift_y_guide * drift_y_mask * (y0_bongflow_orig - y0_bongflow) + + if torch.norm(y0_bongflow_orig - y0_bongflow) != 0 and EO("enable_y0_bongflow_update"): + RK.update_transformer_options({'y0_style_pos': y0_bongflow.clone()}) + + if not EO("skip_yt"): + yt_0 = RK.get_x(y0_bongflow, noise_bongflow, sigma) + yt = RK.get_x(y0_bongflow, noise_bongflow, s_tmp) + + yt_[row] = yt + + if ((LG.lgw[step_sched].item() in {1,0} and LG.lgw_inv[step_sched].item() in {1,0} and LG.lgw[step_sched] == 1-LG.lgw_sync[step_sched] and LG.lgw_inv[step_sched] == 1-LG.lgw_sync_inv[step_sched]) or EO("sync_speed_mode")) and not EO("disable_sync_speed_mode"): + data_y = y0_bongflow.clone() + eps_y = RK.get_eps(yt_0, yt_[row], data_y, sigma, s_tmp) + + else: + eps_y, data_y = RK(yt_[row], s_tmp, yt_0, sigma, transformer_options={'latent_type': 'yt'}) + + eps_x, data_x = RK(x_tmp, s_tmp, x_0, sigma, transformer_options={'latent_type': 'xt', 'row': row, "x_tmp": x_tmp}) + #if hasattr(model.inner_model.inner_model.diffusion_model, "eps_out"): + + + for sync_lure_iter in range(LG.sync_lure_iter): + if LG.sync_lure_sequence == "x -> y": + + if lure_x_mask.abs().sum() > 0: + x_tmp = LG.swap_data(x_tmp, data_x, data_y, s_tmp, lure_x_mask) + eps_x_lure, data_x_lure = RK(x_tmp, s_tmp, x_0, sigma, transformer_options={'latent_type': 'xt'}) + eps_x = eps_x + lure_x_mask * (eps_x_lure - eps_x) + data_x = data_x + lure_x_mask * (data_x_lure - data_x) + + if lure_y_mask.abs().sum() > 0: + y_tmp = yt_[row].clone() + y_tmp = LG.swap_data(y_tmp, data_y, data_x, s_tmp, lure_y_mask) + eps_y_lure, data_y_lure = RK(y_tmp, s_tmp, yt_0, sigma, transformer_options={'latent_type': 'yt'}) + eps_y = eps_y + lure_y_mask * (eps_y_lure - eps_y) + data_y = data_y + lure_y_mask * (data_y_lure - data_y) + + elif LG.sync_lure_sequence == "y -> x": + + if lure_y_mask.abs().sum() > 0: + y_tmp = yt_[row].clone() + y_tmp = LG.swap_data(y_tmp, data_y, data_x, s_tmp, lure_y_mask) + eps_y_lure, data_y_lure = RK(y_tmp, s_tmp, yt_0, sigma, transformer_options={'latent_type': 'yt'}) + eps_y = eps_y + lure_y_mask * (eps_y_lure - eps_y) + data_y = data_y + lure_y_mask * (data_y_lure - data_y) + + if lure_x_mask.abs().sum() > 0: + x_tmp = LG.swap_data(x_tmp, data_x, data_y, s_tmp, lure_x_mask) + eps_x_lure, data_x_lure = RK(x_tmp, s_tmp, x_0, sigma, transformer_options={'latent_type': 'xt'}) + eps_x = eps_x + lure_x_mask * (eps_x_lure - eps_x) + data_x = data_x + lure_x_mask * (data_x_lure - data_x) + + elif LG.sync_lure_sequence == "xy -> xy": + data_x_orig, data_y_orig = data_x.clone(), data_y.clone() + + if lure_x_mask.abs().sum() > 0: + x_tmp = LG.swap_data(x_tmp, data_x_orig, data_y_orig, s_tmp, lure_x_mask) + eps_x_lure, data_x_lure = RK(x_tmp, s_tmp, x_0, sigma, transformer_options={'latent_type': 'xt'}) + eps_x = eps_x + lure_x_mask * (eps_x_lure - eps_x) + data_x = data_x + lure_x_mask * (data_x_lure - data_x) + + if lure_y_mask.abs().sum() > 0: + y_tmp = yt_[row].clone() + y_tmp = LG.swap_data(y_tmp, data_y_orig, data_x_orig, s_tmp, lure_y_mask) + eps_y_lure, data_y_lure = RK(y_tmp, s_tmp, yt_0, sigma, transformer_options={'latent_type': 'yt'}) + eps_y = eps_y + lure_y_mask * (eps_y_lure - eps_y) + data_y = data_y + lure_y_mask * (data_y_lure - data_y) + + if EO("sync_proj_y"): + d_collinear_d_lerp = get_collinear(eps_x, eps_y) + d_lerp_ortho_d = get_orthogonal(eps_y, eps_x) + eps_y = d_collinear_d_lerp + d_lerp_ortho_d + + if EO("sync_proj_y2"): + d_collinear_d_lerp = get_collinear(eps_y, eps_x) + d_lerp_ortho_d = get_orthogonal(eps_x, eps_y) + eps_y = d_collinear_d_lerp + d_lerp_ortho_d + + if EO("sync_proj_x"): + d_collinear_d_lerp = get_collinear(eps_y, eps_x) + d_lerp_ortho_d = get_orthogonal(eps_x, eps_y) + eps_x = d_collinear_d_lerp + d_lerp_ortho_d + + if EO("sync_proj_x2"): + d_collinear_d_lerp = get_collinear(eps_x, eps_y) + d_lerp_ortho_d = get_orthogonal(eps_y, eps_x) + eps_x = d_collinear_d_lerp + d_lerp_ortho_d + + eps_x2y = RK.get_eps(x_0, x_[row], data_y, sigma, s_tmp) + eps_x2y_[row] = eps_x2y + + eps_y2x = RK.get_eps(x_0, x_[row], data_y, sigma, s_tmp) + eps_y2x_[row] = eps_y2x + + if RK.EXPONENTIAL: + if VE_MODEL: # ZERO IS THIS # ONE IS THIS + eps_[row] = sync_mask * eps_x + (1-sync_mask) * eps_x2y + weight_mask * (-eps_y + sigma*(-noise_bongflow)) + if EO("sync_x2y"): + eps_[row] = sync_mask * eps_x + (1-sync_mask) * eps_x2y + weight_mask * (-eps_x2y + sigma*(-noise_bongflow)) + else: + eps_[row] = sync_mask * eps_x + (1-sync_mask) * eps_x2y + weight_mask * (-eps_y + sigma*(y0_bongflow-noise_bongflow)) #+ lure_x_mask * sigma*(data_y - data_x) + if EO("sync_x2y"): + eps_[row] = sync_mask * eps_x - (1-sync_mask) * eps_x2y + weight_mask * (-eps_x2y + sigma*(y0_bongflow-noise_bongflow)) + eps_yt_[row] = sync_mask * eps_y + (1-sync_mask) * eps_y2x + weight_mask * (-eps_x + sigma*(y0_bongflow-noise_bongflow)) # differentiate guide as well toward the x pred? + else: + if VE_MODEL: + eps_[row] = sync_mask * eps_x + (1-sync_mask) * eps_x2y + weight_mask * (noise_bongflow - eps_y) + if EO("sync_x2y"): + eps_[row] = sync_mask * eps_x + (1-sync_mask) * eps_x2y + weight_mask * (noise_bongflow - eps_x2y) + else: + eps_[row] = sync_mask * eps_x + (1-sync_mask) * eps_x2y + weight_mask * (noise_bongflow - eps_y - y0_bongflow) + if EO("sync_x2y"): + eps_[row] = sync_mask * eps_x + (1-sync_mask) * eps_x2y + weight_mask * (noise_bongflow - eps_x2y - y0_bongflow) + eps_yt_[row] = sync_mask * eps_y + (1-sync_mask) * eps_y2x + weight_mask * (noise_bongflow - eps_x - y0_bongflow) # differentiate guide as well toward the x pred? + + if VE_MODEL: + data_[row] = x_0 + sync_mask * NS.h * eps_x + (1-sync_mask) * NS.h * eps_x2y - weight_mask * (sigma*(eps_y + noise_bongflow)) # - lure_x_mask * (sigma*(eps_y + eps_x)) + data_barf_y = yt_0 + sync_mask * NS.h * eps_y + (1-sync_mask) * NS.h * eps_y2x - weight_mask * (sigma*(eps_x + noise_bongflow)) + if EO("sync_x2y"): + data_[row] = x_0 + sync_mask * NS.h * eps_x + (1-sync_mask) * NS.h * eps_x2y - weight_mask * (sigma*(eps_x2y + noise_bongflow)) + + else: + + data_[row] = x_0 + sync_mask * NS.h * eps_x + (1-sync_mask) * NS.h * eps_x2y - weight_mask * (NS.h * eps_y + sigma*(noise_bongflow-y0_bongflow)) + data_barf_y = yt_0 + sync_mask * NS.h * eps_y + (1-sync_mask) * NS.h * eps_y2x - weight_mask * (NS.h * eps_x + sigma*(noise_bongflow-y0_bongflow)) + if EO("sync_x2y"): + data_[row] = x_0 + sync_mask * NS.h * eps_x + (1-sync_mask) * NS.h * eps_x2y - weight_mask * (NS.h * eps_x2y + sigma*(noise_bongflow-y0_bongflow)) + + if EO("data_is_y0_with_lure_x_mask"): + data_[row] = data_[row] + lure_x_mask * (y0_bongflow - data_[row]) + + if EO("eps_is_y0_with_lure_x_mask"): + if RK.EXPONENTIAL: + eps_[row] = eps_[row] + lure_x_mask * ((y0_bongflow - x_0) - eps_[row]) + else: + eps_[row] = eps_[row] + lure_x_mask * (((x_0 - y0_bongflow) / sigma) - eps_[row]) + data_barf = data_[row] + data_cached = data_x + + eps_x_ [row] = eps_x + data_x_[row] = data_x + + eps_y_ [row] = eps_y + data_y_[row] = data_y + + if EO("sync_use_fake_eps_y"): + if RK.EXPONENTIAL: + if VE_MODEL: + eps_y_ [row] = sigma * ( - noise_bongflow) + else: + eps_y_ [row] = sigma * (y0_bongflow - noise_bongflow) + else: + if VE_MODEL: + eps_y_ [row] = noise_bongflow + else: + eps_y_ [row] = noise_bongflow - y0_bongflow + if EO("sync_use_fake_data_y"): + data_y_[row] = y0_bongflow + + + + + elif LG.guide_mode.startswith("flow") and (LG.lgw[step_sched] > 0 or LG.lgw_inv[step_sched] > 0) and not FLOW_STOPPED and not EO("flow_sync") : + lgw_mask_, lgw_mask_inv_ = LG.get_masks_for_step(step) + if not FLOW_STARTED and not FLOW_RESUMED: + FLOW_STARTED = True + data_x_prev_ = torch.zeros_like(data_prev_) + + y0 = LG.HAS_LATENT_GUIDE * LG.mask * LG.y0 + LG.HAS_LATENT_GUIDE_INV * LG.mask_inv * LG.y0_inv + + yx0 = y0.clone() + + if EO("flow_slerp"): + y0_inv = LG.HAS_LATENT_GUIDE * LG.mask * LG.y0_inv + LG.HAS_LATENT_GUIDE_INV * LG.mask_inv * LG.y0 + y0 = LG.y0.clone() + y0_inv = LG.y0_inv.clone() + flow_slerp_guide_ratio = EO("flow_slerp_guide_ratio", 0.5) + y_slerp = slerp_tensor(flow_slerp_guide_ratio, y0, y0_inv) + yx0 = y_slerp.clone() + + x_[row], x_0 = yx0.clone(), yx0.clone() + if EO("guide_step_cutoff") or EO("guide_step_min"): + x_0_orig = yx0.clone() + + if EO("flow_yx0_init_y0_inv"): + yx0 = LG.HAS_LATENT_GUIDE * LG.mask * LG.y0_inv + LG.HAS_LATENT_GUIDE_INV * LG.mask_inv * LG.y0 + + if step > 0: + if EO("flow_manual_masks"): + y0 = (1 - (LG.HAS_LATENT_GUIDE * LG.lgw[step_sched] * LG.mask + LG.HAS_LATENT_GUIDE_INV * LG.lgw_inv[step_sched] * LG.mask_inv)) * denoised + LG.HAS_LATENT_GUIDE * LG.lgw[step_sched] * LG.mask * LG.y0 + LG.HAS_LATENT_GUIDE_INV * LG.lgw_inv[step_sched] * LG.mask_inv * LG.y0_inv + else: + y0 = (1 - (lgw_mask_ + lgw_mask_inv_)) * denoised + lgw_mask_ * LG.y0 + lgw_mask_inv_ * LG.y0_inv + yx0 = y0.clone() + + if EO("flow_slerp"): + if EO("flow_manual_masks"): + y0_inv = (1 - (LG.HAS_LATENT_GUIDE * LG.lgw[step_sched] * LG.mask + LG.HAS_LATENT_GUIDE_INV * LG.lgw_inv[step_sched] * LG.mask_inv)) * denoised + LG.HAS_LATENT_GUIDE * LG.lgw[step_sched] * LG.mask * LG.y0_inv + LG.HAS_LATENT_GUIDE_INV * LG.lgw_inv[step_sched] * LG.mask_inv * LG.y0 + else: + y0_inv = (1 - (lgw_mask_ + lgw_mask_inv_)) * denoised + lgw_mask_ * LG.y0_inv + lgw_mask_inv_ * LG.y0 + flow_slerp_guide_ratio = EO("flow_slerp_guide_ratio", 0.5) + y_slerp = slerp_tensor(flow_slerp_guide_ratio, y0, y0_inv) + yx0 = y_slerp.clone() + + else: + yx0_prev = data_cached + if EO("flow_manual_masks"): + yx0 = (1 - (LG.HAS_LATENT_GUIDE * LG.lgw[step_sched] * LG.mask + LG.HAS_LATENT_GUIDE_INV * LG.lgw_inv[step_sched] * LG.mask_inv)) * yx0_prev + LG.HAS_LATENT_GUIDE * LG.lgw[step_sched] * LG.mask * x_tmp + LG.HAS_LATENT_GUIDE_INV * LG.lgw_inv[step_sched] * LG.mask_inv * x_tmp + else: + yx0 = (1 - (lgw_mask_ + lgw_mask_inv_)) * yx0_prev + (lgw_mask_ + lgw_mask_inv_) * x_tmp + + if not EO("flow_static_guides"): + if EO("flow_manual_masks"): + y0 = (1 - (LG.HAS_LATENT_GUIDE * LG.lgw[step_sched] * LG.mask + LG.HAS_LATENT_GUIDE_INV * LG.lgw_inv[step_sched] * LG.mask_inv)) * yx0_prev + LG.HAS_LATENT_GUIDE * LG.lgw[step_sched] * LG.mask * LG.y0 + LG.HAS_LATENT_GUIDE_INV * LG.lgw_inv[step_sched] * LG.mask_inv * LG.y0_inv + else: + y0 = (1 - (lgw_mask_ + lgw_mask_inv_)) * yx0_prev + lgw_mask_ * LG.y0 + lgw_mask_inv_ * LG.y0_inv + + if EO("flow_slerp"): + if EO("flow_manual_masks"): + y0_inv = (1 - (LG.HAS_LATENT_GUIDE * LG.lgw[step_sched] * LG.mask + LG.HAS_LATENT_GUIDE_INV * LG.lgw_inv[step_sched] * LG.mask_inv)) * yx0_prev + LG.HAS_LATENT_GUIDE * LG.lgw[step_sched] * LG.mask * LG.y0_inv + LG.HAS_LATENT_GUIDE_INV * LG.lgw_inv[step_sched] * LG.mask_inv * LG.y0 + else: + y0_inv = (1 - (lgw_mask_ + lgw_mask_inv_)) * yx0_prev + lgw_mask_ * LG.y0_inv + lgw_mask_inv_ * LG.y0 + + y0_orig = y0.clone() + if EO("flow_proj_xy"): + d_collinear_d_lerp = get_collinear(yx0, y0_orig) + d_lerp_ortho_d = get_orthogonal(y0_orig, yx0) + y0 = d_collinear_d_lerp + d_lerp_ortho_d + + if EO("flow_proj_yx"): + d_collinear_d_lerp = get_collinear(y0_orig, yx0) + d_lerp_ortho_d = get_orthogonal(yx0, y0_orig) + yx0 = d_collinear_d_lerp + d_lerp_ortho_d + + y0_inv_orig = None + if EO("flow_proj_xy_inv"): + y0_inv_orig = y0_inv.clone() + d_collinear_d_lerp = get_collinear(yx0, y0_inv) + d_lerp_ortho_d = get_orthogonal(y0_inv, yx0) + y0_inv = d_collinear_d_lerp + d_lerp_ortho_d + + if EO("flow_proj_yx_inv"): + y0_inv_orig = y0_inv if y0_inv_orig is None else y0_inv_orig + d_collinear_d_lerp = get_collinear(y0_inv_orig, yx0) + d_lerp_ortho_d = get_orthogonal(yx0, y0_inv_orig) + yx0 = d_collinear_d_lerp + d_lerp_ortho_d + del y0_orig + + flow_cossim_iter = EO("flow_cossim_iter", 1) + + if step == 0: + noise_yt = noise_fn(y0, sigma, sigma_next, NS.noise_sampler, flow_cossim_iter) # normalize_zscore(NS.noise_sampler(sigma=sigma, sigma_next=sigma_next), channelwise=True, inplace=True) + if not EO("flow_disable_renoise_y0"): + if noise_yt is None: + noise_yt = noise_fn(x_0, sigma, sigma_next, NS.noise_sampler, flow_cossim_iter) + else: + noise_yt = (1-eta) * noise_yt + eta * noise_fn(x_0, sigma, sigma_next, NS.noise_sampler, flow_cossim_iter) + + if VE_MODEL: + yt = y0 + s_tmp * noise_yt + else: + yt = (NS.sigma_max-s_tmp) * y0 + (s_tmp/NS.sigma_max) * noise_yt + if not EO("flow_disable_doublenoise_y0"): + if noise_yt is None: + noise_yt = noise_fn(x_0, sigma, sigma_next, NS.noise_sampler, flow_cossim_iter) + else: + noise_yt = (1-eta) * noise_yt + eta * noise_fn(x_0, sigma, sigma_next, NS.noise_sampler, flow_cossim_iter) + + if VE_MODEL: + y0_noised = y0 + sigma * noise_yt + else: + y0_noised = (NS.sigma_max-sigma) * y0 + sigma * noise_yt + + if EO("flow_slerp"): + noise = noise_fn(y0_inv, sigma, sigma_next, NS.noise_sampler, flow_cossim_iter) + yt_inv = (NS.sigma_max-s_tmp) * y0_inv + (s_tmp/NS.sigma_max) * noise + if not EO("flow_disable_doublenoise_y0_inv"): + noise = noise_fn(y0_inv, sigma, sigma_next, NS.noise_sampler, flow_cossim_iter) + y0_noised_inv = (NS.sigma_max-sigma) * y0_inv + sigma * noise + + if step == 0: + noise_xt = noise_fn(yx0, sigma, sigma_next, NS.noise_sampler, flow_cossim_iter) + if EO("flow_slerp"): + xt = yx0 + (s_tmp/NS.sigma_max) * (noise - y_slerp) + if not EO("flow_disable_doublenoise_x_0"): + noise = noise_fn(x_0, sigma, sigma_next, NS.noise_sampler, flow_cossim_iter) + x_0_noised = x_0 + sigma * (noise - y_slerp) + else: + if not EO("flow_disable_renoise_x_0"): + if noise_xt is None: + noise_xt = noise_fn(x_0, sigma, sigma_next, NS.noise_sampler, flow_cossim_iter) + else: + noise_xt = (1-eta_substep) * noise_xt + eta_substep * noise_fn(x_0, sigma, sigma_next, NS.noise_sampler, flow_cossim_iter) + + if VE_MODEL: + xt = yx0 + (s_tmp) * yx0 + (s_tmp) * (noise_xt - y0) + else: + xt = yx0 + (s_tmp/NS.sigma_max) * (noise_xt - y0) + if not EO("flow_disable_doublenoise_x_0"): + if noise_xt is None: + noise_xt = noise_fn(x_0, sigma, sigma_next, NS.noise_sampler, flow_cossim_iter) + else: + noise_xt = (1-eta_substep) * noise_xt + eta_substep * noise_fn(x_0, sigma, sigma_next, NS.noise_sampler, flow_cossim_iter) + if VE_MODEL: + x_0_noised = x_0 + (sigma) * x_0 + (sigma) * (noise_xt - y0) + else: + x_0_noised = x_0 + (sigma/NS.sigma_max) * (noise_xt - y0) # just lerp noise add, (1-sigma)*y0 + sigma*noise assuming x_0 == y0, which is true initially... + + eps_y, data_y = RK(yt, s_tmp, y0_noised, sigma, transformer_options={'latent_type': 'yt'}) + eps_x, data_x = RK(xt, s_tmp, x_0_noised, sigma, transformer_options={'latent_type': 'xt'}) + + if EO("flow_slerp"): + eps_y_inv, data_y_inv = RK(yt_inv, s_tmp, y0_noised_inv, sigma, transformer_options={'latent_type': 'yt_inv'}) + + if LG.lgw[step+1] == 0 and LG.lgw_inv[step+1] == 0: # break out of differentiating x0 and return to differentiating eps/velocity field + if EO("flow_shit_out_yx0"): + eps_ [row] = eps_x - eps_y + data_[row] = yx0 + if row == 0: + x_[row] = x_0 = xt + else: + x_[row] = xt + if not EO("flow_shit_out_new"): + eps_ [row] = eps_x + data_[row] = data_x + if row == 0: + x_[row] = x_0 = xt + else: + x_[row] = xt + + else: + eps_ [row] = (1 - (lgw_mask_ + lgw_mask_inv_)) * eps_x + (lgw_mask_ + lgw_mask_inv_) * eps_y + data_[row] = (1 - (lgw_mask_ + lgw_mask_inv_)) * data_x + (lgw_mask_ + lgw_mask_inv_) * data_y + if row == 0: + x_[row] = x_0 = (1 - (lgw_mask_ + lgw_mask_inv_)) * xt + (lgw_mask_ + lgw_mask_inv_) * yt + else: + x_[row] = (1 - (lgw_mask_ + lgw_mask_inv_)) * xt + (lgw_mask_ + lgw_mask_inv_) * yt + + FLOW_STOPPED = True + else: + if not EO("flow_slerp"): + if RK.EXPONENTIAL: + eps_y_alt = data_y - x_0 + eps_x_alt = data_x - x_0 + else: + eps_y_alt = (x_0 - data_y) / sigma + eps_x_alt = (x_0 - data_x) / sigma + + if EO("flow_y_zero"): + eps_y_alt *= LG.mask + + eps_[row] = eps_yx = (eps_y_alt - eps_x_alt) + eps_y_lin = (x_0 - data_y) / sigma + if EO("flow_y_zero"): + eps_y_lin *= LG.mask + eps_x_lin = (x_0 - data_x) / sigma + eps_yx_lin = (eps_y_lin - eps_x_lin) + + data_[row] = (1 - (lgw_mask_ + lgw_mask_inv_)) * data_x + (lgw_mask_ + lgw_mask_inv_) * data_y + + if EO("flow_reverse_data_masks"): + data_[row] = (1 - (lgw_mask_ + lgw_mask_inv_)) * data_y + (lgw_mask_ + lgw_mask_inv_) * data_x + + if flow_sync_eps != 0.0: + if RK.EXPONENTIAL: + eps_[row] = (1-flow_sync_eps) * eps_[row] + flow_sync_eps * (data_[row] - x_0) + else: + eps_[row] = (1-flow_sync_eps) * eps_[row] + flow_sync_eps * (x_0 - data_[row]) / sigma + + if EO("flow_sync_eps_mask"): + flow_sync_eps = EO("flow_sync_eps_mask", 1.0) + if RK.EXPONENTIAL: + eps_[row] = (lgw_mask_ + lgw_mask_inv_) * (1-flow_sync_eps) * eps_[row] + (1 - (lgw_mask_ + lgw_mask_inv_)) * flow_sync_eps * (data_[row] - x_0) + else: + eps_[row] = (lgw_mask_ + lgw_mask_inv_) * (1-flow_sync_eps) * eps_[row] + (1 - (lgw_mask_ + lgw_mask_inv_)) * flow_sync_eps * (x_0 - data_[row]) / sigma + + if EO("flow_sync_eps_revmask"): + flow_sync_eps = EO("flow_sync_eps_revmask", 1.0) + if RK.EXPONENTIAL: + eps_[row] = (1 - (lgw_mask_ + lgw_mask_inv_)) * (1-flow_sync_eps) * eps_[row] + (lgw_mask_ + lgw_mask_inv_) * flow_sync_eps * (data_[row] - x_0) + else: + eps_[row] = (1 - (lgw_mask_ + lgw_mask_inv_)) * (1-flow_sync_eps) * eps_[row] + (lgw_mask_ + lgw_mask_inv_) * flow_sync_eps * (x_0 - data_[row]) / sigma + + if EO("flow_sync_eps_maskonly"): + flow_sync_eps = EO("flow_sync_eps_maskonly", 1.0) + if RK.EXPONENTIAL: + eps_[row] = (lgw_mask_ + lgw_mask_inv_) * eps_[row] + (1 - (lgw_mask_ + lgw_mask_inv_)) * (data_[row] - x_0) + else: + eps_[row] = (lgw_mask_ + lgw_mask_inv_) * eps_[row] + (1 - (lgw_mask_ + lgw_mask_inv_)) * (x_0 - data_[row]) / sigma + + if EO("flow_sync_eps_revmaskonly"): + flow_sync_eps = EO("flow_sync_eps_revmaskonly", 1.0) + if RK.EXPONENTIAL: + eps_[row] = (1 - (lgw_mask_ + lgw_mask_inv_)) * eps_[row] + (lgw_mask_ + lgw_mask_inv_) * (data_[row] - x_0) + else: + eps_[row] = (1 - (lgw_mask_ + lgw_mask_inv_)) * eps_[row] + (lgw_mask_ + lgw_mask_inv_) * (x_0 - data_[row]) / sigma + + if EO("flow_slerp"): + if RK.EXPONENTIAL: + eps_y_alt = data_y - x_0 + eps_y_alt_inv = data_y_inv - x_0 + eps_x_alt = data_x - x_0 + else: + eps_y_alt = (x_0 - data_y) / sigma + eps_y_alt_inv = (x_0 - data_y_inv) / sigma + eps_x_alt = (x_0 - data_x) / sigma + + flow_slerp_ratio2 = EO("flow_slerp_ratio2", 0.5) + + eps_yx = (eps_y_alt - eps_x_alt) + eps_y_lin = (x_0 - data_y) / sigma + eps_x_lin = (x_0 - data_x) / sigma + eps_yx_lin = (eps_y_lin - eps_x_lin) + + eps_yx_inv = (eps_y_alt_inv - eps_x_alt) + eps_y_lin_inv = (x_0 - data_y_inv) / sigma + eps_x_lin = (x_0 - data_x) / sigma + eps_yx_lin_inv = (eps_y_lin_inv - eps_x_lin) + + data_row = x_0 - sigma * eps_yx_lin + data_row_inv = x_0 - sigma * eps_yx_lin_inv + + if EO("flow_slerp_similarity_ratio"): + flow_slerp_similarity_ratio = EO("flow_slerp_similarity_ratio", 1.0) + flow_slerp_ratio2 = find_slerp_ratio_grid(data_row, data_row_inv, LG.y0.clone(), LG.y0_inv.clone(), flow_slerp_similarity_ratio) + + eps_ [row] = slerp_tensor(flow_slerp_ratio2, eps_yx, eps_yx_inv) + data_[row] = slerp_tensor(flow_slerp_ratio2, data_row, data_row_inv) + + if EO("flow_slerp_autoalter"): + data_row_slerp = slerp_tensor(0.5, data_row, data_row_inv) + y0_pearsim = get_pearson_similarity(data_row_slerp, y0) + y0_pearsim_inv = get_pearson_similarity(data_row_slerp, y0_inv) + + if y0_pearsim > y0_pearsim_inv: + data_[row] = data_row_inv + eps_ [row] = (eps_y_alt_inv - eps_x_alt) + else: + data_[row] = data_row + eps_ [row] = (eps_y_alt - eps_x_alt) + + if EO("flow_slerp_recalc_eps_row"): + if RK.EXPONENTIAL: + eps_[row] = data_[row] - x_0 + else: + eps_[row] = (x_0 - data_[row]) / sigma + + if EO("flow_slerp_recalc_data_row"): + if RK.EXPONENTIAL: + data_[row] = x_0 + eps_[row] + else: + data_[row] = x_0 - sigma * eps_[row] + + data_cached = data_x + + if step < EO("direct_pre_pseudo_guide", 0) and step > 0: + for i_pseudo in range(EO("direct_pre_pseudo_guide_iter", 1)): + x_tmp += LG.lgw[step_sched] * LG.mask * (NS.sigma_max - s_tmp) * (LG.y0 - denoised) + LG.lgw_inv[step_sched] * LG.mask_inv * (NS.sigma_max - s_tmp) * (LG.y0_inv - denoised) + eps_[row], data_[row] = RK(x_tmp, s_tmp, x_0, sigma) + + # MODEL CALL MODEL CALL MODEL CALL MODEL CALL MODEL CALL MODEL CALL MODEL CALL MODEL CALL MODEL CALL MODEL CALL MODEL CALL MODEL CALL MODEL CALL MODEL CALL MODEL CALL MODEL CALL MODEL CALL + + if SYNC_GUIDE_ACTIVE: + pass + elif not ((not LG.guide_mode.startswith("flow")) or FLOW_STOPPED or (LG.guide_mode.startswith("flow") and LG.lgw[step_sched] == 0 and LG.lgw_inv[step_sched] == 0)): #(LG.guide_mode.startswith("flow") and (LG.lgw[step_sched] != 0 or LG.lgw_inv[step_sched] != 0)) or FLOW_STOPPED: + pass + elif LG.guide_mode.startswith("lure") and (LG.lgw[step_sched] > 0 or LG.lgw_inv[step_sched] > 0): + eps_[row], data_[row] = RK(x_tmp, s_tmp, x_0, sigma, transformer_options={'latent_type': 'yt'}) + + else: + if EO("protoshock") and StyleMMDiT is not None and StyleMMDiT.data_shock_start_step <= step_sched < StyleMMDiT.data_shock_end_step: + eps_[row], data_[row] = RK(x_tmp, s_tmp, x_0, sigma, transformer_options={'row': row, 'x_tmp': x_tmp, 'sigma_next': sigma_next}) + data_wct = StyleMMDiT.apply_data_shock(data_[row]) + if VE_MODEL: + x_tmp = x_tmp + (data_wct - data_[row]) + else: + x_tmp = x_tmp + (NS.sigma_max-NS.s_[row]) * (data_wct - data_[row]) + #x_[row+RK.row_offset] = x_tmp + x_[row] = x_tmp + if row == 0: + x_0 = x_tmp + + if EO("preshock"): + eps_[row], data_[row] = RK(x_tmp, s_tmp, x_0, sigma, transformer_options={'row': row, 'x_tmp': x_tmp, 'sigma_next': sigma_next}) + if VE_MODEL: + x_tmp = x_tmp + (data_wct - data_[row]) + else: + x_tmp = x_tmp + (NS.sigma_max-NS.s_[row]) * (data_wct - data_[row]) + x_[row] = x_tmp + if row == 0: + x_0 = x_tmp + + eps_[row], data_[row] = RK(x_tmp, s_tmp, x_0, sigma, transformer_options={'row': row, 'x_tmp': x_tmp, 'sigma_next': sigma_next}) + + #if EO("yoloshock") and StyleMMDiT is not None and StyleMMDiT.data_shock_start_step <= step_sched < StyleMMDiT.data_shock_end_step: + if not EO("disable_yoloshock") and StyleMMDiT is not None and StyleMMDiT.data_shock_start_step <= step_sched < StyleMMDiT.data_shock_end_step: + data_wct = StyleMMDiT.apply_data_shock(data_[row]) + if VE_MODEL: + x_tmp = x_tmp + (data_wct - data_[row]) + else: + x_tmp = x_tmp + (NS.sigma_max-NS.s_[row]) * (data_wct - data_[row]) + #x_[row+RK.row_offset] = x_tmp + x_[row] = x_tmp + if row == 0: + x_0 = x_tmp + data_[row] = data_wct + if RK.EXPONENTIAL: + eps_[row] = data_[row] - x_0 + else: + eps_[row] = (x_0 - data_[row]) / sigma + + + if hasattr(model.inner_model.inner_model.diffusion_model, "eps_out"): # fp64 model out override, for testing only + eps_out = model.inner_model.inner_model.diffusion_model.eps_out + del model.inner_model.inner_model.diffusion_model.eps_out + if eps_out.shape[0] == 2: + data_cond = x_0 - sigma * eps_out[1] + data_uncond = x_0 - sigma * eps_out[0] + data_row = data_uncond + model.inner_model.cfg * (data_cond - data_uncond) + eps_row = (x_0 - data_row) / sigma + else: + data_row = x_0 - sigma * eps_out + if RK.EXPONENTIAL: + eps_row = data_row - x_0 + else: + eps_row = eps_out + if torch.norm(eps_row - eps_[row]) < 0.01 and torch.norm(data_row - data_[row]) < 0.01: # if some other cfg/post-cfg func was used, detect and ignore this + eps_[row] = eps_row + data_[row] = data_row + + + if RK.extra_args['model_options']['transformer_options'].get('y0_standard_guide') is not None: + if hasattr(model.inner_model.inner_model.diffusion_model, "y0_standard_guide"): + LG.y0 = model.inner_model.inner_model.diffusion_model.y0_standard_guide.clone() + del model.inner_model.inner_model.diffusion_model.y0_standard_guide + RK.extra_args['model_options']['transformer_options']['y0_standard_guide'] = None + + if RK.extra_args['model_options']['transformer_options'].get('y0_inv_standard_guide') is not None: + if hasattr(model.inner_model.inner_model.diffusion_model, "y0_inv_standard_guide"): + LG.y0_inv = model.inner_model.inner_model.diffusion_model.y0_inv_standard_guide.clone() # RK.extra_args['model_options']['transformer_options'].get('y0_standard_guide') + del model.inner_model.inner_model.diffusion_model.y0_inv_standard_guide + RK.extra_args['model_options']['transformer_options']['y0_inv_standard_guide'] = None + + if LG.guide_mode.startswith("lure") and (LG.lgw[step_sched] > 0 or LG.lgw_inv[step_sched] > 0): + x_tmp = LG.process_guides_data_substep(x_tmp, data_[row], step_sched, s_tmp) + eps_[row], data_[row] = RK(x_tmp, s_tmp, x_0, sigma, transformer_options={'latent_type': 'xt'}) + + if momentum != 0.0: + data_[row] = data_[row] - momentum * (data_prev_[0] - data_[row]) #negative! + eps_[row] = RK.get_epsilon(x_0, x_tmp, data_[row], sigma, s_tmp) # ... why was this here??? for momentum maybe? + + if row < RK.rows and noise_scaling_weight != 0 and noise_scaling_type in {"sampler", "sampler_substep"}: + if noise_scaling_type == "sampler_substep": + sub_lying_su, sub_lying_sigma, sub_lying_sd, sub_lying_alpha_ratio = NS.get_sde_substep(NS.s_[row], NS.s_[row+RK.row_offset+RK.multistep_stages], noise_scaling_eta, noise_scaling_mode) + for _ in range(noise_scaling_cycles-1): + sub_lying_su, sub_lying_sigma, sub_lying_sd, sub_lying_alpha_ratio = NS.get_sde_substep(NS.s_[row], sub_lying_sd, noise_scaling_eta, noise_scaling_mode) + lying_s_[row+1] = sub_lying_sd + substep_noise_scaling_ratio = NS.s_[row+1]/lying_s_[row+1] + if RK.multistep_stages > 0: + substep_noise_scaling_ratio = sigma_next/lying_sd #fails with resample? + + lying_eps_row_factor = (1 - noise_scaling_weight*(substep_noise_scaling_ratio-1)) + + # GUIDE + if not EO("disable_guides_eps_substep"): + eps_, x_ = LG.process_guides_substep(x_0, x_, eps_, data_, row, step_sched, NS.sigma, NS.sigma_next, NS.sigma_down, NS.s_, epsilon_scale, RK) + if not EO("disable_guides_eps_prev_substep"): + eps_prev_, x_ = LG.process_guides_substep(x_0, x_, eps_prev_, data_, row, step_sched, NS.sigma, NS.sigma_next, NS.sigma_down, NS.s_, epsilon_scale, RK) + + if LG.y0_mean is not None and LG.y0_mean.sum() != 0.0: + + if EO("guide_mean_scattersort"): + data_row_mean = apply_scattersort_spatial(data_[row], LG.y0_mean) + eps_row_mean = RK.get_eps(x_0, data_row_mean, s_tmp) + else: + eps_row_mean = eps_[row] - eps_[row].mean(dim=(-2,-1), keepdim=True) + (LG.y0_mean - x_0).mean(dim=(-2,-1), keepdim=True) + + if LG.mask_mean is not None: + eps_row_mean = LG.mask_mean * eps_row_mean + (1-LG.mask_mean) * eps_[row] + + eps_[row] = eps_[row] + LG.lgw_mean[step_sched] * (eps_row_mean - eps_[row]) + + if (full_iter == 0 and diag_iter == 0) or EO("newton_iter_post_use_on_implicit_steps"): + x_, eps_ = RK.newton_iter(x_0, x_, eps_, eps_prev_, data_, NS.s_, row, NS.h, sigmas, step, "post", SYNC_GUIDE_ACTIVE) + + # UPDATE #for row in range(RK.rows - RK.multistep_stages - RK.row_offset + 1): + if EO("exp2lin_override") and RK.EXPONENTIAL: + x_ = RK.update_substep(x_0, x_, eps_, eps_prev_, row, RK.row_offset, NS.h_new, NS.h_new_orig, lying_eps_row_factor=lying_eps_row_factor, sigma=sigma) #modifies eps_[row] if lying_eps_row_factor != 1.0 + #x_ = RK.update_substep(x_0, x_, eps_, eps_prev_, row, RK.row_offset, -sigma*NS.h_new, -sigma*NS.h_new_orig, lying_eps_row_factor=lying_eps_row_factor) #modifies eps_[row] if lying_eps_row_factor != 1.0 + else: + x_ = RK.update_substep(x_0, x_, eps_, eps_prev_, row, RK.row_offset, NS.h_new, NS.h_new_orig, lying_eps_row_factor=lying_eps_row_factor) #modifies eps_[row] if lying_eps_row_factor != 1.0 + + x_[row+RK.row_offset] = NS.rebound_overshoot_substep(x_0, x_[row+RK.row_offset]) + + if SYNC_GUIDE_ACTIVE: #yt_ is not None: + #yt_ = RK.update_substep(yt_0, yt_, eps_y_, eps_prev_y_, row, RK.row_offset, NS.h_new, NS.h_new_orig, lying_eps_row_factor=lying_eps_row_factor) #modifies eps_[row] if lying_eps_row_factor != 1.0 + yt_ = RK.update_substep(yt_0, yt_, eps_yt_, eps_prev_y_, row, RK.row_offset, NS.h_new, NS.h_new_orig, lying_eps_row_factor=lying_eps_row_factor, sigma=sigma) #modifies eps_[row] if lying_eps_row_factor != 1.0 + yt_[row+RK.row_offset] = NS.rebound_overshoot_substep(yt_0, yt_[row+RK.row_offset]) + + if not RK.IMPLICIT and NS.noise_mode_sde_substep != "hard_sq": + + x_means_per_substep = x_[row+RK.row_offset].mean(dim=(-2,-1), keepdim=True) + + if not LG.guide_mode.startswith("flow") or (LG.lgw[step_sched] == 0 and LG.lgw[step+1] == 0 and LG.lgw_inv[step_sched] == 0 and LG.lgw_inv[step+1] == 0): + #if LG.guide_mode.startswith("sync") and (LG.lgw[step_sched] != 0.0 or LG.lgw_inv[step_sched] != 0.0): + # x_row_tmp = x_[row+RK.row_offset].clone() + + #x_[row+RK.row_offset] = NS.swap_noise_substep(x_0, x_[row+RK.row_offset], mask=sde_mask, guide=LG.y0) + x_row_tmp = NS.swap_noise_substep(x_0, x_[row+RK.row_offset], mask=sde_mask, guide=LG.y0) + + #if EO("eps_adain_smartnoise_substep"): + if LG.ADAIN_NOISE_MODE == "smart": + #eps_row_next = (x_0 - x_[row+RK.row_offset]) / (sigma - NS.s_[row+RK.row_offset]) + #denoised_row_next = x_0 - sigma * eps_row_next + # + #eps_swapped = (x_row_tmp - denoised_row_next) / NS.s_[row+RK.row_offset] + # + #noise_row_next = eps_swapped + denoised_row_next + #z_[row+RK.row_offset] = noise_row_next + #RK.update_transformer_options({'z_' : z_}) + data_next = denoised + NS.h_new * RK.zum(row+RK.row_offset+RK.multistep_stages, data_, data_prev_) + if VE_MODEL: + z_[row+RK.row_offset] = (x_row_tmp - data_next) / NS.s_[row+RK.row_offset] + else: + z_[row+RK.row_offset] = (x_row_tmp - (NS.sigma_max-NS.s_[row+RK.row_offset])*data_next) / NS.s_[row+RK.row_offset] + RK.update_transformer_options({'z_' : z_}) + + elif LG.ADAIN_NOISE_MODE == "update": #EO("eps_adain"): + x_init_new = (x_row_tmp - x_[row+RK.row_offset]) / s_tmp + x_init + x_0 += sigma * (x_init_new - x_init) + x_init = x_init_new + RK.update_transformer_options({'x_init' : x_init.clone()}) + + if SYNC_GUIDE_ACTIVE: + noise_bongflow_new = (x_row_tmp - x_[row+RK.row_offset]) / s_tmp + noise_bongflow + yt_[row+RK.row_offset] += s_tmp * (noise_bongflow_new - noise_bongflow) + x_0 += sigma * (noise_bongflow_new - noise_bongflow) + noise_bongflow = noise_bongflow_new + + x_[row+RK.row_offset] = x_row_tmp + + elif LG.guide_mode.startswith("flow"): + pass + + if not LG.guide_mode.startswith("lure"): + x_[row+RK.row_offset] = LG.process_guides_data_substep(x_[row+RK.row_offset], data_[row], step_sched, NS.s_[row]) + + if ((not EO("protoshock") and not EO("yoloshock")) or EO("fuckitshock")) and StyleMMDiT is not None and StyleMMDiT.data_shock_start_step <= step_sched < StyleMMDiT.data_shock_end_step: + data_wct = StyleMMDiT.apply_data_shock(data_[row]) + if VE_MODEL: + x_[row+RK.row_offset] = x_[row+RK.row_offset] + (data_wct - data_[row]) + else: + x_[row+RK.row_offset] = x_[row+RK.row_offset] + (NS.sigma_max-NS.s_[row]) * (data_wct - data_[row]) + + + if SYNC_GUIDE_ACTIVE: # # # # ## # # ## # YIIIIKES --------------------------------------------------------------------------------------------------------- + if VE_MODEL: + yt_[:NS.s_.shape[0], 0] = y0_bongflow + NS.s_.view(-1, *[1]*(x.ndim-1)) * (noise_bongflow) + yt_0 = y0_bongflow + sigma * (noise_bongflow) + else: + yt_[:NS.s_.shape[0], 0] = y0_bongflow + NS.s_.view(-1, *[1]*(x.ndim-1)) * (noise_bongflow - y0_bongflow) + yt_0 = y0_bongflow + sigma * (noise_bongflow - y0_bongflow) + if RK.EXPONENTIAL: + eps_y_ = data_y_ - yt_0 # yt_ # watch out for fuckery with size of tableau being smaller later in a chained sampler + else: + if BONGMATH: + eps_y_[:NS.s_.shape[0]] = (yt_[:NS.s_.shape[0]] - data_y_[:NS.s_.shape[0]]) / NS.s_.view(-1,*[1]*(x_.ndim-1)) + else: + eps_y_[:NS.s_.shape[0]] = (yt_0.repeat(NS.s_.shape[0], *[1]*(x_.ndim-1)) - data_y_[:NS.s_.shape[0]]) / sigma # calc exact to c0 node + if not BONGMATH and (eta != 0 or eta_substep != 0): + if RK.EXPONENTIAL: + eps_x_ = data_x_ - x_0 + else: + eps_x_ = (x_0 - data_x_) / sigma + + weight_mask = lgw_mask_+lgw_mask_inv_ + if LG.SYNC_SEPARATE: + sync_mask = lgw_mask_sync_+lgw_mask_sync_inv_ + else: + sync_mask = 1. + + for ms in range(len(eps_)): + if RK.EXPONENTIAL: + if VE_MODEL: + eps_[ms] = sync_mask * eps_x_[ms] + (1-sync_mask) * eps_x2y_[ms] + weight_mask * (-eps_y_[ms] + sigma*(-noise_bongflow)) + if EO("sync_x2y"): + eps_[ms] = sync_mask * eps_x_[ms] + (1-sync_mask) * eps_x2y_[ms] + weight_mask * (-eps_x2y_[ms] + sigma*(-noise_bongflow)) + else: + eps_[ms] = sync_mask * eps_x_[ms] + (1-sync_mask) * eps_x2y_[ms] + weight_mask * (-eps_y_[ms] + sigma*(y0_bongflow-noise_bongflow)) + if EO("sync_x2y"): + eps_[ms] = sync_mask * eps_x_[ms] + (1-sync_mask) * eps_x2y_[ms] + weight_mask * (-eps_x2y_[ms] + sigma*(y0_bongflow-noise_bongflow)) + else: + if VE_MODEL: + eps_[ms] = sync_mask * eps_x_[ms] + (1-sync_mask) * eps_x2y_[ms] + weight_mask * (-eps_y_[ms] + (noise_bongflow)) + if EO("sync_x2y"): + eps_[ms] = sync_mask * eps_x_[ms] + (1-sync_mask) * eps_x2y_[ms] + weight_mask * (-eps_x2y_[ms] + (noise_bongflow)) + else: + eps_[ms] = sync_mask * eps_x_[ms] + (1-sync_mask) * eps_x2y_[ms] + weight_mask * (-eps_y_[ms] + (noise_bongflow-y0_bongflow)) + if EO("sync_x2y"): + eps_[ms] = sync_mask * eps_x_[ms] + (1-sync_mask) * eps_x2y_[ms] + weight_mask * (-eps_x2y_[ms] + (noise_bongflow-y0_bongflow)) + + if BONGMATH and NS.s_[row] > RK.sigma_min and NS.h < RK.sigma_max/2 and (diag_iter == implicit_steps_diag or EO("enable_diag_explicit_bongmath_all")) and not EO("disable_terminal_bongmath"): + if step == 0 and UNSAMPLE: + pass + elif full_iter == implicit_steps_full or not EO("disable_fully_explicit_bongmath_except_final"): + if sigma > 0.03: + BONGMATH_Y = SYNC_GUIDE_ACTIVE + x_0, x_, eps_ = RK.bong_iter(x_0, x_, eps_, eps_prev_, data_, sigma, NS.s_, row, RK.row_offset, NS.h, step, step_sched, + BONGMATH_Y, y0_bongflow, noise_bongflow, eps_x_, eps_y_, data_x_, data_y_, LG) + # BONGMATH_Y, y0_bongflow, noise_bongflow, eps_x_, eps_y_, eps_x2y_, data_x_, LG) + #if EO("eps_adain_smartnoise_bongmath"): + if LG.ADAIN_NOISE_MODE == "smart": + if VE_MODEL: + z_[:NS.s_.shape[0], ...] = (x_ - data_)[:NS.s_.shape[0], ...] / NS.s_.view(-1,*[1]*(x_.ndim-1)) + else: + z_[:NS.s_.shape[0], ...] = (x_[:NS.s_.shape[0], ...] - (NS.sigma_max - NS.s_.view(-1,*[1]*(x_.ndim-1)))*data_[:NS.s_.shape[0], ...])[:NS.s_.shape[0], ...] / NS.s_.view(-1,*[1]*(x_.ndim-1)) + RK.update_transformer_options({'z_' : z_}) + diag_iter += 1 + + #progress_bar.update( round(1 / implicit_steps_total, 2) ) + + #step_update = round(1 / implicit_steps_total, 2) + #progress_bar.update(float(f"{step_update:.2f}")) + + x_next = x_[RK.rows - RK.multistep_stages - RK.row_offset + 1] + x_next = NS.rebound_overshoot_step(x_0, x_next) + + if SYNC_GUIDE_ACTIVE: # YT_NEXT UPDATE STEP -------------------------------------- + yt_next = yt_[RK.rows - RK.multistep_stages - RK.row_offset + 1] + yt_next = NS.rebound_overshoot_step(yt_0, yt_next) + + eps = (x_0 - x_next) / (sigma - sigma_next) + denoised = x_0 - sigma * eps + + if EO("postshock") and step < EO("postshock", 10): + eps_row, data_row = RK(x_next, sigma_next, x_next, sigma_next, transformer_options={'row': row, 'x_tmp': x_next, 'sigma_next': sigma_next}) + if VE_MODEL: + x_next = x_next + (data_row - denoised) + else: + x_next = x_next + (NS.sigma_max-sigma_next) * (data_row - denoised) + eps = (x_0 - x_next) / (sigma - sigma_next) + denoised = x_0 - sigma * eps + + if EO("data_sampler") and step > EO("data_sampler_start_step", 0) and step < EO("data_sampler_end_step", 5): + data_sampler_weight = EO("data_sampler_weight", 1.0) + denoised_step = RK.zum(row+RK.row_offset+RK.multistep_stages, data_, data_prev_) + x_next = LG.swap_data(x_next, denoised, denoised_step, data_sampler_weight * sigma_next) + eps = (x_0 - x_next) / (sigma - sigma_next) + denoised = x_0 - sigma * eps + + x_0_prev = x_0.clone() + + x_means_per_step = x_next.mean(dim=(-2,-1), keepdim=True) + + if eta == 0.0: + x = x_next + if SYNC_GUIDE_ACTIVE: + yt_0 = yt_[0] = yt_next + #elif LG.guide_mode.startswith("sync") and (LG.lgw[step_sched] != 0.0 or LG.lgw_inv[step_sched] != 0.0): + # noise_sync_new = NS.noise_sampler(sigma=sigma, sigma_next=sigma_next) + # x = x_next + sigma * eta * (noise_sync_new - noise_bongflow) + # noise_bongflow += eta * (noise_sync_new - noise_bongflow) + elif not LG.guide_mode.startswith("flow") or (LG.lgw[step_sched] == 0 and LG.lgw[step+1] == 0 and LG.lgw_inv[step_sched] == 0 and LG.lgw_inv[step+1] == 0): + x = NS.swap_noise_step(x_0, x_next, mask=sde_mask) + + #if EO("eps_adain_smartnoise"): + if LG.ADAIN_NOISE_MODE == "smart": + #noise_next = eps + denoised + #eps_swapped = (x - denoised) / sigma_next + # + #noise_next = eps_swapped + denoised + #z_[0] = noise_next + #RK.update_transformer_options({'z_' : z_}) + if full_iter+1 < implicit_steps_full+1: # are we to loop for full iter after this? + if VE_MODEL: + #z_[row+RK.row_offset] = (x - denoised) / sigma_next + z_[0] = (x_0 - denoised) / sigma + else: + #z_[row+RK.row_offset] = (x - (NS.sigma_max-sigma_next) * denoised) / sigma_next + z_[0] = (x_0 - (NS.sigma_max-sigma) * denoised) / sigma + else: #we're advancing to next step, x is x_next + if VE_MODEL: + #z_[row+RK.row_offset] = (x - denoised) / sigma_next + z_[0] = (x - denoised) / sigma_next + else: + #z_[row+RK.row_offset] = (x - (NS.sigma_max-sigma_next) * denoised) / sigma_next + z_[0] = (x - (NS.sigma_max-sigma_next) * denoised) / sigma_next + RK.update_transformer_options({'z_' : z_}) + + elif LG.ADAIN_NOISE_MODE == "update": #EO("eps_adain"): + x_init_new = (x - x_next) / sigma_next + x_init + x_0 += sigma * (x_init_new - x_init) + x_init = x_init_new + RK.update_transformer_options({'x_init' : x_init.clone()}) + + if SYNC_GUIDE_ACTIVE: + noise_bongflow_new = (x - x_next) / sigma_next + noise_bongflow + yt_next += sigma_next * (noise_bongflow_new - noise_bongflow) + x_0 += sigma * (noise_bongflow_new - noise_bongflow) + if not EO("disable_i_bong"): + for i_bong in range(len(NS.s_)): + x_[i_bong] += NS.s_[i_bong] * (noise_bongflow_new - noise_bongflow) + #x_[0] += sigma * (noise_bongflow_new - noise_bongflow) + yt_0 = yt_[0] = yt_next + noise_bongflow = noise_bongflow_new + else: + x = x_next + + if EO("keep_step_means"): + x = x - x.mean(dim=(-2,-1), keepdim=True) + x_means_per_step + + + callback_step = len(sigmas)-1 - step if sampler_mode == "unsample" else step + preview_callback(x, eps, denoised, x_, eps_, data_, callback_step, sigma, sigma_next, callback, EO, preview_override=data_cached, FLOW_STOPPED=FLOW_STOPPED) + + h_prev = NS.h + x_prev = x_0 + + denoised_prev2 = denoised_prev + denoised_prev = denoised + + full_iter += 1 + + if LG.lgw[step_sched] > 0 and step >= EO("guide_cutoff_start_step", 0) and cossim_counter < EO("guide_cutoff_max_iter", 10) and (EO("guide_cutoff") or EO("guide_min")): + guide_cutoff = EO("guide_cutoff", 1.0) + denoised_norm = data_[0] - data_[0].mean(dim=(-2,-1), keepdim=True) + y0_norm = LG.y0 - LG.y0 .mean(dim=(-2,-1), keepdim=True) + y0_cossim = get_cosine_similarity(denoised_norm, y0_norm) + if y0_cossim > guide_cutoff and LG.lgw[step_sched] > EO("guide_cutoff_floor", 0.0): + if not EO("guide_cutoff_fast"): + LG.lgw[step_sched] *= EO("guide_cutoff_factor", 0.9) + else: + LG.lgw *= EO("guide_cutoff_factor", 0.9) + full_iter -= 1 + if y0_cossim < EO("guide_min", 0.0) and LG.lgw[step_sched] < EO("guide_min_ceiling", 1.0): + if not EO("guide_cutoff_fast"): + LG.lgw[step_sched] *= EO("guide_min_factor", 1.1) + else: + LG.lgw *= EO("guide_min_factor", 1.1) + full_iter -= 1 + + #if EO("smartnoise"): #TODO: determine if this was useful + # z_[0] = z_next + + if FLOW_STARTED and FLOW_STOPPED: + data_prev_ = data_x_prev_ + if FLOW_STARTED and not FLOW_STOPPED: + data_x_prev_[0] = data_cached # data_cached is data_x from flow mode. this allows multistep to resume seamlessly. + for ms in range(recycled_stages): + data_x_prev_[recycled_stages - ms] = data_x_prev_[recycled_stages - ms - 1] + + #if LG.guide_mode.startswith("sync") and (LG.lgw[step_sched] != 0.0 or LG.lgw_inv[step_sched] != 0.0): + # data_prev_[0] = x_0 - sigma * eps_[0] + #else: + data_prev_[0] = data_[0] # with flow mode, this will be the differentiated guide/"denoised" + for ms in range(recycled_stages): + data_prev_[recycled_stages - ms] = data_prev_[recycled_stages - ms - 1] # TODO: verify that this does not run on every substep... + + if SYNC_GUIDE_ACTIVE: + data_prev_x_[0] = data_x + for ms in range(recycled_stages): + data_prev_x_[recycled_stages - ms] = data_prev_x_[recycled_stages - ms - 1] + + data_prev_y_[0] = data_y + for ms in range(recycled_stages): + data_prev_y_[recycled_stages - ms] = data_prev_y_[recycled_stages - ms - 1] + + rk_type = RK.swap_rk_type_at_step_or_threshold(x_0, data_prev_, NS, sigmas, step, rk_swap_step, rk_swap_threshold, rk_swap_type, rk_swap_print) + if step > rk_swap_step: + implicit_steps_full = 0 + implicit_steps_diag = 0 + + if EO("bong2m") or EO("bong3m"): + denoised_data_prev2 = denoised_data_prev + denoised_data_prev = data_[0] + + if SKIP_PSEUDO and not LG.guide_mode.startswith("flow"): + if SKIP_PSEUDO_Y == "y0": + LG.y0 = denoised + LG.HAS_LATENT_GUIDE = True + else: + LG.y0_inv = denoised + LG.HAS_LATENT_GUIDE_INV = True + + if EO("pseudo_mix_strength"): + pseudo_mix_strength = EO("pseudo_mix_strength", 0.0) + LG.y0 = orig_y0 + pseudo_mix_strength * (denoised - orig_y0) + LG.y0_inv = orig_y0_inv + pseudo_mix_strength * (denoised - orig_y0_inv) + + #if sampler_mode == "unsample": + # progress_bar.n -= 1 + # progress_bar.refresh() + #else: + # progress_bar.update(1) + progress_bar.update(1) #THIS WAS HERE + step += 1 + + if EO("skip_step", -1) == step: + step += 1 + + if d_noise_start_step == step: + sigmas = sigmas.clone() * d_noise + if sigmas.max() > NS.sigma_max: + sigmas = sigmas / NS.sigma_max + if d_noise_inv_start_step == step: + sigmas = sigmas.clone() / d_noise_inv + if sigmas.max() > NS.sigma_max: + sigmas = sigmas / NS.sigma_max + + if LG.lgw[step_sched] > 0 and step >= EO("guide_step_cutoff_start_step", 0) and cossim_counter < EO("guide_step_cutoff_max_iter", 10) and (EO("guide_step_cutoff") or EO("guide_step_min")): + guide_cutoff = EO("guide_step_cutoff", 1.0) + eps_trash, data_trash = RK(x, sigma_next, x_0, sigma) + denoised_norm = data_trash - data_trash.mean(dim=(-2,-1), keepdim=True) + y0_norm = LG.y0 - LG.y0 .mean(dim=(-2,-1), keepdim=True) + y0_cossim = get_cosine_similarity(denoised_norm, y0_norm) + if y0_cossim > guide_cutoff and LG.lgw[step_sched] > EO("guide_step_cutoff_floor", 0.0): + if not EO("guide_step_cutoff_fast"): + LG.lgw[step_sched] *= EO("guide_step_cutoff_factor", 0.9) + else: + LG.lgw *= EO("guide_step_cutoff_factor", 0.9) + step -= 1 + x_0 = x = x_[0] = x_0_orig.clone() + if y0_cossim < EO("guide_step_min", 0.0) and LG.lgw[step_sched] < EO("guide_step_min_ceiling", 1.0): + if not EO("guide_step_cutoff_fast"): + LG.lgw[step_sched] *= EO("guide_step_min_factor", 1.1) + else: + LG.lgw *= EO("guide_step_min_factor", 1.1) + step -= 1 + x_0 = x = x_[0] = x_0_orig.clone() + # END SAMPLING LOOP --------------------------------------------------------------------------------------------------- + + #progress_bar.close() + RK.update_transformer_options({'update_cross_attn': None}) + if step == len(sigmas)-2 and sigmas[-1] == 0 and sigmas[-2] == NS.sigma_min and not INIT_SAMPLE_LOOP: + eps, denoised = RK(x, NS.sigma_min, x, NS.sigma_min) + x = denoised + #progress_bar.update(1) + + eps = eps .to(model_device) + denoised = denoised.to(model_device) + x = x .to(model_device) + + progress_bar.close() + + if not (UNSAMPLE and sigmas[1] > sigmas[0]) and not EO("preview_last_step_always") and sigma is not None and not (FLOW_STARTED and not FLOW_STOPPED): + callback_step = len(sigmas)-1 - step if sampler_mode == "unsample" else step + preview_callback(x, eps, denoised, x_, eps_, data_, callback_step, sigma, sigma_next, callback, EO, preview_override=data_cached, FLOW_STOPPED=FLOW_STOPPED) + + if INIT_SAMPLE_LOOP: + state_info_out = state_info + else: + if guides is not None and guides.get('guide_mode', "") == 'inversion': + guide_inversion_y0 = state_info.get('guide_inversion_y0') + guide_inversion_y0_inv = state_info.get('guide_inversion_y0_inv') + + if sampler_mode == "unsample" and guide_inversion_y0 is None: + guide_inversion_y0 = LG.y0.clone() + if sampler_mode == "unsample" and guide_inversion_y0_inv is None: + guide_inversion_y0_inv = LG.y0_inv.clone() + + if sampler_mode in {"standard", "resample"} and guide_inversion_y0 is None: + guide_inversion_y0 = NS.noise_sampler(sigma=NS.sigma_max, sigma_next=NS.sigma_min).to(x) + guide_inversion_y0 = normalize_zscore(guide_inversion_y0, channelwise=True, inplace=True) + if sampler_mode in {"standard", "resample"} and guide_inversion_y0_inv is None: + guide_inversion_y0_inv = NS.noise_sampler(sigma=NS.sigma_max, sigma_next=NS.sigma_min).to(x) + guide_inversion_y0_inv = normalize_zscore(guide_inversion_y0_inv, channelwise=True, inplace=True) + + state_info_out['guide_inversion_y0'] = guide_inversion_y0 + state_info_out['guide_inversion_y0_inv'] = guide_inversion_y0_inv + + state_info_out['raw_x'] = x.to('cpu') + state_info_out['denoised'] = denoised.to('cpu') + state_info_out['data_prev_'] = data_prev_.to('cpu') + state_info_out['end_step'] = step + state_info_out['sigma_next'] = sigma_next.clone() + state_info_out['sigmas'] = sigmas_scheduled.clone() + state_info_out['sampler_mode'] = sampler_mode + state_info_out['last_rng'] = NS.noise_sampler .generator.get_state().clone() + state_info_out['last_rng_substep'] = NS.noise_sampler2.generator.get_state().clone() + state_info_out['completed'] = step == len(sigmas)-2 and sigmas[-1] == 0 and sigmas[-2] == NS.sigma_min + state_info_out['FLOW_STARTED'] = FLOW_STARTED + state_info_out['FLOW_STOPPED'] = FLOW_STOPPED + state_info_out['noise_bongflow'] = noise_bongflow + state_info_out['y0_bongflow'] = y0_bongflow + state_info_out['y0_bongflow_orig'] = y0_bongflow_orig + state_info_out['y0_standard_guide'] = y0_standard_guide + state_info_out['y0_inv_standard_guide'] = y0_inv_standard_guide + state_info_out['data_prev_y_'] = data_prev_y_ + state_info_out['data_prev_x_'] = data_prev_x_ + + if FLOW_STARTED and not FLOW_STOPPED: + state_info_out['y0'] = y0.to('cpu') + #state_info_out['y0_inv'] = y0_inv.to('cpu') # TODO: implement this? + state_info_out['data_cached'] = data_cached.to('cpu') + state_info_out['data_x_prev_'] = data_x_prev_.to('cpu') + + return x + +def noise_fn(x, sigma, sigma_next, noise_sampler, cossim_iter=1): + + noise = normalize_zscore(noise_sampler(sigma=sigma, sigma_next=sigma_next), channelwise=True, inplace=True) + cossim = get_pearson_similarity(x, noise) + + for i in range(cossim_iter): + noise_new = normalize_zscore(noise_sampler(sigma=sigma, sigma_next=sigma_next), channelwise=True, inplace=True) + cossim_new = get_pearson_similarity(x, noise_new) + + if cossim_new > cossim: + noise = noise_new + cossim = cossim_new + + return noise + + +def preview_callback( + x : Tensor, + eps : Tensor, + denoised : Tensor, + x_ : Tensor, + eps_ : Tensor, + data_ : Tensor, + step : int, + sigma : Tensor, + sigma_next : Tensor, + callback : Callable, + EO : ExtraOptions, + preview_override : Optional[Tensor] = None, + FLOW_STOPPED : bool = False): + + if EO("eps_substep_preview"): + row_callback = EO("eps_substep_preview", 0) + denoised_callback = eps_[row_callback] + + elif EO("denoised_substep_preview"): + row_callback = EO("denoised_substep_preview", 0) + denoised_callback = data_[row_callback] + + elif EO("x_substep_preview"): + row_callback = EO("x_substep_preview", 0) + denoised_callback = x_[row_callback] + + elif EO("eps_preview"): + denoised_callback = eps + + elif EO("denoised_preview"): + denoised_callback = denoised + + elif EO("x_preview"): + denoised_callback = x + + elif preview_override is not None and FLOW_STOPPED == False: + denoised_callback = preview_override + + else: + denoised_callback = data_[0] + + callback({'x': x, 'i': step, 'sigma': sigma, 'sigma_next': sigma_next, 'denoised': denoised_callback.to(torch.float32)}) if callback is not None else None + + return + diff --git a/ComfyUI/custom_nodes/RES4LYF/beta/samplers.py b/ComfyUI/custom_nodes/RES4LYF/beta/samplers.py new file mode 100644 index 0000000000000000000000000000000000000000..ae65cc8a312a1f5a265366fc7d2a478fc0fbee66 --- /dev/null +++ b/ComfyUI/custom_nodes/RES4LYF/beta/samplers.py @@ -0,0 +1,2440 @@ +import torch +import torch.nn.functional as F +from torch import Tensor + +from typing import Optional, Callable, Tuple, Dict, Any, Union +import copy +import gc + +import comfy.samplers +import comfy.sample +import comfy.sampler_helpers +import comfy.model_sampling +import comfy.latent_formats +import comfy.sd +import comfy.supported_models +from comfy.samplers import CFGGuider, sampling_function + +import latent_preview + +from ..helper import initialize_or_scale, get_res4lyf_scheduler_list, OptionsManager, ExtraOptions +from ..res4lyf import RESplain +from ..latents import normalize_zscore, get_orthogonal +from ..sigmas import get_sigmas +#import ..models # import ReFluxPatcher + +from .constants import MAX_STEPS, IMPLICIT_TYPE_NAMES +from .noise_classes import NOISE_GENERATOR_CLASSES_SIMPLE, NOISE_GENERATOR_NAMES_SIMPLE, NOISE_GENERATOR_NAMES +from .rk_noise_sampler_beta import NOISE_MODE_NAMES +from .rk_coefficients_beta import get_default_sampler_name, get_sampler_name_list, process_sampler_name + + +def copy_cond(conditioning): + new_conditioning = [] + if type(conditioning[0][0]) == list: + for i in range(len(conditioning)): + new_conditioning_i = [] + for embedding, cond in conditioning[i]: + cond_copy = {} + for k, v in cond.items(): + if isinstance(v, torch.Tensor): + cond_copy[k] = v.clone() + else: + cond_copy[k] = v # ensure we're not copying huge shit like controlnets + new_conditioning_i.append([embedding.clone(), cond_copy]) + new_conditioning.append(new_conditioning_i) + else: + for embedding, cond in conditioning: + cond_copy = {} + for k, v in cond.items(): + if isinstance(v, torch.Tensor): + cond_copy[k] = v.clone() + else: + cond_copy[k] = v # ensure we're not copying huge shit like controlnets + new_conditioning.append([embedding.clone(), cond_copy]) + + return new_conditioning + + +class SharkGuider(CFGGuider): + def __init__(self, model_patcher): + super().__init__(model_patcher) + self.cfgs = {} + + def set_conds(self, **kwargs): + self.inner_set_conds(kwargs) + + def set_cfgs(self, **kwargs): + self.cfgs = {**kwargs} + self.cfg = self.cfgs.get('xt', self.cfg) + + def predict_noise(self, x, timestep, model_options={}, seed=None): + latent_type = model_options['transformer_options'].get('latent_type', 'xt') + positive = self.conds.get(f'{latent_type}_positive', self.conds.get('xt_positive')) + negative = self.conds.get(f'{latent_type}_negative', self.conds.get('xt_negative')) + positive = self.conds.get('xt_positive') if positive is None else positive + negative = self.conds.get('xt_negative') if negative is None else negative + cfg = self.cfgs.get(latent_type, self.cfg) + + model_options['transformer_options']['yt_positive'] = self.conds.get('yt_positive') + model_options['transformer_options']['yt_negative'] = self.conds.get('yt_negative') + + return sampling_function(self.inner_model, x, timestep, negative, positive, cfg, model_options=model_options, seed=seed) + + + +class SharkSampler: + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "noise_type_init": (NOISE_GENERATOR_NAMES_SIMPLE, {"default": "gaussian"}), + "noise_stdev": ("FLOAT", {"default": 1.0, "min": -10000.0, "max": 10000.0, "step":0.01, "round": False, }), + "noise_seed": ("INT", {"default": 0, "min": -1, "max": 0xffffffffffffffff}), + "sampler_mode": (['unsample', 'standard', 'resample'], {"default": "standard"}), + "scheduler": (get_res4lyf_scheduler_list(), {"default": "beta57"},), + "steps": ("INT", {"default": 30, "min": 1, "max": 10000.0}), + "denoise": ("FLOAT", {"default": 1.0, "min": -10000.0, "max": 10000.0, "step":0.01}), + "denoise_alt": ("FLOAT", {"default": 1.0, "min": -10000.0, "max": 10000.0, "step":0.01}), + "cfg": ("FLOAT", {"default": 5.5, "min": -10000.0, "max": 10000.0, "step":0.01, "round": False, "tooltip": "Negative values use channelwise CFG." }), + }, + "optional": { + "model": ("MODEL",), + "positive": ("CONDITIONING", ), + "negative": ("CONDITIONING", ), + "sampler": ("SAMPLER", ), + "sigmas": ("SIGMAS", ), + "latent_image": ("LATENT", ), + "extra_options": ("STRING", {"default": "", "multiline": True}), + "options": ("OPTIONS", ), + } + } + + RETURN_TYPES = ("LATENT", + "LATENT", + "LATENT",) + + RETURN_NAMES = ("output", + "denoised", + "sde_noise",) + + FUNCTION = "main" + CATEGORY = "RES4LYF/samplers" + EXPERIMENTAL = True + + def main(self, + model = None, + cfg : float = 5.5, + scheduler : str = "beta57", + steps : int = 30, + steps_to_run : int = -1, + sampler_mode : str = "standard", + denoise : float = 1.0, + denoise_alt : float = 1.0, + noise_type_init : str = "gaussian", + latent_image : Optional[dict[Tensor]] = None, + + positive = None, + negative = None, + sampler = None, + sigmas : Optional[Tensor] = None, + noise_stdev : float = 1.0, + noise_mean : float = 0.0, + noise_normalize : bool = True, + + d_noise : float = 1.0, + alpha_init : float = -1.0, + k_init : float = 1.0, + cfgpp : float = 0.0, + noise_seed : int = -1, + options = None, + sde_noise = None, + sde_noise_steps : int = 1, + + rebounds : int = 0, + unsample_cfg : float = 1.0, + unsample_eta : float = 0.5, + unsampler_name : str = "none", + unsample_steps_to_run : int = -1, + eta_decay_scale : float = 1.0, + + #ultracascade_stage : str = "stage_UP", + ultracascade_latent_image : Optional[dict[str,Any]] = None, + ultracascade_guide_weights: Optional[Tuple] = None, + + ultracascade_latent_width : int = 0, + ultracascade_latent_height: int = 0, + + extra_options : str = "", + **kwargs, + ): + + + disable_pbar = not comfy.utils.PROGRESS_BAR_ENABLED + + + + # INIT EXTENDABLE OPTIONS INPUTS + + options_mgr = OptionsManager(options, **kwargs) + + extra_options += "\n" + options_mgr.get('extra_options', "") + EO = ExtraOptions(extra_options) + default_dtype = EO("default_dtype", torch.float64) + default_device = EO("work_device", "cuda" if torch.cuda.is_available() else "cpu") + + noise_stdev = options_mgr.get('noise_init_stdev', noise_stdev) + noise_mean = options_mgr.get('noise_init_mean', noise_mean) + noise_type_init = options_mgr.get('noise_type_init', noise_type_init) + d_noise = options_mgr.get('d_noise', d_noise) + alpha_init = options_mgr.get('alpha_init', alpha_init) + k_init = options_mgr.get('k_init', k_init) + sde_noise = options_mgr.get('sde_noise', sde_noise) + sde_noise_steps = options_mgr.get('sde_noise_steps', sde_noise_steps) + rebounds = options_mgr.get('rebounds', rebounds) + unsample_cfg = options_mgr.get('unsample_cfg', unsample_cfg) + unsample_eta = options_mgr.get('unsample_eta', unsample_eta) + unsampler_name = options_mgr.get('unsampler_name', unsampler_name) + unsample_steps_to_run = options_mgr.get('unsample_steps_to_run', unsample_steps_to_run) + + eta_decay_scale = options_mgr.get('eta_decay_scale', eta_decay_scale) + start_at_step = options_mgr.get('start_at_step', -1) + tile_sizes = options_mgr.get('tile_sizes', None) + flow_sync_eps = options_mgr.get('flow_sync_eps', 0.0) + + unsampler_name, _ = process_sampler_name(unsampler_name) + + + #ultracascade_stage = options_mgr.get('ultracascade_stage', ultracascade_stage) + ultracascade_latent_image = options_mgr.get('ultracascade_latent_image', ultracascade_latent_image) + ultracascade_latent_width = options_mgr.get('ultracascade_latent_width', ultracascade_latent_width) + ultracascade_latent_height = options_mgr.get('ultracascade_latent_height', ultracascade_latent_height) + + + if 'BONGMATH' in sampler.extra_options: + sampler.extra_options['start_at_step'] = start_at_step + sampler.extra_options['tile_sizes'] = tile_sizes + + sampler.extra_options['unsample_bongmath'] = options_mgr.get('unsample_bongmath', sampler.extra_options['BONGMATH']) # allow turning off bongmath for unsampling with cycles + sampler.extra_options['flow_sync_eps'] = flow_sync_eps + + is_chained = False + if latent_image is not None: + if 'positive' in latent_image and positive is None: + positive = copy_cond(latent_image['positive']) + if positive is not None and 'control' in positive[0][1]: + for i in range(len(positive)): + positive[i][1]['control'] = latent_image['positive'][i][1]['control'] + if hasattr(latent_image['positive'][i][1]['control'], 'base'): + positive[i][1]['control'].base = latent_image['positive'][i][1]['control'].base + is_chained = True + if 'negative' in latent_image and negative is None: + negative = copy_cond(latent_image['negative']) + if negative is not None and 'control' in negative[0][1]: + for i in range(len(negative)): + negative[i][1]['control'] = latent_image['negative'][i][1]['control'] + if hasattr(latent_image['negative'][i][1]['control'], 'base'): + negative[i][1]['control'].base = latent_image['negative'][i][1]['control'].base + is_chained = True + if 'sampler' in latent_image and sampler is None: + sampler = copy_cond(latent_image['sampler']) #.clone() + is_chained = True + + if 'steps_to_run' in sampler.extra_options: + sampler.extra_options['steps_to_run'] = steps_to_run + + guider_input = options_mgr.get('guider', None) + if guider_input is not None and is_chained is False: + guider = guider_input + work_model = guider.model_patcher + RESplain("Shark: Using model from ClownOptions_GuiderInput: ", guider.model_patcher.model.diffusion_model.__class__.__name__) + RESplain("SharkWarning: \"flow\" guide mode does not work with ClownOptions_GuiderInput") + if hasattr(guider, 'cfg') and guider.cfg is not None: + cfg = guider.cfg + RESplain("Shark: Using cfg from ClownOptions_GuiderInput: ", cfg) + if hasattr(guider, 'original_conds') and guider.original_conds is not None: + if 'positive' in guider.original_conds: + first_ = guider.original_conds['positive'][0]['cross_attn'] + second_ = {k: v for k, v in guider.original_conds['positive'][0].items() if k != 'cross_attn'} + positive = [[first_, second_],] + RESplain("Shark: Using positive cond from ClownOptions_GuiderInput") + if 'negative' in guider.original_conds: + first_ = guider.original_conds['negative'][0]['cross_attn'] + second_ = {k: v for k, v in guider.original_conds['negative'][0].items() if k != 'cross_attn'} + negative = [[first_, second_],] + RESplain("Shark: Using negative cond from ClownOptions_GuiderInput") + else: + guider = None + work_model = model#.clone() + + if latent_image is not None: + latent_image['samples'] = comfy.sample.fix_empty_latent_channels(work_model, latent_image['samples']) + + if positive is None or negative is None: + from ..conditioning import EmptyConditioningGenerator + EmptyCondGen = EmptyConditioningGenerator(work_model) + positive, negative = EmptyCondGen.zero_none_conditionings_([positive, negative]) + + if cfg < 0: + sampler.extra_options['cfg_cw'] = -cfg + cfg = 1.0 + else: + sampler.extra_options.pop("cfg_cw", None) + + + if not EO("disable_dummy_sampler_init"): + sampler_null = comfy.samplers.ksampler("rk_beta", + { + "sampler_mode": "NULL", + }) + if latent_image is not None and 'samples' in latent_image: + latent_vram_factor = EO("latent_vram_factor", 3) + x_null = torch.zeros_like(latent_image['samples']).repeat_interleave(latent_vram_factor, dim=-1) + elif ultracascade_latent_height * ultracascade_latent_width > 0: + x_null = comfy.sample.fix_empty_latent_channels(model, torch.zeros((1,16,ultracascade_latent_height,ultracascade_latent_width))) + else: + print("Fallback: spawning dummy 1,16,256,256 latent.") + x_null = comfy.sample.fix_empty_latent_channels(model, torch.zeros((1,16,256,256))) + _ = comfy.sample.sample_custom(work_model, x_null, cfg, sampler_null, torch.linspace(1, 0, 10).to(x_null.dtype).to(x_null.device), negative, negative, x_null, noise_mask=None, callback=None, disable_pbar=disable_pbar, seed=noise_seed) + + sigma_min = work_model.get_model_object('model_sampling').sigma_min + sigma_max = work_model.get_model_object('model_sampling').sigma_max + + if sampler is None: + raise ValueError("sampler is required") + else: + sampler = copy.deepcopy(sampler) + + + + # INIT SIGMAS + if sigmas is not None: + sigmas = sigmas.clone().to(dtype=default_dtype, device=default_device) # does this type carry into clown after passing through comfy? + sigmas *= denoise # ... otherwise we have to interpolate and that might not be ideal for tiny custom schedules... + else: + sigmas = get_sigmas(work_model, scheduler, steps, abs(denoise)).to(dtype=default_dtype, device=default_device) + sigmas *= denoise_alt + + # USE NULL FLOATS AS "FLAGS" TO PREVENT COMFY NOISE ADDITION + if sampler_mode.startswith("unsample"): + null = torch.tensor([0.0], device=sigmas.device, dtype=sigmas.dtype) + sigmas = torch.flip(sigmas, dims=[0]) + sigmas = torch.cat([sigmas, null]) + + elif sampler_mode.startswith("resample"): + null = torch.tensor([0.0], device=sigmas.device, dtype=sigmas.dtype) + sigmas = torch.cat([null, sigmas]) + sigmas = torch.cat([sigmas, null]) + + + + latent_x = {} + # INIT STATE INFO FOR CONTINUING GENERATION ACROSS MULTIPLE SAMPLER NODES + if latent_image is not None: + latent_x['samples'] = latent_image['samples'].clone() + if 'noise_mask' in latent_image: + latent_x['noise_mask'] = latent_image['noise_mask'].clone() + state_info = copy.deepcopy(latent_image['state_info']) if 'state_info' in latent_image else {} + else: + state_info = {} + state_info_out = {} + + + + # SETUP CONDITIONING EMBEDS + + pos_cond = copy_cond(positive) + neg_cond = copy_cond(negative) + + + + # SETUP FOR ULTRACASCADE IF DETECTED + if work_model.model.model_config.unet_config.get('stable_cascade_stage') == 'up': + + ultracascade_guide_weight = EO("ultracascade_guide_weight", 0.0) + ultracascade_guide_type = EO("ultracascade_guide_type", "residual") + + x_lr = None + if ultracascade_latent_height * ultracascade_latent_width > 0: + x_lr = latent_image['samples'].clone() if latent_image is not None else None + x_lr_bs = 1 if x_lr is None else x_lr.shape[-4] + x_lr_dtype = default_dtype if x_lr is None else x_lr.dtype + x_lr_device = 'cuda' if x_lr is None else x_lr.device + + ultracascade_stage_up_upscale_align_corners = EO("ultracascade_stage_up_upscale_align_corners", False) + ultracascade_stage_up_upscale_mode = EO("ultracascade_stage_up_upscale_mode", "bicubic") + latent_x['samples'] = torch.zeros([x_lr_bs, 16, ultracascade_latent_height, ultracascade_latent_width], dtype=x_lr_dtype, device=x_lr_device) + + data_prev_ = state_info.get('data_prev_') + if EO("ultracascade_stage_up_preserve_data_prev") and data_prev_ is not None: + data_prev_ = data_prev_.squeeze(1) + + if data_prev_.dim() == 4: + data_prev_ = F.interpolate( + data_prev_, + size=latent_x['samples'].shape[-2:], + mode=ultracascade_stage_up_upscale_mode, + align_corners=ultracascade_stage_up_upscale_align_corners + ) + else: + print("data_prev_ upscale failed.") + state_info['data_prev_'] = data_prev_.unsqueeze(1) + + else: + state_info['data_prev_'] = data_prev_ #None # = None was leading to errors even with sampler_mode=standard due to below with = state_info['data_prev_'][batch_num] + + if x_lr is not None: + if x_lr.shape[-2:] != latent_image['samples'].shape[-2:]: + x_height, x_width = latent_image['samples'].shape[-2:] + ultracascade_stage_up_upscale_align_corners = EO("ultracascade_stage_up_upscale_align_corners", False) + ultracascade_stage_up_upscale_mode = EO("ultracascade_stage_up_upscale_mode", "bicubic") + + x_lr = F.interpolate(x_lr, size=(x_height, x_width), mode=ultracascade_stage_up_upscale_mode, align_corners=ultracascade_stage_up_upscale_align_corners) + + ultracascade_guide_weights = initialize_or_scale(ultracascade_guide_weights, ultracascade_guide_weight, MAX_STEPS) + + patch = work_model.model_options.get("transformer_options", {}).get("patches_replace", {}).get("ultracascade", {}).get("main") + if patch is not None: + patch.update(x_lr=x_lr, guide_weights=ultracascade_guide_weights, guide_type=ultracascade_guide_type) + else: + work_model.model.diffusion_model.set_sigmas_schedule(sigmas_schedule = sigmas) + work_model.model.diffusion_model.set_sigmas_prev (sigmas_prev = sigmas[:1]) + work_model.model.diffusion_model.set_guide_weights (guide_weights = ultracascade_guide_weights) + work_model.model.diffusion_model.set_guide_type (guide_type = ultracascade_guide_type) + work_model.model.diffusion_model.set_x_lr (x_lr = x_lr) + + elif work_model.model.model_config.unet_config.get('stable_cascade_stage') == 'b': + #if sampler_mode != "resample": + # state_info['data_prev_'] = None #commented out as it was throwing an error below with = state_info['data_prev_'][batch_num] + + c_pos, c_neg = [], [] + for t in pos_cond: + d_pos = t[1].copy() + d_neg = t[1].copy() + + x_lr = None + if ultracascade_latent_height * ultracascade_latent_width > 0: + x_lr = latent_image['samples'].clone() + latent_x['samples'] = torch.zeros([x_lr.shape[-4], 4, ultracascade_latent_height // 4, ultracascade_latent_width // 4], dtype=x_lr.dtype, device=x_lr.device) + + d_pos['stable_cascade_prior'] = x_lr + + pooled_output = d_neg.get("pooled_output", None) + if pooled_output is not None: + d_neg["pooled_output"] = torch.zeros_like(pooled_output) + + c_pos.append( [t[0], d_pos]) + c_neg.append([torch.zeros_like(t[0]), d_neg]) + pos_cond = c_pos + neg_cond = c_neg + + elif ultracascade_latent_height * ultracascade_latent_width > 0: + latent_x['samples'] = torch.zeros([1, 16, ultracascade_latent_height, ultracascade_latent_width], dtype=default_dtype, device=sigmas.device) + + + + # NOISE, ORTHOGONALIZE, OR ZERO EMBEDS + + if pos_cond is None or neg_cond is None: + from ..conditioning import EmptyConditioningGenerator + EmptyCondGen = EmptyConditioningGenerator(work_model) + pos_cond, neg_cond = EmptyCondGen.zero_none_conditionings_([pos_cond, neg_cond]) + + + + if EO(("cond_noise", "uncond_noise")): + if noise_seed == -1: + cond_seed = torch.initial_seed() + 1 + else: + cond_seed = noise_seed + + t5_seed = EO("t5_seed" , cond_seed) + clip_seed = EO("clip_seed" , cond_seed+1) + t5_noise_type = EO("t5_noise_type" , "gaussian") + clip_noise_type = EO("clip_noise_type" , "gaussian") + t5_noise_sigma_max = EO("t5_noise_sigma_max" , "gaussian") + t5_noise_sigma_min = EO("t5_noise_sigma_min" , "gaussian") + clip_noise_sigma_max = EO("clip_noise_sigma_max", "gaussian") + clip_noise_sigma_min = EO("clip_noise_sigma_min", "gaussian") + + noise_sampler_t5 = NOISE_GENERATOR_CLASSES_SIMPLE.get( t5_noise_type)(x=pos_cond[0][0], seed= t5_seed, sigma_max= t5_noise_sigma_max, sigma_min= t5_noise_sigma_min, ) + noise_sampler_clip = NOISE_GENERATOR_CLASSES_SIMPLE.get(clip_noise_type)(x=pos_cond[0][1]['pooled_output'], seed=clip_seed, sigma_max=clip_noise_sigma_max, sigma_min=clip_noise_sigma_min, ) + + t5_noise_scale = EO("t5_noise_scale", 1.0) + clip_noise_scale = EO("clip_noise_scale", 1.0) + + if EO("cond_noise"): + t5_noise = noise_sampler_t5 (sigma= t5_noise_sigma_max, sigma_next= t5_noise_sigma_min) + clip_noise = noise_sampler_clip(sigma=clip_noise_sigma_max, sigma_next=clip_noise_sigma_min) + + pos_cond[0][0] = pos_cond[0][0] + t5_noise_scale * (t5_noise - pos_cond[0][0]) + pos_cond[0][1]['pooled_output'] = pos_cond[0][1]['pooled_output'] + clip_noise_scale * (clip_noise - pos_cond[0][1]['pooled_output']) + + if EO("uncond_noise"): + t5_noise = noise_sampler_t5 (sigma= t5_noise_sigma_max, sigma_next= t5_noise_sigma_min) + clip_noise = noise_sampler_clip(sigma=clip_noise_sigma_max, sigma_next=clip_noise_sigma_min) + + neg_cond[0][0] = neg_cond[0][0] + t5_noise_scale * (t5_noise - neg_cond[0][0]) + neg_cond[0][1]['pooled_output'] = neg_cond[0][1]['pooled_output'] + clip_noise_scale * (clip_noise - neg_cond[0][1]['pooled_output']) + + if EO("uncond_ortho"): + neg_cond[0][0] = get_orthogonal(neg_cond[0][0], pos_cond[0][0]) + neg_cond[0][1]['pooled_output'] = get_orthogonal(neg_cond[0][1]['pooled_output'], pos_cond[0][1]['pooled_output']) + + + if "noise_seed" in sampler.extra_options: + if sampler.extra_options['noise_seed'] == -1 and noise_seed != -1: + sampler.extra_options['noise_seed'] = noise_seed + 1 + RESplain("Shark: setting clown noise seed to: ", sampler.extra_options['noise_seed'], debug=True) + + if "sampler_mode" in sampler.extra_options: + sampler.extra_options['sampler_mode'] = sampler_mode + + if "extra_options" in sampler.extra_options: + extra_options += "\n" + extra_options += sampler.extra_options['extra_options'] + sampler.extra_options['extra_options'] = extra_options + + latent_image_batch = {"samples": latent_x['samples'].clone()} + if 'noise_mask' in latent_x and latent_x['noise_mask'] is not None: + latent_image_batch['noise_mask'] = latent_x['noise_mask'].clone() + + # UNROLL BATCHES + + out_samples = [] + out_denoised_samples = [] + out_state_info = [] + + for batch_num in range(latent_image_batch['samples'].shape[0]): + latent_unbatch = copy.deepcopy(latent_x) + latent_unbatch['samples'] = latent_image_batch['samples'][batch_num].clone().unsqueeze(0) + + if 'BONGMATH' in sampler.extra_options: + sampler.extra_options['batch_num'] = batch_num + + + if noise_seed == -1 and sampler_mode in {"unsample", "resample"}: + if latent_image.get('state_info', {}).get('last_rng', None) is not None: + seed = torch.initial_seed() + batch_num + else: + seed = torch.initial_seed() + 1 + batch_num + else: + if EO("lock_batch_seed"): + seed = noise_seed + else: + seed = noise_seed + batch_num + torch .manual_seed(seed) + torch.cuda.manual_seed(seed) + + + x = latent_unbatch["samples"].clone().to(default_dtype) # does this type carry into clown after passing through comfy? + + + + if sde_noise is None and sampler_mode.startswith("unsample"): + sde_noise = [] + else: + sde_noise_steps = 1 + + for total_steps_iter in range (sde_noise_steps): + + if noise_type_init == "none" or noise_stdev == 0.0: + noise = torch.zeros_like(x) + else: + RESplain("Initial latent noise seed: ", seed, debug=True) + + noise_sampler_init = NOISE_GENERATOR_CLASSES_SIMPLE.get(noise_type_init)(x=x, seed=seed, sigma_max=sigma_max, sigma_min=sigma_min) + + if noise_type_init == "fractal": + noise_sampler_init.alpha = alpha_init + noise_sampler_init.k = k_init + noise_sampler_init.scale = 0.1 + + """if EO("rare_noise"): + noise, _, _ = sample_most_divergent_noise(noise_sampler_init, sigma_max, sigma_min, EO("rare_noise", 100)) + else: + noise = noise_sampler_init(sigma=sigma_max * noise_stdev, sigma_next=sigma_min)""" + noise = noise_sampler_init(sigma=sigma_max * noise_stdev, sigma_next=sigma_min) # is sigma_max * noise_stdev really a good idea here? + + + + if noise_normalize and noise.std() > 0: + channelwise = EO("init_noise_normalize_channelwise", "true") + channelwise = True if channelwise == "true" else False + noise = normalize_zscore(noise, channelwise=channelwise, inplace=True) + + noise *= noise_stdev + noise = (noise - noise.mean()) + noise_mean + + if 'BONGMATH' in sampler.extra_options: + sampler.extra_options['noise_initial'] = noise + sampler.extra_options['image_initial'] = x + + noise_mask = latent_unbatch["noise_mask"] if "noise_mask" in latent_unbatch else None + + x0_output = {} + + if latent_image is not None and 'state_info' in latent_image and 'sigmas' in latent_image['state_info']: + steps_len = max(sigmas.shape[-1] - 1, latent_image['state_info']['sigmas'].shape[-1]-1) + else: + steps_len = sigmas.shape[-1]-1 + callback = latent_preview.prepare_callback(work_model, steps_len, x0_output) + + if 'BONGMATH' in sampler.extra_options: # verify the sampler is rk_sampler_beta() + sampler.extra_options['state_info'] = copy.deepcopy(state_info) ############################## + if state_info != {} and state_info != {'data_prev_': None}: #second condition is for ultracascade + sampler.extra_options['state_info']['raw_x'] = state_info['raw_x'] [batch_num] + sampler.extra_options['state_info']['data_prev_'] = state_info['data_prev_'] [batch_num] + sampler.extra_options['state_info']['last_rng'] = state_info['last_rng'] [batch_num] + sampler.extra_options['state_info']['last_rng_substep'] = state_info['last_rng_substep'][batch_num] + #state_info = copy.deepcopy(latent_image['state_info']) if 'state_info' in latent_image else {} + state_info_out = {} + sampler.extra_options['state_info_out'] = state_info_out + + if type(pos_cond[0][0]) == list: + pos_cond_tmp = pos_cond[batch_num] + positive_tmp = positive[batch_num] + else: + pos_cond_tmp = pos_cond + positive_tmp = positive + + for i in range(len(neg_cond)): # crude fix for copy.deepcopy converting superclass into real object + if 'control' in neg_cond[i][1]: + neg_cond[i][1]['control'] = negative[i][1]['control'] + if hasattr(negative[i][1]['control'], 'base'): + neg_cond[i][1]['control'].base = negative[i][1]['control'].base + for i in range(len(pos_cond_tmp)): # crude fix for copy.deepcopy converting superclass into real object + if 'control' in pos_cond_tmp[i][1]: + pos_cond_tmp[i][1]['control'] = positive_tmp[i][1]['control'] + if hasattr(positive[i][1]['control'], 'base'): + pos_cond_tmp[i][1]['control'].base = positive_tmp[i][1]['control'].base + + # SETUP REGIONAL COND + + if pos_cond_tmp[0][1] is not None: + if 'callback_regional' in pos_cond_tmp[0][1]: + pos_cond_tmp = pos_cond_tmp[0][1]['callback_regional'](work_model) + + if 'AttnMask' in pos_cond_tmp[0][1]: + sampler.extra_options['AttnMask'] = pos_cond_tmp[0][1]['AttnMask'] + sampler.extra_options['RegContext'] = pos_cond_tmp[0][1]['RegContext'] + sampler.extra_options['RegParam'] = pos_cond_tmp[0][1]['RegParam'] + + if isinstance(model.model.model_config, (comfy.supported_models.SDXL, comfy.supported_models.SD15)): + latent_up_dummy = F.interpolate(latent_image['samples'].to(torch.float16), size=(latent_image['samples'].shape[-2] * 2, latent_image['samples'].shape[-1] * 2), mode="nearest") + sampler.extra_options['AttnMask'].set_latent(latent_up_dummy) + sampler.extra_options['AttnMask'].generate() + sampler.extra_options['AttnMask'].mask_up = sampler.extra_options['AttnMask'].attn_mask.mask + + latent_down_dummy = F.interpolate(latent_image['samples'].to(torch.float16), size=(latent_image['samples'].shape[-2] // 2, latent_image['samples'].shape[-1] // 2), mode="nearest") + sampler.extra_options['AttnMask'].set_latent(latent_down_dummy) + sampler.extra_options['AttnMask'].generate() + sampler.extra_options['AttnMask'].mask_down = sampler.extra_options['AttnMask'].attn_mask.mask + + if isinstance(model.model.model_config, comfy.supported_models.SD15): + latent_down_dummy = F.interpolate(latent_image['samples'].to(torch.float16), size=(latent_image['samples'].shape[-2] // 4, latent_image['samples'].shape[-1] // 4), mode="nearest") + sampler.extra_options['AttnMask'].set_latent(latent_down_dummy) + sampler.extra_options['AttnMask'].generate() + sampler.extra_options['AttnMask'].mask_down2 = sampler.extra_options['AttnMask'].attn_mask.mask + + if isinstance(model.model.model_config, (comfy.supported_models.Stable_Cascade_C)): + latent_up_dummy = F.interpolate(latent_image['samples'].to(torch.float16), size=(latent_image['samples'].shape[-2] * 2, latent_image['samples'].shape[-1] * 2), mode="nearest") + sampler.extra_options['AttnMask'].set_latent(latent_up_dummy) + # cascade concats 4 + 4 tokens (clip_text_pooled, clip_img) + sampler.extra_options['AttnMask'].context_lens = [context_len + 8 for context_len in sampler.extra_options['AttnMask'].context_lens] + sampler.extra_options['AttnMask'].text_len = sum(sampler.extra_options['AttnMask'].context_lens) + else: + sampler.extra_options['AttnMask'].set_latent(latent_image['samples']) + sampler.extra_options['AttnMask'].generate() + + if neg_cond[0][1] is not None: + if 'callback_regional' in neg_cond[0][1]: + neg_cond = neg_cond[0][1]['callback_regional'](work_model) + + if 'AttnMask' in neg_cond[0][1]: + sampler.extra_options['AttnMask_neg'] = neg_cond[0][1]['AttnMask'] + sampler.extra_options['RegContext_neg'] = neg_cond[0][1]['RegContext'] + sampler.extra_options['RegParam_neg'] = neg_cond[0][1]['RegParam'] + + if isinstance(model.model.model_config, (comfy.supported_models.SDXL, comfy.supported_models.SD15)): + latent_up_dummy = F.interpolate(latent_image['samples'].to(torch.float16), size=(latent_image['samples'].shape[-2] * 2, latent_image['samples'].shape[-1] * 2), mode="nearest") + sampler.extra_options['AttnMask_neg'].set_latent(latent_up_dummy) + sampler.extra_options['AttnMask_neg'].generate() + sampler.extra_options['AttnMask_neg'].mask_up = sampler.extra_options['AttnMask_neg'].attn_mask.mask + + latent_down_dummy = F.interpolate(latent_image['samples'].to(torch.float16), size=(latent_image['samples'].shape[-2] // 2, latent_image['samples'].shape[-1] // 2), mode="nearest") + sampler.extra_options['AttnMask_neg'].set_latent(latent_down_dummy) + sampler.extra_options['AttnMask_neg'].generate() + sampler.extra_options['AttnMask_neg'].mask_down = sampler.extra_options['AttnMask_neg'].attn_mask.mask + + if isinstance(model.model.model_config, comfy.supported_models.SD15): + latent_down_dummy = F.interpolate(latent_image['samples'].to(torch.float16), size=(latent_image['samples'].shape[-2] // 4, latent_image['samples'].shape[-1] // 4), mode="nearest") + sampler.extra_options['AttnMask_neg'].set_latent(latent_down_dummy) + sampler.extra_options['AttnMask_neg'].generate() + sampler.extra_options['AttnMask_neg'].mask_down2 = sampler.extra_options['AttnMask_neg'].attn_mask.mask + + if isinstance(model.model.model_config, (comfy.supported_models.Stable_Cascade_C)): + latent_up_dummy = F.interpolate(latent_image['samples'].to(torch.float16), size=(latent_image['samples'].shape[-2] * 2, latent_image['samples'].shape[-1] * 2), mode="nearest") + sampler.extra_options['AttnMask'].set_latent(latent_up_dummy) + # cascade concats 4 + 4 tokens (clip_text_pooled, clip_img) + sampler.extra_options['AttnMask'].context_lens = [context_len + 8 for context_len in sampler.extra_options['AttnMask'].context_lens] + sampler.extra_options['AttnMask'].text_len = sum(sampler.extra_options['AttnMask'].context_lens) + else: + sampler.extra_options['AttnMask_neg'].set_latent(latent_image['samples']) + sampler.extra_options['AttnMask_neg'].generate() + + + + + + if guider is None: + guider = SharkGuider(work_model) + flow_cond = options_mgr.get('flow_cond', {}) + if flow_cond != {} and 'yt_positive' in flow_cond and not 'yt_inv_positive' in flow_cond: #and not 'yt_inv;_positive' in flow_cond: # typo??? + guider.set_conds(yt_positive=flow_cond.get('yt_positive'), yt_negative=flow_cond.get('yt_negative'),) + guider.set_cfgs(yt=flow_cond.get('yt_cfg'), xt=cfg) + elif flow_cond != {} and 'yt_positive' in flow_cond and 'yt_inv_positive' in flow_cond: + guider.set_conds(yt_positive=flow_cond.get('yt_positive'), yt_negative=flow_cond.get('yt_negative'), yt_inv_positive=flow_cond.get('yt_inv_positive'), yt_inv_negative=flow_cond.get('yt_inv_negative'),) + guider.set_cfgs(yt=flow_cond.get('yt_cfg'), yt_inv=flow_cond.get('yt_inv_cfg'), xt=cfg) + else: + guider.set_cfgs(xt=cfg) + + guider.set_conds(xt_positive=pos_cond_tmp, xt_negative=neg_cond) + + elif type(guider) == SharkGuider: + guider.set_cfgs(xt=cfg) + guider.set_conds(xt_positive=pos_cond_tmp, xt_negative=neg_cond) + else: + try: + guider.set_cfg(cfg) + except: + RESplain("SharkWarning: guider.set_cfg failed but assuming cfg already set correctly.") + try: + guider.set_conds(pos_cond_tmp, neg_cond) + except: + RESplain("SharkWarning: guider.set_conds failed but assuming conds already set correctly.") + + if rebounds > 0: + cfgs_cached = guider.cfgs + steps_to_run_cached = sampler.extra_options['steps_to_run'] + eta_cached = sampler.extra_options['eta'] + eta_substep_cached = sampler.extra_options['eta_substep'] + + etas_cached = sampler.extra_options['etas'].clone() + etas_substep_cached = sampler.extra_options['etas_substep'].clone() + + unsample_etas = torch.full_like(etas_cached, unsample_eta) + rk_type_cached = sampler.extra_options['rk_type'] + + if sampler.extra_options['sampler_mode'] == "unsample": + guider.cfgs = { + 'xt': unsample_cfg, + 'yt': unsample_cfg, + } + if unsample_eta != -1.0: + sampler.extra_options['eta_substep'] = unsample_eta + sampler.extra_options['eta'] = unsample_eta + sampler.extra_options['etas_substep'] = unsample_etas + sampler.extra_options['etas'] = unsample_etas + if unsampler_name != "none": + sampler.extra_options['rk_type'] = unsampler_name + if unsample_steps_to_run > -1: + sampler.extra_options['steps_to_run'] = unsample_steps_to_run + + else: + guider.cfgs = cfgs_cached + + guider.cfgs = cfgs_cached + sampler.extra_options['steps_to_run'] = steps_to_run_cached + + eta_decay = eta_cached + eta_substep_decay = eta_substep_cached + unsample_eta_decay = unsample_eta + + etas_decay = etas_cached + etas_substep_decay = etas_substep_cached + unsample_etas_decay = unsample_etas + + samples = guider.sample(noise, x.clone(), sampler, sigmas, denoise_mask=noise_mask, callback=callback, disable_pbar=disable_pbar, seed=noise_seed) + + if rebounds > 0: + noise_seed_cached = sampler.extra_options['noise_seed'] + cfgs_cached = guider.cfgs + sampler_mode_cached = sampler.extra_options['sampler_mode'] + + for restarts_iter in range(rebounds): + sampler.extra_options['state_info'] = sampler.extra_options['state_info_out'] + + #steps = sampler.extra_options['state_info_out']['sigmas'].shape[-1] - 3 + sigmas = sampler.extra_options['state_info_out']['sigmas'] if sigmas is None else sigmas + #if len(sigmas) > 2 and sigmas[1] < sigmas[2] and sampler.extra_options['state_info_out']['sampler_mode'] == "unsample": # and sampler_mode == "resample": + # sigmas = torch.flip(sigmas, dims=[0]) + + if sampler.extra_options['sampler_mode'] == "standard": + sampler.extra_options['sampler_mode'] = "unsample" + elif sampler.extra_options['sampler_mode'] == "unsample": + sampler.extra_options['sampler_mode'] = "resample" + elif sampler.extra_options['sampler_mode'] == "resample": + sampler.extra_options['sampler_mode'] = "unsample" + + sampler.extra_options['noise_seed'] = -1 + + if sampler.extra_options['sampler_mode'] == "unsample": + guider.cfgs = { + 'xt': unsample_cfg, + 'yt': unsample_cfg, + } + if unsample_eta != -1.0: + sampler.extra_options['eta_substep'] = unsample_eta_decay + sampler.extra_options['eta'] = unsample_eta_decay + sampler.extra_options['etas_substep'] = unsample_etas + sampler.extra_options['etas'] = unsample_etas + else: + sampler.extra_options['eta_substep'] = eta_substep_decay + sampler.extra_options['eta'] = eta_decay + sampler.extra_options['etas_substep'] = etas_substep_decay + sampler.extra_options['etas'] = etas_decay + if unsampler_name != "none": + sampler.extra_options['rk_type'] = unsampler_name + if unsample_steps_to_run > -1: + sampler.extra_options['steps_to_run'] = unsample_steps_to_run + else: + guider.cfgs = cfgs_cached + sampler.extra_options['eta_substep'] = eta_substep_decay + sampler.extra_options['eta'] = eta_decay + sampler.extra_options['etas_substep'] = etas_substep_decay + sampler.extra_options['etas'] = etas_decay + sampler.extra_options['rk_type'] = rk_type_cached + sampler.extra_options['steps_to_run'] = steps_to_run_cached + + + samples = guider.sample(noise, samples.clone(), sampler, sigmas, denoise_mask=noise_mask, callback=callback, disable_pbar=disable_pbar, seed=-1) + + eta_substep_decay *= eta_decay_scale + eta_decay *= eta_decay_scale + unsample_eta_decay *= eta_decay_scale + + etas_substep_decay *= eta_decay_scale + etas_decay *= eta_decay_scale + unsample_etas_decay *= eta_decay_scale + + sampler.extra_options['noise_seed'] = noise_seed_cached + guider.cfgs = cfgs_cached + sampler.extra_options['sampler_mode'] = sampler_mode_cached + sampler.extra_options['eta_substep'] = eta_substep_cached + sampler.extra_options['eta'] = eta_cached + sampler.extra_options['etas_substep'] = etas_substep_cached + sampler.extra_options['etas'] = etas_cached + sampler.extra_options['rk_type'] = rk_type_cached + sampler.extra_options['steps_to_run'] = steps_to_run_cached # TODO: verify this is carried on + + + + out = latent_unbatch.copy() + out["samples"] = samples + + if "x0" in x0_output: + out_denoised = latent_unbatch.copy() + out_denoised["samples"] = work_model.model.process_latent_out(x0_output["x0"].cpu()) + else: + out_denoised = out + + out_samples .append(out ["samples"]) + out_denoised_samples.append(out_denoised["samples"]) + + + + # ACCUMULATE UNSAMPLED SDE NOISE + if total_steps_iter > 1: + if 'raw_x' in state_info_out: + sde_noise_out = state_info_out['raw_x'] + else: + sde_noise_out = out["samples"] + sde_noise.append(normalize_zscore(sde_noise_out, channelwise=True, inplace=True)) + + out_state_info.append(state_info_out) + + # INCREMENT BATCH LOOP + if not EO("lock_batch_seed"): + seed += 1 + if latent_image is not None: #needed for ultracascade, where latent_image input is not really used for stage C/first stage + if latent_image.get('state_info', {}).get('last_rng', None) is None: + torch.manual_seed(seed) + + + gc.collect() + + # STACK SDE NOISES, SAVE STATE INFO + state_info_out = out_state_info[0] + if 'raw_x' in out_state_info[0]: + state_info_out['raw_x'] = torch.stack([out_state_info[_]['raw_x'] for _ in range(len(out_state_info))]) + state_info_out['data_prev_'] = torch.stack([out_state_info[_]['data_prev_'] for _ in range(len(out_state_info))]) + state_info_out['last_rng'] = torch.stack([out_state_info[_]['last_rng'] for _ in range(len(out_state_info))]) + state_info_out['last_rng_substep'] = torch.stack([out_state_info[_]['last_rng_substep'] for _ in range(len(out_state_info))]) + elif 'raw_x' in state_info: + state_info_out = state_info + + out_samples = [tensor.squeeze(0) for tensor in out_samples] + out_denoised_samples = [tensor.squeeze(0) for tensor in out_denoised_samples] + + out ['samples'] = torch.stack(out_samples, dim=0) + out_denoised['samples'] = torch.stack(out_denoised_samples, dim=0) + + out['state_info'] = copy.deepcopy(state_info_out) + state_info = {} + + out['positive'] = positive + out['negative'] = negative + out['model'] = work_model#.clone() + out['sampler'] = sampler + + + return (out, out_denoised, sde_noise,) + + + + +class SharkSampler_Beta: + + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "scheduler": (get_res4lyf_scheduler_list(), {"default": "beta57"},), + "steps": ("INT", {"default": 30, "min": 1, "max": 10000.0}), + "steps_to_run": ("INT", {"default": -1, "min": -1, "max": MAX_STEPS}), + "denoise": ("FLOAT", {"default": 1.0, "min": -10000.0, "max": 10000.0, "step":0.01}), + "cfg": ("FLOAT", {"default": 5.5, "min": -10000.0, "max": 10000.0, "step":0.01, "round": False, "tooltip": "Negative values use channelwise CFG." }), + "seed": ("INT", {"default": 0, "min": -1, "max": 0xffffffffffffffff}), + "sampler_mode": (['unsample', 'standard', 'resample'], {"default": "standard"}), + }, + "optional": { + "model": ("MODEL",), + "positive": ("CONDITIONING", ), + "negative": ("CONDITIONING", ), + "sampler": ("SAMPLER", ), + "sigmas": ("SIGMAS", ), + "latent_image": ("LATENT", ), + "options": ("OPTIONS", ), + } + } + + RETURN_TYPES = ("LATENT", + "LATENT", + "OPTIONS",) + + RETURN_NAMES = ("output", + "denoised", + "options",) + + FUNCTION = "main" + CATEGORY = "RES4LYF/samplers" + + def main(self, + model = None, + cfg : float = 5.5, + scheduler : str = "beta57", + steps : int = 30, + steps_to_run : int = -1, + sampler_mode : str = "standard", + denoise : float = 1.0, + denoise_alt : float = 1.0, + noise_type_init : str = "gaussian", + latent_image : Optional[dict[Tensor]] = None, + + positive = None, + negative = None, + sampler = None, + sigmas : Optional[Tensor] = None, + noise_stdev : float = 1.0, + noise_mean : float = 0.0, + noise_normalize : bool = True, + + d_noise : float = 1.0, + alpha_init : float = -1.0, + k_init : float = 1.0, + cfgpp : float = 0.0, + seed : int = -1, + options = None, + sde_noise = None, + sde_noise_steps : int = 1, + + extra_options : str = "", + **kwargs, + ): + + + options_mgr = OptionsManager(options, **kwargs) + + if denoise < 0: + denoise_alt = -denoise + denoise = 1.0 + + #if 'steps_to_run' in sampler.extra_options: + # sampler.extra_options['steps_to_run'] = steps_to_run + if 'positive' in latent_image and positive is None: + positive = latent_image['positive'] + if 'negative' in latent_image and negative is None: + negative = latent_image['negative'] + if 'sampler' in latent_image and sampler is None: + sampler = latent_image['sampler'] + if 'model' in latent_image and model is None: + model = latent_image['model'] + + #if model.model.model_config.unet_config.get('stable_cascade_stage') == 'b': + # if 'noise_type_sde' in sampler.extra_options: + # noise_type_sde = "pyramid-cascade_B" + # noise_type_sde_substep = "pyramid-cascade_B" + + output, denoised, sde_noise = SharkSampler().main( + model = model, + cfg = cfg, + scheduler = scheduler, + steps = steps, + steps_to_run = steps_to_run, + denoise = denoise, + latent_image = latent_image, + positive = positive, + negative = negative, + sampler = sampler, + cfgpp = cfgpp, + noise_seed = seed, + options = options, + sde_noise = sde_noise, + sde_noise_steps = sde_noise_steps, + noise_type_init = noise_type_init, + noise_stdev = noise_stdev, + sampler_mode = sampler_mode, + denoise_alt = denoise_alt, + sigmas = sigmas, + + extra_options = extra_options) + + return (output, denoised,options_mgr.as_dict()) + + + + + +class SharkChainsampler_Beta(SharkSampler_Beta): + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "steps_to_run": ("INT", {"default": -1, "min": -1, "max": MAX_STEPS}), + "cfg": ("FLOAT", {"default": 5.5, "min": -10000.0, "max": 10000.0, "step":0.01, "round": False, "tooltip": "Negative values use channelwise CFG." }), + "sampler_mode": (['unsample', 'resample'], {"default": "resample"}), + }, + "optional": { + "model": ("MODEL",), + "positive": ("CONDITIONING", ), + "negative": ("CONDITIONING", ), + "sampler": ("SAMPLER", ), + "sigmas": ("SIGMAS", ), + "latent_image": ("LATENT", ), + "options": ("OPTIONS", ), + } + } + + def main(self, + model = None, + steps_to_run = -1, + cfg = 5.5, + latent_image = None, + sigmas = None, + sampler_mode = "", + seed : int = -1, + **kwargs): + + steps = latent_image['state_info']['sigmas'].shape[-1] - 3 + sigmas = latent_image['state_info']['sigmas'] if sigmas is None else sigmas + if len(sigmas) > 2 and sigmas[1] < sigmas[2] and latent_image['state_info']['sampler_mode'] == "unsample" and sampler_mode == "resample": + sigmas = torch.flip(sigmas, dims=[0]) + + return super().main(model=model, sampler_mode=sampler_mode, steps_to_run=steps_to_run, sigmas=sigmas, steps=steps, cfg=cfg, seed=seed, latent_image=latent_image, **kwargs) + + + + + +class ClownSamplerAdvanced_Beta: + @classmethod + def INPUT_TYPES(cls): + return {"required": + { + "noise_type_sde": (NOISE_GENERATOR_NAMES_SIMPLE, {"default": "gaussian"}), + "noise_type_sde_substep": (NOISE_GENERATOR_NAMES_SIMPLE, {"default": "gaussian"}), + "noise_mode_sde": (NOISE_MODE_NAMES, {"default": 'hard', "tooltip": "How noise scales with the sigma schedule. Hard is the most aggressive, the others start strong and drop rapidly."}), + "noise_mode_sde_substep": (NOISE_MODE_NAMES, {"default": 'hard', "tooltip": "How noise scales with the sigma schedule. Hard is the most aggressive, the others start strong and drop rapidly."}), + "overshoot_mode": (NOISE_MODE_NAMES, {"default": 'hard', "tooltip": "How step size overshoot scales with the sigma schedule. Hard is the most aggressive, the others start strong and drop rapidly."}), + "overshoot_mode_substep": (NOISE_MODE_NAMES, {"default": 'hard', "tooltip": "How substep size overshoot scales with the sigma schedule. Hard is the most aggressive, the others start strong and drop rapidly."}), + "eta": ("FLOAT", {"default": 0.5, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Calculated noise amount to be added, then removed, after each step."}), + "eta_substep": ("FLOAT", {"default": 0.5, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Calculated noise amount to be added, then removed, after each step."}), + "overshoot": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Boost the size of each denoising step, then rescale to match the original. Has a softening effect."}), + "overshoot_substep": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Boost the size of each denoising substep, then rescale to match the original. Has a softening effect."}), + "noise_scaling_weight": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Set to positive values to create a sharper, grittier, more detailed image. Set to negative values to soften and deepen the colors."}), + "noise_boost_step": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Set to positive values to create a sharper, grittier, more detailed image. Set to negative values to soften and deepen the colors."}), + "noise_boost_substep": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Set to positive values to create a sharper, grittier, more detailed image. Set to negative values to soften and deepen the colors."}), + "noise_anchor": ("FLOAT", {"default": 1.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Typically set to between 1.0 and 0.0. Lower values cerate a grittier, more detailed image."}), + "s_noise": ("FLOAT", {"default": 1.0, "min": -10000, "max": 10000, "step":0.01, "tooltip": "Adds extra SDE noise. Values around 1.03-1.07 can lead to a moderate boost in detail and paint textures."}), + "s_noise_substep": ("FLOAT", {"default": 1.0, "min": -10000, "max": 10000, "step":0.01, "tooltip": "Adds extra SDE noise. Values around 1.03-1.07 can lead to a moderate boost in detail and paint textures."}), + "d_noise": ("FLOAT", {"default": 1.0, "min": -10000, "max": 10000, "step":0.01, "tooltip": "Downscales the sigma schedule. Values around 0.98-0.95 can lead to a large boost in detail and paint textures."}), + "momentum": ("FLOAT", {"default": 1.0, "min": -10000, "max": 10000, "step":0.01, "tooltip": "Accelerate convergence with positive values when sampling, negative values when unsampling."}), + "noise_seed_sde": ("INT", {"default": -1, "min": -1, "max": 0xffffffffffffffff}), + "sampler_name": (get_sampler_name_list(), {"default": get_default_sampler_name()}), + + "implicit_type": (IMPLICIT_TYPE_NAMES, {"default": "predictor-corrector"}), + "implicit_type_substeps": (IMPLICIT_TYPE_NAMES, {"default": "predictor-corrector"}), + "implicit_steps": ("INT", {"default": 0, "min": 0, "max": 10000}), + "implicit_substeps": ("INT", {"default": 0, "min": 0, "max": 10000}), + "bongmath": ("BOOLEAN", {"default": True}), + }, + "optional": + { + "guides": ("GUIDES", ), + "automation": ("AUTOMATION", ), + "extra_options": ("STRING", {"default": "", "multiline": True}), + "options": ("OPTIONS", ), + } + } + + RETURN_TYPES = ("SAMPLER",) + RETURN_NAMES = ("sampler", ) + FUNCTION = "main" + CATEGORY = "RES4LYF/samplers" + EXPERIMENTAL = True + + def main(self, + noise_type_sde : str = "gaussian", + noise_type_sde_substep : str = "gaussian", + noise_mode_sde : str = "hard", + overshoot_mode : str = "hard", + overshoot_mode_substep : str = "hard", + + eta : float = 0.5, + eta_substep : float = 0.5, + momentum : float = 0.0, + + + + noise_scaling_weight : float = 0.0, + noise_scaling_type : str = "sampler", + noise_scaling_mode : str = "linear", + noise_scaling_eta : float = 0.0, + noise_scaling_cycles : int = 1, + + noise_scaling_weights : Optional[Tensor] = None, + noise_scaling_etas : Optional[Tensor] = None, + + noise_boost_step : float = 0.0, + noise_boost_substep : float = 0.0, + noise_boost_normalize : bool = True, + noise_anchor : float = 1.0, + + s_noise : float = 1.0, + s_noise_substep : float = 1.0, + d_noise : float = 1.0, + d_noise_start_step : int = 0, + d_noise_inv : float = 1.0, + d_noise_inv_start_step : int = 0, + + + + alpha_sde : float = -1.0, + k_sde : float = 1.0, + cfgpp : float = 0.0, + c1 : float = 0.0, + c2 : float = 0.5, + c3 : float = 1.0, + noise_seed_sde : int = -1, + sampler_name : str = "res_2m", + implicit_sampler_name : str = "gauss-legendre_2s", + + implicit_substeps : int = 0, + implicit_steps : int = 0, + + rescale_floor : bool = True, + sigmas_override : Optional[Tensor] = None, + + guides = None, + options = None, + sde_noise = None, + sde_noise_steps : int = 1, + + extra_options : str = "", + automation = None, + etas : Optional[Tensor] = None, + etas_substep : Optional[Tensor] = None, + s_noises : Optional[Tensor] = None, + s_noises_substep : Optional[Tensor] = None, + epsilon_scales : Optional[Tensor] = None, + regional_conditioning_weights : Optional[Tensor] = None, + frame_weights_mgr = None, + noise_mode_sde_substep : str = "hard", + + overshoot : float = 0.0, + overshoot_substep : float = 0.0, + + bongmath : bool = True, + + implicit_type : str = "predictor-corrector", + implicit_type_substeps : str = "predictor-corrector", + + rk_swap_step : int = MAX_STEPS, + rk_swap_print : bool = False, + rk_swap_threshold : float = 0.0, + rk_swap_type : str = "", + + steps_to_run : int = -1, + + sde_mask : Optional[Tensor] = None, + + **kwargs, + ): + + + + options_mgr = OptionsManager(options, **kwargs) + extra_options += "\n" + options_mgr.get('extra_options', "") + EO = ExtraOptions(extra_options) + default_dtype = EO("default_dtype", torch.float64) + + + + sampler_name, implicit_sampler_name = process_sampler_name(sampler_name) + + implicit_steps_diag = implicit_substeps + implicit_steps_full = implicit_steps + + if noise_mode_sde == "none": + eta = 0.0 + noise_mode_sde = "hard" + + noise_type_sde = options_mgr.get('noise_type_sde' , noise_type_sde) + noise_mode_sde = options_mgr.get('noise_mode_sde' , noise_mode_sde) + eta = options_mgr.get('eta' , eta) + eta_substep = options_mgr.get('eta_substep' , eta_substep) + + + + noise_scaling_weight = options_mgr.get('noise_scaling_weight' , noise_scaling_weight) + noise_scaling_type = options_mgr.get('noise_scaling_type' , noise_scaling_type) + noise_scaling_mode = options_mgr.get('noise_scaling_mode' , noise_scaling_mode) + noise_scaling_eta = options_mgr.get('noise_scaling_eta' , noise_scaling_eta) + noise_scaling_cycles = options_mgr.get('noise_scaling_cycles' , noise_scaling_cycles) + + noise_scaling_weights = options_mgr.get('noise_scaling_weights' , noise_scaling_weights) + noise_scaling_etas = options_mgr.get('noise_scaling_etas' , noise_scaling_etas) + + noise_boost_step = options_mgr.get('noise_boost_step' , noise_boost_step) + noise_boost_substep = options_mgr.get('noise_boost_substep' , noise_boost_substep) + noise_boost_normalize = options_mgr.get('noise_boost_normalize' , noise_boost_normalize) + noise_anchor = options_mgr.get('noise_anchor' , noise_anchor) + + s_noise = options_mgr.get('s_noise' , s_noise) + s_noise_substep = options_mgr.get('s_noise_substep' , s_noise_substep) + d_noise = options_mgr.get('d_noise' , d_noise) + d_noise_start_step = options_mgr.get('d_noise_start_step' , d_noise_start_step) + d_noise_inv = options_mgr.get('d_noise_inv' , d_noise_inv) + d_noise_inv_start_step = options_mgr.get('d_noise_inv_start_step', d_noise_inv_start_step) + + + + alpha_sde = options_mgr.get('alpha_sde' , alpha_sde) + k_sde = options_mgr.get('k_sde' , k_sde) + c1 = options_mgr.get('c1' , c1) + c2 = options_mgr.get('c2' , c2) + c3 = options_mgr.get('c3' , c3) + + frame_weights_mgr = options_mgr.get('frame_weights_mgr', frame_weights_mgr) + sde_noise = options_mgr.get('sde_noise' , sde_noise) + sde_noise_steps = options_mgr.get('sde_noise_steps' , sde_noise_steps) + + rk_swap_step = options_mgr.get('rk_swap_step' , rk_swap_step) + rk_swap_print = options_mgr.get('rk_swap_print' , rk_swap_print) + rk_swap_threshold = options_mgr.get('rk_swap_threshold', rk_swap_threshold) + rk_swap_type = options_mgr.get('rk_swap_type' , rk_swap_type) + + steps_to_run = options_mgr.get('steps_to_run' , steps_to_run) + + noise_seed_sde = options_mgr.get('noise_seed_sde' , noise_seed_sde) + momentum = options_mgr.get('momentum' , momentum) + + sde_mask = options_mgr.get('sde_mask' , sde_mask) + + + rescale_floor = EO("rescale_floor") + + if automation is not None: + etas = automation['etas'] if 'etas' in automation else None + etas_substep = automation['etas_substep'] if 'etas_substep' in automation else None + s_noises = automation['s_noises'] if 's_noises' in automation else None + s_noises_substep = automation['s_noises_substep'] if 's_noises_substep' in automation else None + epsilon_scales = automation['epsilon_scales'] if 'epsilon_scales' in automation else None + frame_weights_mgr = automation['frame_weights_mgr'] if 'frame_weights_mgr' in automation else None + + etas = options_mgr.get('etas', etas) + etas_substep = options_mgr.get('etas_substep', etas_substep) + + s_noises = options_mgr.get('s_noises', s_noises) + s_noises_substep = options_mgr.get('s_noises_substep', s_noises_substep) + + etas = initialize_or_scale(etas, eta, MAX_STEPS).to(default_dtype) + etas_substep = initialize_or_scale(etas_substep, eta_substep, MAX_STEPS).to(default_dtype) + s_noises = initialize_or_scale(s_noises, s_noise, MAX_STEPS).to(default_dtype) + s_noises_substep = initialize_or_scale(s_noises_substep, s_noise_substep, MAX_STEPS).to(default_dtype) + + etas = F.pad(etas, (0, MAX_STEPS), value=0.0) + etas_substep = F.pad(etas_substep, (0, MAX_STEPS), value=0.0) + s_noises = F.pad(s_noises, (0, MAX_STEPS), value=1.0) + s_noises_substep = F.pad(s_noises_substep, (0, MAX_STEPS), value=1.0) + + if sde_noise is None: + sde_noise = [] + else: + sde_noise = copy.deepcopy(sde_noise) + sde_noise = normalize_zscore(sde_noise, channelwise=True, inplace=True) + + + sampler = comfy.samplers.ksampler("rk_beta", + { + "eta" : eta, + "eta_substep" : eta_substep, + + "alpha" : alpha_sde, + "k" : k_sde, + "c1" : c1, + "c2" : c2, + "c3" : c3, + "cfgpp" : cfgpp, + + "noise_sampler_type" : noise_type_sde, + "noise_sampler_type_substep" : noise_type_sde_substep, + "noise_mode_sde" : noise_mode_sde, + "noise_seed" : noise_seed_sde, + "rk_type" : sampler_name, + "implicit_sampler_name" : implicit_sampler_name, + + "implicit_steps_diag" : implicit_steps_diag, + "implicit_steps_full" : implicit_steps_full, + + "LGW_MASK_RESCALE_MIN" : rescale_floor, + "sigmas_override" : sigmas_override, + "sde_noise" : sde_noise, + + "extra_options" : extra_options, + "sampler_mode" : "standard", + + "etas" : etas, + "etas_substep" : etas_substep, + + + + "s_noises" : s_noises, + "s_noises_substep" : s_noises_substep, + "epsilon_scales" : epsilon_scales, + "regional_conditioning_weights" : regional_conditioning_weights, + + "guides" : guides, + "frame_weights_mgr" : frame_weights_mgr, + "eta_substep" : eta_substep, + "noise_mode_sde_substep" : noise_mode_sde_substep, + + + + "noise_scaling_weight" : noise_scaling_weight, + "noise_scaling_type" : noise_scaling_type, + "noise_scaling_mode" : noise_scaling_mode, + "noise_scaling_eta" : noise_scaling_eta, + "noise_scaling_cycles" : noise_scaling_cycles, + + "noise_scaling_weights" : noise_scaling_weights, + "noise_scaling_etas" : noise_scaling_etas, + + "noise_boost_step" : noise_boost_step, + "noise_boost_substep" : noise_boost_substep, + "noise_boost_normalize" : noise_boost_normalize, + "noise_anchor" : noise_anchor, + + "s_noise" : s_noise, + "s_noise_substep" : s_noise_substep, + "d_noise" : d_noise, + "d_noise_start_step" : d_noise_start_step, + "d_noise_inv" : d_noise_inv, + "d_noise_inv_start_step" : d_noise_inv_start_step, + + + + "overshoot_mode" : overshoot_mode, + "overshoot_mode_substep" : overshoot_mode_substep, + "overshoot" : overshoot, + "overshoot_substep" : overshoot_substep, + "BONGMATH" : bongmath, + + "implicit_type" : implicit_type, + "implicit_type_substeps" : implicit_type_substeps, + + "rk_swap_step" : rk_swap_step, + "rk_swap_print" : rk_swap_print, + "rk_swap_threshold" : rk_swap_threshold, + "rk_swap_type" : rk_swap_type, + + "steps_to_run" : steps_to_run, + + "sde_mask" : sde_mask, + + "momentum" : momentum, + }) + + + return (sampler, ) + + + + + + + +class ClownsharKSampler_Beta: + @classmethod + def INPUT_TYPES(cls): + inputs = {"required": + { + "eta": ("FLOAT", {"default": 0.5, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Calculated noise amount to be added, then removed, after each step."}), + "sampler_name": (get_sampler_name_list (), {"default": get_default_sampler_name()}), + "scheduler": (get_res4lyf_scheduler_list(), {"default": "beta57"},), + "steps": ("INT", {"default": 30, "min": 1, "max": MAX_STEPS}), + "steps_to_run": ("INT", {"default": -1, "min": -1, "max": MAX_STEPS}), + "denoise": ("FLOAT", {"default": 1.0, "min": -10000, "max": MAX_STEPS, "step":0.01}), + "cfg": ("FLOAT", {"default": 5.5, "min": -100.0, "max": 100.0, "step":0.01, "round": False, }), + "seed": ("INT", {"default": 0, "min": -1, "max": 0xffffffffffffffff}), + "sampler_mode": (['unsample', 'standard', 'resample'], {"default": "standard"}), + "bongmath": ("BOOLEAN", {"default": True}), + }, + "optional": + { + "model": ("MODEL",), + "positive": ("CONDITIONING",), + "negative": ("CONDITIONING",), + "latent_image": ("LATENT",), + "sigmas": ("SIGMAS",), + "guides": ("GUIDES",), + "options": ("OPTIONS", {}), + } + } + + return inputs + + RETURN_TYPES = ("LATENT", + "LATENT", + "OPTIONS", + ) + + RETURN_NAMES = ("output", + "denoised", + "options", + ) + + FUNCTION = "main" + CATEGORY = "RES4LYF/samplers" + + def main(self, + model = None, + denoise : float = 1.0, + scheduler : str = "beta57", + cfg : float = 1.0, + seed : int = -1, + positive = None, + negative = None, + latent_image : Optional[dict[Tensor]] = None, + steps : int = 30, + steps_to_run : int = -1, + bongmath : bool = True, + sampler_mode : str = "standard", + + noise_type_sde : str = "gaussian", + noise_type_sde_substep : str = "gaussian", + noise_mode_sde : str = "hard", + noise_mode_sde_substep : str = "hard", + + + overshoot_mode : str = "hard", + overshoot_mode_substep : str = "hard", + overshoot : float = 0.0, + overshoot_substep : float = 0.0, + + eta : float = 0.5, + eta_substep : float = 0.5, + momentum : float = 0.0, + + + + noise_scaling_weight : float = 0.0, + noise_scaling_type : str = "sampler", + noise_scaling_mode : str = "linear", + noise_scaling_eta : float = 0.0, + noise_scaling_cycles : int = 1, + + noise_scaling_weights : Optional[Tensor] = None, + noise_scaling_etas : Optional[Tensor] = None, + + noise_boost_step : float = 0.0, + noise_boost_substep : float = 0.0, + noise_boost_normalize : bool = True, + noise_anchor : float = 1.0, + + s_noise : float = 1.0, + s_noise_substep : float = 1.0, + d_noise : float = 1.0, + d_noise_start_step : int = 0, + d_noise_inv : float = 1.0, + d_noise_inv_start_step : int = 0, + + + + alpha_sde : float = -1.0, + k_sde : float = 1.0, + cfgpp : float = 0.0, + c1 : float = 0.0, + c2 : float = 0.5, + c3 : float = 1.0, + noise_seed_sde : int = -1, + sampler_name : str = "res_2m", + implicit_sampler_name : str = "use_explicit", + + implicit_type : str = "bongmath", + implicit_type_substeps : str = "bongmath", + implicit_steps : int = 0, + implicit_substeps : int = 0, + + sigmas : Optional[Tensor] = None, + sigmas_override : Optional[Tensor] = None, + guides = None, + options = None, + sde_noise = None, + sde_noise_steps : int = 1, + extra_options : str = "", + automation = None, + + epsilon_scales : Optional[Tensor] = None, + regional_conditioning_weights : Optional[Tensor] = None, + frame_weights_mgr = None, + + + rescale_floor : bool = True, + + rk_swap_step : int = MAX_STEPS, + rk_swap_print : bool = False, + rk_swap_threshold : float = 0.0, + rk_swap_type : str = "", + + sde_mask : Optional[Tensor] = None, + + #start_at_step : int = 0, + #stop_at_step : int = MAX_STEPS, + + **kwargs + ): + + options_mgr = OptionsManager(options, **kwargs) + extra_options += "\n" + options_mgr.get('extra_options', "") + + #if model is None: + # model = latent_image['model'] + + + # defaults for ClownSampler + eta_substep = eta + + # defaults for SharkSampler + noise_type_init = "gaussian" + noise_stdev = 1.0 + denoise_alt = 1.0 + channelwise_cfg = False + + if denoise < 0: + denoise_alt = -denoise + denoise = 1.0 + + is_chained = False + + if latent_image is not None and 'positive' in latent_image and positive is None: + positive = latent_image['positive'] + is_chained = True + if latent_image is not None and 'negative' in latent_image and negative is None: + negative = latent_image['negative'] + is_chained = True + if latent_image is not None and 'model' in latent_image and model is None: + model = latent_image['model'] + is_chained = True + + guider = options_mgr.get('guider', None) + if is_chained is False and guider is not None: + model = guider.model_patcher + + if model.model.model_config.unet_config.get('stable_cascade_stage') == 'b': + noise_type_sde = "pyramid-cascade_B" + noise_type_sde_substep = "pyramid-cascade_B" + + + #if options is not None: + #options_mgr = OptionsManager(options_inputs) + noise_seed_sde = options_mgr.get('noise_seed_sde' , noise_seed_sde) + + + noise_type_sde = options_mgr.get('noise_type_sde' , noise_type_sde) + noise_type_sde_substep = options_mgr.get('noise_type_sde_substep', noise_type_sde_substep) + + options_mgr.update('noise_type_sde', noise_type_sde) + options_mgr.update('noise_type_sde_substep', noise_type_sde_substep) + + noise_mode_sde = options_mgr.get('noise_mode_sde' , noise_mode_sde) + noise_mode_sde_substep = options_mgr.get('noise_mode_sde_substep', noise_mode_sde_substep) + + overshoot_mode = options_mgr.get('overshoot_mode' , overshoot_mode) + overshoot_mode_substep = options_mgr.get('overshoot_mode_substep', overshoot_mode_substep) + + eta = options_mgr.get('eta' , eta) + eta_substep = options_mgr.get('eta_substep' , eta_substep) + + options_mgr.update('eta', eta) + options_mgr.update('eta_substep', eta_substep) + + overshoot = options_mgr.get('overshoot' , overshoot) + overshoot_substep = options_mgr.get('overshoot_substep' , overshoot_substep) + + + + noise_scaling_weight = options_mgr.get('noise_scaling_weight' , noise_scaling_weight) + noise_scaling_type = options_mgr.get('noise_scaling_type' , noise_scaling_type) + noise_scaling_mode = options_mgr.get('noise_scaling_mode' , noise_scaling_mode) + noise_scaling_eta = options_mgr.get('noise_scaling_eta' , noise_scaling_eta) + noise_scaling_cycles = options_mgr.get('noise_scaling_cycles' , noise_scaling_cycles) + + noise_scaling_weights = options_mgr.get('noise_scaling_weights' , noise_scaling_weights) + noise_scaling_etas = options_mgr.get('noise_scaling_etas' , noise_scaling_etas) + + noise_boost_step = options_mgr.get('noise_boost_step' , noise_boost_step) + noise_boost_substep = options_mgr.get('noise_boost_substep' , noise_boost_substep) + noise_boost_normalize = options_mgr.get('noise_boost_normalize' , noise_boost_normalize) + noise_anchor = options_mgr.get('noise_anchor' , noise_anchor) + + s_noise = options_mgr.get('s_noise' , s_noise) + s_noise_substep = options_mgr.get('s_noise_substep' , s_noise_substep) + d_noise = options_mgr.get('d_noise' , d_noise) + d_noise_start_step = options_mgr.get('d_noise_start_step' , d_noise_start_step) + d_noise_inv = options_mgr.get('d_noise_inv' , d_noise_inv) + d_noise_inv_start_step = options_mgr.get('d_noise_inv_start_step', d_noise_inv_start_step) + + + momentum = options_mgr.get('momentum' , momentum) + + + implicit_type = options_mgr.get('implicit_type' , implicit_type) + implicit_type_substeps = options_mgr.get('implicit_type_substeps', implicit_type_substeps) + implicit_steps = options_mgr.get('implicit_steps' , implicit_steps) + implicit_substeps = options_mgr.get('implicit_substeps' , implicit_substeps) + + alpha_sde = options_mgr.get('alpha_sde' , alpha_sde) + k_sde = options_mgr.get('k_sde' , k_sde) + c1 = options_mgr.get('c1' , c1) + c2 = options_mgr.get('c2' , c2) + c3 = options_mgr.get('c3' , c3) + + frame_weights_mgr = options_mgr.get('frame_weights_mgr' , frame_weights_mgr) + + sde_noise = options_mgr.get('sde_noise' , sde_noise) + sde_noise_steps = options_mgr.get('sde_noise_steps' , sde_noise_steps) + + extra_options = options_mgr.get('extra_options' , extra_options) + + automation = options_mgr.get('automation' , automation) + + # SharkSampler Options + noise_type_init = options_mgr.get('noise_type_init' , noise_type_init) + noise_stdev = options_mgr.get('noise_stdev' , noise_stdev) + sampler_mode = options_mgr.get('sampler_mode' , sampler_mode) + denoise_alt = options_mgr.get('denoise_alt' , denoise_alt) + + channelwise_cfg = options_mgr.get('channelwise_cfg' , channelwise_cfg) + + options_mgr.update('noise_type_init', noise_type_init) + options_mgr.update('noise_stdev', noise_stdev) + options_mgr.update('denoise_alt', denoise_alt) + #options_mgr.update('channelwise_cfg', channelwise_cfg) + + sigmas = options_mgr.get('sigmas' , sigmas) + + rk_swap_type = options_mgr.get('rk_swap_type' , rk_swap_type) + rk_swap_step = options_mgr.get('rk_swap_step' , rk_swap_step) + rk_swap_threshold = options_mgr.get('rk_swap_threshold' , rk_swap_threshold) + rk_swap_print = options_mgr.get('rk_swap_print' , rk_swap_print) + + sde_mask = options_mgr.get('sde_mask' , sde_mask) + + + #start_at_step = options_mgr.get('start_at_step' , start_at_step) + #stop_at_ste = options_mgr.get('stop_at_step' , stop_at_step) + + if channelwise_cfg: # != 1.0: + cfg = -abs(cfg) # set cfg negative for shark, to flag as cfg_cw + + + + + sampler, = ClownSamplerAdvanced_Beta().main( + noise_type_sde = noise_type_sde, + noise_type_sde_substep = noise_type_sde_substep, + noise_mode_sde = noise_mode_sde, + noise_mode_sde_substep = noise_mode_sde_substep, + + eta = eta, + eta_substep = eta_substep, + + + + overshoot = overshoot, + overshoot_substep = overshoot_substep, + + overshoot_mode = overshoot_mode, + overshoot_mode_substep = overshoot_mode_substep, + + + momentum = momentum, + + alpha_sde = alpha_sde, + k_sde = k_sde, + cfgpp = cfgpp, + c1 = c1, + c2 = c2, + c3 = c3, + sampler_name = sampler_name, + implicit_sampler_name = implicit_sampler_name, + + implicit_type = implicit_type, + implicit_type_substeps = implicit_type_substeps, + implicit_steps = implicit_steps, + implicit_substeps = implicit_substeps, + + rescale_floor = rescale_floor, + sigmas_override = sigmas_override, + + noise_seed_sde = noise_seed_sde, + + guides = guides, + options = options_mgr.as_dict(), + + extra_options = extra_options, + automation = automation, + + + + noise_scaling_weight = noise_scaling_weight, + noise_scaling_type = noise_scaling_type, + noise_scaling_mode = noise_scaling_mode, + noise_scaling_eta = noise_scaling_eta, + noise_scaling_cycles = noise_scaling_cycles, + + noise_scaling_weights = noise_scaling_weights, + noise_scaling_etas = noise_scaling_etas, + + noise_boost_step = noise_boost_step, + noise_boost_substep = noise_boost_substep, + noise_boost_normalize = noise_boost_normalize, + noise_anchor = noise_anchor, + + s_noise = s_noise, + s_noise_substep = s_noise_substep, + d_noise = d_noise, + d_noise_start_step = d_noise_start_step, + d_noise_inv = d_noise_inv, + d_noise_inv_start_step = d_noise_inv_start_step, + + + epsilon_scales = epsilon_scales, + regional_conditioning_weights = regional_conditioning_weights, + frame_weights_mgr = frame_weights_mgr, + + sde_noise = sde_noise, + sde_noise_steps = sde_noise_steps, + + rk_swap_step = rk_swap_step, + rk_swap_print = rk_swap_print, + rk_swap_threshold = rk_swap_threshold, + rk_swap_type = rk_swap_type, + + steps_to_run = steps_to_run, + + sde_mask = sde_mask, + + bongmath = bongmath, + ) + + + output, denoised, sde_noise = SharkSampler().main( + model = model, + cfg = cfg, + scheduler = scheduler, + steps = steps, + steps_to_run = steps_to_run, + denoise = denoise, + latent_image = latent_image, + positive = positive, + negative = negative, + sampler = sampler, + cfgpp = cfgpp, + noise_seed = seed, + options = options_mgr.as_dict(), + sde_noise = sde_noise, + sde_noise_steps = sde_noise_steps, + noise_type_init = noise_type_init, + noise_stdev = noise_stdev, + sampler_mode = sampler_mode, + denoise_alt = denoise_alt, + sigmas = sigmas, + + extra_options = extra_options) + + return (output, denoised, options_mgr.as_dict(),) # {'model':model,},) + + + + + + + + + + + +class ClownsharkChainsampler_Beta(ClownsharKSampler_Beta): + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "eta": ("FLOAT", {"default": 0.5, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Calculated noise amount to be added, then removed, after each step."}), + "sampler_name": (get_sampler_name_list(), {"default": get_default_sampler_name()}), + "steps_to_run": ("INT", {"default": -1, "min": -1, "max": MAX_STEPS}), + "cfg": ("FLOAT", {"default": 5.5, "min": -10000.0, "max": 10000.0, "step":0.01, "round": False, "tooltip": "Negative values use channelwise CFG." }), + "sampler_mode": (['unsample', 'resample'],{"default": "resample"}), + "bongmath": ("BOOLEAN", {"default": True}), + }, + "optional": { + "model": ("MODEL",), + "positive": ("CONDITIONING", ), + "negative": ("CONDITIONING", ), + #"sampler": ("SAMPLER", ), + "sigmas": ("SIGMAS", ), + "latent_image": ("LATENT", ), + "guides": ("GUIDES", ), + "options": ("OPTIONS", ), + } + } + + def main(self, + eta = 0.5, + sampler_name = "res_2m", + steps_to_run = -1, + cfg = 5.5, + bongmath = True, + seed : int = -1, + latent_image = None, + sigmas = None, + sampler_mode = "", + + **kwargs): + + steps = latent_image['state_info']['sigmas'].shape[-1] - 3 + sigmas = latent_image['state_info']['sigmas'] if sigmas is None else sigmas + if len(sigmas) > 2 and sigmas[1] < sigmas[2] and latent_image['state_info']['sampler_mode'] == "unsample" and sampler_mode == "resample": + sigmas = torch.flip(sigmas, dims=[0]) + + return super().main(eta=eta, sampler_name=sampler_name, sampler_mode=sampler_mode, sigmas=sigmas, steps_to_run=steps_to_run, steps=steps, cfg=cfg, bongmath=bongmath, seed=seed, latent_image=latent_image, **kwargs) + + + + + + +class ClownSampler_Beta: + @classmethod + def INPUT_TYPES(cls): + inputs = {"required": + { + "eta": ("FLOAT", {"default": 0.5, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Calculated noise amount to be added, then removed, after each step."}), + "sampler_name": (get_sampler_name_list (), {"default": get_default_sampler_name()}), + "seed": ("INT", {"default": -1, "min": -1, "max": 0xffffffffffffffff}), + "bongmath": ("BOOLEAN", {"default": True}), + }, + "optional": + { + "guides": ("GUIDES",), + "options": ("OPTIONS", {}), + } + } + + return inputs + + RETURN_TYPES = ("SAMPLER",) + + RETURN_NAMES = ("sampler",) + + FUNCTION = "main" + CATEGORY = "RES4LYF/samplers" + + def main(self, + model = None, + denoise : float = 1.0, + scheduler : str = "beta57", + cfg : float = 1.0, + seed : int = -1, + positive = None, + negative = None, + latent_image : Optional[dict[Tensor]] = None, + steps : int = 30, + steps_to_run : int = -1, + bongmath : bool = True, + sampler_mode : str = "standard", + + noise_type_sde : str = "gaussian", + noise_type_sde_substep : str = "gaussian", + noise_mode_sde : str = "hard", + noise_mode_sde_substep : str = "hard", + + + overshoot_mode : str = "hard", + overshoot_mode_substep : str = "hard", + overshoot : float = 0.0, + overshoot_substep : float = 0.0, + + eta : float = 0.5, + eta_substep : float = 0.5, + + noise_scaling_weight : float = 0.0, + noise_boost_step : float = 0.0, + noise_boost_substep : float = 0.0, + noise_anchor : float = 1.0, + + s_noise : float = 1.0, + s_noise_substep : float = 1.0, + d_noise : float = 1.0, + d_noise_start_step : int = 0, + d_noise_inv : float = 1.0, + d_noise_inv_start_step : int = 0, + + + alpha_sde : float = -1.0, + k_sde : float = 1.0, + cfgpp : float = 0.0, + c1 : float = 0.0, + c2 : float = 0.5, + c3 : float = 1.0, + noise_seed_sde : int = -1, + sampler_name : str = "res_2m", + implicit_sampler_name : str = "use_explicit", + + implicit_type : str = "bongmath", + implicit_type_substeps : str = "bongmath", + implicit_steps : int = 0, + implicit_substeps : int = 0, + + sigmas : Optional[Tensor] = None, + sigmas_override : Optional[Tensor] = None, + guides = None, + options = None, + sde_noise = None, + sde_noise_steps : int = 1, + extra_options : str = "", + automation = None, + + epsilon_scales : Optional[Tensor] = None, + regional_conditioning_weights : Optional[Tensor] = None, + frame_weights_mgr = None, + + + rescale_floor : bool = True, + + rk_swap_step : int = MAX_STEPS, + rk_swap_print : bool = False, + rk_swap_threshold : float = 0.0, + rk_swap_type : str = "", + + sde_mask : Optional[Tensor] = None, + + + #start_at_step : int = 0, + #stop_at_step : int = MAX_STEPS, + + **kwargs + ): + + options_mgr = OptionsManager(options, **kwargs) + extra_options += "\n" + options_mgr.get('extra_options', "") + + + # defaults for ClownSampler + eta_substep = eta + + # defaults for SharkSampler + noise_type_init = "gaussian" + noise_stdev = 1.0 + denoise_alt = 1.0 + channelwise_cfg = False #1.0 + + + #if options is not None: + #options_mgr = OptionsManager(options_inputs) + noise_type_sde = options_mgr.get('noise_type_sde' , noise_type_sde) + noise_type_sde_substep = options_mgr.get('noise_type_sde_substep', noise_type_sde_substep) + + noise_mode_sde = options_mgr.get('noise_mode_sde' , noise_mode_sde) + noise_mode_sde_substep = options_mgr.get('noise_mode_sde_substep', noise_mode_sde_substep) + + overshoot_mode = options_mgr.get('overshoot_mode' , overshoot_mode) + overshoot_mode_substep = options_mgr.get('overshoot_mode_substep', overshoot_mode_substep) + + eta = options_mgr.get('eta' , eta) + eta_substep = options_mgr.get('eta_substep' , eta_substep) + + overshoot = options_mgr.get('overshoot' , overshoot) + overshoot_substep = options_mgr.get('overshoot_substep' , overshoot_substep) + + noise_scaling_weight = options_mgr.get('noise_scaling_weight' , noise_scaling_weight) + noise_boost_step = options_mgr.get('noise_boost_step' , noise_boost_step) + noise_boost_substep = options_mgr.get('noise_boost_substep' , noise_boost_substep) + + noise_anchor = options_mgr.get('noise_anchor' , noise_anchor) + + s_noise = options_mgr.get('s_noise' , s_noise) + s_noise_substep = options_mgr.get('s_noise_substep' , s_noise_substep) + + d_noise = options_mgr.get('d_noise' , d_noise) + d_noise_start_step = options_mgr.get('d_noise_start_step' , d_noise_start_step) + d_noise_inv = options_mgr.get('d_noise_inv' , d_noise_inv) + d_noise_inv_start_step = options_mgr.get('d_noise_inv_start_step', d_noise_inv_start_step) + + implicit_type = options_mgr.get('implicit_type' , implicit_type) + implicit_type_substeps = options_mgr.get('implicit_type_substeps', implicit_type_substeps) + implicit_steps = options_mgr.get('implicit_steps' , implicit_steps) + implicit_substeps = options_mgr.get('implicit_substeps' , implicit_substeps) + + alpha_sde = options_mgr.get('alpha_sde' , alpha_sde) + k_sde = options_mgr.get('k_sde' , k_sde) + c1 = options_mgr.get('c1' , c1) + c2 = options_mgr.get('c2' , c2) + c3 = options_mgr.get('c3' , c3) + + frame_weights_mgr = options_mgr.get('frame_weights_mgr' , frame_weights_mgr) + + sde_noise = options_mgr.get('sde_noise' , sde_noise) + sde_noise_steps = options_mgr.get('sde_noise_steps' , sde_noise_steps) + + extra_options = options_mgr.get('extra_options' , extra_options) + + automation = options_mgr.get('automation' , automation) + + # SharkSampler Options + noise_type_init = options_mgr.get('noise_type_init' , noise_type_init) + noise_stdev = options_mgr.get('noise_stdev' , noise_stdev) + sampler_mode = options_mgr.get('sampler_mode' , sampler_mode) + denoise_alt = options_mgr.get('denoise_alt' , denoise_alt) + + channelwise_cfg = options_mgr.get('channelwise_cfg' , channelwise_cfg) + + sigmas = options_mgr.get('sigmas' , sigmas) + + rk_swap_type = options_mgr.get('rk_swap_type' , rk_swap_type) + rk_swap_step = options_mgr.get('rk_swap_step' , rk_swap_step) + rk_swap_threshold = options_mgr.get('rk_swap_threshold' , rk_swap_threshold) + rk_swap_print = options_mgr.get('rk_swap_print' , rk_swap_print) + + sde_mask = options_mgr.get('sde_mask' , sde_mask) + + + #start_at_step = options_mgr.get('start_at_step' , start_at_step) + #stop_at_ste = options_mgr.get('stop_at_step' , stop_at_step) + + if channelwise_cfg: # != 1.0: + cfg = -abs(cfg) # set cfg negative for shark, to flag as cfg_cw + + noise_seed_sde = seed + + + sampler, = ClownSamplerAdvanced_Beta().main( + noise_type_sde = noise_type_sde, + noise_type_sde_substep = noise_type_sde_substep, + noise_mode_sde = noise_mode_sde, + noise_mode_sde_substep = noise_mode_sde_substep, + + eta = eta, + eta_substep = eta_substep, + + s_noise = s_noise, + s_noise_substep = s_noise_substep, + + overshoot = overshoot, + overshoot_substep = overshoot_substep, + + overshoot_mode = overshoot_mode, + overshoot_mode_substep = overshoot_mode_substep, + + d_noise = d_noise, + d_noise_start_step = d_noise_start_step, + d_noise_inv = d_noise_inv, + d_noise_inv_start_step = d_noise_inv_start_step, + + alpha_sde = alpha_sde, + k_sde = k_sde, + cfgpp = cfgpp, + c1 = c1, + c2 = c2, + c3 = c3, + sampler_name = sampler_name, + implicit_sampler_name = implicit_sampler_name, + + implicit_type = implicit_type, + implicit_type_substeps = implicit_type_substeps, + implicit_steps = implicit_steps, + implicit_substeps = implicit_substeps, + + rescale_floor = rescale_floor, + sigmas_override = sigmas_override, + + noise_seed_sde = noise_seed_sde, + + guides = guides, + options = options_mgr.as_dict(), + + extra_options = extra_options, + automation = automation, + + noise_scaling_weight = noise_scaling_weight, + noise_boost_step = noise_boost_step, + noise_boost_substep = noise_boost_substep, + + epsilon_scales = epsilon_scales, + regional_conditioning_weights = regional_conditioning_weights, + frame_weights_mgr = frame_weights_mgr, + + sde_noise = sde_noise, + sde_noise_steps = sde_noise_steps, + + rk_swap_step = rk_swap_step, + rk_swap_print = rk_swap_print, + rk_swap_threshold = rk_swap_threshold, + rk_swap_type = rk_swap_type, + + steps_to_run = steps_to_run, + + sde_mask = sde_mask, + + bongmath = bongmath, + ) + + return (sampler,) + + + + + + + + + +class BongSampler: + @classmethod + def INPUT_TYPES(cls): + inputs = {"required": + { + "model": ("MODEL",), + "seed": ("INT", {"default": 0, "min": -1, "max": 0xffffffffffffffff}), + "steps": ("INT", {"default": 30, "min": 1, "max": MAX_STEPS}), + "cfg": ("FLOAT", {"default": 5.5, "min": -100.0, "max": 100.0, "step":0.01, "round": False, }), + "sampler_name": (["res_2m", "res_3m", "res_2s", "res_3s","res_2m_sde", "res_3m_sde", "res_2s_sde", "res_3s_sde"], {"default": "res_2s_sde"}), + "scheduler": (get_res4lyf_scheduler_list(), {"default": "beta57"},), + "denoise": ("FLOAT", {"default": 1.0, "min": -10000, "max": MAX_STEPS, "step":0.01}), + }, + "optional": + { + "positive": ("CONDITIONING",), + "negative": ("CONDITIONING",), + "latent_image": ("LATENT",), + } + } + + return inputs + + RETURN_TYPES = ("LATENT", ) + + RETURN_NAMES = ("output", ) + + FUNCTION = "main" + CATEGORY = "RES4LYF/samplers" + + def main(self, + model = None, + denoise : float = 1.0, + scheduler : str = "beta57", + cfg : float = 1.0, + seed : int = 42, + positive = None, + negative = None, + latent_image : Optional[dict[Tensor]] = None, + steps : int = 30, + steps_to_run : int = -1, + bongmath : bool = True, + sampler_mode : str = "standard", + + noise_type_sde : str = "brownian", + noise_type_sde_substep : str = "brownian", + noise_mode_sde : str = "hard", + noise_mode_sde_substep : str = "hard", + + + overshoot_mode : str = "hard", + overshoot_mode_substep : str = "hard", + overshoot : float = 0.0, + overshoot_substep : float = 0.0, + + eta : float = 0.5, + eta_substep : float = 0.5, + d_noise : float = 1.0, + s_noise : float = 1.0, + s_noise_substep : float = 1.0, + + alpha_sde : float = -1.0, + k_sde : float = 1.0, + cfgpp : float = 0.0, + c1 : float = 0.0, + c2 : float = 0.5, + c3 : float = 1.0, + noise_seed_sde : int = -1, + sampler_name : str = "res_2m", + implicit_sampler_name : str = "use_explicit", + + implicit_type : str = "bongmath", + implicit_type_substeps : str = "bongmath", + implicit_steps : int = 0, + implicit_substeps : int = 0, + + sigmas : Optional[Tensor] = None, + sigmas_override : Optional[Tensor] = None, + guides = None, + options = None, + sde_noise = None, + sde_noise_steps : int = 1, + extra_options : str = "", + automation = None, + + epsilon_scales : Optional[Tensor] = None, + regional_conditioning_weights : Optional[Tensor] = None, + frame_weights_mgr = None, + noise_scaling_weight : float = 0.0, + noise_boost_step : float = 0.0, + noise_boost_substep : float = 0.0, + noise_anchor : float = 1.0, + + rescale_floor : bool = True, + + rk_swap_step : int = MAX_STEPS, + rk_swap_print : bool = False, + rk_swap_threshold : float = 0.0, + rk_swap_type : str = "", + + #start_at_step : int = 0, + #stop_at_step : int = MAX_STEPS, + + **kwargs + ): + + options_mgr = OptionsManager(options, **kwargs) + extra_options += "\n" + options_mgr.get('extra_options', "") + + if model.model.model_config.unet_config.get('stable_cascade_stage') == 'b': + noise_type_sde = "pyramid-cascade_B" + noise_type_sde_substep = "pyramid-cascade_B" + + if sampler_name.endswith("_sde"): + sampler_name = sampler_name[:-4] + eta = 0.5 + else: + eta = 0.0 + + # defaults for ClownSampler + eta_substep = eta + + # defaults for SharkSampler + noise_type_init = "gaussian" + noise_stdev = 1.0 + denoise_alt = 1.0 + channelwise_cfg = False #1.0 + + + #if options is not None: + #options_mgr = OptionsManager(options_inputs) + noise_type_sde = options_mgr.get('noise_type_sde' , noise_type_sde) + noise_type_sde_substep = options_mgr.get('noise_type_sde_substep', noise_type_sde_substep) + + noise_mode_sde = options_mgr.get('noise_mode_sde' , noise_mode_sde) + noise_mode_sde_substep = options_mgr.get('noise_mode_sde_substep', noise_mode_sde_substep) + + overshoot_mode = options_mgr.get('overshoot_mode' , overshoot_mode) + overshoot_mode_substep = options_mgr.get('overshoot_mode_substep', overshoot_mode_substep) + + eta = options_mgr.get('eta' , eta) + eta_substep = options_mgr.get('eta_substep' , eta_substep) + + overshoot = options_mgr.get('overshoot' , overshoot) + overshoot_substep = options_mgr.get('overshoot_substep' , overshoot_substep) + + noise_scaling_weight = options_mgr.get('noise_scaling_weight' , noise_scaling_weight) + + noise_boost_step = options_mgr.get('noise_boost_step' , noise_boost_step) + noise_boost_substep = options_mgr.get('noise_boost_substep' , noise_boost_substep) + + noise_anchor = options_mgr.get('noise_anchor' , noise_anchor) + + s_noise = options_mgr.get('s_noise' , s_noise) + s_noise_substep = options_mgr.get('s_noise_substep' , s_noise_substep) + + d_noise = options_mgr.get('d_noise' , d_noise) + + implicit_type = options_mgr.get('implicit_type' , implicit_type) + implicit_type_substeps = options_mgr.get('implicit_type_substeps', implicit_type_substeps) + implicit_steps = options_mgr.get('implicit_steps' , implicit_steps) + implicit_substeps = options_mgr.get('implicit_substeps' , implicit_substeps) + + alpha_sde = options_mgr.get('alpha_sde' , alpha_sde) + k_sde = options_mgr.get('k_sde' , k_sde) + c1 = options_mgr.get('c1' , c1) + c2 = options_mgr.get('c2' , c2) + c3 = options_mgr.get('c3' , c3) + + frame_weights_mgr = options_mgr.get('frame_weights_mgr' , frame_weights_mgr) + + sde_noise = options_mgr.get('sde_noise' , sde_noise) + sde_noise_steps = options_mgr.get('sde_noise_steps' , sde_noise_steps) + + extra_options = options_mgr.get('extra_options' , extra_options) + + automation = options_mgr.get('automation' , automation) + + # SharkSampler Options + noise_type_init = options_mgr.get('noise_type_init' , noise_type_init) + noise_stdev = options_mgr.get('noise_stdev' , noise_stdev) + sampler_mode = options_mgr.get('sampler_mode' , sampler_mode) + denoise_alt = options_mgr.get('denoise_alt' , denoise_alt) + + channelwise_cfg = options_mgr.get('channelwise_cfg' , channelwise_cfg) + + sigmas = options_mgr.get('sigmas' , sigmas) + + rk_swap_type = options_mgr.get('rk_swap_type' , rk_swap_type) + rk_swap_step = options_mgr.get('rk_swap_step' , rk_swap_step) + rk_swap_threshold = options_mgr.get('rk_swap_threshold' , rk_swap_threshold) + rk_swap_print = options_mgr.get('rk_swap_print' , rk_swap_print) + + #start_at_step = options_mgr.get('start_at_step' , start_at_step) + #stop_at_ste = options_mgr.get('stop_at_step' , stop_at_step) + + if channelwise_cfg: # != 1.0: + cfg = -abs(cfg) # set cfg negative for shark, to flag as cfg_cw + + + + + sampler, = ClownSamplerAdvanced_Beta().main( + noise_type_sde = noise_type_sde, + noise_type_sde_substep = noise_type_sde_substep, + noise_mode_sde = noise_mode_sde, + noise_mode_sde_substep = noise_mode_sde_substep, + + eta = eta, + eta_substep = eta_substep, + + s_noise = s_noise, + s_noise_substep = s_noise_substep, + + overshoot = overshoot, + overshoot_substep = overshoot_substep, + + overshoot_mode = overshoot_mode, + overshoot_mode_substep = overshoot_mode_substep, + + d_noise = d_noise, + #d_noise_start_step = d_noise_start_step, + #d_noise_inv = d_noise_inv, + #d_noise_inv_start_step = d_noise_inv_start_step, + + alpha_sde = alpha_sde, + k_sde = k_sde, + cfgpp = cfgpp, + c1 = c1, + c2 = c2, + c3 = c3, + sampler_name = sampler_name, + implicit_sampler_name = implicit_sampler_name, + + implicit_type = implicit_type, + implicit_type_substeps = implicit_type_substeps, + implicit_steps = implicit_steps, + implicit_substeps = implicit_substeps, + + rescale_floor = rescale_floor, + sigmas_override = sigmas_override, + + noise_seed_sde = noise_seed_sde, + + guides = guides, + options = options_mgr.as_dict(), + + extra_options = extra_options, + automation = automation, + + noise_scaling_weight = noise_scaling_weight, + noise_boost_step = noise_boost_step, + noise_boost_substep = noise_boost_substep, + + epsilon_scales = epsilon_scales, + regional_conditioning_weights = regional_conditioning_weights, + frame_weights_mgr = frame_weights_mgr, + + sde_noise = sde_noise, + sde_noise_steps = sde_noise_steps, + + rk_swap_step = rk_swap_step, + rk_swap_print = rk_swap_print, + rk_swap_threshold = rk_swap_threshold, + rk_swap_type = rk_swap_type, + + steps_to_run = steps_to_run, + + bongmath = bongmath, + ) + + + output, denoised, sde_noise = SharkSampler().main( + model = model, + cfg = cfg, + scheduler = scheduler, + steps = steps, + steps_to_run = steps_to_run, + denoise = denoise, + latent_image = latent_image, + positive = positive, + negative = negative, + sampler = sampler, + cfgpp = cfgpp, + noise_seed = seed, + options = options_mgr.as_dict(), + sde_noise = sde_noise, + sde_noise_steps = sde_noise_steps, + noise_type_init = noise_type_init, + noise_stdev = noise_stdev, + sampler_mode = sampler_mode, + denoise_alt = denoise_alt, + sigmas = sigmas, + + extra_options = extra_options) + + return (output, ) \ No newline at end of file diff --git a/ComfyUI/custom_nodes/RES4LYF/beta/samplers_extensions.py b/ComfyUI/custom_nodes/RES4LYF/beta/samplers_extensions.py new file mode 100644 index 0000000000000000000000000000000000000000..0bfcdb811c47c0f6924cdb5524d2e8ed62bbea6d --- /dev/null +++ b/ComfyUI/custom_nodes/RES4LYF/beta/samplers_extensions.py @@ -0,0 +1,4638 @@ +import torch +from torch import Tensor +import torch.nn.functional as F + +from dataclasses import dataclass, asdict +from typing import Optional, Callable, Tuple, Dict, Any, Union +import copy + +from nodes import MAX_RESOLUTION + +from ..latents import get_edge_mask +from ..helper import OptionsManager, FrameWeightsManager, initialize_or_scale, get_res4lyf_scheduler_list, parse_range_string, parse_tile_sizes, parse_range_string_int + +from .rk_coefficients_beta import RK_SAMPLER_NAMES_BETA_FOLDERS, get_default_sampler_name, get_sampler_name_list, process_sampler_name + +from .noise_classes import NOISE_GENERATOR_NAMES_SIMPLE +from .rk_noise_sampler_beta import NOISE_MODE_NAMES +from .constants import IMPLICIT_TYPE_NAMES, GUIDE_MODE_NAMES_BETA_SIMPLE, MAX_STEPS, FRAME_WEIGHTS_CONFIG_NAMES, FRAME_WEIGHTS_DYNAMICS_NAMES, FRAME_WEIGHTS_SCHEDULE_NAMES + + +class ClownSamplerSelector_Beta: + @classmethod + def INPUT_TYPES(cls): + return {"required": + { + "sampler_name": (get_sampler_name_list(), {"default": get_default_sampler_name()}), + }, + "optional": + { + } + } + + RETURN_TYPES = (RK_SAMPLER_NAMES_BETA_FOLDERS,) + RETURN_NAMES = ("sampler_name",) + FUNCTION = "main" + CATEGORY = "RES4LYF/sampler_options" + + def main(self, + sampler_name = "res_2m", + ): + + sampler_name, implicit_sampler_name = process_sampler_name(sampler_name) + + sampler_name = sampler_name if implicit_sampler_name == "use_explicit" else implicit_sampler_name + + return (sampler_name,) + + + +class ClownOptions_SDE_Beta: + @classmethod + def INPUT_TYPES(cls): + return {"required": + { + "noise_type_sde": (NOISE_GENERATOR_NAMES_SIMPLE, {"default": "gaussian"}), + "noise_type_sde_substep": (NOISE_GENERATOR_NAMES_SIMPLE, {"default": "gaussian"}), + "noise_mode_sde": (NOISE_MODE_NAMES, {"default": 'hard', "tooltip": "How noise scales with the sigma schedule. Hard is the most aggressive, the others start strong and drop rapidly."}), + "noise_mode_sde_substep": (NOISE_MODE_NAMES, {"default": 'hard', "tooltip": "How noise scales with the sigma schedule. Hard is the most aggressive, the others start strong and drop rapidly."}), + "eta": ("FLOAT", {"default": 0.5, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Calculated noise amount to be added, then removed, after each step."}), + "eta_substep": ("FLOAT", {"default": 0.5, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Calculated noise amount to be added, then removed, after each step."}), + "seed": ("INT", {"default": -1, "min": -1, "max": 0xffffffffffffffff}), + }, + "optional": + { + "etas": ("SIGMAS", ), + "etas_substep": ("SIGMAS", ), + "options": ("OPTIONS", ), + } + } + + RETURN_TYPES = ("OPTIONS",) + RETURN_NAMES = ("options",) + FUNCTION = "main" + CATEGORY = "RES4LYF/sampler_options" + + def main(self, + noise_type_sde = "gaussian", + noise_type_sde_substep = "gaussian", + noise_mode_sde = "hard", + noise_mode_sde_substep = "hard", + eta = 0.5, + eta_substep = 0.5, + seed : int = -1, + etas : Optional[Tensor] = None, + etas_substep : Optional[Tensor] = None, + options = None, + ): + + options = options if options is not None else {} + + if noise_mode_sde == "none": + noise_mode_sde = "hard" + eta = 0.0 + + if noise_mode_sde_substep == "none": + noise_mode_sde_substep = "hard" + eta_substep = 0.0 + + if noise_type_sde == "none": + noise_type_sde = "gaussian" + eta = 0.0 + + if noise_type_sde_substep == "none": + noise_type_sde_substep = "gaussian" + eta_substep = 0.0 + + options['noise_type_sde'] = noise_type_sde + options['noise_type_sde_substep'] = noise_type_sde_substep + options['noise_mode_sde'] = noise_mode_sde + options['noise_mode_sde_substep'] = noise_mode_sde_substep + options['eta'] = eta + options['eta_substep'] = eta_substep + options['noise_seed_sde'] = seed + + options['etas'] = etas + options['etas_substep'] = etas_substep + + return (options,) + + + +class ClownOptions_StepSize_Beta: + @classmethod + def INPUT_TYPES(cls): + return {"required": + { + "overshoot_mode": (NOISE_MODE_NAMES, {"default": 'hard', "tooltip": "How step size overshoot scales with the sigma schedule. Hard is the most aggressive, the others start strong and drop rapidly."}), + "overshoot_mode_substep": (NOISE_MODE_NAMES, {"default": 'hard', "tooltip": "How substep size overshoot scales with the sigma schedule. Hard is the most aggressive, the others start strong and drop rapidly."}), + "overshoot": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Boost the size of each denoising step, then rescale to match the original. Has a softening effect."}), + "overshoot_substep": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Boost the size of each denoising substep, then rescale to match the original. Has a softening effect."}), + }, + "optional": + { + "options": ("OPTIONS", ), + } + } + + RETURN_TYPES = ("OPTIONS",) + RETURN_NAMES = ("options",) + FUNCTION = "main" + CATEGORY = "RES4LYF/sampler_options" + + def main(self, + overshoot_mode = "hard", + overshoot_mode_substep = "hard", + overshoot = 0.0, + overshoot_substep = 0.0, + options = None, + ): + + options = options if options is not None else {} + + options['overshoot_mode'] = overshoot_mode + options['overshoot_mode_substep'] = overshoot_mode_substep + options['overshoot'] = overshoot + options['overshoot_substep'] = overshoot_substep + + return (options, + ) + + +@dataclass +class DetailBoostOptions: + noise_scaling_weight : float = 0.0 + noise_boost_step : float = 0.0 + noise_boost_substep : float = 0.0 + noise_anchor : float = 1.0 + s_noise : float = 1.0 + s_noise_substep : float = 1.0 + d_noise : float = 1.0 + +DETAIL_BOOST_METHODS = [ + 'sampler', + 'sampler_normal', + 'sampler_substep', + 'sampler_substep_normal', + 'model', + 'model_alpha', + ] + +class ClownOptions_DetailBoost_Beta: + @classmethod + def INPUT_TYPES(cls): + return {"required": + { + "weight": ("FLOAT", {"default": 1.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Set to positive values to create a sharper, grittier, more detailed image. Set to negative values to soften and deepen the colors."}), + "method": (DETAIL_BOOST_METHODS, {"default": "model", "tooltip": "Determines whether the sampler or the model underestimates the noise level."}), + #"noise_scaling_mode": (['linear'] + NOISE_MODE_NAMES, {"default": 'hard', "tooltip": "Changes the steps where the effect is greatest. Most affect early steps, sinusoidal affects middle steps."}), + "mode": (NOISE_MODE_NAMES, {"default": 'hard', "tooltip": "Changes the steps where the effect is greatest. Most affect early steps, sinusoidal affects middle steps."}), + "eta": ("FLOAT", {"default": 0.5, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "The strength of the effect of the noise_scaling_mode. Linear ignores this parameter."}), + "start_step": ("INT", {"default": 3, "min": 0, "max": MAX_STEPS}), + "end_step": ("INT", {"default": 10, "min": -1, "max": MAX_STEPS}), + + #"noise_scaling_cycles": ("INT", {"default": 1, "min": 1, "max": MAX_STEPS}), + + #"noise_boost_step": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Set to positive values to create a sharper, grittier, more detailed image. Set to negative values to soften and deepen the colors."}), + #"noise_boost_substep": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Set to positive values to create a sharper, grittier, more detailed image. Set to negative values to soften and deepen the colors."}), + #"sampler_scaling_normalize":("BOOLEAN", {"default": False, "tooltip": "Limit saturation and luminosity drift."}), + }, + "optional": + { + "weights": ("SIGMAS", ), + "etas": ("SIGMAS", ), + "options": ("OPTIONS", ), + } + } + + RETURN_TYPES = ("OPTIONS",) + RETURN_NAMES = ("options",) + FUNCTION = "main" + CATEGORY = "RES4LYF/sampler_options" + + def main(self, + weight : float = 0.0, + method : str = "sampler", + mode : str = "linear", + eta : float = 0.5, + start_step : int = 0, + end_step : int = -1, + + + noise_scaling_cycles : int = 1, + + noise_boost_step : float = 0.0, + noise_boost_substep : float = 0.0, + sampler_scaling_normalize : bool = False, + + weights : Optional[Tensor] = None, + etas : Optional[Tensor] = None, + + options = None + ): + + noise_scaling_weight = weight + noise_scaling_type = method + noise_scaling_mode = mode + noise_scaling_eta = eta + noise_scaling_start_step = start_step + noise_scaling_end_step = end_step + + noise_scaling_weights = weights + noise_scaling_etas = etas + + + options = options if options is not None else {} + + default_dtype = torch.float64 + default_device = torch.device('cuda') + + if noise_scaling_type.endswith("_normal"): + sampler_scaling_normalize = True + noise_scaling_type = noise_scaling_type[:-7] + + if noise_scaling_end_step == -1: + noise_scaling_end_step = MAX_STEPS + + if noise_scaling_weights == None: + noise_scaling_weights = initialize_or_scale(None, noise_scaling_weight, MAX_STEPS).to(default_dtype).to(default_device) + + if noise_scaling_etas == None: + noise_scaling_etas = initialize_or_scale(None, noise_scaling_eta, MAX_STEPS).to(default_dtype).to(default_device) + + noise_scaling_prepend = torch.zeros((noise_scaling_start_step,), dtype=default_dtype, device=default_device) + + noise_scaling_weights = torch.cat((noise_scaling_prepend, noise_scaling_weights), dim=0) + noise_scaling_etas = torch.cat((noise_scaling_prepend, noise_scaling_etas), dim=0) + + if noise_scaling_weights.shape[-1] > noise_scaling_end_step: + noise_scaling_weights = noise_scaling_weights[:noise_scaling_end_step] + + if noise_scaling_etas.shape[-1] > noise_scaling_end_step: + noise_scaling_etas = noise_scaling_etas[:noise_scaling_end_step] + + noise_scaling_weights = F.pad(noise_scaling_weights, (0, MAX_STEPS), value=0.0) + noise_scaling_etas = F.pad(noise_scaling_etas, (0, MAX_STEPS), value=0.0) + + options['noise_scaling_weight'] = noise_scaling_weight + options['noise_scaling_type'] = noise_scaling_type + options['noise_scaling_mode'] = noise_scaling_mode + options['noise_scaling_eta'] = noise_scaling_eta + options['noise_scaling_cycles'] = noise_scaling_cycles + + options['noise_scaling_weights'] = noise_scaling_weights + options['noise_scaling_etas'] = noise_scaling_etas + + options['noise_boost_step'] = noise_boost_step + options['noise_boost_substep'] = noise_boost_substep + options['noise_boost_normalize'] = sampler_scaling_normalize + + """options['DetailBoostOptions'] = DetailBoostOptions( + noise_scaling_weight = noise_scaling_weight, + noise_scaling_type = noise_scaling_type, + noise_scaling_mode = noise_scaling_mode, + noise_scaling_eta = noise_scaling_eta, + + noise_boost_step = noise_boost_step, + noise_boost_substep = noise_boost_substep, + noise_boost_normalize = noise_boost_normalize, + + noise_anchor = noise_anchor, + s_noise = s_noise, + s_noise_substep = s_noise_substep, + d_noise = d_noise + d_noise_start_step = d_noise_start_step + )""" + + return (options,) + + + + +class ClownOptions_SigmaScaling_Beta: + @classmethod + def INPUT_TYPES(cls): + return {"required": + { + "s_noise": ("FLOAT", {"default": 1.0, "min": -10000, "max": 10000, "step":0.01, "tooltip": "Adds extra SDE noise. Values around 1.03-1.07 can lead to a moderate boost in detail and paint textures."}), + "s_noise_substep": ("FLOAT", {"default": 1.0, "min": -10000, "max": 10000, "step":0.01, "tooltip": "Adds extra SDE noise. Values around 1.03-1.07 can lead to a moderate boost in detail and paint textures."}), + "noise_anchor_sde": ("FLOAT", {"default": 1.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Typically set to between 1.0 and 0.0. Lower values cerate a grittier, more detailed image."}), + + "lying": ("FLOAT", {"default": 1.0, "min": -10000, "max": 10000, "step":0.01, "tooltip": "Downscales the sigma schedule. Values around 0.98-0.95 can lead to a large boost in detail and paint textures."}), + "lying_inv": ("FLOAT", {"default": 1.0, "min": -10000, "max": 10000, "step":0.01, "tooltip": "Upscales the sigma schedule. Will soften the image and deepen colors. Use after d_noise to counteract desaturation."}), + "lying_start_step": ("INT", {"default": 0, "min": 0, "max": MAX_STEPS}), + "lying_inv_start_step": ("INT", {"default": 1, "min": 0, "max": MAX_STEPS}), + + }, + "optional": + { + "s_noises": ("SIGMAS", ), + "s_noises_substep": ("SIGMAS", ), + "options": ("OPTIONS", ), + } + } + + RETURN_TYPES = ("OPTIONS",) + RETURN_NAMES = ("options",) + FUNCTION = "main" + CATEGORY = "RES4LYF/sampler_options" + + def main(self, + noise_anchor_sde : float = 1.0, + + s_noise : float = 1.0, + s_noise_substep : float = 1.0, + lying : float = 1.0, + lying_start_step : int = 0, + + lying_inv : float = 1.0, + lying_inv_start_step : int = 1, + + s_noises : Optional[Tensor] = None, + s_noises_substep : Optional[Tensor] = None, + options = None + ): + + options = options if options is not None else {} + + default_dtype = torch.float64 + default_device = torch.device('cuda') + + + + options['noise_anchor'] = noise_anchor_sde + options['s_noise'] = s_noise + options['s_noise_substep'] = s_noise_substep + options['d_noise'] = lying + options['d_noise_start_step'] = lying_start_step + options['d_noise_inv'] = lying_inv + options['d_noise_inv_start_step'] = lying_inv_start_step + + options['s_noises'] = s_noises + options['s_noises_substep'] = s_noises_substep + + return (options,) + + + +class ClownOptions_FlowGuide: + @classmethod + def INPUT_TYPES(cls): + return {"required": + { + "sync_eps": ("FLOAT", {"default": 0.75, "min": -10000.0, "max": 10000.0, "step":0.01, "round": False, "tooltip": "Accelerate convergence with positive values when sampling, negative values when unsampling."}), + }, + "optional": + { + "options": ("OPTIONS", ), + } + } + + RETURN_TYPES = ("OPTIONS",) + RETURN_NAMES = ("options",) + FUNCTION = "main" + CATEGORY = "RES4LYF/sampler_options" + + def main(self, + sync_eps = 0.75, + options = None + ): + + options = options if options is not None else {} + + options['flow_sync_eps'] = sync_eps + + return (options,) + + + +class ClownOptions_Momentum_Beta: + @classmethod + def INPUT_TYPES(cls): + return {"required": + { + "momentum": ("FLOAT", {"default": 0.0, "min": -10000.0, "max": 10000.0, "step":0.01, "round": False, "tooltip": "Accelerate convergence with positive values when sampling, negative values when unsampling."}), + }, + "optional": + { + "options": ("OPTIONS", ), + } + } + + RETURN_TYPES = ("OPTIONS",) + RETURN_NAMES = ("options",) + FUNCTION = "main" + CATEGORY = "RES4LYF/sampler_options" + + def main(self, + momentum = 0.0, + options = None + ): + + options = options if options is not None else {} + + options['momentum'] = momentum + + return (options,) + + + +class ClownOptions_ImplicitSteps_Beta: + @classmethod + def INPUT_TYPES(cls): + return {"required": + { + "implicit_type": (IMPLICIT_TYPE_NAMES, {"default": "bongmath"}), + "implicit_type_substeps": (IMPLICIT_TYPE_NAMES, {"default": "bongmath"}), + "implicit_steps": ("INT", {"default": 0, "min": 0, "max": 10000}), + "implicit_substeps": ("INT", {"default": 0, "min": 0, "max": 10000}), + }, + "optional": + { + "options": ("OPTIONS", ), + } + } + + RETURN_TYPES = ("OPTIONS",) + RETURN_NAMES = ("options",) + FUNCTION = "main" + CATEGORY = "RES4LYF/sampler_options" + + def main(self, + implicit_type = "bongmath", + implicit_type_substeps = "bongmath", + implicit_steps = 0, + implicit_substeps = 0, + options = None + ): + + options = options if options is not None else {} + + options['implicit_type'] = implicit_type + options['implicit_type_substeps'] = implicit_type_substeps + options['implicit_steps'] = implicit_steps + options['implicit_substeps'] = implicit_substeps + + return (options,) + + + +class ClownOptions_Cycles_Beta: + @classmethod + def INPUT_TYPES(cls): + return {"required": + { + "cycles" : ("FLOAT", {"default": 0.0, "min": 0.0, "max": 10000, "step":0.5, "round": 0.5}), + "eta_decay_scale" : ("FLOAT", {"default": 1.0, "min": -10000, "max": 10000, "step":0.01, "tooltip": "Multiplies etas by this number after every cycle. May help drive convergence." }), + "unsample_eta" : ("FLOAT", {"default": 0.5, "min": -10000, "max": 10000, "step":0.01}), + "unsampler_override" : (get_sampler_name_list(), {"default": "none"}), + "unsample_steps_to_run" : ("INT", {"default": -1, "min": -1, "max": 10000, "step":1, "round": 1}), + "unsample_cfg" : ("FLOAT", {"default": 1.0, "min": -10000, "max": 10000, "step":0.01}), + "unsample_bongmath" : ("BOOLEAN", {"default": False}), + }, + "optional": + { + "options": ("OPTIONS", ), + } + } + + RETURN_TYPES = ("OPTIONS",) + RETURN_NAMES = ("options",) + FUNCTION = "main" + CATEGORY = "RES4LYF/sampler_options" + + def main(self, + cycles = 0, + unsample_eta = 0.5, + eta_decay_scale = 1.0, + unsample_cfg = 1.0, + unsampler_override = "none", + unsample_steps_to_run = -1, + unsample_bongmath = False, + options = None + ): + + options = options if options is not None else {} + + options['rebounds'] = int(cycles * 2) + options['unsample_eta'] = unsample_eta + options['unsampler_name'] = unsampler_override + options['eta_decay_scale'] = eta_decay_scale + options['unsample_steps_to_run'] = unsample_steps_to_run + options['unsample_cfg'] = unsample_cfg + options['unsample_bongmath'] = unsample_bongmath + + return (options,) + + + + +class SharkOptions_StartStep_Beta: + @classmethod + def INPUT_TYPES(cls): + return {"required": + { + "start_at_step": ("INT", {"default": 0, "min": -1, "max": 10000, "step":1,}), + + }, + "optional": + { + "options": ("OPTIONS", ), + } + } + + RETURN_TYPES = ("OPTIONS",) + RETURN_NAMES = ("options",) + FUNCTION = "main" + CATEGORY = "RES4LYF/sampler_options" + + def main(self, + start_at_step = 0, + options = None + ): + + options = options if options is not None else {} + + options['start_at_step'] = start_at_step + + return (options,) + + + + +class ClownOptions_Tile_Beta: + @classmethod + def INPUT_TYPES(cls): + return {"required": + { + "tile_width" : ("INT", {"default": 1024, "min": -1, "max": 10000, "step":1,}), + "tile_height": ("INT", {"default": 1024, "min": -1, "max": 10000, "step":1,}), + }, + "optional": + { + "options": ("OPTIONS", ), + } + } + + RETURN_TYPES = ("OPTIONS",) + RETURN_NAMES = ("options",) + FUNCTION = "main" + CATEGORY = "RES4LYF/sampler_options" + + def main(self, + tile_height = 1024, + tile_width = 1024, + options = None + ): + + options = options if options is not None else {} + + tile_sizes = options.get('tile_sizes', []) + tile_sizes.append((tile_height, tile_width)) + options['tile_sizes'] = tile_sizes + + return (options,) + + + +class ClownOptions_Tile_Advanced_Beta: + @classmethod + def INPUT_TYPES(cls): + return {"required": + { + "tile_sizes": ("STRING", {"default": "1024,1024", "multiline": True}), + }, + "optional": + { + "options": ("OPTIONS", ), + } + } + + RETURN_TYPES = ("OPTIONS",) + RETURN_NAMES = ("options",) + FUNCTION = "main" + CATEGORY = "RES4LYF/sampler_options" + + def main(self, + tile_sizes = "1024,1024", + options = None + ): + + options = options if options is not None else {} + + tiles_height_width = parse_tile_sizes(tile_sizes) + options['tile_sizes'] = [(tile[-1], tile[-2]) for tile in tiles_height_width] # swap height and width to be consistent... width, height + + return (options,) + + + + +class ClownOptions_ExtraOptions_Beta: + @classmethod + def INPUT_TYPES(cls): + return {"required": + { + "extra_options": ("STRING", {"default": "", "multiline": True}), + }, + "optional": + { + "options": ("OPTIONS", ), + } + } + + RETURN_TYPES = ("OPTIONS",) + RETURN_NAMES = ("options",) + FUNCTION = "main" + CATEGORY = "RES4LYF/sampler_options" + + def main(self, + extra_options = "", + options = None + ): + + options = options if options is not None else {} + + if 'extra_options' in options: + options['extra_options'] += '\n' + extra_options + else: + options['extra_options'] = extra_options + + return (options, ) + + + + +class ClownOptions_DenoisedSampling_Beta: + @classmethod + def INPUT_TYPES(cls): + return {"required": + { + "cycles" : ("FLOAT", {"default": 0.0, "min": 0.0, "max": 10000, "step":0.5, "round": 0.5}), + "eta_decay_scale" : ("FLOAT", {"default": 1.0, "min": -10000, "max": 10000, "step":0.01, "tooltip": "Multiplies etas by this number after every cycle. May help drive convergence." }), + "unsample_eta" : ("FLOAT", {"default": 0.5, "min": -10000, "max": 10000, "step":0.01}), + "unsampler_override" : (get_sampler_name_list(), {"default": "none"}), + "unsample_steps_to_run" : ("INT", {"default": -1, "min": -1, "max": 10000, "step":1, "round": 1}), + "unsample_cfg" : ("FLOAT", {"default": 1.0, "min": -10000, "max": 10000, "step":0.01}), + "unsample_bongmath" : ("BOOLEAN", {"default": False}), + }, + "optional": + { + "options": ("OPTIONS", ), + } + } + + RETURN_TYPES = ("OPTIONS",) + RETURN_NAMES = ("options",) + FUNCTION = "main" + CATEGORY = "RES4LYF/sampler_options" + + def main(self, + extra_options = "", + options = None + ): + + options = options if options is not None else {} + + if 'extra_options' in options: + options['extra_options'] += '\n' + extra_options + else: + options['extra_options'] = extra_options + + return (options, ) + + + + +class ClownOptions_Automation_Beta: + @classmethod + def INPUT_TYPES(cls): + return {"required": {}, + "optional": { + "etas": ("SIGMAS", ), + "etas_substep": ("SIGMAS", ), + "s_noises": ("SIGMAS", ), + "s_noises_substep": ("SIGMAS", ), + "epsilon_scales": ("SIGMAS", ), + "frame_weights": ("SIGMAS", ), + "options": ("OPTIONS",), + } + } + RETURN_TYPES = ("OPTIONS",) + RETURN_NAMES = ("options",) + FUNCTION = "main" + CATEGORY = "RES4LYF/sampler_options" + + def main(self, + etas = None, + etas_substep = None, + s_noises = None, + s_noises_substep = None, + epsilon_scales = None, + frame_weights = None, + options = None + ): + + options = options if options is not None else {} + + options_mgr = OptionsManager(options) + + frame_weights_mgr = options_mgr.get("frame_weights_mgr") + if frame_weights_mgr is None and frame_weights is not None: + frame_weights_mgr = FrameWeightsManager() + frame_weights_mgr.set_custom_weights("frame_weights", frame_weights) + + automation = { + "etas" : etas, + "etas_substep" : etas_substep, + "s_noises" : s_noises, + "s_noises_substep" : s_noises_substep, + "epsilon_scales" : epsilon_scales, + "frame_weights_mgr" : frame_weights_mgr, + } + + options["automation"] = automation + + return (options, ) + + + + + +class SharkOptions_GuideCond_Beta: + @classmethod + def INPUT_TYPES(cls): + return {"required": {}, + "optional": { + "positive" : ("CONDITIONING", ), + "negative" : ("CONDITIONING", ), + "cfg" : ("FLOAT", {"default": 1.0, "min": -10000, "max": 10000, "step":0.01}), + "options" : ("OPTIONS",), + } + } + RETURN_TYPES = ("OPTIONS",) + RETURN_NAMES = ("options",) + FUNCTION = "main" + CATEGORY = "RES4LYF/sampler_options" + + def main(self, + positive = None, + negative = None, + cfg = 1.0, + options = None, + ): + + options = options if options is not None else {} + + flow_cond = { + "yt_positive" : positive, + "yt_negative" : negative, + "yt_cfg" : cfg, + } + + options["flow_cond"] = flow_cond + + return (options, ) + + + + +class SharkOptions_GuideConds_Beta: + @classmethod + def INPUT_TYPES(cls): + return {"required": {}, + "optional": { + "positive_masked" : ("CONDITIONING", ), + "positive_unmasked" : ("CONDITIONING", ), + "negative_masked" : ("CONDITIONING", ), + "negative_unmasked" : ("CONDITIONING", ), + "cfg_masked" : ("FLOAT", {"default": 1.0, "min": -10000, "max": 10000, "step":0.01}), + "cfg_unmasked" : ("FLOAT", {"default": 1.0, "min": -10000, "max": 10000, "step":0.01}), + "options" : ("OPTIONS",), + } + } + RETURN_TYPES = ("OPTIONS",) + RETURN_NAMES = ("options",) + FUNCTION = "main" + CATEGORY = "RES4LYF/sampler_options" + + def main(self, + positive_masked = None, + negative_masked = None, + cfg_masked = 1.0, + positive_unmasked = None, + negative_unmasked = None, + cfg_unmasked = 1.0, + options = None, + ): + + options = options if options is not None else {} + + flow_cond = { + "yt_positive" : positive_masked, + "yt_negative" : negative_masked, + "yt_cfg" : cfg_masked, + "yt_inv_positive" : positive_unmasked, + "yt_inv_negative" : negative_unmasked, + "yt_inv_cfg" : cfg_unmasked, + } + + options["flow_cond"] = flow_cond + + return (options, ) + + + + + +class SharkOptions_Beta: + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "noise_type_init": (NOISE_GENERATOR_NAMES_SIMPLE, {"default": "gaussian"}), + "s_noise_init": ("FLOAT", {"default": 1.0, "min": -10000.0, "max": 10000.0, "step":0.01, "round": False, }), + "denoise_alt": ("FLOAT", {"default": 1.0, "min": -10000, "max": 10000, "step":0.01}), + "channelwise_cfg": ("BOOLEAN", {"default": False}), + }, + "optional": { + "options": ("OPTIONS", ), + } + } + + RETURN_TYPES = ("OPTIONS",) + RETURN_NAMES = ("options",) + FUNCTION = "main" + CATEGORY = "RES4LYF/sampler_options" + + def main(self, + noise_type_init = "gaussian", + s_noise_init = 1.0, + denoise_alt = 1.0, + channelwise_cfg = False, + options = None + ): + + options = options if options is not None else {} + + options['noise_type_init'] = noise_type_init + options['noise_init_stdev'] = s_noise_init + options['denoise_alt'] = denoise_alt + options['channelwise_cfg'] = channelwise_cfg + + return (options,) + + + + +class SharkOptions_UltraCascade_Latent_Beta: + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "width": ("INT", {"default": 60, "min": 1, "max": MAX_RESOLUTION, "step": 1}), + "height": ("INT", {"default": 36, "min": 1, "max": MAX_RESOLUTION, "step": 1}), + }, + "optional": { + "options": ("OPTIONS",), + } + } + + RETURN_TYPES = ("OPTIONS",) + RETURN_NAMES = ("options",) + FUNCTION = "main" + CATEGORY = "RES4LYF/sampler_options" + + def main(self, + width : int = 60, + height : int = 36, + options = None, + ): + + options = options if options is not None else {} + + options['ultracascade_latent_width'] = width + options['ultracascade_latent_height'] = height + + return (options,) + + + + +class ClownOptions_SwapSampler_Beta: + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "sampler_name": (get_sampler_name_list(), {"default": get_default_sampler_name()}), + "swap_below_err": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Swap samplers if the error per step falls below this threshold."}), + "swap_at_step": ("INT", {"default": 30, "min": 1, "max": 10000}), + "log_err_to_console": ("BOOLEAN", {"default": False}), + }, + "optional": { + "options": ("OPTIONS", ), + } + } + + RETURN_TYPES = ("OPTIONS",) + RETURN_NAMES = ("options",) + FUNCTION = "main" + CATEGORY = "RES4LYF/sampler_options" + + def main(self, + sampler_name = "res_3m", + swap_below_err = 0.0, + swap_at_step = 30, + log_err_to_console = False, + options = None, + ): + + sampler_name, implicit_sampler_name = process_sampler_name(sampler_name) + + sampler_name = sampler_name if implicit_sampler_name == "use_explicit" else implicit_sampler_name + + options = options if options is not None else {} + + options['rk_swap_type'] = sampler_name + options['rk_swap_threshold'] = swap_below_err + options['rk_swap_step'] = swap_at_step + options['rk_swap_print'] = log_err_to_console + + return (options,) + + + + + +class ClownOptions_SDE_Mask_Beta: + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "max": ("FLOAT", {"default": 1.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Clamp the max value for the mask."}), + "min": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Clamp the min value for the mask."}), + "invert_mask": ("BOOLEAN", {"default": False}), + }, + "optional": { + "mask": ("MASK", ), + "options": ("OPTIONS", ), + } + } + + RETURN_TYPES = ("OPTIONS",) + RETURN_NAMES = ("options",) + FUNCTION = "main" + CATEGORY = "RES4LYF/sampler_options" + + def main(self, + max = 1.0, + min = 0.0, + invert_mask = False, + mask = None, + options = None, + ): + + options = copy.deepcopy(options) if options is not None else {} + + if invert_mask: + mask = 1-mask + + mask = ((mask - mask.min()) * (max - min)) / (mask.max() - mask.min()) + min + + options['sde_mask'] = mask + + return (options,) + + + + +class ClownGuide_Mean_Beta: + @classmethod + def INPUT_TYPES(cls): + return {"required": + { + "weight": ("FLOAT", {"default": 0.75, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Set the strength of the guide."}), + "cutoff": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step":0.01, "round": False, "tooltip": "Disables the guide for the next step when the denoised image is similar to the guide. Higher values will strengthen the effect."}), + "weight_scheduler": (["constant"] + get_res4lyf_scheduler_list(), {"default": "beta57"},), + "start_step": ("INT", {"default": 0, "min": 0, "max": 10000}), + "end_step": ("INT", {"default": 15, "min": -1, "max": 10000}), + "invert_mask": ("BOOLEAN", {"default": False}), + }, + "optional": + { + "guide": ("LATENT", ), + "mask": ("MASK", ), + "weights": ("SIGMAS", ), + "guides": ("GUIDES", ), + } + } + + RETURN_TYPES = ("GUIDES",) + RETURN_NAMES = ("guides",) + FUNCTION = "main" + CATEGORY = "RES4LYF/sampler_extensions" + + def main(self, + weight_scheduler = "constant", + start_step = 0, + end_step = 30, + cutoff = 1.0, + guide = None, + weight = 0.0, + + channelwise_mode = False, + projection_mode = False, + weights = None, + mask = None, + invert_mask = False, + + guides = None, + ): + + default_dtype = torch.float64 + + mask = 1-mask if mask is not None else None + + if end_step == -1: + end_step = MAX_STEPS + + if guide is not None: + raw_x = guide.get('state_info', {}).get('raw_x', None) + if raw_x is not None: + guide = {'samples': guide['state_info']['raw_x'].clone()} + else: + guide = {'samples': guide['samples'].clone()} + + if weight_scheduler == "constant": # and weights == None: + weights = initialize_or_scale(None, weight, end_step).to(default_dtype) + weights = F.pad(weights, (0, MAX_STEPS), value=0.0) + + guides = copy.deepcopy(guides) if guides is not None else {} + + guides['weight_mean'] = weight + guides['weights_mean'] = weights + guides['guide_mean'] = guide + guides['mask_mean'] = mask + + guides['weight_scheduler_mean'] = weight_scheduler + guides['start_step_mean'] = start_step + guides['end_step_mean'] = end_step + + guides['cutoff_mean'] = cutoff + + return (guides, ) + + + +class ClownGuide_FrequencySeparation: + @classmethod + def INPUT_TYPES(cls): + return {"required": + { + "apply_to" : (["AdaIN"], {"default": "AdaIN"}), + "method" : (["gaussian", "gaussian_pw", "median", "median_pw",], {"default": "median"}), + "sigma": ("FLOAT", {"default": 3.0, "min": -10000.0, "max": 10000.0, "step":0.01, "round": False, "tooltip": "Low values produce results closer to the guide image. No effect with median."}), + "kernel_size": ("INT", {"default": 8, "min": 1, "max": 11111, "step": 1, "tooltip": "Primary control with median. Set the Re___Patcher node to float32 or lower precision if you have OOMs. You may have them regardless at higher kernel sizes with median."}), + "inner_kernel_size": ("INT", {"default": 2, "min": 1, "max": 11111, "step": 1, "tooltip": "Should be equal to, or less than, kernel_size."}), + "stride": ("INT", {"default": 2, "min": 1, "max": 11111, "step": 1, "tooltip": "Should be equal to, or less than, inner_kernel_size."}), + + + "lowpass_weight": ("FLOAT", {"default": 1.0, "min": -10000.0, "max": 10000.0, "step":0.01, "round": False, "tooltip": "Typically should be set to 1.0. Lower values may sharpen the image, higher values may blur the image."}), + "highpass_weight": ("FLOAT", {"default": 1.0, "min": -10000.0, "max": 10000.0, "step":0.01, "round": False, "tooltip": "Typically should be set to 1.0. Higher values may sharpen the image, lower values may blur the image."}), + + "guides": ("GUIDES", ), + }, + "optional": + { + "mask" : ("MASK",), + } + } + + RETURN_TYPES = ("GUIDES",) + RETURN_NAMES = ("guides",) + FUNCTION = "main" + CATEGORY = "RES4LYF/sampler_extensions" + EXPERIMENTAL = True + + def main(self, + apply_to = "AdaIN", + method = "median", + sigma = 3.0, + kernel_size = 9, + inner_kernel_size = 2, + stride = 2, + lowpass_weight = 1.0, + highpass_weight = 1.0, + guides = None, + mask = None, + ): + + guides = copy.deepcopy(guides) if guides is not None else {} + + guides['freqsep_apply_to'] = apply_to + guides['freqsep_lowpass_method'] = method + guides['freqsep_sigma'] = sigma + guides['freqsep_kernel_size'] = kernel_size + guides['freqsep_inner_kernel_size'] = inner_kernel_size + guides['freqsep_stride'] = stride + + guides['freqsep_lowpass_weight'] = lowpass_weight + guides['freqsep_highpass_weight']= highpass_weight + guides['freqsep_mask'] = mask + + return (guides, ) + + + + +class ClownGuide_Style_Beta: + @classmethod + def INPUT_TYPES(cls): + return {"required": + { + "apply_to": (["positive", "negative", "denoised"], {"default": "positive", "tooltip": "When using CFG, decides whether to apply the guide to the positive or negative conditioning."}), + "method": (["AdaIN", "WCT", "WCT2", "scattersort","none"], {"default": "WCT"}), + "weight": ("FLOAT", {"default": 1.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Set the strength of the guide by multiplying all other weights by this value."}), + "synweight": ("FLOAT", {"default": 1.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Set the relative strength of the guide on the opposite conditioning to what was selected: i.e., negative if positive in apply_to. Recommended to avoid CFG burn."}), + "weight_scheduler": (["constant"] + get_res4lyf_scheduler_list(), {"default": "constant", "tooltip": "Selecting any scheduler except constant will cause the strength to gradually decay to zero. Try beta57 vs. linear quadratic."},), + "start_step": ("INT", {"default": 0, "min": 0, "max": 10000}), + "end_step": ("INT", {"default": -1, "min": -1, "max": 10000}), + "invert_mask": ("BOOLEAN", {"default": False}), + }, + "optional": + { + "guide": ("LATENT", ), + "mask": ("MASK", ), + "weights": ("SIGMAS", ), + "guides": ("GUIDES", ), + } + } + + RETURN_TYPES = ("GUIDES",) + RETURN_NAMES = ("guides",) + FUNCTION = "main" + CATEGORY = "RES4LYF/sampler_extensions" + DESCRIPTION = "Transfer some visual aspects of style from a guide (reference) image. If nothing about style is specified in the prompt, it may just transfer the lighting and color scheme." + \ + "If using CFG results in burn, or a very dark/bright image in the preview followed by a bad output, try duplicating and chaining this node, so that the guide may be applied to both positive and negative conditioning." + \ + "Currently supported models: SD1.5, SDXL, Stable Cascade, SD3.5, AuraFlow, Flux, HiDream, WAN, and LTXV." + + def main(self, + apply_to = "all", + method = "WCT", + weight = 1.0, + synweight = 1.0, + weight_scheduler = "constant", + start_step = 0, + end_step = 15, + invert_mask = False, + + guide = None, + mask = None, + weights = None, + guides = None, + ): + + default_dtype = torch.float64 + + mask = 1-mask if mask is not None else None + + if end_step == -1: + end_step = MAX_STEPS + + if guide is not None: + raw_x = guide.get('state_info', {}).get('raw_x', None) + if raw_x is not None: + guide = {'samples': guide['state_info']['raw_x'].clone()} + else: + guide = {'samples': guide['samples'].clone()} + + if weight_scheduler == "constant": # and weights == None: + weights = initialize_or_scale(None, weight, end_step).to(default_dtype) + prepend = torch.zeros(start_step).to(weights) + weights = torch.cat([prepend, weights]) + weights = F.pad(weights, (0, MAX_STEPS), value=0.0) + + guides = copy.deepcopy(guides) if guides is not None else {} + + guides['style_method'] = method + + if apply_to in {"positive", "all"}: + + guides['weight_style_pos'] = weight + guides['weights_style_pos'] = weights + + guides['synweight_style_pos'] = synweight + + guides['guide_style_pos'] = guide + guides['mask_style_pos'] = mask + + guides['weight_scheduler_style_pos'] = weight_scheduler + guides['start_step_style_pos'] = start_step + guides['end_step_style_pos'] = end_step + + if apply_to in {"negative", "all"}: + guides['weight_style_neg'] = weight + guides['weights_style_neg'] = weights + + guides['synweight_style_neg'] = synweight + + guides['guide_style_neg'] = guide + guides['mask_style_neg'] = mask + + guides['weight_scheduler_style_neg'] = weight_scheduler + guides['start_step_style_neg'] = start_step + guides['end_step_style_neg'] = end_step + + if apply_to in {"denoised", "all"}: + + guides['weight_style_denoised'] = weight + guides['weights_style_denoised'] = weights + + guides['synweight_style_denoised'] = synweight + + guides['guide_style_denoised'] = guide + guides['mask_style_denoised'] = mask + + guides['weight_scheduler_style_denoised'] = weight_scheduler + guides['start_step_style_denoised'] = start_step + guides['end_step_style_denoised'] = end_step + + return (guides, ) + + + + + + +class ClownGuide_Style_EdgeWidth: + @classmethod + def INPUT_TYPES(cls): + return {"required": + { + "edge_width": ("INT", {"default": 20, "min": 1, "max": 10000}), + }, + "optional": + { + "guides": ("GUIDES", ), + } + } + + RETURN_TYPES = ("GUIDES",) + RETURN_NAMES = ("guides",) + FUNCTION = "main" + CATEGORY = "RES4LYF/sampler_extensions" + DESCRIPTION = "Set an edge mask for some style guide types such as scattersort. Can help mitigate seams." + + def main(self, + edge_width = 20, + guides = None, + ): + + guides = copy.deepcopy(guides) if guides is not None else {} + + if guides.get('mask_style_pos') is not None: + guides['mask_edge_style_pos'] = get_edge_mask(guides.get('mask_style_pos'), edge_width) + + if guides.get('mask_style_neg') is not None: + guides['mask_edge_style_neg'] = get_edge_mask(guides.get('mask_style_neg'), edge_width) + + return (guides, ) + + + + + +class ClownGuide_Style_TileSize: + @classmethod + def INPUT_TYPES(cls): + return {"required": + { + "height": ("INT", {"default": 128, "min": 16, "max": 10000, "step": 16}), + "width" : ("INT", {"default": 128, "min": 16, "max": 10000, "step": 16}), + "padding" : ("INT", {"default": 64, "min": 0, "max": 10000, "step": 16}), + }, + "optional": + { + "guides": ("GUIDES", ), + } + } + + RETURN_TYPES = ("GUIDES",) + RETURN_NAMES = ("guides",) + FUNCTION = "main" + CATEGORY = "RES4LYF/sampler_extensions" + DESCRIPTION = "Set a tile size for some style guide types such as scattersort. Can improve adherence to the input image." + + def main(self, + height = 128, + width = 128, + padding = 64, + guides = None, + ): + + guides = copy.deepcopy(guides) if guides is not None else {} + + guides['style_tile_height'] = height // 16 + guides['style_tile_width'] = width // 16 + guides['style_tile_padding'] = padding // 16 + + return (guides, ) + + + + + + + + + +class ClownGuides_Sync: + @classmethod + def INPUT_TYPES(cls): + return {"required": + { + "weight_masked": ("FLOAT", {"default": 1.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Set the strength of the guide."}), + "weight_unmasked": ("FLOAT", {"default": 1.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Set the strength of the guide_bkg."}), + "weight_scheduler_masked": (["constant"] + get_res4lyf_scheduler_list(), {"default": "beta57"},), + "weight_scheduler_unmasked": (["constant"] + get_res4lyf_scheduler_list(), {"default": "constant"},), + "weight_start_step_masked": ("INT", {"default": 0, "min": 0, "max": 10000}), + "weight_start_step_unmasked": ("INT", {"default": 0, "min": 0, "max": 10000}), + "weight_end_step_masked": ("INT", {"default": 15, "min": -1, "max": 10000}), + "weight_end_step_unmasked": ("INT", {"default": 15, "min": -1, "max": 10000}), + + "sync_masked": ("FLOAT", {"default": 1.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Set the strength of the guide."}), + "sync_unmasked": ("FLOAT", {"default": 1.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Set the strength of the guide_bkg."}), + "sync_scheduler_masked": (["constant"] + get_res4lyf_scheduler_list(), {"default": "beta57"},), + "sync_scheduler_unmasked": (["constant"] + get_res4lyf_scheduler_list(), {"default": "constant"},), + "sync_start_step_masked": ("INT", {"default": 0, "min": 0, "max": 10000}), + "sync_start_step_unmasked": ("INT", {"default": 0, "min": 0, "max": 10000}), + "sync_end_step_masked": ("INT", {"default": 15, "min": -1, "max": 10000}), + "sync_end_step_unmasked": ("INT", {"default": 15, "min": -1, "max": 10000}), + "invert_mask": ("BOOLEAN", {"default": False}), + }, + "optional": + { + "guide_masked": ("LATENT", ), + "guide_unmasked": ("LATENT", ), + "mask": ("MASK", ), + "weights_masked": ("SIGMAS", ), + "weights_unmasked": ("SIGMAS", ), + "syncs_masked": ("SIGMAS", ), + "syncs_unmasked": ("SIGMAS", ), + } + } + + RETURN_TYPES = ("GUIDES",) + RETURN_NAMES = ("guides",) + FUNCTION = "main" + CATEGORY = "RES4LYF/sampler_extensions" + EXPERIMENTAL = True + + def main(self, + weight_masked = 0.0, + weight_unmasked = 0.0, + weight_scheduler_masked = "constant", + weight_scheduler_unmasked = "constant", + weight_start_step_masked = 0, + weight_start_step_unmasked = 0, + weight_end_step_masked = 30, + weight_end_step_unmasked = 30, + + sync_masked = 0.0, + sync_unmasked = 0.0, + sync_scheduler_masked = "constant", + sync_scheduler_unmasked = "constant", + sync_start_step_masked = 0, + sync_start_step_unmasked = 0, + sync_end_step_masked = 30, + sync_end_step_unmasked = 30, + + guide_masked = None, + guide_unmasked = None, + + weights_masked = None, + weights_unmasked = None, + syncs_masked = None, + syncs_unmasked = None, + mask = None, + unmask = None, + invert_mask = False, + + guide_mode = "sync", + channelwise_mode = False, + projection_mode = False, + + cutoff_masked = 1.0, + cutoff_unmasked = 1.0, + ): + + default_dtype = torch.float64 + + if weight_end_step_masked == -1: + weight_end_step_masked = MAX_STEPS + if weight_end_step_unmasked == -1: + weight_end_step_unmasked = MAX_STEPS + + if sync_end_step_masked == -1: + sync_end_step_masked = MAX_STEPS + if sync_end_step_unmasked == -1: + sync_end_step_unmasked = MAX_STEPS + + if guide_masked is None: + weight_scheduler_masked = "constant" + weight_start_step_masked = 0 + weight_end_step_masked = 30 + weight_masked = 0.0 + weights_masked = None + + sync_scheduler_masked = "constant" + sync_start_step_masked = 0 + sync_end_step_masked = 30 + sync_masked = 0.0 + syncs_masked = None + + if guide_unmasked is None: + weight_scheduler_unmasked = "constant" + weight_start_step_unmasked = 0 + weight_end_step_unmasked = 30 + weight_unmasked = 0.0 + weights_unmasked = None + + sync_scheduler_unmasked = "constant" + sync_start_step_unmasked = 0 + sync_end_step_unmasked = 30 + sync_unmasked = 0.0 + syncs_unmasked = None + + if guide_masked is not None: + raw_x = guide_masked.get('state_info', {}).get('raw_x', None) + if False: #raw_x is not None: + guide_masked = {'samples': guide_masked['state_info']['raw_x'].clone()} + else: + guide_masked = {'samples': guide_masked['samples'].clone()} + + if guide_unmasked is not None: + raw_x = guide_unmasked.get('state_info', {}).get('raw_x', None) + if False: #raw_x is not None: + guide_unmasked = {'samples': guide_unmasked['state_info']['raw_x'].clone()} + else: + guide_unmasked = {'samples': guide_unmasked['samples'].clone()} + + if invert_mask and mask is not None: + mask = 1-mask + + if projection_mode: + guide_mode = guide_mode + "_projection" + + if channelwise_mode: + guide_mode = guide_mode + "_cw" + + if guide_mode == "unsample_cw": + guide_mode = "unsample" + if guide_mode == "resample_cw": + guide_mode = "resample" + + if weight_scheduler_masked == "constant" and weights_masked == None: + weights_masked = initialize_or_scale(None, weight_masked, weight_end_step_masked).to(default_dtype) + prepend = torch.zeros(weight_start_step_masked, dtype=default_dtype, device=weights_masked.device) + weights_masked = torch.cat((prepend, weights_masked), dim=0) + weights_masked = F.pad(weights_masked, (0, MAX_STEPS), value=0.0) + + if weight_scheduler_unmasked == "constant" and weights_unmasked == None: + weights_unmasked = initialize_or_scale(None, weight_unmasked, weight_end_step_unmasked).to(default_dtype) + prepend = torch.zeros(weight_start_step_unmasked, dtype=default_dtype, device=weights_unmasked.device) + weights_unmasked = torch.cat((prepend, weights_unmasked), dim=0) + weights_unmasked = F.pad(weights_unmasked, (0, MAX_STEPS), value=0.0) + + # Values for the sync scheduler will be inverted in rk_guide_func_beta.py as it's easier to understand: + # makes it so that a sync weight of 1.0 = full guide strength (which previously was 0.0) + if sync_scheduler_masked == "constant" and syncs_masked == None: + syncs_masked = initialize_or_scale(None, sync_masked, sync_end_step_masked).to(default_dtype) + prepend = torch.zeros(sync_start_step_masked, dtype=default_dtype, device=syncs_masked.device) + syncs_masked = torch.cat((prepend, syncs_masked), dim=0) + syncs_masked = F.pad(syncs_masked, (0, MAX_STEPS), value=0.0) + + if sync_scheduler_unmasked == "constant" and syncs_unmasked == None: + syncs_unmasked = initialize_or_scale(None, sync_unmasked, sync_end_step_unmasked).to(default_dtype) + prepend = torch.zeros(sync_start_step_unmasked, dtype=default_dtype, device=syncs_unmasked.device) + syncs_unmasked = torch.cat((prepend, syncs_unmasked), dim=0) + syncs_unmasked = F.pad(syncs_unmasked, (0, MAX_STEPS), value=0.0) + + guides = { + "guide_mode" : guide_mode, + + "guide_masked" : guide_masked, + "guide_unmasked" : guide_unmasked, + "mask" : mask, + "unmask" : unmask, + + "weight_masked" : weight_masked, + "weight_unmasked" : weight_unmasked, + "weight_scheduler_masked" : weight_scheduler_masked, + "weight_scheduler_unmasked" : weight_scheduler_unmasked, + "start_step_masked" : weight_start_step_masked, + "start_step_unmasked" : weight_start_step_unmasked, + "end_step_masked" : weight_end_step_masked, + "end_step_unmasked" : weight_end_step_unmasked, + + "weights_masked" : weights_masked, + "weights_unmasked" : weights_unmasked, + + "weight_masked_sync" : sync_masked, + "weight_unmasked_sync" : sync_unmasked, + "weight_scheduler_masked_sync" : sync_scheduler_masked, + "weight_scheduler_unmasked_sync" : sync_scheduler_unmasked, + "start_step_masked_sync" : sync_start_step_masked, + "start_step_unmasked_sync" : sync_start_step_unmasked, + "end_step_masked_sync" : sync_end_step_masked, + "end_step_unmasked_sync" : sync_end_step_unmasked, + + "weights_masked_sync" : syncs_masked, + "weights_unmasked_sync" : syncs_unmasked, + + "cutoff_masked" : cutoff_masked, + "cutoff_unmasked" : cutoff_unmasked + } + + + return (guides, ) + + + + + + +class ClownGuides_Sync_Advanced: + @classmethod + def INPUT_TYPES(cls): + return {"required": + { + "weight_masked": ("FLOAT", {"default": 1.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Set the strength of the guide."}), + "weight_unmasked": ("FLOAT", {"default": 1.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Set the strength of the guide_bkg."}), + "weight_scheduler_masked": (["constant"] + get_res4lyf_scheduler_list(), {"default": "constant"},), + "weight_scheduler_unmasked": (["constant"] + get_res4lyf_scheduler_list(), {"default": "constant"},), + "weight_start_step_masked": ("INT", {"default": 0, "min": 0, "max": 10000}), + "weight_start_step_unmasked": ("INT", {"default": 0, "min": 0, "max": 10000}), + "weight_end_step_masked": ("INT", {"default": 30, "min": -1, "max": 10000}), + "weight_end_step_unmasked": ("INT", {"default": -1, "min": -1, "max": 10000}), + + "sync_masked": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Set the strength of the guide."}), + "sync_unmasked": ("FLOAT", {"default": 1.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Set the strength of the guide_bkg."}), + "sync_scheduler_masked": (["constant"] + get_res4lyf_scheduler_list(), {"default": "constant"},), + "sync_scheduler_unmasked": (["constant"] + get_res4lyf_scheduler_list(), {"default": "constant"},), + "sync_start_step_masked": ("INT", {"default": 0, "min": 0, "max": 10000}), + "sync_start_step_unmasked": ("INT", {"default": 0, "min": 0, "max": 10000}), + "sync_end_step_masked": ("INT", {"default": -1, "min": -1, "max": 10000}), + "sync_end_step_unmasked": ("INT", {"default": -1, "min": -1, "max": 10000}), + + "drift_x_data": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Set the strength of the guide."}), + "drift_x_sync": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Set the strength of the guide."}), + "drift_x_masked": ("FLOAT", {"default": 1.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Set the strength of the guide."}), + "drift_x_unmasked": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Set the strength of the guide_bkg."}), + "drift_x_scheduler_masked": (["constant"] + get_res4lyf_scheduler_list(), {"default": "constant"},), + "drift_x_scheduler_unmasked": (["constant"] + get_res4lyf_scheduler_list(), {"default": "constant"},), + "drift_x_start_step_masked": ("INT", {"default": 0, "min": 0, "max": 10000}), + "drift_x_start_step_unmasked": ("INT", {"default": 0, "min": 0, "max": 10000}), + "drift_x_end_step_masked": ("INT", {"default": -1, "min": -1, "max": 10000}), + "drift_x_end_step_unmasked": ("INT", {"default": -1, "min": -1, "max": 10000}), + + "drift_y_data": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Set the strength of the guide."}), + "drift_y_sync": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Set the strength of the guide."}), + "drift_y_guide": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Set the strength of the guide."}), + "drift_y_masked": ("FLOAT", {"default": 1.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Set the strength of the guide."}), + "drift_y_unmasked": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Set the strength of the guide_bkg."}), + "drift_y_scheduler_masked": (["constant"] + get_res4lyf_scheduler_list(), {"default": "constant"},), + "drift_y_scheduler_unmasked": (["constant"] + get_res4lyf_scheduler_list(), {"default": "constant"},), + "drift_y_start_step_masked": ("INT", {"default": 0, "min": 0, "max": 10000}), + "drift_y_start_step_unmasked": ("INT", {"default": 0, "min": 0, "max": 10000}), + "drift_y_end_step_masked": ("INT", {"default": -1, "min": -1, "max": 10000}), + "drift_y_end_step_unmasked": ("INT", {"default": -1, "min": -1, "max": 10000}), + + "lure_x_masked": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Set the strength of the guide."}), + "lure_x_unmasked": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Set the strength of the guide_bkg."}), + "lure_x_scheduler_masked": (["constant"] + get_res4lyf_scheduler_list(), {"default": "constant"},), + "lure_x_scheduler_unmasked": (["constant"] + get_res4lyf_scheduler_list(), {"default": "constant"},), + "lure_x_start_step_masked": ("INT", {"default": 0, "min": 0, "max": 10000}), + "lure_x_start_step_unmasked": ("INT", {"default": 0, "min": 0, "max": 10000}), + "lure_x_end_step_masked": ("INT", {"default": -1, "min": -1, "max": 10000}), + "lure_x_end_step_unmasked": ("INT", {"default": -1, "min": -1, "max": 10000}), + + "lure_y_masked": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Set the strength of the guide."}), + "lure_y_unmasked": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Set the strength of the guide_bkg."}), + "lure_y_scheduler_masked": (["constant"] + get_res4lyf_scheduler_list(), {"default": "constant"},), + "lure_y_scheduler_unmasked": (["constant"] + get_res4lyf_scheduler_list(), {"default": "constant"},), + "lure_y_start_step_masked": ("INT", {"default": 0, "min": 0, "max": 10000}), + "lure_y_start_step_unmasked": ("INT", {"default": 0, "min": 0, "max": 10000}), + "lure_y_end_step_masked": ("INT", {"default": -1, "min": -1, "max": 10000}), + "lure_y_end_step_unmasked": ("INT", {"default": -1, "min": -1, "max": 10000}), + + "lure_iter": ("INT", {"default": 0, "min": 0, "max": 10000}), + "lure_sequence": (["x -> y", "y -> x", "xy -> xy"], {"default": "y -> x"}), + + "invert_mask": ("BOOLEAN", {"default": False}), + "invert_mask_sync": ("BOOLEAN", {"default": False}), + "invert_mask_drift_x": ("BOOLEAN", {"default": False}), + "invert_mask_drift_y": ("BOOLEAN", {"default": False}), + "invert_mask_lure_x": ("BOOLEAN", {"default": False}), + "invert_mask_lure_y": ("BOOLEAN", {"default": False}), + + }, + "optional": + { + "guide_masked": ("LATENT", ), + "guide_unmasked": ("LATENT", ), + "mask": ("MASK", ), + "mask_sync": ("MASK", ), + "mask_drift_x": ("MASK", ), + "mask_drift_y": ("MASK", ), + "mask_lure_x": ("MASK", ), + "mask_lure_y": ("MASK", ), + "weights_masked": ("SIGMAS", ), + "weights_unmasked": ("SIGMAS", ), + "syncs_masked": ("SIGMAS", ), + "syncs_unmasked": ("SIGMAS", ), + "drift_xs_masked": ("SIGMAS", ), + "drift_xs_unmasked": ("SIGMAS", ), + "drift_ys_masked": ("SIGMAS", ), + "drift_ys_unmasked": ("SIGMAS", ), + "lure_xs_masked": ("SIGMAS", ), + "lure_xs_unmasked": ("SIGMAS", ), + "lure_ys_masked": ("SIGMAS", ), + "lure_ys_unmasked": ("SIGMAS", ), + } + } + + RETURN_TYPES = ("GUIDES",) + RETURN_NAMES = ("guides",) + FUNCTION = "main" + CATEGORY = "RES4LYF/sampler_extensions" + EXPERIMENTAL = True + + def main(self, + weight_masked = 0.0, + weight_unmasked = 0.0, + weight_scheduler_masked = "constant", + weight_scheduler_unmasked = "constant", + weight_start_step_masked = 0, + weight_start_step_unmasked = 0, + weight_end_step_masked = 30, + weight_end_step_unmasked = 30, + + sync_masked = 0.0, + sync_unmasked = 0.0, + sync_scheduler_masked = "constant", + sync_scheduler_unmasked = "constant", + sync_start_step_masked = 0, + sync_start_step_unmasked = 0, + sync_end_step_masked = 30, + sync_end_step_unmasked = 30, + + drift_x_data = 0.0, + drift_x_sync = 0.0, + drift_y_data = 0.0, + drift_y_sync = 0.0, + drift_y_guide = 0.0, + + drift_x_masked = 0.0, + drift_x_unmasked = 0.0, + drift_x_scheduler_masked = "constant", + drift_x_scheduler_unmasked = "constant", + drift_x_start_step_masked = 0, + drift_x_start_step_unmasked = 0, + drift_x_end_step_masked = 30, + drift_x_end_step_unmasked = 30, + + drift_y_masked = 0.0, + drift_y_unmasked = 0.0, + drift_y_scheduler_masked = "constant", + drift_y_scheduler_unmasked = "constant", + drift_y_start_step_masked = 0, + drift_y_start_step_unmasked = 0, + drift_y_end_step_masked = 30, + drift_y_end_step_unmasked = 30, + + lure_x_masked = 0.0, + lure_x_unmasked = 0.0, + lure_x_scheduler_masked = "constant", + lure_x_scheduler_unmasked = "constant", + lure_x_start_step_masked = 0, + lure_x_start_step_unmasked = 0, + lure_x_end_step_masked = 30, + lure_x_end_step_unmasked = 30, + + lure_y_masked = 0.0, + lure_y_unmasked = 0.0, + lure_y_scheduler_masked = "constant", + lure_y_scheduler_unmasked = "constant", + lure_y_start_step_masked = 0, + lure_y_start_step_unmasked = 0, + lure_y_end_step_masked = 30, + lure_y_end_step_unmasked = 30, + + guide_masked = None, + guide_unmasked = None, + + weights_masked = None, + weights_unmasked = None, + syncs_masked = None, + syncs_unmasked = None, + drift_xs_masked = None, + drift_xs_unmasked = None, + drift_ys_masked = None, + drift_ys_unmasked = None, + lure_xs_masked = None, + lure_xs_unmasked = None, + lure_ys_masked = None, + lure_ys_unmasked = None, + + lure_iter = 0, + lure_sequence = "x -> y", + + mask = None, + unmask = None, + mask_sync = None, + mask_drift_x = None, + mask_drift_y = None, + mask_lure_x = None, + mask_lure_y = None, + + invert_mask = False, + invert_mask_sync = False, + invert_mask_drift_x = False, + invert_mask_drift_y = False, + invert_mask_lure_x = False, + invert_mask_lure_y = False, + + guide_mode = "sync", + channelwise_mode = False, + projection_mode = False, + + cutoff_masked = 1.0, + cutoff_unmasked = 1.0, + ): + + default_dtype = torch.float64 + + if weight_end_step_masked == -1: + weight_end_step_masked = MAX_STEPS + if weight_end_step_unmasked == -1: + weight_end_step_unmasked = MAX_STEPS + + if sync_end_step_masked == -1: + sync_end_step_masked = MAX_STEPS + if sync_end_step_unmasked == -1: + sync_end_step_unmasked = MAX_STEPS + + if drift_x_end_step_masked == -1: + drift_x_end_step_masked = MAX_STEPS + if drift_x_end_step_unmasked == -1: + drift_x_end_step_unmasked = MAX_STEPS + if drift_y_end_step_masked == -1: + drift_y_end_step_masked = MAX_STEPS + if drift_y_end_step_unmasked == -1: + drift_y_end_step_unmasked = MAX_STEPS + + if lure_x_end_step_masked == -1: + lure_x_end_step_masked = MAX_STEPS + if lure_x_end_step_unmasked == -1: + lure_x_end_step_unmasked = MAX_STEPS + if lure_y_end_step_masked == -1: + lure_y_end_step_masked = MAX_STEPS + if lure_y_end_step_unmasked == -1: + lure_y_end_step_unmasked = MAX_STEPS + + + + if guide_masked is None: + weight_scheduler_masked = "constant" + weight_start_step_masked = 0 + weight_end_step_masked = 30 + weight_masked = 0.0 + weights_masked = None + + sync_scheduler_masked = "constant" + sync_start_step_masked = 0 + sync_end_step_masked = 30 + sync_masked = 0.0 + syncs_masked = None + + drift_x_scheduler_masked = "constant" + drift_x_start_step_masked = 0 + drift_x_end_step_masked = 30 + drift_x_masked = 0.0 + drift_xs_masked = None + + drift_y_scheduler_masked = "constant" + drift_y_start_step_masked = 0 + drift_y_end_step_masked = 30 + drift_y_masked = 0.0 + drift_ys_masked = None + + lure_x_scheduler_masked = "constant" + lure_x_start_step_masked = 0 + lure_x_end_step_masked = 30 + lure_x_masked = 0.0 + lure_xs_masked = None + + lure_y_scheduler_masked = "constant" + lure_y_start_step_masked = 0 + lure_y_end_step_masked = 30 + lure_y_masked = 0.0 + lure_ys_masked = None + + if guide_unmasked is None: + weight_scheduler_unmasked = "constant" + weight_start_step_unmasked = 0 + weight_end_step_unmasked = 30 + weight_unmasked = 0.0 + weights_unmasked = None + + sync_scheduler_unmasked = "constant" + sync_start_step_unmasked = 0 + sync_end_step_unmasked = 30 + sync_unmasked = 0.0 + syncs_unmasked = None + + drift_x_scheduler_unmasked = "constant" + drift_x_start_step_unmasked = 0 + drift_x_end_step_unmasked = 30 + drift_x_unmasked = 0.0 + drift_xs_unmasked = None + + drift_y_scheduler_unmasked = "constant" + drift_y_start_step_unmasked = 0 + drift_y_end_step_unmasked = 30 + drift_y_unmasked = 0.0 + drift_ys_unmasked = None + + lure_x_scheduler_unmasked = "constant" + lure_x_start_step_unmasked = 0 + lure_x_end_step_unmasked = 30 + lure_x_unmasked = 0.0 + lure_xs_unmasked = None + + lure_y_scheduler_unmasked = "constant" + lure_y_start_step_unmasked = 0 + lure_y_end_step_unmasked = 30 + lure_y_unmasked = 0.0 + lure_ys_unmasked = None + + + if guide_masked is not None: + raw_x = guide_masked.get('state_info', {}).get('raw_x', None) + if False: #raw_x is not None: + guide_masked = {'samples': guide_masked['state_info']['raw_x'].clone()} + else: + guide_masked = {'samples': guide_masked['samples'].clone()} + + if guide_unmasked is not None: + raw_x = guide_unmasked.get('state_info', {}).get('raw_x', None) + if False: #raw_x is not None: + guide_unmasked = {'samples': guide_unmasked['state_info']['raw_x'].clone()} + else: + guide_unmasked = {'samples': guide_unmasked['samples'].clone()} + + if invert_mask and mask is not None: + mask = 1-mask + if invert_mask_sync and mask_sync is not None: + mask_sync = 1-mask_sync + if invert_mask_drift_x and mask_drift_x is not None: + mask_drift_x = 1-mask_drift_x + if invert_mask_drift_y and mask_drift_y is not None: + mask_drift_y = 1-mask_drift_y + if invert_mask_lure_x and mask_lure_x is not None: + mask_lure_x = 1-mask_lure_x + if invert_mask_lure_y and mask_lure_y is not None: + mask_lure_y = 1-mask_lure_y + + if projection_mode: + guide_mode = guide_mode + "_projection" + + if channelwise_mode: + guide_mode = guide_mode + "_cw" + + if guide_mode == "unsample_cw": + guide_mode = "unsample" + if guide_mode == "resample_cw": + guide_mode = "resample" + + if weight_scheduler_masked == "constant" and weights_masked == None: + weights_masked = initialize_or_scale(None, weight_masked, weight_end_step_masked).to(default_dtype) + prepend = torch.zeros(weight_start_step_masked, dtype=default_dtype, device=weights_masked.device) + weights_masked = torch.cat((prepend, weights_masked), dim=0) + weights_masked = F.pad(weights_masked, (0, MAX_STEPS), value=0.0) + + if weight_scheduler_unmasked == "constant" and weights_unmasked == None: + weights_unmasked = initialize_or_scale(None, weight_unmasked, weight_end_step_unmasked).to(default_dtype) + prepend = torch.zeros(weight_start_step_unmasked, dtype=default_dtype, device=weights_unmasked.device) + weights_unmasked = torch.cat((prepend, weights_unmasked), dim=0) + weights_unmasked = F.pad(weights_unmasked, (0, MAX_STEPS), value=0.0) + + # Values for the sync scheduler will be inverted in rk_guide_func_beta.py as it's easier to understand: + # makes it so that a sync weight of 1.0 = full guide strength (which previously was 0.0) + if sync_scheduler_masked == "constant" and syncs_masked == None: + syncs_masked = initialize_or_scale(None, sync_masked, sync_end_step_masked).to(default_dtype) + prepend = torch.zeros(sync_start_step_masked, dtype=default_dtype, device=syncs_masked.device) + syncs_masked = torch.cat((prepend, syncs_masked), dim=0) + syncs_masked = F.pad(syncs_masked, (0, MAX_STEPS), value=0.0) + + if sync_scheduler_unmasked == "constant" and syncs_unmasked == None: + syncs_unmasked = initialize_or_scale(None, sync_unmasked, sync_end_step_unmasked).to(default_dtype) + prepend = torch.zeros(sync_start_step_unmasked, dtype=default_dtype, device=syncs_unmasked.device) + syncs_unmasked = torch.cat((prepend, syncs_unmasked), dim=0) + syncs_unmasked = F.pad(syncs_unmasked, (0, MAX_STEPS), value=0.0) + + if drift_x_scheduler_masked == "constant" and drift_xs_masked == None: + drift_xs_masked = initialize_or_scale(None, drift_x_masked, drift_x_end_step_masked).to(default_dtype) + prepend = torch.zeros(drift_x_start_step_masked, dtype=default_dtype, device=drift_xs_masked.device) + drift_xs_masked = torch.cat((prepend, drift_xs_masked), dim=0) + drift_xs_masked = F.pad(drift_xs_masked, (0, MAX_STEPS), value=0.0) + + if drift_x_scheduler_unmasked == "constant" and drift_xs_unmasked == None: + drift_xs_unmasked = initialize_or_scale(None, drift_x_unmasked, drift_x_end_step_unmasked).to(default_dtype) + prepend = torch.zeros(drift_x_start_step_unmasked, dtype=default_dtype, device=drift_xs_unmasked.device) + drift_xs_unmasked = torch.cat((prepend, drift_xs_unmasked), dim=0) + drift_xs_unmasked = F.pad(drift_xs_unmasked, (0, MAX_STEPS), value=0.0) + + if drift_y_scheduler_masked == "constant" and drift_ys_masked == None: + drift_ys_masked = initialize_or_scale(None, drift_y_masked, drift_y_end_step_masked).to(default_dtype) + prepend = torch.zeros(drift_y_start_step_masked, dtype=default_dtype, device=drift_ys_masked.device) + drift_ys_masked = torch.cat((prepend, drift_ys_masked), dim=0) + drift_ys_masked = F.pad(drift_ys_masked, (0, MAX_STEPS), value=0.0) + + if drift_y_scheduler_unmasked == "constant" and drift_ys_unmasked == None: + drift_ys_unmasked = initialize_or_scale(None, drift_y_unmasked, drift_y_end_step_unmasked).to(default_dtype) + prepend = torch.zeros(drift_y_start_step_unmasked, dtype=default_dtype, device=drift_ys_unmasked.device) + drift_ys_unmasked = torch.cat((prepend, drift_ys_unmasked), dim=0) + drift_ys_unmasked = F.pad(drift_ys_unmasked, (0, MAX_STEPS), value=0.0) + + if lure_x_scheduler_masked == "constant" and lure_xs_masked == None: + lure_xs_masked = initialize_or_scale(None, lure_x_masked, lure_x_end_step_masked).to(default_dtype) + prepend = torch.zeros(lure_x_start_step_masked, dtype=default_dtype, device=lure_xs_masked.device) + lure_xs_masked = torch.cat((prepend, lure_xs_masked), dim=0) + lure_xs_masked = F.pad(lure_xs_masked, (0, MAX_STEPS), value=0.0) + + if lure_x_scheduler_unmasked == "constant" and lure_xs_unmasked == None: + lure_xs_unmasked = initialize_or_scale(None, lure_x_unmasked, lure_x_end_step_unmasked).to(default_dtype) + prepend = torch.zeros(lure_x_start_step_unmasked, dtype=default_dtype, device=lure_xs_unmasked.device) + lure_xs_unmasked = torch.cat((prepend, lure_xs_unmasked), dim=0) + lure_xs_unmasked = F.pad(lure_xs_unmasked, (0, MAX_STEPS), value=0.0) + + if lure_y_scheduler_masked == "constant" and lure_ys_masked == None: + lure_ys_masked = initialize_or_scale(None, lure_y_masked, lure_y_end_step_masked).to(default_dtype) + prepend = torch.zeros(lure_y_start_step_masked, dtype=default_dtype, device=lure_ys_masked.device) + lure_ys_masked = torch.cat((prepend, lure_ys_masked), dim=0) + lure_ys_masked = F.pad(lure_ys_masked, (0, MAX_STEPS), value=0.0) + + if lure_y_scheduler_unmasked == "constant" and lure_ys_unmasked == None: + lure_ys_unmasked = initialize_or_scale(None, lure_y_unmasked, lure_y_end_step_unmasked).to(default_dtype) + prepend = torch.zeros(lure_y_start_step_unmasked, dtype=default_dtype, device=lure_ys_unmasked.device) + lure_ys_unmasked = torch.cat((prepend, lure_ys_unmasked), dim=0) + lure_ys_unmasked = F.pad(lure_ys_unmasked, (0, MAX_STEPS), value=0.0) + + + guides = { + "guide_mode" : guide_mode, + + "guide_masked" : guide_masked, + "guide_unmasked" : guide_unmasked, + "mask" : mask, + "unmask" : unmask, + "mask_sync" : mask_sync, + "mask_lure_x" : mask_lure_x, + "mask_lure_y" : mask_lure_y, + + "weight_masked" : weight_masked, + "weight_unmasked" : weight_unmasked, + "weight_scheduler_masked" : weight_scheduler_masked, + "weight_scheduler_unmasked" : weight_scheduler_unmasked, + "start_step_masked" : weight_start_step_masked, + "start_step_unmasked" : weight_start_step_unmasked, + "end_step_masked" : weight_end_step_masked, + "end_step_unmasked" : weight_end_step_unmasked, + + "weights_masked" : weights_masked, + "weights_unmasked" : weights_unmasked, + + "weight_masked_sync" : sync_masked, + "weight_unmasked_sync" : sync_unmasked, + "weight_scheduler_masked_sync" : sync_scheduler_masked, + "weight_scheduler_unmasked_sync" : sync_scheduler_unmasked, + "start_step_masked_sync" : sync_start_step_masked, + "start_step_unmasked_sync" : sync_start_step_unmasked, + "end_step_masked_sync" : sync_end_step_masked, + "end_step_unmasked_sync" : sync_end_step_unmasked, + + "weights_masked_sync" : syncs_masked, + "weights_unmasked_sync" : syncs_unmasked, + + "drift_x_data" : drift_x_data, + "drift_x_sync" : drift_x_sync, + "drift_y_data" : drift_y_data, + "drift_y_sync" : drift_y_sync, + "drift_y_guide" : drift_y_guide, + + "weight_masked_drift_x" : drift_x_masked, + "weight_unmasked_drift_x" : drift_x_unmasked, + "weight_scheduler_masked_drift_x" : drift_x_scheduler_masked, + "weight_scheduler_unmasked_drift_x" : drift_x_scheduler_unmasked, + "start_step_masked_drift_x" : drift_x_start_step_masked, + "start_step_unmasked_drift_x" : drift_x_start_step_unmasked, + "end_step_masked_drift_x" : drift_x_end_step_masked, + "end_step_unmasked_drift_x" : drift_x_end_step_unmasked, + + "weights_masked_drift_x" : drift_xs_masked, + "weights_unmasked_drift_x" : drift_xs_unmasked, + + + "weight_masked_drift_y" : drift_y_masked, + "weight_unmasked_drift_y" : drift_y_unmasked, + "weight_scheduler_masked_drift_y" : drift_y_scheduler_masked, + "weight_scheduler_unmasked_drift_y" : drift_y_scheduler_unmasked, + "start_step_masked_drift_y" : drift_y_start_step_masked, + "start_step_unmasked_drift_y" : drift_y_start_step_unmasked, + "end_step_masked_drift_y" : drift_y_end_step_masked, + "end_step_unmasked_drift_y" : drift_y_end_step_unmasked, + + "weights_masked_drift_y" : drift_ys_masked, + "weights_unmasked_drift_y" : drift_ys_unmasked, + + "weight_masked_lure_x" : lure_x_masked, + "weight_unmasked_lure_x" : lure_x_unmasked, + "weight_scheduler_masked_lure_x" : lure_x_scheduler_masked, + "weight_scheduler_unmasked_lure_x" : lure_x_scheduler_unmasked, + "start_step_masked_lure_x" : lure_x_start_step_masked, + "start_step_unmasked_lure_x" : lure_x_start_step_unmasked, + "end_step_masked_lure_x" : lure_x_end_step_masked, + "end_step_unmasked_lure_x" : lure_x_end_step_unmasked, + + "weights_masked_lure_x" : lure_xs_masked, + "weights_unmasked_lure_x" : lure_xs_unmasked, + + + "weight_masked_lure_y" : lure_y_masked, + "weight_unmasked_lure_y" : lure_y_unmasked, + "weight_scheduler_masked_lure_y" : lure_y_scheduler_masked, + "weight_scheduler_unmasked_lure_y" : lure_y_scheduler_unmasked, + "start_step_masked_lure_y" : lure_y_start_step_masked, + "start_step_unmasked_lure_y" : lure_y_start_step_unmasked, + "end_step_masked_lure_y" : lure_y_end_step_masked, + "end_step_unmasked_lure_y" : lure_y_end_step_unmasked, + + "weights_masked_lure_y" : lure_ys_masked, + "weights_unmasked_lure_y" : lure_ys_unmasked, + + "sync_lure_iter" : lure_iter, + "sync_lure_sequence" : lure_sequence, + + "cutoff_masked" : cutoff_masked, + "cutoff_unmasked" : cutoff_unmasked + } + + + return (guides, ) + + + + + + + + + +class ClownGuide_Beta: + @classmethod + def INPUT_TYPES(cls): + return {"required": + { + "guide_mode": (GUIDE_MODE_NAMES_BETA_SIMPLE, {"default": 'epsilon', "tooltip": "Recommended: epsilon or mean/mean_std with sampler_mode = standard, and unsample/resample with sampler_mode = unsample/resample. Epsilon_dynamic_mean, etc. are only used with two latent inputs and a mask. Blend/hard_light/mean/mean_std etc. require low strengths, start with 0.01-0.02."}), + "channelwise_mode": ("BOOLEAN", {"default": True}), + "projection_mode": ("BOOLEAN", {"default": True}), + "weight": ("FLOAT", {"default": 0.75, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Set the strength of the guide."}), + "cutoff": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step":0.01, "round": False, "tooltip": "Disables the guide for the next step when the denoised image is similar to the guide. Higher values will strengthen the effect."}), + "weight_scheduler": (["constant"] + get_res4lyf_scheduler_list(), {"default": "beta57"},), + "start_step": ("INT", {"default": 0, "min": 0, "max": 10000}), + "end_step": ("INT", {"default": 15, "min": -1, "max": 10000}), + "invert_mask": ("BOOLEAN", {"default": False}), + }, + "optional": + { + "guide": ("LATENT", ), + "mask": ("MASK", ), + "weights": ("SIGMAS", ), + } + } + + RETURN_TYPES = ("GUIDES",) + RETURN_NAMES = ("guides",) + FUNCTION = "main" + CATEGORY = "RES4LYF/sampler_extensions" + + def main(self, + weight_scheduler = "constant", + weight_scheduler_unmasked = "constant", + start_step = 0, + start_step_unmasked = 0, + end_step = 30, + end_step_unmasked = 30, + cutoff = 1.0, + cutoff_unmasked = 1.0, + guide = None, + guide_unmasked = None, + weight = 0.0, + weight_unmasked = 0.0, + + guide_mode = "epsilon", + channelwise_mode = False, + projection_mode = False, + weights = None, + weights_unmasked = None, + mask = None, + unmask = None, + invert_mask = False, + ): + + CG = ClownGuides_Beta() + + mask = 1-mask if mask is not None else None + + if end_step == -1: + end_step = MAX_STEPS + + if guide is not None: + raw_x = guide.get('state_info', {}).get('raw_x', None) + + if False: # raw_x is not None: + guide = {'samples': guide['state_info']['raw_x'].clone()} + else: + guide = {'samples': guide['samples'].clone()} + + if guide_unmasked is not None: + raw_x = guide_unmasked.get('state_info', {}).get('raw_x', None) + if False: #raw_x is not None: + guide_unmasked = {'samples': guide_unmasked['state_info']['raw_x'].clone()} + else: + guide_unmasked = {'samples': guide_unmasked['samples'].clone()} + + guides, = CG.main( + weight_scheduler_masked = weight_scheduler, + weight_scheduler_unmasked = weight_scheduler_unmasked, + start_step_masked = start_step, + start_step_unmasked = start_step_unmasked, + end_step_masked = end_step, + end_step_unmasked = end_step_unmasked, + cutoff_masked = cutoff, + cutoff_unmasked = cutoff_unmasked, + guide_masked = guide, + guide_unmasked = guide_unmasked, + weight_masked = weight, + weight_unmasked = weight_unmasked, + + guide_mode = guide_mode, + channelwise_mode = channelwise_mode, + projection_mode = projection_mode, + weights_masked = weights, + weights_unmasked = weights_unmasked, + mask = mask, + unmask = unmask, + invert_mask = invert_mask + ) + + return (guides, ) + + + #return (guides[0], ) + + + + +class ClownGuides_Beta: + @classmethod + def INPUT_TYPES(cls): + return {"required": + { + "guide_mode": (GUIDE_MODE_NAMES_BETA_SIMPLE, {"default": 'epsilon', "tooltip": "Recommended: epsilon or mean/mean_std with sampler_mode = standard, and unsample/resample with sampler_mode = unsample/resample. Epsilon_dynamic_mean, etc. are only used with two latent inputs and a mask. Blend/hard_light/mean/mean_std etc. require low strengths, start with 0.01-0.02."}), + "channelwise_mode": ("BOOLEAN", {"default": True}), + "projection_mode": ("BOOLEAN", {"default": True}), + "weight_masked": ("FLOAT", {"default": 0.75, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Set the strength of the guide."}), + "weight_unmasked": ("FLOAT", {"default": 0.75, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Set the strength of the guide_bkg."}), + "cutoff_masked": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step":0.01, "round": False, "tooltip": "Disables the guide for the next step when the denoised image is similar to the guide. Higher values will strengthen the effect."}), + "cutoff_unmasked": ("FLOAT", {"default": 1.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Disables the guide for the next step when the denoised image is similar to the guide. Higher values will strengthen the effect."}), + "weight_scheduler_masked": (["constant"] + get_res4lyf_scheduler_list(), {"default": "beta57"},), + "weight_scheduler_unmasked": (["constant"] + get_res4lyf_scheduler_list(), {"default": "constant"},), + "start_step_masked": ("INT", {"default": 0, "min": 0, "max": 10000}), + "start_step_unmasked": ("INT", {"default": 0, "min": 0, "max": 10000}), + "end_step_masked": ("INT", {"default": 15, "min": -1, "max": 10000}), + "end_step_unmasked": ("INT", {"default": 15, "min": -1, "max": 10000}), + "invert_mask": ("BOOLEAN", {"default": False}), + }, + "optional": + { + "guide_masked": ("LATENT", ), + "guide_unmasked": ("LATENT", ), + "mask": ("MASK", ), + "weights_masked": ("SIGMAS", ), + "weights_unmasked": ("SIGMAS", ), + } + } + + RETURN_TYPES = ("GUIDES",) + RETURN_NAMES = ("guides",) + FUNCTION = "main" + CATEGORY = "RES4LYF/sampler_extensions" + + def main(self, + weight_scheduler_masked = "constant", + weight_scheduler_unmasked = "constant", + start_step_masked = 0, + start_step_unmasked = 0, + end_step_masked = 30, + end_step_unmasked = 30, + cutoff_masked = 1.0, + cutoff_unmasked = 1.0, + guide_masked = None, + guide_unmasked = None, + weight_masked = 0.0, + weight_unmasked = 0.0, + + guide_mode = "epsilon", + channelwise_mode = False, + projection_mode = False, + weights_masked = None, + weights_unmasked = None, + mask = None, + unmask = None, + invert_mask = False, + ): + + default_dtype = torch.float64 + + if end_step_masked == -1: + end_step_masked = MAX_STEPS + if end_step_unmasked == -1: + end_step_unmasked = MAX_STEPS + + if guide_masked is None: + weight_scheduler_masked = "constant" + start_step_masked = 0 + end_step_masked = 30 + cutoff_masked = 1.0 + guide_masked = None + weight_masked = 0.0 + weights_masked = None + #mask = None + + if guide_unmasked is None: + weight_scheduler_unmasked = "constant" + start_step_unmasked = 0 + end_step_unmasked = 30 + cutoff_unmasked = 1.0 + guide_unmasked = None + weight_unmasked = 0.0 + weights_unmasked = None + #unmask = None + + if guide_masked is not None: + raw_x = guide_masked.get('state_info', {}).get('raw_x', None) + if False: #raw_x is not None: + guide_masked = {'samples': guide_masked['state_info']['raw_x'].clone()} + else: + guide_masked = {'samples': guide_masked['samples'].clone()} + + if guide_unmasked is not None: + raw_x = guide_unmasked.get('state_info', {}).get('raw_x', None) + if False: #raw_x is not None: + guide_unmasked = {'samples': guide_unmasked['state_info']['raw_x'].clone()} + else: + guide_unmasked = {'samples': guide_unmasked['samples'].clone()} + + if invert_mask and mask is not None: + mask = 1-mask + + if projection_mode: + guide_mode = guide_mode + "_projection" + + if channelwise_mode: + guide_mode = guide_mode + "_cw" + + if guide_mode == "unsample_cw": + guide_mode = "unsample" + if guide_mode == "resample_cw": + guide_mode = "resample" + + if weight_scheduler_masked == "constant" and weights_masked == None: + weights_masked = initialize_or_scale(None, weight_masked, end_step_masked).to(default_dtype) + prepend = torch.zeros(start_step_masked, dtype=default_dtype, device=weights_masked.device) + weights_masked = torch.cat((prepend, weights_masked), dim=0) + weights_masked = F.pad(weights_masked, (0, MAX_STEPS), value=0.0) + + if weight_scheduler_unmasked == "constant" and weights_unmasked == None: + weights_unmasked = initialize_or_scale(None, weight_unmasked, end_step_unmasked).to(default_dtype) + prepend = torch.zeros(start_step_unmasked, dtype=default_dtype, device=weights_unmasked.device) + weights_unmasked = torch.cat((prepend, weights_unmasked), dim=0) + weights_unmasked = F.pad(weights_unmasked, (0, MAX_STEPS), value=0.0) + + guides = { + "guide_mode" : guide_mode, + "weight_masked" : weight_masked, + "weight_unmasked" : weight_unmasked, + "weights_masked" : weights_masked, + "weights_unmasked" : weights_unmasked, + "guide_masked" : guide_masked, + "guide_unmasked" : guide_unmasked, + "mask" : mask, + "unmask" : unmask, + + "weight_scheduler_masked" : weight_scheduler_masked, + "weight_scheduler_unmasked" : weight_scheduler_unmasked, + "start_step_masked" : start_step_masked, + "start_step_unmasked" : start_step_unmasked, + "end_step_masked" : end_step_masked, + "end_step_unmasked" : end_step_unmasked, + "cutoff_masked" : cutoff_masked, + "cutoff_unmasked" : cutoff_unmasked + } + + + return (guides, ) + + + + +class ClownGuidesAB_Beta: + @classmethod + def INPUT_TYPES(cls): + return {"required": + { + "guide_mode": (GUIDE_MODE_NAMES_BETA_SIMPLE, {"default": 'epsilon', "tooltip": "Recommended: epsilon or mean/mean_std with sampler_mode = standard, and unsample/resample with sampler_mode = unsample/resample. Epsilon_dynamic_mean, etc. are only used with two latent inputs and a mask. Blend/hard_light/mean/mean_std etc. require low strengths, start with 0.01-0.02."}), + "channelwise_mode": ("BOOLEAN", {"default": False}), + "projection_mode": ("BOOLEAN", {"default": False}), + "weight_A": ("FLOAT", {"default": 0.75, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Set the strength of the guide."}), + "weight_B": ("FLOAT", {"default": 0.75, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Set the strength of the guide_bkg."}), + "cutoff_A": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step":0.01, "round": False, "tooltip": "Disables the guide for the next step when the denoised image is similar to the guide. Higher values will strengthen the effect."}), + "cutoff_B": ("FLOAT", {"default": 1.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Disables the guide for the next step when the denoised image is similar to the guide. Higher values will strengthen the effect."}), + "weight_scheduler_A": (["constant"] + get_res4lyf_scheduler_list(), {"default": "beta57"},), + "weight_scheduler_B": (["constant"] + get_res4lyf_scheduler_list(), {"default": "constant"},), + "start_step_A": ("INT", {"default": 0, "min": 0, "max": 10000}), + "start_step_B": ("INT", {"default": 0, "min": 0, "max": 10000}), + "end_step_A": ("INT", {"default": 15, "min": -1, "max": 10000}), + "end_step_B": ("INT", {"default": 15, "min": -1, "max": 10000}), + "invert_masks": ("BOOLEAN", {"default": False}), + }, + "optional": + { + "guide_A": ("LATENT", ), + "guide_B": ("LATENT", ), + "mask_A": ("MASK", ), + "mask_B": ("MASK", ), + "weights_A": ("SIGMAS", ), + "weights_B": ("SIGMAS", ), + } + } + + RETURN_TYPES = ("GUIDES",) + RETURN_NAMES = ("guides",) + FUNCTION = "main" + CATEGORY = "RES4LYF/sampler_extensions" + + def main(self, + weight_scheduler_A = "constant", + weight_scheduler_B = "constant", + start_step_A = 0, + start_step_B = 0, + end_step_A = 30, + end_step_B = 30, + cutoff_A = 1.0, + cutoff_B = 1.0, + guide_A = None, + guide_B = None, + weight_A = 0.0, + weight_B = 0.0, + + guide_mode = "epsilon", + channelwise_mode = False, + projection_mode = False, + weights_A = None, + weights_B = None, + mask_A = None, + mask_B = None, + invert_masks : bool = False, + ): + + default_dtype = torch.float64 + + if end_step_A == -1: + end_step_A = MAX_STEPS + if end_step_B == -1: + end_step_B = MAX_STEPS + + if guide_A is not None: + raw_x = guide_A.get('state_info', {}).get('raw_x', None) + if False: #raw_x is not None: + guide_A = {'samples': guide_A['state_info']['raw_x'].clone()} + else: + guide_A = {'samples': guide_A['samples'].clone()} + + if guide_B is not None: + raw_x = guide_B.get('state_info', {}).get('raw_x', None) + if False: #raw_x is not None: + guide_B = {'samples': guide_B['state_info']['raw_x'].clone()} + else: + guide_B = {'samples': guide_B['samples'].clone()} + + if guide_A is None: + guide_A = guide_B + guide_B = None + mask_A = mask_B + mask_B = None + weight_B = 0.0 + + if guide_B is None: + weight_B = 0.0 + + if mask_A is None and mask_B is not None: + mask_A = 1-mask_B + + if projection_mode: + guide_mode = guide_mode + "_projection" + + if channelwise_mode: + guide_mode = guide_mode + "_cw" + + if guide_mode == "unsample_cw": + guide_mode = "unsample" + if guide_mode == "resample_cw": + guide_mode = "resample" + + if weight_scheduler_A == "constant" and weights_A == None: + weights_A = initialize_or_scale(None, weight_A, end_step_A).to(default_dtype) + prepend = torch.zeros(start_step_A, dtype=default_dtype, device=weights_A.device) + weights_A = torch.cat((prepend, weights_A), dim=0) + weights_A = F.pad(weights_A, (0, MAX_STEPS), value=0.0) + + if weight_scheduler_B == "constant" and weights_B == None: + weights_B = initialize_or_scale(None, weight_B, end_step_B).to(default_dtype) + prepend = torch.zeros(start_step_B, dtype=default_dtype, device=weights_B.device) + weights_B = torch.cat((prepend, weights_B), dim=0) + weights_B = F.pad(weights_B, (0, MAX_STEPS), value=0.0) + + if invert_masks: + mask_A = 1-mask_A if mask_A is not None else None + mask_B = 1-mask_B if mask_B is not None else None + + guides = { + "guide_mode" : guide_mode, + "weight_masked" : weight_A, + "weight_unmasked" : weight_B, + "weights_masked" : weights_A, + "weights_unmasked" : weights_B, + "guide_masked" : guide_A, + "guide_unmasked" : guide_B, + "mask" : mask_A, + "unmask" : mask_B, + + "weight_scheduler_masked" : weight_scheduler_A, + "weight_scheduler_unmasked" : weight_scheduler_B, + "start_step_masked" : start_step_A, + "start_step_unmasked" : start_step_B, + "end_step_masked" : end_step_A, + "end_step_unmasked" : end_step_B, + "cutoff_masked" : cutoff_A, + "cutoff_unmasked" : cutoff_B + } + + return (guides, ) + + + +class ClownOptions_Combine: + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "options": ("OPTIONS",), + }, + } + + RETURN_TYPES = ("OPTIONS",) + RETURN_NAMES = ("options",) + FUNCTION = "main" + CATEGORY = "RES4LYF/sampler_options" + + def main(self, options, **kwargs): + options_mgr = OptionsManager(options, **kwargs) + return (options_mgr.as_dict(),) + + + +class ClownOptions_Frameweights: + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "config_name": (FRAME_WEIGHTS_CONFIG_NAMES, {"default": "frame_weights", "tooltip": "Apply to specific type of per-frame weights."}), + "dynamics": (FRAME_WEIGHTS_DYNAMICS_NAMES, {"default": "ease_out", "tooltip": "The function type used for the dynamic period. constant: no change, linear: steady change, ease_out: starts fast, ease_in: starts slow"}), + "schedule": (FRAME_WEIGHTS_SCHEDULE_NAMES, {"default": "moderate_early", "tooltip": "fast_early: fast change starts immediately, slow_late: slow change starts later"}), + "scale": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 1.0, "step": 0.01, "tooltip": "The amount of change over the course of the frame weights. 1.0 means that the guides have no influence by the end."}), + "reverse": ("BOOLEAN", {"default": False, "tooltip": "Reverse the frame weights"}), + }, + "optional": { + "frame_weights": ("SIGMAS", {"tooltip": "Overrides all other settings EXCEPT reverse."}), + "custom_string": ("STRING", {"tooltip": "Overrides all other settings EXCEPT reverse.", "multiline": True}), + "options": ("OPTIONS",), + }, + } + + RETURN_TYPES = ("OPTIONS",) + RETURN_NAMES = ("options",) + FUNCTION = "main" + CATEGORY = "RES4LYF/sampler_options" + + def main(self, + config_name, + dynamics, + schedule, + scale, + reverse, + frame_weights = None, + custom_string = None, + options = None, + ): + + options_mgr = OptionsManager(options if options is not None else {}) + + frame_weights_mgr = options_mgr.get("frame_weights_mgr") + if frame_weights_mgr is None: + frame_weights_mgr = FrameWeightsManager() + + if custom_string is not None and custom_string.strip() == "": + custom_string = None + + frame_weights_mgr.add_weight_config( + config_name, + dynamics=dynamics, + schedule=schedule, + scale=scale, + is_reversed=reverse, + frame_weights=frame_weights, + custom_string=custom_string + ) + + options_mgr.update("frame_weights_mgr", frame_weights_mgr) + + return (options_mgr.as_dict(),) + + +class SharkOptions_GuiderInput: + @classmethod + def INPUT_TYPES(s): + return {"required": + {"guider": ("GUIDER", ), + }, + "optional": + {"options": ("OPTIONS", ), + } + } + RETURN_TYPES = ("OPTIONS",) + RETURN_NAMES = ("options",) + FUNCTION = "main" + CATEGORY = "RES4LYF/sampler_options" + + def main(self, guider, options=None): + options_mgr = OptionsManager(options if options is not None else {}) + + if isinstance(guider, dict): + guider = guider.get('samples', None) + + if isinstance(guider, torch.Tensor): + guider = guider.detach().cpu() + + if options_mgr is None: + options_mgr = OptionsManager() + + options_mgr.update("guider", guider) + + return (options_mgr.as_dict(), ) + + + + + +class ClownGuide_AdaIN_MMDiT_Beta: + @classmethod + def INPUT_TYPES(cls): + return {"required": + { + "weight": ("FLOAT", {"default": 1.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Set the strength of the guide by multiplying all other weights by this value."}), + "weight_scheduler": (["constant"] + get_res4lyf_scheduler_list(), {"default": "constant"},), + "double_blocks" : ("STRING", {"default": "", "multiline": True}), + "double_weights" : ("STRING", {"default": "", "multiline": True}), + "single_blocks" : ("STRING", {"default": "20", "multiline": True}), + "single_weights" : ("STRING", {"default": "0.5", "multiline": True}), + "start_step": ("INT", {"default": 0, "min": 0, "max": 10000}), + "end_step": ("INT", {"default": 15, "min": -1, "max": 10000}), + "invert_mask": ("BOOLEAN", {"default": False}), + }, + "optional": + { + "guide": ("LATENT", ), + "mask": ("MASK", ), + "weights": ("SIGMAS", ), + "guides": ("GUIDES", ), + } + } + + RETURN_TYPES = ("GUIDES",) + RETURN_NAMES = ("guides",) + FUNCTION = "main" + CATEGORY = "RES4LYF/sampler_extensions" + + def main(self, + weight = 1.0, + weight_scheduler = "constant", + double_weights = "0.1", + single_weights = "0.0", + double_blocks = "all", + single_blocks = "all", + start_step = 0, + end_step = 15, + invert_mask = False, + + guide = None, + mask = None, + weights = None, + guides = None, + ): + + default_dtype = torch.float64 + + mask = 1-mask if mask is not None else None + + double_weights = parse_range_string(double_weights) + single_weights = parse_range_string(single_weights) + + if len(double_weights) == 0: + double_weights.append(0.0) + if len(single_weights) == 0: + single_weights.append(0.0) + + if len(double_weights) == 1: + double_weights = double_weights * 100 + if len(single_weights) == 1: + single_weights = single_weights * 100 + + if type(double_weights[0]) == int: + double_weights = [float(val) for val in double_weights] + if type(single_weights[0]) == int: + single_weights = [float(val) for val in single_weights] + + if double_blocks == "all": + double_blocks = [val for val in range(100)] + if len(double_weights) == 1: + double_weights = [double_weights[0]] * 100 + else: + double_blocks = parse_range_string(double_blocks) + + weights_expanded = [0.0] * 100 + for b, w in zip(double_blocks, double_weights): + weights_expanded[b] = w + double_weights = weights_expanded + + + if single_blocks == "all": + single_blocks = [val for val in range(100)] + if len(single_weights) == 1: + single_weights = [single_weights[0]] * 100 + else: + single_blocks = parse_range_string(single_blocks) + + weights_expanded = [0.0] * 100 + for b, w in zip(single_blocks, single_weights): + weights_expanded[b] = w + single_weights = weights_expanded + + + + if end_step == -1: + end_step = MAX_STEPS + + if guide is not None: + raw_x = guide.get('state_info', {}).get('raw_x', None) + if raw_x is not None: + guide = {'samples': guide['state_info']['raw_x'].clone()} + else: + guide = {'samples': guide['samples'].clone()} + + if weight_scheduler == "constant": # and weights == None: + weights = initialize_or_scale(None, weight, end_step).to(default_dtype) + prepend = torch.zeros(start_step).to(weights) + weights = torch.cat([prepend, weights]) + weights = F.pad(weights, (0, MAX_STEPS), value=0.0) + + guides = copy.deepcopy(guides) if guides is not None else {} + + guides['weight_adain'] = weight + guides['weights_adain'] = weights + + guides['blocks_adain_mmdit'] = { + "double_weights": double_weights, + "single_weights": single_weights, + "double_blocks" : double_blocks, + "single_blocks" : single_blocks, + } + + guides['guide_adain'] = guide + guides['mask_adain'] = mask + + guides['weight_scheduler_adain'] = weight_scheduler + guides['start_step_adain'] = start_step + guides['end_step_adain'] = end_step + + return (guides, ) + + + + + +class ClownGuide_AttnInj_MMDiT_Beta: + @classmethod + def INPUT_TYPES(cls): + return {"required": + { + "weight": ("FLOAT", {"default": 1.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Set the strength of the guide by multiplying all other weights by this value."}), + "weight_scheduler": (["constant"] + get_res4lyf_scheduler_list(), {"default": "constant"},), + "double_blocks" : ("STRING", {"default": "0,1,3", "multiline": True}), + "double_weights" : ("STRING", {"default": "1.0", "multiline": True}), + "single_blocks" : ("STRING", {"default": "20", "multiline": True}), + "single_weights" : ("STRING", {"default": "0.5", "multiline": True}), + + "img_q": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Set relative injection strength."}), + "img_k": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Set relative injection strength."}), + "img_v": ("FLOAT", {"default": 1.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Set relative injection strength."}), + + "txt_q": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Set relative injection strength."}), + "txt_k": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Set relative injection strength."}), + "txt_v": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Set relative injection strength."}), + + "img_q_norm": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Set relative injection strength."}), + "img_k_norm": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Set relative injection strength."}), + "img_v_norm": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Set relative injection strength."}), + + "txt_q_norm": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Set relative injection strength."}), + "txt_k_norm": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Set relative injection strength."}), + "txt_v_norm": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Set relative injection strength."}), + + "start_step": ("INT", {"default": 0, "min": 0, "max": 10000}), + "end_step": ("INT", {"default": 15, "min": -1, "max": 10000}), + "invert_mask": ("BOOLEAN", {"default": False}), + }, + "optional": + { + "guide": ("LATENT", ), + "mask": ("MASK", ), + "weights": ("SIGMAS", ), + "guides": ("GUIDES", ), + } + } + + RETURN_TYPES = ("GUIDES",) + RETURN_NAMES = ("guides",) + FUNCTION = "main" + CATEGORY = "RES4LYF/sampler_extensions" + + def main(self, + weight = 1.0, + weight_scheduler = "constant", + double_weights = "0.1", + single_weights = "0.0", + double_blocks = "all", + single_blocks = "all", + + img_q = 0.0, + img_k = 0.0, + img_v = 0.0, + + txt_q = 0.0, + txt_k = 0.0, + txt_v = 0.0, + + img_q_norm = 0.0, + img_k_norm = 0.0, + img_v_norm = 0.0, + + txt_q_norm = 0.0, + txt_k_norm = 0.0, + txt_v_norm = 0.0, + + start_step = 0, + end_step = 15, + invert_mask = False, + + guide = None, + mask = None, + weights = None, + guides = None, + ): + + default_dtype = torch.float64 + + mask = 1-mask if mask is not None else None + + double_weights = parse_range_string(double_weights) + single_weights = parse_range_string(single_weights) + + if len(double_weights) == 0: + double_weights.append(0.0) + if len(single_weights) == 0: + single_weights.append(0.0) + + if len(double_weights) == 1: + double_weights = double_weights * 100 + if len(single_weights) == 1: + single_weights = single_weights * 100 + + if type(double_weights[0]) == int: + double_weights = [float(val) for val in double_weights] + if type(single_weights[0]) == int: + single_weights = [float(val) for val in single_weights] + + if double_blocks == "all": + double_blocks = [val for val in range(100)] + if len(double_weights) == 1: + double_weights = [double_weights[0]] * 100 + else: + double_blocks = parse_range_string(double_blocks) + + weights_expanded = [0.0] * 100 + for b, w in zip(double_blocks, double_weights): + weights_expanded[b] = w + double_weights = weights_expanded + + + if single_blocks == "all": + single_blocks = [val for val in range(100)] + if len(single_weights) == 1: + single_weights = [single_weights[0]] * 100 + else: + single_blocks = parse_range_string(single_blocks) + + weights_expanded = [0.0] * 100 + for b, w in zip(single_blocks, single_weights): + weights_expanded[b] = w + single_weights = weights_expanded + + + + if end_step == -1: + end_step = MAX_STEPS + + if guide is not None: + raw_x = guide.get('state_info', {}).get('raw_x', None) + if raw_x is not None: + guide = {'samples': guide['state_info']['raw_x'].clone()} + else: + guide = {'samples': guide['samples'].clone()} + + if weight_scheduler == "constant": # and weights == None: + weights = initialize_or_scale(None, weight, end_step).to(default_dtype) + prepend = torch.zeros(start_step).to(weights) + weights = torch.cat([prepend, weights]) + weights = F.pad(weights, (0, MAX_STEPS), value=0.0) + + guides = copy.deepcopy(guides) if guides is not None else {} + + guides['weight_attninj'] = weight + guides['weights_attninj'] = weights + + guides['blocks_attninj_mmdit'] = { + "double_weights": double_weights, + "single_weights": single_weights, + "double_blocks" : double_blocks, + "single_blocks" : single_blocks, + } + + guides['blocks_attninj_qkv'] = { + "img_q": img_q, + "img_k": img_k, + "img_v": img_v, + "txt_q": txt_q, + "txt_k": txt_k, + "txt_v": txt_v, + + "img_q_norm": img_q_norm, + "img_k_norm": img_k_norm, + "img_v_norm": img_v_norm, + "txt_q_norm": txt_q_norm, + "txt_k_norm": txt_k_norm, + "txt_v_norm": txt_v_norm, + } + + guides['guide_attninj'] = guide + guides['mask_attninj'] = mask + + guides['weight_scheduler_attninj'] = weight_scheduler + guides['start_step_attninj'] = start_step + guides['end_step_attninj'] = end_step + + return (guides, ) + + + + + + +class ClownGuide_StyleNorm_Advanced_HiDream: + + @classmethod + def INPUT_TYPES(cls): + return {"required": + { + "weight": ("FLOAT", {"default": 1.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Set the strength of the guide by multiplying all other weights by this value."}), + "weight_scheduler": (["constant"] + get_res4lyf_scheduler_list(), {"default": "constant"},), + + "double_blocks" : ("STRING", {"default": "all", "multiline": True}), + "double_weights" : ("STRING", {"default": "1.0", "multiline": True}), + "single_blocks" : ("STRING", {"default": "all", "multiline": True}), + "single_weights" : ("STRING", {"default": "1.0", "multiline": True}), + + "mode": (["scattersort", "AdaIN"], {"default": "scattersort"},), + "noise_mode": (["direct", "update", "smart", "recon", "bonanza"], {"default": "smart"},), + + #"shared_experts": ("BOOLEAN", {"default": False}), + + "ff_1" : ("BOOLEAN", {"default": False}), + "ff_1_silu" : ("BOOLEAN", {"default": False}), + "ff_3" : ("BOOLEAN", {"default": False}), + "ff_13" : ("BOOLEAN", {"default": False}), + "ff_2" : ("BOOLEAN", {"default": False}), + + "moe_gate" : ("BOOLEAN", {"default": False}), + "topk_weight" : ("BOOLEAN", {"default": False}), + "moe_ff_1" : ("BOOLEAN", {"default": False}), + "moe_ff_1_silu" : ("BOOLEAN", {"default": False}), + "moe_ff_3" : ("BOOLEAN", {"default": False}), + "moe_ff_13" : ("BOOLEAN", {"default": False}), + "moe_ff_2" : ("BOOLEAN", {"default": False}), + "moe_sum" : ("BOOLEAN", {"default": False}), + "moe_out" : ("BOOLEAN", {"default": False}), + + "double_img_io": ("BOOLEAN", {"default": False}), + "double_img_norm0": ("BOOLEAN", {"default": False}), + "double_img_attn": ("BOOLEAN", {"default": False}), + "double_img_attn_gated": ("BOOLEAN", {"default": False}), + "double_img": ("BOOLEAN", {"default": False}), + "double_img_norm1": ("BOOLEAN", {"default": False}), + "double_img_ff_i": ("BOOLEAN", {"default": False}), + + "double_txt_io": ("BOOLEAN", {"default": False}), + "double_txt_norm0": ("BOOLEAN", {"default": False}), + "double_txt_attn": ("BOOLEAN", {"default": False}), + "double_txt_attn_gated": ("BOOLEAN", {"default": False}), + "double_txt": ("BOOLEAN", {"default": False}), + "double_txt_norm1": ("BOOLEAN", {"default": False}), + "double_txt_ff_t": ("BOOLEAN", {"default": False}), + + "single_img_io": ("BOOLEAN", {"default": False}), + "single_img_norm0": ("BOOLEAN", {"default": False}), + "single_img_attn": ("BOOLEAN", {"default": False}), + "single_img_attn_gated": ("BOOLEAN", {"default": False}), + "single_img": ("BOOLEAN", {"default": False}), + "single_img_norm1": ("BOOLEAN", {"default": False}), + "single_img_ff_i": ("BOOLEAN", {"default": False}), + + "attn_img_q_norm" : ("BOOLEAN", {"default": False}), + "attn_img_k_norm" : ("BOOLEAN", {"default": False}), + "attn_img_v_norm" : ("BOOLEAN", {"default": False}), + "attn_txt_q_norm" : ("BOOLEAN", {"default": False}), + "attn_txt_k_norm" : ("BOOLEAN", {"default": False}), + "attn_txt_v_norm" : ("BOOLEAN", {"default": False}), + "attn_img_double" : ("BOOLEAN", {"default": False}), + "attn_txt_double" : ("BOOLEAN", {"default": False}), + "attn_img_single" : ("BOOLEAN", {"default": False}), + + "proj_out" : ("BOOLEAN", {"default": False}), + + "start_step": ("INT", {"default": 0, "min": 0, "max": 10000}), + "end_step": ("INT", {"default": 15, "min": -1, "max": 10000}), + "invert_mask": ("BOOLEAN", {"default": False}), + }, + "optional": + { + "guide": ("LATENT", ), + "mask": ("MASK", ), + "weights": ("SIGMAS", ), + "guides": ("GUIDES", ), + } + } + + RETURN_TYPES = ("GUIDES",) + RETURN_NAMES = ("guides",) + FUNCTION = "main" + CATEGORY = "RES4LYF/sampler_extensions" + EXPERIMENTAL = True + + def main(self, + weight = 1.0, + weight_scheduler = "constant", + mode = "scattersort", + noise_mode = "smart", + double_weights = "0.1", + single_weights = "0.0", + double_blocks = "all", + single_blocks = "all", + start_step = 0, + end_step = 15, + invert_mask = False, + + moe_gate = False, + topk_weight = False, + moe_out = False, + moe_sum = False, + ff_1 = False, + ff_1_silu = False, + ff_3 = False, + ff_13 = False, + ff_2 = False, + shared_experts = False, + + moe_ff_1 = False, + moe_ff_1_silu = False, + moe_ff_3 = False, + moe_ff_13 = False, + moe_ff_2 = False, + + double_img_io = False, + double_img_norm0 = False, + double_img_attn = False, + double_img_norm1 = False, + double_img_attn_gated = False, + double_img = False, + double_img_ff_i = False, + + double_txt_io = False, + double_txt_norm0 = False, + double_txt_attn = False, + double_txt_attn_gated = False, + double_txt = False, + double_txt_norm1 = False, + double_txt_ff_t = False, + + single_img_io = False, + single_img_norm0 = False, + single_img_attn = False, + single_img_attn_gated = False, + single_img = False, + single_img_norm1 = False, + single_img_ff_i = False, + + attn_img_q_norm = False, + attn_img_k_norm = False, + attn_img_v_norm = False, + attn_txt_q_norm = False, + attn_txt_k_norm = False, + attn_txt_v_norm = False, + attn_img_single = False, + attn_img_double = False, + attn_txt_double = False, + + proj_out = False, + + guide = None, + mask = None, + weights = None, + guides = None, + ): + + default_dtype = torch.float64 + + mask = 1-mask if mask is not None else None + + double_weights = parse_range_string(double_weights) + single_weights = parse_range_string(single_weights) + + if len(double_weights) == 0: + double_weights.append(0.0) + if len(single_weights) == 0: + single_weights.append(0.0) + + if len(double_weights) == 1: + double_weights = double_weights * 100 + if len(single_weights) == 1: + single_weights = single_weights * 100 + + if type(double_weights[0]) == int: + double_weights = [float(val) for val in double_weights] + if type(single_weights[0]) == int: + single_weights = [float(val) for val in single_weights] + + if double_blocks == "all": + double_blocks = [val for val in range(100)] + if len(double_weights) == 1: + double_weights = [double_weights[0]] * 100 + else: + double_blocks = parse_range_string(double_blocks) + + weights_expanded = [0.0] * 100 + for b, w in zip(double_blocks, double_weights): + weights_expanded[b] = w + double_weights = weights_expanded + + + if single_blocks == "all": + single_blocks = [val for val in range(100)] + if len(single_weights) == 1: + single_weights = [single_weights[0]] * 100 + else: + single_blocks = parse_range_string(single_blocks) + + weights_expanded = [0.0] * 100 + for b, w in zip(single_blocks, single_weights): + weights_expanded[b] = w + single_weights = weights_expanded + + + + if end_step == -1: + end_step = MAX_STEPS + + if guide is not None: + raw_x = guide.get('state_info', {}).get('raw_x', None) + if raw_x is not None: + guide = {'samples': guide['state_info']['raw_x'].clone()} + else: + guide = {'samples': guide['samples'].clone()} + + if weight_scheduler == "constant": # and weights == None: + weights = initialize_or_scale(None, weight, end_step).to(default_dtype) + prepend = torch.zeros(start_step).to(weights) + weights = torch.cat([prepend, weights]) + weights = F.pad(weights, (0, MAX_STEPS), value=0.0) + + guides = copy.deepcopy(guides) if guides is not None else {} + + guides['weight_adain'] = weight + guides['weights_adain'] = weights + + guides['blocks_adain_mmdit'] = { + "double_weights": double_weights, + "single_weights": single_weights, + "double_blocks" : double_blocks, + "single_blocks" : single_blocks, + } + guides['sort_and_scatter'] = { + "mode" : mode, + "noise_mode" : noise_mode, + + "moe_gate" : moe_gate, + "topk_weight" : topk_weight, + "moe_sum" : moe_sum, + "moe_out" : moe_out, + + "ff_1" : ff_1, + "ff_1_silu" : ff_1_silu, + "ff_3" : ff_3, + "ff_13" : ff_13, + "ff_2" : ff_2, + + "moe_ff_1" : moe_ff_1, + "moe_ff_1_silu" : moe_ff_1_silu, + "moe_ff_3" : moe_ff_3, + "moe_ff_13" : moe_ff_13, + "moe_ff_2" : moe_ff_2, + + "shared_experts" : shared_experts, + + "double_img_io" : double_img_io, + "double_img_norm0" : double_img_norm0, + "double_img_attn" : double_img_attn, + "double_img_norm1" : double_img_norm1, + "double_img_attn_gated" : double_img_attn_gated, + "double_img" : double_img, + "double_img_ff_i" : double_img_ff_i, + + "double_txt_io" : double_txt_io, + "double_txt_norm0" : double_txt_norm0, + "double_txt_attn" : double_txt_attn, + "double_txt_attn_gated" : double_txt_attn_gated, + "double_txt" : double_txt, + "double_txt_norm1" : double_txt_norm1, + "double_txt_ff_t" : double_txt_ff_t, + + "single_img_io" : single_img_io, + "single_img_norm0" : single_img_norm0, + "single_img_attn" : single_img_attn, + "single_img_attn_gated" : single_img_attn_gated, + "single_img" : single_img, + "single_img_norm1" : single_img_norm1, + "single_img_ff_i" : single_img_ff_i, + + "attn_img_q_norm" : attn_img_q_norm, + "attn_img_k_norm" : attn_img_k_norm, + "attn_img_v_norm" : attn_img_v_norm, + "attn_txt_q_norm" : attn_txt_q_norm, + "attn_txt_k_norm" : attn_txt_k_norm, + "attn_txt_v_norm" : attn_txt_v_norm, + "attn_img_single" : attn_img_single, + "attn_img_double" : attn_img_double, + + "proj_out" : proj_out, + } + + guides['guide_adain'] = guide + guides['mask_adain'] = mask + + guides['weight_scheduler_adain'] = weight_scheduler + guides['start_step_adain'] = start_step + guides['end_step_adain'] = end_step + + return (guides, ) + + + + +from ..style_transfer import StyleMMDiT_Model, StyleUNet_Model, DEFAULT_BLOCK_WEIGHTS_MMDIT, DEFAULT_ATTN_WEIGHTS_MMDIT, DEFAULT_BASE_WEIGHTS_MMDIT + +STYLE_MODES = [ + "none", + #"sinkhornsort", + "scattersort_dir", + "scattersort_dir2", + "scattersort", + "tiled_scattersort", + "AdaIN", + "tiled_AdaIN", + "WCT", + "WCT2", + "injection", +] + +class ClownStyle_Boost: + + @classmethod + def INPUT_TYPES(cls): + return {"required": + { + "noise_mode": (["direct", "update", "smart", "recon", "bonanza"], {"default": "update"},), + "recon_lure": (STYLE_MODES, {"default": "WCT", "tooltip": "Only used if noise_mode = recon. Can increase the strength of the style."},), + "datashock": (STYLE_MODES, {"default": "scattersort", "tooltip": "Will drastically increase the strength at low denoise levels. Use with img2img workflows."},), + "datashock_weight": ("FLOAT", {"default": 1.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Set the strength of the guide by multiplying all other weights by this value."}), + "datashock_start_step": ("INT", {"default": 0, "min": 0, "max": 10000, "step": 1, "tooltip": "Start step for data shock."}), + "datashock_end_step" : ("INT", {"default": 1, "min": 1, "max": 10000, "step": 1, "tooltip": "End step for data shock."}), + + "tile_h" : ("INT", {"default": 128, "min": 16, "max": 10000, "step": 16, "tooltip": "Tile size for tiled modes. Lower values will transfer composition more effectively. Dimensions of image must be divisible by this value."}), + "tile_w" : ("INT", {"default": 128, "min": 16, "max": 10000, "step": 16, "tooltip": "Tile size for tiled modes. Lower values will transfer composition more effectively. Dimensions of image must be divisible by this value."}), + }, + "optional": + { + "guides": ("GUIDES", ), + #"datashock_weights": ("SIGMAS",), + } + } + + RETURN_TYPES = ("GUIDES",) + RETURN_NAMES = ("guides",) + FUNCTION = "main" + CATEGORY = "RES4LYF/sampler_extensions" + + def main(self, + noise_mode = "update", + recon_lure = "default", + datashock = None, + datashock_weight = 1.0, + datashock_start_step = None, + datashock_end_step = None, + tile_h = 0, + tile_w = 0, + guides = None, + ): + guides = copy.deepcopy(guides) if guides is not None else {} + + StyleMMDiT = guides.get('StyleMMDiT') + + if StyleMMDiT is None: + StyleMMDiT = StyleMMDiT_Model() + + weights = { + "h_tile" : tile_h // 16, + "w_tile" : tile_w // 16, + } + StyleMMDiT.set_weights(**weights) + + StyleMMDiT.noise_mode = noise_mode + StyleMMDiT.recon_lure = recon_lure + StyleMMDiT.data_shock = datashock + StyleMMDiT.data_shock_weight = datashock_weight + StyleMMDiT.data_shock_start_step = datashock_start_step + StyleMMDiT.data_shock_end_step = datashock_end_step + + guides['StyleMMDiT'] = StyleMMDiT + return (guides,) + + #guides['StyleMMDiT'].noise_mode = noise_mode + #guides['StyleMMDiT'].recon_lure = recon_lure + #guides['StyleMMDiT'].data_shock = datashock + #guides['StyleMMDiT'].data_shock_start_step = datashock_start_step + #guides['StyleMMDiT'].data_shock_end_step = datashock_end_step + #return (guides, ) + + + + + + +class ClownStyle_MMDiT: + + @classmethod + def INPUT_TYPES(cls): + return {"required": + { + "mode": (STYLE_MODES, {"default": "scattersort"},), + + "proj_in": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Strength of effect on layer; skips extra calculation if set to 0.0. Skips interpolation if set to 1.0."}), + "proj_out": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Strength of effect on layer; skips extra calculation if set to 0.0. Skips interpolation if set to 1.0."}), + + "tile_h" : ("INT", {"default": 128, "min": 16, "max": 10000, "step": 16, "tooltip": "Tile size for tiled modes. Lower values will transfer composition more effectively. Dimensions of image must be divisible by this value."}), + "tile_w" : ("INT", {"default": 128, "min": 16, "max": 10000, "step": 16, "tooltip": "Tile size for tiled modes. Lower values will transfer composition more effectively. Dimensions of image must be divisible by this value."}), + + #"start_step": ("INT", {"default": 0, "min": 16, "max": 10000, "step": 1, "tooltip": "Start step for data shock."}), + #"end_step" : ("INT", {"default": 1, "min": 16, "max": 10000, "step": 1, "tooltip": "End step for data shock."}), + + "invert_mask": ("BOOLEAN", {"default": False}), + }, + "optional": + { + "positive" : ("CONDITIONING", ), + "negative" : ("CONDITIONING", ), + "guide": ("LATENT", ), + "mask": ("MASK", ), + "blocks": ("BLOCKS", ), + "guides": ("GUIDES", ), + } + } + + RETURN_TYPES = ("GUIDES",) + RETURN_NAMES = ("guides",) + FUNCTION = "main" + CATEGORY = "RES4LYF/sampler_extensions" + + def main(self, + mode = "scattersort", + + proj_in = 0.0, + proj_out = 0.0, + tile_h = 128, + tile_w = 128, + invert_mask = False, + positive = None, + negative = None, + guide = None, + mask = None, + blocks = None, + guides = None, + ): + + #mask = 1-mask if mask is not None else None + + if guide is not None: + raw_x = guide.get('state_info', {}).get('raw_x', None) + if raw_x is not None: + guide = {'samples': guide['state_info']['raw_x'].clone()} + else: + guide = {'samples': guide['samples'].clone()} + + guides = copy.deepcopy(guides) if guides is not None else {} + blocks = copy.deepcopy(blocks) if blocks is not None else {} + + StyleMMDiT = blocks.get('StyleMMDiT') + + if StyleMMDiT is None: + StyleMMDiT = StyleMMDiT_Model() + + weights = { + "proj_in" : proj_in, + "proj_out": proj_out, + + "h_tile" : tile_h // 16, + "w_tile" : tile_w // 16, + } + + StyleMMDiT.set_mode(mode) + StyleMMDiT.set_weights(**weights) + StyleMMDiT.set_conditioning(positive, negative) + StyleMMDiT.mask = [mask] + StyleMMDiT.guides = [guide] + + StyleMMDiT_ = guides.get('StyleMMDiT') + if StyleMMDiT_ is not None: + StyleMMDiT_.merge_weights(StyleMMDiT) + else: + StyleMMDiT_ = StyleMMDiT + + guides['StyleMMDiT'] = StyleMMDiT_ + + return (guides, ) + + + +class ClownStyle_Block_MMDiT: + + @classmethod + def INPUT_TYPES(cls): + return {"required": + { + "mode": (STYLE_MODES, {"default": "scattersort"},), + "apply_to": (["img", "img+txt","img,txt", "txt",], {"default": "img+txt"},), + "block_type": (["double", "double,single", "single"], {"default": "single"},), + "block_list": ("STRING", {"default": "all", "multiline": True}), + "block_weights": ("STRING", {"default": "1.0", "multiline": True}), + + "attn_norm": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Strength of effect on layer; skips extra calculation if set to 0.0. Skips interpolation if set to 1.0."}), + "attn_norm_mod": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Strength of effect on layer; skips extra calculation if set to 0.0. Skips interpolation if set to 1.0."}), + "attn": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Strength of effect on layer; skips extra calculation if set to 0.0. Skips interpolation if set to 1.0."}), + "attn_gated": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Strength of effect on layer; skips extra calculation if set to 0.0. Skips interpolation if set to 1.0."}), + "attn_res": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Strength of effect on layer; skips extra calculation if set to 0.0. Skips interpolation if set to 1.0."}), + + "ff_norm": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Strength of effect on layer; skips extra calculation if set to 0.0. Skips interpolation if set to 1.0."}), + "ff_norm_mod": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Strength of effect on layer; skips extra calculation if set to 0.0. Skips interpolation if set to 1.0."}), + "ff": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Strength of effect on layer; skips extra calculation if set to 0.0. Skips interpolation if set to 1.0."}), + "ff_gated": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Strength of effect on layer; skips extra calculation if set to 0.0. Skips interpolation if set to 1.0."}), + "ff_res": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Strength of effect on layer; skips extra calculation if set to 0.0. Skips interpolation if set to 1.0."}), + + "tile_h": ("INT", {"default": 128, "min": 16, "max": 10000, "step": 16, "tooltip": "Tile size for tiled modes. Lower values will transfer composition more effectively. Dimensions of image must be divisible by this value."}), + "tile_w": ("INT", {"default": 128, "min": 16, "max": 10000, "step": 16, "tooltip": "Tile size for tiled modes. Lower values will transfer composition more effectively. Dimensions of image must be divisible by this value."}), + + "invert_mask": ("BOOLEAN",{"default": False}), + }, + "optional": + { + "mask": ("MASK", ), + "blocks": ("BLOCKS", ), + } + } + + RETURN_TYPES = ("BLOCKS",) + RETURN_NAMES = ("blocks",) + FUNCTION = "main" + CATEGORY = "RES4LYF/sampler_extensions" + + def main(self, + mode = "scattersort", + noise_mode = "update", + apply_to = "joint", + block_type = "double", + block_list = "all", + block_weights = "1.0", + + attn_norm = 0.0, + attn_norm_mod = 0.0, + attn = 0.0, + attn_gated = 0.0, + attn_res = 0.0, + ff_norm = 0.0, + ff_norm_mod = 0.0, + ff = 0.0, + ff_gated = 0.0, + ff_res = 0.0, + + tile_h = 128, + tile_w = 128, + + invert_mask = False, + + Attn = None, + MoE = None, + FF = None, + + mask = None, + blocks = None, + ): + + #mask = 1-mask if mask is not None else None + + blocks = copy.deepcopy(blocks) if blocks is not None else {} + + block_weights = parse_range_string(block_weights) + + if len(block_weights) == 0: + block_weights.append(0.0) + + if len(block_weights) == 1: + block_weights = block_weights * 100 + + if type(block_weights[0]) == int: + block_weights = [float(val) for val in block_weights] + + if "all" in block_list: + block_list = [val for val in range(100)] + if len(block_weights) == 1: + block_weights = [block_weights[0]] * 100 + elif "even" in block_list: + block_list = [val for val in range(0, 100, 2)] + if len(block_weights) == 1: + block_weights = [block_weights[0]] * 100 + elif "odd" in block_list: + block_list = [val for val in range(1, 100, 2)] + if len(block_weights) == 1: + block_weights = [block_weights[0]] * 100 + else: + block_list = parse_range_string_int(block_list) + + weights_expanded = [0.0] * 100 + for b, w in zip(block_list, block_weights): + weights_expanded[b] = w + block_weights = weights_expanded + + StyleMMDiT = blocks.get('StyleMMDiT') + if StyleMMDiT is None: + StyleMMDiT = StyleMMDiT_Model() + + weights = { + "attn_norm" : attn_norm, + "attn_norm_mod": attn_norm_mod, + "attn" : attn, + "attn_gated" : attn_gated, + "attn_res" : attn_res, + "ff_norm" : ff_norm, + "ff_norm_mod" : ff_norm_mod, + "ff" : ff, + "ff_gated" : ff_gated, + "ff_res" : ff_res, + + "h_tile" : tile_h // 16, + "w_tile" : tile_w // 16, + } + + block_types = block_type.split(",") + + for block_type in block_types: + + if block_type == "double": + style_blocks = StyleMMDiT.double_blocks + elif block_type == "single": + style_blocks = StyleMMDiT.single_blocks + + for bid in block_list: + block = style_blocks[bid] + scaled_weights = { + k: (v * block_weights[bid]) if isinstance(v, float) else v + for k, v in weights.items() + } + + if "img" in apply_to or block_type == "single": + block.img.set_mode(mode) + block.img.set_weights(**scaled_weights) + block.img.apply_to = [apply_to] + + if "txt" in apply_to and block_type == "double": + mode = "scattersort" if mode == "tiled_scattersort" else mode + mode = "AdaIN" if mode == "tiled_AdaIN" else mode + block.txt.set_mode(mode) + block.txt.set_weights(**scaled_weights) + block.txt.apply_to = [apply_to] + + block.img.apply_to = [apply_to] + if hasattr(block, "txt"): + block.txt.apply_to = [apply_to] + + block.mask = [mask] + + blocks['StyleMMDiT'] = StyleMMDiT + + return (blocks, ) + + + +class ClownStyle_Attn_MMDiT: + + @classmethod + def INPUT_TYPES(cls): + return {"required": + { + "mode": (STYLE_MODES, {"default": "scattersort"},), + "apply_to": (["img","img+txt","img,txt","txt"], {"default": "img+txt"},), + "block_type": (["double", "double,single", "single"], {"default": "single"},), + "block_list": ("STRING", {"default": "all", "multiline": True}), + "block_weights": ("STRING", {"default": "1.0", "multiline": True}), + + "q_proj": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Strength of effect on layer; skips extra calculation if set to 0.0. Skips interpolation if set to 1.0."}), + "k_proj": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Strength of effect on layer; skips extra calculation if set to 0.0. Skips interpolation if set to 1.0."}), + "v_proj": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Strength of effect on layer; skips extra calculation if set to 0.0. Skips interpolation if set to 1.0."}), + "q_norm": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Strength of effect on layer; skips extra calculation if set to 0.0. Skips interpolation if set to 1.0."}), + "k_norm": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Strength of effect on layer; skips extra calculation if set to 0.0. Skips interpolation if set to 1.0."}), + "out": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Strength of effect on layer; skips extra calculation if set to 0.0. Skips interpolation if set to 1.0."}), + + "tile_h": ("INT", {"default": 128, "min": 16, "max": 10000, "step": 16, "tooltip": "Tile size for tiled modes. Lower values will transfer composition more effectively. Dimensions of image must be divisible by this value."}), + "tile_w": ("INT", {"default": 128, "min": 16, "max": 10000, "step": 16, "tooltip": "Tile size for tiled modes. Lower values will transfer composition more effectively. Dimensions of image must be divisible by this value."}), + + "invert_mask": ("BOOLEAN", {"default": False}), + }, + "optional": + { + "mask": ("MASK", ), + "blocks": ("BLOCKS", ), + } + } + + RETURN_TYPES = ("BLOCKS",) + RETURN_NAMES = ("blocks",) + FUNCTION = "main" + CATEGORY = "RES4LYF/sampler_extensions" + + def main(self, + mode = "scattersort", + noise_mode = "update", + apply_to = "joint", + block_type = "double", + block_list = "all", + block_weights = "1.0", + + q_proj = 0.0, + k_proj = 0.0, + v_proj = 0.0, + q_norm = 0.0, + k_norm = 0.0, + out = 0.0, + + tile_h = 128, + tile_w = 128, + + invert_mask = False, + + mask = None, + blocks = None, + ): + + #mask = 1-mask if mask is not None else None + + blocks = copy.deepcopy(blocks) if blocks is not None else {} + + block_weights = parse_range_string(block_weights) + + if len(block_weights) == 0: + block_weights.append(0.0) + + if len(block_weights) == 1: + block_weights = block_weights * 100 + + if type(block_weights[0]) == int: + block_weights = [float(val) for val in block_weights] + + if "all" in block_list: + block_list = [val for val in range(100)] + if len(block_weights) == 1: + block_weights = [block_weights[0]] * 100 + elif "even" in block_list: + block_list = [val for val in range(0, 100, 2)] + if len(block_weights) == 1: + block_weights = [block_weights[0]] * 100 + elif "odd" in block_list: + block_list = [val for val in range(1, 100, 2)] + if len(block_weights) == 1: + block_weights = [block_weights[0]] * 100 + else: + block_list = parse_range_string_int(block_list) + + weights_expanded = [0.0] * 100 + for b, w in zip(block_list, block_weights): + weights_expanded[b] = w + block_weights = weights_expanded + + StyleMMDiT = blocks.get('StyleMMDiT') + if StyleMMDiT is None: + StyleMMDiT = StyleMMDiT_Model() + + weights = { + "q_proj": q_proj, + "k_proj": k_proj, + "v_proj": v_proj, + "q_norm": q_norm, + "k_norm": k_norm, + "out" : out, + + "h_tile": tile_h // 16, + "w_tile": tile_w // 16, + } + + block_types = block_type.split(",") + + for block_type in block_types: + + if block_type == "double": + style_blocks = StyleMMDiT.double_blocks + elif block_type == "single": + style_blocks = StyleMMDiT.single_blocks + + for bid in block_list: + block = style_blocks[bid] + scaled_weights = { + k: (v * block_weights[bid]) if isinstance(v, float) else v + for k, v in weights.items() + } + + if "img" in apply_to or block_type == "single": + block.img.ATTN.set_mode(mode) + block.img.ATTN.set_weights(**scaled_weights) + block.img.ATTN.apply_to = [apply_to] + + if "txt" in apply_to and block_type == "double": + mode = "scattersort" if mode == "tiled_scattersort" else mode + mode = "AdaIN" if mode == "tiled_AdaIN" else mode + block.txt.ATTN.set_mode(mode) + block.txt.ATTN.set_weights(**scaled_weights) + block.txt.ATTN.apply_to = [apply_to] + + block.img.ATTN.apply_to = [apply_to] + if hasattr(block, "txt"): + block.txt.ATTN.apply_to = [apply_to] + + block.attn_mask = [mask] + + blocks['StyleMMDiT'] = StyleMMDiT + + return (blocks, ) + + + + + + + + + + + + + + + +class ClownStyle_UNet: + + @classmethod + def INPUT_TYPES(cls): + return {"required": + { + "mode": (STYLE_MODES, {"default": "scattersort"},), + + "proj_in": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Strength of effect on layer; skips extra calculation if set to 0.0. Skips interpolation if set to 1.0."}), + "proj_out": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Strength of effect on layer; skips extra calculation if set to 0.0. Skips interpolation if set to 1.0."}), + + "tile_h" : ("INT", {"default": 128, "min": 16, "max": 10000, "step": 16, "tooltip": "Tile size for tiled modes. Lower values will transfer composition more effectively. Dimensions of image must be divisible by this value."}), + "tile_w" : ("INT", {"default": 128, "min": 16, "max": 10000, "step": 16, "tooltip": "Tile size for tiled modes. Lower values will transfer composition more effectively. Dimensions of image must be divisible by this value."}), + + #"start_step": ("INT", {"default": 0, "min": 16, "max": 10000, "step": 1, "tooltip": "Start step for data shock."}), + #"end_step" : ("INT", {"default": 1, "min": 16, "max": 10000, "step": 1, "tooltip": "End step for data shock."}), + + "invert_mask": ("BOOLEAN", {"default": False}), + }, + "optional": + { + "positive" : ("CONDITIONING", ), + "negative" : ("CONDITIONING", ), + "guide": ("LATENT", ), + "mask": ("MASK", ), + "blocks": ("BLOCKS", ), + "guides": ("GUIDES", ), + } + } + + RETURN_TYPES = ("GUIDES",) + RETURN_NAMES = ("guides",) + FUNCTION = "main" + CATEGORY = "RES4LYF/sampler_extensions" + + def main(self, + mode = "scattersort", + + proj_in = 0.0, + proj_out = 0.0, + tile_h = 128, + tile_w = 128, + invert_mask = False, + positive = None, + negative = None, + guide = None, + mask = None, + blocks = None, + guides = None, + ): + + #mask = 1-mask if mask is not None else None + + if guide is not None: + raw_x = guide.get('state_info', {}).get('raw_x', None) + if raw_x is not None: + guide = {'samples': guide['state_info']['raw_x'].clone()} + else: + guide = {'samples': guide['samples'].clone()} + + guides = copy.deepcopy(guides) if guides is not None else {} + blocks = copy.deepcopy(blocks) if blocks is not None else {} + + StyleMMDiT = blocks.get('StyleMMDiT') + + if StyleMMDiT is None: + StyleMMDiT = StyleUNet_Model() + + weights = { + "proj_in" : proj_in, + "proj_out": proj_out, + + "h_tile" : tile_h // 8, + "w_tile" : tile_w // 8, + } + + StyleMMDiT.set_mode(mode) + StyleMMDiT.set_weights(**weights) + StyleMMDiT.set_conditioning(positive, negative) + StyleMMDiT.mask = [mask] + StyleMMDiT.guides = [guide] + + StyleMMDiT_ = guides.get('StyleMMDiT') + if StyleMMDiT_ is not None: + StyleMMDiT_.merge_weights(StyleMMDiT) + else: + StyleMMDiT_ = StyleMMDiT + + guides['StyleMMDiT'] = StyleMMDiT_ + + return (guides, ) + + + + +UNET_BLOCK_TYPES = [ + "input", + "middle", + "output", + "input,middle", + "input,output", + "middle,output", + "input,middle,output", +] + +class ClownStyle_Block_UNet: + + @classmethod + def INPUT_TYPES(cls): + return {"required": + { + "mode": (STYLE_MODES, {"default": "scattersort"},), + #"apply_to": (["img", "img+txt","img,txt", "txt",], {"default": "img+txt"},), + "block_type": (UNET_BLOCK_TYPES, {"default": "input"},), + "block_list": ("STRING", {"default": "all", "multiline": True}), + "block_weights": ("STRING", {"default": "1.0", "multiline": True}), + + "resample": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Strength of effect on layer; skips extra calculation if set to 0.0. Skips interpolation if set to 1.0."}), + "res": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Strength of effect on layer; skips extra calculation if set to 0.0. Skips interpolation if set to 1.0."}), + "spatial": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Strength of effect on layer; skips extra calculation if set to 0.0. Skips interpolation if set to 1.0."}), + + "tile_h": ("INT", {"default": 128, "min": 16, "max": 10000, "step": 16, "tooltip": "Tile size for tiled modes. Lower values will transfer composition more effectively. Dimensions of image must be divisible by this value."}), + "tile_w": ("INT", {"default": 128, "min": 16, "max": 10000, "step": 16, "tooltip": "Tile size for tiled modes. Lower values will transfer composition more effectively. Dimensions of image must be divisible by this value."}), + + "invert_mask": ("BOOLEAN",{"default": False}), + }, + "optional": + { + "mask": ("MASK", ), + "blocks": ("BLOCKS", ), + } + } + + RETURN_TYPES = ("BLOCKS",) + RETURN_NAMES = ("blocks",) + FUNCTION = "main" + CATEGORY = "RES4LYF/sampler_extensions" + + def main(self, + mode = "scattersort", + noise_mode = "update", + apply_to = "", + block_type = "input", + block_list = "all", + block_weights = "1.0", + + resample = 0.0, + res = 0.0, + spatial = 0.0, + + tile_h = 128, + tile_w = 128, + + invert_mask = False, + + mask = None, + blocks = None, + ): + + #mask = 1-mask if mask is not None else None + + blocks = copy.deepcopy(blocks) if blocks is not None else {} + + block_weights = parse_range_string(block_weights) + + if len(block_weights) == 0: + block_weights.append(0.0) + + if len(block_weights) == 1: + block_weights = block_weights * 100 + + if type(block_weights[0]) == int: + block_weights = [float(val) for val in block_weights] + + if "all" in block_list: + block_list = [val for val in range(100)] + if len(block_weights) == 1: + block_weights = [block_weights[0]] * 100 + elif "even" in block_list: + block_list = [val for val in range(0, 100, 2)] + if len(block_weights) == 1: + block_weights = [block_weights[0]] * 100 + elif "odd" in block_list: + block_list = [val for val in range(1, 100, 2)] + if len(block_weights) == 1: + block_weights = [block_weights[0]] * 100 + else: + block_list = parse_range_string_int(block_list) + + weights_expanded = [0.0] * 100 + for b, w in zip(block_list, block_weights): + weights_expanded[b] = w + block_weights = weights_expanded + + StyleMMDiT = blocks.get('StyleMMDiT') + if StyleMMDiT is None: + StyleMMDiT = StyleUNet_Model() + + weights = { + "resample": resample, + "res": res, + "spatial": spatial, + + "h_tile" : tile_h // 16, + "w_tile" : tile_w // 16, + } + + block_types = block_type.split(",") + + for block_type in block_types: + + if block_type == "input": + style_blocks = StyleMMDiT.input_blocks + elif block_type == "middle": + style_blocks = StyleMMDiT.middle_blocks + elif block_type == "output": + style_blocks = StyleMMDiT.output_blocks + + for bid in block_list: + block = style_blocks[bid] + scaled_weights = { + k: (v * block_weights[bid]) if isinstance(v, float) else v + for k, v in weights.items() + } + + block.set_mode(mode) + block.set_weights(**scaled_weights) + block.apply_to = [apply_to] + + block.mask = [mask] + + blocks['StyleMMDiT'] = StyleMMDiT + + return (blocks, ) + + + + +class ClownStyle_Attn_UNet: + + @classmethod + def INPUT_TYPES(cls): + return {"required": + { + "mode": (STYLE_MODES, {"default": "scattersort"},), + "apply_to": (["self","self,cross","cross"], {"default": "self"},), + "block_type": (UNET_BLOCK_TYPES, {"default": "input"},), + "block_list": ("STRING", {"default": "all", "multiline": True}), + "block_weights": ("STRING", {"default": "1.0", "multiline": True}), + + "q_proj": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Strength of effect on layer; skips extra calculation if set to 0.0. Skips interpolation if set to 1.0."}), + "k_proj": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Strength of effect on layer; skips extra calculation if set to 0.0. Skips interpolation if set to 1.0."}), + "v_proj": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Strength of effect on layer; skips extra calculation if set to 0.0. Skips interpolation if set to 1.0."}), + + "out": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Strength of effect on layer; skips extra calculation if set to 0.0. Skips interpolation if set to 1.0."}), + + "tile_h": ("INT", {"default": 128, "min": 16, "max": 10000, "step": 16, "tooltip": "Tile size for tiled modes. Lower values will transfer composition more effectively. Dimensions of image must be divisible by this value."}), + "tile_w": ("INT", {"default": 128, "min": 16, "max": 10000, "step": 16, "tooltip": "Tile size for tiled modes. Lower values will transfer composition more effectively. Dimensions of image must be divisible by this value."}), + + "invert_mask": ("BOOLEAN", {"default": False}), + }, + "optional": + { + "mask": ("MASK", ), + "blocks": ("BLOCKS", ), + } + } + + RETURN_TYPES = ("BLOCKS",) + RETURN_NAMES = ("blocks",) + FUNCTION = "main" + CATEGORY = "RES4LYF/sampler_extensions" + + def main(self, + mode = "scattersort", + noise_mode = "update", + apply_to = "self", + block_type = "input", + block_list = "all", + block_weights = "1.0", + + q_proj = 0.0, + k_proj = 0.0, + v_proj = 0.0, + + out = 0.0, + + tile_h = 128, + tile_w = 128, + + invert_mask = False, + + mask = None, + blocks = None, + ): + + #mask = 1-mask if mask is not None else None + + blocks = copy.deepcopy(blocks) if blocks is not None else {} + + block_weights = parse_range_string(block_weights) + + if len(block_weights) == 0: + block_weights.append(0.0) + + if len(block_weights) == 1: + block_weights = block_weights * 100 + + if type(block_weights[0]) == int: + block_weights = [float(val) for val in block_weights] + + if "all" in block_list: + block_list = [val for val in range(100)] + if len(block_weights) == 1: + block_weights = [block_weights[0]] * 100 + elif "even" in block_list: + block_list = [val for val in range(0, 100, 2)] + if len(block_weights) == 1: + block_weights = [block_weights[0]] * 100 + elif "odd" in block_list: + block_list = [val for val in range(1, 100, 2)] + if len(block_weights) == 1: + block_weights = [block_weights[0]] * 100 + else: + block_list = parse_range_string_int(block_list) + + weights_expanded = [0.0] * 100 + for b, w in zip(block_list, block_weights): + weights_expanded[b] = w + block_weights = weights_expanded + + StyleMMDiT = blocks.get('StyleMMDiT') + if StyleMMDiT is None: + StyleMMDiT = StyleUNet_Model() + + weights = { + "q_proj": q_proj, + "k_proj": k_proj, + "v_proj": v_proj, + "out" : out, + + "h_tile": tile_h // 8, + "w_tile": tile_w // 8, + } + + block_types = block_type.split(",") + + for block_type in block_types: + + if block_type == "input": + style_blocks = StyleMMDiT.input_blocks + elif block_type == "middle": + style_blocks = StyleMMDiT.middle_blocks + elif block_type == "output": + style_blocks = StyleMMDiT.output_blocks + + for bid in block_list: + block = style_blocks[bid] + scaled_weights = { + k: (v * block_weights[bid]) if isinstance(v, float) else v + for k, v in weights.items() + } + + #for tfmr_block in block.spatial_block.TFMR: + tfmr_block = block.spatial_block.TFMR + if "self" in apply_to: + tfmr_block.ATTN1.set_mode(mode) + tfmr_block.ATTN1.set_weights(**scaled_weights) + tfmr_block.ATTN1.apply_to = [apply_to] + + if "cross" in apply_to: + tfmr_block.ATTN2.set_mode(mode) + tfmr_block.ATTN2.set_weights(**scaled_weights) + tfmr_block.ATTN2.apply_to = [apply_to] + + block.attn_mask = [mask] + + blocks['StyleMMDiT'] = StyleMMDiT + + return (blocks, ) + + + + + + + + + + + + + + + + + +class ClownStyle_ResBlock_UNet: + + @classmethod + def INPUT_TYPES(cls): + return {"required": + { + "mode": (STYLE_MODES, {"default": "scattersort"},), + #"apply_to": (["img", "img+txt","img,txt", "txt",], {"default": "img+txt"},), + "block_type": (UNET_BLOCK_TYPES, {"default": "input"},), + "block_list": ("STRING", {"default": "all", "multiline": True}), + "block_weights": ("STRING", {"default": "1.0", "multiline": True}), + + "in_norm": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Strength of effect on layer; skips extra calculation if set to 0.0. Skips interpolation if set to 1.0."}), + "in_silu": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Strength of effect on layer; skips extra calculation if set to 0.0. Skips interpolation if set to 1.0."}), + "in_conv": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Strength of effect on layer; skips extra calculation if set to 0.0. Skips interpolation if set to 1.0."}), + + "emb_silu": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Strength of effect on layer; skips extra calculation if set to 0.0. Skips interpolation if set to 1.0."}), + "emb_linear": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Strength of effect on layer; skips extra calculation if set to 0.0. Skips interpolation if set to 1.0."}), + "emb_res": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Strength of effect on layer; skips extra calculation if set to 0.0. Skips interpolation if set to 1.0."}), + + "out_norm": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Strength of effect on layer; skips extra calculation if set to 0.0. Skips interpolation if set to 1.0."}), + "out_silu": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Strength of effect on layer; skips extra calculation if set to 0.0. Skips interpolation if set to 1.0."}), + "out_conv": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Strength of effect on layer; skips extra calculation if set to 0.0. Skips interpolation if set to 1.0."}), + + "residual": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Strength of effect on layer; skips extra calculation if set to 0.0. Skips interpolation if set to 1.0."}), + + "tile_h": ("INT", {"default": 128, "min": 16, "max": 10000, "step": 16, "tooltip": "Tile size for tiled modes. Lower values will transfer composition more effectively. Dimensions of image must be divisible by this value."}), + "tile_w": ("INT", {"default": 128, "min": 16, "max": 10000, "step": 16, "tooltip": "Tile size for tiled modes. Lower values will transfer composition more effectively. Dimensions of image must be divisible by this value."}), + + "invert_mask": ("BOOLEAN",{"default": False}), + }, + "optional": + { + "mask": ("MASK", ), + "blocks": ("BLOCKS", ), + } + } + + RETURN_TYPES = ("BLOCKS",) + RETURN_NAMES = ("blocks",) + FUNCTION = "main" + CATEGORY = "RES4LYF/sampler_extensions" + + def main(self, + mode = "scattersort", + noise_mode = "update", + apply_to = "", + block_type = "input", + block_list = "all", + block_weights = "1.0", + + in_norm = 0.0, + in_silu = 0.0, + in_conv = 0.0, + + emb_silu = 0.0, + emb_linear = 0.0, + emb_res = 0.0, + + out_norm = 0.0, + out_silu = 0.0, + out_conv = 0.0, + + residual = 0.0, + + tile_h = 128, + tile_w = 128, + + invert_mask = False, + + mask = None, + blocks = None, + ): + + #mask = 1-mask if mask is not None else None + + blocks = copy.deepcopy(blocks) if blocks is not None else {} + + block_weights = parse_range_string(block_weights) + + if len(block_weights) == 0: + block_weights.append(0.0) + + if len(block_weights) == 1: + block_weights = block_weights * 100 + + if type(block_weights[0]) == int: + block_weights = [float(val) for val in block_weights] + + if "all" in block_list: + block_list = [val for val in range(100)] + if len(block_weights) == 1: + block_weights = [block_weights[0]] * 100 + elif "even" in block_list: + block_list = [val for val in range(0, 100, 2)] + if len(block_weights) == 1: + block_weights = [block_weights[0]] * 100 + elif "odd" in block_list: + block_list = [val for val in range(1, 100, 2)] + if len(block_weights) == 1: + block_weights = [block_weights[0]] * 100 + else: + block_list = parse_range_string_int(block_list) + + weights_expanded = [0.0] * 100 + for b, w in zip(block_list, block_weights): + weights_expanded[b] = w + block_weights = weights_expanded + + StyleMMDiT = blocks.get('StyleMMDiT') + if StyleMMDiT is None: + StyleMMDiT = StyleUNet_Model() + + weights = { + "in_norm": in_norm, + "in_silu": in_silu, + "in_conv": in_conv, + + "emb_silu": emb_silu, + "emb_linear": emb_linear, + "emb_res": emb_res, + + "out_norm": out_norm, + "out_silu": out_silu, + "out_conv": out_conv, + + "residual": residual, + + "h_tile": tile_h // 8, + "w_tile": tile_w // 8, + } + + block_types = block_type.split(",") + + for block_type in block_types: + + if block_type == "input": + style_blocks = StyleMMDiT.input_blocks + elif block_type == "middle": + style_blocks = StyleMMDiT.middle_blocks + elif block_type == "output": + style_blocks = StyleMMDiT.output_blocks + + for bid in block_list: + block = style_blocks[bid] + scaled_weights = { + k: (v * block_weights[bid]) if isinstance(v, float) else v + for k, v in weights.items() + } + + block.res_block.set_mode(mode) + block.res_block.set_weights(**scaled_weights) + block.res_block.apply_to = [apply_to] + block.res_block.mask = [mask] + + blocks['StyleMMDiT'] = StyleMMDiT + + return (blocks, ) + + + + + + + + +class ClownStyle_SpatialBlock_UNet: + + @classmethod + def INPUT_TYPES(cls): + return {"required": + { + "mode": (STYLE_MODES, {"default": "scattersort"},), + #"apply_to": (["img", "img+txt","img,txt", "txt",], {"default": "img+txt"},), + "block_type": (UNET_BLOCK_TYPES, {"default": "input"},), + "block_list": ("STRING", {"default": "all", "multiline": True}), + "block_weights": ("STRING", {"default": "1.0", "multiline": True}), + + "norm_in": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Strength of effect on layer; skips extra calculation if set to 0.0. Skips interpolation if set to 1.0."}), + "proj_in": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Strength of effect on layer; skips extra calculation if set to 0.0. Skips interpolation if set to 1.0."}), + "transformer_block": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Strength of effect on layer; skips extra calculation if set to 0.0. Skips interpolation if set to 1.0."}), + "transformer": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Strength of effect on layer; skips extra calculation if set to 0.0. Skips interpolation if set to 1.0."}), + "proj_out": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Strength of effect on layer; skips extra calculation if set to 0.0. Skips interpolation if set to 1.0."}), + "res": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Strength of effect on layer; skips extra calculation if set to 0.0. Skips interpolation if set to 1.0."}), + + "tile_h": ("INT", {"default": 128, "min": 16, "max": 10000, "step": 16, "tooltip": "Tile size for tiled modes. Lower values will transfer composition more effectively. Dimensions of image must be divisible by this value."}), + "tile_w": ("INT", {"default": 128, "min": 16, "max": 10000, "step": 16, "tooltip": "Tile size for tiled modes. Lower values will transfer composition more effectively. Dimensions of image must be divisible by this value."}), + + "invert_mask": ("BOOLEAN",{"default": False}), + }, + "optional": + { + "mask": ("MASK", ), + "blocks": ("BLOCKS", ), + } + } + + RETURN_TYPES = ("BLOCKS",) + RETURN_NAMES = ("blocks",) + FUNCTION = "main" + CATEGORY = "RES4LYF/sampler_extensions" + + def main(self, + mode = "scattersort", + noise_mode = "update", + apply_to = "", + block_type = "input", + block_list = "all", + block_weights = "1.0", + + norm_in = 0.0, + proj_in = 0.0, + transformer_block = 0.0, + transformer = 0.0, + proj_out = 0.0, + res = 0.0, + + tile_h = 128, + tile_w = 128, + + invert_mask = False, + + mask = None, + blocks = None, + ): + + spatial_norm_in = norm_in + spatial_proj_in = proj_in + spatial_transformer_block = transformer_block + spatial_transformer = transformer + spatial_proj_out = proj_out + spatial_res = res + + #mask = 1-mask if mask is not None else None + + blocks = copy.deepcopy(blocks) if blocks is not None else {} + + block_weights = parse_range_string(block_weights) + + if len(block_weights) == 0: + block_weights.append(0.0) + + if len(block_weights) == 1: + block_weights = block_weights * 100 + + if type(block_weights[0]) == int: + block_weights = [float(val) for val in block_weights] + + if "all" in block_list: + block_list = [val for val in range(100)] + if len(block_weights) == 1: + block_weights = [block_weights[0]] * 100 + elif "even" in block_list: + block_list = [val for val in range(0, 100, 2)] + if len(block_weights) == 1: + block_weights = [block_weights[0]] * 100 + elif "odd" in block_list: + block_list = [val for val in range(1, 100, 2)] + if len(block_weights) == 1: + block_weights = [block_weights[0]] * 100 + else: + block_list = parse_range_string_int(block_list) + + weights_expanded = [0.0] * 100 + for b, w in zip(block_list, block_weights): + weights_expanded[b] = w + block_weights = weights_expanded + + StyleMMDiT = blocks.get('StyleMMDiT') + if StyleMMDiT is None: + StyleMMDiT = StyleUNet_Model() + + weights = { + "spatial_norm_in" : spatial_norm_in, + "spatial_proj_in" : spatial_proj_in, + "spatial_transformer_block": spatial_transformer_block, + "spatial_transformer": spatial_transformer, + "spatial_proj_out" : spatial_proj_out, + "spatial_res" : spatial_res, + + "h_tile": tile_h // 8, + "w_tile": tile_w // 8, + } + + block_types = block_type.split(",") + + for block_type in block_types: + + if block_type == "input": + style_blocks = StyleMMDiT.input_blocks + elif block_type == "middle": + style_blocks = StyleMMDiT.middle_blocks + elif block_type == "output": + style_blocks = StyleMMDiT.output_blocks + + for bid in block_list: + block = style_blocks[bid] + scaled_weights = { + k: (v * block_weights[bid]) if isinstance(v, float) else v + for k, v in weights.items() + } + + block.spatial_block.set_mode(mode) + block.spatial_block.set_weights(**scaled_weights) + block.spatial_block.apply_to = [apply_to] + + block.spatial_block.mask = [mask] + + blocks['StyleMMDiT'] = StyleMMDiT + + return (blocks, ) + + + + + + + +class ClownStyle_TransformerBlock_UNet: + + @classmethod + def INPUT_TYPES(cls): + return {"required": + { + "mode": (STYLE_MODES, {"default": "scattersort"},), + #"apply_to": (["img", "img+txt","img,txt", "txt",], {"default": "img+txt"},), + "block_type": (UNET_BLOCK_TYPES, {"default": "input"},), + "block_list": ("STRING", {"default": "all", "multiline": True}), + "block_weights": ("STRING", {"default": "1.0", "multiline": True}), + + "norm1": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Strength of effect on layer; skips extra calculation if set to 0.0. Skips interpolation if set to 1.0."}), + "norm2": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Strength of effect on layer; skips extra calculation if set to 0.0. Skips interpolation if set to 1.0."}), + "norm3": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Strength of effect on layer; skips extra calculation if set to 0.0. Skips interpolation if set to 1.0."}), + + "self_attn": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Strength of effect on layer; skips extra calculation if set to 0.0. Skips interpolation if set to 1.0."}), + "cross_attn": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Strength of effect on layer; skips extra calculation if set to 0.0. Skips interpolation if set to 1.0."}), + "ff": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Strength of effect on layer; skips extra calculation if set to 0.0. Skips interpolation if set to 1.0."}), + + "self_attn_res": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Strength of effect on layer; skips extra calculation if set to 0.0. Skips interpolation if set to 1.0."}), + "cross_attn_res": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Strength of effect on layer; skips extra calculation if set to 0.0. Skips interpolation if set to 1.0."}), + "ff_res": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Strength of effect on layer; skips extra calculation if set to 0.0. Skips interpolation if set to 1.0."}), + + "tile_h": ("INT", {"default": 128, "min": 16, "max": 10000, "step": 16, "tooltip": "Tile size for tiled modes. Lower values will transfer composition more effectively. Dimensions of image must be divisible by this value."}), + "tile_w": ("INT", {"default": 128, "min": 16, "max": 10000, "step": 16, "tooltip": "Tile size for tiled modes. Lower values will transfer composition more effectively. Dimensions of image must be divisible by this value."}), + + "invert_mask": ("BOOLEAN",{"default": False}), + }, + "optional": + { + "mask": ("MASK", ), + "blocks": ("BLOCKS", ), + } + } + + RETURN_TYPES = ("BLOCKS",) + RETURN_NAMES = ("blocks",) + FUNCTION = "main" + CATEGORY = "RES4LYF/sampler_extensions" + + def main(self, + mode = "scattersort", + noise_mode = "update", + apply_to = "", + block_type = "input", + block_list = "all", + block_weights = "1.0", + + norm1 = 0.0, + norm2 = 0.0, + norm3 = 0.0, + + self_attn = 0.0, + cross_attn = 0.0, + ff = 0.0, + + self_attn_res = 0.0, + cross_attn_res = 0.0, + ff_res = 0.0, + + tile_h = 128, + tile_w = 128, + + invert_mask = False, + + mask = None, + blocks = None, + ): + + #mask = 1-mask if mask is not None else None + + blocks = copy.deepcopy(blocks) if blocks is not None else {} + + block_weights = parse_range_string(block_weights) + + if len(block_weights) == 0: + block_weights.append(0.0) + + if len(block_weights) == 1: + block_weights = block_weights * 100 + + if type(block_weights[0]) == int: + block_weights = [float(val) for val in block_weights] + + if "all" in block_list: + block_list = [val for val in range(100)] + if len(block_weights) == 1: + block_weights = [block_weights[0]] * 100 + elif "even" in block_list: + block_list = [val for val in range(0, 100, 2)] + if len(block_weights) == 1: + block_weights = [block_weights[0]] * 100 + elif "odd" in block_list: + block_list = [val for val in range(1, 100, 2)] + if len(block_weights) == 1: + block_weights = [block_weights[0]] * 100 + else: + block_list = parse_range_string_int(block_list) + + weights_expanded = [0.0] * 100 + for b, w in zip(block_list, block_weights): + weights_expanded[b] = w + block_weights = weights_expanded + + StyleMMDiT = blocks.get('StyleMMDiT') + if StyleMMDiT is None: + StyleMMDiT = StyleUNet_Model() + + weights = { + "norm1" : norm1, + "norm2" : norm2, + "norm3" : norm3, + + "self_attn" : self_attn, + "cross_attn": cross_attn, + "ff" : ff, + + "self_attn_res" : self_attn_res, + "cross_attn_res": cross_attn_res, + "ff_res" : ff_res, + + "h_tile": tile_h // 8, + "w_tile": tile_w // 8, + } + + block_types = block_type.split(",") + + for block_type in block_types: + + if block_type == "input": + style_blocks = StyleMMDiT.input_blocks + elif block_type == "middle": + style_blocks = StyleMMDiT.middle_blocks + elif block_type == "output": + style_blocks = StyleMMDiT.output_blocks + + for bid in block_list: + block = style_blocks[bid] + scaled_weights = { + k: (v * block_weights[bid]) if isinstance(v, float) else v + for k, v in weights.items() + } + + block.spatial_block.TFMR.set_mode(mode) + block.spatial_block.TFMR.set_weights(**scaled_weights) + block.spatial_block.TFMR.apply_to = [apply_to] + + block.spatial_block.TFMR.mask = [mask] + + blocks['StyleMMDiT'] = StyleMMDiT + + return (blocks, ) + + + diff --git a/ComfyUI/custom_nodes/RES4LYF/chroma/layers.py b/ComfyUI/custom_nodes/RES4LYF/chroma/layers.py new file mode 100644 index 0000000000000000000000000000000000000000..b42d0e92e659c51de28f37573148a7eacf13ad73 --- /dev/null +++ b/ComfyUI/custom_nodes/RES4LYF/chroma/layers.py @@ -0,0 +1,183 @@ +import torch +from torch import Tensor, nn + +#from comfy.ldm.flux.math import attention +from comfy.ldm.flux.layers import ( + MLPEmbedder, + RMSNorm, + QKNorm, + SelfAttention, + ModulationOut, +) + +from .math import attention, rope, apply_rope + +class ChromaModulationOut(ModulationOut): + @classmethod + def from_offset(cls, tensor: torch.Tensor, offset: int = 0) -> ModulationOut: + return cls( + shift=tensor[:, offset : offset + 1, :], + scale=tensor[:, offset + 1 : offset + 2, :], + gate=tensor[:, offset + 2 : offset + 3, :], + ) + + + + +class Approximator(nn.Module): + def __init__(self, in_dim: int, out_dim: int, hidden_dim: int, n_layers = 5, dtype=None, device=None, operations=None): + super().__init__() + self.in_proj = operations.Linear(in_dim, hidden_dim, bias=True, dtype=dtype, device=device) + self.layers = nn.ModuleList([MLPEmbedder(hidden_dim, hidden_dim, dtype=dtype, device=device, operations=operations) for x in range( n_layers)]) + self.norms = nn.ModuleList([RMSNorm(hidden_dim, dtype=dtype, device=device, operations=operations) for x in range( n_layers)]) + self.out_proj = operations.Linear(hidden_dim, out_dim, dtype=dtype, device=device) + + @property + def device(self): + # Get the device of the module (assumes all parameters are on the same device) + return next(self.parameters()).device + + def forward(self, x: Tensor) -> Tensor: + x = self.in_proj(x) + + for layer, norms in zip(self.layers, self.norms): + x = x + layer(norms(x)) + + x = self.out_proj(x) + + return x + + +class ReChromaDoubleStreamBlock(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): + super().__init__() + + mlp_hidden_dim = int(hidden_size * mlp_ratio) + self.num_heads = num_heads + self.hidden_size = hidden_size + self.img_norm1 = operations.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6, dtype=dtype, device=device) + self.img_attn = SelfAttention(dim=hidden_size, num_heads=num_heads, qkv_bias=qkv_bias, dtype=dtype, device=device, operations=operations) + + self.img_norm2 = operations.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6, dtype=dtype, device=device) + self.img_mlp = nn.Sequential( + operations.Linear(hidden_size, mlp_hidden_dim, bias=True, dtype=dtype, device=device), + nn.GELU(approximate="tanh"), + operations.Linear(mlp_hidden_dim, hidden_size, bias=True, dtype=dtype, device=device), + ) + + self.txt_norm1 = operations.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6, dtype=dtype, device=device) + self.txt_attn = SelfAttention(dim=hidden_size, num_heads=num_heads, qkv_bias=qkv_bias, dtype=dtype, device=device, operations=operations) + + self.txt_norm2 = operations.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6, dtype=dtype, device=device) + self.txt_mlp = nn.Sequential( + operations.Linear(hidden_size, mlp_hidden_dim, bias=True, dtype=dtype, device=device), + nn.GELU(approximate="tanh"), + operations.Linear(mlp_hidden_dim, hidden_size, bias=True, dtype=dtype, device=device), + ) + self.flipped_img_txt = flipped_img_txt + + def forward(self, img: Tensor, txt: Tensor, pe: Tensor, vec: Tensor, attn_mask=None): + (img_mod1, img_mod2), (txt_mod1, txt_mod2) = vec + + # prepare image for attention + img_modulated = self.img_norm1(img) + img_modulated = (1 + img_mod1.scale) * img_modulated + img_mod1.shift + 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 = self.img_attn.norm(img_q, img_k, img_v) + + # prepare txt for attention + txt_modulated = self.txt_norm1(txt) + txt_modulated = (1 + txt_mod1.scale) * txt_modulated + txt_mod1.shift + 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 = self.txt_attn.norm(txt_q, txt_k, txt_v) + + # run actual attention + attn = attention(torch.cat((txt_q, img_q), dim=2), + torch.cat((txt_k, img_k), dim=2), + torch.cat((txt_v, img_v), dim=2), + pe=pe, mask=attn_mask) + + txt_attn, img_attn = attn[:, : txt.shape[1]], attn[:, txt.shape[1] :] + + # calculate the img bloks + img = img + img_mod1.gate * self.img_attn.proj(img_attn) + img = img + img_mod2.gate * self.img_mlp((1 + img_mod2.scale) * self.img_norm2(img) + img_mod2.shift) + + # calculate the txt bloks + txt += txt_mod1.gate * self.txt_attn.proj(txt_attn) + txt += txt_mod2.gate * self.txt_mlp((1 + txt_mod2.scale) * self.txt_norm2(txt) + txt_mod2.shift) + + if txt.dtype == torch.float16: + txt = torch.nan_to_num(txt, nan=0.0, posinf=65504, neginf=-65504) + + return img, txt + + +class ReChromaSingleStreamBlock(nn.Module): + """ + A DiT block with parallel linear layers as described in + https://arxiv.org/abs/2302.05442 and adapted modulation interface. + """ + + def __init__( + self, + hidden_size: int, + num_heads: int, + mlp_ratio: float = 4.0, + qk_scale: float = None, + dtype=None, + device=None, + operations=None + ): + super().__init__() + self.hidden_dim = hidden_size + self.num_heads = num_heads + head_dim = hidden_size // num_heads + self.scale = qk_scale or head_dim**-0.5 + + self.mlp_hidden_dim = int(hidden_size * mlp_ratio) + # qkv and mlp_in + self.linear1 = operations.Linear(hidden_size, hidden_size * 3 + self.mlp_hidden_dim, dtype=dtype, device=device) + # proj and mlp_out + self.linear2 = operations.Linear(hidden_size + self.mlp_hidden_dim, hidden_size, dtype=dtype, device=device) + + self.norm = QKNorm(head_dim, dtype=dtype, device=device, operations=operations) + + self.hidden_size = hidden_size + self.pre_norm = operations.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6, dtype=dtype, device=device) + + self.mlp_act = nn.GELU(approximate="tanh") + + def forward(self, x: Tensor, pe: Tensor, vec: Tensor, attn_mask=None) -> Tensor: + mod = vec + x_mod = (1 + mod.scale) * self.pre_norm(x) + mod.shift + qkv, mlp = torch.split(self.linear1(x_mod), [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 = self.norm(q, k, v) + + # compute attention + attn = attention(q, k, v, pe=pe, mask=attn_mask) + # compute activation in mlp stream, cat again and run second linear layer + output = self.linear2(torch.cat((attn, self.mlp_act(mlp)), 2)) + x += mod.gate * output + if x.dtype == torch.float16: + x = torch.nan_to_num(x, nan=0.0, posinf=65504, neginf=-65504) + return x + + +class LastLayer(nn.Module): + def __init__(self, hidden_size: int, patch_size: int, out_channels: int, dtype=None, device=None, operations=None): + super().__init__() + self.norm_final = operations.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6, dtype=dtype, device=device) + self.linear = operations.Linear(hidden_size, out_channels, bias=True, dtype=dtype, device=device) + + def forward(self, x: Tensor, vec: Tensor) -> Tensor: + shift, scale = vec + shift = shift.squeeze(1) + scale = scale.squeeze(1) + x = (1 + scale[:, None, :]) * self.norm_final(x) + shift[:, None, :] + x = self.linear(x) + return x diff --git a/ComfyUI/custom_nodes/RES4LYF/chroma/math.py b/ComfyUI/custom_nodes/RES4LYF/chroma/math.py new file mode 100644 index 0000000000000000000000000000000000000000..b2f59be4d0be8f0bfa98c762bdf5867fc76bb44c --- /dev/null +++ b/ComfyUI/custom_nodes/RES4LYF/chroma/math.py @@ -0,0 +1,43 @@ +import torch +from einops import rearrange +from torch import Tensor +from comfy.ldm.modules.attention import attention_pytorch + +import comfy.model_management + +def attention(q: Tensor, k: Tensor, v: Tensor, pe: Tensor, mask=None) -> Tensor: + q, k = apply_rope(q, k, pe) + + heads = q.shape[1] + x = attention_pytorch(q, k, v, heads, skip_reshape=True, mask=mask) + #if mask is not None: + # x = attention_pytorch(q, k, v, heads, skip_reshape=True, mask=mask) + #else: + # from comfy.ldm.modules.attention import optimized_attention + # x = optimized_attention(q, k, v, heads, skip_reshape=True, mask=None) + return x + + +def rope(pos: Tensor, dim: int, theta: int) -> Tensor: + assert dim % 2 == 0 + if comfy.model_management.is_device_mps(pos.device) or comfy.model_management.is_intel_xpu() or comfy.model_management.is_directml_enabled(): + device = torch.device("cpu") + else: + device = pos.device + + scale = torch.linspace(0, (dim - 2) / dim, steps=dim//2, dtype=torch.float64, device=device) + omega = 1.0 / (theta**scale) + out = torch.einsum("...n,d->...nd", pos.to(dtype=torch.float32, device=device), omega) + out = torch.stack([torch.cos(out), -torch.sin(out), torch.sin(out), torch.cos(out)], dim=-1) + out = rearrange(out, "b n d (i j) -> b n d i j", i=2, j=2) + return out.to(dtype=torch.float32, device=pos.device) + + +def apply_rope(xq: Tensor, xk: Tensor, freqs_cis: Tensor): + xq_ = xq.float().reshape(*xq.shape[:-1], -1, 1, 2) + xk_ = xk.float().reshape(*xk.shape[:-1], -1, 1, 2) + xq_out = freqs_cis[..., 0] * xq_[..., 0] + freqs_cis[..., 1] * xq_[..., 1] + xk_out = freqs_cis[..., 0] * xk_[..., 0] + freqs_cis[..., 1] * xk_[..., 1] + return xq_out.reshape(*xq.shape).type_as(xq), xk_out.reshape(*xk.shape).type_as(xk) + + diff --git a/ComfyUI/custom_nodes/RES4LYF/chroma/model.py b/ComfyUI/custom_nodes/RES4LYF/chroma/model.py new file mode 100644 index 0000000000000000000000000000000000000000..cf41c7142851541b75579ffcdac23a63b35d33b4 --- /dev/null +++ b/ComfyUI/custom_nodes/RES4LYF/chroma/model.py @@ -0,0 +1,1501 @@ +#Original code can be found on: https://github.com/black-forest-labs/flux + +from dataclasses import dataclass + +import torch +import torch.nn.functional as F +from torch import Tensor, nn +from einops import rearrange, repeat +import comfy.ldm.common_dit + +from ..helper import ExtraOptions + +from ..latents import tile_latent, untile_latent, gaussian_blur_2d, median_blur_2d +from ..style_transfer import apply_scattersort_masked, apply_scattersort_tiled, adain_seq_inplace, adain_patchwise_row_batch_med, adain_patchwise_row_batch + +from comfy.ldm.flux.layers import ( + EmbedND, + timestep_embedding, +) + +from .layers import ( + ReChromaDoubleStreamBlock, + LastLayer, + ReChromaSingleStreamBlock, + Approximator, + ChromaModulationOut, +) + + +@dataclass +class ChromaParams: + in_channels : int + out_channels : int + context_in_dim : int + hidden_size : int + mlp_ratio : float + num_heads : int + depth : int + depth_single_blocks: int + axes_dim : list + theta : int + patch_size : int + qkv_bias : bool + in_dim : int + out_dim : int + hidden_dim : int + n_layers : int + + + + +class ReChroma(nn.Module): + """ + Transformer model for flow matching on sequences. + """ + + def __init__(self, image_model=None, final_layer=True, dtype=None, device=None, operations=None, **kwargs): + super().__init__() + self.dtype = dtype + params = ChromaParams(**kwargs) + self.params = params + self.patch_size = params.patch_size + self.in_channels = params.in_channels + self.out_channels = params.out_channels + if params.hidden_size % params.num_heads != 0: + raise ValueError( + f"Hidden size {params.hidden_size} must be divisible by num_heads {params.num_heads}" + ) + pe_dim = params.hidden_size // params.num_heads + if sum(params.axes_dim) != pe_dim: + raise ValueError(f"Got {params.axes_dim} but expected positional dim {pe_dim}") + self.hidden_size = params.hidden_size + self.num_heads = params.num_heads + self.in_dim = params.in_dim + self.out_dim = params.out_dim + self.hidden_dim = params.hidden_dim + self.n_layers = params.n_layers + self.pe_embedder = EmbedND(dim=pe_dim, theta=params.theta, axes_dim=params.axes_dim) + self.img_in = operations.Linear(self.in_channels, self.hidden_size, bias=True, dtype=dtype, device=device) + self.txt_in = operations.Linear(params.context_in_dim, self.hidden_size, dtype=dtype, device=device) + # set as nn identity for now, will overwrite it later. + self.distilled_guidance_layer = Approximator( + in_dim=self.in_dim, + hidden_dim=self.hidden_dim, + out_dim=self.out_dim, + n_layers=self.n_layers, + dtype=dtype, device=device, operations=operations + ) + + + self.double_blocks = nn.ModuleList( + [ + ReChromaDoubleStreamBlock( + self.hidden_size, + self.num_heads, + mlp_ratio=params.mlp_ratio, + qkv_bias=params.qkv_bias, + dtype=dtype, device=device, operations=operations + ) + for _ in range(params.depth) + ] + ) + + self.single_blocks = nn.ModuleList( + [ + ReChromaSingleStreamBlock(self.hidden_size, self.num_heads, mlp_ratio=params.mlp_ratio, dtype=dtype, device=device, operations=operations) + for _ in range(params.depth_single_blocks) + ] + ) + + if final_layer: + self.final_layer = LastLayer(self.hidden_size, 1, self.out_channels, dtype=dtype, device=device, operations=operations) + + self.skip_mmdit = [] + self.skip_dit = [] + self.lite = False + + def get_modulations(self, tensor: torch.Tensor, block_type: str, *, idx: int = 0): + # This function slices up the modulations tensor which has the following layout: + # single : num_single_blocks * 3 elements + # double_img : num_double_blocks * 6 elements + # double_txt : num_double_blocks * 6 elements + # final : 2 elements + if block_type == "final": + return (tensor[:, -2:-1, :], tensor[:, -1:, :]) + single_block_count = self.params.depth_single_blocks + double_block_count = self.params.depth + offset = 3 * idx + if block_type == "single": + return ChromaModulationOut.from_offset(tensor, offset) + # Double block modulations are 6 elements so we double 3 * idx. + offset *= 2 + if block_type in {"double_img", "double_txt"}: + # Advance past the single block modulations. + offset += 3 * single_block_count + if block_type == "double_txt": + # Advance past the double block img modulations. + offset += 6 * double_block_count + return ( + ChromaModulationOut.from_offset(tensor, offset), + ChromaModulationOut.from_offset(tensor, offset + 3), + ) + raise ValueError("Bad block_type") + + + def forward_blocks( + self, + img : Tensor, + img_ids : Tensor, + txt : Tensor, + txt_ids : Tensor, + timesteps : Tensor, + guidance : Tensor = None, + control = None, + update_cross_attn = None, + transformer_options ={}, + attn_mask : Tensor = None, + UNCOND : bool = False, + ) -> Tensor: + patches_replace = transformer_options.get("patches_replace", {}) + if img.ndim != 3 or txt.ndim != 3: + raise ValueError("Input img and txt tensors must have 3 dimensions.") + + # running on sequences img + img = self.img_in(img) + + # distilled vector guidance + mod_index_length = 344 + distill_timestep = timestep_embedding(timesteps.detach().clone(), 16).to(img.device, img.dtype) + # guidance = guidance * + distil_guidance = timestep_embedding(guidance.detach().clone(), 16).to(img.device, img.dtype) + + # get all modulation index + modulation_index = timestep_embedding(torch.arange(mod_index_length), 32).to(img.device, img.dtype) + # we need to broadcast the modulation index here so each batch has all of the index + modulation_index = modulation_index.unsqueeze(0).repeat(img.shape[0], 1, 1).to(img.device, img.dtype) + # and we need to broadcast timestep and guidance along too + timestep_guidance = torch.cat([distill_timestep, distil_guidance], dim=1).unsqueeze(1).repeat(1, mod_index_length, 1).to(img.dtype).to(img.device, img.dtype) + # then and only then we could concatenate it together + input_vec = torch.cat([timestep_guidance, modulation_index], dim=-1).to(img.device, img.dtype) + + mod_vectors = self.distilled_guidance_layer(input_vec) + + txt = self.txt_in(txt) + + ids = torch.cat((txt_ids, img_ids), dim=1) + pe = self.pe_embedder(ids) + + weight = -1 * transformer_options.get("regional_conditioning_weight", 0.0) + floor = -1 * transformer_options.get("regional_conditioning_floor", 0.0) + mask_zero = None + mask = None + + text_len = txt.shape[1] # mask_obj[0].text_len + + if not UNCOND and 'AttnMask' in transformer_options: # and weight != 0: + AttnMask = transformer_options['AttnMask'] + mask = transformer_options['AttnMask'].attn_mask.mask.to('cuda') + if mask_zero is None: + mask_zero = torch.ones_like(mask) + img_len = transformer_options['AttnMask'].img_len + #mask_zero[:text_len, :text_len] = mask[:text_len, :text_len] + mask_zero[:text_len, :] = mask[:text_len, :] + mask_zero[:, :text_len] = mask[:, :text_len] + if weight == 0: + mask = None + + if UNCOND and 'AttnMask_neg' in transformer_options: # and weight != 0: + AttnMask = transformer_options['AttnMask_neg'] + if mask_zero is None: + mask_zero = torch.ones_like(mask) + img_len = transformer_options['AttnMask_neg'].img_len + #mask_zero[:text_len, :text_len] = mask[:text_len, :text_len] + mask_zero[:text_len, :] = mask[:text_len, :] + mask_zero[:, :text_len] = mask[:, :text_len] + if weight == 0: + mask = None + + elif UNCOND and 'AttnMask' in transformer_options: + AttnMask = transformer_options['AttnMask'] + mask = transformer_options['AttnMask'].attn_mask.mask.to('cuda') + if mask_zero is None: + mask_zero = torch.ones_like(mask) + img_len = transformer_options['AttnMask'].img_len + #mask_zero[:text_len, :text_len] = mask[:text_len, :text_len] + mask_zero[:text_len, :] = mask[:text_len, :] + mask_zero[:, :text_len] = mask[:, :text_len] + if weight == 0: + mask = None + + if mask is not None and not type(mask[0][0].item()) == bool: + mask = mask.to(img.dtype) + if mask_zero is not None and not type(mask_zero[0][0].item()) == bool: + mask_zero = mask_zero.to(img.dtype) + + total_layers = len(self.double_blocks) + len(self.single_blocks) + + attn_mask = mask if attn_mask is None else attn_mask + + blocks_replace = patches_replace.get("dit", {}) + for i, block in enumerate(self.double_blocks): + if i not in self.skip_mmdit: + double_mod = ( + self.get_modulations(mod_vectors, "double_img", idx=i), + self.get_modulations(mod_vectors, "double_txt", idx=i), + ) + if ("double_block", i) in blocks_replace: + def block_wrap(args): + out = {} + out["img"], out["txt"] = block( img = args["img"], + txt = args["txt"], + vec = args["vec"], + pe = args["pe"], + attn_mask = args.get("attn_mask")) + return out + + out = blocks_replace[("double_block", i)]({ "img" : img, + "txt" : txt, + "vec" : double_mod, + "pe" : pe, + "attn_mask" : attn_mask}, + {"original_block" : block_wrap}) + txt = out["txt"] + img = out["img"] + else: + + if weight > 0 and mask is not None and weight <= i/total_layers: + img, txt = block(img=img, txt=txt, vec=double_mod, pe=pe, attn_mask=mask_zero) + + elif (weight < 0 and mask is not None and abs(weight) <= (1 - i/total_layers)): + img_tmpZ, txt_tmpZ = img.clone(), txt.clone() + + img_tmpZ, txt = block(img=img_tmpZ, txt=txt_tmpZ, vec=double_mod, pe=pe, attn_mask=mask) + img, txt_tmpZ = block(img=img , txt=txt , vec=double_mod, pe=pe, attn_mask=mask_zero) + + elif floor > 0 and mask is not None and floor >= i/total_layers: + mask_tmp = mask.clone() + mask_tmp[text_len:, text_len:] = 1.0 + img, txt = block(img=img, txt=txt, vec=double_mod, pe=pe, attn_mask=mask_tmp) + + elif floor < 0 and mask is not None and abs(floor) >= (1 - i/total_layers): + mask_tmp = mask.clone() + mask_tmp[text_len:, text_len:] = 1.0 + img, txt = block(img=img, txt=txt, vec=double_mod, pe=pe, attn_mask=mask_tmp) + + elif update_cross_attn is not None and update_cross_attn['skip_cross_attn']: + print("update_cross_attn not yet implemented for Chroma.", flush=True) + #img, txt_init = block(img, img_masks, txt, clip, rope, mask, update_cross_attn=update_cross_attn) + + else: + img, txt = block(img=img, txt=txt, vec=double_mod, pe=pe, attn_mask=attn_mask) + + #img, txt = block(img=img, txt=txt, vec=double_mod, pe=pe, attn_mask=attn_mask) + + if control is not None: # Controlnet + control_i = control.get("input") + if i < len(control_i): + add = control_i[i] + if add is not None: + img += add + + img = torch.cat((txt, img), 1) + + for i, block in enumerate(self.single_blocks): + if i not in self.skip_dit: + single_mod = self.get_modulations(mod_vectors, "single", idx=i) + if ("single_block", i) in blocks_replace: + def block_wrap(args): + out = {} + out["img"] = block( args["img"], + vec=args["vec"], + pe=args["pe"], + attn_mask=args.get("attn_mask")) + return out + + out = blocks_replace[("single_block", i)]({ "img" : img, + "vec" : single_mod, + "pe" : pe, + "attn_mask" : attn_mask}, + {"original_block" : block_wrap}) + img = out["img"] + else: + + if weight > 0 and mask is not None and weight <= (i+len(self.double_blocks))/total_layers: + img = block(img, vec=single_mod, pe=pe, attn_mask=mask_zero) + + elif weight < 0 and mask is not None and abs(weight) <= (1 - (i+len(self.double_blocks))/total_layers): + img = block(img, vec=single_mod, pe=pe, attn_mask=mask_zero) + + elif floor > 0 and mask is not None and floor >= (i+len(self.double_blocks))/total_layers: + mask_tmp = mask.clone() + mask_tmp[text_len:, text_len:] = 1.0 + img = block(img, vec=single_mod, pe=pe, attn_mask=mask_tmp) + + elif floor < 0 and mask is not None and abs(floor) >= (1 - (i+len(self.double_blocks))/total_layers): + mask_tmp = mask.clone() + mask_tmp[text_len:, text_len:] = 1.0 + img = block(img, vec=single_mod, pe=pe, attn_mask=mask_tmp) + + else: + img = block(img, vec=single_mod, pe=pe, attn_mask=attn_mask) + + if control is not None: # Controlnet + control_o = control.get("output") + if i < len(control_o): + add = control_o[i] + if add is not None: + img[:, txt.shape[1] :, ...] += add + + img = img[:, txt.shape[1] :, ...] + final_mod = self.get_modulations(mod_vectors, "final") + img = self.final_layer(img, vec=final_mod) # (N, T, patch_size ** 2 * out_channels) + return img + + def forward_chroma_depr(self, x, timestep, context, guidance, control=None, transformer_options={}, **kwargs): + bs, c, h, w = x.shape + patch_size = 2 + x = comfy.ldm.common_dit.pad_to_patch_size(x, (patch_size, patch_size)) + + img = rearrange(x, "b c (h ph) (w pw) -> b (h w) (c ph pw)", ph=patch_size, pw=patch_size) + + h_len = ((h + (patch_size // 2)) // patch_size) + w_len = ((w + (patch_size // 2)) // patch_size) + img_ids = torch.zeros((h_len, w_len, 3), device=x.device, dtype=x.dtype) + img_ids[:, :, 1] = img_ids[:, :, 1] + torch.linspace(0, h_len - 1, steps=h_len, device=x.device, dtype=x.dtype).unsqueeze(1) + img_ids[:, :, 2] = img_ids[:, :, 2] + torch.linspace(0, w_len - 1, steps=w_len, device=x.device, dtype=x.dtype).unsqueeze(0) + img_ids = repeat(img_ids, "h w c -> b (h w) c", b=bs) + + txt_ids = torch.zeros((bs, context.shape[1], 3), device=x.device, dtype=x.dtype) + out = self.forward_orig(img, img_ids, context, txt_ids, timestep, guidance, control, transformer_options, attn_mask=kwargs.get("attention_mask", None)) + return rearrange(out, "b (h w) (c ph pw) -> b c (h ph) (w pw)", h=h_len, w=w_len, ph=2, pw=2)[:,:,:h,:w] + + + def _get_img_ids(self, x, bs, h_len, w_len, h_start, h_end, w_start, w_end): + img_ids = torch.zeros( (h_len, w_len, 3), device=x.device, dtype=x.dtype) + img_ids[..., 1] += torch.linspace(h_start, h_end - 1, steps=h_len, device=x.device, dtype=x.dtype)[:, None] + img_ids[..., 2] += torch.linspace(w_start, w_end - 1, steps=w_len, device=x.device, dtype=x.dtype)[None, :] + img_ids = repeat(img_ids, "h w c -> b (h w) c", b=bs) + return img_ids + + + + def forward(self, + x, + timestep, + context, + #y, + guidance, + control = None, + transformer_options = {}, + **kwargs + ): + x_orig = x.clone() + SIGMA = timestep[0].unsqueeze(0) + update_cross_attn = transformer_options.get("update_cross_attn") + EO = transformer_options.get("ExtraOptions", ExtraOptions("")) + if EO is not None: + EO.mute = True + + y0_style_pos = transformer_options.get("y0_style_pos") + y0_style_neg = transformer_options.get("y0_style_neg") + + y0_style_pos_weight = transformer_options.get("y0_style_pos_weight", 0.0) + y0_style_pos_synweight = transformer_options.get("y0_style_pos_synweight", 0.0) + y0_style_pos_synweight *= y0_style_pos_weight + + y0_style_neg_weight = transformer_options.get("y0_style_neg_weight", 0.0) + y0_style_neg_synweight = transformer_options.get("y0_style_neg_synweight", 0.0) + y0_style_neg_synweight *= y0_style_neg_weight + + weight = -1 * transformer_options.get("regional_conditioning_weight", 0.0) + floor = -1 * transformer_options.get("regional_conditioning_floor", 0.0) + + freqsep_lowpass_method = transformer_options.get("freqsep_lowpass_method") + freqsep_sigma = transformer_options.get("freqsep_sigma") + freqsep_kernel_size = transformer_options.get("freqsep_kernel_size") + freqsep_inner_kernel_size = transformer_options.get("freqsep_inner_kernel_size") + freqsep_stride = transformer_options.get("freqsep_stride") + + freqsep_lowpass_weight = transformer_options.get("freqsep_lowpass_weight") + freqsep_highpass_weight= transformer_options.get("freqsep_highpass_weight") + freqsep_mask = transformer_options.get("freqsep_mask") + + out_list = [] + for i in range(len(transformer_options['cond_or_uncond'])): + UNCOND = transformer_options['cond_or_uncond'][i] == 1 + + if update_cross_attn is not None: + update_cross_attn['UNCOND'] = UNCOND + + img = x + bs, c, h, w = x.shape + patch_size = 2 + img = comfy.ldm.common_dit.pad_to_patch_size(img, (patch_size, patch_size)) # 1,16,192,192 + + transformer_options['original_shape'] = img.shape + transformer_options['patch_size'] = patch_size + + h_len = ((h + (patch_size // 2)) // patch_size) # h_len 96 + w_len = ((w + (patch_size // 2)) // patch_size) # w_len 96 + + img = rearrange(img, "b c (h ph) (w pw) -> b (h w) (c ph pw)", ph=patch_size, pw=patch_size) # img 1,9216,64 1,16,128,128 -> 1,4096,64 + + context_tmp = None + + if not UNCOND and 'AttnMask' in transformer_options: # and weight != 0: + AttnMask = transformer_options['AttnMask'] + mask = transformer_options['AttnMask'].attn_mask.mask.to('cuda') + + if weight == 0: + context_tmp = transformer_options['RegContext'].context.to(context.dtype).to(context.device) + mask = None + else: + context_tmp = transformer_options['RegContext'].context.to(context.dtype).to(context.device) + + if UNCOND and 'AttnMask_neg' in transformer_options: # and weight != 0: + AttnMask = transformer_options['AttnMask_neg'] + mask = transformer_options['AttnMask_neg'].attn_mask.mask.to('cuda') + + if weight == 0: + context_tmp = transformer_options['RegContext_neg'].context.to(context.dtype).to(context.device) + mask = None + else: + context_tmp = transformer_options['RegContext_neg'].context.to(context.dtype).to(context.device) + + elif UNCOND and 'AttnMask' in transformer_options: + AttnMask = transformer_options['AttnMask'] + mask = transformer_options['AttnMask'].attn_mask.mask.to('cuda') + A = context + B = transformer_options['RegContext'].context + context_tmp = A.repeat(1, (B.shape[1] // A.shape[1]) + 1, 1)[:, :B.shape[1], :] + + if context_tmp is None: + context_tmp = context[i][None,...].clone() + + txt_ids = torch.zeros((bs, context_tmp.shape[1], 3), device=img.device, dtype=img.dtype) # txt_ids 1, 256,3 + img_ids_orig = self._get_img_ids(img, bs, h_len, w_len, 0, h_len, 0, w_len) # img_ids_orig = 1,9216,3 + + + out_tmp = self.forward_blocks(img [i][None,...].clone(), + img_ids_orig[i][None,...].clone(), + context_tmp, + txt_ids [i][None,...].clone(), + timestep [i][None,...].clone(), + #y [i][None,...].clone(), + guidance [i][None,...].clone(), + control, + update_cross_attn=update_cross_attn, + transformer_options=transformer_options, + UNCOND = UNCOND, + ) # context 1,256,4096 y 1,768 + out_list.append(out_tmp) + + out = torch.stack(out_list, dim=0).squeeze(dim=1) + + eps = rearrange(out, "b (h w) (c ph pw) -> b c (h ph) (w pw)", h=h_len, w=w_len, ph=2, pw=2)[:,:,:h,:w] + + + + + + + + + + + dtype = eps.dtype if self.style_dtype is None else self.style_dtype + + if y0_style_pos is not None: + y0_style_pos_weight = transformer_options.get("y0_style_pos_weight") + y0_style_pos_synweight = transformer_options.get("y0_style_pos_synweight") + y0_style_pos_synweight *= y0_style_pos_weight + y0_style_pos_mask = transformer_options.get("y0_style_pos_mask") + y0_style_pos_mask_edge = transformer_options.get("y0_style_pos_mask_edge") + + y0_style_pos = y0_style_pos.to(dtype) + x = x_orig.clone().to(dtype) + eps = eps.to(dtype) + eps_orig = eps.clone() + + sigma = SIGMA #t_orig[0].to(torch.float32) / 1000 + denoised = x - sigma * eps + + denoised_embed = self.Retrojector.embed(denoised) + y0_adain_embed = self.Retrojector.embed(y0_style_pos) + + if transformer_options['y0_style_method'] == "scattersort": + tile_h, tile_w = transformer_options.get('y0_style_tile_height'), transformer_options.get('y0_style_tile_width') + pad = transformer_options.get('y0_style_tile_padding') + if pad is not None and tile_h is not None and tile_w is not None: + + denoised_spatial = rearrange(denoised_embed, "b (h w) c -> b c h w", h=h_len, w=w_len) + y0_adain_spatial = rearrange(y0_adain_embed, "b (h w) c -> b c h w", h=h_len, w=w_len) + + if EO("scattersort_median_LP"): + denoised_spatial_LP = median_blur_2d(denoised_spatial, kernel_size=EO("scattersort_median_LP",7)) + y0_adain_spatial_LP = median_blur_2d(y0_adain_spatial, kernel_size=EO("scattersort_median_LP",7)) + + denoised_spatial_HP = denoised_spatial - denoised_spatial_LP + y0_adain_spatial_HP = y0_adain_spatial - y0_adain_spatial_LP + + denoised_spatial_LP = apply_scattersort_tiled(denoised_spatial_LP, y0_adain_spatial_LP, tile_h, tile_w, pad) + + denoised_spatial = denoised_spatial_LP + denoised_spatial_HP + denoised_embed = rearrange(denoised_spatial, "b c h w -> b (h w) c") + else: + denoised_spatial = apply_scattersort_tiled(denoised_spatial, y0_adain_spatial, tile_h, tile_w, pad) + + denoised_embed = rearrange(denoised_spatial, "b c h w -> b (h w) c") + + else: + denoised_embed = apply_scattersort_masked(denoised_embed, y0_adain_embed, y0_style_pos_mask, y0_style_pos_mask_edge, h_len, w_len) + + + + elif transformer_options['y0_style_method'] == "AdaIN": + if freqsep_mask is not None: + freqsep_mask = freqsep_mask.view(1, 1, *freqsep_mask.shape[-2:]).float() + freqsep_mask = F.interpolate(freqsep_mask.float(), size=(h_len, w_len), mode='nearest-exact') + + if hasattr(self, "adain_tile"): + tile_h, tile_w = self.adain_tile + + denoised_pretile = rearrange(denoised_embed, "b (h w) c -> b c h w", h=h_len, w=w_len) + y0_adain_pretile = rearrange(y0_adain_embed, "b (h w) c -> b c h w", h=h_len, w=w_len) + + if self.adain_flag: + h_off = tile_h // 2 + w_off = tile_w // 2 + denoised_pretile = denoised_pretile[:,:,h_off:-h_off, w_off:-w_off] + self.adain_flag = False + else: + h_off = 0 + w_off = 0 + self.adain_flag = True + + tiles, orig_shape, grid, strides = tile_latent(denoised_pretile, tile_size=(tile_h,tile_w)) + y0_tiles, orig_shape, grid, strides = tile_latent(y0_adain_pretile, tile_size=(tile_h,tile_w)) + + tiles_out = [] + for i in range(tiles.shape[0]): + tile = tiles[i].unsqueeze(0) + y0_tile = y0_tiles[i].unsqueeze(0) + + tile = rearrange(tile, "b c h w -> b (h w) c", h=tile_h, w=tile_w) + y0_tile = rearrange(y0_tile, "b c h w -> b (h w) c", h=tile_h, w=tile_w) + + tile = adain_seq_inplace(tile, y0_tile) + tiles_out.append(rearrange(tile, "b (h w) c -> b c h w", h=tile_h, w=tile_w)) + + tiles_out_tensor = torch.cat(tiles_out, dim=0) + tiles_out_tensor = untile_latent(tiles_out_tensor, orig_shape, grid, strides) + + if h_off == 0: + denoised_pretile = tiles_out_tensor + else: + denoised_pretile[:,:,h_off:-h_off, w_off:-w_off] = tiles_out_tensor + denoised_embed = rearrange(denoised_pretile, "b c h w -> b (h w) c", h=h_len, w=w_len) + + elif freqsep_lowpass_method is not None and freqsep_lowpass_method.endswith("pw"): #EO("adain_pw"): + + denoised_spatial = rearrange(denoised_embed, "b (h w) c -> b c h w", h=h_len, w=w_len) + y0_adain_spatial = rearrange(y0_adain_embed, "b (h w) c -> b c h w", h=h_len, w=w_len) + + if freqsep_lowpass_method == "median_pw": + denoised_spatial_new = adain_patchwise_row_batch_med(denoised_spatial.clone(), y0_adain_spatial.clone().repeat(denoised_spatial.shape[0],1,1,1), sigma=freqsep_sigma, kernel_size=freqsep_kernel_size, use_median_blur=True, lowpass_weight=freqsep_lowpass_weight, highpass_weight=freqsep_highpass_weight) + elif freqsep_lowpass_method == "gaussian_pw": + denoised_spatial_new = adain_patchwise_row_batch(denoised_spatial.clone(), y0_adain_spatial.clone().repeat(denoised_spatial.shape[0],1,1,1), sigma=freqsep_sigma, kernel_size=freqsep_kernel_size) + + denoised_embed = rearrange(denoised_spatial_new, "b c h w -> b (h w) c", h=h_len, w=w_len) + + elif freqsep_lowpass_method is not None: + denoised_spatial = rearrange(denoised_embed, "b (h w) c -> b c h w", h=h_len, w=w_len) + y0_adain_spatial = rearrange(y0_adain_embed, "b (h w) c -> b c h w", h=h_len, w=w_len) + + if freqsep_lowpass_method == "median": + denoised_spatial_LP = median_blur_2d(denoised_spatial, kernel_size=freqsep_kernel_size) + y0_adain_spatial_LP = median_blur_2d(y0_adain_spatial, kernel_size=freqsep_kernel_size) + elif freqsep_lowpass_method == "gaussian": + denoised_spatial_LP = gaussian_blur_2d(denoised_spatial, sigma=freqsep_sigma, kernel_size=freqsep_kernel_size) + y0_adain_spatial_LP = gaussian_blur_2d(y0_adain_spatial, sigma=freqsep_sigma, kernel_size=freqsep_kernel_size) + + denoised_spatial_HP = denoised_spatial - denoised_spatial_LP + + if EO("adain_fs_uhp"): + y0_adain_spatial_HP = y0_adain_spatial - y0_adain_spatial_LP + + denoised_spatial_ULP = gaussian_blur_2d(denoised_spatial, sigma=EO("adain_fs_uhp_sigma", 1.0), kernel_size=EO("adain_fs_uhp_kernel_size", 3)) + y0_adain_spatial_ULP = gaussian_blur_2d(y0_adain_spatial, sigma=EO("adain_fs_uhp_sigma", 1.0), kernel_size=EO("adain_fs_uhp_kernel_size", 3)) + + denoised_spatial_UHP = denoised_spatial_HP - denoised_spatial_ULP + y0_adain_spatial_UHP = y0_adain_spatial_HP - y0_adain_spatial_ULP + + #denoised_spatial_HP = y0_adain_spatial_ULP + denoised_spatial_UHP + denoised_spatial_HP = denoised_spatial_ULP + y0_adain_spatial_UHP + + denoised_spatial_new = freqsep_lowpass_weight * y0_adain_spatial_LP + freqsep_highpass_weight * denoised_spatial_HP + denoised_embed = rearrange(denoised_spatial_new, "b c h w -> b (h w) c", h=h_len, w=w_len) + + else: + denoised_embed = adain_seq_inplace(denoised_embed, y0_adain_embed) + + for adain_iter in range(EO("style_iter", 0)): + denoised_embed = adain_seq_inplace(denoised_embed, y0_adain_embed) + denoised_embed = self.Retrojector.embed(self.Retrojector.unembed(denoised_embed)) + denoised_embed = adain_seq_inplace(denoised_embed, y0_adain_embed) + + elif transformer_options['y0_style_method'] == "WCT": + self.StyleWCT.set(y0_adain_embed) + denoised_embed = self.StyleWCT.get(denoised_embed) + + if transformer_options.get('y0_standard_guide') is not None: + y0_standard_guide = transformer_options.get('y0_standard_guide') + + y0_standard_guide_embed = self.Retrojector.embed(y0_standard_guide) + f_cs = self.StyleWCT.get(y0_standard_guide_embed) + self.y0_standard_guide = self.Retrojector.unembed(f_cs) + + if transformer_options.get('y0_inv_standard_guide') is not None: + y0_inv_standard_guide = transformer_options.get('y0_inv_standard_guide') + + y0_inv_standard_guide_embed = self.Retrojector.embed(y0_inv_standard_guide) + f_cs = self.StyleWCT.get(y0_inv_standard_guide_embed) + self.y0_inv_standard_guide = self.Retrojector.unembed(f_cs) + + denoised_approx = self.Retrojector.unembed(denoised_embed) + + eps = (x - denoised_approx) / sigma + + if not UNCOND: + if eps.shape[0] == 2: + eps[1] = eps_orig[1] + y0_style_pos_weight * (eps[1] - eps_orig[1]) + eps[0] = eps_orig[0] + y0_style_pos_synweight * (eps[0] - eps_orig[0]) + else: + eps[0] = eps_orig[0] + y0_style_pos_weight * (eps[0] - eps_orig[0]) + elif eps.shape[0] == 1 and UNCOND: + eps[0] = eps_orig[0] + y0_style_pos_synweight * (eps[0] - eps_orig[0]) + + eps = eps.float() + + if y0_style_neg is not None: + y0_style_neg_weight = transformer_options.get("y0_style_neg_weight") + y0_style_neg_synweight = transformer_options.get("y0_style_neg_synweight") + y0_style_neg_synweight *= y0_style_neg_weight + y0_style_neg_mask = transformer_options.get("y0_style_neg_mask") + y0_style_neg_mask_edge = transformer_options.get("y0_style_neg_mask_edge") + + y0_style_neg = y0_style_neg.to(dtype) + x = x_orig.clone().to(dtype) + eps = eps.to(dtype) + eps_orig = eps.clone() + + sigma = SIGMA #t_orig[0].to(torch.float32) / 1000 + denoised = x - sigma * eps + + denoised_embed = self.Retrojector.embed(denoised) + y0_adain_embed = self.Retrojector.embed(y0_style_neg) + + if transformer_options['y0_style_method'] == "scattersort": + tile_h, tile_w = transformer_options.get('y0_style_tile_height'), transformer_options.get('y0_style_tile_width') + pad = transformer_options.get('y0_style_tile_padding') + if pad is not None and tile_h is not None and tile_w is not None: + + denoised_spatial = rearrange(denoised_embed, "b (h w) c -> b c h w", h=h_len, w=w_len) + y0_adain_spatial = rearrange(y0_adain_embed, "b (h w) c -> b c h w", h=h_len, w=w_len) + + denoised_spatial = apply_scattersort_tiled(denoised_spatial, y0_adain_spatial, tile_h, tile_w, pad) + + denoised_embed = rearrange(denoised_spatial, "b c h w -> b (h w) c") + + else: + denoised_embed = apply_scattersort_masked(denoised_embed, y0_adain_embed, y0_style_neg_mask, y0_style_neg_mask_edge, h_len, w_len) + + + elif transformer_options['y0_style_method'] == "AdaIN": + denoised_embed = adain_seq_inplace(denoised_embed, y0_adain_embed) + for adain_iter in range(EO("style_iter", 0)): + denoised_embed = adain_seq_inplace(denoised_embed, y0_adain_embed) + denoised_embed = self.Retrojector.embed(self.Retrojector.unembed(denoised_embed)) + denoised_embed = adain_seq_inplace(denoised_embed, y0_adain_embed) + + elif transformer_options['y0_style_method'] == "WCT": + self.StyleWCT.set(y0_adain_embed) + denoised_embed = self.StyleWCT.get(denoised_embed) + + denoised_approx = self.Retrojector.unembed(denoised_embed) + + if UNCOND: + eps = (x - denoised_approx) / sigma + eps[0] = eps_orig[0] + y0_style_neg_weight * (eps[0] - eps_orig[0]) + if eps.shape[0] == 2: + eps[1] = eps_orig[1] + y0_style_neg_synweight * (eps[1] - eps_orig[1]) + elif eps.shape[0] == 1 and not UNCOND: + eps[0] = eps_orig[0] + y0_style_neg_synweight * (eps[0] - eps_orig[0]) + + eps = eps.float() + + return eps + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + dtype = eps.dtype if self.style_dtype is None else self.style_dtype + pinv_dtype = torch.float32 if dtype != torch.float64 else dtype + W_inv = None + + + #if eps.shape[0] == 2 or (eps.shape[0] == 1): #: and not UNCOND): + if y0_style_pos is not None and y0_style_pos_weight != 0.0: + y0_style_pos = y0_style_pos.to(dtype) + x = x.to(dtype) + eps = eps.to(dtype) + eps_orig = eps.clone() + + sigma = SIGMA #t_orig[0].to(torch.float32) / 1000 + denoised = x - sigma * eps + + img = comfy.ldm.common_dit.pad_to_patch_size(denoised, (self.patch_size, self.patch_size)) + + img = rearrange(img, "b c (h ph) (w pw) -> b (h w) (c ph pw)", ph=patch_size, pw=patch_size) # img 1,9216,64 1,16,128,128 -> 1,4096,64 + + img_y0_adain = comfy.ldm.common_dit.pad_to_patch_size(y0_style_pos, (self.patch_size, self.patch_size)) + + img_y0_adain = rearrange(img_y0_adain, "b c (h ph) (w pw) -> b (h w) (c ph pw)", ph=patch_size, pw=patch_size) # img 1,9216,64 1,16,128,128 -> 1,4096,64 + + W = self.img_in.weight.data.to(dtype) # shape [2560, 64] + b = self.img_in.bias.data.to(dtype) # shape [2560] + + denoised_embed = F.linear(img .to(W), W, b).to(img) + y0_adain_embed = F.linear(img_y0_adain.to(W), W, b).to(img_y0_adain) + + if transformer_options['y0_style_method'] == "AdaIN": + if freqsep_mask is not None: + freqsep_mask = freqsep_mask.view(1, 1, *freqsep_mask.shape[-2:]).float() + freqsep_mask = F.interpolate(freqsep_mask.float(), size=(h_len, w_len), mode='nearest-exact') + + if freqsep_lowpass_method is not None and freqsep_lowpass_method.endswith("pw"): #EO("adain_pw"): + + #if self.y0_adain_embed is None or self.y0_adain_embed.shape != y0_adain_embed.shape or torch.norm(self.y0_adain_embed - y0_adain_embed) > 0: + # self.y0_adain_embed = y0_adain_embed + # self.adain_pw_cache = None + + denoised_spatial = rearrange(denoised_embed, "b (h w) c -> b c h w", h=h_len, w=w_len) + y0_adain_spatial = rearrange(y0_adain_embed, "b (h w) c -> b c h w", h=h_len, w=w_len) + + if freqsep_lowpass_method == "median_alt": + denoised_spatial_new = adain_patchwise_row_batch_medblur(denoised_spatial.clone(), y0_adain_spatial.clone().repeat(denoised_spatial.shape[0],1,1,1), sigma=freqsep_sigma, kernel_size=freqsep_kernel_size, use_median_blur=True) + elif freqsep_lowpass_method == "median_pw": + denoised_spatial_new = adain_patchwise_row_batch_realmedblur(denoised_spatial.clone(), y0_adain_spatial.clone().repeat(denoised_spatial.shape[0],1,1,1), sigma=freqsep_sigma, kernel_size=freqsep_kernel_size, use_median_blur=True, lowpass_weight=freqsep_lowpass_weight, highpass_weight=freqsep_highpass_weight) + elif freqsep_lowpass_method == "gaussian_pw": + denoised_spatial_new = adain_patchwise_row_batch(denoised_spatial.clone(), y0_adain_spatial.clone().repeat(denoised_spatial.shape[0],1,1,1), sigma=freqsep_sigma, kernel_size=freqsep_kernel_size) + + denoised_embed = rearrange(denoised_spatial_new, "b c h w -> b (h w) c", h=h_len, w=w_len) + + elif freqsep_lowpass_method is not None and freqsep_lowpass_method == "distribution": + denoised_spatial = rearrange(denoised_embed, "b (h w) c -> b c h w", h=h_len, w=w_len) + y0_adain_spatial = rearrange(y0_adain_embed, "b (h w) c -> b c h w", h=h_len, w=w_len) + + denoised_spatial_new = adain_patchwise_strict_sortmatch9(denoised_spatial.clone(), y0_adain_spatial.clone().repeat(denoised_spatial.shape[0],1,1,1), kernel_size=freqsep_kernel_size, inner_kernel_size=freqsep_inner_kernel_size, mask=freqsep_mask, stride=freqsep_stride) + + denoised_embed = rearrange(denoised_spatial_new, "b c h w -> b (h w) c", h=h_len, w=w_len) + + elif freqsep_lowpass_method is not None: + denoised_spatial = rearrange(denoised_embed, "b (h w) c -> b c h w", h=h_len, w=w_len) + y0_adain_spatial = rearrange(y0_adain_embed, "b (h w) c -> b c h w", h=h_len, w=w_len) + + if freqsep_lowpass_method == "median": + denoised_spatial_LP = median_blur_2d(denoised_spatial, kernel_size=freqsep_kernel_size) + y0_adain_spatial_LP = median_blur_2d(y0_adain_spatial, kernel_size=freqsep_kernel_size) + elif freqsep_lowpass_method == "gaussian": + denoised_spatial_LP = gaussian_blur_2d(denoised_spatial, sigma=freqsep_sigma, kernel_size=freqsep_kernel_size) + y0_adain_spatial_LP = gaussian_blur_2d(y0_adain_spatial, sigma=freqsep_sigma, kernel_size=freqsep_kernel_size) + + denoised_spatial_HP = denoised_spatial - denoised_spatial_LP + + if EO("adain_fs_uhp"): + y0_adain_spatial_HP = y0_adain_spatial - y0_adain_spatial_LP + + denoised_spatial_ULP = gaussian_blur_2d(denoised_spatial, sigma=EO("adain_fs_uhp_sigma", 1.0), kernel_size=EO("adain_fs_uhp_kernel_size", 3)) + y0_adain_spatial_ULP = gaussian_blur_2d(y0_adain_spatial, sigma=EO("adain_fs_uhp_sigma", 1.0), kernel_size=EO("adain_fs_uhp_kernel_size", 3)) + + denoised_spatial_UHP = denoised_spatial_HP - denoised_spatial_ULP + y0_adain_spatial_UHP = y0_adain_spatial_HP - y0_adain_spatial_ULP + + #denoised_spatial_HP = y0_adain_spatial_ULP + denoised_spatial_UHP + denoised_spatial_HP = denoised_spatial_ULP + y0_adain_spatial_UHP + + denoised_spatial_new = freqsep_lowpass_weight * y0_adain_spatial_LP + freqsep_highpass_weight * denoised_spatial_HP + denoised_embed = rearrange(denoised_spatial_new, "b c h w -> b (h w) c", h=h_len, w=w_len) + + else: + denoised_embed = adain_seq_inplace(denoised_embed, y0_adain_embed) + + #denoised_embed = adain_seq_inplace(denoised_embed, y0_adain_embed) + #for adain_iter in range(EO("style_iter", 0)): + # denoised_embed = adain_seq_inplace(denoised_embed, y0_adain_embed) + # denoised_embed = (denoised_embed - b) @ torch.linalg.pinv(W.to(pinv_dtype)).T.to(dtype) + # denoised_embed = F.linear(denoised_embed .to(W), W, b).to(img) + # denoised_embed = adain_seq_inplace(denoised_embed, y0_adain_embed) + + elif transformer_options['y0_style_method'] == "WCT": + if self.y0_adain_embed is None or self.y0_adain_embed.shape != y0_adain_embed.shape or torch.norm(self.y0_adain_embed - y0_adain_embed) > 0: + self.y0_adain_embed = y0_adain_embed + + f_s = y0_adain_embed[0].clone() + self.mu_s = f_s.mean(dim=0, keepdim=True) + f_s_centered = f_s - self.mu_s + + cov = (f_s_centered.T.double() @ f_s_centered.double()) / (f_s_centered.size(0) - 1) + + S_eig, U_eig = torch.linalg.eigh(cov + 1e-5 * torch.eye(cov.size(0), dtype=cov.dtype, device=cov.device)) + S_eig_sqrt = S_eig.clamp(min=0).sqrt() # eigenvalues -> singular values + + whiten = U_eig @ torch.diag(S_eig_sqrt) @ U_eig.T + self.y0_color = whiten.to(f_s_centered) + + for wct_i in range(eps.shape[0]): + f_c = denoised_embed[wct_i].clone() + mu_c = f_c.mean(dim=0, keepdim=True) + f_c_centered = f_c - mu_c + + cov = (f_c_centered.T.double() @ f_c_centered.double()) / (f_c_centered.size(0) - 1) + + S_eig, U_eig = torch.linalg.eigh(cov + 1e-5 * torch.eye(cov.size(0), dtype=cov.dtype, device=cov.device)) + inv_sqrt_eig = S_eig.clamp(min=0).rsqrt() + + whiten = U_eig @ torch.diag(inv_sqrt_eig) @ U_eig.T + whiten = whiten.to(f_c_centered) + + f_c_whitened = f_c_centered @ whiten.T + f_cs = f_c_whitened @ self.y0_color.T + self.mu_s + + denoised_embed[wct_i] = f_cs + + + denoised_approx = (denoised_embed - b.to(denoised_embed)) @ torch.linalg.pinv(W).T.to(denoised_embed) + denoised_approx = denoised_approx.to(eps) + + denoised_approx = rearrange(denoised_approx, "b (h w) (c ph pw) -> b c (h ph) (w pw)", h=h_len, w=w_len, ph=2, pw=2)[:,:,:h,:w] + + eps = (x - denoised_approx) / sigma + + if not UNCOND: + if eps.shape[0] == 2: + eps[1] = eps_orig[1] + y0_style_pos_weight * (eps[1] - eps_orig[1]) + eps[0] = eps_orig[0] + y0_style_pos_synweight * (eps[0] - eps_orig[0]) + else: + eps[0] = eps_orig[0] + y0_style_pos_weight * (eps[0] - eps_orig[0]) + elif eps.shape[0] == 1 and UNCOND: + eps[0] = eps_orig[0] + y0_style_pos_synweight * (eps[0] - eps_orig[0]) + #if eps.shape[0] == 2: + # eps[1] = eps_orig[1] + y0_style_neg_synweight * (eps[1] - eps_orig[1]) + + eps = eps.float() + + #if eps.shape[0] == 2 or (eps.shape[0] == 1): # and UNCOND): + if y0_style_neg is not None and y0_style_neg_weight != 0.0: + y0_style_neg = y0_style_neg.to(dtype) + x = x.to(dtype) + eps = eps.to(dtype) + eps_orig = eps.clone() + + sigma = SIGMA #t_orig[0].to(torch.float32) / 1000 + denoised = x - sigma * eps + + img = comfy.ldm.common_dit.pad_to_patch_size(denoised, (self.patch_size, self.patch_size)) + + h_len = ((h + (patch_size // 2)) // patch_size) # h_len 96 + w_len = ((w + (patch_size // 2)) // patch_size) # w_len 96 + img = rearrange(img, "b c (h ph) (w pw) -> b (h w) (c ph pw)", ph=patch_size, pw=patch_size) # img 1,9216,64 1,16,128,128 -> 1,4096,64 + + img_y0_adain = comfy.ldm.common_dit.pad_to_patch_size(y0_style_neg, (self.patch_size, self.patch_size)) + + img_y0_adain = rearrange(img_y0_adain, "b c (h ph) (w pw) -> b (h w) (c ph pw)", ph=patch_size, pw=patch_size) # img 1,9216,64 1,16,128,128 -> 1,4096,64 + + W = self.img_in.weight.data.to(dtype) # shape [2560, 64] + b = self.img_in.bias.data.to(dtype) # shape [2560] + + denoised_embed = F.linear(img .to(W), W, b).to(img) + y0_adain_embed = F.linear(img_y0_adain.to(W), W, b).to(img_y0_adain) + + if transformer_options['y0_style_method'] == "AdaIN": + if freqsep_mask is not None: + freqsep_mask = freqsep_mask.view(1, 1, *freqsep_mask.shape[-2:]).float() + freqsep_mask = F.interpolate(freqsep_mask.float(), size=(h_len, w_len), mode='nearest-exact') + + if freqsep_lowpass_method is not None and freqsep_lowpass_method.endswith("pw"): #EO("adain_pw"): + + #if self.y0_adain_embed is None or self.y0_adain_embed.shape != y0_adain_embed.shape or torch.norm(self.y0_adain_embed - y0_adain_embed) > 0: + # self.y0_adain_embed = y0_adain_embed + # self.adain_pw_cache = None + + denoised_spatial = rearrange(denoised_embed, "b (h w) c -> b c h w", h=h_len, w=w_len) + y0_adain_spatial = rearrange(y0_adain_embed, "b (h w) c -> b c h w", h=h_len, w=w_len) + + if freqsep_lowpass_method == "median_alt": + denoised_spatial_new = adain_patchwise_row_batch_medblur(denoised_spatial.clone(), y0_adain_spatial.clone(), sigma=freqsep_sigma, kernel_size=freqsep_kernel_size, use_median_blur=True) + elif freqsep_lowpass_method == "median_pw": + denoised_spatial_new = adain_patchwise_row_batch_realmedblur(denoised_spatial.clone(), y0_adain_spatial.clone(), sigma=freqsep_sigma, kernel_size=freqsep_kernel_size, use_median_blur=True, lowpass_weight=freqsep_lowpass_weight, highpass_weight=freqsep_highpass_weight) + elif freqsep_lowpass_method == "gaussian_pw": + denoised_spatial_new = adain_patchwise_row_batch(denoised_spatial.clone(), y0_adain_spatial.clone(), sigma=freqsep_sigma, kernel_size=freqsep_kernel_size) + + denoised_embed = rearrange(denoised_spatial_new, "b c h w -> b (h w) c", h=h_len, w=w_len) + + elif freqsep_lowpass_method is not None and freqsep_lowpass_method == "distribution": + denoised_spatial = rearrange(denoised_embed, "b (h w) c -> b c h w", h=h_len, w=w_len) + y0_adain_spatial = rearrange(y0_adain_embed, "b (h w) c -> b c h w", h=h_len, w=w_len) + + denoised_spatial_new = adain_patchwise_strict_sortmatch9(denoised_spatial.clone(), y0_adain_spatial.clone(), kernel_size=freqsep_kernel_size, inner_kernel_size=freqsep_inner_kernel_size, mask=freqsep_mask, stride=freqsep_stride) + + denoised_embed = rearrange(denoised_spatial_new, "b c h w -> b (h w) c", h=h_len, w=w_len) + + elif freqsep_lowpass_method is not None: + denoised_spatial = rearrange(denoised_embed, "b (h w) c -> b c h w", h=h_len, w=w_len) + y0_adain_spatial = rearrange(y0_adain_embed, "b (h w) c -> b c h w", h=h_len, w=w_len) + + if freqsep_lowpass_method == "median": + denoised_spatial_LP = median_blur_2d(denoised_spatial, kernel_size=freqsep_kernel_size) + y0_adain_spatial_LP = median_blur_2d(y0_adain_spatial, kernel_size=freqsep_kernel_size) + elif freqsep_lowpass_method == "gaussian": + denoised_spatial_LP = gaussian_blur_2d(denoised_spatial, sigma=freqsep_sigma, kernel_size=freqsep_kernel_size) + y0_adain_spatial_LP = gaussian_blur_2d(y0_adain_spatial, sigma=freqsep_sigma, kernel_size=freqsep_kernel_size) + + denoised_spatial_HP = denoised_spatial - denoised_spatial_LP + + if EO("adain_fs_uhp"): + y0_adain_spatial_HP = y0_adain_spatial - y0_adain_spatial_LP + + denoised_spatial_ULP = gaussian_blur_2d(denoised_spatial, sigma=EO("adain_fs_uhp_sigma", 1.0), kernel_size=EO("adain_fs_uhp_kernel_size", 3)) + y0_adain_spatial_ULP = gaussian_blur_2d(y0_adain_spatial, sigma=EO("adain_fs_uhp_sigma", 1.0), kernel_size=EO("adain_fs_uhp_kernel_size", 3)) + + denoised_spatial_UHP = denoised_spatial_HP - denoised_spatial_ULP + y0_adain_spatial_UHP = y0_adain_spatial_HP - y0_adain_spatial_ULP + + #denoised_spatial_HP = y0_adain_spatial_ULP + denoised_spatial_UHP + denoised_spatial_HP = denoised_spatial_ULP + y0_adain_spatial_UHP + + denoised_spatial_new = freqsep_lowpass_weight * y0_adain_spatial_LP + freqsep_highpass_weight * denoised_spatial_HP + denoised_embed = rearrange(denoised_spatial_new, "b c h w -> b (h w) c", h=h_len, w=w_len) + + else: + denoised_embed = adain_seq_inplace(denoised_embed, y0_adain_embed) + + #denoised_embed = adain_seq_inplace(denoised_embed, y0_adain_embed) + #for adain_iter in range(EO("style_iter", 0)): + # denoised_embed = adain_seq_inplace(denoised_embed, y0_adain_embed) + # denoised_embed = (denoised_embed - b) @ torch.linalg.pinv(W.to(pinv_dtype)).T.to(dtype) + # denoised_embed = F.linear(denoised_embed .to(W), W, b).to(img) + # denoised_embed = adain_seq_inplace(denoised_embed, y0_adain_embed) + + elif transformer_options['y0_style_method'] == "WCT": + if self.y0_adain_embed is None or self.y0_adain_embed.shape != y0_adain_embed.shape or torch.norm(self.y0_adain_embed - y0_adain_embed) > 0: + self.y0_adain_embed = y0_adain_embed + + f_s = y0_adain_embed[0].clone() + self.mu_s = f_s.mean(dim=0, keepdim=True) + f_s_centered = f_s - self.mu_s + + cov = (f_s_centered.T.double() @ f_s_centered.double()) / (f_s_centered.size(0) - 1) + + S_eig, U_eig = torch.linalg.eigh(cov + 1e-5 * torch.eye(cov.size(0), dtype=cov.dtype, device=cov.device)) + S_eig_sqrt = S_eig.clamp(min=0).sqrt() # eigenvalues -> singular values + + whiten = U_eig @ torch.diag(S_eig_sqrt) @ U_eig.T + self.y0_color = whiten.to(f_s_centered) + + for wct_i in range(eps.shape[0]): + f_c = denoised_embed[wct_i].clone() + mu_c = f_c.mean(dim=0, keepdim=True) + f_c_centered = f_c - mu_c + + cov = (f_c_centered.T.double() @ f_c_centered.double()) / (f_c_centered.size(0) - 1) + + S_eig, U_eig = torch.linalg.eigh(cov + 1e-5 * torch.eye(cov.size(0), dtype=cov.dtype, device=cov.device)) + inv_sqrt_eig = S_eig.clamp(min=0).rsqrt() + + whiten = U_eig @ torch.diag(inv_sqrt_eig) @ U_eig.T + whiten = whiten.to(f_c_centered) + + f_c_whitened = f_c_centered @ whiten.T + f_cs = f_c_whitened @ self.y0_color.T + self.mu_s + + denoised_embed[wct_i] = f_cs + + denoised_approx = (denoised_embed - b.to(denoised_embed)) @ torch.linalg.pinv(W).T.to(denoised_embed) + denoised_approx = denoised_approx.to(eps) + + denoised_approx = rearrange(denoised_approx, "b (h w) (c ph pw) -> b c (h ph) (w pw)", h=h_len, w=w_len, ph=2, pw=2)[:,:,:h,:w] + + if UNCOND: + eps = (x - denoised_approx) / sigma + eps[0] = eps_orig[0] + y0_style_neg_weight * (eps[0] - eps_orig[0]) + if eps.shape[0] == 2: + eps[1] = eps_orig[1] + y0_style_neg_synweight * (eps[1] - eps_orig[1]) + elif eps.shape[0] == 1 and not UNCOND: + eps[0] = eps_orig[0] + y0_style_neg_synweight * (eps[0] - eps_orig[0]) + + eps = eps.float() + + return eps + + + +def adain_seq(content: torch.Tensor, style: torch.Tensor, eps: float = 1e-7) -> torch.Tensor: + return ((content - content.mean(1, keepdim=True)) / (content.std(1, keepdim=True) + eps)) * (style.std(1, keepdim=True) + eps) + style.mean(1, keepdim=True) + + + +def adain_seq_inplace(content: torch.Tensor, style: torch.Tensor, eps: float = 1e-7) -> torch.Tensor: + mean_c = content.mean(1, keepdim=True) + std_c = content.std (1, keepdim=True).add_(eps) # in-place add + mean_s = style.mean (1, keepdim=True) + std_s = style.std (1, keepdim=True).add_(eps) + + content.sub_(mean_c).div_(std_c).mul_(std_s).add_(mean_s) # in-place chain + return content + + + + + + + + + +def gaussian_blur_2d(img: torch.Tensor, sigma: float, kernel_size: int = None) -> torch.Tensor: + B, C, H, W = img.shape + dtype = img.dtype + device = img.device + + if kernel_size is None: + kernel_size = int(2 * math.ceil(3 * sigma) + 1) + + if kernel_size % 2 == 0: + kernel_size += 1 + + coords = torch.arange(kernel_size, dtype=torch.float64) - kernel_size // 2 + g = torch.exp(-0.5 * (coords / sigma) ** 2) + g = g / g.sum() + + kernel_2d = g[:, None] * g[None, :] + kernel_2d = kernel_2d.to(dtype=dtype, device=device) + + kernel = kernel_2d.expand(C, 1, kernel_size, kernel_size) + + pad = kernel_size // 2 + img_padded = F.pad(img, (pad, pad, pad, pad), mode='reflect') + + return F.conv2d(img_padded, kernel, groups=C) + + +def median_blur_2d(img: torch.Tensor, kernel_size: int = 3) -> torch.Tensor: + if kernel_size % 2 == 0: + kernel_size += 1 + pad = kernel_size // 2 + + B, C, H, W = img.shape + img_padded = F.pad(img, (pad, pad, pad, pad), mode='reflect') + + unfolded = img_padded.unfold(2, kernel_size, 1).unfold(3, kernel_size, 1) + # unfolded: [B, C, H, W, kH, kW] → flatten to patches + patches = unfolded.contiguous().view(B, C, H, W, -1) + median = patches.median(dim=-1).values + return median + + +def adain_patchwise(content: torch.Tensor, style: torch.Tensor, sigma: float = 1.0, kernel_size: int = None, eps: float = 1e-5) -> torch.Tensor: + + B, C, H, W = content.shape + device = content.device + dtype = content.dtype + + if kernel_size is None: + kernel_size = int(2 * math.ceil(3 * sigma) + 1) + if kernel_size % 2 == 0: + kernel_size += 1 + + pad = kernel_size // 2 + coords = torch.arange(kernel_size, dtype=torch.float64, device=device) - pad + gauss = torch.exp(-0.5 * (coords / sigma) ** 2) + gauss /= gauss.sum() + kernel_2d = (gauss[:, None] * gauss[None, :]).to(dtype=dtype) + + weight = kernel_2d.view(1, 1, kernel_size, kernel_size) + + content_padded = F.pad(content, (pad, pad, pad, pad), mode='reflect') + style_padded = F.pad(style, (pad, pad, pad, pad), mode='reflect') + result = torch.zeros_like(content) + + for i in range(H): + for j in range(W): + c_patch = content_padded[:, :, i:i + kernel_size, j:j + kernel_size] + s_patch = style_padded[:, :, i:i + kernel_size, j:j + kernel_size] + w = weight.expand_as(c_patch) + + c_mean = (c_patch * w).sum(dim=(-1, -2), keepdim=True) + c_std = ((c_patch - c_mean)**2 * w).sum(dim=(-1, -2), keepdim=True).sqrt() + eps + s_mean = (s_patch * w).sum(dim=(-1, -2), keepdim=True) + s_std = ((s_patch - s_mean)**2 * w).sum(dim=(-1, -2), keepdim=True).sqrt() + eps + + normed = (c_patch[:, :, pad:pad+1, pad:pad+1] - c_mean) / c_std + stylized = normed * s_std + s_mean + result[:, :, i, j] = stylized.squeeze(-1).squeeze(-1) + + return result + + + + +def adain_patchwise_row_batch(content: torch.Tensor, style: torch.Tensor, sigma: float = 1.0, kernel_size: int = None, eps: float = 1e-5) -> torch.Tensor: + + B, C, H, W = content.shape + device, dtype = content.device, content.dtype + + if kernel_size is None: + kernel_size = int(2 * math.ceil(3 * sigma) + 1) + if kernel_size % 2 == 0: + kernel_size += 1 + + pad = kernel_size // 2 + coords = torch.arange(kernel_size, dtype=torch.float64, device=device) - pad + gauss = torch.exp(-0.5 * (coords / sigma) ** 2) + gauss = (gauss / gauss.sum()).to(dtype) + kernel_2d = (gauss[:, None] * gauss[None, :]) + + weight = kernel_2d.view(1, 1, kernel_size, kernel_size) + + content_padded = F.pad(content, (pad, pad, pad, pad), mode='reflect') + style_padded = F.pad(style, (pad, pad, pad, pad), mode='reflect') + result = torch.zeros_like(content) + + for i in range(H): + c_row_patches = torch.stack([ + content_padded[:, :, i:i+kernel_size, j:j+kernel_size] + for j in range(W) + ], dim=0) # [W, B, C, k, k] + + s_row_patches = torch.stack([ + style_padded[:, :, i:i+kernel_size, j:j+kernel_size] + for j in range(W) + ], dim=0) + + w = weight.expand_as(c_row_patches[0]) + + c_mean = (c_row_patches * w).sum(dim=(-1, -2), keepdim=True) + c_std = ((c_row_patches - c_mean) ** 2 * w).sum(dim=(-1, -2), keepdim=True).sqrt() + eps + s_mean = (s_row_patches * w).sum(dim=(-1, -2), keepdim=True) + s_std = ((s_row_patches - s_mean) ** 2 * w).sum(dim=(-1, -2), keepdim=True).sqrt() + eps + + center = kernel_size // 2 + central = c_row_patches[:, :, :, center:center+1, center:center+1] + normed = (central - c_mean) / c_std + stylized = normed * s_std + s_mean + + result[:, :, i, :] = stylized.squeeze(-1).squeeze(-1).permute(1, 2, 0) # [B,C,W] + + return result + + + + + + + +def adain_patchwise_row_batch_medblur(content: torch.Tensor, style: torch.Tensor, sigma: float = 1.0, kernel_size: int = None, eps: float = 1e-5, mask: torch.Tensor = None, use_median_blur: bool = False) -> torch.Tensor: + B, C, H, W = content.shape + device, dtype = content.device, content.dtype + + if kernel_size is None: + kernel_size = int(2 * math.ceil(3 * abs(sigma)) + 1) + if kernel_size % 2 == 0: + kernel_size += 1 + + pad = kernel_size // 2 + + content_padded = F.pad(content, (pad, pad, pad, pad), mode='reflect') + style_padded = F.pad(style, (pad, pad, pad, pad), mode='reflect') + result = torch.zeros_like(content) + + scaling = torch.ones((B, 1, H, W), device=device, dtype=dtype) + sigma_scale = torch.ones((H, W), device=device, dtype=torch.float32) + if mask is not None: + with torch.no_grad(): + padded_mask = F.pad(mask.float(), (pad, pad, pad, pad), mode="reflect") + blurred_mask = F.avg_pool2d(padded_mask, kernel_size=kernel_size, stride=1, padding=pad) + blurred_mask = blurred_mask[..., pad:-pad, pad:-pad] + edge_proximity = blurred_mask * (1.0 - blurred_mask) + scaling = 1.0 - (edge_proximity / 0.25).clamp(0.0, 1.0) + sigma_scale = scaling[0, 0] # assuming single-channel mask broadcasted across B, C + + if not use_median_blur: + coords = torch.arange(kernel_size, dtype=torch.float64, device=device) - pad + base_gauss = torch.exp(-0.5 * (coords / sigma) ** 2) + base_gauss = (base_gauss / base_gauss.sum()).to(dtype) + gaussian_table = {} + for s in sigma_scale.unique(): + sig = float((sigma * s + eps).clamp(min=1e-3)) + gauss_local = torch.exp(-0.5 * (coords / sig) ** 2) + gauss_local = (gauss_local / gauss_local.sum()).to(dtype) + kernel_2d = gauss_local[:, None] * gauss_local[None, :] + gaussian_table[s.item()] = kernel_2d + + for i in range(H): + row_result = torch.zeros(B, C, W, dtype=dtype, device=device) + for j in range(W): + c_patch = content_padded[:, :, i:i+kernel_size, j:j+kernel_size] + s_patch = style_padded[:, :, i:i+kernel_size, j:j+kernel_size] + + if use_median_blur: + c_flat = c_patch.reshape(B, C, -1) + s_flat = s_patch.reshape(B, C, -1) + + c_median = c_flat.median(dim=-1, keepdim=True).values + s_median = s_flat.median(dim=-1, keepdim=True).values + + c_std = (c_flat - c_median).abs().mean(dim=-1, keepdim=True) + eps + s_std = (s_flat - s_median).abs().mean(dim=-1, keepdim=True) + eps + + center = kernel_size // 2 + central = c_patch[:, :, center, center].unsqueeze(-1) + + normed = (central - c_median) / c_std + stylized = normed * s_std + s_median + else: + k = gaussian_table[float(sigma_scale[i, j].item())] + local_weight = k.view(1, 1, kernel_size, kernel_size).expand(B, C, kernel_size, kernel_size) + + c_mean = (c_patch * local_weight).sum(dim=(-1, -2), keepdim=True) + c_std = ((c_patch - c_mean) ** 2 * local_weight).sum(dim=(-1, -2), keepdim=True).sqrt() + eps + s_mean = (s_patch * local_weight).sum(dim=(-1, -2), keepdim=True) + s_std = ((s_patch - s_mean) ** 2 * local_weight).sum(dim=(-1, -2), keepdim=True).sqrt() + eps + + center = kernel_size // 2 + central = c_patch[:, :, center:center+1, center:center+1] + normed = (central - c_mean) / c_std + stylized = normed * s_std + s_mean + + local_scaling = scaling[:, :, i, j].view(B, 1, 1, 1) + stylized = central * (1 - local_scaling) + stylized * local_scaling + + row_result[:, :, j] = stylized.squeeze(-1).squeeze(-1) + result[:, :, i, :] = row_result + + return result + + + + + + + +def adain_patchwise_row_batch_realmedblur(content: torch.Tensor, style: torch.Tensor, sigma: float = 1.0, kernel_size: int = None, eps: float = 1e-5, mask: torch.Tensor = None, use_median_blur: bool = False, lowpass_weight=1.0, highpass_weight=1.0) -> torch.Tensor: + B, C, H, W = content.shape + device, dtype = content.device, content.dtype + + if kernel_size is None: + kernel_size = int(2 * math.ceil(3 * abs(sigma)) + 1) + if kernel_size % 2 == 0: + kernel_size += 1 + + pad = kernel_size // 2 + + content_padded = F.pad(content, (pad, pad, pad, pad), mode='reflect') + style_padded = F.pad(style, (pad, pad, pad, pad), mode='reflect') + result = torch.zeros_like(content) + + scaling = torch.ones((B, 1, H, W), device=device, dtype=dtype) + sigma_scale = torch.ones((H, W), device=device, dtype=torch.float32) + if mask is not None: + with torch.no_grad(): + padded_mask = F.pad(mask.float(), (pad, pad, pad, pad), mode="reflect") + blurred_mask = F.avg_pool2d(padded_mask, kernel_size=kernel_size, stride=1, padding=pad) + blurred_mask = blurred_mask[..., pad:-pad, pad:-pad] + edge_proximity = blurred_mask * (1.0 - blurred_mask) + scaling = 1.0 - (edge_proximity / 0.25).clamp(0.0, 1.0) + sigma_scale = scaling[0, 0] # assuming single-channel mask broadcasted across B, C + + if not use_median_blur: + coords = torch.arange(kernel_size, dtype=torch.float64, device=device) - pad + base_gauss = torch.exp(-0.5 * (coords / sigma) ** 2) + base_gauss = (base_gauss / base_gauss.sum()).to(dtype) + gaussian_table = {} + for s in sigma_scale.unique(): + sig = float((sigma * s + eps).clamp(min=1e-3)) + gauss_local = torch.exp(-0.5 * (coords / sig) ** 2) + gauss_local = (gauss_local / gauss_local.sum()).to(dtype) + kernel_2d = gauss_local[:, None] * gauss_local[None, :] + gaussian_table[s.item()] = kernel_2d + + for i in range(H): + row_result = torch.zeros(B, C, W, dtype=dtype, device=device) + for j in range(W): + c_patch = content_padded[:, :, i:i+kernel_size, j:j+kernel_size] + s_patch = style_padded[:, :, i:i+kernel_size, j:j+kernel_size] + + if use_median_blur: + # Median blur with residual restoration + unfolded_c = c_patch.reshape(B, C, -1) + unfolded_s = s_patch.reshape(B, C, -1) + + c_median = unfolded_c.median(dim=-1, keepdim=True).values + s_median = unfolded_s.median(dim=-1, keepdim=True).values + + center = kernel_size // 2 + central = c_patch[:, :, center, center].view(B, C, 1) + residual = central - c_median + stylized = lowpass_weight * s_median + residual * highpass_weight + else: + k = gaussian_table[float(sigma_scale[i, j].item())] + local_weight = k.view(1, 1, kernel_size, kernel_size).expand(B, C, kernel_size, kernel_size) + + c_mean = (c_patch * local_weight).sum(dim=(-1, -2), keepdim=True) + c_std = ((c_patch - c_mean) ** 2 * local_weight).sum(dim=(-1, -2), keepdim=True).sqrt() + eps + s_mean = (s_patch * local_weight).sum(dim=(-1, -2), keepdim=True) + s_std = ((s_patch - s_mean) ** 2 * local_weight).sum(dim=(-1, -2), keepdim=True).sqrt() + eps + + center = kernel_size // 2 + central = c_patch[:, :, center:center+1, center:center+1] + normed = (central - c_mean) / c_std + stylized = normed * s_std + s_mean + + local_scaling = scaling[:, :, i, j].view(B, 1, 1) + stylized = central * (1 - local_scaling) + stylized * local_scaling + + row_result[:, :, j] = stylized.squeeze(-1) + result[:, :, i, :] = row_result + + return result + + + + + + + + + + + + + +def patchwise_sort_transfer9(src: torch.Tensor, ref: torch.Tensor) -> torch.Tensor: + """ + src, ref: [B, C, N] where N = K*K + Returns: [B, C, N] with values from ref permuted to match the sort-order of src. + """ + src_sorted, src_idx = src.sort(dim=-1) + ref_sorted, _ = ref.sort(dim=-1) + out = torch.zeros_like(src) + out.scatter_(dim=-1, index=src_idx, src=ref_sorted) + return out + +def masked_patchwise_sort_transfer9( + src : torch.Tensor, # [B, C, N] + ref : torch.Tensor, # [B, C, N] + mask_flat : torch.Tensor # [B, N] bool +) -> torch.Tensor: + """ + Only rearrange N positions where mask_flat[b] is True... to be implemented fully later. + """ + B,C,N = src.shape + out = src.clone() + for b in range(B): + valid = mask_flat[b] # [N] boolean + if valid.sum() == 0: + continue + sc = src[b,:,valid] # [C, M] + ss = ref[b,:,valid] # [C, M] + sc_s, idx = sc.sort(dim=-1) # sort the channelz + ss_s, _ = ss.sort(dim=-1) + res = torch.zeros_like(sc) + res.scatter_(dim=-1, index=idx, src=ss_s) + out[b,:,valid] = res + return out + +def adain_patchwise_strict_sortmatch9( + content : torch.Tensor, # [B,C,H,W] + style : torch.Tensor, # [B,C,H,W] + kernel_size : int, + inner_kernel_size : int = 1, + stride : int = 1, + mask : torch.Tensor = None # [B,1,H,W] +) -> torch.Tensor: + B,C,H,W = content.shape + assert inner_kernel_size <= kernel_size + pad = kernel_size//2 + inner_off = (kernel_size - inner_kernel_size)//2 + + # reflect-pad + cp = F.pad(content, (pad,)*4, mode='reflect') + sp = F.pad(style, (pad,)*4, mode='reflect') + out = content.clone() + + if mask is not None: + mask = mask[:,0].bool() # [B,H,W] + + for i in range(0, H, stride): + for j in range(0, W, stride): + pc = cp[:, :, i:i+kernel_size, j:j+kernel_size] # [B,C,K,K] + ps = sp[:, :, i:i+kernel_size, j:j+kernel_size] + + Bc = pc.reshape(B, C, -1) + Bs = ps.reshape(B, C, -1) + + matched_flat = patchwise_sort_transfer9(Bc, Bs) + matched = matched_flat.view(B, C, kernel_size, kernel_size) + + y0, x0 = inner_off, inner_off + y1, x1 = y0 + inner_kernel_size, x0 + inner_kernel_size + inner = matched[:, :, y0:y1, x0:x1] # [B,C,inner,inner] + + dst_y0 = i + y0 - pad + dst_x0 = j + x0 - pad + dst_y1 = dst_y0 + inner_kernel_size + dst_x1 = dst_x0 + inner_kernel_size + + oy0 = max(dst_y0, 0); ox0 = max(dst_x0, 0) + oy1 = min(dst_y1, H); ox1 = min(dst_x1, W) + + iy0 = oy0 - dst_y0; ix0 = ox0 - dst_x0 + iy1 = iy0 + (oy1 - oy0); ix1 = ix0 + (ox1 - ox0) + + if mask is None: + out[:, :, oy0:oy1, ox0:ox1] = inner[:, :, iy0:iy1, ix0:ix1] + else: + ibm = mask[:, oy0:oy1, ox0:ox1] # [B,inner,inner] + for b in range(B): + sel = ibm[b] # [inner,inner] # w/ regard to kernel + if sel.any(): + out[b:b+1, :, oy0:oy1, ox0:ox1][:, :,sel] = inner[b:b+1, :, iy0:iy1, ix0:ix1][:, :, sel] + return out + + diff --git a/ComfyUI/custom_nodes/RES4LYF/example_workflows/chroma regional antiblur.json b/ComfyUI/custom_nodes/RES4LYF/example_workflows/chroma regional antiblur.json new file mode 100644 index 0000000000000000000000000000000000000000..1a754a7643ba0d9daffd9046a103e1859629a4a8 --- /dev/null +++ b/ComfyUI/custom_nodes/RES4LYF/example_workflows/chroma regional antiblur.json @@ -0,0 +1 @@ +{"last_node_id":726,"last_link_id":2104,"nodes":[{"id":13,"type":"Reroute","pos":[1280,-650],"size":[75,26],"flags":{},"order":12,"mode":0,"inputs":[{"name":"","type":"*","link":2098}],"outputs":[{"name":"","type":"MODEL","links":[1967],"slot_index":0}],"properties":{"showOutputText":false,"horizontal":false}},{"id":490,"type":"Reroute","pos":[1280,-610],"size":[75,26],"flags":{},"order":9,"mode":0,"inputs":[{"name":"","type":"*","link":2099}],"outputs":[{"name":"","type":"CLIP","links":[1939,2092,2101],"slot_index":0}],"properties":{"showOutputText":false,"horizontal":false}},{"id":14,"type":"Reroute","pos":[1280,-570],"size":[75,26],"flags":{},"order":10,"mode":0,"inputs":[{"name":"","type":"*","link":2100}],"outputs":[{"name":"","type":"VAE","links":[18,1328],"slot_index":0}],"properties":{"showOutputText":false,"horizontal":false}},{"id":398,"type":"SaveImage","pos":[1379.9996337890625,-267.2835998535156],"size":[341.7508850097656,561.0067749023438],"flags":{},"order":21,"mode":0,"inputs":[{"name":"images","localized_name":"images","type":"IMAGE","link":1329}],"outputs":[],"properties":{"Node name for S&R":"SaveImage","cnr_id":"comfy-core","ver":"0.3.29"},"widgets_values":["ComfyUI"]},{"id":701,"type":"Note","pos":[80,-520],"size":[342.05950927734375,88],"flags":{},"order":0,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["I usually just lazily draw masks in Load Image nodes (with some random image loaded), but for the sake of reproducibility, here's another approach."],"color":"#432","bgcolor":"#653"},{"id":712,"type":"Note","pos":[-210,-520],"size":[245.76409912109375,91.6677017211914],"flags":{},"order":1,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["So long as these masks are all the same size, the regional conditioning nodes will handle resizing to the image size for you."],"color":"#432","bgcolor":"#653"},{"id":676,"type":"InvertMask","pos":[20,-370],"size":[142.42074584960938,26],"flags":{},"order":7,"mode":0,"inputs":[{"name":"mask","localized_name":"mask","type":"MASK","link":2073}],"outputs":[{"name":"MASK","localized_name":"MASK","type":"MASK","links":[2083],"slot_index":0}],"properties":{"Node name for S&R":"InvertMask"},"widgets_values":[]},{"id":7,"type":"VAEEncodeAdvanced","pos":[719.6110229492188,16.752899169921875],"size":[261.2217712402344,279.3136901855469],"flags":{},"order":16,"mode":0,"inputs":[{"name":"image_1","localized_name":"image_1","type":"IMAGE","shape":7,"link":null},{"name":"image_2","localized_name":"image_2","type":"IMAGE","shape":7,"link":null},{"name":"mask","localized_name":"mask","type":"IMAGE","shape":7,"link":null},{"name":"latent","localized_name":"latent","type":"LATENT","shape":7,"link":null},{"name":"vae","localized_name":"vae","type":"VAE","shape":7,"link":18}],"outputs":[{"name":"latent_1","localized_name":"latent_1","type":"LATENT","links":[],"slot_index":0},{"name":"latent_2","localized_name":"latent_2","type":"LATENT","links":[],"slot_index":1},{"name":"mask","localized_name":"mask","type":"MASK","links":[],"slot_index":2},{"name":"empty_latent","localized_name":"empty_latent","type":"LATENT","links":[1399],"slot_index":3},{"name":"width","localized_name":"width","type":"INT","links":null},{"name":"height","localized_name":"height","type":"INT","links":null}],"properties":{"Node name for S&R":"VAEEncodeAdvanced","cnr_id":"RES4LYF","ver":"5ce9b5a77c227bf864e447a1e65305bf6cada5c2"},"widgets_values":["false",1024,1024,"red",false,"16_channels"]},{"id":710,"type":"MaskPreview","pos":[180,-190],"size":[210,246],"flags":{},"order":17,"mode":0,"inputs":[{"name":"mask","localized_name":"mask","type":"MASK","link":2054}],"outputs":[],"properties":{"Node name for S&R":"MaskPreview"},"widgets_values":[]},{"id":397,"type":"VAEDecode","pos":[1382.3662109375,-374.17059326171875],"size":[210,46],"flags":{},"order":20,"mode":0,"inputs":[{"name":"samples","localized_name":"samples","type":"LATENT","link":2096},{"name":"vae","localized_name":"vae","type":"VAE","link":1328}],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":[1329],"slot_index":0}],"properties":{"Node name for S&R":"VAEDecode","cnr_id":"comfy-core","ver":"0.3.29"},"widgets_values":[]},{"id":715,"type":"SolidMask","pos":[-220,-370],"size":[210,106],"flags":{},"order":2,"mode":0,"inputs":[],"outputs":[{"name":"MASK","localized_name":"MASK","type":"MASK","links":[2073],"slot_index":0}],"properties":{"Node name for S&R":"SolidMask"},"widgets_values":[1,1024,1024]},{"id":716,"type":"SolidMask","pos":[-220,-220],"size":[210,106],"flags":{},"order":3,"mode":0,"inputs":[],"outputs":[{"name":"MASK","localized_name":"MASK","type":"MASK","links":[2065],"slot_index":0}],"properties":{"Node name for S&R":"SolidMask"},"widgets_values":[1,384,864]},{"id":709,"type":"MaskComposite","pos":[190,-370],"size":[210,126],"flags":{},"order":11,"mode":0,"inputs":[{"name":"destination","localized_name":"destination","type":"MASK","link":2083},{"name":"source","localized_name":"source","type":"MASK","link":2065}],"outputs":[{"name":"MASK","localized_name":"MASK","type":"MASK","links":[2054,2091],"slot_index":0}],"properties":{"Node name for S&R":"MaskComposite"},"widgets_values":[256,160,"add"]},{"id":704,"type":"Note","pos":[101.74818420410156,112.67951965332031],"size":[290.7107238769531,155.35317993164062],"flags":{},"order":4,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["ClownRegionalConditionings:\n\nTry raising or lowering weight, and changing the weight scheduler from beta57 to Karras (weakens more quickly), or to linear quadratic (stronger late).\n\nTry changing region_bleed_start_step (earlier will make the image blend together more), and end_step."],"color":"#432","bgcolor":"#653"},{"id":703,"type":"Note","pos":[423.10699462890625,-96.14085388183594],"size":[241.9689483642578,386.7543640136719],"flags":{},"order":5,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["edge_width also creates some overlap around the edges of the mask.\n\nboolean_masked means that the masked area can \"see\" the rest of the image, but the unmasked area cannot. \"boolean\" would mean neither area could see the rest of the image.\n\nTry setting to boolean_unmasked and see what happens!\n\nIf you still have blur, try reducing edge_width (and if you have seams, try increasing it, or setting end_step to something like 20). \n\nAlso verify that you can generate the background prompt alone without blur (if you can't, this won't work). And don't get stuck on one seed.\n\nVaguely human-shaped masks also tend to work better than the blocky one used here."],"color":"#432","bgcolor":"#653"},{"id":401,"type":"ClownsharKSampler_Beta","pos":[1010,-370],"size":[340.55120849609375,666.8208618164062],"flags":{},"order":19,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","shape":7,"link":1967},{"name":"positive","localized_name":"positive","type":"CONDITIONING","shape":7,"link":2104},{"name":"negative","localized_name":"negative","type":"CONDITIONING","shape":7,"link":2102},{"name":"latent_image","localized_name":"latent_image","type":"LATENT","shape":7,"link":1399},{"name":"sigmas","localized_name":"sigmas","type":"SIGMAS","shape":7,"link":null},{"name":"guides","localized_name":"guides","type":"GUIDES","shape":7,"link":null},{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":null}],"outputs":[{"name":"output","localized_name":"output","type":"LATENT","links":[2096],"slot_index":0},{"name":"denoised","localized_name":"denoised","type":"LATENT","links":null},{"name":"options","localized_name":"options","type":"OPTIONS","links":null}],"properties":{"Node name for S&R":"ClownsharKSampler_Beta","cnr_id":"RES4LYF","ver":"5ce9b5a77c227bf864e447a1e65305bf6cada5c2"},"widgets_values":[0.5,"multistep/res_2m","bong_tangent",30,-1,1,4,3,"fixed","standard",true]},{"id":723,"type":"CLIPTextEncode","pos":[460,-240],"size":[210,88],"flags":{"collapsed":false},"order":14,"mode":0,"inputs":[{"name":"clip","localized_name":"clip","type":"CLIP","link":2092}],"outputs":[{"name":"CONDITIONING","localized_name":"CONDITIONING","type":"CONDITIONING","links":[2093],"slot_index":0}],"properties":{"Node name for S&R":"CLIPTextEncode","cnr_id":"comfy-core","ver":"0.3.29"},"widgets_values":["a college campus"]},{"id":662,"type":"CLIPTextEncode","pos":[460,-370],"size":[210,88],"flags":{"collapsed":false},"order":13,"mode":0,"inputs":[{"name":"clip","localized_name":"clip","type":"CLIP","link":1939}],"outputs":[{"name":"CONDITIONING","localized_name":"CONDITIONING","type":"CONDITIONING","links":[2094],"slot_index":0}],"properties":{"Node name for S&R":"CLIPTextEncode","cnr_id":"comfy-core","ver":"0.3.29"},"widgets_values":["a woman wearing a red flannel shirt and a cute shark plush blue hat"]},{"id":724,"type":"ClownModelLoader","pos":[615.2467651367188,-699.0204467773438],"size":[361.6804504394531,266],"flags":{},"order":6,"mode":0,"inputs":[],"outputs":[{"name":"model","localized_name":"model","type":"MODEL","links":[2097],"slot_index":0},{"name":"clip","localized_name":"clip","type":"CLIP","links":[2099],"slot_index":1},{"name":"vae","localized_name":"vae","type":"VAE","links":[2100],"slot_index":2}],"properties":{"Node name for S&R":"ClownModelLoader"},"widgets_values":["chroma-unlocked-v29.5.safetensors","fp8_e4m3fn_fast","t5xxl_fp8_e4m3fn_scaled.safetensors",".none",".none",".none","chroma","ae.sft"]},{"id":725,"type":"ReChromaPatcher","pos":[1030.2850341796875,-698.6190795898438],"size":[210,82],"flags":{},"order":8,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","link":2097}],"outputs":[{"name":"model","localized_name":"model","type":"MODEL","links":[2098],"slot_index":0}],"properties":{"Node name for S&R":"ReChromaPatcher"},"widgets_values":["float64",true]},{"id":726,"type":"CLIPTextEncode","pos":[772.4685668945312,350.9657897949219],"size":[210,88],"flags":{"collapsed":false},"order":15,"mode":0,"inputs":[{"name":"clip","localized_name":"clip","type":"CLIP","link":2101}],"outputs":[{"name":"CONDITIONING","localized_name":"CONDITIONING","type":"CONDITIONING","links":[2102],"slot_index":0}],"properties":{"Node name for S&R":"CLIPTextEncode","cnr_id":"comfy-core","ver":"0.3.29"},"widgets_values":["low quality, bad quality, mutated, low detail, blurry, out of focus, jpeg artifacts"]},{"id":722,"type":"ClownRegionalConditioning2","pos":[690,-370],"size":[287.75750732421875,330],"flags":{},"order":18,"mode":0,"inputs":[{"name":"conditioning_masked","localized_name":"conditioning_masked","type":"CONDITIONING","shape":7,"link":2094},{"name":"conditioning_unmasked","localized_name":"conditioning_unmasked","type":"CONDITIONING","shape":7,"link":2093},{"name":"mask","localized_name":"mask","type":"MASK","shape":7,"link":2091},{"name":"weights","localized_name":"weights","type":"SIGMAS","shape":7,"link":null},{"name":"region_bleeds","localized_name":"region_bleeds","type":"SIGMAS","shape":7,"link":null}],"outputs":[{"name":"conditioning","localized_name":"conditioning","type":"CONDITIONING","links":[2104],"slot_index":0}],"properties":{"Node name for S&R":"ClownRegionalConditioning2"},"widgets_values":[1,0,0,"constant",0,10,"boolean_masked",32,false]}],"links":[[18,14,0,7,4,"VAE"],[1328,14,0,397,1,"VAE"],[1329,397,0,398,0,"IMAGE"],[1399,7,3,401,3,"LATENT"],[1939,490,0,662,0,"CLIP"],[1967,13,0,401,0,"MODEL"],[2054,709,0,710,0,"MASK"],[2065,716,0,709,1,"MASK"],[2073,715,0,676,0,"MASK"],[2083,676,0,709,0,"MASK"],[2091,709,0,722,2,"MASK"],[2092,490,0,723,0,"CLIP"],[2093,723,0,722,1,"CONDITIONING"],[2094,662,0,722,0,"CONDITIONING"],[2096,401,0,397,0,"LATENT"],[2097,724,0,725,0,"MODEL"],[2098,725,0,13,0,"*"],[2099,724,1,490,0,"*"],[2100,724,2,14,0,"*"],[2101,490,0,726,0,"CLIP"],[2102,726,0,401,2,"CONDITIONING"],[2104,722,0,401,1,"CONDITIONING"]],"groups":[],"config":{},"extra":{"ds":{"scale":1.5863092971715371,"offset":[2215.7489179851177,830.3089944212893]},"VHS_latentpreview":false,"VHS_latentpreviewrate":0,"ue_links":[],"VHS_MetadataImage":true,"VHS_KeepIntermediate":true},"version":0.4} \ No newline at end of file diff --git a/ComfyUI/custom_nodes/RES4LYF/example_workflows/chroma txt2img.json b/ComfyUI/custom_nodes/RES4LYF/example_workflows/chroma txt2img.json new file mode 100644 index 0000000000000000000000000000000000000000..e244d5ebd45e06a20a8da0b450bbf6ebb3fc08a4 --- /dev/null +++ b/ComfyUI/custom_nodes/RES4LYF/example_workflows/chroma txt2img.json @@ -0,0 +1 @@ +{"last_node_id":727,"last_link_id":2113,"nodes":[{"id":398,"type":"SaveImage","pos":[1379.9996337890625,-267.2835998535156],"size":[341.7508850097656,561.0067749023438],"flags":{},"order":6,"mode":0,"inputs":[{"name":"images","localized_name":"images","type":"IMAGE","link":1329}],"outputs":[],"properties":{"Node name for S&R":"SaveImage","cnr_id":"comfy-core","ver":"0.3.29"},"widgets_values":["ComfyUI"]},{"id":397,"type":"VAEDecode","pos":[1382.3662109375,-374.17059326171875],"size":[210,46],"flags":{},"order":5,"mode":0,"inputs":[{"name":"samples","localized_name":"samples","type":"LATENT","link":2096},{"name":"vae","localized_name":"vae","type":"VAE","link":2112}],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":[1329],"slot_index":0}],"properties":{"Node name for S&R":"VAEDecode","cnr_id":"comfy-core","ver":"0.3.29"},"widgets_values":[]},{"id":401,"type":"ClownsharKSampler_Beta","pos":[1010,-370],"size":[340.55120849609375,666.8208618164062],"flags":{},"order":4,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","shape":7,"link":2108},{"name":"positive","localized_name":"positive","type":"CONDITIONING","shape":7,"link":2107},{"name":"negative","localized_name":"negative","type":"CONDITIONING","shape":7,"link":2102},{"name":"latent_image","localized_name":"latent_image","type":"LATENT","shape":7,"link":2113},{"name":"sigmas","localized_name":"sigmas","type":"SIGMAS","shape":7,"link":null},{"name":"guides","localized_name":"guides","type":"GUIDES","shape":7,"link":null},{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":null}],"outputs":[{"name":"output","localized_name":"output","type":"LATENT","links":[2096],"slot_index":0},{"name":"denoised","localized_name":"denoised","type":"LATENT","links":null},{"name":"options","localized_name":"options","type":"OPTIONS","links":null}],"properties":{"Node name for S&R":"ClownsharKSampler_Beta","cnr_id":"RES4LYF","ver":"5ce9b5a77c227bf864e447a1e65305bf6cada5c2"},"widgets_values":[0.5,"multistep/res_2m","bong_tangent",30,-1,1,4,3,"fixed","standard",true]},{"id":662,"type":"CLIPTextEncode","pos":[770.2921752929688,-373.6678771972656],"size":[210,88],"flags":{"collapsed":false},"order":2,"mode":0,"inputs":[{"name":"clip","localized_name":"clip","type":"CLIP","link":2109}],"outputs":[{"name":"CONDITIONING","localized_name":"CONDITIONING","type":"CONDITIONING","links":[2107],"slot_index":0}],"properties":{"Node name for S&R":"CLIPTextEncode","cnr_id":"comfy-core","ver":"0.3.29"},"widgets_values":["a woman wearing a red flannel shirt and a cute shark plush blue hat"]},{"id":726,"type":"CLIPTextEncode","pos":[772.46923828125,-238.8079376220703],"size":[210,88],"flags":{"collapsed":false},"order":3,"mode":0,"inputs":[{"name":"clip","localized_name":"clip","type":"CLIP","link":2110}],"outputs":[{"name":"CONDITIONING","localized_name":"CONDITIONING","type":"CONDITIONING","links":[2102],"slot_index":0}],"properties":{"Node name for S&R":"CLIPTextEncode","cnr_id":"comfy-core","ver":"0.3.29"},"widgets_values":["low quality, bad quality, mutated, low detail, blurry, out of focus, jpeg artifacts"]},{"id":727,"type":"EmptyLatentImage","pos":[771.9976196289062,-98.32988739013672],"size":[213.03683471679688,106],"flags":{},"order":0,"mode":0,"inputs":[],"outputs":[{"name":"LATENT","localized_name":"LATENT","type":"LATENT","links":[2113],"slot_index":0}],"properties":{"Node name for S&R":"EmptyLatentImage"},"widgets_values":[1024,1024,1]},{"id":724,"type":"ClownModelLoader","pos":[380.5105285644531,-376.99224853515625],"size":[361.6804504394531,266],"flags":{},"order":1,"mode":0,"inputs":[],"outputs":[{"name":"model","localized_name":"model","type":"MODEL","links":[2108],"slot_index":0},{"name":"clip","localized_name":"clip","type":"CLIP","links":[2109,2110],"slot_index":1},{"name":"vae","localized_name":"vae","type":"VAE","links":[2112],"slot_index":2}],"properties":{"Node name for S&R":"ClownModelLoader"},"widgets_values":["chroma-unlocked-v37-detail-calibrated.safetensors","fp8_e4m3fn_fast","t5xxl_fp8_e4m3fn_scaled.safetensors",".none",".none",".none","chroma","ae.sft"]}],"links":[[1329,397,0,398,0,"IMAGE"],[2096,401,0,397,0,"LATENT"],[2102,726,0,401,2,"CONDITIONING"],[2107,662,0,401,1,"CONDITIONING"],[2108,724,0,401,0,"MODEL"],[2109,724,1,662,0,"CLIP"],[2110,724,1,726,0,"CLIP"],[2112,724,2,397,1,"VAE"],[2113,727,0,401,3,"LATENT"]],"groups":[],"config":{},"extra":{"ds":{"scale":1.5863092971715371,"offset":[1675.8567061174099,917.6014919421251]},"VHS_latentpreview":false,"VHS_latentpreviewrate":0,"ue_links":[],"VHS_MetadataImage":true,"VHS_KeepIntermediate":true},"version":0.4} \ No newline at end of file diff --git a/ComfyUI/custom_nodes/RES4LYF/example_workflows/comparison ksampler vs csksampler chain workflows.json b/ComfyUI/custom_nodes/RES4LYF/example_workflows/comparison ksampler vs csksampler chain workflows.json new file mode 100644 index 0000000000000000000000000000000000000000..70668297b22a805abb5100cff89607650226ebb8 --- /dev/null +++ b/ComfyUI/custom_nodes/RES4LYF/example_workflows/comparison ksampler vs csksampler chain workflows.json @@ -0,0 +1 @@ +{"last_node_id":1423,"last_link_id":3992,"nodes":[{"id":13,"type":"Reroute","pos":[17750,830],"size":[75,26],"flags":{},"order":9,"mode":0,"inputs":[{"name":"","type":"*","link":3988}],"outputs":[{"name":"","type":"MODEL","links":[1395],"slot_index":0}],"properties":{"showOutputText":false,"horizontal":false}},{"id":402,"type":"QuadrupleCLIPLoader","pos":[17300,870],"size":[407.7720031738281,130],"flags":{},"order":0,"mode":0,"inputs":[],"outputs":[{"name":"CLIP","localized_name":"CLIP","type":"CLIP","links":[1552],"slot_index":0}],"properties":{"Node name for S&R":"QuadrupleCLIPLoader","cnr_id":"comfy-core","ver":"0.3.29"},"widgets_values":["clip_l_hidream.safetensors","clip_g_hidream.safetensors","t5xxl_fp8_e4m3fn_scaled.safetensors","llama_3.1_8b_instruct_fp8_scaled.safetensors"]},{"id":403,"type":"UNETLoader","pos":[17390,740],"size":[320.7802429199219,82],"flags":{},"order":1,"mode":0,"inputs":[],"outputs":[{"name":"MODEL","localized_name":"MODEL","type":"MODEL","links":[3988],"slot_index":0}],"properties":{"Node name for S&R":"UNETLoader","cnr_id":"comfy-core","ver":"0.3.29"},"widgets_values":["hidream_i1_full_fp8.safetensors","fp8_e4m3fn"]},{"id":404,"type":"VAELoader","pos":[17500,1060],"size":[210,58],"flags":{},"order":2,"mode":0,"inputs":[],"outputs":[{"name":"VAE","localized_name":"VAE","type":"VAE","links":[1344],"slot_index":0}],"properties":{"Node name for S&R":"VAELoader","cnr_id":"comfy-core","ver":"0.3.29"},"widgets_values":["ae.sft"]},{"id":1381,"type":"Reroute","pos":[18770,-310],"size":[75,26],"flags":{},"order":23,"mode":0,"inputs":[{"name":"","type":"*","link":3961}],"outputs":[{"name":"","type":"CONDITIONING","links":[3881]}],"properties":{"showOutputText":false,"horizontal":false}},{"id":1383,"type":"Reroute","pos":[18770,-420],"size":[75,26],"flags":{},"order":27,"mode":0,"inputs":[{"name":"","type":"*","link":3877}],"outputs":[{"name":"","type":"MODEL","links":[3879],"slot_index":0}],"properties":{"showOutputText":false,"horizontal":false}},{"id":1388,"type":"Reroute","pos":[18750,410],"size":[75,26],"flags":{},"order":28,"mode":0,"inputs":[{"name":"","type":"*","link":3886}],"outputs":[{"name":"","type":"MODEL","links":[3887,3891,3896,3901],"slot_index":0}],"properties":{"showOutputText":false,"horizontal":false}},{"id":1393,"type":"SaveImage","pos":[20400,450],"size":[457.3382263183594,422.2065124511719],"flags":{},"order":51,"mode":0,"inputs":[{"name":"images","localized_name":"images","type":"IMAGE","link":3908}],"outputs":[],"properties":{"Node name for S&R":"SaveImage","cnr_id":"comfy-core","ver":"0.3.29"},"widgets_values":["ComfyUI"]},{"id":1399,"type":"Reroute","pos":[18790,1920],"size":[75,26],"flags":{},"order":22,"mode":0,"inputs":[{"name":"","type":"*","link":3967}],"outputs":[{"name":"","type":"CONDITIONING","links":[3925,3933]}],"properties":{"showOutputText":false,"horizontal":false}},{"id":1401,"type":"Reroute","pos":[18780,1870],"size":[75,26],"flags":{},"order":30,"mode":0,"inputs":[{"name":"","type":"*","link":3916}],"outputs":[{"name":"","type":"MODEL","links":[3924,3931,3932],"slot_index":0}],"properties":{"showOutputText":false,"horizontal":false}},{"id":1408,"type":"FlipSigmas","pos":[19150,2270],"size":[140,26],"flags":{},"order":42,"mode":0,"inputs":[{"name":"sigmas","localized_name":"sigmas","type":"SIGMAS","link":3941}],"outputs":[{"name":"SIGMAS","localized_name":"SIGMAS","type":"SIGMAS","links":[3929]}],"properties":{"Node name for S&R":"FlipSigmas"},"widgets_values":[]},{"id":1394,"type":"SamplerCustom","pos":[18940,1910],"size":[253.52972412109375,230],"flags":{},"order":46,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","link":3924},{"name":"positive","localized_name":"positive","type":"CONDITIONING","link":3925},{"name":"negative","localized_name":"negative","type":"CONDITIONING","link":3926},{"name":"sampler","localized_name":"sampler","type":"SAMPLER","link":3928},{"name":"sigmas","localized_name":"sigmas","type":"SIGMAS","link":3929},{"name":"latent_image","localized_name":"latent_image","type":"LATENT","link":3979}],"outputs":[{"name":"output","localized_name":"output","type":"LATENT","links":[3938],"slot_index":0},{"name":"denoised_output","localized_name":"denoised_output","type":"LATENT","links":null}],"properties":{"Node name for S&R":"SamplerCustom"},"widgets_values":[false,0,"fixed",1]},{"id":1411,"type":"SplitSigmas","pos":[19030,2350],"size":[210,78],"flags":{},"order":38,"mode":0,"inputs":[{"name":"sigmas","localized_name":"sigmas","type":"SIGMAS","link":3940}],"outputs":[{"name":"high_sigmas","localized_name":"high_sigmas","type":"SIGMAS","links":null},{"name":"low_sigmas","localized_name":"low_sigmas","type":"SIGMAS","links":[3941,3942],"slot_index":1}],"properties":{"Node name for S&R":"SplitSigmas"},"widgets_values":[15]},{"id":1409,"type":"BetaSamplingScheduler","pos":[18780,2360],"size":[210,106],"flags":{},"order":34,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","link":3931}],"outputs":[{"name":"SIGMAS","localized_name":"SIGMAS","type":"SIGMAS","links":[3940],"slot_index":0}],"properties":{"Node name for S&R":"BetaSamplingScheduler"},"widgets_values":[30,0.5,0.7]},{"id":1407,"type":"KSamplerSelect","pos":[18720,2210],"size":[210,58],"flags":{},"order":3,"mode":0,"inputs":[],"outputs":[{"name":"SAMPLER","localized_name":"SAMPLER","type":"SAMPLER","links":[3928,3935]}],"properties":{"Node name for S&R":"KSamplerSelect"},"widgets_values":["euler"]},{"id":1395,"type":"Reroute","pos":[18750,1110],"size":[75,26],"flags":{},"order":21,"mode":0,"inputs":[{"name":"","type":"*","link":3965}],"outputs":[{"name":"","type":"CONDITIONING","links":[3949],"slot_index":0}],"properties":{"showOutputText":false,"horizontal":false}},{"id":1405,"type":"VAEDecode","pos":[19650,1810],"size":[210,46],"flags":{},"order":52,"mode":0,"inputs":[{"name":"samples","localized_name":"samples","type":"LATENT","link":3992},{"name":"vae","localized_name":"vae","type":"VAE","link":3922}],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":[3923],"slot_index":0}],"properties":{"Node name for S&R":"VAEDecode","cnr_id":"comfy-core","ver":"0.3.29"},"widgets_values":[]},{"id":1403,"type":"VAEDecode","pos":[19650,990],"size":[210,46],"flags":{},"order":41,"mode":0,"inputs":[{"name":"samples","localized_name":"samples","type":"LATENT","link":3991},{"name":"vae","localized_name":"vae","type":"VAE","link":3919}],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":[3920],"slot_index":0}],"properties":{"Node name for S&R":"VAEDecode","cnr_id":"comfy-core","ver":"0.3.29"},"widgets_values":[]},{"id":1263,"type":"VAEDecode","pos":[20410,-500],"size":[210,46],"flags":{},"order":47,"mode":0,"inputs":[{"name":"samples","localized_name":"samples","type":"LATENT","link":3989},{"name":"vae","localized_name":"vae","type":"VAE","link":3429}],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":[3430],"slot_index":0}],"properties":{"Node name for S&R":"VAEDecode","cnr_id":"comfy-core","ver":"0.3.29"},"widgets_values":[]},{"id":490,"type":"Reroute","pos":[17750,870],"size":[75,26],"flags":{},"order":8,"mode":0,"inputs":[{"name":"","type":"*","link":1552}],"outputs":[{"name":"","type":"CLIP","links":[3959,3960],"slot_index":0}],"properties":{"showOutputText":false,"horizontal":false}},{"id":1385,"type":"Reroute","pos":[18750,520],"size":[75,26],"flags":{},"order":24,"mode":0,"inputs":[{"name":"","type":"*","link":3964}],"outputs":[{"name":"","type":"CONDITIONING","links":[3889,3893,3898,3903],"slot_index":0}],"properties":{"showOutputText":false,"horizontal":false}},{"id":1415,"type":"CLIPTextEncode","pos":[17860,1070],"size":[261.8798522949219,111.21334838867188],"flags":{},"order":15,"mode":0,"inputs":[{"name":"clip","localized_name":"clip","type":"CLIP","link":3960}],"outputs":[{"name":"CONDITIONING","localized_name":"CONDITIONING","type":"CONDITIONING","links":[3961,3964,3966,3968],"slot_index":0}],"properties":{"Node name for S&R":"CLIPTextEncode","cnr_id":"comfy-core","ver":"0.3.29"},"widgets_values":["blurry, out of focus, shallow depth of field, low quality, bad quality, low detail, mutated, jpeg artifacts, compression artifacts,"]},{"id":1414,"type":"CLIPTextEncode","pos":[17860,870],"size":[271.3465270996094,126.98572540283203],"flags":{},"order":14,"mode":0,"inputs":[{"name":"clip","localized_name":"clip","type":"CLIP","link":3959}],"outputs":[{"name":"CONDITIONING","localized_name":"CONDITIONING","type":"CONDITIONING","links":[3962,3963,3965,3967],"slot_index":0}],"properties":{"Node name for S&R":"CLIPTextEncode"},"widgets_values":["a photo of a doghead cannibal holding a sign that says \"the clown jumped the shark\" in a landfill at night"]},{"id":1397,"type":"Reroute","pos":[18750,1060],"size":[75,26],"flags":{},"order":29,"mode":0,"inputs":[{"name":"","type":"*","link":3912}],"outputs":[{"name":"","type":"MODEL","links":[3948],"slot_index":0}],"properties":{"showOutputText":false,"horizontal":false}},{"id":1402,"type":"Reroute","pos":[18780,1980],"size":[75,26],"flags":{},"order":26,"mode":0,"inputs":[{"name":"","type":"*","link":3968}],"outputs":[{"name":"","type":"CONDITIONING","links":[3926,3934],"slot_index":0}],"properties":{"showOutputText":false,"horizontal":false}},{"id":1417,"type":"LoadImage","pos":[18263.712890625,1364.093017578125],"size":[315,314],"flags":{},"order":4,"mode":0,"inputs":[],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":[3973]},{"name":"MASK","localized_name":"MASK","type":"MASK","links":null}],"properties":{"Node name for S&R":"LoadImage"},"widgets_values":["00107-496528661.png","image"]},{"id":1420,"type":"VAEEncode","pos":[18710,2080],"size":[140,46],"flags":{},"order":18,"mode":0,"inputs":[{"name":"pixels","localized_name":"pixels","type":"IMAGE","link":3977},{"name":"vae","localized_name":"vae","type":"VAE","link":3980}],"outputs":[{"name":"LATENT","localized_name":"LATENT","type":"LATENT","links":[3979],"slot_index":0}],"properties":{"Node name for S&R":"VAEEncode"},"widgets_values":[]},{"id":1419,"type":"ImageResize+","pos":[18460,2080],"size":[210,218],"flags":{},"order":11,"mode":0,"inputs":[{"name":"image","localized_name":"image","type":"IMAGE","link":3976}],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":[3977],"slot_index":0},{"name":"width","localized_name":"width","type":"INT","links":null},{"name":"height","localized_name":"height","type":"INT","links":null}],"properties":{"Node name for S&R":"ImageResize+"},"widgets_values":[1024,1024,"bicubic","stretch","always",0]},{"id":14,"type":"Reroute","pos":[17750,910],"size":[75,26],"flags":{},"order":10,"mode":0,"inputs":[{"name":"","type":"*","link":1344}],"outputs":[{"name":"","type":"VAE","links":[3429,3907,3919,3922,3969,3980],"slot_index":0}],"properties":{"showOutputText":false,"horizontal":false}},{"id":1418,"type":"LoadImage","pos":[18120,2080],"size":[315,314],"flags":{},"order":5,"mode":0,"inputs":[],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":[3976],"slot_index":0},{"name":"MASK","localized_name":"MASK","type":"MASK","links":null}],"properties":{"Node name for S&R":"LoadImage"},"widgets_values":["00107-496528661.png","image"]},{"id":1398,"type":"Reroute","pos":[18750,1160],"size":[75,26],"flags":{},"order":25,"mode":0,"inputs":[{"name":"","type":"*","link":3966}],"outputs":[{"name":"","type":"CONDITIONING","links":[3950],"slot_index":0}],"properties":{"showOutputText":false,"horizontal":false}},{"id":1416,"type":"VAEEncodeAdvanced","pos":[18620,1370],"size":[253.78292846679688,278],"flags":{},"order":17,"mode":0,"inputs":[{"name":"image_1","localized_name":"image_1","type":"IMAGE","shape":7,"link":3973},{"name":"image_2","localized_name":"image_2","type":"IMAGE","shape":7,"link":null},{"name":"mask","localized_name":"mask","type":"IMAGE","shape":7,"link":null},{"name":"latent","localized_name":"latent","type":"LATENT","shape":7,"link":null},{"name":"vae","localized_name":"vae","type":"VAE","shape":7,"link":3969}],"outputs":[{"name":"latent_1","localized_name":"latent_1","type":"LATENT","links":[3975],"slot_index":0},{"name":"latent_2","localized_name":"latent_2","type":"LATENT","links":null},{"name":"mask","localized_name":"mask","type":"MASK","links":null},{"name":"empty_latent","localized_name":"empty_latent","type":"LATENT","links":[],"slot_index":3},{"name":"width","localized_name":"width","type":"INT","links":null},{"name":"height","localized_name":"height","type":"INT","links":null}],"properties":{"Node name for S&R":"VAEEncodeAdvanced"},"widgets_values":["false",1024,1024,"red",false,"16_channels"]},{"id":1423,"type":"FluxLoader","pos":[16942.298828125,795.814208984375],"size":[315,282],"flags":{},"order":6,"mode":0,"inputs":[],"outputs":[{"name":"model","localized_name":"model","type":"MODEL","links":null},{"name":"clip","localized_name":"clip","type":"CLIP","links":null},{"name":"vae","localized_name":"vae","type":"VAE","links":null},{"name":"clip_vision","localized_name":"clip_vision","type":"CLIP_VISION","links":null},{"name":"style_model","localized_name":"style_model","type":"STYLE_MODEL","links":null}],"properties":{"Node name for S&R":"FluxLoader"},"widgets_values":["colossusProjectFlux_v42AIO.safetensors","default",".use_ckpt_clip",".none",".use_ckpt_vae",".none",".none"]},{"id":431,"type":"ModelSamplingAdvancedResolution","pos":[17868.26953125,666.623046875],"size":[260.3999938964844,126],"flags":{},"order":16,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","link":1395},{"name":"latent_image","localized_name":"latent_image","type":"LATENT","link":3987}],"outputs":[{"name":"model","localized_name":"model","type":"MODEL","links":[3877,3886,3912,3916],"slot_index":0}],"properties":{"Node name for S&R":"ModelSamplingAdvancedResolution","cnr_id":"RES4LYF","ver":"5ce9b5a77c227bf864e447a1e65305bf6cada5c2"},"widgets_values":["exponential",1.35,0.85]},{"id":1422,"type":"EmptyLatentImage","pos":[17486.916015625,540.6340942382812],"size":[315,106],"flags":{},"order":7,"mode":0,"inputs":[],"outputs":[{"name":"LATENT","localized_name":"LATENT","type":"LATENT","links":[3985,3986,3987],"slot_index":0}],"properties":{"Node name for S&R":"EmptyLatentImage"},"widgets_values":[1024,1024,1]},{"id":1380,"type":"Reroute","pos":[18768.1875,-255.9905242919922],"size":[75,26],"flags":{},"order":12,"mode":0,"inputs":[{"name":"","type":"*","link":3985}],"outputs":[{"name":"","type":"LATENT","links":[3882],"slot_index":0}],"properties":{"showOutputText":false,"horizontal":false}},{"id":1382,"type":"Reroute","pos":[18769.365234375,-367.63720703125],"size":[75,26],"flags":{},"order":19,"mode":0,"inputs":[{"name":"","type":"*","link":3962}],"outputs":[{"name":"","type":"CONDITIONING","links":[3880]}],"properties":{"showOutputText":false,"horizontal":false}},{"id":1386,"type":"Reroute","pos":[18750.548828125,467.08831787109375],"size":[75,26],"flags":{},"order":20,"mode":0,"inputs":[{"name":"","type":"*","link":3963}],"outputs":[{"name":"","type":"CONDITIONING","links":[3888,3892,3897,3902]}],"properties":{"showOutputText":false,"horizontal":false}},{"id":1387,"type":"Reroute","pos":[18747.00390625,569.2838745117188],"size":[75,26],"flags":{},"order":13,"mode":0,"inputs":[{"name":"","type":"*","link":3986}],"outputs":[{"name":"","type":"LATENT","links":[3890]}],"properties":{"showOutputText":false,"horizontal":false}},{"id":1264,"type":"SaveImage","pos":[20410,-410],"size":[457.3382263183594,422.2065124511719],"flags":{},"order":50,"mode":0,"inputs":[{"name":"images","localized_name":"images","type":"IMAGE","link":3430}],"outputs":[],"properties":{"Node name for S&R":"SaveImage","cnr_id":"comfy-core","ver":"0.3.29"},"widgets_values":["ComfyUI"]},{"id":1392,"type":"VAEDecode","pos":[20400,360],"size":[210,46],"flags":{},"order":48,"mode":0,"inputs":[{"name":"samples","localized_name":"samples","type":"LATENT","link":3990},{"name":"vae","localized_name":"vae","type":"VAE","link":3907}],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":[3908],"slot_index":0}],"properties":{"Node name for S&R":"VAEDecode","cnr_id":"comfy-core","ver":"0.3.29"},"widgets_values":[]},{"id":1410,"type":"SamplerCustom","pos":[19300,1900],"size":[272.0888977050781,230],"flags":{},"order":49,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","link":3932},{"name":"positive","localized_name":"positive","type":"CONDITIONING","link":3933},{"name":"negative","localized_name":"negative","type":"CONDITIONING","link":3934},{"name":"sampler","localized_name":"sampler","type":"SAMPLER","link":3935},{"name":"sigmas","localized_name":"sigmas","type":"SIGMAS","link":3942},{"name":"latent_image","localized_name":"latent_image","type":"LATENT","link":3938}],"outputs":[{"name":"output","localized_name":"output","type":"LATENT","links":[3992],"slot_index":0},{"name":"denoised_output","localized_name":"denoised_output","type":"LATENT","links":null}],"properties":{"Node name for S&R":"SamplerCustom"},"widgets_values":[false,0,"fixed",4]},{"id":1261,"type":"ClownsharKSampler_Beta","pos":[18944.17578125,-390],"size":[283.8435974121094,418],"flags":{},"order":31,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","shape":7,"link":3879},{"name":"positive","localized_name":"positive","type":"CONDITIONING","shape":7,"link":3880},{"name":"negative","localized_name":"negative","type":"CONDITIONING","shape":7,"link":3881},{"name":"latent_image","localized_name":"latent_image","type":"LATENT","shape":7,"link":3882},{"name":"sigmas","localized_name":"sigmas","type":"SIGMAS","shape":7,"link":null},{"name":"guides","localized_name":"guides","type":"GUIDES","shape":7,"link":null},{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":null}],"outputs":[{"name":"output","localized_name":"output","type":"LATENT","links":[3427],"slot_index":0},{"name":"denoised","localized_name":"denoised","type":"LATENT","links":null},{"name":"options","localized_name":"options","type":"OPTIONS","links":null}],"properties":{"Node name for S&R":"ClownsharKSampler_Beta","cnr_id":"RES4LYF","ver":"5ce9b5a77c227bf864e447a1e65305bf6cada5c2"},"widgets_values":[0.5,"multistep/res_2m","beta57",30,5,1,4,0,"fixed","standard",true]},{"id":1262,"type":"ClownsharkChainsampler_Beta","pos":[19310.083984375,-402.36279296875],"size":[285.8560485839844,298],"flags":{},"order":35,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","shape":7,"link":null},{"name":"positive","localized_name":"positive","type":"CONDITIONING","shape":7,"link":null},{"name":"negative","localized_name":"negative","type":"CONDITIONING","shape":7,"link":null},{"name":"sigmas","localized_name":"sigmas","type":"SIGMAS","shape":7,"link":null},{"name":"latent_image","localized_name":"latent_image","type":"LATENT","shape":7,"link":3427},{"name":"guides","localized_name":"guides","type":"GUIDES","shape":7,"link":null},{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":null}],"outputs":[{"name":"output","localized_name":"output","type":"LATENT","links":[3435],"slot_index":0},{"name":"denoised","localized_name":"denoised","type":"LATENT","links":null},{"name":"options","localized_name":"options","type":"OPTIONS","links":null}],"properties":{"Node name for S&R":"ClownsharkChainsampler_Beta"},"widgets_values":[0.5,"multistep/res_2m",5,4,"resample",true]},{"id":1266,"type":"ClownsharkChainsampler_Beta","pos":[19679.115234375,-407.62518310546875],"size":[269.3165283203125,298],"flags":{},"order":39,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","shape":7,"link":null},{"name":"positive","localized_name":"positive","type":"CONDITIONING","shape":7,"link":null},{"name":"negative","localized_name":"negative","type":"CONDITIONING","shape":7,"link":null},{"name":"sigmas","localized_name":"sigmas","type":"SIGMAS","shape":7,"link":null},{"name":"latent_image","localized_name":"latent_image","type":"LATENT","shape":7,"link":3435},{"name":"guides","localized_name":"guides","type":"GUIDES","shape":7,"link":null},{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":null}],"outputs":[{"name":"output","localized_name":"output","type":"LATENT","links":[3436],"slot_index":0},{"name":"denoised","localized_name":"denoised","type":"LATENT","links":null},{"name":"options","localized_name":"options","type":"OPTIONS","links":null}],"properties":{"Node name for S&R":"ClownsharkChainsampler_Beta"},"widgets_values":[0.5,"multistep/res_2m",5,4,"resample",true]},{"id":1265,"type":"ClownsharkChainsampler_Beta","pos":[20054.2421875,-408.6135559082031],"size":[271.6801452636719,298],"flags":{},"order":43,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","shape":7,"link":null},{"name":"positive","localized_name":"positive","type":"CONDITIONING","shape":7,"link":null},{"name":"negative","localized_name":"negative","type":"CONDITIONING","shape":7,"link":null},{"name":"sigmas","localized_name":"sigmas","type":"SIGMAS","shape":7,"link":null},{"name":"latent_image","localized_name":"latent_image","type":"LATENT","shape":7,"link":3436},{"name":"guides","localized_name":"guides","type":"GUIDES","shape":7,"link":null},{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":null}],"outputs":[{"name":"output","localized_name":"output","type":"LATENT","links":[3989],"slot_index":0},{"name":"denoised","localized_name":"denoised","type":"LATENT","links":null},{"name":"options","localized_name":"options","type":"OPTIONS","links":null}],"properties":{"Node name for S&R":"ClownsharkChainsampler_Beta"},"widgets_values":[0.5,"multistep/res_2m",-1,4,"resample",true]},{"id":1384,"type":"KSamplerAdvanced","pos":[18936.240234375,444.8757019042969],"size":[278.3764343261719,334],"flags":{},"order":32,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","link":3887},{"name":"positive","localized_name":"positive","type":"CONDITIONING","link":3888},{"name":"negative","localized_name":"negative","type":"CONDITIONING","link":3889},{"name":"latent_image","localized_name":"latent_image","type":"LATENT","link":3890}],"outputs":[{"name":"LATENT","localized_name":"LATENT","type":"LATENT","links":[3895],"slot_index":0}],"properties":{"Node name for S&R":"KSamplerAdvanced"},"widgets_values":["enable",0,"fixed",30,4,"euler","beta57",0,5,"enable"]},{"id":1391,"type":"KSamplerAdvanced","pos":[20044.978515625,449.22869873046875],"size":[278.3769226074219,334],"flags":{},"order":44,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","link":3901},{"name":"positive","localized_name":"positive","type":"CONDITIONING","link":3902},{"name":"negative","localized_name":"negative","type":"CONDITIONING","link":3903},{"name":"latent_image","localized_name":"latent_image","type":"LATENT","link":3905}],"outputs":[{"name":"LATENT","localized_name":"LATENT","type":"LATENT","links":[3990],"slot_index":0}],"properties":{"Node name for S&R":"KSamplerAdvanced"},"widgets_values":["disable",15,"fixed",30,4,"euler","beta57",15,10000,"disable"]},{"id":1390,"type":"KSamplerAdvanced","pos":[19672.99609375,448.818603515625],"size":[273.651123046875,334],"flags":{},"order":40,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","link":3896},{"name":"positive","localized_name":"positive","type":"CONDITIONING","link":3897},{"name":"negative","localized_name":"negative","type":"CONDITIONING","link":3898},{"name":"latent_image","localized_name":"latent_image","type":"LATENT","link":3900}],"outputs":[{"name":"LATENT","localized_name":"LATENT","type":"LATENT","links":[3905],"slot_index":0}],"properties":{"Node name for S&R":"KSamplerAdvanced"},"widgets_values":["disable",10,"fixed",30,4,"euler","beta57",10,15,"enable"]},{"id":1389,"type":"KSamplerAdvanced","pos":[19308.921875,451.14801025390625],"size":[273.652099609375,334],"flags":{},"order":36,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","link":3891},{"name":"positive","localized_name":"positive","type":"CONDITIONING","link":3892},{"name":"negative","localized_name":"negative","type":"CONDITIONING","link":3893},{"name":"latent_image","localized_name":"latent_image","type":"LATENT","link":3895}],"outputs":[{"name":"LATENT","localized_name":"LATENT","type":"LATENT","links":[3900],"slot_index":0}],"properties":{"Node name for S&R":"KSamplerAdvanced"},"widgets_values":["disable",5,"fixed",30,4,"euler","beta57",5,10,"enable"]},{"id":1413,"type":"ClownsharkChainsampler_Beta","pos":[19294.095703125,1089.451171875],"size":[275.2236328125,298],"flags":{},"order":37,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","shape":7,"link":null},{"name":"positive","localized_name":"positive","type":"CONDITIONING","shape":7,"link":null},{"name":"negative","localized_name":"negative","type":"CONDITIONING","shape":7,"link":null},{"name":"sigmas","localized_name":"sigmas","type":"SIGMAS","shape":7,"link":null},{"name":"latent_image","localized_name":"latent_image","type":"LATENT","shape":7,"link":3947},{"name":"guides","localized_name":"guides","type":"GUIDES","shape":7,"link":null},{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":null}],"outputs":[{"name":"output","localized_name":"output","type":"LATENT","links":[3991],"slot_index":0},{"name":"denoised","localized_name":"denoised","type":"LATENT","links":null},{"name":"options","localized_name":"options","type":"OPTIONS","links":null}],"properties":{"Node name for S&R":"ClownsharkChainsampler_Beta"},"widgets_values":[0.5,"exponential/res_2s",-1,4,"resample",true]},{"id":1412,"type":"ClownsharKSampler_Beta","pos":[18922.447265625,1091.1812744140625],"size":[281.48095703125,418],"flags":{},"order":33,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","shape":7,"link":3948},{"name":"positive","localized_name":"positive","type":"CONDITIONING","shape":7,"link":3949},{"name":"negative","localized_name":"negative","type":"CONDITIONING","shape":7,"link":3950},{"name":"latent_image","localized_name":"latent_image","type":"LATENT","shape":7,"link":3975},{"name":"sigmas","localized_name":"sigmas","type":"SIGMAS","shape":7,"link":null},{"name":"guides","localized_name":"guides","type":"GUIDES","shape":7,"link":null},{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":null}],"outputs":[{"name":"output","localized_name":"output","type":"LATENT","links":[3947],"slot_index":0},{"name":"denoised","localized_name":"denoised","type":"LATENT","links":null},{"name":"options","localized_name":"options","type":"OPTIONS","links":null}],"properties":{"Node name for S&R":"ClownsharKSampler_Beta","cnr_id":"RES4LYF","ver":"5ce9b5a77c227bf864e447a1e65305bf6cada5c2"},"widgets_values":[0.5,"exponential/res_2s","beta57",30,15,1,1,0,"fixed","unsample",true]},{"id":1406,"type":"SaveImage","pos":[19650,1900],"size":[457.3382263183594,422.2065124511719],"flags":{},"order":53,"mode":0,"inputs":[{"name":"images","localized_name":"images","type":"IMAGE","link":3923}],"outputs":[],"properties":{"Node name for S&R":"SaveImage","cnr_id":"comfy-core","ver":"0.3.29"},"widgets_values":["ComfyUI"]},{"id":1404,"type":"SaveImage","pos":[19650,1080],"size":[457.3382263183594,422.2065124511719],"flags":{},"order":45,"mode":0,"inputs":[{"name":"images","localized_name":"images","type":"IMAGE","link":3920}],"outputs":[],"properties":{"Node name for S&R":"SaveImage","cnr_id":"comfy-core","ver":"0.3.29"},"widgets_values":["ComfyUI"]}],"links":[[1344,404,0,14,0,"*"],[1395,13,0,431,0,"MODEL"],[1552,402,0,490,0,"*"],[3427,1261,0,1262,4,"LATENT"],[3429,14,0,1263,1,"VAE"],[3430,1263,0,1264,0,"IMAGE"],[3435,1262,0,1266,4,"LATENT"],[3436,1266,0,1265,4,"LATENT"],[3877,431,0,1383,0,"*"],[3879,1383,0,1261,0,"MODEL"],[3880,1382,0,1261,1,"CONDITIONING"],[3881,1381,0,1261,2,"CONDITIONING"],[3882,1380,0,1261,3,"LATENT"],[3886,431,0,1388,0,"*"],[3887,1388,0,1384,0,"MODEL"],[3888,1386,0,1384,1,"CONDITIONING"],[3889,1385,0,1384,2,"CONDITIONING"],[3890,1387,0,1384,3,"LATENT"],[3891,1388,0,1389,0,"MODEL"],[3892,1386,0,1389,1,"CONDITIONING"],[3893,1385,0,1389,2,"CONDITIONING"],[3895,1384,0,1389,3,"LATENT"],[3896,1388,0,1390,0,"MODEL"],[3897,1386,0,1390,1,"CONDITIONING"],[3898,1385,0,1390,2,"CONDITIONING"],[3900,1389,0,1390,3,"LATENT"],[3901,1388,0,1391,0,"MODEL"],[3902,1386,0,1391,1,"CONDITIONING"],[3903,1385,0,1391,2,"CONDITIONING"],[3905,1390,0,1391,3,"LATENT"],[3907,14,0,1392,1,"VAE"],[3908,1392,0,1393,0,"IMAGE"],[3912,431,0,1397,0,"*"],[3916,431,0,1401,0,"*"],[3919,14,0,1403,1,"VAE"],[3920,1403,0,1404,0,"IMAGE"],[3922,14,0,1405,1,"VAE"],[3923,1405,0,1406,0,"IMAGE"],[3924,1401,0,1394,0,"MODEL"],[3925,1399,0,1394,1,"CONDITIONING"],[3926,1402,0,1394,2,"CONDITIONING"],[3928,1407,0,1394,3,"SAMPLER"],[3929,1408,0,1394,4,"SIGMAS"],[3931,1401,0,1409,0,"MODEL"],[3932,1401,0,1410,0,"MODEL"],[3933,1399,0,1410,1,"CONDITIONING"],[3934,1402,0,1410,2,"CONDITIONING"],[3935,1407,0,1410,3,"SAMPLER"],[3938,1394,0,1410,5,"LATENT"],[3940,1409,0,1411,0,"SIGMAS"],[3941,1411,1,1408,0,"SIGMAS"],[3942,1411,1,1410,4,"SIGMAS"],[3947,1412,0,1413,4,"LATENT"],[3948,1397,0,1412,0,"MODEL"],[3949,1395,0,1412,1,"CONDITIONING"],[3950,1398,0,1412,2,"CONDITIONING"],[3959,490,0,1414,0,"CLIP"],[3960,490,0,1415,0,"CLIP"],[3961,1415,0,1381,0,"*"],[3962,1414,0,1382,0,"*"],[3963,1414,0,1386,0,"*"],[3964,1415,0,1385,0,"*"],[3965,1414,0,1395,0,"*"],[3966,1415,0,1398,0,"*"],[3967,1414,0,1399,0,"*"],[3968,1415,0,1402,0,"*"],[3969,14,0,1416,4,"VAE"],[3973,1417,0,1416,0,"IMAGE"],[3975,1416,0,1412,3,"LATENT"],[3976,1418,0,1419,0,"IMAGE"],[3977,1419,0,1420,0,"IMAGE"],[3979,1420,0,1394,5,"LATENT"],[3980,14,0,1420,1,"VAE"],[3985,1422,0,1380,0,"*"],[3986,1422,0,1387,0,"*"],[3987,1422,0,431,1,"LATENT"],[3988,403,0,13,0,"*"],[3989,1265,0,1263,0,"LATENT"],[3990,1391,0,1392,0,"LATENT"],[3991,1413,0,1403,0,"LATENT"],[3992,1410,0,1405,0,"LATENT"]],"groups":[],"config":{},"extra":{"ds":{"scale":0.9849732675807865,"offset":[-14560.618477888858,-446.28944651783576]},"VHS_latentpreview":false,"VHS_latentpreviewrate":0,"ue_links":[],"VHS_MetadataImage":true,"VHS_KeepIntermediate":true},"version":0.4} \ No newline at end of file diff --git a/ComfyUI/custom_nodes/RES4LYF/example_workflows/flux faceswap sync pulid.json b/ComfyUI/custom_nodes/RES4LYF/example_workflows/flux faceswap sync pulid.json new file mode 100644 index 0000000000000000000000000000000000000000..8e65fcbea13f6762e420500a5c4bfe5cb77eac98 --- /dev/null +++ b/ComfyUI/custom_nodes/RES4LYF/example_workflows/flux faceswap sync pulid.json @@ -0,0 +1 @@ +{"last_node_id":1741,"last_link_id":6622,"nodes":[{"id":490,"type":"Reroute","pos":[-1346.8087158203125,-823.3269653320312],"size":[75,26],"flags":{},"order":39,"mode":0,"inputs":[{"name":"","type":"*","link":6398}],"outputs":[{"name":"","type":"CLIP","links":[4157,6103],"slot_index":0}],"properties":{"showOutputText":false,"horizontal":false}},{"id":1162,"type":"Reroute","pos":[1930.0975341796875,-817.45556640625],"size":[75,26],"flags":{},"order":78,"mode":0,"inputs":[{"name":"","type":"*","link":4185}],"outputs":[{"name":"","type":"IMAGE","links":[4186],"slot_index":0}],"properties":{"showOutputText":false,"horizontal":false}},{"id":744,"type":"SaveImage","pos":[1276.456787109375,-719.9273681640625],"size":[424.53594970703125,455.0760192871094],"flags":{},"order":72,"mode":0,"inputs":[{"name":"images","localized_name":"images","type":"IMAGE","link":2241}],"outputs":[],"title":"Save Patch","properties":{"Node name for S&R":"SaveImage","cnr_id":"comfy-core","ver":"0.3.29"},"widgets_values":["ComfyUI"],"color":"#332922","bgcolor":"#593930"},{"id":1022,"type":"ImageBlend","pos":[2313.7607421875,-792.44091796875],"size":[210,102],"flags":{"collapsed":true},"order":73,"mode":0,"inputs":[{"name":"image1","localized_name":"image1","type":"IMAGE","link":3568},{"name":"image2","localized_name":"image2","type":"IMAGE","link":3570}],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":[3569],"slot_index":0}],"properties":{"Node name for S&R":"ImageBlend"},"widgets_values":[0.5,"multiply"]},{"id":729,"type":"SetImageSize","pos":[-812.6932373046875,-86.24114227294922],"size":[210,102],"flags":{},"order":0,"mode":0,"inputs":[],"outputs":[{"name":"width","localized_name":"width","type":"INT","links":[2104,2108,4998],"slot_index":0},{"name":"height","localized_name":"height","type":"INT","links":[2105,2109,4999],"slot_index":1}],"title":"Inpaint Tile Size","properties":{"Node name for S&R":"SetImageSize"},"widgets_values":[1024,1024]},{"id":1161,"type":"Image Save","pos":[2186.75634765625,-722.2388916015625],"size":[351.4677734375,796.8805541992188],"flags":{},"order":79,"mode":0,"inputs":[{"name":"images","localized_name":"images","type":"IMAGE","link":4186}],"outputs":[{"name":"images","localized_name":"images","type":"IMAGE","links":null},{"name":"files","localized_name":"files","type":"STRING","links":null}],"properties":{"Node name for S&R":"Image Save"},"widgets_values":["[time(%Y-%m-%d)]","ComfyUI","_",4,"false","jpeg",300,100,"true","false","false","false","true","true","true"],"color":"#232","bgcolor":"#353"},{"id":1024,"type":"PreviewImage","pos":[1286.05859375,-198.6599884033203],"size":[413.7582092285156,445.8081359863281],"flags":{},"order":76,"mode":0,"inputs":[{"name":"images","localized_name":"images","type":"IMAGE","link":3569}],"outputs":[],"properties":{"Node name for S&R":"PreviewImage"},"widgets_values":[],"color":"#332922","bgcolor":"#593930"},{"id":758,"type":"ImageResize+","pos":[1468.4384765625,-790.391845703125],"size":[210,218],"flags":{"collapsed":true},"order":71,"mode":0,"inputs":[{"name":"image","localized_name":"image","type":"IMAGE","link":2201},{"name":"width","type":"INT","pos":[10,76],"widget":{"name":"width"},"link":2204},{"name":"height","type":"INT","pos":[10,100],"widget":{"name":"height"},"link":2205}],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":[2198],"slot_index":0},{"name":"width","localized_name":"width","type":"INT","links":null},{"name":"height","localized_name":"height","type":"INT","links":null}],"properties":{"Node name for S&R":"ImageResize+"},"widgets_values":[512,512,"lanczos","stretch","always",0]},{"id":1369,"type":"ImageResize+","pos":[2183.37109375,151.09762573242188],"size":[210,218],"flags":{"collapsed":true},"order":44,"mode":0,"inputs":[{"name":"image","localized_name":"image","type":"IMAGE","link":4996},{"name":"width","type":"INT","pos":[10,76],"widget":{"name":"width"},"link":4998},{"name":"height","type":"INT","pos":[10,100],"widget":{"name":"height"},"link":4999}],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":[5000],"slot_index":0},{"name":"width","localized_name":"width","type":"INT","links":null},{"name":"height","localized_name":"height","type":"INT","links":null}],"properties":{"Node name for S&R":"ImageResize+"},"widgets_values":[512,512,"lanczos","stretch","always",0]},{"id":1407,"type":"Reroute","pos":[-914.50390625,-361.0196533203125],"size":[75,26],"flags":{},"order":37,"mode":0,"inputs":[{"name":"","type":"*","link":6620}],"outputs":[{"name":"","type":"MASK","links":[5021],"slot_index":0}],"properties":{"showOutputText":false,"horizontal":false}},{"id":725,"type":"Reroute","pos":[-914.8554077148438,-440.6482238769531],"size":[75,26],"flags":{},"order":36,"mode":0,"inputs":[{"name":"","type":"*","link":6619}],"outputs":[{"name":"","type":"IMAGE","links":[2210,2211,5054],"slot_index":0}],"properties":{"showOutputText":false,"horizontal":false}},{"id":14,"type":"Reroute","pos":[-1346.8087158203125,-783.3269653320312],"size":[75,26],"flags":{},"order":35,"mode":0,"inputs":[{"name":"","type":"*","link":5447}],"outputs":[{"name":"","type":"VAE","links":[2153,3508],"slot_index":0}],"properties":{"showOutputText":false,"horizontal":false}},{"id":1667,"type":"GrowMask","pos":[-302.060302734375,-164.22067260742188],"size":[210,82],"flags":{},"order":53,"mode":0,"inputs":[{"name":"mask","localized_name":"mask","type":"MASK","link":6360}],"outputs":[{"name":"MASK","localized_name":"MASK","type":"MASK","links":[6361],"slot_index":0}],"properties":{"Node name for S&R":"GrowMask"},"widgets_values":[-10,false]},{"id":1039,"type":"ImageBlend","pos":[-769.9498901367188,220.86917114257812],"size":[210,102],"flags":{"collapsed":true},"order":50,"mode":0,"inputs":[{"name":"image1","localized_name":"image1","type":"IMAGE","link":3606},{"name":"image2","localized_name":"image2","type":"IMAGE","link":3605}],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":[3607],"slot_index":0}],"properties":{"Node name for S&R":"ImageBlend"},"widgets_values":[0.5,"multiply"]},{"id":731,"type":"SimpleMath+","pos":[-776.4415893554688,126.82145690917969],"size":[315,98],"flags":{"collapsed":true},"order":33,"mode":0,"inputs":[{"name":"a","localized_name":"a","type":"*","shape":7,"link":2108},{"name":"b","localized_name":"b","type":"*","shape":7,"link":2109},{"name":"c","localized_name":"c","type":"*","shape":7,"link":null}],"outputs":[{"name":"INT","localized_name":"INT","type":"INT","links":null},{"name":"FLOAT","localized_name":"FLOAT","type":"FLOAT","links":[2100],"slot_index":1}],"properties":{"Node name for S&R":"SimpleMath+"},"widgets_values":["a/b"]},{"id":728,"type":"MaskToImage","pos":[-791.0198364257812,176.82147216796875],"size":[176.39999389648438,26],"flags":{"collapsed":true},"order":45,"mode":0,"inputs":[{"name":"mask","localized_name":"mask","type":"MASK","link":2106}],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":[2103,3605],"slot_index":0}],"properties":{"Node name for S&R":"MaskToImage"},"widgets_values":[]},{"id":765,"type":"MaskToImage","pos":[2080.868896484375,-792.6943359375],"size":[182.28543090820312,26],"flags":{"collapsed":true},"order":46,"mode":0,"inputs":[{"name":"mask","localized_name":"mask","type":"MASK","link":5529}],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":[3570],"slot_index":0}],"properties":{"Node name for S&R":"MaskToImage"},"widgets_values":[]},{"id":761,"type":"Image Comparer (rgthree)","pos":[1747.432373046875,-712.1251220703125],"size":[410.4466247558594,447.8973388671875],"flags":{},"order":77,"mode":0,"inputs":[{"name":"image_a","type":"IMAGE","dir":3,"link":2210},{"name":"image_b","type":"IMAGE","dir":3,"link":2200}],"outputs":[],"title":"Compare Output","properties":{"comparer_mode":"Slide"},"widgets_values":[[{"name":"A","selected":true,"url":"/api/view?filename=rgthree.compare._temp_lonqd_00061_.png&type=temp&subfolder=&rand=0.1196562401371497"},{"name":"B","selected":true,"url":"/api/view?filename=rgthree.compare._temp_lonqd_00062_.png&type=temp&subfolder=&rand=0.958614793318614"}]],"color":"#232","bgcolor":"#353"},{"id":1569,"type":"ClownGuides_Sync_Advanced","pos":[261.355224609375,-1000.5784912109375],"size":[315,1938],"flags":{"collapsed":true},"order":56,"mode":0,"inputs":[{"name":"guide_masked","localized_name":"guide_masked","type":"LATENT","shape":7,"link":6201},{"name":"guide_unmasked","localized_name":"guide_unmasked","type":"LATENT","shape":7,"link":6202},{"name":"mask","localized_name":"mask","type":"MASK","shape":7,"link":6223},{"name":"mask_sync","localized_name":"mask_sync","type":"MASK","shape":7,"link":6224},{"name":"mask_drift_x","localized_name":"mask_drift_x","type":"MASK","shape":7,"link":6225},{"name":"mask_drift_y","localized_name":"mask_drift_y","type":"MASK","shape":7,"link":6226},{"name":"mask_lure_x","localized_name":"mask_lure_x","type":"MASK","shape":7,"link":6227},{"name":"mask_lure_y","localized_name":"mask_lure_y","type":"MASK","shape":7,"link":6228},{"name":"weights_masked","localized_name":"weights_masked","type":"SIGMAS","shape":7,"link":null},{"name":"weights_unmasked","localized_name":"weights_unmasked","type":"SIGMAS","shape":7,"link":null},{"name":"syncs_masked","localized_name":"syncs_masked","type":"SIGMAS","shape":7,"link":null},{"name":"syncs_unmasked","localized_name":"syncs_unmasked","type":"SIGMAS","shape":7,"link":null},{"name":"drift_xs_masked","localized_name":"drift_xs_masked","type":"SIGMAS","shape":7,"link":null},{"name":"drift_xs_unmasked","localized_name":"drift_xs_unmasked","type":"SIGMAS","shape":7,"link":null},{"name":"drift_ys_masked","localized_name":"drift_ys_masked","type":"SIGMAS","shape":7,"link":null},{"name":"drift_ys_unmasked","localized_name":"drift_ys_unmasked","type":"SIGMAS","shape":7,"link":null},{"name":"lure_xs_masked","localized_name":"lure_xs_masked","type":"SIGMAS","shape":7,"link":null},{"name":"lure_xs_unmasked","localized_name":"lure_xs_unmasked","type":"SIGMAS","shape":7,"link":null},{"name":"lure_ys_masked","localized_name":"lure_ys_masked","type":"SIGMAS","shape":7,"link":null},{"name":"lure_ys_unmasked","localized_name":"lure_ys_unmasked","type":"SIGMAS","shape":7,"link":null},{"name":"drift_x_data","type":"FLOAT","pos":[10,800],"widget":{"name":"drift_x_data"},"link":6239},{"name":"drift_y_guide","type":"FLOAT","pos":[10,1088],"widget":{"name":"drift_y_guide"},"link":6240},{"name":"sync_masked","type":"FLOAT","pos":[10,608],"widget":{"name":"sync_masked"},"link":6241}],"outputs":[{"name":"guides","localized_name":"guides","type":"GUIDES","links":[6411],"slot_index":0}],"properties":{"Node name for S&R":"ClownGuides_Sync_Advanced"},"widgets_values":[1,1,"constant","constant",0,0,-1,-1,0,1,"constant","constant",0,0,-1,-1,0.2,0,1,0,"constant","constant",0,0,-1,-1,0,0,0.2,1,0,"constant","constant",0,0,-1,-1,0,0,"constant","constant",0,0,-1,-1,0,0,"constant","constant",0,0,-1,-1,0,"y -> x",false,false,false,false,false,false]},{"id":1571,"type":"Reroute","pos":[141.35520935058594,-1030.5784912109375],"size":[75,26],"flags":{},"order":52,"mode":0,"inputs":[{"name":"","type":"*","link":6222}],"outputs":[{"name":"","type":"MASK","links":[6223,6224,6225,6226,6227,6228,6342,6584],"slot_index":0}],"properties":{"showOutputText":false,"horizontal":false}},{"id":1368,"type":"Image Comparer (rgthree)","pos":[1744.9150390625,-199.16920471191406],"size":[410.4466247558594,447.8973388671875],"flags":{},"order":74,"mode":0,"inputs":[{"name":"image_a","type":"IMAGE","dir":3,"link":4997},{"name":"image_b","type":"IMAGE","dir":3,"link":5000}],"outputs":[],"title":"Compare Patch","properties":{"comparer_mode":"Slide"},"widgets_values":[[{"name":"A","selected":true,"url":"/api/view?filename=rgthree.compare._temp_fyekd_00061_.png&type=temp&subfolder=&rand=0.6117808776963016"},{"name":"B","selected":true,"url":"/api/view?filename=rgthree.compare._temp_fyekd_00062_.png&type=temp&subfolder=&rand=0.2735573488508416"}]],"color":"#232","bgcolor":"#353"},{"id":1673,"type":"Note","pos":[1824.9287109375,-1010.687744140625],"size":[322.34954833984375,88],"flags":{},"order":1,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["Preview of first stage output: sometimes it can be worth manually (or automatically, using DINO, etc.) adjusting your mask for the second stage, based on this output."],"color":"#432","bgcolor":"#653"},{"id":1539,"type":"GrowMask","pos":[573.4215698242188,-1145.86767578125],"size":[214.5684051513672,82],"flags":{},"order":57,"mode":4,"inputs":[{"name":"mask","localized_name":"mask","type":"MASK","link":6342}],"outputs":[{"name":"MASK","localized_name":"MASK","type":"MASK","links":[6343,6344,6345,6346,6347,6348],"slot_index":0}],"properties":{"Node name for S&R":"GrowMask"},"widgets_values":[10,false]},{"id":1383,"type":"Note","pos":[216.7359161376953,340.25775146484375],"size":[291.67218017578125,232.2296142578125],"flags":{},"order":2,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["eta > 0.0 means you are using SDE/ancestral sampling. With this guide mode you will generally want to use bongmath = true.\n\nSamplers such as res_2s and res_3s will be very accurate. Try res_5s and res_8s if you really want to go crazy with it. They run 2x (2s), 3x (3s), etc slower than Euler.\n\nres_2m and 3m will be fast and also good, and run at the same speed as Euler.\n\neta_substep will increase the power of bongmath. If it is set to 0.0, you can turn bongmath off without any effect."],"color":"#432","bgcolor":"#653"},{"id":1380,"type":"Note","pos":[544.9375610351562,342.0576477050781],"size":[290.1026611328125,231.5842742919922],"flags":{},"order":3,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["Setting denoise to a negative value is equivalent to just scaling it. For example:\n\nDenoise = -0.90 is the same as multiplying every sigma value in the entire schedule by 0.9.\n\nI find this is a lot easier to control than the regular denoise scale. The difference between -0.95 and -0.9 is much more predictable than with 0.95 and 0.9. Most of us have seen how different denoise 0.8 might be with Karras vs. exponential. \n\nTry a denoise between -0.95 and -0.85. "],"color":"#432","bgcolor":"#653"},{"id":759,"type":"ImageCompositeMasked","pos":[1697.19140625,-790.8740844726562],"size":[210,186],"flags":{"collapsed":true},"order":75,"mode":0,"inputs":[{"name":"destination","localized_name":"destination","type":"IMAGE","link":2211},{"name":"source","localized_name":"source","type":"IMAGE","link":2198},{"name":"mask","localized_name":"mask","type":"MASK","shape":7,"link":6447},{"name":"x","type":"INT","pos":[10,76],"widget":{"name":"x"},"link":2206},{"name":"y","type":"INT","pos":[10,100],"widget":{"name":"y"},"link":2207}],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":[2200,4185],"slot_index":0}],"properties":{"Node name for S&R":"ImageCompositeMasked"},"widgets_values":[712,800,false]},{"id":1687,"type":"Note","pos":[-101.33948516845703,339.7750244140625],"size":[286.97723388671875,180.28128051757812],"flags":{},"order":4,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["The cycles node causes the connected sampler to loop between sampling and unsampling steps. (Unsampling is running the sampler backwards, where it predicts the noise that would lead to a given output).\n\nWhen unsample_eta is set to -1, it simply uses the same settings for eta as in the connected node. "],"color":"#432","bgcolor":"#653"},{"id":745,"type":"VAEDecode","pos":[1297.53369140625,-791.137939453125],"size":[140,46],"flags":{"collapsed":true},"order":70,"mode":0,"inputs":[{"name":"samples","localized_name":"samples","type":"LATENT","link":6478},{"name":"vae","localized_name":"vae","type":"VAE","link":2153}],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":[2201,2241,3568,4997],"slot_index":0}],"properties":{"Node name for S&R":"VAEDecode","cnr_id":"comfy-core","ver":"0.3.29"},"widgets_values":[]},{"id":1678,"type":"Note","pos":[-422.92510986328125,-333.6911926269531],"size":[324.0018005371094,113.63665771484375],"flags":{},"order":5,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["ReduxAdvanced is used to help get things on track. Bypass if you're having problems with it disrupting character likeness.\n\nThe SDE Mask ensures SDE noise is used only in the masked area, limiting change in unmasked areas that could lead to seams. "],"color":"#432","bgcolor":"#653"},{"id":1572,"type":"ClownGuides_Sync_Advanced","pos":[581.355224609375,-1000.5784912109375],"size":[315,1878],"flags":{"collapsed":true},"order":62,"mode":0,"inputs":[{"name":"guide_masked","localized_name":"guide_masked","type":"LATENT","shape":7,"link":6229},{"name":"guide_unmasked","localized_name":"guide_unmasked","type":"LATENT","shape":7,"link":6230},{"name":"mask","localized_name":"mask","type":"MASK","shape":7,"link":6343},{"name":"mask_sync","localized_name":"mask_sync","type":"MASK","shape":7,"link":6344},{"name":"mask_drift_x","localized_name":"mask_drift_x","type":"MASK","shape":7,"link":6345},{"name":"mask_drift_y","localized_name":"mask_drift_y","type":"MASK","shape":7,"link":6346},{"name":"mask_lure_x","localized_name":"mask_lure_x","type":"MASK","shape":7,"link":6347},{"name":"mask_lure_y","localized_name":"mask_lure_y","type":"MASK","shape":7,"link":6348},{"name":"weights_masked","localized_name":"weights_masked","type":"SIGMAS","shape":7,"link":null},{"name":"weights_unmasked","localized_name":"weights_unmasked","type":"SIGMAS","shape":7,"link":null},{"name":"syncs_masked","localized_name":"syncs_masked","type":"SIGMAS","shape":7,"link":null},{"name":"syncs_unmasked","localized_name":"syncs_unmasked","type":"SIGMAS","shape":7,"link":null},{"name":"drift_xs_masked","localized_name":"drift_xs_masked","type":"SIGMAS","shape":7,"link":null},{"name":"drift_xs_unmasked","localized_name":"drift_xs_unmasked","type":"SIGMAS","shape":7,"link":null},{"name":"drift_ys_masked","localized_name":"drift_ys_masked","type":"SIGMAS","shape":7,"link":null},{"name":"drift_ys_unmasked","localized_name":"drift_ys_unmasked","type":"SIGMAS","shape":7,"link":null},{"name":"lure_xs_masked","localized_name":"lure_xs_masked","type":"SIGMAS","shape":7,"link":null},{"name":"lure_xs_unmasked","localized_name":"lure_xs_unmasked","type":"SIGMAS","shape":7,"link":null},{"name":"lure_ys_masked","localized_name":"lure_ys_masked","type":"SIGMAS","shape":7,"link":null},{"name":"lure_ys_unmasked","localized_name":"lure_ys_unmasked","type":"SIGMAS","shape":7,"link":null}],"outputs":[{"name":"guides","localized_name":"guides","type":"GUIDES","links":[6414],"slot_index":0}],"properties":{"Node name for S&R":"ClownGuides_Sync_Advanced"},"widgets_values":[0,1,"constant","constant",0,0,-1,-1,0,1,"constant","constant",0,0,-1,-1,0,0,1,0,"constant","constant",0,0,-1,-1,0,0,0,1,0,"constant","constant",0,0,-1,-1,0,0,"constant","constant",0,0,-1,-1,0,0,"constant","constant",0,0,-1,-1,0,"y -> x",false,false,false,false,false,false]},{"id":1693,"type":"Note","pos":[-1535.57666015625,-641.8590087890625],"size":[276.7918701171875,88],"flags":{},"order":6,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["Padding can be very important. Some models/loras/IPadapter embeds etc. are going to respond very differently if the shot is close up vs. farther away."],"color":"#432","bgcolor":"#653"},{"id":1694,"type":"Note","pos":[-441.5133056640625,-999.14990234375],"size":[291.2616882324219,189.98562622070312],"flags":{},"order":7,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["Increase character likeness by: \n\nDecreasing \"Similarity\"\nIncreasing \"Drift Toward Target\"\nIncreasing cycles\nIncreasing eta (max 1.0)\nIncreasing denoise\n\nIncrease adherence to the input image by:\n\nDoing the opposite of any of the above\nIncreasing \"Drift Toward Guide\"\nEnabling the ReduxAdvanced node\n"],"color":"#432","bgcolor":"#653"},{"id":1277,"type":"SharkOptions_GuideCond_Beta","pos":[575.9444580078125,221.88970947265625],"size":[315,98],"flags":{"collapsed":true},"order":51,"mode":0,"inputs":[{"name":"positive","localized_name":"positive","type":"CONDITIONING","shape":7,"link":5653},{"name":"negative","localized_name":"negative","type":"CONDITIONING","shape":7,"link":4650},{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":null}],"outputs":[{"name":"options","localized_name":"options","type":"OPTIONS","links":[5493],"slot_index":0}],"properties":{"Node name for S&R":"SharkOptions_GuideCond_Beta"},"widgets_values":[1]},{"id":1040,"type":"PreviewImage","pos":[-1267.6248779296875,-30.252229690551758],"size":[304.98114013671875,265.58380126953125],"flags":{},"order":55,"mode":0,"inputs":[{"name":"images","localized_name":"images","type":"IMAGE","link":3607}],"outputs":[],"properties":{"Node name for S&R":"PreviewImage"},"widgets_values":[]},{"id":1698,"type":"Note","pos":[-1623.859375,-355.951416015625],"size":[276.7918701171875,88],"flags":{},"order":8,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["Draw a mask over the face in the Load Image node. Ideally, try stopping precisely at the hairline, and just above or just below the chin."],"color":"#432","bgcolor":"#653"},{"id":1477,"type":"LoraLoader","pos":[-1684.5245361328125,-845.994140625],"size":[315,126],"flags":{},"order":34,"mode":4,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","link":5439},{"name":"clip","localized_name":"clip","type":"CLIP","link":5440}],"outputs":[{"name":"MODEL","localized_name":"MODEL","type":"MODEL","links":[6397],"slot_index":0},{"name":"CLIP","localized_name":"CLIP","type":"CLIP","links":[6398],"slot_index":1}],"properties":{"Node name for S&R":"LoraLoader"},"widgets_values":["FLUX/Kirsten_Dunst_Flux_V1.safetensors",1,1]},{"id":1279,"type":"TorchCompileModels","pos":[-2086.55322265625,-1090.6181640625],"size":[285.9945068359375,179.0001983642578],"flags":{},"order":38,"mode":4,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","link":6397}],"outputs":[{"name":"model","localized_name":"model","type":"MODEL","links":[6396],"slot_index":0}],"properties":{"Node name for S&R":"TorchCompileModels"},"widgets_values":["inductor",false,"default",false,64,0]},{"id":1478,"type":"ModelSamplingAdvancedResolution","pos":[-1773.91259765625,-1030.6773681640625],"size":[260.3999938964844,126],"flags":{},"order":54,"mode":4,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","link":6396},{"name":"latent_image","localized_name":"latent_image","type":"LATENT","link":5442}],"outputs":[{"name":"model","localized_name":"model","type":"MODEL","links":[6383],"slot_index":0}],"properties":{"Node name for S&R":"ModelSamplingAdvancedResolution"},"widgets_values":["exponential",1.35,0.85]},{"id":1454,"type":"ClownOptions_Cycles_Beta","pos":[-74.8967514038086,24.043270111083984],"size":[261.7955627441406,202],"flags":{},"order":9,"mode":0,"inputs":[{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":null}],"outputs":[{"name":"options","localized_name":"options","type":"OPTIONS","links":[6402],"slot_index":0}],"properties":{"Node name for S&R":"ClownOptions_Cycles_Beta"},"widgets_values":[20,1,-1,"none",-1,1,true]},{"id":726,"type":"Mask Bounding Box Aspect Ratio","pos":[-828.6614990234375,-412.50946044921875],"size":[252,250],"flags":{"collapsed":false},"order":40,"mode":0,"inputs":[{"name":"image","localized_name":"image","type":"IMAGE","shape":7,"link":5054},{"name":"mask","localized_name":"mask","type":"MASK","shape":7,"link":5021},{"name":"aspect_ratio","type":"FLOAT","pos":[10,204],"widget":{"name":"aspect_ratio"},"link":2100}],"outputs":[{"name":"image","localized_name":"image","type":"IMAGE","links":[2101,2102,3606,3721,4996,6543],"slot_index":0},{"name":"mask","localized_name":"mask","type":"MASK","links":[2106,5529],"slot_index":1},{"name":"mask_blurred","localized_name":"mask_blurred","type":"MASK","links":[6447],"slot_index":2},{"name":"x","localized_name":"x","type":"INT","links":[2206],"slot_index":3},{"name":"y","localized_name":"y","type":"INT","links":[2207],"slot_index":4},{"name":"width","localized_name":"width","type":"INT","links":[2204],"slot_index":5},{"name":"height","localized_name":"height","type":"INT","links":[2205],"slot_index":6}],"properties":{"Node name for S&R":"Mask Bounding Box Aspect Ratio"},"widgets_values":[100,40,1.75,false]},{"id":1702,"type":"PulidFluxInsightFaceLoader","pos":[-1150,-1080],"size":[365.4000244140625,58],"flags":{"collapsed":true},"order":10,"mode":0,"inputs":[],"outputs":[{"name":"FACEANALYSIS","localized_name":"FACEANALYSIS","type":"FACEANALYSIS","shape":3,"links":[6526],"slot_index":0}],"properties":{"Node name for S&R":"PulidFluxInsightFaceLoader"},"widgets_values":["CPU"]},{"id":1524,"type":"ReFluxPatcher","pos":[-1486.33251953125,-986.468505859375],"size":[210,82],"flags":{},"order":60,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","link":6383}],"outputs":[{"name":"model","localized_name":"model","type":"MODEL","links":[6547],"slot_index":0}],"properties":{"Node name for S&R":"ReFluxPatcher"},"widgets_values":["float64",true]},{"id":13,"type":"Reroute","pos":[-1346.8087158203125,-863.3270874023438],"size":[75,26],"flags":{},"order":64,"mode":0,"inputs":[{"name":"","type":"*","link":6547}],"outputs":[{"name":"","type":"MODEL","links":[6548],"slot_index":0}],"properties":{"showOutputText":false,"horizontal":false}},{"id":1703,"type":"PulidFluxModelLoader","pos":[-1140,-970],"size":[315,58],"flags":{"collapsed":true},"order":11,"mode":0,"inputs":[],"outputs":[{"name":"PULIDFLUX","localized_name":"PULIDFLUX","type":"PULIDFLUX","shape":3,"links":[6524],"slot_index":0}],"properties":{"Node name for S&R":"PulidFluxModelLoader"},"widgets_values":["pulid_flux_v0.9.0.safetensors"]},{"id":1688,"type":"Note","pos":[-1527.4205322265625,-1311.8199462890625],"size":[274.47601318359375,104.34856414794922],"flags":{},"order":12,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["ReFluxPatcher is required to use the \"Style\" nodes. Different \"Re...Patcher\" nodes are available for many other models, from SD1.5/SDXL to SD3.5, HiDream, AuraFlow, Chroma, WAN, and LTXV."],"color":"#432","bgcolor":"#653"},{"id":1071,"type":"CLIPVisionEncode","pos":[586.1533203125,119.24115753173828],"size":[253.60000610351562,78],"flags":{"collapsed":true},"order":43,"mode":0,"inputs":[{"name":"clip_vision","localized_name":"clip_vision","type":"CLIP_VISION","link":6552},{"name":"image","localized_name":"image","type":"IMAGE","link":3721}],"outputs":[{"name":"CLIP_VISION_OUTPUT","localized_name":"CLIP_VISION_OUTPUT","type":"CLIP_VISION_OUTPUT","links":[3720],"slot_index":0}],"properties":{"Node name for S&R":"CLIPVisionEncode"},"widgets_values":["center"]},{"id":1073,"type":"CLIPTextEncode","pos":[575.77001953125,186.9269256591797],"size":[263.280517578125,88.73566436767578],"flags":{"collapsed":true},"order":41,"mode":0,"inputs":[{"name":"clip","localized_name":"clip","type":"CLIP","link":4157}],"outputs":[{"name":"CONDITIONING","localized_name":"CONDITIONING","type":"CONDITIONING","links":[4650,4980],"slot_index":0}],"properties":{"Node name for S&R":"CLIPTextEncode","cnr_id":"comfy-core","ver":"0.3.29"},"widgets_values":[""]},{"id":1476,"type":"FluxLoader","pos":[-2094.3544921875,-847.2406005859375],"size":[385.17449951171875,282],"flags":{},"order":13,"mode":0,"inputs":[],"outputs":[{"name":"model","localized_name":"model","type":"MODEL","links":[5439],"slot_index":0},{"name":"clip","localized_name":"clip","type":"CLIP","links":[5440],"slot_index":1},{"name":"vae","localized_name":"vae","type":"VAE","links":[5447],"slot_index":2},{"name":"clip_vision","localized_name":"clip_vision","type":"CLIP_VISION","links":[6550,6552],"slot_index":3},{"name":"style_model","localized_name":"style_model","type":"STYLE_MODEL","links":[6551,6553],"slot_index":4}],"properties":{"Node name for S&R":"FluxLoader"},"widgets_values":["colossusProjectFlux_v42AIO.safetensors","fp8_e4m3fn_fast",".use_ckpt_clip",".none",".use_ckpt_vae","siglip2-so400m-patch16-512.safetensors","flex1_redux_siglip2_512.safetensors"]},{"id":1716,"type":"Note","pos":[-2101.239013671875,-463.0836486816406],"size":[395.2708740234375,177.91754150390625],"flags":{},"order":14,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["To use the 512x512 Redux models, download and place in the following paths:\n\ncomfy/models/style_models:\nhttps://huggingface.co/ostris/Flex.1-alpha-Redux/blob/main/flex1_redux_siglip2_512.safetensors\n\ncomfy/models/clip_vision:\nhttps://huggingface.co/google/siglip2-so400m-patch16-512/blob/main/model.safetensors\n\nRename the latter as siglip2-so400m-patch16-512.safetensors"],"color":"#432","bgcolor":"#653"},{"id":1701,"type":"PulidFluxEvaClipLoader","pos":[-1145.7685546875,-1024.2314453125],"size":[327.5999755859375,26],"flags":{"collapsed":true},"order":15,"mode":0,"inputs":[],"outputs":[{"name":"EVA_CLIP","localized_name":"EVA_CLIP","type":"EVA_CLIP","shape":3,"links":[6525],"slot_index":0}],"properties":{"Node name for S&R":"PulidFluxEvaClipLoader"},"widgets_values":[]},{"id":1548,"type":"ReduxAdvanced","pos":[-69.81456756591797,-498.3502502441406],"size":[248.6250457763672,234],"flags":{},"order":47,"mode":4,"inputs":[{"name":"conditioning","localized_name":"conditioning","type":"CONDITIONING","link":6422},{"name":"style_model","localized_name":"style_model","type":"STYLE_MODEL","link":6551},{"name":"clip_vision","localized_name":"clip_vision","type":"CLIP_VISION","link":6550},{"name":"image","localized_name":"image","type":"IMAGE","link":6543},{"name":"mask","localized_name":"mask","type":"MASK","shape":7,"link":null}],"outputs":[{"name":"CONDITIONING","localized_name":"CONDITIONING","type":"CONDITIONING","links":[6421],"slot_index":0},{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":null},{"name":"MASK","localized_name":"MASK","type":"MASK","links":null}],"properties":{"Node name for S&R":"ReduxAdvanced"},"widgets_values":[3,"area","center crop (square)",1,0.1]},{"id":1072,"type":"StyleModelApply","pos":[596.4773559570312,153.7720947265625],"size":[262,122],"flags":{"collapsed":true},"order":48,"mode":0,"inputs":[{"name":"conditioning","localized_name":"conditioning","type":"CONDITIONING","link":4980},{"name":"style_model","localized_name":"style_model","type":"STYLE_MODEL","link":6553},{"name":"clip_vision_output","localized_name":"clip_vision_output","type":"CLIP_VISION_OUTPUT","link":3720}],"outputs":[{"name":"CONDITIONING","localized_name":"CONDITIONING","type":"CONDITIONING","links":[5653],"slot_index":0}],"properties":{"Node name for S&R":"StyleModelApply"},"widgets_values":[1,"multiply"]},{"id":1714,"type":"Note","pos":[-816.8351440429688,-725.0016479492188],"size":[252.3572998046875,162.81890869140625],"flags":{},"order":16,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["The repo for PuLID Flux is currently broken, but the ReFluxPatcher node will repair the issues and make it usable. You must have ReFluxPatcher enabled to use this. Aside from that, install as instructed:\n\nhttps://github.com/balazik/ComfyUI-PuLID-Flux\n\n"],"color":"#432","bgcolor":"#653"},{"id":1575,"type":"PrimitiveFloat","pos":[11.355203628540039,-940.5784912109375],"size":[210,58],"flags":{},"order":17,"mode":0,"inputs":[],"outputs":[{"name":"FLOAT","localized_name":"FLOAT","type":"FLOAT","links":[6241],"slot_index":0}],"title":"Similarity","properties":{"Node name for S&R":"PrimitiveFloat"},"widgets_values":[1]},{"id":1573,"type":"PrimitiveFloat","pos":[10.393571853637695,-834.4251708984375],"size":[210,58],"flags":{},"order":18,"mode":0,"inputs":[],"outputs":[{"name":"FLOAT","localized_name":"FLOAT","type":"FLOAT","links":[6239],"slot_index":0}],"title":"Drift Toward Target","properties":{"Node name for S&R":"PrimitiveFloat"},"widgets_values":[0.2]},{"id":1574,"type":"PrimitiveFloat","pos":[11.355203628540039,-720.5784912109375],"size":[210,58],"flags":{},"order":19,"mode":0,"inputs":[],"outputs":[{"name":"FLOAT","localized_name":"FLOAT","type":"FLOAT","links":[6240],"slot_index":0}],"title":"Drift Toward Guide","properties":{"Node name for S&R":"PrimitiveFloat"},"widgets_values":[0.2]},{"id":727,"type":"VAEEncodeAdvanced","pos":[-789.0958862304688,67.53204345703125],"size":[262.4812927246094,298],"flags":{"collapsed":true},"order":49,"mode":0,"inputs":[{"name":"image_1","localized_name":"image_1","type":"IMAGE","shape":7,"link":2101},{"name":"image_2","localized_name":"image_2","type":"IMAGE","shape":7,"link":2102},{"name":"mask","localized_name":"mask","type":"IMAGE","shape":7,"link":2103},{"name":"latent","localized_name":"latent","type":"LATENT","shape":7,"link":null},{"name":"vae","localized_name":"vae","type":"VAE","shape":7,"link":3508},{"name":"width","type":"INT","pos":[10,160],"widget":{"name":"width"},"link":2104},{"name":"height","type":"INT","pos":[10,184],"widget":{"name":"height"},"link":2105}],"outputs":[{"name":"latent_1","localized_name":"latent_1","type":"LATENT","links":[5373,5715,6201,6202,6229,6230,6412],"slot_index":0},{"name":"latent_2","localized_name":"latent_2","type":"LATENT","links":[],"slot_index":1},{"name":"mask","localized_name":"mask","type":"MASK","links":[6222,6360,6569,6570],"slot_index":2},{"name":"empty_latent","localized_name":"empty_latent","type":"LATENT","links":[5442],"slot_index":3},{"name":"width","localized_name":"width","type":"INT","links":[],"slot_index":4},{"name":"height","localized_name":"height","type":"INT","links":[]}],"properties":{"Node name for S&R":"VAEEncodeAdvanced"},"widgets_values":["false",1344,768,"red",false,"16_channels"]},{"id":1674,"type":"Note","pos":[170.8737030029297,-1390.4803466796875],"size":[322.6287841796875,128.15802001953125],"flags":{},"order":20,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["Activate the style nodes if you are having issues with color, detail, light, blurriness or pixelation drifting too far from your source input.\n\nIf end_step is too high, you may get faint halos and an oversharpened look."],"color":"#432","bgcolor":"#653"},{"id":1689,"type":"Note","pos":[525.9268798828125,-1349.89794921875],"size":[263.00439453125,88],"flags":{},"order":21,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["Expanding the mask for the second pass can sometimes help prevent seams."],"color":"#432","bgcolor":"#653"},{"id":1525,"type":"ClownGuide_Style_Beta","pos":[251.35520935058594,-950.5784912109375],"size":[252.0535430908203,286],"flags":{},"order":61,"mode":0,"inputs":[{"name":"guide","localized_name":"guide","type":"LATENT","shape":7,"link":5715},{"name":"mask","localized_name":"mask","type":"MASK","shape":7,"link":6569},{"name":"weights","localized_name":"weights","type":"SIGMAS","shape":7,"link":null},{"name":"guides","localized_name":"guides","type":"GUIDES","shape":7,"link":6411}],"outputs":[{"name":"guides","localized_name":"guides","type":"GUIDES","links":[6051],"slot_index":0}],"properties":{"Node name for S&R":"ClownGuide_Style_Beta"},"widgets_values":["positive","scattersort",1,1,"constant",0,-1,false]},{"id":1672,"type":"ClownGuide_Style_Beta","pos":[561.355224609375,-950.5784912109375],"size":[252.0535430908203,286],"flags":{},"order":65,"mode":0,"inputs":[{"name":"guide","localized_name":"guide","type":"LATENT","shape":7,"link":6412},{"name":"mask","localized_name":"mask","type":"MASK","shape":7,"link":6570},{"name":"weights","localized_name":"weights","type":"SIGMAS","shape":7,"link":null},{"name":"guides","localized_name":"guides","type":"GUIDES","shape":7,"link":6414}],"outputs":[{"name":"guides","localized_name":"guides","type":"GUIDES","links":[6415,6476],"slot_index":0}],"properties":{"Node name for S&R":"ClownGuide_Style_Beta"},"widgets_values":["positive","scattersort",1,1,"constant",0,-1,false]},{"id":1516,"type":"ClownOptions_SDE_Mask_Beta","pos":[-68.4439468383789,-163.1180877685547],"size":[252.8383331298828,126],"flags":{},"order":59,"mode":0,"inputs":[{"name":"mask","localized_name":"mask","type":"MASK","shape":7,"link":6361},{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":null}],"outputs":[{"name":"options","localized_name":"options","type":"OPTIONS","links":[5776],"slot_index":0}],"properties":{"Node name for S&R":"ClownOptions_SDE_Mask_Beta"},"widgets_values":[1,0,false]},{"id":1731,"type":"ClownOptions_SDE_Mask_Beta","pos":[898.4906005859375,-756.2548217773438],"size":[252.8383331298828,126],"flags":{},"order":63,"mode":0,"inputs":[{"name":"mask","localized_name":"mask","type":"MASK","shape":7,"link":6586},{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":null}],"outputs":[{"name":"options","localized_name":"options","type":"OPTIONS","links":[6585,6587],"slot_index":0}],"properties":{"Node name for S&R":"ClownOptions_SDE_Mask_Beta"},"widgets_values":[1,0,false]},{"id":1730,"type":"MaskEdge","pos":[903.2994384765625,-949.55322265625],"size":[248.64459228515625,130],"flags":{},"order":58,"mode":0,"inputs":[{"name":"mask","localized_name":"mask","type":"MASK","link":6584}],"outputs":[{"name":"edge_mask","localized_name":"edge_mask","type":"MASK","links":[6586],"slot_index":0}],"properties":{"Node name for S&R":"MaskEdge"},"widgets_values":[10,"percent",1,1]},{"id":1677,"type":"Note","pos":[-439.5185241699219,-738.3756713867188],"size":[290.3874816894531,88],"flags":{},"order":22,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["Try setting both drift values to 0.0 or 0.2 as a starting point.\n"],"color":"#432","bgcolor":"#653"},{"id":1552,"type":"ClownOptions_SDE_Beta","pos":[-271.7193603515625,259.6875915527344],"size":[315,266],"flags":{"collapsed":true},"order":23,"mode":0,"inputs":[{"name":"etas","localized_name":"etas","type":"SIGMAS","shape":7,"link":null},{"name":"etas_substep","localized_name":"etas_substep","type":"SIGMAS","shape":7,"link":null},{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":null}],"outputs":[{"name":"options","localized_name":"options","type":"OPTIONS","links":[],"slot_index":0}],"properties":{"Node name for S&R":"ClownOptions_SDE_Beta"},"widgets_values":["gaussian","gaussian","hard","hard",1,1,-1,"fixed"]},{"id":1726,"type":"ClownOptions_ImplicitSteps_Beta","pos":[-493.06549072265625,258.3205871582031],"size":[300.7710876464844,130],"flags":{"collapsed":true},"order":24,"mode":0,"inputs":[{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":null}],"outputs":[{"name":"options","localized_name":"options","type":"OPTIONS","links":null}],"properties":{"Node name for S&R":"ClownOptions_ImplicitSteps_Beta"},"widgets_values":["bongmath","bongmath",10,0]},{"id":1722,"type":"ClownOptions_DetailBoost_Beta","pos":[-302.6524963378906,-24.413410186767578],"size":[210.1761016845703,218],"flags":{"collapsed":false},"order":25,"mode":0,"inputs":[{"name":"weights","localized_name":"weights","type":"SIGMAS","shape":7,"link":null},{"name":"etas","localized_name":"etas","type":"SIGMAS","shape":7,"link":null},{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":null}],"outputs":[{"name":"options","localized_name":"options","type":"OPTIONS","links":[6589,6590,6591],"slot_index":0}],"properties":{"Node name for S&R":"ClownOptions_DetailBoost_Beta"},"widgets_values":[1,"model","hard",0.5,3,10]},{"id":1732,"type":"Note","pos":[890.6793823242188,-1148.8226318359375],"size":[290.3854675292969,122.62060546875],"flags":{},"order":26,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["The mask below allows the SDE/ancestral noise used in the last two samplers to only hit the seams around the inpainted area.\n\nTry bypassing the SDE mask and see if you like the results - it lets the entire face be affected by noise."],"color":"#432","bgcolor":"#653"},{"id":1727,"type":"Note","pos":[-453.12371826171875,343.8135681152344],"size":[296.5935363769531,187.9747314453125],"flags":{},"order":27,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["ClownOptions Detail gives a boost to detail a lot like the \"Detail Daemon\" node, though I think with somewhat less risk of mutations and loss of saturation. Change \"weight\", \"eta\", or \"end_step\" to control strength.\n\nImplicit steps can be used in place of \"Cycles\". Try setting steps_to_run to 3 or 4 if you use it.\n\nClownOptions SDE contains extra settings for noise, so you can change the type, amount, etc. with more precision."],"color":"#432","bgcolor":"#653"},{"id":1733,"type":"Note","pos":[-819.1915893554688,-1111.3170166015625],"size":[251.92019653320312,88],"flags":{},"order":28,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["Try changing the weight or end_at if results look plastic."],"color":"#432","bgcolor":"#653"},{"id":1704,"type":"ApplyPulidFlux","pos":[-805.7684326171875,-986.1819458007812],"size":[219.79336547851562,206],"flags":{},"order":66,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","link":6548},{"name":"pulid_flux","localized_name":"pulid_flux","type":"PULIDFLUX","link":6524},{"name":"eva_clip","localized_name":"eva_clip","type":"EVA_CLIP","link":6525},{"name":"face_analysis","localized_name":"face_analysis","type":"FACEANALYSIS","link":6526},{"name":"image","localized_name":"image","type":"IMAGE","link":null},{"name":"attn_mask","localized_name":"attn_mask","type":"MASK","shape":7,"link":null}],"outputs":[{"name":"MODEL","localized_name":"MODEL","type":"MODEL","shape":3,"links":[6549],"slot_index":0}],"properties":{"Node name for S&R":"ApplyPulidFlux"},"widgets_values":[1,0,1]},{"id":1737,"type":"Note","pos":[-1184.4395751953125,-1304.4234619140625],"size":[251.92019653320312,88],"flags":{},"order":29,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["The image you choose is very important. The face should have its proportions clearly distinguishable."],"color":"#432","bgcolor":"#653"},{"id":1717,"type":"LoadImage","pos":[-603.783203125,-1602.01904296875],"size":[315,314],"flags":{},"order":30,"mode":0,"inputs":[],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":[],"slot_index":0},{"name":"MASK","localized_name":"MASK","type":"MASK","links":null}],"properties":{"Node name for S&R":"LoadImage"},"widgets_values":["pasted/image (812).png","image"]},{"id":1446,"type":"ClownsharKSampler_Beta","pos":[214.812255859375,-508.00537109375],"size":[277.5089111328125,735.1378784179688],"flags":{},"order":67,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","shape":7,"link":6549},{"name":"positive","localized_name":"positive","type":"CONDITIONING","shape":7,"link":6421},{"name":"negative","localized_name":"negative","type":"CONDITIONING","shape":7,"link":null},{"name":"latent_image","localized_name":"latent_image","type":"LATENT","shape":7,"link":5373},{"name":"sigmas","localized_name":"sigmas","type":"SIGMAS","shape":7,"link":null},{"name":"guides","localized_name":"guides","type":"GUIDES","shape":7,"link":6051},{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":5493},{"name":"options 2","type":"OPTIONS","link":5776},{"name":"options 3","type":"OPTIONS","link":6402},{"name":"options 4","type":"OPTIONS","link":6589},{"name":"options 5","type":"OPTIONS","link":null}],"outputs":[{"name":"output","localized_name":"output","type":"LATENT","links":[6380],"slot_index":0},{"name":"denoised","localized_name":"denoised","type":"LATENT","links":[],"slot_index":1},{"name":"options","localized_name":"options","type":"OPTIONS","links":null}],"properties":{"Node name for S&R":"ClownsharKSampler_Beta","cnr_id":"RES4LYF","ver":"5ce9b5a77c227bf864e447a1e65305bf6cada5c2"},"widgets_values":[1,"exponential/res_2s","bong_tangent",30,1,0.65,1,100,"fixed","standard",true],"color":"#332922","bgcolor":"#593930"},{"id":1556,"type":"CLIPTextEncode","pos":[-392.6881408691406,-498.2940979003906],"size":[289.0962829589844,113.79679870605469],"flags":{"collapsed":false},"order":42,"mode":0,"inputs":[{"name":"clip","localized_name":"clip","type":"CLIP","link":6103}],"outputs":[{"name":"CONDITIONING","localized_name":"CONDITIONING","type":"CONDITIONING","links":[6422],"slot_index":0}],"properties":{"Node name for S&R":"CLIPTextEncode","cnr_id":"comfy-core","ver":"0.3.29"},"widgets_values":[""],"color":"#2a363b","bgcolor":"#3f5159"},{"id":1707,"type":"LoadImage","pos":[-1272.3699951171875,-406.4196472167969],"size":[315,314],"flags":{},"order":31,"mode":0,"inputs":[],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":[6619],"slot_index":0},{"name":"MASK","localized_name":"MASK","type":"MASK","links":[6620],"slot_index":1}],"properties":{"Node name for S&R":"LoadImage"},"widgets_values":["clipspace/clipspace-mask-18464655.700000048.png [input]","image"]},{"id":1740,"type":"Note","pos":[-892.4718627929688,-1299.925048828125],"size":[251.92019653320312,88],"flags":{},"order":32,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["PuLID will copy much of the lighting and especially position/angle of the face. Keep this in mind."],"color":"#432","bgcolor":"#653"},{"id":1690,"type":"ClownsharkChainsampler_Beta","pos":[865.4187622070312,-518.0064086914062],"size":[281.7781677246094,571.74853515625],"flags":{},"order":69,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","shape":7,"link":null},{"name":"positive","localized_name":"positive","type":"CONDITIONING","shape":7,"link":null},{"name":"negative","localized_name":"negative","type":"CONDITIONING","shape":7,"link":null},{"name":"sigmas","localized_name":"sigmas","type":"SIGMAS","shape":7,"link":null},{"name":"latent_image","localized_name":"latent_image","type":"LATENT","shape":7,"link":6566},{"name":"guides","localized_name":"guides","type":"GUIDES","shape":7,"link":6476},{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":6587},{"name":"options 2","type":"OPTIONS","link":6591},{"name":"options 3","type":"OPTIONS","link":null}],"outputs":[{"name":"output","localized_name":"output","type":"LATENT","links":[6478],"slot_index":0},{"name":"denoised","localized_name":"denoised","type":"LATENT","links":null},{"name":"options","localized_name":"options","type":"OPTIONS","links":null}],"properties":{"Node name for S&R":"ClownsharkChainsampler_Beta"},"widgets_values":[0,"multistep/res_3m",-1,1,"resample",false]},{"id":1479,"type":"ClownsharkChainsampler_Beta","pos":[536.1533203125,-510.75872802734375],"size":[288.1370544433594,571.74853515625],"flags":{},"order":68,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","shape":7,"link":null},{"name":"positive","localized_name":"positive","type":"CONDITIONING","shape":7,"link":null},{"name":"negative","localized_name":"negative","type":"CONDITIONING","shape":7,"link":null},{"name":"sigmas","localized_name":"sigmas","type":"SIGMAS","shape":7,"link":null},{"name":"latent_image","localized_name":"latent_image","type":"LATENT","shape":7,"link":6380},{"name":"guides","localized_name":"guides","type":"GUIDES","shape":7,"link":6415},{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":6585},{"name":"options 2","type":"OPTIONS","link":6590},{"name":"options 3","type":"OPTIONS","link":null}],"outputs":[{"name":"output","localized_name":"output","type":"LATENT","links":[6566],"slot_index":0},{"name":"denoised","localized_name":"denoised","type":"LATENT","links":null},{"name":"options","localized_name":"options","type":"OPTIONS","links":null}],"properties":{"Node name for S&R":"ClownsharkChainsampler_Beta"},"widgets_values":[0,"exponential/res_2s",2,1,"resample",true]}],"links":[[2100,731,1,726,2,"FLOAT"],[2101,726,0,727,0,"IMAGE"],[2102,726,0,727,1,"IMAGE"],[2103,728,0,727,2,"IMAGE"],[2104,729,0,727,5,"INT"],[2105,729,1,727,6,"INT"],[2106,726,1,728,0,"MASK"],[2108,729,0,731,0,"*"],[2109,729,1,731,1,"*"],[2153,14,0,745,1,"VAE"],[2198,758,0,759,1,"IMAGE"],[2200,759,0,761,1,"IMAGE"],[2201,745,0,758,0,"IMAGE"],[2204,726,5,758,1,"INT"],[2205,726,6,758,2,"INT"],[2206,726,3,759,3,"INT"],[2207,726,4,759,4,"INT"],[2210,725,0,761,0,"IMAGE"],[2211,725,0,759,0,"IMAGE"],[2241,745,0,744,0,"IMAGE"],[3508,14,0,727,4,"VAE"],[3568,745,0,1022,0,"IMAGE"],[3569,1022,0,1024,0,"IMAGE"],[3570,765,0,1022,1,"IMAGE"],[3605,728,0,1039,1,"IMAGE"],[3606,726,0,1039,0,"IMAGE"],[3607,1039,0,1040,0,"IMAGE"],[3720,1071,0,1072,2,"CLIP_VISION_OUTPUT"],[3721,726,0,1071,1,"IMAGE"],[4157,490,0,1073,0,"CLIP"],[4185,759,0,1162,0,"*"],[4186,1162,0,1161,0,"IMAGE"],[4650,1073,0,1277,1,"CONDITIONING"],[4980,1073,0,1072,0,"CONDITIONING"],[4996,726,0,1369,0,"IMAGE"],[4997,745,0,1368,0,"IMAGE"],[4998,729,0,1369,1,"INT"],[4999,729,1,1369,2,"INT"],[5000,1369,0,1368,1,"IMAGE"],[5021,1407,0,726,1,"MASK"],[5054,725,0,726,0,"IMAGE"],[5373,727,0,1446,3,"LATENT"],[5439,1476,0,1477,0,"MODEL"],[5440,1476,1,1477,1,"CLIP"],[5442,727,3,1478,1,"LATENT"],[5447,1476,2,14,0,"*"],[5493,1277,0,1446,6,"OPTIONS"],[5529,726,1,765,0,"MASK"],[5653,1072,0,1277,0,"CONDITIONING"],[5715,727,0,1525,0,"LATENT"],[5776,1516,0,1446,7,"OPTIONS"],[6051,1525,0,1446,5,"GUIDES"],[6103,490,0,1556,0,"CLIP"],[6201,727,0,1569,0,"LATENT"],[6202,727,0,1569,1,"LATENT"],[6222,727,2,1571,0,"*"],[6223,1571,0,1569,2,"MASK"],[6224,1571,0,1569,3,"MASK"],[6225,1571,0,1569,4,"MASK"],[6226,1571,0,1569,5,"MASK"],[6227,1571,0,1569,6,"MASK"],[6228,1571,0,1569,7,"MASK"],[6229,727,0,1572,0,"LATENT"],[6230,727,0,1572,1,"LATENT"],[6239,1573,0,1569,20,"FLOAT"],[6240,1574,0,1569,21,"FLOAT"],[6241,1575,0,1569,22,"FLOAT"],[6342,1571,0,1539,0,"MASK"],[6343,1539,0,1572,2,"MASK"],[6344,1539,0,1572,3,"MASK"],[6345,1539,0,1572,4,"MASK"],[6346,1539,0,1572,5,"MASK"],[6347,1539,0,1572,6,"MASK"],[6348,1539,0,1572,7,"MASK"],[6360,727,2,1667,0,"MASK"],[6361,1667,0,1516,0,"MASK"],[6380,1446,0,1479,4,"LATENT"],[6383,1478,0,1524,0,"MODEL"],[6396,1279,0,1478,0,"MODEL"],[6397,1477,0,1279,0,"MODEL"],[6398,1477,1,490,0,"*"],[6402,1454,0,1446,8,"OPTIONS"],[6411,1569,0,1525,3,"GUIDES"],[6412,727,0,1672,0,"LATENT"],[6414,1572,0,1672,3,"GUIDES"],[6415,1672,0,1479,5,"GUIDES"],[6421,1548,0,1446,1,"CONDITIONING"],[6422,1556,0,1548,0,"CONDITIONING"],[6447,726,2,759,2,"MASK"],[6476,1672,0,1690,5,"GUIDES"],[6478,1690,0,745,0,"LATENT"],[6524,1703,0,1704,1,"PULIDFLUX"],[6525,1701,0,1704,2,"EVA_CLIP"],[6526,1702,0,1704,3,"FACEANALYSIS"],[6543,726,0,1548,3,"IMAGE"],[6547,1524,0,13,0,"*"],[6548,13,0,1704,0,"MODEL"],[6549,1704,0,1446,0,"MODEL"],[6550,1476,3,1548,2,"CLIP_VISION"],[6551,1476,4,1548,1,"STYLE_MODEL"],[6552,1476,3,1071,0,"CLIP_VISION"],[6553,1476,4,1072,1,"STYLE_MODEL"],[6566,1479,0,1690,4,"LATENT"],[6569,727,2,1525,1,"MASK"],[6570,727,2,1672,1,"MASK"],[6584,1571,0,1730,0,"MASK"],[6585,1731,0,1479,6,"OPTIONS"],[6586,1730,0,1731,0,"MASK"],[6587,1731,0,1690,6,"OPTIONS"],[6589,1722,0,1446,9,"OPTIONS"],[6590,1722,0,1479,7,"OPTIONS"],[6591,1722,0,1690,7,"OPTIONS"],[6619,1707,0,725,0,"*"],[6620,1707,1,1407,0,"*"]],"groups":[{"id":1,"title":"Prepare Input","bounding":[-1310.92529296875,-489.52618408203125,755.7755737304688,762.867431640625],"color":"#3f789e","font_size":24,"flags":{}},{"id":2,"title":"Patch and Stitch","bounding":[1250.695068359375,-877.5091552734375,1320.4892578125,1148.6859130859375],"color":"#3f789e","font_size":24,"flags":{}},{"id":3,"title":"Loaders","bounding":[-2115.099853515625,-1180.8953857421875,881.3677368164062,646.2952880859375],"color":"#3f789e","font_size":24,"flags":{}},{"id":5,"title":"Sampling","bounding":[-510.548828125,-602.9613037109375,1686.064208984375,874.1248168945312],"color":"#3f789e","font_size":24,"flags":{}},{"id":6,"title":"Guides","bounding":[-37.0714225769043,-1229.123046875,888.9586791992188,587.7683715820312],"color":"#3f789e","font_size":24,"flags":{}},{"id":7,"title":"PuLID","bounding":[-1191.9031982421875,-1177.2020263671875,649.8841552734375,641.718994140625],"color":"#3f789e","font_size":24,"flags":{}}],"config":{},"extra":{"ds":{"scale":1.3310000000000006,"offset":[4741.826990245036,1361.8744550803772]},"VHS_latentpreview":false,"VHS_latentpreviewrate":0,"ue_links":[],"VHS_MetadataImage":true,"VHS_KeepIntermediate":true},"version":0.4} \ No newline at end of file diff --git a/ComfyUI/custom_nodes/RES4LYF/example_workflows/flux faceswap sync.json b/ComfyUI/custom_nodes/RES4LYF/example_workflows/flux faceswap sync.json new file mode 100644 index 0000000000000000000000000000000000000000..0438125c6a1bc1ad725255471241c83f1fa79697 --- /dev/null +++ b/ComfyUI/custom_nodes/RES4LYF/example_workflows/flux faceswap sync.json @@ -0,0 +1 @@ +{"last_node_id":1698,"last_link_id":6519,"nodes":[{"id":490,"type":"Reroute","pos":[-669.7835083007812,-822.2691040039062],"size":[75,26],"flags":{},"order":28,"mode":0,"inputs":[{"name":"","type":"*","link":6398}],"outputs":[{"name":"","type":"CLIP","links":[4157,6103],"slot_index":0}],"properties":{"showOutputText":false,"horizontal":false}},{"id":1162,"type":"Reroute","pos":[1930.0975341796875,-817.45556640625],"size":[75,26],"flags":{},"order":66,"mode":0,"inputs":[{"name":"","type":"*","link":4185}],"outputs":[{"name":"","type":"IMAGE","links":[4186],"slot_index":0}],"properties":{"showOutputText":false,"horizontal":false}},{"id":744,"type":"SaveImage","pos":[1276.456787109375,-719.9273681640625],"size":[424.53594970703125,455.0760192871094],"flags":{},"order":60,"mode":0,"inputs":[{"name":"images","localized_name":"images","type":"IMAGE","link":2241}],"outputs":[],"title":"Save Patch","properties":{"Node name for S&R":"SaveImage","cnr_id":"comfy-core","ver":"0.3.29"},"widgets_values":["ComfyUI"],"color":"#332922","bgcolor":"#593930"},{"id":1022,"type":"ImageBlend","pos":[2313.7607421875,-792.44091796875],"size":[210,102],"flags":{"collapsed":true},"order":61,"mode":0,"inputs":[{"name":"image1","localized_name":"image1","type":"IMAGE","link":3568},{"name":"image2","localized_name":"image2","type":"IMAGE","link":3570}],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":[3569],"slot_index":0}],"properties":{"Node name for S&R":"ImageBlend"},"widgets_values":[0.5,"multiply"]},{"id":729,"type":"SetImageSize","pos":[-812.6932373046875,-86.24114227294922],"size":[210,102],"flags":{},"order":0,"mode":0,"inputs":[],"outputs":[{"name":"width","localized_name":"width","type":"INT","links":[2104,2108,4998],"slot_index":0},{"name":"height","localized_name":"height","type":"INT","links":[2105,2109,4999],"slot_index":1}],"title":"Inpaint Tile Size","properties":{"Node name for S&R":"SetImageSize"},"widgets_values":[1024,1024]},{"id":1161,"type":"Image Save","pos":[2186.75634765625,-722.2388916015625],"size":[351.4677734375,796.8805541992188],"flags":{},"order":67,"mode":0,"inputs":[{"name":"images","localized_name":"images","type":"IMAGE","link":4186}],"outputs":[{"name":"images","localized_name":"images","type":"IMAGE","links":null},{"name":"files","localized_name":"files","type":"STRING","links":null}],"properties":{"Node name for S&R":"Image Save"},"widgets_values":["[time(%Y-%m-%d)]","ComfyUI","_",4,"false","jpeg",300,100,"true","false","false","false","true","true","true"],"color":"#232","bgcolor":"#353"},{"id":1024,"type":"PreviewImage","pos":[1286.05859375,-198.6599884033203],"size":[413.7582092285156,445.8081359863281],"flags":{},"order":64,"mode":0,"inputs":[{"name":"images","localized_name":"images","type":"IMAGE","link":3569}],"outputs":[],"properties":{"Node name for S&R":"PreviewImage"},"widgets_values":[],"color":"#332922","bgcolor":"#593930"},{"id":758,"type":"ImageResize+","pos":[1468.4384765625,-790.391845703125],"size":[210,218],"flags":{"collapsed":true},"order":59,"mode":0,"inputs":[{"name":"image","localized_name":"image","type":"IMAGE","link":2201},{"name":"width","type":"INT","pos":[10,76],"widget":{"name":"width"},"link":2204},{"name":"height","type":"INT","pos":[10,100],"widget":{"name":"height"},"link":2205}],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":[2198],"slot_index":0},{"name":"width","localized_name":"width","type":"INT","links":null},{"name":"height","localized_name":"height","type":"INT","links":null}],"properties":{"Node name for S&R":"ImageResize+"},"widgets_values":[512,512,"lanczos","stretch","always",0]},{"id":1369,"type":"ImageResize+","pos":[2183.37109375,151.09762573242188],"size":[210,218],"flags":{"collapsed":true},"order":33,"mode":0,"inputs":[{"name":"image","localized_name":"image","type":"IMAGE","link":4996},{"name":"width","type":"INT","pos":[10,76],"widget":{"name":"width"},"link":4998},{"name":"height","type":"INT","pos":[10,100],"widget":{"name":"height"},"link":4999}],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":[5000],"slot_index":0},{"name":"width","localized_name":"width","type":"INT","links":null},{"name":"height","localized_name":"height","type":"INT","links":null}],"properties":{"Node name for S&R":"ImageResize+"},"widgets_values":[512,512,"lanczos","stretch","always",0]},{"id":1407,"type":"Reroute","pos":[-914.50390625,-361.0196533203125],"size":[75,26],"flags":{},"order":26,"mode":0,"inputs":[{"name":"","type":"*","link":6519}],"outputs":[{"name":"","type":"MASK","links":[5021],"slot_index":0}],"properties":{"showOutputText":false,"horizontal":false}},{"id":725,"type":"Reroute","pos":[-914.8554077148438,-440.6482238769531],"size":[75,26],"flags":{},"order":25,"mode":0,"inputs":[{"name":"","type":"*","link":6518}],"outputs":[{"name":"","type":"IMAGE","links":[2210,2211,5054],"slot_index":0}],"properties":{"showOutputText":false,"horizontal":false}},{"id":1071,"type":"CLIPVisionEncode","pos":[586.1533203125,119.24115753173828],"size":[253.60000610351562,78],"flags":{"collapsed":true},"order":32,"mode":0,"inputs":[{"name":"clip_vision","localized_name":"clip_vision","type":"CLIP_VISION","link":5443},{"name":"image","localized_name":"image","type":"IMAGE","link":3721}],"outputs":[{"name":"CLIP_VISION_OUTPUT","localized_name":"CLIP_VISION_OUTPUT","type":"CLIP_VISION_OUTPUT","links":[3720],"slot_index":0}],"properties":{"Node name for S&R":"CLIPVisionEncode"},"widgets_values":["center"]},{"id":1575,"type":"PrimitiveFloat","pos":[11.355203628540039,-940.5784912109375],"size":[210,58],"flags":{},"order":1,"mode":0,"inputs":[],"outputs":[{"name":"FLOAT","localized_name":"FLOAT","type":"FLOAT","links":[6241],"slot_index":0}],"title":"Similarity","properties":{"Node name for S&R":"PrimitiveFloat"},"widgets_values":[1]},{"id":1654,"type":"LoadImage","pos":[773.8897705078125,1813.0185546875],"size":[315,314],"flags":{},"order":2,"mode":0,"inputs":[],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":null},{"name":"MASK","localized_name":"MASK","type":"MASK","links":null}],"properties":{"Node name for S&R":"LoadImage"},"widgets_values":["7c2a2a772675a224-photo.JPG","image"]},{"id":1478,"type":"ModelSamplingAdvancedResolution","pos":[-1096.887451171875,-1029.6195068359375],"size":[260.3999938964844,126],"flags":{},"order":43,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","link":6396},{"name":"latent_image","localized_name":"latent_image","type":"LATENT","link":5442}],"outputs":[{"name":"model","localized_name":"model","type":"MODEL","links":[6383],"slot_index":0}],"properties":{"Node name for S&R":"ModelSamplingAdvancedResolution"},"widgets_values":["exponential",1.35,0.85]},{"id":1279,"type":"TorchCompileModels","pos":[-1409.527587890625,-1089.560302734375],"size":[285.9945068359375,179.0001983642578],"flags":{},"order":27,"mode":4,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","link":6397}],"outputs":[{"name":"model","localized_name":"model","type":"MODEL","links":[6396],"slot_index":0}],"properties":{"Node name for S&R":"TorchCompileModels"},"widgets_values":["inductor",false,"default",false,64,0]},{"id":14,"type":"Reroute","pos":[-669.7835083007812,-782.2691040039062],"size":[75,26],"flags":{},"order":24,"mode":0,"inputs":[{"name":"","type":"*","link":5447}],"outputs":[{"name":"","type":"VAE","links":[2153,3508,6353],"slot_index":0}],"properties":{"showOutputText":false,"horizontal":false}},{"id":13,"type":"Reroute","pos":[-669.7835083007812,-862.2692260742188],"size":[75,26],"flags":{},"order":51,"mode":0,"inputs":[{"name":"","type":"*","link":5845}],"outputs":[{"name":"","type":"MODEL","links":[5846],"slot_index":0}],"properties":{"showOutputText":false,"horizontal":false}},{"id":1516,"type":"ClownOptions_SDE_Mask_Beta","pos":[-68.4439468383789,-163.1180877685547],"size":[252.8383331298828,126],"flags":{},"order":47,"mode":0,"inputs":[{"name":"mask","localized_name":"mask","type":"MASK","shape":7,"link":6361},{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":null}],"outputs":[{"name":"options","localized_name":"options","type":"OPTIONS","links":[5776,6016,6477],"slot_index":0}],"properties":{"Node name for S&R":"ClownOptions_SDE_Mask_Beta"},"widgets_values":[1,0,false]},{"id":1667,"type":"GrowMask","pos":[-302.060302734375,-164.22067260742188],"size":[210,82],"flags":{},"order":42,"mode":0,"inputs":[{"name":"mask","localized_name":"mask","type":"MASK","link":6360}],"outputs":[{"name":"MASK","localized_name":"MASK","type":"MASK","links":[6361],"slot_index":0}],"properties":{"Node name for S&R":"GrowMask"},"widgets_values":[-10,false]},{"id":1039,"type":"ImageBlend","pos":[-769.9498901367188,220.86917114257812],"size":[210,102],"flags":{"collapsed":true},"order":39,"mode":0,"inputs":[{"name":"image1","localized_name":"image1","type":"IMAGE","link":3606},{"name":"image2","localized_name":"image2","type":"IMAGE","link":3605}],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":[3607],"slot_index":0}],"properties":{"Node name for S&R":"ImageBlend"},"widgets_values":[0.5,"multiply"]},{"id":727,"type":"VAEEncodeAdvanced","pos":[-789.0958862304688,67.53204345703125],"size":[262.4812927246094,298],"flags":{"collapsed":true},"order":38,"mode":0,"inputs":[{"name":"image_1","localized_name":"image_1","type":"IMAGE","shape":7,"link":2101},{"name":"image_2","localized_name":"image_2","type":"IMAGE","shape":7,"link":2102},{"name":"mask","localized_name":"mask","type":"IMAGE","shape":7,"link":2103},{"name":"latent","localized_name":"latent","type":"LATENT","shape":7,"link":null},{"name":"vae","localized_name":"vae","type":"VAE","shape":7,"link":3508},{"name":"width","type":"INT","pos":[10,160],"widget":{"name":"width"},"link":2104},{"name":"height","type":"INT","pos":[10,184],"widget":{"name":"height"},"link":2105}],"outputs":[{"name":"latent_1","localized_name":"latent_1","type":"LATENT","links":[5373,5715,6201,6202,6229,6230,6412],"slot_index":0},{"name":"latent_2","localized_name":"latent_2","type":"LATENT","links":[],"slot_index":1},{"name":"mask","localized_name":"mask","type":"MASK","links":[6222,6360],"slot_index":2},{"name":"empty_latent","localized_name":"empty_latent","type":"LATENT","links":[5442],"slot_index":3},{"name":"width","localized_name":"width","type":"INT","links":[],"slot_index":4},{"name":"height","localized_name":"height","type":"INT","links":[]}],"properties":{"Node name for S&R":"VAEEncodeAdvanced"},"widgets_values":["false",1344,768,"red",false,"16_channels"]},{"id":731,"type":"SimpleMath+","pos":[-776.4415893554688,126.82145690917969],"size":[315,98],"flags":{"collapsed":true},"order":22,"mode":0,"inputs":[{"name":"a","localized_name":"a","type":"*","shape":7,"link":2108},{"name":"b","localized_name":"b","type":"*","shape":7,"link":2109},{"name":"c","localized_name":"c","type":"*","shape":7,"link":null}],"outputs":[{"name":"INT","localized_name":"INT","type":"INT","links":null},{"name":"FLOAT","localized_name":"FLOAT","type":"FLOAT","links":[2100],"slot_index":1}],"properties":{"Node name for S&R":"SimpleMath+"},"widgets_values":["a/b"]},{"id":728,"type":"MaskToImage","pos":[-791.0198364257812,176.82147216796875],"size":[176.39999389648438,26],"flags":{"collapsed":true},"order":34,"mode":0,"inputs":[{"name":"mask","localized_name":"mask","type":"MASK","link":2106}],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":[2103,3605],"slot_index":0}],"properties":{"Node name for S&R":"MaskToImage"},"widgets_values":[]},{"id":765,"type":"MaskToImage","pos":[2080.868896484375,-792.6943359375],"size":[182.28543090820312,26],"flags":{"collapsed":true},"order":35,"mode":0,"inputs":[{"name":"mask","localized_name":"mask","type":"MASK","link":5529}],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":[3570],"slot_index":0}],"properties":{"Node name for S&R":"MaskToImage"},"widgets_values":[]},{"id":761,"type":"Image Comparer (rgthree)","pos":[1747.432373046875,-712.1251220703125],"size":[410.4466247558594,447.8973388671875],"flags":{},"order":65,"mode":0,"inputs":[{"name":"image_a","type":"IMAGE","dir":3,"link":2210},{"name":"image_b","type":"IMAGE","dir":3,"link":2200}],"outputs":[],"title":"Compare Output","properties":{"comparer_mode":"Slide"},"widgets_values":[[{"name":"A","selected":true,"url":"/api/view?filename=rgthree.compare._temp_udooi_00119_.png&type=temp&subfolder=&rand=0.4602348825653009"},{"name":"B","selected":true,"url":"/api/view?filename=rgthree.compare._temp_udooi_00120_.png&type=temp&subfolder=&rand=0.24695456359911838"}]],"color":"#232","bgcolor":"#353"},{"id":1072,"type":"StyleModelApply","pos":[591.9240112304688,151.93089294433594],"size":[262,122],"flags":{"collapsed":true},"order":37,"mode":0,"inputs":[{"name":"conditioning","localized_name":"conditioning","type":"CONDITIONING","link":4980},{"name":"style_model","localized_name":"style_model","type":"STYLE_MODEL","link":5444},{"name":"clip_vision_output","localized_name":"clip_vision_output","type":"CLIP_VISION_OUTPUT","link":3720}],"outputs":[{"name":"CONDITIONING","localized_name":"CONDITIONING","type":"CONDITIONING","links":[5653],"slot_index":0}],"properties":{"Node name for S&R":"StyleModelApply"},"widgets_values":[1,"multiply"]},{"id":1073,"type":"CLIPTextEncode","pos":[575.77001953125,186.9269256591797],"size":[263.280517578125,88.73566436767578],"flags":{"collapsed":true},"order":30,"mode":0,"inputs":[{"name":"clip","localized_name":"clip","type":"CLIP","link":4157}],"outputs":[{"name":"CONDITIONING","localized_name":"CONDITIONING","type":"CONDITIONING","links":[4650,4980],"slot_index":0}],"properties":{"Node name for S&R":"CLIPTextEncode","cnr_id":"comfy-core","ver":"0.3.29"},"widgets_values":[""]},{"id":1569,"type":"ClownGuides_Sync_Advanced","pos":[261.355224609375,-1000.5784912109375],"size":[315,1938],"flags":{"collapsed":true},"order":45,"mode":0,"inputs":[{"name":"guide_masked","localized_name":"guide_masked","type":"LATENT","shape":7,"link":6201},{"name":"guide_unmasked","localized_name":"guide_unmasked","type":"LATENT","shape":7,"link":6202},{"name":"mask","localized_name":"mask","type":"MASK","shape":7,"link":6223},{"name":"mask_sync","localized_name":"mask_sync","type":"MASK","shape":7,"link":6224},{"name":"mask_drift_x","localized_name":"mask_drift_x","type":"MASK","shape":7,"link":6225},{"name":"mask_drift_y","localized_name":"mask_drift_y","type":"MASK","shape":7,"link":6226},{"name":"mask_lure_x","localized_name":"mask_lure_x","type":"MASK","shape":7,"link":6227},{"name":"mask_lure_y","localized_name":"mask_lure_y","type":"MASK","shape":7,"link":6228},{"name":"weights_masked","localized_name":"weights_masked","type":"SIGMAS","shape":7,"link":null},{"name":"weights_unmasked","localized_name":"weights_unmasked","type":"SIGMAS","shape":7,"link":null},{"name":"syncs_masked","localized_name":"syncs_masked","type":"SIGMAS","shape":7,"link":null},{"name":"syncs_unmasked","localized_name":"syncs_unmasked","type":"SIGMAS","shape":7,"link":null},{"name":"drift_xs_masked","localized_name":"drift_xs_masked","type":"SIGMAS","shape":7,"link":null},{"name":"drift_xs_unmasked","localized_name":"drift_xs_unmasked","type":"SIGMAS","shape":7,"link":null},{"name":"drift_ys_masked","localized_name":"drift_ys_masked","type":"SIGMAS","shape":7,"link":null},{"name":"drift_ys_unmasked","localized_name":"drift_ys_unmasked","type":"SIGMAS","shape":7,"link":null},{"name":"lure_xs_masked","localized_name":"lure_xs_masked","type":"SIGMAS","shape":7,"link":null},{"name":"lure_xs_unmasked","localized_name":"lure_xs_unmasked","type":"SIGMAS","shape":7,"link":null},{"name":"lure_ys_masked","localized_name":"lure_ys_masked","type":"SIGMAS","shape":7,"link":null},{"name":"lure_ys_unmasked","localized_name":"lure_ys_unmasked","type":"SIGMAS","shape":7,"link":null},{"name":"drift_x_data","type":"FLOAT","pos":[10,800],"widget":{"name":"drift_x_data"},"link":6239},{"name":"drift_y_guide","type":"FLOAT","pos":[10,1088],"widget":{"name":"drift_y_guide"},"link":6240},{"name":"sync_masked","type":"FLOAT","pos":[10,608],"widget":{"name":"sync_masked"},"link":6241}],"outputs":[{"name":"guides","localized_name":"guides","type":"GUIDES","links":[6411],"slot_index":0}],"properties":{"Node name for S&R":"ClownGuides_Sync_Advanced"},"widgets_values":[1,1,"constant","constant",0,0,-1,-1,0,1,"constant","constant",0,0,-1,-1,0.2,0,1,0,"constant","constant",0,0,-1,-1,0,0,0.2,1,0,"constant","constant",0,0,-1,-1,0,0,"constant","constant",0,0,-1,-1,0,0,"constant","constant",0,0,-1,-1,0,"y -> x",false,false,false,false,false,false]},{"id":1571,"type":"Reroute","pos":[141.35520935058594,-1030.5784912109375],"size":[75,26],"flags":{},"order":41,"mode":0,"inputs":[{"name":"","type":"*","link":6222}],"outputs":[{"name":"","type":"MASK","links":[6223,6224,6225,6226,6227,6228,6342],"slot_index":0}],"properties":{"showOutputText":false,"horizontal":false}},{"id":1664,"type":"VAEDecode","pos":[1440,-1320],"size":[140,46],"flags":{"collapsed":true},"order":55,"mode":0,"inputs":[{"name":"samples","localized_name":"samples","type":"LATENT","link":6354},{"name":"vae","localized_name":"vae","type":"VAE","link":6353}],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":[6355],"slot_index":0}],"properties":{"Node name for S&R":"VAEDecode","cnr_id":"comfy-core","ver":"0.3.29"},"widgets_values":[]},{"id":1368,"type":"Image Comparer (rgthree)","pos":[1744.9150390625,-199.16920471191406],"size":[410.4466247558594,447.8973388671875],"flags":{},"order":62,"mode":0,"inputs":[{"name":"image_a","type":"IMAGE","dir":3,"link":4997},{"name":"image_b","type":"IMAGE","dir":3,"link":5000}],"outputs":[],"title":"Compare Patch","properties":{"comparer_mode":"Slide"},"widgets_values":[[{"name":"A","selected":true,"url":"/api/view?filename=rgthree.compare._temp_sgbfj_00119_.png&type=temp&subfolder=&rand=0.4913573783056806"},{"name":"B","selected":true,"url":"/api/view?filename=rgthree.compare._temp_sgbfj_00120_.png&type=temp&subfolder=&rand=0.2366457814945162"}]],"color":"#232","bgcolor":"#353"},{"id":1665,"type":"PreviewImage","pos":[1430,-1270],"size":[343.7617492675781,360.52777099609375],"flags":{},"order":57,"mode":0,"inputs":[{"name":"images","localized_name":"images","type":"IMAGE","link":6355}],"outputs":[],"properties":{"Node name for S&R":"PreviewImage"},"widgets_values":[]},{"id":1673,"type":"Note","pos":[1824.9287109375,-1010.687744140625],"size":[322.34954833984375,88],"flags":{},"order":3,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["Preview of first stage output: sometimes it can be worth manually (or automatically, using DINO, etc.) adjusting your mask for the second stage, based on this output."],"color":"#432","bgcolor":"#653"},{"id":1539,"type":"GrowMask","pos":[573.4215698242188,-1145.86767578125],"size":[214.5684051513672,82],"flags":{},"order":46,"mode":4,"inputs":[{"name":"mask","localized_name":"mask","type":"MASK","link":6342}],"outputs":[{"name":"MASK","localized_name":"MASK","type":"MASK","links":[6343,6344,6345,6346,6347,6348],"slot_index":0}],"properties":{"Node name for S&R":"GrowMask"},"widgets_values":[10,false]},{"id":1383,"type":"Note","pos":[216.7359161376953,340.25775146484375],"size":[291.67218017578125,232.2296142578125],"flags":{},"order":4,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["eta > 0.0 means you are using SDE/ancestral sampling. With this guide mode you will generally want to use bongmath = true.\n\nSamplers such as res_2s and res_3s will be very accurate. Try res_5s and res_8s if you really want to go crazy with it. They run 2x (2s), 3x (3s), etc slower than Euler.\n\nres_2m and 3m will be fast and also good, and run at the same speed as Euler.\n\neta_substep will increase the power of bongmath. If it is set to 0.0, you can turn bongmath off without any effect."],"color":"#432","bgcolor":"#653"},{"id":1380,"type":"Note","pos":[544.9375610351562,342.0576477050781],"size":[290.1026611328125,231.5842742919922],"flags":{},"order":5,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["Setting denoise to a negative value is equivalent to just scaling it. For example:\n\nDenoise = -0.90 is the same as multiplying every sigma value in the entire schedule by 0.9.\n\nI find this is a lot easier to control than the regular denoise scale. The difference between -0.95 and -0.9 is much more predictable than with 0.95 and 0.9. Most of us have seen how different denoise 0.8 might be with Karras vs. exponential. \n\nTry a denoise between -0.95 and -0.85. "],"color":"#432","bgcolor":"#653"},{"id":759,"type":"ImageCompositeMasked","pos":[1697.19140625,-790.8740844726562],"size":[210,186],"flags":{"collapsed":true},"order":63,"mode":0,"inputs":[{"name":"destination","localized_name":"destination","type":"IMAGE","link":2211},{"name":"source","localized_name":"source","type":"IMAGE","link":2198},{"name":"mask","localized_name":"mask","type":"MASK","shape":7,"link":6447},{"name":"x","type":"INT","pos":[10,76],"widget":{"name":"x"},"link":2206},{"name":"y","type":"INT","pos":[10,100],"widget":{"name":"y"},"link":2207}],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":[2200,4185],"slot_index":0}],"properties":{"Node name for S&R":"ImageCompositeMasked"},"widgets_values":[712,800,false]},{"id":1552,"type":"ClownOptions_SDE_Beta","pos":[-275.5662841796875,211.60325622558594],"size":[315,266],"flags":{"collapsed":true},"order":6,"mode":0,"inputs":[{"name":"etas","localized_name":"etas","type":"SIGMAS","shape":7,"link":null},{"name":"etas_substep","localized_name":"etas_substep","type":"SIGMAS","shape":7,"link":null},{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":null}],"outputs":[{"name":"options","localized_name":"options","type":"OPTIONS","links":[],"slot_index":0}],"properties":{"Node name for S&R":"ClownOptions_SDE_Beta"},"widgets_values":["gaussian","gaussian","hard","hard",1,1,-1,"fixed"]},{"id":1619,"type":"LoadImage","pos":[79.17283630371094,1820.8131103515625],"size":[315,314],"flags":{},"order":7,"mode":0,"inputs":[],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":null},{"name":"MASK","localized_name":"MASK","type":"MASK","links":null}],"properties":{"Node name for S&R":"LoadImage"},"widgets_values":["9319202660b0e794-photo.JPG","image"]},{"id":1476,"type":"FluxLoader","pos":[-1417.3287353515625,-846.1827392578125],"size":[385.17449951171875,282],"flags":{},"order":8,"mode":0,"inputs":[],"outputs":[{"name":"model","localized_name":"model","type":"MODEL","links":[5439],"slot_index":0},{"name":"clip","localized_name":"clip","type":"CLIP","links":[5440],"slot_index":1},{"name":"vae","localized_name":"vae","type":"VAE","links":[5447],"slot_index":2},{"name":"clip_vision","localized_name":"clip_vision","type":"CLIP_VISION","links":[5443,5993],"slot_index":3},{"name":"style_model","localized_name":"style_model","type":"STYLE_MODEL","links":[5444,5994],"slot_index":4}],"properties":{"Node name for S&R":"FluxLoader"},"widgets_values":["flux1-dev.sft","fp8_e4m3fn_fast","clip_l_flux.safetensors","t5xxl_fp8_e4m3fn_scaled.safetensors","ae.sft","sigclip_vision_patch14_384.safetensors","flux1-redux-dev.safetensors"]},{"id":1687,"type":"Note","pos":[-101.33948516845703,339.7750244140625],"size":[286.97723388671875,180.28128051757812],"flags":{},"order":9,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["The cycles node causes the connected sampler to loop between sampling and unsampling steps. (Unsampling is running the sampler backwards, where it predicts the noise that would lead to a given output).\n\nWhen unsample_eta is set to -1, it simply uses the same settings for eta as in the connected node. "],"color":"#432","bgcolor":"#653"},{"id":745,"type":"VAEDecode","pos":[1297.53369140625,-791.137939453125],"size":[140,46],"flags":{"collapsed":true},"order":58,"mode":0,"inputs":[{"name":"samples","localized_name":"samples","type":"LATENT","link":6478},{"name":"vae","localized_name":"vae","type":"VAE","link":2153}],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":[2201,2241,3568,4997],"slot_index":0}],"properties":{"Node name for S&R":"VAEDecode","cnr_id":"comfy-core","ver":"0.3.29"},"widgets_values":[]},{"id":1689,"type":"Note","pos":[525.9268798828125,-1349.89794921875],"size":[263.00439453125,88],"flags":{},"order":10,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["Expanding the mask for the second pass can sometimes help prevent seams."],"color":"#432","bgcolor":"#653"},{"id":1688,"type":"Note","pos":[-838.7593994140625,-1316.05126953125],"size":[274.47601318359375,104.34856414794922],"flags":{},"order":11,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["ReFluxPatcher is required to use the \"Style\" nodes. Different \"Re...Patcher\" nodes are available for many other models, from SD1.5/SDXL to SD3.5, HiDream, AuraFlow, Chroma, WAN, and LTXV."],"color":"#432","bgcolor":"#653"},{"id":1678,"type":"Note","pos":[-422.92510986328125,-333.6911926269531],"size":[324.0018005371094,113.63665771484375],"flags":{},"order":12,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["ReduxAdvanced is used to help get things on track. Bypass if you're having problems with it disrupting character likeness.\n\nThe SDE Mask ensures SDE noise is used only in the masked area, limiting change in unmasked areas that could lead to seams. "],"color":"#432","bgcolor":"#653"},{"id":1572,"type":"ClownGuides_Sync_Advanced","pos":[581.355224609375,-1000.5784912109375],"size":[315,1878],"flags":{"collapsed":true},"order":50,"mode":0,"inputs":[{"name":"guide_masked","localized_name":"guide_masked","type":"LATENT","shape":7,"link":6229},{"name":"guide_unmasked","localized_name":"guide_unmasked","type":"LATENT","shape":7,"link":6230},{"name":"mask","localized_name":"mask","type":"MASK","shape":7,"link":6343},{"name":"mask_sync","localized_name":"mask_sync","type":"MASK","shape":7,"link":6344},{"name":"mask_drift_x","localized_name":"mask_drift_x","type":"MASK","shape":7,"link":6345},{"name":"mask_drift_y","localized_name":"mask_drift_y","type":"MASK","shape":7,"link":6346},{"name":"mask_lure_x","localized_name":"mask_lure_x","type":"MASK","shape":7,"link":6347},{"name":"mask_lure_y","localized_name":"mask_lure_y","type":"MASK","shape":7,"link":6348},{"name":"weights_masked","localized_name":"weights_masked","type":"SIGMAS","shape":7,"link":null},{"name":"weights_unmasked","localized_name":"weights_unmasked","type":"SIGMAS","shape":7,"link":null},{"name":"syncs_masked","localized_name":"syncs_masked","type":"SIGMAS","shape":7,"link":null},{"name":"syncs_unmasked","localized_name":"syncs_unmasked","type":"SIGMAS","shape":7,"link":null},{"name":"drift_xs_masked","localized_name":"drift_xs_masked","type":"SIGMAS","shape":7,"link":null},{"name":"drift_xs_unmasked","localized_name":"drift_xs_unmasked","type":"SIGMAS","shape":7,"link":null},{"name":"drift_ys_masked","localized_name":"drift_ys_masked","type":"SIGMAS","shape":7,"link":null},{"name":"drift_ys_unmasked","localized_name":"drift_ys_unmasked","type":"SIGMAS","shape":7,"link":null},{"name":"lure_xs_masked","localized_name":"lure_xs_masked","type":"SIGMAS","shape":7,"link":null},{"name":"lure_xs_unmasked","localized_name":"lure_xs_unmasked","type":"SIGMAS","shape":7,"link":null},{"name":"lure_ys_masked","localized_name":"lure_ys_masked","type":"SIGMAS","shape":7,"link":null},{"name":"lure_ys_unmasked","localized_name":"lure_ys_unmasked","type":"SIGMAS","shape":7,"link":null}],"outputs":[{"name":"guides","localized_name":"guides","type":"GUIDES","links":[6414],"slot_index":0}],"properties":{"Node name for S&R":"ClownGuides_Sync_Advanced"},"widgets_values":[0,1,"constant","constant",0,0,-1,-1,0,1,"constant","constant",0,0,-1,-1,0,0,1,0,"constant","constant",0,0,-1,-1,0,0,0,1,0,"constant","constant",0,0,-1,-1,0,0,"constant","constant",0,0,-1,-1,0,0,"constant","constant",0,0,-1,-1,0,"y -> x",false,false,false,false,false,false]},{"id":1674,"type":"Note","pos":[170.8737030029297,-1390.4803466796875],"size":[322.6287841796875,128.15802001953125],"flags":{},"order":13,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["Activate the style nodes if you are having issues with color, detail, light, blurriness or pixelation drifting too far from your source input.\n\nIf end_step is too high, you may get faint halos and an oversharpened look."],"color":"#432","bgcolor":"#653"},{"id":1524,"type":"ReFluxPatcher","pos":[-809.3073120117188,-985.41064453125],"size":[210,82],"flags":{},"order":48,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","link":6383}],"outputs":[{"name":"model","localized_name":"model","type":"MODEL","links":[5845],"slot_index":0}],"properties":{"Node name for S&R":"ReFluxPatcher"},"widgets_values":["float64",true]},{"id":1690,"type":"ClownsharkChainsampler_Beta","pos":[865.4187622070312,-518.0064086914062],"size":[281.7781677246094,571.74853515625],"flags":{},"order":56,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","shape":7,"link":null},{"name":"positive","localized_name":"positive","type":"CONDITIONING","shape":7,"link":null},{"name":"negative","localized_name":"negative","type":"CONDITIONING","shape":7,"link":null},{"name":"sigmas","localized_name":"sigmas","type":"SIGMAS","shape":7,"link":null},{"name":"latent_image","localized_name":"latent_image","type":"LATENT","shape":7,"link":6479},{"name":"guides","localized_name":"guides","type":"GUIDES","shape":7,"link":6476},{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":6477},{"name":"options 2","type":"OPTIONS","link":null}],"outputs":[{"name":"output","localized_name":"output","type":"LATENT","links":[6478],"slot_index":0},{"name":"denoised","localized_name":"denoised","type":"LATENT","links":null},{"name":"options","localized_name":"options","type":"OPTIONS","links":null}],"properties":{"Node name for S&R":"ClownsharkChainsampler_Beta"},"widgets_values":[0,"multistep/res_3m",-1,1,"resample",false]},{"id":1479,"type":"ClownsharkChainsampler_Beta","pos":[536.1533203125,-510.75872802734375],"size":[288.1370544433594,571.74853515625],"flags":{},"order":54,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","shape":7,"link":null},{"name":"positive","localized_name":"positive","type":"CONDITIONING","shape":7,"link":null},{"name":"negative","localized_name":"negative","type":"CONDITIONING","shape":7,"link":null},{"name":"sigmas","localized_name":"sigmas","type":"SIGMAS","shape":7,"link":null},{"name":"latent_image","localized_name":"latent_image","type":"LATENT","shape":7,"link":6380},{"name":"guides","localized_name":"guides","type":"GUIDES","shape":7,"link":6415},{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":6016},{"name":"options 2","type":"OPTIONS","link":null}],"outputs":[{"name":"output","localized_name":"output","type":"LATENT","links":[6479],"slot_index":0},{"name":"denoised","localized_name":"denoised","type":"LATENT","links":null},{"name":"options","localized_name":"options","type":"OPTIONS","links":null}],"properties":{"Node name for S&R":"ClownsharkChainsampler_Beta"},"widgets_values":[0,"exponential/res_2s",2,1,"resample",true]},{"id":1693,"type":"Note","pos":[-858.5514526367188,-640.8011474609375],"size":[276.7918701171875,88],"flags":{},"order":14,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["Padding can be very important. Some models/loras/IPadapter embeds etc. are going to respond very differently if the shot is close up vs. farther away."],"color":"#432","bgcolor":"#653"},{"id":1525,"type":"ClownGuide_Style_Beta","pos":[251.35520935058594,-950.5784912109375],"size":[252.0535430908203,286],"flags":{},"order":49,"mode":0,"inputs":[{"name":"guide","localized_name":"guide","type":"LATENT","shape":7,"link":5715},{"name":"mask","localized_name":"mask","type":"MASK","shape":7,"link":null},{"name":"weights","localized_name":"weights","type":"SIGMAS","shape":7,"link":null},{"name":"guides","localized_name":"guides","type":"GUIDES","shape":7,"link":6411}],"outputs":[{"name":"guides","localized_name":"guides","type":"GUIDES","links":[6051],"slot_index":0}],"properties":{"Node name for S&R":"ClownGuide_Style_Beta"},"widgets_values":["positive","WCT",1,1,"constant",0,-1,false]},{"id":1672,"type":"ClownGuide_Style_Beta","pos":[561.355224609375,-950.5784912109375],"size":[252.0535430908203,286],"flags":{},"order":52,"mode":0,"inputs":[{"name":"guide","localized_name":"guide","type":"LATENT","shape":7,"link":6412},{"name":"mask","localized_name":"mask","type":"MASK","shape":7,"link":null},{"name":"weights","localized_name":"weights","type":"SIGMAS","shape":7,"link":null},{"name":"guides","localized_name":"guides","type":"GUIDES","shape":7,"link":6414}],"outputs":[{"name":"guides","localized_name":"guides","type":"GUIDES","links":[6415,6476],"slot_index":0}],"properties":{"Node name for S&R":"ClownGuide_Style_Beta"},"widgets_values":["positive","WCT",1,1,"constant",0,5,false]},{"id":726,"type":"Mask Bounding Box Aspect Ratio","pos":[-828.6614990234375,-412.50946044921875],"size":[252,250],"flags":{"collapsed":false},"order":29,"mode":0,"inputs":[{"name":"image","localized_name":"image","type":"IMAGE","shape":7,"link":5054},{"name":"mask","localized_name":"mask","type":"MASK","shape":7,"link":5021},{"name":"aspect_ratio","type":"FLOAT","pos":[10,204],"widget":{"name":"aspect_ratio"},"link":2100}],"outputs":[{"name":"image","localized_name":"image","type":"IMAGE","links":[2101,2102,3606,3721,4996,5995],"slot_index":0},{"name":"mask","localized_name":"mask","type":"MASK","links":[2106,5529],"slot_index":1},{"name":"mask_blurred","localized_name":"mask_blurred","type":"MASK","links":[6447],"slot_index":2},{"name":"x","localized_name":"x","type":"INT","links":[2206],"slot_index":3},{"name":"y","localized_name":"y","type":"INT","links":[2207],"slot_index":4},{"name":"width","localized_name":"width","type":"INT","links":[2204],"slot_index":5},{"name":"height","localized_name":"height","type":"INT","links":[2205],"slot_index":6}],"properties":{"Node name for S&R":"Mask Bounding Box Aspect Ratio"},"widgets_values":[100,40,1.75,false]},{"id":1677,"type":"Note","pos":[-439.5185241699219,-738.3756713867188],"size":[290.3874816894531,88],"flags":{},"order":15,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["Try setting both drift values to 0.0 or 0.2 as a starting point.\n"],"color":"#432","bgcolor":"#653"},{"id":1694,"type":"Note","pos":[-441.5133056640625,-999.14990234375],"size":[291.2616882324219,189.98562622070312],"flags":{},"order":16,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["Increase character likeness by: \n\nDecreasing \"Similarity\"\nIncreasing \"Drift Toward Target\"\nIncreasing cycles\nIncreasing eta (max 1.0)\nIncreasing denoise\n\nIncrease adherence to the input image by:\n\nDoing the opposite of any of the above\nIncreasing \"Drift Toward Guide\"\nEnabling the ReduxAdvanced node\n"],"color":"#432","bgcolor":"#653"},{"id":1277,"type":"SharkOptions_GuideCond_Beta","pos":[575.9444580078125,221.88970947265625],"size":[315,98],"flags":{"collapsed":true},"order":40,"mode":0,"inputs":[{"name":"positive","localized_name":"positive","type":"CONDITIONING","shape":7,"link":5653},{"name":"negative","localized_name":"negative","type":"CONDITIONING","shape":7,"link":4650},{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":null}],"outputs":[{"name":"options","localized_name":"options","type":"OPTIONS","links":[5493],"slot_index":0}],"properties":{"Node name for S&R":"SharkOptions_GuideCond_Beta"},"widgets_values":[1]},{"id":1548,"type":"ReduxAdvanced","pos":[-69.81456756591797,-498.3502502441406],"size":[248.6250457763672,234],"flags":{},"order":36,"mode":4,"inputs":[{"name":"conditioning","localized_name":"conditioning","type":"CONDITIONING","link":6422},{"name":"style_model","localized_name":"style_model","type":"STYLE_MODEL","link":5994},{"name":"clip_vision","localized_name":"clip_vision","type":"CLIP_VISION","link":5993},{"name":"image","localized_name":"image","type":"IMAGE","link":5995},{"name":"mask","localized_name":"mask","type":"MASK","shape":7,"link":null}],"outputs":[{"name":"CONDITIONING","localized_name":"CONDITIONING","type":"CONDITIONING","links":[6421],"slot_index":0},{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":null},{"name":"MASK","localized_name":"MASK","type":"MASK","links":null}],"properties":{"Node name for S&R":"ReduxAdvanced"},"widgets_values":[3,"area","center crop (square)",1,0.1]},{"id":1446,"type":"ClownsharKSampler_Beta","pos":[214.812255859375,-508.00537109375],"size":[277.5089111328125,735.1378784179688],"flags":{},"order":53,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","shape":7,"link":5846},{"name":"positive","localized_name":"positive","type":"CONDITIONING","shape":7,"link":6421},{"name":"negative","localized_name":"negative","type":"CONDITIONING","shape":7,"link":null},{"name":"latent_image","localized_name":"latent_image","type":"LATENT","shape":7,"link":5373},{"name":"sigmas","localized_name":"sigmas","type":"SIGMAS","shape":7,"link":null},{"name":"guides","localized_name":"guides","type":"GUIDES","shape":7,"link":6051},{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":5493},{"name":"options 2","type":"OPTIONS","link":5776},{"name":"options 3","type":"OPTIONS","link":6402},{"name":"options 4","type":"OPTIONS","link":null}],"outputs":[{"name":"output","localized_name":"output","type":"LATENT","links":[6380],"slot_index":0},{"name":"denoised","localized_name":"denoised","type":"LATENT","links":[6354],"slot_index":1},{"name":"options","localized_name":"options","type":"OPTIONS","links":null}],"properties":{"Node name for S&R":"ClownsharKSampler_Beta","cnr_id":"RES4LYF","ver":"5ce9b5a77c227bf864e447a1e65305bf6cada5c2"},"widgets_values":[1,"exponential/res_2s","bong_tangent",30,1,0.55,1,100,"fixed","standard",true],"color":"#332922","bgcolor":"#593930"},{"id":1454,"type":"ClownOptions_Cycles_Beta","pos":[-74.8967514038086,24.043270111083984],"size":[261.7955627441406,202],"flags":{},"order":17,"mode":0,"inputs":[{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":null}],"outputs":[{"name":"options","localized_name":"options","type":"OPTIONS","links":[6402],"slot_index":0}],"properties":{"Node name for S&R":"ClownOptions_Cycles_Beta"},"widgets_values":[20,1,-1,"none",-1,1,true]},{"id":1573,"type":"PrimitiveFloat","pos":[10.393571853637695,-834.4251708984375],"size":[210,58],"flags":{},"order":18,"mode":0,"inputs":[],"outputs":[{"name":"FLOAT","localized_name":"FLOAT","type":"FLOAT","links":[6239],"slot_index":0}],"title":"Drift Toward Target","properties":{"Node name for S&R":"PrimitiveFloat"},"widgets_values":[0.2]},{"id":1574,"type":"PrimitiveFloat","pos":[11.355203628540039,-720.5784912109375],"size":[210,58],"flags":{},"order":19,"mode":0,"inputs":[],"outputs":[{"name":"FLOAT","localized_name":"FLOAT","type":"FLOAT","links":[6240],"slot_index":0}],"title":"Drift Toward Guide","properties":{"Node name for S&R":"PrimitiveFloat"},"widgets_values":[0.2]},{"id":1477,"type":"LoraLoader","pos":[-1007.4993896484375,-844.936279296875],"size":[315,126],"flags":{},"order":23,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","link":5439},{"name":"clip","localized_name":"clip","type":"CLIP","link":5440}],"outputs":[{"name":"MODEL","localized_name":"MODEL","type":"MODEL","links":[6397],"slot_index":0},{"name":"CLIP","localized_name":"CLIP","type":"CLIP","links":[6398],"slot_index":1}],"properties":{"Node name for S&R":"LoraLoader"},"widgets_values":["FLUX/Kirsten_Dunst_Flux_V1.safetensors",1,1]},{"id":1556,"type":"CLIPTextEncode","pos":[-392.6881408691406,-498.2940979003906],"size":[289.0962829589844,113.79679870605469],"flags":{"collapsed":false},"order":31,"mode":0,"inputs":[{"name":"clip","localized_name":"clip","type":"CLIP","link":6103}],"outputs":[{"name":"CONDITIONING","localized_name":"CONDITIONING","type":"CONDITIONING","links":[6422],"slot_index":0}],"properties":{"Node name for S&R":"CLIPTextEncode","cnr_id":"comfy-core","ver":"0.3.29"},"widgets_values":["kirsten dunst"],"color":"#2a363b","bgcolor":"#3f5159"},{"id":1451,"type":"LoadImage","pos":[-1267.7357177734375,-412.5631103515625],"size":[315,314],"flags":{},"order":20,"mode":0,"inputs":[],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":[6518],"slot_index":0},{"name":"MASK","localized_name":"MASK","type":"MASK","links":[6519],"slot_index":1}],"properties":{"Node name for S&R":"LoadImage"},"widgets_values":["clipspace/clipspace-mask-54212258.30000001.png [input]","image"]},{"id":1040,"type":"PreviewImage","pos":[-1267.6248779296875,-30.252229690551758],"size":[304.98114013671875,265.58380126953125],"flags":{},"order":44,"mode":0,"inputs":[{"name":"images","localized_name":"images","type":"IMAGE","link":3607}],"outputs":[],"properties":{"Node name for S&R":"PreviewImage"},"widgets_values":[]},{"id":1698,"type":"Note","pos":[-1623.859375,-355.951416015625],"size":[276.7918701171875,88],"flags":{},"order":21,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["Draw a mask over the face in the Load Image node. Ideally, try stopping precisely at the hairline, and just above or just below the chin."],"color":"#432","bgcolor":"#653"}],"links":[[2100,731,1,726,2,"FLOAT"],[2101,726,0,727,0,"IMAGE"],[2102,726,0,727,1,"IMAGE"],[2103,728,0,727,2,"IMAGE"],[2104,729,0,727,5,"INT"],[2105,729,1,727,6,"INT"],[2106,726,1,728,0,"MASK"],[2108,729,0,731,0,"*"],[2109,729,1,731,1,"*"],[2153,14,0,745,1,"VAE"],[2198,758,0,759,1,"IMAGE"],[2200,759,0,761,1,"IMAGE"],[2201,745,0,758,0,"IMAGE"],[2204,726,5,758,1,"INT"],[2205,726,6,758,2,"INT"],[2206,726,3,759,3,"INT"],[2207,726,4,759,4,"INT"],[2210,725,0,761,0,"IMAGE"],[2211,725,0,759,0,"IMAGE"],[2241,745,0,744,0,"IMAGE"],[3508,14,0,727,4,"VAE"],[3568,745,0,1022,0,"IMAGE"],[3569,1022,0,1024,0,"IMAGE"],[3570,765,0,1022,1,"IMAGE"],[3605,728,0,1039,1,"IMAGE"],[3606,726,0,1039,0,"IMAGE"],[3607,1039,0,1040,0,"IMAGE"],[3720,1071,0,1072,2,"CLIP_VISION_OUTPUT"],[3721,726,0,1071,1,"IMAGE"],[4157,490,0,1073,0,"CLIP"],[4185,759,0,1162,0,"*"],[4186,1162,0,1161,0,"IMAGE"],[4650,1073,0,1277,1,"CONDITIONING"],[4980,1073,0,1072,0,"CONDITIONING"],[4996,726,0,1369,0,"IMAGE"],[4997,745,0,1368,0,"IMAGE"],[4998,729,0,1369,1,"INT"],[4999,729,1,1369,2,"INT"],[5000,1369,0,1368,1,"IMAGE"],[5021,1407,0,726,1,"MASK"],[5054,725,0,726,0,"IMAGE"],[5373,727,0,1446,3,"LATENT"],[5439,1476,0,1477,0,"MODEL"],[5440,1476,1,1477,1,"CLIP"],[5442,727,3,1478,1,"LATENT"],[5443,1476,3,1071,0,"CLIP_VISION"],[5444,1476,4,1072,1,"STYLE_MODEL"],[5447,1476,2,14,0,"*"],[5493,1277,0,1446,6,"OPTIONS"],[5529,726,1,765,0,"MASK"],[5653,1072,0,1277,0,"CONDITIONING"],[5715,727,0,1525,0,"LATENT"],[5776,1516,0,1446,7,"OPTIONS"],[5845,1524,0,13,0,"*"],[5846,13,0,1446,0,"MODEL"],[5993,1476,3,1548,2,"CLIP_VISION"],[5994,1476,4,1548,1,"STYLE_MODEL"],[5995,726,0,1548,3,"IMAGE"],[6016,1516,0,1479,6,"OPTIONS"],[6051,1525,0,1446,5,"GUIDES"],[6103,490,0,1556,0,"CLIP"],[6201,727,0,1569,0,"LATENT"],[6202,727,0,1569,1,"LATENT"],[6222,727,2,1571,0,"*"],[6223,1571,0,1569,2,"MASK"],[6224,1571,0,1569,3,"MASK"],[6225,1571,0,1569,4,"MASK"],[6226,1571,0,1569,5,"MASK"],[6227,1571,0,1569,6,"MASK"],[6228,1571,0,1569,7,"MASK"],[6229,727,0,1572,0,"LATENT"],[6230,727,0,1572,1,"LATENT"],[6239,1573,0,1569,20,"FLOAT"],[6240,1574,0,1569,21,"FLOAT"],[6241,1575,0,1569,22,"FLOAT"],[6342,1571,0,1539,0,"MASK"],[6343,1539,0,1572,2,"MASK"],[6344,1539,0,1572,3,"MASK"],[6345,1539,0,1572,4,"MASK"],[6346,1539,0,1572,5,"MASK"],[6347,1539,0,1572,6,"MASK"],[6348,1539,0,1572,7,"MASK"],[6353,14,0,1664,1,"VAE"],[6354,1446,1,1664,0,"LATENT"],[6355,1664,0,1665,0,"IMAGE"],[6360,727,2,1667,0,"MASK"],[6361,1667,0,1516,0,"MASK"],[6380,1446,0,1479,4,"LATENT"],[6383,1478,0,1524,0,"MODEL"],[6396,1279,0,1478,0,"MODEL"],[6397,1477,0,1279,0,"MODEL"],[6398,1477,1,490,0,"*"],[6402,1454,0,1446,8,"OPTIONS"],[6411,1569,0,1525,3,"GUIDES"],[6412,727,0,1672,0,"LATENT"],[6414,1572,0,1672,3,"GUIDES"],[6415,1672,0,1479,5,"GUIDES"],[6421,1548,0,1446,1,"CONDITIONING"],[6422,1556,0,1548,0,"CONDITIONING"],[6447,726,2,759,2,"MASK"],[6476,1672,0,1690,5,"GUIDES"],[6477,1516,0,1690,6,"OPTIONS"],[6478,1690,0,745,0,"LATENT"],[6479,1479,0,1690,4,"LATENT"],[6518,1451,0,725,0,"*"],[6519,1451,1,1407,0,"*"]],"groups":[{"id":1,"title":"Prepare Input","bounding":[-1310.92529296875,-489.52618408203125,755.7755737304688,762.867431640625],"color":"#3f789e","font_size":24,"flags":{}},{"id":2,"title":"Patch and Stitch","bounding":[1250.695068359375,-877.5091552734375,1320.4892578125,1148.6859130859375],"color":"#3f789e","font_size":24,"flags":{}},{"id":3,"title":"Loaders","bounding":[-1438.07421875,-1179.8375244140625,881.3677368164062,646.2952880859375],"color":"#3f789e","font_size":24,"flags":{}},{"id":5,"title":"Sampling","bounding":[-510.548828125,-602.9613037109375,1686.064208984375,874.1248168945312],"color":"#3f789e","font_size":24,"flags":{}},{"id":6,"title":"Guides","bounding":[-37.0714225769043,-1229.123046875,888.9586791992188,587.7683715820312],"color":"#3f789e","font_size":24,"flags":{}}],"config":{},"extra":{"ds":{"scale":1.2100000000000002,"offset":[4241.572246240033,1450.4856076460571]},"VHS_latentpreview":false,"VHS_latentpreviewrate":0,"ue_links":[],"VHS_MetadataImage":true,"VHS_KeepIntermediate":true},"version":0.4} \ No newline at end of file diff --git a/ComfyUI/custom_nodes/RES4LYF/example_workflows/flux faceswap.json b/ComfyUI/custom_nodes/RES4LYF/example_workflows/flux faceswap.json new file mode 100644 index 0000000000000000000000000000000000000000..02315bd87dfe358d87132bf9cd3b5a59ced5d7a3 --- /dev/null +++ b/ComfyUI/custom_nodes/RES4LYF/example_workflows/flux faceswap.json @@ -0,0 +1 @@ +{"last_node_id":1153,"last_link_id":4163,"nodes":[{"id":758,"type":"ImageResize+","pos":[1987.2191162109375,-351.3092041015625],"size":[210,218],"flags":{"collapsed":true},"order":31,"mode":0,"inputs":[{"name":"image","localized_name":"image","type":"IMAGE","link":2201},{"name":"width","type":"INT","pos":[10,76],"widget":{"name":"width"},"link":2204},{"name":"height","type":"INT","pos":[10,100],"widget":{"name":"height"},"link":2205}],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":[2198],"slot_index":0},{"name":"width","localized_name":"width","type":"INT","links":null},{"name":"height","localized_name":"height","type":"INT","links":null}],"properties":{"Node name for S&R":"ImageResize+"},"widgets_values":[512,512,"lanczos","stretch","always",0]},{"id":490,"type":"Reroute","pos":[-693.37158203125,-93.71382904052734],"size":[75,26],"flags":{},"order":10,"mode":0,"inputs":[{"name":"","type":"*","link":4149}],"outputs":[{"name":"","type":"CLIP","links":[4157],"slot_index":0}],"properties":{"showOutputText":false,"horizontal":false}},{"id":728,"type":"MaskToImage","pos":[219.2652130126953,854.9601440429688],"size":[176.39999389648438,26],"flags":{"collapsed":true},"order":14,"mode":0,"inputs":[{"name":"mask","localized_name":"mask","type":"MASK","link":2106}],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":[2103,3605],"slot_index":0}],"properties":{"Node name for S&R":"MaskToImage"},"widgets_values":[]},{"id":765,"type":"MaskToImage","pos":[2707.509765625,226.7833709716797],"size":[182.28543090820312,26],"flags":{"collapsed":true},"order":19,"mode":0,"inputs":[{"name":"mask","localized_name":"mask","type":"MASK","link":2233}],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":[3570],"slot_index":0}],"properties":{"Node name for S&R":"MaskToImage"},"widgets_values":[]},{"id":1024,"type":"PreviewImage","pos":[2707.52197265625,-277.8296203613281],"size":[413.7582092285156,445.8081359863281],"flags":{},"order":36,"mode":0,"inputs":[{"name":"images","localized_name":"images","type":"IMAGE","link":3569}],"outputs":[],"properties":{"Node name for S&R":"PreviewImage"},"widgets_values":[],"color":"#332922","bgcolor":"#593930"},{"id":744,"type":"SaveImage","pos":[1807.2188720703125,-291.30926513671875],"size":[424.53594970703125,455.0760192871094],"flags":{},"order":33,"mode":0,"inputs":[{"name":"images","localized_name":"images","type":"IMAGE","link":2241}],"outputs":[],"title":"Save Patch","properties":{"Node name for S&R":"SaveImage","cnr_id":"comfy-core","ver":"0.3.29"},"widgets_values":["ComfyUI"],"color":"#332922","bgcolor":"#593930"},{"id":1040,"type":"PreviewImage","pos":[-195.9951934814453,694.224609375],"size":[304.98114013671875,265.58380126953125],"flags":{},"order":23,"mode":0,"inputs":[{"name":"images","localized_name":"images","type":"IMAGE","link":3607}],"outputs":[],"properties":{"Node name for S&R":"PreviewImage"},"widgets_values":[]},{"id":731,"type":"SimpleMath+","pos":[219.2652130126953,804.9601440429688],"size":[315,98],"flags":{"collapsed":true},"order":5,"mode":0,"inputs":[{"name":"a","localized_name":"a","type":"*","shape":7,"link":2108},{"name":"b","localized_name":"b","type":"*","shape":7,"link":2109},{"name":"c","localized_name":"c","type":"*","shape":7,"link":null}],"outputs":[{"name":"INT","localized_name":"INT","type":"INT","links":null},{"name":"FLOAT","localized_name":"FLOAT","type":"FLOAT","links":[2100],"slot_index":1}],"properties":{"Node name for S&R":"SimpleMath+"},"widgets_values":["a/b"]},{"id":14,"type":"Reroute","pos":[-693.37158203125,-53.713836669921875],"size":[75,26],"flags":{},"order":7,"mode":0,"inputs":[{"name":"","type":"*","link":4146}],"outputs":[{"name":"","type":"VAE","links":[2153,3508],"slot_index":0}],"properties":{"showOutputText":false,"horizontal":false}},{"id":1039,"type":"ImageBlend","pos":[219.2652130126953,954.9601440429688],"size":[210,102],"flags":{"collapsed":true},"order":17,"mode":0,"inputs":[{"name":"image1","localized_name":"image1","type":"IMAGE","link":3606},{"name":"image2","localized_name":"image2","type":"IMAGE","link":3605}],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":[3607],"slot_index":0}],"properties":{"Node name for S&R":"ImageBlend"},"widgets_values":[0.5,"multiply"]},{"id":1022,"type":"ImageBlend","pos":[2710.7275390625,275.91143798828125],"size":[210,102],"flags":{"collapsed":true},"order":34,"mode":0,"inputs":[{"name":"image1","localized_name":"image1","type":"IMAGE","link":3568},{"name":"image2","localized_name":"image2","type":"IMAGE","link":3570}],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":[3569],"slot_index":0}],"properties":{"Node name for S&R":"ImageBlend"},"widgets_values":[0.5,"multiply"]},{"id":726,"type":"Mask Bounding Box Aspect Ratio","pos":[216.9475860595703,323.4888610839844],"size":[252,250],"flags":{"collapsed":false},"order":11,"mode":0,"inputs":[{"name":"image","localized_name":"image","type":"IMAGE","shape":7,"link":2338},{"name":"mask","localized_name":"mask","type":"MASK","shape":7,"link":4158},{"name":"aspect_ratio","type":"FLOAT","pos":[10,204],"widget":{"name":"aspect_ratio"},"link":2100}],"outputs":[{"name":"image","localized_name":"image","type":"IMAGE","links":[2101,2102,2209,3606,3721],"slot_index":0},{"name":"mask","localized_name":"mask","type":"MASK","links":[2106],"slot_index":1},{"name":"mask_blurred","localized_name":"mask_blurred","type":"MASK","links":[3884],"slot_index":2},{"name":"x","localized_name":"x","type":"INT","links":[2206],"slot_index":3},{"name":"y","localized_name":"y","type":"INT","links":[2207],"slot_index":4},{"name":"width","localized_name":"width","type":"INT","links":[2204],"slot_index":5},{"name":"height","localized_name":"height","type":"INT","links":[2205],"slot_index":6}],"properties":{"Node name for S&R":"Mask Bounding Box Aspect Ratio"},"widgets_values":[100,40,1.75,false]},{"id":760,"type":"SaveImage","pos":[1807.2188720703125,218.6908721923828],"size":[418.26055908203125,456.04608154296875],"flags":{},"order":37,"mode":0,"inputs":[{"name":"images","localized_name":"images","type":"IMAGE","link":2199}],"outputs":[],"title":"Save Output","properties":{},"widgets_values":["ComfyUI"],"color":"#232","bgcolor":"#353"},{"id":761,"type":"Image Comparer (rgthree)","pos":[2257.2197265625,228.6908416748047],"size":[410.4466247558594,447.8973388671875],"flags":{},"order":38,"mode":0,"inputs":[{"name":"image_a","type":"IMAGE","dir":3,"link":2210},{"name":"image_b","type":"IMAGE","dir":3,"link":2200}],"outputs":[],"title":"Compare Output","properties":{"comparer_mode":"Slide"},"widgets_values":[[{"name":"A","selected":true,"url":"/api/view?filename=rgthree.compare._temp_dluyj_00015_.png&type=temp&subfolder=&rand=0.8734695511873163"},{"name":"B","selected":true,"url":"/api/view?filename=rgthree.compare._temp_dluyj_00016_.png&type=temp&subfolder=&rand=0.23774072803641766"}]],"color":"#232","bgcolor":"#353"},{"id":1074,"type":"ClownOptions_SDE_Beta","pos":[790.0368041992188,-161.93728637695312],"size":[315,266],"flags":{"collapsed":true},"order":0,"mode":0,"inputs":[{"name":"etas","localized_name":"etas","type":"SIGMAS","shape":7,"link":null},{"name":"etas_substep","localized_name":"etas_substep","type":"SIGMAS","shape":7,"link":null},{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":null}],"outputs":[{"name":"options","localized_name":"options","type":"OPTIONS","links":[],"slot_index":0}],"properties":{"Node name for S&R":"ClownOptions_SDE_Beta"},"widgets_values":["gaussian","gaussian","hard","hard",0.5,0.75,-1,"fixed"]},{"id":13,"type":"Reroute","pos":[-693.37158203125,-133.7138214111328],"size":[75,26],"flags":{},"order":26,"mode":0,"inputs":[{"name":"","type":"*","link":4163}],"outputs":[{"name":"","type":"MODEL","links":[3812],"slot_index":0}],"properties":{"showOutputText":false,"horizontal":false}},{"id":745,"type":"VAEDecode","pos":[1818.999755859375,-349.32073974609375],"size":[140,46],"flags":{"collapsed":true},"order":30,"mode":0,"inputs":[{"name":"samples","localized_name":"samples","type":"LATENT","link":4031},{"name":"vae","localized_name":"vae","type":"VAE","link":2153}],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":[2201,2208,2241,3568],"slot_index":0}],"properties":{"Node name for S&R":"VAEDecode","cnr_id":"comfy-core","ver":"0.3.29"},"widgets_values":[]},{"id":727,"type":"VAEEncodeAdvanced","pos":[219.2652130126953,904.9601440429688],"size":[262.4812927246094,298],"flags":{"collapsed":true},"order":16,"mode":0,"inputs":[{"name":"image_1","localized_name":"image_1","type":"IMAGE","shape":7,"link":2101},{"name":"image_2","localized_name":"image_2","type":"IMAGE","shape":7,"link":2102},{"name":"mask","localized_name":"mask","type":"IMAGE","shape":7,"link":2103},{"name":"latent","localized_name":"latent","type":"LATENT","shape":7,"link":null},{"name":"vae","localized_name":"vae","type":"VAE","shape":7,"link":3508},{"name":"width","type":"INT","pos":[10,160],"widget":{"name":"width"},"link":2104},{"name":"height","type":"INT","pos":[10,184],"widget":{"name":"height"},"link":2105}],"outputs":[{"name":"latent_1","localized_name":"latent_1","type":"LATENT","links":[3602,3603,3700,3785,3786,4097],"slot_index":0},{"name":"latent_2","localized_name":"latent_2","type":"LATENT","links":[],"slot_index":1},{"name":"mask","localized_name":"mask","type":"MASK","links":[2233,3604,3901],"slot_index":2},{"name":"empty_latent","localized_name":"empty_latent","type":"LATENT","links":[2125],"slot_index":3},{"name":"width","localized_name":"width","type":"INT","links":[],"slot_index":4},{"name":"height","localized_name":"height","type":"INT","links":[]}],"properties":{"Node name for S&R":"VAEEncodeAdvanced"},"widgets_values":["false",1344,768,"red",false,"16_channels"]},{"id":729,"type":"SetImageSize","pos":[257.9150695800781,633.8616333007812],"size":[210,102],"flags":{},"order":1,"mode":0,"inputs":[],"outputs":[{"name":"width","localized_name":"width","type":"INT","links":[2104,2108],"slot_index":0},{"name":"height","localized_name":"height","type":"INT","links":[2105,2109],"slot_index":1}],"title":"Inpaint Tile Size","properties":{"Node name for S&R":"SetImageSize"},"widgets_values":[1024,1024]},{"id":1072,"type":"StyleModelApply","pos":[618.7158813476562,-201.9373016357422],"size":[262,122],"flags":{"collapsed":true},"order":15,"mode":0,"inputs":[{"name":"conditioning","localized_name":"conditioning","type":"CONDITIONING","link":3724},{"name":"style_model","localized_name":"style_model","type":"STYLE_MODEL","link":4151},{"name":"clip_vision_output","localized_name":"clip_vision_output","type":"CLIP_VISION_OUTPUT","link":3720}],"outputs":[{"name":"CONDITIONING","localized_name":"CONDITIONING","type":"CONDITIONING","links":[4088,4102],"slot_index":0}],"properties":{"Node name for S&R":"StyleModelApply"},"widgets_values":[1,"multiply"]},{"id":1071,"type":"CLIPVisionEncode","pos":[618.708251953125,-160.76882934570312],"size":[253.60000610351562,78],"flags":{"collapsed":true},"order":13,"mode":0,"inputs":[{"name":"clip_vision","localized_name":"clip_vision","type":"CLIP_VISION","link":4152},{"name":"image","localized_name":"image","type":"IMAGE","link":3721}],"outputs":[{"name":"CLIP_VISION_OUTPUT","localized_name":"CLIP_VISION_OUTPUT","type":"CLIP_VISION_OUTPUT","links":[3720],"slot_index":0}],"properties":{"Node name for S&R":"CLIPVisionEncode"},"widgets_values":["center"]},{"id":1152,"type":"FluxLoader","pos":[-1424.1221923828125,-136.28652954101562],"size":[315,282],"flags":{},"order":2,"mode":0,"inputs":[],"outputs":[{"name":"model","localized_name":"model","type":"MODEL","links":[4144],"slot_index":0},{"name":"clip","localized_name":"clip","type":"CLIP","links":[4150],"slot_index":1},{"name":"vae","localized_name":"vae","type":"VAE","links":[4146],"slot_index":2},{"name":"clip_vision","localized_name":"clip_vision","type":"CLIP_VISION","links":[4152],"slot_index":3},{"name":"style_model","localized_name":"style_model","type":"STYLE_MODEL","links":[4151],"slot_index":4}],"properties":{"Node name for S&R":"FluxLoader"},"widgets_values":["flux1-dev.sft","fp8_e4m3fn_fast","clip_l_flux.safetensors","t5xxl_fp8_e4m3fn_scaled.safetensors","ae.sft","sigclip_vision_patch14_384.safetensors","flux1-redux-dev.safetensors"]},{"id":1145,"type":"SharkOptions_GuideCond_Beta","pos":[623.8969116210938,-288.85443115234375],"size":[315,98],"flags":{"collapsed":true},"order":18,"mode":0,"inputs":[{"name":"positive","localized_name":"positive","type":"CONDITIONING","shape":7,"link":4088},{"name":"negative","localized_name":"negative","type":"CONDITIONING","shape":7,"link":4087},{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":null}],"outputs":[{"name":"options","localized_name":"options","type":"OPTIONS","links":[4086,4089],"slot_index":0}],"properties":{"Node name for S&R":"SharkOptions_GuideCond_Beta"},"widgets_values":[1]},{"id":762,"type":"Image Comparer (rgthree)","pos":[2254.142822265625,-285.88934326171875],"size":[402.1800842285156,455.1059875488281],"flags":{},"order":32,"mode":0,"inputs":[{"name":"image_a","type":"IMAGE","dir":3,"link":2208},{"name":"image_b","type":"IMAGE","dir":3,"link":2209}],"outputs":[],"title":"Compare Inpaint Patch","properties":{"comparer_mode":"Slide"},"widgets_values":[[{"name":"A","selected":true,"url":"/api/view?filename=rgthree.compare._temp_glyrv_00015_.png&type=temp&subfolder=&rand=0.6304345035966803"},{"name":"B","selected":true,"url":"/api/view?filename=rgthree.compare._temp_glyrv_00016_.png&type=temp&subfolder=&rand=0.03317535764596258"}]],"color":"#332922","bgcolor":"#593930"},{"id":1073,"type":"CLIPTextEncode","pos":[618.718017578125,-243.58985900878906],"size":[263.280517578125,88.73566436767578],"flags":{"collapsed":true},"order":12,"mode":0,"inputs":[{"name":"clip","localized_name":"clip","type":"CLIP","link":4157}],"outputs":[{"name":"CONDITIONING","localized_name":"CONDITIONING","type":"CONDITIONING","links":[3724,4087],"slot_index":0}],"properties":{"Node name for S&R":"CLIPTextEncode","cnr_id":"comfy-core","ver":"0.3.29"},"widgets_values":[""]},{"id":759,"type":"ImageCompositeMasked","pos":[2182.82080078125,-351.82415771484375],"size":[210,186],"flags":{"collapsed":true},"order":35,"mode":0,"inputs":[{"name":"destination","localized_name":"destination","type":"IMAGE","link":2211},{"name":"source","localized_name":"source","type":"IMAGE","link":2198},{"name":"mask","localized_name":"mask","type":"MASK","shape":7,"link":3884},{"name":"x","type":"INT","pos":[10,76],"widget":{"name":"x"},"link":2206},{"name":"y","type":"INT","pos":[10,100],"widget":{"name":"y"},"link":2207}],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":[2199,2200],"slot_index":0}],"properties":{"Node name for S&R":"ImageCompositeMasked"},"widgets_values":[712,800,false]},{"id":1102,"type":"LoadImage","pos":[-205.95057678222656,316.025390625],"size":[315,314],"flags":{},"order":3,"mode":0,"inputs":[],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":[4156],"slot_index":0},{"name":"MASK","localized_name":"MASK","type":"MASK","links":[4158],"slot_index":1}],"properties":{"Node name for S&R":"LoadImage"},"widgets_values":["clipspace/clipspace-mask-67304674.png [input]","image"]},{"id":725,"type":"Reroute","pos":[126.9476318359375,319.6999206542969],"size":[75,26],"flags":{},"order":8,"mode":0,"inputs":[{"name":"","type":"*","link":4156}],"outputs":[{"name":"","type":"IMAGE","links":[2210,2211,2338],"slot_index":0}],"properties":{"showOutputText":false,"horizontal":false}},{"id":1038,"type":"ClownGuides_Beta","pos":[-491.9494934082031,-334.2093505859375],"size":[315,450],"flags":{},"order":20,"mode":0,"inputs":[{"name":"guide_masked","localized_name":"guide_masked","type":"LATENT","shape":7,"link":3602},{"name":"guide_unmasked","localized_name":"guide_unmasked","type":"LATENT","shape":7,"link":3603},{"name":"mask","localized_name":"mask","type":"MASK","shape":7,"link":3604},{"name":"weights_masked","localized_name":"weights_masked","type":"SIGMAS","shape":7,"link":null},{"name":"weights_unmasked","localized_name":"weights_unmasked","type":"SIGMAS","shape":7,"link":null}],"outputs":[{"name":"guides","localized_name":"guides","type":"GUIDES","links":[4095],"slot_index":0}],"properties":{"Node name for S&R":"ClownGuides_Beta"},"widgets_values":["flow",false,false,1,1,1,1,"constant","constant",0,0,8,8,false],"color":"#2a363b","bgcolor":"#3f5159"},{"id":1069,"type":"ClownsharkChainsampler_Beta","pos":[1011.0429077148438,-95.05850219726562],"size":[315,570],"flags":{},"order":28,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","shape":7,"link":null},{"name":"positive","localized_name":"positive","type":"CONDITIONING","shape":7,"link":null},{"name":"negative","localized_name":"negative","type":"CONDITIONING","shape":7,"link":null},{"name":"sigmas","localized_name":"sigmas","type":"SIGMAS","shape":7,"link":null},{"name":"latent_image","localized_name":"latent_image","type":"LATENT","shape":7,"link":3711},{"name":"guides","localized_name":"guides","type":"GUIDES","shape":7,"link":4155},{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":4089},{"name":"options 2","type":"OPTIONS","link":4159},{"name":"options 3","type":"OPTIONS","link":null}],"outputs":[{"name":"output","localized_name":"output","type":"LATENT","links":[4104],"slot_index":0},{"name":"denoised","localized_name":"denoised","type":"LATENT","links":null},{"name":"options","localized_name":"options","type":"OPTIONS","links":null}],"properties":{"Node name for S&R":"ClownsharkChainsampler_Beta"},"widgets_values":[0,"exponential/res_3s",1,1,"resample",true],"color":"#2a363b","bgcolor":"#3f5159"},{"id":1066,"type":"ClownsharKSampler_Beta","pos":[620.0368041992188,-101.93733215332031],"size":[340.55120849609375,730],"flags":{},"order":27,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","shape":7,"link":3812},{"name":"positive","localized_name":"positive","type":"CONDITIONING","shape":7,"link":4102},{"name":"negative","localized_name":"negative","type":"CONDITIONING","shape":7,"link":null},{"name":"latent_image","localized_name":"latent_image","type":"LATENT","shape":7,"link":3700},{"name":"sigmas","localized_name":"sigmas","type":"SIGMAS","shape":7,"link":null},{"name":"guides","localized_name":"guides","type":"GUIDES","shape":7,"link":4096},{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":4086},{"name":"options 2","type":"OPTIONS","link":null}],"outputs":[{"name":"output","localized_name":"output","type":"LATENT","links":[3711],"slot_index":0},{"name":"denoised","localized_name":"denoised","type":"LATENT","links":[],"slot_index":1},{"name":"options","localized_name":"options","type":"OPTIONS","links":null}],"properties":{"Node name for S&R":"ClownsharKSampler_Beta","cnr_id":"RES4LYF","ver":"5ce9b5a77c227bf864e447a1e65305bf6cada5c2"},"widgets_values":[0,"exponential/res_3s","beta57",30,7,1,1,0,"fixed","standard",false],"color":"#2a363b","bgcolor":"#3f5159"},{"id":1070,"type":"ClownsharkChainsampler_Beta","pos":[1361.5435791015625,-100.98193359375],"size":[315,570],"flags":{},"order":29,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","shape":7,"link":null},{"name":"positive","localized_name":"positive","type":"CONDITIONING","shape":7,"link":null},{"name":"negative","localized_name":"negative","type":"CONDITIONING","shape":7,"link":null},{"name":"sigmas","localized_name":"sigmas","type":"SIGMAS","shape":7,"link":null},{"name":"latent_image","localized_name":"latent_image","type":"LATENT","shape":7,"link":4104},{"name":"guides","localized_name":"guides","type":"GUIDES","shape":7,"link":3832},{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":null}],"outputs":[{"name":"output","localized_name":"output","type":"LATENT","links":[4031],"slot_index":0},{"name":"denoised","localized_name":"denoised","type":"LATENT","links":[],"slot_index":1},{"name":"options","localized_name":"options","type":"OPTIONS","links":null}],"properties":{"Node name for S&R":"ClownsharkChainsampler_Beta"},"widgets_values":[0,"exponential/res_3s",-1,1,"resample",true],"color":"#232","bgcolor":"#353"},{"id":1143,"type":"ClownOptions_Cycles_Beta","pos":[1023.6978149414062,-356.53753662109375],"size":[282.6300964355469,202],"flags":{},"order":4,"mode":4,"inputs":[{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":null}],"outputs":[{"name":"options","localized_name":"options","type":"OPTIONS","links":[4159],"slot_index":0}],"properties":{"Node name for S&R":"ClownOptions_Cycles_Beta"},"widgets_values":[5,1,0,"none",1,1,false],"color":"#2a363b","bgcolor":"#3f5159"},{"id":1153,"type":"LoraLoader","pos":[-1079.3297119140625,-135.3394012451172],"size":[315,126],"flags":{},"order":6,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","link":4144},{"name":"clip","localized_name":"clip","type":"CLIP","link":4150}],"outputs":[{"name":"MODEL","localized_name":"MODEL","type":"MODEL","links":[4160],"slot_index":0},{"name":"CLIP","localized_name":"CLIP","type":"CLIP","links":[4149],"slot_index":1}],"properties":{"Node name for S&R":"LoraLoader"},"widgets_values":["FLUX/Raura.safetensors",1,1]},{"id":737,"type":"ModelSamplingAdvancedResolution","pos":[-1125.156005859375,-356.12274169921875],"size":[260.3999938964844,126],"flags":{},"order":22,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","link":4161},{"name":"latent_image","localized_name":"latent_image","type":"LATENT","link":2125}],"outputs":[{"name":"model","localized_name":"model","type":"MODEL","links":[4162],"slot_index":0}],"properties":{"Node name for S&R":"ModelSamplingAdvancedResolution"},"widgets_values":["exponential",1.35,0.85]},{"id":1149,"type":"ReFluxPatcher","pos":[-828.3265380859375,-352.2313232421875],"size":[210,82],"flags":{},"order":25,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","link":4162}],"outputs":[{"name":"model","localized_name":"model","type":"MODEL","links":[4163],"slot_index":0}],"properties":{"Node name for S&R":"ReFluxPatcher"},"widgets_values":["float64",true],"color":"#223","bgcolor":"#335"},{"id":1142,"type":"TorchCompileModels","pos":[-1416.9853515625,-362.2281799316406],"size":[210,178],"flags":{},"order":9,"mode":4,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","link":4160}],"outputs":[{"name":"model","localized_name":"model","type":"MODEL","links":[4161],"slot_index":0}],"properties":{"Node name for S&R":"TorchCompileModels"},"widgets_values":["inductor",false,"default",false,64,0]},{"id":1150,"type":"ClownGuide_Style_Beta","pos":[-140.6088409423828,-331.50213623046875],"size":[248.69369506835938,286],"flags":{"collapsed":false},"order":24,"mode":0,"inputs":[{"name":"guide","localized_name":"guide","type":"LATENT","shape":7,"link":4097},{"name":"mask","localized_name":"mask","type":"MASK","shape":7,"link":null},{"name":"weights","localized_name":"weights","type":"SIGMAS","shape":7,"link":null},{"name":"guides","localized_name":"guides","type":"GUIDES","shape":7,"link":4095}],"outputs":[{"name":"guides","localized_name":"guides","type":"GUIDES","links":[4096,4155],"slot_index":0}],"properties":{"Node name for S&R":"ClownGuide_Style_Beta"},"widgets_values":["positive","WCT",1,1,"constant",0,-1,false],"color":"#223","bgcolor":"#335"},{"id":1088,"type":"ClownGuides_Beta","pos":[145.70831298828125,-329.6731872558594],"size":[315,450],"flags":{},"order":21,"mode":0,"inputs":[{"name":"guide_masked","localized_name":"guide_masked","type":"LATENT","shape":7,"link":3785},{"name":"guide_unmasked","localized_name":"guide_unmasked","type":"LATENT","shape":7,"link":3786},{"name":"mask","localized_name":"mask","type":"MASK","shape":7,"link":3901},{"name":"weights_masked","localized_name":"weights_masked","type":"SIGMAS","shape":7,"link":null},{"name":"weights_unmasked","localized_name":"weights_unmasked","type":"SIGMAS","shape":7,"link":null}],"outputs":[{"name":"guides","localized_name":"guides","type":"GUIDES","links":[3832],"slot_index":0}],"properties":{"Node name for S&R":"ClownGuides_Beta"},"widgets_values":["inversion",false,false,0,1,1,1,"constant","constant",0,0,30,30,false],"color":"#232","bgcolor":"#353"}],"links":[[2100,731,1,726,2,"FLOAT"],[2101,726,0,727,0,"IMAGE"],[2102,726,0,727,1,"IMAGE"],[2103,728,0,727,2,"IMAGE"],[2104,729,0,727,5,"INT"],[2105,729,1,727,6,"INT"],[2106,726,1,728,0,"MASK"],[2108,729,0,731,0,"*"],[2109,729,1,731,1,"*"],[2125,727,3,737,1,"LATENT"],[2153,14,0,745,1,"VAE"],[2198,758,0,759,1,"IMAGE"],[2199,759,0,760,0,"IMAGE"],[2200,759,0,761,1,"IMAGE"],[2201,745,0,758,0,"IMAGE"],[2204,726,5,758,1,"INT"],[2205,726,6,758,2,"INT"],[2206,726,3,759,3,"INT"],[2207,726,4,759,4,"INT"],[2208,745,0,762,0,"IMAGE"],[2209,726,0,762,1,"IMAGE"],[2210,725,0,761,0,"IMAGE"],[2211,725,0,759,0,"IMAGE"],[2233,727,2,765,0,"MASK"],[2241,745,0,744,0,"IMAGE"],[2338,725,0,726,0,"IMAGE"],[3508,14,0,727,4,"VAE"],[3568,745,0,1022,0,"IMAGE"],[3569,1022,0,1024,0,"IMAGE"],[3570,765,0,1022,1,"IMAGE"],[3602,727,0,1038,0,"LATENT"],[3603,727,0,1038,1,"LATENT"],[3604,727,2,1038,2,"MASK"],[3605,728,0,1039,1,"IMAGE"],[3606,726,0,1039,0,"IMAGE"],[3607,1039,0,1040,0,"IMAGE"],[3700,727,0,1066,3,"LATENT"],[3711,1066,0,1069,4,"LATENT"],[3720,1071,0,1072,2,"CLIP_VISION_OUTPUT"],[3721,726,0,1071,1,"IMAGE"],[3724,1073,0,1072,0,"CONDITIONING"],[3785,727,0,1088,0,"LATENT"],[3786,727,0,1088,1,"LATENT"],[3812,13,0,1066,0,"MODEL"],[3832,1088,0,1070,5,"GUIDES"],[3884,726,2,759,2,"MASK"],[3901,727,2,1088,2,"MASK"],[4031,1070,0,745,0,"LATENT"],[4086,1145,0,1066,6,"OPTIONS"],[4087,1073,0,1145,1,"CONDITIONING"],[4088,1072,0,1145,0,"CONDITIONING"],[4089,1145,0,1069,6,"OPTIONS"],[4095,1038,0,1150,3,"GUIDES"],[4096,1150,0,1066,5,"GUIDES"],[4097,727,0,1150,0,"LATENT"],[4102,1072,0,1066,1,"CONDITIONING"],[4104,1069,0,1070,4,"LATENT"],[4144,1152,0,1153,0,"MODEL"],[4146,1152,2,14,0,"*"],[4149,1153,1,490,0,"*"],[4150,1152,1,1153,1,"CLIP"],[4151,1152,4,1072,1,"STYLE_MODEL"],[4152,1152,3,1071,0,"CLIP_VISION"],[4155,1150,0,1069,5,"GUIDES"],[4156,1102,0,725,0,"*"],[4157,490,0,1073,0,"CLIP"],[4158,1102,1,726,1,"MASK"],[4159,1143,0,1069,7,"OPTIONS"],[4160,1153,0,1142,0,"MODEL"],[4161,1142,0,737,0,"MODEL"],[4162,737,0,1149,0,"MODEL"],[4163,1149,0,13,0,"*"]],"groups":[{"id":1,"title":"Prepare Input","bounding":[-240.3173828125,230.5765838623047,755.7755737304688,762.867431640625],"color":"#3f789e","font_size":24,"flags":{}},{"id":2,"title":"Patch and Stitch","bounding":[1762.0626220703125,-449.59136962890625,1387.1339111328125,1156.21923828125],"color":"#3f789e","font_size":24,"flags":{}},{"id":3,"title":"Loaders","bounding":[-1451.647216796875,-453.5611877441406,862.5447998046875,635.2009887695312],"color":"#3f789e","font_size":24,"flags":{}},{"id":5,"title":"Sampling","bounding":[565.7752685546875,-449.1409606933594,1147.30712890625,1118.83447265625],"color":"#3f789e","font_size":24,"flags":{}},{"id":6,"title":"Guides","bounding":[-538.6279296875,-451.06854248046875,1052.895263671875,634.7589721679688],"color":"#3f789e","font_size":24,"flags":{}}],"config":{},"extra":{"ds":{"scale":1.351305709310398,"offset":[2774.203337270875,600.0170992273368]},"VHS_latentpreview":false,"VHS_latentpreviewrate":0,"ue_links":[],"VHS_MetadataImage":true,"VHS_KeepIntermediate":true},"version":0.4} \ No newline at end of file diff --git a/ComfyUI/custom_nodes/RES4LYF/example_workflows/flux inpaint area.json b/ComfyUI/custom_nodes/RES4LYF/example_workflows/flux inpaint area.json new file mode 100644 index 0000000000000000000000000000000000000000..b522ae18174a03760ac44993a823e294c71204d2 --- /dev/null +++ b/ComfyUI/custom_nodes/RES4LYF/example_workflows/flux inpaint area.json @@ -0,0 +1 @@ +{"last_node_id":698,"last_link_id":1968,"nodes":[{"id":670,"type":"SaveImage","pos":[5481.20751953125,763.7216186523438],"size":[315,270],"flags":{},"order":21,"mode":0,"inputs":[{"name":"images","localized_name":"images","type":"IMAGE","link":1883}],"outputs":[],"properties":{},"widgets_values":["ComfyUI"]},{"id":663,"type":"VAEEncodeAdvanced","pos":[4030,1370],"size":[262.4812927246094,278],"flags":{},"order":10,"mode":0,"inputs":[{"name":"image_1","localized_name":"image_1","type":"IMAGE","shape":7,"link":1957},{"name":"image_2","localized_name":"image_2","type":"IMAGE","shape":7,"link":null},{"name":"mask","localized_name":"mask","type":"IMAGE","shape":7,"link":null},{"name":"latent","localized_name":"latent","type":"LATENT","shape":7,"link":null},{"name":"vae","localized_name":"vae","type":"VAE","shape":7,"link":1968}],"outputs":[{"name":"latent_1","localized_name":"latent_1","type":"LATENT","links":[1885,1886],"slot_index":0},{"name":"latent_2","localized_name":"latent_2","type":"LATENT","links":[],"slot_index":1},{"name":"mask","localized_name":"mask","type":"MASK","links":[],"slot_index":2},{"name":"empty_latent","localized_name":"empty_latent","type":"LATENT","links":[1854,1869],"slot_index":3},{"name":"width","localized_name":"width","type":"INT","links":null},{"name":"height","localized_name":"height","type":"INT","links":null}],"properties":{"Node name for S&R":"VAEEncodeAdvanced"},"widgets_values":["false",1024,1024,"red",false,"16_channels"]},{"id":651,"type":"PreviewImage","pos":[4060,1710],"size":[210,246],"flags":{},"order":11,"mode":0,"inputs":[{"name":"images","localized_name":"images","type":"IMAGE","link":1963}],"outputs":[],"properties":{"Node name for S&R":"PreviewImage"},"widgets_values":[]},{"id":624,"type":"CLIPTextEncode","pos":[4329.92578125,1015.7978515625],"size":[306.2455749511719,162.64158630371094],"flags":{},"order":9,"mode":0,"inputs":[{"name":"clip","localized_name":"clip","type":"CLIP","link":1966}],"outputs":[{"name":"CONDITIONING","localized_name":"CONDITIONING","type":"CONDITIONING","links":[1860],"slot_index":0}],"properties":{"Node name for S&R":"CLIPTextEncode"},"widgets_values":["a close up shot of a red coffee mug on a wooden table"]},{"id":346,"type":"ModelSamplingAdvancedResolution","pos":[4034.77978515625,820.2175903320312],"size":[260.3999938964844,126],"flags":{},"order":14,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","link":1965},{"name":"latent_image","localized_name":"latent_image","type":"LATENT","link":1869}],"outputs":[{"name":"model","localized_name":"model","type":"MODEL","links":[1870],"slot_index":0}],"properties":{"Node name for S&R":"ModelSamplingAdvancedResolution"},"widgets_values":["exponential",1.35,0.85]},{"id":674,"type":"Note","pos":[4999.462890625,1603.108642578125],"size":[378.7174377441406,179.35989379882812],"flags":{},"order":0,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["eta is the amount of noise added after each step. It allows the model to change things more aggressively. Try comparing 0.0 vs 0.75.\n\nres_2m and res_3m will be sufficient quality samplers in most cases. Try res_2s and res_3s (which are 2x and 3x slower) if you want an extra quality boost.\n\nYou can get away with fewer than 40 steps in most cases, but 40 gives the model more time to correct any errors. Mileage may vary, experiment!"],"color":"#432","bgcolor":"#653"},{"id":677,"type":"Note","pos":[3783.440185546875,820.546142578125],"size":[210.66668701171875,91.33430480957031],"flags":{},"order":1,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["I have found these values often work quite well with img2img work with the beta57 scheduler.\n\n"],"color":"#432","bgcolor":"#653"},{"id":678,"type":"Note","pos":[3748.428466796875,1012.2677612304688],"size":[210,107.33900451660156],"flags":{},"order":2,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["If you wish to inpaint with another model, just replace the model loader and be sure to change CFG to whatever is appropriate for that model.\n\n"],"color":"#432","bgcolor":"#653"},{"id":672,"type":"Note","pos":[3747.097412109375,1187.65576171875],"size":[210,104.00474548339844],"flags":{},"order":3,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["Padding will increase or decrease the amount of area included around your mask that will give the model more context."],"color":"#432","bgcolor":"#653"},{"id":637,"type":"Note","pos":[3412.000732421875,1202.6614990234375],"size":[280.681884765625,88],"flags":{},"order":4,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["Draw your mask on your image for the area you would like to inpaint."],"color":"#432","bgcolor":"#653"},{"id":658,"type":"Image Comparer (rgthree)","pos":[5007.734375,1021.2513427734375],"size":[450.5037841796875,521.7816162109375],"flags":{},"order":20,"mode":0,"inputs":[{"name":"image_a","type":"IMAGE","dir":3,"link":1829},{"name":"image_b","type":"IMAGE","dir":3,"link":1823}],"outputs":[],"properties":{"comparer_mode":"Slide"},"widgets_values":[[{"name":"A","selected":true,"url":"/api/view?filename=rgthree.compare._temp_sxifa_00003_.png&type=temp&subfolder=&rand=0.14849022700275727"},{"name":"B","selected":true,"url":"/api/view?filename=rgthree.compare._temp_sxifa_00004_.png&type=temp&subfolder=&rand=0.8022985498723256"}]]},{"id":673,"type":"Note","pos":[4330.9345703125,1766.158203125],"size":[488.01611328125,234.97633361816406],"flags":{},"order":5,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["The parameters for \"masked\" will affect your inpainting area. \n\nTry changing weight_masked and end_step_masked. Lower values will allow the model to inpaint more aggressively. Higher will use more information from the original image. \n\n *** You can think of these like a \"denoise\" slider! *** \n\n(With lower weight, lower end_step acting like higher denoise).\n\nweight_scheduler_masked will change how quickly the value in weight_masked drops to zero. \"constant\" will never drop. Try linear_quadratic (drops very gradually, then suddenly at the end) or beta57 (drops earlier). These can make the inpainting process a bit smoother.\n\nHaving some information from the original image helps the model place objects more accurately, if you are replacing something that is already there."],"color":"#432","bgcolor":"#653"},{"id":617,"type":"ClownsharKSampler_Beta","pos":[4660,1020],"size":[315,690],"flags":{},"order":15,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","shape":7,"link":1870},{"name":"positive","localized_name":"positive","type":"CONDITIONING","shape":7,"link":1860},{"name":"negative","localized_name":"negative","type":"CONDITIONING","shape":7,"link":null},{"name":"latent_image","localized_name":"latent_image","type":"LATENT","shape":7,"link":1854},{"name":"sigmas","localized_name":"sigmas","type":"SIGMAS","shape":7,"link":null},{"name":"guides","localized_name":"guides","type":"GUIDES","shape":7,"link":1884},{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":null}],"outputs":[{"name":"output","localized_name":"output","type":"LATENT","links":[1936],"slot_index":0},{"name":"denoised","localized_name":"denoised","type":"LATENT","links":null},{"name":"options","localized_name":"options","type":"OPTIONS","links":null}],"properties":{"Node name for S&R":"ClownsharKSampler_Beta"},"widgets_values":[0.5,"multistep/res_3m","bong_tangent",40,-1,1,1,17,"fixed","standard",true]},{"id":619,"type":"VAEDecode","pos":[4830.248046875,919.5529174804688],"size":[140,46],"flags":{},"order":16,"mode":0,"inputs":[{"name":"samples","localized_name":"samples","type":"LATENT","link":1936},{"name":"vae","localized_name":"vae","type":"VAE","link":1967}],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":[1882,1902],"slot_index":0}],"properties":{"Node name for S&R":"VAEDecode"},"widgets_values":[]},{"id":638,"type":"LoadImage","pos":[3390,1370],"size":[315,314],"flags":{},"order":6,"mode":0,"inputs":[],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":[1828,1829,1955],"slot_index":0},{"name":"MASK","localized_name":"MASK","type":"MASK","links":[1956],"slot_index":1}],"properties":{"Node name for S&R":"LoadImage"},"widgets_values":["clipspace/clipspace-mask-147694527.20000002.png [input]","image"]},{"id":667,"type":"ImageResize+","pos":[5008.23974609375,755.5714111328125],"size":[210,218],"flags":{},"order":17,"mode":0,"inputs":[{"name":"image","localized_name":"image","type":"IMAGE","link":1882},{"name":"width","type":"INT","pos":[10,76],"widget":{"name":"width"},"link":1949},{"name":"height","type":"INT","pos":[10,100],"widget":{"name":"height"},"link":1950}],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":[1876],"slot_index":0},{"name":"width","localized_name":"width","type":"INT","links":null},{"name":"height","localized_name":"height","type":"INT","links":null}],"properties":{"Node name for S&R":"ImageResize+"},"widgets_values":[512,512,"lanczos","stretch","always",0]},{"id":657,"type":"ImageCompositeMasked","pos":[5242.94482421875,761.7905883789062],"size":[210,186],"flags":{},"order":19,"mode":0,"inputs":[{"name":"destination","localized_name":"destination","type":"IMAGE","link":1828},{"name":"source","localized_name":"source","type":"IMAGE","link":1876},{"name":"mask","localized_name":"mask","type":"MASK","shape":7,"link":1953},{"name":"x","type":"INT","pos":[10,76],"widget":{"name":"x"},"link":1952},{"name":"y","type":"INT","pos":[10,100],"widget":{"name":"y"},"link":1951}],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":[1823,1883],"slot_index":0}],"properties":{"Node name for S&R":"ImageCompositeMasked"},"widgets_values":[712,800,false]},{"id":650,"type":"MaskPreview","pos":[3778.59765625,1707.707763671875],"size":[181.5970001220703,246],"flags":{},"order":12,"mode":0,"inputs":[{"name":"mask","localized_name":"mask","type":"MASK","link":1962}],"outputs":[],"properties":{"Node name for S&R":"MaskPreview"},"widgets_values":[]},{"id":671,"type":"ClownGuides_Beta","pos":[4331.12109375,1240.1927490234375],"size":[303.2622985839844,450],"flags":{},"order":13,"mode":0,"inputs":[{"name":"guide_masked","localized_name":"guide_masked","type":"LATENT","shape":7,"link":1885},{"name":"guide_unmasked","localized_name":"guide_unmasked","type":"LATENT","shape":7,"link":1886},{"name":"mask","localized_name":"mask","type":"MASK","shape":7,"link":1954},{"name":"weights_masked","localized_name":"weights_masked","type":"SIGMAS","shape":7,"link":null},{"name":"weights_unmasked","localized_name":"weights_unmasked","type":"SIGMAS","shape":7,"link":null}],"outputs":[{"name":"guides","localized_name":"guides","type":"GUIDES","links":[1884],"slot_index":0}],"properties":{"Node name for S&R":"ClownGuides_Beta"},"widgets_values":["epsilon",false,true,0.5,1,1,1,"beta57","constant",0,0,10,-1,false]},{"id":679,"type":"Image Comparer (rgthree)","pos":[5488.171875,1085.4603271484375],"size":[402.1800842285156,455.1059875488281],"flags":{},"order":18,"mode":0,"inputs":[{"name":"image_a","type":"IMAGE","dir":3,"link":1964},{"name":"image_b","type":"IMAGE","dir":3,"link":1902}],"outputs":[],"properties":{"comparer_mode":"Slide"},"widgets_values":[[{"name":"A","selected":true,"url":"/api/view?filename=rgthree.compare._temp_ejvlo_00001_.png&type=temp&subfolder=&rand=0.5455521700112449"},{"name":"B","selected":true,"url":"/api/view?filename=rgthree.compare._temp_ejvlo_00002_.png&type=temp&subfolder=&rand=0.8898066636829509"}]]},{"id":676,"type":"Mask Bounding Box Aspect Ratio","pos":[3742.82421875,1383.6278076171875],"size":[252,250],"flags":{},"order":8,"mode":0,"inputs":[{"name":"image","localized_name":"image","type":"IMAGE","shape":7,"link":1955},{"name":"mask","localized_name":"mask","type":"MASK","shape":7,"link":1956}],"outputs":[{"name":"image","localized_name":"image","type":"IMAGE","links":[1957,1963,1964],"slot_index":0},{"name":"mask","localized_name":"mask","type":"MASK","links":[1954,1962],"slot_index":1},{"name":"mask_blurred","localized_name":"mask_blurred","type":"MASK","links":[1953],"slot_index":2},{"name":"x","localized_name":"x","type":"INT","links":[1952],"slot_index":3},{"name":"y","localized_name":"y","type":"INT","links":[1951],"slot_index":4},{"name":"width","localized_name":"width","type":"INT","links":[1949],"slot_index":5},{"name":"height","localized_name":"height","type":"INT","links":[1950],"slot_index":6}],"properties":{"Node name for S&R":"Mask Bounding Box Aspect Ratio"},"widgets_values":[20,20,1,false]},{"id":615,"type":"FluxLoader","pos":[3992.056396484375,1016.4193725585938],"size":[315,282],"flags":{},"order":7,"mode":0,"inputs":[],"outputs":[{"name":"model","localized_name":"model","type":"MODEL","links":[1965],"slot_index":0},{"name":"clip","localized_name":"clip","type":"CLIP","links":[1966],"slot_index":1},{"name":"vae","localized_name":"vae","type":"VAE","links":[1967,1968],"slot_index":2},{"name":"clip_vision","localized_name":"clip_vision","type":"CLIP_VISION","links":null},{"name":"style_model","localized_name":"style_model","type":"STYLE_MODEL","links":null}],"properties":{"Node name for S&R":"FluxLoader"},"widgets_values":["colossusProjectFlux_v42AIO.safetensors","default",".use_ckpt_clip",".none",".use_ckpt_vae",".none",".none"]}],"links":[[1823,657,0,658,1,"IMAGE"],[1828,638,0,657,0,"IMAGE"],[1829,638,0,658,0,"IMAGE"],[1854,663,3,617,3,"LATENT"],[1860,624,0,617,1,"CONDITIONING"],[1869,663,3,346,1,"LATENT"],[1870,346,0,617,0,"MODEL"],[1876,667,0,657,1,"IMAGE"],[1882,619,0,667,0,"IMAGE"],[1883,657,0,670,0,"IMAGE"],[1884,671,0,617,5,"GUIDES"],[1885,663,0,671,0,"LATENT"],[1886,663,0,671,1,"LATENT"],[1902,619,0,679,1,"IMAGE"],[1936,617,0,619,0,"LATENT"],[1949,676,5,667,1,"INT"],[1950,676,6,667,2,"INT"],[1951,676,4,657,4,"INT"],[1952,676,3,657,3,"INT"],[1953,676,2,657,2,"MASK"],[1954,676,1,671,2,"MASK"],[1955,638,0,676,0,"IMAGE"],[1956,638,1,676,1,"MASK"],[1957,676,0,663,0,"IMAGE"],[1962,676,1,650,0,"MASK"],[1963,676,0,651,0,"IMAGE"],[1964,676,0,679,0,"IMAGE"],[1965,615,0,346,0,"MODEL"],[1966,615,1,624,0,"CLIP"],[1967,615,2,619,1,"VAE"],[1968,615,2,663,4,"VAE"]],"groups":[],"config":{},"extra":{"ds":{"scale":1.4864362802414468,"offset":[-1333.621998147027,-469.4579733585599]},"node_versions":{"comfy-core":"0.3.26","comfyui_controlnet_aux":"1e9eac6377c882da8bb360c7544607036904362c","ComfyUI-VideoHelperSuite":"c36626c6028faca912eafcedbc71f1d342fb4d2a"},"VHS_latentpreview":false,"VHS_latentpreviewrate":0,"VHS_MetadataImage":true,"VHS_KeepIntermediate":true},"version":0.4} \ No newline at end of file diff --git a/ComfyUI/custom_nodes/RES4LYF/example_workflows/flux inpaint bongmath.json b/ComfyUI/custom_nodes/RES4LYF/example_workflows/flux inpaint bongmath.json new file mode 100644 index 0000000000000000000000000000000000000000..d2dc57b2be4acb7ee72ea9b4d15f29d1f9495ca5 --- /dev/null +++ b/ComfyUI/custom_nodes/RES4LYF/example_workflows/flux inpaint bongmath.json @@ -0,0 +1 @@ +{"last_node_id":1057,"last_link_id":3666,"nodes":[{"id":758,"type":"ImageResize+","pos":[1304.9573974609375,-352.7953796386719],"size":[210,218],"flags":{"collapsed":true},"order":24,"mode":0,"inputs":[{"name":"image","localized_name":"image","type":"IMAGE","link":2201},{"name":"width","type":"INT","pos":[10,76],"widget":{"name":"width"},"link":2204},{"name":"height","type":"INT","pos":[10,100],"widget":{"name":"height"},"link":2205}],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":[2198],"slot_index":0},{"name":"width","localized_name":"width","type":"INT","links":null},{"name":"height","localized_name":"height","type":"INT","links":null}],"properties":{"Node name for S&R":"ImageResize+"},"widgets_values":[512,512,"lanczos","stretch","always",0]},{"id":759,"type":"ImageCompositeMasked","pos":[1494.957763671875,-352.7953796386719],"size":[210,186],"flags":{"collapsed":true},"order":28,"mode":0,"inputs":[{"name":"destination","localized_name":"destination","type":"IMAGE","link":2211},{"name":"source","localized_name":"source","type":"IMAGE","link":2198},{"name":"mask","localized_name":"mask","type":"MASK","shape":7,"link":2301},{"name":"x","type":"INT","pos":[10,76],"widget":{"name":"x"},"link":2206},{"name":"y","type":"INT","pos":[10,100],"widget":{"name":"y"},"link":2207}],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":[2199,2200],"slot_index":0}],"properties":{"Node name for S&R":"ImageCompositeMasked"},"widgets_values":[712,800,false]},{"id":13,"type":"Reroute","pos":[-792.117919921875,-60.3060188293457],"size":[75,26],"flags":{},"order":10,"mode":0,"inputs":[{"name":"","type":"*","link":1964}],"outputs":[{"name":"","type":"MODEL","links":[2317],"slot_index":0}],"properties":{"showOutputText":false,"horizontal":false}},{"id":490,"type":"Reroute","pos":[-792.117919921875,-20.30602264404297],"size":[75,26],"flags":{},"order":6,"mode":0,"inputs":[{"name":"","type":"*","link":1965}],"outputs":[{"name":"","type":"CLIP","links":[3656],"slot_index":0}],"properties":{"showOutputText":false,"horizontal":false}},{"id":737,"type":"ModelSamplingAdvancedResolution","pos":[-972.117919921875,-330.3060302734375],"size":[260.3999938964844,126],"flags":{},"order":19,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","link":2318},{"name":"latent_image","localized_name":"latent_image","type":"LATENT","link":2125}],"outputs":[{"name":"model","localized_name":"model","type":"MODEL","links":[3661],"slot_index":0}],"properties":{"Node name for S&R":"ModelSamplingAdvancedResolution"},"widgets_values":["exponential",1.35,0.85]},{"id":786,"type":"TorchCompileModels","pos":[-1262.117919921875,-360.3060302734375],"size":[256.248779296875,178],"flags":{},"order":13,"mode":4,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","link":2317}],"outputs":[{"name":"model","localized_name":"model","type":"MODEL","links":[2318],"slot_index":0}],"properties":{"Node name for S&R":"TorchCompileModels"},"widgets_values":["inductor",false,"default",false,64,0]},{"id":664,"type":"ReFluxPatcher","pos":[-857.81005859375,-103.69645690917969],"size":[210,82],"flags":{"collapsed":true},"order":5,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","link":1963}],"outputs":[{"name":"model","localized_name":"model","type":"MODEL","links":[1964],"slot_index":0}],"properties":{"Node name for S&R":"ReFluxPatcher"},"widgets_values":["float64",true]},{"id":14,"type":"Reroute","pos":[-792.117919921875,19.69397735595703],"size":[75,26],"flags":{},"order":7,"mode":0,"inputs":[{"name":"","type":"*","link":1966}],"outputs":[{"name":"","type":"VAE","links":[2153,3508],"slot_index":0}],"properties":{"showOutputText":false,"horizontal":false}},{"id":745,"type":"VAEDecode","pos":[1136.7379150390625,-350.8069152832031],"size":[140,46],"flags":{"collapsed":true},"order":23,"mode":0,"inputs":[{"name":"samples","localized_name":"samples","type":"LATENT","link":3665},{"name":"vae","localized_name":"vae","type":"VAE","link":2153}],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":[2201,2208,2241,3568],"slot_index":0}],"properties":{"Node name for S&R":"VAEDecode","cnr_id":"comfy-core","ver":"0.3.29"},"widgets_values":[]},{"id":663,"type":"FluxLoader","pos":[-1262.117919921875,-130.3060302734375],"size":[374.41741943359375,282],"flags":{},"order":0,"mode":0,"inputs":[],"outputs":[{"name":"model","localized_name":"model","type":"MODEL","links":[1963],"slot_index":0},{"name":"clip","localized_name":"clip","type":"CLIP","links":[1965],"slot_index":1},{"name":"vae","localized_name":"vae","type":"VAE","links":[1966],"slot_index":2},{"name":"clip_vision","localized_name":"clip_vision","type":"CLIP_VISION","links":[],"slot_index":3},{"name":"style_model","localized_name":"style_model","type":"STYLE_MODEL","links":[],"slot_index":4}],"properties":{"Node name for S&R":"FluxLoader"},"widgets_values":["colossusProjectFlux_v42AIO.safetensors","fp8_e4m3fn_fast",".use_ckpt_clip",".none",".use_ckpt_vae","sigclip_vision_patch14_384.safetensors","flux1-redux-dev.safetensors"]},{"id":726,"type":"Mask Bounding Box Aspect Ratio","pos":[-153.93637084960938,317.7193298339844],"size":[252,250],"flags":{"collapsed":false},"order":12,"mode":0,"inputs":[{"name":"image","localized_name":"image","type":"IMAGE","shape":7,"link":2338},{"name":"mask","localized_name":"mask","type":"MASK","shape":7,"link":3659},{"name":"aspect_ratio","type":"FLOAT","pos":[10,204],"widget":{"name":"aspect_ratio"},"link":2100}],"outputs":[{"name":"image","localized_name":"image","type":"IMAGE","links":[2101,2102,2209,3606],"slot_index":0},{"name":"mask","localized_name":"mask","type":"MASK","links":[2106],"slot_index":1},{"name":"mask_blurred","localized_name":"mask_blurred","type":"MASK","links":[2301],"slot_index":2},{"name":"x","localized_name":"x","type":"INT","links":[2206],"slot_index":3},{"name":"y","localized_name":"y","type":"INT","links":[2207],"slot_index":4},{"name":"width","localized_name":"width","type":"INT","links":[2204],"slot_index":5},{"name":"height","localized_name":"height","type":"INT","links":[2205],"slot_index":6}],"properties":{"Node name for S&R":"Mask Bounding Box Aspect Ratio"},"widgets_values":[300,40,1.75,false]},{"id":728,"type":"MaskToImage","pos":[-151.61874389648438,849.1907348632812],"size":[176.39999389648438,26],"flags":{"collapsed":true},"order":14,"mode":0,"inputs":[{"name":"mask","localized_name":"mask","type":"MASK","link":2106}],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":[2103,3605],"slot_index":0}],"properties":{"Node name for S&R":"MaskToImage"},"widgets_values":[]},{"id":762,"type":"Image Comparer (rgthree)","pos":[1584.957763671875,-282.7954406738281],"size":[402.1800842285156,455.1059875488281],"flags":{},"order":25,"mode":0,"inputs":[{"name":"image_a","type":"IMAGE","dir":3,"link":2208},{"name":"image_b","type":"IMAGE","dir":3,"link":2209}],"outputs":[],"title":"Compare Inpaint Patch","properties":{"comparer_mode":"Slide"},"widgets_values":[[{"name":"A","selected":true,"url":"/api/view?filename=rgthree.compare._temp_hkrer_00001_.png&type=temp&subfolder=&rand=0.04538135261092524"},{"name":"B","selected":true,"url":"/api/view?filename=rgthree.compare._temp_hkrer_00002_.png&type=temp&subfolder=&rand=0.5206493331921973"}]],"color":"#332922","bgcolor":"#593930"},{"id":765,"type":"MaskToImage","pos":[2025.24755859375,225.29702758789062],"size":[182.28543090820312,26],"flags":{"collapsed":true},"order":17,"mode":0,"inputs":[{"name":"mask","localized_name":"mask","type":"MASK","link":2233}],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":[3570],"slot_index":0}],"properties":{"Node name for S&R":"MaskToImage"},"widgets_values":[]},{"id":1022,"type":"ImageBlend","pos":[2028.46533203125,274.42523193359375],"size":[210,102],"flags":{"collapsed":true},"order":27,"mode":0,"inputs":[{"name":"image1","localized_name":"image1","type":"IMAGE","link":3568},{"name":"image2","localized_name":"image2","type":"IMAGE","link":3570}],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":[3569],"slot_index":0}],"properties":{"Node name for S&R":"ImageBlend"},"widgets_values":[0.5,"overlay"]},{"id":1024,"type":"PreviewImage","pos":[2025.259765625,-279.3157958984375],"size":[413.7582092285156,445.8081359863281],"flags":{},"order":29,"mode":0,"inputs":[{"name":"images","localized_name":"images","type":"IMAGE","link":3569}],"outputs":[],"properties":{"Node name for S&R":"PreviewImage"},"widgets_values":[],"color":"#332922","bgcolor":"#593930"},{"id":760,"type":"SaveImage","pos":[1124.9569091796875,217.20452880859375],"size":[418.26055908203125,456.04608154296875],"flags":{},"order":30,"mode":0,"inputs":[{"name":"images","localized_name":"images","type":"IMAGE","link":2199}],"outputs":[],"title":"Save Output","properties":{},"widgets_values":["ComfyUI"],"color":"#232","bgcolor":"#353"},{"id":761,"type":"Image Comparer (rgthree)","pos":[1574.957763671875,227.20449829101562],"size":[410.4466247558594,447.8973388671875],"flags":{},"order":31,"mode":0,"inputs":[{"name":"image_a","type":"IMAGE","dir":3,"link":2210},{"name":"image_b","type":"IMAGE","dir":3,"link":2200}],"outputs":[],"title":"Compare Output","properties":{"comparer_mode":"Slide"},"widgets_values":[[{"name":"A","selected":true,"url":"/api/view?filename=rgthree.compare._temp_eoplx_00001_.png&type=temp&subfolder=&rand=0.7495673665351654"},{"name":"B","selected":true,"url":"/api/view?filename=rgthree.compare._temp_eoplx_00002_.png&type=temp&subfolder=&rand=0.17529967707052396"}]],"color":"#232","bgcolor":"#353"},{"id":744,"type":"SaveImage","pos":[1124.9569091796875,-292.7954406738281],"size":[424.53594970703125,455.0760192871094],"flags":{},"order":26,"mode":0,"inputs":[{"name":"images","localized_name":"images","type":"IMAGE","link":2241}],"outputs":[],"title":"Save Patch","properties":{"Node name for S&R":"SaveImage","cnr_id":"comfy-core","ver":"0.3.29"},"widgets_values":["ComfyUI"],"color":"#332922","bgcolor":"#593930"},{"id":725,"type":"Reroute","pos":[-243.93637084960938,317.7193298339844],"size":[75,26],"flags":{},"order":9,"mode":0,"inputs":[{"name":"","type":"*","link":3658}],"outputs":[{"name":"","type":"IMAGE","links":[2210,2211,2338],"slot_index":0}],"properties":{"showOutputText":false,"horizontal":false}},{"id":1040,"type":"PreviewImage","pos":[-566.879150390625,688.4552001953125],"size":[304.98114013671875,265.58380126953125],"flags":{},"order":20,"mode":0,"inputs":[{"name":"images","localized_name":"images","type":"IMAGE","link":3607}],"outputs":[],"properties":{"Node name for S&R":"PreviewImage"},"widgets_values":[]},{"id":1039,"type":"ImageBlend","pos":[-151.61874389648438,949.1907348632812],"size":[210,102],"flags":{"collapsed":true},"order":16,"mode":0,"inputs":[{"name":"image1","localized_name":"image1","type":"IMAGE","link":3606},{"name":"image2","localized_name":"image2","type":"IMAGE","link":3605}],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":[3607],"slot_index":0}],"properties":{"Node name for S&R":"ImageBlend"},"widgets_values":[0.5,"overlay"]},{"id":731,"type":"SimpleMath+","pos":[-151.61874389648438,799.1907348632812],"size":[315,98],"flags":{"collapsed":true},"order":8,"mode":0,"inputs":[{"name":"a","localized_name":"a","type":"*","shape":7,"link":2108},{"name":"b","localized_name":"b","type":"*","shape":7,"link":2109},{"name":"c","localized_name":"c","type":"*","shape":7,"link":null}],"outputs":[{"name":"INT","localized_name":"INT","type":"INT","links":null},{"name":"FLOAT","localized_name":"FLOAT","type":"FLOAT","links":[2100],"slot_index":1}],"properties":{"Node name for S&R":"SimpleMath+"},"widgets_values":["a/b"]},{"id":729,"type":"SetImageSize","pos":[-152.42828369140625,628.09228515625],"size":[210,102],"flags":{},"order":1,"mode":0,"inputs":[],"outputs":[{"name":"width","localized_name":"width","type":"INT","links":[2104,2108],"slot_index":0},{"name":"height","localized_name":"height","type":"INT","links":[2105,2109],"slot_index":1}],"title":"Inpaint Tile Size","properties":{"Node name for S&R":"SetImageSize"},"widgets_values":[1024,1024]},{"id":727,"type":"VAEEncodeAdvanced","pos":[-151.61874389648438,899.1907348632812],"size":[262.4812927246094,298],"flags":{"collapsed":true},"order":15,"mode":0,"inputs":[{"name":"image_1","localized_name":"image_1","type":"IMAGE","shape":7,"link":2101},{"name":"image_2","localized_name":"image_2","type":"IMAGE","shape":7,"link":2102},{"name":"mask","localized_name":"mask","type":"IMAGE","shape":7,"link":2103},{"name":"latent","localized_name":"latent","type":"LATENT","shape":7,"link":null},{"name":"vae","localized_name":"vae","type":"VAE","shape":7,"link":3508},{"name":"width","type":"INT","pos":[10,160],"widget":{"name":"width"},"link":2104},{"name":"height","type":"INT","pos":[10,184],"widget":{"name":"height"},"link":2105}],"outputs":[{"name":"latent_1","localized_name":"latent_1","type":"LATENT","links":[3602,3603,3611],"slot_index":0},{"name":"latent_2","localized_name":"latent_2","type":"LATENT","links":[],"slot_index":1},{"name":"mask","localized_name":"mask","type":"MASK","links":[2233,3604],"slot_index":2},{"name":"empty_latent","localized_name":"empty_latent","type":"LATENT","links":[2125,3660],"slot_index":3},{"name":"width","localized_name":"width","type":"INT","links":[],"slot_index":4},{"name":"height","localized_name":"height","type":"INT","links":[]}],"properties":{"Node name for S&R":"VAEEncodeAdvanced"},"widgets_values":["false",1344,768,"red",false,"16_channels"]},{"id":1038,"type":"ClownGuides_Beta","pos":[-570,-350],"size":[315,450],"flags":{},"order":18,"mode":0,"inputs":[{"name":"guide_masked","localized_name":"guide_masked","type":"LATENT","shape":7,"link":3602},{"name":"guide_unmasked","localized_name":"guide_unmasked","type":"LATENT","shape":7,"link":3603},{"name":"mask","localized_name":"mask","type":"MASK","shape":7,"link":3604},{"name":"weights_masked","localized_name":"weights_masked","type":"SIGMAS","shape":7,"link":null},{"name":"weights_unmasked","localized_name":"weights_unmasked","type":"SIGMAS","shape":7,"link":null}],"outputs":[{"name":"guides","localized_name":"guides","type":"GUIDES","links":[3609,3641],"slot_index":0}],"properties":{"Node name for S&R":"ClownGuides_Beta"},"widgets_values":["inversion",false,false,0,1,1,1,"constant","constant",0,0,1,-1,false]},{"id":1055,"type":"LoadImage","pos":[-588.5657958984375,310.53521728515625],"size":[315,314],"flags":{},"order":2,"mode":0,"inputs":[],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":[3658],"slot_index":0},{"name":"MASK","localized_name":"MASK","type":"MASK","links":[3659],"slot_index":1}],"properties":{"Node name for S&R":"LoadImage"},"widgets_values":["clipspace/clipspace-mask-264573735.png [input]","image"]},{"id":1041,"type":"ClownGuide_Style_Beta","pos":[-210,-350],"size":[315,286],"flags":{},"order":21,"mode":4,"inputs":[{"name":"guide","localized_name":"guide","type":"LATENT","shape":7,"link":3611},{"name":"mask","localized_name":"mask","type":"MASK","shape":7,"link":null},{"name":"weights","localized_name":"weights","type":"SIGMAS","shape":7,"link":null},{"name":"guides","localized_name":"guides","type":"GUIDES","shape":7,"link":3609}],"outputs":[{"name":"guides","localized_name":"guides","type":"GUIDES","links":[],"slot_index":0}],"properties":{"Node name for S&R":"ClownGuide_Style_Beta"},"widgets_values":["positive","WCT",1,1,"constant",0,10,false]},{"id":1056,"type":"CLIPTextEncode","pos":[251.24851989746094,-166.23118591308594],"size":[311.10028076171875,154.46998596191406],"flags":{"collapsed":false},"order":11,"mode":0,"inputs":[{"name":"clip","localized_name":"clip","type":"CLIP","link":3656}],"outputs":[{"name":"CONDITIONING","localized_name":"CONDITIONING","type":"CONDITIONING","links":[3662],"slot_index":0}],"properties":{"Node name for S&R":"CLIPTextEncode","cnr_id":"comfy-core","ver":"0.3.29"},"widgets_values":["a soviet T72 tank driving down the middle of a road in a city, crossing over the crosswalk, aiming its gun at the camera"]},{"id":1043,"type":"ClownOptions_SDE_Beta","pos":[249.46791076660156,47.537593841552734],"size":[315,266],"flags":{},"order":3,"mode":0,"inputs":[{"name":"etas","localized_name":"etas","type":"SIGMAS","shape":7,"link":null},{"name":"etas_substep","localized_name":"etas_substep","type":"SIGMAS","shape":7,"link":null},{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":null}],"outputs":[{"name":"options","localized_name":"options","type":"OPTIONS","links":[3643],"slot_index":0}],"properties":{"Node name for S&R":"ClownOptions_SDE_Beta"},"widgets_values":["gaussian","gaussian","hard","hard",0.5,0.75,-1,"fixed"]},{"id":1018,"type":"ClownOptions_ImplicitSteps_Beta","pos":[611.24853515625,-371.7803649902344],"size":[340.20001220703125,130],"flags":{},"order":4,"mode":0,"inputs":[{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":null}],"outputs":[{"name":"options","localized_name":"options","type":"OPTIONS","links":[3664],"slot_index":0}],"properties":{"Node name for S&R":"ClownOptions_ImplicitSteps_Beta"},"widgets_values":["bongmath","bongmath",2,0]},{"id":1053,"type":"ClownsharKSampler_Beta","pos":[611.24853515625,-181.78033447265625],"size":[340.55120849609375,730],"flags":{},"order":22,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","shape":7,"link":3661},{"name":"positive","localized_name":"positive","type":"CONDITIONING","shape":7,"link":3662},{"name":"negative","localized_name":"negative","type":"CONDITIONING","shape":7,"link":null},{"name":"latent_image","localized_name":"latent_image","type":"LATENT","shape":7,"link":3660},{"name":"sigmas","localized_name":"sigmas","type":"SIGMAS","shape":7,"link":null},{"name":"guides","localized_name":"guides","type":"GUIDES","shape":7,"link":3641},{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":3664},{"name":"options 2","type":"OPTIONS","link":3643},{"name":"options 3","type":"OPTIONS","link":null}],"outputs":[{"name":"output","localized_name":"output","type":"LATENT","links":[3665],"slot_index":0},{"name":"denoised","localized_name":"denoised","type":"LATENT","links":[],"slot_index":1},{"name":"options","localized_name":"options","type":"OPTIONS","links":null}],"properties":{"Node name for S&R":"ClownsharKSampler_Beta","cnr_id":"RES4LYF","ver":"5ce9b5a77c227bf864e447a1e65305bf6cada5c2"},"widgets_values":[0.5,"exponential/res_2s","beta57",30,-1,1,1,0,"fixed","standard",true]}],"links":[[1963,663,0,664,0,"MODEL"],[1964,664,0,13,0,"*"],[1965,663,1,490,0,"*"],[1966,663,2,14,0,"*"],[2100,731,1,726,2,"FLOAT"],[2101,726,0,727,0,"IMAGE"],[2102,726,0,727,1,"IMAGE"],[2103,728,0,727,2,"IMAGE"],[2104,729,0,727,5,"INT"],[2105,729,1,727,6,"INT"],[2106,726,1,728,0,"MASK"],[2108,729,0,731,0,"*"],[2109,729,1,731,1,"*"],[2125,727,3,737,1,"LATENT"],[2153,14,0,745,1,"VAE"],[2198,758,0,759,1,"IMAGE"],[2199,759,0,760,0,"IMAGE"],[2200,759,0,761,1,"IMAGE"],[2201,745,0,758,0,"IMAGE"],[2204,726,5,758,1,"INT"],[2205,726,6,758,2,"INT"],[2206,726,3,759,3,"INT"],[2207,726,4,759,4,"INT"],[2208,745,0,762,0,"IMAGE"],[2209,726,0,762,1,"IMAGE"],[2210,725,0,761,0,"IMAGE"],[2211,725,0,759,0,"IMAGE"],[2233,727,2,765,0,"MASK"],[2241,745,0,744,0,"IMAGE"],[2301,726,2,759,2,"MASK"],[2317,13,0,786,0,"MODEL"],[2318,786,0,737,0,"MODEL"],[2338,725,0,726,0,"IMAGE"],[3508,14,0,727,4,"VAE"],[3568,745,0,1022,0,"IMAGE"],[3569,1022,0,1024,0,"IMAGE"],[3570,765,0,1022,1,"IMAGE"],[3602,727,0,1038,0,"LATENT"],[3603,727,0,1038,1,"LATENT"],[3604,727,2,1038,2,"MASK"],[3605,728,0,1039,1,"IMAGE"],[3606,726,0,1039,0,"IMAGE"],[3607,1039,0,1040,0,"IMAGE"],[3609,1038,0,1041,3,"GUIDES"],[3611,727,0,1041,0,"LATENT"],[3641,1038,0,1053,5,"GUIDES"],[3643,1043,0,1053,7,"OPTIONS"],[3656,490,0,1056,0,"CLIP"],[3658,1055,0,725,0,"*"],[3659,1055,1,726,1,"MASK"],[3660,727,3,1053,3,"LATENT"],[3661,737,0,1053,0,"MODEL"],[3662,1056,0,1053,1,"CONDITIONING"],[3664,1018,0,1053,6,"OPTIONS"],[3665,1053,0,745,0,"LATENT"]],"groups":[{"id":1,"title":"Prepare Input","bounding":[-611.2013549804688,224.80706787109375,755.7755737304688,762.867431640625],"color":"#3f789e","font_size":24,"flags":{}},{"id":2,"title":"Patch and Stitch","bounding":[1079.80078125,-451.0775451660156,1387.1339111328125,1156.21923828125],"color":"#3f789e","font_size":24,"flags":{}},{"id":3,"title":"Loaders","bounding":[-1311.103515625,-459.84735107421875,645.1646118164062,640.0969848632812],"color":"#3f789e","font_size":24,"flags":{}},{"id":5,"title":"Sampling","bounding":[204.55885314941406,-455.63134765625,812.3118896484375,1071.2481689453125],"color":"#3f789e","font_size":24,"flags":{}},{"id":6,"title":"Guides","bounding":[-611.8231811523438,-457.95751953125,755.8380737304688,634.3353271484375],"color":"#3f789e","font_size":24,"flags":{}}],"config":{},"extra":{"ds":{"scale":1.3072020475058177,"offset":[3303.9392897394673,741.4045019633804]},"VHS_latentpreview":false,"VHS_latentpreviewrate":0,"ue_links":[],"VHS_MetadataImage":true,"VHS_KeepIntermediate":true},"version":0.4} \ No newline at end of file diff --git a/ComfyUI/custom_nodes/RES4LYF/example_workflows/flux inpainting.jpg b/ComfyUI/custom_nodes/RES4LYF/example_workflows/flux inpainting.jpg new file mode 100644 index 0000000000000000000000000000000000000000..7787aea8c24fedd1c68009b0c0b9e458caf7c54a Binary files /dev/null and b/ComfyUI/custom_nodes/RES4LYF/example_workflows/flux inpainting.jpg differ diff --git a/ComfyUI/custom_nodes/RES4LYF/example_workflows/flux inpainting.json b/ComfyUI/custom_nodes/RES4LYF/example_workflows/flux inpainting.json new file mode 100644 index 0000000000000000000000000000000000000000..99866142b4e99fc790719b2cf59f153e6289c4a6 --- /dev/null +++ b/ComfyUI/custom_nodes/RES4LYF/example_workflows/flux inpainting.json @@ -0,0 +1 @@ +{"last_node_id":637,"last_link_id":1778,"nodes":[{"id":617,"type":"ClownsharKSampler_Beta","pos":[4647.0654296875,1012.7097778320312],"size":[315,690],"flags":{},"order":9,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","shape":7,"link":1730},{"name":"positive","localized_name":"positive","type":"CONDITIONING","shape":7,"link":1754},{"name":"negative","localized_name":"negative","type":"CONDITIONING","shape":7,"link":null},{"name":"latent_image","localized_name":"latent_image","type":"LATENT","shape":7,"link":1733},{"name":"sigmas","localized_name":"sigmas","type":"SIGMAS","shape":7,"link":null},{"name":"guides","localized_name":"guides","type":"GUIDES","shape":7,"link":1744},{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":null}],"outputs":[{"name":"output","localized_name":"output","type":"LATENT","links":[1756],"slot_index":0},{"name":"denoised","localized_name":"denoised","type":"LATENT","links":null},{"name":"options","localized_name":"options","type":"OPTIONS","links":null}],"properties":{"Node name for S&R":"ClownsharKSampler_Beta"},"widgets_values":[0.5,"multistep/res_3m","beta57",40,30,1,1,15,"fixed","standard",true]},{"id":619,"type":"VAEDecode","pos":[5354.6103515625,907.4140014648438],"size":[210,46],"flags":{},"order":11,"mode":0,"inputs":[{"name":"samples","localized_name":"samples","type":"LATENT","link":1771},{"name":"vae","localized_name":"vae","type":"VAE","link":1740}],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":[1765],"slot_index":0}],"properties":{"Node name for S&R":"VAEDecode"},"widgets_values":[]},{"id":631,"type":"SaveImage","pos":[5357.8349609375,1012.29443359375],"size":[315,270],"flags":{},"order":12,"mode":0,"inputs":[{"name":"images","localized_name":"images","type":"IMAGE","link":1765}],"outputs":[],"properties":{},"widgets_values":["ComfyUI"]},{"id":624,"type":"CLIPTextEncode","pos":[4233.03955078125,1015.2553100585938],"size":[380.6268615722656,114.73346710205078],"flags":{},"order":3,"mode":0,"inputs":[{"name":"clip","localized_name":"clip","type":"CLIP","link":1753}],"outputs":[{"name":"CONDITIONING","localized_name":"CONDITIONING","type":"CONDITIONING","links":[1754],"slot_index":0}],"properties":{"Node name for S&R":"CLIPTextEncode"},"widgets_values":["a weird alien tripod with a purple woman's head on top "]},{"id":615,"type":"FluxLoader","pos":[3883.31982421875,1018.0260620117188],"size":[315,282],"flags":{},"order":0,"mode":0,"inputs":[],"outputs":[{"name":"model","localized_name":"model","type":"MODEL","links":[1766],"slot_index":0},{"name":"clip","localized_name":"clip","type":"CLIP","links":[1753],"slot_index":1},{"name":"vae","localized_name":"vae","type":"VAE","links":[1723,1740],"slot_index":2},{"name":"clip_vision","localized_name":"clip_vision","type":"CLIP_VISION","links":null},{"name":"style_model","localized_name":"style_model","type":"STYLE_MODEL","links":null}],"properties":{"Node name for S&R":"FluxLoader"},"widgets_values":["colossusProjectFlux_v42AIO.safetensors","default",".use_ckpt_clip",".none",".use_ckpt_vae",".none",".none"]},{"id":346,"type":"ModelSamplingAdvancedResolution","pos":[3940.993408203125,831.2357177734375],"size":[260.3999938964844,126],"flags":{},"order":7,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","link":1766},{"name":"latent_image","localized_name":"latent_image","type":"LATENT","link":1721}],"outputs":[{"name":"model","localized_name":"model","type":"MODEL","links":[1730],"slot_index":0}],"properties":{"Node name for S&R":"ModelSamplingAdvancedResolution"},"widgets_values":["exponential",1.35,0.85]},{"id":620,"type":"ClownGuide_Beta","pos":[4355.02392578125,1383.0733642578125],"size":[264.49530029296875,290],"flags":{},"order":6,"mode":0,"inputs":[{"name":"guide","localized_name":"guide","type":"LATENT","shape":7,"link":1767},{"name":"mask","localized_name":"mask","type":"MASK","shape":7,"link":1745},{"name":"weights","localized_name":"weights","type":"SIGMAS","shape":7,"link":null}],"outputs":[{"name":"guides","localized_name":"guides","type":"GUIDES","links":[1744],"slot_index":0}],"properties":{"Node name for S&R":"ClownGuide_Beta"},"widgets_values":["flow",false,false,1,1,"constant",0,40,false]},{"id":626,"type":"ClownsharkChainsampler_Beta","pos":[4988.4580078125,1015.6370239257812],"size":[340.20001220703125,509.99993896484375],"flags":{},"order":10,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","shape":7,"link":null},{"name":"positive","localized_name":"positive","type":"CONDITIONING","shape":7,"link":null},{"name":"negative","localized_name":"negative","type":"CONDITIONING","shape":7,"link":null},{"name":"sigmas","localized_name":"sigmas","type":"SIGMAS","shape":7,"link":null},{"name":"latent_image","localized_name":"latent_image","type":"LATENT","shape":7,"link":1756},{"name":"guides","localized_name":"guides","type":"GUIDES","shape":7,"link":1770},{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":null}],"outputs":[{"name":"output","localized_name":"output","type":"LATENT","links":[1771],"slot_index":0},{"name":"denoised","localized_name":"denoised","type":"LATENT","links":[],"slot_index":1},{"name":"options","localized_name":"options","type":"OPTIONS","links":null}],"properties":{"Node name for S&R":"ClownsharkChainsampler_Beta"},"widgets_values":[0.5,"multistep/res_3m",-1,1,"resample",true]},{"id":422,"type":"VAEEncodeAdvanced","pos":[4080.7021484375,1383.7640380859375],"size":[240.29074096679688,278],"flags":{},"order":4,"mode":0,"inputs":[{"name":"image_1","localized_name":"image_1","type":"IMAGE","shape":7,"link":1777},{"name":"image_2","localized_name":"image_2","type":"IMAGE","shape":7,"link":null},{"name":"mask","localized_name":"mask","type":"IMAGE","shape":7,"link":null},{"name":"latent","localized_name":"latent","type":"LATENT","shape":7,"link":null},{"name":"vae","localized_name":"vae","type":"VAE","shape":7,"link":1723}],"outputs":[{"name":"latent_1","localized_name":"latent_1","type":"LATENT","links":[1767,1772],"slot_index":0},{"name":"latent_2","localized_name":"latent_2","type":"LATENT","links":[],"slot_index":1},{"name":"mask","localized_name":"mask","type":"MASK","links":[],"slot_index":2},{"name":"empty_latent","localized_name":"empty_latent","type":"LATENT","links":[1721,1733],"slot_index":3},{"name":"width","localized_name":"width","type":"INT","links":null},{"name":"height","localized_name":"height","type":"INT","links":null}],"properties":{"Node name for S&R":"VAEEncodeAdvanced"},"widgets_values":["false",1024,1024,"red",false,"16_channels"]},{"id":627,"type":"ClownGuide_Beta","pos":[4701.61572265625,1776.4569091796875],"size":[264.49530029296875,290],"flags":{},"order":8,"mode":0,"inputs":[{"name":"guide","localized_name":"guide","type":"LATENT","shape":7,"link":1772},{"name":"mask","localized_name":"mask","type":"MASK","shape":7,"link":1778},{"name":"weights","localized_name":"weights","type":"SIGMAS","shape":7,"link":null}],"outputs":[{"name":"guides","localized_name":"guides","type":"GUIDES","links":[1770],"slot_index":0}],"properties":{"Node name for S&R":"ClownGuide_Beta"},"widgets_values":["flow",false,false,1,1,"constant",0,40,false]},{"id":634,"type":"GrowMask","pos":[4102.16650390625,1794.78857421875],"size":[210,82],"flags":{},"order":5,"mode":0,"inputs":[{"name":"mask","localized_name":"mask","type":"MASK","link":1774}],"outputs":[{"name":"MASK","localized_name":"MASK","type":"MASK","links":[1778],"slot_index":0}],"properties":{"Node name for S&R":"GrowMask"},"widgets_values":[20,false]},{"id":621,"type":"LoadImage","pos":[3718.762939453125,1384.687255859375],"size":[319.33538818359375,313.277587890625],"flags":{},"order":1,"mode":0,"inputs":[],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":[1777],"slot_index":0},{"name":"MASK","localized_name":"MASK","type":"MASK","links":[1745,1774],"slot_index":1}],"properties":{"Node name for S&R":"LoadImage"},"widgets_values":["clipspace/clipspace-mask-150185841.8.png [input]","image"]},{"id":637,"type":"Note","pos":[3731.639892578125,1771.010009765625],"size":[282.0154113769531,88],"flags":{},"order":2,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["Draw your mask on your image for the area you would like to inpaint."],"color":"#432","bgcolor":"#653"}],"links":[[1721,422,3,346,1,"LATENT"],[1723,615,2,422,4,"VAE"],[1730,346,0,617,0,"MODEL"],[1733,422,3,617,3,"LATENT"],[1740,615,2,619,1,"VAE"],[1744,620,0,617,5,"GUIDES"],[1745,621,1,620,1,"MASK"],[1753,615,1,624,0,"CLIP"],[1754,624,0,617,1,"CONDITIONING"],[1756,617,0,626,4,"LATENT"],[1765,619,0,631,0,"IMAGE"],[1766,615,0,346,0,"MODEL"],[1767,422,0,620,0,"LATENT"],[1770,627,0,626,5,"GUIDES"],[1771,626,0,619,0,"LATENT"],[1772,422,0,627,0,"LATENT"],[1774,621,1,634,0,"MASK"],[1777,621,0,422,0,"IMAGE"],[1778,634,0,627,1,"MASK"]],"groups":[],"config":{},"extra":{"ds":{"scale":1.3109994191500227,"offset":[-1810.8840558767379,-650.1028379746496]},"node_versions":{"comfy-core":"0.3.26","comfyui_controlnet_aux":"1e9eac6377c882da8bb360c7544607036904362c","ComfyUI-VideoHelperSuite":"c36626c6028faca912eafcedbc71f1d342fb4d2a"},"VHS_latentpreview":false,"VHS_latentpreviewrate":0,"VHS_MetadataImage":true,"VHS_KeepIntermediate":true},"version":0.4} \ No newline at end of file diff --git a/ComfyUI/custom_nodes/RES4LYF/example_workflows/flux regional antiblur.json b/ComfyUI/custom_nodes/RES4LYF/example_workflows/flux regional antiblur.json new file mode 100644 index 0000000000000000000000000000000000000000..155d31068976b4f7f37977f4b0520d06e8b67f2f --- /dev/null +++ b/ComfyUI/custom_nodes/RES4LYF/example_workflows/flux regional antiblur.json @@ -0,0 +1 @@ +{"last_node_id":723,"last_link_id":2096,"nodes":[{"id":13,"type":"Reroute","pos":[1280,-650],"size":[75,26],"flags":{},"order":11,"mode":0,"inputs":[{"name":"","type":"*","link":1964}],"outputs":[{"name":"","type":"MODEL","links":[1967],"slot_index":0}],"properties":{"showOutputText":false,"horizontal":false}},{"id":490,"type":"Reroute","pos":[1280,-610],"size":[75,26],"flags":{},"order":8,"mode":0,"inputs":[{"name":"","type":"*","link":1965}],"outputs":[{"name":"","type":"CLIP","links":[1939,2092],"slot_index":0}],"properties":{"showOutputText":false,"horizontal":false}},{"id":14,"type":"Reroute","pos":[1280,-570],"size":[75,26],"flags":{},"order":9,"mode":0,"inputs":[{"name":"","type":"*","link":1966}],"outputs":[{"name":"","type":"VAE","links":[18,1328],"slot_index":0}],"properties":{"showOutputText":false,"horizontal":false}},{"id":398,"type":"SaveImage","pos":[1379.9996337890625,-267.2835998535156],"size":[341.7508850097656,561.0067749023438],"flags":{},"order":20,"mode":0,"inputs":[{"name":"images","localized_name":"images","type":"IMAGE","link":1329}],"outputs":[],"properties":{"Node name for S&R":"SaveImage","cnr_id":"comfy-core","ver":"0.3.29"},"widgets_values":["ComfyUI"]},{"id":701,"type":"Note","pos":[80,-520],"size":[342.05950927734375,88],"flags":{},"order":0,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["I usually just lazily draw masks in Load Image nodes (with some random image loaded), but for the sake of reproducibility, here's another approach."],"color":"#432","bgcolor":"#653"},{"id":712,"type":"Note","pos":[-210,-520],"size":[245.76409912109375,91.6677017211914],"flags":{},"order":1,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["So long as these masks are all the same size, the regional conditioning nodes will handle resizing to the image size for you."],"color":"#432","bgcolor":"#653"},{"id":676,"type":"InvertMask","pos":[20,-370],"size":[142.42074584960938,26],"flags":{},"order":10,"mode":0,"inputs":[{"name":"mask","localized_name":"mask","type":"MASK","link":2073}],"outputs":[{"name":"MASK","localized_name":"MASK","type":"MASK","links":[2083],"slot_index":0}],"properties":{"Node name for S&R":"InvertMask"},"widgets_values":[]},{"id":663,"type":"FluxLoader","pos":[630,-720],"size":[374.41741943359375,282],"flags":{},"order":2,"mode":0,"inputs":[],"outputs":[{"name":"model","localized_name":"model","type":"MODEL","links":[1963],"slot_index":0},{"name":"clip","localized_name":"clip","type":"CLIP","links":[1965],"slot_index":1},{"name":"vae","localized_name":"vae","type":"VAE","links":[1966],"slot_index":2},{"name":"clip_vision","localized_name":"clip_vision","type":"CLIP_VISION","links":[],"slot_index":3},{"name":"style_model","localized_name":"style_model","type":"STYLE_MODEL","links":[],"slot_index":4}],"properties":{"Node name for S&R":"FluxLoader"},"widgets_values":["colossusProjectFlux_v42AIO.safetensors","fp8_e4m3fn_fast",".use_ckpt_clip",".none",".use_ckpt_vae",".none",".none"]},{"id":662,"type":"CLIPTextEncode","pos":[460,-370],"size":[210,88],"flags":{"collapsed":false},"order":12,"mode":0,"inputs":[{"name":"clip","localized_name":"clip","type":"CLIP","link":1939}],"outputs":[{"name":"CONDITIONING","localized_name":"CONDITIONING","type":"CONDITIONING","links":[2094],"slot_index":0}],"properties":{"Node name for S&R":"CLIPTextEncode","cnr_id":"comfy-core","ver":"0.3.29"},"widgets_values":["a woman wearing a red flannel shirt and a cute shark plush blue hat"]},{"id":723,"type":"CLIPTextEncode","pos":[460,-240],"size":[210,88],"flags":{"collapsed":false},"order":13,"mode":0,"inputs":[{"name":"clip","localized_name":"clip","type":"CLIP","link":2092}],"outputs":[{"name":"CONDITIONING","localized_name":"CONDITIONING","type":"CONDITIONING","links":[2093],"slot_index":0}],"properties":{"Node name for S&R":"CLIPTextEncode","cnr_id":"comfy-core","ver":"0.3.29"},"widgets_values":["a college campus"]},{"id":7,"type":"VAEEncodeAdvanced","pos":[719.6110229492188,16.752899169921875],"size":[261.2217712402344,279.3136901855469],"flags":{},"order":14,"mode":0,"inputs":[{"name":"image_1","localized_name":"image_1","type":"IMAGE","shape":7,"link":null},{"name":"image_2","localized_name":"image_2","type":"IMAGE","shape":7,"link":null},{"name":"mask","localized_name":"mask","type":"IMAGE","shape":7,"link":null},{"name":"latent","localized_name":"latent","type":"LATENT","shape":7,"link":null},{"name":"vae","localized_name":"vae","type":"VAE","shape":7,"link":18}],"outputs":[{"name":"latent_1","localized_name":"latent_1","type":"LATENT","links":[],"slot_index":0},{"name":"latent_2","localized_name":"latent_2","type":"LATENT","links":[],"slot_index":1},{"name":"mask","localized_name":"mask","type":"MASK","links":[],"slot_index":2},{"name":"empty_latent","localized_name":"empty_latent","type":"LATENT","links":[1399],"slot_index":3},{"name":"width","localized_name":"width","type":"INT","links":null},{"name":"height","localized_name":"height","type":"INT","links":null}],"properties":{"Node name for S&R":"VAEEncodeAdvanced","cnr_id":"RES4LYF","ver":"5ce9b5a77c227bf864e447a1e65305bf6cada5c2"},"widgets_values":["false",1024,1024,"red",false,"16_channels"]},{"id":710,"type":"MaskPreview","pos":[180,-190],"size":[210,246],"flags":{},"order":16,"mode":0,"inputs":[{"name":"mask","localized_name":"mask","type":"MASK","link":2054}],"outputs":[],"properties":{"Node name for S&R":"MaskPreview"},"widgets_values":[]},{"id":664,"type":"ReFluxPatcher","pos":[1040,-720],"size":[210,82],"flags":{},"order":7,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","link":1963}],"outputs":[{"name":"model","localized_name":"model","type":"MODEL","links":[1964],"slot_index":0}],"properties":{"Node name for S&R":"ReFluxPatcher"},"widgets_values":["float64",true]},{"id":397,"type":"VAEDecode","pos":[1382.3662109375,-374.17059326171875],"size":[210,46],"flags":{},"order":19,"mode":0,"inputs":[{"name":"samples","localized_name":"samples","type":"LATENT","link":2096},{"name":"vae","localized_name":"vae","type":"VAE","link":1328}],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":[1329],"slot_index":0}],"properties":{"Node name for S&R":"VAEDecode","cnr_id":"comfy-core","ver":"0.3.29"},"widgets_values":[]},{"id":715,"type":"SolidMask","pos":[-220,-370],"size":[210,106],"flags":{},"order":3,"mode":0,"inputs":[],"outputs":[{"name":"MASK","localized_name":"MASK","type":"MASK","links":[2073],"slot_index":0}],"properties":{"Node name for S&R":"SolidMask"},"widgets_values":[1,1024,1024]},{"id":716,"type":"SolidMask","pos":[-220,-220],"size":[210,106],"flags":{},"order":4,"mode":0,"inputs":[],"outputs":[{"name":"MASK","localized_name":"MASK","type":"MASK","links":[2065],"slot_index":0}],"properties":{"Node name for S&R":"SolidMask"},"widgets_values":[1,384,864]},{"id":709,"type":"MaskComposite","pos":[190,-370],"size":[210,126],"flags":{},"order":15,"mode":0,"inputs":[{"name":"destination","localized_name":"destination","type":"MASK","link":2083},{"name":"source","localized_name":"source","type":"MASK","link":2065}],"outputs":[{"name":"MASK","localized_name":"MASK","type":"MASK","links":[2054,2091],"slot_index":0}],"properties":{"Node name for S&R":"MaskComposite"},"widgets_values":[256,160,"add"]},{"id":704,"type":"Note","pos":[101.74818420410156,112.67951965332031],"size":[290.7107238769531,155.35317993164062],"flags":{},"order":5,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["ClownRegionalConditionings:\n\nTry raising or lowering weight, and changing the weight scheduler from beta57 to Karras (weakens more quickly), or to linear quadratic (stronger late).\n\nTry changing region_bleed_start_step (earlier will make the image blend together more), and end_step."],"color":"#432","bgcolor":"#653"},{"id":401,"type":"ClownsharKSampler_Beta","pos":[1010,-370],"size":[340.55120849609375,666.8208618164062],"flags":{},"order":18,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","shape":7,"link":1967},{"name":"positive","localized_name":"positive","type":"CONDITIONING","shape":7,"link":2095},{"name":"negative","localized_name":"negative","type":"CONDITIONING","shape":7,"link":null},{"name":"latent_image","localized_name":"latent_image","type":"LATENT","shape":7,"link":1399},{"name":"sigmas","localized_name":"sigmas","type":"SIGMAS","shape":7,"link":null},{"name":"guides","localized_name":"guides","type":"GUIDES","shape":7,"link":null},{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":null}],"outputs":[{"name":"output","localized_name":"output","type":"LATENT","links":[2096],"slot_index":0},{"name":"denoised","localized_name":"denoised","type":"LATENT","links":null},{"name":"options","localized_name":"options","type":"OPTIONS","links":null}],"properties":{"Node name for S&R":"ClownsharKSampler_Beta","cnr_id":"RES4LYF","ver":"5ce9b5a77c227bf864e447a1e65305bf6cada5c2"},"widgets_values":[0.5,"multistep/res_2m","bong_tangent",30,-1,1,1,3,"fixed","standard",true]},{"id":722,"type":"ClownRegionalConditioning2","pos":[690,-370],"size":[287.75750732421875,330],"flags":{},"order":17,"mode":0,"inputs":[{"name":"conditioning_masked","localized_name":"conditioning_masked","type":"CONDITIONING","shape":7,"link":2094},{"name":"conditioning_unmasked","localized_name":"conditioning_unmasked","type":"CONDITIONING","shape":7,"link":2093},{"name":"mask","localized_name":"mask","type":"MASK","shape":7,"link":2091},{"name":"weights","localized_name":"weights","type":"SIGMAS","shape":7,"link":null},{"name":"region_bleeds","localized_name":"region_bleeds","type":"SIGMAS","shape":7,"link":null}],"outputs":[{"name":"conditioning","localized_name":"conditioning","type":"CONDITIONING","links":[2095],"slot_index":0}],"properties":{"Node name for S&R":"ClownRegionalConditioning2"},"widgets_values":[1,0,0,"constant",0,-1,"boolean_masked",32,false]},{"id":703,"type":"Note","pos":[423.10699462890625,-96.14085388183594],"size":[241.9689483642578,386.7543640136719],"flags":{},"order":6,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["edge_width also creates some overlap around the edges of the mask.\n\nboolean_masked means that the masked area can \"see\" the rest of the image, but the unmasked area cannot. \"boolean\" would mean neither area could see the rest of the image.\n\nTry setting to boolean_unmasked and see what happens!\n\nIf you still have blur, try reducing edge_width (and if you have seams, try increasing it, or setting end_step to something like 20). \n\nAlso verify that you can generate the background prompt alone without blur (if you can't, this won't work). And don't get stuck on one seed.\n\nVaguely human-shaped masks also tend to work better than the blocky one used here."],"color":"#432","bgcolor":"#653"}],"links":[[18,14,0,7,4,"VAE"],[1328,14,0,397,1,"VAE"],[1329,397,0,398,0,"IMAGE"],[1399,7,3,401,3,"LATENT"],[1939,490,0,662,0,"CLIP"],[1963,663,0,664,0,"MODEL"],[1964,664,0,13,0,"*"],[1965,663,1,490,0,"*"],[1966,663,2,14,0,"*"],[1967,13,0,401,0,"MODEL"],[2054,709,0,710,0,"MASK"],[2065,716,0,709,1,"MASK"],[2073,715,0,676,0,"MASK"],[2083,676,0,709,0,"MASK"],[2091,709,0,722,2,"MASK"],[2092,490,0,723,0,"CLIP"],[2093,723,0,722,1,"CONDITIONING"],[2094,662,0,722,0,"CONDITIONING"],[2095,722,0,401,1,"CONDITIONING"],[2096,401,0,397,0,"LATENT"]],"groups":[],"config":{},"extra":{"ds":{"scale":1.91943424957756,"offset":[1680.6010824178522,841.7668875984083]},"VHS_latentpreview":false,"VHS_latentpreviewrate":0,"ue_links":[],"VHS_MetadataImage":true,"VHS_KeepIntermediate":true},"version":0.4} \ No newline at end of file diff --git a/ComfyUI/custom_nodes/RES4LYF/example_workflows/flux regional redux (2 zone).json b/ComfyUI/custom_nodes/RES4LYF/example_workflows/flux regional redux (2 zone).json new file mode 100644 index 0000000000000000000000000000000000000000..73d7ab9d72f300e700c38695992ce7edcab2d66e --- /dev/null +++ b/ComfyUI/custom_nodes/RES4LYF/example_workflows/flux regional redux (2 zone).json @@ -0,0 +1 @@ +{"last_node_id":704,"last_link_id":2042,"nodes":[{"id":13,"type":"Reroute","pos":[1300,-790],"size":[75,26],"flags":{},"order":16,"mode":0,"inputs":[{"name":"","type":"*","link":1964}],"outputs":[{"name":"","type":"MODEL","links":[1967],"slot_index":0}],"properties":{"showOutputText":false,"horizontal":false}},{"id":490,"type":"Reroute","pos":[1300,-750],"size":[75,26],"flags":{},"order":11,"mode":0,"inputs":[{"name":"","type":"*","link":1965}],"outputs":[{"name":"","type":"CLIP","links":[1706,1939],"slot_index":0}],"properties":{"showOutputText":false,"horizontal":false}},{"id":541,"type":"CLIPTextEncode","pos":[692.1508178710938,183.7528839111328],"size":[265.775390625,113.01970672607422],"flags":{},"order":17,"mode":0,"inputs":[{"name":"clip","localized_name":"clip","type":"CLIP","link":1706}],"outputs":[{"name":"CONDITIONING","localized_name":"CONDITIONING","type":"CONDITIONING","links":[1732],"slot_index":0}],"properties":{"Node name for S&R":"CLIPTextEncode","cnr_id":"comfy-core","ver":"0.3.29"},"widgets_values":["blurry, out of focus, shallow depth of field, low quality, bad quality, low detail, mutated, jpeg artifacts, compression artifacts,"]},{"id":14,"type":"Reroute","pos":[1300,-710],"size":[75,26],"flags":{},"order":12,"mode":0,"inputs":[{"name":"","type":"*","link":1966}],"outputs":[{"name":"","type":"VAE","links":[18,1328],"slot_index":0}],"properties":{"showOutputText":false,"horizontal":false}},{"id":397,"type":"VAEDecode","pos":[1403.6392822265625,-371.9699401855469],"size":[210,46],"flags":{},"order":31,"mode":0,"inputs":[{"name":"samples","localized_name":"samples","type":"LATENT","link":1988},{"name":"vae","localized_name":"vae","type":"VAE","link":1328}],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":[1329],"slot_index":0}],"properties":{"Node name for S&R":"VAEDecode","cnr_id":"comfy-core","ver":"0.3.29"},"widgets_values":[]},{"id":680,"type":"Reroute","pos":[1310,-660],"size":[75,26],"flags":{},"order":13,"mode":0,"inputs":[{"name":"","type":"*","link":2001}],"outputs":[{"name":"","type":"CLIP_VISION","links":[2004,2009]}],"properties":{"showOutputText":false,"horizontal":false}},{"id":678,"type":"StyleModelApply","pos":[101.3630142211914,-560.2020874023438],"size":[262,122],"flags":{},"order":24,"mode":0,"inputs":[{"name":"conditioning","localized_name":"conditioning","type":"CONDITIONING","link":2005},{"name":"style_model","localized_name":"style_model","type":"STYLE_MODEL","link":1999},{"name":"clip_vision_output","localized_name":"clip_vision_output","type":"CLIP_VISION_OUTPUT","link":2003}],"outputs":[{"name":"CONDITIONING","localized_name":"CONDITIONING","type":"CONDITIONING","links":[2002],"slot_index":0}],"properties":{"Node name for S&R":"StyleModelApply"},"widgets_values":[1,"multiply"]},{"id":683,"type":"CLIPVisionEncode","pos":[-170,-220],"size":[253.60000610351562,78],"flags":{},"order":21,"mode":0,"inputs":[{"name":"clip_vision","localized_name":"clip_vision","type":"CLIP_VISION","link":2009},{"name":"image","localized_name":"image","type":"IMAGE","link":2035}],"outputs":[{"name":"CLIP_VISION_OUTPUT","localized_name":"CLIP_VISION_OUTPUT","type":"CLIP_VISION_OUTPUT","links":[2008]}],"properties":{"Node name for S&R":"CLIPVisionEncode"},"widgets_values":["center"]},{"id":682,"type":"StyleModelApply","pos":[100,-250],"size":[262,122],"flags":{},"order":25,"mode":0,"inputs":[{"name":"conditioning","localized_name":"conditioning","type":"CONDITIONING","link":2006},{"name":"style_model","localized_name":"style_model","type":"STYLE_MODEL","link":2007},{"name":"clip_vision_output","localized_name":"clip_vision_output","type":"CLIP_VISION_OUTPUT","link":2008}],"outputs":[{"name":"CONDITIONING","localized_name":"CONDITIONING","type":"CONDITIONING","links":[2020],"slot_index":0}],"properties":{"Node name for S&R":"StyleModelApply"},"widgets_values":[1,"multiply"]},{"id":681,"type":"CLIPVisionEncode","pos":[-173.92124938964844,-524.1537475585938],"size":[253.60000610351562,78],"flags":{},"order":20,"mode":0,"inputs":[{"name":"clip_vision","localized_name":"clip_vision","type":"CLIP_VISION","link":2004},{"name":"image","localized_name":"image","type":"IMAGE","link":2028}],"outputs":[{"name":"CLIP_VISION_OUTPUT","localized_name":"CLIP_VISION_OUTPUT","type":"CLIP_VISION_OUTPUT","links":[2003]}],"properties":{"Node name for S&R":"CLIPVisionEncode"},"widgets_values":["center"]},{"id":694,"type":"LoadImage","pos":[-536.0714111328125,-640.6544189453125],"size":[315,314],"flags":{},"order":0,"mode":0,"inputs":[],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":[2028],"slot_index":0},{"name":"MASK","localized_name":"MASK","type":"MASK","links":null}],"properties":{"Node name for S&R":"LoadImage"},"widgets_values":["ChatGPT Image Apr 29, 2025, 07_47_12 PM.png","image"]},{"id":7,"type":"VAEEncodeAdvanced","pos":[696.7778930664062,-164.97328186035156],"size":[261.2217712402344,279.3136901855469],"flags":{},"order":19,"mode":0,"inputs":[{"name":"image_1","localized_name":"image_1","type":"IMAGE","shape":7,"link":null},{"name":"image_2","localized_name":"image_2","type":"IMAGE","shape":7,"link":null},{"name":"mask","localized_name":"mask","type":"IMAGE","shape":7,"link":null},{"name":"latent","localized_name":"latent","type":"LATENT","shape":7,"link":null},{"name":"vae","localized_name":"vae","type":"VAE","shape":7,"link":18}],"outputs":[{"name":"latent_1","localized_name":"latent_1","type":"LATENT","links":[],"slot_index":0},{"name":"latent_2","localized_name":"latent_2","type":"LATENT","links":[],"slot_index":1},{"name":"mask","localized_name":"mask","type":"MASK","links":[],"slot_index":2},{"name":"empty_latent","localized_name":"empty_latent","type":"LATENT","links":[1399],"slot_index":3},{"name":"width","localized_name":"width","type":"INT","links":null},{"name":"height","localized_name":"height","type":"INT","links":null}],"properties":{"Node name for S&R":"VAEEncodeAdvanced","cnr_id":"RES4LYF","ver":"5ce9b5a77c227bf864e447a1e65305bf6cada5c2"},"widgets_values":["false",768,1344,"red",false,"16_channels"]},{"id":596,"type":"ClownRegionalConditioning","pos":[425.9762268066406,-243.12513732910156],"size":[211.60000610351562,122],"flags":{},"order":27,"mode":0,"inputs":[{"name":"cond_regions","localized_name":"cond_regions","type":"COND_REGIONS","shape":7,"link":null},{"name":"conditioning","localized_name":"conditioning","type":"CONDITIONING","shape":7,"link":2020},{"name":"mask","localized_name":"mask","type":"MASK","shape":7,"link":2042}],"outputs":[{"name":"cond_regions","localized_name":"cond_regions","type":"COND_REGIONS","links":[1937],"slot_index":0}],"properties":{"Node name for S&R":"ClownRegionalConditioning"},"widgets_values":[false,256]},{"id":401,"type":"ClownsharKSampler_Beta","pos":[1010,-370],"size":[340.55120849609375,666.8208618164062],"flags":{},"order":30,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","shape":7,"link":1967},{"name":"positive","localized_name":"positive","type":"CONDITIONING","shape":7,"link":1735},{"name":"negative","localized_name":"negative","type":"CONDITIONING","shape":7,"link":1732},{"name":"latent_image","localized_name":"latent_image","type":"LATENT","shape":7,"link":1399},{"name":"sigmas","localized_name":"sigmas","type":"SIGMAS","shape":7,"link":null},{"name":"guides","localized_name":"guides","type":"GUIDES","shape":7,"link":null},{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":null}],"outputs":[{"name":"output","localized_name":"output","type":"LATENT","links":[1988],"slot_index":0},{"name":"denoised","localized_name":"denoised","type":"LATENT","links":null},{"name":"options","localized_name":"options","type":"OPTIONS","links":null}],"properties":{"Node name for S&R":"ClownsharKSampler_Beta","cnr_id":"RES4LYF","ver":"5ce9b5a77c227bf864e447a1e65305bf6cada5c2"},"widgets_values":[0.5,"exponential/res_2s","bong_tangent",20,-1,1,1,109,"fixed","standard",true]},{"id":560,"type":"ClownRegionalConditionings","pos":[676.1644897460938,-499.31219482421875],"size":[278.4758605957031,266],"flags":{},"order":29,"mode":0,"inputs":[{"name":"cond_regions","localized_name":"cond_regions","type":"COND_REGIONS","shape":7,"link":1938},{"name":"weights","localized_name":"weights","type":"SIGMAS","shape":7,"link":null},{"name":"region_bleeds","localized_name":"region_bleeds","type":"SIGMAS","shape":7,"link":null}],"outputs":[{"name":"conditioning","localized_name":"conditioning","type":"CONDITIONING","links":[1735],"slot_index":0}],"properties":{"Node name for S&R":"ClownRegionalConditionings"},"widgets_values":[0.5,1,14,"beta57",0,20,"boolean",false]},{"id":690,"type":"LoadImage","pos":[-531.4011840820312,-234.04151916503906],"size":[315,314],"flags":{},"order":1,"mode":0,"inputs":[],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":[2035],"slot_index":0},{"name":"MASK","localized_name":"MASK","type":"MASK","links":null}],"properties":{"Node name for S&R":"LoadImage"},"widgets_values":["ComfyUI_00452_.png","image"]},{"id":676,"type":"InvertMask","pos":[-1270,-450],"size":[140,26],"flags":{},"order":9,"mode":0,"inputs":[{"name":"mask","localized_name":"mask","type":"MASK","link":1990}],"outputs":[{"name":"MASK","localized_name":"MASK","type":"MASK","links":[1991],"slot_index":0}],"properties":{"Node name for S&R":"InvertMask"},"widgets_values":[]},{"id":666,"type":"SolidMask","pos":[-1500,-450],"size":[210,106],"flags":{},"order":2,"mode":0,"inputs":[],"outputs":[{"name":"MASK","localized_name":"MASK","type":"MASK","links":[1990],"slot_index":0}],"properties":{"Node name for S&R":"SolidMask"},"widgets_values":[1,1344,768]},{"id":667,"type":"MaskPreview","pos":[-840,-570],"size":[210,246],"flags":{},"order":22,"mode":0,"inputs":[{"name":"mask","localized_name":"mask","type":"MASK","link":1969}],"outputs":[],"properties":{"Node name for S&R":"MaskPreview"},"widgets_values":[]},{"id":670,"type":"MaskPreview","pos":[-840,-280],"size":[210,246],"flags":{},"order":26,"mode":0,"inputs":[{"name":"mask","localized_name":"mask","type":"MASK","link":2041}],"outputs":[],"properties":{"Node name for S&R":"MaskPreview"},"widgets_values":[]},{"id":661,"type":"ClownRegionalConditioning","pos":[411.9298095703125,-539.053955078125],"size":[211.60000610351562,122],"flags":{},"order":28,"mode":0,"inputs":[{"name":"cond_regions","localized_name":"cond_regions","type":"COND_REGIONS","shape":7,"link":1937},{"name":"conditioning","localized_name":"conditioning","type":"CONDITIONING","shape":7,"link":2002},{"name":"mask","localized_name":"mask","type":"MASK","shape":7,"link":2036}],"outputs":[{"name":"cond_regions","localized_name":"cond_regions","type":"COND_REGIONS","links":[1938],"slot_index":0}],"properties":{"Node name for S&R":"ClownRegionalConditioning"},"widgets_values":[false,256]},{"id":665,"type":"MaskComposite","pos":[-1100,-450],"size":[210,126],"flags":{},"order":15,"mode":0,"inputs":[{"name":"destination","localized_name":"destination","type":"MASK","link":1991},{"name":"source","localized_name":"source","type":"MASK","link":1995}],"outputs":[{"name":"MASK","localized_name":"MASK","type":"MASK","links":[1969,2036,2038],"slot_index":0}],"properties":{"Node name for S&R":"MaskComposite"},"widgets_values":[0,0,"add"]},{"id":700,"type":"MaskFlip+","pos":[-1098.6136474609375,-267.628173828125],"size":[210,58],"flags":{},"order":23,"mode":0,"inputs":[{"name":"mask","localized_name":"mask","type":"MASK","link":2038}],"outputs":[{"name":"MASK","localized_name":"MASK","type":"MASK","links":[2041,2042],"slot_index":0}],"properties":{"Node name for S&R":"MaskFlip+"},"widgets_values":["x"]},{"id":668,"type":"SolidMask","pos":[-1502.6644287109375,-289.3330993652344],"size":[210,106],"flags":{},"order":3,"mode":0,"inputs":[],"outputs":[{"name":"MASK","localized_name":"MASK","type":"MASK","links":[1995],"slot_index":0}],"properties":{"Node name for S&R":"SolidMask"},"widgets_values":[1,768,768]},{"id":701,"type":"Note","pos":[-1378.6959228515625,-637.0702514648438],"size":[342.05950927734375,88],"flags":{},"order":4,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["I usually just lazily draw masks in Load Image nodes (with some random image loaded), but for the sake of reproducibility, here's another approach."],"color":"#432","bgcolor":"#653"},{"id":663,"type":"FluxLoader","pos":[654.6221923828125,-858.3792724609375],"size":[374.41741943359375,282],"flags":{},"order":5,"mode":0,"inputs":[],"outputs":[{"name":"model","localized_name":"model","type":"MODEL","links":[1963],"slot_index":0},{"name":"clip","localized_name":"clip","type":"CLIP","links":[1965],"slot_index":1},{"name":"vae","localized_name":"vae","type":"VAE","links":[1966],"slot_index":2},{"name":"clip_vision","localized_name":"clip_vision","type":"CLIP_VISION","links":[2001],"slot_index":3},{"name":"style_model","localized_name":"style_model","type":"STYLE_MODEL","links":[2000],"slot_index":4}],"properties":{"Node name for S&R":"FluxLoader"},"widgets_values":["colossusProjectFlux_v42AIO.safetensors","fp8_e4m3fn_fast",".use_ckpt_clip",".none",".use_ckpt_vae","sigclip_vision_patch14_384.safetensors","flux1-redux-dev.safetensors"]},{"id":664,"type":"ReFluxPatcher","pos":[1064.7325439453125,-863.0516967773438],"size":[210,82],"flags":{},"order":10,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","link":1963}],"outputs":[{"name":"model","localized_name":"model","type":"MODEL","links":[1964],"slot_index":0}],"properties":{"Node name for S&R":"ReFluxPatcher"},"widgets_values":["float64",true]},{"id":679,"type":"Reroute","pos":[1300,-610],"size":[75,26],"flags":{},"order":14,"mode":0,"inputs":[{"name":"","type":"*","link":2000}],"outputs":[{"name":"","type":"STYLE_MODEL","links":[1999,2007]}],"properties":{"showOutputText":false,"horizontal":false}},{"id":662,"type":"CLIPTextEncode","pos":[-140.3179168701172,-670.337158203125],"size":[210,88],"flags":{"collapsed":false},"order":18,"mode":0,"inputs":[{"name":"clip","localized_name":"clip","type":"CLIP","link":1939}],"outputs":[{"name":"CONDITIONING","localized_name":"CONDITIONING","type":"CONDITIONING","links":[2005,2006],"slot_index":0}],"properties":{"Node name for S&R":"CLIPTextEncode","cnr_id":"comfy-core","ver":"0.3.29"},"widgets_values":[""]},{"id":702,"type":"Note","pos":[-1222.3177490234375,-134.59034729003906],"size":[278.04071044921875,88],"flags":{},"order":6,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["Note that these masks are overlapping."],"color":"#432","bgcolor":"#653"},{"id":703,"type":"Note","pos":[358.4803466796875,-41.564422607421875],"size":[278.04071044921875,88],"flags":{},"order":7,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["edge_width also creates some overlap around the edges of the mask."],"color":"#432","bgcolor":"#653"},{"id":704,"type":"Note","pos":[324.8023986816406,-781.4505004882812],"size":[290.7107238769531,155.35317993164062],"flags":{},"order":8,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["ClownRegionalConditionings:\n\nTry raising or lowering weight, and changing the weight scheduler from beta57 to Karras (weakens more quickly), or to linear quadratic (stronger late).\n\nTry changing region_bleed_start_step, and end_step."],"color":"#432","bgcolor":"#653"},{"id":398,"type":"SaveImage","pos":[1379.9996337890625,-267.2835998535156],"size":[341.7508850097656,561.0067749023438],"flags":{},"order":32,"mode":0,"inputs":[{"name":"images","localized_name":"images","type":"IMAGE","link":1329}],"outputs":[],"properties":{"Node name for S&R":"SaveImage","cnr_id":"comfy-core","ver":"0.3.29"},"widgets_values":["ComfyUI"]}],"links":[[18,14,0,7,4,"VAE"],[1328,14,0,397,1,"VAE"],[1329,397,0,398,0,"IMAGE"],[1399,7,3,401,3,"LATENT"],[1706,490,0,541,0,"CLIP"],[1732,541,0,401,2,"CONDITIONING"],[1735,560,0,401,1,"CONDITIONING"],[1937,596,0,661,0,"COND_REGIONS"],[1938,661,0,560,0,"COND_REGIONS"],[1939,490,0,662,0,"CLIP"],[1963,663,0,664,0,"MODEL"],[1964,664,0,13,0,"*"],[1965,663,1,490,0,"*"],[1966,663,2,14,0,"*"],[1967,13,0,401,0,"MODEL"],[1969,665,0,667,0,"MASK"],[1988,401,0,397,0,"LATENT"],[1990,666,0,676,0,"MASK"],[1991,676,0,665,0,"MASK"],[1995,668,0,665,1,"MASK"],[1999,679,0,678,1,"STYLE_MODEL"],[2000,663,4,679,0,"*"],[2001,663,3,680,0,"*"],[2002,678,0,661,1,"CONDITIONING"],[2003,681,0,678,2,"CLIP_VISION_OUTPUT"],[2004,680,0,681,0,"CLIP_VISION"],[2005,662,0,678,0,"CONDITIONING"],[2006,662,0,682,0,"CONDITIONING"],[2007,679,0,682,1,"STYLE_MODEL"],[2008,683,0,682,2,"CLIP_VISION_OUTPUT"],[2009,680,0,683,0,"CLIP_VISION"],[2020,682,0,596,1,"CONDITIONING"],[2028,694,0,681,1,"IMAGE"],[2035,690,0,683,1,"IMAGE"],[2036,665,0,661,2,"MASK"],[2038,665,0,700,0,"MASK"],[2041,700,0,670,0,"MASK"],[2042,700,0,596,2,"MASK"]],"groups":[],"config":{},"extra":{"ds":{"scale":1.7449402268886907,"offset":[2753.5015634091214,978.5823037629943]},"VHS_latentpreview":false,"VHS_latentpreviewrate":0,"ue_links":[],"VHS_MetadataImage":true,"VHS_KeepIntermediate":true},"version":0.4} \ No newline at end of file diff --git a/ComfyUI/custom_nodes/RES4LYF/example_workflows/flux regional redux (3 zone, overlapping).json b/ComfyUI/custom_nodes/RES4LYF/example_workflows/flux regional redux (3 zone, overlapping).json new file mode 100644 index 0000000000000000000000000000000000000000..e6466f7bfbede0966cff3c2f8c451e0b3e0a3566 --- /dev/null +++ b/ComfyUI/custom_nodes/RES4LYF/example_workflows/flux regional redux (3 zone, overlapping).json @@ -0,0 +1 @@ +{"last_node_id":715,"last_link_id":2063,"nodes":[{"id":13,"type":"Reroute","pos":[1300,-790],"size":[75,26],"flags":{},"order":17,"mode":0,"inputs":[{"name":"","type":"*","link":1964}],"outputs":[{"name":"","type":"MODEL","links":[1967],"slot_index":0}],"properties":{"showOutputText":false,"horizontal":false}},{"id":490,"type":"Reroute","pos":[1300,-750],"size":[75,26],"flags":{},"order":12,"mode":0,"inputs":[{"name":"","type":"*","link":1965}],"outputs":[{"name":"","type":"CLIP","links":[1706,1939],"slot_index":0}],"properties":{"showOutputText":false,"horizontal":false}},{"id":541,"type":"CLIPTextEncode","pos":[692.1508178710938,183.7528839111328],"size":[265.775390625,113.01970672607422],"flags":{},"order":18,"mode":0,"inputs":[{"name":"clip","localized_name":"clip","type":"CLIP","link":1706}],"outputs":[{"name":"CONDITIONING","localized_name":"CONDITIONING","type":"CONDITIONING","links":[1732],"slot_index":0}],"properties":{"Node name for S&R":"CLIPTextEncode","cnr_id":"comfy-core","ver":"0.3.29"},"widgets_values":["blurry, out of focus, shallow depth of field, low quality, bad quality, low detail, mutated, jpeg artifacts, compression artifacts,"]},{"id":14,"type":"Reroute","pos":[1300,-710],"size":[75,26],"flags":{},"order":13,"mode":0,"inputs":[{"name":"","type":"*","link":1966}],"outputs":[{"name":"","type":"VAE","links":[18,1328],"slot_index":0}],"properties":{"showOutputText":false,"horizontal":false}},{"id":680,"type":"Reroute","pos":[1310,-660],"size":[75,26],"flags":{},"order":14,"mode":0,"inputs":[{"name":"","type":"*","link":2001}],"outputs":[{"name":"","type":"CLIP_VISION","links":[2004,2009,2043]}],"properties":{"showOutputText":false,"horizontal":false}},{"id":678,"type":"StyleModelApply","pos":[101.3630142211914,-560.2020874023438],"size":[262,122],"flags":{},"order":26,"mode":0,"inputs":[{"name":"conditioning","localized_name":"conditioning","type":"CONDITIONING","link":2005},{"name":"style_model","localized_name":"style_model","type":"STYLE_MODEL","link":1999},{"name":"clip_vision_output","localized_name":"clip_vision_output","type":"CLIP_VISION_OUTPUT","link":2003}],"outputs":[{"name":"CONDITIONING","localized_name":"CONDITIONING","type":"CONDITIONING","links":[2002],"slot_index":0}],"properties":{"Node name for S&R":"StyleModelApply"},"widgets_values":[1,"multiply"]},{"id":681,"type":"CLIPVisionEncode","pos":[-173.92124938964844,-524.1537475585938],"size":[253.60000610351562,78],"flags":{},"order":21,"mode":0,"inputs":[{"name":"clip_vision","localized_name":"clip_vision","type":"CLIP_VISION","link":2004},{"name":"image","localized_name":"image","type":"IMAGE","link":2028}],"outputs":[{"name":"CLIP_VISION_OUTPUT","localized_name":"CLIP_VISION_OUTPUT","type":"CLIP_VISION_OUTPUT","links":[2003]}],"properties":{"Node name for S&R":"CLIPVisionEncode"},"widgets_values":["center"]},{"id":676,"type":"InvertMask","pos":[-1270,-450],"size":[140,26],"flags":{},"order":16,"mode":0,"inputs":[{"name":"mask","localized_name":"mask","type":"MASK","link":1990}],"outputs":[{"name":"MASK","localized_name":"MASK","type":"MASK","links":[1991,2051],"slot_index":0}],"properties":{"Node name for S&R":"InvertMask"},"widgets_values":[]},{"id":667,"type":"MaskPreview","pos":[-840,-570],"size":[210,246],"flags":{},"order":29,"mode":0,"inputs":[{"name":"mask","localized_name":"mask","type":"MASK","link":1969}],"outputs":[],"properties":{"Node name for S&R":"MaskPreview"},"widgets_values":[]},{"id":661,"type":"ClownRegionalConditioning","pos":[411.9298095703125,-539.053955078125],"size":[211.60000610351562,122],"flags":{},"order":35,"mode":0,"inputs":[{"name":"cond_regions","localized_name":"cond_regions","type":"COND_REGIONS","shape":7,"link":1937},{"name":"conditioning","localized_name":"conditioning","type":"CONDITIONING","shape":7,"link":2002},{"name":"mask","localized_name":"mask","type":"MASK","shape":7,"link":2036}],"outputs":[{"name":"cond_regions","localized_name":"cond_regions","type":"COND_REGIONS","links":[1938],"slot_index":0}],"properties":{"Node name for S&R":"ClownRegionalConditioning"},"widgets_values":[false,256]},{"id":701,"type":"Note","pos":[-1378.6959228515625,-637.0702514648438],"size":[342.05950927734375,88],"flags":{},"order":0,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["I usually just lazily draw masks in Load Image nodes (with some random image loaded), but for the sake of reproducibility, here's another approach."],"color":"#432","bgcolor":"#653"},{"id":663,"type":"FluxLoader","pos":[654.6221923828125,-858.3792724609375],"size":[374.41741943359375,282],"flags":{},"order":1,"mode":0,"inputs":[],"outputs":[{"name":"model","localized_name":"model","type":"MODEL","links":[1963],"slot_index":0},{"name":"clip","localized_name":"clip","type":"CLIP","links":[1965],"slot_index":1},{"name":"vae","localized_name":"vae","type":"VAE","links":[1966],"slot_index":2},{"name":"clip_vision","localized_name":"clip_vision","type":"CLIP_VISION","links":[2001],"slot_index":3},{"name":"style_model","localized_name":"style_model","type":"STYLE_MODEL","links":[2000],"slot_index":4}],"properties":{"Node name for S&R":"FluxLoader"},"widgets_values":["colossusProjectFlux_v42AIO.safetensors","fp8_e4m3fn_fast",".use_ckpt_clip",".none",".use_ckpt_vae","sigclip_vision_patch14_384.safetensors","flux1-redux-dev.safetensors"]},{"id":664,"type":"ReFluxPatcher","pos":[1064.7325439453125,-863.0516967773438],"size":[210,82],"flags":{},"order":11,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","link":1963}],"outputs":[{"name":"model","localized_name":"model","type":"MODEL","links":[1964],"slot_index":0}],"properties":{"Node name for S&R":"ReFluxPatcher"},"widgets_values":["float64",true]},{"id":679,"type":"Reroute","pos":[1300,-610],"size":[75,26],"flags":{},"order":15,"mode":0,"inputs":[{"name":"","type":"*","link":2000}],"outputs":[{"name":"","type":"STYLE_MODEL","links":[1999,2007,2046]}],"properties":{"showOutputText":false,"horizontal":false}},{"id":662,"type":"CLIPTextEncode","pos":[-140.3179168701172,-670.337158203125],"size":[210,88],"flags":{"collapsed":false},"order":19,"mode":0,"inputs":[{"name":"clip","localized_name":"clip","type":"CLIP","link":1939}],"outputs":[{"name":"CONDITIONING","localized_name":"CONDITIONING","type":"CONDITIONING","links":[2005,2006,2045],"slot_index":0}],"properties":{"Node name for S&R":"CLIPTextEncode","cnr_id":"comfy-core","ver":"0.3.29"},"widgets_values":[""]},{"id":398,"type":"SaveImage","pos":[1379.9996337890625,-267.2835998535156],"size":[341.7508850097656,561.0067749023438],"flags":{},"order":39,"mode":0,"inputs":[{"name":"images","localized_name":"images","type":"IMAGE","link":1329}],"outputs":[],"properties":{"Node name for S&R":"SaveImage","cnr_id":"comfy-core","ver":"0.3.29"},"widgets_values":["ComfyUI"]},{"id":683,"type":"CLIPVisionEncode","pos":[-170,-220],"size":[253.60000610351562,78],"flags":{},"order":22,"mode":0,"inputs":[{"name":"clip_vision","localized_name":"clip_vision","type":"CLIP_VISION","link":2009},{"name":"image","localized_name":"image","type":"IMAGE","link":2062}],"outputs":[{"name":"CLIP_VISION_OUTPUT","localized_name":"CLIP_VISION_OUTPUT","type":"CLIP_VISION_OUTPUT","links":[2008]}],"properties":{"Node name for S&R":"CLIPVisionEncode"},"widgets_values":["center"]},{"id":682,"type":"StyleModelApply","pos":[100,-250],"size":[262,122],"flags":{},"order":27,"mode":0,"inputs":[{"name":"conditioning","localized_name":"conditioning","type":"CONDITIONING","link":2006},{"name":"style_model","localized_name":"style_model","type":"STYLE_MODEL","link":2007},{"name":"clip_vision_output","localized_name":"clip_vision_output","type":"CLIP_VISION_OUTPUT","link":2008}],"outputs":[{"name":"CONDITIONING","localized_name":"CONDITIONING","type":"CONDITIONING","links":[2020],"slot_index":0}],"properties":{"Node name for S&R":"StyleModelApply"},"widgets_values":[1,"multiply"]},{"id":596,"type":"ClownRegionalConditioning","pos":[425.9762268066406,-243.12513732910156],"size":[211.60000610351562,122],"flags":{},"order":34,"mode":0,"inputs":[{"name":"cond_regions","localized_name":"cond_regions","type":"COND_REGIONS","shape":7,"link":2050},{"name":"conditioning","localized_name":"conditioning","type":"CONDITIONING","shape":7,"link":2020},{"name":"mask","localized_name":"mask","type":"MASK","shape":7,"link":2042}],"outputs":[{"name":"cond_regions","localized_name":"cond_regions","type":"COND_REGIONS","links":[1937],"slot_index":0}],"properties":{"Node name for S&R":"ClownRegionalConditioning"},"widgets_values":[false,256]},{"id":706,"type":"CLIPVisionEncode","pos":[-180,180],"size":[253.60000610351562,78],"flags":{},"order":23,"mode":0,"inputs":[{"name":"clip_vision","localized_name":"clip_vision","type":"CLIP_VISION","link":2043},{"name":"image","localized_name":"image","type":"IMAGE","link":2061}],"outputs":[{"name":"CLIP_VISION_OUTPUT","localized_name":"CLIP_VISION_OUTPUT","type":"CLIP_VISION_OUTPUT","links":[2047]}],"properties":{"Node name for S&R":"CLIPVisionEncode"},"widgets_values":["center"]},{"id":707,"type":"StyleModelApply","pos":[90,150],"size":[262,122],"flags":{},"order":28,"mode":0,"inputs":[{"name":"conditioning","localized_name":"conditioning","type":"CONDITIONING","link":2045},{"name":"style_model","localized_name":"style_model","type":"STYLE_MODEL","link":2046},{"name":"clip_vision_output","localized_name":"clip_vision_output","type":"CLIP_VISION_OUTPUT","link":2047}],"outputs":[{"name":"CONDITIONING","localized_name":"CONDITIONING","type":"CONDITIONING","links":[2048],"slot_index":0}],"properties":{"Node name for S&R":"StyleModelApply"},"widgets_values":[1,"multiply"]},{"id":708,"type":"ClownRegionalConditioning","pos":[420,160],"size":[211.60000610351562,122],"flags":{},"order":32,"mode":0,"inputs":[{"name":"cond_regions","localized_name":"cond_regions","type":"COND_REGIONS","shape":7,"link":null},{"name":"conditioning","localized_name":"conditioning","type":"CONDITIONING","shape":7,"link":2048},{"name":"mask","localized_name":"mask","type":"MASK","shape":7,"link":2057}],"outputs":[{"name":"cond_regions","localized_name":"cond_regions","type":"COND_REGIONS","links":[2050],"slot_index":0}],"properties":{"Node name for S&R":"ClownRegionalConditioning"},"widgets_values":[false,256]},{"id":665,"type":"MaskComposite","pos":[-1100,-450],"size":[210,126],"flags":{},"order":24,"mode":0,"inputs":[{"name":"destination","localized_name":"destination","type":"MASK","link":1991},{"name":"source","localized_name":"source","type":"MASK","link":1995}],"outputs":[{"name":"MASK","localized_name":"MASK","type":"MASK","links":[1969,2036,2038],"slot_index":0}],"properties":{"Node name for S&R":"MaskComposite"},"widgets_values":[0,0,"add"]},{"id":670,"type":"MaskPreview","pos":[-840.8076782226562,-235.62042236328125],"size":[210,246],"flags":{},"order":33,"mode":0,"inputs":[{"name":"mask","localized_name":"mask","type":"MASK","link":2041}],"outputs":[],"properties":{"Node name for S&R":"MaskPreview"},"widgets_values":[]},{"id":700,"type":"MaskFlip+","pos":[-1099.420166015625,-236.15890502929688],"size":[210,58],"flags":{},"order":30,"mode":0,"inputs":[{"name":"mask","localized_name":"mask","type":"MASK","link":2038}],"outputs":[{"name":"MASK","localized_name":"MASK","type":"MASK","links":[2041,2042],"slot_index":0}],"properties":{"Node name for S&R":"MaskFlip+"},"widgets_values":["x"]},{"id":710,"type":"MaskPreview","pos":[-847.5751953125,166.58413696289062],"size":[210,246],"flags":{},"order":31,"mode":0,"inputs":[{"name":"mask","localized_name":"mask","type":"MASK","link":2054}],"outputs":[],"properties":{"Node name for S&R":"MaskPreview"},"widgets_values":[]},{"id":397,"type":"VAEDecode","pos":[1403.6392822265625,-371.9699401855469],"size":[210,46],"flags":{},"order":38,"mode":0,"inputs":[{"name":"samples","localized_name":"samples","type":"LATENT","link":2056},{"name":"vae","localized_name":"vae","type":"VAE","link":1328}],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":[1329],"slot_index":0}],"properties":{"Node name for S&R":"VAEDecode","cnr_id":"comfy-core","ver":"0.3.29"},"widgets_values":[]},{"id":401,"type":"ClownsharKSampler_Beta","pos":[1010,-370],"size":[340.55120849609375,666.8208618164062],"flags":{},"order":37,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","shape":7,"link":1967},{"name":"positive","localized_name":"positive","type":"CONDITIONING","shape":7,"link":1735},{"name":"negative","localized_name":"negative","type":"CONDITIONING","shape":7,"link":1732},{"name":"latent_image","localized_name":"latent_image","type":"LATENT","shape":7,"link":1399},{"name":"sigmas","localized_name":"sigmas","type":"SIGMAS","shape":7,"link":null},{"name":"guides","localized_name":"guides","type":"GUIDES","shape":7,"link":null},{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":null}],"outputs":[{"name":"output","localized_name":"output","type":"LATENT","links":[2056],"slot_index":0},{"name":"denoised","localized_name":"denoised","type":"LATENT","links":null},{"name":"options","localized_name":"options","type":"OPTIONS","links":null}],"properties":{"Node name for S&R":"ClownsharKSampler_Beta","cnr_id":"RES4LYF","ver":"5ce9b5a77c227bf864e447a1e65305bf6cada5c2"},"widgets_values":[0.5,"exponential/res_2s","bong_tangent",20,-1,1,1,109,"fixed","standard",true]},{"id":7,"type":"VAEEncodeAdvanced","pos":[696.7778930664062,-164.97328186035156],"size":[261.2217712402344,279.3136901855469],"flags":{},"order":20,"mode":0,"inputs":[{"name":"image_1","localized_name":"image_1","type":"IMAGE","shape":7,"link":null},{"name":"image_2","localized_name":"image_2","type":"IMAGE","shape":7,"link":null},{"name":"mask","localized_name":"mask","type":"IMAGE","shape":7,"link":null},{"name":"latent","localized_name":"latent","type":"LATENT","shape":7,"link":null},{"name":"vae","localized_name":"vae","type":"VAE","shape":7,"link":18}],"outputs":[{"name":"latent_1","localized_name":"latent_1","type":"LATENT","links":[],"slot_index":0},{"name":"latent_2","localized_name":"latent_2","type":"LATENT","links":[],"slot_index":1},{"name":"mask","localized_name":"mask","type":"MASK","links":[],"slot_index":2},{"name":"empty_latent","localized_name":"empty_latent","type":"LATENT","links":[1399],"slot_index":3},{"name":"width","localized_name":"width","type":"INT","links":null},{"name":"height","localized_name":"height","type":"INT","links":null}],"properties":{"Node name for S&R":"VAEEncodeAdvanced","cnr_id":"RES4LYF","ver":"5ce9b5a77c227bf864e447a1e65305bf6cada5c2"},"widgets_values":["false",1344,768,"red",false,"16_channels"]},{"id":694,"type":"LoadImage","pos":[-536.0714111328125,-640.6544189453125],"size":[315,314],"flags":{},"order":3,"mode":0,"inputs":[],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":[2028],"slot_index":0},{"name":"MASK","localized_name":"MASK","type":"MASK","links":null}],"properties":{"Node name for S&R":"LoadImage"},"widgets_values":["ChatGPT Image Apr 29, 2025, 08_07_01 PM.png","image"]},{"id":666,"type":"SolidMask","pos":[-1500,-450],"size":[210,106],"flags":{},"order":4,"mode":0,"inputs":[],"outputs":[{"name":"MASK","localized_name":"MASK","type":"MASK","links":[1990],"slot_index":0}],"properties":{"Node name for S&R":"SolidMask"},"widgets_values":[1,1536,512]},{"id":712,"type":"Note","pos":[-1511.985107421875,-66.87181854248047],"size":[245.76409912109375,91.6677017211914],"flags":{},"order":5,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["So long as these masks are all the same size, the regional conditioning nodes will handle resizing to the image size for you."],"color":"#432","bgcolor":"#653"},{"id":668,"type":"SolidMask","pos":[-1502.6644287109375,-289.3330993652344],"size":[210,106],"flags":{},"order":6,"mode":0,"inputs":[],"outputs":[{"name":"MASK","localized_name":"MASK","type":"MASK","links":[1995],"slot_index":0}],"properties":{"Node name for S&R":"SolidMask"},"widgets_values":[1,512,512]},{"id":690,"type":"LoadImage","pos":[-549.7396240234375,-227.43971252441406],"size":[315,314],"flags":{},"order":7,"mode":0,"inputs":[],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":[2062],"slot_index":0},{"name":"MASK","localized_name":"MASK","type":"MASK","links":null}],"properties":{"Node name for S&R":"LoadImage"},"widgets_values":["ComfyUI_00464_.png","image"]},{"id":705,"type":"LoadImage","pos":[-551.003662109375,157.5296173095703],"size":[315,314],"flags":{},"order":8,"mode":0,"inputs":[],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":[2061],"slot_index":0},{"name":"MASK","localized_name":"MASK","type":"MASK","links":null}],"properties":{"Node name for S&R":"LoadImage"},"widgets_values":["ComfyUI_00479_.png","image"]},{"id":560,"type":"ClownRegionalConditionings","pos":[676.1644897460938,-499.31219482421875],"size":[278.4758605957031,266],"flags":{},"order":36,"mode":0,"inputs":[{"name":"cond_regions","localized_name":"cond_regions","type":"COND_REGIONS","shape":7,"link":1938},{"name":"weights","localized_name":"weights","type":"SIGMAS","shape":7,"link":null},{"name":"region_bleeds","localized_name":"region_bleeds","type":"SIGMAS","shape":7,"link":null}],"outputs":[{"name":"conditioning","localized_name":"conditioning","type":"CONDITIONING","links":[1735],"slot_index":0}],"properties":{"Node name for S&R":"ClownRegionalConditionings"},"widgets_values":[0.5,1,10,"beta57",0,20,"boolean",false]},{"id":704,"type":"Note","pos":[324.8023986816406,-781.4505004882812],"size":[290.7107238769531,155.35317993164062],"flags":{},"order":9,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["ClownRegionalConditionings:\n\nTry raising or lowering weight, and changing the weight scheduler from beta57 to Karras (weakens more quickly), or to linear quadratic (stronger late).\n\nTry changing region_bleed_start_step (earlier will make the image blend together more), and end_step."],"color":"#432","bgcolor":"#653"},{"id":715,"type":"SolidMask","pos":[-1486.6612548828125,192.47415161132812],"size":[210,106],"flags":{},"order":10,"mode":0,"inputs":[],"outputs":[{"name":"MASK","localized_name":"MASK","type":"MASK","links":[2063],"slot_index":0}],"properties":{"Node name for S&R":"SolidMask"},"widgets_values":[1,1280,512]},{"id":709,"type":"MaskComposite","pos":[-1104.1712646484375,170.6186981201172],"size":[210,126],"flags":{},"order":25,"mode":0,"inputs":[{"name":"destination","localized_name":"destination","type":"MASK","link":2051},{"name":"source","localized_name":"source","type":"MASK","link":2063}],"outputs":[{"name":"MASK","localized_name":"MASK","type":"MASK","links":[2054,2057],"slot_index":0}],"properties":{"Node name for S&R":"MaskComposite"},"widgets_values":[128,0,"add"]},{"id":703,"type":"Note","pos":[384.9622802734375,346.1895751953125],"size":[278.04071044921875,88],"flags":{},"order":2,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["edge_width also creates some overlap around the edges of the mask."],"color":"#432","bgcolor":"#653"}],"links":[[18,14,0,7,4,"VAE"],[1328,14,0,397,1,"VAE"],[1329,397,0,398,0,"IMAGE"],[1399,7,3,401,3,"LATENT"],[1706,490,0,541,0,"CLIP"],[1732,541,0,401,2,"CONDITIONING"],[1735,560,0,401,1,"CONDITIONING"],[1937,596,0,661,0,"COND_REGIONS"],[1938,661,0,560,0,"COND_REGIONS"],[1939,490,0,662,0,"CLIP"],[1963,663,0,664,0,"MODEL"],[1964,664,0,13,0,"*"],[1965,663,1,490,0,"*"],[1966,663,2,14,0,"*"],[1967,13,0,401,0,"MODEL"],[1969,665,0,667,0,"MASK"],[1990,666,0,676,0,"MASK"],[1991,676,0,665,0,"MASK"],[1995,668,0,665,1,"MASK"],[1999,679,0,678,1,"STYLE_MODEL"],[2000,663,4,679,0,"*"],[2001,663,3,680,0,"*"],[2002,678,0,661,1,"CONDITIONING"],[2003,681,0,678,2,"CLIP_VISION_OUTPUT"],[2004,680,0,681,0,"CLIP_VISION"],[2005,662,0,678,0,"CONDITIONING"],[2006,662,0,682,0,"CONDITIONING"],[2007,679,0,682,1,"STYLE_MODEL"],[2008,683,0,682,2,"CLIP_VISION_OUTPUT"],[2009,680,0,683,0,"CLIP_VISION"],[2020,682,0,596,1,"CONDITIONING"],[2028,694,0,681,1,"IMAGE"],[2036,665,0,661,2,"MASK"],[2038,665,0,700,0,"MASK"],[2041,700,0,670,0,"MASK"],[2042,700,0,596,2,"MASK"],[2043,680,0,706,0,"CLIP_VISION"],[2045,662,0,707,0,"CONDITIONING"],[2046,679,0,707,1,"STYLE_MODEL"],[2047,706,0,707,2,"CLIP_VISION_OUTPUT"],[2048,707,0,708,1,"CONDITIONING"],[2050,708,0,596,0,"COND_REGIONS"],[2051,676,0,709,0,"MASK"],[2054,709,0,710,0,"MASK"],[2056,401,0,397,0,"LATENT"],[2057,709,0,708,2,"MASK"],[2061,705,0,706,1,"IMAGE"],[2062,690,0,683,1,"IMAGE"],[2063,715,0,709,1,"MASK"]],"groups":[],"config":{},"extra":{"ds":{"scale":1.5863092971715371,"offset":[2841.6279889989714,922.4028503570233]},"VHS_latentpreview":false,"VHS_latentpreviewrate":0,"ue_links":[],"VHS_MetadataImage":true,"VHS_KeepIntermediate":true},"version":0.4} \ No newline at end of file diff --git a/ComfyUI/custom_nodes/RES4LYF/example_workflows/flux regional redux (3 zones).json b/ComfyUI/custom_nodes/RES4LYF/example_workflows/flux regional redux (3 zones).json new file mode 100644 index 0000000000000000000000000000000000000000..ef02421def0db0f57a1e14ba1279f050c5a2bfdb --- /dev/null +++ b/ComfyUI/custom_nodes/RES4LYF/example_workflows/flux regional redux (3 zones).json @@ -0,0 +1 @@ +{"last_node_id":714,"last_link_id":2062,"nodes":[{"id":13,"type":"Reroute","pos":[1300,-790],"size":[75,26],"flags":{},"order":16,"mode":0,"inputs":[{"name":"","type":"*","link":1964}],"outputs":[{"name":"","type":"MODEL","links":[1967],"slot_index":0}],"properties":{"showOutputText":false,"horizontal":false}},{"id":490,"type":"Reroute","pos":[1300,-750],"size":[75,26],"flags":{},"order":11,"mode":0,"inputs":[{"name":"","type":"*","link":1965}],"outputs":[{"name":"","type":"CLIP","links":[1706,1939],"slot_index":0}],"properties":{"showOutputText":false,"horizontal":false}},{"id":541,"type":"CLIPTextEncode","pos":[692.1508178710938,183.7528839111328],"size":[265.775390625,113.01970672607422],"flags":{},"order":17,"mode":0,"inputs":[{"name":"clip","localized_name":"clip","type":"CLIP","link":1706}],"outputs":[{"name":"CONDITIONING","localized_name":"CONDITIONING","type":"CONDITIONING","links":[1732],"slot_index":0}],"properties":{"Node name for S&R":"CLIPTextEncode","cnr_id":"comfy-core","ver":"0.3.29"},"widgets_values":["blurry, out of focus, shallow depth of field, low quality, bad quality, low detail, mutated, jpeg artifacts, compression artifacts,"]},{"id":14,"type":"Reroute","pos":[1300,-710],"size":[75,26],"flags":{},"order":12,"mode":0,"inputs":[{"name":"","type":"*","link":1966}],"outputs":[{"name":"","type":"VAE","links":[18,1328],"slot_index":0}],"properties":{"showOutputText":false,"horizontal":false}},{"id":680,"type":"Reroute","pos":[1310,-660],"size":[75,26],"flags":{},"order":13,"mode":0,"inputs":[{"name":"","type":"*","link":2001}],"outputs":[{"name":"","type":"CLIP_VISION","links":[2004,2009,2043]}],"properties":{"showOutputText":false,"horizontal":false}},{"id":678,"type":"StyleModelApply","pos":[101.3630142211914,-560.2020874023438],"size":[262,122],"flags":{},"order":25,"mode":0,"inputs":[{"name":"conditioning","localized_name":"conditioning","type":"CONDITIONING","link":2005},{"name":"style_model","localized_name":"style_model","type":"STYLE_MODEL","link":1999},{"name":"clip_vision_output","localized_name":"clip_vision_output","type":"CLIP_VISION_OUTPUT","link":2003}],"outputs":[{"name":"CONDITIONING","localized_name":"CONDITIONING","type":"CONDITIONING","links":[2002],"slot_index":0}],"properties":{"Node name for S&R":"StyleModelApply"},"widgets_values":[1,"multiply"]},{"id":681,"type":"CLIPVisionEncode","pos":[-173.92124938964844,-524.1537475585938],"size":[253.60000610351562,78],"flags":{},"order":20,"mode":0,"inputs":[{"name":"clip_vision","localized_name":"clip_vision","type":"CLIP_VISION","link":2004},{"name":"image","localized_name":"image","type":"IMAGE","link":2028}],"outputs":[{"name":"CLIP_VISION_OUTPUT","localized_name":"CLIP_VISION_OUTPUT","type":"CLIP_VISION_OUTPUT","links":[2003]}],"properties":{"Node name for S&R":"CLIPVisionEncode"},"widgets_values":["center"]},{"id":560,"type":"ClownRegionalConditionings","pos":[676.1644897460938,-499.31219482421875],"size":[278.4758605957031,266],"flags":{},"order":35,"mode":0,"inputs":[{"name":"cond_regions","localized_name":"cond_regions","type":"COND_REGIONS","shape":7,"link":1938},{"name":"weights","localized_name":"weights","type":"SIGMAS","shape":7,"link":null},{"name":"region_bleeds","localized_name":"region_bleeds","type":"SIGMAS","shape":7,"link":null}],"outputs":[{"name":"conditioning","localized_name":"conditioning","type":"CONDITIONING","links":[1735],"slot_index":0}],"properties":{"Node name for S&R":"ClownRegionalConditionings"},"widgets_values":[0.5,1,14,"beta57",0,20,"boolean",false]},{"id":676,"type":"InvertMask","pos":[-1270,-450],"size":[140,26],"flags":{},"order":15,"mode":0,"inputs":[{"name":"mask","localized_name":"mask","type":"MASK","link":1990}],"outputs":[{"name":"MASK","localized_name":"MASK","type":"MASK","links":[1991,2051],"slot_index":0}],"properties":{"Node name for S&R":"InvertMask"},"widgets_values":[]},{"id":667,"type":"MaskPreview","pos":[-840,-570],"size":[210,246],"flags":{},"order":28,"mode":0,"inputs":[{"name":"mask","localized_name":"mask","type":"MASK","link":1969}],"outputs":[],"properties":{"Node name for S&R":"MaskPreview"},"widgets_values":[]},{"id":661,"type":"ClownRegionalConditioning","pos":[411.9298095703125,-539.053955078125],"size":[211.60000610351562,122],"flags":{},"order":34,"mode":0,"inputs":[{"name":"cond_regions","localized_name":"cond_regions","type":"COND_REGIONS","shape":7,"link":1937},{"name":"conditioning","localized_name":"conditioning","type":"CONDITIONING","shape":7,"link":2002},{"name":"mask","localized_name":"mask","type":"MASK","shape":7,"link":2036}],"outputs":[{"name":"cond_regions","localized_name":"cond_regions","type":"COND_REGIONS","links":[1938],"slot_index":0}],"properties":{"Node name for S&R":"ClownRegionalConditioning"},"widgets_values":[false,256]},{"id":701,"type":"Note","pos":[-1378.6959228515625,-637.0702514648438],"size":[342.05950927734375,88],"flags":{},"order":0,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["I usually just lazily draw masks in Load Image nodes (with some random image loaded), but for the sake of reproducibility, here's another approach."],"color":"#432","bgcolor":"#653"},{"id":663,"type":"FluxLoader","pos":[654.6221923828125,-858.3792724609375],"size":[374.41741943359375,282],"flags":{},"order":1,"mode":0,"inputs":[],"outputs":[{"name":"model","localized_name":"model","type":"MODEL","links":[1963],"slot_index":0},{"name":"clip","localized_name":"clip","type":"CLIP","links":[1965],"slot_index":1},{"name":"vae","localized_name":"vae","type":"VAE","links":[1966],"slot_index":2},{"name":"clip_vision","localized_name":"clip_vision","type":"CLIP_VISION","links":[2001],"slot_index":3},{"name":"style_model","localized_name":"style_model","type":"STYLE_MODEL","links":[2000],"slot_index":4}],"properties":{"Node name for S&R":"FluxLoader"},"widgets_values":["colossusProjectFlux_v42AIO.safetensors","fp8_e4m3fn_fast",".use_ckpt_clip",".none",".use_ckpt_vae","sigclip_vision_patch14_384.safetensors","flux1-redux-dev.safetensors"]},{"id":664,"type":"ReFluxPatcher","pos":[1064.7325439453125,-863.0516967773438],"size":[210,82],"flags":{},"order":10,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","link":1963}],"outputs":[{"name":"model","localized_name":"model","type":"MODEL","links":[1964],"slot_index":0}],"properties":{"Node name for S&R":"ReFluxPatcher"},"widgets_values":["float64",true]},{"id":679,"type":"Reroute","pos":[1300,-610],"size":[75,26],"flags":{},"order":14,"mode":0,"inputs":[{"name":"","type":"*","link":2000}],"outputs":[{"name":"","type":"STYLE_MODEL","links":[1999,2007,2046]}],"properties":{"showOutputText":false,"horizontal":false}},{"id":662,"type":"CLIPTextEncode","pos":[-140.3179168701172,-670.337158203125],"size":[210,88],"flags":{"collapsed":false},"order":18,"mode":0,"inputs":[{"name":"clip","localized_name":"clip","type":"CLIP","link":1939}],"outputs":[{"name":"CONDITIONING","localized_name":"CONDITIONING","type":"CONDITIONING","links":[2005,2006,2045],"slot_index":0}],"properties":{"Node name for S&R":"CLIPTextEncode","cnr_id":"comfy-core","ver":"0.3.29"},"widgets_values":[""]},{"id":704,"type":"Note","pos":[324.8023986816406,-781.4505004882812],"size":[290.7107238769531,155.35317993164062],"flags":{},"order":2,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["ClownRegionalConditionings:\n\nTry raising or lowering weight, and changing the weight scheduler from beta57 to Karras (weakens more quickly), or to linear quadratic (stronger late).\n\nTry changing region_bleed_start_step, and end_step."],"color":"#432","bgcolor":"#653"},{"id":398,"type":"SaveImage","pos":[1379.9996337890625,-267.2835998535156],"size":[341.7508850097656,561.0067749023438],"flags":{},"order":38,"mode":0,"inputs":[{"name":"images","localized_name":"images","type":"IMAGE","link":1329}],"outputs":[],"properties":{"Node name for S&R":"SaveImage","cnr_id":"comfy-core","ver":"0.3.29"},"widgets_values":["ComfyUI"]},{"id":703,"type":"Note","pos":[-84.50921630859375,-859.7656860351562],"size":[278.04071044921875,88],"flags":{},"order":3,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["edge_width also creates some overlap around the edges of the mask."],"color":"#432","bgcolor":"#653"},{"id":683,"type":"CLIPVisionEncode","pos":[-170,-220],"size":[253.60000610351562,78],"flags":{},"order":21,"mode":0,"inputs":[{"name":"clip_vision","localized_name":"clip_vision","type":"CLIP_VISION","link":2009},{"name":"image","localized_name":"image","type":"IMAGE","link":2062}],"outputs":[{"name":"CLIP_VISION_OUTPUT","localized_name":"CLIP_VISION_OUTPUT","type":"CLIP_VISION_OUTPUT","links":[2008]}],"properties":{"Node name for S&R":"CLIPVisionEncode"},"widgets_values":["center"]},{"id":682,"type":"StyleModelApply","pos":[100,-250],"size":[262,122],"flags":{},"order":26,"mode":0,"inputs":[{"name":"conditioning","localized_name":"conditioning","type":"CONDITIONING","link":2006},{"name":"style_model","localized_name":"style_model","type":"STYLE_MODEL","link":2007},{"name":"clip_vision_output","localized_name":"clip_vision_output","type":"CLIP_VISION_OUTPUT","link":2008}],"outputs":[{"name":"CONDITIONING","localized_name":"CONDITIONING","type":"CONDITIONING","links":[2020],"slot_index":0}],"properties":{"Node name for S&R":"StyleModelApply"},"widgets_values":[1,"multiply"]},{"id":596,"type":"ClownRegionalConditioning","pos":[425.9762268066406,-243.12513732910156],"size":[211.60000610351562,122],"flags":{},"order":33,"mode":0,"inputs":[{"name":"cond_regions","localized_name":"cond_regions","type":"COND_REGIONS","shape":7,"link":2050},{"name":"conditioning","localized_name":"conditioning","type":"CONDITIONING","shape":7,"link":2020},{"name":"mask","localized_name":"mask","type":"MASK","shape":7,"link":2042}],"outputs":[{"name":"cond_regions","localized_name":"cond_regions","type":"COND_REGIONS","links":[1937],"slot_index":0}],"properties":{"Node name for S&R":"ClownRegionalConditioning"},"widgets_values":[false,256]},{"id":706,"type":"CLIPVisionEncode","pos":[-180,180],"size":[253.60000610351562,78],"flags":{},"order":22,"mode":0,"inputs":[{"name":"clip_vision","localized_name":"clip_vision","type":"CLIP_VISION","link":2043},{"name":"image","localized_name":"image","type":"IMAGE","link":2061}],"outputs":[{"name":"CLIP_VISION_OUTPUT","localized_name":"CLIP_VISION_OUTPUT","type":"CLIP_VISION_OUTPUT","links":[2047]}],"properties":{"Node name for S&R":"CLIPVisionEncode"},"widgets_values":["center"]},{"id":707,"type":"StyleModelApply","pos":[90,150],"size":[262,122],"flags":{},"order":27,"mode":0,"inputs":[{"name":"conditioning","localized_name":"conditioning","type":"CONDITIONING","link":2045},{"name":"style_model","localized_name":"style_model","type":"STYLE_MODEL","link":2046},{"name":"clip_vision_output","localized_name":"clip_vision_output","type":"CLIP_VISION_OUTPUT","link":2047}],"outputs":[{"name":"CONDITIONING","localized_name":"CONDITIONING","type":"CONDITIONING","links":[2048],"slot_index":0}],"properties":{"Node name for S&R":"StyleModelApply"},"widgets_values":[1,"multiply"]},{"id":708,"type":"ClownRegionalConditioning","pos":[420,160],"size":[211.60000610351562,122],"flags":{},"order":31,"mode":0,"inputs":[{"name":"cond_regions","localized_name":"cond_regions","type":"COND_REGIONS","shape":7,"link":null},{"name":"conditioning","localized_name":"conditioning","type":"CONDITIONING","shape":7,"link":2048},{"name":"mask","localized_name":"mask","type":"MASK","shape":7,"link":2057}],"outputs":[{"name":"cond_regions","localized_name":"cond_regions","type":"COND_REGIONS","links":[2050],"slot_index":0}],"properties":{"Node name for S&R":"ClownRegionalConditioning"},"widgets_values":[false,256]},{"id":665,"type":"MaskComposite","pos":[-1100,-450],"size":[210,126],"flags":{},"order":23,"mode":0,"inputs":[{"name":"destination","localized_name":"destination","type":"MASK","link":1991},{"name":"source","localized_name":"source","type":"MASK","link":1995}],"outputs":[{"name":"MASK","localized_name":"MASK","type":"MASK","links":[1969,2036,2038],"slot_index":0}],"properties":{"Node name for S&R":"MaskComposite"},"widgets_values":[0,0,"add"]},{"id":670,"type":"MaskPreview","pos":[-840.8076782226562,-235.62042236328125],"size":[210,246],"flags":{},"order":32,"mode":0,"inputs":[{"name":"mask","localized_name":"mask","type":"MASK","link":2041}],"outputs":[],"properties":{"Node name for S&R":"MaskPreview"},"widgets_values":[]},{"id":700,"type":"MaskFlip+","pos":[-1099.420166015625,-236.15890502929688],"size":[210,58],"flags":{},"order":29,"mode":0,"inputs":[{"name":"mask","localized_name":"mask","type":"MASK","link":2038}],"outputs":[{"name":"MASK","localized_name":"MASK","type":"MASK","links":[2041,2042],"slot_index":0}],"properties":{"Node name for S&R":"MaskFlip+"},"widgets_values":["x"]},{"id":710,"type":"MaskPreview","pos":[-847.5751953125,166.58413696289062],"size":[210,246],"flags":{},"order":30,"mode":0,"inputs":[{"name":"mask","localized_name":"mask","type":"MASK","link":2054}],"outputs":[],"properties":{"Node name for S&R":"MaskPreview"},"widgets_values":[]},{"id":397,"type":"VAEDecode","pos":[1403.6392822265625,-371.9699401855469],"size":[210,46],"flags":{},"order":37,"mode":0,"inputs":[{"name":"samples","localized_name":"samples","type":"LATENT","link":2056},{"name":"vae","localized_name":"vae","type":"VAE","link":1328}],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":[1329],"slot_index":0}],"properties":{"Node name for S&R":"VAEDecode","cnr_id":"comfy-core","ver":"0.3.29"},"widgets_values":[]},{"id":401,"type":"ClownsharKSampler_Beta","pos":[1010,-370],"size":[340.55120849609375,666.8208618164062],"flags":{},"order":36,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","shape":7,"link":1967},{"name":"positive","localized_name":"positive","type":"CONDITIONING","shape":7,"link":1735},{"name":"negative","localized_name":"negative","type":"CONDITIONING","shape":7,"link":1732},{"name":"latent_image","localized_name":"latent_image","type":"LATENT","shape":7,"link":1399},{"name":"sigmas","localized_name":"sigmas","type":"SIGMAS","shape":7,"link":null},{"name":"guides","localized_name":"guides","type":"GUIDES","shape":7,"link":null},{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":null}],"outputs":[{"name":"output","localized_name":"output","type":"LATENT","links":[2056],"slot_index":0},{"name":"denoised","localized_name":"denoised","type":"LATENT","links":null},{"name":"options","localized_name":"options","type":"OPTIONS","links":null}],"properties":{"Node name for S&R":"ClownsharKSampler_Beta","cnr_id":"RES4LYF","ver":"5ce9b5a77c227bf864e447a1e65305bf6cada5c2"},"widgets_values":[0.5,"exponential/res_2s","bong_tangent",20,-1,1,1,109,"fixed","standard",true]},{"id":7,"type":"VAEEncodeAdvanced","pos":[696.7778930664062,-164.97328186035156],"size":[261.2217712402344,279.3136901855469],"flags":{},"order":19,"mode":0,"inputs":[{"name":"image_1","localized_name":"image_1","type":"IMAGE","shape":7,"link":null},{"name":"image_2","localized_name":"image_2","type":"IMAGE","shape":7,"link":null},{"name":"mask","localized_name":"mask","type":"IMAGE","shape":7,"link":null},{"name":"latent","localized_name":"latent","type":"LATENT","shape":7,"link":null},{"name":"vae","localized_name":"vae","type":"VAE","shape":7,"link":18}],"outputs":[{"name":"latent_1","localized_name":"latent_1","type":"LATENT","links":[],"slot_index":0},{"name":"latent_2","localized_name":"latent_2","type":"LATENT","links":[],"slot_index":1},{"name":"mask","localized_name":"mask","type":"MASK","links":[],"slot_index":2},{"name":"empty_latent","localized_name":"empty_latent","type":"LATENT","links":[1399],"slot_index":3},{"name":"width","localized_name":"width","type":"INT","links":null},{"name":"height","localized_name":"height","type":"INT","links":null}],"properties":{"Node name for S&R":"VAEEncodeAdvanced","cnr_id":"RES4LYF","ver":"5ce9b5a77c227bf864e447a1e65305bf6cada5c2"},"widgets_values":["false",1344,768,"red",false,"16_channels"]},{"id":694,"type":"LoadImage","pos":[-536.0714111328125,-640.6544189453125],"size":[315,314],"flags":{},"order":4,"mode":0,"inputs":[],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":[2028],"slot_index":0},{"name":"MASK","localized_name":"MASK","type":"MASK","links":null}],"properties":{"Node name for S&R":"LoadImage"},"widgets_values":["ChatGPT Image Apr 29, 2025, 08_07_01 PM.png","image"]},{"id":666,"type":"SolidMask","pos":[-1500,-450],"size":[210,106],"flags":{},"order":5,"mode":0,"inputs":[],"outputs":[{"name":"MASK","localized_name":"MASK","type":"MASK","links":[1990],"slot_index":0}],"properties":{"Node name for S&R":"SolidMask"},"widgets_values":[1,1536,512]},{"id":712,"type":"Note","pos":[-1511.985107421875,-66.87181854248047],"size":[245.76409912109375,91.6677017211914],"flags":{},"order":6,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["So long as these masks are all the same size, the regional conditioning nodes will handle resizing to the image size for you."],"color":"#432","bgcolor":"#653"},{"id":709,"type":"MaskComposite","pos":[-1104.1712646484375,170.6186981201172],"size":[210,126],"flags":{},"order":24,"mode":0,"inputs":[{"name":"destination","localized_name":"destination","type":"MASK","link":2051},{"name":"source","localized_name":"source","type":"MASK","link":2060}],"outputs":[{"name":"MASK","localized_name":"MASK","type":"MASK","links":[2054,2057],"slot_index":0}],"properties":{"Node name for S&R":"MaskComposite"},"widgets_values":[512,0,"add"]},{"id":668,"type":"SolidMask","pos":[-1502.6644287109375,-289.3330993652344],"size":[210,106],"flags":{},"order":7,"mode":0,"inputs":[],"outputs":[{"name":"MASK","localized_name":"MASK","type":"MASK","links":[1995,2060],"slot_index":0}],"properties":{"Node name for S&R":"SolidMask"},"widgets_values":[1,512,512]},{"id":690,"type":"LoadImage","pos":[-549.7396240234375,-227.43971252441406],"size":[315,314],"flags":{},"order":8,"mode":0,"inputs":[],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":[2062],"slot_index":0},{"name":"MASK","localized_name":"MASK","type":"MASK","links":null}],"properties":{"Node name for S&R":"LoadImage"},"widgets_values":["ComfyUI_00464_.png","image"]},{"id":705,"type":"LoadImage","pos":[-551.003662109375,157.5296173095703],"size":[315,314],"flags":{},"order":9,"mode":0,"inputs":[],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":[2061],"slot_index":0},{"name":"MASK","localized_name":"MASK","type":"MASK","links":null}],"properties":{"Node name for S&R":"LoadImage"},"widgets_values":["ComfyUI_00479_.png","image"]}],"links":[[18,14,0,7,4,"VAE"],[1328,14,0,397,1,"VAE"],[1329,397,0,398,0,"IMAGE"],[1399,7,3,401,3,"LATENT"],[1706,490,0,541,0,"CLIP"],[1732,541,0,401,2,"CONDITIONING"],[1735,560,0,401,1,"CONDITIONING"],[1937,596,0,661,0,"COND_REGIONS"],[1938,661,0,560,0,"COND_REGIONS"],[1939,490,0,662,0,"CLIP"],[1963,663,0,664,0,"MODEL"],[1964,664,0,13,0,"*"],[1965,663,1,490,0,"*"],[1966,663,2,14,0,"*"],[1967,13,0,401,0,"MODEL"],[1969,665,0,667,0,"MASK"],[1990,666,0,676,0,"MASK"],[1991,676,0,665,0,"MASK"],[1995,668,0,665,1,"MASK"],[1999,679,0,678,1,"STYLE_MODEL"],[2000,663,4,679,0,"*"],[2001,663,3,680,0,"*"],[2002,678,0,661,1,"CONDITIONING"],[2003,681,0,678,2,"CLIP_VISION_OUTPUT"],[2004,680,0,681,0,"CLIP_VISION"],[2005,662,0,678,0,"CONDITIONING"],[2006,662,0,682,0,"CONDITIONING"],[2007,679,0,682,1,"STYLE_MODEL"],[2008,683,0,682,2,"CLIP_VISION_OUTPUT"],[2009,680,0,683,0,"CLIP_VISION"],[2020,682,0,596,1,"CONDITIONING"],[2028,694,0,681,1,"IMAGE"],[2036,665,0,661,2,"MASK"],[2038,665,0,700,0,"MASK"],[2041,700,0,670,0,"MASK"],[2042,700,0,596,2,"MASK"],[2043,680,0,706,0,"CLIP_VISION"],[2045,662,0,707,0,"CONDITIONING"],[2046,679,0,707,1,"STYLE_MODEL"],[2047,706,0,707,2,"CLIP_VISION_OUTPUT"],[2048,707,0,708,1,"CONDITIONING"],[2050,708,0,596,0,"COND_REGIONS"],[2051,676,0,709,0,"MASK"],[2054,709,0,710,0,"MASK"],[2056,401,0,397,0,"LATENT"],[2057,709,0,708,2,"MASK"],[2060,668,0,709,1,"MASK"],[2061,705,0,706,1,"IMAGE"],[2062,690,0,683,1,"IMAGE"]],"groups":[],"config":{},"extra":{"ds":{"scale":1.586309297171537,"offset":[2736.1731738476205,939.9577246808323]},"VHS_latentpreview":false,"VHS_latentpreviewrate":0,"ue_links":[],"VHS_MetadataImage":true,"VHS_KeepIntermediate":true},"version":0.4} \ No newline at end of file diff --git a/ComfyUI/custom_nodes/RES4LYF/example_workflows/flux style antiblur.json b/ComfyUI/custom_nodes/RES4LYF/example_workflows/flux style antiblur.json new file mode 100644 index 0000000000000000000000000000000000000000..87d8bc1f6e817f1528ba4f864445eae73654d18d --- /dev/null +++ b/ComfyUI/custom_nodes/RES4LYF/example_workflows/flux style antiblur.json @@ -0,0 +1 @@ +{"last_node_id":739,"last_link_id":2113,"nodes":[{"id":13,"type":"Reroute","pos":[1280,-650],"size":[75,26],"flags":{},"order":7,"mode":0,"inputs":[{"name":"","type":"*","link":1964}],"outputs":[{"name":"","type":"MODEL","links":[1967],"slot_index":0}],"properties":{"showOutputText":false,"horizontal":false}},{"id":490,"type":"Reroute","pos":[1280,-610],"size":[75,26],"flags":{},"order":5,"mode":0,"inputs":[{"name":"","type":"*","link":1965}],"outputs":[{"name":"","type":"CLIP","links":[1939],"slot_index":0}],"properties":{"showOutputText":false,"horizontal":false}},{"id":14,"type":"Reroute","pos":[1280,-570],"size":[75,26],"flags":{},"order":6,"mode":0,"inputs":[{"name":"","type":"*","link":1966}],"outputs":[{"name":"","type":"VAE","links":[18,1328],"slot_index":0}],"properties":{"showOutputText":false,"horizontal":false}},{"id":398,"type":"SaveImage","pos":[1379.9996337890625,-267.2835998535156],"size":[341.7508850097656,561.0067749023438],"flags":{},"order":13,"mode":0,"inputs":[{"name":"images","localized_name":"images","type":"IMAGE","link":1329}],"outputs":[],"properties":{"Node name for S&R":"SaveImage","cnr_id":"comfy-core","ver":"0.3.29"},"widgets_values":["ComfyUI"]},{"id":663,"type":"FluxLoader","pos":[630,-720],"size":[374.41741943359375,282],"flags":{},"order":0,"mode":0,"inputs":[],"outputs":[{"name":"model","localized_name":"model","type":"MODEL","links":[1963],"slot_index":0},{"name":"clip","localized_name":"clip","type":"CLIP","links":[1965],"slot_index":1},{"name":"vae","localized_name":"vae","type":"VAE","links":[1966],"slot_index":2},{"name":"clip_vision","localized_name":"clip_vision","type":"CLIP_VISION","links":[],"slot_index":3},{"name":"style_model","localized_name":"style_model","type":"STYLE_MODEL","links":[],"slot_index":4}],"properties":{"Node name for S&R":"FluxLoader"},"widgets_values":["colossusProjectFlux_v42AIO.safetensors","fp8_e4m3fn_fast",".use_ckpt_clip",".none",".use_ckpt_vae",".none",".none"]},{"id":664,"type":"ReFluxPatcher","pos":[1040,-720],"size":[210,82],"flags":{},"order":4,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","link":1963}],"outputs":[{"name":"model","localized_name":"model","type":"MODEL","links":[1964],"slot_index":0}],"properties":{"Node name for S&R":"ReFluxPatcher"},"widgets_values":["float64",true]},{"id":397,"type":"VAEDecode","pos":[1382.3662109375,-374.17059326171875],"size":[210,46],"flags":{},"order":12,"mode":0,"inputs":[{"name":"samples","localized_name":"samples","type":"LATENT","link":2096},{"name":"vae","localized_name":"vae","type":"VAE","link":1328}],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":[1329],"slot_index":0}],"properties":{"Node name for S&R":"VAEDecode","cnr_id":"comfy-core","ver":"0.3.29"},"widgets_values":[]},{"id":7,"type":"VAEEncodeAdvanced","pos":[412.2475280761719,-199.0681915283203],"size":[261.2217712402344,279.3136901855469],"flags":{},"order":9,"mode":0,"inputs":[{"name":"image_1","localized_name":"image_1","type":"IMAGE","shape":7,"link":2113},{"name":"image_2","localized_name":"image_2","type":"IMAGE","shape":7,"link":null},{"name":"mask","localized_name":"mask","type":"IMAGE","shape":7,"link":null},{"name":"latent","localized_name":"latent","type":"LATENT","shape":7,"link":null},{"name":"vae","localized_name":"vae","type":"VAE","shape":7,"link":18}],"outputs":[{"name":"latent_1","localized_name":"latent_1","type":"LATENT","links":[2100],"slot_index":0},{"name":"latent_2","localized_name":"latent_2","type":"LATENT","links":[],"slot_index":1},{"name":"mask","localized_name":"mask","type":"MASK","links":[],"slot_index":2},{"name":"empty_latent","localized_name":"empty_latent","type":"LATENT","links":[1399],"slot_index":3},{"name":"width","localized_name":"width","type":"INT","links":null},{"name":"height","localized_name":"height","type":"INT","links":null}],"properties":{"Node name for S&R":"VAEEncodeAdvanced","cnr_id":"RES4LYF","ver":"5ce9b5a77c227bf864e447a1e65305bf6cada5c2"},"widgets_values":["false",1024,1024,"red",false,"16_channels"]},{"id":662,"type":"CLIPTextEncode","pos":[761.3005981445312,-357.2689208984375],"size":[210,102.54972839355469],"flags":{"collapsed":false},"order":8,"mode":0,"inputs":[{"name":"clip","localized_name":"clip","type":"CLIP","link":1939}],"outputs":[{"name":"CONDITIONING","localized_name":"CONDITIONING","type":"CONDITIONING","links":[2098],"slot_index":0}],"properties":{"Node name for S&R":"CLIPTextEncode","cnr_id":"comfy-core","ver":"0.3.29"},"widgets_values":["a woman wearing a red flannel shirt and a cute shark plush blue hat, a college campus, brick buildings"]},{"id":727,"type":"Note","pos":[412.8926086425781,-351.8606872558594],"size":[272.4425048828125,88],"flags":{},"order":1,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["This approach can be combined with the regional conditioning anti-blur approach for an even more powerful effect."],"color":"#432","bgcolor":"#653"},{"id":401,"type":"ClownsharKSampler_Beta","pos":[1010,-370],"size":[340.55120849609375,666.8208618164062],"flags":{},"order":11,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","shape":7,"link":1967},{"name":"positive","localized_name":"positive","type":"CONDITIONING","shape":7,"link":2098},{"name":"negative","localized_name":"negative","type":"CONDITIONING","shape":7,"link":null},{"name":"latent_image","localized_name":"latent_image","type":"LATENT","shape":7,"link":1399},{"name":"sigmas","localized_name":"sigmas","type":"SIGMAS","shape":7,"link":null},{"name":"guides","localized_name":"guides","type":"GUIDES","shape":7,"link":2099},{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":null}],"outputs":[{"name":"output","localized_name":"output","type":"LATENT","links":[2096],"slot_index":0},{"name":"denoised","localized_name":"denoised","type":"LATENT","links":null},{"name":"options","localized_name":"options","type":"OPTIONS","links":null}],"properties":{"Node name for S&R":"ClownsharKSampler_Beta","cnr_id":"RES4LYF","ver":"5ce9b5a77c227bf864e447a1e65305bf6cada5c2"},"widgets_values":[0.5,"multistep/res_2m","bong_tangent",30,-1,1,1,7,"fixed","standard",true]},{"id":724,"type":"ClownGuide_Style_Beta","pos":[703.7374267578125,-198.63233947753906],"size":[262.8634033203125,286],"flags":{},"order":10,"mode":0,"inputs":[{"name":"guide","localized_name":"guide","type":"LATENT","shape":7,"link":2100},{"name":"mask","localized_name":"mask","type":"MASK","shape":7,"link":null},{"name":"weights","localized_name":"weights","type":"SIGMAS","shape":7,"link":null},{"name":"guides","localized_name":"guides","type":"GUIDES","shape":7,"link":null}],"outputs":[{"name":"guides","localized_name":"guides","type":"GUIDES","links":[2099],"slot_index":0}],"properties":{"Node name for S&R":"ClownGuide_Style_Beta"},"widgets_values":["positive","WCT",1,1,"constant",0,10,false]},{"id":739,"type":"LoadImage","pos":[70.82455444335938,-201.66342163085938],"size":[315,314],"flags":{},"order":3,"mode":0,"inputs":[],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":[2113],"slot_index":0},{"name":"MASK","localized_name":"MASK","type":"MASK","links":null}],"properties":{"Node name for S&R":"LoadImage"},"widgets_values":["pasted/image (655).png","image"]},{"id":726,"type":"Note","pos":[415.7740478515625,153.59271240234375],"size":[364.5906677246094,164.38613891601562],"flags":{},"order":2,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["The best style guide images will share the lighting and color composition of your desired scene. Some are just inexplicably ineffective at killing blur. Just gather up a bunch of images to try, you'll find some good ones that can be reused for many things. I'm including the one used here in the example_workflows directory, be sure to check for it.\n\nAnd don't forget to change seeds. Don't optimize for one seed only. Don't get stuck on one seed! Sometimes one is just not going to work out for whatever you're doing."],"color":"#432","bgcolor":"#653"}],"links":[[18,14,0,7,4,"VAE"],[1328,14,0,397,1,"VAE"],[1329,397,0,398,0,"IMAGE"],[1399,7,3,401,3,"LATENT"],[1939,490,0,662,0,"CLIP"],[1963,663,0,664,0,"MODEL"],[1964,664,0,13,0,"*"],[1965,663,1,490,0,"*"],[1966,663,2,14,0,"*"],[1967,13,0,401,0,"MODEL"],[2096,401,0,397,0,"LATENT"],[2098,662,0,401,1,"CONDITIONING"],[2099,724,0,401,5,"GUIDES"],[2100,7,0,724,0,"LATENT"],[2113,739,0,7,0,"IMAGE"]],"groups":[],"config":{},"extra":{"ds":{"scale":1.91943424957756,"offset":[1140.4413839969193,798.117449447068]},"VHS_latentpreview":false,"VHS_latentpreviewrate":0,"ue_links":[],"VHS_MetadataImage":true,"VHS_KeepIntermediate":true},"version":0.4} \ No newline at end of file diff --git a/ComfyUI/custom_nodes/RES4LYF/example_workflows/flux style transfer gguf.json b/ComfyUI/custom_nodes/RES4LYF/example_workflows/flux style transfer gguf.json new file mode 100644 index 0000000000000000000000000000000000000000..d949bc659e8b71d5e8b634fe9ffc6d8ef13434c4 --- /dev/null +++ b/ComfyUI/custom_nodes/RES4LYF/example_workflows/flux style transfer gguf.json @@ -0,0 +1 @@ +{"last_node_id":1392,"last_link_id":3739,"nodes":[{"id":13,"type":"Reroute","pos":[13508.9013671875,-109.2831802368164],"size":[75,26],"flags":{},"order":20,"mode":0,"inputs":[{"name":"","type":"*","link":3737}],"outputs":[{"name":"","type":"MODEL","links":[1395],"slot_index":0}],"properties":{"showOutputText":false,"horizontal":false}},{"id":14,"type":"Reroute","pos":[13508.9013671875,-29.283178329467773],"size":[75,26],"flags":{},"order":16,"mode":0,"inputs":[{"name":"","type":"*","link":3739}],"outputs":[{"name":"","type":"VAE","links":[18,2696],"slot_index":0}],"properties":{"showOutputText":false,"horizontal":false}},{"id":490,"type":"Reroute","pos":[13508.9013671875,-69.28317260742188],"size":[75,26],"flags":{},"order":17,"mode":0,"inputs":[{"name":"","type":"*","link":3738}],"outputs":[{"name":"","type":"CLIP","links":[2881,3581],"slot_index":0}],"properties":{"showOutputText":false,"horizontal":false}},{"id":1308,"type":"ClownGuide_Style_Beta","pos":[14108.255859375,675.60693359375],"size":[246.31312561035156,286],"flags":{},"order":29,"mode":0,"inputs":[{"name":"guide","localized_name":"guide","type":"LATENT","shape":7,"link":3709},{"name":"mask","localized_name":"mask","type":"MASK","shape":7,"link":null},{"name":"weights","localized_name":"weights","type":"SIGMAS","shape":7,"link":null},{"name":"guides","localized_name":"guides","type":"GUIDES","shape":7,"link":3699}],"outputs":[{"name":"guides","localized_name":"guides","type":"GUIDES","links":[3604],"slot_index":0}],"properties":{"Node name for S&R":"ClownGuide_Style_Beta"},"widgets_values":["positive","WCT",1,1,"constant",0,-1,false]},{"id":431,"type":"ModelSamplingAdvancedResolution","pos":[13218.9013671875,-309.28314208984375],"size":[260.3999938964844,126],"flags":{},"order":28,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","link":1395},{"name":"latent_image","localized_name":"latent_image","type":"LATENT","link":1398}],"outputs":[{"name":"model","localized_name":"model","type":"MODEL","links":[2692],"slot_index":0}],"properties":{"Node name for S&R":"ModelSamplingAdvancedResolution","cnr_id":"RES4LYF","ver":"5ce9b5a77c227bf864e447a1e65305bf6cada5c2"},"widgets_values":["exponential",1.35,0.85]},{"id":970,"type":"CLIPTextEncode","pos":[13688.255859375,165.60690307617188],"size":[281.9206848144531,109.87118530273438],"flags":{},"order":21,"mode":0,"inputs":[{"name":"clip","localized_name":"clip","type":"CLIP","link":2881}],"outputs":[{"name":"CONDITIONING","localized_name":"CONDITIONING","type":"CONDITIONING","links":[2882,3627],"slot_index":0}],"properties":{"Node name for S&R":"CLIPTextEncode","cnr_id":"comfy-core","ver":"0.3.29"},"widgets_values":["blurry, out of focus, shallow depth of field, jpeg artifacts, low quality, bad quality, unsharp"]},{"id":1378,"type":"Reroute","pos":[13184.07421875,533.128662109375],"size":[75,26],"flags":{},"order":19,"mode":0,"inputs":[{"name":"","type":"*","link":3721}],"outputs":[{"name":"","type":"IMAGE","links":[3724,3729],"slot_index":0}],"properties":{"showOutputText":false,"horizontal":false}},{"id":1379,"type":"Reroute","pos":[13185.853515625,168.15780639648438],"size":[75,26],"flags":{},"order":18,"mode":0,"inputs":[{"name":"","type":"*","link":3725}],"outputs":[{"name":"","type":"IMAGE","links":[3726],"slot_index":0}],"properties":{"showOutputText":false,"horizontal":false}},{"id":909,"type":"SaveImage","pos":[15220,-259.5838928222656],"size":[457.3382263183594,422.2065124511719],"flags":{},"order":34,"mode":0,"inputs":[{"name":"images","localized_name":"images","type":"IMAGE","link":2697}],"outputs":[],"properties":{"Node name for S&R":"SaveImage","cnr_id":"comfy-core","ver":"0.3.29"},"widgets_values":["ComfyUI"]},{"id":7,"type":"VAEEncodeAdvanced","pos":[13400,560],"size":[261.2217712402344,298],"flags":{"collapsed":true},"order":26,"mode":0,"inputs":[{"name":"image_1","localized_name":"image_1","type":"IMAGE","shape":7,"link":3688},{"name":"image_2","localized_name":"image_2","type":"IMAGE","shape":7,"link":3727},{"name":"mask","localized_name":"mask","type":"IMAGE","shape":7,"link":null},{"name":"latent","localized_name":"latent","type":"LATENT","shape":7,"link":null},{"name":"vae","localized_name":"vae","type":"VAE","shape":7,"link":18},{"name":"width","type":"INT","pos":[10,160.00003051757812],"widget":{"name":"width"},"link":3732},{"name":"height","type":"INT","pos":[10,184.00003051757812],"widget":{"name":"height"},"link":3733}],"outputs":[{"name":"latent_1","localized_name":"latent_1","type":"LATENT","links":[2983,3710],"slot_index":0},{"name":"latent_2","localized_name":"latent_2","type":"LATENT","links":[3709],"slot_index":1},{"name":"mask","localized_name":"mask","type":"MASK","links":[],"slot_index":2},{"name":"empty_latent","localized_name":"empty_latent","type":"LATENT","links":[1398],"slot_index":3},{"name":"width","localized_name":"width","type":"INT","links":[],"slot_index":4},{"name":"height","localized_name":"height","type":"INT","links":[],"slot_index":5}],"properties":{"Node name for S&R":"VAEEncodeAdvanced","cnr_id":"RES4LYF","ver":"5ce9b5a77c227bf864e447a1e65305bf6cada5c2"},"widgets_values":["false",1344,768,"red",false,"16_channels"]},{"id":1371,"type":"Image Repeat Tile To Size","pos":[13390,500],"size":[210,146],"flags":{"collapsed":true},"order":23,"mode":0,"inputs":[{"name":"image","localized_name":"image","type":"IMAGE","link":3726},{"name":"width","type":"INT","pos":[10,36],"widget":{"name":"width"},"link":3730},{"name":"height","type":"INT","pos":[10,60],"widget":{"name":"height"},"link":3731}],"outputs":[{"name":"image","localized_name":"image","type":"IMAGE","links":[3727,3728],"slot_index":0}],"properties":{"Node name for S&R":"Image Repeat Tile To Size"},"widgets_values":[1024,1024,true]},{"id":1380,"type":"SetImageSize","pos":[13380,320],"size":[210,102],"flags":{},"order":0,"mode":0,"inputs":[],"outputs":[{"name":"width","localized_name":"width","type":"INT","links":[3730,3732],"slot_index":0},{"name":"height","localized_name":"height","type":"INT","links":[3731,3733],"slot_index":1}],"properties":{"Node name for S&R":"SetImageSize"},"widgets_values":[1344,768]},{"id":1377,"type":"Image Comparer (rgthree)","pos":[15742.4619140625,-253.3526153564453],"size":[461.9190368652344,413.5953369140625],"flags":{},"order":35,"mode":0,"inputs":[{"name":"image_a","type":"IMAGE","dir":3,"link":3720},{"name":"image_b","type":"IMAGE","dir":3,"link":3729}],"outputs":[],"properties":{"comparer_mode":"Slide"},"widgets_values":[[{"name":"A","selected":true,"url":"/api/view?filename=rgthree.compare._temp_clqis_00009_.png&type=temp&subfolder=&rand=0.8606788093916207"},{"name":"B","selected":true,"url":"/api/view?filename=rgthree.compare._temp_clqis_00010_.png&type=temp&subfolder=&rand=0.7775594190958295"}]]},{"id":908,"type":"VAEDecode","pos":[15217.7802734375,-312.1965637207031],"size":[210,46],"flags":{"collapsed":true},"order":33,"mode":0,"inputs":[{"name":"samples","localized_name":"samples","type":"LATENT","link":3469},{"name":"vae","localized_name":"vae","type":"VAE","link":2696}],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":[2697,3720],"slot_index":0}],"properties":{"Node name for S&R":"VAEDecode","cnr_id":"comfy-core","ver":"0.3.29"},"widgets_values":[]},{"id":1383,"type":"Note","pos":[14428.40234375,580.1749877929688],"size":[261.9539489746094,88],"flags":{},"order":1,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["Samplers like res_2s in this cycling node will also work and are faster. res_2m and res_3m are even faster, but sometimes the effect takes longer in wall time to fully kick in."],"color":"#432","bgcolor":"#653"},{"id":1384,"type":"Note","pos":[14793.0322265625,518.4120483398438],"size":[261.9539489746094,88],"flags":{},"order":2,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["res_2m or res_3m can be used here instead and are faster, but are less likely to fully clean up lingering artifacts."],"color":"#432","bgcolor":"#653"},{"id":1385,"type":"Note","pos":[14398.345703125,768.2096557617188],"size":[261.9539489746094,88],"flags":{},"order":3,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["method = AdaIN is faster and uses less memory, but is less accurate. Some prefer the effect."],"color":"#432","bgcolor":"#653"},{"id":1328,"type":"ClownOptions_SDE_Beta","pos":[14186.4755859375,-132.6126251220703],"size":[315,266],"flags":{"collapsed":true},"order":4,"mode":0,"inputs":[{"name":"etas","localized_name":"etas","type":"SIGMAS","shape":7,"link":null},{"name":"etas_substep","localized_name":"etas_substep","type":"SIGMAS","shape":7,"link":null},{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":null}],"outputs":[{"name":"options","localized_name":"options","type":"OPTIONS","links":[3707],"slot_index":0}],"properties":{"Node name for S&R":"ClownOptions_SDE_Beta"},"widgets_values":["gaussian","gaussian","hard","hard",0.5,0.75,-1,"fixed"]},{"id":1381,"type":"Note","pos":[13881.6279296875,-217.62835693359375],"size":[261.9539489746094,88],"flags":{},"order":5,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["Increase or decrease \"steps_to_run\" in ClownsharKSampler to change the effective denoise level."],"color":"#432","bgcolor":"#653"},{"id":1382,"type":"Note","pos":[14718.0498046875,-295.4144592285156],"size":[268.1851806640625,124.49711608886719],"flags":{},"order":6,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["Increasing cycles will increase the amount of change, but take longer.\n\nCycles will rerun the same step over and over, forwards and backwards, iteratively refining an image at a controlled noise level."],"color":"#432","bgcolor":"#653"},{"id":1387,"type":"ReFluxPatcher","pos":[13262.294921875,-130.79653930664062],"size":[210,82],"flags":{},"order":15,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","link":3736}],"outputs":[{"name":"model","localized_name":"model","type":"MODEL","links":[3737],"slot_index":0}],"properties":{"Node name for S&R":"ReFluxPatcher"},"widgets_values":["float64",true]},{"id":1386,"type":"UnetLoaderGGUF","pos":[12817.208984375,-323.9640808105469],"size":[315,58],"flags":{},"order":7,"mode":0,"inputs":[],"outputs":[{"name":"MODEL","localized_name":"MODEL","type":"MODEL","links":[3736],"slot_index":0}],"properties":{"Node name for S&R":"UnetLoaderGGUF"},"widgets_values":["flux1-dev-Q4_K_S.gguf"]},{"id":1389,"type":"VAELoader","pos":[12824.330078125,-56.021827697753906],"size":[315,58],"flags":{},"order":8,"mode":0,"inputs":[],"outputs":[{"name":"VAE","localized_name":"VAE","type":"VAE","links":[3739],"slot_index":0}],"properties":{"Node name for S&R":"VAELoader"},"widgets_values":["ae.sft"]},{"id":980,"type":"ClownsharkChainsampler_Beta","pos":[14378.255859375,-64.39308166503906],"size":[340.20001220703125,570],"flags":{},"order":31,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","shape":7,"link":null},{"name":"positive","localized_name":"positive","type":"CONDITIONING","shape":7,"link":3626},{"name":"negative","localized_name":"negative","type":"CONDITIONING","shape":7,"link":3627},{"name":"sigmas","localized_name":"sigmas","type":"SIGMAS","shape":7,"link":null},{"name":"latent_image","localized_name":"latent_image","type":"LATENT","shape":7,"link":3578},{"name":"guides","localized_name":"guides","type":"GUIDES","shape":7,"link":3604},{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":3533},{"name":"options 2","type":"OPTIONS","link":3707},{"name":"options 3","type":"OPTIONS","link":null}],"outputs":[{"name":"output","localized_name":"output","type":"LATENT","links":[3698],"slot_index":0},{"name":"denoised","localized_name":"denoised","type":"LATENT","links":null},{"name":"options","localized_name":"options","type":"OPTIONS","links":null}],"properties":{"Node name for S&R":"ClownsharkChainsampler_Beta"},"widgets_values":[0.5,"exponential/res_2s",1,1,"resample",true]},{"id":981,"type":"ClownsharkChainsampler_Beta","pos":[14758.255859375,-64.39308166503906],"size":[340.20001220703125,510],"flags":{},"order":32,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","shape":7,"link":null},{"name":"positive","localized_name":"positive","type":"CONDITIONING","shape":7,"link":null},{"name":"negative","localized_name":"negative","type":"CONDITIONING","shape":7,"link":null},{"name":"sigmas","localized_name":"sigmas","type":"SIGMAS","shape":7,"link":null},{"name":"latent_image","localized_name":"latent_image","type":"LATENT","shape":7,"link":3698},{"name":"guides","localized_name":"guides","type":"GUIDES","shape":7,"link":null},{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":null}],"outputs":[{"name":"output","localized_name":"output","type":"LATENT","links":[3469],"slot_index":0},{"name":"denoised","localized_name":"denoised","type":"LATENT","links":null},{"name":"options","localized_name":"options","type":"OPTIONS","links":null}],"properties":{"Node name for S&R":"ClownsharkChainsampler_Beta"},"widgets_values":[0.5,"exponential/res_2s",-1,1,"resample",true]},{"id":1388,"type":"DualCLIPLoaderGGUF","pos":[12819.8798828125,-213.58253479003906],"size":[315,106],"flags":{},"order":9,"mode":0,"inputs":[],"outputs":[{"name":"CLIP","localized_name":"CLIP","type":"CLIP","links":[3738],"slot_index":0}],"properties":{"Node name for S&R":"DualCLIPLoaderGGUF"},"widgets_values":["clip_l_flux.safetensors","t5xxl_fp16.safetensors","flux"]},{"id":907,"type":"ClownsharKSampler_Beta","pos":[14008.255859375,-64.39308166503906],"size":[340.55120849609375,666.8208618164062],"flags":{},"order":30,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","shape":7,"link":2692},{"name":"positive","localized_name":"positive","type":"CONDITIONING","shape":7,"link":3602},{"name":"negative","localized_name":"negative","type":"CONDITIONING","shape":7,"link":2882},{"name":"latent_image","localized_name":"latent_image","type":"LATENT","shape":7,"link":2983},{"name":"sigmas","localized_name":"sigmas","type":"SIGMAS","shape":7,"link":null},{"name":"guides","localized_name":"guides","type":"GUIDES","shape":7,"link":3708},{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":null}],"outputs":[{"name":"output","localized_name":"output","type":"LATENT","links":[3578],"slot_index":0},{"name":"denoised","localized_name":"denoised","type":"LATENT","links":null},{"name":"options","localized_name":"options","type":"OPTIONS","links":null}],"properties":{"Node name for S&R":"ClownsharKSampler_Beta","cnr_id":"RES4LYF","ver":"5ce9b5a77c227bf864e447a1e65305bf6cada5c2"},"widgets_values":[0.5,"multistep/res_2m","beta57",20,14,1,1,201,"fixed","unsample",true]},{"id":1333,"type":"CLIPTextEncode","pos":[13688.255859375,-44.393089294433594],"size":[280.6252746582031,164.06936645507812],"flags":{"collapsed":false},"order":22,"mode":0,"inputs":[{"name":"clip","localized_name":"clip","type":"CLIP","link":3581}],"outputs":[{"name":"CONDITIONING","localized_name":"CONDITIONING","type":"CONDITIONING","links":[3602,3626],"slot_index":0}],"properties":{"Node name for S&R":"CLIPTextEncode","cnr_id":"comfy-core","ver":"0.3.29"},"widgets_values":["black and white anime cartoon of the inside of a car driving down a creepy road"]},{"id":1374,"type":"LoadImage","pos":[12805.896484375,167.56053161621094],"size":[315,314],"flags":{},"order":10,"mode":0,"inputs":[],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":[3725],"slot_index":0},{"name":"MASK","localized_name":"MASK","type":"MASK","links":null}],"title":"Load Image (Style Guide)","properties":{"Node name for S&R":"LoadImage"},"widgets_values":["ComfyUI_14651_.png","image"]},{"id":1373,"type":"LoadImage","pos":[12810.2314453125,534.0346069335938],"size":[315,314],"flags":{},"order":11,"mode":0,"inputs":[],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":[3721],"slot_index":0},{"name":"MASK","localized_name":"MASK","type":"MASK","links":null}],"title":"Load Image (Composition)","properties":{"Node name for S&R":"LoadImage"},"widgets_values":["pasted/image (476).png","image"]},{"id":1362,"type":"PreviewImage","pos":[13380,620],"size":[210,246],"flags":{},"order":25,"mode":0,"inputs":[{"name":"images","localized_name":"images","type":"IMAGE","link":3682}],"outputs":[],"properties":{"Node name for S&R":"PreviewImage"},"widgets_values":[]},{"id":1390,"type":"Note","pos":[13148.0439453125,257.643310546875],"size":[210,88],"flags":{},"order":12,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["Color Match SOMETIMES helps accelerate style transfer.\n"],"color":"#432","bgcolor":"#653"},{"id":1318,"type":"ClownGuide_Beta","pos":[13828.255859375,675.60693359375],"size":[263.102783203125,290],"flags":{},"order":27,"mode":4,"inputs":[{"name":"guide","localized_name":"guide","type":"LATENT","shape":7,"link":3710},{"name":"mask","localized_name":"mask","type":"MASK","shape":7,"link":null},{"name":"weights","localized_name":"weights","type":"SIGMAS","shape":7,"link":null}],"outputs":[{"name":"guides","localized_name":"guides","type":"GUIDES","links":[3699,3708],"slot_index":0}],"properties":{"Node name for S&R":"ClownGuide_Beta"},"widgets_values":["inversion",false,false,0.7,1,"constant",0,-1,false]},{"id":1376,"type":"Note","pos":[13710.3271484375,473.56817626953125],"size":[265.1909484863281,137.36415100097656],"flags":{},"order":13,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["Increase or decrease weight in ClownGuide to alter adherence to the input image.\n\nFor now, set to low weights or bypass if using any model except HiDream. The HiDream code was adapted so that this composition guide doesn't fight the style guide. Others will be added soon."],"color":"#432","bgcolor":"#653"},{"id":1317,"type":"ClownOptions_Cycles_Beta","pos":[14418.0478515625,-325.06365966796875],"size":[265.2884826660156,202],"flags":{},"order":14,"mode":0,"inputs":[{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":null}],"outputs":[{"name":"options","localized_name":"options","type":"OPTIONS","links":[3533],"slot_index":0}],"properties":{"Node name for S&R":"ClownOptions_Cycles_Beta"},"widgets_values":[10,1,-1,"none",-1,1,false]},{"id":1350,"type":"ColorMatch","pos":[13380,160],"size":[210,102],"flags":{"collapsed":false},"order":24,"mode":0,"inputs":[{"name":"image_ref","localized_name":"image_ref","type":"IMAGE","link":3728},{"name":"image_target","localized_name":"image_target","type":"IMAGE","link":3724}],"outputs":[{"name":"image","localized_name":"image","type":"IMAGE","links":[3682,3688],"slot_index":0}],"properties":{"Node name for S&R":"ColorMatch"},"widgets_values":["mkl",0]}],"links":[[18,14,0,7,4,"VAE"],[1395,13,0,431,0,"MODEL"],[1398,7,3,431,1,"LATENT"],[2692,431,0,907,0,"MODEL"],[2696,14,0,908,1,"VAE"],[2697,908,0,909,0,"IMAGE"],[2881,490,0,970,0,"CLIP"],[2882,970,0,907,2,"CONDITIONING"],[2983,7,0,907,3,"LATENT"],[3469,981,0,908,0,"LATENT"],[3533,1317,0,980,6,"OPTIONS"],[3578,907,0,980,4,"LATENT"],[3581,490,0,1333,0,"CLIP"],[3602,1333,0,907,1,"CONDITIONING"],[3604,1308,0,980,5,"GUIDES"],[3626,1333,0,980,1,"CONDITIONING"],[3627,970,0,980,2,"CONDITIONING"],[3682,1350,0,1362,0,"IMAGE"],[3688,1350,0,7,0,"IMAGE"],[3698,980,0,981,4,"LATENT"],[3699,1318,0,1308,3,"GUIDES"],[3707,1328,0,980,7,"OPTIONS"],[3708,1318,0,907,5,"GUIDES"],[3709,7,1,1308,0,"LATENT"],[3710,7,0,1318,0,"LATENT"],[3720,908,0,1377,0,"IMAGE"],[3721,1373,0,1378,0,"*"],[3724,1378,0,1350,1,"IMAGE"],[3725,1374,0,1379,0,"*"],[3726,1379,0,1371,0,"IMAGE"],[3727,1371,0,7,1,"IMAGE"],[3728,1371,0,1350,0,"IMAGE"],[3729,1378,0,1377,1,"IMAGE"],[3730,1380,0,1371,1,"INT"],[3731,1380,1,1371,2,"INT"],[3732,1380,0,7,5,"INT"],[3733,1380,1,7,6,"INT"],[3736,1386,0,1387,0,"MODEL"],[3737,1387,0,13,0,"*"],[3738,1388,0,490,0,"*"],[3739,1389,0,14,0,"*"]],"groups":[{"id":1,"title":"Model Loaders","bounding":[12796.72265625,-401.9004211425781,822.762451171875,436.0693359375],"color":"#3f789e","font_size":24,"flags":{}},{"id":2,"title":"Sampling","bounding":[13652.6533203125,-402.70721435546875,1470.8076171875,1409.0289306640625],"color":"#3f789e","font_size":24,"flags":{}},{"id":3,"title":"Input Prep","bounding":[12797.1396484375,77.69412231445312,817.4218139648438,820.6239624023438],"color":"#3f789e","font_size":24,"flags":{}},{"id":4,"title":"Save and Compare","bounding":[15180.705078125,-399.09112548828125,1050.6468505859375,615.8845825195312],"color":"#3f789e","font_size":24,"flags":{}}],"config":{},"extra":{"ds":{"scale":1.4379222522564015,"offset":[-11124.689104031433,546.0824398349012]},"VHS_latentpreview":false,"VHS_latentpreviewrate":0,"ue_links":[],"VHS_MetadataImage":true,"VHS_KeepIntermediate":true},"version":0.4} \ No newline at end of file diff --git a/ComfyUI/custom_nodes/RES4LYF/example_workflows/flux upscale thumbnail large multistage.json b/ComfyUI/custom_nodes/RES4LYF/example_workflows/flux upscale thumbnail large multistage.json new file mode 100644 index 0000000000000000000000000000000000000000..914e5c60b935c0637fc4d84e91b6eb02d67e393c --- /dev/null +++ b/ComfyUI/custom_nodes/RES4LYF/example_workflows/flux upscale thumbnail large multistage.json @@ -0,0 +1 @@ +{"last_node_id":431,"last_link_id":1176,"nodes":[{"id":361,"type":"CLIPVisionEncode","pos":[860,820],"size":[253.60000610351562,78],"flags":{},"order":17,"mode":0,"inputs":[{"name":"clip_vision","localized_name":"clip_vision","type":"CLIP_VISION","link":1004},{"name":"image","localized_name":"image","type":"IMAGE","link":1107}],"outputs":[{"name":"CLIP_VISION_OUTPUT","localized_name":"CLIP_VISION_OUTPUT","type":"CLIP_VISION_OUTPUT","links":[1006],"slot_index":0}],"properties":{"Node name for S&R":"CLIPVisionEncode"},"widgets_values":["center"]},{"id":364,"type":"CLIPTextEncode","pos":[899.5093383789062,952.8309936523438],"size":[210,88],"flags":{},"order":14,"mode":0,"inputs":[{"name":"clip","localized_name":"clip","type":"CLIP","link":1007}],"outputs":[{"name":"CONDITIONING","localized_name":"CONDITIONING","type":"CONDITIONING","links":[1008,1055],"slot_index":0}],"properties":{"Node name for S&R":"CLIPTextEncode"},"widgets_values":[""]},{"id":369,"type":"ClownGuide_Style_Beta","pos":[1138.06640625,1574.328857421875],"size":[231.30213928222656,286],"flags":{},"order":25,"mode":0,"inputs":[{"name":"guide","localized_name":"guide","type":"LATENT","shape":7,"link":1101},{"name":"mask","localized_name":"mask","type":"MASK","shape":7,"link":null},{"name":"weights","localized_name":"weights","type":"SIGMAS","shape":7,"link":null},{"name":"guides","localized_name":"guides","type":"GUIDES","shape":7,"link":null}],"outputs":[{"name":"guides","localized_name":"guides","type":"GUIDES","links":[1099],"slot_index":0}],"properties":{"Node name for S&R":"ClownGuide_Style_Beta"},"widgets_values":["positive","WCT",1,1,"constant",0,-1,false]},{"id":374,"type":"ClownsharkChainsampler_Beta","pos":[2403.98583984375,1081.333740234375],"size":[274.9878234863281,528.6721801757812],"flags":{},"order":30,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","shape":7,"link":null},{"name":"positive","localized_name":"positive","type":"CONDITIONING","shape":7,"link":1134},{"name":"negative","localized_name":"negative","type":"CONDITIONING","shape":7,"link":null},{"name":"sigmas","localized_name":"sigmas","type":"SIGMAS","shape":7,"link":null},{"name":"latent_image","localized_name":"latent_image","type":"LATENT","shape":7,"link":1097},{"name":"guides","localized_name":"guides","type":"GUIDES","shape":7,"link":null},{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":null}],"outputs":[{"name":"output","localized_name":"output","type":"LATENT","links":[1088],"slot_index":0},{"name":"denoised","localized_name":"denoised","type":"LATENT","links":null},{"name":"options","localized_name":"options","type":"OPTIONS","links":null}],"properties":{"Node name for S&R":"ClownsharkChainsampler_Beta"},"widgets_values":[0.5,"multistep/res_3m",-1,1,"resample",true]},{"id":372,"type":"SaveImage","pos":[2740,1080],"size":[442.38494873046875,530.0809936523438],"flags":{},"order":32,"mode":0,"inputs":[{"name":"images","localized_name":"images","type":"IMAGE","link":1030}],"outputs":[],"properties":{},"widgets_values":["ComfyUI"]},{"id":355,"type":"ModelSamplingAdvancedResolution","pos":[1134.0809326171875,1057.9874267578125],"size":[260.3999938964844,126],"flags":{},"order":24,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","link":1047},{"name":"latent_image","localized_name":"latent_image","type":"LATENT","link":1111}],"outputs":[{"name":"model","localized_name":"model","type":"MODEL","links":[1024],"slot_index":0}],"properties":{"Node name for S&R":"ModelSamplingAdvancedResolution"},"widgets_values":["exponential",1.35,0.85]},{"id":368,"type":"ReFluxPatcher","pos":[897.4150390625,1095.9840087890625],"size":[210,82],"flags":{},"order":12,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","link":1022}],"outputs":[{"name":"model","localized_name":"model","type":"MODEL","links":[1047],"slot_index":0}],"properties":{"Node name for S&R":"ReFluxPatcher"},"widgets_values":["float32",true]},{"id":349,"type":"FluxLoader","pos":[554.6767578125,1099.277099609375],"size":[315,282],"flags":{},"order":0,"mode":0,"inputs":[],"outputs":[{"name":"model","localized_name":"model","type":"MODEL","links":[1022,1144],"slot_index":0},{"name":"clip","localized_name":"clip","type":"CLIP","links":[1007,1137],"slot_index":1},{"name":"vae","localized_name":"vae","type":"VAE","links":[1029,1038,1058,1155,1164,1168],"slot_index":2},{"name":"clip_vision","localized_name":"clip_vision","type":"CLIP_VISION","links":[1004,1135],"slot_index":3},{"name":"style_model","localized_name":"style_model","type":"STYLE_MODEL","links":[1009,1172]}],"properties":{"Node name for S&R":"FluxLoader"},"widgets_values":["colossusProjectFlux_v42AIO.safetensors","default",".use_ckpt_clip",".none",".use_ckpt_vae","sigclip_vision_patch14_384.safetensors","flux1-redux-dev.safetensors"]},{"id":373,"type":"ClownsharkChainsampler_Beta","pos":[1740,1080],"size":[272.9876403808594,526.665771484375],"flags":{},"order":28,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","shape":7,"link":null},{"name":"positive","localized_name":"positive","type":"CONDITIONING","shape":7,"link":1118},{"name":"negative","localized_name":"negative","type":"CONDITIONING","shape":7,"link":null},{"name":"sigmas","localized_name":"sigmas","type":"SIGMAS","shape":7,"link":null},{"name":"latent_image","localized_name":"latent_image","type":"LATENT","shape":7,"link":1031},{"name":"guides","localized_name":"guides","type":"GUIDES","shape":7,"link":1099},{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":1044},{"name":"options 2","type":"OPTIONS","link":null}],"outputs":[{"name":"output","localized_name":"output","type":"LATENT","links":[1053],"slot_index":0},{"name":"denoised","localized_name":"denoised","type":"LATENT","links":null},{"name":"options","localized_name":"options","type":"OPTIONS","links":null}],"properties":{"Node name for S&R":"ClownsharkChainsampler_Beta"},"widgets_values":[0.5,"multistep/res_3m",1,1,"resample",true]},{"id":370,"type":"ClownsharKSampler_Beta","pos":[1417.3414306640625,1078.0023193359375],"size":[277.65570068359375,627.99951171875],"flags":{},"order":27,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","shape":7,"link":1024},{"name":"positive","localized_name":"positive","type":"CONDITIONING","shape":7,"link":1117},{"name":"negative","localized_name":"negative","type":"CONDITIONING","shape":7,"link":null},{"name":"latent_image","localized_name":"latent_image","type":"LATENT","shape":7,"link":1102},{"name":"sigmas","localized_name":"sigmas","type":"SIGMAS","shape":7,"link":null},{"name":"guides","localized_name":"guides","type":"GUIDES","shape":7,"link":null},{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":null}],"outputs":[{"name":"output","localized_name":"output","type":"LATENT","links":[1031],"slot_index":0},{"name":"denoised","localized_name":"denoised","type":"LATENT","links":null},{"name":"options","localized_name":"options","type":"OPTIONS","links":null}],"properties":{"Node name for S&R":"ClownsharKSampler_Beta"},"widgets_values":[0.5,"multistep/res_3m","beta57",30,14,1,1,0,"fixed","unsample",true]},{"id":380,"type":"ClownsharkChainsampler_Beta","pos":[2078.66015625,1080.6669921875],"size":[263.6514892578125,527.99951171875],"flags":{},"order":29,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","shape":7,"link":null},{"name":"positive","localized_name":"positive","type":"CONDITIONING","shape":7,"link":null},{"name":"negative","localized_name":"negative","type":"CONDITIONING","shape":7,"link":null},{"name":"sigmas","localized_name":"sigmas","type":"SIGMAS","shape":7,"link":null},{"name":"latent_image","localized_name":"latent_image","type":"LATENT","shape":7,"link":1053},{"name":"guides","localized_name":"guides","type":"GUIDES","shape":7,"link":null},{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":1051},{"name":"options 2","type":"OPTIONS","link":null}],"outputs":[{"name":"output","localized_name":"output","type":"LATENT","links":[1097],"slot_index":0},{"name":"denoised","localized_name":"denoised","type":"LATENT","links":null},{"name":"options","localized_name":"options","type":"OPTIONS","links":null}],"properties":{"Node name for S&R":"ClownsharkChainsampler_Beta"},"widgets_values":[0.5,"multistep/res_3m",1,1,"resample",true]},{"id":403,"type":"Note","pos":[2098.053466796875,680.7237548828125],"size":[215.7804412841797,88],"flags":{},"order":1,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["Raise cycles here if you see halos. It doesn't hurt to go as high as 20. (About 20 seconds on a 4090 at 1024x1024)."],"color":"#432","bgcolor":"#653"},{"id":402,"type":"Note","pos":[1755.3779296875,678.1484985351562],"size":[241.524658203125,132.7487030029297],"flags":{},"order":2,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["Lower cycles here if you see halos.\n\nThese step(s)/cycle(s) (that use the ClownGuide Style node) are needed to prevent blurring when upscaling tiny thumbnail images."],"color":"#432","bgcolor":"#653"},{"id":382,"type":"ControlNetApplyAdvanced","pos":[1440,830],"size":[210,186],"flags":{},"order":23,"mode":0,"inputs":[{"name":"positive","localized_name":"positive","type":"CONDITIONING","link":1108},{"name":"negative","localized_name":"negative","type":"CONDITIONING","link":1055},{"name":"control_net","localized_name":"control_net","type":"CONTROL_NET","link":1056},{"name":"image","localized_name":"image","type":"IMAGE","link":1112},{"name":"vae","localized_name":"vae","type":"VAE","shape":7,"link":1058}],"outputs":[{"name":"positive","localized_name":"positive","type":"CONDITIONING","links":[1118],"slot_index":0},{"name":"negative","localized_name":"negative","type":"CONDITIONING","links":null}],"properties":{"Node name for S&R":"ControlNetApplyAdvanced"},"widgets_values":[1,0,1]},{"id":404,"type":"Image Repeat Tile To Size","pos":[899.620361328125,1259.9044189453125],"size":[210,106],"flags":{},"order":18,"mode":0,"inputs":[{"name":"image","localized_name":"image","type":"IMAGE","link":1123}],"outputs":[{"name":"image","localized_name":"image","type":"IMAGE","links":[1124],"slot_index":0}],"properties":{"Node name for S&R":"Image Repeat Tile To Size"},"widgets_values":[1024,1024,false]},{"id":375,"type":"VAEEncodeAdvanced","pos":[1140,1240],"size":[228.90342712402344,278],"flags":{},"order":21,"mode":0,"inputs":[{"name":"image_1","localized_name":"image_1","type":"IMAGE","shape":7,"link":1113},{"name":"image_2","localized_name":"image_2","type":"IMAGE","shape":7,"link":1124},{"name":"mask","localized_name":"mask","type":"IMAGE","shape":7,"link":null},{"name":"latent","localized_name":"latent","type":"LATENT","shape":7,"link":null},{"name":"vae","localized_name":"vae","type":"VAE","shape":7,"link":1038}],"outputs":[{"name":"latent_1","localized_name":"latent_1","type":"LATENT","links":[1102,1111],"slot_index":0},{"name":"latent_2","localized_name":"latent_2","type":"LATENT","links":[1101],"slot_index":1},{"name":"mask","localized_name":"mask","type":"MASK","links":null},{"name":"empty_latent","localized_name":"empty_latent","type":"LATENT","links":null,"slot_index":3},{"name":"width","localized_name":"width","type":"INT","links":null},{"name":"height","localized_name":"height","type":"INT","links":null}],"properties":{"Node name for S&R":"VAEEncodeAdvanced"},"widgets_values":["false",1024,1024,"red",false,"16_channels"]},{"id":401,"type":"LoadImage","pos":[608.10400390625,1453.0382080078125],"size":[315,314],"flags":{},"order":3,"mode":0,"inputs":[],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":[1122],"slot_index":0},{"name":"MASK","localized_name":"MASK","type":"MASK","links":null}],"properties":{"Node name for S&R":"LoadImage"},"widgets_values":["pasted/image (579).png","image"]},{"id":359,"type":"ControlNetLoader","pos":[596.1650390625,977.5371704101562],"size":[270.0880432128906,58],"flags":{},"order":4,"mode":0,"inputs":[],"outputs":[{"name":"CONTROL_NET","localized_name":"CONTROL_NET","type":"CONTROL_NET","links":[1056,1162],"slot_index":0}],"properties":{"Node name for S&R":"ControlNetLoader"},"widgets_values":["flux_tile.safetensors"]},{"id":362,"type":"StyleModelApply","pos":[1138.0474853515625,827.8412475585938],"size":[270.06890869140625,122],"flags":{},"order":20,"mode":0,"inputs":[{"name":"conditioning","localized_name":"conditioning","type":"CONDITIONING","link":1008},{"name":"style_model","localized_name":"style_model","type":"STYLE_MODEL","link":1009},{"name":"clip_vision_output","localized_name":"clip_vision_output","type":"CLIP_VISION_OUTPUT","link":1006}],"outputs":[{"name":"CONDITIONING","localized_name":"CONDITIONING","type":"CONDITIONING","links":[1108,1117,1134],"slot_index":0}],"properties":{"Node name for S&R":"StyleModelApply"},"widgets_values":[1,"multiply"]},{"id":408,"type":"CLIPVisionEncode","pos":[3300,810],"size":[253.60000610351562,78],"flags":{},"order":19,"mode":0,"inputs":[{"name":"clip_vision","localized_name":"clip_vision","type":"CLIP_VISION","link":1135},{"name":"image","localized_name":"image","type":"IMAGE","link":1176}],"outputs":[{"name":"CLIP_VISION_OUTPUT","localized_name":"CLIP_VISION_OUTPUT","type":"CLIP_VISION_OUTPUT","links":[1173],"slot_index":0}],"properties":{"Node name for S&R":"CLIPVisionEncode"},"widgets_values":["center"]},{"id":409,"type":"CLIPTextEncode","pos":[3340,940],"size":[210,88],"flags":{},"order":15,"mode":0,"inputs":[{"name":"clip","localized_name":"clip","type":"CLIP","link":1137}],"outputs":[{"name":"CONDITIONING","localized_name":"CONDITIONING","type":"CONDITIONING","links":[1161,1171],"slot_index":0}],"properties":{"Node name for S&R":"CLIPTextEncode"},"widgets_values":[""]},{"id":410,"type":"ClownGuide_Style_Beta","pos":[3570,1560],"size":[231.30213928222656,286],"flags":{},"order":38,"mode":0,"inputs":[{"name":"guide","localized_name":"guide","type":"LATENT","shape":7,"link":1138},{"name":"mask","localized_name":"mask","type":"MASK","shape":7,"link":null},{"name":"weights","localized_name":"weights","type":"SIGMAS","shape":7,"link":null},{"name":"guides","localized_name":"guides","type":"GUIDES","shape":7,"link":null}],"outputs":[{"name":"guides","localized_name":"guides","type":"GUIDES","links":[1147],"slot_index":0}],"properties":{"Node name for S&R":"ClownGuide_Style_Beta"},"widgets_values":["positive","WCT",1,1,"constant",0,-1,false]},{"id":411,"type":"ClownsharkChainsampler_Beta","pos":[4840,1070],"size":[274.9878234863281,528.6721801757812],"flags":{},"order":42,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","shape":7,"link":null},{"name":"positive","localized_name":"positive","type":"CONDITIONING","shape":7,"link":1139},{"name":"negative","localized_name":"negative","type":"CONDITIONING","shape":7,"link":null},{"name":"sigmas","localized_name":"sigmas","type":"SIGMAS","shape":7,"link":null},{"name":"latent_image","localized_name":"latent_image","type":"LATENT","shape":7,"link":1140},{"name":"guides","localized_name":"guides","type":"GUIDES","shape":7,"link":null},{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":null}],"outputs":[{"name":"output","localized_name":"output","type":"LATENT","links":[1154],"slot_index":0},{"name":"denoised","localized_name":"denoised","type":"LATENT","links":null},{"name":"options","localized_name":"options","type":"OPTIONS","links":null}],"properties":{"Node name for S&R":"ClownsharkChainsampler_Beta"},"widgets_values":[0.5,"multistep/res_3m",-1,1,"resample",true]},{"id":412,"type":"SaveImage","pos":[5180,1070],"size":[442.38494873046875,530.0809936523438],"flags":{},"order":44,"mode":0,"inputs":[{"name":"images","localized_name":"images","type":"IMAGE","link":1141}],"outputs":[],"properties":{},"widgets_values":["ComfyUI"]},{"id":413,"type":"ModelSamplingAdvancedResolution","pos":[3570,1050],"size":[260.3999938964844,126],"flags":{},"order":37,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","link":1142},{"name":"latent_image","localized_name":"latent_image","type":"LATENT","link":1143}],"outputs":[{"name":"model","localized_name":"model","type":"MODEL","links":[1149],"slot_index":0}],"properties":{"Node name for S&R":"ModelSamplingAdvancedResolution"},"widgets_values":["exponential",1.35,0.85]},{"id":414,"type":"ReFluxPatcher","pos":[3330,1080],"size":[210,82],"flags":{},"order":13,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","link":1144}],"outputs":[{"name":"model","localized_name":"model","type":"MODEL","links":[1142],"slot_index":0}],"properties":{"Node name for S&R":"ReFluxPatcher"},"widgets_values":["float32",true]},{"id":415,"type":"ClownsharkChainsampler_Beta","pos":[4180,1070],"size":[272.9876403808594,526.665771484375],"flags":{},"order":40,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","shape":7,"link":null},{"name":"positive","localized_name":"positive","type":"CONDITIONING","shape":7,"link":1145},{"name":"negative","localized_name":"negative","type":"CONDITIONING","shape":7,"link":null},{"name":"sigmas","localized_name":"sigmas","type":"SIGMAS","shape":7,"link":null},{"name":"latent_image","localized_name":"latent_image","type":"LATENT","shape":7,"link":1146},{"name":"guides","localized_name":"guides","type":"GUIDES","shape":7,"link":1147},{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":1148},{"name":"options 2","type":"OPTIONS","link":null}],"outputs":[{"name":"output","localized_name":"output","type":"LATENT","links":[1152],"slot_index":0},{"name":"denoised","localized_name":"denoised","type":"LATENT","links":null},{"name":"options","localized_name":"options","type":"OPTIONS","links":null}],"properties":{"Node name for S&R":"ClownsharkChainsampler_Beta"},"widgets_values":[0.5,"multistep/res_3m",1,1,"resample",true]},{"id":417,"type":"ClownsharkChainsampler_Beta","pos":[4510,1070],"size":[263.6514892578125,527.99951171875],"flags":{},"order":41,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","shape":7,"link":null},{"name":"positive","localized_name":"positive","type":"CONDITIONING","shape":7,"link":null},{"name":"negative","localized_name":"negative","type":"CONDITIONING","shape":7,"link":null},{"name":"sigmas","localized_name":"sigmas","type":"SIGMAS","shape":7,"link":null},{"name":"latent_image","localized_name":"latent_image","type":"LATENT","shape":7,"link":1152},{"name":"guides","localized_name":"guides","type":"GUIDES","shape":7,"link":null},{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":1153},{"name":"options 2","type":"OPTIONS","link":null}],"outputs":[{"name":"output","localized_name":"output","type":"LATENT","links":[1140],"slot_index":0},{"name":"denoised","localized_name":"denoised","type":"LATENT","links":null},{"name":"options","localized_name":"options","type":"OPTIONS","links":null}],"properties":{"Node name for S&R":"ClownsharkChainsampler_Beta"},"widgets_values":[0.5,"multistep/res_3m",1,1,"resample",true]},{"id":418,"type":"Note","pos":[4530,670],"size":[215.7804412841797,88],"flags":{},"order":5,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["Raise cycles here if you see halos. It doesn't hurt to go as high as 20. (About 20 seconds on a 4090 at 1024x1024)."],"color":"#432","bgcolor":"#653"},{"id":419,"type":"Note","pos":[4190,670],"size":[241.524658203125,132.7487030029297],"flags":{},"order":6,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["Lower cycles here if you see halos.\n\nThese step(s)/cycle(s) (that use the ClownGuide Style node) are needed to prevent blurring when upscaling tiny thumbnail images."],"color":"#432","bgcolor":"#653"},{"id":420,"type":"VAEDecode","pos":[5180,960],"size":[140,46],"flags":{},"order":43,"mode":0,"inputs":[{"name":"samples","localized_name":"samples","type":"LATENT","link":1154},{"name":"vae","localized_name":"vae","type":"VAE","link":1155}],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":[1141,1169],"slot_index":0}],"properties":{"Node name for S&R":"VAEDecode","cnr_id":"comfy-core","ver":"0.3.26","widget_ue_connectable":{}},"widgets_values":[]},{"id":421,"type":"Reroute","pos":[3470,1450],"size":[75,26],"flags":{},"order":34,"mode":0,"inputs":[{"name":"","type":"*","link":1174}],"outputs":[{"name":"","type":"IMAGE","links":[1165,1166,1170]}],"properties":{"showOutputText":false,"horizontal":false}},{"id":425,"type":"ControlNetApplyAdvanced","pos":[3880,820],"size":[210,186],"flags":{},"order":26,"mode":0,"inputs":[{"name":"positive","localized_name":"positive","type":"CONDITIONING","link":1160},{"name":"negative","localized_name":"negative","type":"CONDITIONING","link":1161},{"name":"control_net","localized_name":"control_net","type":"CONTROL_NET","link":1162},{"name":"image","localized_name":"image","type":"IMAGE","link":1175},{"name":"vae","localized_name":"vae","type":"VAE","shape":7,"link":1164}],"outputs":[{"name":"positive","localized_name":"positive","type":"CONDITIONING","links":[1145],"slot_index":0},{"name":"negative","localized_name":"negative","type":"CONDITIONING","links":null}],"properties":{"Node name for S&R":"ControlNetApplyAdvanced"},"widgets_values":[1,0,1]},{"id":429,"type":"Image Comparer (rgthree)","pos":[5170,1650],"size":[446.2193603515625,494.8704528808594],"flags":{},"order":45,"mode":0,"inputs":[{"name":"image_a","type":"IMAGE","dir":3,"link":1169},{"name":"image_b","type":"IMAGE","dir":3,"link":1170}],"outputs":[],"properties":{"comparer_mode":"Slide"},"widgets_values":[[{"name":"A","selected":true,"url":"/api/view?filename=rgthree.compare._temp_txgkm_00005_.png&type=temp&subfolder=&rand=0.44944358112719196"},{"name":"B","selected":true,"url":"/api/view?filename=rgthree.compare._temp_txgkm_00006_.png&type=temp&subfolder=&rand=0.15903319456700227"}]]},{"id":430,"type":"StyleModelApply","pos":[3570,820],"size":[270.06890869140625,122],"flags":{},"order":22,"mode":0,"inputs":[{"name":"conditioning","localized_name":"conditioning","type":"CONDITIONING","link":1171},{"name":"style_model","localized_name":"style_model","type":"STYLE_MODEL","link":1172},{"name":"clip_vision_output","localized_name":"clip_vision_output","type":"CLIP_VISION_OUTPUT","link":1173}],"outputs":[{"name":"CONDITIONING","localized_name":"CONDITIONING","type":"CONDITIONING","links":[1139,1150,1160],"slot_index":0}],"properties":{"Node name for S&R":"StyleModelApply"},"widgets_values":[1,"multiply"]},{"id":387,"type":"Image Comparer (rgthree)","pos":[2732.6875,1661.954833984375],"size":[446.2193603515625,494.8704528808594],"flags":{},"order":33,"mode":0,"inputs":[{"name":"image_a","type":"IMAGE","dir":3,"link":1068},{"name":"image_b","type":"IMAGE","dir":3,"link":1115}],"outputs":[],"properties":{"comparer_mode":"Slide"},"widgets_values":[[{"name":"A","selected":true,"url":"/api/view?filename=rgthree.compare._temp_lvxiv_00017_.png&type=temp&subfolder=&rand=0.23193425033461956"},{"name":"B","selected":true,"url":"/api/view?filename=rgthree.compare._temp_lvxiv_00018_.png&type=temp&subfolder=&rand=0.4600603671403143"}]]},{"id":416,"type":"ClownsharKSampler_Beta","pos":[3850,1070],"size":[277.65570068359375,627.99951171875],"flags":{},"order":39,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","shape":7,"link":1149},{"name":"positive","localized_name":"positive","type":"CONDITIONING","shape":7,"link":1150},{"name":"negative","localized_name":"negative","type":"CONDITIONING","shape":7,"link":null},{"name":"latent_image","localized_name":"latent_image","type":"LATENT","shape":7,"link":1151},{"name":"sigmas","localized_name":"sigmas","type":"SIGMAS","shape":7,"link":null},{"name":"guides","localized_name":"guides","type":"GUIDES","shape":7,"link":null},{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":null}],"outputs":[{"name":"output","localized_name":"output","type":"LATENT","links":[1146],"slot_index":0},{"name":"denoised","localized_name":"denoised","type":"LATENT","links":null},{"name":"options","localized_name":"options","type":"OPTIONS","links":null}],"properties":{"Node name for S&R":"ClownsharKSampler_Beta"},"widgets_values":[0.5,"multistep/res_3m","beta57",30,14,1,1,0,"fixed","unsample",true]},{"id":427,"type":"Image Repeat Tile To Size","pos":[3340,1250],"size":[210,106],"flags":{},"order":35,"mode":0,"inputs":[{"name":"image","localized_name":"image","type":"IMAGE","link":1165}],"outputs":[{"name":"image","localized_name":"image","type":"IMAGE","links":[1167],"slot_index":0}],"properties":{"Node name for S&R":"Image Repeat Tile To Size"},"widgets_values":[1536,1536,false]},{"id":428,"type":"VAEEncodeAdvanced","pos":[3580,1230],"size":[228.90342712402344,278],"flags":{},"order":36,"mode":0,"inputs":[{"name":"image_1","localized_name":"image_1","type":"IMAGE","shape":7,"link":1166},{"name":"image_2","localized_name":"image_2","type":"IMAGE","shape":7,"link":1167},{"name":"mask","localized_name":"mask","type":"IMAGE","shape":7,"link":null},{"name":"latent","localized_name":"latent","type":"LATENT","shape":7,"link":null},{"name":"vae","localized_name":"vae","type":"VAE","shape":7,"link":1168}],"outputs":[{"name":"latent_1","localized_name":"latent_1","type":"LATENT","links":[1143,1151],"slot_index":0},{"name":"latent_2","localized_name":"latent_2","type":"LATENT","links":[1138],"slot_index":1},{"name":"mask","localized_name":"mask","type":"MASK","links":null},{"name":"empty_latent","localized_name":"empty_latent","type":"LATENT","links":null,"slot_index":3},{"name":"width","localized_name":"width","type":"INT","links":null},{"name":"height","localized_name":"height","type":"INT","links":null}],"properties":{"Node name for S&R":"VAEEncodeAdvanced"},"widgets_values":["false",1536,1536,"red",false,"16_channels"]},{"id":371,"type":"VAEDecode","pos":[2741.197265625,974.4011840820312],"size":[140,46],"flags":{},"order":31,"mode":0,"inputs":[{"name":"samples","localized_name":"samples","type":"LATENT","link":1088},{"name":"vae","localized_name":"vae","type":"VAE","link":1029}],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":[1030,1068,1174],"slot_index":0}],"properties":{"Node name for S&R":"VAEDecode","cnr_id":"comfy-core","ver":"0.3.26","widget_ue_connectable":{}},"widgets_values":[]},{"id":378,"type":"ClownOptions_Cycles_Beta","pos":[1768.675537109375,881.3336791992188],"size":[210,130],"flags":{},"order":7,"mode":0,"inputs":[{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":null}],"outputs":[{"name":"options","localized_name":"options","type":"OPTIONS","links":[1044]}],"properties":{"Node name for S&R":"ClownOptions_Cycles_Beta"},"widgets_values":[5,1,0.5,1]},{"id":381,"type":"ClownOptions_Cycles_Beta","pos":[2103.203857421875,881.467041015625],"size":[210,130],"flags":{},"order":8,"mode":0,"inputs":[{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":null}],"outputs":[{"name":"options","localized_name":"options","type":"OPTIONS","links":[1051]}],"properties":{"Node name for S&R":"ClownOptions_Cycles_Beta"},"widgets_values":[5,1,0.5,1]},{"id":426,"type":"ClownOptions_Cycles_Beta","pos":[4200,870],"size":[210,130],"flags":{},"order":9,"mode":0,"inputs":[{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":null}],"outputs":[{"name":"options","localized_name":"options","type":"OPTIONS","links":[1148]}],"properties":{"Node name for S&R":"ClownOptions_Cycles_Beta"},"widgets_values":[5,1,0.5,1]},{"id":424,"type":"ClownOptions_Cycles_Beta","pos":[4540,870],"size":[210,130],"flags":{},"order":10,"mode":0,"inputs":[{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":null}],"outputs":[{"name":"options","localized_name":"options","type":"OPTIONS","links":[1153]}],"properties":{"Node name for S&R":"ClownOptions_Cycles_Beta"},"widgets_values":[5,1,0.5,1]},{"id":398,"type":"Reroute","pos":[1034.667724609375,1458.654541015625],"size":[75,26],"flags":{},"order":16,"mode":0,"inputs":[{"name":"","type":"*","link":1122}],"outputs":[{"name":"","type":"IMAGE","links":[1107,1112,1113,1115,1123,1175,1176],"slot_index":0}],"properties":{"showOutputText":false,"horizontal":false}},{"id":431,"type":"Note","pos":[356.2033386230469,1583.169677734375],"size":[210,88],"flags":{},"order":11,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["Used a 384x384 image.\n\nAny size will work."],"color":"#432","bgcolor":"#653"}],"links":[[141,151,0,8,1,"VAE"],[142,151,0,72,1,"VAE"],[143,151,0,35,1,"VAE"],[144,151,0,154,7,"VAE"],[159,151,0,72,1,"VAE"],[160,151,0,157,7,"VAE"],[161,151,0,8,1,"VAE"],[162,151,0,154,7,"VAE"],[163,151,0,72,1,"VAE"],[164,151,0,8,1,"VAE"],[165,151,0,154,7,"VAE"],[171,151,0,8,1,"VAE"],[172,151,0,72,1,"VAE"],[173,151,0,154,7,"VAE"],[174,151,0,157,7,"VAE"],[176,151,0,8,1,"VAE"],[177,151,0,72,1,"VAE"],[178,151,0,154,7,"VAE"],[179,151,0,157,7,"VAE"],[195,151,0,8,1,"VAE"],[196,151,0,72,1,"VAE"],[197,151,0,154,7,"VAE"],[198,151,0,157,7,"VAE"],[199,151,0,160,7,"VAE"],[200,151,0,8,1,"VAE"],[201,151,0,72,1,"VAE"],[202,151,0,154,7,"VAE"],[203,151,0,157,7,"VAE"],[204,151,0,160,7,"VAE"],[217,151,0,8,1,"VAE"],[218,151,0,72,1,"VAE"],[219,151,0,154,7,"VAE"],[220,151,0,157,7,"VAE"],[221,151,0,160,7,"VAE"],[222,151,0,8,1,"VAE"],[223,151,0,72,1,"VAE"],[224,151,0,157,7,"VAE"],[225,151,0,8,1,"VAE"],[226,151,0,72,1,"VAE"],[227,151,0,157,7,"VAE"],[250,151,0,62,1,"VAE"],[251,151,0,157,7,"VAE"],[252,151,0,8,1,"VAE"],[253,151,0,72,1,"VAE"],[254,151,0,62,1,"VAE"],[255,151,0,157,7,"VAE"],[256,151,0,8,1,"VAE"],[257,151,0,72,1,"VAE"],[258,151,0,160,7,"VAE"],[271,151,0,62,1,"VAE"],[272,151,0,157,7,"VAE"],[273,151,0,8,1,"VAE"],[274,151,0,72,1,"VAE"],[275,151,0,160,7,"VAE"],[276,151,0,154,7,"VAE"],[277,151,0,62,1,"VAE"],[278,151,0,157,7,"VAE"],[279,151,0,8,1,"VAE"],[280,151,0,72,1,"VAE"],[281,151,0,160,7,"VAE"],[282,151,0,154,7,"VAE"],[294,151,0,157,7,"VAE"],[295,151,0,72,1,"VAE"],[296,151,0,160,7,"VAE"],[297,151,0,154,7,"VAE"],[298,151,0,8,1,"VAE"],[299,151,0,313,1,"VAE"],[300,151,0,62,1,"VAE"],[301,151,0,157,7,"VAE"],[302,151,0,72,1,"VAE"],[303,151,0,160,7,"VAE"],[304,151,0,8,1,"VAE"],[305,151,0,313,1,"VAE"],[306,151,0,62,1,"VAE"],[307,151,0,154,7,"VAE"],[309,151,0,157,7,"VAE"],[310,151,0,72,1,"VAE"],[311,151,0,160,7,"VAE"],[312,151,0,8,1,"VAE"],[313,151,0,313,1,"VAE"],[314,151,0,62,1,"VAE"],[315,151,0,154,7,"VAE"],[316,151,0,157,7,"VAE"],[317,151,0,72,1,"VAE"],[318,151,0,160,7,"VAE"],[319,151,0,8,1,"VAE"],[320,151,0,313,1,"VAE"],[321,151,0,62,1,"VAE"],[322,151,0,154,7,"VAE"],[327,151,0,157,7,"VAE"],[328,151,0,72,1,"VAE"],[329,151,0,8,1,"VAE"],[330,151,0,313,1,"VAE"],[331,151,0,62,1,"VAE"],[332,151,0,154,7,"VAE"],[333,151,0,160,7,"VAE"],[343,151,0,157,7,"VAE"],[344,151,0,72,1,"VAE"],[345,151,0,8,1,"VAE"],[346,151,0,313,1,"VAE"],[347,151,0,62,1,"VAE"],[348,151,0,160,7,"VAE"],[349,151,0,154,7,"VAE"],[351,151,0,157,7,"VAE"],[352,151,0,72,1,"VAE"],[353,151,0,8,1,"VAE"],[354,151,0,313,1,"VAE"],[355,151,0,62,1,"VAE"],[356,151,0,160,7,"VAE"],[357,151,0,154,7,"VAE"],[363,151,0,157,7,"VAE"],[364,151,0,72,1,"VAE"],[365,151,0,8,1,"VAE"],[366,151,0,160,7,"VAE"],[367,151,0,154,7,"VAE"],[368,151,0,62,1,"VAE"],[370,151,0,157,7,"VAE"],[371,151,0,72,1,"VAE"],[372,151,0,8,1,"VAE"],[373,151,0,160,7,"VAE"],[374,151,0,154,7,"VAE"],[375,151,0,62,1,"VAE"],[377,151,0,157,7,"VAE"],[378,151,0,72,1,"VAE"],[379,151,0,8,1,"VAE"],[380,151,0,160,7,"VAE"],[381,151,0,154,7,"VAE"],[382,151,0,62,1,"VAE"],[383,151,0,157,7,"VAE"],[384,151,0,72,1,"VAE"],[385,151,0,8,1,"VAE"],[386,151,0,160,7,"VAE"],[387,151,0,154,7,"VAE"],[388,151,0,62,1,"VAE"],[391,151,0,157,7,"VAE"],[392,151,0,72,1,"VAE"],[393,151,0,8,1,"VAE"],[394,151,0,160,7,"VAE"],[395,151,0,154,7,"VAE"],[396,151,0,62,1,"VAE"],[402,151,0,157,7,"VAE"],[403,151,0,72,1,"VAE"],[404,151,0,8,1,"VAE"],[405,151,0,160,7,"VAE"],[406,151,0,154,7,"VAE"],[407,151,0,62,1,"VAE"],[408,151,0,157,7,"VAE"],[409,151,0,72,1,"VAE"],[410,151,0,8,1,"VAE"],[411,151,0,160,7,"VAE"],[412,151,0,154,7,"VAE"],[413,151,0,62,1,"VAE"],[421,151,0,157,7,"VAE"],[422,151,0,72,1,"VAE"],[423,151,0,8,1,"VAE"],[424,151,0,160,7,"VAE"],[425,151,0,154,7,"VAE"],[426,151,0,62,1,"VAE"],[427,151,0,157,7,"VAE"],[428,151,0,72,1,"VAE"],[429,151,0,8,1,"VAE"],[430,151,0,160,7,"VAE"],[431,151,0,154,7,"VAE"],[432,151,0,62,1,"VAE"],[1004,349,3,361,0,"CLIP_VISION"],[1006,361,0,362,2,"CLIP_VISION_OUTPUT"],[1007,349,1,364,0,"CLIP"],[1008,364,0,362,0,"CONDITIONING"],[1009,349,4,362,1,"STYLE_MODEL"],[1022,349,0,368,0,"MODEL"],[1024,355,0,370,0,"MODEL"],[1029,349,2,371,1,"VAE"],[1030,371,0,372,0,"IMAGE"],[1031,370,0,373,4,"LATENT"],[1038,349,2,375,4,"VAE"],[1044,378,0,373,6,"OPTIONS"],[1047,368,0,355,0,"MODEL"],[1051,381,0,380,6,"OPTIONS"],[1053,373,0,380,4,"LATENT"],[1055,364,0,382,1,"CONDITIONING"],[1056,359,0,382,2,"CONTROL_NET"],[1058,349,2,382,4,"VAE"],[1068,371,0,387,0,"IMAGE"],[1088,374,0,371,0,"LATENT"],[1097,380,0,374,4,"LATENT"],[1099,369,0,373,5,"GUIDES"],[1101,375,1,369,0,"LATENT"],[1102,375,0,370,3,"LATENT"],[1107,398,0,361,1,"IMAGE"],[1108,362,0,382,0,"CONDITIONING"],[1111,375,0,355,1,"LATENT"],[1112,398,0,382,3,"IMAGE"],[1113,398,0,375,0,"IMAGE"],[1115,398,0,387,1,"IMAGE"],[1117,362,0,370,1,"CONDITIONING"],[1118,382,0,373,1,"CONDITIONING"],[1122,401,0,398,0,"*"],[1123,398,0,404,0,"IMAGE"],[1124,404,0,375,1,"IMAGE"],[1134,362,0,374,1,"CONDITIONING"],[1135,349,3,408,0,"CLIP_VISION"],[1137,349,1,409,0,"CLIP"],[1138,428,1,410,0,"LATENT"],[1139,430,0,411,1,"CONDITIONING"],[1140,417,0,411,4,"LATENT"],[1141,420,0,412,0,"IMAGE"],[1142,414,0,413,0,"MODEL"],[1143,428,0,413,1,"LATENT"],[1144,349,0,414,0,"MODEL"],[1145,425,0,415,1,"CONDITIONING"],[1146,416,0,415,4,"LATENT"],[1147,410,0,415,5,"GUIDES"],[1148,426,0,415,6,"OPTIONS"],[1149,413,0,416,0,"MODEL"],[1150,430,0,416,1,"CONDITIONING"],[1151,428,0,416,3,"LATENT"],[1152,415,0,417,4,"LATENT"],[1153,424,0,417,6,"OPTIONS"],[1154,411,0,420,0,"LATENT"],[1155,349,2,420,1,"VAE"],[1160,430,0,425,0,"CONDITIONING"],[1161,409,0,425,1,"CONDITIONING"],[1162,359,0,425,2,"CONTROL_NET"],[1164,349,2,425,4,"VAE"],[1165,421,0,427,0,"IMAGE"],[1166,421,0,428,0,"IMAGE"],[1167,427,0,428,1,"IMAGE"],[1168,349,2,428,4,"VAE"],[1169,420,0,429,0,"IMAGE"],[1170,421,0,429,1,"IMAGE"],[1171,409,0,430,0,"CONDITIONING"],[1172,349,4,430,1,"STYLE_MODEL"],[1173,408,0,430,2,"CLIP_VISION_OUTPUT"],[1174,371,0,421,0,"*"],[1175,398,0,425,3,"IMAGE"],[1176,398,0,408,1,"IMAGE"]],"groups":[],"config":{},"extra":{"ds":{"scale":1.3109994191500252,"offset":[916.9662500305632,-478.4961303433991]},"ue_links":[{"downstream":157,"downstream_slot":7,"upstream":"151","upstream_slot":0,"controller":64,"type":"VAE"},{"downstream":154,"downstream_slot":7,"upstream":"151","upstream_slot":0,"controller":64,"type":"VAE"},{"downstream":72,"downstream_slot":1,"upstream":"151","upstream_slot":0,"controller":64,"type":"VAE"},{"downstream":62,"downstream_slot":1,"upstream":"151","upstream_slot":0,"controller":64,"type":"VAE"}],"VHS_latentpreview":false,"VHS_latentpreviewrate":0,"VHS_MetadataImage":true,"VHS_KeepIntermediate":true,"links_added_by_ue":[959,960,961,962],"frontendVersion":"1.18.6"},"version":0.4} \ No newline at end of file diff --git a/ComfyUI/custom_nodes/RES4LYF/example_workflows/flux upscale thumbnail widescreen.json b/ComfyUI/custom_nodes/RES4LYF/example_workflows/flux upscale thumbnail widescreen.json new file mode 100644 index 0000000000000000000000000000000000000000..daf7c405e947b86fa02cebca622c1f5db1bed5ee --- /dev/null +++ b/ComfyUI/custom_nodes/RES4LYF/example_workflows/flux upscale thumbnail widescreen.json @@ -0,0 +1 @@ +{"last_node_id":411,"last_link_id":1130,"nodes":[{"id":369,"type":"ClownGuide_Style_Beta","pos":[1138.06640625,1574.328857421875],"size":[231.30213928222656,286],"flags":{},"order":18,"mode":0,"inputs":[{"name":"guide","localized_name":"guide","type":"LATENT","shape":7,"link":1101},{"name":"mask","localized_name":"mask","type":"MASK","shape":7,"link":null},{"name":"weights","localized_name":"weights","type":"SIGMAS","shape":7,"link":null},{"name":"guides","localized_name":"guides","type":"GUIDES","shape":7,"link":null}],"outputs":[{"name":"guides","localized_name":"guides","type":"GUIDES","links":[1099],"slot_index":0}],"properties":{"Node name for S&R":"ClownGuide_Style_Beta"},"widgets_values":["positive","WCT",1,1,"constant",0,-1,false]},{"id":374,"type":"ClownsharkChainsampler_Beta","pos":[2403.98583984375,1081.333740234375],"size":[274.9878234863281,528.6721801757812],"flags":{},"order":22,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","shape":7,"link":null},{"name":"positive","localized_name":"positive","type":"CONDITIONING","shape":7,"link":1109},{"name":"negative","localized_name":"negative","type":"CONDITIONING","shape":7,"link":null},{"name":"sigmas","localized_name":"sigmas","type":"SIGMAS","shape":7,"link":null},{"name":"latent_image","localized_name":"latent_image","type":"LATENT","shape":7,"link":1097},{"name":"guides","localized_name":"guides","type":"GUIDES","shape":7,"link":null},{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":null}],"outputs":[{"name":"output","localized_name":"output","type":"LATENT","links":[1088],"slot_index":0},{"name":"denoised","localized_name":"denoised","type":"LATENT","links":null},{"name":"options","localized_name":"options","type":"OPTIONS","links":null}],"properties":{"Node name for S&R":"ClownsharkChainsampler_Beta"},"widgets_values":[0.5,"multistep/res_3m",-1,1,"resample",true]},{"id":372,"type":"SaveImage","pos":[2740,1080],"size":[442.38494873046875,530.0809936523438],"flags":{},"order":24,"mode":0,"inputs":[{"name":"images","localized_name":"images","type":"IMAGE","link":1030}],"outputs":[],"properties":{},"widgets_values":["ComfyUI"]},{"id":355,"type":"ModelSamplingAdvancedResolution","pos":[1134.0809326171875,1057.9874267578125],"size":[260.3999938964844,126],"flags":{},"order":17,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","link":1047},{"name":"latent_image","localized_name":"latent_image","type":"LATENT","link":1111}],"outputs":[{"name":"model","localized_name":"model","type":"MODEL","links":[1024],"slot_index":0}],"properties":{"Node name for S&R":"ModelSamplingAdvancedResolution"},"widgets_values":["exponential",1.35,0.85]},{"id":368,"type":"ReFluxPatcher","pos":[897.4150390625,1095.9840087890625],"size":[210,82],"flags":{},"order":9,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","link":1022}],"outputs":[{"name":"model","localized_name":"model","type":"MODEL","links":[1047],"slot_index":0}],"properties":{"Node name for S&R":"ReFluxPatcher"},"widgets_values":["float32",true]},{"id":349,"type":"FluxLoader","pos":[554.6767578125,1099.277099609375],"size":[315,282],"flags":{},"order":0,"mode":0,"inputs":[],"outputs":[{"name":"model","localized_name":"model","type":"MODEL","links":[1022],"slot_index":0},{"name":"clip","localized_name":"clip","type":"CLIP","links":[1007],"slot_index":1},{"name":"vae","localized_name":"vae","type":"VAE","links":[1029,1038,1058],"slot_index":2},{"name":"clip_vision","localized_name":"clip_vision","type":"CLIP_VISION","links":[1004],"slot_index":3},{"name":"style_model","localized_name":"style_model","type":"STYLE_MODEL","links":[1009]}],"properties":{"Node name for S&R":"FluxLoader"},"widgets_values":["colossusProjectFlux_v42AIO.safetensors","default",".use_ckpt_clip",".none",".use_ckpt_vae","sigclip_vision_patch14_384.safetensors","flux1-redux-dev.safetensors"]},{"id":387,"type":"Image Comparer (rgthree)","pos":[3228.67529296875,1082.0006103515625],"size":[502.8477478027344,526.1139526367188],"flags":{},"order":25,"mode":0,"inputs":[{"name":"image_a","type":"IMAGE","dir":3,"link":1068},{"name":"image_b","type":"IMAGE","dir":3,"link":1115}],"outputs":[],"properties":{"comparer_mode":"Slide"},"widgets_values":[[{"name":"A","selected":true,"url":"/api/view?filename=rgthree.compare._temp_klodp_00033_.png&type=temp&subfolder=&rand=0.5892199958912905"},{"name":"B","selected":true,"url":"/api/view?filename=rgthree.compare._temp_klodp_00034_.png&type=temp&subfolder=&rand=0.10900460801823297"}]]},{"id":373,"type":"ClownsharkChainsampler_Beta","pos":[1740,1080],"size":[272.9876403808594,526.665771484375],"flags":{},"order":20,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","shape":7,"link":null},{"name":"positive","localized_name":"positive","type":"CONDITIONING","shape":7,"link":1118},{"name":"negative","localized_name":"negative","type":"CONDITIONING","shape":7,"link":null},{"name":"sigmas","localized_name":"sigmas","type":"SIGMAS","shape":7,"link":null},{"name":"latent_image","localized_name":"latent_image","type":"LATENT","shape":7,"link":1031},{"name":"guides","localized_name":"guides","type":"GUIDES","shape":7,"link":1099},{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":1044},{"name":"options 2","type":"OPTIONS","link":null}],"outputs":[{"name":"output","localized_name":"output","type":"LATENT","links":[1053],"slot_index":0},{"name":"denoised","localized_name":"denoised","type":"LATENT","links":null},{"name":"options","localized_name":"options","type":"OPTIONS","links":null}],"properties":{"Node name for S&R":"ClownsharkChainsampler_Beta"},"widgets_values":[0.5,"multistep/res_3m",1,1,"resample",true]},{"id":382,"type":"ControlNetApplyAdvanced","pos":[1440,830],"size":[210,186],"flags":{},"order":16,"mode":0,"inputs":[{"name":"positive","localized_name":"positive","type":"CONDITIONING","link":1108},{"name":"negative","localized_name":"negative","type":"CONDITIONING","link":1055},{"name":"control_net","localized_name":"control_net","type":"CONTROL_NET","link":1056},{"name":"image","localized_name":"image","type":"IMAGE","link":1112},{"name":"vae","localized_name":"vae","type":"VAE","shape":7,"link":1058}],"outputs":[{"name":"positive","localized_name":"positive","type":"CONDITIONING","links":[1118],"slot_index":0},{"name":"negative","localized_name":"negative","type":"CONDITIONING","links":null}],"properties":{"Node name for S&R":"ControlNetApplyAdvanced"},"widgets_values":[1,0,1]},{"id":380,"type":"ClownsharkChainsampler_Beta","pos":[2078.66015625,1080.6669921875],"size":[263.6514892578125,527.99951171875],"flags":{},"order":21,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","shape":7,"link":null},{"name":"positive","localized_name":"positive","type":"CONDITIONING","shape":7,"link":null},{"name":"negative","localized_name":"negative","type":"CONDITIONING","shape":7,"link":null},{"name":"sigmas","localized_name":"sigmas","type":"SIGMAS","shape":7,"link":null},{"name":"latent_image","localized_name":"latent_image","type":"LATENT","shape":7,"link":1053},{"name":"guides","localized_name":"guides","type":"GUIDES","shape":7,"link":null},{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":1051},{"name":"options 2","type":"OPTIONS","link":null}],"outputs":[{"name":"output","localized_name":"output","type":"LATENT","links":[1097],"slot_index":0},{"name":"denoised","localized_name":"denoised","type":"LATENT","links":null},{"name":"options","localized_name":"options","type":"OPTIONS","links":null}],"properties":{"Node name for S&R":"ClownsharkChainsampler_Beta"},"widgets_values":[0.5,"multistep/res_3m",1,1,"resample",true]},{"id":371,"type":"VAEDecode","pos":[2741.197265625,974.4011840820312],"size":[140,46],"flags":{},"order":23,"mode":0,"inputs":[{"name":"samples","localized_name":"samples","type":"LATENT","link":1088},{"name":"vae","localized_name":"vae","type":"VAE","link":1029}],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":[1030,1068],"slot_index":0}],"properties":{"Node name for S&R":"VAEDecode","cnr_id":"comfy-core","ver":"0.3.26","widget_ue_connectable":{}},"widgets_values":[]},{"id":378,"type":"ClownOptions_Cycles_Beta","pos":[1768.675537109375,881.3336791992188],"size":[210,130],"flags":{},"order":1,"mode":0,"inputs":[{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":null}],"outputs":[{"name":"options","localized_name":"options","type":"OPTIONS","links":[1044]}],"properties":{"Node name for S&R":"ClownOptions_Cycles_Beta"},"widgets_values":[5,1,0.5,1]},{"id":381,"type":"ClownOptions_Cycles_Beta","pos":[2103.203857421875,881.467041015625],"size":[210,130],"flags":{},"order":2,"mode":0,"inputs":[{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":null}],"outputs":[{"name":"options","localized_name":"options","type":"OPTIONS","links":[1051]}],"properties":{"Node name for S&R":"ClownOptions_Cycles_Beta"},"widgets_values":[20,1,0.5,1]},{"id":403,"type":"Note","pos":[2098.053466796875,680.7237548828125],"size":[215.7804412841797,88],"flags":{},"order":3,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["Raise cycles here if you see halos. It doesn't hurt to go as high as 20. Minimum of 5 recommended."],"color":"#432","bgcolor":"#653"},{"id":402,"type":"Note","pos":[1755.3779296875,678.1484985351562],"size":[241.524658203125,132.7487030029297],"flags":{},"order":4,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["Lower cycles here if you see halos. Minimum of 1 or 2 recommended.\n\nThese step(s)/cycle(s) (that use the ClownGuide Style node) are needed to prevent blurring when upscaling tiny thumbnail images."],"color":"#432","bgcolor":"#653"},{"id":359,"type":"ControlNetLoader","pos":[597.9067993164062,977.3353881835938],"size":[270.0880432128906,58],"flags":{},"order":5,"mode":0,"inputs":[],"outputs":[{"name":"CONTROL_NET","localized_name":"CONTROL_NET","type":"CONTROL_NET","links":[1056],"slot_index":0}],"properties":{"Node name for S&R":"ControlNetLoader"},"widgets_values":["flux_tile.safetensors"]},{"id":362,"type":"StyleModelApply","pos":[1141.4669189453125,829.1477661132812],"size":[270.06890869140625,122],"flags":{},"order":14,"mode":0,"inputs":[{"name":"conditioning","localized_name":"conditioning","type":"CONDITIONING","link":1008},{"name":"style_model","localized_name":"style_model","type":"STYLE_MODEL","link":1009},{"name":"clip_vision_output","localized_name":"clip_vision_output","type":"CLIP_VISION_OUTPUT","link":1006}],"outputs":[{"name":"CONDITIONING","localized_name":"CONDITIONING","type":"CONDITIONING","links":[1108,1109,1117],"slot_index":0}],"properties":{"Node name for S&R":"StyleModelApply"},"widgets_values":[1,"multiply"]},{"id":361,"type":"CLIPVisionEncode","pos":[862.2003784179688,825.134765625],"size":[253.60000610351562,78],"flags":{},"order":12,"mode":0,"inputs":[{"name":"clip_vision","localized_name":"clip_vision","type":"CLIP_VISION","link":1004},{"name":"image","localized_name":"image","type":"IMAGE","link":1107}],"outputs":[{"name":"CLIP_VISION_OUTPUT","localized_name":"CLIP_VISION_OUTPUT","type":"CLIP_VISION_OUTPUT","links":[1006],"slot_index":0}],"properties":{"Node name for S&R":"CLIPVisionEncode"},"widgets_values":["center"]},{"id":364,"type":"CLIPTextEncode","pos":[899.5093383789062,952.8309936523438],"size":[210,88],"flags":{},"order":10,"mode":0,"inputs":[{"name":"clip","localized_name":"clip","type":"CLIP","link":1007}],"outputs":[{"name":"CONDITIONING","localized_name":"CONDITIONING","type":"CONDITIONING","links":[1008,1055],"slot_index":0}],"properties":{"Node name for S&R":"CLIPTextEncode"},"widgets_values":[""]},{"id":408,"type":"Note","pos":[549.5983276367188,826.2056884765625],"size":[294.1452331542969,99.538818359375],"flags":{},"order":6,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["Jasper's tile controlnet was used.\n\nhttps://huggingface.co/jasperai/Flux.1-dev-Controlnet-Upscaler/blob/main/diffusion_pytorch_model.safetensors"],"color":"#432","bgcolor":"#653"},{"id":370,"type":"ClownsharKSampler_Beta","pos":[1417.3414306640625,1078.0023193359375],"size":[277.65570068359375,627.99951171875],"flags":{},"order":19,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","shape":7,"link":1024},{"name":"positive","localized_name":"positive","type":"CONDITIONING","shape":7,"link":1117},{"name":"negative","localized_name":"negative","type":"CONDITIONING","shape":7,"link":null},{"name":"latent_image","localized_name":"latent_image","type":"LATENT","shape":7,"link":1102},{"name":"sigmas","localized_name":"sigmas","type":"SIGMAS","shape":7,"link":null},{"name":"guides","localized_name":"guides","type":"GUIDES","shape":7,"link":null},{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":null}],"outputs":[{"name":"output","localized_name":"output","type":"LATENT","links":[1031],"slot_index":0},{"name":"denoised","localized_name":"denoised","type":"LATENT","links":null},{"name":"options","localized_name":"options","type":"OPTIONS","links":null}],"properties":{"Node name for S&R":"ClownsharKSampler_Beta"},"widgets_values":[0.5,"multistep/res_3m","beta57",30,14,1,1,0,"fixed","unsample",true]},{"id":404,"type":"Image Repeat Tile To Size","pos":[899.620361328125,1259.9044189453125],"size":[210,106],"flags":{},"order":13,"mode":0,"inputs":[{"name":"image","localized_name":"image","type":"IMAGE","link":1123}],"outputs":[{"name":"image","localized_name":"image","type":"IMAGE","links":[1124],"slot_index":0}],"properties":{"Node name for S&R":"Image Repeat Tile To Size"},"widgets_values":[1792,1024,true]},{"id":375,"type":"VAEEncodeAdvanced","pos":[1140,1240],"size":[228.90342712402344,278],"flags":{},"order":15,"mode":0,"inputs":[{"name":"image_1","localized_name":"image_1","type":"IMAGE","shape":7,"link":1113},{"name":"image_2","localized_name":"image_2","type":"IMAGE","shape":7,"link":1124},{"name":"mask","localized_name":"mask","type":"IMAGE","shape":7,"link":null},{"name":"latent","localized_name":"latent","type":"LATENT","shape":7,"link":null},{"name":"vae","localized_name":"vae","type":"VAE","shape":7,"link":1038}],"outputs":[{"name":"latent_1","localized_name":"latent_1","type":"LATENT","links":[1102,1111],"slot_index":0},{"name":"latent_2","localized_name":"latent_2","type":"LATENT","links":[1101],"slot_index":1},{"name":"mask","localized_name":"mask","type":"MASK","links":null},{"name":"empty_latent","localized_name":"empty_latent","type":"LATENT","links":null,"slot_index":3},{"name":"width","localized_name":"width","type":"INT","links":null},{"name":"height","localized_name":"height","type":"INT","links":null}],"properties":{"Node name for S&R":"VAEEncodeAdvanced"},"widgets_values":["image_2",1,1,"red",false,"16_channels"]},{"id":398,"type":"Reroute","pos":[1034.0006103515625,1404.638671875],"size":[75,26],"flags":{},"order":11,"mode":0,"inputs":[{"name":"","type":"*","link":1130}],"outputs":[{"name":"","type":"IMAGE","links":[1107,1112,1113,1115,1123],"slot_index":0}],"properties":{"showOutputText":false,"horizontal":false}},{"id":411,"type":"LoadImage","pos":[791.842041015625,1491.6041259765625],"size":[315,314],"flags":{},"order":7,"mode":0,"inputs":[],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":[1130],"slot_index":0},{"name":"MASK","localized_name":"MASK","type":"MASK","links":null}],"properties":{"Node name for S&R":"LoadImage"},"widgets_values":["pasted/image (595).png","image"]},{"id":407,"type":"Note","pos":[552.9491577148438,1493.21923828125],"size":[210.6668243408203,166.69004821777344],"flags":{},"order":8,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["Input image was 672x384.\n\nAny size can be used. Just be sure to keep the aspect ratio the same, per usual.\n\nBest results will be with minimum size = 384 (height and/or width), due to that being what SigCLIP was trained on (which is what Redux uses)."],"color":"#432","bgcolor":"#653"}],"links":[[141,151,0,8,1,"VAE"],[142,151,0,72,1,"VAE"],[143,151,0,35,1,"VAE"],[144,151,0,154,7,"VAE"],[159,151,0,72,1,"VAE"],[160,151,0,157,7,"VAE"],[161,151,0,8,1,"VAE"],[162,151,0,154,7,"VAE"],[163,151,0,72,1,"VAE"],[164,151,0,8,1,"VAE"],[165,151,0,154,7,"VAE"],[171,151,0,8,1,"VAE"],[172,151,0,72,1,"VAE"],[173,151,0,154,7,"VAE"],[174,151,0,157,7,"VAE"],[176,151,0,8,1,"VAE"],[177,151,0,72,1,"VAE"],[178,151,0,154,7,"VAE"],[179,151,0,157,7,"VAE"],[195,151,0,8,1,"VAE"],[196,151,0,72,1,"VAE"],[197,151,0,154,7,"VAE"],[198,151,0,157,7,"VAE"],[199,151,0,160,7,"VAE"],[200,151,0,8,1,"VAE"],[201,151,0,72,1,"VAE"],[202,151,0,154,7,"VAE"],[203,151,0,157,7,"VAE"],[204,151,0,160,7,"VAE"],[217,151,0,8,1,"VAE"],[218,151,0,72,1,"VAE"],[219,151,0,154,7,"VAE"],[220,151,0,157,7,"VAE"],[221,151,0,160,7,"VAE"],[222,151,0,8,1,"VAE"],[223,151,0,72,1,"VAE"],[224,151,0,157,7,"VAE"],[225,151,0,8,1,"VAE"],[226,151,0,72,1,"VAE"],[227,151,0,157,7,"VAE"],[250,151,0,62,1,"VAE"],[251,151,0,157,7,"VAE"],[252,151,0,8,1,"VAE"],[253,151,0,72,1,"VAE"],[254,151,0,62,1,"VAE"],[255,151,0,157,7,"VAE"],[256,151,0,8,1,"VAE"],[257,151,0,72,1,"VAE"],[258,151,0,160,7,"VAE"],[271,151,0,62,1,"VAE"],[272,151,0,157,7,"VAE"],[273,151,0,8,1,"VAE"],[274,151,0,72,1,"VAE"],[275,151,0,160,7,"VAE"],[276,151,0,154,7,"VAE"],[277,151,0,62,1,"VAE"],[278,151,0,157,7,"VAE"],[279,151,0,8,1,"VAE"],[280,151,0,72,1,"VAE"],[281,151,0,160,7,"VAE"],[282,151,0,154,7,"VAE"],[294,151,0,157,7,"VAE"],[295,151,0,72,1,"VAE"],[296,151,0,160,7,"VAE"],[297,151,0,154,7,"VAE"],[298,151,0,8,1,"VAE"],[299,151,0,313,1,"VAE"],[300,151,0,62,1,"VAE"],[301,151,0,157,7,"VAE"],[302,151,0,72,1,"VAE"],[303,151,0,160,7,"VAE"],[304,151,0,8,1,"VAE"],[305,151,0,313,1,"VAE"],[306,151,0,62,1,"VAE"],[307,151,0,154,7,"VAE"],[309,151,0,157,7,"VAE"],[310,151,0,72,1,"VAE"],[311,151,0,160,7,"VAE"],[312,151,0,8,1,"VAE"],[313,151,0,313,1,"VAE"],[314,151,0,62,1,"VAE"],[315,151,0,154,7,"VAE"],[316,151,0,157,7,"VAE"],[317,151,0,72,1,"VAE"],[318,151,0,160,7,"VAE"],[319,151,0,8,1,"VAE"],[320,151,0,313,1,"VAE"],[321,151,0,62,1,"VAE"],[322,151,0,154,7,"VAE"],[327,151,0,157,7,"VAE"],[328,151,0,72,1,"VAE"],[329,151,0,8,1,"VAE"],[330,151,0,313,1,"VAE"],[331,151,0,62,1,"VAE"],[332,151,0,154,7,"VAE"],[333,151,0,160,7,"VAE"],[343,151,0,157,7,"VAE"],[344,151,0,72,1,"VAE"],[345,151,0,8,1,"VAE"],[346,151,0,313,1,"VAE"],[347,151,0,62,1,"VAE"],[348,151,0,160,7,"VAE"],[349,151,0,154,7,"VAE"],[351,151,0,157,7,"VAE"],[352,151,0,72,1,"VAE"],[353,151,0,8,1,"VAE"],[354,151,0,313,1,"VAE"],[355,151,0,62,1,"VAE"],[356,151,0,160,7,"VAE"],[357,151,0,154,7,"VAE"],[363,151,0,157,7,"VAE"],[364,151,0,72,1,"VAE"],[365,151,0,8,1,"VAE"],[366,151,0,160,7,"VAE"],[367,151,0,154,7,"VAE"],[368,151,0,62,1,"VAE"],[370,151,0,157,7,"VAE"],[371,151,0,72,1,"VAE"],[372,151,0,8,1,"VAE"],[373,151,0,160,7,"VAE"],[374,151,0,154,7,"VAE"],[375,151,0,62,1,"VAE"],[377,151,0,157,7,"VAE"],[378,151,0,72,1,"VAE"],[379,151,0,8,1,"VAE"],[380,151,0,160,7,"VAE"],[381,151,0,154,7,"VAE"],[382,151,0,62,1,"VAE"],[383,151,0,157,7,"VAE"],[384,151,0,72,1,"VAE"],[385,151,0,8,1,"VAE"],[386,151,0,160,7,"VAE"],[387,151,0,154,7,"VAE"],[388,151,0,62,1,"VAE"],[391,151,0,157,7,"VAE"],[392,151,0,72,1,"VAE"],[393,151,0,8,1,"VAE"],[394,151,0,160,7,"VAE"],[395,151,0,154,7,"VAE"],[396,151,0,62,1,"VAE"],[402,151,0,157,7,"VAE"],[403,151,0,72,1,"VAE"],[404,151,0,8,1,"VAE"],[405,151,0,160,7,"VAE"],[406,151,0,154,7,"VAE"],[407,151,0,62,1,"VAE"],[408,151,0,157,7,"VAE"],[409,151,0,72,1,"VAE"],[410,151,0,8,1,"VAE"],[411,151,0,160,7,"VAE"],[412,151,0,154,7,"VAE"],[413,151,0,62,1,"VAE"],[421,151,0,157,7,"VAE"],[422,151,0,72,1,"VAE"],[423,151,0,8,1,"VAE"],[424,151,0,160,7,"VAE"],[425,151,0,154,7,"VAE"],[426,151,0,62,1,"VAE"],[427,151,0,157,7,"VAE"],[428,151,0,72,1,"VAE"],[429,151,0,8,1,"VAE"],[430,151,0,160,7,"VAE"],[431,151,0,154,7,"VAE"],[432,151,0,62,1,"VAE"],[1004,349,3,361,0,"CLIP_VISION"],[1006,361,0,362,2,"CLIP_VISION_OUTPUT"],[1007,349,1,364,0,"CLIP"],[1008,364,0,362,0,"CONDITIONING"],[1009,349,4,362,1,"STYLE_MODEL"],[1022,349,0,368,0,"MODEL"],[1024,355,0,370,0,"MODEL"],[1029,349,2,371,1,"VAE"],[1030,371,0,372,0,"IMAGE"],[1031,370,0,373,4,"LATENT"],[1038,349,2,375,4,"VAE"],[1044,378,0,373,6,"OPTIONS"],[1047,368,0,355,0,"MODEL"],[1051,381,0,380,6,"OPTIONS"],[1053,373,0,380,4,"LATENT"],[1055,364,0,382,1,"CONDITIONING"],[1056,359,0,382,2,"CONTROL_NET"],[1058,349,2,382,4,"VAE"],[1068,371,0,387,0,"IMAGE"],[1088,374,0,371,0,"LATENT"],[1097,380,0,374,4,"LATENT"],[1099,369,0,373,5,"GUIDES"],[1101,375,1,369,0,"LATENT"],[1102,375,0,370,3,"LATENT"],[1107,398,0,361,1,"IMAGE"],[1108,362,0,382,0,"CONDITIONING"],[1109,362,0,374,1,"CONDITIONING"],[1111,375,0,355,1,"LATENT"],[1112,398,0,382,3,"IMAGE"],[1113,398,0,375,0,"IMAGE"],[1115,398,0,387,1,"IMAGE"],[1117,362,0,370,1,"CONDITIONING"],[1118,382,0,373,1,"CONDITIONING"],[1123,398,0,404,0,"IMAGE"],[1124,404,0,375,1,"IMAGE"],[1130,411,0,398,0,"*"]],"groups":[],"config":{},"extra":{"ds":{"scale":1.7449402268886842,"offset":[634.5784677482833,-682.7929436822943]},"ue_links":[{"downstream":157,"downstream_slot":7,"upstream":"151","upstream_slot":0,"controller":64,"type":"VAE"},{"downstream":154,"downstream_slot":7,"upstream":"151","upstream_slot":0,"controller":64,"type":"VAE"},{"downstream":72,"downstream_slot":1,"upstream":"151","upstream_slot":0,"controller":64,"type":"VAE"},{"downstream":62,"downstream_slot":1,"upstream":"151","upstream_slot":0,"controller":64,"type":"VAE"}],"VHS_latentpreview":false,"VHS_latentpreviewrate":0,"VHS_MetadataImage":true,"VHS_KeepIntermediate":true,"links_added_by_ue":[959,960,961,962],"frontendVersion":"1.18.6"},"version":0.4} \ No newline at end of file diff --git a/ComfyUI/custom_nodes/RES4LYF/example_workflows/hidream guide data projection.json b/ComfyUI/custom_nodes/RES4LYF/example_workflows/hidream guide data projection.json new file mode 100644 index 0000000000000000000000000000000000000000..2d486ac8704babdaa2b03bdb374f7d2864729537 --- /dev/null +++ b/ComfyUI/custom_nodes/RES4LYF/example_workflows/hidream guide data projection.json @@ -0,0 +1 @@ +{"last_node_id":641,"last_link_id":2035,"nodes":[{"id":628,"type":"LoadImage","pos":[599.166015625,156.38429260253906],"size":[315,314],"flags":{},"order":0,"mode":0,"inputs":[],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":[2017]},{"name":"MASK","localized_name":"MASK","type":"MASK","links":null}],"properties":{"Node name for S&R":"LoadImage"},"widgets_values":["ComfyUI_14254_.png","image"]},{"id":632,"type":"ModelSamplingAdvancedResolution","pos":[962.5586547851562,-316.3705139160156],"size":[277.62237548828125,126],"flags":{},"order":6,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","link":2025},{"name":"latent_image","localized_name":"latent_image","type":"LATENT","link":2015}],"outputs":[{"name":"model","localized_name":"model","type":"MODEL","links":[2016],"slot_index":0}],"properties":{"Node name for S&R":"ModelSamplingAdvancedResolution"},"widgets_values":["exponential",1.35,0.85]},{"id":636,"type":"ClownModelLoader","pos":[599.3463745117188,-176.31788635253906],"size":[315,266],"flags":{},"order":1,"mode":0,"inputs":[],"outputs":[{"name":"model","localized_name":"model","type":"MODEL","links":[2025],"slot_index":0},{"name":"clip","localized_name":"clip","type":"CLIP","links":[2024,2028],"slot_index":1},{"name":"vae","localized_name":"vae","type":"VAE","links":[2026,2027],"slot_index":2}],"properties":{"Node name for S&R":"ClownModelLoader"},"widgets_values":["hidream_i1_full_fp8.safetensors","fp8_e4m3fn","clip_l_hidream.safetensors","clip_g_hidream.safetensors","t5xxl_fp8_e4m3fn_scaled.safetensors","llama_3.1_8b_instruct_fp8_scaled.safetensors","hidream","ae.sft"]},{"id":591,"type":"VAEDecode","pos":[1610,-230],"size":[210,46],"flags":{"collapsed":false},"order":8,"mode":0,"inputs":[{"name":"samples","localized_name":"samples","label":"samples","type":"LATENT","link":2030},{"name":"vae","localized_name":"vae","label":"vae","type":"VAE","link":2027}],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","label":"IMAGE","type":"IMAGE","shape":3,"links":[2019],"slot_index":0}],"properties":{"Node name for S&R":"VAEDecode"},"widgets_values":[]},{"id":633,"type":"SaveImage","pos":[1610,-120],"size":[436.4179382324219,508.5302429199219],"flags":{},"order":9,"mode":0,"inputs":[{"name":"images","localized_name":"images","type":"IMAGE","link":2019}],"outputs":[],"properties":{},"widgets_values":["ComfyUI"]},{"id":629,"type":"VAEEncodeAdvanced","pos":[961.6965942382812,242.70477294921875],"size":[278.0284423828125,280.5834045410156],"flags":{},"order":4,"mode":0,"inputs":[{"name":"image_1","localized_name":"image_1","type":"IMAGE","shape":7,"link":2017},{"name":"image_2","localized_name":"image_2","type":"IMAGE","shape":7,"link":null},{"name":"mask","localized_name":"mask","type":"IMAGE","shape":7,"link":null},{"name":"latent","localized_name":"latent","type":"LATENT","shape":7,"link":null},{"name":"vae","localized_name":"vae","type":"VAE","shape":7,"link":2026}],"outputs":[{"name":"latent_1","localized_name":"latent_1","type":"LATENT","links":[2013,2020],"slot_index":0},{"name":"latent_2","localized_name":"latent_2","type":"LATENT","links":null},{"name":"mask","localized_name":"mask","type":"MASK","links":null},{"name":"empty_latent","localized_name":"empty_latent","type":"LATENT","links":[2015]},{"name":"width","localized_name":"width","type":"INT","links":null},{"name":"height","localized_name":"height","type":"INT","links":null}],"properties":{"Node name for S&R":"VAEEncodeAdvanced"},"widgets_values":["false",1024,1024,"red",false,"16_channels"]},{"id":630,"type":"ClownsharKSampler_Beta","pos":[1271.7001953125,-124.3408432006836],"size":[291.7499084472656,650],"flags":{},"order":7,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","shape":7,"link":2016},{"name":"positive","localized_name":"positive","type":"CONDITIONING","shape":7,"link":2018},{"name":"negative","localized_name":"negative","type":"CONDITIONING","shape":7,"link":2029},{"name":"latent_image","localized_name":"latent_image","type":"LATENT","shape":7,"link":2013},{"name":"sigmas","localized_name":"sigmas","type":"SIGMAS","shape":7,"link":null},{"name":"guides","localized_name":"guides","type":"GUIDES","shape":7,"link":2021},{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":null}],"outputs":[{"name":"output","localized_name":"output","type":"LATENT","links":[2030],"slot_index":0},{"name":"denoised","localized_name":"denoised","type":"LATENT","links":null},{"name":"options","localized_name":"options","type":"OPTIONS","links":null}],"properties":{"Node name for S&R":"ClownsharKSampler_Beta"},"widgets_values":[0.5,"multistep/res_3m","beta57",30,-1,1,4,0,"fixed","standard",true]},{"id":637,"type":"CLIPTextEncode","pos":[962.297607421875,99.93917846679688],"size":[278.4529113769531,88],"flags":{"collapsed":false},"order":3,"mode":0,"inputs":[{"name":"clip","localized_name":"clip","label":"clip","type":"CLIP","link":2028}],"outputs":[{"name":"CONDITIONING","localized_name":"CONDITIONING","label":"CONDITIONING","type":"CONDITIONING","shape":3,"links":[2029],"slot_index":0}],"title":"Positive Prompt","properties":{"Node name for S&R":"CLIPTextEncode"},"widgets_values":["low quality, low detail, blurry, shallow depth of field, mutated, symmetrical, generic"]},{"id":107,"type":"CLIPTextEncode","pos":[959.4713745117188,-123.3353500366211],"size":[282.33453369140625,173.58438110351562],"flags":{"collapsed":false},"order":2,"mode":0,"inputs":[{"name":"clip","localized_name":"clip","label":"clip","type":"CLIP","link":2024}],"outputs":[{"name":"CONDITIONING","localized_name":"CONDITIONING","label":"CONDITIONING","type":"CONDITIONING","shape":3,"links":[2018],"slot_index":0}],"title":"Positive Prompt","properties":{"Node name for S&R":"CLIPTextEncode"},"widgets_values":["the mournful lamentations of of a female rock singer on stage with chaos behind her, her face screaming her sorrowful refrains the despairing cries of anguished screams howling agonized moans, her pained whispers mournful sighs distant echoes across the smoky stage, fading memories of lost loves, forgotten dreams, shattered hopes, crushed spirits, broken hearts"]},{"id":634,"type":"ClownGuide_Beta","pos":[1276.0064697265625,-480.84442138671875],"size":[284.860595703125,290.8609924316406],"flags":{},"order":5,"mode":0,"inputs":[{"name":"guide","localized_name":"guide","type":"LATENT","shape":7,"link":2020},{"name":"mask","localized_name":"mask","type":"MASK","shape":7,"link":null},{"name":"weights","localized_name":"weights","type":"SIGMAS","shape":7,"link":null}],"outputs":[{"name":"guides","localized_name":"guides","type":"GUIDES","links":[2021],"slot_index":0}],"properties":{"Node name for S&R":"ClownGuide_Beta"},"widgets_values":["data",false,true,1,1,"beta57",0,2,false]}],"links":[[2013,629,0,630,3,"LATENT"],[2015,629,3,632,1,"LATENT"],[2016,632,0,630,0,"MODEL"],[2017,628,0,629,0,"IMAGE"],[2018,107,0,630,1,"CONDITIONING"],[2019,591,0,633,0,"IMAGE"],[2020,629,0,634,0,"LATENT"],[2021,634,0,630,5,"GUIDES"],[2024,636,1,107,0,"CLIP"],[2025,636,0,632,0,"MODEL"],[2026,636,2,629,4,"VAE"],[2027,636,2,591,1,"VAE"],[2028,636,1,637,0,"CLIP"],[2029,637,0,630,2,"CONDITIONING"],[2030,630,0,591,0,"LATENT"]],"groups":[],"config":{},"extra":{"ds":{"scale":1.7985878990923265,"offset":[1686.8845871920696,637.6012821508443]},"VHS_latentpreview":false,"VHS_latentpreviewrate":0},"version":0.4} \ No newline at end of file diff --git a/ComfyUI/custom_nodes/RES4LYF/example_workflows/hidream guide flow.json b/ComfyUI/custom_nodes/RES4LYF/example_workflows/hidream guide flow.json new file mode 100644 index 0000000000000000000000000000000000000000..2ed8db45766a40e944b8fff2195887b7fb597693 --- /dev/null +++ b/ComfyUI/custom_nodes/RES4LYF/example_workflows/hidream guide flow.json @@ -0,0 +1 @@ +{"last_node_id":640,"last_link_id":2035,"nodes":[{"id":628,"type":"LoadImage","pos":[599.166015625,156.38429260253906],"size":[315,314],"flags":{},"order":0,"mode":0,"inputs":[],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":[2017]},{"name":"MASK","localized_name":"MASK","type":"MASK","links":null}],"properties":{"Node name for S&R":"LoadImage"},"widgets_values":["ComfyUI_14254_.png","image"]},{"id":632,"type":"ModelSamplingAdvancedResolution","pos":[962.5586547851562,-316.3705139160156],"size":[277.62237548828125,126],"flags":{},"order":9,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","link":2025},{"name":"latent_image","localized_name":"latent_image","type":"LATENT","link":2015}],"outputs":[{"name":"model","localized_name":"model","type":"MODEL","links":[2016],"slot_index":0}],"properties":{"Node name for S&R":"ModelSamplingAdvancedResolution"},"widgets_values":["exponential",1.35,0.85]},{"id":636,"type":"ClownModelLoader","pos":[599.3463745117188,-176.31788635253906],"size":[315,266],"flags":{},"order":1,"mode":0,"inputs":[],"outputs":[{"name":"model","localized_name":"model","type":"MODEL","links":[2025],"slot_index":0},{"name":"clip","localized_name":"clip","type":"CLIP","links":[2024,2028,2034],"slot_index":1},{"name":"vae","localized_name":"vae","type":"VAE","links":[2026,2027],"slot_index":2}],"properties":{"Node name for S&R":"ClownModelLoader"},"widgets_values":["hidream_i1_full_fp8.safetensors","fp8_e4m3fn","clip_l_hidream.safetensors","clip_g_hidream.safetensors","t5xxl_fp8_e4m3fn_scaled.safetensors","llama_3.1_8b_instruct_fp8_scaled.safetensors","hidream","ae.sft"]},{"id":591,"type":"VAEDecode","pos":[1610,-230],"size":[210,46],"flags":{"collapsed":false},"order":11,"mode":0,"inputs":[{"name":"samples","localized_name":"samples","label":"samples","type":"LATENT","link":2030},{"name":"vae","localized_name":"vae","label":"vae","type":"VAE","link":2027}],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","label":"IMAGE","type":"IMAGE","shape":3,"links":[2019],"slot_index":0}],"properties":{"Node name for S&R":"VAEDecode"},"widgets_values":[]},{"id":633,"type":"SaveImage","pos":[1610,-120],"size":[436.4179382324219,508.5302429199219],"flags":{},"order":12,"mode":0,"inputs":[{"name":"images","localized_name":"images","type":"IMAGE","link":2019}],"outputs":[],"properties":{},"widgets_values":["ComfyUI"]},{"id":629,"type":"VAEEncodeAdvanced","pos":[961.6965942382812,242.70477294921875],"size":[278.0284423828125,280.5834045410156],"flags":{},"order":6,"mode":0,"inputs":[{"name":"image_1","localized_name":"image_1","type":"IMAGE","shape":7,"link":2017},{"name":"image_2","localized_name":"image_2","type":"IMAGE","shape":7,"link":null},{"name":"mask","localized_name":"mask","type":"IMAGE","shape":7,"link":null},{"name":"latent","localized_name":"latent","type":"LATENT","shape":7,"link":null},{"name":"vae","localized_name":"vae","type":"VAE","shape":7,"link":2026}],"outputs":[{"name":"latent_1","localized_name":"latent_1","type":"LATENT","links":[2013,2020],"slot_index":0},{"name":"latent_2","localized_name":"latent_2","type":"LATENT","links":null},{"name":"mask","localized_name":"mask","type":"MASK","links":null},{"name":"empty_latent","localized_name":"empty_latent","type":"LATENT","links":[2015]},{"name":"width","localized_name":"width","type":"INT","links":null},{"name":"height","localized_name":"height","type":"INT","links":null}],"properties":{"Node name for S&R":"VAEEncodeAdvanced"},"widgets_values":["false",1024,1024,"red",false,"16_channels"]},{"id":634,"type":"ClownGuide_Beta","pos":[1276.0064697265625,-480.84442138671875],"size":[284.860595703125,290.8609924316406],"flags":{},"order":8,"mode":0,"inputs":[{"name":"guide","localized_name":"guide","type":"LATENT","shape":7,"link":2020},{"name":"mask","localized_name":"mask","type":"MASK","shape":7,"link":null},{"name":"weights","localized_name":"weights","type":"SIGMAS","shape":7,"link":null}],"outputs":[{"name":"guides","localized_name":"guides","type":"GUIDES","links":[2021],"slot_index":0}],"properties":{"Node name for S&R":"ClownGuide_Beta"},"widgets_values":["flow",false,false,1,1,"beta57",0,10,false]},{"id":630,"type":"ClownsharKSampler_Beta","pos":[1271.7001953125,-124.3408432006836],"size":[291.7499084472656,650],"flags":{},"order":10,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","shape":7,"link":2016},{"name":"positive","localized_name":"positive","type":"CONDITIONING","shape":7,"link":2018},{"name":"negative","localized_name":"negative","type":"CONDITIONING","shape":7,"link":2029},{"name":"latent_image","localized_name":"latent_image","type":"LATENT","shape":7,"link":2013},{"name":"sigmas","localized_name":"sigmas","type":"SIGMAS","shape":7,"link":null},{"name":"guides","localized_name":"guides","type":"GUIDES","shape":7,"link":2021},{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":2032},{"name":"options 2","type":"OPTIONS","link":null}],"outputs":[{"name":"output","localized_name":"output","type":"LATENT","links":[2030],"slot_index":0},{"name":"denoised","localized_name":"denoised","type":"LATENT","links":null},{"name":"options","localized_name":"options","type":"OPTIONS","links":null}],"properties":{"Node name for S&R":"ClownsharKSampler_Beta"},"widgets_values":[0.5,"multistep/res_3m","beta57",30,-1,1,4,0,"fixed","standard",true]},{"id":638,"type":"SharkOptions_GuideCond_Beta","pos":[955.9966430664062,585.7319946289062],"size":[284.5923156738281,98],"flags":{},"order":7,"mode":0,"inputs":[{"name":"positive","localized_name":"positive","type":"CONDITIONING","shape":7,"link":2035},{"name":"negative","localized_name":"negative","type":"CONDITIONING","shape":7,"link":2033},{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":null}],"outputs":[{"name":"options","localized_name":"options","type":"OPTIONS","links":[2032],"slot_index":0}],"properties":{"Node name for S&R":"SharkOptions_GuideCond_Beta"},"widgets_values":[4]},{"id":637,"type":"CLIPTextEncode","pos":[962.297607421875,99.93917846679688],"size":[278.4529113769531,88],"flags":{"collapsed":false},"order":4,"mode":0,"inputs":[{"name":"clip","localized_name":"clip","label":"clip","type":"CLIP","link":2028}],"outputs":[{"name":"CONDITIONING","localized_name":"CONDITIONING","label":"CONDITIONING","type":"CONDITIONING","shape":3,"links":[2029,2033],"slot_index":0}],"title":"Positive Prompt","properties":{"Node name for S&R":"CLIPTextEncode"},"widgets_values":["low quality, low detail, blurry, shallow depth of field, mutated, symmetrical, generic"]},{"id":107,"type":"CLIPTextEncode","pos":[959.4713745117188,-123.3353500366211],"size":[282.33453369140625,173.58438110351562],"flags":{"collapsed":false},"order":3,"mode":0,"inputs":[{"name":"clip","localized_name":"clip","label":"clip","type":"CLIP","link":2024}],"outputs":[{"name":"CONDITIONING","localized_name":"CONDITIONING","label":"CONDITIONING","type":"CONDITIONING","shape":3,"links":[2018],"slot_index":0}],"title":"Positive Prompt","properties":{"Node name for S&R":"CLIPTextEncode"},"widgets_values":["the mournful lamentations of of a female rock singer on stage with chaos behind her, her face screaming her sorrowful refrains the despairing cries of anguished screams howling agonized moans, her pained whispers mournful sighs distant echoes across the smoky stage, fading memories of lost loves, forgotten dreams, shattered hopes, crushed spirits, broken hearts"]},{"id":639,"type":"CLIPTextEncode","pos":[599.5145263671875,565.6756591796875],"size":[315.33026123046875,117.94475555419922],"flags":{"collapsed":false},"order":5,"mode":0,"inputs":[{"name":"clip","localized_name":"clip","label":"clip","type":"CLIP","link":2034}],"outputs":[{"name":"CONDITIONING","localized_name":"CONDITIONING","label":"CONDITIONING","type":"CONDITIONING","shape":3,"links":[2035],"slot_index":0}],"title":"Positive Prompt","properties":{"Node name for S&R":"CLIPTextEncode"},"widgets_values":["illustration of a singing clock with huge teeth in a surreal forest with torquiose mountains and a red and yellow sky, ragged trees and a pool of black oil on the ground, dripping paint oozing off the clock"]},{"id":640,"type":"Note","pos":[246.91494750976562,519.0934448242188],"size":[323.0928649902344,167.39759826660156],"flags":{},"order":2,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["With the \"flow\" mode it is usually beneficial to use the supplemental GuideCond node, which allows you to set conditionings for the guide itself. With \"flow\", the guide changes during the sampling process. Without GuideCond in use, it will default to reusing your main prompt, which may result in some loss of adherence to the guide image.\n\n\"Lure\" is the only other mode that will use GuideCond."],"color":"#432","bgcolor":"#653"}],"links":[[2013,629,0,630,3,"LATENT"],[2015,629,3,632,1,"LATENT"],[2016,632,0,630,0,"MODEL"],[2017,628,0,629,0,"IMAGE"],[2018,107,0,630,1,"CONDITIONING"],[2019,591,0,633,0,"IMAGE"],[2020,629,0,634,0,"LATENT"],[2021,634,0,630,5,"GUIDES"],[2024,636,1,107,0,"CLIP"],[2025,636,0,632,0,"MODEL"],[2026,636,2,629,4,"VAE"],[2027,636,2,591,1,"VAE"],[2028,636,1,637,0,"CLIP"],[2029,637,0,630,2,"CONDITIONING"],[2030,630,0,591,0,"LATENT"],[2032,638,0,630,6,"OPTIONS"],[2033,637,0,638,1,"CONDITIONING"],[2034,636,1,639,0,"CLIP"],[2035,639,0,638,0,"CONDITIONING"]],"groups":[],"config":{},"extra":{"ds":{"scale":1.7985878990923265,"offset":[1119.4904101845082,499.1497204604395]},"VHS_latentpreview":false,"VHS_latentpreviewrate":0},"version":0.4} \ No newline at end of file diff --git a/ComfyUI/custom_nodes/RES4LYF/example_workflows/hidream guide lure.json b/ComfyUI/custom_nodes/RES4LYF/example_workflows/hidream guide lure.json new file mode 100644 index 0000000000000000000000000000000000000000..2461f5d5347c5924e49ec577ef3bb7b03c5fb42e --- /dev/null +++ b/ComfyUI/custom_nodes/RES4LYF/example_workflows/hidream guide lure.json @@ -0,0 +1 @@ +{"last_node_id":640,"last_link_id":2035,"nodes":[{"id":628,"type":"LoadImage","pos":[599.166015625,156.38429260253906],"size":[315,314],"flags":{},"order":0,"mode":0,"inputs":[],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":[2017]},{"name":"MASK","localized_name":"MASK","type":"MASK","links":null}],"properties":{"Node name for S&R":"LoadImage"},"widgets_values":["ComfyUI_14254_.png","image"]},{"id":632,"type":"ModelSamplingAdvancedResolution","pos":[962.5586547851562,-316.3705139160156],"size":[277.62237548828125,126],"flags":{},"order":9,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","link":2025},{"name":"latent_image","localized_name":"latent_image","type":"LATENT","link":2015}],"outputs":[{"name":"model","localized_name":"model","type":"MODEL","links":[2016],"slot_index":0}],"properties":{"Node name for S&R":"ModelSamplingAdvancedResolution"},"widgets_values":["exponential",1.35,0.85]},{"id":636,"type":"ClownModelLoader","pos":[599.3463745117188,-176.31788635253906],"size":[315,266],"flags":{},"order":1,"mode":0,"inputs":[],"outputs":[{"name":"model","localized_name":"model","type":"MODEL","links":[2025],"slot_index":0},{"name":"clip","localized_name":"clip","type":"CLIP","links":[2024,2028,2034],"slot_index":1},{"name":"vae","localized_name":"vae","type":"VAE","links":[2026,2027],"slot_index":2}],"properties":{"Node name for S&R":"ClownModelLoader"},"widgets_values":["hidream_i1_full_fp8.safetensors","fp8_e4m3fn","clip_l_hidream.safetensors","clip_g_hidream.safetensors","t5xxl_fp8_e4m3fn_scaled.safetensors","llama_3.1_8b_instruct_fp8_scaled.safetensors","hidream","ae.sft"]},{"id":591,"type":"VAEDecode","pos":[1610,-230],"size":[210,46],"flags":{"collapsed":false},"order":11,"mode":0,"inputs":[{"name":"samples","localized_name":"samples","label":"samples","type":"LATENT","link":2030},{"name":"vae","localized_name":"vae","label":"vae","type":"VAE","link":2027}],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","label":"IMAGE","type":"IMAGE","shape":3,"links":[2019],"slot_index":0}],"properties":{"Node name for S&R":"VAEDecode"},"widgets_values":[]},{"id":633,"type":"SaveImage","pos":[1610,-120],"size":[436.4179382324219,508.5302429199219],"flags":{},"order":12,"mode":0,"inputs":[{"name":"images","localized_name":"images","type":"IMAGE","link":2019}],"outputs":[],"properties":{},"widgets_values":["ComfyUI"]},{"id":629,"type":"VAEEncodeAdvanced","pos":[961.6965942382812,242.70477294921875],"size":[278.0284423828125,280.5834045410156],"flags":{},"order":6,"mode":0,"inputs":[{"name":"image_1","localized_name":"image_1","type":"IMAGE","shape":7,"link":2017},{"name":"image_2","localized_name":"image_2","type":"IMAGE","shape":7,"link":null},{"name":"mask","localized_name":"mask","type":"IMAGE","shape":7,"link":null},{"name":"latent","localized_name":"latent","type":"LATENT","shape":7,"link":null},{"name":"vae","localized_name":"vae","type":"VAE","shape":7,"link":2026}],"outputs":[{"name":"latent_1","localized_name":"latent_1","type":"LATENT","links":[2013,2020],"slot_index":0},{"name":"latent_2","localized_name":"latent_2","type":"LATENT","links":null},{"name":"mask","localized_name":"mask","type":"MASK","links":null},{"name":"empty_latent","localized_name":"empty_latent","type":"LATENT","links":[2015]},{"name":"width","localized_name":"width","type":"INT","links":null},{"name":"height","localized_name":"height","type":"INT","links":null}],"properties":{"Node name for S&R":"VAEEncodeAdvanced"},"widgets_values":["false",1024,1024,"red",false,"16_channels"]},{"id":630,"type":"ClownsharKSampler_Beta","pos":[1271.7001953125,-124.3408432006836],"size":[291.7499084472656,650],"flags":{},"order":10,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","shape":7,"link":2016},{"name":"positive","localized_name":"positive","type":"CONDITIONING","shape":7,"link":2018},{"name":"negative","localized_name":"negative","type":"CONDITIONING","shape":7,"link":2029},{"name":"latent_image","localized_name":"latent_image","type":"LATENT","shape":7,"link":2013},{"name":"sigmas","localized_name":"sigmas","type":"SIGMAS","shape":7,"link":null},{"name":"guides","localized_name":"guides","type":"GUIDES","shape":7,"link":2021},{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":2032},{"name":"options 2","type":"OPTIONS","link":null}],"outputs":[{"name":"output","localized_name":"output","type":"LATENT","links":[2030],"slot_index":0},{"name":"denoised","localized_name":"denoised","type":"LATENT","links":null},{"name":"options","localized_name":"options","type":"OPTIONS","links":null}],"properties":{"Node name for S&R":"ClownsharKSampler_Beta"},"widgets_values":[0.5,"multistep/res_3m","beta57",30,-1,1,4,0,"fixed","standard",true]},{"id":638,"type":"SharkOptions_GuideCond_Beta","pos":[955.9966430664062,585.7319946289062],"size":[284.5923156738281,98],"flags":{},"order":7,"mode":0,"inputs":[{"name":"positive","localized_name":"positive","type":"CONDITIONING","shape":7,"link":2035},{"name":"negative","localized_name":"negative","type":"CONDITIONING","shape":7,"link":2033},{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":null}],"outputs":[{"name":"options","localized_name":"options","type":"OPTIONS","links":[2032],"slot_index":0}],"properties":{"Node name for S&R":"SharkOptions_GuideCond_Beta"},"widgets_values":[4]},{"id":637,"type":"CLIPTextEncode","pos":[962.297607421875,99.93917846679688],"size":[278.4529113769531,88],"flags":{"collapsed":false},"order":4,"mode":0,"inputs":[{"name":"clip","localized_name":"clip","label":"clip","type":"CLIP","link":2028}],"outputs":[{"name":"CONDITIONING","localized_name":"CONDITIONING","label":"CONDITIONING","type":"CONDITIONING","shape":3,"links":[2029,2033],"slot_index":0}],"title":"Positive Prompt","properties":{"Node name for S&R":"CLIPTextEncode"},"widgets_values":["low quality, low detail, blurry, shallow depth of field, mutated, symmetrical, generic"]},{"id":107,"type":"CLIPTextEncode","pos":[959.4713745117188,-123.3353500366211],"size":[282.33453369140625,173.58438110351562],"flags":{"collapsed":false},"order":3,"mode":0,"inputs":[{"name":"clip","localized_name":"clip","label":"clip","type":"CLIP","link":2024}],"outputs":[{"name":"CONDITIONING","localized_name":"CONDITIONING","label":"CONDITIONING","type":"CONDITIONING","shape":3,"links":[2018],"slot_index":0}],"title":"Positive Prompt","properties":{"Node name for S&R":"CLIPTextEncode"},"widgets_values":["the mournful lamentations of of a female rock singer on stage with chaos behind her, her face screaming her sorrowful refrains the despairing cries of anguished screams howling agonized moans, her pained whispers mournful sighs distant echoes across the smoky stage, fading memories of lost loves, forgotten dreams, shattered hopes, crushed spirits, broken hearts"]},{"id":639,"type":"CLIPTextEncode","pos":[599.5145263671875,565.6756591796875],"size":[315.33026123046875,117.94475555419922],"flags":{"collapsed":false},"order":5,"mode":0,"inputs":[{"name":"clip","localized_name":"clip","label":"clip","type":"CLIP","link":2034}],"outputs":[{"name":"CONDITIONING","localized_name":"CONDITIONING","label":"CONDITIONING","type":"CONDITIONING","shape":3,"links":[2035],"slot_index":0}],"title":"Positive Prompt","properties":{"Node name for S&R":"CLIPTextEncode"},"widgets_values":["illustration of a singing clock with huge teeth in a surreal forest with torquiose mountains and a red and yellow sky, ragged trees and a pool of black oil on the ground, dripping paint oozing off the clock"]},{"id":640,"type":"Note","pos":[245.6206512451172,517.1527709960938],"size":[323.0928649902344,167.39759826660156],"flags":{},"order":2,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["With the \"flow\" mode it is usually beneficial to use the supplemental GuideCond node, which allows you to set conditionings for the guide itself. With \"flow\", the guide changes during the sampling process. Without GuideCond in use, it will default to reusing your main prompt, which may result in some loss of adherence to the guide image.\n\n\"Lure\" is the only other mode that will use GuideCond."],"color":"#432","bgcolor":"#653"},{"id":634,"type":"ClownGuide_Beta","pos":[1276.0064697265625,-480.84442138671875],"size":[284.860595703125,290.8609924316406],"flags":{},"order":8,"mode":0,"inputs":[{"name":"guide","localized_name":"guide","type":"LATENT","shape":7,"link":2020},{"name":"mask","localized_name":"mask","type":"MASK","shape":7,"link":null},{"name":"weights","localized_name":"weights","type":"SIGMAS","shape":7,"link":null}],"outputs":[{"name":"guides","localized_name":"guides","type":"GUIDES","links":[2021],"slot_index":0}],"properties":{"Node name for S&R":"ClownGuide_Beta"},"widgets_values":["lure",false,false,1,1,"linear_quadratic",0,13,false]}],"links":[[2013,629,0,630,3,"LATENT"],[2015,629,3,632,1,"LATENT"],[2016,632,0,630,0,"MODEL"],[2017,628,0,629,0,"IMAGE"],[2018,107,0,630,1,"CONDITIONING"],[2019,591,0,633,0,"IMAGE"],[2020,629,0,634,0,"LATENT"],[2021,634,0,630,5,"GUIDES"],[2024,636,1,107,0,"CLIP"],[2025,636,0,632,0,"MODEL"],[2026,636,2,629,4,"VAE"],[2027,636,2,591,1,"VAE"],[2028,636,1,637,0,"CLIP"],[2029,637,0,630,2,"CONDITIONING"],[2030,630,0,591,0,"LATENT"],[2032,638,0,630,6,"OPTIONS"],[2033,637,0,638,1,"CONDITIONING"],[2034,636,1,639,0,"CLIP"],[2035,639,0,638,0,"CONDITIONING"]],"groups":[],"config":{},"extra":{"ds":{"scale":1.7985878990923265,"offset":[1342.694620988285,531.4979770514516]},"VHS_latentpreview":false,"VHS_latentpreviewrate":0},"version":0.4} \ No newline at end of file diff --git a/ComfyUI/custom_nodes/RES4LYF/example_workflows/hidream guide pseudoimplicit.json b/ComfyUI/custom_nodes/RES4LYF/example_workflows/hidream guide pseudoimplicit.json new file mode 100644 index 0000000000000000000000000000000000000000..7b056120aff309c5e9d4c8a2164820040300aabe --- /dev/null +++ b/ComfyUI/custom_nodes/RES4LYF/example_workflows/hidream guide pseudoimplicit.json @@ -0,0 +1 @@ +{"last_node_id":641,"last_link_id":2035,"nodes":[{"id":628,"type":"LoadImage","pos":[599.166015625,156.38429260253906],"size":[315,314],"flags":{},"order":0,"mode":0,"inputs":[],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":[2017]},{"name":"MASK","localized_name":"MASK","type":"MASK","links":null}],"properties":{"Node name for S&R":"LoadImage"},"widgets_values":["ComfyUI_14254_.png","image"]},{"id":632,"type":"ModelSamplingAdvancedResolution","pos":[962.5586547851562,-316.3705139160156],"size":[277.62237548828125,126],"flags":{},"order":6,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","link":2025},{"name":"latent_image","localized_name":"latent_image","type":"LATENT","link":2015}],"outputs":[{"name":"model","localized_name":"model","type":"MODEL","links":[2016],"slot_index":0}],"properties":{"Node name for S&R":"ModelSamplingAdvancedResolution"},"widgets_values":["exponential",1.35,0.85]},{"id":636,"type":"ClownModelLoader","pos":[599.3463745117188,-176.31788635253906],"size":[315,266],"flags":{},"order":1,"mode":0,"inputs":[],"outputs":[{"name":"model","localized_name":"model","type":"MODEL","links":[2025],"slot_index":0},{"name":"clip","localized_name":"clip","type":"CLIP","links":[2024,2028],"slot_index":1},{"name":"vae","localized_name":"vae","type":"VAE","links":[2026,2027],"slot_index":2}],"properties":{"Node name for S&R":"ClownModelLoader"},"widgets_values":["hidream_i1_full_fp8.safetensors","fp8_e4m3fn","clip_l_hidream.safetensors","clip_g_hidream.safetensors","t5xxl_fp8_e4m3fn_scaled.safetensors","llama_3.1_8b_instruct_fp8_scaled.safetensors","hidream","ae.sft"]},{"id":591,"type":"VAEDecode","pos":[1610,-230],"size":[210,46],"flags":{"collapsed":false},"order":8,"mode":0,"inputs":[{"name":"samples","localized_name":"samples","label":"samples","type":"LATENT","link":2030},{"name":"vae","localized_name":"vae","label":"vae","type":"VAE","link":2027}],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","label":"IMAGE","type":"IMAGE","shape":3,"links":[2019],"slot_index":0}],"properties":{"Node name for S&R":"VAEDecode"},"widgets_values":[]},{"id":633,"type":"SaveImage","pos":[1610,-120],"size":[436.4179382324219,508.5302429199219],"flags":{},"order":9,"mode":0,"inputs":[{"name":"images","localized_name":"images","type":"IMAGE","link":2019}],"outputs":[],"properties":{},"widgets_values":["ComfyUI"]},{"id":629,"type":"VAEEncodeAdvanced","pos":[961.6965942382812,242.70477294921875],"size":[278.0284423828125,280.5834045410156],"flags":{},"order":4,"mode":0,"inputs":[{"name":"image_1","localized_name":"image_1","type":"IMAGE","shape":7,"link":2017},{"name":"image_2","localized_name":"image_2","type":"IMAGE","shape":7,"link":null},{"name":"mask","localized_name":"mask","type":"IMAGE","shape":7,"link":null},{"name":"latent","localized_name":"latent","type":"LATENT","shape":7,"link":null},{"name":"vae","localized_name":"vae","type":"VAE","shape":7,"link":2026}],"outputs":[{"name":"latent_1","localized_name":"latent_1","type":"LATENT","links":[2013,2020],"slot_index":0},{"name":"latent_2","localized_name":"latent_2","type":"LATENT","links":null},{"name":"mask","localized_name":"mask","type":"MASK","links":null},{"name":"empty_latent","localized_name":"empty_latent","type":"LATENT","links":[2015]},{"name":"width","localized_name":"width","type":"INT","links":null},{"name":"height","localized_name":"height","type":"INT","links":null}],"properties":{"Node name for S&R":"VAEEncodeAdvanced"},"widgets_values":["false",1024,1024,"red",false,"16_channels"]},{"id":630,"type":"ClownsharKSampler_Beta","pos":[1271.7001953125,-124.3408432006836],"size":[291.7499084472656,650],"flags":{},"order":7,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","shape":7,"link":2016},{"name":"positive","localized_name":"positive","type":"CONDITIONING","shape":7,"link":2018},{"name":"negative","localized_name":"negative","type":"CONDITIONING","shape":7,"link":2029},{"name":"latent_image","localized_name":"latent_image","type":"LATENT","shape":7,"link":2013},{"name":"sigmas","localized_name":"sigmas","type":"SIGMAS","shape":7,"link":null},{"name":"guides","localized_name":"guides","type":"GUIDES","shape":7,"link":2021},{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":null}],"outputs":[{"name":"output","localized_name":"output","type":"LATENT","links":[2030],"slot_index":0},{"name":"denoised","localized_name":"denoised","type":"LATENT","links":null},{"name":"options","localized_name":"options","type":"OPTIONS","links":null}],"properties":{"Node name for S&R":"ClownsharKSampler_Beta"},"widgets_values":[0.5,"multistep/res_3m","beta57",30,-1,1,4,0,"fixed","standard",true]},{"id":637,"type":"CLIPTextEncode","pos":[962.297607421875,99.93917846679688],"size":[278.4529113769531,88],"flags":{"collapsed":false},"order":3,"mode":0,"inputs":[{"name":"clip","localized_name":"clip","label":"clip","type":"CLIP","link":2028}],"outputs":[{"name":"CONDITIONING","localized_name":"CONDITIONING","label":"CONDITIONING","type":"CONDITIONING","shape":3,"links":[2029],"slot_index":0}],"title":"Positive Prompt","properties":{"Node name for S&R":"CLIPTextEncode"},"widgets_values":["low quality, low detail, blurry, shallow depth of field, mutated, symmetrical, generic"]},{"id":107,"type":"CLIPTextEncode","pos":[959.4713745117188,-123.3353500366211],"size":[282.33453369140625,173.58438110351562],"flags":{"collapsed":false},"order":2,"mode":0,"inputs":[{"name":"clip","localized_name":"clip","label":"clip","type":"CLIP","link":2024}],"outputs":[{"name":"CONDITIONING","localized_name":"CONDITIONING","label":"CONDITIONING","type":"CONDITIONING","shape":3,"links":[2018],"slot_index":0}],"title":"Positive Prompt","properties":{"Node name for S&R":"CLIPTextEncode"},"widgets_values":["the mournful lamentations of of a female rock singer on stage with chaos behind her, her face screaming her sorrowful refrains the despairing cries of anguished screams howling agonized moans, her pained whispers mournful sighs distant echoes across the smoky stage, fading memories of lost loves, forgotten dreams, shattered hopes, crushed spirits, broken hearts"]},{"id":634,"type":"ClownGuide_Beta","pos":[1276.0064697265625,-480.84442138671875],"size":[284.860595703125,290.8609924316406],"flags":{},"order":5,"mode":0,"inputs":[{"name":"guide","localized_name":"guide","type":"LATENT","shape":7,"link":2020},{"name":"mask","localized_name":"mask","type":"MASK","shape":7,"link":null},{"name":"weights","localized_name":"weights","type":"SIGMAS","shape":7,"link":null}],"outputs":[{"name":"guides","localized_name":"guides","type":"GUIDES","links":[2021],"slot_index":0}],"properties":{"Node name for S&R":"ClownGuide_Beta"},"widgets_values":["pseudoimplicit",false,false,0.1,1,"beta57",0,5,false]}],"links":[[2013,629,0,630,3,"LATENT"],[2015,629,3,632,1,"LATENT"],[2016,632,0,630,0,"MODEL"],[2017,628,0,629,0,"IMAGE"],[2018,107,0,630,1,"CONDITIONING"],[2019,591,0,633,0,"IMAGE"],[2020,629,0,634,0,"LATENT"],[2021,634,0,630,5,"GUIDES"],[2024,636,1,107,0,"CLIP"],[2025,636,0,632,0,"MODEL"],[2026,636,2,629,4,"VAE"],[2027,636,2,591,1,"VAE"],[2028,636,1,637,0,"CLIP"],[2029,637,0,630,2,"CONDITIONING"],[2030,630,0,591,0,"LATENT"]],"groups":[],"config":{},"extra":{"ds":{"scale":1.7985878990923265,"offset":[1182.8926069221118,636.9542766363238]},"VHS_latentpreview":false,"VHS_latentpreviewrate":0},"version":0.4} \ No newline at end of file diff --git a/ComfyUI/custom_nodes/RES4LYF/example_workflows/hidream hires fix.json b/ComfyUI/custom_nodes/RES4LYF/example_workflows/hidream hires fix.json new file mode 100644 index 0000000000000000000000000000000000000000..771dd9a23198bdd1757541adb0a36a12a000742c --- /dev/null +++ b/ComfyUI/custom_nodes/RES4LYF/example_workflows/hidream hires fix.json @@ -0,0 +1 @@ +{"last_node_id":1358,"last_link_id":3624,"nodes":[{"id":490,"type":"Reroute","pos":[13130,-70],"size":[75,26],"flags":{},"order":13,"mode":0,"inputs":[{"name":"","type":"*","link":3534}],"outputs":[{"name":"","type":"CLIP","links":[2881,3323],"slot_index":0}],"properties":{"showOutputText":false,"horizontal":false}},{"id":1317,"type":"ClownModelLoader","pos":[12770,-90],"size":[315,266],"flags":{},"order":0,"mode":0,"inputs":[],"outputs":[{"name":"model","localized_name":"model","type":"MODEL","links":[3539],"slot_index":0},{"name":"clip","localized_name":"clip","type":"CLIP","links":[3534],"slot_index":1},{"name":"vae","localized_name":"vae","type":"VAE","links":[3535],"slot_index":2}],"properties":{"Node name for S&R":"ClownModelLoader"},"widgets_values":["hidream_i1_full_fp8.safetensors","fp8_e4m3fn_fast","clip_l_hidream.safetensors","clip_g_hidream.safetensors","t5xxl_fp16.safetensors","llama_3.1_8b_instruct_fp8_scaled.safetensors","hidream","ae.sft"]},{"id":7,"type":"VAEEncodeAdvanced","pos":[13253.044921875,283.4559020996094],"size":[261.2217712402344,279.3136901855469],"flags":{},"order":18,"mode":0,"inputs":[{"name":"image_1","localized_name":"image_1","type":"IMAGE","shape":7,"link":null},{"name":"image_2","localized_name":"image_2","type":"IMAGE","shape":7,"link":null},{"name":"mask","localized_name":"mask","type":"IMAGE","shape":7,"link":null},{"name":"latent","localized_name":"latent","type":"LATENT","shape":7,"link":null},{"name":"vae","localized_name":"vae","type":"VAE","shape":7,"link":18}],"outputs":[{"name":"latent_1","localized_name":"latent_1","type":"LATENT","links":[],"slot_index":0},{"name":"latent_2","localized_name":"latent_2","type":"LATENT","links":[],"slot_index":1},{"name":"mask","localized_name":"mask","type":"MASK","links":[],"slot_index":2},{"name":"empty_latent","localized_name":"empty_latent","type":"LATENT","links":[3540],"slot_index":3},{"name":"width","localized_name":"width","type":"INT","links":null},{"name":"height","localized_name":"height","type":"INT","links":null}],"properties":{"Node name for S&R":"VAEEncodeAdvanced","cnr_id":"RES4LYF","ver":"5ce9b5a77c227bf864e447a1e65305bf6cada5c2"},"widgets_values":["false",1536,768,"red",false,"16_channels"]},{"id":13,"type":"Reroute","pos":[13130,-110],"size":[75,26],"flags":{},"order":12,"mode":0,"inputs":[{"name":"","type":"*","link":3539}],"outputs":[{"name":"","type":"MODEL","links":[3548,3597],"slot_index":0}],"properties":{"showOutputText":false,"horizontal":false}},{"id":1224,"type":"CLIPTextEncode","pos":[13250,-90],"size":[269.0397644042969,155.65545654296875],"flags":{"collapsed":false},"order":17,"mode":0,"inputs":[{"name":"clip","localized_name":"clip","type":"CLIP","link":3323}],"outputs":[{"name":"CONDITIONING","localized_name":"CONDITIONING","type":"CONDITIONING","links":[3480,3599],"slot_index":0}],"properties":{"Node name for S&R":"CLIPTextEncode","cnr_id":"comfy-core","ver":"0.3.29"},"widgets_values":["a cold war era photograph from 1983 of a group of four friends holding up their hands inside an antique living room in a victorian era mansion"]},{"id":970,"type":"CLIPTextEncode","pos":[13253.0546875,116.28263854980469],"size":[261.8798522949219,111.21334838867188],"flags":{},"order":16,"mode":0,"inputs":[{"name":"clip","localized_name":"clip","type":"CLIP","link":2881}],"outputs":[{"name":"CONDITIONING","localized_name":"CONDITIONING","type":"CONDITIONING","links":[2882,3600],"slot_index":0}],"properties":{"Node name for S&R":"CLIPTextEncode","cnr_id":"comfy-core","ver":"0.3.29"},"widgets_values":["blurry, out of focus, shallow depth of field, low quality, bad quality, low detail, mutated, jpeg artifacts, compression artifacts,"]},{"id":14,"type":"Reroute","pos":[13130,-30],"size":[75,26],"flags":{},"order":14,"mode":0,"inputs":[{"name":"","type":"*","link":3535}],"outputs":[{"name":"","type":"VAE","links":[18,2696],"slot_index":0}],"properties":{"showOutputText":false,"horizontal":false}},{"id":1322,"type":"ClownsharkChainsampler_Beta","pos":[14503.9365234375,-99.09358978271484],"size":[281.6568603515625,542.124755859375],"flags":{},"order":24,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","shape":7,"link":null},{"name":"positive","localized_name":"positive","type":"CONDITIONING","shape":7,"link":null},{"name":"negative","localized_name":"negative","type":"CONDITIONING","shape":7,"link":null},{"name":"sigmas","localized_name":"sigmas","type":"SIGMAS","shape":7,"link":null},{"name":"latent_image","localized_name":"latent_image","type":"LATENT","shape":7,"link":3612},{"name":"guides","localized_name":"guides","type":"GUIDES","shape":7,"link":null},{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":3610},{"name":"options 2","type":"OPTIONS","link":null}],"outputs":[{"name":"output","localized_name":"output","type":"LATENT","links":[3550],"slot_index":0},{"name":"denoised","localized_name":"denoised","type":"LATENT","links":null},{"name":"options","localized_name":"options","type":"OPTIONS","links":null}],"properties":{"Node name for S&R":"ClownsharkChainsampler_Beta"},"widgets_values":[0.5,"multistep/res_3m",-1,4,"resample",true]},{"id":1350,"type":"ClownOptions_Tile_Beta","pos":[14700,540],"size":[210,82],"flags":{},"order":19,"mode":0,"inputs":[{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":3614}],"outputs":[{"name":"options","localized_name":"options","type":"OPTIONS","links":[3615],"slot_index":0}],"properties":{"Node name for S&R":"ClownOptions_Tile_Beta"},"widgets_values":[1216,832]},{"id":1351,"type":"ClownOptions_Tile_Beta","pos":[14940,540],"size":[210,82],"flags":{},"order":21,"mode":0,"inputs":[{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":3615}],"outputs":[{"name":"options","localized_name":"options","type":"OPTIONS","links":[],"slot_index":0}],"properties":{"Node name for S&R":"ClownOptions_Tile_Beta"},"widgets_values":[1152,896]},{"id":1349,"type":"ClownOptions_Tile_Beta","pos":[14470,540],"size":[210,82],"flags":{},"order":15,"mode":0,"inputs":[{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":3616}],"outputs":[{"name":"options","localized_name":"options","type":"OPTIONS","links":[3614],"slot_index":0}],"properties":{"Node name for S&R":"ClownOptions_Tile_Beta"},"widgets_values":[1536,768]},{"id":1352,"type":"ClownOptions_Tile_Beta","pos":[14233.716796875,538.3314819335938],"size":[210,82],"flags":{},"order":1,"mode":0,"inputs":[{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":null}],"outputs":[{"name":"options","localized_name":"options","type":"OPTIONS","links":[3616],"slot_index":0}],"properties":{"Node name for S&R":"ClownOptions_Tile_Beta"},"widgets_values":[2048,1024]},{"id":1353,"type":"ClownOptions_Tile_Beta","pos":[14232.0498046875,680.947998046875],"size":[210,82],"flags":{},"order":2,"mode":0,"inputs":[{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":null}],"outputs":[{"name":"options","localized_name":"options","type":"OPTIONS","links":[],"slot_index":0}],"properties":{"Node name for S&R":"ClownOptions_Tile_Beta"},"widgets_values":[-1,-1]},{"id":1354,"type":"Note","pos":[14476.6044921875,675.5231323242188],"size":[258.67279052734375,88],"flags":{},"order":3,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["As with the rest of RES4LYF nodes, \"-1\" means \"go to the end\" or \"max value\". In this case, that means \"use full image sizes\". So, the node to the left will be equivalent to the one above."],"color":"#432","bgcolor":"#653"},{"id":907,"type":"ClownsharKSampler_Beta","pos":[13550.5615234375,-92.92960357666016],"size":[301.752197265625,657.727294921875],"flags":{},"order":20,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","shape":7,"link":3548},{"name":"positive","localized_name":"positive","type":"CONDITIONING","shape":7,"link":3480},{"name":"negative","localized_name":"negative","type":"CONDITIONING","shape":7,"link":2882},{"name":"latent_image","localized_name":"latent_image","type":"LATENT","shape":7,"link":3540},{"name":"sigmas","localized_name":"sigmas","type":"SIGMAS","shape":7,"link":null},{"name":"guides","localized_name":"guides","type":"GUIDES","shape":7,"link":null},{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":null}],"outputs":[{"name":"output","localized_name":"output","type":"LATENT","links":[3618],"slot_index":0},{"name":"denoised","localized_name":"denoised","type":"LATENT","links":[],"slot_index":1},{"name":"options","localized_name":"options","type":"OPTIONS","links":null}],"properties":{"Node name for S&R":"ClownsharKSampler_Beta","cnr_id":"RES4LYF","ver":"5ce9b5a77c227bf864e447a1e65305bf6cada5c2"},"widgets_values":[0.5,"multistep/res_3m","bong_tangent",30,15,1,4,4,"fixed","standard",true]},{"id":1355,"type":"LatentUpscale","pos":[13877.537109375,-92.35859680175781],"size":[286.32501220703125,130],"flags":{},"order":22,"mode":0,"inputs":[{"name":"samples","localized_name":"samples","type":"LATENT","link":3618}],"outputs":[{"name":"LATENT","localized_name":"LATENT","type":"LATENT","links":[3619],"slot_index":0}],"properties":{"Node name for S&R":"LatentUpscale"},"widgets_values":["nearest-exact",2048,1024,"disabled"]},{"id":1345,"type":"ClownOptions_Tile_Beta","pos":[13953.123046875,285.76708984375],"size":[210,82],"flags":{},"order":4,"mode":0,"inputs":[{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":null}],"outputs":[{"name":"options","localized_name":"options","type":"OPTIONS","links":[3609,3610],"slot_index":0}],"properties":{"Node name for S&R":"ClownOptions_Tile_Beta"},"widgets_values":[1536,768]},{"id":909,"type":"SaveImage","pos":[14811.001953125,-99.0184555053711],"size":[457.3382263183594,422.2065124511719],"flags":{},"order":26,"mode":0,"inputs":[{"name":"images","localized_name":"images","type":"IMAGE","link":2697}],"outputs":[],"properties":{"Node name for S&R":"SaveImage","cnr_id":"comfy-core","ver":"0.3.29"},"widgets_values":["ComfyUI"]},{"id":908,"type":"VAEDecode","pos":[14808.998046875,-201.5235595703125],"size":[140,46],"flags":{},"order":25,"mode":0,"inputs":[{"name":"samples","localized_name":"samples","type":"LATENT","link":3550},{"name":"vae","localized_name":"vae","type":"VAE","link":2696}],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":[2697],"slot_index":0}],"properties":{"Node name for S&R":"VAEDecode","cnr_id":"comfy-core","ver":"0.3.29"},"widgets_values":[]},{"id":1356,"type":"Note","pos":[12793.412109375,-250.5360870361328],"size":[276.617431640625,88],"flags":{},"order":5,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["Want to use this workflow with another model? Just hook up a different model! You may need to set CFG = 1.0 if you're going to use a distilled model, such as HiDream Dev (or Fast) or Flux Dev."],"color":"#432","bgcolor":"#653"},{"id":1321,"type":"Note","pos":[12769.740234375,239.9431915283203],"size":[345.97113037109375,161.35496520996094],"flags":{},"order":6,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["There are many samplers to try, but res_2m, res_3m, res_2s, and res_3s are very reliable. If you want to push quality a bit higher in exchange for time, you could even try res_5s.\n\nres_2m and res_3m begin with higher order steps (one res_2s step, and two res_3s steps, respectively) to initialize the sampling process. Ultimately, the result is faster convergence in terms of wall time, as fewer steps end up being necessary."],"color":"#432","bgcolor":"#653"},{"id":1347,"type":"Note","pos":[13505.927734375,-326.1947937011719],"size":[348.3962097167969,172.26731872558594],"flags":{},"order":7,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["Connect \"Upscale Latent\" directly to the last chainsampler to skip the iterative refinement steps (which is what implicit steps are: they use the output of a step as the input, then re-run it to refine). They help minimize mutations with a \"hires fix\" workflow like this.\n\n\"rebound\" is the highest quality implicit_type, but is also slightly slower.\n\nYou may also use ClownOptions Cycles instead of ClownOptions Implicit Steps."],"color":"#432","bgcolor":"#653"},{"id":1346,"type":"Note","pos":[13898.4658203125,421.6622314453125],"size":[261.7038269042969,363.83868408203125],"flags":{},"order":8,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["If you use tiled sampling, be sure to choose tile sizes that will need to overlap each other, or you might see seams. For example, for 2048x1024, it would be unwise to choose 1024x1024 or 512x512 as your only tile size, as 2048 / 1024 = 1.0, 2048 / 512 = 4.0, etc.\n\nThis workflow will upscale to 2048x1024. 2048 is not divisible by 1536, and 1024 is not divisible by 768, thereofer they will have overlapping areas.\n\nIt's best to pick tile sizes that you know the model is trained at, with which you can generate txt2img without hallucination, doubling, mutations, \"grid\" artifacts, etc.\n\nTiled sampling will be slower, but can prevent drifts in luminosity, hue, artifacts around the edge of the image, and mutations, while reducing VRAM use. However, it can also cause parts of the image to look \"out of sync\". You can alternate tile sizes like shown to the right, which can sometimes help."],"color":"#432","bgcolor":"#653"},{"id":1324,"type":"ClownsharkChainsampler_Beta","pos":[14189.3935546875,-89.69397735595703],"size":[285.5440673828125,552.053955078125],"flags":{},"order":23,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","shape":7,"link":3597},{"name":"positive","localized_name":"positive","type":"CONDITIONING","shape":7,"link":3599},{"name":"negative","localized_name":"negative","type":"CONDITIONING","shape":7,"link":3600},{"name":"sigmas","localized_name":"sigmas","type":"SIGMAS","shape":7,"link":null},{"name":"latent_image","localized_name":"latent_image","type":"LATENT","shape":7,"link":3619},{"name":"guides","localized_name":"guides","type":"GUIDES","shape":7,"link":null},{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":3624},{"name":"options 2","type":"OPTIONS","link":3609},{"name":"options 3","type":"OPTIONS","link":null}],"outputs":[{"name":"output","localized_name":"output","type":"LATENT","links":[3612],"slot_index":0},{"name":"denoised","localized_name":"denoised","type":"LATENT","links":null},{"name":"options","localized_name":"options","type":"OPTIONS","links":null}],"properties":{"Node name for S&R":"ClownsharkChainsampler_Beta"},"widgets_values":[0.5,"multistep/res_3m",1,4,"resample",true]},{"id":1325,"type":"ClownOptions_ImplicitSteps_Beta","pos":[13884.9677734375,94.86456298828125],"size":[278.0316467285156,130],"flags":{},"order":9,"mode":0,"inputs":[{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":null}],"outputs":[{"name":"options","localized_name":"options","type":"OPTIONS","links":[],"slot_index":0}],"properties":{"Node name for S&R":"ClownOptions_ImplicitSteps_Beta"},"widgets_values":["rebound","bongmath",10,0]},{"id":1357,"type":"Note","pos":[14184.4599609375,-302.5225830078125],"size":[305.0502014160156,150.26080322265625],"flags":{},"order":10,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["The sampling will appear to froze for a minute at this node, but it is not actually frozen. Reducing implicit_steps or cycles will speed things up.\n\nIf you are willing to use a slower sampler to improve quality, the biggest bang for your buck will be with this first chainsampler. Try changing the sampler_name to res_3s, or gauss-legendre_2s.\n"],"color":"#432","bgcolor":"#653"},{"id":1358,"type":"ClownOptions_Cycles_Beta","pos":[13880.7060546875,-310.925537109375],"size":[280.4444274902344,154],"flags":{},"order":11,"mode":0,"inputs":[{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":null}],"outputs":[{"name":"options","localized_name":"options","type":"OPTIONS","links":[3624],"slot_index":0}],"properties":{"Node name for S&R":"ClownOptions_Cycles_Beta"},"widgets_values":[6,1,0.5,"none",4]}],"links":[[18,14,0,7,4,"VAE"],[2696,14,0,908,1,"VAE"],[2697,908,0,909,0,"IMAGE"],[2881,490,0,970,0,"CLIP"],[2882,970,0,907,2,"CONDITIONING"],[3323,490,0,1224,0,"CLIP"],[3480,1224,0,907,1,"CONDITIONING"],[3534,1317,1,490,0,"*"],[3535,1317,2,14,0,"*"],[3539,1317,0,13,0,"*"],[3540,7,3,907,3,"LATENT"],[3548,13,0,907,0,"MODEL"],[3550,1322,0,908,0,"LATENT"],[3597,13,0,1324,0,"MODEL"],[3599,1224,0,1324,1,"CONDITIONING"],[3600,970,0,1324,2,"CONDITIONING"],[3609,1345,0,1324,7,"OPTIONS"],[3610,1345,0,1322,6,"OPTIONS"],[3612,1324,0,1322,4,"LATENT"],[3614,1349,0,1350,0,"OPTIONS"],[3615,1350,0,1351,0,"OPTIONS"],[3616,1352,0,1349,0,"OPTIONS"],[3618,907,0,1355,0,"LATENT"],[3619,1355,0,1324,4,"LATENT"],[3624,1358,0,1324,6,"OPTIONS"]],"groups":[],"config":{},"extra":{"ds":{"scale":1.9194342495775452,"offset":[-11744.076730306608,403.1731222243355]},"VHS_latentpreview":false,"VHS_latentpreviewrate":0,"ue_links":[],"VHS_MetadataImage":true,"VHS_KeepIntermediate":true},"version":0.4} \ No newline at end of file diff --git a/ComfyUI/custom_nodes/RES4LYF/example_workflows/hidream style antiblur.json b/ComfyUI/custom_nodes/RES4LYF/example_workflows/hidream style antiblur.json new file mode 100644 index 0000000000000000000000000000000000000000..57a11056a97939171e09c418df82f00dedb88122 --- /dev/null +++ b/ComfyUI/custom_nodes/RES4LYF/example_workflows/hidream style antiblur.json @@ -0,0 +1 @@ +{"last_node_id":742,"last_link_id":2119,"nodes":[{"id":13,"type":"Reroute","pos":[1280,-650],"size":[75,26],"flags":{},"order":7,"mode":0,"inputs":[{"name":"","type":"*","link":2115}],"outputs":[{"name":"","type":"MODEL","links":[1967],"slot_index":0}],"properties":{"showOutputText":false,"horizontal":false}},{"id":490,"type":"Reroute","pos":[1280,-610],"size":[75,26],"flags":{},"order":5,"mode":0,"inputs":[{"name":"","type":"*","link":2116}],"outputs":[{"name":"","type":"CLIP","links":[1939,2119],"slot_index":0}],"properties":{"showOutputText":false,"horizontal":false}},{"id":14,"type":"Reroute","pos":[1280,-570],"size":[75,26],"flags":{},"order":6,"mode":0,"inputs":[{"name":"","type":"*","link":2117}],"outputs":[{"name":"","type":"VAE","links":[18,1328],"slot_index":0}],"properties":{"showOutputText":false,"horizontal":false}},{"id":398,"type":"SaveImage","pos":[1379.9996337890625,-267.2835998535156],"size":[341.7508850097656,561.0067749023438],"flags":{},"order":14,"mode":0,"inputs":[{"name":"images","localized_name":"images","type":"IMAGE","link":1329}],"outputs":[],"properties":{"Node name for S&R":"SaveImage","cnr_id":"comfy-core","ver":"0.3.29"},"widgets_values":["ComfyUI"]},{"id":397,"type":"VAEDecode","pos":[1382.3662109375,-374.17059326171875],"size":[210,46],"flags":{},"order":13,"mode":0,"inputs":[{"name":"samples","localized_name":"samples","type":"LATENT","link":2096},{"name":"vae","localized_name":"vae","type":"VAE","link":1328}],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":[1329],"slot_index":0}],"properties":{"Node name for S&R":"VAEDecode","cnr_id":"comfy-core","ver":"0.3.29"},"widgets_values":[]},{"id":7,"type":"VAEEncodeAdvanced","pos":[412.2475280761719,-199.0681915283203],"size":[261.2217712402344,279.3136901855469],"flags":{},"order":10,"mode":0,"inputs":[{"name":"image_1","localized_name":"image_1","type":"IMAGE","shape":7,"link":2113},{"name":"image_2","localized_name":"image_2","type":"IMAGE","shape":7,"link":null},{"name":"mask","localized_name":"mask","type":"IMAGE","shape":7,"link":null},{"name":"latent","localized_name":"latent","type":"LATENT","shape":7,"link":null},{"name":"vae","localized_name":"vae","type":"VAE","shape":7,"link":18}],"outputs":[{"name":"latent_1","localized_name":"latent_1","type":"LATENT","links":[2100],"slot_index":0},{"name":"latent_2","localized_name":"latent_2","type":"LATENT","links":[],"slot_index":1},{"name":"mask","localized_name":"mask","type":"MASK","links":[],"slot_index":2},{"name":"empty_latent","localized_name":"empty_latent","type":"LATENT","links":[1399],"slot_index":3},{"name":"width","localized_name":"width","type":"INT","links":null},{"name":"height","localized_name":"height","type":"INT","links":null}],"properties":{"Node name for S&R":"VAEEncodeAdvanced","cnr_id":"RES4LYF","ver":"5ce9b5a77c227bf864e447a1e65305bf6cada5c2"},"widgets_values":["false",1024,1024,"red",false,"16_channels"]},{"id":662,"type":"CLIPTextEncode","pos":[761.3005981445312,-357.2689208984375],"size":[210,102.54972839355469],"flags":{"collapsed":false},"order":8,"mode":0,"inputs":[{"name":"clip","localized_name":"clip","type":"CLIP","link":1939}],"outputs":[{"name":"CONDITIONING","localized_name":"CONDITIONING","type":"CONDITIONING","links":[2098],"slot_index":0}],"properties":{"Node name for S&R":"CLIPTextEncode","cnr_id":"comfy-core","ver":"0.3.29"},"widgets_values":["a woman wearing a red flannel shirt and a cute shark plush blue hat, a college campus, brick buildings"]},{"id":727,"type":"Note","pos":[412.8926086425781,-351.8606872558594],"size":[272.4425048828125,88],"flags":{},"order":0,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["This approach can be combined with the regional conditioning anti-blur approach for an even more powerful effect."],"color":"#432","bgcolor":"#653"},{"id":724,"type":"ClownGuide_Style_Beta","pos":[703.7374267578125,-198.63233947753906],"size":[262.8634033203125,286],"flags":{},"order":11,"mode":0,"inputs":[{"name":"guide","localized_name":"guide","type":"LATENT","shape":7,"link":2100},{"name":"mask","localized_name":"mask","type":"MASK","shape":7,"link":null},{"name":"weights","localized_name":"weights","type":"SIGMAS","shape":7,"link":null},{"name":"guides","localized_name":"guides","type":"GUIDES","shape":7,"link":null}],"outputs":[{"name":"guides","localized_name":"guides","type":"GUIDES","links":[2099],"slot_index":0}],"properties":{"Node name for S&R":"ClownGuide_Style_Beta"},"widgets_values":["positive","WCT",1,1,"constant",0,10,false]},{"id":739,"type":"LoadImage","pos":[70.82455444335938,-201.66342163085938],"size":[315,314],"flags":{},"order":1,"mode":0,"inputs":[],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":[2113],"slot_index":0},{"name":"MASK","localized_name":"MASK","type":"MASK","links":null}],"properties":{"Node name for S&R":"LoadImage"},"widgets_values":["pasted/image (655).png","image"]},{"id":741,"type":"ReHiDreamPatcher","pos":[1000,-680],"size":[210,82],"flags":{},"order":4,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","link":2114}],"outputs":[{"name":"model","localized_name":"model","type":"MODEL","links":[2115],"slot_index":0}],"properties":{"Node name for S&R":"ReHiDreamPatcher"},"widgets_values":["float64",true]},{"id":740,"type":"ClownModelLoader","pos":[650,-680],"size":[315,266],"flags":{},"order":2,"mode":0,"inputs":[],"outputs":[{"name":"model","localized_name":"model","type":"MODEL","links":[2114],"slot_index":0},{"name":"clip","localized_name":"clip","type":"CLIP","links":[2116],"slot_index":1},{"name":"vae","localized_name":"vae","type":"VAE","links":[2117],"slot_index":2}],"properties":{"Node name for S&R":"ClownModelLoader"},"widgets_values":["hidream_i1_full_fp8.safetensors","fp8_e4m3fn_fast","clip_l_hidream.safetensors","clip_g_hidream.safetensors","t5xxl_fp8_e4m3fn_scaled.safetensors","llama_3.1_8b_instruct_fp8_scaled.safetensors","hidream","ae.sft"]},{"id":401,"type":"ClownsharKSampler_Beta","pos":[1010,-370],"size":[340.55120849609375,666.8208618164062],"flags":{},"order":12,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","shape":7,"link":1967},{"name":"positive","localized_name":"positive","type":"CONDITIONING","shape":7,"link":2098},{"name":"negative","localized_name":"negative","type":"CONDITIONING","shape":7,"link":2118},{"name":"latent_image","localized_name":"latent_image","type":"LATENT","shape":7,"link":1399},{"name":"sigmas","localized_name":"sigmas","type":"SIGMAS","shape":7,"link":null},{"name":"guides","localized_name":"guides","type":"GUIDES","shape":7,"link":2099},{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":null}],"outputs":[{"name":"output","localized_name":"output","type":"LATENT","links":[2096],"slot_index":0},{"name":"denoised","localized_name":"denoised","type":"LATENT","links":null},{"name":"options","localized_name":"options","type":"OPTIONS","links":null}],"properties":{"Node name for S&R":"ClownsharKSampler_Beta","cnr_id":"RES4LYF","ver":"5ce9b5a77c227bf864e447a1e65305bf6cada5c2"},"widgets_values":[0.5,"multistep/res_2m","bong_tangent",30,-1,1,4,7,"fixed","standard",true]},{"id":742,"type":"CLIPTextEncode","pos":[703.5707397460938,144.26979064941406],"size":[261.8798522949219,111.21334838867188],"flags":{},"order":9,"mode":0,"inputs":[{"name":"clip","localized_name":"clip","type":"CLIP","link":2119}],"outputs":[{"name":"CONDITIONING","localized_name":"CONDITIONING","type":"CONDITIONING","links":[2118],"slot_index":0}],"properties":{"Node name for S&R":"CLIPTextEncode","cnr_id":"comfy-core","ver":"0.3.29"},"widgets_values":["blurry, out of focus, shallow depth of field, low quality, bad quality, low detail, mutated, jpeg artifacts, compression artifacts,"]},{"id":726,"type":"Note","pos":[305.74163818359375,169.59754943847656],"size":[364.5906677246094,164.38613891601562],"flags":{},"order":3,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["The best style guide images will share the lighting and color composition of your desired scene. Some are just inexplicably ineffective at killing blur. Just gather up a bunch of images to try, you'll find some good ones that can be reused for many things. I'm including the one used here in the example_workflows directory, be sure to check for it.\n\nAnd don't forget to change seeds. Don't optimize for one seed only. Don't get stuck on one seed! Sometimes one is just not going to work out for whatever you're doing."],"color":"#432","bgcolor":"#653"}],"links":[[18,14,0,7,4,"VAE"],[1328,14,0,397,1,"VAE"],[1329,397,0,398,0,"IMAGE"],[1399,7,3,401,3,"LATENT"],[1939,490,0,662,0,"CLIP"],[1967,13,0,401,0,"MODEL"],[2096,401,0,397,0,"LATENT"],[2098,662,0,401,1,"CONDITIONING"],[2099,724,0,401,5,"GUIDES"],[2100,7,0,724,0,"LATENT"],[2113,739,0,7,0,"IMAGE"],[2114,740,0,741,0,"MODEL"],[2115,741,0,13,0,"*"],[2116,740,1,490,0,"*"],[2117,740,2,14,0,"*"],[2118,742,0,401,2,"CONDITIONING"],[2119,490,0,742,0,"CLIP"]],"groups":[],"config":{},"extra":{"ds":{"scale":1.7449402268886909,"offset":[1731.8135682982838,807.2501654184575]},"VHS_latentpreview":false,"VHS_latentpreviewrate":0,"ue_links":[],"VHS_MetadataImage":true,"VHS_KeepIntermediate":true},"version":0.4} \ No newline at end of file diff --git a/ComfyUI/custom_nodes/RES4LYF/example_workflows/hidream style transfer.json b/ComfyUI/custom_nodes/RES4LYF/example_workflows/hidream style transfer.json new file mode 100644 index 0000000000000000000000000000000000000000..b403a7046a630f6aed800bf6b02684d31d000513 --- /dev/null +++ b/ComfyUI/custom_nodes/RES4LYF/example_workflows/hidream style transfer.json @@ -0,0 +1 @@ +{"last_node_id":1317,"last_link_id":3533,"nodes":[{"id":13,"type":"Reroute","pos":[13140,110],"size":[75,26],"flags":{},"order":11,"mode":0,"inputs":[{"name":"","type":"*","link":3509}],"outputs":[{"name":"","type":"MODEL","links":[1395],"slot_index":0}],"properties":{"showOutputText":false,"horizontal":false}},{"id":402,"type":"QuadrupleCLIPLoader","pos":[12690,150],"size":[407.7720031738281,130],"flags":{},"order":0,"mode":0,"inputs":[],"outputs":[{"name":"CLIP","localized_name":"CLIP","type":"CLIP","links":[1552],"slot_index":0}],"properties":{"Node name for S&R":"QuadrupleCLIPLoader","cnr_id":"comfy-core","ver":"0.3.29"},"widgets_values":["clip_l_hidream.safetensors","clip_g_hidream.safetensors","t5xxl_fp8_e4m3fn_scaled.safetensors","llama_3.1_8b_instruct_fp8_scaled.safetensors"]},{"id":490,"type":"Reroute","pos":[13140,150],"size":[75,26],"flags":{},"order":6,"mode":0,"inputs":[{"name":"","type":"*","link":1552}],"outputs":[{"name":"","type":"CLIP","links":[2881,3323],"slot_index":0}],"properties":{"showOutputText":false,"horizontal":false}},{"id":981,"type":"ClownsharkChainsampler_Beta","pos":[14277.9453125,-92.8893051147461],"size":[340.20001220703125,510],"flags":{},"order":17,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","shape":7,"link":null},{"name":"positive","localized_name":"positive","type":"CONDITIONING","shape":7,"link":null},{"name":"negative","localized_name":"negative","type":"CONDITIONING","shape":7,"link":null},{"name":"sigmas","localized_name":"sigmas","type":"SIGMAS","shape":7,"link":null},{"name":"latent_image","localized_name":"latent_image","type":"LATENT","shape":7,"link":3250},{"name":"guides","localized_name":"guides","type":"GUIDES","shape":7,"link":null},{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":null}],"outputs":[{"name":"output","localized_name":"output","type":"LATENT","links":[3469],"slot_index":0},{"name":"denoised","localized_name":"denoised","type":"LATENT","links":null},{"name":"options","localized_name":"options","type":"OPTIONS","links":null}],"properties":{"Node name for S&R":"ClownsharkChainsampler_Beta"},"widgets_values":[0.5,"exponential/res_2s",-1,4,"resample",true]},{"id":908,"type":"VAEDecode","pos":[14640.490234375,-94.68604278564453],"size":[210,46],"flags":{},"order":18,"mode":0,"inputs":[{"name":"samples","localized_name":"samples","type":"LATENT","link":3469},{"name":"vae","localized_name":"vae","type":"VAE","link":2696}],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":[2697],"slot_index":0}],"properties":{"Node name for S&R":"VAEDecode","cnr_id":"comfy-core","ver":"0.3.29"},"widgets_values":[]},{"id":909,"type":"SaveImage","pos":[14635.966796875,4.407815933227539],"size":[457.3382263183594,422.2065124511719],"flags":{},"order":19,"mode":0,"inputs":[{"name":"images","localized_name":"images","type":"IMAGE","link":2697}],"outputs":[],"properties":{"Node name for S&R":"SaveImage","cnr_id":"comfy-core","ver":"0.3.29"},"widgets_values":["ComfyUI"]},{"id":431,"type":"ModelSamplingAdvancedResolution","pos":[13253.2275390625,-90.14451599121094],"size":[260.3999938964844,126],"flags":{},"order":14,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","link":1395},{"name":"latent_image","localized_name":"latent_image","type":"LATENT","link":1398}],"outputs":[{"name":"model","localized_name":"model","type":"MODEL","links":[2692],"slot_index":0}],"properties":{"Node name for S&R":"ModelSamplingAdvancedResolution","cnr_id":"RES4LYF","ver":"5ce9b5a77c227bf864e447a1e65305bf6cada5c2"},"widgets_values":["exponential",1.35,0.85]},{"id":14,"type":"Reroute","pos":[13140,190],"size":[75,26],"flags":{},"order":8,"mode":0,"inputs":[{"name":"","type":"*","link":1344}],"outputs":[{"name":"","type":"VAE","links":[18,2696],"slot_index":0}],"properties":{"showOutputText":false,"horizontal":false}},{"id":403,"type":"UNETLoader","pos":[12780,20],"size":[320.7802429199219,82],"flags":{},"order":1,"mode":0,"inputs":[],"outputs":[{"name":"MODEL","localized_name":"MODEL","type":"MODEL","links":[3508],"slot_index":0}],"properties":{"Node name for S&R":"UNETLoader","cnr_id":"comfy-core","ver":"0.3.29"},"widgets_values":["hidream_i1_full_fp8.safetensors","fp8_e4m3fn"]},{"id":404,"type":"VAELoader","pos":[12887.7998046875,328.069091796875],"size":[210,58],"flags":{},"order":2,"mode":0,"inputs":[],"outputs":[{"name":"VAE","localized_name":"VAE","type":"VAE","links":[1344],"slot_index":0}],"properties":{"Node name for S&R":"VAELoader","cnr_id":"comfy-core","ver":"0.3.29"},"widgets_values":["ae.sft"]},{"id":1308,"type":"ClownGuide_Style_Beta","pos":[13637.08984375,660.7327270507812],"size":[246.31312561035156,286],"flags":{},"order":13,"mode":0,"inputs":[{"name":"guide","localized_name":"guide","type":"LATENT","shape":7,"link":3531},{"name":"mask","localized_name":"mask","type":"MASK","shape":7,"link":null},{"name":"weights","localized_name":"weights","type":"SIGMAS","shape":7,"link":null},{"name":"guides","localized_name":"guides","type":"GUIDES","shape":7,"link":null}],"outputs":[{"name":"guides","localized_name":"guides","type":"GUIDES","links":[3530],"slot_index":0}],"properties":{"Node name for S&R":"ClownGuide_Style_Beta"},"widgets_values":["positive","WCT",1,1,"constant",0,-1,false]},{"id":980,"type":"ClownsharkChainsampler_Beta","pos":[13918.0234375,-98.65141296386719],"size":[340.20001220703125,570],"flags":{},"order":16,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","shape":7,"link":null},{"name":"positive","localized_name":"positive","type":"CONDITIONING","shape":7,"link":null},{"name":"negative","localized_name":"negative","type":"CONDITIONING","shape":7,"link":null},{"name":"sigmas","localized_name":"sigmas","type":"SIGMAS","shape":7,"link":null},{"name":"latent_image","localized_name":"latent_image","type":"LATENT","shape":7,"link":2971},{"name":"guides","localized_name":"guides","type":"GUIDES","shape":7,"link":3530},{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":3533},{"name":"options 2","type":"OPTIONS","link":null}],"outputs":[{"name":"output","localized_name":"output","type":"LATENT","links":[3250],"slot_index":0},{"name":"denoised","localized_name":"denoised","type":"LATENT","links":null},{"name":"options","localized_name":"options","type":"OPTIONS","links":null}],"properties":{"Node name for S&R":"ClownsharkChainsampler_Beta"},"widgets_values":[0.5,"exponential/res_2s",1,4,"resample",true]},{"id":7,"type":"VAEEncodeAdvanced","pos":[13250.6240234375,672.3837890625],"size":[261.2217712402344,279.3136901855469],"flags":{},"order":12,"mode":0,"inputs":[{"name":"image_1","localized_name":"image_1","type":"IMAGE","shape":7,"link":3515},{"name":"image_2","localized_name":"image_2","type":"IMAGE","shape":7,"link":3532},{"name":"mask","localized_name":"mask","type":"IMAGE","shape":7,"link":null},{"name":"latent","localized_name":"latent","type":"LATENT","shape":7,"link":null},{"name":"vae","localized_name":"vae","type":"VAE","shape":7,"link":18}],"outputs":[{"name":"latent_1","localized_name":"latent_1","type":"LATENT","links":[2983],"slot_index":0},{"name":"latent_2","localized_name":"latent_2","type":"LATENT","links":[3531],"slot_index":1},{"name":"mask","localized_name":"mask","type":"MASK","links":[],"slot_index":2},{"name":"empty_latent","localized_name":"empty_latent","type":"LATENT","links":[1398],"slot_index":3},{"name":"width","localized_name":"width","type":"INT","links":null},{"name":"height","localized_name":"height","type":"INT","links":null}],"properties":{"Node name for S&R":"VAEEncodeAdvanced","cnr_id":"RES4LYF","ver":"5ce9b5a77c227bf864e447a1e65305bf6cada5c2"},"widgets_values":["false",896,1152,"red",false,"16_channels"]},{"id":1285,"type":"LoadImage","pos":[12887.7626953125,444.2932434082031],"size":[315,314],"flags":{},"order":3,"mode":0,"inputs":[],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":[3515],"slot_index":0},{"name":"MASK","localized_name":"MASK","type":"MASK","links":null}],"properties":{"Node name for S&R":"LoadImage"},"widgets_values":["pasted/image (544).png","image"]},{"id":907,"type":"ClownsharKSampler_Beta","pos":[13550.5615234375,-92.92960357666016],"size":[340.55120849609375,666.8208618164062],"flags":{},"order":15,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","shape":7,"link":2692},{"name":"positive","localized_name":"positive","type":"CONDITIONING","shape":7,"link":3480},{"name":"negative","localized_name":"negative","type":"CONDITIONING","shape":7,"link":2882},{"name":"latent_image","localized_name":"latent_image","type":"LATENT","shape":7,"link":2983},{"name":"sigmas","localized_name":"sigmas","type":"SIGMAS","shape":7,"link":null},{"name":"guides","localized_name":"guides","type":"GUIDES","shape":7,"link":null},{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":null}],"outputs":[{"name":"output","localized_name":"output","type":"LATENT","links":[2971],"slot_index":0},{"name":"denoised","localized_name":"denoised","type":"LATENT","links":null},{"name":"options","localized_name":"options","type":"OPTIONS","links":null}],"properties":{"Node name for S&R":"ClownsharKSampler_Beta","cnr_id":"RES4LYF","ver":"5ce9b5a77c227bf864e447a1e65305bf6cada5c2"},"widgets_values":[0.5,"exponential/res_2s","beta57",20,14,1,4,201,"fixed","unsample",true]},{"id":1309,"type":"LoadImage","pos":[12889.3486328125,815.3554077148438],"size":[315,314],"flags":{},"order":4,"mode":0,"inputs":[],"outputs":[{"name":"IMAGE","localized_name":"IMAGE","type":"IMAGE","links":[3532],"slot_index":0},{"name":"MASK","localized_name":"MASK","type":"MASK","links":null}],"properties":{"Node name for S&R":"LoadImage"},"widgets_values":["ChatGPT Image Apr 29, 2025, 09_18_46 PM.png","image"]},{"id":1297,"type":"ReHiDreamPatcher","pos":[12779.865234375,-110.67424774169922],"size":[321.6453552246094,82],"flags":{},"order":7,"mode":0,"inputs":[{"name":"model","localized_name":"model","type":"MODEL","link":3508}],"outputs":[{"name":"model","localized_name":"model","type":"MODEL","links":[3509],"slot_index":0}],"properties":{"Node name for S&R":"ReHiDreamPatcher","cnr_id":"RES4LYF","ver":"5ce9b5a77c227bf864e447a1e65305bf6cada5c2"},"widgets_values":["float32",true]},{"id":1224,"type":"CLIPTextEncode","pos":[13247.2734375,95.37741088867188],"size":[269.0397644042969,155.65545654296875],"flags":{"collapsed":false},"order":10,"mode":0,"inputs":[{"name":"clip","localized_name":"clip","type":"CLIP","link":3323}],"outputs":[{"name":"CONDITIONING","localized_name":"CONDITIONING","type":"CONDITIONING","links":[3480],"slot_index":0}],"properties":{"Node name for S&R":"CLIPTextEncode","cnr_id":"comfy-core","ver":"0.3.29"},"widgets_values":["a gritty illustration of a japanese woman with traditional hair in traditional clothes"]},{"id":970,"type":"CLIPTextEncode","pos":[13257.970703125,316.4944152832031],"size":[261.8798522949219,111.21334838867188],"flags":{},"order":9,"mode":0,"inputs":[{"name":"clip","localized_name":"clip","type":"CLIP","link":2881}],"outputs":[{"name":"CONDITIONING","localized_name":"CONDITIONING","type":"CONDITIONING","links":[2882],"slot_index":0}],"properties":{"Node name for S&R":"CLIPTextEncode","cnr_id":"comfy-core","ver":"0.3.29"},"widgets_values":["blurry, out of focus, shallow depth of field, low quality, bad quality, low detail, mutated, jpeg artifacts, compression artifacts,"]},{"id":1317,"type":"ClownOptions_Cycles_Beta","pos":[13959.880859375,541.2625122070312],"size":[265.2884826660156,178],"flags":{},"order":5,"mode":0,"inputs":[{"name":"options","localized_name":"options","type":"OPTIONS","shape":7,"link":null}],"outputs":[{"name":"options","localized_name":"options","type":"OPTIONS","links":[3533],"slot_index":0}],"properties":{"Node name for S&R":"ClownOptions_Cycles_Beta"},"widgets_values":[20,1,0.5,"none",-1,4]}],"links":[[18,14,0,7,4,"VAE"],[1344,404,0,14,0,"*"],[1395,13,0,431,0,"MODEL"],[1398,7,3,431,1,"LATENT"],[1552,402,0,490,0,"*"],[2692,431,0,907,0,"MODEL"],[2696,14,0,908,1,"VAE"],[2697,908,0,909,0,"IMAGE"],[2881,490,0,970,0,"CLIP"],[2882,970,0,907,2,"CONDITIONING"],[2971,907,0,980,4,"LATENT"],[2983,7,0,907,3,"LATENT"],[3250,980,0,981,4,"LATENT"],[3323,490,0,1224,0,"CLIP"],[3469,981,0,908,0,"LATENT"],[3480,1224,0,907,1,"CONDITIONING"],[3508,403,0,1297,0,"MODEL"],[3509,1297,0,13,0,"*"],[3515,1285,0,7,0,"IMAGE"],[3530,1308,0,980,5,"GUIDES"],[3531,7,1,1308,0,"LATENT"],[3532,1309,0,7,1,"IMAGE"],[3533,1317,0,980,6,"OPTIONS"]],"groups":[],"config":{},"extra":{"ds":{"scale":1.7398859252302459,"offset":[-10583.206320408986,234.77974623579652]},"VHS_latentpreview":false,"VHS_latentpreviewrate":0,"ue_links":[],"VHS_MetadataImage":true,"VHS_KeepIntermediate":true},"version":0.4} \ No newline at end of file diff --git a/ComfyUI/custom_nodes/RES4LYF/legacy/flux/controlnet.py b/ComfyUI/custom_nodes/RES4LYF/legacy/flux/controlnet.py new file mode 100644 index 0000000000000000000000000000000000000000..c033dea52f2e677c57797c17677cb9072fbb04e5 --- /dev/null +++ b/ComfyUI/custom_nodes/RES4LYF/legacy/flux/controlnet.py @@ -0,0 +1,205 @@ +#Original code can be found on: https://github.com/XLabs-AI/x-flux/blob/main/src/flux/controlnet.py +#modified to support different types of flux controlnets + +import torch +import math +from torch import Tensor, nn +from einops import rearrange, repeat + +from .layers import (DoubleStreamBlock, EmbedND, LastLayer, + MLPEmbedder, SingleStreamBlock, + timestep_embedding) + +from .model import Flux +import comfy.ldm.common_dit + +class MistolineCondDownsamplBlock(nn.Module): + def __init__(self, dtype=None, device=None, operations=None): + super().__init__() + self.encoder = nn.Sequential( + operations.Conv2d(3, 16, 3, padding=1, dtype=dtype, device=device), + nn.SiLU(), + operations.Conv2d(16, 16, 1, dtype=dtype, device=device), + nn.SiLU(), + operations.Conv2d(16, 16, 3, padding=1, dtype=dtype, device=device), + nn.SiLU(), + operations.Conv2d(16, 16, 3, padding=1, stride=2, dtype=dtype, device=device), + nn.SiLU(), + operations.Conv2d(16, 16, 3, padding=1, dtype=dtype, device=device), + nn.SiLU(), + operations.Conv2d(16, 16, 3, padding=1, stride=2, dtype=dtype, device=device), + nn.SiLU(), + operations.Conv2d(16, 16, 3, padding=1, dtype=dtype, device=device), + nn.SiLU(), + operations.Conv2d(16, 16, 3, padding=1, stride=2, dtype=dtype, device=device), + nn.SiLU(), + operations.Conv2d(16, 16, 1, dtype=dtype, device=device), + nn.SiLU(), + operations.Conv2d(16, 16, 3, padding=1, dtype=dtype, device=device) + ) + + def forward(self, x): + return self.encoder(x) + +class MistolineControlnetBlock(nn.Module): + def __init__(self, hidden_size, dtype=None, device=None, operations=None): + super().__init__() + self.linear = operations.Linear(hidden_size, hidden_size, dtype=dtype, device=device) + self.act = nn.SiLU() + + def forward(self, x): + return self.act(self.linear(x)) + + +class ControlNetFlux(Flux): + def __init__(self, latent_input=False, num_union_modes=0, mistoline=False, control_latent_channels=None, image_model=None, dtype=None, device=None, operations=None, **kwargs): + super().__init__(final_layer=False, dtype=dtype, device=device, operations=operations, **kwargs) + + self.main_model_double = 19 + self.main_model_single = 38 + + self.mistoline = mistoline + # add ControlNet blocks + if self.mistoline: + control_block = lambda : MistolineControlnetBlock(self.hidden_size, dtype=dtype, device=device, operations=operations) + else: + control_block = lambda : operations.Linear(self.hidden_size, self.hidden_size, dtype=dtype, device=device) + + self.controlnet_blocks = nn.ModuleList([]) + for _ in range(self.params.depth): + self.controlnet_blocks.append(control_block()) + + self.controlnet_single_blocks = nn.ModuleList([]) + for _ in range(self.params.depth_single_blocks): + self.controlnet_single_blocks.append(control_block()) + + self.num_union_modes = num_union_modes + self.controlnet_mode_embedder = None + if self.num_union_modes > 0: + self.controlnet_mode_embedder = operations.Embedding(self.num_union_modes, self.hidden_size, dtype=dtype, device=device) + + self.gradient_checkpointing = False + self.latent_input = latent_input + if control_latent_channels is None: + control_latent_channels = self.in_channels + else: + control_latent_channels *= 2 * 2 #patch size + + self.pos_embed_input = operations.Linear(control_latent_channels, self.hidden_size, bias=True, dtype=dtype, device=device) + if not self.latent_input: + if self.mistoline: + self.input_cond_block = MistolineCondDownsamplBlock(dtype=dtype, device=device, operations=operations) + else: + self.input_hint_block = nn.Sequential( + operations.Conv2d(3, 16, 3, padding=1, dtype=dtype, device=device), + nn.SiLU(), + operations.Conv2d(16, 16, 3, padding=1, dtype=dtype, device=device), + nn.SiLU(), + operations.Conv2d(16, 16, 3, padding=1, stride=2, dtype=dtype, device=device), + nn.SiLU(), + operations.Conv2d(16, 16, 3, padding=1, dtype=dtype, device=device), + nn.SiLU(), + operations.Conv2d(16, 16, 3, padding=1, stride=2, dtype=dtype, device=device), + nn.SiLU(), + operations.Conv2d(16, 16, 3, padding=1, dtype=dtype, device=device), + nn.SiLU(), + operations.Conv2d(16, 16, 3, padding=1, stride=2, dtype=dtype, device=device), + nn.SiLU(), + operations.Conv2d(16, 16, 3, padding=1, dtype=dtype, device=device) + ) + + def forward_orig( + self, + img: Tensor, + img_ids: Tensor, + controlnet_cond: Tensor, + txt: Tensor, + txt_ids: Tensor, + timesteps: Tensor, + y: Tensor, + guidance: Tensor = None, + control_type: Tensor = None, + ) -> Tensor: + if img.ndim != 3 or txt.ndim != 3: + raise ValueError("Input img and txt tensors must have 3 dimensions.") + + # running on sequences img + img = self.img_in(img) + + controlnet_cond = self.pos_embed_input(controlnet_cond) + img = img + controlnet_cond + vec = self.time_in(timestep_embedding(timesteps, 256)) + if self.params.guidance_embed: + vec = vec + self.guidance_in(timestep_embedding(guidance, 256)) + vec = vec + self.vector_in(y) + txt = self.txt_in(txt) + + if self.controlnet_mode_embedder is not None and len(control_type) > 0: + control_cond = self.controlnet_mode_embedder(torch.tensor(control_type, device=img.device), out_dtype=img.dtype).unsqueeze(0).repeat((txt.shape[0], 1, 1)) + txt = torch.cat([control_cond, txt], dim=1) + txt_ids = torch.cat([txt_ids[:,:1], txt_ids], dim=1) + + ids = torch.cat((txt_ids, img_ids), dim=1) + pe = self.pe_embedder(ids) + + controlnet_double = () + + for i in range(len(self.double_blocks)): + img, txt = self.double_blocks[i](img=img, txt=txt, vec=vec, pe=pe) + controlnet_double = controlnet_double + (self.controlnet_blocks[i](img),) + + img = torch.cat((txt, img), 1) + + controlnet_single = () + + for i in range(len(self.single_blocks)): + img = self.single_blocks[i](img, vec=vec, pe=pe) + controlnet_single = controlnet_single + (self.controlnet_single_blocks[i](img[:, txt.shape[1] :, ...]),) + + repeat = math.ceil(self.main_model_double / len(controlnet_double)) + if self.latent_input: + out_input = () + for x in controlnet_double: + out_input += (x,) * repeat + else: + out_input = (controlnet_double * repeat) + + out = {"input": out_input[:self.main_model_double]} + if len(controlnet_single) > 0: + repeat = math.ceil(self.main_model_single / len(controlnet_single)) + out_output = () + if self.latent_input: + for x in controlnet_single: + out_output += (x,) * repeat + else: + out_output = (controlnet_single * repeat) + out["output"] = out_output[:self.main_model_single] + return out + + def forward(self, x, timesteps, context, y, guidance=None, hint=None, **kwargs): + patch_size = 2 + if self.latent_input: + hint = comfy.ldm.common_dit.pad_to_patch_size(hint, (patch_size, patch_size)) + elif self.mistoline: + hint = hint * 2.0 - 1.0 + hint = self.input_cond_block(hint) + else: + hint = hint * 2.0 - 1.0 + hint = self.input_hint_block(hint) + + hint = rearrange(hint, "b c (h ph) (w pw) -> b (h w) (c ph pw)", ph=patch_size, pw=patch_size) + + bs, c, h, w = x.shape + x = comfy.ldm.common_dit.pad_to_patch_size(x, (patch_size, patch_size)) + + img = rearrange(x, "b c (h ph) (w pw) -> b (h w) (c ph pw)", ph=patch_size, pw=patch_size) + + h_len = ((h + (patch_size // 2)) // patch_size) + w_len = ((w + (patch_size // 2)) // patch_size) + img_ids = torch.zeros((h_len, w_len, 3), device=x.device, dtype=x.dtype) + img_ids[..., 1] = img_ids[..., 1] + torch.linspace(0, h_len - 1, steps=h_len, device=x.device, dtype=x.dtype)[:, None] + img_ids[..., 2] = img_ids[..., 2] + torch.linspace(0, w_len - 1, steps=w_len, device=x.device, dtype=x.dtype)[None, :] + img_ids = repeat(img_ids, "h w c -> b (h w) c", b=bs) + + txt_ids = torch.zeros((bs, context.shape[1], 3), device=x.device, dtype=x.dtype) + return self.forward_orig(img, img_ids, hint, context, txt_ids, timesteps, y, guidance, control_type=kwargs.get("control_type", [])) diff --git a/ComfyUI/custom_nodes/RES4LYF/legacy/flux/layers.py b/ComfyUI/custom_nodes/RES4LYF/legacy/flux/layers.py new file mode 100644 index 0000000000000000000000000000000000000000..017dfa6fb8408898c3976032dab57721058ae185 --- /dev/null +++ b/ComfyUI/custom_nodes/RES4LYF/legacy/flux/layers.py @@ -0,0 +1,288 @@ +# Adapted from: https://github.com/black-forest-labs/flux + +import math +import torch +from torch import Tensor, nn + +import torch.nn.functional as F +from einops import rearrange +from torch import Tensor +from dataclasses import dataclass + +from .math import attention, rope, apply_rope +import comfy.ldm.common_dit + +class EmbedND(nn.Module): + def __init__(self, dim: int, theta: int, axes_dim: list): + super().__init__() + self.dim = dim + self.theta = theta + self.axes_dim = axes_dim + + def forward(self, ids: Tensor) -> Tensor: + n_axes = ids.shape[-1] + emb = torch.cat( + [rope(ids[..., i], self.axes_dim[i], self.theta) for i in range(n_axes)], + dim=-3, + ) + + return emb.unsqueeze(1) + +def attention_weights(q, k): + # implementation of in-place softmax to reduce memory req + scores = torch.matmul(q, k.transpose(-2, -1)) + scores.div_(math.sqrt(q.size(-1))) + torch.exp(scores, out=scores) + summed = torch.sum(scores, dim=-1, keepdim=True) + scores /= summed + return scores.nan_to_num_(0.0, 65504., -65504.) + +def timestep_embedding(t: Tensor, dim, max_period=10000, time_factor: float = 1000.0): + """ + Create sinusoidal timestep embeddings. + :param t: a 1-D Tensor of N indices, one per batch element. + These may be fractional. + :param dim: the dimension of the output. + :param max_period: controls the minimum frequency of the embeddings. + :return: an (N, D) Tensor of positional embeddings. + """ + t = time_factor * t + half = dim // 2 + freqs = torch.exp(-math.log(max_period) * torch.arange(start=0, end=half, dtype=torch.float32, device=t.device) / half) + + args = t[:, None].float() * freqs[None] + embedding = torch.cat([torch.cos(args), torch.sin(args)], dim=-1) + if dim % 2: + embedding = torch.cat([embedding, torch.zeros_like(embedding[:, :1])], dim=-1) + if torch.is_floating_point(t): + embedding = embedding.to(t) + return embedding + +class MLPEmbedder(nn.Module): + def __init__(self, in_dim: int, hidden_dim: int, dtype=None, device=None, operations=None): + super().__init__() + self.in_layer = operations.Linear( in_dim, hidden_dim, bias=True, dtype=dtype, device=device) + self.silu = nn.SiLU() + self.out_layer = operations.Linear(hidden_dim, hidden_dim, bias=True, dtype=dtype, device=device) + + def forward(self, x: Tensor) -> Tensor: + return self.out_layer(self.silu(self.in_layer(x))) + + +class RMSNorm(torch.nn.Module): + def __init__(self, dim: int, dtype=None, device=None, operations=None): + super().__init__() + self.scale = nn.Parameter(torch.empty((dim), dtype=dtype, device=device)) # self.scale.shape = 128 + + def forward(self, x: Tensor): + return comfy.ldm.common_dit.rms_norm(x, self.scale, 1e-6) + + +class QKNorm(torch.nn.Module): + def __init__(self, dim: int, dtype=None, device=None, operations=None): + super().__init__() + self.query_norm = RMSNorm(dim, dtype=dtype, device=device, operations=operations) + self.key_norm = RMSNorm(dim, dtype=dtype, device=device, operations=operations) + + def forward(self, q: Tensor, k: Tensor, v: Tensor) -> tuple: + q = self.query_norm(q) + k = self.key_norm(k) + return q.to(v), k.to(v) + + +class SelfAttention(nn.Module): + def __init__(self, dim: int, num_heads: int = 8, qkv_bias: bool = False, dtype=None, device=None, operations=None): + super().__init__() + self.num_heads = num_heads # 24 + head_dim = dim // num_heads # 128 = 3072 / 24 + + self.qkv = operations.Linear(dim, dim * 3, bias=qkv_bias, dtype=dtype, device=device) + self.norm = QKNorm(head_dim, dtype=dtype, device=device, operations=operations) + self.proj = operations.Linear(dim, dim, dtype=dtype, device=device) # dim is usually 3072 + + +@dataclass +class ModulationOut: + shift: Tensor + scale: Tensor + gate: Tensor + +class Modulation(nn.Module): + def __init__(self, dim: int, double: bool, dtype=None, device=None, operations=None): + super().__init__() + self.is_double = double + self.multiplier = 6 if double else 3 + self.lin = operations.Linear(dim, self.multiplier * dim, bias=True, dtype=dtype, device=device) + + def forward(self, vec: Tensor) -> tuple: + out = self.lin(nn.functional.silu(vec))[:, None, :].chunk(self.multiplier, dim=-1) + return (ModulationOut(*out[:3]), ModulationOut(*out[3:]) if self.is_double else None,) + + +class DoubleStreamBlock(nn.Module): + def __init__(self, hidden_size: int, num_heads: int, mlp_ratio: float, qkv_bias: bool = False, dtype=None, device=None, operations=None, idx=-1): + super().__init__() + + self.idx = idx + + mlp_hidden_dim = int(hidden_size * mlp_ratio) + self.num_heads = num_heads + self.hidden_size = hidden_size + + self.img_mod = Modulation(hidden_size, double=True, dtype=dtype, device=device, operations=operations) # in_features=3072, out_features=18432 (3072*6) + self.txt_mod = Modulation(hidden_size, double=True, dtype=dtype, device=device, operations=operations) # in_features=3072, out_features=18432 (3072*6) + + self.img_attn = SelfAttention(dim=hidden_size, num_heads=num_heads, qkv_bias=qkv_bias, dtype=dtype, device=device, operations=operations) # .qkv: in_features=3072, out_features=9216 .proj: 3072,3072 + self.txt_attn = SelfAttention(dim=hidden_size, num_heads=num_heads, qkv_bias=qkv_bias, dtype=dtype, device=device, operations=operations) # .qkv: in_features=3072, out_features=9216 .proj: 3072,3072 + + self.img_norm1 = operations.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6, dtype=dtype, device=device) + self.txt_norm1 = operations.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6, dtype=dtype, device=device) + + self.img_norm2 = operations.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6, dtype=dtype, device=device) + self.txt_norm2 = operations.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6, dtype=dtype, device=device) + + self.img_mlp = nn.Sequential( + operations.Linear(hidden_size, mlp_hidden_dim, bias=True, dtype=dtype, device=device), + nn.GELU(approximate="tanh"), + operations.Linear(mlp_hidden_dim, hidden_size, bias=True, dtype=dtype, device=device), + ) # 3072->12288, 12288->3072 (3072*4) + self.txt_mlp = nn.Sequential( + operations.Linear(hidden_size, mlp_hidden_dim, bias=True, dtype=dtype, device=device), + nn.GELU(approximate="tanh"), + operations.Linear(mlp_hidden_dim, hidden_size, bias=True, dtype=dtype, device=device), + ) # 3072->12288, 12288->3072 (3072*4) + + def img_attn_preproc(self, img, img_mod1): + img_modulated = self.img_norm1(img) + img_modulated = (1 + img_mod1.scale) * img_modulated + img_mod1.shift + img_qkv = self.img_attn.qkv(img_modulated) + img_q, img_k, img_v = rearrange(img_qkv, "B L (K H D) -> K B H L D", K=3, H=self.num_heads) + img_q, img_k = self.img_attn.norm(img_q, img_k, img_v) + return img_q, img_k, img_v + + def txt_attn_preproc(self, txt, txt_mod1): + txt_modulated = self.txt_norm1(txt) + txt_modulated = (1 + txt_mod1.scale) * txt_modulated + txt_mod1.shift + txt_qkv = self.txt_attn.qkv(txt_modulated) + txt_q, txt_k, txt_v = rearrange(txt_qkv, "B L (K H D) -> K B H L D", K=3, H=self.num_heads) # Batch SeqLen (9216==3*3072) -> 3*1 24 SeqLen 128 + txt_q, txt_k = self.txt_attn.norm(txt_q, txt_k, txt_v) + return txt_q, txt_k, txt_v + + def forward(self, img: Tensor, txt: Tensor, vec: Tensor, pe: Tensor, timestep, transformer_options={}, mask=None, weight=1): # vec 1,3072 + + img_mod1, img_mod2 = self.img_mod(vec) # -> 3072, 3072 + txt_mod1, txt_mod2 = self.txt_mod(vec) + + img_q, img_k, img_v = self.img_attn_preproc(img, img_mod1) + txt_q, txt_k, txt_v = self.txt_attn_preproc(txt, txt_mod1) + + q, k, v = torch.cat((txt_q, img_q), dim=2), torch.cat((txt_k, img_k), dim=2), torch.cat((txt_v, img_v), dim=2) + + """if mask is None: + attn = attention(q, k, v, pe=pe) + else: + attn_false = attention(q, k, v, pe=pe) + attn = attention(q, k, v, pe=pe, mask=mask.to(torch.bool)) + attn = attn_false + weight * (attn - attn_false)""" + + #I = torch.eye(q.shape[-2], q.shape[-2], dtype=q.dtype, device=q.device).expand((1,1) + (-1, -1)) + #attn_map = attention_weights(q, k) + """mask_resized = None + if mask is not None: + txt_a = txt[:,:,:] + txt_qa, txt_ka, txt_va = self.txt_attn_preproc(txt_a, txt_mod1) + + txt_q_rope, txt_k_rope = apply_rope(txt_q, txt_k, pe[:,:,:512,:,:]) + img_q_rope, img_k_rope = apply_rope(img_q, img_k, pe[:,:,512:,:,:]) + + attn_weights = attention_weights(txt_q_rope, img_k_rope) + attn_weights = attn_weights.permute(0,1,3,2) + attn_weights_slice = attn_weights[:,:,:,:] + test = attn_weights_slice.mean(dim=1) + test2 = rearrange(test, "b (h w) (c ph pw) -> b c (h ph) (w pw)", h=64, w=64, ph=1, pw=1) + test3 = test2.mean(dim=1) + mask_resized = F.interpolate(test3[None,:,:,:], size=(1024,1024), mode='bilinear', align_corners=False).squeeze(1)""" + + attn = attention(q, k, v, pe=pe, mask=mask) + txt_attn = attn[:, :txt.shape[1]] # 1, 768,3072 + img_attn = attn[:, txt.shape[1]:] + + img += img_mod1.gate * self.img_attn.proj(img_attn) + txt += txt_mod1.gate * self.txt_attn.proj(txt_attn) + + img += img_mod2.gate * self.img_mlp((1 + img_mod2.scale) * self.img_norm2(img) + img_mod2.shift) + txt += txt_mod2.gate * self.txt_mlp((1 + txt_mod2.scale) * self.txt_norm2(txt) + txt_mod2.shift) + + return img, txt #, mask_resized + + + +class SingleStreamBlock(nn.Module): + """ + A DiT block with parallel linear layers as described in + https://arxiv.org/abs/2302.05442 and adapted modulation interface. + """ + def __init__(self, hidden_size: int, num_heads: int, mlp_ratio: float = 4.0, qk_scale: float = None, dtype=None, device=None, operations=None, idx=-1): + super().__init__() + self.idx = idx + self.hidden_dim = hidden_size #3072 + self.num_heads = num_heads #24 + head_dim = hidden_size // num_heads + self.scale = qk_scale or head_dim**-0.5 #0.08838834764831845 + + self.mlp_hidden_dim = int(hidden_size * mlp_ratio) #12288 == 3072 * 4 + # qkv and mlp_in + self.linear1 = operations.Linear(hidden_size, hidden_size * 3 + self.mlp_hidden_dim, dtype=dtype, device=device) + # proj and mlp_out + self.linear2 = operations.Linear(hidden_size + self.mlp_hidden_dim, hidden_size, dtype=dtype, device=device) + + self.norm = QKNorm(head_dim, dtype=dtype, device=device, operations=operations) + + self.hidden_size = hidden_size #3072 + self.pre_norm = operations.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6, dtype=dtype, device=device) + + self.mlp_act = nn.GELU(approximate="tanh") + self.modulation = Modulation(hidden_size, double=False, dtype=dtype, device=device, operations=operations) + + def img_attn(self, img, mod, pe, mask, weight): + img_mod = (1 + mod.scale) * self.pre_norm(img) + mod.shift # mod => vec + qkv, mlp = torch.split(self.linear1(img_mod), [3 * self.hidden_size, self.mlp_hidden_dim], dim=-1) + + q, k, v = rearrange(qkv, "B L (K H D) -> K B H L D", K=3, H=self.num_heads) + q, k = self.norm(q, k, v) + + """if mask is None: + attn = attention(q, k, v, pe=pe) + else: + attn_false = attention(q, k, v, pe=pe) + attn = attention(q, k, v, pe=pe, mask=mask.to(torch.bool)) + attn = attn_false + weight * (attn - attn_false)""" + + attn = attention(q, k, v, pe=pe, mask=mask) + return attn, mlp + + # vec 1,3072 x 1,9984,3072 + def forward(self, img: Tensor, vec: Tensor, pe: Tensor, timestep, transformer_options={}, mask=None, weight=1) -> Tensor: # x 1,9984,3072 if 2 reg embeds, 1,9472,3072 if none # 9216x4096 = 16x1536x1536 + mod, _ = self.modulation(vec) + attn, mlp = self.img_attn(img, mod, pe, mask, weight) + output = self.linear2(torch.cat((attn, self.mlp_act(mlp)), 2)) + + img += mod.gate * output + return img + + + +class LastLayer(nn.Module): + def __init__(self, hidden_size: int, patch_size: int, out_channels: int, dtype=None, device=None, operations=None): + super().__init__() + self.norm_final = operations.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6, 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)) + + def forward(self, x: Tensor, vec: Tensor) -> Tensor: + shift, scale = self.adaLN_modulation(vec).chunk(2, dim=1) + x = (1 + scale[:, None, :]) * self.norm_final(x) + shift[:, None, :] + x = self.linear(x) + return x + + diff --git a/ComfyUI/custom_nodes/RES4LYF/legacy/flux/math.py b/ComfyUI/custom_nodes/RES4LYF/legacy/flux/math.py new file mode 100644 index 0000000000000000000000000000000000000000..249a76a2e7a78184f5b1354bab8fabdfda047a6e --- /dev/null +++ b/ComfyUI/custom_nodes/RES4LYF/legacy/flux/math.py @@ -0,0 +1,37 @@ +import torch +from einops import rearrange +from torch import Tensor +from comfy.ldm.modules.attention import optimized_attention +import comfy.model_management + +def attention(q: Tensor, k: Tensor, v: Tensor, pe: Tensor, mask=None) -> Tensor: + q, k = apply_rope(q, k, pe) + + heads = q.shape[1] + x = optimized_attention(q, k, v, heads, skip_reshape=True, mask=mask) + return x + + +def rope(pos: Tensor, dim: int, theta: int) -> Tensor: + assert dim % 2 == 0 + if comfy.model_management.is_device_mps(pos.device) or comfy.model_management.is_intel_xpu(): + device = torch.device("cpu") + else: + device = pos.device + + scale = torch.linspace(0, (dim - 2) / dim, steps=dim//2, dtype=torch.float64, device=device) + omega = 1.0 / (theta**scale) + out = torch.einsum("...n,d->...nd", pos.to(dtype=torch.float32, device=device), omega) + out = torch.stack([torch.cos(out), -torch.sin(out), torch.sin(out), torch.cos(out)], dim=-1) + out = rearrange(out, "b n d (i j) -> b n d i j", i=2, j=2) + return out.to(dtype=torch.float32, device=pos.device) + + +def apply_rope(xq: Tensor, xk: Tensor, freqs_cis: Tensor): + xq_ = xq.float().reshape(*xq.shape[:-1], -1, 1, 2) + xk_ = xk.float().reshape(*xk.shape[:-1], -1, 1, 2) + xq_out = freqs_cis[..., 0] * xq_[..., 0] + freqs_cis[..., 1] * xq_[..., 1] + xk_out = freqs_cis[..., 0] * xk_[..., 0] + freqs_cis[..., 1] * xk_[..., 1] + return xq_out.reshape(*xq.shape).type_as(xq), xk_out.reshape(*xk.shape).type_as(xk) + + diff --git a/ComfyUI/custom_nodes/RES4LYF/legacy/flux/model.py b/ComfyUI/custom_nodes/RES4LYF/legacy/flux/model.py new file mode 100644 index 0000000000000000000000000000000000000000..f8fd2151d0541298f47b2c34d235c64bd5023f6b --- /dev/null +++ b/ComfyUI/custom_nodes/RES4LYF/legacy/flux/model.py @@ -0,0 +1,204 @@ +# Adapted from: https://github.com/black-forest-labs/flux + +import torch +from torch import Tensor, nn +from dataclasses import dataclass +import copy + +from .layers import ( + DoubleStreamBlock, + EmbedND, + LastLayer, + MLPEmbedder, + SingleStreamBlock, + timestep_embedding, +) + +from comfy.ldm.flux.layers import timestep_embedding +from comfy.ldm.flux.model import Flux as Flux + +from einops import rearrange, repeat +import comfy.ldm.common_dit + +@dataclass +class FluxParams: + in_channels: int + out_channels: int + vec_in_dim: int + context_in_dim: int + hidden_size: int + mlp_ratio: float + num_heads: int + depth: int + depth_single_blocks: int + axes_dim: list + theta: int + patch_size: int + qkv_bias: bool + guidance_embed: bool + +class ReFlux(Flux): + def __init__(self, image_model=None, final_layer=True, dtype=None, device=None, operations=None, **kwargs): + super().__init__() + self.dtype = dtype + self.timestep = -1.0 + self.threshold_inv = False + params = FluxParams(**kwargs) + + self.params = params #self.params FluxParams(in_channels=16, out_channels=16, vec_in_dim=768, context_in_dim=4096, hidden_size=3072, mlp_ratio=4.0, num_heads=24, depth=19, depth_single_blocks=38, axes_dim=[16, 56, 56], theta=10000, patch_size=2, qkv_bias=True, guidance_embed=False) + self.patch_size = params.patch_size + self.in_channels = params.in_channels * params.patch_size * params.patch_size # in_channels 64 + self.out_channels = params.out_channels * params.patch_size * params.patch_size # out_channels 64 + + if params.hidden_size % params.num_heads != 0: + raise ValueError(f"Hidden size {params.hidden_size} must be divisible by num_heads {params.num_heads}") + pe_dim = params.hidden_size // params.num_heads + if sum(params.axes_dim) != pe_dim: + raise ValueError(f"Got {params.axes_dim} but expected positional dim {pe_dim}") + + self.hidden_size = params.hidden_size # 3072 + self.num_heads = params.num_heads # 24 + self.pe_embedder = EmbedND(dim=pe_dim, theta=params.theta, axes_dim=params.axes_dim) + + self.img_in = operations.Linear( self.in_channels, self.hidden_size, bias=True, dtype=dtype, device=device) # in_features= 64, out_features=3072 + self.txt_in = operations.Linear(params.context_in_dim, self.hidden_size, dtype=dtype, device=device) # in_features=4096, out_features=3072, bias=True + + self.time_in = MLPEmbedder( in_dim=256, hidden_dim=self.hidden_size, dtype=dtype, device=device, operations=operations) + self.vector_in = MLPEmbedder(params.vec_in_dim, self.hidden_size, dtype=dtype, device=device, operations=operations) # in_features=768, out_features=3072 (first layer) second layer 3072,3072 + self.guidance_in = (MLPEmbedder( in_dim=256, hidden_dim=self.hidden_size, dtype=dtype, device=device, operations=operations) if params.guidance_embed else nn.Identity()) + + self.double_blocks = nn.ModuleList([DoubleStreamBlock(self.hidden_size, self.num_heads, mlp_ratio=params.mlp_ratio, qkv_bias=params.qkv_bias, dtype=dtype, device=device, operations=operations, idx=_) for _ in range(params.depth)]) + self.single_blocks = nn.ModuleList([SingleStreamBlock(self.hidden_size, self.num_heads, mlp_ratio=params.mlp_ratio, dtype=dtype, device=device, operations=operations, idx=_) for _ in range(params.depth_single_blocks)]) + + if final_layer: + self.final_layer = LastLayer(self.hidden_size, 1, self.out_channels, dtype=dtype, device=device, operations=operations) + + + + + def forward_blocks(self, img: Tensor, img_ids: Tensor, txt: Tensor, txt_ids: Tensor, timesteps: Tensor, y: Tensor, guidance: Tensor = None, control=None, transformer_options = {},) -> Tensor: + if img.ndim != 3 or txt.ndim != 3: + raise ValueError("Input img and txt tensors must have 3 dimensions.") + + # running on sequences img + img = self.img_in(img) # 1,9216,64 == 768x192 # 1,9216,64 == 1,16,128,256 + 1,16,64,64 # 1,8192,64 with uncond/cond #:,:,64 -> :,:,3072 + vec = self.time_in(timestep_embedding(timesteps, 256).to(img.dtype)) # 1 -> 1,3072 + if self.params.guidance_embed: + if guidance is None: + print("Guidance strength is none, not using distilled guidance.") + else: + vec = vec + self.guidance_in(timestep_embedding(guidance, 256).to(img.dtype)) + + vec = vec + self.vector_in(y) #y.shape=1,768 y==all 0s + txt = self.txt_in(txt) # + + ids = torch.cat((txt_ids, img_ids), dim=1) # img_ids.shape=1,8192,3 txt_ids.shape=1,512,3 #ids.shape=1,8704,3 + pe = self.pe_embedder(ids) # pe.shape 1,1,8704,64,2,2 + + weight = transformer_options['reg_cond_weight'] if 'reg_cond_weight' in transformer_options else 0.0 + floor = transformer_options['reg_cond_floor'] if 'reg_cond_floor' in transformer_options else 0.0 + mask_orig, mask_self = None, None + mask_obj = transformer_options.get('patches', {}).get('regional_conditioning_mask', None) + if mask_obj is not None and weight >= 0: + mask_orig = mask_obj[0](transformer_options, weight.item()) + mask_self = mask_orig.clone() + mask_self[mask_obj[0].text_len:, mask_obj[0].text_len:] = mask_self.max() + + mask_resized_list = [] + mask = None + mask_obj = transformer_options.get('patches', {}).get('regional_conditioning_mask', None) + if mask_obj is not None and weight >= 0: + mask = mask_obj[0](transformer_options, weight.item()) + text_len = mask_obj[0].text_len + mask[text_len:,text_len:] = torch.clamp(mask[text_len:,text_len:], min=floor.to(mask.device)) + + + + for i, block in enumerate(self.double_blocks): + #img, txt, mask_resized = block(img=img, txt=txt, vec=vec, pe=pe, timestep=timesteps, transformer_options=transformer_options, mask=mask, weight=weight) #, mask=mask) + img, txt = block(img=img, txt=txt, vec=vec, pe=pe, timestep=timesteps, transformer_options=transformer_options, mask=mask, weight=weight) #, mask=mask) + #if mask is not None: + # mask_resized_list.append(mask_resized) + + if control is not None: # Controlnet + control_i = control.get("input") + if i < len(control_i): + add = control_i[i] + if add is not None: + img[:1] += add + + + + img = torch.cat((txt, img), 1) #first 256 is txt embed + for i, block in enumerate(self.single_blocks): + img = block(img, vec=vec, pe=pe, timestep=timesteps, transformer_options=transformer_options, mask=mask, weight=weight) + + if control is not None: # Controlnet + control_o = control.get("output") + if i < len(control_o): + add = control_o[i] + if add is not None: + img[:1, txt.shape[1] :, ...] += add + + + + img = img[:, txt.shape[1] :, ...] + img = self.final_layer(img, vec) # (N, T, patch_size ** 2 * out_channels) 1,8192,3072 -> 1,8192,64 + return img + + + + def _get_img_ids(self, x, bs, h_len, w_len, h_start, h_end, w_start, w_end): + img_ids = torch.zeros((h_len, w_len, 3), device=x.device, dtype=x.dtype) + img_ids[..., 1] = img_ids[..., 1] + torch.linspace(h_start, h_end - 1, steps=h_len, device=x.device, dtype=x.dtype)[:, None] + img_ids[..., 2] = img_ids[..., 2] + torch.linspace(w_start, w_end - 1, steps=w_len, device=x.device, dtype=x.dtype)[None, :] + img_ids = repeat(img_ids, "h w c -> b (h w) c", b=bs) + return img_ids + + + + def forward(self, x, timestep, context, y, guidance, control=None, transformer_options={}, **kwargs): + + out_list = [] + for i in range(len(transformer_options['cond_or_uncond'])): + UNCOND = transformer_options['cond_or_uncond'][i] == 1 + + bs, c, h, w = x.shape + transformer_options['original_shape'] = x.shape + patch_size = 2 + x = comfy.ldm.common_dit.pad_to_patch_size(x, (patch_size, patch_size)) # 1,16,192,192 + transformer_options['patch_size'] = patch_size + + #if 'regional_conditioning_weight' not in transformer_options: # this breaks the graph + # transformer_options['regional_conditioning_weight'] = timestep[0] / 1.5 + + h_len = ((h + (patch_size // 2)) // patch_size) # h_len 96 + w_len = ((w + (patch_size // 2)) // patch_size) # w_len 96 + + img = rearrange(x, "b c (h ph) (w pw) -> b (h w) (c ph pw)", ph=patch_size, pw=patch_size) # img 1,9216,64 + + if UNCOND: + transformer_options['reg_cond_weight'] = -1 + context_tmp = context[i][None,...].clone() + elif UNCOND == False: + transformer_options['reg_cond_weight'] = transformer_options['regional_conditioning_weight'] + transformer_options['reg_cond_floor'] = transformer_options['regional_conditioning_floor'] #if "regional_conditioning_floor" in transformer_options else 0.0 + regional_conditioning_positive = transformer_options.get('patches', {}).get('regional_conditioning_positive', None) + context_tmp = regional_conditioning_positive[0].concat_cond(context[i][None,...], transformer_options) + + txt_ids = torch.zeros((bs, context_tmp.shape[1], 3), device=x.device, dtype=x.dtype) # txt_ids 1, 256,3 + img_ids_orig = self._get_img_ids(x, bs, h_len, w_len, 0, h_len, 0, w_len) # img_ids_orig = 1,9216,3 + + out_tmp = self.forward_blocks(img [i][None,...].clone(), + img_ids_orig[i][None,...].clone(), + context_tmp, + txt_ids [i][None,...].clone(), + timestep [i][None,...].clone(), + y [i][None,...].clone(), + guidance [i][None,...].clone(), + control, transformer_options=transformer_options) # context 1,256,4096 y 1,768 + out_list.append(out_tmp) + + out = torch.stack(out_list, dim=0).squeeze(dim=1) + + return rearrange(out, "b (h w) (c ph pw) -> b c (h ph) (w pw)", h=h_len, w=w_len, ph=2, pw=2)[:,:,:h,:w] diff --git a/ComfyUI/custom_nodes/RES4LYF/legacy/flux/redux.py b/ComfyUI/custom_nodes/RES4LYF/legacy/flux/redux.py new file mode 100644 index 0000000000000000000000000000000000000000..527e83164ea2fd1bdf7431f3791ebede00895051 --- /dev/null +++ b/ComfyUI/custom_nodes/RES4LYF/legacy/flux/redux.py @@ -0,0 +1,25 @@ +import torch +import comfy.ops + +ops = comfy.ops.manual_cast + +class ReduxImageEncoder(torch.nn.Module): + def __init__( + self, + redux_dim: int = 1152, + txt_in_features: int = 4096, + device=None, + dtype=None, + ) -> None: + super().__init__() + + self.redux_dim = redux_dim + self.device = device + self.dtype = dtype + + self.redux_up = ops.Linear(redux_dim, txt_in_features * 3, dtype=dtype) + self.redux_down = ops.Linear(txt_in_features * 3, txt_in_features, dtype=dtype) + + def forward(self, sigclip_embeds) -> torch.Tensor: + projected_x = self.redux_down(torch.nn.functional.silu(self.redux_up(sigclip_embeds))) + return projected_x diff --git a/ComfyUI/custom_nodes/RES4LYF/legacy/noise_classes.py b/ComfyUI/custom_nodes/RES4LYF/legacy/noise_classes.py new file mode 100644 index 0000000000000000000000000000000000000000..b47a36a00148b400459c59c9c4eb0e92728d5f51 --- /dev/null +++ b/ComfyUI/custom_nodes/RES4LYF/legacy/noise_classes.py @@ -0,0 +1,683 @@ +import torch +from torch import nn, Tensor, Generator, lerp +from torch.nn.functional import unfold +import torch.nn.functional as F +from typing import Callable, Tuple +from math import pi +from comfy.k_diffusion.sampling import BrownianTreeNoiseSampler +from torch.distributions import StudentT, Laplace +import numpy as np +import pywt +import functools +from ..res4lyf import RESplain + +# Set this to "True" if you have installed OpenSimplex. Recommended to install without dependencies due to conflicting packages: pip3 install opensimplex --no-deps +OPENSIMPLEX_ENABLE = False + +if OPENSIMPLEX_ENABLE: + from opensimplex import OpenSimplex + +class PrecisionTool: + def __init__(self, cast_type='fp64'): + self.cast_type = cast_type + + def cast_tensor(self, func): + @functools.wraps(func) + def wrapper(*args, **kwargs): + if self.cast_type not in ['fp64', 'fp32', 'fp16']: + return func(*args, **kwargs) + + target_device = None + for arg in args: + if torch.is_tensor(arg): + target_device = arg.device + break + if target_device is None: + for v in kwargs.values(): + if torch.is_tensor(v): + target_device = v.device + break + + # recursively zs_recast tensors in nested dictionaries + def cast_and_move_to_device(data): + if torch.is_tensor(data): + if self.cast_type == 'fp64': + return data.to(torch.float64).to(target_device) + elif self.cast_type == 'fp32': + return data.to(torch.float32).to(target_device) + elif self.cast_type == 'fp16': + return data.to(torch.float16).to(target_device) + elif isinstance(data, dict): + return {k: cast_and_move_to_device(v) for k, v in data.items()} + return data + + new_args = [cast_and_move_to_device(arg) for arg in args] + new_kwargs = {k: cast_and_move_to_device(v) for k, v in kwargs.items()} + + return func(*new_args, **new_kwargs) + return wrapper + + def set_cast_type(self, new_value): + if new_value in ['fp64', 'fp32', 'fp16']: + self.cast_type = new_value + else: + self.cast_type = 'fp64' + +precision_tool = PrecisionTool(cast_type='fp64') + + +def noise_generator_factory(cls, **fixed_params): + def create_instance(**kwargs): + params = {**fixed_params, **kwargs} + return cls(**params) + return create_instance + +def like(x): + return {'size': x.shape, 'dtype': x.dtype, 'layout': x.layout, 'device': x.device} + +def scale_to_range(x, scaled_min = -1.73, scaled_max = 1.73): #1.73 is roughly the square root of 3 + return scaled_min + (x - x.min()) * (scaled_max - scaled_min) / (x.max() - x.min()) + +def normalize(x): + return (x - x.mean())/ x.std() + +class NoiseGenerator: + def __init__(self, x=None, size=None, dtype=None, layout=None, device=None, seed=42, generator=None, sigma_min=None, sigma_max=None): + self.seed = seed + + if x is not None: + self.x = x + self.size = x.shape + self.dtype = x.dtype + self.layout = x.layout + self.device = x.device + else: + self.x = torch.zeros(size, dtype, layout, device) + + # allow overriding parameters imported from latent 'x' if specified + if size is not None: + self.size = size + if dtype is not None: + self.dtype = dtype + if layout is not None: + self.layout = layout + if device is not None: + self.device = device + + self.sigma_max = sigma_max.to(device) if isinstance(sigma_max, torch.Tensor) else sigma_max + self.sigma_min = sigma_min.to(device) if isinstance(sigma_min, torch.Tensor) else sigma_min + + self.last_seed = seed + + if generator is None: + self.generator = torch.Generator(device=self.device).manual_seed(seed) + else: + self.generator = generator + + def __call__(self): + raise NotImplementedError("This method got clownsharked!") + + def update(self, **kwargs): + + if not isinstance(self, BrownianNoiseGenerator): + self.last_seed += 1 + + updated_values = [] + for attribute_name, value in kwargs.items(): + if value is not None: + setattr(self, attribute_name, value) + updated_values.append(getattr(self, attribute_name)) + return tuple(updated_values) + + + +class BrownianNoiseGenerator(NoiseGenerator): + def __call__(self, *, sigma=None, sigma_next=None, **kwargs): + return BrownianTreeNoiseSampler(self.x, self.sigma_min, self.sigma_max, seed=self.seed, cpu = self.device.type=='cpu')(sigma, sigma_next) + + + +class FractalNoiseGenerator(NoiseGenerator): + def __init__(self, x=None, size=None, dtype=None, layout=None, device=None, seed=42, generator=None, sigma_min=None, sigma_max=None, + alpha=0.0, k=1.0, scale=0.1): + super().__init__(x, size, dtype, layout, device, seed, generator, sigma_min, sigma_max) + self.update(alpha=alpha, k=k, scale=scale) + + def __call__(self, *, alpha=None, k=None, scale=None, **kwargs): + self.update(alpha=alpha, k=k, scale=scale) + if len(self.size) == 5: + b, c, t, h, w = self.size + else: + b, c, h, w = self.size + + noise = torch.normal(mean=0.0, std=1.0, size=self.size, dtype=self.dtype, layout=self.layout, device=self.device, generator=self.generator) + + y_freq = torch.fft.fftfreq(h, 1/h, device=self.device) + x_freq = torch.fft.fftfreq(w, 1/w, device=self.device) + + if len(self.size) == 5: + t_freq = torch.fft.fftfreq(t, 1/t, device=self.device) + freq = torch.sqrt(y_freq[:, None, None]**2 + x_freq[None, :, None]**2 + t_freq[None, None, :]**2).clamp(min=1e-10) + else: + freq = torch.sqrt(y_freq[:, None]**2 + x_freq[None, :]**2).clamp(min=1e-10) + + spectral_density = self.k / torch.pow(freq, self.alpha * self.scale) + spectral_density[0, 0] = 0 + + noise_fft = torch.fft.fftn(noise) + modified_fft = noise_fft * spectral_density + noise = torch.fft.ifftn(modified_fft).real + + return noise / torch.std(noise) + + + +class SimplexNoiseGenerator(NoiseGenerator): + def __init__(self, x=None, size=None, dtype=None, layout=None, device=None, seed=42, generator=None, sigma_min=None, sigma_max=None, + scale=0.01): + super().__init__(x, size, dtype, layout, device, seed, generator, sigma_min, sigma_max) + self.noise = OpenSimplex(seed=seed) + self.scale = scale + + def __call__(self, *, scale=None, **kwargs): + self.update(scale=scale) + + if len(self.size) == 5: + b, c, t, h, w = self.size + else: + b, c, h, w = self.size + + noise_array = self.noise.noise3array(np.arange(w),np.arange(h),np.arange(c)) + self.noise = OpenSimplex(seed=self.noise.get_seed()+1) + + noise_tensor = torch.from_numpy(noise_array).to(self.device) + noise_tensor = torch.unsqueeze(noise_tensor, dim=0) + if len(self.size) == 5: + noise_tensor = torch.unsqueeze(noise_tensor, dim=0) + + return noise_tensor / noise_tensor.std() + #return normalize(scale_to_range(noise_tensor)) + + + +class HiresPyramidNoiseGenerator(NoiseGenerator): + def __init__(self, x=None, size=None, dtype=None, layout=None, device=None, seed=42, generator=None, sigma_min=None, sigma_max=None, + discount=0.7, mode='nearest-exact'): + super().__init__(x, size, dtype, layout, device, seed, generator, sigma_min, sigma_max) + self.update(discount=discount, mode=mode) + + def __call__(self, *, discount=None, mode=None, **kwargs): + self.update(discount=discount, mode=mode) + + if len(self.size) == 5: + b, c, t, h, w = self.size + orig_h, orig_w, orig_t = h, w, t + u = nn.Upsample(size=(orig_h, orig_w, orig_t), mode=self.mode).to(self.device) + else: + b, c, h, w = self.size + orig_h, orig_w = h, w + orig_t = t = 1 + u = nn.Upsample(size=(orig_h, orig_w), mode=self.mode).to(self.device) + + noise = ((torch.rand(size=self.size, dtype=self.dtype, layout=self.layout, device=self.device, generator=self.generator) - 0.5) * 2 * 1.73) + + for i in range(4): + r = torch.rand(1, device=self.device, generator=self.generator).item() * 2 + 2 + h, w = min(orig_h * 15, int(h * (r ** i))), min(orig_w * 15, int(w * (r ** i))) + if len(self.size) == 5: + t = min(orig_t * 15, int(t * (r ** i))) + new_noise = torch.randn((b, c, t, h, w), dtype=self.dtype, layout=self.layout, device=self.device, generator=self.generator) + else: + new_noise = torch.randn((b, c, h, w), dtype=self.dtype, layout=self.layout, device=self.device, generator=self.generator) + + upsampled_noise = u(new_noise) + noise += upsampled_noise * self.discount ** i + + if h >= orig_h * 15 or w >= orig_w * 15 or t >= orig_t * 15: + break # if resolution is too high + + return noise / noise.std() + + + +class PyramidNoiseGenerator(NoiseGenerator): + def __init__(self, x=None, size=None, dtype=None, layout=None, device=None, seed=42, generator=None, sigma_min=None, sigma_max=None, + discount=0.8, mode='nearest-exact'): + super().__init__(x, size, dtype, layout, device, seed, generator, sigma_min, sigma_max) + self.update(discount=discount, mode=mode) + + def __call__(self, *, discount=None, mode=None, **kwargs): + self.update(discount=discount, mode=mode) + + x = torch.zeros(self.size, dtype=self.dtype, layout=self.layout, device=self.device) + + if len(self.size) == 5: + b, c, t, h, w = self.size + orig_h, orig_w, orig_t = h, w, t + else: + b, c, h, w = self.size + orig_h, orig_w = h, w + + r = 1 + for i in range(5): + r *= 2 + + if len(self.size) == 5: + scaledSize = (b, c, t * r, h * r, w * r) + origSize = (orig_h, orig_w, orig_t) + else: + scaledSize = (b, c, h * r, w * r) + origSize = (orig_h, orig_w) + + x += torch.nn.functional.interpolate( + torch.normal(mean=0, std=0.5 ** i, size=scaledSize, dtype=self.dtype, layout=self.layout, device=self.device, generator=self.generator), + size=origSize, mode=self.mode + ) * self.discount ** i + return x / x.std() + + + +class InterpolatedPyramidNoiseGenerator(NoiseGenerator): + def __init__(self, x=None, size=None, dtype=None, layout=None, device=None, seed=42, generator=None, sigma_min=None, sigma_max=None, + discount=0.7, mode='nearest-exact'): + super().__init__(x, size, dtype, layout, device, seed, generator, sigma_min, sigma_max) + self.update(discount=discount, mode=mode) + + + def __call__(self, *, discount=None, mode=None, **kwargs): + self.update(discount=discount, mode=mode) + + if len(self.size) == 5: + b, c, t, h, w = self.size + orig_t, orig_h, orig_w = t, h, w + else: + b, c, h, w = self.size + orig_h, orig_w = h, w + t = orig_t = 1 + + noise = ((torch.rand(size=self.size, dtype=self.dtype, layout=self.layout, device=self.device, generator=self.generator) - 0.5) * 2 * 1.73) + multipliers = [1] + + for i in range(4): + r = torch.rand(1, device=self.device, generator=self.generator).item() * 2 + 2 + h, w = min(orig_h * 15, int(h * (r ** i))), min(orig_w * 15, int(w * (r ** i))) + + if len(self.size) == 5: + t = min(orig_t * 15, int(t * (r ** i))) + new_noise = torch.randn((b, c, t, h, w), dtype=self.dtype, layout=self.layout, device=self.device, generator=self.generator) + upsampled_noise = nn.functional.interpolate(new_noise, size=(orig_t, orig_h, orig_w), mode=self.mode) + else: + new_noise = torch.randn((b, c, h, w), dtype=self.dtype, layout=self.layout, device=self.device, generator=self.generator) + upsampled_noise = nn.functional.interpolate(new_noise, size=(orig_h, orig_w), mode=self.mode) + + noise += upsampled_noise * self.discount ** i + multipliers.append(self.discount ** i) + + if h >= orig_h * 15 or w >= orig_w * 15 or (len(self.size) == 5 and t >= orig_t * 15): + break # if resolution is too high + + noise = noise / sum([m ** 2 for m in multipliers]) ** 0.5 + return noise / noise.std() + + + +class CascadeBPyramidNoiseGenerator(NoiseGenerator): + def __init__(self, x=None, size=None, dtype=None, layout=None, device=None, seed=42, generator=None, sigma_min=None, sigma_max=None, + levels=10, mode='nearest', size_range=[1,16]): + super().__init__(x, size, dtype, layout, device, seed, generator, sigma_min, sigma_max) + self.update(epsilon=x, levels=levels, mode=mode, size_range=size_range) + + def __call__(self, *, levels=10, mode='nearest', size_range=[1,16], **kwargs): + self.update(levels=levels, mode=mode) + + b, c, h, w = self.size + + epsilon = torch.randn(self.size, dtype=self.dtype, layout=self.layout, device=self.device, generator=self.generator) + multipliers = [1] + for i in range(1, levels): + m = 0.75 ** i + + h, w = int(epsilon.size(-2) // (2 ** i)), int(epsilon.size(-2) // (2 ** i)) + if size_range is None or (size_range[0] <= h <= size_range[1] or size_range[0] <= w <= size_range[1]): + offset = torch.randn(epsilon.size(0), epsilon.size(1), h, w, device=self.device, generator=self.generator) + epsilon = epsilon + torch.nn.functional.interpolate(offset, size=epsilon.shape[-2:], mode=self.mode) * m + multipliers.append(m) + + if h <= 1 or w <= 1: + break + epsilon = epsilon / sum([m ** 2 for m in multipliers]) ** 0.5 #divides the epsilon tensor by the square root of the sum of the squared multipliers. + + return epsilon + + +class UniformNoiseGenerator(NoiseGenerator): + def __init__(self, x=None, size=None, dtype=None, layout=None, device=None, seed=42, generator=None, sigma_min=None, sigma_max=None, + mean=0.0, scale=1.73): + super().__init__(x, size, dtype, layout, device, seed, generator, sigma_min, sigma_max) + self.update(mean=mean, scale=scale) + + def __call__(self, *, mean=None, scale=None, **kwargs): + self.update(mean=mean, scale=scale) + + noise = torch.rand(self.size, dtype=self.dtype, layout=self.layout, device=self.device, generator=self.generator) + + return self.scale * 2 * (noise - 0.5) + self.mean + +class GaussianNoiseGenerator(NoiseGenerator): + def __init__(self, x=None, size=None, dtype=None, layout=None, device=None, seed=42, generator=None, sigma_min=None, sigma_max=None, + mean=0.0, std=1.0): + super().__init__(x, size, dtype, layout, device, seed, generator, sigma_min, sigma_max) + self.update(mean=mean, std=std) + + def __call__(self, *, mean=None, std=None, **kwargs): + self.update(mean=mean, std=std) + + noise = torch.randn(self.size, dtype=self.dtype, layout=self.layout, device=self.device, generator=self.generator) + + return (noise - noise.mean()) / noise.std() + +class GaussianBackwardsNoiseGenerator(NoiseGenerator): + def __init__(self, x=None, size=None, dtype=None, layout=None, device=None, seed=42, generator=None, sigma_min=None, sigma_max=None, + mean=0.0, std=1.0): + super().__init__(x, size, dtype, layout, device, seed, generator, sigma_min, sigma_max) + self.update(mean=mean, std=std) + + def __call__(self, *, mean=None, std=None, **kwargs): + self.update(mean=mean, std=std) + RESplain("GaussianBackwards last seed:", self.generator.initial_seed()) + self.generator.manual_seed(self.generator.initial_seed() - 1) + noise = torch.randn(self.size, dtype=self.dtype, layout=self.layout, device=self.device, generator=self.generator) + + return (noise - noise.mean()) / noise.std() + +class LaplacianNoiseGenerator(NoiseGenerator): + def __init__(self, x=None, size=None, dtype=None, layout=None, device=None, seed=42, generator=None, sigma_min=None, sigma_max=None, + loc=0, scale=1.0): + super().__init__(x, size, dtype, layout, device, seed, generator, sigma_min, sigma_max) + self.update(loc=loc, scale=scale) + + def __call__(self, *, loc=None, scale=None, **kwargs): + self.update(loc=loc, scale=scale) + + # b, c, h, w = self.size + # orig_h, orig_w = h, w + + noise = torch.randn(self.size, dtype=self.dtype, layout=self.layout, device=self.device, generator=self.generator) / 4.0 + + rng_state = torch.random.get_rng_state() + torch.manual_seed(self.generator.initial_seed()) + laplacian_noise = Laplace(loc=self.loc, scale=self.scale).rsample(self.size).to(self.device) + self.generator.manual_seed(self.generator.initial_seed() + 1) + torch.random.set_rng_state(rng_state) + + noise += laplacian_noise + return noise / noise.std() + +class StudentTNoiseGenerator(NoiseGenerator): + def __init__(self, x=None, size=None, dtype=None, layout=None, device=None, seed=42, generator=None, sigma_min=None, sigma_max=None, + loc=0, scale=0.2, df=1): + super().__init__(x, size, dtype, layout, device, seed, generator, sigma_min, sigma_max) + self.update(loc=loc, scale=scale, df=df) + + def __call__(self, *, loc=None, scale=None, df=None, **kwargs): + self.update(loc=loc, scale=scale, df=df) + + # b, c, h, w = self.size + # orig_h, orig_w = h, w + + rng_state = torch.random.get_rng_state() + torch.manual_seed(self.generator.initial_seed()) + + noise = StudentT(loc=self.loc, scale=self.scale, df=self.df).rsample(self.size) + + s = torch.quantile(noise.flatten(start_dim=1).abs(), 0.75, dim=-1) + + if len(self.size) == 5: + s = s.reshape(*s.shape, 1, 1, 1, 1) + else: + s = s.reshape(*s.shape, 1, 1, 1) + + noise = noise.clamp(-s, s) + noise_latent = torch.copysign(torch.pow(torch.abs(noise), 0.5), noise).to(self.device) + + self.generator.manual_seed(self.generator.initial_seed() + 1) + torch.random.set_rng_state(rng_state) + return (noise_latent - noise_latent.mean()) / noise_latent.std() + +class WaveletNoiseGenerator(NoiseGenerator): + def __init__(self, x=None, size=None, dtype=None, layout=None, device=None, seed=42, generator=None, sigma_min=None, sigma_max=None, + wavelet='haar'): + super().__init__(x, size, dtype, layout, device, seed, generator, sigma_min, sigma_max) + self.update(wavelet=wavelet) + + def __call__(self, *, wavelet=None, **kwargs): + self.update(wavelet=wavelet) + + # b, c, h, w = self.size + # orig_h, orig_w = h, w + + # noise for spatial dimensions only + coeffs = pywt.wavedecn(torch.randn(self.size, dtype=self.dtype, layout=self.layout, device=self.device, generator=self.generator).to(self.device), wavelet=self.wavelet, mode='periodization') + noise = pywt.waverecn(coeffs, wavelet=self.wavelet, mode='periodization') + noise_tensor = torch.tensor(noise, dtype=self.dtype, device=self.device) + + noise_tensor = (noise_tensor - noise_tensor.mean()) / noise_tensor.std() + return noise_tensor + +class PerlinNoiseGenerator(NoiseGenerator): + def __init__(self, x=None, size=None, dtype=None, layout=None, device=None, seed=42, generator=None, sigma_min=None, sigma_max=None, + detail=0.0): + super().__init__(x, size, dtype, layout, device, seed, generator, sigma_min, sigma_max) + self.update(detail=detail) + + @staticmethod + def get_positions(block_shape: Tuple[int, int]) -> Tensor: + bh, bw = block_shape + positions = torch.stack( + torch.meshgrid( + [(torch.arange(b) + 0.5) / b for b in (bw, bh)], + indexing="xy", + ), + -1, + ).view(1, bh, bw, 1, 1, 2) + return positions + + @staticmethod + def unfold_grid(vectors: Tensor) -> Tensor: + batch_size, _, gpy, gpx = vectors.shape + return ( + unfold(vectors, (2, 2)) + .view(batch_size, 2, 4, -1) + .permute(0, 2, 3, 1) + .view(batch_size, 4, gpy - 1, gpx - 1, 2) + ) + + @staticmethod + def smooth_step(t: Tensor) -> Tensor: + return t * t * (3.0 - 2.0 * t) + + @staticmethod + def perlin_noise_tensor( + self, + vectors: Tensor, positions: Tensor, step: Callable = None + ) -> Tensor: + if step is None: + step = self.smooth_step + + batch_size = vectors.shape[0] + # grid height, grid width + gh, gw = vectors.shape[2:4] + # block height, block width + bh, bw = positions.shape[1:3] + + for i in range(2): + if positions.shape[i + 3] not in (1, vectors.shape[i + 2]): + raise Exception( + f"Blocks shapes do not match: vectors ({vectors.shape[1]}, {vectors.shape[2]}), positions {gh}, {gw})" + ) + + if positions.shape[0] not in (1, batch_size): + raise Exception( + f"Batch sizes do not match: vectors ({vectors.shape[0]}), positions ({positions.shape[0]})" + ) + + vectors = vectors.view(batch_size, 4, 1, gh * gw, 2) + positions = positions.view(positions.shape[0], bh * bw, -1, 2) + + step_x = step(positions[..., 0]) + step_y = step(positions[..., 1]) + + row0 = lerp( + (vectors[:, 0] * positions).sum(dim=-1), + (vectors[:, 1] * (positions - positions.new_tensor((1, 0)))).sum(dim=-1), + step_x, + ) + row1 = lerp( + (vectors[:, 2] * (positions - positions.new_tensor((0, 1)))).sum(dim=-1), + (vectors[:, 3] * (positions - positions.new_tensor((1, 1)))).sum(dim=-1), + step_x, + ) + noise = lerp(row0, row1, step_y) + return ( + noise.view( + batch_size, + bh, + bw, + gh, + gw, + ) + .permute(0, 3, 1, 4, 2) + .reshape(batch_size, gh * bh, gw * bw) + ) + + def perlin_noise( + self, + grid_shape: Tuple[int, int], + out_shape: Tuple[int, int], + batch_size: int = 1, + generator: Generator = None, + *args, + **kwargs, + ) -> Tensor: + gh, gw = grid_shape # grid height and width + oh, ow = out_shape # output height and width + bh, bw = oh // gh, ow // gw # block height and width + + if oh != bh * gh: + raise Exception(f"Output height {oh} must be divisible by grid height {gh}") + if ow != bw * gw != 0: + raise Exception(f"Output width {ow} must be divisible by grid width {gw}") + + angle = torch.empty( + [batch_size] + [s + 1 for s in grid_shape], device=self.device, *args, **kwargs + ).uniform_(to=2.0 * pi, generator=self.generator) + # random vectors on grid points + vectors = self.unfold_grid(torch.stack((torch.cos(angle), torch.sin(angle)), dim=1)) + # positions inside grid cells [0, 1) + positions = self.get_positions((bh, bw)).to(vectors) + return self.perlin_noise_tensor(self, vectors, positions).squeeze(0) + + def __call__(self, *, detail=None, **kwargs): + self.update(detail=detail) #currently unused + + if len(self.size) == 5: + b, c, t, h, w = self.size + noise = torch.randn(self.size, dtype=self.dtype, layout=self.layout, device=self.device, generator=self.generator) / 2.0 + + for tt in range(t): + for i in range(2): + perlin_slice = self.perlin_noise((h, w), (h, w), batch_size=c, generator=self.generator).to(self.device) + perlin_expanded = perlin_slice.unsqueeze(0).unsqueeze(2) + time_slice = noise[:, :, tt:tt+1, :, :] + noise[:, :, tt:tt+1, :, :] += perlin_expanded + else: + b, c, h, w = self.size + #orig_h, orig_w = h, w + + noise = torch.randn(self.size, dtype=self.dtype, layout=self.layout, device=self.device, generator=self.generator) / 2.0 + for i in range(2): + noise += self.perlin_noise((h, w), (h, w), batch_size=c, generator=self.generator).to(self.device) + + return noise / noise.std() + +from functools import partial + +NOISE_GENERATOR_CLASSES = { + "fractal": FractalNoiseGenerator, + + "gaussian": GaussianNoiseGenerator, + "gaussian_backwards": GaussianBackwardsNoiseGenerator, + "uniform": UniformNoiseGenerator, + "pyramid-cascade_B": CascadeBPyramidNoiseGenerator, + "pyramid-interpolated": InterpolatedPyramidNoiseGenerator, + "pyramid-bilinear": noise_generator_factory(PyramidNoiseGenerator, mode='bilinear'), + "pyramid-bicubic": noise_generator_factory(PyramidNoiseGenerator, mode='bicubic'), + "pyramid-nearest": noise_generator_factory(PyramidNoiseGenerator, mode='nearest'), + "hires-pyramid-bilinear": noise_generator_factory(HiresPyramidNoiseGenerator, mode='bilinear'), + "hires-pyramid-bicubic": noise_generator_factory(HiresPyramidNoiseGenerator, mode='bicubic'), + "hires-pyramid-nearest": noise_generator_factory(HiresPyramidNoiseGenerator, mode='nearest'), + "brownian": BrownianNoiseGenerator, + "laplacian": LaplacianNoiseGenerator, + "studentt": StudentTNoiseGenerator, + "wavelet": WaveletNoiseGenerator, + "perlin": PerlinNoiseGenerator, +} + + +NOISE_GENERATOR_CLASSES_SIMPLE = { + "none": GaussianNoiseGenerator, + "brownian": BrownianNoiseGenerator, + "gaussian": GaussianNoiseGenerator, + "gaussian_backwards": GaussianBackwardsNoiseGenerator, + "laplacian": LaplacianNoiseGenerator, + "perlin": PerlinNoiseGenerator, + "studentt": StudentTNoiseGenerator, + "uniform": UniformNoiseGenerator, + "wavelet": WaveletNoiseGenerator, + "brown": noise_generator_factory(FractalNoiseGenerator, alpha=2.0), + "pink": noise_generator_factory(FractalNoiseGenerator, alpha=1.0), + "white": noise_generator_factory(FractalNoiseGenerator, alpha=0.0), + "blue": noise_generator_factory(FractalNoiseGenerator, alpha=-1.0), + "violet": noise_generator_factory(FractalNoiseGenerator, alpha=-2.0), + "hires-pyramid-bicubic": noise_generator_factory(HiresPyramidNoiseGenerator, mode='bicubic'), + "hires-pyramid-bilinear": noise_generator_factory(HiresPyramidNoiseGenerator, mode='bilinear'), + "hires-pyramid-nearest": noise_generator_factory(HiresPyramidNoiseGenerator, mode='nearest'), + "pyramid-bicubic": noise_generator_factory(PyramidNoiseGenerator, mode='bicubic'), + "pyramid-bilinear": noise_generator_factory(PyramidNoiseGenerator, mode='bilinear'), + "pyramid-nearest": noise_generator_factory(PyramidNoiseGenerator, mode='nearest'), + "pyramid-interpolated": InterpolatedPyramidNoiseGenerator, + "pyramid-cascade_B": CascadeBPyramidNoiseGenerator, +} + +if OPENSIMPLEX_ENABLE: + NOISE_GENERATOR_CLASSES.update({ + "simplex": SimplexNoiseGenerator, + }) + +NOISE_GENERATOR_NAMES = tuple(NOISE_GENERATOR_CLASSES.keys()) +NOISE_GENERATOR_NAMES_SIMPLE = tuple(NOISE_GENERATOR_CLASSES_SIMPLE.keys()) + + +@precision_tool.cast_tensor +def prepare_noise(latent_image, seed, noise_type, noise_inds=None, alpha=1.0, k=1.0): # adapted from comfy/sample.py: https://github.com/comfyanonymous/ComfyUI + #optional arg skip can be used to skip and discard x number of noise generations for a given seed + noise_func = NOISE_GENERATOR_CLASSES.get(noise_type)(x=latent_image, seed=seed, sigma_min=0.0291675, sigma_max=14.614642) + + if noise_type == "fractal": + noise_func.alpha = alpha + noise_func.k = k + + # from here until return is very similar to comfy/sample.py + if noise_inds is None: + return noise_func(sigma=14.614642, sigma_next=0.0291675) + + unique_inds, inverse = np.unique(noise_inds, return_inverse=True) + noises = [] + for i in range(unique_inds[-1]+1): + noise = noise_func(size = [1] + list(latent_image.size())[1:], dtype=latent_image.dtype, layout=latent_image.layout, device=latent_image.device) + if i in unique_inds: + noises.append(noise) + noises = [noises[i] for i in inverse] + noises = torch.cat(noises, axis=0) + return noises + diff --git a/ComfyUI/custom_nodes/RES4LYF/legacy/noise_sigmas_timesteps_scaling.py b/ComfyUI/custom_nodes/RES4LYF/legacy/noise_sigmas_timesteps_scaling.py new file mode 100644 index 0000000000000000000000000000000000000000..101b868a348d3211679287ab586049120aeb00e5 --- /dev/null +++ b/ComfyUI/custom_nodes/RES4LYF/legacy/noise_sigmas_timesteps_scaling.py @@ -0,0 +1,225 @@ +import torch +#from..noise_classes import * +import comfy.model_patcher +from .helper import has_nested_attr + +def get_alpha_ratio_from_sigma_up(sigma_up, sigma_next, eta, sigma_max=1.0): + if sigma_up >= sigma_next and sigma_next > 0: + print("Maximum VPSDE noise level exceeded: falling back to hard noise mode.") + # Values below are the theoretical max, but break with exponential integrator stepsize calcs: + #sigma_up = sigma_next + #alpha_ratio = sigma_max - sigma_next + #sigma_down = 0 * sigma_next + #return alpha_ratio, sigma_up, sigma_down + if eta >= 1: + sigma_up = sigma_next * 0.9999 #avoid sqrt(neg_num) later + else: + sigma_up = sigma_next * eta + + sigma_signal = sigma_max - sigma_next + sigma_residual = torch.sqrt(sigma_next**2 - sigma_up**2) + + alpha_ratio = sigma_signal + sigma_residual + sigma_down = sigma_residual / alpha_ratio + return alpha_ratio, sigma_up, sigma_down + +def get_alpha_ratio_from_sigma_down(sigma_down, sigma_next, eta, sigma_max=1.0): + alpha_ratio = (1 - sigma_next) / (1 - sigma_down) + sigma_up = (sigma_next ** 2 - sigma_down ** 2 * alpha_ratio ** 2) ** 0.5 + + if sigma_up >= sigma_next: # "clamp" noise level to max if max exceeded + alpha_ratio, sigma_up, sigma_down = get_alpha_ratio_from_sigma_up(sigma_up, sigma_next, eta, sigma_max) + + return alpha_ratio, sigma_up, sigma_down + +def get_ancestral_step_RF_var(sigma, sigma_next, eta, sigma_max=1.0): + dtype = sigma.dtype #calculate variance adjusted sigma up... sigma_up = sqrt(dt) + + sigma, sigma_next = sigma.to(torch.float64), sigma_next.to(torch.float64) # float64 is very important to avoid numerical precision issues + + sigma_diff = (sigma - sigma_next).abs() + 1e-10 + sigma_up = torch.sqrt(sigma_diff).to(torch.float64) * eta + + sigma_down_num = (sigma_next**2 - sigma_up**2).to(torch.float64) + sigma_down = torch.sqrt(sigma_down_num) / ((1 - sigma_next).to(torch.float64) + torch.sqrt(sigma_down_num).to(torch.float64)) + + alpha_ratio = (1 - sigma_next).to(torch.float64) / (1 - sigma_down).to(torch.float64) + + return sigma_up.to(dtype), sigma_down.to(dtype), alpha_ratio.to(dtype) + +def get_ancestral_step_RF_lorentzian(sigma, sigma_next, eta, sigma_max=1.0): + dtype = sigma.dtype + alpha = 1 / ((sigma.to(torch.float64))**2 + 1) + sigma_up = eta * (1 - alpha) ** 0.5 + alpha_ratio, sigma_up, sigma_down = get_alpha_ratio_from_sigma_up(sigma_up, sigma_next, eta, sigma_max) + return sigma_up.to(dtype), sigma_down.to(dtype), alpha_ratio.to(dtype) + +def get_ancestral_step_EPS(sigma, sigma_next, eta=1.): + # Calculates the noise level (sigma_down) to step down to and the amount of noise to add (sigma_up) when doing an ancestral sampling step. + alpha_ratio = torch.full_like(sigma, 1.0) + + if not eta or not sigma_next: + return torch.full_like(sigma, 0.0), sigma_next, alpha_ratio + + sigma_up = min(sigma_next, eta * (sigma_next ** 2 * (sigma ** 2 - sigma_next ** 2) / sigma ** 2) ** 0.5) + sigma_down = (sigma_next ** 2 - sigma_up ** 2) ** 0.5 + + return sigma_up, sigma_down, alpha_ratio + +def get_ancestral_step_RF_sinusoidal(sigma_next, eta, sigma_max=1.0): + sigma_up = eta * sigma_next * torch.sin(torch.pi * sigma_next) ** 2 + alpha_ratio, sigma_up, sigma_down = get_alpha_ratio_from_sigma_up(sigma_up, sigma_next, eta, sigma_max) + return sigma_up, sigma_down, alpha_ratio + +def get_ancestral_step_RF_softer(sigma, sigma_next, eta, sigma_max=1.0): + # math adapted from get_ancestral_step_EPS to work with RF + sigma_down = sigma_next * torch.sqrt(1 - (eta**2 * (sigma**2 - sigma_next**2)) / sigma**2) + alpha_ratio, sigma_up, sigma_down = get_alpha_ratio_from_sigma_down(sigma_down, sigma_next, eta, sigma_max) + return sigma_up, sigma_down, alpha_ratio + +def get_ancestral_step_RF_soft(sigma, sigma_next, eta, sigma_max=1.0): + """Calculates the noise level (sigma_down) to step down to and the amount of noise to add (sigma_up) when doing a rectified flow sampling step, + and a mixing ratio (alpha_ratio) for scaling the latent during noise addition. Scale is to shape the sigma_down curve.""" + down_ratio = (1 - eta) + eta * ((sigma_next) / sigma) + sigma_down = down_ratio * sigma_next + alpha_ratio, sigma_up, sigma_down = get_alpha_ratio_from_sigma_down(sigma_down, sigma_next, eta, sigma_max) + return sigma_up, sigma_down, alpha_ratio + +def get_ancestral_step_RF_soft_linear(sigma, sigma_next, eta, sigma_max=1.0): + sigma_down = sigma_next + eta * (sigma_next - sigma) + if sigma_down < 0: + return torch.full_like(sigma, 0.), sigma_next, torch.full_like(sigma, 1.) + alpha_ratio, sigma_up, sigma_down = get_alpha_ratio_from_sigma_down(sigma_down, sigma_next, eta, sigma_max) + + return sigma_up, sigma_down, alpha_ratio + +def get_ancestral_step_RF_exp(sigma, sigma_next, eta, sigma_max=1.0): # TODO: fix black image issue with linear RK + h = -torch.log(sigma_next/sigma) + sigma_up = sigma_next * (1 - (-2*eta*h).exp())**0.5 + alpha_ratio, sigma_up, sigma_down = get_alpha_ratio_from_sigma_up(sigma_up, sigma_next, eta, sigma_max) + return sigma_up, sigma_down, alpha_ratio + +def get_ancestral_step_RF_sqrd(sigma, sigma_next, eta, sigma_max=1.0): + sigma_hat = sigma * (1 + eta) + sigma_up = (sigma_hat ** 2 - sigma ** 2) ** .5 + alpha_ratio, sigma_up, sigma_down = get_alpha_ratio_from_sigma_up(sigma_up, sigma_next, eta, sigma_max) + return sigma_up, sigma_down, alpha_ratio + +def get_ancestral_step_RF_hard(sigma_next, eta, sigma_max=1.0): + sigma_up = sigma_next * eta + alpha_ratio, sigma_up, sigma_down = get_alpha_ratio_from_sigma_up(sigma_up, sigma_next, eta, sigma_max) + return sigma_up, sigma_down, alpha_ratio + +def get_vpsde_step_RF(sigma, sigma_next, eta, sigma_max=1.0): + dt = sigma - sigma_next + sigma_up = eta * sigma * dt**0.5 + alpha_ratio = 1 - dt * (eta**2/4) * (1 + sigma) + sigma_down = sigma_next - (eta/4)*sigma*(1-sigma)*(sigma - sigma_next) + return sigma_up, sigma_down, alpha_ratio + +def get_fuckery_step_RF(sigma, sigma_next, eta, sigma_max=1.0): + sigma_down = (1-eta) * sigma_next + sigma_up = torch.sqrt(sigma_next**2 - sigma_down**2) + alpha_ratio = torch.ones_like(sigma_next) + return sigma_up, sigma_down, alpha_ratio + + +def get_res4lyf_step_with_model(model, sigma, sigma_next, eta=0.0, noise_mode="hard"): + + su, sd, alpha_ratio = torch.zeros_like(sigma), sigma_next.clone(), torch.ones_like(sigma) + + if has_nested_attr(model, "inner_model.inner_model.model_sampling"): + model_sampling = model.inner_model.inner_model.model_sampling + elif has_nested_attr(model, "model.model_sampling"): + model_sampling = model.model.model_sampling + + if isinstance(model_sampling, comfy.model_sampling.CONST): + sigma_var = (-1 + torch.sqrt(1 + 4 * sigma)) / 2 # sigma_var = (torch.sqrt(1 + 4 * sigma) - 1) / 2 sigma_var = ((4*sigma+1)**0.5 - 1) / 2 + if noise_mode == "hard_var" and eta > 0.0 and sigma_next > sigma_var: + su, sd, alpha_ratio = get_ancestral_step_RF_var(sigma, sigma_next, eta) + else: + if noise_mode == "soft": + su, sd, alpha_ratio = get_ancestral_step_RF_soft(sigma, sigma_next, eta) + elif noise_mode == "softer": + su, sd, alpha_ratio = get_ancestral_step_RF_softer(sigma, sigma_next, eta) + elif noise_mode == "hard_sq": + su, sd, alpha_ratio = get_ancestral_step_RF_sqrd(sigma, sigma_next, eta) + elif noise_mode == "sinusoidal": + su, sd, alpha_ratio = get_ancestral_step_RF_sinusoidal(sigma_next, eta) + elif noise_mode == "exp": + su, sd, alpha_ratio = get_ancestral_step_RF_exp(sigma, sigma_next, eta) + elif noise_mode == "soft-linear": + su, sd, alpha_ratio = get_ancestral_step_RF_soft_linear(sigma, sigma_next, eta) + elif noise_mode == "lorentzian": + su, sd, alpha_ratio = get_ancestral_step_RF_lorentzian(sigma, sigma_next, eta) + elif noise_mode == "vpsde": + su, sd, alpha_ratio = get_vpsde_step_RF(sigma, sigma_next, eta) + elif noise_mode == "fuckery": + su, sd, alpha_ratio = get_fuckery_step_RF(sigma, sigma_next, eta) + else: #elif noise_mode == "hard": #fall back to hard noise from hard_var + su, sd, alpha_ratio = get_ancestral_step_RF_hard(sigma_next, eta) + else: + alpha_ratio = torch.full_like(sigma, 1.0) + if noise_mode == "hard_sq": + sd = sigma_next + sigma_hat = sigma * (1 + eta) + su = (sigma_hat ** 2 - sigma ** 2) ** .5 + sigma = sigma_hat + elif noise_mode == "hard": + su = eta * sigma_next + sd = (sigma_next ** 2 - su ** 2) ** 0.5 + elif noise_mode == "exp": + h = -torch.log(sigma_next/sigma) + su = sigma_next * (1 - (-2*eta*h).exp())**0.5 + sd = (sigma_next ** 2 - su ** 2) ** 0.5 + else: #if noise_mode == "soft" or noise_mode == "softer": + su = min(sigma_next, eta * (sigma_next ** 2 * (sigma ** 2 - sigma_next ** 2) / sigma ** 2) ** 0.5) + #su, sd, alpha_ratio = get_ancestral_step_EPS(sigma, sigma_next, eta) + + + su = torch.nan_to_num(su, 0.0) + sd = torch.nan_to_num(sd, float(sigma_next)) + alpha_ratio = torch.nan_to_num(alpha_ratio, 1.0) + + return su, sigma, sd, alpha_ratio + + + +NOISE_MODE_NAMES = ["none", + "hard_sq", + "hard", + #"hard_down", + "lorentzian", + "soft", + "soft-linear", + "softer", + "eps", + "sinusoidal", + "exp", + "vpsde", + #"fuckery", + "hard_var", + ] + + + +def get_res4lyf_half_step3(sigma, sigma_next, c2=0.5, c3=1.0, t_fn=None, sigma_fn=None, t_fn_formula="", sigma_fn_formula="", ): + + t_fn_x = eval(f"lambda sigma: {t_fn_formula}", {"torch": torch}) if t_fn_formula else t_fn + sigma_fn_x = eval(f"lambda t: {sigma_fn_formula}", {"torch": torch}) if sigma_fn_formula else sigma_fn + + t_x, t_next_x = t_fn_x(sigma), t_fn_x(sigma_next) + h_x = t_next_x - t_x + + s2 = t_x + h_x * c2 + s3 = t_x + h_x * c3 + sigma_2 = sigma_fn_x(s2) + sigma_3 = sigma_fn_x(s3) + + h = t_fn(sigma_next) - t_fn(sigma) + c2 = (t_fn(sigma_2) - t_fn(sigma)) / h + c3 = (t_fn(sigma_3) - t_fn(sigma)) / h + + return c2, c3 + + diff --git a/ComfyUI/custom_nodes/RES4LYF/legacy/phi_functions.py b/ComfyUI/custom_nodes/RES4LYF/legacy/phi_functions.py new file mode 100644 index 0000000000000000000000000000000000000000..85e867f5cc95dfcad48521c34f2b6f3b777c830d --- /dev/null +++ b/ComfyUI/custom_nodes/RES4LYF/legacy/phi_functions.py @@ -0,0 +1,103 @@ +import torch +import math +from typing import Optional + + +# Remainder solution +def _phi(j, neg_h): + remainder = torch.zeros_like(neg_h) + + for k in range(j): + remainder += (neg_h)**k / math.factorial(k) + phi_j_h = ((neg_h).exp() - remainder) / (neg_h)**j + + return phi_j_h + +def calculate_gamma(c2, c3): + return (3*(c3**3) - 2*c3) / (c2*(2 - 3*c2)) + +# Exact analytic solution originally calculated by Clybius. https://github.com/Clybius/ComfyUI-Extra-Samplers/tree/main +def _gamma(n: int,) -> int: + """ + https://en.wikipedia.org/wiki/Gamma_function + for every positive integer n, + Γ(n) = (n-1)! + """ + return math.factorial(n-1) + +def _incomplete_gamma(s: int, x: float, gamma_s: Optional[int] = None) -> float: + """ + https://en.wikipedia.org/wiki/Incomplete_gamma_function#Special_values + if s is a positive integer, + Γ(s, x) = (s-1)!*∑{k=0..s-1}(x^k/k!) + """ + if gamma_s is None: + gamma_s = _gamma(s) + + sum_: float = 0 + # {k=0..s-1} inclusive + for k in range(s): + numerator: float = x**k + denom: int = math.factorial(k) + quotient: float = numerator/denom + sum_ += quotient + incomplete_gamma_: float = sum_ * math.exp(-x) * gamma_s + return incomplete_gamma_ + +def phi(j: int, neg_h: float, ): + """ + For j={1,2,3}: you could alternatively use Kat's phi_1, phi_2, phi_3 which perform fewer steps + + Lemma 1 + https://arxiv.org/abs/2308.02157 + ϕj(-h) = 1/h^j*∫{0..h}(e^(τ-h)*(τ^(j-1))/((j-1)!)dτ) + + https://www.wolframalpha.com/input?i=integrate+e%5E%28%CF%84-h%29*%28%CF%84%5E%28j-1%29%2F%28j-1%29%21%29d%CF%84 + = 1/h^j*[(e^(-h)*(-τ)^(-j)*τ(j))/((j-1)!)]{0..h} + https://www.wolframalpha.com/input?i=integrate+e%5E%28%CF%84-h%29*%28%CF%84%5E%28j-1%29%2F%28j-1%29%21%29d%CF%84+between+0+and+h + = 1/h^j*((e^(-h)*(-h)^(-j)*h^j*(Γ(j)-Γ(j,-h)))/(j-1)!) + = (e^(-h)*(-h)^(-j)*h^j*(Γ(j)-Γ(j,-h))/((j-1)!*h^j) + = (e^(-h)*(-h)^(-j)*(Γ(j)-Γ(j,-h))/(j-1)! + = (e^(-h)*(-h)^(-j)*(Γ(j)-Γ(j,-h))/Γ(j) + = (e^(-h)*(-h)^(-j)*(1-Γ(j,-h)/Γ(j)) + + requires j>0 + """ + assert j > 0 + gamma_: float = _gamma(j) + incomp_gamma_: float = _incomplete_gamma(j, neg_h, gamma_s=gamma_) + phi_: float = math.exp(neg_h) * neg_h**-j * (1-incomp_gamma_/gamma_) + return phi_ + + + +class Phi: + def __init__(self, h, c, analytic_solution=False): + self.h = h + self.c = c + self.cache = {} + if analytic_solution: + self.phi_f = phi + else: + self.phi_f = _phi # remainder method + + def __call__(self, j, i=-1): + if (j, i) in self.cache: + return self.cache[(j, i)] + + if i < 0: + c = 1 + else: + c = self.c[i - 1] + if c == 0: + self.cache[(j, i)] = 0 + return 0 + + if j == 0: + result = torch.exp(-self.h * c) + else: + result = self.phi_f(j, -self.h * c) + + self.cache[(j, i)] = result + + return result diff --git a/ComfyUI/custom_nodes/RES4LYF/legacy/rk_guide_func.py b/ComfyUI/custom_nodes/RES4LYF/legacy/rk_guide_func.py new file mode 100644 index 0000000000000000000000000000000000000000..3182e3cc21a75a438af6f8a72ca1f5b04c131d2a --- /dev/null +++ b/ComfyUI/custom_nodes/RES4LYF/legacy/rk_guide_func.py @@ -0,0 +1,1193 @@ +import torch +import torch.nn.functional as F +from typing import Tuple + +from einops import rearrange + +from .sigmas import get_sigmas +from .latents import hard_light_blend, normalize_latent, initialize_or_scale +from .rk_method import RK_Method +from .helper import get_extra_options_kv, extra_options_flag, get_cosine_similarity, get_extra_options_list + + +import itertools + + +def normalize_inputs(x, y0, y0_inv, guide_mode, extra_options): + + if guide_mode == "epsilon_guide_mean_std_from_bkg": + y0 = normalize_latent(y0, y0_inv) + + input_norm = get_extra_options_kv("input_norm", "", extra_options) + input_std = float(get_extra_options_kv("input_std", "1.0", extra_options)) + + if input_norm == "input_ch_mean_set_std_to": + x = normalize_latent(x, set_std=input_std) + + if input_norm == "input_ch_set_std_to": + x = normalize_latent(x, set_std=input_std, mean=False) + + if input_norm == "input_mean_set_std_to": + x = normalize_latent(x, set_std=input_std, channelwise=False) + + if input_norm == "input_std_set_std_to": + x = normalize_latent(x, set_std=input_std, mean=False, channelwise=False) + + return x, y0, y0_inv + + +class LatentGuide: + def __init__(self, guides, x, model, sigmas, UNSAMPLE, LGW_MASK_RESCALE_MIN, extra_options, device='cuda', dtype=torch.float64, max_steps=10000): + self.model = model + self.sigma_min = model.inner_model.inner_model.model_sampling.sigma_min.to(dtype) + self.sigma_max = model.inner_model.inner_model.model_sampling.sigma_max.to(dtype) + self.sigmas = sigmas + self.UNSAMPLE = UNSAMPLE + self.SAMPLE = (sigmas[0] > sigmas[1]) + self.extra_options = extra_options + self.y0 = torch.zeros_like(x) + self.y0_inv = torch.zeros_like(x) + self.guide_mode = "" + self.mask = None + self.mask_inv = None + + self.latent_guide = None + self.latent_guide_inv = None + + self.lgw_masks = [] + self.lgw_masks_inv = [] + self.lgw, self.lgw_inv = [torch.full_like(sigmas, 0.) for _ in range(2)] + + self.guide_cossim_cutoff_, self.guide_bkg_cossim_cutoff_ = 1.0, 1.0 + + latent_guide_weight, latent_guide_weight_inv = 0.,0. + latent_guide_weights, latent_guide_weights_inv = None, None + latent_guide_weights = torch.zeros_like(sigmas) + latent_guide_weights_inv = torch.zeros_like(sigmas) + if guides is not None: + self.guide_mode, latent_guide_weight, latent_guide_weight_inv, latent_guide_weights, latent_guide_weights_inv, self.latent_guide, self.latent_guide_inv, latent_guide_mask, latent_guide_mask_inv, scheduler_, scheduler_inv_, steps_, steps_inv_, denoise_, denoise_inv_ = guides + + self.mask, self.mask_inv = latent_guide_mask, latent_guide_mask_inv + self.guide_cossim_cutoff_, self.guide_bkg_cossim_cutoff_ = denoise_, denoise_inv_ + + if latent_guide_weights == None: + latent_guide_weights = get_sigmas(model, scheduler_, steps_, 1.0).to(x.dtype) + + if latent_guide_weights_inv == None: + latent_guide_weights_inv = get_sigmas(model, scheduler_inv_, steps_inv_, 1.0).to(x.dtype) + + latent_guide_weights = initialize_or_scale(latent_guide_weights, latent_guide_weight, max_steps).to(dtype) + latent_guide_weights_inv = initialize_or_scale(latent_guide_weights_inv, latent_guide_weight_inv, max_steps).to(dtype) + + latent_guide_weights = F.pad(latent_guide_weights, (0, max_steps), value=0.0) + latent_guide_weights_inv = F.pad(latent_guide_weights_inv, (0, max_steps), value=0.0) + + + if latent_guide_weights is not None: + self.lgw = latent_guide_weights.to(x.device) + if latent_guide_weights_inv is not None: + self.lgw_inv = latent_guide_weights_inv.to(x.device) + + self.mask, LGW_MASK_RESCALE_MIN = prepare_mask(x, self.mask, LGW_MASK_RESCALE_MIN) + if self.mask_inv is not None: + self.mask_inv, LGW_MASK_RESCALE_MIN = prepare_mask(x, self.mask_inv, LGW_MASK_RESCALE_MIN) + elif not self.SAMPLE: + self.mask_inv = (1-self.mask) + + for step in range(len(self.sigmas)-1): + lgw_mask, lgw_mask_inv = prepare_weighted_masks(self.mask, self.mask_inv, self.lgw[step], self.lgw_inv[step], self.latent_guide, self.latent_guide_inv, LGW_MASK_RESCALE_MIN) + self.lgw_masks.append(lgw_mask) + self.lgw_masks_inv.append(lgw_mask_inv) + + + def init_guides(self, x, noise_sampler, latent_guide=None, latent_guide_inv=None): + self.y0, self.y0_inv = torch.zeros_like(x), torch.zeros_like(x) + latent_guide = self.latent_guide if latent_guide is None else latent_guide + latent_guide_inv = self.latent_guide_inv if latent_guide_inv is None else latent_guide_inv + + + if latent_guide is not None: + if type(latent_guide) == dict: + latent_guide_samples = self.model.inner_model.inner_model.process_latent_in(latent_guide['samples']).clone().to(x.device) + else: + latent_guide_samples = latent_guide + if self.SAMPLE: + self.y0 = latent_guide_samples + elif self.UNSAMPLE: # and self.mask is not None: + x = (1-self.mask) * x + self.mask * latent_guide_samples + else: + x = latent_guide_samples + + if latent_guide_inv is not None: + if type(latent_guide_inv) == dict: + latent_guide_inv_samples = self.model.inner_model.inner_model.process_latent_in(latent_guide_inv['samples']).clone().to(x.device) + else: + latent_guide_inv_samples = latent_guide_inv + if self.SAMPLE: + self.y0_inv = latent_guide_inv_samples + elif self.UNSAMPLE: # and self.mask is not None: + x = (1-self.mask_inv) * x + self.mask_inv * latent_guide_inv_samples #fixed old approach, which was mask, (1-mask) + else: + x = latent_guide_inv_samples #THIS COULD LEAD TO WEIRD BEHAVIOR! OVERWRITING X WITH LG_INV AFTER SETTING TO LG above! + + if self.UNSAMPLE and not self.SAMPLE: #sigma_next > sigma: + self.y0 = noise_sampler(sigma=self.sigma_max, sigma_next=self.sigma_min) + self.y0 = (self.y0 - self.y0.mean()) / self.y0.std() + self.y0_inv = noise_sampler(sigma=self.sigma_max, sigma_next=self.sigma_min) + self.y0_inv = (self.y0_inv - self.y0_inv.mean()) / self.y0_inv.std() + + x, self.y0, self.y0_inv = normalize_inputs(x, self.y0, self.y0_inv, self.guide_mode, self.extra_options) + + return x + + + + def process_guides_substep(self, x_0, x_, eps_, data_, row, step, sigma, sigma_next, sigma_down, s_, unsample_resample_scale, rk, rk_type, extra_options, frame_weights_grp=None): + + y0 = self.y0 + if self.y0.shape[0] > 1: + y0 = self.y0[min(step, self.y0.shape[0]-1)].unsqueeze(0) + y0_inv = self.y0_inv + + lgw_mask = self.lgw_masks[step].clone() + lgw_mask_inv = self.lgw_masks_inv[step].clone() if self.lgw_masks_inv is not None else None + + lgw = self.lgw[step] + lgw_inv = self.lgw_inv[step] + + latent_guide = self.latent_guide + latent_guide_inv = self.latent_guide_inv + guide_mode = self.guide_mode + UNSAMPLE = self.UNSAMPLE + + if x_0.dim() == 5 and frame_weights_grp is not None: + apply_frame_weights(lgw_mask, frame_weights_grp[0]) + apply_frame_weights(lgw_mask_inv, frame_weights_grp[1]) + + if self.guide_mode: + data_norm = data_[row] - data_[row].mean(dim=(-2,-1), keepdim=True) + y0_norm = y0 - y0.mean(dim=(-2,-1), keepdim=True) + y0_inv_norm = y0_inv - y0_inv.mean(dim=(-2,-1), keepdim=True) + + y0_cossim = get_cosine_similarity(data_norm*lgw_mask, y0_norm *lgw_mask) + y0_cossim_inv = get_cosine_similarity(data_norm*lgw_mask_inv, y0_inv_norm*lgw_mask_inv) + + if y0_cossim < self.guide_cossim_cutoff_ or y0_cossim_inv < self.guide_bkg_cossim_cutoff_: + lgw_mask_cossim, lgw_mask_cossim_inv = lgw_mask, lgw_mask_inv + if y0_cossim >= self.guide_cossim_cutoff_: + lgw_mask_cossim = torch.zeros_like(lgw_mask) + if y0_cossim_inv >= self.guide_bkg_cossim_cutoff_: + lgw_mask_cossim_inv = torch.zeros_like(lgw_mask_inv) + lgw_mask = lgw_mask_cossim + lgw_mask_inv = lgw_mask_cossim_inv + else: + return eps_, x_ + else: + return eps_, x_ + + if self.UNSAMPLE and RK_Method.is_exponential(rk_type): + if not (extra_options_flag("disable_power_unsample", extra_options) or extra_options_flag("disable_power_resample", extra_options)): + extra_options += "\npower_unsample\npower_resample\n" + if not extra_options_flag("disable_lgw_scaling_substep_ch_mean_std", extra_options): + extra_options += "\nsubstep_eps_ch_mean_std\n" + + + + s_in = x_0.new_ones([x_0.shape[0]]) + eps_orig = eps_.clone() + + if extra_options_flag("dynamic_guides_mean_std", extra_options): + y_shift, y_inv_shift = normalize_latent([y0, y0_inv], [data_, data_]) + y0 = y_shift + if extra_options_flag("dynamic_guides_inv", extra_options): + y0_inv = y_inv_shift + + if extra_options_flag("dynamic_guides_mean", extra_options): + y_shift, y_inv_shift = normalize_latent([y0, y0_inv], [data_, data_], std=False) + y0 = y_shift + if extra_options_flag("dynamic_guides_inv", extra_options): + y0_inv = y_inv_shift + + if "data" == guide_mode: + y0_tmp = y0.clone() + if latent_guide_inv is not None: + y0_tmp = (1-lgw_mask) * data_[row] + lgw_mask * y0 + y0_tmp = (1-lgw_mask_inv) * y0_tmp + lgw_mask_inv * y0_inv + x_[row+1] = y0_tmp + eps_[row] + + if guide_mode == "data_projection": + + d_lerp = data_[row] + lgw_mask * (y0-data_[row]) + lgw_mask_inv * (y0_inv-data_[row]) + + d_collinear_d_lerp = get_collinear(data_[row], d_lerp) + d_lerp_ortho_d = get_orthogonal(d_lerp, data_[row]) + + data_[row] = d_collinear_d_lerp + d_lerp_ortho_d + + x_[row+1] = data_[row] + eps_[row] * sigma + + + elif "epsilon" in guide_mode: + if sigma > sigma_next: + + tol_value = float(get_extra_options_kv("tol", "-1.0", extra_options)) + if tol_value >= 0 and (lgw > 0 or lgw_inv > 0): + for b, c in itertools.product(range(x_0.shape[0]), range(x_0.shape[1])): + current_diff = torch.norm(data_[row][b][c] - y0 [b][c]) + current_diff_inv = torch.norm(data_[row][b][c] - y0_inv[b][c]) + + lgw_scaled = torch.nan_to_num(1-(tol_value/current_diff), 0) + lgw_scaled_inv = torch.nan_to_num(1-(tol_value/current_diff_inv), 0) + + lgw_tmp = min(lgw , lgw_scaled) + lgw_tmp_inv = min(lgw_inv, lgw_scaled_inv) + + lgw_mask_clamp = torch.clamp(lgw_mask, max=lgw_tmp) + lgw_mask_clamp_inv = torch.clamp(lgw_mask_inv, max=lgw_tmp_inv) + + eps_row, eps_row_inv = get_guide_epsilon_substep(x_0, x_, y0, y0_inv, s_, row, rk_type, b, c) + eps_[row][b][c] = eps_[row][b][c] + lgw_mask_clamp[b][c] * (eps_row - eps_[row][b][c]) + lgw_mask_clamp_inv[b][c] * (eps_row_inv - eps_[row][b][c]) + + + elif guide_mode == "epsilon_projection": + eps_row, eps_row_inv = get_guide_epsilon_substep(x_0, x_, y0, y0_inv, s_, row, rk_type) + + if extra_options_flag("eps_proj_v2", extra_options): + + eps_row_lerp_fg = eps_[row] + lgw_mask * (eps_row-eps_[row]) + eps_row_lerp_bg = eps_[row] + lgw_mask_inv * (eps_row_inv-eps_[row]) + + eps_collinear_eps_lerp_fg = get_collinear(eps_[row], eps_row_lerp_fg) + eps_lerp_ortho_eps_fg = get_orthogonal(eps_row_lerp_fg, eps_[row]) + + eps_collinear_eps_lerp_bg = get_collinear(eps_[row], eps_row_lerp_bg) + eps_lerp_ortho_eps_bg = get_orthogonal(eps_row_lerp_bg, eps_[row]) + + eps_[row] = eps_[row] + lgw_mask * (eps_collinear_eps_lerp_fg + eps_lerp_ortho_eps_fg - eps_[row]) + lgw_mask_inv * (eps_collinear_eps_lerp_bg + eps_lerp_ortho_eps_bg - eps_[row]) + + elif extra_options_flag("eps_proj_v3", extra_options): + + eps_collinear_eps_lerp_fg = get_collinear(eps_[row], eps_row) + eps_lerp_ortho_eps_fg = get_orthogonal(eps_row, eps_[row]) + + eps_collinear_eps_lerp_bg = get_collinear(eps_[row], eps_row_inv) + eps_lerp_ortho_eps_bg = get_orthogonal(eps_row_inv, eps_[row]) + + eps_[row] = eps_[row] + lgw_mask * (eps_collinear_eps_lerp_fg + eps_lerp_ortho_eps_fg - eps_[row]) + lgw_mask_inv * (eps_collinear_eps_lerp_bg + eps_lerp_ortho_eps_bg - eps_[row]) + + elif extra_options_flag("eps_proj_v5", extra_options): + + eps2g_collin = get_collinear(eps_[row], eps_row) + g2eps_ortho = get_orthogonal(eps_row, eps_[row]) + + g2eps_collin = get_collinear(eps_row, eps_[row]) + eps2g_ortho = get_orthogonal(eps_[row], eps_row) + + eps2i_collin = get_collinear(eps_[row], eps_row_inv) + i2eps_ortho = get_orthogonal(eps_row_inv, eps_[row]) + + i2eps_collin = get_collinear(eps_row_inv, eps_[row]) + eps2i_ortho = get_orthogonal(eps_[row], eps_row_inv) + + #eps_[row] = (eps2g_collin+g2eps_ortho) + (g2eps_collin+eps2g_ortho) + (eps2i_collin+i2eps_ortho) + (i2eps_collin+eps2i_ortho) + #eps_[row] = eps_[row] + lgw_mask * (eps2g_collin+g2eps_ortho) + (1-lgw_mask) * (g2eps_collin+eps2g_ortho) + lgw_mask_inv * (eps2i_collin+i2eps_ortho) + (1-lgw_mask_inv) * (i2eps_collin+eps2i_ortho) + + eps_[row] = lgw_mask * (eps2g_collin+g2eps_ortho) - lgw_mask * (g2eps_collin+eps2g_ortho) + lgw_mask_inv * (eps2i_collin+i2eps_ortho) - lgw_mask_inv * (i2eps_collin+eps2i_ortho) + + #eps_[row] = eps_[row] + lgw_mask * (eps_collinear_eps_lerp_fg + eps_lerp_ortho_eps_fg - eps_[row]) + lgw_mask_inv * (eps_collinear_eps_lerp_bg + eps_lerp_ortho_eps_bg - eps_[row]) + + + elif extra_options_flag("eps_proj_v4a", extra_options): + eps_row_lerp = eps_[row] + lgw_mask * (eps_row-eps_[row]) + lgw_mask_inv * (eps_row_inv-eps_[row]) + + eps_collinear_eps_lerp = get_collinear(eps_[row], eps_row_lerp) + eps_lerp_ortho_eps = get_orthogonal(eps_row_lerp, eps_[row]) + + eps_[row] = (1 - torch.clamp(lgw_mask + lgw_mask_inv, max=1.0)) * eps_[row] + torch.clamp((lgw_mask + lgw_mask_inv), max=1.0) * (eps_collinear_eps_lerp + eps_lerp_ortho_eps) + + + elif extra_options_flag("eps_proj_v4b", extra_options): + eps_row_lerp = eps_[row] + lgw_mask * (eps_row-eps_[row]) + lgw_mask_inv * (eps_row_inv-eps_[row]) + + eps_collinear_eps_lerp = get_collinear(eps_[row], eps_row_lerp) + eps_lerp_ortho_eps = get_orthogonal(eps_row_lerp, eps_[row]) + + eps_[row] = (1 - (lgw_mask + lgw_mask_inv)/2) * eps_[row] + ((lgw_mask + lgw_mask_inv)/2) * (eps_collinear_eps_lerp + eps_lerp_ortho_eps) + + elif extra_options_flag("eps_proj_v4c", extra_options): + eps_row_lerp = eps_[row] + lgw_mask * (eps_row-eps_[row]) + lgw_mask_inv * (eps_row_inv-eps_[row]) + + eps_collinear_eps_lerp = get_collinear(eps_[row], eps_row_lerp) + eps_lerp_ortho_eps = get_orthogonal(eps_row_lerp, eps_[row]) + + lgw_mask_sum = (lgw_mask + lgw_mask_inv) + + + eps_[row] = (1 - (lgw_mask + lgw_mask_inv)/2) * eps_[row] + ((lgw_mask + lgw_mask_inv)/2) * (eps_collinear_eps_lerp + eps_lerp_ortho_eps) + + elif extra_options_flag("eps_proj_v4e", extra_options): + eps_row_lerp = eps_[row] + lgw_mask * (eps_row-eps_[row]) + lgw_mask_inv * (eps_row_inv-eps_[row]) + + eps_collinear_eps_lerp = get_collinear(eps_[row], eps_row_lerp) + eps_lerp_ortho_eps = get_orthogonal(eps_row_lerp, eps_[row]) + + eps_sum = eps_collinear_eps_lerp + eps_lerp_ortho_eps + + eps_[row] = eps_[row] + self.mask * (eps_sum - eps_[row]) + self.mask_inv * (eps_sum - eps_[row]) + + elif extra_options_flag("eps_proj_self1", extra_options): + eps_row_lerp = eps_[row] + self.mask * (eps_row-eps_[row]) + self.mask_inv * (eps_row_inv-eps_[row]) + + eps_collinear_eps_lerp = get_collinear(eps_[row], eps_[row]) + eps_lerp_ortho_eps = get_orthogonal(eps_[row], eps_[row]) + + eps_[row] = eps_collinear_eps_lerp + eps_lerp_ortho_eps + + elif extra_options_flag("eps_proj_v4z", extra_options): + eps_row_lerp = eps_[row] + self.mask * (eps_row-eps_[row]) + self.mask_inv * (eps_row_inv-eps_[row]) + + eps_collinear_eps_lerp = get_collinear(eps_[row], eps_row_lerp) + eps_lerp_ortho_eps = get_orthogonal(eps_row_lerp, eps_[row]) + + peak = max(lgw, lgw_inv) + lgw_mask_sum = (lgw_mask + lgw_mask_inv) + + eps_sum = eps_collinear_eps_lerp + eps_lerp_ortho_eps + #NOT FINISHED!!! + #eps_[row] = eps_[row] + lgw_mask * (eps_sum - eps_[row]) + lgw_mask_inv * (eps_sum - eps_[row]) + + elif extra_options_flag("eps_proj_v5", extra_options): + eps_row_lerp = eps_[row] + lgw_mask * (eps_row-eps_[row]) + lgw_mask_inv * (eps_row_inv-eps_[row]) + + eps_collinear_eps_lerp = get_collinear(eps_[row], eps_row_lerp) + eps_lerp_ortho_eps = get_orthogonal(eps_row_lerp, eps_[row]) + + eps_[row] = ((lgw_mask + lgw_mask_inv)==0) * eps_[row] + ((lgw_mask + lgw_mask_inv)>0) * (eps_collinear_eps_lerp + eps_lerp_ortho_eps) + + elif extra_options_flag("eps_proj_v6", extra_options): + eps_row_lerp = eps_[row] + lgw_mask * (eps_row-eps_[row]) + lgw_mask_inv * (eps_row_inv-eps_[row]) + + eps_collinear_eps_lerp = get_collinear(eps_[row], eps_row_lerp) + eps_lerp_ortho_eps = get_orthogonal(eps_row_lerp, eps_[row]) + + eps_[row] = ((lgw_mask * lgw_mask_inv)==0) * eps_[row] + ((lgw_mask * lgw_mask_inv)>0) * (eps_collinear_eps_lerp + eps_lerp_ortho_eps) + + + elif extra_options_flag("eps_proj_old_default", extra_options): + eps_row_lerp = eps_[row] + lgw_mask * (eps_row-eps_[row]) + lgw_mask_inv * (eps_row_inv-eps_[row]) + #eps_row_lerp = eps_[row] + lgw_mask * (eps_row-eps_[row]) + (1-lgw_mask) * (eps_row_inv-eps_[row]) + + eps_collinear_eps_lerp = get_collinear(eps_[row], eps_row_lerp) + eps_lerp_ortho_eps = get_orthogonal(eps_row_lerp, eps_[row]) + + eps_[row] = eps_collinear_eps_lerp + eps_lerp_ortho_eps + + else: #elif extra_options_flag("eps_proj_v4d", extra_options): + #if row > 0: + #lgw_mask_factor = float(get_extra_options_kv("substep_lgw_mask_factor", "1.0", extra_options)) + #lgw_mask_inv_factor = float(get_extra_options_kv("substep_lgw_mask_inv_factor", "1.0", extra_options)) + lgw_mask_factor = 1 + if extra_options_flag("substep_eps_proj_scaling", extra_options): + lgw_mask_factor = 1/(row+1) + + if extra_options_flag("substep_eps_proj_factors", extra_options): + #value_str = get_extra_options_list("substep_eps_proj_factors", "", extra_options) + #float_list = [float(item.strip()) for item in value_str.split(',') if item.strip()] + float_list = get_extra_options_list("substep_eps_proj_factors", "", extra_options, ret_type=float) + lgw_mask_factor = float_list[row] + + eps_row_lerp = eps_[row] + self.mask * (eps_row-eps_[row]) + (1-self.mask) * (eps_row_inv-eps_[row]) + + eps_collinear_eps_lerp = get_collinear(eps_[row], eps_row_lerp) + eps_lerp_ortho_eps = get_orthogonal(eps_row_lerp, eps_[row]) + + eps_sum = eps_collinear_eps_lerp + eps_lerp_ortho_eps + + eps_[row] = eps_[row] + lgw_mask_factor*lgw_mask * (eps_sum - eps_[row]) + lgw_mask_factor*lgw_mask_inv * (eps_sum - eps_[row]) + + + + elif extra_options_flag("disable_lgw_scaling", extra_options): + eps_row, eps_row_inv = get_guide_epsilon_substep(x_0, x_, y0, y0_inv, s_, row, rk_type) + eps_[row] = eps_[row] + lgw_mask * (eps_row - eps_[row]) + lgw_mask_inv * (eps_row_inv - eps_[row]) + + + elif (lgw > 0 or lgw_inv > 0): # default old channelwise epsilon + avg, avg_inv = 0, 0 + for b, c in itertools.product(range(x_0.shape[0]), range(x_0.shape[1])): + avg += torch.norm(data_[row][b][c] - y0 [b][c]) + avg_inv += torch.norm(data_[row][b][c] - y0_inv[b][c]) + avg /= x_0.shape[1] + avg_inv /= x_0.shape[1] + + for b, c in itertools.product(range(x_0.shape[0]), range(x_0.shape[1])): + ratio = torch.nan_to_num(torch.norm(data_[row][b][c] - y0 [b][c]) / avg, 0) + ratio_inv = torch.nan_to_num(torch.norm(data_[row][b][c] - y0_inv[b][c]) / avg_inv, 0) + + eps_row, eps_row_inv = get_guide_epsilon_substep(x_0, x_, y0, y0_inv, s_, row, rk_type, b, c) + eps_[row][b][c] = eps_[row][b][c] + ratio * lgw_mask[b][c] * (eps_row - eps_[row][b][c]) + ratio_inv * lgw_mask_inv[b][c] * (eps_row_inv - eps_[row][b][c]) + + temporal_smoothing = float(get_extra_options_kv("temporal_smoothing", "0.0", extra_options)) + if temporal_smoothing > 0: + eps_[row] = apply_temporal_smoothing(eps_[row], temporal_smoothing) + + + + + elif (UNSAMPLE or guide_mode in {"resample", "unsample"}) and (lgw > 0 or lgw_inv > 0): + + cvf = rk.get_epsilon(x_0, x_[row+1], y0, sigma, s_[row], sigma_down, unsample_resample_scale, extra_options) + if UNSAMPLE and sigma > sigma_next and latent_guide_inv is not None: + cvf_inv = rk.get_epsilon(x_0, x_[row+1], y0_inv, sigma, s_[row], sigma_down, unsample_resample_scale, extra_options) + else: + cvf_inv = torch.zeros_like(cvf) + + tol_value = float(get_extra_options_kv("tol", "-1.0", extra_options)) + if tol_value >= 0: + for b, c in itertools.product(range(x_0.shape[0]), range(x_0.shape[1])): + current_diff = torch.norm(data_[row][b][c] - y0 [b][c]) + current_diff_inv = torch.norm(data_[row][b][c] - y0_inv[b][c]) + + lgw_scaled = torch.nan_to_num(1-(tol_value/current_diff), 0) + lgw_scaled_inv = torch.nan_to_num(1-(tol_value/current_diff_inv), 0) + + lgw_tmp = min(lgw , lgw_scaled) + lgw_tmp_inv = min(lgw_inv, lgw_scaled_inv) + + lgw_mask_clamp = torch.clamp(lgw_mask, max=lgw_tmp) + lgw_mask_clamp_inv = torch.clamp(lgw_mask_inv, max=lgw_tmp_inv) + + eps_[row][b][c] = eps_[row][b][c] + lgw_mask_clamp[b][c] * (cvf[b][c] - eps_[row][b][c]) + lgw_mask_clamp_inv[b][c] * (cvf_inv[b][c] - eps_[row][b][c]) + + elif extra_options_flag("disable_lgw_scaling", extra_options): + eps_[row] = eps_[row] + lgw_mask * (cvf - eps_[row]) + lgw_mask_inv * (cvf_inv - eps_[row]) + + else: + avg, avg_inv = 0, 0 + for b, c in itertools.product(range(x_0.shape[0]), range(x_0.shape[1])): + avg += torch.norm(lgw_mask [b][c] * data_[row][b][c] - lgw_mask [b][c] * y0 [b][c]) + avg_inv += torch.norm(lgw_mask_inv[b][c] * data_[row][b][c] - lgw_mask_inv[b][c] * y0_inv[b][c]) + avg /= x_0.shape[1] + avg_inv /= x_0.shape[1] + + for b, c in itertools.product(range(x_0.shape[0]), range(x_0.shape[1])): + ratio = torch.nan_to_num(torch.norm(lgw_mask [b][c] * data_[row][b][c] - lgw_mask [b][c] * y0 [b][c]) / avg, 0) + ratio_inv = torch.nan_to_num(torch.norm(lgw_mask_inv[b][c] * data_[row][b][c] - lgw_mask_inv[b][c] * y0_inv[b][c]) / avg_inv, 0) + + eps_[row][b][c] = eps_[row][b][c] + ratio * lgw_mask[b][c] * (cvf[b][c] - eps_[row][b][c]) + ratio_inv * lgw_mask_inv[b][c] * (cvf_inv[b][c] - eps_[row][b][c]) + + if extra_options_flag("substep_eps_ch_mean_std", extra_options): + eps_[row] = normalize_latent(eps_[row], eps_orig[row]) + if extra_options_flag("substep_eps_ch_mean", extra_options): + eps_[row] = normalize_latent(eps_[row], eps_orig[row], std=False) + if extra_options_flag("substep_eps_ch_std", extra_options): + eps_[row] = normalize_latent(eps_[row], eps_orig[row], mean=False) + if extra_options_flag("substep_eps_mean_std", extra_options): + eps_[row] = normalize_latent(eps_[row], eps_orig[row], channelwise=False) + if extra_options_flag("substep_eps_mean", extra_options): + eps_[row] = normalize_latent(eps_[row], eps_orig[row], std=False, channelwise=False) + if extra_options_flag("substep_eps_std", extra_options): + eps_[row] = normalize_latent(eps_[row], eps_orig[row], mean=False, channelwise=False) + return eps_, x_ + + + + @torch.no_grad + def process_guides_poststep(self, x, denoised, eps, step, extra_options): + x_orig = x.clone() + mean_weight = float(get_extra_options_kv("mean_weight", "0.01", extra_options)) + + y0 = self.y0 + if self.y0.shape[0] > 1: + y0 = self.y0[min(step, self.y0.shape[0]-1)].unsqueeze(0) + y0_inv = self.y0_inv + + lgw_mask = self.lgw_masks[step].clone() + lgw_mask_inv = self.lgw_masks_inv[step].clone() if self.lgw_masks_inv is not None else None + mask = self.mask #needed for bitwise mask below + + lgw = self.lgw[step] + lgw_inv = self.lgw_inv[step] + + latent_guide = self.latent_guide + latent_guide_inv = self.latent_guide_inv + guide_mode = self.guide_mode + UNSAMPLE = self.UNSAMPLE + + if self.guide_mode: + data_norm = denoised - denoised.mean(dim=(-2,-1), keepdim=True) + y0_norm = y0 - y0.mean(dim=(-2,-1), keepdim=True) + y0_inv_norm = y0_inv - y0_inv.mean(dim=(-2,-1), keepdim=True) + + y0_cossim = get_cosine_similarity(data_norm*lgw_mask, y0_norm *lgw_mask) + y0_cossim_inv = get_cosine_similarity(data_norm*lgw_mask_inv, y0_inv_norm*lgw_mask_inv) + + if y0_cossim < self.guide_cossim_cutoff_ or y0_cossim_inv < self.guide_bkg_cossim_cutoff_: + lgw_mask_cossim, lgw_mask_cossim_inv = lgw_mask, lgw_mask_inv + if y0_cossim >= self.guide_cossim_cutoff_: + lgw_mask_cossim = torch.zeros_like(lgw_mask) + if y0_cossim_inv >= self.guide_bkg_cossim_cutoff_: + lgw_mask_cossim_inv = torch.zeros_like(lgw_mask_inv) + lgw_mask = lgw_mask_cossim + lgw_mask_inv = lgw_mask_cossim_inv + else: + return x + + if guide_mode in {"epsilon_dynamic_mean_std", "epsilon_dynamic_mean", "epsilon_dynamic_std", "epsilon_dynamic_mean_from_bkg"}: + + denoised_masked = denoised * ((mask==1)*mask) + denoised_masked_inv = denoised * ((mask==0)*(1-mask)) + + + d_shift, d_shift_inv = torch.zeros_like(x), torch.zeros_like(x) + + for b, c in itertools.product(range(x.shape[0]), range(x.shape[1])): + denoised_mask = denoised[b][c][mask[b][c] == 1] + denoised_mask_inv = denoised[b][c][mask[b][c] == 0] + + if guide_mode == "epsilon_dynamic_mean_std": + d_shift[b][c] = (denoised_masked[b][c] - denoised_mask.mean()) / denoised_mask.std() + d_shift[b][c] = (d_shift[b][c] * denoised_mask_inv.std()) + denoised_mask_inv.mean() + + elif guide_mode == "epsilon_dynamic_mean": + d_shift[b][c] = denoised_masked[b][c] - denoised_mask.mean() + denoised_mask_inv.mean() + d_shift_inv[b][c] = denoised_masked_inv[b][c] - denoised_mask_inv.mean() + denoised_mask.mean() + + elif guide_mode == "epsilon_dynamic_mean_from_bkg": + d_shift[b][c] = denoised_masked[b][c] - denoised_mask.mean() + denoised_mask_inv.mean() + + if guide_mode in {"epsilon_dynamic_mean_std", "epsilon_dynamic_mean_from_bkg"}: + denoised_shifted = denoised + mean_weight * lgw_mask * (d_shift - denoised_masked) + elif guide_mode == "epsilon_dynamic_mean": + denoised_shifted = denoised + mean_weight * lgw_mask * (d_shift - denoised_masked) + mean_weight * lgw_mask_inv * (d_shift_inv - denoised_masked_inv) + + x = denoised_shifted + eps + + + if UNSAMPLE == False and (latent_guide is not None or latent_guide_inv is not None) and guide_mode in ("hard_light", "blend", "blend_projection", "mean_std", "mean", "mean_tiled", "std"): + if guide_mode == "hard_light": + d_shift, d_shift_inv = hard_light_blend(y0, denoised), hard_light_blend(y0_inv, denoised) + elif guide_mode == "blend": + d_shift, d_shift_inv = y0, y0_inv + + elif guide_mode == "blend_projection": + #d_shift = get_collinear(denoised, y0) + #d_shift_inv = get_collinear(denoised, y0_inv) + + d_lerp = denoised + lgw_mask * (y0-denoised) + lgw_mask_inv * (y0_inv-denoised) + + d_collinear_d_lerp = get_collinear(denoised, d_lerp) + d_lerp_ortho_d = get_orthogonal(d_lerp, denoised) + + denoised_shifted = d_collinear_d_lerp + d_lerp_ortho_d + x = denoised_shifted + eps + return x + + + elif guide_mode == "mean_std": + d_shift, d_shift_inv = normalize_latent([denoised, denoised], [y0, y0_inv]) + elif guide_mode == "mean": + d_shift, d_shift_inv = normalize_latent([denoised, denoised], [y0, y0_inv], std=False) + elif guide_mode == "std": + d_shift, d_shift_inv = normalize_latent([denoised, denoised], [y0, y0_inv], mean=False) + elif guide_mode == "mean_tiled": + mean_tile_size = int(get_extra_options_kv("mean_tile", "8", extra_options)) + y0_tiled = rearrange(y0, "b c (h t1) (w t2) -> (t1 t2) b c h w", t1=mean_tile_size, t2=mean_tile_size) + y0_inv_tiled = rearrange(y0_inv, "b c (h t1) (w t2) -> (t1 t2) b c h w", t1=mean_tile_size, t2=mean_tile_size) + denoised_tiled = rearrange(denoised, "b c (h t1) (w t2) -> (t1 t2) b c h w", t1=mean_tile_size, t2=mean_tile_size) + d_shift_tiled, d_shift_inv_tiled = torch.zeros_like(y0_tiled), torch.zeros_like(y0_tiled) + for i in range(y0_tiled.shape[0]): + d_shift_tiled[i], d_shift_inv_tiled[i] = normalize_latent([denoised_tiled[i], denoised_tiled[i]], [y0_tiled[i], y0_inv_tiled[i]], std=False) + d_shift = rearrange(d_shift_tiled, "(t1 t2) b c h w -> b c (h t1) (w t2)", t1=mean_tile_size, t2=mean_tile_size) + d_shift_inv = rearrange(d_shift_inv_tiled, "(t1 t2) b c h w -> b c (h t1) (w t2)", t1=mean_tile_size, t2=mean_tile_size) + + + if guide_mode in ("hard_light", "blend", "mean_std", "mean", "mean_tiled", "std"): + if latent_guide_inv is None: + denoised_shifted = denoised + lgw_mask * (d_shift - denoised) + else: + denoised_shifted = denoised + lgw_mask * (d_shift - denoised) + lgw_mask_inv * (d_shift_inv - denoised) + + if extra_options_flag("poststep_denoised_ch_mean_std", extra_options): + denoised_shifted = normalize_latent(denoised_shifted, denoised) + if extra_options_flag("poststep_denoised_ch_mean", extra_options): + denoised_shifted = normalize_latent(denoised_shifted, denoised, std=False) + if extra_options_flag("poststep_denoised_ch_std", extra_options): + denoised_shifted = normalize_latent(denoised_shifted, denoised, mean=False) + if extra_options_flag("poststep_denoised_mean_std", extra_options): + denoised_shifted = normalize_latent(denoised_shifted, denoised, channelwise=False) + if extra_options_flag("poststep_denoised_mean", extra_options): + denoised_shifted = normalize_latent(denoised_shifted, denoised, std=False, channelwise=False) + if extra_options_flag("poststep_denoised_std", extra_options): + denoised_shifted = normalize_latent(denoised_shifted, denoised, mean=False, channelwise=False) + + x = denoised_shifted + eps + + if extra_options_flag("poststep_x_ch_mean_std", extra_options): + x = normalize_latent(x, x_orig) + if extra_options_flag("poststep_x_ch_mean", extra_options): + x = normalize_latent(x, x_orig, std=False) + if extra_options_flag("poststep_x_ch_std", extra_options): + x = normalize_latent(x, x_orig, mean=False) + if extra_options_flag("poststep_x_mean_std", extra_options): + x = normalize_latent(x, x_orig, channelwise=False) + if extra_options_flag("poststep_x_mean", extra_options): + x = normalize_latent(x, x_orig, std=False, channelwise=False) + if extra_options_flag("poststep_x_std", extra_options): + x = normalize_latent(x, x_orig, mean=False, channelwise=False) + return x + + + +def apply_frame_weights(mask, frame_weights): + if frame_weights is not None: + for f in range(mask.shape[2]): + frame_weight = frame_weights[f] + mask[..., f:f+1, :, :] *= frame_weight + + + +def prepare_mask(x, mask, LGW_MASK_RESCALE_MIN) -> Tuple[torch.Tensor, bool]: + if mask is None: + mask = torch.ones_like(x) + LGW_MASK_RESCALE_MIN = False + return mask, LGW_MASK_RESCALE_MIN + + spatial_mask = mask.unsqueeze(1) + target_height = x.shape[-2] + target_width = x.shape[-1] + spatial_mask = F.interpolate(spatial_mask, size=(target_height, target_width), mode='bilinear', align_corners=False) + + while spatial_mask.dim() < x.dim(): + spatial_mask = spatial_mask.unsqueeze(2) + + repeat_shape = [1] #batch + for i in range(1, x.dim() - 2): + repeat_shape.append(x.shape[i]) + repeat_shape.extend([1, 1]) #height and width + + mask = spatial_mask.repeat(*repeat_shape).to(x.dtype).to(x.device) + + del spatial_mask + return mask, LGW_MASK_RESCALE_MIN + +def prepare_weighted_masks(mask, mask_inv, lgw_, lgw_inv_, latent_guide, latent_guide_inv, LGW_MASK_RESCALE_MIN): + if LGW_MASK_RESCALE_MIN: + lgw_mask = mask * (1-lgw_) + lgw_ + lgw_mask_inv = (1-mask) * (1-lgw_inv_) + lgw_inv_ + else: + if latent_guide is not None: + lgw_mask = mask * lgw_ + else: + lgw_mask = torch.zeros_like(mask) + if latent_guide_inv is not None: + if mask_inv is not None: + lgw_mask_inv = torch.minimum(1-mask_inv, (1-mask) * lgw_inv_) + else: + lgw_mask_inv = (1-mask) * lgw_inv_ + else: + lgw_mask_inv = torch.zeros_like(mask) + return lgw_mask, lgw_mask_inv + +def apply_temporal_smoothing(tensor, temporal_smoothing): + if temporal_smoothing <= 0 or tensor.dim() != 5: + return tensor + + kernel_size = 5 + padding = kernel_size // 2 + temporal_kernel = torch.tensor( + [0.1, 0.2, 0.4, 0.2, 0.1], + device=tensor.device, dtype=tensor.dtype + ) * temporal_smoothing + temporal_kernel[kernel_size//2] += (1 - temporal_smoothing) + temporal_kernel = temporal_kernel / temporal_kernel.sum() + + # resahpe for conv1d + b, c, f, h, w = tensor.shape + data_flat = tensor.permute(0, 1, 3, 4, 2).reshape(-1, f) + + # apply smoohting + data_smooth = F.conv1d( + data_flat.unsqueeze(1), + temporal_kernel.view(1, 1, -1), + padding=padding + ).squeeze(1) + + return data_smooth.view(b, c, h, w, f).permute(0, 1, 4, 2, 3) + +def get_guide_epsilon_substep(x_0, x_, y0, y0_inv, s_, row, rk_type, b=None, c=None): + s_in = x_0.new_ones([x_0.shape[0]]) + + if b is not None and c is not None: + index = (b, c) + elif b is not None: + index = (b,) + else: + index = () + + if RK_Method.is_exponential(rk_type): + eps_row = y0 [index] - x_0[index] + eps_row_inv = y0_inv[index] - x_0[index] + else: + eps_row = (x_[row+1][index] - y0 [index]) / (s_[row] * s_in) + eps_row_inv = (x_[row+1][index] - y0_inv[index]) / (s_[row] * s_in) + + return eps_row, eps_row_inv + +def get_guide_epsilon(x_0, x_, y0, sigma, rk_type, b=None, c=None): + s_in = x_0.new_ones([x_0.shape[0]]) + + if b is not None and c is not None: + index = (b, c) + elif b is not None: + index = (b,) + else: + index = () + + if RK_Method.is_exponential(rk_type): + eps = y0 [index] - x_0[index] + else: + eps = (x_[index] - y0 [index]) / (sigma * s_in) + + return eps + + + + +@torch.no_grad +def noise_cossim_guide_tiled(x_list, guide, cossim_mode="forward", tile_size=2, step=0): + + guide_tiled = rearrange(guide, "b c (h t1) (w t2) -> b (t1 t2) c h w", t1=tile_size, t2=tile_size) + + x_tiled_list = [ + rearrange(x, "b c (h t1) (w t2) -> b (t1 t2) c h w", t1=tile_size, t2=tile_size) + for x in x_list + ] + x_tiled_stack = torch.stack([x_tiled[0] for x_tiled in x_tiled_list]) # [n_x, n_tiles, c, h, w] + + guide_flat = guide_tiled[0].view(guide_tiled.shape[1], -1).unsqueeze(0) # [1, n_tiles, c*h*w] + x_flat = x_tiled_stack.view(x_tiled_stack.size(0), x_tiled_stack.size(1), -1) # [n_x, n_tiles, c*h*w] + + cossim_tmp_all = F.cosine_similarity(x_flat, guide_flat, dim=-1) # [n_x, n_tiles] + + if cossim_mode == "forward": + indices = cossim_tmp_all.argmax(dim=0) + elif cossim_mode == "reverse": + indices = cossim_tmp_all.argmin(dim=0) + elif cossim_mode == "orthogonal": + indices = torch.abs(cossim_tmp_all).argmin(dim=0) + elif cossim_mode == "forward_reverse": + if step % 2 == 0: + indices = cossim_tmp_all.argmax(dim=0) + else: + indices = cossim_tmp_all.argmin(dim=0) + elif cossim_mode == "reverse_forward": + if step % 2 == 1: + indices = cossim_tmp_all.argmax(dim=0) + else: + indices = cossim_tmp_all.argmin(dim=0) + elif cossim_mode == "orthogonal_reverse": + if step % 2 == 0: + indices = torch.abs(cossim_tmp_all).argmin(dim=0) + else: + indices = cossim_tmp_all.argmin(dim=0) + elif cossim_mode == "reverse_orthogonal": + if step % 2 == 1: + indices = torch.abs(cossim_tmp_all).argmin(dim=0) + else: + indices = cossim_tmp_all.argmin(dim=0) + else: + target_value = float(cossim_mode) + indices = torch.abs(cossim_tmp_all - target_value).argmin(dim=0) + + x_tiled_out = x_tiled_stack[indices, torch.arange(indices.size(0))] # [n_tiles, c, h, w] + + x_tiled_out = x_tiled_out.unsqueeze(0) + x_detiled = rearrange(x_tiled_out, "b (t1 t2) c h w -> b c (h t1) (w t2)", t1=tile_size, t2=tile_size) + + return x_detiled + + +@torch.no_grad +def noise_cossim_eps_tiled(x_list, eps, noise_list, cossim_mode="forward", tile_size=2, step=0): + + eps_tiled = rearrange(eps, "b c (h t1) (w t2) -> b (t1 t2) c h w", t1=tile_size, t2=tile_size) + x_tiled_list = [ + rearrange(x, "b c (h t1) (w t2) -> b (t1 t2) c h w", t1=tile_size, t2=tile_size) + for x in x_list + ] + noise_tiled_list = [ + rearrange(noise, "b c (h t1) (w t2) -> b (t1 t2) c h w", t1=tile_size, t2=tile_size) + for noise in noise_list + ] + + noise_tiled_stack = torch.stack([noise_tiled[0] for noise_tiled in noise_tiled_list]) # [n_x, n_tiles, c, h, w] + eps_expanded = eps_tiled[0].view(eps_tiled.shape[1], -1).unsqueeze(0) # [1, n_tiles, c*h*w] + noise_flat = noise_tiled_stack.view(noise_tiled_stack.size(0), noise_tiled_stack.size(1), -1) # [n_x, n_tiles, c*h*w] + cossim_tmp_all = F.cosine_similarity(noise_flat, eps_expanded, dim=-1) # [n_x, n_tiles] + + if cossim_mode == "forward": + indices = cossim_tmp_all.argmax(dim=0) + elif cossim_mode == "reverse": + indices = cossim_tmp_all.argmin(dim=0) + elif cossim_mode == "orthogonal": + indices = torch.abs(cossim_tmp_all).argmin(dim=0) + elif cossim_mode == "orthogonal_pos": + positive_mask = cossim_tmp_all > 0 + positive_tmp = torch.where(positive_mask, cossim_tmp_all, torch.full_like(cossim_tmp_all, float('inf'))) + indices = positive_tmp.argmin(dim=0) + elif cossim_mode == "orthogonal_neg": + negative_mask = cossim_tmp_all < 0 + negative_tmp = torch.where(negative_mask, cossim_tmp_all, torch.full_like(cossim_tmp_all, float('-inf'))) + indices = negative_tmp.argmax(dim=0) + elif cossim_mode == "orthogonal_posneg": + if step % 2 == 0: + positive_mask = cossim_tmp_all > 0 + positive_tmp = torch.where(positive_mask, cossim_tmp_all, torch.full_like(cossim_tmp_all, float('inf'))) + indices = positive_tmp.argmin(dim=0) + else: + negative_mask = cossim_tmp_all < 0 + negative_tmp = torch.where(negative_mask, cossim_tmp_all, torch.full_like(cossim_tmp_all, float('-inf'))) + indices = negative_tmp.argmax(dim=0) + elif cossim_mode == "orthogonal_negpos": + if step % 2 == 1: + positive_mask = cossim_tmp_all > 0 + positive_tmp = torch.where(positive_mask, cossim_tmp_all, torch.full_like(cossim_tmp_all, float('inf'))) + indices = positive_tmp.argmin(dim=0) + else: + negative_mask = cossim_tmp_all < 0 + negative_tmp = torch.where(negative_mask, cossim_tmp_all, torch.full_like(cossim_tmp_all, float('-inf'))) + indices = negative_tmp.argmax(dim=0) + elif cossim_mode == "forward_reverse": + if step % 2 == 0: + indices = cossim_tmp_all.argmax(dim=0) + else: + indices = cossim_tmp_all.argmin(dim=0) + elif cossim_mode == "reverse_forward": + if step % 2 == 1: + indices = cossim_tmp_all.argmax(dim=0) + else: + indices = cossim_tmp_all.argmin(dim=0) + elif cossim_mode == "orthogonal_reverse": + if step % 2 == 0: + indices = torch.abs(cossim_tmp_all).argmin(dim=0) + else: + indices = cossim_tmp_all.argmin(dim=0) + elif cossim_mode == "reverse_orthogonal": + if step % 2 == 1: + indices = torch.abs(cossim_tmp_all).argmin(dim=0) + else: + indices = cossim_tmp_all.argmin(dim=0) + else: + target_value = float(cossim_mode) + indices = torch.abs(cossim_tmp_all - target_value).argmin(dim=0) + #else: + # raise ValueError(f"Unknown cossim_mode: {cossim_mode}") + + x_tiled_stack = torch.stack([x_tiled[0] for x_tiled in x_tiled_list]) # [n_x, n_tiles, c, h, w] + x_tiled_out = x_tiled_stack[indices, torch.arange(indices.size(0))] # [n_tiles, c, h, w] + + x_tiled_out = x_tiled_out.unsqueeze(0) # restore batch dim + x_detiled = rearrange(x_tiled_out, "b (t1 t2) c h w -> b c (h t1) (w t2)", t1=tile_size, t2=tile_size) + return x_detiled + + + +@torch.no_grad +def noise_cossim_guide_eps_tiled(x_0, x_list, y0, noise_list, cossim_mode="forward", tile_size=2, step=0, sigma=None, rk_type=None): + + x_tiled_stack = torch.stack([ + rearrange(x, "b c (h t1) (w t2) -> b (t1 t2) c h w", t1=tile_size, t2=tile_size)[0] + for x in x_list + ]) # [n_x, n_tiles, c, h, w] + eps_guide_stack = torch.stack([ + rearrange(x - y0, "b c (h t1) (w t2) -> b (t1 t2) c h w", t1=tile_size, t2=tile_size)[0] + for x in x_list + ]) # [n_x, n_tiles, c, h, w] + del x_list + + noise_tiled_stack = torch.stack([ + rearrange(noise, "b c (h t1) (w t2) -> b (t1 t2) c h w", t1=tile_size, t2=tile_size)[0] + for noise in noise_list + ]) # [n_x, n_tiles, c, h, w] + del noise_list + + noise_flat = noise_tiled_stack.view(noise_tiled_stack.size(0), noise_tiled_stack.size(1), -1) # [n_x, n_tiles, c*h*w] + eps_guide_flat = eps_guide_stack.view(eps_guide_stack.size(0), eps_guide_stack.size(1), -1) # [n_x, n_tiles, c*h*w] + + cossim_tmp_all = F.cosine_similarity(noise_flat, eps_guide_flat, dim=-1) # [n_x, n_tiles] + del noise_tiled_stack, noise_flat, eps_guide_stack, eps_guide_flat + + if cossim_mode == "forward": + indices = cossim_tmp_all.argmax(dim=0) + elif cossim_mode == "reverse": + indices = cossim_tmp_all.argmin(dim=0) + elif cossim_mode == "orthogonal": + indices = torch.abs(cossim_tmp_all).argmin(dim=0) + elif cossim_mode == "orthogonal_pos": + positive_mask = cossim_tmp_all > 0 + positive_tmp = torch.where(positive_mask, cossim_tmp_all, torch.full_like(cossim_tmp_all, float('inf'))) + indices = positive_tmp.argmin(dim=0) + elif cossim_mode == "orthogonal_neg": + negative_mask = cossim_tmp_all < 0 + negative_tmp = torch.where(negative_mask, cossim_tmp_all, torch.full_like(cossim_tmp_all, float('-inf'))) + indices = negative_tmp.argmax(dim=0) + elif cossim_mode == "orthogonal_posneg": + if step % 2 == 0: + positive_mask = cossim_tmp_all > 0 + positive_tmp = torch.where(positive_mask, cossim_tmp_all, torch.full_like(cossim_tmp_all, float('inf'))) + indices = positive_tmp.argmin(dim=0) + else: + negative_mask = cossim_tmp_all < 0 + negative_tmp = torch.where(negative_mask, cossim_tmp_all, torch.full_like(cossim_tmp_all, float('-inf'))) + indices = negative_tmp.argmax(dim=0) + elif cossim_mode == "orthogonal_negpos": + if step % 2 == 1: + positive_mask = cossim_tmp_all > 0 + positive_tmp = torch.where(positive_mask, cossim_tmp_all, torch.full_like(cossim_tmp_all, float('inf'))) + indices = positive_tmp.argmin(dim=0) + else: + negative_mask = cossim_tmp_all < 0 + negative_tmp = torch.where(negative_mask, cossim_tmp_all, torch.full_like(cossim_tmp_all, float('-inf'))) + indices = negative_tmp.argmax(dim=0) + elif cossim_mode == "forward_reverse": + if step % 2 == 0: + indices = cossim_tmp_all.argmax(dim=0) + else: + indices = cossim_tmp_all.argmin(dim=0) + elif cossim_mode == "reverse_forward": + if step % 2 == 1: + indices = cossim_tmp_all.argmax(dim=0) + else: + indices = cossim_tmp_all.argmin(dim=0) + elif cossim_mode == "orthogonal_reverse": + if step % 2 == 0: + indices = torch.abs(cossim_tmp_all).argmin(dim=0) + else: + indices = cossim_tmp_all.argmin(dim=0) + elif cossim_mode == "reverse_orthogonal": + if step % 2 == 1: + indices = torch.abs(cossim_tmp_all).argmin(dim=0) + else: + indices = cossim_tmp_all.argmin(dim=0) + else: + target_value = float(cossim_mode) + indices = torch.abs(cossim_tmp_all - target_value).argmin(dim=0) + + x_tiled_out = x_tiled_stack[indices, torch.arange(indices.size(0))] # [n_tiles, c, h, w] + del x_tiled_stack + + x_tiled_out = x_tiled_out.unsqueeze(0) + x_detiled = rearrange(x_tiled_out, "b (t1 t2) c h w -> b c (h t1) (w t2)", t1=tile_size, t2=tile_size) + + return x_detiled + + + + + + +def get_collinear(x, y): + + y_flat = y.view(y.size(0), -1).clone() + x_flat = x.view(x.size(0), -1).clone() + + y_flat /= y_flat.norm(dim=-1, keepdim=True) + x_proj_y = torch.sum(x_flat * y_flat, dim=-1, keepdim=True) * y_flat + + return x_proj_y.view_as(x) + + +def get_orthogonal(x, y): + + y_flat = y.view(y.size(0), -1).clone() + x_flat = x.view(x.size(0), -1).clone() + + y_flat /= y_flat.norm(dim=-1, keepdim=True) + x_proj_y = torch.sum(x_flat * y_flat, dim=-1, keepdim=True) * y_flat + + x_ortho_y = x_flat - x_proj_y + + return x_ortho_y.view_as(x) + + + +def get_orthogonal_noise_from_channelwise(*refs, max_iter=500, max_score=1e-15): + noise, *refs = refs + noise_tmp = noise.clone() + #b,c,h,w = noise.shape + if (noise.dim() == 4): + b,ch,h,w = noise.shape + elif (noise.dim() == 5): + b,ch,t,h,w = noise.shape + + for i in range(max_iter): + noise_tmp = gram_schmidt_channels_optimized(noise_tmp, *refs) + + cossim_scores = [] + for ref in refs: + #for c in range(noise.shape[-3]): + for c in range(ch): + cossim_scores.append(get_cosine_similarity(noise_tmp[0][c], ref[0][c]).abs()) + cossim_scores.append(get_cosine_similarity(noise_tmp[0], ref[0]).abs()) + + if max(cossim_scores) < max_score: + break + + return noise_tmp + + + +def gram_schmidt_channels_optimized(A, *refs): + if (A.dim() == 4): + b,c,h,w = A.shape + elif (A.dim() == 5): + b,c,t,h,w = A.shape + + A_flat = A.view(b, c, -1) + + for ref in refs: + ref_flat = ref.view(b, c, -1).clone() + + ref_flat /= ref_flat.norm(dim=-1, keepdim=True) + + proj_coeff = torch.sum(A_flat * ref_flat, dim=-1, keepdim=True) + projection = proj_coeff * ref_flat + + A_flat -= projection + + return A_flat.view_as(A) + + + +class NoiseStepHandlerOSDE: + def __init__(self, x, eps=None, data=None, x_init=None, guide=None, guide_bkg=None): + self.noise = None + self.x = x + self.eps = eps + self.data = data + self.x_init = x_init + self.guide = guide + self.guide_bkg = guide_bkg + + self.eps_list = None + + self.noise_cossim_map = { + "eps_orthogonal": [self.noise, self.eps], + "eps_data_orthogonal": [self.noise, self.eps, self.data], + + "data_orthogonal": [self.noise, self.data], + "xinit_orthogonal": [self.noise, self.x_init], + + "x_orthogonal": [self.noise, self.x], + "x_data_orthogonal": [self.noise, self.x, self.data], + "x_eps_orthogonal": [self.noise, self.x, self.eps], + + "x_eps_data_orthogonal": [self.noise, self.x, self.eps, self.data], + "x_eps_data_xinit_orthogonal": [self.noise, self.x, self.eps, self.data, self.x_init], + + "x_eps_guide_orthogonal": [self.noise, self.x, self.eps, self.guide], + "x_eps_guide_bkg_orthogonal": [self.noise, self.x, self.eps, self.guide_bkg], + + "noise_orthogonal": [self.noise, self.x_init], + + "guide_orthogonal": [self.noise, self.guide], + "guide_bkg_orthogonal": [self.noise, self.guide_bkg], + } + + def check_cossim_source(self, source): + return source in self.noise_cossim_map + + def get_ortho_noise(self, noise, prev_noises=None, max_iter=100, max_score=1e-7, NOISE_COSSIM_SOURCE="eps_orthogonal"): + + if NOISE_COSSIM_SOURCE not in self.noise_cossim_map: + raise ValueError(f"Invalid NOISE_COSSIM_SOURCE: {NOISE_COSSIM_SOURCE}") + + self.noise_cossim_map[NOISE_COSSIM_SOURCE][0] = noise + + params = self.noise_cossim_map[NOISE_COSSIM_SOURCE] + + noise = get_orthogonal_noise_from_channelwise(*params, max_iter=max_iter, max_score=max_score) + + return noise + + + +def handle_tiled_etc_noise_steps(x_0, x, x_prenoise, x_init, eps, denoised, y0, y0_inv, step, + rk_type, rk, sigma_up, sigma, sigma_next, alpha_ratio, s_noise, noise_mode, SDE_NOISE_EXTERNAL, sde_noise_t, + NOISE_COSSIM_SOURCE, NOISE_COSSIM_MODE, noise_cossim_tile_size, noise_cossim_iterations, + extra_options): + + x_tmp, cossim_tmp, noise_tmp_list = [], [], [] + if step > int(get_extra_options_kv("noise_cossim_end_step", "10000", extra_options)): + NOISE_COSSIM_SOURCE = get_extra_options_kv("noise_cossim_takeover_source", "eps", extra_options) + NOISE_COSSIM_MODE = get_extra_options_kv("noise_cossim_takeover_mode", "forward", extra_options) + noise_cossim_tile_size = int(get_extra_options_kv("noise_cossim_takeover_tile", str(noise_cossim_tile_size), extra_options)) + noise_cossim_iterations = int(get_extra_options_kv("noise_cossim_takeover_iterations", str(noise_cossim_iterations), extra_options)) + + for i in range(noise_cossim_iterations): + x_tmp.append(rk.add_noise_post(x, sigma_up, sigma, sigma_next, alpha_ratio, s_noise, noise_mode, SDE_NOISE_EXTERNAL, sde_noise_t) )#y0, lgw, sigma_down are currently unused + noise_tmp = x_tmp[i] - x + if extra_options_flag("noise_noise_zscore_norm", extra_options): + noise_tmp = (noise_tmp - noise_tmp.mean()) / noise_tmp.std() + if extra_options_flag("noise_eps_zscore_norm", extra_options): + eps = (eps - eps.mean()) / eps.std() + if NOISE_COSSIM_SOURCE in ("eps_tiled", "guide_epsilon_tiled", "guide_bkg_epsilon_tiled", "iig_tiled"): + noise_tmp_list.append(noise_tmp) + if NOISE_COSSIM_SOURCE == "eps": + cossim_tmp.append(get_cosine_similarity(eps, noise_tmp)) + if NOISE_COSSIM_SOURCE == "eps_ch": + cossim_total = torch.zeros_like(eps[0][0][0][0]) + for ch in range(eps.shape[1]): + cossim_total += get_cosine_similarity(eps[0][ch], noise_tmp[0][ch]) + cossim_tmp.append(cossim_total) + elif NOISE_COSSIM_SOURCE == "data": + cossim_tmp.append(get_cosine_similarity(denoised, noise_tmp)) + elif NOISE_COSSIM_SOURCE == "latent": + cossim_tmp.append(get_cosine_similarity(x_prenoise, noise_tmp)) + elif NOISE_COSSIM_SOURCE == "x_prenoise": + cossim_tmp.append(get_cosine_similarity(x_prenoise, x_tmp[i])) + elif NOISE_COSSIM_SOURCE == "x": + cossim_tmp.append(get_cosine_similarity(x, x_tmp[i])) + elif NOISE_COSSIM_SOURCE == "x_data": + cossim_tmp.append(get_cosine_similarity(denoised, x_tmp[i])) + elif NOISE_COSSIM_SOURCE == "x_init_vs_noise": + cossim_tmp.append(get_cosine_similarity(x_init, noise_tmp)) + elif NOISE_COSSIM_SOURCE == "mom": + cossim_tmp.append(get_cosine_similarity(denoised, x + sigma_next*noise_tmp)) + elif NOISE_COSSIM_SOURCE == "guide": + cossim_tmp.append(get_cosine_similarity(y0, x_tmp[i])) + elif NOISE_COSSIM_SOURCE == "guide_bkg": + cossim_tmp.append(get_cosine_similarity(y0_inv, x_tmp[i])) + + if step < int(get_extra_options_kv("noise_cossim_start_step", "0", extra_options)): + x = x_tmp[0] + + elif (NOISE_COSSIM_SOURCE == "eps_tiled"): + x = noise_cossim_eps_tiled(x_tmp, eps, noise_tmp_list, cossim_mode=NOISE_COSSIM_MODE, tile_size=noise_cossim_tile_size, step=step) + elif (NOISE_COSSIM_SOURCE == "guide_epsilon_tiled"): + x = noise_cossim_guide_eps_tiled(x_0, x_tmp, y0, noise_tmp_list, cossim_mode=NOISE_COSSIM_MODE, tile_size=noise_cossim_tile_size, step=step, sigma=sigma, rk_type=rk_type) + elif (NOISE_COSSIM_SOURCE == "guide_bkg_epsilon_tiled"): + x = noise_cossim_guide_eps_tiled(x_0, x_tmp, y0_inv, noise_tmp_list, cossim_mode=NOISE_COSSIM_MODE, tile_size=noise_cossim_tile_size, step=step, sigma=sigma, rk_type=rk_type) + elif (NOISE_COSSIM_SOURCE == "guide_tiled"): + x = noise_cossim_guide_tiled(x_tmp, y0, cossim_mode=NOISE_COSSIM_MODE, tile_size=noise_cossim_tile_size, step=step) + elif (NOISE_COSSIM_SOURCE == "guide_bkg_tiled"): + x = noise_cossim_guide_tiled(x_tmp, y0_inv, cossim_mode=NOISE_COSSIM_MODE, tile_size=noise_cossim_tile_size) + else: + for i in range(len(x_tmp)): + if (NOISE_COSSIM_MODE == "forward") and (cossim_tmp[i] == max(cossim_tmp)): + x = x_tmp[i] + break + elif (NOISE_COSSIM_MODE == "reverse") and (cossim_tmp[i] == min(cossim_tmp)): + x = x_tmp[i] + break + elif (NOISE_COSSIM_MODE == "orthogonal") and (abs(cossim_tmp[i]) == min(abs(val) for val in cossim_tmp)): + x = x_tmp[i] + break + elif (NOISE_COSSIM_MODE != "forward") and (NOISE_COSSIM_MODE != "reverse") and (NOISE_COSSIM_MODE != "orthogonal"): + x = x_tmp[0] + break + return x + + + diff --git a/ComfyUI/custom_nodes/RES4LYF/legacy/rk_method.py b/ComfyUI/custom_nodes/RES4LYF/legacy/rk_method.py new file mode 100644 index 0000000000000000000000000000000000000000..b7b616eb55a09a9398198cecac95c246567f6cf0 --- /dev/null +++ b/ComfyUI/custom_nodes/RES4LYF/legacy/rk_method.py @@ -0,0 +1,334 @@ +import torch +import re + +import torch.nn.functional as F +import torchvision.transforms as T + +from .noise_classes import * + +import comfy.model_patcher +import comfy.supported_models + +import itertools + +from .rk_coefficients import * +from .phi_functions import * + + + +class RK_Method: + def __init__(self, model, name="", method="explicit", dynamic_method=False, device='cuda', dtype=torch.float64): + self.model = model + self.model_sampling = model.inner_model.inner_model.model_sampling + self.device = device + self.dtype = dtype + + self.method = method + self.dynamic_method = dynamic_method + + self.stages = 0 + self.name = name + self.ab = None + self.a = None + self.b = None + self.c = None + self.denoised = None + self.uncond = None + + self.rows = 0 + self.cols = 0 + + self.y0 = None + self.y0_inv = None + + self.sigma_min = model.inner_model.inner_model.model_sampling.sigma_min.to(dtype) + self.sigma_max = model.inner_model.inner_model.model_sampling.sigma_max.to(dtype) + + self.noise_sampler = None + + self.h_prev = None + self.h_prev2 = None + self.multistep_stages = 0 + + self.cfg_cw = 1.0 + + + @staticmethod + def is_exponential(rk_type): + #if rk_type.startswith(("res", "dpmpp", "ddim", "irk_exp_diag_2s" )): + if rk_type.startswith(("res", "dpmpp", "ddim", "lawson", "genlawson")): + return True + else: + return False + + @staticmethod + def create(model, rk_type, device='cuda', dtype=torch.float64, name="", method="explicit"): + if RK_Method.is_exponential(rk_type): + return RK_Method_Exponential(model, name, method, device, dtype) + else: + return RK_Method_Linear(model, name, method, device, dtype) + + def __call__(self): + raise NotImplementedError("This method got clownsharked!") + + def model_epsilon(self, x, sigma, **extra_args): + s_in = x.new_ones([x.shape[0]]) + denoised = self.model(x, sigma * s_in, **extra_args) + denoised = self.calc_cfg_channelwise(denoised) + + #return x0 ###################################THIS WORKS ONLY WITH THE MODEL SAMPLING PATCH + eps = (x - denoised) / (sigma * s_in).view(x.shape[0], 1, 1, 1) + return eps, denoised + + def model_denoised(self, x, sigma, **extra_args): + s_in = x.new_ones([x.shape[0]]) + denoised = self.model(x, sigma * s_in, **extra_args) + denoised = self.calc_cfg_channelwise(denoised) + return denoised + + + + def init_noise_sampler(self, x, noise_seed, noise_sampler_type, alpha, k=1., scale=0.1): + seed = torch.initial_seed()+1 if noise_seed == -1 else noise_seed + if noise_sampler_type == "fractal": + self.noise_sampler = NOISE_GENERATOR_CLASSES.get(noise_sampler_type)(x=x, seed=seed, sigma_min=self.sigma_min, sigma_max=self.sigma_max) + self.noise_sampler.alpha = alpha + self.noise_sampler.k = k + self.noise_sampler.scale = scale + else: + self.noise_sampler = NOISE_GENERATOR_CLASSES_SIMPLE.get(noise_sampler_type)(x=x, seed=seed, sigma_min=self.sigma_min, sigma_max=self.sigma_max) + + def add_noise_pre(self, x, sigma_up, sigma, sigma_next, alpha_ratio, s_noise, noise_mode, SDE_NOISE_EXTERNAL=False, sde_noise_t=None): + if isinstance(self.model_sampling, comfy.model_sampling.CONST) == False and noise_mode == "hard": + return self.add_noise(x, sigma_up, sigma, sigma_next, alpha_ratio, s_noise, SDE_NOISE_EXTERNAL, sde_noise_t) + else: + return x + + def add_noise_post(self, x, sigma_up, sigma, sigma_next, alpha_ratio, s_noise, noise_mode, SDE_NOISE_EXTERNAL=False, sde_noise_t=None): + if isinstance(self.model_sampling, comfy.model_sampling.CONST) == True or (isinstance(self.model_sampling, comfy.model_sampling.CONST) == False and noise_mode != "hard"): + return self.add_noise(x, sigma_up, sigma, sigma_next, alpha_ratio, s_noise, SDE_NOISE_EXTERNAL, sde_noise_t) + else: + return x + + def add_noise(self, x, sigma_up, sigma, sigma_next, alpha_ratio, s_noise, SDE_NOISE_EXTERNAL, sde_noise_t): + + if sigma_next > 0.0: + noise = self.noise_sampler(sigma=sigma, sigma_next=sigma_next) + noise = torch.nan_to_num((noise - noise.mean()) / noise.std(), 0.0) + + if SDE_NOISE_EXTERNAL: + noise = (1-s_noise) * noise + s_noise * sde_noise_t + + return alpha_ratio * x + noise * sigma_up * s_noise + + else: + return x + + + def set_coeff(self, rk_type, h, c1=0.0, c2=0.5, c3=1.0, stepcount=0, sigmas=None, sigma=None, sigma_down=None, extra_options=None): + if rk_type == "default": + return + + sigma = sigmas[stepcount] + sigma_next = sigmas[stepcount+1] + + a, b, ci, multistep_stages, FSAL = get_rk_methods(rk_type, h, c1, c2, c3, self.h_prev, self.h_prev2, stepcount, sigmas, sigma, sigma_next, sigma_down, extra_options) + + self.multistep_stages = multistep_stages + + self.a = torch.tensor(a, dtype=h.dtype, device=h.device) + self.a = self.a.view(*self.a.shape, 1, 1, 1, 1, 1) + + + self.b = torch.tensor(b, dtype=h.dtype, device=h.device) + self.b = self.b.view(*self.b.shape, 1, 1, 1, 1, 1) + + self.c = torch.tensor(ci, dtype=h.dtype, device=h.device) + self.rows = self.a.shape[0] + self.cols = self.a.shape[1] + + + def a_k_sum(self, k, row): + if len(k.shape) == 4: + a_coeff = self.a[row].squeeze(-1) + ks = k * a_coeff.sum(dim=0) + elif len(k.shape) == 5: + a_coeff = self.a[row].squeeze(-1) + ks = (k[0:self.cols] * a_coeff).sum(dim=0) + elif len(k.shape) == 6: + a_coeff = self.a[row] + ks = (k[0:self.cols] * a_coeff).sum(dim=0) + else: + raise ValueError(f"Unexpected k shape: {k.shape}") + return ks + + def b_k_sum(self, k, row): + if len(k.shape) == 4: + b_coeff = self.b[row].squeeze(-1) + ks = k * b_coeff.sum(dim=0) + elif len(k.shape) == 5: + b_coeff = self.b[row].squeeze(-1) + ks = (k[0:self.cols] * b_coeff).sum(dim=0) + elif len(k.shape) == 6: + b_coeff = self.b[row] + ks = (k[0:self.cols] * b_coeff).sum(dim=0) + else: + raise ValueError(f"Unexpected k shape: {k.shape}") + return ks + + + def init_cfg_channelwise(self, x, cfg_cw=1.0, **extra_args): + self.uncond = [torch.full_like(x, 0.0)] + self.cfg_cw = cfg_cw + if cfg_cw != 1.0: + def post_cfg_function(args): + self.uncond[0] = args["uncond_denoised"] + return args["denoised"] + model_options = extra_args.get("model_options", {}).copy() + extra_args["model_options"] = comfy.model_patcher.set_model_options_post_cfg_function(model_options, post_cfg_function, disable_cfg1_optimization=True) + return extra_args + + + def calc_cfg_channelwise(self, denoised): + if self.cfg_cw != 1.0: + avg = 0 + for b, c in itertools.product(range(denoised.shape[0]), range(denoised.shape[1])): + avg += torch.norm(denoised[b][c] - self.uncond[0][b][c]) + avg /= denoised.shape[1] + + for b, c in itertools.product(range(denoised.shape[0]), range(denoised.shape[1])): + ratio = torch.nan_to_num(torch.norm(denoised[b][c] - self.uncond[0][b][c]) / avg, 0) + denoised_new = self.uncond[0] + ratio * self.cfg_cw * (denoised - self.uncond[0]) + return denoised_new + else: + return denoised + + + +class RK_Method_Exponential(RK_Method): + def __init__(self, model, name="", method="explicit", device='cuda', dtype=torch.float64): + super().__init__(model, name, method, device, dtype) + self.exponential = True + self.eps_pred = True + + @staticmethod + def alpha_fn(neg_h): + return torch.exp(neg_h) + + @staticmethod + def sigma_fn(t): + return t.neg().exp() + + @staticmethod + def t_fn(sigma): + return sigma.log().neg() + + @staticmethod + def h_fn(sigma_down, sigma): + return -torch.log(sigma_down/sigma) + + def __call__(self, x_0, x, sigma, h, **extra_args): + + denoised = self.model_denoised(x, sigma, **extra_args) + epsilon = denoised - x_0 + + """if self.uncond == None: + self.uncond = [torch.zeros_like(x)] + denoised_u = self.uncond[0].clone() + if torch.all(denoised_u == 0): + epsilon_u = [torch.zeros_like(x_0)] + else: + epsilon_u = denoised_u[0] - x_0""" + if h is not None: + self.h_prev2 = self.h_prev + self.h_prev = h + #print("MODEL SIGMA: ", round(float(sigma),3)) + return epsilon, denoised + + def data_to_vel(self, x, data, sigma): + return data - x + + def get_epsilon(self, x_0, x, y, sigma, sigma_cur, sigma_down=None, unsample_resample_scale=None, extra_options=None): + if sigma_down > sigma: + sigma_cur = self.sigma_max - sigma_cur.clone() + sigma_cur = unsample_resample_scale if unsample_resample_scale is not None else sigma_cur + + if extra_options is not None: + if re.search(r"\bpower_unsample\b", extra_options) or re.search(r"\bpower_resample\b", extra_options): + if sigma_down is None: + return y - x_0 + else: + if sigma_down > sigma: + return (x_0 - y) * sigma_cur + else: + return (y - x_0) * sigma_cur + else: + if sigma_down is None: + return (y - x_0) / sigma_cur + else: + if sigma_down > sigma: + return (x_0 - y) / sigma_cur + else: + return (y - x_0) / sigma_cur + + + +class RK_Method_Linear(RK_Method): + def __init__(self, model, name="", method="explicit", device='cuda', dtype=torch.float64): + super().__init__(model, name, method, device, dtype) + self.expanential = False + self.eps_pred = True + + @staticmethod + def alpha_fn(neg_h): + return torch.ones_like(neg_h) + + @staticmethod + def sigma_fn(t): + return t + + @staticmethod + def t_fn(sigma): + return sigma + + @staticmethod + def h_fn(sigma_down, sigma): + return sigma_down - sigma + + def __call__(self, x_0, x, sigma, h, **extra_args): + #s_in = x.new_ones([x.shape[0]]) + + epsilon, denoised = self.model_epsilon(x, sigma, **extra_args) + + """if self.uncond == None: + self.uncond = [torch.zeros_like(x)] + denoised_u = self.uncond[0].clone() + if torch.all(denoised_u[0] == 0): + epsilon_u = [torch.zeros_like(x_0)] + else: + epsilon_u = (x_0 - denoised_u[0]) / (sigma * s_in).view(x.shape[0], 1, 1, 1)""" + if h is not None: + self.h_prev2 = self.h_prev + self.h_prev = h + #print("MODEL SIGMA: ", round(float(sigma),3)) + + return epsilon, denoised + + def data_to_vel(self, x, data, sigma): + return (data - x) / sigma + + def get_epsilon(self, x_0, x, y, sigma, sigma_cur, sigma_down=None, unsample_resample_scale=None, extra_options=None): + if sigma_down > sigma: + sigma_cur = self.sigma_max - sigma_cur.clone() + sigma_cur = unsample_resample_scale if unsample_resample_scale is not None else sigma_cur + + if sigma_down is None: + return (x - y) / sigma_cur + else: + if sigma_down > sigma: + return (y - x) / sigma_cur + else: + return (x - y) / sigma_cur + + + diff --git a/ComfyUI/custom_nodes/RES4LYF/legacy/rk_sampler.py b/ComfyUI/custom_nodes/RES4LYF/legacy/rk_sampler.py new file mode 100644 index 0000000000000000000000000000000000000000..eb434db771e03634e42138199dea82ae3a7c3552 --- /dev/null +++ b/ComfyUI/custom_nodes/RES4LYF/legacy/rk_sampler.py @@ -0,0 +1,683 @@ +import torch +import torch.nn.functional as F + +from tqdm.auto import trange + + +from .noise_classes import * +from .noise_sigmas_timesteps_scaling import get_res4lyf_step_with_model, get_res4lyf_half_step3 + +from .rk_method import RK_Method +from .rk_guide_func import * + +from .latents import normalize_latent, initialize_or_scale, latent_normalize_channels +from .helper import get_extra_options_kv, extra_options_flag, get_cosine_similarity, is_RF_model +from .sigmas import get_sigmas + +PRINT_DEBUG=False + + +def prepare_sigmas(model, sigmas): + if sigmas[0] == 0.0: #remove padding used to prevent comfy from adding noise to the latent (for unsampling, etc.) + UNSAMPLE = True + sigmas = sigmas[1:-1] + else: + UNSAMPLE = False + + if hasattr(model, "sigmas"): + model.sigmas = sigmas + + return sigmas, UNSAMPLE + + +def prepare_step_to_sigma_zero(rk, irk, rk_type, irk_type, model, x, extra_options, alpha, k, noise_sampler_type, cfg_cw=1.0, **extra_args): + rk_type_final_step = f"ralston_{rk_type[-2:]}" if rk_type[-2:] in {"2s", "3s"} else "ralston_3s" + rk_type_final_step = f"deis_2m" if rk_type[-2:] in {"2m", "3m", "4m"} else rk_type_final_step + rk_type_final_step = f"buehler" if rk_type in {"ddim"} else rk_type_final_step + rk_type_final_step = get_extra_options_kv("rk_type_final_step", rk_type_final_step, extra_options) + rk = RK_Method.create(model, rk_type_final_step, x.device) + rk.init_noise_sampler(x, torch.initial_seed() + 1, noise_sampler_type, alpha=alpha, k=k) + extra_args = rk.init_cfg_channelwise(x, cfg_cw, **extra_args) + + if any(element >= 1 for element in irk.c): + irk_type_final_step = f"gauss-legendre_{rk_type[-2:]}" if rk_type[-2:] in {"2s", "3s", "4s", "5s"} else "gauss-legendre_2s" + irk_type_final_step = f"deis_2m" if rk_type[-2:] in {"2m", "3m", "4m"} else irk_type_final_step + irk_type_final_step = get_extra_options_kv("irk_type_final_step", irk_type_final_step, extra_options) + irk = RK_Method.create(model, irk_type_final_step, x.device) + irk.init_noise_sampler(x, torch.initial_seed() + 100, noise_sampler_type, alpha=alpha, k=k) + extra_args = irk.init_cfg_channelwise(x, cfg_cw, **extra_args) + else: + irk_type_final_step = irk_type + + eta, eta_var = 0, 0 + return rk, irk, rk_type_final_step, irk_type_final_step, eta, eta_var, extra_args + + + +@torch.no_grad() +def sample_rk(model, x, sigmas, extra_args=None, callback=None, disable=None, noise_sampler_type="gaussian", noise_mode="hard", noise_seed=-1, rk_type="res_2m", implicit_sampler_name="explicit_full", + sigma_fn_formula="", t_fn_formula="", + eta=0.0, eta_var=0.0, s_noise=1., d_noise=1., alpha=-1.0, k=1.0, scale=0.1, c1=0.0, c2=0.5, c3=1.0, implicit_steps=0, reverse_weight=0.0, + latent_guide=None, latent_guide_inv=None, latent_guide_weight=0.0, latent_guide_weight_inv=0.0, latent_guide_weights=None, latent_guide_weights_inv=None, guide_mode="", + GARBAGE_COLLECT=False, mask=None, mask_inv=None, LGW_MASK_RESCALE_MIN=True, sigmas_override=None, unsample_resample_scales=None,regional_conditioning_weights=None, sde_noise=[], + extra_options="", + etas=None, s_noises=None, momentums=None, guides=None, cfgpp=0.0, cfg_cw = 1.0,regional_conditioning_floors=None, frame_weights_grp=None, eta_substep=0.0, noise_mode_sde_substep="hard", guide_cossim_cutoff_=1.0, guide_bkg_cossim_cutoff_=1.0, + ): + extra_args = {} if extra_args is None else extra_args + + noise_cossim_iterations = int(get_extra_options_kv("noise_cossim_iterations", "1", extra_options)) + noise_substep_cossim_iterations = int(get_extra_options_kv("noise_substep_cossim_iterations", "1", extra_options)) + NOISE_COSSIM_MODE = get_extra_options_kv("noise_cossim_mode", "orthogonal", extra_options) + NOISE_COSSIM_SOURCE = get_extra_options_kv("noise_cossim_source", "x_eps_data_xinit_orthogonal", extra_options) + NOISE_SUBSTEP_COSSIM_MODE = get_extra_options_kv("noise_substep_cossim_mode", "orthogonal", extra_options) + NOISE_SUBSTEP_COSSIM_SOURCE = get_extra_options_kv("noise_substep_cossim_source", "x_eps_data_xinit_orthogonal", extra_options) + SUBSTEP_SKIP_LAST = get_extra_options_kv("substep_skip_last", "false", extra_options) == "true" + noise_cossim_tile_size = int(get_extra_options_kv("noise_cossim_tile", "2", extra_options)) + noise_substep_cossim_tile_size = int(get_extra_options_kv("noise_substep_cossim_tile", "2", extra_options)) + + substep_eta = float(get_extra_options_kv("substep_eta", str(eta_substep), extra_options)) + substep_noise_scaling = float(get_extra_options_kv("substep_noise_scaling", "0.0", extra_options)) + substep_noise_mode = get_extra_options_kv("substep_noise_mode", noise_mode_sde_substep, extra_options) + + substep_eta_start_step = int(get_extra_options_kv("substep_noise_start_step", "-1", extra_options)) + substep_eta_final_step = int(get_extra_options_kv("substep_noise_final_step", "-1", extra_options)) + + noise_substep_cossim_max_iter = int(get_extra_options_kv("noise_substep_cossim_max_iter", "5", extra_options)) + noise_cossim_max_iter = int(get_extra_options_kv("noise_cossim_max_iter", "5", extra_options)) + noise_substep_cossim_max_score = float(get_extra_options_kv("noise_substep_cossim_max_score", "1e-7", extra_options)) + noise_cossim_max_score = float(get_extra_options_kv("noise_cossim_max_score", "1e-7", extra_options)) + + c1 = c1_ = float(get_extra_options_kv("c1", str(c1), extra_options)) + c2 = c2_ = float(get_extra_options_kv("c2", str(c2), extra_options)) + c3 = c3_ = float(get_extra_options_kv("c3", str(c3), extra_options)) + + guide_skip_steps = int(get_extra_options_kv("guide_skip_steps", 0, extra_options)) + + cfg_cw = float(get_extra_options_kv("cfg_cw", str(cfg_cw), extra_options)) + + MODEL_SAMPLING = model.inner_model.inner_model.model_sampling + + s_in, s_one = x.new_ones([x.shape[0]]), x.new_ones([1]) + default_dtype = getattr(torch, get_extra_options_kv("default_dtype", "float64", extra_options), torch.float64) + max_steps=10000 + + + + if sigmas_override is not None: + sigmas = sigmas_override.clone() + sigmas = sigmas.clone() * d_noise + sigmas, UNSAMPLE = prepare_sigmas(model, sigmas) + + SDE_NOISE_EXTERNAL = False + if sde_noise is not None: + if len(sde_noise) > 0 and sigmas[1] > sigmas[2]: + SDE_NOISE_EXTERNAL = True + sigma_up_total = torch.zeros_like(sigmas[0]) + for i in range(len(sde_noise)-1): + sigma_up_total += sigmas[i+1] + eta = eta / sigma_up_total + + irk_type = implicit_sampler_name + if implicit_sampler_name in ("explicit_full", "explicit_diagonal", "none"): + irk_type = rk_type + + rk_type = "buehler" if implicit_steps > 0 and implicit_sampler_name == "explicit_full" else rk_type + rk_type = get_extra_options_kv("rk_type", rk_type, extra_options) + print("rk_type: ", rk_type) + + rk = RK_Method.create(model, rk_type, x.device) + irk = RK_Method.create(model, irk_type, x.device) + + extra_args = irk.init_cfg_channelwise(x, cfg_cw, **extra_args) + extra_args = rk.init_cfg_channelwise(x, cfg_cw, **extra_args) + + rk. init_noise_sampler(x, noise_seed, noise_sampler_type, alpha=alpha, k=k) + irk.init_noise_sampler(x, noise_seed+100, noise_sampler_type, alpha=alpha, k=k) + + + + frame_weights, frame_weights_inv = None, None + if frame_weights_grp is not None and frame_weights_grp[0] is not None: + frame_weights = initialize_or_scale(frame_weights_grp[0], 1.0, max_steps).to(default_dtype) + frame_weights = F.pad(frame_weights, (0, max_steps), value=0.0) + if frame_weights_grp is not None and frame_weights_grp[1] is not None: + frame_weights_inv = initialize_or_scale(frame_weights_grp[1], 1.0, max_steps).to(default_dtype) + frame_weights_inv = F.pad(frame_weights_inv, (0, max_steps), value=0.0) + frame_weights_grp = (frame_weights, frame_weights_inv) + + LG = LatentGuide(guides, x, model, sigmas, UNSAMPLE, LGW_MASK_RESCALE_MIN, extra_options) + x = LG.init_guides(x, rk.noise_sampler) + + y0, y0_inv = LG.y0, LG.y0_inv + lgw, lgw_inv = LG.lgw, LG.lgw_inv + guide_mode = LG.guide_mode + + + + denoised, denoised_prev, eps, eps_prev = [torch.zeros_like(x) for _ in range(4)] + prev_noises = [] + x_init = x.clone() + + + + for step in trange(len(sigmas)-1, disable=disable): + + sigma, sigma_next = sigmas[step], sigmas[step+1] + unsample_resample_scale = float(unsample_resample_scales[step]) if unsample_resample_scales is not None else None + if regional_conditioning_weights is not None: + extra_args['model_options']['transformer_options']['regional_conditioning_weight'] = regional_conditioning_weights[step] + extra_args['model_options']['transformer_options']['regional_conditioning_floor'] = regional_conditioning_floors [step] + else: + extra_args['model_options']['transformer_options']['regional_conditioning_weight'] = 0.0 + extra_args['model_options']['transformer_options']['regional_conditioning_floor'] = 0.0 + + eta = eta_var = etas[step] if etas is not None else eta + s_noise = s_noises[step] if s_noises is not None else s_noise + + + if sigma_next == 0: + rk, irk, rk_type, irk_type, eta, eta_var, extra_args = prepare_step_to_sigma_zero(rk, irk, rk_type, irk_type, model, x, extra_options, alpha, k, noise_sampler_type, cfg_cw=cfg_cw, **extra_args) + + sigma_up, sigma, sigma_down, alpha_ratio = get_res4lyf_step_with_model(model, sigma, sigma_next, eta, noise_mode) + h = rk.h_fn(sigma_down, sigma) + h_irk = irk.h_fn(sigma_down, sigma) + + c2, c3 = get_res4lyf_half_step3(sigma, sigma_down, c2_, c3_, t_fn=rk.t_fn, sigma_fn=rk.sigma_fn, t_fn_formula=t_fn_formula, sigma_fn_formula=sigma_fn_formula) + + rk. set_coeff(rk_type, h, c1, c2, c3, step, sigmas, sigma, sigma_down, extra_options) + irk.set_coeff(irk_type, h_irk, c1, c2, c3, step, sigmas, sigma, sigma_down, extra_options) + + s_ = [( rk.sigma_fn( rk.t_fn(sigma) + h*c_)) * s_one for c_ in rk.c] + s_irk_rk = [( rk.sigma_fn( rk.t_fn(sigma) + h*c_)) * s_one for c_ in irk.c] + s_irk = [( irk.sigma_fn(irk.t_fn(sigma) + h_irk*c_)) * s_one for c_ in irk.c] + + if step == 0 or step == guide_skip_steps: + x_, data_, data_u, eps_ = (torch.zeros(max(rk.rows, irk.rows) + 2, *x.shape, dtype=x.dtype, device=x.device) for step in range(4)) + + + sde_noise_t = None + if SDE_NOISE_EXTERNAL: + if step >= len(sde_noise): + SDE_NOISE_EXTERNAL=False + else: + sde_noise_t = sde_noise[step] + + + x_prenoise = x.clone() + x_[0] = x + if sigma_up > 0: + x_[0] = rk.add_noise_pre(x, sigma_up, sigma, sigma_next, alpha_ratio, s_noise, noise_mode, SDE_NOISE_EXTERNAL, sde_noise_t) #y0, lgw, sigma_down are currently unused + + x_0 = x_[0].clone() + + for ms in range(rk.multistep_stages): + if RK_Method.is_exponential(rk_type): + eps_ [rk.multistep_stages - ms] = -(x_0 - data_ [rk.multistep_stages - ms]) + else: + eps_ [rk.multistep_stages - ms] = (x_0 - data_ [rk.multistep_stages - ms]) / sigma + + if implicit_steps == 0 or implicit_sampler_name == "explicit_diagonal": + for row in range(rk.rows - rk.multistep_stages): + for exim_iter in range(implicit_steps+1): + sub_sigma_up, sub_sigma, sub_sigma_next, sub_sigma_down, sub_alpha_ratio = 0, s_[row], s_[row+1], s_[row+1], 1 + + if (substep_eta_final_step < 0 and step == len(sigmas)-1+substep_eta_final_step) or (substep_eta_final_step > 0 and step > substep_eta_final_step): + sub_sigma_up, sub_sigma, sub_sigma_down, sub_alpha_ratio = 0, s_[row], s_[row+1], 1 + + edsef=1 + if extra_options_flag("explicit_diagonal_eta_substep_factors", extra_options): + #value_str = get_extra_options_list("explicit_diagonal_eta_substep_factors", "", extra_options) + #float_list = [float(item.strip()) for item in value_str.split(',') if item.strip()] + float_list = get_extra_options_list("explicit_diagonal_eta_substep_factors", "", extra_options, ret_type=float) + edsef = float_list[exim_iter] + nsef = 1 + if extra_options_flag("noise_eta_substep_factors", extra_options): + #value_str = get_extra_options_list("noise_eta_substep_factors", "", extra_options) + #nsef_list = [float(item.strip()) for item in value_str.split(',') if item.strip()] + nsef_list = get_extra_options_list("noise_eta_substep_factors", "", extra_options, ret_type=float) + nsef = nsef_list[row] + if exim_iter > 0 and rk_type.endswith("m") and step >= int(rk_type[-2]): + sub_sigma_up, sub_sigma, sub_sigma_down, sub_alpha_ratio = get_res4lyf_step_with_model(model, sigma, sigma_next, substep_eta*edsef*nsef, substep_noise_mode) + sub_sigma_next = sigma_next + if (row > 0 and not extra_options_flag("disable_rough_noise", extra_options)): # and s_[row-1] >= s_[row]: + sub_sigma_up, sub_sigma, sub_sigma_down, sub_alpha_ratio = get_res4lyf_step_with_model(model, s_[row-1], s_[row], substep_eta*edsef*nsef, substep_noise_mode) + sub_sigma_next = s_[row] + + if row > 0 and substep_eta*edsef*nsef > 0 and row < rk.rows and ((SUBSTEP_SKIP_LAST == False) or (row < rk.rows - rk.multistep_stages - 1)) and (sub_sigma_down > 0) and sigma_next > 0: + substep_noise_scaling_ratio = s_[row+1]/sub_sigma_down + eps_[row-1] *= 1 + substep_noise_scaling*(substep_noise_scaling_ratio-1) + + h_new = h.clone() + if (rk_type.endswith("m") and step >= int(rk_type[-2]) and sub_sigma_up > 0) or (row > 0 and sub_sigma_up > 0): + if extra_options_flag("substep_eta_c_row_plus_one", extra_options): + h_new = (rk.h_fn(sub_sigma_down, sigma) / rk.c[row+1])[0] + else: + if exim_iter > 0 and rk_type.endswith("m") and step >= int(rk_type[-2]): + c_val = -rk.h_prev/h + h_new = (rk.h_fn(sub_sigma_down, sigma)) / c_val + else: + h_new = (rk.h_fn(sub_sigma_down, sigma) / rk.c[row])[0] #used to be rk.c[row+1] + + s_new_ = [( rk.sigma_fn( rk.t_fn(sigma) + h_new*c_)) * s_one for c_ in rk.c] + """print("step, row: ", step, row) + print("h, h_new: ", h.item(), h_new.item()) + print("s_: ", s_) + print("s_new_: ", s_new_) + print("sub_sigma_up, sub_sigma, sub_sigma_next, sub_sigma_down, sub_alpha_ratio: ", sub_sigma_up.item(), sub_sigma.item(), sub_sigma_next.item(), sub_sigma_down.item(), sub_alpha_ratio.item())""" + # UPDATE + #print("UPDATE: step,row,h_new: ", step, row, h_new.item()) + x_[row+1] = x_0 + h_new * rk.a_k_sum(eps_, row) + if row > 0: + if PRINT_DEBUG: + print("A: step,row,h,h_new: \n", step, row, round(float(h.item()),3), round(float(h_new.item()),3)) + + #print("step, row, exim_iter: ", step, row, exim_iter) + + + # NOISE ADD + if is_RF_model(model) == True or (is_RF_model(model) == False and noise_mode != "hard"): + if (exim_iter < implicit_steps and sub_sigma_up > 0) or ((row > 0) and (sub_sigma_up > 0) and ((SUBSTEP_SKIP_LAST == False) or (row < rk.rows - rk.multistep_stages - 1))): + if PRINT_DEBUG: + print("A: sub_sigma_up, sub_sigma, sub_sigma_next, sub_sigma_down, sub_alpha_ratio: \n", round(float(sub_sigma_up),3), round(float(sub_sigma),3), round(float(sub_sigma_next),3), round(float(sub_sigma_down),3), round(float(sub_alpha_ratio),3)) + + data_tmp = denoised_prev if data_[row-1].sum() == 0 else data_[row-1] + eps_tmp = eps_prev if eps_[row-1].sum() == 0 else eps_ [row-1] + Osde = NoiseStepHandlerOSDE(x_[row+1], eps_tmp, data_tmp, x_init, y0, y0_inv) + if Osde.check_cossim_source(NOISE_SUBSTEP_COSSIM_SOURCE): + noise = rk.noise_sampler(sigma=sub_sigma, sigma_next=sub_sigma_next) + noise_osde = Osde.get_ortho_noise(noise, prev_noises, max_iter=noise_substep_cossim_max_iter, max_score=noise_substep_cossim_max_score, NOISE_COSSIM_SOURCE=NOISE_SUBSTEP_COSSIM_SOURCE) + x_[row+1] = sub_alpha_ratio * x_[row+1] + sub_sigma_up * noise_osde * s_noise + elif extra_options_flag("noise_substep_cossim", extra_options): + x_[row+1] = handle_tiled_etc_noise_steps(x_0, x_[row+1], x_prenoise, x_init, eps_tmp, data_tmp, y0, y0_inv, row, rk_type, rk, sub_sigma_up, s_[row-1], s_[row], sub_alpha_ratio, s_noise, substep_noise_mode, SDE_NOISE_EXTERNAL, sde_noise_t, + NOISE_SUBSTEP_COSSIM_SOURCE, NOISE_SUBSTEP_COSSIM_MODE, noise_substep_cossim_tile_size, noise_substep_cossim_iterations, extra_options) + else: + x_[row+1] = rk.add_noise_post(x_[row+1], sub_sigma_up, sub_sigma, sub_sigma_next, sub_alpha_ratio, s_noise, substep_noise_mode, SDE_NOISE_EXTERNAL, sde_noise_t) + + + # MODEL CALL + if step < guide_skip_steps: + eps_row, eps_row_inv = get_guide_epsilon_substep(x_0, x_, y0, y0_inv, s_, row, rk_type) + eps_[row] = LG.mask * eps_row + (1-LG.mask) * eps_row_inv + else: + if implicit_steps == 0 or row > 0 or (row == 0 and not extra_options_flag("explicit_diagonal_implicit_predictor", extra_options)): + eps_[row], data_[row] = rk(x_0, x_[row+1], s_[row], h, **extra_args) + #print("exim: ", step, row, exim_iter) + else: + if extra_options_flag("explicit_diagonal_implicit_predictor_disable_noise", extra_options): + sub_sigma_up, sub_sigma_down, sub_alpha_ratio = sub_sigma_up*0, sub_sigma_next, sub_alpha_ratio/sub_alpha_ratio + eps_[row], data_[row] = rk(x_0, x_[row+1], s_[row], h, **extra_args) + eps_, x_ = LG.process_guides_substep(x_0, x_, eps_, data_, row, step, sigma, sigma_next, sigma_down, s_, unsample_resample_scale, rk, rk_type, extra_options, frame_weights_grp) + h_mini = rk.h_fn(sub_sigma_down, sub_sigma) + x_[row+1] = x_0 + h_mini * eps_[row] + + Osde = NoiseStepHandlerOSDE(x_[row+1], eps_[row], data_[row], x_init, y0, y0_inv) + if Osde.check_cossim_source(NOISE_SUBSTEP_COSSIM_SOURCE): + noise = rk.noise_sampler(sigma=sub_sigma, sigma_next=sub_sigma_next) + noise_osde = Osde.get_ortho_noise(noise, prev_noises, max_iter=noise_substep_cossim_max_iter, max_score=noise_substep_cossim_max_score, NOISE_COSSIM_SOURCE=NOISE_SUBSTEP_COSSIM_SOURCE) + x_[row+1] = sub_alpha_ratio * x_[row+1] + sub_sigma_up * noise_osde * s_noise + else: + x_[row+1] = rk.add_noise_post(x_[row+1], sub_sigma_up, sub_sigma, sub_sigma_next, sub_alpha_ratio, s_noise, substep_noise_mode, SDE_NOISE_EXTERNAL, sde_noise_t) + + for inner_exim_iter in range(implicit_steps): # implicit buehler update to find Yn+1 + #print("inner_exim: ", step, row, inner_exim_iter) + eps_[row], data_[row] = rk(x_0, x_[row+1], s_[row+1], h, **extra_args) + eps_, x_ = LG.process_guides_substep(x_0, x_, eps_, data_, row, step, sigma, sigma_next, sigma_down, s_, unsample_resample_scale, rk, rk_type, extra_options, frame_weights_grp) + x_[row+1] = x_0 + h_mini * eps_[row] + + Osde = NoiseStepHandlerOSDE(x_[row+1], eps_[row], data_[row], x_init, y0, y0_inv) + if Osde.check_cossim_source(NOISE_SUBSTEP_COSSIM_SOURCE): + noise = rk.noise_sampler(sigma=sub_sigma, sigma_next=sub_sigma_next) + noise_osde = Osde.get_ortho_noise(noise, prev_noises, max_iter=noise_substep_cossim_max_iter, max_score=noise_substep_cossim_max_score, NOISE_COSSIM_SOURCE=NOISE_SUBSTEP_COSSIM_SOURCE) + x_[row+1] = sub_alpha_ratio * x_[row+1] + sub_sigma_up * noise_osde * s_noise + else: + x_[row+1] = rk.add_noise_post(x_[row+1], sub_sigma_up, sub_sigma, sub_sigma_next, sub_alpha_ratio, s_noise, substep_noise_mode, SDE_NOISE_EXTERNAL, sde_noise_t) + + + + if extra_options_flag("rk_linear_straight", extra_options): + eps_[row] = (x_0 - data_[row]) / sigma + if sub_sigma_up > 0 and not RK_Method.is_exponential(rk_type): + eps_[row] = (x_0 - data_[row]) / sigma + + + # GUIDES + eps_row_tmp, x_row_tmp = eps_[row].clone(), x_[row+1].clone() + eps_, x_ = LG.process_guides_substep(x_0, x_, eps_, data_, row, step, sigma, sigma_next, sigma_down, s_, unsample_resample_scale, rk, rk_type, extra_options, frame_weights_grp) + + if extra_options_flag("explicit_diagonal_eps_proj_factors", extra_options): + #value_str = get_extra_options_list("explicit_diagonal_eps_proj_factors", "", extra_options) + #float_list = [float(item.strip()) for item in value_str.split(',') if item.strip()] + value_str = get_extra_options_list("explicit_diagonal_eps_proj_factors", "", extra_options, ret_type=float) + eps_[row] = (float_list[exim_iter]) * eps_[row] + (1-float_list[exim_iter]) * eps_row_tmp + x_[row+1] = (float_list[exim_iter]) * x_[row+1] + (1-float_list[exim_iter]) * x_row_tmp + + if row > 0 and exim_iter <= implicit_steps and implicit_steps > 0: + eps_[row-1] = eps_[row] + + if implicit_steps > 0 and row == 0: + break + + if PRINT_DEBUG: + print("B: step,h,h_new: \n", step, round(float(h.item()),3), round(float(h_new.item()),3)) + print("B: sub_sigma_up, sub_sigma, sub_sigma_next, sub_sigma_down, sub_alpha_ratio: \n", round(float(sub_sigma_up),3), round(float(sub_sigma),3), round(float(sub_sigma_next),3), round(float(sub_sigma_down),3), round(float(sub_alpha_ratio),3)) + + x = x_0 + h * rk.b_k_sum(eps_, 0) + + denoised = x_0 + ((sigma / (sigma - sigma_down)) * h) * rk.b_k_sum(eps_, 0) + eps = x - denoised + x = LG.process_guides_poststep(x, denoised, eps, step, extra_options) + + + + + # DIAGONALLY IMPLICIT + elif implicit_sampler_name=="explicit_diagonal_alt" or any(irk_type.startswith(prefix) for prefix in {"crouzeix", "irk_exp_diag", "pareschi_russo", "kraaijevanger_spijker", "qin_zhang",}): + s_irk = [torch.full_like(s_irk[0], sigma.item())] + s_irk + + for row in range(irk.rows - irk.multistep_stages): + + sub_sigma_up, sub_sigma, sub_sigma_next, sub_sigma_down, sub_alpha_ratio = 0.0, s_irk[row], s_irk[row+1], s_irk[row+1], 1.0 + if irk.c[row] > 0: + sub_sigma_up, sub_sigma, sub_sigma_down, sub_alpha_ratio = get_res4lyf_step_with_model(model, s_irk[row], s_irk[row+1], substep_eta, substep_noise_mode) + + if not extra_options_flag("diagonal_implicit_skip_initial", extra_options): + # MODEL CALL + eps_[row], data_[row] = irk(x_0, x_[row], s_irk[row], h_irk, **extra_args) + + # GUIDES + eps_, x_ = LG.process_guides_substep(x_0, x_, eps_, data_, row, step, sigma, sigma_next, sigma_down, s_irk, unsample_resample_scale, irk, irk_type, extra_options, frame_weights_grp) + + for diag_iter in range(implicit_steps): + h_new_irk = h.clone() + if irk.c[row] > 0: + h_new_irk = (irk.h_fn(sub_sigma_down, sigma) / irk.c[row])[0] + + # UPDATE + x_[row+1] = x_0 + h_new_irk * irk.a_k_sum(eps_, row) + + # NOISE ADD + if is_RF_model(model) == True or (is_RF_model(model) == False and noise_mode != "hard"): + if (row > 0) and (sub_sigma_up > 0) and ((SUBSTEP_SKIP_LAST == False) or (row < irk.rows - irk.multistep_stages - 1)): + data_tmp = denoised_prev if data_[row-1].sum() == 0 else data_[row-1] + eps_tmp = eps_prev if eps_[row-1].sum() == 0 else eps_ [row-1] + Osde = NoiseStepHandlerOSDE(x_[row+1], eps_tmp, data_tmp, x_init, y0, y0_inv) + if Osde.check_cossim_source(NOISE_SUBSTEP_COSSIM_SOURCE): + noise = irk.noise_sampler(sigma=sub_sigma, sigma_next=sub_sigma_next) + noise_osde = Osde.get_ortho_noise(noise, prev_noises, max_iter=noise_substep_cossim_max_iter, max_score=noise_substep_cossim_max_score, NOISE_COSSIM_SOURCE=NOISE_SUBSTEP_COSSIM_SOURCE) + x_[row+1] = sub_alpha_ratio * x_[row+1] + sub_sigma_up * noise_osde * s_noise + elif extra_options_flag("noise_substep_cossim", extra_options): + x_[row+1] = handle_tiled_etc_noise_steps(x_0, x_[row+1], x_prenoise, x_init, eps_tmp, data_tmp, y0, y0_inv, row, + irk_type, irk, sub_sigma_up, s_irk[row-1], s_irk[row], sub_alpha_ratio, s_noise, substep_noise_mode, SDE_NOISE_EXTERNAL, sde_noise_t, + NOISE_SUBSTEP_COSSIM_SOURCE, NOISE_SUBSTEP_COSSIM_MODE, noise_substep_cossim_tile_size, noise_substep_cossim_iterations, + extra_options) + else: + x_[row+1] = irk.add_noise_post(x_[row+1], sub_sigma_up, sub_sigma, sub_sigma_next, sub_alpha_ratio, s_noise, substep_noise_mode, SDE_NOISE_EXTERNAL, sde_noise_t) + + # MODEL CALL + eps_[row], data_[row] = irk(x_0, x_[row+1], s_irk[row+1], h_irk, **extra_args) + if sub_sigma_up > 0 and not RK_Method.is_exponential(irk_type): + eps_[row] = (x_0 - data_[row]) / sigma + + # GUIDES + eps_, x_ = LG.process_guides_substep(x_0, x_, eps_, data_, row, step, sigma, sigma_next, sigma_down, s_irk, unsample_resample_scale, irk, irk_type, extra_options, frame_weights_grp) + + + x = x_0 + h_irk * irk.b_k_sum(eps_, 0) + + denoised = x_0 + (sigma / (sigma - sigma_down)) * h_irk * irk.b_k_sum(eps_, 0) + eps = x - denoised + x = LG.process_guides_poststep(x, denoised, eps, step, extra_options) + + + # FULLY IMPLICIT + else: + s2 = s_irk_rk[:] + s2.append(sigma.unsqueeze(dim=0)) + s_all = torch.sort(torch.stack(s2, dim=0).squeeze(dim=1).unique(), descending=True)[0] + sigmas_and = torch.cat( (sigmas[0:step], s_all), dim=0) + + data_[0].zero_() + eps_ [0].zero_() + eps_list = [] + + if extra_options_flag("fast_implicit_guess", extra_options): + if denoised.sum() == 0: + if extra_options_flag("fast_implicit_guess_use_guide", extra_options): + data_s = y0 + eps_s = x_0 - data_s + else: + eps_s, data_s = rk(x_0, x_0, sigma, h, **extra_args) + else: + eps_s, data_s = eps, denoised + for i in range(len(s_all)-1): + eps_list.append(eps_s * s_all[i]/sigma) + if torch.allclose(s_all[-1], sigma_down, atol=1e-8): + eps_list.append(eps_s * sigma_down/sigma) + else: + # EXPLICIT GUESS + x_mid = x + for i in range(len(s_all)-1): + x_mid, eps_, data_ = get_explicit_rk_step(rk, rk_type, x_mid, LG, step, s_all[i], s_all[i+1], eta, eta_var, s_noise, noise_mode, c2, c3, step+i, sigmas_and, x_, eps_, data_, unsample_resample_scale, extra_options, frame_weights_grp, + x_init, x_prenoise, NOISE_COSSIM_SOURCE, NOISE_COSSIM_MODE, noise_cossim_max_iter, noise_cossim_max_score, noise_cossim_tile_size, noise_cossim_iterations,SDE_NOISE_EXTERNAL,sde_noise_t,MODEL_SAMPLING, + **extra_args) + + eps_list.append(eps_[0]) + data_[0].zero_() + eps_ [0].zero_() + + if torch.allclose(s_all[-1], sigma_down, atol=1e-8): + eps_down, data_down = rk(x_0, x_mid, sigma_down, h, **extra_args) #should h_irk = h? going to change it for now. + eps_list.append(eps_down) + + s_all = [s for s in s_all if s in s_irk_rk] + + eps_list = [eps_list[s_all.index(s)].clone() for s in s_irk_rk] + eps2_ = torch.stack(eps_list, dim=0) + + # FULLY IMPLICIT LOOP + for implicit_iter in range(implicit_steps): + for row in range(irk.rows): + x_[row+1] = x_0 + h_irk * irk.a_k_sum(eps2_, row) + eps2_[row], data_[row] = irk(x_0, x_[row+1], s_irk[row], h_irk, **extra_args) + if not extra_options_flag("implicit_loop_skip_guide", extra_options): + eps2_, x_ = LG.process_guides_substep(x_0, x_, eps2_, data_, row, step, sigma, sigma_next, sigma_down, s_irk, unsample_resample_scale, irk, irk_type, extra_options, frame_weights_grp) + x = x_0 + h_irk * irk.b_k_sum(eps2_, 0) + denoised = x_0 + (sigma / (sigma - sigma_down)) * h_irk * irk.b_k_sum(eps2_, 0) + eps = x - denoised + x = LG.process_guides_poststep(x, denoised, eps, step, extra_options) + + + preview_callback(x, eps, denoised, x_, eps_, data_, step, sigma, sigma_next, callback, extra_options) + + sde_noise_t = None + if SDE_NOISE_EXTERNAL: + if step >= len(sde_noise): + SDE_NOISE_EXTERNAL=False + else: + sde_noise_t = sde_noise[step] + + if is_RF_model(model) == True or (is_RF_model(model) == False and noise_mode != "hard"): + if sigma_up > 0: + #print("NOISE_FULL: sigma_up, sigma, sigma_next, sigma_down, alpha_ratio: ", sigma_up.item(), sigma.item(), sigma_next.item(), sigma_down.item(), alpha_ratio.item()) + if implicit_steps==0: + rk_or_irk = rk + rk_or_irk_type = rk_type + else: + rk_or_irk = irk + rk_or_irk_type = irk_type + Osde = NoiseStepHandlerOSDE(x, eps, denoised, x_init, y0, y0_inv) + if Osde.check_cossim_source(NOISE_COSSIM_SOURCE): + noise = rk_or_irk.noise_sampler(sigma=sigma, sigma_next=sigma_next) + noise_osde = Osde.get_ortho_noise(noise, prev_noises, max_iter=noise_cossim_max_iter, max_score=noise_cossim_max_score, NOISE_COSSIM_SOURCE=NOISE_COSSIM_SOURCE) + x = alpha_ratio * x + sigma_up * noise_osde * s_noise + elif extra_options_flag("noise_cossim", extra_options): + x = handle_tiled_etc_noise_steps(x_0, x, x_prenoise, x_init, eps, denoised, y0, y0_inv, step, + rk_or_irk_type, rk_or_irk, sigma_up, sigma, sigma_next, alpha_ratio, s_noise, noise_mode, SDE_NOISE_EXTERNAL, sde_noise_t, + NOISE_COSSIM_SOURCE, NOISE_COSSIM_MODE, noise_cossim_tile_size, noise_cossim_iterations, + extra_options) + else: + x = rk_or_irk.add_noise_post(x, sigma_up, sigma, sigma_next, alpha_ratio, s_noise, noise_mode, SDE_NOISE_EXTERNAL, sde_noise_t) + + if PRINT_DEBUG: + print("Data vs. y0 cossim score: ", get_cosine_similarity(data_[0], y0).item()) + + for ms in range(rk.multistep_stages): + if RK_Method.is_exponential(rk_type): + eps_[rk.multistep_stages - ms] = data_[rk.multistep_stages - ms - 1] - x + else: + eps_[rk.multistep_stages - ms] = (x - data_[rk.multistep_stages - ms - 1]) / sigma + + #eps_ [rk.multistep_stages - ms] = eps_ [rk.multistep_stages - ms - 1] + data_[rk.multistep_stages - ms] = data_[rk.multistep_stages - ms - 1] + + eps_ [0] = torch.zeros_like(eps_ [0]) + data_[0] = torch.zeros_like(data_[0]) + + denoised_prev = denoised + eps_prev = eps + + preview_callback(x, eps, denoised, x_, eps_, data_, step, sigma, sigma_next, callback, extra_options, FINAL_STEP=True) + return x + + + +def get_explicit_rk_step(rk, rk_type, x, LG, step, sigma, sigma_next, eta, eta_var, s_noise, noise_mode, c2, c3, stepcount, sigmas, x_, eps_, data_, unsample_resample_scale, extra_options, frame_weights_grp, + x_init, x_prenoise, NOISE_COSSIM_SOURCE, NOISE_COSSIM_MODE, noise_cossim_max_iter, noise_cossim_max_score, noise_cossim_tile_size, noise_cossim_iterations,SDE_NOISE_EXTERNAL,sde_noise_t,MODEL_SAMPLING, + **extra_args): + + extra_args = {} if extra_args is None else extra_args + s_in = x.new_ones([x.shape[0]]) + + eta = float(get_extra_options_kv("implicit_substep_eta", eta, extra_options)) + + sigma_up, sigma, sigma_down, alpha_ratio = get_res4lyf_step_with_model(rk.model, sigma, sigma_next, eta, noise_mode) + h = rk.h_fn(sigma_down, sigma) + c2, c3 = get_res4lyf_half_step3(sigma, sigma_down, c2, c3, t_fn=rk.t_fn, sigma_fn=rk.sigma_fn) + + rk.set_coeff(rk_type, h, c2=c2, c3=c3, stepcount=stepcount, sigmas=sigmas, sigma_down=sigma_down, extra_options=extra_options) + + s_ = [(sigma + h * c_) * s_in for c_ in rk.c] + x_[0] = rk.add_noise_pre(x, sigma_up, sigma, sigma_next, alpha_ratio, s_noise, noise_mode) + + x_0 = x_[0].clone() + + for ms in range(rk.multistep_stages): + if RK_Method.is_exponential(rk_type): + eps_ [rk.multistep_stages - ms] = data_ [rk.multistep_stages - ms] - x_0 + else: + eps_ [rk.multistep_stages - ms] = (x_0 - data_ [rk.multistep_stages - ms]) / sigma + + for row in range(rk.rows - rk.multistep_stages): + x_[row+1] = x_0 + h * rk.a_k_sum(eps_, row) + eps_[row], data_[row] = rk(x_0, x_[row+1], s_[row], h, **extra_args) + + eps_, x_ = LG.process_guides_substep(x_0, x_, eps_, data_, row, step, sigma, sigma_next, sigma_down, s_, unsample_resample_scale, rk, rk_type, extra_options, frame_weights_grp) + + x = x_0 + h * rk.b_k_sum(eps_, 0) + + denoised = x_0 + (sigma / (sigma - sigma_down)) * h * rk.b_k_sum(eps_, 0) + eps = x - denoised + + y0 = LG.y0 + if LG.y0.shape[0] > 1: + y0 = LG.y0[min(step, LG.y0.shape[0]-1)].unsqueeze(0) + + x = LG.process_guides_poststep(x, denoised, eps, step, extra_options) + + #x = rk.add_noise_post(x, sigma_up, sigma, sigma_next, alpha_ratio, s_noise, noise_mode) + + if is_RF_model(rk.model) == True or (is_RF_model(rk.model) == False and noise_mode != "hard"): + if sigma_up > 0: + Osde = NoiseStepHandlerOSDE(x, eps, denoised, x_init, y0, LG.y0_inv) + if Osde.check_cossim_source(NOISE_COSSIM_SOURCE): + noise = rk.noise_sampler(sigma=sigma, sigma_next=sigma_next) + noise_osde = Osde.get_ortho_noise(noise, [], max_iter=noise_cossim_max_iter, max_score=noise_cossim_max_score, NOISE_COSSIM_SOURCE=NOISE_COSSIM_SOURCE) + x = alpha_ratio * x + sigma_up * noise_osde * s_noise + elif extra_options_flag("noise_cossim", extra_options): + x = handle_tiled_etc_noise_steps(x_0, x, x_prenoise, x_init, eps, denoised, y0, LG.y0_inv, step, + rk_type, rk, sigma_up, sigma, sigma_next, alpha_ratio, s_noise, noise_mode, SDE_NOISE_EXTERNAL, sde_noise_t, + NOISE_COSSIM_SOURCE, NOISE_COSSIM_MODE, noise_cossim_tile_size, noise_cossim_iterations, + extra_options) + else: + x = rk.add_noise_post(x, sigma_up, sigma, sigma_next, alpha_ratio, s_noise, noise_mode, SDE_NOISE_EXTERNAL, sde_noise_t) + + for ms in range(rk.multistep_stages): # NEEDS ADJUSTING? + eps_ [rk.multistep_stages - ms] = eps_ [rk.multistep_stages - ms - 1] + data_[rk.multistep_stages - ms] = data_[rk.multistep_stages - ms - 1] + + return x, eps_, data_ + + + +def preview_callback(x, eps, denoised, x_, eps_, data_, step, sigma, sigma_next, callback, extra_options, FINAL_STEP=False): + + if FINAL_STEP: + denoised_callback = denoised + + elif extra_options_flag("eps_substep_preview", extra_options): + row_callback = int(get_extra_options_kv("eps_substep_preview", "0", extra_options)) + denoised_callback = eps_[row_callback] + + elif extra_options_flag("denoised_substep_preview", extra_options): + row_callback = int(get_extra_options_kv("denoised_substep_preview", "0", extra_options)) + denoised_callback = data_[row_callback] + + elif extra_options_flag("x_substep_preview", extra_options): + row_callback = int(get_extra_options_kv("x_substep_preview", "0", extra_options)) + denoised_callback = x_[row_callback] + + elif extra_options_flag("eps_preview", extra_options): + denoised_callback = eps + + elif extra_options_flag("denoised_preview", extra_options): + denoised_callback = denoised + + elif extra_options_flag("x_preview", extra_options): + denoised_callback = x + + else: + denoised_callback = data_[0] + + callback({'x': x, 'i': step, 'sigma': sigma, 'sigma_next': sigma_next, 'denoised': denoised_callback.to(torch.float32)}) if callback is not None else None + + return + + + + +def sample_res_2m(model, x, sigmas, extra_args=None, callback=None, disable=None): + return sample_rk(model, x, sigmas, extra_args, callback, disable, noise_sampler_type="gaussian", noise_mode="hard", noise_seed=-1, rk_type="res_2m", eta=0.0, ) +def sample_res_2s(model, x, sigmas, extra_args=None, callback=None, disable=None): + return sample_rk(model, x, sigmas, extra_args, callback, disable, noise_sampler_type="gaussian", noise_mode="hard", noise_seed=-1, rk_type="res_2s", eta=0.0, ) +def sample_res_3s(model, x, sigmas, extra_args=None, callback=None, disable=None): + return sample_rk(model, x, sigmas, extra_args, callback, disable, noise_sampler_type="gaussian", noise_mode="hard", noise_seed=-1, rk_type="res_3s", eta=0.0, ) +def sample_res_5s(model, x, sigmas, extra_args=None, callback=None, disable=None): + return sample_rk(model, x, sigmas, extra_args, callback, disable, noise_sampler_type="gaussian", noise_mode="hard", noise_seed=-1, rk_type="res_5s", eta=0.0, ) +def sample_res_6s(model, x, sigmas, extra_args=None, callback=None, disable=None): + return sample_rk(model, x, sigmas, extra_args, callback, disable, noise_sampler_type="gaussian", noise_mode="hard", noise_seed=-1, rk_type="res_6s", eta=0.0, ) + +def sample_res_2m_sde(model, x, sigmas, extra_args=None, callback=None, disable=None): + return sample_rk(model, x, sigmas, extra_args, callback, disable, noise_sampler_type="gaussian", noise_mode="hard", noise_seed=-1, rk_type="res_2m", eta=0.5, eta_substep=0.5, ) +def sample_res_2s_sde(model, x, sigmas, extra_args=None, callback=None, disable=None): + return sample_rk(model, x, sigmas, extra_args, callback, disable, noise_sampler_type="gaussian", noise_mode="hard", noise_seed=-1, rk_type="res_2s", eta=0.5, eta_substep=0.5, ) +def sample_res_3s_sde(model, x, sigmas, extra_args=None, callback=None, disable=None): + return sample_rk(model, x, sigmas, extra_args, callback, disable, noise_sampler_type="gaussian", noise_mode="hard", noise_seed=-1, rk_type="res_3s", eta=0.5, eta_substep=0.5, ) +def sample_res_5s_sde(model, x, sigmas, extra_args=None, callback=None, disable=None): + return sample_rk(model, x, sigmas, extra_args, callback, disable, noise_sampler_type="gaussian", noise_mode="hard", noise_seed=-1, rk_type="res_5s", eta=0.5, eta_substep=0.5, ) +def sample_res_6s_sde(model, x, sigmas, extra_args=None, callback=None, disable=None): + return sample_rk(model, x, sigmas, extra_args, callback, disable, noise_sampler_type="gaussian", noise_mode="hard", noise_seed=-1, rk_type="res_6s", eta=0.5, eta_substep=0.5, ) + +def sample_deis_2m(model, x, sigmas, extra_args=None, callback=None, disable=None): + return sample_rk(model, x, sigmas, extra_args, callback, disable, noise_sampler_type="gaussian", noise_mode="hard", noise_seed=-1, rk_type="deis_2m", eta=0.0, ) +def sample_deis_3m(model, x, sigmas, extra_args=None, callback=None, disable=None): + return sample_rk(model, x, sigmas, extra_args, callback, disable, noise_sampler_type="gaussian", noise_mode="hard", noise_seed=-1, rk_type="deis_3m", eta=0.0, ) +def sample_deis_4m(model, x, sigmas, extra_args=None, callback=None, disable=None): + return sample_rk(model, x, sigmas, extra_args, callback, disable, noise_sampler_type="gaussian", noise_mode="hard", noise_seed=-1, rk_type="deis_4m", eta=0.0, ) + +def sample_deis_2m_sde(model, x, sigmas, extra_args=None, callback=None, disable=None): + return sample_rk(model, x, sigmas, extra_args, callback, disable, noise_sampler_type="gaussian", noise_mode="hard", noise_seed=-1, rk_type="deis_2m", eta=0.5, eta_substep=0.5, ) +def sample_deis_3m_sde(model, x, sigmas, extra_args=None, callback=None, disable=None): + return sample_rk(model, x, sigmas, extra_args, callback, disable, noise_sampler_type="gaussian", noise_mode="hard", noise_seed=-1, rk_type="deis_3m", eta=0.5, eta_substep=0.5, ) +def sample_deis_4m_sde(model, x, sigmas, extra_args=None, callback=None, disable=None): + return sample_rk(model, x, sigmas, extra_args, callback, disable, noise_sampler_type="gaussian", noise_mode="hard", noise_seed=-1, rk_type="deis_4m", eta=0.5, eta_substep=0.5, ) + diff --git a/ComfyUI/custom_nodes/RES4LYF/legacy/samplers.py b/ComfyUI/custom_nodes/RES4LYF/legacy/samplers.py new file mode 100644 index 0000000000000000000000000000000000000000..4655a17be3d93498db6eae73b7438409386e103b --- /dev/null +++ b/ComfyUI/custom_nodes/RES4LYF/legacy/samplers.py @@ -0,0 +1,804 @@ +from .noise_classes import prepare_noise, NOISE_GENERATOR_CLASSES_SIMPLE, NOISE_GENERATOR_NAMES_SIMPLE, NOISE_GENERATOR_NAMES +from .sigmas import get_sigmas + + +from .constants import MAX_STEPS + +import comfy.samplers +import comfy.sample +import comfy.sampler_helpers +import comfy.model_sampling +import comfy.latent_formats +import comfy.sd +import comfy.supported_models + +import latent_preview +import torch +import torch.nn.functional as F + +import math +import copy + +from .helper import get_extra_options_kv, extra_options_flag, get_res4lyf_scheduler_list +from .latents import initialize_or_scale + +from .noise_classes import prepare_noise, NOISE_GENERATOR_CLASSES_SIMPLE, NOISE_GENERATOR_NAMES_SIMPLE, NOISE_GENERATOR_NAMES +from .sigmas import get_sigmas + + +from .rk_sampler import sample_rk +from .rk_coefficients import RK_SAMPLER_NAMES, IRK_SAMPLER_NAMES +from .rk_guide_func import get_orthogonal +from .noise_sigmas_timesteps_scaling import NOISE_MODE_NAMES + + +def move_to_same_device(*tensors): + if not tensors: + return tensors + + device = tensors[0].device + return tuple(tensor.to(device) for tensor in tensors) + + +#SCHEDULER_NAMES = comfy.samplers.SCHEDULER_NAMES + ["beta57"] + + + +class ClownSamplerAdvanced: + @classmethod + def INPUT_TYPES(s): + return {"required": + { + "noise_type_sde": (NOISE_GENERATOR_NAMES_SIMPLE, {"default": "gaussian"}), + "noise_type_sde_substep": (NOISE_GENERATOR_NAMES_SIMPLE, {"default": "gaussian"}), + "noise_mode_sde": (NOISE_MODE_NAMES, {"default": 'hard', "tooltip": "How noise scales with the sigma schedule. Hard is the most aggressive, the others start strong and drop rapidly."}), + "noise_mode_sde_substep": (NOISE_MODE_NAMES, {"default": 'hard', "tooltip": "How noise scales with the sigma schedule. Hard is the most aggressive, the others start strong and drop rapidly."}), + "eta": ("FLOAT", {"default": 0.5, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Calculated noise amount to be added, then removed, after each step."}), + "eta_substep": ("FLOAT", {"default": 0.5, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Calculated noise amount to be added, then removed, after each step."}), + "s_noise": ("FLOAT", {"default": 1.0, "min": -10000, "max": 10000, "step":0.01, "tooltip": "Adds extra SDE noise. Values around 1.03-1.07 can lead to a moderate boost in detail and paint textures."}), + "d_noise": ("FLOAT", {"default": 1.0, "min": -10000, "max": 10000, "step":0.01, "tooltip": "Downscales the sigma schedule. Values around 0.98-0.95 can lead to a large boost in detail and paint textures."}), + "noise_seed_sde": ("INT", {"default": -1, "min": -1, "max": 0xffffffffffffffff}), + "sampler_name": (RK_SAMPLER_NAMES, {"default": "res_2m"}), + "implicit_sampler_name": (IRK_SAMPLER_NAMES, {"default": "explicit_diagonal"}), + "implicit_steps": ("INT", {"default": 0, "min": 0, "max": 10000}), + }, + "optional": + { + "guides": ("GUIDES", ), + "options": ("OPTIONS", ), + "automation": ("AUTOMATION", ), + "extra_options": ("STRING", {"default": "", "multiline": True}), + } + } + + RETURN_TYPES = ("SAMPLER",) + RETURN_NAMES = ("sampler", ) + + FUNCTION = "main" + + CATEGORY = "RES4LYF/legacy/samplers" + DEPRECATED = True + + def main(self, + noise_type_sde="gaussian", noise_type_sde_substep="gaussian", noise_mode_sde="hard", + eta=0.25, eta_var=0.0, d_noise=1.0, s_noise=1.0, alpha_sde=-1.0, k_sde=1.0, cfgpp=0.0, c1=0.0, c2=0.5, c3=1.0, noise_seed_sde=-1, sampler_name="res_2m", implicit_sampler_name="gauss-legendre_2s", + t_fn_formula=None, sigma_fn_formula=None, implicit_steps=0, + latent_guide=None, latent_guide_inv=None, guide_mode="", latent_guide_weights=None, latent_guide_weights_inv=None, latent_guide_mask=None, latent_guide_mask_inv=None, rescale_floor=True, sigmas_override=None, + guides=None, options=None, sde_noise=None,sde_noise_steps=1, + extra_options="", automation=None, etas=None, s_noises=None,unsample_resample_scales=None, regional_conditioning_weights=None,frame_weights_grp=None, eta_substep=0.5, noise_mode_sde_substep="hard", + ): + if implicit_sampler_name == "none": + implicit_steps = 0 + implicit_sampler_name = "gauss-legendre_2s" + + if noise_mode_sde == "none": + eta, eta_var = 0.0, 0.0 + noise_mode_sde = "hard" + + default_dtype = getattr(torch, get_extra_options_kv("default_dtype", "float64", extra_options), torch.float64) + + unsample_resample_scales_override = unsample_resample_scales + + if options is not None: + noise_type_sde = options.get('noise_type_sde', noise_type_sde) + noise_mode_sde = options.get('noise_mode_sde', noise_mode_sde) + eta = options.get('eta', eta) + s_noise = options.get('s_noise', s_noise) + d_noise = options.get('d_noise', d_noise) + alpha_sde = options.get('alpha_sde', alpha_sde) + k_sde = options.get('k_sde', k_sde) + c1 = options.get('c1', c1) + c2 = options.get('c2', c2) + c3 = options.get('c3', c3) + t_fn_formula = options.get('t_fn_formula', t_fn_formula) + sigma_fn_formula = options.get('sigma_fn_formula', sigma_fn_formula) + frame_weights_grp = options.get('frame_weights_grp', frame_weights_grp) + sde_noise = options.get('sde_noise', sde_noise) + sde_noise_steps = options.get('sde_noise_steps', sde_noise_steps) + + #noise_seed_sde = torch.initial_seed()+1 if noise_seed_sde < 0 else noise_seed_sde + + rescale_floor = extra_options_flag("rescale_floor", extra_options) + + if automation is not None: + etas = automation['etas'] if 'etas' in automation else None + s_noises = automation['s_noises'] if 's_noises' in automation else None + unsample_resample_scales = automation['unsample_resample_scales'] if 'unsample_resample_scales' in automation else None + frame_weights_grp = automation['frame_weights_grp'] if 'frame_weights_grp' in automation else None + + etas = initialize_or_scale(etas, eta, MAX_STEPS).to(default_dtype) + etas = F.pad(etas, (0, MAX_STEPS), value=0.0) + s_noises = initialize_or_scale(s_noises, s_noise, MAX_STEPS).to(default_dtype) + s_noises = F.pad(s_noises, (0, MAX_STEPS), value=0.0) + + if sde_noise is None: + sde_noise = [] + else: + sde_noise = copy.deepcopy(sde_noise) + for i in range(len(sde_noise)): + sde_noise[i] = sde_noise[i] + for j in range(sde_noise[i].shape[1]): + sde_noise[i][0][j] = ((sde_noise[i][0][j] - sde_noise[i][0][j].mean()) / sde_noise[i][0][j].std()) + + if unsample_resample_scales_override is not None: + unsample_resample_scales = unsample_resample_scales_override + + sampler = comfy.samplers.ksampler("rk", {"eta": eta, "eta_var": eta_var, "s_noise": s_noise, "d_noise": d_noise, "alpha": alpha_sde, "k": k_sde, "c1": c1, "c2": c2, "c3": c3, "cfgpp": cfgpp, + "noise_sampler_type": noise_type_sde, "noise_mode": noise_mode_sde, "noise_seed": noise_seed_sde, "rk_type": sampler_name, "implicit_sampler_name": implicit_sampler_name, + "t_fn_formula": t_fn_formula, "sigma_fn_formula": sigma_fn_formula, "implicit_steps": implicit_steps, + "latent_guide": latent_guide, "latent_guide_inv": latent_guide_inv, "mask": latent_guide_mask, "mask_inv": latent_guide_mask_inv, + "latent_guide_weights": latent_guide_weights, "latent_guide_weights_inv": latent_guide_weights_inv, "guide_mode": guide_mode, + "LGW_MASK_RESCALE_MIN": rescale_floor, "sigmas_override": sigmas_override, "sde_noise": sde_noise, + "extra_options": extra_options, + "etas": etas, "s_noises": s_noises, "unsample_resample_scales": unsample_resample_scales, "regional_conditioning_weights": regional_conditioning_weights, + "guides": guides, "frame_weights_grp": frame_weights_grp, "eta_substep": eta_substep, "noise_mode_sde_substep": noise_mode_sde_substep, + }) + + return (sampler, ) + + + + +class ClownSampler: + @classmethod + def INPUT_TYPES(s): + return {"required": + { + "noise_type_sde": (NOISE_GENERATOR_NAMES_SIMPLE, {"default": "gaussian"}), + "noise_mode_sde": (NOISE_MODE_NAMES, {"default": 'hard', "tooltip": "How noise scales with the sigma schedule. Hard is the most aggressive, the others start strong and drop rapidly."}), + "eta": ("FLOAT", {"default": 0.5, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Calculated noise amount to be added, then removed, after each step."}), + "s_noise": ("FLOAT", {"default": 1.0, "min": -10000, "max": 10000, "step":0.01}), + "d_noise": ("FLOAT", {"default": 1.0, "min": -10000, "max": 10000, "step":0.01}), + "noise_seed_sde": ("INT", {"default": -1, "min": -1, "max": 0xffffffffffffffff}), + "sampler_name": (RK_SAMPLER_NAMES, {"default": "res_2m"}), + "implicit_sampler_name": (IRK_SAMPLER_NAMES, {"default": "explicit_diagonal"}), + "implicit_steps": ("INT", {"default": 0, "min": 0, "max": 10000}), + }, + "optional": + { + "guides": ("GUIDES", ), + "options": ("OPTIONS", ), + "automation": ("AUTOMATION", ), + "extra_options": ("STRING", {"default": "", "multiline": True}), + } + } + + RETURN_TYPES = ("SAMPLER",) + RETURN_NAMES = ("sampler", ) + + FUNCTION = "main" + + CATEGORY = "RES4LYF/legacy/samplers" + DEPRECATED = True + + def main(self, + noise_type_sde="gaussian", noise_type_sde_substep="gaussian", noise_mode_sde="hard", + eta=0.25, eta_var=0.0, d_noise=1.0, s_noise=1.0, alpha_sde=-1.0, k_sde=1.0, cfgpp=0.0, c1=0.0, c2=0.5, c3=1.0, noise_seed_sde=-1, sampler_name="res_2m", implicit_sampler_name="gauss-legendre_2s", + t_fn_formula=None, sigma_fn_formula=None, implicit_steps=0, + latent_guide=None, latent_guide_inv=None, guide_mode="", latent_guide_weights=None, latent_guide_weights_inv=None, latent_guide_mask=None, latent_guide_mask_inv=None, rescale_floor=True, sigmas_override=None, + guides=None, options=None, sde_noise=None,sde_noise_steps=1, + extra_options="", automation=None, etas=None, s_noises=None,unsample_resample_scales=None, regional_conditioning_weights=None,frame_weights_grp=None,eta_substep=0.0, noise_mode_sde_substep="hard", + ): + + eta_substep = eta + noise_mode_sde_substep = noise_mode_sde + noise_type_sde_substep = noise_type_sde + + sampler = ClownSamplerAdvanced().main( + noise_type_sde=noise_type_sde, noise_type_sde_substep=noise_type_sde_substep, noise_mode_sde=noise_mode_sde, + eta=eta, eta_var=eta_var, d_noise=d_noise, s_noise=s_noise, alpha_sde=alpha_sde, k_sde=k_sde, cfgpp=cfgpp, c1=c1, c2=c2, c3=c3, noise_seed_sde=noise_seed_sde, sampler_name=sampler_name, implicit_sampler_name=implicit_sampler_name, + t_fn_formula=t_fn_formula, sigma_fn_formula=sigma_fn_formula, implicit_steps=implicit_steps, + latent_guide=latent_guide, latent_guide_inv=latent_guide_inv, guide_mode=guide_mode, latent_guide_weights=latent_guide_weights, latent_guide_weights_inv=latent_guide_weights_inv, latent_guide_mask=latent_guide_mask, latent_guide_mask_inv=latent_guide_mask_inv, rescale_floor=rescale_floor, sigmas_override=sigmas_override, + guides=guides, options=options, sde_noise=sde_noise,sde_noise_steps=sde_noise_steps, + extra_options=extra_options, automation=automation, etas=etas, s_noises=s_noises,unsample_resample_scales=unsample_resample_scales, regional_conditioning_weights=regional_conditioning_weights,frame_weights_grp=frame_weights_grp, eta_substep=eta_substep, noise_mode_sde_substep=noise_mode_sde_substep, + ) + + return sampler + + + +def process_sampler_name(selected_value): + processed_name = selected_value.split("/")[-1] + + if selected_value.startswith("fully_implicit") or selected_value.startswith("diag_implicit"): + implicit_sampler_name = processed_name + sampler_name = "buehler" + else: + sampler_name = processed_name + implicit_sampler_name = "use_explicit" + + return sampler_name, implicit_sampler_name + + +def copy_cond(positive): + new_positive = [] + for embedding, cond in positive: + cond_copy = {} + for k, v in cond.items(): + if isinstance(v, torch.Tensor): + cond_copy[k] = v.clone() + else: + cond_copy[k] = v # ensure we're not copying huge shit like controlnets + new_positive.append([embedding.clone(), cond_copy]) + return new_positive + + + +class SharkSamplerAlpha: + @classmethod + def INPUT_TYPES(s): + return {"required": + {"model": ("MODEL",), + "noise_type_init": (NOISE_GENERATOR_NAMES_SIMPLE, {"default": "gaussian"}), + "noise_stdev": ("FLOAT", {"default": 1.0, "min": -10000.0, "max": 10000.0, "step":0.01, "round": False, }), + "noise_seed": ("INT", {"default": 0, "min": -1, "max": 0xffffffffffffffff}), + "sampler_mode": (['standard', 'unsample', 'resample'],), + "scheduler": (get_res4lyf_scheduler_list(), {"default": "beta57"},), + "steps": ("INT", {"default": 30, "min": 1, "max": 10000}), + "denoise": ("FLOAT", {"default": 1.0, "min": -10000, "max": 10000, "step":0.01}), + "denoise_alt": ("FLOAT", {"default": 1.0, "min": -10000, "max": 10000, "step":0.01}), + "cfg": ("FLOAT", {"default": 3.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Negative values use channelwise CFG." }), + }, + "optional": + { + "positive": ("CONDITIONING", ), + "negative": ("CONDITIONING", ), + "sampler": ("SAMPLER", ), + "sigmas": ("SIGMAS", ), + "latent_image": ("LATENT", ), + "options": ("OPTIONS", ), + "extra_options": ("STRING", {"default": "", "multiline": True}), + } + } + + RETURN_TYPES = ("LATENT","LATENT", "LATENT",) + RETURN_NAMES = ("output", "denoised","sde_noise",) + + FUNCTION = "main" + + CATEGORY = "RES4LYF/legacy/samplers" + DEPRECATED = True + + def main(self, model, cfg, scheduler, steps, sampler_mode="standard",denoise=1.0, denoise_alt=1.0, + noise_type_init="gaussian", latent_image=None, + positive=None, negative=None, sampler=None, sigmas=None, latent_noise=None, latent_noise_match=None, + noise_stdev=1.0, noise_mean=0.0, noise_normalize=True, + d_noise=1.0, alpha_init=-1.0, k_init=1.0, cfgpp=0.0, noise_seed=-1, + options=None, sde_noise=None,sde_noise_steps=1, + extra_options="", + ): + # blame comfy here + raw_x = latent_image['raw_x'] if 'raw_x' in latent_image else None + last_seed = latent_image['last_seed'] if 'last_seed' in latent_image else None + + pos_cond = copy_cond(positive) + neg_cond = copy_cond(negative) + + if sampler is None: + raise ValueError("sampler is required") + else: + sampler = copy.deepcopy(sampler) + + default_dtype = getattr(torch, get_extra_options_kv("default_dtype", "float64", extra_options), torch.float64) + + model = model.clone() + if pos_cond[0][1] is not None: + if "regional_conditioning_weights" in pos_cond[0][1]: + sampler.extra_options['regional_conditioning_weights'] = pos_cond[0][1]['regional_conditioning_weights'] + sampler.extra_options['regional_conditioning_floors'] = pos_cond[0][1]['regional_conditioning_floors'] + regional_generate_conditionings_and_masks_fn = pos_cond[0][1]['regional_generate_conditionings_and_masks_fn'] + regional_conditioning, regional_mask = regional_generate_conditionings_and_masks_fn(latent_image['samples']) + regional_conditioning = copy.deepcopy(regional_conditioning) + regional_mask = copy.deepcopy(regional_mask) + model.set_model_patch(regional_conditioning, 'regional_conditioning_positive') + model.set_model_patch(regional_mask, 'regional_conditioning_mask') + + if "noise_seed" in sampler.extra_options: + if sampler.extra_options['noise_seed'] == -1 and noise_seed != -1: + sampler.extra_options['noise_seed'] = noise_seed + 1 + #print("Shark: setting clown noise seed to: ", sampler.extra_options['noise_seed']) + + if "sampler_mode" in sampler.extra_options: + sampler.extra_options['sampler_mode'] = sampler_mode + + if "extra_options" in sampler.extra_options: + extra_options += " " + extra_options += sampler.extra_options['extra_options'] + sampler.extra_options['extra_options'] = extra_options + + batch_size = int(get_extra_options_kv("batch_size", "1", extra_options)) + if batch_size > 1: + latent_image['samples'] = latent_image['samples'].repeat(batch_size, 1, 1, 1) + + latent_image_batch = {"samples": latent_image['samples']} + out_samples, out_samples_fp64, out_denoised_samples, out_denoised_samples_fp64 = [], [], [], [] + for batch_num in range(latent_image_batch['samples'].shape[0]): + latent_unbatch = copy.deepcopy(latent_image) + latent_unbatch['samples'] = latent_image_batch['samples'][batch_num].clone().unsqueeze(0) + + + + if noise_seed == -1: + seed = torch.initial_seed() + 1 + batch_num + else: + seed = noise_seed + batch_num + torch.manual_seed(seed) + torch.cuda.manual_seed(seed) + #torch.cuda.manual_seed_all(seed) + + + if options is not None: + noise_stdev = options.get('noise_init_stdev', noise_stdev) + noise_mean = options.get('noise_init_mean', noise_mean) + noise_type_init = options.get('noise_type_init', noise_type_init) + d_noise = options.get('d_noise', d_noise) + alpha_init = options.get('alpha_init', alpha_init) + k_init = options.get('k_init', k_init) + sde_noise = options.get('sde_noise', sde_noise) + sde_noise_steps = options.get('sde_noise_steps', sde_noise_steps) + + latent_image_dtype = latent_unbatch['samples'].dtype + + if isinstance(model.model.model_config, comfy.supported_models.Flux) or isinstance(model.model.model_config, comfy.supported_models.FluxSchnell): + if pos_cond is None: + pos_cond = [[ + torch.zeros((1, 256, 4096)), + {'pooled_output': torch.zeros((1, 768))} + ]] + + if extra_options_flag("uncond_ortho_flux", extra_options): + if neg_cond is None: + print("uncond_ortho_flux: using random negative conditioning...") + neg_cond = [[ + torch.randn((1, 256, 4096)), + {'pooled_output': torch.randn((1, 768))} + ]] + #neg_cond[0][0] = get_orthogonal(neg_cond[0][0].to(torch.bfloat16), pos_cond[0][0].to(torch.bfloat16)) + #neg_cond[0][1]['pooled_output'] = get_orthogonal(neg_cond[0][1]['pooled_output'].to(torch.bfloat16), pos_cond[0][1]['pooled_output'].to(torch.bfloat16)) + neg_cond[0][0] = get_orthogonal(neg_cond[0][0], pos_cond[0][0]) + neg_cond[0][1]['pooled_output'] = get_orthogonal(neg_cond[0][1]['pooled_output'], pos_cond[0][1]['pooled_output']) + + if neg_cond is None: + neg_cond = [[ + torch.zeros((1, 256, 4096)), + {'pooled_output': torch.zeros((1, 768))} + ]] + else: + if pos_cond is None: + pos_cond = [[ + torch.zeros((1, 154, 4096)), + {'pooled_output': torch.zeros((1, 2048))} + ]] + + if extra_options_flag("uncond_ortho_sd35", extra_options): + if neg_cond is None: + neg_cond = [[ + torch.randn((1, 154, 4096)), + {'pooled_output': torch.randn((1, 2048))} + ]] + + neg_cond[0][0] = get_orthogonal(neg_cond[0][0], pos_cond[0][0]) + neg_cond[0][1]['pooled_output'] = get_orthogonal(neg_cond[0][1]['pooled_output'], pos_cond[0][1]['pooled_output']) + + + if neg_cond is None: + neg_cond = [[ + torch.zeros((1, 154, 4096)), + {'pooled_output': torch.zeros((1, 2048))} + ]] + + + if extra_options_flag("zero_uncond_t5", extra_options): + neg_cond[0][0] = torch.zeros_like(neg_cond[0][0]) + + if extra_options_flag("zero_uncond_pooled_output", extra_options): + neg_cond[0][1]['pooled_output'] = torch.zeros_like(neg_cond[0][1]['pooled_output']) + + if extra_options_flag("zero_pooled_output", extra_options): + pos_cond[0][1]['pooled_output'] = torch.zeros_like(pos_cond[0][1]['pooled_output']) + neg_cond[0][1]['pooled_output'] = torch.zeros_like(neg_cond[0][1]['pooled_output']) + + if denoise_alt < 0: + d_noise = denoise_alt = -denoise_alt + if options is not None: + d_noise = options.get('d_noise', d_noise) + + if sigmas is not None: + sigmas = sigmas.clone().to(default_dtype) + else: + sigmas = get_sigmas(model, scheduler, steps, denoise).to(default_dtype) + sigmas *= denoise_alt + + if sampler_mode.startswith("unsample"): + null = torch.tensor([0.0], device=sigmas.device, dtype=sigmas.dtype) + sigmas = torch.flip(sigmas, dims=[0]) + sigmas = torch.cat([sigmas, null]) + elif sampler_mode.startswith("resample"): + null = torch.tensor([0.0], device=sigmas.device, dtype=sigmas.dtype) + sigmas = torch.cat([null, sigmas]) + sigmas = torch.cat([sigmas, null]) + + x = latent_unbatch["samples"].clone().to(default_dtype) + if latent_unbatch is not None: + if "samples_fp64" in latent_unbatch: + if latent_unbatch['samples'].shape == latent_unbatch['samples_fp64'].shape: + if torch.norm(latent_unbatch['samples'] - latent_unbatch['samples_fp64']) < 0.01: + x = latent_unbatch["samples_fp64"].clone() + + if latent_noise is not None: + latent_noise_samples = latent_noise["samples"].clone().to(default_dtype) + if latent_noise_match is not None: + latent_noise_match_samples = latent_noise_match["samples"].clone().to(default_dtype) + + truncate_conditioning = extra_options_flag("truncate_conditioning", extra_options) + if truncate_conditioning == "true" or truncate_conditioning == "true_and_zero_neg": + if pos_cond is not None: + pos_cond[0][0] = pos_cond[0][0].clone().to(default_dtype) + pos_cond[0][1]["pooled_output"] = pos_cond[0][1]["pooled_output"].clone().to(default_dtype) + if neg_cond is not None: + neg_cond[0][0] = neg_cond[0][0].clone().to(default_dtype) + neg_cond[0][1]["pooled_output"] = neg_cond[0][1]["pooled_output"].clone().to(default_dtype) + c = [] + for t in pos_cond: + d = t[1].copy() + pooled_output = d.get("pooled_output", None) + + for t in neg_cond: + d = t[1].copy() + pooled_output = d.get("pooled_output", None) + if pooled_output is not None: + if truncate_conditioning == "true_and_zero_neg": + d["pooled_output"] = torch.zeros((1,2048), dtype=t[0].dtype, device=t[0].device) + n = [torch.zeros((1,154,4096), dtype=t[0].dtype, device=t[0].device), d] + else: + d["pooled_output"] = d["pooled_output"][:, :2048] + n = [t[0][:, :154, :4096], d] + c.append(n) + neg_cond = c + + sigmin = model.model.model_sampling.sigma_min + sigmax = model.model.model_sampling.sigma_max + + if sde_noise is None and sampler_mode.startswith("unsample"): + total_steps = len(sigmas)+1 + sde_noise = [] + else: + total_steps = 1 + + for total_steps_iter in range (sde_noise_steps): + + if noise_type_init == "none": + noise = torch.zeros_like(x) + elif latent_noise is None: + print("Initial latent noise seed: ", seed) + noise_sampler_init = NOISE_GENERATOR_CLASSES_SIMPLE.get(noise_type_init)(x=x, seed=seed, sigma_min=sigmin, sigma_max=sigmax) + + if noise_type_init == "fractal": + noise_sampler_init.alpha = alpha_init + noise_sampler_init.k = k_init + noise_sampler_init.scale = 0.1 + noise = noise_sampler_init(sigma=sigmax, sigma_next=sigmin) + else: + noise = latent_noise_samples + + if noise_normalize and noise.std() > 0: + noise = (noise - noise.mean(dim=(-2, -1), keepdim=True)) / noise.std(dim=(-2, -1), keepdim=True) + #noise.sub_(noise.mean()).div_(noise.std()) + noise *= noise_stdev + noise = (noise - noise.mean()) + noise_mean + + if latent_noise_match is not None: + for i in range(latent_noise_match_samples.shape[1]): + noise[0][i] = (noise[0][i] - noise[0][i].mean()) + noise[0][i] = (noise[0][i]) + latent_noise_match_samples[0][i].mean() + + noise_mask = latent_unbatch["noise_mask"] if "noise_mask" in latent_unbatch else None + + x0_output = {} + + + if cfg < 0: + sampler.extra_options['cfg_cw'] = -cfg + cfg = 1.0 + else: + sampler.extra_options.pop("cfg_cw", None) + + + if sde_noise is None: + sde_noise = [] + else: + sde_noise = copy.deepcopy(sde_noise) + for i in range(len(sde_noise)): + sde_noise[i] = sde_noise[i] + for j in range(sde_noise[i].shape[1]): + sde_noise[i][0][j] = ((sde_noise[i][0][j] - sde_noise[i][0][j].mean()) / sde_noise[i][0][j].std()) + + callback = latent_preview.prepare_callback(model, sigmas.shape[-1] - 1, x0_output) + + disable_pbar = not comfy.utils.PROGRESS_BAR_ENABLED + + model.model.diffusion_model.raw_x = raw_x + model.model.diffusion_model.last_seed = last_seed + samples = comfy.sample.sample_custom(model, noise, cfg, sampler, sigmas, pos_cond, neg_cond, x.clone(), noise_mask=noise_mask, callback=callback, disable_pbar=disable_pbar, seed=noise_seed) + + out = latent_unbatch.copy() + out["samples"] = samples + if "x0" in x0_output: + out_denoised = latent_unbatch.copy() + out_denoised["samples"] = model.model.process_latent_out(x0_output["x0"].cpu()) + else: + out_denoised = out + + out["samples_fp64"] = out["samples"].clone() + out["samples"] = out["samples"].to(latent_image_dtype) + + out_denoised["samples_fp64"] = out_denoised["samples"].clone() + out_denoised["samples"] = out_denoised["samples"].to(latent_image_dtype) + + out_samples. append(out["samples"]) + out_samples_fp64.append(out["samples_fp64"]) + + out_denoised_samples. append(out_denoised["samples"]) + out_denoised_samples_fp64.append(out_denoised["samples_fp64"]) + + seed += 1 + torch.manual_seed(seed) + if total_steps_iter > 1: + sde_noise.append(out["samples_fp64"]) + + out_samples = [tensor.squeeze(0) for tensor in out_samples] + out_samples_fp64 = [tensor.squeeze(0) for tensor in out_samples_fp64] + out_denoised_samples = [tensor.squeeze(0) for tensor in out_denoised_samples] + out_denoised_samples_fp64 = [tensor.squeeze(0) for tensor in out_denoised_samples_fp64] + + out['samples'] = torch.stack(out_samples, dim=0) + out['samples_fp64'] = torch.stack(out_samples_fp64, dim=0) + + out_denoised['samples'] = torch.stack(out_denoised_samples, dim=0) + out_denoised['samples_fp64'] = torch.stack(out_denoised_samples_fp64, dim=0) + + out['raw_x'] = None + if hasattr(model.model.diffusion_model, "raw_x"): + if model.model.diffusion_model.raw_x is not None: + out['raw_x'] = model.model.diffusion_model.raw_x.clone() + del model.model.diffusion_model.raw_x + + out['last_seed'] = None + if hasattr(model.model.diffusion_model, "last_seed"): + if model.model.diffusion_model.last_seed is not None: + out['last_seed'] = model.model.diffusion_model.last_seed + del model.model.diffusion_model.last_seed + + return ( out, out_denoised, sde_noise,) + + + +class ClownsharKSampler: + @classmethod + def INPUT_TYPES(s): + return {"required": + {"model": ("MODEL",), + "noise_type_init": (NOISE_GENERATOR_NAMES_SIMPLE, {"default": "gaussian"}), + "noise_type_sde": (NOISE_GENERATOR_NAMES_SIMPLE, {"default": "gaussian"}), + "noise_mode_sde": (NOISE_MODE_NAMES, {"default": 'hard', "tooltip": "How noise scales with the sigma schedule. Hard is the most aggressive, the others start strong and drop rapidly."}), + "eta": ("FLOAT", {"default": 0.5, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Calculated noise amount to be added, then removed, after each step."}), + "noise_seed": ("INT", {"default": 0, "min": -1, "max": 0xffffffffffffffff}), + "sampler_mode": (['standard', 'unsample', 'resample'],), + "sampler_name": (RK_SAMPLER_NAMES, {"default": "res_2m"}), + "implicit_sampler_name": (IRK_SAMPLER_NAMES, {"default": "explicit_diagonal"}), + "scheduler": (get_res4lyf_scheduler_list(), {"default": "beta57"},), + "steps": ("INT", {"default": 30, "min": 1, "max": 10000}), + "implicit_steps": ("INT", {"default": 0, "min": 0, "max": 10000}), + "denoise": ("FLOAT", {"default": 1.0, "min": -10000, "max": 10000, "step":0.01}), + "denoise_alt": ("FLOAT", {"default": 1.0, "min": -10000, "max": 10000, "step":0.01}), + "cfg": ("FLOAT", {"default": 3.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, }), + "extra_options": ("STRING", {"default": "", "multiline": True}), + }, + "optional": + { + "positive": ("CONDITIONING", ), + "negative": ("CONDITIONING", ), + "sigmas": ("SIGMAS", ), + "latent_image": ("LATENT", ), + "guides": ("GUIDES", ), + "options": ("OPTIONS", ), + "automation": ("AUTOMATION", ), + } + } + + RETURN_TYPES = ("LATENT","LATENT", "LATENT",) + RETURN_NAMES = ("output", "denoised","sde_noise",) + + FUNCTION = "main" + + CATEGORY = "RES4LYF/legacy/samplers" + DEPRECATED = True + + def main(self, model, cfg, sampler_mode, scheduler, steps, denoise=1.0, denoise_alt=1.0, + noise_type_init="gaussian", noise_type_sde="brownian", noise_mode_sde="hard", latent_image=None, + positive=None, negative=None, sigmas=None, latent_noise=None, latent_noise_match=None, + noise_stdev=1.0, noise_mean=0.0, noise_normalize=True, noise_is_latent=False, + eta=0.25, eta_var=0.0, d_noise=1.0, s_noise=1.0, alpha_init=-1.0, k_init=1.0, alpha_sde=-1.0, k_sde=1.0, cfgpp=0.0, c1=0.0, c2=0.5, c3=1.0, noise_seed=-1, sampler_name="res_2m", implicit_sampler_name="default", + t_fn_formula=None, sigma_fn_formula=None, implicit_steps=0, + latent_guide=None, latent_guide_inv=None, guide_mode="blend", latent_guide_weights=None, latent_guide_weights_inv=None, latent_guide_mask=None, latent_guide_mask_inv=None, rescale_floor=True, sigmas_override=None, + shift=3.0, base_shift=0.85, guides=None, options=None, sde_noise=None,sde_noise_steps=1, shift_scaling="exponential", + extra_options="", automation=None, etas=None, s_noises=None,unsample_resample_scales=None, regional_conditioning_weights=None,frame_weights_grp=None, + ): + + if noise_seed >= 0: + noise_seed_sde = noise_seed + 1 + else: + noise_seed_sde = -1 + + eta_substep = eta + noise_mode_sde_substep = noise_mode_sde + noise_type_sde_substep = noise_type_sde + + sampler = ClownSamplerAdvanced().main( + noise_type_sde=noise_type_sde, noise_type_sde_substep=noise_type_sde_substep, noise_mode_sde=noise_mode_sde, + eta=eta, eta_var=eta_var, d_noise=d_noise, s_noise=s_noise, alpha_sde=alpha_sde, k_sde=k_sde, cfgpp=cfgpp, c1=c1, c2=c2, c3=c3, noise_seed_sde=noise_seed_sde, sampler_name=sampler_name, implicit_sampler_name=implicit_sampler_name, + t_fn_formula=t_fn_formula, sigma_fn_formula=sigma_fn_formula, implicit_steps=implicit_steps, + latent_guide=latent_guide, latent_guide_inv=latent_guide_inv, guide_mode=guide_mode, latent_guide_weights=latent_guide_weights, latent_guide_weights_inv=latent_guide_weights_inv, latent_guide_mask=latent_guide_mask, latent_guide_mask_inv=latent_guide_mask_inv, rescale_floor=rescale_floor, sigmas_override=sigmas_override, + guides=guides, options=options, sde_noise=sde_noise,sde_noise_steps=sde_noise_steps, + extra_options=extra_options, automation=automation, etas=etas, s_noises=s_noises,unsample_resample_scales=unsample_resample_scales, regional_conditioning_weights=regional_conditioning_weights,frame_weights_grp=frame_weights_grp, eta_substep=eta_substep, noise_mode_sde_substep=noise_mode_sde_substep, + ) + + return SharkSamplerAlpha().main( + model=model, cfg=cfg, sampler_mode=sampler_mode, scheduler=scheduler, steps=steps, + denoise=denoise, denoise_alt=denoise_alt, noise_type_init=noise_type_init, + latent_image=latent_image, positive=positive, negative=negative, sampler=sampler[0], + sigmas=sigmas, latent_noise=latent_noise, latent_noise_match=latent_noise_match, + noise_stdev=noise_stdev, noise_mean=noise_mean, noise_normalize=noise_normalize, + d_noise=d_noise, alpha_init=alpha_init, k_init=k_init, cfgpp=cfgpp, noise_seed=noise_seed, + options=options, sde_noise=sde_noise, sde_noise_steps=sde_noise_steps, + extra_options=extra_options + ) + + + + + +class UltraSharkSampler: + # for use with https://github.com/ClownsharkBatwing/UltraCascade + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "model": ("MODEL",), + "add_noise": ("BOOLEAN", {"default": True}), + "normalize_noise": ("BOOLEAN", {"default": False}), + "noise_type": (NOISE_GENERATOR_NAMES, ), + "alpha": ("FLOAT", {"default": 1.0, "min": -10000.0, "max": 10000.0, "step":0.1, "round": 0.01}), + "k": ("FLOAT", {"default": 1.0, "min": -10000.0, "max": 10000.0, "step":2.0, "round": 0.01}), + "noise_seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}), + "cfg": ("FLOAT", {"default": 6.0, "min": 0.0, "max": 100.0, "step":0.5, "round": 0.01}), + "positive": ("CONDITIONING", ), + "negative": ("CONDITIONING", ), + "sampler": ("SAMPLER", ), + "sigmas": ("SIGMAS", ), + "latent_image": ("LATENT", ), + "guide_type": (['residual', 'weighted'], ), + "guide_weight": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": 0.01}), + }, + "optional": { + #"latent_noise": ("LATENT", ), + "guide": ("LATENT",), + "guide_weights": ("SIGMAS",), + #"style": ("CONDITIONING", ), + #"img_style": ("CONDITIONING", ), + } + } + + RETURN_TYPES = ("LATENT","LATENT","LATENT") + RETURN_NAMES = ("output", "denoised_output", "latent_batch") + + FUNCTION = "main" + + CATEGORY = "RES4LYF/legacy/samplers/UltraCascade" + DESCRIPTION = "For use with Stable Cascade and UltraCascade." + DEPRECATED = True + + def main(self, model, add_noise, normalize_noise, noise_type, noise_seed, cfg, alpha, k, positive, negative, sampler, + sigmas, guide_type, guide_weight, latent_image, latent_noise=None, guide=None, guide_weights=None, style=None, img_style=None): + + if model.model.model_config.unet_config.get('stable_cascade_stage') == 'up': + model = model.clone() + x_lr = guide['samples'] if guide is not None else None + guide_weights = initialize_or_scale(guide_weights, guide_weight, 10000)#("FLOAT", {"default": 1.0, "min": -10000, "max": 10000, "step":0.01}), + #model.model.diffusion_model.set_guide_weights(guide_weights=guide_weights) + #model.model.diffusion_model.set_guide_type(guide_type=guide_type) + #model.model.diffusion_model.set_x_lr(x_lr=x_lr) + patch = model.model_options.get("transformer_options", {}).get("patches_replace", {}).get("ultracascade", {}).get("main") + if patch is not None: + patch.update(x_lr=x_lr, guide_weights=guide_weights, guide_type=guide_type) + else: + model.model.diffusion_model.set_sigmas_schedule(sigmas_schedule=sigmas) + model.model.diffusion_model.set_sigmas_prev(sigmas_prev=sigmas[:1]) + model.model.diffusion_model.set_guide_weights(guide_weights=guide_weights) + model.model.diffusion_model.set_guide_type(guide_type=guide_type) + model.model.diffusion_model.set_x_lr(x_lr=x_lr) + + elif model.model.model_config.unet_config['stable_cascade_stage'] == 'b': + c_pos, c_neg = [], [] + for t in positive: + d_pos = t[1].copy() + d_neg = t[1].copy() + + d_pos['stable_cascade_prior'] = guide['samples'] + + pooled_output = d_neg.get("pooled_output", None) + if pooled_output is not None: + d_neg["pooled_output"] = torch.zeros_like(pooled_output) + + c_pos.append([t[0], d_pos]) + c_neg.append([torch.zeros_like(t[0]), d_neg]) + positive = c_pos + negative = c_neg + + if style is not None: + model.set_model_patch(style, 'style_cond') + if img_style is not None: + model.set_model_patch(img_style,'img_style_cond') + + # 1, 768 clip_style[0][0][1]['unclip_conditioning'][0]['clip_vision_output'].image_embeds.shape + # 1, 1280 clip_style[0][0][1]['pooled_output'].shape + # 1, 77, 1280 clip_style[0][0][0].shape + + latent = latent_image + latent_image = latent["samples"] + torch.manual_seed(noise_seed) + + if not add_noise: + noise = torch.zeros(latent_image.size(), dtype=latent_image.dtype, layout=latent_image.layout, device="cpu") + elif latent_noise is None: + batch_inds = latent["batch_index"] if "batch_index" in latent else None + noise = prepare_noise(latent_image, noise_seed, noise_type, batch_inds, alpha, k) + else: + noise = latent_noise["samples"]#.to(torch.float64) + + if normalize_noise and noise.std() > 0: + noise = (noise - noise.mean(dim=(-2, -1), keepdim=True)) / noise.std(dim=(-2, -1), keepdim=True) + + noise_mask = None + if "noise_mask" in latent: + noise_mask = latent["noise_mask"] + + x0_output = {} + callback = latent_preview.prepare_callback(model, sigmas.shape[-1] - 1, x0_output) + disable_pbar = False + + samples = comfy.sample.sample_custom(model, noise, cfg, sampler, sigmas, positive, negative, latent_image, + noise_mask=noise_mask, callback=callback, disable_pbar=disable_pbar, + seed=noise_seed) + + out = latent.copy() + out["samples"] = samples + if "x0" in x0_output: + out_denoised = latent.copy() + out_denoised["samples"] = model.model.process_latent_out(x0_output["x0"].cpu()) + else: + out_denoised = out + + return (out, out_denoised) + + diff --git a/ComfyUI/custom_nodes/RES4LYF/legacy/samplers_extensions.py b/ComfyUI/custom_nodes/RES4LYF/legacy/samplers_extensions.py new file mode 100644 index 0000000000000000000000000000000000000000..d3463a8a45b98ceb6f62959df8ee174285a0b094 --- /dev/null +++ b/ComfyUI/custom_nodes/RES4LYF/legacy/samplers_extensions.py @@ -0,0 +1,592 @@ +from .noise_classes import NOISE_GENERATOR_CLASSES, NOISE_GENERATOR_CLASSES_SIMPLE, NOISE_GENERATOR_NAMES, NOISE_GENERATOR_NAMES_SIMPLE + +import comfy.sample +import comfy.sampler_helpers +import comfy.model_sampling +import comfy.latent_formats +import comfy.sd +import comfy.supported_models +from .conditioning import FluxRegionalPrompt, FluxRegionalConditioning +from .models import ReFluxPatcher + + +import torch +import torch.nn.functional as F + +import copy + +from .helper import initialize_or_scale, get_res4lyf_scheduler_list + + +def move_to_same_device(*tensors): + if not tensors: + return tensors + + device = tensors[0].device + return tuple(tensor.to(device) for tensor in tensors) + + + + + + +class SamplerOptions_TimestepScaling: + # for patching the t_fn and sigma_fn (sigma <-> timestep) formulas to allow picking Runge-Kutta Ci values ("midpoints") with different scaling. + @classmethod + def INPUT_TYPES(s): + return {"required": + { + "sampler": ("SAMPLER", ), + "t_fn_formula": ("STRING", {"default": "1/((sigma).exp()+1)", "multiline": True}), + "sigma_fn_formula": ("STRING", {"default": "((1-t)/t).log()", "multiline": True}), + }, + "optional": + { + } + } + RETURN_TYPES = ("SAMPLER",) + RETURN_NAMES = ("sampler",) + FUNCTION = "set_sampler_extra_options" + + CATEGORY = "RES4LYF/legacy/sampler_extensions" + DESCRIPTION = "Patches ClownSampler's t_fn and sigma_fn (sigma <-> timestep) formulas to allow picking Runge-Kutta Ci values (midpoints) with different scaling." + DEPRECATED = True + + def set_sampler_extra_options(self, sampler, t_fn_formula=None, sigma_fn_formula=None, ): + + sampler = copy.deepcopy(sampler) + + sampler.extra_options['t_fn_formula'] = t_fn_formula + sampler.extra_options['sigma_fn_formula'] = sigma_fn_formula + + return (sampler, ) + + + +class SamplerOptions_GarbageCollection: + @classmethod + def INPUT_TYPES(s): + return {"required": + { + "sampler": ("SAMPLER", ), + "garbage_collection": ("BOOLEAN", {"default": True}), + }, + "optional": + { + } + } + RETURN_TYPES = ("SAMPLER",) + RETURN_NAMES = ("sampler",) + FUNCTION = "set_sampler_extra_options" + CATEGORY = "RES4LYF/legacy/sampler_extensions" + DESCRIPTION = "Patches ClownSampler to use garbage collection after every step. This can help with OOM issues during inference for large models like Flux. The tradeoff is slower sampling." + DEPRECATED = True + + def set_sampler_extra_options(self, sampler, garbage_collection): + sampler = copy.deepcopy(sampler) + sampler.extra_options['GARBAGE_COLLECT'] = garbage_collection + return (sampler, ) + + + +GUIDE_MODE_NAMES = ["unsample", + "resample", + "epsilon", + "epsilon_projection", + "epsilon_dynamic_mean", + "epsilon_dynamic_mean_std", + "epsilon_dynamic_mean_from_bkg", + "epsilon_guide_mean_std_from_bkg", + "hard_light", + "blend", + "blend_projection", + "mean_std", + "mean", + "mean_tiled", + "std", + "data", + #"data_projection", + "none", +] + + + + +class ClownInpaint: ################################################################################################################################## + @classmethod + def INPUT_TYPES(s): + return {"required": + {#"guide_mode": (GUIDE_MODE_NAMES, {"default": 'epsilon', "tooltip": "Recommended: epsilon or mean/mean_std with sampler_mode = standard, and unsample/resample with sampler_mode = unsample/resample. Epsilon_dynamic_mean, etc. are only used with two latent inputs and a mask. Blend/hard_light/mean/mean_std etc. require low strengths, start with 0.01-0.02."}), + "guide_weight": ("FLOAT", {"default": 0.10, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Set the strength of the guide."}), + "guide_weight_bkg": ("FLOAT", {"default": 1.00, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Set the strength of the guide_bkg."}), + "guide_weight_scheduler": (["constant"] + get_res4lyf_scheduler_list(), {"default": "beta57"},), + "guide_weight_scheduler_bkg": (["constant"] + get_res4lyf_scheduler_list(), {"default": "constant"},), + "guide_end_step": ("INT", {"default": 15, "min": 1, "max": 10000}), + "guide_bkg_end_step": ("INT", {"default": 10000, "min": 1, "max": 10000}), + }, + "optional": + { + "model": ("MODEL", ), + "positive_inpaint": ("CONDITIONING", ), + "positive_bkg": ("CONDITIONING", ), + "negative": ("CONDITIONING", ), + "latent_image": ("LATENT", ), + "mask": ("MASK", ), + "guide_weights": ("SIGMAS", ), + "guide_weights_bkg": ("SIGMAS", ), + } + } + RETURN_TYPES = ("MODEL","CONDITIONING","CONDITIONING","LATENT","GUIDES",) + RETURN_NAMES = ("model","positive" ,"negative" ,"latent","guides",) + CATEGORY = "RES4LYF/legacy/sampler_extensions" + + FUNCTION = "main" + DEPRECATED = True + + def main(self, guide_weight_scheduler="constant", guide_weight_scheduler_bkg="constant", guide_end_step=10000, guide_bkg_end_step=30, guide_weight_scale=1.0, guide_weight_bkg_scale=1.0, guide=None, guide_bkg=None, guide_weight=1.0, guide_weight_bkg=1.0, + guide_mode="epsilon", guide_weights=None, guide_weights_bkg=None, guide_mask_bkg=None, + model=None, positive_inpaint=None, positive_bkg=None, negative=None, latent_image=None, mask=None, + ): + default_dtype = torch.float64 + guide = latent_image + guide_bkg = {'samples': latent_image['samples'].clone()} + + max_steps = 10000 + + denoise, denoise_bkg = guide_weight_scale, guide_weight_bkg_scale + + if guide_mode.startswith("epsilon_") and not guide_mode.startswith("epsilon_projection") and guide_bkg == None: + print("Warning: need two latent inputs for guide_mode=",guide_mode," to work. Falling back to epsilon.") + guide_mode = "epsilon" + + if guide_weight_scheduler == "constant": + guide_weights = initialize_or_scale(None, guide_weight, guide_end_step).to(default_dtype) + guide_weights = F.pad(guide_weights, (0, max_steps), value=0.0) + + if guide_weight_scheduler_bkg == "constant": + guide_weights_bkg = initialize_or_scale(None, guide_weight_bkg, guide_bkg_end_step).to(default_dtype) + guide_weights_bkg = F.pad(guide_weights_bkg, (0, max_steps), value=0.0) + + guides = (guide_mode, guide_weight, guide_weight_bkg, guide_weights, guide_weights_bkg, guide, guide_bkg, mask, guide_mask_bkg, + guide_weight_scheduler, guide_weight_scheduler_bkg, guide_end_step, guide_bkg_end_step, denoise, denoise_bkg) + + latent = {'samples': torch.zeros_like(latent_image['samples'])} + if (positive_inpaint is None) and (positive_bkg is None): + positive = None + else: + if positive_bkg is None: + if positive_bkg is None: + positive_bkg = [[ + torch.zeros((1, 256, 4096)), + {'pooled_output': torch.zeros((1, 768))} + ]] + cond_regional, mask_inv = FluxRegionalPrompt().main(cond=positive_inpaint, mask=mask) + cond_regional, mask_inv_inv = FluxRegionalPrompt().main(cond=positive_bkg , cond_regional=cond_regional, mask=mask_inv) + + positive, = FluxRegionalConditioning().main(conditioning_regional=cond_regional, self_attn_floor=0.0) + + model, = ReFluxPatcher().main(model, enable=True) + + return (model, positive, negative, latent, guides, ) + + +class ClownInpaintSimple: ################################################################################################################################## + @classmethod + def INPUT_TYPES(s): + return {"required": + {#"guide_mode": (GUIDE_MODE_NAMES, {"default": 'epsilon', "tooltip": "Recommended: epsilon or mean/mean_std with sampler_mode = standard, and unsample/resample with sampler_mode = unsample/resample. Epsilon_dynamic_mean, etc. are only used with two latent inputs and a mask. Blend/hard_light/mean/mean_std etc. require low strengths, start with 0.01-0.02."}), + "guide_weight": ("FLOAT", {"default": 0.10, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Set the strength of the guide."}), + "guide_weight_scheduler": (["constant"] + get_res4lyf_scheduler_list(), {"default": "beta57"},), + "guide_end_step": ("INT", {"default": 15, "min": 1, "max": 10000}), + }, + "optional": + { + "model": ("MODEL", ), + "positive_inpaint": ("CONDITIONING", ), + "negative": ("CONDITIONING", ), + "latent_image": ("LATENT", ), + "mask": ("MASK", ), + } + } + RETURN_TYPES = ("MODEL","CONDITIONING","CONDITIONING","LATENT","GUIDES",) + RETURN_NAMES = ("model","positive" ,"negative" ,"latent","guides",) + CATEGORY = "RES4LYF/legacy/sampler_extensions" + + FUNCTION = "main" + DEPRECATED = True + + def main(self, guide_weight_scheduler="constant", guide_weight_scheduler_bkg="constant", guide_end_step=10000, guide_bkg_end_step=30, guide_weight_scale=1.0, guide_weight_bkg_scale=1.0, guide=None, guide_bkg=None, guide_weight=1.0, guide_weight_bkg=1.0, + guide_mode="epsilon", guide_weights=None, guide_weights_bkg=None, guide_mask_bkg=None, + model=None, positive_inpaint=None, positive_bkg=None, negative=None, latent_image=None, mask=None, + ): + default_dtype = torch.float64 + guide = latent_image + guide_bkg = {'samples': latent_image['samples'].clone()} + + max_steps = 10000 + + denoise, denoise_bkg = guide_weight_scale, guide_weight_bkg_scale + + if guide_mode.startswith("epsilon_") and not guide_mode.startswith("epsilon_projection") and guide_bkg == None: + print("Warning: need two latent inputs for guide_mode=",guide_mode," to work. Falling back to epsilon.") + guide_mode = "epsilon" + + if guide_weight_scheduler == "constant": + guide_weights = initialize_or_scale(None, guide_weight, guide_end_step).to(default_dtype) + guide_weights = F.pad(guide_weights, (0, max_steps), value=0.0) + + if guide_weight_scheduler_bkg == "constant": + guide_weights_bkg = initialize_or_scale(None, guide_weight_bkg, guide_bkg_end_step).to(default_dtype) + guide_weights_bkg = F.pad(guide_weights_bkg, (0, max_steps), value=0.0) + + guides = (guide_mode, guide_weight, guide_weight_bkg, guide_weights, guide_weights_bkg, guide, guide_bkg, mask, guide_mask_bkg, + guide_weight_scheduler, guide_weight_scheduler_bkg, guide_end_step, guide_bkg_end_step, denoise, denoise_bkg) + + latent = {'samples': torch.zeros_like(latent_image['samples'])} + if (positive_inpaint is None) and (positive_bkg is None): + positive = None + else: + if positive_bkg is None: + if positive_bkg is None: + positive_bkg = [[ + torch.zeros((1, 256, 4096)), + {'pooled_output': torch.zeros((1, 768))} + ]] + cond_regional, mask_inv = FluxRegionalPrompt().main(cond=positive_inpaint, mask=mask) + cond_regional, mask_inv_inv = FluxRegionalPrompt().main(cond=positive_bkg , cond_regional=cond_regional, mask=mask_inv) + + positive, = FluxRegionalConditioning().main(conditioning_regional=cond_regional, self_attn_floor=1.0) + + model, = ReFluxPatcher().main(model, enable=True) + + return (model, positive, negative, latent, guides, ) + + +################################################################################################################################## + + +class ClownsharKSamplerGuide: + @classmethod + def INPUT_TYPES(s): + return {"required": + {"guide_mode": (GUIDE_MODE_NAMES, {"default": 'epsilon_projection', "tooltip": "Recommended: epsilon or mean/mean_std with sampler_mode = standard, and unsample/resample with sampler_mode = unsample/resample. Epsilon_dynamic_mean, etc. are only used with two latent inputs and a mask. Blend/hard_light/mean/mean_std etc. require low strengths, start with 0.01-0.02."}), + "guide_weight": ("FLOAT", {"default": 0.75, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Set the strength of the guide."}), + #"guide_weight_bkg": ("FLOAT", {"default": 0.75, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Set the strength of the guide_bkg."}), + "guide_weight_scale": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step":0.01, "round": False, "tooltip": "Disables the guide for the next step when the denoised image is similar to the guide. Higher values will strengthen the effect."}), + #"guide_weight_bkg_scale": ("FLOAT", {"default": 1.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Disables the guide for the next step when the denoised image is similar to the guide. Higher values will strengthen the effect."}), + "guide_weight_scheduler": (["constant"] + get_res4lyf_scheduler_list(), {"default": "beta57"},), + #"guide_weight_scheduler_bkg": (["constant"] + comfy.samplers.SCHEDULER_NAMES + ["beta57"], {"default": "beta57"},), + "guide_end_step": ("INT", {"default": 15, "min": 1, "max": 10000}), + #"guide_bkg_end_step": ("INT", {"default": 15, "min": 1, "max": 10000}), + }, + "optional": + { + "guide": ("LATENT", ), + #"guide_bkg": ("LATENT", ), + "guide_mask": ("MASK", ), + #"guide_mask_bkg": ("MASK", ), + "guide_weights": ("SIGMAS", ), + #"guide_weights_bkg": ("SIGMAS", ), + } + } + RETURN_TYPES = ("GUIDES",) + RETURN_NAMES = ("guides",) + CATEGORY = "RES4LYF/legacy/sampler_extensions" + + FUNCTION = "main" + DEPRECATED = True + + def main(self, guide_weight_scheduler="constant", guide_weight_scheduler_bkg="constant", guide_end_step=30, guide_bkg_end_step=30, guide_weight_scale=1.0, guide_weight_bkg_scale=1.0, guide=None, guide_bkg=None, guide_weight=0.0, guide_weight_bkg=0.0, + guide_mode="blend", guide_weights=None, guide_weights_bkg=None, guide_mask=None, guide_mask_bkg=None, + ): + default_dtype = torch.float64 + + max_steps = 10000 + + denoise, denoise_bkg = guide_weight_scale, guide_weight_bkg_scale + + if guide_mode.startswith("epsilon_") and not guide_mode.startswith("epsilon_projection") and guide_bkg == None: + print("Warning: need two latent inputs for guide_mode=",guide_mode," to work. Falling back to epsilon.") + guide_mode = "epsilon" + + if guide_weight_scheduler == "constant" and guide_weights == None: + guide_weights = initialize_or_scale(None, 1.0, guide_end_step).to(default_dtype) + #guide_weights = initialize_or_scale(None, guide_weight, guide_end_step).to(default_dtype) + guide_weights = F.pad(guide_weights, (0, max_steps), value=0.0) + + if guide_weight_scheduler_bkg == "constant": + guide_weights_bkg = initialize_or_scale(None, 0.0, guide_bkg_end_step).to(default_dtype) + #guide_weights_bkg = initialize_or_scale(None, guide_weight_bkg, guide_bkg_end_step).to(default_dtype) + guide_weights_bkg = F.pad(guide_weights_bkg, (0, max_steps), value=0.0) + + guides = (guide_mode, guide_weight, guide_weight_bkg, guide_weights, guide_weights_bkg, guide, guide_bkg, guide_mask, guide_mask_bkg, + guide_weight_scheduler, guide_weight_scheduler_bkg, guide_end_step, guide_bkg_end_step, denoise, denoise_bkg) + return (guides, ) + + + + +class ClownsharKSamplerGuides: + @classmethod + def INPUT_TYPES(s): + return {"required": + {"guide_mode": (GUIDE_MODE_NAMES, {"default": 'epsilon_projection', "tooltip": "Recommended: epsilon or mean/mean_std with sampler_mode = standard, and unsample/resample with sampler_mode = unsample/resample. Epsilon_dynamic_mean, etc. are only used with two latent inputs and a mask. Blend/hard_light/mean/mean_std etc. require low strengths, start with 0.01-0.02."}), + "guide_weight": ("FLOAT", {"default": 0.75, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Set the strength of the guide."}), + "guide_weight_bkg": ("FLOAT", {"default": 0.75, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Set the strength of the guide_bkg."}), + "guide_weight_scale": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step":0.01, "round": False, "tooltip": "Disables the guide for the next step when the denoised image is similar to the guide. Higher values will strengthen the effect."}), + "guide_weight_bkg_scale": ("FLOAT", {"default": 1.0, "min": -100.0, "max": 100.0, "step":0.01, "round": False, "tooltip": "Disables the guide for the next step when the denoised image is similar to the guide. Higher values will strengthen the effect."}), + "guide_weight_scheduler": (["constant"] + get_res4lyf_scheduler_list(), {"default": "beta57"},), + "guide_weight_scheduler_bkg": (["constant"] + get_res4lyf_scheduler_list(), {"default": "constant"},), + "guide_end_step": ("INT", {"default": 15, "min": 1, "max": 10000}), + "guide_bkg_end_step": ("INT", {"default": 15, "min": 1, "max": 10000}), + }, + "optional": + { + "guide": ("LATENT", ), + "guide_bkg": ("LATENT", ), + "guide_mask": ("MASK", ), + "guide_mask_bkg": ("MASK", ), + "guide_weights": ("SIGMAS", ), + "guide_weights_bkg": ("SIGMAS", ), + } + } + RETURN_TYPES = ("GUIDES",) + RETURN_NAMES = ("guides",) + CATEGORY = "RES4LYF/legacy/sampler_extensions" + + FUNCTION = "main" + DEPRECATED = True + + def main(self, guide_weight_scheduler="constant", guide_weight_scheduler_bkg="constant", guide_end_step=30, guide_bkg_end_step=30, guide_weight_scale=1.0, guide_weight_bkg_scale=1.0, guide=None, guide_bkg=None, guide_weight=0.0, guide_weight_bkg=0.0, + guide_mode="blend", guide_weights=None, guide_weights_bkg=None, guide_mask=None, guide_mask_bkg=None, + ): + default_dtype = torch.float64 + + max_steps = 10000 + + denoise, denoise_bkg = guide_weight_scale, guide_weight_bkg_scale + + if guide_mode.startswith("epsilon_") and not guide_mode.startswith("epsilon_projection") and guide_bkg == None: + print("Warning: need two latent inputs for guide_mode=",guide_mode," to work. Falling back to epsilon.") + guide_mode = "epsilon" + + if guide_weight_scheduler == "constant" and guide_weights == None: + guide_weights = initialize_or_scale(None, 1.0, guide_end_step).to(default_dtype) + guide_weights = F.pad(guide_weights, (0, max_steps), value=0.0) + + if guide_weight_scheduler_bkg == "constant" and guide_weights_bkg == None: + guide_weights_bkg = initialize_or_scale(None, 1.0, guide_bkg_end_step).to(default_dtype) + guide_weights_bkg = F.pad(guide_weights_bkg, (0, max_steps), value=0.0) + + guides = (guide_mode, guide_weight, guide_weight_bkg, guide_weights, guide_weights_bkg, guide, guide_bkg, guide_mask, guide_mask_bkg, + guide_weight_scheduler, guide_weight_scheduler_bkg, guide_end_step, guide_bkg_end_step, denoise, denoise_bkg) + return (guides, ) + + + + + + + + +class ClownsharKSamplerAutomation: + @classmethod + def INPUT_TYPES(s): + return {"required": + { + }, + "optional": + { + "etas": ("SIGMAS", ), + "s_noises": ("SIGMAS", ), + "unsample_resample_scales": ("SIGMAS", ), + + } + } + RETURN_TYPES = ("AUTOMATION",) + RETURN_NAMES = ("automation",) + CATEGORY = "RES4LYF/legacy/sampler_extensions" + + FUNCTION = "main" + DEPRECATED = True + + def main(self, etas=None, s_noises=None, unsample_resample_scales=None,): + automation = (etas, s_noises, unsample_resample_scales) + return (automation, ) + + + + +class ClownsharKSamplerAutomation_Advanced: + @classmethod + def INPUT_TYPES(s): + return {"required": + { + }, + "optional": + { + "automation": ("AUTOMATION", ), + "etas": ("SIGMAS", ), + "etas_substep": ("SIGMAS", ), + "s_noises": ("SIGMAS", ), + "unsample_resample_scales": ("SIGMAS", ), + "frame_weights": ("SIGMAS", ), + "frame_weights_bkg": ("SIGMAS", ), + } + } + RETURN_TYPES = ("AUTOMATION",) + RETURN_NAMES = ("automation",) + CATEGORY = "RES4LYF/legacy/sampler_extensions" + + FUNCTION = "main" + DEPRECATED = True + + def main(self, automation=None, etas=None, etas_substep=None, s_noises=None, unsample_resample_scales=None, frame_weights=None, frame_weights_bkg=None): + + if automation is None: + automation = {} + + frame_weights_grp = (frame_weights, frame_weights_bkg) + + automation['etas'] = etas + automation['etas_substep'] = etas_substep + automation['s_noises'] = s_noises + automation['unsample_resample_scales'] = unsample_resample_scales + automation['frame_weights_grp'] = frame_weights_grp + + return (automation, ) + + + +class ClownsharKSamplerOptions: + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "noise_init_stdev": ("FLOAT", {"default": 1.0, "min": -10000.0, "max": 10000.0, "step":0.01, "round": False, }), + "noise_init_mean": ("FLOAT", {"default": 0.0, "min": -10000.0, "max": 10000.0, "step":0.01, "round": False, }), + "noise_type_init": (NOISE_GENERATOR_NAMES, {"default": "gaussian"}), + "noise_type_sde": (NOISE_GENERATOR_NAMES, {"default": "brownian"}), + "noise_mode_sde": (["hard", "hard_var", "hard_sq", "soft", "softer", "exp"], {"default": 'hard', "tooltip": "How noise scales with the sigma schedule. Hard is the most aggressive, the others start strong and drop rapidly."}), + "eta": ("FLOAT", {"default": 0.25, "min": -100.0, "max": 100.0, "step":0.01, "round": False}), + "s_noise": ("FLOAT", {"default": 1.0, "min": -10000, "max": 10000, "step":0.01, "round": False}), + "d_noise": ("FLOAT", {"default": 1.0, "min": -10000, "max": 10000, "step":0.01}), + "alpha_init": ("FLOAT", {"default": 0.0, "min": -10000.0, "max": 10000.0, "step": 0.1}), + "k_init": ("FLOAT", {"default": 1.0, "min": -10000.0, "max": 10000.0, "step": 2}), + "alpha_sde": ("FLOAT", {"default": 0.0, "min": -10000.0, "max": 10000.0, "step": 0.1}), + "k_sde": ("FLOAT", {"default": 1.0, "min": -10000.0, "max": 10000.0, "step": 2}), + "noise_seed": ("INT", {"default": -1, "min": -1, "max": 0xffffffffffffffff, "tooltip": "Seed for the SDE noise that is added after each step if eta or eta_var are non-zero. If set to -1, it will use the increment the seed most recently used by the workflow."}), + "c1": ("FLOAT", {"default": 0.0, "min": -1.0, "max": 10000.0, "step": 0.01}), + "c2": ("FLOAT", {"default": 0.5, "min": -1.0, "max": 10000.0, "step": 0.01}), + "c3": ("FLOAT", {"default": 1.0, "min": -1.0, "max": 10000.0, "step": 0.01}), + "t_fn_formula": ("STRING", {"default": "", "multiline": True}), + "sigma_fn_formula": ("STRING", {"default": "", "multiline": True}), + #"unsampler_type": (['linear', 'exponential', 'constant'],), + }, + "optional": { + "options": ("OPTIONS",), + } + } + + RETURN_TYPES = ("OPTIONS",) + RETURN_NAMES = ("options",) + CATEGORY = "RES4LYF/legacy/sampler_extensions" + + FUNCTION = "main" + DEPRECATED = True + + def main(self, noise_init_stdev, noise_init_mean, c1, c2, c3, eta, s_noise, d_noise, noise_type_init, noise_type_sde, noise_mode_sde, noise_seed, + alpha_init, k_init, alpha_sde, k_sde, t_fn_formula=None, sigma_fn_formula=None, unsampler_type="linear", + alphas=None, etas=None, s_noises=None, d_noises=None, c2s=None, c3s=None, + options=None, + ): + + if options is None: + options = {} + + options['noise_init_stdev'] = noise_init_stdev + options['noise_init_mean'] = noise_init_mean + options['noise_type_init'] = noise_type_init + options['noise_type_sde'] = noise_type_sde + options['noise_mode_sde'] = noise_mode_sde + options['eta'] = eta + options['s_noise'] = s_noise + options['d_noise'] = d_noise + options['alpha_init'] = alpha_init + options['k_init'] = k_init + options['alpha_sde'] = alpha_sde + options['k_sde'] = k_sde + options['noise_seed_sde'] = noise_seed + options['c1'] = c1 + options['c2'] = c2 + options['c3'] = c3 + options['t_fn_formula'] = t_fn_formula + options['sigma_fn_formula'] = sigma_fn_formula + options['unsampler_type'] = unsampler_type + + return (options,) + + + +class ClownOptions_SDE_Noise: + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "sde_noise_steps": ("INT", {"default": 1, "min": 1, "max": 10000}), + }, + "optional": { + "sde_noise": ("LATENT",), + "options" : ("OPTIONS",), + } + } + + RETURN_TYPES = ("OPTIONS",) + RETURN_NAMES = ("options",) + CATEGORY = "RES4LYF/legacy/sampler_options" + FUNCTION = "main" + DEPRECATED = True + + def main(self, sde_noise_steps, sde_noise, options=None,): + + if options is None: + options = {} + + options['sde_noise_steps'] = sde_noise_steps + options['sde_noise'] = sde_noise + + return (options,) + + + +class ClownOptions_FrameWeights: + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "frame_weights": ("SIGMAS", ), + }, + "optional": { + "options": ("OPTIONS",), + } + } + + DEPRECATED = True + RETURN_TYPES = ("OPTIONS",) + RETURN_NAMES = ("options",) + CATEGORY = "RES4LYF/legacy/sampler_options" + + FUNCTION = "main" + DEPRECATED = True + + def main(self, frame_weights, options=None,): + + if options is None: + options = {} + + frame_weights_grp = (frame_weights, frame_weights) + options['frame_weights_grp'] = frame_weights_grp + + return (options,) + + diff --git a/ComfyUI/custom_nodes/RES4LYF/legacy/samplers_tiled.py b/ComfyUI/custom_nodes/RES4LYF/legacy/samplers_tiled.py new file mode 100644 index 0000000000000000000000000000000000000000..d77aaa56f33510c6da34b7adeccbd449d1f6bfd0 --- /dev/null +++ b/ComfyUI/custom_nodes/RES4LYF/legacy/samplers_tiled.py @@ -0,0 +1,568 @@ +# tiled sampler code adapted from https://github.com/BlenderNeko/ComfyUI_TiledKSampler +# and heavily modified for use with https://github.com/ClownsharkBatwing/UltraCascade + +import sys +import os +import copy +from functools import partial + +from tqdm.auto import tqdm + +import torch + +sys.path.insert(0, os.path.join(os.path.dirname(os.path.realpath(__file__)), "comfy")) +import comfy.sd +import comfy.controlnet +import comfy.model_management +import comfy.sample +import comfy.sampler_helpers +import latent_preview + +from nodes import MAX_RESOLUTION +#MAX_RESOLUTION=8192 + +import comfy.clip_vision +import folder_paths + +from . import tiling +from .noise_classes import * + +def initialize_or_scale(tensor, value, steps): + if tensor is None: + return torch.full((steps,), value) + else: + return value * tensor + +def cv_cond(cv_out, conditioning, strength, noise_augmentation): + + c = [] + for t in conditioning: + o = t[1].copy() + x = {"clip_vision_output": cv_out, "strength": strength, "noise_augmentation": noise_augmentation} + if "unclip_conditioning" in o: + o["unclip_conditioning"] = o["unclip_conditioning"][:] + [x] + else: + o["unclip_conditioning"] = [x] + n = [t[0], o] + c.append(n) + + return c + + +def recursion_to_list(obj, attr): + current = obj + yield current + while True: + current = getattr(current, attr, None) + if current is not None: + yield current + else: + return + +def copy_cond(cond): + return [[c1,c2.copy()] for c1,c2 in cond] + +def slice_cond(tile_h, tile_h_len, tile_w, tile_w_len, cond, area): + tile_h_end = tile_h + tile_h_len + tile_w_end = tile_w + tile_w_len + coords = area[0] #h_len, w_len, h, w, + mask = area[1] + if coords is not None: + h_len, w_len, h, w = coords + h_end = h + h_len + w_end = w + w_len + if h < tile_h_end and h_end > tile_h and w < tile_w_end and w_end > tile_w: + new_h = max(0, h - tile_h) + new_w = max(0, w - tile_w) + new_h_end = min(tile_h_end, h_end - tile_h) + new_w_end = min(tile_w_end, w_end - tile_w) + cond[1]['area'] = (new_h_end - new_h, new_w_end - new_w, new_h, new_w) + else: + return (cond, True) + if mask is not None: + new_mask = tiling.get_slice(mask, tile_h,tile_h_len,tile_w,tile_w_len) + if new_mask.sum().cpu() == 0.0 and 'mask' in cond[1]: + return (cond, True) + else: + cond[1]['mask'] = new_mask + return (cond, False) + +def slice_gligen(tile_h, tile_h_len, tile_w, tile_w_len, cond, gligen): + tile_h_end = tile_h + tile_h_len + tile_w_end = tile_w + tile_w_len + if gligen is None: + return + gligen_type = gligen[0] + gligen_model = gligen[1] + gligen_areas = gligen[2] + + gligen_areas_new = [] + for emb, h_len, w_len, h, w in gligen_areas: + h_end = h + h_len + w_end = w + w_len + if h < tile_h_end and h_end > tile_h and w < tile_w_end and w_end > tile_w: + new_h = max(0, h - tile_h) + new_w = max(0, w - tile_w) + new_h_end = min(tile_h_end, h_end - tile_h) + new_w_end = min(tile_w_end, w_end - tile_w) + gligen_areas_new.append((emb, new_h_end - new_h, new_w_end - new_w, new_h, new_w)) + + if len(gligen_areas_new) == 0: + del cond['gligen'] + else: + cond['gligen'] = (gligen_type, gligen_model, gligen_areas_new) + +def slice_cnet(h, h_len, w, w_len, model:comfy.controlnet.ControlBase, img): + if img is None: + img = model.cond_hint_original + hint = tiling.get_slice(img, h*8, h_len*8, w*8, w_len*8) + if isinstance(model, comfy.controlnet.ControlLora): + model.cond_hint = hint.float().to(model.device) + else: + model.cond_hint = hint.to(model.control_model.dtype).to(model.device) + +def slices_T2I(h, h_len, w, w_len, model:comfy.controlnet.ControlBase, img): + model.control_input = None + if img is None: + img = model.cond_hint_original + model.cond_hint = tiling.get_slice(img, h*8, h_len*8, w*8, w_len*8).float().to(model.device) + +# TODO: refactor some of the mess + + +def cnets_and_cnet_imgs(positive, negative, shape): + # cnets + cnets = [c['control'] for (_, c) in positive + negative if 'control' in c] + # unroll recursion + cnets = list(set([x for m in cnets for x in recursion_to_list(m, "previous_controlnet")])) + # filter down to only cnets + cnets = [x for x in cnets if isinstance(x, comfy.controlnet.ControlNet)] + cnet_imgs = [ + torch.nn.functional.interpolate(m.cond_hint_original, (shape[-2] * 8, shape[-1] * 8), mode='nearest-exact').to('cpu') + if m.cond_hint_original.shape[-2] != shape[-2] * 8 or m.cond_hint_original.shape[-1] != shape[-1] * 8 else None + for m in cnets] + return cnets, cnet_imgs + +def T2Is_and_T2I_imgs(positive, negative, shape): + # T2I + T2Is = [c['control'] for (_, c) in positive + negative if 'control' in c] + # unroll recursion + T2Is = [x for m in T2Is for x in recursion_to_list(m, "previous_controlnet")] + # filter down to only T2I + T2Is = [x for x in T2Is if isinstance(x, comfy.controlnet.T2IAdapter)] + T2I_imgs = [ + torch.nn.functional.interpolate(m.cond_hint_original, (shape[-2] * 8, shape[-1] * 8), mode='nearest-exact').to('cpu') + if m.cond_hint_original.shape[-2] != shape[-2] * 8 or m.cond_hint_original.shape[-1] != shape[-1] * 8 or (m.channels_in == 1 and m.cond_hint_original.shape[1] != 1) else None + for m in T2Is + ] + T2I_imgs = [ + torch.mean(img, 1, keepdim=True) if img is not None and m.channels_in == 1 and m.cond_hint_original.shape[1] else img + for m, img in zip(T2Is, T2I_imgs) + ] + return T2Is, T2I_imgs + +def spatial_conds_posneg(positive, negative, shape, device): #cond area and mask + spatial_conds_pos = [ + (c[1]['area'] if 'area' in c[1] else None, + comfy.sample.prepare_mask(c[1]['mask'], shape, device) if 'mask' in c[1] else None) + for c in positive + ] + spatial_conds_neg = [ + (c[1]['area'] if 'area' in c[1] else None, + comfy.sample.prepare_mask(c[1]['mask'], shape, device) if 'mask' in c[1] else None) + for c in negative + ] + return spatial_conds_pos, spatial_conds_neg + +def gligen_posneg(positive, negative): + #gligen + gligen_pos = [ + c[1]['gligen'] if 'gligen' in c[1] else None + for c in positive + ] + gligen_neg = [ + c[1]['gligen'] if 'gligen' in c[1] else None + for c in negative + ] + return gligen_pos, gligen_neg + + +def cascade_tiles(x, input_x, tile_h, tile_w, tile_h_len, tile_w_len): + h_cascade = input_x.shape[-2] + w_cascade = input_x.shape[-1] + + h_samples = x.shape[-2] + w_samples = x.shape[-1] + + tile_h_cascade = (h_cascade * tile_h) // h_samples + tile_w_cascade = (w_cascade * tile_w) // w_samples + + tile_h_len_cascade = (h_cascade * tile_h_len) // h_samples + tile_w_len_cascade = (w_cascade * tile_w_len) // w_samples + + return tile_h_cascade, tile_w_cascade, tile_h_len_cascade, tile_w_len_cascade + + + +def sample_common(model, x, noise, noise_mask, noise_seed, tile_width, tile_height, tiling_strategy, cfg, positive, negative, + preview=False, sampler=None, sigmas=None, + clip_name=None, strength=1.0, noise_augment=1.0, image_cv=None, max_tile_batch_size=3, + guide=None, guide_type='residual', guide_weight=1.0, guide_weights=None, + ): + + device = comfy.model_management.get_torch_device() + steps = len(sigmas)-1 + + conds0 = \ + {"positive": comfy.sampler_helpers.convert_cond(positive), + "negative": comfy.sampler_helpers.convert_cond(negative)} + + conds = {} + for k in conds0: + conds[k] = list(map(lambda a: a.copy(), conds0[k])) + + modelPatches, inference_memory = comfy.sampler_helpers.get_additional_models(conds, model.model_dtype()) + comfy.model_management.load_models_gpu([model] + modelPatches, model.memory_required(noise.shape) + inference_memory) + + + if model.model.model_config.unet_config['stable_cascade_stage'] == 'up': + compression = 1 + guide_weight = 1.0 if guide_weight is None else guide_weight + guide_type = 'residual' if guide_type is None else guide_type + guide = guide['samples'] if guide is not None else None + guide_weights = initialize_or_scale(guide_weights, guide_weight, 10000) + + patch = model.model_options.get("transformer_options", {}).get("patches_replace", {}).get("ultracascade", {}).get("main") #CHANGED HERE + if patch is not None: + patch.update(x_lr=guide, guide_weights=guide_weights, guide_type=guide_type) + else: + model = model.clone() + model.model.diffusion_model.set_sigmas_prev(sigmas_prev=sigmas[:1]) + model.model.diffusion_model.set_guide_weights(guide_weights=guide_weights) + model.model.diffusion_model.set_guide_type(guide_type=guide_type) + + elif model.model.model_config.unet_config['stable_cascade_stage'] == 'c': + compression = 1 + + elif model.model.model_config.unet_config['stable_cascade_stage'] == 'b': + compression = 4 + + c_pos, c_neg = [], [] + for t in positive: + d_pos = t[1].copy() + d_neg = t[1].copy() + + d_pos['stable_cascade_prior'] = guide['samples'] + + pooled_output = d_neg.get("pooled_output", None) + if pooled_output is not None: + d_neg["pooled_output"] = torch.zeros_like(pooled_output) + + c_pos.append([t[0], d_pos]) + c_neg.append([torch.zeros_like(t[0]), d_neg]) + positive = c_pos + negative = c_neg + effnet_samples = positive[0][1]['stable_cascade_prior'].clone() + effnet_interpolated = nn.functional.interpolate(effnet_samples.clone().to(torch.float16).to(device), size=torch.Size((x.shape[-2] // 2, x.shape[-1] // 2,)), mode='bilinear', align_corners=True) + effnet_full_map = model.model.diffusion_model.effnet_mapper(effnet_interpolated) + else: + compression = 8 #sd1.5, sdxl, sd3, flux, etc + + + if image_cv is not None: #CLIP VISION LOAD + clip_path = folder_paths.get_full_path("clip_vision", clip_name) + clip_vision = comfy.clip_vision.load(clip_path) + + + + + cnets, cnet_imgs = cnets_and_cnet_imgs (positive, negative, x.shape) + T2Is, T2I_imgs = T2Is_and_T2I_imgs (positive, negative, x.shape) + spatial_conds_pos, spatial_conds_neg = spatial_conds_posneg(positive, negative, x.shape, device) + gligen_pos, gligen_neg = gligen_posneg (positive, negative) + + + + tile_width = min(x.shape[-1] * compression, tile_width) + tile_height = min(x.shape[2] * compression, tile_height) + + if tiling_strategy != 'padded': + if noise_mask is not None: + x += sigmas[0] * noise_mask * model.model.process_latent_out(noise) + else: + x += sigmas[0] * model.model.process_latent_out(noise) + + + + if tiling_strategy == 'random' or tiling_strategy == 'random strict': + tiles = tiling.get_tiles_and_masks_rgrid(steps, x.shape, tile_height, tile_width, torch.manual_seed(noise_seed), compression=compression) + elif tiling_strategy == 'padded': + tiles = tiling.get_tiles_and_masks_padded(steps, x.shape, tile_height, tile_width, compression=compression) + else: + tiles = tiling.get_tiles_and_masks_simple(steps, x.shape, tile_height, tile_width, compression=compression) + + + + total_steps = sum([num_steps for img_pass in tiles for steps_list in img_pass for _,_,_,_,num_steps,_ in steps_list]) + current_step = [0] + with tqdm(total=total_steps) as pbar_tqdm: + pbar = comfy.utils.ProgressBar(total_steps) + def callback(step, x0, x, total_steps, step_inc=1): + current_step[0] += step_inc + preview_bytes = None + if preview == True: + previewer = latent_preview.get_previewer(device, model.model.latent_format) + preview_bytes = previewer.decode_latent_to_preview_image("JPEG", x0) + pbar.update_absolute(current_step[0], preview=preview_bytes) + pbar_tqdm.update(step_inc) + + + + if tiling_strategy == "random strict": + x_next = x.clone() + + for img_pass in tiles: # img_pass is a set of non-intersecting tiles + effnet_slices, effnet_map_slices, tiled_noise_list, tiled_latent_list, tiled_mask_list, tile_h_list, tile_w_list, tile_h_len_list, tile_w_len_list = [],[],[],[],[],[],[],[],[] + + for i in range(len(img_pass)): + for iteration, (tile_h, tile_h_len, tile_w, tile_w_len, tile_steps, tile_mask) in enumerate(img_pass[i]): + tiled_mask = None + if noise_mask is not None: + tiled_mask = tiling.get_slice(noise_mask, tile_h, tile_h_len, tile_w, tile_w_len).to(device) + if tile_mask is not None: + if tiled_mask is not None: + tiled_mask *= tile_mask.to(device) + else: + tiled_mask = tile_mask.to(device) + + if tiling_strategy == 'padded' or tiling_strategy == 'random strict': + tile_h, tile_h_len, tile_w, tile_w_len, tiled_mask = tiling.mask_at_boundary( tile_h, tile_h_len, tile_w, tile_w_len, + tile_height, tile_width, x.shape[-2], x.shape[-1], + tiled_mask, device, compression=compression) + + if tiled_mask is not None and tiled_mask.sum().cpu() == 0.0: + continue + + tiled_latent = tiling.get_slice(x, tile_h, tile_h_len, tile_w, tile_w_len).to(device) + + if tiling_strategy == 'padded': + tiled_noise = tiling.get_slice(noise, tile_h, tile_h_len, tile_w, tile_w_len).to(device) + else: + if tiled_mask is None or noise_mask is None: + tiled_noise = torch.zeros_like(tiled_latent) + else: + tiled_noise = tiling.get_slice(noise, tile_h, tile_h_len, tile_w, tile_w_len).to(device) * (1 - tiled_mask) + + #TODO: all other condition based stuff like area sets and GLIGEN should also happen here + + #cnets + for m, img in zip(cnets, cnet_imgs): + slice_cnet(tile_h, tile_h_len, tile_w, tile_w_len, m, img) + + #T2I + for m, img in zip(T2Is, T2I_imgs): + slices_T2I(tile_h, tile_h_len, tile_w, tile_w_len, m, img) + + pos = copy.deepcopy(positive) + neg = copy.deepcopy(negative) + + #cond areas + pos = [slice_cond(tile_h, tile_h_len, tile_w, tile_w_len, c, area) for c, area in zip(pos, spatial_conds_pos)] + pos = [c for c, ignore in pos if not ignore] + neg = [slice_cond(tile_h, tile_h_len, tile_w, tile_w_len, c, area) for c, area in zip(neg, spatial_conds_neg)] + neg = [c for c, ignore in neg if not ignore] + + #gligen + for cond, gligen in zip(pos, gligen_pos): + slice_gligen(tile_h, tile_h_len, tile_w, tile_w_len, cond, gligen) + for cond, gligen in zip(neg, gligen_neg): + slice_gligen(tile_h, tile_h_len, tile_w, tile_w_len, cond, gligen) + + start_step = i * tile_steps + last_step = i * tile_steps + tile_steps + + if last_step is not None and last_step < (len(sigmas) - 1): + sigmas = sigmas[:last_step + 1] + + if start_step is not None: + if start_step < (len(sigmas) - 1): + sigmas = sigmas[start_step:] + else: + if tiled_latent is not None: + return tiled_latent + else: + return torch.zeros_like(noise) + + # SLICE, DICE, AND DENOISE + if image_cv is not None: #slice and dice ClipVision for tiling + image_cv = image_cv. permute(0,3,1,2) + tile_h_cascade, tile_w_cascade, tile_h_len_cascade, tile_w_len_cascade = cascade_tiles(x, image_cv, tile_h, tile_w, tile_h_len, tile_w_len) + + image_slice = copy.deepcopy(image_cv) + image_slice = tiling.get_slice(image_slice, tile_h_cascade, tile_h_len_cascade, tile_w_cascade, tile_w_len_cascade).to(device) + image_slice = image_slice.permute(0,2,3,1) + image_cv = image_cv. permute(0,2,3,1) + + cv_out_slice = clip_vision.encode_image(image_slice) + pos = cv_cond(cv_out_slice, pos, strength, noise_augment) + + if model.model.model_config.unet_config['stable_cascade_stage'] == 'up': #slice and dice stage UP guide + tile_h_cascade, tile_w_cascade, tile_h_len_cascade, tile_w_len_cascade = cascade_tiles(x, guide, tile_h, tile_w, tile_h_len, tile_w_len) + + guide_slice = copy.deepcopy(guide) + guide_slice = tiling.get_slice(guide_slice.clone(), tile_h_cascade, tile_h_len_cascade, tile_w_cascade, tile_w_len_cascade).to(device) + model.model.diffusion_model.set_x_lr(x_lr=guide_slice) + + tile_result = comfy.sample.sample_custom(model, tiled_noise, cfg, sampler, sigmas, pos, neg, tiled_latent, noise_mask=tiled_mask, callback=callback, disable_pbar=True, seed=noise_seed) + + elif model.model.model_config.unet_config['stable_cascade_stage'] == 'b': #slice and dice stage B conditioning + tile_h_cascade, tile_w_cascade, tile_h_len_cascade, tile_w_len_cascade = cascade_tiles(x, effnet_samples.clone(), tile_h, tile_w, tile_h_len, tile_w_len) + effnet_slice = tiling.get_slice(effnet_samples.clone(), tile_h_cascade, tile_h_len_cascade, tile_w_cascade, tile_w_len_cascade).to(device) + effnet_slices.append(effnet_slice) + + tile_h_cascade, tile_w_cascade, tile_h_len_cascade, tile_w_len_cascade = cascade_tiles(x, effnet_full_map.clone(), tile_h, tile_w, tile_h_len, tile_w_len) + effnet_map_slice = tiling.get_slice(effnet_full_map.clone(), tile_h_cascade, tile_h_len_cascade, tile_w_cascade, tile_w_len_cascade).to(device) + effnet_map_slices.append(effnet_map_slice) + + else: # not stage UP or stage B, default + tile_result = comfy.sample.sample_custom(model, tiled_noise, cfg, sampler, sigmas, pos, neg, tiled_latent, noise_mask=tiled_mask, callback=callback, disable_pbar=True, seed=noise_seed) + + if model.model.model_config.unet_config['stable_cascade_stage'] != 'b': + tile_result = tile_result.cpu() + if tiled_mask is not None: + tiled_mask = tiled_mask.cpu() + if tiling_strategy == "random strict": + tiling.set_slice(x_next, tile_result, tile_h, tile_h_len, tile_w, tile_w_len, tiled_mask) + else: + tiling.set_slice(x, tile_result, tile_h, tile_h_len, tile_w, tile_w_len, tiled_mask) + + + tiled_noise_list .append(tiled_noise) + tiled_latent_list.append(tiled_latent) + tiled_mask_list .append(tiled_mask) + tile_h_list .append(tile_h) + tile_w_list .append(tile_w) + tile_h_len_list .append(tile_h_len) + tile_w_len_list .append(tile_w_len) + + #END OF NON-INTERSECTING SET OF TILES + + if tiling_strategy == "random strict": # IS THIS ONE LEVEL OVER?? + x = x_next.clone() + + if model.model.model_config.unet_config['stable_cascade_stage'] == 'b': + + for start_idx in range(0, len(tiled_latent_list), max_tile_batch_size): + + end_idx = start_idx + max_tile_batch_size + + #print("Tiled batch size: ", min(max_tile_batch_size, len(tiled_latent_list))) #end_idx - start_idx) + + tiled_noise_batch = torch.cat(tiled_noise_list [start_idx:end_idx]) + tiled_latent_batch = torch.cat(tiled_latent_list[start_idx:end_idx]) + tiled_mask_batch = torch.cat(tiled_mask_list [start_idx:end_idx]) + + print("Tiled batch size: ", tiled_latent_batch.shape[0]) + + pos[0][1]['stable_cascade_prior'] = torch.cat(effnet_slices[start_idx:end_idx]) + neg[0][1]['stable_cascade_prior'] = torch.cat(effnet_slices[start_idx:end_idx]) + + tile_result = comfy.sample.sample_custom(model, tiled_noise_batch, cfg, sampler, sigmas, pos, neg, tiled_latent_batch, noise_mask=tiled_mask_batch, callback=partial(callback, step_inc=tiled_latent_batch.shape[0]), disable_pbar=True, seed=noise_seed) + + for i in range(tile_result.shape[0]): + idx = start_idx + i + + single_tile = tile_result[i].unsqueeze(dim=0) + single_mask = tiled_mask_batch[i].unsqueeze(dim=0) + + tiling.set_slice(x, single_tile, tile_h_list[idx], tile_h_len_list[idx], tile_w_list[idx], tile_w_len_list[idx], single_mask.cpu()) + + x = x.to('cpu') + + comfy.sampler_helpers.cleanup_additional_models(modelPatches) + + return x.cpu() + + + +class UltraSharkSampler_Tiled: #this is for use with https://github.com/ClownsharkBatwing/UltraCascade + @classmethod + def INPUT_TYPES(s): + return {"required": + { + "add_noise": ("BOOLEAN", {"default": True}), + "noise_is_latent": ("BOOLEAN", {"default": False}), + "noise_type": (NOISE_GENERATOR_NAMES, ), + "alpha": ("FLOAT", {"default": 1.0, "min": -10000.0, "max": 10000.0, "step":0.1, "round": 0.01}), + "k": ("FLOAT", {"default": 1.0, "min": -10000.0, "max": 10000.0, "step":2.0, "round": 0.01}), + "noise_seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}), + "cfg": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 100.0}), + "guide_type": (['residual', 'weighted'], ), + "guide_weight": ("FLOAT", {"default": 0.0, "min": -100.0, "max": 100.0, "step":0.01, "round": 0.01}), + + "tile_width": ("INT", {"default": 1024, "min": 2, "max": MAX_RESOLUTION, "step": 1}), + "tile_height": ("INT", {"default": 1024, "min": 2, "max": MAX_RESOLUTION, "step": 1}), + "tiling_strategy": (["padded", "random", "random strict", 'simple'], ), + "max_tile_batch_size": ("INT", {"default": 64, "min": 1, "max": 256, "step": 1}), + + "model": ("MODEL",), + "positive": ("CONDITIONING", ), + "negative": ("CONDITIONING", ), + "sampler": ("SAMPLER",), + "sigmas": ("SIGMAS",), + "latent_image": ("LATENT", ), + + "clip_name": (folder_paths.get_filename_list("clip_vision"), {'default': "clip-vit-large-patch14.safetensors"}), + "strength": ("FLOAT", {"default": 1.0, "min": -10.0, "max": 10.0, "step": 0.01}), + "noise_augment": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}), + + }, + "optional": { + "latent_noise": ("LATENT", ), + "guide": ("LATENT", ), + "guide_weights": ("SIGMAS",), + "image_cv": ("IMAGE",), + + }, + + } + + RETURN_TYPES = ("LATENT",) + FUNCTION = "sample" + + CATEGORY = "RES4LYF/legacy/samplers/ultracascade" + DESCRIPTION = "For use with UltraCascade." + DEPRECATED = True + + def sample(self, model, noise_seed, add_noise, noise_is_latent, noise_type, alpha, k, tile_width, tile_height, tiling_strategy, cfg, positive, negative, latent_image, latent_noise=None, sampler=None, sigmas=None, guide=None, + clip_name=None, strength=1.0, noise_augment=1.0, image_cv=None, max_tile_batch_size=3, + guide_type='residual', guide_weight=1.0, guide_weights=None, + ): + + x = latent_image["samples"].clone() + + torch.manual_seed(noise_seed) + + if not add_noise: + noise = torch.zeros(x.size(), dtype=x.dtype, layout=x.layout, device="cpu") + elif latent_noise is None: + skip = latent_image["batch_index"] if "batch_index" in latent_image else None + noise = prepare_noise(x, noise_seed, noise_type, skip, alpha, k) + else: + noise = latent_noise["samples"] + + if noise_is_latent: #add noise and latent together and normalize --> noise + noise += x.cpu() + noise.sub_(noise.mean()).div_(noise.std()) + + noise_mask = latent_image["noise_mask"].clone() if "noise_mask" in latent_image else None + + latent_out = latent_image.copy() + latent_out['samples'] = sample_common(model, x=x, noise=noise, noise_mask=noise_mask, noise_seed=noise_seed, tile_width=tile_width, tile_height=tile_height, tiling_strategy=tiling_strategy, cfg=cfg, positive=positive, negative=negative, + preview=True, sampler=sampler, sigmas=sigmas, + clip_name=clip_name, strength=strength, noise_augment=noise_augment, image_cv=image_cv, max_tile_batch_size=max_tile_batch_size, + guide=guide, guide_type=guide_type, guide_weight=guide_weight, guide_weights=guide_weights, + ) + return (latent_out,) + + diff --git a/ComfyUI/custom_nodes/RES4LYF/legacy/sigmas.py b/ComfyUI/custom_nodes/RES4LYF/legacy/sigmas.py new file mode 100644 index 0000000000000000000000000000000000000000..dfece719141480556904a84082e1048d6d282f07 --- /dev/null +++ b/ComfyUI/custom_nodes/RES4LYF/legacy/sigmas.py @@ -0,0 +1,1266 @@ +import torch + +import numpy as np +from math import * +import builtins +from scipy.interpolate import CubicSpline +import torch.nn.functional as F +import torch.nn as nn +import torch.optim as optim + +from comfy.k_diffusion.sampling import get_sigmas_polyexponential, get_sigmas_karras +import comfy.samplers + +def rescale_linear(input, input_min, input_max, output_min, output_max): + output = ((input - input_min) / (input_max - input_min)) * (output_max - output_min) + output_min; + return output + +class set_precision_sigmas: + def __init__(self): + pass + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "sigmas": ("SIGMAS", ), + "precision": (["16", "32", "64"], ), + "set_default": ("BOOLEAN", {"default": False}) + }, + } + + RETURN_TYPES = ("SIGMAS",) + RETURN_NAMES = ("passthrough",) + CATEGORY = "RES4LYF/precision" + + FUNCTION = "main" + + def main(self, precision="32", sigmas=None, set_default=False): + match precision: + case "16": + if set_default is True: + torch.set_default_dtype(torch.float16) + sigmas = sigmas.to(torch.float16) + case "32": + if set_default is True: + torch.set_default_dtype(torch.float32) + sigmas = sigmas.to(torch.float32) + case "64": + if set_default is True: + torch.set_default_dtype(torch.float64) + sigmas = sigmas.to(torch.float64) + return (sigmas, ) + + +class SimpleInterpolator(nn.Module): + def __init__(self): + super(SimpleInterpolator, self).__init__() + self.net = nn.Sequential( + nn.Linear(1, 16), + nn.ReLU(), + nn.Linear(16, 32), + nn.ReLU(), + nn.Linear(32, 1) + ) + + def forward(self, x): + return self.net(x) + +def train_interpolator(model, sigma_schedule, steps, epochs=5000, lr=0.01): + with torch.inference_mode(False): + model = SimpleInterpolator() + sigma_schedule = sigma_schedule.clone() + + criterion = nn.MSELoss() + optimizer = optim.Adam(model.parameters(), lr=lr) + + x_train = torch.linspace(0, 1, steps=steps).unsqueeze(1) + y_train = sigma_schedule.unsqueeze(1) + + # disable inference mode for training + model.train() + for epoch in range(epochs): + optimizer.zero_grad() + + # fwd pass + outputs = model(x_train) + loss = criterion(outputs, y_train) + loss.backward() + optimizer.step() + + return model + +def interpolate_sigma_schedule_model(sigma_schedule, target_steps): + model = SimpleInterpolator() + sigma_schedule = sigma_schedule.float().detach() + + # train on original sigma schedule + trained_model = train_interpolator(model, sigma_schedule, len(sigma_schedule)) + + # generate target steps for interpolation + x_interpolated = torch.linspace(0, 1, target_steps).unsqueeze(1) + + # inference w/o gradients + trained_model.eval() + with torch.no_grad(): + interpolated_sigma = trained_model(x_interpolated).squeeze() + + return interpolated_sigma + + + + +class sigmas_interpolate: + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "sigmas_0": ("SIGMAS", {"forceInput": True}), + "sigmas_1": ("SIGMAS", {"forceInput": True}), + "mode": (["linear", "nearest", "polynomial", "exponential", "power", "model"],), + "order": ("INT", {"default": 8, "min": 1,"max": 64,"step": 1}), + } + } + + FUNCTION = "main" + RETURN_TYPES = ("SIGMAS","SIGMAS",) + RETURN_NAMES = ("sigmas_0", "sigmas_1") + CATEGORY = "RES4LYF/sigmas" + + + + + def interpolate_sigma_schedule_poly(self, sigma_schedule, target_steps): + order = self.order + sigma_schedule_np = sigma_schedule.cpu().numpy() + + # orig steps (assuming even spacing) + original_steps = np.linspace(0, 1, len(sigma_schedule_np)) + + # fit polynomial of the given order + coefficients = np.polyfit(original_steps, sigma_schedule_np, deg=order) + + # generate new steps where we want to interpolate the data + target_steps_np = np.linspace(0, 1, target_steps) + + # eval polynomial at new steps + interpolated_sigma_np = np.polyval(coefficients, target_steps_np) + + interpolated_sigma = torch.tensor(interpolated_sigma_np, device=sigma_schedule.device, dtype=sigma_schedule.dtype) + return interpolated_sigma + + def interpolate_sigma_schedule_constrained(self, sigma_schedule, target_steps): + sigma_schedule_np = sigma_schedule.cpu().numpy() + + # orig steps + original_steps = np.linspace(0, 1, len(sigma_schedule_np)) + + # target steps for interpolation + target_steps_np = np.linspace(0, 1, target_steps) + + # fit cubic spline with fixed start and end values + cs = CubicSpline(original_steps, sigma_schedule_np, bc_type=((1, 0.0), (1, 0.0))) + + # eval spline at the target steps + interpolated_sigma_np = cs(target_steps_np) + + interpolated_sigma = torch.tensor(interpolated_sigma_np, device=sigma_schedule.device, dtype=sigma_schedule.dtype) + + return interpolated_sigma + + def interpolate_sigma_schedule_exp(self, sigma_schedule, target_steps): + # transform to log space + log_sigma_schedule = torch.log(sigma_schedule) + + # define the original and target step ranges + original_steps = torch.linspace(0, 1, steps=len(sigma_schedule)) + target_steps = torch.linspace(0, 1, steps=target_steps) + + # interpolate in log space + interpolated_log_sigma = F.interpolate( + log_sigma_schedule.unsqueeze(0).unsqueeze(0), # Add fake batch and channel dimensions + size=target_steps.shape[0], + mode='linear', + align_corners=True + ).squeeze() + + # transform back to exponential space + interpolated_sigma_schedule = torch.exp(interpolated_log_sigma) + + return interpolated_sigma_schedule + + def interpolate_sigma_schedule_power(self, sigma_schedule, target_steps): + sigma_schedule_np = sigma_schedule.cpu().numpy() + original_steps = np.linspace(1, len(sigma_schedule_np), len(sigma_schedule_np)) + + # power regression using a log-log transformation + log_x = np.log(original_steps) + log_y = np.log(sigma_schedule_np) + + # linear regression on log-log data + coefficients = np.polyfit(log_x, log_y, deg=1) # degree 1 for linear fit in log-log space + a = np.exp(coefficients[1]) # a = "b" = intercept (exp because of the log transform) + b = coefficients[0] # b = "m" = slope + + target_steps_np = np.linspace(1, len(sigma_schedule_np), target_steps) + + # power law prediction: y = a * x^b + interpolated_sigma_np = a * (target_steps_np ** b) + + interpolated_sigma = torch.tensor(interpolated_sigma_np, device=sigma_schedule.device, dtype=sigma_schedule.dtype) + + return interpolated_sigma + + def interpolate_sigma_schedule_linear(self, sigma_schedule, target_steps): + return F.interpolate(sigma_schedule.unsqueeze(0).unsqueeze(0), target_steps, mode='linear').squeeze(0).squeeze(0) + + def interpolate_sigma_schedule_nearest(self, sigma_schedule, target_steps): + return F.interpolate(sigma_schedule.unsqueeze(0).unsqueeze(0), target_steps, mode='nearest').squeeze(0).squeeze(0) + + def interpolate_nearest_neighbor(self, sigma_schedule, target_steps): + original_steps = torch.linspace(0, 1, steps=len(sigma_schedule)) + target_steps = torch.linspace(0, 1, steps=target_steps) + + # interpolate original -> target steps using nearest neighbor + indices = torch.searchsorted(original_steps, target_steps) + indices = torch.clamp(indices, 0, len(sigma_schedule) - 1) # clamp indices to valid range + + # set nearest neighbor via indices + interpolated_sigma = sigma_schedule[indices] + + return interpolated_sigma + + + def main(self, sigmas_0, sigmas_1, mode, order): + + self.order = order + + if mode == "linear": + interpolate = self.interpolate_sigma_schedule_linear + if mode == "nearest": + interpolate = self.interpolate_nearest_neighbor + elif mode == "polynomial": + interpolate = self.interpolate_sigma_schedule_poly + elif mode == "exponential": + interpolate = self.interpolate_sigma_schedule_exp + elif mode == "power": + interpolate = self.interpolate_sigma_schedule_power + elif mode == "model": + with torch.inference_mode(False): + interpolate = interpolate_sigma_schedule_model + + sigmas_0 = interpolate(sigmas_0, len(sigmas_1)) + return (sigmas_0, sigmas_1,) + +class sigmas_noise_inversion: + # flip sigmas for unsampling, and pad both fwd/rev directions with null bytes to disable noise scaling, etc from the model. + # will cause model to return epsilon prediction instead of calculated denoised latent image. + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "sigmas": ("SIGMAS", {"forceInput": True}), + } + } + + FUNCTION = "main" + RETURN_TYPES = ("SIGMAS","SIGMAS",) + RETURN_NAMES = ("sigmas_fwd","sigmas_rev",) + CATEGORY = "RES4LYF/sigmas" + DESCRIPTION = "For use with unsampling. Connect sigmas_fwd to the unsampling (first) node, and sigmas_rev to the sampling (second) node." + + def main(self, sigmas): + sigmas = sigmas.clone().to(torch.float64) + + null = torch.tensor([0.0], device=sigmas.device, dtype=sigmas.dtype) + sigmas_fwd = torch.flip(sigmas, dims=[0]) + sigmas_fwd = torch.cat([sigmas_fwd, null]) + + sigmas_rev = torch.cat([null, sigmas]) + sigmas_rev = torch.cat([sigmas_rev, null]) + + return (sigmas_fwd, sigmas_rev,) + + +def compute_sigma_next_variance_floor(sigma): + return (-1 + torch.sqrt(1 + 4 * sigma)) / 2 + +class sigmas_variance_floor: + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "sigmas": ("SIGMAS", {"forceInput": True}), + } + } + + FUNCTION = "main" + RETURN_TYPES = ("SIGMAS",) + CATEGORY = "RES4LYF/sigmas" + + DESCRIPTION = ("Process a sigma schedule so that any steps that are too large for variance-locked SDE sampling are replaced with the maximum permissible value." + "Will be very difficult to approach sigma = 0 due to the nature of the math, as steps become very small much below approximately sigma = 0.15 to 0.2.") + + def main(self, sigmas): + dtype = sigmas.dtype + sigmas = sigmas.clone().to(torch.float64) + for i in range(len(sigmas) - 1): + sigma_next = (-1 + torch.sqrt(1 + 4 * sigmas[i])) / 2 + + if sigmas[i+1] < sigma_next and sigmas[i+1] > 0.0: + print("swapped i+1 with sigma_next+0.001: ", sigmas[i+1], sigma_next + 0.001) + sigmas[i+1] = sigma_next + 0.001 + return (sigmas.to(dtype),) + + +class sigmas_from_text: + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "text": ("STRING", {"default": "", "multiline": True}), + } + } + + FUNCTION = "main" + RETURN_TYPES = ("SIGMAS",) + RETURN_NAMES = ("sigmas",) + CATEGORY = "RES4LYF/sigmas" + + def main(self, text): + text_list = [float(val) for val in text.replace(",", " ").split()] + #text_list = [float(val.strip()) for val in text.split(",")] + + sigmas = torch.tensor(text_list).to('cuda').to(torch.float64) + + return (sigmas,) + + + +class sigmas_concatenate: + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "sigmas_1": ("SIGMAS", {"forceInput": True}), + "sigmas_2": ("SIGMAS", {"forceInput": True}), + } + } + + FUNCTION = "main" + RETURN_TYPES = ("SIGMAS",) + CATEGORY = "RES4LYF/sigmas" + + def main(self, sigmas_1, sigmas_2): + return (torch.cat((sigmas_1, sigmas_2)),) + +class sigmas_truncate: + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "sigmas": ("SIGMAS", {"forceInput": True}), + "sigmas_until": ("INT", {"default": 10, "min": 0,"max": 1000,"step": 1}), + } + } + + FUNCTION = "main" + RETURN_TYPES = ("SIGMAS",) + CATEGORY = "RES4LYF/sigmas" + + def main(self, sigmas, sigmas_until): + return (sigmas[:sigmas_until],) + +class sigmas_start: + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "sigmas": ("SIGMAS", {"forceInput": True}), + "sigmas_until": ("INT", {"default": 10, "min": 0,"max": 1000,"step": 1}), + } + } + + FUNCTION = "main" + RETURN_TYPES = ("SIGMAS",) + CATEGORY = "RES4LYF/sigmas" + + def main(self, sigmas, sigmas_until): + return (sigmas[sigmas_until:],) + +class sigmas_split: + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "sigmas": ("SIGMAS", {"forceInput": True}), + "sigmas_start": ("INT", {"default": 0, "min": 0,"max": 1000,"step": 1}), + "sigmas_end": ("INT", {"default": 1000, "min": 0,"max": 1000,"step": 1}), + } + } + + FUNCTION = "main" + RETURN_TYPES = ("SIGMAS",) + CATEGORY = "RES4LYF/sigmas" + + def main(self, sigmas, sigmas_start, sigmas_end): + return (sigmas[sigmas_start:sigmas_end],) + + sigmas_stop_step = sigmas_end - sigmas_start + return (sigmas[sigmas_start:][:sigmas_stop_step],) + +class sigmas_pad: + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "sigmas": ("SIGMAS", {"forceInput": True}), + "value": ("FLOAT", {"default": 0.0, "min": -10000,"max": 10000,"step": 0.01}) + } + } + + FUNCTION = "main" + RETURN_TYPES = ("SIGMAS",) + CATEGORY = "RES4LYF/sigmas" + + def main(self, sigmas, value): + return (torch.cat((sigmas, torch.tensor([value], dtype=sigmas.dtype))),) + +class sigmas_unpad: + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "sigmas": ("SIGMAS", {"forceInput": True}), + } + } + + FUNCTION = "main" + RETURN_TYPES = ("SIGMAS",) + CATEGORY = "RES4LYF/sigmas" + + def main(self, sigmas): + return (sigmas[:-1],) + +class sigmas_set_floor: + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "sigmas": ("SIGMAS", {"forceInput": True}), + "floor": ("FLOAT", {"default": 0.0291675, "min": -10000,"max": 10000,"step": 0.01}), + "new_floor": ("FLOAT", {"default": 0.0291675, "min": -10000,"max": 10000,"step": 0.01}) + } + } + + RETURN_TYPES = ("SIGMAS",) + FUNCTION = "set_floor" + + CATEGORY = "RES4LYF/sigmas" + + def set_floor(self, sigmas, floor, new_floor): + sigmas[sigmas <= floor] = new_floor + return (sigmas,) + +class sigmas_delete_below_floor: + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "sigmas": ("SIGMAS", {"forceInput": True}), + "floor": ("FLOAT", {"default": 0.0291675, "min": -10000,"max": 10000,"step": 0.01}) + } + } + + RETURN_TYPES = ("SIGMAS",) + FUNCTION = "delete_below_floor" + + CATEGORY = "RES4LYF/sigmas" + + def delete_below_floor(self, sigmas, floor): + return (sigmas[sigmas >= floor],) + +class sigmas_delete_value: + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "sigmas": ("SIGMAS", {"forceInput": True}), + "value": ("FLOAT", {"default": 0.0, "min": -1000,"max": 1000,"step": 0.01}) + } + } + + RETURN_TYPES = ("SIGMAS",) + FUNCTION = "delete_value" + + CATEGORY = "RES4LYF/sigmas" + + def delete_value(self, sigmas, value): + return (sigmas[sigmas != value],) + +class sigmas_delete_consecutive_duplicates: + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "sigmas_1": ("SIGMAS", {"forceInput": True}) + } + } + + RETURN_TYPES = ("SIGMAS",) + FUNCTION = "delete_consecutive_duplicates" + + CATEGORY = "RES4LYF/sigmas" + + def delete_consecutive_duplicates(self, sigmas_1): + mask = sigmas_1[:-1] != sigmas_1[1:] + mask = torch.cat((mask, torch.tensor([True]))) + return (sigmas_1[mask],) + +class sigmas_cleanup: + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "sigmas": ("SIGMAS", {"forceInput": True}), + "sigmin": ("FLOAT", {"default": 0.0291675, "min": 0,"max": 1000,"step": 0.01}) + } + } + + RETURN_TYPES = ("SIGMAS",) + FUNCTION = "cleanup" + + CATEGORY = "RES4LYF/sigmas" + + def cleanup(self, sigmas, sigmin): + sigmas_culled = sigmas[sigmas >= sigmin] + + mask = sigmas_culled[:-1] != sigmas_culled[1:] + mask = torch.cat((mask, torch.tensor([True]))) + filtered_sigmas = sigmas_culled[mask] + return (torch.cat((filtered_sigmas,torch.tensor([0]))),) + +class sigmas_mult: + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "sigmas": ("SIGMAS", {"forceInput": True}), + "multiplier": ("FLOAT", {"default": 1, "min": -10000,"max": 10000,"step": 0.01}) + }, + "optional": { + "sigmas2": ("SIGMAS", {"forceInput": False}) + } + } + + FUNCTION = "main" + RETURN_TYPES = ("SIGMAS",) + CATEGORY = "RES4LYF/sigmas" + + def main(self, sigmas, multiplier, sigmas2=None): + if sigmas2 is not None: + return (sigmas * sigmas2 * multiplier,) + else: + return (sigmas * multiplier,) + +class sigmas_modulus: + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "sigmas": ("SIGMAS", {"forceInput": True}), + "divisor": ("FLOAT", {"default": 1, "min": -1000,"max": 1000,"step": 0.01}) + } + } + + FUNCTION = "main" + RETURN_TYPES = ("SIGMAS",) + CATEGORY = "RES4LYF/sigmas" + + def main(self, sigmas, divisor): + return (sigmas % divisor,) + +class sigmas_quotient: + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "sigmas": ("SIGMAS", {"forceInput": True}), + "divisor": ("FLOAT", {"default": 1, "min": -1000,"max": 1000,"step": 0.01}) + } + } + + FUNCTION = "main" + RETURN_TYPES = ("SIGMAS",) + CATEGORY = "RES4LYF/sigmas" + + def main(self, sigmas, divisor): + return (sigmas // divisor,) + +class sigmas_add: + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "sigmas": ("SIGMAS", {"forceInput": True}), + "addend": ("FLOAT", {"default": 1, "min": -1000,"max": 1000,"step": 0.01}) + } + } + + FUNCTION = "main" + RETURN_TYPES = ("SIGMAS",) + CATEGORY = "RES4LYF/sigmas" + + def main(self, sigmas, addend): + return (sigmas + addend,) + +class sigmas_power: + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "sigmas": ("SIGMAS", {"forceInput": True}), + "power": ("FLOAT", {"default": 1, "min": -100,"max": 100,"step": 0.01}) + } + } + + FUNCTION = "main" + RETURN_TYPES = ("SIGMAS",) + CATEGORY = "RES4LYF/sigmas" + + def main(self, sigmas, power): + return (sigmas ** power,) + +class sigmas_abs: + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "sigmas": ("SIGMAS", {"forceInput": True}) + } + } + + FUNCTION = "main" + RETURN_TYPES = ("SIGMAS",) + CATEGORY = "RES4LYF/sigmas" + + def main(self, sigmas): + return (abs(sigmas),) + +class sigmas2_mult: + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "sigmas_1": ("SIGMAS", {"forceInput": True}), + "sigmas_2": ("SIGMAS", {"forceInput": True}), + } + } + + FUNCTION = "main" + RETURN_TYPES = ("SIGMAS",) + CATEGORY = "RES4LYF/sigmas" + + def main(self, sigmas_1, sigmas_2): + return (sigmas_1 * sigmas_2,) + +class sigmas2_add: + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "sigmas_1": ("SIGMAS", {"forceInput": True}), + "sigmas_2": ("SIGMAS", {"forceInput": True}), + } + } + + FUNCTION = "main" + RETURN_TYPES = ("SIGMAS",) + CATEGORY = "RES4LYF/sigmas" + + def main(self, sigmas_1, sigmas_2): + return (sigmas_1 + sigmas_2,) + +class sigmas_rescale: + def __init__(self): + pass + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "start": ("FLOAT", {"default": 1.0, "min": -10000,"max": 10000,"step": 0.01}), + "end": ("FLOAT", {"default": 0.0, "min": -10000,"max": 10000,"step": 0.01}), + "sigmas": ("SIGMAS", ), + }, + "optional": { + } + } + FUNCTION = "main" + RETURN_TYPES = ("SIGMAS",) + RETURN_NAMES = ("sigmas_rescaled",) + CATEGORY = "RES4LYF/sigmas" + DESCRIPTION = ("Can be used to set denoise. Results are generally better than with the approach used by KSampler and most nodes with denoise values " + "(which slice the sigmas schedule according to step count, not the noise level). Will also flip the sigma schedule if the start and end values are reversed." + ) + + def main(self, start=0, end=-1, sigmas=None): + + s_out_1 = ((sigmas - sigmas.min()) * (start - end)) / (sigmas.max() - sigmas.min()) + end + + return (s_out_1,) + + +class sigmas_math1: + def __init__(self): + pass + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "start": ("INT", {"default": 0, "min": 0,"max": 10000,"step": 1}), + "stop": ("INT", {"default": 0, "min": 0,"max": 10000,"step": 1}), + "trim": ("INT", {"default": 0, "min": -10000,"max": 0,"step": 1}), + "x": ("FLOAT", {"default": 1, "min": -10000,"max": 10000,"step": 0.01}), + "y": ("FLOAT", {"default": 1, "min": -10000,"max": 10000,"step": 0.01}), + "z": ("FLOAT", {"default": 1, "min": -10000,"max": 10000,"step": 0.01}), + "f1": ("STRING", {"default": "s", "multiline": True}), + "rescale" : ("BOOLEAN", {"default": False}), + "max1": ("FLOAT", {"default": 14.614642, "min": -10000,"max": 10000,"step": 0.01}), + "min1": ("FLOAT", {"default": 0.0291675, "min": -10000,"max": 10000,"step": 0.01}), + }, + "optional": { + "a": ("SIGMAS", {"forceInput": False}), + "b": ("SIGMAS", {"forceInput": False}), + "c": ("SIGMAS", {"forceInput": False}), + } + } + FUNCTION = "main" + RETURN_TYPES = ("SIGMAS",) + CATEGORY = "RES4LYF/sigmas" + def main(self, start=0, stop=0, trim=0, a=None, b=None, c=None, x=1.0, y=1.0, z=1.0, f1="s", rescale=False, min1=1.0, max1=1.0): + if stop == 0: + t_lens = [len(tensor) for tensor in [a, b, c] if tensor is not None] + t_len = stop = min(t_lens) if t_lens else 0 + else: + stop = stop + 1 + t_len = stop - start + + stop = stop + trim + t_len = t_len + trim + + t_a = t_b = t_c = None + if a is not None: + t_a = a[start:stop] + if b is not None: + t_b = b[start:stop] + if c is not None: + t_c = c[start:stop] + + t_s = torch.arange(0.0, t_len) + + t_x = torch.full((t_len,), x) + t_y = torch.full((t_len,), y) + t_z = torch.full((t_len,), z) + eval_namespace = {"__builtins__": None, "round": builtins.round, "np": np, "a": t_a, "b": t_b, "c": t_c, "x": t_x, "y": t_y, "z": t_z, "s": t_s, "torch": torch} + eval_namespace.update(np.__dict__) + + s_out_1 = eval(f1, eval_namespace) + + if rescale == True: + s_out_1 = ((s_out_1 - min(s_out_1)) * (max1 - min1)) / (max(s_out_1) - min(s_out_1)) + min1 + + return (s_out_1,) + +class sigmas_math3: + def __init__(self): + pass + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "start": ("INT", {"default": 0, "min": 0,"max": 10000,"step": 1}), + "stop": ("INT", {"default": 0, "min": 0,"max": 10000,"step": 1}), + "trim": ("INT", {"default": 0, "min": -10000,"max": 0,"step": 1}), + }, + "optional": { + "a": ("SIGMAS", {"forceInput": False}), + "b": ("SIGMAS", {"forceInput": False}), + "c": ("SIGMAS", {"forceInput": False}), + "x": ("FLOAT", {"default": 1, "min": -10000,"max": 10000,"step": 0.01}), + "y": ("FLOAT", {"default": 1, "min": -10000,"max": 10000,"step": 0.01}), + "z": ("FLOAT", {"default": 1, "min": -10000,"max": 10000,"step": 0.01}), + "f1": ("STRING", {"default": "s", "multiline": True}), + "rescale1" : ("BOOLEAN", {"default": False}), + "max1": ("FLOAT", {"default": 14.614642, "min": -10000,"max": 10000,"step": 0.01}), + "min1": ("FLOAT", {"default": 0.0291675, "min": -10000,"max": 10000,"step": 0.01}), + "f2": ("STRING", {"default": "s", "multiline": True}), + "rescale2" : ("BOOLEAN", {"default": False}), + "max2": ("FLOAT", {"default": 14.614642, "min": -10000,"max": 10000,"step": 0.01}), + "min2": ("FLOAT", {"default": 0.0291675, "min": -10000,"max": 10000,"step": 0.01}), + "f3": ("STRING", {"default": "s", "multiline": True}), + "rescale3" : ("BOOLEAN", {"default": False}), + "max3": ("FLOAT", {"default": 14.614642, "min": -10000,"max": 10000,"step": 0.01}), + "min3": ("FLOAT", {"default": 0.0291675, "min": -10000,"max": 10000,"step": 0.01}), + } + } + FUNCTION = "main" + RETURN_TYPES = ("SIGMAS","SIGMAS","SIGMAS") + CATEGORY = "RES4LYF/sigmas" + def main(self, start=0, stop=0, trim=0, a=None, b=None, c=None, x=1.0, y=1.0, z=1.0, f1="s", f2="s", f3="s", rescale1=False, rescale2=False, rescale3=False, min1=1.0, max1=1.0, min2=1.0, max2=1.0, min3=1.0, max3=1.0): + if stop == 0: + t_lens = [len(tensor) for tensor in [a, b, c] if tensor is not None] + t_len = stop = min(t_lens) if t_lens else 0 + else: + stop = stop + 1 + t_len = stop - start + + stop = stop + trim + t_len = t_len + trim + + t_a = t_b = t_c = None + if a is not None: + t_a = a[start:stop] + if b is not None: + t_b = b[start:stop] + if c is not None: + t_c = c[start:stop] + + t_s = torch.arange(0.0, t_len) + + t_x = torch.full((t_len,), x) + t_y = torch.full((t_len,), y) + t_z = torch.full((t_len,), z) + eval_namespace = {"__builtins__": None, "np": np, "a": t_a, "b": t_b, "c": t_c, "x": t_x, "y": t_y, "z": t_z, "s": t_s, "torch": torch} + eval_namespace.update(np.__dict__) + + s_out_1 = eval(f1, eval_namespace) + s_out_2 = eval(f2, eval_namespace) + s_out_3 = eval(f3, eval_namespace) + + if rescale1 == True: + s_out_1 = ((s_out_1 - min(s_out_1)) * (max1 - min1)) / (max(s_out_1) - min(s_out_1)) + min1 + if rescale2 == True: + s_out_2 = ((s_out_2 - min(s_out_2)) * (max2 - min2)) / (max(s_out_2) - min(s_out_2)) + min2 + if rescale3 == True: + s_out_3 = ((s_out_3 - min(s_out_3)) * (max3 - min3)) / (max(s_out_3) - min(s_out_3)) + min3 + + return s_out_1, s_out_2, s_out_3 + +class sigmas_iteration_karras: + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "steps_up": ("INT", {"default": 30, "min": 0,"max": 10000,"step": 1}), + "steps_down": ("INT", {"default": 30, "min": 0,"max": 10000,"step": 1}), + "rho_up": ("FLOAT", {"default": 3, "min": -10000,"max": 10000,"step": 0.01}), + "rho_down": ("FLOAT", {"default": 4, "min": -10000,"max": 10000,"step": 0.01}), + "s_min_start": ("FLOAT", {"default":0.0291675, "min": -10000,"max": 10000,"step": 0.01}), + "s_max": ("FLOAT", {"default": 2, "min": -10000,"max": 10000,"step": 0.01}), + "s_min_end": ("FLOAT", {"default": 0.0291675, "min": -10000,"max": 10000,"step": 0.01}), + }, + "optional": { + "momentums": ("SIGMAS", {"forceInput": False}), + "sigmas": ("SIGMAS", {"forceInput": False}), + } + } + + FUNCTION = "main" + RETURN_TYPES = ("SIGMAS","SIGMAS") + RETURN_NAMES = ("momentums","sigmas") + CATEGORY = "RES4LYF/schedulers" + + def main(self, steps_up, steps_down, rho_up, rho_down, s_min_start, s_max, s_min_end, sigmas=None, momentums=None): + s_up = get_sigmas_karras(steps_up, s_min_start, s_max, rho_up) + s_down = get_sigmas_karras(steps_down, s_min_end, s_max, rho_down) + s_up = s_up[:-1] + s_down = s_down[:-1] + s_up = torch.flip(s_up, dims=[0]) + sigmas_new = torch.cat((s_up, s_down), dim=0) + momentums_new = torch.cat((s_up, -1*s_down), dim=0) + + if sigmas is not None: + sigmas = torch.cat([sigmas, sigmas_new]) + else: + sigmas = sigmas_new + + if momentums is not None: + momentums = torch.cat([momentums, momentums_new]) + else: + momentums = momentums_new + + return (momentums,sigmas) + +class sigmas_iteration_polyexp: + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "steps_up": ("INT", {"default": 30, "min": 0,"max": 10000,"step": 1}), + "steps_down": ("INT", {"default": 30, "min": 0,"max": 10000,"step": 1}), + "rho_up": ("FLOAT", {"default": 0.6, "min": -10000,"max": 10000,"step": 0.01}), + "rho_down": ("FLOAT", {"default": 0.8, "min": -10000,"max": 10000,"step": 0.01}), + "s_min_start": ("FLOAT", {"default":0.0291675, "min": -10000,"max": 10000,"step": 0.01}), + "s_max": ("FLOAT", {"default": 2, "min": -10000,"max": 10000,"step": 0.01}), + "s_min_end": ("FLOAT", {"default": 0.0291675, "min": -10000,"max": 10000,"step": 0.01}), + }, + "optional": { + "momentums": ("SIGMAS", {"forceInput": False}), + "sigmas": ("SIGMAS", {"forceInput": False}), + } + } + + FUNCTION = "main" + RETURN_TYPES = ("SIGMAS","SIGMAS") + RETURN_NAMES = ("momentums","sigmas") + CATEGORY = "RES4LYF/schedulers" + + def main(self, steps_up, steps_down, rho_up, rho_down, s_min_start, s_max, s_min_end, sigmas=None, momentums=None): + s_up = get_sigmas_polyexponential(steps_up, s_min_start, s_max, rho_up) + s_down = get_sigmas_polyexponential(steps_down, s_min_end, s_max, rho_down) + s_up = s_up[:-1] + s_down = s_down[:-1] + s_up = torch.flip(s_up, dims=[0]) + sigmas_new = torch.cat((s_up, s_down), dim=0) + momentums_new = torch.cat((s_up, -1*s_down), dim=0) + + if sigmas is not None: + sigmas = torch.cat([sigmas, sigmas_new]) + else: + sigmas = sigmas_new + + if momentums is not None: + momentums = torch.cat([momentums, momentums_new]) + else: + momentums = momentums_new + + return (momentums,sigmas) + +class tan_scheduler: + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "steps": ("INT", {"default": 20, "min": 0,"max": 100000,"step": 1}), + "offset": ("FLOAT", {"default": 20, "min": 0,"max": 100000,"step": 0.1}), + "slope": ("FLOAT", {"default": 20, "min": -100000,"max": 100000,"step": 0.1}), + "start": ("FLOAT", {"default": 20, "min": -100000,"max": 100000,"step": 0.1}), + "end": ("FLOAT", {"default": 20, "min": -100000,"max": 100000,"step": 0.1}), + "sgm" : ("BOOLEAN", {"default": False}), + "pad" : ("BOOLEAN", {"default": False}), + } + } + + FUNCTION = "main" + RETURN_TYPES = ("SIGMAS",) + CATEGORY = "RES4LYF/schedulers" + + def main(self, steps, slope, offset, start, end, sgm, pad): + smax = ((2/pi)*atan(-slope*(0-offset))+1)/2 + smin = ((2/pi)*atan(-slope*((steps-1)-offset))+1)/2 + + srange = smax-smin + sscale = start - end + + if sgm: + steps+=1 + + sigmas = [ ( (((2/pi)*atan(-slope*(x-offset))+1)/2) - smin) * (1/srange) * sscale + end for x in range(steps)] + + if sgm: + sigmas = sigmas[:-1] + if pad: + sigmas = torch.tensor(sigmas+[0]) + else: + sigmas = torch.tensor(sigmas) + return (sigmas,) + +class tan_scheduler_2stage: + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "steps": ("INT", {"default": 40, "min": 0,"max": 100000,"step": 1}), + "midpoint": ("INT", {"default": 20, "min": 0,"max": 100000,"step": 1}), + "pivot_1": ("INT", {"default": 10, "min": 0,"max": 100000,"step": 1}), + "pivot_2": ("INT", {"default": 30, "min": 0,"max": 100000,"step": 1}), + "slope_1": ("FLOAT", {"default": 1, "min": -100000,"max": 100000,"step": 0.1}), + "slope_2": ("FLOAT", {"default": 1, "min": -100000,"max": 100000,"step": 0.1}), + "start": ("FLOAT", {"default": 1.0, "min": -100000,"max": 100000,"step": 0.1}), + "middle": ("FLOAT", {"default": 0.5, "min": -100000,"max": 100000,"step": 0.1}), + "end": ("FLOAT", {"default": 0.0, "min": -100000,"max": 100000,"step": 0.1}), + "pad" : ("BOOLEAN", {"default": False}), + } + } + + FUNCTION = "main" + RETURN_TYPES = ("SIGMAS",) + RETURN_NAMES = ("sigmas",) + CATEGORY = "RES4LYF/schedulers" + + def get_tan_sigmas(self, steps, slope, pivot, start, end): + smax = ((2/pi)*atan(-slope*(0-pivot))+1)/2 + smin = ((2/pi)*atan(-slope*((steps-1)-pivot))+1)/2 + + srange = smax-smin + sscale = start - end + + sigmas = [ ( (((2/pi)*atan(-slope*(x-pivot))+1)/2) - smin) * (1/srange) * sscale + end for x in range(steps)] + + return sigmas + + def main(self, steps, midpoint, start, middle, end, pivot_1, pivot_2, slope_1, slope_2, pad): + steps += 2 + stage_2_len = steps - midpoint + stage_1_len = steps - stage_2_len + + tan_sigmas_1 = self.get_tan_sigmas(stage_1_len, slope_1, pivot_1, start, middle) + tan_sigmas_2 = self.get_tan_sigmas(stage_2_len, slope_2, pivot_2 - stage_1_len, middle, end) + + tan_sigmas_1 = tan_sigmas_1[:-1] + if pad: + tan_sigmas_2 = tan_sigmas_2+[0] + + tan_sigmas = torch.tensor(tan_sigmas_1 + tan_sigmas_2) + + return (tan_sigmas,) + +class tan_scheduler_2stage_simple: + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "steps": ("INT", {"default": 40, "min": 0,"max": 100000,"step": 1}), + "pivot_1": ("FLOAT", {"default": 1, "min": -100000,"max": 100000,"step": 0.01}), + "pivot_2": ("FLOAT", {"default": 1, "min": -100000,"max": 100000,"step": 0.01}), + "slope_1": ("FLOAT", {"default": 1, "min": -100000,"max": 100000,"step": 0.01}), + "slope_2": ("FLOAT", {"default": 1, "min": -100000,"max": 100000,"step": 0.01}), + "start": ("FLOAT", {"default": 1.0, "min": -100000,"max": 100000,"step": 0.01}), + "middle": ("FLOAT", {"default": 0.5, "min": -100000,"max": 100000,"step": 0.01}), + "end": ("FLOAT", {"default": 0.0, "min": -100000,"max": 100000,"step": 0.01}), + "pad" : ("BOOLEAN", {"default": False}), + } + } + + FUNCTION = "main" + RETURN_TYPES = ("SIGMAS",) + RETURN_NAMES = ("sigmas",) + CATEGORY = "RES4LYF/schedulers" + + def get_tan_sigmas(self, steps, slope, pivot, start, end): + smax = ((2/pi)*atan(-slope*(0-pivot))+1)/2 + smin = ((2/pi)*atan(-slope*((steps-1)-pivot))+1)/2 + + srange = smax-smin + sscale = start - end + + sigmas = [ ( (((2/pi)*atan(-slope*(x-pivot))+1)/2) - smin) * (1/srange) * sscale + end for x in range(steps)] + + return sigmas + + def main(self, steps, start, middle, end, pivot_1, pivot_2, slope_1, slope_2, pad): + steps += 2 + + midpoint = int( (steps*pivot_1 + steps*pivot_2) / 2 ) + pivot_1 = int(steps * pivot_1) + pivot_2 = int(steps * pivot_2) + + slope_1 = slope_1 / (steps/40) + slope_2 = slope_2 / (steps/40) + + stage_2_len = steps - midpoint + stage_1_len = steps - stage_2_len + + tan_sigmas_1 = self.get_tan_sigmas(stage_1_len, slope_1, pivot_1, start, middle) + tan_sigmas_2 = self.get_tan_sigmas(stage_2_len, slope_2, pivot_2 - stage_1_len, middle, end) + + tan_sigmas_1 = tan_sigmas_1[:-1] + if pad: + tan_sigmas_2 = tan_sigmas_2+[0] + + tan_sigmas = torch.tensor(tan_sigmas_1 + tan_sigmas_2) + + return (tan_sigmas,) + +class linear_quadratic_advanced: + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "model": ("MODEL",), + "steps": ("INT", {"default": 40, "min": 0,"max": 100000,"step": 1}), + "denoise": ("FLOAT", {"default": 1.0, "min": -100000,"max": 100000,"step": 0.01}), + "inflection_percent": ("FLOAT", {"default": 0.5, "min": 0,"max": 1,"step": 0.01}), + }, + # "optional": { + # } + } + + FUNCTION = "main" + RETURN_TYPES = ("SIGMAS",) + RETURN_NAMES = ("sigmas",) + CATEGORY = "RES4LYF/schedulers" + + def main(self, steps, denoise, inflection_percent, model=None): + sigmas = get_sigmas(model, "linear_quadratic", steps, denoise, inflection_percent) + + return (sigmas, ) + + +class constant_scheduler: + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "steps": ("INT", {"default": 40, "min": 0,"max": 100000,"step": 1}), + "value_start": ("FLOAT", {"default": 1.0, "min": -100000,"max": 100000,"step": 0.01}), + "value_end": ("FLOAT", {"default": 0.0, "min": -100000,"max": 100000,"step": 0.01}), + "cutoff_percent": ("FLOAT", {"default": 1.0, "min": 0,"max": 1,"step": 0.01}), + } + } + + FUNCTION = "main" + RETURN_TYPES = ("SIGMAS",) + RETURN_NAMES = ("sigmas",) + CATEGORY = "RES4LYF/schedulers" + + def main(self, steps, value_start, value_end, cutoff_percent): + sigmas = torch.ones(steps + 1) * value_start + cutoff_step = int(round(steps * cutoff_percent)) + 1 + sigmas = torch.concat((sigmas[:cutoff_step], torch.ones(steps + 1 - cutoff_step) * value_end), dim=0) + + return (sigmas,) + + +def get_sigmas_simple_exponential(model, steps): + s = model.model_sampling + sigs = [] + ss = len(s.sigmas) / steps + for x in range(steps): + sigs += [float(s.sigmas[-(1 + int(x * ss))])] + sigs += [0.0] + sigs = torch.FloatTensor(sigs) + exp = torch.exp(torch.log(torch.linspace(1, 0, steps + 1))) + return sigs * exp + +extra_schedulers = { + "simple_exponential": get_sigmas_simple_exponential +} + + + + +def get_sigmas(model, scheduler, steps, denoise, lq_inflection_percent=0.5): #adapted from comfyui + total_steps = steps + if denoise < 1.0: + if denoise <= 0.0: + return (torch.FloatTensor([]),) + total_steps = int(steps/denoise) + + #model_sampling = model.get_model_object("model_sampling") + if hasattr(model, "model"): + model_sampling = model.model.model_sampling + elif hasattr(model, "inner_model"): + model_sampling = model.inner_model.inner_model.model_sampling + if scheduler == "beta57": + sigmas = comfy.samplers.beta_scheduler(model_sampling, total_steps, alpha=0.5, beta=0.7) + elif scheduler == "linear_quadratic": + linear_steps = int(total_steps * lq_inflection_percent) + sigmas = comfy.samplers.linear_quadratic_schedule(model_sampling, total_steps, threshold_noise=0.025, linear_steps=linear_steps) + else: + sigmas = comfy.samplers.calculate_sigmas(model_sampling, scheduler, total_steps).cpu() + + sigmas = sigmas[-(steps + 1):] + return sigmas + + + diff --git a/ComfyUI/custom_nodes/RES4LYF/legacy/tiling.py b/ComfyUI/custom_nodes/RES4LYF/legacy/tiling.py new file mode 100644 index 0000000000000000000000000000000000000000..207dac3828f64fe0f086aa9e9b1b1dce100916dd --- /dev/null +++ b/ComfyUI/custom_nodes/RES4LYF/legacy/tiling.py @@ -0,0 +1,180 @@ +import torch +import itertools +import numpy as np + +# tiled sampler code adapted from https://github.com/BlenderNeko/ComfyUI_TiledKSampler +# for use with https://github.com/ClownsharkBatwing/UltraCascade + +def grouper(n, iterable): + it = iter(iterable) + while True: + chunk = list(itertools.islice(it, n)) + if not chunk: + return + yield chunk + +def create_batches(n, iterable): + groups = itertools.groupby(iterable, key= lambda x: (x[1], x[3])) + for _, x in groups: + for y in grouper(n, x): + yield y + + +def get_slice(tensor, h, h_len, w, w_len): + t = tensor.narrow(-2, h, h_len) + t = t.narrow(-1, w, w_len) + return t + +def set_slice(tensor1,tensor2, h, h_len, w, w_len, mask=None): + if mask is not None: + tensor1[:,:,h:h+h_len,w:w+w_len] = tensor1[:,:,h:h+h_len,w:w+w_len] * (1 - mask) + tensor2 * mask + else: + tensor1[:,:,h:h+h_len,w:w+w_len] = tensor2 + +def get_tiles_and_masks_simple(steps, latent_shape, tile_height, tile_width, compression=4): + latent_size_h = latent_shape[-2] + latent_size_w = latent_shape[-1] + tile_size_h = int(tile_height // compression) #CHANGED FROM 8 + tile_size_w = int(tile_width // compression) #CHANGED FROM 8 + + h = np.arange(0,latent_size_h, tile_size_h) + w = np.arange(0,latent_size_w, tile_size_w) + + def create_tile(hs, ws, i, j): + h = int(hs[i]) + w = int(ws[j]) + h_len = min(tile_size_h, latent_size_h - h) + w_len = min(tile_size_w, latent_size_w - w) + return (h, h_len, w, w_len, steps, None) + + passes = [ + [[create_tile(h, w, i, j) for i in range(len(h)) for j in range(len(w))]], + ] + return passes + +def get_tiles_and_masks_padded(steps, latent_shape, tile_height, tile_width, compression=4): + batch_size = latent_shape[0] + latent_size_h = latent_shape[-2] + latent_size_w = latent_shape[-1] + + tile_size_h = int(tile_height // compression) #CHANGED FROM 8 + tile_size_w = int(tile_width // compression) #CHANGED FROM 8 + #if compression > 1: + tile_size_h = int((tile_size_h // 4) * 4) #MIGHT BE A PROBLEM WITH STAGE C? + tile_size_w = int((tile_size_w // 4) * 4) + + #masks + mask_h = [0,tile_size_h // 4, tile_size_h - tile_size_h // 4, tile_size_h] + mask_w = [0,tile_size_w // 4, tile_size_w - tile_size_w // 4, tile_size_w] + masks = [[] for _ in range(3)] + for i in range(3): + for j in range(3): + mask = torch.zeros((batch_size,1,tile_size_h, tile_size_w), dtype=torch.float32, device='cpu') + mask[:,:, mask_h[i]:mask_h[i+1], + mask_w[j]:mask_w[j+1]] = 1.0 + masks[i].append(mask) + + def create_mask(h_ind, w_ind, h_ind_max, w_ind_max, mask_h, mask_w, h_len, w_len): + mask = masks[1][1] + if not (h_ind == 0 or h_ind == h_ind_max or w_ind == 0 or w_ind == w_ind_max): + return get_slice(mask, 0, h_len, 0, w_len) + mask = mask.clone() + if h_ind == 0 and mask_h: + mask += masks[0][1] + if h_ind == h_ind_max and mask_h: + mask += masks[2][1] + if w_ind == 0 and mask_w: + mask += masks[1][0] + if w_ind == w_ind_max and mask_w: + mask += masks[1][2] + if h_ind == 0 and w_ind == 0 and mask_h and mask_w: + mask += masks[0][0] + if h_ind == 0 and w_ind == w_ind_max and mask_h and mask_w: + mask += masks[0][2] + if h_ind == h_ind_max and w_ind == 0 and mask_h and mask_w: + mask += masks[2][0] + if h_ind == h_ind_max and w_ind == w_ind_max and mask_h and mask_w: + mask += masks[2][2] + return get_slice(mask, 0, h_len, 0, w_len) + + h = np.arange(0,latent_size_h, tile_size_h) + h_shift = np.arange(tile_size_h // 2, latent_size_h - tile_size_h // 2, tile_size_h) + w = np.arange(0,latent_size_w, tile_size_w) + w_shift = np.arange(tile_size_w // 2, latent_size_w - tile_size_h // 2, tile_size_w) + + + def create_tile(hs, ws, mask_h, mask_w, i, j): + h = int(hs[i]) + w = int(ws[j]) + h_len = min(tile_size_h, latent_size_h - h) + w_len = min(tile_size_w, latent_size_w - w) + mask = create_mask(i,j,len(hs)-1, len(ws)-1, mask_h, mask_w, h_len, w_len) + return (h, h_len, w, w_len, steps, mask) + + passes = [ + [[create_tile(h, w, True, True, i, j) for i in range(len(h)) for j in range(len(w))]], + [[create_tile(h_shift, w, False, True, i, j) for i in range(len(h_shift)) for j in range(len(w))]], + [[create_tile(h, w_shift, True, False, i, j) for i in range(len(h)) for j in range(len(w_shift))]], + [[create_tile(h_shift, w_shift, False, False, i,j) for i in range(len(h_shift)) for j in range(len(w_shift))]], + ] + + return passes + +def mask_at_boundary(h, h_len, w, w_len, tile_size_h, tile_size_w, latent_size_h, latent_size_w, mask, device='cpu', compression=4): + tile_size_h = int(tile_size_h // compression) #CHANGED FROM 8 + tile_size_w = int(tile_size_w // compression) #CHANGED FROM 8 + + if (h_len == tile_size_h or h_len == latent_size_h) and (w_len == tile_size_w or w_len == latent_size_w): + return h, h_len, w, w_len, mask + h_offset = min(0, latent_size_h - (h + tile_size_h)) + w_offset = min(0, latent_size_w - (w + tile_size_w)) + new_mask = torch.zeros((1,1,tile_size_h, tile_size_w), dtype=torch.float32, device=device) + new_mask[:,:,-h_offset:h_len if h_offset == 0 else tile_size_h, -w_offset:w_len if w_offset == 0 else tile_size_w] = 1.0 if mask is None else mask + return h + h_offset, tile_size_h, w + w_offset, tile_size_w, new_mask + +def get_tiles_and_masks_rgrid(steps, latent_shape, tile_height, tile_width, generator, compression=4): + + def calc_coords(latent_size, tile_size, jitter): + tile_coords = int((latent_size + jitter - 1) // tile_size + 1) + tile_coords = [np.clip(tile_size * c - jitter, 0, latent_size) for c in range(tile_coords + 1)] + tile_coords = [(c1, c2-c1) for c1, c2 in zip(tile_coords, tile_coords[1:])] + return tile_coords + + #calc stuff + batch_size = latent_shape[0] + latent_size_h = latent_shape[-2] + latent_size_w = latent_shape[-1] + tile_size_h = int(tile_height // compression) #CHANGED FROM 8 + tile_size_w = int(tile_width // compression) #CHANGED FROM 8 + + tiles_all = [] + + for s in range(steps): + rands = torch.rand((2,), dtype=torch.float32, generator=generator, device='cpu').numpy() + + jitter_w1 = int(rands[0] * tile_size_w) + jitter_w2 = int(((rands[0] + .5) % 1.0) * tile_size_w) + jitter_h1 = int(rands[1] * tile_size_h) + jitter_h2 = int(((rands[1] + .5) % 1.0) * tile_size_h) + + #calc number of tiles + tiles_h = [ + calc_coords(latent_size_h, tile_size_h, jitter_h1), + calc_coords(latent_size_h, tile_size_h, jitter_h2) + ] + tiles_w = [ + calc_coords(latent_size_w, tile_size_w, jitter_w1), + calc_coords(latent_size_w, tile_size_w, jitter_w2) + ] + + tiles = [] + if s % 2 == 0: + for i, h in enumerate(tiles_h[0]): + for w in tiles_w[i%2]: + tiles.append((int(h[0]), int(h[1]), int(w[0]), int(w[1]), 1, None)) + else: + for i, w in enumerate(tiles_w[0]): + for h in tiles_h[i%2]: + tiles.append((int(h[0]), int(h[1]), int(w[0]), int(w[1]), 1, None)) + tiles_all.append(tiles) + return [tiles_all] diff --git a/ComfyUI/custom_nodes/RES4LYF/lightricks/model.py b/ComfyUI/custom_nodes/RES4LYF/lightricks/model.py new file mode 100644 index 0000000000000000000000000000000000000000..8ac8c02738c7e46ff402125db2d7b2d1ab25dd36 --- /dev/null +++ b/ComfyUI/custom_nodes/RES4LYF/lightricks/model.py @@ -0,0 +1,734 @@ +import torch +from torch import nn +import torch.nn.functional as F + +import comfy.ldm.modules.attention +import comfy.ldm.common_dit +from einops import rearrange +import math +from typing import Dict, Optional, Tuple, List + +from .symmetric_patchifier import SymmetricPatchifier, latent_to_pixel_coords +from ..helper import ExtraOptions + + +def get_timestep_embedding( + timesteps: torch.Tensor, + embedding_dim: int, + flip_sin_to_cos: bool = False, + downscale_freq_shift: float = 1, + scale: float = 1, + max_period: int = 10000, +): + """ + This matches the implementation in Denoising Diffusion Probabilistic Models: Create sinusoidal timestep embeddings. + + Args + timesteps (torch.Tensor): + a 1-D Tensor of N indices, one per batch element. These may be fractional. + embedding_dim (int): + the dimension of the output. + flip_sin_to_cos (bool): + Whether the embedding order should be `cos, sin` (if True) or `sin, cos` (if False) + downscale_freq_shift (float): + Controls the delta between frequencies between dimensions + scale (float): + Scaling factor applied to the embeddings. + max_period (int): + Controls the maximum frequency of the embeddings + Returns + torch.Tensor: an [N x dim] Tensor of positional embeddings. + """ + assert len(timesteps.shape) == 1, "Timesteps should be a 1d-array" + + half_dim = embedding_dim // 2 + exponent = -math.log(max_period) * torch.arange( + start=0, end=half_dim, dtype=torch.float32, device=timesteps.device + ) + exponent = exponent / (half_dim - downscale_freq_shift) + + emb = torch.exp(exponent) + emb = timesteps[:, None].float() * emb[None, :] + + # scale embeddings + emb = scale * emb + + # concat sine and cosine embeddings + emb = torch.cat([torch.sin(emb), torch.cos(emb)], dim=-1) + + # flip sine and cosine embeddings + if flip_sin_to_cos: + emb = torch.cat([emb[:, half_dim:], emb[:, :half_dim]], dim=-1) + + # zero pad + if embedding_dim % 2 == 1: + emb = torch.nn.functional.pad(emb, (0, 1, 0, 0)) + return emb + + +class TimestepEmbedding(nn.Module): + def __init__( + self, + in_channels: int, + time_embed_dim: int, + act_fn: str = "silu", + out_dim: int = None, + post_act_fn: Optional[str] = None, + cond_proj_dim=None, + sample_proj_bias=True, + dtype=None, device=None, operations=None, + ): + super().__init__() + + self.linear_1 = operations.Linear(in_channels, time_embed_dim, sample_proj_bias, dtype=dtype, device=device) + + if cond_proj_dim is not None: + self.cond_proj = operations.Linear(cond_proj_dim, in_channels, bias=False, dtype=dtype, device=device) + else: + self.cond_proj = None + + self.act = nn.SiLU() + + if out_dim is not None: + time_embed_dim_out = out_dim + else: + time_embed_dim_out = time_embed_dim + self.linear_2 = operations.Linear(time_embed_dim, time_embed_dim_out, sample_proj_bias, dtype=dtype, device=device) + + if post_act_fn is None: + self.post_act = None + # else: + # self.post_act = get_activation(post_act_fn) + + def forward(self, sample, condition=None): + if condition is not None: + sample = sample + self.cond_proj(condition) + sample = self.linear_1(sample) + + if self.act is not None: + sample = self.act(sample) + + sample = self.linear_2(sample) + + if self.post_act is not None: + sample = self.post_act(sample) + return sample + + +class Timesteps(nn.Module): + def __init__(self, num_channels: int, flip_sin_to_cos: bool, downscale_freq_shift: float, scale: int = 1): + super().__init__() + self.num_channels = num_channels + self.flip_sin_to_cos = flip_sin_to_cos + self.downscale_freq_shift = downscale_freq_shift + self.scale = scale + + def forward(self, timesteps): + t_emb = get_timestep_embedding( + timesteps, + self.num_channels, + flip_sin_to_cos=self.flip_sin_to_cos, + downscale_freq_shift=self.downscale_freq_shift, + scale=self.scale, + ) + return t_emb + + +class PixArtAlphaCombinedTimestepSizeEmbeddings(nn.Module): + """ + For PixArt-Alpha. + + Reference: + https://github.com/PixArt-alpha/PixArt-alpha/blob/0f55e922376d8b797edd44d25d0e7464b260dcab/diffusion/model/nets/PixArtMS.py#L164C9-L168C29 + """ + + def __init__(self, embedding_dim, size_emb_dim, use_additional_conditions: bool = False, dtype=None, device=None, operations=None): + super().__init__() + + self.outdim = size_emb_dim + self.time_proj = Timesteps(num_channels=256, flip_sin_to_cos=True, downscale_freq_shift=0) + self.timestep_embedder = TimestepEmbedding(in_channels=256, time_embed_dim=embedding_dim, dtype=dtype, device=device, operations=operations) + + def forward(self, timestep, resolution, aspect_ratio, batch_size, hidden_dtype): + timesteps_proj = self.time_proj(timestep) + timesteps_emb = self.timestep_embedder(timesteps_proj.to(dtype=hidden_dtype)) # (N, D) + return timesteps_emb + + +class AdaLayerNormSingle(nn.Module): + r""" + Norm layer adaptive layer norm single (adaLN-single). + + As proposed in PixArt-Alpha (see: https://arxiv.org/abs/2310.00426; Section 2.3). + + Parameters: + embedding_dim (`int`): The size of each embedding vector. + use_additional_conditions (`bool`): To use additional conditions for normalization or not. + """ + + def __init__(self, embedding_dim: int, use_additional_conditions: bool = False, dtype=None, device=None, operations=None): + super().__init__() + + self.emb = PixArtAlphaCombinedTimestepSizeEmbeddings( + embedding_dim, size_emb_dim=embedding_dim // 3, use_additional_conditions=use_additional_conditions, dtype=dtype, device=device, operations=operations + ) + + self.silu = nn.SiLU() + self.linear = operations.Linear(embedding_dim, 6 * embedding_dim, bias=True, dtype=dtype, device=device) + + def forward( + self, + timestep: torch.Tensor, + added_cond_kwargs: Optional[Dict[str, torch.Tensor]] = None, + batch_size: Optional[int] = None, + hidden_dtype: Optional[torch.dtype] = None, + ) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor]: + # No modulation happening here. + added_cond_kwargs = added_cond_kwargs or {"resolution": None, "aspect_ratio": None} + embedded_timestep = self.emb(timestep, **added_cond_kwargs, batch_size=batch_size, hidden_dtype=hidden_dtype) + return self.linear(self.silu(embedded_timestep)), embedded_timestep + +class PixArtAlphaTextProjection(nn.Module): + """ + Projects caption embeddings. Also handles dropout for classifier-free guidance. + + Adapted from https://github.com/PixArt-alpha/PixArt-alpha/blob/master/diffusion/model/nets/PixArt_blocks.py + """ + + def __init__(self, in_features, hidden_size, out_features=None, act_fn="gelu_tanh", dtype=None, device=None, operations=None): + super().__init__() + if out_features is None: + out_features = hidden_size + self.linear_1 = operations.Linear(in_features=in_features, out_features=hidden_size, bias=True, dtype=dtype, device=device) + if act_fn == "gelu_tanh": + self.act_1 = nn.GELU(approximate="tanh") + elif act_fn == "silu": + self.act_1 = nn.SiLU() + else: + raise ValueError(f"Unknown activation function: {act_fn}") + self.linear_2 = operations.Linear(in_features=hidden_size, out_features=out_features, bias=True, dtype=dtype, device=device) + + def forward(self, caption): + hidden_states = self.linear_1(caption) + hidden_states = self.act_1(hidden_states) + hidden_states = self.linear_2(hidden_states) + return hidden_states + + +class GELU_approx(nn.Module): + def __init__(self, dim_in, dim_out, dtype=None, device=None, operations=None): + super().__init__() + self.proj = operations.Linear(dim_in, dim_out, dtype=dtype, device=device) + + def forward(self, x): + return torch.nn.functional.gelu(self.proj(x), approximate="tanh") + + +class FeedForward(nn.Module): + def __init__(self, dim, dim_out, mult=4, glu=False, dropout=0., dtype=None, device=None, operations=None): + super().__init__() + inner_dim = int(dim * mult) + project_in = GELU_approx(dim, inner_dim, dtype=dtype, device=device, operations=operations) + + self.net = nn.Sequential( + project_in, + nn.Dropout(dropout), + operations.Linear(inner_dim, dim_out, dtype=dtype, device=device) + ) + + def forward(self, x): + return self.net(x) + + +def apply_rotary_emb(input_tensor, freqs_cis): #TODO: remove duplicate funcs and pick the best/fastest one + cos_freqs = freqs_cis[0] + sin_freqs = freqs_cis[1] + + t_dup = rearrange(input_tensor, "... (d r) -> ... d r", r=2) + t1, t2 = t_dup.unbind(dim=-1) + t_dup = torch.stack((-t2, t1), dim=-1) + input_tensor_rot = rearrange(t_dup, "... d r -> ... (d r)") + + out = input_tensor * cos_freqs + input_tensor_rot * sin_freqs + + return out + + +class CrossAttention(nn.Module): + def __init__(self, query_dim, context_dim=None, heads=8, dim_head=64, dropout=0., attn_precision=None, dtype=None, device=None, operations=None): + super().__init__() + inner_dim = dim_head * heads + context_dim = query_dim if context_dim is None else context_dim + self.attn_precision = attn_precision + + self.heads = heads + self.dim_head = dim_head + + self.q_norm = operations.RMSNorm(inner_dim, dtype=dtype, device=device) + self.k_norm = operations.RMSNorm(inner_dim, dtype=dtype, device=device) + + self.to_q = operations.Linear(query_dim, inner_dim, bias=True, dtype=dtype, device=device) + self.to_k = operations.Linear(context_dim, inner_dim, bias=True, dtype=dtype, device=device) + self.to_v = operations.Linear(context_dim, inner_dim, bias=True, dtype=dtype, device=device) + + self.to_out = nn.Sequential(operations.Linear(inner_dim, query_dim, dtype=dtype, device=device), nn.Dropout(dropout)) + + def forward(self, x, context=None, mask=None, pe=None): + q = self.to_q(x) + context = x if context is None else context + k = self.to_k(context) + v = self.to_v(context) + + q = self.q_norm(q) + k = self.k_norm(k) + + if pe is not None: + q = apply_rotary_emb(q, pe) + k = apply_rotary_emb(k, pe) + + if mask is None: + out = comfy.ldm.modules.attention.optimized_attention(q, k, v, self.heads, attn_precision=self.attn_precision) + else: + out = comfy.ldm.modules.attention.optimized_attention_masked(q, k, v, self.heads, mask, attn_precision=self.attn_precision) + return self.to_out(out) + + +class BasicTransformerBlock(nn.Module): + def __init__(self, dim, n_heads, d_head, context_dim=None, attn_precision=None, dtype=None, device=None, operations=None): + super().__init__() + + self.attn_precision = attn_precision + self.attn1 = CrossAttention(query_dim=dim, heads=n_heads, dim_head=d_head, context_dim=None, attn_precision=self.attn_precision, dtype=dtype, device=device, operations=operations) + self.ff = FeedForward(dim, dim_out=dim, glu=True, dtype=dtype, device=device, operations=operations) + + self.attn2 = CrossAttention(query_dim=dim, context_dim=context_dim, heads=n_heads, dim_head=d_head, attn_precision=self.attn_precision, dtype=dtype, device=device, operations=operations) + + self.scale_shift_table = nn.Parameter(torch.empty(6, dim, device=device, dtype=dtype)) + + def forward(self, x, context=None, attention_mask=None, timestep=None, pe=None): + shift_msa, scale_msa, gate_msa, shift_mlp, scale_mlp, gate_mlp = (self.scale_shift_table[None, None].to(device=x.device, dtype=x.dtype) + timestep.reshape(x.shape[0], timestep.shape[1], self.scale_shift_table.shape[0], -1)).unbind(dim=2) + + x += self.attn1(comfy.ldm.common_dit.rms_norm(x) * (1 + scale_msa) + shift_msa, pe=pe) * gate_msa + + x += self.attn2(x, context=context, mask=attention_mask) + + y = comfy.ldm.common_dit.rms_norm(x) * (1 + scale_mlp) + shift_mlp + x += self.ff(y) * gate_mlp + + return x + +def get_fractional_positions(indices_grid, max_pos): + fractional_positions = torch.stack( + [ + indices_grid[:, i] / max_pos[i] + for i in range(3) + ], + dim=-1, + ) + return fractional_positions + + +def precompute_freqs_cis(indices_grid, dim, out_dtype, theta=10000.0, max_pos=[20, 2048, 2048]): + dtype = torch.float32 #self.dtype + + fractional_positions = get_fractional_positions(indices_grid, max_pos) + + start = 1 + end = theta + device = fractional_positions.device + + indices = theta ** ( + torch.linspace( + math.log(start, theta), + math.log(end, theta), + dim // 6, + device=device, + dtype=dtype, + ) + ) + indices = indices.to(dtype=dtype) + + indices = indices * math.pi / 2 + + freqs = ( + (indices * (fractional_positions.unsqueeze(-1) * 2 - 1)) + .transpose(-1, -2) + .flatten(2) + ) + + cos_freq = freqs.cos().repeat_interleave(2, dim=-1) + sin_freq = freqs.sin().repeat_interleave(2, dim=-1) + if dim % 6 != 0: + cos_padding = torch.ones_like(cos_freq[:, :, : dim % 6]) + sin_padding = torch.zeros_like(cos_freq[:, :, : dim % 6]) + cos_freq = torch.cat([cos_padding, cos_freq], dim=-1) + sin_freq = torch.cat([sin_padding, sin_freq], dim=-1) + return cos_freq.to(out_dtype), sin_freq.to(out_dtype) + + +class ReLTXVModel(torch.nn.Module): + def __init__(self, + in_channels=128, + cross_attention_dim=2048, + attention_head_dim=64, + num_attention_heads=32, + + caption_channels=4096, + num_layers=28, + + + positional_embedding_theta=10000.0, + positional_embedding_max_pos=[20, 2048, 2048], + causal_temporal_positioning=False, + vae_scale_factors=(8, 32, 32), + dtype=None, device=None, operations=None, **kwargs): + super().__init__() + self.generator = None + self.vae_scale_factors = vae_scale_factors + self.dtype = dtype + self.out_channels = in_channels + self.inner_dim = num_attention_heads * attention_head_dim + self.causal_temporal_positioning = causal_temporal_positioning + + self.patchify_proj = operations.Linear(in_channels, self.inner_dim, bias=True, dtype=dtype, device=device) + + self.adaln_single = AdaLayerNormSingle( + self.inner_dim, use_additional_conditions=False, dtype=dtype, device=device, operations=operations + ) + + # self.adaln_single.linear = operations.Linear(self.inner_dim, 4 * self.inner_dim, bias=True, dtype=dtype, device=device) + + self.caption_projection = PixArtAlphaTextProjection( + in_features=caption_channels, hidden_size=self.inner_dim, dtype=dtype, device=device, operations=operations + ) + + self.transformer_blocks = nn.ModuleList( + [ + BasicTransformerBlock( + self.inner_dim, + num_attention_heads, + attention_head_dim, + context_dim=cross_attention_dim, + # attn_precision=attn_precision, + dtype=dtype, device=device, operations=operations + ) + for d in range(num_layers) + ] + ) + + self.scale_shift_table = nn.Parameter(torch.empty(2, self.inner_dim, dtype=dtype, device=device)) + self.norm_out = operations.LayerNorm(self.inner_dim, elementwise_affine=False, eps=1e-6, dtype=dtype, device=device) + self.proj_out = operations.Linear(self.inner_dim, self.out_channels, dtype=dtype, device=device) + + self.patchifier = SymmetricPatchifier(1) + + def forward(self, x, timestep, context, attention_mask, frame_rate=25, transformer_options={}, keyframe_idxs=None, **kwargs): + patches_replace = transformer_options.get("patches_replace", {}) + + SIGMA = timestep[0].unsqueeze(0) #/ 1000 + EO = transformer_options.get("ExtraOptions", ExtraOptions("")) + + y0_style_pos = transformer_options.get("y0_style_pos") + y0_style_neg = transformer_options.get("y0_style_neg") + + y0_style_pos_weight = transformer_options.get("y0_style_pos_weight", 0.0) + y0_style_pos_synweight = transformer_options.get("y0_style_pos_synweight", 0.0) + y0_style_pos_synweight *= y0_style_pos_weight + + y0_style_neg_weight = transformer_options.get("y0_style_neg_weight", 0.0) + y0_style_neg_synweight = transformer_options.get("y0_style_neg_synweight", 0.0) + y0_style_neg_synweight *= y0_style_neg_weight + + x_orig = x.clone() + + orig_shape = list(x.shape) + + x, latent_coords = self.patchifier.patchify(x) + pixel_coords = latent_to_pixel_coords( + latent_coords=latent_coords, + scale_factors=self.vae_scale_factors, + causal_fix=self.causal_temporal_positioning, + ) + + if keyframe_idxs is not None: + pixel_coords[:, :, -keyframe_idxs.shape[2]:] = keyframe_idxs + + fractional_coords = pixel_coords.to(torch.float32) + fractional_coords[:, 0] = fractional_coords[:, 0] * (1.0 / frame_rate) + + x = self.patchify_proj(x) + timestep = timestep * 1000.0 + + if attention_mask is not None and not torch.is_floating_point(attention_mask): + attention_mask = (attention_mask - 1).to(x.dtype).reshape((attention_mask.shape[0], 1, -1, attention_mask.shape[-1])) * torch.finfo(x.dtype).max + + pe = precompute_freqs_cis(fractional_coords, dim=self.inner_dim, out_dtype=x.dtype) + + batch_size = x.shape[0] + timestep, embedded_timestep = self.adaln_single( + timestep.flatten(), + {"resolution": None, "aspect_ratio": None}, + batch_size=batch_size, + hidden_dtype=x.dtype, + ) + # Second dimension is 1 or number of tokens (if timestep_per_token) + timestep = timestep.view(batch_size, -1, timestep.shape[-1]) + embedded_timestep = embedded_timestep.view( + batch_size, -1, embedded_timestep.shape[-1] + ) + + # 2. Blocks + if self.caption_projection is not None: + batch_size = x.shape[0] + context = self.caption_projection(context) + context = context.view( + batch_size, -1, x.shape[-1] + ) + + blocks_replace = patches_replace.get("dit", {}) + for i, block in enumerate(self.transformer_blocks): + if ("double_block", i) in blocks_replace: + def block_wrap(args): + out = {} + out["img"] = block(args["img"], context=args["txt"], attention_mask=args["attention_mask"], timestep=args["vec"], pe=args["pe"]) + return out + + out = blocks_replace[("double_block", i)]({"img": x, "txt": context, "attention_mask": attention_mask, "vec": timestep, "pe": pe}, {"original_block": block_wrap}) + x = out["img"] + else: + x = block( + x, + context=context, + attention_mask=attention_mask, + timestep=timestep, + pe=pe + ) + + # 3. Output + scale_shift_values = ( + self.scale_shift_table[None, None].to(device=x.device, dtype=x.dtype) + embedded_timestep[:, :, None] + ) + shift, scale = scale_shift_values[:, :, 0], scale_shift_values[:, :, 1] + x = self.norm_out(x) + # Modulation + x = x * (1 + scale) + shift + x = self.proj_out(x) + + x = self.patchifier.unpatchify( + latents=x, + output_height=orig_shape[3], + output_width=orig_shape[4], + output_num_frames=orig_shape[2], + out_channels=orig_shape[1] // math.prod(self.patchifier.patch_size), + ) + + eps = x + + + + + + + dtype = eps.dtype if self.style_dtype is None else self.style_dtype + pinv_dtype = torch.float32 if dtype != torch.float64 else dtype + W_inv = None + + + #if eps.shape[0] == 2 or (eps.shape[0] == 1): #: and not UNCOND): + if y0_style_pos is not None and y0_style_pos_weight != 0.0: + y0_style_pos = y0_style_pos.to(torch.float32) + x = x_orig.clone().to(torch.float32) + eps = eps.to(torch.float32) + eps_orig = eps.clone() + + sigma = SIGMA #t_orig[0].to(torch.float32) / 1000 + denoised = x - sigma * eps + + img, img_latent_coords = self.patchifier.patchify(denoised) + img_y0_adain, img_y0_adain_latent_coords = self.patchifier.patchify(y0_style_pos) + + W = self.patchify_proj.weight.data.to(torch.float32) # shape [2560, 64] + b = self.patchify_proj.bias .data.to(torch.float32) # shape [2560] + + denoised_embed = F.linear(img .to(W), W, b).to(img) + y0_adain_embed = F.linear(img_y0_adain.to(W), W, b).to(img_y0_adain) + + if transformer_options['y0_style_method'] == "AdaIN": + denoised_embed = adain_seq_inplace(denoised_embed, y0_adain_embed) + for adain_iter in range(EO("style_iter", 0)): + denoised_embed = adain_seq_inplace(denoised_embed, y0_adain_embed) + denoised_embed = (denoised_embed - b) @ torch.linalg.pinv(W.to(pinv_dtype)).T.to(dtype) + denoised_embed = F.linear(denoised_embed.to(W), W, b).to(img) + denoised_embed = adain_seq_inplace(denoised_embed, y0_adain_embed) + + elif transformer_options['y0_style_method'] == "WCT": + if self.y0_adain_embed is None or self.y0_adain_embed.shape != y0_adain_embed.shape or torch.norm(self.y0_adain_embed - y0_adain_embed) > 0: + self.y0_adain_embed = y0_adain_embed + + f_s = y0_adain_embed[0].clone() + self.mu_s = f_s.mean(dim=0, keepdim=True) + f_s_centered = f_s - self.mu_s + + cov = (f_s_centered.T.double() @ f_s_centered.double()) / (f_s_centered.size(0) - 1) + + S_eig, U_eig = torch.linalg.eigh(cov + 1e-5 * torch.eye(cov.size(0), dtype=cov.dtype, device=cov.device)) + S_eig_sqrt = S_eig.clamp(min=0).sqrt() # eigenvalues -> singular values + + whiten = U_eig @ torch.diag(S_eig_sqrt) @ U_eig.T + self.y0_color = whiten.to(f_s_centered) + + for wct_i in range(eps.shape[0]): + f_c = denoised_embed[wct_i].clone() + mu_c = f_c.mean(dim=0, keepdim=True) + f_c_centered = f_c - mu_c + + cov = (f_c_centered.T.double() @ f_c_centered.double()) / (f_c_centered.size(0) - 1) + + S_eig, U_eig = torch.linalg.eigh(cov + 1e-5 * torch.eye(cov.size(0), dtype=cov.dtype, device=cov.device)) + inv_sqrt_eig = S_eig.clamp(min=0).rsqrt() + + whiten = U_eig @ torch.diag(inv_sqrt_eig) @ U_eig.T + whiten = whiten.to(f_c_centered) + + f_c_whitened = f_c_centered @ whiten.T + f_cs = f_c_whitened @ self.y0_color.T + self.mu_s + + denoised_embed[wct_i] = f_cs + + + denoised_approx = (denoised_embed - b.to(denoised_embed)) @ torch.linalg.pinv(W).T.to(denoised_embed) + denoised_approx = denoised_approx.to(eps) + + + denoised_approx = self.patchifier.unpatchify( + latents=denoised_approx, + output_height=orig_shape[3], + output_width=orig_shape[4], + output_num_frames=orig_shape[2], + out_channels=orig_shape[1] // math.prod(self.patchifier.patch_size), + ) + + eps = (x - denoised_approx) / sigma + + #UNCOND = transformer_options['cond_or_uncond'][cond_iter] == 1 + + if eps.shape[0] == 1 and transformer_options['cond_or_uncond'][0] == 1: + eps[0] = eps_orig[0] + y0_style_pos_synweight * (eps[0] - eps_orig[0]) + #if eps.shape[0] == 2: + # eps[1] = eps_orig[1] + y0_style_neg_synweight * (eps[1] - eps_orig[1]) + else: #if not UNCOND: + if eps.shape[0] == 2: + eps[1] = eps_orig[1] + y0_style_pos_weight * (eps[1] - eps_orig[1]) + eps[0] = eps_orig[0] + y0_style_pos_synweight * (eps[0] - eps_orig[0]) + else: + eps[0] = eps_orig[0] + y0_style_pos_weight * (eps[0] - eps_orig[0]) + + eps = eps.float() + + #if eps.shape[0] == 2 or (eps.shape[0] == 1): # and UNCOND): + if y0_style_neg is not None and y0_style_neg_weight != 0.0: + y0_style_neg = y0_style_neg.to(torch.float32) + x = x_orig.clone().to(torch.float32) + eps = eps.to(torch.float32) + eps_orig = eps.clone() + + sigma = SIGMA #t_orig[0].to(torch.float32) / 1000 + denoised = x - sigma * eps + + img, img_latent_coords = self.patchifier.patchify(denoised) + img_y0_adain, img_y0_adain_latent_coords = self.patchifier.patchify(y0_style_neg) + + W = self.patchify_proj.weight.data.to(torch.float32) # shape [2560, 64] + b = self.patchify_proj.bias .data.to(torch.float32) # shape [2560] + + denoised_embed = F.linear(img .to(W), W, b).to(img) + y0_adain_embed = F.linear(img_y0_adain.to(W), W, b).to(img_y0_adain) + + if transformer_options['y0_style_method'] == "AdaIN": + denoised_embed = adain_seq_inplace(denoised_embed, y0_adain_embed) + for adain_iter in range(EO("style_iter", 0)): + denoised_embed = adain_seq_inplace(denoised_embed, y0_adain_embed) + denoised_embed = (denoised_embed - b) @ torch.linalg.pinv(W.to(pinv_dtype)).T.to(dtype) + denoised_embed = F.linear(denoised_embed.to(W), W, b).to(img) + denoised_embed = adain_seq_inplace(denoised_embed, y0_adain_embed) + + elif transformer_options['y0_style_method'] == "WCT": + if self.y0_adain_embed is None or self.y0_adain_embed.shape != y0_adain_embed.shape or torch.norm(self.y0_adain_embed - y0_adain_embed) > 0: + self.y0_adain_embed = y0_adain_embed + + f_s = y0_adain_embed[0].clone() + self.mu_s = f_s.mean(dim=0, keepdim=True) + f_s_centered = f_s - self.mu_s + + cov = (f_s_centered.T.double() @ f_s_centered.double()) / (f_s_centered.size(0) - 1) + + S_eig, U_eig = torch.linalg.eigh(cov + 1e-5 * torch.eye(cov.size(0), dtype=cov.dtype, device=cov.device)) + S_eig_sqrt = S_eig.clamp(min=0).sqrt() # eigenvalues -> singular values + + whiten = U_eig @ torch.diag(S_eig_sqrt) @ U_eig.T + self.y0_color = whiten.to(f_s_centered) + + for wct_i in range(eps.shape[0]): + f_c = denoised_embed[wct_i].clone() + mu_c = f_c.mean(dim=0, keepdim=True) + f_c_centered = f_c - mu_c + + cov = (f_c_centered.T.double() @ f_c_centered.double()) / (f_c_centered.size(0) - 1) + + S_eig, U_eig = torch.linalg.eigh(cov + 1e-5 * torch.eye(cov.size(0), dtype=cov.dtype, device=cov.device)) + inv_sqrt_eig = S_eig.clamp(min=0).rsqrt() + + whiten = U_eig @ torch.diag(inv_sqrt_eig) @ U_eig.T + whiten = whiten.to(f_c_centered) + + f_c_whitened = f_c_centered @ whiten.T + f_cs = f_c_whitened @ self.y0_color.T + self.mu_s + + denoised_embed[wct_i] = f_cs + + denoised_approx = (denoised_embed - b.to(denoised_embed)) @ torch.linalg.pinv(W).T.to(denoised_embed) + denoised_approx = denoised_approx.to(eps) + + #denoised_approx = rearrange(denoised_approx, "b (h w) (c ph pw) -> b c (h ph) (w pw)", h=h_len, w=w_len, ph=2, pw=2)[:,:,:h,:w] + + #denoised_approx = self.unpatchify(denoised_approx, (h + 1) // self.patch_size, (w + 1) // self.patch_size)[:,:,:h,:w] + + denoised_approx = self.patchifier.unpatchify( + latents=denoised_approx, + output_height=orig_shape[3], + output_width=orig_shape[4], + output_num_frames=orig_shape[2], + out_channels=orig_shape[1] // math.prod(self.patchifier.patch_size), + ) + + if eps.shape[0] == 1 and not transformer_options['cond_or_uncond'][0] == 1: + eps[0] = eps_orig[0] + y0_style_neg_synweight * (eps[0] - eps_orig[0]) + else: + eps = (x - denoised_approx) / sigma + eps[0] = eps_orig[0] + y0_style_neg_weight * (eps[0] - eps_orig[0]) + if eps.shape[0] == 2: + eps[1] = eps_orig[1] + y0_style_neg_synweight * (eps[1] - eps_orig[1]) + + eps = eps.float() + + return eps + + + + + +def adain_seq_inplace(content: torch.Tensor, style: torch.Tensor, eps: float = 1e-7) -> torch.Tensor: + mean_c = content.mean(1, keepdim=True) + std_c = content.std (1, keepdim=True).add_(eps) # in-place add + mean_s = style.mean (1, keepdim=True) + std_s = style.std (1, keepdim=True).add_(eps) + + content.sub_(mean_c).div_(std_c).mul_(std_s).add_(mean_s) # in-place chain + return content + + +def adain_seq(content: torch.Tensor, style: torch.Tensor, eps: float = 1e-7) -> torch.Tensor: + return ((content - content.mean(1, keepdim=True)) / (content.std(1, keepdim=True) + eps)) * (style.std(1, keepdim=True) + eps) + style.mean(1, keepdim=True) + + + diff --git a/ComfyUI/custom_nodes/RES4LYF/lightricks/symmetric_patchifier.py b/ComfyUI/custom_nodes/RES4LYF/lightricks/symmetric_patchifier.py new file mode 100644 index 0000000000000000000000000000000000000000..4b9972b9fb589bb5516c64dd557c0914dda45116 --- /dev/null +++ b/ComfyUI/custom_nodes/RES4LYF/lightricks/symmetric_patchifier.py @@ -0,0 +1,117 @@ +from abc import ABC, abstractmethod +from typing import Tuple + +import torch +from einops import rearrange +from torch import Tensor + + +def latent_to_pixel_coords( + latent_coords: Tensor, scale_factors: Tuple[int, int, int], causal_fix: bool = False +) -> Tensor: + """ + Converts latent coordinates to pixel coordinates by scaling them according to the VAE's + configuration. + Args: + latent_coords (Tensor): A tensor of shape [batch_size, 3, num_latents] + containing the latent corner coordinates of each token. + scale_factors (Tuple[int, int, int]): The scale factors of the VAE's latent space. + causal_fix (bool): Whether to take into account the different temporal scale + of the first frame. Default = False for backwards compatibility. + Returns: + Tensor: A tensor of pixel coordinates corresponding to the input latent coordinates. + """ + pixel_coords = ( + latent_coords + * torch.tensor(scale_factors, device=latent_coords.device)[None, :, None] + ) + if causal_fix: + # Fix temporal scale for first frame to 1 due to causality + pixel_coords[:, 0] = (pixel_coords[:, 0] + 1 - scale_factors[0]).clamp(min=0) + return pixel_coords + + +class Patchifier(ABC): + def __init__(self, patch_size: int): + super().__init__() + self._patch_size = (1, patch_size, patch_size) + + @abstractmethod + def patchify( + self, latents: Tensor, frame_rates: Tensor, scale_grid: bool + ) -> Tuple[Tensor, Tensor]: + pass + + @abstractmethod + def unpatchify( + self, + latents: Tensor, + output_height: int, + output_width: int, + output_num_frames: int, + out_channels: int, + ) -> Tuple[Tensor, Tensor]: + pass + + @property + def patch_size(self): + return self._patch_size + + def get_latent_coords( + self, latent_num_frames, latent_height, latent_width, batch_size, device + ): + """ + Return a tensor of shape [batch_size, 3, num_patches] containing the + top-left corner latent coordinates of each latent patch. + The tensor is repeated for each batch element. + """ + latent_sample_coords = torch.meshgrid( + torch.arange(0, latent_num_frames, self._patch_size[0], device=device), + torch.arange(0, latent_height, self._patch_size[1], device=device), + torch.arange(0, latent_width, self._patch_size[2], device=device), + indexing="ij", + ) + latent_sample_coords = torch.stack(latent_sample_coords, dim=0) + latent_coords = latent_sample_coords.unsqueeze(0).repeat(batch_size, 1, 1, 1, 1) + latent_coords = rearrange( + latent_coords, "b c f h w -> b c (f h w)", b=batch_size + ) + return latent_coords + + +class SymmetricPatchifier(Patchifier): + def patchify( + self, + latents: Tensor, + ) -> Tuple[Tensor, Tensor]: + b, _, f, h, w = latents.shape + latent_coords = self.get_latent_coords(f, h, w, b, latents.device) + latents = rearrange( + latents, + "b c (f p1) (h p2) (w p3) -> b (f h w) (c p1 p2 p3)", + p1=self._patch_size[0], + p2=self._patch_size[1], + p3=self._patch_size[2], + ) + return latents, latent_coords + + def unpatchify( + self, + latents: Tensor, + output_height: int, + output_width: int, + output_num_frames: int, + out_channels: int, + ) -> Tuple[Tensor, Tensor]: + output_height = output_height // self._patch_size[1] + output_width = output_width // self._patch_size[2] + latents = rearrange( + latents, + "b (f h w) (c p q) -> b c f (h p) (w q) ", + f=output_num_frames, + h=output_height, + w=output_width, + p=self._patch_size[1], + q=self._patch_size[2], + ) + return latents diff --git a/ComfyUI/custom_nodes/RES4LYF/lightricks/vae/causal_conv3d.py b/ComfyUI/custom_nodes/RES4LYF/lightricks/vae/causal_conv3d.py new file mode 100644 index 0000000000000000000000000000000000000000..70d612e86376c86bd73a95df620a5434871ee2c7 --- /dev/null +++ b/ComfyUI/custom_nodes/RES4LYF/lightricks/vae/causal_conv3d.py @@ -0,0 +1,65 @@ +from typing import Tuple, Union + +import torch +import torch.nn as nn +import comfy.ops +ops = comfy.ops.disable_weight_init + + +class CausalConv3d(nn.Module): + def __init__( + self, + in_channels, + out_channels, + kernel_size: int = 3, + stride: Union[int, Tuple[int]] = 1, + dilation: int = 1, + groups: int = 1, + spatial_padding_mode: str = "zeros", + **kwargs, + ): + super().__init__() + + self.in_channels = in_channels + self.out_channels = out_channels + + kernel_size = (kernel_size, kernel_size, kernel_size) + self.time_kernel_size = kernel_size[0] + + dilation = (dilation, 1, 1) + + height_pad = kernel_size[1] // 2 + width_pad = kernel_size[2] // 2 + padding = (0, height_pad, width_pad) + + self.conv = ops.Conv3d( + in_channels, + out_channels, + kernel_size, + stride=stride, + dilation=dilation, + padding=padding, + padding_mode=spatial_padding_mode, + groups=groups, + ) + + def forward(self, x, causal: bool = True): + if causal: + first_frame_pad = x[:, :, :1, :, :].repeat( + (1, 1, self.time_kernel_size - 1, 1, 1) + ) + x = torch.concatenate((first_frame_pad, x), dim=2) + else: + first_frame_pad = x[:, :, :1, :, :].repeat( + (1, 1, (self.time_kernel_size - 1) // 2, 1, 1) + ) + last_frame_pad = x[:, :, -1:, :, :].repeat( + (1, 1, (self.time_kernel_size - 1) // 2, 1, 1) + ) + x = torch.concatenate((first_frame_pad, x, last_frame_pad), dim=2) + x = self.conv(x) + return x + + @property + def weight(self): + return self.conv.weight diff --git a/ComfyUI/custom_nodes/RES4LYF/lightricks/vae/causal_video_autoencoder.py b/ComfyUI/custom_nodes/RES4LYF/lightricks/vae/causal_video_autoencoder.py new file mode 100644 index 0000000000000000000000000000000000000000..f91870d711766f94d1a30d26d93658155672f64a --- /dev/null +++ b/ComfyUI/custom_nodes/RES4LYF/lightricks/vae/causal_video_autoencoder.py @@ -0,0 +1,1092 @@ +from __future__ import annotations +import torch +from torch import nn +from functools import partial +import math +from einops import rearrange +from typing import List, Optional, Tuple, Union +from .conv_nd_factory import make_conv_nd, make_linear_nd +from .pixel_norm import PixelNorm +from ..model import PixArtAlphaCombinedTimestepSizeEmbeddings +import comfy.ops + +ops = comfy.ops.disable_weight_init + +class Encoder(nn.Module): + r""" + The `Encoder` layer of a variational autoencoder that encodes its input into a latent representation. + + Args: + dims (`int` or `Tuple[int, int]`, *optional*, defaults to 3): + The number of dimensions to use in convolutions. + in_channels (`int`, *optional*, defaults to 3): + The number of input channels. + out_channels (`int`, *optional*, defaults to 3): + The number of output channels. + blocks (`List[Tuple[str, int]]`, *optional*, defaults to `[("res_x", 1)]`): + The blocks to use. Each block is a tuple of the block name and the number of layers. + base_channels (`int`, *optional*, defaults to 128): + The number of output channels for the first convolutional layer. + norm_num_groups (`int`, *optional*, defaults to 32): + The number of groups for normalization. + patch_size (`int`, *optional*, defaults to 1): + The patch size to use. Should be a power of 2. + norm_layer (`str`, *optional*, defaults to `group_norm`): + The normalization layer to use. Can be either `group_norm` or `pixel_norm`. + latent_log_var (`str`, *optional*, defaults to `per_channel`): + The number of channels for the log variance. Can be either `per_channel`, `uniform`, `constant` or `none`. + """ + + def __init__( + self, + dims: Union[int, Tuple[int, int]] = 3, + in_channels: int = 3, + out_channels: int = 3, + blocks: List[Tuple[str, int | dict]] = [("res_x", 1)], + base_channels: int = 128, + norm_num_groups: int = 32, + patch_size: Union[int, Tuple[int]] = 1, + norm_layer: str = "group_norm", # group_norm, pixel_norm + latent_log_var: str = "per_channel", + spatial_padding_mode: str = "zeros", + ): + super().__init__() + self.patch_size = patch_size + self.norm_layer = norm_layer + self.latent_channels = out_channels + self.latent_log_var = latent_log_var + self.blocks_desc = blocks + + in_channels = in_channels * patch_size**2 + output_channel = base_channels + + self.conv_in = make_conv_nd( + dims=dims, + in_channels=in_channels, + out_channels=output_channel, + kernel_size=3, + stride=1, + padding=1, + causal=True, + spatial_padding_mode=spatial_padding_mode, + ) + + self.down_blocks = nn.ModuleList([]) + + for block_name, block_params in blocks: + input_channel = output_channel + if isinstance(block_params, int): + block_params = {"num_layers": block_params} + + if block_name == "res_x": + block = UNetMidBlock3D( + dims=dims, + in_channels=input_channel, + num_layers=block_params["num_layers"], + resnet_eps=1e-6, + resnet_groups=norm_num_groups, + norm_layer=norm_layer, + spatial_padding_mode=spatial_padding_mode, + ) + elif block_name == "res_x_y": + output_channel = block_params.get("multiplier", 2) * output_channel + block = ResnetBlock3D( + dims=dims, + in_channels=input_channel, + out_channels=output_channel, + eps=1e-6, + groups=norm_num_groups, + norm_layer=norm_layer, + spatial_padding_mode=spatial_padding_mode, + ) + elif block_name == "compress_time": + block = make_conv_nd( + dims=dims, + in_channels=input_channel, + out_channels=output_channel, + kernel_size=3, + stride=(2, 1, 1), + causal=True, + spatial_padding_mode=spatial_padding_mode, + ) + elif block_name == "compress_space": + block = make_conv_nd( + dims=dims, + in_channels=input_channel, + out_channels=output_channel, + kernel_size=3, + stride=(1, 2, 2), + causal=True, + spatial_padding_mode=spatial_padding_mode, + ) + elif block_name == "compress_all": + block = make_conv_nd( + dims=dims, + in_channels=input_channel, + out_channels=output_channel, + kernel_size=3, + stride=(2, 2, 2), + causal=True, + spatial_padding_mode=spatial_padding_mode, + ) + elif block_name == "compress_all_x_y": + output_channel = block_params.get("multiplier", 2) * output_channel + block = make_conv_nd( + dims=dims, + in_channels=input_channel, + out_channels=output_channel, + kernel_size=3, + stride=(2, 2, 2), + causal=True, + spatial_padding_mode=spatial_padding_mode, + ) + elif block_name == "compress_all_res": + output_channel = block_params.get("multiplier", 2) * output_channel + block = SpaceToDepthDownsample( + dims=dims, + in_channels=input_channel, + out_channels=output_channel, + stride=(2, 2, 2), + spatial_padding_mode=spatial_padding_mode, + ) + elif block_name == "compress_space_res": + output_channel = block_params.get("multiplier", 2) * output_channel + block = SpaceToDepthDownsample( + dims=dims, + in_channels=input_channel, + out_channels=output_channel, + stride=(1, 2, 2), + spatial_padding_mode=spatial_padding_mode, + ) + elif block_name == "compress_time_res": + output_channel = block_params.get("multiplier", 2) * output_channel + block = SpaceToDepthDownsample( + dims=dims, + in_channels=input_channel, + out_channels=output_channel, + stride=(2, 1, 1), + spatial_padding_mode=spatial_padding_mode, + ) + else: + raise ValueError(f"unknown block: {block_name}") + + self.down_blocks.append(block) + + # out + if norm_layer == "group_norm": + self.conv_norm_out = nn.GroupNorm( + num_channels=output_channel, num_groups=norm_num_groups, eps=1e-6 + ) + elif norm_layer == "pixel_norm": + self.conv_norm_out = PixelNorm() + elif norm_layer == "layer_norm": + self.conv_norm_out = LayerNorm(output_channel, eps=1e-6) + + self.conv_act = nn.SiLU() + + conv_out_channels = out_channels + if latent_log_var == "per_channel": + conv_out_channels *= 2 + elif latent_log_var == "uniform": + conv_out_channels += 1 + elif latent_log_var == "constant": + conv_out_channels += 1 + elif latent_log_var != "none": + raise ValueError(f"Invalid latent_log_var: {latent_log_var}") + self.conv_out = make_conv_nd( + dims, + output_channel, + conv_out_channels, + 3, + padding=1, + causal=True, + spatial_padding_mode=spatial_padding_mode, + ) + + self.gradient_checkpointing = False + + def forward(self, sample: torch.FloatTensor) -> torch.FloatTensor: + r"""The forward method of the `Encoder` class.""" + + sample = patchify(sample, patch_size_hw=self.patch_size, patch_size_t=1) + sample = self.conv_in(sample) + + checkpoint_fn = ( + partial(torch.utils.checkpoint.checkpoint, use_reentrant=False) + if self.gradient_checkpointing and self.training + else lambda x: x + ) + + for down_block in self.down_blocks: + sample = checkpoint_fn(down_block)(sample) + + sample = self.conv_norm_out(sample) + sample = self.conv_act(sample) + sample = self.conv_out(sample) + + if self.latent_log_var == "uniform": + last_channel = sample[:, -1:, ...] + num_dims = sample.dim() + + if num_dims == 4: + # For shape (B, C, H, W) + repeated_last_channel = last_channel.repeat( + 1, sample.shape[1] - 2, 1, 1 + ) + sample = torch.cat([sample, repeated_last_channel], dim=1) + elif num_dims == 5: + # For shape (B, C, F, H, W) + repeated_last_channel = last_channel.repeat( + 1, sample.shape[1] - 2, 1, 1, 1 + ) + sample = torch.cat([sample, repeated_last_channel], dim=1) + else: + raise ValueError(f"Invalid input shape: {sample.shape}") + elif self.latent_log_var == "constant": + sample = sample[:, :-1, ...] + approx_ln_0 = ( + -30 + ) # this is the minimal clamp value in DiagonalGaussianDistribution objects + sample = torch.cat( + [sample, torch.ones_like(sample, device=sample.device) * approx_ln_0], + dim=1, + ) + + return sample + + +class Decoder(nn.Module): + r""" + The `Decoder` layer of a variational autoencoder that decodes its latent representation into an output sample. + + Args: + dims (`int` or `Tuple[int, int]`, *optional*, defaults to 3): + The number of dimensions to use in convolutions. + in_channels (`int`, *optional*, defaults to 3): + The number of input channels. + out_channels (`int`, *optional*, defaults to 3): + The number of output channels. + blocks (`List[Tuple[str, int]]`, *optional*, defaults to `[("res_x", 1)]`): + The blocks to use. Each block is a tuple of the block name and the number of layers. + base_channels (`int`, *optional*, defaults to 128): + The number of output channels for the first convolutional layer. + norm_num_groups (`int`, *optional*, defaults to 32): + The number of groups for normalization. + patch_size (`int`, *optional*, defaults to 1): + The patch size to use. Should be a power of 2. + norm_layer (`str`, *optional*, defaults to `group_norm`): + The normalization layer to use. Can be either `group_norm` or `pixel_norm`. + causal (`bool`, *optional*, defaults to `True`): + Whether to use causal convolutions or not. + """ + + def __init__( + self, + dims, + in_channels: int = 3, + out_channels: int = 3, + blocks: List[Tuple[str, int | dict]] = [("res_x", 1)], + base_channels: int = 128, + layers_per_block: int = 2, + norm_num_groups: int = 32, + patch_size: int = 1, + norm_layer: str = "group_norm", + causal: bool = True, + timestep_conditioning: bool = False, + spatial_padding_mode: str = "zeros", + ): + super().__init__() + self.patch_size = patch_size + self.layers_per_block = layers_per_block + out_channels = out_channels * patch_size**2 + self.causal = causal + self.blocks_desc = blocks + + # Compute output channel to be product of all channel-multiplier blocks + output_channel = base_channels + for block_name, block_params in list(reversed(blocks)): + block_params = block_params if isinstance(block_params, dict) else {} + if block_name == "res_x_y": + output_channel = output_channel * block_params.get("multiplier", 2) + if block_name == "compress_all": + output_channel = output_channel * block_params.get("multiplier", 1) + + self.conv_in = make_conv_nd( + dims, + in_channels, + output_channel, + kernel_size=3, + stride=1, + padding=1, + causal=True, + spatial_padding_mode=spatial_padding_mode, + ) + + self.up_blocks = nn.ModuleList([]) + + for block_name, block_params in list(reversed(blocks)): + input_channel = output_channel + if isinstance(block_params, int): + block_params = {"num_layers": block_params} + + if block_name == "res_x": + block = UNetMidBlock3D( + dims=dims, + in_channels=input_channel, + num_layers=block_params["num_layers"], + resnet_eps=1e-6, + resnet_groups=norm_num_groups, + norm_layer=norm_layer, + inject_noise=block_params.get("inject_noise", False), + timestep_conditioning=timestep_conditioning, + spatial_padding_mode=spatial_padding_mode, + ) + elif block_name == "attn_res_x": + block = UNetMidBlock3D( + dims=dims, + in_channels=input_channel, + num_layers=block_params["num_layers"], + resnet_groups=norm_num_groups, + norm_layer=norm_layer, + inject_noise=block_params.get("inject_noise", False), + timestep_conditioning=timestep_conditioning, + attention_head_dim=block_params["attention_head_dim"], + spatial_padding_mode=spatial_padding_mode, + ) + elif block_name == "res_x_y": + output_channel = output_channel // block_params.get("multiplier", 2) + block = ResnetBlock3D( + dims=dims, + in_channels=input_channel, + out_channels=output_channel, + eps=1e-6, + groups=norm_num_groups, + norm_layer=norm_layer, + inject_noise=block_params.get("inject_noise", False), + timestep_conditioning=False, + spatial_padding_mode=spatial_padding_mode, + ) + elif block_name == "compress_time": + block = DepthToSpaceUpsample( + dims=dims, + in_channels=input_channel, + stride=(2, 1, 1), + spatial_padding_mode=spatial_padding_mode, + ) + elif block_name == "compress_space": + block = DepthToSpaceUpsample( + dims=dims, + in_channels=input_channel, + stride=(1, 2, 2), + spatial_padding_mode=spatial_padding_mode, + ) + elif block_name == "compress_all": + output_channel = output_channel // block_params.get("multiplier", 1) + block = DepthToSpaceUpsample( + dims=dims, + in_channels=input_channel, + stride=(2, 2, 2), + residual=block_params.get("residual", False), + out_channels_reduction_factor=block_params.get("multiplier", 1), + spatial_padding_mode=spatial_padding_mode, + ) + else: + raise ValueError(f"unknown layer: {block_name}") + + self.up_blocks.append(block) + + if norm_layer == "group_norm": + self.conv_norm_out = nn.GroupNorm( + num_channels=output_channel, num_groups=norm_num_groups, eps=1e-6 + ) + elif norm_layer == "pixel_norm": + self.conv_norm_out = PixelNorm() + elif norm_layer == "layer_norm": + self.conv_norm_out = LayerNorm(output_channel, eps=1e-6) + + self.conv_act = nn.SiLU() + self.conv_out = make_conv_nd( + dims, + output_channel, + out_channels, + 3, + padding=1, + causal=True, + spatial_padding_mode=spatial_padding_mode, + ) + + self.gradient_checkpointing = False + + self.timestep_conditioning = timestep_conditioning + + if timestep_conditioning: + self.timestep_scale_multiplier = nn.Parameter( + torch.tensor(1000.0, dtype=torch.float32) + ) + self.last_time_embedder = PixArtAlphaCombinedTimestepSizeEmbeddings( + output_channel * 2, 0, operations=ops, + ) + self.last_scale_shift_table = nn.Parameter(torch.empty(2, output_channel)) + + # def forward(self, sample: torch.FloatTensor, target_shape) -> torch.FloatTensor: + def forward( + self, + sample: torch.FloatTensor, + timestep: Optional[torch.Tensor] = None, + ) -> torch.FloatTensor: + r"""The forward method of the `Decoder` class.""" + batch_size = sample.shape[0] + + sample = self.conv_in(sample, causal=self.causal) + + checkpoint_fn = ( + partial(torch.utils.checkpoint.checkpoint, use_reentrant=False) + if self.gradient_checkpointing and self.training + else lambda x: x + ) + + scaled_timestep = None + if self.timestep_conditioning: + assert ( + timestep is not None + ), "should pass timestep with timestep_conditioning=True" + scaled_timestep = timestep * self.timestep_scale_multiplier.to(dtype=sample.dtype, device=sample.device) + + for up_block in self.up_blocks: + if self.timestep_conditioning and isinstance(up_block, UNetMidBlock3D): + sample = checkpoint_fn(up_block)( + sample, causal=self.causal, timestep=scaled_timestep + ) + else: + sample = checkpoint_fn(up_block)(sample, causal=self.causal) + + sample = self.conv_norm_out(sample) + + if self.timestep_conditioning: + embedded_timestep = self.last_time_embedder( + timestep=scaled_timestep.flatten(), + resolution=None, + aspect_ratio=None, + batch_size=sample.shape[0], + hidden_dtype=sample.dtype, + ) + embedded_timestep = embedded_timestep.view( + batch_size, embedded_timestep.shape[-1], 1, 1, 1 + ) + ada_values = self.last_scale_shift_table[ + None, ..., None, None, None + ].to(device=sample.device, dtype=sample.dtype) + embedded_timestep.reshape( + batch_size, + 2, + -1, + embedded_timestep.shape[-3], + embedded_timestep.shape[-2], + embedded_timestep.shape[-1], + ) + shift, scale = ada_values.unbind(dim=1) + sample = sample * (1 + scale) + shift + + sample = self.conv_act(sample) + sample = self.conv_out(sample, causal=self.causal) + + sample = unpatchify(sample, patch_size_hw=self.patch_size, patch_size_t=1) + + return sample + + +class UNetMidBlock3D(nn.Module): + """ + A 3D UNet mid-block [`UNetMidBlock3D`] with multiple residual blocks. + + Args: + in_channels (`int`): The number of input channels. + dropout (`float`, *optional*, defaults to 0.0): The dropout rate. + num_layers (`int`, *optional*, defaults to 1): The number of residual blocks. + resnet_eps (`float`, *optional*, 1e-6 ): The epsilon value for the resnet blocks. + resnet_groups (`int`, *optional*, defaults to 32): + The number of groups to use in the group normalization layers of the resnet blocks. + norm_layer (`str`, *optional*, defaults to `group_norm`): + The normalization layer to use. Can be either `group_norm` or `pixel_norm`. + inject_noise (`bool`, *optional*, defaults to `False`): + Whether to inject noise into the hidden states. + timestep_conditioning (`bool`, *optional*, defaults to `False`): + Whether to condition the hidden states on the timestep. + + Returns: + `torch.FloatTensor`: The output of the last residual block, which is a tensor of shape `(batch_size, + in_channels, height, width)`. + + """ + + def __init__( + self, + dims: Union[int, Tuple[int, int]], + in_channels: int, + dropout: float = 0.0, + num_layers: int = 1, + resnet_eps: float = 1e-6, + resnet_groups: int = 32, + norm_layer: str = "group_norm", + inject_noise: bool = False, + timestep_conditioning: bool = False, + spatial_padding_mode: str = "zeros", + ): + super().__init__() + resnet_groups = ( + resnet_groups if resnet_groups is not None else min(in_channels // 4, 32) + ) + + self.timestep_conditioning = timestep_conditioning + + if timestep_conditioning: + self.time_embedder = PixArtAlphaCombinedTimestepSizeEmbeddings( + in_channels * 4, 0, operations=ops, + ) + + self.res_blocks = nn.ModuleList( + [ + ResnetBlock3D( + dims=dims, + in_channels=in_channels, + out_channels=in_channels, + eps=resnet_eps, + groups=resnet_groups, + dropout=dropout, + norm_layer=norm_layer, + inject_noise=inject_noise, + timestep_conditioning=timestep_conditioning, + spatial_padding_mode=spatial_padding_mode, + ) + for _ in range(num_layers) + ] + ) + + def forward( + self, + hidden_states: torch.FloatTensor, + causal: bool = True, + timestep: Optional[torch.Tensor] = None, + ) -> torch.FloatTensor: + timestep_embed = None + if self.timestep_conditioning: + assert ( + timestep is not None + ), "should pass timestep with timestep_conditioning=True" + batch_size = hidden_states.shape[0] + timestep_embed = self.time_embedder( + timestep=timestep.flatten(), + resolution=None, + aspect_ratio=None, + batch_size=batch_size, + hidden_dtype=hidden_states.dtype, + ) + timestep_embed = timestep_embed.view( + batch_size, timestep_embed.shape[-1], 1, 1, 1 + ) + + for resnet in self.res_blocks: + hidden_states = resnet(hidden_states, causal=causal, timestep=timestep_embed) + + return hidden_states + + +class SpaceToDepthDownsample(nn.Module): + def __init__(self, dims, in_channels, out_channels, stride, spatial_padding_mode): + super().__init__() + self.stride = stride + self.group_size = in_channels * math.prod(stride) // out_channels + self.conv = make_conv_nd( + dims=dims, + in_channels=in_channels, + out_channels=out_channels // math.prod(stride), + kernel_size=3, + stride=1, + causal=True, + spatial_padding_mode=spatial_padding_mode, + ) + + def forward(self, x, causal: bool = True): + if self.stride[0] == 2: + x = torch.cat( + [x[:, :, :1, :, :], x], dim=2 + ) # duplicate first frames for padding + + # skip connection + x_in = rearrange( + x, + "b c (d p1) (h p2) (w p3) -> b (c p1 p2 p3) d h w", + p1=self.stride[0], + p2=self.stride[1], + p3=self.stride[2], + ) + x_in = rearrange(x_in, "b (c g) d h w -> b c g d h w", g=self.group_size) + x_in = x_in.mean(dim=2) + + # conv + x = self.conv(x, causal=causal) + x = rearrange( + x, + "b c (d p1) (h p2) (w p3) -> b (c p1 p2 p3) d h w", + p1=self.stride[0], + p2=self.stride[1], + p3=self.stride[2], + ) + + x = x + x_in + + return x + + +class DepthToSpaceUpsample(nn.Module): + def __init__( + self, + dims, + in_channels, + stride, + residual=False, + out_channels_reduction_factor=1, + spatial_padding_mode="zeros", + ): + super().__init__() + self.stride = stride + self.out_channels = ( + math.prod(stride) * in_channels // out_channels_reduction_factor + ) + self.conv = make_conv_nd( + dims=dims, + in_channels=in_channels, + out_channels=self.out_channels, + kernel_size=3, + stride=1, + causal=True, + spatial_padding_mode=spatial_padding_mode, + ) + self.residual = residual + self.out_channels_reduction_factor = out_channels_reduction_factor + + def forward(self, x, causal: bool = True, timestep: Optional[torch.Tensor] = None): + if self.residual: + # Reshape and duplicate the input to match the output shape + x_in = rearrange( + x, + "b (c p1 p2 p3) d h w -> b c (d p1) (h p2) (w p3)", + p1=self.stride[0], + p2=self.stride[1], + p3=self.stride[2], + ) + num_repeat = math.prod(self.stride) // self.out_channels_reduction_factor + x_in = x_in.repeat(1, num_repeat, 1, 1, 1) + if self.stride[0] == 2: + x_in = x_in[:, :, 1:, :, :] + x = self.conv(x, causal=causal) + x = rearrange( + x, + "b (c p1 p2 p3) d h w -> b c (d p1) (h p2) (w p3)", + p1=self.stride[0], + p2=self.stride[1], + p3=self.stride[2], + ) + if self.stride[0] == 2: + x = x[:, :, 1:, :, :] + if self.residual: + x = x + x_in + return x + +class LayerNorm(nn.Module): + def __init__(self, dim, eps, elementwise_affine=True) -> None: + super().__init__() + self.norm = ops.LayerNorm(dim, eps=eps, elementwise_affine=elementwise_affine) + + def forward(self, x): + x = rearrange(x, "b c d h w -> b d h w c") + x = self.norm(x) + x = rearrange(x, "b d h w c -> b c d h w") + return x + + +class ResnetBlock3D(nn.Module): + r""" + A Resnet block. + + Parameters: + in_channels (`int`): The number of channels in the input. + out_channels (`int`, *optional*, default to be `None`): + The number of output channels for the first conv layer. If None, same as `in_channels`. + dropout (`float`, *optional*, defaults to `0.0`): The dropout probability to use. + groups (`int`, *optional*, default to `32`): The number of groups to use for the first normalization layer. + eps (`float`, *optional*, defaults to `1e-6`): The epsilon to use for the normalization. + """ + + def __init__( + self, + dims: Union[int, Tuple[int, int]], + in_channels: int, + out_channels: Optional[int] = None, + dropout: float = 0.0, + groups: int = 32, + eps: float = 1e-6, + norm_layer: str = "group_norm", + inject_noise: bool = False, + timestep_conditioning: bool = False, + spatial_padding_mode: str = "zeros", + ): + super().__init__() + self.in_channels = in_channels + out_channels = in_channels if out_channels is None else out_channels + self.out_channels = out_channels + self.inject_noise = inject_noise + + if norm_layer == "group_norm": + self.norm1 = nn.GroupNorm( + num_groups=groups, num_channels=in_channels, eps=eps, affine=True + ) + elif norm_layer == "pixel_norm": + self.norm1 = PixelNorm() + elif norm_layer == "layer_norm": + self.norm1 = LayerNorm(in_channels, eps=eps, elementwise_affine=True) + + self.non_linearity = nn.SiLU() + + self.conv1 = make_conv_nd( + dims, + in_channels, + out_channels, + kernel_size=3, + stride=1, + padding=1, + causal=True, + spatial_padding_mode=spatial_padding_mode, + ) + + if inject_noise: + self.per_channel_scale1 = nn.Parameter(torch.zeros((in_channels, 1, 1))) + + if norm_layer == "group_norm": + self.norm2 = nn.GroupNorm( + num_groups=groups, num_channels=out_channels, eps=eps, affine=True + ) + elif norm_layer == "pixel_norm": + self.norm2 = PixelNorm() + elif norm_layer == "layer_norm": + self.norm2 = LayerNorm(out_channels, eps=eps, elementwise_affine=True) + + self.dropout = torch.nn.Dropout(dropout) + + self.conv2 = make_conv_nd( + dims, + out_channels, + out_channels, + kernel_size=3, + stride=1, + padding=1, + causal=True, + spatial_padding_mode=spatial_padding_mode, + ) + + if inject_noise: + self.per_channel_scale2 = nn.Parameter(torch.zeros((in_channels, 1, 1))) + + self.conv_shortcut = ( + make_linear_nd( + dims=dims, in_channels=in_channels, out_channels=out_channels + ) + if in_channels != out_channels + else nn.Identity() + ) + + self.norm3 = ( + LayerNorm(in_channels, eps=eps, elementwise_affine=True) + if in_channels != out_channels + else nn.Identity() + ) + + self.timestep_conditioning = timestep_conditioning + + if timestep_conditioning: + self.scale_shift_table = nn.Parameter( + torch.randn(4, in_channels) / in_channels**0.5 + ) + + def _feed_spatial_noise( + self, hidden_states: torch.FloatTensor, per_channel_scale: torch.FloatTensor + ) -> torch.FloatTensor: + spatial_shape = hidden_states.shape[-2:] + device = hidden_states.device + dtype = hidden_states.dtype + + # similar to the "explicit noise inputs" method in style-gan + spatial_noise = torch.randn(spatial_shape, device=device, dtype=dtype)[None] + scaled_noise = (spatial_noise * per_channel_scale)[None, :, None, ...] + hidden_states = hidden_states + scaled_noise + + return hidden_states + + def forward( + self, + input_tensor: torch.FloatTensor, + causal: bool = True, + timestep: Optional[torch.Tensor] = None, + ) -> torch.FloatTensor: + hidden_states = input_tensor + batch_size = hidden_states.shape[0] + + hidden_states = self.norm1(hidden_states) + if self.timestep_conditioning: + assert ( + timestep is not None + ), "should pass timestep with timestep_conditioning=True" + ada_values = self.scale_shift_table[ + None, ..., None, None, None + ].to(device=hidden_states.device, dtype=hidden_states.dtype) + timestep.reshape( + batch_size, + 4, + -1, + timestep.shape[-3], + timestep.shape[-2], + timestep.shape[-1], + ) + shift1, scale1, shift2, scale2 = ada_values.unbind(dim=1) + + hidden_states = hidden_states * (1 + scale1) + shift1 + + hidden_states = self.non_linearity(hidden_states) + + hidden_states = self.conv1(hidden_states, causal=causal) + + if self.inject_noise: + hidden_states = self._feed_spatial_noise( + hidden_states, self.per_channel_scale1.to(device=hidden_states.device, dtype=hidden_states.dtype) + ) + + hidden_states = self.norm2(hidden_states) + + if self.timestep_conditioning: + hidden_states = hidden_states * (1 + scale2) + shift2 + + hidden_states = self.non_linearity(hidden_states) + + hidden_states = self.dropout(hidden_states) + + hidden_states = self.conv2(hidden_states, causal=causal) + + if self.inject_noise: + hidden_states = self._feed_spatial_noise( + hidden_states, self.per_channel_scale2.to(device=hidden_states.device, dtype=hidden_states.dtype) + ) + + input_tensor = self.norm3(input_tensor) + + batch_size = input_tensor.shape[0] + + input_tensor = self.conv_shortcut(input_tensor) + + output_tensor = input_tensor + hidden_states + + return output_tensor + + +def patchify(x, patch_size_hw, patch_size_t=1): + if patch_size_hw == 1 and patch_size_t == 1: + return x + if x.dim() == 4: + x = rearrange( + x, "b c (h q) (w r) -> b (c r q) h w", q=patch_size_hw, r=patch_size_hw + ) + elif x.dim() == 5: + x = rearrange( + x, + "b c (f p) (h q) (w r) -> b (c p r q) f h w", + p=patch_size_t, + q=patch_size_hw, + r=patch_size_hw, + ) + else: + raise ValueError(f"Invalid input shape: {x.shape}") + + return x + + +def unpatchify(x, patch_size_hw, patch_size_t=1): + if patch_size_hw == 1 and patch_size_t == 1: + return x + + if x.dim() == 4: + x = rearrange( + x, "b (c r q) h w -> b c (h q) (w r)", q=patch_size_hw, r=patch_size_hw + ) + elif x.dim() == 5: + x = rearrange( + x, + "b (c p r q) f h w -> b c (f p) (h q) (w r)", + p=patch_size_t, + q=patch_size_hw, + r=patch_size_hw, + ) + + return x + +class processor(nn.Module): + def __init__(self): + super().__init__() + self.register_buffer("std-of-means", torch.empty(128)) + self.register_buffer("mean-of-means", torch.empty(128)) + self.register_buffer("mean-of-stds", torch.empty(128)) + self.register_buffer("mean-of-stds_over_std-of-means", torch.empty(128)) + self.register_buffer("channel", torch.empty(128)) + + def un_normalize(self, x): + return (x * self.get_buffer("std-of-means").view(1, -1, 1, 1, 1).to(x)) + self.get_buffer("mean-of-means").view(1, -1, 1, 1, 1).to(x) + + def normalize(self, x): + return (x - self.get_buffer("mean-of-means").view(1, -1, 1, 1, 1).to(x)) / self.get_buffer("std-of-means").view(1, -1, 1, 1, 1).to(x) + +class VideoVAE(nn.Module): + def __init__(self, version=0, config=None): + super().__init__() + + if config is None: + config = self.guess_config(version) + + self.timestep_conditioning = config.get("timestep_conditioning", False) + double_z = config.get("double_z", True) + latent_log_var = config.get( + "latent_log_var", "per_channel" if double_z else "none" + ) + + self.encoder = Encoder( + dims=config["dims"], + in_channels=config.get("in_channels", 3), + out_channels=config["latent_channels"], + blocks=config.get("encoder_blocks", config.get("encoder_blocks", config.get("blocks"))), + patch_size=config.get("patch_size", 1), + latent_log_var=latent_log_var, + norm_layer=config.get("norm_layer", "group_norm"), + spatial_padding_mode=config.get("spatial_padding_mode", "zeros"), + ) + + self.decoder = Decoder( + dims=config["dims"], + in_channels=config["latent_channels"], + out_channels=config.get("out_channels", 3), + blocks=config.get("decoder_blocks", config.get("decoder_blocks", config.get("blocks"))), + patch_size=config.get("patch_size", 1), + norm_layer=config.get("norm_layer", "group_norm"), + causal=config.get("causal_decoder", False), + timestep_conditioning=self.timestep_conditioning, + spatial_padding_mode=config.get("spatial_padding_mode", "zeros"), + ) + + self.per_channel_statistics = processor() + + def guess_config(self, version): + if version == 0: + config = { + "_class_name": "CausalVideoAutoencoder", + "dims": 3, + "in_channels": 3, + "out_channels": 3, + "latent_channels": 128, + "blocks": [ + ["res_x", 4], + ["compress_all", 1], + ["res_x_y", 1], + ["res_x", 3], + ["compress_all", 1], + ["res_x_y", 1], + ["res_x", 3], + ["compress_all", 1], + ["res_x", 3], + ["res_x", 4], + ], + "scaling_factor": 1.0, + "norm_layer": "pixel_norm", + "patch_size": 4, + "latent_log_var": "uniform", + "use_quant_conv": False, + "causal_decoder": False, + } + elif version == 1: + config = { + "_class_name": "CausalVideoAutoencoder", + "dims": 3, + "in_channels": 3, + "out_channels": 3, + "latent_channels": 128, + "decoder_blocks": [ + ["res_x", {"num_layers": 5, "inject_noise": True}], + ["compress_all", {"residual": True, "multiplier": 2}], + ["res_x", {"num_layers": 6, "inject_noise": True}], + ["compress_all", {"residual": True, "multiplier": 2}], + ["res_x", {"num_layers": 7, "inject_noise": True}], + ["compress_all", {"residual": True, "multiplier": 2}], + ["res_x", {"num_layers": 8, "inject_noise": False}] + ], + "encoder_blocks": [ + ["res_x", {"num_layers": 4}], + ["compress_all", {}], + ["res_x_y", 1], + ["res_x", {"num_layers": 3}], + ["compress_all", {}], + ["res_x_y", 1], + ["res_x", {"num_layers": 3}], + ["compress_all", {}], + ["res_x", {"num_layers": 3}], + ["res_x", {"num_layers": 4}] + ], + "scaling_factor": 1.0, + "norm_layer": "pixel_norm", + "patch_size": 4, + "latent_log_var": "uniform", + "use_quant_conv": False, + "causal_decoder": False, + "timestep_conditioning": True, + } + else: + config = { + "_class_name": "CausalVideoAutoencoder", + "dims": 3, + "in_channels": 3, + "out_channels": 3, + "latent_channels": 128, + "encoder_blocks": [ + ["res_x", {"num_layers": 4}], + ["compress_space_res", {"multiplier": 2}], + ["res_x", {"num_layers": 6}], + ["compress_time_res", {"multiplier": 2}], + ["res_x", {"num_layers": 6}], + ["compress_all_res", {"multiplier": 2}], + ["res_x", {"num_layers": 2}], + ["compress_all_res", {"multiplier": 2}], + ["res_x", {"num_layers": 2}] + ], + "decoder_blocks": [ + ["res_x", {"num_layers": 5, "inject_noise": False}], + ["compress_all", {"residual": True, "multiplier": 2}], + ["res_x", {"num_layers": 5, "inject_noise": False}], + ["compress_all", {"residual": True, "multiplier": 2}], + ["res_x", {"num_layers": 5, "inject_noise": False}], + ["compress_all", {"residual": True, "multiplier": 2}], + ["res_x", {"num_layers": 5, "inject_noise": False}] + ], + "scaling_factor": 1.0, + "norm_layer": "pixel_norm", + "patch_size": 4, + "latent_log_var": "uniform", + "use_quant_conv": False, + "causal_decoder": False, + "timestep_conditioning": True + } + return config + + def encode(self, x): + frames_count = x.shape[2] + if ((frames_count - 1) % 8) != 0: + raise ValueError("Invalid number of frames: Encode input must have 1 + 8 * x frames (e.g., 1, 9, 17, ...). Please check your input.") + means, logvar = torch.chunk(self.encoder(x), 2, dim=1) + return self.per_channel_statistics.normalize(means) + + def decode(self, x, timestep=0.05, noise_scale=0.025): + if self.timestep_conditioning: #TODO: seed + x = torch.randn_like(x) * noise_scale + (1.0 - noise_scale) * x + return self.decoder(self.per_channel_statistics.un_normalize(x), timestep=timestep) + diff --git a/ComfyUI/custom_nodes/RES4LYF/lightricks/vae/conv_nd_factory.py b/ComfyUI/custom_nodes/RES4LYF/lightricks/vae/conv_nd_factory.py new file mode 100644 index 0000000000000000000000000000000000000000..b4026b14fae386850459b18fb06e8b5d7f79ab94 --- /dev/null +++ b/ComfyUI/custom_nodes/RES4LYF/lightricks/vae/conv_nd_factory.py @@ -0,0 +1,90 @@ +from typing import Tuple, Union + + +from .dual_conv3d import DualConv3d +from .causal_conv3d import CausalConv3d +import comfy.ops +ops = comfy.ops.disable_weight_init + +def make_conv_nd( + dims: Union[int, Tuple[int, int]], + in_channels: int, + out_channels: int, + kernel_size: int, + stride=1, + padding=0, + dilation=1, + groups=1, + bias=True, + causal=False, + spatial_padding_mode="zeros", + temporal_padding_mode="zeros", +): + if not (spatial_padding_mode == temporal_padding_mode or causal): + raise NotImplementedError("spatial and temporal padding modes must be equal") + if dims == 2: + return ops.Conv2d( + in_channels=in_channels, + out_channels=out_channels, + kernel_size=kernel_size, + stride=stride, + padding=padding, + dilation=dilation, + groups=groups, + bias=bias, + padding_mode=spatial_padding_mode, + ) + elif dims == 3: + if causal: + return CausalConv3d( + in_channels=in_channels, + out_channels=out_channels, + kernel_size=kernel_size, + stride=stride, + padding=padding, + dilation=dilation, + groups=groups, + bias=bias, + spatial_padding_mode=spatial_padding_mode, + ) + return ops.Conv3d( + in_channels=in_channels, + out_channels=out_channels, + kernel_size=kernel_size, + stride=stride, + padding=padding, + dilation=dilation, + groups=groups, + bias=bias, + padding_mode=spatial_padding_mode, + ) + elif dims == (2, 1): + return DualConv3d( + in_channels=in_channels, + out_channels=out_channels, + kernel_size=kernel_size, + stride=stride, + padding=padding, + bias=bias, + padding_mode=spatial_padding_mode, + ) + else: + raise ValueError(f"unsupported dimensions: {dims}") + + +def make_linear_nd( + dims: int, + in_channels: int, + out_channels: int, + bias=True, +): + if dims == 2: + return ops.Conv2d( + in_channels=in_channels, out_channels=out_channels, kernel_size=1, bias=bias + ) + elif dims == 3 or dims == (2, 1): + return ops.Conv3d( + in_channels=in_channels, out_channels=out_channels, kernel_size=1, bias=bias + ) + else: + raise ValueError(f"unsupported dimensions: {dims}") diff --git a/ComfyUI/custom_nodes/RES4LYF/lightricks/vae/dual_conv3d.py b/ComfyUI/custom_nodes/RES4LYF/lightricks/vae/dual_conv3d.py new file mode 100644 index 0000000000000000000000000000000000000000..dcf889296750d3d7e553af37ecf77d1b10245af3 --- /dev/null +++ b/ComfyUI/custom_nodes/RES4LYF/lightricks/vae/dual_conv3d.py @@ -0,0 +1,217 @@ +import math +from typing import Tuple, Union + +import torch +import torch.nn as nn +import torch.nn.functional as F +from einops import rearrange + + +class DualConv3d(nn.Module): + def __init__( + self, + in_channels, + out_channels, + kernel_size, + stride: Union[int, Tuple[int, int, int]] = 1, + padding: Union[int, Tuple[int, int, int]] = 0, + dilation: Union[int, Tuple[int, int, int]] = 1, + groups=1, + bias=True, + padding_mode="zeros", + ): + super(DualConv3d, self).__init__() + + self.in_channels = in_channels + self.out_channels = out_channels + self.padding_mode = padding_mode + # Ensure kernel_size, stride, padding, and dilation are tuples of length 3 + if isinstance(kernel_size, int): + kernel_size = (kernel_size, kernel_size, kernel_size) + if kernel_size == (1, 1, 1): + raise ValueError( + "kernel_size must be greater than 1. Use make_linear_nd instead." + ) + if isinstance(stride, int): + stride = (stride, stride, stride) + if isinstance(padding, int): + padding = (padding, padding, padding) + if isinstance(dilation, int): + dilation = (dilation, dilation, dilation) + + # Set parameters for convolutions + self.groups = groups + self.bias = bias + + # Define the size of the channels after the first convolution + intermediate_channels = ( + out_channels if in_channels < out_channels else in_channels + ) + + # Define parameters for the first convolution + self.weight1 = nn.Parameter( + torch.Tensor( + intermediate_channels, + in_channels // groups, + 1, + kernel_size[1], + kernel_size[2], + ) + ) + self.stride1 = (1, stride[1], stride[2]) + self.padding1 = (0, padding[1], padding[2]) + self.dilation1 = (1, dilation[1], dilation[2]) + if bias: + self.bias1 = nn.Parameter(torch.Tensor(intermediate_channels)) + else: + self.register_parameter("bias1", None) + + # Define parameters for the second convolution + self.weight2 = nn.Parameter( + torch.Tensor( + out_channels, intermediate_channels // groups, kernel_size[0], 1, 1 + ) + ) + self.stride2 = (stride[0], 1, 1) + self.padding2 = (padding[0], 0, 0) + self.dilation2 = (dilation[0], 1, 1) + if bias: + self.bias2 = nn.Parameter(torch.Tensor(out_channels)) + else: + self.register_parameter("bias2", None) + + # Initialize weights and biases + self.reset_parameters() + + def reset_parameters(self): + nn.init.kaiming_uniform_(self.weight1, a=math.sqrt(5)) + nn.init.kaiming_uniform_(self.weight2, a=math.sqrt(5)) + if self.bias: + fan_in1, _ = nn.init._calculate_fan_in_and_fan_out(self.weight1) + bound1 = 1 / math.sqrt(fan_in1) + nn.init.uniform_(self.bias1, -bound1, bound1) + fan_in2, _ = nn.init._calculate_fan_in_and_fan_out(self.weight2) + bound2 = 1 / math.sqrt(fan_in2) + nn.init.uniform_(self.bias2, -bound2, bound2) + + def forward(self, x, use_conv3d=False, skip_time_conv=False): + if use_conv3d: + return self.forward_with_3d(x=x, skip_time_conv=skip_time_conv) + else: + return self.forward_with_2d(x=x, skip_time_conv=skip_time_conv) + + def forward_with_3d(self, x, skip_time_conv): + # First convolution + x = F.conv3d( + x, + self.weight1, + self.bias1, + self.stride1, + self.padding1, + self.dilation1, + self.groups, + padding_mode=self.padding_mode, + ) + + if skip_time_conv: + return x + + # Second convolution + x = F.conv3d( + x, + self.weight2, + self.bias2, + self.stride2, + self.padding2, + self.dilation2, + self.groups, + padding_mode=self.padding_mode, + ) + + return x + + def forward_with_2d(self, x, skip_time_conv): + b, c, d, h, w = x.shape + + # First 2D convolution + x = rearrange(x, "b c d h w -> (b d) c h w") + # Squeeze the depth dimension out of weight1 since it's 1 + weight1 = self.weight1.squeeze(2) + # Select stride, padding, and dilation for the 2D convolution + stride1 = (self.stride1[1], self.stride1[2]) + padding1 = (self.padding1[1], self.padding1[2]) + dilation1 = (self.dilation1[1], self.dilation1[2]) + x = F.conv2d( + x, + weight1, + self.bias1, + stride1, + padding1, + dilation1, + self.groups, + padding_mode=self.padding_mode, + ) + + _, _, h, w = x.shape + + if skip_time_conv: + x = rearrange(x, "(b d) c h w -> b c d h w", b=b) + return x + + # Second convolution which is essentially treated as a 1D convolution across the 'd' dimension + x = rearrange(x, "(b d) c h w -> (b h w) c d", b=b) + + # Reshape weight2 to match the expected dimensions for conv1d + weight2 = self.weight2.squeeze(-1).squeeze(-1) + # Use only the relevant dimension for stride, padding, and dilation for the 1D convolution + stride2 = self.stride2[0] + padding2 = self.padding2[0] + dilation2 = self.dilation2[0] + x = F.conv1d( + x, + weight2, + self.bias2, + stride2, + padding2, + dilation2, + self.groups, + padding_mode=self.padding_mode, + ) + x = rearrange(x, "(b h w) c d -> b c d h w", b=b, h=h, w=w) + + return x + + @property + def weight(self): + return self.weight2 + + +def test_dual_conv3d_consistency(): + # Initialize parameters + in_channels = 3 + out_channels = 5 + kernel_size = (3, 3, 3) + stride = (2, 2, 2) + padding = (1, 1, 1) + + # Create an instance of the DualConv3d class + dual_conv3d = DualConv3d( + in_channels=in_channels, + out_channels=out_channels, + kernel_size=kernel_size, + stride=stride, + padding=padding, + bias=True, + ) + + # Example input tensor + test_input = torch.randn(1, 3, 10, 10, 10) + + # Perform forward passes with both 3D and 2D settings + output_conv3d = dual_conv3d(test_input, use_conv3d=True) + output_2d = dual_conv3d(test_input, use_conv3d=False) + + # Assert that the outputs from both methods are sufficiently close + assert torch.allclose( + output_conv3d, output_2d, atol=1e-6 + ), "Outputs are not consistent between 3D and 2D convolutions." diff --git a/ComfyUI/custom_nodes/RES4LYF/lightricks/vae/pixel_norm.py b/ComfyUI/custom_nodes/RES4LYF/lightricks/vae/pixel_norm.py new file mode 100644 index 0000000000000000000000000000000000000000..9bc3ea60e8a6453e7e12a7fb5aca4de3958a2567 --- /dev/null +++ b/ComfyUI/custom_nodes/RES4LYF/lightricks/vae/pixel_norm.py @@ -0,0 +1,12 @@ +import torch +from torch import nn + + +class PixelNorm(nn.Module): + def __init__(self, dim=1, eps=1e-8): + super(PixelNorm, self).__init__() + self.dim = dim + self.eps = eps + + def forward(self, x): + return x / torch.sqrt(torch.mean(x**2, dim=self.dim, keepdim=True) + self.eps) diff --git a/ComfyUI/custom_nodes/RES4LYF/misc_scripts/replace_metadata.py b/ComfyUI/custom_nodes/RES4LYF/misc_scripts/replace_metadata.py new file mode 100644 index 0000000000000000000000000000000000000000..7ff177ac270b739f6d315f29ee35da91517d231f --- /dev/null +++ b/ComfyUI/custom_nodes/RES4LYF/misc_scripts/replace_metadata.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python3 + +import argparse +from PIL import Image +from PIL.PngImagePlugin import PngInfo + +def extract_metadata(image_path): + image = Image.open(image_path) + metadata = image.info + return metadata + +def replace_metadata(source_image_path, target_image_path, output_image_path): + metadata = extract_metadata(source_image_path) + + target_image = Image.open(target_image_path) + + png_info = PngInfo() + for key, value in metadata.items(): + png_info.add_text(key, str(value)) + + target_image.save(output_image_path, pnginfo=png_info) + +def main(): + parser = argparse.ArgumentParser(description="Copy metadata from one PNG image to another.") + parser.add_argument('source', type=str, help="Path to the source PNG image with the metadata.") + parser.add_argument('target', type=str, help="Path to the target PNG image to replace metadata.") + parser.add_argument('output', type=str, help="Path for the output PNG image with replaced metadata.") + + args = parser.parse_args() + + replace_metadata(args.source, args.target, args.output) + + print(f"Metadata from '{args.source}' has been copied to '{args.output}'.") + +if __name__ == "__main__": + main() + + + diff --git a/ComfyUI/custom_nodes/RES4LYF/sd/attention.py b/ComfyUI/custom_nodes/RES4LYF/sd/attention.py new file mode 100644 index 0000000000000000000000000000000000000000..ee16e2082cb12657f5fba35c54de0e6c75a8bc51 --- /dev/null +++ b/ComfyUI/custom_nodes/RES4LYF/sd/attention.py @@ -0,0 +1,1088 @@ +import math +import sys + +import torch +import torch.nn.functional as F +from torch import nn, einsum +from einops import rearrange, repeat +from typing import Optional +import logging + +from comfy.ldm.modules.diffusionmodules.util import AlphaBlender, timestep_embedding +from comfy.ldm.modules.sub_quadratic_attention import efficient_dot_product_attention + +from comfy import model_management + +if model_management.xformers_enabled(): + import xformers + import xformers.ops + +if model_management.sage_attention_enabled(): + try: + from sageattention import sageattn + except ModuleNotFoundError: + logging.error(f"\n\nTo use the `--use-sage-attention` feature, the `sageattention` package must be installed first.\ncommand:\n\t{sys.executable} -m pip install sageattention") + exit(-1) + +if model_management.flash_attention_enabled(): + try: + from flash_attn import flash_attn_func + except ModuleNotFoundError: + logging.error(f"\n\nTo use the `--use-flash-attention` feature, the `flash-attn` package must be installed first.\ncommand:\n\t{sys.executable} -m pip install flash-attn") + exit(-1) + +from comfy.cli_args import args +import comfy.ops +ops = comfy.ops.disable_weight_init + +from ..style_transfer import apply_scattersort, apply_scattersort_spatial + +FORCE_UPCAST_ATTENTION_DTYPE = model_management.force_upcast_attention_dtype() + +def get_attn_precision(attn_precision, current_dtype): + if args.dont_upcast_attention: + return None + + if FORCE_UPCAST_ATTENTION_DTYPE is not None and current_dtype in FORCE_UPCAST_ATTENTION_DTYPE: + return FORCE_UPCAST_ATTENTION_DTYPE[current_dtype] + return attn_precision + +def exists(val): + return val is not None + + +def default(val, d): + if exists(val): + return val + return d + + +# feedforward +class GEGLU(nn.Module): + def __init__(self, dim_in, dim_out, dtype=None, device=None, operations=ops): + super().__init__() + self.proj = operations.Linear(dim_in, dim_out * 2, dtype=dtype, device=device) + + def forward(self, x): + x, gate = self.proj(x).chunk(2, dim=-1) + return x * F.gelu(gate) + + +class FeedForward(nn.Module): + def __init__(self, dim, dim_out=None, mult=4, glu=False, dropout=0., dtype=None, device=None, operations=ops): + super().__init__() + inner_dim = int(dim * mult) + dim_out = default(dim_out, dim) + project_in = nn.Sequential( + operations.Linear(dim, inner_dim, dtype=dtype, device=device), + nn.GELU() + ) if not glu else GEGLU(dim, inner_dim, dtype=dtype, device=device, operations=operations) + + self.net = nn.Sequential( + project_in, + nn.Dropout(dropout), + operations.Linear(inner_dim, dim_out, dtype=dtype, device=device) + ) + + def forward(self, x): + return self.net(x) + +def Normalize(in_channels, dtype=None, device=None): + return torch.nn.GroupNorm(num_groups=32, num_channels=in_channels, eps=1e-6, affine=True, dtype=dtype, device=device) + +def attention_basic(q, k, v, heads, mask=None, attn_precision=None, skip_reshape=False, skip_output_reshape=False): + attn_precision = get_attn_precision(attn_precision, q.dtype) + + if skip_reshape: + b, _, _, dim_head = q.shape + else: + b, _, dim_head = q.shape + dim_head //= heads + + scale = dim_head ** -0.5 + + h = heads + if skip_reshape: + q, k, v = map( + lambda t: t.reshape(b * heads, -1, dim_head), + (q, k, v), + ) + else: + q, k, v = map( + lambda t: t.unsqueeze(3) + .reshape(b, -1, heads, dim_head) + .permute(0, 2, 1, 3) + .reshape(b * heads, -1, dim_head) + .contiguous(), + (q, k, v), + ) + + # force cast to fp32 to avoid overflowing + if attn_precision == torch.float32: + sim = einsum('b i d, b j d -> b i j', q.float(), k.float()) * scale + else: + sim = einsum('b i d, b j d -> b i j', q, k) * scale + + del q, k + + if exists(mask): + if mask.dtype == torch.bool: + mask = rearrange(mask, 'b ... -> b (...)') #TODO: check if this bool part matches pytorch attention + max_neg_value = -torch.finfo(sim.dtype).max + mask = repeat(mask, 'b j -> (b h) () j', h=h) + sim.masked_fill_(~mask, max_neg_value) + else: + if len(mask.shape) == 2: + bs = 1 + else: + bs = mask.shape[0] + mask = mask.reshape(bs, -1, mask.shape[-2], mask.shape[-1]).expand(b, heads, -1, -1).reshape(-1, mask.shape[-2], mask.shape[-1]) + sim.add_(mask) + + # attention, what we cannot get enough of + sim = sim.softmax(dim=-1) + + out = einsum('b i j, b j d -> b i d', sim.to(v.dtype), v) + + if skip_output_reshape: + out = ( + out.unsqueeze(0) + .reshape(b, heads, -1, dim_head) + ) + else: + out = ( + out.unsqueeze(0) + .reshape(b, heads, -1, dim_head) + .permute(0, 2, 1, 3) + .reshape(b, -1, heads * dim_head) + ) + return out + + +def attention_sub_quad(query, key, value, heads, mask=None, attn_precision=None, skip_reshape=False, skip_output_reshape=False): + attn_precision = get_attn_precision(attn_precision, query.dtype) + + if skip_reshape: + b, _, _, dim_head = query.shape + else: + b, _, dim_head = query.shape + dim_head //= heads + + if skip_reshape: + query = query.reshape(b * heads, -1, dim_head) + value = value.reshape(b * heads, -1, dim_head) + key = key.reshape(b * heads, -1, dim_head).movedim(1, 2) + else: + query = query.unsqueeze(3).reshape(b, -1, heads, dim_head).permute(0, 2, 1, 3).reshape(b * heads, -1, dim_head) + value = value.unsqueeze(3).reshape(b, -1, heads, dim_head).permute(0, 2, 1, 3).reshape(b * heads, -1, dim_head) + key = key.unsqueeze(3).reshape(b, -1, heads, dim_head).permute(0, 2, 3, 1).reshape(b * heads, dim_head, -1) + + + dtype = query.dtype + upcast_attention = attn_precision == torch.float32 and query.dtype != torch.float32 + if upcast_attention: + bytes_per_token = torch.finfo(torch.float32).bits//8 + else: + bytes_per_token = torch.finfo(query.dtype).bits//8 + batch_x_heads, q_tokens, _ = query.shape + _, _, k_tokens = key.shape + + mem_free_total, _ = model_management.get_free_memory(query.device, True) + + kv_chunk_size_min = None + kv_chunk_size = None + query_chunk_size = None + + for x in [4096, 2048, 1024, 512, 256]: + count = mem_free_total / (batch_x_heads * bytes_per_token * x * 4.0) + if count >= k_tokens: + kv_chunk_size = k_tokens + query_chunk_size = x + break + + if query_chunk_size is None: + query_chunk_size = 512 + + if mask is not None: + if len(mask.shape) == 2: + bs = 1 + else: + bs = mask.shape[0] + mask = mask.reshape(bs, -1, mask.shape[-2], mask.shape[-1]).expand(b, heads, -1, -1).reshape(-1, mask.shape[-2], mask.shape[-1]) + + hidden_states = efficient_dot_product_attention( + query, + key, + value, + query_chunk_size=query_chunk_size, + kv_chunk_size=kv_chunk_size, + kv_chunk_size_min=kv_chunk_size_min, + use_checkpoint=False, + upcast_attention=upcast_attention, + mask=mask, + ) + + hidden_states = hidden_states.to(dtype) + if skip_output_reshape: + hidden_states = hidden_states.unflatten(0, (-1, heads)) + else: + hidden_states = hidden_states.unflatten(0, (-1, heads)).transpose(1,2).flatten(start_dim=2) + return hidden_states + +def attention_split(q, k, v, heads, mask=None, attn_precision=None, skip_reshape=False, skip_output_reshape=False): + attn_precision = get_attn_precision(attn_precision, q.dtype) + + if skip_reshape: + b, _, _, dim_head = q.shape + else: + b, _, dim_head = q.shape + dim_head //= heads + + scale = dim_head ** -0.5 + + if skip_reshape: + q, k, v = map( + lambda t: t.reshape(b * heads, -1, dim_head), + (q, k, v), + ) + else: + q, k, v = map( + lambda t: t.unsqueeze(3) + .reshape(b, -1, heads, dim_head) + .permute(0, 2, 1, 3) + .reshape(b * heads, -1, dim_head) + .contiguous(), + (q, k, v), + ) + + r1 = torch.zeros(q.shape[0], q.shape[1], v.shape[2], device=q.device, dtype=q.dtype) + + mem_free_total = model_management.get_free_memory(q.device) + + if attn_precision == torch.float32: + element_size = 4 + upcast = True + else: + element_size = q.element_size() + upcast = False + + gb = 1024 ** 3 + tensor_size = q.shape[0] * q.shape[1] * k.shape[1] * element_size + modifier = 3 + mem_required = tensor_size * modifier + steps = 1 + + + if mem_required > mem_free_total: + steps = 2**(math.ceil(math.log(mem_required / mem_free_total, 2))) + # print(f"Expected tensor size:{tensor_size/gb:0.1f}GB, cuda free:{mem_free_cuda/gb:0.1f}GB " + # f"torch free:{mem_free_torch/gb:0.1f} total:{mem_free_total/gb:0.1f} steps:{steps}") + + if steps > 64: + max_res = math.floor(math.sqrt(math.sqrt(mem_free_total / 2.5)) / 8) * 64 + raise RuntimeError(f'Not enough memory, use lower resolution (max approx. {max_res}x{max_res}). ' + f'Need: {mem_required/64/gb:0.1f}GB free, Have:{mem_free_total/gb:0.1f}GB free') + + if mask is not None: + if len(mask.shape) == 2: + bs = 1 + else: + bs = mask.shape[0] + mask = mask.reshape(bs, -1, mask.shape[-2], mask.shape[-1]).expand(b, heads, -1, -1).reshape(-1, mask.shape[-2], mask.shape[-1]) + + # print("steps", steps, mem_required, mem_free_total, modifier, q.element_size(), tensor_size) + first_op_done = False + cleared_cache = False + while True: + try: + slice_size = q.shape[1] // steps if (q.shape[1] % steps) == 0 else q.shape[1] + for i in range(0, q.shape[1], slice_size): + end = i + slice_size + if upcast: + with torch.autocast(enabled=False, device_type = 'cuda'): + s1 = einsum('b i d, b j d -> b i j', q[:, i:end].float(), k.float()) * scale + else: + s1 = einsum('b i d, b j d -> b i j', q[:, i:end], k) * scale + + if mask is not None: + if len(mask.shape) == 2: + s1 += mask[i:end] + else: + if mask.shape[1] == 1: + s1 += mask + else: + s1 += mask[:, i:end] + + s2 = s1.softmax(dim=-1).to(v.dtype) + del s1 + first_op_done = True + + r1[:, i:end] = einsum('b i j, b j d -> b i d', s2, v) + del s2 + break + except model_management.OOM_EXCEPTION as e: + if first_op_done == False: + model_management.soft_empty_cache(True) + if cleared_cache == False: + cleared_cache = True + logging.warning("out of memory error, emptying cache and trying again") + continue + steps *= 2 + if steps > 64: + raise e + logging.warning("out of memory error, increasing steps and trying again {}".format(steps)) + else: + raise e + + del q, k, v + + if skip_output_reshape: + r1 = ( + r1.unsqueeze(0) + .reshape(b, heads, -1, dim_head) + ) + else: + r1 = ( + r1.unsqueeze(0) + .reshape(b, heads, -1, dim_head) + .permute(0, 2, 1, 3) + .reshape(b, -1, heads * dim_head) + ) + return r1 + +BROKEN_XFORMERS = False +try: + x_vers = xformers.__version__ + # XFormers bug confirmed on all versions from 0.0.21 to 0.0.26 (q with bs bigger than 65535 gives CUDA error) + BROKEN_XFORMERS = x_vers.startswith("0.0.2") and not x_vers.startswith("0.0.20") +except: + pass + +def attention_xformers(q, k, v, heads, mask=None, attn_precision=None, skip_reshape=False, skip_output_reshape=False): + b = q.shape[0] + dim_head = q.shape[-1] + # check to make sure xformers isn't broken + disabled_xformers = False + + if BROKEN_XFORMERS: + if b * heads > 65535: + disabled_xformers = True + + if not disabled_xformers: + if torch.jit.is_tracing() or torch.jit.is_scripting(): + disabled_xformers = True + + if disabled_xformers: + return attention_pytorch(q, k, v, heads, mask, skip_reshape=skip_reshape) + + if skip_reshape: + # b h k d -> b k h d + q, k, v = map( + lambda t: t.permute(0, 2, 1, 3), + (q, k, v), + ) + # actually do the reshaping + else: + dim_head //= heads + q, k, v = map( + lambda t: t.reshape(b, -1, heads, dim_head), + (q, k, v), + ) + + if mask is not None: + # add a singleton batch dimension + if mask.ndim == 2: + mask = mask.unsqueeze(0) + # add a singleton heads dimension + if mask.ndim == 3: + mask = mask.unsqueeze(1) + # pad to a multiple of 8 + pad = 8 - mask.shape[-1] % 8 + # the xformers docs says that it's allowed to have a mask of shape (1, Nq, Nk) + # but when using separated heads, the shape has to be (B, H, Nq, Nk) + # in flux, this matrix ends up being over 1GB + # here, we create a mask with the same batch/head size as the input mask (potentially singleton or full) + mask_out = torch.empty([mask.shape[0], mask.shape[1], q.shape[1], mask.shape[-1] + pad], dtype=q.dtype, device=q.device) + + mask_out[..., :mask.shape[-1]] = mask + # doesn't this remove the padding again?? + mask = mask_out[..., :mask.shape[-1]] + mask = mask.expand(b, heads, -1, -1) + + out = xformers.ops.memory_efficient_attention(q, k, v, attn_bias=mask) + + if skip_output_reshape: + out = out.permute(0, 2, 1, 3) + else: + out = ( + out.reshape(b, -1, heads * dim_head) + ) + + return out + +if model_management.is_nvidia(): #pytorch 2.3 and up seem to have this issue. + SDP_BATCH_LIMIT = 2**15 +else: + #TODO: other GPUs ? + SDP_BATCH_LIMIT = 2**31 + + +def attention_pytorch(q, k, v, heads, mask=None, attn_precision=None, skip_reshape=False, skip_output_reshape=False): + if skip_reshape: + b, _, _, dim_head = q.shape + else: + b, _, dim_head = q.shape + dim_head //= heads + q, k, v = map( + lambda t: t.view(b, -1, heads, dim_head).transpose(1, 2), + (q, k, v), + ) + + if mask is not None: + # add a batch dimension if there isn't already one + if mask.ndim == 2: + mask = mask.unsqueeze(0) + # add a heads dimension if there isn't already one + if mask.ndim == 3: + mask = mask.unsqueeze(1) + + if SDP_BATCH_LIMIT >= b: + out = torch.nn.functional.scaled_dot_product_attention(q, k, v, attn_mask=mask, dropout_p=0.0, is_causal=False) + if not skip_output_reshape: + out = ( + out.transpose(1, 2).reshape(b, -1, heads * dim_head) + ) + else: + out = torch.empty((b, q.shape[2], heads * dim_head), dtype=q.dtype, layout=q.layout, device=q.device) + for i in range(0, b, SDP_BATCH_LIMIT): + m = mask + if mask is not None: + if mask.shape[0] > 1: + m = mask[i : i + SDP_BATCH_LIMIT] + + out[i : i + SDP_BATCH_LIMIT] = torch.nn.functional.scaled_dot_product_attention( + q[i : i + SDP_BATCH_LIMIT], + k[i : i + SDP_BATCH_LIMIT], + v[i : i + SDP_BATCH_LIMIT], + attn_mask=m, + dropout_p=0.0, is_causal=False + ).transpose(1, 2).reshape(-1, q.shape[2], heads * dim_head) + return out + + +def attention_sage(q, k, v, heads, mask=None, attn_precision=None, skip_reshape=False, skip_output_reshape=False): + if skip_reshape: + b, _, _, dim_head = q.shape + tensor_layout = "HND" + else: + b, _, dim_head = q.shape + dim_head //= heads + q, k, v = map( + lambda t: t.view(b, -1, heads, dim_head), + (q, k, v), + ) + tensor_layout = "NHD" + + if mask is not None: + # add a batch dimension if there isn't already one + if mask.ndim == 2: + mask = mask.unsqueeze(0) + # add a heads dimension if there isn't already one + if mask.ndim == 3: + mask = mask.unsqueeze(1) + + try: + out = sageattn(q, k, v, attn_mask=mask, is_causal=False, tensor_layout=tensor_layout) + except Exception as e: + logging.error("Error running sage attention: {}, using pytorch attention instead.".format(e)) + if tensor_layout == "NHD": + q, k, v = map( + lambda t: t.transpose(1, 2), + (q, k, v), + ) + return attention_pytorch(q, k, v, heads, mask=mask, skip_reshape=True, skip_output_reshape=skip_output_reshape) + + if tensor_layout == "HND": + if not skip_output_reshape: + out = ( + out.transpose(1, 2).reshape(b, -1, heads * dim_head) + ) + else: + if skip_output_reshape: + out = out.transpose(1, 2) + else: + out = out.reshape(b, -1, heads * dim_head) + return out + + +try: + @torch.library.custom_op("flash_attention::flash_attn", mutates_args=()) + def flash_attn_wrapper(q: torch.Tensor, k: torch.Tensor, v: torch.Tensor, + dropout_p: float = 0.0, causal: bool = False) -> torch.Tensor: + return flash_attn_func(q, k, v, dropout_p=dropout_p, causal=causal) + + + @flash_attn_wrapper.register_fake + def flash_attn_fake(q, k, v, dropout_p=0.0, causal=False): + # Output shape is the same as q + return q.new_empty(q.shape) +except AttributeError as error: + FLASH_ATTN_ERROR = error + + def flash_attn_wrapper(q: torch.Tensor, k: torch.Tensor, v: torch.Tensor, + dropout_p: float = 0.0, causal: bool = False) -> torch.Tensor: + assert False, f"Could not define flash_attn_wrapper: {FLASH_ATTN_ERROR}" + + +def attention_flash(q, k, v, heads, mask=None, attn_precision=None, skip_reshape=False, skip_output_reshape=False): + if skip_reshape: + b, _, _, dim_head = q.shape + else: + b, _, dim_head = q.shape + dim_head //= heads + q, k, v = map( + lambda t: t.view(b, -1, heads, dim_head).transpose(1, 2), + (q, k, v), + ) + + if mask is not None: + # add a batch dimension if there isn't already one + if mask.ndim == 2: + mask = mask.unsqueeze(0) + # add a heads dimension if there isn't already one + if mask.ndim == 3: + mask = mask.unsqueeze(1) + + try: + assert mask is None + out = flash_attn_wrapper( + q.transpose(1, 2), + k.transpose(1, 2), + v.transpose(1, 2), + dropout_p=0.0, + causal=False, + ).transpose(1, 2) + except Exception as e: + logging.warning(f"Flash Attention failed, using default SDPA: {e}") + out = torch.nn.functional.scaled_dot_product_attention(q, k, v, attn_mask=mask, dropout_p=0.0, is_causal=False) + if not skip_output_reshape: + out = ( + out.transpose(1, 2).reshape(b, -1, heads * dim_head) + ) + return out + + +optimized_attention = attention_basic + +if model_management.sage_attention_enabled(): + logging.info("Using sage attention") + optimized_attention = attention_sage +elif model_management.xformers_enabled(): + logging.info("Using xformers attention") + optimized_attention = attention_xformers +elif model_management.flash_attention_enabled(): + logging.info("Using Flash Attention") + optimized_attention = attention_flash +elif model_management.pytorch_attention_enabled(): + logging.info("Using pytorch attention") + optimized_attention = attention_pytorch +else: + if args.use_split_cross_attention: + logging.info("Using split optimization for attention") + optimized_attention = attention_split + else: + logging.info("Using sub quadratic optimization for attention, if you have memory or speed issues try using: --use-split-cross-attention") + optimized_attention = attention_sub_quad + +optimized_attention_masked = optimized_attention + +def optimized_attention_for_device(device, mask=False, small_input=False): + if small_input: + if model_management.pytorch_attention_enabled(): + return attention_pytorch #TODO: need to confirm but this is probably slightly faster for small inputs in all cases + else: + return attention_basic + + if device == torch.device("cpu"): + return attention_sub_quad + + if mask: + return optimized_attention_masked + + return optimized_attention + + +class ReCrossAttention(nn.Module): + def __init__(self, query_dim, context_dim=None, heads=8, dim_head=64, dropout=0., attn_precision=None, dtype=None, device=None, operations=ops): + super().__init__() + inner_dim = dim_head * heads + context_dim = default(context_dim, query_dim) + self.attn_precision = attn_precision + + self.heads = heads + self.dim_head = dim_head + + self.to_q = operations.Linear(query_dim, inner_dim, bias=False, dtype=dtype, device=device) + self.to_k = operations.Linear(context_dim, inner_dim, bias=False, dtype=dtype, device=device) + self.to_v = operations.Linear(context_dim, inner_dim, bias=False, dtype=dtype, device=device) + + self.to_out = nn.Sequential(operations.Linear(inner_dim, query_dim, dtype=dtype, device=device), nn.Dropout(dropout)) + + def forward(self, x, context=None, value=None, mask=None, style_block=None): + q = self.to_q(x) + q = style_block(q, "q_proj") + #SELF_ATTN = True if context is None else False + context = default(context, x) # if context is None, return x + k = self.to_k(context) + k = style_block(k, "k_proj") + if value is not None: + v = self.to_v(value) + del value + else: + v = self.to_v(context) + v = style_block(v, "v_proj") + + if mask is None: + out = optimized_attention(q, k, v, self.heads, attn_precision=self.attn_precision) + else: + #if SELF_ATTN and mask.shape[-2] != q.shape[-2]: + # mask = F.interpolate(mask[None, None].float(), size=(q.shape[-2], q.shape[-2]), mode='nearest')[0,0].to(mask) + #elif mask.shape[-2] != q.shape[-2]: # cross attn + # mask = F.interpolate(mask[None, None].float(), size=(q.shape[-2], mask.shape[-1]), mode='nearest')[0,0].to(mask) + out = attention_pytorch(q, k, v, self.heads, mask=mask) + #out = optimized_attention_masked(q, k, v, self.heads, mask, attn_precision=self.attn_precision) + out = style_block(out, "out") + return self.to_out(out) + + +class ReBasicTransformerBlock(nn.Module): + def __init__(self, dim, n_heads, d_head, dropout=0., context_dim=None, gated_ff=True, checkpoint=True, ff_in=False, inner_dim=None, + disable_self_attn=False, disable_temporal_crossattention=False, switch_temporal_ca_to_sa=False, attn_precision=None, dtype=None, device=None, operations=ops): + super().__init__() + + self.ff_in = ff_in or inner_dim is not None + if inner_dim is None: + inner_dim = dim + + self.is_res = inner_dim == dim + self.attn_precision = attn_precision + + if self.ff_in: + self.norm_in = operations.LayerNorm(dim, dtype=dtype, device=device) + self.ff_in = FeedForward(dim, dim_out=inner_dim, dropout=dropout, glu=gated_ff, dtype=dtype, device=device, operations=operations) + + self.disable_self_attn = disable_self_attn + self.attn1 = ReCrossAttention(query_dim=inner_dim, heads=n_heads, dim_head=d_head, dropout=dropout, + context_dim=context_dim if self.disable_self_attn else None, attn_precision=self.attn_precision, dtype=dtype, device=device, operations=operations) # is a self-attention if not self.disable_self_attn + self.ff = FeedForward(inner_dim, dim_out=dim, dropout=dropout, glu=gated_ff, dtype=dtype, device=device, operations=operations) + + if disable_temporal_crossattention: + if switch_temporal_ca_to_sa: + raise ValueError + else: + self.attn2 = None + else: + context_dim_attn2 = None + if not switch_temporal_ca_to_sa: + context_dim_attn2 = context_dim + + self.attn2 = ReCrossAttention(query_dim=inner_dim, context_dim=context_dim_attn2, + heads=n_heads, dim_head=d_head, dropout=dropout, attn_precision=self.attn_precision, dtype=dtype, device=device, operations=operations) # is self-attn if context is none + self.norm2 = operations.LayerNorm(inner_dim, dtype=dtype, device=device) + + self.norm1 = operations.LayerNorm(inner_dim, dtype=dtype, device=device) + self.norm3 = operations.LayerNorm(inner_dim, dtype=dtype, device=device) + self.n_heads = n_heads + self.d_head = d_head + self.switch_temporal_ca_to_sa = switch_temporal_ca_to_sa + + def forward(self, x, context=None, transformer_options={}, style_block=None): + extra_options = {} + block = transformer_options.get("block", None) + block_index = transformer_options.get("block_index", 0) + transformer_patches = {} + transformer_patches_replace = {} + + self_mask = transformer_options.get('self_mask') + cross_mask = transformer_options.get('cross_mask') + + if self_mask is not None and cross_mask is not None: + if self_mask.shape[-2] == x.shape[-2]: + pass + elif self_mask.shape[-2] < x.shape[-2]: + self_mask = transformer_options.get('self_mask_up') + cross_mask = transformer_options.get('cross_mask_up') + else: + self_mask = transformer_options.get('self_mask_down') + cross_mask = transformer_options.get('cross_mask_down') + + if self_mask.shape[-2] > x.shape[-2]: + self_mask = transformer_options.get('self_mask_down2') + cross_mask = transformer_options.get('cross_mask_down2') + + for k in transformer_options: + if k == "patches": + transformer_patches = transformer_options[k] + elif k == "patches_replace": + transformer_patches_replace = transformer_options[k] + else: + extra_options[k] = transformer_options[k] + + extra_options["n_heads"] = self.n_heads + extra_options["dim_head"] = self.d_head + extra_options["attn_precision"] = self.attn_precision + + if self.ff_in: # never true for sdxl? + x_skip = x + x = self.ff_in(self.norm_in(x)) + if self.is_res: + x += x_skip + + n = self.norm1(x) + n = style_block(n, "norm1") + if self.disable_self_attn: + context_attn1 = context + else: + context_attn1 = None + value_attn1 = None + + if "attn1_patch" in transformer_patches: + patch = transformer_patches["attn1_patch"] + if context_attn1 is None: + context_attn1 = n + value_attn1 = context_attn1 + for p in patch: + n, context_attn1, value_attn1 = p(n, context_attn1, value_attn1, extra_options) + + if block is not None: + transformer_block = (block[0], block[1], block_index) + else: + transformer_block = None + attn1_replace_patch = transformer_patches_replace.get("attn1", {}) + block_attn1 = transformer_block + if block_attn1 not in attn1_replace_patch: + block_attn1 = block + + if block_attn1 in attn1_replace_patch: + if context_attn1 is None: + context_attn1 = n + value_attn1 = n + n = self.attn1.to_q(n) + context_attn1 = self.attn1.to_k(context_attn1) + value_attn1 = self.attn1.to_v(value_attn1) + n = attn1_replace_patch[block_attn1](n, context_attn1, value_attn1, extra_options) + n = self.attn1.to_out(n) + else: + n = self.attn1(n, context=context_attn1, value=value_attn1, mask=self_mask, style_block=style_block.ATTN1) # self attention ##### + n = style_block(n, "self_attn") + + if "attn1_output_patch" in transformer_patches: + patch = transformer_patches["attn1_output_patch"] + for p in patch: + n = p(n, extra_options) + + x += n ########### + x = style_block(x, "self_attn_res") + if "middle_patch" in transformer_patches: + patch = transformer_patches["middle_patch"] + for p in patch: + x = p(x, extra_options) + + if self.attn2 is not None: + n = self.norm2(x) + n = style_block(n, "norm2") + + if self.switch_temporal_ca_to_sa: + context_attn2 = n + else: + context_attn2 = context + value_attn2 = None + if "attn2_patch" in transformer_patches: + patch = transformer_patches["attn2_patch"] + value_attn2 = context_attn2 + for p in patch: + n, context_attn2, value_attn2 = p(n, context_attn2, value_attn2, extra_options) + + attn2_replace_patch = transformer_patches_replace.get("attn2", {}) + block_attn2 = transformer_block + if block_attn2 not in attn2_replace_patch: + block_attn2 = block + + if block_attn2 in attn2_replace_patch: + if value_attn2 is None: + value_attn2 = context_attn2 + n = self.attn2.to_q(n) + context_attn2 = self.attn2.to_k(context_attn2) + value_attn2 = self.attn2.to_v(value_attn2) + n = attn2_replace_patch[block_attn2](n, context_attn2, value_attn2, extra_options) + n = self.attn2.to_out(n) + else: + n = self.attn2(n, context=context_attn2, value=value_attn2, mask=cross_mask, style_block=style_block.ATTN2) # real cross attention ##### b (h w) c + n = style_block(n, "cross_attn") + + + if "attn2_output_patch" in transformer_patches: + patch = transformer_patches["attn2_output_patch"] + for p in patch: + n = p(n, extra_options) + + x += n ########### + x = style_block(x, "cross_attn_res") + + if self.is_res: # always true with sdxl? + x_skip = x + + if not self.is_res: + pass + + x = self.norm3(x) + x = style_block(x, "norm3") + x = self.ff(x) + x = style_block(x, "ff") + + if self.is_res: + x += x_skip + + x = style_block(x, "ff_res") + + return x + + +class ReSpatialTransformer(nn.Module): + """ + Transformer block for image-like data. + First, project the input (aka embedding) + and reshape to b, t, d. + Then apply standard transformer action. + Finally, reshape to image + NEW: use_linear for more efficiency instead of the 1x1 convs + """ + def __init__(self, in_channels, n_heads, d_head, + depth=1, dropout=0., context_dim=None, + disable_self_attn=False, use_linear=False, + use_checkpoint=True, attn_precision=None, dtype=None, device=None, operations=ops): + super().__init__() + if exists(context_dim) and not isinstance(context_dim, list): + context_dim = [context_dim] * depth + self.in_channels = in_channels + inner_dim = n_heads * d_head + self.norm = operations.GroupNorm(num_groups=32, num_channels=in_channels, eps=1e-6, affine=True, dtype=dtype, device=device) + if not use_linear: + self.proj_in = operations.Conv2d(in_channels, + inner_dim, + kernel_size=1, + stride=1, + padding=0, dtype=dtype, device=device) + else: + self.proj_in = operations.Linear(in_channels, inner_dim, dtype=dtype, device=device) + + self.transformer_blocks = nn.ModuleList( + [ReBasicTransformerBlock(inner_dim, n_heads, d_head, dropout=dropout, context_dim=context_dim[d], + disable_self_attn=disable_self_attn, checkpoint=use_checkpoint, attn_precision=attn_precision, dtype=dtype, device=device, operations=operations) + for d in range(depth)] + ) + if not use_linear: + self.proj_out = operations.Conv2d(inner_dim,in_channels, + kernel_size=1, + stride=1, + padding=0, dtype=dtype, device=device) + else: + self.proj_out = operations.Linear(in_channels, inner_dim, dtype=dtype, device=device) + self.use_linear = use_linear + + def forward(self, x, context=None, style_block=None, transformer_options={}): + # note: if no context is given, cross-attention defaults to self-attention + if not isinstance(context, list): + context = [context] * len(self.transformer_blocks) + b, c, h, w = x.shape + transformer_options["activations_shape"] = list(x.shape) + x_in = x + x = self.norm(x) + x = style_block(x, "spatial_norm_in") + if not self.use_linear: + x = self.proj_in(x) + x = style_block(x, "spatial_proj_in") + x = x.movedim(1, 3).flatten(1, 2).contiguous() + if self.use_linear: + x = self.proj_in(x) + x = style_block(x, "spatial_proj_in") + for i, block in enumerate(self.transformer_blocks): + transformer_options["block_index"] = i + x = block(x, context=context[i], style_block=style_block.TFMR, transformer_options=transformer_options) + x = style_block(x, "spatial_transformer_block") + x = style_block(x, "spatial_transformer") + if self.use_linear: + x = self.proj_out(x) + x = x.reshape(x.shape[0], h, w, x.shape[-1]).movedim(3, 1).contiguous() + if not self.use_linear: + x = self.proj_out(x) + x = style_block(x, "spatial_proj_out") + x = x + x_in + x = style_block(x, "spatial_res") + return x + + +class SpatialVideoTransformer(ReSpatialTransformer): + def __init__( + self, + in_channels, + n_heads, + d_head, + depth=1, + dropout=0.0, + use_linear=False, + context_dim=None, + use_spatial_context=False, + timesteps=None, + merge_strategy: str = "fixed", + merge_factor: float = 0.5, + time_context_dim=None, + ff_in=False, + checkpoint=False, + time_depth=1, + disable_self_attn=False, + disable_temporal_crossattention=False, + max_time_embed_period: int = 10000, + attn_precision=None, + dtype=None, device=None, operations=ops + ): + super().__init__( + in_channels, + n_heads, + d_head, + depth=depth, + dropout=dropout, + use_checkpoint=checkpoint, + context_dim=context_dim, + use_linear=use_linear, + disable_self_attn=disable_self_attn, + attn_precision=attn_precision, + dtype=dtype, device=device, operations=operations + ) + self.time_depth = time_depth + self.depth = depth + self.max_time_embed_period = max_time_embed_period + + time_mix_d_head = d_head + n_time_mix_heads = n_heads + + time_mix_inner_dim = int(time_mix_d_head * n_time_mix_heads) + + inner_dim = n_heads * d_head + if use_spatial_context: + time_context_dim = context_dim + + self.time_stack = nn.ModuleList( + [ + BasicTransformerBlock( + inner_dim, + n_time_mix_heads, + time_mix_d_head, + dropout=dropout, + context_dim=time_context_dim, + # timesteps=timesteps, + checkpoint=checkpoint, + ff_in=ff_in, + inner_dim=time_mix_inner_dim, + disable_self_attn=disable_self_attn, + disable_temporal_crossattention=disable_temporal_crossattention, + attn_precision=attn_precision, + dtype=dtype, device=device, operations=operations + ) + for _ in range(self.depth) + ] + ) + + assert len(self.time_stack) == len(self.transformer_blocks) + + self.use_spatial_context = use_spatial_context + self.in_channels = in_channels + + time_embed_dim = self.in_channels * 4 + self.time_pos_embed = nn.Sequential( + operations.Linear(self.in_channels, time_embed_dim, dtype=dtype, device=device), + nn.SiLU(), + operations.Linear(time_embed_dim, self.in_channels, dtype=dtype, device=device), + ) + + self.time_mixer = AlphaBlender( + alpha=merge_factor, merge_strategy=merge_strategy + ) + + def forward( + self, + x: torch.Tensor, + context: Optional[torch.Tensor] = None, + time_context: Optional[torch.Tensor] = None, + timesteps: Optional[int] = None, + image_only_indicator: Optional[torch.Tensor] = None, + transformer_options={} + ) -> torch.Tensor: + _, _, h, w = x.shape + transformer_options["activations_shape"] = list(x.shape) + x_in = x + spatial_context = None + if exists(context): + spatial_context = context + + if self.use_spatial_context: + assert ( + context.ndim == 3 + ), f"n dims of spatial context should be 3 but are {context.ndim}" + + if time_context is None: + time_context = context + time_context_first_timestep = time_context[::timesteps] + time_context = repeat( + time_context_first_timestep, "b ... -> (b n) ...", n=h * w + ) + elif time_context is not None and not self.use_spatial_context: + time_context = repeat(time_context, "b ... -> (b n) ...", n=h * w) + if time_context.ndim == 2: + time_context = rearrange(time_context, "b c -> b 1 c") + + x = self.norm(x) + if not self.use_linear: + x = self.proj_in(x) + x = rearrange(x, "b c h w -> b (h w) c") + if self.use_linear: + x = self.proj_in(x) + + num_frames = torch.arange(timesteps, device=x.device) + num_frames = repeat(num_frames, "t -> b t", b=x.shape[0] // timesteps) + num_frames = rearrange(num_frames, "b t -> (b t)") + t_emb = timestep_embedding(num_frames, self.in_channels, repeat_only=False, max_period=self.max_time_embed_period).to(x.dtype) + emb = self.time_pos_embed(t_emb) + emb = emb[:, None, :] + + for it_, (block, mix_block) in enumerate( + zip(self.transformer_blocks, self.time_stack) + ): + transformer_options["block_index"] = it_ + x = block( + x, + context=spatial_context, + transformer_options=transformer_options, + ) + + x_mix = x + x_mix = x_mix + emb + + B, S, C = x_mix.shape + x_mix = rearrange(x_mix, "(b t) s c -> (b s) t c", t=timesteps) + x_mix = mix_block(x_mix, context=time_context) #TODO: transformer_options + x_mix = rearrange( + x_mix, "(b s) t c -> (b t) s c", s=S, b=B // timesteps, c=C, t=timesteps + ) + + x = self.time_mixer(x_spatial=x, x_temporal=x_mix, image_only_indicator=image_only_indicator) + + if self.use_linear: + x = self.proj_out(x) + x = rearrange(x, "b (h w) c -> b c h w", h=h, w=w) + if not self.use_linear: + x = self.proj_out(x) + out = x + x_in + return out + + diff --git a/ComfyUI/custom_nodes/RES4LYF/sd/openaimodel.py b/ComfyUI/custom_nodes/RES4LYF/sd/openaimodel.py new file mode 100644 index 0000000000000000000000000000000000000000..4ce4e110f5d7df8586ddf35723ff03b5b5b51588 --- /dev/null +++ b/ComfyUI/custom_nodes/RES4LYF/sd/openaimodel.py @@ -0,0 +1,1458 @@ +from abc import abstractmethod +import torch +import torch as th +import torch.nn as nn +import torch.nn.functional as F +from einops import rearrange +import logging +import copy + +from ..helper import ExtraOptions + +from comfy.ldm.modules.diffusionmodules.util import ( + checkpoint, + avg_pool_nd, + timestep_embedding, + AlphaBlender, +) +from comfy.ldm.modules.attention import SpatialTransformer, SpatialVideoTransformer, default +from .attention import ReSpatialTransformer, ReBasicTransformerBlock +from comfy.ldm.util import exists +import comfy.patcher_extension +import comfy.ops +ops = comfy.ops.disable_weight_init + +from comfy.ldm.modules.diffusionmodules.openaimodel import TimestepBlock, TimestepEmbedSequential, Upsample, Downsample, ResBlock, VideoResBlock +from ..latents import slerp_tensor, interpolate_spd, tile_latent, untile_latent, gaussian_blur_2d, median_blur_2d + +from ..style_transfer import apply_scattersort_masked, apply_scattersort_tiled, adain_seq_inplace, adain_patchwise_row_batch_med, adain_patchwise_row_batch, apply_scattersort, apply_scattersort_spatial, StyleMMDiT_Model, StyleUNet_Model + +#This is needed because accelerate makes a copy of transformer_options which breaks "transformer_index" +def forward_timestep_embed(ts, x, emb, context=None, transformer_options={}, output_shape=None, time_context=None, num_video_frames=None, image_only_indicator=None, style_block=None): + for layer in ts: + if isinstance(layer, VideoResBlock): # UNUSED + x = layer(x, emb, num_video_frames, image_only_indicator) + elif isinstance(layer, TimestepBlock): # ResBlock(TimestepBlock) + x = layer(x, emb, style_block.res_block) + x = style_block(x, "res") + elif isinstance(layer, SpatialVideoTransformer): # UNUSED + x = layer(x, context, time_context, num_video_frames, image_only_indicator, transformer_options) + if "transformer_index" in transformer_options: + transformer_options["transformer_index"] += 1 + elif isinstance(layer, ReSpatialTransformer): # USED + x = layer(x, context, style_block.spatial_block, transformer_options,) + x = style_block(x, "spatial") + if "transformer_index" in transformer_options: + transformer_options["transformer_index"] += 1 + elif isinstance(layer, Upsample): + x = layer(x, output_shape=output_shape) + x = style_block(x, "resample") + elif isinstance(layer, Downsample): + x = layer(x) + x = style_block(x, "resample") + else: + if "patches" in transformer_options and "forward_timestep_embed_patch" in transformer_options["patches"]: + found_patched = False + for class_type, handler in transformer_options["patches"]["forward_timestep_embed_patch"]: + if isinstance(layer, class_type): + x = handler(layer, x, emb, context, transformer_options, output_shape, time_context, num_video_frames, image_only_indicator) + found_patched = True + break + if found_patched: + continue + x = layer(x) + return x + + + + +class ReResBlock(TimestepBlock): + """ + A residual block that can optionally change the number of channels. + :param channels: the number of input channels. + :param emb_channels: the number of timestep embedding channels. + :param dropout: the rate of dropout. + :param out_channels: if specified, the number of out channels. + :param use_conv: if True and out_channels is specified, use a spatial + convolution instead of a smaller 1x1 convolution to change the + channels in the skip connection. + :param dims: determines if the signal is 1D, 2D, or 3D. + :param use_checkpoint: if True, use gradient checkpointing on this module. + :param up: if True, use this block for upsampling. + :param down: if True, use this block for downsampling. + """ + + def __init__( + self, + channels, + emb_channels, + dropout, + out_channels=None, + use_conv=False, + use_scale_shift_norm=False, + dims=2, + use_checkpoint=False, + up=False, + down=False, + kernel_size=3, + exchange_temb_dims=False, + skip_t_emb=False, + dtype=None, + device=None, + operations=ops + ): + super().__init__() + self.channels = channels + self.emb_channels = emb_channels + self.dropout = dropout + self.out_channels = out_channels or channels + self.use_conv = use_conv + self.use_checkpoint = use_checkpoint + self.use_scale_shift_norm = use_scale_shift_norm + self.exchange_temb_dims = exchange_temb_dims + + if isinstance(kernel_size, list): + padding = [k // 2 for k in kernel_size] + else: + padding = kernel_size // 2 + + self.in_layers = nn.Sequential( + operations.GroupNorm(32, channels, dtype=dtype, device=device), + nn.SiLU(), + operations.conv_nd(dims, channels, self.out_channels, kernel_size, padding=padding, dtype=dtype, device=device), + ) + + self.updown = up or down + + if up: + self.h_upd = Upsample(channels, False, dims, dtype=dtype, device=device) + self.x_upd = Upsample(channels, False, dims, dtype=dtype, device=device) + elif down: + self.h_upd = Downsample(channels, False, dims, dtype=dtype, device=device) + self.x_upd = Downsample(channels, False, dims, dtype=dtype, device=device) + else: + self.h_upd = self.x_upd = nn.Identity() + + self.skip_t_emb = skip_t_emb + if self.skip_t_emb: + self.emb_layers = None + self.exchange_temb_dims = False + else: + self.emb_layers = nn.Sequential( + nn.SiLU(), + operations.Linear( + emb_channels, + 2 * self.out_channels if use_scale_shift_norm else self.out_channels, dtype=dtype, device=device + ), + ) + self.out_layers = nn.Sequential( + operations.GroupNorm(32, self.out_channels, dtype=dtype, device=device), + nn.SiLU(), + nn.Dropout(p=dropout), + operations.conv_nd(dims, self.out_channels, self.out_channels, kernel_size, padding=padding, dtype=dtype, device=device) + , + ) + + if self.out_channels == channels: + self.skip_connection = nn.Identity() + elif use_conv: + self.skip_connection = operations.conv_nd( + dims, channels, self.out_channels, kernel_size, padding=padding, dtype=dtype, device=device + ) + else: + self.skip_connection = operations.conv_nd(dims, channels, self.out_channels, 1, dtype=dtype, device=device) + + def forward(self, x, emb, style_block=None): + """ + Apply the block to a Tensor, conditioned on a timestep embedding. + :param x: an [N x C x ...] Tensor of features. + :param emb: an [N x emb_channels] Tensor of timestep embeddings. + :return: an [N x C x ...] Tensor of outputs. + """ + return checkpoint( + self._forward, (x, emb, style_block), self.parameters(), self.use_checkpoint + ) + + + def _forward(self, x, emb, style_block=None): + #if self.updown: # not used with sdxl? + # in_rest, in_conv = self.in_layers[:-1], self.in_layers[-1] + # h = in_rest(x) + # h = self.h_upd(h) + # x = self.x_upd(x) + # h = in_conv(h) + #else: + # h = self.in_layers(x) + + h = self.in_layers[0](x) + h = style_block(h, "in_norm") + + h = self.in_layers[1](h) + h = style_block(h, "in_silu") + + h = self.in_layers[2](h) + h = style_block(h, "in_conv") + + + emb_out = None + if not self.skip_t_emb: + #emb_out = self.emb_layers(emb).type(h.dtype) + emb_out = self.emb_layers[0](emb).type(h.dtype) + emb_out = style_block(emb_out, "emb_silu") + + emb_out = self.emb_layers[1](emb_out) + emb_out = style_block(emb_out, "emb_linear") + + while len(emb_out.shape) < len(h.shape): + emb_out = emb_out[..., None] + + if self.use_scale_shift_norm: # not used with sdxl? + out_norm, out_rest = self.out_layers[0], self.out_layers[1:] + h = out_norm(h) + if emb_out is not None: + scale, shift = th.chunk(emb_out, 2, dim=1) + h *= (1 + scale) + h += shift + h = out_rest(h) + else: + if emb_out is not None: + if self.exchange_temb_dims: + emb_out = emb_out.movedim(1, 2) + h = h + emb_out + h = style_block(h, "emb_res") + #h = self.out_layers(h) + h = self.out_layers[0](h) + h = style_block(h, "out_norm") + + h = self.out_layers[1](h) + h = style_block(h, "out_silu") + + h = self.out_layers[3](h) # [2] is dropout + h = style_block(h, "out_conv") + + res_out = self.skip_connection(x) + h + res_out = style_block(res_out, "residual") + return res_out + #return self.skip_connection(x) + h + + + + +class Timestep(nn.Module): + def __init__(self, dim): + super().__init__() + self.dim = dim + + def forward(self, t): + return timestep_embedding(t, self.dim) + +def apply_control(h, control, name): + if control is not None and name in control and len(control[name]) > 0: + ctrl = control[name].pop() + if ctrl is not None: + try: + h += ctrl + except: + logging.warning("warning control could not be applied {} {}".format(h.shape, ctrl.shape)) + return h + + +class ReUNetModel(nn.Module): + """ + The full UNet model with attention and timestep embedding. + :param in_channels: channels in the input Tensor. + :param model_channels: base channel count for the model. + :param out_channels: channels in the output Tensor. + :param num_res_blocks: number of residual blocks per downsample. + :param dropout: the dropout probability. + :param channel_mult: channel multiplier for each level of the UNet. + :param conv_resample: if True, use learned convolutions for upsampling and + downsampling. + :param dims: determines if the signal is 1D, 2D, or 3D. + :param num_classes: if specified (as an int), then this model will be + class-conditional with `num_classes` classes. + :param use_checkpoint: use gradient checkpointing to reduce memory usage. + :param num_heads: the number of attention heads in each attention layer. + :param num_heads_channels: if specified, ignore num_heads and instead use + a fixed channel width per attention head. + :param num_heads_upsample: works with num_heads to set a different number + of heads for upsampling. Deprecated. + :param use_scale_shift_norm: use a FiLM-like conditioning mechanism. + :param resblock_updown: use residual blocks for up/downsampling. + :param use_new_attention_order: use a different attention pattern for potentially + increased efficiency. + """ + + def __init__( + self, + image_size, + in_channels, + model_channels, + out_channels, + num_res_blocks, + dropout = 0, + channel_mult = (1, 2, 4, 8), + conv_resample = True, + dims = 2, + num_classes = None, + use_checkpoint = False, + dtype = th.float32, + num_heads = -1, + num_head_channels = -1, + num_heads_upsample = -1, + use_scale_shift_norm = False, + resblock_updown = False, + use_new_attention_order = False, + use_spatial_transformer = False, # custom transformer support + transformer_depth = 1, # custom transformer support + context_dim = None, # custom transformer support + n_embed = None, # custom support for prediction of discrete ids into codebook of first stage vq model + legacy = True, + disable_self_attentions = None, + num_attention_blocks = None, + disable_middle_self_attn = False, + use_linear_in_transformer = False, + adm_in_channels = None, + transformer_depth_middle = None, + transformer_depth_output = None, + use_temporal_resblock = False, + use_temporal_attention = False, + time_context_dim = None, + extra_ff_mix_layer = False, + use_spatial_context = False, + merge_strategy = None, + merge_factor = 0.0, + video_kernel_size = None, + disable_temporal_crossattention = False, + max_ddpm_temb_period = 10000, + attn_precision = None, + device = None, + operations = ops, + ): + super().__init__() + + if context_dim is not None: + assert use_spatial_transformer, 'Fool!! You forgot to use the spatial transformer for your cross-attention conditioning...' + # from omegaconf.listconfig import ListConfig + # if type(context_dim) == ListConfig: + # context_dim = list(context_dim) + + if num_heads_upsample == -1: + num_heads_upsample = num_heads + + if num_heads == -1: + assert num_head_channels != -1, 'Either num_heads or num_head_channels has to be set' + + if num_head_channels == -1: + assert num_heads != -1, 'Either num_heads or num_head_channels has to be set' + + self.in_channels = in_channels + self.model_channels = model_channels + self.out_channels = out_channels + + if isinstance(num_res_blocks, int): + self.num_res_blocks = len(channel_mult) * [num_res_blocks] + else: + if len(num_res_blocks) != len(channel_mult): + raise ValueError("provide num_res_blocks either as an int (globally constant) or " + "as a list/tuple (per-level) with the same length as channel_mult") + self.num_res_blocks = num_res_blocks + + if disable_self_attentions is not None: + # should be a list of booleans, indicating whether to disable self-attention in TransformerBlocks or not + assert len(disable_self_attentions) == len(channel_mult) + if num_attention_blocks is not None: + assert len(num_attention_blocks) == len(self.num_res_blocks) + + transformer_depth = transformer_depth[:] + transformer_depth_output = transformer_depth_output[:] + + self.dropout = dropout + self.channel_mult = channel_mult + self.conv_resample = conv_resample + self.num_classes = num_classes + self.use_checkpoint = use_checkpoint + self.dtype = dtype + self.num_heads = num_heads + self.num_head_channels = num_head_channels + self.num_heads_upsample = num_heads_upsample + self.use_temporal_resblocks = use_temporal_resblock + self.predict_codebook_ids = n_embed is not None + + self.default_num_video_frames = None + + time_embed_dim = model_channels * 4 + self.time_embed = nn.Sequential( + operations.Linear(model_channels, time_embed_dim, dtype=self.dtype, device=device), + nn.SiLU(), + operations.Linear(time_embed_dim, time_embed_dim, dtype=self.dtype, device=device), + ) + + if self.num_classes is not None: + if isinstance(self.num_classes, int): + self.label_emb = nn.Embedding(num_classes, time_embed_dim, dtype=self.dtype, device=device) + elif self.num_classes == "continuous": + logging.debug("setting up linear c_adm embedding layer") + self.label_emb = nn.Linear(1, time_embed_dim) + elif self.num_classes == "sequential": + assert adm_in_channels is not None + self.label_emb = nn.Sequential( + nn.Sequential( + operations.Linear(adm_in_channels, time_embed_dim, dtype=self.dtype, device=device), + nn.SiLU(), + operations.Linear(time_embed_dim, time_embed_dim, dtype=self.dtype, device=device), + ) + ) + else: + raise ValueError() + + self.input_blocks = nn.ModuleList( + [ + TimestepEmbedSequential( + operations.conv_nd(dims, in_channels, model_channels, 3, padding=1, dtype=self.dtype, device=device) + ) + ] + ) + self._feature_size = model_channels + input_block_chans = [model_channels] + ch = model_channels + ds = 1 + + def get_attention_layer( + ch, + num_heads, + dim_head, + depth=1, + context_dim=None, + use_checkpoint=False, + disable_self_attn=False, + ): + if use_temporal_attention: + return SpatialVideoTransformer( + ch, + num_heads, + dim_head, + depth = depth, + context_dim = context_dim, + time_context_dim = time_context_dim, + dropout = dropout, + ff_in = extra_ff_mix_layer, + use_spatial_context = use_spatial_context, + merge_strategy = merge_strategy, + merge_factor = merge_factor, + checkpoint = use_checkpoint, + use_linear = use_linear_in_transformer, + disable_self_attn = disable_self_attn, + disable_temporal_crossattention = disable_temporal_crossattention, + max_time_embed_period = max_ddpm_temb_period, + attn_precision = attn_precision, + dtype=self.dtype, device=device, operations=operations, + ) + else: + return SpatialTransformer( + ch, num_heads, dim_head, depth=depth, context_dim=context_dim, + disable_self_attn=disable_self_attn, use_linear=use_linear_in_transformer, + use_checkpoint=use_checkpoint, attn_precision=attn_precision, dtype=self.dtype, device=device, operations=operations + ) + + def get_resblock( + merge_factor, + merge_strategy, + video_kernel_size, + ch, + time_embed_dim, + dropout, + out_channels, + dims, + use_checkpoint, + use_scale_shift_norm, + down = False, + up = False, + dtype = None, + device = None, + operations = ops + ): + if self.use_temporal_resblocks: + return VideoResBlock( + merge_factor = merge_factor, + merge_strategy = merge_strategy, + video_kernel_size = video_kernel_size, + channels = ch, + emb_channels = time_embed_dim, + dropout = dropout, + out_channels = out_channels, + dims = dims, + use_checkpoint = use_checkpoint, + use_scale_shift_norm = use_scale_shift_norm, + down = down, + up = up, + dtype=dtype, device=device, operations=operations, + ) + else: + return ResBlock( + channels = ch, + emb_channels = time_embed_dim, + dropout = dropout, + out_channels = out_channels, + use_checkpoint = use_checkpoint, + dims = dims, + use_scale_shift_norm = use_scale_shift_norm, + down = down, + up = up, + dtype=dtype, device=device, operations=operations, + ) + + for level, mult in enumerate(channel_mult): + for nr in range(self.num_res_blocks[level]): + layers = [ + get_resblock( + merge_factor = merge_factor, + merge_strategy = merge_strategy, + video_kernel_size = video_kernel_size, + ch = ch, + time_embed_dim = time_embed_dim, + dropout = dropout, + out_channels = mult * model_channels, + dims = dims, + use_checkpoint = use_checkpoint, + use_scale_shift_norm = use_scale_shift_norm, + dtype=self.dtype, device=device, operations=operations, + ) + ] + ch = mult * model_channels + num_transformers = transformer_depth.pop(0) + if num_transformers > 0: + if num_head_channels == -1: + dim_head = ch // num_heads + else: + num_heads = ch // num_head_channels + dim_head = num_head_channels + if legacy: + #num_heads = 1 + dim_head = ch // num_heads if use_spatial_transformer else num_head_channels + if exists(disable_self_attentions): + disabled_sa = disable_self_attentions[level] + else: + disabled_sa = False + + if not exists(num_attention_blocks) or nr < num_attention_blocks[level]: + layers.append(get_attention_layer( + ch, num_heads, dim_head, depth=num_transformers, context_dim=context_dim, + disable_self_attn=disabled_sa, use_checkpoint=use_checkpoint) + ) + self.input_blocks.append(TimestepEmbedSequential(*layers)) + self._feature_size += ch + input_block_chans.append(ch) + if level != len(channel_mult) - 1: + out_ch = ch + self.input_blocks.append( + TimestepEmbedSequential( + get_resblock( + merge_factor = merge_factor, + merge_strategy = merge_strategy, + video_kernel_size = video_kernel_size, + ch = ch, + time_embed_dim = time_embed_dim, + dropout = dropout, + out_channels = out_ch, + dims = dims, + use_checkpoint = use_checkpoint, + use_scale_shift_norm = use_scale_shift_norm, + down = True, + dtype=self.dtype, device=device, operations=operations, + ) + if resblock_updown + else Downsample(ch, conv_resample, dims=dims, out_channels=out_ch, dtype=self.dtype, device=device, operations=operations) + ) + ) + ch = out_ch + input_block_chans.append(ch) + ds *= 2 + self._feature_size += ch + + if num_head_channels == -1: + dim_head = ch // num_heads + else: + num_heads = ch // num_head_channels + dim_head = num_head_channels + if legacy: + #num_heads = 1 + dim_head = ch // num_heads if use_spatial_transformer else num_head_channels + mid_block = [ + get_resblock( + merge_factor = merge_factor, + merge_strategy = merge_strategy, + video_kernel_size = video_kernel_size, + ch = ch, + time_embed_dim = time_embed_dim, + dropout = dropout, + out_channels = None, + dims = dims, + use_checkpoint = use_checkpoint, + use_scale_shift_norm = use_scale_shift_norm, + dtype=self.dtype, device=device, operations=operations, + )] + + self.middle_block = None + if transformer_depth_middle >= -1: + if transformer_depth_middle >= 0: + mid_block += [get_attention_layer( # always uses a self-attn + ch, num_heads, dim_head, depth=transformer_depth_middle, context_dim=context_dim, + disable_self_attn=disable_middle_self_attn, use_checkpoint=use_checkpoint + ), + get_resblock( + merge_factor = merge_factor, + merge_strategy = merge_strategy, + video_kernel_size = video_kernel_size, + ch = ch, + time_embed_dim = time_embed_dim, + dropout = dropout, + out_channels = None, + dims = dims, + use_checkpoint = use_checkpoint, + use_scale_shift_norm = use_scale_shift_norm, + dtype=self.dtype, device=device, operations=operations, + )] + self.middle_block = TimestepEmbedSequential(*mid_block) + self._feature_size += ch + + self.output_blocks = nn.ModuleList([]) + for level, mult in list(enumerate(channel_mult))[::-1]: + for i in range(self.num_res_blocks[level] + 1): + ich = input_block_chans.pop() + layers = [ + get_resblock( + merge_factor = merge_factor, + merge_strategy = merge_strategy, + video_kernel_size = video_kernel_size, + ch = ch + ich, + time_embed_dim = time_embed_dim, + dropout = dropout, + out_channels = model_channels * mult, + dims = dims, + use_checkpoint = use_checkpoint, + use_scale_shift_norm = use_scale_shift_norm, + dtype=self.dtype, device=device, operations=operations, + ) + ] + ch = model_channels * mult + num_transformers = transformer_depth_output.pop() + if num_transformers > 0: + if num_head_channels == -1: + dim_head = ch // num_heads + else: + num_heads = ch // num_head_channels + dim_head = num_head_channels + if legacy: + #num_heads = 1 + dim_head = ch // num_heads if use_spatial_transformer else num_head_channels + if exists(disable_self_attentions): + disabled_sa = disable_self_attentions[level] + else: + disabled_sa = False + + if not exists(num_attention_blocks) or i < num_attention_blocks[level]: + layers.append( + get_attention_layer( + ch, num_heads, dim_head, depth=num_transformers, context_dim=context_dim, + disable_self_attn=disabled_sa, use_checkpoint=use_checkpoint + ) + ) + if level and i == self.num_res_blocks[level]: + out_ch = ch + layers.append( + get_resblock( + merge_factor = merge_factor, + merge_strategy = merge_strategy, + video_kernel_size = video_kernel_size, + ch = ch, + time_embed_dim = time_embed_dim, + dropout = dropout, + out_channels = out_ch, + dims = dims, + use_checkpoint = use_checkpoint, + use_scale_shift_norm = use_scale_shift_norm, + up = True, + dtype=self.dtype, device=device, operations=operations, + ) + if resblock_updown + else Upsample(ch, conv_resample, dims=dims, out_channels=out_ch, dtype=self.dtype, device=device, operations=operations) + ) + ds //= 2 + self.output_blocks.append(TimestepEmbedSequential(*layers)) + self._feature_size += ch + + self.out = nn.Sequential( + operations.GroupNorm(32, ch, dtype=self.dtype, device=device), + nn.SiLU(), + operations.conv_nd(dims, model_channels, out_channels, 3, padding=1, dtype=self.dtype, device=device), + ) + if self.predict_codebook_ids: + self.id_predictor = nn.Sequential( + operations.GroupNorm(32, ch, dtype=self.dtype, device=device), + operations.conv_nd(dims, model_channels, n_embed, 1, dtype=self.dtype, device=device), + #nn.LogSoftmax(dim=1) # change to cross_entropy and produce non-normalized logits + ) + + + + def forward(self, x, timesteps=None, context=None, y=None, control=None, transformer_options={}, **kwargs): + return comfy.patcher_extension.WrapperExecutor.new_class_executor( + self._forward, + self, + comfy.patcher_extension.get_all_wrappers(comfy.patcher_extension.WrappersMP.DIFFUSION_MODEL, transformer_options) + ).execute(x, timesteps, context, y, control, transformer_options, **kwargs) + + def _forward(self, x, timesteps=None, context=None, y=None, control=None, transformer_options={}, **kwargs): + """ + Apply the model to an input batch. + :param x: an [N x C x ...] Tensor of inputs. + :param timesteps: a 1-D batch of timesteps. + :param context: conditioning plugged in via crossattn + :param y: an [N] Tensor of labels, if class-conditional. + :return: an [N x C x ...] Tensor of outputs. + """ + h_len, w_len = x.shape[-2:] + img_len = h_len * w_len + transformer_options["original_shape"] = list(x.shape) + transformer_options["transformer_index"] = 0 + transformer_patches = transformer_options.get("patches", {}) + SIGMA = transformer_options['sigmas'].to(x) # timestep[0].unsqueeze(0) #/ 1000 + + img_slice = slice(None, -1) #slice(None, img_len) # for the sake of cross attn... :-1 + txt_slice = slice(None, -1) + + EO = transformer_options.get("ExtraOptions", ExtraOptions("")) + if EO is not None: + EO.mute = True + + if EO("zero_heads"): + HEADS = 0 + else: + HEADS = 10 # self.input_blocks[4][1].transformer_blocks[0].attn2.heads # HEADS = 10 + + StyleMMDiT = transformer_options.get('StyleMMDiT', StyleUNet_Model()) + StyleMMDiT.set_len(h_len, w_len, img_slice, txt_slice, HEADS=HEADS) + StyleMMDiT.Retrojector = self.Retrojector if hasattr(self, "Retrojector") else None + transformer_options['StyleMMDiT'] = None + + x_tmp = transformer_options.get("x_tmp") + if x_tmp is not None: + x_tmp = x_tmp.clone() / ((SIGMA ** 2 + 1) ** 0.5) + x_tmp = x_tmp.expand_as(x) # (x.shape[0], -1, -1, -1) # .clone().to(x) + + y0_style, img_y0_style = None, None + + + x_orig, timesteps_orig, y_orig, context_orig = clone_inputs(x, timesteps, y, context) + h_orig = x_orig.clone() + + weight = -1 * transformer_options.get("regional_conditioning_weight", 0.0) + floor = -1 * transformer_options.get("regional_conditioning_floor", 0.0) + + #floor = min(floor, weight) + mask_zero, mask_up_zero, mask_down_zero, mask_down2_zero = None, None, None, None + txt_len = context.shape[1] # mask_obj[0].text_len + + + z_ = transformer_options.get("z_") # initial noise and/or image+noise from start of rk_sampler_beta() + rk_row = transformer_options.get("row") # for "smart noise" + if z_ is not None: + x_init = z_[rk_row].to(x) + elif 'x_init' in transformer_options: + x_init = transformer_options.get('x_init').to(x) + + # recon loop to extract exact noise pred for scattersort guide assembly + RECON_MODE = StyleMMDiT.noise_mode == "recon" + recon_iterations = 2 if StyleMMDiT.noise_mode == "recon" else 1 + for recon_iter in range(recon_iterations): + y0_style = StyleMMDiT.guides + y0_style_active = True if type(y0_style) == torch.Tensor else False + + RECON_MODE = True if StyleMMDiT.noise_mode == "recon" and recon_iter == 0 else False + + ISIGMA = SIGMA + if StyleMMDiT.noise_mode == "recon" and recon_iter == 1: + ISIGMA = SIGMA * EO("ISIGMA_FACTOR", 1.0) + + model_sampling = transformer_options.get('model_sampling') + timesteps_orig = model_sampling.timestep(ISIGMA).expand_as(timesteps_orig) + + x_recon = x_tmp if x_tmp is not None else x_orig + #noise_prediction = x_recon + (1-SIGMA.to(x_recon)) * eps.to(x_recon) + noise_prediction = eps.to(x_recon) + denoised = x_recon * ((SIGMA.to(x_recon) ** 2 + 1) ** 0.5) - SIGMA.to(x_recon) * eps.to(x_recon) + + denoised = StyleMMDiT.apply_recon_lure(denoised, y0_style.to(x_recon)) # .to(denoised) + + new_x = (denoised + ISIGMA.to(x_recon) * noise_prediction) / ((ISIGMA.to(x_recon) ** 2 + 1) ** 0.5) + h_orig = new_x.clone().to(x) + x_init = noise_prediction + elif StyleMMDiT.noise_mode == "bonanza": + x_init = torch.randn_like(x_init) + + if y0_style_active: + if y0_style.sum() == 0.0 and y0_style.std() == 0.0: + y0_style_noised = x.clone() + else: + y0_style_noised = (y0_style + ISIGMA.to(y0_style) * x_init.expand_as(x).to(y0_style)) / ((ISIGMA.to(y0_style) ** 2 + 1) ** 0.5) #x_init.expand(x.shape[0],-1,-1,-1).to(y0_style)) + + out_list = [] + for cond_iter in range(len(transformer_options['cond_or_uncond'])): + UNCOND = transformer_options['cond_or_uncond'][cond_iter] == 1 + + bsz_style = y0_style.shape[0] if y0_style_active else 0 + bsz = 1 if RECON_MODE else bsz_style + 1 + + h, timesteps, context = clone_inputs(h_orig[cond_iter].unsqueeze(0), timesteps_orig[cond_iter].unsqueeze(0), context_orig[cond_iter].unsqueeze(0)) + y = y_orig[cond_iter].unsqueeze(0).clone() if y_orig is not None else None + + + mask, mask_up, mask_down, mask_down2 = None, None, None, None + if not UNCOND and 'AttnMask' in transformer_options: # and weight != 0: + AttnMask = transformer_options['AttnMask'] + mask = transformer_options['AttnMask'].attn_mask.mask.to('cuda') + mask_up = transformer_options['AttnMask'].mask_up.to('cuda') + mask_down = transformer_options['AttnMask'].mask_down.to('cuda') + if hasattr(transformer_options['AttnMask'], "mask_down2"): + mask_down2 = transformer_options['AttnMask'].mask_down2.to('cuda') + if weight == 0: + context = transformer_options['RegContext'].context.to(context.dtype).to(context.device) + mask, mask_up, mask_down, mask_down2 = None, None, None, None + else: + context = transformer_options['RegContext'].context.to(context.dtype).to(context.device) + + txt_len = context.shape[1] + if mask_zero is None: + mask_zero = torch.ones_like(mask) + mask_zero[:, :txt_len] = mask[:, :txt_len] + if mask_up_zero is None: + mask_up_zero = torch.ones_like(mask_up) + mask_up_zero[:, :txt_len] = mask_up[:, :txt_len] + if mask_down_zero is None: + mask_down_zero = torch.ones_like(mask_down) + mask_down_zero[:, :txt_len] = mask_down[:, :txt_len] + if mask_down2_zero is None and mask_down2 is not None: + mask_down2_zero = torch.ones_like(mask_down2) + mask_down2_zero[:, :txt_len] = mask_down2[:, :txt_len] + + + if UNCOND and 'AttnMask_neg' in transformer_options: # and weight != 0: + AttnMask = transformer_options['AttnMask_neg'] + mask = transformer_options['AttnMask_neg'].attn_mask.mask.to('cuda') + mask_up = transformer_options['AttnMask_neg'].mask_up.to('cuda') + mask_down = transformer_options['AttnMask_neg'].mask_down.to('cuda') + if hasattr(transformer_options['AttnMask_neg'], "mask_down2"): + mask_down2 = transformer_options['AttnMask_neg'].mask_down2.to('cuda') + if weight == 0: + context = transformer_options['RegContext_neg'].context.to(context.dtype).to(context.device) + mask, mask_up, mask_down, mask_down2 = None, None, None, None + else: + context = transformer_options['RegContext_neg'].context.to(context.dtype).to(context.device) + + txt_len = context.shape[1] + if mask_zero is None: + mask_zero = torch.ones_like(mask) + mask_zero[:, :txt_len] = mask[:, :txt_len] + if mask_up_zero is None: + mask_up_zero = torch.ones_like(mask_up) + mask_up_zero[:, :txt_len] = mask_up[:, :txt_len] + if mask_down_zero is None: + mask_down_zero = torch.ones_like(mask_down) + mask_down_zero[:, :txt_len] = mask_down[:, :txt_len] + if mask_down2_zero is None and mask_down2 is not None: + mask_down2_zero = torch.ones_like(mask_down2) + mask_down2_zero[:, :txt_len] = mask_down2[:, :txt_len] + + elif UNCOND and 'AttnMask' in transformer_options: + AttnMask = transformer_options['AttnMask'] + mask = transformer_options['AttnMask'].attn_mask.mask.to('cuda') + mask_up = transformer_options['AttnMask'].mask_up.to('cuda') + mask_down = transformer_options['AttnMask'].mask_down.to('cuda') + if hasattr(transformer_options['AttnMask'], "mask_down2"): + mask_down2 = transformer_options['AttnMask'].mask_down2.to('cuda') + A = context + B = transformer_options['RegContext'].context + context = A.repeat(1, (B.shape[1] // A.shape[1]) + 1, 1)[:, :B.shape[1], :] + + txt_len = context.shape[1] + if mask_zero is None: + mask_zero = torch.ones_like(mask) + mask_zero[:, :txt_len] = mask[:, :txt_len] + if mask_up_zero is None: + mask_up_zero = torch.ones_like(mask_up) + mask_up_zero[:, :txt_len] = mask_up[:, :txt_len] + if mask_down_zero is None: + mask_down_zero = torch.ones_like(mask_down) + mask_down_zero[:, :txt_len] = mask_down[:, :txt_len] + if mask_down2_zero is None and mask_down2 is not None: + mask_down2_zero = torch.ones_like(mask_down2) + mask_down2_zero[:, :txt_len] = mask_down2[:, :txt_len] + if weight == 0: # ADDED 5/23/2025 + mask, mask_up, mask_down, mask_down2 = None, None, None, None + + + if mask is not None: + if mask is not None and not type(mask[0][0] .item()) == bool: + mask = mask .to(x.dtype) + if mask_up is not None and not type(mask_up[0][0] .item()) == bool: + mask_up = mask_up .to(x.dtype) + if mask_down is not None and not type(mask_down[0][0] .item()) == bool: + mask_down = mask_down .to(x.dtype) + if mask_down2 is not None and not type(mask_down2[0][0] .item()) == bool: + mask_down2 = mask_down2 .to(x.dtype) + + if mask_zero is not None and not type(mask_zero[0][0] .item()) == bool: + mask_zero = mask_zero .to(x.dtype) + if mask_up_zero is not None and not type(mask_up_zero[0][0] .item()) == bool: + mask_up_zero = mask_up_zero .to(x.dtype) + if mask_down_zero is not None and not type(mask_down_zero[0][0] .item()) == bool: + mask_down_zero = mask_down_zero .to(x.dtype) + if mask_down2_zero is not None and not type(mask_down2_zero[0][0].item()) == bool: + mask_down2_zero = mask_down2_zero.to(x.dtype) + + transformer_options['cross_mask'] = mask [:,:txt_len] + transformer_options['self_mask'] = mask [:,txt_len:] + transformer_options['cross_mask_up'] = mask_up [:,:txt_len] + transformer_options['self_mask_up'] = mask_up [:,txt_len:] + transformer_options['cross_mask_down'] = mask_down [:,:txt_len] + transformer_options['self_mask_down'] = mask_down [:,txt_len:] + transformer_options['cross_mask_down2'] = mask_down2[:,:txt_len] if mask_down2 is not None else None + transformer_options['self_mask_down2'] = mask_down2[:,txt_len:] if mask_down2 is not None else None + + #h = x + if y0_style_active and not RECON_MODE: + if mask is None: + context, y, _ = StyleMMDiT.apply_style_conditioning( + UNCOND = UNCOND, + base_context = context, + base_y = y, + base_llama3 = None, + ) + else: + context = context.repeat(bsz_style + 1, 1, 1) + y = y.repeat(bsz_style + 1, 1) if y is not None else None + h = torch.cat([h, y0_style_noised[cond_iter:cond_iter+1]], dim=0).to(h) + + + + total_layers = len(self.input_blocks) + len(self.middle_block) + len(self.output_blocks) + + num_video_frames = kwargs.get("num_video_frames", self.default_num_video_frames) + image_only_indicator = kwargs.get("image_only_indicator", None) + time_context = kwargs.get("time_context", None) + + assert (y is not None) == ( + self.num_classes is not None + ), "must specify y if and only if the model is class-conditional" + hs, hs_adain = [], [] + t_emb = timestep_embedding(timesteps, self.model_channels, repeat_only=False).to(x.dtype) + emb = self.time_embed(t_emb) + + if "emb_patch" in transformer_patches: + patch = transformer_patches["emb_patch"] + for p in patch: + emb = p(emb, self.model_channels, transformer_options) + + if self.num_classes is not None: + assert y.shape[0] == h.shape[0] + emb = emb + self.label_emb(y) + + #for id, module in enumerate(self.input_blocks): + for id, (module, style_block) in enumerate(zip(self.input_blocks, StyleMMDiT.input_blocks)): + transformer_options["block"] = ("input", id) + + if mask is not None: + transformer_options['cross_mask'] = mask [:,:txt_len] + transformer_options['self_mask'] = mask [:,txt_len:] + transformer_options['cross_mask_up'] = mask_up [:,:txt_len] + transformer_options['self_mask_up'] = mask_up [:,txt_len:] + transformer_options['cross_mask_down'] = mask_down [:,:txt_len] + transformer_options['self_mask_down'] = mask_down [:,txt_len:] + transformer_options['cross_mask_down2'] = mask_down2[:,:txt_len] if mask_down2 is not None else None + transformer_options['self_mask_down2'] = mask_down2[:,txt_len:] if mask_down2 is not None else None + + if weight > 0 and mask is not None and weight < id/total_layers: + transformer_options['cross_mask'] = None + transformer_options['self_mask'] = None + + elif weight < 0 and mask is not None and abs(weight) < (1 - id/total_layers): + transformer_options['cross_mask'] = None + transformer_options['self_mask'] = None + + elif floor > 0 and mask is not None and floor > id/total_layers: + transformer_options['cross_mask'] = mask_zero [:,:txt_len] + transformer_options['self_mask'] = mask_zero [:,txt_len:] + transformer_options['cross_mask_up'] = mask_up_zero [:,:txt_len] + transformer_options['self_mask_up'] = mask_up_zero [:,txt_len:] + transformer_options['cross_mask_down'] = mask_down_zero [:,:txt_len] + transformer_options['self_mask_down'] = mask_down_zero [:,txt_len:] + transformer_options['cross_mask_down2'] = mask_down2_zero[:,:txt_len] if mask_down2_zero is not None else None + transformer_options['self_mask_down2'] = mask_down2_zero[:,txt_len:] if mask_down2_zero is not None else None + + elif floor < 0 and mask is not None and abs(floor) > (1 - id/total_layers): + transformer_options['cross_mask'] = mask_zero [:,:txt_len] + transformer_options['self_mask'] = mask_zero [:,txt_len:] + transformer_options['cross_mask_up'] = mask_up_zero [:,:txt_len] + transformer_options['self_mask_up'] = mask_up_zero [:,txt_len:] + transformer_options['cross_mask_down'] = mask_down_zero [:,:txt_len] + transformer_options['self_mask_down'] = mask_down_zero [:,txt_len:] + transformer_options['cross_mask_down2'] = mask_down2_zero[:,:txt_len] if mask_down2_zero is not None else None + transformer_options['self_mask_down2'] = mask_down2_zero[:,txt_len:] if mask_down2_zero is not None else None + + h = forward_timestep_embed(module, h, emb, context, transformer_options, time_context=time_context, num_video_frames=num_video_frames, image_only_indicator=image_only_indicator, style_block=style_block) + if id == 0: + h = StyleMMDiT(h, "proj_in") + h = apply_control(h, control, 'input') + if "input_block_patch" in transformer_patches: + patch = transformer_patches["input_block_patch"] + for p in patch: + h = p(h, transformer_options) + + hs.append(h) + + if "input_block_patch_after_skip" in transformer_patches: + patch = transformer_patches["input_block_patch_after_skip"] + for p in patch: + h = p(h, transformer_options) + + transformer_options["block"] = ("middle", 0) + if self.middle_block is not None: + style_block = StyleMMDiT.middle_blocks[0] + + if mask is not None: + transformer_options['cross_mask'] = mask [:,:txt_len] + transformer_options['self_mask'] = mask [:,txt_len:] + transformer_options['cross_mask_up'] = mask_up [:,:txt_len] + transformer_options['self_mask_up'] = mask_up [:,txt_len:] + transformer_options['cross_mask_down'] = mask_down [:,:txt_len] + transformer_options['self_mask_down'] = mask_down [:,txt_len:] + transformer_options['cross_mask_down2'] = mask_down2[:,:txt_len] if mask_down2 is not None else None + transformer_options['self_mask_down2'] = mask_down2[:,txt_len:] if mask_down2 is not None else None + + if weight > 0 and mask is not None and weight < (len(self.input_blocks) + 1)/total_layers: + transformer_options['cross_mask'] = None + transformer_options['self_mask'] = None + + elif weight < 0 and mask is not None and abs(weight) < (1 - (len(self.input_blocks) + 1)/total_layers): + transformer_options['cross_mask'] = None + transformer_options['self_mask'] = None + + elif floor > 0 and mask is not None and floor > (len(self.input_blocks) + 1)/total_layers: + transformer_options['cross_mask'] = mask_zero [:,:txt_len] + transformer_options['self_mask'] = mask_zero [:,txt_len:] + transformer_options['cross_mask_up'] = mask_up_zero [:,:txt_len] + transformer_options['self_mask_up'] = mask_up_zero [:,txt_len:] + transformer_options['cross_mask_down'] = mask_down_zero [:,:txt_len] + transformer_options['self_mask_down'] = mask_down_zero [:,txt_len:] + transformer_options['cross_mask_down2'] = mask_down2_zero[:,:txt_len] if mask_down2_zero is not None else None + transformer_options['self_mask_down2'] = mask_down2_zero[:,txt_len:] if mask_down2_zero is not None else None + + elif floor < 0 and mask is not None and abs(floor) > (1 - (len(self.input_blocks) + 1)/total_layers): + transformer_options['cross_mask'] = mask_zero [:,:txt_len] + transformer_options['self_mask'] = mask_zero [:,txt_len:] + transformer_options['cross_mask_up'] = mask_up_zero [:,:txt_len] + transformer_options['self_mask_up'] = mask_up_zero [:,txt_len:] + transformer_options['cross_mask_down'] = mask_down_zero [:,:txt_len] + transformer_options['self_mask_down'] = mask_down_zero [:,txt_len:] + transformer_options['cross_mask_down2'] = mask_down2_zero[:,:txt_len] if mask_down2_zero is not None else None + transformer_options['self_mask_down2'] = mask_down2_zero[:,txt_len:] if mask_down2_zero is not None else None + + h = forward_timestep_embed(self.middle_block, h, emb, context, transformer_options, time_context=time_context, num_video_frames=num_video_frames, image_only_indicator=image_only_indicator, style_block=style_block) + + h = apply_control(h, control, 'middle') + + #for id, module in enumerate(self.output_blocks): + for id, (module, style_block) in enumerate(zip(self.output_blocks, StyleMMDiT.output_blocks)): + transformer_options["block"] = ("output", id) + + hsp = hs.pop() + hsp = apply_control(hsp, control, 'output') + + if "output_block_patch" in transformer_patches: + patch = transformer_patches["output_block_patch"] + for p in patch: + h, hsp = p(h, hsp, transformer_options) + + h = th.cat([h, hsp], dim=1) + del hsp + if len(hs) > 0: + output_shape = hs[-1].shape + else: + output_shape = None + + + + if mask is not None: + transformer_options['cross_mask'] = mask [:,:txt_len] + transformer_options['self_mask'] = mask [:,txt_len:] + transformer_options['cross_mask_up'] = mask_up [:,:txt_len] + transformer_options['self_mask_up'] = mask_up [:,txt_len:] + transformer_options['cross_mask_down'] = mask_down [:,:txt_len] + transformer_options['self_mask_down'] = mask_down [:,txt_len:] + transformer_options['cross_mask_down2'] = mask_down2[:,:txt_len] if mask_down2 is not None else None + transformer_options['self_mask_down2'] = mask_down2[:,txt_len:] if mask_down2 is not None else None + + if weight > 0 and mask is not None and weight < (len(self.input_blocks) + 1 + id)/total_layers: + transformer_options['cross_mask'] = None + transformer_options['self_mask'] = None + + elif weight < 0 and mask is not None and abs(weight) < (1 - (len(self.input_blocks) + 1 + id)/total_layers): + transformer_options['cross_mask'] = None + transformer_options['self_mask'] = None + + elif floor > 0 and mask is not None and floor > (len(self.input_blocks) + 1 + id)/total_layers: + transformer_options['cross_mask'] = mask_zero [:,:txt_len] + transformer_options['self_mask'] = mask_zero [:,txt_len:] + transformer_options['cross_mask_up'] = mask_up_zero [:,:txt_len] + transformer_options['self_mask_up'] = mask_up_zero [:,txt_len:] + transformer_options['cross_mask_down'] = mask_down_zero [:,:txt_len] + transformer_options['self_mask_down'] = mask_down_zero [:,txt_len:] + transformer_options['cross_mask_down2'] = mask_down2_zero[:,:txt_len] if mask_down2_zero is not None else None + transformer_options['self_mask_down2'] = mask_down2_zero[:,txt_len:] if mask_down2_zero is not None else None + + elif floor < 0 and mask is not None and abs(floor) > (1 - (len(self.input_blocks) + 1 + id)/total_layers): + transformer_options['cross_mask'] = mask_zero [:,:txt_len] + transformer_options['self_mask'] = mask_zero [:,txt_len:] + transformer_options['cross_mask_up'] = mask_up_zero [:,:txt_len] + transformer_options['self_mask_up'] = mask_up_zero [:,txt_len:] + transformer_options['cross_mask_down'] = mask_down_zero [:,:txt_len] + transformer_options['self_mask_down'] = mask_down_zero [:,txt_len:] + transformer_options['cross_mask_down2'] = mask_down2_zero[:,:txt_len] if mask_down2_zero is not None else None + transformer_options['self_mask_down2'] = mask_down2_zero[:,txt_len:] if mask_down2_zero is not None else None + + h = forward_timestep_embed(module, h, emb, context, transformer_options, output_shape, time_context=time_context, num_video_frames=num_video_frames, image_only_indicator=image_only_indicator, style_block=style_block) + + h = h.type(x.dtype) + + if self.predict_codebook_ids: + eps = self.id_predictor(h) + else: + eps = self.out(h) + eps = StyleMMDiT(eps, "proj_out") + + out_list.append(eps[0:1]) + + eps = torch.stack(out_list, dim=0).squeeze(dim=1) + + if recon_iter == 1: + denoised = new_x * ((ISIGMA ** 2 + 1) ** 0.5) - ISIGMA.to(new_x) * eps.to(new_x) + if x_tmp is not None: + eps = (x_tmp * ((SIGMA ** 2 + 1) ** 0.5) - denoised.to(x_tmp)) / SIGMA.to(x_tmp) + else: + eps = (x_orig * ((SIGMA ** 2 + 1) ** 0.5) - denoised.to(x_orig)) / SIGMA.to(x_orig) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + y0_style_pos = transformer_options.get("y0_style_pos") + y0_style_neg = transformer_options.get("y0_style_neg") + + y0_style_pos_weight = transformer_options.get("y0_style_pos_weight", 0.0) + y0_style_pos_synweight = transformer_options.get("y0_style_pos_synweight", 0.0) + y0_style_pos_synweight *= y0_style_pos_weight + + y0_style_neg_weight = transformer_options.get("y0_style_neg_weight", 0.0) + y0_style_neg_synweight = transformer_options.get("y0_style_neg_synweight", 0.0) + y0_style_neg_synweight *= y0_style_neg_weight + + + freqsep_lowpass_method = transformer_options.get("freqsep_lowpass_method") + freqsep_sigma = transformer_options.get("freqsep_sigma") + freqsep_kernel_size = transformer_options.get("freqsep_kernel_size") + freqsep_inner_kernel_size = transformer_options.get("freqsep_inner_kernel_size") + freqsep_stride = transformer_options.get("freqsep_stride") + + freqsep_lowpass_weight = transformer_options.get("freqsep_lowpass_weight") + freqsep_highpass_weight= transformer_options.get("freqsep_highpass_weight") + freqsep_mask = transformer_options.get("freqsep_mask") + + + + dtype = eps.dtype if self.style_dtype is None else self.style_dtype + h_len //= self.Retrojector.patch_size + w_len //= self.Retrojector.patch_size + + if y0_style_pos is not None: + y0_style_pos_weight = transformer_options.get("y0_style_pos_weight") + y0_style_pos_synweight = transformer_options.get("y0_style_pos_synweight") + y0_style_pos_synweight *= y0_style_pos_weight + y0_style_pos_mask = transformer_options.get("y0_style_pos_mask") + y0_style_pos_mask_edge = transformer_options.get("y0_style_pos_mask_edge") + + y0_style_pos = y0_style_pos.to(dtype) + #x = x.to(dtype) + x = x_orig.clone().to(torch.float64) * ((SIGMA ** 2 + 1) ** 0.5) + eps = eps.to(dtype) + eps_orig = eps.clone() + + sigma = SIGMA #t_orig[0].to(torch.float32) / 1000 + denoised = x - sigma * eps + + denoised_embed = self.Retrojector.embed(denoised) # 2,4,96,168 -> 2,16128,320 + y0_adain_embed = self.Retrojector.embed(y0_style_pos) + + if transformer_options['y0_style_method'] == "scattersort": + tile_h, tile_w = transformer_options.get('y0_style_tile_height'), transformer_options.get('y0_style_tile_width') + pad = transformer_options.get('y0_style_tile_padding') + if pad is not None and tile_h is not None and tile_w is not None: + + denoised_spatial = rearrange(denoised_embed, "b (h w) c -> b c h w", h=h_len, w=w_len) + y0_adain_spatial = rearrange(y0_adain_embed, "b (h w) c -> b c h w", h=h_len, w=w_len) + + if EO("scattersort_median_LP"): + denoised_spatial_LP = median_blur_2d(denoised_spatial, kernel_size=EO("scattersort_median_LP",7)) + y0_adain_spatial_LP = median_blur_2d(y0_adain_spatial, kernel_size=EO("scattersort_median_LP",7)) + + denoised_spatial_HP = denoised_spatial - denoised_spatial_LP + y0_adain_spatial_HP = y0_adain_spatial - y0_adain_spatial_LP + + denoised_spatial_LP = apply_scattersort_tiled(denoised_spatial_LP, y0_adain_spatial_LP, tile_h, tile_w, pad) + + denoised_spatial = denoised_spatial_LP + denoised_spatial_HP + denoised_embed = rearrange(denoised_spatial, "b c h w -> b (h w) c") + else: + denoised_spatial = apply_scattersort_tiled(denoised_spatial, y0_adain_spatial, tile_h, tile_w, pad) + + denoised_embed = rearrange(denoised_spatial, "b c h w -> b (h w) c") + + else: + denoised_embed = apply_scattersort_masked(denoised_embed, y0_adain_embed, y0_style_pos_mask, y0_style_pos_mask_edge, h_len, w_len) + + + + elif transformer_options['y0_style_method'] == "AdaIN": + if freqsep_mask is not None: + freqsep_mask = freqsep_mask.view(1, 1, *freqsep_mask.shape[-2:]).float() + freqsep_mask = F.interpolate(freqsep_mask.float(), size=(h_len, w_len), mode='nearest-exact') + + if hasattr(self, "adain_tile"): + tile_h, tile_w = self.adain_tile + + denoised_pretile = rearrange(denoised_embed, "b (h w) c -> b c h w", h=h_len, w=w_len) + y0_adain_pretile = rearrange(y0_adain_embed, "b (h w) c -> b c h w", h=h_len, w=w_len) + + if self.adain_flag: + h_off = tile_h // 2 + w_off = tile_w // 2 + denoised_pretile = denoised_pretile[:,:,h_off:-h_off, w_off:-w_off] + self.adain_flag = False + else: + h_off = 0 + w_off = 0 + self.adain_flag = True + + tiles, orig_shape, grid, strides = tile_latent(denoised_pretile, tile_size=(tile_h,tile_w)) + y0_tiles, orig_shape, grid, strides = tile_latent(y0_adain_pretile, tile_size=(tile_h,tile_w)) + + tiles_out = [] + for i in range(tiles.shape[0]): + tile = tiles[i].unsqueeze(0) + y0_tile = y0_tiles[i].unsqueeze(0) + + tile = rearrange(tile, "b c h w -> b (h w) c", h=tile_h, w=tile_w) + y0_tile = rearrange(y0_tile, "b c h w -> b (h w) c", h=tile_h, w=tile_w) + + tile = adain_seq_inplace(tile, y0_tile) + tiles_out.append(rearrange(tile, "b (h w) c -> b c h w", h=tile_h, w=tile_w)) + + tiles_out_tensor = torch.cat(tiles_out, dim=0) + tiles_out_tensor = untile_latent(tiles_out_tensor, orig_shape, grid, strides) + + if h_off == 0: + denoised_pretile = tiles_out_tensor + else: + denoised_pretile[:,:,h_off:-h_off, w_off:-w_off] = tiles_out_tensor + denoised_embed = rearrange(denoised_pretile, "b c h w -> b (h w) c", h=h_len, w=w_len) + + elif freqsep_lowpass_method is not None and freqsep_lowpass_method.endswith("pw"): #EO("adain_pw"): + + denoised_spatial = rearrange(denoised_embed, "b (h w) c -> b c h w", h=h_len, w=w_len) + y0_adain_spatial = rearrange(y0_adain_embed, "b (h w) c -> b c h w", h=h_len, w=w_len) + + if freqsep_lowpass_method == "median_pw": + denoised_spatial_new = adain_patchwise_row_batch_med(denoised_spatial.clone(), y0_adain_spatial.clone().repeat(denoised_spatial.shape[0],1,1,1), sigma=freqsep_sigma, kernel_size=freqsep_kernel_size, use_median_blur=True, lowpass_weight=freqsep_lowpass_weight, highpass_weight=freqsep_highpass_weight) + elif freqsep_lowpass_method == "gaussian_pw": + denoised_spatial_new = adain_patchwise_row_batch(denoised_spatial.clone(), y0_adain_spatial.clone().repeat(denoised_spatial.shape[0],1,1,1), sigma=freqsep_sigma, kernel_size=freqsep_kernel_size) + + denoised_embed = rearrange(denoised_spatial_new, "b c h w -> b (h w) c", h=h_len, w=w_len) + + elif freqsep_lowpass_method is not None: + denoised_spatial = rearrange(denoised_embed, "b (h w) c -> b c h w", h=h_len, w=w_len) + y0_adain_spatial = rearrange(y0_adain_embed, "b (h w) c -> b c h w", h=h_len, w=w_len) + + if freqsep_lowpass_method == "median": + denoised_spatial_LP = median_blur_2d(denoised_spatial, kernel_size=freqsep_kernel_size) + y0_adain_spatial_LP = median_blur_2d(y0_adain_spatial, kernel_size=freqsep_kernel_size) + elif freqsep_lowpass_method == "gaussian": + denoised_spatial_LP = gaussian_blur_2d(denoised_spatial, sigma=freqsep_sigma, kernel_size=freqsep_kernel_size) + y0_adain_spatial_LP = gaussian_blur_2d(y0_adain_spatial, sigma=freqsep_sigma, kernel_size=freqsep_kernel_size) + + denoised_spatial_HP = denoised_spatial - denoised_spatial_LP + + if EO("adain_fs_uhp"): + y0_adain_spatial_HP = y0_adain_spatial - y0_adain_spatial_LP + + denoised_spatial_ULP = gaussian_blur_2d(denoised_spatial, sigma=EO("adain_fs_uhp_sigma", 1.0), kernel_size=EO("adain_fs_uhp_kernel_size", 3)) + y0_adain_spatial_ULP = gaussian_blur_2d(y0_adain_spatial, sigma=EO("adain_fs_uhp_sigma", 1.0), kernel_size=EO("adain_fs_uhp_kernel_size", 3)) + + denoised_spatial_UHP = denoised_spatial_HP - denoised_spatial_ULP + y0_adain_spatial_UHP = y0_adain_spatial_HP - y0_adain_spatial_ULP + + #denoised_spatial_HP = y0_adain_spatial_ULP + denoised_spatial_UHP + denoised_spatial_HP = denoised_spatial_ULP + y0_adain_spatial_UHP + + denoised_spatial_new = freqsep_lowpass_weight * y0_adain_spatial_LP + freqsep_highpass_weight * denoised_spatial_HP + denoised_embed = rearrange(denoised_spatial_new, "b c h w -> b (h w) c", h=h_len, w=w_len) + + else: + denoised_embed = adain_seq_inplace(denoised_embed, y0_adain_embed) + + for adain_iter in range(EO("style_iter", 0)): + denoised_embed = adain_seq_inplace(denoised_embed, y0_adain_embed) + denoised_embed = self.Retrojector.embed(self.Retrojector.unembed(denoised_embed)) + denoised_embed = adain_seq_inplace(denoised_embed, y0_adain_embed) + + elif transformer_options['y0_style_method'] == "WCT": + self.StyleWCT.set(y0_adain_embed) + denoised_embed = self.StyleWCT.get(denoised_embed) + + if transformer_options.get('y0_standard_guide') is not None: + y0_standard_guide = transformer_options.get('y0_standard_guide') + + y0_standard_guide_embed = self.Retrojector.embed(y0_standard_guide) + f_cs = self.StyleWCT.get(y0_standard_guide_embed) + self.y0_standard_guide = self.Retrojector.unembed(f_cs) + + if transformer_options.get('y0_inv_standard_guide') is not None: + y0_inv_standard_guide = transformer_options.get('y0_inv_standard_guide') + + y0_inv_standard_guide_embed = self.Retrojector.embed(y0_inv_standard_guide) + f_cs = self.StyleWCT.get(y0_inv_standard_guide_embed) + self.y0_inv_standard_guide = self.Retrojector.unembed(f_cs) + + denoised_approx = self.Retrojector.unembed(denoised_embed) + + eps = (x - denoised_approx) / sigma + + if not UNCOND: + if eps.shape[0] == 2: + eps[1] = eps_orig[1] + y0_style_pos_weight * (eps[1] - eps_orig[1]) + eps[0] = eps_orig[0] + y0_style_pos_synweight * (eps[0] - eps_orig[0]) + else: + eps[0] = eps_orig[0] + y0_style_pos_weight * (eps[0] - eps_orig[0]) + elif eps.shape[0] == 1 and UNCOND: + eps[0] = eps_orig[0] + y0_style_pos_synweight * (eps[0] - eps_orig[0]) + + eps = eps.float() + + if y0_style_neg is not None: + y0_style_neg_weight = transformer_options.get("y0_style_neg_weight") + y0_style_neg_synweight = transformer_options.get("y0_style_neg_synweight") + y0_style_neg_synweight *= y0_style_neg_weight + y0_style_neg_mask = transformer_options.get("y0_style_neg_mask") + y0_style_neg_mask_edge = transformer_options.get("y0_style_neg_mask_edge") + + y0_style_neg = y0_style_neg.to(dtype) + #x = x.to(dtype) + x = x_orig.clone().to(torch.float64) * ((SIGMA ** 2 + 1) ** 0.5) + eps = eps.to(dtype) + eps_orig = eps.clone() + + sigma = SIGMA #t_orig[0].to(torch.float32) / 1000 + denoised = x - sigma * eps + + denoised_embed = self.Retrojector.embed(denoised) + y0_adain_embed = self.Retrojector.embed(y0_style_neg) + + if transformer_options['y0_style_method'] == "scattersort": + tile_h, tile_w = transformer_options.get('y0_style_tile_height'), transformer_options.get('y0_style_tile_width') + pad = transformer_options.get('y0_style_tile_padding') + if pad is not None and tile_h is not None and tile_w is not None: + + denoised_spatial = rearrange(denoised_embed, "b (h w) c -> b c h w", h=h_len, w=w_len) + y0_adain_spatial = rearrange(y0_adain_embed, "b (h w) c -> b c h w", h=h_len, w=w_len) + + denoised_spatial = apply_scattersort_tiled(denoised_spatial, y0_adain_spatial, tile_h, tile_w, pad) + + denoised_embed = rearrange(denoised_spatial, "b c h w -> b (h w) c") + + else: + denoised_embed = apply_scattersort_masked(denoised_embed, y0_adain_embed, y0_style_neg_mask, y0_style_neg_mask_edge, h_len, w_len) + + + elif transformer_options['y0_style_method'] == "AdaIN": + denoised_embed = adain_seq_inplace(denoised_embed, y0_adain_embed) + for adain_iter in range(EO("style_iter", 0)): + denoised_embed = adain_seq_inplace(denoised_embed, y0_adain_embed) + denoised_embed = self.Retrojector.embed(self.Retrojector.unembed(denoised_embed)) + denoised_embed = adain_seq_inplace(denoised_embed, y0_adain_embed) + + elif transformer_options['y0_style_method'] == "WCT": + self.StyleWCT.set(y0_adain_embed) + denoised_embed = self.StyleWCT.get(denoised_embed) + + denoised_approx = self.Retrojector.unembed(denoised_embed) + + if UNCOND: + eps = (x - denoised_approx) / sigma + eps[0] = eps_orig[0] + y0_style_neg_weight * (eps[0] - eps_orig[0]) + if eps.shape[0] == 2: + eps[1] = eps_orig[1] + y0_style_neg_synweight * (eps[1] - eps_orig[1]) + elif eps.shape[0] == 1 and not UNCOND: + eps[0] = eps_orig[0] + y0_style_neg_synweight * (eps[0] - eps_orig[0]) + + eps = eps.float() + + return eps + + + + + + + + + +def clone_inputs_unsafe(*args, index: int=None): + + if index is None: + return tuple(x.clone() for x in args) + else: + return tuple(x[index].unsqueeze(0).clone() for x in args) + + +def clone_inputs(*args, index: int = None): + if index is None: + return tuple(x.clone() if x is not None else None for x in args) + else: + return tuple(x[index].unsqueeze(0).clone() if x is not None else None for x in args) + + + diff --git a/ComfyUI/custom_nodes/RES4LYF/sd35/mmdit.py b/ComfyUI/custom_nodes/RES4LYF/sd35/mmdit.py new file mode 100644 index 0000000000000000000000000000000000000000..24d3edd49ae815698fc6f1d9016a9e06505d2634 --- /dev/null +++ b/ComfyUI/custom_nodes/RES4LYF/sd35/mmdit.py @@ -0,0 +1,1739 @@ +from functools import partial +from typing import Dict, Optional, List + +import numpy as np +import torch +import torch.nn as nn +import torch.nn.functional as F + +import copy + +from comfy.ldm.modules.attention import optimized_attention +from comfy.ldm.modules.attention import attention_pytorch #as optimized_attention +from einops import rearrange, repeat +from comfy.ldm.modules.diffusionmodules.util import timestep_embedding +import comfy.ops +import comfy.ldm.common_dit + +from ..helper import ExtraOptions + +from ..latents import tile_latent, untile_latent, gaussian_blur_2d, median_blur_2d +from ..style_transfer import apply_scattersort_masked, apply_scattersort_tiled, adain_seq_inplace, adain_patchwise_row_batch_med, adain_patchwise_row_batch + +#from .attention import optimized_attention +#from .util import timestep_embedding +#import ops +#import common_dit + + +def default(x, y): + if x is not None: + return x + return y + +class Mlp(nn.Module): + """ MLP as used in Vision Transformer, MLP-Mixer and related networks + """ + def __init__( + self, + in_features, + hidden_features = None, + out_features = None, + act_layer = nn.GELU, + norm_layer = None, + bias = True, + drop = 0., + use_conv = False, + dtype = None, + device = None, + operations = None, + ): + super().__init__() + out_features = out_features or in_features + hidden_features = hidden_features or in_features + drop_probs = drop + linear_layer = partial(operations.Conv2d, kernel_size=1) if use_conv else operations.Linear + + self.fc1 = linear_layer(in_features, hidden_features, bias =bias, dtype=dtype, device=device) + self.act = act_layer() + self.drop1 = nn.Dropout(drop_probs) + self.norm = norm_layer(hidden_features) if norm_layer is not None else nn.Identity() + self.fc2 = linear_layer(hidden_features, out_features, bias=bias, dtype=dtype, device=device) + self.drop2 = nn.Dropout(drop_probs) + + def forward(self, x): + x = self.fc1 (x) + x = self.act (x) + x = self.drop1(x) + x = self.norm (x) + x = self.fc2 (x) + x = self.drop2(x) + return x + +class PatchEmbed(nn.Module): + """ 2D Image to Patch Embedding + """ + dynamic_img_pad: torch.jit.Final[bool] + + def __init__( + self, + img_size : Optional[int] = 224, + patch_size : int = 16, + in_chans : int = 3, + embed_dim : int = 768, + norm_layer = None, + flatten : bool = True, + bias : bool = True, + strict_img_size : bool = True, + dynamic_img_pad : bool = True, + padding_mode ='circular', + conv3d = False, + dtype = None, + device = None, + operations = None, + ): + super().__init__() + try: + len(patch_size) + self.patch_size = patch_size + except: + if conv3d: + self.patch_size = (patch_size, patch_size, patch_size) + else: + self.patch_size = (patch_size, patch_size) + self.padding_mode = padding_mode + + # flatten spatial dim and transpose to channels last, kept for bwd compat + self.flatten = flatten + self.strict_img_size = strict_img_size + self.dynamic_img_pad = dynamic_img_pad + if conv3d: + self.proj = operations.Conv3d(in_chans, embed_dim, kernel_size=patch_size, stride=patch_size, bias=bias, dtype=dtype, device=device) + else: + self.proj = operations.Conv2d(in_chans, embed_dim, kernel_size=patch_size, stride=patch_size, bias=bias, dtype=dtype, device=device) + self.norm = norm_layer(embed_dim) if norm_layer else nn.Identity() + + def forward(self, x): + if self.dynamic_img_pad: + x = comfy.ldm.common_dit.pad_to_patch_size(x, self.patch_size, padding_mode=self.padding_mode) + x = self.proj(x) + if self.flatten: + x = x.flatten(2).transpose(1, 2) # NCHW -> NLC + x = self.norm(x) + return x + +def modulate(x, shift, scale): + if shift is None: + shift = torch.zeros_like(scale) + return x * (1 + scale.unsqueeze(1)) + shift.unsqueeze(1) + + +################################################################################# +# Sine/Cosine Positional Embedding Functions # +################################################################################# + + +def get_2d_sincos_pos_embed( + embed_dim, + grid_size, + cls_token = False, + extra_tokens = 0, + scaling_factor = None, + offset = None, +): + """ + grid_size: int of the grid height and width + return: + pos_embed: [grid_size*grid_size, embed_dim] or [1+grid_size*grid_size, embed_dim] (w/ or w/o cls_token) + """ + grid_h = np.arange(grid_size, dtype=np.float32) + grid_w = np.arange(grid_size, dtype=np.float32) + grid = np.meshgrid(grid_w, grid_h) # here w goes first + grid = np.stack(grid, axis=0) + if scaling_factor is not None: + grid = grid / scaling_factor + if offset is not None: + grid = grid - offset + + grid = grid.reshape([2, 1, grid_size, grid_size]) + pos_embed = get_2d_sincos_pos_embed_from_grid(embed_dim, grid) + if cls_token and extra_tokens > 0: + pos_embed = np.concatenate( + [np.zeros([extra_tokens, embed_dim]), pos_embed], axis=0 + ) + return pos_embed + + +def get_2d_sincos_pos_embed_from_grid(embed_dim, grid): + assert embed_dim % 2 == 0 + + # use half of dimensions to encode grid_h + emb_h = get_1d_sincos_pos_embed_from_grid(embed_dim // 2, grid[0]) # (H*W, D/2) + emb_w = get_1d_sincos_pos_embed_from_grid(embed_dim // 2, grid[1]) # (H*W, D/2) + + emb = np.concatenate([emb_h, emb_w], axis=1) # (H*W, D) + return emb + + +def get_1d_sincos_pos_embed_from_grid(embed_dim, pos): + """ + embed_dim: output dimension for each position + pos: a list of positions to be encoded: size (M,) + out: (M, D) + """ + assert embed_dim % 2 == 0 + omega = np.arange(embed_dim // 2, dtype=np.float64) + omega /= embed_dim / 2.0 + omega = 1.0 / 10000**omega # (D/2,) + + pos = pos.reshape(-1) # (M,) + out = np.einsum("m,d->md", pos, omega) # (M, D/2), outer product + + emb_sin = np.sin(out) # (M, D/2) + emb_cos = np.cos(out) # (M, D/2) + + emb = np.concatenate([emb_sin, emb_cos], axis=1) # (M, D) + return emb + +def get_1d_sincos_pos_embed_from_grid_torch(embed_dim, pos, device=None, dtype=torch.float32): + omega = torch.arange(embed_dim // 2, device=device, dtype=dtype) + omega /= embed_dim / 2.0 + omega = 1.0 / 10000**omega # (D/2,) + pos = pos.reshape(-1) # (M,) + out = torch.einsum("m,d->md", pos, omega) # (M, D/2), outer product + emb_sin = torch.sin(out) # (M, D/2) + emb_cos = torch.cos(out) # (M, D/2) + emb = torch.cat([emb_sin, emb_cos], dim=1) # (M, D) + return emb + +def get_2d_sincos_pos_embed_torch(embed_dim, w, h, val_center=7.5, val_magnitude=7.5, device=None, dtype=torch.float32): + small = min(h, w) + val_h = (h / small) * val_magnitude + val_w = (w / small) * val_magnitude + grid_h, grid_w = torch.meshgrid(torch.linspace(-val_h + val_center, val_h + val_center, h, device=device, dtype=dtype), torch.linspace(-val_w + val_center, val_w + val_center, w, device=device, dtype=dtype), indexing='ij') + emb_h = get_1d_sincos_pos_embed_from_grid_torch(embed_dim // 2, grid_h, device=device, dtype=dtype) + emb_w = get_1d_sincos_pos_embed_from_grid_torch(embed_dim // 2, grid_w, device=device, dtype=dtype) + emb = torch.cat([emb_w, emb_h], dim=1) # (H*W, D) + return emb + + +################################################################################# +# Embedding Layers for Timesteps and Class Labels # +################################################################################# + + +class TimestepEmbedder(nn.Module): + """ + Embeds scalar timesteps into vector representations. + """ + + def __init__(self, hidden_size, frequency_embedding_size=256, dtype=None, device=None, operations=None): + super().__init__() + self.mlp = nn.Sequential( + operations.Linear(frequency_embedding_size, hidden_size, bias=True, dtype=dtype, device=device), + nn.SiLU(), + operations.Linear(hidden_size, hidden_size, bias=True, dtype=dtype, device=device), + ) + self.frequency_embedding_size = frequency_embedding_size + + def forward(self, t, dtype, **kwargs): + t_freq = timestep_embedding(t, self.frequency_embedding_size).to(dtype) + t_emb = self.mlp(t_freq) + return t_emb + + +class VectorEmbedder(nn.Module): + """ + Embeds a flat vector of dimension input_dim + """ + + def __init__(self, input_dim: int, hidden_size: int, dtype=None, device=None, operations=None): + super().__init__() + self.mlp = nn.Sequential( + operations.Linear(input_dim, hidden_size, bias=True, dtype=dtype, device=device), + nn.SiLU(), + operations.Linear(hidden_size, hidden_size, bias=True, dtype=dtype, device=device), + ) + + def forward(self, x: torch.Tensor) -> torch.Tensor: + emb = self.mlp(x) + return emb + + +################################################################################# +# Core DiT Model # +################################################################################# + + +def split_qkv(qkv, head_dim): + qkv = qkv.reshape(qkv.shape[0], qkv.shape[1], 3, -1, head_dim).movedim(2, 0) + return qkv[0], qkv[1], qkv[2] + + +class SelfAttention(nn.Module): + ATTENTION_MODES = ("xformers", "torch", "torch-hb", "math", "debug") + + def __init__( + self, + dim : int, + num_heads : int = 8, + qkv_bias : bool = False, + qk_scale : Optional[float] = None, + proj_drop : float = 0.0, + attn_mode : str = "xformers", + pre_only : bool = False, + qk_norm : Optional[str] = None, + rmsnorm : bool = False, + dtype = None, + device = None, + operations = None, + ): + super().__init__() + self.num_heads = num_heads + self.head_dim = dim // num_heads + + self.qkv = operations.Linear(dim, dim * 3, bias=qkv_bias, dtype=dtype, device=device) + if not pre_only: + self.proj = operations.Linear(dim, dim, dtype=dtype, device=device) + self.proj_drop = nn.Dropout(proj_drop) + assert attn_mode in self.ATTENTION_MODES + self.attn_mode = attn_mode + self.pre_only = pre_only + + if qk_norm == "rms": + self.ln_q = RMSNorm(self.head_dim, elementwise_affine=True, eps=1.0e-6, dtype=dtype, device=device) + self.ln_k = RMSNorm(self.head_dim, elementwise_affine=True, eps=1.0e-6, dtype=dtype, device=device) + elif qk_norm == "ln": + self.ln_q = operations.LayerNorm(self.head_dim, elementwise_affine=True, eps=1.0e-6, dtype=dtype, device=device) + self.ln_k = operations.LayerNorm(self.head_dim, elementwise_affine=True, eps=1.0e-6, dtype=dtype, device=device) + elif qk_norm is None: + self.ln_q = nn.Identity() + self.ln_k = nn.Identity() + else: + raise ValueError(qk_norm) + + def pre_attention(self, x: torch.Tensor) -> torch.Tensor: + B, L, C = x.shape + qkv = self.qkv(x) + q, k, v = split_qkv(qkv, self.head_dim) + q = self.ln_q(q).reshape(q.shape[0], q.shape[1], -1) + k = self.ln_k(k).reshape(q.shape[0], q.shape[1], -1) + return (q, k, v) + + def post_attention(self, x: torch.Tensor) -> torch.Tensor: + assert not self.pre_only + x = self.proj (x) + x = self.proj_drop(x) + return x + + def forward(self, x: torch.Tensor) -> torch.Tensor: + q, k, v = self.pre_attention(x) + x = optimized_attention( + q, k, v, heads=self.num_heads + ) + x = self.post_attention(x) + return x + + +class RMSNorm(torch.nn.Module): + def __init__( + self, dim: int, elementwise_affine: bool = False, eps: float = 1e-6, device=None, dtype=None + ): + """ + Initialize the RMSNorm normalization layer. + Args: + dim (int): The dimension of the input tensor. + eps (float, optional): A small value added to the denominator for numerical stability. Default is 1e-6. + Attributes: + eps (float): A small value added to the denominator for numerical stability. + weight (nn.Parameter): Learnable scaling parameter. + """ + super().__init__() + self.eps = eps + self.learnable_scale = elementwise_affine + if self.learnable_scale: + self.weight = nn.Parameter(torch.empty(dim, device=device, dtype=dtype)) + else: + self.register_parameter("weight", None) + + def forward(self, x): + return comfy.ldm.common_dit.rms_norm(x, self.weight, self.eps) + + + +class SwiGLUFeedForward(nn.Module): + def __init__( + self, + dim : int, + hidden_dim : int, + multiple_of : int, + ffn_dim_multiplier : Optional[float] = None, + ): + """ + Initialize the FeedForward module. + + Args: + dim (int): Input dimension. + hidden_dim (int): Hidden dimension of the feedforward layer. + multiple_of (int): Value to ensure hidden dimension is a multiple of this value. + ffn_dim_multiplier (float, optional): Custom multiplier for hidden dimension. Defaults to None. + + Attributes: + w1 (ColumnParallelLinear): Linear transformation for the first layer. + w2 (RowParallelLinear): Linear transformation for the second layer. + w3 (ColumnParallelLinear): Linear transformation for the third layer. + + """ + super().__init__() + hidden_dim = int(2 * hidden_dim / 3) + # custom dim factor multiplier + if ffn_dim_multiplier is not None: + hidden_dim = int(ffn_dim_multiplier * hidden_dim) + hidden_dim = multiple_of * ((hidden_dim + multiple_of - 1) // multiple_of) + + self.w1 = nn.Linear(dim, hidden_dim, bias=False) + self.w2 = nn.Linear(hidden_dim, dim, bias=False) + self.w3 = nn.Linear(dim, hidden_dim, bias=False) + + def forward(self, x): + return self.w2(nn.functional.silu(self.w1(x)) * self.w3(x)) + + +class DismantledBlock(nn.Module): + """ + A DiT block with gated adaptive layer norm (adaLN) conditioning. + """ + + ATTENTION_MODES = ("xformers", "torch", "torch-hb", "math", "debug") + + def __init__( + self, + hidden_size : int, + num_heads : int, + mlp_ratio : float = 4.0, + attn_mode : str = "xformers", + qkv_bias : bool = False, + pre_only : bool = False, + rmsnorm : bool = False, + scale_mod_only : bool = False, + swiglu : bool = False, + qk_norm : Optional[str] = None, + x_block_self_attn : bool = False, + dtype = None, + device = None, + operations = None, + **block_kwargs, + ): + super().__init__() + assert attn_mode in self.ATTENTION_MODES + if not rmsnorm: + self.norm1 = operations.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6, dtype=dtype, device=device) + else: + self.norm1 = RMSNorm(hidden_size, elementwise_affine=False, eps=1e-6) + self.attn = SelfAttention( + dim = hidden_size, + num_heads = num_heads, + qkv_bias = qkv_bias, + attn_mode = attn_mode, + pre_only = pre_only, + qk_norm = qk_norm, + rmsnorm = rmsnorm, + dtype = dtype, + device = device, + operations = operations + ) + if x_block_self_attn: + assert not pre_only + assert not scale_mod_only + self.x_block_self_attn = True + self.attn2 = SelfAttention( + dim = hidden_size, + num_heads = num_heads, + qkv_bias = qkv_bias, + attn_mode = attn_mode, + pre_only = False, + qk_norm = qk_norm, + rmsnorm = rmsnorm, + dtype = dtype, + device = device, + operations = operations + ) + else: + self.x_block_self_attn = False + if not pre_only: + if not rmsnorm: + self.norm2 = operations.LayerNorm( + hidden_size, elementwise_affine=False, eps=1e-6, dtype=dtype, device=device + ) + else: + self.norm2 = RMSNorm(hidden_size, elementwise_affine=False, eps=1e-6) + mlp_hidden_dim = int(hidden_size * mlp_ratio) + if not pre_only: + if not swiglu: + self.mlp = Mlp( + in_features = hidden_size, + hidden_features = mlp_hidden_dim, + act_layer = lambda: nn.GELU(approximate = "tanh"), + drop = 0, + dtype = dtype, + device = device, + operations = operations + ) + else: + self.mlp = SwiGLUFeedForward( + dim = hidden_size, + hidden_dim = mlp_hidden_dim, + multiple_of = 256, + ) + self.scale_mod_only = scale_mod_only + if x_block_self_attn: + assert not pre_only + assert not scale_mod_only + n_mods = 9 + elif not scale_mod_only: + n_mods = 6 if not pre_only else 2 + else: + n_mods = 4 if not pre_only else 1 + self.adaLN_modulation = nn.Sequential( + nn.SiLU(), operations.Linear(hidden_size, n_mods * hidden_size, bias=True, dtype=dtype, device=device) + ) + self.pre_only = pre_only + + def pre_attention(self, x: torch.Tensor, c: torch.Tensor) -> torch.Tensor: + if not self.pre_only: + if not self.scale_mod_only: + ( + shift_msa, + scale_msa, + gate_msa, + shift_mlp, + scale_mlp, + gate_mlp, + ) = self.adaLN_modulation(c).chunk(6, dim=1) + else: + shift_msa = None + shift_mlp = None + ( + scale_msa, + gate_msa, + scale_mlp, + gate_mlp, + ) = self.adaLN_modulation( + c + ).chunk(4, dim=1) + qkv = self.attn.pre_attention(modulate(self.norm1(x), shift_msa, scale_msa)) + return qkv, ( + x, + gate_msa, + shift_mlp, + scale_mlp, + gate_mlp, + ) + else: + if not self.scale_mod_only: + ( + shift_msa, + scale_msa, + ) = self.adaLN_modulation( + c + ).chunk(2, dim=1) + else: + shift_msa = None + scale_msa = self.adaLN_modulation(c) + qkv = self.attn.pre_attention(modulate(self.norm1(x), shift_msa, scale_msa)) + return qkv, None + + def post_attention(self, attn, x, gate_msa, shift_mlp, scale_mlp, gate_mlp): + assert not self.pre_only + x = x + gate_msa.unsqueeze(1) * self.attn.post_attention(attn) + x = x + gate_mlp.unsqueeze(1) * self.mlp( + modulate(self.norm2(x), shift_mlp, scale_mlp) + ) + return x + + def pre_attention_x(self, x: torch.Tensor, c: torch.Tensor) -> torch.Tensor: + assert self.x_block_self_attn + ( + shift_msa, + scale_msa, + gate_msa, + shift_mlp, + scale_mlp, + gate_mlp, + shift_msa2, + scale_msa2, + gate_msa2, + ) = self.adaLN_modulation(c).chunk(9, dim=1) + x_norm = self.norm1(x) + qkv = self.attn .pre_attention(modulate(x_norm, shift_msa, scale_msa )) + qkv2 = self.attn2.pre_attention(modulate(x_norm, shift_msa2, scale_msa2)) + return qkv, qkv2, ( + x, + gate_msa, + shift_mlp, + scale_mlp, + gate_mlp, + gate_msa2, + ) + + def post_attention_x(self, attn, attn2, x, gate_msa, shift_mlp, scale_mlp, gate_mlp, gate_msa2): + assert not self.pre_only + attn1 = self.attn .post_attention(attn) + attn2 = self.attn2.post_attention(attn2) + out1 = gate_msa .unsqueeze(1) * attn1 + out2 = gate_msa2.unsqueeze(1) * attn2 + x = x + out1 + x = x + out2 + x = x + gate_mlp.unsqueeze(1) * self.mlp( + modulate(self.norm2(x), shift_mlp, scale_mlp) + ) + return x + + def forward(self, x: torch.Tensor, c: torch.Tensor) -> torch.Tensor: + assert not self.pre_only + if self.x_block_self_attn: + qkv, qkv2, intermediates = self.pre_attention_x(x, c) + attn, _ = optimized_attention( + qkv[0], qkv[1], qkv[2], + num_heads=self.attn.num_heads, + ) + attn2, _ = optimized_attention( + qkv2[0], qkv2[1], qkv2[2], + num_heads=self.attn2.num_heads, + ) + return self.post_attention_x(attn, attn2, *intermediates) + else: + qkv, intermediates = self.pre_attention (x, c) + attn = optimized_attention( + qkv[0], qkv[1], qkv[2], + heads=self.attn.num_heads, + ) + return self.post_attention(attn, *intermediates) + + +def block_mixing(*args, use_checkpoint=True, **kwargs): + if use_checkpoint: + return torch.utils.checkpoint.checkpoint( + _block_mixing, *args, use_reentrant=False, **kwargs + ) + else: + return _block_mixing(*args, **kwargs) + +# context_qkv = Tuple[Tensor,Tensor,Tensor] 2,154,1536 2,154,1536 2,154,24,64 x_qkv 2,4096,1536, ..., 2,4096,24,64 +def _block_mixing(context, x, context_block, x_block, c, mask=None): + context_qkv, context_intermediates = context_block.pre_attention(context, c) + + if x_block.x_block_self_attn: # x_qkv2 = self-attn? + x_qkv, x_qkv2, x_intermediates = x_block.pre_attention_x(x, c) + else: + x_qkv, x_intermediates = x_block.pre_attention (x, c) + + o = [] + for t in range(3): + o.append(torch.cat((context_qkv[t], x_qkv[t]), dim=1)) + qkv = tuple(o) + + if mask is not None: + attn = attention_pytorch( #1,4186,1536 + qkv[0], qkv[1], qkv[2], + heads = x_block.attn.num_heads, + mask = mask #> 0 if mask is not None else None, + ) + else: + attn = optimized_attention( #1,4186,1536 + qkv[0], qkv[1], qkv[2], + heads = x_block.attn.num_heads, + mask = None #> 0 if mask is not None else None, + ) + + context_attn, x_attn = ( + attn[:, : context_qkv[0].shape[1] ], + attn[:, context_qkv[0].shape[1] : ], + ) + + if not context_block.pre_only: + context = context_block.post_attention(context_attn, *context_intermediates) + + else: + context = None + if x_block.x_block_self_attn: + attn2 = optimized_attention( # x_qkv2 2,4096,1536 + x_qkv2[0], x_qkv2[1], x_qkv2[2], + heads = x_block.attn2.num_heads, + ) + x = x_block.post_attention_x(x_attn, attn2, *x_intermediates) + else: + x = x_block.post_attention (x_attn, *x_intermediates) + return context, x + + +class ReJointBlock(nn.Module): + """just a small wrapper to serve as a fsdp unit""" + + def __init__( + self, + *args, + **kwargs, + ): + super().__init__() + pre_only = kwargs.pop("pre_only") + qk_norm = kwargs.pop("qk_norm", None ) + x_block_self_attn = kwargs.pop("x_block_self_attn", False) + self.context_block = DismantledBlock(*args, pre_only=pre_only, qk_norm=qk_norm, **kwargs) + self.x_block = DismantledBlock(*args, pre_only=False, qk_norm=qk_norm, x_block_self_attn=x_block_self_attn, **kwargs) + + def forward(self, *args, **kwargs): # context_block, x_block are DismantledBlock + return block_mixing( # args = Tuple[Tensor,Tensor] 2,154,1536 2,4096,1536 + *args, context_block=self.context_block, x_block=self.x_block, **kwargs + ) + + +class FinalLayer(nn.Module): + """ + The final layer of DiT. + """ + + def __init__( + self, + hidden_size : int, + patch_size : int, + out_channels : int, + total_out_channels : Optional[int] = None, + dtype = None, + device = None, + operations = None, + ): + super().__init__() + self.norm_final = operations.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6, dtype=dtype, device=device) + self.linear = ( + operations.Linear(hidden_size, patch_size * patch_size * out_channels, bias=True, dtype=dtype, device=device) + if (total_out_channels is None) + else operations.Linear(hidden_size, total_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) + ) + + def forward(self, x: torch.Tensor, c: torch.Tensor) -> torch.Tensor: + shift, scale = self.adaLN_modulation(c).chunk(2, dim=1) + x = modulate(self.norm_final(x), shift, scale) + x = self.linear(x) + return x + +class SelfAttentionContext(nn.Module): + def __init__(self, dim, heads=8, dim_head=64, dtype=None, device=None, operations=None): + super().__init__() + dim_head = dim // heads + inner_dim = dim + + self.heads = heads + self.dim_head = dim_head + + self.qkv = operations.Linear(dim, dim * 3, bias=True, dtype=dtype, device=device) + + self.proj = operations.Linear(inner_dim, dim, dtype=dtype, device=device) + + def forward(self, x): + qkv = self.qkv(x) + q, k, v = split_qkv(qkv, self.dim_head) + x = optimized_attention(q.reshape(q.shape[0], q.shape[1], -1), k, v, heads=self.heads) + return self.proj(x) + +class ContextProcessorBlock(nn.Module): + def __init__(self, context_size, dtype=None, device=None, operations=None): + super().__init__() + self.norm1 = operations.LayerNorm(context_size, elementwise_affine=False, eps=1e-6, dtype=dtype, device=device) + self.attn = SelfAttentionContext(context_size, dtype=dtype, device=device, operations=operations) + self.norm2 = operations.LayerNorm(context_size, elementwise_affine=False, eps=1e-6, dtype=dtype, device=device) + self.mlp = Mlp(in_features=context_size, hidden_features=(context_size * 4), act_layer=lambda: nn.GELU(approximate="tanh"), drop=0, dtype=dtype, device=device, operations=operations) + + def forward(self, x): + x += self.attn(self.norm1(x)) + x += self.mlp (self.norm2(x)) + return x + +class ContextProcessor(nn.Module): + def __init__(self, context_size, num_layers, dtype=None, device=None, operations=None): + super().__init__() + self.layers = torch.nn.ModuleList([ContextProcessorBlock(context_size, dtype=dtype, device=device, operations=operations) for i in range(num_layers)]) + self.norm = operations.LayerNorm(context_size, elementwise_affine=False, eps=1e-6, dtype=dtype, device=device) + + def forward(self, x): + for i, l in enumerate(self.layers): + x = l(x) + return self.norm(x) + +class MMDiT(nn.Module): + """ + Diffusion model with a Transformer backbone. + """ + + def __init__( + self, + input_size : int = 32, + patch_size : int = 2, + in_channels : int = 4, + depth : int = 28, + # hidden_size : Optional[int] = None, + # num_heads : Optional[int] = None, + mlp_ratio : float = 4.0, + learn_sigma : bool = False, + adm_in_channels : Optional[int] = None, + context_embedder_config : Optional[Dict] = None, + compile_core : bool = False, + use_checkpoint : bool = False, + register_length : int = 0, + attn_mode : str = "torch", + rmsnorm : bool = False, + scale_mod_only : bool = False, + swiglu : bool = False, + out_channels : Optional[int] = None, + pos_embed_scaling_factor : Optional[float] = None, + pos_embed_offset : Optional[float] = None, + pos_embed_max_size : Optional[int] = None, + num_patches = None, + qk_norm : Optional[str] = None, + qkv_bias : bool = True, + context_processor_layers = None, + x_block_self_attn : bool = False, + x_block_self_attn_layers : Optional[List[int]] = [], + context_size = 4096, + num_blocks = None, + final_layer = True, + skip_blocks = False, + dtype = None, #TODO + device = None, + operations = None, + ): + super().__init__() + self.dtype = dtype + self.learn_sigma = learn_sigma + self.in_channels = in_channels + default_out_channels = in_channels * 2 if learn_sigma else in_channels + self.out_channels = default(out_channels, default_out_channels) + self.patch_size = patch_size + self.pos_embed_scaling_factor = pos_embed_scaling_factor + self.pos_embed_offset = pos_embed_offset + self.pos_embed_max_size = pos_embed_max_size + self.x_block_self_attn_layers = x_block_self_attn_layers + + # hidden_size = default(hidden_size, 64 * depth) + # num_heads = default(num_heads, hidden_size // 64) + + # apply magic --> this defines a head_size of 64 + self.hidden_size = 64 * depth + num_heads = depth + if num_blocks is None: + num_blocks = depth + + self.depth = depth + self.num_heads = num_heads + + self.x_embedder = PatchEmbed( + input_size, + patch_size, + in_channels, + self.hidden_size, + bias = True, + strict_img_size = self.pos_embed_max_size is None, + dtype = dtype, + device = device, + operations = operations + ) + self.t_embedder = TimestepEmbedder(self.hidden_size, dtype=dtype, device=device, operations=operations) + + self.y_embedder = None + if adm_in_channels is not None: + assert isinstance(adm_in_channels, int) + self.y_embedder = VectorEmbedder(adm_in_channels, self.hidden_size, dtype=dtype, device=device, operations=operations) + + if context_processor_layers is not None: + self.context_processor = ContextProcessor(context_size, context_processor_layers, dtype=dtype, device=device, operations=operations) + else: + self.context_processor = None + + self.context_embedder = nn.Identity() + if context_embedder_config is not None: + if context_embedder_config["target"] == "torch.nn.Linear": + self.context_embedder = operations.Linear(**context_embedder_config["params"], dtype=dtype, device=device) + + self.register_length = register_length + if self.register_length > 0: + self.register = nn.Parameter(torch.randn(1, register_length, self.hidden_size, dtype=dtype, device=device)) + + # num_patches = self.x_embedder.num_patches + # Will use fixed sin-cos embedding: + # just use a buffer already + if num_patches is not None: + self.register_buffer( + "pos_embed", + torch.empty(1, num_patches, self.hidden_size, dtype=dtype, device=device), + ) + else: + self.pos_embed = None + + self.use_checkpoint = use_checkpoint + if not skip_blocks: + self.joint_blocks = nn.ModuleList( + [ + ReJointBlock( + self.hidden_size, + num_heads, + mlp_ratio = mlp_ratio, + qkv_bias = qkv_bias, + attn_mode = attn_mode, + pre_only = (i == num_blocks - 1) and final_layer, + rmsnorm = rmsnorm, + scale_mod_only = scale_mod_only, + swiglu = swiglu, + qk_norm = qk_norm, + x_block_self_attn = (i in self.x_block_self_attn_layers) or x_block_self_attn, + dtype = dtype, + device = device, + operations = operations, + ) + for i in range(num_blocks) + ] + ) + + if final_layer: + self.final_layer = FinalLayer(self.hidden_size, patch_size, self.out_channels, dtype=dtype, device=device, operations=operations) + + if compile_core: + assert False + self.forward_core_with_concat = torch.compile(self.forward_core_with_concat) + + def cropped_pos_embed(self, hw, device=None): + p = self.x_embedder.patch_size[0] + h, w = hw + # patched size + h = (h + 1) // p + w = (w + 1) // p + if self.pos_embed is None: + return get_2d_sincos_pos_embed_torch(self.hidden_size, w, h, device=device) + assert self.pos_embed_max_size is not None + assert h <= self.pos_embed_max_size, (h, self.pos_embed_max_size) + assert w <= self.pos_embed_max_size, (w, self.pos_embed_max_size) + top = (self.pos_embed_max_size - h) // 2 + left = (self.pos_embed_max_size - w) // 2 + spatial_pos_embed = rearrange( + self.pos_embed, + "1 (h w) c -> 1 h w c", + h = self.pos_embed_max_size, + w = self.pos_embed_max_size, + ) + spatial_pos_embed = spatial_pos_embed[:, top : top + h, left : left + w, :] + spatial_pos_embed = rearrange(spatial_pos_embed, "1 h w c -> 1 (h w) c") + # print(spatial_pos_embed, top, left, h, w) + # # t = get_2d_sincos_pos_embed_torch(self.hidden_size, w, h, 7.875, 7.875, device=device) #matches exactly for 1024 res + # t = get_2d_sincos_pos_embed_torch(self.hidden_size, w, h, 7.5, 7.5, device=device) #scales better + # # print(t) + # return t + return spatial_pos_embed + + def unpatchify(self, x, hw=None): + """ + x: (N, T, patch_size**2 * C) + imgs: (N, H, W, C) + """ + c = self.out_channels + p = self.x_embedder.patch_size[0] + if hw is None: + h = w = int(x.shape[1] ** 0.5) + else: + h, w = hw + h = (h + 1) // p + w = (w + 1) // p + assert h * w == x.shape[1] + + x = x.reshape(shape=(x.shape[0], h, w, p, p, c)) + x = torch.einsum("nhwpqc->nchpwq", x) + imgs = x.reshape(shape=(x.shape[0], c, h * p, w * p)) + return imgs + + + + + def forward_core_with_concat( + self, + x : torch.Tensor, + c_mod : torch.Tensor, + c_mod_base : torch.Tensor, + context : Optional[torch.Tensor] = None, + context_base : Optional[torch.Tensor] = None, + control = None, + transformer_options = {}, + ) -> torch.Tensor: + patches_replace = transformer_options.get("patches_replace", {}) + if self.register_length > 0: + context = torch.cat( + ( + repeat(self.register, "1 ... -> b ...", b=x.shape[0]), + default(context, torch.Tensor([]).type_as(x)), + ), + 1, + ) + + weight = transformer_options['reg_cond_weight'] if 'reg_cond_weight' in transformer_options else 0.0 + floor = transformer_options['reg_cond_floor'] if 'reg_cond_floor' in transformer_options else 0.0 + floor = min(floor, weight) + + if type(weight) == float or type(weight) == int: + pass + else: + weight = weight.item() + + AttnMask = transformer_options.get('AttnMask') + mask = None + if AttnMask is not None and weight > 0: + mask = AttnMask.get(weight=weight) #mask_obj[0](transformer_options, weight.item()) + + mask_type_bool = type(mask[0][0].item()) == bool if mask is not None else False + if not mask_type_bool: + mask = mask.to(x.dtype) + + text_len = context.shape[1] # mask_obj[0].text_len + + mask[text_len:,text_len:] = torch.clamp(mask[text_len:,text_len:], min=floor.to(mask.device)) #ORIGINAL SELF-ATTN REGION BLEED + #reg_cond_mask = reg_cond_mask_expanded.unsqueeze(0).clone() if reg_cond_mask_expanded is not None else None + mask_type_bool = type(mask[0][0].item()) == bool if mask is not None else False + if weight <= 0.0: + mask = None + context = context_base + c_mod = c_mod_base + + # context is B, L', D + # x is B, L, D + blocks_replace = patches_replace.get("dit", {}) + blocks = len(self.joint_blocks) + for i in range(blocks): + if mask_type_bool and weight < (i / (blocks-1)) and mask is not None: + mask = mask.to(x.dtype) # torch.ones((*mask.shape,), dtype=mask.dtype, device=mask.device) #(mask == mask) #set all to false + + if ("double_block", i) in blocks_replace: + def block_wrap(args): + out = {} + out["txt"], out["img"] = self.joint_blocks[i](args["txt"], args["img"], c=args["vec"]) + return out + + out = blocks_replace[("double_block", i)]({"img": x, "txt": context, "vec": c_mod}, {"original_block": block_wrap}) + context = out["txt"] + x = out["img"] + else: + context, x = self.joint_blocks[i]( + context, + x, + c = c_mod, + use_checkpoint = self.use_checkpoint, + mask = mask, + ) + if control is not None: + control_o = control.get("output") + if i < len(control_o): + add = control_o[i] + if add is not None: + x += add + + x = self.final_layer(x, c_mod) # (N, T, patch_size ** 2 * out_channels) + return x + + def forward( + self, + x : torch.Tensor, + t : torch.Tensor, + y : Optional[torch.Tensor] = None, + context: Optional[torch.Tensor] = None, + control = None, + transformer_options = {}, + ) -> torch.Tensor: + """ + Forward pass of DiT. + x: (N, C, H, W) tensor of spatial inputs (images or latent representations of images) + t: (N,) tensor of diffusion timesteps + y: (N,) tensor of class labels + """ + SIGMA = t[0].clone() / 1000 + EO = transformer_options.get("ExtraOptions", ExtraOptions("")) + if EO is not None: + EO.mute = True + + y0_style_pos = transformer_options.get("y0_style_pos") + y0_style_neg = transformer_options.get("y0_style_neg") + + y0_style_pos_weight = transformer_options.get("y0_style_pos_weight", 0.0) + y0_style_pos_synweight = transformer_options.get("y0_style_pos_synweight", 0.0) + y0_style_pos_synweight *= y0_style_pos_weight + + y0_style_neg_weight = transformer_options.get("y0_style_neg_weight", 0.0) + y0_style_neg_synweight = transformer_options.get("y0_style_neg_synweight", 0.0) + y0_style_neg_synweight *= y0_style_neg_weight + + weight = -1 * transformer_options.get("regional_conditioning_weight", 0.0) + floor = -1 * transformer_options.get("regional_conditioning_floor", 0.0) + + freqsep_lowpass_method = transformer_options.get("freqsep_lowpass_method") + freqsep_sigma = transformer_options.get("freqsep_sigma") + freqsep_kernel_size = transformer_options.get("freqsep_kernel_size") + freqsep_inner_kernel_size = transformer_options.get("freqsep_inner_kernel_size") + freqsep_stride = transformer_options.get("freqsep_stride") + + freqsep_lowpass_weight = transformer_options.get("freqsep_lowpass_weight") + freqsep_highpass_weight= transformer_options.get("freqsep_highpass_weight") + freqsep_mask = transformer_options.get("freqsep_mask") + + + x_orig = x.clone() + y_orig = y.clone() + + h,w = x.shape[-2:] + h_len = ((h + (self.patch_size // 2)) // self.patch_size) # h_len 96 + w_len = ((w + (self.patch_size // 2)) // self.patch_size) # w_len 96 + + out_list = [] + for i in range(len(transformer_options['cond_or_uncond'])): + UNCOND = transformer_options['cond_or_uncond'][i] == 1 + + x = x_orig.clone() + y = y_orig.clone() + + context_base = context[i][None,...].clone() + + if UNCOND: + #transformer_options['reg_cond_weight'] = -1 + #context_tmp = context[i][None,...].clone() + + transformer_options['reg_cond_weight'] = transformer_options.get("regional_conditioning_weight", 0.0) #transformer_options['regional_conditioning_weight'] + transformer_options['reg_cond_floor'] = transformer_options.get("regional_conditioning_floor", 0.0) #transformer_options['regional_conditioning_floor'] #if "regional_conditioning_floor" in transformer_options else 0.0 + transformer_options['reg_cond_mask_orig'] = transformer_options.get('regional_conditioning_mask_orig') + + AttnMask = transformer_options.get('AttnMask', None) + RegContext = transformer_options.get('RegContext', None) + + if AttnMask is not None and transformer_options['reg_cond_weight'] > 0.0: + AttnMask.attn_mask_recast(x.dtype) + context_tmp = RegContext.get().to(context.dtype) + #context_tmp = 0 * context_tmp.clone() + + A = context[i][None,...].clone() + B = context_tmp + context_tmp = A.repeat(1, (B.shape[1] // A.shape[1]) + 1, 1)[:, :B.shape[1], :] + + else: + context_tmp = context[i][None,...].clone() + + elif UNCOND == False: + transformer_options['reg_cond_weight'] = transformer_options.get("regional_conditioning_weight", 0.0) #transformer_options['regional_conditioning_weight'] + transformer_options['reg_cond_floor'] = transformer_options.get("regional_conditioning_floor", 0.0) #transformer_options['regional_conditioning_floor'] #if "regional_conditioning_floor" in transformer_options else 0.0 + transformer_options['reg_cond_mask_orig'] = transformer_options.get('regional_conditioning_mask_orig') + + AttnMask = transformer_options.get('AttnMask', None) + RegContext = transformer_options.get('RegContext', None) + + if AttnMask is not None and transformer_options['reg_cond_weight'] > 0.0: + AttnMask.attn_mask_recast(x.dtype) + context_tmp = RegContext.get().to(context.dtype) + else: + context_tmp = context[i][None,...].clone() + + + if context_tmp is None: + context_tmp = context[i][None,...].clone() + + #context = context_tmp + + + if self.context_processor is not None: + context_tmp = self.context_processor(context_tmp) + + hw = x.shape[-2:] + x = self.x_embedder(x) + comfy.ops.cast_to_input(self.cropped_pos_embed(hw, device=x.device), x) + c = self.t_embedder(t, dtype=x.dtype) # (N, D) # c is like vec... + if y is not None and self.y_embedder is not None: + y = self.y_embedder(y_orig.clone()) # (N, D) + c = c + y # (N, D) # vec = vec + y (y = pooled_output 1,2048) + + if context_tmp is not None: + context_tmp = self.context_embedder(context_tmp) + + + + if self.context_processor is not None: + context_base = self.context_processor(context_base) + + #hw = x.shape[-2:] + #x = self.x_embedder(x) + comfy.ops.cast_to_input(self.cropped_pos_embed(hw, device=x.device), x) + c_base = self.t_embedder(t, dtype=x.dtype) # (N, D) # c is like vec... + if y is not None and self.y_embedder is not None: + y = self.y_embedder(y_orig.clone()) # (N, D) + c_base = c_base + y # (N, D) # vec = vec + y (y = pooled_output 1,2048) + + if context_base is not None: + context_base = self.context_embedder(context_base) + + + x = self.forward_core_with_concat( + x[i][None,...], + c[i][None,...], + c_base[i][None,...], + context_tmp, + context_base, #context[i][None,...].clone(), + control, + transformer_options, + ) + + x = self.unpatchify(x, hw=hw) # (N, out_channels, H, W) + + out_list.append(x) + + + x = torch.stack(out_list, dim=0).squeeze(dim=1) + eps = x[:,:,:hw[-2],:hw[-1]] + + + + + + + + + dtype = eps.dtype if self.style_dtype is None else self.style_dtype + + if y0_style_pos is not None: + y0_style_pos_weight = transformer_options.get("y0_style_pos_weight") + y0_style_pos_synweight = transformer_options.get("y0_style_pos_synweight") + y0_style_pos_synweight *= y0_style_pos_weight + y0_style_pos_mask = transformer_options.get("y0_style_pos_mask") + y0_style_pos_mask_edge = transformer_options.get("y0_style_pos_mask_edge") + + y0_style_pos = y0_style_pos.to(dtype) + x = x_orig.clone().to(dtype) + eps = eps.to(dtype) + eps_orig = eps.clone() + + sigma = SIGMA #t_orig[0].to(torch.float32) / 1000 + denoised = x - sigma * eps + + denoised_embed = self.Retrojector.embed(denoised) + y0_adain_embed = self.Retrojector.embed(y0_style_pos) + + if transformer_options['y0_style_method'] == "scattersort": + tile_h, tile_w = transformer_options.get('y0_style_tile_height'), transformer_options.get('y0_style_tile_width') + pad = transformer_options.get('y0_style_tile_padding') + if pad is not None and tile_h is not None and tile_w is not None: + + denoised_spatial = rearrange(denoised_embed, "b (h w) c -> b c h w", h=h_len, w=w_len) + y0_adain_spatial = rearrange(y0_adain_embed, "b (h w) c -> b c h w", h=h_len, w=w_len) + + if EO("scattersort_median_LP"): + denoised_spatial_LP = median_blur_2d(denoised_spatial, kernel_size=EO("scattersort_median_LP",7)) + y0_adain_spatial_LP = median_blur_2d(y0_adain_spatial, kernel_size=EO("scattersort_median_LP",7)) + + denoised_spatial_HP = denoised_spatial - denoised_spatial_LP + y0_adain_spatial_HP = y0_adain_spatial - y0_adain_spatial_LP + + denoised_spatial_LP = apply_scattersort_tiled(denoised_spatial_LP, y0_adain_spatial_LP, tile_h, tile_w, pad) + + denoised_spatial = denoised_spatial_LP + denoised_spatial_HP + denoised_embed = rearrange(denoised_spatial, "b c h w -> b (h w) c") + else: + denoised_spatial = apply_scattersort_tiled(denoised_spatial, y0_adain_spatial, tile_h, tile_w, pad) + + denoised_embed = rearrange(denoised_spatial, "b c h w -> b (h w) c") + + else: + denoised_embed = apply_scattersort_masked(denoised_embed, y0_adain_embed, y0_style_pos_mask, y0_style_pos_mask_edge, h_len, w_len) + + + + elif transformer_options['y0_style_method'] == "AdaIN": + if freqsep_mask is not None: + freqsep_mask = freqsep_mask.view(1, 1, *freqsep_mask.shape[-2:]).float() + freqsep_mask = F.interpolate(freqsep_mask.float(), size=(h_len, w_len), mode='nearest-exact') + + if hasattr(self, "adain_tile"): + tile_h, tile_w = self.adain_tile + + denoised_pretile = rearrange(denoised_embed, "b (h w) c -> b c h w", h=h_len, w=w_len) + y0_adain_pretile = rearrange(y0_adain_embed, "b (h w) c -> b c h w", h=h_len, w=w_len) + + if self.adain_flag: + h_off = tile_h // 2 + w_off = tile_w // 2 + denoised_pretile = denoised_pretile[:,:,h_off:-h_off, w_off:-w_off] + self.adain_flag = False + else: + h_off = 0 + w_off = 0 + self.adain_flag = True + + tiles, orig_shape, grid, strides = tile_latent(denoised_pretile, tile_size=(tile_h,tile_w)) + y0_tiles, orig_shape, grid, strides = tile_latent(y0_adain_pretile, tile_size=(tile_h,tile_w)) + + tiles_out = [] + for i in range(tiles.shape[0]): + tile = tiles[i].unsqueeze(0) + y0_tile = y0_tiles[i].unsqueeze(0) + + tile = rearrange(tile, "b c h w -> b (h w) c", h=tile_h, w=tile_w) + y0_tile = rearrange(y0_tile, "b c h w -> b (h w) c", h=tile_h, w=tile_w) + + tile = adain_seq_inplace(tile, y0_tile) + tiles_out.append(rearrange(tile, "b (h w) c -> b c h w", h=tile_h, w=tile_w)) + + tiles_out_tensor = torch.cat(tiles_out, dim=0) + tiles_out_tensor = untile_latent(tiles_out_tensor, orig_shape, grid, strides) + + if h_off == 0: + denoised_pretile = tiles_out_tensor + else: + denoised_pretile[:,:,h_off:-h_off, w_off:-w_off] = tiles_out_tensor + denoised_embed = rearrange(denoised_pretile, "b c h w -> b (h w) c", h=h_len, w=w_len) + + elif freqsep_lowpass_method is not None and freqsep_lowpass_method.endswith("pw"): #EO("adain_pw"): + + denoised_spatial = rearrange(denoised_embed, "b (h w) c -> b c h w", h=h_len, w=w_len) + y0_adain_spatial = rearrange(y0_adain_embed, "b (h w) c -> b c h w", h=h_len, w=w_len) + + if freqsep_lowpass_method == "median_pw": + denoised_spatial_new = adain_patchwise_row_batch_med(denoised_spatial.clone(), y0_adain_spatial.clone().repeat(denoised_spatial.shape[0],1,1,1), sigma=freqsep_sigma, kernel_size=freqsep_kernel_size, use_median_blur=True, lowpass_weight=freqsep_lowpass_weight, highpass_weight=freqsep_highpass_weight) + elif freqsep_lowpass_method == "gaussian_pw": + denoised_spatial_new = adain_patchwise_row_batch(denoised_spatial.clone(), y0_adain_spatial.clone().repeat(denoised_spatial.shape[0],1,1,1), sigma=freqsep_sigma, kernel_size=freqsep_kernel_size) + + denoised_embed = rearrange(denoised_spatial_new, "b c h w -> b (h w) c", h=h_len, w=w_len) + + elif freqsep_lowpass_method is not None: + denoised_spatial = rearrange(denoised_embed, "b (h w) c -> b c h w", h=h_len, w=w_len) + y0_adain_spatial = rearrange(y0_adain_embed, "b (h w) c -> b c h w", h=h_len, w=w_len) + + if freqsep_lowpass_method == "median": + denoised_spatial_LP = median_blur_2d(denoised_spatial, kernel_size=freqsep_kernel_size) + y0_adain_spatial_LP = median_blur_2d(y0_adain_spatial, kernel_size=freqsep_kernel_size) + elif freqsep_lowpass_method == "gaussian": + denoised_spatial_LP = gaussian_blur_2d(denoised_spatial, sigma=freqsep_sigma, kernel_size=freqsep_kernel_size) + y0_adain_spatial_LP = gaussian_blur_2d(y0_adain_spatial, sigma=freqsep_sigma, kernel_size=freqsep_kernel_size) + + denoised_spatial_HP = denoised_spatial - denoised_spatial_LP + + if EO("adain_fs_uhp"): + y0_adain_spatial_HP = y0_adain_spatial - y0_adain_spatial_LP + + denoised_spatial_ULP = gaussian_blur_2d(denoised_spatial, sigma=EO("adain_fs_uhp_sigma", 1.0), kernel_size=EO("adain_fs_uhp_kernel_size", 3)) + y0_adain_spatial_ULP = gaussian_blur_2d(y0_adain_spatial, sigma=EO("adain_fs_uhp_sigma", 1.0), kernel_size=EO("adain_fs_uhp_kernel_size", 3)) + + denoised_spatial_UHP = denoised_spatial_HP - denoised_spatial_ULP + y0_adain_spatial_UHP = y0_adain_spatial_HP - y0_adain_spatial_ULP + + #denoised_spatial_HP = y0_adain_spatial_ULP + denoised_spatial_UHP + denoised_spatial_HP = denoised_spatial_ULP + y0_adain_spatial_UHP + + denoised_spatial_new = freqsep_lowpass_weight * y0_adain_spatial_LP + freqsep_highpass_weight * denoised_spatial_HP + denoised_embed = rearrange(denoised_spatial_new, "b c h w -> b (h w) c", h=h_len, w=w_len) + + else: + denoised_embed = adain_seq_inplace(denoised_embed, y0_adain_embed) + + for adain_iter in range(EO("style_iter", 0)): + denoised_embed = adain_seq_inplace(denoised_embed, y0_adain_embed) + denoised_embed = self.Retrojector.embed(self.Retrojector.unembed(denoised_embed)) + denoised_embed = adain_seq_inplace(denoised_embed, y0_adain_embed) + + elif transformer_options['y0_style_method'] == "WCT": + self.StyleWCT.set(y0_adain_embed) + denoised_embed = self.StyleWCT.get(denoised_embed) + + if transformer_options.get('y0_standard_guide') is not None: + y0_standard_guide = transformer_options.get('y0_standard_guide') + + y0_standard_guide_embed = self.Retrojector.embed(y0_standard_guide) + f_cs = self.StyleWCT.get(y0_standard_guide_embed) + self.y0_standard_guide = self.Retrojector.unembed(f_cs) + + if transformer_options.get('y0_inv_standard_guide') is not None: + y0_inv_standard_guide = transformer_options.get('y0_inv_standard_guide') + + y0_inv_standard_guide_embed = self.Retrojector.embed(y0_inv_standard_guide) + f_cs = self.StyleWCT.get(y0_inv_standard_guide_embed) + self.y0_inv_standard_guide = self.Retrojector.unembed(f_cs) + + denoised_approx = self.Retrojector.unembed(denoised_embed) + + eps = (x - denoised_approx) / sigma + + if not UNCOND: + if eps.shape[0] == 2: + eps[1] = eps_orig[1] + y0_style_pos_weight * (eps[1] - eps_orig[1]) + eps[0] = eps_orig[0] + y0_style_pos_synweight * (eps[0] - eps_orig[0]) + else: + eps[0] = eps_orig[0] + y0_style_pos_weight * (eps[0] - eps_orig[0]) + elif eps.shape[0] == 1 and UNCOND: + eps[0] = eps_orig[0] + y0_style_pos_synweight * (eps[0] - eps_orig[0]) + + eps = eps.float() + + if y0_style_neg is not None: + y0_style_neg_weight = transformer_options.get("y0_style_neg_weight") + y0_style_neg_synweight = transformer_options.get("y0_style_neg_synweight") + y0_style_neg_synweight *= y0_style_neg_weight + y0_style_neg_mask = transformer_options.get("y0_style_neg_mask") + y0_style_neg_mask_edge = transformer_options.get("y0_style_neg_mask_edge") + + y0_style_neg = y0_style_neg.to(dtype) + x = x_orig.clone().to(dtype) + eps = eps.to(dtype) + eps_orig = eps.clone() + + sigma = SIGMA #t_orig[0].to(torch.float32) / 1000 + denoised = x - sigma * eps + + denoised_embed = self.Retrojector.embed(denoised) + y0_adain_embed = self.Retrojector.embed(y0_style_neg) + + if transformer_options['y0_style_method'] == "scattersort": + tile_h, tile_w = transformer_options.get('y0_style_tile_height'), transformer_options.get('y0_style_tile_width') + pad = transformer_options.get('y0_style_tile_padding') + if pad is not None and tile_h is not None and tile_w is not None: + + denoised_spatial = rearrange(denoised_embed, "b (h w) c -> b c h w", h=h_len, w=w_len) + y0_adain_spatial = rearrange(y0_adain_embed, "b (h w) c -> b c h w", h=h_len, w=w_len) + + denoised_spatial = apply_scattersort_tiled(denoised_spatial, y0_adain_spatial, tile_h, tile_w, pad) + + denoised_embed = rearrange(denoised_spatial, "b c h w -> b (h w) c") + + else: + denoised_embed = apply_scattersort_masked(denoised_embed, y0_adain_embed, y0_style_neg_mask, y0_style_neg_mask_edge, h_len, w_len) + + + elif transformer_options['y0_style_method'] == "AdaIN": + denoised_embed = adain_seq_inplace(denoised_embed, y0_adain_embed) + for adain_iter in range(EO("style_iter", 0)): + denoised_embed = adain_seq_inplace(denoised_embed, y0_adain_embed) + denoised_embed = self.Retrojector.embed(self.Retrojector.unembed(denoised_embed)) + denoised_embed = adain_seq_inplace(denoised_embed, y0_adain_embed) + + elif transformer_options['y0_style_method'] == "WCT": + self.StyleWCT.set(y0_adain_embed) + denoised_embed = self.StyleWCT.get(denoised_embed) + + denoised_approx = self.Retrojector.unembed(denoised_embed) + + if UNCOND: + eps = (x - denoised_approx) / sigma + eps[0] = eps_orig[0] + y0_style_neg_weight * (eps[0] - eps_orig[0]) + if eps.shape[0] == 2: + eps[1] = eps_orig[1] + y0_style_neg_synweight * (eps[1] - eps_orig[1]) + elif eps.shape[0] == 1 and not UNCOND: + eps[0] = eps_orig[0] + y0_style_neg_synweight * (eps[0] - eps_orig[0]) + + eps = eps.float() + + return eps + + + + + + + + + + + + + + + + + + + + + dtype = eps.dtype if self.style_dtype is None else self.style_dtype + pinv_dtype = torch.float32 if dtype != torch.float64 else dtype + W_inv = None + + #if eps.shape[0] == 2 or (eps.shape[0] == 1 and not UNCOND): + if y0_style_pos is not None: + y0_style_pos_weight = transformer_options.get("y0_style_pos_weight") + y0_style_pos_synweight = transformer_options.get("y0_style_pos_synweight") + y0_style_pos_synweight *= y0_style_pos_weight + + y0_style_pos = y0_style_pos.to(torch.float64) + x = x_orig.to(torch.float64) + eps = eps.to(torch.float64) + eps_orig = eps.clone() + + sigma = SIGMA# t_orig[0].to(torch.float64) / 1000 + denoised = x - sigma * eps + + hw = denoised.shape[-2:] + + features = 1536# denoised_embed.shape[-1] # should be 1536 + + W_conv = self.x_embedder.proj.weight.to(torch.float64) # [1536, 16, 2, 2] + W_flat = W_conv.view(features, -1).to(torch.float64) # [1536, 64] + W_pinv = torch.linalg.pinv(W_flat) # [64, 1536] + + x_embedder64 = copy.deepcopy(self.x_embedder.proj).to(denoised) + + #y = self.x_embedder.proj(denoised.to(torch.float16)).float() + y = x_embedder64(denoised) + B, C_out, H_out, W_out = y.shape + y_flat = y.view(B, C_out, -1) # [B, 1536, N] + y_flat = y_flat.permute(0, 2, 1) # [B, N, 1536] + + bias = self.x_embedder.proj.bias.to(torch.float64) # [1536] + denoised_embed = y_flat - bias.view(1, 1, -1) + + + + + #y = self.x_embedder.proj(y0_style_pos.to(torch.float16)).float() + y = x_embedder64(y0_style_pos) + + B, C_out, H_out, W_out = y.shape + y_flat = y.view(B, C_out, -1) # [B, 1536, N] + y_flat = y_flat.permute(0, 2, 1) # [B, N , 1536] + + bias = self.x_embedder.proj.bias.to(torch.float64) # [1536] + y0_adain_embed = y_flat - bias.view(1, 1, -1) + + + #denoised_embed = adain_seq(denoised_embed, y0_adain_embed) + + + + if transformer_options['y0_style_method'] == "AdaIN": + denoised_embed = adain_seq_inplace(denoised_embed, y0_adain_embed) + """for adain_iter in range(EO("style_iter", 0)): + denoised_embed = adain_seq_inplace(denoised_embed, y0_adain_embed) + denoised_embed = (denoised_embed - b) @ torch.linalg.pinv(W.to(pinv_dtype)).T.to(dtype) # not going to work! needs + denoised_embed = F.linear(denoised_embed .to(W), W, b).to(img) + denoised_embed = adain_seq_inplace(denoised_embed, y0_adain_embed)""" + + elif transformer_options['y0_style_method'] == "WCT": + if self.y0_adain_embed is None or self.y0_adain_embed.shape != y0_adain_embed.shape or torch.norm(self.y0_adain_embed - y0_adain_embed) > 0: + self.y0_adain_embed = y0_adain_embed + + f_s = y0_adain_embed[0].clone() + self.mu_s = f_s.mean(dim=0, keepdim=True) + f_s_centered = f_s - self.mu_s + + cov = (f_s_centered.T.double() @ f_s_centered.double()) / (f_s_centered.size(0) - 1) + + S_eig, U_eig = torch.linalg.eigh(cov + 1e-5 * torch.eye(cov.size(0), dtype=cov.dtype, device=cov.device)) + S_eig_sqrt = S_eig.clamp(min=0).sqrt() # eigenvalues -> singular values + + whiten = U_eig @ torch.diag(S_eig_sqrt) @ U_eig.T + self.y0_color = whiten.to(f_s_centered) + + for wct_i in range(eps.shape[0]): + f_c = denoised_embed[wct_i].clone() + mu_c = f_c.mean(dim=0, keepdim=True) + f_c_centered = f_c - mu_c + + cov = (f_c_centered.T.double() @ f_c_centered.double()) / (f_c_centered.size(0) - 1) + + S_eig, U_eig = torch.linalg.eigh(cov + 1e-5 * torch.eye(cov.size(0), dtype=cov.dtype, device=cov.device)) + inv_sqrt_eig = S_eig.clamp(min=0).rsqrt() + + whiten = U_eig @ torch.diag(inv_sqrt_eig) @ U_eig.T + whiten = whiten.to(f_c_centered) + + f_c_whitened = f_c_centered @ whiten.T + f_cs = f_c_whitened @ self.y0_color.T + self.mu_s + + denoised_embed[wct_i] = f_cs + + + x_patches = denoised_embed @ W_pinv.T # [B,N,64] + + x_patches = x_patches.permute(0, 2, 1) # [B,64,N] + + x_reconstructed = torch.nn.functional.fold( + x_patches, # [B, 64, N] + output_size=(H_out * 2, W_out * 2), # restore original input shape + kernel_size=2, + stride=2 + ) + + denoised_approx = x_reconstructed #.view(B, 16, H_out * 2, W_out * 2) + + + + eps = (x - denoised_approx) / sigma + #if eps.shape[0] == 2: + # eps[1] = eps_orig[1] + y0_style_pos_weight * (eps[1] - eps_orig[1]) + # eps[0] = eps_orig[0] + y0_style_pos_synweight * (eps[0] - eps_orig[0]) + #else: + # eps[0] = eps_orig[0] + y0_style_pos_weight * (eps[0] - eps_orig[0]) + + if not UNCOND: + if eps.shape[0] == 2: + eps[1] = eps_orig[1] + y0_style_pos_weight * (eps[1] - eps_orig[1]) + eps[0] = eps_orig[0] + y0_style_pos_synweight * (eps[0] - eps_orig[0]) + else: + eps[0] = eps_orig[0] + y0_style_pos_weight * (eps[0] - eps_orig[0]) + elif eps.shape[0] == 1 and UNCOND: + eps[0] = eps_orig[0] + y0_style_pos_synweight * (eps[0] - eps_orig[0]) + + eps = eps.float() + + #if eps.shape[0] == 2 or (eps.shape[0] == 1 and UNCOND): + if y0_style_neg is not None: + y0_style_neg_weight = transformer_options.get("y0_style_neg_weight") + y0_style_neg_synweight = transformer_options.get("y0_style_neg_synweight") + y0_style_neg_synweight *= y0_style_neg_weight + + y0_style_neg = y0_style_neg.to(torch.float64) + x = x_orig.to(torch.float64) + eps = eps.to(torch.float64) + eps_orig = eps.clone() + + sigma = SIGMA# t_orig[0].to(torch.float64) / 1000 + denoised = x - sigma * eps + + hw = denoised.shape[-2:] + + features = 1536# denoised_embed.shape[-1] # should be 1536 + + W_conv = self.x_embedder.proj.weight.float() # [1536, 16, 2, 2] + W_flat = W_conv.view(features, -1).float() # [1536, 64] + W_pinv = torch.linalg.pinv(W_flat) # [64, 1536] + + + + y = self.x_embedder.proj(denoised.to(torch.float16)).float() + B, C_out, H_out, W_out = y.shape + y_flat = y.view(B, C_out, -1) # [B, 1536, N] + y_flat = y_flat.permute(0, 2, 1) # [B, N, 1536] + + bias = self.x_embedder.proj.bias.float() # [1536] + denoised_embed = y_flat - bias.view(1, 1, -1) + + + + y = self.x_embedder.proj(y0_style_neg.to(torch.float16)).float() + B, C_out, H_out, W_out = y.shape + y_flat = y.view(B, C_out, -1) # [B, 1536, N] + y_flat = y_flat.permute(0, 2, 1) # [B, N , 1536] + + bias = self.x_embedder.proj.bias.float() # [1536] + y0_adain_embed = y_flat - bias.view(1, 1, -1) + + + #denoised_embed = adain_seq(denoised_embed, y0_adain_embed) + + if transformer_options['y0_style_method'] == "AdaIN": + denoised_embed = adain_seq_inplace(denoised_embed, y0_adain_embed) + """for adain_iter in range(EO("style_iter", 0)): + denoised_embed = adain_seq_inplace(denoised_embed, y0_adain_embed) + denoised_embed = (denoised_embed - b) @ torch.linalg.pinv(W.to(pinv_dtype)).T.to(dtype) + denoised_embed = F.linear(denoised_embed .to(W), W, b).to(img) + denoised_embed = adain_seq_inplace(denoised_embed, y0_adain_embed)""" + + elif transformer_options['y0_style_method'] == "WCT": + if self.y0_adain_embed is None or self.y0_adain_embed.shape != y0_adain_embed.shape or torch.norm(self.y0_adain_embed - y0_adain_embed) > 0: + self.y0_adain_embed = y0_adain_embed + + f_s = y0_adain_embed[0].clone() + self.mu_s = f_s.mean(dim=0, keepdim=True) + f_s_centered = f_s - self.mu_s + + cov = (f_s_centered.T.double() @ f_s_centered.double()) / (f_s_centered.size(0) - 1) + + S_eig, U_eig = torch.linalg.eigh(cov + 1e-5 * torch.eye(cov.size(0), dtype=cov.dtype, device=cov.device)) + S_eig_sqrt = S_eig.clamp(min=0).sqrt() # eigenvalues -> singular values + + whiten = U_eig @ torch.diag(S_eig_sqrt) @ U_eig.T + self.y0_color = whiten.to(f_s_centered) + + for wct_i in range(eps.shape[0]): + f_c = denoised_embed[wct_i].clone() + mu_c = f_c.mean(dim=0, keepdim=True) + f_c_centered = f_c - mu_c + + cov = (f_c_centered.T.double() @ f_c_centered.double()) / (f_c_centered.size(0) - 1) + + S_eig, U_eig = torch.linalg.eigh(cov + 1e-5 * torch.eye(cov.size(0), dtype=cov.dtype, device=cov.device)) + inv_sqrt_eig = S_eig.clamp(min=0).rsqrt() + + whiten = U_eig @ torch.diag(inv_sqrt_eig) @ U_eig.T + whiten = whiten.to(f_c_centered) + + f_c_whitened = f_c_centered @ whiten.T + f_cs = f_c_whitened @ self.y0_color.T + self.mu_s + + denoised_embed[wct_i] = f_cs + + + x_patches = denoised_embed @ W_pinv.T # [B,N,64] + + x_patches = x_patches.permute(0, 2, 1) # [B,64,N] + + x_reconstructed = torch.nn.functional.fold( + x_patches, # [B, 64, N] + output_size=(H_out * 2, W_out * 2), # restore original input shape + kernel_size=2, + stride=2 + ) + + denoised_approx = x_reconstructed #.view(B, 16, H_out * 2, W_out * 2) + + + + #eps = (x - denoised_approx) / sigma + #eps[0] = eps_orig[0] + y0_style_neg_weight * (eps[0] - eps_orig[0]) + #if eps.shape[0] == 2: + # eps[1] = eps_orig[1] + y0_style_neg_synweight * (eps[1] - eps_orig[1]) + + if UNCOND: + eps = (x - denoised_approx) / sigma + eps[0] = eps_orig[0] + y0_style_neg_weight * (eps[0] - eps_orig[0]) + if eps.shape[0] == 2: + eps[1] = eps_orig[1] + y0_style_neg_synweight * (eps[1] - eps_orig[1]) + elif eps.shape[0] == 1 and not UNCOND: + eps[0] = eps_orig[0] + y0_style_neg_synweight * (eps[0] - eps_orig[0]) + + eps = eps.float() + + + + return eps + + + + + +class ReOpenAISignatureMMDITWrapper(MMDiT): + def forward( + self, + x : torch.Tensor, + timesteps : torch.Tensor, + context : Optional[torch.Tensor] = None, + y : Optional[torch.Tensor] = None, + control = None, + transformer_options = {}, + **kwargs, + ) -> torch.Tensor: + return super().forward(x, timesteps, context=context, y=y, control=control, transformer_options=transformer_options) + + + +def adain_seq_inplace(content: torch.Tensor, style: torch.Tensor, eps: float = 1e-7) -> torch.Tensor: + mean_c = content.mean(1, keepdim=True) + std_c = content.std (1, keepdim=True).add_(eps) # in-place add + mean_s = style.mean (1, keepdim=True) + std_s = style.std (1, keepdim=True).add_(eps) + + content.sub_(mean_c).div_(std_c).mul_(std_s).add_(mean_s) # in-place chain + return content + + + +def adain_seq(content: torch.Tensor, style: torch.Tensor, eps: float = 1e-7) -> torch.Tensor: + return ((content - content.mean(1, keepdim=True)) / (content.std(1, keepdim=True) + eps)) * (style.std(1, keepdim=True) + eps) + style.mean(1, keepdim=True) + + + + diff --git a/ComfyUI/custom_nodes/RES4LYF/wan/model.py b/ComfyUI/custom_nodes/RES4LYF/wan/model.py new file mode 100644 index 0000000000000000000000000000000000000000..00ad31a85b40e9a168ac49057bdcead1da8ea7cd --- /dev/null +++ b/ComfyUI/custom_nodes/RES4LYF/wan/model.py @@ -0,0 +1,1346 @@ +# original version: https://github.com/Wan-Video/Wan2.1/blob/main/wan/modules/model.py +# Copyright 2024-2025 The Alibaba Wan Team Authors. All rights reserved. +import math +from typing import Optional, Callable, Tuple, Dict, Any, Union + +import torch +import torch.nn as nn +import torch.nn.functional as F +from einops import repeat + +from comfy.ldm.modules.attention import optimized_attention, attention_pytorch +from comfy.ldm.flux.layers import EmbedND +from comfy.ldm.flux.math import apply_rope +from comfy.ldm.modules.diffusionmodules.mmdit import RMSNorm +import comfy.ldm.common_dit +import comfy.model_management + +from ..latents import interpolate_spd +from ..helper import ExtraOptions + + +def sinusoidal_embedding_1d(dim, position): + # preprocess + assert dim % 2 == 0 + half = dim // 2 + position = position.type(torch.float32) + + # calculation + sinusoid = torch.outer( + position, torch.pow(10000, -torch.arange(half).to(position).div(half))) + x = torch.cat([torch.cos(sinusoid), torch.sin(sinusoid)], dim=1) + return x + + + +class ReWanRawSelfAttention(nn.Module): + + def __init__(self, + dim, + num_heads, + window_size = (-1, -1), + qk_norm = True, + eps = 1e-6, + operation_settings = {}): + assert dim % num_heads == 0 + super().__init__() + self.dim = dim + self.num_heads = num_heads + self.head_dim = dim // num_heads + self.window_size = window_size + self.qk_norm = qk_norm + self.eps = eps + + # layers + self.q = operation_settings.get("operations").Linear(dim, dim, device=operation_settings.get("device"), dtype=operation_settings.get("dtype")) + self.k = operation_settings.get("operations").Linear(dim, dim, device=operation_settings.get("device"), dtype=operation_settings.get("dtype")) + self.v = operation_settings.get("operations").Linear(dim, dim, device=operation_settings.get("device"), dtype=operation_settings.get("dtype")) + self.o = operation_settings.get("operations").Linear(dim, dim, device=operation_settings.get("device"), dtype=operation_settings.get("dtype")) + self.norm_q = RMSNorm(dim, eps=eps, elementwise_affine=True, device=operation_settings.get("device"), dtype=operation_settings.get("dtype")) if qk_norm else nn.Identity() + self.norm_k = RMSNorm(dim, eps=eps, elementwise_affine=True, device=operation_settings.get("device"), dtype=operation_settings.get("dtype")) if qk_norm else nn.Identity() + + def forward(self, x, freqs, mask=None): + r""" + Args: + x(Tensor): Shape [B, L, num_heads, C / num_heads] + freqs(Tensor): Rope freqs, shape [1024, C / num_heads / 2] + """ + b, s, n, d = *x.shape[:2], self.num_heads, self.head_dim + + # query, key, value function + def qkv_fn(x): + q = self.norm_q(self.q(x)).view(b, s, n, d) + k = self.norm_k(self.k(x)).view(b, s, n, d) + v = self.v(x).view(b, s, n * d) + return q, k, v + + q, k, v = qkv_fn(x) + q, k = apply_rope(q, k, freqs) + # q,k.shape = 2,14040,12,128 v.shape = 2,14040,1536 + + x = optimized_attention( + q.view(b, s, n * d), + k.view(b, s, n * d), + v, + heads=self.num_heads, + ) + + x = self.o(x) + return x + + +def attention_weights(q, k): + # implementation of in-place softmax to reduce memory req + scores = torch.matmul(q, k.transpose(-2, -1)) + scores.div_(math.sqrt(q.size(-1))) + torch.exp(scores, out=scores) + summed = torch.sum(scores, dim=-1, keepdim=True) + scores /= summed + return scores.nan_to_num_(0.0, 65504., -65504.) + + + + + +class ReWanSlidingSelfAttention(nn.Module): + + def __init__(self, + dim, + num_heads, + window_size = (-1, -1), + qk_norm = True, + eps = 1e-6, + operation_settings = {}): + assert dim % num_heads == 0 + super().__init__() + self.dim = dim + self.num_heads = num_heads + self.head_dim = dim // num_heads + self.window_size = window_size + self.qk_norm = qk_norm + self.eps = eps + self.winderz = 15 + self.winderz_type= "standard" + + # layers + self.q = operation_settings.get("operations").Linear(dim, dim, device=operation_settings.get("device"), dtype=operation_settings.get("dtype")) + self.k = operation_settings.get("operations").Linear(dim, dim, device=operation_settings.get("device"), dtype=operation_settings.get("dtype")) + self.v = operation_settings.get("operations").Linear(dim, dim, device=operation_settings.get("device"), dtype=operation_settings.get("dtype")) + self.o = operation_settings.get("operations").Linear(dim, dim, device=operation_settings.get("device"), dtype=operation_settings.get("dtype")) + self.norm_q = RMSNorm(dim, eps=eps, elementwise_affine=True, device=operation_settings.get("device"), dtype=operation_settings.get("dtype")) if qk_norm else nn.Identity() + self.norm_k = RMSNorm(dim, eps=eps, elementwise_affine=True, device=operation_settings.get("device"), dtype=operation_settings.get("dtype")) if qk_norm else nn.Identity() + + + def forward(self, x, freqs, mask=None, grid_sizes=None): + r""" + Args: + x(Tensor): Shape [B, L, num_heads, C / num_heads] + freqs(Tensor): Rope freqs, shape [1024, C / num_heads / 2] + """ + b, s, n, d = *x.shape[:2], self.num_heads, self.head_dim + + # query, key, value function + def qkv_fn(x): + q = self.norm_q(self.q(x)).view(b, s, n, d) + k = self.norm_k(self.k(x)).view(b, s, n, d) + v = self.v(x).view(b, s, n * d) + return q, k, v + + q, k, v = qkv_fn(x) + q, k = apply_rope(q, k, freqs) + # q,k.shape = 2,14040,12,128 v.shape = 2,14040,1536 + + img_len = grid_sizes[1] * grid_sizes[2] + total_frames = int(q.shape[1] // img_len) + + window_size = self.winderz + half_window = window_size // 2 + + q_ = q.view(b, s, n * d) + k_ = k.view(b, s, n * d) + x_list = [] + + for i in range(total_frames): + q_start = i * img_len + q_end = (i + 1) * img_len + + # circular frame indices for key/value window + center = i + #window_indices = [(center + offset) % total_frames for offset in range(-half_window, half_window + 1)] + if self.winderz_type == "standard": + start = max(0, center - half_window) + end = min(total_frames, center + half_window + 1) + # Shift window if it would be too short + if end - start < window_size: + if start == 0: + end = min(total_frames, start + window_size) + elif end == total_frames: + start = max(0, end - window_size) + + window_indices = list(range(start, end)) + elif self.winderz_type == "circular": + window_indices = [(center + offset) % total_frames for offset in range(-half_window, half_window + 1)] + + # frame indices to token indices + token_indices = [] + for frame in window_indices: + start = frame * img_len + token_indices.extend(range(start, start + img_len)) + + token_indices = torch.tensor(token_indices, device=q.device) + + x = optimized_attention( + q_[:, q_start:q_end, :], # [B, img_len, C] + k_.index_select(1, token_indices), # [B, window_size * img_len, C] + v .index_select(1, token_indices), + heads=self.num_heads, + ) + + x_list.append(x) + + x = torch.cat(x_list, dim=1) + del x_list, q, k, v, q_, k_ + + x = self.o(x) + return x + + + + +class ReWanT2VSlidingCrossAttention(ReWanSlidingSelfAttention): + + def forward(self, x, context, context_clip=None, mask=None, grid_sizes=None): + r""" + Args: + x(Tensor): Shape [B, L1, C] + context(Tensor): Shape [B, L2, C] + """ + # compute query, key, value + q = self.norm_q(self.q(x)) + k = self.norm_k(self.k(context)) + v = self.v(context) + + img_len = grid_sizes[1] * grid_sizes[2] + total_frames = int(q.shape[1] // img_len) + + window_size = self.winderz + half_window = window_size // 2 + + b, s, n, d = *x.shape[:2], self.num_heads, self.head_dim + q_, k_ = q, k + #q_ = q.view(b, s, n * d) + #k_ = k.view(b, s, n * d) + x_list = [] + + for i in range(total_frames): + q_start = i * img_len + q_end = (i + 1) * img_len + + # circular frame indices for key/value window + center = i + #window_indices = [(center + offset) % total_frames for offset in range(-half_window, half_window + 1)] + if self.winderz_type == "standard": + start = max(0, center - half_window) + end = min(total_frames, center + half_window + 1) + # Shift window if it would be too short + if end - start < window_size: + if start == 0: + end = min(total_frames, start + window_size) + elif end == total_frames: + start = max(0, end - window_size) + + window_indices = list(range(start, end)) + elif self.winderz_type == "circular": + window_indices = [(center + offset) % total_frames for offset in range(-half_window, half_window + 1)] + + # frame indices to token indices + token_indices = [] + for frame in window_indices: + start = frame * img_len + token_indices.extend(range(start, start + img_len)) + + token_indices = torch.tensor(token_indices, device=q.device) + + x = optimized_attention( + q_[:, q_start:q_end, :], # [B, img_len, C] + k_, #.index_select(1, token_indices), # [B, window_size * img_len, C] + v , #.index_select(1, token_indices), + heads=self.num_heads, + ) + + x_list.append(x) + + x = torch.cat(x_list, dim=1) + del x_list, q, k, v, q_, k_ + + x = self.o(x) + return x + + + + +class ReWanSelfAttention(nn.Module): + + def __init__(self, + dim, + num_heads, + window_size = (-1, -1), + qk_norm = True, + eps = 1e-6, + operation_settings = {}): + assert dim % num_heads == 0 + super().__init__() + self.dim = dim + self.num_heads = num_heads + self.head_dim = dim // num_heads + self.window_size = window_size + self.qk_norm = qk_norm + self.eps = eps + + # layers + self.q = operation_settings.get("operations").Linear(dim, dim, device=operation_settings.get("device"), dtype=operation_settings.get("dtype")) + self.k = operation_settings.get("operations").Linear(dim, dim, device=operation_settings.get("device"), dtype=operation_settings.get("dtype")) + self.v = operation_settings.get("operations").Linear(dim, dim, device=operation_settings.get("device"), dtype=operation_settings.get("dtype")) + self.o = operation_settings.get("operations").Linear(dim, dim, device=operation_settings.get("device"), dtype=operation_settings.get("dtype")) + self.norm_q = RMSNorm(dim, eps=eps, elementwise_affine=True, device=operation_settings.get("device"), dtype=operation_settings.get("dtype")) if qk_norm else nn.Identity() + self.norm_k = RMSNorm(dim, eps=eps, elementwise_affine=True, device=operation_settings.get("device"), dtype=operation_settings.get("dtype")) if qk_norm else nn.Identity() + + + def forward(self, x, freqs, mask=None, grid_sizes=None): + r""" + Args: + x(Tensor): Shape [B, L, num_heads, C / num_heads] + freqs(Tensor): Rope freqs, shape [1024, C / num_heads / 2] + """ + b, s, n, d = *x.shape[:2], self.num_heads, self.head_dim + + # query, key, value function + def qkv_fn(x): + q = self.norm_q(self.q(x)).view(b, s, n, d) + k = self.norm_k(self.k(x)).view(b, s, n, d) + v = self.v(x).view(b, s, n * d) + return q, k, v + + q, k, v = qkv_fn(x) + q, k = apply_rope(q, k, freqs) + # q,k.shape = 2,14040,12,128 v.shape = 2,14040,1536 + + if mask is not None and mask.shape[-1] > 0: + #dtype = mask.dtype if mask.dtype == torch.bool else q.dtype + #txt_len = mask.shape[1] - mask.shape[0] + x = attention_pytorch( + q.view(b, s, n * d), + k.view(b, s, n * d), + v, + heads=self.num_heads, + mask=mask#[:,txt_len:].to(dtype) + ) + else: + x = optimized_attention( + q.view(b, s, n * d), + k.view(b, s, n * d), + v, + heads=self.num_heads, + ) + + x = self.o(x) + return x + + +class ReWanT2VRawCrossAttention(ReWanSelfAttention): + + def forward(self, x, context, context_clip=None, mask=None, grid_sizes=None): + r""" + Args: + x(Tensor): Shape [B, L1, C] + context(Tensor): Shape [B, L2, C] + """ + # compute query, key, value + q = self.norm_q(self.q(x)) + k = self.norm_k(self.k(context)) + v = self.v(context) + + x = optimized_attention(q, k, v, heads=self.num_heads, mask=None) + + x = self.o(x) + return x + + +class ReWanT2VCrossAttention(ReWanSelfAttention): + + def forward(self, x, context, context_clip=None, mask=None, grid_sizes=None): + r""" + Args: + x(Tensor): Shape [B, L1, C] + context(Tensor): Shape [B, L2, C] + """ + # compute query, key, value + q = self.norm_q(self.q(x)) + k = self.norm_k(self.k(context)) + v = self.v(context) + #if mask is not None: + # num_repeats = q.shape[1] // mask.shape[0] + # mask = mask.repeat(num_repeats, 1) + # compute attention # x.shape 2,14040,1536 q.shape 2,14040,1536 k,v.shape = 2,512,1536 mask = 14040,512 num_heads=12 + if mask is not None: # and (mask.shape[-1] - mask.shape[-2]) == k.shape[-2]: # need mask shape 11664,5120 + #dtype = mask.dtype if mask.dtype == torch.bool else q.dtype + dtype = torch.bool + x = attention_pytorch(q, k, v, heads=self.num_heads, mask=mask.to(q.device).bool()) + + #x = attention_pytorch(q, k, v, heads=self.num_heads, mask=mask[:,:k.shape[-2]].to(q.device).bool()) + else: + x = optimized_attention(q, k, v, heads=self.num_heads, mask=None) + + x = self.o(x) + return x + + +class ReWanI2VCrossAttention(ReWanSelfAttention): # image2video only + + def __init__(self, + dim, + num_heads, + window_size=(-1, -1), + qk_norm=True, + eps=1e-6, operation_settings={}, ): + super().__init__(dim, num_heads, window_size, qk_norm, eps, operation_settings=operation_settings) + + self.k_img = operation_settings.get("operations").Linear(dim, dim, device=operation_settings.get("device"), dtype=operation_settings.get("dtype")) + self.v_img = operation_settings.get("operations").Linear(dim, dim, device=operation_settings.get("device"), dtype=operation_settings.get("dtype")) + # self.alpha = nn.Parameter(torch.zeros((1, ))) + self.norm_k_img = RMSNorm(dim, eps=eps, elementwise_affine=True, device=operation_settings.get("device"), dtype=operation_settings.get("dtype")) if qk_norm else nn.Identity() + + def forward(self, x, context, context_clip=None, mask=None, grid_sizes=None): + r""" + Args: + x(Tensor): Shape [B, L1, C] + context(Tensor): Shape [B, L2, C] + """ + """context_img = context[:, :257] + context = context[:, 257:] + mask_clip = None""" + + context_img = context_clip + + mask_clip = None + if mask is not None: + mask_clip = F.interpolate(mask[None, None, ...].to(torch.float16), (mask.shape[0], 257 * mask.shape[1]//512), mode='nearest-exact').squeeze().to(mask.dtype) + """mask_clip = [] + for i in range(mask.shape[-1]//512): + mask_clip.append(mask[:,i*512:i*512 + 257]) + mask_clip = torch.cat(mask_clip, dim=-1)""" + + # compute query, key, value + q = self.norm_q(self.q(x)) + k = self.norm_k(self.k(context)) + v = self.v(context) + k_img = self.norm_k_img(self.k_img(context_img)) + v_img = self.v_img(context_img) + img_x = optimized_attention(q, k_img, v_img, heads=self.num_heads, mask=mask_clip) + # compute attention + x = optimized_attention(q, k, v, heads=self.num_heads, mask=mask) + + # output + x = x + img_x + x = self.o(x) + return x + + +WAN_CROSSATTENTION_CLASSES = { + 't2v_cross_attn': ReWanT2VCrossAttention, + 'i2v_cross_attn': ReWanI2VCrossAttention, +} + + +class ReWanAttentionBlock(nn.Module): + + def __init__(self, + cross_attn_type, + dim, + ffn_dim, + num_heads, + window_size = (-1, -1), + qk_norm = True, + cross_attn_norm = False, + eps = 1e-6, + operation_settings = {}): + super().__init__() + self.dim = dim + self.ffn_dim = ffn_dim + self.num_heads = num_heads + self.window_size = window_size + self.qk_norm = qk_norm + self.cross_attn_norm = cross_attn_norm + self.eps = eps + + # layers + self.norm1 = operation_settings.get("operations").LayerNorm(dim, eps, elementwise_affine=False, device=operation_settings.get("device"), dtype=operation_settings.get("dtype")) + self.self_attn = ReWanSelfAttention( dim, num_heads, window_size, qk_norm, + eps, operation_settings=operation_settings) + self.norm3 = operation_settings.get("operations").LayerNorm( + dim, eps, + elementwise_affine=True, device=operation_settings.get("device"), dtype=operation_settings.get("dtype")) if cross_attn_norm else nn.Identity() + + self.cross_attn = WAN_CROSSATTENTION_CLASSES[cross_attn_type]( + dim, + num_heads, + (-1, -1), + qk_norm, + eps, + operation_settings=operation_settings) + + self.norm2 = operation_settings.get("operations").LayerNorm(dim, eps, elementwise_affine=False, device=operation_settings.get("device"), dtype=operation_settings.get("dtype")) + self.ffn = nn.Sequential( + operation_settings.get("operations").Linear(dim, ffn_dim, device=operation_settings.get("device"), dtype=operation_settings.get("dtype")), nn.GELU(approximate='tanh'), + operation_settings.get("operations").Linear(ffn_dim, dim, device=operation_settings.get("device"), dtype=operation_settings.get("dtype"))) + + # modulation + self.modulation = nn.Parameter(torch.empty(1, 6, dim, device=operation_settings.get("device"), dtype=operation_settings.get("dtype"))) + + def forward( + self, + x, + e, + freqs, + context, + context_clip=None, + self_mask=None, + cross_mask=None, + grid_sizes = None, + #mask=None, + ): + r""" + Args: + x(Tensor): Shape [B, L, C] + e(Tensor): Shape [B, 6, C] + freqs(Tensor): Rope freqs, shape [1024, C / num_heads / 2] + """ + # assert e.dtype == torch.float32 + + e = (comfy.model_management.cast_to(self.modulation, dtype=x.dtype, device=x.device) + e).chunk(6, dim=1) + # assert e[0].dtype == torch.float32 + # e = tuple with 6 elem, shape = 2,1,1536 # with length = 33 so 9 frames + # self-attention + + y = self.self_attn( + self.norm1(x) * (1 + e[1]) + e[0], + freqs, + grid_sizes=grid_sizes, + mask=self_mask) # mask[:,txt_len:]) + + x = x + y * e[2] + + # cross-attention & ffn # x,y.shape 2,14040,1536 + x = x + self.cross_attn(self.norm3(x), context, context_clip=context_clip, mask=cross_mask, grid_sizes=grid_sizes,) #mask[:,:txt_len]) + #print("before norm2 ", torch.cuda.memory_allocated() / 1024**3) + y = self.ffn(self.norm2(x) * (1 + e[4]) + e[3]) + #print("after norm2 ", torch.cuda.memory_allocated() / 1024**3) + x = x + y * e[5] + return x + + +class Head(nn.Module): + + def __init__(self, dim, out_dim, patch_size, eps=1e-6, operation_settings={}): + super().__init__() + self.dim = dim + self.out_dim = out_dim + self.patch_size = patch_size + self.eps = eps + + # layers + out_dim = math.prod(patch_size) * out_dim + self.norm = operation_settings.get("operations").LayerNorm(dim, eps, elementwise_affine=False, device=operation_settings.get("device"), dtype=operation_settings.get("dtype")) + self.head = operation_settings.get("operations").Linear (dim, out_dim, device=operation_settings.get("device"), dtype=operation_settings.get("dtype")) + + # modulation + self.modulation = nn.Parameter(torch.empty(1, 2, dim, device=operation_settings.get("device"), dtype=operation_settings.get("dtype"))) + + def forward(self, x, e): + r""" + Args: + x(Tensor): Shape [B, L1, C] + e(Tensor): Shape [B, C] + """ + # assert e.dtype == torch.float32 + e = (comfy.model_management.cast_to(self.modulation, dtype=x.dtype, device=x.device) + e.unsqueeze(1)).chunk(2, dim=1) + x = (self.head(self.norm(x) * (1 + e[1]) + e[0])) + return x + + +class MLPProj(torch.nn.Module): + + def __init__(self, in_dim, out_dim, operation_settings={}): + super().__init__() + + self.proj = torch.nn.Sequential( + operation_settings .get("operations").LayerNorm(in_dim, device=operation_settings.get("device"), dtype=operation_settings.get("dtype")), operation_settings.get("operations").Linear(in_dim, in_dim, device=operation_settings.get("device"), dtype=operation_settings.get("dtype")), + torch.nn.GELU(), operation_settings.get("operations").Linear (in_dim, out_dim, device=operation_settings.get("device"), dtype=operation_settings.get("dtype")), + operation_settings .get("operations").LayerNorm(out_dim, device=operation_settings.get("device"), dtype=operation_settings.get("dtype"))) + + def forward(self, image_embeds): + clip_extra_context_tokens = self.proj(image_embeds) + return clip_extra_context_tokens + + +class ReWanModel(torch.nn.Module): + r""" + Wan diffusion backbone supporting both text-to-video and image-to-video. + """ + + def __init__(self, + model_type = 't2v', + patch_size = (1, 2, 2), + text_len = 512, + in_dim = 16, + dim = 2048, + ffn_dim = 8192, + freq_dim = 256, + text_dim = 4096, + out_dim = 16, + num_heads = 16, + num_layers = 32, + window_size = (-1, -1), + qk_norm = True, + cross_attn_norm = True, + eps = 1e-6, + image_model = None, + device = None, + dtype = None, + operations = None, + ): + r""" + Initialize the diffusion model backbone. + + Args: + model_type (`str`, *optional*, defaults to 't2v'): + Model variant - 't2v' (text-to-video) or 'i2v' (image-to-video) + patch_size (`tuple`, *optional*, defaults to (1, 2, 2)): + 3D patch dimensions for video embedding (t_patch, h_patch, w_patch) + text_len (`int`, *optional*, defaults to 512): + Fixed length for text embeddings + in_dim (`int`, *optional*, defaults to 16): + Input video channels (C_in) + dim (`int`, *optional*, defaults to 2048): + Hidden dimension of the transformer + ffn_dim (`int`, *optional*, defaults to 8192): + Intermediate dimension in feed-forward network + freq_dim (`int`, *optional*, defaults to 256): + Dimension for sinusoidal time embeddings + text_dim (`int`, *optional*, defaults to 4096): + Input dimension for text embeddings + out_dim (`int`, *optional*, defaults to 16): + Output video channels (C_out) + num_heads (`int`, *optional*, defaults to 16): + Number of attention heads + num_layers (`int`, *optional*, defaults to 32): + Number of transformer blocks + window_size (`tuple`, *optional*, defaults to (-1, -1)): + Window size for local attention (-1 indicates global attention) + qk_norm (`bool`, *optional*, defaults to True): + Enable query/key normalization + cross_attn_norm (`bool`, *optional*, defaults to False): + Enable cross-attention normalization + eps (`float`, *optional*, defaults to 1e-6): + Epsilon value for normalization layers + """ + + super().__init__() + self.dtype = dtype + operation_settings = {"operations": operations, "device": device, "dtype": dtype} + + assert model_type in ['t2v', 'i2v'] + self.model_type = model_type + + self.patch_size = patch_size + self.text_len = text_len + self.in_dim = in_dim + self.dim = dim + self.ffn_dim = ffn_dim + self.freq_dim = freq_dim + self.text_dim = text_dim + self.out_dim = out_dim + self.num_heads = num_heads + self.num_layers = num_layers + self.window_size = window_size + self.qk_norm = qk_norm + self.cross_attn_norm = cross_attn_norm + self.eps = eps + + # embeddings + self.patch_embedding = operations.Conv3d( + in_dim, dim, kernel_size=patch_size, stride=patch_size, device=operation_settings.get("device"), dtype=operation_settings.get("dtype")) #dtype=torch.float32) + + + self.text_embedding = nn.Sequential( + operations.Linear(text_dim, dim, device=operation_settings.get("device"), dtype=operation_settings.get("dtype")), nn.GELU(approximate='tanh'), + operations.Linear(dim, dim, device=operation_settings.get("device"), dtype=operation_settings.get("dtype"))) + + self.time_embedding = nn.Sequential( + operations.Linear(freq_dim, dim, device=operation_settings.get("device"), dtype=operation_settings.get("dtype")), nn.SiLU(), operations.Linear(dim, dim, device=operation_settings.get("device"), dtype=operation_settings.get("dtype"))) + self.time_projection = nn.Sequential(nn.SiLU(), operations.Linear(dim, dim * 6, device=operation_settings.get("device"), dtype=operation_settings.get("dtype"))) + + # blocks + cross_attn_type = 't2v_cross_attn' if model_type == 't2v' else 'i2v_cross_attn' + + self.blocks = nn.ModuleList([ + ReWanAttentionBlock( + cross_attn_type, + dim, + ffn_dim, num_heads, + window_size, + qk_norm, + cross_attn_norm, + eps, + operation_settings=operation_settings) + + for _ in range(num_layers) + ]) + + # head + self.head = Head(dim, out_dim, patch_size, eps, operation_settings=operation_settings) + + d = dim // num_heads + self.rope_embedder = EmbedND(dim=d, theta=10000.0, axes_dim=[d - 4 * (d // 6), 2 * (d // 6), 2 * (d // 6)]) + + if model_type == 'i2v': + self.img_emb = MLPProj(1280, dim, operation_settings=operation_settings) + else: + self.img_emb = None + + + def invert_patch_embedding(self, z: torch.Tensor, original_shape: torch.Size, grid_sizes: Optional[Tuple[int,int,int]] = None) -> torch.Tensor: + + import torch.nn.functional as F + B, C_in, D, H, W = original_shape + pD, pH, pW = self.patch_size + sD, sH, sW = pD, pH, pW + + if z.ndim == 3: + # [B, S, C_out] -> reshape to [B, C_out, D', H', W'] + S = z.shape[1] + if grid_sizes is None: + Dp = D // pD + Hp = H // pH + Wp = W // pW + else: + Dp, Hp, Wp = grid_sizes + C_out = z.shape[2] + z = z.transpose(1, 2).reshape(B, C_out, Dp, Hp, Wp) + else: + B2, C_out, Dp, Hp, Wp = z.shape + assert B2 == B, "Batch size mismatch... ya sharked it." + + # kncokout bias + b = self.patch_embedding.bias.view(1, C_out, 1, 1, 1) + z_nobias = z - b + + # 2D filter -> pinv + w3 = self.patch_embedding.weight # [C_out, C_in, 1, pH, pW] + w2 = w3.squeeze(2) # [C_out, C_in, pH, pW] + out_ch, in_ch, kH, kW = w2.shape + W_flat = w2.view(out_ch, -1) # [C_out, in_ch*pH*pW] + W_pinv = torch.linalg.pinv(W_flat) # [in_ch*pH*pW, C_out] + + # merge depth for 2D unfold wackiness + z2 = z_nobias.permute(0,2,1,3,4).reshape(B*Dp, C_out, Hp, Wp) + + # apply pinv ... get patch vectors + z_flat = z2.reshape(B*Dp, C_out, -1) # [B*Dp, C_out, L] + x_patches = W_pinv @ z_flat # [B*Dp, in_ch*pH*pW, L] + + # fold -> spatial frames + x2 = F.fold( + x_patches, + output_size=(H, W), + kernel_size=(pH, pW), + stride=(sH, sW) + ) # → [B*Dp, C_in, H, W] + + # un-merge depth + x2 = x2.reshape(B, Dp, in_ch, H, W) # [B, Dp, C_in, H, W] + x_recon = x2.permute(0,2,1,3,4).contiguous() # [B, C_in, D, H, W] + return x_recon + + + def forward_orig( + self, + x, + t, + context, + clip_fea = None, + freqs = None, + transformer_options = {}, + UNCOND = False, + ): + r""" + Forward pass through the diffusion model + + Args: + x (Tensor): + List of input video tensors with shape [B, C_in, F, H, W] + t (Tensor): + Diffusion timesteps tensor of shape [B] + context (List[Tensor]): + List of text embeddings each with shape [B, L, C] + seq_len (`int`): + Maximum sequence length for positional encoding + clip_fea (Tensor, *optional*): + CLIP image features for image-to-video mode + y (List[Tensor], *optional*): + Conditional video inputs for image-to-video mode, same shape as x + + Returns: + List[Tensor]: + List of denoised video tensors with original input shapes [C_out, F, H / 8, W / 8] + """ + + + """trash = x[:,16:,...] + x_slice_flip = torch.cat([x[:,:16,...], torch.flip(trash, dims=[2])], dim=1) + x_slice_flip = self.patch_embedding(x_slice_flip.float()).to(x.dtype) + x = self.patch_embedding(x.float()).to(x.dtype) + x = torch.cat([x[:,:,:9,...], x_slice_flip[:,:,9:,...]], dim=2)""" + + """x1 = self.patch_embedding(x[:,:,:8,...].float()).to(x.dtype) + + x_slice = torch.cat([x[:,:16,8:,...], trash[:,:,0:9, ...]], dim=1) + + x2 = self.patch_embedding(x_slice.float()).to(x.dtype) + + x = torch.cat([x1, x2], dim=2)""" + + + y0_style_pos = transformer_options.get("y0_style_pos") + y0_style_neg = transformer_options.get("y0_style_neg") + SIGMA = t[0].clone() / 1000 + EO = transformer_options.get("ExtraOptions", ExtraOptions("")) + + # embeddings + #self.patch_embedding.to(self.time_embedding[0].weight.dtype) + x_orig = x.clone() + #x = self.patch_embedding(x.float()).to(self.time_embedding[0].weight.dtype) #next line to torch.Size([1, 5120, 17, 30, 30]) from 1,36,17,30,30 + x = self.patch_embedding(x.float()).to(x.dtype) # vram jumped from ~16-16.5 up to 17.98 gained 300mb with weights at torch.float8_e4m3fn + grid_sizes = x.shape[2:] + x = x.flatten(2).transpose(1, 2) # x.shape 1,32400,5120 bfloat16 316.4 MB + + # time embeddings + e = self.time_embedding( + sinusoidal_embedding_1d(self.freq_dim, t).to(dtype=x[0].dtype)) + e0 = self.time_projection(e).unflatten(1, (6, self.dim)) # e0.shape = 2,6,1536 tiny ( < 0.1 MB) + + # context + context = self.text_embedding(context) + + context_clip = None + if clip_fea is not None and self.img_emb is not None: + context_clip = self.img_emb(clip_fea) # bs x 257 x dim + #context = torch.concat([context_clip, context], dim=1) + + # arguments + kwargs = dict( + e = e0, + freqs = freqs, # 1,32400,1,64,2,2 bfloat16 15.8 MB + context = context, # 1,1536,5120 bfloat16 15.0 MB + context_clip = context_clip, + grid_sizes = grid_sizes) + + + + + + weight = transformer_options['reg_cond_weight'] if 'reg_cond_weight' in transformer_options else 0.0 + floor = transformer_options['reg_cond_floor'] if 'reg_cond_floor' in transformer_options else 0.0 + + floor = min(floor, weight) + + if type(weight) == float or type(weight) == int: + pass + else: + weight = weight.item() + + AttnMask = transformer_options.get('AttnMask') # somewhere around here, jumped to 20.6GB + mask = None + if AttnMask is not None and weight > 0: + mask = AttnMask.get(weight=weight) #mask_obj[0](transformer_options, weight.item()) # 32400,33936 bool 1048.6 MB + + mask_type_bool = type(mask[0][0].item()) == bool if mask is not None else False + if not mask_type_bool: + mask = mask.to(x.dtype) + + #text_len = context.shape[1] # mask_obj[0].text_len + + #mask[text_len:,text_len:] = torch.clamp(mask[text_len:,text_len:], min=floor.to(mask.device)) #ORIGINAL SELF-ATTN REGION BLEED + #reg_cond_mask = reg_cond_mask_expanded.unsqueeze(0).clone() if reg_cond_mask_expanded is not None else None + + mask_type_bool = type(mask[0][0].item()) == bool if mask is not None else False + + + + + txt_len = context.shape[1] # mask_obj[0].text_len + #txt_len = mask.shape[-1] - mask.shape[-2] if mask is not None else "Unlogic Condition" #what's the point of this? + + #self_attn_mask = mask[:, txt_len:] + #cross_attn_mask = mask[:,:txt_len ].bool() + #i = 0 + #for block in self.blocks: + for i, block in enumerate(self.blocks): + if mask_type_bool and weight < (i / (len(self.blocks)-1)) and mask is not None: + mask = mask.to(x.dtype) + + #if mask_type_bool and weight < (i / (len(self.blocks)-1)) and mask is not None: + # mask = mask.to(x.dtype) + + if mask is not None: + #if True: + # x = block(x, self_mask=None, cross_mask=mask.bool(), **kwargs) + if mask_type_bool and floor < 0 and (i / (len(self.blocks)-1)) < (-floor): # use self-attn mask until block number + x = block(x, self_mask=mask[:,txt_len:], cross_mask=mask[:,:txt_len].bool(), **kwargs) + elif mask_type_bool and floor > 0 and floor < (i / (len(self.blocks)-1)): # use self-attn mask after block number + x = block(x, self_mask=mask[:,txt_len:], cross_mask=mask[:,:txt_len].bool(), **kwargs) + #x = block(x, self_mask=None, cross_mask=mask[:,:txt_len].bool(), **kwargs) + elif floor == 0: + x = block(x, self_mask=mask[:,txt_len:], cross_mask=mask[:,:txt_len].bool(), **kwargs) + else: + #x = block(x, self_mask=mask[:,txt_len:], cross_mask=mask[:,:txt_len].bool(), **kwargs) + x = block(x, self_mask=None, cross_mask=mask[:,:txt_len].bool(), **kwargs) + + else: + x = block(x, **kwargs) + #x = block(x, mask=mask, **kwargs) + + #i += 1 + + # head + x = self.head(x, e) + + # unpatchify + eps = self.unpatchify(x, grid_sizes) + + + + + + + + + + + dtype = eps.dtype if self.style_dtype is None else self.style_dtype + pinv_dtype = torch.float32 if dtype != torch.float64 else dtype + W_inv = None + + + #if eps.shape[0] == 2 or (eps.shape[0] == 1 and not UNCOND): + if y0_style_pos is not None: + y0_style_pos_weight = transformer_options.get("y0_style_pos_weight") + y0_style_pos_synweight = transformer_options.get("y0_style_pos_synweight") + y0_style_pos_synweight *= y0_style_pos_weight + + y0_style_pos = y0_style_pos.to(torch.float32) + x = x_orig.clone().to(torch.float32) + eps = eps.to(torch.float32) + eps_orig = eps.clone() + + sigma = SIGMA #t_orig[0].to(torch.float32) / 1000 + denoised = x - sigma * eps + + + img = comfy.ldm.common_dit.pad_to_patch_size(denoised, self.patch_size) + patch_size = self.patch_size + + denoised_embed = self.patch_embedding(img.float()) #.to(x.dtype) # vram jumped from ~16-16.5 up to 17.98 gained 300mb with weights at torch.float8_e4m3fn + grid_sizes = denoised_embed.shape[2:] + denoised_embed = denoised_embed.flatten(2).transpose(1, 2) + + + img_y0_adain = comfy.ldm.common_dit.pad_to_patch_size(y0_style_pos, self.patch_size) + patch_size = self.patch_size + + y0_adain_embed = self.patch_embedding(img_y0_adain.float()) #.to(x.dtype) # vram jumped from ~16-16.5 up to 17.98 gained 300mb with weights at torch.float8_e4m3fn + grid_sizes = y0_adain_embed.shape[2:] + y0_adain_embed = y0_adain_embed.flatten(2).transpose(1, 2) + + + if transformer_options['y0_style_method'] == "AdaIN": + denoised_embed = adain_seq_inplace(denoised_embed, y0_adain_embed) + for adain_iter in range(EO("style_iter", 0)): + denoised_embed = adain_seq_inplace(denoised_embed, y0_adain_embed) + #denoised_embed = (denoised_embed - b) @ torch.linalg.pinv(W.to(pinv_dtype)).T.to(dtype) + denoised_embed = self.invert_patch_embedding(denoised_embed, x_orig.shape, grid_sizes) + denoised_embed = self.patch_embedding(denoised_embed.float()) #.to(x.dtype) # vram jumped from ~16-16.5 up to 17.98 gained 300mb with weights at torch.float8_e4m3fn + grid_sizes = denoised_embed.shape[2:] + denoised_embed = denoised_embed.flatten(2).transpose(1, 2) + + #denoised_embed = F.linear(denoised_embed .to(W), W, b).to(img) + denoised_embed = adain_seq_inplace(denoised_embed, y0_adain_embed) + + + + + elif transformer_options['y0_style_method'] == "WCT": + if self.y0_adain_embed is None or self.y0_adain_embed.shape != y0_adain_embed.shape or torch.norm(self.y0_adain_embed - y0_adain_embed) > 0: + self.y0_adain_embed = y0_adain_embed + + f_s = y0_adain_embed[0].clone() + self.mu_s = f_s.mean(dim=0, keepdim=True) + f_s_centered = f_s - self.mu_s + + cov = (f_s_centered.T.double() @ f_s_centered.double()) / (f_s_centered.size(0) - 1) + + S_eig, U_eig = torch.linalg.eigh(cov + 1e-5 * torch.eye(cov.size(0), dtype=cov.dtype, device=cov.device)) + S_eig_sqrt = S_eig.clamp(min=0).sqrt() # eigenvalues -> singular values + + whiten = U_eig @ torch.diag(S_eig_sqrt) @ U_eig.T + self.y0_color = whiten.to(f_s_centered) + + for wct_i in range(eps.shape[0]): + f_c = denoised_embed[wct_i].clone() + mu_c = f_c.mean(dim=0, keepdim=True) + f_c_centered = f_c - mu_c + + cov = (f_c_centered.T.double() @ f_c_centered.double()) / (f_c_centered.size(0) - 1) + + S_eig, U_eig = torch.linalg.eigh(cov + 1e-5 * torch.eye(cov.size(0), dtype=cov.dtype, device=cov.device)) + inv_sqrt_eig = S_eig.clamp(min=0).rsqrt() + + whiten = U_eig @ torch.diag(inv_sqrt_eig) @ U_eig.T + whiten = whiten.to(f_c_centered) + + f_c_whitened = f_c_centered @ whiten.T + f_cs = f_c_whitened @ self.y0_color.T + self.mu_s + + denoised_embed[wct_i] = f_cs + + denoised_approx = self.invert_patch_embedding(denoised_embed, x_orig.shape, grid_sizes) + + denoised_approx = denoised_approx.to(eps) + + eps = (x - denoised_approx) / sigma + #if eps.shape[0] == 2: + # eps[1] = eps_orig[1] + y0_style_pos_weight * (eps[1] - eps_orig[1]) + # eps[0] = eps_orig[0] + y0_style_pos_synweight * (eps[0] - eps_orig[0]) + #else: + # eps[0] = eps_orig[0] + y0_style_pos_weight * (eps[0] - eps_orig[0]) + + if not UNCOND: + if eps.shape[0] == 2: + eps[1] = eps_orig[1] + y0_style_pos_weight * (eps[1] - eps_orig[1]) + eps[0] = eps_orig[0] + y0_style_pos_synweight * (eps[0] - eps_orig[0]) + else: + eps[0] = eps_orig[0] + y0_style_pos_weight * (eps[0] - eps_orig[0]) + elif eps.shape[0] == 1 and UNCOND: + eps[0] = eps_orig[0] + y0_style_pos_synweight * (eps[0] - eps_orig[0]) + + eps = eps.float() + + + #if eps.shape[0] == 2 or (eps.shape[0] == 1 and UNCOND): + if y0_style_neg is not None: + y0_style_neg_weight = transformer_options.get("y0_style_neg_weight") + y0_style_neg_synweight = transformer_options.get("y0_style_neg_synweight") + y0_style_neg_synweight *= y0_style_neg_weight + + y0_style_neg = y0_style_neg.to(torch.float32) + x = x_orig.clone().to(torch.float32) + eps = eps.to(torch.float32) + eps_orig = eps.clone() + + sigma = SIGMA #t_orig[0].to(torch.float32) / 1000 + denoised = x - sigma * eps + + + img = comfy.ldm.common_dit.pad_to_patch_size(denoised, self.patch_size) + patch_size = self.patch_size + + denoised_embed = self.patch_embedding(img.float()) #.to(x.dtype) # vram jumped from ~16-16.5 up to 17.98 gained 300mb with weights at torch.float8_e4m3fn + grid_sizes = denoised_embed.shape[2:] + denoised_embed = denoised_embed.flatten(2).transpose(1, 2) + + + img_y0_adain = comfy.ldm.common_dit.pad_to_patch_size(y0_style_neg, self.patch_size) + patch_size = self.patch_size + + y0_adain_embed = self.patch_embedding(img_y0_adain.float()) #.to(x.dtype) # vram jumped from ~16-16.5 up to 17.98 gained 300mb with weights at torch.float8_e4m3fn + grid_sizes = y0_adain_embed.shape[2:] + y0_adain_embed = y0_adain_embed.flatten(2).transpose(1, 2) + + + if transformer_options['y0_style_method'] == "AdaIN": + denoised_embed = adain_seq_inplace(denoised_embed, y0_adain_embed) + for adain_iter in range(EO("style_iter", 0)): + denoised_embed = adain_seq_inplace(denoised_embed, y0_adain_embed) + #denoised_embed = (denoised_embed - b) @ torch.linalg.pinv(W.to(pinv_dtype)).T.to(dtype) + denoised_embed = self.invert_patch_embedding(denoised_embed, x_orig.shape, grid_sizes) + denoised_embed = self.patch_embedding(denoised_embed.float()) #.to(x.dtype) # vram jumped from ~16-16.5 up to 17.98 gained 300mb with weights at torch.float8_e4m3fn + grid_sizes = denoised_embed.shape[2:] + denoised_embed = denoised_embed.flatten(2).transpose(1, 2) + + #denoised_embed = F.linear(denoised_embed .to(W), W, b).to(img) + denoised_embed = adain_seq_inplace(denoised_embed, y0_adain_embed) + + + + + elif transformer_options['y0_style_method'] == "WCT": + if self.y0_adain_embed is None or self.y0_adain_embed.shape != y0_adain_embed.shape or torch.norm(self.y0_adain_embed - y0_adain_embed) > 0: + self.y0_adain_embed = y0_adain_embed + + f_s = y0_adain_embed[0].clone() + self.mu_s = f_s.mean(dim=0, keepdim=True) + f_s_centered = f_s - self.mu_s + + cov = (f_s_centered.T.double() @ f_s_centered.double()) / (f_s_centered.size(0) - 1) + + S_eig, U_eig = torch.linalg.eigh(cov + 1e-5 * torch.eye(cov.size(0), dtype=cov.dtype, device=cov.device)) + S_eig_sqrt = S_eig.clamp(min=0).sqrt() # eigenvalues -> singular values + + whiten = U_eig @ torch.diag(S_eig_sqrt) @ U_eig.T + self.y0_color = whiten.to(f_s_centered) + + for wct_i in range(eps.shape[0]): + f_c = denoised_embed[wct_i].clone() + mu_c = f_c.mean(dim=0, keepdim=True) + f_c_centered = f_c - mu_c + + cov = (f_c_centered.T.double() @ f_c_centered.double()) / (f_c_centered.size(0) - 1) + + S_eig, U_eig = torch.linalg.eigh(cov + 1e-5 * torch.eye(cov.size(0), dtype=cov.dtype, device=cov.device)) + inv_sqrt_eig = S_eig.clamp(min=0).rsqrt() + + whiten = U_eig @ torch.diag(inv_sqrt_eig) @ U_eig.T + whiten = whiten.to(f_c_centered) + + f_c_whitened = f_c_centered @ whiten.T + f_cs = f_c_whitened @ self.y0_color.T + self.mu_s + + denoised_embed[wct_i] = f_cs + + denoised_approx = self.invert_patch_embedding(denoised_embed, x_orig.shape, grid_sizes) + + denoised_approx = denoised_approx.to(eps) + + #eps = (x - denoised_approx) / sigma + #eps[0] = eps_orig[0] + y0_style_neg_weight * (eps[0] - eps_orig[0]) + #if eps.shape[0] == 2: + # eps[1] = eps_orig[1] + y0_style_neg_synweight * (eps[1] - eps_orig[1]) + + if UNCOND: + eps = (x - denoised_approx) / sigma + eps[0] = eps_orig[0] + y0_style_neg_weight * (eps[0] - eps_orig[0]) + if eps.shape[0] == 2: + eps[1] = eps_orig[1] + y0_style_neg_synweight * (eps[1] - eps_orig[1]) + elif eps.shape[0] == 1 and not UNCOND: + eps[0] = eps_orig[0] + y0_style_neg_synweight * (eps[0] - eps_orig[0]) + + eps = eps.float() + + + + + + + + + + + + + + + + + + return eps + + + + + + + # context.shape = 2,512,1536 x.shape = 2,14040,1536 timestep.shape h_len=30, w_len=52 30 * 52 = 1560 + def forward(self, x, timestep, context, clip_fea=None, transformer_options={}, **kwargs): + + """if False: #clip_fea is not None: + bs, c, t, h, w = x.shape + x = comfy.ldm.common_dit.pad_to_patch_size(x, self.patch_size) + patch_size = self.patch_size # tuple = 1,2,2, + + t_len = ((t + (patch_size[0] // 2)) // patch_size[0]) + h_len = ((h + (patch_size[1] // 2)) // patch_size[1]) + w_len = ((w + (patch_size[2] // 2)) // patch_size[2]) + + img_ids = torch.zeros((t_len, h_len, w_len, 3), device=x.device, dtype=x.dtype) + + img_ids[:, :, :, 0] = img_ids[:, :, :, 0] + torch.linspace(0, t_len - 1, steps=t_len, device=x.device, dtype=x.dtype).reshape(-1, 1, 1) + img_ids[:, :, :, 1] = img_ids[:, :, :, 1] + torch.linspace(0, h_len - 1, steps=h_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) + # 14040 = 9 * 1560 1560 = 1536 + 24 1560/24 = 65 + freqs = self.rope_embedder(img_ids).movedim(1, 2) + return self.forward_orig(x, timestep, context, clip_fea=clip_fea, freqs=freqs)[:, :, :t, :h, :w]""" + + + + + #x = torch.cat([x[:,:,:8,...], torch.flip(x[:,:,8:,...], dims=[2])], dim=2) + + x_orig = x.clone() # 1,16,36,60,60 bfloat16 + timestep_orig = timestep.clone() # 1000 float32 + context_orig = context.clone() # 1,512,4096 bfloat16 + + + out_list = [] + for i in range(len(transformer_options['cond_or_uncond'])): + UNCOND = transformer_options['cond_or_uncond'][i] == 1 + + x = x_orig.clone() + timestep = timestep_orig.clone() + context = context_orig.clone() + + + bs, c, t, h, w = x.shape + x = comfy.ldm.common_dit.pad_to_patch_size(x, self.patch_size) + patch_size = self.patch_size + + + + transformer_options['original_shape'] = x.shape + transformer_options['patch_size'] = patch_size + + + """if UNCOND: + transformer_options['reg_cond_weight'] = 0.0 # -1 + context_tmp = context[i][None,...].clone()""" + + if UNCOND: + #transformer_options['reg_cond_weight'] = -1 + #context_tmp = context[i][None,...].clone() + + transformer_options['reg_cond_weight'] = transformer_options.get("regional_conditioning_weight", 0.0) #transformer_options['regional_conditioning_weight'] + transformer_options['reg_cond_floor'] = transformer_options.get("regional_conditioning_floor", 0.0) #transformer_options['regional_conditioning_floor'] #if "regional_conditioning_floor" in transformer_options else 0.0 + transformer_options['reg_cond_mask_orig'] = transformer_options.get('regional_conditioning_mask_orig') + + AttnMask = transformer_options.get('AttnMask', None) + RegContext = transformer_options.get('RegContext', None) + + if AttnMask is not None and transformer_options['reg_cond_weight'] != 0.0: + AttnMask.attn_mask_recast(x.dtype) + context_tmp = RegContext.get().to(context.dtype) + clip_fea = RegContext.get_clip_fea() + clip_fea = clip_fea.to(x.dtype) if clip_fea else None + + A = context[i][None,...].clone() + B = context_tmp + context_tmp = A.repeat(1, (B.shape[1] // A.shape[1]) + 1, 1)[:, :B.shape[1], :] + + else: + context_tmp = context[i][None,...].clone() + + elif UNCOND == False: + transformer_options['reg_cond_weight'] = transformer_options.get("regional_conditioning_weight", 0.0) #transformer_options['regional_conditioning_weight'] + transformer_options['reg_cond_floor'] = transformer_options.get("regional_conditioning_floor", 0.0) #transformer_options['regional_conditioning_floor'] #if "regional_conditioning_floor" in transformer_options else 0.0 + transformer_options['reg_cond_mask_orig'] = transformer_options.get('regional_conditioning_mask_orig') + + AttnMask = transformer_options.get('AttnMask', None) + RegContext = transformer_options.get('RegContext', None) + + if AttnMask is not None and transformer_options['reg_cond_weight'] != 0.0: + AttnMask.attn_mask_recast(x.dtype) + context_tmp = RegContext.get() + clip_fea = RegContext.get_clip_fea() + clip_fea = clip_fea.to(x.dtype) if clip_fea else None + else: + context_tmp = context[i][None,...].clone() + + if context_tmp is None: + context_tmp = context[i][None,...].clone() + context_tmp = context_tmp.to(context.dtype) + + + + t_len = ((t + (patch_size[0] // 2)) // patch_size[0]) + h_len = ((h + (patch_size[1] // 2)) // patch_size[1]) + w_len = ((w + (patch_size[2] // 2)) // patch_size[2]) + + img_ids = torch.zeros((t_len, h_len, w_len, 3), device=x.device, dtype=x.dtype) + + img_ids[:, :, :, 0] = img_ids[:, :, :, 0] + torch.linspace(0, t_len - 1, steps=t_len, device=x.device, dtype=x.dtype).reshape(-1, 1, 1) + img_ids[:, :, :, 1] = img_ids[:, :, :, 1] + torch.linspace(0, h_len - 1, steps=h_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) + # 14040 = 9 * 1560 1560 = 1536 + 24 1560/24 = 65 + freqs = self.rope_embedder(img_ids).movedim(1, 2).to(x.dtype) + + + + + out_x = self.forward_orig( + x [i][None,...], + timestep [i][None,...], + context_tmp, + clip_fea = clip_fea, + freqs = freqs[i][None,...], + transformer_options = transformer_options, + UNCOND = UNCOND, + )[:, :, :t, :h, :w] + + #out_x = torch.cat([out_x[:,:,:8,...], torch.flip(out_x[:,:,8:,...], dims=[2])], dim=2) + out_list.append(out_x) + + out_stack = torch.stack(out_list, dim=0).squeeze(dim=1) + + + + + + + + + + + + return out_stack + + + def unpatchify(self, x, grid_sizes): + r""" + Reconstruct video tensors from patch embeddings. + + Args: + x (List[Tensor]): + List of patchified features, each with shape [L, C_out * prod(patch_size)] + grid_sizes (Tensor): + Original spatial-temporal grid dimensions before patching, + shape [B, 3] (3 dimensions correspond to F_patches, H_patches, W_patches) + + Returns: + List[Tensor]: + Reconstructed video tensors with shape [L, C_out, F, H / 8, W / 8] + """ + + c = self.out_dim + u = x + b = u.shape[0] + u = u[:, :math.prod(grid_sizes)].view(b, *grid_sizes, *self.patch_size, c) + u = torch.einsum('bfhwpqrc->bcfphqwr', u) + u = u.reshape(b, c, *[i * j for i, j in zip(grid_sizes, self.patch_size)]) + + + + return u + + + + +def adain_seq_inplace(content: torch.Tensor, style: torch.Tensor, eps: float = 1e-7) -> torch.Tensor: + mean_c = content.mean(1, keepdim=True) + std_c = content.std (1, keepdim=True).add_(eps) + mean_s = style.mean (1, keepdim=True) + std_s = style.std (1, keepdim=True).add_(eps) + + content.sub_(mean_c).div_(std_c).mul_(std_s).add_(mean_s) + return content diff --git a/ComfyUI/custom_nodes/RES4LYF/wan/vae.py b/ComfyUI/custom_nodes/RES4LYF/wan/vae.py new file mode 100644 index 0000000000000000000000000000000000000000..a8ebc5ec6c4ee2cbce802837c052004eb8cc362c --- /dev/null +++ b/ComfyUI/custom_nodes/RES4LYF/wan/vae.py @@ -0,0 +1,567 @@ +# original version: https://github.com/Wan-Video/Wan2.1/blob/main/wan/modules/vae.py +# Copyright 2024-2025 The Alibaba Wan Team Authors. All rights reserved. + +import torch +import torch.nn as nn +import torch.nn.functional as F +from einops import rearrange +from comfy.ldm.modules.diffusionmodules.model import vae_attention + +import comfy.ops +ops = comfy.ops.disable_weight_init + +CACHE_T = 2 + + +class CausalConv3d(ops.Conv3d): + """ + Causal 3d convolusion. + """ + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self._padding = (self.padding[2], self.padding[2], self.padding[1], + self.padding[1], 2 * self.padding[0], 0) + self.padding = (0, 0, 0) + + def forward(self, x, cache_x=None): + padding = list(self._padding) + if cache_x is not None and self._padding[4] > 0: + cache_x = cache_x.to(x.device) + x = torch.cat([cache_x, x], dim=2) + padding[4] -= cache_x.shape[2] + x = F.pad(x, padding) + + return super().forward(x) + + +class RMS_norm(nn.Module): + + def __init__(self, dim, channel_first=True, images=True, bias=False): + super().__init__() + broadcastable_dims = (1, 1, 1) if not images else (1, 1) + shape = (dim, *broadcastable_dims) if channel_first else (dim,) + + self.channel_first = channel_first + self.scale = dim**0.5 + self.gamma = nn.Parameter(torch.ones(shape)) + self.bias = nn.Parameter(torch.zeros(shape)) if bias else None + + def forward(self, x): + return F.normalize( + x, dim=(1 if self.channel_first else -1)) * self.scale * self.gamma.to(x) + (self.bias.to(x) if self.bias is not None else 0) + + +class Upsample(nn.Upsample): + + def forward(self, x): + """ + Fix bfloat16 support for nearest neighbor interpolation. + """ + return super().forward(x.float()).type_as(x) + + +class Resample(nn.Module): + + def __init__(self, dim, mode): + assert mode in ('none', 'upsample2d', 'upsample3d', 'downsample2d', + 'downsample3d') + super().__init__() + self.dim = dim + self.mode = mode + + # layers + if mode == 'upsample2d': + self.resample = nn.Sequential( + Upsample(scale_factor=(2., 2.), mode='nearest-exact'), + ops.Conv2d(dim, dim // 2, 3, padding=1)) + elif mode == 'upsample3d': + self.resample = nn.Sequential( + Upsample(scale_factor=(2., 2.), mode='nearest-exact'), + ops.Conv2d(dim, dim // 2, 3, padding=1)) + self.time_conv = CausalConv3d( + dim, dim * 2, (3, 1, 1), padding=(1, 0, 0)) + + elif mode == 'downsample2d': + self.resample = nn.Sequential( + nn.ZeroPad2d((0, 1, 0, 1)), + ops.Conv2d(dim, dim, 3, stride=(2, 2))) + elif mode == 'downsample3d': + self.resample = nn.Sequential( + nn.ZeroPad2d((0, 1, 0, 1)), + ops.Conv2d(dim, dim, 3, stride=(2, 2))) + self.time_conv = CausalConv3d( + dim, dim, (3, 1, 1), stride=(2, 1, 1), padding=(0, 0, 0)) + + else: + self.resample = nn.Identity() + + def forward(self, x, feat_cache=None, feat_idx=[0]): + b, c, t, h, w = x.size() + if self.mode == 'upsample3d': + if feat_cache is not None: + idx = feat_idx[0] + if feat_cache[idx] is None: + feat_cache[idx] = 'Rep' + feat_idx[0] += 1 + else: + + cache_x = x[:, :, -CACHE_T:, :, :].clone() + if cache_x.shape[2] < 2 and feat_cache[ + idx] is not None and feat_cache[idx] != 'Rep': + # cache last frame of last two chunk + cache_x = torch.cat([ + feat_cache[idx][:, :, -1, :, :].unsqueeze(2).to( + cache_x.device), cache_x + ], + dim=2) + if cache_x.shape[2] < 2 and feat_cache[ + idx] is not None and feat_cache[idx] == 'Rep': + cache_x = torch.cat([ + torch.zeros_like(cache_x).to(cache_x.device), + cache_x + ], + dim=2) + if feat_cache[idx] == 'Rep': + x = self.time_conv(x) + else: + x = self.time_conv(x, feat_cache[idx]) + feat_cache[idx] = cache_x + feat_idx[0] += 1 + + x = x.reshape(b, 2, c, t, h, w) + x = torch.stack((x[:, 0, :, :, :, :], x[:, 1, :, :, :, :]), + 3) + x = x.reshape(b, c, t * 2, h, w) + t = x.shape[2] + x = rearrange(x, 'b c t h w -> (b t) c h w') + x = self.resample(x) + x = rearrange(x, '(b t) c h w -> b c t h w', t=t) + + if self.mode == 'downsample3d': + if feat_cache is not None: + idx = feat_idx[0] + if feat_cache[idx] is None: + feat_cache[idx] = x.clone() + feat_idx[0] += 1 + else: + + cache_x = x[:, :, -1:, :, :].clone() + # if cache_x.shape[2] < 2 and feat_cache[idx] is not None and feat_cache[idx]!='Rep': + # # cache last frame of last two chunk + # cache_x = torch.cat([feat_cache[idx][:, :, -1, :, :].unsqueeze(2).to(cache_x.device), cache_x], dim=2) + + x = self.time_conv( + torch.cat([feat_cache[idx][:, :, -1:, :, :], x], 2)) + feat_cache[idx] = cache_x + feat_idx[0] += 1 + return x + + def init_weight(self, conv): + conv_weight = conv.weight + nn.init.zeros_(conv_weight) + c1, c2, t, h, w = conv_weight.size() + one_matrix = torch.eye(c1, c2) + init_matrix = one_matrix + nn.init.zeros_(conv_weight) + #conv_weight.data[:,:,-1,1,1] = init_matrix * 0.5 + conv_weight.data[:, :, 1, 0, 0] = init_matrix #* 0.5 + conv.weight.data.copy_(conv_weight) + nn.init.zeros_(conv.bias.data) + + def init_weight2(self, conv): + conv_weight = conv.weight.data + nn.init.zeros_(conv_weight) + c1, c2, t, h, w = conv_weight.size() + init_matrix = torch.eye(c1 // 2, c2) + #init_matrix = repeat(init_matrix, 'o ... -> (o 2) ...').permute(1,0,2).contiguous().reshape(c1,c2) + conv_weight[:c1 // 2, :, -1, 0, 0] = init_matrix + conv_weight[c1 // 2:, :, -1, 0, 0] = init_matrix + conv.weight.data.copy_(conv_weight) + nn.init.zeros_(conv.bias.data) + + +class ResidualBlock(nn.Module): + + def __init__(self, in_dim, out_dim, dropout=0.0): + super().__init__() + self.in_dim = in_dim + self.out_dim = out_dim + + # layers + self.residual = nn.Sequential( + RMS_norm(in_dim, images=False), nn.SiLU(), + CausalConv3d(in_dim, out_dim, 3, padding=1), + RMS_norm(out_dim, images=False), nn.SiLU(), nn.Dropout(dropout), + CausalConv3d(out_dim, out_dim, 3, padding=1)) + self.shortcut = CausalConv3d(in_dim, out_dim, 1) \ + if in_dim != out_dim else nn.Identity() + + def forward(self, x, feat_cache=None, feat_idx=[0]): + h = self.shortcut(x) + for layer in self.residual: + if isinstance(layer, CausalConv3d) and feat_cache is not None: + idx = feat_idx[0] + cache_x = x[:, :, -CACHE_T:, :, :].clone() + if cache_x.shape[2] < 2 and feat_cache[idx] is not None: + # cache last frame of last two chunk + cache_x = torch.cat([ + feat_cache[idx][:, :, -1, :, :].unsqueeze(2).to( + cache_x.device), cache_x + ], + dim=2) + x = layer(x, feat_cache[idx]) + feat_cache[idx] = cache_x + feat_idx[0] += 1 + else: + x = layer(x) + return x + h + + +class AttentionBlock(nn.Module): + """ + Causal self-attention with a single head. + """ + + def __init__(self, dim): + super().__init__() + self.dim = dim + + # layers + self.norm = RMS_norm(dim) + self.to_qkv = ops.Conv2d(dim, dim * 3, 1) + self.proj = ops.Conv2d(dim, dim, 1) + self.optimized_attention = vae_attention() + + def forward(self, x): + identity = x + b, c, t, h, w = x.size() + x = rearrange(x, 'b c t h w -> (b t) c h w') + x = self.norm(x) + # compute query, key, value + + q, k, v = self.to_qkv(x).chunk(3, dim=1) + x = self.optimized_attention(q, k, v) + + # output + x = self.proj(x) + x = rearrange(x, '(b t) c h w-> b c t h w', t=t) + return x + identity + + +class Encoder3d(nn.Module): + + def __init__(self, + dim=128, + z_dim=4, + dim_mult=[1, 2, 4, 4], + num_res_blocks=2, + attn_scales=[], + temperal_downsample=[True, True, False], + dropout=0.0): + super().__init__() + self.dim = dim + self.z_dim = z_dim + self.dim_mult = dim_mult + self.num_res_blocks = num_res_blocks + self.attn_scales = attn_scales + self.temperal_downsample = temperal_downsample + + # dimensions + dims = [dim * u for u in [1] + dim_mult] + scale = 1.0 + + # init block + self.conv1 = CausalConv3d(3, dims[0], 3, padding=1) + + # downsample blocks + downsamples = [] + for i, (in_dim, out_dim) in enumerate(zip(dims[:-1], dims[1:])): + # residual (+attention) blocks + for _ in range(num_res_blocks): + downsamples.append(ResidualBlock(in_dim, out_dim, dropout)) + if scale in attn_scales: + downsamples.append(AttentionBlock(out_dim)) + in_dim = out_dim + + # downsample block + if i != len(dim_mult) - 1: + mode = 'downsample3d' if temperal_downsample[ + i] else 'downsample2d' + downsamples.append(Resample(out_dim, mode=mode)) + scale /= 2.0 + self.downsamples = nn.Sequential(*downsamples) + + # middle blocks + self.middle = nn.Sequential( + ResidualBlock(out_dim, out_dim, dropout), AttentionBlock(out_dim), + ResidualBlock(out_dim, out_dim, dropout)) + + # output blocks + self.head = nn.Sequential( + RMS_norm(out_dim, images=False), nn.SiLU(), + CausalConv3d(out_dim, z_dim, 3, padding=1)) + + def forward(self, x, feat_cache=None, feat_idx=[0]): + if feat_cache is not None: + idx = feat_idx[0] + cache_x = x[:, :, -CACHE_T:, :, :].clone() + if cache_x.shape[2] < 2 and feat_cache[idx] is not None: + # cache last frame of last two chunk + cache_x = torch.cat([ + feat_cache[idx][:, :, -1, :, :].unsqueeze(2).to( + cache_x.device), cache_x + ], + dim=2) + x = self.conv1(x, feat_cache[idx]) + feat_cache[idx] = cache_x + feat_idx[0] += 1 + else: + x = self.conv1(x) + + ## downsamples + for layer in self.downsamples: + if feat_cache is not None: + x = layer(x, feat_cache, feat_idx) + else: + x = layer(x) + + ## middle + for layer in self.middle: + if isinstance(layer, ResidualBlock) and feat_cache is not None: + x = layer(x, feat_cache, feat_idx) + else: + x = layer(x) + + ## head + for layer in self.head: + if isinstance(layer, CausalConv3d) and feat_cache is not None: + idx = feat_idx[0] + cache_x = x[:, :, -CACHE_T:, :, :].clone() + if cache_x.shape[2] < 2 and feat_cache[idx] is not None: + # cache last frame of last two chunk + cache_x = torch.cat([ + feat_cache[idx][:, :, -1, :, :].unsqueeze(2).to( + cache_x.device), cache_x + ], + dim=2) + x = layer(x, feat_cache[idx]) + feat_cache[idx] = cache_x + feat_idx[0] += 1 + else: + x = layer(x) + return x + + +class Decoder3d(nn.Module): + + def __init__(self, + dim=128, + z_dim=4, + dim_mult=[1, 2, 4, 4], + num_res_blocks=2, + attn_scales=[], + temperal_upsample=[False, True, True], + dropout=0.0): + super().__init__() + self.dim = dim + self.z_dim = z_dim + self.dim_mult = dim_mult + self.num_res_blocks = num_res_blocks + self.attn_scales = attn_scales + self.temperal_upsample = temperal_upsample + + # dimensions + dims = [dim * u for u in [dim_mult[-1]] + dim_mult[::-1]] + scale = 1.0 / 2**(len(dim_mult) - 2) + + # init block + self.conv1 = CausalConv3d(z_dim, dims[0], 3, padding=1) + + # middle blocks + self.middle = nn.Sequential( + ResidualBlock(dims[0], dims[0], dropout), AttentionBlock(dims[0]), + ResidualBlock(dims[0], dims[0], dropout)) + + # upsample blocks + upsamples = [] + for i, (in_dim, out_dim) in enumerate(zip(dims[:-1], dims[1:])): + # residual (+attention) blocks + if i == 1 or i == 2 or i == 3: + in_dim = in_dim // 2 + for _ in range(num_res_blocks + 1): + upsamples.append(ResidualBlock(in_dim, out_dim, dropout)) + if scale in attn_scales: + upsamples.append(AttentionBlock(out_dim)) + in_dim = out_dim + + # upsample block + if i != len(dim_mult) - 1: + mode = 'upsample3d' if temperal_upsample[i] else 'upsample2d' + upsamples.append(Resample(out_dim, mode=mode)) + scale *= 2.0 + self.upsamples = nn.Sequential(*upsamples) + + # output blocks + self.head = nn.Sequential( + RMS_norm(out_dim, images=False), nn.SiLU(), + CausalConv3d(out_dim, 3, 3, padding=1)) + + def forward(self, x, feat_cache=None, feat_idx=[0]): + ## conv1 + if feat_cache is not None: + idx = feat_idx[0] + cache_x = x[:, :, -CACHE_T:, :, :].clone() + if cache_x.shape[2] < 2 and feat_cache[idx] is not None: + # cache last frame of last two chunk + cache_x = torch.cat([ + feat_cache[idx][:, :, -1, :, :].unsqueeze(2).to( + cache_x.device), cache_x + ], + dim=2) + x = self.conv1(x, feat_cache[idx]) + feat_cache[idx] = cache_x + feat_idx[0] += 1 + else: + x = self.conv1(x) + + ## middle + for layer in self.middle: + if isinstance(layer, ResidualBlock) and feat_cache is not None: + x = layer(x, feat_cache, feat_idx) + else: + x = layer(x) + + ## upsamples + for layer in self.upsamples: + if feat_cache is not None: + x = layer(x, feat_cache, feat_idx) + else: + x = layer(x) + + ## head + for layer in self.head: + if isinstance(layer, CausalConv3d) and feat_cache is not None: + idx = feat_idx[0] + cache_x = x[:, :, -CACHE_T:, :, :].clone() + if cache_x.shape[2] < 2 and feat_cache[idx] is not None: + # cache last frame of last two chunk + cache_x = torch.cat([ + feat_cache[idx][:, :, -1, :, :].unsqueeze(2).to( + cache_x.device), cache_x + ], + dim=2) + x = layer(x, feat_cache[idx]) + feat_cache[idx] = cache_x + feat_idx[0] += 1 + else: + x = layer(x) + return x + + +def count_conv3d(model): + count = 0 + for m in model.modules(): + if isinstance(m, CausalConv3d): + count += 1 + return count + + +class WanVAE(nn.Module): + + def __init__(self, + dim=128, + z_dim=4, + dim_mult=[1, 2, 4, 4], + num_res_blocks=2, + attn_scales=[], + temperal_downsample=[True, True, False], + dropout=0.0): + super().__init__() + self.dim = dim + self.z_dim = z_dim + self.dim_mult = dim_mult + self.num_res_blocks = num_res_blocks + self.attn_scales = attn_scales + self.temperal_downsample = temperal_downsample + self.temperal_upsample = temperal_downsample[::-1] + + # modules + self.encoder = Encoder3d(dim, z_dim * 2, dim_mult, num_res_blocks, + attn_scales, self.temperal_downsample, dropout) + self.conv1 = CausalConv3d(z_dim * 2, z_dim * 2, 1) + self.conv2 = CausalConv3d(z_dim, z_dim, 1) + self.decoder = Decoder3d(dim, z_dim, dim_mult, num_res_blocks, + attn_scales, self.temperal_upsample, dropout) + + def forward(self, x): + mu, log_var = self.encode(x) + z = self.reparameterize(mu, log_var) + x_recon = self.decode(z) + return x_recon, mu, log_var + + def encode(self, x): + self.clear_cache() + ## cache + t = x.shape[2] + iter_ = 1 + (t - 1) // 4 + ## 对encode输入的x,按时间拆分为1、4、4、4.... + for i in range(iter_): + self._enc_conv_idx = [0] + if i == 0: + out = self.encoder( + x[:, :, :1, :, :], + feat_cache=self._enc_feat_map, + feat_idx=self._enc_conv_idx) + else: + out_ = self.encoder( + x[:, :, 1 + 4 * (i - 1):1 + 4 * i, :, :], + feat_cache=self._enc_feat_map, + feat_idx=self._enc_conv_idx) + out = torch.cat([out, out_], 2) + mu, log_var = self.conv1(out).chunk(2, dim=1) + self.clear_cache() + return mu + + def decode(self, z): + self.clear_cache() + # z: [b,c,t,h,w] + + iter_ = z.shape[2] + x = self.conv2(z) + for i in range(iter_): + self._conv_idx = [0] + if i == 0: + out = self.decoder( + x[:, :, i:i + 1, :, :], + feat_cache=self._feat_map, + feat_idx=self._conv_idx) + else: + out_ = self.decoder( + x[:, :, i:i + 1, :, :], + feat_cache=self._feat_map, + feat_idx=self._conv_idx) + out = torch.cat([out, out_], 2) + self.clear_cache() + return out + + def reparameterize(self, mu, log_var): + std = torch.exp(0.5 * log_var) + eps = torch.randn_like(std) + return eps * std + mu + + def sample(self, imgs, deterministic=False): + mu, log_var = self.encode(imgs) + if deterministic: + return mu + std = torch.exp(0.5 * log_var.clamp(-30.0, 20.0)) + return mu + std * torch.randn_like(std) + + def clear_cache(self): + self._conv_num = count_conv3d(self.decoder) + self._conv_idx = [0] + self._feat_map = [None] * self._conv_num + #cache encode + self._enc_conv_num = count_conv3d(self.encoder) + self._enc_conv_idx = [0] + self._enc_feat_map = [None] * self._enc_conv_num diff --git a/ComfyUI/custom_nodes/RES4LYF/web/js/RES4LYF_dynamicWidgets.js b/ComfyUI/custom_nodes/RES4LYF/web/js/RES4LYF_dynamicWidgets.js new file mode 100644 index 0000000000000000000000000000000000000000..b9b3850c2898308156096c776ad9ce71cd5cda4f --- /dev/null +++ b/ComfyUI/custom_nodes/RES4LYF/web/js/RES4LYF_dynamicWidgets.js @@ -0,0 +1,440 @@ +import { app } from "../../scripts/app.js"; +import { ComfyWidgets } from "../../scripts/widgets.js"; + +let RESDEBUG = false; +let TOP_CLOWNDOG = true; +let DISPLAY_CATEGORY = true; + +let nodeCounter = 1; +const processedNodeMap = new WeakMap(); + +const originalGetNodeTypesCategories = typeof LiteGraph.getNodeTypesCategories === 'function' ? LiteGraph.getNodeTypesCategories : null; + +// Override the getNodeTypesCategories method if it exists +if (originalGetNodeTypesCategories) { + LiteGraph.getNodeTypesCategories = function(filter) { + if (TOP_CLOWNDOG == false) { + return originalGetNodeTypesCategories.call(this, filter); + } + + try { + // Get the original categories + const categories = originalGetNodeTypesCategories.call(this, filter); + + categories.sort((a, b) => { + const isARes4Lyf = a.startsWith("RES4LYF"); + const isBRes4Lyf = b.startsWith("RES4LYF"); + if (isARes4Lyf && !isBRes4Lyf) return -1; + if (!isARes4Lyf && isBRes4Lyf) return 1; + + // Do the other auto sorting if enabled + if (LiteGraph.auto_sort_node_types) { + return a.localeCompare(b); + } + return 0; + }); + return categories; + } catch (error) { + return originalGetNodeTypesCategories.call(this, filter); + } + }; +} + +function debugLog(...args) { + let force = false; + if (typeof args[args.length - 1] === "boolean") { + force = args.pop(); + } + if (RESDEBUG || force) { + console.log(...args); + + // Attempt to post the log text to the Python backend + const logText = args.join(' '); + fetch('/reslyf/log', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ log: logText }) + }).catch(error => { + console.error('Error posting log to backend:', error); + }); + } +} + +const resDebugLog = debugLog; + +// Adapted from essentials.DisplayAny from ComfyUI_essentials +app.registerExtension({ + name: "Comfy.RES4LYF.DisplayInfo", + async beforeRegisterNodeDef(nodeType, nodeData, app) { + if (!nodeData?.category?.startsWith("RES4LYF")) { + return; + } + + if (nodeData.name === "Latent Display State Info") { + const onExecuted = nodeType.prototype.onExecuted; + + nodeType.prototype.onExecuted = function (message) { + onExecuted?.apply(this, arguments); + + if (this.widgets && this.widgets.length === 0) { + for (let i = 1; i < this.widgets.length; i++) { + this.widgets[i].onRemove?.(); + } + this.widgets.length = 0; + } + + // Check if the "text" widget already exists. + let textWidget = this.widgets && this.widgets.length > 0 && this.widgets.find(w => w.name === "displaytext"); + if (!textWidget) { + textWidget = ComfyWidgets["STRING"](this, "displaytext", ["STRING", { multiline: true }], app).widget; + textWidget.inputEl.readOnly = true; + textWidget.inputEl.style.border = "none"; + textWidget.inputEl.style.backgroundColor = "transparent"; + } + textWidget.value = message["text"].join(""); + }; + } + }, +}); + +app.registerExtension({ + name: "Comfy.RES4LYF.DynamicWidgets", + + async setup(app) { + app.ui.settings.addSetting({ + id: "RES4LYF.topClownDog", + name: "RES4LYF: Top ClownDog", + defaultValue: true, + type: "boolean", + options: [ + { value: true, text: "On" }, + { value: false, text: "Off" }, + ], + onChange: (value) => { + TOP_CLOWNDOG = value; + debugLog(`Top ClownDog ${value ? "enabled" : "disabled"}`); + + // Send to backend + fetch('/reslyf/settings', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + setting: "topClownDog", + value: value + }) + }).catch(error => { + debugLog(`Error updating topClownDog setting: ${error}`); + }); + }, + }); + + app.ui.settings.addSetting({ + id: "RES4LYF.enableDebugLogs", + name: "RES4LYF: Enable debug logging to console", + defaultValue: false, + type: "boolean", + options: [ + { value: true, text: "On" }, + { value: false, text: "Off" }, + ], + onChange: (value) => { + RESDEBUG = value; + debugLog(`Debug logging ${value ? "enabled" : "disabled"}`); + + // Send to backend + fetch('/reslyf/settings', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + setting: "enableDebugLogs", + value: value + }) + }).catch(error => { + debugLog(`Error updating enableDebugLogs setting: ${error}`); + }); + }, + }); + + app.ui.settings.addSetting({ + id: "RES4LYF.displayCategory", + name: "RES4LYF: Display Category in Sampler Names (requires browser refresh)", + defaultValue: true, + type: "boolean", + options: [ + { value: true, text: "On" }, + { value: false, text: "Off" }, + ], + onChange: (value) => { + DISPLAY_CATEGORY = value; + resDebugLog(`Display Category ${value ? "enabled" : "disabled"}`); + + // Send to backend + fetch('/reslyf/settings', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + setting: "displayCategory", + value: value + }) + }).catch(error => { + resDebugLog(`Error updating displayCategory setting: ${error}`); + }); + }, + }); + + }, + + + nodeCreated(node) { + if (NODES_WITH_EXPANDABLE_OPTIONS.includes(node.comfyClass)) { + //debugLog(`Setting up expandable options for ${node.comfyClass}`, true); + setupExpandableOptions(node); + } + } +}); + +const NODES_WITH_EXPANDABLE_OPTIONS = [ + "ClownsharKSampler_Beta", + "ClownsharkChainsampler_Beta", + "SharkChainsampler_Beta", + + + "ClownSampler_Beta", + "ClownSamplerAdvanced_Beta", + + "SharkSampler", + "SharkSampler_Beta", + "SharkSamplerAdvanced_Beta", + + "ClownOptions_Combine", +] + +function setupExpandableOptions(node) { + if (!processedNodeMap.has(node)) { + processedNodeMap.set(node, ++nodeCounter); + //debugLog(`Assigned ID ${nodeCounter} to node ${node.comfyClass}`); + } else { + //debugLog(`Node ${node.comfyClass} already processed with ID ${processedNodeMap.get(node)} - skipping`); + return; + } + + const originalOnConnectionsChange = node.onConnectionsChange; + + const hasOptionsInput = node.inputs.some(input => input.name === "options"); + if (!hasOptionsInput) { + //debugLog(`Node ${node.comfyClass} doesn't have an options input - skipping`); + return; + } + + node.onConnectionsChange = function(type, index, connected, link_info) { + if (originalOnConnectionsChange) { + originalOnConnectionsChange.call(this, type, index, connected, link_info); + } + + if (type === LiteGraph.INPUT && !connected) { + const input = this.inputs[index]; + if (!input || !input.name.startsWith("options")) { + return; + } + + //debugLog(`Options input disconnected: ${input.name}`); + + // setTimeout to let the graph update first + setTimeout(() => { + cleanupOptionsInputs(this); + }, 100); + return; + } + + if (type === LiteGraph.INPUT && connected && link_info) { + const input = this.inputs[index]; + if (!input || !input.name.startsWith("options")) { + return; + } + + let hasEmptyOptions = false; + for (let i = 0; i < this.inputs.length; i++) { + const input = this.inputs[i]; + if (input.name.startsWith("options") && input.link === null) { + hasEmptyOptions = true; + break; + } + } + + if (!hasEmptyOptions) { + //debugLog(`All options inputs are connected, adding a new one`); + + // Find the highest index number in existing options inputs + let maxIndex = 0; + for (let i = 0; i < this.inputs.length; i++) { + const input = this.inputs[i]; + if (input.name === "options") { + continue; // Skip the base "options" input + } else if (input.name.startsWith("options ")) { + const match = input.name.match(/options (\d+)/); + if (match) { + const index = parseInt(match[1]) - 1; + maxIndex = Math.max(maxIndex, index); + } + } + } + + const newName = maxIndex === 0 ? "options 2" : `options ${maxIndex + 2}`; + this.addInput(newName, "OPTIONS"); + //debugLog(`Created new options input: ${newName}`); + + this.setDirtyCanvas(true, true); + } + } + }; + + const optionsInputs = node.inputs.filter(input => + input.name.startsWith("options") + ); + + const baseOptionsInput = optionsInputs.find(input => input.name === "options"); + const hasOptionsWithIndex = optionsInputs.some(input => input.name !== "options"); + + // if (baseOptionsInput && !hasOptionsWithIndex) { + // debugLog(`Adding initial options 1 input to ${node.comfyClass}`); + // node.addInput("options 1", "OPTIONS"); + // node.setDirtyCanvas(true, true); + // } + + const originalOnConfigure = node.onConfigure; + node.onConfigure = function(info) { + if (originalOnConfigure) { + originalOnConfigure.call(this, info); + } + + let hasEmptyOptions = false; + for (let i = 0; i < this.inputs.length; i++) { + const input = this.inputs[i]; + if (input.name.startsWith("options") && input.link === null) { + hasEmptyOptions = true; + break; + } + } + + if (!hasEmptyOptions && this.inputs.some(i => i.name.startsWith("options"))) { + let maxIndex = 0; + for (let i = 0; i < this.inputs.length; i++) { + const input = this.inputs[i]; + if (input.name === "options") { + continue; + } else if (input.name.startsWith("options ")) { + const match = input.name.match(/options (\d+)/); + if (match) { + const index = parseInt(match[1]) - 1; + maxIndex = Math.max(maxIndex, index); + } + } + } + + const newName = maxIndex === 0 ? "options 2" : `options ${maxIndex + 2}`; + this.addInput(newName, "OPTIONS"); + } + }; + + function cleanupOptionsInputs(node) { + const optionsInputs = []; + for (let i = 0; i < node.inputs.length; i++) { + const input = node.inputs[i]; + if (input.name.startsWith("options")) { + optionsInputs.push({ + index: i, + name: input.name, + connected: input.link !== null, + isBase: input.name === "options" + }); + } + } + + const baseInput = optionsInputs.find(info => info.isBase); + const nonBaseInputs = optionsInputs.filter(info => !info.isBase); + + let needsRenumbering = false; + + if (baseInput && !baseInput.connected && nonBaseInputs.every(info => !info.connected)) { + nonBaseInputs.sort((a, b) => b.index - a.index); + + for (const inputInfo of nonBaseInputs) { + //debugLog(`Removing unnecessary options input: ${inputInfo.name} (index ${inputInfo.index})`); + node.removeInput(inputInfo.index); + needsRenumbering = true; + } + + node.setDirtyCanvas(true, true); + return; + } + + const disconnectedInputs = nonBaseInputs.filter(info => !info.connected); + + if (disconnectedInputs.length > 1) { + disconnectedInputs.sort((a, b) => b.index - a.index); + + for (let i = 1; i < disconnectedInputs.length; i++) { + //debugLog(`Removing unnecessary options input: ${disconnectedInputs[i].name} (index ${disconnectedInputs[i].index})`); + node.removeInput(disconnectedInputs[i].index); + needsRenumbering = true; + } + } + + const hasConnectedOptions = optionsInputs.some(info => info.connected); + const hasEmptyOptions = optionsInputs.some(info => !info.connected && !info.isBase); + + if (hasConnectedOptions && !hasEmptyOptions) { + node.addInput("options temp", "OPTIONS"); + //debugLog(`Added new empty options input`); + needsRenumbering = true; + } + + if (needsRenumbering) { + renumberOptionsInputs(node); + node.setDirtyCanvas(true, true); + } + } + + function renumberOptionsInputs(node) { + const optionsInfo = []; + for (let i = 0; i < node.inputs.length; i++) { + const input = node.inputs[i]; + if (input.name.startsWith("options")) { + if (input.name === "options") { + continue; + } + + optionsInfo.push({ + index: i, + connected: input.link !== null, + name: input.name + }); + } + } + + optionsInfo.sort((a, b) => { + if (a.connected !== b.connected) { + return b.connected ? 1 : -1; // Connected inputs first + } + return a.index - b.index; + }); + + for (let i = 0; i < optionsInfo.length; i++) { + const inputInfo = optionsInfo[i]; + const newName = `options ${i + 2}`; + + if (inputInfo.name !== newName) { + //debugLog(`Renaming ${inputInfo.name} to ${newName}`); + node.inputs[inputInfo.index].name = newName; + } + } + } +} diff --git a/ComfyUI/custom_nodes/RES4LYF/web/js/conditioningToBase64.js b/ComfyUI/custom_nodes/RES4LYF/web/js/conditioningToBase64.js new file mode 100644 index 0000000000000000000000000000000000000000..fadfaa1ec3563871607c6ff862769e0af9b09b82 --- /dev/null +++ b/ComfyUI/custom_nodes/RES4LYF/web/js/conditioningToBase64.js @@ -0,0 +1,57 @@ +import { app } from "../../../scripts/app.js"; +import { ComfyWidgets } from "../../../scripts/widgets.js"; + +// Displays input text on a node +app.registerExtension({ + name: "res4lyf.ConditioningToBase64", + async beforeRegisterNodeDef(nodeType, nodeData, app) { + if (nodeData.name === "ConditioningToBase64") { + function populate(text) { + if (this.widgets) { + for (let i = 1; i < this.widgets.length; i++) { + this.widgets[i].onRemove?.(); + } + this.widgets.length = 1; + } + + const v = [...text]; + if (!v[0]) { + v.shift(); + } + for (const list of v) { + const w = ComfyWidgets["STRING"](this, "text2", ["STRING", { multiline: true }], app).widget; + w.inputEl.readOnly = true; + w.inputEl.style.opacity = 0.6; + w.value = list; + } + + requestAnimationFrame(() => { + const sz = this.computeSize(); + if (sz[0] < this.size[0]) { + sz[0] = this.size[0]; + } + if (sz[1] < this.size[1]) { + sz[1] = this.size[1]; + } + this.onResize?.(sz); + app.graph.setDirtyCanvas(true, false); + }); + } + + // When the node is executed we will be sent the input text, display this in the widget + const onExecuted = nodeType.prototype.onExecuted; + nodeType.prototype.onExecuted = function (message) { + onExecuted?.apply(this, arguments); + populate.call(this, message.text); + }; + + const onConfigure = nodeType.prototype.onConfigure; + nodeType.prototype.onConfigure = function () { + onConfigure?.apply(this, arguments); + if (this.widgets_values?.length) { + populate.call(this, this.widgets_values.slice(+this.widgets_values.length > 1)); + } + }; + } + }, +}); diff --git a/ComfyUI/custom_nodes/RES4LYF/web/js/res4lyf.default.json b/ComfyUI/custom_nodes/RES4LYF/web/js/res4lyf.default.json new file mode 100644 index 0000000000000000000000000000000000000000..cefd64744abecd7ba71bfd778c482da65ee64867 --- /dev/null +++ b/ComfyUI/custom_nodes/RES4LYF/web/js/res4lyf.default.json @@ -0,0 +1,6 @@ +{ + "name": "RES4LYF", + "topClownDog": true, + "enableDebugLogs": false, + "displayCategory": true +} \ No newline at end of file diff --git a/ComfyUI/custom_nodes/comfyui-kjnodes/.github/FUNDING.yml b/ComfyUI/custom_nodes/comfyui-kjnodes/.github/FUNDING.yml new file mode 100644 index 0000000000000000000000000000000000000000..f475f425424d9172d049b4c016ebe8817987149e --- /dev/null +++ b/ComfyUI/custom_nodes/comfyui-kjnodes/.github/FUNDING.yml @@ -0,0 +1,2 @@ +github: [kijai] +custom: ["https://www.paypal.me/kijaidesign"] diff --git a/ComfyUI/custom_nodes/comfyui-kjnodes/.github/workflows/publish.yml b/ComfyUI/custom_nodes/comfyui-kjnodes/.github/workflows/publish.yml new file mode 100644 index 0000000000000000000000000000000000000000..e155f5f40e46fa83942dee5a9460e8093f3b4208 --- /dev/null +++ b/ComfyUI/custom_nodes/comfyui-kjnodes/.github/workflows/publish.yml @@ -0,0 +1,25 @@ +name: Publish to Comfy registry +on: + workflow_dispatch: + push: + branches: + - main + paths: + - "pyproject.toml" + +permissions: + issues: write + +jobs: + publish-node: + name: Publish Custom Node to registry + runs-on: ubuntu-latest + if: ${{ github.repository_owner == 'kijai' }} + steps: + - name: Check out code + uses: actions/checkout@v4 + - name: Publish Custom Node + uses: Comfy-Org/publish-node-action@v1 + with: + ## Add your own personal access token to your Github Repository secrets and reference it here. + personal_access_token: ${{ secrets.REGISTRY_ACCESS_TOKEN }} diff --git a/ComfyUI/custom_nodes/comfyui-kjnodes/docs/images/319121566-05f66385-7568-4b1f-8bbc-11053660b02f.png b/ComfyUI/custom_nodes/comfyui-kjnodes/docs/images/319121566-05f66385-7568-4b1f-8bbc-11053660b02f.png new file mode 100644 index 0000000000000000000000000000000000000000..e749239c1c4ffd5ab29b51695dd8d8b51ed3597f Binary files /dev/null and b/ComfyUI/custom_nodes/comfyui-kjnodes/docs/images/319121566-05f66385-7568-4b1f-8bbc-11053660b02f.png differ diff --git a/ComfyUI/custom_nodes/comfyui-kjnodes/docs/images/319121636-706b5081-9120-4a29-bd76-901691ada688.png b/ComfyUI/custom_nodes/comfyui-kjnodes/docs/images/319121636-706b5081-9120-4a29-bd76-901691ada688.png new file mode 100644 index 0000000000000000000000000000000000000000..b53ad666ff060d87971f3962e74101f0cb2a5c3f Binary files /dev/null and b/ComfyUI/custom_nodes/comfyui-kjnodes/docs/images/319121636-706b5081-9120-4a29-bd76-901691ada688.png differ diff --git a/ComfyUI/custom_nodes/comfyui-kjnodes/example_workflows/leapfusion_hunyuuanvideo_i2v_native_testing.json b/ComfyUI/custom_nodes/comfyui-kjnodes/example_workflows/leapfusion_hunyuuanvideo_i2v_native_testing.json new file mode 100644 index 0000000000000000000000000000000000000000..134a83788815d04b5574a807db67eb6e45bf9263 --- /dev/null +++ b/ComfyUI/custom_nodes/comfyui-kjnodes/example_workflows/leapfusion_hunyuuanvideo_i2v_native_testing.json @@ -0,0 +1,1188 @@ +{ + "last_node_id": 86, + "last_link_id": 144, + "nodes": [ + { + "id": 62, + "type": "FluxGuidance", + "pos": [ + -630, + -170 + ], + "size": [ + 317.4000244140625, + 58 + ], + "flags": {}, + "order": 13, + "mode": 0, + "inputs": [ + { + "name": "conditioning", + "type": "CONDITIONING", + "link": 82 + } + ], + "outputs": [ + { + "name": "CONDITIONING", + "type": "CONDITIONING", + "links": [ + 83 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "FluxGuidance" + }, + "widgets_values": [ + 6 + ] + }, + { + "id": 51, + "type": "KSamplerSelect", + "pos": [ + -610, + -480 + ], + "size": [ + 315, + 58 + ], + "flags": {}, + "order": 0, + "mode": 0, + "inputs": [], + "outputs": [ + { + "name": "SAMPLER", + "type": "SAMPLER", + "links": [ + 61 + ] + } + ], + "properties": { + "Node name for S&R": "KSamplerSelect" + }, + "widgets_values": [ + "euler" + ] + }, + { + "id": 57, + "type": "VAEDecodeTiled", + "pos": [ + -200, + 90 + ], + "size": [ + 315, + 150 + ], + "flags": {}, + "order": 20, + "mode": 0, + "inputs": [ + { + "name": "samples", + "type": "LATENT", + "link": 142 + }, + { + "name": "vae", + "type": "VAE", + "link": 74 + } + ], + "outputs": [ + { + "name": "IMAGE", + "type": "IMAGE", + "links": [ + 105 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "VAEDecodeTiled" + }, + "widgets_values": [ + 128, + 64, + 64, + 8 + ] + }, + { + "id": 65, + "type": "LoadImage", + "pos": [ + -2212.498779296875, + -632.4085083007812 + ], + "size": [ + 315, + 314 + ], + "flags": {}, + "order": 1, + "mode": 0, + "inputs": [], + "outputs": [ + { + "name": "IMAGE", + "type": "IMAGE", + "links": [ + 86 + ], + "slot_index": 0 + }, + { + "name": "MASK", + "type": "MASK", + "links": null + } + ], + "properties": { + "Node name for S&R": "LoadImage" + }, + "widgets_values": [ + "Mona-Lisa-oil-wood-panel-Leonardo-da.webp", + "image" + ] + }, + { + "id": 64, + "type": "VAEEncode", + "pos": [ + -1336.7884521484375, + -492.5806884765625 + ], + "size": [ + 210, + 46 + ], + "flags": {}, + "order": 14, + "mode": 0, + "inputs": [ + { + "name": "pixels", + "type": "IMAGE", + "link": 144 + }, + { + "name": "vae", + "type": "VAE", + "link": 88 + } + ], + "outputs": [ + { + "name": "LATENT", + "type": "LATENT", + "links": [ + 137 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "VAEEncode" + }, + "widgets_values": [] + }, + { + "id": 44, + "type": "UNETLoader", + "pos": [ + -2373.55029296875, + -193.91510009765625 + ], + "size": [ + 459.56060791015625, + 82 + ], + "flags": {}, + "order": 2, + "mode": 0, + "inputs": [], + "outputs": [ + { + "name": "MODEL", + "type": "MODEL", + "links": [ + 135 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "UNETLoader" + }, + "widgets_values": [ + "hyvideo\\hunyuan_video_720_fp8_e4m3fn.safetensors", + "fp8_e4m3fn_fast" + ] + }, + { + "id": 49, + "type": "VAELoader", + "pos": [ + -1876.39306640625, + -35.19633865356445 + ], + "size": [ + 433.7603454589844, + 58.71116256713867 + ], + "flags": {}, + "order": 3, + "mode": 0, + "inputs": [], + "outputs": [ + { + "name": "VAE", + "type": "VAE", + "links": [ + 74, + 88 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "VAELoader" + }, + "widgets_values": [ + "hyvid\\hunyuan_video_vae_bf16.safetensors" + ] + }, + { + "id": 47, + "type": "DualCLIPLoader", + "pos": [ + -2284.893798828125, + 150.4042205810547 + ], + "size": [ + 343.3958435058594, + 106.86042785644531 + ], + "flags": {}, + "order": 4, + "mode": 0, + "inputs": [], + "outputs": [ + { + "name": "CLIP", + "type": "CLIP", + "links": [ + 56 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "DualCLIPLoader" + }, + "widgets_values": [ + "clip_l.safetensors", + "llava_llama3_fp16.safetensors", + "hunyuan_video", + "default" + ] + }, + { + "id": 45, + "type": "CLIPTextEncode", + "pos": [ + -1839.1649169921875, + 143.5203094482422 + ], + "size": [ + 400, + 200 + ], + "flags": {}, + "order": 8, + "mode": 0, + "inputs": [ + { + "name": "clip", + "type": "CLIP", + "link": 56 + } + ], + "outputs": [ + { + "name": "CONDITIONING", + "type": "CONDITIONING", + "links": [ + 69, + 82 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "CLIPTextEncode" + }, + "widgets_values": [ + "woman puts on sunglasses" + ] + }, + { + "id": 53, + "type": "EmptyHunyuanLatentVideo", + "pos": [ + -1120, + 90 + ], + "size": [ + 315, + 130 + ], + "flags": {}, + "order": 10, + "mode": 0, + "inputs": [ + { + "name": "width", + "type": "INT", + "link": 89, + "widget": { + "name": "width" + } + }, + { + "name": "height", + "type": "INT", + "link": 90, + "widget": { + "name": "height" + } + } + ], + "outputs": [ + { + "name": "LATENT", + "type": "LATENT", + "links": [ + 119 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "EmptyHunyuanLatentVideo" + }, + "widgets_values": [ + 960, + 544, + 65, + 1 + ] + }, + { + "id": 55, + "type": "ConditioningZeroOut", + "pos": [ + -910, + 300 + ], + "size": [ + 251.14309692382812, + 26 + ], + "flags": { + "collapsed": true + }, + "order": 12, + "mode": 0, + "inputs": [ + { + "name": "conditioning", + "type": "CONDITIONING", + "link": 69 + } + ], + "outputs": [ + { + "name": "CONDITIONING", + "type": "CONDITIONING", + "links": [ + 70 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "ConditioningZeroOut" + }, + "widgets_values": [] + }, + { + "id": 52, + "type": "BasicScheduler", + "pos": [ + -600, + -350 + ], + "size": [ + 315, + 106 + ], + "flags": {}, + "order": 17, + "mode": 0, + "inputs": [ + { + "name": "model", + "type": "MODEL", + "link": 78 + } + ], + "outputs": [ + { + "name": "SIGMAS", + "type": "SIGMAS", + "links": [ + 62 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "BasicScheduler" + }, + "widgets_values": [ + "simple", + 20, + 1 + ] + }, + { + "id": 42, + "type": "SamplerCustom", + "pos": [ + -640, + 10 + ], + "size": [ + 355.20001220703125, + 467.4666748046875 + ], + "flags": {}, + "order": 18, + "mode": 0, + "inputs": [ + { + "name": "model", + "type": "MODEL", + "link": 77 + }, + { + "name": "positive", + "type": "CONDITIONING", + "link": 83 + }, + { + "name": "negative", + "type": "CONDITIONING", + "link": 70 + }, + { + "name": "sampler", + "type": "SAMPLER", + "link": 61 + }, + { + "name": "sigmas", + "type": "SIGMAS", + "link": 62 + }, + { + "name": "latent_image", + "type": "LATENT", + "link": 119 + } + ], + "outputs": [ + { + "name": "output", + "type": "LATENT", + "links": null + }, + { + "name": "denoised_output", + "type": "LATENT", + "links": [ + 141 + ], + "slot_index": 1 + } + ], + "properties": { + "Node name for S&R": "SamplerCustom" + }, + "widgets_values": [ + true, + 6, + "fixed", + 1, + null + ] + }, + { + "id": 84, + "type": "GetLatentRangeFromBatch", + "pos": [ + -240, + -100 + ], + "size": [ + 340.20001220703125, + 82 + ], + "flags": {}, + "order": 19, + "mode": 0, + "inputs": [ + { + "name": "latents", + "type": "LATENT", + "link": 141 + } + ], + "outputs": [ + { + "name": "LATENT", + "type": "LATENT", + "links": [ + 142 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "GetLatentRangeFromBatch" + }, + "widgets_values": [ + 1, + -1 + ] + }, + { + "id": 50, + "type": "VHS_VideoCombine", + "pos": [ + 165.77645874023438, + -619.0606079101562 + ], + "size": [ + 1112.6898193359375, + 1076.4598388671875 + ], + "flags": {}, + "order": 21, + "mode": 0, + "inputs": [ + { + "name": "images", + "type": "IMAGE", + "link": 105 + }, + { + "name": "audio", + "type": "AUDIO", + "link": null, + "shape": 7 + }, + { + "name": "meta_batch", + "type": "VHS_BatchManager", + "link": null, + "shape": 7 + }, + { + "name": "vae", + "type": "VAE", + "link": null, + "shape": 7 + } + ], + "outputs": [ + { + "name": "Filenames", + "type": "VHS_FILENAMES", + "links": null + } + ], + "properties": { + "Node name for S&R": "VHS_VideoCombine" + }, + "widgets_values": { + "frame_rate": 24, + "loop_count": 0, + "filename_prefix": "hyvidcomfy", + "format": "video/h264-mp4", + "pix_fmt": "yuv420p", + "crf": 19, + "save_metadata": true, + "trim_to_audio": false, + "pingpong": false, + "save_output": false, + "videopreview": { + "hidden": false, + "paused": false, + "params": { + "filename": "hyvidcomfy_00001.mp4", + "subfolder": "", + "type": "temp", + "format": "video/h264-mp4", + "frame_rate": 24, + "workflow": "hyvidcomfy_00001.png", + "fullpath": "N:\\AI\\ComfyUI\\temp\\hyvidcomfy_00001.mp4" + }, + "muted": false + } + } + }, + { + "id": 54, + "type": "ModelSamplingSD3", + "pos": [ + -1079.9112548828125, + -146.69448852539062 + ], + "size": [ + 315, + 58 + ], + "flags": {}, + "order": 16, + "mode": 0, + "inputs": [ + { + "name": "model", + "type": "MODEL", + "link": 117 + } + ], + "outputs": [ + { + "name": "MODEL", + "type": "MODEL", + "links": [ + 77, + 78 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "ModelSamplingSD3" + }, + "widgets_values": [ + 9 + ] + }, + { + "id": 80, + "type": "PathchSageAttentionKJ", + "pos": [ + -2273.926513671875, + -36.720542907714844 + ], + "size": [ + 315, + 58 + ], + "flags": {}, + "order": 7, + "mode": 4, + "inputs": [ + { + "name": "model", + "type": "MODEL", + "link": 135 + } + ], + "outputs": [ + { + "name": "MODEL", + "type": "MODEL", + "links": [ + 136 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "PathchSageAttentionKJ" + }, + "widgets_values": [ + "auto" + ] + }, + { + "id": 85, + "type": "Note", + "pos": [ + -1838.572265625, + -302.1575927734375 + ], + "size": [ + 408.4594421386719, + 58 + ], + "flags": {}, + "order": 5, + "mode": 0, + "inputs": [], + "outputs": [], + "properties": {}, + "widgets_values": [ + "https://huggingface.co/Kijai/Leapfusion-image2vid-comfy/blob/main/leapfusion_img2vid544p_comfy.safetensors" + ], + "color": "#432", + "bgcolor": "#653" + }, + { + "id": 74, + "type": "LeapfusionHunyuanI2VPatcher", + "pos": [ + -1059.552978515625, + -459.34674072265625 + ], + "size": [ + 277.3238525390625, + 150 + ], + "flags": {}, + "order": 15, + "mode": 0, + "inputs": [ + { + "name": "model", + "type": "MODEL", + "link": 123 + }, + { + "name": "latent", + "type": "LATENT", + "link": 137 + } + ], + "outputs": [ + { + "name": "MODEL", + "type": "MODEL", + "links": [ + 117 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "LeapfusionHunyuanI2VPatcher" + }, + "widgets_values": [ + 0, + 0, + 1, + 0.8 + ] + }, + { + "id": 59, + "type": "LoraLoaderModelOnly", + "pos": [ + -1870.3748779296875, + -194.6091766357422 + ], + "size": [ + 442.8438720703125, + 82 + ], + "flags": {}, + "order": 11, + "mode": 0, + "inputs": [ + { + "name": "model", + "type": "MODEL", + "link": 136 + } + ], + "outputs": [ + { + "name": "MODEL", + "type": "MODEL", + "links": [ + 123 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "LoraLoaderModelOnly" + }, + "widgets_values": [ + "hyvid\\musubi-tuner\\img2vid544p.safetensors", + 1 + ] + }, + { + "id": 66, + "type": "ImageResizeKJ", + "pos": [ + -1821.1531982421875, + -632.925048828125 + ], + "size": [ + 315, + 266 + ], + "flags": {}, + "order": 6, + "mode": 0, + "inputs": [ + { + "name": "image", + "type": "IMAGE", + "link": 86 + }, + { + "name": "get_image_size", + "type": "IMAGE", + "link": null, + "shape": 7 + }, + { + "name": "width_input", + "type": "INT", + "link": null, + "widget": { + "name": "width_input" + }, + "shape": 7 + }, + { + "name": "height_input", + "type": "INT", + "link": null, + "widget": { + "name": "height_input" + }, + "shape": 7 + } + ], + "outputs": [ + { + "name": "IMAGE", + "type": "IMAGE", + "links": [ + 143 + ], + "slot_index": 0 + }, + { + "name": "width", + "type": "INT", + "links": [ + 89 + ], + "slot_index": 1 + }, + { + "name": "height", + "type": "INT", + "links": [ + 90 + ], + "slot_index": 2 + } + ], + "properties": { + "Node name for S&R": "ImageResizeKJ" + }, + "widgets_values": [ + 960, + 640, + "lanczos", + false, + 2, + 0, + 0, + "center" + ] + }, + { + "id": 86, + "type": "ImageNoiseAugmentation", + "pos": [ + -1361.111572265625, + -667.0104370117188 + ], + "size": [ + 315, + 106 + ], + "flags": {}, + "order": 9, + "mode": 0, + "inputs": [ + { + "name": "image", + "type": "IMAGE", + "link": 143 + } + ], + "outputs": [ + { + "name": "IMAGE", + "type": "IMAGE", + "links": [ + 144 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "ImageNoiseAugmentation" + }, + "widgets_values": [ + 0.05, + 123, + "fixed" + ] + } + ], + "links": [ + [ + 56, + 47, + 0, + 45, + 0, + "CLIP" + ], + [ + 61, + 51, + 0, + 42, + 3, + "SAMPLER" + ], + [ + 62, + 52, + 0, + 42, + 4, + "SIGMAS" + ], + [ + 69, + 45, + 0, + 55, + 0, + "CONDITIONING" + ], + [ + 70, + 55, + 0, + 42, + 2, + "CONDITIONING" + ], + [ + 74, + 49, + 0, + 57, + 1, + "VAE" + ], + [ + 77, + 54, + 0, + 42, + 0, + "MODEL" + ], + [ + 78, + 54, + 0, + 52, + 0, + "MODEL" + ], + [ + 82, + 45, + 0, + 62, + 0, + "CONDITIONING" + ], + [ + 83, + 62, + 0, + 42, + 1, + "CONDITIONING" + ], + [ + 86, + 65, + 0, + 66, + 0, + "IMAGE" + ], + [ + 88, + 49, + 0, + 64, + 1, + "VAE" + ], + [ + 89, + 66, + 1, + 53, + 0, + "INT" + ], + [ + 90, + 66, + 2, + 53, + 1, + "INT" + ], + [ + 105, + 57, + 0, + 50, + 0, + "IMAGE" + ], + [ + 117, + 74, + 0, + 54, + 0, + "MODEL" + ], + [ + 119, + 53, + 0, + 42, + 5, + "LATENT" + ], + [ + 123, + 59, + 0, + 74, + 0, + "MODEL" + ], + [ + 135, + 44, + 0, + 80, + 0, + "MODEL" + ], + [ + 136, + 80, + 0, + 59, + 0, + "MODEL" + ], + [ + 137, + 64, + 0, + 74, + 1, + "LATENT" + ], + [ + 141, + 42, + 1, + 84, + 0, + "LATENT" + ], + [ + 142, + 84, + 0, + 57, + 0, + "LATENT" + ], + [ + 143, + 66, + 0, + 86, + 0, + "IMAGE" + ], + [ + 144, + 86, + 0, + 64, + 0, + "IMAGE" + ] + ], + "groups": [], + "config": {}, + "extra": { + "ds": { + "scale": 0.740024994425854, + "offset": [ + 2525.036093151529, + 802.59123935694 + ] + }, + "node_versions": { + "comfy-core": "0.3.13", + "ComfyUI-KJNodes": "a8aeef670b3f288303f956bf94385cb87978ea93", + "ComfyUI-VideoHelperSuite": "c47b10ca1798b4925ff5a5f07d80c51ca80a837d" + }, + "VHS_latentpreview": true, + "VHS_latentpreviewrate": 0 + }, + "version": 0.4 +} \ No newline at end of file diff --git a/ComfyUI/custom_nodes/comfyui-kjnodes/intrinsic_loras/intrinsic_loras.txt b/ComfyUI/custom_nodes/comfyui-kjnodes/intrinsic_loras/intrinsic_loras.txt new file mode 100644 index 0000000000000000000000000000000000000000..62ee933763a8aa9e1b232d228717ac754ab22751 --- /dev/null +++ b/ComfyUI/custom_nodes/comfyui-kjnodes/intrinsic_loras/intrinsic_loras.txt @@ -0,0 +1,4 @@ +source for the loras: +https://github.com/duxiaodan/intrinsic-lora + +Renamed and conveted to .safetensors \ No newline at end of file diff --git a/ComfyUI/custom_nodes/comfyui-kjnodes/kjweb_async/marked.min.js b/ComfyUI/custom_nodes/comfyui-kjnodes/kjweb_async/marked.min.js new file mode 100644 index 0000000000000000000000000000000000000000..2e66c369c388c135cc68d399861a737f4c5e68cd --- /dev/null +++ b/ComfyUI/custom_nodes/comfyui-kjnodes/kjweb_async/marked.min.js @@ -0,0 +1,6 @@ +/** + * marked v12.0.1 - a markdown parser + * Copyright (c) 2011-2024, Christopher Jeffrey. (MIT Licensed) + * https://github.com/markedjs/marked + */ +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).marked={})}(this,(function(e){"use strict";function t(){return{async:!1,breaks:!1,extensions:null,gfm:!0,hooks:null,pedantic:!1,renderer:null,silent:!1,tokenizer:null,walkTokens:null}}function n(t){e.defaults=t}e.defaults={async:!1,breaks:!1,extensions:null,gfm:!0,hooks:null,pedantic:!1,renderer:null,silent:!1,tokenizer:null,walkTokens:null};const s=/[&<>"']/,r=new RegExp(s.source,"g"),i=/[<>"']|&(?!(#\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\w+);)/,l=new RegExp(i.source,"g"),o={"&":"&","<":"<",">":">",'"':""","'":"'"},a=e=>o[e];function c(e,t){if(t){if(s.test(e))return e.replace(r,a)}else if(i.test(e))return e.replace(l,a);return e}const h=/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/gi;function p(e){return e.replace(h,((e,t)=>"colon"===(t=t.toLowerCase())?":":"#"===t.charAt(0)?"x"===t.charAt(1)?String.fromCharCode(parseInt(t.substring(2),16)):String.fromCharCode(+t.substring(1)):""))}const u=/(^|[^\[])\^/g;function k(e,t){let n="string"==typeof e?e:e.source;t=t||"";const s={replace:(e,t)=>{let r="string"==typeof t?t:t.source;return r=r.replace(u,"$1"),n=n.replace(e,r),s},getRegex:()=>new RegExp(n,t)};return s}function g(e){try{e=encodeURI(e).replace(/%25/g,"%")}catch(e){return null}return e}const f={exec:()=>null};function d(e,t){const n=e.replace(/\|/g,((e,t,n)=>{let s=!1,r=t;for(;--r>=0&&"\\"===n[r];)s=!s;return s?"|":" |"})).split(/ \|/);let s=0;if(n[0].trim()||n.shift(),n.length>0&&!n[n.length-1].trim()&&n.pop(),t)if(n.length>t)n.splice(t);else for(;n.length0)return{type:"space",raw:t[0]}}code(e){const t=this.rules.block.code.exec(e);if(t){const e=t[0].replace(/^ {1,4}/gm,"");return{type:"code",raw:t[0],codeBlockStyle:"indented",text:this.options.pedantic?e:x(e,"\n")}}}fences(e){const t=this.rules.block.fences.exec(e);if(t){const e=t[0],n=function(e,t){const n=e.match(/^(\s+)(?:```)/);if(null===n)return t;const s=n[1];return t.split("\n").map((e=>{const t=e.match(/^\s+/);if(null===t)return e;const[n]=t;return n.length>=s.length?e.slice(s.length):e})).join("\n")}(e,t[3]||"");return{type:"code",raw:e,lang:t[2]?t[2].trim().replace(this.rules.inline.anyPunctuation,"$1"):t[2],text:n}}}heading(e){const t=this.rules.block.heading.exec(e);if(t){let e=t[2].trim();if(/#$/.test(e)){const t=x(e,"#");this.options.pedantic?e=t.trim():t&&!/ $/.test(t)||(e=t.trim())}return{type:"heading",raw:t[0],depth:t[1].length,text:e,tokens:this.lexer.inline(e)}}}hr(e){const t=this.rules.block.hr.exec(e);if(t)return{type:"hr",raw:t[0]}}blockquote(e){const t=this.rules.block.blockquote.exec(e);if(t){const e=x(t[0].replace(/^ *>[ \t]?/gm,""),"\n"),n=this.lexer.state.top;this.lexer.state.top=!0;const s=this.lexer.blockTokens(e);return this.lexer.state.top=n,{type:"blockquote",raw:t[0],tokens:s,text:e}}}list(e){let t=this.rules.block.list.exec(e);if(t){let n=t[1].trim();const s=n.length>1,r={type:"list",raw:"",ordered:s,start:s?+n.slice(0,-1):"",loose:!1,items:[]};n=s?`\\d{1,9}\\${n.slice(-1)}`:`\\${n}`,this.options.pedantic&&(n=s?n:"[*+-]");const i=new RegExp(`^( {0,3}${n})((?:[\t ][^\\n]*)?(?:\\n|$))`);let l="",o="",a=!1;for(;e;){let n=!1;if(!(t=i.exec(e)))break;if(this.rules.block.hr.test(e))break;l=t[0],e=e.substring(l.length);let s=t[2].split("\n",1)[0].replace(/^\t+/,(e=>" ".repeat(3*e.length))),c=e.split("\n",1)[0],h=0;this.options.pedantic?(h=2,o=s.trimStart()):(h=t[2].search(/[^ ]/),h=h>4?1:h,o=s.slice(h),h+=t[1].length);let p=!1;if(!s&&/^ *$/.test(c)&&(l+=c+"\n",e=e.substring(c.length+1),n=!0),!n){const t=new RegExp(`^ {0,${Math.min(3,h-1)}}(?:[*+-]|\\d{1,9}[.)])((?:[ \t][^\\n]*)?(?:\\n|$))`),n=new RegExp(`^ {0,${Math.min(3,h-1)}}((?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$)`),r=new RegExp(`^ {0,${Math.min(3,h-1)}}(?:\`\`\`|~~~)`),i=new RegExp(`^ {0,${Math.min(3,h-1)}}#`);for(;e;){const a=e.split("\n",1)[0];if(c=a,this.options.pedantic&&(c=c.replace(/^ {1,4}(?=( {4})*[^ ])/g," ")),r.test(c))break;if(i.test(c))break;if(t.test(c))break;if(n.test(e))break;if(c.search(/[^ ]/)>=h||!c.trim())o+="\n"+c.slice(h);else{if(p)break;if(s.search(/[^ ]/)>=4)break;if(r.test(s))break;if(i.test(s))break;if(n.test(s))break;o+="\n"+c}p||c.trim()||(p=!0),l+=a+"\n",e=e.substring(a.length+1),s=c.slice(h)}}r.loose||(a?r.loose=!0:/\n *\n *$/.test(l)&&(a=!0));let u,k=null;this.options.gfm&&(k=/^\[[ xX]\] /.exec(o),k&&(u="[ ] "!==k[0],o=o.replace(/^\[[ xX]\] +/,""))),r.items.push({type:"list_item",raw:l,task:!!k,checked:u,loose:!1,text:o,tokens:[]}),r.raw+=l}r.items[r.items.length-1].raw=l.trimEnd(),r.items[r.items.length-1].text=o.trimEnd(),r.raw=r.raw.trimEnd();for(let e=0;e"space"===e.type)),n=t.length>0&&t.some((e=>/\n.*\n/.test(e.raw)));r.loose=n}if(r.loose)for(let e=0;e$/,"$1").replace(this.rules.inline.anyPunctuation,"$1"):"",s=t[3]?t[3].substring(1,t[3].length-1).replace(this.rules.inline.anyPunctuation,"$1"):t[3];return{type:"def",tag:e,raw:t[0],href:n,title:s}}}table(e){const t=this.rules.block.table.exec(e);if(!t)return;if(!/[:|]/.test(t[2]))return;const n=d(t[1]),s=t[2].replace(/^\||\| *$/g,"").split("|"),r=t[3]&&t[3].trim()?t[3].replace(/\n[ \t]*$/,"").split("\n"):[],i={type:"table",raw:t[0],header:[],align:[],rows:[]};if(n.length===s.length){for(const e of s)/^ *-+: *$/.test(e)?i.align.push("right"):/^ *:-+: *$/.test(e)?i.align.push("center"):/^ *:-+ *$/.test(e)?i.align.push("left"):i.align.push(null);for(const e of n)i.header.push({text:e,tokens:this.lexer.inline(e)});for(const e of r)i.rows.push(d(e,i.header.length).map((e=>({text:e,tokens:this.lexer.inline(e)}))));return i}}lheading(e){const t=this.rules.block.lheading.exec(e);if(t)return{type:"heading",raw:t[0],depth:"="===t[2].charAt(0)?1:2,text:t[1],tokens:this.lexer.inline(t[1])}}paragraph(e){const t=this.rules.block.paragraph.exec(e);if(t){const e="\n"===t[1].charAt(t[1].length-1)?t[1].slice(0,-1):t[1];return{type:"paragraph",raw:t[0],text:e,tokens:this.lexer.inline(e)}}}text(e){const t=this.rules.block.text.exec(e);if(t)return{type:"text",raw:t[0],text:t[0],tokens:this.lexer.inline(t[0])}}escape(e){const t=this.rules.inline.escape.exec(e);if(t)return{type:"escape",raw:t[0],text:c(t[1])}}tag(e){const t=this.rules.inline.tag.exec(e);if(t)return!this.lexer.state.inLink&&/^/i.test(t[0])&&(this.lexer.state.inLink=!1),!this.lexer.state.inRawBlock&&/^<(pre|code|kbd|script)(\s|>)/i.test(t[0])?this.lexer.state.inRawBlock=!0:this.lexer.state.inRawBlock&&/^<\/(pre|code|kbd|script)(\s|>)/i.test(t[0])&&(this.lexer.state.inRawBlock=!1),{type:"html",raw:t[0],inLink:this.lexer.state.inLink,inRawBlock:this.lexer.state.inRawBlock,block:!1,text:t[0]}}link(e){const t=this.rules.inline.link.exec(e);if(t){const e=t[2].trim();if(!this.options.pedantic&&/^$/.test(e))return;const t=x(e.slice(0,-1),"\\");if((e.length-t.length)%2==0)return}else{const e=function(e,t){if(-1===e.indexOf(t[1]))return-1;let n=0;for(let s=0;s-1){const n=(0===t[0].indexOf("!")?5:4)+t[1].length+e;t[2]=t[2].substring(0,e),t[0]=t[0].substring(0,n).trim(),t[3]=""}}let n=t[2],s="";if(this.options.pedantic){const e=/^([^'"]*[^\s])\s+(['"])(.*)\2/.exec(n);e&&(n=e[1],s=e[3])}else s=t[3]?t[3].slice(1,-1):"";return n=n.trim(),/^$/.test(e)?n.slice(1):n.slice(1,-1)),b(t,{href:n?n.replace(this.rules.inline.anyPunctuation,"$1"):n,title:s?s.replace(this.rules.inline.anyPunctuation,"$1"):s},t[0],this.lexer)}}reflink(e,t){let n;if((n=this.rules.inline.reflink.exec(e))||(n=this.rules.inline.nolink.exec(e))){const e=t[(n[2]||n[1]).replace(/\s+/g," ").toLowerCase()];if(!e){const e=n[0].charAt(0);return{type:"text",raw:e,text:e}}return b(n,e,n[0],this.lexer)}}emStrong(e,t,n=""){let s=this.rules.inline.emStrongLDelim.exec(e);if(!s)return;if(s[3]&&n.match(/[\p{L}\p{N}]/u))return;if(!(s[1]||s[2]||"")||!n||this.rules.inline.punctuation.exec(n)){const n=[...s[0]].length-1;let r,i,l=n,o=0;const a="*"===s[0][0]?this.rules.inline.emStrongRDelimAst:this.rules.inline.emStrongRDelimUnd;for(a.lastIndex=0,t=t.slice(-1*e.length+n);null!=(s=a.exec(t));){if(r=s[1]||s[2]||s[3]||s[4]||s[5]||s[6],!r)continue;if(i=[...r].length,s[3]||s[4]){l+=i;continue}if((s[5]||s[6])&&n%3&&!((n+i)%3)){o+=i;continue}if(l-=i,l>0)continue;i=Math.min(i,i+l+o);const t=[...s[0]][0].length,a=e.slice(0,n+s.index+t+i);if(Math.min(n,i)%2){const e=a.slice(1,-1);return{type:"em",raw:a,text:e,tokens:this.lexer.inlineTokens(e)}}const c=a.slice(2,-2);return{type:"strong",raw:a,text:c,tokens:this.lexer.inlineTokens(c)}}}}codespan(e){const t=this.rules.inline.code.exec(e);if(t){let e=t[2].replace(/\n/g," ");const n=/[^ ]/.test(e),s=/^ /.test(e)&&/ $/.test(e);return n&&s&&(e=e.substring(1,e.length-1)),e=c(e,!0),{type:"codespan",raw:t[0],text:e}}}br(e){const t=this.rules.inline.br.exec(e);if(t)return{type:"br",raw:t[0]}}del(e){const t=this.rules.inline.del.exec(e);if(t)return{type:"del",raw:t[0],text:t[2],tokens:this.lexer.inlineTokens(t[2])}}autolink(e){const t=this.rules.inline.autolink.exec(e);if(t){let e,n;return"@"===t[2]?(e=c(t[1]),n="mailto:"+e):(e=c(t[1]),n=e),{type:"link",raw:t[0],text:e,href:n,tokens:[{type:"text",raw:e,text:e}]}}}url(e){let t;if(t=this.rules.inline.url.exec(e)){let e,n;if("@"===t[2])e=c(t[0]),n="mailto:"+e;else{let s;do{s=t[0],t[0]=this.rules.inline._backpedal.exec(t[0])?.[0]??""}while(s!==t[0]);e=c(t[0]),n="www."===t[1]?"http://"+t[0]:t[0]}return{type:"link",raw:t[0],text:e,href:n,tokens:[{type:"text",raw:e,text:e}]}}}inlineText(e){const t=this.rules.inline.text.exec(e);if(t){let e;return e=this.lexer.state.inRawBlock?t[0]:c(t[0]),{type:"text",raw:t[0],text:e}}}}const m=/^ {0,3}((?:-[\t ]*){3,}|(?:_[ \t]*){3,}|(?:\*[ \t]*){3,})(?:\n+|$)/,y=/(?:[*+-]|\d{1,9}[.)])/,$=k(/^(?!bull |blockCode|fences|blockquote|heading|html)((?:.|\n(?!\s*?\n|bull |blockCode|fences|blockquote|heading|html))+?)\n {0,3}(=+|-+) *(?:\n+|$)/).replace(/bull/g,y).replace(/blockCode/g,/ {4}/).replace(/fences/g,/ {0,3}(?:`{3,}|~{3,})/).replace(/blockquote/g,/ {0,3}>/).replace(/heading/g,/ {0,3}#{1,6}/).replace(/html/g,/ {0,3}<[^\n>]+>\n/).getRegex(),z=/^([^\n]+(?:\n(?!hr|heading|lheading|blockquote|fences|list|html|table| +\n)[^\n]+)*)/,T=/(?!\s*\])(?:\\.|[^\[\]\\])+/,R=k(/^ {0,3}\[(label)\]: *(?:\n *)?([^<\s][^\s]*|<.*?>)(?:(?: +(?:\n *)?| *\n *)(title))? *(?:\n+|$)/).replace("label",T).replace("title",/(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/).getRegex(),_=k(/^( {0,3}bull)([ \t][^\n]+?)?(?:\n|$)/).replace(/bull/g,y).getRegex(),A="address|article|aside|base|basefont|blockquote|body|caption|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option|p|param|search|section|summary|table|tbody|td|tfoot|th|thead|title|tr|track|ul",S=/|$))/,I=k("^ {0,3}(?:<(script|pre|style|textarea)[\\s>][\\s\\S]*?(?:[^\\n]*\\n+|$)|comment[^\\n]*(\\n+|$)|<\\?[\\s\\S]*?(?:\\?>\\n*|$)|\\n*|$)|\\n*|$)|)[\\s\\S]*?(?:(?:\\n *)+\\n|$)|<(?!script|pre|style|textarea)([a-z][\\w-]*)(?:attribute)*? */?>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n *)+\\n|$)|(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n *)+\\n|$))","i").replace("comment",S).replace("tag",A).replace("attribute",/ +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/).getRegex(),E=k(z).replace("hr",m).replace("heading"," {0,3}#{1,6}(?:\\s|$)").replace("|lheading","").replace("|table","").replace("blockquote"," {0,3}>").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html",")|<(?:script|pre|style|textarea|!--)").replace("tag",A).getRegex(),q={blockquote:k(/^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/).replace("paragraph",E).getRegex(),code:/^( {4}[^\n]+(?:\n(?: *(?:\n|$))*)?)+/,def:R,fences:/^ {0,3}(`{3,}(?=[^`\n]*(?:\n|$))|~{3,})([^\n]*)(?:\n|$)(?:|([\s\S]*?)(?:\n|$))(?: {0,3}\1[~`]* *(?=\n|$)|$)/,heading:/^ {0,3}(#{1,6})(?=\s|$)(.*)(?:\n+|$)/,hr:m,html:I,lheading:$,list:_,newline:/^(?: *(?:\n|$))+/,paragraph:E,table:f,text:/^[^\n]+/},Z=k("^ *([^\\n ].*)\\n {0,3}((?:\\| *)?:?-+:? *(?:\\| *:?-+:? *)*(?:\\| *)?)(?:\\n((?:(?! *\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)").replace("hr",m).replace("heading"," {0,3}#{1,6}(?:\\s|$)").replace("blockquote"," {0,3}>").replace("code"," {4}[^\\n]").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html",")|<(?:script|pre|style|textarea|!--)").replace("tag",A).getRegex(),L={...q,table:Z,paragraph:k(z).replace("hr",m).replace("heading"," {0,3}#{1,6}(?:\\s|$)").replace("|lheading","").replace("table",Z).replace("blockquote"," {0,3}>").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html",")|<(?:script|pre|style|textarea|!--)").replace("tag",A).getRegex()},P={...q,html:k("^ *(?:comment *(?:\\n|\\s*$)|<(tag)[\\s\\S]+? *(?:\\n{2,}|\\s*$)|\\s]*)*?/?> *(?:\\n{2,}|\\s*$))").replace("comment",S).replace(/tag/g,"(?!(?:a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)\\b)\\w+(?!:|[^\\w\\s@]*@)\\b").getRegex(),def:/^ *\[([^\]]+)\]: *]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/,heading:/^(#{1,6})(.*)(?:\n+|$)/,fences:f,lheading:/^(.+?)\n {0,3}(=+|-+) *(?:\n+|$)/,paragraph:k(z).replace("hr",m).replace("heading"," *#{1,6} *[^\n]").replace("lheading",$).replace("|table","").replace("blockquote"," {0,3}>").replace("|fences","").replace("|list","").replace("|html","").replace("|tag","").getRegex()},Q=/^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/,v=/^( {2,}|\\)\n(?!\s*$)/,B="\\p{P}\\p{S}",C=k(/^((?![*_])[\spunctuation])/,"u").replace(/punctuation/g,B).getRegex(),M=k(/^(?:\*+(?:((?!\*)[punct])|[^\s*]))|^_+(?:((?!_)[punct])|([^\s_]))/,"u").replace(/punct/g,B).getRegex(),O=k("^[^_*]*?__[^_*]*?\\*[^_*]*?(?=__)|[^*]+(?=[^*])|(?!\\*)[punct](\\*+)(?=[\\s]|$)|[^punct\\s](\\*+)(?!\\*)(?=[punct\\s]|$)|(?!\\*)[punct\\s](\\*+)(?=[^punct\\s])|[\\s](\\*+)(?!\\*)(?=[punct])|(?!\\*)[punct](\\*+)(?!\\*)(?=[punct])|[^punct\\s](\\*+)(?=[^punct\\s])","gu").replace(/punct/g,B).getRegex(),D=k("^[^_*]*?\\*\\*[^_*]*?_[^_*]*?(?=\\*\\*)|[^_]+(?=[^_])|(?!_)[punct](_+)(?=[\\s]|$)|[^punct\\s](_+)(?!_)(?=[punct\\s]|$)|(?!_)[punct\\s](_+)(?=[^punct\\s])|[\\s](_+)(?!_)(?=[punct])|(?!_)[punct](_+)(?!_)(?=[punct])","gu").replace(/punct/g,B).getRegex(),j=k(/\\([punct])/,"gu").replace(/punct/g,B).getRegex(),H=k(/^<(scheme:[^\s\x00-\x1f<>]*|email)>/).replace("scheme",/[a-zA-Z][a-zA-Z0-9+.-]{1,31}/).replace("email",/[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+(@)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?![-_])/).getRegex(),U=k(S).replace("(?:--\x3e|$)","--\x3e").getRegex(),X=k("^comment|^|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>|^<\\?[\\s\\S]*?\\?>|^|^").replace("comment",U).replace("attribute",/\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/).getRegex(),F=/(?:\[(?:\\.|[^\[\]\\])*\]|\\.|`[^`]*`|[^\[\]\\`])*?/,N=k(/^!?\[(label)\]\(\s*(href)(?:\s+(title))?\s*\)/).replace("label",F).replace("href",/<(?:\\.|[^\n<>\\])+>|[^\s\x00-\x1f]*/).replace("title",/"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/).getRegex(),G=k(/^!?\[(label)\]\[(ref)\]/).replace("label",F).replace("ref",T).getRegex(),J=k(/^!?\[(ref)\](?:\[\])?/).replace("ref",T).getRegex(),K={_backpedal:f,anyPunctuation:j,autolink:H,blockSkip:/\[[^[\]]*?\]\([^\(\)]*?\)|`[^`]*?`|<[^<>]*?>/g,br:v,code:/^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/,del:f,emStrongLDelim:M,emStrongRDelimAst:O,emStrongRDelimUnd:D,escape:Q,link:N,nolink:J,punctuation:C,reflink:G,reflinkSearch:k("reflink|nolink(?!\\()","g").replace("reflink",G).replace("nolink",J).getRegex(),tag:X,text:/^(`+|[^`])(?:(?= {2,}\n)|[\s\S]*?(?:(?=[\\t+" ".repeat(n.length)));e;)if(!(this.options.extensions&&this.options.extensions.block&&this.options.extensions.block.some((s=>!!(n=s.call({lexer:this},e,t))&&(e=e.substring(n.raw.length),t.push(n),!0)))))if(n=this.tokenizer.space(e))e=e.substring(n.raw.length),1===n.raw.length&&t.length>0?t[t.length-1].raw+="\n":t.push(n);else if(n=this.tokenizer.code(e))e=e.substring(n.raw.length),s=t[t.length-1],!s||"paragraph"!==s.type&&"text"!==s.type?t.push(n):(s.raw+="\n"+n.raw,s.text+="\n"+n.text,this.inlineQueue[this.inlineQueue.length-1].src=s.text);else if(n=this.tokenizer.fences(e))e=e.substring(n.raw.length),t.push(n);else if(n=this.tokenizer.heading(e))e=e.substring(n.raw.length),t.push(n);else if(n=this.tokenizer.hr(e))e=e.substring(n.raw.length),t.push(n);else if(n=this.tokenizer.blockquote(e))e=e.substring(n.raw.length),t.push(n);else if(n=this.tokenizer.list(e))e=e.substring(n.raw.length),t.push(n);else if(n=this.tokenizer.html(e))e=e.substring(n.raw.length),t.push(n);else if(n=this.tokenizer.def(e))e=e.substring(n.raw.length),s=t[t.length-1],!s||"paragraph"!==s.type&&"text"!==s.type?this.tokens.links[n.tag]||(this.tokens.links[n.tag]={href:n.href,title:n.title}):(s.raw+="\n"+n.raw,s.text+="\n"+n.raw,this.inlineQueue[this.inlineQueue.length-1].src=s.text);else if(n=this.tokenizer.table(e))e=e.substring(n.raw.length),t.push(n);else if(n=this.tokenizer.lheading(e))e=e.substring(n.raw.length),t.push(n);else{if(r=e,this.options.extensions&&this.options.extensions.startBlock){let t=1/0;const n=e.slice(1);let s;this.options.extensions.startBlock.forEach((e=>{s=e.call({lexer:this},n),"number"==typeof s&&s>=0&&(t=Math.min(t,s))})),t<1/0&&t>=0&&(r=e.substring(0,t+1))}if(this.state.top&&(n=this.tokenizer.paragraph(r)))s=t[t.length-1],i&&"paragraph"===s.type?(s.raw+="\n"+n.raw,s.text+="\n"+n.text,this.inlineQueue.pop(),this.inlineQueue[this.inlineQueue.length-1].src=s.text):t.push(n),i=r.length!==e.length,e=e.substring(n.raw.length);else if(n=this.tokenizer.text(e))e=e.substring(n.raw.length),s=t[t.length-1],s&&"text"===s.type?(s.raw+="\n"+n.raw,s.text+="\n"+n.text,this.inlineQueue.pop(),this.inlineQueue[this.inlineQueue.length-1].src=s.text):t.push(n);else if(e){const t="Infinite loop on byte: "+e.charCodeAt(0);if(this.options.silent){console.error(t);break}throw new Error(t)}}return this.state.top=!0,t}inline(e,t=[]){return this.inlineQueue.push({src:e,tokens:t}),t}inlineTokens(e,t=[]){let n,s,r,i,l,o,a=e;if(this.tokens.links){const e=Object.keys(this.tokens.links);if(e.length>0)for(;null!=(i=this.tokenizer.rules.inline.reflinkSearch.exec(a));)e.includes(i[0].slice(i[0].lastIndexOf("[")+1,-1))&&(a=a.slice(0,i.index)+"["+"a".repeat(i[0].length-2)+"]"+a.slice(this.tokenizer.rules.inline.reflinkSearch.lastIndex))}for(;null!=(i=this.tokenizer.rules.inline.blockSkip.exec(a));)a=a.slice(0,i.index)+"["+"a".repeat(i[0].length-2)+"]"+a.slice(this.tokenizer.rules.inline.blockSkip.lastIndex);for(;null!=(i=this.tokenizer.rules.inline.anyPunctuation.exec(a));)a=a.slice(0,i.index)+"++"+a.slice(this.tokenizer.rules.inline.anyPunctuation.lastIndex);for(;e;)if(l||(o=""),l=!1,!(this.options.extensions&&this.options.extensions.inline&&this.options.extensions.inline.some((s=>!!(n=s.call({lexer:this},e,t))&&(e=e.substring(n.raw.length),t.push(n),!0)))))if(n=this.tokenizer.escape(e))e=e.substring(n.raw.length),t.push(n);else if(n=this.tokenizer.tag(e))e=e.substring(n.raw.length),s=t[t.length-1],s&&"text"===n.type&&"text"===s.type?(s.raw+=n.raw,s.text+=n.text):t.push(n);else if(n=this.tokenizer.link(e))e=e.substring(n.raw.length),t.push(n);else if(n=this.tokenizer.reflink(e,this.tokens.links))e=e.substring(n.raw.length),s=t[t.length-1],s&&"text"===n.type&&"text"===s.type?(s.raw+=n.raw,s.text+=n.text):t.push(n);else if(n=this.tokenizer.emStrong(e,a,o))e=e.substring(n.raw.length),t.push(n);else if(n=this.tokenizer.codespan(e))e=e.substring(n.raw.length),t.push(n);else if(n=this.tokenizer.br(e))e=e.substring(n.raw.length),t.push(n);else if(n=this.tokenizer.del(e))e=e.substring(n.raw.length),t.push(n);else if(n=this.tokenizer.autolink(e))e=e.substring(n.raw.length),t.push(n);else if(this.state.inLink||!(n=this.tokenizer.url(e))){if(r=e,this.options.extensions&&this.options.extensions.startInline){let t=1/0;const n=e.slice(1);let s;this.options.extensions.startInline.forEach((e=>{s=e.call({lexer:this},n),"number"==typeof s&&s>=0&&(t=Math.min(t,s))})),t<1/0&&t>=0&&(r=e.substring(0,t+1))}if(n=this.tokenizer.inlineText(r))e=e.substring(n.raw.length),"_"!==n.raw.slice(-1)&&(o=n.raw.slice(-1)),l=!0,s=t[t.length-1],s&&"text"===s.type?(s.raw+=n.raw,s.text+=n.text):t.push(n);else if(e){const t="Infinite loop on byte: "+e.charCodeAt(0);if(this.options.silent){console.error(t);break}throw new Error(t)}}else e=e.substring(n.raw.length),t.push(n);return t}}class se{options;constructor(t){this.options=t||e.defaults}code(e,t,n){const s=(t||"").match(/^\S*/)?.[0];return e=e.replace(/\n$/,"")+"\n",s?'
    '+(n?e:c(e,!0))+"
    \n":"
    "+(n?e:c(e,!0))+"
    \n"}blockquote(e){return`
    \n${e}
    \n`}html(e,t){return e}heading(e,t,n){return`${e}\n`}hr(){return"
    \n"}list(e,t,n){const s=t?"ol":"ul";return"<"+s+(t&&1!==n?' start="'+n+'"':"")+">\n"+e+"\n"}listitem(e,t,n){return`
  • ${e}
  • \n`}checkbox(e){return"'}paragraph(e){return`

    ${e}

    \n`}table(e,t){return t&&(t=`${t}`),"\n\n"+e+"\n"+t+"
    \n"}tablerow(e){return`\n${e}\n`}tablecell(e,t){const n=t.header?"th":"td";return(t.align?`<${n} align="${t.align}">`:`<${n}>`)+e+`\n`}strong(e){return`${e}`}em(e){return`${e}`}codespan(e){return`${e}`}br(){return"
    "}del(e){return`${e}`}link(e,t,n){const s=g(e);if(null===s)return n;let r='
    ",r}image(e,t,n){const s=g(e);if(null===s)return n;let r=`${n}0&&"paragraph"===n.tokens[0].type?(n.tokens[0].text=e+" "+n.tokens[0].text,n.tokens[0].tokens&&n.tokens[0].tokens.length>0&&"text"===n.tokens[0].tokens[0].type&&(n.tokens[0].tokens[0].text=e+" "+n.tokens[0].tokens[0].text)):n.tokens.unshift({type:"text",text:e+" "}):o+=e+" "}o+=this.parse(n.tokens,i),l+=this.renderer.listitem(o,r,!!s)}n+=this.renderer.list(l,t,s);continue}case"html":{const e=r;n+=this.renderer.html(e.text,e.block);continue}case"paragraph":{const e=r;n+=this.renderer.paragraph(this.parseInline(e.tokens));continue}case"text":{let i=r,l=i.tokens?this.parseInline(i.tokens):i.text;for(;s+1{const r=e[s].flat(1/0);n=n.concat(this.walkTokens(r,t))})):e.tokens&&(n=n.concat(this.walkTokens(e.tokens,t)))}}return n}use(...e){const t=this.defaults.extensions||{renderers:{},childTokens:{}};return e.forEach((e=>{const n={...e};if(n.async=this.defaults.async||n.async||!1,e.extensions&&(e.extensions.forEach((e=>{if(!e.name)throw new Error("extension name required");if("renderer"in e){const n=t.renderers[e.name];t.renderers[e.name]=n?function(...t){let s=e.renderer.apply(this,t);return!1===s&&(s=n.apply(this,t)),s}:e.renderer}if("tokenizer"in e){if(!e.level||"block"!==e.level&&"inline"!==e.level)throw new Error("extension level must be 'block' or 'inline'");const n=t[e.level];n?n.unshift(e.tokenizer):t[e.level]=[e.tokenizer],e.start&&("block"===e.level?t.startBlock?t.startBlock.push(e.start):t.startBlock=[e.start]:"inline"===e.level&&(t.startInline?t.startInline.push(e.start):t.startInline=[e.start]))}"childTokens"in e&&e.childTokens&&(t.childTokens[e.name]=e.childTokens)})),n.extensions=t),e.renderer){const t=this.defaults.renderer||new se(this.defaults);for(const n in e.renderer){if(!(n in t))throw new Error(`renderer '${n}' does not exist`);if("options"===n)continue;const s=n,r=e.renderer[s],i=t[s];t[s]=(...e)=>{let n=r.apply(t,e);return!1===n&&(n=i.apply(t,e)),n||""}}n.renderer=t}if(e.tokenizer){const t=this.defaults.tokenizer||new w(this.defaults);for(const n in e.tokenizer){if(!(n in t))throw new Error(`tokenizer '${n}' does not exist`);if(["options","rules","lexer"].includes(n))continue;const s=n,r=e.tokenizer[s],i=t[s];t[s]=(...e)=>{let n=r.apply(t,e);return!1===n&&(n=i.apply(t,e)),n}}n.tokenizer=t}if(e.hooks){const t=this.defaults.hooks||new le;for(const n in e.hooks){if(!(n in t))throw new Error(`hook '${n}' does not exist`);if("options"===n)continue;const s=n,r=e.hooks[s],i=t[s];le.passThroughHooks.has(n)?t[s]=e=>{if(this.defaults.async)return Promise.resolve(r.call(t,e)).then((e=>i.call(t,e)));const n=r.call(t,e);return i.call(t,n)}:t[s]=(...e)=>{let n=r.apply(t,e);return!1===n&&(n=i.apply(t,e)),n}}n.hooks=t}if(e.walkTokens){const t=this.defaults.walkTokens,s=e.walkTokens;n.walkTokens=function(e){let n=[];return n.push(s.call(this,e)),t&&(n=n.concat(t.call(this,e))),n}}this.defaults={...this.defaults,...n}})),this}setOptions(e){return this.defaults={...this.defaults,...e},this}lexer(e,t){return ne.lex(e,t??this.defaults)}parser(e,t){return ie.parse(e,t??this.defaults)}#e(e,t){return(n,s)=>{const r={...s},i={...this.defaults,...r};!0===this.defaults.async&&!1===r.async&&(i.silent||console.warn("marked(): The async option was set to true by an extension. The async: false option sent to parse will be ignored."),i.async=!0);const l=this.#t(!!i.silent,!!i.async);if(null==n)return l(new Error("marked(): input parameter is undefined or null"));if("string"!=typeof n)return l(new Error("marked(): input parameter is of type "+Object.prototype.toString.call(n)+", string expected"));if(i.hooks&&(i.hooks.options=i),i.async)return Promise.resolve(i.hooks?i.hooks.preprocess(n):n).then((t=>e(t,i))).then((e=>i.hooks?i.hooks.processAllTokens(e):e)).then((e=>i.walkTokens?Promise.all(this.walkTokens(e,i.walkTokens)).then((()=>e)):e)).then((e=>t(e,i))).then((e=>i.hooks?i.hooks.postprocess(e):e)).catch(l);try{i.hooks&&(n=i.hooks.preprocess(n));let s=e(n,i);i.hooks&&(s=i.hooks.processAllTokens(s)),i.walkTokens&&this.walkTokens(s,i.walkTokens);let r=t(s,i);return i.hooks&&(r=i.hooks.postprocess(r)),r}catch(e){return l(e)}}}#t(e,t){return n=>{if(n.message+="\nPlease report this to https://github.com/markedjs/marked.",e){const e="

    An error occurred:

    "+c(n.message+"",!0)+"
    ";return t?Promise.resolve(e):e}if(t)return Promise.reject(n);throw n}}}const ae=new oe;function ce(e,t){return ae.parse(e,t)}ce.options=ce.setOptions=function(e){return ae.setOptions(e),ce.defaults=ae.defaults,n(ce.defaults),ce},ce.getDefaults=t,ce.defaults=e.defaults,ce.use=function(...e){return ae.use(...e),ce.defaults=ae.defaults,n(ce.defaults),ce},ce.walkTokens=function(e,t){return ae.walkTokens(e,t)},ce.parseInline=ae.parseInline,ce.Parser=ie,ce.parser=ie.parse,ce.Renderer=se,ce.TextRenderer=re,ce.Lexer=ne,ce.lexer=ne.lex,ce.Tokenizer=w,ce.Hooks=le,ce.parse=ce;const he=ce.options,pe=ce.setOptions,ue=ce.use,ke=ce.walkTokens,ge=ce.parseInline,fe=ce,de=ie.parse,xe=ne.lex;e.Hooks=le,e.Lexer=ne,e.Marked=oe,e.Parser=ie,e.Renderer=se,e.TextRenderer=re,e.Tokenizer=w,e.getDefaults=t,e.lexer=xe,e.marked=ce,e.options=he,e.parse=fe,e.parseInline=ge,e.parser=de,e.setOptions=pe,e.use=ue,e.walkTokens=ke})); diff --git a/ComfyUI/custom_nodes/comfyui-kjnodes/kjweb_async/protovis.min.js b/ComfyUI/custom_nodes/comfyui-kjnodes/kjweb_async/protovis.min.js new file mode 100644 index 0000000000000000000000000000000000000000..dfb84166521a49e4f7e41539933b101e126bd72f --- /dev/null +++ b/ComfyUI/custom_nodes/comfyui-kjnodes/kjweb_async/protovis.min.js @@ -0,0 +1,277 @@ +var a;if(!Array.prototype.map)Array.prototype.map=function(b,c){for(var d=this.length,f=new Array(d),g=0;g>>0,f=0;f=d)throw new Error("reduce: no values, no initial value");}for(;f=0&&d=69&&m<100?1900:0)});return"([0-9]+)";case "%Y":q.push(function(m){g=m});return"([0-9]+)";case "%%":q.push(function(){}); +return"%"}return p});(f=f.match(n))&&f.forEach(function(p,m){q[m](p)});return new Date(g,h,i,j,k,l)};return c}; +pv.Format.time=function(b){function c(f){f=Number(f);switch(b){case "short":if(f>=31536E6)return(f/31536E6).toFixed(1)+" years";else if(f>=6048E5)return(f/6048E5).toFixed(1)+" weeks";else if(f>=864E5)return(f/864E5).toFixed(1)+" days";else if(f>=36E5)return(f/36E5).toFixed(1)+" hours";else if(f>=6E4)return(f/6E4).toFixed(1)+" minutes";return(f/1E3).toFixed(1)+" seconds";case "long":var g=[],h=f%36E5/6E4>>0;g.push(d("0",2,f%6E4/1E3>>0));if(f>=36E5){var i=f%864E5/36E5>>0;g.push(d("0",2,h));if(f>=864E5){g.push(d("0", +2,i));g.push(Math.floor(f/864E5).toFixed())}else g.push(i.toFixed())}else g.push(h.toFixed());return g.reverse().join(":")}}var d=pv.Format.pad;c.format=c;c.parse=function(f){switch(b){case "short":for(var g=/([0-9,.]+)\s*([a-z]+)/g,h,i=0;h=g.exec(f);){var j=parseFloat(h[0].replace(",","")),k=0;switch(h[2].toLowerCase()){case "year":case "years":k=31536E6;break;case "week":case "weeks":k=6048E5;break;case "day":case "days":k=864E5;break;case "hour":case "hours":k=36E5;break;case "minute":case "minutes":k= +6E4;break;case "second":case "seconds":k=1E3;break}i+=j*k}return i;case "long":h=f.replace(",","").split(":").reverse();i=0;if(h.length)i+=parseFloat(h[0])*1E3;if(h.length>1)i+=parseFloat(h[1])*6E4;if(h.length>2)i+=parseFloat(h[2])*36E5;if(h.length>3)i+=parseFloat(h[3])*864E5;return i}};return c}; +pv.Format.number=function(){function b(r){if(Infinity>h)r=Math.round(r*i)/i;var s=String(Math.abs(r)).split("."),t=s[0];if(t.length>d)t=t.substring(t.length-d);if(l&&t.length3)t=t.replace(/\B(?=(?:\d{3})+(?!\d))/g,n);if(!l&&t.lengthd)s=s.substring(s.length-d);r=r[1]?Number("0."+r[1]):0;if(Infinity>h)r=Math.round(r*i)/i;return Math.round(s)+r};b.integerDigits=function(r,s){if(arguments.length){c=Number(r);d=arguments.length>1?Number(s):c;f=c+Math.floor(c/3)*n.length;return this}return[c,d]};b.fractionDigits=function(r,s){if(arguments.length){g= +Number(r);h=arguments.length>1?Number(s):g;i=Math.pow(10,h);return this}return[g,h]};b.integerPad=function(r){if(arguments.length){j=String(r);l=/\d/.test(j);return this}return j};b.fractionPad=function(r){if(arguments.length){k=String(r);return this}return k};b.decimal=function(r){if(arguments.length){q=String(r);return this}return q};b.group=function(r){if(arguments.length){n=r?String(r):"";f=c+Math.floor(c/3)*n.length;return this}return n};b.negativeAffix=function(r,s){if(arguments.length){p=String(r|| +"");m=String(s||"");return this}return[p,m]};return b};pv.map=function(b,c){var d={};return c?b.map(function(f,g){d.index=g;return c.call(d,f)}):b.slice()};pv.repeat=function(b,c){if(arguments.length==1)c=2;return pv.blend(pv.range(c).map(function(){return b}))};pv.cross=function(b,c){for(var d=[],f=0,g=b.length,h=c.length;fc){b.length=d;for(var f=c;fc?1:0}; +pv.reverseOrder=function(b,c){return cb?1:0};pv.search=function(b,c,d){if(!d)d=pv.identity;for(var f=0,g=b.length-1;f<=g;){var h=f+g>>1,i=d(b[h]);if(ic)g=h-1;else return h}return-f-1};pv.search.index=function(b,c,d){b=pv.search(b,c,d);return b<0?-b-1:b}; +pv.range=function(b,c,d){if(arguments.length==1){c=b;b=0}if(d==undefined)d=1;if((c-b)/d==Infinity)throw new Error("range must be finite");var f=[],g=0,h;c-=(c-b)*1.0E-10;if(d<0)for(;(h=b+d*g++)>c;)f.push(h);else for(;(h=b+d*g++)f){f=i;d=h}}return d}; +pv.min=function(b,c){if(c==pv.index)return 0;return Math.min.apply(null,c?pv.map(b,c):b)};pv.min.index=function(b,c){if(!b.length)return-1;if(c==pv.index)return 0;if(!c)c=pv.identity;for(var d=0,f=Infinity,g={},h=0;h0?Math.pow(c,Math.floor(pv.log(b,c))):-Math.pow(c,-Math.floor(-pv.log(-b,c)))};pv.logCeil=function(b,c){return b>0?Math.pow(c,Math.ceil(pv.log(b,c))):-Math.pow(c,-Math.ceil(-pv.log(-b,c)))}; +(function(){var b=Math.PI/180,c=180/Math.PI;pv.radians=function(d){return b*d};pv.degrees=function(d){return c*d}})();pv.keys=function(b){var c=[];for(var d in b)c.push(d);return c};pv.entries=function(b){var c=[];for(var d in b)c.push({key:d,value:b[d]});return c};pv.values=function(b){var c=[];for(var d in b)c.push(b[d]);return c};pv.dict=function(b,c){for(var d={},f={},g=0;g=94608E6){p=31536E6;u="%Y";o=function(w){w.setFullYear(w.getFullYear()+v)}}else if(t>=7776E6){p=2592E6;u="%m/%Y";o=function(w){w.setMonth(w.getMonth()+v)}}else if(t>=18144E5){p=6048E5;u="%m/%d";o=function(w){w.setDate(w.getDate()+7*v)}}else if(t>=2592E5){p=864E5;u="%m/%d";o=function(w){w.setDate(w.getDate()+v)}}else if(t>=108E5){p=36E5;u="%I:%M %p";o=function(w){w.setHours(w.getHours()+ +v)}}else if(t>=18E4){p=6E4;u="%I:%M %p";o=function(w){w.setMinutes(w.getMinutes()+v)}}else if(t>=3E3){p=1E3;u="%I:%M:%S";o=function(w){w.setSeconds(w.getSeconds()+v)}}else{p=1;u="%S.%Qs";o=function(w){w.setTime(w.getTime()+v)}}q=pv.Format.date(u);s=new Date(s);u=[];x(s,p);t=t/p;if(t>10)switch(p){case 36E5:v=t>20?6:3;s.setHours(Math.floor(s.getHours()/v)*v);break;case 2592E6:v=3;s.setMonth(Math.floor(s.getMonth()/v)*v);break;case 6E4:v=t>30?15:t>15?10:5;s.setMinutes(Math.floor(s.getMinutes()/v)*v); +break;case 1E3:v=t>90?15:t>60?10:5;s.setSeconds(Math.floor(s.getSeconds()/v)*v);break;case 1:v=t>1E3?250:t>200?100:t>100?50:t>50?25:5;s.setMilliseconds(Math.floor(s.getMilliseconds()/v)*v);break;default:v=pv.logCeil(t/15,10);if(t/v<2)v/=5;else if(t/v<5)v/=2;s.setFullYear(Math.floor(s.getFullYear()/v)*v);break}for(;;){o(s);if(s>m)break;u.push(new Date(s))}return r?u.reverse():u}arguments.length||(n=10);v=pv.logFloor(t/n,10);p=n/(t/v);if(p<=0.15)v*=10;else if(p<=0.35)v*=5;else if(p<=0.75)v*=2;p=Math.ceil(s/ +v)*v;m=Math.floor(m/v)*v;q=pv.Format.number().fractionDigits(Math.max(0,-Math.floor(pv.log(v,10)+0.01)));m=pv.range(p,m+v,v);return r?m.reverse():m};c.tickFormat=function(n){return q(n)};c.nice=function(){if(d.length!=2)return this;var n=d[0],p=d[d.length-1],m=p0;i--)l.push(-g(-j)*i);else{for(;jh[1];k--);return l.slice(j,k)};b.tickFormat=function(h){return h.toPrecision(1)}; +b.nice=function(){var h=b.domain();return b.domain(pv.logFloor(h[0],c),pv.logCeil(h[1],c))};b.base=function(h){if(arguments.length){c=Number(h);d=Math.log(c);b.transform(f,g);return this}return c};b.domain.apply(b,arguments);return b.base(10)};pv.Scale.root=function(){var b=pv.Scale.quantitative();b.power=function(c){if(arguments.length){var d=Number(c),f=1/d;b.transform(function(g){return Math.pow(g,f)},function(g){return Math.pow(g,d)});return this}return d};b.domain.apply(b,arguments);return b.power(2)}; +pv.Scale.ordinal=function(){function b(g){g in d||(d[g]=c.push(g)-1);return f[d[g]%f.length]}var c=[],d={},f=[];b.domain=function(g,h){if(arguments.length){g=g instanceof Array?arguments.length>1?pv.map(g,h):g:Array.prototype.slice.call(arguments);c=[];for(var i={},j=0;j1?pv.map(g,h):g:Array.prototype.slice.call(arguments); +if(typeof f[0]=="string")f=f.map(pv.color);return this}return f};b.split=function(g,h){var i=(h-g)/this.domain().length;f=pv.range(g+i/2,h,i);return this};b.splitFlush=function(g,h){var i=this.domain().length,j=(h-g)/(i-1);f=i==1?[(g+h)/2]:pv.range(g,h+j/2,j);return this};b.splitBanded=function(g,h,i){if(arguments.length<3)i=1;if(i<0){var j=this.domain().length;j=(h-g- -i*j)/(j+1);f=pv.range(g+j,h,j-i);f.band=-i}else{j=(h-g)/(this.domain().length+(1-i));f=pv.range(g+j*(1-i),h,j);f.band=j*i}return this}; +b.by=function(g){function h(){return b(g.apply(this,arguments))}for(var i in b)h[i]=b[i];return h};b.domain.apply(b,arguments);return b}; +pv.Scale.quantile=function(){function b(i){return h(Math.max(0,Math.min(d,pv.search.index(f,i)-1))/d)}var c=-1,d=-1,f=[],g=[],h=pv.Scale.linear();b.quantiles=function(i){if(arguments.length){c=Number(i);if(c<0){f=[g[0]].concat(g);d=g.length-1}else{f=[];f[0]=g[0];for(var j=1;j<=c;j++)f[j]=g[~~(j*(g.length-1)/c)];d=c-1}return this}return f};b.domain=function(i,j){if(arguments.length){g=i instanceof Array?pv.map(i,j):Array.prototype.slice.call(arguments);g.sort(pv.naturalOrder);b.quantiles(c);return this}return g}; +b.range=function(){if(arguments.length){h.range.apply(h,arguments);return this}return h.range()};b.by=function(i){function j(){return b(i.apply(this,arguments))}for(var k in b)j[k]=b[k];return j};b.domain.apply(b,arguments);return b}; +pv.histogram=function(b,c){var d=true;return{bins:function(f){var g=pv.map(b,c),h=[];arguments.length||(f=pv.Scale.linear(g).ticks());for(var i=0;i360)j-=360;else if(j<0)j+=360;if(j<60)return i+(h-i)*j/60;if(j<180)return h;if(j<240)return i+(h-i)*(240-j)/60;return i}function c(j){return Math.round(b(j)*255)}var d=this.h,f=this.s,g=this.l;d%=360;if(d<0)d+=360;f=Math.max(0,Math.min(f,1));g=Math.max(0,Math.min(g,1));var h=g<=0.5?g*(1+f):g+f-g*f,i=2*g-h;return pv.rgb(c(d+120),c(d),c(d-120),this.a)}; +pv.Color.names={aliceblue:"#f0f8ff",antiquewhite:"#faebd7",aqua:"#00ffff",aquamarine:"#7fffd4",azure:"#f0ffff",beige:"#f5f5dc",bisque:"#ffe4c4",black:"#000000",blanchedalmond:"#ffebcd",blue:"#0000ff",blueviolet:"#8a2be2",brown:"#a52a2a",burlywood:"#deb887",cadetblue:"#5f9ea0",chartreuse:"#7fff00",chocolate:"#d2691e",coral:"#ff7f50",cornflowerblue:"#6495ed",cornsilk:"#fff8dc",crimson:"#dc143c",cyan:"#00ffff",darkblue:"#00008b",darkcyan:"#008b8b",darkgoldenrod:"#b8860b",darkgray:"#a9a9a9",darkgreen:"#006400", +darkgrey:"#a9a9a9",darkkhaki:"#bdb76b",darkmagenta:"#8b008b",darkolivegreen:"#556b2f",darkorange:"#ff8c00",darkorchid:"#9932cc",darkred:"#8b0000",darksalmon:"#e9967a",darkseagreen:"#8fbc8f",darkslateblue:"#483d8b",darkslategray:"#2f4f4f",darkslategrey:"#2f4f4f",darkturquoise:"#00ced1",darkviolet:"#9400d3",deeppink:"#ff1493",deepskyblue:"#00bfff",dimgray:"#696969",dimgrey:"#696969",dodgerblue:"#1e90ff",firebrick:"#b22222",floralwhite:"#fffaf0",forestgreen:"#228b22",fuchsia:"#ff00ff",gainsboro:"#dcdcdc", +ghostwhite:"#f8f8ff",gold:"#ffd700",goldenrod:"#daa520",gray:"#808080",green:"#008000",greenyellow:"#adff2f",grey:"#808080",honeydew:"#f0fff0",hotpink:"#ff69b4",indianred:"#cd5c5c",indigo:"#4b0082",ivory:"#fffff0",khaki:"#f0e68c",lavender:"#e6e6fa",lavenderblush:"#fff0f5",lawngreen:"#7cfc00",lemonchiffon:"#fffacd",lightblue:"#add8e6",lightcoral:"#f08080",lightcyan:"#e0ffff",lightgoldenrodyellow:"#fafad2",lightgray:"#d3d3d3",lightgreen:"#90ee90",lightgrey:"#d3d3d3",lightpink:"#ffb6c1",lightsalmon:"#ffa07a", +lightseagreen:"#20b2aa",lightskyblue:"#87cefa",lightslategray:"#778899",lightslategrey:"#778899",lightsteelblue:"#b0c4de",lightyellow:"#ffffe0",lime:"#00ff00",limegreen:"#32cd32",linen:"#faf0e6",magenta:"#ff00ff",maroon:"#800000",mediumaquamarine:"#66cdaa",mediumblue:"#0000cd",mediumorchid:"#ba55d3",mediumpurple:"#9370db",mediumseagreen:"#3cb371",mediumslateblue:"#7b68ee",mediumspringgreen:"#00fa9a",mediumturquoise:"#48d1cc",mediumvioletred:"#c71585",midnightblue:"#191970",mintcream:"#f5fffa",mistyrose:"#ffe4e1", +moccasin:"#ffe4b5",navajowhite:"#ffdead",navy:"#000080",oldlace:"#fdf5e6",olive:"#808000",olivedrab:"#6b8e23",orange:"#ffa500",orangered:"#ff4500",orchid:"#da70d6",palegoldenrod:"#eee8aa",palegreen:"#98fb98",paleturquoise:"#afeeee",palevioletred:"#db7093",papayawhip:"#ffefd5",peachpuff:"#ffdab9",peru:"#cd853f",pink:"#ffc0cb",plum:"#dda0dd",powderblue:"#b0e0e6",purple:"#800080",red:"#ff0000",rosybrown:"#bc8f8f",royalblue:"#4169e1",saddlebrown:"#8b4513",salmon:"#fa8072",sandybrown:"#f4a460",seagreen:"#2e8b57", +seashell:"#fff5ee",sienna:"#a0522d",silver:"#c0c0c0",skyblue:"#87ceeb",slateblue:"#6a5acd",slategray:"#708090",slategrey:"#708090",snow:"#fffafa",springgreen:"#00ff7f",steelblue:"#4682b4",tan:"#d2b48c",teal:"#008080",thistle:"#d8bfd8",tomato:"#ff6347",turquoise:"#40e0d0",violet:"#ee82ee",wheat:"#f5deb3",white:"#ffffff",whitesmoke:"#f5f5f5",yellow:"#ffff00",yellowgreen:"#9acd32",transparent:pv.Color.transparent=pv.rgb(0,0,0,0)};(function(){var b=pv.Color.names;for(var c in b)b[c]=pv.color(b[c])})(); +pv.colors=function(){var b=pv.Scale.ordinal();b.range.apply(b,arguments);return b};pv.Colors={};pv.Colors.category10=function(){var b=pv.colors("#1f77b4","#ff7f0e","#2ca02c","#d62728","#9467bd","#8c564b","#e377c2","#7f7f7f","#bcbd22","#17becf");b.domain.apply(b,arguments);return b}; +pv.Colors.category20=function(){var b=pv.colors("#1f77b4","#aec7e8","#ff7f0e","#ffbb78","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5","#8c564b","#c49c94","#e377c2","#f7b6d2","#7f7f7f","#c7c7c7","#bcbd22","#dbdb8d","#17becf","#9edae5");b.domain.apply(b,arguments);return b}; +pv.Colors.category19=function(){var b=pv.colors("#9c9ede","#7375b5","#4a5584","#cedb9c","#b5cf6b","#8ca252","#637939","#e7cb94","#e7ba52","#bd9e39","#8c6d31","#e7969c","#d6616b","#ad494a","#843c39","#de9ed6","#ce6dbd","#a55194","#7b4173");b.domain.apply(b,arguments);return b};pv.ramp=function(){var b=pv.Scale.linear();b.range.apply(b,arguments);return b}; +pv.Scene=pv.SvgScene={svg:"http://www.w3.org/2000/svg",xmlns:"http://www.w3.org/2000/xmlns",xlink:"http://www.w3.org/1999/xlink",xhtml:"http://www.w3.org/1999/xhtml",scale:1,events:["DOMMouseScroll","mousewheel","mousedown","mouseup","mouseover","mouseout","mousemove","click","dblclick"],implicit:{svg:{"shape-rendering":"auto","pointer-events":"painted",x:0,y:0,dy:0,"text-anchor":"start",transform:"translate(0,0)",fill:"none","fill-opacity":1,stroke:"none","stroke-opacity":1,"stroke-width":1.5,"stroke-linejoin":"miter"}, +css:{font:"10px sans-serif"}}};pv.SvgScene.updateAll=function(b){if(b.length&&b[0].reverse&&b.type!="line"&&b.type!="area"){for(var c=pv.extend(b),d=0,f=b.length-1;f>=0;d++,f--)c[d]=b[f];b=c}this.removeSiblings(this[b.type](b))};pv.SvgScene.create=function(b){return document.createElementNS(this.svg,b)}; +pv.SvgScene.expect=function(b,c,d,f){if(b){if(b.tagName=="a")b=b.firstChild;if(b.tagName!=c){c=this.create(c);b.parentNode.replaceChild(c,b);b=c}}else b=this.create(c);for(var g in d){c=d[g];if(c==this.implicit.svg[g])c=null;c==null?b.removeAttribute(g):b.setAttribute(g,c)}for(g in f){c=f[g];if(c==this.implicit.css[g])c=null;if(c==null)b.style.removeProperty(g);else b.style[g]=c}return b}; +pv.SvgScene.append=function(b,c,d){b.$scene={scenes:c,index:d};b=this.title(b,c[d]);b.parentNode||c.$g.appendChild(b);return b.nextSibling};pv.SvgScene.title=function(b,c){var d=b.parentNode;if(d&&d.tagName!="a")d=null;if(c.title){if(!d){d=this.create("a");b.parentNode&&b.parentNode.replaceChild(d,b);d.appendChild(b)}d.setAttributeNS(this.xlink,"title",c.title);return d}d&&d.parentNode.replaceChild(b,d);return b}; +pv.SvgScene.dispatch=pv.listener(function(b){var c=b.target.$scene;if(c){var d=b.type;switch(d){case "DOMMouseScroll":d="mousewheel";b.wheel=-480*b.detail;break;case "mousewheel":b.wheel=(window.opera?12:1)*b.wheelDelta;break}pv.Mark.dispatch(d,c.scenes,c.index)&&b.preventDefault()}});pv.SvgScene.removeSiblings=function(b){for(;b;){var c=b.nextSibling;b.parentNode.removeChild(b);b=c}};pv.SvgScene.undefined=function(){}; +pv.SvgScene.pathBasis=function(){function b(f,g,h,i,j){return{x:f[0]*g.left+f[1]*h.left+f[2]*i.left+f[3]*j.left,y:f[0]*g.top+f[1]*h.top+f[2]*i.top+f[3]*j.top}}var c=[[1/6,2/3,1/6,0],[0,2/3,1/3,0],[0,1/3,2/3,0],[0,1/6,2/3,1/6]],d=function(f,g,h,i){var j=b(c[1],f,g,h,i),k=b(c[2],f,g,h,i);f=b(c[3],f,g,h,i);return"C"+j.x+","+j.y+","+k.x+","+k.y+","+f.x+","+f.y};d.segment=function(f,g,h,i){var j=b(c[0],f,g,h,i),k=b(c[1],f,g,h,i),l=b(c[2],f,g,h,i);f=b(c[3],f,g,h,i);return"M"+j.x+","+j.y+"C"+k.x+","+k.y+ +","+l.x+","+l.y+","+f.x+","+f.y};return d}();pv.SvgScene.curveBasis=function(b){if(b.length<=2)return"";var c="",d=b[0],f=d,g=d,h=b[1];c+=this.pathBasis(d,f,g,h);for(var i=2;i1){j=c[1];h=b[k];k++;f+="C"+(g.left+i.x)+","+(g.top+i.y)+","+(h.left-j.x)+","+(h.top-j.y)+","+h.left+","+h.top;for(g=2;g9){k=3/Math.sqrt(k);f[h]= +k*i*d[h];f[h+1]=k*j*d[h]}}for(h=0;h2&&(g.interpolate=="basis"||g.interpolate=="cardinal"||g.interpolate=="monotone")?d:c)(l,q-1));l=q-1}}if(!j.length)return f;f=this.expect(f,"path",{"shape-rendering":g.antialias?null:"crispEdges","pointer-events":g.events,cursor:g.cursor,d:"M"+j.join("ZM")+"Z",fill:h.color,"fill-opacity":h.opacity|| +null,stroke:i.color,"stroke-opacity":i.opacity||null,"stroke-width":i.opacity?g.lineWidth/this.scale:null});return this.append(f,b,0)}; +pv.SvgScene.areaSegment=function(b){var c=b.$g.firstChild,d=b[0],f,g;if(d.interpolate=="basis"||d.interpolate=="cardinal"||d.interpolate=="monotone"){f=[];g=[];for(var h=0,i=b.length;h2&&(d.interpolate=="basis"||d.interpolate=="cardinal"||d.interpolate=="monotone"))switch(d.interpolate){case "basis":h+=this.curveBasis(b);break;case "cardinal":h+=this.curveCardinal(b,d.tension);break;case "monotone":h+=this.curveMonotone(b); +break}else for(var i=1;i1)break;return"A"+f+","+f+" 0 0,"+d+" "+c.left+","+c.top;case "step-before":return"V"+c.top+"H"+c.left;case "step-after":return"H"+c.left+"V"+c.top}return"L"+c.left+","+c.top};pv.SvgScene.lineIntersect=function(b,c,d,f){return b.plus(c.times(d.minus(b).dot(f.perp())/c.dot(f.perp())))}; +pv.SvgScene.pathJoin=function(b,c,d,f){var g=pv.vector(c.left,c.top);d=pv.vector(d.left,d.top);var h=d.minus(g),i=h.perp().norm(),j=i.times(c.lineWidth/(2*this.scale));c=g.plus(j);var k=d.plus(j),l=d.minus(j);j=g.minus(j);if(b&&b.visible){b=g.minus(b.left,b.top).perp().norm().plus(i);j=this.lineIntersect(g,b,j,h);c=this.lineIntersect(g,b,c,h)}if(f&&f.visible){f=pv.vector(f.left,f.top).minus(d).perp().norm().plus(i);l=this.lineIntersect(d,f,l,h);k=this.lineIntersect(d,f,k,h)}return"M"+c.x+","+c.y+ +"L"+k.x+","+k.y+" "+l.x+","+l.y+" "+j.x+","+j.y}; +pv.SvgScene.panel=function(b){for(var c=b.$g,d=c&&c.firstChild,f=0;f=2*Math.PI)i=i?"M0,"+j+"A"+j+","+j+" 0 1,1 0,"+-j+"A"+j+","+j+" 0 1,1 0,"+j+"M0,"+i+"A"+i+","+i+" 0 1,1 0,"+-i+"A"+i+","+i+" 0 1,1 0,"+i+"Z":"M0,"+j+"A"+j+","+j+" 0 1,1 0,"+-j+"A"+j+","+j+" 0 1,1 0,"+j+"Z";else{var l=Math.min(f.startAngle,f.endAngle),q=Math.max(f.startAngle,f.endAngle), +n=Math.cos(l),p=Math.cos(q);l=Math.sin(l);q=Math.sin(q);i=i?"M"+j*n+","+j*l+"A"+j+","+j+" 0 "+(k1?c:null)}; +a.anchor=function(b){b||(b="center");return(new pv.Anchor(this)).name(b).data(function(){return this.scene.target.map(function(c){return c.data})}).visible(function(){return this.scene.target[this.index].visible}).left(function(){var c=this.scene.target[this.index],d=c.width||0;switch(this.name()){case "bottom":case "top":case "center":return c.left+d/2;case "left":return null}return c.left+d}).top(function(){var c=this.scene.target[this.index],d=c.height||0;switch(this.name()){case "left":case "right":case "center":return c.top+ +d/2;case "top":return null}return c.top+d}).right(function(){var c=this.scene.target[this.index];return this.name()=="left"?c.right+(c.width||0):null}).bottom(function(){var c=this.scene.target[this.index];return this.name()=="top"?c.bottom+(c.height||0):null}).textAlign(function(){switch(this.name()){case "bottom":case "top":case "center":return"center";case "right":return"right"}return"left"}).textBaseline(function(){switch(this.name()){case "right":case "left":case "center":return"middle";case "top":return"top"}return"bottom"})}; +a.anchorTarget=function(){return this.target};a.margin=function(b){return this.left(b).right(b).top(b).bottom(b)};a.instance=function(b){var c=this.scene||this.parent.instance(-1).children[this.childIndex],d=!arguments.length||this.hasOwnProperty("index")?this.index:b;return c[d<0?c.length-1:d]}; +a.instances=function(b){for(var c=this,d=[],f;!(f=c.scene);){b=b.parent;d.push({index:b.index,childIndex:c.childIndex});c=c.parent}for(;d.length;){b=d.pop();f=f[b.index].children[b.childIndex]}if(this.hasOwnProperty("index")){d=pv.extend(f[this.index]);d.right=d.top=d.left=d.bottom=0;return[d]}return f};a.first=function(){return this.scene[0]};a.last=function(){return this.scene[this.scene.length-1]};a.sibling=function(){return this.index==0?null:this.scene[this.index-1]}; +a.cousin=function(){var b=this.parent;return(b=b&&b.sibling())&&b.children?b.children[this.childIndex][this.index]:null}; +a.render=function(){function b(i,j,k){i.scale=k;if(j=0;l--){var q=k[l];if(!(q.name in c)){c[q.name]=q;switch(q.name){case "data":f=q;break;case "visible":g=q;break;default:d[q.type].push(q);break}}}while(j=j.proto)}var c={},d=[[],[],[],[]],f,g;b(this);b(this.defaults);d[1].reverse();d[3].reverse();var h=this;do for(var i in h.properties)i in c||d[2].push(c[i]={name:i,type:2,value:null});while(h=h.proto);h=d[0].concat(d[1]);for(i=0;ih.id)d[g.name]={id:0,value:g.type&1?g.value.apply(this,c):g.value}}}d=this.binds.data;d=d.type& +1?d.value.apply(this,c):d.value;c.unshift(null);b.length=d.length;for(f=0;f0;l--){p=m[l];p.scale=q;q*=p.scene[p.index].transform.k}if(n.children){l=0;for(m=n.children.length;l=3*Math.PI/2};pv.Wedge.prototype.buildImplied=function(b){if(b.angle==null)b.angle=b.endAngle-b.startAngle;else if(b.endAngle==null)b.endAngle=b.startAngle+b.angle;pv.Mark.prototype.buildImplied.call(this,b)};pv.simulation=function(b){return new pv.Simulation(b)};pv.Simulation=function(b){for(var c=0;c=s,u=q.y>=t;l.leaf=false;switch((u<<1)+x){case 0:l=l.c1||(l.c1=new pv.Quadtree.Node);break;case 1:l=l.c2||(l.c2=new pv.Quadtree.Node);break;case 2:l=l.c3||(l.c3=new pv.Quadtree.Node);break;case 3:l=l.c4||(l.c4=new pv.Quadtree.Node); +break}if(x)n=s;else m=s;if(u)p=t;else r=t;c(l,q,n,p,m,r)}var f,g=Number.POSITIVE_INFINITY,h=g,i=Number.NEGATIVE_INFINITY,j=i;for(f=b;f;f=f.next){if(f.xi)i=f.x;if(f.y>j)j=f.y}f=i-g;var k=j-h;if(f>k)j=h+f;else i=g+k;this.xMin=g;this.yMin=h;this.xMax=i;this.yMax=j;this.root=new pv.Quadtree.Node;for(f=b;f;f=f.next)c(this.root,f,g,h,i,j)};pv.Quadtree.Node=function(){this.leaf=true;this.p=this.c4=this.c3=this.c2=this.c1=null};pv.Force={}; +pv.Force.charge=function(b){function c(l){function q(m){c(m);l.cn+=m.cn;n+=m.cn*m.cx;p+=m.cn*m.cy}var n=0,p=0;l.cn=0;if(!l.leaf){l.c1&&q(l.c1);l.c2&&q(l.c2);l.c3&&q(l.c3);l.c4&&q(l.c4)}if(l.p){l.cn+=b;n+=b*l.p.x;p+=b*l.p.y}l.cx=n/l.cn;l.cy=p/l.cn}function d(l,q,n,p,m,r){var s=l.cx-q.x,t=l.cy-q.y,x=1/Math.sqrt(s*s+t*t);if(l.leaf&&l.p!=q||(m-n)*xg)x=g;l=l.cn*x*x*x;s=s*l;t=t*l;q.fx+=s;q.fy+=t}}else if(!l.leaf){var u=(n+m)*0.5,o=(p+r)*0.5;l.c1&&d(l.c1,q,n,p,u,o);l.c2&&d(l.c2,q,u,p, +m,o);l.c3&&d(l.c3,q,n,o,u,r);l.c4&&d(l.c4,q,u,o,m,r);if(!(xg)x=g;if(l.p&&l.p!=q){l=b*x*x*x;s=s*l;t=t*l;q.fx+=s;q.fy+=t}}}}var f=2,g=1/f,h=500,i=1/h,j=0.9,k={};arguments.length||(b=-40);k.constant=function(l){if(arguments.length){b=Number(l);return k}return b};k.domain=function(l,q){if(arguments.length){f=Number(l);g=1/f;h=Number(q);i=1/h;return k}return[f,h]};k.theta=function(l){if(arguments.length){j=Number(l);return k}return j};k.apply=function(l,q){c(q.root);for(l=l;l;l=l.next)d(q.root, +l,q.xMin,q.yMin,q.xMax,q.yMax)};return k};pv.Force.drag=function(b){var c={};arguments.length||(b=0.1);c.constant=function(d){if(arguments.length){b=d;return c}return b};c.apply=function(d){if(b)for(d=d;d;d=d.next){d.fx-=b*d.vx;d.fy-=b*d.vy}};return c}; +pv.Force.spring=function(b){var c=0.1,d=20,f,g,h={};arguments.length||(b=0.1);h.links=function(i){if(arguments.length){f=i;g=i.map(function(j){return 1/Math.sqrt(Math.max(j.sourceNode.linkDegree,j.targetNode.linkDegree))});return h}return f};h.constant=function(i){if(arguments.length){b=Number(i);return h}return b};h.damping=function(i){if(arguments.length){c=Number(i);return h}return c};h.length=function(i){if(arguments.length){d=Number(i);return h}return d};h.apply=function(){for(var i=0;ig,o=sh){l.c1&&u&&c(l.c1,q,n,p,s,t);l.c2&&o&&c(l.c2,q,s,p,m,t)}if(x){l.c3&&u&&c(l.c3,q,n,t,s,r);l.c4&&o&&c(l.c4,q,s,t,m,r)}}if(l.p&&l.p!=q){n=q.x-l.p.x;p=q.y-l.p.y;m=Math.sqrt(n*n+p*p);r=f+b(l.p);if(mm)m=p}for(var r=0;rc.max?c.max:g.x;if(d)for(g=f;g;g=g.next)g.y=g.yd.max?d.max:g.y};return b};pv.Layout=function(){pv.Panel.call(this)};pv.Layout.prototype=pv.extend(pv.Panel); +pv.Layout.prototype.property=function(b,c){if(!this.hasOwnProperty("properties"))this.properties=pv.extend(this.properties);this.properties[b]=true;this.propertyMethod(b,false,pv.Mark.cast[b]=c);return this}; +pv.Layout.Network=function(){pv.Layout.call(this);var b=this;this.$id=pv.id();(this.node=(new pv.Mark).data(function(){return b.nodes()}).strokeStyle("#1f77b4").fillStyle("#fff").left(function(c){return c.x}).top(function(c){return c.y})).parent=this;this.link=(new pv.Mark).extend(this.node).data(function(c){return[c.sourceNode,c.targetNode]}).fillStyle(null).lineWidth(function(c,d){return d.linkValue*1.5}).strokeStyle("rgba(0,0,0,.2)");this.link.add=function(c){return b.add(pv.Panel).data(function(){return b.links()}).add(c).extend(this)}; +(this.label=(new pv.Mark).extend(this.node).textMargin(7).textBaseline("middle").text(function(c){return c.nodeName||c.nodeValue}).textAngle(function(c){c=c.midAngle;return pv.Wedge.upright(c)?c:c+Math.PI}).textAlign(function(c){return pv.Wedge.upright(c.midAngle)?"left":"right"})).parent=this}; +pv.Layout.Network.prototype=pv.extend(pv.Layout).property("nodes",function(b){return b.map(function(c,d){if(typeof c!="object")c={nodeValue:c};c.index=d;return c})}).property("links",function(b){return b.map(function(c){if(isNaN(c.linkValue))c.linkValue=isNaN(c.value)?1:c.value;return c})});pv.Layout.Network.prototype.reset=function(){this.$id=pv.id();return this};pv.Layout.Network.prototype.buildProperties=function(b,c){if((b.$id||0)=this.$id)return true;b.$id=this.$id;b.nodes.forEach(function(c){c.linkDegree=0});b.links.forEach(function(c){var d=c.linkValue;(c.sourceNode||(c.sourceNode=b.nodes[c.source])).linkDegree+=d;(c.targetNode||(c.targetNode=b.nodes[c.target])).linkDegree+=d})};pv.Layout.Hierarchy=function(){pv.Layout.Network.call(this);this.link.strokeStyle("#ccc")};pv.Layout.Hierarchy.prototype=pv.extend(pv.Layout.Network); +pv.Layout.Hierarchy.prototype.buildImplied=function(b){if(!b.links)b.links=pv.Layout.Hierarchy.links.call(this);pv.Layout.Network.prototype.buildImplied.call(this,b)};pv.Layout.Hierarchy.links=function(){return this.nodes().filter(function(b){return b.parentNode}).map(function(b){return{sourceNode:b,targetNode:b.parentNode,linkValue:1}})}; +pv.Layout.Hierarchy.NodeLink={buildImplied:function(b){function c(m){return m.parentNode?m.depth*(n-q)+q:0}function d(m){return m.parentNode?(m.breadth-0.25)*2*Math.PI:0}function f(m){switch(i){case "left":return m.depth*k;case "right":return k-m.depth*k;case "top":return m.breadth*k;case "bottom":return k-m.breadth*k;case "radial":return k/2+c(m)*Math.cos(m.midAngle)}}function g(m){switch(i){case "left":return m.breadth*l;case "right":return l-m.breadth*l;case "top":return m.depth*l;case "bottom":return l- +m.depth*l;case "radial":return l/2+c(m)*Math.sin(m.midAngle)}}var h=b.nodes,i=b.orient,j=/^(top|bottom)$/.test(i),k=b.width,l=b.height;if(i=="radial"){var q=b.innerRadius,n=b.outerRadius;if(q==null)q=0;if(n==null)n=Math.min(k,l)/2}for(b=0;bb.dy?0:-Math.PI/2});(this.leaf=(new pv.Mark).extend(this.node).fillStyle(null).strokeStyle(null).visible(function(b){return!b.firstChild})).parent= +this;delete this.link};pv.Layout.Treemap.prototype=pv.extend(pv.Layout.Hierarchy).property("round",Boolean).property("paddingLeft",Number).property("paddingRight",Number).property("paddingTop",Number).property("paddingBottom",Number).property("mode",String).property("order",String);a=pv.Layout.Treemap.prototype;a.defaults=(new pv.Layout.Treemap).extend(pv.Layout.Hierarchy.prototype.defaults).mode("squarify").order("ascending");a.padding=function(b){return this.paddingLeft(b).paddingRight(b).paddingTop(b).paddingBottom(b)}; +a.$size=function(b){return Number(b.nodeValue)};a.size=function(b){this.$size=pv.functor(b);return this}; +a.buildImplied=function(b){function c(r,s,t,x,u,o,v){for(var w=0,y=0;wt)t=v;u+=v}u*=u;s*=s;return Math.max(s*t/u,u/(s*x))}function f(r,s){function t(A){var D=o==y,G=pv.sum(A,n),E=y?p(G/y):0;c(A,G,D,x,u,D?o:E,D?E:v);if(D){u+=E;v-=E}else{x+= +E;o-=E}y=Math.min(o,v);return D}var x=r.x+j,u=r.y+l,o=r.dx-j-k,v=r.dy-l-q;if(m!="squarify")c(r.childNodes,r.size,m=="slice"?true:m=="dice"?false:s&1,x,u,o,v);else{var w=[];s=Infinity;var y=Math.min(o,v),z=o*v/r.size;if(!(r.size<=0)){r.visitBefore(function(A){A.size*=z});for(r=r.childNodes.slice();r.length;){var C=r[r.length-1];if(C.size){w.push(C);z=d(w,y);if(z<=s){r.pop();s=z}else{w.pop();t(w);w.length=0;s=Infinity}}else r.pop()}if(t(w))for(s=0;s0){i(k(C,o,v),o,B);A+=B;D+=B}G+=C.mod;A+=y.mod;E+=w.mod;D+=z.mod;C=h(C);y=g(y)}if(C&&!h(z)){z.thread=C;z.mod+=G-D}if(y&&!g(w)){w.thread=y;w.mod+=A-E;v=o}}return v}function g(o){return o.firstChild||o.thread}function h(o){return o.lastChild||o.thread}function i(o,v,w){var y=v.number-o.number;v.change-=w/y;v.shift+=w;o.change+= +w/y;v.prelim+=w;v.mod+=w}function j(o){var v=0,w=0;for(o=o.lastChild;o;o=o.previousSibling){o.prelim+=v;o.mod+=v;w+=o.change;v+=o.shift+w}}function k(o,v,w){return o.ancestor.parentNode==v.parentNode?o.ancestor:w}function l(o,v){return(v?1:t+1)/(m=="radial"?o:1)}function q(o){return m=="radial"?o.breadth/r:0}function n(o){switch(m){case "left":return o.depth;case "right":return x-o.depth;case "top":case "bottom":return o.breadth+x/2;case "radial":return x/2+o.depth*Math.cos(q(o))}}function p(o){switch(m){case "left":case "right":return o.breadth+ +u/2;case "top":return o.depth;case "bottom":return u-o.depth;case "radial":return u/2+o.depth*Math.sin(q(o))}}if(!pv.Layout.Hierarchy.prototype.buildImplied.call(this,b)){var m=b.orient,r=b.depth,s=b.breadth,t=b.group,x=b.width,u=b.height;b=b.nodes[0];b.visitAfter(function(o,v){o.ancestor=o;o.prelim=0;o.mod=0;o.change=0;o.shift=0;o.number=o.previousSibling?o.previousSibling.number+1:0;o.depth=v});c(b);d(b,-b.prelim,0);b.visitAfter(function(o){o.breadth*=s;o.depth*=r;o.midAngle=q(o);o.x=n(o);o.y=p(o); +if(o.firstChild)o.midAngle+=Math.PI;delete o.breadth;delete o.depth;delete o.ancestor;delete o.prelim;delete o.mod;delete o.change;delete o.shift;delete o.number;delete o.thread})}};pv.Layout.Indent=function(){pv.Layout.Hierarchy.call(this);this.link.interpolate("step-after")};pv.Layout.Indent.prototype=pv.extend(pv.Layout.Hierarchy).property("depth",Number).property("breadth",Number);pv.Layout.Indent.prototype.defaults=(new pv.Layout.Indent).extend(pv.Layout.Hierarchy.prototype.defaults).depth(15).breadth(15); +pv.Layout.Indent.prototype.buildImplied=function(b){function c(i,j,k){i.x=g+k++*f;i.y=h+j++*d;i.midAngle=0;for(i=i.firstChild;i;i=i.nextSibling)j=c(i,j,k);return j}if(!pv.Layout.Hierarchy.prototype.buildImplied.call(this,b)){var d=b.breadth,f=b.depth,g=0,h=0;c(b.nodes[0],1,1)}};pv.Layout.Pack=function(){pv.Layout.Hierarchy.call(this);this.node.radius(function(b){return b.radius}).strokeStyle("rgb(31, 119, 180)").fillStyle("rgba(31, 119, 180, .25)");this.label.textAlign("center");delete this.link}; +pv.Layout.Pack.prototype=pv.extend(pv.Layout.Hierarchy).property("spacing",Number).property("order",String);pv.Layout.Pack.prototype.defaults=(new pv.Layout.Pack).extend(pv.Layout.Hierarchy.prototype.defaults).spacing(1).order("ascending");pv.Layout.Pack.prototype.$radius=function(){return 1};pv.Layout.Pack.prototype.size=function(b){this.$radius=typeof b=="function"?function(){return Math.sqrt(b.apply(this,arguments))}:(b=Math.sqrt(b),function(){return b});return this}; +pv.Layout.Pack.prototype.buildImplied=function(b){function c(n){var p=pv.Mark.stack;p.unshift(null);for(var m=0,r=n.length;m0.0010}var t=Infinity,x=-Infinity,u=Infinity,o=-Infinity,v,w,y,z,C;v=n[0];v.x=-v.radius;v.y=0;p(v);if(n.length>1){w=n[1];w.x=w.radius;w.y=0;p(w);if(n.length>2){y=n[2];g(v,w,y);p(y);m(v,y);v.p= +y;m(y,w);w=v.n;for(var A=3;A0){r(v,z);w=z;A--}else if(D<0){r(z,w);v=z;A--}}}}v=(t+x)/2;w=(u+o)/2;for(A=y=0;An.min){n.sim.step(); +q=true}q&&d.render()},42)}else for(k=0;kg)g=j;i.size=i.firstChild?pv.sum(i.childNodes,function(k){return k.size}):c.$size.apply(c,(f[0]=i,f))});f.shift();switch(b.order){case "ascending":d.sort(function(i,j){return i.size-j.size});break;case "descending":d.sort(function(i,j){return j.size-i.size});break}var h=1/g;d.minBreadth=0;d.breadth= +0.5;d.maxBreadth=1;d.visitBefore(function(i){for(var j=i.minBreadth,k=i.maxBreadth-j,l=i.firstChild;l;l=l.nextSibling){l.minBreadth=j;l.maxBreadth=j+=l.size/i.size*k;l.breadth=(j+l.minBreadth)/2}});d.visitAfter(function(i,j){i.minDepth=(j-1)*h;i.maxDepth=i.depth=j*h});pv.Layout.Hierarchy.NodeLink.buildImplied.call(this,b)}};pv.Layout.Partition.Fill=function(){pv.Layout.Partition.call(this);pv.Layout.Hierarchy.Fill.constructor.call(this)};pv.Layout.Partition.Fill.prototype=pv.extend(pv.Layout.Partition); +pv.Layout.Partition.Fill.prototype.buildImplied=function(b){pv.Layout.Partition.prototype.buildImplied.call(this,b)||pv.Layout.Hierarchy.Fill.buildImplied.call(this,b)};pv.Layout.Arc=function(){pv.Layout.Network.call(this);var b,c,d,f=this.buildImplied;this.buildImplied=function(g){f.call(this,g);c=g.directed;b=g.orient=="radial"?"linear":"polar";d=g.orient=="right"||g.orient=="top"};this.link.data(function(g){var h=g.sourceNode;g=g.targetNode;return d!=(c||h.breadth>1)*f:null}).bottom(function(k,l){return d=="mirror"?l&1?null:(l+1>>1)*-f:(l&1||-1)*(l+1>>1)*f}).fillStyle(function(k,l){return(l&1?h:i)((l>>1)+1)});this.band.add=function(k){return b.add(pv.Panel).extend(c).add(k).extend(this)}};pv.Layout.Horizon.prototype=pv.extend(pv.Layout).property("bands",Number).property("mode",String).property("backgroundStyle",pv.color).property("positiveStyle",pv.color).property("negativeStyle",pv.color); +pv.Layout.Horizon.prototype.defaults=(new pv.Layout.Horizon).extend(pv.Layout.prototype.defaults).bands(2).mode("offset").backgroundStyle("white").positiveStyle("#1f77b4").negativeStyle("#d62728"); +pv.Layout.Rollup=function(){pv.Layout.Network.call(this);var b=this,c,d,f=b.buildImplied;this.buildImplied=function(g){f.call(this,g);c=g.$rollup.nodes;d=g.$rollup.links};this.node.data(function(){return c}).size(function(g){return g.nodes.length*20});this.link.interpolate("polar").eccentricity(0.8);this.link.add=function(g){return b.add(pv.Panel).data(function(){return d}).add(g).extend(this)}};pv.Layout.Rollup.prototype=pv.extend(pv.Layout.Network).property("directed",Boolean); +pv.Layout.Rollup.prototype.x=function(b){this.$x=pv.functor(b);return this};pv.Layout.Rollup.prototype.y=function(b){this.$y=pv.functor(b);return this}; +pv.Layout.Rollup.prototype.buildImplied=function(b){function c(r){return i[r]+","+j[r]}if(!pv.Layout.Network.prototype.buildImplied.call(this,b)){var d=b.nodes,f=b.links,g=b.directed,h=d.length,i=[],j=[],k=0,l={},q={},n=pv.Mark.stack,p={parent:this};n.unshift(null);for(var m=0;mk.index?k.index+","+d.index:d.index+","+k.index;(n=q[h])||(n=q[h]={sourceNode:d,targetNode:k,linkValue:0,links:[]});n.links.push(f[m]);n.linkValue+=f[m].linkValue}b.$rollup={nodes:pv.values(l),links:pv.values(q)}}}; +pv.Layout.Matrix=function(){pv.Layout.Network.call(this);var b,c,d,f,g,h=this.buildImplied;this.buildImplied=function(i){h.call(this,i);b=i.nodes.length;c=i.width/b;d=i.height/b;f=i.$matrix.labels;g=i.$matrix.pairs};this.link.data(function(){return g}).left(function(){return c*(this.index%b)}).top(function(){return d*Math.floor(this.index/b)}).width(function(){return c}).height(function(){return d}).lineWidth(1.5).strokeStyle("#fff").fillStyle(function(i){return i.linkValue?"#555":"#eee"}).parent= +this;delete this.link.add;this.label.data(function(){return f}).left(function(){return this.index&1?c*((this.index>>1)+0.5):0}).top(function(){return this.index&1?0:d*((this.index>>1)+0.5)}).textMargin(4).textAlign(function(){return this.index&1?"left":"right"}).textAngle(function(){return this.index&1?-Math.PI/2:0});delete this.node};pv.Layout.Matrix.prototype=pv.extend(pv.Layout.Network).property("directed",Boolean);pv.Layout.Matrix.prototype.sort=function(b){this.$sort=b;return this}; +pv.Layout.Matrix.prototype.buildImplied=function(b){if(!pv.Layout.Network.prototype.buildImplied.call(this,b)){var c=b.nodes,d=b.links,f=this.$sort,g=c.length,h=pv.range(g),i=[],j=[],k={};b.$matrix={labels:i,pairs:j};f&&h.sort(function(m,r){return f(c[m],c[r])});for(var l=0;lk)l=null;if(g){if(l&&g.scene==l.scene&&g.index==l.index)return;pv.Mark.dispatch("unpoint",g.scene,g.index)}if(g=l){pv.Mark.dispatch("point",l.scene,l.index);pv.listen(this.root.canvas(),"mouseout",f)}}function f(l){if(g&&!pv.ancestor(this,l.relatedTarget)){pv.Mark.dispatch("unpoint",g.scene,g.index);g=null}}var g,h=null,i=1,j=1,k=arguments.length?b*b:900;d.collapse=function(l){if(arguments.length){h=String(l);switch(h){case "y":i= +1;j=0;break;case "x":i=0;j=1;break;default:j=i=1;break}return d}return h};return d}; +pv.Behavior.select=function(){function b(j){g=this.index;f=this.scene;i=this.mouse();h=j;h.x=i.x;h.y=i.y;h.dx=h.dy=0;pv.Mark.dispatch("selectstart",f,g)}function c(){if(f){f.mark.context(f,g,function(){var j=this.mouse();h.x=Math.max(0,Math.min(i.x,j.x));h.y=Math.max(0,Math.min(i.y,j.y));h.dx=Math.min(this.width(),Math.max(j.x,i.x))-h.x;h.dy=Math.min(this.height(),Math.max(j.y,i.y))-h.y;this.render()});pv.Mark.dispatch("select",f,g)}}function d(){if(f){pv.Mark.dispatch("selectend",f,g);f=null}}var f, +g,h,i;pv.listen(window,"mousemove",c);pv.listen(window,"mouseup",d);return b}; +pv.Behavior.resize=function(b){function c(k){h=this.index;g=this.scene;j=this.mouse();i=k;switch(b){case "left":j.x=i.x+i.dx;break;case "right":j.x=i.x;break;case "top":j.y=i.y+i.dy;break;case "bottom":j.y=i.y;break}pv.Mark.dispatch("resizestart",g,h)}function d(){if(g){g.mark.context(g,h,function(){var k=this.mouse();i.x=Math.max(0,Math.min(j.x,k.x));i.y=Math.max(0,Math.min(j.y,k.y));i.dx=Math.min(this.parent.width(),Math.max(k.x,j.x))-i.x;i.dy=Math.min(this.parent.height(),Math.max(k.y,j.y))-i.y; +this.render()});pv.Mark.dispatch("resize",g,h)}}function f(){if(g){pv.Mark.dispatch("resizeend",g,h);g=null}}var g,h,i,j;pv.listen(window,"mousemove",d);pv.listen(window,"mouseup",f);return c}; +pv.Behavior.pan=function(){function b(){g=this.index;f=this.scene;i=pv.vector(pv.event.pageX,pv.event.pageY);h=this.transform();j=1/(h.k*this.scale);if(k)k={x:(1-h.k)*this.width(),y:(1-h.k)*this.height()}}function c(){if(f){f.mark.context(f,g,function(){var l=h.translate((pv.event.pageX-i.x)*j,(pv.event.pageY-i.y)*j);if(k){l.x=Math.max(k.x,Math.min(0,l.x));l.y=Math.max(k.y,Math.min(0,l.y))}this.transform(l).render()});pv.Mark.dispatch("pan",f,g)}}function d(){f=null}var f,g,h,i,j,k;b.bound=function(l){if(arguments.length){k= +Boolean(l);return this}return Boolean(k)};pv.listen(window,"mousemove",c);pv.listen(window,"mouseup",d);return b}; +pv.Behavior.zoom=function(b){function c(){var f=this.mouse(),g=pv.event.wheel*b;f=this.transform().translate(f.x,f.y).scale(g<0?1E3/(1E3-g):(1E3+g)/1E3).translate(-f.x,-f.y);if(d){f.k=Math.max(1,f.k);f.x=Math.max((1-f.k)*this.width(),Math.min(0,f.x));f.y=Math.max((1-f.k)*this.height(),Math.min(0,f.y))}this.transform(f).render();pv.Mark.dispatch("zoom",this.scene,this.index)}var d;arguments.length||(b=1/48);c.bound=function(f){if(arguments.length){d=Boolean(f);return this}return Boolean(d)};return c}; +pv.Geo=function(){}; +pv.Geo.projections={mercator:{project:function(b){return{x:b.lng/180,y:b.lat>85?1:b.lat<-85?-1:Math.log(Math.tan(Math.PI/4+pv.radians(b.lat)/2))/Math.PI}},invert:function(b){return{lng:b.x*180,lat:pv.degrees(2*Math.atan(Math.exp(b.y*Math.PI))-Math.PI/2)}}},"gall-peters":{project:function(b){return{x:b.lng/180,y:Math.sin(pv.radians(b.lat))}},invert:function(b){return{lng:b.x*180,lat:pv.degrees(Math.asin(b.y))}}},sinusoidal:{project:function(b){return{x:pv.radians(b.lng)*Math.cos(pv.radians(b.lat))/Math.PI, +y:b.lat/90}},invert:function(b){return{lng:pv.degrees(b.x*Math.PI/Math.cos(b.y*Math.PI/2)),lat:b.y*90}}},aitoff:{project:function(b){var c=pv.radians(b.lng);b=pv.radians(b.lat);var d=Math.acos(Math.cos(b)*Math.cos(c/2));return{x:2*(d?Math.cos(b)*Math.sin(c/2)*d/Math.sin(d):0)/Math.PI,y:2*(d?Math.sin(b)*d/Math.sin(d):0)/Math.PI}},invert:function(b){var c=b.y*Math.PI/2;return{lng:pv.degrees(b.x*Math.PI/2/Math.cos(c)),lat:pv.degrees(c)}}},hammer:{project:function(b){var c=pv.radians(b.lng);b=pv.radians(b.lat); +var d=Math.sqrt(1+Math.cos(b)*Math.cos(c/2));return{x:2*Math.SQRT2*Math.cos(b)*Math.sin(c/2)/d/3,y:Math.SQRT2*Math.sin(b)/d/1.5}},invert:function(b){var c=b.x*3;b=b.y*1.5;var d=Math.sqrt(1-c*c/16-b*b/4);return{lng:pv.degrees(2*Math.atan2(d*c,2*(2*d*d-1))),lat:pv.degrees(Math.asin(d*b))}}},identity:{project:function(b){return{x:b.lng/180,y:b.lat/90}},invert:function(b){return{lng:b.x*180,lat:b.y*90}}}}; +pv.Geo.scale=function(b){function c(m){if(!n||m.lng!=n.lng||m.lat!=n.lat){n=m;m=d(m);p={x:k(m.x),y:l(m.y)}}return p}function d(m){return j.project({lng:m.lng-q.lng,lat:m.lat})}function f(m){m=j.invert(m);m.lng+=q.lng;return m}var g={x:0,y:0},h={x:1,y:1},i=[],j=pv.Geo.projections.identity,k=pv.Scale.linear(-1,1).range(0,1),l=pv.Scale.linear(-1,1).range(1,0),q={lng:0,lat:0},n,p;c.x=function(m){return c(m).x};c.y=function(m){return c(m).y};c.ticks={lng:function(m){var r;if(i.length>1){var s=pv.Scale.linear(); +if(m==undefined)m=10;r=s.domain(i,function(t){return t.lat}).ticks(m);m=s.domain(i,function(t){return t.lng}).ticks(m)}else{r=pv.range(-80,81,10);m=pv.range(-180,181,10)}return m.map(function(t){return r.map(function(x){return{lat:x,lng:t}})})},lat:function(m){return pv.transpose(c.ticks.lng(m))}};c.invert=function(m){return f({x:k.invert(m.x),y:l.invert(m.y)})};c.domain=function(m,r){if(arguments.length){i=m instanceof Array?arguments.length>1?pv.map(m,r):m:Array.prototype.slice.call(arguments); +if(i.length>1){var s=i.map(function(x){return x.lng}),t=i.map(function(x){return x.lat});q={lng:(pv.max(s)+pv.min(s))/2,lat:(pv.max(t)+pv.min(t))/2};s=i.map(d);k.domain(s,function(x){return x.x});l.domain(s,function(x){return x.y})}else{q={lng:0,lat:0};k.domain(-1,1);l.domain(-1,1)}n=null;return this}return i};c.range=function(m,r){if(arguments.length){if(typeof m=="object"){g={x:Number(m.x),y:Number(m.y)};h={x:Number(r.x),y:Number(r.y)}}else{g={x:0,y:0};h={x:Number(m),y:Number(r)}}k.range(g.x,h.x); +l.range(h.y,g.y);n=null;return this}return[g,h]};c.projection=function(m){if(arguments.length){j=typeof m=="string"?pv.Geo.projections[m]||pv.Geo.projections.identity:m;return this.domain(i)}return m};c.by=function(m){function r(){return c(m.apply(this,arguments))}for(var s in c)r[s]=c[s];return r};arguments.length&&c.projection(b);return c}; diff --git a/ComfyUI/custom_nodes/comfyui-kjnodes/kjweb_async/purify.min.js b/ComfyUI/custom_nodes/comfyui-kjnodes/kjweb_async/purify.min.js new file mode 100644 index 0000000000000000000000000000000000000000..c2f5164618eebcc44b0186f594ccb8092639c670 --- /dev/null +++ b/ComfyUI/custom_nodes/comfyui-kjnodes/kjweb_async/purify.min.js @@ -0,0 +1,3 @@ +/*! @license DOMPurify 3.0.11 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.0.11/LICENSE */ +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).DOMPurify=t()}(this,(function(){"use strict";const{entries:e,setPrototypeOf:t,isFrozen:n,getPrototypeOf:o,getOwnPropertyDescriptor:r}=Object;let{freeze:i,seal:a,create:l}=Object,{apply:c,construct:s}="undefined"!=typeof Reflect&&Reflect;i||(i=function(e){return e}),a||(a=function(e){return e}),c||(c=function(e,t,n){return e.apply(t,n)}),s||(s=function(e,t){return new e(...t)});const u=b(Array.prototype.forEach),m=b(Array.prototype.pop),p=b(Array.prototype.push),f=b(String.prototype.toLowerCase),d=b(String.prototype.toString),h=b(String.prototype.match),g=b(String.prototype.replace),T=b(String.prototype.indexOf),y=b(String.prototype.trim),E=b(Object.prototype.hasOwnProperty),A=b(RegExp.prototype.test),_=(N=TypeError,function(){for(var e=arguments.length,t=new Array(e),n=0;n1?n-1:0),r=1;r2&&void 0!==arguments[2]?arguments[2]:f;t&&t(e,null);let i=o.length;for(;i--;){let t=o[i];if("string"==typeof t){const e=r(t);e!==t&&(n(o)||(o[i]=e),t=e)}e[t]=!0}return e}function R(e){for(let t=0;t/gm),B=a(/\${[\w\W]*}/gm),W=a(/^data-[\-\w.\u00B7-\uFFFF]/),G=a(/^aria-[\-\w]+$/),Y=a(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i),j=a(/^(?:\w+script|data):/i),X=a(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g),q=a(/^html$/i),$=a(/^[a-z][.\w]*(-[.\w]+)+$/i);var K=Object.freeze({__proto__:null,MUSTACHE_EXPR:H,ERB_EXPR:z,TMPLIT_EXPR:B,DATA_ATTR:W,ARIA_ATTR:G,IS_ALLOWED_URI:Y,IS_SCRIPT_OR_DATA:j,ATTR_WHITESPACE:X,DOCTYPE_NAME:q,CUSTOM_ELEMENT:$});const V=function(){return"undefined"==typeof window?null:window},Z=function(e,t){if("object"!=typeof e||"function"!=typeof e.createPolicy)return null;let n=null;const o="data-tt-policy-suffix";t&&t.hasAttribute(o)&&(n=t.getAttribute(o));const r="dompurify"+(n?"#"+n:"");try{return e.createPolicy(r,{createHTML:e=>e,createScriptURL:e=>e})}catch(e){return console.warn("TrustedTypes policy "+r+" could not be created."),null}};var J=function t(){let n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:V();const o=e=>t(e);if(o.version="3.0.11",o.removed=[],!n||!n.document||9!==n.document.nodeType)return o.isSupported=!1,o;let{document:r}=n;const a=r,c=a.currentScript,{DocumentFragment:s,HTMLTemplateElement:N,Node:b,Element:R,NodeFilter:H,NamedNodeMap:z=n.NamedNodeMap||n.MozNamedAttrMap,HTMLFormElement:B,DOMParser:W,trustedTypes:G}=n,j=R.prototype,X=L(j,"cloneNode"),$=L(j,"nextSibling"),J=L(j,"childNodes"),Q=L(j,"parentNode");if("function"==typeof N){const e=r.createElement("template");e.content&&e.content.ownerDocument&&(r=e.content.ownerDocument)}let ee,te="";const{implementation:ne,createNodeIterator:oe,createDocumentFragment:re,getElementsByTagName:ie}=r,{importNode:ae}=a;let le={};o.isSupported="function"==typeof e&&"function"==typeof Q&&ne&&void 0!==ne.createHTMLDocument;const{MUSTACHE_EXPR:ce,ERB_EXPR:se,TMPLIT_EXPR:ue,DATA_ATTR:me,ARIA_ATTR:pe,IS_SCRIPT_OR_DATA:fe,ATTR_WHITESPACE:de,CUSTOM_ELEMENT:he}=K;let{IS_ALLOWED_URI:ge}=K,Te=null;const ye=S({},[...D,...C,...O,...v,...M]);let Ee=null;const Ae=S({},[...I,...U,...P,...F]);let _e=Object.seal(l(null,{tagNameCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},attributeNameCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},allowCustomizedBuiltInElements:{writable:!0,configurable:!1,enumerable:!0,value:!1}})),Ne=null,be=null,Se=!0,Re=!0,we=!1,Le=!0,De=!1,Ce=!0,Oe=!1,xe=!1,ve=!1,ke=!1,Me=!1,Ie=!1,Ue=!0,Pe=!1;const Fe="user-content-";let He=!0,ze=!1,Be={},We=null;const Ge=S({},["annotation-xml","audio","colgroup","desc","foreignobject","head","iframe","math","mi","mn","mo","ms","mtext","noembed","noframes","noscript","plaintext","script","style","svg","template","thead","title","video","xmp"]);let Ye=null;const je=S({},["audio","video","img","source","image","track"]);let Xe=null;const qe=S({},["alt","class","for","id","label","name","pattern","placeholder","role","summary","title","value","style","xmlns"]),$e="http://www.w3.org/1998/Math/MathML",Ke="http://www.w3.org/2000/svg",Ve="http://www.w3.org/1999/xhtml";let Ze=Ve,Je=!1,Qe=null;const et=S({},[$e,Ke,Ve],d);let tt=null;const nt=["application/xhtml+xml","text/html"],ot="text/html";let rt=null,it=null;const at=r.createElement("form"),lt=function(e){return e instanceof RegExp||e instanceof Function},ct=function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};if(!it||it!==e){if(e&&"object"==typeof e||(e={}),e=w(e),tt=-1===nt.indexOf(e.PARSER_MEDIA_TYPE)?ot:e.PARSER_MEDIA_TYPE,rt="application/xhtml+xml"===tt?d:f,Te=E(e,"ALLOWED_TAGS")?S({},e.ALLOWED_TAGS,rt):ye,Ee=E(e,"ALLOWED_ATTR")?S({},e.ALLOWED_ATTR,rt):Ae,Qe=E(e,"ALLOWED_NAMESPACES")?S({},e.ALLOWED_NAMESPACES,d):et,Xe=E(e,"ADD_URI_SAFE_ATTR")?S(w(qe),e.ADD_URI_SAFE_ATTR,rt):qe,Ye=E(e,"ADD_DATA_URI_TAGS")?S(w(je),e.ADD_DATA_URI_TAGS,rt):je,We=E(e,"FORBID_CONTENTS")?S({},e.FORBID_CONTENTS,rt):Ge,Ne=E(e,"FORBID_TAGS")?S({},e.FORBID_TAGS,rt):{},be=E(e,"FORBID_ATTR")?S({},e.FORBID_ATTR,rt):{},Be=!!E(e,"USE_PROFILES")&&e.USE_PROFILES,Se=!1!==e.ALLOW_ARIA_ATTR,Re=!1!==e.ALLOW_DATA_ATTR,we=e.ALLOW_UNKNOWN_PROTOCOLS||!1,Le=!1!==e.ALLOW_SELF_CLOSE_IN_ATTR,De=e.SAFE_FOR_TEMPLATES||!1,Ce=!1!==e.SAFE_FOR_XML,Oe=e.WHOLE_DOCUMENT||!1,ke=e.RETURN_DOM||!1,Me=e.RETURN_DOM_FRAGMENT||!1,Ie=e.RETURN_TRUSTED_TYPE||!1,ve=e.FORCE_BODY||!1,Ue=!1!==e.SANITIZE_DOM,Pe=e.SANITIZE_NAMED_PROPS||!1,He=!1!==e.KEEP_CONTENT,ze=e.IN_PLACE||!1,ge=e.ALLOWED_URI_REGEXP||Y,Ze=e.NAMESPACE||Ve,_e=e.CUSTOM_ELEMENT_HANDLING||{},e.CUSTOM_ELEMENT_HANDLING&<(e.CUSTOM_ELEMENT_HANDLING.tagNameCheck)&&(_e.tagNameCheck=e.CUSTOM_ELEMENT_HANDLING.tagNameCheck),e.CUSTOM_ELEMENT_HANDLING&<(e.CUSTOM_ELEMENT_HANDLING.attributeNameCheck)&&(_e.attributeNameCheck=e.CUSTOM_ELEMENT_HANDLING.attributeNameCheck),e.CUSTOM_ELEMENT_HANDLING&&"boolean"==typeof e.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements&&(_e.allowCustomizedBuiltInElements=e.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements),De&&(Re=!1),Me&&(ke=!0),Be&&(Te=S({},M),Ee=[],!0===Be.html&&(S(Te,D),S(Ee,I)),!0===Be.svg&&(S(Te,C),S(Ee,U),S(Ee,F)),!0===Be.svgFilters&&(S(Te,O),S(Ee,U),S(Ee,F)),!0===Be.mathMl&&(S(Te,v),S(Ee,P),S(Ee,F))),e.ADD_TAGS&&(Te===ye&&(Te=w(Te)),S(Te,e.ADD_TAGS,rt)),e.ADD_ATTR&&(Ee===Ae&&(Ee=w(Ee)),S(Ee,e.ADD_ATTR,rt)),e.ADD_URI_SAFE_ATTR&&S(Xe,e.ADD_URI_SAFE_ATTR,rt),e.FORBID_CONTENTS&&(We===Ge&&(We=w(We)),S(We,e.FORBID_CONTENTS,rt)),He&&(Te["#text"]=!0),Oe&&S(Te,["html","head","body"]),Te.table&&(S(Te,["tbody"]),delete Ne.tbody),e.TRUSTED_TYPES_POLICY){if("function"!=typeof e.TRUSTED_TYPES_POLICY.createHTML)throw _('TRUSTED_TYPES_POLICY configuration option must provide a "createHTML" hook.');if("function"!=typeof e.TRUSTED_TYPES_POLICY.createScriptURL)throw _('TRUSTED_TYPES_POLICY configuration option must provide a "createScriptURL" hook.');ee=e.TRUSTED_TYPES_POLICY,te=ee.createHTML("")}else void 0===ee&&(ee=Z(G,c)),null!==ee&&"string"==typeof te&&(te=ee.createHTML(""));i&&i(e),it=e}},st=S({},["mi","mo","mn","ms","mtext"]),ut=S({},["foreignobject","desc","title","annotation-xml"]),mt=S({},["title","style","font","a","script"]),pt=S({},[...C,...O,...x]),ft=S({},[...v,...k]),dt=function(e){let t=Q(e);t&&t.tagName||(t={namespaceURI:Ze,tagName:"template"});const n=f(e.tagName),o=f(t.tagName);return!!Qe[e.namespaceURI]&&(e.namespaceURI===Ke?t.namespaceURI===Ve?"svg"===n:t.namespaceURI===$e?"svg"===n&&("annotation-xml"===o||st[o]):Boolean(pt[n]):e.namespaceURI===$e?t.namespaceURI===Ve?"math"===n:t.namespaceURI===Ke?"math"===n&&ut[o]:Boolean(ft[n]):e.namespaceURI===Ve?!(t.namespaceURI===Ke&&!ut[o])&&(!(t.namespaceURI===$e&&!st[o])&&(!ft[n]&&(mt[n]||!pt[n]))):!("application/xhtml+xml"!==tt||!Qe[e.namespaceURI]))},ht=function(e){p(o.removed,{element:e});try{e.parentNode.removeChild(e)}catch(t){e.remove()}},gt=function(e,t){try{p(o.removed,{attribute:t.getAttributeNode(e),from:t})}catch(e){p(o.removed,{attribute:null,from:t})}if(t.removeAttribute(e),"is"===e&&!Ee[e])if(ke||Me)try{ht(t)}catch(e){}else try{t.setAttribute(e,"")}catch(e){}},Tt=function(e){let t=null,n=null;if(ve)e=""+e;else{const t=h(e,/^[\r\n\t ]+/);n=t&&t[0]}"application/xhtml+xml"===tt&&Ze===Ve&&(e=''+e+"");const o=ee?ee.createHTML(e):e;if(Ze===Ve)try{t=(new W).parseFromString(o,tt)}catch(e){}if(!t||!t.documentElement){t=ne.createDocument(Ze,"template",null);try{t.documentElement.innerHTML=Je?te:o}catch(e){}}const i=t.body||t.documentElement;return e&&n&&i.insertBefore(r.createTextNode(n),i.childNodes[0]||null),Ze===Ve?ie.call(t,Oe?"html":"body")[0]:Oe?t.documentElement:i},yt=function(e){return oe.call(e.ownerDocument||e,e,H.SHOW_ELEMENT|H.SHOW_COMMENT|H.SHOW_TEXT|H.SHOW_PROCESSING_INSTRUCTION|H.SHOW_CDATA_SECTION,null)},Et=function(e){return e instanceof B&&("string"!=typeof e.nodeName||"string"!=typeof e.textContent||"function"!=typeof e.removeChild||!(e.attributes instanceof z)||"function"!=typeof e.removeAttribute||"function"!=typeof e.setAttribute||"string"!=typeof e.namespaceURI||"function"!=typeof e.insertBefore||"function"!=typeof e.hasChildNodes)},At=function(e){return"function"==typeof b&&e instanceof b},_t=function(e,t,n){le[e]&&u(le[e],(e=>{e.call(o,t,n,it)}))},Nt=function(e){let t=null;if(_t("beforeSanitizeElements",e,null),Et(e))return ht(e),!0;const n=rt(e.nodeName);if(_t("uponSanitizeElement",e,{tagName:n,allowedTags:Te}),e.hasChildNodes()&&!At(e.firstElementChild)&&A(/<[/\w]/g,e.innerHTML)&&A(/<[/\w]/g,e.textContent))return ht(e),!0;if(7===e.nodeType)return ht(e),!0;if(Ce&&8===e.nodeType&&A(/<[/\w]/g,e.data))return ht(e),!0;if(!Te[n]||Ne[n]){if(!Ne[n]&&St(n)){if(_e.tagNameCheck instanceof RegExp&&A(_e.tagNameCheck,n))return!1;if(_e.tagNameCheck instanceof Function&&_e.tagNameCheck(n))return!1}if(He&&!We[n]){const t=Q(e)||e.parentNode,n=J(e)||e.childNodes;if(n&&t){for(let o=n.length-1;o>=0;--o)t.insertBefore(X(n[o],!0),$(e))}}return ht(e),!0}return e instanceof R&&!dt(e)?(ht(e),!0):"noscript"!==n&&"noembed"!==n&&"noframes"!==n||!A(/<\/no(script|embed|frames)/i,e.innerHTML)?(De&&3===e.nodeType&&(t=e.textContent,u([ce,se,ue],(e=>{t=g(t,e," ")})),e.textContent!==t&&(p(o.removed,{element:e.cloneNode()}),e.textContent=t)),_t("afterSanitizeElements",e,null),!1):(ht(e),!0)},bt=function(e,t,n){if(Ue&&("id"===t||"name"===t)&&(n in r||n in at))return!1;if(Re&&!be[t]&&A(me,t));else if(Se&&A(pe,t));else if(!Ee[t]||be[t]){if(!(St(e)&&(_e.tagNameCheck instanceof RegExp&&A(_e.tagNameCheck,e)||_e.tagNameCheck instanceof Function&&_e.tagNameCheck(e))&&(_e.attributeNameCheck instanceof RegExp&&A(_e.attributeNameCheck,t)||_e.attributeNameCheck instanceof Function&&_e.attributeNameCheck(t))||"is"===t&&_e.allowCustomizedBuiltInElements&&(_e.tagNameCheck instanceof RegExp&&A(_e.tagNameCheck,n)||_e.tagNameCheck instanceof Function&&_e.tagNameCheck(n))))return!1}else if(Xe[t]);else if(A(ge,g(n,de,"")));else if("src"!==t&&"xlink:href"!==t&&"href"!==t||"script"===e||0!==T(n,"data:")||!Ye[e]){if(we&&!A(fe,g(n,de,"")));else if(n)return!1}else;return!0},St=function(e){return"annotation-xml"!==e&&h(e,he)},Rt=function(e){_t("beforeSanitizeAttributes",e,null);const{attributes:t}=e;if(!t)return;const n={attrName:"",attrValue:"",keepAttr:!0,allowedAttributes:Ee};let r=t.length;for(;r--;){const i=t[r],{name:a,namespaceURI:l,value:c}=i,s=rt(a);let p="value"===a?c:y(c);if(n.attrName=s,n.attrValue=p,n.keepAttr=!0,n.forceKeepAttr=void 0,_t("uponSanitizeAttribute",e,n),p=n.attrValue,n.forceKeepAttr)continue;if(gt(a,e),!n.keepAttr)continue;if(!Le&&A(/\/>/i,p)){gt(a,e);continue}De&&u([ce,se,ue],(e=>{p=g(p,e," ")}));const f=rt(e.nodeName);if(bt(f,s,p)){if(!Pe||"id"!==s&&"name"!==s||(gt(a,e),p=Fe+p),ee&&"object"==typeof G&&"function"==typeof G.getAttributeType)if(l);else switch(G.getAttributeType(f,s)){case"TrustedHTML":p=ee.createHTML(p);break;case"TrustedScriptURL":p=ee.createScriptURL(p)}try{l?e.setAttributeNS(l,a,p):e.setAttribute(a,p),m(o.removed)}catch(e){}}}_t("afterSanitizeAttributes",e,null)},wt=function e(t){let n=null;const o=yt(t);for(_t("beforeSanitizeShadowDOM",t,null);n=o.nextNode();)_t("uponSanitizeShadowNode",n,null),Nt(n)||(n.content instanceof s&&e(n.content),Rt(n));_t("afterSanitizeShadowDOM",t,null)};return o.sanitize=function(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=null,r=null,i=null,l=null;if(Je=!e,Je&&(e="\x3c!--\x3e"),"string"!=typeof e&&!At(e)){if("function"!=typeof e.toString)throw _("toString is not a function");if("string"!=typeof(e=e.toString()))throw _("dirty is not a string, aborting")}if(!o.isSupported)return e;if(xe||ct(t),o.removed=[],"string"==typeof e&&(ze=!1),ze){if(e.nodeName){const t=rt(e.nodeName);if(!Te[t]||Ne[t])throw _("root node is forbidden and cannot be sanitized in-place")}}else if(e instanceof b)n=Tt("\x3c!----\x3e"),r=n.ownerDocument.importNode(e,!0),1===r.nodeType&&"BODY"===r.nodeName||"HTML"===r.nodeName?n=r:n.appendChild(r);else{if(!ke&&!De&&!Oe&&-1===e.indexOf("<"))return ee&&Ie?ee.createHTML(e):e;if(n=Tt(e),!n)return ke?null:Ie?te:""}n&&ve&&ht(n.firstChild);const c=yt(ze?e:n);for(;i=c.nextNode();)Nt(i)||(i.content instanceof s&&wt(i.content),Rt(i));if(ze)return e;if(ke){if(Me)for(l=re.call(n.ownerDocument);n.firstChild;)l.appendChild(n.firstChild);else l=n;return(Ee.shadowroot||Ee.shadowrootmode)&&(l=ae.call(a,l,!0)),l}let m=Oe?n.outerHTML:n.innerHTML;return Oe&&Te["!doctype"]&&n.ownerDocument&&n.ownerDocument.doctype&&n.ownerDocument.doctype.name&&A(q,n.ownerDocument.doctype.name)&&(m="\n"+m),De&&u([ce,se,ue],(e=>{m=g(m,e," ")})),ee&&Ie?ee.createHTML(m):m},o.setConfig=function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};ct(e),xe=!0},o.clearConfig=function(){it=null,xe=!1},o.isValidAttribute=function(e,t,n){it||ct({});const o=rt(e),r=rt(t);return bt(o,r,n)},o.addHook=function(e,t){"function"==typeof t&&(le[e]=le[e]||[],p(le[e],t))},o.removeHook=function(e){if(le[e])return m(le[e])},o.removeHooks=function(e){le[e]&&(le[e]=[])},o.removeAllHooks=function(){le={}},o}();return J})); +//# sourceMappingURL=purify.min.js.map diff --git a/ComfyUI/custom_nodes/comfyui-kjnodes/kjweb_async/svg-path-properties.min.js b/ComfyUI/custom_nodes/comfyui-kjnodes/kjweb_async/svg-path-properties.min.js new file mode 100644 index 0000000000000000000000000000000000000000..88d47e0de4c54f881083164c20045a7e8b621caf --- /dev/null +++ b/ComfyUI/custom_nodes/comfyui-kjnodes/kjweb_async/svg-path-properties.min.js @@ -0,0 +1,2 @@ +// http://geoexamples.com/path-properties/ v1.2.0 Copyright 2023 Roger Veciana i Rovira +!function(t,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports):"function"==typeof define&&define.amd?define(["exports"],n):n((t="undefined"!=typeof globalThis?globalThis:t||self).svgPathProperties={})}(this,(function(t){"use strict";function n(t,n){for(var e=0;et.length)&&(n=t.length);for(var e=0,i=new Array(n);eu.length&&(t=u.length);var n=f({x:u.x0,y:u.y0},u.rx,u.ry,u.xAxisRotate,u.LargeArcFlag,u.SweepFlag,{x:u.x1,y:u.y1},t/u.length);return{x:n.x,y:n.y}})),i(this,"getTangentAtLength",(function(t){t<0?t=0:t>u.length&&(t=u.length);var n,e=.05,i=u.getPointAtLength(t);t<0?t=0:t>u.length&&(t=u.length);var r=(n=t1&&(n=Math.sqrt(c)*n,e=Math.sqrt(c)*e);var f=(Math.pow(n,2)*Math.pow(e,2)-Math.pow(n,2)*Math.pow(l.y,2)-Math.pow(e,2)*Math.pow(l.x,2))/(Math.pow(n,2)*Math.pow(l.y,2)+Math.pow(e,2)*Math.pow(l.x,2));f=f<0?0:f;var y=(r!==h?1:-1)*Math.sqrt(f),v=y*(n*l.y/e),M=y*(-e*l.x/n),L={x:Math.cos(o)*v-Math.sin(o)*M+(t.x+s.x)/2,y:Math.sin(o)*v+Math.cos(o)*M+(t.y+s.y)/2},d={x:(l.x-v)/n,y:(l.y-M)/e},A=w({x:1,y:0},d),b=w(d,{x:(-l.x-v)/n,y:(-l.y-M)/e});!h&&b>0?b-=2*Math.PI:h&&b<0&&(b+=2*Math.PI);var P=A+(b%=2*Math.PI)*a,m=n*Math.cos(P),T=e*Math.sin(P);return{x:Math.cos(o)*m-Math.sin(o)*T+L.x,y:Math.sin(o)*m+Math.cos(o)*T+L.y,ellipticalArcStartAngle:A,ellipticalArcEndAngle:A+b,ellipticalArcAngle:P,ellipticalArcCenter:L,resultantRx:n,resultantRy:e}},y=function(t,n){t=t||500;for(var e,i=0,r=[],h=[],s=n(0),a=0;a0?Math.sqrt(l*l+c):0,y=u*u+c>0?Math.sqrt(u*u+c):0,p=u+Math.sqrt(u*u+c)!==0&&(l+f)/(u+y)!=0?c*Math.log(Math.abs((l+f)/(u+y))):0;return Math.sqrt(a)/2*(l*f-u*y+p)},_=function(t,n,e){return{x:2*(1-e)*(t[1]-t[0])+2*e*(t[2]-t[1]),y:2*(1-e)*(n[1]-n[0])+2*e*(n[2]-n[1])}};function S(t,n,e){var i=N(1,e,t),r=N(1,e,n),h=i*i+r*r;return Math.sqrt(h)}var N=function t(n,e,i){var r,h,s=i.length-1;if(0===s)return 0;if(0===n){h=0;for(var a=0;a<=s;a++)h+=A[s][a]*Math.pow(1-e,s-a)*Math.pow(e,a)*i[a];return h}r=new Array(s);for(var o=0;o.001;){var a=e(r+h),o=Math.abs(t-a)/n;if(o500)break}return r},j=e((function(t,n,e,r,h,s,a,o){var g=this;i(this,"a",void 0),i(this,"b",void 0),i(this,"c",void 0),i(this,"d",void 0),i(this,"length",void 0),i(this,"getArcLength",void 0),i(this,"getPoint",void 0),i(this,"getDerivative",void 0),i(this,"getTotalLength",(function(){return g.length})),i(this,"getPointAtLength",(function(t){var n=[g.a.x,g.b.x,g.c.x,g.d.x],e=[g.a.y,g.b.y,g.c.y,g.d.y],i=C(t,g.length,(function(t){return g.getArcLength(n,e,t)}));return g.getPoint(n,e,i)})),i(this,"getTangentAtLength",(function(t){var n=[g.a.x,g.b.x,g.c.x,g.d.x],e=[g.a.y,g.b.y,g.c.y,g.d.y],i=C(t,g.length,(function(t){return g.getArcLength(n,e,t)})),r=g.getDerivative(n,e,i),h=Math.sqrt(r.x*r.x+r.y*r.y);return h>0?{x:r.x/h,y:r.y/h}:{x:0,y:0}})),i(this,"getPropertiesAtLength",(function(t){var n,e=[g.a.x,g.b.x,g.c.x,g.d.x],i=[g.a.y,g.b.y,g.c.y,g.d.y],r=C(t,g.length,(function(t){return g.getArcLength(e,i,t)})),h=g.getDerivative(e,i,r),s=Math.sqrt(h.x*h.x+h.y*h.y);n=s>0?{x:h.x/s,y:h.y/s}:{x:0,y:0};var a=g.getPoint(e,i,r);return{x:a.x,y:a.y,tangentX:n.x,tangentY:n.y}})),i(this,"getC",(function(){return g.c})),i(this,"getD",(function(){return g.d})),this.a={x:t,y:n},this.b={x:e,y:r},this.c={x:h,y:s},void 0!==a&&void 0!==o?(this.getArcLength=m,this.getPoint=b,this.getDerivative=P,this.d={x:a,y:o}):(this.getArcLength=q,this.getPoint=T,this.getDerivative=_,this.d={x:0,y:0}),this.length=this.getArcLength([this.a.x,this.b.x,this.c.x,this.d.x],[this.a.y,this.b.y,this.c.y,this.d.y],1)})),O=e((function(t){var n=this;i(this,"length",0),i(this,"partial_lengths",[]),i(this,"functions",[]),i(this,"initial_point",null),i(this,"getPartAtLength",(function(t){t<0?t=0:t>n.length&&(t=n.length);for(var e=n.partial_lengths.length-1;n.partial_lengths[e]>=t&&e>0;)e--;return e++,{fraction:t-n.partial_lengths[e-1],i:e}})),i(this,"getTotalLength",(function(){return n.length})),i(this,"getPointAtLength",(function(t){var e=n.getPartAtLength(t),i=n.functions[e.i];if(i)return i.getPointAtLength(e.fraction);if(n.initial_point)return n.initial_point;throw new Error("Wrong function at this part.")})),i(this,"getTangentAtLength",(function(t){var e=n.getPartAtLength(t),i=n.functions[e.i];if(i)return i.getTangentAtLength(e.fraction);if(n.initial_point)return{x:0,y:0};throw new Error("Wrong function at this part.")})),i(this,"getPropertiesAtLength",(function(t){var e=n.getPartAtLength(t),i=n.functions[e.i];if(i)return i.getPropertiesAtLength(e.fraction);if(n.initial_point)return{x:n.initial_point.x,y:n.initial_point.y,tangentX:0,tangentY:0};throw new Error("Wrong function at this part.")})),i(this,"getParts",(function(){for(var t=[],e=0;e0?t:"M0,0").match(o);if(!n)throw new Error("No path elements found in string ".concat(t));return n.reduce((function(t,n){var e=n.charAt(0),i=e.toLowerCase(),h=u(n.substring(1));if("m"===i&&h.length>2&&(t.push([e].concat(r(h.splice(0,2)))),i="l",e="m"===e?"l":"L"),"a"===i.toLowerCase()&&(5===h.length||6===h.length)){var s=n.substring(1).trim().split(" ");h=[Number(s[0]),Number(s[1]),Number(s[2]),Number(s[3].charAt(0)),Number(s[3].charAt(1)),Number(s[3].substring(2)),Number(s[4])]}for(;h.length>=0;){if(h.length===a[i]){t.push([e].concat(r(h.splice(0,a[i]))));break}if(h.length0?(this.length+=e.getTotalLength(),this.functions.push(e),s=[h[y][5]+s[0],h[y][6]+s[1]]):this.functions.push(new l(s[0],s[0],s[1],s[1]));else if("S"===h[y][0]){if(y>0&&["C","c","S","s"].indexOf(h[y-1][0])>-1){if(e){var p=e.getC();e=new j(s[0],s[1],2*s[0]-p.x,2*s[1]-p.y,h[y][1],h[y][2],h[y][3],h[y][4])}}else e=new j(s[0],s[1],s[0],s[1],h[y][1],h[y][2],h[y][3],h[y][4]);e&&(this.length+=e.getTotalLength(),s=[h[y][3],h[y][4]],this.functions.push(e))}else if("s"===h[y][0]){if(y>0&&["C","c","S","s"].indexOf(h[y-1][0])>-1){if(e){var x=e.getC(),v=e.getD();e=new j(s[0],s[1],s[0]+v.x-x.x,s[1]+v.y-x.y,s[0]+h[y][1],s[1]+h[y][2],s[0]+h[y][3],s[1]+h[y][4])}}else e=new j(s[0],s[1],s[0],s[1],s[0]+h[y][1],s[1]+h[y][2],s[0]+h[y][3],s[1]+h[y][4]);e&&(this.length+=e.getTotalLength(),s=[h[y][3]+s[0],h[y][4]+s[1]],this.functions.push(e))}else if("Q"===h[y][0]){if(s[0]==h[y][1]&&s[1]==h[y][2]){var M=new l(h[y][1],h[y][3],h[y][2],h[y][4]);this.length+=M.getTotalLength(),this.functions.push(M)}else e=new j(s[0],s[1],h[y][1],h[y][2],h[y][3],h[y][4],void 0,void 0),this.length+=e.getTotalLength(),this.functions.push(e);s=[h[y][3],h[y][4]],g=[h[y][1],h[y][2]]}else if("q"===h[y][0]){if(0!=h[y][1]||0!=h[y][2])e=new j(s[0],s[1],s[0]+h[y][1],s[1]+h[y][2],s[0]+h[y][3],s[1]+h[y][4],void 0,void 0),this.length+=e.getTotalLength(),this.functions.push(e);else{var w=new l(s[0]+h[y][1],s[0]+h[y][3],s[1]+h[y][2],s[1]+h[y][4]);this.length+=w.getTotalLength(),this.functions.push(w)}g=[s[0]+h[y][1],s[1]+h[y][2]],s=[h[y][3]+s[0],h[y][4]+s[1]]}else if("T"===h[y][0]){if(y>0&&["Q","q","T","t"].indexOf(h[y-1][0])>-1)e=new j(s[0],s[1],2*s[0]-g[0],2*s[1]-g[1],h[y][1],h[y][2],void 0,void 0),this.functions.push(e),this.length+=e.getTotalLength();else{var L=new l(s[0],h[y][1],s[1],h[y][2]);this.functions.push(L),this.length+=L.getTotalLength()}g=[2*s[0]-g[0],2*s[1]-g[1]],s=[h[y][1],h[y][2]]}else if("t"===h[y][0]){if(y>0&&["Q","q","T","t"].indexOf(h[y-1][0])>-1)e=new j(s[0],s[1],2*s[0]-g[0],2*s[1]-g[1],s[0]+h[y][1],s[1]+h[y][2],void 0,void 0),this.length+=e.getTotalLength(),this.functions.push(e);else{var d=new l(s[0],s[0]+h[y][1],s[1],s[1]+h[y][2]);this.length+=d.getTotalLength(),this.functions.push(d)}g=[2*s[0]-g[0],2*s[1]-g[1]],s=[h[y][1]+s[0],h[y][2]+s[1]]}else if("A"===h[y][0]){var A=new c(s[0],s[1],h[y][1],h[y][2],h[y][3],1===h[y][4],1===h[y][5],h[y][6],h[y][7]);this.length+=A.getTotalLength(),s=[h[y][6],h[y][7]],this.functions.push(A)}else if("a"===h[y][0]){var b=new c(s[0],s[1],h[y][1],h[y][2],h[y][3],1===h[y][4],1===h[y][5],s[0]+h[y][6],s[1]+h[y][7]);this.length+=b.getTotalLength(),s=[s[0]+h[y][6],s[1]+h[y][7]],this.functions.push(b)}this.partial_lengths.push(this.length)}})),E=e((function(t){var n=this;if(i(this,"inst",void 0),i(this,"getTotalLength",(function(){return n.inst.getTotalLength()})),i(this,"getPointAtLength",(function(t){return n.inst.getPointAtLength(t)})),i(this,"getTangentAtLength",(function(t){return n.inst.getTangentAtLength(t)})),i(this,"getPropertiesAtLength",(function(t){return n.inst.getPropertiesAtLength(t)})),i(this,"getParts",(function(){return n.inst.getParts()})),this.inst=new O(t),!(this instanceof E))return new E(t)}));t.svgPathProperties=E})); diff --git a/ComfyUI/custom_nodes/comfyui-kjnodes/nodes/audioscheduler_nodes.py b/ComfyUI/custom_nodes/comfyui-kjnodes/nodes/audioscheduler_nodes.py new file mode 100644 index 0000000000000000000000000000000000000000..69d0422e7da875298f87fe60a7f6d1494530dca2 --- /dev/null +++ b/ComfyUI/custom_nodes/comfyui-kjnodes/nodes/audioscheduler_nodes.py @@ -0,0 +1,251 @@ +# to be used with https://github.com/a1lazydog/ComfyUI-AudioScheduler +import torch +from torchvision.transforms import functional as TF +from PIL import Image, ImageDraw +import numpy as np +from ..utility.utility import pil2tensor +from nodes import MAX_RESOLUTION + +class NormalizedAmplitudeToMask: + @classmethod + def INPUT_TYPES(s): + return {"required": { + "normalized_amp": ("NORMALIZED_AMPLITUDE",), + "width": ("INT", {"default": 512,"min": 16, "max": 4096, "step": 1}), + "height": ("INT", {"default": 512,"min": 16, "max": 4096, "step": 1}), + "frame_offset": ("INT", {"default": 0,"min": -255, "max": 255, "step": 1}), + "location_x": ("INT", {"default": 256,"min": 0, "max": 4096, "step": 1}), + "location_y": ("INT", {"default": 256,"min": 0, "max": 4096, "step": 1}), + "size": ("INT", {"default": 128,"min": 8, "max": 4096, "step": 1}), + "shape": ( + [ + 'none', + 'circle', + 'square', + 'triangle', + ], + { + "default": 'none' + }), + "color": ( + [ + 'white', + 'amplitude', + ], + { + "default": 'amplitude' + }), + },} + + CATEGORY = "KJNodes/audio" + RETURN_TYPES = ("MASK",) + FUNCTION = "convert" + DESCRIPTION = """ +Works as a bridge to the AudioScheduler -nodes: +https://github.com/a1lazydog/ComfyUI-AudioScheduler +Creates masks based on the normalized amplitude. +""" + + def convert(self, normalized_amp, width, height, frame_offset, shape, location_x, location_y, size, color): + # Ensure normalized_amp is an array and within the range [0, 1] + normalized_amp = np.clip(normalized_amp, 0.0, 1.0) + + # Offset the amplitude values by rolling the array + normalized_amp = np.roll(normalized_amp, frame_offset) + + # Initialize an empty list to hold the image tensors + out = [] + # Iterate over each amplitude value to create an image + for amp in normalized_amp: + # Scale the amplitude value to cover the full range of grayscale values + if color == 'amplitude': + grayscale_value = int(amp * 255) + elif color == 'white': + grayscale_value = 255 + # Convert the grayscale value to an RGB format + gray_color = (grayscale_value, grayscale_value, grayscale_value) + finalsize = size * amp + + if shape == 'none': + shapeimage = Image.new("RGB", (width, height), gray_color) + else: + shapeimage = Image.new("RGB", (width, height), "black") + + draw = ImageDraw.Draw(shapeimage) + if shape == 'circle' or shape == 'square': + # Define the bounding box for the shape + left_up_point = (location_x - finalsize, location_y - finalsize) + right_down_point = (location_x + finalsize,location_y + finalsize) + two_points = [left_up_point, right_down_point] + + if shape == 'circle': + draw.ellipse(two_points, fill=gray_color) + elif shape == 'square': + draw.rectangle(two_points, fill=gray_color) + + elif shape == 'triangle': + # Define the points for the triangle + left_up_point = (location_x - finalsize, location_y + finalsize) # bottom left + right_down_point = (location_x + finalsize, location_y + finalsize) # bottom right + top_point = (location_x, location_y) # top point + draw.polygon([top_point, left_up_point, right_down_point], fill=gray_color) + + shapeimage = pil2tensor(shapeimage) + mask = shapeimage[:, :, :, 0] + out.append(mask) + + return (torch.cat(out, dim=0),) + +class NormalizedAmplitudeToFloatList: + @classmethod + def INPUT_TYPES(s): + return {"required": { + "normalized_amp": ("NORMALIZED_AMPLITUDE",), + },} + + CATEGORY = "KJNodes/audio" + RETURN_TYPES = ("FLOAT",) + FUNCTION = "convert" + DESCRIPTION = """ +Works as a bridge to the AudioScheduler -nodes: +https://github.com/a1lazydog/ComfyUI-AudioScheduler +Creates a list of floats from the normalized amplitude. +""" + + def convert(self, normalized_amp): + # Ensure normalized_amp is an array and within the range [0, 1] + normalized_amp = np.clip(normalized_amp, 0.0, 1.0) + return (normalized_amp.tolist(),) + +class OffsetMaskByNormalizedAmplitude: + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "normalized_amp": ("NORMALIZED_AMPLITUDE",), + "mask": ("MASK",), + "x": ("INT", { "default": 0, "min": -4096, "max": MAX_RESOLUTION, "step": 1, "display": "number" }), + "y": ("INT", { "default": 0, "min": -4096, "max": MAX_RESOLUTION, "step": 1, "display": "number" }), + "rotate": ("BOOLEAN", { "default": False }), + "angle_multiplier": ("FLOAT", { "default": 0.0, "min": -1.0, "max": 1.0, "step": 0.001, "display": "number" }), + } + } + + RETURN_TYPES = ("MASK",) + RETURN_NAMES = ("mask",) + FUNCTION = "offset" + CATEGORY = "KJNodes/audio" + DESCRIPTION = """ +Works as a bridge to the AudioScheduler -nodes: +https://github.com/a1lazydog/ComfyUI-AudioScheduler +Offsets masks based on the normalized amplitude. +""" + + def offset(self, mask, x, y, angle_multiplier, rotate, normalized_amp): + + # Ensure normalized_amp is an array and within the range [0, 1] + offsetmask = mask.clone() + normalized_amp = np.clip(normalized_amp, 0.0, 1.0) + + batch_size, height, width = mask.shape + + if rotate: + for i in range(batch_size): + rotation_amp = int(normalized_amp[i] * (360 * angle_multiplier)) + rotation_angle = rotation_amp + offsetmask[i] = TF.rotate(offsetmask[i].unsqueeze(0), rotation_angle).squeeze(0) + if x != 0 or y != 0: + for i in range(batch_size): + offset_amp = normalized_amp[i] * 10 + shift_x = min(x*offset_amp, width-1) + shift_y = min(y*offset_amp, height-1) + if shift_x != 0: + offsetmask[i] = torch.roll(offsetmask[i], shifts=int(shift_x), dims=1) + if shift_y != 0: + offsetmask[i] = torch.roll(offsetmask[i], shifts=int(shift_y), dims=0) + + return offsetmask, + +class ImageTransformByNormalizedAmplitude: + @classmethod + def INPUT_TYPES(s): + return {"required": { + "normalized_amp": ("NORMALIZED_AMPLITUDE",), + "zoom_scale": ("FLOAT", { "default": 0.0, "min": -1.0, "max": 1.0, "step": 0.001, "display": "number" }), + "x_offset": ("INT", { "default": 0, "min": (1 -MAX_RESOLUTION), "max": MAX_RESOLUTION, "step": 1, "display": "number" }), + "y_offset": ("INT", { "default": 0, "min": (1 -MAX_RESOLUTION), "max": MAX_RESOLUTION, "step": 1, "display": "number" }), + "cumulative": ("BOOLEAN", { "default": False }), + "image": ("IMAGE",), + }} + + RETURN_TYPES = ("IMAGE",) + FUNCTION = "amptransform" + CATEGORY = "KJNodes/audio" + DESCRIPTION = """ +Works as a bridge to the AudioScheduler -nodes: +https://github.com/a1lazydog/ComfyUI-AudioScheduler +Transforms image based on the normalized amplitude. +""" + + def amptransform(self, image, normalized_amp, zoom_scale, cumulative, x_offset, y_offset): + # Ensure normalized_amp is an array and within the range [0, 1] + normalized_amp = np.clip(normalized_amp, 0.0, 1.0) + transformed_images = [] + + # Initialize the cumulative zoom factor + prev_amp = 0.0 + + for i in range(image.shape[0]): + img = image[i] # Get the i-th image in the batch + amp = normalized_amp[i] # Get the corresponding amplitude value + + # Incrementally increase the cumulative zoom factor + if cumulative: + prev_amp += amp + amp += prev_amp + + # Convert the image tensor from BxHxWxC to CxHxW format expected by torchvision + img = img.permute(2, 0, 1) + + # Convert PyTorch tensor to PIL Image for processing + pil_img = TF.to_pil_image(img) + + # Calculate the crop size based on the amplitude + width, height = pil_img.size + crop_size = int(min(width, height) * (1 - amp * zoom_scale)) + crop_size = max(crop_size, 1) + + # Calculate the crop box coordinates (centered crop) + left = (width - crop_size) // 2 + top = (height - crop_size) // 2 + right = (width + crop_size) // 2 + bottom = (height + crop_size) // 2 + + # Crop and resize back to original size + cropped_img = TF.crop(pil_img, top, left, crop_size, crop_size) + resized_img = TF.resize(cropped_img, (height, width)) + + # Convert back to tensor in CxHxW format + tensor_img = TF.to_tensor(resized_img) + + # Convert the tensor back to BxHxWxC format + tensor_img = tensor_img.permute(1, 2, 0) + + # Offset the image based on the amplitude + offset_amp = amp * 10 # Calculate the offset magnitude based on the amplitude + shift_x = min(x_offset * offset_amp, img.shape[1] - 1) # Calculate the shift in x direction + shift_y = min(y_offset * offset_amp, img.shape[0] - 1) # Calculate the shift in y direction + + # Apply the offset to the image tensor + if shift_x != 0: + tensor_img = torch.roll(tensor_img, shifts=int(shift_x), dims=1) + if shift_y != 0: + tensor_img = torch.roll(tensor_img, shifts=int(shift_y), dims=0) + + # Add to the list + transformed_images.append(tensor_img) + + # Stack all transformed images into a batch + transformed_batch = torch.stack(transformed_images) + + return (transformed_batch,) \ No newline at end of file diff --git a/ComfyUI/custom_nodes/comfyui-kjnodes/nodes/batchcrop_nodes.py b/ComfyUI/custom_nodes/comfyui-kjnodes/nodes/batchcrop_nodes.py new file mode 100644 index 0000000000000000000000000000000000000000..3b8cd3aa39a2b14662aa323a86005a68579f4b04 --- /dev/null +++ b/ComfyUI/custom_nodes/comfyui-kjnodes/nodes/batchcrop_nodes.py @@ -0,0 +1,763 @@ +from ..utility.utility import tensor2pil, pil2tensor +from PIL import Image, ImageDraw, ImageFilter +import numpy as np +import torch +from torchvision.transforms import Resize, CenterCrop, InterpolationMode +import math + +#based on nodes from mtb https://github.com/melMass/comfy_mtb + +def bbox_to_region(bbox, target_size=None): + bbox = bbox_check(bbox, target_size) + return (bbox[0], bbox[1], bbox[0] + bbox[2], bbox[1] + bbox[3]) + +def bbox_check(bbox, target_size=None): + if not target_size: + return bbox + + new_bbox = ( + bbox[0], + bbox[1], + min(target_size[0] - bbox[0], bbox[2]), + min(target_size[1] - bbox[1], bbox[3]), + ) + return new_bbox + +class BatchCropFromMask: + + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "original_images": ("IMAGE",), + "masks": ("MASK",), + "crop_size_mult": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}), + "bbox_smooth_alpha": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 1.0, "step": 0.01}), + }, + } + + RETURN_TYPES = ( + "IMAGE", + "IMAGE", + "BBOX", + "INT", + "INT", + ) + RETURN_NAMES = ( + "original_images", + "cropped_images", + "bboxes", + "width", + "height", + ) + FUNCTION = "crop" + CATEGORY = "KJNodes/masking" + + def smooth_bbox_size(self, prev_bbox_size, curr_bbox_size, alpha): + if alpha == 0: + return prev_bbox_size + return round(alpha * curr_bbox_size + (1 - alpha) * prev_bbox_size) + + def smooth_center(self, prev_center, curr_center, alpha=0.5): + if alpha == 0: + return prev_center + return ( + round(alpha * curr_center[0] + (1 - alpha) * prev_center[0]), + round(alpha * curr_center[1] + (1 - alpha) * prev_center[1]) + ) + + def crop(self, masks, original_images, crop_size_mult, bbox_smooth_alpha): + + bounding_boxes = [] + cropped_images = [] + + self.max_bbox_width = 0 + self.max_bbox_height = 0 + + # First, calculate the maximum bounding box size across all masks + curr_max_bbox_width = 0 + curr_max_bbox_height = 0 + for mask in masks: + _mask = tensor2pil(mask)[0] + non_zero_indices = np.nonzero(np.array(_mask)) + min_x, max_x = np.min(non_zero_indices[1]), np.max(non_zero_indices[1]) + min_y, max_y = np.min(non_zero_indices[0]), np.max(non_zero_indices[0]) + width = max_x - min_x + height = max_y - min_y + curr_max_bbox_width = max(curr_max_bbox_width, width) + curr_max_bbox_height = max(curr_max_bbox_height, height) + + # Smooth the changes in the bounding box size + self.max_bbox_width = self.smooth_bbox_size(self.max_bbox_width, curr_max_bbox_width, bbox_smooth_alpha) + self.max_bbox_height = self.smooth_bbox_size(self.max_bbox_height, curr_max_bbox_height, bbox_smooth_alpha) + + # Apply the crop size multiplier + self.max_bbox_width = round(self.max_bbox_width * crop_size_mult) + self.max_bbox_height = round(self.max_bbox_height * crop_size_mult) + bbox_aspect_ratio = self.max_bbox_width / self.max_bbox_height + + # Then, for each mask and corresponding image... + for i, (mask, img) in enumerate(zip(masks, original_images)): + _mask = tensor2pil(mask)[0] + non_zero_indices = np.nonzero(np.array(_mask)) + min_x, max_x = np.min(non_zero_indices[1]), np.max(non_zero_indices[1]) + min_y, max_y = np.min(non_zero_indices[0]), np.max(non_zero_indices[0]) + + # Calculate center of bounding box + center_x = np.mean(non_zero_indices[1]) + center_y = np.mean(non_zero_indices[0]) + curr_center = (round(center_x), round(center_y)) + + # If this is the first frame, initialize prev_center with curr_center + if not hasattr(self, 'prev_center'): + self.prev_center = curr_center + + # Smooth the changes in the center coordinates from the second frame onwards + if i > 0: + center = self.smooth_center(self.prev_center, curr_center, bbox_smooth_alpha) + else: + center = curr_center + + # Update prev_center for the next frame + self.prev_center = center + + # Create bounding box using max_bbox_width and max_bbox_height + half_box_width = round(self.max_bbox_width / 2) + half_box_height = round(self.max_bbox_height / 2) + min_x = max(0, center[0] - half_box_width) + max_x = min(img.shape[1], center[0] + half_box_width) + min_y = max(0, center[1] - half_box_height) + max_y = min(img.shape[0], center[1] + half_box_height) + + # Append bounding box coordinates + bounding_boxes.append((min_x, min_y, max_x - min_x, max_y - min_y)) + + # Crop the image from the bounding box + cropped_img = img[min_y:max_y, min_x:max_x, :] + + # Calculate the new dimensions while maintaining the aspect ratio + new_height = min(cropped_img.shape[0], self.max_bbox_height) + new_width = round(new_height * bbox_aspect_ratio) + + # Resize the image + resize_transform = Resize((new_height, new_width)) + resized_img = resize_transform(cropped_img.permute(2, 0, 1)) + + # Perform the center crop to the desired size + crop_transform = CenterCrop((self.max_bbox_height, self.max_bbox_width)) # swap the order here if necessary + cropped_resized_img = crop_transform(resized_img) + + cropped_images.append(cropped_resized_img.permute(1, 2, 0)) + + cropped_out = torch.stack(cropped_images, dim=0) + + return (original_images, cropped_out, bounding_boxes, self.max_bbox_width, self.max_bbox_height, ) + +class BatchUncrop: + + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "original_images": ("IMAGE",), + "cropped_images": ("IMAGE",), + "bboxes": ("BBOX",), + "border_blending": ("FLOAT", {"default": 0.25, "min": 0.0, "max": 1.0, "step": 0.01}, ), + "crop_rescale": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.01}), + "border_top": ("BOOLEAN", {"default": True}), + "border_bottom": ("BOOLEAN", {"default": True}), + "border_left": ("BOOLEAN", {"default": True}), + "border_right": ("BOOLEAN", {"default": True}), + } + } + + RETURN_TYPES = ("IMAGE",) + FUNCTION = "uncrop" + + CATEGORY = "KJNodes/masking" + + def uncrop(self, original_images, cropped_images, bboxes, border_blending, crop_rescale, border_top, border_bottom, border_left, border_right): + def inset_border(image, border_width, border_color, border_top, border_bottom, border_left, border_right): + draw = ImageDraw.Draw(image) + width, height = image.size + if border_top: + draw.rectangle((0, 0, width, border_width), fill=border_color) + if border_bottom: + draw.rectangle((0, height - border_width, width, height), fill=border_color) + if border_left: + draw.rectangle((0, 0, border_width, height), fill=border_color) + if border_right: + draw.rectangle((width - border_width, 0, width, height), fill=border_color) + return image + + if len(original_images) != len(cropped_images): + raise ValueError(f"The number of original_images ({len(original_images)}) and cropped_images ({len(cropped_images)}) should be the same") + + # Ensure there are enough bboxes, but drop the excess if there are more bboxes than images + if len(bboxes) > len(original_images): + print(f"Warning: Dropping excess bounding boxes. Expected {len(original_images)}, but got {len(bboxes)}") + bboxes = bboxes[:len(original_images)] + elif len(bboxes) < len(original_images): + raise ValueError("There should be at least as many bboxes as there are original and cropped images") + + input_images = tensor2pil(original_images) + crop_imgs = tensor2pil(cropped_images) + + out_images = [] + for i in range(len(input_images)): + img = input_images[i] + crop = crop_imgs[i] + bbox = bboxes[i] + + # uncrop the image based on the bounding box + bb_x, bb_y, bb_width, bb_height = bbox + + paste_region = bbox_to_region((bb_x, bb_y, bb_width, bb_height), img.size) + + # scale factors + scale_x = crop_rescale + scale_y = crop_rescale + + # scaled paste_region + paste_region = (round(paste_region[0]*scale_x), round(paste_region[1]*scale_y), round(paste_region[2]*scale_x), round(paste_region[3]*scale_y)) + + # rescale the crop image to fit the paste_region + crop = crop.resize((round(paste_region[2]-paste_region[0]), round(paste_region[3]-paste_region[1]))) + crop_img = crop.convert("RGB") + + if border_blending > 1.0: + border_blending = 1.0 + elif border_blending < 0.0: + border_blending = 0.0 + + blend_ratio = (max(crop_img.size) / 2) * float(border_blending) + + blend = img.convert("RGBA") + mask = Image.new("L", img.size, 0) + + mask_block = Image.new("L", (paste_region[2]-paste_region[0], paste_region[3]-paste_region[1]), 255) + mask_block = inset_border(mask_block, round(blend_ratio / 2), (0), border_top, border_bottom, border_left, border_right) + + mask.paste(mask_block, paste_region) + blend.paste(crop_img, paste_region) + + mask = mask.filter(ImageFilter.BoxBlur(radius=blend_ratio / 4)) + mask = mask.filter(ImageFilter.GaussianBlur(radius=blend_ratio / 4)) + + blend.putalpha(mask) + img = Image.alpha_composite(img.convert("RGBA"), blend) + out_images.append(img.convert("RGB")) + + return (pil2tensor(out_images),) + +class BatchCropFromMaskAdvanced: + + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "original_images": ("IMAGE",), + "masks": ("MASK",), + "crop_size_mult": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.01}), + "bbox_smooth_alpha": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 1.0, "step": 0.01}), + }, + } + + RETURN_TYPES = ( + "IMAGE", + "IMAGE", + "MASK", + "IMAGE", + "MASK", + "BBOX", + "BBOX", + "INT", + "INT", + ) + RETURN_NAMES = ( + "original_images", + "cropped_images", + "cropped_masks", + "combined_crop_image", + "combined_crop_masks", + "bboxes", + "combined_bounding_box", + "bbox_width", + "bbox_height", + ) + FUNCTION = "crop" + CATEGORY = "KJNodes/masking" + + def smooth_bbox_size(self, prev_bbox_size, curr_bbox_size, alpha): + return round(alpha * curr_bbox_size + (1 - alpha) * prev_bbox_size) + + def smooth_center(self, prev_center, curr_center, alpha=0.5): + return (round(alpha * curr_center[0] + (1 - alpha) * prev_center[0]), + round(alpha * curr_center[1] + (1 - alpha) * prev_center[1])) + + def crop(self, masks, original_images, crop_size_mult, bbox_smooth_alpha): + bounding_boxes = [] + combined_bounding_box = [] + cropped_images = [] + cropped_masks = [] + cropped_masks_out = [] + combined_crop_out = [] + combined_cropped_images = [] + combined_cropped_masks = [] + + def calculate_bbox(mask): + non_zero_indices = np.nonzero(np.array(mask)) + + # handle empty masks + min_x, max_x, min_y, max_y = 0, 0, 0, 0 + if len(non_zero_indices[1]) > 0 and len(non_zero_indices[0]) > 0: + min_x, max_x = np.min(non_zero_indices[1]), np.max(non_zero_indices[1]) + min_y, max_y = np.min(non_zero_indices[0]), np.max(non_zero_indices[0]) + + width = max_x - min_x + height = max_y - min_y + bbox_size = max(width, height) + return min_x, max_x, min_y, max_y, bbox_size + + combined_mask = torch.max(masks, dim=0)[0] + _mask = tensor2pil(combined_mask)[0] + new_min_x, new_max_x, new_min_y, new_max_y, combined_bbox_size = calculate_bbox(_mask) + center_x = (new_min_x + new_max_x) / 2 + center_y = (new_min_y + new_max_y) / 2 + half_box_size = round(combined_bbox_size // 2) + new_min_x = max(0, round(center_x - half_box_size)) + new_max_x = min(original_images[0].shape[1], round(center_x + half_box_size)) + new_min_y = max(0, round(center_y - half_box_size)) + new_max_y = min(original_images[0].shape[0], round(center_y + half_box_size)) + + combined_bounding_box.append((new_min_x, new_min_y, new_max_x - new_min_x, new_max_y - new_min_y)) + + self.max_bbox_size = 0 + + # First, calculate the maximum bounding box size across all masks + curr_max_bbox_size = max(calculate_bbox(tensor2pil(mask)[0])[-1] for mask in masks) + # Smooth the changes in the bounding box size + self.max_bbox_size = self.smooth_bbox_size(self.max_bbox_size, curr_max_bbox_size, bbox_smooth_alpha) + # Apply the crop size multiplier + self.max_bbox_size = round(self.max_bbox_size * crop_size_mult) + # Make sure max_bbox_size is divisible by 16, if not, round it upwards so it is + self.max_bbox_size = math.ceil(self.max_bbox_size / 16) * 16 + + if self.max_bbox_size > original_images[0].shape[0] or self.max_bbox_size > original_images[0].shape[1]: + # max_bbox_size can only be as big as our input's width or height, and it has to be even + self.max_bbox_size = math.floor(min(original_images[0].shape[0], original_images[0].shape[1]) / 2) * 2 + + # Then, for each mask and corresponding image... + for i, (mask, img) in enumerate(zip(masks, original_images)): + _mask = tensor2pil(mask)[0] + non_zero_indices = np.nonzero(np.array(_mask)) + + # check for empty masks + if len(non_zero_indices[0]) > 0 and len(non_zero_indices[1]) > 0: + min_x, max_x = np.min(non_zero_indices[1]), np.max(non_zero_indices[1]) + min_y, max_y = np.min(non_zero_indices[0]), np.max(non_zero_indices[0]) + + # Calculate center of bounding box + center_x = np.mean(non_zero_indices[1]) + center_y = np.mean(non_zero_indices[0]) + curr_center = (round(center_x), round(center_y)) + + # If this is the first frame, initialize prev_center with curr_center + if not hasattr(self, 'prev_center'): + self.prev_center = curr_center + + # Smooth the changes in the center coordinates from the second frame onwards + if i > 0: + center = self.smooth_center(self.prev_center, curr_center, bbox_smooth_alpha) + else: + center = curr_center + + # Update prev_center for the next frame + self.prev_center = center + + # Create bounding box using max_bbox_size + half_box_size = self.max_bbox_size // 2 + min_x = max(0, center[0] - half_box_size) + max_x = min(img.shape[1], center[0] + half_box_size) + min_y = max(0, center[1] - half_box_size) + max_y = min(img.shape[0], center[1] + half_box_size) + + # Append bounding box coordinates + bounding_boxes.append((min_x, min_y, max_x - min_x, max_y - min_y)) + + # Crop the image from the bounding box + cropped_img = img[min_y:max_y, min_x:max_x, :] + cropped_mask = mask[min_y:max_y, min_x:max_x] + + # Resize the cropped image to a fixed size + new_size = max(cropped_img.shape[0], cropped_img.shape[1]) + resize_transform = Resize(new_size, interpolation=InterpolationMode.NEAREST, max_size=max(img.shape[0], img.shape[1])) + resized_mask = resize_transform(cropped_mask.unsqueeze(0).unsqueeze(0)).squeeze(0).squeeze(0) + resized_img = resize_transform(cropped_img.permute(2, 0, 1)) + # Perform the center crop to the desired size + # Constrain the crop to the smaller of our bbox or our image so we don't expand past the image dimensions. + crop_transform = CenterCrop((min(self.max_bbox_size, resized_img.shape[1]), min(self.max_bbox_size, resized_img.shape[2]))) + + cropped_resized_img = crop_transform(resized_img) + cropped_images.append(cropped_resized_img.permute(1, 2, 0)) + + cropped_resized_mask = crop_transform(resized_mask) + cropped_masks.append(cropped_resized_mask) + + combined_cropped_img = original_images[i][new_min_y:new_max_y, new_min_x:new_max_x, :] + combined_cropped_images.append(combined_cropped_img) + + combined_cropped_mask = masks[i][new_min_y:new_max_y, new_min_x:new_max_x] + combined_cropped_masks.append(combined_cropped_mask) + else: + bounding_boxes.append((0, 0, img.shape[1], img.shape[0])) + cropped_images.append(img) + cropped_masks.append(mask) + combined_cropped_images.append(img) + combined_cropped_masks.append(mask) + + cropped_out = torch.stack(cropped_images, dim=0) + combined_crop_out = torch.stack(combined_cropped_images, dim=0) + cropped_masks_out = torch.stack(cropped_masks, dim=0) + combined_crop_mask_out = torch.stack(combined_cropped_masks, dim=0) + + return (original_images, cropped_out, cropped_masks_out, combined_crop_out, combined_crop_mask_out, bounding_boxes, combined_bounding_box, self.max_bbox_size, self.max_bbox_size) + +class FilterZeroMasksAndCorrespondingImages: + + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "masks": ("MASK",), + }, + "optional": { + "original_images": ("IMAGE",), + }, + } + + RETURN_TYPES = ("MASK", "IMAGE", "IMAGE", "INDEXES",) + RETURN_NAMES = ("non_zero_masks_out", "non_zero_mask_images_out", "zero_mask_images_out", "zero_mask_images_out_indexes",) + FUNCTION = "filter" + CATEGORY = "KJNodes/masking" + DESCRIPTION = """ +Filter out all the empty (i.e. all zero) mask in masks +Also filter out all the corresponding images in original_images by indexes if provide + +original_images (optional): If provided, need have same length as masks. +""" + + def filter(self, masks, original_images=None): + non_zero_masks = [] + non_zero_mask_images = [] + zero_mask_images = [] + zero_mask_images_indexes = [] + + masks_num = len(masks) + also_process_images = False + if original_images is not None: + imgs_num = len(original_images) + if len(original_images) == masks_num: + also_process_images = True + else: + print(f"[WARNING] ignore input: original_images, due to number of original_images ({imgs_num}) is not equal to number of masks ({masks_num})") + + for i in range(masks_num): + non_zero_num = np.count_nonzero(np.array(masks[i])) + if non_zero_num > 0: + non_zero_masks.append(masks[i]) + if also_process_images: + non_zero_mask_images.append(original_images[i]) + else: + zero_mask_images.append(original_images[i]) + zero_mask_images_indexes.append(i) + + non_zero_masks_out = torch.stack(non_zero_masks, dim=0) + non_zero_mask_images_out = zero_mask_images_out = zero_mask_images_out_indexes = None + + if also_process_images: + non_zero_mask_images_out = torch.stack(non_zero_mask_images, dim=0) + if len(zero_mask_images) > 0: + zero_mask_images_out = torch.stack(zero_mask_images, dim=0) + zero_mask_images_out_indexes = zero_mask_images_indexes + + return (non_zero_masks_out, non_zero_mask_images_out, zero_mask_images_out, zero_mask_images_out_indexes) + +class InsertImageBatchByIndexes: + + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "images": ("IMAGE",), + "images_to_insert": ("IMAGE",), + "insert_indexes": ("INDEXES",), + }, + } + + RETURN_TYPES = ("IMAGE", ) + RETURN_NAMES = ("images_after_insert", ) + FUNCTION = "insert" + CATEGORY = "KJNodes/image" + DESCRIPTION = """ +This node is designed to be use with node FilterZeroMasksAndCorrespondingImages +It inserts the images_to_insert into images according to insert_indexes + +Returns: + images_after_insert: updated original images with origonal sequence order +""" + + def insert(self, images, images_to_insert, insert_indexes): + images_after_insert = images + + if images_to_insert is not None and insert_indexes is not None: + images_to_insert_num = len(images_to_insert) + insert_indexes_num = len(insert_indexes) + if images_to_insert_num == insert_indexes_num: + images_after_insert = [] + + i_images = 0 + for i in range(len(images) + images_to_insert_num): + if i in insert_indexes: + images_after_insert.append(images_to_insert[insert_indexes.index(i)]) + else: + images_after_insert.append(images[i_images]) + i_images += 1 + + images_after_insert = torch.stack(images_after_insert, dim=0) + + else: + print(f"[WARNING] skip this node, due to number of images_to_insert ({images_to_insert_num}) is not equal to number of insert_indexes ({insert_indexes_num})") + + + return (images_after_insert, ) + +class BatchUncropAdvanced: + + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "original_images": ("IMAGE",), + "cropped_images": ("IMAGE",), + "cropped_masks": ("MASK",), + "combined_crop_mask": ("MASK",), + "bboxes": ("BBOX",), + "border_blending": ("FLOAT", {"default": 0.25, "min": 0.0, "max": 1.0, "step": 0.01}, ), + "crop_rescale": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.01}), + "use_combined_mask": ("BOOLEAN", {"default": False}), + "use_square_mask": ("BOOLEAN", {"default": True}), + }, + "optional": { + "combined_bounding_box": ("BBOX", {"default": None}), + }, + } + + RETURN_TYPES = ("IMAGE",) + FUNCTION = "uncrop" + CATEGORY = "KJNodes/masking" + + + def uncrop(self, original_images, cropped_images, cropped_masks, combined_crop_mask, bboxes, border_blending, crop_rescale, use_combined_mask, use_square_mask, combined_bounding_box = None): + + def inset_border(image, border_width=20, border_color=(0)): + width, height = image.size + bordered_image = Image.new(image.mode, (width, height), border_color) + bordered_image.paste(image, (0, 0)) + draw = ImageDraw.Draw(bordered_image) + draw.rectangle((0, 0, width - 1, height - 1), outline=border_color, width=border_width) + return bordered_image + + if len(original_images) != len(cropped_images): + raise ValueError(f"The number of original_images ({len(original_images)}) and cropped_images ({len(cropped_images)}) should be the same") + + # Ensure there are enough bboxes, but drop the excess if there are more bboxes than images + if len(bboxes) > len(original_images): + print(f"Warning: Dropping excess bounding boxes. Expected {len(original_images)}, but got {len(bboxes)}") + bboxes = bboxes[:len(original_images)] + elif len(bboxes) < len(original_images): + raise ValueError("There should be at least as many bboxes as there are original and cropped images") + + crop_imgs = tensor2pil(cropped_images) + input_images = tensor2pil(original_images) + out_images = [] + + for i in range(len(input_images)): + img = input_images[i] + crop = crop_imgs[i] + bbox = bboxes[i] + + if use_combined_mask: + bb_x, bb_y, bb_width, bb_height = combined_bounding_box[0] + paste_region = bbox_to_region((bb_x, bb_y, bb_width, bb_height), img.size) + mask = combined_crop_mask[i] + else: + bb_x, bb_y, bb_width, bb_height = bbox + paste_region = bbox_to_region((bb_x, bb_y, bb_width, bb_height), img.size) + mask = cropped_masks[i] + + # scale paste_region + scale_x = scale_y = crop_rescale + paste_region = (round(paste_region[0]*scale_x), round(paste_region[1]*scale_y), round(paste_region[2]*scale_x), round(paste_region[3]*scale_y)) + + # rescale the crop image to fit the paste_region + crop = crop.resize((round(paste_region[2]-paste_region[0]), round(paste_region[3]-paste_region[1]))) + crop_img = crop.convert("RGB") + + #border blending + if border_blending > 1.0: + border_blending = 1.0 + elif border_blending < 0.0: + border_blending = 0.0 + + blend_ratio = (max(crop_img.size) / 2) * float(border_blending) + blend = img.convert("RGBA") + + if use_square_mask: + mask = Image.new("L", img.size, 0) + mask_block = Image.new("L", (paste_region[2]-paste_region[0], paste_region[3]-paste_region[1]), 255) + mask_block = inset_border(mask_block, round(blend_ratio / 2), (0)) + mask.paste(mask_block, paste_region) + else: + original_mask = tensor2pil(mask)[0] + original_mask = original_mask.resize((paste_region[2]-paste_region[0], paste_region[3]-paste_region[1])) + mask = Image.new("L", img.size, 0) + mask.paste(original_mask, paste_region) + + mask = mask.filter(ImageFilter.BoxBlur(radius=blend_ratio / 4)) + mask = mask.filter(ImageFilter.GaussianBlur(radius=blend_ratio / 4)) + + blend.paste(crop_img, paste_region) + blend.putalpha(mask) + + img = Image.alpha_composite(img.convert("RGBA"), blend) + out_images.append(img.convert("RGB")) + + return (pil2tensor(out_images),) + +class SplitBboxes: + + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "bboxes": ("BBOX",), + "index": ("INT", {"default": 0,"min": 0, "max": 99999999, "step": 1}), + }, + } + + RETURN_TYPES = ("BBOX","BBOX",) + RETURN_NAMES = ("bboxes_a","bboxes_b",) + FUNCTION = "splitbbox" + CATEGORY = "KJNodes/masking" + DESCRIPTION = """ +Splits the specified bbox list at the given index into two lists. +""" + + def splitbbox(self, bboxes, index): + bboxes_a = bboxes[:index] # Sub-list from the start of bboxes up to (but not including) the index + bboxes_b = bboxes[index:] # Sub-list from the index to the end of bboxes + + return (bboxes_a, bboxes_b,) + +class BboxToInt: + + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "bboxes": ("BBOX",), + "index": ("INT", {"default": 0,"min": 0, "max": 99999999, "step": 1}), + }, + } + + RETURN_TYPES = ("INT","INT","INT","INT","INT","INT",) + RETURN_NAMES = ("x_min","y_min","width","height", "center_x","center_y",) + FUNCTION = "bboxtoint" + CATEGORY = "KJNodes/masking" + DESCRIPTION = """ +Returns selected index from bounding box list as integers. +""" + def bboxtoint(self, bboxes, index): + x_min, y_min, width, height = bboxes[index] + center_x = int(x_min + width / 2) + center_y = int(y_min + height / 2) + + return (x_min, y_min, width, height, center_x, center_y,) + +class BboxVisualize: + + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "images": ("IMAGE",), + "bboxes": ("BBOX",), + "line_width": ("INT", {"default": 1,"min": 1, "max": 10, "step": 1}), + "bbox_format": (["xywh", "xyxy"], {"default": "xywh"}), + }, + } + + RETURN_TYPES = ("IMAGE",) + RETURN_NAMES = ("images",) + FUNCTION = "visualizebbox" + DESCRIPTION = """ +Visualizes the specified bbox on the image. +""" + + CATEGORY = "KJNodes/masking" + + def visualizebbox(self, bboxes, images, line_width, bbox_format): + image_list = [] + for image, bbox in zip(images, bboxes): + if bbox_format == "xywh": + x_min, y_min, width, height = bbox + elif bbox_format == "xyxy": + x_min, y_min, x_max, y_max = bbox + width = x_max - x_min + height = y_max - y_min + else: + raise ValueError(f"Unknown bbox_format: {bbox_format}") + + # Ensure bbox coordinates are integers + x_min = int(x_min) + y_min = int(y_min) + width = int(width) + height = int(height) + + # Permute the image dimensions + image = image.permute(2, 0, 1) + + # Clone the image to draw bounding boxes + img_with_bbox = image.clone() + + # Define the color for the bbox, e.g., red + color = torch.tensor([1, 0, 0], dtype=torch.float32) + + # Ensure color tensor matches the image channels + if color.shape[0] != img_with_bbox.shape[0]: + color = color.unsqueeze(1).expand(-1, line_width) + + # Draw lines for each side of the bbox with the specified line width + for lw in range(line_width): + # Top horizontal line + if y_min + lw < img_with_bbox.shape[1]: + img_with_bbox[:, y_min + lw, x_min:x_min + width] = color[:, None] + + # Bottom horizontal line + if y_min + height - lw < img_with_bbox.shape[1]: + img_with_bbox[:, y_min + height - lw, x_min:x_min + width] = color[:, None] + + # Left vertical line + if x_min + lw < img_with_bbox.shape[2]: + img_with_bbox[:, y_min:y_min + height, x_min + lw] = color[:, None] + + # Right vertical line + if x_min + width - lw < img_with_bbox.shape[2]: + img_with_bbox[:, y_min:y_min + height, x_min + width - lw] = color[:, None] + + # Permute the image dimensions back + img_with_bbox = img_with_bbox.permute(1, 2, 0).unsqueeze(0) + image_list.append(img_with_bbox) + + return (torch.cat(image_list, dim=0),) \ No newline at end of file diff --git a/ComfyUI/custom_nodes/comfyui-kjnodes/nodes/curve_nodes.py b/ComfyUI/custom_nodes/comfyui-kjnodes/nodes/curve_nodes.py new file mode 100644 index 0000000000000000000000000000000000000000..77be51f9a8a7a7fcc78b8fdc8ea6c423ed2a185c --- /dev/null +++ b/ComfyUI/custom_nodes/comfyui-kjnodes/nodes/curve_nodes.py @@ -0,0 +1,1636 @@ +import torch +from torchvision import transforms +import json +from PIL import Image, ImageDraw, ImageFont, ImageColor, ImageFilter, ImageChops +import numpy as np +from ..utility.utility import pil2tensor, tensor2pil +import folder_paths +import io +import base64 + +from comfy.utils import common_upscale + +def parse_color(color): + if isinstance(color, str) and ',' in color: + return tuple(int(c.strip()) for c in color.split(',')) + return color + +def parse_json_tracks(tracks): + tracks_data = [] + try: + # If tracks is a string, try to parse it as JSON + if isinstance(tracks, str): + parsed = json.loads(tracks.replace("'", '"')) + tracks_data.extend(parsed) + else: + # If tracks is a list of strings, parse each one + for track_str in tracks: + parsed = json.loads(track_str.replace("'", '"')) + tracks_data.append(parsed) + + # Check if we have a single track (dict with x,y) or a list of tracks + if tracks_data and isinstance(tracks_data[0], dict) and 'x' in tracks_data[0]: + # Single track detected, wrap it in a list + tracks_data = [tracks_data] + 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]: + # Already a list of tracks, nothing to do + pass + else: + # Unexpected format + print(f"Warning: Unexpected track format: {type(tracks_data[0])}") + + except json.JSONDecodeError as e: + print(f"Error parsing tracks JSON: {e}") + tracks_data = [] + + return tracks_data + +def plot_coordinates_to_tensor(coordinates, height, width, bbox_height, bbox_width, size_multiplier, prompt): + import matplotlib + matplotlib.use('Agg') + from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas + text_color = '#999999' + bg_color = '#353535' + matplotlib.pyplot.rcParams['text.color'] = text_color + fig, ax = matplotlib.pyplot.subplots(figsize=(width/100, height/100), dpi=100) + fig.patch.set_facecolor(bg_color) + ax.set_facecolor(bg_color) + ax.grid(color=text_color, linestyle='-', linewidth=0.5) + ax.set_xlabel('x', color=text_color) + ax.set_ylabel('y', color=text_color) + for text in ax.get_xticklabels() + ax.get_yticklabels(): + text.set_color(text_color) + ax.set_title('position for: ' + prompt) + ax.set_xlabel('X Coordinate') + ax.set_ylabel('Y Coordinate') + #ax.legend().remove() + ax.set_xlim(0, width) # Set the x-axis to match the input latent width + ax.set_ylim(height, 0) # Set the y-axis to match the input latent height, with (0,0) at top-left + # Adjust the margins of the subplot + matplotlib.pyplot.subplots_adjust(left=0.08, right=0.95, bottom=0.05, top=0.95, wspace=0.2, hspace=0.2) + + cmap = matplotlib.pyplot.get_cmap('rainbow') + image_batch = [] + canvas = FigureCanvas(fig) + width, height = fig.get_size_inches() * fig.get_dpi() + # Draw a box at each coordinate + for i, ((x, y), size) in enumerate(zip(coordinates, size_multiplier)): + color_index = i / (len(coordinates) - 1) + color = cmap(color_index) + draw_height = bbox_height * size + draw_width = bbox_width * size + rect = matplotlib.patches.Rectangle((x - draw_width/2, y - draw_height/2), draw_width, draw_height, + linewidth=1, edgecolor=color, facecolor='none', alpha=0.5) + ax.add_patch(rect) + + # Check if there is a next coordinate to draw an arrow to + if i < len(coordinates) - 1: + x1, y1 = coordinates[i] + x2, y2 = coordinates[i + 1] + ax.annotate("", xy=(x2, y2), xytext=(x1, y1), + arrowprops=dict(arrowstyle="->", + linestyle="-", + lw=1, + color=color, + mutation_scale=20)) + canvas.draw() + image_np = np.frombuffer(canvas.tostring_rgb(), dtype='uint8').reshape(int(height), int(width), 3).copy() + image_tensor = torch.from_numpy(image_np).float() / 255.0 + image_tensor = image_tensor.unsqueeze(0) + image_batch.append(image_tensor) + + matplotlib.pyplot.close(fig) + image_batch_tensor = torch.cat(image_batch, dim=0) + + return image_batch_tensor + +class PlotCoordinates: + @classmethod + def INPUT_TYPES(s): + return {"required": { + "coordinates": ("STRING", {"forceInput": True}), + "text": ("STRING", {"default": 'title', "multiline": False}), + "width": ("INT", {"default": 512, "min": 8, "max": 4096, "step": 8}), + "height": ("INT", {"default": 512, "min": 8, "max": 4096, "step": 8}), + "bbox_width": ("INT", {"default": 128, "min": 8, "max": 4096, "step": 8}), + "bbox_height": ("INT", {"default": 128, "min": 8, "max": 4096, "step": 8}), + }, + "optional": {"size_multiplier": ("FLOAT", {"default": [1.0], "forceInput": True})}, + } + RETURN_TYPES = ("IMAGE", "INT", "INT", "INT", "INT",) + RETURN_NAMES = ("images", "width", "height", "bbox_width", "bbox_height",) + FUNCTION = "append" + CATEGORY = "KJNodes/experimental" + DESCRIPTION = """ +Plots coordinates to sequence of images using Matplotlib. + +""" + + def append(self, coordinates, text, width, height, bbox_width, bbox_height, size_multiplier=[1.0]): + coordinates = json.loads(coordinates.replace("'", '"')) + coordinates = [(coord['x'], coord['y']) for coord in coordinates] + batch_size = len(coordinates) + if not size_multiplier or len(size_multiplier) != batch_size: + size_multiplier = [0] * batch_size + else: + size_multiplier = size_multiplier * (batch_size // len(size_multiplier)) + size_multiplier[:batch_size % len(size_multiplier)] + + plot_image_tensor = plot_coordinates_to_tensor(coordinates, height, width, bbox_height, bbox_width, size_multiplier, text) + + return (plot_image_tensor, width, height, bbox_width, bbox_height) + +class SplineEditor: + + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "points_store": ("STRING", {"multiline": False}), + "coordinates": ("STRING", {"multiline": False}), + "mask_width": ("INT", {"default": 512, "min": 8, "max": 4096, "step": 8}), + "mask_height": ("INT", {"default": 512, "min": 8, "max": 4096, "step": 8}), + "points_to_sample": ("INT", {"default": 16, "min": 2, "max": 1000, "step": 1}), + "sampling_method": ( + [ + 'path', + 'time', + 'controlpoints', + 'speed' + ], + { + "default": 'time' + }), + "interpolation": ( + [ + 'cardinal', + 'monotone', + 'basis', + 'linear', + 'step-before', + 'step-after', + 'polar', + 'polar-reverse', + ], + { + "default": 'cardinal' + }), + "tension": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 1.0, "step": 0.01}), + "repeat_output": ("INT", {"default": 1, "min": 1, "max": 4096, "step": 1}), + "float_output_type": ( + [ + 'list', + 'pandas series', + 'tensor', + ], + { + "default": 'list' + }), + }, + "optional": { + "min_value": ("FLOAT", {"default": 0.0, "min": -10000.0, "max": 10000.0, "step": 0.01}), + "max_value": ("FLOAT", {"default": 1.0, "min": -10000.0, "max": 10000.0, "step": 0.01}), + "bg_image": ("IMAGE", ), + } + } + + RETURN_TYPES = ("MASK", "STRING", "FLOAT", "INT", "STRING",) + RETURN_NAMES = ("mask", "coord_str", "float", "count", "normalized_str",) + FUNCTION = "splinedata" + CATEGORY = "KJNodes/weights" + DESCRIPTION = """ +# WORK IN PROGRESS +Do not count on this as part of your workflow yet, +probably contains lots of bugs and stability is not +guaranteed!! + +## Graphical editor to create values for various +## schedules and/or mask batches. + +**Shift + click** to add control point at end. +**Ctrl + click** to add control point (subdivide) between two points. +**Right click on a point** to delete it. +Note that you can't delete from start/end. + +Right click on canvas for context menu: +NEW!: +- Add new spline + - Creates a new spline on same canvas, currently these paths are only outputed + as coordinates. +- Add single point + - Creates a single point that only returns it's current position coords +- Delete spline + - Deletes the currently selected spline, you can select a spline by clicking on + it's path, or cycle through them with the 'Next spline' -option. + +These are purely visual options, doesn't affect the output: + - Toggle handles visibility + - Display sample points: display the points to be returned. + +**points_to_sample** value sets the number of samples +returned from the **drawn spline itself**, this is independent from the +actual control points, so the interpolation type matters. +sampling_method: + - time: samples along the time axis, used for schedules + - path: samples along the path itself, useful for coordinates + - controlpoints: samples only the control points themselves + +output types: + - mask batch + example compatible nodes: anything that takes masks + - list of floats + example compatible nodes: IPAdapter weights + - pandas series + example compatible nodes: anything that takes Fizz' + nodes Batch Value Schedule + - torch tensor + example compatible nodes: unknown +""" + + def splinedata(self, mask_width, mask_height, coordinates, float_output_type, interpolation, + points_to_sample, sampling_method, points_store, tension, repeat_output, + min_value=0.0, max_value=1.0, bg_image=None): + + coordinates = json.loads(coordinates) + + # Handle nested list structure if present + all_normalized = [] + all_normalized_y_values = [] + + # Check if we have a nested list structure + if isinstance(coordinates, list) and len(coordinates) > 0 and isinstance(coordinates[0], list): + # Process each list of coordinates in the nested structure + coordinate_sets = coordinates + else: + # If not nested, treat as a single list of coordinates + coordinate_sets = [coordinates] + + # Process each set of coordinates + for coord_set in coordinate_sets: + normalized = [] + normalized_y_values = [] + + for coord in coord_set: + coord['x'] = int(round(coord['x'])) + coord['y'] = int(round(coord['y'])) + norm_x = (1.0 - (coord['x'] / mask_height) - 0.0) * (max_value - min_value) + min_value + norm_y = (1.0 - (coord['y'] / mask_height) - 0.0) * (max_value - min_value) + min_value + normalized_y_values.append(norm_y) + normalized.append({'x':norm_x, 'y':norm_y}) + + all_normalized.extend(normalized) + all_normalized_y_values.extend(normalized_y_values) + + # Use the combined normalized values for output + if float_output_type == 'list': + out_floats = all_normalized_y_values * repeat_output + elif float_output_type == 'pandas series': + try: + import pandas as pd + except: + raise Exception("MaskOrImageToWeight: pandas is not installed. Please install pandas to use this output_type") + out_floats = pd.Series(all_normalized_y_values * repeat_output), + elif float_output_type == 'tensor': + out_floats = torch.tensor(all_normalized_y_values * repeat_output, dtype=torch.float32) + + # Create a color map for grayscale intensities + color_map = lambda y: torch.full((mask_height, mask_width, 3), y, dtype=torch.float32) + + # Create image tensors for each normalized y value + mask_tensors = [color_map(y) for y in all_normalized_y_values] + masks_out = torch.stack(mask_tensors) + masks_out = masks_out.repeat(repeat_output, 1, 1, 1) + masks_out = masks_out.mean(dim=-1) + + if bg_image is None: + return (masks_out, json.dumps(coordinates if len(coordinates) > 1 else coordinates[0]), out_floats, len(out_floats), json.dumps(all_normalized)) + else: + transform = transforms.ToPILImage() + image = transform(bg_image[0].permute(2, 0, 1)) + buffered = io.BytesIO() + image.save(buffered, format="JPEG", quality=75) + + # Encode the image bytes to a Base64 string + img_bytes = buffered.getvalue() + img_base64 = base64.b64encode(img_bytes).decode('utf-8') + + return { + "ui": {"bg_image": [img_base64]}, + "result": (masks_out, json.dumps(coordinates if len(coordinates) > 1 else coordinates[0]), out_floats, len(out_floats), json.dumps(all_normalized)) + } + + +class CreateShapeMaskOnPath: + + RETURN_TYPES = ("MASK", "MASK",) + RETURN_NAMES = ("mask", "mask_inverted",) + FUNCTION = "createshapemask" + CATEGORY = "KJNodes/masking/generate" + DESCRIPTION = """ +Creates a mask or batch of masks with the specified shape. +Locations are center locations. +""" + DEPRECATED = True + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "shape": ( + [ 'circle', + 'square', + 'triangle', + ], + { + "default": 'circle' + }), + "coordinates": ("STRING", {"forceInput": True}), + "frame_width": ("INT", {"default": 512,"min": 16, "max": 4096, "step": 1}), + "frame_height": ("INT", {"default": 512,"min": 16, "max": 4096, "step": 1}), + "shape_width": ("INT", {"default": 128,"min": 8, "max": 4096, "step": 1}), + "shape_height": ("INT", {"default": 128,"min": 8, "max": 4096, "step": 1}), + }, + "optional": { + "size_multiplier": ("FLOAT", {"default": [1.0], "forceInput": True}), + } + } + + def createshapemask(self, coordinates, frame_width, frame_height, shape_width, shape_height, shape, size_multiplier=[1.0]): + # Define the number of images in the batch + coordinates = coordinates.replace("'", '"') + coordinates = json.loads(coordinates) + + batch_size = len(coordinates) + out = [] + color = "white" + if not size_multiplier or len(size_multiplier) != batch_size: + size_multiplier = [0] * batch_size + else: + size_multiplier = size_multiplier * (batch_size // len(size_multiplier)) + size_multiplier[:batch_size % len(size_multiplier)] + for i, coord in enumerate(coordinates): + image = Image.new("RGB", (frame_width, frame_height), "black") + draw = ImageDraw.Draw(image) + + # Calculate the size for this frame and ensure it's not less than 0 + current_width = max(0, shape_width + i * size_multiplier[i]) + current_height = max(0, shape_height + i * size_multiplier[i]) + + location_x = coord['x'] + location_y = coord['y'] + + if shape == 'circle' or shape == 'square': + # Define the bounding box for the shape + left_up_point = (location_x - current_width // 2, location_y - current_height // 2) + right_down_point = (location_x + current_width // 2, location_y + current_height // 2) + two_points = [left_up_point, right_down_point] + + if shape == 'circle': + draw.ellipse(two_points, fill=color) + elif shape == 'square': + draw.rectangle(two_points, fill=color) + + elif shape == 'triangle': + # Define the points for the triangle + left_up_point = (location_x - current_width // 2, location_y + current_height // 2) # bottom left + right_down_point = (location_x + current_width // 2, location_y + current_height // 2) # bottom right + top_point = (location_x, location_y - current_height // 2) # top point + draw.polygon([top_point, left_up_point, right_down_point], fill=color) + + image = pil2tensor(image) + mask = image[:, :, :, 0] + out.append(mask) + outstack = torch.cat(out, dim=0) + return (outstack, 1.0 - outstack,) + + + +class CreateShapeImageOnPath: + + RETURN_TYPES = ("IMAGE", "MASK",) + RETURN_NAMES = ("image","mask", ) + FUNCTION = "createshapemask" + CATEGORY = "KJNodes/image" + DESCRIPTION = """ +Creates an image or batch of images with the specified shape. +Locations are center locations. +""" + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "shape": ( + [ 'circle', + 'square', + 'triangle', + ], + { + "default": 'circle' + }), + "coordinates": ("STRING", {"forceInput": True}), + "frame_width": ("INT", {"default": 512,"min": 16, "max": 4096, "step": 1}), + "frame_height": ("INT", {"default": 512,"min": 16, "max": 4096, "step": 1}), + "shape_width": ("INT", {"default": 128,"min": 2, "max": 4096, "step": 1}), + "shape_height": ("INT", {"default": 128,"min": 2, "max": 4096, "step": 1}), + "shape_color": ("STRING", {"default": 'white'}), + "bg_color": ("STRING", {"default": 'black'}), + "blur_radius": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 100, "step": 0.1}), + "intensity": ("FLOAT", {"default": 1.0, "min": 0.01, "max": 100.0, "step": 0.01}), + }, + "optional": { + "size_multiplier": ("FLOAT", {"default": [1.0], "forceInput": True}), + "trailing": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.01}), + "border_width": ("INT", {"default": 0, "min": 0, "max": 100, "step": 1}), + "border_color": ("STRING", {"default": 'black'}), + } + } + + def createshapemask(self, coordinates, frame_width, frame_height, shape_width, shape_height, shape_color, + bg_color, blur_radius, shape, intensity, size_multiplier=[1.0], trailing=1.0, border_width=0, border_color='black'): + + shape_color = parse_color(shape_color) + border_color = parse_color(border_color) + bg_color = parse_color(bg_color) + coords_list = parse_json_tracks(coordinates) + + batch_size = len(coords_list[0]) + images_list = [] + masks_list = [] + + if not size_multiplier or len(size_multiplier) != batch_size: + size_multiplier = [1] * batch_size + else: + size_multiplier = size_multiplier * (batch_size // len(size_multiplier)) + size_multiplier[:batch_size % len(size_multiplier)] + + previous_output = None + + for i in range(batch_size): + image = Image.new("RGB", (frame_width, frame_height), bg_color) + draw = ImageDraw.Draw(image) + + # Calculate the size for this frame and ensure it's not less than 0 + current_width = shape_width * size_multiplier[i] + current_height = shape_height * size_multiplier[i] + + for coords in coords_list: + location_x = coords[i]['x'] + location_y = coords[i]['y'] + + if shape == 'circle' or shape == 'square': + # Define the bounding box for the shape + left_up_point = (location_x - current_width // 2, location_y - current_height // 2) + right_down_point = (location_x + current_width // 2, location_y + current_height // 2) + two_points = [left_up_point, right_down_point] + + if shape == 'circle': + if border_width > 0: + draw.ellipse(two_points, fill=shape_color, outline=border_color, width=border_width) + else: + draw.ellipse(two_points, fill=shape_color) + elif shape == 'square': + if border_width > 0: + draw.rectangle(two_points, fill=shape_color, outline=border_color, width=border_width) + else: + draw.rectangle(two_points, fill=shape_color) + + elif shape == 'triangle': + # Define the points for the triangle + left_up_point = (location_x - current_width // 2, location_y + current_height // 2) # bottom left + right_down_point = (location_x + current_width // 2, location_y + current_height // 2) # bottom right + top_point = (location_x, location_y - current_height // 2) # top point + + if border_width > 0: + draw.polygon([top_point, left_up_point, right_down_point], fill=shape_color, outline=border_color, width=border_width) + else: + draw.polygon([top_point, left_up_point, right_down_point], fill=shape_color) + + if blur_radius != 0: + image = image.filter(ImageFilter.GaussianBlur(blur_radius)) + # Blend the current image with the accumulated image + + image = pil2tensor(image) + if trailing != 1.0 and previous_output is not None: + # Add the decayed previous output to the current frame + image += trailing * previous_output + image = image / image.max() + previous_output = image + image = image * intensity + mask = image[:, :, :, 0] + masks_list.append(mask) + images_list.append(image) + out_images = torch.cat(images_list, dim=0).cpu().float() + out_masks = torch.cat(masks_list, dim=0) + return (out_images, out_masks) + +class CreateTextOnPath: + + RETURN_TYPES = ("IMAGE", "MASK", "MASK",) + RETURN_NAMES = ("image", "mask", "mask_inverted",) + FUNCTION = "createtextmask" + CATEGORY = "KJNodes/masking/generate" + DESCRIPTION = """ +Creates a mask or batch of masks with the specified text. +Locations are center locations. +""" + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "coordinates": ("STRING", {"forceInput": True}), + "text": ("STRING", {"default": 'text', "multiline": True}), + "frame_width": ("INT", {"default": 512,"min": 16, "max": 4096, "step": 1}), + "frame_height": ("INT", {"default": 512,"min": 16, "max": 4096, "step": 1}), + "font": (folder_paths.get_filename_list("kjnodes_fonts"), ), + "font_size": ("INT", {"default": 42}), + "alignment": ( + [ 'left', + 'center', + 'right' + ], + {"default": 'center'} + ), + "text_color": ("STRING", {"default": 'white'}), + }, + "optional": { + "size_multiplier": ("FLOAT", {"default": [1.0], "forceInput": True}), + } + } + + def createtextmask(self, coordinates, frame_width, frame_height, font, font_size, text, text_color, alignment, size_multiplier=[1.0]): + coordinates = coordinates.replace("'", '"') + coordinates = json.loads(coordinates) + + batch_size = len(coordinates) + mask_list = [] + image_list = [] + color = parse_color(text_color) + font_path = folder_paths.get_full_path("kjnodes_fonts", font) + + if len(size_multiplier) != batch_size: + size_multiplier = size_multiplier * (batch_size // len(size_multiplier)) + size_multiplier[:batch_size % len(size_multiplier)] + + for i, coord in enumerate(coordinates): + image = Image.new("RGB", (frame_width, frame_height), "black") + draw = ImageDraw.Draw(image) + lines = text.split('\n') # Split the text into lines + # Apply the size multiplier to the font size for this iteration + current_font_size = int(font_size * size_multiplier[i]) + current_font = ImageFont.truetype(font_path, current_font_size) + line_heights = [current_font.getbbox(line)[3] for line in lines] # List of line heights + total_text_height = sum(line_heights) # Total height of text block + + # Calculate the starting Y position to center the block of text + start_y = coord['y'] - total_text_height // 2 + for j, line in enumerate(lines): + text_width, text_height = current_font.getbbox(line)[2], line_heights[j] + if alignment == 'left': + location_x = coord['x'] + elif alignment == 'center': + location_x = int(coord['x'] - text_width // 2) + elif alignment == 'right': + location_x = int(coord['x'] - text_width) + + location_y = int(start_y + sum(line_heights[:j])) + text_position = (location_x, location_y) + # Draw the text + try: + draw.text(text_position, line, fill=color, font=current_font, features=['-liga']) + except: + draw.text(text_position, line, fill=color, font=current_font) + + image = pil2tensor(image) + non_black_pixels = (image > 0).any(dim=-1) + mask = non_black_pixels.to(image.dtype) + mask_list.append(mask) + image_list.append(image) + + out_images = torch.cat(image_list, dim=0).cpu().float() + out_masks = torch.cat(mask_list, dim=0) + return (out_images, out_masks, 1.0 - out_masks,) + +class CreateGradientFromCoords: + + RETURN_TYPES = ("IMAGE", ) + RETURN_NAMES = ("image", ) + FUNCTION = "generate" + CATEGORY = "KJNodes/image" + DESCRIPTION = """ +Creates a gradient image from coordinates. +""" + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "coordinates": ("STRING", {"forceInput": True}), + "frame_width": ("INT", {"default": 512,"min": 16, "max": 4096, "step": 1}), + "frame_height": ("INT", {"default": 512,"min": 16, "max": 4096, "step": 1}), + "start_color": ("STRING", {"default": 'white'}), + "end_color": ("STRING", {"default": 'black'}), + "multiplier": ("FLOAT", {"default": 1.0, "min": 0.01, "max": 100.0, "step": 0.01}), + }, + } + + def generate(self, coordinates, frame_width, frame_height, start_color, end_color, multiplier): + # Parse the coordinates + coordinates = json.loads(coordinates.replace("'", '"')) + + # Create an image + image = Image.new("RGB", (frame_width, frame_height)) + draw = ImageDraw.Draw(image) + + # Extract start and end points for the gradient + start_coord = coordinates[0] + end_coord = coordinates[1] + + start_color = parse_color(start_color) + end_color = parse_color(end_color) + + # Calculate the gradient direction (vector) + gradient_direction = (end_coord['x'] - start_coord['x'], end_coord['y'] - start_coord['y']) + gradient_length = (gradient_direction[0] ** 2 + gradient_direction[1] ** 2) ** 0.5 + + # Iterate over each pixel in the image + for y in range(frame_height): + for x in range(frame_width): + # Calculate the projection of the point on the gradient line + point_vector = (x - start_coord['x'], y - start_coord['y']) + projection = (point_vector[0] * gradient_direction[0] + point_vector[1] * gradient_direction[1]) / gradient_length + projection = max(min(projection, gradient_length), 0) # Clamp the projection value + + # Calculate the blend factor for the current pixel + blend = projection * multiplier / gradient_length + + # Determine the color of the current pixel + color = ( + int(start_color[0] + (end_color[0] - start_color[0]) * blend), + int(start_color[1] + (end_color[1] - start_color[1]) * blend), + int(start_color[2] + (end_color[2] - start_color[2]) * blend) + ) + + # Set the pixel color + draw.point((x, y), fill=color) + + # Convert the PIL image to a tensor (assuming such a function exists in your context) + image_tensor = pil2tensor(image) + + return (image_tensor,) + +class GradientToFloat: + + RETURN_TYPES = ("FLOAT", "FLOAT",) + RETURN_NAMES = ("float_x", "float_y", ) + FUNCTION = "sample" + CATEGORY = "KJNodes/image" + DESCRIPTION = """ +Calculates list of floats from image. +""" + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "image": ("IMAGE", ), + "steps": ("INT", {"default": 10, "min": 2, "max": 10000, "step": 1}), + }, + } + + def sample(self, image, steps): + # Assuming image is a tensor with shape [B, H, W, C] + B, H, W, C = image.shape + + # Sample along the width axis (W) + w_intervals = torch.linspace(0, W - 1, steps=steps, dtype=torch.int64) + # Assuming we're sampling from the first batch and the first channel + w_sampled = image[0, :, w_intervals, 0] + + # Sample along the height axis (H) + h_intervals = torch.linspace(0, H - 1, steps=steps, dtype=torch.int64) + # Assuming we're sampling from the first batch and the first channel + h_sampled = image[0, h_intervals, :, 0] + + # Taking the mean across the height for width sampling, and across the width for height sampling + w_values = w_sampled.mean(dim=0).tolist() + h_values = h_sampled.mean(dim=1).tolist() + + return (w_values, h_values) + +class MaskOrImageToWeight: + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "output_type": ( + [ + 'list', + 'pandas series', + 'tensor', + 'string' + ], + { + "default": 'list' + }), + }, + "optional": { + "images": ("IMAGE",), + "masks": ("MASK",), + }, + + } + RETURN_TYPES = ("FLOAT", "STRING",) + FUNCTION = "execute" + CATEGORY = "KJNodes/weights" + DESCRIPTION = """ +Gets the mean values from mask or image batch +and returns that as the selected output type. +""" + + def execute(self, output_type, images=None, masks=None): + mean_values = [] + if masks is not None and images is None: + for mask in masks: + mean_values.append(mask.mean().item()) + elif masks is None and images is not None: + for image in images: + mean_values.append(image.mean().item()) + elif masks is not None and images is not None: + raise Exception("MaskOrImageToWeight: Use either mask or image input only.") + + # Convert mean_values to the specified output_type + if output_type == 'list': + out = mean_values + elif output_type == 'pandas series': + try: + import pandas as pd + except: + raise Exception("MaskOrImageToWeight: pandas is not installed. Please install pandas to use this output_type") + out = pd.Series(mean_values), + elif output_type == 'tensor': + out = torch.tensor(mean_values, dtype=torch.float32), + return (out, [str(value) for value in mean_values],) + +class WeightScheduleConvert: + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "input_values": ("FLOAT", {"default": 0.0, "forceInput": True}), + "output_type": ( + [ + 'match_input', + 'list', + 'pandas series', + 'tensor', + ], + { + "default": 'list' + }), + "invert": ("BOOLEAN", {"default": False}), + "repeat": ("INT", {"default": 1,"min": 1, "max": 255, "step": 1}), + }, + "optional": { + "remap_to_frames": ("INT", {"default": 0}), + "interpolation_curve": ("FLOAT", {"forceInput": True}), + "remap_values": ("BOOLEAN", {"default": False}), + "remap_min": ("FLOAT", {"default": 0.0, "min": -100000, "max": 100000.0, "step": 0.01}), + "remap_max": ("FLOAT", {"default": 1.0, "min": -100000, "max": 100000.0, "step": 0.01}), + }, + + } + RETURN_TYPES = ("FLOAT", "STRING", "INT",) + FUNCTION = "execute" + CATEGORY = "KJNodes/weights" + DESCRIPTION = """ +Converts different value lists/series to another type. +""" + + def detect_input_type(self, input_values): + import pandas as pd + if isinstance(input_values, list): + return 'list' + elif isinstance(input_values, pd.Series): + return 'pandas series' + elif isinstance(input_values, torch.Tensor): + return 'tensor' + else: + raise ValueError("Unsupported input type") + + def execute(self, input_values, output_type, invert, repeat, remap_to_frames=0, interpolation_curve=None, remap_min=0.0, remap_max=1.0, remap_values=False): + import pandas as pd + input_type = self.detect_input_type(input_values) + + if input_type == 'pandas series': + float_values = input_values.tolist() + elif input_type == 'tensor': + float_values = input_values + else: + float_values = input_values + + if invert: + float_values = [1 - value for value in float_values] + + if interpolation_curve is not None: + interpolated_pattern = [] + orig_float_values = float_values + for value in interpolation_curve: + min_val = min(orig_float_values) + max_val = max(orig_float_values) + # Normalize the values to [0, 1] + normalized_values = [(value - min_val) / (max_val - min_val) for value in orig_float_values] + # Interpolate the normalized values to the new frame count + remapped_float_values = np.interp(np.linspace(0, 1, int(remap_to_frames * value)), np.linspace(0, 1, len(normalized_values)), normalized_values).tolist() + interpolated_pattern.extend(remapped_float_values) + float_values = interpolated_pattern + else: + # Remap float_values to match target_frame_amount + if remap_to_frames > 0 and remap_to_frames != len(float_values): + min_val = min(float_values) + max_val = max(float_values) + # Normalize the values to [0, 1] + normalized_values = [(value - min_val) / (max_val - min_val) for value in float_values] + # Interpolate the normalized values to the new frame count + float_values = np.interp(np.linspace(0, 1, remap_to_frames), np.linspace(0, 1, len(normalized_values)), normalized_values).tolist() + + float_values = float_values * repeat + if remap_values: + float_values = self.remap_values(float_values, remap_min, remap_max) + + if output_type == 'list': + out = float_values, + elif output_type == 'pandas series': + out = pd.Series(float_values), + elif output_type == 'tensor': + if input_type == 'pandas series': + out = torch.tensor(float_values.values, dtype=torch.float32), + else: + out = torch.tensor(float_values, dtype=torch.float32), + elif output_type == 'match_input': + out = float_values, + return (out, [str(value) for value in float_values], [int(value) for value in float_values]) + + def remap_values(self, values, target_min, target_max): + # Determine the current range + current_min = min(values) + current_max = max(values) + current_range = current_max - current_min + + # Determine the target range + target_range = target_max - target_min + + # Perform the linear interpolation for each value + remapped_values = [(value - current_min) / current_range * target_range + target_min for value in values] + + return remapped_values + + +class FloatToMask: + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "input_values": ("FLOAT", {"forceInput": True, "default": 0}), + "width": ("INT", {"default": 100, "min": 1}), + "height": ("INT", {"default": 100, "min": 1}), + }, + } + RETURN_TYPES = ("MASK",) + FUNCTION = "execute" + CATEGORY = "KJNodes/masking/generate" + DESCRIPTION = """ +Generates a batch of masks based on the input float values. +The batch size is determined by the length of the input float values. +Each mask is generated with the specified width and height. +""" + + def execute(self, input_values, width, height): + import pandas as pd + # Ensure input_values is a list + if isinstance(input_values, (float, int)): + input_values = [input_values] + elif isinstance(input_values, pd.Series): + input_values = input_values.tolist() + elif isinstance(input_values, list) and all(isinstance(item, list) for item in input_values): + input_values = [item for sublist in input_values for item in sublist] + + # Generate a batch of masks based on the input_values + masks = [] + for value in input_values: + # Assuming value is a float between 0 and 1 representing the mask's intensity + mask = torch.ones((height, width), dtype=torch.float32) * value + masks.append(mask) + masks_out = torch.stack(masks, dim=0) + + return(masks_out,) +class WeightScheduleExtend: + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "input_values_1": ("FLOAT", {"default": 0.0, "forceInput": True}), + "input_values_2": ("FLOAT", {"default": 0.0, "forceInput": True}), + "output_type": ( + [ + 'match_input', + 'list', + 'pandas series', + 'tensor', + ], + { + "default": 'match_input' + }), + }, + + } + RETURN_TYPES = ("FLOAT",) + FUNCTION = "execute" + CATEGORY = "KJNodes/weights" + DESCRIPTION = """ +Extends, and converts if needed, different value lists/series +""" + + def detect_input_type(self, input_values): + import pandas as pd + if isinstance(input_values, list): + return 'list' + elif isinstance(input_values, pd.Series): + return 'pandas series' + elif isinstance(input_values, torch.Tensor): + return 'tensor' + else: + raise ValueError("Unsupported input type") + + def execute(self, input_values_1, input_values_2, output_type): + import pandas as pd + input_type_1 = self.detect_input_type(input_values_1) + input_type_2 = self.detect_input_type(input_values_2) + # Convert input_values_2 to the same format as input_values_1 if they do not match + if not input_type_1 == input_type_2: + print("Converting input_values_2 to the same format as input_values_1") + if input_type_1 == 'pandas series': + # Convert input_values_2 to a pandas Series + float_values_2 = pd.Series(input_values_2) + elif input_type_1 == 'tensor': + # Convert input_values_2 to a tensor + float_values_2 = torch.tensor(input_values_2, dtype=torch.float32) + else: + print("Input types match, no conversion needed") + # If the types match, no conversion is needed + float_values_2 = input_values_2 + + float_values = input_values_1 + float_values_2 + + if output_type == 'list': + return float_values, + elif output_type == 'pandas series': + return pd.Series(float_values), + elif output_type == 'tensor': + if input_type_1 == 'pandas series': + return torch.tensor(float_values.values, dtype=torch.float32), + else: + return torch.tensor(float_values, dtype=torch.float32), + elif output_type == 'match_input': + return float_values, + else: + raise ValueError(f"Unsupported output_type: {output_type}") + +class FloatToSigmas: + @classmethod + def INPUT_TYPES(s): + return {"required": + { + "float_list": ("FLOAT", {"default": 0.0, "forceInput": True}), + } + } + RETURN_TYPES = ("SIGMAS",) + RETURN_NAMES = ("SIGMAS",) + CATEGORY = "KJNodes/noise" + FUNCTION = "customsigmas" + DESCRIPTION = """ +Creates a sigmas tensor from list of float values. + +""" + def customsigmas(self, float_list): + return torch.tensor(float_list, dtype=torch.float32), + +class SigmasToFloat: + @classmethod + def INPUT_TYPES(s): + return {"required": + { + "sigmas": ("SIGMAS",), + } + } + RETURN_TYPES = ("FLOAT",) + RETURN_NAMES = ("float",) + CATEGORY = "KJNodes/noise" + FUNCTION = "customsigmas" + DESCRIPTION = """ +Creates a float list from sigmas tensors. + +""" + def customsigmas(self, sigmas): + return sigmas.tolist(), + +class GLIGENTextBoxApplyBatchCoords: + @classmethod + def INPUT_TYPES(s): + return {"required": {"conditioning_to": ("CONDITIONING", ), + "latents": ("LATENT", ), + "clip": ("CLIP", ), + "gligen_textbox_model": ("GLIGEN", ), + "coordinates": ("STRING", {"forceInput": True}), + "text": ("STRING", {"multiline": True}), + "width": ("INT", {"default": 128, "min": 8, "max": 4096, "step": 8}), + "height": ("INT", {"default": 128, "min": 8, "max": 4096, "step": 8}), + }, + "optional": {"size_multiplier": ("FLOAT", {"default": [1.0], "forceInput": True})}, + } + RETURN_TYPES = ("CONDITIONING", "IMAGE", ) + RETURN_NAMES = ("conditioning", "coord_preview", ) + FUNCTION = "append" + CATEGORY = "KJNodes/experimental" + DESCRIPTION = """ +This node allows scheduling GLIGEN text box positions in a batch, +to be used with AnimateDiff-Evolved. Intended to pair with the +Spline Editor -node. + +GLIGEN model can be downloaded through the Manage's "Install Models" menu. +Or directly from here: +https://huggingface.co/comfyanonymous/GLIGEN_pruned_safetensors/tree/main + +Inputs: +- **latents** input is used to calculate batch size +- **clip** is your standard text encoder, use same as for the main prompt +- **gligen_textbox_model** connects to GLIGEN Loader +- **coordinates** takes a json string of points, directly compatible +with the spline editor node. +- **text** is the part of the prompt to set position for +- **width** and **height** are the size of the GLIGEN bounding box + +Outputs: +- **conditioning** goes between to clip text encode and the sampler +- **coord_preview** is an optional preview of the coordinates and +bounding boxes. + +""" + + def append(self, latents, coordinates, conditioning_to, clip, gligen_textbox_model, text, width, height, size_multiplier=[1.0]): + coordinates = json.loads(coordinates.replace("'", '"')) + coordinates = [(coord['x'], coord['y']) for coord in coordinates] + + batch_size = sum(tensor.size(0) for tensor in latents.values()) + if len(coordinates) != batch_size: + print("GLIGENTextBoxApplyBatchCoords WARNING: The number of coordinates does not match the number of latents") + + c = [] + _, cond_pooled = clip.encode_from_tokens(clip.tokenize(text), return_pooled=True) + + for t in conditioning_to: + n = [t[0], t[1].copy()] + + position_params_batch = [[] for _ in range(batch_size)] # Initialize a list of empty lists for each batch item + if len(size_multiplier) != batch_size: + size_multiplier = size_multiplier * (batch_size // len(size_multiplier)) + size_multiplier[:batch_size % len(size_multiplier)] + + for i in range(batch_size): + x_position, y_position = coordinates[i] + position_param = (cond_pooled, int((height // 8) * size_multiplier[i]), int((width // 8) * size_multiplier[i]), (y_position - height // 2) // 8, (x_position - width // 2) // 8) + position_params_batch[i].append(position_param) # Append position_param to the correct sublist + + prev = [] + if "gligen" in n[1]: + prev = n[1]['gligen'][2] + else: + prev = [[] for _ in range(batch_size)] + # Concatenate prev and position_params_batch, ensuring both are lists of lists + # and each sublist corresponds to a batch item + combined_position_params = [prev_item + batch_item for prev_item, batch_item in zip(prev, position_params_batch)] + n[1]['gligen'] = ("position_batched", gligen_textbox_model, combined_position_params) + c.append(n) + + image_height = latents['samples'].shape[-2] * 8 + image_width = latents['samples'].shape[-1] * 8 + plot_image_tensor = plot_coordinates_to_tensor(coordinates, image_height, image_width, height, width, size_multiplier, text) + + return (c, plot_image_tensor,) + +class CreateInstanceDiffusionTracking: + + RETURN_TYPES = ("TRACKING", "STRING", "INT", "INT", "INT", "INT",) + RETURN_NAMES = ("tracking", "prompt", "width", "height", "bbox_width", "bbox_height",) + FUNCTION = "tracking" + CATEGORY = "KJNodes/InstanceDiffusion" + DESCRIPTION = """ +Creates tracking data to be used with InstanceDiffusion: +https://github.com/logtd/ComfyUI-InstanceDiffusion + +InstanceDiffusion prompt format: +"class_id.class_name": "prompt", +for example: +"1.head": "((head))", +""" + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "coordinates": ("STRING", {"forceInput": True}), + "width": ("INT", {"default": 512,"min": 16, "max": 4096, "step": 1}), + "height": ("INT", {"default": 512,"min": 16, "max": 4096, "step": 1}), + "bbox_width": ("INT", {"default": 512,"min": 16, "max": 4096, "step": 1}), + "bbox_height": ("INT", {"default": 512,"min": 16, "max": 4096, "step": 1}), + "class_name": ("STRING", {"default": "class_name"}), + "class_id": ("INT", {"default": 0,"min": 0, "max": 255, "step": 1}), + "prompt": ("STRING", {"default": "prompt", "multiline": True}), + }, + "optional": { + "size_multiplier": ("FLOAT", {"default": [1.0], "forceInput": True}), + "fit_in_frame": ("BOOLEAN", {"default": True}), + } + } + + def tracking(self, coordinates, class_name, class_id, width, height, bbox_width, bbox_height, prompt, size_multiplier=[1.0], fit_in_frame=True): + # Define the number of images in the batch + coordinates = coordinates.replace("'", '"') + coordinates = json.loads(coordinates) + + tracked = {} + tracked[class_name] = {} + batch_size = len(coordinates) + # Initialize a list to hold the coordinates for the current ID + id_coordinates = [] + if not size_multiplier or len(size_multiplier) != batch_size: + size_multiplier = [0] * batch_size + else: + size_multiplier = size_multiplier * (batch_size // len(size_multiplier)) + size_multiplier[:batch_size % len(size_multiplier)] + for i, coord in enumerate(coordinates): + x = coord['x'] + y = coord['y'] + adjusted_bbox_width = bbox_width * size_multiplier[i] + adjusted_bbox_height = bbox_height * size_multiplier[i] + # Calculate the top left and bottom right coordinates + top_left_x = x - adjusted_bbox_width // 2 + top_left_y = y - adjusted_bbox_height // 2 + bottom_right_x = x + adjusted_bbox_width // 2 + bottom_right_y = y + adjusted_bbox_height // 2 + + if fit_in_frame: + # Clip the coordinates to the frame boundaries + top_left_x = max(0, top_left_x) + top_left_y = max(0, top_left_y) + bottom_right_x = min(width, bottom_right_x) + bottom_right_y = min(height, bottom_right_y) + # Ensure width and height are positive + adjusted_bbox_width = max(1, bottom_right_x - top_left_x) + adjusted_bbox_height = max(1, bottom_right_y - top_left_y) + + # Update the coordinates with the new width and height + bottom_right_x = top_left_x + adjusted_bbox_width + bottom_right_y = top_left_y + adjusted_bbox_height + + # Append the top left and bottom right coordinates to the list for the current ID + id_coordinates.append([top_left_x, top_left_y, bottom_right_x, bottom_right_y, width, height]) + + class_id = int(class_id) + # Assign the list of coordinates to the specified ID within the class_id dictionary + tracked[class_name][class_id] = id_coordinates + + prompt_string = "" + for class_name, class_data in tracked.items(): + for class_id in class_data.keys(): + class_id_str = str(class_id) + # Use the incoming prompt for each class name and ID + prompt_string += f'"{class_id_str}.{class_name}": "({prompt})",\n' + + # Remove the last comma and newline + prompt_string = prompt_string.rstrip(",\n") + + return (tracked, prompt_string, width, height, bbox_width, bbox_height) + +class AppendInstanceDiffusionTracking: + + RETURN_TYPES = ("TRACKING", "STRING",) + RETURN_NAMES = ("tracking", "prompt",) + FUNCTION = "append" + CATEGORY = "KJNodes/InstanceDiffusion" + DESCRIPTION = """ +Appends tracking data to be used with InstanceDiffusion: +https://github.com/logtd/ComfyUI-InstanceDiffusion + +""" + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "tracking_1": ("TRACKING", {"forceInput": True}), + "tracking_2": ("TRACKING", {"forceInput": True}), + }, + "optional": { + "prompt_1": ("STRING", {"default": "", "forceInput": True}), + "prompt_2": ("STRING", {"default": "", "forceInput": True}), + } + } + + def append(self, tracking_1, tracking_2, prompt_1="", prompt_2=""): + tracking_copy = tracking_1.copy() + # Check for existing class names and class IDs, and raise an error if they exist + for class_name, class_data in tracking_2.items(): + if class_name not in tracking_copy: + tracking_copy[class_name] = class_data + else: + # If the class name exists, merge the class data from tracking_2 into tracking_copy + # This will add new class IDs under the same class name without raising an error + tracking_copy[class_name].update(class_data) + prompt_string = prompt_1 + "," + prompt_2 + return (tracking_copy, prompt_string) + +class InterpolateCoords: + + RETURN_TYPES = ("STRING",) + RETURN_NAMES = ("coordinates",) + FUNCTION = "interpolate" + CATEGORY = "KJNodes/experimental" + DESCRIPTION = """ +Interpolates coordinates based on a curve. +""" + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "coordinates": ("STRING", {"forceInput": True}), + "interpolation_curve": ("FLOAT", {"forceInput": True}), + + }, + } + + def interpolate(self, coordinates, interpolation_curve): + # Parse the JSON string to get the list of coordinates + coordinates = json.loads(coordinates.replace("'", '"')) + + # Convert the list of dictionaries to a list of (x, y) tuples for easier processing + coordinates = [(coord['x'], coord['y']) for coord in coordinates] + + # Calculate the total length of the original path + path_length = sum(np.linalg.norm(np.array(coordinates[i]) - np.array(coordinates[i-1])) + for i in range(1, len(coordinates))) + + # Initialize variables for interpolation + interpolated_coords = [] + current_length = 0 + current_index = 0 + + # Iterate over the normalized curve + for normalized_length in interpolation_curve: + target_length = normalized_length * path_length # Convert to the original scale + while current_index < len(coordinates) - 1: + segment_start, segment_end = np.array(coordinates[current_index]), np.array(coordinates[current_index + 1]) + segment_length = np.linalg.norm(segment_end - segment_start) + if current_length + segment_length >= target_length: + break + current_length += segment_length + current_index += 1 + + # Interpolate between the last two points + if current_index < len(coordinates) - 1: + p1, p2 = np.array(coordinates[current_index]), np.array(coordinates[current_index + 1]) + segment_length = np.linalg.norm(p2 - p1) + if segment_length > 0: + t = (target_length - current_length) / segment_length + interpolated_point = p1 + t * (p2 - p1) + interpolated_coords.append(interpolated_point.tolist()) + else: + interpolated_coords.append(p1.tolist()) + else: + # If the target_length is at or beyond the end of the path, add the last coordinate + interpolated_coords.append(coordinates[-1]) + + # Convert back to string format if necessary + interpolated_coords_str = "[" + ", ".join([f"{{'x': {round(coord[0])}, 'y': {round(coord[1])}}}" for coord in interpolated_coords]) + "]" + print(interpolated_coords_str) + + return (interpolated_coords_str,) + +class DrawInstanceDiffusionTracking: + + RETURN_TYPES = ("IMAGE",) + RETURN_NAMES = ("image", ) + FUNCTION = "draw" + CATEGORY = "KJNodes/InstanceDiffusion" + DESCRIPTION = """ +Draws the tracking data from +CreateInstanceDiffusionTracking -node. + +""" + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "image": ("IMAGE", ), + "tracking": ("TRACKING", {"forceInput": True}), + "box_line_width": ("INT", {"default": 2, "min": 1, "max": 10, "step": 1}), + "draw_text": ("BOOLEAN", {"default": True}), + "font": (folder_paths.get_filename_list("kjnodes_fonts"), ), + "font_size": ("INT", {"default": 20}), + }, + } + + def draw(self, image, tracking, box_line_width, draw_text, font, font_size): + import matplotlib.cm as cm + + modified_images = [] + + colormap = cm.get_cmap('rainbow', len(tracking)) + if draw_text: + font_path = folder_paths.get_full_path("kjnodes_fonts", font) + font = ImageFont.truetype(font_path, font_size) + + # Iterate over each image in the batch + for i in range(image.shape[0]): + # Extract the current image and convert it to a PIL image + current_image = image[i, :, :, :].permute(2, 0, 1) + pil_image = transforms.ToPILImage()(current_image) + + draw = ImageDraw.Draw(pil_image) + + # Iterate over the bounding boxes for the current image + for j, (class_name, class_data) in enumerate(tracking.items()): + for class_id, bbox_list in class_data.items(): + # Check if the current index is within the bounds of the bbox_list + if i < len(bbox_list): + bbox = bbox_list[i] + # Ensure bbox is a list or tuple before unpacking + if isinstance(bbox, (list, tuple)): + x1, y1, x2, y2, _, _ = bbox + # Convert coordinates to integers + x1, y1, x2, y2 = int(x1), int(y1), int(x2), int(y2) + # Generate a color from the rainbow colormap + color = tuple(int(255 * x) for x in colormap(j / len(tracking)))[:3] + # Draw the bounding box on the image with the generated color + draw.rectangle([x1, y1, x2, y2], outline=color, width=box_line_width) + if draw_text: + # Draw the class name and ID as text above the box with the generated color + text = f"{class_id}.{class_name}" + # Calculate the width and height of the text + _, _, text_width, text_height = draw.textbbox((0, 0), text=text, font=font) + # Position the text above the top-left corner of the box + text_position = (x1, y1 - text_height) + draw.text(text_position, text, fill=color, font=font) + else: + print(f"Unexpected data type for bbox: {type(bbox)}") + + # Convert the drawn image back to a torch tensor and adjust back to (H, W, C) + modified_image_tensor = transforms.ToTensor()(pil_image).permute(1, 2, 0) + modified_images.append(modified_image_tensor) + + # Stack the modified images back into a batch + image_tensor_batch = torch.stack(modified_images).cpu().float() + + return image_tensor_batch, + +class PointsEditor: + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "points_store": ("STRING", {"multiline": False}), + "coordinates": ("STRING", {"multiline": False}), + "neg_coordinates": ("STRING", {"multiline": False}), + "bbox_store": ("STRING", {"multiline": False}), + "bboxes": ("STRING", {"multiline": False}), + "bbox_format": ( + [ + 'xyxy', + 'xywh', + ], + ), + "width": ("INT", {"default": 512, "min": 8, "max": 4096, "step": 8}), + "height": ("INT", {"default": 512, "min": 8, "max": 4096, "step": 8}), + "normalize": ("BOOLEAN", {"default": False}), + }, + "optional": { + "bg_image": ("IMAGE", ), + }, + } + + RETURN_TYPES = ("STRING", "STRING", "BBOX", "MASK", "IMAGE") + RETURN_NAMES = ("positive_coords", "negative_coords", "bbox", "bbox_mask", "cropped_image") + FUNCTION = "pointdata" + CATEGORY = "KJNodes/experimental" + DESCRIPTION = """ +# WORK IN PROGRESS +Do not count on this as part of your workflow yet, +probably contains lots of bugs and stability is not +guaranteed!! + +## Graphical editor to create coordinates + +**Shift + click** to add a positive (green) point. +**Shift + right click** to add a negative (red) point. +**Ctrl + click** to draw a box. +**Right click on a point** to delete it. +Note that you can't delete from start/end of the points array. + +To add an image select the node and copy/paste or drag in the image. +Or from the bg_image input on queue (first frame of the batch). + +**THE IMAGE IS SAVED TO THE NODE AND WORKFLOW METADATA** +you can clear the image from the context menu by right clicking on the canvas + +""" + + def pointdata(self, points_store, bbox_store, width, height, coordinates, neg_coordinates, normalize, bboxes, bbox_format="xyxy", bg_image=None): + coordinates = json.loads(coordinates) + pos_coordinates = [] + for coord in coordinates: + coord['x'] = int(round(coord['x'])) + coord['y'] = int(round(coord['y'])) + if normalize: + norm_x = coord['x'] / width + norm_y = coord['y'] / height + pos_coordinates.append({'x': norm_x, 'y': norm_y}) + else: + pos_coordinates.append({'x': coord['x'], 'y': coord['y']}) + + if neg_coordinates: + coordinates = json.loads(neg_coordinates) + neg_coordinates = [] + for coord in coordinates: + coord['x'] = int(round(coord['x'])) + coord['y'] = int(round(coord['y'])) + if normalize: + norm_x = coord['x'] / width + norm_y = coord['y'] / height + neg_coordinates.append({'x': norm_x, 'y': norm_y}) + else: + neg_coordinates.append({'x': coord['x'], 'y': coord['y']}) + + # Create a blank mask + mask = np.zeros((height, width), dtype=np.uint8) + bboxes = json.loads(bboxes) + print(bboxes) + valid_bboxes = [] + for bbox in bboxes: + if (bbox.get("startX") is None or + bbox.get("startY") is None or + bbox.get("endX") is None or + bbox.get("endY") is None): + continue # Skip this bounding box if any value is None + else: + # Ensure that endX and endY are greater than startX and startY + x_min = min(int(bbox["startX"]), int(bbox["endX"])) + y_min = min(int(bbox["startY"]), int(bbox["endY"])) + x_max = max(int(bbox["startX"]), int(bbox["endX"])) + y_max = max(int(bbox["startY"]), int(bbox["endY"])) + + valid_bboxes.append((x_min, y_min, x_max, y_max)) + + bboxes_xyxy = [] + for bbox in valid_bboxes: + x_min, y_min, x_max, y_max = bbox + bboxes_xyxy.append((x_min, y_min, x_max, y_max)) + mask[y_min:y_max, x_min:x_max] = 1 # Fill the bounding box area with 1s + + if bbox_format == "xywh": + bboxes_xywh = [] + for bbox in valid_bboxes: + x_min, y_min, x_max, y_max = bbox + width = x_max - x_min + height = y_max - y_min + bboxes_xywh.append((x_min, y_min, width, height)) + bboxes = bboxes_xywh + else: + bboxes = bboxes_xyxy + + mask_tensor = torch.from_numpy(mask) + mask_tensor = mask_tensor.unsqueeze(0).float().cpu() + + if bg_image is not None and len(valid_bboxes) > 0: + x_min, y_min, x_max, y_max = bboxes[0] + cropped_image = bg_image[:, y_min:y_max, x_min:x_max, :] + + elif bg_image is not None: + cropped_image = bg_image + + if bg_image is None: + return (json.dumps(pos_coordinates), json.dumps(neg_coordinates), bboxes, mask_tensor) + else: + transform = transforms.ToPILImage() + image = transform(bg_image[0].permute(2, 0, 1)) + buffered = io.BytesIO() + image.save(buffered, format="JPEG", quality=75) + + # Step 3: Encode the image bytes to a Base64 string + img_bytes = buffered.getvalue() + img_base64 = base64.b64encode(img_bytes).decode('utf-8') + + return { + "ui": {"bg_image": [img_base64]}, + "result": (json.dumps(pos_coordinates), json.dumps(neg_coordinates), bboxes, mask_tensor, cropped_image) + } + +class CutAndDragOnPath: + RETURN_TYPES = ("IMAGE", "MASK",) + RETURN_NAMES = ("image","mask", ) + FUNCTION = "cutanddrag" + CATEGORY = "KJNodes/image" + DESCRIPTION = """ +Cuts the masked area from the image, and drags it along the path. If inpaint is enabled, and no bg_image is provided, the cut area is filled using cv2 TELEA algorithm. +""" + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "image": ("IMAGE",), + "coordinates": ("STRING", {"forceInput": True}), + "mask": ("MASK",), + "frame_width": ("INT", {"default": 512,"min": 16, "max": 4096, "step": 1}), + "frame_height": ("INT", {"default": 512,"min": 16, "max": 4096, "step": 1}), + "inpaint": ("BOOLEAN", {"default": True}), + }, + "optional": { + "bg_image": ("IMAGE",), + } + } + + def cutanddrag(self, image, coordinates, mask, frame_width, frame_height, inpaint, bg_image=None): + # Parse coordinates + coords_list = parse_json_tracks(coordinates) + + batch_size = len(coords_list[0]) + images_list = [] + masks_list = [] + + # Convert input image and mask to PIL + input_image = tensor2pil(image)[0] + input_mask = tensor2pil(mask)[0] + + # Find masked region bounds + mask_array = np.array(input_mask) + y_indices, x_indices = np.where(mask_array > 0) + if len(x_indices) == 0 or len(y_indices) == 0: + return (image, mask) + + x_min, x_max = x_indices.min(), x_indices.max() + y_min, y_max = y_indices.min(), y_indices.max() + + # Cut out the masked region + cut_width = x_max - x_min + cut_height = y_max - y_min + cut_image = input_image.crop((x_min, y_min, x_max, y_max)) + cut_mask = input_mask.crop((x_min, y_min, x_max, y_max)) + + # Create inpainted background + if bg_image is None: + background = input_image.copy() + # Inpaint the cut area + if inpaint: + import cv2 + border = 5 # Create small border around cut area for better inpainting + fill_mask = Image.new("L", background.size, 0) + draw = ImageDraw.Draw(fill_mask) + draw.rectangle([x_min-border, y_min-border, x_max+border, y_max+border], fill=255) + background = cv2.inpaint( + np.array(background), + np.array(fill_mask), + inpaintRadius=3, + flags=cv2.INPAINT_TELEA + ) + background = Image.fromarray(background) + else: + background = tensor2pil(bg_image)[0] + + # Create batch of images with cut region at different positions + for i in range(batch_size): + # Create new image + new_image = background.copy() + new_mask = Image.new("L", (frame_width, frame_height), 0) + + # Get target position from coordinates + for coords in coords_list: + target_x = int(coords[i]['x'] - cut_width/2) + target_y = int(coords[i]['y'] - cut_height/2) + + # Paste cut region at new position + new_image.paste(cut_image, (target_x, target_y), cut_mask) + new_mask.paste(cut_mask, (target_x, target_y)) + + # Convert to tensor and append + image_tensor = pil2tensor(new_image) + mask_tensor = pil2tensor(new_mask) + + images_list.append(image_tensor) + masks_list.append(mask_tensor) + + # Stack tensors into batches + out_images = torch.cat(images_list, dim=0).cpu().float() + out_masks = torch.cat(masks_list, dim=0) + + return (out_images, out_masks) \ No newline at end of file diff --git a/ComfyUI/custom_nodes/comfyui-kjnodes/nodes/image_nodes.py b/ComfyUI/custom_nodes/comfyui-kjnodes/nodes/image_nodes.py new file mode 100644 index 0000000000000000000000000000000000000000..7cb9cae1189534ed1d35bd4d018c85a28a5f29d1 --- /dev/null +++ b/ComfyUI/custom_nodes/comfyui-kjnodes/nodes/image_nodes.py @@ -0,0 +1,3797 @@ +import numpy as np +import time +import torch +import torch.nn.functional as F +import torchvision.transforms as T +import io +import base64 +import random +import math +import os +import re +import json +import importlib +from PIL.PngImagePlugin import PngInfo +try: + import cv2 +except: + print("OpenCV not installed") + pass +from PIL import ImageGrab, ImageDraw, ImageFont, Image, ImageOps + +from nodes import MAX_RESOLUTION, SaveImage +from comfy_extras.nodes_mask import ImageCompositeMasked +from comfy.cli_args import args +from comfy.utils import ProgressBar, common_upscale +import folder_paths +from comfy import model_management +try: + from server import PromptServer +except: + PromptServer = None +from concurrent.futures import ThreadPoolExecutor + +script_directory = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + +class ImagePass: + @classmethod + def INPUT_TYPES(s): + return { + "required": { + }, + "optional": { + "image": ("IMAGE",), + }, + } + RETURN_TYPES = ("IMAGE",) + FUNCTION = "passthrough" + CATEGORY = "KJNodes/image" + DESCRIPTION = """ +Passes the image through without modifying it. +""" + + def passthrough(self, image=None): + return image, + +class ColorMatch: + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "image_ref": ("IMAGE",), + "image_target": ("IMAGE",), + "method": ( + [ + 'mkl', + 'hm', + 'reinhard', + 'mvgd', + 'hm-mvgd-hm', + 'hm-mkl-hm', + ], { + "default": 'mkl' + }), + }, + "optional": { + "strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.01}), + "multithread": ("BOOLEAN", {"default": True}), + } + } + + CATEGORY = "KJNodes/image" + + RETURN_TYPES = ("IMAGE",) + RETURN_NAMES = ("image",) + FUNCTION = "colormatch" + DESCRIPTION = """ +color-matcher enables color transfer across images which comes in handy for automatic +color-grading of photographs, paintings and film sequences as well as light-field +and stopmotion corrections. + +The methods behind the mappings are based on the approach from Reinhard et al., +the Monge-Kantorovich Linearization (MKL) as proposed by Pitie et al. and our analytical solution +to a Multi-Variate Gaussian Distribution (MVGD) transfer in conjunction with classical histogram +matching. As shown below our HM-MVGD-HM compound outperforms existing methods. +https://github.com/hahnec/color-matcher/ + +""" + + def colormatch(self, image_ref, image_target, method, strength=1.0, multithread=True): + try: + from color_matcher import ColorMatcher + except: + raise Exception("Can't import color-matcher, did you install requirements.txt? Manual install: pip install color-matcher") + + image_ref = image_ref.cpu() + image_target = image_target.cpu() + batch_size = image_target.size(0) + + images_target = image_target.squeeze() + images_ref = image_ref.squeeze() + + image_ref_np = images_ref.numpy() + images_target_np = images_target.numpy() + + def process(i): + cm = ColorMatcher() + image_target_np_i = images_target_np if batch_size == 1 else images_target[i].numpy() + image_ref_np_i = image_ref_np if image_ref.size(0) == 1 else images_ref[i].numpy() + try: + image_result = cm.transfer(src=image_target_np_i, ref=image_ref_np_i, method=method) + image_result = image_target_np_i + strength * (image_result - image_target_np_i) + return torch.from_numpy(image_result) + except Exception as e: + print(f"Thread {i} error: {e}") + return torch.from_numpy(image_target_np_i) # fallback + + if multithread and batch_size > 1: + max_threads = min(os.cpu_count() or 1, batch_size) + with ThreadPoolExecutor(max_workers=max_threads) as executor: + out = list(executor.map(process, range(batch_size))) + else: + out = [process(i) for i in range(batch_size)] + + out = torch.stack(out, dim=0).to(torch.float32) + out.clamp_(0, 1) + return (out,) + +class SaveImageWithAlpha: + def __init__(self): + self.output_dir = folder_paths.get_output_directory() + self.type = "output" + self.prefix_append = "" + + @classmethod + def INPUT_TYPES(s): + return {"required": + {"images": ("IMAGE", ), + "mask": ("MASK", ), + "filename_prefix": ("STRING", {"default": "ComfyUI"})}, + "hidden": {"prompt": "PROMPT", "extra_pnginfo": "EXTRA_PNGINFO"}, + } + + RETURN_TYPES = () + FUNCTION = "save_images_alpha" + OUTPUT_NODE = True + CATEGORY = "KJNodes/image" + DESCRIPTION = """ +Saves an image and mask as .PNG with the mask as the alpha channel. +""" + + def save_images_alpha(self, images, mask, filename_prefix="ComfyUI_image_with_alpha", prompt=None, extra_pnginfo=None): + from PIL.PngImagePlugin import PngInfo + 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]) + results = list() + if mask.dtype == torch.float16: + mask = mask.to(torch.float32) + def file_counter(): + max_counter = 0 + # Loop through the existing files + for existing_file in os.listdir(full_output_folder): + # Check if the file matches the expected format + match = re.fullmatch(fr"{filename}_(\d+)_?\.[a-zA-Z0-9]+", existing_file) + if match: + # Extract the numeric portion of the filename + file_counter = int(match.group(1)) + # Update the maximum counter value if necessary + if file_counter > max_counter: + max_counter = file_counter + return max_counter + + for image, alpha in zip(images, mask): + i = 255. * image.cpu().numpy() + a = 255. * alpha.cpu().numpy() + img = Image.fromarray(np.clip(i, 0, 255).astype(np.uint8)) + + # Resize the mask to match the image size + a_resized = Image.fromarray(a).resize(img.size, Image.LANCZOS) + a_resized = np.clip(a_resized, 0, 255).astype(np.uint8) + img.putalpha(Image.fromarray(a_resized, mode='L')) + metadata = None + if not args.disable_metadata: + metadata = PngInfo() + if prompt is not None: + metadata.add_text("prompt", json.dumps(prompt)) + if extra_pnginfo is not None: + for x in extra_pnginfo: + metadata.add_text(x, json.dumps(extra_pnginfo[x])) + + # Increment the counter by 1 to get the next available value + counter = file_counter() + 1 + file = f"{filename}_{counter:05}.png" + img.save(os.path.join(full_output_folder, file), pnginfo=metadata, compress_level=4) + results.append({ + "filename": file, + "subfolder": subfolder, + "type": self.type + }) + + return { "ui": { "images": results } } + +class ImageConcanate: + @classmethod + def INPUT_TYPES(s): + return {"required": { + "image1": ("IMAGE",), + "image2": ("IMAGE",), + "direction": ( + [ 'right', + 'down', + 'left', + 'up', + ], + { + "default": 'right' + }), + "match_image_size": ("BOOLEAN", {"default": True}), + }} + + RETURN_TYPES = ("IMAGE",) + FUNCTION = "concatenate" + CATEGORY = "KJNodes/image" + DESCRIPTION = """ +Concatenates the image2 to image1 in the specified direction. +""" + + def concatenate(self, image1, image2, direction, match_image_size, first_image_shape=None): + # Check if the batch sizes are different + batch_size1 = image1.shape[0] + batch_size2 = image2.shape[0] + + if batch_size1 != batch_size2: + # Calculate the number of repetitions needed + max_batch_size = max(batch_size1, batch_size2) + repeats1 = max_batch_size - batch_size1 + repeats2 = max_batch_size - batch_size2 + + # Repeat the last image to match the largest batch size + if repeats1 > 0: + last_image1 = image1[-1].unsqueeze(0).repeat(repeats1, 1, 1, 1) + image1 = torch.cat([image1.clone(), last_image1], dim=0) + if repeats2 > 0: + last_image2 = image2[-1].unsqueeze(0).repeat(repeats2, 1, 1, 1) + image2 = torch.cat([image2.clone(), last_image2], dim=0) + + if match_image_size: + # Use first_image_shape if provided; otherwise, default to image1's shape + target_shape = first_image_shape if first_image_shape is not None else image1.shape + + original_height = image2.shape[1] + original_width = image2.shape[2] + original_aspect_ratio = original_width / original_height + + if direction in ['left', 'right']: + # Match the height and adjust the width to preserve aspect ratio + target_height = target_shape[1] # B, H, W, C format + target_width = int(target_height * original_aspect_ratio) + elif direction in ['up', 'down']: + # Match the width and adjust the height to preserve aspect ratio + target_width = target_shape[2] # B, H, W, C format + target_height = int(target_width / original_aspect_ratio) + + # Adjust image2 to the expected format for common_upscale + image2_for_upscale = image2.movedim(-1, 1) # Move C to the second position (B, C, H, W) + + # Resize image2 to match the target size while preserving aspect ratio + image2_resized = common_upscale(image2_for_upscale, target_width, target_height, "lanczos", "disabled") + + # Adjust image2 back to the original format (B, H, W, C) after resizing + image2_resized = image2_resized.movedim(1, -1) + else: + image2_resized = image2 + + # Ensure both images have the same number of channels + channels_image1 = image1.shape[-1] + channels_image2 = image2_resized.shape[-1] + + if channels_image1 != channels_image2: + if channels_image1 < channels_image2: + # Add alpha channel to image1 if image2 has it + alpha_channel = torch.ones((*image1.shape[:-1], channels_image2 - channels_image1), device=image1.device) + image1 = torch.cat((image1, alpha_channel), dim=-1) + else: + # Add alpha channel to image2 if image1 has it + alpha_channel = torch.ones((*image2_resized.shape[:-1], channels_image1 - channels_image2), device=image2_resized.device) + image2_resized = torch.cat((image2_resized, alpha_channel), dim=-1) + + + # Concatenate based on the specified direction + if direction == 'right': + concatenated_image = torch.cat((image1, image2_resized), dim=2) # Concatenate along width + elif direction == 'down': + concatenated_image = torch.cat((image1, image2_resized), dim=1) # Concatenate along height + elif direction == 'left': + concatenated_image = torch.cat((image2_resized, image1), dim=2) # Concatenate along width + elif direction == 'up': + concatenated_image = torch.cat((image2_resized, image1), dim=1) # Concatenate along height + return concatenated_image, + +import torch # Make sure you have PyTorch installed + +class ImageConcatFromBatch: + @classmethod + def INPUT_TYPES(s): + return {"required": { + "images": ("IMAGE",), + "num_columns": ("INT", {"default": 3, "min": 1, "max": 255, "step": 1}), + "match_image_size": ("BOOLEAN", {"default": False}), + "max_resolution": ("INT", {"default": 4096}), + }, + } + + RETURN_TYPES = ("IMAGE",) + FUNCTION = "concat" + CATEGORY = "KJNodes/image" + DESCRIPTION = """ + Concatenates images from a batch into a grid with a specified number of columns. + """ + + def concat(self, images, num_columns, match_image_size, max_resolution): + # Assuming images is a batch of images (B, H, W, C) + batch_size, height, width, channels = images.shape + num_rows = (batch_size + num_columns - 1) // num_columns # Calculate number of rows + + print(f"Initial dimensions: batch_size={batch_size}, height={height}, width={width}, channels={channels}") + print(f"num_rows={num_rows}, num_columns={num_columns}") + + if match_image_size: + target_shape = images[0].shape + + resized_images = [] + for image in images: + original_height = image.shape[0] + original_width = image.shape[1] + original_aspect_ratio = original_width / original_height + + if original_aspect_ratio > 1: + target_height = target_shape[0] + target_width = int(target_height * original_aspect_ratio) + else: + target_width = target_shape[1] + target_height = int(target_width / original_aspect_ratio) + + print(f"Resizing image from ({original_height}, {original_width}) to ({target_height}, {target_width})") + + # Resize the image to match the target size while preserving aspect ratio + resized_image = common_upscale(image.movedim(-1, 0), target_width, target_height, "lanczos", "disabled") + resized_image = resized_image.movedim(0, -1) # Move channels back to the last dimension + resized_images.append(resized_image) + + # Convert the list of resized images back to a tensor + images = torch.stack(resized_images) + + height, width = target_shape[:2] # Update height and width + + # Initialize an empty grid + grid_height = num_rows * height + grid_width = num_columns * width + + print(f"Grid dimensions before scaling: grid_height={grid_height}, grid_width={grid_width}") + + # Original scale factor calculation remains unchanged + scale_factor = min(max_resolution / grid_height, max_resolution / grid_width, 1.0) + + # Apply scale factor to height and width + scaled_height = height * scale_factor + scaled_width = width * scale_factor + + # Round scaled dimensions to the nearest number divisible by 8 + height = max(1, int(round(scaled_height / 8) * 8)) + width = max(1, int(round(scaled_width / 8) * 8)) + + if abs(scaled_height - height) > 4: + height = max(1, int(round((scaled_height + 4) / 8) * 8)) + if abs(scaled_width - width) > 4: + width = max(1, int(round((scaled_width + 4) / 8) * 8)) + + # Recalculate grid dimensions with adjusted height and width + grid_height = num_rows * height + grid_width = num_columns * width + print(f"Grid dimensions after scaling: grid_height={grid_height}, grid_width={grid_width}") + print(f"Final image dimensions: height={height}, width={width}") + + grid = torch.zeros((grid_height, grid_width, channels), dtype=images.dtype) + + for idx, image in enumerate(images): + resized_image = torch.nn.functional.interpolate(image.unsqueeze(0).permute(0, 3, 1, 2), size=(height, width), mode="bilinear").squeeze().permute(1, 2, 0) + row = idx // num_columns + col = idx % num_columns + grid[row*height:(row+1)*height, col*width:(col+1)*width, :] = resized_image + + return grid.unsqueeze(0), + + +class ImageGridComposite2x2: + @classmethod + def INPUT_TYPES(s): + return {"required": { + "image1": ("IMAGE",), + "image2": ("IMAGE",), + "image3": ("IMAGE",), + "image4": ("IMAGE",), + }} + + RETURN_TYPES = ("IMAGE",) + FUNCTION = "compositegrid" + CATEGORY = "KJNodes/image" + DESCRIPTION = """ +Concatenates the 4 input images into a 2x2 grid. +""" + + def compositegrid(self, image1, image2, image3, image4): + top_row = torch.cat((image1, image2), dim=2) + bottom_row = torch.cat((image3, image4), dim=2) + grid = torch.cat((top_row, bottom_row), dim=1) + return (grid,) + +class ImageGridComposite3x3: + @classmethod + def INPUT_TYPES(s): + return {"required": { + "image1": ("IMAGE",), + "image2": ("IMAGE",), + "image3": ("IMAGE",), + "image4": ("IMAGE",), + "image5": ("IMAGE",), + "image6": ("IMAGE",), + "image7": ("IMAGE",), + "image8": ("IMAGE",), + "image9": ("IMAGE",), + }} + + RETURN_TYPES = ("IMAGE",) + FUNCTION = "compositegrid" + CATEGORY = "KJNodes/image" + DESCRIPTION = """ +Concatenates the 9 input images into a 3x3 grid. +""" + + def compositegrid(self, image1, image2, image3, image4, image5, image6, image7, image8, image9): + top_row = torch.cat((image1, image2, image3), dim=2) + mid_row = torch.cat((image4, image5, image6), dim=2) + bottom_row = torch.cat((image7, image8, image9), dim=2) + grid = torch.cat((top_row, mid_row, bottom_row), dim=1) + return (grid,) + +class ImageBatchTestPattern: + @classmethod + def INPUT_TYPES(s): + return {"required": { + "batch_size": ("INT", {"default": 1,"min": 1, "max": 255, "step": 1}), + "start_from": ("INT", {"default": 0,"min": 0, "max": 255, "step": 1}), + "text_x": ("INT", {"default": 256,"min": 0, "max": 4096, "step": 1}), + "text_y": ("INT", {"default": 256,"min": 0, "max": 4096, "step": 1}), + "width": ("INT", {"default": 512,"min": 16, "max": 4096, "step": 1}), + "height": ("INT", {"default": 512,"min": 16, "max": 4096, "step": 1}), + "font": (folder_paths.get_filename_list("kjnodes_fonts"), ), + "font_size": ("INT", {"default": 255,"min": 8, "max": 4096, "step": 1}), + }} + + RETURN_TYPES = ("IMAGE",) + FUNCTION = "generatetestpattern" + CATEGORY = "KJNodes/text" + + def generatetestpattern(self, batch_size, font, font_size, start_from, width, height, text_x, text_y): + out = [] + # Generate the sequential numbers for each image + numbers = np.arange(start_from, start_from + batch_size) + font_path = folder_paths.get_full_path("kjnodes_fonts", font) + + for number in numbers: + # Create a black image with the number as a random color text + image = Image.new("RGB", (width, height), color='black') + draw = ImageDraw.Draw(image) + + # Generate a random color for the text + font_color = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)) + + font = ImageFont.truetype(font_path, font_size) + + # Get the size of the text and position it in the center + text = str(number) + + try: + draw.text((text_x, text_y), text, font=font, fill=font_color, features=['-liga']) + except: + draw.text((text_x, text_y), text, font=font, fill=font_color,) + + # Convert the image to a numpy array and normalize the pixel values + image_np = np.array(image).astype(np.float32) / 255.0 + image_tensor = torch.from_numpy(image_np).unsqueeze(0) + out.append(image_tensor) + out_tensor = torch.cat(out, dim=0) + + return (out_tensor,) + +class ImageGrabPIL: + + @classmethod + def IS_CHANGED(cls): + + return + + RETURN_TYPES = ("IMAGE",) + RETURN_NAMES = ("image",) + FUNCTION = "screencap" + CATEGORY = "KJNodes/image" + DESCRIPTION = """ +Captures an area specified by screen coordinates. +Can be used for realtime diffusion with autoqueue. +""" + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "x": ("INT", {"default": 0,"min": 0, "max": 4096, "step": 1}), + "y": ("INT", {"default": 0,"min": 0, "max": 4096, "step": 1}), + "width": ("INT", {"default": 512,"min": 0, "max": 4096, "step": 1}), + "height": ("INT", {"default": 512,"min": 0, "max": 4096, "step": 1}), + "num_frames": ("INT", {"default": 1,"min": 1, "max": 255, "step": 1}), + "delay": ("FLOAT", {"default": 0.1,"min": 0.0, "max": 10.0, "step": 0.01}), + }, + } + + def screencap(self, x, y, width, height, num_frames, delay): + start_time = time.time() + captures = [] + bbox = (x, y, x + width, y + height) + + for _ in range(num_frames): + # Capture screen + screen_capture = ImageGrab.grab(bbox=bbox) + screen_capture_torch = torch.from_numpy(np.array(screen_capture, dtype=np.float32) / 255.0).unsqueeze(0) + captures.append(screen_capture_torch) + + # Wait for a short delay if more than one frame is to be captured + if num_frames > 1: + time.sleep(delay) + + elapsed_time = time.time() - start_time + print(f"screengrab took {elapsed_time} seconds.") + + return (torch.cat(captures, dim=0),) + +class Screencap_mss: + + @classmethod + def IS_CHANGED(s, **kwargs): + return float("NaN") + + RETURN_TYPES = ("IMAGE",) + RETURN_NAMES = ("image",) + FUNCTION = "screencap" + CATEGORY = "KJNodes/image" + DESCRIPTION = """ +Captures an area specified by screen coordinates. +Can be used for realtime diffusion with autoqueue. +""" + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "x": ("INT", {"default": 0,"min": 0, "max": 10000, "step": 1}), + "y": ("INT", {"default": 0,"min": 0, "max": 10000, "step": 1}), + "width": ("INT", {"default": 512,"min": 0, "max": 10000, "step": 1}), + "height": ("INT", {"default": 512,"min": 0, "max": 10000, "step": 1}), + "num_frames": ("INT", {"default": 1,"min": 1, "max": 255, "step": 1}), + "delay": ("FLOAT", {"default": 0.1,"min": 0.0, "max": 10.0, "step": 0.01}), + }, + } + + def screencap(self, x, y, width, height, num_frames, delay): + from mss import mss + captures = [] + with mss() as sct: + bbox = {'top': y, 'left': x, 'width': width, 'height': height} + + for _ in range(num_frames): + sct_img = sct.grab(bbox) + img_np = np.array(sct_img) + img_torch = torch.from_numpy(img_np[..., [2, 1, 0]]).float() / 255.0 + captures.append(img_torch) + + if num_frames > 1: + time.sleep(delay) + + return (torch.stack(captures, 0),) + +class WebcamCaptureCV2: + + @classmethod + def IS_CHANGED(cls): + return + + RETURN_TYPES = ("IMAGE",) + RETURN_NAMES = ("image",) + FUNCTION = "capture" + CATEGORY = "KJNodes/experimental" + DESCRIPTION = """ +Captures a frame from a webcam using CV2. +Can be used for realtime diffusion with autoqueue. +""" + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "x": ("INT", {"default": 0,"min": 0, "max": 4096, "step": 1}), + "y": ("INT", {"default": 0,"min": 0, "max": 4096, "step": 1}), + "width": ("INT", {"default": 512,"min": 0, "max": 4096, "step": 1}), + "height": ("INT", {"default": 512,"min": 0, "max": 4096, "step": 1}), + "cam_index": ("INT", {"default": 0,"min": 0, "max": 255, "step": 1}), + "release": ("BOOLEAN", {"default": False}), + }, + } + + def capture(self, x, y, cam_index, width, height, release): + # Check if the camera index has changed or the capture object doesn't exist + if not hasattr(self, "cap") or self.cap is None or self.current_cam_index != cam_index: + if hasattr(self, "cap") and self.cap is not None: + self.cap.release() + self.current_cam_index = cam_index + self.cap = cv2.VideoCapture(cam_index) + try: + self.cap.set(cv2.CAP_PROP_FRAME_WIDTH, width) + self.cap.set(cv2.CAP_PROP_FRAME_HEIGHT, height) + except: + pass + if not self.cap.isOpened(): + raise Exception("Could not open webcam") + + ret, frame = self.cap.read() + if not ret: + raise Exception("Failed to capture image from webcam") + + # Crop the frame to the specified bbox + frame = frame[y:y+height, x:x+width] + img_torch = torch.from_numpy(frame[..., [2, 1, 0]]).float() / 255.0 + + if release: + self.cap.release() + self.cap = None + + return (img_torch.unsqueeze(0),) + +class AddLabel: + @classmethod + def INPUT_TYPES(s): + return {"required": { + "image":("IMAGE",), + "text_x": ("INT", {"default": 10, "min": 0, "max": 4096, "step": 1}), + "text_y": ("INT", {"default": 2, "min": 0, "max": 4096, "step": 1}), + "height": ("INT", {"default": 48, "min": -1, "max": 4096, "step": 1}), + "font_size": ("INT", {"default": 32, "min": 0, "max": 4096, "step": 1}), + "font_color": ("STRING", {"default": "white"}), + "label_color": ("STRING", {"default": "black"}), + "font": (folder_paths.get_filename_list("kjnodes_fonts"), ), + "text": ("STRING", {"default": "Text"}), + "direction": ( + [ 'up', + 'down', + 'left', + 'right', + 'overlay' + ], + { + "default": 'up' + }), + }, + "optional":{ + "caption": ("STRING", {"default": "", "forceInput": True}), + } + } + RETURN_TYPES = ("IMAGE",) + FUNCTION = "addlabel" + CATEGORY = "KJNodes/text" + DESCRIPTION = """ +Creates a new with the given text, and concatenates it to +either above or below the input image. +Note that this changes the input image's height! +Fonts are loaded from this folder: +ComfyUI/custom_nodes/ComfyUI-KJNodes/fonts +""" + + def addlabel(self, image, text_x, text_y, text, height, font_size, font_color, label_color, font, direction, caption=""): + batch_size = image.shape[0] + width = image.shape[2] + + font_path = os.path.join(script_directory, "fonts", "TTNorms-Black.otf") if font == "TTNorms-Black.otf" else folder_paths.get_full_path("kjnodes_fonts", font) + + def process_image(input_image, caption_text): + font = ImageFont.truetype(font_path, font_size) + words = caption_text.split() + lines = [] + current_line = [] + current_line_width = 0 + + for word in words: + word_width = font.getbbox(word)[2] + if current_line_width + word_width <= width - 2 * text_x: + current_line.append(word) + current_line_width += word_width + font.getbbox(" ")[2] # Add space width + else: + lines.append(" ".join(current_line)) + current_line = [word] + current_line_width = word_width + + if current_line: + lines.append(" ".join(current_line)) + + if direction == 'overlay': + pil_image = Image.fromarray((input_image.cpu().numpy() * 255).astype(np.uint8)) + else: + if height == -1: + # Adjust the image height automatically + margin = 8 + required_height = (text_y + len(lines) * font_size) + margin # Calculate required height + pil_image = Image.new("RGB", (width, required_height), label_color) + else: + # Initialize with a minimal height + label_image = Image.new("RGB", (width, height), label_color) + pil_image = label_image + + draw = ImageDraw.Draw(pil_image) + + + y_offset = text_y + for line in lines: + try: + draw.text((text_x, y_offset), line, font=font, fill=font_color, features=['-liga']) + except: + draw.text((text_x, y_offset), line, font=font, fill=font_color) + y_offset += font_size + + processed_image = torch.from_numpy(np.array(pil_image).astype(np.float32) / 255.0).unsqueeze(0) + return processed_image + + if caption == "": + processed_images = [process_image(img, text) for img in image] + else: + assert len(caption) == batch_size, f"Number of captions {(len(caption))} does not match number of images" + processed_images = [process_image(img, cap) for img, cap in zip(image, caption)] + processed_batch = torch.cat(processed_images, dim=0) + + # Combine images based on direction + if direction == 'down': + combined_images = torch.cat((image, processed_batch), dim=1) + elif direction == 'up': + combined_images = torch.cat((processed_batch, image), dim=1) + elif direction == 'left': + processed_batch = torch.rot90(processed_batch, 3, (2, 3)).permute(0, 3, 1, 2) + combined_images = torch.cat((processed_batch, image), dim=2) + elif direction == 'right': + processed_batch = torch.rot90(processed_batch, 3, (2, 3)).permute(0, 3, 1, 2) + combined_images = torch.cat((image, processed_batch), dim=2) + else: + combined_images = processed_batch + + return (combined_images,) + +class GetImageSizeAndCount: + @classmethod + def INPUT_TYPES(s): + return {"required": { + "image": ("IMAGE",), + }} + + RETURN_TYPES = ("IMAGE","INT", "INT", "INT",) + RETURN_NAMES = ("image", "width", "height", "count",) + FUNCTION = "getsize" + CATEGORY = "KJNodes/image" + DESCRIPTION = """ +Returns width, height and batch size of the image, +and passes it through unchanged. + +""" + + def getsize(self, image): + width = image.shape[2] + height = image.shape[1] + count = image.shape[0] + return {"ui": { + "text": [f"{count}x{width}x{height}"]}, + "result": (image, width, height, count) + } + +class GetLatentSizeAndCount: + @classmethod + def INPUT_TYPES(s): + return {"required": { + "latent": ("LATENT",), + }} + + RETURN_TYPES = ("LATENT","INT", "INT", "INT", "INT", "INT") + RETURN_NAMES = ("latent", "batch_size", "channels", "frames", "width", "height") + FUNCTION = "getsize" + CATEGORY = "KJNodes/image" + DESCRIPTION = """ +Returns latent tensor dimensions, +and passes the latent through unchanged. + +""" + def getsize(self, latent): + if len(latent["samples"].shape) == 5: + B, C, T, H, W = latent["samples"].shape + elif len(latent["samples"].shape) == 4: + B, C, H, W = latent["samples"].shape + T = 0 + else: + raise ValueError("Invalid latent shape") + + return {"ui": { + "text": [f"{B}x{C}x{T}x{H}x{W}"]}, + "result": (latent, B, C, T, H, W) + } + +class ImageBatchRepeatInterleaving: + + RETURN_TYPES = ("IMAGE", "MASK",) + FUNCTION = "repeat" + CATEGORY = "KJNodes/image" + DESCRIPTION = """ +Repeats each image in a batch by the specified number of times. +Example batch of 5 images: 0, 1 ,2, 3, 4 +with repeats 2 becomes batch of 10 images: 0, 0, 1, 1, 2, 2, 3, 3, 4, 4 +""" + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "images": ("IMAGE",), + "repeats": ("INT", {"default": 1, "min": 1, "max": 4096}), + }, + "optional": { + "mask": ("MASK",), + } + } + + def repeat(self, images, repeats, mask=None): + original_count = images.shape[0] + total_count = original_count * repeats + + repeated_images = torch.repeat_interleave(images, repeats=repeats, dim=0) + if mask is not None: + mask = torch.repeat_interleave(mask, repeats=repeats, dim=0) + else: + mask = torch.zeros((total_count, images.shape[1], images.shape[2]), + device=images.device, dtype=images.dtype) + for i in range(original_count): + mask[i * repeats] = 1.0 + + print("mask shape", mask.shape) + return (repeated_images, mask) + +class ImageUpscaleWithModelBatched: + @classmethod + def INPUT_TYPES(s): + return {"required": { "upscale_model": ("UPSCALE_MODEL",), + "images": ("IMAGE",), + "per_batch": ("INT", {"default": 16, "min": 1, "max": 4096, "step": 1}), + }} + RETURN_TYPES = ("IMAGE",) + FUNCTION = "upscale" + CATEGORY = "KJNodes/image" + DESCRIPTION = """ +Same as ComfyUI native model upscaling node, +but allows setting sub-batches for reduced VRAM usage. +""" + def upscale(self, upscale_model, images, per_batch): + + device = model_management.get_torch_device() + upscale_model.to(device) + in_img = images.movedim(-1,-3) + + steps = in_img.shape[0] + pbar = ProgressBar(steps) + t = [] + + for start_idx in range(0, in_img.shape[0], per_batch): + sub_images = upscale_model(in_img[start_idx:start_idx+per_batch].to(device)) + t.append(sub_images.cpu()) + # Calculate the number of images processed in this batch + batch_count = sub_images.shape[0] + # Update the progress bar by the number of images processed in this batch + pbar.update(batch_count) + upscale_model.cpu() + + t = torch.cat(t, dim=0).permute(0, 2, 3, 1).cpu() + + return (t,) + +class ImageNormalize_Neg1_To_1: + @classmethod + def INPUT_TYPES(s): + return {"required": { + "images": ("IMAGE",), + + }} + RETURN_TYPES = ("IMAGE",) + FUNCTION = "normalize" + CATEGORY = "KJNodes/image" + DESCRIPTION = """ +Normalize the images to be in the range [-1, 1] +""" + + def normalize(self,images): + images = images * 2.0 - 1.0 + return (images,) + +class RemapImageRange: + @classmethod + def INPUT_TYPES(s): + return {"required": { + "image": ("IMAGE",), + "min": ("FLOAT", {"default": 0.0,"min": -10.0, "max": 1.0, "step": 0.01}), + "max": ("FLOAT", {"default": 1.0,"min": 0.0, "max": 10.0, "step": 0.01}), + "clamp": ("BOOLEAN", {"default": True}), + }, + } + + RETURN_TYPES = ("IMAGE",) + FUNCTION = "remap" + CATEGORY = "KJNodes/image" + DESCRIPTION = """ +Remaps the image values to the specified range. +""" + + def remap(self, image, min, max, clamp): + if image.dtype == torch.float16: + image = image.to(torch.float32) + image = min + image * (max - min) + if clamp: + image = torch.clamp(image, min=0.0, max=1.0) + return (image, ) + +class SplitImageChannels: + @classmethod + def INPUT_TYPES(s): + return {"required": { + "image": ("IMAGE",), + }, + } + + RETURN_TYPES = ("IMAGE", "IMAGE", "IMAGE", "MASK") + RETURN_NAMES = ("red", "green", "blue", "mask") + FUNCTION = "split" + CATEGORY = "KJNodes/image" + DESCRIPTION = """ +Splits image channels into images where the selected channel +is repeated for all channels, and the alpha as a mask. +""" + + def split(self, image): + red = image[:, :, :, 0:1] # Red channel + green = image[:, :, :, 1:2] # Green channel + blue = image[:, :, :, 2:3] # Blue channel + alpha = image[:, :, :, 3:4] # Alpha channel + alpha = alpha.squeeze(-1) + + # Repeat the selected channel for all channels + red = torch.cat([red, red, red], dim=3) + green = torch.cat([green, green, green], dim=3) + blue = torch.cat([blue, blue, blue], dim=3) + return (red, green, blue, alpha) + +class MergeImageChannels: + @classmethod + def INPUT_TYPES(s): + return {"required": { + "red": ("IMAGE",), + "green": ("IMAGE",), + "blue": ("IMAGE",), + + }, + "optional": { + "alpha": ("MASK", {"default": None}), + }, + } + + RETURN_TYPES = ("IMAGE",) + RETURN_NAMES = ("image",) + FUNCTION = "merge" + CATEGORY = "KJNodes/image" + DESCRIPTION = """ +Merges channel data into an image. +""" + + def merge(self, red, green, blue, alpha=None): + image = torch.stack([ + red[..., 0, None], # Red channel + green[..., 1, None], # Green channel + blue[..., 2, None] # Blue channel + ], dim=-1) + image = image.squeeze(-2) + if alpha is not None: + image = torch.cat([image, alpha.unsqueeze(-1)], dim=-1) + return (image,) + +class ImagePadForOutpaintMasked: + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "image": ("IMAGE",), + "left": ("INT", {"default": 0, "min": 0, "max": MAX_RESOLUTION, "step": 8}), + "top": ("INT", {"default": 0, "min": 0, "max": MAX_RESOLUTION, "step": 8}), + "right": ("INT", {"default": 0, "min": 0, "max": MAX_RESOLUTION, "step": 8}), + "bottom": ("INT", {"default": 0, "min": 0, "max": MAX_RESOLUTION, "step": 8}), + "feathering": ("INT", {"default": 0, "min": 0, "max": MAX_RESOLUTION, "step": 1}), + }, + "optional": { + "mask": ("MASK",), + } + } + + RETURN_TYPES = ("IMAGE", "MASK") + FUNCTION = "expand_image" + + CATEGORY = "image" + + def expand_image(self, image, left, top, right, bottom, feathering, mask=None): + if mask is not None: + if torch.allclose(mask, torch.zeros_like(mask)): + print("Warning: The incoming mask is fully black. Handling it as None.") + mask = None + B, H, W, C = image.size() + + new_image = torch.ones( + (B, H + top + bottom, W + left + right, C), + dtype=torch.float32, + ) * 0.5 + + new_image[:, top:top + H, left:left + W, :] = image + + if mask is None: + new_mask = torch.ones( + (B, H + top + bottom, W + left + right), + dtype=torch.float32, + ) + + t = torch.zeros( + (B, H, W), + dtype=torch.float32 + ) + else: + # If a mask is provided, pad it to fit the new image size + mask = F.pad(mask, (left, right, top, bottom), mode='constant', value=0) + mask = 1 - mask + t = torch.zeros_like(mask) + + if feathering > 0 and feathering * 2 < H and feathering * 2 < W: + + for i in range(H): + for j in range(W): + dt = i if top != 0 else H + db = H - i if bottom != 0 else H + + dl = j if left != 0 else W + dr = W - j if right != 0 else W + + d = min(dt, db, dl, dr) + + if d >= feathering: + continue + + v = (feathering - d) / feathering + + if mask is None: + t[:, i, j] = v * v + else: + t[:, top + i, left + j] = v * v + + if mask is None: + new_mask[:, top:top + H, left:left + W] = t + return (new_image, new_mask,) + else: + return (new_image, mask,) + +class ImagePadForOutpaintTargetSize: + upscale_methods = ["nearest-exact", "bilinear", "area", "bicubic", "lanczos"] + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "image": ("IMAGE",), + "target_width": ("INT", {"default": 0, "min": 0, "max": MAX_RESOLUTION, "step": 8}), + "target_height": ("INT", {"default": 0, "min": 0, "max": MAX_RESOLUTION, "step": 8}), + "feathering": ("INT", {"default": 0, "min": 0, "max": MAX_RESOLUTION, "step": 1}), + "upscale_method": (s.upscale_methods,), + }, + "optional": { + "mask": ("MASK",), + } + } + + RETURN_TYPES = ("IMAGE", "MASK") + FUNCTION = "expand_image" + + CATEGORY = "image" + + def expand_image(self, image, target_width, target_height, feathering, upscale_method, mask=None): + B, H, W, C = image.size() + new_height = H + new_width = W + # Calculate the scaling factor while maintaining aspect ratio + scaling_factor = min(target_width / W, target_height / H) + + # Check if the image needs to be downscaled + if scaling_factor < 1: + image = image.movedim(-1,1) + # Calculate the new width and height after downscaling + new_width = int(W * scaling_factor) + new_height = int(H * scaling_factor) + + # Downscale the image + image_scaled = common_upscale(image, new_width, new_height, upscale_method, "disabled").movedim(1,-1) + if mask is not None: + mask_scaled = mask.unsqueeze(0) # Add an extra dimension for batch size + mask_scaled = F.interpolate(mask_scaled, size=(new_height, new_width), mode="nearest") + mask_scaled = mask_scaled.squeeze(0) # Remove the extra dimension after interpolation + else: + mask_scaled = mask + else: + # If downscaling is not needed, use the original image dimensions + image_scaled = image + mask_scaled = mask + + # Calculate how much padding is needed to reach the target dimensions + pad_top = max(0, (target_height - new_height) // 2) + pad_bottom = max(0, target_height - new_height - pad_top) + pad_left = max(0, (target_width - new_width) // 2) + pad_right = max(0, target_width - new_width - pad_left) + + # Now call the original expand_image with the calculated padding + return ImagePadForOutpaintMasked.expand_image(self, image_scaled, pad_left, pad_top, pad_right, pad_bottom, feathering, mask_scaled) + +class ImagePrepForICLora: + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "reference_image": ("IMAGE",), + "output_width": ("INT", {"default": 1024, "min": 1, "max": 4096, "step": 1}), + "output_height": ("INT", {"default": 1024, "min": 1, "max": 4096, "step": 1}), + "border_width": ("INT", {"default": 0, "min": 0, "max": 4096, "step": 1}), + }, + "optional": { + "latent_image": ("IMAGE",), + "latent_mask": ("MASK",), + "reference_mask": ("MASK",), + } + } + + RETURN_TYPES = ("IMAGE", "MASK") + FUNCTION = "expand_image" + + CATEGORY = "image" + + def expand_image(self, reference_image, output_width, output_height, border_width, latent_image=None, reference_mask=None, latent_mask=None): + + if reference_mask is not None: + if torch.allclose(reference_mask, torch.zeros_like(reference_mask)): + print("Warning: The incoming mask is fully black. Handling it as None.") + reference_mask = None + image = reference_image + if latent_image is not None: + if image.shape[0] != latent_image.shape[0]: + image = image.repeat(latent_image.shape[0], 1, 1, 1) + B, H, W, C = image.size() + + # Handle mask + if reference_mask is not None: + resized_mask = torch.nn.functional.interpolate( + reference_mask.unsqueeze(1), + size=(H, W), + mode='nearest' + ).squeeze(1) + print(resized_mask.shape) + image = image * resized_mask.unsqueeze(-1) + + # Calculate new width maintaining aspect ratio + new_width = int((W / H) * output_height) + + # Resize image to new height while maintaining aspect ratio + resized_image = common_upscale(image.movedim(-1,1), new_width, output_height, "lanczos", "disabled").movedim(1,-1) + + # Create padded image + if latent_image is None: + pad_image = torch.zeros((B, output_height, output_width, C), device=image.device) + else: + resized_latent_image = common_upscale(latent_image.movedim(-1,1), output_width, output_height, "lanczos", "disabled").movedim(1,-1) + pad_image = resized_latent_image + if latent_mask is not None: + resized_latent_mask = torch.nn.functional.interpolate( + latent_mask.unsqueeze(1), + size=(pad_image.shape[1], pad_image.shape[2]), + mode='nearest' + ).squeeze(1) + + if border_width > 0: + border = torch.zeros((B, output_height, border_width, C), device=image.device) + padded_image = torch.cat((resized_image, border, pad_image), dim=2) + if latent_mask is not None: + padded_mask = torch.zeros((B, padded_image.shape[1], padded_image.shape[2]), device=image.device) + padded_mask[:, :, (new_width + border_width):] = resized_latent_mask + else: + padded_mask = torch.ones((B, padded_image.shape[1], padded_image.shape[2]), device=image.device) + padded_mask[:, :, :new_width + border_width] = 0 + else: + padded_image = torch.cat((resized_image, pad_image), dim=2) + if latent_mask is not None: + padded_mask = torch.zeros((B, padded_image.shape[1], padded_image.shape[2]), device=image.device) + padded_mask[:, :, new_width:] = resized_latent_mask + else: + padded_mask = torch.ones((B, padded_image.shape[1], padded_image.shape[2]), device=image.device) + padded_mask[:, :, :new_width] = 0 + + return (padded_image, padded_mask) + + +class ImageAndMaskPreview(SaveImage): + def __init__(self): + self.output_dir = folder_paths.get_temp_directory() + self.type = "temp" + self.prefix_append = "_temp_" + ''.join(random.choice("abcdefghijklmnopqrstupvxyz") for x in range(5)) + self.compress_level = 4 + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "mask_opacity": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}), + "mask_color": ("STRING", {"default": "255, 255, 255"}), + "pass_through": ("BOOLEAN", {"default": False}), + }, + "optional": { + "image": ("IMAGE",), + "mask": ("MASK",), + }, + "hidden": {"prompt": "PROMPT", "extra_pnginfo": "EXTRA_PNGINFO"}, + } + RETURN_TYPES = ("IMAGE",) + RETURN_NAMES = ("composite",) + FUNCTION = "execute" + CATEGORY = "KJNodes/masking" + DESCRIPTION = """ +Preview an image or a mask, when both inputs are used +composites the mask on top of the image. +with pass_through on the preview is disabled and the +composite is returned from the composite slot instead, +this allows for the preview to be passed for video combine +nodes for example. +""" + + def execute(self, mask_opacity, mask_color, pass_through, filename_prefix="ComfyUI", image=None, mask=None, prompt=None, extra_pnginfo=None): + if mask is not None and image is None: + preview = mask.reshape((-1, 1, mask.shape[-2], mask.shape[-1])).movedim(1, -1).expand(-1, -1, -1, 3) + elif mask is None and image is not None: + preview = image + elif mask is not None and image is not None: + mask_adjusted = mask * mask_opacity + mask_image = mask.reshape((-1, 1, mask.shape[-2], mask.shape[-1])).movedim(1, -1).expand(-1, -1, -1, 3).clone() + + if ',' in mask_color: + color_list = np.clip([int(channel) for channel in mask_color.split(',')], 0, 255) # RGB format + else: + mask_color = mask_color.lstrip('#') + color_list = [int(mask_color[i:i+2], 16) for i in (0, 2, 4)] # Hex format + mask_image[:, :, :, 0] = color_list[0] / 255 # Red channel + mask_image[:, :, :, 1] = color_list[1] / 255 # Green channel + mask_image[:, :, :, 2] = color_list[2] / 255 # Blue channel + + preview, = ImageCompositeMasked.composite(self, image, mask_image, 0, 0, True, mask_adjusted) + if pass_through: + return (preview, ) + return(self.save_images(preview, filename_prefix, prompt, extra_pnginfo)) + +def crossfade(images_1, images_2, alpha): + crossfade = (1 - alpha) * images_1 + alpha * images_2 + return crossfade +def ease_in(t): + return t * t +def ease_out(t): + return 1 - (1 - t) * (1 - t) +def ease_in_out(t): + return 3 * t * t - 2 * t * t * t +def bounce(t): + if t < 0.5: + return ease_out(t * 2) * 0.5 + else: + return ease_in((t - 0.5) * 2) * 0.5 + 0.5 +def elastic(t): + return math.sin(13 * math.pi / 2 * t) * math.pow(2, 10 * (t - 1)) +def glitchy(t): + return t + 0.1 * math.sin(40 * t) +def exponential_ease_out(t): + return 1 - (1 - t) ** 4 + +easing_functions = { + "linear": lambda t: t, + "ease_in": ease_in, + "ease_out": ease_out, + "ease_in_out": ease_in_out, + "bounce": bounce, + "elastic": elastic, + "glitchy": glitchy, + "exponential_ease_out": exponential_ease_out, +} + +class CrossFadeImages: + + RETURN_TYPES = ("IMAGE",) + FUNCTION = "crossfadeimages" + CATEGORY = "KJNodes/image" + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "images_1": ("IMAGE",), + "images_2": ("IMAGE",), + "interpolation": (["linear", "ease_in", "ease_out", "ease_in_out", "bounce", "elastic", "glitchy", "exponential_ease_out"],), + "transition_start_index": ("INT", {"default": 1,"min": -4096, "max": 4096, "step": 1}), + "transitioning_frames": ("INT", {"default": 1,"min": 0, "max": 4096, "step": 1}), + "start_level": ("FLOAT", {"default": 0.0,"min": 0.0, "max": 1.0, "step": 0.01}), + "end_level": ("FLOAT", {"default": 1.0,"min": 0.0, "max": 1.0, "step": 0.01}), + }, + } + + def crossfadeimages(self, images_1, images_2, transition_start_index, transitioning_frames, interpolation, start_level, end_level): + + crossfade_images = [] + + if transition_start_index < 0: + transition_start_index = len(images_1) + transition_start_index + if transition_start_index < 0: + raise ValueError("Transition start index is out of range for images_1.") + + transitioning_frames = min(transitioning_frames, len(images_1) - transition_start_index, len(images_2)) + + alphas = torch.linspace(start_level, end_level, transitioning_frames) + for i in range(transitioning_frames): + alpha = alphas[i] + image1 = images_1[transition_start_index + i] + image2 = images_2[i] + easing_function = easing_functions.get(interpolation) + alpha = easing_function(alpha) # Apply the easing function to the alpha value + + crossfade_image = crossfade(image1, image2, alpha) + crossfade_images.append(crossfade_image) + + # Convert crossfade_images to tensor + crossfade_images = torch.stack(crossfade_images, dim=0) + + # Append the beginning of images_1 (before the transition) + beginning_images_1 = images_1[:transition_start_index] + crossfade_images = torch.cat([beginning_images_1, crossfade_images], dim=0) + + # Append the remaining frames of images_2 (after the transition) + remaining_images_2 = images_2[transitioning_frames:] + if len(remaining_images_2) > 0: + crossfade_images = torch.cat([crossfade_images, remaining_images_2], dim=0) + + return (crossfade_images, ) + +class CrossFadeImagesMulti: + RETURN_TYPES = ("IMAGE",) + FUNCTION = "crossfadeimages" + CATEGORY = "KJNodes/image" + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "inputcount": ("INT", {"default": 2, "min": 2, "max": 1000, "step": 1}), + "image_1": ("IMAGE",), + "interpolation": (["linear", "ease_in", "ease_out", "ease_in_out", "bounce", "elastic", "glitchy", "exponential_ease_out"],), + "transitioning_frames": ("INT", {"default": 1,"min": 0, "max": 4096, "step": 1}), + }, + "optional": { + "image_2": ("IMAGE",), + } + } + + def crossfadeimages(self, inputcount, transitioning_frames, interpolation, **kwargs): + + image_1 = kwargs["image_1"] + first_image_shape = image_1.shape + first_image_device = image_1.device + height = image_1.shape[1] + width = image_1.shape[2] + + easing_function = easing_functions[interpolation] + + for c in range(1, inputcount): + frames = [] + new_image = kwargs.get(f"image_{c + 1}", torch.zeros(first_image_shape)).to(first_image_device) + new_image_height = new_image.shape[1] + new_image_width = new_image.shape[2] + + if new_image_height != height or new_image_width != width: + new_image = common_upscale(new_image.movedim(-1, 1), width, height, "lanczos", "disabled") + new_image = new_image.movedim(1, -1) # Move channels back to the last dimension + + last_frame_image_1 = image_1[-1] + first_frame_image_2 = new_image[0] + + for frame in range(transitioning_frames): + t = frame / (transitioning_frames - 1) + alpha = easing_function(t) + alpha_tensor = torch.tensor(alpha, dtype=last_frame_image_1.dtype, device=last_frame_image_1.device) + frame_image = crossfade(last_frame_image_1, first_frame_image_2, alpha_tensor) + frames.append(frame_image) + + frames = torch.stack(frames) + image_1 = torch.cat((image_1, frames, new_image), dim=0) + + return image_1, + +def transition_images(images_1, images_2, alpha, transition_type, blur_radius, reverse): + width = images_1.shape[1] + height = images_1.shape[0] + + mask = torch.zeros_like(images_1, device=images_1.device) + + alpha = alpha.item() + if reverse: + alpha = 1 - alpha + + #transitions from matteo's essential nodes + if "horizontal slide" in transition_type: + pos = round(width * alpha) + mask[:, :pos, :] = 1.0 + elif "vertical slide" in transition_type: + pos = round(height * alpha) + mask[:pos, :, :] = 1.0 + elif "box" in transition_type: + box_w = round(width * alpha) + box_h = round(height * alpha) + x1 = (width - box_w) // 2 + y1 = (height - box_h) // 2 + x2 = x1 + box_w + y2 = y1 + box_h + mask[y1:y2, x1:x2, :] = 1.0 + elif "circle" in transition_type: + radius = math.ceil(math.sqrt(pow(width, 2) + pow(height, 2)) * alpha / 2) + c_x = width // 2 + c_y = height // 2 + x = torch.arange(0, width, dtype=torch.float32, device="cpu") + y = torch.arange(0, height, dtype=torch.float32, device="cpu") + y, x = torch.meshgrid((y, x), indexing="ij") + circle = ((x - c_x) ** 2 + (y - c_y) ** 2) <= (radius ** 2) + mask[circle] = 1.0 + elif "horizontal door" in transition_type: + bar = math.ceil(height * alpha / 2) + if bar > 0: + mask[:bar, :, :] = 1.0 + mask[-bar:,:, :] = 1.0 + elif "vertical door" in transition_type: + bar = math.ceil(width * alpha / 2) + if bar > 0: + mask[:, :bar,:] = 1.0 + mask[:, -bar:,:] = 1.0 + elif "fade" in transition_type: + mask[:, :, :] = alpha + + mask = gaussian_blur(mask, blur_radius) + + return images_1 * (1 - mask) + images_2 * mask + +def gaussian_blur(mask, blur_radius): + if blur_radius > 0: + kernel_size = int(blur_radius * 2) + 1 + if kernel_size % 2 == 0: + kernel_size += 1 # Ensure kernel size is odd + sigma = blur_radius / 3 + x = torch.arange(-kernel_size // 2 + 1, kernel_size // 2 + 1, dtype=torch.float32) + x = torch.exp(-0.5 * (x / sigma) ** 2) + kernel1d = x / x.sum() + kernel2d = kernel1d[:, None] * kernel1d[None, :] + kernel2d = kernel2d.to(mask.device) + kernel2d = kernel2d.expand(mask.shape[2], 1, kernel2d.shape[0], kernel2d.shape[1]) + mask = mask.permute(2, 0, 1).unsqueeze(0) # Change to [C, H, W] and add batch dimension + mask = F.conv2d(mask, kernel2d, padding=kernel_size // 2, groups=mask.shape[1]) + mask = mask.squeeze(0).permute(1, 2, 0) # Change back to [H, W, C] + return mask + +easing_functions = { + "linear": lambda t: t, + "ease_in": ease_in, + "ease_out": ease_out, + "ease_in_out": ease_in_out, + "bounce": bounce, + "elastic": elastic, + "glitchy": glitchy, + "exponential_ease_out": exponential_ease_out, +} + +class TransitionImagesMulti: + RETURN_TYPES = ("IMAGE",) + FUNCTION = "transition" + CATEGORY = "KJNodes/image" + DESCRIPTION = """ +Creates transitions between images. +""" + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "inputcount": ("INT", {"default": 2, "min": 2, "max": 1000, "step": 1}), + "image_1": ("IMAGE",), + "interpolation": (["linear", "ease_in", "ease_out", "ease_in_out", "bounce", "elastic", "glitchy", "exponential_ease_out"],), + "transition_type": (["horizontal slide", "vertical slide", "box", "circle", "horizontal door", "vertical door", "fade"],), + "transitioning_frames": ("INT", {"default": 2,"min": 2, "max": 4096, "step": 1}), + "blur_radius": ("FLOAT", {"default": 0.0,"min": 0.0, "max": 100.0, "step": 0.1}), + "reverse": ("BOOLEAN", {"default": False}), + "device": (["CPU", "GPU"], {"default": "CPU"}), + }, + "optional": { + "image_2": ("IMAGE",), + } + } + + def transition(self, inputcount, transitioning_frames, transition_type, interpolation, device, blur_radius, reverse, **kwargs): + + gpu = model_management.get_torch_device() + + image_1 = kwargs["image_1"] + height = image_1.shape[1] + width = image_1.shape[2] + first_image_shape = image_1.shape + first_image_device = image_1.device + + easing_function = easing_functions[interpolation] + + for c in range(1, inputcount): + frames = [] + new_image = kwargs.get(f"image_{c + 1}", torch.zeros(first_image_shape)).to(first_image_device) + new_image_height = new_image.shape[1] + new_image_width = new_image.shape[2] + + if new_image_height != height or new_image_width != width: + new_image = common_upscale(new_image.movedim(-1, 1), width, height, "lanczos", "disabled") + new_image = new_image.movedim(1, -1) # Move channels back to the last dimension + + last_frame_image_1 = image_1[-1] + first_frame_image_2 = new_image[0] + if device == "GPU": + last_frame_image_1 = last_frame_image_1.to(gpu) + first_frame_image_2 = first_frame_image_2.to(gpu) + + if reverse: + last_frame_image_1, first_frame_image_2 = first_frame_image_2, last_frame_image_1 + + for frame in range(transitioning_frames): + t = frame / (transitioning_frames - 1) + alpha = easing_function(t) + alpha_tensor = torch.tensor(alpha, dtype=last_frame_image_1.dtype, device=last_frame_image_1.device) + frame_image = transition_images(last_frame_image_1, first_frame_image_2, alpha_tensor, transition_type, blur_radius, reverse) + frames.append(frame_image) + + frames = torch.stack(frames).cpu() + image_1 = torch.cat((image_1, frames, new_image), dim=0) + + return image_1.cpu(), + +class TransitionImagesInBatch: + RETURN_TYPES = ("IMAGE",) + FUNCTION = "transition" + CATEGORY = "KJNodes/image" + DESCRIPTION = """ +Creates transitions between images in a batch. +""" + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "images": ("IMAGE",), + "interpolation": (["linear", "ease_in", "ease_out", "ease_in_out", "bounce", "elastic", "glitchy", "exponential_ease_out"],), + "transition_type": (["horizontal slide", "vertical slide", "box", "circle", "horizontal door", "vertical door", "fade"],), + "transitioning_frames": ("INT", {"default": 1,"min": 0, "max": 4096, "step": 1}), + "blur_radius": ("FLOAT", {"default": 0.0,"min": 0.0, "max": 100.0, "step": 0.1}), + "reverse": ("BOOLEAN", {"default": False}), + "device": (["CPU", "GPU"], {"default": "CPU"}), + }, + } + + #transitions from matteo's essential nodes + def transition(self, images, transitioning_frames, transition_type, interpolation, device, blur_radius, reverse): + if images.shape[0] == 1: + return images, + + gpu = model_management.get_torch_device() + + easing_function = easing_functions[interpolation] + + images_list = [] + pbar = ProgressBar(images.shape[0] - 1) + for i in range(images.shape[0] - 1): + frames = [] + image_1 = images[i] + image_2 = images[i + 1] + + if device == "GPU": + image_1 = image_1.to(gpu) + image_2 = image_2.to(gpu) + + if reverse: + image_1, image_2 = image_2, image_1 + + for frame in range(transitioning_frames): + t = frame / (transitioning_frames - 1) + alpha = easing_function(t) + alpha_tensor = torch.tensor(alpha, dtype=image_1.dtype, device=image_1.device) + frame_image = transition_images(image_1, image_2, alpha_tensor, transition_type, blur_radius, reverse) + frames.append(frame_image) + pbar.update(1) + + frames = torch.stack(frames).cpu() + images_list.append(frames) + images = torch.cat(images_list, dim=0) + + return images.cpu(), + +class ImageBatchJoinWithTransition: + RETURN_TYPES = ("IMAGE",) + FUNCTION = "transition_batches" + CATEGORY = "KJNodes/image" + DESCRIPTION = """ +Transitions between two batches of images, starting at a specified index in the first batch. +During the transition, frames from both batches are blended frame-by-frame, so the video keeps playing. +""" + + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "images_1": ("IMAGE",), + "images_2": ("IMAGE",), + "start_index": ("INT", {"default": 0, "min": -10000, "max": 10000, "step": 1}), + "interpolation": (["linear", "ease_in", "ease_out", "ease_in_out", "bounce", "elastic", "glitchy", "exponential_ease_out"],), + "transition_type": (["horizontal slide", "vertical slide", "box", "circle", "horizontal door", "vertical door", "fade"],), + "transitioning_frames": ("INT", {"default": 1, "min": 1, "max": 4096, "step": 1}), + "blur_radius": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 100.0, "step": 0.1}), + "reverse": ("BOOLEAN", {"default": False}), + "device": (["CPU", "GPU"], {"default": "CPU"}), + }, + } + + def transition_batches(self, images_1, images_2, start_index, interpolation, transition_type, transitioning_frames, blur_radius, reverse, device): + if images_1.shape[0] == 0 or images_2.shape[0] == 0: + raise ValueError("Both input batches must have at least one image.") + + if start_index < 0: + start_index = images_1.shape[0] + start_index + if start_index < 0 or start_index > images_1.shape[0]: + raise ValueError("start_index is out of range.") + + gpu = model_management.get_torch_device() + easing_function = easing_functions[interpolation] + out_frames = [] + + # Add images from images_1 up to start_index + if start_index > 0: + out_frames.append(images_1[:start_index]) + + # Determine how many frames we can blend + max_transition = min(transitioning_frames, images_1.shape[0] - start_index, images_2.shape[0]) + + # Blend corresponding frames from both batches + for i in range(max_transition): + img1 = images_1[start_index + i] + img2 = images_2[i] + if device == "GPU": + img1 = img1.to(gpu) + img2 = img2.to(gpu) + if reverse: + img1, img2 = img2, img1 + t = i / (max_transition - 1) if max_transition > 1 else 1.0 + alpha = easing_function(t) + alpha_tensor = torch.tensor(alpha, dtype=img1.dtype, device=img1.device) + frame_image = transition_images(img1, img2, alpha_tensor, transition_type, blur_radius, reverse) + out_frames.append(frame_image.cpu().unsqueeze(0)) + + # Add remaining images from images_2 after transition + if images_2.shape[0] > max_transition: + out_frames.append(images_2[max_transition:]) + + # Concatenate all frames + out = torch.cat(out_frames, dim=0) + return (out.cpu(),) + +class ShuffleImageBatch: + RETURN_TYPES = ("IMAGE",) + FUNCTION = "shuffle" + CATEGORY = "KJNodes/image" + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "images": ("IMAGE",), + "seed": ("INT", {"default": 123,"min": 0, "max": 0xffffffffffffffff, "step": 1}), + }, + } + + def shuffle(self, images, seed): + torch.manual_seed(seed) + B, H, W, C = images.shape + indices = torch.randperm(B) + shuffled_images = images[indices] + + return shuffled_images, + +class GetImageRangeFromBatch: + + RETURN_TYPES = ("IMAGE", "MASK", ) + FUNCTION = "imagesfrombatch" + CATEGORY = "KJNodes/image" + DESCRIPTION = """ +Returns a range of images from a batch. +""" + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "start_index": ("INT", {"default": 0,"min": -1, "max": 4096, "step": 1}), + "num_frames": ("INT", {"default": 1,"min": 1, "max": 4096, "step": 1}), + }, + "optional": { + "images": ("IMAGE",), + "masks": ("MASK",), + } + } + + def imagesfrombatch(self, start_index, num_frames, images=None, masks=None): + chosen_images = None + chosen_masks = None + + # Process images if provided + if images is not None: + if start_index == -1: + start_index = max(0, len(images) - num_frames) + if start_index < 0 or start_index >= len(images): + raise ValueError("Start index is out of range") + end_index = min(start_index + num_frames, len(images)) + chosen_images = images[start_index:end_index] + + # Process masks if provided + if masks is not None: + if start_index == -1: + start_index = max(0, len(masks) - num_frames) + if start_index < 0 or start_index >= len(masks): + raise ValueError("Start index is out of range for masks") + end_index = min(start_index + num_frames, len(masks)) + chosen_masks = masks[start_index:end_index] + + return (chosen_images, chosen_masks,) + +class GetLatentRangeFromBatch: + + RETURN_TYPES = ("LATENT", ) + FUNCTION = "latentsfrombatch" + CATEGORY = "KJNodes/latents" + DESCRIPTION = """ +Returns a range of latents from a batch. +""" + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "latents": ("LATENT",), + "start_index": ("INT", {"default": 0,"min": -1, "max": 4096, "step": 1}), + "num_frames": ("INT", {"default": 1,"min": -1, "max": 4096, "step": 1}), + }, + } + + def latentsfrombatch(self, latents, start_index, num_frames): + chosen_latents = None + samples = latents["samples"] + if len(samples.shape) == 4: + B, C, H, W = samples.shape + num_latents = B + elif len(samples.shape) == 5: + B, C, T, H, W = samples.shape + num_latents = T + + if start_index == -1: + start_index = max(0, num_latents - num_frames) + if start_index < 0 or start_index >= num_latents: + raise ValueError("Start index is out of range") + + end_index = num_latents if num_frames == -1 else min(start_index + num_frames, num_latents) + + if len(samples.shape) == 4: + chosen_latents = samples[start_index:end_index] + elif len(samples.shape) == 5: + chosen_latents = samples[:, :, start_index:end_index] + + return ({"samples": chosen_latents.contiguous(),},) + +class InsertLatentToIndex: + + RETURN_TYPES = ("LATENT", ) + FUNCTION = "insert" + CATEGORY = "KJNodes/latents" + DESCRIPTION = """ +Inserts a latent at the specified index into the original latent batch. +""" + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "source": ("LATENT",), + "destination": ("LATENT",), + "index": ("INT", {"default": 0,"min": -1, "max": 4096, "step": 1}), + }, + } + + def insert(self, source, destination, index): + samples_destination = destination["samples"] + samples_source = source["samples"].to(samples_destination) + + if len(samples_source.shape) == 4: + B, C, H, W = samples_source.shape + num_latents = B + elif len(samples_source.shape) == 5: + B, C, T, H, W = samples_source.shape + num_latents = T + + if index >= num_latents or index < 0: + raise ValueError(f"Index {index} out of bounds for tensor with {num_latents} latents") + + if len(samples_source.shape) == 4: + joined_latents = torch.cat([ + samples_destination[:index], + samples_source, + samples_destination[index+1:] + ], dim=0) + else: + joined_latents = torch.cat([ + samples_destination[:, :, :index], + samples_source, + samples_destination[:, :, index+1:] + ], dim=2) + + return ({"samples": joined_latents,},) + +class ImageBatchFilter: + + RETURN_TYPES = ("IMAGE", "STRING",) + RETURN_NAMES = ("images", "removed_indices",) + FUNCTION = "filter" + CATEGORY = "KJNodes/image" + DESCRIPTION = "Removes empty images from a batch" + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "images": ("IMAGE",), + "empty_color": ("STRING", {"default": "0, 0, 0"}), + "empty_threshold": ("FLOAT", {"default": 0.01,"min": 0.0, "max": 1.0, "step": 0.01}), + }, + "optional": { + "replacement_image": ("IMAGE",), + } + } + + def filter(self, images, empty_color, empty_threshold, replacement_image=None): + B, H, W, C = images.shape + + input_images = images.clone() + + empty_color_list = [int(color.strip()) for color in empty_color.split(',')] + empty_color_tensor = torch.tensor(empty_color_list, dtype=torch.float32).to(input_images.device) + + color_diff = torch.abs(input_images - empty_color_tensor) + mean_diff = color_diff.mean(dim=(1, 2, 3)) + + empty_indices = mean_diff <= empty_threshold + empty_indices_string = ', '.join([str(i) for i in range(B) if empty_indices[i]]) + + if replacement_image is not None: + B_rep, H_rep, W_rep, C_rep = replacement_image.shape + replacement = replacement_image.clone() + if (H_rep != images.shape[1]) or (W_rep != images.shape[2]) or (C_rep != images.shape[3]): + replacement = common_upscale(replacement.movedim(-1, 1), W, H, "lanczos", "center").movedim(1, -1) + input_images[empty_indices] = replacement[0] + + return (input_images, empty_indices_string,) + else: + non_empty_images = input_images[~empty_indices] + return (non_empty_images, empty_indices_string,) + +class GetImagesFromBatchIndexed: + + RETURN_TYPES = ("IMAGE",) + FUNCTION = "indexedimagesfrombatch" + CATEGORY = "KJNodes/image" + DESCRIPTION = """ +Selects and returns the images at the specified indices as an image batch. +""" + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "images": ("IMAGE",), + "indexes": ("STRING", {"default": "0, 1, 2", "multiline": True}), + }, + } + + def indexedimagesfrombatch(self, images, indexes): + + # Parse the indexes string into a list of integers + index_list = [int(index.strip()) for index in indexes.split(',')] + + # Convert list of indices to a PyTorch tensor + indices_tensor = torch.tensor(index_list, dtype=torch.long) + + # Select the images at the specified indices + chosen_images = images[indices_tensor] + + return (chosen_images,) + +class InsertImagesToBatchIndexed: + + RETURN_TYPES = ("IMAGE",) + FUNCTION = "insertimagesfrombatch" + CATEGORY = "KJNodes/image" + DESCRIPTION = """ +Inserts images at the specified indices into the original image batch. +""" + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "original_images": ("IMAGE",), + "images_to_insert": ("IMAGE",), + "indexes": ("STRING", {"default": "0, 1, 2", "multiline": True}), + }, + "optional": { + "mode": (["replace", "insert"],), + } + } + + def insertimagesfrombatch(self, original_images, images_to_insert, indexes, mode="replace"): + if indexes == "": + return (original_images,) + + input_images = original_images.clone() + + # Parse the indexes string into a list of integers + index_list = [int(index.strip()) for index in indexes.split(',')] + + # Convert list of indices to a PyTorch tensor + indices_tensor = torch.tensor(index_list, dtype=torch.long) + + # Ensure the images_to_insert is a tensor + if not isinstance(images_to_insert, torch.Tensor): + images_to_insert = torch.tensor(images_to_insert) + + if mode == "replace": + # Replace the images at the specified indices + for index, image in zip(indices_tensor, images_to_insert): + input_images[index] = image + else: + # Create a list to hold the new image sequence + new_images = [] + insert_offset = 0 + + for i in range(len(input_images) + len(indices_tensor)): + if insert_offset < len(indices_tensor) and i == indices_tensor[insert_offset]: + # Use modulo to cycle through images_to_insert + new_images.append(images_to_insert[insert_offset % len(images_to_insert)]) + insert_offset += 1 + else: + new_images.append(input_images[i - insert_offset]) + + # Convert the list back to a tensor + input_images = torch.stack(new_images, dim=0) + + return (input_images,) + +class PadImageBatchInterleaved: + + RETURN_TYPES = ("IMAGE", "MASK",) + RETURN_NAMES = ("images", "masks",) + FUNCTION = "pad" + CATEGORY = "KJNodes/image" + DESCRIPTION = """ +Inserts empty frames between the images in a batch. +""" + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "images": ("IMAGE",), + "empty_frames_per_image": ("INT", {"default": 1,"min": 0, "max": 4096, "step": 1}), + "pad_frame_value": ("FLOAT", {"default": 0.0,"min": 0.0, "max": 1.0, "step": 0.01}), + "add_after_last": ("BOOLEAN", {"default": False}), + }, + } + + def pad(self, images, empty_frames_per_image, pad_frame_value, add_after_last): + B, H, W, C = images.shape + + # Handle single frame case specifically + if B == 1: + total_frames = 1 + empty_frames_per_image if add_after_last else 1 + else: + # Original B images + (B-1) sets of empty frames between them + total_frames = B + (B-1) * empty_frames_per_image + # Add additional empty frames after the last image if requested + if add_after_last: + total_frames += empty_frames_per_image + + # Create new tensor with zeros (empty frames) + padded_batch = torch.ones((total_frames, H, W, C), + dtype=images.dtype, + device=images.device) * pad_frame_value + # Create mask tensor (1 for original frames, 0 for empty frames) + mask = torch.zeros((total_frames, H, W), + dtype=images.dtype, + device=images.device) + + # Fill in original images at their new positions + for i in range(B): + if B == 1: + # For single frame, just place it at the beginning + new_pos = 0 + else: + # Each image is separated by empty_frames_per_image blank frames + new_pos = i * (empty_frames_per_image + 1) + + padded_batch[new_pos] = images[i] + mask[new_pos] = 1.0 # Mark this as an original frame + + return (padded_batch, mask) + +class ReplaceImagesInBatch: + + RETURN_TYPES = ("IMAGE", "MASK",) + FUNCTION = "replace" + CATEGORY = "KJNodes/image" + DESCRIPTION = """ +Replaces the images in a batch, starting from the specified start index, +with the replacement images. +""" + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "start_index": ("INT", {"default": 1,"min": 0, "max": 4096, "step": 1}), + }, + "optional": { + "original_images": ("IMAGE",), + "replacement_images": ("IMAGE",), + "original_masks": ("MASK",), + "replacement_masks": ("MASK",), + } + } + + def replace(self, original_images=None, replacement_images=None, start_index=1, original_masks=None, replacement_masks=None): + images = None + masks = None + + if original_images is not None and replacement_images is not None: + if start_index >= len(original_images): + raise ValueError("ReplaceImagesInBatch: Start index is out of range") + end_index = start_index + len(replacement_images) + if end_index > len(original_images): + raise ValueError("ReplaceImagesInBatch: End index is out of range") + + original_images_copy = original_images.clone() + if original_images_copy.shape[2] != replacement_images.shape[2] or original_images_copy.shape[3] != replacement_images.shape[3]: + replacement_images = common_upscale(replacement_images.movedim(-1, 1), original_images_copy.shape[1], original_images_copy.shape[2], "lanczos", "center").movedim(1, -1) + + original_images_copy[start_index:end_index] = replacement_images + images = original_images_copy + else: + images = torch.zeros((1, 64, 64, 3)) + + if original_masks is not None and replacement_masks is not None: + if start_index >= len(original_masks): + raise ValueError("ReplaceImagesInBatch: Start index is out of range") + end_index = start_index + len(replacement_masks) + if end_index > len(original_masks): + raise ValueError("ReplaceImagesInBatch: End index is out of range") + + original_masks_copy = original_masks.clone() + if original_masks_copy.shape[1] != replacement_masks.shape[1] or original_masks_copy.shape[2] != replacement_masks.shape[2]: + replacement_masks = common_upscale(replacement_masks.unsqueeze(1), original_masks_copy.shape[1], original_masks_copy.shape[2], "nearest-exact", "center").squeeze(0) + + original_masks_copy[start_index:end_index] = replacement_masks + masks = original_masks_copy + else: + masks = torch.zeros((1, 64, 64)) + + return (images, masks) + + +class ReverseImageBatch: + + RETURN_TYPES = ("IMAGE",) + FUNCTION = "reverseimagebatch" + CATEGORY = "KJNodes/image" + DESCRIPTION = """ +Reverses the order of the images in a batch. +""" + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "images": ("IMAGE",), + }, + } + + def reverseimagebatch(self, images): + reversed_images = torch.flip(images, [0]) + return (reversed_images, ) + +class ImageBatchMulti: + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "inputcount": ("INT", {"default": 2, "min": 2, "max": 1000, "step": 1}), + "image_1": ("IMAGE", ), + + }, + "optional": { + "image_2": ("IMAGE", ), + } + } + + RETURN_TYPES = ("IMAGE",) + RETURN_NAMES = ("images",) + FUNCTION = "combine" + CATEGORY = "KJNodes/image" + DESCRIPTION = """ +Creates an image batch from multiple images. +You can set how many inputs the node has, +with the **inputcount** and clicking update. +""" + + def combine(self, inputcount, **kwargs): + from nodes import ImageBatch + image_batch_node = ImageBatch() + image = kwargs["image_1"].cpu() + first_image_shape = image.shape + for c in range(1, inputcount): + new_image = kwargs.get(f"image_{c + 1}", torch.zeros(first_image_shape)).cpu() + image, = image_batch_node.batch(image, new_image) + return (image,) + + +class ImageTensorList: + @classmethod + def INPUT_TYPES(s): + return {"required": { + "image1": ("IMAGE",), + "image2": ("IMAGE",), + }} + + RETURN_TYPES = ("IMAGE",) + FUNCTION = "append" + CATEGORY = "KJNodes/image" + DESCRIPTION = """ +Creates an image list from the input images. +""" + + def append(self, image1, image2): + image_list = [] + if isinstance(image1, torch.Tensor) and isinstance(image2, torch.Tensor): + image_list = [image1, image2] + elif isinstance(image1, list) and isinstance(image2, torch.Tensor): + image_list = image1 + [image2] + elif isinstance(image1, torch.Tensor) and isinstance(image2, list): + image_list = [image1] + image2 + elif isinstance(image1, list) and isinstance(image2, list): + image_list = image1 + image2 + return image_list, + +class ImageAddMulti: + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "inputcount": ("INT", {"default": 2, "min": 2, "max": 1000, "step": 1}), + "image_1": ("IMAGE", ), + "image_2": ("IMAGE", ), + "blending": ( + [ 'add', + 'subtract', + 'multiply', + 'difference', + ], + { + "default": 'add' + }), + "blend_amount": ("FLOAT", {"default": 0.5, "min": 0, "max": 1, "step": 0.01}), + }, + } + + RETURN_TYPES = ("IMAGE",) + RETURN_NAMES = ("images",) + FUNCTION = "add" + CATEGORY = "KJNodes/image" + DESCRIPTION = """ +Add blends multiple images together. +You can set how many inputs the node has, +with the **inputcount** and clicking update. +""" + + def add(self, inputcount, blending, blend_amount, **kwargs): + image = kwargs["image_1"] + for c in range(1, inputcount): + new_image = kwargs[f"image_{c + 1}"] + if blending == "add": + image = torch.add(image * blend_amount, new_image * blend_amount) + elif blending == "subtract": + image = torch.sub(image * blend_amount, new_image * blend_amount) + elif blending == "multiply": + image = torch.mul(image * blend_amount, new_image * blend_amount) + elif blending == "difference": + image = torch.sub(image, new_image) + return (image,) + +class ImageConcatMulti: + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "inputcount": ("INT", {"default": 2, "min": 2, "max": 1000, "step": 1}), + "image_1": ("IMAGE", ), + + "direction": ( + [ 'right', + 'down', + 'left', + 'up', + ], + { + "default": 'right' + }), + "match_image_size": ("BOOLEAN", {"default": False}), + }, + "optional": { + "image_2": ("IMAGE", ), + }, + } + + RETURN_TYPES = ("IMAGE",) + RETURN_NAMES = ("images",) + FUNCTION = "combine" + CATEGORY = "KJNodes/image" + DESCRIPTION = """ +Creates an image from multiple images. +You can set how many inputs the node has, +with the **inputcount** and clicking update. +""" + + def combine(self, inputcount, direction, match_image_size, **kwargs): + image = kwargs["image_1"] + first_image_shape = None + if first_image_shape is None: + first_image_shape = image.shape + for c in range(1, inputcount): + new_image = kwargs.get(f"image_{c + 1}", torch.zeros(first_image_shape)) + image, = ImageConcanate.concatenate(self, image, new_image, direction, match_image_size, first_image_shape=first_image_shape) + first_image_shape = None + return (image,) + +class PreviewAnimation: + def __init__(self): + self.output_dir = folder_paths.get_temp_directory() + self.type = "temp" + self.prefix_append = "_temp_" + ''.join(random.choice("abcdefghijklmnopqrstupvxyz") for x in range(5)) + self.compress_level = 1 + + methods = {"default": 4, "fastest": 0, "slowest": 6} + @classmethod + def INPUT_TYPES(s): + return {"required": + { + "fps": ("FLOAT", {"default": 8.0, "min": 0.01, "max": 1000.0, "step": 0.01}), + }, + "optional": { + "images": ("IMAGE", ), + "masks": ("MASK", ), + }, + } + + RETURN_TYPES = () + FUNCTION = "preview" + OUTPUT_NODE = True + CATEGORY = "KJNodes/image" + + def preview(self, fps, images=None, masks=None): + filename_prefix = "AnimPreview" + full_output_folder, filename, counter, subfolder, filename_prefix = folder_paths.get_save_image_path(filename_prefix, self.output_dir) + results = list() + + pil_images = [] + + if images is not None and masks is not None: + for image in images: + i = 255. * image.cpu().numpy() + img = Image.fromarray(np.clip(i, 0, 255).astype(np.uint8)) + pil_images.append(img) + for mask in masks: + if pil_images: + mask_np = mask.cpu().numpy() + mask_np = np.clip(mask_np * 255, 0, 255).astype(np.uint8) # Convert to values between 0 and 255 + mask_img = Image.fromarray(mask_np, mode='L') + img = pil_images.pop(0) # Remove and get the first image + img = img.convert("RGBA") # Convert base image to RGBA + + # Create a new RGBA image based on the grayscale mask + rgba_mask_img = Image.new("RGBA", img.size, (255, 255, 255, 255)) + rgba_mask_img.putalpha(mask_img) # Use the mask image as the alpha channel + + # Composite the RGBA mask onto the base image + composited_img = Image.alpha_composite(img, rgba_mask_img) + pil_images.append(composited_img) # Add the composited image back + + elif images is not None and masks is None: + for image in images: + i = 255. * image.cpu().numpy() + img = Image.fromarray(np.clip(i, 0, 255).astype(np.uint8)) + pil_images.append(img) + + elif masks is not None and images is None: + for mask in masks: + mask_np = 255. * mask.cpu().numpy() + mask_img = Image.fromarray(np.clip(mask_np, 0, 255).astype(np.uint8)) + pil_images.append(mask_img) + else: + print("PreviewAnimation: No images or masks provided") + return { "ui": { "images": results, "animated": (None,), "text": "empty" }} + + num_frames = len(pil_images) + + c = len(pil_images) + for i in range(0, c, num_frames): + file = f"{filename}_{counter:05}_.webp" + 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], lossless=False, quality=50, method=0) + results.append({ + "filename": file, + "subfolder": subfolder, + "type": self.type + }) + counter += 1 + + animated = num_frames != 1 + return { "ui": { "images": results, "animated": (animated,), "text": [f"{num_frames}x{pil_images[0].size[0]}x{pil_images[0].size[1]}"] } } + +class ImageResizeKJ: + upscale_methods = ["nearest-exact", "bilinear", "area", "bicubic", "lanczos"] + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "image": ("IMAGE",), + "width": ("INT", { "default": 512, "min": 0, "max": MAX_RESOLUTION, "step": 1, }), + "height": ("INT", { "default": 512, "min": 0, "max": MAX_RESOLUTION, "step": 1, }), + "upscale_method": (s.upscale_methods,), + "keep_proportion": ("BOOLEAN", { "default": False }), + "divisible_by": ("INT", { "default": 2, "min": 0, "max": 512, "step": 1, }), + }, + "optional" : { + #"width_input": ("INT", { "forceInput": True}), + #"height_input": ("INT", { "forceInput": True}), + "get_image_size": ("IMAGE",), + "crop": (["disabled","center", 0], { "tooltip": "0 will do the default center crop, this is a workaround for the widget order changing with the new frontend, as in old workflows the value of this widget becomes 0 automatically" }), + } + } + + RETURN_TYPES = ("IMAGE", "INT", "INT",) + RETURN_NAMES = ("IMAGE", "width", "height",) + FUNCTION = "resize" + CATEGORY = "KJNodes/image" + DEPRECATED = True + DESCRIPTION = """ +DEPRECATED! + +Due to ComfyUI frontend changes, this node should no longer be used, please check the +v2 of the node. This node is only kept to not completely break older workflows. + +""" + + def resize(self, image, width, height, keep_proportion, upscale_method, divisible_by, + width_input=None, height_input=None, get_image_size=None, crop="disabled"): + B, H, W, C = image.shape + + if width_input: + width = width_input + if height_input: + height = height_input + if get_image_size is not None: + _, height, width, _ = get_image_size.shape + + if keep_proportion and get_image_size is None: + # If one of the dimensions is zero, calculate it to maintain the aspect ratio + if width == 0 and height != 0: + ratio = height / H + width = round(W * ratio) + elif height == 0 and width != 0: + ratio = width / W + height = round(H * ratio) + elif width != 0 and height != 0: + # Scale based on which dimension is smaller in proportion to the desired dimensions + ratio = min(width / W, height / H) + width = round(W * ratio) + height = round(H * ratio) + else: + if width == 0: + width = W + if height == 0: + height = H + + if divisible_by > 1 and get_image_size is None: + width = width - (width % divisible_by) + height = height - (height % divisible_by) + + if crop == 0: #workaround for old workflows + crop = "center" + + image = image.movedim(-1,1) + image = common_upscale(image, width, height, upscale_method, crop) + image = image.movedim(1,-1) + + return(image, image.shape[2], image.shape[1],) + +class ImageResizeKJv2: + upscale_methods = ["nearest-exact", "bilinear", "area", "bicubic", "lanczos"] + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "image": ("IMAGE",), + "width": ("INT", { "default": 512, "min": 0, "max": MAX_RESOLUTION, "step": 1, }), + "height": ("INT", { "default": 512, "min": 0, "max": MAX_RESOLUTION, "step": 1, }), + "upscale_method": (s.upscale_methods,), + "keep_proportion": (["stretch", "resize", "pad", "pad_edge", "crop"], { "default": False }), + "pad_color": ("STRING", { "default": "0, 0, 0", "tooltip": "Color to use for padding."}), + "crop_position": (["center", "top", "bottom", "left", "right"], { "default": "center" }), + "divisible_by": ("INT", { "default": 2, "min": 0, "max": 512, "step": 1, }), + }, + "optional" : { + "mask": ("MASK",), + "device": (["cpu", "gpu"],), + }, + "hidden": { + "unique_id": "UNIQUE_ID", + }, + } + + RETURN_TYPES = ("IMAGE", "INT", "INT", "MASK",) + RETURN_NAMES = ("IMAGE", "width", "height", "mask",) + FUNCTION = "resize" + CATEGORY = "KJNodes/image" + DESCRIPTION = """ +Resizes the image to the specified width and height. +Size can be retrieved from the input. + +Keep proportions keeps the aspect ratio of the image, by +highest dimension. +""" + + def resize(self, image, width, height, keep_proportion, upscale_method, divisible_by, pad_color, crop_position, unique_id, device="cpu", mask=None): + B, H, W, C = image.shape + + if device == "gpu": + if upscale_method == "lanczos": + raise Exception("Lanczos is not supported on the GPU") + device = model_management.get_torch_device() + else: + device = torch.device("cpu") + + if width == 0: + width = W + if height == 0: + height = H + + if keep_proportion == "resize" or keep_proportion.startswith("pad"): + # If one of the dimensions is zero, calculate it to maintain the aspect ratio + if width == 0 and height != 0: + ratio = height / H + new_width = round(W * ratio) + elif height == 0 and width != 0: + ratio = width / W + new_height = round(H * ratio) + elif width != 0 and height != 0: + # Scale based on which dimension is smaller in proportion to the desired dimensions + ratio = min(width / W, height / H) + new_width = round(W * ratio) + new_height = round(H * ratio) + + if keep_proportion.startswith("pad"): + # Calculate padding based on position + if crop_position == "center": + pad_left = (width - new_width) // 2 + pad_right = width - new_width - pad_left + pad_top = (height - new_height) // 2 + pad_bottom = height - new_height - pad_top + elif crop_position == "top": + pad_left = (width - new_width) // 2 + pad_right = width - new_width - pad_left + pad_top = 0 + pad_bottom = height - new_height + elif crop_position == "bottom": + pad_left = (width - new_width) // 2 + pad_right = width - new_width - pad_left + pad_top = height - new_height + pad_bottom = 0 + elif crop_position == "left": + pad_left = 0 + pad_right = width - new_width + pad_top = (height - new_height) // 2 + pad_bottom = height - new_height - pad_top + elif crop_position == "right": + pad_left = width - new_width + pad_right = 0 + pad_top = (height - new_height) // 2 + pad_bottom = height - new_height - pad_top + + width = new_width + height = new_height + + if divisible_by > 1: + width = width - (width % divisible_by) + height = height - (height % divisible_by) + + out_image = image.clone().to(device) + + if mask is not None: + out_mask = mask.clone().to(device) + else: + out_mask = None + + if keep_proportion == "crop": + old_width = W + old_height = H + old_aspect = old_width / old_height + new_aspect = width / height + + # Calculate dimensions to keep + if old_aspect > new_aspect: # Image is wider than target + crop_w = round(old_height * new_aspect) + crop_h = old_height + else: # Image is taller than target + crop_w = old_width + crop_h = round(old_width / new_aspect) + + # Calculate crop position + if crop_position == "center": + x = (old_width - crop_w) // 2 + y = (old_height - crop_h) // 2 + elif crop_position == "top": + x = (old_width - crop_w) // 2 + y = 0 + elif crop_position == "bottom": + x = (old_width - crop_w) // 2 + y = old_height - crop_h + elif crop_position == "left": + x = 0 + y = (old_height - crop_h) // 2 + elif crop_position == "right": + x = old_width - crop_w + y = (old_height - crop_h) // 2 + + # Apply crop + out_image = out_image.narrow(-2, x, crop_w).narrow(-3, y, crop_h) + if mask is not None: + out_mask = out_mask.narrow(-1, x, crop_w).narrow(-2, y, crop_h) + + out_image = common_upscale(out_image.movedim(-1,1), width, height, upscale_method, crop="disabled").movedim(1,-1) + + if mask is not None: + if upscale_method == "lanczos": + out_mask = common_upscale(out_mask.unsqueeze(1).repeat(1, 3, 1, 1), width, height, upscale_method, crop="disabled").movedim(1,-1)[:, :, :, 0] + else: + out_mask = common_upscale(out_mask.unsqueeze(1), width, height, upscale_method, crop="disabled").squeeze(1) + + if keep_proportion.startswith("pad"): + if pad_left > 0 or pad_right > 0 or pad_top > 0 or pad_bottom > 0: + padded_width = width + pad_left + pad_right + padded_height = height + pad_top + pad_bottom + if divisible_by > 1: + width_remainder = padded_width % divisible_by + height_remainder = padded_height % divisible_by + if width_remainder > 0: + extra_width = divisible_by - width_remainder + pad_right += extra_width + if height_remainder > 0: + extra_height = divisible_by - height_remainder + pad_bottom += extra_height + out_image, _ = ImagePadKJ.pad(self, out_image, pad_left, pad_right, pad_top, pad_bottom, 0, pad_color, "edge" if keep_proportion == "pad_edge" else "color") + if mask is not None: + out_mask = out_mask.unsqueeze(1).repeat(1, 3, 1, 1).movedim(1,-1) + out_mask, _ = ImagePadKJ.pad(self, out_mask, pad_left, pad_right, pad_top, pad_bottom, 0, pad_color, "edge" if keep_proportion == "pad_edge" else "color") + out_mask = out_mask[:, :, :, 0] + else: + B, H_pad, W_pad, _ = out_image.shape + out_mask = torch.ones((B, H_pad, W_pad), dtype=out_image.dtype, device=out_image.device) + out_mask[:, pad_top:pad_top+height, pad_left:pad_left+width] = 0.0 + + + if unique_id and PromptServer is not None: + try: + num_elements = out_image.numel() + element_size = out_image.element_size() + memory_size_mb = (num_elements * element_size) / (1024 * 1024) + + PromptServer.instance.send_progress_text( + f"Output: {out_image.shape[0]} x {out_image.shape[2]} x {out_image.shape[1]} | {memory_size_mb:.2f}MB", + unique_id + ) + except: + pass + + return(out_image.cpu(), out_image.shape[2], out_image.shape[1], out_mask.cpu() if out_mask is not None else torch.zeros(64,64, device=torch.device("cpu"), dtype=torch.float32)) + +import pathlib +class LoadAndResizeImage: + _color_channels = ["alpha", "red", "green", "blue"] + @classmethod + def INPUT_TYPES(s): + input_dir = folder_paths.get_input_directory() + files = [f.name for f in pathlib.Path(input_dir).iterdir() if f.is_file()] + return {"required": + { + "image": (sorted(files), {"image_upload": True}), + "resize": ("BOOLEAN", { "default": False }), + "width": ("INT", { "default": 512, "min": 0, "max": MAX_RESOLUTION, "step": 8, }), + "height": ("INT", { "default": 512, "min": 0, "max": MAX_RESOLUTION, "step": 8, }), + "repeat": ("INT", { "default": 1, "min": 1, "max": 4096, "step": 1, }), + "keep_proportion": ("BOOLEAN", { "default": False }), + "divisible_by": ("INT", { "default": 2, "min": 0, "max": 512, "step": 1, }), + "mask_channel": (s._color_channels, {"tooltip": "Channel to use for the mask output"}), + "background_color": ("STRING", { "default": "", "tooltip": "Fills the alpha channel with the specified color."}), + }, + } + + CATEGORY = "KJNodes/image" + RETURN_TYPES = ("IMAGE", "MASK", "INT", "INT", "STRING",) + RETURN_NAMES = ("image", "mask", "width", "height","image_path",) + FUNCTION = "load_image" + + def load_image(self, image, resize, width, height, repeat, keep_proportion, divisible_by, mask_channel, background_color): + from PIL import ImageColor, Image, ImageOps, ImageSequence + import numpy as np + import torch + image_path = folder_paths.get_annotated_filepath(image) + + import node_helpers + img = node_helpers.pillow(Image.open, image_path) + + # Process the background_color + if background_color: + try: + # Try to parse as RGB tuple + bg_color_rgba = tuple(int(x.strip()) for x in background_color.split(',')) + except ValueError: + # If parsing fails, it might be a hex color or named color + if background_color.startswith('#') or background_color.lower() in ImageColor.colormap: + bg_color_rgba = ImageColor.getrgb(background_color) + else: + raise ValueError(f"Invalid background color: {background_color}") + + bg_color_rgba += (255,) # Add alpha channel + else: + bg_color_rgba = None # No background color specified + + output_images = [] + output_masks = [] + w, h = None, None + + excluded_formats = ['MPO'] + + W, H = img.size + if resize: + if keep_proportion: + ratio = min(width / W, height / H) + width = round(W * ratio) + height = round(H * ratio) + else: + if width == 0: + width = W + if height == 0: + height = H + + if divisible_by > 1: + width = width - (width % divisible_by) + height = height - (height % divisible_by) + else: + width, height = W, H + + for frame in ImageSequence.Iterator(img): + frame = node_helpers.pillow(ImageOps.exif_transpose, frame) + + if frame.mode == 'I': + frame = frame.point(lambda i: i * (1 / 255)) + + if frame.mode == 'P': + frame = frame.convert("RGBA") + elif 'A' in frame.getbands(): + frame = frame.convert("RGBA") + + # Extract alpha channel if it exists + if 'A' in frame.getbands() and bg_color_rgba: + alpha_mask = np.array(frame.getchannel('A')).astype(np.float32) / 255.0 + alpha_mask = 1. - torch.from_numpy(alpha_mask) + bg_image = Image.new("RGBA", frame.size, bg_color_rgba) + # Composite the frame onto the background + frame = Image.alpha_composite(bg_image, frame) + else: + alpha_mask = torch.zeros((64, 64), dtype=torch.float32, device="cpu") + + image = frame.convert("RGB") + + if len(output_images) == 0: + w = image.size[0] + h = image.size[1] + + if image.size[0] != w or image.size[1] != h: + continue + if resize: + image = image.resize((width, height), Image.Resampling.BILINEAR) + + image = np.array(image).astype(np.float32) / 255.0 + image = torch.from_numpy(image)[None,] + + c = mask_channel[0].upper() + if c in frame.getbands(): + if resize: + frame = frame.resize((width, height), Image.Resampling.BILINEAR) + mask = np.array(frame.getchannel(c)).astype(np.float32) / 255.0 + mask = torch.from_numpy(mask) + if c == 'A' and bg_color_rgba: + mask = alpha_mask + elif c == 'A': + mask = 1. - mask + else: + mask = torch.zeros((64, 64), dtype=torch.float32, device="cpu") + + output_images.append(image) + output_masks.append(mask.unsqueeze(0)) + + if len(output_images) > 1 and img.format not in excluded_formats: + output_image = torch.cat(output_images, dim=0) + output_mask = torch.cat(output_masks, dim=0) + else: + output_image = output_images[0] + output_mask = output_masks[0] + if repeat > 1: + output_image = output_image.repeat(repeat, 1, 1, 1) + output_mask = output_mask.repeat(repeat, 1, 1) + + return (output_image, output_mask, width, height, image_path) + + + # @classmethod + # def IS_CHANGED(s, image, **kwargs): + # image_path = folder_paths.get_annotated_filepath(image) + # m = hashlib.sha256() + # with open(image_path, 'rb') as f: + # m.update(f.read()) + # return m.digest().hex() + + @classmethod + def VALIDATE_INPUTS(s, image): + if not folder_paths.exists_annotated_filepath(image): + return "Invalid image file: {}".format(image) + + return True + +import hashlib +class LoadImagesFromFolderKJ: + # Dictionary to store folder hashes + folder_hashes = {} + + @classmethod + def IS_CHANGED(cls, folder, **kwargs): + if not os.path.isdir(folder): + return float("NaN") + + valid_extensions = ['.jpg', '.jpeg', '.png', '.webp', '.tga'] + include_subfolders = kwargs.get('include_subfolders', False) + + file_data = [] + if include_subfolders: + for root, _, files in os.walk(folder): + for file in files: + if any(file.lower().endswith(ext) for ext in valid_extensions): + path = os.path.join(root, file) + try: + mtime = os.path.getmtime(path) + file_data.append((path, mtime)) + except OSError: + pass + else: + for file in os.listdir(folder): + if any(file.lower().endswith(ext) for ext in valid_extensions): + path = os.path.join(folder, file) + try: + mtime = os.path.getmtime(path) + file_data.append((path, mtime)) + except OSError: + pass + + file_data.sort() + + combined_hash = hashlib.md5() + combined_hash.update(folder.encode('utf-8')) + combined_hash.update(str(len(file_data)).encode('utf-8')) + + for path, mtime in file_data: + combined_hash.update(f"{path}:{mtime}".encode('utf-8')) + + current_hash = combined_hash.hexdigest() + + old_hash = cls.folder_hashes.get(folder) + cls.folder_hashes[folder] = current_hash + + if old_hash == current_hash: + return old_hash + + return current_hash + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "folder": ("STRING", {"default": ""}), + "width": ("INT", {"default": 1024, "min": -1, "step": 1}), + "height": ("INT", {"default": 1024, "min": -1, "step": 1}), + "keep_aspect_ratio": (["crop", "pad", "stretch",],), + }, + "optional": { + "image_load_cap": ("INT", {"default": 0, "min": 0, "step": 1}), + "start_index": ("INT", {"default": 0, "min": 0, "step": 1}), + "include_subfolders": ("BOOLEAN", {"default": False}), + } + } + + RETURN_TYPES = ("IMAGE", "MASK", "INT", "STRING",) + RETURN_NAMES = ("image", "mask", "count", "image_path",) + FUNCTION = "load_images" + CATEGORY = "KJNodes/image" + DESCRIPTION = """Loads images from a folder into a batch, images are resized and loaded into a batch.""" + + def load_images(self, folder, width, height, image_load_cap, start_index, keep_aspect_ratio, include_subfolders=False): + if not os.path.isdir(folder): + raise FileNotFoundError(f"Folder '{folder} cannot be found.'") + + valid_extensions = ['.jpg', '.jpeg', '.png', '.webp', '.tga'] + image_paths = [] + if include_subfolders: + for root, _, files in os.walk(folder): + for file in files: + if any(file.lower().endswith(ext) for ext in valid_extensions): + image_paths.append(os.path.join(root, file)) + else: + for file in os.listdir(folder): + if any(file.lower().endswith(ext) for ext in valid_extensions): + image_paths.append(os.path.join(folder, file)) + + dir_files = sorted(image_paths) + + if len(dir_files) == 0: + raise FileNotFoundError(f"No files in directory '{folder}'.") + + # start at start_index + dir_files = dir_files[start_index:] + + images = [] + masks = [] + image_path_list = [] + + limit_images = False + if image_load_cap > 0: + limit_images = True + image_count = 0 + + for image_path in dir_files: + if os.path.isdir(image_path): + continue + if limit_images and image_count >= image_load_cap: + break + i = Image.open(image_path) + i = ImageOps.exif_transpose(i) + + # Resize image to maximum dimensions + if width == -1 and height == -1: + width = i.size[0] + height = i.size[1] + if i.size != (width, height): + i = self.resize_with_aspect_ratio(i, width, height, keep_aspect_ratio) + + + image = i.convert("RGB") + image = np.array(image).astype(np.float32) / 255.0 + image = torch.from_numpy(image)[None,] + + if 'A' in i.getbands(): + mask = np.array(i.getchannel('A')).astype(np.float32) / 255.0 + mask = 1. - torch.from_numpy(mask) + if mask.shape != (height, width): + mask = torch.nn.functional.interpolate(mask.unsqueeze(0).unsqueeze(0), + size=(height, width), + mode='bilinear', + align_corners=False).squeeze() + else: + mask = torch.zeros((height, width), dtype=torch.float32, device="cpu") + + images.append(image) + masks.append(mask) + image_path_list.append(image_path) + image_count += 1 + + if len(images) == 1: + return (images[0], masks[0], 1, image_path_list) + + elif len(images) > 1: + image1 = images[0] + mask1 = masks[0].unsqueeze(0) + + for image2 in images[1:]: + image1 = torch.cat((image1, image2), dim=0) + + for mask2 in masks[1:]: + mask1 = torch.cat((mask1, mask2.unsqueeze(0)), dim=0) + + return (image1, mask1, len(images), image_path_list) + def resize_with_aspect_ratio(self, img, width, height, mode): + if mode == "stretch": + return img.resize((width, height), Image.Resampling.LANCZOS) + + img_width, img_height = img.size + aspect_ratio = img_width / img_height + target_ratio = width / height + + if mode == "crop": + # Calculate dimensions for center crop + if aspect_ratio > target_ratio: + # Image is wider - crop width + new_width = int(height * aspect_ratio) + img = img.resize((new_width, height), Image.Resampling.LANCZOS) + left = (new_width - width) // 2 + return img.crop((left, 0, left + width, height)) + else: + # Image is taller - crop height + new_height = int(width / aspect_ratio) + img = img.resize((width, new_height), Image.Resampling.LANCZOS) + top = (new_height - height) // 2 + return img.crop((0, top, width, top + height)) + + elif mode == "pad": + pad_color = self.get_edge_color(img) + # Calculate dimensions for padding + if aspect_ratio > target_ratio: + # Image is wider - pad height + new_height = int(width / aspect_ratio) + img = img.resize((width, new_height), Image.Resampling.LANCZOS) + padding = (height - new_height) // 2 + padded = Image.new('RGBA', (width, height), pad_color) + padded.paste(img, (0, padding)) + return padded + else: + # Image is taller - pad width + new_width = int(height * aspect_ratio) + img = img.resize((new_width, height), Image.Resampling.LANCZOS) + padding = (width - new_width) // 2 + padded = Image.new('RGBA', (width, height), pad_color) + padded.paste(img, (padding, 0)) + return padded + def get_edge_color(self, img): + from PIL import ImageStat + """Sample edges and return dominant color""" + width, height = img.size + img = img.convert('RGBA') + + # Create 1-pixel high/wide images from edges + top = img.crop((0, 0, width, 1)) + bottom = img.crop((0, height-1, width, height)) + left = img.crop((0, 0, 1, height)) + right = img.crop((width-1, 0, width, height)) + + # Combine edges into single image + edges = Image.new('RGBA', (width*2 + height*2, 1)) + edges.paste(top, (0, 0)) + edges.paste(bottom, (width, 0)) + edges.paste(left.resize((height, 1)), (width*2, 0)) + edges.paste(right.resize((height, 1)), (width*2 + height, 0)) + + # Get median color + stat = ImageStat.Stat(edges) + median = tuple(map(int, stat.median)) + return median + + +class ImageGridtoBatch: + @classmethod + def INPUT_TYPES(s): + return {"required": { + "image": ("IMAGE", ), + "columns": ("INT", {"default": 3, "min": 1, "max": 8, "tooltip": "The number of columns in the grid."}), + "rows": ("INT", {"default": 0, "min": 1, "max": 8, "tooltip": "The number of rows in the grid. Set to 0 for automatic calculation."}), + } + } + + RETURN_TYPES = ("IMAGE",) + FUNCTION = "decompose" + CATEGORY = "KJNodes/image" + DESCRIPTION = "Converts a grid of images to a batch of images." + + def decompose(self, image, columns, rows): + B, H, W, C = image.shape + print("input size: ", image.shape) + + # Calculate cell width, rounding down + cell_width = W // columns + + if rows == 0: + # If rows is 0, calculate number of full rows + rows = H // cell_height + else: + # If rows is specified, adjust cell_height + cell_height = H // rows + + # Crop the image to fit full cells + image = image[:, :rows*cell_height, :columns*cell_width, :] + + # Reshape and permute the image to get the grid + image = image.view(B, rows, cell_height, columns, cell_width, C) + image = image.permute(0, 1, 3, 2, 4, 5).contiguous() + image = image.view(B, rows * columns, cell_height, cell_width, C) + + # Reshape to the final batch tensor + img_tensor = image.view(-1, cell_height, cell_width, C) + + return (img_tensor,) + +class SaveImageKJ: + def __init__(self): + self.type = "output" + self.prefix_append = "" + self.compress_level = 4 + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "images": ("IMAGE", {"tooltip": "The images to save."}), + "filename_prefix": ("STRING", {"default": "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."}), + "output_folder": ("STRING", {"default": "output", "tooltip": "The folder to save the images to."}), + }, + "optional": { + "caption_file_extension": ("STRING", {"default": ".txt", "tooltip": "The extension for the caption file."}), + "caption": ("STRING", {"forceInput": True, "tooltip": "string to save as .txt file"}), + }, + "hidden": { + "prompt": "PROMPT", "extra_pnginfo": "EXTRA_PNGINFO" + }, + } + + RETURN_TYPES = ("STRING",) + RETURN_NAMES = ("filename",) + FUNCTION = "save_images" + + OUTPUT_NODE = True + + CATEGORY = "KJNodes/image" + DESCRIPTION = "Saves the input images to your ComfyUI output directory." + + def save_images(self, images, output_folder, filename_prefix="ComfyUI", prompt=None, extra_pnginfo=None, caption=None, caption_file_extension=".txt"): + filename_prefix += self.prefix_append + + if os.path.isabs(output_folder): + if not os.path.exists(output_folder): + os.makedirs(output_folder, exist_ok=True) + full_output_folder = output_folder + _, filename, counter, subfolder, filename_prefix = folder_paths.get_save_image_path(filename_prefix, output_folder, images[0].shape[1], images[0].shape[0]) + else: + self.output_dir = folder_paths.get_output_directory() + 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() + for (batch_number, image) in enumerate(images): + i = 255. * image.cpu().numpy() + img = Image.fromarray(np.clip(i, 0, 255).astype(np.uint8)) + metadata = None + if not args.disable_metadata: + metadata = PngInfo() + if prompt is not None: + metadata.add_text("prompt", json.dumps(prompt)) + if extra_pnginfo is not None: + for x in extra_pnginfo: + metadata.add_text(x, json.dumps(extra_pnginfo[x])) + + filename_with_batch_num = filename.replace("%batch_num%", str(batch_number)) + base_file_name = f"{filename_with_batch_num}_{counter:05}_" + file = f"{base_file_name}.png" + img.save(os.path.join(full_output_folder, file), pnginfo=metadata, compress_level=self.compress_level) + results.append({ + "filename": file, + "subfolder": subfolder, + "type": self.type + }) + if caption is not None: + txt_file = base_file_name + caption_file_extension + file_path = os.path.join(full_output_folder, txt_file) + with open(file_path, 'w') as f: + f.write(caption) + + counter += 1 + + return file, + +class SaveStringKJ: + def __init__(self): + self.output_dir = folder_paths.get_output_directory() + self.type = "output" + self.prefix_append = "" + self.compress_level = 4 + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "string": ("STRING", {"forceInput": True, "tooltip": "string to save as .txt file"}), + "filename_prefix": ("STRING", {"default": "text", "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."}), + "output_folder": ("STRING", {"default": "output", "tooltip": "The folder to save the images to."}), + }, + "optional": { + "file_extension": ("STRING", {"default": ".txt", "tooltip": "The extension for the caption file."}), + }, + } + + RETURN_TYPES = ("STRING",) + RETURN_NAMES = ("filename",) + FUNCTION = "save_string" + + OUTPUT_NODE = True + + CATEGORY = "KJNodes/misc" + DESCRIPTION = "Saves the input string to your ComfyUI output directory." + + def save_string(self, string, output_folder, filename_prefix="text", file_extension=".txt"): + filename_prefix += self.prefix_append + + full_output_folder, filename, counter, subfolder, filename_prefix = folder_paths.get_save_image_path(filename_prefix, self.output_dir) + if output_folder != "output": + if not os.path.exists(output_folder): + os.makedirs(output_folder, exist_ok=True) + full_output_folder = output_folder + + base_file_name = f"{filename_prefix}_{counter:05}_" + results = list() + + txt_file = base_file_name + file_extension + file_path = os.path.join(full_output_folder, txt_file) + with open(file_path, 'w') as f: + f.write(string) + + return results, + +to_pil_image = T.ToPILImage() + +class FastPreview: + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "image": ("IMAGE", ), + "format": (["JPEG", "PNG", "WEBP"], {"default": "JPEG"}), + "quality" : ("INT", {"default": 75, "min": 1, "max": 100, "step": 1}), + }, + } + + RETURN_TYPES = () + FUNCTION = "preview" + CATEGORY = "KJNodes/experimental" + OUTPUT_NODE = True + DESCRIPTION = "Experimental node for faster image previews by displaying through base64 it without saving to disk." + + def preview(self, image, format, quality): + pil_image = to_pil_image(image[0].permute(2, 0, 1)) + + with io.BytesIO() as buffered: + pil_image.save(buffered, format=format, quality=quality) + img_bytes = buffered.getvalue() + + img_base64 = base64.b64encode(img_bytes).decode('utf-8') + + return { + "ui": {"bg_image": [img_base64]}, + "result": () + } + +class ImageCropByMaskAndResize: + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "image": ("IMAGE", ), + "mask": ("MASK", ), + "base_resolution": ("INT", { "default": 512, "min": 0, "max": MAX_RESOLUTION, "step": 8, }), + "padding": ("INT", { "default": 0, "min": 0, "max": MAX_RESOLUTION, "step": 1, }), + "min_crop_resolution": ("INT", { "default": 128, "min": 0, "max": MAX_RESOLUTION, "step": 8, }), + "max_crop_resolution": ("INT", { "default": 512, "min": 0, "max": MAX_RESOLUTION, "step": 8, }), + + }, + } + + RETURN_TYPES = ("IMAGE", "MASK", "BBOX", ) + RETURN_NAMES = ("images", "masks", "bbox",) + FUNCTION = "crop" + CATEGORY = "KJNodes/image" + + def crop_by_mask(self, mask, padding=0, min_crop_resolution=None, max_crop_resolution=None): + iy, ix = (mask == 1).nonzero(as_tuple=True) + h0, w0 = mask.shape + + if iy.numel() == 0: + x_c = w0 / 2.0 + y_c = h0 / 2.0 + width = 0 + height = 0 + else: + x_min = ix.min().item() + x_max = ix.max().item() + y_min = iy.min().item() + y_max = iy.max().item() + + width = x_max - x_min + height = y_max - y_min + + if width > w0 or height > h0: + raise Exception("Masked area out of bounds") + + x_c = (x_min + x_max) / 2.0 + y_c = (y_min + y_max) / 2.0 + + if min_crop_resolution: + width = max(width, min_crop_resolution) + height = max(height, min_crop_resolution) + + if max_crop_resolution: + width = min(width, max_crop_resolution) + height = min(height, max_crop_resolution) + + if w0 <= width: + x0 = 0 + w = w0 + else: + x0 = max(0, x_c - width / 2 - padding) + w = width + 2 * padding + if x0 + w > w0: + x0 = w0 - w + + if h0 <= height: + y0 = 0 + h = h0 + else: + y0 = max(0, y_c - height / 2 - padding) + h = height + 2 * padding + if y0 + h > h0: + y0 = h0 - h + + return (int(x0), int(y0), int(w), int(h)) + + def crop(self, image, mask, base_resolution, padding=0, min_crop_resolution=128, max_crop_resolution=512): + mask = mask.round() + image_list = [] + mask_list = [] + bbox_list = [] + + # First, collect all bounding boxes + bbox_params = [] + aspect_ratios = [] + for i in range(image.shape[0]): + x0, y0, w, h = self.crop_by_mask(mask[i], padding, min_crop_resolution, max_crop_resolution) + bbox_params.append((x0, y0, w, h)) + aspect_ratios.append(w / h) + + # Find maximum width and height + max_w = max([w for x0, y0, w, h in bbox_params]) + max_h = max([h for x0, y0, w, h in bbox_params]) + max_aspect_ratio = max(aspect_ratios) + + # Ensure dimensions are divisible by 16 + max_w = (max_w + 15) // 16 * 16 + max_h = (max_h + 15) // 16 * 16 + # Calculate common target dimensions + if max_aspect_ratio > 1: + target_width = base_resolution + target_height = int(base_resolution / max_aspect_ratio) + else: + target_height = base_resolution + target_width = int(base_resolution * max_aspect_ratio) + + for i in range(image.shape[0]): + x0, y0, w, h = bbox_params[i] + + # Adjust cropping to use maximum width and height + x_center = x0 + w / 2 + y_center = y0 + h / 2 + + x0_new = int(max(0, x_center - max_w / 2)) + y0_new = int(max(0, y_center - max_h / 2)) + x1_new = int(min(x0_new + max_w, image.shape[2])) + y1_new = int(min(y0_new + max_h, image.shape[1])) + x0_new = x1_new - max_w + y0_new = y1_new - max_h + + cropped_image = image[i][y0_new:y1_new, x0_new:x1_new, :] + cropped_mask = mask[i][y0_new:y1_new, x0_new:x1_new] + + # Ensure dimensions are divisible by 16 + target_width = (target_width + 15) // 16 * 16 + target_height = (target_height + 15) // 16 * 16 + + cropped_image = cropped_image.unsqueeze(0).movedim(-1, 1) # Move C to the second position (B, C, H, W) + cropped_image = common_upscale(cropped_image, target_width, target_height, "lanczos", "disabled") + cropped_image = cropped_image.movedim(1, -1).squeeze(0) + + cropped_mask = cropped_mask.unsqueeze(0).unsqueeze(0) + cropped_mask = common_upscale(cropped_mask, target_width, target_height, 'bilinear', "disabled") + cropped_mask = cropped_mask.squeeze(0).squeeze(0) + + image_list.append(cropped_image) + mask_list.append(cropped_mask) + bbox_list.append((x0_new, y0_new, x1_new, y1_new)) + + + return (torch.stack(image_list), torch.stack(mask_list), bbox_list) + +class ImageCropByMask: + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "image": ("IMAGE", ), + "mask": ("MASK", ), + }, + } + + RETURN_TYPES = ("IMAGE", ) + RETURN_NAMES = ("image", ) + FUNCTION = "crop" + CATEGORY = "KJNodes/image" + DESCRIPTION = "Crops the input images based on the provided mask." + + def crop(self, image, mask): + B, H, W, C = image.shape + mask = mask.round() + + # Find bounding box for each batch + crops = [] + + for b in range(B): + # Get coordinates of non-zero elements + rows = torch.any(mask[min(b, mask.shape[0]-1)] > 0, dim=1) + cols = torch.any(mask[min(b, mask.shape[0]-1)] > 0, dim=0) + + # Find boundaries + y_min, y_max = torch.where(rows)[0][[0, -1]] + x_min, x_max = torch.where(cols)[0][[0, -1]] + + # Crop image and mask + crop = image[b:b+1, y_min:y_max+1, x_min:x_max+1, :] + crops.append(crop) + + # Stack results back together + cropped_images = torch.cat(crops, dim=0) + + return (cropped_images, ) + + + +class ImageUncropByMask: + + @classmethod + def INPUT_TYPES(s): + return {"required": + { + "destination": ("IMAGE",), + "source": ("IMAGE",), + "mask": ("MASK",), + "bbox": ("BBOX",), + }, + } + + CATEGORY = "KJNodes/image" + RETURN_TYPES = ("IMAGE",) + RETURN_NAMES = ("image",) + FUNCTION = "uncrop" + + def uncrop(self, destination, source, mask, bbox=None): + + output_list = [] + + B, H, W, C = destination.shape + + for i in range(source.shape[0]): + x0, y0, x1, y1 = bbox[i] + bbox_height = y1 - y0 + bbox_width = x1 - x0 + + # Resize source image to match the bounding box dimensions + #resized_source = F.interpolate(source[i].unsqueeze(0).movedim(-1, 1), size=(bbox_height, bbox_width), mode='bilinear', align_corners=False) + resized_source = common_upscale(source[i].unsqueeze(0).movedim(-1, 1), bbox_width, bbox_height, "lanczos", "disabled") + resized_source = resized_source.movedim(1, -1).squeeze(0) + + # Resize mask to match the bounding box dimensions + resized_mask = common_upscale(mask[i].unsqueeze(0).unsqueeze(0), bbox_width, bbox_height, "bilinear", "disabled") + resized_mask = resized_mask.squeeze(0).squeeze(0) + + # Calculate padding values + pad_left = x0 + pad_right = W - x1 + pad_top = y0 + pad_bottom = H - y1 + + # Pad the resized source image and mask to fit the destination dimensions + padded_source = F.pad(resized_source, pad=(0, 0, pad_left, pad_right, pad_top, pad_bottom), mode='constant', value=0) + padded_mask = F.pad(resized_mask, pad=(pad_left, pad_right, pad_top, pad_bottom), mode='constant', value=0) + + # Ensure the padded mask has the correct shape + padded_mask = padded_mask.unsqueeze(2).expand(-1, -1, destination[i].shape[2]) + # Ensure the padded source has the correct shape + padded_source = padded_source.unsqueeze(2).expand(-1, -1, -1, destination[i].shape[2]).squeeze(2) + + # Combine the destination and padded source images using the mask + result = destination[i] * (1.0 - padded_mask) + padded_source * padded_mask + + output_list.append(result) + + + return (torch.stack(output_list),) + +class ImageCropByMaskBatch: + @classmethod + def INPUT_TYPES(s): + return {"required": { + "image": ("IMAGE", ), + "masks": ("MASK", ), + "width": ("INT", {"default": 512, "min": 0, "max": MAX_RESOLUTION, "step": 8, }), + "height": ("INT", {"default": 512, "min": 0, "max": MAX_RESOLUTION, "step": 8, }), + "padding": ("INT", {"default": 0, "min": 0, "max": 4096, "step": 1, }), + "preserve_size": ("BOOLEAN", {"default": False}), + "bg_color": ("STRING", {"default": "0, 0, 0", "tooltip": "Color as RGB values in range 0-255, separated by commas."}), + } + } + + RETURN_TYPES = ("IMAGE", "MASK", ) + RETURN_NAMES = ("images", "masks",) + FUNCTION = "crop" + CATEGORY = "KJNodes/image" + DESCRIPTION = "Crops the input images based on the provided masks." + + def crop(self, image, masks, width, height, bg_color, padding, preserve_size): + B, H, W, C = image.shape + BM, HM, WM = masks.shape + mask_count = BM + if HM != H or WM != W: + masks = F.interpolate(masks.unsqueeze(1), size=(H, W), mode='nearest-exact').squeeze(1) + print(masks.shape) + output_images = [] + output_masks = [] + + bg_color = [int(x.strip())/255.0 for x in bg_color.split(",")] + + # For each mask + for i in range(mask_count): + curr_mask = masks[i] + + # Find bounds + y_indices, x_indices = torch.nonzero(curr_mask, as_tuple=True) + if len(y_indices) == 0 or len(x_indices) == 0: + continue + + # Get exact bounds with padding + min_y = max(0, y_indices.min().item() - padding) + max_y = min(H, y_indices.max().item() + 1 + padding) + min_x = max(0, x_indices.min().item() - padding) + max_x = min(W, x_indices.max().item() + 1 + padding) + + # Ensure mask has correct shape for multiplication + curr_mask = curr_mask.unsqueeze(-1).expand(-1, -1, C) + + # Crop image and mask together + cropped_img = image[0, min_y:max_y, min_x:max_x, :] + cropped_mask = curr_mask[min_y:max_y, min_x:max_x, :] + + crop_h, crop_w = cropped_img.shape[0:2] + new_w = crop_w + new_h = crop_h + + if not preserve_size or crop_w > width or crop_h > height: + scale = min(width/crop_w, height/crop_h) + new_w = int(crop_w * scale) + new_h = int(crop_h * scale) + + # Resize RGB + resized_img = common_upscale(cropped_img.permute(2,0,1).unsqueeze(0), new_w, new_h, "lanczos", "disabled").squeeze(0).permute(1,2,0) + resized_mask = torch.nn.functional.interpolate( + cropped_mask.permute(2,0,1).unsqueeze(0), + size=(new_h, new_w), + mode='nearest' + ).squeeze(0).permute(1,2,0) + else: + resized_img = cropped_img + resized_mask = cropped_mask + + # Create empty tensors + new_img = torch.zeros((height, width, 3), dtype=image.dtype) + new_mask = torch.zeros((height, width), dtype=image.dtype) + + # Pad both + pad_x = (width - new_w) // 2 + pad_y = (height - new_h) // 2 + new_img[pad_y:pad_y+new_h, pad_x:pad_x+new_w, :] = resized_img + if len(resized_mask.shape) == 3: + resized_mask = resized_mask[:,:,0] # Take first channel if 3D + new_mask[pad_y:pad_y+new_h, pad_x:pad_x+new_w] = resized_mask + + output_images.append(new_img) + output_masks.append(new_mask) + + if not output_images: + return (torch.zeros((0, height, width, 3), dtype=image.dtype),) + + out_rgb = torch.stack(output_images, dim=0) + out_masks = torch.stack(output_masks, dim=0) + + # Apply mask to RGB + mask_expanded = out_masks.unsqueeze(-1).expand(-1, -1, -1, 3) + background_color = torch.tensor(bg_color, dtype=torch.float32, device=image.device) + out_rgb = out_rgb * mask_expanded + background_color * (1 - mask_expanded) + + return (out_rgb, out_masks) + +class ImagePadKJ: + @classmethod + def INPUT_TYPES(s): + return {"required": { + "image": ("IMAGE", ), + "left": ("INT", {"default": 0, "min": 0, "max": MAX_RESOLUTION, "step": 1, }), + "right": ("INT", {"default": 0, "min": 0, "max": MAX_RESOLUTION, "step": 1, }), + "top": ("INT", {"default": 0, "min": 0, "max": MAX_RESOLUTION, "step": 1, }), + "bottom": ("INT", {"default": 0, "min": 0, "max": MAX_RESOLUTION, "step": 1, }), + "extra_padding": ("INT", {"default": 0, "min": 0, "max": MAX_RESOLUTION, "step": 1, }), + "pad_mode": (["edge", "color"],), + "color": ("STRING", {"default": "0, 0, 0", "tooltip": "Color as RGB values in range 0-255, separated by commas."}), + }, + "optional": { + "mask": ("MASK", ), + "target_width": ("INT", {"default": 512, "min": 0, "max": MAX_RESOLUTION, "step": 1, "forceInput": True}), + "target_height": ("INT", {"default": 512, "min": 0, "max": MAX_RESOLUTION, "step": 1, "forceInput": True}), + } + } + + RETURN_TYPES = ("IMAGE", "MASK", ) + RETURN_NAMES = ("images", "masks",) + FUNCTION = "pad" + CATEGORY = "KJNodes/image" + DESCRIPTION = "Pad the input image and optionally mask with the specified padding." + + def pad(self, image, left, right, top, bottom, extra_padding, color, pad_mode, mask=None, target_width=None, target_height=None): + B, H, W, C = image.shape + + # Resize masks to image dimensions if necessary + if mask is not None: + BM, HM, WM = mask.shape + if HM != H or WM != W: + mask = F.interpolate(mask.unsqueeze(1), size=(H, W), mode='nearest-exact').squeeze(1) + + # Parse background color + bg_color = [int(x.strip())/255.0 for x in color.split(",")] + if len(bg_color) == 1: + bg_color = bg_color * 3 # Grayscale to RGB + bg_color = torch.tensor(bg_color, dtype=image.dtype, device=image.device) + + # Calculate padding sizes with extra padding + if target_width is not None and target_height is not None: + if extra_padding > 0: + image = common_upscale(image.movedim(-1, 1), W - extra_padding, H - extra_padding, "lanczos", "disabled").movedim(1, -1) + B, H, W, C = image.shape + + padded_width = target_width + padded_height = target_height + pad_left = (padded_width - W) // 2 + pad_right = padded_width - W - pad_left + pad_top = (padded_height - H) // 2 + pad_bottom = padded_height - H - pad_top + else: + pad_left = left + extra_padding + pad_right = right + extra_padding + pad_top = top + extra_padding + pad_bottom = bottom + extra_padding + + padded_width = W + pad_left + pad_right + padded_height = H + pad_top + pad_bottom + out_image = torch.zeros((B, padded_height, padded_width, C), dtype=image.dtype, device=image.device) + + # Fill padded areas + for b in range(B): + if pad_mode == "edge": + # Pad with edge color + # Define edge pixels + top_edge = image[b, 0, :, :] + bottom_edge = image[b, H-1, :, :] + left_edge = image[b, :, 0, :] + right_edge = image[b, :, W-1, :] + + # Fill borders with edge colors + out_image[b, :pad_top, :, :] = top_edge.mean(dim=0) + out_image[b, pad_top+H:, :, :] = bottom_edge.mean(dim=0) + out_image[b, :, :pad_left, :] = left_edge.mean(dim=0) + out_image[b, :, pad_left+W:, :] = right_edge.mean(dim=0) + out_image[b, pad_top:pad_top+H, pad_left:pad_left+W, :] = image[b] + else: + # Pad with specified background color + out_image[b, :, :, :] = bg_color.unsqueeze(0).unsqueeze(0) # Expand for H and W dimensions + out_image[b, pad_top:pad_top+H, pad_left:pad_left+W, :] = image[b] + + + if mask is not None: + out_masks = torch.nn.functional.pad( + mask, + (pad_left, pad_right, pad_top, pad_bottom), + mode='replicate' + ) + else: + out_masks = torch.ones((B, padded_height, padded_width), dtype=image.dtype, device=image.device) + for m in range(B): + out_masks[m, pad_top:pad_top+H, pad_left:pad_left+W] = 0.0 + + return (out_image, out_masks) + +# extends https://github.com/Kosinkadink/ComfyUI-VideoHelperSuite +class LoadVideosFromFolder: + @classmethod + def __init__(cls): + try: + cls.vhs_nodes = importlib.import_module("ComfyUI-VideoHelperSuite.videohelpersuite") + except ImportError: + try: + cls.vhs_nodes = importlib.import_module("comfyui-videohelpersuite.videohelpersuite") + except ImportError: + raise ImportError("This node requires ComfyUI-VideoHelperSuite to be installed.") + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "video": ("STRING", {"default": "X://insert/path/"},), + "force_rate": ("FLOAT", {"default": 0, "min": 0, "max": 60, "step": 1, "disable": 0}), + "custom_width": ("INT", {"default": 0, "min": 0, "max": 4096, 'disable': 0}), + "custom_height": ("INT", {"default": 0, "min": 0, "max": 4096, 'disable': 0}), + "frame_load_cap": ("INT", {"default": 0, "min": 0, "max": 10000, "step": 1, "disable": 0}), + "skip_first_frames": ("INT", {"default": 0, "min": 0, "max": 10000, "step": 1}), + "select_every_nth": ("INT", {"default": 1, "min": 1, "max": 1000, "step": 1}), + "output_type": (["batch", "grid"], {"default": "batch"}), + "grid_max_columns": ("INT", {"default": 4, "min": 1, "max": 16, "step": 1, "disable": 1}), + "add_label": ( "BOOLEAN", {"default": False} ), + }, + "hidden": { + "force_size": "STRING", + "unique_id": "UNIQUE_ID" + }, + } + + CATEGORY = "KJNodes/misc" + + RETURN_TYPES = ("IMAGE", ) + RETURN_NAMES = ("IMAGE", ) + + FUNCTION = "load_video" + + def load_video(self, output_type, grid_max_columns, add_label=False, **kwargs): + if self.vhs_nodes is None: + raise ImportError("This node requires ComfyUI-VideoHelperSuite to be installed.") + videos_list = [] + filenames = [] + for f in os.listdir(kwargs['video']): + if os.path.isfile(os.path.join(kwargs['video'], f)): + file_parts = f.split('.') + if len(file_parts) > 1 and (file_parts[-1].lower() in ['webm', 'mp4', 'mkv', 'gif', 'mov']): + videos_list.append(os.path.join(kwargs['video'], f)) + filenames.append(f) + print(videos_list) + kwargs.pop('video') + loaded_videos = [] + for idx, video in enumerate(videos_list): + video_tensor = self.vhs_nodes.load_video_nodes.load_video(video=video, **kwargs)[0] + if add_label: + # Add filename label above video (without extension) + if video_tensor.dim() == 4: + _, h, w, c = video_tensor.shape + else: + h, w, c = video_tensor.shape + # Remove extension from filename + label_text = filenames[idx].rsplit('.', 1)[0] + font_size = max(16, w // 20) + try: + font = ImageFont.truetype("arial.ttf", font_size) + except: + font = ImageFont.load_default() + dummy_img = Image.new("RGB", (w, 10), (0,0,0)) + draw = ImageDraw.Draw(dummy_img) + text_bbox = draw.textbbox((0,0), label_text, font=font) + extra_padding = max(12, font_size // 2) # More padding under the font + label_height = text_bbox[3] - text_bbox[1] + extra_padding + label_img = Image.new("RGB", (w, label_height), (0,0,0)) + draw = ImageDraw.Draw(label_img) + draw.text((w//2 - (text_bbox[2]-text_bbox[0])//2, 4), label_text, font=font, fill=(255,255,255)) + label_np = np.asarray(label_img).astype(np.float32) / 255.0 + label_tensor = torch.from_numpy(label_np) + if c == 1: + label_tensor = label_tensor.mean(dim=2, keepdim=True) + elif c == 4: + alpha = torch.ones((label_height, w, 1), dtype=label_tensor.dtype) + label_tensor = torch.cat([label_tensor, alpha], dim=2) + if video_tensor.dim() == 4: + label_tensor = label_tensor.unsqueeze(0).expand(video_tensor.shape[0], -1, -1, -1) + video_tensor = torch.cat([label_tensor, video_tensor], dim=1) + else: + video_tensor = torch.cat([label_tensor, video_tensor], dim=0) + loaded_videos.append(video_tensor) + if output_type == "batch": + out_tensor = torch.cat(loaded_videos) + elif output_type == "grid": + rows = (len(loaded_videos) + grid_max_columns - 1) // grid_max_columns + # Pad the last row if needed + total_slots = rows * grid_max_columns + while len(loaded_videos) < total_slots: + loaded_videos.append(torch.zeros_like(loaded_videos[0])) + # Create grid by rows + row_tensors = [] + for row_idx in range(rows): + start_idx = row_idx * grid_max_columns + end_idx = start_idx + grid_max_columns + row_videos = loaded_videos[start_idx:end_idx] + # Pad all videos in this row to the same height + heights = [v.shape[1] for v in row_videos] + max_height = max(heights) + padded_row_videos = [] + for v in row_videos: + pad_height = max_height - v.shape[1] + if pad_height > 0: + # Pad (frames, H, W, C) or (H, W, C) + if v.dim() == 4: + pad = (0,0, 0,0, 0,pad_height, 0,0) # (C,W,H,F) + v = torch.nn.functional.pad(v, (0,0,0,0,0,pad_height,0,0)) + else: + v = torch.nn.functional.pad(v, (0,0,0,0,pad_height,0)) + padded_row_videos.append(v) + row_tensor = torch.cat(padded_row_videos, dim=2) # Concatenate horizontally + row_tensors.append(row_tensor) + out_tensor = torch.cat(row_tensors, dim=1) # Concatenate rows vertically + print(out_tensor.shape) + return out_tensor, + + @classmethod + def IS_CHANGED(s, video, **kwargs): + if s.vhs_nodes is not None: + return s.vhs_nodes.utils.hash_path(video) + return None \ No newline at end of file diff --git a/ComfyUI/custom_nodes/comfyui-kjnodes/nodes/intrinsic_lora_nodes.py b/ComfyUI/custom_nodes/comfyui-kjnodes/nodes/intrinsic_lora_nodes.py new file mode 100644 index 0000000000000000000000000000000000000000..c8f125363836cc7721b4b61d100702594522d389 --- /dev/null +++ b/ComfyUI/custom_nodes/comfyui-kjnodes/nodes/intrinsic_lora_nodes.py @@ -0,0 +1,115 @@ +import folder_paths +import os +import torch +import torch.nn.functional as F +from comfy.utils import ProgressBar, load_torch_file +import comfy.sample +from nodes import CLIPTextEncode + +script_directory = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +folder_paths.add_model_folder_path("intrinsic_loras", os.path.join(script_directory, "intrinsic_loras")) + +class Intrinsic_lora_sampling: + def __init__(self): + self.loaded_lora = None + + @classmethod + def INPUT_TYPES(s): + return {"required": { "model": ("MODEL",), + "lora_name": (folder_paths.get_filename_list("intrinsic_loras"), ), + "task": ( + [ + 'depth map', + 'surface normals', + 'albedo', + 'shading', + ], + { + "default": 'depth map' + }), + "text": ("STRING", {"multiline": True, "default": ""}), + "clip": ("CLIP", ), + "vae": ("VAE", ), + "per_batch": ("INT", {"default": 16, "min": 1, "max": 4096, "step": 1}), + }, + "optional": { + "image": ("IMAGE",), + "optional_latent": ("LATENT",), + }, + } + + RETURN_TYPES = ("IMAGE", "LATENT",) + FUNCTION = "onestepsample" + CATEGORY = "KJNodes" + DESCRIPTION = """ +Sampler to use the intrinsic loras: +https://github.com/duxiaodan/intrinsic-lora +These LoRAs are tiny and thus included +with this node pack. +""" + + def onestepsample(self, model, lora_name, clip, vae, text, task, per_batch, image=None, optional_latent=None): + pbar = ProgressBar(3) + + if optional_latent is None: + image_list = [] + for start_idx in range(0, image.shape[0], per_batch): + sub_pixels = vae.vae_encode_crop_pixels(image[start_idx:start_idx+per_batch]) + image_list.append(vae.encode(sub_pixels[:,:,:,:3])) + sample = torch.cat(image_list, dim=0) + else: + sample = optional_latent["samples"] + noise = torch.zeros(sample.size(), dtype=sample.dtype, layout=sample.layout, device="cpu") + prompt = task + "," + text + positive, = CLIPTextEncode.encode(self, clip, prompt) + negative = positive #negative shouldn't do anything in this scenario + + pbar.update(1) + + #custom model sampling to pass latent through as it is + class X0_PassThrough(comfy.model_sampling.EPS): + def calculate_denoised(self, sigma, model_output, model_input): + return model_output + def calculate_input(self, sigma, noise): + return noise + sampling_base = comfy.model_sampling.ModelSamplingDiscrete + sampling_type = X0_PassThrough + + class ModelSamplingAdvanced(sampling_base, sampling_type): + pass + model_sampling = ModelSamplingAdvanced(model.model.model_config) + + #load lora + model_clone = model.clone() + lora_path = folder_paths.get_full_path("intrinsic_loras", lora_name) + lora = load_torch_file(lora_path, safe_load=True) + self.loaded_lora = (lora_path, lora) + + model_clone_with_lora = comfy.sd.load_lora_for_models(model_clone, None, lora, 1.0, 0)[0] + + model_clone_with_lora.add_object_patch("model_sampling", model_sampling) + + samples = {"samples": comfy.sample.sample(model_clone_with_lora, noise, 1, 1.0, "euler", "simple", positive, negative, sample, + denoise=1.0, disable_noise=True, start_step=0, last_step=1, + force_full_denoise=True, noise_mask=None, callback=None, disable_pbar=True, seed=None)} + pbar.update(1) + + decoded = [] + for start_idx in range(0, samples["samples"].shape[0], per_batch): + decoded.append(vae.decode(samples["samples"][start_idx:start_idx+per_batch])) + image_out = torch.cat(decoded, dim=0) + + pbar.update(1) + + if task == 'depth map': + imax = image_out.max() + imin = image_out.min() + image_out = (image_out-imin)/(imax-imin) + image_out = torch.max(image_out, dim=3, keepdim=True)[0].repeat(1, 1, 1, 3) + elif task == 'surface normals': + image_out = F.normalize(image_out * 2 - 1, dim=3) / 2 + 0.5 + image_out = 1.0 - image_out + else: + image_out = image_out.clamp(-1.,1.) + + return (image_out, samples,) \ No newline at end of file diff --git a/ComfyUI/custom_nodes/comfyui-kjnodes/nodes/lora_nodes.py b/ComfyUI/custom_nodes/comfyui-kjnodes/nodes/lora_nodes.py new file mode 100644 index 0000000000000000000000000000000000000000..fe9959c01bcad0a27641b0cd3ba13b53cfa9434e --- /dev/null +++ b/ComfyUI/custom_nodes/comfyui-kjnodes/nodes/lora_nodes.py @@ -0,0 +1,190 @@ +import torch +import comfy.model_management +import comfy.utils +import folder_paths +import os +import logging +from tqdm import tqdm + +device = comfy.model_management.get_torch_device() + +CLAMP_QUANTILE = 0.99 + +def extract_lora(diff, key, rank, algorithm, lora_type, lowrank_iters=7, adaptive_param=1.0): + """ + Extracts LoRA weights from a weight difference tensor using SVD. + """ + conv2d = (len(diff.shape) == 4) + kernel_size = None if not conv2d else diff.size()[2:4] + conv2d_3x3 = conv2d and kernel_size != (1, 1) + out_dim, in_dim = diff.size()[0:2] + + if conv2d: + if conv2d_3x3: + diff = diff.flatten(start_dim=1) + else: + diff = diff.squeeze() + + diff_float = diff.float() + if algorithm == "svd_lowrank": + U, S, V = torch.svd_lowrank(diff_float, q=min(rank, in_dim, out_dim), niter=lowrank_iters) + U = U @ torch.diag(S) + Vh = V.t() + else: + #torch.linalg.svdvals() + U, S, Vh = torch.linalg.svd(diff_float) + # Flexible rank selection logic like locon: https://github.com/KohakuBlueleaf/LyCORIS/blob/main/tools/extract_locon.py + if "adaptive" in lora_type: + if lora_type == "adaptive_ratio": + min_s = torch.max(S) * adaptive_param + lora_rank = torch.sum(S > min_s).item() + elif lora_type == "adaptive_energy": + energy = torch.cumsum(S**2, dim=0) + total_energy = torch.sum(S**2) + threshold = adaptive_param * total_energy # e.g., adaptive_param=0.95 for 95% + lora_rank = torch.sum(energy < threshold).item() + 1 + elif lora_type == "adaptive_quantile": + s_cum = torch.cumsum(S, dim=0) + min_cum_sum = adaptive_param * torch.sum(S) + lora_rank = torch.sum(s_cum < min_cum_sum).item() + print(f"{key} Extracted LoRA rank: {lora_rank}") + else: + lora_rank = rank + + lora_rank = max(1, lora_rank) + lora_rank = min(out_dim, in_dim, lora_rank) + + U = U[:, :lora_rank] + S = S[:lora_rank] + U = U @ torch.diag(S) + Vh = Vh[:lora_rank, :] + + dist = torch.cat([U.flatten(), Vh.flatten()]) + if dist.numel() > 100_000: + # Sample 100,000 elements for quantile estimation + idx = torch.randperm(dist.numel(), device=dist.device)[:100_000] + dist_sample = dist[idx] + hi_val = torch.quantile(dist_sample, CLAMP_QUANTILE) + else: + hi_val = torch.quantile(dist, CLAMP_QUANTILE) + low_val = -hi_val + + U = U.clamp(low_val, hi_val) + Vh = Vh.clamp(low_val, hi_val) + if conv2d: + U = U.reshape(out_dim, lora_rank, 1, 1) + Vh = Vh.reshape(lora_rank, in_dim, kernel_size[0], kernel_size[1]) + return (U, Vh) + + +def calc_lora_model(model_diff, rank, prefix_model, prefix_lora, output_sd, lora_type, algorithm, lowrank_iters, out_dtype, bias_diff=False, adaptive_param=1.0): + comfy.model_management.load_models_gpu([model_diff], force_patch_weights=True) + model_diff.model.diffusion_model.cpu() + sd = model_diff.model_state_dict(filter_prefix=prefix_model) + del model_diff + comfy.model_management.soft_empty_cache() + for k, v in sd.items(): + if isinstance(v, torch.Tensor): + sd[k] = v.cpu() + + # Get total number of keys to process for progress bar + total_keys = len([k for k in sd if k.endswith(".weight") or (bias_diff and k.endswith(".bias"))]) + + # Create progress bar + progress_bar = tqdm(total=total_keys, desc=f"Extracting LoRA ({prefix_lora.strip('.')})") + comfy_pbar = comfy.utils.ProgressBar(total_keys) + + for k in sd: + if k.endswith(".weight"): + weight_diff = sd[k] + if weight_diff.ndim == 5: + logging.info(f"Skipping 5D tensor for key {k}") #skip patch embed + progress_bar.update(1) + comfy_pbar.update(1) + continue + if lora_type != "full": + if weight_diff.ndim < 2: + if bias_diff: + output_sd["{}{}.diff".format(prefix_lora, k[len(prefix_model):-7])] = weight_diff.contiguous().to(out_dtype).cpu() + progress_bar.update(1) + comfy_pbar.update(1) + continue + try: + out = extract_lora(weight_diff.to(device), k, rank, algorithm, lora_type, lowrank_iters=lowrank_iters, adaptive_param=adaptive_param) + output_sd["{}{}.lora_up.weight".format(prefix_lora, k[len(prefix_model):-7])] = out[0].contiguous().to(out_dtype).cpu() + output_sd["{}{}.lora_down.weight".format(prefix_lora, k[len(prefix_model):-7])] = out[1].contiguous().to(out_dtype).cpu() + except Exception as e: + logging.warning(f"Could not generate lora weights for key {k}, error {e}") + else: + output_sd["{}{}.diff".format(prefix_lora, k[len(prefix_model):-7])] = weight_diff.contiguous().to(out_dtype).cpu() + + progress_bar.update(1) + comfy_pbar.update(1) + + elif bias_diff and k.endswith(".bias"): + output_sd["{}{}.diff_b".format(prefix_lora, k[len(prefix_model):-5])] = sd[k].contiguous().to(out_dtype).cpu() + progress_bar.update(1) + comfy_pbar.update(1) + progress_bar.close() + return output_sd + +class LoraExtractKJ: + def __init__(self): + self.output_dir = folder_paths.get_output_directory() + + @classmethod + def INPUT_TYPES(s): + return {"required": + { + "finetuned_model": ("MODEL",), + "original_model": ("MODEL",), + "filename_prefix": ("STRING", {"default": "loras/ComfyUI_extracted_lora"}), + "rank": ("INT", {"default": 8, "min": 1, "max": 4096, "step": 1}), + "lora_type": (["standard", "full", "adaptive_ratio", "adaptive_quantile", "adaptive_energy"],), + "algorithm": (["svd_linalg", "svd_lowrank"], {"default": "svd_linalg", "tooltip": "SVD algorithm to use, svd_lowrank is faster but less accurate."}), + "lowrank_iters": ("INT", {"default": 7, "min": 1, "max": 100, "step": 1, "tooltip": "The number of subspace iterations for lowrank SVD algorithm."}), + "output_dtype": (["fp16", "bf16", "fp32"], {"default": "fp16"}), + "bias_diff": ("BOOLEAN", {"default": True}), + "adaptive_param": ("FLOAT", {"default": 0.15, "min": 0.0, "max": 1.0, "step": 0.01, "tooltip": "For ratio mode, this is the ratio of the maximum singular value. For quantile mode, this is the quantile of the singular values."}), + }, + + } + RETURN_TYPES = () + FUNCTION = "save" + OUTPUT_NODE = True + + CATEGORY = "KJNodes/lora" + + def save(self, finetuned_model, original_model, filename_prefix, rank, lora_type, algorithm, lowrank_iters, output_dtype, bias_diff, adaptive_param): + if algorithm == "svd_lowrank" and lora_type != "standard": + raise ValueError("svd_lowrank algorithm is only supported for standard LoRA extraction.") + + dtype = {"fp8_e4m3fn": torch.float8_e4m3fn, "bf16": torch.bfloat16, "fp16": torch.float16, "fp16_fast": torch.float16, "fp32": torch.float32}[output_dtype] + m = finetuned_model.clone() + kp = original_model.get_key_patches("diffusion_model.") + for k in kp: + m.add_patches({k: kp[k]}, - 1.0, 1.0) + model_diff = m + + full_output_folder, filename, counter, subfolder, filename_prefix = folder_paths.get_save_image_path(filename_prefix, self.output_dir) + + output_sd = {} + if model_diff is not None: + output_sd = calc_lora_model(model_diff, rank, "diffusion_model.", "diffusion_model.", output_sd, lora_type, algorithm, lowrank_iters, dtype, bias_diff=bias_diff, adaptive_param=adaptive_param) + if "adaptive" in lora_type: + rank_str = f"{lora_type}_{adaptive_param:.2f}" + else: + rank_str = rank + output_checkpoint = f"{filename}_rank_{rank_str}_{output_dtype}_{counter:05}_.safetensors" + output_checkpoint = os.path.join(full_output_folder, output_checkpoint) + + comfy.utils.save_torch_file(output_sd, output_checkpoint, metadata=None) + return {} + +NODE_CLASS_MAPPINGS = { + "LoraExtractKJ": LoraExtractKJ +} + +NODE_DISPLAY_NAME_MAPPINGS = { + "LoraExtractKJ": "LoraExtractKJ" +} diff --git a/ComfyUI/custom_nodes/comfyui-kjnodes/nodes/mask_nodes.py b/ComfyUI/custom_nodes/comfyui-kjnodes/nodes/mask_nodes.py new file mode 100644 index 0000000000000000000000000000000000000000..9a84a127dd618ebaabb2e9e522bf84594d9549e1 --- /dev/null +++ b/ComfyUI/custom_nodes/comfyui-kjnodes/nodes/mask_nodes.py @@ -0,0 +1,1427 @@ +import torch +import torch.nn.functional as F +from torchvision.transforms import functional as TF +from PIL import Image, ImageDraw, ImageFilter, ImageFont +import scipy.ndimage +import numpy as np +from contextlib import nullcontext +import os + +from comfy import model_management +from comfy.utils import ProgressBar +from comfy.utils import common_upscale +from nodes import MAX_RESOLUTION + +import folder_paths + +from ..utility.utility import tensor2pil, pil2tensor + +script_directory = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + +class BatchCLIPSeg: + + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(s): + + return {"required": + { + "images": ("IMAGE",), + "text": ("STRING", {"multiline": False}), + "threshold": ("FLOAT", {"default": 0.5,"min": 0.0, "max": 10.0, "step": 0.001}), + "binary_mask": ("BOOLEAN", {"default": True}), + "combine_mask": ("BOOLEAN", {"default": False}), + "use_cuda": ("BOOLEAN", {"default": True}), + }, + "optional": + { + "blur_sigma": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 100.0, "step": 0.1}), + "opt_model": ("CLIPSEGMODEL", ), + "prev_mask": ("MASK", {"default": None}), + "image_bg_level": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 1.0, "step": 0.01}), + "invert": ("BOOLEAN", {"default": False}), + } + } + + CATEGORY = "KJNodes/masking" + RETURN_TYPES = ("MASK", "IMAGE", ) + RETURN_NAMES = ("Mask", "Image", ) + FUNCTION = "segment_image" + DESCRIPTION = """ +Segments an image or batch of images using CLIPSeg. +""" + + def segment_image(self, images, text, threshold, binary_mask, combine_mask, use_cuda, blur_sigma=0.0, opt_model=None, prev_mask=None, invert= False, image_bg_level=0.5): + from transformers import CLIPSegProcessor, CLIPSegForImageSegmentation + import torchvision.transforms as transforms + offload_device = model_management.unet_offload_device() + device = model_management.get_torch_device() + if not use_cuda: + device = torch.device("cpu") + dtype = model_management.unet_dtype() + + if opt_model is None: + checkpoint_path = os.path.join(folder_paths.models_dir,'clip_seg', 'clipseg-rd64-refined-fp16') + if not hasattr(self, "model"): + try: + if not os.path.exists(checkpoint_path): + from huggingface_hub import snapshot_download + snapshot_download(repo_id="Kijai/clipseg-rd64-refined-fp16", local_dir=checkpoint_path, local_dir_use_symlinks=False) + self.model = CLIPSegForImageSegmentation.from_pretrained(checkpoint_path) + except: + checkpoint_path = "CIDAS/clipseg-rd64-refined" + self.model = CLIPSegForImageSegmentation.from_pretrained(checkpoint_path) + processor = CLIPSegProcessor.from_pretrained(checkpoint_path) + + else: + self.model = opt_model['model'] + processor = opt_model['processor'] + + self.model.to(dtype).to(device) + + B, H, W, C = images.shape + images = images.to(device) + + autocast_condition = (dtype != torch.float32) and not model_management.is_device_mps(device) + with torch.autocast(model_management.get_autocast_device(device), dtype=dtype) if autocast_condition else nullcontext(): + + PIL_images = [Image.fromarray(np.clip(255. * image.cpu().numpy().squeeze(), 0, 255).astype(np.uint8)) for image in images ] + prompt = [text] * len(images) + input_prc = processor(text=prompt, images=PIL_images, return_tensors="pt") + + for key in input_prc: + input_prc[key] = input_prc[key].to(device) + outputs = self.model(**input_prc) + + mask_tensor = torch.sigmoid(outputs.logits) + mask_tensor = (mask_tensor - mask_tensor.min()) / (mask_tensor.max() - mask_tensor.min()) + mask_tensor = torch.where(mask_tensor > (threshold), mask_tensor, torch.tensor(0, dtype=torch.float)) + print(mask_tensor.shape) + if len(mask_tensor.shape) == 2: + mask_tensor = mask_tensor.unsqueeze(0) + mask_tensor = F.interpolate(mask_tensor.unsqueeze(1), size=(H, W), mode='nearest') + mask_tensor = mask_tensor.squeeze(1) + + self.model.to(offload_device) + + if binary_mask: + mask_tensor = (mask_tensor > 0).float() + if blur_sigma > 0: + kernel_size = int(6 * int(blur_sigma) + 1) + blur = transforms.GaussianBlur(kernel_size=(kernel_size, kernel_size), sigma=(blur_sigma, blur_sigma)) + mask_tensor = blur(mask_tensor) + + if combine_mask: + mask_tensor = torch.max(mask_tensor, dim=0)[0] + mask_tensor = mask_tensor.unsqueeze(0).repeat(len(images),1,1) + + del outputs + model_management.soft_empty_cache() + + if prev_mask is not None: + if prev_mask.shape != mask_tensor.shape: + prev_mask = F.interpolate(prev_mask.unsqueeze(1), size=(H, W), mode='nearest') + mask_tensor = mask_tensor + prev_mask.to(device) + torch.clamp(mask_tensor, min=0.0, max=1.0) + + if invert: + mask_tensor = 1 - mask_tensor + + image_tensor = images * mask_tensor.unsqueeze(-1) + (1 - mask_tensor.unsqueeze(-1)) * image_bg_level + image_tensor = torch.clamp(image_tensor, min=0.0, max=1.0).cpu().float() + + mask_tensor = mask_tensor.cpu().float() + + return mask_tensor, image_tensor, + +class DownloadAndLoadCLIPSeg: + + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(s): + + return {"required": + { + "model": ( + [ 'Kijai/clipseg-rd64-refined-fp16', + 'CIDAS/clipseg-rd64-refined', + ], + ), + }, + } + + CATEGORY = "KJNodes/masking" + RETURN_TYPES = ("CLIPSEGMODEL",) + RETURN_NAMES = ("clipseg_model",) + FUNCTION = "segment_image" + DESCRIPTION = """ +Downloads and loads CLIPSeg model with huggingface_hub, +to ComfyUI/models/clip_seg +""" + + def segment_image(self, model): + from transformers import CLIPSegProcessor, CLIPSegForImageSegmentation + checkpoint_path = os.path.join(folder_paths.models_dir,'clip_seg', os.path.basename(model)) + if not hasattr(self, "model"): + if not os.path.exists(checkpoint_path): + from huggingface_hub import snapshot_download + snapshot_download(repo_id=model, local_dir=checkpoint_path, local_dir_use_symlinks=False) + self.model = CLIPSegForImageSegmentation.from_pretrained(checkpoint_path) + + processor = CLIPSegProcessor.from_pretrained(checkpoint_path) + + clipseg_model = {} + clipseg_model['model'] = self.model + clipseg_model['processor'] = processor + + return clipseg_model, + +class CreateTextMask: + + RETURN_TYPES = ("IMAGE", "MASK",) + FUNCTION = "createtextmask" + CATEGORY = "KJNodes/text" + DESCRIPTION = """ +Creates a text image and mask. +Looks for fonts from this folder: +ComfyUI/custom_nodes/ComfyUI-KJNodes/fonts + +If start_rotation and/or end_rotation are different values, +creates animation between them. +""" + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "invert": ("BOOLEAN", {"default": False}), + "frames": ("INT", {"default": 1,"min": 1, "max": 4096, "step": 1}), + "text_x": ("INT", {"default": 0,"min": 0, "max": 4096, "step": 1}), + "text_y": ("INT", {"default": 0,"min": 0, "max": 4096, "step": 1}), + "font_size": ("INT", {"default": 32,"min": 8, "max": 4096, "step": 1}), + "font_color": ("STRING", {"default": "white"}), + "text": ("STRING", {"default": "HELLO!", "multiline": True}), + "font": (folder_paths.get_filename_list("kjnodes_fonts"), ), + "width": ("INT", {"default": 512,"min": 16, "max": 4096, "step": 1}), + "height": ("INT", {"default": 512,"min": 16, "max": 4096, "step": 1}), + "start_rotation": ("INT", {"default": 0,"min": 0, "max": 359, "step": 1}), + "end_rotation": ("INT", {"default": 0,"min": -359, "max": 359, "step": 1}), + }, + } + + def createtextmask(self, frames, width, height, invert, text_x, text_y, text, font_size, font_color, font, start_rotation, end_rotation): + # Define the number of images in the batch + batch_size = frames + out = [] + masks = [] + rotation = start_rotation + if start_rotation != end_rotation: + rotation_increment = (end_rotation - start_rotation) / (batch_size - 1) + + font_path = folder_paths.get_full_path("kjnodes_fonts", font) + # Generate the text + for i in range(batch_size): + image = Image.new("RGB", (width, height), "black") + draw = ImageDraw.Draw(image) + font = ImageFont.truetype(font_path, font_size) + + # Split the text into words + words = text.split() + + # Initialize variables for line creation + lines = [] + current_line = [] + current_line_width = 0 + try: #new pillow + # Iterate through words to create lines + for word in words: + word_width = font.getbbox(word)[2] + if current_line_width + word_width <= width - 2 * text_x: + current_line.append(word) + current_line_width += word_width + font.getbbox(" ")[2] # Add space width + else: + lines.append(" ".join(current_line)) + current_line = [word] + current_line_width = word_width + except: #old pillow + for word in words: + word_width = font.getsize(word)[0] + if current_line_width + word_width <= width - 2 * text_x: + current_line.append(word) + current_line_width += word_width + font.getsize(" ")[0] # Add space width + else: + lines.append(" ".join(current_line)) + current_line = [word] + current_line_width = word_width + + # Add the last line if it's not empty + if current_line: + lines.append(" ".join(current_line)) + + # Draw each line of text separately + y_offset = text_y + for line in lines: + text_width = font.getlength(line) + text_height = font_size + text_center_x = text_x + text_width / 2 + text_center_y = y_offset + text_height / 2 + try: + draw.text((text_x, y_offset), line, font=font, fill=font_color, features=['-liga']) + except: + draw.text((text_x, y_offset), line, font=font, fill=font_color) + y_offset += text_height # Move to the next line + + if start_rotation != end_rotation: + image = image.rotate(rotation, center=(text_center_x, text_center_y)) + rotation += rotation_increment + + image = np.array(image).astype(np.float32) / 255.0 + image = torch.from_numpy(image)[None,] + mask = image[:, :, :, 0] + masks.append(mask) + out.append(image) + + if invert: + return (1.0 - torch.cat(out, dim=0), 1.0 - torch.cat(masks, dim=0),) + return (torch.cat(out, dim=0),torch.cat(masks, dim=0),) + +class ColorToMask: + + RETURN_TYPES = ("MASK",) + FUNCTION = "clip" + CATEGORY = "KJNodes/masking" + DESCRIPTION = """ +Converts chosen RGB value to a mask. +With batch inputs, the **per_batch** +controls the number of images processed at once. +""" + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "images": ("IMAGE",), + "invert": ("BOOLEAN", {"default": False}), + "red": ("INT", {"default": 0,"min": 0, "max": 255, "step": 1}), + "green": ("INT", {"default": 0,"min": 0, "max": 255, "step": 1}), + "blue": ("INT", {"default": 0,"min": 0, "max": 255, "step": 1}), + "threshold": ("INT", {"default": 10,"min": 0, "max": 255, "step": 1}), + "per_batch": ("INT", {"default": 16, "min": 1, "max": 4096, "step": 1}), + }, + } + + def clip(self, images, red, green, blue, threshold, invert, per_batch): + + color = torch.tensor([red, green, blue], dtype=torch.uint8) + black = torch.tensor([0, 0, 0], dtype=torch.uint8) + white = torch.tensor([255, 255, 255], dtype=torch.uint8) + + if invert: + black, white = white, black + + steps = images.shape[0] + pbar = ProgressBar(steps) + tensors_out = [] + + for start_idx in range(0, images.shape[0], per_batch): + + # Calculate color distances + color_distances = torch.norm(images[start_idx:start_idx+per_batch] * 255 - color, dim=-1) + + # Create a mask based on the threshold + mask = color_distances <= threshold + + # Apply the mask to create new images + mask_out = torch.where(mask.unsqueeze(-1), white, black).float() + mask_out = mask_out.mean(dim=-1) + + tensors_out.append(mask_out.cpu()) + batch_count = mask_out.shape[0] + pbar.update(batch_count) + + tensors_out = torch.cat(tensors_out, dim=0) + tensors_out = torch.clamp(tensors_out, min=0.0, max=1.0) + return tensors_out, + +class CreateFluidMask: + + RETURN_TYPES = ("IMAGE", "MASK") + FUNCTION = "createfluidmask" + CATEGORY = "KJNodes/masking/generate" + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "invert": ("BOOLEAN", {"default": False}), + "frames": ("INT", {"default": 1,"min": 1, "max": 4096, "step": 1}), + "width": ("INT", {"default": 256,"min": 16, "max": 4096, "step": 1}), + "height": ("INT", {"default": 256,"min": 16, "max": 4096, "step": 1}), + "inflow_count": ("INT", {"default": 3,"min": 0, "max": 255, "step": 1}), + "inflow_velocity": ("INT", {"default": 1,"min": 0, "max": 255, "step": 1}), + "inflow_radius": ("INT", {"default": 8,"min": 0, "max": 255, "step": 1}), + "inflow_padding": ("INT", {"default": 50,"min": 0, "max": 255, "step": 1}), + "inflow_duration": ("INT", {"default": 60,"min": 0, "max": 255, "step": 1}), + }, + } + #using code from https://github.com/GregTJ/stable-fluids + def createfluidmask(self, frames, width, height, invert, inflow_count, inflow_velocity, inflow_radius, inflow_padding, inflow_duration): + from ..utility.fluid import Fluid + try: + from scipy.special import erf + except: + from scipy.spatial import erf + out = [] + masks = [] + RESOLUTION = width, height + DURATION = frames + + INFLOW_PADDING = inflow_padding + INFLOW_DURATION = inflow_duration + INFLOW_RADIUS = inflow_radius + INFLOW_VELOCITY = inflow_velocity + INFLOW_COUNT = inflow_count + + print('Generating fluid solver, this may take some time.') + fluid = Fluid(RESOLUTION, 'dye') + + center = np.floor_divide(RESOLUTION, 2) + r = np.min(center) - INFLOW_PADDING + + points = np.linspace(-np.pi, np.pi, INFLOW_COUNT, endpoint=False) + points = tuple(np.array((np.cos(p), np.sin(p))) for p in points) + normals = tuple(-p for p in points) + points = tuple(r * p + center for p in points) + + inflow_velocity = np.zeros_like(fluid.velocity) + inflow_dye = np.zeros(fluid.shape) + for p, n in zip(points, normals): + mask = np.linalg.norm(fluid.indices - p[:, None, None], axis=0) <= INFLOW_RADIUS + inflow_velocity[:, mask] += n[:, None] * INFLOW_VELOCITY + inflow_dye[mask] = 1 + + + for f in range(DURATION): + print(f'Computing frame {f + 1} of {DURATION}.') + if f <= INFLOW_DURATION: + fluid.velocity += inflow_velocity + fluid.dye += inflow_dye + + curl = fluid.step()[1] + # Using the error function to make the contrast a bit higher. + # Any other sigmoid function e.g. smoothstep would work. + curl = (erf(curl * 2) + 1) / 4 + + color = np.dstack((curl, np.ones(fluid.shape), fluid.dye)) + color = (np.clip(color, 0, 1) * 255).astype('uint8') + image = np.array(color).astype(np.float32) / 255.0 + image = torch.from_numpy(image)[None,] + mask = image[:, :, :, 0] + masks.append(mask) + out.append(image) + + if invert: + return (1.0 - torch.cat(out, dim=0),1.0 - torch.cat(masks, dim=0),) + return (torch.cat(out, dim=0),torch.cat(masks, dim=0),) + +class CreateAudioMask: + + RETURN_TYPES = ("IMAGE",) + FUNCTION = "createaudiomask" + CATEGORY = "KJNodes/deprecated" + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "invert": ("BOOLEAN", {"default": False}), + "frames": ("INT", {"default": 16,"min": 1, "max": 255, "step": 1}), + "scale": ("FLOAT", {"default": 0.5,"min": 0.0, "max": 2.0, "step": 0.01}), + "audio_path": ("STRING", {"default": "audio.wav"}), + "width": ("INT", {"default": 256,"min": 16, "max": 4096, "step": 1}), + "height": ("INT", {"default": 256,"min": 16, "max": 4096, "step": 1}), + }, + } + + def createaudiomask(self, frames, width, height, invert, audio_path, scale): + try: + import librosa + except ImportError: + raise Exception("Can not import librosa. Install it with 'pip install librosa'") + batch_size = frames + out = [] + masks = [] + if audio_path == "audio.wav": #I don't know why relative path won't work otherwise... + audio_path = os.path.join(script_directory, audio_path) + audio, sr = librosa.load(audio_path) + spectrogram = np.abs(librosa.stft(audio)) + + for i in range(batch_size): + image = Image.new("RGB", (width, height), "black") + draw = ImageDraw.Draw(image) + frame = spectrogram[:, i] + circle_radius = int(height * np.mean(frame)) + circle_radius *= scale + circle_center = (width // 2, height // 2) # Calculate the center of the image + + draw.ellipse([(circle_center[0] - circle_radius, circle_center[1] - circle_radius), + (circle_center[0] + circle_radius, circle_center[1] + circle_radius)], + fill='white') + + image = np.array(image).astype(np.float32) / 255.0 + image = torch.from_numpy(image)[None,] + mask = image[:, :, :, 0] + masks.append(mask) + out.append(image) + + if invert: + return (1.0 - torch.cat(out, dim=0),) + return (torch.cat(out, dim=0),torch.cat(masks, dim=0),) + +class CreateGradientMask: + + RETURN_TYPES = ("MASK",) + FUNCTION = "createmask" + CATEGORY = "KJNodes/masking/generate" + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "invert": ("BOOLEAN", {"default": False}), + "frames": ("INT", {"default": 0,"min": 0, "max": 255, "step": 1}), + "width": ("INT", {"default": 256,"min": 16, "max": 4096, "step": 1}), + "height": ("INT", {"default": 256,"min": 16, "max": 4096, "step": 1}), + }, + } + def createmask(self, frames, width, height, invert): + # Define the number of images in the batch + batch_size = frames + out = [] + # Create an empty array to store the image batch + image_batch = np.zeros((batch_size, height, width), dtype=np.float32) + # Generate the black to white gradient for each image + for i in range(batch_size): + gradient = np.linspace(1.0, 0.0, width, dtype=np.float32) + time = i / frames # Calculate the time variable + offset_gradient = gradient - time # Offset the gradient values based on time + image_batch[i] = offset_gradient.reshape(1, -1) + output = torch.from_numpy(image_batch) + mask = output + out.append(mask) + if invert: + return (1.0 - torch.cat(out, dim=0),) + return (torch.cat(out, dim=0),) + +class CreateFadeMask: + + RETURN_TYPES = ("MASK",) + FUNCTION = "createfademask" + CATEGORY = "KJNodes/deprecated" + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "invert": ("BOOLEAN", {"default": False}), + "frames": ("INT", {"default": 2,"min": 2, "max": 10000, "step": 1}), + "width": ("INT", {"default": 256,"min": 16, "max": 4096, "step": 1}), + "height": ("INT", {"default": 256,"min": 16, "max": 4096, "step": 1}), + "interpolation": (["linear", "ease_in", "ease_out", "ease_in_out"],), + "start_level": ("FLOAT", {"default": 1.0,"min": 0.0, "max": 1.0, "step": 0.01}), + "midpoint_level": ("FLOAT", {"default": 0.5,"min": 0.0, "max": 1.0, "step": 0.01}), + "end_level": ("FLOAT", {"default": 0.0,"min": 0.0, "max": 1.0, "step": 0.01}), + "midpoint_frame": ("INT", {"default": 0,"min": 0, "max": 4096, "step": 1}), + }, + } + + def createfademask(self, frames, width, height, invert, interpolation, start_level, midpoint_level, end_level, midpoint_frame): + def ease_in(t): + return t * t + + def ease_out(t): + return 1 - (1 - t) * (1 - t) + + def ease_in_out(t): + return 3 * t * t - 2 * t * t * t + + batch_size = frames + out = [] + image_batch = np.zeros((batch_size, height, width), dtype=np.float32) + + if midpoint_frame == 0: + midpoint_frame = batch_size // 2 + + for i in range(batch_size): + if i <= midpoint_frame: + t = i / midpoint_frame + if interpolation == "ease_in": + t = ease_in(t) + elif interpolation == "ease_out": + t = ease_out(t) + elif interpolation == "ease_in_out": + t = ease_in_out(t) + color = start_level - t * (start_level - midpoint_level) + else: + t = (i - midpoint_frame) / (batch_size - midpoint_frame) + if interpolation == "ease_in": + t = ease_in(t) + elif interpolation == "ease_out": + t = ease_out(t) + elif interpolation == "ease_in_out": + t = ease_in_out(t) + color = midpoint_level - t * (midpoint_level - end_level) + + color = np.clip(color, 0, 255) + image = np.full((height, width), color, dtype=np.float32) + image_batch[i] = image + + output = torch.from_numpy(image_batch) + mask = output + out.append(mask) + + if invert: + return (1.0 - torch.cat(out, dim=0),) + return (torch.cat(out, dim=0),) + +class CreateFadeMaskAdvanced: + + RETURN_TYPES = ("MASK",) + FUNCTION = "createfademask" + CATEGORY = "KJNodes/masking/generate" + DESCRIPTION = """ +Create a batch of masks interpolated between given frames and values. +Uses same syntax as Fizz' BatchValueSchedule. +First value is the frame index (not that this starts from 0, not 1) +and the second value inside the brackets is the float value of the mask in range 0.0 - 1.0 + +For example the default values: +0:(0.0) +7:(1.0) +15:(0.0) + +Would create a mask batch fo 16 frames, starting from black, +interpolating with the chosen curve to fully white at the 8th frame, +and interpolating from that to fully black at the 16th frame. +""" + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "points_string": ("STRING", {"default": "0:(0.0),\n7:(1.0),\n15:(0.0)\n", "multiline": True}), + "invert": ("BOOLEAN", {"default": False}), + "frames": ("INT", {"default": 16,"min": 2, "max": 10000, "step": 1}), + "width": ("INT", {"default": 512,"min": 1, "max": 4096, "step": 1}), + "height": ("INT", {"default": 512,"min": 1, "max": 4096, "step": 1}), + "interpolation": (["linear", "ease_in", "ease_out", "ease_in_out", "none", "default_to_black"],), + }, + } + + def createfademask(self, frames, width, height, invert, points_string, interpolation): + def ease_in(t): + return t * t + + def ease_out(t): + return 1 - (1 - t) * (1 - t) + + def ease_in_out(t): + return 3 * t * t - 2 * t * t * t + + # Parse the input string into a list of tuples + points = [] + points_string = points_string.rstrip(',\n') + for point_str in points_string.split(','): + frame_str, color_str = point_str.split(':') + frame = int(frame_str.strip()) + color = float(color_str.strip()[1:-1]) # Remove parentheses around color + points.append((frame, color)) + + # Check if the last frame is already in the points + if (interpolation != "default_to_black") and (len(points) == 0 or points[-1][0] != frames - 1): + # If not, add it with the color of the last specified frame + points.append((frames - 1, points[-1][1] if points else 0)) + + # Sort the points by frame number + points.sort(key=lambda x: x[0]) + + batch_size = frames + out = [] + image_batch = np.zeros((batch_size, height, width), dtype=np.float32) + + # Index of the next point to interpolate towards + next_point = 1 + + for i in range(batch_size): + while next_point < len(points) and i > points[next_point][0]: + next_point += 1 + + # Interpolate between the previous point and the next point + prev_point = next_point - 1 + + if interpolation == "none": + exact_match = False + for p in points: + if p[0] == i: # Exact frame match + color = p[1] + exact_match = True + break + if not exact_match: + color = points[prev_point][1] + + elif interpolation == "default_to_black": + exact_match = False + for p in points: + if p[0] == i: # Exact frame match + color = p[1] + exact_match = True + break + if not exact_match: + color = 0 + else: + t = (i - points[prev_point][0]) / (points[next_point][0] - points[prev_point][0]) + if interpolation == "ease_in": + t = ease_in(t) + elif interpolation == "ease_out": + t = ease_out(t) + elif interpolation == "ease_in_out": + t = ease_in_out(t) + elif interpolation == "linear": + pass # No need to modify `t` for linear interpolation + + color = points[prev_point][1] - t * (points[prev_point][1] - points[next_point][1]) + + color = np.clip(color, 0, 255) + image = np.full((height, width), color, dtype=np.float32) + image_batch[i] = image + + output = torch.from_numpy(image_batch) + mask = output + out.append(mask) + + if invert: + return (1.0 - torch.cat(out, dim=0),) + return (torch.cat(out, dim=0),) + +class CreateMagicMask: + + RETURN_TYPES = ("MASK", "MASK",) + RETURN_NAMES = ("mask", "mask_inverted",) + FUNCTION = "createmagicmask" + CATEGORY = "KJNodes/masking/generate" + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "frames": ("INT", {"default": 16,"min": 2, "max": 4096, "step": 1}), + "depth": ("INT", {"default": 12,"min": 1, "max": 500, "step": 1}), + "distortion": ("FLOAT", {"default": 1.5,"min": 0.0, "max": 100.0, "step": 0.01}), + "seed": ("INT", {"default": 123,"min": 0, "max": 99999999, "step": 1}), + "transitions": ("INT", {"default": 1,"min": 1, "max": 20, "step": 1}), + "frame_width": ("INT", {"default": 512,"min": 16, "max": 4096, "step": 1}), + "frame_height": ("INT", {"default": 512,"min": 16, "max": 4096, "step": 1}), + }, + } + + def createmagicmask(self, frames, transitions, depth, distortion, seed, frame_width, frame_height): + from ..utility.magictex import coordinate_grid, random_transform, magic + import matplotlib.pyplot as plt + rng = np.random.default_rng(seed) + out = [] + coords = coordinate_grid((frame_width, frame_height)) + + # Calculate the number of frames for each transition + frames_per_transition = frames // transitions + + # Generate a base set of parameters + base_params = { + "coords": random_transform(coords, rng), + "depth": depth, + "distortion": distortion, + } + for t in range(transitions): + # Generate a second set of parameters that is at most max_diff away from the base parameters + params1 = base_params.copy() + params2 = base_params.copy() + + params1['coords'] = random_transform(coords, rng) + params2['coords'] = random_transform(coords, rng) + + for i in range(frames_per_transition): + # Compute the interpolation factor + alpha = i / frames_per_transition + + # Interpolate between the two sets of parameters + params = params1.copy() + params['coords'] = (1 - alpha) * params1['coords'] + alpha * params2['coords'] + + tex = magic(**params) + + dpi = frame_width / 10 + fig = plt.figure(figsize=(10, 10), dpi=dpi) + + ax = fig.add_subplot(111) + plt.subplots_adjust(left=0, right=1, bottom=0, top=1) + + ax.get_yaxis().set_ticks([]) + ax.get_xaxis().set_ticks([]) + ax.imshow(tex, aspect='auto') + + fig.canvas.draw() + img = np.array(fig.canvas.renderer._renderer) + + plt.close(fig) + + pil_img = Image.fromarray(img).convert("L") + mask = torch.tensor(np.array(pil_img)) / 255.0 + + out.append(mask) + + return (torch.stack(out, dim=0), 1.0 - torch.stack(out, dim=0),) + +class CreateShapeMask: + + RETURN_TYPES = ("MASK", "MASK",) + RETURN_NAMES = ("mask", "mask_inverted",) + FUNCTION = "createshapemask" + CATEGORY = "KJNodes/masking/generate" + DESCRIPTION = """ +Creates a mask or batch of masks with the specified shape. +Locations are center locations. +Grow value is the amount to grow the shape on each frame, creating animated masks. +""" + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "shape": ( + [ 'circle', + 'square', + 'triangle', + ], + { + "default": 'circle' + }), + "frames": ("INT", {"default": 1,"min": 1, "max": 4096, "step": 1}), + "location_x": ("INT", {"default": 256,"min": 0, "max": 4096, "step": 1}), + "location_y": ("INT", {"default": 256,"min": 0, "max": 4096, "step": 1}), + "grow": ("INT", {"default": 0, "min": -512, "max": 512, "step": 1}), + "frame_width": ("INT", {"default": 512,"min": 16, "max": 4096, "step": 1}), + "frame_height": ("INT", {"default": 512,"min": 16, "max": 4096, "step": 1}), + "shape_width": ("INT", {"default": 128,"min": 8, "max": 4096, "step": 1}), + "shape_height": ("INT", {"default": 128,"min": 8, "max": 4096, "step": 1}), + }, + } + + def createshapemask(self, frames, frame_width, frame_height, location_x, location_y, shape_width, shape_height, grow, shape): + # Define the number of images in the batch + batch_size = frames + out = [] + color = "white" + for i in range(batch_size): + image = Image.new("RGB", (frame_width, frame_height), "black") + draw = ImageDraw.Draw(image) + + # Calculate the size for this frame and ensure it's not less than 0 + current_width = max(0, shape_width + i*grow) + current_height = max(0, shape_height + i*grow) + + if shape == 'circle' or shape == 'square': + # Define the bounding box for the shape + left_up_point = (location_x - current_width // 2, location_y - current_height // 2) + right_down_point = (location_x + current_width // 2, location_y + current_height // 2) + two_points = [left_up_point, right_down_point] + + if shape == 'circle': + draw.ellipse(two_points, fill=color) + elif shape == 'square': + draw.rectangle(two_points, fill=color) + + elif shape == 'triangle': + # Define the points for the triangle + left_up_point = (location_x - current_width // 2, location_y + current_height // 2) # bottom left + right_down_point = (location_x + current_width // 2, location_y + current_height // 2) # bottom right + top_point = (location_x, location_y - current_height // 2) # top point + draw.polygon([top_point, left_up_point, right_down_point], fill=color) + + image = pil2tensor(image) + mask = image[:, :, :, 0] + out.append(mask) + outstack = torch.cat(out, dim=0) + return (outstack, 1.0 - outstack,) + +class CreateVoronoiMask: + + RETURN_TYPES = ("MASK", "MASK",) + RETURN_NAMES = ("mask", "mask_inverted",) + FUNCTION = "createvoronoi" + CATEGORY = "KJNodes/masking/generate" + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "frames": ("INT", {"default": 16,"min": 2, "max": 4096, "step": 1}), + "num_points": ("INT", {"default": 15,"min": 1, "max": 4096, "step": 1}), + "line_width": ("INT", {"default": 4,"min": 1, "max": 4096, "step": 1}), + "speed": ("FLOAT", {"default": 0.5,"min": 0.0, "max": 1.0, "step": 0.01}), + "frame_width": ("INT", {"default": 512,"min": 16, "max": 4096, "step": 1}), + "frame_height": ("INT", {"default": 512,"min": 16, "max": 4096, "step": 1}), + }, + } + + def createvoronoi(self, frames, num_points, line_width, speed, frame_width, frame_height): + from scipy.spatial import Voronoi + # Define the number of images in the batch + batch_size = frames + out = [] + + # Calculate aspect ratio + aspect_ratio = frame_width / frame_height + + # Create start and end points for each point, considering the aspect ratio + start_points = np.random.rand(num_points, 2) + start_points[:, 0] *= aspect_ratio + + end_points = np.random.rand(num_points, 2) + end_points[:, 0] *= aspect_ratio + + for i in range(batch_size): + # Interpolate the points' positions based on the current frame + t = (i * speed) / (batch_size - 1) # normalize to [0, 1] over the frames + t = np.clip(t, 0, 1) # ensure t is in [0, 1] + points = (1 - t) * start_points + t * end_points # lerp + + # Adjust points for aspect ratio + points[:, 0] *= aspect_ratio + + vor = Voronoi(points) + + # Create a blank image with a white background + fig, ax = plt.subplots() + plt.subplots_adjust(left=0, right=1, bottom=0, top=1) + ax.set_xlim([0, aspect_ratio]); ax.set_ylim([0, 1]) # adjust x limits + ax.axis('off') + ax.margins(0, 0) + fig.set_size_inches(aspect_ratio * frame_height/100, frame_height/100) # adjust figure size + ax.fill_between([0, 1], [0, 1], color='white') + + # Plot each Voronoi ridge + for simplex in vor.ridge_vertices: + simplex = np.asarray(simplex) + if np.all(simplex >= 0): + plt.plot(vor.vertices[simplex, 0], vor.vertices[simplex, 1], 'k-', linewidth=line_width) + + fig.canvas.draw() + img = np.array(fig.canvas.renderer._renderer) + + plt.close(fig) + + pil_img = Image.fromarray(img).convert("L") + mask = torch.tensor(np.array(pil_img)) / 255.0 + + out.append(mask) + + return (torch.stack(out, dim=0), 1.0 - torch.stack(out, dim=0),) + +class GetMaskSizeAndCount: + @classmethod + def INPUT_TYPES(s): + return {"required": { + "mask": ("MASK",), + }} + + RETURN_TYPES = ("MASK","INT", "INT", "INT",) + RETURN_NAMES = ("mask", "width", "height", "count",) + FUNCTION = "getsize" + CATEGORY = "KJNodes/masking" + DESCRIPTION = """ +Returns the width, height and batch size of the mask, +and passes it through unchanged. + +""" + + def getsize(self, mask): + width = mask.shape[2] + height = mask.shape[1] + count = mask.shape[0] + return {"ui": { + "text": [f"{count}x{width}x{height}"]}, + "result": (mask, width, height, count) + } + +class GrowMaskWithBlur: + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "mask": ("MASK",), + "expand": ("INT", {"default": 0, "min": -MAX_RESOLUTION, "max": MAX_RESOLUTION, "step": 1}), + "incremental_expandrate": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 100.0, "step": 0.1}), + "tapered_corners": ("BOOLEAN", {"default": True}), + "flip_input": ("BOOLEAN", {"default": False}), + "blur_radius": ("FLOAT", { + "default": 0.0, + "min": 0.0, + "max": 100, + "step": 0.1 + }), + "lerp_alpha": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}), + "decay_factor": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}), + }, + "optional": { + "fill_holes": ("BOOLEAN", {"default": False}), + }, + } + + CATEGORY = "KJNodes/masking" + RETURN_TYPES = ("MASK", "MASK",) + RETURN_NAMES = ("mask", "mask_inverted",) + FUNCTION = "expand_mask" + DESCRIPTION = """ +# GrowMaskWithBlur +- mask: Input mask or mask batch +- expand: Expand or contract mask or mask batch by a given amount +- incremental_expandrate: increase expand rate by a given amount per frame +- tapered_corners: use tapered corners +- flip_input: flip input mask +- blur_radius: value higher than 0 will blur the mask +- lerp_alpha: alpha value for interpolation between frames +- decay_factor: decay value for interpolation between frames +- fill_holes: fill holes in the mask (slow)""" + + def expand_mask(self, mask, expand, tapered_corners, flip_input, blur_radius, incremental_expandrate, lerp_alpha, decay_factor, fill_holes=False): + alpha = lerp_alpha + decay = decay_factor + if flip_input: + mask = 1.0 - mask + c = 0 if tapered_corners else 1 + kernel = np.array([[c, 1, c], + [1, 1, 1], + [c, 1, c]]) + growmask = mask.reshape((-1, mask.shape[-2], mask.shape[-1])).cpu() + out = [] + previous_output = None + current_expand = expand + for m in growmask: + output = m.numpy().astype(np.float32) + for _ in range(abs(round(current_expand))): + if current_expand < 0: + output = scipy.ndimage.grey_erosion(output, footprint=kernel) + else: + output = scipy.ndimage.grey_dilation(output, footprint=kernel) + if current_expand < 0: + current_expand -= abs(incremental_expandrate) + else: + current_expand += abs(incremental_expandrate) + if fill_holes: + binary_mask = output > 0 + output = scipy.ndimage.binary_fill_holes(binary_mask) + output = output.astype(np.float32) * 255 + output = torch.from_numpy(output) + if alpha < 1.0 and previous_output is not None: + # Interpolate between the previous and current frame + output = alpha * output + (1 - alpha) * previous_output + if decay < 1.0 and previous_output is not None: + # Add the decayed previous output to the current frame + output += decay * previous_output + output = output / output.max() + previous_output = output + out.append(output) + + if blur_radius != 0: + # Convert the tensor list to PIL images, apply blur, and convert back + for idx, tensor in enumerate(out): + # Convert tensor to PIL image + pil_image = tensor2pil(tensor.cpu().detach())[0] + # Apply Gaussian blur + pil_image = pil_image.filter(ImageFilter.GaussianBlur(blur_radius)) + # Convert back to tensor + out[idx] = pil2tensor(pil_image) + blurred = torch.cat(out, dim=0) + return (blurred, 1.0 - blurred) + else: + return (torch.stack(out, dim=0), 1.0 - torch.stack(out, dim=0),) + +class MaskBatchMulti: + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "inputcount": ("INT", {"default": 2, "min": 2, "max": 1000, "step": 1}), + "mask_1": ("MASK", ), + "mask_2": ("MASK", ), + }, + } + + RETURN_TYPES = ("MASK",) + RETURN_NAMES = ("masks",) + FUNCTION = "combine" + CATEGORY = "KJNodes/masking" + DESCRIPTION = """ +Creates an image batch from multiple masks. +You can set how many inputs the node has, +with the **inputcount** and clicking update. +""" + + def combine(self, inputcount, **kwargs): + mask = kwargs["mask_1"] + for c in range(1, inputcount): + new_mask = kwargs[f"mask_{c + 1}"] + if mask.shape[1:] != new_mask.shape[1:]: + new_mask = F.interpolate(new_mask.unsqueeze(1), size=(mask.shape[1], mask.shape[2]), mode="bicubic").squeeze(1) + mask = torch.cat((mask, new_mask), dim=0) + return (mask,) + +class OffsetMask: + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "mask": ("MASK",), + "x": ("INT", { "default": 0, "min": -4096, "max": MAX_RESOLUTION, "step": 1, "display": "number" }), + "y": ("INT", { "default": 0, "min": -4096, "max": MAX_RESOLUTION, "step": 1, "display": "number" }), + "angle": ("INT", { "default": 0, "min": -360, "max": 360, "step": 1, "display": "number" }), + "duplication_factor": ("INT", { "default": 1, "min": 1, "max": 1000, "step": 1, "display": "number" }), + "roll": ("BOOLEAN", { "default": False }), + "incremental": ("BOOLEAN", { "default": False }), + "padding_mode": ( + [ + 'empty', + 'border', + 'reflection', + + ], { + "default": 'empty' + }), + } + } + + RETURN_TYPES = ("MASK",) + RETURN_NAMES = ("mask",) + FUNCTION = "offset" + CATEGORY = "KJNodes/masking" + DESCRIPTION = """ +Offsets the mask by the specified amount. + - mask: Input mask or mask batch + - x: Horizontal offset + - y: Vertical offset + - angle: Angle in degrees + - roll: roll edge wrapping + - duplication_factor: Number of times to duplicate the mask to form a batch + - border padding_mode: Padding mode for the mask +""" + + def offset(self, mask, x, y, angle, roll=False, incremental=False, duplication_factor=1, padding_mode="empty"): + # Create duplicates of the mask batch + mask = mask.repeat(duplication_factor, 1, 1).clone() + + batch_size, height, width = mask.shape + + if angle != 0 and incremental: + for i in range(batch_size): + rotation_angle = angle * (i+1) + mask[i] = TF.rotate(mask[i].unsqueeze(0), rotation_angle).squeeze(0) + elif angle > 0: + for i in range(batch_size): + mask[i] = TF.rotate(mask[i].unsqueeze(0), angle).squeeze(0) + + if roll: + if incremental: + for i in range(batch_size): + shift_x = min(x*(i+1), width-1) + shift_y = min(y*(i+1), height-1) + if shift_x != 0: + mask[i] = torch.roll(mask[i], shifts=shift_x, dims=1) + if shift_y != 0: + mask[i] = torch.roll(mask[i], shifts=shift_y, dims=0) + else: + shift_x = min(x, width-1) + shift_y = min(y, height-1) + if shift_x != 0: + mask = torch.roll(mask, shifts=shift_x, dims=2) + if shift_y != 0: + mask = torch.roll(mask, shifts=shift_y, dims=1) + else: + + for i in range(batch_size): + if incremental: + temp_x = min(x * (i+1), width-1) + temp_y = min(y * (i+1), height-1) + else: + temp_x = min(x, width-1) + temp_y = min(y, height-1) + if temp_x > 0: + if padding_mode == 'empty': + mask[i] = torch.cat([torch.zeros((height, temp_x)), mask[i, :, :-temp_x]], dim=1) + elif padding_mode in ['replicate', 'reflect']: + mask[i] = F.pad(mask[i, :, :-temp_x], (0, temp_x), mode=padding_mode) + elif temp_x < 0: + if padding_mode == 'empty': + mask[i] = torch.cat([mask[i, :, :temp_x], torch.zeros((height, -temp_x))], dim=1) + elif padding_mode in ['replicate', 'reflect']: + mask[i] = F.pad(mask[i, :, -temp_x:], (temp_x, 0), mode=padding_mode) + + if temp_y > 0: + if padding_mode == 'empty': + mask[i] = torch.cat([torch.zeros((temp_y, width)), mask[i, :-temp_y, :]], dim=0) + elif padding_mode in ['replicate', 'reflect']: + mask[i] = F.pad(mask[i, :-temp_y, :], (0, temp_y), mode=padding_mode) + elif temp_y < 0: + if padding_mode == 'empty': + mask[i] = torch.cat([mask[i, :temp_y, :], torch.zeros((-temp_y, width))], dim=0) + elif padding_mode in ['replicate', 'reflect']: + mask[i] = F.pad(mask[i, -temp_y:, :], (temp_y, 0), mode=padding_mode) + + return mask, + +class RoundMask: + @classmethod + def INPUT_TYPES(s): + return {"required": { + "mask": ("MASK",), + }} + + RETURN_TYPES = ("MASK",) + FUNCTION = "round" + CATEGORY = "KJNodes/masking" + DESCRIPTION = """ +Rounds the mask or batch of masks to a binary mask. +RoundMask example + +""" + + def round(self, mask): + mask = mask.round() + return (mask,) + +class ResizeMask: + upscale_methods = ["nearest-exact", "bilinear", "area", "bicubic", "lanczos"] + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "mask": ("MASK",), + "width": ("INT", { "default": 512, "min": 0, "max": MAX_RESOLUTION, "step": 1, "display": "number" }), + "height": ("INT", { "default": 512, "min": 0, "max": MAX_RESOLUTION, "step": 1, "display": "number" }), + "keep_proportions": ("BOOLEAN", { "default": False }), + "upscale_method": (s.upscale_methods,), + "crop": (["disabled","center"],), + } + } + + RETURN_TYPES = ("MASK", "INT", "INT",) + RETURN_NAMES = ("mask", "width", "height",) + FUNCTION = "resize" + CATEGORY = "KJNodes/masking" + DESCRIPTION = """ +Resizes the mask or batch of masks to the specified width and height. +""" + + def resize(self, mask, width, height, keep_proportions, upscale_method,crop): + if keep_proportions: + _, oh, ow = mask.shape + width = ow if width == 0 else width + height = oh if height == 0 else height + ratio = min(width / ow, height / oh) + width = round(ow*ratio) + height = round(oh*ratio) + + if upscale_method == "lanczos": + out_mask = common_upscale(mask.unsqueeze(1).repeat(1, 3, 1, 1), width, height, upscale_method, crop=crop).movedim(1,-1)[:, :, :, 0] + else: + out_mask = common_upscale(mask.unsqueeze(1), width, height, upscale_method, crop=crop).squeeze(1) + + return(out_mask, out_mask.shape[2], out_mask.shape[1],) + +class RemapMaskRange: + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "mask": ("MASK",), + "min": ("FLOAT", {"default": 0.0,"min": -10.0, "max": 1.0, "step": 0.01}), + "max": ("FLOAT", {"default": 1.0,"min": 0.0, "max": 10.0, "step": 0.01}), + } + } + + RETURN_TYPES = ("MASK",) + RETURN_NAMES = ("mask",) + FUNCTION = "remap" + CATEGORY = "KJNodes/masking" + DESCRIPTION = """ +Sets new min and max values for the mask. +""" + + def remap(self, mask, min, max): + + # Find the maximum value in the mask + mask_max = torch.max(mask) + + # If the maximum mask value is zero, avoid division by zero by setting it to 1 + mask_max = mask_max if mask_max > 0 else 1 + + # Scale the mask values to the new range defined by min and max + # The highest pixel value in the mask will be scaled to max + scaled_mask = (mask / mask_max) * (max - min) + min + + # Clamp the values to ensure they are within [0.0, 1.0] + scaled_mask = torch.clamp(scaled_mask, min=0.0, max=1.0) + + return (scaled_mask, ) + + +def get_mask_polygon(self, mask_np): + import cv2 + """Helper function to get polygon points from mask""" + # Find contours + contours, _ = cv2.findContours(mask_np, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) + + if not contours: + return None + + # Get the largest contour + largest_contour = max(contours, key=cv2.contourArea) + + # Approximate polygon + epsilon = 0.02 * cv2.arcLength(largest_contour, True) + polygon = cv2.approxPolyDP(largest_contour, epsilon, True) + + return polygon.squeeze() + +import cv2 +class SeparateMasks: + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "mask": ("MASK", ), + "size_threshold_width" : ("INT", {"default": 256, "min": 0.0, "max": 4096, "step": 1}), + "size_threshold_height" : ("INT", {"default": 256, "min": 0.0, "max": 4096, "step": 1}), + "mode": (["convex_polygons", "area", "box"],), + "max_poly_points": ("INT", {"default": 8, "min": 3, "max": 32, "step": 1}), + + }, + } + + RETURN_TYPES = ("MASK",) + RETURN_NAMES = ("mask",) + FUNCTION = "separate" + CATEGORY = "KJNodes/masking" + OUTPUT_NODE = True + DESCRIPTION = "Separates a mask into multiple masks based on the size of the connected components." + + def polygon_to_mask(self, polygon, shape): + mask = np.zeros((shape[0], shape[1]), dtype=np.uint8) # Fixed shape handling + + if len(polygon.shape) == 2: # Check if polygon points are valid + polygon = polygon.astype(np.int32) + cv2.fillPoly(mask, [polygon], 1) + return mask + + def get_mask_polygon(self, mask_np, max_points): + contours, _ = cv2.findContours(mask_np, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) + if not contours: + return None + + largest_contour = max(contours, key=cv2.contourArea) + hull = cv2.convexHull(largest_contour) + + # Initialize with smaller epsilon for more points + perimeter = cv2.arcLength(hull, True) + epsilon = perimeter * 0.01 # Start smaller + + min_eps = perimeter * 0.001 # Much smaller minimum + max_eps = perimeter * 0.2 # Smaller maximum + + best_approx = None + best_diff = float('inf') + max_iterations = 20 + + #print(f"Target points: {max_points}, Perimeter: {perimeter}") + + for i in range(max_iterations): + curr_eps = (min_eps + max_eps) / 2 + approx = cv2.approxPolyDP(hull, curr_eps, True) + points_diff = len(approx) - max_points + + #print(f"Iteration {i}: points={len(approx)}, eps={curr_eps:.4f}") + + if abs(points_diff) < best_diff: + best_approx = approx + best_diff = abs(points_diff) + + if len(approx) > max_points: + min_eps = curr_eps * 1.1 # More gradual adjustment + elif len(approx) < max_points: + max_eps = curr_eps * 0.9 # More gradual adjustment + else: + return approx.squeeze() + + if abs(max_eps - min_eps) < perimeter * 0.0001: # Relative tolerance + break + + # If we didn't find exact match, return best approximation + return best_approx.squeeze() if best_approx is not None else hull.squeeze() + + def separate(self, mask: torch.Tensor, size_threshold_width: int, size_threshold_height: int, max_poly_points: int, mode: str): + from scipy.ndimage import label, center_of_mass + import numpy as np + + B, H, W = mask.shape + separated = [] + + mask = mask.round() + + for b in range(B): + mask_np = mask[b].cpu().numpy().astype(np.uint8) + structure = np.ones((3, 3), dtype=np.int8) + labeled, ncomponents = label(mask_np, structure=structure) + pbar = ProgressBar(ncomponents) + + for component in range(1, ncomponents + 1): + component_mask_np = (labeled == component).astype(np.uint8) + + rows = np.any(component_mask_np, axis=1) + cols = np.any(component_mask_np, axis=0) + y_min, y_max = np.where(rows)[0][[0, -1]] + x_min, x_max = np.where(cols)[0][[0, -1]] + + width = x_max - x_min + 1 + height = y_max - y_min + 1 + centroid_x = (x_min + x_max) / 2 # Calculate x centroid + print(f"Component {component}: width={width}, height={height}, x_pos={centroid_x}") + + if width >= size_threshold_width and height >= size_threshold_height: + if mode == "convex_polygons": + polygon = self.get_mask_polygon(component_mask_np, max_poly_points) + if polygon is not None: + poly_mask = self.polygon_to_mask(polygon, (H, W)) + poly_mask = torch.tensor(poly_mask, device=mask.device) + separated.append((centroid_x, poly_mask)) + elif mode == "box": + # Create bounding box mask + box_mask = np.zeros((H, W), dtype=np.uint8) + box_mask[y_min:y_max+1, x_min:x_max+1] = 1 + box_mask = torch.tensor(box_mask, device=mask.device) + separated.append((centroid_x, box_mask)) + else: + area_mask = torch.tensor(component_mask_np, device=mask.device) + separated.append((centroid_x, area_mask)) + pbar.update(1) + + if len(separated) > 0: + # Sort by x position and extract only the masks + separated.sort(key=lambda x: x[0]) + separated = [x[1] for x in separated] + out_masks = torch.stack(separated, dim=0) + return out_masks, + else: + return torch.empty((1, 64, 64), device=mask.device), + \ No newline at end of file diff --git a/ComfyUI/custom_nodes/comfyui-kjnodes/nodes/model_optimization_nodes.py b/ComfyUI/custom_nodes/comfyui-kjnodes/nodes/model_optimization_nodes.py new file mode 100644 index 0000000000000000000000000000000000000000..c1f2e88d276d177a6db75dc53cff14e5b63b4542 --- /dev/null +++ b/ComfyUI/custom_nodes/comfyui-kjnodes/nodes/model_optimization_nodes.py @@ -0,0 +1,1885 @@ +from comfy.ldm.modules import attention as comfy_attention +import logging +import comfy.model_patcher +import comfy.utils +import comfy.sd +import torch +import folder_paths +import comfy.model_management as mm +from comfy.cli_args import args +from typing import Optional, Tuple + + +sageattn_modes = ["disabled", "auto", "sageattn_qk_int8_pv_fp16_cuda", "sageattn_qk_int8_pv_fp16_triton", "sageattn_qk_int8_pv_fp8_cuda", "sageattn_qk_int8_pv_fp8_cuda++"] + +_initialized = False +_original_functions = {} + +if not _initialized: + _original_functions["orig_attention"] = comfy_attention.optimized_attention + _original_functions["original_patch_model"] = comfy.model_patcher.ModelPatcher.patch_model + _original_functions["original_load_lora_for_models"] = comfy.sd.load_lora_for_models + try: + _original_functions["original_qwen_forward"] = comfy.ldm.qwen_image.model.Attention.forward + except: + pass + _initialized = True + +class BaseLoaderKJ: + original_linear = None + cublas_patched = False + + @torch.compiler.disable() + def _patch_modules(self, patch_cublaslinear, sage_attention): + try: + from comfy.ldm.qwen_image.model import apply_rotary_emb + def qwen_sage_forward( + self, + hidden_states: torch.FloatTensor, # Image stream + encoder_hidden_states: torch.FloatTensor = None, # Text stream + encoder_hidden_states_mask: torch.FloatTensor = None, + attention_mask: Optional[torch.FloatTensor] = None, + image_rotary_emb: Optional[torch.Tensor] = None, + ) -> Tuple[torch.Tensor, torch.Tensor]: + seq_txt = encoder_hidden_states.shape[1] + + img_query = self.to_q(hidden_states).unflatten(-1, (self.heads, -1)) + img_key = self.to_k(hidden_states).unflatten(-1, (self.heads, -1)) + img_value = self.to_v(hidden_states).unflatten(-1, (self.heads, -1)) + + txt_query = self.add_q_proj(encoder_hidden_states).unflatten(-1, (self.heads, -1)) + txt_key = self.add_k_proj(encoder_hidden_states).unflatten(-1, (self.heads, -1)) + txt_value = self.add_v_proj(encoder_hidden_states).unflatten(-1, (self.heads, -1)) + + img_query = self.norm_q(img_query) + img_key = self.norm_k(img_key) + txt_query = self.norm_added_q(txt_query) + txt_key = self.norm_added_k(txt_key) + + joint_query = torch.cat([txt_query, img_query], dim=1) + joint_key = torch.cat([txt_key, img_key], dim=1) + joint_value = torch.cat([txt_value, img_value], dim=1) + + joint_query = apply_rotary_emb(joint_query, image_rotary_emb) + joint_key = apply_rotary_emb(joint_key, image_rotary_emb) + + joint_query = joint_query.flatten(start_dim=2) + joint_key = joint_key.flatten(start_dim=2) + joint_value = joint_value.flatten(start_dim=2) + + joint_hidden_states = attention_sage(joint_query, joint_key, joint_value, self.heads, attention_mask) + + txt_attn_output = joint_hidden_states[:, :seq_txt, :] + img_attn_output = joint_hidden_states[:, seq_txt:, :] + + img_attn_output = self.to_out[0](img_attn_output) + img_attn_output = self.to_out[1](img_attn_output) + txt_attn_output = self.to_add_out(txt_attn_output) + + return img_attn_output, txt_attn_output + except: + print("Failed to patch QwenImage attention, Comfy not updated, skipping") + + from comfy.ops import disable_weight_init, CastWeightBiasOp, cast_bias_weight + + if sage_attention != "disabled": + print("Patching comfy attention to use sageattn") + from sageattention import sageattn + def set_sage_func(sage_attention): + if sage_attention == "auto": + def func(q, k, v, is_causal=False, attn_mask=None, tensor_layout="NHD"): + return sageattn(q, k, v, is_causal=is_causal, attn_mask=attn_mask, tensor_layout=tensor_layout) + return func + elif sage_attention == "sageattn_qk_int8_pv_fp16_cuda": + from sageattention import sageattn_qk_int8_pv_fp16_cuda + def func(q, k, v, is_causal=False, attn_mask=None, tensor_layout="NHD"): + return sageattn_qk_int8_pv_fp16_cuda(q, k, v, is_causal=is_causal, attn_mask=attn_mask, pv_accum_dtype="fp32", tensor_layout=tensor_layout) + return func + elif sage_attention == "sageattn_qk_int8_pv_fp16_triton": + from sageattention import sageattn_qk_int8_pv_fp16_triton + def func(q, k, v, is_causal=False, attn_mask=None, tensor_layout="NHD"): + return sageattn_qk_int8_pv_fp16_triton(q, k, v, is_causal=is_causal, attn_mask=attn_mask, tensor_layout=tensor_layout) + return func + elif sage_attention == "sageattn_qk_int8_pv_fp8_cuda": + from sageattention import sageattn_qk_int8_pv_fp8_cuda + def func(q, k, v, is_causal=False, attn_mask=None, tensor_layout="NHD"): + return sageattn_qk_int8_pv_fp8_cuda(q, k, v, is_causal=is_causal, attn_mask=attn_mask, pv_accum_dtype="fp32+fp32", tensor_layout=tensor_layout) + return func + elif sage_attention == "sageattn_qk_int8_pv_fp8_cuda++": + from sageattention import sageattn_qk_int8_pv_fp8_cuda + def func(q, k, v, is_causal=False, attn_mask=None, tensor_layout="NHD"): + return sageattn_qk_int8_pv_fp8_cuda(q, k, v, is_causal=is_causal, attn_mask=attn_mask, pv_accum_dtype="fp32+fp16", tensor_layout=tensor_layout) + return func + + sage_func = set_sage_func(sage_attention) + + @torch.compiler.disable() + def attention_sage(q, k, v, heads, mask=None, attn_precision=None, skip_reshape=False, skip_output_reshape=False): + if skip_reshape: + b, _, _, dim_head = q.shape + tensor_layout="HND" + else: + b, _, dim_head = q.shape + dim_head //= heads + q, k, v = map( + lambda t: t.view(b, -1, heads, dim_head), + (q, k, v), + ) + tensor_layout="NHD" + if mask is not None: + # add a batch dimension if there isn't already one + if mask.ndim == 2: + mask = mask.unsqueeze(0) + # add a heads dimension if there isn't already one + if mask.ndim == 3: + mask = mask.unsqueeze(1) + out = sage_func(q, k, v, attn_mask=mask, is_causal=False, tensor_layout=tensor_layout) + if tensor_layout == "HND": + if not skip_output_reshape: + out = ( + out.transpose(1, 2).reshape(b, -1, heads * dim_head) + ) + else: + if skip_output_reshape: + out = out.transpose(1, 2) + else: + out = out.reshape(b, -1, heads * dim_head) + return out + + comfy_attention.optimized_attention = attention_sage + comfy.ldm.hunyuan_video.model.optimized_attention = attention_sage + comfy.ldm.flux.math.optimized_attention = attention_sage + comfy.ldm.genmo.joint_model.asymm_models_joint.optimized_attention = attention_sage + comfy.ldm.cosmos.blocks.optimized_attention = attention_sage + comfy.ldm.wan.model.optimized_attention = attention_sage + try: + comfy.ldm.qwen_image.model.Attention.forward = qwen_sage_forward + except: + pass + + else: + print("Restoring initial comfy attention") + comfy_attention.optimized_attention = _original_functions.get("orig_attention") + comfy.ldm.hunyuan_video.model.optimized_attention = _original_functions.get("orig_attention") + comfy.ldm.flux.math.optimized_attention = _original_functions.get("orig_attention") + comfy.ldm.genmo.joint_model.asymm_models_joint.optimized_attention = _original_functions.get("orig_attention") + comfy.ldm.cosmos.blocks.optimized_attention = _original_functions.get("orig_attention") + comfy.ldm.wan.model.optimized_attention = _original_functions.get("orig_attention") + try: + comfy.ldm.qwen_image.model.Attention.forward = _original_functions.get("original_qwen_forward") + except: + pass + + if patch_cublaslinear: + if not BaseLoaderKJ.cublas_patched: + BaseLoaderKJ.original_linear = disable_weight_init.Linear + try: + from cublas_ops import CublasLinear + except ImportError: + raise Exception("Can't import 'torch-cublas-hgemm', install it from here https://github.com/aredden/torch-cublas-hgemm") + + class PatchedLinear(CublasLinear, CastWeightBiasOp): + def reset_parameters(self): + pass + + def forward_comfy_cast_weights(self, input): + weight, bias = cast_bias_weight(self, input) + return torch.nn.functional.linear(input, weight, bias) + + def forward(self, *args, **kwargs): + if self.comfy_cast_weights: + return self.forward_comfy_cast_weights(*args, **kwargs) + else: + return super().forward(*args, **kwargs) + + disable_weight_init.Linear = PatchedLinear + BaseLoaderKJ.cublas_patched = True + else: + if BaseLoaderKJ.cublas_patched: + disable_weight_init.Linear = BaseLoaderKJ.original_linear + BaseLoaderKJ.cublas_patched = False + + +from comfy.patcher_extension import CallbacksMP +class PathchSageAttentionKJ(BaseLoaderKJ): + @classmethod + def INPUT_TYPES(s): + return {"required": { + "model": ("MODEL",), + "sage_attention": (sageattn_modes, {"default": False, "tooltip": "Global patch comfy attention to use sageattn, once patched to revert back to normal you would need to run this node again with disabled option."}), + }} + + RETURN_TYPES = ("MODEL", ) + FUNCTION = "patch" + DESCRIPTION = "Experimental node for patching attention mode. This doesn't use the model patching system and thus can't be disabled without running the node again with 'disabled' option." + EXPERIMENTAL = True + CATEGORY = "KJNodes/experimental" + + def patch(self, model, sage_attention): + model_clone = model.clone() + @torch.compiler.disable() + def patch_attention_enable(model): + self._patch_modules(False, sage_attention) + @torch.compiler.disable() + def patch_attention_disable(model): + self._patch_modules(False, "disabled") + + model_clone.add_callback(CallbacksMP.ON_PRE_RUN, patch_attention_enable) + model_clone.add_callback(CallbacksMP.ON_CLEANUP, patch_attention_disable) + + return model_clone, + +class CheckpointLoaderKJ(BaseLoaderKJ): + @classmethod + def INPUT_TYPES(s): + return {"required": { + "ckpt_name": (folder_paths.get_filename_list("checkpoints"), {"tooltip": "The name of the checkpoint (model) to load."}), + "weight_dtype": (["default", "fp8_e4m3fn", "fp8_e4m3fn_fast", "fp8_e5m2", "fp16", "bf16", "fp32"],), + "compute_dtype": (["default", "fp16", "bf16", "fp32"], {"default": "default", "tooltip": "The compute dtype to use for the model."}), + "patch_cublaslinear": ("BOOLEAN", {"default": False, "tooltip": "Enable or disable the patching, won't take effect on already loaded models!"}), + "sage_attention": (sageattn_modes, {"default": False, "tooltip": "Patch comfy attention to use sageattn."}), + "enable_fp16_accumulation": ("BOOLEAN", {"default": False, "tooltip": "Enable torch.backends.cuda.matmul.allow_fp16_accumulation, requires pytorch 2.7.0 nightly."}), + }} + + RETURN_TYPES = ("MODEL", "CLIP", "VAE") + FUNCTION = "patch" + DESCRIPTION = "Experimental node for patching torch.nn.Linear with CublasLinear." + EXPERIMENTAL = True + CATEGORY = "KJNodes/experimental" + + def patch(self, ckpt_name, weight_dtype, compute_dtype, patch_cublaslinear, sage_attention, enable_fp16_accumulation): + DTYPE_MAP = { + "fp8_e4m3fn": torch.float8_e4m3fn, + "fp8_e5m2": torch.float8_e5m2, + "fp16": torch.float16, + "bf16": torch.bfloat16, + "fp32": torch.float32 + } + model_options = {} + if dtype := DTYPE_MAP.get(weight_dtype): + model_options["dtype"] = dtype + print(f"Setting {ckpt_name} weight dtype to {dtype}") + + if weight_dtype == "fp8_e4m3fn_fast": + model_options["dtype"] = torch.float8_e4m3fn + model_options["fp8_optimizations"] = True + + ckpt_path = folder_paths.get_full_path_or_raise("checkpoints", ckpt_name) + sd, metadata = comfy.utils.load_torch_file(ckpt_path, return_metadata=True) + + model, clip, vae = self.load_state_dict_guess_config( + sd, + output_vae=True, + output_clip=True, + embedding_directory=folder_paths.get_folder_paths("embeddings"), + metadata=metadata, + model_options=model_options) + + if dtype := DTYPE_MAP.get(compute_dtype): + model.set_model_compute_dtype(dtype) + model.force_cast_weights = False + print(f"Setting {ckpt_name} compute dtype to {dtype}") + + if enable_fp16_accumulation: + if hasattr(torch.backends.cuda.matmul, "allow_fp16_accumulation"): + torch.backends.cuda.matmul.allow_fp16_accumulation = True + else: + raise RuntimeError("Failed to set fp16 accumulation, this requires pytorch 2.7.0 nightly currently") + else: + if hasattr(torch.backends.cuda.matmul, "allow_fp16_accumulation"): + torch.backends.cuda.matmul.allow_fp16_accumulation = False + + def patch_attention(model): + self._patch_modules(patch_cublaslinear, sage_attention) + model.add_callback(CallbacksMP.ON_PRE_RUN,patch_attention) + return model, clip, vae + + def load_state_dict_guess_config(self, sd, output_vae=True, output_clip=True, embedding_directory=None, output_model=True, model_options={}, te_model_options={}, metadata=None): + from comfy.sd import load_diffusion_model_state_dict, model_detection, VAE, CLIP + clip = None + vae = None + model = None + model_patcher = None + + diffusion_model_prefix = model_detection.unet_prefix_from_state_dict(sd) + parameters = comfy.utils.calculate_parameters(sd, diffusion_model_prefix) + weight_dtype = comfy.utils.weight_dtype(sd, diffusion_model_prefix) + load_device = mm.get_torch_device() + + model_config = model_detection.model_config_from_unet(sd, diffusion_model_prefix, metadata=metadata) + if model_config is None: + logging.warning("Warning, This is not a checkpoint file, trying to load it as a diffusion model only.") + diffusion_model = load_diffusion_model_state_dict(sd, model_options={}) + if diffusion_model is None: + return None + return (diffusion_model, None, VAE(sd={}), None) # The VAE object is there to throw an exception if it's actually used' + + + unet_weight_dtype = list(model_config.supported_inference_dtypes) + if model_config.scaled_fp8 is not None: + weight_dtype = None + + model_config.custom_operations = model_options.get("custom_operations", None) + unet_dtype = model_options.get("dtype", model_options.get("weight_dtype", None)) + + if unet_dtype is None: + unet_dtype = mm.unet_dtype(model_params=parameters, supported_dtypes=unet_weight_dtype, weight_dtype=weight_dtype) + + manual_cast_dtype = mm.unet_manual_cast(unet_dtype, load_device, model_config.supported_inference_dtypes) + model_config.set_inference_dtype(unet_dtype, manual_cast_dtype) + + if output_model: + inital_load_device = mm.unet_inital_load_device(parameters, unet_dtype) + model = model_config.get_model(sd, diffusion_model_prefix, device=inital_load_device) + model.load_model_weights(sd, diffusion_model_prefix) + + if output_vae: + vae_sd = comfy.utils.state_dict_prefix_replace(sd, {k: "" for k in model_config.vae_key_prefix}, filter_keys=True) + vae_sd = model_config.process_vae_state_dict(vae_sd) + vae = VAE(sd=vae_sd, metadata=metadata) + + if output_clip: + clip_target = model_config.clip_target(state_dict=sd) + if clip_target is not None: + clip_sd = model_config.process_clip_state_dict(sd) + if len(clip_sd) > 0: + parameters = comfy.utils.calculate_parameters(clip_sd) + clip = CLIP(clip_target, embedding_directory=embedding_directory, tokenizer_data=clip_sd, parameters=parameters, model_options=te_model_options) + m, u = clip.load_sd(clip_sd, full_model=True) + if len(m) > 0: + m_filter = list(filter(lambda a: ".logit_scale" not in a and ".transformer.text_projection.weight" not in a, m)) + if len(m_filter) > 0: + logging.warning("clip missing: {}".format(m)) + else: + logging.debug("clip missing: {}".format(m)) + + if len(u) > 0: + logging.debug("clip unexpected {}:".format(u)) + else: + logging.warning("no CLIP/text encoder weights in checkpoint, the text encoder model will not be loaded.") + + left_over = sd.keys() + if len(left_over) > 0: + logging.debug("left over keys: {}".format(left_over)) + + if output_model: + model_patcher = comfy.model_patcher.ModelPatcher(model, load_device=load_device, offload_device=mm.unet_offload_device()) + if inital_load_device != torch.device("cpu"): + logging.info("loaded diffusion model directly to GPU") + mm.load_models_gpu([model_patcher], force_full_load=True) + + return (model_patcher, clip, vae) + +class DiffusionModelSelector(): + @classmethod + def INPUT_TYPES(s): + return {"required": { + "model_name": (folder_paths.get_filename_list("diffusion_models"), {"tooltip": "The name of the checkpoint (model) to load."}), + }, + } + + RETURN_TYPES = ("STRING",) + RETURN_NAMES = ("model_path",) + FUNCTION = "get_path" + DESCRIPTION = "Returns the path to the model as a string." + EXPERIMENTAL = True + CATEGORY = "KJNodes/experimental" + + def get_path(self, model_name): + model_path = folder_paths.get_full_path_or_raise("diffusion_models", model_name) + return (model_path,) + +class DiffusionModelLoaderKJ(BaseLoaderKJ): + @classmethod + def INPUT_TYPES(s): + return {"required": { + "model_name": (folder_paths.get_filename_list("diffusion_models"), {"tooltip": "The name of the checkpoint (model) to load."}), + "weight_dtype": (["default", "fp8_e4m3fn", "fp8_e4m3fn_fast", "fp8_e5m2", "fp16", "bf16", "fp32"],), + "compute_dtype": (["default", "fp16", "bf16", "fp32"], {"default": "default", "tooltip": "The compute dtype to use for the model."}), + "patch_cublaslinear": ("BOOLEAN", {"default": False, "tooltip": "Enable or disable the patching, won't take effect on already loaded models!"}), + "sage_attention": (sageattn_modes, {"default": False, "tooltip": "Patch comfy attention to use sageattn."}), + "enable_fp16_accumulation": ("BOOLEAN", {"default": False, "tooltip": "Enable torch.backends.cuda.matmul.allow_fp16_accumulation, requires pytorch 2.7.0 nightly."}), + }, + "optional": { + "extra_state_dict": ("STRING", {"forceInput": True, "tooltip": "The full path to an additional state dict to load, this will be merged with the main state dict. Useful for example to add VACE module to a WanVideoModel. You can use DiffusionModelSelector to easily get the path."}), + } + } + + RETURN_TYPES = ("MODEL",) + FUNCTION = "patch_and_load" + DESCRIPTION = "Node for patching torch.nn.Linear with CublasLinear." + EXPERIMENTAL = True + CATEGORY = "KJNodes/experimental" + + def patch_and_load(self, model_name, weight_dtype, compute_dtype, patch_cublaslinear, sage_attention, enable_fp16_accumulation, extra_state_dict=None): + DTYPE_MAP = { + "fp8_e4m3fn": torch.float8_e4m3fn, + "fp8_e5m2": torch.float8_e5m2, + "fp16": torch.float16, + "bf16": torch.bfloat16, + "fp32": torch.float32 + } + model_options = {} + if dtype := DTYPE_MAP.get(weight_dtype): + model_options["dtype"] = dtype + print(f"Setting {model_name} weight dtype to {dtype}") + + if weight_dtype == "fp8_e4m3fn_fast": + model_options["dtype"] = torch.float8_e4m3fn + model_options["fp8_optimizations"] = True + + if enable_fp16_accumulation: + if hasattr(torch.backends.cuda.matmul, "allow_fp16_accumulation"): + torch.backends.cuda.matmul.allow_fp16_accumulation = True + else: + raise RuntimeError("Failed to set fp16 accumulation, this requires pytorch 2.7.0 nightly currently") + else: + if hasattr(torch.backends.cuda.matmul, "allow_fp16_accumulation"): + torch.backends.cuda.matmul.allow_fp16_accumulation = False + + unet_path = folder_paths.get_full_path_or_raise("diffusion_models", model_name) + + sd = comfy.utils.load_torch_file(unet_path) + if extra_state_dict is not None: + extra_sd = comfy.utils.load_torch_file(extra_state_dict) + sd.update(extra_sd) + del extra_sd + + model = comfy.sd.load_diffusion_model_state_dict(sd, model_options=model_options) + if dtype := DTYPE_MAP.get(compute_dtype): + model.set_model_compute_dtype(dtype) + model.force_cast_weights = False + print(f"Setting {model_name} compute dtype to {dtype}") + + def patch_attention(model): + self._patch_modules(patch_cublaslinear, sage_attention) + model.add_callback(CallbacksMP.ON_PRE_RUN,patch_attention) + + return (model,) + +class ModelPatchTorchSettings: + @classmethod + def INPUT_TYPES(s): + return {"required": { + "model": ("MODEL",), + "enable_fp16_accumulation": ("BOOLEAN", {"default": False, "tooltip": "Enable torch.backends.cuda.matmul.allow_fp16_accumulation, requires pytorch 2.7.0 nightly."}), + }} + + RETURN_TYPES = ("MODEL",) + FUNCTION = "patch" + DESCRIPTION = "Adds callbacks to model to set torch settings before and after running the model." + EXPERIMENTAL = True + CATEGORY = "KJNodes/experimental" + + def patch(self, model, enable_fp16_accumulation): + model_clone = model.clone() + + def patch_enable_fp16_accum(model): + print("Patching torch settings: torch.backends.cuda.matmul.allow_fp16_accumulation = True") + torch.backends.cuda.matmul.allow_fp16_accumulation = True + def patch_disable_fp16_accum(model): + print("Patching torch settings: torch.backends.cuda.matmul.allow_fp16_accumulation = False") + torch.backends.cuda.matmul.allow_fp16_accumulation = False + + if enable_fp16_accumulation: + if hasattr(torch.backends.cuda.matmul, "allow_fp16_accumulation"): + model_clone.add_callback(CallbacksMP.ON_PRE_RUN, patch_enable_fp16_accum) + model_clone.add_callback(CallbacksMP.ON_CLEANUP, patch_disable_fp16_accum) + else: + raise RuntimeError("Failed to set fp16 accumulation, this requires pytorch 2.7.0 nightly currently") + else: + if hasattr(torch.backends.cuda.matmul, "allow_fp16_accumulation"): + model_clone.add_callback(CallbacksMP.ON_PRE_RUN, patch_disable_fp16_accum) + else: + raise RuntimeError("Failed to set fp16 accumulation, this requires pytorch 2.7.0 nightly currently") + + return (model_clone,) + +def patched_patch_model(self, device_to=None, lowvram_model_memory=0, load_weights=True, force_patch_weights=False): + with self.use_ejected(): + + device_to = mm.get_torch_device() + + full_load_override = getattr(self.model, "full_load_override", "auto") + if full_load_override in ["enabled", "disabled"]: + full_load = full_load_override == "enabled" + else: + full_load = lowvram_model_memory == 0 + + self.load(device_to, lowvram_model_memory=lowvram_model_memory, force_patch_weights=force_patch_weights, full_load=full_load) + + for k in self.object_patches: + old = comfy.utils.set_attr(self.model, k, self.object_patches[k]) + if k not in self.object_patches_backup: + self.object_patches_backup[k] = old + + self.inject_model() + return self.model + +def patched_load_lora_for_models(model, clip, lora, strength_model, strength_clip): + + patch_keys = list(model.object_patches_backup.keys()) + for k in patch_keys: + #print("backing up object patch: ", k) + comfy.utils.set_attr(model.model, k, model.object_patches_backup[k]) + + key_map = {} + if model is not None: + key_map = comfy.lora.model_lora_keys_unet(model.model, key_map) + if clip is not None: + key_map = comfy.lora.model_lora_keys_clip(clip.cond_stage_model, key_map) + + lora = comfy.lora_convert.convert_lora(lora) + loaded = comfy.lora.load_lora(lora, key_map) + #print(temp_object_patches_backup) + + if model is not None: + new_modelpatcher = model.clone() + k = new_modelpatcher.add_patches(loaded, strength_model) + else: + k = () + new_modelpatcher = None + + if clip is not None: + new_clip = clip.clone() + k1 = new_clip.add_patches(loaded, strength_clip) + else: + k1 = () + new_clip = None + k = set(k) + k1 = set(k1) + for x in loaded: + if (x not in k) and (x not in k1): + print("NOT LOADED {}".format(x)) + + if patch_keys: + if hasattr(model.model, "compile_settings"): + compile_settings = getattr(model.model, "compile_settings") + print("compile_settings: ", compile_settings) + for k in patch_keys: + if "diffusion_model." in k: + # Remove the prefix to get the attribute path + key = k.replace('diffusion_model.', '') + attributes = key.split('.') + # Start with the diffusion_model object + block = model.get_model_object("diffusion_model") + # Navigate through the attributes to get to the block + for attr in attributes: + if attr.isdigit(): + block = block[int(attr)] + else: + block = getattr(block, attr) + # Compile the block + compiled_block = torch.compile(block, mode=compile_settings["mode"], dynamic=compile_settings["dynamic"], fullgraph=compile_settings["fullgraph"], backend=compile_settings["backend"]) + # Add the compiled block back as an object patch + model.add_object_patch(k, compiled_block) + return (new_modelpatcher, new_clip) + +class PatchModelPatcherOrder: + @classmethod + def INPUT_TYPES(s): + return {"required": { + "model": ("MODEL",), + "patch_order": (["object_patch_first", "weight_patch_first"], {"default": "weight_patch_first", "tooltip": "Patch the comfy patch_model function to load weight patches (LoRAs) before compiling the model"}), + "full_load": (["enabled", "disabled", "auto"], {"default": "auto", "tooltip": "Disabling may help with memory issues when loading large models, when changing this you should probably force model reload to avoid issues!"}), + }} + RETURN_TYPES = ("MODEL",) + FUNCTION = "patch" + CATEGORY = "KJNodes/experimental" + DESCRIPTION = "Patch the comfy patch_model function patching order, useful for torch.compile (used as object_patch) as it should come last if you want to use LoRAs with compile" + EXPERIMENTAL = True + + def patch(self, model, patch_order, full_load): + comfy.model_patcher.ModelPatcher.temp_object_patches_backup = {} + setattr(model.model, "full_load_override", full_load) + if patch_order == "weight_patch_first": + comfy.model_patcher.ModelPatcher.patch_model = patched_patch_model + comfy.sd.load_lora_for_models = patched_load_lora_for_models + else: + comfy.model_patcher.ModelPatcher.patch_model = _original_functions.get("original_patch_model") + comfy.sd.load_lora_for_models = _original_functions.get("original_load_lora_for_models") + + return model, + +class TorchCompileModelFluxAdvanced: + def __init__(self): + self._compiled = False + + @classmethod + def INPUT_TYPES(s): + return {"required": { + "model": ("MODEL",), + "backend": (["inductor", "cudagraphs"],), + "fullgraph": ("BOOLEAN", {"default": False, "tooltip": "Enable full graph mode"}), + "mode": (["default", "max-autotune", "max-autotune-no-cudagraphs", "reduce-overhead"], {"default": "default"}), + "double_blocks": ("STRING", {"default": "0-18", "multiline": True}), + "single_blocks": ("STRING", {"default": "0-37", "multiline": True}), + "dynamic": ("BOOLEAN", {"default": False, "tooltip": "Enable dynamic mode"}), + }, + "optional": { + "dynamo_cache_size_limit": ("INT", {"default": 64, "min": 0, "max": 1024, "step": 1, "tooltip": "torch._dynamo.config.cache_size_limit"}), + } + } + RETURN_TYPES = ("MODEL",) + FUNCTION = "patch" + + CATEGORY = "KJNodes/torchcompile" + EXPERIMENTAL = True + DEPRECATED = True + + def parse_blocks(self, blocks_str): + blocks = [] + for part in blocks_str.split(','): + part = part.strip() + if '-' in part: + start, end = map(int, part.split('-')) + blocks.extend(range(start, end + 1)) + else: + blocks.append(int(part)) + return blocks + + def patch(self, model, backend, mode, fullgraph, single_blocks, double_blocks, dynamic, dynamo_cache_size_limit): + single_block_list = self.parse_blocks(single_blocks) + double_block_list = self.parse_blocks(double_blocks) + m = model.clone() + diffusion_model = m.get_model_object("diffusion_model") + torch._dynamo.config.cache_size_limit = dynamo_cache_size_limit + + if not self._compiled: + try: + for i, block in enumerate(diffusion_model.double_blocks): + if i in double_block_list: + #print("Compiling double_block", i) + m.add_object_patch(f"diffusion_model.double_blocks.{i}", torch.compile(block, mode=mode, dynamic=dynamic, fullgraph=fullgraph, backend=backend)) + for i, block in enumerate(diffusion_model.single_blocks): + if i in single_block_list: + #print("Compiling single block", i) + m.add_object_patch(f"diffusion_model.single_blocks.{i}", torch.compile(block, mode=mode, dynamic=dynamic, fullgraph=fullgraph, backend=backend)) + self._compiled = True + compile_settings = { + "backend": backend, + "mode": mode, + "fullgraph": fullgraph, + "dynamic": dynamic, + } + setattr(m.model, "compile_settings", compile_settings) + except: + raise RuntimeError("Failed to compile model") + + return (m, ) + # rest of the layers that are not patched + # diffusion_model.final_layer = torch.compile(diffusion_model.final_layer, mode=mode, fullgraph=fullgraph, backend=backend) + # diffusion_model.guidance_in = torch.compile(diffusion_model.guidance_in, mode=mode, fullgraph=fullgraph, backend=backend) + # diffusion_model.img_in = torch.compile(diffusion_model.img_in, mode=mode, fullgraph=fullgraph, backend=backend) + # diffusion_model.time_in = torch.compile(diffusion_model.time_in, mode=mode, fullgraph=fullgraph, backend=backend) + # diffusion_model.txt_in = torch.compile(diffusion_model.txt_in, mode=mode, fullgraph=fullgraph, backend=backend) + # diffusion_model.vector_in = torch.compile(diffusion_model.vector_in, mode=mode, fullgraph=fullgraph, backend=backend) + +class TorchCompileModelFluxAdvancedV2: + def __init__(self): + self._compiled = False + + @classmethod + def INPUT_TYPES(s): + return {"required": { + "model": ("MODEL",), + "backend": (["inductor", "cudagraphs"],), + "fullgraph": ("BOOLEAN", {"default": False, "tooltip": "Enable full graph mode"}), + "mode": (["default", "max-autotune", "max-autotune-no-cudagraphs", "reduce-overhead"], {"default": "default"}), + "double_blocks": ("BOOLEAN", {"default": True, "tooltip": "Compile double blocks"}), + "single_blocks": ("BOOLEAN", {"default": True, "tooltip": "Compile single blocks"}), + "dynamic": ("BOOLEAN", {"default": False, "tooltip": "Enable dynamic mode"}), + }, + "optional": { + "dynamo_cache_size_limit": ("INT", {"default": 64, "min": 0, "max": 1024, "step": 1, "tooltip": "torch._dynamo.config.cache_size_limit"}), + } + } + RETURN_TYPES = ("MODEL",) + FUNCTION = "patch" + + CATEGORY = "KJNodes/torchcompile" + EXPERIMENTAL = True + + def patch(self, model, backend, mode, fullgraph, single_blocks, double_blocks, dynamic, dynamo_cache_size_limit): + from comfy_api.torch_helpers import set_torch_compile_wrapper + m = model.clone() + diffusion_model = m.get_model_object("diffusion_model") + torch._dynamo.config.cache_size_limit = dynamo_cache_size_limit + + compile_key_list = [] + + try: + if double_blocks: + for i, block in enumerate(diffusion_model.double_blocks): + compile_key_list.append(f"diffusion_model.double_blocks.{i}") + if single_blocks: + for i, block in enumerate(diffusion_model.single_blocks): + compile_key_list.append(f"diffusion_model.single_blocks.{i}") + + set_torch_compile_wrapper(model=m, keys=compile_key_list, backend=backend, mode=mode, dynamic=dynamic, fullgraph=fullgraph) + except: + raise RuntimeError("Failed to compile model") + + return (m, ) + # rest of the layers that are not patched + # diffusion_model.final_layer = torch.compile(diffusion_model.final_layer, mode=mode, fullgraph=fullgraph, backend=backend) + # diffusion_model.guidance_in = torch.compile(diffusion_model.guidance_in, mode=mode, fullgraph=fullgraph, backend=backend) + # diffusion_model.img_in = torch.compile(diffusion_model.img_in, mode=mode, fullgraph=fullgraph, backend=backend) + # diffusion_model.time_in = torch.compile(diffusion_model.time_in, mode=mode, fullgraph=fullgraph, backend=backend) + # diffusion_model.txt_in = torch.compile(diffusion_model.txt_in, mode=mode, fullgraph=fullgraph, backend=backend) + # diffusion_model.vector_in = torch.compile(diffusion_model.vector_in, mode=mode, fullgraph=fullgraph, backend=backend) + + +class TorchCompileModelHyVideo: + def __init__(self): + self._compiled = False + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "model": ("MODEL",), + "backend": (["inductor","cudagraphs"], {"default": "inductor"}), + "fullgraph": ("BOOLEAN", {"default": False, "tooltip": "Enable full graph mode"}), + "mode": (["default", "max-autotune", "max-autotune-no-cudagraphs", "reduce-overhead"], {"default": "default"}), + "dynamic": ("BOOLEAN", {"default": False, "tooltip": "Enable dynamic mode"}), + "dynamo_cache_size_limit": ("INT", {"default": 64, "min": 0, "max": 1024, "step": 1, "tooltip": "torch._dynamo.config.cache_size_limit"}), + "compile_single_blocks": ("BOOLEAN", {"default": True, "tooltip": "Compile single blocks"}), + "compile_double_blocks": ("BOOLEAN", {"default": True, "tooltip": "Compile double blocks"}), + "compile_txt_in": ("BOOLEAN", {"default": False, "tooltip": "Compile txt_in layers"}), + "compile_vector_in": ("BOOLEAN", {"default": False, "tooltip": "Compile vector_in layers"}), + "compile_final_layer": ("BOOLEAN", {"default": False, "tooltip": "Compile final layer"}), + + }, + } + RETURN_TYPES = ("MODEL",) + FUNCTION = "patch" + + CATEGORY = "KJNodes/torchcompile" + EXPERIMENTAL = True + + def patch(self, model, backend, fullgraph, mode, dynamic, dynamo_cache_size_limit, compile_single_blocks, compile_double_blocks, compile_txt_in, compile_vector_in, compile_final_layer): + m = model.clone() + diffusion_model = m.get_model_object("diffusion_model") + torch._dynamo.config.cache_size_limit = dynamo_cache_size_limit + if not self._compiled: + try: + if compile_single_blocks: + for i, block in enumerate(diffusion_model.single_blocks): + compiled_block = torch.compile(block, fullgraph=fullgraph, dynamic=dynamic, backend=backend, mode=mode) + m.add_object_patch(f"diffusion_model.single_blocks.{i}", compiled_block) + if compile_double_blocks: + for i, block in enumerate(diffusion_model.double_blocks): + compiled_block = torch.compile(block, fullgraph=fullgraph, dynamic=dynamic, backend=backend, mode=mode) + m.add_object_patch(f"diffusion_model.double_blocks.{i}", compiled_block) + if compile_txt_in: + compiled_block = torch.compile(diffusion_model.txt_in, fullgraph=fullgraph, dynamic=dynamic, backend=backend, mode=mode) + m.add_object_patch("diffusion_model.txt_in", compiled_block) + if compile_vector_in: + compiled_block = torch.compile(diffusion_model.vector_in, fullgraph=fullgraph, dynamic=dynamic, backend=backend, mode=mode) + m.add_object_patch("diffusion_model.vector_in", compiled_block) + if compile_final_layer: + compiled_block = torch.compile(diffusion_model.final_layer, fullgraph=fullgraph, dynamic=dynamic, backend=backend, mode=mode) + m.add_object_patch("diffusion_model.final_layer", compiled_block) + self._compiled = True + compile_settings = { + "backend": backend, + "mode": mode, + "fullgraph": fullgraph, + "dynamic": dynamic, + } + setattr(m.model, "compile_settings", compile_settings) + except: + raise RuntimeError("Failed to compile model") + return (m, ) + +class TorchCompileModelWanVideo: + def __init__(self): + self._compiled = False + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "model": ("MODEL",), + "backend": (["inductor","cudagraphs"], {"default": "inductor"}), + "fullgraph": ("BOOLEAN", {"default": False, "tooltip": "Enable full graph mode"}), + "mode": (["default", "max-autotune", "max-autotune-no-cudagraphs", "reduce-overhead"], {"default": "default"}), + "dynamic": ("BOOLEAN", {"default": False, "tooltip": "Enable dynamic mode"}), + "dynamo_cache_size_limit": ("INT", {"default": 64, "min": 0, "max": 1024, "step": 1, "tooltip": "torch._dynamo.config.cache_size_limit"}), + "compile_transformer_blocks_only": ("BOOLEAN", {"default": False, "tooltip": "Compile only transformer blocks"}), + }, + } + RETURN_TYPES = ("MODEL",) + FUNCTION = "patch" + + CATEGORY = "KJNodes/torchcompile" + EXPERIMENTAL = True + DEPRECATED = True + + def patch(self, model, backend, fullgraph, mode, dynamic, dynamo_cache_size_limit, compile_transformer_blocks_only): + m = model.clone() + diffusion_model = m.get_model_object("diffusion_model") + torch._dynamo.config.cache_size_limit = dynamo_cache_size_limit + try: + if compile_transformer_blocks_only: + for i, block in enumerate(diffusion_model.blocks): + if hasattr(block, "_orig_mod"): + block = block._orig_mod + compiled_block = torch.compile(block, fullgraph=fullgraph, dynamic=dynamic, backend=backend, mode=mode) + m.add_object_patch(f"diffusion_model.blocks.{i}", compiled_block) + else: + compiled_model = torch.compile(diffusion_model, fullgraph=fullgraph, dynamic=dynamic, backend=backend, mode=mode) + m.add_object_patch("diffusion_model", compiled_model) + + compile_settings = { + "backend": backend, + "mode": mode, + "fullgraph": fullgraph, + "dynamic": dynamic, + } + setattr(m.model, "compile_settings", compile_settings) + except: + raise RuntimeError("Failed to compile model") + return (m, ) + +class TorchCompileModelWanVideoV2: + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "model": ("MODEL",), + "backend": (["inductor","cudagraphs"], {"default": "inductor"}), + "fullgraph": ("BOOLEAN", {"default": False, "tooltip": "Enable full graph mode"}), + "mode": (["default", "max-autotune", "max-autotune-no-cudagraphs", "reduce-overhead"], {"default": "default"}), + "dynamic": ("BOOLEAN", {"default": False, "tooltip": "Enable dynamic mode"}), + "compile_transformer_blocks_only": ("BOOLEAN", {"default": True, "tooltip": "Compile only transformer blocks, faster compile and less error prone"}), + "dynamo_cache_size_limit": ("INT", {"default": 64, "min": 0, "max": 1024, "step": 1, "tooltip": "torch._dynamo.config.cache_size_limit"}), + }, + } + RETURN_TYPES = ("MODEL",) + FUNCTION = "patch" + + CATEGORY = "KJNodes/torchcompile" + EXPERIMENTAL = True + + def patch(self, model, backend, fullgraph, mode, dynamic, dynamo_cache_size_limit, compile_transformer_blocks_only): + from comfy_api.torch_helpers import set_torch_compile_wrapper + m = model.clone() + diffusion_model = m.get_model_object("diffusion_model") + torch._dynamo.config.cache_size_limit = dynamo_cache_size_limit + try: + if compile_transformer_blocks_only: + compile_key_list = [] + for i, block in enumerate(diffusion_model.blocks): + compile_key_list.append(f"diffusion_model.blocks.{i}") + else: + compile_key_list =["diffusion_model"] + + set_torch_compile_wrapper(model=m, keys=compile_key_list, backend=backend, mode=mode, dynamic=dynamic, fullgraph=fullgraph) + except: + raise RuntimeError("Failed to compile model") + + return (m, ) + +class TorchCompileModelQwenImage: + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "model": ("MODEL",), + "backend": (["inductor","cudagraphs"], {"default": "inductor"}), + "fullgraph": ("BOOLEAN", {"default": False, "tooltip": "Enable full graph mode"}), + "mode": (["default", "max-autotune", "max-autotune-no-cudagraphs", "reduce-overhead"], {"default": "default"}), + "dynamic": ("BOOLEAN", {"default": False, "tooltip": "Enable dynamic mode"}), + "compile_transformer_blocks_only": ("BOOLEAN", {"default": True, "tooltip": "Compile only transformer blocks, faster compile and less error prone"}), + "dynamo_cache_size_limit": ("INT", {"default": 64, "min": 0, "max": 1024, "step": 1, "tooltip": "torch._dynamo.config.cache_size_limit"}), + }, + } + RETURN_TYPES = ("MODEL",) + FUNCTION = "patch" + + CATEGORY = "KJNodes/torchcompile" + EXPERIMENTAL = True + + def patch(self, model, backend, fullgraph, mode, dynamic, dynamo_cache_size_limit, compile_transformer_blocks_only): + from comfy_api.torch_helpers import set_torch_compile_wrapper + m = model.clone() + diffusion_model = m.get_model_object("diffusion_model") + torch._dynamo.config.cache_size_limit = dynamo_cache_size_limit + try: + if compile_transformer_blocks_only: + compile_key_list = [] + for i, block in enumerate(diffusion_model.transformer_blocks): + compile_key_list.append(f"diffusion_model.transformer_blocks.{i}") + else: + compile_key_list =["diffusion_model"] + + set_torch_compile_wrapper(model=m, keys=compile_key_list, backend=backend, mode=mode, dynamic=dynamic, fullgraph=fullgraph) + except: + raise RuntimeError("Failed to compile model") + + return (m, ) + +class TorchCompileVAE: + def __init__(self): + self._compiled_encoder = False + self._compiled_decoder = False + + @classmethod + def INPUT_TYPES(s): + return {"required": { + "vae": ("VAE",), + "backend": (["inductor", "cudagraphs"],), + "fullgraph": ("BOOLEAN", {"default": False, "tooltip": "Enable full graph mode"}), + "mode": (["default", "max-autotune", "max-autotune-no-cudagraphs", "reduce-overhead"], {"default": "default"}), + "compile_encoder": ("BOOLEAN", {"default": True, "tooltip": "Compile encoder"}), + "compile_decoder": ("BOOLEAN", {"default": True, "tooltip": "Compile decoder"}), + }} + RETURN_TYPES = ("VAE",) + FUNCTION = "compile" + + CATEGORY = "KJNodes/torchcompile" + EXPERIMENTAL = True + + def compile(self, vae, backend, mode, fullgraph, compile_encoder, compile_decoder): + if compile_encoder: + if not self._compiled_encoder: + encoder_name = "encoder" + if hasattr(vae.first_stage_model, "taesd_encoder"): + encoder_name = "taesd_encoder" + + try: + setattr( + vae.first_stage_model, + encoder_name, + torch.compile( + getattr(vae.first_stage_model, encoder_name), + mode=mode, + fullgraph=fullgraph, + backend=backend, + ), + ) + self._compiled_encoder = True + except: + raise RuntimeError("Failed to compile model") + if compile_decoder: + if not self._compiled_decoder: + decoder_name = "decoder" + if hasattr(vae.first_stage_model, "taesd_decoder"): + decoder_name = "taesd_decoder" + + try: + setattr( + vae.first_stage_model, + decoder_name, + torch.compile( + getattr(vae.first_stage_model, decoder_name), + mode=mode, + fullgraph=fullgraph, + backend=backend, + ), + ) + self._compiled_decoder = True + except: + raise RuntimeError("Failed to compile model") + return (vae, ) + +class TorchCompileControlNet: + def __init__(self): + self._compiled= False + + @classmethod + def INPUT_TYPES(s): + return {"required": { + "controlnet": ("CONTROL_NET",), + "backend": (["inductor", "cudagraphs"],), + "fullgraph": ("BOOLEAN", {"default": False, "tooltip": "Enable full graph mode"}), + "mode": (["default", "max-autotune", "max-autotune-no-cudagraphs", "reduce-overhead"], {"default": "default"}), + }} + RETURN_TYPES = ("CONTROL_NET",) + FUNCTION = "compile" + + CATEGORY = "KJNodes/torchcompile" + EXPERIMENTAL = True + + def compile(self, controlnet, backend, mode, fullgraph): + if not self._compiled: + try: + # for i, block in enumerate(controlnet.control_model.double_blocks): + # print("Compiling controlnet double_block", i) + # controlnet.control_model.double_blocks[i] = torch.compile(block, mode=mode, fullgraph=fullgraph, backend=backend) + controlnet.control_model = torch.compile(controlnet.control_model, mode=mode, fullgraph=fullgraph, backend=backend) + self._compiled = True + except: + self._compiled = False + raise RuntimeError("Failed to compile model") + + return (controlnet, ) + +class TorchCompileLTXModel: + def __init__(self): + self._compiled = False + + @classmethod + def INPUT_TYPES(s): + return {"required": { + "model": ("MODEL",), + "backend": (["inductor", "cudagraphs"],), + "fullgraph": ("BOOLEAN", {"default": False, "tooltip": "Enable full graph mode"}), + "mode": (["default", "max-autotune", "max-autotune-no-cudagraphs", "reduce-overhead"], {"default": "default"}), + "dynamic": ("BOOLEAN", {"default": False, "tooltip": "Enable dynamic mode"}), + }} + RETURN_TYPES = ("MODEL",) + FUNCTION = "patch" + + CATEGORY = "KJNodes/torchcompile" + EXPERIMENTAL = True + + def patch(self, model, backend, mode, fullgraph, dynamic): + m = model.clone() + diffusion_model = m.get_model_object("diffusion_model") + + if not self._compiled: + try: + for i, block in enumerate(diffusion_model.transformer_blocks): + compiled_block = torch.compile(block, mode=mode, dynamic=dynamic, fullgraph=fullgraph, backend=backend) + m.add_object_patch(f"diffusion_model.transformer_blocks.{i}", compiled_block) + self._compiled = True + compile_settings = { + "backend": backend, + "mode": mode, + "fullgraph": fullgraph, + "dynamic": dynamic, + } + setattr(m.model, "compile_settings", compile_settings) + + except: + raise RuntimeError("Failed to compile model") + + return (m, ) + +class TorchCompileCosmosModel: + def __init__(self): + self._compiled = False + + @classmethod + def INPUT_TYPES(s): + return {"required": { + "model": ("MODEL",), + "backend": (["inductor", "cudagraphs"],), + "fullgraph": ("BOOLEAN", {"default": False, "tooltip": "Enable full graph mode"}), + "mode": (["default", "max-autotune", "max-autotune-no-cudagraphs", "reduce-overhead"], {"default": "default"}), + "dynamic": ("BOOLEAN", {"default": False, "tooltip": "Enable dynamic mode"}), + "dynamo_cache_size_limit": ("INT", {"default": 64, "tooltip": "Set the dynamo cache size limit"}), + }} + RETURN_TYPES = ("MODEL",) + FUNCTION = "patch" + + CATEGORY = "KJNodes/torchcompile" + EXPERIMENTAL = True + + def patch(self, model, backend, mode, fullgraph, dynamic, dynamo_cache_size_limit): + + m = model.clone() + diffusion_model = m.get_model_object("diffusion_model") + torch._dynamo.config.cache_size_limit = dynamo_cache_size_limit + + if not self._compiled: + try: + for name, block in diffusion_model.blocks.items(): + #print(f"Compiling block {name}") + compiled_block = torch.compile(block, mode=mode, dynamic=dynamic, fullgraph=fullgraph, backend=backend) + m.add_object_patch(f"diffusion_model.blocks.{name}", compiled_block) + #diffusion_model.blocks[name] = compiled_block + + self._compiled = True + compile_settings = { + "backend": backend, + "mode": mode, + "fullgraph": fullgraph, + "dynamic": dynamic, + } + setattr(m.model, "compile_settings", compile_settings) + + except: + raise RuntimeError("Failed to compile model") + + return (m, ) + + +#teacache + +try: + from comfy.ldm.wan.model import sinusoidal_embedding_1d +except: + pass +from einops import repeat +from unittest.mock import patch +from contextlib import nullcontext +import numpy as np + +def relative_l1_distance(last_tensor, current_tensor): + l1_distance = torch.abs(last_tensor - current_tensor).mean() + norm = torch.abs(last_tensor).mean() + relative_l1_distance = l1_distance / norm + return relative_l1_distance.to(torch.float32) + +@torch.compiler.disable() +def tea_cache(self, x, e0, e, transformer_options): + #teacache for cond and uncond separately + rel_l1_thresh = transformer_options["rel_l1_thresh"] + + is_cond = True if transformer_options["cond_or_uncond"] == [0] else False + + should_calc = True + suffix = "cond" if is_cond else "uncond" + + # Init cache dict if not exists + if not hasattr(self, 'teacache_state'): + self.teacache_state = { + 'cond': {'accumulated_rel_l1_distance': 0, 'prev_input': None, + 'teacache_skipped_steps': 0, 'previous_residual': None}, + 'uncond': {'accumulated_rel_l1_distance': 0, 'prev_input': None, + 'teacache_skipped_steps': 0, 'previous_residual': None} + } + logging.info("\nTeaCache: Initialized") + + cache = self.teacache_state[suffix] + + if cache['prev_input'] is not None: + if transformer_options["coefficients"] == []: + temb_relative_l1 = relative_l1_distance(cache['prev_input'], e0) + curr_acc_dist = cache['accumulated_rel_l1_distance'] + temb_relative_l1 + else: + rescale_func = np.poly1d(transformer_options["coefficients"]) + curr_acc_dist = cache['accumulated_rel_l1_distance'] + rescale_func(((e-cache['prev_input']).abs().mean() / cache['prev_input'].abs().mean()).cpu().item()) + try: + if curr_acc_dist < rel_l1_thresh: + should_calc = False + cache['accumulated_rel_l1_distance'] = curr_acc_dist + else: + should_calc = True + cache['accumulated_rel_l1_distance'] = 0 + except: + should_calc = True + cache['accumulated_rel_l1_distance'] = 0 + + if transformer_options["coefficients"] == []: + cache['prev_input'] = e0.clone().detach() + else: + cache['prev_input'] = e.clone().detach() + + if not should_calc: + x += cache['previous_residual'].to(x.device) + cache['teacache_skipped_steps'] += 1 + #print(f"TeaCache: Skipping {suffix} step") + return should_calc, cache + +def teacache_wanvideo_vace_forward_orig(self, x, t, context, vace_context, vace_strength, clip_fea=None, freqs=None, transformer_options={}, **kwargs): + # embeddings + x = self.patch_embedding(x.float()).to(x.dtype) + grid_sizes = x.shape[2:] + x = x.flatten(2).transpose(1, 2) + + # time embeddings + e = self.time_embedding( + sinusoidal_embedding_1d(self.freq_dim, t).to(dtype=x[0].dtype)) + e0 = self.time_projection(e).unflatten(1, (6, self.dim)) + + # context + context = self.text_embedding(context) + + context_img_len = None + if clip_fea is not None: + if self.img_emb is not None: + context_clip = self.img_emb(clip_fea) # bs x 257 x dim + context = torch.concat([context_clip, context], dim=1) + context_img_len = clip_fea.shape[-2] + + orig_shape = list(vace_context.shape) + vace_context = vace_context.movedim(0, 1).reshape([-1] + orig_shape[2:]) + c = self.vace_patch_embedding(vace_context.float()).to(vace_context.dtype) + c = c.flatten(2).transpose(1, 2) + c = list(c.split(orig_shape[0], dim=0)) + + if not transformer_options: + raise RuntimeError("Can't access transformer_options, this requires ComfyUI nightly version from Mar 14, 2025 or later") + + teacache_enabled = transformer_options.get("teacache_enabled", False) + if not teacache_enabled: + should_calc = True + else: + should_calc, cache = tea_cache(self, x, e0, e, transformer_options) + + if should_calc: + original_x = x.clone().detach() + patches_replace = transformer_options.get("patches_replace", {}) + blocks_replace = patches_replace.get("dit", {}) + for i, block in enumerate(self.blocks): + if ("double_block", i) in blocks_replace: + def block_wrap(args): + out = {} + out["img"] = block(args["img"], context=args["txt"], e=args["vec"], freqs=args["pe"], context_img_len=context_img_len) + return out + out = blocks_replace[("double_block", i)]({"img": x, "txt": context, "vec": e0, "pe": freqs}, {"original_block": block_wrap, "transformer_options": transformer_options}) + x = out["img"] + else: + x = block(x, e=e0, freqs=freqs, context=context, context_img_len=context_img_len) + + ii = self.vace_layers_mapping.get(i, None) + if ii is not None: + for iii in range(len(c)): + c_skip, c[iii] = self.vace_blocks[ii](c[iii], x=original_x, e=e0, freqs=freqs, context=context, context_img_len=context_img_len) + x += c_skip * vace_strength[iii] + del c_skip + + if teacache_enabled: + cache['previous_residual'] = (x - original_x).to(transformer_options["teacache_device"]) + + # head + x = self.head(x, e) + + # unpatchify + x = self.unpatchify(x, grid_sizes) + return x + +def teacache_wanvideo_forward_orig(self, x, t, context, clip_fea=None, freqs=None, transformer_options={}, **kwargs): + # embeddings + x = self.patch_embedding(x.float()).to(x.dtype) + grid_sizes = x.shape[2:] + x = x.flatten(2).transpose(1, 2) + + # time embeddings + e = self.time_embedding( + sinusoidal_embedding_1d(self.freq_dim, t).to(dtype=x[0].dtype)) + e0 = self.time_projection(e).unflatten(1, (6, self.dim)) + + # context + context = self.text_embedding(context) + + context_img_len = None + if clip_fea is not None: + if self.img_emb is not None: + context_clip = self.img_emb(clip_fea) # bs x 257 x dim + context = torch.concat([context_clip, context], dim=1) + context_img_len = clip_fea.shape[-2] + + + teacache_enabled = transformer_options.get("teacache_enabled", False) + if not teacache_enabled: + should_calc = True + else: + should_calc, cache = tea_cache(self, x, e0, e, transformer_options) + + if should_calc: + original_x = x.clone().detach() + patches_replace = transformer_options.get("patches_replace", {}) + blocks_replace = patches_replace.get("dit", {}) + for i, block in enumerate(self.blocks): + if ("double_block", i) in blocks_replace: + def block_wrap(args): + out = {} + out["img"] = block(args["img"], context=args["txt"], e=args["vec"], freqs=args["pe"], context_img_len=context_img_len) + return out + out = blocks_replace[("double_block", i)]({"img": x, "txt": context, "vec": e0, "pe": freqs}, {"original_block": block_wrap, "transformer_options": transformer_options}) + x = out["img"] + else: + x = block(x, e=e0, freqs=freqs, context=context, context_img_len=context_img_len) + + if teacache_enabled: + cache['previous_residual'] = (x - original_x).to(transformer_options["teacache_device"]) + + # head + x = self.head(x, e) + + # unpatchify + x = self.unpatchify(x, grid_sizes) + return x + +class WanVideoTeaCacheKJ: + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "model": ("MODEL",), + "rel_l1_thresh": ("FLOAT", {"default": 0.275, "min": 0.0, "max": 10.0, "step": 0.001, "tooltip": "Threshold for to determine when to apply the cache, compromise between speed and accuracy. When using coefficients a good value range is something between 0.2-0.4 for all but 1.3B model, which should be about 10 times smaller, same as when not using coefficients."}), + "start_percent": ("FLOAT", {"default": 0.1, "min": 0.0, "max": 1.0, "step": 0.01, "tooltip": "The start percentage of the steps to use with TeaCache."}), + "end_percent": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01, "tooltip": "The end percentage of the steps to use with TeaCache."}), + "cache_device": (["main_device", "offload_device"], {"default": "offload_device", "tooltip": "Device to cache to"}), + "coefficients": (["disabled", "1.3B", "14B", "i2v_480", "i2v_720"], {"default": "i2v_480", "tooltip": "Coefficients for rescaling the relative l1 distance, if disabled the threshold value should be about 10 times smaller than the value used with coefficients."}), + } + } + + RETURN_TYPES = ("MODEL",) + RETURN_NAMES = ("model",) + FUNCTION = "patch_teacache" + CATEGORY = "KJNodes/teacache" + DESCRIPTION = """ +Patch WanVideo model to use TeaCache. Speeds up inference by caching the output and +applying it instead of doing the step. Best results are achieved by choosing the +appropriate coefficients for the model. Early steps should never be skipped, with too +aggressive values this can happen and the motion suffers. Starting later can help with that too. +When NOT using coefficients, the threshold value should be +about 10 times smaller than the value used with coefficients. + +Official recommended values https://github.com/ali-vilab/TeaCache/tree/main/TeaCache4Wan2.1: + + +
    ++-------------------+--------+---------+--------+
    +|       Model       |  Low   | Medium  |  High  |
    ++-------------------+--------+---------+--------+
    +| Wan2.1 t2v 1.3B  |  0.05  |  0.07   |  0.08  |
    +| Wan2.1 t2v 14B   |  0.14  |  0.15   |  0.20  |
    +| Wan2.1 i2v 480P  |  0.13  |  0.19   |  0.26  |
    +| Wan2.1 i2v 720P  |  0.18  |  0.20   |  0.30  |
    ++-------------------+--------+---------+--------+
    +
    +""" + EXPERIMENTAL = True + + def patch_teacache(self, model, rel_l1_thresh, start_percent, end_percent, cache_device, coefficients): + if rel_l1_thresh == 0: + return (model,) + + if coefficients == "disabled" and rel_l1_thresh > 0.1: + logging.warning("Threshold value is too high for TeaCache without coefficients, consider using coefficients for better results.") + if coefficients != "disabled" and rel_l1_thresh < 0.1 and "1.3B" not in coefficients: + logging.warning("Threshold value is too low for TeaCache with coefficients, consider using higher threshold value for better results.") + + # type_str = str(type(model.model.model_config).__name__) + #if model.model.diffusion_model.dim == 1536: + # model_type ="1.3B" + # else: + # if "WAN21_T2V" in type_str: + # model_type = "14B" + # elif "WAN21_I2V" in type_str: + # model_type = "i2v_480" + # else: + # model_type = "i2v_720" #how to detect this? + + + teacache_coefficients_map = { + "disabled": [], + "1.3B": [2.39676752e+03, -1.31110545e+03, 2.01331979e+02, -8.29855975e+00, 1.37887774e-01], + "14B": [-5784.54975374, 5449.50911966, -1811.16591783, 256.27178429, -13.02252404], + "i2v_480": [-3.02331670e+02, 2.23948934e+02, -5.25463970e+01, 5.87348440e+00, -2.01973289e-01], + "i2v_720": [-114.36346466, 65.26524496, -18.82220707, 4.91518089, -0.23412683], + } + coefficients = teacache_coefficients_map[coefficients] + + teacache_device = mm.get_torch_device() if cache_device == "main_device" else mm.unet_offload_device() + + model_clone = model.clone() + if 'transformer_options' not in model_clone.model_options: + model_clone.model_options['transformer_options'] = {} + model_clone.model_options["transformer_options"]["rel_l1_thresh"] = rel_l1_thresh + model_clone.model_options["transformer_options"]["teacache_device"] = teacache_device + model_clone.model_options["transformer_options"]["coefficients"] = coefficients + diffusion_model = model_clone.get_model_object("diffusion_model") + + def outer_wrapper(start_percent, end_percent): + def unet_wrapper_function(model_function, kwargs): + input = kwargs["input"] + timestep = kwargs["timestep"] + c = kwargs["c"] + sigmas = c["transformer_options"]["sample_sigmas"] + cond_or_uncond = kwargs["cond_or_uncond"] + last_step = (len(sigmas) - 1) + + matched_step_index = (sigmas == timestep[0] ).nonzero() + if len(matched_step_index) > 0: + current_step_index = matched_step_index.item() + else: + for i in range(len(sigmas) - 1): + # walk from beginning of steps until crossing the timestep + if (sigmas[i] - timestep[0]) * (sigmas[i + 1] - timestep[0]) <= 0: + current_step_index = i + break + else: + current_step_index = 0 + + if current_step_index == 0: + if (len(cond_or_uncond) == 1 and cond_or_uncond[0] == 1) or len(cond_or_uncond) == 2: + if hasattr(diffusion_model, "teacache_state"): + delattr(diffusion_model, "teacache_state") + logging.info("\nResetting TeaCache state") + + current_percent = current_step_index / (len(sigmas) - 1) + c["transformer_options"]["current_percent"] = current_percent + if start_percent <= current_percent <= end_percent: + c["transformer_options"]["teacache_enabled"] = True + + forward_function = teacache_wanvideo_vace_forward_orig if hasattr(diffusion_model, "vace_layers") else teacache_wanvideo_forward_orig + context = patch.multiple( + diffusion_model, + forward_orig=forward_function.__get__(diffusion_model, diffusion_model.__class__) + ) + + with context: + out = model_function(input, timestep, **c) + if current_step_index+1 == last_step and hasattr(diffusion_model, "teacache_state"): + if len(cond_or_uncond) == 1 and cond_or_uncond[0] == 0: + skipped_steps_cond = diffusion_model.teacache_state["cond"]["teacache_skipped_steps"] + skipped_steps_uncond = diffusion_model.teacache_state["uncond"]["teacache_skipped_steps"] + logging.info("-----------------------------------") + logging.info(f"TeaCache skipped:") + logging.info(f"{skipped_steps_cond} cond steps") + logging.info(f"{skipped_steps_uncond} uncond step") + logging.info(f"out of {last_step} steps") + logging.info("-----------------------------------") + elif len(cond_or_uncond) == 2: + skipped_steps_cond = diffusion_model.teacache_state["uncond"]["teacache_skipped_steps"] + logging.info("-----------------------------------") + logging.info(f"TeaCache skipped:") + logging.info(f"{skipped_steps_cond} cond steps") + logging.info(f"out of {last_step} steps") + logging.info("-----------------------------------") + + return out + return unet_wrapper_function + + model_clone.set_model_unet_function_wrapper(outer_wrapper(start_percent=start_percent, end_percent=end_percent)) + + return (model_clone,) + + + + +from comfy.ldm.flux.math import apply_rope + +def modified_wan_self_attention_forward(self, x, freqs): + r""" + Args: + x(Tensor): Shape [B, L, num_heads, C / num_heads] + freqs(Tensor): Rope freqs, shape [1024, C / num_heads / 2] + """ + b, s, n, d = *x.shape[:2], self.num_heads, self.head_dim + + # query, key, value function + def qkv_fn(x): + q = self.norm_q(self.q(x)).view(b, s, n, d) + k = self.norm_k(self.k(x)).view(b, s, n, d) + v = self.v(x).view(b, s, n * d) + return q, k, v + + q, k, v = qkv_fn(x) + + q, k = apply_rope(q, k, freqs) + + feta_scores = get_feta_scores(q, k, self.num_frames, self.enhance_weight) + + x = comfy.ldm.modules.attention.optimized_attention( + q.view(b, s, n * d), + k.view(b, s, n * d), + v, + heads=self.num_heads, + ) + + x = self.o(x) + + x *= feta_scores + + return x + +from einops import rearrange +def get_feta_scores(query, key, num_frames, enhance_weight): + img_q, img_k = query, key #torch.Size([2, 9216, 12, 128]) + + _, ST, num_heads, head_dim = img_q.shape + spatial_dim = ST / num_frames + spatial_dim = int(spatial_dim) + + query_image = rearrange( + img_q, "B (T S) N C -> (B S) N T C", T=num_frames, S=spatial_dim, N=num_heads, C=head_dim + ) + key_image = rearrange( + img_k, "B (T S) N C -> (B S) N T C", T=num_frames, S=spatial_dim, N=num_heads, C=head_dim + ) + + return feta_score(query_image, key_image, head_dim, num_frames, enhance_weight) + +def feta_score(query_image, key_image, head_dim, num_frames, enhance_weight): + scale = head_dim**-0.5 + query_image = query_image * scale + attn_temp = query_image @ key_image.transpose(-2, -1) # translate attn to float32 + attn_temp = attn_temp.to(torch.float32) + attn_temp = attn_temp.softmax(dim=-1) + + # Reshape to [batch_size * num_tokens, num_frames, num_frames] + attn_temp = attn_temp.reshape(-1, num_frames, num_frames) + + # Create a mask for diagonal elements + diag_mask = torch.eye(num_frames, device=attn_temp.device).bool() + diag_mask = diag_mask.unsqueeze(0).expand(attn_temp.shape[0], -1, -1) + + # Zero out diagonal elements + attn_wo_diag = attn_temp.masked_fill(diag_mask, 0) + + # Calculate mean for each token's attention matrix + # Number of off-diagonal elements per matrix is n*n - n + num_off_diag = num_frames * num_frames - num_frames + mean_scores = attn_wo_diag.sum(dim=(1, 2)) / num_off_diag + + enhance_scores = mean_scores.mean() * (num_frames + enhance_weight) + enhance_scores = enhance_scores.clamp(min=1) + return enhance_scores + +import types +class WanAttentionPatch: + def __init__(self, num_frames, weight): + self.num_frames = num_frames + self.enhance_weight = weight + + def __get__(self, obj, objtype=None): + # Create bound method with stored parameters + def wrapped_attention(self_module, *args, **kwargs): + self_module.num_frames = self.num_frames + self_module.enhance_weight = self.enhance_weight + return modified_wan_self_attention_forward(self_module, *args, **kwargs) + return types.MethodType(wrapped_attention, obj) + +class WanVideoEnhanceAVideoKJ: + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "model": ("MODEL",), + "latent": ("LATENT", {"tooltip": "Only used to get the latent count"}), + "weight": ("FLOAT", {"default": 2.0, "min": 0.0, "max": 10.0, "step": 0.001, "tooltip": "Strength of the enhance effect"}), + } + } + + RETURN_TYPES = ("MODEL",) + RETURN_NAMES = ("model",) + FUNCTION = "enhance" + CATEGORY = "KJNodes/experimental" + DESCRIPTION = "https://github.com/NUS-HPC-AI-Lab/Enhance-A-Video" + EXPERIMENTAL = True + + def enhance(self, model, weight, latent): + if weight == 0: + return (model,) + + num_frames = latent["samples"].shape[2] + + model_clone = model.clone() + if 'transformer_options' not in model_clone.model_options: + model_clone.model_options['transformer_options'] = {} + model_clone.model_options["transformer_options"]["enhance_weight"] = weight + diffusion_model = model_clone.get_model_object("diffusion_model") + + compile_settings = getattr(model.model, "compile_settings", None) + for idx, block in enumerate(diffusion_model.blocks): + patched_attn = WanAttentionPatch(num_frames, weight).__get__(block.self_attn, block.__class__) + if compile_settings is not None: + patched_attn = torch.compile(patched_attn, mode=compile_settings["mode"], dynamic=compile_settings["dynamic"], fullgraph=compile_settings["fullgraph"], backend=compile_settings["backend"]) + + model_clone.add_object_patch(f"diffusion_model.blocks.{idx}.self_attn.forward", patched_attn) + + return (model_clone,) + +def normalized_attention_guidance(self, query, context_positive, context_negative): + k_positive = self.norm_k(self.k(context_positive)) + v_positive = self.v(context_positive) + k_negative = self.norm_k(self.k(context_negative)) + v_negative = self.v(context_negative) + + x_positive = comfy.ldm.modules.attention.optimized_attention(query, k_positive, v_positive, heads=self.num_heads).flatten(2) + x_negative = comfy.ldm.modules.attention.optimized_attention(query, k_negative, v_negative, heads=self.num_heads).flatten(2) + + nag_guidance = x_positive * self.nag_scale - x_negative * (self.nag_scale - 1) + + norm_positive = torch.norm(x_positive, p=1, dim=-1, keepdim=True).expand_as(x_positive) + norm_guidance = torch.norm(nag_guidance, p=1, dim=-1, keepdim=True).expand_as(nag_guidance) + + scale = torch.nan_to_num(norm_guidance / norm_positive, nan=10.0) + + mask = scale > self.nag_tau + adjustment = (norm_positive * self.nag_tau) / (norm_guidance + 1e-7) + nag_guidance = torch.where(mask, nag_guidance * adjustment, nag_guidance) + + x = nag_guidance * self.nag_alpha + x_positive * (1 - self.nag_alpha) + del nag_guidance + + return x + +#region NAG +def wan_crossattn_forward_nag(self, x, context, **kwargs): + r""" + Args: + x(Tensor): Shape [B, L1, C] + context(Tensor): Shape [B, L2, C] + """ + # Determine batch splitting and context handling + if self.input_type == "default": + # Single or [pos, neg] pair + if context.shape[0] == 1: + x_pos, context_pos = x, context + x_neg, context_neg = None, None + else: + x_pos, x_neg = torch.chunk(x, 2, dim=0) + context_pos, context_neg = torch.chunk(context, 2, dim=0) + elif self.input_type == "batch": + # Standard batch, no CFG + x_pos, context_pos = x, context + x_neg, context_neg = None, None + + # Positive branch + q_pos = self.norm_q(self.q(x_pos)) + nag_context = self.nag_context + if self.input_type == "batch": + nag_context = nag_context.repeat(x_pos.shape[0], 1, 1) + x_pos_out = normalized_attention_guidance(self, q_pos, context_pos, nag_context) + + # Negative branch + if x_neg is not None and context_neg is not None: + q_neg = self.norm_q(self.q(x_neg)) + k_neg = self.norm_k(self.k(context_neg)) + v_neg = self.v(context_neg) + x_neg_out = comfy.ldm.modules.attention.optimized_attention(q_neg, k_neg, v_neg, heads=self.num_heads) + x = torch.cat([x_pos_out, x_neg_out], dim=0) + else: + x = x_pos_out + + return self.o(x) + + +def wan_i2v_crossattn_forward_nag(self, x, context, context_img_len): + r""" + Args: + x(Tensor): Shape [B, L1, C] + context(Tensor): Shape [B, L2, C] + """ + context_img = context[:, :context_img_len] + context = context[:, context_img_len:] + + q_img = self.norm_q(self.q(x)) + k_img = self.norm_k_img(self.k_img(context_img)) + v_img = self.v_img(context_img) + img_x = comfy.ldm.modules.attention.optimized_attention(q_img, k_img, v_img, heads=self.num_heads) + + if context.shape[0] == 2: + x, x_real_negative = torch.chunk(x, 2, dim=0) + context_positive, context_negative = torch.chunk(context, 2, dim=0) + else: + context_positive = context + context_negative = None + + q = self.norm_q(self.q(x)) + + x = normalized_attention_guidance(self, q, context_positive, self.nag_context) + + if context_negative is not None: + q_real_negative = self.norm_q(self.q(x_real_negative)) + k_real_negative = self.norm_k(self.k(context_negative)) + v_real_negative = self.v(context_negative) + x_real_negative = comfy.ldm.modules.attention.optimized_attention(q_real_negative, k_real_negative, v_real_negative, heads=self.num_heads) + x = torch.cat([x, x_real_negative], dim=0) + + # output + x = x + img_x + x = self.o(x) + return x + +class WanCrossAttentionPatch: + def __init__(self, context, nag_scale, nag_alpha, nag_tau, i2v=False, input_type="default"): + self.nag_context = context + self.nag_scale = nag_scale + self.nag_alpha = nag_alpha + self.nag_tau = nag_tau + self.i2v = i2v + self.input_type = input_type + def __get__(self, obj, objtype=None): + # Create bound method with stored parameters + def wrapped_attention(self_module, *args, **kwargs): + self_module.nag_context = self.nag_context + self_module.nag_scale = self.nag_scale + self_module.nag_alpha = self.nag_alpha + self_module.nag_tau = self.nag_tau + self_module.input_type = self.input_type + if self.i2v: + return wan_i2v_crossattn_forward_nag(self_module, *args, **kwargs) + else: + return wan_crossattn_forward_nag(self_module, *args, **kwargs) + return types.MethodType(wrapped_attention, obj) + +class WanVideoNAG: + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "model": ("MODEL",), + "conditioning": ("CONDITIONING",), + "nag_scale": ("FLOAT", {"default": 11.0, "min": 0.0, "max": 100.0, "step": 0.001, "tooltip": "Strength of negative guidance effect"}), + "nag_alpha": ("FLOAT", {"default": 0.25, "min": 0.0, "max": 1.0, "step": 0.001, "tooltip": "Mixing coefficient in that controls the balance between the normalized guided representation and the original positive representation."}), + "nag_tau": ("FLOAT", {"default": 2.5, "min": 0.0, "max": 10.0, "step": 0.001, "tooltip": "Clipping threshold that controls how much the guided attention can deviate from the positive attention."}), + }, + "optional": { + "input_type": (["default", "batch"], {"tooltip": "Type of the model input"}), + }, + + } + + RETURN_TYPES = ("MODEL",) + RETURN_NAMES = ("model",) + FUNCTION = "patch" + CATEGORY = "KJNodes/experimental" + DESCRIPTION = "https://github.com/ChenDarYen/Normalized-Attention-Guidance" + EXPERIMENTAL = True + + def patch(self, model, conditioning, nag_scale, nag_alpha, nag_tau, input_type="default"): + if nag_scale == 0: + return (model,) + + device = mm.get_torch_device() + dtype = mm.unet_dtype() + + model_clone = model.clone() + + diffusion_model = model_clone.get_model_object("diffusion_model") + + diffusion_model.text_embedding.to(device) + context = diffusion_model.text_embedding(conditioning[0][0].to(device, dtype)) + + type_str = str(type(model.model.model_config).__name__) + i2v = True if "WAN21_I2V" in type_str else False + + for idx, block in enumerate(diffusion_model.blocks): + patched_attn = WanCrossAttentionPatch(context, nag_scale, nag_alpha, nag_tau, i2v, input_type=input_type).__get__(block.cross_attn, block.__class__) + + model_clone.add_object_patch(f"diffusion_model.blocks.{idx}.cross_attn.forward", patched_attn) + + return (model_clone,) + +class SkipLayerGuidanceWanVideo: + @classmethod + def INPUT_TYPES(s): + return {"required": {"model": ("MODEL", ), + "blocks": ("STRING", {"default": "10", "multiline": False}), + "start_percent": ("FLOAT", {"default": 0.2, "min": 0.0, "max": 1.0, "step": 0.001}), + "end_percent": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.001}), + }} + RETURN_TYPES = ("MODEL",) + FUNCTION = "slg" + EXPERIMENTAL = True + DESCRIPTION = "Simplified skip layer guidance that only skips the uncond on selected blocks" + + CATEGORY = "advanced/guidance" + + def slg(self, model, start_percent, end_percent, blocks): + def skip(args, extra_args): + transformer_options = extra_args.get("transformer_options", {}) + original_block = extra_args["original_block"] + + if not transformer_options: + raise ValueError("transformer_options not found in extra_args, currently SkipLayerGuidanceWanVideo only works with TeaCacheKJ") + if start_percent <= transformer_options["current_percent"] <= end_percent: + if args["img"].shape[0] == 2: + prev_img_uncond = args["img"][0].unsqueeze(0) + + new_args = { + "img": args["img"][1].unsqueeze(0), + "txt": args["txt"][1].unsqueeze(0), + "vec": args["vec"][1].unsqueeze(0), + "pe": args["pe"][1].unsqueeze(0) + } + + block_out = original_block(new_args) + + out = { + "img": torch.cat([prev_img_uncond, block_out["img"]], dim=0), + "txt": args["txt"], + "vec": args["vec"], + "pe": args["pe"] + } + else: + if transformer_options.get("cond_or_uncond") == [0]: + out = original_block(args) + else: + out = args + else: + out = original_block(args) + return out + + block_list = [int(x.strip()) for x in blocks.split(",")] + blocks = [int(i) for i in block_list] + logging.info(f"Selected blocks to skip uncond on: {blocks}") + + m = model.clone() + + for b in blocks: + #m.set_model_patch_replace(skip, "dit", "double_block", b) + model_options = m.model_options["transformer_options"].copy() + if "patches_replace" not in model_options: + model_options["patches_replace"] = {} + else: + model_options["patches_replace"] = model_options["patches_replace"].copy() + + if "dit" not in model_options["patches_replace"]: + model_options["patches_replace"]["dit"] = {} + else: + model_options["patches_replace"]["dit"] = model_options["patches_replace"]["dit"].copy() + + block = ("double_block", b) + + model_options["patches_replace"]["dit"][block] = skip + m.model_options["transformer_options"] = model_options + + + return (m, ) + +class CFGZeroStarAndInit: + @classmethod + def INPUT_TYPES(s): + return {"required": { + "model": ("MODEL",), + "use_zero_init": ("BOOLEAN", {"default": True}), + "zero_init_steps": ("INT", {"default": 0, "min": 0, "tooltip": "for zero init, starts from 0 so first step is always zeroed out if use_zero_init enabled"}), + }} + RETURN_TYPES = ("MODEL",) + FUNCTION = "patch" + DESCRIPTION = "https://github.com/WeichenFan/CFG-Zero-star" + CATEGORY = "KJNodes/experimental" + EXPERIMENTAL = True + + def patch(self, model, use_zero_init, zero_init_steps): + def cfg_zerostar(args): + #zero init + cond = args["cond"] + timestep = args["timestep"] + sigmas = args["model_options"]["transformer_options"]["sample_sigmas"] + matched_step_index = (sigmas == timestep[0]).nonzero() + if len(matched_step_index) > 0: + current_step_index = matched_step_index.item() + else: + for i in range(len(sigmas) - 1): + if (sigmas[i] - timestep[0]) * (sigmas[i + 1] - timestep[0]) <= 0: + current_step_index = i + break + else: + current_step_index = 0 + + if (current_step_index <= zero_init_steps) and use_zero_init: + return cond * 0 + + uncond = args["uncond"] + cond_scale = args["cond_scale"] + + batch_size = cond.shape[0] + + positive_flat = cond.view(batch_size, -1) + negative_flat = uncond.view(batch_size, -1) + + dot_product = torch.sum(positive_flat * negative_flat, dim=1, keepdim=True) + squared_norm = torch.sum(negative_flat ** 2, dim=1, keepdim=True) + 1e-8 + alpha = dot_product / squared_norm + alpha = alpha.view(batch_size, *([1] * (len(cond.shape) - 1))) + + noise_pred = uncond * alpha + cond_scale * (cond - uncond * alpha) + return noise_pred + + m = model.clone() + m.set_model_sampler_cfg_function(cfg_zerostar) + return (m, ) \ No newline at end of file diff --git a/ComfyUI/custom_nodes/comfyui-kjnodes/nodes/nodes.py b/ComfyUI/custom_nodes/comfyui-kjnodes/nodes/nodes.py new file mode 100644 index 0000000000000000000000000000000000000000..afc793f36035c5308891ed9f91887ba5a6a1fba0 --- /dev/null +++ b/ComfyUI/custom_nodes/comfyui-kjnodes/nodes/nodes.py @@ -0,0 +1,2625 @@ +import torch +import torch.nn as nn +import numpy as np +from PIL import Image +import json, re, os, io, time +import re +import importlib + +from comfy import model_management +import folder_paths +from nodes import MAX_RESOLUTION +from comfy.utils import common_upscale, ProgressBar, load_torch_file +from comfy.comfy_types.node_typing import IO + +script_directory = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +folder_paths.add_model_folder_path("kjnodes_fonts", os.path.join(script_directory, "fonts")) + +class BOOLConstant: + @classmethod + def INPUT_TYPES(s): + return {"required": { + "value": ("BOOLEAN", {"default": True}), + }, + } + RETURN_TYPES = ("BOOLEAN",) + RETURN_NAMES = ("value",) + FUNCTION = "get_value" + CATEGORY = "KJNodes/constants" + + def get_value(self, value): + return (value,) + +class INTConstant: + @classmethod + def INPUT_TYPES(s): + return {"required": { + "value": ("INT", {"default": 0, "min": -0xffffffffffffffff, "max": 0xffffffffffffffff}), + }, + } + RETURN_TYPES = ("INT",) + RETURN_NAMES = ("value",) + FUNCTION = "get_value" + CATEGORY = "KJNodes/constants" + + def get_value(self, value): + return (value,) + +class FloatConstant: + @classmethod + def INPUT_TYPES(s): + return {"required": { + "value": ("FLOAT", {"default": 0.0, "min": -0xffffffffffffffff, "max": 0xffffffffffffffff, "step": 0.00001}), + }, + } + + RETURN_TYPES = ("FLOAT",) + RETURN_NAMES = ("value",) + FUNCTION = "get_value" + CATEGORY = "KJNodes/constants" + + def get_value(self, value): + return (round(value, 6),) + +class StringConstant: + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "string": ("STRING", {"default": '', "multiline": False}), + } + } + RETURN_TYPES = ("STRING",) + FUNCTION = "passtring" + CATEGORY = "KJNodes/constants" + + def passtring(self, string): + return (string, ) + +class StringConstantMultiline: + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "string": ("STRING", {"default": "", "multiline": True}), + "strip_newlines": ("BOOLEAN", {"default": True}), + } + } + RETURN_TYPES = ("STRING",) + FUNCTION = "stringify" + CATEGORY = "KJNodes/constants" + + def stringify(self, string, strip_newlines): + new_string = [] + for line in io.StringIO(string): + if not line.strip().startswith("\n") and strip_newlines: + line = line.replace("\n", '') + new_string.append(line) + new_string = "\n".join(new_string) + + return (new_string, ) + + + +class ScaleBatchPromptSchedule: + + RETURN_TYPES = ("STRING",) + FUNCTION = "scaleschedule" + CATEGORY = "KJNodes/misc" + DESCRIPTION = """ +Scales a batch schedule from Fizz' nodes BatchPromptSchedule +to a different frame count. +""" + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "input_str": ("STRING", {"forceInput": True,"default": "0:(0.0),\n7:(1.0),\n15:(0.0)\n"}), + "old_frame_count": ("INT", {"forceInput": True,"default": 1,"min": 1, "max": 4096, "step": 1}), + "new_frame_count": ("INT", {"forceInput": True,"default": 1,"min": 1, "max": 4096, "step": 1}), + + }, + } + + def scaleschedule(self, old_frame_count, input_str, new_frame_count): + pattern = r'"(\d+)"\s*:\s*"(.*?)"(?:,|\Z)' + frame_strings = dict(re.findall(pattern, input_str)) + + # Calculate the scaling factor + scaling_factor = (new_frame_count - 1) / (old_frame_count - 1) + + # Initialize a dictionary to store the new frame numbers and strings + new_frame_strings = {} + + # Iterate over the frame numbers and strings + for old_frame, string in frame_strings.items(): + # Calculate the new frame number + new_frame = int(round(int(old_frame) * scaling_factor)) + + # Store the new frame number and corresponding string + new_frame_strings[new_frame] = string + + # Format the output string + output_str = ', '.join([f'"{k}":"{v}"' for k, v in sorted(new_frame_strings.items())]) + return (output_str,) + + +class GetLatentsFromBatchIndexed: + + RETURN_TYPES = ("LATENT",) + FUNCTION = "indexedlatentsfrombatch" + CATEGORY = "KJNodes/latents" + DESCRIPTION = """ +Selects and returns the latents at the specified indices as an latent batch. +""" + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "latents": ("LATENT",), + "indexes": ("STRING", {"default": "0, 1, 2", "multiline": True}), + "latent_format": (["BCHW", "BTCHW", "BCTHW"], {"default": "BCHW"}), + }, + } + + def indexedlatentsfrombatch(self, latents, indexes, latent_format): + + samples = latents.copy() + latent_samples = samples["samples"] + + # Parse the indexes string into a list of integers + index_list = [int(index.strip()) for index in indexes.split(',')] + + # Convert list of indices to a PyTorch tensor + indices_tensor = torch.tensor(index_list, dtype=torch.long) + + # Select the latents at the specified indices + if latent_format == "BCHW": + chosen_latents = latent_samples[indices_tensor] + elif latent_format == "BTCHW": + chosen_latents = latent_samples[:, indices_tensor] + elif latent_format == "BCTHW": + chosen_latents = latent_samples[:, :, indices_tensor] + + samples["samples"] = chosen_latents + return (samples,) + + +class ConditioningMultiCombine: + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "inputcount": ("INT", {"default": 2, "min": 2, "max": 20, "step": 1}), + "operation": (["combine", "concat"], {"default": "combine"}), + "conditioning_1": ("CONDITIONING", ), + "conditioning_2": ("CONDITIONING", ), + }, + } + + RETURN_TYPES = ("CONDITIONING", "INT") + RETURN_NAMES = ("combined", "inputcount") + FUNCTION = "combine" + CATEGORY = "KJNodes/masking/conditioning" + DESCRIPTION = """ +Combines multiple conditioning nodes into one +""" + + def combine(self, inputcount, operation, **kwargs): + from nodes import ConditioningCombine + from nodes import ConditioningConcat + cond_combine_node = ConditioningCombine() + cond_concat_node = ConditioningConcat() + cond = kwargs["conditioning_1"] + for c in range(1, inputcount): + new_cond = kwargs[f"conditioning_{c + 1}"] + if operation == "combine": + cond = cond_combine_node.combine(new_cond, cond)[0] + elif operation == "concat": + cond = cond_concat_node.concat(cond, new_cond)[0] + return (cond, inputcount,) + +class AppendStringsToList: + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "string1": ("STRING", {"default": '', "forceInput": True}), + "string2": ("STRING", {"default": '', "forceInput": True}), + } + } + RETURN_TYPES = ("STRING",) + FUNCTION = "joinstring" + CATEGORY = "KJNodes/text" + + def joinstring(self, string1, string2): + if not isinstance(string1, list): + string1 = [string1] + if not isinstance(string2, list): + string2 = [string2] + + joined_string = string1 + string2 + return (joined_string, ) + +class JoinStrings: + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "delimiter": ("STRING", {"default": ' ', "multiline": False}), + }, + "optional": { + "string1": ("STRING", {"default": '', "forceInput": True}), + "string2": ("STRING", {"default": '', "forceInput": True}), + } + } + RETURN_TYPES = ("STRING",) + FUNCTION = "joinstring" + CATEGORY = "KJNodes/text" + + def joinstring(self, delimiter, string1="", string2=""): + joined_string = string1 + delimiter + string2 + return (joined_string, ) + +class JoinStringMulti: + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "inputcount": ("INT", {"default": 2, "min": 2, "max": 1000, "step": 1}), + "string_1": ("STRING", {"default": '', "forceInput": True}), + "delimiter": ("STRING", {"default": ' ', "multiline": False}), + "return_list": ("BOOLEAN", {"default": False}), + }, + "optional": { + "string_2": ("STRING", {"default": '', "forceInput": True}), + } + } + + RETURN_TYPES = ("STRING",) + RETURN_NAMES = ("string",) + FUNCTION = "combine" + CATEGORY = "KJNodes/text" + DESCRIPTION = """ +Creates single string, or a list of strings, from +multiple input strings. +You can set how many inputs the node has, +with the **inputcount** and clicking update. +""" + + def combine(self, inputcount, delimiter, **kwargs): + string = kwargs["string_1"] + return_list = kwargs["return_list"] + strings = [string] # Initialize a list with the first string + for c in range(1, inputcount): + new_string = kwargs.get(f"string_{c + 1}", "") + if not new_string: + continue + if return_list: + strings.append(new_string) # Add new string to the list + else: + string = string + delimiter + new_string + if return_list: + return (strings,) # Return the list of strings + else: + return (string,) # Return the combined string + +class CondPassThrough: + @classmethod + def INPUT_TYPES(s): + return { + "required": { + }, + "optional": { + "positive": ("CONDITIONING", ), + "negative": ("CONDITIONING", ), + }, + } + + RETURN_TYPES = ("CONDITIONING", "CONDITIONING",) + RETURN_NAMES = ("positive", "negative") + FUNCTION = "passthrough" + CATEGORY = "KJNodes/misc" + DESCRIPTION = """ + Simply passes through the positive and negative conditioning, + workaround for Set node not allowing bypassed inputs. +""" + + def passthrough(self, positive=None, negative=None): + return (positive, negative,) + +class ModelPassThrough: + @classmethod + def INPUT_TYPES(s): + return { + "required": { + }, + "optional": { + "model": ("MODEL", ), + }, + } + + RETURN_TYPES = ("MODEL", ) + RETURN_NAMES = ("model",) + FUNCTION = "passthrough" + CATEGORY = "KJNodes/misc" + DESCRIPTION = """ + Simply passes through the model, + workaround for Set node not allowing bypassed inputs. +""" + + def passthrough(self, model=None): + return (model,) + +def append_helper(t, mask, c, set_area_to_bounds, strength): + n = [t[0], t[1].copy()] + _, h, w = mask.shape + n[1]['mask'] = mask + n[1]['set_area_to_bounds'] = set_area_to_bounds + n[1]['mask_strength'] = strength + c.append(n) + +class ConditioningSetMaskAndCombine: + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "positive_1": ("CONDITIONING", ), + "negative_1": ("CONDITIONING", ), + "positive_2": ("CONDITIONING", ), + "negative_2": ("CONDITIONING", ), + "mask_1": ("MASK", ), + "mask_2": ("MASK", ), + "mask_1_strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.01}), + "mask_2_strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.01}), + "set_cond_area": (["default", "mask bounds"],), + } + } + + RETURN_TYPES = ("CONDITIONING","CONDITIONING",) + RETURN_NAMES = ("combined_positive", "combined_negative",) + FUNCTION = "append" + CATEGORY = "KJNodes/masking/conditioning" + DESCRIPTION = """ +Bundles multiple conditioning mask and combine nodes into one,functionality is identical to ComfyUI native nodes +""" + + def append(self, positive_1, negative_1, positive_2, negative_2, mask_1, mask_2, set_cond_area, mask_1_strength, mask_2_strength): + c = [] + c2 = [] + set_area_to_bounds = False + if set_cond_area != "default": + set_area_to_bounds = True + if len(mask_1.shape) < 3: + mask_1 = mask_1.unsqueeze(0) + if len(mask_2.shape) < 3: + mask_2 = mask_2.unsqueeze(0) + for t in positive_1: + append_helper(t, mask_1, c, set_area_to_bounds, mask_1_strength) + for t in positive_2: + append_helper(t, mask_2, c, set_area_to_bounds, mask_2_strength) + for t in negative_1: + append_helper(t, mask_1, c2, set_area_to_bounds, mask_1_strength) + for t in negative_2: + append_helper(t, mask_2, c2, set_area_to_bounds, mask_2_strength) + return (c, c2) + +class ConditioningSetMaskAndCombine3: + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "positive_1": ("CONDITIONING", ), + "negative_1": ("CONDITIONING", ), + "positive_2": ("CONDITIONING", ), + "negative_2": ("CONDITIONING", ), + "positive_3": ("CONDITIONING", ), + "negative_3": ("CONDITIONING", ), + "mask_1": ("MASK", ), + "mask_2": ("MASK", ), + "mask_3": ("MASK", ), + "mask_1_strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.01}), + "mask_2_strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.01}), + "mask_3_strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.01}), + "set_cond_area": (["default", "mask bounds"],), + } + } + + RETURN_TYPES = ("CONDITIONING","CONDITIONING",) + RETURN_NAMES = ("combined_positive", "combined_negative",) + FUNCTION = "append" + CATEGORY = "KJNodes/masking/conditioning" + DESCRIPTION = """ +Bundles multiple conditioning mask and combine nodes into one,functionality is identical to ComfyUI native nodes +""" + + def append(self, positive_1, negative_1, positive_2, positive_3, negative_2, negative_3, mask_1, mask_2, mask_3, set_cond_area, mask_1_strength, mask_2_strength, mask_3_strength): + c = [] + c2 = [] + set_area_to_bounds = False + if set_cond_area != "default": + set_area_to_bounds = True + if len(mask_1.shape) < 3: + mask_1 = mask_1.unsqueeze(0) + if len(mask_2.shape) < 3: + mask_2 = mask_2.unsqueeze(0) + if len(mask_3.shape) < 3: + mask_3 = mask_3.unsqueeze(0) + for t in positive_1: + append_helper(t, mask_1, c, set_area_to_bounds, mask_1_strength) + for t in positive_2: + append_helper(t, mask_2, c, set_area_to_bounds, mask_2_strength) + for t in positive_3: + append_helper(t, mask_3, c, set_area_to_bounds, mask_3_strength) + for t in negative_1: + append_helper(t, mask_1, c2, set_area_to_bounds, mask_1_strength) + for t in negative_2: + append_helper(t, mask_2, c2, set_area_to_bounds, mask_2_strength) + for t in negative_3: + append_helper(t, mask_3, c2, set_area_to_bounds, mask_3_strength) + return (c, c2) + +class ConditioningSetMaskAndCombine4: + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "positive_1": ("CONDITIONING", ), + "negative_1": ("CONDITIONING", ), + "positive_2": ("CONDITIONING", ), + "negative_2": ("CONDITIONING", ), + "positive_3": ("CONDITIONING", ), + "negative_3": ("CONDITIONING", ), + "positive_4": ("CONDITIONING", ), + "negative_4": ("CONDITIONING", ), + "mask_1": ("MASK", ), + "mask_2": ("MASK", ), + "mask_3": ("MASK", ), + "mask_4": ("MASK", ), + "mask_1_strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.01}), + "mask_2_strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.01}), + "mask_3_strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.01}), + "mask_4_strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.01}), + "set_cond_area": (["default", "mask bounds"],), + } + } + + RETURN_TYPES = ("CONDITIONING","CONDITIONING",) + RETURN_NAMES = ("combined_positive", "combined_negative",) + FUNCTION = "append" + CATEGORY = "KJNodes/masking/conditioning" + DESCRIPTION = """ +Bundles multiple conditioning mask and combine nodes into one,functionality is identical to ComfyUI native nodes +""" + + def append(self, positive_1, negative_1, positive_2, positive_3, positive_4, negative_2, negative_3, negative_4, mask_1, mask_2, mask_3, mask_4, set_cond_area, mask_1_strength, mask_2_strength, mask_3_strength, mask_4_strength): + c = [] + c2 = [] + set_area_to_bounds = False + if set_cond_area != "default": + set_area_to_bounds = True + if len(mask_1.shape) < 3: + mask_1 = mask_1.unsqueeze(0) + if len(mask_2.shape) < 3: + mask_2 = mask_2.unsqueeze(0) + if len(mask_3.shape) < 3: + mask_3 = mask_3.unsqueeze(0) + if len(mask_4.shape) < 3: + mask_4 = mask_4.unsqueeze(0) + for t in positive_1: + append_helper(t, mask_1, c, set_area_to_bounds, mask_1_strength) + for t in positive_2: + append_helper(t, mask_2, c, set_area_to_bounds, mask_2_strength) + for t in positive_3: + append_helper(t, mask_3, c, set_area_to_bounds, mask_3_strength) + for t in positive_4: + append_helper(t, mask_4, c, set_area_to_bounds, mask_4_strength) + for t in negative_1: + append_helper(t, mask_1, c2, set_area_to_bounds, mask_1_strength) + for t in negative_2: + append_helper(t, mask_2, c2, set_area_to_bounds, mask_2_strength) + for t in negative_3: + append_helper(t, mask_3, c2, set_area_to_bounds, mask_3_strength) + for t in negative_4: + append_helper(t, mask_4, c2, set_area_to_bounds, mask_4_strength) + return (c, c2) + +class ConditioningSetMaskAndCombine5: + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "positive_1": ("CONDITIONING", ), + "negative_1": ("CONDITIONING", ), + "positive_2": ("CONDITIONING", ), + "negative_2": ("CONDITIONING", ), + "positive_3": ("CONDITIONING", ), + "negative_3": ("CONDITIONING", ), + "positive_4": ("CONDITIONING", ), + "negative_4": ("CONDITIONING", ), + "positive_5": ("CONDITIONING", ), + "negative_5": ("CONDITIONING", ), + "mask_1": ("MASK", ), + "mask_2": ("MASK", ), + "mask_3": ("MASK", ), + "mask_4": ("MASK", ), + "mask_5": ("MASK", ), + "mask_1_strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.01}), + "mask_2_strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.01}), + "mask_3_strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.01}), + "mask_4_strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.01}), + "mask_5_strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.01}), + "set_cond_area": (["default", "mask bounds"],), + } + } + + RETURN_TYPES = ("CONDITIONING","CONDITIONING",) + RETURN_NAMES = ("combined_positive", "combined_negative",) + FUNCTION = "append" + CATEGORY = "KJNodes/masking/conditioning" + DESCRIPTION = """ +Bundles multiple conditioning mask and combine nodes into one,functionality is identical to ComfyUI native nodes +""" + + def append(self, positive_1, negative_1, positive_2, positive_3, positive_4, positive_5, negative_2, negative_3, negative_4, negative_5, mask_1, mask_2, mask_3, mask_4, mask_5, set_cond_area, mask_1_strength, mask_2_strength, mask_3_strength, mask_4_strength, mask_5_strength): + c = [] + c2 = [] + set_area_to_bounds = False + if set_cond_area != "default": + set_area_to_bounds = True + if len(mask_1.shape) < 3: + mask_1 = mask_1.unsqueeze(0) + if len(mask_2.shape) < 3: + mask_2 = mask_2.unsqueeze(0) + if len(mask_3.shape) < 3: + mask_3 = mask_3.unsqueeze(0) + if len(mask_4.shape) < 3: + mask_4 = mask_4.unsqueeze(0) + if len(mask_5.shape) < 3: + mask_5 = mask_5.unsqueeze(0) + for t in positive_1: + append_helper(t, mask_1, c, set_area_to_bounds, mask_1_strength) + for t in positive_2: + append_helper(t, mask_2, c, set_area_to_bounds, mask_2_strength) + for t in positive_3: + append_helper(t, mask_3, c, set_area_to_bounds, mask_3_strength) + for t in positive_4: + append_helper(t, mask_4, c, set_area_to_bounds, mask_4_strength) + for t in positive_5: + append_helper(t, mask_5, c, set_area_to_bounds, mask_5_strength) + for t in negative_1: + append_helper(t, mask_1, c2, set_area_to_bounds, mask_1_strength) + for t in negative_2: + append_helper(t, mask_2, c2, set_area_to_bounds, mask_2_strength) + for t in negative_3: + append_helper(t, mask_3, c2, set_area_to_bounds, mask_3_strength) + for t in negative_4: + append_helper(t, mask_4, c2, set_area_to_bounds, mask_4_strength) + for t in negative_5: + append_helper(t, mask_5, c2, set_area_to_bounds, mask_5_strength) + return (c, c2) + +class VRAM_Debug: + + @classmethod + + def INPUT_TYPES(s): + return { + "required": { + + "empty_cache": ("BOOLEAN", {"default": True}), + "gc_collect": ("BOOLEAN", {"default": True}), + "unload_all_models": ("BOOLEAN", {"default": False}), + }, + "optional": { + "any_input": (IO.ANY,), + "image_pass": ("IMAGE",), + "model_pass": ("MODEL",), + } + } + + RETURN_TYPES = (IO.ANY, "IMAGE","MODEL","INT", "INT",) + RETURN_NAMES = ("any_output", "image_pass", "model_pass", "freemem_before", "freemem_after") + FUNCTION = "VRAMdebug" + CATEGORY = "KJNodes/misc" + DESCRIPTION = """ +Returns the inputs unchanged, they are only used as triggers, +and performs comfy model management functions and garbage collection, +reports free VRAM before and after the operations. +""" + + def VRAMdebug(self, gc_collect, empty_cache, unload_all_models, image_pass=None, model_pass=None, any_input=None): + freemem_before = model_management.get_free_memory() + print("VRAMdebug: free memory before: ", f"{freemem_before:,.0f}") + if empty_cache: + model_management.soft_empty_cache() + if unload_all_models: + model_management.unload_all_models() + if gc_collect: + import gc + gc.collect() + freemem_after = model_management.get_free_memory() + print("VRAMdebug: free memory after: ", f"{freemem_after:,.0f}") + print("VRAMdebug: freed memory: ", f"{freemem_after - freemem_before:,.0f}") + return {"ui": { + "text": [f"{freemem_before:,.0f}x{freemem_after:,.0f}"]}, + "result": (any_input, image_pass, model_pass, freemem_before, freemem_after) + } + +class SomethingToString: + @classmethod + + def INPUT_TYPES(s): + return { + "required": { + "input": (IO.ANY, ), + }, + "optional": { + "prefix": ("STRING", {"default": ""}), + "suffix": ("STRING", {"default": ""}), + } + } + RETURN_TYPES = ("STRING",) + FUNCTION = "stringify" + CATEGORY = "KJNodes/text" + DESCRIPTION = """ +Converts any type to a string. +""" + + def stringify(self, input, prefix="", suffix=""): + if isinstance(input, (int, float, bool)): + stringified = str(input) + elif isinstance(input, list): + stringified = ', '.join(str(item) for item in input) + else: + return + if prefix: # Check if prefix is not empty + stringified = prefix + stringified # Add the prefix + if suffix: # Check if suffix is not empty + stringified = stringified + suffix # Add the suffix + + return (stringified,) + +class Sleep: + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "input": (IO.ANY, ), + "minutes": ("INT", {"default": 0, "min": 0, "max": 1439}), + "seconds": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 59.99, "step": 0.01}), + }, + } + RETURN_TYPES = (IO.ANY,) + FUNCTION = "sleepdelay" + CATEGORY = "KJNodes/misc" + DESCRIPTION = """ +Delays the execution for the input amount of time. +""" + + def sleepdelay(self, input, minutes, seconds): + total_seconds = minutes * 60 + seconds + time.sleep(total_seconds) + return input, + +class EmptyLatentImagePresets: + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "dimensions": ( + [ + '512 x 512 (1:1)', + '768 x 512 (1.5:1)', + '960 x 512 (1.875:1)', + '1024 x 512 (2:1)', + '1024 x 576 (1.778:1)', + '1536 x 640 (2.4:1)', + '1344 x 768 (1.75:1)', + '1216 x 832 (1.46:1)', + '1152 x 896 (1.286:1)', + '1024 x 1024 (1:1)', + ], + { + "default": '512 x 512 (1:1)' + }), + + "invert": ("BOOLEAN", {"default": False}), + "batch_size": ("INT", { + "default": 1, + "min": 1, + "max": 4096 + }), + }, + } + + RETURN_TYPES = ("LATENT", "INT", "INT") + RETURN_NAMES = ("Latent", "Width", "Height") + FUNCTION = "generate" + CATEGORY = "KJNodes/latents" + + def generate(self, dimensions, invert, batch_size): + from nodes import EmptyLatentImage + result = [x.strip() for x in dimensions.split('x')] + + # Remove the aspect ratio part + result[0] = result[0].split('(')[0].strip() + result[1] = result[1].split('(')[0].strip() + + if invert: + width = int(result[1].split(' ')[0]) + height = int(result[0]) + else: + width = int(result[0]) + height = int(result[1].split(' ')[0]) + latent = EmptyLatentImage().generate(width, height, batch_size)[0] + + return (latent, int(width), int(height),) + +class EmptyLatentImageCustomPresets: + @classmethod + def INPUT_TYPES(cls): + try: + with open(os.path.join(script_directory, 'custom_dimensions.json')) as f: + dimensions_dict = json.load(f) + except FileNotFoundError: + dimensions_dict = [] + return { + "required": { + "dimensions": ( + [f"{d['label']} - {d['value']}" for d in dimensions_dict], + ), + + "invert": ("BOOLEAN", {"default": False}), + "batch_size": ("INT", { + "default": 1, + "min": 1, + "max": 4096 + }), + }, + } + + RETURN_TYPES = ("LATENT", "INT", "INT") + RETURN_NAMES = ("Latent", "Width", "Height") + FUNCTION = "generate" + CATEGORY = "KJNodes/latents" + DESCRIPTION = """ +Generates an empty latent image with the specified dimensions. +The choices are loaded from 'custom_dimensions.json' in the nodes folder. +""" + + def generate(self, dimensions, invert, batch_size): + from nodes import EmptyLatentImage + # Split the string into label and value + label, value = dimensions.split(' - ') + # Split the value into width and height + width, height = [x.strip() for x in value.split('x')] + + if invert: + width, height = height, width + + latent = EmptyLatentImage().generate(int(width), int(height), batch_size)[0] + + return (latent, int(width), int(height),) + +class WidgetToString: + @classmethod + def IS_CHANGED(cls,*,id,node_title,any_input,**kwargs): + if any_input is not None and (id != 0 or node_title != ""): + return float("NaN") + + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "id": ("INT", {"default": 0, "min": 0, "max": 100000, "step": 1}), + "widget_name": ("STRING", {"multiline": False}), + "return_all": ("BOOLEAN", {"default": False}), + }, + "optional": { + "any_input": (IO.ANY, ), + "node_title": ("STRING", {"multiline": False}), + "allowed_float_decimals": ("INT", {"default": 2, "min": 0, "max": 10, "tooltip": "Number of decimal places to display for float values"}), + + }, + "hidden": {"extra_pnginfo": "EXTRA_PNGINFO", + "prompt": "PROMPT", + "unique_id": "UNIQUE_ID",}, + } + + RETURN_TYPES = ("STRING", ) + FUNCTION = "get_widget_value" + CATEGORY = "KJNodes/text" + DESCRIPTION = """ +Selects a node and it's specified widget and outputs the value as a string. +If no node id or title is provided it will use the 'any_input' link and use that node. +To see node id's, enable node id display from Manager badge menu. +Alternatively you can search with the node title. Node titles ONLY exist if they +are manually edited! +The 'any_input' is required for making sure the node you want the value from exists in the workflow. +""" + + def get_widget_value(self, id, widget_name, extra_pnginfo, prompt, unique_id, return_all=False, any_input=None, node_title="", allowed_float_decimals=2): + workflow = extra_pnginfo["workflow"] + #print(json.dumps(workflow, indent=4)) + results = [] + node_id = None # Initialize node_id to handle cases where no match is found + link_id = None + link_to_node_map = {} + + for node in workflow["nodes"]: + if node_title: + if "title" in node: + if node["title"] == node_title: + node_id = node["id"] + break + else: + print("Node title not found.") + elif id != 0: + if node["id"] == id: + node_id = id + break + elif any_input is not None: + if node["type"] == "WidgetToString" and node["id"] == int(unique_id) and not link_id: + for node_input in node["inputs"]: + if node_input["name"] == "any_input": + link_id = node_input["link"] + + # Construct a map of links to node IDs for future reference + node_outputs = node.get("outputs", None) + if not node_outputs: + continue + for output in node_outputs: + node_links = output.get("links", None) + if not node_links: + continue + for link in node_links: + link_to_node_map[link] = node["id"] + if link_id and link == link_id: + break + + if link_id: + node_id = link_to_node_map.get(link_id, None) + + if node_id is None: + raise ValueError("No matching node found for the given title or id") + + values = prompt[str(node_id)] + if "inputs" in values: + if return_all: + # Format items based on type + formatted_items = [] + for k, v in values["inputs"].items(): + if isinstance(v, float): + item = f"{k}: {v:.{allowed_float_decimals}f}" + else: + item = f"{k}: {str(v)}" + formatted_items.append(item) + results.append(', '.join(formatted_items)) + elif widget_name in values["inputs"]: + v = values["inputs"][widget_name] + if isinstance(v, float): + v = f"{v:.{allowed_float_decimals}f}" + else: + v = str(v) + return (v, ) + else: + raise NameError(f"Widget not found: {node_id}.{widget_name}") + return (', '.join(results).strip(', '), ) + +class DummyOut: + + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "any_input": (IO.ANY, ), + } + } + + RETURN_TYPES = (IO.ANY,) + FUNCTION = "dummy" + CATEGORY = "KJNodes/misc" + OUTPUT_NODE = True + DESCRIPTION = """ +Does nothing, used to trigger generic workflow output. +A way to get previews in the UI without saving anything to disk. +""" + + def dummy(self, any_input): + return (any_input,) + +class FlipSigmasAdjusted: + @classmethod + def INPUT_TYPES(s): + return {"required": + {"sigmas": ("SIGMAS", ), + "divide_by_last_sigma": ("BOOLEAN", {"default": False}), + "divide_by": ("FLOAT", {"default": 1,"min": 1, "max": 255, "step": 0.01}), + "offset_by": ("INT", {"default": 1,"min": -100, "max": 100, "step": 1}), + } + } + RETURN_TYPES = ("SIGMAS", "STRING",) + RETURN_NAMES = ("SIGMAS", "sigmas_string",) + CATEGORY = "KJNodes/noise" + FUNCTION = "get_sigmas_adjusted" + + def get_sigmas_adjusted(self, sigmas, divide_by_last_sigma, divide_by, offset_by): + + sigmas = sigmas.flip(0) + if sigmas[0] == 0: + sigmas[0] = 0.0001 + adjusted_sigmas = sigmas.clone() + #offset sigma + for i in range(1, len(sigmas)): + offset_index = i - offset_by + if 0 <= offset_index < len(sigmas): + adjusted_sigmas[i] = sigmas[offset_index] + else: + adjusted_sigmas[i] = 0.0001 + if adjusted_sigmas[0] == 0: + adjusted_sigmas[0] = 0.0001 + if divide_by_last_sigma: + adjusted_sigmas = adjusted_sigmas / adjusted_sigmas[-1] + + sigma_np_array = adjusted_sigmas.numpy() + array_string = np.array2string(sigma_np_array, precision=2, separator=', ', threshold=np.inf) + adjusted_sigmas = adjusted_sigmas / divide_by + return (adjusted_sigmas, array_string,) + +class CustomSigmas: + @classmethod + def INPUT_TYPES(s): + return {"required": + { + "sigmas_string" :("STRING", {"default": "14.615, 6.475, 3.861, 2.697, 1.886, 1.396, 0.963, 0.652, 0.399, 0.152, 0.029","multiline": True}), + "interpolate_to_steps": ("INT", {"default": 10,"min": 0, "max": 255, "step": 1}), + } + } + RETURN_TYPES = ("SIGMAS",) + RETURN_NAMES = ("SIGMAS",) + CATEGORY = "KJNodes/noise" + FUNCTION = "customsigmas" + DESCRIPTION = """ +Creates a sigmas tensor from a string of comma separated values. +Examples: + +Nvidia's optimized AYS 10 step schedule for SD 1.5: +14.615, 6.475, 3.861, 2.697, 1.886, 1.396, 0.963, 0.652, 0.399, 0.152, 0.029 +SDXL: +14.615, 6.315, 3.771, 2.181, 1.342, 0.862, 0.555, 0.380, 0.234, 0.113, 0.029 +SVD: +700.00, 54.5, 15.886, 7.977, 4.248, 1.789, 0.981, 0.403, 0.173, 0.034, 0.002 +""" + def customsigmas(self, sigmas_string, interpolate_to_steps): + sigmas_list = sigmas_string.split(', ') + sigmas_float_list = [float(sigma) for sigma in sigmas_list] + sigmas_tensor = torch.FloatTensor(sigmas_float_list) + if len(sigmas_tensor) != interpolate_to_steps + 1: + sigmas_tensor = self.loglinear_interp(sigmas_tensor, interpolate_to_steps + 1) + sigmas_tensor[-1] = 0 + return (sigmas_tensor.float(),) + + def loglinear_interp(self, t_steps, num_steps): + """ + Performs log-linear interpolation of a given array of decreasing numbers. + """ + t_steps_np = t_steps.numpy() + + xs = np.linspace(0, 1, len(t_steps_np)) + ys = np.log(t_steps_np[::-1]) + + new_xs = np.linspace(0, 1, num_steps) + new_ys = np.interp(new_xs, xs, ys) + + interped_ys = np.exp(new_ys)[::-1].copy() + interped_ys_tensor = torch.tensor(interped_ys) + return interped_ys_tensor + +class StringToFloatList: + @classmethod + def INPUT_TYPES(s): + return {"required": + { + "string" :("STRING", {"default": "1, 2, 3", "multiline": True}), + } + } + RETURN_TYPES = ("FLOAT",) + RETURN_NAMES = ("FLOAT",) + CATEGORY = "KJNodes/misc" + FUNCTION = "createlist" + + def createlist(self, string): + float_list = [float(x.strip()) for x in string.split(',')] + return (float_list,) + + +class InjectNoiseToLatent: + @classmethod + def INPUT_TYPES(s): + return {"required": { + "latents":("LATENT",), + "strength": ("FLOAT", {"default": 0.1, "min": 0.0, "max": 200.0, "step": 0.0001}), + "noise": ("LATENT",), + "normalize": ("BOOLEAN", {"default": False}), + "average": ("BOOLEAN", {"default": False}), + }, + "optional":{ + "mask": ("MASK", ), + "mix_randn_amount": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1000.0, "step": 0.001}), + "seed": ("INT", {"default": 123,"min": 0, "max": 0xffffffffffffffff, "step": 1}), + } + } + + RETURN_TYPES = ("LATENT",) + FUNCTION = "injectnoise" + CATEGORY = "KJNodes/noise" + + def injectnoise(self, latents, strength, noise, normalize, average, mix_randn_amount=0, seed=None, mask=None): + samples = latents["samples"].clone().cpu() + noise = noise["samples"].clone().cpu() + if samples.shape != samples.shape: + raise ValueError("InjectNoiseToLatent: Latent and noise must have the same shape") + if average: + noised = (samples + noise) / 2 + else: + noised = samples + noise * strength + if normalize: + noised = noised / noised.std() + if mask is not None: + mask = torch.nn.functional.interpolate(mask.reshape((-1, 1, mask.shape[-2], mask.shape[-1])), size=(noised.shape[2], noised.shape[3]), mode="bilinear") + mask = mask.expand((-1,noised.shape[1],-1,-1)) + if mask.shape[0] < noised.shape[0]: + mask = mask.repeat((noised.shape[0] -1) // mask.shape[0] + 1, 1, 1, 1)[:noised.shape[0]] + noised = mask * noised + (1-mask) * samples + if mix_randn_amount > 0: + if seed is not None: + generator = torch.manual_seed(seed) + rand_noise = torch.randn(noised.size(), dtype=noised.dtype, layout=noised.layout, generator=generator, device="cpu") + noised = noised + (mix_randn_amount * rand_noise) + + return ({"samples":noised},) + +class SoundReactive: + @classmethod + def INPUT_TYPES(s): + return {"required": { + "sound_level": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 99999, "step": 0.01}), + "start_range_hz": ("INT", {"default": 150, "min": 0, "max": 9999, "step": 1}), + "end_range_hz": ("INT", {"default": 2000, "min": 0, "max": 9999, "step": 1}), + "multiplier": ("FLOAT", {"default": 1.0, "min": 0.01, "max": 99999, "step": 0.01}), + "smoothing_factor": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 1.0, "step": 0.01}), + "normalize": ("BOOLEAN", {"default": False}), + }, + } + + RETURN_TYPES = ("FLOAT","INT",) + RETURN_NAMES =("sound_level", "sound_level_int",) + FUNCTION = "react" + CATEGORY = "KJNodes/audio" + DESCRIPTION = """ +Reacts to the sound level of the input. +Uses your browsers sound input options and requires. +Meant to be used with realtime diffusion with autoqueue. +""" + + def react(self, sound_level, start_range_hz, end_range_hz, smoothing_factor, multiplier, normalize): + + sound_level *= multiplier + + if normalize: + sound_level /= 255 + + sound_level_int = int(sound_level) + return (sound_level, sound_level_int, ) + +class GenerateNoise: + @classmethod + def INPUT_TYPES(s): + return {"required": { + "width": ("INT", {"default": 512,"min": 16, "max": 4096, "step": 1}), + "height": ("INT", {"default": 512,"min": 16, "max": 4096, "step": 1}), + "batch_size": ("INT", {"default": 1, "min": 1, "max": 4096}), + "seed": ("INT", {"default": 123,"min": 0, "max": 0xffffffffffffffff, "step": 1}), + "multiplier": ("FLOAT", {"default": 1.0,"min": 0.0, "max": 4096, "step": 0.01}), + "constant_batch_noise": ("BOOLEAN", {"default": False}), + "normalize": ("BOOLEAN", {"default": False}), + }, + "optional": { + "model": ("MODEL", ), + "sigmas": ("SIGMAS", ), + "latent_channels": (['4', '16', ],), + "shape": (["BCHW", "BCTHW","BTCHW",],), + } + } + + RETURN_TYPES = ("LATENT",) + FUNCTION = "generatenoise" + CATEGORY = "KJNodes/noise" + DESCRIPTION = """ +Generates noise for injection or to be used as empty latents on samplers with add_noise off. +""" + + def generatenoise(self, batch_size, width, height, seed, multiplier, constant_batch_noise, normalize, sigmas=None, model=None, latent_channels=4, shape="BCHW"): + + generator = torch.manual_seed(seed) + if shape == "BCHW": + noise = torch.randn([batch_size, int(latent_channels), height // 8, width // 8], dtype=torch.float32, layout=torch.strided, generator=generator, device="cpu") + elif shape == "BCTHW": + noise = torch.randn([1, int(latent_channels), batch_size,height // 8, width // 8], dtype=torch.float32, layout=torch.strided, generator=generator, device="cpu") + elif shape == "BTCHW": + noise = torch.randn([1, batch_size, int(latent_channels), height // 8, width // 8], dtype=torch.float32, layout=torch.strided, generator=generator, device="cpu") + if sigmas is not None: + sigma = sigmas[0] - sigmas[-1] + sigma /= model.model.latent_format.scale_factor + noise *= sigma + + noise *=multiplier + + if normalize: + noise = noise / noise.std() + if constant_batch_noise: + noise = noise[0].repeat(batch_size, 1, 1, 1) + + + return ({"samples":noise}, ) + +def camera_embeddings(elevation, azimuth): + elevation = torch.as_tensor([elevation]) + azimuth = torch.as_tensor([azimuth]) + embeddings = torch.stack( + [ + torch.deg2rad( + (90 - elevation) - (90) + ), # Zero123 polar is 90-elevation + torch.sin(torch.deg2rad(azimuth)), + torch.cos(torch.deg2rad(azimuth)), + torch.deg2rad( + 90 - torch.full_like(elevation, 0) + ), + ], dim=-1).unsqueeze(1) + + return embeddings + +def interpolate_angle(start, end, fraction): + # Calculate the difference in angles and adjust for wraparound if necessary + diff = (end - start + 540) % 360 - 180 + # Apply fraction to the difference + interpolated = start + fraction * diff + # Normalize the result to be within the range of -180 to 180 + return (interpolated + 180) % 360 - 180 + + +class StableZero123_BatchSchedule: + @classmethod + def INPUT_TYPES(s): + return {"required": { "clip_vision": ("CLIP_VISION",), + "init_image": ("IMAGE",), + "vae": ("VAE",), + "width": ("INT", {"default": 256, "min": 16, "max": MAX_RESOLUTION, "step": 8}), + "height": ("INT", {"default": 256, "min": 16, "max": MAX_RESOLUTION, "step": 8}), + "batch_size": ("INT", {"default": 1, "min": 1, "max": 4096}), + "interpolation": (["linear", "ease_in", "ease_out", "ease_in_out"],), + "azimuth_points_string": ("STRING", {"default": "0:(0.0),\n7:(1.0),\n15:(0.0)\n", "multiline": True}), + "elevation_points_string": ("STRING", {"default": "0:(0.0),\n7:(0.0),\n15:(0.0)\n", "multiline": True}), + }} + + RETURN_TYPES = ("CONDITIONING", "CONDITIONING", "LATENT") + RETURN_NAMES = ("positive", "negative", "latent") + FUNCTION = "encode" + CATEGORY = "KJNodes/experimental" + + def encode(self, clip_vision, init_image, vae, width, height, batch_size, azimuth_points_string, elevation_points_string, interpolation): + output = clip_vision.encode_image(init_image) + pooled = output.image_embeds.unsqueeze(0) + pixels = common_upscale(init_image.movedim(-1,1), width, height, "bilinear", "center").movedim(1,-1) + encode_pixels = pixels[:,:,:,:3] + t = vae.encode(encode_pixels) + + def ease_in(t): + return t * t + def ease_out(t): + return 1 - (1 - t) * (1 - t) + def ease_in_out(t): + return 3 * t * t - 2 * t * t * t + + # Parse the azimuth input string into a list of tuples + azimuth_points = [] + azimuth_points_string = azimuth_points_string.rstrip(',\n') + for point_str in azimuth_points_string.split(','): + frame_str, azimuth_str = point_str.split(':') + frame = int(frame_str.strip()) + azimuth = float(azimuth_str.strip()[1:-1]) + azimuth_points.append((frame, azimuth)) + # Sort the points by frame number + azimuth_points.sort(key=lambda x: x[0]) + + # Parse the elevation input string into a list of tuples + elevation_points = [] + elevation_points_string = elevation_points_string.rstrip(',\n') + for point_str in elevation_points_string.split(','): + frame_str, elevation_str = point_str.split(':') + frame = int(frame_str.strip()) + elevation_val = float(elevation_str.strip()[1:-1]) + elevation_points.append((frame, elevation_val)) + # Sort the points by frame number + elevation_points.sort(key=lambda x: x[0]) + + # Index of the next point to interpolate towards + next_point = 1 + next_elevation_point = 1 + + positive_cond_out = [] + positive_pooled_out = [] + negative_cond_out = [] + negative_pooled_out = [] + + #azimuth interpolation + for i in range(batch_size): + # Find the interpolated azimuth for the current frame + while next_point < len(azimuth_points) and i >= azimuth_points[next_point][0]: + next_point += 1 + # If next_point is equal to the length of points, we've gone past the last point + if next_point == len(azimuth_points): + next_point -= 1 # Set next_point to the last index of points + prev_point = max(next_point - 1, 0) # Ensure prev_point is not less than 0 + + # Calculate fraction + if azimuth_points[next_point][0] != azimuth_points[prev_point][0]: # Prevent division by zero + fraction = (i - azimuth_points[prev_point][0]) / (azimuth_points[next_point][0] - azimuth_points[prev_point][0]) + if interpolation == "ease_in": + fraction = ease_in(fraction) + elif interpolation == "ease_out": + fraction = ease_out(fraction) + elif interpolation == "ease_in_out": + fraction = ease_in_out(fraction) + + # Use the new interpolate_angle function + interpolated_azimuth = interpolate_angle(azimuth_points[prev_point][1], azimuth_points[next_point][1], fraction) + else: + interpolated_azimuth = azimuth_points[prev_point][1] + # Interpolate the elevation + next_elevation_point = 1 + while next_elevation_point < len(elevation_points) and i >= elevation_points[next_elevation_point][0]: + next_elevation_point += 1 + if next_elevation_point == len(elevation_points): + next_elevation_point -= 1 + prev_elevation_point = max(next_elevation_point - 1, 0) + + if elevation_points[next_elevation_point][0] != elevation_points[prev_elevation_point][0]: + fraction = (i - elevation_points[prev_elevation_point][0]) / (elevation_points[next_elevation_point][0] - elevation_points[prev_elevation_point][0]) + if interpolation == "ease_in": + fraction = ease_in(fraction) + elif interpolation == "ease_out": + fraction = ease_out(fraction) + elif interpolation == "ease_in_out": + fraction = ease_in_out(fraction) + + interpolated_elevation = interpolate_angle(elevation_points[prev_elevation_point][1], elevation_points[next_elevation_point][1], fraction) + else: + interpolated_elevation = elevation_points[prev_elevation_point][1] + + cam_embeds = camera_embeddings(interpolated_elevation, interpolated_azimuth) + cond = torch.cat([pooled, cam_embeds.repeat((pooled.shape[0], 1, 1))], dim=-1) + + positive_pooled_out.append(t) + positive_cond_out.append(cond) + negative_pooled_out.append(torch.zeros_like(t)) + negative_cond_out.append(torch.zeros_like(pooled)) + + # Concatenate the conditions and pooled outputs + final_positive_cond = torch.cat(positive_cond_out, dim=0) + final_positive_pooled = torch.cat(positive_pooled_out, dim=0) + final_negative_cond = torch.cat(negative_cond_out, dim=0) + final_negative_pooled = torch.cat(negative_pooled_out, dim=0) + + # Structure the final output + final_positive = [[final_positive_cond, {"concat_latent_image": final_positive_pooled}]] + final_negative = [[final_negative_cond, {"concat_latent_image": final_negative_pooled}]] + + latent = torch.zeros([batch_size, 4, height // 8, width // 8]) + return (final_positive, final_negative, {"samples": latent}) + +def linear_interpolate(start, end, fraction): + return start + (end - start) * fraction + +class SV3D_BatchSchedule: + @classmethod + def INPUT_TYPES(s): + return {"required": { "clip_vision": ("CLIP_VISION",), + "init_image": ("IMAGE",), + "vae": ("VAE",), + "width": ("INT", {"default": 576, "min": 16, "max": MAX_RESOLUTION, "step": 8}), + "height": ("INT", {"default": 576, "min": 16, "max": MAX_RESOLUTION, "step": 8}), + "batch_size": ("INT", {"default": 21, "min": 1, "max": 4096}), + "interpolation": (["linear", "ease_in", "ease_out", "ease_in_out"],), + "azimuth_points_string": ("STRING", {"default": "0:(0.0),\n9:(180.0),\n20:(360.0)\n", "multiline": True}), + "elevation_points_string": ("STRING", {"default": "0:(0.0),\n9:(0.0),\n20:(0.0)\n", "multiline": True}), + }} + + RETURN_TYPES = ("CONDITIONING", "CONDITIONING", "LATENT") + RETURN_NAMES = ("positive", "negative", "latent") + FUNCTION = "encode" + CATEGORY = "KJNodes/experimental" + DESCRIPTION = """ +Allow scheduling of the azimuth and elevation conditions for SV3D. +Note that SV3D is still a video model and the schedule needs to always go forward +https://huggingface.co/stabilityai/sv3d +""" + + def encode(self, clip_vision, init_image, vae, width, height, batch_size, azimuth_points_string, elevation_points_string, interpolation): + output = clip_vision.encode_image(init_image) + pooled = output.image_embeds.unsqueeze(0) + pixels = common_upscale(init_image.movedim(-1,1), width, height, "bilinear", "center").movedim(1,-1) + encode_pixels = pixels[:,:,:,:3] + t = vae.encode(encode_pixels) + + def ease_in(t): + return t * t + def ease_out(t): + return 1 - (1 - t) * (1 - t) + def ease_in_out(t): + return 3 * t * t - 2 * t * t * t + + # Parse the azimuth input string into a list of tuples + azimuth_points = [] + azimuth_points_string = azimuth_points_string.rstrip(',\n') + for point_str in azimuth_points_string.split(','): + frame_str, azimuth_str = point_str.split(':') + frame = int(frame_str.strip()) + azimuth = float(azimuth_str.strip()[1:-1]) + azimuth_points.append((frame, azimuth)) + # Sort the points by frame number + azimuth_points.sort(key=lambda x: x[0]) + + # Parse the elevation input string into a list of tuples + elevation_points = [] + elevation_points_string = elevation_points_string.rstrip(',\n') + for point_str in elevation_points_string.split(','): + frame_str, elevation_str = point_str.split(':') + frame = int(frame_str.strip()) + elevation_val = float(elevation_str.strip()[1:-1]) + elevation_points.append((frame, elevation_val)) + # Sort the points by frame number + elevation_points.sort(key=lambda x: x[0]) + + # Index of the next point to interpolate towards + next_point = 1 + next_elevation_point = 1 + elevations = [] + azimuths = [] + # For azimuth interpolation + for i in range(batch_size): + # Find the interpolated azimuth for the current frame + while next_point < len(azimuth_points) and i >= azimuth_points[next_point][0]: + next_point += 1 + if next_point == len(azimuth_points): + next_point -= 1 + prev_point = max(next_point - 1, 0) + + if azimuth_points[next_point][0] != azimuth_points[prev_point][0]: + fraction = (i - azimuth_points[prev_point][0]) / (azimuth_points[next_point][0] - azimuth_points[prev_point][0]) + # Apply the ease function to the fraction + if interpolation == "ease_in": + fraction = ease_in(fraction) + elif interpolation == "ease_out": + fraction = ease_out(fraction) + elif interpolation == "ease_in_out": + fraction = ease_in_out(fraction) + + interpolated_azimuth = linear_interpolate(azimuth_points[prev_point][1], azimuth_points[next_point][1], fraction) + else: + interpolated_azimuth = azimuth_points[prev_point][1] + + # Interpolate the elevation + next_elevation_point = 1 + while next_elevation_point < len(elevation_points) and i >= elevation_points[next_elevation_point][0]: + next_elevation_point += 1 + if next_elevation_point == len(elevation_points): + next_elevation_point -= 1 + prev_elevation_point = max(next_elevation_point - 1, 0) + + if elevation_points[next_elevation_point][0] != elevation_points[prev_elevation_point][0]: + fraction = (i - elevation_points[prev_elevation_point][0]) / (elevation_points[next_elevation_point][0] - elevation_points[prev_elevation_point][0]) + # Apply the ease function to the fraction + if interpolation == "ease_in": + fraction = ease_in(fraction) + elif interpolation == "ease_out": + fraction = ease_out(fraction) + elif interpolation == "ease_in_out": + fraction = ease_in_out(fraction) + + interpolated_elevation = linear_interpolate(elevation_points[prev_elevation_point][1], elevation_points[next_elevation_point][1], fraction) + else: + interpolated_elevation = elevation_points[prev_elevation_point][1] + + azimuths.append(interpolated_azimuth) + elevations.append(interpolated_elevation) + + #print("azimuths", azimuths) + #print("elevations", elevations) + + # Structure the final output + final_positive = [[pooled, {"concat_latent_image": t, "elevation": elevations, "azimuth": azimuths}]] + final_negative = [[torch.zeros_like(pooled), {"concat_latent_image": torch.zeros_like(t),"elevation": elevations, "azimuth": azimuths}]] + + latent = torch.zeros([batch_size, 4, height // 8, width // 8]) + return (final_positive, final_negative, {"samples": latent}) + +class LoadResAdapterNormalization: + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "model": ("MODEL",), + "resadapter_path": (folder_paths.get_filename_list("checkpoints"), ) + } + } + + RETURN_TYPES = ("MODEL",) + FUNCTION = "load_res_adapter" + CATEGORY = "KJNodes/experimental" + + def load_res_adapter(self, model, resadapter_path): + print("ResAdapter: Checking ResAdapter path") + resadapter_full_path = folder_paths.get_full_path("checkpoints", resadapter_path) + if not os.path.exists(resadapter_full_path): + raise Exception("Invalid model path") + else: + print("ResAdapter: Loading ResAdapter normalization weights") + from comfy.utils import load_torch_file + prefix_to_remove = 'diffusion_model.' + model_clone = model.clone() + norm_state_dict = load_torch_file(resadapter_full_path) + new_values = {key[len(prefix_to_remove):]: value for key, value in norm_state_dict.items() if key.startswith(prefix_to_remove)} + print("ResAdapter: Attempting to add patches with ResAdapter weights") + try: + for key in model.model.diffusion_model.state_dict().keys(): + if key in new_values: + original_tensor = model.model.diffusion_model.state_dict()[key] + new_tensor = new_values[key].to(model.model.diffusion_model.dtype) + if original_tensor.shape == new_tensor.shape: + model_clone.add_object_patch(f"diffusion_model.{key}.data", new_tensor) + else: + print("ResAdapter: No match for key: ",key) + except: + raise Exception("Could not patch model, this way of patching was added to ComfyUI on March 3rd 2024, is your ComfyUI up to date?") + print("ResAdapter: Added resnet normalization patches") + return (model_clone, ) + +class Superprompt: + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "instruction_prompt": ("STRING", {"default": 'Expand the following prompt to add more detail', "multiline": True}), + "prompt": ("STRING", {"default": '', "multiline": True, "forceInput": True}), + "max_new_tokens": ("INT", {"default": 128, "min": 1, "max": 4096, "step": 1}), + } + } + + RETURN_TYPES = ("STRING",) + FUNCTION = "process" + CATEGORY = "KJNodes/text" + DESCRIPTION = """ +# SuperPrompt +A T5 model fine-tuned on the SuperPrompt dataset for +upsampling text prompts to more detailed descriptions. +Meant to be used as a pre-generation step for text-to-image +models that benefit from more detailed prompts. +https://huggingface.co/roborovski/superprompt-v1 +""" + + def process(self, instruction_prompt, prompt, max_new_tokens): + device = model_management.get_torch_device() + from transformers import T5Tokenizer, T5ForConditionalGeneration + + checkpoint_path = os.path.join(script_directory, "models","superprompt-v1") + if not os.path.exists(checkpoint_path): + print(f"Downloading model to: {checkpoint_path}") + from huggingface_hub import snapshot_download + snapshot_download(repo_id="roborovski/superprompt-v1", + local_dir=checkpoint_path, + local_dir_use_symlinks=False) + tokenizer = T5Tokenizer.from_pretrained("google/flan-t5-small", legacy=False) + + model = T5ForConditionalGeneration.from_pretrained(checkpoint_path, device_map=device) + model.to(device) + input_text = instruction_prompt + ": " + prompt + + input_ids = tokenizer(input_text, return_tensors="pt").input_ids.to(device) + outputs = model.generate(input_ids, max_new_tokens=max_new_tokens) + out = (tokenizer.decode(outputs[0])) + out = out.replace('', '') + out = out.replace('', '') + + return (out, ) + + +class CameraPoseVisualizer: + + @classmethod + def INPUT_TYPES(s): + return {"required": { + "pose_file_path": ("STRING", {"default": '', "multiline": False}), + "base_xval": ("FLOAT", {"default": 0.2,"min": 0, "max": 100, "step": 0.01}), + "zval": ("FLOAT", {"default": 0.3,"min": 0, "max": 100, "step": 0.01}), + "scale": ("FLOAT", {"default": 1.0,"min": 0.01, "max": 10.0, "step": 0.01}), + "use_exact_fx": ("BOOLEAN", {"default": False}), + "relative_c2w": ("BOOLEAN", {"default": True}), + "use_viewer": ("BOOLEAN", {"default": False}), + }, + "optional": { + "cameractrl_poses": ("CAMERACTRL_POSES", {"default": None}), + } + } + + RETURN_TYPES = ("IMAGE",) + FUNCTION = "plot" + CATEGORY = "KJNodes/misc" + DESCRIPTION = """ +Visualizes the camera poses, from Animatediff-Evolved CameraCtrl Pose +or a .txt file with RealEstate camera intrinsics and coordinates, in a 3D plot. +""" + + def plot(self, pose_file_path, scale, base_xval, zval, use_exact_fx, relative_c2w, use_viewer, cameractrl_poses=None): + import matplotlib as mpl + import matplotlib.pyplot as plt + from torchvision.transforms import ToTensor + + x_min = -2.0 * scale + x_max = 2.0 * scale + y_min = -2.0 * scale + y_max = 2.0 * scale + z_min = -2.0 * scale + z_max = 2.0 * scale + plt.rcParams['text.color'] = '#999999' + self.fig = plt.figure(figsize=(18, 7)) + self.fig.patch.set_facecolor('#353535') + self.ax = self.fig.add_subplot(projection='3d') + self.ax.set_facecolor('#353535') # Set the background color here + self.ax.grid(color='#999999', linestyle='-', linewidth=0.5) + self.plotly_data = None # plotly data traces + self.ax.set_aspect("auto") + self.ax.set_xlim(x_min, x_max) + self.ax.set_ylim(y_min, y_max) + self.ax.set_zlim(z_min, z_max) + self.ax.set_xlabel('x', color='#999999') + self.ax.set_ylabel('y', color='#999999') + self.ax.set_zlabel('z', color='#999999') + for text in self.ax.get_xticklabels() + self.ax.get_yticklabels() + self.ax.get_zticklabels(): + text.set_color('#999999') + print('initialize camera pose visualizer') + + if pose_file_path != "": + with open(pose_file_path, 'r') as f: + poses = f.readlines() + w2cs = [np.asarray([float(p) for p in pose.strip().split(' ')[7:]]).reshape(3, 4) for pose in poses[1:]] + fxs = [float(pose.strip().split(' ')[1]) for pose in poses[1:]] + #print(poses) + elif cameractrl_poses is not None: + poses = cameractrl_poses + w2cs = [np.array(pose[7:]).reshape(3, 4) for pose in cameractrl_poses] + fxs = [pose[1] for pose in cameractrl_poses] + else: + raise ValueError("Please provide either pose_file_path or cameractrl_poses") + + total_frames = len(w2cs) + transform_matrix = np.asarray([[1, 0, 0, 0], [0, 0, 1, 0], [0, -1, 0, 0], [0, 0, 0, 1]]).reshape(4, 4) + last_row = np.zeros((1, 4)) + last_row[0, -1] = 1.0 + + w2cs = [np.concatenate((w2c, last_row), axis=0) for w2c in w2cs] + c2ws = self.get_c2w(w2cs, transform_matrix, relative_c2w) + + for frame_idx, c2w in enumerate(c2ws): + self.extrinsic2pyramid(c2w, frame_idx / total_frames, hw_ratio=1/1, base_xval=base_xval, + zval=(fxs[frame_idx] if use_exact_fx else zval)) + + # Create the colorbar + cmap = mpl.cm.rainbow + norm = mpl.colors.Normalize(vmin=0, vmax=total_frames) + colorbar = self.fig.colorbar(mpl.cm.ScalarMappable(norm=norm, cmap=cmap), ax=self.ax, orientation='vertical') + + # Change the colorbar label + colorbar.set_label('Frame', color='#999999') # Change the label and its color + + # Change the tick colors + colorbar.ax.yaxis.set_tick_params(colors='#999999') # Change the tick color + + # Change the tick frequency + # Assuming you want to set the ticks at every 10th frame + ticks = np.arange(0, total_frames, 10) + colorbar.ax.yaxis.set_ticks(ticks) + + plt.title('') + plt.draw() + buf = io.BytesIO() + plt.savefig(buf, format='png', bbox_inches='tight', pad_inches=0) + buf.seek(0) + img = Image.open(buf) + tensor_img = ToTensor()(img) + buf.close() + tensor_img = tensor_img.permute(1, 2, 0).unsqueeze(0) + if use_viewer: + time.sleep(1) + plt.show() + return (tensor_img,) + + def extrinsic2pyramid(self, extrinsic, color_map='red', hw_ratio=1/1, base_xval=1, zval=3): + import matplotlib.pyplot as plt + from mpl_toolkits.mplot3d.art3d import Poly3DCollection + vertex_std = np.array([[0, 0, 0, 1], + [base_xval, -base_xval * hw_ratio, zval, 1], + [base_xval, base_xval * hw_ratio, zval, 1], + [-base_xval, base_xval * hw_ratio, zval, 1], + [-base_xval, -base_xval * hw_ratio, zval, 1]]) + vertex_transformed = vertex_std @ extrinsic.T + meshes = [[vertex_transformed[0, :-1], vertex_transformed[1][:-1], vertex_transformed[2, :-1]], + [vertex_transformed[0, :-1], vertex_transformed[2, :-1], vertex_transformed[3, :-1]], + [vertex_transformed[0, :-1], vertex_transformed[3, :-1], vertex_transformed[4, :-1]], + [vertex_transformed[0, :-1], vertex_transformed[4, :-1], vertex_transformed[1, :-1]], + [vertex_transformed[1, :-1], vertex_transformed[2, :-1], vertex_transformed[3, :-1], vertex_transformed[4, :-1]]] + + color = color_map if isinstance(color_map, str) else plt.cm.rainbow(color_map) + + self.ax.add_collection3d( + Poly3DCollection(meshes, facecolors=color, linewidths=0.3, edgecolors=color, alpha=0.25)) + + def customize_legend(self, list_label): + from matplotlib.patches import Patch + import matplotlib.pyplot as plt + list_handle = [] + for idx, label in enumerate(list_label): + color = plt.cm.rainbow(idx / len(list_label)) + patch = Patch(color=color, label=label) + list_handle.append(patch) + plt.legend(loc='right', bbox_to_anchor=(1.8, 0.5), handles=list_handle) + + def get_c2w(self, w2cs, transform_matrix, relative_c2w): + if relative_c2w: + target_cam_c2w = np.array([ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [0, 0, 0, 1] + ]) + abs2rel = target_cam_c2w @ w2cs[0] + ret_poses = [target_cam_c2w, ] + [abs2rel @ np.linalg.inv(w2c) for w2c in w2cs[1:]] + else: + ret_poses = [np.linalg.inv(w2c) for w2c in w2cs] + ret_poses = [transform_matrix @ x for x in ret_poses] + return np.array(ret_poses, dtype=np.float32) + + + +class CheckpointPerturbWeights: + + @classmethod + def INPUT_TYPES(s): + return {"required": { + "model": ("MODEL",), + "joint_blocks": ("FLOAT", {"default": 0.02, "min": 0.001, "max": 10.0, "step": 0.001}), + "final_layer": ("FLOAT", {"default": 0.02, "min": 0.001, "max": 10.0, "step": 0.001}), + "rest_of_the_blocks": ("FLOAT", {"default": 0.02, "min": 0.001, "max": 10.0, "step": 0.001}), + "seed": ("INT", {"default": 123,"min": 0, "max": 0xffffffffffffffff, "step": 1}), + } + } + RETURN_TYPES = ("MODEL",) + FUNCTION = "mod" + OUTPUT_NODE = True + + CATEGORY = "KJNodes/experimental" + + def mod(self, seed, model, joint_blocks, final_layer, rest_of_the_blocks): + import copy + torch.manual_seed(seed) + torch.cuda.manual_seed_all(seed) + device = model_management.get_torch_device() + model_copy = copy.deepcopy(model) + model_copy.model.to(device) + keys = model_copy.model.diffusion_model.state_dict().keys() + + dict = {} + for key in keys: + dict[key] = model_copy.model.diffusion_model.state_dict()[key] + + pbar = ProgressBar(len(keys)) + for k in keys: + v = dict[k] + print(f'{k}: {v.std()}') + if k.startswith('joint_blocks'): + multiplier = joint_blocks + elif k.startswith('final_layer'): + multiplier = final_layer + else: + multiplier = rest_of_the_blocks + dict[k] += torch.normal(torch.zeros_like(v) * v.mean(), torch.ones_like(v) * v.std() * multiplier).to(device) + pbar.update(1) + model_copy.model.diffusion_model.load_state_dict(dict) + return model_copy, + +class DifferentialDiffusionAdvanced(): + @classmethod + def INPUT_TYPES(s): + return {"required": { + "model": ("MODEL", ), + "samples": ("LATENT",), + "mask": ("MASK",), + "multiplier": ("FLOAT", {"default": 1.0, "min": -10.0, "max": 10.0, "step": 0.001}), + }} + RETURN_TYPES = ("MODEL", "LATENT") + FUNCTION = "apply" + CATEGORY = "_for_testing" + INIT = False + + def apply(self, model, samples, mask, multiplier): + self.multiplier = multiplier + model = model.clone() + model.set_model_denoise_mask_function(self.forward) + s = samples.copy() + s["noise_mask"] = mask.reshape((-1, 1, mask.shape[-2], mask.shape[-1])) + return (model, s) + + def forward(self, sigma: torch.Tensor, denoise_mask: torch.Tensor, extra_options: dict): + model = extra_options["model"] + step_sigmas = extra_options["sigmas"] + sigma_to = model.inner_model.model_sampling.sigma_min + if step_sigmas[-1] > sigma_to: + sigma_to = step_sigmas[-1] + sigma_from = step_sigmas[0] + + ts_from = model.inner_model.model_sampling.timestep(sigma_from) + ts_to = model.inner_model.model_sampling.timestep(sigma_to) + current_ts = model.inner_model.model_sampling.timestep(sigma[0]) + + threshold = (current_ts - ts_to) / (ts_from - ts_to) / self.multiplier + + return (denoise_mask >= threshold).to(denoise_mask.dtype) + +class FluxBlockLoraSelect: + def __init__(self): + self.loaded_lora = None + + @classmethod + def INPUT_TYPES(s): + arg_dict = {} + argument = ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1000.0, "step": 0.01}) + + for i in range(19): + arg_dict["double_blocks.{}.".format(i)] = argument + + for i in range(38): + arg_dict["single_blocks.{}.".format(i)] = argument + + return {"required": arg_dict} + + RETURN_TYPES = ("SELECTEDDITBLOCKS", ) + RETURN_NAMES = ("blocks", ) + OUTPUT_TOOLTIPS = ("The modified diffusion model.",) + FUNCTION = "load_lora" + + CATEGORY = "KJNodes/experimental" + DESCRIPTION = "Select individual block alpha values, value of 0 removes the block altogether" + + def load_lora(self, **kwargs): + return (kwargs,) + +class HunyuanVideoBlockLoraSelect: + @classmethod + def INPUT_TYPES(s): + arg_dict = {} + argument = ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1000.0, "step": 0.01}) + + for i in range(20): + arg_dict["double_blocks.{}.".format(i)] = argument + + for i in range(40): + arg_dict["single_blocks.{}.".format(i)] = argument + + return {"required": arg_dict} + + RETURN_TYPES = ("SELECTEDDITBLOCKS", ) + RETURN_NAMES = ("blocks", ) + OUTPUT_TOOLTIPS = ("The modified diffusion model.",) + FUNCTION = "load_lora" + + CATEGORY = "KJNodes/experimental" + DESCRIPTION = "Select individual block alpha values, value of 0 removes the block altogether" + + def load_lora(self, **kwargs): + return (kwargs,) + +class Wan21BlockLoraSelect: + @classmethod + def INPUT_TYPES(s): + arg_dict = {} + argument = ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1000.0, "step": 0.01}) + + for i in range(40): + arg_dict["blocks.{}.".format(i)] = argument + + return {"required": arg_dict} + + RETURN_TYPES = ("SELECTEDDITBLOCKS", ) + RETURN_NAMES = ("blocks", ) + OUTPUT_TOOLTIPS = ("The modified diffusion model.",) + FUNCTION = "load_lora" + + CATEGORY = "KJNodes/experimental" + DESCRIPTION = "Select individual block alpha values, value of 0 removes the block altogether" + + def load_lora(self, **kwargs): + return (kwargs,) + +class DiTBlockLoraLoader: + def __init__(self): + self.loaded_lora = None + + @classmethod + def INPUT_TYPES(s): + return {"required": { + "model": ("MODEL", {"tooltip": "The diffusion model the LoRA will be applied to."}), + "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."}), + + }, + "optional": { + "lora_name": (folder_paths.get_filename_list("loras"), {"tooltip": "The name of the LoRA."}), + "opt_lora_path": ("STRING", {"forceInput": True, "tooltip": "Absolute path of the LoRA."}), + "blocks": ("SELECTEDDITBLOCKS",), + } + } + + RETURN_TYPES = ("MODEL", "STRING", ) + RETURN_NAMES = ("model", "rank", ) + OUTPUT_TOOLTIPS = ("The modified diffusion model.", "possible rank of the LoRA.") + FUNCTION = "load_lora" + CATEGORY = "KJNodes/experimental" + + def load_lora(self, model, strength_model, lora_name=None, opt_lora_path=None, blocks=None): + + import comfy.lora + + if opt_lora_path: + lora_path = opt_lora_path + else: + lora_path = folder_paths.get_full_path("loras", lora_name) + + lora = None + if self.loaded_lora is not None: + if self.loaded_lora[0] == lora_path: + lora = self.loaded_lora[1] + else: + self.loaded_lora = None + + if lora is None: + lora = load_torch_file(lora_path, safe_load=True) + self.loaded_lora = (lora_path, lora) + + # Find the first key that ends with "weight" + rank = "unknown" + weight_key = next((key for key in lora.keys() if key.endswith('weight')), None) + # Print the shape of the value corresponding to the key + if weight_key: + print(f"Shape of the first 'weight' key ({weight_key}): {lora[weight_key].shape}") + rank = str(lora[weight_key].shape[0]) + else: + print("No key ending with 'weight' found.") + rank = "Couldn't find rank" + self.loaded_lora = (lora_path, lora) + + key_map = {} + if model is not None: + key_map = comfy.lora.model_lora_keys_unet(model.model, key_map) + + loaded = comfy.lora.load_lora(lora, key_map) + + if blocks is not None: + keys_to_delete = [] + + for block in blocks: + for key in list(loaded.keys()): + match = False + if isinstance(key, str) and block in key: + match = True + elif isinstance(key, tuple): + for k in key: + if block in k: + match = True + break + + if match: + ratio = blocks[block] + if ratio == 0: + keys_to_delete.append(key) + else: + # Only modify LoRA adapters, skip diff tuples + value = loaded[key] + if hasattr(value, 'weights'): + print(f"Modifying LoRA adapter for key: {key}") + weights_list = list(value.weights) + weights_list[2] = ratio + loaded[key].weights = tuple(weights_list) + else: + print(f"Skipping non-LoRA entry for key: {key}") + + for key in keys_to_delete: + del loaded[key] + + print("loading lora keys:") + for key, value in loaded.items(): + if hasattr(value, 'weights'): + print(f"Key: {key}, Alpha: {value.weights[2]}") + else: + print(f"Key: {key}, Type: {type(value)}") + + if model is not None: + new_modelpatcher = model.clone() + k = new_modelpatcher.add_patches(loaded, strength_model) + + k = set(k) + for x in loaded: + if (x not in k): + print("NOT LOADED {}".format(x)) + + return (new_modelpatcher, rank) + +class CustomControlNetWeightsFluxFromList: + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "list_of_floats": ("FLOAT", {"forceInput": True}, ), + }, + "optional": { + "uncond_multiplier": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}, ), + "cn_extras": ("CN_WEIGHTS_EXTRAS",), + "autosize": ("ACNAUTOSIZE", {"padding": 0}), + } + } + + RETURN_TYPES = ("CONTROL_NET_WEIGHTS", "TIMESTEP_KEYFRAME",) + RETURN_NAMES = ("CN_WEIGHTS", "TK_SHORTCUT") + FUNCTION = "load_weights" + DESCRIPTION = "Creates controlnet weights from a list of floats for Advanced-ControlNet" + + CATEGORY = "KJNodes/controlnet" + + def load_weights(self, list_of_floats: list[float], + uncond_multiplier: float=1.0, cn_extras: dict[str]={}): + + adv_control = importlib.import_module("ComfyUI-Advanced-ControlNet.adv_control") + ControlWeights = adv_control.utils.ControlWeights + TimestepKeyframeGroup = adv_control.utils.TimestepKeyframeGroup + TimestepKeyframe = adv_control.utils.TimestepKeyframe + + weights = ControlWeights.controlnet(weights_input=list_of_floats, uncond_multiplier=uncond_multiplier, extras=cn_extras) + print(weights.weights_input) + return (weights, TimestepKeyframeGroup.default(TimestepKeyframe(control_weights=weights))) + +SHAKKERLABS_UNION_CONTROLNET_TYPES = { + "canny": 0, + "tile": 1, + "depth": 2, + "blur": 3, + "pose": 4, + "gray": 5, + "low quality": 6, +} + +class SetShakkerLabsUnionControlNetType: + @classmethod + def INPUT_TYPES(s): + return {"required": {"control_net": ("CONTROL_NET", ), + "type": (["auto"] + list(SHAKKERLABS_UNION_CONTROLNET_TYPES.keys()),) + }} + + CATEGORY = "conditioning/controlnet" + RETURN_TYPES = ("CONTROL_NET",) + + FUNCTION = "set_controlnet_type" + + def set_controlnet_type(self, control_net, type): + control_net = control_net.copy() + type_number = SHAKKERLABS_UNION_CONTROLNET_TYPES.get(type, -1) + if type_number >= 0: + control_net.set_extra_arg("control_type", [type_number]) + else: + control_net.set_extra_arg("control_type", []) + + return (control_net,) + +class ModelSaveKJ: + def __init__(self): + self.output_dir = folder_paths.get_output_directory() + + @classmethod + def INPUT_TYPES(s): + return {"required": { "model": ("MODEL",), + "filename_prefix": ("STRING", {"default": "diffusion_models/ComfyUI"}), + "model_key_prefix": ("STRING", {"default": "model.diffusion_model."}), + }, + "hidden": {"prompt": "PROMPT", "extra_pnginfo": "EXTRA_PNGINFO"},} + RETURN_TYPES = () + FUNCTION = "save" + OUTPUT_NODE = True + + CATEGORY = "advanced/model_merging" + + def save(self, model, filename_prefix, model_key_prefix, prompt=None, extra_pnginfo=None): + from comfy.utils import save_torch_file + full_output_folder, filename, counter, subfolder, filename_prefix = folder_paths.get_save_image_path(filename_prefix, self.output_dir) + + output_checkpoint = f"{filename}_{counter:05}_.safetensors" + output_checkpoint = os.path.join(full_output_folder, output_checkpoint) + + load_models = [model] + + model_management.load_models_gpu(load_models, force_patch_weights=True) + default_prefix = "model.diffusion_model." + + sd = model.model.state_dict_for_saving(None, None, None) + + new_sd = {} + for k in sd: + if k.startswith(default_prefix): + new_key = model_key_prefix + k[len(default_prefix):] + else: + new_key = k # In case the key doesn't start with the default prefix, keep it unchanged + t = sd[k] + if not t.is_contiguous(): + t = t.contiguous() + new_sd[new_key] = t + print(full_output_folder) + if not os.path.exists(full_output_folder): + os.makedirs(full_output_folder) + save_torch_file(new_sd, os.path.join(full_output_folder, output_checkpoint)) + return {} + +class StyleModelApplyAdvanced: + @classmethod + def INPUT_TYPES(s): + return {"required": {"conditioning": ("CONDITIONING", ), + "style_model": ("STYLE_MODEL", ), + "clip_vision_output": ("CLIP_VISION_OUTPUT", ), + "strength": ("FLOAT", {"default": 1.0, "min": -10.0, "max": 10.0, "step": 0.001}), + }} + RETURN_TYPES = ("CONDITIONING",) + FUNCTION = "apply_stylemodel" + CATEGORY = "KJNodes/experimental" + DESCRIPTION = "StyleModelApply but with strength parameter" + + def apply_stylemodel(self, clip_vision_output, style_model, conditioning, strength=1.0): + cond = style_model.get_cond(clip_vision_output).flatten(start_dim=0, end_dim=1).unsqueeze(dim=0) + cond = strength * cond + c = [] + for t in conditioning: + n = [torch.cat((t[0], cond), dim=1), t[1].copy()] + c.append(n) + return (c, ) + +class AudioConcatenate: + @classmethod + def INPUT_TYPES(s): + return {"required": { + "audio1": ("AUDIO",), + "audio2": ("AUDIO",), + "direction": ( + [ 'right', + 'left', + ], + { + "default": 'right' + }), + }} + + RETURN_TYPES = ("AUDIO",) + FUNCTION = "concanate" + CATEGORY = "KJNodes/audio" + DESCRIPTION = """ +Concatenates the audio1 to audio2 in the specified direction. +""" + + def concanate(self, audio1, audio2, direction): + sample_rate_1 = audio1["sample_rate"] + sample_rate_2 = audio2["sample_rate"] + if sample_rate_1 != sample_rate_2: + raise Exception("Sample rates of the two audios do not match") + + waveform_1 = audio1["waveform"] + print(waveform_1.shape) + waveform_2 = audio2["waveform"] + + # Concatenate based on the specified direction + if direction == 'right': + concatenated_audio = torch.cat((waveform_1, waveform_2), dim=2) # Concatenate along width + elif direction == 'left': + concatenated_audio= torch.cat((waveform_2, waveform_1), dim=2) # Concatenate along width + return ({"waveform": concatenated_audio, "sample_rate": sample_rate_1},) + +class LeapfusionHunyuanI2V: + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "model": ("MODEL",), + "latent": ("LATENT",), + "index": ("INT", {"default": 0, "min": -1, "max": 1000, "step": 1,"tooltip": "The index of the latent to be replaced. 0 for first frame and -1 for last"}), + "start_percent": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.01, "tooltip": "The start percentage of steps to apply"}), + "end_percent": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01, "tooltip": "The end percentage of steps to apply"}), + "strength": ("FLOAT", {"default": 1.0, "min": -10.0, "max": 10.0, "step": 0.001}), + } + } + + RETURN_TYPES = ("MODEL",) + FUNCTION = "patch" + + CATEGORY = "KJNodes/experimental" + + def patch(self, model, latent, index, strength, start_percent, end_percent): + + def outer_wrapper(samples, index, start_percent, end_percent): + def unet_wrapper(apply_model, args): + steps = args["c"]["transformer_options"]["sample_sigmas"] + inp, timestep, c = args["input"], args["timestep"], args["c"] + matched_step_index = (steps == timestep).nonzero() + if len(matched_step_index) > 0: + current_step_index = matched_step_index.item() + else: + for i in range(len(steps) - 1): + # walk from beginning of steps until crossing the timestep + if (steps[i] - timestep[0]) * (steps[i + 1] - timestep[0]) <= 0: + current_step_index = i + break + else: + current_step_index = 0 + current_percent = current_step_index / (len(steps) - 1) + if samples is not None: + if start_percent <= current_percent <= end_percent: + inp[:, :, [index], :, :] = samples[:, :, [0], :, :].to(inp) + else: + inp[:, :, [index], :, :] = torch.zeros(1) + return apply_model(inp, timestep, **c) + return unet_wrapper + + samples = latent["samples"] * 0.476986 * strength + m = model.clone() + m.set_model_unet_function_wrapper(outer_wrapper(samples, index, start_percent, end_percent)) + + return (m,) + +class ImageNoiseAugmentation: + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "image": ("IMAGE",), + "noise_aug_strength": ("FLOAT", {"default": None, "min": 0.0, "max": 100.0, "step": 0.001}), + "seed": ("INT", {"default": 123,"min": 0, "max": 0xffffffffffffffff, "step": 1}), + } + } + + RETURN_TYPES = ("IMAGE",) + FUNCTION = "add_noise" + CATEGORY = "KJNodes/image" + DESCRIPTION = """ + Add noise to an image. + """ + + def add_noise(self, image, noise_aug_strength, seed): + torch.manual_seed(seed) + sigma = torch.ones((image.shape[0],)).to(image.device, image.dtype) * noise_aug_strength + image_noise = torch.randn_like(image) * sigma[:, None, None, None] + image_noise = torch.where(image==-1, torch.zeros_like(image), image_noise) + image_out = image + image_noise + return image_out, + +class VAELoaderKJ: + @staticmethod + def vae_list(): + vaes = folder_paths.get_filename_list("vae") + approx_vaes = folder_paths.get_filename_list("vae_approx") + sdxl_taesd_enc = False + sdxl_taesd_dec = False + sd1_taesd_enc = False + sd1_taesd_dec = False + sd3_taesd_enc = False + sd3_taesd_dec = False + f1_taesd_enc = False + f1_taesd_dec = False + + for v in approx_vaes: + if v.startswith("taesd_decoder."): + sd1_taesd_dec = True + elif v.startswith("taesd_encoder."): + sd1_taesd_enc = True + elif v.startswith("taesdxl_decoder."): + sdxl_taesd_dec = True + elif v.startswith("taesdxl_encoder."): + sdxl_taesd_enc = True + elif v.startswith("taesd3_decoder."): + sd3_taesd_dec = True + elif v.startswith("taesd3_encoder."): + sd3_taesd_enc = True + elif v.startswith("taef1_encoder."): + f1_taesd_dec = True + elif v.startswith("taef1_decoder."): + f1_taesd_enc = True + if sd1_taesd_dec and sd1_taesd_enc: + vaes.append("taesd") + if sdxl_taesd_dec and sdxl_taesd_enc: + vaes.append("taesdxl") + if sd3_taesd_dec and sd3_taesd_enc: + vaes.append("taesd3") + if f1_taesd_dec and f1_taesd_enc: + vaes.append("taef1") + return vaes + + @staticmethod + def load_taesd(name): + sd = {} + approx_vaes = folder_paths.get_filename_list("vae_approx") + + encoder = next(filter(lambda a: a.startswith("{}_encoder.".format(name)), approx_vaes)) + decoder = next(filter(lambda a: a.startswith("{}_decoder.".format(name)), approx_vaes)) + + enc = load_torch_file(folder_paths.get_full_path_or_raise("vae_approx", encoder)) + for k in enc: + sd["taesd_encoder.{}".format(k)] = enc[k] + + dec = load_torch_file(folder_paths.get_full_path_or_raise("vae_approx", decoder)) + for k in dec: + sd["taesd_decoder.{}".format(k)] = dec[k] + + if name == "taesd": + sd["vae_scale"] = torch.tensor(0.18215) + sd["vae_shift"] = torch.tensor(0.0) + elif name == "taesdxl": + sd["vae_scale"] = torch.tensor(0.13025) + sd["vae_shift"] = torch.tensor(0.0) + elif name == "taesd3": + sd["vae_scale"] = torch.tensor(1.5305) + sd["vae_shift"] = torch.tensor(0.0609) + elif name == "taef1": + sd["vae_scale"] = torch.tensor(0.3611) + sd["vae_shift"] = torch.tensor(0.1159) + return sd + + @classmethod + def INPUT_TYPES(s): + return { + "required": { "vae_name": (s.vae_list(), ), + "device": (["main_device", "cpu"],), + "weight_dtype": (["bf16", "fp16", "fp32" ],), + } + } + + RETURN_TYPES = ("VAE",) + FUNCTION = "load_vae" + CATEGORY = "KJNodes/vae" + + def load_vae(self, vae_name, device, weight_dtype): + from comfy.sd import VAE + dtype = {"bf16": torch.bfloat16, "fp16": torch.float16, "fp32": torch.float32}[weight_dtype] + if device == "main_device": + device = model_management.get_torch_device() + elif device == "cpu": + device = torch.device("cpu") + if vae_name in ["taesd", "taesdxl", "taesd3", "taef1"]: + sd = self.load_taesd(vae_name) + else: + vae_path = folder_paths.get_full_path_or_raise("vae", vae_name) + sd = load_torch_file(vae_path) + vae = VAE(sd=sd, device=device, dtype=dtype) + return (vae,) + +from comfy.samplers import sampling_function, CFGGuider +class Guider_ScheduledCFG(CFGGuider): + + def set_cfg(self, cfg, start_percent, end_percent): + self.cfg = cfg + self.start_percent = start_percent + self.end_percent = end_percent + + def predict_noise(self, x, timestep, model_options={}, seed=None): + steps = model_options["transformer_options"]["sample_sigmas"] + matched_step_index = (steps == timestep).nonzero() + assert not (isinstance(self.cfg, list) and len(self.cfg) != (len(steps) - 1)), "cfg list length must match step count" + if len(matched_step_index) > 0: + current_step_index = matched_step_index.item() + else: + for i in range(len(steps) - 1): + # walk from beginning of steps until crossing the timestep + if (steps[i] - timestep[0]) * (steps[i + 1] - timestep[0]) <= 0: + current_step_index = i + break + else: + current_step_index = 0 + current_percent = current_step_index / (len(steps) - 1) + + if self.start_percent <= current_percent <= self.end_percent: + if isinstance(self.cfg, list): + cfg = self.cfg[current_step_index] + else: + cfg = self.cfg + uncond = self.conds.get("negative", None) + else: + uncond = None + cfg = 1.0 + + return sampling_function(self.inner_model, x, timestep, uncond, self.conds.get("positive", None), cfg, model_options=model_options, seed=seed) + +class ScheduledCFGGuidance: + @classmethod + def INPUT_TYPES(s): + return {"required": { + "model": ("MODEL",), + "positive": ("CONDITIONING", ), + "negative": ("CONDITIONING", ), + "cfg": ("FLOAT", {"default": 6.0, "min": 0.0, "max": 100.0, "step": 0.01}), + "start_percent": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step":0.01}), + "end_percent": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step":0.01}), + }, + } + RETURN_TYPES = ("GUIDER",) + FUNCTION = "get_guider" + CATEGORY = "KJNodes/experimental" + DESCRiPTION = """ +CFG Guider that allows for scheduled CFG changes over steps, the steps outside the range will use CFG 1.0 thus being processed faster. +cfg input can be a list of floats matching step count, or a single float for all steps. +""" + + def get_guider(self, model, cfg, positive, negative, start_percent, end_percent): + guider = Guider_ScheduledCFG(model) + guider.set_conds(positive, negative) + guider.set_cfg(cfg, start_percent, end_percent) + return (guider, ) + + +class ApplyRifleXRoPE_WanVideo: + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "model": ("MODEL",), + "latent": ("LATENT", {"tooltip": "Only used to get the latent count"}), + "k": ("INT", {"default": 6, "min": 1, "max": 100, "step": 1, "tooltip": "Index of intrinsic frequency"}), + } + } + + RETURN_TYPES = ("MODEL",) + FUNCTION = "patch" + CATEGORY = "KJNodes/experimental" + EXPERIMENTAL = True + DESCRIPTION = "Extends the potential frame count of HunyuanVideo using this method: https://github.com/thu-ml/RIFLEx" + + def patch(self, model, latent, k): + model_class = model.model.diffusion_model + + model_clone = model.clone() + num_frames = latent["samples"].shape[2] + d = model_class.dim // model_class.num_heads + + rope_embedder = EmbedND_RifleX( + d, + 10000.0, + [d - 4 * (d // 6), 2 * (d // 6), 2 * (d // 6)], + num_frames, + k + ) + + model_clone.add_object_patch(f"diffusion_model.rope_embedder", rope_embedder) + + return (model_clone, ) + +class ApplyRifleXRoPE_HunuyanVideo: + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "model": ("MODEL",), + "latent": ("LATENT", {"tooltip": "Only used to get the latent count"}), + "k": ("INT", {"default": 4, "min": 1, "max": 100, "step": 1, "tooltip": "Index of intrinsic frequency"}), + } + } + + RETURN_TYPES = ("MODEL",) + FUNCTION = "patch" + CATEGORY = "KJNodes/experimental" + EXPERIMENTAL = True + DESCRIPTION = "Extends the potential frame count of HunyuanVideo using this method: https://github.com/thu-ml/RIFLEx" + + def patch(self, model, latent, k): + model_class = model.model.diffusion_model + + model_clone = model.clone() + num_frames = latent["samples"].shape[2] + + pe_embedder = EmbedND_RifleX( + model_class.params.hidden_size // model_class.params.num_heads, + model_class.params.theta, + model_class.params.axes_dim, + num_frames, + k + ) + + model_clone.add_object_patch(f"diffusion_model.pe_embedder", pe_embedder) + + return (model_clone, ) + +def rope_riflex(pos, dim, theta, L_test, k): + from einops import rearrange + assert dim % 2 == 0 + if model_management.is_device_mps(pos.device) or model_management.is_intel_xpu() or model_management.is_directml_enabled(): + device = torch.device("cpu") + else: + device = pos.device + + scale = torch.linspace(0, (dim - 2) / dim, steps=dim//2, dtype=torch.float64, device=device) + omega = 1.0 / (theta**scale) + + # RIFLEX modification - adjust last frequency component if L_test and k are provided + if k and L_test: + omega[k-1] = 0.9 * 2 * torch.pi / L_test + + out = torch.einsum("...n,d->...nd", pos.to(dtype=torch.float32, device=device), omega) + out = torch.stack([torch.cos(out), -torch.sin(out), torch.sin(out), torch.cos(out)], dim=-1) + out = rearrange(out, "b n d (i j) -> b n d i j", i=2, j=2) + return out.to(dtype=torch.float32, device=pos.device) + +class EmbedND_RifleX(nn.Module): + def __init__(self, dim, theta, axes_dim, num_frames, k): + super().__init__() + self.dim = dim + self.theta = theta + self.axes_dim = axes_dim + self.num_frames = num_frames + self.k = k + + def forward(self, ids): + n_axes = ids.shape[-1] + emb = torch.cat( + [rope_riflex(ids[..., i], self.axes_dim[i], self.theta, self.num_frames, self.k if i == 0 else 0) for i in range(n_axes)], + dim=-3, + ) + return emb.unsqueeze(1) + + +class Timer: + def __init__(self, name): + self.name = name + self.start_time = None + self.elapsed = 0 + +class TimerNodeKJ: + @classmethod + + def INPUT_TYPES(s): + return { + "required": { + "any_input": (IO.ANY, ), + "mode": (["start", "stop"],), + "name": ("STRING", {"default": "Timer"}), + }, + "optional": { + "timer": ("TIMER",), + }, + } + + RETURN_TYPES = (IO.ANY, "TIMER", "INT", ) + RETURN_NAMES = ("any_output", "timer", "time") + FUNCTION = "timer" + CATEGORY = "KJNodes/misc" + + def timer(self, mode, name, any_input=None, timer=None): + if timer is None: + if mode == "start": + timer = Timer(name=name) + timer.start_time = time.time() + return {"ui": { + "text": [f"{timer.start_time}"]}, + "result": (any_input, timer, 0) + } + elif mode == "stop" and timer is not None: + end_time = time.time() + timer.elapsed = int((end_time - timer.start_time) * 1000) + timer.start_time = None + return (any_input, timer, timer.elapsed) + +class HunyuanVideoEncodeKeyframesToCond: + @classmethod + def INPUT_TYPES(s): + return {"required": { + "model": ("MODEL",), + "positive": ("CONDITIONING", ), + "vae": ("VAE", ), + "start_frame": ("IMAGE", ), + "end_frame": ("IMAGE", ), + "num_frames": ("INT", {"default": 33, "min": 2, "max": 4096, "step": 1}), + "tile_size": ("INT", {"default": 512, "min": 64, "max": 4096, "step": 64}), + "overlap": ("INT", {"default": 64, "min": 0, "max": 4096, "step": 32}), + "temporal_size": ("INT", {"default": 64, "min": 8, "max": 4096, "step": 4, "tooltip": "Only used for video VAEs: Amount of frames to encode at a time."}), + "temporal_overlap": ("INT", {"default": 8, "min": 4, "max": 4096, "step": 4, "tooltip": "Only used for video VAEs: Amount of frames to overlap."}), + }, + "optional": { + "negative": ("CONDITIONING", ), + } + } + + RETURN_TYPES = ("MODEL", "CONDITIONING","CONDITIONING","LATENT") + RETURN_NAMES = ("model", "positive", "negative", "latent") + FUNCTION = "encode" + + CATEGORY = "KJNodes/videomodels" + + def encode(self, model, positive, start_frame, end_frame, num_frames, vae, tile_size, overlap, temporal_size, temporal_overlap, negative=None): + + model_clone = model.clone() + + model_clone.add_object_patch("concat_keys", ("concat_image",)) + + + x = (start_frame.shape[1] // 8) * 8 + y = (start_frame.shape[2] // 8) * 8 + + if start_frame.shape[1] != x or start_frame.shape[2] != y: + x_offset = (start_frame.shape[1] % 8) // 2 + y_offset = (start_frame.shape[2] % 8) // 2 + start_frame = start_frame[:,x_offset:x + x_offset, y_offset:y + y_offset,:] + if end_frame.shape[1] != x or end_frame.shape[2] != y: + x_offset = (start_frame.shape[1] % 8) // 2 + y_offset = (start_frame.shape[2] % 8) // 2 + end_frame = end_frame[:,x_offset:x + x_offset, y_offset:y + y_offset,:] + + video_frames = torch.zeros(num_frames-2, start_frame.shape[1], start_frame.shape[2], start_frame.shape[3], device=start_frame.device, dtype=start_frame.dtype) + video_frames = torch.cat([start_frame, video_frames, end_frame], dim=0) + + concat_latent = vae.encode_tiled(video_frames[:,:,:,:3], tile_x=tile_size, tile_y=tile_size, overlap=overlap, tile_t=temporal_size, overlap_t=temporal_overlap) + + out_latent = {} + out_latent["samples"] = torch.zeros_like(concat_latent) + + out = [] + for conditioning in [positive, negative if negative is not None else []]: + c = [] + for t in conditioning: + d = t[1].copy() + d["concat_latent_image"] = concat_latent + n = [t[0], d] + c.append(n) + out.append(c) + if len(out) == 1: + out.append(out[0]) + return (model_clone, out[0], out[1], out_latent) + + +class LazySwitchKJ: + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "switch": ("BOOLEAN",), + "on_false": (IO.ANY, {"lazy": True}), + "on_true": (IO.ANY, {"lazy": True}), + }, + } + + RETURN_TYPES = (IO.ANY,) + FUNCTION = "switch" + CATEGORY = "KJNodes/misc" + DESCRIPTION = "Controls flow of execution based on a boolean switch." + + def check_lazy_status(self, switch, on_false=None, on_true=None): + if switch and on_true is None: + return ["on_true"] + if not switch and on_false is None: + return ["on_false"] + + def switch(self, switch, on_false = None, on_true=None): + value = on_true if switch else on_false + return (value,) \ No newline at end of file diff --git a/ComfyUI/custom_nodes/comfyui-kjnodes/utility/fluid.py b/ComfyUI/custom_nodes/comfyui-kjnodes/utility/fluid.py new file mode 100644 index 0000000000000000000000000000000000000000..c0691987f5249a031ecbb74329ba513d5788b691 --- /dev/null +++ b/ComfyUI/custom_nodes/comfyui-kjnodes/utility/fluid.py @@ -0,0 +1,67 @@ +import numpy as np +from scipy.ndimage import map_coordinates, spline_filter +from scipy.sparse.linalg import factorized + +from .numerical import difference, operator + + +class Fluid: + def __init__(self, shape, *quantities, pressure_order=1, advect_order=3): + self.shape = shape + self.dimensions = len(shape) + + # Prototyping is simplified by dynamically + # creating advected quantities as needed. + self.quantities = quantities + for q in quantities: + setattr(self, q, np.zeros(shape)) + + self.indices = np.indices(shape) + self.velocity = np.zeros((self.dimensions, *shape)) + + laplacian = operator(shape, difference(2, pressure_order)) + self.pressure_solver = factorized(laplacian) + + self.advect_order = advect_order + + def step(self): + # Advection is computed backwards in time as described in Stable Fluids. + advection_map = self.indices - self.velocity + + # SciPy's spline filter introduces checkerboard divergence. + # A linear blend of the filtered and unfiltered fields based + # on some value epsilon eliminates this error. + def advect(field, filter_epsilon=10e-2, mode='constant'): + filtered = spline_filter(field, order=self.advect_order, mode=mode) + field = filtered * (1 - filter_epsilon) + field * filter_epsilon + return map_coordinates(field, advection_map, prefilter=False, order=self.advect_order, mode=mode) + + # Apply advection to each axis of the + # velocity field and each user-defined quantity. + for d in range(self.dimensions): + self.velocity[d] = advect(self.velocity[d]) + + for q in self.quantities: + setattr(self, q, advect(getattr(self, q))) + + # Compute the jacobian at each point in the + # velocity field to extract curl and divergence. + jacobian_shape = (self.dimensions,) * 2 + partials = tuple(np.gradient(d) for d in self.velocity) + jacobian = np.stack(partials).reshape(*jacobian_shape, *self.shape) + + divergence = jacobian.trace() + + # If this curl calculation is extended to 3D, the y-axis value must be negated. + # This corresponds to the coefficients of the levi-civita symbol in that dimension. + # Higher dimensions do not have a vector -> scalar, or vector -> vector, + # correspondence between velocity and curl due to differing isomorphisms + # between exterior powers in dimensions != 2 or 3 respectively. + curl_mask = np.triu(np.ones(jacobian_shape, dtype=bool), k=1) + curl = (jacobian[curl_mask] - jacobian[curl_mask.T]).squeeze() + + # Apply the pressure correction to the fluid's velocity field. + pressure = self.pressure_solver(divergence.flatten()).reshape(self.shape) + self.velocity -= np.gradient(pressure) + + return divergence, curl, pressure \ No newline at end of file diff --git a/ComfyUI/custom_nodes/comfyui-kjnodes/utility/magictex.py b/ComfyUI/custom_nodes/comfyui-kjnodes/utility/magictex.py new file mode 100644 index 0000000000000000000000000000000000000000..e6d426f7deb3deb977604dd37581eb4e9fe9e6a9 --- /dev/null +++ b/ComfyUI/custom_nodes/comfyui-kjnodes/utility/magictex.py @@ -0,0 +1,95 @@ +"""Generates psychedelic color textures in the spirit of Blender's magic texture shader using Python/Numpy + +https://github.com/cheind/magic-texture +""" +from typing import Tuple, Optional +import numpy as np + + +def coordinate_grid(shape: Tuple[int, int], dtype=np.float32): + """Returns a three-dimensional coordinate grid of given shape for use in `magic`.""" + x = np.linspace(-1, 1, shape[1], endpoint=True, dtype=dtype) + y = np.linspace(-1, 1, shape[0], endpoint=True, dtype=dtype) + X, Y = np.meshgrid(x, y) + XYZ = np.stack((X, Y, np.ones_like(X)), -1) + return XYZ + + +def random_transform(coords: np.ndarray, rng: np.random.Generator = None): + """Returns randomly transformed coordinates""" + H, W = coords.shape[:2] + rng = rng or np.random.default_rng() + m = rng.uniform(-1.0, 1.0, size=(3, 3)).astype(coords.dtype) + return (coords.reshape(-1, 3) @ m.T).reshape(H, W, 3) + + +def magic( + coords: np.ndarray, + depth: Optional[int] = None, + distortion: Optional[int] = None, + rng: np.random.Generator = None, +): + """Returns color magic color texture. + + The implementation is based on Blender's (https://www.blender.org/) magic + texture shader. The following adaptions have been made: + - we exchange the nested if-cascade by a probabilistic iterative approach + + Kwargs + ------ + coords: HxWx3 array + Coordinates transformed into colors by this method. See + `magictex.coordinate_grid` to generate the default. + depth: int (optional) + Number of transformations applied. Higher numbers lead to more + nested patterns. If not specified, randomly sampled. + distortion: float (optional) + Distortion of patterns. Larger values indicate more distortion, + lower values tend to generate smoother patterns. If not specified, + randomly sampled. + rng: np.random.Generator + Optional random generator to draw samples from. + + Returns + ------- + colors: HxWx3 array + Three channel color image in range [0,1] + """ + rng = rng or np.random.default_rng() + if distortion is None: + distortion = rng.uniform(1, 4) + if depth is None: + depth = rng.integers(1, 5) + + H, W = coords.shape[:2] + XYZ = coords + x = np.sin((XYZ[..., 0] + XYZ[..., 1] + XYZ[..., 2]) * distortion) + y = np.cos((-XYZ[..., 0] + XYZ[..., 1] - XYZ[..., 2]) * distortion) + z = -np.cos((-XYZ[..., 0] - XYZ[..., 1] + XYZ[..., 2]) * distortion) + + if depth > 0: + x *= distortion + y *= distortion + z *= distortion + y = -np.cos(x - y + z) + y *= distortion + + xyz = [x, y, z] + fns = [np.cos, np.sin] + for _ in range(1, depth): + axis = rng.choice(3) + fn = fns[rng.choice(2)] + signs = rng.binomial(n=1, p=0.5, size=4) * 2 - 1 + + xyz[axis] = signs[-1] * fn( + signs[0] * xyz[0] + signs[1] * xyz[1] + signs[2] * xyz[2] + ) + xyz[axis] *= distortion + + x, y, z = xyz + x /= 2 * distortion + y /= 2 * distortion + z /= 2 * distortion + c = 0.5 - np.stack((x, y, z), -1) + np.clip(c, 0, 1.0) + return c \ No newline at end of file diff --git a/ComfyUI/custom_nodes/comfyui-kjnodes/utility/numerical.py b/ComfyUI/custom_nodes/comfyui-kjnodes/utility/numerical.py new file mode 100644 index 0000000000000000000000000000000000000000..b5b88bc63c45d63d8913e56cbd06eb7ab413fe4f --- /dev/null +++ b/ComfyUI/custom_nodes/comfyui-kjnodes/utility/numerical.py @@ -0,0 +1,25 @@ +from functools import reduce +from itertools import cycle +from math import factorial + +import numpy as np +import scipy.sparse as sp + + +def difference(derivative, accuracy=1): + # Central differences implemented based on the article here: + # http://web.media.mit.edu/~crtaylor/calculator.html + derivative += 1 + radius = accuracy + derivative // 2 - 1 + points = range(-radius, radius + 1) + coefficients = np.linalg.inv(np.vander(points)) + return coefficients[-derivative] * factorial(derivative - 1), points + + +def operator(shape, *differences): + # Credit to Philip Zucker for figuring out + # that kronsum's argument order is reversed. + # Without that bit of wisdom I'd have lost it. + differences = zip(shape, cycle(differences)) + factors = (sp.diags(*diff, shape=(dim,) * 2) for dim, diff in differences) + return reduce(lambda a, f: sp.kronsum(f, a, format='csc'), factors) \ No newline at end of file diff --git a/ComfyUI/custom_nodes/comfyui-kjnodes/utility/utility.py b/ComfyUI/custom_nodes/comfyui-kjnodes/utility/utility.py new file mode 100644 index 0000000000000000000000000000000000000000..f3b5c425922784522791e33c225c29be1e8249e0 --- /dev/null +++ b/ComfyUI/custom_nodes/comfyui-kjnodes/utility/utility.py @@ -0,0 +1,39 @@ +import torch +import numpy as np +from PIL import Image +from typing import Union, List + +# Utility functions from mtb nodes: https://github.com/melMass/comfy_mtb +def pil2tensor(image: Union[Image.Image, List[Image.Image]]) -> torch.Tensor: + if isinstance(image, list): + return torch.cat([pil2tensor(img) for img in image], dim=0) + + return torch.from_numpy(np.array(image).astype(np.float32) / 255.0).unsqueeze(0) + + +def np2tensor(img_np: Union[np.ndarray, List[np.ndarray]]) -> torch.Tensor: + if isinstance(img_np, list): + return torch.cat([np2tensor(img) for img in img_np], dim=0) + + return torch.from_numpy(img_np.astype(np.float32) / 255.0).unsqueeze(0) + + +def tensor2np(tensor: torch.Tensor): + if len(tensor.shape) == 3: # Single image + return np.clip(255.0 * tensor.cpu().numpy(), 0, 255).astype(np.uint8) + else: # Batch of images + return [np.clip(255.0 * t.cpu().numpy(), 0, 255).astype(np.uint8) for t in tensor] + +def tensor2pil(image: torch.Tensor) -> List[Image.Image]: + batch_count = image.size(0) if len(image.shape) > 3 else 1 + if batch_count > 1: + out = [] + for i in range(batch_count): + out.extend(tensor2pil(image[i])) + return out + + return [ + Image.fromarray( + np.clip(255.0 * image.cpu().numpy().squeeze(), 0, 255).astype(np.uint8) + ) + ] \ No newline at end of file diff --git a/ComfyUI/custom_nodes/comfyui-kjnodes/web/green.png b/ComfyUI/custom_nodes/comfyui-kjnodes/web/green.png new file mode 100644 index 0000000000000000000000000000000000000000..900964e4b3907145fe1e75a5b58473567450e16d Binary files /dev/null and b/ComfyUI/custom_nodes/comfyui-kjnodes/web/green.png differ diff --git a/ComfyUI/custom_nodes/comfyui-kjnodes/web/js/appearance.js b/ComfyUI/custom_nodes/comfyui-kjnodes/web/js/appearance.js new file mode 100644 index 0000000000000000000000000000000000000000..d90b4aa34d4c52b22a4411194100972c83eed88d --- /dev/null +++ b/ComfyUI/custom_nodes/comfyui-kjnodes/web/js/appearance.js @@ -0,0 +1,23 @@ +import { app } from "../../../scripts/app.js"; + +app.registerExtension({ + name: "KJNodes.appearance", + nodeCreated(node) { + switch (node.comfyClass) { + case "INTConstant": + node.setSize([200, 58]); + node.color = "#1b4669"; + node.bgcolor = "#29699c"; + break; + case "FloatConstant": + node.setSize([200, 58]); + node.color = LGraphCanvas.node_colors.green.color; + node.bgcolor = LGraphCanvas.node_colors.green.bgcolor; + break; + case "ConditioningMultiCombine": + node.color = LGraphCanvas.node_colors.brown.color; + node.bgcolor = LGraphCanvas.node_colors.brown.bgcolor; + break; + } + } +}); diff --git a/ComfyUI/custom_nodes/comfyui-kjnodes/web/js/browserstatus.js b/ComfyUI/custom_nodes/comfyui-kjnodes/web/js/browserstatus.js new file mode 100644 index 0000000000000000000000000000000000000000..45abafb163481d9d760c8b273aad4d2a00db1e92 --- /dev/null +++ b/ComfyUI/custom_nodes/comfyui-kjnodes/web/js/browserstatus.js @@ -0,0 +1,55 @@ +import { api } from "../../../scripts/api.js"; +import { app } from "../../../scripts/app.js"; + +app.registerExtension({ + name: "KJNodes.browserstatus", + setup() { + if (!app.ui.settings.getSettingValue("KJNodes.browserStatus")) { + return; + } + api.addEventListener("status", ({ detail }) => { + let title = "ComfyUI"; + let favicon = "green"; + let queueRemaining = detail && detail.exec_info.queue_remaining; + + if (queueRemaining) { + favicon = "red"; + title = `00% - ${queueRemaining} | ${title}`; + } + let link = document.querySelector("link[rel~='icon']"); + if (!link) { + link = document.createElement("link"); + link.rel = "icon"; + document.head.appendChild(link); + } + link.href = new URL(`../${favicon}.png`, import.meta.url); + document.title = title; + }); + //add progress to the title + api.addEventListener("progress", ({ detail }) => { + const { value, max } = detail; + const progress = Math.floor((value / max) * 100); + let title = document.title; + + if (!isNaN(progress) && progress >= 0 && progress <= 100) { + const paddedProgress = String(progress).padStart(2, '0'); + title = `${paddedProgress}% ${title.replace(/^\d+%\s/, '')}`; + } + document.title = title; + }); + }, + init() { + if (!app.ui.settings.getSettingValue("KJNodes.browserStatus")) { + return; + } + const pythongossFeed = app.extensions.find( + (e) => e.name === 'pysssss.FaviconStatus', + ) + if (pythongossFeed) { + console.warn("KJNodes - Overriding pysssss.FaviconStatus") + pythongossFeed.setup = function() { + console.warn("Disabled by KJNodes") + }; + } + }, +}); \ No newline at end of file diff --git a/ComfyUI/custom_nodes/comfyui-kjnodes/web/js/contextmenu.js b/ComfyUI/custom_nodes/comfyui-kjnodes/web/js/contextmenu.js new file mode 100644 index 0000000000000000000000000000000000000000..8485658ef819722280160b124a2acf39353bb96d --- /dev/null +++ b/ComfyUI/custom_nodes/comfyui-kjnodes/web/js/contextmenu.js @@ -0,0 +1,147 @@ +import { app } from "../../../scripts/app.js"; + +// Adds context menu entries, code partly from pyssssscustom-scripts + +function addMenuHandler(nodeType, cb) { + const getOpts = nodeType.prototype.getExtraMenuOptions; + nodeType.prototype.getExtraMenuOptions = function () { + const r = getOpts.apply(this, arguments); + cb.apply(this, arguments); + return r; + }; +} + +function addNode(name, nextTo, options) { + console.log("name:", name); + console.log("nextTo:", nextTo); + options = { side: "left", select: true, shiftY: 0, shiftX: 0, ...(options || {}) }; + const node = LiteGraph.createNode(name); + app.graph.add(node); + + node.pos = [ + options.side === "left" ? nextTo.pos[0] - (node.size[0] + options.offset): nextTo.pos[0] + nextTo.size[0] + options.offset, + + nextTo.pos[1] + options.shiftY, + ]; + if (options.select) { + app.canvas.selectNode(node, false); + } + return node; +} + +app.registerExtension({ + name: "KJNodesContextmenu", + async beforeRegisterNodeDef(nodeType, nodeData, app) { + if (nodeData.input && nodeData.input.required) { + addMenuHandler(nodeType, function (_, options) { + options.unshift( + { + content: "Add GetNode", + callback: () => {addNode("GetNode", this, { side:"left", offset: 30});} + }, + { + content: "Add SetNode", + callback: () => {addNode("SetNode", this, { side:"right", offset: 30 }); + }, + }); + }); + } + }, + async setup(app) { + const updateSlots = (value) => { + const valuesToAddToIn = ["GetNode"]; + const valuesToAddToOut = ["SetNode"]; + // Remove entries if they exist + for (const arr of Object.values(LiteGraph.slot_types_default_in)) { + for (const valueToAdd of valuesToAddToIn) { + const idx = arr.indexOf(valueToAdd); + if (idx !== -1) { + arr.splice(idx, 1); + } + } + } + + for (const arr of Object.values(LiteGraph.slot_types_default_out)) { + for (const valueToAdd of valuesToAddToOut) { + const idx = arr.indexOf(valueToAdd); + if (idx !== -1) { + arr.splice(idx, 1); + } + } + } + if (value!="disabled") { + for (const arr of Object.values(LiteGraph.slot_types_default_in)) { + for (const valueToAdd of valuesToAddToIn) { + const idx = arr.indexOf(valueToAdd); + if (idx !== -1) { + arr.splice(idx, 1); + } + if (value === "top") { + arr.unshift(valueToAdd); + } else { + arr.push(valueToAdd); + } + } + } + + for (const arr of Object.values(LiteGraph.slot_types_default_out)) { + for (const valueToAdd of valuesToAddToOut) { + const idx = arr.indexOf(valueToAdd); + if (idx !== -1) { + arr.splice(idx, 1); + } + if (value === "top") { + arr.unshift(valueToAdd); + } else { + arr.push(valueToAdd); + } + } + } + } + }; + + app.ui.settings.addSetting({ + id: "KJNodes.SetGetMenu", + name: "KJNodes: Make Set/Get -nodes defaults", + tooltip: 'Adds Set/Get nodes to the top or bottom of the list of available node suggestions.', + options: ['disabled', 'top', 'bottom'], + defaultValue: 'disabled', + type: "combo", + onChange: updateSlots, + + }); + app.ui.settings.addSetting({ + id: "KJNodes.MiddleClickDefault", + name: "KJNodes: Middle click default node adding", + defaultValue: false, + type: "boolean", + onChange: (value) => { + LiteGraph.middle_click_slot_add_default_node = value; + }, + }); + app.ui.settings.addSetting({ + id: "KJNodes.nodeAutoColor", + name: "KJNodes: Automatically set node colors", + type: "boolean", + defaultValue: true, + }); + app.ui.settings.addSetting({ + id: "KJNodes.helpPopup", + name: "KJNodes: Help popups", + defaultValue: true, + type: "boolean", + }); + app.ui.settings.addSetting({ + id: "KJNodes.disablePrefix", + name: "KJNodes: Disable automatic Set_ and Get_ prefix", + defaultValue: true, + type: "boolean", + }); + app.ui.settings.addSetting({ + id: "KJNodes.browserStatus", + name: "KJNodes: 🟢 Stoplight browser status icon 🔴", + defaultValue: false, + type: "boolean", + }); +} +}); diff --git a/ComfyUI/custom_nodes/comfyui-kjnodes/web/js/fast_preview.js b/ComfyUI/custom_nodes/comfyui-kjnodes/web/js/fast_preview.js new file mode 100644 index 0000000000000000000000000000000000000000..822c1f745ba4e895364664f7dbed3d225f0430b2 --- /dev/null +++ b/ComfyUI/custom_nodes/comfyui-kjnodes/web/js/fast_preview.js @@ -0,0 +1,95 @@ +import { app } from '../../../scripts/app.js' + +//from melmass +export function makeUUID() { + let dt = new Date().getTime() + const uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => { + const r = ((dt + Math.random() * 16) % 16) | 0 + dt = Math.floor(dt / 16) + return (c === 'x' ? r : (r & 0x3) | 0x8).toString(16) + }) + return uuid +} + +function chainCallback(object, property, callback) { + if (object == undefined) { + //This should not happen. + console.error("Tried to add callback to non-existant object") + return; + } + if (property in object) { + const callback_orig = object[property] + object[property] = function () { + const r = callback_orig.apply(this, arguments); + callback.apply(this, arguments); + return r + }; + } else { + object[property] = callback; + } +} +app.registerExtension({ + name: 'KJNodes.FastPreview', + + async beforeRegisterNodeDef(nodeType, nodeData) { + if (nodeData?.name === 'FastPreview') { + chainCallback(nodeType.prototype, "onNodeCreated", function () { + + var element = document.createElement("div"); + this.uuid = makeUUID() + element.id = `fast-preview-${this.uuid}` + + this.previewWidget = this.addDOMWidget(nodeData.name, "FastPreviewWidget", element, { + serialize: false, + hideOnZoom: false, + }); + + this.previewer = new Previewer(this); + + this.setSize([550, 550]); + this.resizable = false; + this.previewWidget.parentEl = document.createElement("div"); + this.previewWidget.parentEl.className = "fast-preview"; + this.previewWidget.parentEl.id = `fast-preview-${this.uuid}` + element.appendChild(this.previewWidget.parentEl); + + chainCallback(this, "onExecuted", function (message) { + let bg_image = message["bg_image"]; + this.properties.imgData = { + name: "bg_image", + base64: bg_image + }; + this.previewer.refreshBackgroundImage(this); + }); + + + }); // onAfterGraphConfigured + }//node created + } //before register +})//register + +class Previewer { + constructor(context) { + this.node = context; + this.previousWidth = null; + this.previousHeight = null; + } + refreshBackgroundImage = () => { + const imgData = this.node?.properties?.imgData; + if (imgData?.base64) { + const base64String = imgData.base64; + const imageUrl = `data:${imgData.type};base64,${base64String}`; + const img = new Image(); + img.src = imageUrl; + img.onload = () => { + const { width, height } = img; + if (width !== this.previousWidth || height !== this.previousHeight) { + this.node.setSize([width, height]); + this.previousWidth = width; + this.previousHeight = height; + } + this.node.previewWidget.element.style.backgroundImage = `url(${imageUrl})`; + }; + } + }; + } \ No newline at end of file diff --git a/ComfyUI/custom_nodes/comfyui-kjnodes/web/js/help_popup.js b/ComfyUI/custom_nodes/comfyui-kjnodes/web/js/help_popup.js new file mode 100644 index 0000000000000000000000000000000000000000..c4734befd7726d6190b246e66140e99dfb9f7e65 --- /dev/null +++ b/ComfyUI/custom_nodes/comfyui-kjnodes/web/js/help_popup.js @@ -0,0 +1,326 @@ +import { app } from "../../../scripts/app.js"; + +// code based on mtb nodes by Mel Massadian https://github.com/melMass/comfy_mtb/ +export const loadScript = ( + FILE_URL, + async = true, + type = 'text/javascript', +) => { + return new Promise((resolve, reject) => { + try { + // Check if the script already exists + const existingScript = document.querySelector(`script[src="${FILE_URL}"]`) + if (existingScript) { + resolve({ status: true, message: 'Script already loaded' }) + return + } + + const scriptEle = document.createElement('script') + scriptEle.type = type + scriptEle.async = async + scriptEle.src = FILE_URL + + scriptEle.addEventListener('load', (ev) => { + resolve({ status: true }) + }) + + scriptEle.addEventListener('error', (ev) => { + reject({ + status: false, + message: `Failed to load the script ${FILE_URL}`, + }) + }) + + document.body.appendChild(scriptEle) + } catch (error) { + reject(error) + } + }) +} + +loadScript('kjweb_async/marked.min.js').catch((e) => { + console.log(e) +}) +loadScript('kjweb_async/purify.min.js').catch((e) => { + console.log(e) +}) + +const categories = ["KJNodes", "SUPIR", "VoiceCraft", "Marigold", "IC-Light", "WanVideoWrapper"]; +app.registerExtension({ + name: "KJNodes.HelpPopup", + async beforeRegisterNodeDef(nodeType, nodeData) { + + if (app.ui.settings.getSettingValue("KJNodes.helpPopup") === false) { + return; + } + try { + categories.forEach(category => { + if (nodeData?.category?.startsWith(category)) { + addDocumentation(nodeData, nodeType); + } + else return + }); + } catch (error) { + console.error("Error in registering KJNodes.HelpPopup", error); + } + }, +}); + +const create_documentation_stylesheet = () => { + const tag = 'kj-documentation-stylesheet' + + let styleTag = document.head.querySelector(tag) + + if (!styleTag) { + styleTag = document.createElement('style') + styleTag.type = 'text/css' + styleTag.id = tag + styleTag.innerHTML = ` + .kj-documentation-popup { + background: var(--comfy-menu-bg); + position: absolute; + color: var(--fg-color); + font: 12px monospace; + line-height: 1.5em; + padding: 10px; + border-radius: 10px; + border-style: solid; + border-width: medium; + border-color: var(--border-color); + z-index: 5; + overflow: hidden; + } + .content-wrapper { + overflow: auto; + max-height: 100%; + /* Scrollbar styling for Chrome */ + &::-webkit-scrollbar { + width: 6px; + } + &::-webkit-scrollbar-track { + background: var(--bg-color); + } + &::-webkit-scrollbar-thumb { + background-color: var(--fg-color); + border-radius: 6px; + border: 3px solid var(--bg-color); + } + + /* Scrollbar styling for Firefox */ + scrollbar-width: thin; + scrollbar-color: var(--fg-color) var(--bg-color); + a { + color: yellow; + } + a:visited { + color: orange; + } + a:hover { + color: red; + } + } + ` + document.head.appendChild(styleTag) + } + } + + /** Add documentation widget to the selected node */ + export const addDocumentation = ( + nodeData, + nodeType, + opts = { icon_size: 14, icon_margin: 4 },) => { + + opts = opts || {} + const iconSize = opts.icon_size ? opts.icon_size : 14 + const iconMargin = opts.icon_margin ? opts.icon_margin : 4 + let docElement = null + let contentWrapper = null + //if no description in the node python code, don't do anything + if (!nodeData.description) { + return + } + + const drawFg = nodeType.prototype.onDrawForeground + nodeType.prototype.onDrawForeground = function (ctx) { + const r = drawFg ? drawFg.apply(this, arguments) : undefined + if (this.flags.collapsed) return r + + // icon position + const x = this.size[0] - iconSize - iconMargin + + // create the popup + if (this.show_doc && docElement === null) { + docElement = document.createElement('div') + contentWrapper = document.createElement('div'); + docElement.appendChild(contentWrapper); + + create_documentation_stylesheet() + contentWrapper.classList.add('content-wrapper'); + docElement.classList.add('kj-documentation-popup') + + //parse the string from the python node code to html with marked, and sanitize the html with DOMPurify + contentWrapper.innerHTML = DOMPurify.sanitize(marked.parse(nodeData.description,)) + + // resize handle + const resizeHandle = document.createElement('div'); + resizeHandle.style.width = '0'; + resizeHandle.style.height = '0'; + resizeHandle.style.position = 'absolute'; + resizeHandle.style.bottom = '0'; + resizeHandle.style.right = '0'; + resizeHandle.style.cursor = 'se-resize'; + + // Add pseudo-elements to create a triangle shape + const borderColor = getComputedStyle(document.documentElement).getPropertyValue('--border-color').trim(); + resizeHandle.style.borderTop = '10px solid transparent'; + resizeHandle.style.borderLeft = '10px solid transparent'; + resizeHandle.style.borderBottom = `10px solid ${borderColor}`; + resizeHandle.style.borderRight = `10px solid ${borderColor}`; + + docElement.appendChild(resizeHandle) + let isResizing = false + let startX, startY, startWidth, startHeight + + resizeHandle.addEventListener('mousedown', function (e) { + e.preventDefault(); + e.stopPropagation(); + isResizing = true; + startX = e.clientX; + startY = e.clientY; + startWidth = parseInt(document.defaultView.getComputedStyle(docElement).width, 10); + startHeight = parseInt(document.defaultView.getComputedStyle(docElement).height, 10); + }, + { signal: this.docCtrl.signal }, + ); + + // close button + const closeButton = document.createElement('div'); + closeButton.textContent = '❌'; + closeButton.style.position = 'absolute'; + closeButton.style.top = '0'; + closeButton.style.right = '0'; + closeButton.style.cursor = 'pointer'; + closeButton.style.padding = '5px'; + closeButton.style.color = 'red'; + closeButton.style.fontSize = '12px'; + + docElement.appendChild(closeButton) + + closeButton.addEventListener('mousedown', (e) => { + e.stopPropagation(); + this.show_doc = !this.show_doc + docElement.parentNode.removeChild(docElement) + docElement = null + if (contentWrapper) { + contentWrapper.remove() + contentWrapper = null + } + }, + { signal: this.docCtrl.signal }, + ); + + document.addEventListener('mousemove', function (e) { + if (!isResizing) return; + const scale = app.canvas.ds.scale; + const newWidth = startWidth + (e.clientX - startX) / scale; + const newHeight = startHeight + (e.clientY - startY) / scale;; + docElement.style.width = `${newWidth}px`; + docElement.style.height = `${newHeight}px`; + }, + { signal: this.docCtrl.signal }, + ); + + document.addEventListener('mouseup', function () { + isResizing = false + }, + { signal: this.docCtrl.signal }, + ) + + document.body.appendChild(docElement) + } + // close the popup + else if (!this.show_doc && docElement !== null) { + docElement.parentNode.removeChild(docElement) + docElement = null + } + // update position of the popup + if (this.show_doc && docElement !== null) { + const rect = ctx.canvas.getBoundingClientRect() + const scaleX = rect.width / ctx.canvas.width + const scaleY = rect.height / ctx.canvas.height + + const transform = new DOMMatrix() + .scaleSelf(scaleX, scaleY) + .multiplySelf(ctx.getTransform()) + .translateSelf(this.size[0] * scaleX * Math.max(1.0,window.devicePixelRatio) , 0) + .translateSelf(10, -32) + + const scale = new DOMMatrix() + .scaleSelf(transform.a, transform.d); + const bcr = app.canvas.canvas.getBoundingClientRect() + + const styleObject = { + transformOrigin: '0 0', + transform: scale, + left: `${transform.a + bcr.x + transform.e}px`, + top: `${transform.d + bcr.y + transform.f}px`, + }; + Object.assign(docElement.style, styleObject); + } + + ctx.save() + ctx.translate(x - 2, iconSize - 34) + ctx.scale(iconSize / 32, iconSize / 32) + ctx.strokeStyle = 'rgba(255,255,255,0.3)' + ctx.lineCap = 'round' + ctx.lineJoin = 'round' + ctx.lineWidth = 2.4 + ctx.font = 'bold 36px monospace' + ctx.fillStyle = 'orange'; + ctx.fillText('?', 0, 24) + ctx.restore() + return r + } + // handle clicking of the icon + const mouseDown = nodeType.prototype.onMouseDown + nodeType.prototype.onMouseDown = function (e, localPos, canvas) { + const r = mouseDown ? mouseDown.apply(this, arguments) : undefined + const iconX = this.size[0] - iconSize - iconMargin + const iconY = iconSize - 34 + if ( + localPos[0] > iconX && + localPos[0] < iconX + iconSize && + localPos[1] > iconY && + localPos[1] < iconY + iconSize + ) { + if (this.show_doc === undefined) { + this.show_doc = true + } else { + this.show_doc = !this.show_doc + } + if (this.show_doc) { + this.docCtrl = new AbortController() + } else { + this.docCtrl.abort() + } + return true; + } + return r; + } + const onRem = nodeType.prototype.onRemoved + + nodeType.prototype.onRemoved = function () { + const r = onRem ? onRem.apply(this, []) : undefined + + if (docElement) { + docElement.remove() + docElement = null + } + + if (contentWrapper) { + contentWrapper.remove() + contentWrapper = null + } + return r + } +} \ No newline at end of file diff --git a/ComfyUI/custom_nodes/comfyui-kjnodes/web/js/point_editor.js b/ComfyUI/custom_nodes/comfyui-kjnodes/web/js/point_editor.js new file mode 100644 index 0000000000000000000000000000000000000000..6baa10830d5cba754eaa440685fdabc0c9d9f8e8 --- /dev/null +++ b/ComfyUI/custom_nodes/comfyui-kjnodes/web/js/point_editor.js @@ -0,0 +1,734 @@ +import { app } from '../../../scripts/app.js' + +//from melmass +export function makeUUID() { + let dt = new Date().getTime() + const uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => { + const r = ((dt + Math.random() * 16) % 16) | 0 + dt = Math.floor(dt / 16) + return (c === 'x' ? r : (r & 0x3) | 0x8).toString(16) + }) + return uuid +} + +export const loadScript = ( + FILE_URL, + async = true, + type = 'text/javascript', +) => { + return new Promise((resolve, reject) => { + try { + // Check if the script already exists + const existingScript = document.querySelector(`script[src="${FILE_URL}"]`) + if (existingScript) { + resolve({ status: true, message: 'Script already loaded' }) + return + } + + const scriptEle = document.createElement('script') + scriptEle.type = type + scriptEle.async = async + scriptEle.src = FILE_URL + + scriptEle.addEventListener('load', (ev) => { + resolve({ status: true }) + }) + + scriptEle.addEventListener('error', (ev) => { + reject({ + status: false, + message: `Failed to load the script ${FILE_URL}`, + }) + }) + + document.body.appendChild(scriptEle) + } catch (error) { + reject(error) + } + }) +} +const create_documentation_stylesheet = () => { + const tag = 'kj-pointseditor-stylesheet' + + let styleTag = document.head.querySelector(tag) + + if (!styleTag) { + styleTag = document.createElement('style') + styleTag.type = 'text/css' + styleTag.id = tag + styleTag.innerHTML = ` + .points-editor { + + position: absolute; + + font: 12px monospace; + line-height: 1.5em; + padding: 10px; + z-index: 0; + overflow: hidden; + } + ` + document.head.appendChild(styleTag) + } +} + +loadScript('kjweb_async/svg-path-properties.min.js').catch((e) => { + console.log(e) +}) +loadScript('kjweb_async/protovis.min.js').catch((e) => { + console.log(e) +}) +create_documentation_stylesheet() + +function chainCallback(object, property, callback) { + if (object == undefined) { + //This should not happen. + console.error("Tried to add callback to non-existant object") + return; + } + if (property in object) { + const callback_orig = object[property] + object[property] = function () { + const r = callback_orig.apply(this, arguments); + callback.apply(this, arguments); + return r + }; + } else { + object[property] = callback; + } +} +app.registerExtension({ + name: 'KJNodes.PointEditor', + + async beforeRegisterNodeDef(nodeType, nodeData) { + if (nodeData?.name === 'PointsEditor') { + chainCallback(nodeType.prototype, "onNodeCreated", function () { + + hideWidgetForGood(this, this.widgets.find(w => w.name === "coordinates")) + hideWidgetForGood(this, this.widgets.find(w => w.name === "neg_coordinates")) + hideWidgetForGood(this, this.widgets.find(w => w.name === "bboxes")) + + var element = document.createElement("div"); + this.uuid = makeUUID() + element.id = `points-editor-${this.uuid}` + + this.previewMediaType = 'image' + + this.pointsEditor = this.addDOMWidget(nodeData.name, "PointsEditorWidget", element, { + serialize: false, + hideOnZoom: false, + }); + + // context menu + this.contextMenu = document.createElement("div"); + this.contextMenu.id = "context-menu"; + this.contextMenu.style.display = "none"; + this.contextMenu.style.position = "absolute"; + this.contextMenu.style.backgroundColor = "#202020"; + this.contextMenu.style.minWidth = "100px"; + this.contextMenu.style.boxShadow = "0px 8px 16px 0px rgba(0,0,0,0.2)"; + this.contextMenu.style.zIndex = "100"; + this.contextMenu.style.padding = "5px"; + + function styleMenuItem(menuItem) { + menuItem.style.display = "block"; + menuItem.style.padding = "5px"; + menuItem.style.color = "#FFF"; + menuItem.style.fontFamily = "Arial, sans-serif"; + menuItem.style.fontSize = "16px"; + menuItem.style.textDecoration = "none"; + menuItem.style.marginBottom = "5px"; + } + function createMenuItem(id, textContent) { + let menuItem = document.createElement("a"); + menuItem.href = "#"; + menuItem.id = `menu-item-${id}`; + menuItem.textContent = textContent; + styleMenuItem(menuItem); + return menuItem; + } + + // Create an array of menu items using the createMenuItem function + this.menuItems = [ + createMenuItem(0, "Load Image"), + createMenuItem(1, "Clear Image"), + ]; + + // Add mouseover and mouseout event listeners to each menu item for styling + this.menuItems.forEach(menuItem => { + menuItem.addEventListener('mouseover', function () { + this.style.backgroundColor = "gray"; + }); + + menuItem.addEventListener('mouseout', function () { + this.style.backgroundColor = "#202020"; + }); + }); + + // Append each menu item to the context menu + this.menuItems.forEach(menuItem => { + this.contextMenu.appendChild(menuItem); + }); + + document.body.appendChild(this.contextMenu); + + this.addWidget("button", "New canvas", null, () => { + if (!this.properties || !("points" in this.properties)) { + this.editor = new PointsEditor(this); + this.addProperty("points", this.constructor.type, "string"); + this.addProperty("neg_points", this.constructor.type, "string"); + + } + else { + this.editor = new PointsEditor(this, true); + } + }); + + this.setSize([550, 550]); + this.resizable = false; + this.pointsEditor.parentEl = document.createElement("div"); + this.pointsEditor.parentEl.className = "points-editor"; + this.pointsEditor.parentEl.id = `points-editor-${this.uuid}` + element.appendChild(this.pointsEditor.parentEl); + + chainCallback(this, "onConfigure", function () { + try { + this.editor = new PointsEditor(this); + } catch (error) { + console.error("An error occurred while configuring the editor:", error); + } + }); + chainCallback(this, "onExecuted", function (message) { + let bg_image = message["bg_image"]; + this.properties.imgData = { + name: "bg_image", + base64: bg_image + }; + this.editor.refreshBackgroundImage(this); + }); + + }); // onAfterGraphConfigured + }//node created + } //before register +})//register + +class PointsEditor { + constructor(context, reset = false) { + this.node = context; + this.reset = reset; + const self = this; // Keep a reference to the main class context + + console.log("creatingPointEditor") + + this.node.pasteFile = (file) => { + if (file.type.startsWith("image/")) { + this.handleImageFile(file); + return true; + } + return false; + }; + + this.node.onDragOver = function (e) { + if (e.dataTransfer && e.dataTransfer.items) { + return [...e.dataTransfer.items].some(f => f.kind === "file" && f.type.startsWith("image/")); + } + return false; + }; + + // On drop upload files + this.node.onDragDrop = (e) => { + console.log("onDragDrop called"); + let handled = false; + for (const file of e.dataTransfer.files) { + if (file.type.startsWith("image/")) { + this.handleImageFile(file); + handled = true; + } + } + return handled; + }; + + // context menu + this.createContextMenu(); + + if (reset && context.pointsEditor.element) { + context.pointsEditor.element.innerHTML = ''; // Clear the container + } + this.pos_coordWidget = context.widgets.find(w => w.name === "coordinates"); + this.neg_coordWidget = context.widgets.find(w => w.name === "neg_coordinates"); + this.pointsStoreWidget = context.widgets.find(w => w.name === "points_store"); + this.widthWidget = context.widgets.find(w => w.name === "width"); + this.heightWidget = context.widgets.find(w => w.name === "height"); + this.bboxStoreWidget = context.widgets.find(w => w.name === "bbox_store"); + this.bboxWidget = context.widgets.find(w => w.name === "bboxes"); + + //widget callbacks + this.widthWidget.callback = () => { + this.width = this.widthWidget.value; + if (this.width > 256) { + context.setSize([this.width + 45, context.size[1]]); + } + this.vis.width(this.width); + this.updateData(); + } + this.heightWidget.callback = () => { + this.height = this.heightWidget.value + this.vis.height(this.height) + context.setSize([context.size[0], this.height + 300]); + this.updateData(); + } + this.pointsStoreWidget.callback = () => { + this.points = JSON.parse(pointsStoreWidget.value).positive; + this.neg_points = JSON.parse(pointsStoreWidget.value).negative; + this.updateData(); + } + this.bboxStoreWidget.callback = () => { + this.bbox = JSON.parse(bboxStoreWidget.value) + this.updateData(); + } + + this.width = this.widthWidget.value; + this.height = this.heightWidget.value; + var i = 3; + this.points = []; + this.neg_points = []; + this.bbox = [{}]; + var drawing = false; + + // Initialize or reset points array + if (!reset && this.pointsStoreWidget.value != "") { + this.points = JSON.parse(this.pointsStoreWidget.value).positive; + this.neg_points = JSON.parse(this.pointsStoreWidget.value).negative; + this.bbox = JSON.parse(this.bboxStoreWidget.value); + console.log(this.bbox) + } else { + this.points = [ + { + x: this.width / 2, // Middle point horizontally centered + y: this.height / 2 // Middle point vertically centered + } + ]; + this.neg_points = [ + { + x: 0, // Middle point horizontally centered + y: 0 // Middle point vertically centered + } + ]; + const combinedPoints = { + positive: this.points, + negative: this.neg_points, + }; + this.pointsStoreWidget.value = JSON.stringify(combinedPoints); + this.bboxStoreWidget.value = JSON.stringify(this.bbox); + } + + //create main canvas panel + this.vis = new pv.Panel() + .width(this.width) + .height(this.height) + .fillStyle("#222") + .strokeStyle("gray") + .lineWidth(2) + .antialias(false) + .margin(10) + .event("mousedown", function () { + if (pv.event.shiftKey && pv.event.button === 2) { // Use pv.event to access the event object + let scaledMouse = { + x: this.mouse().x / app.canvas.ds.scale, + y: this.mouse().y / app.canvas.ds.scale + }; + i = self.neg_points.push(scaledMouse) - 1; + self.updateData(); + return this; + } + else if (pv.event.shiftKey) { + let scaledMouse = { + x: this.mouse().x / app.canvas.ds.scale, + y: this.mouse().y / app.canvas.ds.scale + }; + i = self.points.push(scaledMouse) - 1; + self.updateData(); + return this; + } + else if (pv.event.ctrlKey) { + console.log("start drawing at " + this.mouse().x / app.canvas.ds.scale + ", " + this.mouse().y / app.canvas.ds.scale); + drawing = true; + self.bbox[0].startX = this.mouse().x / app.canvas.ds.scale; + self.bbox[0].startY = this.mouse().y / app.canvas.ds.scale; + } + else if (pv.event.button === 2) { + self.node.contextMenu.style.display = 'block'; + self.node.contextMenu.style.left = `${pv.event.clientX}px`; + self.node.contextMenu.style.top = `${pv.event.clientY}px`; + } + }) + .event("mousemove", function () { + if (drawing) { + self.bbox[0].endX = this.mouse().x / app.canvas.ds.scale; + self.bbox[0].endY = this.mouse().y / app.canvas.ds.scale; + self.vis.render(); + } + }) + .event("mouseup", function () { + console.log("end drawing at " + this.mouse().x / app.canvas.ds.scale + ", " + this.mouse().y / app.canvas.ds.scale); + drawing = false; + self.updateData(); + }); + + this.backgroundImage = this.vis.add(pv.Image).visible(false) + + //create bounding box + this.bounding_box = this.vis.add(pv.Area) + .data(function () { + if (drawing || (self.bbox && self.bbox[0] && Object.keys(self.bbox[0]).length > 0)) { + return [self.bbox[0].startX, self.bbox[0].endX]; + } else { + return []; + } + }) + .bottom(function () {return self.height - Math.max(self.bbox[0].startY, self.bbox[0].endY); }) + .left(function (d) {return d; }) + .height(function () {return Math.abs(self.bbox[0].startY - self.bbox[0].endY);}) + .fillStyle("rgba(70, 130, 180, 0.5)") + .strokeStyle("steelblue") + .visible(function () {return drawing || Object.keys(self.bbox[0]).length > 0; }) + .add(pv.Dot) + .visible(function () {return drawing || Object.keys(self.bbox[0]).length > 0; }) + .data(() => { + if (self.bbox && Object.keys(self.bbox[0]).length > 0) { + return [{ + x: self.bbox[0].endX, + y: self.bbox[0].endY + }]; + } else { + return []; + } + }) + .left(d => d.x) + .top(d => d.y) + .radius(Math.log(Math.min(self.width, self.height)) * 1) + .shape("square") + .cursor("move") + .strokeStyle("steelblue") + .lineWidth(2) + .fillStyle(function () { return "rgba(100, 100, 100, 0.6)"; }) + .event("mousedown", pv.Behavior.drag()) + .event("drag", function () { + let adjustedX = this.mouse().x / app.canvas.ds.scale; // Adjust the new position by the inverse of the scale factor + let adjustedY = this.mouse().y / app.canvas.ds.scale; + + // Adjust the new position if it would place the dot outside the bounds of the vis.Panel + adjustedX = Math.max(0, Math.min(self.vis.width(), adjustedX)); + adjustedY = Math.max(0, Math.min(self.vis.height(), adjustedY)); + self.bbox[0].endX = this.mouse().x / app.canvas.ds.scale; + self.bbox[0].endY = this.mouse().y / app.canvas.ds.scale; + self.vis.render(); + }) + .event("dragend", function () { + self.updateData(); + }); + + //create positive points + this.vis.add(pv.Dot) + .data(() => this.points) + .left(d => d.x) + .top(d => d.y) + .radius(Math.log(Math.min(self.width, self.height)) * 4) + .shape("circle") + .cursor("move") + .strokeStyle(function () { return i == this.index ? "#07f907" : "#139613"; }) + .lineWidth(4) + .fillStyle(function () { return "rgba(100, 100, 100, 0.6)"; }) + .event("mousedown", pv.Behavior.drag()) + .event("dragstart", function () { + i = this.index; + }) + .event("dragend", function () { + if (pv.event.button === 2 && i !== 0 && i !== self.points.length - 1) { + this.index = i; + self.points.splice(i--, 1); + } + self.updateData(); + + }) + .event("drag", function () { + let adjustedX = this.mouse().x / app.canvas.ds.scale; // Adjust the new X position by the inverse of the scale factor + let adjustedY = this.mouse().y / app.canvas.ds.scale; // Adjust the new Y position by the inverse of the scale factor + // Determine the bounds of the vis.Panel + const panelWidth = self.vis.width(); + const panelHeight = self.vis.height(); + + // Adjust the new position if it would place the dot outside the bounds of the vis.Panel + adjustedX = Math.max(0, Math.min(panelWidth, adjustedX)); + adjustedY = Math.max(0, Math.min(panelHeight, adjustedY)); + self.points[this.index] = { x: adjustedX, y: adjustedY }; // Update the point's position + self.vis.render(); // Re-render the visualization to reflect the new position + }) + + .anchor("center") + .add(pv.Label) + .left(d => d.x < this.width / 2 ? d.x + 30 : d.x - 35) // Shift label to right if on left half, otherwise shift to left + .top(d => d.y < this.height / 2 ? d.y + 25 : d.y - 25) // Shift label down if on top half, otherwise shift up + .font(25 + "px sans-serif") + .text(d => {return this.points.indexOf(d); }) + .textStyle("#139613") + .textShadow("2px 2px 2px black") + .add(pv.Dot) // Add smaller point in the center + .data(() => this.points) + .left(d => d.x) + .top(d => d.y) + .radius(2) // Smaller radius for the center point + .shape("circle") + .fillStyle("red") // Color for the center point + .lineWidth(1); // Stroke thickness for the center point + + //create negative points + this.vis.add(pv.Dot) + .data(() => this.neg_points) + .left(d => d.x) + .top(d => d.y) + .radius(Math.log(Math.min(self.width, self.height)) * 4) + .shape("circle") + .cursor("move") + .strokeStyle(function () { return i == this.index ? "#f91111" : "#891616"; }) + .lineWidth(4) + .fillStyle(function () { return "rgba(100, 100, 100, 0.6)"; }) + .event("mousedown", pv.Behavior.drag()) + .event("dragstart", function () { + i = this.index; + }) + .event("dragend", function () { + if (pv.event.button === 2 && i !== 0 && i !== self.neg_points.length - 1) { + this.index = i; + self.neg_points.splice(i--, 1); + } + self.updateData(); + + }) + .event("drag", function () { + let adjustedX = this.mouse().x / app.canvas.ds.scale; // Adjust the new X position by the inverse of the scale factor + let adjustedY = this.mouse().y / app.canvas.ds.scale; // Adjust the new Y position by the inverse of the scale factor + // Determine the bounds of the vis.Panel + const panelWidth = self.vis.width(); + const panelHeight = self.vis.height(); + + // Adjust the new position if it would place the dot outside the bounds of the vis.Panel + adjustedX = Math.max(0, Math.min(panelWidth, adjustedX)); + adjustedY = Math.max(0, Math.min(panelHeight, adjustedY)); + self.neg_points[this.index] = { x: adjustedX, y: adjustedY }; // Update the point's position + self.vis.render(); // Re-render the visualization to reflect the new position + }) + .anchor("center") + .add(pv.Label) + .left(d => d.x < this.width / 2 ? d.x + 30 : d.x - 35) // Shift label to right if on left half, otherwise shift to left + .top(d => d.y < this.height / 2 ? d.y + 25 : d.y - 25) // Shift label down if on top half, otherwise shift up + .font(25 + "px sans-serif") + .text(d => {return this.neg_points.indexOf(d); }) + .textStyle("red") + .textShadow("2px 2px 2px black") + .add(pv.Dot) // Add smaller point in the center + .data(() => this.neg_points) + .left(d => d.x) + .top(d => d.y) + .radius(2) // Smaller radius for the center point + .shape("circle") + .fillStyle("red") // Color for the center point + .lineWidth(1); // Stroke thickness for the center point + + if (this.points.length != 0) { + this.vis.render(); + } + + var svgElement = this.vis.canvas(); + svgElement.style['zIndex'] = "2" + svgElement.style['position'] = "relative" + this.node.pointsEditor.element.appendChild(svgElement); + + if (this.width > 256) { + this.node.setSize([this.width + 45, this.node.size[1]]); + } + this.node.setSize([this.node.size[0], this.height + 300]); + this.updateData(); + this.refreshBackgroundImage(); + + }//end constructor + + updateData = () => { + if (!this.points || this.points.length === 0) { + console.log("no points"); + return; + } + const combinedPoints = { + positive: this.points, + negative: this.neg_points, + }; + this.pointsStoreWidget.value = JSON.stringify(combinedPoints); + this.pos_coordWidget.value = JSON.stringify(this.points); + this.neg_coordWidget.value = JSON.stringify(this.neg_points); + + if (this.bbox.length != 0) { + let bboxString = JSON.stringify(this.bbox); + this.bboxStoreWidget.value = bboxString; + this.bboxWidget.value = bboxString; + } + + this.vis.render(); + }; + + handleImageLoad = (img, file, base64String) => { + console.log(img.width, img.height); // Access width and height here + this.widthWidget.value = img.width; + this.heightWidget.value = img.height; + + if (img.width != this.vis.width() || img.height != this.vis.height()) { + if (img.width > 256) { + this.node.setSize([img.width + 45, this.node.size[1]]); + } + this.node.setSize([this.node.size[0], img.height + 300]); + this.vis.width(img.width); + this.vis.height(img.height); + this.height = img.height; + this.width = img.width; + this.updateData(); + } + this.backgroundImage.url(file ? URL.createObjectURL(file) : `data:${this.node.properties.imgData.type};base64,${base64String}`).visible(true).root.render(); + }; + + processImage = (img, file) => { + const canvas = document.createElement('canvas'); + const ctx = canvas.getContext('2d'); + + const maxWidth = 800; // maximum width + const maxHeight = 600; // maximum height + let width = img.width; + let height = img.height; + + // Calculate the new dimensions while preserving the aspect ratio + if (width > height) { + if (width > maxWidth) { + height *= maxWidth / width; + width = maxWidth; + } + } else { + if (height > maxHeight) { + width *= maxHeight / height; + height = maxHeight; + } + } + + canvas.width = width; + canvas.height = height; + ctx.drawImage(img, 0, 0, width, height); + + // Get the compressed image data as a Base64 string + const base64String = canvas.toDataURL('image/jpeg', 0.5).replace('data:', '').replace(/^.+,/, ''); // 0.5 is the quality from 0 to 1 + + this.node.properties.imgData = { + name: file.name, + lastModified: file.lastModified, + size: file.size, + type: file.type, + base64: base64String + }; + handleImageLoad(img, file, base64String); +}; + + handleImageFile = (file) => { + const reader = new FileReader(); + reader.onloadend = () => { + const img = new Image(); + img.src = reader.result; + img.onload = () => processImage(img, file); + }; + reader.readAsDataURL(file); + + const imageUrl = URL.createObjectURL(file); + const img = new Image(); + img.src = imageUrl; + img.onload = () => this.handleImageLoad(img, file, null); + }; + + refreshBackgroundImage = () => { + if (this.node.properties.imgData && this.node.properties.imgData.base64) { + const base64String = this.node.properties.imgData.base64; + const imageUrl = `data:${this.node.properties.imgData.type};base64,${base64String}`; + const img = new Image(); + img.src = imageUrl; + img.onload = () => this.handleImageLoad(img, null, base64String); + } + }; + + createContextMenu = () => { + self = this; + document.addEventListener('contextmenu', function (e) { + e.preventDefault(); + }); + + document.addEventListener('click', function (e) { + if (!self.node.contextMenu.contains(e.target)) { + self.node.contextMenu.style.display = 'none'; + } + }); + + this.node.menuItems.forEach((menuItem, index) => { + self = this; + menuItem.addEventListener('click', function (e) { + e.preventDefault(); + switch (index) { + case 0: + // Create file input element + const fileInput = document.createElement('input'); + fileInput.type = 'file'; + fileInput.accept = 'image/*'; // Accept only image files + + // Listen for file selection + fileInput.addEventListener('change', function (event) { + const file = event.target.files[0]; // Get the selected file + + if (file) { + const imageUrl = URL.createObjectURL(file); + let img = new Image(); + img.src = imageUrl; + img.onload = () => self.handleImageLoad(img, file, null); + } + }); + + fileInput.click(); + + self.node.contextMenu.style.display = 'none'; + break; + case 1: + self.backgroundImage.visible(false).root.render(); + self.node.properties.imgData = null; + self.node.contextMenu.style.display = 'none'; + break; + } + }); + }); + }//end createContextMenu +}//end class + + +//from melmass +export function hideWidgetForGood(node, widget, suffix = '') { + widget.origType = widget.type + widget.origComputeSize = widget.computeSize + widget.origSerializeValue = widget.serializeValue + widget.computeSize = () => [0, -4] // -4 is due to the gap litegraph adds between widgets automatically + widget.type = "converted-widget" + suffix + // widget.serializeValue = () => { + // // Prevent serializing the widget if we have no input linked + // const w = node.inputs?.find((i) => i.widget?.name === widget.name); + // if (w?.link == null) { + // return undefined; + // } + // return widget.origSerializeValue ? widget.origSerializeValue() : widget.value; + // }; + + // Hide any linked widgets, e.g. seed+seedControl + if (widget.linkedWidgets) { + for (const w of widget.linkedWidgets) { + hideWidgetForGood(node, w, ':' + widget.name) + } + } +} \ No newline at end of file diff --git a/ComfyUI/custom_nodes/comfyui-kjnodes/web/js/setgetnodes.js b/ComfyUI/custom_nodes/comfyui-kjnodes/web/js/setgetnodes.js new file mode 100644 index 0000000000000000000000000000000000000000..c4531881378c51128b1885b212aabc70d2a8c602 --- /dev/null +++ b/ComfyUI/custom_nodes/comfyui-kjnodes/web/js/setgetnodes.js @@ -0,0 +1,565 @@ +import { app } from "../../../scripts/app.js"; + +//based on diffus3's SetGet: https://github.com/diffus3/ComfyUI-extensions + +// Nodes that allow you to tunnel connections for cleaner graphs +function setColorAndBgColor(type) { + const colorMap = { + "MODEL": LGraphCanvas.node_colors.blue, + "LATENT": LGraphCanvas.node_colors.purple, + "VAE": LGraphCanvas.node_colors.red, + "CONDITIONING": LGraphCanvas.node_colors.brown, + "IMAGE": LGraphCanvas.node_colors.pale_blue, + "CLIP": LGraphCanvas.node_colors.yellow, + "FLOAT": LGraphCanvas.node_colors.green, + "MASK": { color: "#1c5715", bgcolor: "#1f401b"}, + "INT": { color: "#1b4669", bgcolor: "#29699c"}, + "CONTROL_NET": { color: "#156653", bgcolor: "#1c453b"}, + "NOISE": { color: "#2e2e2e", bgcolor: "#242121"}, + "GUIDER": { color: "#3c7878", bgcolor: "#1c453b"}, + "SAMPLER": { color: "#614a4a", bgcolor: "#3b2c2c"}, + "SIGMAS": { color: "#485248", bgcolor: "#272e27"}, + + }; + + const colors = colorMap[type]; + if (colors) { + this.color = colors.color; + this.bgcolor = colors.bgcolor; + } +} +let disablePrefix = app.ui.settings.getSettingValue("KJNodes.disablePrefix") +const LGraphNode = LiteGraph.LGraphNode + +function showAlert(message) { + app.extensionManager.toast.add({ + severity: 'warn', + summary: "KJ Get/Set", + detail: `${message}. Most likely you're missing custom nodes`, + life: 5000, + }) +} +app.registerExtension({ + name: "SetNode", + registerCustomNodes() { + class SetNode extends LGraphNode { + defaultVisibility = true; + serialize_widgets = true; + drawConnection = false; + currentGetters = null; + slotColor = "#FFF"; + canvas = app.canvas; + menuEntry = "Show connections"; + + constructor(title) { + super(title) + if (!this.properties) { + this.properties = { + "previousName": "" + }; + } + this.properties.showOutputText = SetNode.defaultVisibility; + + const node = this; + + this.addWidget( + "text", + "Constant", + '', + (s, t, u, v, x) => { + node.validateName(node.graph); + if(this.widgets[0].value !== ''){ + this.title = (!disablePrefix ? "Set_" : "") + this.widgets[0].value; + } + this.update(); + this.properties.previousName = this.widgets[0].value; + }, + {} + ) + + this.addInput("*", "*"); + this.addOutput("*", '*'); + + this.onConnectionsChange = function( + slotType, //1 = input, 2 = output + slot, + isChangeConnect, + link_info, + output + ) { + //On Disconnect + if (slotType == 1 && !isChangeConnect) { + if(this.inputs[slot].name === ''){ + this.inputs[slot].type = '*'; + this.inputs[slot].name = '*'; + this.title = "Set" + } + } + if (slotType == 2 && !isChangeConnect) { + if (this.outputs && this.outputs[slot]) { + this.outputs[slot].type = '*'; + this.outputs[slot].name = '*'; + } + } + //On Connect + if (link_info && node.graph && slotType == 1 && isChangeConnect) { + const fromNode = node.graph._nodes.find((otherNode) => otherNode.id == link_info.origin_id); + + if (fromNode && fromNode.outputs && fromNode.outputs[link_info.origin_slot]) { + const type = fromNode.outputs[link_info.origin_slot].type; + + if (this.title === "Set"){ + this.title = (!disablePrefix ? "Set_" : "") + type; + } + if (this.widgets[0].value === '*'){ + this.widgets[0].value = type + } + + this.validateName(node.graph); + this.inputs[0].type = type; + this.inputs[0].name = type; + + if (app.ui.settings.getSettingValue("KJNodes.nodeAutoColor")){ + setColorAndBgColor.call(this, type); + } + } else { + showAlert("node input undefined.") + } + } + if (link_info && node.graph && slotType == 2 && isChangeConnect) { + const fromNode = node.graph._nodes.find((otherNode) => otherNode.id == link_info.origin_id); + + if (fromNode && fromNode.inputs && fromNode.inputs[link_info.origin_slot]) { + const type = fromNode.inputs[link_info.origin_slot].type; + + this.outputs[0].type = type; + this.outputs[0].name = type; + } else { + showAlert('node output undefined'); + } + } + + + //Update either way + this.update(); + } + + this.validateName = function(graph) { + let widgetValue = node.widgets[0].value; + + if (widgetValue !== '') { + let tries = 0; + const existingValues = new Set(); + + graph._nodes.forEach(otherNode => { + if (otherNode !== this && otherNode.type === 'SetNode') { + existingValues.add(otherNode.widgets[0].value); + } + }); + + while (existingValues.has(widgetValue)) { + widgetValue = node.widgets[0].value + "_" + tries; + tries++; + } + + node.widgets[0].value = widgetValue; + this.update(); + } + } + + this.clone = function () { + const cloned = SetNode.prototype.clone.apply(this); + cloned.inputs[0].name = '*'; + cloned.inputs[0].type = '*'; + cloned.value = ''; + cloned.properties.previousName = ''; + cloned.size = cloned.computeSize(); + return cloned; + }; + + this.onAdded = function(graph) { + this.validateName(graph); + } + + + this.update = function() { + if (!node.graph) { + return; + } + + const getters = this.findGetters(node.graph); + getters.forEach(getter => { + getter.setType(this.inputs[0].type); + }); + + if (this.widgets[0].value) { + const gettersWithPreviousName = this.findGetters(node.graph, true); + gettersWithPreviousName.forEach(getter => { + getter.setName(this.widgets[0].value); + }); + } + + const allGetters = node.graph._nodes.filter(otherNode => otherNode.type === "GetNode"); + allGetters.forEach(otherNode => { + if (otherNode.setComboValues) { + otherNode.setComboValues(); + } + }); + } + + + this.findGetters = function(graph, checkForPreviousName) { + const name = checkForPreviousName ? this.properties.previousName : this.widgets[0].value; + return graph._nodes.filter(otherNode => otherNode.type === 'GetNode' && otherNode.widgets[0].value === name && name !== ''); + } + + + // This node is purely frontend and does not impact the resulting prompt so should not be serialized + this.isVirtualNode = true; + } + + + onRemoved() { + const allGetters = this.graph._nodes.filter((otherNode) => otherNode.type == "GetNode"); + allGetters.forEach((otherNode) => { + if (otherNode.setComboValues) { + otherNode.setComboValues([this]); + } + }) + } + getExtraMenuOptions(_, options) { + this.menuEntry = this.drawConnection ? "Hide connections" : "Show connections"; + options.unshift( + { + content: this.menuEntry, + callback: () => { + this.currentGetters = this.findGetters(this.graph); + if (this.currentGetters.length == 0) return; + let linkType = (this.currentGetters[0].outputs[0].type); + this.slotColor = this.canvas.default_connection_color_byType[linkType] + this.menuEntry = this.drawConnection ? "Hide connections" : "Show connections"; + this.drawConnection = !this.drawConnection; + this.canvas.setDirty(true, true); + + }, + has_submenu: true, + submenu: { + title: "Color", + options: [ + { + content: "Highlight", + callback: () => { + this.slotColor = "orange" + this.canvas.setDirty(true, true); + } + } + ], + }, + }, + { + content: "Hide all connections", + callback: () => { + const allGetters = this.graph._nodes.filter(otherNode => otherNode.type === "GetNode" || otherNode.type === "SetNode"); + allGetters.forEach(otherNode => { + otherNode.drawConnection = false; + console.log(otherNode); + }); + + this.menuEntry = "Show connections"; + this.drawConnection = false + this.canvas.setDirty(true, true); + + }, + + }, + ); + // Dynamically add a submenu for all getters + this.currentGetters = this.findGetters(this.graph); + if (this.currentGetters) { + + let gettersSubmenu = this.currentGetters.map(getter => ({ + + content: `${getter.title} id: ${getter.id}`, + callback: () => { + this.canvas.centerOnNode(getter); + this.canvas.selectNode(getter, false); + this.canvas.setDirty(true, true); + + }, + })); + + options.unshift({ + content: "Getters", + has_submenu: true, + submenu: { + title: "GetNodes", + options: gettersSubmenu, + } + }); + } + } + + + onDrawForeground(ctx, lGraphCanvas) { + if (this.drawConnection) { + this._drawVirtualLinks(lGraphCanvas, ctx); + } + } + // onDrawCollapsed(ctx, lGraphCanvas) { + // if (this.drawConnection) { + // this._drawVirtualLinks(lGraphCanvas, ctx); + // } + // } + _drawVirtualLinks(lGraphCanvas, ctx) { + if (!this.currentGetters?.length) return; + var title = this.getTitle ? this.getTitle() : this.title; + var title_width = ctx.measureText(title).width; + if (!this.flags.collapsed) { + var start_node_slotpos = [ + this.size[0], + LiteGraph.NODE_TITLE_HEIGHT * 0.5, + ]; + } + else { + + var start_node_slotpos = [ + title_width + 55, + -15, + + ]; + } + // Provide a default link object with necessary properties, to avoid errors as link can't be null anymore + const defaultLink = { type: 'default', color: this.slotColor }; + + for (const getter of this.currentGetters) { + if (!this.flags.collapsed) { + var end_node_slotpos = this.getConnectionPos(false, 0); + end_node_slotpos = [ + getter.pos[0] - end_node_slotpos[0] + this.size[0], + getter.pos[1] - end_node_slotpos[1] + ]; + } + else { + var end_node_slotpos = this.getConnectionPos(false, 0); + end_node_slotpos = [ + getter.pos[0] - end_node_slotpos[0] + title_width + 50, + getter.pos[1] - end_node_slotpos[1] - 30 + ]; + } + lGraphCanvas.renderLink( + ctx, + start_node_slotpos, + end_node_slotpos, + defaultLink, + false, + null, + this.slotColor, + LiteGraph.RIGHT, + LiteGraph.LEFT + ); + } + } + } + + LiteGraph.registerNodeType( + "SetNode", + Object.assign(SetNode, { + title: "Set", + }) + ); + + SetNode.category = "KJNodes"; + }, +}); + +app.registerExtension({ + name: "GetNode", + registerCustomNodes() { + class GetNode extends LGraphNode { + + defaultVisibility = true; + serialize_widgets = true; + drawConnection = false; + slotColor = "#FFF"; + currentSetter = null; + canvas = app.canvas; + + constructor(title) { + super(title) + if (!this.properties) { + this.properties = {}; + } + this.properties.showOutputText = GetNode.defaultVisibility; + const node = this; + this.addWidget( + "combo", + "Constant", + "", + (e) => { + this.onRename(); + }, + { + values: () => { + const setterNodes = node.graph._nodes.filter((otherNode) => otherNode.type == 'SetNode'); + return setterNodes.map((otherNode) => otherNode.widgets[0].value).sort(); + } + } + ) + + this.addOutput("*", '*'); + this.onConnectionsChange = function( + slotType, //0 = output, 1 = input + slot, //self-explanatory + isChangeConnect, + link_info, + output + ) { + this.validateLinks(); + } + + this.setName = function(name) { + node.widgets[0].value = name; + node.onRename(); + node.serialize(); + } + + this.onRename = function() { + const setter = this.findSetter(node.graph); + if (setter) { + let linkType = (setter.inputs[0].type); + + this.setType(linkType); + this.title = (!disablePrefix ? "Get_" : "") + setter.widgets[0].value; + + if (app.ui.settings.getSettingValue("KJNodes.nodeAutoColor")){ + setColorAndBgColor.call(this, linkType); + } + + } else { + this.setType('*'); + } + } + + this.clone = function () { + const cloned = GetNode.prototype.clone.apply(this); + cloned.size = cloned.computeSize(); + return cloned; + }; + + this.validateLinks = function() { + if (this.outputs[0].type !== '*' && this.outputs[0].links) { + this.outputs[0].links.filter(linkId => { + const link = node.graph.links[linkId]; + return link && (!link.type.split(",").includes(this.outputs[0].type) && link.type !== '*'); + }).forEach(linkId => { + node.graph.removeLink(linkId); + }); + } + }; + + this.setType = function(type) { + this.outputs[0].name = type; + this.outputs[0].type = type; + this.validateLinks(); + } + + this.findSetter = function(graph) { + const name = this.widgets[0].value; + const foundNode = graph._nodes.find(otherNode => otherNode.type === 'SetNode' && otherNode.widgets[0].value === name && name !== ''); + return foundNode; + }; + + this.goToSetter = function() { + const setter = this.findSetter(this.graph); + this.canvas.centerOnNode(setter); + this.canvas.selectNode(setter, false); + }; + + // This node is purely frontend and does not impact the resulting prompt so should not be serialized + this.isVirtualNode = true; + } + + getInputLink(slot) { + const setter = this.findSetter(this.graph); + + if (setter) { + const slotInfo = setter.inputs[slot]; + const link = this.graph.links[slotInfo.link]; + return link; + } else { + const errorMessage = "No SetNode found for " + this.widgets[0].value + "(" + this.type + ")"; + showAlert(errorMessage); + //throw new Error(errorMessage); + } + } + onAdded(graph) { + } + getExtraMenuOptions(_, options) { + let menuEntry = this.drawConnection ? "Hide connections" : "Show connections"; + + options.unshift( + { + content: "Go to setter", + callback: () => { + this.goToSetter(); + }, + }, + { + content: menuEntry, + callback: () => { + this.currentSetter = this.findSetter(this.graph); + if (this.currentSetter.length == 0) return; + let linkType = (this.currentSetter.inputs[0].type); + this.drawConnection = !this.drawConnection; + this.slotColor = this.canvas.default_connection_color_byType[linkType] + menuEntry = this.drawConnection ? "Hide connections" : "Show connections"; + this.canvas.setDirty(true, true); + }, + }, + ); + } + + onDrawForeground(ctx, lGraphCanvas) { + if (this.drawConnection) { + this._drawVirtualLink(lGraphCanvas, ctx); + } + } + // onDrawCollapsed(ctx, lGraphCanvas) { + // if (this.drawConnection) { + // this._drawVirtualLink(lGraphCanvas, ctx); + // } + // } + _drawVirtualLink(lGraphCanvas, ctx) { + if (!this.currentSetter) return; + + // Provide a default link object with necessary properties, to avoid errors as link can't be null anymore + const defaultLink = { type: 'default', color: this.slotColor }; + + let start_node_slotpos = this.currentSetter.getConnectionPos(false, 0); + start_node_slotpos = [ + start_node_slotpos[0] - this.pos[0], + start_node_slotpos[1] - this.pos[1], + ]; + let end_node_slotpos = [0, -LiteGraph.NODE_TITLE_HEIGHT * 0.5]; + lGraphCanvas.renderLink( + ctx, + start_node_slotpos, + end_node_slotpos, + defaultLink, + false, + null, + this.slotColor + ); + } + } + + LiteGraph.registerNodeType( + "GetNode", + Object.assign(GetNode, { + title: "Get", + }) + ); + + GetNode.category = "KJNodes"; + }, +}); diff --git a/ComfyUI/custom_nodes/comfyui-kjnodes/web/js/spline_editor.js b/ComfyUI/custom_nodes/comfyui-kjnodes/web/js/spline_editor.js new file mode 100644 index 0000000000000000000000000000000000000000..cc095990a7ab48b4ea930563e0755982ddc39cf5 --- /dev/null +++ b/ComfyUI/custom_nodes/comfyui-kjnodes/web/js/spline_editor.js @@ -0,0 +1,1379 @@ +import { app } from '../../../scripts/app.js' + +//from melmass +export function makeUUID() { + let dt = new Date().getTime() + const uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => { + const r = ((dt + Math.random() * 16) % 16) | 0 + dt = Math.floor(dt / 16) + return (c === 'x' ? r : (r & 0x3) | 0x8).toString(16) + }) + return uuid +} + +export const loadScript = ( + FILE_URL, + async = true, + type = 'text/javascript', + ) => { + return new Promise((resolve, reject) => { + try { + // Check if the script already exists + const existingScript = document.querySelector(`script[src="${FILE_URL}"]`) + if (existingScript) { + resolve({ status: true, message: 'Script already loaded' }) + return + } + + const scriptEle = document.createElement('script') + scriptEle.type = type + scriptEle.async = async + scriptEle.src = FILE_URL + + scriptEle.addEventListener('load', (ev) => { + resolve({ status: true }) + }) + + scriptEle.addEventListener('error', (ev) => { + reject({ + status: false, + message: `Failed to load the script ${FILE_URL}`, + }) + }) + + document.body.appendChild(scriptEle) + } catch (error) { + reject(error) + } + }) + } + const create_documentation_stylesheet = () => { + const tag = 'kj-splineditor-stylesheet' + + let styleTag = document.head.querySelector(tag) + + if (!styleTag) { + styleTag = document.createElement('style') + styleTag.type = 'text/css' + styleTag.id = tag + styleTag.innerHTML = ` + .spline-editor { + + position: absolute; + + font: 12px monospace; + line-height: 1.5em; + padding: 10px; + z-index: 0; + overflow: hidden; + } + ` + document.head.appendChild(styleTag) + } + } + +loadScript('kjweb_async/svg-path-properties.min.js').catch((e) => { + console.log(e) +}) +loadScript('kjweb_async/protovis.min.js').catch((e) => { + console.log(e) +}) +create_documentation_stylesheet() + +function chainCallback(object, property, callback) { + if (object == undefined) { + //This should not happen. + console.error("Tried to add callback to non-existant object") + return; + } + if (property in object) { + const callback_orig = object[property] + object[property] = function () { + const r = callback_orig.apply(this, arguments); + callback.apply(this, arguments); + return r + }; + } else { + object[property] = callback; + } +} +app.registerExtension({ + name: 'KJNodes.SplineEditor', + + async beforeRegisterNodeDef(nodeType, nodeData) { + if (nodeData?.name === 'SplineEditor') { + chainCallback(nodeType.prototype, "onNodeCreated", function () { + + this.widgets.find(w => w.name === "coordinates").hidden = true + + var element = document.createElement("div"); + this.uuid = makeUUID() + element.id = `spline-editor-${this.uuid}` + + this.previewMediaType = 'image' + + this.splineEditor = this.addDOMWidget(nodeData.name, "SplineEditorWidget", element, { + serialize: false, + hideOnZoom: false, + }); + + // context menu + this.contextMenu = document.createElement("div"); + this.contextMenu.className = 'spline-editor-context-menu'; + this.contextMenu.id = "context-menu"; + this.contextMenu.style.display = "none"; + this.contextMenu.style.position = "absolute"; + this.contextMenu.style.backgroundColor = "#202020"; + this.contextMenu.style.minWidth = "100px"; + this.contextMenu.style.boxShadow = "0px 8px 16px 0px rgba(0,0,0,0.2)"; + this.contextMenu.style.zIndex = "100"; + this.contextMenu.style.padding = "5px"; + + function styleMenuItem(menuItem) { + menuItem.style.display = "block"; + menuItem.style.padding = "5px"; + menuItem.style.color = "#FFF"; + menuItem.style.fontFamily = "Arial, sans-serif"; + menuItem.style.fontSize = "16px"; + menuItem.style.textDecoration = "none"; + menuItem.style.marginBottom = "5px"; + } + function createMenuItem(id, textContent) { + let menuItem = document.createElement("a"); + menuItem.href = "#"; + menuItem.id = `menu-item-${id}`; + menuItem.textContent = textContent; + styleMenuItem(menuItem); + return menuItem; + } + + // Create an array of menu items using the createMenuItem function + this.menuItems = [ + createMenuItem(0, "Toggle handles"), + createMenuItem(1, "Display sample points"), + createMenuItem(2, "Switch point shape"), + createMenuItem(3, "Background image"), + createMenuItem(4, "Invert point order"), + createMenuItem(5, "Clear Image"), + createMenuItem(6, "Add new spline"), + createMenuItem(7, "Add new single point"), + createMenuItem(8, "Delete current spline"), + createMenuItem(9, "Next spline"), + ]; + + // Add mouseover and mouseout event listeners to each menu item for styling + this.menuItems.forEach(menuItem => { + menuItem.addEventListener('mouseover', function() { + this.style.backgroundColor = "gray"; + }); + + menuItem.addEventListener('mouseout', function() { + this.style.backgroundColor = "#202020"; + }); + }); + + // Append each menu item to the context menu + this.menuItems.forEach(menuItem => { + this.contextMenu.appendChild(menuItem); + }); + + document.body.appendChild(this.contextMenu); + + this.addWidget("button", "New canvas", null, () => { + if (!this.properties || !("points" in this.properties)) { + this.editor = new SplineEditor(this); + this.addProperty("points", this.constructor.type, "string"); + } + else { + this.editor = new SplineEditor(this, true); + } + }); + + this.setSize([550, 1000]); + this.resizable = false; + this.splineEditor.parentEl = document.createElement("div"); + this.splineEditor.parentEl.className = "spline-editor"; + this.splineEditor.parentEl.id = `spline-editor-${this.uuid}` + element.appendChild(this.splineEditor.parentEl); + + chainCallback(this, "onConfigure", function () { + try { + this.editor = new SplineEditor(this); + } catch (error) { + console.error("An error occurred while configuring the editor:", error); + } + }); + chainCallback(this, "onExecuted", function (message) { + let bg_image = message["bg_image"]; + this.properties.imgData = { + name: "bg_image", + base64: bg_image + }; + this.editor.refreshBackgroundImage(this); + }); + + }); // onAfterGraphConfigured + }//node created + } //before register +})//register + + +class SplineEditor{ + constructor(context, reset = false) { + this.node = context; + this.reset=reset; + const self = this; + console.log("creatingSplineEditor") + + this.node.pasteFile = (file) => { + if (file.type.startsWith("image/")) { + this.handleImageFile(file); + return true; + } + return false; + }; + + this.node.onDragOver = function (e) { + if (e.dataTransfer && e.dataTransfer.items) { + return [...e.dataTransfer.items].some(f => f.kind === "file" && f.type.startsWith("image/")); + } + return false; + }; + + // On drop upload files + this.node.onDragDrop = (e) => { + console.log("onDragDrop called"); + let handled = false; + for (const file of e.dataTransfer.files) { + if (file.type.startsWith("image/")) { + this.handleImageFile(file); + handled = true; + } + } + return handled; + }; + + // context menu + this.createContextMenu(); + + + this.dotShape = "circle"; + this.drawSamplePoints = false; + + if (reset && context.splineEditor.element) { + context.splineEditor.element.innerHTML = ''; // Clear the container + } + this.coordWidget = context.widgets.find(w => w.name === "coordinates"); + this.interpolationWidget = context.widgets.find(w => w.name === "interpolation"); + this.pointsWidget = context.widgets.find(w => w.name === "points_to_sample"); + this.pointsStoreWidget = context.widgets.find(w => w.name === "points_store"); + this.tensionWidget = context.widgets.find(w => w.name === "tension"); + this.minValueWidget = context.widgets.find(w => w.name === "min_value"); + this.maxValueWidget = context.widgets.find(w => w.name === "max_value"); + this.samplingMethodWidget = context.widgets.find(w => w.name === "sampling_method"); + this.widthWidget = context.widgets.find(w => w.name === "mask_width"); + this.heightWidget = context.widgets.find(w => w.name === "mask_height"); + + this.interpolation = this.interpolationWidget.value + this.tension = this.tensionWidget.value + this.points_to_sample = this.pointsWidget.value + this.rangeMin = this.minValueWidget.value + this.rangeMax = this.maxValueWidget.value + this.pointsLayer = null; + this.samplingMethod = this.samplingMethodWidget.value + + if (this.samplingMethod == "path"||this.samplingMethod == "speed") { + this.dotShape = "triangle" + } + + + this.interpolationWidget.callback = () => { + this.interpolation = this.interpolationWidget.value + this.updatePath(); + } + this.samplingMethodWidget.callback = () => { + this.samplingMethod = this.samplingMethodWidget.value + if (this.samplingMethod == "path") { + this.dotShape = "triangle" + } + else if (this.samplingMethod == "controlpoints") { + this.dotShape = "circle" + this.drawSamplePoints = true; + } + this.updatePath(); + } + this.tensionWidget.callback = () => { + this.tension = this.tensionWidget.value + this.updatePath(); + } + this.pointsWidget.callback = () => { + this.points_to_sample = this.pointsWidget.value + this.updatePath(); + } + this.minValueWidget.callback = () => { + this.rangeMin = this.minValueWidget.value + this.updatePath(); + } + this.maxValueWidget.callback = () => { + this.rangeMax = this.maxValueWidget.value + this.updatePath(); + } + this.widthWidget.callback = () => { + this.width = this.widthWidget.value; + if (this.width > 256) { + context.setSize([this.width + 45, context.size[1]]); + } + this.vis.width(this.width); + this.updatePath(); +} +this.heightWidget.callback = () => { + this.height = this.heightWidget.value + this.vis.height(this.height) + context.setSize([context.size[0], this.height + 450]); + this.updatePath(); + } + this.pointsStoreWidget.callback = () => { + points = JSON.parse(this.pointsStoreWidget.value); + this.updatePath(); + } + + // Initialize or reset points array + this.drawHandles = false; + this.drawRuler = true; + var hoverIndex = -1; + var isDragging = false; + this.width = this.widthWidget.value; + this.height = this.heightWidget.value; + var i = 3; + this.splines = []; + this.activeSplineIndex = 0; // Track which spline is being edited + // init mouse position +this.lastMousePosition = { x: this.width/2, y: this.height/2 }; + + if (!reset && this.pointsStoreWidget.value != "") { + try { + const parsedData = JSON.parse(this.pointsStoreWidget.value); + // Check if it's already in the new format (array of splines) + if (Array.isArray(parsedData) && parsedData.length > 0 && parsedData[0].hasOwnProperty('points')) { + this.splines = parsedData; + } else { + // Convert old format (single array of points) to new format + this.splines = [{ + points: parsedData, + color: "#1f77b4", + name: "Spline 1" + }]; + } + } catch (e) { + console.error("Error parsing spline data:", e); + this.initializeDefaultSplines(); + } +} else { + this.initializeDefaultSplines(); + this.pointsStoreWidget.value = JSON.stringify(this.splines); +} + + this.vis = new pv.Panel() + .width(this.width) + .height(this.height) + .fillStyle("#222") + .strokeStyle("gray") + .lineWidth(2) + .antialias(false) + .margin(10) + .event("mousedown", function () { + if (pv.event.shiftKey) { // Use pv.event to access the event object + let scaledMouse = { + x: this.mouse().x / app.canvas.ds.scale, + y: this.mouse().y / app.canvas.ds.scale + }; + i = self.splines[self.activeSplineIndex].points.push(scaledMouse) - 1; + self.updatePath(); + return this; + } + else if (pv.event.ctrlKey) { + // Capture the clicked location + let clickedPoint = { + x: this.mouse().x / app.canvas.ds.scale, + y: this.mouse().y / app.canvas.ds.scale + }; + + // Find the two closest points to the clicked location + const activePoints = self.splines[self.activeSplineIndex].points; + let { point1Index, point2Index } = self.findClosestPoints(self.splines[self.activeSplineIndex].points, clickedPoint); + + // Calculate the midpoint between the two closest points + let midpoint = { + x: (activePoints[point1Index].x + activePoints[point2Index].x) / 2, + y: (activePoints[point1Index].y + activePoints[point2Index].y) / 2 + }; + + // Insert the midpoint into the array + activePoints.splice(point2Index, 0, midpoint); + i = point2Index; + self.updatePath(); + } + else if (pv.event.button === 2) { + // Store the current mouse position adjusted for scale + self.lastMousePosition = { + x: this.mouse().x / app.canvas.ds.scale, + y: this.mouse().y / app.canvas.ds.scale + }; + + self.node.contextMenu.style.display = 'block'; + self.node.contextMenu.style.left = `${pv.event.clientX}px`; + self.node.contextMenu.style.top = `${pv.event.clientY}px`; + } + }) + this.backgroundImage = this.vis.add(pv.Image).visible(false) + + this.vis.add(pv.Rule) + .data(pv.range(0, this.height, 64)) + .bottom(d => d) + .strokeStyle("gray") + .lineWidth(3) + .visible(() => self.drawRuler) + + this.hoverSplineIndex = -1; + + this.splines.forEach((spline, splineIndex) => { + const strokeObj = this.vis.add(pv.Line) + .data(() => spline.points) + .left(d => d.x) + .top(d => d.y) + .interpolate(() => this.interpolation) + .tension(() => this.tension) + .segmented(() => false) + .strokeStyle("black") // Stroke color + .lineWidth(() => { + // Make stroke slightly wider than the main line + if (splineIndex === this.activeSplineIndex) return 5; + if (splineIndex === this.hoverSplineIndex) return 4; + return 3.5; + }); + + this.vis.add(pv.Line) + .data(() => spline.points) + .left(d => d.x) + .top(d => d.y) + .interpolate(() => this.interpolation) + .tension(() => this.tension) + .segmented(() => false) + .strokeStyle(spline.color) + .lineWidth(() => { + // Change line width based on active or hover state + if (splineIndex === this.activeSplineIndex) return 3; + if (splineIndex === this.hoverSplineIndex) return 2; + return 1.5; + }) + .event("mouseover", () => { + this.hoverSplineIndex = splineIndex; + this.vis.render(); + }) + .event("mouseout", () => { + this.hoverSplineIndex = -1; + this.vis.render(); + }) + .event("mousedown", () => { + if (this.activeSplineIndex !== splineIndex) { + this.activeSplineIndex = splineIndex; + this.refreshSplineElements(); + } + }); + }); + + this.vis.add(pv.Dot) + .data(() => { + const activeSpline = this.splines[this.activeSplineIndex]; + // If this is a single point, don't show it in the main visualization + if (activeSpline.isSinglePoint || (activeSpline.points && activeSpline.points.length === 1)) { + return []; // Return empty array to hide in main visualization + } + return activeSpline.points; + }) + .left(d => d.x) + .top(d => d.y) + .radius(12) + .shape(function() { + return self.dotShape; + }) + .angle(function() { + const index = this.index; + let angle = 0; + + if (self.dotShape === "triangle") { + const activePoints = self.splines[self.activeSplineIndex].points; + let dxNext = 0, dyNext = 0; + if (index < activePoints.length - 1) { + dxNext = activePoints[index + 1].x - activePoints[index].x; + dyNext = activePoints[index + 1].y - activePoints[index].y; + } + + let dxPrev = 0, dyPrev = 0; + if (index > 0) { + dxPrev = activePoints[index].x - activePoints[index - 1].x; + dyPrev = activePoints[index].y - activePoints[index - 1].y; + } + + const dx = (dxNext + dxPrev) / 2; + const dy = (dyNext + dyPrev) / 2; + + angle = Math.atan2(dy, dx); + angle -= Math.PI / 2; + angle = (angle + 2 * Math.PI) % (2 * Math.PI); + } + + return angle; + }) + .cursor("move") + .strokeStyle(function () { return i == this.index ? "#ff7f0e" : "#1f77b4"; }) + .fillStyle(function () { return "rgba(100, 100, 100, 0.3)"; }) + .event("mousedown", pv.Behavior.drag()) + .event("dragstart", function () { + i = this.index; + hoverIndex = this.index; + isDragging = true; + const activePoints = self.splines[self.activeSplineIndex].points; + if (pv.event.button === 2 && i !== 0 && i !== activePoints.length - 1) { + activePoints.splice(i--, 1); + self.vis.render(); + } + return this; + }) + .event("dragend", function() { + if (this.pathElements !== null) { + self.updatePath(); + } + isDragging = false; + }) + .event("drag", function () { + let adjustedX = this.mouse().x / app.canvas.ds.scale; // Adjust the new X position by the inverse of the scale factor + let adjustedY = this.mouse().y / app.canvas.ds.scale; // Adjust the new Y position by the inverse of the scale factor + // Determine the bounds of the vis.Panel + const panelWidth = self.vis.width(); + const panelHeight = self.vis.height(); + + // Adjust the new position if it would place the dot outside the bounds of the vis.Panel + adjustedX = Math.max(0, Math.min(panelWidth, adjustedX)); + adjustedY = Math.max(0, Math.min(panelHeight, adjustedY)); + self.splines[self.activeSplineIndex].points[this.index] = { x: adjustedX, y: adjustedY }; // Update the point's position + self.vis.render(); // Re-render the visualization to reflect the new position + }) + .event("mouseover", function() { + hoverIndex = this.index; // Set the hover index to the index of the hovered dot + self.vis.render(); // Re-render the visualization + }) + .event("mouseout", function() { + !isDragging && (hoverIndex = -1); // Reset the hover index when the mouse leaves the dot + self.vis.render(); // Re-render the visualization + }) + .anchor("center") + .add(pv.Label) + .visible(function() { + return hoverIndex === this.index; // Only show the label for the hovered dot + }) + .left(d => d.x < this.width / 2 ? d.x + 80 : d.x - 70) // Shift label to right if on left half, otherwise shift to left + .top(d => d.y < this.height / 2 ? d.y + 20 : d.y - 20) // Shift label down if on top half, otherwise shift up + .font(12 + "px sans-serif") + .text(d => { + if (this.samplingMethod == "path") { + return `X: ${Math.round(d.x)}, Y: ${Math.round(d.y)}`; + } else { + let frame = Math.round((d.x / self.width) * self.points_to_sample); + let normalizedY = (1.0 - (d.y / self.height) - 0.0) * (self.rangeMax - self.rangeMin) + self.rangeMin; + let normalizedX = (d.x / self.width); + return `F: ${frame}, X: ${normalizedX.toFixed(2)}, Y: ${normalizedY.toFixed(2)}`; + } + }) + .textStyle("orange") + + // single points + this.vis.add(pv.Dot) + .data(() => { + // Collect all single points from all splines + const singlePoints = []; + this.splines.forEach((spline, splineIndex) => { + if (spline.isSinglePoint || (spline.points && spline.points.length === 1)) { + singlePoints.push({ + x: spline.points[0].x, + y: spline.points[0].y, + splineIndex: splineIndex, + color: spline.color + }); + } + }); + return singlePoints; + }) + .left(d => d.x) + .top(d => d.y) + .radius(6) + .shape("square") + .strokeStyle(d => d.splineIndex === this.activeSplineIndex ? "#ff7f0e" : d.color) + .fillStyle(d => "rgba(100, 100, 100, 0.9)") + .lineWidth(d => d.splineIndex === this.activeSplineIndex ? 3 : 1.5) + .cursor("move") + .event("mousedown", pv.Behavior.drag()) + .event("dragstart", function(d) { + self.activeSplineIndex = d.splineIndex; + self.refreshSplineElements(); + return this; + }) + .event("drag", function(d) { + let adjustedX = this.mouse().x / app.canvas.ds.scale; + let adjustedY = this.mouse().y / app.canvas.ds.scale; + + // Determine the bounds of the vis.Panel + const panelWidth = self.vis.width(); + const panelHeight = self.vis.height(); + + // Adjust the new position if it would place the dot outside the bounds + adjustedX = Math.max(0, Math.min(panelWidth, adjustedX)); + adjustedY = Math.max(0, Math.min(panelHeight, adjustedY)); + + // Update the point position + const spline = self.splines[d.splineIndex]; + spline.points[0] = { x: adjustedX, y: adjustedY }; + + // For single points, we need to refresh the entire spline element + // to prevent the line-drawing effect + + }) + .event("dragend", function(d) { + self.refreshSplineElements(); + self.updatePath(); + }) + .visible(d => true); // Make always visible + + if (this.splines.length != 0) { + this.vis.render(); + } + var svgElement = this.vis.canvas(); + svgElement.style['zIndex'] = "2" + svgElement.style['position'] = "relative" + this.node.splineEditor.element.appendChild(svgElement); + this.pathElements = svgElement.getElementsByTagName('path'); // Get all path elements + + if (this.width > 256) { + this.node.setSize([this.width + 45, this.node.size[1]]); + } + this.node.setSize([this.node.size[0], this.height + 450]); + this.updatePath(); + this.refreshBackgroundImage(); + } + + updatePath = () => { + if (!this.splines || this.splines.length === 0) { + console.log("no splines"); + return; + } + // Get active spline points + console.log("this.activeSplineIndex", this.activeSplineIndex); + const activeSpline = this.splines[this.activeSplineIndex]; + const activePoints = activeSpline.points; + + if (!activePoints || activePoints.length === 0) { + console.log("no points in active spline"); + return; + } + + + let coords; + if (this.samplingMethod != "controlpoints") { + coords = this.samplePoints(this.pathElements[this.activeSplineIndex], this.points_to_sample, this.samplingMethod, this.width, this.activeSplineIndex); + } else { + coords = activePoints; + } + + let allSplineCoords = []; + for (let i = 0; i < this.splines.length; i++) { + // Use the same sampling method for all splines + let splineCoords; + const pathElement = this.pathElements[i]; + + if (this.samplingMethod != "controlpoints" && pathElement) { + splineCoords = this.samplePoints(pathElement, this.points_to_sample, this.samplingMethod, this.width, i); + } else { + // Fall back to control points if no path element or sampling method is "controlpoints" + splineCoords = this.splines[i].points; + } + + allSplineCoords.push(splineCoords); + } + + if (this.drawSamplePoints) { + if (this.pointsLayer) { + // Update the data of the existing points layer + this.pointsLayer.data(coords); + } else { + // Create the points layer if it doesn't exist + this.pointsLayer = this.vis.add(pv.Dot) + .data(coords) + .left(function(d) { return d.x; }) + .top(function(d) { return d.y; }) + .radius(5) // Adjust the radius as needed + .fillStyle("red") // Change the color as needed + .strokeStyle("black") // Change the stroke color as needed + .lineWidth(1); // Adjust the line width as needed + } + } else { + if (this.pointsLayer) { + // Remove the points layer + this.pointsLayer.data([]); + this.vis.render(); + } + } + this.pointsStoreWidget.value = JSON.stringify(this.splines); + if (this.coordWidget) { + this.coordWidget.value = JSON.stringify(allSplineCoords); + } + this.vis.render(); + }; + + handleImageLoad = (img, file, base64String) => { + //console.log(img.width, img.height); // Access width and height here + this.widthWidget.value = img.width; + this.heightWidget.value = img.height; + this.drawRuler = false; + + if (img.width != this.vis.width() || img.height != this.vis.height()) { + if (img.width > 256) { + this.node.setSize([img.width + 45, this.node.size[1]]); + } + this.node.setSize([this.node.size[0], img.height + 520]); + this.vis.width(img.width); + this.vis.height(img.height); + this.height = img.height; + this.width = img.width; + + this.updatePath(); + } + this.backgroundImage.url(file ? URL.createObjectURL(file) : `data:${this.node.properties.imgData.type};base64,${base64String}`).visible(true).root.render(); + }; + + processImage = (img, file) => { + const canvas = document.createElement('canvas'); + const ctx = canvas.getContext('2d'); + + const maxWidth = 800; // maximum width + const maxHeight = 600; // maximum height + let width = img.width; + let height = img.height; + + // Calculate the new dimensions while preserving the aspect ratio + if (width > height) { + if (width > maxWidth) { + height *= maxWidth / width; + width = maxWidth; + } + } else { + if (height > maxHeight) { + width *= maxHeight / height; + height = maxHeight; + } + } + + canvas.width = width; + canvas.height = height; + ctx.drawImage(img, 0, 0, width, height); + + // Get the compressed image data as a Base64 string + const base64String = canvas.toDataURL('image/jpeg', 0.5).replace('data:', '').replace(/^.+,/, ''); // 0.5 is the quality from 0 to 1 + + this.node.properties.imgData = { + name: file.name, + lastModified: file.lastModified, + size: file.size, + type: file.type, + base64: base64String + }; + handleImageLoad(img, file, base64String); + }; + + handleImageFile = (file) => { + const reader = new FileReader(); + reader.onloadend = () => { + const img = new Image(); + img.src = reader.result; + img.onload = () => processImage(img, file); + }; + reader.readAsDataURL(file); + + const imageUrl = URL.createObjectURL(file); + const img = new Image(); + img.src = imageUrl; + img.onload = () => this.handleImageLoad(img, file, null); + }; + + refreshBackgroundImage = () => { + if (this.node.properties.imgData && this.node.properties.imgData.base64) { + const base64String = this.node.properties.imgData.base64; + const imageUrl = `data:${this.node.properties.imgData.type};base64,${base64String}`; + const img = new Image(); + img.src = imageUrl; + img.onload = () => this.handleImageLoad(img, null, base64String); + } + }; + + refreshSplineElements = () => { + // Clear existing line elements and recreate them + const svgElement = this.vis.canvas(); + + // Remove all existing line elements + const oldLines = svgElement.querySelectorAll('path'); + oldLines.forEach(line => line.remove()); + + this.pathElements = []; + this.lineObjects = []; + + const originalChildren = [...this.vis.children]; + + // Find line objects to remove (those that represent splines) + const linesToRemove = originalChildren.filter(child => + child instanceof pv.Line + ); + linesToRemove.forEach(line => line.visible(false)); + + // Re-add all spline lines and store references to them + this.splines.forEach((spline, splineIndex) => { + // For single points, we need a special handling + if (spline.isSinglePoint || (spline.points && spline.points.length === 1)) { + const point = spline.points[0]; + // For single points, create a tiny line at the same point + // This ensures we have a path element for the point + const lineObj = this.vis.add(pv.Line) + .data([point, {x: point.x + 0.001, y: point.y + 0.001}]) + .left(d => d.x) + .top(d => d.y) + .strokeStyle(spline.color) + .lineWidth(() => { + if (splineIndex === this.activeSplineIndex) return 3; + if (splineIndex === this.hoverSplineIndex) return 2; + return 1.5; + }) + .event("mouseover", () => { + this.hoverSplineIndex = splineIndex; + this.vis.render(); + }) + .event("mouseout", () => { + this.hoverSplineIndex = -1; + this.vis.render(); + }) + .event("mousedown", () => { + if (this.activeSplineIndex !== splineIndex) { + this.activeSplineIndex = splineIndex; + this.refreshSplineElements(); + } + }); + this.lineObjects.push(lineObj); + } else { + // For normal multi-point splines + const strokeObj = this.vis.add(pv.Line) + .data(() => spline.points) + .left(d => d.x) + .top(d => d.y) + .interpolate(() => this.interpolation) + .tension(() => this.tension) + .segmented(() => false) + .strokeStyle("black") // Stroke color + .lineWidth(() => { + // Make stroke slightly wider than the main line + if (splineIndex === this.activeSplineIndex) return 5; + if (splineIndex === this.hoverSplineIndex) return 4; + return 3.5; + }); + const lineObj = this.vis.add(pv.Line) + .data(() => spline.points) + .left(d => d.x) + .top(d => d.y) + .interpolate(() => this.interpolation) + .tension(() => this.tension) + .segmented(() => false) + .strokeStyle(spline.color) + .lineWidth(() => { + if (splineIndex === this.activeSplineIndex) return 3; + if (splineIndex === this.hoverSplineIndex) return 2; + return 1.5; + }) + .event("mouseover", () => { + this.hoverSplineIndex = splineIndex; + this.vis.render(); + }) + .event("mouseout", () => { + this.hoverSplineIndex = -1; + this.vis.render(); + }) + .event("mousedown", () => { + if (this.activeSplineIndex !== splineIndex) { + this.activeSplineIndex = splineIndex; + this.refreshSplineElements(); + } + }); + + // // Add invisible wider hit area for easier selection + // this.vis.add(pv.Line) + // .data(() => spline.points) + // .left(d => d.x) + // .top(d => d.y) + // .interpolate(() => this.interpolation) + // .tension(() => this.tension) + // .segmented(() => false) + // .strokeStyle("rgba(0,0,0,0.01)") // Nearly invisible + // .lineWidth(15) // Much wider hit area + // .event("mouseover", () => { + // this.hoverSplineIndex = splineIndex; + // this.vis.render(); + // }) + // .event("mouseout", () => { + // this.hoverSplineIndex = -1; + // this.vis.render(); + // }) + // .event("mousedown", () => { + // if (pv.event.shiftKey) { + // if (this.activeSplineIndex !== splineIndex) { + // this.activeSplineIndex = splineIndex; + // this.refreshSplineElements(); + // } + // }} + // ); + + this.lineObjects.push(lineObj); + } + }); + + this.vis.render(); + + requestAnimationFrame(() => { + const allPaths = Array.from(svgElement.querySelectorAll('path')); + this.pathElements = []; + + // First try: look at paths with specific childIndex values + this.lineObjects.forEach((lineObj, i) => { + // Find paths that correspond to our line objects + const childIndex = lineObj.childIndex; + const matchingPath = allPaths.find(path => + path.$scene && path.$scene.scenes && + path.$scene.scenes.childIndex === childIndex + ); + + if (matchingPath) { + //console.log("matchingPath:", matchingPath); + this.pathElements[i] = matchingPath; + } + }); + + // Check if we found all paths + if (this.pathElements.filter(p => p).length !== this.splines.length) { + // Fallback to color matching + this.pathElements = []; + for (let i = 0; i < this.splines.length; i++) { + const color = this.splines[i].color; + const matchingPath = allPaths.find(p => + p.getAttribute('style')?.includes(color) && + !this.pathElements.includes(p) + ); + + if (matchingPath) { + this.pathElements[i] = matchingPath; + } + } + } + + // If we still don't have the right number of paths, use the first N paths + if (this.pathElements.filter(p => p).length !== this.splines.length) { + this.pathElements = allPaths.slice(0, this.splines.length); + } + + this.updatePath(); + }); + }; + + + initializeDefaultSplines() { + this.splines = [{ + points: pv.range(1, 4).map((i, index) => { + if (index === 0) { + return { x: 0, y: this.height }; + } else if (index === 2) { + return { x: this.width, y: 0 }; + } else { + return { + x: i * this.width / 5, + y: 50 + Math.random() * (this.height - 100) + }; + } + }), + color: this.getSplineColor(0), + name: "Spline 1" + }]; + } + + getSplineColor(index) { + const colors = [ + "#1f77b4", "#ff7f0e", "#2ca02c", "#d62728", + "#9467bd", "#8c564b", "#e377c2", "#7f7f7f", + "#bcbd22", "#17becf" + ]; + return colors[index % colors.length]; + } + + createContextMenu = () => { + const self = this; + const oldMenu = this.node.contextMenu; + const newMenu = oldMenu.cloneNode(true); + oldMenu.parentNode.replaceChild(newMenu, oldMenu); + this.node.contextMenu = newMenu; + + document.addEventListener('contextmenu', function (e) { + e.preventDefault(); + }); + + document.addEventListener('click', function (e) { + document.querySelectorAll('.spline-editor-context-menu').forEach(menu => { + menu.style.display = 'none'; + }); + }); + + this.node.contextMenu.addEventListener('click', function(e) { + e.preventDefault(); + if (e.target.tagName === 'A') { + const id = parseInt(e.target.id.split('-')[2]); + + switch(id) { + case 0: + e.preventDefault(); + if (!self.drawHandles) { + self.drawHandles = true + self.vis.add(pv.Line) + .data(() => self.splines[self.activeSplineIndex].points.map((point, index) => ({ + start: point, + end: [index] + }))) + .left(d => d.start.x) + .top(d => d.start.y) + .interpolate("linear") + .tension(0) // Straight lines + .strokeStyle("#ff7f0e") // Same color as control points + .lineWidth(1) + .visible(() => self.drawHandles); + self.vis.render(); + } else { + self.drawHandles = false + self.vis.render(); + } + self.node.contextMenu.style.display = 'none'; + break; + case 1: + + self.drawSamplePoints = !self.drawSamplePoints; + self.updatePath(); + break; + case 2: + if (self.dotShape == "circle"){ + self.dotShape = "triangle" + } + else { + self.dotShape = "circle" + } + self.updatePath(); + break; + case 3: + // Create file input element + const fileInput = document.createElement('input'); + fileInput.type = 'file'; + fileInput.accept = 'image/*'; // Accept only image files + + // Listen for file selection + fileInput.addEventListener('change', function (event) { + const file = event.target.files[0]; // Get the selected file + + if (file) { + const imageUrl = URL.createObjectURL(file); + let img = new Image(); + img.src = imageUrl; + img.onload = () => self.handleImageLoad(img, file, null); + } + }); + + fileInput.click(); + + self.node.contextMenu.style.display = 'none'; + break; + case 4: + self.splines[self.activeSplineIndex].points.reverse(); + self.updatePath(); + break; + case 5: + self.backgroundImage.visible(false).root.render(); + self.node.properties.imgData = null; + self.node.contextMenu.style.display = 'none'; + break; + case 6: // Add new spline + const newSplineIndex = self.splines.length; + self.splines.push({ + points: [ + // Create default points for the new spline + { x: 0, y: self.height }, + { x: self.width/2, y: self.height/2 }, + { x: self.width, y: 0 } + ], + color: self.getSplineColor(newSplineIndex), + name: `Spline ${newSplineIndex + 1}` + }); + self.activeSplineIndex = newSplineIndex; + self.refreshSplineElements(); + self.node.contextMenu.style.display = 'none'; + break; + case 7: // Add new single point + const newSingleSplineIndex = self.splines.length; + self.splines.push({ + points: [ + { x: self.lastMousePosition.x, y: self.lastMousePosition.y }, + ], + color: self.getSplineColor(newSingleSplineIndex), + name: `Spline ${newSingleSplineIndex + 1}`, + isSinglePoint: true + }); + self.activeSplineIndex = newSingleSplineIndex; + self.refreshSplineElements(); + self.node.contextMenu.style.display = 'none'; + break; + case 8: // Delete current spline + if (self.splines.length > 1) { + self.splines.splice(self.activeSplineIndex, 1); + self.activeSplineIndex = Math.min(self.activeSplineIndex, self.splines.length - 1); + self.refreshSplineElements(); + } + self.node.contextMenu.style.display = 'none'; + break; + case 9: // Next spline + self.activeSplineIndex = (self.activeSplineIndex + 1) % self.splines.length; + self.refreshSplineElements(); + self.node.contextMenu.style.display = 'none'; + break; + } + } + }); + } + + samplePoints(svgPathElement, numSamples, samplingMethod, width, splineIndex) { + const spline = this.splines[splineIndex]; + + // Check if this is a single point spline + if (spline && (spline.isSinglePoint || (spline.points && spline.points.length === 1))) { + // For a single point, return an array with the same coordinates repeated + const point = spline.points[0]; + return Array(numSamples).fill().map(() => ({ x: point.x, y: point.y })); + } + + if (!svgPathElement) { + console.warn(`Path element not found for spline index: ${splineIndex}. Available paths: ${this.pathElements.length}`); + + + const splinePoints = this.splines[splineIndex].points; + + // If we have no points, return an empty array + if (!splinePoints || splinePoints.length === 0) { + return []; + } + + // Create a simple interpolation between control points + const result = []; + for (let i = 0; i < numSamples; i++) { + const t = i / (numSamples - 1); + const idx = Math.min( + Math.floor(t * (splinePoints.length - 1)), + splinePoints.length - 2 + ); + const fraction = (t * (splinePoints.length - 1)) - idx; + + const x = splinePoints[idx].x + fraction * (splinePoints[idx + 1].x - splinePoints[idx].x); + const y = splinePoints[idx].y + fraction * (splinePoints[idx + 1].y - splinePoints[idx].y); + + result.push({ x, y }); + } + return result; + } + + var svgWidth = width; // Fixed width of the SVG element + var pathLength = svgPathElement.getTotalLength(); + var points = []; + + if (samplingMethod === "speed") { + // Calculate control point distances along the path + const controlPoints = this.splines[splineIndex].points; + const pathPositions = []; + + // Find approximate path positions for each control point + for (const cp of controlPoints) { + let bestDist = Infinity; + let bestPos = 0; + + // Sample the path to find closest point to each control point + for (let pos = 0; pos <= pathLength; pos += pathLength / 100) { + const pt = svgPathElement.getPointAtLength(pos); + const dist = Math.sqrt(Math.pow(pt.x - cp.x, 2) + Math.pow(pt.y - cp.y, 2)); + + if (dist < bestDist) { + bestDist = dist; + bestPos = pos; + } + } + pathPositions.push(bestPos); + } + + // Sort positions along path + pathPositions.sort((a, b) => a - b); + + // Create a smooth speed mapping function with synchronization + const createSynchronizedMapping = () => { + // Calculate segment lengths and densities + const segments = []; + let totalLength = pathPositions[pathPositions.length - 1] - pathPositions[0]; + + for (let i = 0; i < pathPositions.length - 1; i++) { + const segLength = pathPositions[i+1] - pathPositions[i]; + // Inverse relationship - shorter segments = higher density = slower speed + const density = 1 / Math.max(segLength, 0.0001); + segments.push({ + position: pathPositions[i], + length: segLength, + density: density + }); + } + + // Create mapping function with forced synchronization at endpoints + return t => { + // Force synchronization at t=0 and t=1 + if (t === 0) return 0; + if (t === 1) return pathLength; + + // For intermediate points, use the speed control + // Scale t to fit between first and last control points + const firstPos = pathPositions[0]; + const lastPos = pathPositions[pathPositions.length - 1]; + + // Create a density-weighted position mapping + let totalWeight = 0; + let weights = []; + + for (let i = 0; i < segments.length; i++) { + totalWeight += segments[i].density; + weights.push(segments[i].density); + } + + // Normalize weights + const normalizedWeights = weights.map(w => w / totalWeight); + + // Calculate cumulative weights + let cumulativeWeight = 0; + const cumulativeWeights = normalizedWeights.map(w => { + cumulativeWeight += w; + return cumulativeWeight; + }); + + // Find the segment for this t value + let segmentIndex = 0; + for (let i = 0; i < cumulativeWeights.length; i++) { + if (t <= cumulativeWeights[i]) { + segmentIndex = i; + break; + } + } + + // Calculate position within segment + const segmentStart = segmentIndex > 0 ? cumulativeWeights[segmentIndex - 1] : 0; + const segmentEnd = cumulativeWeights[segmentIndex]; + const segmentT = (t - segmentStart) / (segmentEnd - segmentStart); + + // Map to path position + const pathStart = pathPositions[segmentIndex]; + const pathEnd = pathPositions[segmentIndex + 1]; + const pos = pathStart + segmentT * (pathEnd - pathStart); + + // Scale to fill entire path + return pos; + }; + }; + + const mapToPath = createSynchronizedMapping(); + + // Sample using the synchronized mapping function + for (let i = 0; i < numSamples; i++) { + const t = i / (numSamples - 1); + const pathPos = mapToPath(t); + const point = svgPathElement.getPointAtLength(pathPos); + points.push({ x: point.x, y: point.y }); + } + + return points; + + } + else{ + for (var i = 0; i < numSamples; i++) { + if (samplingMethod === "time") { + // Calculate the x-coordinate for the current sample based on the SVG's width + var x = (svgWidth / (numSamples - 1)) * i; + // Find the point on the path that intersects the vertical line at the calculated x-coordinate + var point = this.findPointAtX(svgPathElement, x, pathLength); + } + else if (samplingMethod === "path") { + // Calculate the distance along the path for the current sample + var distance = (pathLength / (numSamples - 1)) * i; + // Get the point at the current distance + var point = svgPathElement.getPointAtLength(distance); + } + + // Add the point to the array of points + points.push({ x: point.x, y: point.y }); + } + return points; + } + } + + findClosestPoints(points, clickedPoint) { + // Calculate distances from clickedPoint to each point in the array + let distances = points.map(point => { + let dx = clickedPoint.x - point.x; + let dy = clickedPoint.y - point.y; + return { index: points.indexOf(point), distance: Math.sqrt(dx * dx + dy * dy) }; + }); + // Sort distances and get the indices of the two closest points + let sortedDistances = distances.sort((a, b) => a.distance - b.distance); + let closestPoint1Index = sortedDistances[0].index; + let closestPoint2Index = sortedDistances[1].index; + // Ensure point1Index is always the smaller index + if (closestPoint1Index > closestPoint2Index) { + [closestPoint1Index, closestPoint2Index] = [closestPoint2Index, closestPoint1Index]; + } + return { point1Index: closestPoint1Index, point2Index: closestPoint2Index }; + } + + findPointAtX(svgPathElement, targetX, pathLength) { + let low = 0; + let high = pathLength; + let bestPoint = svgPathElement.getPointAtLength(0); + + while (low <= high) { + let mid = low + (high - low) / 2; + let point = svgPathElement.getPointAtLength(mid); + + if (Math.abs(point.x - targetX) < 1) { + return point; // The point is close enough to the target + } + + if (point.x < targetX) { + low = mid + 1; + } else { + high = mid - 1; + } + + // Keep track of the closest point found so far + if (Math.abs(point.x - targetX) < Math.abs(bestPoint.x - targetX)) { + bestPoint = point; + } + } + + // Return the closest point found + return bestPoint; + } +} \ No newline at end of file diff --git a/ComfyUI/custom_nodes/comfyui-kjnodes/web/red.png b/ComfyUI/custom_nodes/comfyui-kjnodes/web/red.png new file mode 100644 index 0000000000000000000000000000000000000000..4352c118b2c5fa6f33edc4d99a5e4d22649ff827 Binary files /dev/null and b/ComfyUI/custom_nodes/comfyui-kjnodes/web/red.png differ diff --git a/ComfyUI/models/checkpoints/put_checkpoints_here b/ComfyUI/models/checkpoints/put_checkpoints_here new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/ComfyUI/models/clip/put_clip_or_text_encoder_models_here b/ComfyUI/models/clip/put_clip_or_text_encoder_models_here new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/ComfyUI/models/clip_vision/put_clip_vision_models_here b/ComfyUI/models/clip_vision/put_clip_vision_models_here new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/ComfyUI/models/diffusion_models/put_diffusion_model_files_here b/ComfyUI/models/diffusion_models/put_diffusion_model_files_here new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391