Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5f630466fb | ||
|
|
81045cc845 | ||
|
|
20f2cac6f0 | ||
|
|
348c3dcb6b | ||
|
|
1ec3037613 | ||
|
|
d4136bc955 | ||
|
|
c9b3aecd1a | ||
|
|
a2bb6f7c91 | ||
|
|
e1e408d8c9 | ||
|
|
f7686845c6 | ||
|
|
37f465d4a9 | ||
|
|
48b4254e81 | ||
|
|
24de5a846b | ||
|
|
6fe85ed6bf | ||
|
|
216f4660e1 | ||
|
|
314b676cc3 | ||
|
|
cd89590a7d | ||
|
|
dc70f40eff | ||
|
|
825296c3e0 | ||
|
|
bf0e86a10d | ||
|
|
727295b52e | ||
|
|
6c23f7691d | ||
|
|
6248f31402 | ||
|
|
cb0b652703 | ||
|
|
9c4fa5fb27 | ||
|
|
759b4d0dc9 | ||
|
|
a65fd56b7f | ||
|
|
47d981eca2 | ||
|
|
b2f382776f | ||
|
|
0e4e439d39 | ||
|
|
18d25a29a0 | ||
|
|
2b724e5ed2 | ||
|
|
96cc242f78 | ||
|
|
28267da071 |
@@ -49,7 +49,7 @@ This custom node helps to conveniently enhance images through Detector, Detailer
|
||||
|
||||
### ControlNet, IPAdapter
|
||||
* `ControlNetApply (SEGS)` - To apply ControlNet in SEGS, you need to use the Preprocessor Provider node from the Inspire Pack to utilize this node.
|
||||
* `segs_preprocessor` and `control_image` can be selectively applied. If an `control_image` is given, `segs_preprocessor` will be ignored.
|
||||
* `segs_preprocessor` and `control_image` can be selectively applied. If a `control_image` is given, `segs_preprocessor` will be ignored.
|
||||
* If set to `control_image`, you can preview the cropped cnet image through `SEGSPreview (CNET Image)`. Images generated by `segs_preprocessor` should be verified through the `cnet_images` output of each Detailer.
|
||||
* The `segs_preprocessor` operates by applying preprocessing on-the-fly based on the cropped image during the detailing process, while `control_image` will be cropped and used as input to `ControlNetApply (SEGS)`.
|
||||
* `ControlNetClear (SEGS)` - Clear applied ControlNet in SEGS
|
||||
@@ -108,6 +108,7 @@ This custom node helps to conveniently enhance images through Detector, Detailer
|
||||
* `SEGS Filter (range)` - This node retrieves only SEGs from SEGS that have a size and position within a certain range.
|
||||
* `SEGS Assign (label)` - Assign labels sequentially to SEGS. This node is useful when used with `[LAB]` of FaceDetailer.
|
||||
* `SEGSConcat` - Concatenate segs1 and segs2. If source shape of segs1 and segs2 are different from segs2 will be ignored.
|
||||
* `SEGS Merge` - SEGS contains multiple SEGs. SEGS Merge integrates several SEGs into a single merged SEG. The label is changed to `merged` and the confidence becomes the minimum confidence. The applied controlnet and cropped_image are removed.
|
||||
* `Picker (SEGS)` - Among the input SEGS, you can select a specific SEG through a dialog. If no SEG is selected, it outputs an empty SEGS. Increasing the batch_size of SEGSDetailer can be used for the purpose of selecting from the candidates.
|
||||
* `Set Default Image For SEGS` - Set a default image for SEGS. SEGS with images set this way do not need to have a fallback image set. When override is set to false, the original image is preserved.
|
||||
* `Remove Image from SEGS` - Remove the image set for the SEGS that has been configured by "Set Default Image for SEGS" or SEGSDetailer. When the image for the SEGS is removed, the Detailer node will operate based on the currently processed image instead of the SEGS.
|
||||
@@ -242,7 +243,7 @@ This custom node helps to conveniently enhance images through Detector, Detailer
|
||||
|
||||
### Logics (experimental)
|
||||
* These nodes are experimental nodes designed to implement the logic for loops and dynamic switching.
|
||||
* `ImpactCompare`, `ImpactConditionalBranch`, `ImpactConditionalBranchSelMode`, `ImpactInt`, `ImpactValueSender`, `ImpactValueReceiver`, `ImpactImageInfo`, `ImpactMinMax`, `ImpactNeg`, `ImpactConditionalStopIteration`
|
||||
* `ImpactCompare`, `ImpactConditionalBranch`, `ImpactConditionalBranchSelMode`, `ImpactInt`, `ImpactBoolean`, `ImpactValueSender`, `ImpactValueReceiver`, `ImpactImageInfo`, `ImpactMinMax`, `ImpactNeg`, `ImpactConditionalStopIteration`
|
||||
* `ImpactIsNotEmptySEGS` - This node returns `true` only if the input SEGS is not empty.
|
||||
* `ImpactIfNone` - Returns `true` if any_input is None, and returns `false` if it is not None.
|
||||
* `Queue Trigger` - When this node is executed, it adds a new queue to assist with repetitive tasks. It will only execute if the signal's status changes.
|
||||
@@ -285,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
|
||||
@@ -343,9 +345,9 @@ mmdet_skip = False
|
||||
3. `cd ComfyUI-Impact-Pack`
|
||||
4. (optional) `git clone https://github.com/ltdrdata/ComfyUI-Impact-Subpack impact_subpack`
|
||||
* Impact Pack will automatically download subpack during its initial launch.
|
||||
5. (optional) `python install.py`
|
||||
5. (optional) `python install-manual.py`
|
||||
* Impact Pack will automatically install its dependencies during its initial launch.
|
||||
* For the portable version, you should execute the command `..\..\..\python_embeded\python.exe install.py` to run the installation script.
|
||||
* For the portable version, you should execute the command `..\..\..\python_embeded\python.exe install-manual.py` to run the installation script.
|
||||
6. Restart ComfyUI
|
||||
|
||||
* NOTE1: If an error occurs during the installation process, please refer to [Troubleshooting Page](troubleshooting/TROUBLESHOOTING.md) for assistance.
|
||||
|
||||
+7
-2
@@ -223,6 +223,7 @@ NODE_CLASS_MAPPINGS = {
|
||||
"ImpactSEGSConcat": SEGSConcat,
|
||||
"ImpactSEGSPicker": SEGSPicker,
|
||||
"ImpactMakeTileSEGS": MakeTileSEGS,
|
||||
"ImpactSEGSMerge": SEGSMerge,
|
||||
|
||||
"SEGSDetailerForAnimateDiff": SEGSDetailerForAnimateDiff,
|
||||
|
||||
@@ -260,6 +261,7 @@ NODE_CLASS_MAPPINGS = {
|
||||
"ImpactLogicalOperators": ImpactLogicalOperators,
|
||||
"ImpactInt": ImpactInt,
|
||||
"ImpactFloat": ImpactFloat,
|
||||
"ImpactBoolean": ImpactBoolean,
|
||||
"ImpactValueSender": ImpactValueSender,
|
||||
"ImpactValueReceiver": ImpactValueReceiver,
|
||||
"ImpactImageInfo": ImpactImageInfo,
|
||||
@@ -271,6 +273,7 @@ NODE_CLASS_MAPPINGS = {
|
||||
"StringListToString": StringListToString,
|
||||
"WildcardPromptFromString": WildcardPromptFromString,
|
||||
"ImpactExecutionOrderController": ImpactExecutionOrderController,
|
||||
"ImpactListBridge": ImpactListBridge,
|
||||
|
||||
"RemoveNoiseMask": RemoveNoiseMask,
|
||||
|
||||
@@ -304,8 +307,8 @@ NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"ImpactSimpleDetectorSEGS_for_AD": "Simple Detector for AnimateDiff (SEGS)",
|
||||
"ImpactSimpleDetectorSEGS": "Simple Detector (SEGS)",
|
||||
"ImpactSimpleDetectorSEGSPipe": "Simple Detector (SEGS/pipe)",
|
||||
"ImpactControlNetApplySEGS": "ControlNetApply (SEGS)",
|
||||
"ImpactControlNetApplyAdvancedSEGS": "ControlNetApplyAdvanced (SEGS)",
|
||||
"ImpactControlNetApplySEGS": "ControlNetApply (SEGS) - DEPRECATED",
|
||||
"ImpactControlNetApplyAdvancedSEGS": "ControlNetApply (SEGS)",
|
||||
"ImpactIPAdapterApplySEGS": "IPAdapterApply (SEGS)",
|
||||
|
||||
"BboxDetectorCombined_v2": "BBOX Detector (combined)",
|
||||
@@ -367,6 +370,7 @@ NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"ImpactSEGSToMaskBatch": "SEGS to Mask Batch",
|
||||
"ImpactSEGSPicker": "Picker (SEGS)",
|
||||
"ImpactMakeTileSEGS": "Make Tile SEGS",
|
||||
"ImpactSEGSMerge": "SEGS Merge",
|
||||
|
||||
"ImpactDecomposeSEGS": "Decompose (SEGS)",
|
||||
"ImpactAssembleSEGS": "Assemble (SEGS)",
|
||||
@@ -390,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",
|
||||
|
||||
@@ -0,0 +1,183 @@
|
||||
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':
|
||||
sys.path.append('.') # for portable version
|
||||
|
||||
|
||||
impact_path = os.path.join(os.path.dirname(__file__), "modules")
|
||||
subpack_path = os.path.join(os.path.dirname(__file__), "impact_subpack")
|
||||
subpack_repo = "https://github.com/ltdrdata/ComfyUI-Impact-Subpack"
|
||||
|
||||
|
||||
comfy_path = os.environ.get('COMFYUI_PATH')
|
||||
if comfy_path is None:
|
||||
print(f"\n[bold yellow]WARN: The `COMFYUI_PATH` environment variable is not set. Assuming `{os.path.dirname(__file__)}/../../` as the ComfyUI path.[/bold yellow]", file=sys.stderr)
|
||||
comfy_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..'))
|
||||
|
||||
model_path = os.environ.get('COMFYUI_MODEL_PATH')
|
||||
if model_path is None:
|
||||
try:
|
||||
import folder_paths
|
||||
model_path = folder_paths.models_dir
|
||||
except:
|
||||
pass
|
||||
|
||||
if model_path is None:
|
||||
model_path = os.path.abspath(os.path.join(comfy_path, 'models'))
|
||||
print(f"\n[bold yellow]WARN: The `COMFYUI_MODEL_PATH` environment variable is not set. Assuming `{model_path}` as the ComfyUI path.[/bold yellow]", file=sys.stderr)
|
||||
|
||||
|
||||
sys.path.append(impact_path)
|
||||
sys.path.append(comfy_path)
|
||||
|
||||
|
||||
# ---
|
||||
def handle_stream(stream, is_stdout):
|
||||
stream.reconfigure(encoding=locale.getpreferredencoding(), errors='replace')
|
||||
|
||||
for msg in stream:
|
||||
if is_stdout:
|
||||
print(msg, end="", file=sys.stdout)
|
||||
else:
|
||||
print(msg, end="", file=sys.stderr)
|
||||
|
||||
|
||||
def process_wrap(cmd_str, cwd=None, handler=None, env=None):
|
||||
print(f"[Impact Pack] EXECUTE: {cmd_str} in '{cwd}'")
|
||||
process = subprocess.Popen(cmd_str, cwd=cwd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env, text=True, bufsize=1)
|
||||
|
||||
if handler is None:
|
||||
handler = handle_stream
|
||||
|
||||
stdout_thread = threading.Thread(target=handler, args=(process.stdout, True))
|
||||
stderr_thread = threading.Thread(target=handler, args=(process.stderr, False))
|
||||
|
||||
stdout_thread.start()
|
||||
stderr_thread.start()
|
||||
|
||||
stdout_thread.join()
|
||||
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"]
|
||||
|
||||
# ---
|
||||
|
||||
|
||||
try:
|
||||
import platform
|
||||
import folder_paths
|
||||
from torchvision.datasets.utils import download_url
|
||||
import impact.config
|
||||
|
||||
print("### ComfyUI-Impact-Pack: Check dependencies")
|
||||
def ensure_subpack():
|
||||
import git
|
||||
if os.path.exists(subpack_path):
|
||||
try:
|
||||
repo = git.Repo(subpack_path)
|
||||
repo.remotes.origin.pull()
|
||||
except:
|
||||
traceback.print_exc()
|
||||
if platform.system() == 'Windows':
|
||||
print(f"[ComfyUI-Impact-Pack] Please turn off ComfyUI and remove '{subpack_path}' and restart ComfyUI.")
|
||||
else:
|
||||
shutil.rmtree(subpack_path)
|
||||
git.Repo.clone_from(subpack_repo, subpack_path)
|
||||
else:
|
||||
git.Repo.clone_from(subpack_repo, subpack_path)
|
||||
|
||||
|
||||
def install():
|
||||
subpack_install_script = os.path.join(subpack_path, "install.py")
|
||||
|
||||
print(f"### ComfyUI-Impact-Pack: Updating subpack")
|
||||
ensure_subpack() # The installation of the subpack must take place before ensure_pip. cv2 triggers a permission error.
|
||||
|
||||
new_env = os.environ.copy()
|
||||
new_env["COMFYUI_PATH"] = comfy_path
|
||||
new_env["COMFYUI_MODEL_PATH"] = model_path
|
||||
|
||||
if os.path.exists(subpack_install_script):
|
||||
if not is_requirements_installed(os.path.join(subpack_path, 'requirements.txt')):
|
||||
process_wrap(pip_install + ['-r', 'requirements.txt'], cwd=subpack_path)
|
||||
|
||||
process_wrap([sys.executable, 'install.py'], cwd=subpack_path, env=new_env)
|
||||
else:
|
||||
print(f"### ComfyUI-Impact-Pack: (Install Failed) Subpack\nFile not found: `{subpack_install_script}`")
|
||||
|
||||
# Download model
|
||||
print("### ComfyUI-Impact-Pack: Check basic models")
|
||||
sam_path = os.path.join(model_path, "sams")
|
||||
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)
|
||||
|
||||
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(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(onnx_path):
|
||||
print(f"### ComfyUI-Impact-Pack: onnx model directory created ({onnx_path})")
|
||||
os.mkdir(onnx_path)
|
||||
|
||||
impact.config.write_config()
|
||||
|
||||
install()
|
||||
|
||||
except Exception as e:
|
||||
print("[ERROR] ComfyUI-Impact-Pack: Dependency installation has failed. Please install manually.")
|
||||
traceback.print_exc()
|
||||
+13
-10
@@ -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})")
|
||||
|
||||
+42
-40
@@ -353,7 +353,7 @@ app.registerExtension({
|
||||
}
|
||||
|
||||
this.outputs[0].type = origin_type;
|
||||
this.outputs[0].name = origin_type;
|
||||
this.outputs[0].name = 'output1';
|
||||
}
|
||||
|
||||
return;
|
||||
@@ -383,7 +383,7 @@ app.registerExtension({
|
||||
}
|
||||
|
||||
let select_slot = this.inputs.find(x => x.name == "select");
|
||||
if(this.widgets) {
|
||||
if(this.widgets?.length) {
|
||||
this.widgets[0].options.max = select_slot?this.outputs.length-1:this.outputs.length;
|
||||
this.widgets[0].value = Math.min(this.widgets[0].value, this.widgets[0].options.max);
|
||||
if(this.widgets[0].options.max > 0 && this.widgets[0].value == 0)
|
||||
@@ -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;
|
||||
@@ -537,7 +541,7 @@ app.registerExtension({
|
||||
this.addInput(`${input_name}${slot_i}`, this.outputs[0].type);
|
||||
}
|
||||
|
||||
if(this.widgets) {
|
||||
if(this.widgets?.length) {
|
||||
this.widgets[0].options.max = select_slot?this.inputs.length-1:this.inputs.length;
|
||||
this.widgets[0].value = Math.min(this.widgets[0].value, this.widgets[0].options.max);
|
||||
if(this.widgets[0].options.max > 0 && this.widgets[0].value == 0)
|
||||
@@ -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"; }
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -20,7 +20,8 @@ class PreviewBridge:
|
||||
"image": ("STRING", {"default": ""}),
|
||||
},
|
||||
"optional": {
|
||||
"block": ("BOOLEAN", {"default": False, "label_on": "if_empty_mask", "label_off": "never", "tooltip": "is_empty_mask: If the mask is empty, the execution is stopped.\nnever: The execution is never stopped."})
|
||||
"block": ("BOOLEAN", {"default": False, "label_on": "if_empty_mask", "label_off": "never", "tooltip": "is_empty_mask: If the mask is empty, the execution is stopped.\nnever: The execution is never stopped."}),
|
||||
"restore_mask": (["never", "always", "if_same_size"], {"tooltip": "if_same_size: If the changed input image is the same size as the previous image, restore using the last saved mask\nalways: Whenever the input image changes, always restore using the last saved mask\nnever: Do not restore the mask.\n`restore_mask` has higher priority than `block`"}),
|
||||
},
|
||||
"hidden": {"unique_id": "UNIQUE_ID", "extra_pnginfo": "EXTRA_PNGINFO"},
|
||||
}
|
||||
@@ -75,7 +76,7 @@ class PreviewBridge:
|
||||
|
||||
return image, mask.unsqueeze(0), ui_item
|
||||
|
||||
def doit(self, images, image, unique_id, block=False, prompt=None, extra_pnginfo=None):
|
||||
def doit(self, images, image, unique_id, block=False, restore_mask="never", prompt=None, extra_pnginfo=None):
|
||||
need_refresh = False
|
||||
|
||||
if unique_id not in core.preview_bridge_cache:
|
||||
@@ -88,10 +89,25 @@ class PreviewBridge:
|
||||
pixels, mask, path_item = PreviewBridge.load_image(image)
|
||||
image = [path_item]
|
||||
else:
|
||||
res = nodes.PreviewImage().save_images(images, filename_prefix="PreviewBridge/PB-", prompt=prompt, extra_pnginfo=extra_pnginfo)
|
||||
if restore_mask != "never":
|
||||
mask = core.preview_bridge_last_mask_cache.get(unique_id)
|
||||
if mask is None or (restore_mask != "always" and mask.shape[1:] != images.shape[1:3]):
|
||||
mask = None
|
||||
else:
|
||||
mask = None
|
||||
|
||||
if mask is None:
|
||||
mask = torch.zeros((64, 64), dtype=torch.float32, device="cpu")
|
||||
res = nodes.PreviewImage().save_images(images, filename_prefix="PreviewBridge/PB-", prompt=prompt, extra_pnginfo=extra_pnginfo)
|
||||
else:
|
||||
masked_images = tensor_convert_rgba(images)
|
||||
resized_mask = resize_mask(mask, (images.shape[1], images.shape[2])).unsqueeze(3)
|
||||
resized_mask = 1 - resized_mask
|
||||
tensor_putalpha(masked_images, resized_mask)
|
||||
res = nodes.PreviewImage().save_images(masked_images, filename_prefix="PreviewBridge/PB-", prompt=prompt, extra_pnginfo=extra_pnginfo)
|
||||
|
||||
image2 = res['ui']['images']
|
||||
pixels = images
|
||||
mask = torch.zeros((64, 64), dtype=torch.float32, device="cpu")
|
||||
|
||||
path = os.path.join(folder_paths.get_temp_directory(), 'PreviewBridge', image2[0]['filename'])
|
||||
core.set_previewbridge_image(unique_id, path, image2[0])
|
||||
@@ -103,7 +119,7 @@ class PreviewBridge:
|
||||
|
||||
is_empty_mask = torch.all(mask == 0)
|
||||
|
||||
if block and is_empty_mask and core.is_execution_model_version_supported:
|
||||
if block and is_empty_mask and core.is_execution_model_version_supported():
|
||||
from comfy_execution.graph import ExecutionBlocker
|
||||
result = ExecutionBlocker(None), ExecutionBlocker(None)
|
||||
elif block and is_empty_mask:
|
||||
@@ -112,6 +128,9 @@ class PreviewBridge:
|
||||
else:
|
||||
result = pixels, mask
|
||||
|
||||
if not is_empty_mask:
|
||||
core.preview_bridge_last_mask_cache[unique_id] = mask
|
||||
|
||||
return {
|
||||
"ui": {"images": image},
|
||||
"result": result,
|
||||
@@ -167,6 +186,9 @@ def decode_latent(latent, preview_method, vae_opt=None):
|
||||
elif preview_method == "Latent2RGB-FLUX.1":
|
||||
latent_format = latent_formats.Flux()
|
||||
method = LatentPreviewMethod.Latent2RGB
|
||||
elif preview_method == "Latent2RGB-LTXV":
|
||||
latent_format = latent_formats.LTXV()
|
||||
method = LatentPreviewMethod.Latent2RGB
|
||||
else:
|
||||
print(f"[Impact Pack] PreviewBridgeLatent: '{preview_method}' is unsupported preview method.")
|
||||
latent_format = latent_formats.SD15()
|
||||
@@ -192,11 +214,13 @@ class PreviewBridgeLatent:
|
||||
"Latent2RGB-SDXL", "Latent2RGB-SD15", "Latent2RGB-SD3",
|
||||
"Latent2RGB-SD-X4", "Latent2RGB-Playground-2.5",
|
||||
"Latent2RGB-SC-Prior", "Latent2RGB-SC-B",
|
||||
"Latent2RGB-LTXV",
|
||||
"TAEF1", "TAESDXL", "TAESD15", "TAESD3"],),
|
||||
},
|
||||
"optional": {
|
||||
"vae_opt": ("VAE", ),
|
||||
"block": ("BOOLEAN", {"default": False, "label_on": "if_empty_mask", "label_off": "never", "tooltip": "is_empty_mask: If the mask is empty, the execution is stopped.\nnever: The execution is never stopped. Instead, it returns a white mask."})
|
||||
"block": ("BOOLEAN", {"default": False, "label_on": "if_empty_mask", "label_off": "never", "tooltip": "is_empty_mask: If the mask is empty, the execution is stopped.\nnever: The execution is never stopped. Instead, it returns a white mask."}),
|
||||
"restore_mask": (["never", "always", "if_same_size"], {"tooltip": "if_same_size: If the changed input latent is the same size as the previous latent, restore using the last saved mask\nalways: Whenever the input latent changes, always restore using the last saved mask\nnever: Do not restore the mask.\n`restore_mask` has higher priority than `block`\nIf the input latent already has a mask, do not restore mask."}),
|
||||
},
|
||||
"hidden": {"unique_id": "UNIQUE_ID", "prompt": "PROMPT", "extra_pnginfo": "EXTRA_PNGINFO"},
|
||||
}
|
||||
@@ -252,9 +276,15 @@ class PreviewBridgeLatent:
|
||||
|
||||
return image, mask, ui_item
|
||||
|
||||
def doit(self, latent, image, preview_method, vae_opt=None, block=False, unique_id=None, prompt=None, extra_pnginfo=None):
|
||||
def doit(self, latent, image, preview_method, vae_opt=None, block=False, unique_id=None, restore_mask='never', prompt=None, extra_pnginfo=None):
|
||||
latent_channels = latent['samples'].shape[1]
|
||||
preview_method_channels = 16 if 'SD3' in preview_method or 'SC-Prior' in preview_method or 'FLUX.1' in preview_method or 'TAEF1' == preview_method else 4
|
||||
|
||||
if 'SD3' in preview_method or 'SC-Prior' in preview_method or 'FLUX.1' in preview_method or 'TAEF1' == preview_method:
|
||||
preview_method_channels = 16
|
||||
elif 'LTXV' in preview_method:
|
||||
preview_method_channels = 128
|
||||
else:
|
||||
preview_method_channels = 4
|
||||
|
||||
if vae_opt is None and latent_channels != preview_method_channels:
|
||||
print(f"[PreviewBridgeLatent] The version of latent is not compatible with preview_method.\nSD3, SD1/SD2, SDXL, SC-Prior, SC-B and FLUX.1 are not compatible with each other.")
|
||||
@@ -311,13 +341,28 @@ class PreviewBridgeLatent:
|
||||
'type': 'temp',
|
||||
}]
|
||||
|
||||
is_empty_mask = torch.all(mask == 1)
|
||||
is_empty_mask = False
|
||||
else:
|
||||
mask = torch.ones(latent['samples'].shape[2:], dtype=torch.float32, device="cpu").unsqueeze(0)
|
||||
res = nodes.PreviewImage().save_images(decoded_image, filename_prefix="PreviewBridge/PBL-", prompt=prompt, extra_pnginfo=extra_pnginfo)
|
||||
if restore_mask != "never":
|
||||
mask = core.preview_bridge_last_mask_cache.get(unique_id)
|
||||
if mask is None or (restore_mask != "always" and mask.shape[1:] != decoded_image.shape[1:3]):
|
||||
mask = None
|
||||
else:
|
||||
mask = None
|
||||
|
||||
if mask is None:
|
||||
mask = torch.ones(latent['samples'].shape[2:], dtype=torch.float32, device="cpu").unsqueeze(0)
|
||||
res = nodes.PreviewImage().save_images(decoded_image, filename_prefix="PreviewBridge/PBL-", prompt=prompt, extra_pnginfo=extra_pnginfo)
|
||||
else:
|
||||
masked_images = tensor_convert_rgba(decoded_image)
|
||||
resized_mask = resize_mask(mask, (decoded_image.shape[1], decoded_image.shape[2])).unsqueeze(3)
|
||||
resized_mask = 1 - resized_mask
|
||||
tensor_putalpha(masked_images, resized_mask)
|
||||
res = nodes.PreviewImage().save_images(masked_images, filename_prefix="PreviewBridge/PBL-", prompt=prompt, extra_pnginfo=extra_pnginfo)
|
||||
|
||||
res_image = res['ui']['images']
|
||||
|
||||
is_empty_mask = True
|
||||
is_empty_mask = torch.all(mask == 1)
|
||||
|
||||
path = os.path.join(folder_paths.get_temp_directory(), 'PreviewBridge', res_image[0]['filename'])
|
||||
core.set_previewbridge_image(unique_id, path, res_image[0])
|
||||
@@ -327,7 +372,7 @@ class PreviewBridgeLatent:
|
||||
|
||||
res_latent = latent
|
||||
|
||||
if block and is_empty_mask and core.is_execution_model_version_supported:
|
||||
if block and is_empty_mask and core.is_execution_model_version_supported():
|
||||
from comfy_execution.graph import ExecutionBlocker
|
||||
result = ExecutionBlocker(None), ExecutionBlocker(None)
|
||||
elif block and is_empty_mask:
|
||||
@@ -336,6 +381,9 @@ class PreviewBridgeLatent:
|
||||
else:
|
||||
result = res_latent, mask
|
||||
|
||||
if not is_empty_mask:
|
||||
core.preview_bridge_last_mask_cache[unique_id] = mask
|
||||
|
||||
return {
|
||||
"ui": {"images": res_image},
|
||||
"result": result,
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import configparser
|
||||
import os
|
||||
|
||||
version_code = [7, 7]
|
||||
version_code = [7, 14, 2]
|
||||
version = f"V{version_code[0]}.{version_code[1]}" + (f'.{version_code[2]}' if len(version_code) > 2 else '')
|
||||
|
||||
dependency_version = 22
|
||||
dependency_version = 24
|
||||
|
||||
my_path = os.path.dirname(__file__)
|
||||
old_config_path = os.path.join(my_path, "impact-pack.ini")
|
||||
|
||||
+56
-22
@@ -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
|
||||
@@ -24,6 +25,8 @@ from comfy import model_management
|
||||
from impact import utils
|
||||
from impact import impact_sampling
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
import inspect
|
||||
|
||||
|
||||
try:
|
||||
from comfy_extras import nodes_differential_diffusion
|
||||
@@ -39,10 +42,13 @@ SEG = namedtuple("SEG",
|
||||
pb_id_cnt = time.time()
|
||||
preview_bridge_image_id_map = {}
|
||||
preview_bridge_image_name_map = {}
|
||||
|
||||
preview_bridge_cache = {}
|
||||
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():
|
||||
@@ -64,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
|
||||
@@ -314,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:
|
||||
@@ -1349,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]
|
||||
|
||||
@@ -1373,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)
|
||||
@@ -1392,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)
|
||||
@@ -1413,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)
|
||||
@@ -1445,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)
|
||||
@@ -1825,13 +1848,14 @@ class ControlNetWrapper:
|
||||
|
||||
class ControlNetAdvancedWrapper:
|
||||
def __init__(self, control_net, strength, start_percent, end_percent, preprocessor, prev_control_net=None,
|
||||
original_size=None, crop_region=None, control_image=None):
|
||||
original_size=None, crop_region=None, control_image=None, vae=None):
|
||||
self.control_net = control_net
|
||||
self.strength = strength
|
||||
self.preprocessor = preprocessor
|
||||
self.prev_control_net = prev_control_net
|
||||
self.start_percent = start_percent
|
||||
self.end_percent = end_percent
|
||||
self.vae = vae
|
||||
|
||||
if original_size is not None and crop_region is not None and control_image is not None:
|
||||
self.control_image = utils.tensor_resize(control_image, original_size[1], original_size[0])
|
||||
@@ -1872,7 +1896,17 @@ class ControlNetAdvancedWrapper:
|
||||
"To use 'ControlNetAdvancedWrapper' for AnimateDiff, 'ComfyUI-Advanced-ControlNet' extension is required.")
|
||||
raise Exception("'ACN_AdvancedControlNetApply' node isn't installed.")
|
||||
else:
|
||||
positive, negative = nodes.ControlNetApplyAdvanced().apply_controlnet(positive, negative, self.control_net, cnet_image, self.strength, self.start_percent, self.end_percent)
|
||||
if self.vae is not None:
|
||||
apply_controlnet = nodes.ControlNetApplyAdvanced().apply_controlnet
|
||||
signature = inspect.signature(apply_controlnet)
|
||||
|
||||
if 'vae' in signature.parameters:
|
||||
positive, negative = nodes.ControlNetApplyAdvanced().apply_controlnet(positive, negative, self.control_net, cnet_image, self.strength, self.start_percent, self.end_percent, vae=self.vae)
|
||||
else:
|
||||
print(f"[Impact Pack] ERROR: The ComfyUI version is outdated. VAE cannot be used in ApplyControlNet.")
|
||||
raise Exception("[Impact Pack] ERROR: The ComfyUI version is outdated. VAE cannot be used in ApplyControlNet.")
|
||||
else:
|
||||
positive, negative = nodes.ControlNetApplyAdvanced().apply_controlnet(positive, negative, self.control_net, cnet_image, self.strength, self.start_percent, self.end_percent)
|
||||
|
||||
return positive, negative, cnet_image_list
|
||||
|
||||
|
||||
@@ -2027,7 +2027,12 @@ class LatentSender(nodes.SaveLatent):
|
||||
"samples": ("LATENT", ),
|
||||
"filename_prefix": ("STRING", {"default": "latents/LatentSender"}),
|
||||
"link_id": ("INT", {"default": 0, "min": 0, "max": sys.maxsize, "step": 1}),
|
||||
"preview_method": (["Latent2RGB-SDXL", "Latent2RGB-SD15", "TAESDXL", "TAESD15"],)
|
||||
"preview_method": (["Latent2RGB-FLUX.1",
|
||||
"Latent2RGB-SDXL", "Latent2RGB-SD15", "Latent2RGB-SD3",
|
||||
"Latent2RGB-SD-X4", "Latent2RGB-Playground-2.5",
|
||||
"Latent2RGB-SC-Prior", "Latent2RGB-SC-B",
|
||||
"Latent2RGB-LTXV",
|
||||
"TAEF1", "TAESDXL", "TAESD15", "TAESD3"],)
|
||||
},
|
||||
"hidden": {"prompt": "PROMPT", "extra_pnginfo": "EXTRA_PNGINFO"},
|
||||
}
|
||||
@@ -2069,14 +2074,33 @@ class LatentSender(nodes.SaveLatent):
|
||||
if preview_method == "Latent2RGB-SD15":
|
||||
latent_format = latent_formats.SD15()
|
||||
method = LatentPreviewMethod.Latent2RGB
|
||||
elif preview_method == "TAESD15":
|
||||
elif preview_method == "Latent2RGB-SDXL":
|
||||
latent_format = latent_formats.SDXL()
|
||||
method = LatentPreviewMethod.Latent2RGB
|
||||
elif preview_method == "Latent2RGB-SD3":
|
||||
latent_format = latent_formats.SD3()
|
||||
method = LatentPreviewMethod.Latent2RGB
|
||||
elif preview_method == "Latent2RGB-SD-X4":
|
||||
latent_format = latent_formats.SD_X4()
|
||||
method = LatentPreviewMethod.Latent2RGB
|
||||
elif preview_method == "Latent2RGB-Playground-2.5":
|
||||
latent_format = latent_formats.SDXL_Playground_2_5()
|
||||
method = LatentPreviewMethod.Latent2RGB
|
||||
elif preview_method == "Latent2RGB-SC-Prior":
|
||||
latent_format = latent_formats.SC_Prior()
|
||||
method = LatentPreviewMethod.Latent2RGB
|
||||
elif preview_method == "Latent2RGB-SC-B":
|
||||
latent_format = latent_formats.SC_B()
|
||||
method = LatentPreviewMethod.Latent2RGB
|
||||
elif preview_method == "Latent2RGB-FLUX.1":
|
||||
latent_format = latent_formats.Flux()
|
||||
method = LatentPreviewMethod.Latent2RGB
|
||||
elif preview_method == "Latent2RGB-LTXV":
|
||||
latent_format = latent_formats.LTXV()
|
||||
method = LatentPreviewMethod.Latent2RGB
|
||||
else:
|
||||
print(f"[Impact Pack] LatentSender: '{preview_method}' is unsupported preview method.")
|
||||
latent_format = latent_formats.SD15()
|
||||
method = LatentPreviewMethod.TAESD
|
||||
elif preview_method == "TAESDXL":
|
||||
latent_format = latent_formats.SDXL()
|
||||
method = LatentPreviewMethod.TAESD
|
||||
else: # preview_method == "Latent2RGB-SDXL"
|
||||
latent_format = latent_formats.SDXL()
|
||||
method = LatentPreviewMethod.Latent2RGB
|
||||
|
||||
previewer = core.get_previewer("cpu", latent_format=latent_format, force=True, method=method)
|
||||
@@ -2150,16 +2174,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 +2205,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 +2245,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"
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -409,9 +409,14 @@ def gc_preview_bridge_cache(json_data):
|
||||
|
||||
for key in list(core.preview_bridge_cache.keys()):
|
||||
if key not in prompt_keys:
|
||||
print(f"key deleted: {key}")
|
||||
# print(f"key deleted [PB]: {key}")
|
||||
del core.preview_bridge_cache[key]
|
||||
|
||||
for key in list(core.preview_bridge_last_mask_cache.keys()):
|
||||
if key not in prompt_keys:
|
||||
# print(f"key deleted [PB_last_mask]: {key}")
|
||||
del core.preview_bridge_last_mask_cache[key]
|
||||
|
||||
|
||||
def workflow_imagereceiver_update(json_data):
|
||||
prompt = json_data['prompt']
|
||||
|
||||
@@ -272,6 +272,24 @@ class ImpactFloat:
|
||||
return (value, )
|
||||
|
||||
|
||||
class ImpactBoolean:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"value": ("BOOLEAN", {"default": False}),
|
||||
},
|
||||
}
|
||||
|
||||
FUNCTION = "doit"
|
||||
CATEGORY = "ImpactPack/Logic"
|
||||
|
||||
RETURN_TYPES = ("BOOLEAN", )
|
||||
|
||||
def doit(self, value):
|
||||
return (value, )
|
||||
|
||||
|
||||
class ImpactValueSender:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
@@ -673,7 +691,7 @@ class ImpactControlBridge:
|
||||
def doit(self, value, mode, behavior="Stop", unique_id=None, prompt=None, extra_pnginfo=None):
|
||||
global error_skip_flag
|
||||
|
||||
if core.is_execution_model_version_supported:
|
||||
if core.is_execution_model_version_supported():
|
||||
from comfy_execution.graph import ExecutionBlocker
|
||||
else:
|
||||
print("[Impact Pack] ImpactControlBridge: ComfyUI is outdated. The 'Stop' behavior cannot function properly.")
|
||||
@@ -748,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
|
||||
|
||||
|
||||
|
||||
@@ -704,6 +704,68 @@ class SEGSToMaskBatch:
|
||||
return (mask_batch,)
|
||||
|
||||
|
||||
class SEGSMerge:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {"required": {
|
||||
"segs": ("SEGS", ),
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("SEGS",)
|
||||
FUNCTION = "doit"
|
||||
|
||||
CATEGORY = "ImpactPack/Util"
|
||||
|
||||
DESCRIPTION = "SEGS contains multiple SEGs. SEGS Merge integrates several SEGs into a single merged SEG. The label is changed to `merged` and the confidence becomes the minimum confidence. The applied controlnet and cropped_image are removed."
|
||||
|
||||
def doit(self, segs):
|
||||
crop_left = sys.maxsize
|
||||
crop_right = 0
|
||||
crop_top = sys.maxsize
|
||||
crop_bottom = 0
|
||||
|
||||
bbox_left = sys.maxsize
|
||||
bbox_right = 0
|
||||
bbox_top = sys.maxsize
|
||||
bbox_bottom = 0
|
||||
|
||||
min_confidence = 1.0
|
||||
|
||||
for seg in segs[1]:
|
||||
cx1 = seg.crop_region[0]
|
||||
cy1 = seg.crop_region[1]
|
||||
cx2 = seg.crop_region[2]
|
||||
cy2 = seg.crop_region[3]
|
||||
|
||||
bx1 = seg.bbox[0]
|
||||
by1 = seg.bbox[1]
|
||||
bx2 = seg.bbox[2]
|
||||
by2 = seg.bbox[3]
|
||||
|
||||
crop_left = min(crop_left, cx1)
|
||||
crop_top = min(crop_top, cy1)
|
||||
crop_right = max(crop_right, cx2)
|
||||
crop_bottom = max(crop_bottom, cy2)
|
||||
|
||||
bbox_left = min(bbox_left, bx1)
|
||||
bbox_top = min(bbox_top, by1)
|
||||
bbox_right = max(bbox_right, bx2)
|
||||
bbox_bottom = max(bbox_bottom, by2)
|
||||
|
||||
min_confidence = min(min_confidence, seg.confidence)
|
||||
|
||||
combined_mask = core.segs_to_combined_mask(segs)
|
||||
cropped_mask = combined_mask[crop_top:crop_bottom, crop_left:crop_right]
|
||||
cropped_mask = cropped_mask.unsqueeze(0)
|
||||
|
||||
crop_region = [crop_left, crop_top, crop_right, crop_bottom]
|
||||
bbox = [bbox_left, bbox_top, bbox_right, bbox_bottom]
|
||||
|
||||
seg = SEG(None, cropped_mask, min_confidence, crop_region, bbox, 'merged', None)
|
||||
return ((segs[0], [seg]),)
|
||||
|
||||
|
||||
class SEGSConcat:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
@@ -834,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:
|
||||
@@ -1039,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
|
||||
@@ -1300,6 +1362,8 @@ class ControlNetApplySEGS:
|
||||
RETURN_TYPES = ("SEGS",)
|
||||
FUNCTION = "doit"
|
||||
|
||||
DEPRECATED = True
|
||||
|
||||
CATEGORY = "ImpactPack/Util"
|
||||
|
||||
@staticmethod
|
||||
@@ -1327,7 +1391,8 @@ class ControlNetApplyAdvancedSEGS:
|
||||
},
|
||||
"optional": {
|
||||
"segs_preprocessor": ("SEGS_PREPROCESSOR",),
|
||||
"control_image": ("IMAGE",)
|
||||
"control_image": ("IMAGE",),
|
||||
"vae": ("VAE",)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1337,13 +1402,13 @@ class ControlNetApplyAdvancedSEGS:
|
||||
CATEGORY = "ImpactPack/Util"
|
||||
|
||||
@staticmethod
|
||||
def doit(segs, control_net, strength, start_percent, end_percent, segs_preprocessor=None, control_image=None):
|
||||
def doit(segs, control_net, strength, start_percent, end_percent, segs_preprocessor=None, control_image=None, vae=None):
|
||||
new_segs = []
|
||||
|
||||
for seg in segs[1]:
|
||||
control_net_wrapper = core.ControlNetAdvancedWrapper(control_net, strength, start_percent, end_percent, segs_preprocessor,
|
||||
seg.control_net_wrapper, original_size=segs[0], crop_region=seg.crop_region,
|
||||
control_image=control_image)
|
||||
control_image=control_image, vae=vae)
|
||||
new_seg = SEG(seg.cropped_image, seg.cropped_mask, seg.confidence, seg.crop_region, seg.bbox, seg.label, control_net_wrapper)
|
||||
new_segs.append(new_seg)
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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"}),
|
||||
@@ -45,7 +52,10 @@ class GeneralSwitch:
|
||||
|
||||
print(f"SELECTED: {input_name}")
|
||||
|
||||
return [input_name]
|
||||
if input_name in kwargs:
|
||||
return [input_name]
|
||||
else:
|
||||
return []
|
||||
|
||||
@staticmethod
|
||||
def doit(*args, **kwargs):
|
||||
@@ -55,6 +65,13 @@ class GeneralSwitch:
|
||||
selected_label = input_name
|
||||
node_id = kwargs['unique_id']
|
||||
|
||||
if input_name not in kwargs:
|
||||
if core.is_execution_model_version_supported():
|
||||
from comfy_execution.graph import ExecutionBlocker
|
||||
return ExecutionBlocker(None), selected_label, selected_index
|
||||
else:
|
||||
print("[Impact Pack] ImpactSwitch: ComfyUI is outdated. Cannot block empty selection.")
|
||||
|
||||
if 'extra_pnginfo' in kwargs and kwargs['extra_pnginfo'] is not None:
|
||||
nodelist = kwargs['extra_pnginfo']['workflow']['nodes']
|
||||
for node in nodelist:
|
||||
@@ -163,7 +180,7 @@ class GeneralInversedSwitch:
|
||||
CATEGORY = "ImpactPack/Util"
|
||||
|
||||
def doit(self, select, prompt, unique_id, input, **kwargs):
|
||||
if core.is_execution_model_version_supported:
|
||||
if core.is_execution_model_version_supported():
|
||||
from comfy_execution.graph import ExecutionBlocker
|
||||
else:
|
||||
print("[Impact Pack] InversedSwitch: ComfyUI is outdated. The 'select_on_execution' mode cannot function properly.")
|
||||
@@ -181,7 +198,7 @@ class GeneralInversedSwitch:
|
||||
for i in range(0, cnt + 1):
|
||||
if select == i+1:
|
||||
res.append(input)
|
||||
elif core.is_execution_model_version_supported:
|
||||
elif core.is_execution_model_version_supported():
|
||||
res.append(ExecutionBlocker(None))
|
||||
else:
|
||||
res.append(None)
|
||||
@@ -366,7 +383,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 +511,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,)
|
||||
|
||||
@@ -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
@@ -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.7"
|
||||
version = "7.14.2"
|
||||
license = { file = "LICENSE.txt" }
|
||||
dependencies = ["segment-anything", "scikit-image", "piexif", "transformers", "opencv-python-headless", "GitPython", "scipy>=1.11.4"]
|
||||
|
||||
|
||||
Reference in New Issue
Block a user