|
import traceback |
|
|
|
import comfy |
|
import nodes |
|
import numpy as np |
|
import torch |
|
import torch.nn.functional as F |
|
from . import prompt_support |
|
|
|
class RegionalPromptSimple: |
|
@classmethod |
|
def INPUT_TYPES(s): |
|
return { |
|
"required": { |
|
"basic_pipe": ("BASIC_PIPE",), |
|
"mask": ("MASK",), |
|
"cfg": ("FLOAT", {"default": 8.0, "min": 0.0, "max": 100.0}), |
|
"sampler_name": (comfy.samplers.KSampler.SAMPLERS,), |
|
"scheduler": (comfy.samplers.KSampler.SCHEDULERS,), |
|
"wildcard_prompt": ("STRING", {"multiline": True, "dynamicPrompts": False, "placeholder": "wildcard prompt"}), |
|
"controlnet_in_pipe": ("BOOLEAN", {"default": False, "label_on": "Keep", "label_off": "Override"}), |
|
}, |
|
} |
|
|
|
RETURN_TYPES = ("REGIONAL_PROMPTS", ) |
|
FUNCTION = "doit" |
|
|
|
CATEGORY = "InspirePack/Regional" |
|
|
|
def doit(self, basic_pipe, mask, cfg, sampler_name, scheduler, wildcard_prompt, controlnet_in_pipe=False): |
|
if 'RegionalPrompt' not in nodes.NODE_CLASS_MAPPINGS: |
|
raise Exception(f"[ERROR] To use RegionalPromptSimple, you need to install 'ComfyUI-Impact-Pack'") |
|
|
|
model, clip, vae, positive, negative = basic_pipe |
|
|
|
iwe = nodes.NODE_CLASS_MAPPINGS['ImpactWildcardEncode']() |
|
kap = nodes.NODE_CLASS_MAPPINGS['KSamplerAdvancedProvider']() |
|
rp = nodes.NODE_CLASS_MAPPINGS['RegionalPrompt']() |
|
|
|
if wildcard_prompt != "": |
|
model, clip, new_positive, _ = iwe.doit(model=model, clip=clip, populated_text=wildcard_prompt) |
|
|
|
if controlnet_in_pipe: |
|
prev_cnet = None |
|
for t in positive: |
|
if 'control' in t[1] and 'control_apply_to_uncond' in t[1]: |
|
prev_cnet = t[1]['control'], t[1]['control_apply_to_uncond'] |
|
break |
|
|
|
if prev_cnet is not None: |
|
for t in new_positive: |
|
t[1]['control'] = prev_cnet[0] |
|
t[1]['control_apply_to_uncond'] = prev_cnet[1] |
|
|
|
else: |
|
new_positive = positive |
|
|
|
basic_pipe = model, clip, vae, new_positive, negative |
|
|
|
sampler = kap.doit(cfg, sampler_name, scheduler, basic_pipe)[0] |
|
regional_prompts = rp.doit(mask, sampler)[0] |
|
|
|
return (regional_prompts, ) |
|
|
|
|
|
def color_to_mask(color_mask, mask_color): |
|
try: |
|
if mask_color.startswith("#"): |
|
selected = int(mask_color[1:], 16) |
|
else: |
|
selected = int(mask_color, 10) |
|
except Exception: |
|
raise Exception(f"[ERROR] Invalid mask_color value. mask_color should be a color value for RGB") |
|
|
|
temp = (torch.clamp(color_mask, 0, 1.0) * 255.0).round().to(torch.int) |
|
temp = torch.bitwise_left_shift(temp[:, :, :, 0], 16) + torch.bitwise_left_shift(temp[:, :, :, 1], 8) + temp[:, :, :, 2] |
|
mask = torch.where(temp == selected, 1.0, 0.0) |
|
return mask |
|
|
|
|
|
class RegionalPromptColorMask: |
|
@classmethod |
|
def INPUT_TYPES(s): |
|
return { |
|
"required": { |
|
"basic_pipe": ("BASIC_PIPE",), |
|
"color_mask": ("IMAGE",), |
|
"mask_color": ("STRING", {"multiline": False, "default": "#FFFFFF"}), |
|
"cfg": ("FLOAT", {"default": 8.0, "min": 0.0, "max": 100.0}), |
|
"sampler_name": (comfy.samplers.KSampler.SAMPLERS,), |
|
"scheduler": (comfy.samplers.KSampler.SCHEDULERS,), |
|
"wildcard_prompt": ("STRING", {"multiline": True, "dynamicPrompts": False, "placeholder": "wildcard prompt"}), |
|
"controlnet_in_pipe": ("BOOLEAN", {"default": False, "label_on": "Keep", "label_off": "Override"}), |
|
}, |
|
} |
|
|
|
RETURN_TYPES = ("REGIONAL_PROMPTS", "MASK") |
|
FUNCTION = "doit" |
|
|
|
CATEGORY = "InspirePack/Regional" |
|
|
|
def doit(self, basic_pipe, color_mask, mask_color, cfg, sampler_name, scheduler, wildcard_prompt, controlnet_in_pipe=False): |
|
mask = color_to_mask(color_mask, mask_color) |
|
rp = RegionalPromptSimple().doit(basic_pipe, mask, cfg, sampler_name, scheduler, wildcard_prompt, controlnet_in_pipe)[0] |
|
return (rp, mask) |
|
|
|
|
|
class RegionalConditioningSimple: |
|
@classmethod |
|
def INPUT_TYPES(s): |
|
return { |
|
"required": { |
|
"clip": ("CLIP", ), |
|
"mask": ("MASK",), |
|
"strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.01}), |
|
"set_cond_area": (["default", "mask bounds"],), |
|
"prompt": ("STRING", {"multiline": True, "placeholder": "prompt"}), |
|
}, |
|
} |
|
|
|
RETURN_TYPES = ("CONDITIONING", ) |
|
FUNCTION = "doit" |
|
|
|
CATEGORY = "InspirePack/Regional" |
|
|
|
def doit(self, clip, mask, strength, set_cond_area, prompt): |
|
conditioning = nodes.CLIPTextEncode().encode(clip, prompt)[0] |
|
conditioning = nodes.ConditioningSetMask().append(conditioning, mask, set_cond_area, strength)[0] |
|
return (conditioning, ) |
|
|
|
|
|
class RegionalConditioningColorMask: |
|
@classmethod |
|
def INPUT_TYPES(s): |
|
return { |
|
"required": { |
|
"clip": ("CLIP", ), |
|
"color_mask": ("IMAGE",), |
|
"mask_color": ("STRING", {"multiline": False, "default": "#FFFFFF"}), |
|
"strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.01}), |
|
"set_cond_area": (["default", "mask bounds"],), |
|
"prompt": ("STRING", {"multiline": True, "placeholder": "prompt"}), |
|
}, |
|
} |
|
|
|
RETURN_TYPES = ("CONDITIONING", "MASK") |
|
FUNCTION = "doit" |
|
|
|
CATEGORY = "InspirePack/Regional" |
|
|
|
def doit(self, clip, color_mask, mask_color, strength, set_cond_area, prompt): |
|
mask = color_to_mask(color_mask, mask_color) |
|
|
|
conditioning = nodes.CLIPTextEncode().encode(clip, prompt)[0] |
|
conditioning = nodes.ConditioningSetMask().append(conditioning, mask, set_cond_area, strength)[0] |
|
return (conditioning, mask) |
|
|
|
|
|
class ToIPAdapterPipe: |
|
@classmethod |
|
def INPUT_TYPES(s): |
|
return { |
|
"required": { |
|
"ipadapter": ("IPADAPTER", ), |
|
"clip_vision": ("CLIP_VISION",), |
|
"model": ("MODEL", ), |
|
} |
|
} |
|
|
|
RETURN_TYPES = ("IPADAPTER_PIPE",) |
|
FUNCTION = "doit" |
|
|
|
CATEGORY = "InspirePack/Util" |
|
|
|
def doit(self, ipadapter, clip_vision, model): |
|
pipe = ipadapter, clip_vision, model |
|
|
|
return (pipe,) |
|
|
|
|
|
class FromIPAdapterPipe: |
|
@classmethod |
|
def INPUT_TYPES(s): |
|
return { |
|
"required": { |
|
"ipadapter_pipe": ("IPADAPTER_PIPE", ), |
|
} |
|
} |
|
|
|
RETURN_TYPES = ("IPADAPTER", "CLIP_VISION", "MODEL") |
|
FUNCTION = "doit" |
|
|
|
CATEGORY = "InspirePack/Util" |
|
|
|
def doit(self, ipadapter_pipe): |
|
return ipadapter_pipe |
|
|
|
|
|
class IPAdapterConditioning: |
|
def __init__(self, mask, weight, weight_type, noise=None, image=None, embeds=None): |
|
self.mask = mask |
|
self.image = image |
|
self.embeds = embeds |
|
self.weight = weight |
|
self.noise = noise |
|
self.weight_type = weight_type |
|
|
|
def doit(self, ipadapter, clip_vision, model): |
|
if 'IPAdapterApply' not in nodes.NODE_CLASS_MAPPINGS: |
|
raise Exception(f"[ERROR] To use Regional IPAdapter, you need to install 'ComfyUI_IPAdapter_plus'") |
|
|
|
obj = nodes.NODE_CLASS_MAPPINGS['IPAdapterApply'] |
|
|
|
if self.image is None: |
|
clip_vision = None |
|
|
|
model = obj().apply_ipadapter(ipadapter, model, self.weight, clip_vision=clip_vision, image=self.image, |
|
embeds=self.embeds, weight_type=self.weight_type, noise=self.noise, |
|
attn_mask=self.mask)[0] |
|
|
|
return model |
|
|
|
|
|
class RegionalIPAdapterMask: |
|
@classmethod |
|
def INPUT_TYPES(s): |
|
return { |
|
"required": { |
|
"mask": ("MASK",), |
|
|
|
"image": ("IMAGE",), |
|
"weight": ("FLOAT", {"default": 0.7, "min": -1, "max": 3, "step": 0.05}), |
|
"noise": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 1.0, "step": 0.01}), |
|
"weight_type": (["original", "linear", "channel penalty"],), |
|
}, |
|
} |
|
|
|
RETURN_TYPES = ("REGIONAL_IPADAPTER", ) |
|
FUNCTION = "doit" |
|
|
|
CATEGORY = "InspirePack/Regional" |
|
|
|
def doit(self, mask, image, weight, noise, weight_type): |
|
cond = IPAdapterConditioning(mask, weight, weight_type, noise=noise, image=image) |
|
return (cond, ) |
|
|
|
|
|
class RegionalIPAdapterColorMask: |
|
@classmethod |
|
def INPUT_TYPES(s): |
|
return { |
|
"required": { |
|
"color_mask": ("IMAGE",), |
|
"mask_color": ("STRING", {"multiline": False, "default": "#FFFFFF"}), |
|
|
|
"image": ("IMAGE",), |
|
"weight": ("FLOAT", {"default": 0.7, "min": -1, "max": 3, "step": 0.05}), |
|
"noise": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 1.0, "step": 0.01}), |
|
"weight_type": (["original", "linear", "channel penalty"], ), |
|
}, |
|
} |
|
|
|
RETURN_TYPES = ("REGIONAL_IPADAPTER", "MASK") |
|
FUNCTION = "doit" |
|
|
|
CATEGORY = "InspirePack/Regional" |
|
|
|
def doit(self, color_mask, mask_color, image, weight, noise, weight_type): |
|
mask = color_to_mask(color_mask, mask_color) |
|
cond = IPAdapterConditioning(mask, weight, weight_type, noise=noise, image=image) |
|
return (cond, mask) |
|
|
|
|
|
class RegionalIPAdapterEncodedMask: |
|
@classmethod |
|
def INPUT_TYPES(s): |
|
return { |
|
"required": { |
|
"mask": ("MASK",), |
|
|
|
"embeds": ("embeds",), |
|
"weight": ("FLOAT", {"default": 0.7, "min": -1, "max": 3, "step": 0.05}), |
|
"weight_type": (["original", "linear", "channel penalty"],), |
|
}, |
|
} |
|
|
|
RETURN_TYPES = ("REGIONAL_IPADAPTER", ) |
|
FUNCTION = "doit" |
|
|
|
CATEGORY = "InspirePack/Regional" |
|
|
|
def doit(self, mask, embeds, weight, weight_type): |
|
cond = IPAdapterConditioning(mask, weight, weight_type, embeds=embeds) |
|
return (cond, ) |
|
|
|
|
|
class RegionalIPAdapterEncodedColorMask: |
|
@classmethod |
|
def INPUT_TYPES(s): |
|
return { |
|
"required": { |
|
"color_mask": ("IMAGE",), |
|
"mask_color": ("STRING", {"multiline": False, "default": "#FFFFFF"}), |
|
|
|
"embeds": ("EMBEDS",), |
|
"weight": ("FLOAT", {"default": 0.7, "min": -1, "max": 3, "step": 0.05}), |
|
"weight_type": (["original", "linear", "channel penalty"],), |
|
}, |
|
} |
|
|
|
RETURN_TYPES = ("REGIONAL_IPADAPTER", "MASK") |
|
FUNCTION = "doit" |
|
|
|
CATEGORY = "InspirePack/Regional" |
|
|
|
def doit(self, color_mask, mask_color, embeds, weight, weight_type): |
|
mask = color_to_mask(color_mask, mask_color) |
|
cond = IPAdapterConditioning(mask, weight, weight_type, embeds=embeds) |
|
return (cond, mask) |
|
|
|
|
|
class ApplyRegionalIPAdapters: |
|
@classmethod |
|
def INPUT_TYPES(s): |
|
return {"required": { |
|
"ipadapter_pipe": ("IPADAPTER_PIPE",), |
|
"regional_ipadapter1": ("REGIONAL_IPADAPTER", ), |
|
}, |
|
} |
|
|
|
RETURN_TYPES = ("MODEL", ) |
|
FUNCTION = "doit" |
|
|
|
CATEGORY = "InspirePack/Regional" |
|
|
|
def doit(self, **kwargs): |
|
ipadapter_pipe = kwargs['ipadapter_pipe'] |
|
ipadapter, clip_vision, model = ipadapter_pipe |
|
|
|
del kwargs['ipadapter_pipe'] |
|
|
|
for k, v in kwargs.items(): |
|
model = v.doit(ipadapter, clip_vision, model) |
|
|
|
return (model, ) |
|
|
|
|
|
class RegionalSeedExplorerMask: |
|
@classmethod |
|
def INPUT_TYPES(s): |
|
return { |
|
"required": { |
|
"mask": ("MASK",), |
|
|
|
"noise": ("NOISE",), |
|
"seed_prompt": ("STRING", {"multiline": True, "dynamicPrompts": False, "pysssss.autocomplete": False}), |
|
"enable_additional": ("BOOLEAN", {"default": True, "label_on": "true", "label_off": "false"}), |
|
"additional_seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}), |
|
"additional_strength": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.01}), |
|
"noise_mode": (["GPU(=A1111)", "CPU"],), |
|
}, |
|
} |
|
|
|
RETURN_TYPES = ("NOISE",) |
|
FUNCTION = "doit" |
|
|
|
CATEGORY = "InspirePack/Regional" |
|
|
|
def doit(self, mask, noise, seed_prompt, enable_additional, additional_seed, additional_strength, noise_mode): |
|
device = comfy.model_management.get_torch_device() |
|
noise_device = "cpu" if noise_mode == "CPU" else device |
|
|
|
noise = noise.to(device) |
|
mask = mask.to(device) |
|
|
|
if len(mask.shape) == 2: |
|
mask = mask.unsqueeze(0) |
|
|
|
mask = torch.nn.functional.interpolate(mask.reshape((-1, 1, mask.shape[-2], mask.shape[-1])), size=(noise.shape[2], noise.shape[3]), mode="bilinear").squeeze(0) |
|
|
|
try: |
|
seed_prompt = seed_prompt.replace("\n", "") |
|
items = seed_prompt.strip().split(",") |
|
|
|
if items == ['']: |
|
items = [] |
|
|
|
if enable_additional: |
|
items.append((additional_seed, additional_strength)) |
|
|
|
noise = prompt_support.SeedExplorer.apply_variation(noise, items, noise_device, mask) |
|
except Exception: |
|
print(f"[ERROR] IGNORED: RegionalSeedExplorerColorMask is failed.") |
|
traceback.print_exc() |
|
|
|
noise = noise.cpu() |
|
mask.cpu() |
|
return (noise,) |
|
|
|
|
|
class RegionalSeedExplorerColorMask: |
|
@classmethod |
|
def INPUT_TYPES(s): |
|
return { |
|
"required": { |
|
"color_mask": ("IMAGE",), |
|
"mask_color": ("STRING", {"multiline": False, "default": "#FFFFFF"}), |
|
|
|
"noise": ("NOISE",), |
|
"seed_prompt": ("STRING", {"multiline": True, "dynamicPrompts": False, "pysssss.autocomplete": False}), |
|
"enable_additional": ("BOOLEAN", {"default": True, "label_on": "true", "label_off": "false"}), |
|
"additional_seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}), |
|
"additional_strength": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.01}), |
|
"noise_mode": (["GPU(=A1111)", "CPU"],), |
|
}, |
|
} |
|
|
|
RETURN_TYPES = ("NOISE", "MASK") |
|
FUNCTION = "doit" |
|
|
|
CATEGORY = "InspirePack/Regional" |
|
|
|
def doit(self, color_mask, mask_color, noise, seed_prompt, enable_additional, additional_seed, additional_strength, noise_mode): |
|
device = comfy.model_management.get_torch_device() |
|
noise_device = "cpu" if noise_mode == "CPU" else device |
|
|
|
color_mask = color_mask.to(device) |
|
noise = noise.to(device) |
|
|
|
mask = color_to_mask(color_mask, mask_color) |
|
original_mask = mask |
|
mask = torch.nn.functional.interpolate(mask.reshape((-1, 1, mask.shape[-2], mask.shape[-1])), size=(noise.shape[2], noise.shape[3]), mode="bilinear").squeeze(0) |
|
|
|
mask = mask.to(device) |
|
|
|
try: |
|
seed_prompt = seed_prompt.replace("\n", "") |
|
items = seed_prompt.strip().split(",") |
|
|
|
if items == ['']: |
|
items = [] |
|
|
|
if enable_additional: |
|
items.append((additional_seed, additional_strength)) |
|
|
|
noise = prompt_support.SeedExplorer.apply_variation(noise, items, noise_device, mask) |
|
except Exception: |
|
print(f"[ERROR] IGNORED: RegionalSeedExplorerColorMask is failed.") |
|
traceback.print_exc() |
|
|
|
color_mask.cpu() |
|
noise = noise.cpu() |
|
original_mask = original_mask.cpu() |
|
return (noise, original_mask) |
|
|
|
|
|
NODE_CLASS_MAPPINGS = { |
|
"RegionalPromptSimple //Inspire": RegionalPromptSimple, |
|
"RegionalPromptColorMask //Inspire": RegionalPromptColorMask, |
|
"RegionalConditioningSimple //Inspire": RegionalConditioningSimple, |
|
"RegionalConditioningColorMask //Inspire": RegionalConditioningColorMask, |
|
"RegionalIPAdapterMask //Inspire": RegionalIPAdapterMask, |
|
"RegionalIPAdapterColorMask //Inspire": RegionalIPAdapterColorMask, |
|
"RegionalIPAdapterEncodedMask //Inspire": RegionalIPAdapterEncodedMask, |
|
"RegionalIPAdapterEncodedColorMask //Inspire": RegionalIPAdapterEncodedColorMask, |
|
"RegionalSeedExplorerMask //Inspire": RegionalSeedExplorerMask, |
|
"RegionalSeedExplorerColorMask //Inspire": RegionalSeedExplorerColorMask, |
|
"ToIPAdapterPipe //Inspire": ToIPAdapterPipe, |
|
"FromIPAdapterPipe //Inspire": FromIPAdapterPipe, |
|
"ApplyRegionalIPAdapters //Inspire": ApplyRegionalIPAdapters, |
|
} |
|
|
|
NODE_DISPLAY_NAME_MAPPINGS = { |
|
"RegionalPromptSimple //Inspire": "Regional Prompt Simple (Inspire)", |
|
"RegionalPromptColorMask //Inspire": "Regional Prompt By Color Mask (Inspire)", |
|
"RegionalConditioningSimple //Inspire": "Regional Conditioning Simple (Inspire)", |
|
"RegionalConditioningColorMask //Inspire": "Regional Conditioning By Color Mask (Inspire)", |
|
"RegionalIPAdapterMask //Inspire": "Regional IPAdapter Mask (Inspire)", |
|
"RegionalIPAdapterColorMask //Inspire": "Regional IPAdapter By Color Mask (Inspire)", |
|
"RegionalIPAdapterEncodedMask //Inspire": "Regional IPAdapter Encoded Mask (Inspire)", |
|
"RegionalIPAdapterEncodedColorMask //Inspire": "Regional IPAdapter Encoded By Color Mask (Inspire)", |
|
"RegionalSeedExplorerMask //Inspire": "Regional Seed Explorer By Mask (Inspire)", |
|
"RegionalSeedExplorerColorMask //Inspire": "Regional Seed Explorer By Color Mask (Inspire)", |
|
"ToIPAdapterPipe //Inspire": "ToIPAdapterPipe (Inspire)", |
|
"FromIPAdapterPipe //Inspire": "FromIPAdapterPipe (Inspire)", |
|
"ApplyRegionalIPAdapters //Inspire": "Apply Regional IPAdapters (Inspire)" |
|
} |
|
|