Files
ExponentialML-ComfyUI_Visua…/nodes.py
T

126 lines
4.1 KiB
Python

import comfy
import torch
from .utils.attention_functions import VisualStyleProcessor
from .utils.cond_functions import cat_cond
class ApplyVisualStyle:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"model": ("MODEL",),
"clip": ("CLIP",),
"reference_latent": ("LATENT",),
"reference_cond": ("CONDITIONING",),
"positive": ("CONDITIONING",),
"negative": ("CONDITIONING",),
"enabled": ("BOOLEAN", {"default": True}),
"denoise": ("FLOAT", {"default": 1., "min": 0., "max": 1., "step": 1e-2}),
"input_blocks": ("BOOLEAN", {"default": False}),
"skip_input_layers": ("INT", {"default": 24, "min": 0, "max": 48, "step": 1}),
"middle_block": ("BOOLEAN", {"default": False}),
"skip_middle_layers": ("INT", {"default": 1, "min": 0, "max": 2, "step": 1}),
"output_blocks": ("BOOLEAN", {"default": True}),
"skip_output_layers": ("INT", {"default": 24, "min": 0, "max": 72, "step": 1}),
},
"optional": {
"init_image": ("IMAGE",),
}
}
CATEGORY = "VisualStylePrompting/apply"
RETURN_TYPES = ("MODEL", "CONDITIONING", "CONDITIONING", "LATENT")
RETURN_NAMES = ("model", "positive", "negative", "latents")
FUNCTION = "apply_visual_style_prompt"
def get_block_choices(self, input_blocks, middle_block, output_blocks):
block_choices_map = (
[input_blocks, "input"],
[middle_block, "middle"],
[output_blocks, "output"]
)
block_choices = []
for block_choice in block_choices_map:
if block_choice[0]:
block_choices.append(block_choice[1])
return block_choices
def activate_block_choice(self, key, block_choices):
return any([block in key for block in block_choices])
def apply_visual_style_prompt(
self,
model: comfy.model_patcher.ModelPatcher,
clip,
reference_latent,
reference_cond,
positive,
negative,
enabled,
denoise,
input_blocks,
middle_block,
output_blocks,
init_image = None,
skip_input_layers=0,
skip_middle_layers=0,
skip_output_layers=0
):
reference_samples = reference_latent["samples"]
block_choices = self.get_block_choices(input_blocks, middle_block, output_blocks)
layer_indexes = {
"input": 0,
"middle": 0,
"output": 0
}
n_skip_per_block = {
"input": skip_input_layers,
"middle": skip_middle_layers,
"output": skip_output_layers
}
class_names = set()
for n, m in model.model.diffusion_model.named_modules():
if m.__class__.__name__ == "CrossAttention":
is_enabled = self.activate_block_choice(n, block_choices)
if is_enabled:
block_name = n.split("_")[0]
if layer_indexes[block_name] < n_skip_per_block[block_name]:
is_enabled = False
layer_indexes[block_name] += 1
if hasattr(m.forward, 'module_self'):
m.forward.enabled = is_enabled and enabled
else:
processor = VisualStyleProcessor(m, enabled=is_enabled)
setattr(m, 'forward', processor)
positive_cat = cat_cond(clip, reference_cond, positive)
negative_cat = cat_cond(clip, negative, negative)
latents = torch.zeros_like(reference_samples)
latents = torch.cat([latents] * 2)
latents[0] = reference_samples
denoise_mask = torch.ones_like(latents)[:, :1, ...] * denoise
denoise_mask[0] = 0.
return (model, positive_cat, negative_cat, {"samples": latents, "noise_mask": denoise_mask})
NODE_CLASS_MAPPINGS = {
"ApplyVisualStyle": ApplyVisualStyle,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"ApplyVisualStyle": "Apply Visual Style Prompting",
}