Compare commits

...
21 Commits
Author SHA1 Message Date
Dr.Lt.Data 1ec3037613 FIXED: wildcard, lora selector bug
from https://github.com/ltdrdata/ComfyUI-Impact-Pack/pull/838/files)

FIXED: SAM Detector
https://github.com/ltdrdata/ComfyUI-Impact-Pack/issues/836
2024-11-30 22:48:00 +09:00
filtered d4136bc955 Prefer widget.callback over stack trace check (#838)
* Prefer widget.callback over stack trace check

Update to LiteGraph included a rename of a local function.  This value was being checked to prevent unwanted value sets.

This has been replaced with widget callback(), which for combo boxes is always called when a value is clicked, regardless of any change.

* nit

* Fix error caused by bug in litegraph

Known issue w/widget.callback and setter/getter.
2024-11-30 21:47:22 +09:00
Dr.Lt.Data c9b3aecd1a fix: install script exception handling for torchvision.download_url 2024-11-26 18:33:27 +09:00
Dr.Lt.Data a2bb6f7c91 feat: LTXV[default] scheduler is added 2024-11-26 01:16:57 +09:00
Dr.Lt.Data e1e408d8c9 improve: add description to wildcard nodes 2024-11-22 21:37:23 +09:00
Jonathan Nogueira f7686845c6 Update core.py - save mask when edited (#817)
* Update core.py - save mask when edited

In order to facilitate the `restore_mask` functionality better, one needs to save the mask to the cache when the mask is edited.

* Update core.py - add missing unsqueeze(0)

mask cache was missing unsqueeze function, giving the resulting mask the wrong size.
2024-11-22 13:41:55 +09:00
FennelFetish 37f465d4a9 Fixes call to make_3d_mask in MakeMaskBatch (#823) 2024-11-22 13:28:50 +09:00
Dr.Lt.Data 48b4254e81 fix: compatibility patch for updated ComfyUI
https://github.com/comfyanonymous/ComfyUI/commit/156a28786be9ba6352061090096462bfb1b485bb

https://github.com/ltdrdata/ComfyUI-Impact-Pack/issues/821
2024-11-20 09:46:42 +09:00
Dr.Lt.Data 24de5a846b hotfix: failing of ultralytics due to security policy 2024-11-09 19:46:33 +09:00
Dr.Lt.Data 6fe85ed6bf version marker 2024-11-08 22:02:22 +09:00
H.D.Tài 216f4660e1 Typecast possible float values in SEG_ELT to int (#811) 2024-11-08 22:01:28 +09:00
Dr.Lt.Data 314b676cc3 FIX: compatibility patch latest ComfyUI update.
https://github.com/comfyanonymous/ComfyUI/commit/b49616f9511c57c8d54c4032e305d72352ac4ff5

https://github.com/ltdrdata/ComfyUI-Impact-Pack/issues/812
2024-11-08 21:58:06 +09:00
Dr.Lt.Data cd89590a7d FEAT: List Bridge node is added. 2024-11-07 23:33:59 +09:00
Vedat Baday dc70f40eff perf: cache pip installed packages (#803) 2024-11-03 04:13:37 +09:00
Vedat Baday 825296c3e0 fix: install manual (#794) 2024-11-02 05:39:08 +09:00
Dr.Lt.Data bf0e86a10d FIXED: ImageListToImageBatch - make sure output is not list when item size is less than 2
https://github.com/comfyanonymous/ComfyUI/issues/5039
2024-11-01 02:28:15 +09:00
Dr.Lt.Data 727295b52e fix: wildcard bugs
- cannot remove comment
- cannot populate <single-wildcard-pattern> if `{count$$<single-wildcard-pattern>}`

https://github.com/ltdrdata/ComfyUI-Impact-Pack/issues/789

https://github.com/ltdrdata/ComfyUI-Impact-Pack/issues/788
2024-10-24 00:25:57 +09:00
Dr.Lt.Data 6c23f7691d fixed: wildcard - invalid regex syntax
https://github.com/ltdrdata/ComfyUI-Impact-Pack/issues/790
2024-10-22 12:20:59 +09:00
Dr.Lt.Data 6248f31402 improved: lora in wildcard - Allows omitting leading zero when specifying floating-point values less than 1.
e.g. .1 instead of 0.1

https://github.com/ltdrdata/ComfyUI-Inspire-Pack/issues/178
2024-10-21 12:44:00 +09:00
Dr.Lt.Data cb0b652703 REFACTOR: GeneralSwitch - better implementation 2024-10-16 21:42:41 +09:00
Dr.Lt.Data 9c4fa5fb27 FIXED: switch any <-> inverse switch disconnection issue
https://github.com/ltdrdata/ComfyUI-extension-tutorials/issues/58#issuecomment-2415330114
2024-10-16 20:27:33 +09:00
16 changed files with 238 additions and 97 deletions
+1
View File
@@ -286,6 +286,7 @@ This custom node helps to conveniently enhance images through Detector, Detailer
* `Negative Cond Placeholder` - Models like FLUX.1 do not use Negative Conditioning. This is a placeholder node for them. You can use FLUX.1 by replacing the Negative Conditioning used in Impact KSampler, KSampler (Inspire), and Detailer with this node.
* `Execution Order Controller` - A helper node that can forcibly control the execution order of nodes.
* Connect the output of the node that should be executed first to the signal, and make the input of the node that should be executed later pass through this node.
* `List Bridge` - When passing the list output through this node, it collects and organizes the data before forwarding it, which ensures that the previous stage's sub-workflow has been completed.
## MMDet nodes (DEPRECATED) - Don't use these nodes
+2
View File
@@ -273,6 +273,7 @@ NODE_CLASS_MAPPINGS = {
"StringListToString": StringListToString,
"WildcardPromptFromString": WildcardPromptFromString,
"ImpactExecutionOrderController": ImpactExecutionOrderController,
"ImpactListBridge": ImpactListBridge,
"RemoveNoiseMask": RemoveNoiseMask,
@@ -393,6 +394,7 @@ NODE_DISPLAY_NAME_MAPPINGS = {
"ImpactSwitch": "Switch (Any)",
"ImpactInversedSwitch": "Inversed Switch (Any)",
"ImpactExecutionOrderController": "Execution Order Controller",
"ImpactListBridge": "List Bridge",
"MasksToMaskList": "Mask Batch to Mask List",
"MaskListToMaskBatch": "Mask List to Mask Batch",
+39
View File
@@ -1,10 +1,13 @@
import functools
import os
import re
import shutil
import sys
import subprocess
import threading
import locale
import traceback
from typing import Set
if sys.argv[0] == 'install.py':
@@ -66,6 +69,42 @@ def process_wrap(cmd_str, cwd=None, handler=None, env=None):
stderr_thread.join()
return process.wait()
@functools.cache
def get_installed_packages() -> Set[str]:
try:
result = subprocess.check_output([sys.executable, '-m', 'pip', 'list'], universal_newlines=True)
pip_list = set([line.split()[0].lower() for line in result.split('\n') if line.strip()])
return pip_list
except subprocess.CalledProcessError as e:
raise Exception(f"[ComfyUI-Impact-Pack] Failed to retrieve the information of installed pip packages.")
def is_package_installed(name: str) -> bool:
name = name.strip()
pattern = r'([^<>!=]+)([<>!=]=?)'
match = re.search(pattern, name)
if match:
name = match.group(1)
result = name.lower() in get_installed_packages()
return result
def is_requirements_installed(file_path: str) -> bool:
print(f"Requirements file: {file_path}")
if os.path.exists(file_path):
with open(file_path, "r") as file:
lines = file.readlines()
for line in lines:
if not is_package_installed(line):
return False
pip_install = [sys.executable, "-m", "pip", "install", "-U"]
# ---
+13 -10
View File
@@ -114,19 +114,22 @@ try:
onnx_path = os.path.join(model_path, "onnx")
if not os.path.exists(os.path.join(os.path.dirname(__file__), '..', 'skip_download_model')):
if not impact.config.get_config()['mmdet_skip']:
bbox_path = os.path.join(model_path, "mmdets", "bbox")
if not os.path.exists(bbox_path):
os.makedirs(bbox_path)
try:
if not impact.config.get_config()['mmdet_skip']:
bbox_path = os.path.join(model_path, "mmdets", "bbox")
if not os.path.exists(bbox_path):
os.makedirs(bbox_path)
if not os.path.exists(os.path.join(bbox_path, "mmdet_anime-face_yolov3.pth")):
download_url("https://huggingface.co/dustysys/ddetailer/resolve/main/mmdet/bbox/mmdet_anime-face_yolov3.pth", bbox_path)
if not os.path.exists(os.path.join(bbox_path, "mmdet_anime-face_yolov3.pth")):
download_url("https://huggingface.co/dustysys/ddetailer/resolve/main/mmdet/bbox/mmdet_anime-face_yolov3.pth", bbox_path)
if not os.path.exists(os.path.join(bbox_path, "mmdet_anime-face_yolov3.py")):
download_url("https://raw.githubusercontent.com/Bing-su/dddetailer/master/config/mmdet_anime-face_yolov3.py", bbox_path)
if not os.path.exists(os.path.join(bbox_path, "mmdet_anime-face_yolov3.py")):
download_url("https://raw.githubusercontent.com/Bing-su/dddetailer/master/config/mmdet_anime-face_yolov3.py", bbox_path)
if not os.path.exists(os.path.join(sam_path, "sam_vit_b_01ec64.pth")):
download_url("https://dl.fbaipublicfiles.com/segment_anything/sam_vit_b_01ec64.pth", sam_path)
if not os.path.exists(os.path.join(sam_path, "sam_vit_b_01ec64.pth")):
download_url("https://dl.fbaipublicfiles.com/segment_anything/sam_vit_b_01ec64.pth", sam_path)
except:
print(f"[Impact Pack] Failed to auto-download model files. Please download them manually.")
if not os.path.exists(onnx_path):
print(f"### ComfyUI-Impact-Pack: onnx model directory created ({onnx_path})")
+40 -38
View File
@@ -353,7 +353,7 @@ app.registerExtension({
}
this.outputs[0].type = origin_type;
this.outputs[0].name = origin_type;
this.outputs[0].name = 'output1';
}
return;
@@ -483,8 +483,12 @@ app.registerExtension({
if(this.inputs[0].type == '*'){
const node = app.graph.getNodeById(link_info.origin_id);
let origin_type = node.outputs[link_info.origin_slot].type;
let origin_type = node.outputs[link_info.origin_slot]?.type;
if(link_info.target_slot == 0 && this.inputs.length > 1) {
origin_type = this.inputs[1].type;
node.connect(link_info.origin_slot, node.id, 'input1');
}
if(origin_type == '*') {
this.disconnectInput(link_info.target_slot);
return;
@@ -583,17 +587,17 @@ app.registerExtension({
}
if(node.comfyClass == "ImpactSEGSLabelFilter" || node.comfyClass == "SEGSLabelFilterDetailerHookProvider") {
node.widgets[0].callback = (value, canvas, node, pos, e) => {
if(node.widgets[1].value.trim() != "" && !node.widgets[1].value.trim().endsWith(","))
node.widgets[1].value += ", "
node.widgets[1].value += value;
if(node.widgets_values)
node.widgets_values[1] = node.widgets[1].value;
}
Object.defineProperty(node.widgets[0], "value", {
set: (value) => {
const stackTrace = new Error().stack;
if(stackTrace.includes('inner_value_change')) {
if(node.widgets[1].value.trim() != "" && !node.widgets[1].value.trim().endsWith(","))
node.widgets[1].value += ", "
node.widgets[1].value += value;
node.widgets_values[1] = node.widgets[1].value;
}
node._value = value;
},
get: () => {
@@ -663,18 +667,18 @@ app.registerExtension({
break;
}
node.widgets[combo_id+1].callback = (value, canvas, node, pos, e) => {
if(node.widgets[tbox_id].value != '')
node.widgets[tbox_id].value += ', '
node.widgets[tbox_id].value += node._wildcard_value;
}
Object.defineProperty(node.widgets[combo_id+1], "value", {
set: (value) => {
const stackTrace = new Error().stack;
if(stackTrace.includes('inner_value_change')) {
if(value != "Select the Wildcard to add to the text") {
if(node.widgets[tbox_id].value != '')
node.widgets[tbox_id].value += ', '
node.widgets[tbox_id].value += value;
}
}
},
if (value !== "Select the Wildcard to add to the text")
node._wildcard_value = value;
},
get: () => { return "Select the Wildcard to add to the text"; }
});
@@ -686,24 +690,22 @@ app.registerExtension({
});
if(has_lora) {
node.widgets[combo_id].callback = (value, canvas, node, pos, e) => {
let lora_name = node._value;
if(lora_name.endsWith('.safetensors')) {
lora_name = lora_name.slice(0, -12);
}
node.widgets[tbox_id].value += `<lora:${lora_name}>`;
if(node.widgets_values) {
node.widgets_values[tbox_id] = node.widgets[tbox_id].value;
}
}
Object.defineProperty(node.widgets[combo_id], "value", {
set: (value) => {
const stackTrace = new Error().stack;
if(stackTrace.includes('inner_value_change')) {
if(value != "Select the LoRA to add to the text") {
let lora_name = value;
if (lora_name.endsWith('.safetensors')) {
lora_name = lora_name.slice(0, -12);
}
node.widgets[tbox_id].value += `<lora:${lora_name}>`;
if(node.widgets_values) {
node.widgets_values[tbox_id] = node.widgets[tbox_id].value;
}
}
}
node._value = value;
if (value !== "Select the LoRA to add to the text")
node._value = value;
},
get: () => { return "Select the LoRA to add to the text"; }
+5 -2
View File
@@ -353,13 +353,16 @@ class ImpactSamEditorDialog extends ComfyDialog {
imgCtx.drawImage(orig_image, 0, 0, drawWidth, drawHeight);
// update mask
let w = (drawWidth * imgCanvas.clientWidth/imgCanvas.width) + "px";
let h = (drawHeight * imgCanvas.clientHeight/imgCanvas.height) + "px";
pointsCanvas.width = drawWidth;
pointsCanvas.height = drawHeight;
pointsCanvas.style.top = imgCanvas.offsetTop + "px";
pointsCanvas.style.left = imgCanvas.offsetLeft + "px";
maskCanvas.width = drawWidth;
maskCanvas.height = drawHeight;
maskCanvas.style.width = w;
maskCanvas.style.height = h;
maskCanvas.style.top = imgCanvas.offsetTop + "px";
maskCanvas.style.left = imgCanvas.offsetLeft + "px";
+2 -2
View File
@@ -1,10 +1,10 @@
import configparser
import os
version_code = [7, 10, 1]
version_code = [7, 13]
version = f"V{version_code[0]}.{version_code[1]}" + (f'.{version_code[2]}' if len(version_code) > 2 else '')
dependency_version = 23
dependency_version = 24
my_path = os.path.dirname(__file__)
old_config_path = os.path.join(my_path, "impact-pack.ini")
+38 -20
View File
@@ -11,6 +11,7 @@ from impact.utils import *
from collections import namedtuple
import numpy as np
from skimage.measure import label
from PIL import ImageOps
import nodes
import comfy_extras.nodes_upscale_model as model_upscale
@@ -47,7 +48,7 @@ preview_bridge_last_mask_cache = {}
current_prompt = None
SCHEDULERS = comfy.samplers.KSampler.SCHEDULERS + ['AYS SDXL', 'AYS SD1', 'AYS SVD', 'GITS[coeff=1.2]']
SCHEDULERS = comfy.samplers.KSampler.SCHEDULERS + ['AYS SDXL', 'AYS SD1', 'AYS SVD', 'GITS[coeff=1.2]', 'LTXV[default]']
def is_execution_model_version_supported():
@@ -69,6 +70,13 @@ def set_previewbridge_image(node_id, file, item):
pb_id = f"${node_id}-{pb_id_cnt}"
preview_bridge_image_id_map[pb_id] = (file, item)
preview_bridge_image_name_map[node_id, file] = (pb_id, item)
if os.path.isfile(file):
i = Image.open(file)
i = ImageOps.exif_transpose(i)
if 'A' in i.getbands():
mask = np.array(i.getchannel('A')).astype(np.float32) / 255.0
mask = 1. - torch.from_numpy(mask)
preview_bridge_last_mask_cache[node_id] = mask.unsqueeze(0)
pb_id_cnt += 1
return pb_id
@@ -319,7 +327,12 @@ def enhance_detail(image, model, clip, vae, guide_size, guide_size_for_bbox, max
# prepare mask
if noise_mask is not None and inpaint_model:
positive, negative, latent_image = nodes.InpaintModelConditioning().encode(positive, negative, upscaled_image, vae, noise_mask)
imc_encode = nodes.InpaintModelConditioning().encode
if 'noise_mask' in inspect.signature(imc_encode).parameters:
positive, negative, latent_image = imc_encode(positive, negative, upscaled_image, vae, mask=noise_mask, noise_mask=True)
else:
print(f"[Impact Pack] ComfyUI is an outdated version.")
positive, negative, latent_image = imc_encode(positive, negative, upscaled_image, vae, noise_mask)
else:
latent_image = to_latent_image(upscaled_image, vae)
if noise_mask is not None:
@@ -1354,9 +1367,14 @@ def segs_to_masklist(segs):
return masks
def vae_decode(vae, samples, use_tile, hook, tile_size=512):
def vae_decode(vae, samples, use_tile, hook, tile_size=512, overlap=64):
if use_tile:
pixels = nodes.VAEDecodeTiled().decode(vae, samples, tile_size)[0]
decoder = nodes.VAEDecodeTiled()
if 'overlap' in inspect.signature(decoder.decode).parameters:
pixels = decoder.decode(vae, samples, tile_size, overlap=overlap)[0]
else:
print(f"[Impact Pack] Your ComfyUI is outdated.")
pixels = decoder.decode(vae, samples, tile_size)[0]
else:
pixels = nodes.VAEDecode().decode(vae, samples)[0]
@@ -1378,12 +1396,12 @@ def vae_encode(vae, pixels, use_tile, hook, tile_size=512):
return samples
def latent_upscale_on_pixel_space_shape(samples, scale_method, w, h, vae, use_tile=False, tile_size=512, save_temp_prefix=None, hook=None):
return latent_upscale_on_pixel_space_shape2(samples, scale_method, w, h, vae, use_tile, tile_size, save_temp_prefix, hook)[0]
def latent_upscale_on_pixel_space_shape(samples, scale_method, w, h, vae, use_tile=False, tile_size=512, save_temp_prefix=None, hook=None, overlap=64):
return latent_upscale_on_pixel_space_shape2(samples, scale_method, w, h, vae, use_tile, tile_size, save_temp_prefix, hook, overlap=overlap)[0]
def latent_upscale_on_pixel_space_shape2(samples, scale_method, w, h, vae, use_tile=False, tile_size=512, save_temp_prefix=None, hook=None):
pixels = vae_decode(vae, samples, use_tile, hook, tile_size=tile_size)
def latent_upscale_on_pixel_space_shape2(samples, scale_method, w, h, vae, use_tile=False, tile_size=512, save_temp_prefix=None, hook=None, overlap=64):
pixels = vae_decode(vae, samples, use_tile, hook, tile_size=tile_size, overlap=overlap)
if save_temp_prefix is not None:
nodes.PreviewImage().save_images(pixels, filename_prefix=save_temp_prefix)
@@ -1397,12 +1415,12 @@ def latent_upscale_on_pixel_space_shape2(samples, scale_method, w, h, vae, use_t
return (vae_encode(vae, pixels, use_tile, hook, tile_size=tile_size), old_pixels)
def latent_upscale_on_pixel_space(samples, scale_method, scale_factor, vae, use_tile=False, tile_size=512, save_temp_prefix=None, hook=None):
return latent_upscale_on_pixel_space2(samples, scale_method, scale_factor, vae, use_tile, tile_size, save_temp_prefix, hook)[0]
def latent_upscale_on_pixel_space(samples, scale_method, scale_factor, vae, use_tile=False, tile_size=512, save_temp_prefix=None, hook=None, overlap=64):
return latent_upscale_on_pixel_space2(samples, scale_method, scale_factor, vae, use_tile, tile_size, save_temp_prefix, hook, overlap=overlap)[0]
def latent_upscale_on_pixel_space2(samples, scale_method, scale_factor, vae, use_tile=False, tile_size=512, save_temp_prefix=None, hook=None):
pixels = vae_decode(vae, samples, use_tile, hook, tile_size=tile_size)
def latent_upscale_on_pixel_space2(samples, scale_method, scale_factor, vae, use_tile=False, tile_size=512, save_temp_prefix=None, hook=None, overlap=64):
pixels = vae_decode(vae, samples, use_tile, hook, tile_size=tile_size, overlap=overlap)
if save_temp_prefix is not None:
nodes.PreviewImage().save_images(pixels, filename_prefix=save_temp_prefix)
@@ -1418,12 +1436,12 @@ def latent_upscale_on_pixel_space2(samples, scale_method, scale_factor, vae, use
return (vae_encode(vae, pixels, use_tile, hook, tile_size=tile_size), old_pixels)
def latent_upscale_on_pixel_space_with_model_shape(samples, scale_method, upscale_model, new_w, new_h, vae, use_tile=False, tile_size=512, save_temp_prefix=None, hook=None):
return latent_upscale_on_pixel_space_with_model_shape2(samples, scale_method, upscale_model, new_w, new_h, vae, use_tile, tile_size, save_temp_prefix, hook)[0]
def latent_upscale_on_pixel_space_with_model_shape(samples, scale_method, upscale_model, new_w, new_h, vae, use_tile=False, tile_size=512, save_temp_prefix=None, hook=None, overlap=64):
return latent_upscale_on_pixel_space_with_model_shape2(samples, scale_method, upscale_model, new_w, new_h, vae, use_tile, tile_size, save_temp_prefix, hook, overlap=overlap)[0]
def latent_upscale_on_pixel_space_with_model_shape2(samples, scale_method, upscale_model, new_w, new_h, vae, use_tile=False, tile_size=512, save_temp_prefix=None, hook=None):
pixels = vae_decode(vae, samples, use_tile, hook, tile_size=tile_size)
def latent_upscale_on_pixel_space_with_model_shape2(samples, scale_method, upscale_model, new_w, new_h, vae, use_tile=False, tile_size=512, save_temp_prefix=None, hook=None, overlap=64):
pixels = vae_decode(vae, samples, use_tile, hook, tile_size=tile_size, overlap=overlap)
if save_temp_prefix is not None:
nodes.PreviewImage().save_images(pixels, filename_prefix=save_temp_prefix)
@@ -1450,12 +1468,12 @@ def latent_upscale_on_pixel_space_with_model_shape2(samples, scale_method, upsca
def latent_upscale_on_pixel_space_with_model(samples, scale_method, upscale_model, scale_factor, vae, use_tile=False,
tile_size=512, save_temp_prefix=None, hook=None):
return latent_upscale_on_pixel_space_with_model2(samples, scale_method, upscale_model, scale_factor, vae, use_tile, tile_size, save_temp_prefix, hook)[0]
tile_size=512, save_temp_prefix=None, hook=None, overlap=64):
return latent_upscale_on_pixel_space_with_model2(samples, scale_method, upscale_model, scale_factor, vae, use_tile, tile_size, save_temp_prefix, hook, overlap=overlap)[0]
def latent_upscale_on_pixel_space_with_model2(samples, scale_method, upscale_model, scale_factor, vae, use_tile=False,
tile_size=512, save_temp_prefix=None, hook=None):
pixels = vae_decode(vae, samples, use_tile, hook, tile_size=tile_size)
tile_size=512, save_temp_prefix=None, hook=None, overlap=64):
pixels = vae_decode(vae, samples, use_tile, hook, tile_size=tile_size, overlap=overlap)
if save_temp_prefix is not None:
nodes.PreviewImage().save_images(pixels, filename_prefix=save_temp_prefix)
+17 -9
View File
@@ -2150,16 +2150,19 @@ class ImpactWildcardProcessor:
@classmethod
def INPUT_TYPES(s):
return {"required": {
"wildcard_text": ("STRING", {"multiline": True, "dynamicPrompts": False}),
"populated_text": ("STRING", {"multiline": True, "dynamicPrompts": False}),
"mode": ("BOOLEAN", {"default": True, "label_on": "Populate", "label_off": "Fixed"}),
"seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}),
"wildcard_text": ("STRING", {"multiline": True, "dynamicPrompts": False, "tooltip": "Enter a prompt using wildcard syntax."}),
"populated_text": ("STRING", {"multiline": True, "dynamicPrompts": False, "tooltip": "The actual value passed during the execution of 'ImpactWildcardProcessor' is what is shown here. The behavior varies slightly depending on the mode. Wildcard syntax can also be used in 'populated_text'."}),
"mode": ("BOOLEAN", {"default": True, "label_on": "Populate", "label_off": "Fixed", "tooltip": "Populate: Before running the workflow, it overwrites the existing value of 'populated_text' with the prompt processed from 'wildcard_text'. In this mode, 'populated_text' cannot be edited.\nFixed: Ignores wildcard_text and keeps 'populated_text' as is. You can edit 'populated_text' in this mode."}),
"seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff, "tooltip": "Determines the random seed to be used for wildcard processing."}),
"Select to add Wildcard": (["Select the Wildcard to add to the text"],),
},
}
CATEGORY = "ImpactPack/Prompt"
DESCRIPTION = ("The 'ImpactWildcardProcessor' processes text prompts written in wildcard syntax and outputs the processed text prompt.\n\n"
"TIP: Before the workflow is executed, the processing result of 'wildcard_text' is displayed in 'populated_text', and the populated text is saved along with the workflow. If you want to use a seed converted as input, write the prompt directly in 'populated_text' instead of 'wildcard_text', and set the mode to 'Fixed'.")
RETURN_TYPES = ("STRING", )
FUNCTION = "doit"
@@ -2178,17 +2181,22 @@ class ImpactWildcardEncode:
return {"required": {
"model": ("MODEL",),
"clip": ("CLIP",),
"wildcard_text": ("STRING", {"multiline": True, "dynamicPrompts": False}),
"populated_text": ("STRING", {"multiline": True, "dynamicPrompts": False}),
"mode": ("BOOLEAN", {"default": True, "label_on": "Populate", "label_off": "Fixed"}),
"wildcard_text": ("STRING", {"multiline": True, "dynamicPrompts": False, "tooltip": "Enter a prompt using wildcard syntax."}),
"populated_text": ("STRING", {"multiline": True, "dynamicPrompts": False, "tooltip": "The actual value passed during the execution of 'ImpactWildcardEncode' is what is shown here. The behavior varies slightly depending on the mode. Wildcard syntax can also be used in 'populated_text'."}),
"mode": ("BOOLEAN", {"default": True, "label_on": "Populate", "label_off": "Fixed", "tooltip": "Populate: Before running the workflow, it overwrites the existing value of 'populated_text' with the prompt processed from 'wildcard_text'. In this mode, 'populated_text' cannot be edited.\n"
"Fixed: Ignores wildcard_text and keeps 'populated_text' as is. You can edit 'populated_text' in this mode."}),
"Select to add LoRA": (["Select the LoRA to add to the text"] + folder_paths.get_filename_list("loras"), ),
"Select to add Wildcard": (["Select the Wildcard to add to the text"], ),
"seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}),
"seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff, "tooltip": "Determines the random seed to be used for wildcard processing."}),
},
}
CATEGORY = "ImpactPack/Prompt"
DESCRIPTION = ("The 'ImpactWildcardEncode' node processes text prompts written in wildcard syntax and outputs them as conditioning. It also supports LoRA syntax, with the applied LoRA reflected in the model's output.\n\n"
"TIP1: Before the workflow is executed, the processing result of 'wildcard_text' is displayed in 'populated_text', and the populated text is saved along with the workflow. If you want to use a seed converted as input, write the prompt directly in 'populated_text' instead of 'wildcard_text', and set the mode to 'Fixed'.\n"
"TIP2: If the 'Inspire Pack' is installed, LBW(LoRA Block Weight) syntax can also be applied.")
RETURN_TYPES = ("MODEL", "CLIP", "CONDITIONING", "STRING")
RETURN_NAMES = ("model", "clip", "conditioning", "populated_text")
FUNCTION = "doit"
@@ -2213,7 +2221,7 @@ class ImpactSchedulerAdapter:
def INPUT_TYPES(s):
return {"required": {
"scheduler": (comfy.samplers.KSampler.SCHEDULERS, {"defaultInput": True, }),
"extra_scheduler": (['None', 'AYS SDXL', 'AYS SD1', 'AYS SVD', 'GITS[coeff=1.2]'],),
"extra_scheduler": (['None', 'AYS SDXL', 'AYS SD1', 'AYS SVD', 'GITS[coeff=1.2]', 'LTXV[default]'],),
}}
CATEGORY = "ImpactPack/Util"
+2
View File
@@ -27,6 +27,8 @@ def calculate_sigmas(model, sampler, scheduler, steps):
sigmas = nodes.NODE_CLASS_MAPPINGS['AlignYourStepsScheduler']().get_sigmas(scheduler[4:], steps, denoise=1.0)[0]
elif scheduler.startswith('GITS[coeff='):
sigmas = nodes.NODE_CLASS_MAPPINGS['GITSScheduler']().get_sigmas(float(scheduler[11:-1]), steps, denoise=1.0)[0]
elif scheduler == 'LTXV[default]':
sigmas = nodes.NODE_CLASS_MAPPINGS['LTXVScheduler']().get_sigmas(20, 2.05, 0.95, True, 0.1)[0]
else:
sigmas = samplers.calculate_sigmas(model.get_model_object("model_sampling"), scheduler, steps)
+23
View File
@@ -766,6 +766,29 @@ class ImpactExecutionOrderController:
return signal, value
class ImpactListBridge:
@classmethod
def INPUT_TYPES(cls):
return {"required": {
"list_input": (any_typ,),
}}
FUNCTION = "doit"
DESCRIPTION = "When passing the list output through this node, it collects and organizes the data before forwarding it, which ensures that the previous stage's sub-workflow has been completed."
CATEGORY = "ImpactPack/Util"
RETURN_TYPES = (any_typ, )
RETURN_NAMES = ("list_output", )
INPUT_IS_LIST = True
OUTPUT_IS_LIST = (True, )
@staticmethod
def doit(list_input):
return (list_input,)
original_handle_execution = execution.PromptExecutor.handle_execution_error
+5 -5
View File
@@ -896,7 +896,7 @@ class From_SEG_ELT_bbox:
CATEGORY = "ImpactPack/Util"
def doit(self, bbox):
return bbox
return [int(c) for c in bbox]
class From_SEG_ELT_crop_region:
@@ -1101,10 +1101,10 @@ class SEG_ELT_BBOX_ScaleBy:
x1, y1, x2, y2 = x1-cx1, y1-cy1, x2-cx1, y2-cy1
h, w = mask.shape
x1 = min(w-1, max(0, x1))
x2 = min(w-1, max(0, x2))
y1 = min(h-1, max(0, y1))
y2 = min(h-1, max(0, y2))
x1 = int(min(w-1, max(0, x1)))
x2 = int(min(w-1, max(0, x2)))
y1 = int(min(h-1, max(0, y1)))
y2 = int(min(h-1, max(0, y2)))
mask_cropped = mask.copy()
mask_cropped[:, :x1] = 0 # zero fill left side
+6 -1
View File
@@ -106,7 +106,12 @@ def img2img_segs(image, model, clip, vae, seed, steps, cfg, sampler_name, schedu
# prepare mask
if noise_mask is not None and inpaint_model:
positive, negative, latent_image = nodes.InpaintModelConditioning().encode(positive, negative, image, vae, noise_mask)
imc_encode = nodes.InpaintModelConditioning().encode
if 'noise_mask' in inspect.signature(imc_encode).parameters:
positive, negative, latent_image = imc_encode(positive, negative, image, vae, mask=noise_mask, noise_mask=True)
else:
print(f"[Impact Pack] ComfyUI is an outdated version.")
positive, negative, latent_image = imc_encode(positive, negative, image, vae, noise_mask)
else:
latent_image = to_latent_image(image, vae)
if noise_mask is not None:
+12 -5
View File
@@ -17,9 +17,16 @@ class GeneralSwitch:
dyn_inputs = {"input1": (any_typ, {"lazy": True, "tooltip": "Any input. When connected, one more input slot is added."}), }
if core.is_execution_model_version_supported():
stack = inspect.stack()
if stack[2].function == 'get_input_info' and stack[3].function == 'add_node':
for x in range(2, 200):
dyn_inputs[f"input{x}"] = (any_typ, {"lazy": True})
if stack[2].function == 'get_input_info':
# bypass validation
class AllContainer:
def __contains__(self, item):
return True
def __getitem__(self, key):
return any_typ, {"lazy": True}
dyn_inputs = AllContainer()
inputs = {"required": {
"select": ("INT", {"default": 1, "min": 1, "max": 999999, "step": 1, "tooltip": "The input number you want to output among the inputs"}),
@@ -366,7 +373,7 @@ class ImageListToImageBatch:
def doit(self, images):
if len(images) <= 1:
return (images,)
return (images[0],)
else:
image1 = images[0]
for image2 in images[1:]:
@@ -494,7 +501,7 @@ class MakeMaskBatch:
def doit(self, **kwargs):
mask1 = kwargs['mask1']
del kwargs['mask1']
masks = [utils.make_3d_mask(value) for value in kwargs.values()]
masks = [make_3d_mask(value) for value in kwargs.values()]
if len(masks) == 0:
return (mask1,)
+32 -4
View File
@@ -58,11 +58,11 @@ def read_wildcard_dict(wildcard_path):
try:
with open(file_path, 'r', encoding="ISO-8859-1") as f:
lines = f.read().splitlines()
wildcard_dict[key] = lines
wildcard_dict[key] = [x for x in lines if not x.strip().startswith('#')]
except yaml.reader.ReaderError:
with open(file_path, 'r', encoding="UTF-8", errors="ignore") as f:
lines = f.read().splitlines()
wildcard_dict[key] = lines
wildcard_dict[key] = [x for x in lines if not x.strip().startswith('#')]
elif file.endswith('.yaml'):
file_path = os.path.join(root, file)
@@ -150,7 +150,7 @@ def process(text, seed=None):
matches = re.findall(wildcard_pattern, multi_select_pattern[1])
if len(options) == 1 and matches:
# count$$<single wildcard>
options = local_wildcard_dict.get(matches[0])
options = get_wildcard_options(multi_select_pattern[1])
else:
# count$$opt1|opt2|...
options[0] = multi_select_pattern[1]
@@ -199,6 +199,34 @@ def process(text, seed=None):
return replaced_string, replacements_found
def get_wildcard_options(string):
pattern = r"__([\w.\-+/*\\]+?)__"
matches = re.findall(pattern, string)
options = []
for match in matches:
keyword = match.lower()
keyword = wildcard_normalize(keyword)
if keyword in local_wildcard_dict:
options.extend(local_wildcard_dict[keyword])
elif '*' in keyword:
subpattern = keyword.replace('*', '.*').replace('+', '\\+')
total_patterns = []
found = False
for k, v in local_wildcard_dict.items():
if re.match(subpattern, k) is not None or re.match(subpattern, k+'/') is not None:
total_patterns += v
found = True
if found:
options.extend(total_patterns)
elif '/' not in keyword:
string_fallback = string.replace(f"__{match}__", f"__*/{match}__", 1)
options.extend(get_wildcard_options(string_fallback))
return options
def replace_wildcard(string):
pattern = r"__([\w.\-+/*\\]+?)__"
matches = re.findall(pattern, string)
@@ -259,7 +287,7 @@ def process(text, seed=None):
def is_numeric_string(input_str):
return re.match(r'^-?\d+(\.\d+)?$', input_str) is not None
return re.match(r'^-?(\d*\.?\d+|\d+\.?\d*)$', input_str) is not None
def safe_float(x):
+1 -1
View File
@@ -1,7 +1,7 @@
[project]
name = "comfyui-impact-pack"
description = "This extension offers various detector nodes and detailer nodes that allow you to configure a workflow that automatically enhances facial details. And provide iterative upscaler."
version = "7.10.1"
version = "7.13"
license = { file = "LICENSE.txt" }
dependencies = ["segment-anything", "scikit-image", "piexif", "transformers", "opencv-python-headless", "GitPython", "scipy>=1.11.4"]