add:easy ipadapterApply and easy ipadapterApplyADV

This commit is contained in:
yolain
2024-03-28 21:33:47 +08:00
parent 4c25580295
commit aadefaf40b
9 changed files with 588 additions and 89 deletions
+7
View File
@@ -33,6 +33,13 @@
**v1.1.2 (2024/3/25)**
PS: Please update [ComfyUI_IPAdapter_plus](https://github.com/cubiq/ComfyUI_IPAdapter_plus) to v2, and moved v1 models to **ComfyUI\models\ipadapter** (Otherwise, the latest model is automatically downloaded from Huggingface)
<br>
- Added `easy ipadapterApply`
- Added `easy ipadapterApplyADV`
(4c25580)
- `easy kSamplerInpainting` add *additional* widget,you can choose 'Differential Diffusion' or 'Only InpaintModelConditioning'
- Fixed `easy pipeEdit` error when add lora to prompt
- Fixed layerDiffuse xyplot bug
+9 -1
View File
@@ -27,6 +27,7 @@
- 简化 Stable Cascade [示例参考](https://github.com/yolain/ComfyUI-Easy-Use?tab=readme-ov-file#StableCascade)
- 简化 Layer Diffuse [示例参考](https://github.com/yolain/ComfyUI-Easy-Use?tab=readme-ov-file#LayerDiffusion), 首次使用您可能需要运行 `pip install -r requirements.txt` 安装所需依赖
- 简化 InstantID [示例参考](https://github.com/yolain/ComfyUI-Easy-Use?tab=readme-ov-file#InstantID), 需先保证自定义节点包中安装了 [ComfyUI_InstantID](https://github.com/cubiq/ComfyUI_InstantID)
- 简化 IPAdapter, 需先保证自定义节点包中安装最新版v2的 [ComfyUI_IPAdapter_plus](https://github.com/cubiq/ComfyUI_IPAdapter_plus)
- 扩展 XYplot 的可用性
- 整合了Fooocus Inpaint功能
- 整合了常用的逻辑计算、转换类型、展示所有类型等
@@ -36,11 +37,18 @@
**v1.1.2 (2024/3/25)**
PS: 请更新至最新版v2的 [ComfyUI_IPAdapter_plus](https://github.com/cubiq/ComfyUI_IPAdapter_plus), 并移动v1版本模型文件至 ComfyUI\models\ipadapter (否则会自动从huggingface下载最新模型)
<br>
- 增加 `easy ipadapterApply`
- 增加 `easy ipadapterApplyADV`
(4c25580)
- `easy kSamplerInpainting` 增加 *additional* 属性,可设置成 Differential Diffusion 或 Only InpaintModelConditioning
- 修复 `easy pipeEdit` 提示词输入lora时报错
- 修复 layerDiffuse xyplot相关bug
**v1.1.1 (2024/3/21)**
**v1.1.1 (5c8af8f)**
- 修复首次添加含seed的节点且当前模式为control_before_generate时,seed为0的问题
- `easy preSamplingAdvanced` 增加 **return_with_leftover_noise**
+91
View File
@@ -141,4 +141,95 @@ REMBG_MODELS = {
"RMBG-1.4": {
"model_url": "https://huggingface.co/briaai/RMBG-1.4/resolve/main/model.pth"
}
}
#ipadapter
IPADAPTER_DIR = os.path.join(folder_paths.models_dir, "ipadapter")
IPADAPTER_MODELS = {
"LIGHT - SD1.5 only (low strength)": {
"sd15": {
"model_url": "https://huggingface.co/h94/IP-Adapter/resolve/main/models/ip-adapter_sd15_light_v11.bin"
},
"sdxl": {
"model_url": ""
}
},
"STANDARD (medium strength)": {
"sd15": {
"model_url": "https://huggingface.co/h94/IP-Adapter/resolve/main/models/ip-adapter_sd15.safetensors"
},
"sdxl": {
"model_url": "https://huggingface.co/h94/IP-Adapter/resolve/main/sdxl_models/ip-adapter_sdxl.safetensors"
}
},
"VIT-G (medium strength)": {
"sd15": {
"model_url": "https://huggingface.co/h94/IP-Adapter/resolve/main/models/ip-adapter_sd15_vit-G.safetensors"
},
"sdxl": {
"model_url": "https://huggingface.co/h94/IP-Adapter/resolve/main/sdxl_models/ip-adapter_sdxl_vit-h.safetensors"
}
},
"PLUS (high strength)": {
"sd15": {
"model_url": "https://huggingface.co/h94/IP-Adapter/resolve/main/models/ip-adapter-plus_sd15.safetensors"
},
"sdxl": {
"model_url": "https://huggingface.co/h94/IP-Adapter/resolve/main/sdxl_models/ip-adapter-plus_sdxl_vit-h.safetensors"
}
},
"PLUS FACE (portraits)": {
"sd15": {
"model_url": "https://huggingface.co/h94/IP-Adapter/resolve/main/models/ip-adapter-plus-face_sd15.safetensors"
},
"sdxl": {
"model_url": "https://huggingface.co/h94/IP-Adapter/resolve/main/sdxl_models/ip-adapter-plus-face_sdxl_vit-h.safetensors"
}
},
"FULL FACE - SD1.5 only (portraits stronger)": {
"sd15": {
"model_url": "https://huggingface.co/h94/IP-Adapter/resolve/main/models/ip-adapter-full-face_sd15.safetensors"
},
"sdxl": {
"model_url": ""
}
},
"FACEID": {
"sd15": {
"model_url": "https://huggingface.co/h94/IP-Adapter-FaceID/resolve/main/ip-adapter-faceid_sd15.bin",
"lora_url": "https://huggingface.co/h94/IP-Adapter-FaceID/resolve/main/ip-adapter-faceid_sd15_lora.safetensors"
},
"sdxl": {
"model_url": "https://huggingface.co/h94/IP-Adapter-FaceID/resolve/main/ip-adapter-faceid_sdxl.bin",
"lora_url": "https://huggingface.co/h94/IP-Adapter-FaceID/resolve/main/ip-adapter-faceid_sdxl_lora.safetensors"
}
},
"FACEID PLUS - SD1.5 only": {
"sd15": {
"model_url": "https://huggingface.co/h94/IP-Adapter-FaceID/resolve/main/ip-adapter-faceid-plus_sd15.bin",
"lora_url": "https://huggingface.co/h94/IP-Adapter-FaceID/resolve/main/ip-adapter-faceid-plus_sd15_lora.safetensors"
},
"sdxl": {
"model_url": "",
"lora_url": ""
}
},
"FACEID PLUS V2": {
"sd15": {
"model_url": "https://huggingface.co/h94/IP-Adapter-FaceID/resolve/main/ip-adapter-faceid-plusv2_sd15.bin",
"lora_url": "https://huggingface.co/h94/IP-Adapter-FaceID/resolve/main/ip-adapter-faceid-plusv2_sd15_lora.safetensors"
},
"sdxl": {
"model_url": "https://huggingface.co/h94/IP-Adapter-FaceID/resolve/main/ip-adapter-faceid-plusv2_sdxl.bin",
"lora_url": "https://huggingface.co/h94/IP-Adapter-FaceID/resolve/main/ip-adapter-faceid-plusv2_sdxl_lora.safetensors"
}
},
"FACEID PORTRAIT (style transfer)": {
"sd15": {
"model_url": "https://huggingface.co/h94/IP-Adapter-FaceID/resolve/main/ip-adapter-faceid-portrait-v11_sd15.bin",
},
"sdxl": {
"model_url": "https://huggingface.co/h94/IP-Adapter-FaceID/resolve/main/ip-adapter-faceid-portrait_sdxl.bin",
}
}
}
+368 -21
View File
@@ -6,18 +6,19 @@ from comfy.sd import CLIP, VAE
from comfy.model_patcher import ModelPatcher
from comfy_extras.chainner_models import model_loading
from comfy_extras.nodes_mask import LatentCompositeMasked
from comfy.clip_vision import load as load_clip_vision
from urllib.request import urlopen
from PIL import Image
from server import PromptServer
from nodes import MAX_RESOLUTION, LatentFromBatch, RepeatLatentBatch, NODE_CLASS_MAPPINGS as ALL_NODE_CLASS_MAPPINGS, ConditioningSetMask, ConditioningConcat, CLIPTextEncode, VAEEncodeForInpaint, InpaintModelConditioning
from .config import MAX_SEED_NUM, BASE_RESOLUTIONS, RESOURCES_DIR, INPAINT_DIR, FOOOCUS_STYLES_DIR, FOOOCUS_INPAINT_HEAD, FOOOCUS_INPAINT_PATCH
from .config import MAX_SEED_NUM, BASE_RESOLUTIONS, RESOURCES_DIR, INPAINT_DIR, FOOOCUS_STYLES_DIR, FOOOCUS_INPAINT_HEAD, FOOOCUS_INPAINT_PATCH, IPADAPTER_DIR, IPADAPTER_MODELS
from .log import log_node_info, log_node_error, log_node_warn
from .wildcards import process_with_loras, get_wildcard_list, process
from .adv_encode import advanced_encode
from .layer_diffuse.func import LayerDiffuse, LayerMethod
from .libs.utils import find_wildcards_seed, is_linked_styles_selector, easySave, get_local_filepath, add_folder_path_and_extensions, get_sd_version
from .libs.utils import find_wildcards_seed, is_linked_styles_selector, easySave, get_local_filepath, add_folder_path_and_extensions
from .libs.loader import easyLoader
from .libs.sampler import easySampler
from .libs.xyplot import easyXYPlot
@@ -40,6 +41,7 @@ add_folder_path_and_extensions("onnx", [os.path.join(model_path, "onnx")], {'.on
add_folder_path_and_extensions("instantid", [os.path.join(model_path, "instantid")], folder_paths.supported_pt_extensions)
add_folder_path_and_extensions("layer_model", [os.path.join(model_path, "layer_model")], folder_paths.supported_pt_extensions)
add_folder_path_and_extensions("rembg", [os.path.join(model_path, "rembg")], folder_paths.supported_pt_extensions)
add_folder_path_and_extensions("ipadapter", [os.path.join(model_path, "ipadapter")], folder_paths.supported_pt_extensions)
# ---------------------------------------------------------------提示词 开始----------------------------------------------------------------------#
@@ -1543,9 +1545,9 @@ class LLLiteLoader:
return (model_lllite,)
#---------------------------------------------------------------测试 开始----------------------------------------------------------------------#
#---------------------------------------------------------------Inpaint 开始----------------------------------------------------------------------#
# FooocusInpaint (Testing)
# FooocusInpaint
from .fooocus import InpaintHead, InpaintWorker
inpaint_head_model = None
class fooocusInpaintLoader:
@@ -1560,7 +1562,7 @@ class fooocusInpaintLoader:
RETURN_TYPES = ("INPAINT_PATCH",)
RETURN_NAMES = ("patch",)
CATEGORY = "EasyUse/__for_testing"
CATEGORY = "EasyUse/Inpaint"
FUNCTION = "apply"
def apply(self, head, patch):
@@ -1577,6 +1579,345 @@ class fooocusInpaintLoader:
return ((inpaint_head_model, inpaint_lora),)
#---------------------------------------------------------------适配器 开始----------------------------------------------------------------------#
def insightface_loader(provider):
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="buffalo_l", root=path, providers=[provider + 'ExecutionProvider', ])
model.prepare(ctx_id=0, det_size=(640, 640))
return model
# Apply Ipadapter
class ipadapter:
def __init__(self):
self.normol_presets = [
'LIGHT - SD1.5 only (low strength)',
'STANDARD (medium strength)',
'VIT-G (medium strength)',
'PLUS (high strength)',
'PLUS FACE (portraits)',
'FULL FACE - SD1.5 only (portraits stronger)'
]
self.faceid_presets = [
'FACEID',
'FACEID PLUS - SD1.5 only',
'FACEID PLUS V2',
'FACEID PORTRAIT (style transfer)'
]
self.presets = self.normol_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("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
if clipvision_name is not None:
log_node_info(node_name, f"Using {clipvision_name}")
return clipvision_file, clipvision_name
def get_ipadapter_file(self, preset, is_sdxl, node_name):
preset = preset.lower()
ipadapter_list = folder_paths.get_filename_list("ipadapter")
is_insightface = False
lora_pattern = None
if preset.startswith("light"):
if is_sdxl:
raise Exception("light model is not supported for SDXL")
pattern = 'sd15.light.v11\.(safetensors|bin)$'
# if light model v11 is not found, try with the old version
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("plus ("):
if is_sdxl:
pattern = 'plus.sdxl.vit.h\.(safetensors|bin)$'
else:
pattern = 'ip.adapter.plus.sd15\.(safetensors|bin)$'
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("faceid portrait"):
if is_sdxl:
raise Exception("portrait model is not supported for SDXL")
pattern = 'portrait.sd15\.(safetensors|bin)$'
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 -"):
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
if ipadapter_name is not None:
log_node_info(node_name, f"Using {ipadapter_name}")
return ipadapter_file, ipadapter_name, is_insightface, 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=True)
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
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
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}}
if optional_ipadapter is not None:
pipeline = optional_ipadapter
# 1. Load the clipvision model
if not clip_vision:
clipvision_file, clipvision_name = self.get_clipvision_file(preset, node_name)
if clipvision_file is None:
raise Exception("ClipVision model not found.")
if clipvision_file == pipeline['clipvision']['file']:
clip_vision = pipeline['clipvision']['model']
elif cache_mode in ["all", "clip_vision only"] and clipvision_name in cache:
log_node_info("easy ipadapterApply", f"Using ClipModel {clipvision_name} Cached")
clip_vision = cache[clipvision_name][1]
else:
clip_vision = load_clip_vision(clipvision_file)
update_cache(clipvision_name, (False, clip_vision))
pipeline['clipvision']['file'] = clipvision_file
pipeline['clipvision']['model'] = clip_vision
# 2. Load the ipadapter model
is_sdxl = isinstance(model.model, comfy.model_base.SDXL)
ipadapter_file, ipadapter_name, is_insightface, lora_pattern = self.get_ipadapter_file(preset, is_sdxl, node_name)
model_type = 'sdxl' if is_sdxl else 'sd15'
if ipadapter_file is None:
ipadapter_file = get_local_filepath(IPADAPTER_MODELS[preset][model_type]["model_url"], IPADAPTER_DIR)
ipadapter = self.ipadapter_model_loader(ipadapter_file)
pipeline['ipadapter']['file'] = ipadapter_file
pipeline['ipadapter']['model'] = ipadapter
# 3. Load the lora model if needed
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)
# 4. Load the insightface model if needed
if is_insightface:
icache_key = 'insightface-' + provider
if provider == pipeline['insightface']['provider']:
insightface = pipeline['insightface']['model']
elif icache_key in cache:
log_node_info("easy ipadapterApply", f"Using InsightFaceModel {icache_key} Cached")
insightface = cache[icache_key][1]
else:
insightface = insightface_loader(provider)
update_cache(icache_key, (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"],),
"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", "all", "none"], {"default": "insightface only"},),
"use_tiled": ("BOOLEAN", {"default": False},),
},
"optional": {
"attn_mask": ("MASK",),
"optional_ipadapter": ("IPADAPTER",),
}
}
RETURN_TYPES = ("MODEL", "IMAGE", "MASK", "IPADAPTER",)
RETURN_NAMES = ("model", "tiles", "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):
tiles, masks = [None], [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:
if "IPAdapterTiled" not in ALL_NODE_CLASS_MAPPINGS:
self.error()
cls = ALL_NODE_CLASS_MAPPINGS["IPAdapterTiled"]
model, tiles, 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 V2', 'FACEID PORTRAIT (style transfer)']:
if "IPAdapterAdvanced" not in ALL_NODE_CLASS_MAPPINGS:
self.error()
cls = ALL_NODE_CLASS_MAPPINGS["IPAdapterAdvanced"]
model, = cls().apply_ipadapter(model, ipadapter, image, weight, "linear", start_at, end_at, combine_embeds="concat", weight_faceidv2=weight_faceidv2, image_negative=None, clip_vision=None, attn_mask=attn_mask, insightface=None, embeds_scaling='V only')
else:
if "IPAdapter" not in ALL_NODE_CLASS_MAPPINGS:
self.error()
cls = ALL_NODE_CLASS_MAPPINGS["IPAdapter"]
model, = cls().apply_ipadapter(model, ipadapter, image, weight, start_at, end_at, attn_mask)
return (model, tiles, masks, ipadapter)
class ipadapterApplyAdvanced(ipadapter):
def __init__(self):
super().__init__()
pass
@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)']
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"],),
"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", "all", "none"], {"default": "insightface only"},),
"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",),
}
}
RETURN_TYPES = ("MODEL", "IMAGE", "MASK", "IPADAPTER",)
RETURN_NAMES = ("model", "tiles", "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, image_negative=None, clip_vision=None, attn_mask=None, optional_ipadapter=None):
tiles, masks = [None], [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:
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, tiles, masks = cls().apply_tiled(model, ipadapter, image, weight, weight_type, start_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, = 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)
#Apply InstantID
class instantID:
@@ -1670,7 +2011,7 @@ class instantIDApply(instantID):
OUTPUT_NODE = True
FUNCTION = "apply"
CATEGORY = "EasyUse/__for_testing"
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):
@@ -1718,7 +2059,7 @@ class instantIDApplyAdvanced(instantID):
OUTPUT_NODE = True
FUNCTION = "apply_advanced"
CATEGORY = "EasyUse/__for_testing"
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):
@@ -5326,6 +5667,9 @@ class showLoaderSettingsNames:
NODE_CLASS_MAPPINGS = {
# seed 随机种
"easy seed": easySeed,
"easy globalSeed": globalSeed,
# prompt 提示词
"easy positive": positivePrompt,
"easy negative": negativePrompt,
@@ -5344,12 +5688,16 @@ NODE_CLASS_MAPPINGS = {
"easy controlnetLoader": controlnetSimple,
"easy controlnetLoaderADV": controlnetAdvanced,
"easy LLLiteLoader": LLLiteLoader,
# Adapter 适配器
"easy ipadapterApply": ipadapterApply,
"easy ipadapterApplyADV": ipadapterApplyAdvanced,
"easy instantIDApply": instantIDApply,
"easy instantIDApplyADV": instantIDApplyAdvanced,
# Inpaint 内补
"easy fooocusInpaintLoader": fooocusInpaintLoader,
# latent 潜空间
"easy latentNoisy": latentNoisy,
"easy latentCompositeMaskedWithCond": latentCompositeMaskedWithCond,
# seed 随机种
"easy seed": easySeed,
"easy globalSeed": globalSeed,
# preSampling 预采样处理
"easy preSampling": samplerSettings,
"easy preSamplingAdvanced": samplerSettingsAdvanced,
@@ -5404,13 +5752,12 @@ NODE_CLASS_MAPPINGS = {
"easy showLoaderSettingsNames": showLoaderSettingsNames,
# "easy imageRemoveBG": imageREMBG,
"dynamicThresholdingFull": dynamicThresholdingFull,
# __for_testing 测试
"easy fooocusInpaintLoader": fooocusInpaintLoader,
"easy instantIDApply": instantIDApply,
"easy instantIDApplyADV": instantIDApplyAdvanced,
}
NODE_DISPLAY_NAME_MAPPINGS = {
# seed 随机种
"easy seed": "EasySeed",
"easy globalSeed": "EasyGlobalSeed",
# prompt 提示词
"easy positive": "Positive",
"easy negative": "Negative",
@@ -5429,12 +5776,16 @@ NODE_DISPLAY_NAME_MAPPINGS = {
"easy controlnetLoader": "EasyControlnet",
"easy controlnetLoaderADV": "EasyControlnet (Advanced)",
"easy LLLiteLoader": "EasyLLLite",
# Adapter 适配器
"easy ipadapterApply": "Easy Apply IPAdapter",
"easy ipadapterApplyADV": "Easy Apply IPAdapter (Advanced)",
"easy instantIDApply": "Easy Apply InstantID",
"easy instantIDApplyADV": "Easy Apply InstantID (Advanced)",
# Inpaint 内补
"easy fooocusInpaintLoader": "Load Fooocus Inpaint",
# latent 潜空间
"easy latentNoisy": "LatentNoisy",
"easy latentCompositeMaskedWithCond": "LatentCompositeMaskedWithCond",
# seed 随机种
"easy seed": "EasySeed",
"easy globalSeed": "EasyGlobalSeed",
# preSampling 预采样处理
"easy preSampling": "PreSampling",
"easy preSamplingAdvanced": "PreSampling (Advanced)",
@@ -5489,8 +5840,4 @@ NODE_DISPLAY_NAME_MAPPINGS = {
"easy showLoaderSettingsNames": "Show Loader Settings Names",
"easy imageRemoveBG": "ImageRemoveBG",
"dynamicThresholdingFull": "DynamicThresholdingFull",
# __for_testing 测试
"easy fooocusInpaintLoader": "Load Fooocus Inpaint",
"easy instantIDApply": "Easy Apply InstantID",
"easy instantIDApplyADV": "Easy Apply InstantID (Advanced)",
}
+2 -32
View File
@@ -1,5 +1,4 @@
from PIL import Image
from enum import Enum
import os
import hashlib
import folder_paths
@@ -7,37 +6,7 @@ import torch
import numpy as np
from nodes import MAX_RESOLUTION
from .log import log_node_info
def pil2tensor(image):
return torch.from_numpy(np.array(image).astype(np.float32) / 255.0).unsqueeze(0)
class ResizeMode(Enum):
RESIZE = "Just Resize"
INNER_FIT = "Crop and Resize"
OUTER_FIT = "Resize and Fill"
def int_value(self):
if self == ResizeMode.RESIZE:
return 0
elif self == ResizeMode.INNER_FIT:
return 1
elif self == ResizeMode.OUTER_FIT:
return 2
assert False, "NOTREACHED"
RESIZE_MODES = [ResizeMode.RESIZE.value, ResizeMode.INNER_FIT.value, ResizeMode.OUTER_FIT.value]
def get_new_bounds(width, height, left, right, top, bottom):
"""Returns the new bounds for an image with inset crop data."""
left = 0 + left
right = width - right
top = 0 + top
bottom = height - bottom
return (left, right, top, bottom)
# Tensor to PIL
def tensor2pil(image):
return Image.fromarray(np.clip(255. * image.cpu().numpy().squeeze(), 0, 255).astype(np.uint8))
from .libs.image import pil2tensor, tensor2pil, ResizeMode, get_new_bounds
# 图像裁切
class imageInsetCrop:
@@ -324,6 +293,7 @@ class imageScaleDownToSize(imageScaleDownBy):
class imagePixelPerfect:
@classmethod
def INPUT_TYPES(s):
RESIZE_MODES = [ResizeMode.RESIZE.value, ResizeMode.INNER_FIT.value, ResizeMode.OUTER_FIT.value]
return {
"required": {
"image": ("IMAGE",),
+35
View File
@@ -0,0 +1,35 @@
import torch
import numpy as np
from enum import Enum
from PIL import Image
# PIL to Tensor
def pil2tensor(image):
return torch.from_numpy(np.array(image).astype(np.float32) / 255.0).unsqueeze(0)
# Tensor to PIL
def tensor2pil(image):
return Image.fromarray(np.clip(255. * image.cpu().numpy().squeeze(), 0, 255).astype(np.uint8))
# Get new bounds
def get_new_bounds(width, height, left, right, top, bottom):
"""Returns the new bounds for an image with inset crop data."""
left = 0 + left
right = width - right
top = 0 + top
bottom = height - bottom
return (left, right, top, bottom)
class ResizeMode(Enum):
RESIZE = "Just Resize"
INNER_FIT = "Crop and Resize"
OUTER_FIT = "Resize and Fill"
def int_value(self):
if self == ResizeMode.RESIZE:
return 0
elif self == ResizeMode.INNER_FIT:
return 1
elif self == ResizeMode.OUTER_FIT:
return 2
assert False, "NOTREACHED"
+1 -28
View File
@@ -154,31 +154,4 @@ def easySave(images, filename_prefix, output_type, prompt=None, extra_pnginfo=No
return results['ui']['images']
else:
results = SaveImage().save_images(images, filename_prefix, prompt, extra_pnginfo)
return results['ui']['images']
# Image Utils
# from PIL import Image, ImageDraw
# import numpy as np
# import torch
# def is_image_transparent(img):
# print(img.shape)
# if len(img.shape) > 3 and img.shape[3] == 4:
# return True
# else:
# m = tensor2pil(img)
# if m.mode == "RGBA":
# return True
# else:
# return False
#
# def create_grid(image_size, box_size):
# img = Image.new('RGBA', image_size, (255, 255, 255, 255)) # 白色背景
# draw = ImageDraw.Draw(img)
#
# for x in range(0, img.width, box_size):
# for y in range(0, img.height, box_size):
# if (x // box_size % 2 == 0 and y // box_size % 2 == 0) or (x // box_size % 2 == 1 and y // box_size % 2 == 1):
# draw.rectangle([(x, y), (x+box_size, y+box_size)], fill=(204, 204, 204, 255)) # 不透明
# else:
# continue # 保持透明
# return img
return results['ui']['images']
+47 -7
View File
@@ -4,13 +4,13 @@ import { ComfyWidgets } from "/scripts/widgets.js";
let origProps = {};
const seedNodes = ["easy seed", "easy latentNoisy", "easy wildcards", "easy preSampling", "easy preSamplingAdvanced", "easy preSamplingNoiseIn", "easy preSamplingSdTurbo", "easy preSamplingCascade", "easy preSamplingDynamicCFG", "easy preSamplingLayerDiffusion", "easy fullkSampler", "easy fullCascadeKSampler"]
const loaderNodes = ["easy fullLoader", "easy a1111Loader", "easy comfyLoader"]
const findWidgetByName = (node, name) => node.widgets.find((w) => w.name === name);
const doesInputWithNameExist = (node, name) => node.inputs ? node.inputs.some((input) => input.name === name) : false;
function updateNodeHeight(node) {
node.setSize([node.size[0], node.computeSize()[1]]);
}
function updateNodeHeight(node) {node.setSize([node.size[0], node.computeSize()[1]]);}
function toggleWidget(node, widget, show = false, suffix = "") {
if (!widget || doesInputWithNameExist(node, widget.name)) return;
@@ -26,7 +26,6 @@ function toggleWidget(node, widget, show = false, suffix = "") {
const height = show ? Math.max(node.computeSize()[1], origSize[1]) : node.size[1];
node.setSize([node.size[0], height]);
}
function widgetLogic(node, widget) {
@@ -208,6 +207,45 @@ function widgetLogic(node, widget) {
toggleWidget(node, findWidgetByName(node, 'new_cond_end'), true)
}
}
if (widget.name === 'preset') {
const normol_presets = [
'LIGHT - SD1.5 only (low strength)',
'STANDARD (medium strength)',
'VIT-G (medium strength)',
'PLUS (high strength)', 'PLUS FACE (portraits)',
'FULL FACE - SD1.5 only (portraits stronger)',
'FACEID PORTRAIT (style transfer)'
]
const faceid_presets = [
'FACEID',
'FACEID PLUS - SD1.5 only',
'FACEID PLUS V2',
]
if(normol_presets.includes(widget.value)){
toggleWidget(node, findWidgetByName(node, 'lora_strength'))
toggleWidget(node, findWidgetByName(node, 'provider'))
toggleWidget(node, findWidgetByName(node, 'weight_faceidv2'))
}
else if(faceid_presets.includes(widget.value)){
if(widget.value == 'FACEID PLUS V2'){
toggleWidget(node, findWidgetByName(node, 'weight_faceidv2'), true)
}else{
toggleWidget(node, findWidgetByName(node, 'weight_faceidv2'))
}
toggleWidget(node, findWidgetByName(node, 'lora_strength'), true)
toggleWidget(node, findWidgetByName(node, 'provider'), true)
}
updateNodeHeight(node)
}
if (widget.name === 'use_tiled') {
if(widget.value)
toggleWidget(node, findWidgetByName(node, 'sharpening'), true)
else
toggleWidget(node, findWidgetByName(node, 'sharpening'))
updateNodeHeight(node)
}
}
function widgetLogic2(node, widget) {
@@ -486,6 +524,8 @@ app.registerExtension({
case "easy rangeFloat":
case 'easy latentCompositeMaskedWithCond':
case 'easy pipeEdit':
case 'easy ipadapterApply':
case 'easy ipadapterApplyADV':
getSetters(node)
break
case "easy wildcards":
@@ -734,7 +774,7 @@ app.registerExtension({
};
}
if (["easy fullLoader", "easy a1111Loader", "easy comfyLoader"].includes(nodeData.name)) {
if (loaderNodes.includes(nodeData.name)) {
function populate(text, type = 'positive') {
if (this.widgets) {
const pos = this.widgets.findIndex((w) => w.name === type + "_prompt");
@@ -781,7 +821,7 @@ app.registerExtension({
};
}
if (["easy seed", "easy latentNoisy", "easy wildcards", "easy preSampling", "easy preSamplingAdvanced", "easy preSamplingNoiseIn", "easy preSamplingSdTurbo", "easy preSamplingCascade", "easy preSamplingDynamicCFG", "easy preSamplingLayerDiffusion", "easy fullkSampler", "easy fullCascadeKSampler"].includes(nodeData.name)) {
if (seedNodes.includes(nodeData.name)) {
const onNodeCreated = nodeType.prototype.onNodeCreated;
nodeType.prototype.onNodeCreated = async function () {
onNodeCreated ? onNodeCreated.apply(this, []) : undefined;
@@ -922,7 +962,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']
'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']
function getSetters(node) {
if (node.widgets)
+28
View File
@@ -4,6 +4,7 @@ const loaders = ['easy fullLoader', 'easy a1111Loader', 'easy comfyLoader']
const preSampling = ['easy preSampling', 'easy preSamplingAdvanced', 'easy preSamplingDynamicCFG', 'easy preSamplingNoiseIn', 'easy preSamplingLayerDiffusion', 'easy fullkSampler']
const kSampler = ['easy kSampler', 'easy kSamplerTiled', 'easy kSamplerInpainting', 'easy kSamplerDownscaleUnet', 'easy kSamplerLayerDiffusion']
const controlnet = ['easy controlnetLoader', 'easy controlnetLoaderADV', 'easy instantIDApply', 'easy instantIDApplyADV']
const ipadapter = ['easy ipadapterApply', 'easy ipadapterApplyADV']
const positive_prompt = ['easy positive', 'easy wildcards']
const widgetMapping = {
"positive_prompt":{
@@ -45,6 +46,17 @@ const widgetMapping = {
"cn_strength": ["strength", "cn_strength"],
"cn_soft_weights": ["scale_soft_weights","cn_soft_weights"],
},
"ipadapter":{
"preset":"preset",
"lora_strength": "lora_strength",
"provider": "provider",
"weight":"weight",
"weight_faceidv2": "weight_faceidv2",
"start_at": "start_at",
"end_at": "end_at",
"cache_mode": "cache_mode",
"use_tiled": "use_tiled",
}
}
const inputMapping = {
"loaders":{
@@ -73,6 +85,12 @@ const inputMapping = {
"positive_prompt":{
},
"ipadapter":{
"model":"model",
"image":"image",
"attn_mask":"attn_mask",
"optional_ipadapter":"optional_ipadapter"
}
};
const outputMapping = {
@@ -104,6 +122,12 @@ const outputMapping = {
"load_image":{
"IMAGE":"IMAGE",
"MASK": "MASK"
},
"ipadapter":{
"model":"model",
"tiles":"tiles",
"masks":"masks",
"ipadapter":"ipadapter"
}
};
@@ -491,6 +515,10 @@ app.registerExtension({
if (controlnet.includes(nodeData.name)) {
addMenu("↪️ Swap EasyControlnet", 'controlnet', controlnet, nodeType)
}
// Swap IPAdapater
if (ipadapter.includes(nodeData.name)) {
addMenu("↪️ Swap EasyIPAdapater", 'ipadapter', ipadapter, nodeType)
}
}
});