Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0e4e439d39 | ||
|
|
18d25a29a0 | ||
|
|
2b724e5ed2 | ||
|
|
96cc242f78 | ||
|
|
28267da071 | ||
|
|
86d7f73981 | ||
|
|
f428182ddf | ||
|
|
a43dae373e | ||
|
|
1087f2ee06 | ||
|
|
841a245cd4 | ||
|
|
aecc925630 | ||
|
|
b5abf19a1b | ||
|
|
9775a98f03 | ||
|
|
ac288bbcb9 | ||
|
|
fd69570977 | ||
|
|
1c488eec11 | ||
|
|
c3eed0936f | ||
|
|
44adbbe282 | ||
|
|
940ad41b15 | ||
|
|
e848a9dfb7 | ||
|
|
76653016dc | ||
|
|
9ed160123d |
@@ -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.
|
||||
@@ -106,6 +108,7 @@ This custom node helps to conveniently enhance images through Detector, Detailer
|
||||
* `SEGS Filter (range)` - This node retrieves only SEGs from SEGS that have a size and position within a certain range.
|
||||
* `SEGS Assign (label)` - Assign labels sequentially to SEGS. This node is useful when used with `[LAB]` of FaceDetailer.
|
||||
* `SEGSConcat` - Concatenate segs1 and segs2. If source shape of segs1 and segs2 are different from segs2 will be ignored.
|
||||
* `SEGS Merge` - SEGS contains multiple SEGs. SEGS Merge integrates several SEGs into a single merged SEG. The label is changed to `merged` and the confidence becomes the minimum confidence. The applied controlnet and cropped_image are removed.
|
||||
* `Picker (SEGS)` - Among the input SEGS, you can select a specific SEG through a dialog. If no SEG is selected, it outputs an empty SEGS. Increasing the batch_size of SEGSDetailer can be used for the purpose of selecting from the candidates.
|
||||
* `Set Default Image For SEGS` - Set a default image for SEGS. SEGS with images set this way do not need to have a fallback image set. When override is set to false, the original image is preserved.
|
||||
* `Remove Image from SEGS` - Remove the image set for the SEGS that has been configured by "Set Default Image for SEGS" or SEGSDetailer. When the image for the SEGS is removed, the Detailer node will operate based on the currently processed image instead of the SEGS.
|
||||
@@ -228,16 +231,19 @@ 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.
|
||||
* `ImpactCompare`, `ImpactConditionalBranch`, `ImpactConditionalBranchSelMode`, `ImpactInt`, `ImpactValueSender`, `ImpactValueReceiver`, `ImpactImageInfo`, `ImpactMinMax`, `ImpactNeg`, `ImpactConditionalStopIteration`
|
||||
* `ImpactCompare`, `ImpactConditionalBranch`, `ImpactConditionalBranchSelMode`, `ImpactInt`, `ImpactBoolean`, `ImpactValueSender`, `ImpactValueReceiver`, `ImpactImageInfo`, `ImpactMinMax`, `ImpactNeg`, `ImpactConditionalStopIteration`
|
||||
* `ImpactIsNotEmptySEGS` - This node returns `true` only if the input SEGS is not empty.
|
||||
* `ImpactIfNone` - Returns `true` if any_input is None, and returns `false` if it is not None.
|
||||
* `Queue Trigger` - When this node is executed, it adds a new queue to assist with repetitive tasks. It will only execute if the signal's status changes.
|
||||
@@ -306,7 +312,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 +335,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 +347,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
|
||||
|
||||
+22
-23
@@ -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,
|
||||
@@ -237,6 +223,7 @@ NODE_CLASS_MAPPINGS = {
|
||||
"ImpactSEGSConcat": SEGSConcat,
|
||||
"ImpactSEGSPicker": SEGSPicker,
|
||||
"ImpactMakeTileSEGS": MakeTileSEGS,
|
||||
"ImpactSEGSMerge": SEGSMerge,
|
||||
|
||||
"SEGSDetailerForAnimateDiff": SEGSDetailerForAnimateDiff,
|
||||
|
||||
@@ -249,6 +236,9 @@ NODE_CLASS_MAPPINGS = {
|
||||
"ImpactImageBatchToImageList": ImageBatchToImageList,
|
||||
"ImpactMakeImageList": MakeImageList,
|
||||
"ImpactMakeImageBatch": MakeImageBatch,
|
||||
"ImpactMakeAnyList": MakeAnyList,
|
||||
"ImpactMakeMaskList": MakeMaskList,
|
||||
"ImpactMakeMaskBatch": MakeMaskBatch,
|
||||
|
||||
"RegionalSampler": RegionalSampler,
|
||||
"RegionalSamplerAdvanced": RegionalSamplerAdvanced,
|
||||
@@ -271,6 +261,7 @@ NODE_CLASS_MAPPINGS = {
|
||||
"ImpactLogicalOperators": ImpactLogicalOperators,
|
||||
"ImpactInt": ImpactInt,
|
||||
"ImpactFloat": ImpactFloat,
|
||||
"ImpactBoolean": ImpactBoolean,
|
||||
"ImpactValueSender": ImpactValueSender,
|
||||
"ImpactValueReceiver": ImpactValueReceiver,
|
||||
"ImpactImageInfo": ImpactImageInfo,
|
||||
@@ -332,6 +323,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 +346,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)",
|
||||
@@ -376,6 +369,7 @@ NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"ImpactSEGSToMaskBatch": "SEGS to Mask Batch",
|
||||
"ImpactSEGSPicker": "Picker (SEGS)",
|
||||
"ImpactMakeTileSEGS": "Make Tile SEGS",
|
||||
"ImpactSEGSMerge": "SEGS Merge",
|
||||
|
||||
"ImpactDecomposeSEGS": "Decompose (SEGS)",
|
||||
"ImpactAssembleSEGS": "Assemble (SEGS)",
|
||||
@@ -400,12 +394,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
@@ -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:
|
||||
|
||||
+14
-4
@@ -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
|
||||
@@ -383,7 +383,7 @@ app.registerExtension({
|
||||
}
|
||||
|
||||
let select_slot = this.inputs.find(x => x.name == "select");
|
||||
if(this.widgets) {
|
||||
if(this.widgets?.length) {
|
||||
this.widgets[0].options.max = select_slot?this.outputs.length-1:this.outputs.length;
|
||||
this.widgets[0].value = Math.min(this.widgets[0].value, this.widgets[0].options.max);
|
||||
if(this.widgets[0].options.max > 0 && this.widgets[0].value == 0)
|
||||
@@ -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;
|
||||
@@ -527,7 +537,7 @@ app.registerExtension({
|
||||
this.addInput(`${input_name}${slot_i}`, this.outputs[0].type);
|
||||
}
|
||||
|
||||
if(this.widgets) {
|
||||
if(this.widgets?.length) {
|
||||
this.widgets[0].options.max = select_slot?this.inputs.length-1:this.inputs.length;
|
||||
this.widgets[0].value = Math.min(this.widgets[0].value, this.widgets[0].options.max);
|
||||
if(this.widgets[0].options.max > 0 && this.widgets[0].value == 0)
|
||||
|
||||
+11
-7
@@ -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');
|
||||
});
|
||||
}
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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,10 +1,10 @@
|
||||
import configparser
|
||||
import os
|
||||
|
||||
version_code = [7, 4, 2]
|
||||
version_code = [7, 8]
|
||||
version = f"V{version_code[0]}.{version_code[1]}" + (f'.{version_code[2]}' if len(version_code) > 2 else '')
|
||||
|
||||
dependency_version = 22
|
||||
dependency_version = 23
|
||||
|
||||
my_path = os.path.dirname(__file__)
|
||||
old_config_path = os.path.join(my_path, "impact-pack.ini")
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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")
|
||||
|
||||
|
||||
@@ -272,6 +272,24 @@ class ImpactFloat:
|
||||
return (value, )
|
||||
|
||||
|
||||
class ImpactBoolean:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"value": ("BOOLEAN", {"default": False}),
|
||||
},
|
||||
}
|
||||
|
||||
FUNCTION = "doit"
|
||||
CATEGORY = "ImpactPack/Logic"
|
||||
|
||||
RETURN_TYPES = ("BOOLEAN", )
|
||||
|
||||
def doit(self, value):
|
||||
return (value, )
|
||||
|
||||
|
||||
class ImpactValueSender:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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]:
|
||||
@@ -694,6 +704,68 @@ class SEGSToMaskBatch:
|
||||
return (mask_batch,)
|
||||
|
||||
|
||||
class SEGSMerge:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {"required": {
|
||||
"segs": ("SEGS", ),
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("SEGS",)
|
||||
FUNCTION = "doit"
|
||||
|
||||
CATEGORY = "ImpactPack/Util"
|
||||
|
||||
DESCRIPTION = "SEGS contains multiple SEGs. SEGS Merge integrates several SEGs into a single merged SEG. The label is changed to `merged` and the confidence becomes the minimum confidence. The applied controlnet and cropped_image are removed."
|
||||
|
||||
def doit(self, segs):
|
||||
crop_left = sys.maxsize
|
||||
crop_right = 0
|
||||
crop_top = sys.maxsize
|
||||
crop_bottom = 0
|
||||
|
||||
bbox_left = sys.maxsize
|
||||
bbox_right = 0
|
||||
bbox_top = sys.maxsize
|
||||
bbox_bottom = 0
|
||||
|
||||
min_confidence = 1.0
|
||||
|
||||
for seg in segs[1]:
|
||||
cx1 = seg.crop_region[0]
|
||||
cy1 = seg.crop_region[1]
|
||||
cx2 = seg.crop_region[2]
|
||||
cy2 = seg.crop_region[3]
|
||||
|
||||
bx1 = seg.bbox[0]
|
||||
by1 = seg.bbox[1]
|
||||
bx2 = seg.bbox[2]
|
||||
by2 = seg.bbox[3]
|
||||
|
||||
crop_left = min(crop_left, cx1)
|
||||
crop_top = min(crop_top, cy1)
|
||||
crop_right = max(crop_right, cx2)
|
||||
crop_bottom = max(crop_bottom, cy2)
|
||||
|
||||
bbox_left = min(bbox_left, bx1)
|
||||
bbox_top = min(bbox_top, by1)
|
||||
bbox_right = max(bbox_right, bx2)
|
||||
bbox_bottom = max(bbox_bottom, by2)
|
||||
|
||||
min_confidence = min(min_confidence, seg.confidence)
|
||||
|
||||
combined_mask = core.segs_to_combined_mask(segs)
|
||||
cropped_mask = combined_mask[crop_top:crop_bottom, crop_left:crop_right]
|
||||
cropped_mask = cropped_mask.unsqueeze(0)
|
||||
|
||||
crop_region = [crop_left, crop_top, crop_right, crop_bottom]
|
||||
bbox = [bbox_left, bbox_top, bbox_right, bbox_bottom]
|
||||
|
||||
seg = SEG(None, cropped_mask, min_confidence, crop_region, bbox, 'merged', None)
|
||||
return ((segs[0], [seg]),)
|
||||
|
||||
|
||||
class SEGSConcat:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
@@ -1403,8 +1475,6 @@ class SEGSPicker:
|
||||
|
||||
RETURN_TYPES = ("SEGS", )
|
||||
|
||||
OUTPUT_NODE = True
|
||||
|
||||
FUNCTION = "doit"
|
||||
|
||||
CATEGORY = "ImpactPack/Util"
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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.8"
|
||||
license = { file = "LICENSE.txt" }
|
||||
dependencies = ["segment-anything", "scikit-image", "piexif", "transformers", "opencv-python-headless", "GitPython", "scipy>=1.11.4"]
|
||||
|
||||
|
||||
@@ -6,3 +6,5 @@ opencv-python-headless
|
||||
GitPython
|
||||
scipy>=1.11.4
|
||||
numpy<2
|
||||
dill
|
||||
matplotlib
|
||||
Reference in New Issue
Block a user