Compare commits

...
17 Commits
Author SHA1 Message Date
Dr.Lt.Data 86d7f73981 update README and version marker 2024-09-24 22:02:32 +09:00
mijuku233 f428182ddf feat: MakeAnyList and AnyPipeToBasic (#753) 2024-09-24 21:53:23 +09:00
Dr.Lt.Data a43dae373e remove useless code 2024-09-24 01:47:46 +09:00
Dr.Lt.Data 1087f2ee06 Restored the default model download feature during the installation step. 2024-09-22 07:30:15 +09:00
Dr.Lt.Data 841a245cd4 FIXED: Anomaly where cropped becomes identical to cropped_refined
https://github.com/ltdrdata/ComfyUI-extension-tutorials/issues/28
2024-09-22 07:18:39 +09:00
Dr.Lt.Data aecc925630 update README 2024-09-21 19:32:05 +09:00
Dr.Lt.Data b5abf19a1b remove automatic installation feature 2024-09-21 19:27:06 +09:00
Dr.Lt.Data 9775a98f03 improve: IterativeUpscale - support noise_mask
https://github.com/ltdrdata/ComfyUI-Impact-Pack/issues/746
2024-09-21 01:33:32 +09:00
Kristian Helk ac288bbcb9 Make a small fix in wildcard replacement (#743)
Fixes wildcard replacement when multiple wildcards are separated by a symbol that is included in the regex (e.g. "__custom_wildcard__-__another_custom_wildcard__")
2024-09-16 12:43:38 +09:00
Dr.Lt.Data fd69570977 Modified: SEGSPicker - it would be better if it were not an output node.
https://github.com/ltdrdata/ComfyUI-Impact-Pack/issues/742
2024-09-14 15:19:10 +09:00
Dr.Lt.Data 1c488eec11 fix: apply differential diffusion before move to device.
This patch will prevent OOM from DifferentialDiffusion
2024-09-13 18:20:09 +09:00
Dr.Lt.Data c3eed0936f update dependency: dill
https://github.com/ltdrdata/ComfyUI-Impact-Pack/issues/735
2024-09-08 11:32:20 +09:00
Dr.Lt.Data 44adbbe282 feat: ImpactMakeMaskList, ImpactMakeMaskBatch, MasksToMaskList, MaskListToMaskBatch, ImpactFlattenMask 2024-09-07 00:48:25 +09:00
Dr.Lt.Data 940ad41b15 fix: invalid patch on last commit 2024-09-05 02:12:11 +09:00
Dr.Lt.Data e848a9dfb7 fix: potential null-deref on impact-wildcard.js
https://github.com/ltdrdata/ComfyUI-Impact-Pack/issues/731
2024-09-05 00:08:29 +09:00
Dr.Lt.Data 76653016dc change default value of PreviewBridge
https://github.com/ltdrdata/ComfyUI-Impact-Pack/issues/728
2024-09-04 12:42:50 +09:00
Dr.Lt.Data 9ed160123d fix: controlbridge - invalid auto typing of output 2024-09-04 08:21:34 +09:00
18 changed files with 239 additions and 248 deletions
+23 -8
View File
@@ -7,6 +7,7 @@ This custom node helps to conveniently enhance images through Detector, Detailer
## NOTICE
* 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.
* V6.0: Supports FLUX.1 model in Impact KSampler, Detailers, PreviewBridgeLatent
* V5.0: It is no longer compatible with versions of ComfyUI before 2024.04.08.
@@ -89,6 +90,7 @@ This custom node helps to conveniently enhance images through Detector, Detailer
* `MaskDetailer (pipe)` - This is a simple inpaint node that applies the Detailer to the mask area.
* `FromDetailer (SDXL/pipe)`, `BasicPipe -> DetailerPipe (SDXL)`, `Edit DetailerPipe (SDXL)` - These are pipe functions used in Detailer for utilizing the refiner model of SDXL.
* `Any PIPE -> BasicPipe` - Convert the PIPE Value of other custom nodes that are not BASIC_PIPE but internally have the same structure as BASIC_PIPE to BASIC_PIPE. If an incompatible type is applied, it may cause runtime errors.
### SEGS Manipulation nodes
* `SEGSDetailer` - Performs detailed work on SEGS without pasting it back onto the original image.
@@ -228,12 +230,15 @@ This custom node helps to conveniently enhance images through Detector, Detailer
### Batch/List Util
* `Image batch To Image List` - Convert Image batch to Image List
* `Image Batch to Image List` - Convert Image batch to Image List
- You can use images generated in a multi batch to handle them
* `Image List to Image Batch` - Convert Image List to Image Batch
* `Make Image List` - Convert multiple images into a single image list
* `Make Image Batch` - Convert multiple images into a single image batch
- The input of images can be scaled up as needed
* `Masks to Mask List`, `Mask List to Masks`, `Make Mask List`, `Make Mask Batch` - It has the same functionality as the nodes above, but uses mask as input instead of image.
* `Flatten Mask Batch` - Flattens a Mask Batch into a single Mask. Normal operation is not guaranteed for non-binary masks.
* `Make List (Any)` - Create a list with arbitrary values.
### Logics (experimental)
* These nodes are experimental nodes designed to implement the logic for loops and dynamic switching.
@@ -306,7 +311,8 @@ This custom node helps to conveniently enhance images through Detector, Detailer
## Ultralytics models
* huggingface.co/Bingsu/[adetailer](https://github.com/ultralytics/assets/releases/) - You can download face, people detection models, and clothing detection 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.
@@ -328,6 +334,10 @@ mmdet_skip = False
## Installation
### Install via ComfyUI-Manager (Recommended)
* Search `ComfyUI Impact Pack` in ComfyUI-Manager and click `Install` button.
### Manual Install (Not Recommended)
1. `cd custom_nodes`
2. `git clone https://github.com/ltdrdata/ComfyUI-Impact-Pack.git`
3. `cd ComfyUI-Impact-Pack`
@@ -336,24 +346,29 @@ mmdet_skip = False
5. (optional) `python install.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.
6. Restart ComfyUI
* NOTE1: If an error occurs during the installation process, please refer to [Troubleshooting Page](troubleshooting/TROUBLESHOOTING.md) for assistance.
* NOTE2: You can use this colab notebook [colab notebook](https://colab.research.google.com/github/ltdrdata/ComfyUI-Impact-Pack/blob/Main/notebook/comfyui_colab_impact_pack.ipynb) to launch it. This notebook automatically downloads the impact pack to the custom_nodes directory, installs the tested dependencies, and runs it.
* NOTE3: If you create an empty file named `skip_download_model` in the `ComfyUI/custom_nodes/` directory, it will skip the model download step during the installation of the impact pack.
## Package Dependencies (If you need to manual setup.)
* pip install
* openmim
* segment-anything
* ultralytics
* scikit-image
* piexif
* (optional) pycocotools
* piexif
* opencv-python
* GitPython
* scipy
* numpy<2
* dill
* matplotlib
* (optional) onnxruntime
* (deprecated) openmim # for mim
* (deprecated) pycocotools # for mim
* mim install (deprecated)
* mmcv==2.0.0, mmdet==3.0.0, mmengine==0.7.2
+19 -23
View File
@@ -23,22 +23,6 @@ import impact.sample_error_enhancer
print(f"### Loading: ComfyUI-Impact-Pack ({impact.config.version})")
def do_install():
import importlib
spec = importlib.util.spec_from_file_location('impact_install', os.path.join(os.path.dirname(__file__), 'install.py'))
impact_install = importlib.util.module_from_spec(spec)
spec.loader.exec_module(impact_install)
# ensure dependency
if not os.path.exists(os.path.join(subpack_path, ".git")) and os.path.exists(subpack_path):
print(f"### CompfyUI-Impact-Pack: corrupted subpack detected.")
shutil.rmtree(subpack_path)
if impact.config.get_config()['dependency_version'] < impact.config.dependency_version or not os.path.exists(subpack_path):
print(f"### ComfyUI-Impact-Pack: Updating dependencies [{impact.config.get_config()['dependency_version']} -> {impact.config.dependency_version}]")
do_install()
sys.path.append(subpack_path)
# Core
@@ -63,10 +47,10 @@ try:
import mmcv
from mmdet.apis import (inference_detector, init_detector)
from mmdet.evaluation import get_classes
except:
import importlib
print("### ComfyUI-Impact-Pack: Reinstall dependencies (several dependencies are missing.)")
do_install()
except Exception as e:
import logging
logging.error("[Impact Pack] Failed to import due to several dependencies are missing!!!!")
raise e
import impact.impact_server # to load server api
@@ -116,6 +100,7 @@ NODE_CLASS_MAPPINGS = {
"FromDetailerPipe": FromDetailerPipe,
"FromDetailerPipe_v2": FromDetailerPipe_v2,
"FromDetailerPipeSDXL": FromDetailerPipe_SDXL,
"AnyPipeToBasic": AnyPipeToBasic,
"ToBasicPipe": ToBasicPipe,
"FromBasicPipe": FromBasicPipe,
"FromBasicPipe_v2": FromBasicPipe_v2,
@@ -161,6 +146,7 @@ NODE_CLASS_MAPPINGS = {
"ImpactSegsAndMask": SegsBitwiseAndMask,
"ImpactSegsAndMaskForEach": SegsBitwiseAndMaskForEach,
"EmptySegs": EmptySEGS,
"ImpactFlattenMask": FlattenMask,
"MediaPipeFaceMeshToSEGS": MediaPipeFaceMeshToSEGS,
"MaskToSEGS": MaskToSEGS,
@@ -249,6 +235,9 @@ NODE_CLASS_MAPPINGS = {
"ImpactImageBatchToImageList": ImageBatchToImageList,
"ImpactMakeImageList": MakeImageList,
"ImpactMakeImageBatch": MakeImageBatch,
"ImpactMakeAnyList": MakeAnyList,
"ImpactMakeMaskList": MakeMaskList,
"ImpactMakeMaskBatch": MakeMaskBatch,
"RegionalSampler": RegionalSampler,
"RegionalSamplerAdvanced": RegionalSamplerAdvanced,
@@ -332,6 +321,7 @@ NODE_DISPLAY_NAME_MAPPINGS = {
"BitwiseAndMask": "Pixelwise(MASK & MASK)",
"SubtractMask": "Pixelwise(MASK - MASK)",
"AddMask": "Pixelwise(MASK + MASK)",
"ImpactFlattenMask": "Flatten Mask Batch",
"DetailerForEach": "Detailer (SEGS)",
"DetailerForEachPipe": "Detailer (SEGS/pipe)",
"DetailerForEachDebug": "DetailerDebug (SEGS)",
@@ -354,6 +344,7 @@ NODE_DISPLAY_NAME_MAPPINGS = {
"DetailerPipeToBasicPipe": "DetailerPipe -> BasicPipe",
"EditBasicPipe": "Edit BasicPipe",
"EditDetailerPipe": "Edit DetailerPipe",
"AnyPipeToBasic": "Any PIPE -> BasicPipe",
"LatentPixelScale": "Latent Scale (on Pixel Space)",
"IterativeLatentUpscale": "Iterative Upscale (Latent/on Pixel Space)",
@@ -400,12 +391,17 @@ NODE_DISPLAY_NAME_MAPPINGS = {
"ImpactInversedSwitch": "Inversed Switch (Any)",
"ImpactExecutionOrderController": "Execution Order Controller",
"MasksToMaskList": "Masks to Mask List",
"MaskListToMaskBatch": "Mask List to Masks",
"ImpactImageBatchToImageList": "Image batch to Image List",
"MasksToMaskList": "Mask Batch to Mask List",
"MaskListToMaskBatch": "Mask List to Mask Batch",
"ImpactImageBatchToImageList": "Image Batch to Image List",
"ImageListToImageBatch": "Image List to Image Batch",
"ImpactMakeImageList": "Make Image List",
"ImpactMakeImageBatch": "Make Image Batch",
"ImpactMakeMaskList": "Make Mask List",
"ImpactMakeMaskBatch": "Make Mask Batch",
"ImpactMakeAnyList": "Make List (Any)",
"ImpactStringSelector": "String Selector",
"StringListToString": "String List to String",
"WildcardPromptFromString": "Wildcard Prompt from String",
+5 -163
View File
@@ -5,7 +5,6 @@ import subprocess
import threading
import locale
import traceback
import re
if sys.argv[0] == 'install.py':
@@ -13,7 +12,6 @@ if sys.argv[0] == 'install.py':
impact_path = os.path.join(os.path.dirname(__file__), "modules")
old_subpack_path = os.path.join(os.path.dirname(__file__), "subpack")
subpack_path = os.path.join(os.path.dirname(__file__), "impact_subpack")
subpack_repo = "https://github.com/ltdrdata/ComfyUI-Impact-Subpack"
@@ -71,64 +69,13 @@ def process_wrap(cmd_str, cwd=None, handler=None, env=None):
# ---
pip_list = None
def get_installed_packages():
global pip_list
if pip_list is None:
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()])
except subprocess.CalledProcessError as e:
print(f"[ComfyUI-Manager] Failed to retrieve the information of installed pip packages.")
return set()
return pip_list
def is_installed(name):
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):
print(f"req_path: {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_installed(line):
return False
return True
try:
import platform
import folder_paths
from torchvision.datasets.utils import download_url
import impact.config
print("### ComfyUI-Impact-Pack: Check dependencies")
if "python_embeded" in sys.executable or "python_embedded" in sys.executable:
pip_install = [sys.executable, '-s', '-m', 'pip', 'install']
pip_upgrade = [sys.executable, '-s', '-m', 'pip', 'install', '-U']
mim_install = [sys.executable, '-s', '-m', 'mim', 'install']
else:
pip_install = [sys.executable, '-m', 'pip', 'install']
pip_upgrade = [sys.executable, '-m', 'pip', 'install', '-U']
mim_install = [sys.executable, '-m', 'mim', 'install']
def ensure_subpack():
import git
if os.path.exists(subpack_path):
@@ -145,106 +92,11 @@ try:
else:
git.Repo.clone_from(subpack_repo, subpack_path)
if os.path.exists(old_subpack_path):
shutil.rmtree(old_subpack_path)
def ensure_pip_packages_first():
subpack_req = os.path.join(subpack_path, "requirements.txt")
if os.path.exists(subpack_req) and not is_requirements_installed(subpack_req):
process_wrap(pip_install + ['-r', 'requirements.txt'], cwd=subpack_path)
if not impact.config.get_config()['mmdet_skip']:
process_wrap(pip_install + ['openmim'])
try:
import pycocotools
except Exception:
if platform.system() not in ["Windows"] or platform.machine() not in ["AMD64", "x86_64"]:
print(f"Your system is {platform.system()}; !! You need to install 'libpython3-dev' for this step. !!")
process_wrap(pip_install + ['pycocotools'])
else:
pycocotools = {
(3, 8): "https://github.com/Bing-su/dddetailer/releases/download/pycocotools/pycocotools-2.0.6-cp38-cp38-win_amd64.whl",
(3, 9): "https://github.com/Bing-su/dddetailer/releases/download/pycocotools/pycocotools-2.0.6-cp39-cp39-win_amd64.whl",
(3, 10): "https://github.com/Bing-su/dddetailer/releases/download/pycocotools/pycocotools-2.0.6-cp310-cp310-win_amd64.whl",
(3, 11): "https://github.com/Bing-su/dddetailer/releases/download/pycocotools/pycocotools-2.0.6-cp311-cp311-win_amd64.whl",
}
version = sys.version_info[:2]
url = pycocotools[version]
process_wrap(pip_install + [url])
def ensure_pip_packages_last():
my_path = os.path.dirname(__file__)
requirements_path = os.path.join(my_path, "requirements.txt")
if not is_requirements_installed(requirements_path):
process_wrap(pip_install + ['-r', requirements_path])
# fallback
try:
import segment_anything
from skimage.measure import label, regionprops
import piexif
except Exception:
process_wrap(pip_install + ['-r', requirements_path])
# !! cv2 importing test must be very last !!
try:
from cv2 import setNumThreads
except Exception:
try:
is_open_cv_installed = False
# upgrade if opencv is installed already
if is_installed('opencv-python'):
process_wrap(pip_upgrade + ['opencv-python'])
is_open_cv_installed = True
if is_installed('opencv-python-headless'):
process_wrap(pip_upgrade + ['opencv-python-headless'])
is_open_cv_installed = True
if is_installed('opencv-contrib-python'):
process_wrap(pip_upgrade + ['opencv-contrib-python'])
is_open_cv_installed = True
if is_installed('opencv-contrib-python-headless'):
process_wrap(pip_upgrade + ['opencv-contrib-python-headless'])
is_open_cv_installed = True
# if opencv is not installed install `opencv-python-headless`
if not is_open_cv_installed:
process_wrap(pip_install + ['opencv-python-headless'])
except:
print(f"[ERROR] ComfyUI-Impact-Pack: failed to install 'opencv-python'. Please, install manually.")
def ensure_mmdet_package():
try:
import mmcv
import mmdet
from mmdet.evaluation import get_classes
except Exception:
process_wrap(pip_install + ['opendatalab==0.0.9'])
process_wrap(pip_install + ['-U', 'openmim'])
process_wrap(mim_install + ['mmcv>=2.0.0rc4, <2.1.0'])
process_wrap(mim_install + ['mmdet==3.0.0'])
process_wrap(mim_install + ['mmengine==0.7.4'])
def install():
subpack_install_script = os.path.join(subpack_path, "install.py")
print(f"### ComfyUI-Impact-Pack: Updating subpack")
try:
import git
except Exception:
if not is_installed('GitPython'):
process_wrap(pip_install + ['GitPython'])
ensure_subpack() # The installation of the subpack must take place before ensure_pip. cv2 triggers a permission error.
new_env = os.environ.copy()
@@ -253,29 +105,20 @@ try:
if os.path.exists(subpack_install_script):
process_wrap([sys.executable, 'install.py'], cwd=subpack_path, env=new_env)
if not is_requirements_installed(os.path.join(subpack_path, 'requirements.txt')):
process_wrap(pip_install + ['-r', 'requirements.txt'], cwd=subpack_path)
else:
print(f"### ComfyUI-Impact-Pack: (Install Failed) Subpack\nFile not found: `{subpack_install_script}`")
ensure_pip_packages_first()
if not impact.config.get_config()['mmdet_skip']:
ensure_mmdet_package()
ensure_pip_packages_last()
# Download model
print("### ComfyUI-Impact-Pack: Check basic models")
bbox_path = os.path.join(model_path, "mmdets", "bbox")
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 os.path.exists(bbox_path):
os.makedirs(bbox_path)
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)
@@ -291,7 +134,6 @@ try:
impact.config.write_config()
install()
except Exception as e:
+12 -2
View File
@@ -237,7 +237,7 @@ app.registerExtension({
if(nodeData.name == "ImpactControlBridge") {
const onConnectionsChange = nodeType.prototype.onConnectionsChange;
nodeType.prototype.onConnectionsChange = function (type, index, connected, link_info) {
if(!link_info || this.inputs[0].type != '*')
if(index != 0 || !link_info || this.inputs[0].type != '*')
return;
// assign type
@@ -393,7 +393,8 @@ app.registerExtension({
}
if (nodeData.name === 'ImpactMakeImageList' || nodeData.name === 'ImpactMakeImageBatch' ||
nodeData.name === 'CombineRegionalPrompts' ||
nodeData.name === 'ImpactMakeMaskList' || nodeData.name === 'ImpactMakeMaskBatch' ||
nodeData.name === 'ImpactMakeAnyList' || nodeData.name === 'CombineRegionalPrompts' ||
nodeData.name === 'ImpactCombineConditionings' || nodeData.name === 'ImpactConcatConditionings' ||
nodeData.name === 'ImpactSEGSConcat' ||
nodeData.name === 'ImpactSwitch' || nodeData.name === 'LatentSwitch' || nodeData.name == 'SEGSSwitch') {
@@ -405,6 +406,15 @@ app.registerExtension({
input_name = "image";
break;
case 'ImpactMakeMaskList':
case 'ImpactMakeMaskBatch':
input_name = "mask";
break;
case 'ImpactMakeAnyList':
input_name = "value";
break;
case 'ImpactSEGSConcat':
input_name = "segs";
break;
+11 -7
View File
@@ -6,11 +6,15 @@ let refresh_btn2 = document.querySelector('button[title="Refresh widgets in node
let orig = refresh_btn.onclick;
refresh_btn.onclick = function() {
orig();
api.fetchApi('/impact/wildcards/refresh');
};
if(refresh_btn) {
refresh_btn.onclick = function() {
orig();
api.fetchApi('/impact/wildcards/refresh');
};
}
refresh_btn2.addEventListener('click', function() {
api.fetchApi('/impact/wildcards/refresh');
});
if(refresh_btn2) {
refresh_btn2?.addEventListener('click', function() {
api.fetchApi('/impact/wildcards/refresh');
});
}
+10
View File
@@ -5,6 +5,13 @@ from impact.core import SEG
from impact.segs_nodes import SEGSPaste
try:
from comfy_extras import nodes_differential_diffusion
except Exception:
print(f"\n#############################################\n[Impact Pack] ComfyUI is an outdated version.\n#############################################\n")
raise Exception("[Impact Pack] ComfyUI is an outdated version.")
class SEGSDetailerForAnimateDiff:
@classmethod
def INPUT_TYPES(cls):
@@ -53,6 +60,9 @@ class SEGSDetailerForAnimateDiff:
new_segs = []
cnet_image_list = []
if noise_mask_feather > 0 and 'denoise_mask_function' not in model.model_options:
model = nodes_differential_diffusion.DifferentialDiffusion().apply(model)[0]
for seg in segs[1]:
cropped_image_frames = None
+2 -2
View File
@@ -20,7 +20,7 @@ class PreviewBridge:
"image": ("STRING", {"default": ""}),
},
"optional": {
"block": ("BOOLEAN", {"default": True, "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."})
},
"hidden": {"unique_id": "UNIQUE_ID", "extra_pnginfo": "EXTRA_PNGINFO"},
}
@@ -196,7 +196,7 @@ class PreviewBridgeLatent:
},
"optional": {
"vae_opt": ("VAE", ),
"block": ("BOOLEAN", {"default": True, "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."})
},
"hidden": {"unique_id": "UNIQUE_ID", "prompt": "PROMPT", "extra_pnginfo": "EXTRA_PNGINFO"},
}
+1 -1
View File
@@ -1,7 +1,7 @@
import configparser
import os
version_code = [7, 4, 2]
version_code = [7, 7]
version = f"V{version_code[0]}.{version_code[1]}" + (f'.{version_code[2]}' if len(version_code) > 2 else '')
dependency_version = 22
+6
View File
@@ -1680,6 +1680,9 @@ class PixelKSampleUpscaler:
self.hook.pre_ksample(model, seed, steps, cfg, sampler_name, scheduler, positive, negative,
upscaled_latent, denoise)
if 'noise_mask' in samples:
upscaled_latent['noise_mask'] = samples['noise_mask']
refined_latent = self.sample(model, seed, steps, cfg, sampler_name, scheduler, positive, negative, upscaled_latent, denoise, upscaled_images)
return refined_latent
@@ -1709,6 +1712,9 @@ class PixelKSampleUpscaler:
self.hook.pre_ksample(model, seed, steps, cfg, sampler_name, scheduler, positive, negative,
upscaled_latent, denoise)
if 'noise_mask' in samples:
upscaled_latent['noise_mask'] = samples['noise_mask']
refined_latent = self.sample(model, seed, steps, cfg, sampler_name, scheduler, positive, negative, upscaled_latent, denoise, upscaled_images)
return refined_latent
+37 -3
View File
@@ -29,6 +29,14 @@ import impact.wildcards as wildcards
from . import hooks
from . import utils
try:
from comfy_extras import nodes_differential_diffusion
except Exception:
print(f"\n#############################################\n[Impact Pack] ComfyUI is an outdated version.\n#############################################\n")
raise Exception("[Impact Pack] ComfyUI is an outdated version.")
warnings.filterwarnings('ignore', category=UserWarning, message='TypedStorage is deprecated')
model_path = folder_paths.models_dir
@@ -257,6 +265,9 @@ class DetailerForEach:
else:
ordered_segs = segs[1]
if noise_mask_feather > 0 and 'denoise_mask_function' not in model.model_options:
model = nodes_differential_diffusion.DifferentialDiffusion().apply(model)[0]
for i, seg in enumerate(ordered_segs):
cropped_image = crop_ndarray4(image.cpu().numpy(), seg.crop_region) # Never use seg.cropped_image to handle overlapping area
cropped_image = to_tensor(cropped_image)
@@ -308,6 +319,7 @@ class DetailerForEach:
if wildcard_item and wildcard_item.strip() == '[STOP]':
break
orig_cropped_image = cropped_image.clone()
enhanced_image, cnet_pils = core.enhance_detail(cropped_image, model, clip, vae, guide_size, guide_size_for_bbox, max_size,
seg.bbox, seg_seed, steps, cfg, sampler_name, scheduler,
cropped_positive, cropped_negative, denoise, cropped_mask, force_inpaint,
@@ -327,7 +339,7 @@ class DetailerForEach:
# use image paste
image = image.cpu()
enhanced_image = enhanced_image.cpu()
tensor_paste(image, enhanced_image, (seg.crop_region[0], seg.crop_region[1]), mask)
tensor_paste(image, enhanced_image, (seg.crop_region[0], seg.crop_region[1]), mask) # this code affecting to `cropped_image`.
enhanced_list.append(enhanced_image)
if detailer_hook is not None:
@@ -345,7 +357,7 @@ class DetailerForEach:
else:
new_seg_image = None
cropped_list.append(cropped_image)
cropped_list.append(orig_cropped_image) # NOTE: Don't use `cropped_image`
new_seg = SEG(new_seg_image, seg.cropped_mask, seg.confidence, seg.crop_region, seg.bbox, seg.label, seg.control_net_wrapper)
new_segs.append(new_seg)
@@ -1238,6 +1250,7 @@ class IterativeLatentUpscale:
upscale_factor_unit = max(0, (upscale_factor - 1.0) / steps)
current_latent = samples
noise_mask = current_latent.get('noise_mask')
scale = 1
for i in range(steps-1):
@@ -1252,6 +1265,8 @@ class IterativeLatentUpscale:
print(f"IterativeLatentUpscale[{i+1}/{steps}]: {new_w:.1f}x{new_h:.1f} (scale:{scale:.2f}) ")
step_info = i, steps
current_latent = upscaler.upscale_shape(step_info, current_latent, new_w, new_h, temp_prefix)
if noise_mask is not None:
current_latent['noise_mask'] = noise_mask
if scale < upscale_factor:
new_w = w*upscale_factor
@@ -1263,7 +1278,7 @@ class IterativeLatentUpscale:
core.update_node_status(unique_id, "", None)
return (current_latent, upscaler.vae)
return current_latent, upscaler.vae
class IterativeImageUpscale:
@@ -1696,6 +1711,25 @@ class ToBinaryMask:
return (mask,)
class FlattenMask:
@classmethod
def INPUT_TYPES(s):
return {"required": {
"masks": ("MASK",),
}
}
RETURN_TYPES = ("MASK",)
FUNCTION = "doit"
CATEGORY = "ImpactPack/Operation"
def doit(self, masks):
masks = utils.make_3d_mask(masks)
masks = utils.flatten_mask(masks)
return (masks,)
class BitwiseAndMask:
@classmethod
def INPUT_TYPES(s):
-31
View File
@@ -24,37 +24,6 @@ import random
from server import PromptServer
@PromptServer.instance.routes.post("/upload/temp")
async def upload_image(request):
upload_dir = folder_paths.get_temp_directory()
if not os.path.exists(upload_dir):
os.makedirs(upload_dir)
post = await request.post()
image = post.get("image")
if image and image.file:
filename = image.filename
if not filename:
return web.Response(status=400)
split = os.path.splitext(filename)
i = 1
while os.path.exists(os.path.join(upload_dir, filename)):
filename = f"{split[0]} ({i}){split[1]}"
i += 1
filepath = os.path.join(upload_dir, filename)
with open(filepath, "wb") as f:
f.write(image.file.read())
return web.json_response({"name": filename})
else:
return web.Response(status=400)
sam_predictor = None
default_sam_model_name = os.path.join(impact_pack.model_path, "sams", "sam_vit_b_01ec64.pth")
+19
View File
@@ -1,5 +1,7 @@
import folder_paths
import impact.wildcards
from impact.utils import any_typ
class ToDetailerPipe:
@classmethod
@@ -108,6 +110,23 @@ class FromDetailerPipe_SDXL:
return detailer_pipe, model, clip, vae, positive, negative, bbox_detector, sam_model_opt, segm_detector_opt, detailer_hook, refiner_model, refiner_clip, refiner_positive, refiner_negative
class AnyPipeToBasic:
@classmethod
def INPUT_TYPES(s):
return {
"required": {"any_pipe": (any_typ,)},
}
RETURN_TYPES = ("BASIC_PIPE", )
RETURN_NAMES = ("basic_pipe", )
FUNCTION = "doit"
CATEGORY = "ImpactPack/Pipe"
def doit(self, any_pipe):
return (any_pipe[:5], )
class ToBasicPipe:
@classmethod
def INPUT_TYPES(s):
+10 -2
View File
@@ -14,6 +14,13 @@ from comfy.cli_args import args
import math
try:
from comfy_extras import nodes_differential_diffusion
except Exception:
print(f"\n#############################################\n[Impact Pack] ComfyUI is an outdated version.\n#############################################\n")
raise Exception("[Impact Pack] ComfyUI is an outdated version.")
class SEGSDetailer:
@classmethod
def INPUT_TYPES(s):
@@ -69,6 +76,9 @@ class SEGSDetailer:
new_segs = []
cnet_pil_list = []
if noise_mask_feather > 0 and 'denoise_mask_function' not in model.model_options:
model = nodes_differential_diffusion.DifferentialDiffusion().apply(model)[0]
for i in range(batch_size):
seed += 1
for seg in segs[1]:
@@ -1403,8 +1413,6 @@ class SEGSPicker:
RETURN_TYPES = ("SEGS", )
OUTPUT_NODE = True
FUNCTION = "doit"
CATEGORY = "ImpactPack/Util"
+69
View File
@@ -392,6 +392,50 @@ class ImageBatchToImageList:
return (images, )
class MakeAnyList:
@classmethod
def INPUT_TYPES(s):
return {
"required": {},
"optional": {"value1": (any_typ,), }
}
RETURN_TYPES = (any_typ,)
OUTPUT_IS_LIST = (True,)
FUNCTION = "doit"
CATEGORY = "ImpactPack/Util"
def doit(self, **kwargs):
values = []
for k, v in kwargs.items():
if v is not None:
values.append(v)
return (values, )
class MakeMaskList:
@classmethod
def INPUT_TYPES(s):
return {"required": {"mask1": ("MASK",), }}
RETURN_TYPES = ("MASK",)
OUTPUT_IS_LIST = (True,)
FUNCTION = "doit"
CATEGORY = "ImpactPack/Util"
def doit(self, **kwargs):
masks = []
for k, v in kwargs.items():
masks.append(v)
return (masks, )
class MakeImageList:
@classmethod
def INPUT_TYPES(s):
@@ -437,6 +481,31 @@ class MakeImageBatch:
return (image1,)
class MakeMaskBatch:
@classmethod
def INPUT_TYPES(s):
return {"required": {"mask1": ("MASK",), }}
RETURN_TYPES = ("MASK",)
FUNCTION = "doit"
CATEGORY = "ImpactPack/Util"
def doit(self, **kwargs):
mask1 = kwargs['mask1']
del kwargs['mask1']
masks = [utils.make_3d_mask(value) for value in kwargs.values()]
if len(masks) == 0:
return (mask1,)
else:
for mask2 in masks:
if mask1.shape[1:] != mask2.shape[1:]:
mask2 = comfy.utils.common_upscale(mask2.movedim(-1, 1), mask1.shape[2], mask1.shape[1], "lanczos", "center").movedim(1, -1)
mask1 = torch.cat((mask1, mask2), dim=0)
return (mask1,)
class ReencodeLatent:
@classmethod
def INPUT_TYPES(s):
+9 -2
View File
@@ -5,8 +5,7 @@ import numpy as np
import folder_paths
import nodes
from . import config
from PIL import Image, ImageFilter
from scipy.ndimage import zoom
from PIL import Image
import comfy
@@ -579,6 +578,14 @@ def apply_mask_alpha_to_pil(decoded_pil, mask):
return decoded_rgba
def flatten_mask(all_masks):
merged_mask = (all_masks[0] * 255).to(torch.uint8)
for mask in all_masks[1:]:
merged_mask |= (mask * 255).to(torch.uint8)
return merged_mask
def try_install_custom_node(custom_node_url, msg):
try:
import cm_global
+3 -3
View File
@@ -12,7 +12,7 @@ from impact import config
wildcards_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "wildcards"))
RE_WildCardQuantifier = re.compile(r"(?P<quantifier>\d+)#__(?P<keyword>[\w.\-+/*\\]+)__", re.IGNORECASE)
RE_WildCardQuantifier = re.compile(r"(?P<quantifier>\d+)#__(?P<keyword>[\w.\-+/*\\]+?)__", re.IGNORECASE)
wildcard_lock = threading.Lock()
wildcard_dict = {}
@@ -121,7 +121,7 @@ def process(text, seed=None):
select_sep = ' '
range_pattern = r'(\d+)(-(\d+))?'
range_pattern2 = r'-(\d+)'
wildcard_pattern = r"__([\w.\-+/*\\]+)__"
wildcard_pattern = r"__([\w.\-+/*\\]+?)__"
if len(multi_select_pattern) > 1:
r = re.match(range_pattern, options[0])
@@ -200,7 +200,7 @@ def process(text, seed=None):
return replaced_string, replacements_found
def replace_wildcard(string):
pattern = r"__([\w.\-+/*\\]+)__"
pattern = r"__([\w.\-+/*\\]+?)__"
matches = re.findall(pattern, string)
replacements_found = False
+1 -1
View File
@@ -1,7 +1,7 @@
[project]
name = "comfyui-impact-pack"
description = "This extension offers various detector nodes and detailer nodes that allow you to configure a workflow that automatically enhances facial details. And provide iterative upscaler."
version = "7.4.2"
version = "7.7"
license = { file = "LICENSE.txt" }
dependencies = ["segment-anything", "scikit-image", "piexif", "transformers", "opencv-python-headless", "GitPython", "scipy>=1.11.4"]
+2
View File
@@ -6,3 +6,5 @@ opencv-python-headless
GitPython
scipy>=1.11.4
numpy<2
dill
matplotlib