From 83ca2f8ab68b04bcdc59e14545a633ce6a75c5ab Mon Sep 17 00:00:00 2001 From: yolain Date: Fri, 29 Mar 2024 18:00:40 +0800 Subject: [PATCH] add:easy ipadapterApplyEncoder and ipadapterApplyEmbeds --- README.en.md | 2 + README.md | 2 + py/easyNodes.py | 151 ++++++++++++++++++++++++++++-- web/js/easy/easyDynamicWidgets.js | 27 +++++- 4 files changed, 171 insertions(+), 11 deletions(-) diff --git a/README.en.md b/README.en.md index 07855dc..247451e 100644 --- a/README.en.md +++ b/README.en.md @@ -37,6 +37,8 @@ PS: Please update [ComfyUI_IPAdapter_plus](https://github.com/cubiq/ComfyUI_IPAd
- Added `easy ipadapterApply` - Added `easy ipadapterApplyADV` +- Added `easy ipadapterApplyEncoder` +- Added `easy ipadapterApplyEmbeds` - Added `easy preMaskDetailerFix` - Fixed `easy stylesSelector` is change the prompt when not select the style diff --git a/README.md b/README.md index 852b59e..79f3be3 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,8 @@ PS: 请更新至最新版v2的 [ComfyUI_IPAdapter_plus](https://github.com/cubiq
- 增加 `easy ipadapterApply` - 增加 `easy ipadapterApplyADV` +- 增加 `easy ipadapterApplyEncoder` +- 增加 `easy ipadapterApplyEmbeds` - 增加 `easy preMaskDetailerFix` - 修复 `easy stylesSelector` 当未选择样式时,原有提示词发生了变化 diff --git a/py/easyNodes.py b/py/easyNodes.py index a060964..0685ce5 100644 --- a/py/easyNodes.py +++ b/py/easyNodes.py @@ -1598,7 +1598,7 @@ def insightface_loader(provider): class ipadapter: def __init__(self): - self.normol_presets = [ + self.normal_presets = [ 'LIGHT - SD1.5 only (low strength)', 'STANDARD (medium strength)', 'VIT-G (medium strength)', @@ -1612,7 +1612,9 @@ class ipadapter: 'FACEID PLUS V2', 'FACEID PORTRAIT (style transfer)' ] - self.presets = self.normol_presets + self.faceid_presets + self.weight_types = ["linear", "ease in", "ease out", 'ease in-out', 'reverse in-out', 'weak input', 'weak output', + 'weak middle', 'strong middle', 'style transfer (SDXL)'] + self.presets = self.normal_presets + self.faceid_presets def error(self): @@ -1831,7 +1833,7 @@ class ipadapterApply(ipadapter): 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): - tiles, masks = [None], [None] + tiles, 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 use_tiled and preset not in self.faceid_presets: if "IPAdapterTiled" not in ALL_NODE_CLASS_MAPPINGS: @@ -1859,9 +1861,9 @@ class ipadapterApplyAdvanced(ipadapter): @classmethod def INPUT_TYPES(cls): - presets = cls().presets - WEIGHT_TYPES = ["linear", "ease in", "ease out", 'ease in-out', 'reverse in-out', 'weak input', 'weak output', - 'weak middle', 'strong middle', 'style transfer (SDXL)'] + ipa_cls = cls() + presets = ipa_cls.presets + weight_types = ipa_cls.weight_types return { "required": { "model": ("MODEL",), @@ -1871,7 +1873,7 @@ class ipadapterApplyAdvanced(ipadapter): "provider": (["CPU", "CUDA", "ROCM", "DirectML", "OpenVINO", "CoreML"],), "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,), + "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}), @@ -1896,7 +1898,7 @@ class ipadapterApplyAdvanced(ipadapter): 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, image_negative=None, clip_vision=None, attn_mask=None, optional_ipadapter=None): - tiles, masks = [None], [None] + tiles, 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 use_tiled: if use_batch: @@ -1920,6 +1922,135 @@ class ipadapterApplyAdvanced(ipadapter): model, = cls().apply_ipadapter(model, ipadapter, image, weight, weight_type, start_at, end_at, combine_embeds=combine_embeds, weight_faceidv2=weight_faceidv2, image_negative=image_negative, clip_vision=clip_vision, attn_mask=attn_mask, insightface=None, embeds_scaling=embeds_scaling) return (model, tiles, masks, 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 = 3 + inputs = { + "required": { + "model": ("MODEL",), + "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", "IPADAPTER", "EMBEDS", "EMBEDS", ) + RETURN_NAMES = ("model", "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'] + preset = kwargs['preset'] + if 'optional_ipadapter' in kwargs: + ipadapter = kwargs['optional_ipadapter'] + else: + model, ipadapter = self.load_model(model, preset, 0, 'CPU', clip_vision=None, 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=None) + 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, 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",), + "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, 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, = cls().apply_ipadapter(model, ipadapter, pos_embed, weight, weight_type, start_at, end_at, neg_embed=neg_embed, attn_mask=attn_mask, clip_vision=None, embeds_scaling=embeds_scaling) + + return (model, ipadapter) + #Apply InstantID class instantID: @@ -5872,6 +6003,8 @@ NODE_CLASS_MAPPINGS = { # Adapter 适配器 "easy ipadapterApply": ipadapterApply, "easy ipadapterApplyADV": ipadapterApplyAdvanced, + "easy ipadapterApplyEncoder": ipadapterApplyEncoder, + "easy ipadapterApplyEmbeds": ipadapterApplyEmbeds, "easy instantIDApply": instantIDApply, "easy instantIDApplyADV": instantIDApplyAdvanced, # Inpaint 内补 @@ -5961,6 +6094,8 @@ NODE_DISPLAY_NAME_MAPPINGS = { # Adapter 适配器 "easy ipadapterApply": "Easy Apply IPAdapter", "easy ipadapterApplyADV": "Easy Apply IPAdapter (Advanced)", + "easy ipadapterApplyEncoder": "Easy Apply IPAdapter (Encoder)", + "easy ipadapterApplyEmbeds": "Easy Apply IPAdapter (Embeds)", "easy instantIDApply": "Easy Apply InstantID", "easy instantIDApplyADV": "Easy Apply InstantID (Advanced)", # Inpaint 内补 diff --git a/web/js/easy/easyDynamicWidgets.js b/web/js/easy/easyDynamicWidgets.js index ea5683e..58cbe2e 100644 --- a/web/js/easy/easyDynamicWidgets.js +++ b/web/js/easy/easyDynamicWidgets.js @@ -28,6 +28,11 @@ function toggleWidget(node, widget, show = false, suffix = "") { node.setSize([node.size[0], height]); } +function toggleInput(node, name, show = false) { + if(!show){ + } +} + function widgetLogic(node, widget) { if (widget.name === 'lora_name') { if (widget.value === "None") { @@ -227,8 +232,8 @@ function widgetLogic(node, widget) { toggleWidget(node, findWidgetByName(node, 'provider')) toggleWidget(node, findWidgetByName(node, 'weight_faceidv2')) toggleWidget(node, findWidgetByName(node, 'use_tiled'), true) - let use_tiled = findWidgetByName(node, 'use_tiled').value - if(use_tiled){ + let use_tiled = findWidgetByName(node, 'use_tiled') + if(use_tiled && use_tiled.value){ toggleWidget(node, findWidgetByName(node, 'sharpening'), true) }else { toggleWidget(node, findWidgetByName(node, 'sharpening')) @@ -261,6 +266,21 @@ function widgetLogic(node, widget) { toggleWidget(node, findWidgetByName(node, 'sharpening')) updateNodeHeight(node) } + + if (widget.name === 'num_embeds') { + let number_to_show = widget.value + 1 + for (let i = 0; i < number_to_show; i++) { + toggleInput(node, 'image'+i, true) + toggleInput(node, 'mask'+i, true) + toggleWidget(node, findWidgetByName(node, 'weight'+i), true) + } + for (let i = number_to_show; i < 6; i++) { + toggleInput(node, 'image'+i) + toggleInput(node, 'mask'+i) + toggleWidget(node, findWidgetByName(node, 'weight'+i)) + } + updateNodeHeight(node) + } } function widgetLogic2(node, widget) { @@ -541,6 +561,7 @@ app.registerExtension({ case 'easy pipeEdit': case 'easy ipadapterApply': case 'easy ipadapterApplyADV': + case 'easy ipadapterApplyEncoder': getSetters(node) break case "easy wildcards": @@ -977,7 +998,7 @@ const getSetWidgets = ['rescale_after_model', 'rescale', 'refiner_lora1_name', 'refiner_lora2_name', 'upscale_method', 'image_output', 'add_noise', 'info', 'sampler_name', 'ckpt_B_name', 'ckpt_C_name', 'save_model', 'refiner_ckpt_name', - 'num_loras', 'mode', 'toggle', 'resolution', 'target_parameter', 'input_count', 'replace_count', 'downscale_mode', 'range_mode','text_combine_mode', 'input_mode','lora_count','ckpt_count', 'conditioning_mode', 'preset', 'use_tiled', 'use_batch'] + 'num_loras', 'mode', 'toggle', 'resolution', 'target_parameter', 'input_count', 'replace_count', 'downscale_mode', 'range_mode','text_combine_mode', 'input_mode','lora_count','ckpt_count', 'conditioning_mode', 'preset', 'use_tiled', 'use_batch', 'num_embeds'] function getSetters(node) { if (node.widgets)