Upgrade to v1.1.6

This commit is contained in:
yolain
2024-04-26 11:55:12 +08:00
parent 79b81100ff
commit f6b5f5c99c
17 changed files with 520 additions and 56 deletions
+39 -14
View File
@@ -33,6 +33,17 @@
-
## Changelog
**v1.1.6 (2024/4/26)**
- Added `easy styleAlignedBatchAlign` - Credit of [style_aligned_comfy](https://github.com/brianfitzgerald/style_aligned_comfy)
- Added `easy ckptNames`
- Added `easy controlnetNames`
- Added `easy imagesSplitimage` - Batch images split into single images
- Added `easy imageCount` - Get Image Count
- Added `easy textSwitch` - Text Switch
- `easy kSampler` & `easy fullkSampler` added **Preview&Choose** to **image_output** widget
**v1.1.5 (2024/4/24)**
- Rewrite `easy cleanGPUUsed` - the memory usage of the comfyUI can to be cleared
@@ -60,7 +71,7 @@
- Added `easy promptConcat`
- `easy wildcards` Added **multiline_mode**
**v1.1.2 (39c5ccf)**
**v1.1.2**
- Optimized some of the recommended nodes for slots related to EasyUse
- Added **Enable ContextMenu Auto Nest Subdirectories** The setting item is enabled by default, and it can be classified into subdirectories, checkpoints and loras previews
@@ -76,7 +87,7 @@
- Fixed layerDiffuse xyplot bug
- `easy kSamplerInpainting` add *additional* widget,you can choose 'Differential Diffusion' or 'Only InpaintModelConditioning'
**v1.1.1 (2024/3/16)**
**v1.1.1**
- The issue that the seed is 0 when a node with a seed control is added and **control before generate** is fixed for the first time run queue prompt.
- `easy preSamplingAdvanced` Added **return_with_leftover_noise**
@@ -86,7 +97,7 @@
- Remove forced **control_before_generate** settings。 If you want to use control_before_generate, change widget_value_control_mode to before in system settings
- Added `easy imageRemBg` - The default is BriaAI's RMBG-1.4 model, which removes the background effect more and faster
**v1.1.0 (d5ff84e)**
**v1.1.0**
- Added `easy imageSplitList` - to split every N images
- Added `easy preSamplingDiffusionADDTL` - It can modify foreground、background or blended additional prompt
@@ -102,15 +113,18 @@
- Fixed `easy instantIDApply` mask not input right
**v1.0.9 (ff1add1)**
<details>
<summary><b>v1.0.9</b></summary>
- Fixed the error when ComfyUI-Impack-Pack and ComfyUI_InstantID were not installed
- Fixed `easy pipeIn`
- 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
</details>
**v1.0.8 (f28cbf7)**
<details>
<summary><b>v1.0.8</b></summary>
- `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
@@ -120,8 +134,10 @@
- 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
</details>
**v1.0.7 (2024-02-19)**
<details>
<summary><b>v1.0.7</b></summary>
- Added `easy cascadeLoader` - stable cascade Loader
- Added `easy preSamplingCascade` - stable cascade preSampling Settings
@@ -129,22 +145,28 @@
- Added `easy cascadeKSampler` - stable cascade stage-c ksampler simple
-
- Optimize the image to image[Example](https://github.com/yolain/ComfyUI-Easy-Use/blob/main/README.en.md#image-to-image)
</details>
**v1.0.6**
<details>
<summary><b>v1.0.6</b></summary>
- Added `easy XYInputs: Checkpoint`
- Added `easy XYInputs: Lora`
- `easy seed` can manually switch the random seed when increasing the fixed seed value
- Fixed `easy fullLoader` and all loaders to automatically adjust the node size when switching LoRa
- Removed the original ttn image saving logic and adapted to the default image saving format extension of ComfyUI
</details>
- **v1.0.5**
<details>
<summary><b>v1.0.5</b></summary>
- Added `easy isSDXL`
- Added prompt word control on `easy svdLoader`, which can be used with open_clip model
- Added **populated_text** on `easy wildcards`, wildcard populated text can be output
</details>
**v1.0.4**
<details>
<summary><b>v1.0.4</b></summary>
- `easy showAnything` added support for converting other types (e.g., tensor conditions, images, etc.)
- Added `easy showLoaderSettingsNames` can display the model and VAE name in the output loader assembly
@@ -164,9 +186,10 @@
- Changing the first-time install node package no longer automatically replaces the theme, you need to manually adjust and refresh the page
- `easy imageSave` added **only_preivew**
- Adjust the `easy latentCompositeMaskedWithCond` node
</details>
**v1.0.3**
<details>
<summary><b>v1.0.3</b></summary>
- Added `easy stylesSelector`
- Added **scale_soft_weights** in `easy controlnetLoader` and `easy controlnetLoaderADV`
@@ -184,9 +207,10 @@
- Adjust the UI theme, divided into two sets of styles: the official default background and the dark black background, which can be switched in the color palette in the settings
- Modify the styles path to be compatible with other environments
</details>
**v1.0.2**
<details>
<summary><b>v1.0.2</b></summary>
- Added `easy XYPlotAdvanced` and some nodes about `easy XYInputs`
- Added **Alt+1-Alt+9** Shortcut keys to quickly paste node presets for Node templates (corresponding to 1~9 sequences)
@@ -203,7 +227,7 @@
- Removed `easy imageRemBg`
- Remove the introductory diagram and workflow files from the package to reduce the package size
- Replaced the font file used in the generation of XY diagrams
</details>
<details>
<summary><b>v1.0.1</b></summary>
@@ -298,6 +322,7 @@ Disclaimer: Opened source was not easy. I have a lot of respect for the contribu
| easy preSamplingLayerDiffusion | [ComfyUI-layerdiffusion](https://github.com/huchenlei/ComfyUI-layerdiffusion) | LayeredDiffusionApply... |
| easy dynamiCrafterLoader | [ComfyUI-layerdiffusion](https://github.com/ExponentialML/ComfyUI_Native_DynamiCrafter) | Apply Dynamicrafter |
| easy imageChooser | [cg-image-picker](https://github.com/chrisgoringe/cg-image-picker) | Preview Chooser |
| easy styleAlignedBatchAlign | [style_aligned_comfy](https://github.com/chrisgoringe/cg-image-picker) | styleAlignedBatchAlign |
## Workflow Examples
+26 -9
View File
@@ -37,7 +37,17 @@
## 更新日志
**v1.1.5 (2024/4/24)**
**v1.1.6 (2024/4/26)**
- 增加 `easy styleAlignedBatchAlign` - 风格对齐 [style_aligned_comfy](https://github.com/brianfitzgerald/style_aligned_comfy)
- 增加 `easy ckptNames`
- 增加 `easy controlnetNames`
- 增加 `easy imagesSplitimage` - 批次图像拆分单张
- 增加 `easy imageCount` - 图像数量
- 增加 `easy textSwitch` - 文字切换
- `easy kSampler` 和 `easy fullkSampler` 的 **image_output** 增加 **Preview&Choose**选项
**v1.1.5**
- 重写 `easy cleanGPUUsed` - 可强制清理comfyUI的模型显存占用
- 增加 `easy humanSegmentation` - 多类分割、人像分割
@@ -47,7 +57,7 @@
- 增加 `easy imageInterrogator` - 图像反推
- 增加 `easy stableDiffusion3API` - 简易的Stable Diffusion 3 多账号API节点
**v1.1.4 (2024/4/13)**
**v1.1.4**
- 增加 `easy imageChooser` - 从[cg-image-picker](https://github.com/chrisgoringe/cg-image-picker)简化的图片选择器
- 增加 `easy preSamplingCustom` - 自定义预采样,可支持cosXL-edit
@@ -56,7 +66,7 @@
- 修复 `easy preSamplingNoiseIn`、`easy latentNoisy`、`east Unsampler` 以兼容ComfyUI Revision>=2098 [0542088e] 以上版本
- 修复 FooocusInpaint修改ModelPatcher计算权重引发的问题,理应在生成model后重置ModelPatcher为默认值
**v1.1.3 (2024/4/4)**
**v1.1.3**
- `easy ipadapterApply` 增加 **COMPOSITION** 预置项
- 增加 对[ResAdapter](https://huggingface.co/jiaxiangc/res-adapter) lora模型 的加载支持
@@ -66,7 +76,7 @@
- `easy wildcards` 增加 **multiline_mode**属性
- 增加 当节点需要下载模型时,若huggingface连接超时,会切换至镜像地址下载模型
**v1.1.2 (39c5ccf)**
**v1.1.2**
- 改写 EasyUse 相关节点的部分插槽推荐节点
- 增加 **启用上下文菜单自动嵌套子目录** 设置项,默认为启用状态,可分类子目录及checkpoints、loras预览图
@@ -82,7 +92,7 @@
- 修复 `easy pipeEdit` 提示词输入lora时报错
- 修复 layerDiffuse xyplot相关bug
**v1.1.1 (5c8af8f)**
**v1.1.1**
- 修复首次添加含seed的节点且当前模式为control_before_generate时,seed为0的问题
- `easy preSamplingAdvanced` 增加 **return_with_leftover_noise**
@@ -93,7 +103,7 @@
- 去除强制**control_before_generate**设定
- 增加 `easy imageRemBg` - 默认为BriaAI的RMBG-1.4模型, 移除背景效果更加,速度更快
**v1.1.0 (d5ff84e)**
**v1.1.0**
- 增加 `easy imageSplitList` - 拆分每 N 张图像
- 增加 `easy preSamplingDiffusionADDTL` - 可配置前景、背景、blended的additional_prompt等
@@ -107,15 +117,18 @@
- 修复 `easy instantIDApply` mask 未传入正确值
- 修复 在 非a1111提示词风格下 BREAK 不生效的问题
**v1.0.9 (ff1add1)**
<details>
<summary><b>v1.0.9</b></summary>
- 修复未安装 ComfyUI-Impack-Pack 和 ComfyUI_InstantID 时报错
- 修复 `easy pipeIn` - pipe设为可不必选
- 增加 `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` 在替换负面提示词时报错
</details>
**v1.0.8 (f28cbf7)**
<details>
<summary><b>v1.0.8</b></summary>
- `easy cascadeLoader` stage_c 与 stage_b 支持checkpoint模型 (需要下载[checkpoints](https://huggingface.co/stabilityai/stable-cascade/tree/main/comfyui_checkpoints))
- `easy styleSelector` 搜索框修改为不区分大小写匹配
@@ -129,13 +142,16 @@
(翻译对照已由 [AIGODLIKE-COMFYUI-TRANSLATION](https://github.com/AIGODLIKE/AIGODLIKE-ComfyUI-Translation) 统一维护啦!
首次下载或者版本较早的朋友请更新 AIGODLIKE-COMFYUI-TRANSLATION 和本节点包至最新版本。)
</details>
**v1.0.7**
<details>
<summary><b>v1.0.7</b></summary>
- 增加 `easy cascadeLoader` - stable cascade 加载器
- 增加 `easy preSamplingCascade` - stabled cascade stage_c 预采样参数
- 增加 `easy fullCascadeKSampler` - stable cascade stage_c 完整版采样器
- 增加 `easy cascadeKSampler` - stable cascade stage-c ksampler simple
</details>
<details>
<summary><b>v1.0.6</b></summary>
@@ -310,6 +326,7 @@
| easy preSamplingLayerDiffusion | [ComfyUI-layerdiffusion](https://github.com/huchenlei/ComfyUI-layerdiffusion) | LayeredDiffusionApply等 |
| easy dynamiCrafterLoader | [ComfyUI-layerdiffusion](https://github.com/ExponentialML/ComfyUI_Native_DynamiCrafter) | Apply Dynamicrafter |
| easy imageChooser | [cg-image-picker](https://github.com/chrisgoringe/cg-image-picker) | Preview Chooser |
| easy styleAlignedBatchAlign | [style_aligned_comfy](https://github.com/chrisgoringe/cg-image-picker) | styleAlignedBatchAlign |
## 示例
+3 -1
View File
@@ -1,3 +1,5 @@
__version__ = "1.1.6"
import os
import glob
import folder_paths
@@ -85,4 +87,4 @@ WEB_DIRECTORY = "./web"
__all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS', "WEB_DIRECTORY"]
print('\033[34mComfy-Easy-Use (v1.1.5): \033[92mLoaded\033[0m')
print(f'\033[34mComfy-Easy-Use v{__version__}: \033[92mLoaded\033[0m')
+92 -5
View File
@@ -22,7 +22,7 @@ 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, AlwaysEqualProxy
from .libs.utils import find_wildcards_seed, is_linked_styles_selector, easySave, get_local_filepath, add_folder_path_and_extensions, AlwaysEqualProxy, get_sd_version
from .libs.loader import easyLoader
from .libs.sampler import easySampler
from .libs.xyplot import easyXYPlot
@@ -819,6 +819,39 @@ class globalSeed:
#---------------------------------------------------------------加载器 开始----------------------------------------------------------------------#
class setCkptName:
@classmethod
def INPUT_TYPES(cls):
return {"required": {
"ckpt_name": (folder_paths.get_filename_list("checkpoints"),),
}
}
RETURN_TYPES = (AlwaysEqualProxy('*'),)
RETURN_NAMES = ("ckpt_name",)
FUNCTION = "set_name"
CATEGORY = "EasyUse/Utils"
def set_name(self, ckpt_name):
return (ckpt_name,)
class setControlName:
@classmethod
def INPUT_TYPES(cls):
return {"required": {
"controlnet_name": (folder_paths.get_filename_list("controlnet"),),
}
}
RETURN_TYPES = (AlwaysEqualProxy('*'),)
RETURN_NAMES = ("controlnet_name",)
FUNCTION = "set_name"
CATEGORY = "EasyUse/Utils"
def set_name(self, controlnet_name):
return (controlnet_name,)
# 简易加载器完整
class fullLoader:
@@ -2084,6 +2117,27 @@ class fooocusInpaintLoader:
return ((inpaint_head_model, inpaint_lora),)
#---------------------------------------------------------------适配器 开始----------------------------------------------------------------------#
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),)
def insightface_loader(provider):
try:
from insightface.app import FaceAnalysis
@@ -3236,7 +3290,7 @@ class samplerCustomSettings:
"cfg": ("FLOAT", {"default": 8.0, "min": 0.0, "max": 100.0}),
"cfg_negative": ("FLOAT", {"default": 8.0, "min": 0.0, "max": 100.0}),
"sampler_name": (comfy.samplers.KSampler.SAMPLERS + ['inversed_euler'],),
"scheduler": (comfy.samplers.KSampler.SCHEDULERS + ['karrasADV','exponentialADV','polyExponential','sdturbo','vp'],),
"scheduler": (comfy.samplers.KSampler.SCHEDULERS + ['karrasADV','exponentialADV','polyExponential', 'sdturbo', 'vp'],),
"steps": ("INT", {"default": 20, "min": 1, "max": 10000}),
"sigma_max": ("FLOAT", {"default": 14.614642, "min": 0.0, "max": 1000.0, "step": 0.01, "round": False}),
"sigma_min": ("FLOAT", {"default": 0.0291675, "min": 0.0, "max": 1000.0, "step": 0.01, "round": False}),
@@ -3327,6 +3381,7 @@ class samplerCustomSettings:
return (ksampler,)
def settings(self, pipe, guider, cfg, cfg_negative, sampler_name, scheduler, steps, sigma_max, sigma_min, rho, beta_d, beta_min, eps_s, flip_sigmas, denoise, add_noise, seed, image_to_latent=None, latent=None, optional_sampler=None, optional_sigmas=None, prompt=None, extra_pnginfo=None, my_unique_id=None):
# 图生图转换
vae = pipe["vae"]
model = pipe["model"]
@@ -3334,6 +3389,7 @@ class samplerCustomSettings:
negative = pipe['negative']
batch_size = pipe["loader_settings"]["batch_size"] if "batch_size" in pipe["loader_settings"] else 1
_guider, sigmas = None, None
print(get_sd_version(model))
if image_to_latent is not None:
if guider == "IP2P+DualCFG":
positive, negative, latent = self.ip2p(pipe['positive'], pipe['negative'], vae, image_to_latent)
@@ -3384,6 +3440,8 @@ class samplerCustomSettings:
sigmas, = PolyexponentialScheduler().get_sigmas(steps, sigma_max, sigma_min, rho)
elif scheduler == 'sdturbo':
sigmas, = SDTurboScheduler().get_sigmas(model, steps, denoise)
elif scheduler == 'alignYourSteps':
pass
else:
sigmas, = BasicScheduler().get_sigmas(model, scheduler, steps, denoise)
@@ -3937,6 +3995,7 @@ class dynamicThresholdingFull:
#---------------------------------------------------------------采样器 开始----------------------------------------------------------------------
# 完整采样器
from .libs.chooser import ChooserMessage, ChooserCancelled
class samplerFull(LayerDiffuse):
def __init__(self):
@@ -3951,7 +4010,7 @@ class samplerFull(LayerDiffuse):
"sampler_name": (comfy.samplers.KSampler.SAMPLERS,),
"scheduler": (comfy.samplers.KSampler.SCHEDULERS,),
"denoise": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}),
"image_output": (["Hide", "Preview", "Save", "Hide/Save", "Sender", "Sender/Save"],),
"image_output": (["Hide", "Preview", "Preview&Choose", "Save", "Hide/Save", "Sender", "Sender/Save"],),
"link_id": ("INT", {"default": 0, "min": 0, "max": sys.maxsize, "step": 1}),
"save_prefix": ("STRING", {"default": "ComfyUI"}),
},
@@ -4121,6 +4180,26 @@ class samplerFull(LayerDiffuse):
del pipe
if image_output == 'Preview&Choose':
if my_unique_id not in ChooserMessage.stash:
ChooserMessage.stash[my_unique_id] = {}
my_stash = ChooserMessage.stash[my_unique_id]
PromptServer.instance.send_sync("easyuse-image-choose", {"id": my_unique_id, "urls": results})
# wait for selection
try:
selections = ChooserMessage.waitForMessage(my_unique_id, asList=True)
new_images = [new_images[x] for x in selections if x >= 0] if len(selections) > 1 else [0]
samp_images = [samp_images[x] for x in selections if x >= 0] if len(selections) > 1 else [0]
except ChooserCancelled:
raise comfy.model_management.InterruptProcessingException()
new_pipe['images'] = new_images
new_pipe['samp_images'] = samp_images
return {"ui": {"images": results},
"result": sampler.get_output(new_pipe,)}
if image_output in ("Hide", "Hide/Save"):
return {"ui": {},
"result": sampler.get_output(new_pipe,)}
@@ -4272,7 +4351,7 @@ class samplerSimple:
def INPUT_TYPES(cls):
return {"required":
{"pipe": ("PIPE_LINE",),
"image_output": (["Hide", "Preview", "Save", "Hide/Save", "Sender", "Sender/Save"],{"default": "Preview"}),
"image_output": (["Hide", "Preview", "Preview&Choose", "Save", "Hide/Save", "Sender", "Sender/Save"],{"default": "Preview"}),
"link_id": ("INT", {"default": 0, "min": 0, "max": sys.maxsize, "step": 1}),
"save_prefix": ("STRING", {"default": "ComfyUI"}),
},
@@ -6946,6 +7025,7 @@ NODE_CLASS_MAPPINGS = {
"easy ipadapterStyleComposition": ipadapterStyleComposition,
"easy instantIDApply": instantIDApply,
"easy instantIDApplyADV": instantIDApplyAdvanced,
"easy styleAlignedBatchAlign": styleAlignedBatchAlign,
# Inpaint 内补
"easy fooocusInpaintLoader": fooocusInpaintLoader,
# latent 潜空间
@@ -7009,6 +7089,9 @@ NODE_CLASS_MAPPINGS = {
"dynamicThresholdingFull": dynamicThresholdingFull,
# api 相关
"easy stableDiffusion3API": stableDiffusion3API,
# utils
"easy ckptNames": setCkptName,
"easy controlnetNames": setControlName,
}
NODE_DISPLAY_NAME_MAPPINGS = {
@@ -7048,6 +7131,7 @@ NODE_DISPLAY_NAME_MAPPINGS = {
"easy ipadapterApplyFromParams": "Easy Apply IPAdapter (From Params)",
"easy instantIDApply": "Easy Apply InstantID",
"easy instantIDApplyADV": "Easy Apply InstantID (Advanced)",
"easy styleAlignedBatchAlign": "Easy Apply StyleAlign",
# Inpaint 内补
"easy fooocusInpaintLoader": "Load Fooocus Inpaint",
# latent 潜空间
@@ -7111,4 +7195,7 @@ NODE_DISPLAY_NAME_MAPPINGS = {
"dynamicThresholdingFull": "DynamicThresholdingFull",
# api 相关
"easy stableDiffusion3API": "Stable Diffusion 3 (API)",
}
# utils
"easy ckptNames": "Ckpt Names",
"easy controlnetNames": "ControlNet Names",
}
+43 -2
View File
@@ -15,6 +15,25 @@ from .libs.chooser import ChooserMessage, ChooserCancelled
from .config import REMBG_DIR, REMBG_MODELS, HUMANPARSING_MODELS, MEDIAPIPE_MODELS, MEDIAPIPE_DIR
# 图像数量
class imageCount:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"images": ("IMAGE",),
}
}
CATEGORY = "EasyUse/Image"
RETURN_TYPES = ("INT",)
RETURN_NAMES = ("count",)
FUNCTION = "get_count"
def get_count(self, images):
return (images.size(0),)
# 图像裁切
class imageInsetCrop:
@@ -519,6 +538,24 @@ class imageSplitList:
new_images[1].append(img)
return new_images
class imagesSplitImage:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"images": ("IMAGE",),
}
}
RETURN_TYPES = ("IMAGE", "IMAGE", "IMAGE", "IMAGE", "IMAGE")
RETURN_NAMES = ("image1", "image2", "image3", "image4", "image5")
FUNCTION = "split"
CATEGORY = "EasyUse/Image"
def split(self, images,):
new_images = torch.chunk(images, len(images), dim=0)
return new_images
# 图片背景移除
from .briaai.rembg import BriaRMBG, preprocess_image, postprocess_image
from .libs.utils import get_local_filepath, easySave, install_package
@@ -901,7 +938,7 @@ class loadImageBase64:
RETURN_TYPES = ("IMAGE", "MASK")
OUTPUT_NODE = True
FUNCTION = "load_image"
CATEGORY = "image"
CATEGORY = "EasyUse/Image/LoadImage"
def convert_color(self, image,):
if len(image.shape) > 2 and image.shape[2] >= 4:
@@ -1005,6 +1042,7 @@ class poseEditor:
NODE_CLASS_MAPPINGS = {
"easy imageInsetCrop": imageInsetCrop,
"easy imageCount": imageCount,
"easy imageSize": imageSize,
"easy imageSizeBySide": imageSizeBySide,
"easy imageSizeByLongerSide": imageSizeByLongerSide,
@@ -1015,6 +1053,7 @@ NODE_CLASS_MAPPINGS = {
"easy imageRatio": imageRatio,
"easy imageToMask": imageToMask,
"easy imageSplitList": imageSplitList,
"easy imagesSplitImage": imagesSplitImage,
"easy imageSave": imageSaveSimple,
"easy imageRemBg": imageRemBg,
"easy imageChooser": imageChooser,
@@ -1029,6 +1068,7 @@ NODE_CLASS_MAPPINGS = {
NODE_DISPLAY_NAME_MAPPINGS = {
"easy imageInsetCrop": "ImageInsetCrop",
"easy imageCount": "ImageCount",
"easy imageSize": "ImageSize",
"easy imageSizeBySide": "ImageSize (Side)",
"easy imageSizeByLongerSide": "ImageSize (LongerSide)",
@@ -1040,13 +1080,14 @@ NODE_DISPLAY_NAME_MAPPINGS = {
"easy imageToMask": "ImageToMask",
"easy imageHSVMask": "ImageHSVMask",
"easy imageSplitList": "imageSplitList",
"easy imagesSplitImage": "imagesSplitImage",
"easy imageSave": "SaveImage (Simple)",
"easy imageRemBg": "Image Remove Bg",
"easy imageChooser": "Image Chooser",
"easy imageColorMatch": "Image Color Match",
"easy imageInterrogator": "Image To Prompt",
"easy joinImageBatch": "JoinImageBatch",
"easy loadImageBase64": "LoadImage (Base64)",
"easy loadImageBase64": "Load Image (Base64)",
"easy imageToBase64": "Image To Base64",
"easy humanSegmentation": "Human Segmentation",
"easy poseEditor": "PoseEditor",
+14 -15
View File
@@ -111,27 +111,26 @@ class CI_Inference:
def image_to_prompt(self, image, mode, model_name='ViT-L-14/openai', low_vram=False):
try:
install_package("clip_interrogator", "0.6.0")
from clip_interrogator import Config, Interrogator
global Config, Interrogator
except:
install_package("clip_interrogator", "0.6.0")
from clip_interrogator import Config, Interrogator
pbar = comfy.utils.ProgressBar(len(image))
pbar = comfy.utils.ProgressBar(len(image))
self._load_model(model_name, low_vram)
prompt = []
for i in range(len(image)):
im = image[i]
self._load_model(model_name, low_vram)
prompt = []
for i in range(len(image)):
im = image[i]
im = tensor2pil(im)
im = im.convert('RGB')
im = tensor2pil(im)
im = im.convert('RGB')
_prompt = self._interrogate(im, mode)
pbar.update(1)
prompt.append(_prompt)
_prompt = self._interrogate(im, mode)
pbar.update(1)
prompt.append(_prompt)
return prompt
except Exception as e:
print(e)
return [""]
return prompt
ci = CI_Inference()
+148
View File
@@ -0,0 +1,148 @@
import torch
import torch.nn as nn
from comfy.model_patcher import ModelPatcher
from typing import Union
T = torch.Tensor
def exists(val):
return val is not None
def default(val, d):
if exists(val):
return val
return d
class StyleAlignedArgs:
def __init__(self, share_attn: str) -> None:
self.adain_keys = "k" in share_attn
self.adain_values = "v" in share_attn
self.adain_queries = "q" in share_attn
share_attention: bool = True
adain_queries: bool = True
adain_keys: bool = True
adain_values: bool = True
def expand_first(
feat: T,
scale=1.0,
) -> T:
"""
Expand the first element so it has the same shape as the rest of the batch.
"""
b = feat.shape[0]
feat_style = torch.stack((feat[0], feat[b // 2])).unsqueeze(1)
if scale == 1:
feat_style = feat_style.expand(2, b // 2, *feat.shape[1:])
else:
feat_style = feat_style.repeat(1, b // 2, 1, 1, 1)
feat_style = torch.cat([feat_style[:, :1], scale * feat_style[:, 1:]], dim=1)
return feat_style.reshape(*feat.shape)
def concat_first(feat: T, dim=2, scale=1.0) -> T:
"""
concat the the feature and the style feature expanded above
"""
feat_style = expand_first(feat, scale=scale)
return torch.cat((feat, feat_style), dim=dim)
def calc_mean_std(feat, eps: float = 1e-5) -> "tuple[T, T]":
feat_std = (feat.var(dim=-2, keepdims=True) + eps).sqrt()
feat_mean = feat.mean(dim=-2, keepdims=True)
return feat_mean, feat_std
def adain(feat: T) -> T:
feat_mean, feat_std = calc_mean_std(feat)
feat_style_mean = expand_first(feat_mean)
feat_style_std = expand_first(feat_std)
feat = (feat - feat_mean) / feat_std
feat = feat * feat_style_std + feat_style_mean
return feat
class SharedAttentionProcessor:
def __init__(self, args: StyleAlignedArgs, scale: float):
self.args = args
self.scale = scale
def __call__(self, q, k, v, extra_options):
if self.args.adain_queries:
q = adain(q)
if self.args.adain_keys:
k = adain(k)
if self.args.adain_values:
v = adain(v)
if self.args.share_attention:
k = concat_first(k, -2, scale=self.scale)
v = concat_first(v, -2)
return q, k, v
def get_norm_layers(
layer: nn.Module,
norm_layers_: "dict[str, list[Union[nn.GroupNorm, nn.LayerNorm]]]",
share_layer_norm: bool,
share_group_norm: bool,
):
if isinstance(layer, nn.LayerNorm) and share_layer_norm:
norm_layers_["layer"].append(layer)
if isinstance(layer, nn.GroupNorm) and share_group_norm:
norm_layers_["group"].append(layer)
else:
for child_layer in layer.children():
get_norm_layers(
child_layer, norm_layers_, share_layer_norm, share_group_norm
)
def register_norm_forward(
norm_layer: Union[nn.GroupNorm, nn.LayerNorm],
) -> Union[nn.GroupNorm, nn.LayerNorm]:
if not hasattr(norm_layer, "orig_forward"):
setattr(norm_layer, "orig_forward", norm_layer.forward)
orig_forward = norm_layer.orig_forward
def forward_(hidden_states: T) -> T:
n = hidden_states.shape[-2]
hidden_states = concat_first(hidden_states, dim=-2)
hidden_states = orig_forward(hidden_states) # type: ignore
return hidden_states[..., :n, :]
norm_layer.forward = forward_ # type: ignore
return norm_layer
def register_shared_norm(
model: ModelPatcher,
share_group_norm: bool = True,
share_layer_norm: bool = True,
):
norm_layers = {"group": [], "layer": []}
get_norm_layers(model.model, norm_layers, share_layer_norm, share_group_norm)
print(
f"Patching {len(norm_layers['group'])} group norms, {len(norm_layers['layer'])} layer norms."
)
return [register_norm_forward(layer) for layer in norm_layers["group"]] + [
register_norm_forward(layer) for layer in norm_layers["layer"]
]
SHARE_NORM_OPTIONS = ["both", "group", "layer", "disabled"]
SHARE_ATTN_OPTIONS = ["q+k", "q+k+v", "disabled"]
def styleAlignBatch(model, share_norm, share_attn, scale=1.0):
m = model.clone()
share_group_norm = share_norm in ["group", "both"]
share_layer_norm = share_norm in ["layer", "both"]
register_shared_norm(model, share_group_norm, share_layer_norm)
args = StyleAlignedArgs(share_attn)
m.set_model_attn1_patch(SharedAttentionProcessor(args, scale))
return m
+6 -1
View File
@@ -98,12 +98,17 @@ import comfy.supported_models_base
def get_sd_version(model):
base: BaseModel = model.model
model_config: comfy.supported_models.supported_models_base.BASE = base.model_config
print(model_config)
if isinstance(model_config, comfy.supported_models.SDXL):
return 'sdxl'
elif isinstance(
model_config, (comfy.supported_models.SD15, comfy.supported_models.SD20)
):
return 'sd15'
elif isinstance(
model_config, (comfy.supported_models.SVD_img2vid)
):
return 'svd'
else:
return 'unknown'
@@ -231,7 +236,7 @@ def easySave(images, filename_prefix, output_type, prompt=None, extra_pnginfo=No
from nodes import PreviewImage, SaveImage
if output_type == "Hide":
return list()
if output_type == "Preview":
if output_type in ["Preview", "Preview&Choose"]:
filename_prefix = 'easyPreview'
results = PreviewImage().save_images(images, filename_prefix, prompt, extra_pnginfo)
return results['ui']['images']
+29
View File
@@ -277,6 +277,30 @@ class imageSwitch:
else:
return (image_b, )
class textSwitch:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"input": ("INT", {"default": 1, "min": 1, "max": 2}),
},
"optional": {
"text1": ("STRING", {"forceInput": True}),
"text2": ("STRING", {"forceInput": True}),
}
}
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ("STRING",)
CATEGORY = "EasyUse/Logic/Switch"
FUNCTION = "switch"
def switch(self, input, text1=None, text2=None,):
if input == 1:
return (text1,)
else:
return (text2,)
# ---------------------------------------------------------------运算 开始----------------------------------------------------------------------#
COMPARE_FUNCTIONS = {
@@ -549,6 +573,9 @@ class clearCacheAll:
remove_cache('*')
return ()
NODE_CLASS_MAPPINGS = {
"easy string": String,
"easy int": Int,
@@ -558,6 +585,7 @@ NODE_CLASS_MAPPINGS = {
"easy boolean": Boolean,
"easy compare": Compare,
"easy imageSwitch": imageSwitch,
"easy textSwitch": textSwitch,
"easy if": If,
"easy isSDXL": isSDXL,
"easy xyAny": xyAny,
@@ -577,6 +605,7 @@ NODE_DISPLAY_NAME_MAPPINGS = {
"easy boolean": "Boolean",
"easy compare": "Compare",
"easy imageSwitch": "Image Switch",
"easy textSwitch": "Text Switch",
"easy if": "If",
"easy isSDXL": "Is SDXL",
"easy xyAny": "XYAny",
+30
View File
@@ -0,0 +1,30 @@
.easyuse-chooser-dialog{
max-width: 600px;
}
.easyuse-chooser-dialog-title{
font-size: 18px;
font-weight: 700;
text-align: center;
color:var(--input-text);
margin:0;
}
.easyuse-chooser-dialog-images{
margin-top:10px;
display: flex;
flex-wrap: wrap;
width: 100%;
box-sizing: border-box;
}
.easyuse-chooser-dialog-images img{
width: 50%;
height: auto;
cursor: pointer;
box-sizing: border-box;
filter:brightness(80%);
}
.easyuse-chooser-dialog-images img:hover{
filter:brightness(100%);
}
.easyuse-chooser-dialog-images img.selected{
border: 4px solid var(--success-color);
}
+2 -1
View File
@@ -5,4 +5,5 @@
@import "contextmenu.css";
@import "modelinfo.css";
@import "toast.css";
@import "account.css";
@import "account.css";
@import "chooser.css";
+3
View File
@@ -59,6 +59,9 @@ const zhCN = {
"APIKEY is not Empty": "APIKEY 不能为空",
"Add Account": "添加账号",
"Getting Your APIKEY": "获取您的APIKEY",
// choosers
"Choose Selected Images": "选择选中的图片",
"Choose images to continue": "选择图片以继续",
// seg
"Background": "背景",
"Hat": "帽子",
+1 -1
View File
@@ -56,7 +56,7 @@ function widgetLogic(node, widget) {
}else {
toggleWidget(node, findWidgetByName(node, 'link_id'))
}
if (widget.value === 'Hide' || widget.value === 'Preview' || widget.value == 'PreviewChooser' || widget.value === 'Sender') {
if (widget.value === 'Hide' || widget.value === 'Preview' || widget.value == 'Preview&Choose' || widget.value === 'Sender') {
toggleWidget(node, findWidgetByName(node, 'save_prefix'))
toggleWidget(node, findWidgetByName(node, 'output_path'))
toggleWidget(node, findWidgetByName(node, 'embed_workflow'))
+14 -2
View File
@@ -273,11 +273,11 @@ const addMenu = (content, type, nodes_include, nodeType, has_submenu=true) => {
has_submenu: has_submenu,
callback: (value, options, e, menu, node) => showSwapMenu(value, options, e, menu, node, type, nodes_include)
})
if(type == 'loaders'){
if(type == 'loaders') {
options.unshift({
content: $t("💎 View Lora Info..."),
callback: (value, options, e, menu, node) => {
const widget = node.widgets.find(cate=> cate.name == 'lora_name')
const widget = node.widgets.find(cate => cate.name == 'lora_name')
let name = widget.value;
if (!name || name == 'None') return
new LoraInfoDialog(name).show('loras', name);
@@ -522,7 +522,19 @@ app.registerExtension({
}
}
})
// ckptNames
if(nodeData.name == 'easy ckptNames'){
options.unshift({
content: $t("💎 View Checkpoint Info..."),
callback: (value, options, e, menu, node) => {
let name = node.widgets[0].value;
if (!name || name == 'None') return
new CheckpointInfoDialog(name).show('checkpoints', name);
}
})
}
})
// Swap提示词
if (positive_prompt.includes(nodeData.name)) {
addMenu("↪️ Swap EasyPrompt", 'positive_prompt', positive_prompt, nodeType)
+1
View File
@@ -747,6 +747,7 @@ const NODE_COLORS = {
"easy promptReplace":"cyan",
"easy XYInputs: Seeds++ Batch": customXYLink,
"easy XYInputs: ModelMergeBlocks": customXYLink,
'easy textSwitch': "pale_blue"
}
function setNodeColors(node, theme) {
+64 -4
View File
@@ -1,14 +1,70 @@
import { app } from "/scripts/app.js";
import { api } from "/scripts/api.js";
import { ComfyDialog, $el } from "/scripts/ui.js";
import { restart_from_here } from "./prompt.js";
import { FlowState } from "./state.js";
import { send_cancel, send_message, send_onstart, skip_next_restart_message } from "./messaging.js";
import { display_preview_images, additionalDrawBackground, click_is_in_image } from "./preview.js";
import {toggleWidget} from "../common/utils.js";
import {$t} from "../common/i18n.js";
function progressButtonPressed() {
const node = app.graph._nodes_by_id[this.node_id];
class chooserImageDialog extends ComfyDialog {
constructor() {
super();
this.node = null
this.select_index = []
this.dialog_div = null
}
show(image,node){
this.select_index = []
this.node = node
const images_div = image.map((img, index) => {
const imgEl = $el('img', {
src: img.src,
onclick: _ => {
if(this.select_index.includes(index)){
this.select_index = this.select_index.filter(i => i !== index)
imgEl.classList.remove('selected')
} else {
this.select_index.push(index)
imgEl.classList.add('selected')
}
if (node.selected.has(index)) node.selected.delete(index);
else node.selected.add(index);
}
})
return imgEl
})
super.show($el('div.easyuse-chooser-dialog',[
$el('h5.easyuse-chooser-dialog-title', $t('Choose images to continue')),
$el('div.easyuse-chooser-dialog-images',images_div)
]))
}
createButtons() {
const btns = super.createButtons();
btns[0].onclick = _ => {
cancelButtonPressed()
super.close()
}
btns.unshift($el('button', {
type: 'button',
textContent: $t('Choose Selected Images'),
onclick: _ => {
progressButtonPressed(this.node)
super.close()
}
}))
return btns
}
}
function progressButtonPressed(_node) {
const node = _node ? _node : app.graph._nodes_by_id[this.node_id];
if (node) {
if (FlowState.paused()) {
send_message(node.id, [...node.selected, -1, ...node.anti_selected]);
@@ -29,7 +85,11 @@ app.registerExtension({
},
setup(app) {
function easyuseImageChooser(event) {
display_preview_images(event);
const {node,image,isKSampler} = display_preview_images(event);
if(isKSampler) {
const dialog = new chooserImageDialog();
dialog.show(image,node)
}
}
api.addEventListener("easyuse-image-choose", easyuseImageChooser);
+5 -1
View File
@@ -1,11 +1,14 @@
import { app } from "/scripts/app.js";
const kSampler = ['easy kSampler', 'easy kSamplerTiled', 'easy fullkSampler']
function display_preview_images(event) {
const node = app.graph._nodes_by_id[event.detail.id];
if (node) {
node.selected = new Set();
node.anti_selected = new Set();
showImages(node, event.detail.urls);
const image = showImages(node, event.detail.urls);
return {node,image,isKSampler:kSampler.includes(node.type)}
} else {
console.log(`Image Chooser Preview - failed to find ${event.detail.id}`)
}
@@ -20,6 +23,7 @@ function showImages(node, urls) {
img.src = `/view?filename=${encodeURIComponent(u.filename)}&type=temp&subfolder=${app.getPreviewFormatParam()}`
})
node.setSizeForImage?.();
return node.imgs
}
function drawRect(node, s, ctx) {