diff --git a/README.en.md b/README.en.md
index ab3921b..197a285 100644
--- a/README.en.md
+++ b/README.en.md
@@ -29,7 +29,7 @@ After installing the node package, the UI interface will be automatically switch
### Stable Cascade
-[WorkFlow Example](https://github.com/yolain/ComfyUI-Easy-Use?tab=readme-ov-file#StableCascade)
+[WorkFlow Example](https://github.com/yolain/ComfyUI-Easy-Use/blob/main/README.en.md#StableCascade)
Currently, txt2img and img2img are supported,Lora and Controlnet are comming soon!
Usage:
@@ -39,24 +39,21 @@ Usage:
## Changelog
-**2024-02-29**
+**v1.0.9 [2024-3-2]**
-- Fixed `easy svdLoader` error when the positive or negative is empty
+- Added `easy instantIDApply` - you need installed [ComfyUI_InstantID](https://github.com/cubiq/ComfyUI_InstantID) fisrt, Workflow[Example](https://github.com/yolain/ComfyUI-Easy-Use/blob/main/README.en.md#InstantID)
+- Fixed `easy detailerFix` not added to the list of nodes available for saving images formatting extensions
+- Fixed `easy XYInputs: PromptSR` errors are reported when replacing negative prompts
-**2024-02-28**
-
-- Fixed the issue that 'easy preSampling' and other similar node, latent could not be generated based on the batch index after passing in
-
-**2024-02-26**
-
-- `easy fullLoader` **positive**、**negative**、**latent** added to the output items
-- Fixed the error of SDXLClipModel in ComfyUI revision 2016[c2cb8e88] and above (the revision number was judged to be compatible with the old revision)
-- Fixed `easy detailerFix` generation error when batch size is greater than 1
-
-**v1.0.8(2024-02-25)**
+**v1.0.8 (f28cbf7)**
- `easy cascadeLoader` stage_c and stage_b support the checkpoint model (Download [checkpoints](https://huggingface.co/stabilityai/stable-cascade/tree/main/comfyui_checkpoints) models)
- `easy styleSelector` The search box is modified to be case-insensitive
+- `easy fullLoader` **positive**、**negative**、**latent** added to the output items
+- Fixed the issue that 'easy preSampling' and other similar node, latent could not be generated based on the batch index after passing in
+- Fixed `easy svdLoader` error when the positive or negative is empty
+- Fixed the error of SDXLClipModel in ComfyUI revision 2016[c2cb8e88] and above (the revision number was judged to be compatible with the old revision)
+- Fixed `easy detailerFix` generation error when batch size is greater than 1
- Optimize the code, reduce a lot of redundant code and improve the running speed
**v1.0.7 (2024-02-19)**
@@ -66,7 +63,7 @@ Usage:
- Added `easy fullCascadeKSampler` - stable cascade stage-c ksampler full
- Added `easy cascadeKSampler` - stable cascade stage-c ksampler simple
-
-- Optimize the image to image[Example](https://github.com/yolain/ComfyUI-Easy-Use?tab=readme-ov-file#image-to-image)
+- Optimize the image to image[Example](https://github.com/yolain/ComfyUI-Easy-Use/blob/main/README.en.md#image-to-image)
**v1.0.6**
diff --git a/README.md b/README.md
index 51fe134..784bb3f 100644
--- a/README.md
+++ b/README.md
@@ -43,33 +43,28 @@ stage_c 与 stage_b 可以使用[checkpoints](https://huggingface.co/stabilityai
## 更新日志
-**2024-02-29**
-- 修复 `easy svdLoader` 报错
+**v1.0.9 [2024-3-2]**
-**2024-02-28**
+- 新增 `easy instantIDApply` - 需要先安装 [ComfyUI_InstantID](https://github.com/cubiq/ComfyUI_InstantID), 工作流参考[示例](https://github.com/yolain/ComfyUI-Easy-Use?tab=readme-ov-file#InstantID)
+- 修复 `easy detailerFix` 未添加到保存图片格式化扩展名可用节点列表
+- 修复 `easy XYInputs: PromptSR` 在替换负面提示词时报错
+**v1.0.8 (f28cbf7)**
+
+- `easy cascadeLoader` stage_c 与 stage_b 支持checkpoint模型 (需要下载[checkpoints](https://huggingface.co/stabilityai/stable-cascade/tree/main/comfyui_checkpoints))
+- `easy styleSelector` 搜索框修改为不区分大小写匹配
+- `easy fullLoader` 增加 **positive**、**negative**、**latent** 输出项
+- 修复 SDXLClipModel 在 ComfyUI 修订版本号 2016[c2cb8e88] 及以上的报错(判断了版本号可兼容老版本)
+- 修复 `easy detailerFix` 批次大小大于1时生成出错
- 修复`easy preSampling`等 latent传入后无法根据批次索引生成的问题
-
-**2024-02-27**
-
+- 修复 `easy svdLoader` 报错
+- 优化代码,减少了诸多冗余,提升运行速度
- 去除中文翻译对照文本
(翻译对照已由 [AIGODLIKE-COMFYUI-TRANSLATION](https://github.com/AIGODLIKE/AIGODLIKE-ComfyUI-Translation) 统一维护啦!
首次下载或者版本较早的朋友请更新 AIGODLIKE-COMFYUI-TRANSLATION 和本节点包至最新版本。)
-**2024-02-26**
-
-- `easy fullLoader` 增加 **positive**、**negative**、**latent** 输出项
-- 修复 SDXLClipModel 在 ComfyUI 修订版本号 2016[c2cb8e88] 及以上的报错(判断了版本号可兼容老版本)
-- 修复 `easy detailerFix` 批次大小大于1时生成出错
-
-**v1.0.8(2024-02-25)**
-
-- `easy cascadeLoader` stage_c 与 stage_b 支持checkpoint模型 (需要下载[checkpoints](https://huggingface.co/stabilityai/stable-cascade/tree/main/comfyui_checkpoints))
-- `easy styleSelector` 搜索框修改为不区分大小写匹配
-- 优化代码,减少了诸多冗余,提升运行速度
-
**v1.0.7**
- 增加 `easy cascadeLoader` - stable cascade 加载器
@@ -255,9 +250,9 @@ stage_c 与 stage_b 可以使用[checkpoints](https://huggingface.co/stabilityai
-#### SDTurbo+高清修复+SVD
+#### InstantID
-
+
### StableCascade
#### 文生图
@@ -266,6 +261,7 @@ stage_c 与 stage_b 可以使用[checkpoints](https://huggingface.co/stabilityai
#### 图生图
+
## Credits
[ComfyUI](https://github.com/comfyanonymous/ComfyUI) - 功能强大且模块化的Stable Diffusion GUI
diff --git a/__init__.py b/__init__.py
index bbd5adc..93c24fc 100644
--- a/__init__.py
+++ b/__init__.py
@@ -74,4 +74,4 @@ WEB_DIRECTORY = "./web"
__all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS', "WEB_DIRECTORY"]
-print('\033[34mComfy-Easy-Use (v1.0.8): \033[92mLoaded\033[0m')
\ No newline at end of file
+print('\033[34mComfy-Easy-Use (v1.0.9): \033[92mLoaded\033[0m')
\ No newline at end of file
diff --git a/py/easyNodes.py b/py/easyNodes.py
index 385317a..4ee0037 100644
--- a/py/easyNodes.py
+++ b/py/easyNodes.py
@@ -1441,6 +1441,7 @@ class controlnetSimple:
"loader_settings": pipe["loader_settings"]
}
+ del pipe
return (new_pipe, positive, negative)
# controlnetADV
@@ -1535,6 +1536,8 @@ class LLLiteLoader:
return (model_lllite,)
+#---------------------------------------------------------------测试 开始----------------------------------------------------------------------#
+
# FooocusInpaint (Testing)
from .fooocus import InpaintHead, InpaintWorker
inpaint_head_model = None
@@ -1567,6 +1570,90 @@ class fooocusInpaintLoader:
return ((inpaint_head_model, inpaint_lora),)
+#Apply InstantID
+class instantIDApply:
+
+ def __init__(self):
+ 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")
+ OUTPUT_NODE = True
+
+ FUNCTION = "apply"
+ CATEGORY = "EasyUse/__for_testing"
+
+ def error(self):
+ raise Exception(f"[ERROR] To use instantIDApply, you need to install 'ComfyUI_InstantID'")
+
+ 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):
+ instantid_model, insightface_model, face_embeds = None, None, None
+ model = pipe['model']
+ positive = pipe['positive']
+ negative = pipe['negative']
+ # Load InstantID
+ if "InstantIDModelLoader" in ALL_NODE_CLASS_MAPPINGS:
+ load_instant_cls = ALL_NODE_CLASS_MAPPINGS["InstantIDModelLoader"]
+ instantid_model, = load_instant_cls().load_model(instantid_file)
+ else:
+ self.error()
+ if "InstantIDFaceAnalysis" in ALL_NODE_CLASS_MAPPINGS:
+ load_insightface_cls = ALL_NODE_CLASS_MAPPINGS["InstantIDFaceAnalysis"]
+ insightface_model, = load_insightface_cls().load_insight_face(insightface)
+ else:
+ self.error()
+
+ # Apply InstantID
+ if "ApplyInstantID" in ALL_NODE_CLASS_MAPPINGS:
+ instantid_apply = ALL_NODE_CLASS_MAPPINGS['ApplyInstantID']
+ control_net = easyControlnet().load_controlnet(control_net_name, control_net, 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=None)
+ 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)
#---------------------------------------------------------------预采样 开始----------------------------------------------------------------------#
# 预采样设置(基础)
@@ -3373,26 +3460,6 @@ class detailerFix:
return {"ui": {"images": results}, "result": (new_pipe, result_img, result_cropped_enhanced, result_cropped_enhanced_alpha, result_mask, result_cnet_images )}
-def add_folder_path_and_extensions(folder_name, full_folder_paths, extensions):
- for full_folder_path in full_folder_paths:
- folder_paths.add_model_folder_path(folder_name, full_folder_path)
- if folder_name in folder_paths.folder_names_and_paths:
- current_paths, current_extensions = folder_paths.folder_names_and_paths[folder_name]
- updated_extensions = current_extensions | extensions
- folder_paths.folder_names_and_paths[folder_name] = (current_paths, updated_extensions)
- else:
- folder_paths.folder_names_and_paths[folder_name] = (full_folder_paths, extensions)
-
-model_path = folder_paths.models_dir
-add_folder_path_and_extensions("ultralytics_bbox", [os.path.join(model_path, "ultralytics", "bbox")], folder_paths.supported_pt_extensions)
-add_folder_path_and_extensions("ultralytics_segm", [os.path.join(model_path, "ultralytics", "segm")], folder_paths.supported_pt_extensions)
-add_folder_path_and_extensions("ultralytics", [os.path.join(model_path, "ultralytics")], folder_paths.supported_pt_extensions)
-add_folder_path_and_extensions("mmdets_bbox", [os.path.join(model_path, "mmdets", "bbox")], folder_paths.supported_pt_extensions)
-add_folder_path_and_extensions("mmdets_segm", [os.path.join(model_path, "mmdets", "segm")], folder_paths.supported_pt_extensions)
-add_folder_path_and_extensions("mmdets", [os.path.join(model_path, "mmdets")], folder_paths.supported_pt_extensions)
-add_folder_path_and_extensions("sams", [os.path.join(model_path, "sams")], folder_paths.supported_pt_extensions)
-add_folder_path_and_extensions("onnx", [os.path.join(model_path, "onnx")], {'.onnx'})
-
class ultralyticsDetectorForDetailerFix:
@classmethod
def INPUT_TYPES(s):
@@ -4697,7 +4764,8 @@ NODE_CLASS_MAPPINGS = {
# "easy imageRemoveBG": imageREMBG,
"dynamicThresholdingFull": dynamicThresholdingFull,
# __for_testing 测试
- "easy fooocusInpaintLoader": fooocusInpaintLoader
+ "easy fooocusInpaintLoader": fooocusInpaintLoader,
+ "easy instantIDApply": instantIDApply,
}
NODE_DISPLAY_NAME_MAPPINGS = {
# prompt 提示词
@@ -4773,5 +4841,6 @@ NODE_DISPLAY_NAME_MAPPINGS = {
"easy imageRemoveBG": "ImageRemoveBG",
"dynamicThresholdingFull": "DynamicThresholdingFull",
# __for_testing 测试
- "easy fooocusInpaintLoader": "Load Fooocus Inpaint"
+ "easy fooocusInpaintLoader": "Load Fooocus Inpaint",
+ "easy instantIDApply": "Easy Apply InstantID"
}
\ No newline at end of file
diff --git a/py/libs/controlnet.py b/py/libs/controlnet.py
index c37c8b2..034c9b0 100644
--- a/py/libs/controlnet.py
+++ b/py/libs/controlnet.py
@@ -1,12 +1,13 @@
import folder_paths
import comfy.controlnet
+import comfy.model_management
from nodes import NODE_CLASS_MAPPINGS
class easyControlnet:
def __init__(self):
pass
- def apply(self, control_net_name, image, positive, negative, strength, start_percent=0, end_percent=1, control_net=None, scale_soft_weights=1):
+ def load_controlnet(self, control_net_name, control_net, scale_soft_weights):
if control_net is None:
if scale_soft_weights < 1:
if "ScaledSoftControlNetWeights" in NODE_CLASS_MAPPINGS:
@@ -19,43 +20,64 @@ class easyControlnet:
else:
controlnet_path = folder_paths.get_full_path("controlnet", control_net_name)
control_net = comfy.controlnet.load_controlnet(controlnet_path)
+ return control_net
+
+ def apply(self, control_net_name, image, positive, negative, strength, start_percent=0, end_percent=1, control_net=None, scale_soft_weights=1, mask=None):
+ if strength == 0:
+ return (positive, negative)
+
+ control_net = self.load_controlnet(control_net_name, control_net, scale_soft_weights)
+
+ if mask is not None:
+ mask = mask.to(self.device)
+
+ if mask is not None and len(mask.shape) < 3:
+ mask = mask.unsqueeze(0)
control_hint = image.movedim(-1, 1)
- if strength != 0:
- if negative is None:
- p = []
- for t in positive:
- n = [t[0], t[1].copy()]
- c_net = control_net.copy().set_cond_hint(control_hint, strength, (start_percent, end_percent))
- if 'control' in t[1]:
- c_net.set_previous_controlnet(t[1]['control'])
- n[1]['control'] = c_net
- n[1]['control_apply_to_uncond'] = True
- p.append(n)
- positive = p
- else:
- cnets = {}
- out = []
- for conditioning in [positive, negative]:
- c = []
- for t in conditioning:
- d = t[1].copy()
+ is_cond = True
+ if negative is None:
+ p = []
+ for t in positive:
+ n = [t[0], t[1].copy()]
+ c_net = control_net.copy().set_cond_hint(control_hint, strength, (start_percent, end_percent))
+ if 'control' in t[1]:
+ c_net.set_previous_controlnet(t[1]['control'])
+ n[1]['control'] = c_net
+ n[1]['control_apply_to_uncond'] = True
+ if mask is not None:
+ n[1]['mask'] = mask
+ n[1]['set_area_to_bounds'] = False
+ p.append(n)
+ positive = p
+ else:
+ cnets = {}
+ out = []
+ for conditioning in [positive, negative]:
+ c = []
+ for t in conditioning:
+ d = t[1].copy()
- prev_cnet = d.get('control', None)
- if prev_cnet in cnets:
- c_net = cnets[prev_cnet]
- else:
- c_net = control_net.copy().set_cond_hint(control_hint, strength, (start_percent, end_percent))
- c_net.set_previous_controlnet(prev_cnet)
- cnets[prev_cnet] = c_net
+ prev_cnet = d.get('control', None)
+ if prev_cnet in cnets:
+ c_net = cnets[prev_cnet]
+ else:
+ c_net = control_net.copy().set_cond_hint(control_hint, strength, (start_percent, end_percent))
+ c_net.set_previous_controlnet(prev_cnet)
+ cnets[prev_cnet] = c_net
- d['control'] = c_net
- d['control_apply_to_uncond'] = False
- n = [t[0], d]
- c.append(n)
- out.append(c)
- positive = out[0]
- negative = out[1]
+ d['control'] = c_net
+ d['control_apply_to_uncond'] = False
+
+ if mask is not None:
+ d['mask'] = mask
+ d['set_area_to_bounds'] = False
+
+ n = [t[0], d]
+ c.append(n)
+ out.append(c)
+ positive = out[0]
+ negative = out[1]
return (positive, negative)
\ No newline at end of file
diff --git a/py/libs/xyplot.py b/py/libs/xyplot.py
index ffb9b47..4e21aef 100644
--- a/py/libs/xyplot.py
+++ b/py/libs/xyplot.py
@@ -367,7 +367,7 @@ class easyXYPlot():
w_max=1.0,
apply_to_pooled="enable", a1111_prompt_style=a1111_prompt_style, steps=steps)
if "negative_cond" in plot_image_vars:
- positive = positive + plot_image_vars["negative_cond"]
+ negative = negative + plot_image_vars["negative_cond"]
# ControlNet
if "ControlNet" in self.x_type or "ControlNet" in self.y_type:
diff --git a/web/js/easy/easySaveImage.js b/web/js/easy/easySaveImage.js
index 469804e..84e8c63 100644
--- a/web/js/easy/easySaveImage.js
+++ b/web/js/easy/easySaveImage.js
@@ -4,7 +4,7 @@ import { applyTextReplacements } from "/scripts/utils.js";
app.registerExtension({
name: "Comfy.Easy.SaveImageExtraOutput",
async beforeRegisterNodeDef(nodeType, nodeData, app) {
- if (["easy imageSave", "easy fullkSampler", "easy kSampler", "easy kSamplerTiled","easy kSamplerInpainting", "easy kSamplerDownscaleUnet", "easy kSamplerSDTurbo"].includes(nodeData.name)) {
+ if (["easy imageSave", "easy fullkSampler", "easy kSampler", "easy kSamplerTiled","easy kSamplerInpainting", "easy kSamplerDownscaleUnet", "easy kSamplerSDTurbo","easy detailerFix"].includes(nodeData.name)) {
const onNodeCreated = nodeType.prototype.onNodeCreated;
// When the SaveImage node is created we want to override the serialization of the output name widget to run our S&R
nodeType.prototype.onNodeCreated = function () {