Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bfcb8674b3 | ||
|
|
839e5a9f90 | ||
|
|
5a2adda580 | ||
|
|
d05882a9e0 | ||
|
|
d900939861 | ||
|
|
cd2696f6fd | ||
|
|
0b1ac0f1c5 | ||
|
|
216d7fd60c | ||
|
|
28cd2f70b2 | ||
|
|
2708eba825 | ||
|
|
c19ab92172 | ||
|
|
6e3d07277b | ||
|
|
782c6f439e | ||
|
|
0e3e6a193a | ||
|
|
798776838e | ||
|
|
66493d8cb4 | ||
|
|
b4fd0834e0 | ||
|
|
808b0dedf0 | ||
|
|
c6056b132d | ||
|
|
1ae7cae2df | ||
|
|
ccb6285548 |
@@ -7,15 +7,19 @@ on:
|
|||||||
paths:
|
paths:
|
||||||
- "pyproject.toml"
|
- "pyproject.toml"
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
issues: write
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
publish-node:
|
publish-node:
|
||||||
name: Publish Custom Node to registry
|
name: Publish Custom Node to registry
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
if: ${{ github.repository_owner == 'ltdrdata' }}
|
||||||
steps:
|
steps:
|
||||||
- name: Check out code
|
- name: Check out code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
- name: Publish Custom Node
|
- name: Publish Custom Node
|
||||||
uses: Comfy-Org/publish-node-action@main
|
uses: Comfy-Org/publish-node-action@v1
|
||||||
with:
|
with:
|
||||||
## Add your own personal access token to your Github Repository secrets and reference it here.
|
## Add your own personal access token to your Github Repository secrets and reference it here.
|
||||||
personal_access_token: ${{ secrets.REGISTRY_ACCESS_TOKEN }}
|
personal_access_token: ${{ secrets.REGISTRY_ACCESS_TOKEN }}
|
||||||
|
|||||||
@@ -32,6 +32,31 @@ NOTE: The UltralyticsDetectorProvider node is not part of the ComfyUI-Impact-Pac
|
|||||||
* With the addition of wildcard support in FaceDetailer, the structure of DETAILER_PIPE-related nodes and Detailer nodes has changed. There may be malfunctions when using the existing workflow.
|
* With the addition of wildcard support in FaceDetailer, the structure of DETAILER_PIPE-related nodes and Detailer nodes has changed. There may be malfunctions when using the existing workflow.
|
||||||
|
|
||||||
|
|
||||||
|
## How To Install
|
||||||
|
|
||||||
|
### **Recommended**
|
||||||
|
* Install via [ComfyUI-Manager](https://github.com/ltdrdata/ComfyUI-Manager).
|
||||||
|
|
||||||
|
### **Manual**
|
||||||
|
* Navigate to `ComfyUI/custom_nodes` in your terminal (cmd).
|
||||||
|
* Clone the repository under the `custom_nodes` directory using the following command:
|
||||||
|
```
|
||||||
|
git clone https://github.com/ltdrdata/ComfyUI-Impact-Pack comfyui-impact-pack
|
||||||
|
cd comfyui-impact-pack
|
||||||
|
```
|
||||||
|
* Install dependencies in your Python environment.
|
||||||
|
* For Windows Portable, run the following command inside `ComfyUI\custom_nodes\comfyui-impact-pack`:
|
||||||
|
```
|
||||||
|
..\..\..\python_embeded\python.exe -m pip install -r requirements.txt
|
||||||
|
```
|
||||||
|
* If using venv or conda, activate your Python environment first, then run:
|
||||||
|
```
|
||||||
|
pip install -r requirements.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
### Companion Pack
|
||||||
|
* If you need the `Ultralytics Detector Provider` to use various YOLO detection models, you should also install [ComfyUI-Impact-Subpack](https://github.com/ltdrdata/ComfyUI-Impact-Subpack).
|
||||||
|
|
||||||
## Custom Nodes
|
## Custom Nodes
|
||||||
### [Detector nodes](https://github.com/ltdrdata/ComfyUI-extension-tutorials/blob/Main/ComfyUI-Impact-Pack/tutorial/detectors.md)
|
### [Detector nodes](https://github.com/ltdrdata/ComfyUI-extension-tutorials/blob/Main/ComfyUI-Impact-Pack/tutorial/detectors.md)
|
||||||
* `SAMLoader` - Loads the SAM model.
|
* `SAMLoader` - Loads the SAM model.
|
||||||
@@ -107,6 +132,8 @@ NOTE: The UltralyticsDetectorProvider node is not part of the ComfyUI-Impact-Pac
|
|||||||
* `SEGS Filter (label)` - This node filters SEGS based on the label of the detected areas.
|
* `SEGS Filter (label)` - This node filters SEGS based on the label of the detected areas.
|
||||||
* `SEGS Filter (ordered)` - This node sorts SEGS based on size and position and retrieves SEGs within a certain range.
|
* `SEGS Filter (ordered)` - This node sorts SEGS based on size and position and retrieves SEGs 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 Filter (range)` - This node retrieves only SEGs from SEGS that have a size and position within a certain range.
|
||||||
|
* `SEGS Filter (non max suppression)` - This node filters SEGS by removing those with high overlap based on the Intersection over Union (IoU) threshold, keeping only the most confident detections.
|
||||||
|
* `SEGS Filter (intersection)` - This node filters segs1, keeping only the SEGS that do not significantly overlap with any SEGS in segs2, based on the Intersection over Area (IoA) threshold.
|
||||||
* `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.
|
* `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.
|
||||||
@@ -224,7 +251,7 @@ NOTE: The UltralyticsDetectorProvider node is not part of the ComfyUI-Impact-Pac
|
|||||||
|
|
||||||
|
|
||||||
### Impact KSampler
|
### Impact KSampler
|
||||||
* These samplers support basic_pipe and AYS scheduler
|
* These samplers support basic_pipe and AYS/OSS/GITS scheduler
|
||||||
* `KSampler (pipe)` - pipe version of KSampler
|
* `KSampler (pipe)` - pipe version of KSampler
|
||||||
* `KSampler (advanced/pipe)` - pipe version of KSamplerAdvacned
|
* `KSampler (advanced/pipe)` - pipe version of KSamplerAdvacned
|
||||||
* When converting the scheduler widget to input, refer to the `Impact Scheduler Adapter` node to resolve compatibility issues.
|
* When converting the scheduler widget to input, refer to the `Impact Scheduler Adapter` node to resolve compatibility issues.
|
||||||
@@ -241,6 +268,7 @@ NOTE: The UltralyticsDetectorProvider node is not part of the ComfyUI-Impact-Pac
|
|||||||
* `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.
|
* `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.
|
* `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.
|
* `Make List (Any)` - Create a list with arbitrary values.
|
||||||
|
* `Select Nth Item (Any list)` - Selects the Nth item from a list. If the index is out of range, it returns the last item in the list.
|
||||||
|
|
||||||
### 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.
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ modules_path = os.path.join(os.path.dirname(__file__), "modules")
|
|||||||
sys.path.append(modules_path)
|
sys.path.append(modules_path)
|
||||||
|
|
||||||
import impact.config
|
import impact.config
|
||||||
import impact.sample_error_enhancer
|
|
||||||
print(f"### Loading: ComfyUI-Impact-Pack ({impact.config.version})")
|
print(f"### Loading: ComfyUI-Impact-Pack ({impact.config.version})")
|
||||||
|
|
||||||
# Core
|
# Core
|
||||||
@@ -235,6 +234,7 @@ NODE_CLASS_MAPPINGS = {
|
|||||||
"ImpactMakeAnyList": MakeAnyList,
|
"ImpactMakeAnyList": MakeAnyList,
|
||||||
"ImpactMakeMaskList": MakeMaskList,
|
"ImpactMakeMaskList": MakeMaskList,
|
||||||
"ImpactMakeMaskBatch": MakeMaskBatch,
|
"ImpactMakeMaskBatch": MakeMaskBatch,
|
||||||
|
"ImpactSelectNthItemOfAnyList": NthItemOfAnyList,
|
||||||
|
|
||||||
"RegionalSampler": RegionalSampler,
|
"RegionalSampler": RegionalSampler,
|
||||||
"RegionalSamplerAdvanced": RegionalSamplerAdvanced,
|
"RegionalSamplerAdvanced": RegionalSamplerAdvanced,
|
||||||
@@ -248,6 +248,8 @@ NODE_CLASS_MAPPINGS = {
|
|||||||
"ImpactSEGSLabelFilter": SEGSLabelFilter,
|
"ImpactSEGSLabelFilter": SEGSLabelFilter,
|
||||||
"ImpactSEGSRangeFilter": SEGSRangeFilter,
|
"ImpactSEGSRangeFilter": SEGSRangeFilter,
|
||||||
"ImpactSEGSOrderedFilter": SEGSOrderedFilter,
|
"ImpactSEGSOrderedFilter": SEGSOrderedFilter,
|
||||||
|
"ImpactSEGSIntersectionFilter": SEGSIntersectionFilter,
|
||||||
|
"ImpactSEGSNMSFilter": SEGSNMSFilter,
|
||||||
|
|
||||||
"ImpactCompare": ImpactCompare,
|
"ImpactCompare": ImpactCompare,
|
||||||
"ImpactConditionalBranch": ImpactConditionalBranch,
|
"ImpactConditionalBranch": ImpactConditionalBranch,
|
||||||
@@ -363,6 +365,8 @@ NODE_DISPLAY_NAME_MAPPINGS = {
|
|||||||
"ImpactSEGSLabelFilter": "SEGS Filter (label)",
|
"ImpactSEGSLabelFilter": "SEGS Filter (label)",
|
||||||
"ImpactSEGSRangeFilter": "SEGS Filter (range)",
|
"ImpactSEGSRangeFilter": "SEGS Filter (range)",
|
||||||
"ImpactSEGSOrderedFilter": "SEGS Filter (ordered)",
|
"ImpactSEGSOrderedFilter": "SEGS Filter (ordered)",
|
||||||
|
"ImpactSEGSIntersectionFilter": "SEGS Filter (intersection)",
|
||||||
|
"ImpactSEGSNMSFilter": "SEGS Filter (non max suppression)",
|
||||||
"ImpactSEGSConcat": "SEGS Concat",
|
"ImpactSEGSConcat": "SEGS Concat",
|
||||||
"ImpactSEGSToMaskList": "SEGS to Mask List",
|
"ImpactSEGSToMaskList": "SEGS to Mask List",
|
||||||
"ImpactSEGSToMaskBatch": "SEGS to Mask Batch",
|
"ImpactSEGSToMaskBatch": "SEGS to Mask Batch",
|
||||||
@@ -404,6 +408,7 @@ NODE_DISPLAY_NAME_MAPPINGS = {
|
|||||||
"ImpactMakeMaskList": "Make Mask List",
|
"ImpactMakeMaskList": "Make Mask List",
|
||||||
"ImpactMakeMaskBatch": "Make Mask Batch",
|
"ImpactMakeMaskBatch": "Make Mask Batch",
|
||||||
"ImpactMakeAnyList": "Make List (Any)",
|
"ImpactMakeAnyList": "Make List (Any)",
|
||||||
|
"ImpactSelectNthItemOfAnyList": "Select Nth Item (Any list)",
|
||||||
|
|
||||||
"ImpactStringSelector": "String Selector",
|
"ImpactStringSelector": "String Selector",
|
||||||
"StringListToString": "String List to String",
|
"StringListToString": "String List to String",
|
||||||
|
|||||||
|
After Width: | Height: | Size: 63 KiB |
|
After Width: | Height: | Size: 112 KiB |
@@ -0,0 +1,596 @@
|
|||||||
|
{
|
||||||
|
"last_node_id": 5,
|
||||||
|
"last_link_id": 5,
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"type": "LoadImage",
|
||||||
|
"pos": [
|
||||||
|
30,
|
||||||
|
210
|
||||||
|
],
|
||||||
|
"size": [
|
||||||
|
390,
|
||||||
|
320
|
||||||
|
],
|
||||||
|
"flags": {},
|
||||||
|
"order": 0,
|
||||||
|
"mode": 0,
|
||||||
|
"inputs": [],
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "IMAGE",
|
||||||
|
"type": "IMAGE",
|
||||||
|
"shape": 3,
|
||||||
|
"links": [
|
||||||
|
1
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "MASK",
|
||||||
|
"type": "MASK",
|
||||||
|
"shape": 3,
|
||||||
|
"links": [
|
||||||
|
2
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"Node name for S&R": "LoadImage"
|
||||||
|
},
|
||||||
|
"widgets_values": [
|
||||||
|
"clipspace/clipspace-mask-609196.2000000011.png [input]",
|
||||||
|
"image"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 5,
|
||||||
|
"type": "PreviewImage",
|
||||||
|
"pos": [
|
||||||
|
1230,
|
||||||
|
210
|
||||||
|
],
|
||||||
|
"size": [
|
||||||
|
210,
|
||||||
|
246
|
||||||
|
],
|
||||||
|
"flags": {},
|
||||||
|
"order": 3,
|
||||||
|
"mode": 0,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "images",
|
||||||
|
"type": "IMAGE",
|
||||||
|
"link": 5
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputs": [],
|
||||||
|
"properties": {
|
||||||
|
"Node name for S&R": "PreviewImage"
|
||||||
|
},
|
||||||
|
"widgets_values": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3,
|
||||||
|
"type": "workflow>Impact::MAKE_BASIC_PIPE",
|
||||||
|
"pos": [
|
||||||
|
20,
|
||||||
|
620
|
||||||
|
],
|
||||||
|
"size": [
|
||||||
|
400,
|
||||||
|
200
|
||||||
|
],
|
||||||
|
"flags": {},
|
||||||
|
"order": 1,
|
||||||
|
"mode": 0,
|
||||||
|
"inputs": [],
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "basic_pipe",
|
||||||
|
"type": "BASIC_PIPE",
|
||||||
|
"shape": 3,
|
||||||
|
"links": [
|
||||||
|
3
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"Node name for S&R": "workflow/Impact::MAKE_BASIC_PIPE"
|
||||||
|
},
|
||||||
|
"widgets_values": [
|
||||||
|
"SD1.5/realcartoon3d_v13.safetensors",
|
||||||
|
"(best quality:1.4), fox girl",
|
||||||
|
"(worst quality:1.4), nsfw"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"type": "MaskDetailerPipe",
|
||||||
|
"pos": [
|
||||||
|
530,
|
||||||
|
210
|
||||||
|
],
|
||||||
|
"size": [
|
||||||
|
569.4000244140625,
|
||||||
|
850
|
||||||
|
],
|
||||||
|
"flags": {},
|
||||||
|
"order": 2,
|
||||||
|
"mode": 0,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "image",
|
||||||
|
"type": "IMAGE",
|
||||||
|
"link": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "mask",
|
||||||
|
"type": "MASK",
|
||||||
|
"link": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "basic_pipe",
|
||||||
|
"type": "BASIC_PIPE",
|
||||||
|
"link": 3,
|
||||||
|
"slot_index": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "refiner_basic_pipe_opt",
|
||||||
|
"type": "BASIC_PIPE",
|
||||||
|
"shape": 7,
|
||||||
|
"link": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "detailer_hook",
|
||||||
|
"type": "DETAILER_HOOK",
|
||||||
|
"shape": 7,
|
||||||
|
"link": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "scheduler_func_opt",
|
||||||
|
"type": "SCHEDULER_FUNC",
|
||||||
|
"shape": 7,
|
||||||
|
"link": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "image",
|
||||||
|
"type": "IMAGE",
|
||||||
|
"shape": 3,
|
||||||
|
"links": [
|
||||||
|
5
|
||||||
|
],
|
||||||
|
"slot_index": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "cropped_refined",
|
||||||
|
"type": "IMAGE",
|
||||||
|
"shape": 6,
|
||||||
|
"links": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "cropped_enhanced_alpha",
|
||||||
|
"type": "IMAGE",
|
||||||
|
"shape": 6,
|
||||||
|
"links": [
|
||||||
|
4
|
||||||
|
],
|
||||||
|
"slot_index": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "basic_pipe",
|
||||||
|
"type": "BASIC_PIPE",
|
||||||
|
"shape": 3,
|
||||||
|
"links": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "refiner_basic_pipe_opt",
|
||||||
|
"type": "BASIC_PIPE",
|
||||||
|
"shape": 3,
|
||||||
|
"links": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"Node name for S&R": "MaskDetailerPipe"
|
||||||
|
},
|
||||||
|
"widgets_values": [
|
||||||
|
512,
|
||||||
|
true,
|
||||||
|
1024,
|
||||||
|
true,
|
||||||
|
1003,
|
||||||
|
"fixed",
|
||||||
|
20,
|
||||||
|
8,
|
||||||
|
"euler",
|
||||||
|
"normal",
|
||||||
|
0.75,
|
||||||
|
5,
|
||||||
|
3,
|
||||||
|
10,
|
||||||
|
0.2,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
false,
|
||||||
|
20,
|
||||||
|
false,
|
||||||
|
false
|
||||||
|
],
|
||||||
|
"color": "#322",
|
||||||
|
"bgcolor": "#533"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 4,
|
||||||
|
"type": "PreviewImage",
|
||||||
|
"pos": [
|
||||||
|
1230,
|
||||||
|
560
|
||||||
|
],
|
||||||
|
"size": [
|
||||||
|
210,
|
||||||
|
246
|
||||||
|
],
|
||||||
|
"flags": {},
|
||||||
|
"order": 4,
|
||||||
|
"mode": 0,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "images",
|
||||||
|
"type": "IMAGE",
|
||||||
|
"link": 4
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputs": [],
|
||||||
|
"properties": {
|
||||||
|
"Node name for S&R": "PreviewImage"
|
||||||
|
},
|
||||||
|
"widgets_values": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"links": [
|
||||||
|
[
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
2,
|
||||||
|
0,
|
||||||
|
"IMAGE"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
2,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
1,
|
||||||
|
"MASK"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
3,
|
||||||
|
3,
|
||||||
|
0,
|
||||||
|
2,
|
||||||
|
2,
|
||||||
|
"BASIC_PIPE"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
4,
|
||||||
|
2,
|
||||||
|
2,
|
||||||
|
4,
|
||||||
|
0,
|
||||||
|
"IMAGE"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
5,
|
||||||
|
2,
|
||||||
|
0,
|
||||||
|
5,
|
||||||
|
0,
|
||||||
|
"IMAGE"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"groups": [],
|
||||||
|
"config": {},
|
||||||
|
"extra": {
|
||||||
|
"ds": {
|
||||||
|
"scale": 1,
|
||||||
|
"offset": [
|
||||||
|
80,
|
||||||
|
-110
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"groupNodes": {
|
||||||
|
"Impact::MAKE_BASIC_PIPE": {
|
||||||
|
"author": "Dr.Lt.Data",
|
||||||
|
"category": "",
|
||||||
|
"config": {
|
||||||
|
"1": {
|
||||||
|
"input": {
|
||||||
|
"text": {
|
||||||
|
"name": "Positive prompt"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"2": {
|
||||||
|
"input": {
|
||||||
|
"text": {
|
||||||
|
"name": "Negative prompt"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"datetime": 1708272471445,
|
||||||
|
"external": [],
|
||||||
|
"links": [
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
"CLIP"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
"CLIP"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
3,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
"MODEL"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
3,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
"CLIP"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
2,
|
||||||
|
3,
|
||||||
|
2,
|
||||||
|
1,
|
||||||
|
"VAE"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
3,
|
||||||
|
3,
|
||||||
|
3,
|
||||||
|
"CONDITIONING"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
2,
|
||||||
|
0,
|
||||||
|
3,
|
||||||
|
4,
|
||||||
|
4,
|
||||||
|
"CONDITIONING"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"flags": {},
|
||||||
|
"index": 0,
|
||||||
|
"mode": 0,
|
||||||
|
"order": 0,
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"links": [],
|
||||||
|
"name": "MODEL",
|
||||||
|
"shape": 3,
|
||||||
|
"slot_index": 0,
|
||||||
|
"type": "MODEL",
|
||||||
|
"localized_name": "MODEL"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"links": [],
|
||||||
|
"name": "CLIP",
|
||||||
|
"shape": 3,
|
||||||
|
"slot_index": 1,
|
||||||
|
"type": "CLIP",
|
||||||
|
"localized_name": "CLIP"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"links": [],
|
||||||
|
"name": "VAE",
|
||||||
|
"shape": 3,
|
||||||
|
"slot_index": 2,
|
||||||
|
"type": "VAE",
|
||||||
|
"localized_name": "VAE"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"pos": [
|
||||||
|
550,
|
||||||
|
360
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"Node name for S&R": "CheckpointLoaderSimple"
|
||||||
|
},
|
||||||
|
"size": {
|
||||||
|
"0": 315,
|
||||||
|
"1": 98
|
||||||
|
},
|
||||||
|
"type": "CheckpointLoaderSimple",
|
||||||
|
"widgets_values": [
|
||||||
|
"SDXL/sd_xl_base_1.0_0.9vae.safetensors"
|
||||||
|
],
|
||||||
|
"inputs": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"flags": {},
|
||||||
|
"index": 1,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"link": null,
|
||||||
|
"name": "clip",
|
||||||
|
"type": "CLIP",
|
||||||
|
"localized_name": "clip"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"mode": 0,
|
||||||
|
"order": 1,
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"links": [],
|
||||||
|
"name": "CONDITIONING",
|
||||||
|
"shape": 3,
|
||||||
|
"slot_index": 0,
|
||||||
|
"type": "CONDITIONING",
|
||||||
|
"localized_name": "CONDITIONING"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"pos": [
|
||||||
|
940,
|
||||||
|
480
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"Node name for S&R": "CLIPTextEncode"
|
||||||
|
},
|
||||||
|
"size": {
|
||||||
|
"0": 263,
|
||||||
|
"1": 99
|
||||||
|
},
|
||||||
|
"title": "Positive",
|
||||||
|
"type": "CLIPTextEncode",
|
||||||
|
"widgets_values": [
|
||||||
|
""
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"flags": {},
|
||||||
|
"index": 2,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"link": null,
|
||||||
|
"name": "clip",
|
||||||
|
"type": "CLIP",
|
||||||
|
"localized_name": "clip"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"mode": 0,
|
||||||
|
"order": 2,
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"links": [],
|
||||||
|
"name": "CONDITIONING",
|
||||||
|
"shape": 3,
|
||||||
|
"slot_index": 0,
|
||||||
|
"type": "CONDITIONING",
|
||||||
|
"localized_name": "CONDITIONING"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"pos": [
|
||||||
|
940,
|
||||||
|
640
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"Node name for S&R": "CLIPTextEncode"
|
||||||
|
},
|
||||||
|
"size": {
|
||||||
|
"0": 263,
|
||||||
|
"1": 99
|
||||||
|
},
|
||||||
|
"title": "Negative",
|
||||||
|
"type": "CLIPTextEncode",
|
||||||
|
"widgets_values": [
|
||||||
|
""
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"flags": {},
|
||||||
|
"index": 3,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"link": null,
|
||||||
|
"name": "model",
|
||||||
|
"type": "MODEL",
|
||||||
|
"localized_name": "model"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"link": null,
|
||||||
|
"name": "clip",
|
||||||
|
"type": "CLIP",
|
||||||
|
"localized_name": "clip"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"link": null,
|
||||||
|
"name": "vae",
|
||||||
|
"type": "VAE",
|
||||||
|
"localized_name": "vae"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"link": null,
|
||||||
|
"name": "positive",
|
||||||
|
"type": "CONDITIONING",
|
||||||
|
"localized_name": "positive"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"link": null,
|
||||||
|
"name": "negative",
|
||||||
|
"type": "CONDITIONING",
|
||||||
|
"localized_name": "negative"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"mode": 0,
|
||||||
|
"order": 3,
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"links": null,
|
||||||
|
"name": "basic_pipe",
|
||||||
|
"shape": 3,
|
||||||
|
"slot_index": 0,
|
||||||
|
"type": "BASIC_PIPE",
|
||||||
|
"localized_name": "basic_pipe"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"pos": [
|
||||||
|
1320,
|
||||||
|
360
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"Node name for S&R": "ToBasicPipe"
|
||||||
|
},
|
||||||
|
"size": {
|
||||||
|
"0": 241.79998779296875,
|
||||||
|
"1": 106
|
||||||
|
},
|
||||||
|
"type": "ToBasicPipe"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"packname": "Impact",
|
||||||
|
"version": "1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"controller_panel": {
|
||||||
|
"controllers": {},
|
||||||
|
"hidden": true,
|
||||||
|
"highlight": true,
|
||||||
|
"version": 2,
|
||||||
|
"default_order": []
|
||||||
|
},
|
||||||
|
"node_versions": {
|
||||||
|
"comfy-core": "0.3.14",
|
||||||
|
"comfyui-impact-pack": "1ae7cae2df8cca06027edfa3a24512671239d6c4"
|
||||||
|
},
|
||||||
|
"ue_links": [],
|
||||||
|
"VHS_latentpreview": false,
|
||||||
|
"VHS_latentpreviewrate": 0,
|
||||||
|
"VHS_MetadataImage": true,
|
||||||
|
"VHS_KeepIntermediate": true
|
||||||
|
},
|
||||||
|
"version": 0.4
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 42 KiB |
|
After Width: | Height: | Size: 106 KiB |
|
After Width: | Height: | Size: 67 KiB |
|
After Width: | Height: | Size: 128 KiB |
|
After Width: | Height: | Size: 526 KiB |
@@ -3,6 +3,48 @@ import { app } from "../../scripts/app.js";
|
|||||||
|
|
||||||
let original_show = app.ui.dialog.show;
|
let original_show = app.ui.dialog.show;
|
||||||
|
|
||||||
|
export function customAlert(message) {
|
||||||
|
try {
|
||||||
|
app.extensionManager.toast.addAlert(message);
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
alert(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isBeforeFrontendVersion(compareVersion) {
|
||||||
|
try {
|
||||||
|
const frontendVersion = window['__COMFYUI_FRONTEND_VERSION__'];
|
||||||
|
if (typeof frontendVersion !== 'string') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseVersion(versionString) {
|
||||||
|
const parts = versionString.split('.').map(Number);
|
||||||
|
return parts.length === 3 && parts.every(part => !isNaN(part)) ? parts : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentVersion = parseVersion(frontendVersion);
|
||||||
|
const comparisonVersion = parseVersion(compareVersion);
|
||||||
|
|
||||||
|
if (!currentVersion || !comparisonVersion) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < 3; i++) {
|
||||||
|
if (currentVersion[i] > comparisonVersion[i]) {
|
||||||
|
return false;
|
||||||
|
} else if (currentVersion[i] < comparisonVersion[i]) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
} catch {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function dialog_show_wrapper(html) {
|
function dialog_show_wrapper(html) {
|
||||||
if (typeof html === "string") {
|
if (typeof html === "string") {
|
||||||
if(html.includes("IMPACT-PACK-SIGNAL: STOP CONTROL BRIDGE")) {
|
if(html.includes("IMPACT-PACK-SIGNAL: STOP CONTROL BRIDGE")) {
|
||||||
|
|||||||
@@ -1,6 +1,13 @@
|
|||||||
import { ComfyApp, app } from "../../scripts/app.js";
|
import { ComfyApp, app } from "../../scripts/app.js";
|
||||||
import { ComfyDialog, $el } from "../../scripts/ui.js";
|
import { ComfyDialog, $el } from "../../scripts/ui.js";
|
||||||
import { api } from "../../scripts/api.js";
|
import { api } from "../../scripts/api.js";
|
||||||
|
import { customAlert, isBeforeFrontendVersion } from "./common.js";
|
||||||
|
|
||||||
|
const is_legacy_front = () => isBeforeFrontendVersion('1.16.9');
|
||||||
|
|
||||||
|
if(is_legacy_front()) {
|
||||||
|
customAlert("An outdated version(<1.16.9) of the `comfyui-frontend-package` is installed. It is not compatible with the current version of the Impact Pack.");
|
||||||
|
}
|
||||||
|
|
||||||
let wildcards_list = [];
|
let wildcards_list = [];
|
||||||
async function load_wildcards() {
|
async function load_wildcards() {
|
||||||
@@ -223,29 +230,29 @@ api.addEventListener("executed", progressExecuteHandler);
|
|||||||
app.registerExtension({
|
app.registerExtension({
|
||||||
name: "Comfy.Impack",
|
name: "Comfy.Impack",
|
||||||
|
|
||||||
commands: [
|
commands: [
|
||||||
{
|
{
|
||||||
id: 'refresh-impact-wildcard',
|
id: 'refresh-impact-wildcard',
|
||||||
label: 'Impact: Refresh Wildcard',
|
label: 'Impact: Refresh Wildcard',
|
||||||
function: async () => {
|
function: async () => {
|
||||||
await api.fetchApi('/impact/wildcards/refresh');
|
await api.fetchApi('/impact/wildcards/refresh');
|
||||||
await load_wildcards();
|
await load_wildcards();
|
||||||
app.extensionManager.toast.add({
|
app.extensionManager.toast.add({
|
||||||
severity: 'info',
|
severity: 'info',
|
||||||
summary: 'Refreshed!',
|
summary: 'Refreshed!',
|
||||||
detail: 'Impact Wildcard List is refreshed!!',
|
detail: 'Impact Wildcard List is refreshed!!',
|
||||||
life: 3000
|
life: 3000
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
menuCommands: [
|
menuCommands: [
|
||||||
{
|
{
|
||||||
path: ['Edit'],
|
path: ['Edit'],
|
||||||
commands: ['refresh-impact-wildcard']
|
commands: ['refresh-impact-wildcard']
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
loadedGraphNode(node, app) {
|
loadedGraphNode(node, app) {
|
||||||
if (node.comfyClass == "MaskPainter") {
|
if (node.comfyClass == "MaskPainter") {
|
||||||
@@ -324,6 +331,32 @@ app.registerExtension({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(nodeData.name == "ImpactSelectNthItemOfAnyList") {
|
||||||
|
const onConnectionsChange = nodeType.prototype.onConnectionsChange;
|
||||||
|
nodeType.prototype.onConnectionsChange = function (type, index, connected, link_info) {
|
||||||
|
if(!link_info || this.inputs[0].type != '*')
|
||||||
|
return;
|
||||||
|
|
||||||
|
if(index >= 2)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// assign type
|
||||||
|
let slot_type = '*';
|
||||||
|
|
||||||
|
if(type == 2) {
|
||||||
|
slot_type = link_info.type;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const node = app.graph.getNodeById(link_info.origin_id);
|
||||||
|
slot_type = node.outputs[link_info.origin_slot].type;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.inputs[0].type = slot_type;
|
||||||
|
this.outputs[0].type = slot_type;
|
||||||
|
this.outputs[0].label = slot_type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(nodeData.name === 'ImpactInversedSwitch') {
|
if(nodeData.name === 'ImpactInversedSwitch') {
|
||||||
nodeData.output = ['*'];
|
nodeData.output = ['*'];
|
||||||
nodeData.output_is_list = [false];
|
nodeData.output_is_list = [false];
|
||||||
@@ -403,6 +436,9 @@ app.registerExtension({
|
|||||||
let slot_i = 1;
|
let slot_i = 1;
|
||||||
for (let i = 0; i < this.outputs.length; i++) {
|
for (let i = 0; i < this.outputs.length; i++) {
|
||||||
this.outputs[i].name = `output${slot_i}`
|
this.outputs[i].name = `output${slot_i}`
|
||||||
|
if (this.outputs[i].slot_index === undefined) {
|
||||||
|
this.outputs[i].slot_index = i;
|
||||||
|
}
|
||||||
slot_i++;
|
slot_i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -471,6 +507,13 @@ app.registerExtension({
|
|||||||
|
|
||||||
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) {
|
||||||
|
const stackTrace = new Error().stack;
|
||||||
|
if(stackTrace.includes('LGraph.configure')) {
|
||||||
|
this.widgets[0].options.max = this.inputs.length-3;
|
||||||
|
this.widgets[0].value = Math.min(this.widgets[0].value, this.widgets[0].options.max);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if(!link_info)
|
if(!link_info)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -513,7 +556,7 @@ 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) {
|
if(link_info.target_slot == 0 && this.inputs.length > 3) { // NOTE: widgets are regarded as input since new front
|
||||||
origin_type = this.inputs[1].type;
|
origin_type = this.inputs[1].type;
|
||||||
node.connect(link_info.origin_slot, node.id, 'input1');
|
node.connect(link_info.origin_slot, node.id, 'input1');
|
||||||
}
|
}
|
||||||
@@ -536,15 +579,8 @@ app.registerExtension({
|
|||||||
}
|
}
|
||||||
|
|
||||||
let select_slot = this.inputs.find(x => x.name == "select");
|
let select_slot = this.inputs.find(x => x.name == "select");
|
||||||
let mode_slot = this.inputs.find(x => x.name == "sel_mode");
|
|
||||||
|
|
||||||
let converted_count = 0;
|
|
||||||
converted_count += select_slot?1:0;
|
|
||||||
converted_count += mode_slot?1:0;
|
|
||||||
|
|
||||||
if (!connected && (this.inputs.length > 1+converted_count)) {
|
|
||||||
const stackTrace = new Error().stack;
|
|
||||||
|
|
||||||
|
if (!connected && (this.inputs.length > 3)) {
|
||||||
if(
|
if(
|
||||||
!stackTrace.includes('LGraphNode.prototype.connect') && // for touch device
|
!stackTrace.includes('LGraphNode.prototype.connect') && // for touch device
|
||||||
!stackTrace.includes('LGraphNode.connect') && // for mouse device
|
!stackTrace.includes('LGraphNode.connect') && // for mouse device
|
||||||
@@ -554,6 +590,7 @@ app.registerExtension({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
let slot_i = 1;
|
let slot_i = 1;
|
||||||
for (let i = 0; i < this.inputs.length; i++) {
|
for (let i = 0; i < this.inputs.length; i++) {
|
||||||
let input_i = this.inputs[i];
|
let input_i = this.inputs[i];
|
||||||
@@ -563,19 +600,12 @@ app.registerExtension({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let last_slot = this.inputs[this.inputs.length - 1];
|
if(connected) {
|
||||||
if (
|
this.addInput(`${input_name}${slot_i}`, this.outputs[0].type);
|
||||||
(last_slot.name == 'select' && last_slot.name != 'sel_mode' && this.inputs[this.inputs.length - 2].link != undefined)
|
|
||||||
|| (last_slot.name != 'select' && last_slot.name != 'sel_mode' && last_slot.link != undefined)) {
|
|
||||||
this.addInput(`${input_name}${slot_i}`, this.outputs[0].type);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(this.widgets?.length) {
|
this.widgets[0].options.max = this.inputs.length-3;
|
||||||
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)
|
|
||||||
this.widgets[0].value = 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -617,12 +647,14 @@ app.registerExtension({
|
|||||||
|
|
||||||
if(node.comfyClass == "ImpactSEGSLabelFilter" || node.comfyClass == "SEGSLabelFilterDetailerHookProvider") {
|
if(node.comfyClass == "ImpactSEGSLabelFilter" || node.comfyClass == "SEGSLabelFilterDetailerHookProvider") {
|
||||||
node.widgets[0].callback = (value, canvas, node, pos, e) => {
|
node.widgets[0].callback = (value, canvas, node, pos, e) => {
|
||||||
if(node.widgets[1].value.trim() != "" && !node.widgets[1].value.trim().endsWith(","))
|
if(node) {
|
||||||
node.widgets[1].value += ", "
|
if(node.widgets[1].value.trim() != "" && !node.widgets[1].value.trim().endsWith(","))
|
||||||
|
node.widgets[1].value += ", "
|
||||||
|
|
||||||
node.widgets[1].value += value;
|
node.widgets[1].value += value;
|
||||||
if(node.widgets_values)
|
if(node.widgets_values)
|
||||||
node.widgets_values[1] = node.widgets[1].value;
|
node.widgets_values[1] = node.widgets[1].value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Object.defineProperty(node.widgets[0], "value", {
|
Object.defineProperty(node.widgets[0], "value", {
|
||||||
@@ -696,18 +728,20 @@ app.registerExtension({
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
node.widgets[combo_id+1].callback = (value, canvas, node, pos, e) => {
|
node.widgets[combo_id+1].callback = (value, canvas, node, pos, e) => {
|
||||||
if(node.widgets[tbox_id].value != '')
|
if(node) {
|
||||||
node.widgets[tbox_id].value += ', '
|
if(node.widgets[tbox_id].value != '')
|
||||||
|
node.widgets[tbox_id].value += ', '
|
||||||
|
|
||||||
node.widgets[tbox_id].value += node._wildcard_value;
|
node.widgets[tbox_id].value += node._wildcard_value;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Object.defineProperty(node.widgets[combo_id+1], "value", {
|
Object.defineProperty(node.widgets[combo_id+1], "value", {
|
||||||
set: (value) => {
|
set: (value) => {
|
||||||
if (value !== "Select the Wildcard to add to the text")
|
if (value !== "Select the Wildcard to add to the text")
|
||||||
node._wildcard_value = value;
|
node._wildcard_value = value;
|
||||||
},
|
},
|
||||||
get: () => { return "Select the Wildcard to add to the text"; }
|
get: () => { return "Select the Wildcard to add to the text"; }
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -720,14 +754,16 @@ app.registerExtension({
|
|||||||
|
|
||||||
if(has_lora) {
|
if(has_lora) {
|
||||||
node.widgets[combo_id].callback = (value, canvas, node, pos, e) => {
|
node.widgets[combo_id].callback = (value, canvas, node, pos, e) => {
|
||||||
let lora_name = node._value;
|
if(node) {
|
||||||
if(lora_name.endsWith('.safetensors')) {
|
let lora_name = node._value;
|
||||||
lora_name = lora_name.slice(0, -12);
|
if(lora_name.endsWith('.safetensors')) {
|
||||||
}
|
lora_name = lora_name.slice(0, -12);
|
||||||
|
}
|
||||||
|
|
||||||
node.widgets[tbox_id].value += `<lora:${lora_name}>`;
|
node.widgets[tbox_id].value += `<lora:${lora_name}>`;
|
||||||
if(node.widgets_values) {
|
if(node.widgets_values) {
|
||||||
node.widgets_values[tbox_id] = node.widgets[tbox_id].value;
|
node.widgets_values[tbox_id] = node.widgets[tbox_id].value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import configparser
|
import configparser
|
||||||
import os
|
import os
|
||||||
|
|
||||||
version_code = [8, 8]
|
version_code = [8, 13]
|
||||||
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 = 24
|
dependency_version = 24
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ preview_bridge_last_mask_cache = {}
|
|||||||
|
|
||||||
current_prompt = None
|
current_prompt = None
|
||||||
|
|
||||||
SCHEDULERS = comfy.samplers.KSampler.SCHEDULERS + ['AYS SDXL', 'AYS SD1', 'AYS SVD', 'GITS[coeff=1.2]', 'LTXV[default]']
|
SCHEDULERS = comfy.samplers.KSampler.SCHEDULERS + ['AYS SDXL', 'AYS SD1', 'AYS SVD', 'GITS[coeff=1.2]', 'LTXV[default]', 'OSS FLUX', 'OSS Wan']
|
||||||
|
|
||||||
|
|
||||||
def is_execution_model_version_supported():
|
def is_execution_model_version_supported():
|
||||||
|
|||||||
@@ -96,9 +96,13 @@ class SAMLoader:
|
|||||||
@classmethod
|
@classmethod
|
||||||
def INPUT_TYPES(cls):
|
def INPUT_TYPES(cls):
|
||||||
models = [x for x in folder_paths.get_filename_list("sams") if 'hq' not in x]
|
models = [x for x in folder_paths.get_filename_list("sams") if 'hq' not in x]
|
||||||
|
|
||||||
|
if 'ESAM_ModelLoader_Zho' in nodes.NODE_CLASS_MAPPINGS:
|
||||||
|
models.append('ESAM')
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"required": {
|
"required": {
|
||||||
"model_name": (models + ['ESAM'], {"tooltip": "The detection accuracy varies depending on the SAM model. ESAM can only be used if ComfyUI-YoloWorld-EfficientSAM is installed."}),
|
"model_name": (models, {"tooltip": "The detection accuracy varies depending on the SAM model. ESAM can only be used if ComfyUI-YoloWorld-EfficientSAM is installed."}),
|
||||||
"device_mode": (["AUTO", "Prefer GPU", "CPU"], {"tooltip": "AUTO: Only applicable when a GPU is available. It temporarily loads the SAM_MODEL into VRAM only when the detection function is used.\n"
|
"device_mode": (["AUTO", "Prefer GPU", "CPU"], {"tooltip": "AUTO: Only applicable when a GPU is available. It temporarily loads the SAM_MODEL into VRAM only when the detection function is used.\n"
|
||||||
"Prefer GPU: Tries to keep the SAM_MODEL on the GPU whenever possible. This can be used when there is sufficient VRAM available.\n"
|
"Prefer GPU: Tries to keep the SAM_MODEL on the GPU whenever possible. This can be used when there is sufficient VRAM available.\n"
|
||||||
"CPU: Always loads only on the CPU."}),
|
"CPU: Always loads only on the CPU."}),
|
||||||
@@ -2431,7 +2435,7 @@ class ImpactSchedulerAdapter:
|
|||||||
def INPUT_TYPES(s):
|
def INPUT_TYPES(s):
|
||||||
return {"required": {
|
return {"required": {
|
||||||
"scheduler": (comfy.samplers.KSampler.SCHEDULERS, {"defaultInput": True, }),
|
"scheduler": (comfy.samplers.KSampler.SCHEDULERS, {"defaultInput": True, }),
|
||||||
"extra_scheduler": (['None', 'AYS SDXL', 'AYS SD1', 'AYS SVD', 'GITS[coeff=1.2]', 'LTXV[default]'],),
|
"extra_scheduler": (['None', 'AYS SDXL', 'AYS SD1', 'AYS SVD', 'GITS[coeff=1.2]', 'LTXV[default]', 'OSS FLUX', 'OSS Wan'],),
|
||||||
}}
|
}}
|
||||||
|
|
||||||
CATEGORY = "ImpactPack/Util"
|
CATEGORY = "ImpactPack/Util"
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ def calculate_sigmas(model, sampler, scheduler, steps):
|
|||||||
sigmas = nodes.NODE_CLASS_MAPPINGS['GITSScheduler']().get_sigmas(float(scheduler[11:-1]), steps, denoise=1.0)[0]
|
sigmas = nodes.NODE_CLASS_MAPPINGS['GITSScheduler']().get_sigmas(float(scheduler[11:-1]), steps, denoise=1.0)[0]
|
||||||
elif scheduler == 'LTXV[default]':
|
elif scheduler == 'LTXV[default]':
|
||||||
sigmas = nodes.NODE_CLASS_MAPPINGS['LTXVScheduler']().get_sigmas(20, 2.05, 0.95, True, 0.1)[0]
|
sigmas = nodes.NODE_CLASS_MAPPINGS['LTXVScheduler']().get_sigmas(20, 2.05, 0.95, True, 0.1)[0]
|
||||||
|
elif scheduler.startswith('OSS'):
|
||||||
|
sigmas = nodes.NODE_CLASS_MAPPINGS['OptimalStepsScheduler']().get_sigmas(scheduler[4:], steps, denoise=1.0)[0]
|
||||||
else:
|
else:
|
||||||
sigmas = samplers.calculate_sigmas(model.get_model_object("model_sampling"), scheduler, steps)
|
sigmas = samplers.calculate_sigmas(model.get_model_object("model_sampling"), scheduler, steps)
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ from impact.utils import any_typ
|
|||||||
import impact.core as core
|
import impact.core as core
|
||||||
import re
|
import re
|
||||||
import nodes
|
import nodes
|
||||||
import traceback
|
|
||||||
|
|
||||||
class ImpactCompare:
|
class ImpactCompare:
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -574,27 +574,6 @@ class ImpactSleep:
|
|||||||
return (signal,)
|
return (signal,)
|
||||||
|
|
||||||
|
|
||||||
error_skip_flag = False
|
|
||||||
try:
|
|
||||||
import cm_global
|
|
||||||
def filter_message(str):
|
|
||||||
global error_skip_flag
|
|
||||||
|
|
||||||
if "IMPACT-PACK-SIGNAL: STOP CONTROL BRIDGE" in str:
|
|
||||||
return True
|
|
||||||
elif error_skip_flag and "ERROR:root:!!! Exception during processing !!!\n" == str:
|
|
||||||
error_skip_flag = False
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
return False
|
|
||||||
|
|
||||||
cm_global.try_call(api='cm.register_message_collapse', f=filter_message)
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"[WARN] ComfyUI-Impact-Pack: `ComfyUI` or `ComfyUI-Manager` is an outdated version.")
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def workflow_to_map(workflow):
|
def workflow_to_map(workflow):
|
||||||
nodes = {}
|
nodes = {}
|
||||||
links = {}
|
links = {}
|
||||||
@@ -701,6 +680,9 @@ class ImpactControlBridge:
|
|||||||
return (value, )
|
return (value, )
|
||||||
else:
|
else:
|
||||||
return (ExecutionBlocker(None), )
|
return (ExecutionBlocker(None), )
|
||||||
|
elif extra_pnginfo is None:
|
||||||
|
logging.warn(f"[Impact Pack] limitation: '{behavior}' behavior cannot be used in API execution.")
|
||||||
|
return (value,)
|
||||||
else:
|
else:
|
||||||
workflow_nodes, links = workflow_to_map(extra_pnginfo['workflow'])
|
workflow_nodes, links = workflow_to_map(extra_pnginfo['workflow'])
|
||||||
|
|
||||||
|
|||||||
@@ -1,25 +0,0 @@
|
|||||||
import comfy.sample
|
|
||||||
import traceback
|
|
||||||
|
|
||||||
original_sample = comfy.sample.sample
|
|
||||||
|
|
||||||
|
|
||||||
def informative_sample(*args, **kwargs):
|
|
||||||
try:
|
|
||||||
return original_sample(*args, **kwargs) # This code helps interpret error messages that occur within exceptions but does not have any impact on other operations.
|
|
||||||
except RuntimeError as e:
|
|
||||||
is_model_mix_issue = False
|
|
||||||
try:
|
|
||||||
if 'mat1 and mat2 shapes cannot be multiplied' in e.args[0]:
|
|
||||||
if 'torch.nn.functional.linear' in traceback.format_exc().strip().split('\n')[-3]:
|
|
||||||
is_model_mix_issue = True
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
if is_model_mix_issue:
|
|
||||||
raise RuntimeError("\n\n#### It seems that models and clips are mixed and interconnected between SDXL Base, SDXL Refiner, SD1.x, and SD2.x. Please verify. ####\n\n")
|
|
||||||
else:
|
|
||||||
raise e
|
|
||||||
|
|
||||||
|
|
||||||
comfy.sample.sample = informative_sample
|
|
||||||
@@ -13,6 +13,7 @@ from . import segs_upscaler
|
|||||||
from comfy.cli_args import args
|
from comfy.cli_args import args
|
||||||
import math
|
import math
|
||||||
|
|
||||||
|
from typing import Callable, Union
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from comfy_extras import nodes_differential_diffusion
|
from comfy_extras import nodes_differential_diffusion
|
||||||
@@ -504,7 +505,7 @@ class SEGSOrderedFilter:
|
|||||||
def INPUT_TYPES(s):
|
def INPUT_TYPES(s):
|
||||||
return {"required": {
|
return {"required": {
|
||||||
"segs": ("SEGS", ),
|
"segs": ("SEGS", ),
|
||||||
"target": (["area(=w*h)", "width", "height", "x1", "y1", "x2", "y2", "confidence"],),
|
"target": (["area(=w*h)", "width", "height", "x1", "y1", "x2", "y2", "confidence", "none"],),
|
||||||
"order": ("BOOLEAN", {"default": True, "label_on": "descending", "label_off": "ascending"}),
|
"order": ("BOOLEAN", {"default": True, "label_on": "descending", "label_off": "ascending"}),
|
||||||
"take_start": ("INT", {"default": 0, "min": 0, "max": sys.maxsize, "step": 1}),
|
"take_start": ("INT", {"default": 0, "min": 0, "max": sys.maxsize, "step": 1}),
|
||||||
"take_count": ("INT", {"default": 1, "min": 0, "max": sys.maxsize, "step": 1}),
|
"take_count": ("INT", {"default": 1, "min": 0, "max": sys.maxsize, "step": 1}),
|
||||||
@@ -517,51 +518,35 @@ class SEGSOrderedFilter:
|
|||||||
|
|
||||||
CATEGORY = "ImpactPack/Util"
|
CATEGORY = "ImpactPack/Util"
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_sort_key_fn(target: str) -> Union[Callable, None]:
|
||||||
|
if target == "none":
|
||||||
|
return None
|
||||||
|
|
||||||
|
def sort_key_fn(seg):
|
||||||
|
x1, y1, x2, y2 = seg.crop_region
|
||||||
|
if target == "confidence": return seg.confidence
|
||||||
|
if target == "area(=w*h)": return (x2 - x1) * (y2 - y1)
|
||||||
|
if target == "width": return x2 - x1
|
||||||
|
if target == "height": return y2 - y1
|
||||||
|
if target == "x1": return x1
|
||||||
|
if target == "y1": return y1
|
||||||
|
if target == "x2": return x2
|
||||||
|
if target == "y2": return y2
|
||||||
|
raise Exception(f"[Impact Pack] SEGSOrderedFilter - Unexpected target '{target}'")
|
||||||
|
|
||||||
|
return sort_key_fn
|
||||||
|
|
||||||
def doit(self, segs, target, order, take_start, take_count):
|
def doit(self, segs, target, order, take_start, take_count):
|
||||||
segs_with_order = []
|
sort_key_fn = SEGSOrderedFilter.get_sort_key_fn(target)
|
||||||
|
|
||||||
for seg in segs[1]:
|
sorted_list = list(segs[1]) # make a shallow copy, so it does not mutate the original list when sort
|
||||||
x1 = seg.crop_region[0]
|
if sort_key_fn is not None:
|
||||||
y1 = seg.crop_region[1]
|
sorted_list.sort(key=sort_key_fn, reverse=order)
|
||||||
x2 = seg.crop_region[2]
|
|
||||||
y2 = seg.crop_region[3]
|
|
||||||
|
|
||||||
if target == "area(=w*h)":
|
take_stop = take_start + take_count
|
||||||
value = (y2 - y1) * (x2 - x1)
|
return (segs[0], sorted_list[take_start:take_stop]), \
|
||||||
elif target == "width":
|
(segs[0], sorted_list[:take_start] + sorted_list[take_stop:]),
|
||||||
value = x2 - x1
|
|
||||||
elif target == "height":
|
|
||||||
value = y2 - y1
|
|
||||||
elif target == "x1":
|
|
||||||
value = x1
|
|
||||||
elif target == "x2":
|
|
||||||
value = x2
|
|
||||||
elif target == "y1":
|
|
||||||
value = y1
|
|
||||||
elif target == "y2":
|
|
||||||
value = y2
|
|
||||||
elif target == "confidence":
|
|
||||||
value = seg.confidence
|
|
||||||
else:
|
|
||||||
raise Exception(f"[Impact Pack] SEGSOrderedFilter - Unexpected target '{target}'")
|
|
||||||
|
|
||||||
segs_with_order.append((value, seg))
|
|
||||||
|
|
||||||
if order:
|
|
||||||
sorted_list = sorted(segs_with_order, key=lambda x: x[0], reverse=True)
|
|
||||||
else:
|
|
||||||
sorted_list = sorted(segs_with_order, key=lambda x: x[0], reverse=False)
|
|
||||||
|
|
||||||
result_list = []
|
|
||||||
remained_list = []
|
|
||||||
|
|
||||||
for i, item in enumerate(sorted_list):
|
|
||||||
if take_start <= i < take_start + take_count:
|
|
||||||
result_list.append(item[1])
|
|
||||||
else:
|
|
||||||
remained_list.append(item[1])
|
|
||||||
|
|
||||||
return (segs[0], result_list), (segs[0], remained_list),
|
|
||||||
|
|
||||||
|
|
||||||
class SEGSRangeFilter:
|
class SEGSRangeFilter:
|
||||||
@@ -629,6 +614,111 @@ class SEGSRangeFilter:
|
|||||||
return (segs[0], new_segs), (segs[0], remained_segs),
|
return (segs[0], new_segs), (segs[0], remained_segs),
|
||||||
|
|
||||||
|
|
||||||
|
class SEGSIntersectionFilter:
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(s):
|
||||||
|
return {"required": {
|
||||||
|
"segs1": ("SEGS", ),
|
||||||
|
"segs2": ("SEGS", ),
|
||||||
|
"ioa_threshold": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 1.0, "step": 0.01}),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN_TYPES = ("SEGS",)
|
||||||
|
RETURN_NAMES = ("filtered_SEGS",)
|
||||||
|
FUNCTION = "doit"
|
||||||
|
|
||||||
|
CATEGORY = "ImpactPack/Util"
|
||||||
|
|
||||||
|
def compute_ioa(self, mask1, mask2):
|
||||||
|
"""Compute Intersection over Area (IoA) between two boxes."""
|
||||||
|
inter_mask = utils.bitwise_and_masks(mask1, mask2)
|
||||||
|
|
||||||
|
inter_area = (inter_mask > 0).sum()
|
||||||
|
area1 = (mask1 > 0).sum()
|
||||||
|
|
||||||
|
return inter_area / area1 if area1 > 0 else 0
|
||||||
|
|
||||||
|
def doit(self, segs1, segs2, ioa_threshold):
|
||||||
|
"""Remove segments from segs1 if their IoA with any segment in segs2 exceeds the threshold."""
|
||||||
|
# Extract bounding boxes for all segments in segs1 and segs2
|
||||||
|
keep = []
|
||||||
|
|
||||||
|
# Iterate over all segments in segs1
|
||||||
|
for idx1, seg1 in enumerate(segs1[1]):
|
||||||
|
keep_segment = True # Assume the segment should be kept
|
||||||
|
mask1 = core.segs_to_combined_mask((segs1[0], [seg1]))
|
||||||
|
|
||||||
|
# Compare with every segment in segs2
|
||||||
|
for seg2 in segs2[1]:
|
||||||
|
mask2 = core.segs_to_combined_mask((segs2[0], [seg2]))
|
||||||
|
ioa = self.compute_ioa(mask1, mask2) # IoA between segment 1 and segment 2
|
||||||
|
|
||||||
|
if ioa > ioa_threshold: # If IoA exceeds the threshold, mark the segment for removal
|
||||||
|
keep_segment = False
|
||||||
|
break # If one overlap exceeds threshold, break early and mark for removal
|
||||||
|
|
||||||
|
# Keep the segment if it did not exceed the threshold with any other segment
|
||||||
|
if keep_segment:
|
||||||
|
keep.append(segs1[1][idx1])
|
||||||
|
|
||||||
|
return (segs1[0], keep), # Return the updated SEGS
|
||||||
|
|
||||||
|
|
||||||
|
class SEGSNMSFilter:
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(cls):
|
||||||
|
return {
|
||||||
|
"required": {
|
||||||
|
"segs": ("SEGS",),
|
||||||
|
"iou_threshold": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 1.0, "step": 0.01}),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN_TYPES = ("SEGS",)
|
||||||
|
RETURN_NAMES = ("filtered_SEGS",)
|
||||||
|
FUNCTION = "doit"
|
||||||
|
|
||||||
|
CATEGORY = "ImpactPack/Util"
|
||||||
|
|
||||||
|
def compute_iou(self, mask1, mask2):
|
||||||
|
"""Compute IoU between two bounding boxes (x1, y1, x2, y2)."""
|
||||||
|
inter_mask = utils.bitwise_and_masks(mask1, mask2)
|
||||||
|
union_mask = utils.add_masks(mask1, mask2)
|
||||||
|
|
||||||
|
inter_area = (inter_mask > 0).sum()
|
||||||
|
union_area = (union_mask > 0).sum()
|
||||||
|
|
||||||
|
return inter_area / union_area if union_area > 0 else 0
|
||||||
|
|
||||||
|
def doit(self, segs, iou_threshold):
|
||||||
|
"""Perform NMS to filter overlapping segments."""
|
||||||
|
confidences = np.ndarray.flatten(np.array([seg.confidence for seg in segs[1]]))
|
||||||
|
|
||||||
|
# Sort boxes by confidence (high to low)
|
||||||
|
sorted_indices = np.argsort(confidences)[::-1].tolist()
|
||||||
|
keep = []
|
||||||
|
|
||||||
|
while len(sorted_indices) > 0:
|
||||||
|
idx = sorted_indices[0]
|
||||||
|
mask1 = core.segs_to_combined_mask((segs[0], [segs[1][idx]]))
|
||||||
|
keep.append(idx)
|
||||||
|
sorted_indices = sorted_indices[1:]
|
||||||
|
|
||||||
|
# Filter indices only contain the indices where the bbox does not intersect
|
||||||
|
filtered_indices = []
|
||||||
|
for i in sorted_indices:
|
||||||
|
mask2 = core.segs_to_combined_mask((segs[0], [segs[1][i]]))
|
||||||
|
iou = self.compute_iou(mask1, mask2)
|
||||||
|
if iou < iou_threshold:
|
||||||
|
filtered_indices.append(i)
|
||||||
|
|
||||||
|
sorted_indices = np.array(filtered_indices)
|
||||||
|
|
||||||
|
filtered_segs = [segs[1][i] for i in keep]
|
||||||
|
return (segs[0], filtered_segs),
|
||||||
|
|
||||||
|
|
||||||
class SEGSToImageList:
|
class SEGSToImageList:
|
||||||
@classmethod
|
@classmethod
|
||||||
def INPUT_TYPES(s):
|
def INPUT_TYPES(s):
|
||||||
|
|||||||
@@ -446,6 +446,31 @@ class MakeMaskList:
|
|||||||
return (masks, )
|
return (masks, )
|
||||||
|
|
||||||
|
|
||||||
|
class NthItemOfAnyList:
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(s):
|
||||||
|
return {"required": {
|
||||||
|
"any_list": (any_typ,),
|
||||||
|
"index": ("INT", {"default": 0, "min": 0, "max": sys.maxsize, "step": 1, "tooltip": "The index of the item you want to select from the list."}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN_TYPES = (any_typ,)
|
||||||
|
INPUT_IS_LIST = True
|
||||||
|
FUNCTION = "doit"
|
||||||
|
|
||||||
|
CATEGORY = "ImpactPack/Util"
|
||||||
|
|
||||||
|
DESCRIPTION = "Selects the Nth item from a list. If the index is out of range, it returns the last item in the list."
|
||||||
|
|
||||||
|
def doit(self, any_list, index):
|
||||||
|
i = index[0]
|
||||||
|
if i >= len(any_list):
|
||||||
|
return (any_list[-1],)
|
||||||
|
else:
|
||||||
|
return (any_list[i],)
|
||||||
|
|
||||||
|
|
||||||
class MakeImageList:
|
class MakeImageList:
|
||||||
@classmethod
|
@classmethod
|
||||||
def INPUT_TYPES(s):
|
def INPUT_TYPES(s):
|
||||||
|
|||||||
@@ -44,7 +44,9 @@ def read_wildcard(k, v):
|
|||||||
elif isinstance(v, str):
|
elif isinstance(v, str):
|
||||||
k = wildcard_normalize(k)
|
k = wildcard_normalize(k)
|
||||||
wildcard_dict[k] = [v]
|
wildcard_dict[k] = [v]
|
||||||
|
elif isinstance(v, (int, float)):
|
||||||
|
k = wildcard_normalize(k)
|
||||||
|
wildcard_dict[k] = [str(v)]
|
||||||
|
|
||||||
def read_wildcard_dict(wildcard_path):
|
def read_wildcard_dict(wildcard_path):
|
||||||
global wildcard_dict
|
global wildcard_dict
|
||||||
@@ -135,6 +137,8 @@ def process(text, seed=None):
|
|||||||
b = r.group(3)
|
b = r.group(3)
|
||||||
if b is not None:
|
if b is not None:
|
||||||
b = b.strip()
|
b = b.strip()
|
||||||
|
else:
|
||||||
|
b = "-1"
|
||||||
|
|
||||||
if r is not None:
|
if r is not None:
|
||||||
if b is not None and is_numeric_string(a) and is_numeric_string(b):
|
if b is not None and is_numeric_string(a) and is_numeric_string(b):
|
||||||
@@ -145,26 +149,32 @@ def process(text, seed=None):
|
|||||||
x = int(a)
|
x = int(a)
|
||||||
select_range = (x, x)
|
select_range = (x, x)
|
||||||
|
|
||||||
|
# Expand wildcard path or return the string after $$
|
||||||
|
def expand_wildcard_or_return_string(options, pattern, wildcard_pattern):
|
||||||
|
matches = re.findall(wildcard_pattern, pattern)
|
||||||
|
if len(options) == 1 and matches:
|
||||||
|
# $$<single wildcard>
|
||||||
|
return get_wildcard_options(pattern)
|
||||||
|
else:
|
||||||
|
# $$opt1|opt2|...
|
||||||
|
options[0] = pattern
|
||||||
|
return options
|
||||||
|
|
||||||
if select_range is not None and len(multi_select_pattern) == 2:
|
if select_range is not None and len(multi_select_pattern) == 2:
|
||||||
# PATTERN: count$$
|
# PATTERN: count$$
|
||||||
matches = re.findall(wildcard_pattern, multi_select_pattern[1])
|
options = expand_wildcard_or_return_string(options, multi_select_pattern[1], wildcard_pattern )
|
||||||
if len(options) == 1 and matches:
|
|
||||||
# count$$<single wildcard>
|
|
||||||
options = get_wildcard_options(multi_select_pattern[1])
|
|
||||||
else:
|
|
||||||
# count$$opt1|opt2|...
|
|
||||||
options[0] = multi_select_pattern[1]
|
|
||||||
elif select_range is not None and len(multi_select_pattern) == 3:
|
elif select_range is not None and len(multi_select_pattern) == 3:
|
||||||
# PATTERN: count$$ sep $$
|
# PATTERN: count$$ sep $$
|
||||||
select_sep = multi_select_pattern[1]
|
select_sep = multi_select_pattern[1]
|
||||||
options[0] = multi_select_pattern[2]
|
options = expand_wildcard_or_return_string(options, multi_select_pattern[2], wildcard_pattern )
|
||||||
|
|
||||||
adjusted_probabilities = []
|
adjusted_probabilities = []
|
||||||
|
|
||||||
total_prob = 0
|
total_prob = 0
|
||||||
|
|
||||||
for option in options:
|
for option in options:
|
||||||
parts = option.split('::', 1)
|
parts = option.split('::', 1) if isinstance(option, str) else f"{option}".split('::', 1)
|
||||||
|
|
||||||
if len(parts) == 2 and is_numeric_string(parts[0].strip()):
|
if len(parts) == 2 and is_numeric_string(parts[0].strip()):
|
||||||
config_value = float(parts[0].strip())
|
config_value = float(parts[0].strip())
|
||||||
else:
|
else:
|
||||||
@@ -178,15 +188,30 @@ def process(text, seed=None):
|
|||||||
if select_range is None:
|
if select_range is None:
|
||||||
select_count = 1
|
select_count = 1
|
||||||
else:
|
else:
|
||||||
select_count = random_gen.integers(low=select_range[0], high=select_range[1]+1, size=1)
|
def calculate_max(_options_length, _max_select_range):
|
||||||
|
return min(_max_select_range + 1, _options_length + 1) if _max_select_range > 0 else _options_length + 1
|
||||||
|
|
||||||
if select_count > len(options):
|
def calculate_select_count(_max_value, _min_select_range, random_gen):
|
||||||
|
if max(_max_value, _min_select_range) <= 0:
|
||||||
|
return 0
|
||||||
|
# fix: low >= high
|
||||||
|
elif _max_value == _min_select_range:
|
||||||
|
return _max_value
|
||||||
|
else:
|
||||||
|
# fix: low >= high
|
||||||
|
_low_value = min(_min_select_range, _max_value)
|
||||||
|
_high_value = max(_min_select_range, _max_value)
|
||||||
|
return random_gen.integers(low=_low_value, high=_high_value, size=1)
|
||||||
|
select_count = calculate_select_count(calculate_max(len(options), select_range[1]), select_range[0], random_gen)
|
||||||
|
|
||||||
|
if select_count > len(options) or total_prob <= 1:
|
||||||
random_gen.shuffle(options)
|
random_gen.shuffle(options)
|
||||||
selected_items = options
|
selected_items = options
|
||||||
else:
|
else:
|
||||||
selected_items = random_gen.choice(options, p=normalized_probabilities, size=select_count, replace=False)
|
selected_items = random_gen.choice(options, p=normalized_probabilities, size=select_count, replace=False)
|
||||||
|
|
||||||
selected_items2 = [re.sub(r'^\s*[0-9.]+::', '', x, 1) for x in selected_items]
|
# x may be numpy.int32, convert to string
|
||||||
|
selected_items2 = [re.sub(r'^\s*[0-9.]+::', '', str(x), 1) for x in selected_items]
|
||||||
replacement = select_sep.join(selected_items2)
|
replacement = select_sep.join(selected_items2)
|
||||||
if '::' in replacement:
|
if '::' in replacement:
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
[project]
|
[project]
|
||||||
name = "comfyui-impact-pack"
|
name = "comfyui-impact-pack"
|
||||||
description = "This node pack offers various detector nodes and detailer nodes that allow you to configure a workflow that automatically enhances facial details. And provide iterative upscaler."
|
description = "This node pack offers various detector nodes and detailer nodes that allow you to configure a workflow that automatically enhances facial details. And provide iterative upscaler."
|
||||||
version = "8.8"
|
version = "8.13"
|
||||||
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"]
|
||||||
|
|
||||||
|
|||||||