Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1616dd6602 | ||
|
|
282eedfea6 | ||
|
|
17b163e234 | ||
|
|
501d97bb5c | ||
|
|
de92038f88 | ||
|
|
530333d72d | ||
|
|
041f49540c | ||
|
|
71c7865d2d | ||
|
|
c7fbf05970 | ||
|
|
2986a01469 | ||
|
|
fa7c5d8b4d | ||
|
|
1d8db7510b | ||
|
|
7ef0612ce7 | ||
|
|
7ff4790493 | ||
|
|
640ef31625 | ||
|
|
e4ac947d96 | ||
|
|
d287e28e5c | ||
|
|
f33c17f762 | ||
|
|
e07b8cc7bf | ||
|
|
6abe07bb79 | ||
|
|
7fbd03bda7 | ||
|
|
9cc2ac02da | ||
|
|
2f2a3035a2 | ||
|
|
5d8f0a3b0a | ||
|
|
8aadd72494 | ||
|
|
fceec754a4 | ||
|
|
419b7c985c | ||
|
|
ce62fc73da | ||
|
|
4f31641da3 | ||
|
|
deec62ab76 | ||
|
|
5c8cdb58c7 |
@@ -515,7 +515,6 @@ git clone https://github.com/yolain/ComfyUI-Easy-Use
|
||||
**Comfyui-Easy-Use** 是一个 GPL 许可的开源项目。为了项目取得更好、可持续的发展,我希望能够获得更多的支持。 如果我的自定义节点为您的一天增添了价值,请考虑喝杯咖啡来进一步补充能量! 💖感谢您的支持,每一杯咖啡都是我创作的动力!
|
||||
|
||||
- [BiliBili充电](https://space.bilibili.com/1840885116)
|
||||
- [爱发电](https://afdian.com/a/yolain)
|
||||
- [Wechat/Alipay](https://github.com/user-attachments/assets/803469bd-ed6a-4fab-932d-50e5088a2d03)
|
||||
|
||||
感谢您的捐助,我将用这些费用来租用 GPU 或购买其他 GPT 服务,以便更好地调试和完善 ComfyUI-Easy-Use 功能
|
||||
|
||||
@@ -47,6 +47,12 @@ Double-click install.bat to install the required dependencies
|
||||
|
||||
## 📜 Changelog
|
||||
|
||||
**v1.3.1**
|
||||
|
||||
- Rewrite drawNodeWidget and fix the GroupNode preview issue.
|
||||
- Updated some features of XYPlot by [mekinney](https://github.com/mekinney)
|
||||
- Add `easy seedList` node (It's useful for in loops)
|
||||
|
||||
**v1.3.0**
|
||||
|
||||
- Set loop nodes maximum number of inputs and outputs to 20
|
||||
@@ -499,7 +505,6 @@ If my custom nodes has added value to your day, consider indulging in a coffee t
|
||||
💖You can support me in any of the following ways:
|
||||
|
||||
- [BiliBili](https://space.bilibili.com/1840885116)
|
||||
- [Afdian](https://afdian.com/a/yolain)
|
||||
- [Wechat / Alipay](https://github.com/user-attachments/assets/803469bd-ed6a-4fab-932d-50e5088a2d03)
|
||||
|
||||
## 🌟Stargazers
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
__version__ = "1.3.0"
|
||||
__version__ = "1.3.1"
|
||||
|
||||
import yaml
|
||||
import json
|
||||
|
||||
@@ -1756,6 +1756,35 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"easy seedList": {
|
||||
"display_name": "随机种列表",
|
||||
"description": "可用于for循环的随机数种子列表,通过与easy forLoopStart节点的索引与easy indexAny节点相连接可实现在循环中使用不同种子值进行采样",
|
||||
"inputs": {
|
||||
"min_num": {
|
||||
"name": "最小值"
|
||||
},
|
||||
"max_num": {
|
||||
"name": "最大值"
|
||||
},
|
||||
"method": {
|
||||
"name": "生成方式"
|
||||
},
|
||||
"total": {
|
||||
"name": "总量"
|
||||
},
|
||||
"seed": {
|
||||
"name": "列表序号"
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
"0": {
|
||||
"name": "随机种"
|
||||
},
|
||||
"1": {
|
||||
"name": "总量"
|
||||
}
|
||||
}
|
||||
},
|
||||
"easy globalSeed": {
|
||||
"display_name": "全局随机种",
|
||||
"inputs": {
|
||||
@@ -6365,7 +6394,7 @@
|
||||
"name": "高度"
|
||||
},
|
||||
"scale": {
|
||||
"name": "缩放洗漱"
|
||||
"name": "缩放系数"
|
||||
},
|
||||
"flip_w/h": {
|
||||
"name": "翻转宽高"
|
||||
@@ -6708,4 +6737,4 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+5
-1
@@ -351,7 +351,7 @@ class easyLoader:
|
||||
lora_path = None
|
||||
|
||||
if lora_path is not None:
|
||||
log_node_info("Load LORA",f"{lora_name}: {model_strength}, {clip_strength}, LBW={lbw}, A={lbw_a}, B={lbw_b}")
|
||||
log_node_info("Load LORA",f"{lora_name}: model={model_strength:.3f}, clip={clip_strength:.3f}, LBW={lbw}, A={lbw_a}, B={lbw_b}")
|
||||
if lbw:
|
||||
lbw = lora["lbw"]
|
||||
lbw_a = lora["lbw_a"]
|
||||
@@ -432,10 +432,13 @@ class easyLoader:
|
||||
clip_vision = None
|
||||
lora_stack = []
|
||||
|
||||
# Check for model override
|
||||
can_load_lora = True
|
||||
# 判断是否存在 模型或Lora叠加xyplot, 若存在优先缓存第一个模型
|
||||
# Determine whether there is a model or Lora overlapping xyplot, and if there is, prioritize caching the first model.
|
||||
xy_model_id = next((x for x in prompt if str(prompt[x]["class_type"]) in ["easy XYInputs: ModelMergeBlocks",
|
||||
"easy XYInputs: Checkpoint"]), None)
|
||||
# This will find nodes that aren't actively connected to anything, and skip loading lora's for them.
|
||||
xy_lora_id = next((x for x in prompt if str(prompt[x]["class_type"]) == "easy XYInputs: Lora"), None)
|
||||
if xy_lora_id is not None:
|
||||
can_load_lora = False
|
||||
@@ -461,6 +464,7 @@ class easyLoader:
|
||||
|
||||
if optional_lora_stack is not None and can_load_lora:
|
||||
for lora in optional_lora_stack:
|
||||
# This is a subtle bit of code because it uses the model created by the last call, and passes it to the next call.
|
||||
lora = {"lora_name": lora[0], "model": model, "clip": clip, "model_strength": lora[1],
|
||||
"clip_strength": lora[2]}
|
||||
model, clip = self.load_lora(lora)
|
||||
|
||||
+90
-15
@@ -8,6 +8,7 @@ from .log import log_node_warn
|
||||
from ..modules.layer_diffuse import LayerDiffuse
|
||||
from ..config import RESOURCES_DIR
|
||||
from nodes import CLIPTextEncode
|
||||
import pprint
|
||||
try:
|
||||
from comfy_extras.nodes_flux import FluxGuidance
|
||||
except:
|
||||
@@ -52,7 +53,7 @@ class easyXYPlot():
|
||||
|
||||
plot_image_vars[value_type] = value
|
||||
if value_type in ["seed", "Seeds++ Batch"]:
|
||||
value_label = f"{value}"
|
||||
value_label = f"seed: {value}"
|
||||
else:
|
||||
value_label = f"{value_type}: {value}"
|
||||
|
||||
@@ -63,7 +64,9 @@ class easyXYPlot():
|
||||
arr = value.split(',')
|
||||
model_name = os.path.basename(os.path.splitext(arr[0])[0])
|
||||
trigger_words = ' ' + arr[3] if value_type == 'Lora' and len(arr[3]) > 2 else ''
|
||||
value_label = f"{model_name}{trigger_words}"
|
||||
lora_weight = float(arr[1]) if value_type == 'Lora' and len(arr) > 1 else 0
|
||||
lora_weight_desc = f"({lora_weight:.2f})" if lora_weight > 0 else ''
|
||||
value_label = f"{model_name[:30]}{lora_weight_desc} {trigger_words}"
|
||||
|
||||
if value_type in ["ModelMergeBlocks"]:
|
||||
if ":" in value:
|
||||
@@ -118,24 +121,32 @@ class easyXYPlot():
|
||||
|
||||
def calculate_background_dimensions(self):
|
||||
border_size = int((self.max_width // 8) * 1.5) if self.y_type != "None" or self.x_type != "None" else 0
|
||||
|
||||
bg_width = self.num_cols * (self.max_width + self.grid_spacing) - self.grid_spacing + border_size * (
|
||||
self.y_type != "None")
|
||||
bg_height = self.num_rows * (self.max_height + self.grid_spacing) - self.grid_spacing + border_size * (
|
||||
self.x_type != "None")
|
||||
|
||||
# Add space at the bottom of the image for common informaiton about the image
|
||||
bg_height = bg_height + (border_size*2)
|
||||
# print(f"Grid Size: width = {bg_width} height = {bg_height} border_size = {border_size}")
|
||||
|
||||
x_offset_initial = border_size if self.y_type != "None" else 0
|
||||
y_offset = border_size if self.x_type != "None" else 0
|
||||
|
||||
return bg_width, bg_height, x_offset_initial, y_offset
|
||||
|
||||
|
||||
def adjust_font_size(self, text, initial_font_size, label_width):
|
||||
font = self.get_font(initial_font_size, self.custom_font)
|
||||
text_width = font.getbbox(text)
|
||||
# pprint.pp(f"Initial font size: {initial_font_size}, text: {text}, text_width: {text_width}")
|
||||
if text_width and text_width[2]:
|
||||
text_width = text_width[2]
|
||||
|
||||
scaling_factor = 0.9
|
||||
if text_width > (label_width * scaling_factor):
|
||||
# print(f"Adjusting font size from {initial_font_size} to fit text width {text_width} into label width {label_width} scaling_factor {scaling_factor}")
|
||||
return int(initial_font_size * (label_width / text_width) * scaling_factor)
|
||||
else:
|
||||
return initial_font_size
|
||||
@@ -144,15 +155,22 @@ class easyXYPlot():
|
||||
_, _, width, height = d.textbbox((0, 0), text=text, font=font)
|
||||
return width, height
|
||||
|
||||
def create_label(self, img, text, initial_font_size, is_x_label=True, max_font_size=70, min_font_size=10):
|
||||
label_width = img.width if is_x_label else img.height
|
||||
def create_label(self, img, text, initial_font_size, is_x_label=True, max_font_size=70, min_font_size=10, label_width=0, label_height=0):
|
||||
|
||||
# if the label_width is specified, leave it along. Otherwise do the old logic.
|
||||
if label_width == 0:
|
||||
label_width = img.width if is_x_label else img.height
|
||||
|
||||
text_lines = text.split('\n')
|
||||
longest_line = max(text_lines, key=len)
|
||||
|
||||
# Adjust font size
|
||||
font_size = self.adjust_font_size(text, initial_font_size, label_width)
|
||||
font_size = self.adjust_font_size(longest_line, initial_font_size, label_width)
|
||||
font_size = min(max_font_size, font_size) # Ensure font isn't too large
|
||||
font_size = max(min_font_size, font_size) # Ensure font isn't too small
|
||||
|
||||
label_height = int(font_size * 1.5) if is_x_label else font_size
|
||||
if label_height == 0:
|
||||
label_height = int(font_size * 1.5) if is_x_label else font_size
|
||||
|
||||
label_bg = Image.new('RGBA', (label_width, label_height), color=(255, 255, 255, 0))
|
||||
d = ImageDraw.Draw(label_bg)
|
||||
@@ -166,7 +184,7 @@ class easyXYPlot():
|
||||
text = text + '...'
|
||||
|
||||
# Compute text width and height for multi-line text
|
||||
text_lines = text.split('\n')
|
||||
|
||||
text_widths, text_heights = zip(*[self.textsize(d, line, font=font) for line in text_lines])
|
||||
max_text_width = max(text_widths)
|
||||
total_text_height = sum(text_heights)
|
||||
@@ -195,8 +213,7 @@ class easyXYPlot():
|
||||
clip = clip if clip is not None else plot_image_vars["clip"]
|
||||
steps = plot_image_vars['steps'] if "steps" in plot_image_vars else 1
|
||||
|
||||
sd_version = get_sd_version(plot_image_vars['model'])
|
||||
|
||||
sd_version = get_sd_version(plot_image_vars['model'])
|
||||
# 高级用法
|
||||
if plot_image_vars["x_node_type"] == "advanced" or plot_image_vars["y_node_type"] == "advanced":
|
||||
if self.x_type == "Seeds++ Batch" or self.y_type == "Seeds++ Batch":
|
||||
@@ -347,17 +364,24 @@ class easyXYPlot():
|
||||
|
||||
# Lora
|
||||
if self.x_type == "Lora" or self.y_type == "Lora":
|
||||
# print(f"Lora: {x_value} {y_value}")
|
||||
model = model if model is not None else plot_image_vars["model"]
|
||||
clip = clip if clip is not None else plot_image_vars["clip"]
|
||||
|
||||
xy_values = x_value if self.x_type == "Lora" else y_value
|
||||
lora_name, lora_model_strength, lora_clip_strength, _ = xy_values.split(",")
|
||||
lora_stack = [{"lora_name": lora_name, "model": model, "clip" :clip, "model_strength": float(lora_model_strength), "clip_strength": float(lora_clip_strength)}]
|
||||
|
||||
# print(f"new_lora_stack: {new_lora_stack}")
|
||||
|
||||
|
||||
if 'lora_stack' in plot_image_vars:
|
||||
lora_stack = lora_stack + plot_image_vars['lora_stack']
|
||||
|
||||
|
||||
if lora_stack is not None and lora_stack != []:
|
||||
for lora in lora_stack:
|
||||
# Each generation of the model, must use the reference to previously created model / clip objects.
|
||||
lora['model'] = model
|
||||
lora['clip'] = clip
|
||||
model, clip = self.easyCache.load_lora(lora)
|
||||
|
||||
# 提示词
|
||||
@@ -464,6 +488,7 @@ class easyXYPlot():
|
||||
plot_image_vars['negative_weight_interpretation'], w_max=1.0,
|
||||
apply_to_pooled="enable", a1111_prompt_style=a1111_prompt_style, steps=steps)
|
||||
|
||||
|
||||
model = model if model is not None else plot_image_vars["model"]
|
||||
vae = vae if vae is not None else plot_image_vars["vae"]
|
||||
positive = positive if positive is not None else plot_image_vars["positive_cond"]
|
||||
@@ -582,11 +607,10 @@ class easyXYPlot():
|
||||
|
||||
return self.latents_plot
|
||||
|
||||
def plot_images_and_labels(self):
|
||||
# Calculate the background dimensions
|
||||
def plot_images_and_labels(self, plot_image_vars):
|
||||
|
||||
bg_width, bg_height, x_offset_initial, y_offset = self.calculate_background_dimensions()
|
||||
|
||||
# Create the white background image
|
||||
background = Image.new('RGBA', (int(bg_width), int(bg_height)), color=(255, 255, 255, 255))
|
||||
|
||||
output_image = []
|
||||
@@ -618,4 +642,55 @@ class easyXYPlot():
|
||||
|
||||
y_offset += img.height + self.grid_spacing
|
||||
|
||||
return (self.sampler.pil2tensor(background), output_image)
|
||||
# lookup used models in the image
|
||||
common_label = ""
|
||||
# Update to add a function to do the heavy lifting. Parameters are plot_image_vars name, label to use, names of the axis,
|
||||
|
||||
# pprint.pp(plot_image_vars)
|
||||
|
||||
# We don't process LORAs here because there can be multiple of them.
|
||||
labels = [
|
||||
{"id": "ckpt_name", "id_desc": "ckpt", "axis_type" : "Checkpoint"},
|
||||
{"id": "vae_name", "id_desc": '', "axis_type" : "vae_name"},
|
||||
{"id": "sampler_name", "id_desc": "sampler", "axis_type" : "Sampler"},
|
||||
{"id": "scheduler", "id_desc": '', "axis_type" : "Scheduler"},
|
||||
{"id": "steps", "id_desc": '', "axis_type" : "Steps"},
|
||||
{"id": "Flux Guidance", "id_desc": 'guidance', "axis_type" : "Flux Guidance"},
|
||||
{"id": "seed", "id_desc": '', "axis_type" : "Seeds++ Batch"}
|
||||
]
|
||||
|
||||
for item in labels:
|
||||
# Only add the label if it's not one of the axis
|
||||
# print(f"Checking item: {item['id']} axis_type {item['axis_type']} x_type: {self.x_type} y_type: {self.y_type}")
|
||||
if self.x_type != item['axis_type'] and self.y_type != item['axis_type']:
|
||||
common_label += self.add_common_label(item['id'], plot_image_vars, item['id_desc'])
|
||||
common_label += f"\n"
|
||||
|
||||
if plot_image_vars['lora_stack'] is not None and plot_image_vars['lora_stack'] != []:
|
||||
# print(f"lora_stack: {plot_image_vars['lora_stack']}")
|
||||
for lora in plot_image_vars['lora_stack']:
|
||||
|
||||
lora_name = lora['lora_name']
|
||||
lora_weight = lora['model_strength']
|
||||
if lora_name is not None and len(lora_name) > 0 and lora_weight > 0:
|
||||
common_label += f"LORA: {lora_name} weight: {lora_weight:.2f} \n"
|
||||
|
||||
common_label = common_label.strip()
|
||||
|
||||
if len(common_label) > 0:
|
||||
label_height = background.height - y_offset
|
||||
label_bg = self.create_label(background, common_label, int(48 * background.width / 512), label_width=background.width, label_height=label_height)
|
||||
label_x = (background.width - label_bg.width) // 2
|
||||
label_y = y_offset
|
||||
# print(f"Adding common label: {common_label} x = {label_x} y = {label_y}")
|
||||
background.alpha_composite(label_bg, (label_x, label_y))
|
||||
|
||||
return (self.sampler.pil2tensor(background), output_image)
|
||||
|
||||
def add_common_label(self, tag, plot_image_vars, description = ''):
|
||||
label = ''
|
||||
if description == '': description = tag
|
||||
if tag in plot_image_vars and plot_image_vars[tag] is not None and plot_image_vars[tag] != 'None':
|
||||
label += f"{description}: {plot_image_vars[tag]} "
|
||||
# print(f"add_common_label: {tag} description: {description} label: {label}" )
|
||||
return label
|
||||
|
||||
+20
-13
@@ -1057,7 +1057,12 @@ class imageChooser(PreviewImage):
|
||||
images_in = torch.cat(kwargs.pop('images'))
|
||||
self.batch = images_in.shape[0]
|
||||
for x in kwargs: kwargs[x] = kwargs[x][0]
|
||||
result = self.save_images(images=images_in, prompt=prompt)
|
||||
|
||||
try:
|
||||
pnginfo = extra_pnginfo[0]
|
||||
except:
|
||||
pnginfo = None
|
||||
result = self.save_images(images=images_in, prompt=prompt, extra_pnginfo=pnginfo)
|
||||
|
||||
images = result['ui']['images']
|
||||
PromptServer.instance.send_sync("easyuse-image-choose", {"id": id, "urls": images})
|
||||
@@ -1964,7 +1969,7 @@ class makeImageForICRepaint:
|
||||
b = torch.full([batch_size, height, width, 1], ((color) & 0xFF) / 0xFF)
|
||||
return torch.cat((r, g, b), dim=-1)
|
||||
|
||||
def resize_image_and_mask(self, image, mask, w, h ):
|
||||
def resize_image_and_mask(self, image, mask, w, h ,fit='fill'):
|
||||
ret_images = []
|
||||
ret_masks = []
|
||||
_mask = Image.new('L', size=(w, h), color='black')
|
||||
@@ -1972,12 +1977,12 @@ class makeImageForICRepaint:
|
||||
if image is not None and len(image) > 0:
|
||||
for i in image:
|
||||
_image = tensor2pil(i).convert('RGB')
|
||||
_image = fit_resize_image(_image, w, h, 'fill', Image.LANCZOS, '#000000')
|
||||
_image = fit_resize_image(_image, w, h, fit, Image.LANCZOS, '#000000')
|
||||
ret_images.append(pil2tensor(_image))
|
||||
if mask is not None and len(mask) > 0:
|
||||
for m in mask:
|
||||
_mask = tensor2pil(m).convert('L')
|
||||
_mask = fit_resize_image(_mask, w, h, 'fill', Image.LANCZOS).convert('L')
|
||||
_mask = fit_resize_image(_mask, w, h, fit, Image.LANCZOS).convert('L')
|
||||
ret_masks.append(image2mask(_mask))
|
||||
|
||||
if len(ret_images) > 0 and len(ret_masks) > 0:
|
||||
@@ -2017,16 +2022,18 @@ class makeImageForICRepaint:
|
||||
image, mask, context_mask = None, None, None
|
||||
|
||||
# resize
|
||||
if img1_h != img2_h and img1_w != img2_w:
|
||||
if img1_h != img2_h or img1_w != img2_w:
|
||||
width, height = img2_w, img2_h
|
||||
if direction == 'left-right' and img1_h != img2_h:
|
||||
scale_factor = img2_h / img1_h
|
||||
width = round(img1_w * scale_factor)
|
||||
elif direction == 'top-bottom' and img1_w != img2_w:
|
||||
scale_factor = img2_w / img1_w
|
||||
height = round(img1_h * scale_factor)
|
||||
|
||||
image_1, mask_1 = self.resize_image_and_mask(image_1, mask_1, width, height)
|
||||
fit = 'crop'
|
||||
if method != 'uniform width':
|
||||
if direction == 'left-right' and img1_h != img2_h:
|
||||
scale_factor = img2_h / img1_h
|
||||
width = round(img1_w * scale_factor)
|
||||
elif direction == 'top-bottom' and img1_w != img2_w:
|
||||
scale_factor = img2_w / img1_w
|
||||
height = round(img1_h * scale_factor)
|
||||
fit = 'fill'
|
||||
image_1, mask_1 = self.resize_image_and_mask(image_1, mask_1, width, height, fit)
|
||||
|
||||
if mask_1 is None:
|
||||
mask_1 = torch.full((1, image_1.shape[1], image_1.shape[2]), 0, dtype=torch.float32, device="cpu")
|
||||
|
||||
+17
-15
@@ -166,7 +166,7 @@ class Float:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {"value": ("FLOAT", {"default": 0, "step": 0.01, "min": -999999, "max": 999999, })},
|
||||
"required": {"value": ("FLOAT", {"default": 0, "step": 0.01, "min":-0xffffffffffffffff, "max": 0xffffffffffffffff, })},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("FLOAT",)
|
||||
@@ -175,7 +175,7 @@ class Float:
|
||||
CATEGORY = "EasyUse/Logic/Type"
|
||||
|
||||
def execute(self, value):
|
||||
return (value,)
|
||||
return (round(value, 3),)
|
||||
|
||||
|
||||
# 浮点数范围
|
||||
@@ -239,9 +239,9 @@ class RangeFloat:
|
||||
error_if_mismatched_list_args(locals())
|
||||
getcontext().prec = 12
|
||||
|
||||
start = [Decimal(s) for s in start]
|
||||
stop = [Decimal(s) for s in stop]
|
||||
step = [Decimal(s) for s in step]
|
||||
start = [round(Decimal(s),2) for s in start]
|
||||
stop = [round(Decimal(s),2) for s in stop]
|
||||
step = [round(Decimal(s),2) for s in step]
|
||||
|
||||
ranges = []
|
||||
range_sizes = []
|
||||
@@ -573,17 +573,17 @@ class mathFloatOperation:
|
||||
|
||||
def float_math_operation(self, a, b, operation):
|
||||
if operation == "add":
|
||||
return (a + b,)
|
||||
return (round(a + b,3),)
|
||||
elif operation == "subtract":
|
||||
return (a - b,)
|
||||
return (round(a - b,3),)
|
||||
elif operation == "multiply":
|
||||
return (a * b,)
|
||||
return (round(a * b,3),)
|
||||
elif operation == "divide":
|
||||
return (a / b,)
|
||||
return (round(a / b,3),)
|
||||
elif operation == "modulo":
|
||||
return (a % b,)
|
||||
return (round(a % b,3),)
|
||||
elif operation == "power":
|
||||
return (a ** b,)
|
||||
return (round(a ** b,3),)
|
||||
|
||||
|
||||
class mathStringOperation:
|
||||
@@ -1607,8 +1607,10 @@ class saveText:
|
||||
if not os.path.exists(output_file_path):
|
||||
os.makedirs(output_file_path)
|
||||
|
||||
if not overwrite:
|
||||
pass
|
||||
if overwrite:
|
||||
file_mode = "w"
|
||||
else:
|
||||
file_mode = "a"
|
||||
|
||||
log_node_info("Save Text", f"Saving to {filepath}")
|
||||
|
||||
@@ -1617,13 +1619,13 @@ class saveText:
|
||||
for i in text.split("\n"):
|
||||
text_list.append(i.strip())
|
||||
|
||||
with open(filepath, "w", newline="", encoding='utf-8') as csv_file:
|
||||
with open(filepath, file_mode, newline="", encoding='utf-8') as csv_file:
|
||||
csv_writer = csv.writer(csv_file)
|
||||
# Write each line as a separate row in the CSV file
|
||||
for line in text_list:
|
||||
csv_writer.writerow([line])
|
||||
else:
|
||||
with open(filepath, "w", newline="", encoding='utf-8') as text_file:
|
||||
with open(filepath, file_mode, newline="", encoding='utf-8') as text_file:
|
||||
for line in text:
|
||||
text_file.write(line)
|
||||
|
||||
|
||||
@@ -513,7 +513,7 @@ class samplerFull:
|
||||
|
||||
samp_samples = {"samples": latents_plot}
|
||||
|
||||
images, image_list = sampleXYplot.plot_images_and_labels()
|
||||
images, image_list = sampleXYplot.plot_images_and_labels(plot_image_vars)
|
||||
|
||||
# Generate output_images
|
||||
output_images = torch.stack([tensor.squeeze() for tensor in image_list])
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
from ..config import MAX_SEED_NUM
|
||||
import hashlib
|
||||
import random
|
||||
|
||||
class easySeed:
|
||||
@classmethod
|
||||
@@ -19,6 +21,53 @@ class easySeed:
|
||||
def doit(self, seed=0, prompt=None, extra_pnginfo=None, my_unique_id=None):
|
||||
return seed,
|
||||
|
||||
class seedList:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"min_num": ("INT", {"default": 0, "min": 0, "max": MAX_SEED_NUM}),
|
||||
"max_num": ("INT", {"default": 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,}),
|
||||
},
|
||||
"hidden": {"prompt": "PROMPT", "extra_pnginfo": "EXTRA_PNGINFO", "my_unique_id": "UNIQUE_ID"},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("INT", "INT")
|
||||
RETURN_NAMES = ("seed", "total")
|
||||
FUNCTION = "doit"
|
||||
DESCRIPTION = "Random number seed that can be used in a for loop, by connecting index and easy indexAny node to realize different seed values in the loop."
|
||||
|
||||
CATEGORY = "EasyUse/Seed"
|
||||
|
||||
def doit(self, min_num, max_num, method, total, seed=0, prompt=None, extra_pnginfo=None, my_unique_id=None):
|
||||
random.seed(seed)
|
||||
|
||||
seed_list = []
|
||||
if min_num > max_num:
|
||||
min_num, max_num = max_num, min_num
|
||||
for i in range(total):
|
||||
if method == 'random':
|
||||
s = random.randint(min_num, max_num)
|
||||
elif method == 'increment':
|
||||
s = min_num + i
|
||||
if s > max_num:
|
||||
s = max_num
|
||||
elif method == 'decrement':
|
||||
s = max_num - i
|
||||
if s < min_num:
|
||||
s = min_num
|
||||
seed_list.append(s)
|
||||
return seed_list, total
|
||||
|
||||
@classmethod
|
||||
def IS_CHANGED(s, seed, **kwargs):
|
||||
m = hashlib.sha256()
|
||||
m.update(seed)
|
||||
return m.digest().hex()
|
||||
|
||||
# 全局随机种
|
||||
class globalSeed:
|
||||
@classmethod
|
||||
@@ -46,10 +95,12 @@ class globalSeed:
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"easy seed": easySeed,
|
||||
"easy seedList": seedList,
|
||||
"easy globalSeed": globalSeed,
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"easy seed": "EasySeed",
|
||||
"easy seedList": "EasySeedList",
|
||||
"easy globalSeed": "EasyGlobalSeed",
|
||||
}
|
||||
+20
-1
@@ -106,6 +106,23 @@ class setControlName:
|
||||
|
||||
def set_name(self, controlnet_name):
|
||||
return (controlnet_name,)
|
||||
|
||||
class setLoraName:
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {"required": {
|
||||
"lora_name": (folder_paths.get_filename_list("loras"),),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = (AlwaysEqualProxy('*'),)
|
||||
RETURN_NAMES = ("lora_name",)
|
||||
FUNCTION = "set_name"
|
||||
CATEGORY = "EasyUse/Util"
|
||||
|
||||
def set_name(self, lora_name):
|
||||
return (lora_name,)
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
@@ -113,6 +130,7 @@ NODE_CLASS_MAPPINGS = {
|
||||
"easy sliderControl": sliderControl,
|
||||
"easy ckptNames": setCkptName,
|
||||
"easy controlnetNames": setControlName,
|
||||
"easy loraNames": setLoraName,
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
@@ -120,4 +138,5 @@ NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"easy sliderControl": "Easy Slider Control",
|
||||
"easy ckptNames": "Ckpt Names",
|
||||
"easy controlnetNames": "ControlNet Names",
|
||||
}
|
||||
"easy loraNames": "Lora Names",
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
[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.0"
|
||||
version = "1.3.1"
|
||||
license = { file = "LICENSE" }
|
||||
dependencies = ["diffusers", "accelerate", "clip_interrogator>=0.6.0", "sentencepiece", "lark", "onnxruntime", "spandrel", "opencv-python", "matplotlib", "peft"]
|
||||
|
||||
|
||||
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
Reference in New Issue
Block a user