Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6fe85ed6bf | ||
|
|
216f4660e1 | ||
|
|
314b676cc3 | ||
|
|
cd89590a7d | ||
|
|
dc70f40eff | ||
|
|
825296c3e0 | ||
|
|
bf0e86a10d | ||
|
|
727295b52e | ||
|
|
6c23f7691d | ||
|
|
6248f31402 | ||
|
|
cb0b652703 | ||
|
|
9c4fa5fb27 | ||
|
|
759b4d0dc9 | ||
|
|
a65fd56b7f | ||
|
|
47d981eca2 | ||
|
|
b2f382776f | ||
|
|
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
|
## 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.
|
* 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
|
||||||
* V5.0: It is no longer compatible with versions of ComfyUI before 2024.04.08.
|
* 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.
|
* `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.
|
* `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
|
### SEGS Manipulation nodes
|
||||||
* `SEGSDetailer` - Performs detailed work on SEGS without pasting it back onto the original image.
|
* `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 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.
|
* `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.
|
* `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.
|
* `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.
|
* `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.
|
* `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
|
### 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
|
- 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 List` - Convert multiple images into a single image list
|
||||||
* `Make Image Batch` - Convert multiple images into a single image batch
|
* `Make Image Batch` - Convert multiple images into a single image batch
|
||||||
- The input of images can be scaled up as needed
|
- 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)
|
### Logics (experimental)
|
||||||
* These nodes are experimental nodes designed to implement the logic for loops and dynamic switching.
|
* 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.
|
* `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.
|
* `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.
|
* `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.
|
||||||
@@ -280,6 +286,7 @@ This custom node helps to conveniently enhance images through Detector, Detailer
|
|||||||
* `Negative Cond Placeholder` - Models like FLUX.1 do not use Negative Conditioning. This is a placeholder node for them. You can use FLUX.1 by replacing the Negative Conditioning used in Impact KSampler, KSampler (Inspire), and Detailer with this node.
|
* `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
|
||||||
@@ -306,7 +313,8 @@ This custom node helps to conveniently enhance images through Detector, Detailer
|
|||||||
|
|
||||||
|
|
||||||
## Ultralytics models
|
## 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.
|
* 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.
|
* 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,32 +336,41 @@ mmdet_skip = False
|
|||||||
|
|
||||||
## Installation
|
## 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`
|
1. `cd custom_nodes`
|
||||||
2. `git clone https://github.com/ltdrdata/ComfyUI-Impact-Pack.git`
|
2. `git clone https://github.com/ltdrdata/ComfyUI-Impact-Pack.git`
|
||||||
3. `cd ComfyUI-Impact-Pack`
|
3. `cd ComfyUI-Impact-Pack`
|
||||||
4. (optional) `git clone https://github.com/ltdrdata/ComfyUI-Impact-Subpack impact_subpack`
|
4. (optional) `git clone https://github.com/ltdrdata/ComfyUI-Impact-Subpack impact_subpack`
|
||||||
* Impact Pack will automatically download subpack during its initial launch.
|
* Impact Pack will automatically download subpack during its initial launch.
|
||||||
5. (optional) `python install.py`
|
5. (optional) `python install-manual.py`
|
||||||
* Impact Pack will automatically install its dependencies during its initial launch.
|
* Impact Pack will automatically install its dependencies during its initial launch.
|
||||||
* For the portable version, you should execute the command `..\..\..\python_embeded\python.exe install.py` to run the installation script.
|
* For the portable version, you should execute the command `..\..\..\python_embeded\python.exe install-manual.py` to run the installation script.
|
||||||
|
|
||||||
|
|
||||||
6. Restart ComfyUI
|
6. Restart ComfyUI
|
||||||
|
|
||||||
* NOTE1: If an error occurs during the installation process, please refer to [Troubleshooting Page](troubleshooting/TROUBLESHOOTING.md) for assistance.
|
* 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.
|
* 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.
|
* 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.)
|
## Package Dependencies (If you need to manual setup.)
|
||||||
|
|
||||||
* pip install
|
* pip install
|
||||||
* openmim
|
|
||||||
* segment-anything
|
* segment-anything
|
||||||
* ultralytics
|
* ultralytics
|
||||||
* scikit-image
|
* scikit-image
|
||||||
* piexif
|
* piexif
|
||||||
* (optional) pycocotools
|
* opencv-python
|
||||||
|
* GitPython
|
||||||
|
* scipy
|
||||||
|
* numpy<2
|
||||||
|
* dill
|
||||||
|
* matplotlib
|
||||||
* (optional) onnxruntime
|
* (optional) onnxruntime
|
||||||
|
* (deprecated) openmim # for mim
|
||||||
|
* (deprecated) pycocotools # for mim
|
||||||
|
|
||||||
* mim install (deprecated)
|
* mim install (deprecated)
|
||||||
* mmcv==2.0.0, mmdet==3.0.0, mmengine==0.7.2
|
* mmcv==2.0.0, mmdet==3.0.0, mmengine==0.7.2
|
||||||
|
|||||||
+26
-25
@@ -23,22 +23,6 @@ import impact.sample_error_enhancer
|
|||||||
print(f"### Loading: ComfyUI-Impact-Pack ({impact.config.version})")
|
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)
|
sys.path.append(subpack_path)
|
||||||
|
|
||||||
# Core
|
# Core
|
||||||
@@ -63,10 +47,10 @@ try:
|
|||||||
import mmcv
|
import mmcv
|
||||||
from mmdet.apis import (inference_detector, init_detector)
|
from mmdet.apis import (inference_detector, init_detector)
|
||||||
from mmdet.evaluation import get_classes
|
from mmdet.evaluation import get_classes
|
||||||
except:
|
except Exception as e:
|
||||||
import importlib
|
import logging
|
||||||
print("### ComfyUI-Impact-Pack: Reinstall dependencies (several dependencies are missing.)")
|
logging.error("[Impact Pack] Failed to import due to several dependencies are missing!!!!")
|
||||||
do_install()
|
raise e
|
||||||
|
|
||||||
|
|
||||||
import impact.impact_server # to load server api
|
import impact.impact_server # to load server api
|
||||||
@@ -116,6 +100,7 @@ NODE_CLASS_MAPPINGS = {
|
|||||||
"FromDetailerPipe": FromDetailerPipe,
|
"FromDetailerPipe": FromDetailerPipe,
|
||||||
"FromDetailerPipe_v2": FromDetailerPipe_v2,
|
"FromDetailerPipe_v2": FromDetailerPipe_v2,
|
||||||
"FromDetailerPipeSDXL": FromDetailerPipe_SDXL,
|
"FromDetailerPipeSDXL": FromDetailerPipe_SDXL,
|
||||||
|
"AnyPipeToBasic": AnyPipeToBasic,
|
||||||
"ToBasicPipe": ToBasicPipe,
|
"ToBasicPipe": ToBasicPipe,
|
||||||
"FromBasicPipe": FromBasicPipe,
|
"FromBasicPipe": FromBasicPipe,
|
||||||
"FromBasicPipe_v2": FromBasicPipe_v2,
|
"FromBasicPipe_v2": FromBasicPipe_v2,
|
||||||
@@ -161,6 +146,7 @@ NODE_CLASS_MAPPINGS = {
|
|||||||
"ImpactSegsAndMask": SegsBitwiseAndMask,
|
"ImpactSegsAndMask": SegsBitwiseAndMask,
|
||||||
"ImpactSegsAndMaskForEach": SegsBitwiseAndMaskForEach,
|
"ImpactSegsAndMaskForEach": SegsBitwiseAndMaskForEach,
|
||||||
"EmptySegs": EmptySEGS,
|
"EmptySegs": EmptySEGS,
|
||||||
|
"ImpactFlattenMask": FlattenMask,
|
||||||
|
|
||||||
"MediaPipeFaceMeshToSEGS": MediaPipeFaceMeshToSEGS,
|
"MediaPipeFaceMeshToSEGS": MediaPipeFaceMeshToSEGS,
|
||||||
"MaskToSEGS": MaskToSEGS,
|
"MaskToSEGS": MaskToSEGS,
|
||||||
@@ -237,6 +223,7 @@ NODE_CLASS_MAPPINGS = {
|
|||||||
"ImpactSEGSConcat": SEGSConcat,
|
"ImpactSEGSConcat": SEGSConcat,
|
||||||
"ImpactSEGSPicker": SEGSPicker,
|
"ImpactSEGSPicker": SEGSPicker,
|
||||||
"ImpactMakeTileSEGS": MakeTileSEGS,
|
"ImpactMakeTileSEGS": MakeTileSEGS,
|
||||||
|
"ImpactSEGSMerge": SEGSMerge,
|
||||||
|
|
||||||
"SEGSDetailerForAnimateDiff": SEGSDetailerForAnimateDiff,
|
"SEGSDetailerForAnimateDiff": SEGSDetailerForAnimateDiff,
|
||||||
|
|
||||||
@@ -249,6 +236,9 @@ NODE_CLASS_MAPPINGS = {
|
|||||||
"ImpactImageBatchToImageList": ImageBatchToImageList,
|
"ImpactImageBatchToImageList": ImageBatchToImageList,
|
||||||
"ImpactMakeImageList": MakeImageList,
|
"ImpactMakeImageList": MakeImageList,
|
||||||
"ImpactMakeImageBatch": MakeImageBatch,
|
"ImpactMakeImageBatch": MakeImageBatch,
|
||||||
|
"ImpactMakeAnyList": MakeAnyList,
|
||||||
|
"ImpactMakeMaskList": MakeMaskList,
|
||||||
|
"ImpactMakeMaskBatch": MakeMaskBatch,
|
||||||
|
|
||||||
"RegionalSampler": RegionalSampler,
|
"RegionalSampler": RegionalSampler,
|
||||||
"RegionalSamplerAdvanced": RegionalSamplerAdvanced,
|
"RegionalSamplerAdvanced": RegionalSamplerAdvanced,
|
||||||
@@ -271,6 +261,7 @@ NODE_CLASS_MAPPINGS = {
|
|||||||
"ImpactLogicalOperators": ImpactLogicalOperators,
|
"ImpactLogicalOperators": ImpactLogicalOperators,
|
||||||
"ImpactInt": ImpactInt,
|
"ImpactInt": ImpactInt,
|
||||||
"ImpactFloat": ImpactFloat,
|
"ImpactFloat": ImpactFloat,
|
||||||
|
"ImpactBoolean": ImpactBoolean,
|
||||||
"ImpactValueSender": ImpactValueSender,
|
"ImpactValueSender": ImpactValueSender,
|
||||||
"ImpactValueReceiver": ImpactValueReceiver,
|
"ImpactValueReceiver": ImpactValueReceiver,
|
||||||
"ImpactImageInfo": ImpactImageInfo,
|
"ImpactImageInfo": ImpactImageInfo,
|
||||||
@@ -282,6 +273,7 @@ NODE_CLASS_MAPPINGS = {
|
|||||||
"StringListToString": StringListToString,
|
"StringListToString": StringListToString,
|
||||||
"WildcardPromptFromString": WildcardPromptFromString,
|
"WildcardPromptFromString": WildcardPromptFromString,
|
||||||
"ImpactExecutionOrderController": ImpactExecutionOrderController,
|
"ImpactExecutionOrderController": ImpactExecutionOrderController,
|
||||||
|
"ImpactListBridge": ImpactListBridge,
|
||||||
|
|
||||||
"RemoveNoiseMask": RemoveNoiseMask,
|
"RemoveNoiseMask": RemoveNoiseMask,
|
||||||
|
|
||||||
@@ -315,8 +307,8 @@ NODE_DISPLAY_NAME_MAPPINGS = {
|
|||||||
"ImpactSimpleDetectorSEGS_for_AD": "Simple Detector for AnimateDiff (SEGS)",
|
"ImpactSimpleDetectorSEGS_for_AD": "Simple Detector for AnimateDiff (SEGS)",
|
||||||
"ImpactSimpleDetectorSEGS": "Simple Detector (SEGS)",
|
"ImpactSimpleDetectorSEGS": "Simple Detector (SEGS)",
|
||||||
"ImpactSimpleDetectorSEGSPipe": "Simple Detector (SEGS/pipe)",
|
"ImpactSimpleDetectorSEGSPipe": "Simple Detector (SEGS/pipe)",
|
||||||
"ImpactControlNetApplySEGS": "ControlNetApply (SEGS)",
|
"ImpactControlNetApplySEGS": "ControlNetApply (SEGS) - DEPRECATED",
|
||||||
"ImpactControlNetApplyAdvancedSEGS": "ControlNetApplyAdvanced (SEGS)",
|
"ImpactControlNetApplyAdvancedSEGS": "ControlNetApply (SEGS)",
|
||||||
"ImpactIPAdapterApplySEGS": "IPAdapterApply (SEGS)",
|
"ImpactIPAdapterApplySEGS": "IPAdapterApply (SEGS)",
|
||||||
|
|
||||||
"BboxDetectorCombined_v2": "BBOX Detector (combined)",
|
"BboxDetectorCombined_v2": "BBOX Detector (combined)",
|
||||||
@@ -332,6 +324,7 @@ NODE_DISPLAY_NAME_MAPPINGS = {
|
|||||||
"BitwiseAndMask": "Pixelwise(MASK & MASK)",
|
"BitwiseAndMask": "Pixelwise(MASK & MASK)",
|
||||||
"SubtractMask": "Pixelwise(MASK - MASK)",
|
"SubtractMask": "Pixelwise(MASK - MASK)",
|
||||||
"AddMask": "Pixelwise(MASK + MASK)",
|
"AddMask": "Pixelwise(MASK + MASK)",
|
||||||
|
"ImpactFlattenMask": "Flatten Mask Batch",
|
||||||
"DetailerForEach": "Detailer (SEGS)",
|
"DetailerForEach": "Detailer (SEGS)",
|
||||||
"DetailerForEachPipe": "Detailer (SEGS/pipe)",
|
"DetailerForEachPipe": "Detailer (SEGS/pipe)",
|
||||||
"DetailerForEachDebug": "DetailerDebug (SEGS)",
|
"DetailerForEachDebug": "DetailerDebug (SEGS)",
|
||||||
@@ -354,6 +347,7 @@ NODE_DISPLAY_NAME_MAPPINGS = {
|
|||||||
"DetailerPipeToBasicPipe": "DetailerPipe -> BasicPipe",
|
"DetailerPipeToBasicPipe": "DetailerPipe -> BasicPipe",
|
||||||
"EditBasicPipe": "Edit BasicPipe",
|
"EditBasicPipe": "Edit BasicPipe",
|
||||||
"EditDetailerPipe": "Edit DetailerPipe",
|
"EditDetailerPipe": "Edit DetailerPipe",
|
||||||
|
"AnyPipeToBasic": "Any PIPE -> BasicPipe",
|
||||||
|
|
||||||
"LatentPixelScale": "Latent Scale (on Pixel Space)",
|
"LatentPixelScale": "Latent Scale (on Pixel Space)",
|
||||||
"IterativeLatentUpscale": "Iterative Upscale (Latent/on Pixel Space)",
|
"IterativeLatentUpscale": "Iterative Upscale (Latent/on Pixel Space)",
|
||||||
@@ -376,6 +370,7 @@ NODE_DISPLAY_NAME_MAPPINGS = {
|
|||||||
"ImpactSEGSToMaskBatch": "SEGS to Mask Batch",
|
"ImpactSEGSToMaskBatch": "SEGS to Mask Batch",
|
||||||
"ImpactSEGSPicker": "Picker (SEGS)",
|
"ImpactSEGSPicker": "Picker (SEGS)",
|
||||||
"ImpactMakeTileSEGS": "Make Tile SEGS",
|
"ImpactMakeTileSEGS": "Make Tile SEGS",
|
||||||
|
"ImpactSEGSMerge": "SEGS Merge",
|
||||||
|
|
||||||
"ImpactDecomposeSEGS": "Decompose (SEGS)",
|
"ImpactDecomposeSEGS": "Decompose (SEGS)",
|
||||||
"ImpactAssembleSEGS": "Assemble (SEGS)",
|
"ImpactAssembleSEGS": "Assemble (SEGS)",
|
||||||
@@ -399,13 +394,19 @@ 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": "Masks to Mask List",
|
"MasksToMaskList": "Mask Batch to Mask List",
|
||||||
"MaskListToMaskBatch": "Mask List to Masks",
|
"MaskListToMaskBatch": "Mask List to Mask Batch",
|
||||||
"ImpactImageBatchToImageList": "Image batch to Image List",
|
"ImpactImageBatchToImageList": "Image Batch to Image List",
|
||||||
"ImageListToImageBatch": "Image List to Image Batch",
|
"ImageListToImageBatch": "Image List to Image Batch",
|
||||||
|
|
||||||
"ImpactMakeImageList": "Make Image List",
|
"ImpactMakeImageList": "Make Image List",
|
||||||
"ImpactMakeImageBatch": "Make Image Batch",
|
"ImpactMakeImageBatch": "Make Image Batch",
|
||||||
|
"ImpactMakeMaskList": "Make Mask List",
|
||||||
|
"ImpactMakeMaskBatch": "Make Mask Batch",
|
||||||
|
"ImpactMakeAnyList": "Make List (Any)",
|
||||||
|
|
||||||
"ImpactStringSelector": "String Selector",
|
"ImpactStringSelector": "String Selector",
|
||||||
"StringListToString": "String List to String",
|
"StringListToString": "String List to String",
|
||||||
"WildcardPromptFromString": "Wildcard Prompt from String",
|
"WildcardPromptFromString": "Wildcard Prompt from String",
|
||||||
|
|||||||
@@ -0,0 +1,183 @@
|
|||||||
|
import functools
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import shutil
|
||||||
|
import sys
|
||||||
|
import subprocess
|
||||||
|
import threading
|
||||||
|
import locale
|
||||||
|
import traceback
|
||||||
|
from typing import Set
|
||||||
|
|
||||||
|
|
||||||
|
if sys.argv[0] == 'install.py':
|
||||||
|
sys.path.append('.') # for portable version
|
||||||
|
|
||||||
|
|
||||||
|
impact_path = os.path.join(os.path.dirname(__file__), "modules")
|
||||||
|
subpack_path = os.path.join(os.path.dirname(__file__), "impact_subpack")
|
||||||
|
subpack_repo = "https://github.com/ltdrdata/ComfyUI-Impact-Subpack"
|
||||||
|
|
||||||
|
|
||||||
|
comfy_path = os.environ.get('COMFYUI_PATH')
|
||||||
|
if comfy_path is None:
|
||||||
|
print(f"\n[bold yellow]WARN: The `COMFYUI_PATH` environment variable is not set. Assuming `{os.path.dirname(__file__)}/../../` as the ComfyUI path.[/bold yellow]", file=sys.stderr)
|
||||||
|
comfy_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..'))
|
||||||
|
|
||||||
|
model_path = os.environ.get('COMFYUI_MODEL_PATH')
|
||||||
|
if model_path is None:
|
||||||
|
try:
|
||||||
|
import folder_paths
|
||||||
|
model_path = folder_paths.models_dir
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if model_path is None:
|
||||||
|
model_path = os.path.abspath(os.path.join(comfy_path, 'models'))
|
||||||
|
print(f"\n[bold yellow]WARN: The `COMFYUI_MODEL_PATH` environment variable is not set. Assuming `{model_path}` as the ComfyUI path.[/bold yellow]", file=sys.stderr)
|
||||||
|
|
||||||
|
|
||||||
|
sys.path.append(impact_path)
|
||||||
|
sys.path.append(comfy_path)
|
||||||
|
|
||||||
|
|
||||||
|
# ---
|
||||||
|
def handle_stream(stream, is_stdout):
|
||||||
|
stream.reconfigure(encoding=locale.getpreferredencoding(), errors='replace')
|
||||||
|
|
||||||
|
for msg in stream:
|
||||||
|
if is_stdout:
|
||||||
|
print(msg, end="", file=sys.stdout)
|
||||||
|
else:
|
||||||
|
print(msg, end="", file=sys.stderr)
|
||||||
|
|
||||||
|
|
||||||
|
def process_wrap(cmd_str, cwd=None, handler=None, env=None):
|
||||||
|
print(f"[Impact Pack] EXECUTE: {cmd_str} in '{cwd}'")
|
||||||
|
process = subprocess.Popen(cmd_str, cwd=cwd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env, text=True, bufsize=1)
|
||||||
|
|
||||||
|
if handler is None:
|
||||||
|
handler = handle_stream
|
||||||
|
|
||||||
|
stdout_thread = threading.Thread(target=handler, args=(process.stdout, True))
|
||||||
|
stderr_thread = threading.Thread(target=handler, args=(process.stderr, False))
|
||||||
|
|
||||||
|
stdout_thread.start()
|
||||||
|
stderr_thread.start()
|
||||||
|
|
||||||
|
stdout_thread.join()
|
||||||
|
stderr_thread.join()
|
||||||
|
|
||||||
|
return process.wait()
|
||||||
|
|
||||||
|
|
||||||
|
@functools.cache
|
||||||
|
def get_installed_packages() -> Set[str]:
|
||||||
|
try:
|
||||||
|
result = subprocess.check_output([sys.executable, '-m', 'pip', 'list'], universal_newlines=True)
|
||||||
|
pip_list = set([line.split()[0].lower() for line in result.split('\n') if line.strip()])
|
||||||
|
return pip_list
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
raise Exception(f"[ComfyUI-Impact-Pack] Failed to retrieve the information of installed pip packages.")
|
||||||
|
|
||||||
|
|
||||||
|
def is_package_installed(name: str) -> bool:
|
||||||
|
name = name.strip()
|
||||||
|
pattern = r'([^<>!=]+)([<>!=]=?)'
|
||||||
|
match = re.search(pattern, name)
|
||||||
|
|
||||||
|
if match:
|
||||||
|
name = match.group(1)
|
||||||
|
|
||||||
|
result = name.lower() in get_installed_packages()
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def is_requirements_installed(file_path: str) -> bool:
|
||||||
|
print(f"Requirements file: {file_path}")
|
||||||
|
if os.path.exists(file_path):
|
||||||
|
with open(file_path, "r") as file:
|
||||||
|
lines = file.readlines()
|
||||||
|
|
||||||
|
for line in lines:
|
||||||
|
if not is_package_installed(line):
|
||||||
|
return False
|
||||||
|
|
||||||
|
pip_install = [sys.executable, "-m", "pip", "install", "-U"]
|
||||||
|
|
||||||
|
# ---
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
import platform
|
||||||
|
import folder_paths
|
||||||
|
from torchvision.datasets.utils import download_url
|
||||||
|
import impact.config
|
||||||
|
|
||||||
|
print("### ComfyUI-Impact-Pack: Check dependencies")
|
||||||
|
def ensure_subpack():
|
||||||
|
import git
|
||||||
|
if os.path.exists(subpack_path):
|
||||||
|
try:
|
||||||
|
repo = git.Repo(subpack_path)
|
||||||
|
repo.remotes.origin.pull()
|
||||||
|
except:
|
||||||
|
traceback.print_exc()
|
||||||
|
if platform.system() == 'Windows':
|
||||||
|
print(f"[ComfyUI-Impact-Pack] Please turn off ComfyUI and remove '{subpack_path}' and restart ComfyUI.")
|
||||||
|
else:
|
||||||
|
shutil.rmtree(subpack_path)
|
||||||
|
git.Repo.clone_from(subpack_repo, subpack_path)
|
||||||
|
else:
|
||||||
|
git.Repo.clone_from(subpack_repo, subpack_path)
|
||||||
|
|
||||||
|
|
||||||
|
def install():
|
||||||
|
subpack_install_script = os.path.join(subpack_path, "install.py")
|
||||||
|
|
||||||
|
print(f"### ComfyUI-Impact-Pack: Updating subpack")
|
||||||
|
ensure_subpack() # The installation of the subpack must take place before ensure_pip. cv2 triggers a permission error.
|
||||||
|
|
||||||
|
new_env = os.environ.copy()
|
||||||
|
new_env["COMFYUI_PATH"] = comfy_path
|
||||||
|
new_env["COMFYUI_MODEL_PATH"] = model_path
|
||||||
|
|
||||||
|
if os.path.exists(subpack_install_script):
|
||||||
|
if not is_requirements_installed(os.path.join(subpack_path, 'requirements.txt')):
|
||||||
|
process_wrap(pip_install + ['-r', 'requirements.txt'], cwd=subpack_path)
|
||||||
|
|
||||||
|
process_wrap([sys.executable, 'install.py'], cwd=subpack_path, env=new_env)
|
||||||
|
else:
|
||||||
|
print(f"### ComfyUI-Impact-Pack: (Install Failed) Subpack\nFile not found: `{subpack_install_script}`")
|
||||||
|
|
||||||
|
# Download model
|
||||||
|
print("### ComfyUI-Impact-Pack: Check basic models")
|
||||||
|
sam_path = os.path.join(model_path, "sams")
|
||||||
|
onnx_path = os.path.join(model_path, "onnx")
|
||||||
|
|
||||||
|
if not os.path.exists(os.path.join(os.path.dirname(__file__), '..', 'skip_download_model')):
|
||||||
|
if not impact.config.get_config()['mmdet_skip']:
|
||||||
|
bbox_path = os.path.join(model_path, "mmdets", "bbox")
|
||||||
|
if not os.path.exists(bbox_path):
|
||||||
|
os.makedirs(bbox_path)
|
||||||
|
|
||||||
|
if not os.path.exists(os.path.join(bbox_path, "mmdet_anime-face_yolov3.pth")):
|
||||||
|
download_url("https://huggingface.co/dustysys/ddetailer/resolve/main/mmdet/bbox/mmdet_anime-face_yolov3.pth", bbox_path)
|
||||||
|
|
||||||
|
if not os.path.exists(os.path.join(bbox_path, "mmdet_anime-face_yolov3.py")):
|
||||||
|
download_url("https://raw.githubusercontent.com/Bing-su/dddetailer/master/config/mmdet_anime-face_yolov3.py", bbox_path)
|
||||||
|
|
||||||
|
if not os.path.exists(os.path.join(sam_path, "sam_vit_b_01ec64.pth")):
|
||||||
|
download_url("https://dl.fbaipublicfiles.com/segment_anything/sam_vit_b_01ec64.pth", sam_path)
|
||||||
|
|
||||||
|
if not os.path.exists(onnx_path):
|
||||||
|
print(f"### ComfyUI-Impact-Pack: onnx model directory created ({onnx_path})")
|
||||||
|
os.mkdir(onnx_path)
|
||||||
|
|
||||||
|
impact.config.write_config()
|
||||||
|
|
||||||
|
install()
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print("[ERROR] ComfyUI-Impact-Pack: Dependency installation has failed. Please install manually.")
|
||||||
|
traceback.print_exc()
|
||||||
+5
-163
@@ -5,7 +5,6 @@ import subprocess
|
|||||||
import threading
|
import threading
|
||||||
import locale
|
import locale
|
||||||
import traceback
|
import traceback
|
||||||
import re
|
|
||||||
|
|
||||||
|
|
||||||
if sys.argv[0] == 'install.py':
|
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")
|
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_path = os.path.join(os.path.dirname(__file__), "impact_subpack")
|
||||||
subpack_repo = "https://github.com/ltdrdata/ComfyUI-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:
|
try:
|
||||||
import platform
|
import platform
|
||||||
|
import folder_paths
|
||||||
from torchvision.datasets.utils import download_url
|
from torchvision.datasets.utils import download_url
|
||||||
import impact.config
|
import impact.config
|
||||||
|
|
||||||
|
|
||||||
print("### ComfyUI-Impact-Pack: Check dependencies")
|
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():
|
def ensure_subpack():
|
||||||
import git
|
import git
|
||||||
if os.path.exists(subpack_path):
|
if os.path.exists(subpack_path):
|
||||||
@@ -145,106 +92,11 @@ try:
|
|||||||
else:
|
else:
|
||||||
git.Repo.clone_from(subpack_repo, subpack_path)
|
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():
|
def install():
|
||||||
subpack_install_script = os.path.join(subpack_path, "install.py")
|
subpack_install_script = os.path.join(subpack_path, "install.py")
|
||||||
|
|
||||||
print(f"### ComfyUI-Impact-Pack: Updating subpack")
|
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.
|
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()
|
||||||
@@ -253,29 +105,20 @@ try:
|
|||||||
|
|
||||||
if os.path.exists(subpack_install_script):
|
if os.path.exists(subpack_install_script):
|
||||||
process_wrap([sys.executable, 'install.py'], cwd=subpack_path, env=new_env)
|
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:
|
else:
|
||||||
print(f"### ComfyUI-Impact-Pack: (Install Failed) Subpack\nFile not found: `{subpack_install_script}`")
|
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
|
# Download model
|
||||||
print("### ComfyUI-Impact-Pack: Check basic models")
|
print("### ComfyUI-Impact-Pack: Check basic models")
|
||||||
bbox_path = os.path.join(model_path, "mmdets", "bbox")
|
|
||||||
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 os.path.exists(bbox_path):
|
|
||||||
os.makedirs(bbox_path)
|
|
||||||
|
|
||||||
if not impact.config.get_config()['mmdet_skip']:
|
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")):
|
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)
|
||||||
|
|
||||||
@@ -291,7 +134,6 @@ try:
|
|||||||
|
|
||||||
impact.config.write_config()
|
impact.config.write_config()
|
||||||
|
|
||||||
|
|
||||||
install()
|
install()
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|||||||
+21
-7
@@ -237,7 +237,7 @@ app.registerExtension({
|
|||||||
if(nodeData.name == "ImpactControlBridge") {
|
if(nodeData.name == "ImpactControlBridge") {
|
||||||
const onConnectionsChange = nodeType.prototype.onConnectionsChange;
|
const onConnectionsChange = nodeType.prototype.onConnectionsChange;
|
||||||
nodeType.prototype.onConnectionsChange = function (type, index, connected, link_info) {
|
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;
|
return;
|
||||||
|
|
||||||
// assign type
|
// assign type
|
||||||
@@ -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;
|
||||||
@@ -383,7 +383,7 @@ app.registerExtension({
|
|||||||
}
|
}
|
||||||
|
|
||||||
let select_slot = this.inputs.find(x => x.name == "select");
|
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].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);
|
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)
|
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' ||
|
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 === 'ImpactCombineConditionings' || nodeData.name === 'ImpactConcatConditionings' ||
|
||||||
nodeData.name === 'ImpactSEGSConcat' ||
|
nodeData.name === 'ImpactSEGSConcat' ||
|
||||||
nodeData.name === 'ImpactSwitch' || nodeData.name === 'LatentSwitch' || nodeData.name == 'SEGSSwitch') {
|
nodeData.name === 'ImpactSwitch' || nodeData.name === 'LatentSwitch' || nodeData.name == 'SEGSSwitch') {
|
||||||
@@ -405,6 +406,15 @@ app.registerExtension({
|
|||||||
input_name = "image";
|
input_name = "image";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'ImpactMakeMaskList':
|
||||||
|
case 'ImpactMakeMaskBatch':
|
||||||
|
input_name = "mask";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'ImpactMakeAnyList':
|
||||||
|
input_name = "value";
|
||||||
|
break;
|
||||||
|
|
||||||
case 'ImpactSEGSConcat':
|
case 'ImpactSEGSConcat':
|
||||||
input_name = "segs";
|
input_name = "segs";
|
||||||
break;
|
break;
|
||||||
@@ -473,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;
|
||||||
@@ -527,7 +541,7 @@ app.registerExtension({
|
|||||||
this.addInput(`${input_name}${slot_i}`, this.outputs[0].type);
|
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].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);
|
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)
|
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;
|
let orig = refresh_btn.onclick;
|
||||||
|
|
||||||
refresh_btn.onclick = function() {
|
if(refresh_btn) {
|
||||||
orig();
|
refresh_btn.onclick = function() {
|
||||||
api.fetchApi('/impact/wildcards/refresh');
|
orig();
|
||||||
};
|
api.fetchApi('/impact/wildcards/refresh');
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
refresh_btn2.addEventListener('click', function() {
|
if(refresh_btn2) {
|
||||||
api.fetchApi('/impact/wildcards/refresh');
|
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
|
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:
|
class SEGSDetailerForAnimateDiff:
|
||||||
@classmethod
|
@classmethod
|
||||||
def INPUT_TYPES(cls):
|
def INPUT_TYPES(cls):
|
||||||
@@ -53,6 +60,9 @@ class SEGSDetailerForAnimateDiff:
|
|||||||
new_segs = []
|
new_segs = []
|
||||||
cnet_image_list = []
|
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]:
|
for seg in segs[1]:
|
||||||
cropped_image_frames = None
|
cropped_image_frames = None
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,8 @@ class PreviewBridge:
|
|||||||
"image": ("STRING", {"default": ""}),
|
"image": ("STRING", {"default": ""}),
|
||||||
},
|
},
|
||||||
"optional": {
|
"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."}),
|
||||||
|
"restore_mask": (["never", "always", "if_same_size"], {"tooltip": "if_same_size: If the changed input image is the same size as the previous image, restore using the last saved mask\nalways: Whenever the input image changes, always restore using the last saved mask\nnever: Do not restore the mask.\n`restore_mask` has higher priority than `block`"}),
|
||||||
},
|
},
|
||||||
"hidden": {"unique_id": "UNIQUE_ID", "extra_pnginfo": "EXTRA_PNGINFO"},
|
"hidden": {"unique_id": "UNIQUE_ID", "extra_pnginfo": "EXTRA_PNGINFO"},
|
||||||
}
|
}
|
||||||
@@ -75,7 +76,7 @@ class PreviewBridge:
|
|||||||
|
|
||||||
return image, mask.unsqueeze(0), ui_item
|
return image, mask.unsqueeze(0), ui_item
|
||||||
|
|
||||||
def doit(self, images, image, unique_id, block=False, prompt=None, extra_pnginfo=None):
|
def doit(self, images, image, unique_id, block=False, restore_mask="never", prompt=None, extra_pnginfo=None):
|
||||||
need_refresh = False
|
need_refresh = False
|
||||||
|
|
||||||
if unique_id not in core.preview_bridge_cache:
|
if unique_id not in core.preview_bridge_cache:
|
||||||
@@ -88,10 +89,25 @@ class PreviewBridge:
|
|||||||
pixels, mask, path_item = PreviewBridge.load_image(image)
|
pixels, mask, path_item = PreviewBridge.load_image(image)
|
||||||
image = [path_item]
|
image = [path_item]
|
||||||
else:
|
else:
|
||||||
res = nodes.PreviewImage().save_images(images, filename_prefix="PreviewBridge/PB-", prompt=prompt, extra_pnginfo=extra_pnginfo)
|
if restore_mask != "never":
|
||||||
|
mask = core.preview_bridge_last_mask_cache.get(unique_id)
|
||||||
|
if mask is None or (restore_mask != "always" and mask.shape[1:] != images.shape[1:3]):
|
||||||
|
mask = None
|
||||||
|
else:
|
||||||
|
mask = None
|
||||||
|
|
||||||
|
if mask is None:
|
||||||
|
mask = torch.zeros((64, 64), dtype=torch.float32, device="cpu")
|
||||||
|
res = nodes.PreviewImage().save_images(images, filename_prefix="PreviewBridge/PB-", prompt=prompt, extra_pnginfo=extra_pnginfo)
|
||||||
|
else:
|
||||||
|
masked_images = tensor_convert_rgba(images)
|
||||||
|
resized_mask = resize_mask(mask, (images.shape[1], images.shape[2])).unsqueeze(3)
|
||||||
|
resized_mask = 1 - resized_mask
|
||||||
|
tensor_putalpha(masked_images, resized_mask)
|
||||||
|
res = nodes.PreviewImage().save_images(masked_images, filename_prefix="PreviewBridge/PB-", prompt=prompt, extra_pnginfo=extra_pnginfo)
|
||||||
|
|
||||||
image2 = res['ui']['images']
|
image2 = res['ui']['images']
|
||||||
pixels = images
|
pixels = images
|
||||||
mask = torch.zeros((64, 64), dtype=torch.float32, device="cpu")
|
|
||||||
|
|
||||||
path = os.path.join(folder_paths.get_temp_directory(), 'PreviewBridge', image2[0]['filename'])
|
path = os.path.join(folder_paths.get_temp_directory(), 'PreviewBridge', image2[0]['filename'])
|
||||||
core.set_previewbridge_image(unique_id, path, image2[0])
|
core.set_previewbridge_image(unique_id, path, image2[0])
|
||||||
@@ -103,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:
|
||||||
@@ -112,6 +128,9 @@ class PreviewBridge:
|
|||||||
else:
|
else:
|
||||||
result = pixels, mask
|
result = pixels, mask
|
||||||
|
|
||||||
|
if not is_empty_mask:
|
||||||
|
core.preview_bridge_last_mask_cache[unique_id] = mask
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"ui": {"images": image},
|
"ui": {"images": image},
|
||||||
"result": result,
|
"result": result,
|
||||||
@@ -196,7 +215,8 @@ class PreviewBridgeLatent:
|
|||||||
},
|
},
|
||||||
"optional": {
|
"optional": {
|
||||||
"vae_opt": ("VAE", ),
|
"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."}),
|
||||||
|
"restore_mask": (["never", "always", "if_same_size"], {"tooltip": "if_same_size: If the changed input latent is the same size as the previous latent, restore using the last saved mask\nalways: Whenever the input latent changes, always restore using the last saved mask\nnever: Do not restore the mask.\n`restore_mask` has higher priority than `block`\nIf the input latent already has a mask, do not restore mask."}),
|
||||||
},
|
},
|
||||||
"hidden": {"unique_id": "UNIQUE_ID", "prompt": "PROMPT", "extra_pnginfo": "EXTRA_PNGINFO"},
|
"hidden": {"unique_id": "UNIQUE_ID", "prompt": "PROMPT", "extra_pnginfo": "EXTRA_PNGINFO"},
|
||||||
}
|
}
|
||||||
@@ -252,7 +272,7 @@ class PreviewBridgeLatent:
|
|||||||
|
|
||||||
return image, mask, ui_item
|
return image, mask, ui_item
|
||||||
|
|
||||||
def doit(self, latent, image, preview_method, vae_opt=None, block=False, unique_id=None, prompt=None, extra_pnginfo=None):
|
def doit(self, latent, image, preview_method, vae_opt=None, block=False, unique_id=None, restore_mask='never', prompt=None, extra_pnginfo=None):
|
||||||
latent_channels = latent['samples'].shape[1]
|
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
|
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
|
||||||
|
|
||||||
@@ -311,13 +331,28 @@ class PreviewBridgeLatent:
|
|||||||
'type': 'temp',
|
'type': 'temp',
|
||||||
}]
|
}]
|
||||||
|
|
||||||
is_empty_mask = torch.all(mask == 1)
|
is_empty_mask = False
|
||||||
else:
|
else:
|
||||||
mask = torch.ones(latent['samples'].shape[2:], dtype=torch.float32, device="cpu").unsqueeze(0)
|
if restore_mask != "never":
|
||||||
res = nodes.PreviewImage().save_images(decoded_image, filename_prefix="PreviewBridge/PBL-", prompt=prompt, extra_pnginfo=extra_pnginfo)
|
mask = core.preview_bridge_last_mask_cache.get(unique_id)
|
||||||
|
if mask is None or (restore_mask != "always" and mask.shape[1:] != decoded_image.shape[1:3]):
|
||||||
|
mask = None
|
||||||
|
else:
|
||||||
|
mask = None
|
||||||
|
|
||||||
|
if mask is None:
|
||||||
|
mask = torch.ones(latent['samples'].shape[2:], dtype=torch.float32, device="cpu").unsqueeze(0)
|
||||||
|
res = nodes.PreviewImage().save_images(decoded_image, filename_prefix="PreviewBridge/PBL-", prompt=prompt, extra_pnginfo=extra_pnginfo)
|
||||||
|
else:
|
||||||
|
masked_images = tensor_convert_rgba(decoded_image)
|
||||||
|
resized_mask = resize_mask(mask, (decoded_image.shape[1], decoded_image.shape[2])).unsqueeze(3)
|
||||||
|
resized_mask = 1 - resized_mask
|
||||||
|
tensor_putalpha(masked_images, resized_mask)
|
||||||
|
res = nodes.PreviewImage().save_images(masked_images, filename_prefix="PreviewBridge/PBL-", prompt=prompt, extra_pnginfo=extra_pnginfo)
|
||||||
|
|
||||||
res_image = res['ui']['images']
|
res_image = res['ui']['images']
|
||||||
|
|
||||||
is_empty_mask = True
|
is_empty_mask = torch.all(mask == 1)
|
||||||
|
|
||||||
path = os.path.join(folder_paths.get_temp_directory(), 'PreviewBridge', res_image[0]['filename'])
|
path = os.path.join(folder_paths.get_temp_directory(), 'PreviewBridge', res_image[0]['filename'])
|
||||||
core.set_previewbridge_image(unique_id, path, res_image[0])
|
core.set_previewbridge_image(unique_id, path, res_image[0])
|
||||||
@@ -327,7 +362,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:
|
||||||
@@ -336,6 +371,9 @@ class PreviewBridgeLatent:
|
|||||||
else:
|
else:
|
||||||
result = res_latent, mask
|
result = res_latent, mask
|
||||||
|
|
||||||
|
if not is_empty_mask:
|
||||||
|
core.preview_bridge_last_mask_cache[unique_id] = mask
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"ui": {"images": res_image},
|
"ui": {"images": res_image},
|
||||||
"result": result,
|
"result": result,
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import configparser
|
import configparser
|
||||||
import os
|
import os
|
||||||
|
|
||||||
version_code = [7, 4, 2]
|
version_code = [7, 11, 2]
|
||||||
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 = 22
|
dependency_version = 23
|
||||||
|
|
||||||
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")
|
||||||
|
|||||||
+47
-20
@@ -24,6 +24,8 @@ from comfy import model_management
|
|||||||
from impact import utils
|
from impact import utils
|
||||||
from impact import impact_sampling
|
from impact import impact_sampling
|
||||||
from concurrent.futures import ThreadPoolExecutor
|
from concurrent.futures import ThreadPoolExecutor
|
||||||
|
import inspect
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from comfy_extras import nodes_differential_diffusion
|
from comfy_extras import nodes_differential_diffusion
|
||||||
@@ -39,7 +41,10 @@ SEG = namedtuple("SEG",
|
|||||||
pb_id_cnt = time.time()
|
pb_id_cnt = time.time()
|
||||||
preview_bridge_image_id_map = {}
|
preview_bridge_image_id_map = {}
|
||||||
preview_bridge_image_name_map = {}
|
preview_bridge_image_name_map = {}
|
||||||
|
|
||||||
preview_bridge_cache = {}
|
preview_bridge_cache = {}
|
||||||
|
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]']
|
||||||
@@ -1349,9 +1354,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]
|
||||||
|
|
||||||
@@ -1373,12 +1383,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)
|
||||||
@@ -1392,12 +1402,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)
|
||||||
@@ -1413,12 +1423,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)
|
||||||
@@ -1445,12 +1455,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)
|
||||||
@@ -1680,6 +1690,9 @@ class PixelKSampleUpscaler:
|
|||||||
self.hook.pre_ksample(model, seed, steps, cfg, sampler_name, scheduler, positive, negative,
|
self.hook.pre_ksample(model, seed, steps, cfg, sampler_name, scheduler, positive, negative,
|
||||||
upscaled_latent, denoise)
|
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)
|
refined_latent = self.sample(model, seed, steps, cfg, sampler_name, scheduler, positive, negative, upscaled_latent, denoise, upscaled_images)
|
||||||
return refined_latent
|
return refined_latent
|
||||||
|
|
||||||
@@ -1709,6 +1722,9 @@ class PixelKSampleUpscaler:
|
|||||||
self.hook.pre_ksample(model, seed, steps, cfg, sampler_name, scheduler, positive, negative,
|
self.hook.pre_ksample(model, seed, steps, cfg, sampler_name, scheduler, positive, negative,
|
||||||
upscaled_latent, denoise)
|
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)
|
refined_latent = self.sample(model, seed, steps, cfg, sampler_name, scheduler, positive, negative, upscaled_latent, denoise, upscaled_images)
|
||||||
return refined_latent
|
return refined_latent
|
||||||
|
|
||||||
@@ -1819,13 +1835,14 @@ class ControlNetWrapper:
|
|||||||
|
|
||||||
class ControlNetAdvancedWrapper:
|
class ControlNetAdvancedWrapper:
|
||||||
def __init__(self, control_net, strength, start_percent, end_percent, preprocessor, prev_control_net=None,
|
def __init__(self, control_net, strength, start_percent, end_percent, preprocessor, prev_control_net=None,
|
||||||
original_size=None, crop_region=None, control_image=None):
|
original_size=None, crop_region=None, control_image=None, vae=None):
|
||||||
self.control_net = control_net
|
self.control_net = control_net
|
||||||
self.strength = strength
|
self.strength = strength
|
||||||
self.preprocessor = preprocessor
|
self.preprocessor = preprocessor
|
||||||
self.prev_control_net = prev_control_net
|
self.prev_control_net = prev_control_net
|
||||||
self.start_percent = start_percent
|
self.start_percent = start_percent
|
||||||
self.end_percent = end_percent
|
self.end_percent = end_percent
|
||||||
|
self.vae = vae
|
||||||
|
|
||||||
if original_size is not None and crop_region is not None and control_image is not None:
|
if original_size is not None and crop_region is not None and control_image is not None:
|
||||||
self.control_image = utils.tensor_resize(control_image, original_size[1], original_size[0])
|
self.control_image = utils.tensor_resize(control_image, original_size[1], original_size[0])
|
||||||
@@ -1866,7 +1883,17 @@ class ControlNetAdvancedWrapper:
|
|||||||
"To use 'ControlNetAdvancedWrapper' for AnimateDiff, 'ComfyUI-Advanced-ControlNet' extension is required.")
|
"To use 'ControlNetAdvancedWrapper' for AnimateDiff, 'ComfyUI-Advanced-ControlNet' extension is required.")
|
||||||
raise Exception("'ACN_AdvancedControlNetApply' node isn't installed.")
|
raise Exception("'ACN_AdvancedControlNetApply' node isn't installed.")
|
||||||
else:
|
else:
|
||||||
positive, negative = nodes.ControlNetApplyAdvanced().apply_controlnet(positive, negative, self.control_net, cnet_image, self.strength, self.start_percent, self.end_percent)
|
if self.vae is not None:
|
||||||
|
apply_controlnet = nodes.ControlNetApplyAdvanced().apply_controlnet
|
||||||
|
signature = inspect.signature(apply_controlnet)
|
||||||
|
|
||||||
|
if 'vae' in signature.parameters:
|
||||||
|
positive, negative = nodes.ControlNetApplyAdvanced().apply_controlnet(positive, negative, self.control_net, cnet_image, self.strength, self.start_percent, self.end_percent, vae=self.vae)
|
||||||
|
else:
|
||||||
|
print(f"[Impact Pack] ERROR: The ComfyUI version is outdated. VAE cannot be used in ApplyControlNet.")
|
||||||
|
raise Exception("[Impact Pack] ERROR: The ComfyUI version is outdated. VAE cannot be used in ApplyControlNet.")
|
||||||
|
else:
|
||||||
|
positive, negative = nodes.ControlNetApplyAdvanced().apply_controlnet(positive, negative, self.control_net, cnet_image, self.strength, self.start_percent, self.end_percent)
|
||||||
|
|
||||||
return positive, negative, cnet_image_list
|
return positive, negative, cnet_image_list
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,14 @@ import impact.wildcards as wildcards
|
|||||||
from . import hooks
|
from . import hooks
|
||||||
from . import utils
|
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')
|
warnings.filterwarnings('ignore', category=UserWarning, message='TypedStorage is deprecated')
|
||||||
|
|
||||||
model_path = folder_paths.models_dir
|
model_path = folder_paths.models_dir
|
||||||
@@ -257,6 +265,9 @@ class DetailerForEach:
|
|||||||
else:
|
else:
|
||||||
ordered_segs = segs[1]
|
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):
|
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 = crop_ndarray4(image.cpu().numpy(), seg.crop_region) # Never use seg.cropped_image to handle overlapping area
|
||||||
cropped_image = to_tensor(cropped_image)
|
cropped_image = to_tensor(cropped_image)
|
||||||
@@ -308,6 +319,7 @@ class DetailerForEach:
|
|||||||
if wildcard_item and wildcard_item.strip() == '[STOP]':
|
if wildcard_item and wildcard_item.strip() == '[STOP]':
|
||||||
break
|
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,
|
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,
|
seg.bbox, seg_seed, steps, cfg, sampler_name, scheduler,
|
||||||
cropped_positive, cropped_negative, denoise, cropped_mask, force_inpaint,
|
cropped_positive, cropped_negative, denoise, cropped_mask, force_inpaint,
|
||||||
@@ -327,7 +339,7 @@ class DetailerForEach:
|
|||||||
# use image paste
|
# use image paste
|
||||||
image = image.cpu()
|
image = image.cpu()
|
||||||
enhanced_image = enhanced_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)
|
enhanced_list.append(enhanced_image)
|
||||||
|
|
||||||
if detailer_hook is not None:
|
if detailer_hook is not None:
|
||||||
@@ -345,7 +357,7 @@ class DetailerForEach:
|
|||||||
else:
|
else:
|
||||||
new_seg_image = None
|
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_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)
|
new_segs.append(new_seg)
|
||||||
@@ -1238,6 +1250,7 @@ class IterativeLatentUpscale:
|
|||||||
upscale_factor_unit = max(0, (upscale_factor - 1.0) / steps)
|
upscale_factor_unit = max(0, (upscale_factor - 1.0) / steps)
|
||||||
|
|
||||||
current_latent = samples
|
current_latent = samples
|
||||||
|
noise_mask = current_latent.get('noise_mask')
|
||||||
scale = 1
|
scale = 1
|
||||||
|
|
||||||
for i in range(steps-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}) ")
|
print(f"IterativeLatentUpscale[{i+1}/{steps}]: {new_w:.1f}x{new_h:.1f} (scale:{scale:.2f}) ")
|
||||||
step_info = i, steps
|
step_info = i, steps
|
||||||
current_latent = upscaler.upscale_shape(step_info, current_latent, new_w, new_h, temp_prefix)
|
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:
|
if scale < upscale_factor:
|
||||||
new_w = w*upscale_factor
|
new_w = w*upscale_factor
|
||||||
@@ -1263,7 +1278,7 @@ class IterativeLatentUpscale:
|
|||||||
|
|
||||||
core.update_node_status(unique_id, "", None)
|
core.update_node_status(unique_id, "", None)
|
||||||
|
|
||||||
return (current_latent, upscaler.vae)
|
return current_latent, upscaler.vae
|
||||||
|
|
||||||
|
|
||||||
class IterativeImageUpscale:
|
class IterativeImageUpscale:
|
||||||
@@ -1696,6 +1711,25 @@ class ToBinaryMask:
|
|||||||
return (mask,)
|
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:
|
class BitwiseAndMask:
|
||||||
@classmethod
|
@classmethod
|
||||||
def INPUT_TYPES(s):
|
def INPUT_TYPES(s):
|
||||||
|
|||||||
@@ -24,37 +24,6 @@ import random
|
|||||||
from server import PromptServer
|
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
|
sam_predictor = None
|
||||||
default_sam_model_name = os.path.join(impact_pack.model_path, "sams", "sam_vit_b_01ec64.pth")
|
default_sam_model_name = os.path.join(impact_pack.model_path, "sams", "sam_vit_b_01ec64.pth")
|
||||||
|
|
||||||
@@ -440,9 +409,14 @@ def gc_preview_bridge_cache(json_data):
|
|||||||
|
|
||||||
for key in list(core.preview_bridge_cache.keys()):
|
for key in list(core.preview_bridge_cache.keys()):
|
||||||
if key not in prompt_keys:
|
if key not in prompt_keys:
|
||||||
print(f"key deleted: {key}")
|
# print(f"key deleted [PB]: {key}")
|
||||||
del core.preview_bridge_cache[key]
|
del core.preview_bridge_cache[key]
|
||||||
|
|
||||||
|
for key in list(core.preview_bridge_last_mask_cache.keys()):
|
||||||
|
if key not in prompt_keys:
|
||||||
|
# print(f"key deleted [PB_last_mask]: {key}")
|
||||||
|
del core.preview_bridge_last_mask_cache[key]
|
||||||
|
|
||||||
|
|
||||||
def workflow_imagereceiver_update(json_data):
|
def workflow_imagereceiver_update(json_data):
|
||||||
prompt = json_data['prompt']
|
prompt = json_data['prompt']
|
||||||
|
|||||||
@@ -272,6 +272,24 @@ class ImpactFloat:
|
|||||||
return (value, )
|
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:
|
class ImpactValueSender:
|
||||||
@classmethod
|
@classmethod
|
||||||
def INPUT_TYPES(cls):
|
def INPUT_TYPES(cls):
|
||||||
@@ -673,7 +691,7 @@ class ImpactControlBridge:
|
|||||||
def doit(self, value, mode, behavior="Stop", unique_id=None, prompt=None, extra_pnginfo=None):
|
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.")
|
||||||
@@ -748,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
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import folder_paths
|
import folder_paths
|
||||||
import impact.wildcards
|
import impact.wildcards
|
||||||
|
from impact.utils import any_typ
|
||||||
|
|
||||||
|
|
||||||
class ToDetailerPipe:
|
class ToDetailerPipe:
|
||||||
@classmethod
|
@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
|
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:
|
class ToBasicPipe:
|
||||||
@classmethod
|
@classmethod
|
||||||
def INPUT_TYPES(s):
|
def INPUT_TYPES(s):
|
||||||
|
|||||||
@@ -14,6 +14,13 @@ from comfy.cli_args import args
|
|||||||
import math
|
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:
|
class SEGSDetailer:
|
||||||
@classmethod
|
@classmethod
|
||||||
def INPUT_TYPES(s):
|
def INPUT_TYPES(s):
|
||||||
@@ -69,6 +76,9 @@ class SEGSDetailer:
|
|||||||
new_segs = []
|
new_segs = []
|
||||||
cnet_pil_list = []
|
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):
|
for i in range(batch_size):
|
||||||
seed += 1
|
seed += 1
|
||||||
for seg in segs[1]:
|
for seg in segs[1]:
|
||||||
@@ -694,6 +704,68 @@ class SEGSToMaskBatch:
|
|||||||
return (mask_batch,)
|
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:
|
class SEGSConcat:
|
||||||
@classmethod
|
@classmethod
|
||||||
def INPUT_TYPES(s):
|
def INPUT_TYPES(s):
|
||||||
@@ -824,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:
|
||||||
@@ -1029,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
|
||||||
@@ -1290,6 +1362,8 @@ class ControlNetApplySEGS:
|
|||||||
RETURN_TYPES = ("SEGS",)
|
RETURN_TYPES = ("SEGS",)
|
||||||
FUNCTION = "doit"
|
FUNCTION = "doit"
|
||||||
|
|
||||||
|
DEPRECATED = True
|
||||||
|
|
||||||
CATEGORY = "ImpactPack/Util"
|
CATEGORY = "ImpactPack/Util"
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@@ -1317,7 +1391,8 @@ class ControlNetApplyAdvancedSEGS:
|
|||||||
},
|
},
|
||||||
"optional": {
|
"optional": {
|
||||||
"segs_preprocessor": ("SEGS_PREPROCESSOR",),
|
"segs_preprocessor": ("SEGS_PREPROCESSOR",),
|
||||||
"control_image": ("IMAGE",)
|
"control_image": ("IMAGE",),
|
||||||
|
"vae": ("VAE",)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1327,13 +1402,13 @@ class ControlNetApplyAdvancedSEGS:
|
|||||||
CATEGORY = "ImpactPack/Util"
|
CATEGORY = "ImpactPack/Util"
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def doit(segs, control_net, strength, start_percent, end_percent, segs_preprocessor=None, control_image=None):
|
def doit(segs, control_net, strength, start_percent, end_percent, segs_preprocessor=None, control_image=None, vae=None):
|
||||||
new_segs = []
|
new_segs = []
|
||||||
|
|
||||||
for seg in segs[1]:
|
for seg in segs[1]:
|
||||||
control_net_wrapper = core.ControlNetAdvancedWrapper(control_net, strength, start_percent, end_percent, segs_preprocessor,
|
control_net_wrapper = core.ControlNetAdvancedWrapper(control_net, strength, start_percent, end_percent, segs_preprocessor,
|
||||||
seg.control_net_wrapper, original_size=segs[0], crop_region=seg.crop_region,
|
seg.control_net_wrapper, original_size=segs[0], crop_region=seg.crop_region,
|
||||||
control_image=control_image)
|
control_image=control_image, vae=vae)
|
||||||
new_seg = SEG(seg.cropped_image, seg.cropped_mask, seg.confidence, seg.crop_region, seg.bbox, seg.label, control_net_wrapper)
|
new_seg = SEG(seg.cropped_image, seg.cropped_mask, seg.confidence, seg.crop_region, seg.bbox, seg.label, control_net_wrapper)
|
||||||
new_segs.append(new_seg)
|
new_segs.append(new_seg)
|
||||||
|
|
||||||
@@ -1403,8 +1478,6 @@ class SEGSPicker:
|
|||||||
|
|
||||||
RETURN_TYPES = ("SEGS", )
|
RETURN_TYPES = ("SEGS", )
|
||||||
|
|
||||||
OUTPUT_NODE = True
|
|
||||||
|
|
||||||
FUNCTION = "doit"
|
FUNCTION = "doit"
|
||||||
|
|
||||||
CATEGORY = "ImpactPack/Util"
|
CATEGORY = "ImpactPack/Util"
|
||||||
|
|||||||
@@ -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"}),
|
||||||
@@ -163,7 +170,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 +188,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 +373,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:]:
|
||||||
@@ -392,6 +399,50 @@ class ImageBatchToImageList:
|
|||||||
return (images, )
|
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:
|
class MakeImageList:
|
||||||
@classmethod
|
@classmethod
|
||||||
def INPUT_TYPES(s):
|
def INPUT_TYPES(s):
|
||||||
@@ -437,6 +488,31 @@ class MakeImageBatch:
|
|||||||
return (image1,)
|
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:
|
class ReencodeLatent:
|
||||||
@classmethod
|
@classmethod
|
||||||
def INPUT_TYPES(s):
|
def INPUT_TYPES(s):
|
||||||
|
|||||||
@@ -5,8 +5,7 @@ import numpy as np
|
|||||||
import folder_paths
|
import folder_paths
|
||||||
import nodes
|
import nodes
|
||||||
from . import config
|
from . import config
|
||||||
from PIL import Image, ImageFilter
|
from PIL import Image
|
||||||
from scipy.ndimage import zoom
|
|
||||||
import comfy
|
import comfy
|
||||||
|
|
||||||
|
|
||||||
@@ -579,6 +578,14 @@ def apply_mask_alpha_to_pil(decoded_pil, mask):
|
|||||||
return decoded_rgba
|
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):
|
def try_install_custom_node(custom_node_url, msg):
|
||||||
try:
|
try:
|
||||||
import cm_global
|
import cm_global
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ from impact import config
|
|||||||
|
|
||||||
wildcards_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "wildcards"))
|
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_lock = threading.Lock()
|
||||||
wildcard_dict = {}
|
wildcard_dict = {}
|
||||||
|
|
||||||
@@ -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)
|
||||||
|
|
||||||
@@ -121,7 +121,7 @@ def process(text, seed=None):
|
|||||||
select_sep = ' '
|
select_sep = ' '
|
||||||
range_pattern = r'(\d+)(-(\d+))?'
|
range_pattern = r'(\d+)(-(\d+))?'
|
||||||
range_pattern2 = r'-(\d+)'
|
range_pattern2 = r'-(\d+)'
|
||||||
wildcard_pattern = r"__([\w.\-+/*\\]+)__"
|
wildcard_pattern = r"__([\w.\-+/*\\]+?)__"
|
||||||
|
|
||||||
if len(multi_select_pattern) > 1:
|
if len(multi_select_pattern) > 1:
|
||||||
r = re.match(range_pattern, options[0])
|
r = re.match(range_pattern, options[0])
|
||||||
@@ -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,8 +199,36 @@ 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)
|
||||||
|
|
||||||
replacements_found = False
|
replacements_found = False
|
||||||
@@ -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):
|
||||||
|
|||||||
+1
-1
@@ -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 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.11.2"
|
||||||
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"]
|
||||||
|
|
||||||
|
|||||||
@@ -6,3 +6,5 @@ opencv-python-headless
|
|||||||
GitPython
|
GitPython
|
||||||
scipy>=1.11.4
|
scipy>=1.11.4
|
||||||
numpy<2
|
numpy<2
|
||||||
|
dill
|
||||||
|
matplotlib
|
||||||
Reference in New Issue
Block a user