Compare commits

..
14 Commits
19 changed files with 131 additions and 57 deletions
+13
View File
@@ -52,6 +52,19 @@ git clone https://github.com/yolain/ComfyUI-Easy-Use
## 📜 更新日志
**v1.3.4**
- 修复 `easy seedList` 最大值 #879
- 为xyplot添加controlnet input #877
- 为 `easy indexAnything` 支持 `反向索引`
**v1.3.3**
- 删除CSS类名称`gird-cols-1` #859
- 修复锁定种子在 `easy promptAwait` 中不起作用
- 重命名节点图
- 修复`easy ImageChooser`输出错误类型 #845
**v1.3.2**
- 改造 `easy imageChooser` 节点以兼容 frontend>=v1.24.2, 解决方案参考自 [Comfyui_LG_Tools](https://github.com/LAOGOU-666/Comfyui_LG_Tools)
+13
View File
@@ -47,6 +47,19 @@ Double-click install.bat to install the required dependencies
## 📜 Changelog
**v1.3.4**
- Fix `easy seedList` max_num #879
- Add controlnet input to xyplot #877
- Support `nagative indexing` for `easy indexAnything`
**v1.3.3**
- Removed the definition of the CSS class name gird-cols-1 #859
- Fix lock seed not working in `easy promptAwait`
- Rename the nodes map
- Fix `easy imageChooser` output error type #845
**v1.3.2**
- Revamp `easy imageChooser` node to adapt frontend>=v1.24.2, solution referenced from [Comfyui_LG_Tools](https://github.com/LAOGOU-666/Comfyui_LG_Tools)
+1 -1
View File
@@ -1,4 +1,4 @@
__version__ = "1.3.2"
__version__ = "1.3.4"
import yaml
import json
+1 -1
View File
@@ -68,7 +68,7 @@
"name": "样式选择器显示类型",
"tooltip": "样式选择器显示类型,如果设置为“网格”,则显示为网格,如果设置为“列表”,则显示为列表",
"options": {
"Gird": "网格",
"Grid": "网格",
"List": "列表"
}
}
+15 -10
View File
@@ -1,7 +1,11 @@
from threading import Event
import torch
from server import PromptServer
from aiohttp import web
from comfy import model_management as mm
from comfy_execution.graph import ExecutionBlocker
import time
class ChooserCancelled(Exception):
@@ -25,7 +29,7 @@ def cleanup_session_data(node_id):
def wait_for_chooser(id, images, mode, period=0.1):
try:
node_data = get_chooser_cache()
images = [images[i:i + 1, ...] for i in range(images.shape[0])]
if mode == "Keep Last Selection":
if id in node_data and "last_selection" in node_data[id]:
last_selection = node_data[id]["last_selection"]
@@ -41,7 +45,9 @@ def wait_for_chooser(id, images, mode, period=0.1):
pass
cleanup_session_data(id)
indices_str = ','.join(str(i) for i in valid_indices)
return {"result": ([images[idx] for idx in valid_indices],)}
images = [images[idx] for idx in valid_indices]
images = torch.cat(images, dim=0)
return {"result": (images,)}
if id in node_data:
del node_data[id]
@@ -78,19 +84,18 @@ def wait_for_chooser(id, images, mode, period=0.1):
if id not in node_data:
node_data[id] = {}
node_data[id]["last_selection"] = valid_indices
cleanup_session_data(id)
indices_str = ','.join(str(i) for i in valid_indices)
return {"result": (selected_images, indices_str)}
selected_images = torch.cat(selected_images, dim=0)
return {"result": (selected_images,)}
else:
cleanup_session_data(id)
return {"result": ([images[0]] if len(images) > 0 else [], "0" if len(images) > 0 else "")}
return {"result": (images[0] if len(images) > 0 else ExecutionBlocker(None),)}
else:
cleanup_session_data(id)
return {
"result": ([images[0]] if len(images) > 0 else [],)}
"result": (images[0] if len(images) > 0 else ExecutionBlocker(None),)}
else:
return {"result": ([images[0]] if len(images) > 0 else [],)}
return {"result": (images[0] if len(images) > 0 else ExecutionBlocker(None),)}
except ChooserCancelled:
raise mm.InterruptProcessingException()
@@ -99,9 +104,9 @@ def wait_for_chooser(id, images, mode, period=0.1):
if id in node_data:
cleanup_session_data(id)
if 'image_list' in locals() and len(images) > 0:
return {"result": ([images[0]])}
return {"result": (images[0])}
else:
return {"result": ([])}
return {"result": (ExecutionBlocker(None),)}
@PromptServer.instance.routes.post('/easyuse/image_chooser_message')
+2 -1
View File
@@ -430,7 +430,8 @@ class easyXYPlot():
strength = item[2]
start_percent = item[3]
end_percent = item[4]
positive, negative = easyControlnet().apply(control_net_name, image, positive, negative, strength, start_percent, end_percent, None, 1)
provided_control_net = item[5] if len(item) > 5 else None
positive, negative = easyControlnet().apply(control_net_name, image, positive, negative, strength, start_percent, end_percent, provided_control_net, 1)
# Flux guidance
if self.x_type == "Flux Guidance" or self.y_type == "Flux Guidance":
positive = plot_image_vars["positive_cond"] if "positive" in plot_image_vars else None
+9 -2
View File
@@ -1,5 +1,6 @@
import re
import torch
import folder_paths
import comfy.utils, comfy.sample, comfy.samplers, comfy.controlnet, comfy.model_base, comfy.model_management, comfy.sampler_helpers, comfy.supported_models
from comfy_extras.nodes_compositing import JoinImageWithAlpha
from comfy.clip_vision import load as load_clip_vision
@@ -180,7 +181,10 @@ class icLightApply:
image = self.removebg(image)
else:
mask = torch.full((1, height, width), 1.0, dtype=torch.float32, device="cpu")
image, = JoinImageWithAlpha().join_image_with_alpha(image, mask)
try:
image, = JoinImageWithAlpha().execute(image, mask)
except:
image, = JoinImageWithAlpha().join_image_with_alpha(image, mask)
iclight = ICLight()
if mode == 'Foreground':
@@ -190,7 +194,10 @@ class icLightApply:
if source not in ['Use Background Image', 'Use Flipped Background Image']:
_, height, width, _ = lighting_image.shape
mask = torch.full((1, height, width), 1.0, dtype=torch.float32, device="cpu")
lighting_image, = JoinImageWithAlpha().join_image_with_alpha(lighting_image, mask)
try:
lighting_image, = JoinImageWithAlpha().execute(lighting_image, mask)
except:
lighting_image, = JoinImageWithAlpha().join_image_with_alpha(lighting_image, mask)
if batch_size < 2:
image = self.batch(image, lighting_image)
else:
+7 -3
View File
@@ -1043,8 +1043,8 @@ class imageChooser(PreviewImage):
id = my_unique_id[0]
id = id.split('.')[len(id.split('.')) - 1] if "." in id else id
if (kwargs['images'] is None):
return (None,)
if (kwargs.get('images') is None):
return (torch.zeros(1, 1, 1, 3),)
images_in = torch.cat(kwargs.pop('images'))
for x in kwargs: kwargs[x] = kwargs[x][0]
@@ -1396,7 +1396,11 @@ class humanSegmentation:
alpha = 1.0 - mask
output_image, = JoinImageWithAlpha().join_image_with_alpha(image, alpha)
try:
output_image, = JoinImageWithAlpha().execute(image, alpha)
except:
output_image, = JoinImageWithAlpha().join_image_with_alpha(image, alpha)
elif method == "human_parts (deeplabv3p)":
if method in cache:
+4 -1
View File
@@ -331,7 +331,10 @@ class applyInpaint:
new_pipe = self.inpaint_model_conditioning(new_pipe, image, vae, mask, grow_mask_by, noise_mask=noise_mask)
cls = ALL_NODE_CLASS_MAPPINGS['DifferentialDiffusion']
if cls is not None:
model, = cls().apply(new_pipe['model'])
try:
model, = cls().execute(new_pipe['model'])
except Exception:
model, = cls().apply(new_pipe['model'])
new_pipe['model'] = model
else:
raise Exception("Differential Diffusion not found,please update comfyui")
+22 -5
View File
@@ -908,6 +908,10 @@ COMPARE_FUNCTIONS = {
"a > b": lambda a, b: a > b,
"a <= b": lambda a, b: a <= b,
"a >= b": lambda a, b: a >= b,
"a > 0": lambda a, b: a > 0,
"a <= 0": lambda a, b: a <= 0,
"b > 0": lambda a, b: b > 0,
"b <= 0": lambda a, b: b <= 0,
}
@@ -917,7 +921,7 @@ class Compare:
def INPUT_TYPES(s):
compare_functions = list(COMPARE_FUNCTIONS.keys())
return {
"required": {
"optional": {
"a": (any_type, {"default": 0}),
"b": (any_type, {"default": 0}),
"comparison": (compare_functions, {"default": "a == b"}),
@@ -929,7 +933,7 @@ class Compare:
FUNCTION = "compare"
CATEGORY = "EasyUse/Logic/Math"
def compare(self, a, b, comparison):
def compare(self, a=0, b=0, comparison="a == b"):
return (COMPARE_FUNCTIONS[comparison](a, b),)
@@ -1209,7 +1213,7 @@ class indexAnything:
return {
"required": {
"any": (any_type, {}),
"index": ("INT", {"default": 0, "min": 0, "max": 1000000, "step": 1}),
"index": ("INT", {"default": 0, "min": -1000000, "max": 1000000, "step": 1}),
},
"hidden":{
"prompt": "PROMPT",
@@ -1235,14 +1239,26 @@ class indexAnything:
node_class = ALL_NODE_CLASS_MAPPINGS[class_type]
output_is_list = node_class.OUTPUT_IS_LIST[slot] if hasattr(node_class, 'OUTPUT_IS_LIST') else False
def normalize_index(index, length):
"""标准化索引,处理负索引并确保在有效范围内"""
if index < 0:
index = length + index
if index < 0:
index = 0
return min(max(0, index), length - 1)
if output_is_list or len(any) > 1:
index = normalize_index(index, len(any))
return (any[index],)
elif isinstance(any[0], torch.Tensor):
batch_index = min(any[0].shape[0] - 1, index)
index = normalize_index(index, any[0].shape[0])
s = any[0][index:index + 1].clone()
return (s,)
else:
return (any[0][index],)
if hasattr(any[0], '__len__') and len(any[0]) > 0:
index = normalize_index(index, len(any[0]))
return (any[0][index],)
return (any[0],)
class batchAnything:
@@ -1790,3 +1806,4 @@ NODE_DISPLAY_NAME_MAPPINGS = {
"easy saveText": "Save Text",
"easy sleep": "Sleep",
}
+23 -15
View File
@@ -1,6 +1,7 @@
import sys, re, time
import torch
import comfy.utils, comfy.sample, comfy.samplers, comfy.controlnet, comfy.model_base, comfy.model_management, comfy.sampler_helpers, comfy.supported_models
import folder_paths
from comfy.model_patcher import ModelPatcher
from comfy_extras.nodes_mask import GrowMask
import comfy_extras.nodes_custom_sampler as custom_samplers
@@ -118,7 +119,14 @@ class samplerFull:
def get_custom_cls(self, sampler_name):
try:
cls = custom_samplers.__dict__[sampler_name]
return cls()
cls = cls()
if hasattr(cls, "get_sigmas"):
cls.execute = cls.get_sigmas
elif hasattr(cls, "get_guider"):
cls.execute = cls.get_guider
elif hasattr(cls, "get_sampler"):
cls.execute = cls.get_sampler
return cls
except:
raise Exception(f"Custom sampler {sampler_name} not found, Please updated your ComfyUI")
@@ -156,15 +164,15 @@ class samplerFull:
sigmas = optional_sigmas
else:
if scheduler == 'vp':
sigmas, = self.get_custom_cls('VPScheduler').get_sigmas(steps, beta_d, beta_min, eps_s)
sigmas, = self.get_custom_cls('VPScheduler').execute(steps, beta_d, beta_min, eps_s)
elif scheduler == 'karrasADV':
sigmas, = self.get_custom_cls('KarrasScheduler').get_sigmas(steps, sigma_max, sigma_min, rho)
sigmas, = self.get_custom_cls('KarrasScheduler').execute(steps, sigma_max, sigma_min, rho)
elif scheduler == 'exponentialADV':
sigmas, = self.get_custom_cls('ExponentialScheduler').get_sigmas(steps, sigma_max, sigma_min)
sigmas, = self.get_custom_cls('ExponentialScheduler').execute(steps, sigma_max, sigma_min)
elif scheduler == 'polyExponential':
sigmas, = self.get_custom_cls('PolyexponentialScheduler').get_sigmas(steps, sigma_max, sigma_min, rho)
sigmas, = self.get_custom_cls('PolyexponentialScheduler').execute(steps, sigma_max, sigma_min, rho)
elif scheduler == 'sdturbo':
sigmas, = self.get_custom_cls('SDTurboScheduler').get_sigmas(model, steps, denoise)
sigmas, = self.get_custom_cls('SDTurboScheduler').execute(model, steps, denoise)
elif scheduler == 'alignYourSteps':
model_type = get_sd_version(model)
if model_type == 'unknown':
@@ -173,11 +181,11 @@ class samplerFull:
elif scheduler == 'gits':
sigmas, = gitsScheduler().get_sigmas(coeff, steps, denoise)
else:
sigmas, = self.get_custom_cls('BasicScheduler').get_sigmas(model, scheduler, steps, denoise)
sigmas, = self.get_custom_cls('BasicScheduler').execute(model, scheduler, steps, denoise)
# filp_sigmas
if flip_sigmas:
sigmas, = self.get_custom_cls('FlipSigmas').get_sigmas(sigmas)
sigmas, = self.get_custom_cls('FlipSigmas').execute(sigmas)
#######################################################################################
# brushnet
@@ -209,12 +217,12 @@ class samplerFull:
positive = c
if guider in ['CFG', 'IP2P+CFG']:
_guider, = self.get_custom_cls('CFGGuider').get_guider(model, positive, negative, cfg)
_guider, = self.get_custom_cls('CFGGuider').execute(model, positive, negative, cfg)
elif guider in ['DualCFG', 'IP2P+DualCFG']:
_guider, = self.get_custom_cls('DualCFGGuider').get_guider(model, positive, middle,
_guider, = self.get_custom_cls('DualCFGGuider').execute(model, positive, middle,
negative, cfg, cfg_negative)
else:
_guider, = self.get_custom_cls('BasicGuider').get_guider(model, positive)
_guider, = self.get_custom_cls('BasicGuider').execute(model, positive)
# sampler
if optional_sampler:
@@ -223,7 +231,7 @@ class samplerFull:
if sampler_name == 'inversed_euler':
_sampler, = self.get_inversed_euler_sampler()
else:
_sampler, = self.get_custom_cls('KSamplerSelect').get_sampler(sampler_name)
_sampler, = self.get_custom_cls('KSamplerSelect').execute(sampler_name)
return (_guider, _sampler, sigmas)
@@ -277,18 +285,18 @@ class samplerFull:
if width_downscale_factor > 1.75:
log_node_warn("Patch model unet add downscale...")
log_node_warn("Downscale factor:" + str(width_downscale_factor))
(samp_model,) = cls().patch(samp_model, downscale_options['block_number'], width_downscale_factor, 0, 0.35, True, "bicubic",
(samp_model,) = cls().execute(samp_model, downscale_options['block_number'], width_downscale_factor, 0, 0.35, True, "bicubic",
"bicubic")
elif height_downscale_factor > 1.25:
log_node_warn("Patch model unet add downscale....")
log_node_warn("Downscale factor:" + str(height_downscale_factor))
(samp_model,) = cls().patch(samp_model, downscale_options['block_number'], height_downscale_factor, 0, 0.35, True, "bicubic",
(samp_model,) = cls().execute(samp_model, downscale_options['block_number'], height_downscale_factor, 0, 0.35, True, "bicubic",
"bicubic")
else:
cls = ALL_NODE_CLASS_MAPPINGS['PatchModelAddDownscale']
log_node_warn("Patch model unet add downscale....")
log_node_warn("Downscale factor:" + str(downscale_options['downscale_factor']))
(samp_model,) = cls().patch(samp_model, downscale_options['block_number'], downscale_options['downscale_factor'], downscale_options['start_percent'], downscale_options['end_percent'], downscale_options['downscale_after_skip'], downscale_options['downscale_method'], downscale_options['upscale_method'])
(samp_model,) = cls().execute(samp_model, downscale_options['block_number'], downscale_options['downscale_factor'], downscale_options['start_percent'], downscale_options['end_percent'], downscale_options['downscale_after_skip'], downscale_options['downscale_method'], downscale_options['upscale_method'])
return samp_model
def process_sample_state(pipe, samp_model, samp_clip, samp_samples, samp_vae, samp_seed, samp_positive,
+1 -1
View File
@@ -27,7 +27,7 @@ class seedList:
return {
"required": {
"min_num": ("INT", {"default": 0, "min": 0, "max": MAX_SEED_NUM}),
"max_num": ("INT", {"default": MAX_SEED_NUM, "min": 0 }),
"max_num": ("INT", {"default": MAX_SEED_NUM, "max": MAX_SEED_NUM, "min": 0 }),
"method": (["random", "increment", "decrement"], {"default": "random"}),
"total": ("INT", {"default": 1, "min": 1, "max": 100000}),
"seed": ("INT", {"default": 0, "min": 0, "max": MAX_SEED_NUM,}),
+13 -10
View File
@@ -413,6 +413,9 @@ class XYplot_Control_Net:
"start_percent": ("FLOAT", {"default": 0.0, "min": 0.00, "max": 1.0, "step": 0.01}),
"end_percent": ("FLOAT", {"default": 1.0, "min": 0.00, "max": 1.0, "step": 0.01}),
},
"optional": {
"control_net": ("CONTROL_NET",),
},
}
RETURN_TYPES = ("X_Y",)
@@ -421,7 +424,7 @@ class XYplot_Control_Net:
CATEGORY = "EasyUse/XY Inputs"
def xy_value(self, control_net_name, image, target_parameter, batch_count, first_strength, last_strength, first_start_percent,
last_start_percent, first_end_percent, last_end_percent, strength, start_percent, end_percent):
last_start_percent, first_end_percent, last_end_percent, strength, start_percent, end_percent, control_net=None):
axis, = None,
@@ -430,38 +433,38 @@ class XYplot_Control_Net:
if target_parameter == "strength":
axis = "advanced: ControlNetStrength"
values.append([(control_net_name, image, first_strength, start_percent, end_percent)])
values.append([(control_net_name, image, first_strength, start_percent, end_percent, control_net)])
strength_increment = (last_strength - first_strength) / (batch_count - 1) if batch_count > 1 else 0
for i in range(1, batch_count - 1):
values.append([(control_net_name, image, first_strength + i * strength_increment, start_percent,
end_percent)])
end_percent, control_net)])
if batch_count > 1:
values.append([(control_net_name, image, last_strength, start_percent, end_percent)])
values.append([(control_net_name, image, last_strength, start_percent, end_percent, control_net)])
elif target_parameter == "start_percent":
axis = "advanced: ControlNetStart%"
percent_increment = (last_start_percent - first_start_percent) / (batch_count - 1) if batch_count > 1 else 0
values.append([(control_net_name, image, strength, first_start_percent, end_percent)])
values.append([(control_net_name, image, strength, first_start_percent, end_percent, control_net)])
for i in range(1, batch_count - 1):
values.append([(control_net_name, image, strength, first_start_percent + i * percent_increment,
end_percent)])
end_percent, control_net)])
# Always add the last start_percent if batch_count is more than 1.
if batch_count > 1:
values.append((control_net_name, image, strength, last_start_percent, end_percent))
values.append([(control_net_name, image, strength, last_start_percent, end_percent, control_net)])
elif target_parameter == "end_percent":
axis = "advanced: ControlNetEnd%"
percent_increment = (last_end_percent - first_end_percent) / (batch_count - 1) if batch_count > 1 else 0
values.append([(control_net_name, image, image, strength, start_percent, first_end_percent)])
values.append([(control_net_name, image, strength, start_percent, first_end_percent, control_net)])
for i in range(1, batch_count - 1):
values.append([(control_net_name, image, strength, start_percent,
first_end_percent + i * percent_increment)])
first_end_percent + i * percent_increment, control_net)])
if batch_count > 1:
values.append([(control_net_name, image, strength, start_percent, last_end_percent)])
values.append([(control_net_name, image, strength, start_percent, last_end_percent, control_net)])
return ({"axis": axis, "values": values},)
+2 -2
View File
@@ -1,9 +1,9 @@
[project]
name = "comfyui-easy-use"
description = "To enhance the usability of ComfyUI, optimizations and integrations have been implemented for several commonly used nodes."
version = "1.3.2"
version = "1.3.4"
license = { file = "LICENSE" }
dependencies = ["diffusers", "accelerate", "clip_interrogator>=0.6.0", "sentencepiece", "lark", "onnxruntime", "spandrel", "opencv-python", "matplotlib", "peft"]
dependencies = ["diffusers", "accelerate", "clip_interrogator>=0.6.0", "sentencepiece", "lark", "onnxruntime", "spandrel", "opencv-python-headless", "matplotlib", "peft"]
[project.urls]
Repository = "https://github.com/yolain/ComfyUI-Easy-Use"
+1 -1
View File
@@ -3,7 +3,7 @@ accelerate
clip_interrogator>=0.6.0
lark
onnxruntime
opencv-python
opencv-python-headless
sentencepiece
spandrel
matplotlib
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long