Compare commits

...
32 Commits
Author SHA1 Message Date
Dr.Lt.Data 64f709741a The Impact Subpack must now be installed separately. 2024-12-11 21:56:33 +09:00
Dr.Lt.Data 455e993354 Merge branch 'Main' into refactor/subpack 2024-12-11 21:54:51 +09:00
Dr.Lt.Data ce23d436fc improve: switch - disconnect output connection instead of input connection if select_on_prompt 2024-12-11 05:25:01 +09:00
Dr.Lt.Data 5f630466fb fixed: block if empty slot is selected and select_on_prompt
https://github.com/ltdrdata/ComfyUI-Impact-Pack/issues/846
2024-12-11 03:36:12 +09:00
Dr.Lt.Data 81045cc845 fixed: remove from lazy input if empty slot is selected.
https://github.com/ltdrdata/ComfyUI-Impact-Pack/issues/846
2024-12-11 03:25:29 +09:00
Ikko Eltociear Ashimine 20f2cac6f0 docs: update README.md (#844)
minor fix
2024-12-06 05:10:09 +09:00
Dr.Lt.Data 348c3dcb6b improve: PreviewBridgeLatent - add Latent2RGB-LTXV
improve: LatentSender - add multiple latent formats
2024-12-01 02:02:05 +09:00
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
Dr.Lt.Data 66bf101341 Merge branch 'Main' into refactor/subpack 2024-11-02 05:39:28 +09:00
Vedat Baday 825296c3e0 fix: install manual (#794) 2024-11-02 05:39:08 +09:00
Dr.Lt.Data 5c0535942a Merge branch 'Main' into refactor/subpack 2024-11-01 02:31:49 +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 6be257254e Separate Impact Pack and Impact Subpack 2024-11-01 02:25:14 +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
Dr.Lt.Data 759b4d0dc9 fix: invalid version check code
https://github.com/ltdrdata/ComfyUI-Impact-Pack/issues/782
2024-10-15 23:34:25 +09:00
19 changed files with 278 additions and 322 deletions
+6 -14
View File
@@ -2,11 +2,13 @@
# ComfyUI-Impact-Pack # ComfyUI-Impact-Pack
**Custom nodes pack for ComfyUI** **Custom node pack for ComfyUI**
This custom node helps to conveniently enhance images through Detector, Detailer, Upscaler, Pipe, and more. This node pack helps to conveniently enhance images through Detector, Detailer, Upscaler, Pipe, and more.
NOTE: The UltralyticsDetectorProvider node is not part of the ComfyUI-Impact-Pack. To use the UltralyticsDetectorProvider node, please install the ComfyUI-Impact-Subpack separately.
## NOTICE ## NOTICE
* V8.0: The `Impact Subpack` is no longer installed automatically. To use `UltralyticsDetectorProvider` nodes, please install the `Impact Subpack` separately.
* V7.6: Automatic installation is no longer supported. Please install using ComfyUI-Manager, or manually install requirements.txt and run install.py to complete the installation. * V7.6: Automatic installation is no longer supported. Please install using ComfyUI-Manager, or manually install requirements.txt and run install.py to complete the installation.
* V7.0: Supports Switch based on Execution Model Inversion. * V7.0: Supports Switch based on Execution Model Inversion.
* V6.0: Supports FLUX.1 model in Impact KSampler, Detailers, PreviewBridgeLatent * V6.0: Supports FLUX.1 model in Impact KSampler, Detailers, PreviewBridgeLatent
@@ -33,9 +35,6 @@ This custom node helps to conveniently enhance images through Detector, Detailer
## Custom Nodes ## Custom Nodes
### [Detector nodes](https://github.com/ltdrdata/ComfyUI-extension-tutorials/blob/Main/ComfyUI-Impact-Pack/tutorial/detectors.md) ### [Detector nodes](https://github.com/ltdrdata/ComfyUI-extension-tutorials/blob/Main/ComfyUI-Impact-Pack/tutorial/detectors.md)
* `SAMLoader` - Loads the SAM model. * `SAMLoader` - Loads the SAM model.
* `UltralyticsDetectorProvider` - Loads the Ultralystics model to provide SEGM_DETECTOR, BBOX_DETECTOR.
- Unlike `MMDetDetectorProvider`, for segm models, `BBOX_DETECTOR` is also provided.
- The various models available in UltralyticsDetectorProvider can be downloaded through **ComfyUI-Manager**.
* `ONNXDetectorProvider` - Loads the ONNX model to provide BBOX_DETECTOR. * `ONNXDetectorProvider` - Loads the ONNX model to provide BBOX_DETECTOR.
* `CLIPSegDetectorProvider` - Wrapper for CLIPSeg to provide BBOX_DETECTOR. * `CLIPSegDetectorProvider` - Wrapper for CLIPSeg to provide BBOX_DETECTOR.
* You need to install the ComfyUI-CLIPSeg node extension. * You need to install the ComfyUI-CLIPSeg node extension.
@@ -49,7 +48,7 @@ This custom node helps to conveniently enhance images through Detector, Detailer
### ControlNet, IPAdapter ### 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. * `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. * 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)`. * 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 * `ControlNetClear (SEGS)` - Clear applied ControlNet in SEGS
@@ -286,6 +285,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. * `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. * `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. * 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 ## MMDet nodes (DEPRECATED) - Don't use these nodes
@@ -311,12 +311,6 @@ This custom node helps to conveniently enhance images through Detector, Detailer
* To use the existing deprecated legacy nodes, you need to enable the MMDet usage configuration. * To use the existing deprecated legacy nodes, you need to enable the MMDet usage configuration.
## Ultralytics models
* When using ultralytics models, save them separately in `models/ultralytics/bbox` and `models/ultralytics/segm` depending on the type of model. Many models can be downloaded by searching for `ultralytics` in the Model Manager of ComfyUI-Manager.
* huggingface.co/Bingsu/[adetailer](https://huggingface.co/Bingsu/adetailer/tree/main) - You can download face, people detection models, and clothing detection models.
* ultralytics/[assets](https://github.com/ultralytics/assets/releases/) - You can download various types of detection models other than faces or people.
* civitai/[adetailer](https://civitai.com/search/models?sortBy=models_v5&query=adetailer) - You can download various types detection models....Many models are associated with NSFW content.
## How to activate 'MMDet usage' (DEPRECATED) ## How to activate 'MMDet usage' (DEPRECATED)
* Upon the initial execution, an `impact-pack.ini` file will be generated in the custom_nodes/ComfyUI-Impact-Pack directory. * Upon the initial execution, an `impact-pack.ini` file will be generated in the custom_nodes/ComfyUI-Impact-Pack directory.
``` ```
@@ -358,11 +352,9 @@ mmdet_skip = False
* pip install * pip install
* segment-anything * segment-anything
* ultralytics
* scikit-image * scikit-image
* piexif * piexif
* opencv-python * opencv-python
* GitPython
* scipy * scipy
* numpy<2 * numpy<2
* dill * dill
+2 -19
View File
@@ -13,7 +13,6 @@ import traceback
comfy_path = os.path.dirname(folder_paths.__file__) comfy_path = os.path.dirname(folder_paths.__file__)
impact_path = os.path.join(os.path.dirname(__file__)) impact_path = os.path.join(os.path.dirname(__file__))
subpack_path = os.path.join(os.path.dirname(__file__), "impact_subpack")
modules_path = os.path.join(os.path.dirname(__file__), "modules") modules_path = os.path.join(os.path.dirname(__file__), "modules")
sys.path.append(modules_path) sys.path.append(modules_path)
@@ -22,14 +21,9 @@ import impact.config
import impact.sample_error_enhancer import impact.sample_error_enhancer
print(f"### Loading: ComfyUI-Impact-Pack ({impact.config.version})") print(f"### Loading: ComfyUI-Impact-Pack ({impact.config.version})")
sys.path.append(subpack_path)
# Core # Core
# recheck dependencies for colab # recheck dependencies for colab
try: try:
import impact.subpack_nodes # This import must be done before cv2.
import folder_paths import folder_paths
import torch import torch
import cv2 import cv2
@@ -273,6 +267,7 @@ NODE_CLASS_MAPPINGS = {
"StringListToString": StringListToString, "StringListToString": StringListToString,
"WildcardPromptFromString": WildcardPromptFromString, "WildcardPromptFromString": WildcardPromptFromString,
"ImpactExecutionOrderController": ImpactExecutionOrderController, "ImpactExecutionOrderController": ImpactExecutionOrderController,
"ImpactListBridge": ImpactListBridge,
"RemoveNoiseMask": RemoveNoiseMask, "RemoveNoiseMask": RemoveNoiseMask,
@@ -393,6 +388,7 @@ NODE_DISPLAY_NAME_MAPPINGS = {
"ImpactSwitch": "Switch (Any)", "ImpactSwitch": "Switch (Any)",
"ImpactInversedSwitch": "Inversed Switch (Any)", "ImpactInversedSwitch": "Inversed Switch (Any)",
"ImpactExecutionOrderController": "Execution Order Controller", "ImpactExecutionOrderController": "Execution Order Controller",
"ImpactListBridge": "List Bridge",
"MasksToMaskList": "Mask Batch to Mask List", "MasksToMaskList": "Mask Batch to Mask List",
"MaskListToMaskBatch": "Mask List to Mask Batch", "MaskListToMaskBatch": "Mask List to Mask Batch",
@@ -463,19 +459,6 @@ if not impact.config.get_config()['mmdet_skip']:
"SegmDetectorCombined": "SegmDetectorCombined (Legacy)", "SegmDetectorCombined": "SegmDetectorCombined (Legacy)",
}) })
try:
import impact.subpack_nodes
NODE_CLASS_MAPPINGS.update(impact.subpack_nodes.NODE_CLASS_MAPPINGS)
NODE_DISPLAY_NAME_MAPPINGS.update(impact.subpack_nodes.NODE_DISPLAY_NAME_MAPPINGS)
except Exception as e:
print("### ComfyUI-Impact-Pack: (IMPORT FAILED) Subpack\n")
print(" The module at the `custom_nodes/ComfyUI-Impact-Pack/impact_subpack` path appears to be incomplete.")
print(" Recommended to delete the path and restart ComfyUI.")
print(" If the issue persists, please report it to https://github.com/ltdrdata/ComfyUI-Impact-Pack/issues.")
print("\n---------------------------------")
traceback.print_exc()
print("---------------------------------\n")
# NOTE: Inject directly into EXTENSION_WEB_DIRS instead of WEB_DIRECTORY # NOTE: Inject directly into EXTENSION_WEB_DIRS instead of WEB_DIRECTORY
# Provide the js path fixed as ComfyUI-Impact-Pack instead of the path name, making it available for external use # Provide the js path fixed as ComfyUI-Impact-Pack instead of the path name, making it available for external use
-144
View File
@@ -1,144 +0,0 @@
import os
import shutil
import sys
import subprocess
import threading
import locale
import traceback
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()
# ---
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()
+21 -41
View File
@@ -12,13 +12,11 @@ if sys.argv[0] == 'install.py':
impact_path = os.path.join(os.path.dirname(__file__), "modules") 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') comfy_path = os.environ.get('COMFYUI_PATH')
if comfy_path is None: 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) print(f"\nWARN: The `COMFYUI_PATH` environment variable is not set. Assuming `{os.path.dirname(__file__)}/../../` as the ComfyUI path.", file=sys.stderr)
comfy_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..')) comfy_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..'))
model_path = os.environ.get('COMFYUI_MODEL_PATH') model_path = os.environ.get('COMFYUI_MODEL_PATH')
@@ -31,7 +29,7 @@ if model_path is None:
if model_path is None: if model_path is None:
model_path = os.path.abspath(os.path.join(comfy_path, 'models')) 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) print(f"\nWARN: The `COMFYUI_MODEL_PATH` environment variable is not set. Assuming `{model_path}` as the ComfyUI path.", file=sys.stderr)
sys.path.append(impact_path) sys.path.append(impact_path)
@@ -76,57 +74,33 @@ try:
import impact.config import impact.config
print("### ComfyUI-Impact-Pack: Check dependencies") 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(): 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 = os.environ.copy()
new_env["COMFYUI_PATH"] = comfy_path new_env["COMFYUI_PATH"] = comfy_path
new_env["COMFYUI_MODEL_PATH"] = model_path new_env["COMFYUI_MODEL_PATH"] = model_path
if os.path.exists(subpack_install_script):
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 # Download model
print("### ComfyUI-Impact-Pack: Check basic models") print("### ComfyUI-Impact-Pack: Check basic models")
sam_path = os.path.join(model_path, "sams") sam_path = os.path.join(model_path, "sams")
onnx_path = os.path.join(model_path, "onnx") 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 os.path.exists(os.path.join(os.path.dirname(__file__), '..', 'skip_download_model')):
if not impact.config.get_config()['mmdet_skip']: try:
bbox_path = os.path.join(model_path, "mmdets", "bbox") if not impact.config.get_config()['mmdet_skip']:
if not os.path.exists(bbox_path): bbox_path = os.path.join(model_path, "mmdets", "bbox")
os.makedirs(bbox_path) 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")): 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) 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")): 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) 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")): 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) 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): if not os.path.exists(onnx_path):
print(f"### ComfyUI-Impact-Pack: onnx model directory created ({onnx_path})") print(f"### ComfyUI-Impact-Pack: onnx model directory created ({onnx_path})")
@@ -134,6 +108,12 @@ try:
impact.config.write_config() impact.config.write_config()
# Remove legacy subpack
subpack_path = os.path.join(os.path.dirname(__file__), 'impact_subpack')
if os.path.exists(subpack_path):
shutil.rmtree(subpack_path)
print(f"Legacy subpack is detected. '{subpack_path}' is removed.")
install() install()
except Exception as e: except Exception as e:
+40 -38
View File
@@ -353,7 +353,7 @@ app.registerExtension({
} }
this.outputs[0].type = origin_type; this.outputs[0].type = origin_type;
this.outputs[0].name = origin_type; this.outputs[0].name = 'output1';
} }
return; return;
@@ -483,8 +483,12 @@ app.registerExtension({
if(this.inputs[0].type == '*'){ if(this.inputs[0].type == '*'){
const node = app.graph.getNodeById(link_info.origin_id); 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 == '*') { if(origin_type == '*') {
this.disconnectInput(link_info.target_slot); this.disconnectInput(link_info.target_slot);
return; return;
@@ -583,17 +587,17 @@ app.registerExtension({
} }
if(node.comfyClass == "ImpactSEGSLabelFilter" || node.comfyClass == "SEGSLabelFilterDetailerHookProvider") { 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", { Object.defineProperty(node.widgets[0], "value", {
set: (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; node._value = value;
}, },
get: () => { get: () => {
@@ -663,18 +667,18 @@ app.registerExtension({
break; 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", { Object.defineProperty(node.widgets[combo_id+1], "value", {
set: (value) => { set: (value) => {
const stackTrace = new Error().stack; if (value !== "Select the Wildcard to add to the text")
if(stackTrace.includes('inner_value_change')) { node._wildcard_value = value;
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;
}
}
},
get: () => { return "Select the Wildcard to add to the text"; } get: () => { return "Select the Wildcard to add to the text"; }
}); });
@@ -686,24 +690,22 @@ app.registerExtension({
}); });
if(has_lora) { 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", { Object.defineProperty(node.widgets[combo_id], "value", {
set: (value) => { set: (value) => {
const stackTrace = new Error().stack; if (value !== "Select the LoRA to add to the text")
if(stackTrace.includes('inner_value_change')) { node._value = value;
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;
}, },
get: () => { return "Select the LoRA to add to the text"; } 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); imgCtx.drawImage(orig_image, 0, 0, drawWidth, drawHeight);
// update mask // update mask
let w = (drawWidth * imgCanvas.clientWidth/imgCanvas.width) + "px";
let h = (drawHeight * imgCanvas.clientHeight/imgCanvas.height) + "px";
pointsCanvas.width = drawWidth; pointsCanvas.width = drawWidth;
pointsCanvas.height = drawHeight; pointsCanvas.height = drawHeight;
pointsCanvas.style.top = imgCanvas.offsetTop + "px"; pointsCanvas.style.top = imgCanvas.offsetTop + "px";
pointsCanvas.style.left = imgCanvas.offsetLeft + "px"; pointsCanvas.style.left = imgCanvas.offsetLeft + "px";
maskCanvas.width = drawWidth; maskCanvas.style.width = w;
maskCanvas.height = drawHeight; maskCanvas.style.height = h;
maskCanvas.style.top = imgCanvas.offsetTop + "px"; maskCanvas.style.top = imgCanvas.offsetTop + "px";
maskCanvas.style.left = imgCanvas.offsetLeft + "px"; maskCanvas.style.left = imgCanvas.offsetLeft + "px";
+13 -3
View File
@@ -119,7 +119,7 @@ class PreviewBridge:
is_empty_mask = torch.all(mask == 0) 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 from comfy_execution.graph import ExecutionBlocker
result = ExecutionBlocker(None), ExecutionBlocker(None) result = ExecutionBlocker(None), ExecutionBlocker(None)
elif block and is_empty_mask: elif block and is_empty_mask:
@@ -186,6 +186,9 @@ def decode_latent(latent, preview_method, vae_opt=None):
elif preview_method == "Latent2RGB-FLUX.1": elif preview_method == "Latent2RGB-FLUX.1":
latent_format = latent_formats.Flux() latent_format = latent_formats.Flux()
method = LatentPreviewMethod.Latent2RGB method = LatentPreviewMethod.Latent2RGB
elif preview_method == "Latent2RGB-LTXV":
latent_format = latent_formats.LTXV()
method = LatentPreviewMethod.Latent2RGB
else: else:
print(f"[Impact Pack] PreviewBridgeLatent: '{preview_method}' is unsupported preview method.") print(f"[Impact Pack] PreviewBridgeLatent: '{preview_method}' is unsupported preview method.")
latent_format = latent_formats.SD15() latent_format = latent_formats.SD15()
@@ -211,6 +214,7 @@ class PreviewBridgeLatent:
"Latent2RGB-SDXL", "Latent2RGB-SD15", "Latent2RGB-SD3", "Latent2RGB-SDXL", "Latent2RGB-SD15", "Latent2RGB-SD3",
"Latent2RGB-SD-X4", "Latent2RGB-Playground-2.5", "Latent2RGB-SD-X4", "Latent2RGB-Playground-2.5",
"Latent2RGB-SC-Prior", "Latent2RGB-SC-B", "Latent2RGB-SC-Prior", "Latent2RGB-SC-B",
"Latent2RGB-LTXV",
"TAEF1", "TAESDXL", "TAESD15", "TAESD3"],), "TAEF1", "TAESDXL", "TAESD15", "TAESD3"],),
}, },
"optional": { "optional": {
@@ -274,7 +278,13 @@ class PreviewBridgeLatent:
def doit(self, latent, image, preview_method, vae_opt=None, block=False, unique_id=None, restore_mask='never', 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] 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: 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.") 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.")
@@ -362,7 +372,7 @@ class PreviewBridgeLatent:
res_latent = latent 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 from comfy_execution.graph import ExecutionBlocker
result = ExecutionBlocker(None), ExecutionBlocker(None) result = ExecutionBlocker(None), ExecutionBlocker(None)
elif block and is_empty_mask: elif block and is_empty_mask:
+2 -2
View File
@@ -1,10 +1,10 @@
import configparser import configparser
import os import os
version_code = [7, 10] version_code = [8, 0]
version = f"V{version_code[0]}.{version_code[1]}" + (f'.{version_code[2]}' if len(version_code) > 2 else '') 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__) my_path = os.path.dirname(__file__)
old_config_path = os.path.join(my_path, "impact-pack.ini") 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 from collections import namedtuple
import numpy as np import numpy as np
from skimage.measure import label from skimage.measure import label
from PIL import ImageOps
import nodes import nodes
import comfy_extras.nodes_upscale_model as model_upscale import comfy_extras.nodes_upscale_model as model_upscale
@@ -47,7 +48,7 @@ preview_bridge_last_mask_cache = {}
current_prompt = None 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(): 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}" pb_id = f"${node_id}-{pb_id_cnt}"
preview_bridge_image_id_map[pb_id] = (file, item) preview_bridge_image_id_map[pb_id] = (file, item)
preview_bridge_image_name_map[node_id, file] = (pb_id, 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 pb_id_cnt += 1
return pb_id return pb_id
@@ -319,7 +327,12 @@ def enhance_detail(image, model, clip, vae, guide_size, guide_size_for_bbox, max
# prepare mask # prepare mask
if noise_mask is not None and inpaint_model: 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: else:
latent_image = to_latent_image(upscaled_image, vae) latent_image = to_latent_image(upscaled_image, vae)
if noise_mask is not None: if noise_mask is not None:
@@ -1354,9 +1367,14 @@ def segs_to_masklist(segs):
return masks 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: 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: else:
pixels = nodes.VAEDecode().decode(vae, samples)[0] pixels = nodes.VAEDecode().decode(vae, samples)[0]
@@ -1378,12 +1396,12 @@ def vae_encode(vae, pixels, use_tile, hook, tile_size=512):
return samples 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): 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)[0] 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): 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) pixels = vae_decode(vae, samples, use_tile, hook, tile_size=tile_size, overlap=overlap)
if save_temp_prefix is not None: if save_temp_prefix is not None:
nodes.PreviewImage().save_images(pixels, filename_prefix=save_temp_prefix) 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) 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): 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)[0] 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): 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) pixels = vae_decode(vae, samples, use_tile, hook, tile_size=tile_size, overlap=overlap)
if save_temp_prefix is not None: if save_temp_prefix is not None:
nodes.PreviewImage().save_images(pixels, filename_prefix=save_temp_prefix) 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) 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): 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)[0] 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): 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) pixels = vae_decode(vae, samples, use_tile, hook, tile_size=tile_size, overlap=overlap)
if save_temp_prefix is not None: if save_temp_prefix is not None:
nodes.PreviewImage().save_images(pixels, filename_prefix=save_temp_prefix) 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, 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): 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)[0] 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, 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): tile_size=512, save_temp_prefix=None, hook=None, overlap=64):
pixels = vae_decode(vae, samples, use_tile, hook, tile_size=tile_size) pixels = vae_decode(vae, samples, use_tile, hook, tile_size=tile_size, overlap=overlap)
if save_temp_prefix is not None: if save_temp_prefix is not None:
nodes.PreviewImage().save_images(pixels, filename_prefix=save_temp_prefix) nodes.PreviewImage().save_images(pixels, filename_prefix=save_temp_prefix)
+53 -17
View File
@@ -225,6 +225,10 @@ class DetailerForEach:
CATEGORY = "ImpactPack/Detailer" CATEGORY = "ImpactPack/Detailer"
@staticmethod
def get_core_module():
return core
@staticmethod @staticmethod
def do_detail(image, segs, model, clip, vae, guide_size, guide_size_for_bbox, max_size, seed, steps, cfg, sampler_name, scheduler, def do_detail(image, segs, model, clip, vae, guide_size, guide_size_for_bbox, max_size, seed, steps, cfg, sampler_name, scheduler,
positive, negative, denoise, feather, noise_mask, force_inpaint, wildcard_opt=None, detailer_hook=None, positive, negative, denoise, feather, noise_mask, force_inpaint, wildcard_opt=None, detailer_hook=None,
@@ -2027,7 +2031,12 @@ class LatentSender(nodes.SaveLatent):
"samples": ("LATENT", ), "samples": ("LATENT", ),
"filename_prefix": ("STRING", {"default": "latents/LatentSender"}), "filename_prefix": ("STRING", {"default": "latents/LatentSender"}),
"link_id": ("INT", {"default": 0, "min": 0, "max": sys.maxsize, "step": 1}), "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"}, "hidden": {"prompt": "PROMPT", "extra_pnginfo": "EXTRA_PNGINFO"},
} }
@@ -2069,14 +2078,33 @@ class LatentSender(nodes.SaveLatent):
if preview_method == "Latent2RGB-SD15": if preview_method == "Latent2RGB-SD15":
latent_format = latent_formats.SD15() latent_format = latent_formats.SD15()
method = LatentPreviewMethod.Latent2RGB 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() 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 method = LatentPreviewMethod.Latent2RGB
previewer = core.get_previewer("cpu", latent_format=latent_format, force=True, method=method) previewer = core.get_previewer("cpu", latent_format=latent_format, force=True, method=method)
@@ -2150,16 +2178,19 @@ class ImpactWildcardProcessor:
@classmethod @classmethod
def INPUT_TYPES(s): def INPUT_TYPES(s):
return {"required": { return {"required": {
"wildcard_text": ("STRING", {"multiline": True, "dynamicPrompts": False}), "wildcard_text": ("STRING", {"multiline": True, "dynamicPrompts": False, "tooltip": "Enter a prompt using wildcard syntax."}),
"populated_text": ("STRING", {"multiline": True, "dynamicPrompts": False}), "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"}), "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}), "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"],), "Select to add Wildcard": (["Select the Wildcard to add to the text"],),
}, },
} }
CATEGORY = "ImpactPack/Prompt" 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", ) RETURN_TYPES = ("STRING", )
FUNCTION = "doit" FUNCTION = "doit"
@@ -2178,17 +2209,22 @@ class ImpactWildcardEncode:
return {"required": { return {"required": {
"model": ("MODEL",), "model": ("MODEL",),
"clip": ("CLIP",), "clip": ("CLIP",),
"wildcard_text": ("STRING", {"multiline": True, "dynamicPrompts": False}), "wildcard_text": ("STRING", {"multiline": True, "dynamicPrompts": False, "tooltip": "Enter a prompt using wildcard syntax."}),
"populated_text": ("STRING", {"multiline": True, "dynamicPrompts": False}), "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"}), "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 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"], ), "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" 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_TYPES = ("MODEL", "CLIP", "CONDITIONING", "STRING")
RETURN_NAMES = ("model", "clip", "conditioning", "populated_text") RETURN_NAMES = ("model", "clip", "conditioning", "populated_text")
FUNCTION = "doit" FUNCTION = "doit"
@@ -2213,7 +2249,7 @@ class ImpactSchedulerAdapter:
def INPUT_TYPES(s): def INPUT_TYPES(s):
return {"required": { return {"required": {
"scheduler": (comfy.samplers.KSampler.SCHEDULERS, {"defaultInput": True, }), "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" 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] sigmas = nodes.NODE_CLASS_MAPPINGS['AlignYourStepsScheduler']().get_sigmas(scheduler[4:], steps, denoise=1.0)[0]
elif scheduler.startswith('GITS[coeff='): elif scheduler.startswith('GITS[coeff='):
sigmas = nodes.NODE_CLASS_MAPPINGS['GITSScheduler']().get_sigmas(float(scheduler[11:-1]), steps, denoise=1.0)[0] 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: else:
sigmas = samplers.calculate_sigmas(model.get_model_object("model_sampling"), scheduler, steps) sigmas = samplers.calculate_sigmas(model.get_model_object("model_sampling"), scheduler, steps)
+9
View File
@@ -315,6 +315,8 @@ def onprompt_for_switch(json_data):
inversed_switch_info = {} inversed_switch_info = {}
onprompt_switch_info = {} onprompt_switch_info = {}
onprompt_cond_branch_info = {} onprompt_cond_branch_info = {}
disabled_switch = set()
for k, v in json_data['prompt'].items(): for k, v in json_data['prompt'].items():
if 'class_type' not in v: if 'class_type' not in v:
@@ -348,6 +350,10 @@ def onprompt_for_switch(json_data):
else: else:
onprompt_switch_info[k] = select_input onprompt_switch_info[k] = select_input
if k in onprompt_switch_info and f'input{onprompt_switch_info[k]}' not in v['inputs']:
# disconnect output
disabled_switch.add(k)
elif cls == 'ImpactConditionalBranchSelMode': elif cls == 'ImpactConditionalBranchSelMode':
if 'sel_mode' in v['inputs'] and v['inputs']['sel_mode'] and 'cond' in v['inputs']: if 'sel_mode' in v['inputs'] and v['inputs']['sel_mode'] and 'cond' in v['inputs']:
cond_input = v['inputs']['cond'] cond_input = v['inputs']['cond']
@@ -372,6 +378,9 @@ def onprompt_for_switch(json_data):
if vv[1] + 1 != inversed_switch_info[vv[0]]: if vv[1] + 1 != inversed_switch_info[vv[0]]:
disable_targets.add(kk) disable_targets.add(kk)
if vv[0] in disabled_switch:
disable_targets.add(kk)
if k in onprompt_switch_info: if k in onprompt_switch_info:
selected_slot_name = f"input{onprompt_switch_info[k]}" selected_slot_name = f"input{onprompt_switch_info[k]}"
for kk, vv in v['inputs'].items(): for kk, vv in v['inputs'].items():
+24 -1
View File
@@ -691,7 +691,7 @@ class ImpactControlBridge:
def doit(self, value, mode, behavior="Stop", unique_id=None, prompt=None, extra_pnginfo=None): def doit(self, value, mode, behavior="Stop", unique_id=None, prompt=None, extra_pnginfo=None):
global error_skip_flag global error_skip_flag
if core.is_execution_model_version_supported: if core.is_execution_model_version_supported():
from comfy_execution.graph import ExecutionBlocker from comfy_execution.graph import ExecutionBlocker
else: else:
print("[Impact Pack] ImpactControlBridge: ComfyUI is outdated. The 'Stop' behavior cannot function properly.") print("[Impact Pack] ImpactControlBridge: ComfyUI is outdated. The 'Stop' behavior cannot function properly.")
@@ -766,6 +766,29 @@ class ImpactExecutionOrderController:
return signal, value 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 original_handle_execution = execution.PromptExecutor.handle_execution_error
+5 -5
View File
@@ -896,7 +896,7 @@ class From_SEG_ELT_bbox:
CATEGORY = "ImpactPack/Util" CATEGORY = "ImpactPack/Util"
def doit(self, bbox): def doit(self, bbox):
return bbox return [int(c) for c in bbox]
class From_SEG_ELT_crop_region: 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 x1, y1, x2, y2 = x1-cx1, y1-cy1, x2-cx1, y2-cy1
h, w = mask.shape h, w = mask.shape
x1 = min(w-1, max(0, x1)) x1 = int(min(w-1, max(0, x1)))
x2 = min(w-1, max(0, x2)) x2 = int(min(w-1, max(0, x2)))
y1 = min(h-1, max(0, y1)) y1 = int(min(h-1, max(0, y1)))
y2 = min(h-1, max(0, y2)) y2 = int(min(h-1, max(0, y2)))
mask_cropped = mask.copy() mask_cropped = mask.copy()
mask_cropped[:, :x1] = 0 # zero fill left side 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 # prepare mask
if noise_mask is not None and inpaint_model: 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: else:
latent_image = to_latent_image(image, vae) latent_image = to_latent_image(image, vae)
if noise_mask is not None: if noise_mask is not None:
+18 -8
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."}), } 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(): if core.is_execution_model_version_supported():
stack = inspect.stack() stack = inspect.stack()
if stack[2].function == 'get_input_info' and stack[3].function == 'add_node': if stack[2].function == 'get_input_info':
for x in range(2, 200): # bypass validation
dyn_inputs[f"input{x}"] = (any_typ, {"lazy": True}) class AllContainer:
def __contains__(self, item):
return True
def __getitem__(self, key):
return any_typ, {"lazy": True}
dyn_inputs = AllContainer()
inputs = {"required": { inputs = {"required": {
"select": ("INT", {"default": 1, "min": 1, "max": 999999, "step": 1, "tooltip": "The input number you want to output among the inputs"}), "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}") print(f"SELECTED: {input_name}")
return [input_name] if input_name in kwargs:
return [input_name]
else:
return []
@staticmethod @staticmethod
def doit(*args, **kwargs): def doit(*args, **kwargs):
@@ -163,7 +173,7 @@ class GeneralInversedSwitch:
CATEGORY = "ImpactPack/Util" CATEGORY = "ImpactPack/Util"
def doit(self, select, prompt, unique_id, input, **kwargs): 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 from comfy_execution.graph import ExecutionBlocker
else: else:
print("[Impact Pack] InversedSwitch: ComfyUI is outdated. The 'select_on_execution' mode cannot function properly.") print("[Impact Pack] InversedSwitch: ComfyUI is outdated. The 'select_on_execution' mode cannot function properly.")
@@ -181,7 +191,7 @@ class GeneralInversedSwitch:
for i in range(0, cnt + 1): for i in range(0, cnt + 1):
if select == i+1: if select == i+1:
res.append(input) res.append(input)
elif core.is_execution_model_version_supported: elif core.is_execution_model_version_supported():
res.append(ExecutionBlocker(None)) res.append(ExecutionBlocker(None))
else: else:
res.append(None) res.append(None)
@@ -366,7 +376,7 @@ class ImageListToImageBatch:
def doit(self, images): def doit(self, images):
if len(images) <= 1: if len(images) <= 1:
return (images,) return (images[0],)
else: else:
image1 = images[0] image1 = images[0]
for image2 in images[1:]: for image2 in images[1:]:
@@ -494,7 +504,7 @@ class MakeMaskBatch:
def doit(self, **kwargs): def doit(self, **kwargs):
mask1 = kwargs['mask1'] mask1 = kwargs['mask1']
del 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: if len(masks) == 0:
return (mask1,) return (mask1,)
+32 -4
View File
@@ -58,11 +58,11 @@ def read_wildcard_dict(wildcard_path):
try: try:
with open(file_path, 'r', encoding="ISO-8859-1") as f: with open(file_path, 'r', encoding="ISO-8859-1") as f:
lines = f.read().splitlines() 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: except yaml.reader.ReaderError:
with open(file_path, 'r', encoding="UTF-8", errors="ignore") as f: with open(file_path, 'r', encoding="UTF-8", errors="ignore") as f:
lines = f.read().splitlines() 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'): elif file.endswith('.yaml'):
file_path = os.path.join(root, file) 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]) matches = re.findall(wildcard_pattern, multi_select_pattern[1])
if len(options) == 1 and matches: if len(options) == 1 and matches:
# count$$<single wildcard> # count$$<single wildcard>
options = local_wildcard_dict.get(matches[0]) options = get_wildcard_options(multi_select_pattern[1])
else: else:
# count$$opt1|opt2|... # count$$opt1|opt2|...
options[0] = multi_select_pattern[1] options[0] = multi_select_pattern[1]
@@ -199,6 +199,34 @@ def process(text, seed=None):
return replaced_string, replacements_found 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): def replace_wildcard(string):
pattern = r"__([\w.\-+/*\\]+?)__" pattern = r"__([\w.\-+/*\\]+?)__"
matches = re.findall(pattern, string) matches = re.findall(pattern, string)
@@ -259,7 +287,7 @@ def process(text, seed=None):
def is_numeric_string(input_str): 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): def safe_float(x):
+2 -2
View File
@@ -1,7 +1,7 @@
[project] [project]
name = "comfyui-impact-pack" 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." description = "This node pack 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" version = "8.0"
license = { file = "LICENSE.txt" } license = { file = "LICENSE.txt" }
dependencies = ["segment-anything", "scikit-image", "piexif", "transformers", "opencv-python-headless", "GitPython", "scipy>=1.11.4"] dependencies = ["segment-anything", "scikit-image", "piexif", "transformers", "opencv-python-headless", "GitPython", "scipy>=1.11.4"]
-1
View File
@@ -3,7 +3,6 @@ scikit-image
piexif piexif
transformers transformers
opencv-python-headless opencv-python-headless
GitPython
scipy>=1.11.4 scipy>=1.11.4
numpy<2 numpy<2
dill dill