| import re |
| import torch |
| import folder_paths |
| import comfy.utils, comfy.sample, comfy.samplers, comfy.controlnet, comfy.model_base, comfy.model_management, comfy.sampler_helpers, comfy.supported_models |
| from comfy_extras.nodes_compositing import JoinImageWithAlpha |
| from comfy.clip_vision import load as load_clip_vision |
|
|
| from nodes import NODE_CLASS_MAPPINGS as ALL_NODE_CLASS_MAPPINGS |
| from ..config import * |
|
|
| from ..libs.log import log_node_info, log_node_warn |
| from ..libs.utils import get_local_filepath, get_sd_version |
| from ..libs.wildcards import process_with_loras |
| from ..libs.controlnet import easyControlnet |
| from ..libs.conditioning import prompt_to_cond |
| from ..libs import cache as backend_cache |
|
|
| from .. import easyCache |
|
|
| class applyLoraPrompt: |
| @classmethod |
| def INPUT_TYPES(s): |
| return { |
| "required": { |
| "model": ("MODEL",), |
| "clip": ("CLIP",), |
| "positive": ("STRING", {"default": "", "forceInput": True}), |
| }, |
| "optional": { |
| "negative": ("STRING", {"default": "", "forceInput": True}), |
| } |
| } |
|
|
| RETURN_TYPES = ("MODEL", "CLIP", "STRING", "STRING") |
| RETURN_NAMES = ("model", "clip", "positive", "negative") |
| CATEGORY = "EasyUse/Adapter" |
| FUNCTION = "apply" |
|
|
| def apply(self, model, clip, positive, negative=None): |
| model, clip, positive, _, _, _ = process_with_loras(positive, model, clip, 'Positive', easyCache=easyCache) |
| if negative is not None: |
| model, clip, negative, _, _, _ = process_with_loras(negative, model, clip, 'Negative', easyCache=easyCache) |
| |
| return (model, clip, positive, negative if negative is not None else "") |
| |
| class applyLoraStack: |
| @classmethod |
| def INPUT_TYPES(s): |
| return { |
| "required": { |
| "lora_stack": ("LORA_STACK",), |
| "model": ("MODEL",), |
| }, |
| "optional": { |
| "optional_clip": ("CLIP",), |
| } |
| } |
|
|
| RETURN_TYPES = ("MODEL", "CLIP") |
| RETURN_NAMES = ("model", "clip") |
| CATEGORY = "EasyUse/Adapter" |
| FUNCTION = "apply" |
|
|
| def apply(self, lora_stack, model, optional_clip=None): |
| clip = None |
| if lora_stack is not None and len(lora_stack) > 0: |
| for lora in lora_stack: |
| lora = {"lora_name": lora[0], "model": model, "clip": optional_clip, "model_strength": lora[1], |
| "clip_strength": lora[2]} |
| model, clip = easyCache.load_lora(lora, model, optional_clip, use_cache=False) |
| return (model, optional_clip if clip is None else clip) |
|
|
| class applyControlnetStack: |
| @classmethod |
| def INPUT_TYPES(s): |
| return { |
| "required": { |
| "controlnet_stack": ("CONTROL_NET_STACK",), |
| "pipe": ("PIPE_LINE",), |
| }, |
| "optional": { |
| } |
| } |
|
|
| RETURN_TYPES = ("PIPE_LINE",) |
| RETURN_NAMES = ("pipe",) |
| CATEGORY = "EasyUse/Adapter" |
| FUNCTION = "apply" |
|
|
| def apply(self, controlnet_stack, pipe): |
|
|
| positive = pipe['positive'] |
| negative = pipe['negative'] |
| model = pipe['model'] |
| vae = pipe['vae'] |
|
|
| if controlnet_stack is not None and len(controlnet_stack) >0: |
| for controlnet in controlnet_stack: |
| positive, negative = easyControlnet().apply(controlnet[0], controlnet[5], positive, negative, controlnet[1], start_percent=controlnet[2], end_percent=controlnet[3], control_net=None, scale_soft_weights=controlnet[4], mask=None, easyCache=easyCache, use_cache=False, model=model, vae=vae) |
|
|
| new_pipe = { |
| **pipe, |
| "positive": positive, |
| "negetive": negative, |
| } |
| del pipe |
|
|
| return (new_pipe,) |
|
|
| |
| from ..libs.styleAlign import styleAlignBatch, SHARE_NORM_OPTIONS, SHARE_ATTN_OPTIONS |
| class styleAlignedBatchAlign: |
|
|
| @classmethod |
| def INPUT_TYPES(cls): |
| return { |
| "required": { |
| "model": ("MODEL",), |
| "share_norm": (SHARE_NORM_OPTIONS,), |
| "share_attn": (SHARE_ATTN_OPTIONS,), |
| "scale": ("FLOAT", {"default": 1, "min": 0, "max": 1.0, "step": 0.1}), |
| } |
| } |
|
|
| RETURN_TYPES = ("MODEL",) |
| FUNCTION = "align" |
| CATEGORY = "EasyUse/Adapter" |
|
|
| def align(self, model, share_norm, share_attn, scale): |
| return (styleAlignBatch(model, share_norm, share_attn, scale),) |
|
|
| |
| from ..modules.ic_light import ICLight, VAEEncodeArgMax |
| class icLightApply: |
|
|
| @classmethod |
| def INPUT_TYPES(cls): |
| return { |
| "required": { |
| "mode": (list(IC_LIGHT_MODELS.keys()),), |
| "model": ("MODEL",), |
| "image": ("IMAGE",), |
| "vae": ("VAE",), |
| "lighting": (['None', 'Left Light', 'Right Light', 'Top Light', 'Bottom Light', 'Circle Light'],{"default": "None"}), |
| "source": (['Use Background Image', 'Use Flipped Background Image', 'Left Light', 'Right Light', 'Top Light', 'Bottom Light', 'Ambient'],{"default": "Use Background Image"}), |
| "remove_bg": ("BOOLEAN", {"default": True}), |
| }, |
| } |
|
|
| RETURN_TYPES = ("MODEL", "IMAGE") |
| RETURN_NAMES = ("model", "lighting_image") |
| FUNCTION = "apply" |
| CATEGORY = "EasyUse/Adapter" |
|
|
| def batch(self, image1, image2): |
| if image1.shape[1:] != image2.shape[1:]: |
| image2 = comfy.utils.common_upscale(image2.movedim(-1, 1), image1.shape[2], image1.shape[1], "bilinear", |
| "center").movedim(1, -1) |
| s = torch.cat((image1, image2), dim=0) |
| return s |
|
|
| def removebg(self, image): |
| if "easy imageRemBg" not in ALL_NODE_CLASS_MAPPINGS: |
| raise Exception("Please re-install ComfyUI-Easy-Use") |
| cls = ALL_NODE_CLASS_MAPPINGS['easy imageRemBg'] |
| results = cls().remove('RMBG-1.4', image, 'Hide', 'ComfyUI') |
| if "result" in results: |
| image, _ = results['result'] |
| return image |
|
|
| def apply(self, mode, model, image, vae, lighting, source, remove_bg): |
| model_type = get_sd_version(model) |
| if model_type == 'sdxl': |
| raise Exception("IC Light model is not supported for SDXL now") |
|
|
| batch_size, height, width, channel = image.shape |
| if channel == 3: |
| |
| if mode == 'Foreground' or batch_size == 1: |
| if remove_bg: |
| image = self.removebg(image) |
| else: |
| mask = torch.full((1, height, width), 1.0, dtype=torch.float32, device="cpu") |
| try: |
| image, = JoinImageWithAlpha().execute(image, mask) |
| except: |
| image, = JoinImageWithAlpha().join_image_with_alpha(image, mask) |
|
|
| iclight = ICLight() |
| if mode == 'Foreground': |
| lighting_image = iclight.generate_lighting_image(image, lighting) |
| else: |
| lighting_image = iclight.generate_source_image(image, source) |
| if source not in ['Use Background Image', 'Use Flipped Background Image']: |
| _, height, width, _ = lighting_image.shape |
| mask = torch.full((1, height, width), 1.0, dtype=torch.float32, device="cpu") |
| try: |
| lighting_image, = JoinImageWithAlpha().execute(lighting_image, mask) |
| except: |
| lighting_image, = JoinImageWithAlpha().join_image_with_alpha(lighting_image, mask) |
| if batch_size < 2: |
| image = self.batch(image, lighting_image) |
| else: |
| original_image = [img.unsqueeze(0) for img in image] |
| original_image = self.removebg(original_image[0]) |
| image = self.batch(original_image, lighting_image) |
|
|
| latent, = VAEEncodeArgMax().encode(vae, image) |
| key = 'iclight_' + mode + '_' + model_type |
| model_path = get_local_filepath(IC_LIGHT_MODELS[mode]['sd1']["model_url"], |
| os.path.join(folder_paths.models_dir, "unet")) |
| ic_model = None |
| if key in backend_cache.cache: |
| log_node_info("easy icLightApply", f"Using icLightModel {mode+'_'+model_type} Cached") |
| _, ic_model = backend_cache.cache[key][1] |
| m, _ = iclight.apply(model_path, model, latent, ic_model) |
| else: |
| m, ic_model = iclight.apply(model_path, model, latent, ic_model) |
| backend_cache.update_cache(key, 'iclight', (False, ic_model)) |
| return (m, lighting_image) |
|
|
|
|
| def insightface_loader(provider, name='buffalo_l'): |
| try: |
| from insightface.app import FaceAnalysis |
| except ImportError as e: |
| raise Exception(e) |
| path = os.path.join(folder_paths.models_dir, "insightface") |
| model = FaceAnalysis(name=name, root=path, providers=[provider + 'ExecutionProvider', ]) |
| model.prepare(ctx_id=0, det_size=(640, 640)) |
| return model |
|
|
| |
| class ipadapter: |
|
|
| def __init__(self): |
| self.normal_presets = [ |
| 'LIGHT - SD1.5 only (low strength)', |
| 'STANDARD (medium strength)', |
| 'VIT-G (medium strength)', |
| 'PLUS (high strength)', |
| 'PLUS (kolors genernal)', |
| 'REGULAR - FLUX and SD3.5 only (high strength)', |
| 'PLUS FACE (portraits)', |
| 'FULL FACE - SD1.5 only (portraits stronger)', |
| 'COMPOSITION' |
| ] |
| self.faceid_presets = [ |
| 'FACEID', |
| 'FACEID PLUS - SD1.5 only', |
| "FACEID PLUS KOLORS", |
| 'FACEID PLUS V2', |
| 'FACEID PORTRAIT (style transfer)', |
| 'FACEID PORTRAIT UNNORM - SDXL only (strong)' |
| ] |
| self.weight_types = ["linear", "ease in", "ease out", 'ease in-out', 'reverse in-out', 'weak input', 'weak output', 'weak middle', 'strong middle', 'style transfer', 'composition', 'strong style transfer', 'style and composition', 'style transfer precise'] |
| self.presets = self.normal_presets + self.faceid_presets |
|
|
|
|
| def error(self): |
| raise Exception(f"[ERROR] To use ipadapterApply, you need to install 'ComfyUI_IPAdapter_plus'") |
|
|
| def get_clipvision_file(self, preset, node_name): |
| preset = preset.lower() |
| clipvision_list = folder_paths.get_filename_list("clip_vision") |
| if preset.startswith("regular"): |
| |
| pattern = 'siglip.so400m.patch14.384' |
| elif preset.startswith("plus (kolors") or preset.startswith("faceid plus kolors"): |
| pattern = 'Vit.Large.patch14.336.(bin|safetensors)$' |
| elif preset.startswith("vit-g"): |
| pattern = '(ViT.bigG.14.*39B.b160k|ipadapter.*sdxl|sdxl.*model.(bin|safetensors))' |
| else: |
| pattern = '(ViT.H.14.*s32B.b79K|ipadapter.*sd15|sd1.?5.*model.(bin|safetensors))' |
| clipvision_files = [e for e in clipvision_list if re.search(pattern, e, re.IGNORECASE)] |
| clipvision_name = clipvision_files[0] if len(clipvision_files)>0 else None |
| clipvision_file = folder_paths.get_full_path("clip_vision", clipvision_name) if clipvision_name else None |
| |
| |
| return clipvision_file, clipvision_name |
|
|
| def get_ipadapter_file(self, preset, model_type, node_name): |
| preset = preset.lower() |
| ipadapter_list = folder_paths.get_filename_list("ipadapter") |
| is_insightface = False |
| lora_pattern = None |
| is_sdxl = model_type == 'sdxl' |
| is_flux = model_type == 'flux' |
|
|
| if preset.startswith("light"): |
| if is_sdxl: |
| raise Exception("light model is not supported for SDXL") |
| pattern = 'sd15.light.v11.(safetensors|bin)$' |
| |
| if not [e for e in ipadapter_list if re.search(pattern, e, re.IGNORECASE)]: |
| pattern = 'sd15.light.(safetensors|bin)$' |
| elif preset.startswith("standard"): |
| if is_sdxl: |
| pattern = 'ip.adapter.sdxl.vit.h.(safetensors|bin)$' |
| else: |
| pattern = 'ip.adapter.sd15.(safetensors|bin)$' |
| elif preset.startswith("vit-g"): |
| if is_sdxl: |
| pattern = 'ip.adapter.sdxl.(safetensors|bin)$' |
| else: |
| pattern = 'sd15.vit.g.(safetensors|bin)$' |
| elif preset.startswith("regular"): |
| if is_flux: |
| pattern = 'ip.adapter.flux.1.dev.(safetensors|bin)$' |
| else: |
| pattern = 'ip.adapter.sd35.(safetensors|bin)$' |
| elif preset.startswith("plus (high"): |
| if is_sdxl: |
| pattern = 'plus.sdxl.vit.h.(safetensors|bin)$' |
| else: |
| pattern = 'ip.adapter.plus.sd15.(safetensors|bin)$' |
| elif preset.startswith("plus (kolors"): |
| if is_sdxl: |
| pattern = 'plus.gener(nal|al).(safetensors|bin)$' |
| else: |
| raise Exception("kolors model is not supported for SD15") |
| elif preset.startswith("plus face"): |
| if is_sdxl: |
| pattern = 'plus.face.sdxl.vit.h.(safetensors|bin)$' |
| else: |
| pattern = 'plus.face.sd15.(safetensors|bin)$' |
| elif preset.startswith("full"): |
| if is_sdxl: |
| raise Exception("full face model is not supported for SDXL") |
| pattern = 'full.face.sd15.(safetensors|bin)$' |
| elif preset.startswith("composition"): |
| if is_sdxl: |
| pattern = 'plus.composition.sdxl.(safetensors|bin)$' |
| else: |
| pattern = 'plus.composition.sd15.(safetensors|bin)$' |
| elif preset.startswith("faceid portrait ("): |
| if is_sdxl: |
| pattern = 'portrait.sdxl.(safetensors|bin)$' |
| else: |
| pattern = 'portrait.v11.sd15.(safetensors|bin)$' |
| |
| if not [e for e in ipadapter_list if re.search(pattern, e, re.IGNORECASE)]: |
| pattern = 'portrait.sd15.(safetensors|bin)$' |
| is_insightface = True |
| elif preset.startswith("faceid portrait unnorm"): |
| if is_sdxl: |
| pattern = r'portrait.sdxl.unnorm.(safetensors|bin)$' |
| else: |
| raise Exception("portrait unnorm model is not supported for SD1.5") |
| is_insightface = True |
| elif preset == "faceid": |
| if is_sdxl: |
| pattern = 'faceid.sdxl.(safetensors|bin)$' |
| lora_pattern = 'faceid.sdxl.lora.safetensors$' |
| else: |
| pattern = 'faceid.sd15.(safetensors|bin)$' |
| lora_pattern = 'faceid.sd15.lora.safetensors$' |
| is_insightface = True |
| elif preset.startswith("faceid plus kolors"): |
| if is_sdxl: |
| pattern = '(kolors.ip.adapter.faceid.plus|ipa.faceid.plus).(safetensors|bin)$' |
| else: |
| raise Exception("faceid plus kolors model is not supported for SD1.5") |
| is_insightface = True |
| elif preset.startswith("faceid plus -"): |
| if is_sdxl: |
| raise Exception("faceid plus model is not supported for SDXL") |
| pattern = 'faceid.plus.sd15.(safetensors|bin)$' |
| lora_pattern = 'faceid.plus.sd15.lora.safetensors$' |
| is_insightface = True |
| elif preset.startswith("faceid plus v2"): |
| if is_sdxl: |
| pattern = 'faceid.plusv2.sdxl.(safetensors|bin)$' |
| lora_pattern = 'faceid.plusv2.sdxl.lora.safetensors$' |
| else: |
| pattern = 'faceid.plusv2.sd15.(safetensors|bin)$' |
| lora_pattern = 'faceid.plusv2.sd15.lora.safetensors$' |
| is_insightface = True |
| else: |
| raise Exception(f"invalid type '{preset}'") |
|
|
| ipadapter_files = [e for e in ipadapter_list if re.search(pattern, e, re.IGNORECASE)] |
| ipadapter_name = ipadapter_files[0] if len(ipadapter_files)>0 else None |
| ipadapter_file = folder_paths.get_full_path("ipadapter", ipadapter_name) if ipadapter_name else None |
| |
| |
|
|
| return ipadapter_file, ipadapter_name, is_insightface, lora_pattern |
|
|
| def get_lora_pattern(self, file): |
| basename = os.path.basename(file) |
| lora_pattern = None |
| if re.search(r'faceid.sdxl.(safetensors|bin)$', basename, re.IGNORECASE): |
| lora_pattern = 'faceid.sdxl.lora.safetensors$' |
| elif re.search(r'faceid.sd15.(safetensors|bin)$', basename, re.IGNORECASE): |
| lora_pattern = 'faceid.sd15.lora.safetensors$' |
| elif re.search(r'faceid.plus.sd15.(safetensors|bin)$', basename, re.IGNORECASE): |
| lora_pattern = 'faceid.plus.sd15.lora.safetensors$' |
| elif re.search(r'faceid.plusv2.sdxl.(safetensors|bin)$', basename, re.IGNORECASE): |
| lora_pattern = 'faceid.plusv2.sdxl.lora.safetensors$' |
| elif re.search(r'faceid.plusv2.sd15.(safetensors|bin)$', basename, re.IGNORECASE): |
| lora_pattern = 'faceid.plusv2.sd15.lora.safetensors$' |
|
|
| return lora_pattern |
|
|
| def get_lora_file(self, preset, pattern, model_type, model, model_strength, clip_strength, clip=None): |
| lora_list = folder_paths.get_filename_list("loras") |
| lora_files = [e for e in lora_list if re.search(pattern, e, re.IGNORECASE)] |
| lora_name = lora_files[0] if lora_files else None |
| if lora_name: |
| return easyCache.load_lora({"model": model, "clip": clip, "lora_name": lora_name, "model_strength":model_strength, "clip_strength":clip_strength},) |
| else: |
| if "lora_url" in IPADAPTER_MODELS[preset][model_type]: |
| lora_name = get_local_filepath(IPADAPTER_MODELS[preset][model_type]["lora_url"], os.path.join(folder_paths.models_dir, "loras")) |
| return easyCache.load_lora({"model": model, "clip": clip, "lora_name": lora_name, "model_strength":model_strength, "clip_strength":clip_strength},) |
| return (model, clip) |
|
|
| def ipadapter_model_loader(self, file): |
| model = comfy.utils.load_torch_file(file, safe_load=False) |
|
|
| if file.lower().endswith(".safetensors"): |
| st_model = {"image_proj": {}, "ip_adapter": {}} |
| for key in model.keys(): |
| if key.startswith("image_proj."): |
| st_model["image_proj"][key.replace("image_proj.", "")] = model[key] |
| elif key.startswith("ip_adapter."): |
| st_model["ip_adapter"][key.replace("ip_adapter.", "")] = model[key] |
| model = st_model |
| del st_model |
|
|
| model_keys = model.keys() |
| if "adapter_modules" in model_keys: |
| model["ip_adapter"] = model["adapter_modules"] |
| model["faceidplusv2"] = True |
| del model['adapter_modules'] |
|
|
| if not "ip_adapter" in model_keys or not model["ip_adapter"]: |
| raise Exception("invalid IPAdapter model {}".format(file)) |
|
|
| if 'plusv2' in file.lower(): |
| model["faceidplusv2"] = True |
|
|
| if 'unnorm' in file.lower(): |
| model["portraitunnorm"] = True |
|
|
| return model |
|
|
| def load_model(self, model, preset, lora_model_strength, provider="CPU", clip_vision=None, optional_ipadapter=None, cache_mode='none', node_name='easy ipadapterApply'): |
| pipeline = {"clipvision": {'file': None, 'model': None}, "ipadapter": {'file': None, 'model': None}, |
| "insightface": {'provider': None, 'model': None}} |
| ipadapter, insightface, is_insightface, lora_pattern = None, None, None, None |
| if optional_ipadapter is not None: |
| pipeline = optional_ipadapter |
| if not clip_vision: |
| clip_vision = pipeline['clipvision']['model'] |
| ipadapter = pipeline['ipadapter']['model'] |
| if 'insightface' in pipeline: |
| insightface = pipeline['insightface']['model'] |
| lora_pattern = self.get_lora_pattern(pipeline['ipadapter']['file']) |
|
|
| |
| if not clip_vision: |
| clipvision_file, clipvision_name = self.get_clipvision_file(preset, node_name) |
| if clipvision_file is None: |
| if preset.lower().startswith("regular"): |
| |
| |
| from huggingface_hub import snapshot_download |
| import shutil |
| CLIP_PATH = os.path.join(folder_paths.models_dir, "clip_vision", "google--siglip-so400m-patch14-384") |
| print("CLIP_VISION not found locally. Downloading google/siglip-so400m-patch14-384...") |
| try: |
| snapshot_download( |
| repo_id="google/siglip-so400m-patch14-384", |
| local_dir=os.path.join(folder_paths.models_dir, "clip_vision", |
| "cache--google--siglip-so400m-patch14-384"), |
| local_dir_use_symlinks=False, |
| resume_download=True |
| ) |
| shutil.move(os.path.join(folder_paths.models_dir, "clip_vision", |
| "cache--google--siglip-so400m-patch14-384"), CLIP_PATH) |
| print(f"CLIP_VISION has been downloaded to {CLIP_PATH}") |
| except Exception as e: |
| print(f"Error downloading CLIP model: {e}") |
| raise |
| clipvision_file = CLIP_PATH |
| elif preset.lower().startswith("plus (kolors"): |
| model_url = IPADAPTER_CLIPVISION_MODELS["clip-vit-large-patch14-336"]["model_url"] |
| clipvision_file = get_local_filepath(model_url, IPADAPTER_DIR, "clip-vit-large-patch14-336.bin") |
| else: |
| model_url = IPADAPTER_CLIPVISION_MODELS["clip-vit-h-14-laion2B-s32B-b79K"]["model_url"] |
| clipvision_file = get_local_filepath(model_url, IPADAPTER_DIR, "clip-vit-h-14-laion2B-s32B-b79K.safetensors") |
| clipvision_name = os.path.basename(model_url) |
| if clipvision_file == pipeline['clipvision']['file']: |
| clip_vision = pipeline['clipvision']['model'] |
| elif cache_mode in ["all", "clip_vision only"] and clipvision_name in backend_cache.cache: |
| log_node_info("easy ipadapterApply", f"Using ClipVisonModel {clipvision_name} Cached") |
| _, clip_vision = backend_cache.cache[clipvision_name][1] |
| else: |
| if preset.lower().startswith("regular"): |
| from transformers import SiglipVisionModel, AutoProcessor |
| image_encoder_path = os.path.dirname(clipvision_file) |
| image_encoder = SiglipVisionModel.from_pretrained(image_encoder_path) |
| clip_image_processor = AutoProcessor.from_pretrained(image_encoder_path) |
| clip_vision = { |
| 'image_encoder': image_encoder, |
| 'clip_image_processor': clip_image_processor |
| } |
| else: |
| clip_vision = load_clip_vision(clipvision_file) |
| log_node_info("easy ipadapterApply", f"Using ClipVisonModel {clipvision_name}") |
| if cache_mode in ["all", "clip_vision only"]: |
| backend_cache.update_cache(clipvision_name, 'clip_vision', (False, clip_vision)) |
| pipeline['clipvision']['file'] = clipvision_file |
| pipeline['clipvision']['model'] = clip_vision |
| |
| model_type = get_sd_version(model) |
| if not ipadapter: |
| ipadapter_file, ipadapter_name, is_insightface, lora_pattern = self.get_ipadapter_file(preset, model_type, node_name) |
| if ipadapter_file is None: |
| model_url = IPADAPTER_MODELS[preset][model_type]["model_url"] |
| local_file_name = IPADAPTER_MODELS[preset][model_type]['model_file_name'] if "model_file_name" in IPADAPTER_MODELS[preset][model_type] else None |
| ipadapter_file = get_local_filepath(model_url, IPADAPTER_DIR, local_file_name) |
| ipadapter_name = os.path.basename(model_url) |
| if ipadapter_file == pipeline['ipadapter']['file']: |
| ipadapter = pipeline['ipadapter']['model'] |
| elif cache_mode in ["all", "ipadapter only"] and ipadapter_name in backend_cache.cache: |
| log_node_info("easy ipadapterApply", f"Using IpAdapterModel {ipadapter_name} Cached") |
| _, ipadapter = backend_cache.cache[ipadapter_name][1] |
| else: |
| ipadapter = self.ipadapter_model_loader(ipadapter_file) |
| pipeline['ipadapter']['file'] = ipadapter_file |
| log_node_info("easy ipadapterApply", f"Using IpAdapterModel {ipadapter_name}") |
| if cache_mode in ["all", "ipadapter only"]: |
| backend_cache.update_cache(ipadapter_name, 'ipadapter', (False, ipadapter)) |
|
|
| pipeline['ipadapter']['model'] = ipadapter |
|
|
| |
| if lora_pattern is not None: |
| if lora_model_strength > 0: |
| model, _ = self.get_lora_file(preset, lora_pattern, model_type, model, lora_model_strength, 1) |
|
|
| |
| if is_insightface: |
| if not insightface: |
| icache_key = 'insightface-' + provider |
| if provider == pipeline['insightface']['provider']: |
| insightface = pipeline['insightface']['model'] |
| elif cache_mode in ["all", "insightface only"] and icache_key in backend_cache.cache: |
| log_node_info("easy ipadapterApply", f"Using InsightFaceModel {icache_key} Cached") |
| _, insightface = backend_cache.cache[icache_key][1] |
| else: |
| insightface = insightface_loader(provider, 'antelopev2' if preset == 'FACEID PLUS KOLORS' else 'buffalo_l') |
| if cache_mode in ["all", "insightface only"]: |
| backend_cache.update_cache(icache_key, 'insightface',(False, insightface)) |
| pipeline['insightface']['provider'] = provider |
| pipeline['insightface']['model'] = insightface |
|
|
| return (model, pipeline,) |
|
|
| class ipadapterApply(ipadapter): |
| def __init__(self): |
| super().__init__() |
| pass |
|
|
| @classmethod |
| def INPUT_TYPES(cls): |
| presets = cls().presets |
| return { |
| "required": { |
| "model": ("MODEL",), |
| "image": ("IMAGE",), |
| "preset": (presets,), |
| "lora_strength": ("FLOAT", {"default": 0.6, "min": 0, "max": 1, "step": 0.01}), |
| "provider": (["CPU", "CUDA", "ROCM", "DirectML", "OpenVINO", "CoreML"], {"default": "CUDA"}), |
| "weight": ("FLOAT", {"default": 1.0, "min": -1, "max": 3, "step": 0.05}), |
| "weight_faceidv2": ("FLOAT", { "default": 1.0, "min": -1, "max": 5.0, "step": 0.05 }), |
| "start_at": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.001}), |
| "end_at": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.001}), |
| "cache_mode": (["insightface only", "clip_vision only", "ipadapter only", "all", "none"], {"default": "all"},), |
| "use_tiled": ("BOOLEAN", {"default": False},), |
| }, |
|
|
| "optional": { |
| "attn_mask": ("MASK",), |
| "optional_ipadapter": ("IPADAPTER",), |
| } |
| } |
|
|
| RETURN_TYPES = ("MODEL", "IMAGE", "MASK", "IPADAPTER",) |
| RETURN_NAMES = ("model", "images", "masks", "ipadapter", ) |
| CATEGORY = "EasyUse/Adapter" |
| FUNCTION = "apply" |
|
|
| def apply(self, model, image, preset, lora_strength, provider, weight, weight_faceidv2, start_at, end_at, cache_mode, use_tiled, attn_mask=None, optional_ipadapter=None, weight_kolors=None): |
| images, masks = image, [None] |
| model, ipadapter = self.load_model(model, preset, lora_strength, provider, clip_vision=None, optional_ipadapter=optional_ipadapter, cache_mode=cache_mode) |
| if preset == 'REGULAR - FLUX and SD3.5 only (high strength)': |
| from ..modules.ipadapter import InstantXFluxIpadapterApply, InstantXSD3IpadapterApply |
| model_type = get_sd_version(model) |
| if model_type == 'flux': |
| model, images = InstantXFluxIpadapterApply().apply_ipadapter(model, ipadapter, image, weight, start_at, end_at, provider) |
| elif model_type == 'sd3': |
| model, images = InstantXSD3IpadapterApply().apply_ipadapter(model, ipadapter, image, weight, start_at, end_at, provider) |
| elif use_tiled and preset not in self.faceid_presets: |
| if "IPAdapterTiled" not in ALL_NODE_CLASS_MAPPINGS: |
| self.error() |
| cls = ALL_NODE_CLASS_MAPPINGS["IPAdapterTiled"] |
| model, images, masks = cls().apply_tiled(model, ipadapter, image, weight, "linear", start_at, end_at, sharpening=0.0, combine_embeds="concat", image_negative=None, attn_mask=attn_mask, clip_vision=None, embeds_scaling='V only') |
| else: |
| if preset in ['FACEID PLUS KOLORS', 'FACEID PLUS V2', 'FACEID PORTRAIT (style transfer)']: |
| if "IPAdapterAdvanced" not in ALL_NODE_CLASS_MAPPINGS: |
| self.error() |
| cls = ALL_NODE_CLASS_MAPPINGS["IPAdapterAdvanced"] |
| if weight_kolors is None: |
| weight_kolors = weight |
| model, images = cls().apply_ipadapter(model, ipadapter, start_at=start_at, end_at=end_at, weight=weight, weight_type="linear", combine_embeds="concat", weight_faceidv2=weight_faceidv2, image=image, image_negative=None, clip_vision=None, attn_mask=attn_mask, insightface=None, embeds_scaling='V only', weight_kolors=weight_kolors) |
| else: |
| if "IPAdapter" not in ALL_NODE_CLASS_MAPPINGS: |
| self.error() |
| cls = ALL_NODE_CLASS_MAPPINGS["IPAdapter"] |
| model, images = cls().apply_ipadapter(model, ipadapter, image, weight, start_at, end_at, weight_type='standard', attn_mask=attn_mask) |
| if images is None: |
| images = image |
| return (model, images, masks, ipadapter,) |
|
|
| class ipadapterApplyAdvanced(ipadapter): |
| def __init__(self): |
| super().__init__() |
| pass |
|
|
| @classmethod |
| def INPUT_TYPES(cls): |
| ipa_cls = cls() |
| presets = ipa_cls.presets |
| weight_types = ipa_cls.weight_types |
| return { |
| "required": { |
| "model": ("MODEL",), |
| "image": ("IMAGE",), |
| "preset": (presets,), |
| "lora_strength": ("FLOAT", {"default": 0.6, "min": 0, "max": 1, "step": 0.01}), |
| "provider": (["CPU", "CUDA", "ROCM", "DirectML", "OpenVINO", "CoreML"], {"default": "CUDA"}), |
| "weight": ("FLOAT", {"default": 1.0, "min": -1, "max": 3, "step": 0.05}), |
| "weight_faceidv2": ("FLOAT", {"default": 1.0, "min": -1, "max": 5.0, "step": 0.05 }), |
| "weight_type": (weight_types,), |
| "combine_embeds": (["concat", "add", "subtract", "average", "norm average"],), |
| "start_at": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.001}), |
| "end_at": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.001}), |
| "embeds_scaling": (['V only', 'K+V', 'K+V w/ C penalty', 'K+mean(V) w/ C penalty'],), |
| "cache_mode": (["insightface only", "clip_vision only","ipadapter only", "all", "none"], {"default": "all"},), |
| "use_tiled": ("BOOLEAN", {"default": False},), |
| "use_batch": ("BOOLEAN", {"default": False},), |
| "sharpening": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.05}), |
| }, |
|
|
| "optional": { |
| "image_negative": ("IMAGE",), |
| "attn_mask": ("MASK",), |
| "clip_vision": ("CLIP_VISION",), |
| "optional_ipadapter": ("IPADAPTER",), |
| "layer_weights": ("STRING", {"default": "", "multiline": True, "placeholder": "Mad Scientist Layer Weights"}), |
| } |
| } |
|
|
| RETURN_TYPES = ("MODEL", "IMAGE", "MASK", "IPADAPTER",) |
| RETURN_NAMES = ("model", "images", "masks", "ipadapter", ) |
| CATEGORY = "EasyUse/Adapter" |
| FUNCTION = "apply" |
|
|
| def apply(self, model, image, preset, lora_strength, provider, weight, weight_faceidv2, weight_type, combine_embeds, start_at, end_at, embeds_scaling, cache_mode, use_tiled, use_batch, sharpening, weight_style=1.0, weight_composition=1.0, image_style=None, image_composition=None, expand_style=False, image_negative=None, clip_vision=None, attn_mask=None, optional_ipadapter=None, layer_weights=None, weight_kolors=None): |
| images, masks = image, [None] |
| model, ipadapter = self.load_model(model, preset, lora_strength, provider, clip_vision=clip_vision, optional_ipadapter=optional_ipadapter, cache_mode=cache_mode) |
|
|
| if weight_kolors is None: |
| weight_kolors = weight |
|
|
| if layer_weights: |
| if "IPAdapterMS" not in ALL_NODE_CLASS_MAPPINGS: |
| self.error() |
| cls = ALL_NODE_CLASS_MAPPINGS["IPAdapterAdvanced"] |
| model, images = cls().apply_ipadapter(model, ipadapter, weight=weight, weight_type=weight_type, start_at=start_at, end_at=end_at, combine_embeds=combine_embeds, weight_faceidv2=weight_faceidv2, image=image, image_negative=image_negative, weight_style=weight_style, weight_composition=weight_composition, image_style=image_style, image_composition=image_composition, expand_style=expand_style, clip_vision=clip_vision, attn_mask=attn_mask, insightface=None, embeds_scaling=embeds_scaling, layer_weights=layer_weights, weight_kolors=weight_kolors) |
| elif use_tiled: |
| if use_batch: |
| if "IPAdapterTiledBatch" not in ALL_NODE_CLASS_MAPPINGS: |
| self.error() |
| cls = ALL_NODE_CLASS_MAPPINGS["IPAdapterTiledBatch"] |
| else: |
| if "IPAdapterTiled" not in ALL_NODE_CLASS_MAPPINGS: |
| self.error() |
| cls = ALL_NODE_CLASS_MAPPINGS["IPAdapterTiled"] |
| model, images, masks = cls().apply_tiled(model, ipadapter, image=image, weight=weight, weight_type=weight_type, start_at=start_at, end_at=end_at, sharpening=sharpening, combine_embeds=combine_embeds, image_negative=image_negative, attn_mask=attn_mask, clip_vision=clip_vision, embeds_scaling=embeds_scaling) |
| else: |
| if use_batch: |
| if "IPAdapterBatch" not in ALL_NODE_CLASS_MAPPINGS: |
| self.error() |
| cls = ALL_NODE_CLASS_MAPPINGS["IPAdapterBatch"] |
| else: |
| if "IPAdapterAdvanced" not in ALL_NODE_CLASS_MAPPINGS: |
| self.error() |
| cls = ALL_NODE_CLASS_MAPPINGS["IPAdapterAdvanced"] |
| model, images = cls().apply_ipadapter(model, ipadapter, weight=weight, weight_type=weight_type, start_at=start_at, end_at=end_at, combine_embeds=combine_embeds, weight_faceidv2=weight_faceidv2, image=image, image_negative=image_negative, weight_style=1.0, weight_composition=1.0, image_style=image_style, image_composition=image_composition, expand_style=expand_style, clip_vision=clip_vision, attn_mask=attn_mask, insightface=None, embeds_scaling=embeds_scaling, weight_kolors=weight_kolors) |
| if images is None: |
| images = image |
| return (model, images, masks, ipadapter) |
|
|
| class ipadapterApplyFaceIDKolors(ipadapterApplyAdvanced): |
|
|
| @classmethod |
| def INPUT_TYPES(cls): |
| ipa_cls = cls() |
| presets = ipa_cls.presets |
| weight_types = ipa_cls.weight_types |
| return { |
| "required": { |
| "model": ("MODEL",), |
| "image": ("IMAGE",), |
| "preset": (['FACEID PLUS KOLORS'], {"default":"FACEID PLUS KOLORS"}), |
| "lora_strength": ("FLOAT", {"default": 0.6, "min": 0, "max": 1, "step": 0.01}), |
| "provider": (["CPU", "CUDA", "ROCM", "DirectML", "OpenVINO", "CoreML"], {"default": "CUDA"}), |
| "weight": ("FLOAT", {"default": 0.8, "min": -1, "max": 3, "step": 0.05}), |
| "weight_faceidv2": ("FLOAT", {"default": 1.0, "min": -1, "max": 5.0, "step": 0.05}), |
| "weight_kolors": ("FLOAT", {"default": 0.8, "min": -1, "max": 5.0, "step": 0.05}), |
| "weight_type": (weight_types,), |
| "combine_embeds": (["concat", "add", "subtract", "average", "norm average"],), |
| "start_at": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.001}), |
| "end_at": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.001}), |
| "embeds_scaling": (['V only', 'K+V', 'K+V w/ C penalty', 'K+mean(V) w/ C penalty'],), |
| "cache_mode": (["insightface only", "clip_vision only", "ipadapter only", "all", "none"], {"default": "all"},), |
| "use_tiled": ("BOOLEAN", {"default": False},), |
| "use_batch": ("BOOLEAN", {"default": False},), |
| "sharpening": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.05}), |
| }, |
|
|
| "optional": { |
| "image_negative": ("IMAGE",), |
| "attn_mask": ("MASK",), |
| "clip_vision": ("CLIP_VISION",), |
| "optional_ipadapter": ("IPADAPTER",), |
| } |
| } |
|
|
|
|
| class ipadapterStyleComposition(ipadapter): |
| def __init__(self): |
| super().__init__() |
| pass |
|
|
| @classmethod |
| def INPUT_TYPES(cls): |
| ipa_cls = cls() |
| normal_presets = ipa_cls.normal_presets |
| weight_types = ipa_cls.weight_types |
| return { |
| "required": { |
| "model": ("MODEL",), |
| "image_style": ("IMAGE",), |
| "preset": (normal_presets,), |
| "weight_style": ("FLOAT", {"default": 1.0, "min": -1, "max": 5, "step": 0.05}), |
| "weight_composition": ("FLOAT", {"default": 1.0, "min": -1, "max": 5, "step": 0.05}), |
| "expand_style": ("BOOLEAN", {"default": False}), |
| "combine_embeds": (["concat", "add", "subtract", "average", "norm average"], {"default": "average"}), |
| "start_at": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.001}), |
| "end_at": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.001}), |
| "embeds_scaling": (['V only', 'K+V', 'K+V w/ C penalty', 'K+mean(V) w/ C penalty'],), |
| "cache_mode": (["insightface only", "clip_vision only", "ipadapter only", "all", "none"], |
| {"default": "all"},), |
| }, |
| "optional": { |
| "image_composition": ("IMAGE",), |
| "image_negative": ("IMAGE",), |
| "attn_mask": ("MASK",), |
| "clip_vision": ("CLIP_VISION",), |
| "optional_ipadapter": ("IPADAPTER",), |
| } |
| } |
|
|
| CATEGORY = "EasyUse/Adapter" |
|
|
| RETURN_TYPES = ("MODEL", "IPADAPTER",) |
| RETURN_NAMES = ("model", "ipadapter",) |
| CATEGORY = "EasyUse/Adapter" |
| FUNCTION = "apply" |
|
|
| def apply(self, model, preset, weight_style, weight_composition, expand_style, combine_embeds, start_at, end_at, embeds_scaling, cache_mode, image_style=None , image_composition=None, image_negative=None, clip_vision=None, attn_mask=None, optional_ipadapter=None): |
| model, ipadapter = self.load_model(model, preset, 0, 'CPU', clip_vision=None, optional_ipadapter=optional_ipadapter, cache_mode=cache_mode) |
|
|
| if "IPAdapterAdvanced" not in ALL_NODE_CLASS_MAPPINGS: |
| self.error() |
| cls = ALL_NODE_CLASS_MAPPINGS["IPAdapterAdvanced"] |
|
|
| model, image = cls().apply_ipadapter(model, ipadapter, start_at=start_at, end_at=end_at, weight_style=weight_style, weight_composition=weight_composition, weight_type='linear', combine_embeds=combine_embeds, weight_faceidv2=weight_composition, image_style=image_style, image_composition=image_composition, image_negative=image_negative, expand_style=expand_style, clip_vision=clip_vision, attn_mask=attn_mask, insightface=None, embeds_scaling=embeds_scaling) |
| return (model, ipadapter) |
|
|
| class ipadapterApplyEncoder(ipadapter): |
| def __init__(self): |
| super().__init__() |
| pass |
|
|
| @classmethod |
| def INPUT_TYPES(cls): |
| ipa_cls = cls() |
| normal_presets = ipa_cls.normal_presets |
| max_embeds_num = 4 |
| inputs = { |
| "required": { |
| "model": ("MODEL",), |
| "clip_vision": ("CLIP_VISION",), |
| "image1": ("IMAGE",), |
| "preset": (normal_presets,), |
| "num_embeds": ("INT", {"default": 2, "min": 1, "max": max_embeds_num}), |
| }, |
| "optional": {} |
| } |
|
|
| for i in range(1, max_embeds_num + 1): |
| if i > 1: |
| inputs["optional"][f"image{i}"] = ("IMAGE",) |
| for i in range(1, max_embeds_num + 1): |
| inputs["optional"][f"mask{i}"] = ("MASK",) |
| inputs["optional"][f"weight{i}"] = ("FLOAT", {"default": 1.0, "min": -1, "max": 3, "step": 0.05}) |
| inputs["optional"]["combine_method"] = (["concat", "add", "subtract", "average", "norm average", "max", "min"],) |
| inputs["optional"]["optional_ipadapter"] = ("IPADAPTER",) |
| inputs["optional"]["pos_embeds"] = ("EMBEDS",) |
| inputs["optional"]["neg_embeds"] = ("EMBEDS",) |
| return inputs |
|
|
| RETURN_TYPES = ("MODEL", "CLIP_VISION","IPADAPTER", "EMBEDS", "EMBEDS", ) |
| RETURN_NAMES = ("model", "clip_vision","ipadapter", "pos_embed", "neg_embed",) |
| CATEGORY = "EasyUse/Adapter" |
| FUNCTION = "apply" |
|
|
| def batch(self, embeds, method): |
| if method == 'concat' and len(embeds) == 1: |
| return (embeds[0],) |
|
|
| embeds = [embed for embed in embeds if embed is not None] |
| embeds = torch.cat(embeds, dim=0) |
|
|
| if method == "add": |
| embeds = torch.sum(embeds, dim=0).unsqueeze(0) |
| elif method == "subtract": |
| embeds = embeds[0] - torch.mean(embeds[1:], dim=0) |
| embeds = embeds.unsqueeze(0) |
| elif method == "average": |
| embeds = torch.mean(embeds, dim=0).unsqueeze(0) |
| elif method == "norm average": |
| embeds = torch.mean(embeds / torch.norm(embeds, dim=0, keepdim=True), dim=0).unsqueeze(0) |
| elif method == "max": |
| embeds = torch.max(embeds, dim=0).values.unsqueeze(0) |
| elif method == "min": |
| embeds = torch.min(embeds, dim=0).values.unsqueeze(0) |
|
|
| return embeds |
|
|
| def apply(self, **kwargs): |
| model = kwargs['model'] |
| clip_vision = kwargs['clip_vision'] |
| preset = kwargs['preset'] |
| if 'optional_ipadapter' in kwargs: |
| ipadapter = kwargs['optional_ipadapter'] |
| else: |
| model, ipadapter = self.load_model(model, preset, 0, 'CPU', clip_vision=clip_vision, optional_ipadapter=None, cache_mode='none') |
|
|
| if "IPAdapterEncoder" not in ALL_NODE_CLASS_MAPPINGS: |
| self.error() |
| encoder_cls = ALL_NODE_CLASS_MAPPINGS["IPAdapterEncoder"] |
| pos_embeds = kwargs["pos_embeds"] if "pos_embeds" in kwargs else [] |
| neg_embeds = kwargs["neg_embeds"] if "neg_embeds" in kwargs else [] |
| for i in range(1, kwargs['num_embeds'] + 1): |
| if f"image{i}" not in kwargs: |
| raise Exception(f"image{i} is required") |
| kwargs[f"mask{i}"] = kwargs[f"mask{i}"] if f"mask{i}" in kwargs else None |
| kwargs[f"weight{i}"] = kwargs[f"weight{i}"] if f"weight{i}" in kwargs else 1.0 |
|
|
| pos, neg = encoder_cls().encode(ipadapter, kwargs[f"image{i}"], kwargs[f"weight{i}"], kwargs[f"mask{i}"], clip_vision=clip_vision) |
| pos_embeds.append(pos) |
| neg_embeds.append(neg) |
|
|
| pos_embeds = self.batch(pos_embeds, kwargs['combine_method']) |
| neg_embeds = self.batch(neg_embeds, kwargs['combine_method']) |
|
|
| return (model,clip_vision, ipadapter, pos_embeds, neg_embeds) |
|
|
| class ipadapterApplyEmbeds(ipadapter): |
| def __init__(self): |
| super().__init__() |
| pass |
|
|
| @classmethod |
| def INPUT_TYPES(cls): |
| ipa_cls = cls() |
| weight_types = ipa_cls.weight_types |
| return { |
| "required": { |
| "model": ("MODEL",), |
| "clip_vision": ("CLIP_VISION",), |
| "ipadapter": ("IPADAPTER",), |
| "pos_embed": ("EMBEDS",), |
| "weight": ("FLOAT", {"default": 1.0, "min": -1, "max": 3, "step": 0.05}), |
| "weight_type": (weight_types,), |
| "start_at": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.001}), |
| "end_at": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.001}), |
| "embeds_scaling": (['V only', 'K+V', 'K+V w/ C penalty', 'K+mean(V) w/ C penalty'],), |
| }, |
|
|
| "optional": { |
| "neg_embed": ("EMBEDS",), |
| "attn_mask": ("MASK",), |
| } |
| } |
|
|
| RETURN_TYPES = ("MODEL", "IPADAPTER",) |
| RETURN_NAMES = ("model", "ipadapter", ) |
| CATEGORY = "EasyUse/Adapter" |
| FUNCTION = "apply" |
|
|
| def apply(self, model, ipadapter, clip_vision, pos_embed, weight, weight_type, start_at, end_at, embeds_scaling, attn_mask=None, neg_embed=None,): |
| if "IPAdapterEmbeds" not in ALL_NODE_CLASS_MAPPINGS: |
| self.error() |
|
|
| cls = ALL_NODE_CLASS_MAPPINGS["IPAdapterEmbeds"] |
| model, image = cls().apply_ipadapter(model, ipadapter, pos_embed, weight, weight_type, start_at, end_at, neg_embed=neg_embed, attn_mask=attn_mask, clip_vision=clip_vision, embeds_scaling=embeds_scaling) |
|
|
| return (model, ipadapter) |
|
|
| class ipadapterApplyRegional(ipadapter): |
| def __init__(self): |
| super().__init__() |
| pass |
|
|
| @classmethod |
| def INPUT_TYPES(cls): |
| ipa_cls = cls() |
| weight_types = ipa_cls.weight_types |
| return { |
| "required": { |
| "pipe": ("PIPE_LINE",), |
| "image": ("IMAGE",), |
| "positive": ("STRING", {"default": "", "placeholder": "positive", "multiline": True}), |
| "negative": ("STRING", {"default": "", "placeholder": "negative", "multiline": True}), |
| "image_weight": ("FLOAT", {"default": 1.0, "min": -1.0, "max": 3.0, "step": 0.05}), |
| "prompt_weight": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.05}), |
| "weight_type": (weight_types,), |
| "start_at": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.001}), |
| "end_at": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.001}), |
| }, |
|
|
| "optional": { |
| "mask": ("MASK",), |
| "optional_ipadapter_params": ("IPADAPTER_PARAMS",), |
| }, |
| "hidden": {"prompt": "PROMPT", "my_unique_id": "UNIQUE_ID"} |
| } |
|
|
| RETURN_TYPES = ("PIPE_LINE", "IPADAPTER_PARAMS", "CONDITIONING", "CONDITIONING") |
| RETURN_NAMES = ("pipe", "ipadapter_params", "positive", "negative") |
| CATEGORY = "EasyUse/Adapter" |
| FUNCTION = "apply" |
|
|
| def apply(self, pipe, image, positive, negative, image_weight, prompt_weight, weight_type, start_at, end_at, mask=None, optional_ipadapter_params=None, prompt=None, my_unique_id=None): |
| model = pipe['model'] |
|
|
| if positive == '': |
| positive = pipe['loader_settings']['positive'] |
| if negative == '': |
| negative = pipe['loader_settings']['negative'] |
|
|
| if "clip" not in pipe or not pipe['clip']: |
| if "chatglm3_model" in pipe: |
| from ..modules.kolors.text_encode import chatglm3_adv_text_encode |
| chatglm3_model = pipe['chatglm3_model'] |
| |
| log_node_warn("Positive encoding...") |
| positive_embeddings_final = chatglm3_adv_text_encode(chatglm3_model, positive, False) |
| log_node_warn("Negative encoding...") |
| negative_embeddings_final = chatglm3_adv_text_encode(chatglm3_model, negative, False) |
| else: |
| clip = pipe['clip'] |
| clip_skip = pipe['loader_settings']['clip_skip'] |
| a1111_prompt_style = pipe['loader_settings']['a1111_prompt_style'] |
| pipe_lora_stack = pipe['loader_settings']['lora_stack'] |
| positive_token_normalization = pipe['loader_settings']['positive_token_normalization'] |
| positive_weight_interpretation = pipe['loader_settings']['positive_weight_interpretation'] |
| negative_token_normalization = pipe['loader_settings']['negative_token_normalization'] |
| negative_weight_interpretation = pipe['loader_settings']['negative_weight_interpretation'] |
|
|
| positive_embeddings_final, positive_wildcard_prompt, model, clip = prompt_to_cond('positive', model, clip, clip_skip, pipe_lora_stack, positive, positive_token_normalization, positive_weight_interpretation, a1111_prompt_style, my_unique_id, prompt, easyCache) |
| negative_embeddings_final, negative_wildcard_prompt, model, clip = prompt_to_cond('negative', model, clip, clip_skip, pipe_lora_stack, negative, negative_token_normalization, negative_weight_interpretation, a1111_prompt_style, my_unique_id, prompt, easyCache) |
|
|
| |
| if "IPAdapterRegionalConditioning" not in ALL_NODE_CLASS_MAPPINGS: |
| self.error() |
|
|
| cls = ALL_NODE_CLASS_MAPPINGS["IPAdapterRegionalConditioning"] |
| ipadapter_params, new_positive_embeds, new_negative_embeds = cls().conditioning(image, image_weight, prompt_weight, weight_type, start_at, end_at, mask=mask, positive=positive_embeddings_final, negative=negative_embeddings_final) |
|
|
| if optional_ipadapter_params is not None: |
| positive_embeds = pipe['positive'] + new_positive_embeds |
| negative_embeds = pipe['negative'] + new_negative_embeds |
| _ipadapter_params = { |
| "image": optional_ipadapter_params["image"] + ipadapter_params["image"], |
| "attn_mask": optional_ipadapter_params["attn_mask"] + ipadapter_params["attn_mask"], |
| "weight": optional_ipadapter_params["weight"] + ipadapter_params["weight"], |
| "weight_type": optional_ipadapter_params["weight_type"] + ipadapter_params["weight_type"], |
| "start_at": optional_ipadapter_params["start_at"] + ipadapter_params["start_at"], |
| "end_at": optional_ipadapter_params["end_at"] + ipadapter_params["end_at"], |
| } |
| ipadapter_params = _ipadapter_params |
| del _ipadapter_params |
| else: |
| positive_embeds = new_positive_embeds |
| negative_embeds = new_negative_embeds |
|
|
| new_pipe = { |
| **pipe, |
| "positive": positive_embeds, |
| "negative": negative_embeds, |
| } |
|
|
| del pipe |
|
|
| return (new_pipe, ipadapter_params, positive_embeds, negative_embeds) |
|
|
| class ipadapterApplyFromParams(ipadapter): |
| def __init__(self): |
| super().__init__() |
| pass |
|
|
| @classmethod |
| def INPUT_TYPES(cls): |
| ipa_cls = cls() |
| normal_presets = ipa_cls.normal_presets |
| return { |
| "required": { |
| "model": ("MODEL",), |
| "preset": (normal_presets,), |
| "ipadapter_params": ("IPADAPTER_PARAMS",), |
| "combine_embeds": (["concat", "add", "subtract", "average", "norm average", "max", "min"],), |
| "embeds_scaling": (['V only', 'K+V', 'K+V w/ C penalty', 'K+mean(V) w/ C penalty'],), |
| "cache_mode": (["insightface only", "clip_vision only", "ipadapter only", "all", "none"], |
| {"default": "insightface only"}), |
| }, |
|
|
| "optional": { |
| "optional_ipadapter": ("IPADAPTER",), |
| "image_negative": ("IMAGE",), |
| } |
| } |
|
|
| RETURN_TYPES = ("MODEL", "IPADAPTER",) |
| RETURN_NAMES = ("model", "ipadapter", ) |
| CATEGORY = "EasyUse/Adapter" |
| FUNCTION = "apply" |
|
|
| def apply(self, model, preset, ipadapter_params, combine_embeds, embeds_scaling, cache_mode, optional_ipadapter=None, image_negative=None,): |
| model, ipadapter = self.load_model(model, preset, 0, 'CPU', clip_vision=None, optional_ipadapter=optional_ipadapter, cache_mode=cache_mode) |
| if "IPAdapterFromParams" not in ALL_NODE_CLASS_MAPPINGS: |
| self.error() |
| cls = ALL_NODE_CLASS_MAPPINGS["IPAdapterFromParams"] |
| model, image = cls().apply_ipadapter(model, ipadapter, clip_vision=None, combine_embeds=combine_embeds, embeds_scaling=embeds_scaling, image_negative=image_negative, ipadapter_params=ipadapter_params) |
|
|
| return (model, ipadapter) |
|
|
| |
| class instantID: |
|
|
| def error(self): |
| raise Exception(f"[ERROR] To use instantIDApply, you need to install 'ComfyUI_InstantID'") |
|
|
| def run(self, pipe, image, instantid_file, insightface, control_net_name, cn_strength, cn_soft_weights, weight, start_at, end_at, noise, image_kps=None, mask=None, control_net=None, positive=None, negative=None, prompt=None, extra_pnginfo=None, my_unique_id=None): |
| instantid_model, insightface_model, face_embeds = None, None, None |
| model = pipe['model'] |
| |
| cache_key = 'instantID' |
| if cache_key in backend_cache.cache: |
| log_node_info("easy instantIDApply","Using InstantIDModel Cached") |
| _, instantid_model = backend_cache.cache[cache_key][1] |
| if "InstantIDModelLoader" in ALL_NODE_CLASS_MAPPINGS: |
| load_instant_cls = ALL_NODE_CLASS_MAPPINGS["InstantIDModelLoader"] |
| instantid_model, = load_instant_cls().load_model(instantid_file) |
| backend_cache.update_cache(cache_key, 'instantid', (False, instantid_model)) |
| else: |
| self.error() |
| icache_key = 'insightface-' + insightface |
| if icache_key in backend_cache.cache: |
| log_node_info("easy instantIDApply", f"Using InsightFaceModel {insightface} Cached") |
| _, insightface_model = backend_cache.cache[icache_key][1] |
| elif "InstantIDFaceAnalysis" in ALL_NODE_CLASS_MAPPINGS: |
| load_insightface_cls = ALL_NODE_CLASS_MAPPINGS["InstantIDFaceAnalysis"] |
| insightface_model, = load_insightface_cls().load_insight_face(insightface) |
| backend_cache.update_cache(icache_key, 'insightface', (False, insightface_model)) |
| else: |
| self.error() |
|
|
| |
| if "ApplyInstantID" in ALL_NODE_CLASS_MAPPINGS: |
| instantid_apply = ALL_NODE_CLASS_MAPPINGS['ApplyInstantID'] |
| if control_net is None: |
| control_net = easyCache.load_controlnet(control_net_name, cn_soft_weights) |
| model, positive, negative = instantid_apply().apply_instantid(instantid_model, insightface_model, control_net, image, model, positive, negative, start_at, end_at, weight=weight, ip_weight=None, cn_strength=cn_strength, noise=noise, image_kps=image_kps, mask=mask) |
| else: |
| self.error() |
|
|
| new_pipe = { |
| "model": model, |
| "positive": positive, |
| "negative": negative, |
| "vae": pipe['vae'], |
| "clip": pipe['clip'], |
|
|
| "samples": pipe["samples"], |
| "images": pipe["images"], |
| "seed": 0, |
|
|
| "loader_settings": pipe["loader_settings"] |
| } |
|
|
| del pipe |
|
|
| return (new_pipe, model, positive, negative) |
|
|
| class instantIDApply(instantID): |
|
|
| def __init__(self): |
| super().__init__() |
| pass |
|
|
| @classmethod |
| def INPUT_TYPES(cls): |
| return { |
| "required":{ |
| "pipe": ("PIPE_LINE",), |
| "image": ("IMAGE",), |
| "instantid_file": (folder_paths.get_filename_list("instantid"),), |
| "insightface": (["CPU", "CUDA", "ROCM"],), |
| "control_net_name": (folder_paths.get_filename_list("controlnet"),), |
| "cn_strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.01}), |
| "cn_soft_weights": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.001},), |
| "weight": ("FLOAT", {"default": .8, "min": 0.0, "max": 5.0, "step": 0.01, }), |
| "start_at": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.001, }), |
| "end_at": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.001, }), |
| "noise": ("FLOAT", {"default": 0.35, "min": 0.0, "max": 1.0, "step": 0.05, }), |
| }, |
| "optional": { |
| "image_kps": ("IMAGE",), |
| "mask": ("MASK",), |
| "control_net": ("CONTROL_NET",), |
| }, |
| "hidden": { |
| "prompt": "PROMPT", "extra_pnginfo": "EXTRA_PNGINFO", "my_unique_id": "UNIQUE_ID" |
| }, |
| } |
|
|
| RETURN_TYPES = ("PIPE_LINE", "MODEL", "CONDITIONING", "CONDITIONING") |
| RETURN_NAMES = ("pipe", "model", "positive", "negative") |
|
|
| FUNCTION = "apply" |
| CATEGORY = "EasyUse/Adapter" |
|
|
|
|
| def apply(self, pipe, image, instantid_file, insightface, control_net_name, cn_strength, cn_soft_weights, weight, start_at, end_at, noise, image_kps=None, mask=None, control_net=None, prompt=None, extra_pnginfo=None, my_unique_id=None): |
| positive = pipe['positive'] |
| negative = pipe['negative'] |
| return self.run(pipe, image, instantid_file, insightface, control_net_name, cn_strength, cn_soft_weights, weight, start_at, end_at, noise, image_kps, mask, control_net, positive, negative, prompt, extra_pnginfo, my_unique_id) |
|
|
| |
| class instantIDApplyAdvanced(instantID): |
|
|
| def __init__(self): |
| super().__init__() |
| pass |
|
|
| @classmethod |
| def INPUT_TYPES(cls): |
| return { |
| "required":{ |
| "pipe": ("PIPE_LINE",), |
| "image": ("IMAGE",), |
| "instantid_file": (folder_paths.get_filename_list("instantid"),), |
| "insightface": (["CPU", "CUDA", "ROCM"],), |
| "control_net_name": (folder_paths.get_filename_list("controlnet"),), |
| "cn_strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.01}), |
| "cn_soft_weights": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.001},), |
| "weight": ("FLOAT", {"default": .8, "min": 0.0, "max": 5.0, "step": 0.01, }), |
| "start_at": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.001, }), |
| "end_at": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.001, }), |
| "noise": ("FLOAT", {"default": 0.35, "min": 0.0, "max": 1.0, "step": 0.05, }), |
| }, |
| "optional": { |
| "image_kps": ("IMAGE",), |
| "mask": ("MASK",), |
| "control_net": ("CONTROL_NET",), |
| "positive": ("CONDITIONING",), |
| "negative": ("CONDITIONING",), |
| }, |
| "hidden": { |
| "prompt": "PROMPT", "extra_pnginfo": "EXTRA_PNGINFO", "my_unique_id": "UNIQUE_ID" |
| }, |
| } |
|
|
| RETURN_TYPES = ("PIPE_LINE", "MODEL", "CONDITIONING", "CONDITIONING") |
| RETURN_NAMES = ("pipe", "model", "positive", "negative") |
|
|
| FUNCTION = "apply_advanced" |
| CATEGORY = "EasyUse/Adapter" |
|
|
| def apply_advanced(self, pipe, image, instantid_file, insightface, control_net_name, cn_strength, cn_soft_weights, weight, start_at, end_at, noise, image_kps=None, mask=None, control_net=None, positive=None, negative=None, prompt=None, extra_pnginfo=None, my_unique_id=None): |
|
|
| positive = positive if positive is not None else pipe['positive'] |
| negative = negative if negative is not None else pipe['negative'] |
|
|
| return self.run(pipe, image, instantid_file, insightface, control_net_name, cn_strength, cn_soft_weights, weight, start_at, end_at, noise, image_kps, mask, control_net, positive, negative, prompt, extra_pnginfo, my_unique_id) |
|
|
| class applyPulID: |
| @classmethod |
| def INPUT_TYPES(s): |
| return { |
| "required": { |
| "model": ("MODEL",), |
| "pulid_file": (folder_paths.get_filename_list("pulid"),), |
| "insightface": (["CPU", "CUDA", "ROCM"],), |
| "image": ("IMAGE",), |
| "method": (["fidelity", "style", "neutral"],), |
| "weight": ("FLOAT", {"default": 1.0, "min": -1.0, "max": 5.0, "step": 0.05}), |
| "start_at": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.001}), |
| "end_at": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.001}), |
| }, |
| "optional": { |
| "attn_mask": ("MASK",), |
| }, |
| } |
|
|
| RETURN_TYPES = ("MODEL",) |
| RETURN_NAMES = ("model",) |
|
|
| FUNCTION = "run" |
| CATEGORY = "EasyUse/Adapter" |
|
|
| def error(self): |
| raise Exception(f"[ERROR] To use pulIDApply, you need to install 'ComfyUI_PulID'") |
|
|
| def run(self, model, image, pulid_file, insightface, weight, start_at, end_at, method=None, noise=0.0, fidelity=None, projection=None, attn_mask=None, prompt=None, extra_pnginfo=None, my_unique_id=None): |
| pulid_model, insightface_model, eva_clip = None, None, None |
| |
| cache_key = 'pulID' |
| if cache_key in backend_cache.cache: |
| log_node_info("easy pulIDApply","Using InstantIDModel Cached") |
| _, pulid_model = backend_cache.cache[cache_key][1] |
| if "PulidModelLoader" in ALL_NODE_CLASS_MAPPINGS: |
| load_pulid_cls = ALL_NODE_CLASS_MAPPINGS["PulidModelLoader"] |
| pulid_model, = load_pulid_cls().load_model(pulid_file) |
| backend_cache.update_cache(cache_key, 'pulid', (False, pulid_model)) |
| else: |
| self.error() |
| |
| icache_key = 'insightface-' + insightface |
| if icache_key in backend_cache.cache: |
| log_node_info("easy pulIDApply", f"Using InsightFaceModel {insightface} Cached") |
| _, insightface_model = backend_cache.cache[icache_key][1] |
| elif "PulidInsightFaceLoader" in ALL_NODE_CLASS_MAPPINGS: |
| load_insightface_cls = ALL_NODE_CLASS_MAPPINGS["PulidInsightFaceLoader"] |
| insightface_model, = load_insightface_cls().load_insightface(insightface) |
| backend_cache.update_cache(icache_key, 'insightface', (False, insightface_model)) |
| else: |
| self.error() |
| |
| ecache_key = 'eva_clip' |
| if ecache_key in backend_cache.cache: |
| log_node_info("easy pulIDApply", f"Using EVAClipModel Cached") |
| _, eva_clip = backend_cache.cache[ecache_key][1] |
| elif "PulidEvaClipLoader" in ALL_NODE_CLASS_MAPPINGS: |
| load_evaclip_cls = ALL_NODE_CLASS_MAPPINGS["PulidEvaClipLoader"] |
| eva_clip, = load_evaclip_cls().load_eva_clip() |
| backend_cache.update_cache(ecache_key, 'eva_clip', (False, eva_clip)) |
| else: |
| self.error() |
|
|
| |
| if method is not None: |
| if "ApplyPulid" in ALL_NODE_CLASS_MAPPINGS: |
| cls = ALL_NODE_CLASS_MAPPINGS['ApplyPulid'] |
| model, = cls().apply_pulid(model, pulid=pulid_model, eva_clip=eva_clip, face_analysis=insightface_model, image=image, weight=weight, method=method, start_at=start_at, end_at=end_at, attn_mask=attn_mask) |
| else: |
| self.error() |
| else: |
| if "ApplyPulidAdvanced" in ALL_NODE_CLASS_MAPPINGS: |
| cls = ALL_NODE_CLASS_MAPPINGS['ApplyPulidAdvanced'] |
| model, = cls().apply_pulid(model, pulid=pulid_model, eva_clip=eva_clip, face_analysis=insightface_model, image=image, weight=weight, projection=projection, fidelity=fidelity, noise=noise, start_at=start_at, end_at=end_at, attn_mask=attn_mask) |
| else: |
| self.error() |
|
|
| return (model,) |
|
|
| class applyPulIDADV(applyPulID): |
|
|
| @classmethod |
| def INPUT_TYPES(s): |
| return { |
| "required": { |
| "model": ("MODEL",), |
| "pulid_file": (folder_paths.get_filename_list("pulid"),), |
| "insightface": (["CPU", "CUDA", "ROCM"],), |
| "image": ("IMAGE",), |
| "weight": ("FLOAT", {"default": 1.0, "min": -1.0, "max": 5.0, "step": 0.05}), |
| "projection": (["ortho_v2", "ortho", "none"], {"default":"ortho_v2"}), |
| "fidelity": ("INT", {"default": 8, "min": 0, "max": 32, "step": 1}), |
| "noise": ("FLOAT", {"default": 0.0, "min": -1.0, "max": 1.0, "step": 0.1}), |
| "start_at": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.001}), |
| "end_at": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.001}), |
| }, |
| "optional": { |
| "attn_mask": ("MASK",), |
| }, |
| } |
|
|
|
|
|
|
| NODE_CLASS_MAPPINGS = { |
| "easy loraPromptApply": applyLoraPrompt, |
| "easy loraStackApply": applyLoraStack, |
| "easy controlnetStackApply": applyControlnetStack, |
| "easy ipadapterApply": ipadapterApply, |
| "easy ipadapterApplyADV": ipadapterApplyAdvanced, |
| "easy ipadapterApplyFaceIDKolors": ipadapterApplyFaceIDKolors, |
| "easy ipadapterApplyEncoder": ipadapterApplyEncoder, |
| "easy ipadapterApplyEmbeds": ipadapterApplyEmbeds, |
| "easy ipadapterApplyRegional": ipadapterApplyRegional, |
| "easy ipadapterApplyFromParams": ipadapterApplyFromParams, |
| "easy ipadapterStyleComposition": ipadapterStyleComposition, |
| "easy instantIDApply": instantIDApply, |
| "easy instantIDApplyADV": instantIDApplyAdvanced, |
| "easy pulIDApply": applyPulID, |
| "easy pulIDApplyADV": applyPulIDADV, |
| "easy styleAlignedBatchAlign": styleAlignedBatchAlign, |
| "easy icLightApply": icLightApply |
| } |
|
|
| NODE_DISPLAY_NAME_MAPPINGS = { |
| "easy loraPromptApply": "Easy Apply LoraPrompt", |
| "easy loraStackApply": "Easy Apply LoraStack", |
| "easy controlnetStackApply": "Easy Apply CnetStack", |
| "easy ipadapterApply": "Easy Apply IPAdapter", |
| "easy ipadapterApplyADV": "Easy Apply IPAdapter (Advanced)", |
| "easy ipadapterApplyFaceIDKolors": "Easy Apply IPAdapter (FaceID Kolors)", |
| "easy ipadapterStyleComposition": "Easy Apply IPAdapter (StyleComposition)", |
| "easy ipadapterApplyEncoder": "Easy Apply IPAdapter (Encoder)", |
| "easy ipadapterApplyRegional": "Easy Apply IPAdapter (Regional)", |
| "easy ipadapterApplyEmbeds": "Easy Apply IPAdapter (Embeds)", |
| "easy ipadapterApplyFromParams": "Easy Apply IPAdapter (From Params)", |
| "easy instantIDApply": "Easy Apply InstantID", |
| "easy instantIDApplyADV": "Easy Apply InstantID (Advanced)", |
| "easy pulIDApply": "Easy Apply PuLID", |
| "easy pulIDApplyADV": "Easy Apply PuLID (Advanced)", |
| "easy styleAlignedBatchAlign": "Easy Apply StyleAlign", |
| "easy icLightApply": "Easy Apply ICLight" |
| } |