Compare commits

..
7 Commits
8 changed files with 100 additions and 7 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"]
# ---
+1 -1
View File
@@ -1,7 +1,7 @@
import configparser
import os
version_code = [7, 10, 3]
version_code = [7, 11]
version = f"V{version_code[0]}.{version_code[1]}" + (f'.{version_code[2]}' if len(version_code) > 2 else '')
dependency_version = 23
+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
+1 -1
View File
@@ -373,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:]:
+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.3"
version = "7.11"
license = { file = "LICENSE.txt" }
dependencies = ["segment-anything", "scikit-image", "piexif", "transformers", "opencv-python-headless", "GitPython", "scipy>=1.11.4"]