Spaces:
Running
Running
gartajackhats1985
commited on
Commit
•
681fa96
1
Parent(s):
c89ae80
Upload 1633 files
Browse filesThis view is limited to 50 files because it contains too many changes.
See raw diff
- .gitattributes +5 -0
- ComfyUI_InstantID/.github/FUNDING.yml +1 -0
- ComfyUI_InstantID/.github/workflows/publish.yml +22 -0
- ComfyUI_InstantID/.gitignore +160 -0
- ComfyUI_InstantID/CrossAttentionPatch.py +190 -0
- ComfyUI_InstantID/InstantID.py +611 -0
- ComfyUI_InstantID/LICENSE +201 -0
- ComfyUI_InstantID/README.md +141 -0
- ComfyUI_InstantID/README.zh-CN.md +137 -0
- ComfyUI_InstantID/__init__.py +3 -0
- ComfyUI_InstantID/__pycache__/CrossAttentionPatch.cpython-312.pyc +0 -0
- ComfyUI_InstantID/__pycache__/InstantID.cpython-312.pyc +0 -0
- ComfyUI_InstantID/__pycache__/__init__.cpython-312.pyc +0 -0
- ComfyUI_InstantID/__pycache__/resampler.cpython-312.pyc +0 -0
- ComfyUI_InstantID/__pycache__/utils.cpython-312.pyc +0 -0
- ComfyUI_InstantID/examples/InstantID_IPAdapter.json +861 -0
- ComfyUI_InstantID/examples/InstantID_basic.json +657 -0
- ComfyUI_InstantID/examples/InstantID_depth.json +881 -0
- ComfyUI_InstantID/examples/InstantID_multi_id.json +1364 -0
- ComfyUI_InstantID/examples/InstantID_posed.json +704 -0
- ComfyUI_InstantID/examples/daydreaming.jpg +0 -0
- ComfyUI_InstantID/examples/instant_id_ipadapter.jpg +0 -0
- ComfyUI_InstantID/examples/instantid_basic_workflow.jpg +0 -0
- ComfyUI_InstantID/examples/instantid_multi_id.jpg +0 -0
- ComfyUI_InstantID/pyproject.toml +15 -0
- ComfyUI_InstantID/requirements.txt +3 -0
- ComfyUI_InstantID/resampler.py +121 -0
- ComfyUI_InstantID/utils.py +24 -0
- ComfyUI_essentials/.github/workflows/publish.yml +22 -0
- ComfyUI_essentials/.gitignore +6 -0
- ComfyUI_essentials/LICENSE +21 -0
- ComfyUI_essentials/README.md +49 -0
- ComfyUI_essentials/__init__.py +36 -0
- ComfyUI_essentials/__pycache__/__init__.cpython-312.pyc +0 -0
- ComfyUI_essentials/__pycache__/conditioning.cpython-312.pyc +0 -0
- ComfyUI_essentials/__pycache__/image.cpython-312.pyc +0 -0
- ComfyUI_essentials/__pycache__/mask.cpython-312.pyc +0 -0
- ComfyUI_essentials/__pycache__/misc.cpython-312.pyc +0 -0
- ComfyUI_essentials/__pycache__/sampling.cpython-312.pyc +0 -0
- ComfyUI_essentials/__pycache__/segmentation.cpython-312.pyc +0 -0
- ComfyUI_essentials/__pycache__/text.cpython-312.pyc +0 -0
- ComfyUI_essentials/__pycache__/utils.cpython-312.pyc +0 -0
- ComfyUI_essentials/carve.py +454 -0
- ComfyUI_essentials/conditioning.py +280 -0
- ComfyUI_essentials/fonts/ShareTechMono-Regular.ttf +0 -0
- ComfyUI_essentials/fonts/put_font_files_here.txt +0 -0
- ComfyUI_essentials/histogram_matching.py +87 -0
- ComfyUI_essentials/image.py +1770 -0
- ComfyUI_essentials/js/DisplayAny.js +36 -0
- ComfyUI_essentials/js/FluxAttentionSeeker.js +133 -0
.gitattributes
CHANGED
@@ -33,3 +33,8 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
36 |
+
comfyui_controlnet_aux/examples/example_mesh_graphormer.png filter=lfs diff=lfs merge=lfs -text
|
37 |
+
comfyui_controlnet_aux/examples/ExecuteAll.png filter=lfs diff=lfs merge=lfs -text
|
38 |
+
comfyui_controlnet_aux/examples/ExecuteAll1.jpg filter=lfs diff=lfs merge=lfs -text
|
39 |
+
comfyui_controlnet_aux/examples/ExecuteAll2.jpg filter=lfs diff=lfs merge=lfs -text
|
40 |
+
comfyui_controlnet_aux/src/custom_controlnet_aux/mesh_graphormer/hand_landmarker.task filter=lfs diff=lfs merge=lfs -text
|
ComfyUI_InstantID/.github/FUNDING.yml
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
github: cubiq
|
ComfyUI_InstantID/.github/workflows/publish.yml
ADDED
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
name: Publish to Comfy registry
|
2 |
+
on:
|
3 |
+
workflow_dispatch:
|
4 |
+
push:
|
5 |
+
branches:
|
6 |
+
- main
|
7 |
+
- master
|
8 |
+
paths:
|
9 |
+
- "pyproject.toml"
|
10 |
+
|
11 |
+
jobs:
|
12 |
+
publish-node:
|
13 |
+
name: Publish Custom Node to registry
|
14 |
+
runs-on: ubuntu-latest
|
15 |
+
steps:
|
16 |
+
- name: Check out code
|
17 |
+
uses: actions/checkout@v4
|
18 |
+
- name: Publish Custom Node
|
19 |
+
uses: Comfy-Org/publish-node-action@main
|
20 |
+
with:
|
21 |
+
## Add your own personal access token to your Github Repository secrets and reference it here.
|
22 |
+
personal_access_token: ${{ secrets.REGISTRY_ACCESS_TOKEN }}
|
ComfyUI_InstantID/.gitignore
ADDED
@@ -0,0 +1,160 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Byte-compiled / optimized / DLL files
|
2 |
+
__pycache__/
|
3 |
+
*.py[cod]
|
4 |
+
*$py.class
|
5 |
+
|
6 |
+
# C extensions
|
7 |
+
*.so
|
8 |
+
|
9 |
+
# Distribution / packaging
|
10 |
+
.Python
|
11 |
+
build/
|
12 |
+
develop-eggs/
|
13 |
+
dist/
|
14 |
+
downloads/
|
15 |
+
eggs/
|
16 |
+
.eggs/
|
17 |
+
lib/
|
18 |
+
lib64/
|
19 |
+
parts/
|
20 |
+
sdist/
|
21 |
+
var/
|
22 |
+
wheels/
|
23 |
+
share/python-wheels/
|
24 |
+
*.egg-info/
|
25 |
+
.installed.cfg
|
26 |
+
*.egg
|
27 |
+
MANIFEST
|
28 |
+
|
29 |
+
# PyInstaller
|
30 |
+
# Usually these files are written by a python script from a template
|
31 |
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
32 |
+
*.manifest
|
33 |
+
*.spec
|
34 |
+
|
35 |
+
# Installer logs
|
36 |
+
pip-log.txt
|
37 |
+
pip-delete-this-directory.txt
|
38 |
+
|
39 |
+
# Unit test / coverage reports
|
40 |
+
htmlcov/
|
41 |
+
.tox/
|
42 |
+
.nox/
|
43 |
+
.coverage
|
44 |
+
.coverage.*
|
45 |
+
.cache
|
46 |
+
nosetests.xml
|
47 |
+
coverage.xml
|
48 |
+
*.cover
|
49 |
+
*.py,cover
|
50 |
+
.hypothesis/
|
51 |
+
.pytest_cache/
|
52 |
+
cover/
|
53 |
+
|
54 |
+
# Translations
|
55 |
+
*.mo
|
56 |
+
*.pot
|
57 |
+
|
58 |
+
# Django stuff:
|
59 |
+
*.log
|
60 |
+
local_settings.py
|
61 |
+
db.sqlite3
|
62 |
+
db.sqlite3-journal
|
63 |
+
|
64 |
+
# Flask stuff:
|
65 |
+
instance/
|
66 |
+
.webassets-cache
|
67 |
+
|
68 |
+
# Scrapy stuff:
|
69 |
+
.scrapy
|
70 |
+
|
71 |
+
# Sphinx documentation
|
72 |
+
docs/_build/
|
73 |
+
|
74 |
+
# PyBuilder
|
75 |
+
.pybuilder/
|
76 |
+
target/
|
77 |
+
|
78 |
+
# Jupyter Notebook
|
79 |
+
.ipynb_checkpoints
|
80 |
+
|
81 |
+
# IPython
|
82 |
+
profile_default/
|
83 |
+
ipython_config.py
|
84 |
+
|
85 |
+
# pyenv
|
86 |
+
# For a library or package, you might want to ignore these files since the code is
|
87 |
+
# intended to run in multiple environments; otherwise, check them in:
|
88 |
+
# .python-version
|
89 |
+
|
90 |
+
# pipenv
|
91 |
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
92 |
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
93 |
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
94 |
+
# install all needed dependencies.
|
95 |
+
#Pipfile.lock
|
96 |
+
|
97 |
+
# poetry
|
98 |
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
99 |
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
100 |
+
# commonly ignored for libraries.
|
101 |
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
102 |
+
#poetry.lock
|
103 |
+
|
104 |
+
# pdm
|
105 |
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
106 |
+
#pdm.lock
|
107 |
+
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
108 |
+
# in version control.
|
109 |
+
# https://pdm.fming.dev/#use-with-ide
|
110 |
+
.pdm.toml
|
111 |
+
|
112 |
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
113 |
+
__pypackages__/
|
114 |
+
|
115 |
+
# Celery stuff
|
116 |
+
celerybeat-schedule
|
117 |
+
celerybeat.pid
|
118 |
+
|
119 |
+
# SageMath parsed files
|
120 |
+
*.sage.py
|
121 |
+
|
122 |
+
# Environments
|
123 |
+
.env
|
124 |
+
.venv
|
125 |
+
env/
|
126 |
+
venv/
|
127 |
+
ENV/
|
128 |
+
env.bak/
|
129 |
+
venv.bak/
|
130 |
+
|
131 |
+
# Spyder project settings
|
132 |
+
.spyderproject
|
133 |
+
.spyproject
|
134 |
+
|
135 |
+
# Rope project settings
|
136 |
+
.ropeproject
|
137 |
+
|
138 |
+
# mkdocs documentation
|
139 |
+
/site
|
140 |
+
|
141 |
+
# mypy
|
142 |
+
.mypy_cache/
|
143 |
+
.dmypy.json
|
144 |
+
dmypy.json
|
145 |
+
|
146 |
+
# Pyre type checker
|
147 |
+
.pyre/
|
148 |
+
|
149 |
+
# pytype static type analyzer
|
150 |
+
.pytype/
|
151 |
+
|
152 |
+
# Cython debug symbols
|
153 |
+
cython_debug/
|
154 |
+
|
155 |
+
# PyCharm
|
156 |
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
157 |
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
158 |
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
159 |
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
160 |
+
#.idea/
|
ComfyUI_InstantID/CrossAttentionPatch.py
ADDED
@@ -0,0 +1,190 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import torch
|
2 |
+
import math
|
3 |
+
import torch.nn.functional as F
|
4 |
+
from comfy.ldm.modules.attention import optimized_attention
|
5 |
+
from .utils import tensor_to_size
|
6 |
+
|
7 |
+
class Attn2Replace:
|
8 |
+
def __init__(self, callback=None, **kwargs):
|
9 |
+
self.callback = [callback]
|
10 |
+
self.kwargs = [kwargs]
|
11 |
+
|
12 |
+
def add(self, callback, **kwargs):
|
13 |
+
self.callback.append(callback)
|
14 |
+
self.kwargs.append(kwargs)
|
15 |
+
|
16 |
+
for key, value in kwargs.items():
|
17 |
+
setattr(self, key, value)
|
18 |
+
|
19 |
+
def __call__(self, q, k, v, extra_options):
|
20 |
+
dtype = q.dtype
|
21 |
+
out = optimized_attention(q, k, v, extra_options["n_heads"])
|
22 |
+
sigma = extra_options["sigmas"].detach().cpu()[0].item() if 'sigmas' in extra_options else 999999999.9
|
23 |
+
|
24 |
+
for i, callback in enumerate(self.callback):
|
25 |
+
if sigma <= self.kwargs[i]["sigma_start"] and sigma >= self.kwargs[i]["sigma_end"]:
|
26 |
+
out = out + callback(out, q, k, v, extra_options, **self.kwargs[i])
|
27 |
+
|
28 |
+
return out.to(dtype=dtype)
|
29 |
+
|
30 |
+
def instantid_attention(out, q, k, v, extra_options, module_key='', ipadapter=None, weight=1.0, cond=None, cond_alt=None, uncond=None, weight_type="linear", mask=None, sigma_start=0.0, sigma_end=1.0, unfold_batch=False, embeds_scaling='V only', **kwargs):
|
31 |
+
dtype = q.dtype
|
32 |
+
cond_or_uncond = extra_options["cond_or_uncond"]
|
33 |
+
block_type = extra_options["block"][0]
|
34 |
+
#block_id = extra_options["block"][1]
|
35 |
+
t_idx = extra_options["transformer_index"]
|
36 |
+
layers = 11 if '101_to_k_ip' in ipadapter.ip_layers.to_kvs else 16
|
37 |
+
k_key = module_key + "_to_k_ip"
|
38 |
+
v_key = module_key + "_to_v_ip"
|
39 |
+
|
40 |
+
# extra options for AnimateDiff
|
41 |
+
ad_params = extra_options['ad_params'] if "ad_params" in extra_options else None
|
42 |
+
|
43 |
+
b = q.shape[0]
|
44 |
+
seq_len = q.shape[1]
|
45 |
+
batch_prompt = b // len(cond_or_uncond)
|
46 |
+
_, _, oh, ow = extra_options["original_shape"]
|
47 |
+
|
48 |
+
if weight_type == 'ease in':
|
49 |
+
weight = weight * (0.05 + 0.95 * (1 - t_idx / layers))
|
50 |
+
elif weight_type == 'ease out':
|
51 |
+
weight = weight * (0.05 + 0.95 * (t_idx / layers))
|
52 |
+
elif weight_type == 'ease in-out':
|
53 |
+
weight = weight * (0.05 + 0.95 * (1 - abs(t_idx - (layers/2)) / (layers/2)))
|
54 |
+
elif weight_type == 'reverse in-out':
|
55 |
+
weight = weight * (0.05 + 0.95 * (abs(t_idx - (layers/2)) / (layers/2)))
|
56 |
+
elif weight_type == 'weak input' and block_type == 'input':
|
57 |
+
weight = weight * 0.2
|
58 |
+
elif weight_type == 'weak middle' and block_type == 'middle':
|
59 |
+
weight = weight * 0.2
|
60 |
+
elif weight_type == 'weak output' and block_type == 'output':
|
61 |
+
weight = weight * 0.2
|
62 |
+
elif weight_type == 'strong middle' and (block_type == 'input' or block_type == 'output'):
|
63 |
+
weight = weight * 0.2
|
64 |
+
elif isinstance(weight, dict):
|
65 |
+
if t_idx not in weight:
|
66 |
+
return 0
|
67 |
+
|
68 |
+
weight = weight[t_idx]
|
69 |
+
|
70 |
+
if cond_alt is not None and t_idx in cond_alt:
|
71 |
+
cond = cond_alt[t_idx]
|
72 |
+
del cond_alt
|
73 |
+
|
74 |
+
if unfold_batch:
|
75 |
+
# Check AnimateDiff context window
|
76 |
+
if ad_params is not None and ad_params["sub_idxs"] is not None:
|
77 |
+
if isinstance(weight, torch.Tensor):
|
78 |
+
weight = tensor_to_size(weight, ad_params["full_length"])
|
79 |
+
weight = torch.Tensor(weight[ad_params["sub_idxs"]])
|
80 |
+
if torch.all(weight == 0):
|
81 |
+
return 0
|
82 |
+
weight = weight.repeat(len(cond_or_uncond), 1, 1) # repeat for cond and uncond
|
83 |
+
elif weight == 0:
|
84 |
+
return 0
|
85 |
+
|
86 |
+
# if image length matches or exceeds full_length get sub_idx images
|
87 |
+
if cond.shape[0] >= ad_params["full_length"]:
|
88 |
+
cond = torch.Tensor(cond[ad_params["sub_idxs"]])
|
89 |
+
uncond = torch.Tensor(uncond[ad_params["sub_idxs"]])
|
90 |
+
# otherwise get sub_idxs images
|
91 |
+
else:
|
92 |
+
cond = tensor_to_size(cond, ad_params["full_length"])
|
93 |
+
uncond = tensor_to_size(uncond, ad_params["full_length"])
|
94 |
+
cond = cond[ad_params["sub_idxs"]]
|
95 |
+
uncond = uncond[ad_params["sub_idxs"]]
|
96 |
+
else:
|
97 |
+
if isinstance(weight, torch.Tensor):
|
98 |
+
weight = tensor_to_size(weight, batch_prompt)
|
99 |
+
if torch.all(weight == 0):
|
100 |
+
return 0
|
101 |
+
weight = weight.repeat(len(cond_or_uncond), 1, 1) # repeat for cond and uncond
|
102 |
+
elif weight == 0:
|
103 |
+
return 0
|
104 |
+
|
105 |
+
cond = tensor_to_size(cond, batch_prompt)
|
106 |
+
uncond = tensor_to_size(uncond, batch_prompt)
|
107 |
+
|
108 |
+
k_cond = ipadapter.ip_layers.to_kvs[k_key](cond)
|
109 |
+
k_uncond = ipadapter.ip_layers.to_kvs[k_key](uncond)
|
110 |
+
v_cond = ipadapter.ip_layers.to_kvs[v_key](cond)
|
111 |
+
v_uncond = ipadapter.ip_layers.to_kvs[v_key](uncond)
|
112 |
+
else:
|
113 |
+
# TODO: should we always convert the weights to a tensor?
|
114 |
+
if isinstance(weight, torch.Tensor):
|
115 |
+
weight = tensor_to_size(weight, batch_prompt)
|
116 |
+
if torch.all(weight == 0):
|
117 |
+
return 0
|
118 |
+
weight = weight.repeat(len(cond_or_uncond), 1, 1) # repeat for cond and uncond
|
119 |
+
elif weight == 0:
|
120 |
+
return 0
|
121 |
+
|
122 |
+
k_cond = ipadapter.ip_layers.to_kvs[k_key](cond).repeat(batch_prompt, 1, 1)
|
123 |
+
k_uncond = ipadapter.ip_layers.to_kvs[k_key](uncond).repeat(batch_prompt, 1, 1)
|
124 |
+
v_cond = ipadapter.ip_layers.to_kvs[v_key](cond).repeat(batch_prompt, 1, 1)
|
125 |
+
v_uncond = ipadapter.ip_layers.to_kvs[v_key](uncond).repeat(batch_prompt, 1, 1)
|
126 |
+
|
127 |
+
ip_k = torch.cat([(k_cond, k_uncond)[i] for i in cond_or_uncond], dim=0)
|
128 |
+
ip_v = torch.cat([(v_cond, v_uncond)[i] for i in cond_or_uncond], dim=0)
|
129 |
+
|
130 |
+
if embeds_scaling == 'K+mean(V) w/ C penalty':
|
131 |
+
scaling = float(ip_k.shape[2]) / 1280.0
|
132 |
+
weight = weight * scaling
|
133 |
+
ip_k = ip_k * weight
|
134 |
+
ip_v_mean = torch.mean(ip_v, dim=1, keepdim=True)
|
135 |
+
ip_v = (ip_v - ip_v_mean) + ip_v_mean * weight
|
136 |
+
out_ip = optimized_attention(q, ip_k, ip_v, extra_options["n_heads"])
|
137 |
+
del ip_v_mean
|
138 |
+
elif embeds_scaling == 'K+V w/ C penalty':
|
139 |
+
scaling = float(ip_k.shape[2]) / 1280.0
|
140 |
+
weight = weight * scaling
|
141 |
+
ip_k = ip_k * weight
|
142 |
+
ip_v = ip_v * weight
|
143 |
+
out_ip = optimized_attention(q, ip_k, ip_v, extra_options["n_heads"])
|
144 |
+
elif embeds_scaling == 'K+V':
|
145 |
+
ip_k = ip_k * weight
|
146 |
+
ip_v = ip_v * weight
|
147 |
+
out_ip = optimized_attention(q, ip_k, ip_v, extra_options["n_heads"])
|
148 |
+
else:
|
149 |
+
#ip_v = ip_v * weight
|
150 |
+
out_ip = optimized_attention(q, ip_k, ip_v, extra_options["n_heads"])
|
151 |
+
out_ip = out_ip * weight # I'm doing this to get the same results as before
|
152 |
+
|
153 |
+
if mask is not None:
|
154 |
+
mask_h = oh / math.sqrt(oh * ow / seq_len)
|
155 |
+
mask_h = int(mask_h) + int((seq_len % int(mask_h)) != 0)
|
156 |
+
mask_w = seq_len // mask_h
|
157 |
+
|
158 |
+
# check if using AnimateDiff and sliding context window
|
159 |
+
if (mask.shape[0] > 1 and ad_params is not None and ad_params["sub_idxs"] is not None):
|
160 |
+
# if mask length matches or exceeds full_length, get sub_idx masks
|
161 |
+
if mask.shape[0] >= ad_params["full_length"]:
|
162 |
+
mask = torch.Tensor(mask[ad_params["sub_idxs"]])
|
163 |
+
mask = F.interpolate(mask.unsqueeze(1), size=(mask_h, mask_w), mode="bilinear").squeeze(1)
|
164 |
+
else:
|
165 |
+
mask = F.interpolate(mask.unsqueeze(1), size=(mask_h, mask_w), mode="bilinear").squeeze(1)
|
166 |
+
mask = tensor_to_size(mask, ad_params["full_length"])
|
167 |
+
mask = mask[ad_params["sub_idxs"]]
|
168 |
+
else:
|
169 |
+
mask = F.interpolate(mask.unsqueeze(1), size=(mask_h, mask_w), mode="bilinear").squeeze(1)
|
170 |
+
mask = tensor_to_size(mask, batch_prompt)
|
171 |
+
|
172 |
+
mask = mask.repeat(len(cond_or_uncond), 1, 1)
|
173 |
+
mask = mask.view(mask.shape[0], -1, 1).repeat(1, 1, out.shape[2])
|
174 |
+
|
175 |
+
# covers cases where extreme aspect ratios can cause the mask to have a wrong size
|
176 |
+
mask_len = mask_h * mask_w
|
177 |
+
if mask_len < seq_len:
|
178 |
+
pad_len = seq_len - mask_len
|
179 |
+
pad1 = pad_len // 2
|
180 |
+
pad2 = pad_len - pad1
|
181 |
+
mask = F.pad(mask, (0, 0, pad1, pad2), value=0.0)
|
182 |
+
elif mask_len > seq_len:
|
183 |
+
crop_start = (mask_len - seq_len) // 2
|
184 |
+
mask = mask[:, crop_start:crop_start+seq_len, :]
|
185 |
+
|
186 |
+
out_ip = out_ip * mask
|
187 |
+
|
188 |
+
#out = out + out_ip
|
189 |
+
|
190 |
+
return out_ip.to(dtype=dtype)
|
ComfyUI_InstantID/InstantID.py
ADDED
@@ -0,0 +1,611 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import torch
|
2 |
+
import os
|
3 |
+
import comfy.utils
|
4 |
+
import folder_paths
|
5 |
+
import numpy as np
|
6 |
+
import math
|
7 |
+
import cv2
|
8 |
+
import PIL.Image
|
9 |
+
from .resampler import Resampler
|
10 |
+
from .CrossAttentionPatch import Attn2Replace, instantid_attention
|
11 |
+
from .utils import tensor_to_image
|
12 |
+
|
13 |
+
from insightface.app import FaceAnalysis
|
14 |
+
|
15 |
+
try:
|
16 |
+
import torchvision.transforms.v2 as T
|
17 |
+
except ImportError:
|
18 |
+
import torchvision.transforms as T
|
19 |
+
|
20 |
+
import torch.nn.functional as F
|
21 |
+
|
22 |
+
MODELS_DIR = os.path.join(folder_paths.models_dir, "instantid")
|
23 |
+
if "instantid" not in folder_paths.folder_names_and_paths:
|
24 |
+
current_paths = [MODELS_DIR]
|
25 |
+
else:
|
26 |
+
current_paths, _ = folder_paths.folder_names_and_paths["instantid"]
|
27 |
+
folder_paths.folder_names_and_paths["instantid"] = (current_paths, folder_paths.supported_pt_extensions)
|
28 |
+
|
29 |
+
INSIGHTFACE_DIR = os.path.join(folder_paths.models_dir, "insightface")
|
30 |
+
|
31 |
+
def draw_kps(image_pil, kps, color_list=[(255,0,0), (0,255,0), (0,0,255), (255,255,0), (255,0,255)]):
|
32 |
+
stickwidth = 4
|
33 |
+
limbSeq = np.array([[0, 2], [1, 2], [3, 2], [4, 2]])
|
34 |
+
kps = np.array(kps)
|
35 |
+
|
36 |
+
h, w, _ = image_pil.shape
|
37 |
+
out_img = np.zeros([h, w, 3])
|
38 |
+
|
39 |
+
for i in range(len(limbSeq)):
|
40 |
+
index = limbSeq[i]
|
41 |
+
color = color_list[index[0]]
|
42 |
+
|
43 |
+
x = kps[index][:, 0]
|
44 |
+
y = kps[index][:, 1]
|
45 |
+
length = ((x[0] - x[1]) ** 2 + (y[0] - y[1]) ** 2) ** 0.5
|
46 |
+
angle = math.degrees(math.atan2(y[0] - y[1], x[0] - x[1]))
|
47 |
+
polygon = cv2.ellipse2Poly((int(np.mean(x)), int(np.mean(y))), (int(length / 2), stickwidth), int(angle), 0, 360, 1)
|
48 |
+
out_img = cv2.fillConvexPoly(out_img.copy(), polygon, color)
|
49 |
+
out_img = (out_img * 0.6).astype(np.uint8)
|
50 |
+
|
51 |
+
for idx_kp, kp in enumerate(kps):
|
52 |
+
color = color_list[idx_kp]
|
53 |
+
x, y = kp
|
54 |
+
out_img = cv2.circle(out_img.copy(), (int(x), int(y)), 10, color, -1)
|
55 |
+
|
56 |
+
out_img_pil = PIL.Image.fromarray(out_img.astype(np.uint8))
|
57 |
+
return out_img_pil
|
58 |
+
|
59 |
+
class InstantID(torch.nn.Module):
|
60 |
+
def __init__(self, instantid_model, cross_attention_dim=1280, output_cross_attention_dim=1024, clip_embeddings_dim=512, clip_extra_context_tokens=16):
|
61 |
+
super().__init__()
|
62 |
+
|
63 |
+
self.clip_embeddings_dim = clip_embeddings_dim
|
64 |
+
self.cross_attention_dim = cross_attention_dim
|
65 |
+
self.output_cross_attention_dim = output_cross_attention_dim
|
66 |
+
self.clip_extra_context_tokens = clip_extra_context_tokens
|
67 |
+
|
68 |
+
self.image_proj_model = self.init_proj()
|
69 |
+
|
70 |
+
self.image_proj_model.load_state_dict(instantid_model["image_proj"])
|
71 |
+
self.ip_layers = To_KV(instantid_model["ip_adapter"])
|
72 |
+
|
73 |
+
def init_proj(self):
|
74 |
+
image_proj_model = Resampler(
|
75 |
+
dim=self.cross_attention_dim,
|
76 |
+
depth=4,
|
77 |
+
dim_head=64,
|
78 |
+
heads=20,
|
79 |
+
num_queries=self.clip_extra_context_tokens,
|
80 |
+
embedding_dim=self.clip_embeddings_dim,
|
81 |
+
output_dim=self.output_cross_attention_dim,
|
82 |
+
ff_mult=4
|
83 |
+
)
|
84 |
+
return image_proj_model
|
85 |
+
|
86 |
+
@torch.inference_mode()
|
87 |
+
def get_image_embeds(self, clip_embed, clip_embed_zeroed):
|
88 |
+
#image_prompt_embeds = clip_embed.clone().detach()
|
89 |
+
image_prompt_embeds = self.image_proj_model(clip_embed)
|
90 |
+
#uncond_image_prompt_embeds = clip_embed_zeroed.clone().detach()
|
91 |
+
uncond_image_prompt_embeds = self.image_proj_model(clip_embed_zeroed)
|
92 |
+
|
93 |
+
return image_prompt_embeds, uncond_image_prompt_embeds
|
94 |
+
|
95 |
+
class ImageProjModel(torch.nn.Module):
|
96 |
+
def __init__(self, cross_attention_dim=1024, clip_embeddings_dim=1024, clip_extra_context_tokens=4):
|
97 |
+
super().__init__()
|
98 |
+
|
99 |
+
self.cross_attention_dim = cross_attention_dim
|
100 |
+
self.clip_extra_context_tokens = clip_extra_context_tokens
|
101 |
+
self.proj = torch.nn.Linear(clip_embeddings_dim, self.clip_extra_context_tokens * cross_attention_dim)
|
102 |
+
self.norm = torch.nn.LayerNorm(cross_attention_dim)
|
103 |
+
|
104 |
+
def forward(self, image_embeds):
|
105 |
+
embeds = image_embeds
|
106 |
+
clip_extra_context_tokens = self.proj(embeds).reshape(-1, self.clip_extra_context_tokens, self.cross_attention_dim)
|
107 |
+
clip_extra_context_tokens = self.norm(clip_extra_context_tokens)
|
108 |
+
return clip_extra_context_tokens
|
109 |
+
|
110 |
+
class To_KV(torch.nn.Module):
|
111 |
+
def __init__(self, state_dict):
|
112 |
+
super().__init__()
|
113 |
+
|
114 |
+
self.to_kvs = torch.nn.ModuleDict()
|
115 |
+
for key, value in state_dict.items():
|
116 |
+
k = key.replace(".weight", "").replace(".", "_")
|
117 |
+
self.to_kvs[k] = torch.nn.Linear(value.shape[1], value.shape[0], bias=False)
|
118 |
+
self.to_kvs[k].weight.data = value
|
119 |
+
|
120 |
+
def _set_model_patch_replace(model, patch_kwargs, key):
|
121 |
+
to = model.model_options["transformer_options"].copy()
|
122 |
+
if "patches_replace" not in to:
|
123 |
+
to["patches_replace"] = {}
|
124 |
+
else:
|
125 |
+
to["patches_replace"] = to["patches_replace"].copy()
|
126 |
+
|
127 |
+
if "attn2" not in to["patches_replace"]:
|
128 |
+
to["patches_replace"]["attn2"] = {}
|
129 |
+
else:
|
130 |
+
to["patches_replace"]["attn2"] = to["patches_replace"]["attn2"].copy()
|
131 |
+
|
132 |
+
if key not in to["patches_replace"]["attn2"]:
|
133 |
+
to["patches_replace"]["attn2"][key] = Attn2Replace(instantid_attention, **patch_kwargs)
|
134 |
+
model.model_options["transformer_options"] = to
|
135 |
+
else:
|
136 |
+
to["patches_replace"]["attn2"][key].add(instantid_attention, **patch_kwargs)
|
137 |
+
|
138 |
+
class InstantIDModelLoader:
|
139 |
+
@classmethod
|
140 |
+
def INPUT_TYPES(s):
|
141 |
+
return {"required": { "instantid_file": (folder_paths.get_filename_list("instantid"), )}}
|
142 |
+
|
143 |
+
RETURN_TYPES = ("INSTANTID",)
|
144 |
+
FUNCTION = "load_model"
|
145 |
+
CATEGORY = "InstantID"
|
146 |
+
|
147 |
+
def load_model(self, instantid_file):
|
148 |
+
ckpt_path = folder_paths.get_full_path("instantid", instantid_file)
|
149 |
+
|
150 |
+
model = comfy.utils.load_torch_file(ckpt_path, safe_load=True)
|
151 |
+
|
152 |
+
if ckpt_path.lower().endswith(".safetensors"):
|
153 |
+
st_model = {"image_proj": {}, "ip_adapter": {}}
|
154 |
+
for key in model.keys():
|
155 |
+
if key.startswith("image_proj."):
|
156 |
+
st_model["image_proj"][key.replace("image_proj.", "")] = model[key]
|
157 |
+
elif key.startswith("ip_adapter."):
|
158 |
+
st_model["ip_adapter"][key.replace("ip_adapter.", "")] = model[key]
|
159 |
+
model = st_model
|
160 |
+
|
161 |
+
model = InstantID(
|
162 |
+
model,
|
163 |
+
cross_attention_dim=1280,
|
164 |
+
output_cross_attention_dim=model["ip_adapter"]["1.to_k_ip.weight"].shape[1],
|
165 |
+
clip_embeddings_dim=512,
|
166 |
+
clip_extra_context_tokens=16,
|
167 |
+
)
|
168 |
+
|
169 |
+
return (model,)
|
170 |
+
|
171 |
+
def extractFeatures(insightface, image, extract_kps=False):
|
172 |
+
face_img = tensor_to_image(image)
|
173 |
+
out = []
|
174 |
+
|
175 |
+
insightface.det_model.input_size = (640,640) # reset the detection size
|
176 |
+
|
177 |
+
for i in range(face_img.shape[0]):
|
178 |
+
for size in [(size, size) for size in range(640, 128, -64)]:
|
179 |
+
insightface.det_model.input_size = size # TODO: hacky but seems to be working
|
180 |
+
face = insightface.get(face_img[i])
|
181 |
+
if face:
|
182 |
+
face = sorted(face, key=lambda x:(x['bbox'][2]-x['bbox'][0])*(x['bbox'][3]-x['bbox'][1]))[-1]
|
183 |
+
|
184 |
+
if extract_kps:
|
185 |
+
out.append(draw_kps(face_img[i], face['kps']))
|
186 |
+
else:
|
187 |
+
out.append(torch.from_numpy(face['embedding']).unsqueeze(0))
|
188 |
+
|
189 |
+
if 640 not in size:
|
190 |
+
print(f"\033[33mINFO: InsightFace detection resolution lowered to {size}.\033[0m")
|
191 |
+
break
|
192 |
+
|
193 |
+
if out:
|
194 |
+
if extract_kps:
|
195 |
+
out = torch.stack(T.ToTensor()(out), dim=0).permute([0,2,3,1])
|
196 |
+
else:
|
197 |
+
out = torch.stack(out, dim=0)
|
198 |
+
else:
|
199 |
+
out = None
|
200 |
+
|
201 |
+
return out
|
202 |
+
|
203 |
+
class InstantIDFaceAnalysis:
|
204 |
+
@classmethod
|
205 |
+
def INPUT_TYPES(s):
|
206 |
+
return {
|
207 |
+
"required": {
|
208 |
+
"provider": (["CPU", "CUDA", "ROCM", "CoreML"], ),
|
209 |
+
},
|
210 |
+
}
|
211 |
+
|
212 |
+
RETURN_TYPES = ("FACEANALYSIS",)
|
213 |
+
FUNCTION = "load_insight_face"
|
214 |
+
CATEGORY = "InstantID"
|
215 |
+
|
216 |
+
def load_insight_face(self, provider):
|
217 |
+
model = FaceAnalysis(name="antelopev2", root=INSIGHTFACE_DIR, providers=[provider + 'ExecutionProvider',]) # alternative to buffalo_l
|
218 |
+
model.prepare(ctx_id=0, det_size=(640, 640))
|
219 |
+
|
220 |
+
return (model,)
|
221 |
+
|
222 |
+
class FaceKeypointsPreprocessor:
|
223 |
+
@classmethod
|
224 |
+
def INPUT_TYPES(s):
|
225 |
+
return {
|
226 |
+
"required": {
|
227 |
+
"faceanalysis": ("FACEANALYSIS", ),
|
228 |
+
"image": ("IMAGE", ),
|
229 |
+
},
|
230 |
+
}
|
231 |
+
RETURN_TYPES = ("IMAGE",)
|
232 |
+
FUNCTION = "preprocess_image"
|
233 |
+
CATEGORY = "InstantID"
|
234 |
+
|
235 |
+
def preprocess_image(self, faceanalysis, image):
|
236 |
+
face_kps = extractFeatures(faceanalysis, image, extract_kps=True)
|
237 |
+
|
238 |
+
if face_kps is None:
|
239 |
+
face_kps = torch.zeros_like(image)
|
240 |
+
print(f"\033[33mWARNING: no face detected, unable to extract the keypoints!\033[0m")
|
241 |
+
#raise Exception('Face Keypoints Image: No face detected.')
|
242 |
+
|
243 |
+
return (face_kps,)
|
244 |
+
|
245 |
+
def add_noise(image, factor):
|
246 |
+
seed = int(torch.sum(image).item()) % 1000000007
|
247 |
+
torch.manual_seed(seed)
|
248 |
+
mask = (torch.rand_like(image) < factor).float()
|
249 |
+
noise = torch.rand_like(image)
|
250 |
+
noise = torch.zeros_like(image) * (1-mask) + noise * mask
|
251 |
+
|
252 |
+
return factor*noise
|
253 |
+
|
254 |
+
class ApplyInstantID:
|
255 |
+
@classmethod
|
256 |
+
def INPUT_TYPES(s):
|
257 |
+
return {
|
258 |
+
"required": {
|
259 |
+
"instantid": ("INSTANTID", ),
|
260 |
+
"insightface": ("FACEANALYSIS", ),
|
261 |
+
"control_net": ("CONTROL_NET", ),
|
262 |
+
"image": ("IMAGE", ),
|
263 |
+
"model": ("MODEL", ),
|
264 |
+
"positive": ("CONDITIONING", ),
|
265 |
+
"negative": ("CONDITIONING", ),
|
266 |
+
"weight": ("FLOAT", {"default": .8, "min": 0.0, "max": 5.0, "step": 0.01, }),
|
267 |
+
"start_at": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.001, }),
|
268 |
+
"end_at": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.001, }),
|
269 |
+
},
|
270 |
+
"optional": {
|
271 |
+
"image_kps": ("IMAGE",),
|
272 |
+
"mask": ("MASK",),
|
273 |
+
}
|
274 |
+
}
|
275 |
+
|
276 |
+
RETURN_TYPES = ("MODEL", "CONDITIONING", "CONDITIONING",)
|
277 |
+
RETURN_NAMES = ("MODEL", "positive", "negative", )
|
278 |
+
FUNCTION = "apply_instantid"
|
279 |
+
CATEGORY = "InstantID"
|
280 |
+
|
281 |
+
def apply_instantid(self, instantid, insightface, control_net, image, model, positive, negative, start_at, end_at, weight=.8, ip_weight=None, cn_strength=None, noise=0.35, image_kps=None, mask=None, combine_embeds='average'):
|
282 |
+
dtype = comfy.model_management.unet_dtype()
|
283 |
+
if dtype not in [torch.float32, torch.float16, torch.bfloat16]:
|
284 |
+
dtype = torch.float16 if comfy.model_management.should_use_fp16() else torch.float32
|
285 |
+
|
286 |
+
self.dtype = dtype
|
287 |
+
self.device = comfy.model_management.get_torch_device()
|
288 |
+
|
289 |
+
ip_weight = weight if ip_weight is None else ip_weight
|
290 |
+
cn_strength = weight if cn_strength is None else cn_strength
|
291 |
+
|
292 |
+
face_embed = extractFeatures(insightface, image)
|
293 |
+
if face_embed is None:
|
294 |
+
raise Exception('Reference Image: No face detected.')
|
295 |
+
|
296 |
+
# if no keypoints image is provided, use the image itself (only the first one in the batch)
|
297 |
+
face_kps = extractFeatures(insightface, image_kps if image_kps is not None else image[0].unsqueeze(0), extract_kps=True)
|
298 |
+
|
299 |
+
if face_kps is None:
|
300 |
+
face_kps = torch.zeros_like(image) if image_kps is None else image_kps
|
301 |
+
print(f"\033[33mWARNING: No face detected in the keypoints image!\033[0m")
|
302 |
+
|
303 |
+
clip_embed = face_embed
|
304 |
+
# InstantID works better with averaged embeds (TODO: needs testing)
|
305 |
+
if clip_embed.shape[0] > 1:
|
306 |
+
if combine_embeds == 'average':
|
307 |
+
clip_embed = torch.mean(clip_embed, dim=0).unsqueeze(0)
|
308 |
+
elif combine_embeds == 'norm average':
|
309 |
+
clip_embed = torch.mean(clip_embed / torch.norm(clip_embed, dim=0, keepdim=True), dim=0).unsqueeze(0)
|
310 |
+
|
311 |
+
if noise > 0:
|
312 |
+
seed = int(torch.sum(clip_embed).item()) % 1000000007
|
313 |
+
torch.manual_seed(seed)
|
314 |
+
clip_embed_zeroed = noise * torch.rand_like(clip_embed)
|
315 |
+
#clip_embed_zeroed = add_noise(clip_embed, noise)
|
316 |
+
else:
|
317 |
+
clip_embed_zeroed = torch.zeros_like(clip_embed)
|
318 |
+
|
319 |
+
# 1: patch the attention
|
320 |
+
self.instantid = instantid
|
321 |
+
self.instantid.to(self.device, dtype=self.dtype)
|
322 |
+
|
323 |
+
image_prompt_embeds, uncond_image_prompt_embeds = self.instantid.get_image_embeds(clip_embed.to(self.device, dtype=self.dtype), clip_embed_zeroed.to(self.device, dtype=self.dtype))
|
324 |
+
|
325 |
+
image_prompt_embeds = image_prompt_embeds.to(self.device, dtype=self.dtype)
|
326 |
+
uncond_image_prompt_embeds = uncond_image_prompt_embeds.to(self.device, dtype=self.dtype)
|
327 |
+
|
328 |
+
work_model = model.clone()
|
329 |
+
|
330 |
+
sigma_start = model.get_model_object("model_sampling").percent_to_sigma(start_at)
|
331 |
+
sigma_end = model.get_model_object("model_sampling").percent_to_sigma(end_at)
|
332 |
+
|
333 |
+
if mask is not None:
|
334 |
+
mask = mask.to(self.device)
|
335 |
+
|
336 |
+
patch_kwargs = {
|
337 |
+
"ipadapter": self.instantid,
|
338 |
+
"weight": ip_weight,
|
339 |
+
"cond": image_prompt_embeds,
|
340 |
+
"uncond": uncond_image_prompt_embeds,
|
341 |
+
"mask": mask,
|
342 |
+
"sigma_start": sigma_start,
|
343 |
+
"sigma_end": sigma_end,
|
344 |
+
}
|
345 |
+
|
346 |
+
number = 0
|
347 |
+
for id in [4,5,7,8]: # id of input_blocks that have cross attention
|
348 |
+
block_indices = range(2) if id in [4, 5] else range(10) # transformer_depth
|
349 |
+
for index in block_indices:
|
350 |
+
patch_kwargs["module_key"] = str(number*2+1)
|
351 |
+
_set_model_patch_replace(work_model, patch_kwargs, ("input", id, index))
|
352 |
+
number += 1
|
353 |
+
for id in range(6): # id of output_blocks that have cross attention
|
354 |
+
block_indices = range(2) if id in [3, 4, 5] else range(10) # transformer_depth
|
355 |
+
for index in block_indices:
|
356 |
+
patch_kwargs["module_key"] = str(number*2+1)
|
357 |
+
_set_model_patch_replace(work_model, patch_kwargs, ("output", id, index))
|
358 |
+
number += 1
|
359 |
+
for index in range(10):
|
360 |
+
patch_kwargs["module_key"] = str(number*2+1)
|
361 |
+
_set_model_patch_replace(work_model, patch_kwargs, ("middle", 1, index))
|
362 |
+
number += 1
|
363 |
+
|
364 |
+
# 2: do the ControlNet
|
365 |
+
if mask is not None and len(mask.shape) < 3:
|
366 |
+
mask = mask.unsqueeze(0)
|
367 |
+
|
368 |
+
cnets = {}
|
369 |
+
cond_uncond = []
|
370 |
+
|
371 |
+
is_cond = True
|
372 |
+
for conditioning in [positive, negative]:
|
373 |
+
c = []
|
374 |
+
for t in conditioning:
|
375 |
+
d = t[1].copy()
|
376 |
+
|
377 |
+
prev_cnet = d.get('control', None)
|
378 |
+
if prev_cnet in cnets:
|
379 |
+
c_net = cnets[prev_cnet]
|
380 |
+
else:
|
381 |
+
c_net = control_net.copy().set_cond_hint(face_kps.movedim(-1,1), cn_strength, (start_at, end_at))
|
382 |
+
c_net.set_previous_controlnet(prev_cnet)
|
383 |
+
cnets[prev_cnet] = c_net
|
384 |
+
|
385 |
+
d['control'] = c_net
|
386 |
+
d['control_apply_to_uncond'] = False
|
387 |
+
d['cross_attn_controlnet'] = image_prompt_embeds.to(comfy.model_management.intermediate_device(), dtype=c_net.cond_hint_original.dtype) if is_cond else uncond_image_prompt_embeds.to(comfy.model_management.intermediate_device(), dtype=c_net.cond_hint_original.dtype)
|
388 |
+
|
389 |
+
if mask is not None and is_cond:
|
390 |
+
d['mask'] = mask
|
391 |
+
d['set_area_to_bounds'] = False
|
392 |
+
|
393 |
+
n = [t[0], d]
|
394 |
+
c.append(n)
|
395 |
+
cond_uncond.append(c)
|
396 |
+
is_cond = False
|
397 |
+
|
398 |
+
return(work_model, cond_uncond[0], cond_uncond[1], )
|
399 |
+
|
400 |
+
class ApplyInstantIDAdvanced(ApplyInstantID):
|
401 |
+
@classmethod
|
402 |
+
def INPUT_TYPES(s):
|
403 |
+
return {
|
404 |
+
"required": {
|
405 |
+
"instantid": ("INSTANTID", ),
|
406 |
+
"insightface": ("FACEANALYSIS", ),
|
407 |
+
"control_net": ("CONTROL_NET", ),
|
408 |
+
"image": ("IMAGE", ),
|
409 |
+
"model": ("MODEL", ),
|
410 |
+
"positive": ("CONDITIONING", ),
|
411 |
+
"negative": ("CONDITIONING", ),
|
412 |
+
"ip_weight": ("FLOAT", {"default": .8, "min": 0.0, "max": 3.0, "step": 0.01, }),
|
413 |
+
"cn_strength": ("FLOAT", {"default": .8, "min": 0.0, "max": 10.0, "step": 0.01, }),
|
414 |
+
"start_at": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.001, }),
|
415 |
+
"end_at": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.001, }),
|
416 |
+
"noise": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.1, }),
|
417 |
+
"combine_embeds": (['average', 'norm average', 'concat'], {"default": 'average'}),
|
418 |
+
},
|
419 |
+
"optional": {
|
420 |
+
"image_kps": ("IMAGE",),
|
421 |
+
"mask": ("MASK",),
|
422 |
+
}
|
423 |
+
}
|
424 |
+
|
425 |
+
class InstantIDAttentionPatch:
|
426 |
+
@classmethod
|
427 |
+
def INPUT_TYPES(s):
|
428 |
+
return {
|
429 |
+
"required": {
|
430 |
+
"instantid": ("INSTANTID", ),
|
431 |
+
"insightface": ("FACEANALYSIS", ),
|
432 |
+
"image": ("IMAGE", ),
|
433 |
+
"model": ("MODEL", ),
|
434 |
+
"weight": ("FLOAT", {"default": 1.0, "min": -1.0, "max": 3.0, "step": 0.01, }),
|
435 |
+
"start_at": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.001, }),
|
436 |
+
"end_at": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.001, }),
|
437 |
+
"noise": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.1, }),
|
438 |
+
},
|
439 |
+
"optional": {
|
440 |
+
"mask": ("MASK",),
|
441 |
+
}
|
442 |
+
}
|
443 |
+
|
444 |
+
RETURN_TYPES = ("MODEL", "FACE_EMBEDS")
|
445 |
+
FUNCTION = "patch_attention"
|
446 |
+
CATEGORY = "InstantID"
|
447 |
+
|
448 |
+
def patch_attention(self, instantid, insightface, image, model, weight, start_at, end_at, noise=0.0, mask=None):
|
449 |
+
self.dtype = torch.float16 if comfy.model_management.should_use_fp16() else torch.float32
|
450 |
+
self.device = comfy.model_management.get_torch_device()
|
451 |
+
|
452 |
+
face_embed = extractFeatures(insightface, image)
|
453 |
+
if face_embed is None:
|
454 |
+
raise Exception('Reference Image: No face detected.')
|
455 |
+
|
456 |
+
clip_embed = face_embed
|
457 |
+
# InstantID works better with averaged embeds (TODO: needs testing)
|
458 |
+
if clip_embed.shape[0] > 1:
|
459 |
+
clip_embed = torch.mean(clip_embed, dim=0).unsqueeze(0)
|
460 |
+
|
461 |
+
if noise > 0:
|
462 |
+
seed = int(torch.sum(clip_embed).item()) % 1000000007
|
463 |
+
torch.manual_seed(seed)
|
464 |
+
clip_embed_zeroed = noise * torch.rand_like(clip_embed)
|
465 |
+
else:
|
466 |
+
clip_embed_zeroed = torch.zeros_like(clip_embed)
|
467 |
+
|
468 |
+
# 1: patch the attention
|
469 |
+
self.instantid = instantid
|
470 |
+
self.instantid.to(self.device, dtype=self.dtype)
|
471 |
+
|
472 |
+
image_prompt_embeds, uncond_image_prompt_embeds = self.instantid.get_image_embeds(clip_embed.to(self.device, dtype=self.dtype), clip_embed_zeroed.to(self.device, dtype=self.dtype))
|
473 |
+
|
474 |
+
image_prompt_embeds = image_prompt_embeds.to(self.device, dtype=self.dtype)
|
475 |
+
uncond_image_prompt_embeds = uncond_image_prompt_embeds.to(self.device, dtype=self.dtype)
|
476 |
+
|
477 |
+
if weight == 0:
|
478 |
+
return (model, { "cond": image_prompt_embeds, "uncond": uncond_image_prompt_embeds } )
|
479 |
+
|
480 |
+
work_model = model.clone()
|
481 |
+
|
482 |
+
sigma_start = model.get_model_object("model_sampling").percent_to_sigma(start_at)
|
483 |
+
sigma_end = model.get_model_object("model_sampling").percent_to_sigma(end_at)
|
484 |
+
|
485 |
+
if mask is not None:
|
486 |
+
mask = mask.to(self.device)
|
487 |
+
|
488 |
+
patch_kwargs = {
|
489 |
+
"weight": weight,
|
490 |
+
"ipadapter": self.instantid,
|
491 |
+
"cond": image_prompt_embeds,
|
492 |
+
"uncond": uncond_image_prompt_embeds,
|
493 |
+
"mask": mask,
|
494 |
+
"sigma_start": sigma_start,
|
495 |
+
"sigma_end": sigma_end,
|
496 |
+
}
|
497 |
+
|
498 |
+
number = 0
|
499 |
+
for id in [4,5,7,8]: # id of input_blocks that have cross attention
|
500 |
+
block_indices = range(2) if id in [4, 5] else range(10) # transformer_depth
|
501 |
+
for index in block_indices:
|
502 |
+
patch_kwargs["module_key"] = str(number*2+1)
|
503 |
+
_set_model_patch_replace(work_model, patch_kwargs, ("input", id, index))
|
504 |
+
number += 1
|
505 |
+
for id in range(6): # id of output_blocks that have cross attention
|
506 |
+
block_indices = range(2) if id in [3, 4, 5] else range(10) # transformer_depth
|
507 |
+
for index in block_indices:
|
508 |
+
patch_kwargs["module_key"] = str(number*2+1)
|
509 |
+
_set_model_patch_replace(work_model, patch_kwargs, ("output", id, index))
|
510 |
+
number += 1
|
511 |
+
for index in range(10):
|
512 |
+
patch_kwargs["module_key"] = str(number*2+1)
|
513 |
+
_set_model_patch_replace(work_model, patch_kwargs, ("middle", 0, index))
|
514 |
+
number += 1
|
515 |
+
|
516 |
+
return(work_model, { "cond": image_prompt_embeds, "uncond": uncond_image_prompt_embeds }, )
|
517 |
+
|
518 |
+
class ApplyInstantIDControlNet:
|
519 |
+
@classmethod
|
520 |
+
def INPUT_TYPES(s):
|
521 |
+
return {
|
522 |
+
"required": {
|
523 |
+
"face_embeds": ("FACE_EMBEDS", ),
|
524 |
+
"control_net": ("CONTROL_NET", ),
|
525 |
+
"image_kps": ("IMAGE", ),
|
526 |
+
"positive": ("CONDITIONING", ),
|
527 |
+
"negative": ("CONDITIONING", ),
|
528 |
+
"strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.01, }),
|
529 |
+
"start_at": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.001, }),
|
530 |
+
"end_at": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.001, }),
|
531 |
+
},
|
532 |
+
"optional": {
|
533 |
+
"mask": ("MASK",),
|
534 |
+
}
|
535 |
+
}
|
536 |
+
|
537 |
+
RETURN_TYPES = ("CONDITIONING", "CONDITIONING",)
|
538 |
+
RETURN_NAMES = ("positive", "negative", )
|
539 |
+
FUNCTION = "apply_controlnet"
|
540 |
+
CATEGORY = "InstantID"
|
541 |
+
|
542 |
+
def apply_controlnet(self, face_embeds, control_net, image_kps, positive, negative, strength, start_at, end_at, mask=None):
|
543 |
+
self.device = comfy.model_management.get_torch_device()
|
544 |
+
|
545 |
+
if strength == 0:
|
546 |
+
return (positive, negative)
|
547 |
+
|
548 |
+
if mask is not None:
|
549 |
+
mask = mask.to(self.device)
|
550 |
+
|
551 |
+
if mask is not None and len(mask.shape) < 3:
|
552 |
+
mask = mask.unsqueeze(0)
|
553 |
+
|
554 |
+
image_prompt_embeds = face_embeds['cond']
|
555 |
+
uncond_image_prompt_embeds = face_embeds['uncond']
|
556 |
+
|
557 |
+
cnets = {}
|
558 |
+
cond_uncond = []
|
559 |
+
control_hint = image_kps.movedim(-1,1)
|
560 |
+
|
561 |
+
is_cond = True
|
562 |
+
for conditioning in [positive, negative]:
|
563 |
+
c = []
|
564 |
+
for t in conditioning:
|
565 |
+
d = t[1].copy()
|
566 |
+
|
567 |
+
prev_cnet = d.get('control', None)
|
568 |
+
if prev_cnet in cnets:
|
569 |
+
c_net = cnets[prev_cnet]
|
570 |
+
else:
|
571 |
+
c_net = control_net.copy().set_cond_hint(control_hint, strength, (start_at, end_at))
|
572 |
+
c_net.set_previous_controlnet(prev_cnet)
|
573 |
+
cnets[prev_cnet] = c_net
|
574 |
+
|
575 |
+
d['control'] = c_net
|
576 |
+
d['control_apply_to_uncond'] = False
|
577 |
+
d['cross_attn_controlnet'] = image_prompt_embeds.to(comfy.model_management.intermediate_device()) if is_cond else uncond_image_prompt_embeds.to(comfy.model_management.intermediate_device())
|
578 |
+
|
579 |
+
if mask is not None and is_cond:
|
580 |
+
d['mask'] = mask
|
581 |
+
d['set_area_to_bounds'] = False
|
582 |
+
|
583 |
+
n = [t[0], d]
|
584 |
+
c.append(n)
|
585 |
+
cond_uncond.append(c)
|
586 |
+
is_cond = False
|
587 |
+
|
588 |
+
return(cond_uncond[0], cond_uncond[1])
|
589 |
+
|
590 |
+
|
591 |
+
NODE_CLASS_MAPPINGS = {
|
592 |
+
"InstantIDModelLoader": InstantIDModelLoader,
|
593 |
+
"InstantIDFaceAnalysis": InstantIDFaceAnalysis,
|
594 |
+
"ApplyInstantID": ApplyInstantID,
|
595 |
+
"ApplyInstantIDAdvanced": ApplyInstantIDAdvanced,
|
596 |
+
"FaceKeypointsPreprocessor": FaceKeypointsPreprocessor,
|
597 |
+
|
598 |
+
"InstantIDAttentionPatch": InstantIDAttentionPatch,
|
599 |
+
"ApplyInstantIDControlNet": ApplyInstantIDControlNet,
|
600 |
+
}
|
601 |
+
|
602 |
+
NODE_DISPLAY_NAME_MAPPINGS = {
|
603 |
+
"InstantIDModelLoader": "Load InstantID Model",
|
604 |
+
"InstantIDFaceAnalysis": "InstantID Face Analysis",
|
605 |
+
"ApplyInstantID": "Apply InstantID",
|
606 |
+
"ApplyInstantIDAdvanced": "Apply InstantID Advanced",
|
607 |
+
"FaceKeypointsPreprocessor": "Face Keypoints Preprocessor",
|
608 |
+
|
609 |
+
"InstantIDAttentionPatch": "InstantID Patch Attention",
|
610 |
+
"ApplyInstantIDControlNet": "InstantID Apply ControlNet",
|
611 |
+
}
|
ComfyUI_InstantID/LICENSE
ADDED
@@ -0,0 +1,201 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
Apache License
|
2 |
+
Version 2.0, January 2004
|
3 |
+
http://www.apache.org/licenses/
|
4 |
+
|
5 |
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
6 |
+
|
7 |
+
1. Definitions.
|
8 |
+
|
9 |
+
"License" shall mean the terms and conditions for use, reproduction,
|
10 |
+
and distribution as defined by Sections 1 through 9 of this document.
|
11 |
+
|
12 |
+
"Licensor" shall mean the copyright owner or entity authorized by
|
13 |
+
the copyright owner that is granting the License.
|
14 |
+
|
15 |
+
"Legal Entity" shall mean the union of the acting entity and all
|
16 |
+
other entities that control, are controlled by, or are under common
|
17 |
+
control with that entity. For the purposes of this definition,
|
18 |
+
"control" means (i) the power, direct or indirect, to cause the
|
19 |
+
direction or management of such entity, whether by contract or
|
20 |
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
21 |
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
22 |
+
|
23 |
+
"You" (or "Your") shall mean an individual or Legal Entity
|
24 |
+
exercising permissions granted by this License.
|
25 |
+
|
26 |
+
"Source" form shall mean the preferred form for making modifications,
|
27 |
+
including but not limited to software source code, documentation
|
28 |
+
source, and configuration files.
|
29 |
+
|
30 |
+
"Object" form shall mean any form resulting from mechanical
|
31 |
+
transformation or translation of a Source form, including but
|
32 |
+
not limited to compiled object code, generated documentation,
|
33 |
+
and conversions to other media types.
|
34 |
+
|
35 |
+
"Work" shall mean the work of authorship, whether in Source or
|
36 |
+
Object form, made available under the License, as indicated by a
|
37 |
+
copyright notice that is included in or attached to the work
|
38 |
+
(an example is provided in the Appendix below).
|
39 |
+
|
40 |
+
"Derivative Works" shall mean any work, whether in Source or Object
|
41 |
+
form, that is based on (or derived from) the Work and for which the
|
42 |
+
editorial revisions, annotations, elaborations, or other modifications
|
43 |
+
represent, as a whole, an original work of authorship. For the purposes
|
44 |
+
of this License, Derivative Works shall not include works that remain
|
45 |
+
separable from, or merely link (or bind by name) to the interfaces of,
|
46 |
+
the Work and Derivative Works thereof.
|
47 |
+
|
48 |
+
"Contribution" shall mean any work of authorship, including
|
49 |
+
the original version of the Work and any modifications or additions
|
50 |
+
to that Work or Derivative Works thereof, that is intentionally
|
51 |
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
52 |
+
or by an individual or Legal Entity authorized to submit on behalf of
|
53 |
+
the copyright owner. For the purposes of this definition, "submitted"
|
54 |
+
means any form of electronic, verbal, or written communication sent
|
55 |
+
to the Licensor or its representatives, including but not limited to
|
56 |
+
communication on electronic mailing lists, source code control systems,
|
57 |
+
and issue tracking systems that are managed by, or on behalf of, the
|
58 |
+
Licensor for the purpose of discussing and improving the Work, but
|
59 |
+
excluding communication that is conspicuously marked or otherwise
|
60 |
+
designated in writing by the copyright owner as "Not a Contribution."
|
61 |
+
|
62 |
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
63 |
+
on behalf of whom a Contribution has been received by Licensor and
|
64 |
+
subsequently incorporated within the Work.
|
65 |
+
|
66 |
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
67 |
+
this License, each Contributor hereby grants to You a perpetual,
|
68 |
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
69 |
+
copyright license to reproduce, prepare Derivative Works of,
|
70 |
+
publicly display, publicly perform, sublicense, and distribute the
|
71 |
+
Work and such Derivative Works in Source or Object form.
|
72 |
+
|
73 |
+
3. Grant of Patent License. Subject to the terms and conditions of
|
74 |
+
this License, each Contributor hereby grants to You a perpetual,
|
75 |
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
76 |
+
(except as stated in this section) patent license to make, have made,
|
77 |
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
78 |
+
where such license applies only to those patent claims licensable
|
79 |
+
by such Contributor that are necessarily infringed by their
|
80 |
+
Contribution(s) alone or by combination of their Contribution(s)
|
81 |
+
with the Work to which such Contribution(s) was submitted. If You
|
82 |
+
institute patent litigation against any entity (including a
|
83 |
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
84 |
+
or a Contribution incorporated within the Work constitutes direct
|
85 |
+
or contributory patent infringement, then any patent licenses
|
86 |
+
granted to You under this License for that Work shall terminate
|
87 |
+
as of the date such litigation is filed.
|
88 |
+
|
89 |
+
4. Redistribution. You may reproduce and distribute copies of the
|
90 |
+
Work or Derivative Works thereof in any medium, with or without
|
91 |
+
modifications, and in Source or Object form, provided that You
|
92 |
+
meet the following conditions:
|
93 |
+
|
94 |
+
(a) You must give any other recipients of the Work or
|
95 |
+
Derivative Works a copy of this License; and
|
96 |
+
|
97 |
+
(b) You must cause any modified files to carry prominent notices
|
98 |
+
stating that You changed the files; and
|
99 |
+
|
100 |
+
(c) You must retain, in the Source form of any Derivative Works
|
101 |
+
that You distribute, all copyright, patent, trademark, and
|
102 |
+
attribution notices from the Source form of the Work,
|
103 |
+
excluding those notices that do not pertain to any part of
|
104 |
+
the Derivative Works; and
|
105 |
+
|
106 |
+
(d) If the Work includes a "NOTICE" text file as part of its
|
107 |
+
distribution, then any Derivative Works that You distribute must
|
108 |
+
include a readable copy of the attribution notices contained
|
109 |
+
within such NOTICE file, excluding those notices that do not
|
110 |
+
pertain to any part of the Derivative Works, in at least one
|
111 |
+
of the following places: within a NOTICE text file distributed
|
112 |
+
as part of the Derivative Works; within the Source form or
|
113 |
+
documentation, if provided along with the Derivative Works; or,
|
114 |
+
within a display generated by the Derivative Works, if and
|
115 |
+
wherever such third-party notices normally appear. The contents
|
116 |
+
of the NOTICE file are for informational purposes only and
|
117 |
+
do not modify the License. You may add Your own attribution
|
118 |
+
notices within Derivative Works that You distribute, alongside
|
119 |
+
or as an addendum to the NOTICE text from the Work, provided
|
120 |
+
that such additional attribution notices cannot be construed
|
121 |
+
as modifying the License.
|
122 |
+
|
123 |
+
You may add Your own copyright statement to Your modifications and
|
124 |
+
may provide additional or different license terms and conditions
|
125 |
+
for use, reproduction, or distribution of Your modifications, or
|
126 |
+
for any such Derivative Works as a whole, provided Your use,
|
127 |
+
reproduction, and distribution of the Work otherwise complies with
|
128 |
+
the conditions stated in this License.
|
129 |
+
|
130 |
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
131 |
+
any Contribution intentionally submitted for inclusion in the Work
|
132 |
+
by You to the Licensor shall be under the terms and conditions of
|
133 |
+
this License, without any additional terms or conditions.
|
134 |
+
Notwithstanding the above, nothing herein shall supersede or modify
|
135 |
+
the terms of any separate license agreement you may have executed
|
136 |
+
with Licensor regarding such Contributions.
|
137 |
+
|
138 |
+
6. Trademarks. This License does not grant permission to use the trade
|
139 |
+
names, trademarks, service marks, or product names of the Licensor,
|
140 |
+
except as required for reasonable and customary use in describing the
|
141 |
+
origin of the Work and reproducing the content of the NOTICE file.
|
142 |
+
|
143 |
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
144 |
+
agreed to in writing, Licensor provides the Work (and each
|
145 |
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
146 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
147 |
+
implied, including, without limitation, any warranties or conditions
|
148 |
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
149 |
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
150 |
+
appropriateness of using or redistributing the Work and assume any
|
151 |
+
risks associated with Your exercise of permissions under this License.
|
152 |
+
|
153 |
+
8. Limitation of Liability. In no event and under no legal theory,
|
154 |
+
whether in tort (including negligence), contract, or otherwise,
|
155 |
+
unless required by applicable law (such as deliberate and grossly
|
156 |
+
negligent acts) or agreed to in writing, shall any Contributor be
|
157 |
+
liable to You for damages, including any direct, indirect, special,
|
158 |
+
incidental, or consequential damages of any character arising as a
|
159 |
+
result of this License or out of the use or inability to use the
|
160 |
+
Work (including but not limited to damages for loss of goodwill,
|
161 |
+
work stoppage, computer failure or malfunction, or any and all
|
162 |
+
other commercial damages or losses), even if such Contributor
|
163 |
+
has been advised of the possibility of such damages.
|
164 |
+
|
165 |
+
9. Accepting Warranty or Additional Liability. While redistributing
|
166 |
+
the Work or Derivative Works thereof, You may choose to offer,
|
167 |
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
168 |
+
or other liability obligations and/or rights consistent with this
|
169 |
+
License. However, in accepting such obligations, You may act only
|
170 |
+
on Your own behalf and on Your sole responsibility, not on behalf
|
171 |
+
of any other Contributor, and only if You agree to indemnify,
|
172 |
+
defend, and hold each Contributor harmless for any liability
|
173 |
+
incurred by, or claims asserted against, such Contributor by reason
|
174 |
+
of your accepting any such warranty or additional liability.
|
175 |
+
|
176 |
+
END OF TERMS AND CONDITIONS
|
177 |
+
|
178 |
+
APPENDIX: How to apply the Apache License to your work.
|
179 |
+
|
180 |
+
To apply the Apache License to your work, attach the following
|
181 |
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
182 |
+
replaced with your own identifying information. (Don't include
|
183 |
+
the brackets!) The text should be enclosed in the appropriate
|
184 |
+
comment syntax for the file format. We also recommend that a
|
185 |
+
file or class name and description of purpose be included on the
|
186 |
+
same "printed page" as the copyright notice for easier
|
187 |
+
identification within third-party archives.
|
188 |
+
|
189 |
+
Copyright [yyyy] [name of copyright owner]
|
190 |
+
|
191 |
+
Licensed under the Apache License, Version 2.0 (the "License");
|
192 |
+
you may not use this file except in compliance with the License.
|
193 |
+
You may obtain a copy of the License at
|
194 |
+
|
195 |
+
http://www.apache.org/licenses/LICENSE-2.0
|
196 |
+
|
197 |
+
Unless required by applicable law or agreed to in writing, software
|
198 |
+
distributed under the License is distributed on an "AS IS" BASIS,
|
199 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
200 |
+
See the License for the specific language governing permissions and
|
201 |
+
limitations under the License.
|
ComfyUI_InstantID/README.md
ADDED
@@ -0,0 +1,141 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# ComfyUI InstantID (Native Support)
|
2 |
+
|
3 |
+
## Translations
|
4 |
+
- [简体中文 (Simplified Chinese)](./README.zh-CN.md)
|
5 |
+
|
6 |
+
Native [InstantID](https://github.com/InstantID/InstantID) support for [ComfyUI](https://github.com/comfyanonymous/ComfyUI).
|
7 |
+
|
8 |
+
This extension differs from the many already available as it doesn't use *diffusers* but instead implements InstantID natively and it fully integrates with ComfyUI.
|
9 |
+
|
10 |
+
# Sponsorship
|
11 |
+
|
12 |
+
<div align="center">
|
13 |
+
|
14 |
+
**[:heart: Github Sponsor](https://github.com/sponsors/cubiq) | [:coin: Paypal](https://paypal.me/matt3o)**
|
15 |
+
|
16 |
+
</div>
|
17 |
+
|
18 |
+
If you like my work and wish to see updates and new features please consider sponsoring my projects.
|
19 |
+
|
20 |
+
- [ComfyUI IPAdapter Plus](https://github.com/cubiq/ComfyUI_IPAdapter_plus)
|
21 |
+
- [ComfyUI InstantID (Native)](https://github.com/cubiq/ComfyUI_InstantID)
|
22 |
+
- [ComfyUI Essentials](https://github.com/cubiq/ComfyUI_essentials)
|
23 |
+
- [ComfyUI FaceAnalysis](https://github.com/cubiq/ComfyUI_FaceAnalysis)
|
24 |
+
|
25 |
+
Not to mention the documentation and videos tutorials. Check my **ComfyUI Advanced Understanding** videos on YouTube for example, [part 1](https://www.youtube.com/watch?v=_C7kR2TFIX0) and [part 2](https://www.youtube.com/watch?v=ijqXnW_9gzc)
|
26 |
+
|
27 |
+
The only way to keep the code open and free is by sponsoring its development. The more sponsorships the more time I can dedicate to my open source projects.
|
28 |
+
|
29 |
+
Please consider a [Github Sponsorship](https://github.com/sponsors/cubiq) or [PayPal donation](https://paypal.me/matt3o) (Matteo "matt3o" Spinelli). For sponsorships of $50+, let me know if you'd like to be mentioned in this readme file, you can find me on [Discord](https://latent.vision/discord) or _matt3o :snail: gmail.com_.
|
30 |
+
|
31 |
+
## Important updates
|
32 |
+
|
33 |
+
- **2024/02/27:** Added [noise injection](#noise-injection) in the negative embeds.
|
34 |
+
|
35 |
+
- **2024/02/26:** Fixed a small but nasty bug. Results will be different and you may need to lower the CFG.
|
36 |
+
|
37 |
+
- **2024/02/20:** I refactored the nodes so they are hopefully easier to use. **This is a breaking update**, the previous workflows won't work anymore.
|
38 |
+
|
39 |
+
## Basic Workflow
|
40 |
+
|
41 |
+
In the `examples` directory you'll find some basic workflows.
|
42 |
+
|
43 |
+
![workflow](examples/instantid_basic_workflow.jpg)
|
44 |
+
|
45 |
+
## Video Tutorial
|
46 |
+
|
47 |
+
<a href="https://youtu.be/wMLiGhogOPE" target="_blank">
|
48 |
+
<img src="https://img.youtube.com/vi/wMLiGhogOPE/hqdefault.jpg" alt="Watch the video" />
|
49 |
+
</a>
|
50 |
+
|
51 |
+
** :movie_camera: [Introduction to InstantID features](https://youtu.be/wMLiGhogOPE)**
|
52 |
+
|
53 |
+
## Installation
|
54 |
+
|
55 |
+
**Upgrade ComfyUI to the latest version!**
|
56 |
+
|
57 |
+
Download or `git clone` this repository into the `ComfyUI/custom_nodes/` directory or use the Manager.
|
58 |
+
|
59 |
+
InstantID requires `insightface`, you need to add it to your libraries together with `onnxruntime` and `onnxruntime-gpu`.
|
60 |
+
|
61 |
+
The InsightFace model is **antelopev2** (not the classic buffalo_l). Download the models (for example from [here](https://drive.google.com/file/d/18wEUfMNohBJ4K3Ly5wpTejPfDzp-8fI8/view?usp=sharing) or [here](https://huggingface.co/MonsterMMORPG/tools/tree/main)), unzip and place them in the `ComfyUI/models/insightface/models/antelopev2` directory.
|
62 |
+
|
63 |
+
The **main model** can be downloaded from [HuggingFace](https://huggingface.co/InstantX/InstantID/resolve/main/ip-adapter.bin?download=true) and should be placed into the `ComfyUI/models/instantid` directory. (Note that the model is called *ip_adapter* as it is based on the [IPAdapter](https://github.com/tencent-ailab/IP-Adapter)).
|
64 |
+
|
65 |
+
You also needs a [controlnet](https://huggingface.co/InstantX/InstantID/resolve/main/ControlNetModel/diffusion_pytorch_model.safetensors?download=true), place it in the ComfyUI controlnet directory.
|
66 |
+
|
67 |
+
**Remember at the moment this is only for SDXL.**
|
68 |
+
|
69 |
+
## Watermarks!
|
70 |
+
|
71 |
+
The training data is full of watermarks, to avoid them to show up in your generations use a resolution slightly different from 1024×1024 (or the standard ones) for example **1016×1016** works pretty well.
|
72 |
+
|
73 |
+
## Lower the CFG!
|
74 |
+
|
75 |
+
It's important to lower the CFG to at least 4/5 or you can use the `RescaleCFG` node.
|
76 |
+
|
77 |
+
## Face keypoints
|
78 |
+
|
79 |
+
The person is posed based on the keypoints generated from the reference image. You can use a different pose by sending an image to the `image_kps` input.
|
80 |
+
|
81 |
+
<img src="examples/daydreaming.jpg" width="386" height="386" alt="Day Dreaming" />
|
82 |
+
|
83 |
+
## Noise Injection
|
84 |
+
|
85 |
+
The default InstantID implementation seems to really burn the image, I find that by injecting noise to the negative embeds we can mitigate the effect and also increase the likeliness to the reference. The default Apply InstantID node automatically injects 35% noise, if you want to fine tune the effect you can use the Advanced InstantID node.
|
86 |
+
|
87 |
+
This is still experimental and may change in the future.
|
88 |
+
|
89 |
+
## Additional Controlnets
|
90 |
+
|
91 |
+
You can add more controlnets to the generation. An example workflow for depth controlnet is provided.
|
92 |
+
|
93 |
+
## Styling with IPAdapter
|
94 |
+
|
95 |
+
It's possible to style the composition with IPAdapter. An example is provided.
|
96 |
+
|
97 |
+
<img src="examples/instant_id_ipadapter.jpg" width="512" alt="IPAdapter" />
|
98 |
+
|
99 |
+
## Multi-ID
|
100 |
+
|
101 |
+
Multi-ID is supported but the workflow is a bit complicated and the generation slower. I'll check if I can find a better way of doing it. The "hackish" workflow is provided in the example directory.
|
102 |
+
|
103 |
+
<img src="examples/instantid_multi_id.jpg" width="768" alt="IPAdapter" />
|
104 |
+
|
105 |
+
## Advanced Node
|
106 |
+
|
107 |
+
There's an InstantID advanced node available, at the moment the only difference with the standard one is that you can set the weights for the instantID models and the controlnet separately. It now also includes a noise injection option. It might be helpful for finetuning.
|
108 |
+
|
109 |
+
The instantID model influences the composition of about 25%, the rest is the controlnet.
|
110 |
+
|
111 |
+
The noise helps reducing the "burn" effect.
|
112 |
+
|
113 |
+
## Other notes
|
114 |
+
|
115 |
+
It works very well with SDXL Turbo/Lighting. Best results with community's checkpoints.
|
116 |
+
|
117 |
+
|
118 |
+
## Current sponsors
|
119 |
+
|
120 |
+
It's only thanks to generous sponsors that **the whole community** can enjoy open and free software. Please join me in thanking the following companies and individuals!
|
121 |
+
|
122 |
+
### :trophy: Gold sponsors
|
123 |
+
|
124 |
+
[![Kaiber.ai](https://f.latent.vision/imgs/kaiber.png)](https://kaiber.ai/) [![InstaSD](https://f.latent.vision/imgs/instasd.png)](https://www.instasd.com/)
|
125 |
+
|
126 |
+
### :tada: Silver sponsors
|
127 |
+
|
128 |
+
[![OperArt.ai](https://f.latent.vision/imgs/openart.png?r=1)](https://openart.ai/workflows) [![Finetuners](https://f.latent.vision/imgs/finetuners.png)](https://www.finetuners.ai/) [![Comfy.ICU](https://f.latent.vision/imgs/comfyicu.png?r=1)](https://comfy.icu/)
|
129 |
+
|
130 |
+
### Other companies supporting my projects
|
131 |
+
|
132 |
+
- [RunComfy](https://www.runcomfy.com/) (ComfyUI Cloud)
|
133 |
+
|
134 |
+
### Esteemed individuals
|
135 |
+
|
136 |
+
- [Øystein Ø. Olsen](https://github.com/FireNeslo)
|
137 |
+
- [Jack Gane](https://github.com/ganeJackS)
|
138 |
+
- [Nathan Shipley](https://www.nathanshipley.com/)
|
139 |
+
- [Dkdnzia](https://github.com/Dkdnzia)
|
140 |
+
|
141 |
+
[And all my public and private sponsors!](https://github.com/sponsors/cubiq)
|
ComfyUI_InstantID/README.zh-CN.md
ADDED
@@ -0,0 +1,137 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# ComfyUI InstantID (原生支持)
|
2 |
+
|
3 |
+
[InstantID](https://github.com/InstantID/InstantID) 的原生 [ComfyUI](https://github.com/comfyanonymous/ComfyUI) 支持。
|
4 |
+
|
5 |
+
此扩展不同于许多已可用的扩展,因为它不使用 *diffusers*,而是原生实现了 InstantID,并且与 ComfyUI 完全集成。
|
6 |
+
|
7 |
+
# 赞助
|
8 |
+
|
9 |
+
<div align="center">
|
10 |
+
|
11 |
+
**[:heart: Github 赞助](https://github.com/sponsors/cubiq) | [:coin: Paypal](https://paypal.me/matt3o)**
|
12 |
+
|
13 |
+
</div>
|
14 |
+
|
15 |
+
如果您喜欢我的工作并希望看到更新和新功能,请考虑赞助我的项目。
|
16 |
+
|
17 |
+
- [ComfyUI IPAdapter Plus](https://github.com/cubiq/ComfyUI_IPAdapter_plus)
|
18 |
+
- [ComfyUI InstantID (原生)](https://github.com/cubiq/ComfyUI_InstantID)
|
19 |
+
- [ComfyUI Essentials](https://github.com/cubiq/ComfyUI_essentials)
|
20 |
+
- [ComfyUI FaceAnalysis](https://github.com/cubiq/ComfyUI_FaceAnalysis)
|
21 |
+
|
22 |
+
更不用说文档和视频教程。可以查看我在 YouTube 上的 **ComfyUI 高级理解** 视频,例如 [第 1 部分](https://www.youtube.com/watch?v=_C7kR2TFIX0) 和 [第 2 部分](https://www.youtube.com/watch?v=ijqXnW_9gzc)。
|
23 |
+
|
24 |
+
保持代码开源和免费的唯一方法是通过赞助其开发。赞助越多,我就能投入更多时间在我的开源项目上。
|
25 |
+
|
26 |
+
请考虑 [Github 赞助](https://github.com/sponsors/cubiq) 或 [PayPal 捐赠](https://paypal.me/matt3o)(Matteo "matt3o" Spinelli)。对于赞助 $50+ 的人,请告诉我是否希望在此 README 文件中被提及,您可以在 [Discord](https://latent.vision/discord) 或通过 _matt3o :snail: gmail.com_ 联系我。
|
27 |
+
|
28 |
+
## 重要更新
|
29 |
+
|
30 |
+
- **2024/02/27:** 在负嵌入中添加了[噪声注入](#noise-injection)。
|
31 |
+
|
32 |
+
- **2024/02/26:** 修复了一个小但讨厌的错误。结果将有所不同,您可能需要降低 CFG。
|
33 |
+
|
34 |
+
- **2024/02/20:** 我重构了节点,希望它们更易于使用。**这是一次重大更新**,以前的工作流将不再可用。
|
35 |
+
|
36 |
+
## 基本工作流
|
37 |
+
|
38 |
+
在 `examples` 目录中,您会找到一些基本工作流。
|
39 |
+
|
40 |
+
![workflow](examples/instantid_basic_workflow.jpg)
|
41 |
+
|
42 |
+
## 视频教程
|
43 |
+
|
44 |
+
<a href="https://youtu.be/wMLiGhogOPE" target="_blank">
|
45 |
+
<img src="https://img.youtube.com/vi/wMLiGhogOPE/hqdefault.jpg" alt="观看视频" />
|
46 |
+
</a>
|
47 |
+
|
48 |
+
** :movie_camera: [InstantID 功能介绍](https://youtu.be/wMLiGhogOPE)**
|
49 |
+
|
50 |
+
## 安装
|
51 |
+
|
52 |
+
**将 ComfyUI 升级到最新版本!**
|
53 |
+
|
54 |
+
下载或 `git clone` 此仓库到 `ComfyUI/custom_nodes/` 目录或使用 Manager。
|
55 |
+
|
56 |
+
InstantID 需要 `insightface`,您需要将其添加到您的库中,连同 `onnxruntime` 和 `onnxruntime-gpu`。
|
57 |
+
|
58 |
+
InsightFace 模型是 **antelopev2**(不是经典的 buffalo_l)。下载模型(例如从 [这里](https://drive.google.com/file/d/18wEUfMNohBJ4K3Ly5wpTejPfDzp-8fI8/view?usp=sharing) 或 [这里](https://huggingface.co/MonsterMMORPG/tools/tree/main)),解压并将其放置在 `ComfyUI/models/insightface/models/antelopev2` 目录中。
|
59 |
+
|
60 |
+
**主模型**可以从 [HuggingFace](https://huggingface.co/InstantX/InstantID/resolve/main/ip-adapter.bin?download=true) 下载,应将其放置在 `ComfyUI/models/instantid` 目录中。(请注意,该模型称为 *ip_adapter*,因为它基于 [IPAdapter](https://github.com/tencent-ailab/IP-Adapter))。
|
61 |
+
|
62 |
+
您还需要一个 [controlnet](https://huggingface.co/InstantX/InstantID/resolve/main/ControlNetModel/diffusion_pytorch_model.safetensors?download=true),将其放置在 ComfyUI controlnet 目录中。
|
63 |
+
|
64 |
+
**请记住,目前这仅适用于 SDXL。**
|
65 |
+
|
66 |
+
## 水印!
|
67 |
+
|
68 |
+
训练数据中充满了水印,为避免水印出现在您的生成中,请使用与 1024×1024(或标准尺寸)略有不同的分辨率,例如 **1016×1016** 效果很好。
|
69 |
+
|
70 |
+
## 降低 CFG!
|
71 |
+
|
72 |
+
重要的是将 CFG 降低到至少 4/5,或者您可以使用 `RescaleCFG` 节点。
|
73 |
+
|
74 |
+
## 面部关键点
|
75 |
+
|
76 |
+
人物的姿势是基于从参考图像生成的关键点。您可以通过向 `image_kps` 输入发送图像来使用不同的姿势。
|
77 |
+
|
78 |
+
<img src="examples/daydreaming.jpg" width="386" height="386" alt="白日梦" />
|
79 |
+
|
80 |
+
## 噪声注入
|
81 |
+
|
82 |
+
默认的 InstantID 实现似乎真的“烧坏”了图像,我发现通过向负嵌入中注入噪声,我们可以缓解这一效果,并增加与参考的相似性。默认的 Apply InstantID 节点自动注入 35% 的噪声,如果您想微调效果,可以使用 Advanced InstantID 节点。
|
83 |
+
|
84 |
+
这仍然是实验性的,可能会在未来发生变化。
|
85 |
+
|
86 |
+
## 额外的 Controlnets
|
87 |
+
|
88 |
+
您可以向生成中添加更多 controlnets。提供了一个用于深度 controlnet 的示例工作流。
|
89 |
+
|
90 |
+
## 使用 IPAdapter 进行样式化
|
91 |
+
|
92 |
+
可以使用 IPAdapter 对构图进行样式化。提供了一个示例。
|
93 |
+
|
94 |
+
<img src="examples/instant_id_ipadapter.jpg" width="512" alt="IPAdapter" />
|
95 |
+
|
96 |
+
## 多-ID 支持
|
97 |
+
|
98 |
+
支持多 ID,但工作流有点复杂,生成速度较慢。我会检查是否可以找到更好的方法。示例工作流在 examples 目录中提供。
|
99 |
+
|
100 |
+
<img src="examples/instantid_multi_id.jpg" width="768" alt="IPAdapter" />
|
101 |
+
|
102 |
+
## 高级节点
|
103 |
+
|
104 |
+
目前有一个高级的 InstantID 节点,当前与标准节点的唯一区别是您可以分别设置 instantID 模型和 controlnet 的权重。它现在还包括一个噪声注入选项。对于微调可能很有帮助。
|
105 |
+
|
106 |
+
instantID 模型对构图的影响约为 25%,其余的是 controlnet。
|
107 |
+
|
108 |
+
噪声有助于减少“燃烧”效果。
|
109 |
+
|
110 |
+
## 其他注意事项
|
111 |
+
|
112 |
+
它与 SDXL Turbo/Lighting 非常兼容。使用社区的检查点效果最好。
|
113 |
+
|
114 |
+
## 当前赞助商
|
115 |
+
|
116 |
+
正是由于慷慨的赞助商,**整个社区**才能享受开源和免费软件。请与我一起感谢以下公司和个人!
|
117 |
+
|
118 |
+
### :trophy: 金牌赞助商
|
119 |
+
|
120 |
+
[![Kaiber.ai](https://f.latent.vision/imgs/kaiber.png)](https://kaiber.ai/) [![InstaSD](https://f.latent.vision/imgs/instasd.png)](https://www.instasd.com/)
|
121 |
+
|
122 |
+
### :tada: 银牌赞助商
|
123 |
+
|
124 |
+
[![OperArt.ai](https://f.latent.vision/imgs/openart.png?r=1)](https://openart.ai/workflows) [![Finetuners](https://f.latent.vision/imgs/finetuners.png)](https://www.finetuners.ai/) [![Comfy.ICU](https://f.latent.vision/imgs/comfyicu.png?r=1)](https://comfy.icu/)
|
125 |
+
|
126 |
+
### 其他支持我项目的公司
|
127 |
+
|
128 |
+
- [RunComfy](https://www.runcomfy.com/) (ComfyUI 云)
|
129 |
+
|
130 |
+
### 尊敬的个人
|
131 |
+
|
132 |
+
- [Øystein Ø. Olsen](https://github.com/FireNeslo)
|
133 |
+
- [Jack Gane](https://github.com/ganeJackS)
|
134 |
+
- [Nathan Shipley](https://www.nathanshipley.com/)
|
135 |
+
- [Dkdnzia](https://github.com/Dkdnzia)
|
136 |
+
|
137 |
+
[以及所有我的公开和私密赞助商!](https://github.com/sponsors/cubiq)
|
ComfyUI_InstantID/__init__.py
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
from .InstantID import NODE_CLASS_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS
|
2 |
+
|
3 |
+
__all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS']
|
ComfyUI_InstantID/__pycache__/CrossAttentionPatch.cpython-312.pyc
ADDED
Binary file (9.57 kB). View file
|
|
ComfyUI_InstantID/__pycache__/InstantID.cpython-312.pyc
ADDED
Binary file (29.1 kB). View file
|
|
ComfyUI_InstantID/__pycache__/__init__.cpython-312.pyc
ADDED
Binary file (327 Bytes). View file
|
|
ComfyUI_InstantID/__pycache__/resampler.cpython-312.pyc
ADDED
Binary file (5.81 kB). View file
|
|
ComfyUI_InstantID/__pycache__/utils.cpython-312.pyc
ADDED
Binary file (1.66 kB). View file
|
|
ComfyUI_InstantID/examples/InstantID_IPAdapter.json
ADDED
@@ -0,0 +1,861 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"last_node_id": 72,
|
3 |
+
"last_link_id": 231,
|
4 |
+
"nodes": [
|
5 |
+
{
|
6 |
+
"id": 11,
|
7 |
+
"type": "InstantIDModelLoader",
|
8 |
+
"pos": [
|
9 |
+
560,
|
10 |
+
70
|
11 |
+
],
|
12 |
+
"size": {
|
13 |
+
"0": 238.72393798828125,
|
14 |
+
"1": 58
|
15 |
+
},
|
16 |
+
"flags": {},
|
17 |
+
"order": 0,
|
18 |
+
"mode": 0,
|
19 |
+
"outputs": [
|
20 |
+
{
|
21 |
+
"name": "INSTANTID",
|
22 |
+
"type": "INSTANTID",
|
23 |
+
"links": [
|
24 |
+
197
|
25 |
+
],
|
26 |
+
"shape": 3,
|
27 |
+
"slot_index": 0
|
28 |
+
}
|
29 |
+
],
|
30 |
+
"properties": {
|
31 |
+
"Node name for S&R": "InstantIDModelLoader"
|
32 |
+
},
|
33 |
+
"widgets_values": [
|
34 |
+
"ip-adapter.bin"
|
35 |
+
]
|
36 |
+
},
|
37 |
+
{
|
38 |
+
"id": 38,
|
39 |
+
"type": "InstantIDFaceAnalysis",
|
40 |
+
"pos": [
|
41 |
+
570,
|
42 |
+
180
|
43 |
+
],
|
44 |
+
"size": {
|
45 |
+
"0": 227.09793090820312,
|
46 |
+
"1": 58
|
47 |
+
},
|
48 |
+
"flags": {},
|
49 |
+
"order": 1,
|
50 |
+
"mode": 0,
|
51 |
+
"outputs": [
|
52 |
+
{
|
53 |
+
"name": "FACEANALYSIS",
|
54 |
+
"type": "FACEANALYSIS",
|
55 |
+
"links": [
|
56 |
+
198
|
57 |
+
],
|
58 |
+
"shape": 3,
|
59 |
+
"slot_index": 0
|
60 |
+
}
|
61 |
+
],
|
62 |
+
"properties": {
|
63 |
+
"Node name for S&R": "InstantIDFaceAnalysis"
|
64 |
+
},
|
65 |
+
"widgets_values": [
|
66 |
+
"CPU"
|
67 |
+
]
|
68 |
+
},
|
69 |
+
{
|
70 |
+
"id": 16,
|
71 |
+
"type": "ControlNetLoader",
|
72 |
+
"pos": [
|
73 |
+
560,
|
74 |
+
290
|
75 |
+
],
|
76 |
+
"size": {
|
77 |
+
"0": 250.07241821289062,
|
78 |
+
"1": 58
|
79 |
+
},
|
80 |
+
"flags": {},
|
81 |
+
"order": 2,
|
82 |
+
"mode": 0,
|
83 |
+
"outputs": [
|
84 |
+
{
|
85 |
+
"name": "CONTROL_NET",
|
86 |
+
"type": "CONTROL_NET",
|
87 |
+
"links": [
|
88 |
+
199
|
89 |
+
],
|
90 |
+
"shape": 3,
|
91 |
+
"slot_index": 0
|
92 |
+
}
|
93 |
+
],
|
94 |
+
"properties": {
|
95 |
+
"Node name for S&R": "ControlNetLoader"
|
96 |
+
},
|
97 |
+
"widgets_values": [
|
98 |
+
"instantid/diffusion_pytorch_model.safetensors"
|
99 |
+
]
|
100 |
+
},
|
101 |
+
{
|
102 |
+
"id": 15,
|
103 |
+
"type": "PreviewImage",
|
104 |
+
"pos": [
|
105 |
+
1910,
|
106 |
+
290
|
107 |
+
],
|
108 |
+
"size": {
|
109 |
+
"0": 584.0855712890625,
|
110 |
+
"1": 610.4592895507812
|
111 |
+
},
|
112 |
+
"flags": {},
|
113 |
+
"order": 15,
|
114 |
+
"mode": 0,
|
115 |
+
"inputs": [
|
116 |
+
{
|
117 |
+
"name": "images",
|
118 |
+
"type": "IMAGE",
|
119 |
+
"link": 19
|
120 |
+
}
|
121 |
+
],
|
122 |
+
"properties": {
|
123 |
+
"Node name for S&R": "PreviewImage"
|
124 |
+
}
|
125 |
+
},
|
126 |
+
{
|
127 |
+
"id": 5,
|
128 |
+
"type": "EmptyLatentImage",
|
129 |
+
"pos": [
|
130 |
+
910,
|
131 |
+
540
|
132 |
+
],
|
133 |
+
"size": {
|
134 |
+
"0": 315,
|
135 |
+
"1": 106
|
136 |
+
},
|
137 |
+
"flags": {},
|
138 |
+
"order": 3,
|
139 |
+
"mode": 0,
|
140 |
+
"outputs": [
|
141 |
+
{
|
142 |
+
"name": "LATENT",
|
143 |
+
"type": "LATENT",
|
144 |
+
"links": [
|
145 |
+
2
|
146 |
+
],
|
147 |
+
"slot_index": 0
|
148 |
+
}
|
149 |
+
],
|
150 |
+
"properties": {
|
151 |
+
"Node name for S&R": "EmptyLatentImage"
|
152 |
+
},
|
153 |
+
"widgets_values": [
|
154 |
+
1016,
|
155 |
+
1016,
|
156 |
+
1
|
157 |
+
]
|
158 |
+
},
|
159 |
+
{
|
160 |
+
"id": 8,
|
161 |
+
"type": "VAEDecode",
|
162 |
+
"pos": [
|
163 |
+
1910,
|
164 |
+
200
|
165 |
+
],
|
166 |
+
"size": {
|
167 |
+
"0": 210,
|
168 |
+
"1": 46
|
169 |
+
},
|
170 |
+
"flags": {},
|
171 |
+
"order": 14,
|
172 |
+
"mode": 0,
|
173 |
+
"inputs": [
|
174 |
+
{
|
175 |
+
"name": "samples",
|
176 |
+
"type": "LATENT",
|
177 |
+
"link": 7
|
178 |
+
},
|
179 |
+
{
|
180 |
+
"name": "vae",
|
181 |
+
"type": "VAE",
|
182 |
+
"link": 8
|
183 |
+
}
|
184 |
+
],
|
185 |
+
"outputs": [
|
186 |
+
{
|
187 |
+
"name": "IMAGE",
|
188 |
+
"type": "IMAGE",
|
189 |
+
"links": [
|
190 |
+
19
|
191 |
+
],
|
192 |
+
"slot_index": 0
|
193 |
+
}
|
194 |
+
],
|
195 |
+
"properties": {
|
196 |
+
"Node name for S&R": "VAEDecode"
|
197 |
+
}
|
198 |
+
},
|
199 |
+
{
|
200 |
+
"id": 39,
|
201 |
+
"type": "CLIPTextEncode",
|
202 |
+
"pos": [
|
203 |
+
520,
|
204 |
+
430
|
205 |
+
],
|
206 |
+
"size": {
|
207 |
+
"0": 291.9967346191406,
|
208 |
+
"1": 128.62518310546875
|
209 |
+
},
|
210 |
+
"flags": {},
|
211 |
+
"order": 9,
|
212 |
+
"mode": 0,
|
213 |
+
"inputs": [
|
214 |
+
{
|
215 |
+
"name": "clip",
|
216 |
+
"type": "CLIP",
|
217 |
+
"link": 122
|
218 |
+
}
|
219 |
+
],
|
220 |
+
"outputs": [
|
221 |
+
{
|
222 |
+
"name": "CONDITIONING",
|
223 |
+
"type": "CONDITIONING",
|
224 |
+
"links": [
|
225 |
+
203
|
226 |
+
],
|
227 |
+
"shape": 3,
|
228 |
+
"slot_index": 0
|
229 |
+
}
|
230 |
+
],
|
231 |
+
"properties": {
|
232 |
+
"Node name for S&R": "CLIPTextEncode"
|
233 |
+
},
|
234 |
+
"widgets_values": [
|
235 |
+
"comic character. graphic illustration, comic art, graphic novel art, vibrant, highly detailed"
|
236 |
+
]
|
237 |
+
},
|
238 |
+
{
|
239 |
+
"id": 40,
|
240 |
+
"type": "CLIPTextEncode",
|
241 |
+
"pos": [
|
242 |
+
520,
|
243 |
+
620
|
244 |
+
],
|
245 |
+
"size": {
|
246 |
+
"0": 286.3603515625,
|
247 |
+
"1": 112.35245513916016
|
248 |
+
},
|
249 |
+
"flags": {},
|
250 |
+
"order": 10,
|
251 |
+
"mode": 0,
|
252 |
+
"inputs": [
|
253 |
+
{
|
254 |
+
"name": "clip",
|
255 |
+
"type": "CLIP",
|
256 |
+
"link": 123
|
257 |
+
}
|
258 |
+
],
|
259 |
+
"outputs": [
|
260 |
+
{
|
261 |
+
"name": "CONDITIONING",
|
262 |
+
"type": "CONDITIONING",
|
263 |
+
"links": [
|
264 |
+
204
|
265 |
+
],
|
266 |
+
"shape": 3,
|
267 |
+
"slot_index": 0
|
268 |
+
}
|
269 |
+
],
|
270 |
+
"properties": {
|
271 |
+
"Node name for S&R": "CLIPTextEncode"
|
272 |
+
},
|
273 |
+
"widgets_values": [
|
274 |
+
"photograph, deformed, glitch, noisy, realistic, stock photo"
|
275 |
+
]
|
276 |
+
},
|
277 |
+
{
|
278 |
+
"id": 4,
|
279 |
+
"type": "CheckpointLoaderSimple",
|
280 |
+
"pos": [
|
281 |
+
70,
|
282 |
+
520
|
283 |
+
],
|
284 |
+
"size": {
|
285 |
+
"0": 315,
|
286 |
+
"1": 98
|
287 |
+
},
|
288 |
+
"flags": {},
|
289 |
+
"order": 4,
|
290 |
+
"mode": 0,
|
291 |
+
"outputs": [
|
292 |
+
{
|
293 |
+
"name": "MODEL",
|
294 |
+
"type": "MODEL",
|
295 |
+
"links": [
|
296 |
+
206
|
297 |
+
],
|
298 |
+
"slot_index": 0
|
299 |
+
},
|
300 |
+
{
|
301 |
+
"name": "CLIP",
|
302 |
+
"type": "CLIP",
|
303 |
+
"links": [
|
304 |
+
122,
|
305 |
+
123
|
306 |
+
],
|
307 |
+
"slot_index": 1
|
308 |
+
},
|
309 |
+
{
|
310 |
+
"name": "VAE",
|
311 |
+
"type": "VAE",
|
312 |
+
"links": [
|
313 |
+
8
|
314 |
+
],
|
315 |
+
"slot_index": 2
|
316 |
+
}
|
317 |
+
],
|
318 |
+
"properties": {
|
319 |
+
"Node name for S&R": "CheckpointLoaderSimple"
|
320 |
+
},
|
321 |
+
"widgets_values": [
|
322 |
+
"sdxl/AlbedoBaseXL.safetensors"
|
323 |
+
]
|
324 |
+
},
|
325 |
+
{
|
326 |
+
"id": 13,
|
327 |
+
"type": "LoadImage",
|
328 |
+
"pos": [
|
329 |
+
290,
|
330 |
+
70
|
331 |
+
],
|
332 |
+
"size": {
|
333 |
+
"0": 210,
|
334 |
+
"1": 314
|
335 |
+
},
|
336 |
+
"flags": {},
|
337 |
+
"order": 5,
|
338 |
+
"mode": 0,
|
339 |
+
"outputs": [
|
340 |
+
{
|
341 |
+
"name": "IMAGE",
|
342 |
+
"type": "IMAGE",
|
343 |
+
"links": [
|
344 |
+
214
|
345 |
+
],
|
346 |
+
"shape": 3,
|
347 |
+
"slot_index": 0
|
348 |
+
},
|
349 |
+
{
|
350 |
+
"name": "MASK",
|
351 |
+
"type": "MASK",
|
352 |
+
"links": null,
|
353 |
+
"shape": 3
|
354 |
+
}
|
355 |
+
],
|
356 |
+
"properties": {
|
357 |
+
"Node name for S&R": "LoadImage"
|
358 |
+
},
|
359 |
+
"widgets_values": [
|
360 |
+
"joseph-gonzalez-iFgRcqHznqg-unsplash.jpg",
|
361 |
+
"image"
|
362 |
+
]
|
363 |
+
},
|
364 |
+
{
|
365 |
+
"id": 3,
|
366 |
+
"type": "KSampler",
|
367 |
+
"pos": [
|
368 |
+
1540,
|
369 |
+
200
|
370 |
+
],
|
371 |
+
"size": {
|
372 |
+
"0": 315,
|
373 |
+
"1": 262
|
374 |
+
},
|
375 |
+
"flags": {},
|
376 |
+
"order": 13,
|
377 |
+
"mode": 0,
|
378 |
+
"inputs": [
|
379 |
+
{
|
380 |
+
"name": "model",
|
381 |
+
"type": "MODEL",
|
382 |
+
"link": 231
|
383 |
+
},
|
384 |
+
{
|
385 |
+
"name": "positive",
|
386 |
+
"type": "CONDITIONING",
|
387 |
+
"link": 200
|
388 |
+
},
|
389 |
+
{
|
390 |
+
"name": "negative",
|
391 |
+
"type": "CONDITIONING",
|
392 |
+
"link": 201
|
393 |
+
},
|
394 |
+
{
|
395 |
+
"name": "latent_image",
|
396 |
+
"type": "LATENT",
|
397 |
+
"link": 2
|
398 |
+
}
|
399 |
+
],
|
400 |
+
"outputs": [
|
401 |
+
{
|
402 |
+
"name": "LATENT",
|
403 |
+
"type": "LATENT",
|
404 |
+
"links": [
|
405 |
+
7
|
406 |
+
],
|
407 |
+
"slot_index": 0
|
408 |
+
}
|
409 |
+
],
|
410 |
+
"properties": {
|
411 |
+
"Node name for S&R": "KSampler"
|
412 |
+
},
|
413 |
+
"widgets_values": [
|
414 |
+
1631591432,
|
415 |
+
"fixed",
|
416 |
+
30,
|
417 |
+
4.5,
|
418 |
+
"ddpm",
|
419 |
+
"karras",
|
420 |
+
1
|
421 |
+
]
|
422 |
+
},
|
423 |
+
{
|
424 |
+
"id": 68,
|
425 |
+
"type": "IPAdapterModelLoader",
|
426 |
+
"pos": [
|
427 |
+
830,
|
428 |
+
-500
|
429 |
+
],
|
430 |
+
"size": {
|
431 |
+
"0": 315,
|
432 |
+
"1": 58
|
433 |
+
},
|
434 |
+
"flags": {},
|
435 |
+
"order": 6,
|
436 |
+
"mode": 0,
|
437 |
+
"outputs": [
|
438 |
+
{
|
439 |
+
"name": "IPADAPTER",
|
440 |
+
"type": "IPADAPTER",
|
441 |
+
"links": [
|
442 |
+
227
|
443 |
+
],
|
444 |
+
"shape": 3,
|
445 |
+
"slot_index": 0
|
446 |
+
}
|
447 |
+
],
|
448 |
+
"properties": {
|
449 |
+
"Node name for S&R": "IPAdapterModelLoader"
|
450 |
+
},
|
451 |
+
"widgets_values": [
|
452 |
+
"ip-adapter-plus_sdxl_vit-h.safetensors"
|
453 |
+
]
|
454 |
+
},
|
455 |
+
{
|
456 |
+
"id": 60,
|
457 |
+
"type": "ApplyInstantID",
|
458 |
+
"pos": [
|
459 |
+
910,
|
460 |
+
210
|
461 |
+
],
|
462 |
+
"size": {
|
463 |
+
"0": 315,
|
464 |
+
"1": 266
|
465 |
+
},
|
466 |
+
"flags": {},
|
467 |
+
"order": 11,
|
468 |
+
"mode": 0,
|
469 |
+
"inputs": [
|
470 |
+
{
|
471 |
+
"name": "instantid",
|
472 |
+
"type": "INSTANTID",
|
473 |
+
"link": 197
|
474 |
+
},
|
475 |
+
{
|
476 |
+
"name": "insightface",
|
477 |
+
"type": "FACEANALYSIS",
|
478 |
+
"link": 198
|
479 |
+
},
|
480 |
+
{
|
481 |
+
"name": "control_net",
|
482 |
+
"type": "CONTROL_NET",
|
483 |
+
"link": 199
|
484 |
+
},
|
485 |
+
{
|
486 |
+
"name": "image",
|
487 |
+
"type": "IMAGE",
|
488 |
+
"link": 214
|
489 |
+
},
|
490 |
+
{
|
491 |
+
"name": "model",
|
492 |
+
"type": "MODEL",
|
493 |
+
"link": 206
|
494 |
+
},
|
495 |
+
{
|
496 |
+
"name": "positive",
|
497 |
+
"type": "CONDITIONING",
|
498 |
+
"link": 203
|
499 |
+
},
|
500 |
+
{
|
501 |
+
"name": "negative",
|
502 |
+
"type": "CONDITIONING",
|
503 |
+
"link": 204
|
504 |
+
},
|
505 |
+
{
|
506 |
+
"name": "image_kps",
|
507 |
+
"type": "IMAGE",
|
508 |
+
"link": null
|
509 |
+
},
|
510 |
+
{
|
511 |
+
"name": "mask",
|
512 |
+
"type": "MASK",
|
513 |
+
"link": null
|
514 |
+
}
|
515 |
+
],
|
516 |
+
"outputs": [
|
517 |
+
{
|
518 |
+
"name": "MODEL",
|
519 |
+
"type": "MODEL",
|
520 |
+
"links": [
|
521 |
+
230
|
522 |
+
],
|
523 |
+
"shape": 3,
|
524 |
+
"slot_index": 0
|
525 |
+
},
|
526 |
+
{
|
527 |
+
"name": "POSITIVE",
|
528 |
+
"type": "CONDITIONING",
|
529 |
+
"links": [
|
530 |
+
200
|
531 |
+
],
|
532 |
+
"shape": 3,
|
533 |
+
"slot_index": 1
|
534 |
+
},
|
535 |
+
{
|
536 |
+
"name": "NEGATIVE",
|
537 |
+
"type": "CONDITIONING",
|
538 |
+
"links": [
|
539 |
+
201
|
540 |
+
],
|
541 |
+
"shape": 3,
|
542 |
+
"slot_index": 2
|
543 |
+
}
|
544 |
+
],
|
545 |
+
"properties": {
|
546 |
+
"Node name for S&R": "ApplyInstantID"
|
547 |
+
},
|
548 |
+
"widgets_values": [
|
549 |
+
0.8,
|
550 |
+
0,
|
551 |
+
1
|
552 |
+
]
|
553 |
+
},
|
554 |
+
{
|
555 |
+
"id": 70,
|
556 |
+
"type": "CLIPVisionLoader",
|
557 |
+
"pos": [
|
558 |
+
830,
|
559 |
+
-390
|
560 |
+
],
|
561 |
+
"size": {
|
562 |
+
"0": 315,
|
563 |
+
"1": 58
|
564 |
+
},
|
565 |
+
"flags": {},
|
566 |
+
"order": 7,
|
567 |
+
"mode": 0,
|
568 |
+
"outputs": [
|
569 |
+
{
|
570 |
+
"name": "CLIP_VISION",
|
571 |
+
"type": "CLIP_VISION",
|
572 |
+
"links": [
|
573 |
+
228
|
574 |
+
],
|
575 |
+
"shape": 3,
|
576 |
+
"slot_index": 0
|
577 |
+
}
|
578 |
+
],
|
579 |
+
"properties": {
|
580 |
+
"Node name for S&R": "CLIPVisionLoader"
|
581 |
+
},
|
582 |
+
"widgets_values": [
|
583 |
+
"CLIP-ViT-H-14-laion2B-s32B-b79K.safetensors"
|
584 |
+
]
|
585 |
+
},
|
586 |
+
{
|
587 |
+
"id": 71,
|
588 |
+
"type": "LoadImage",
|
589 |
+
"pos": [
|
590 |
+
830,
|
591 |
+
-280
|
592 |
+
],
|
593 |
+
"size": {
|
594 |
+
"0": 315,
|
595 |
+
"1": 314
|
596 |
+
},
|
597 |
+
"flags": {},
|
598 |
+
"order": 8,
|
599 |
+
"mode": 0,
|
600 |
+
"outputs": [
|
601 |
+
{
|
602 |
+
"name": "IMAGE",
|
603 |
+
"type": "IMAGE",
|
604 |
+
"links": [
|
605 |
+
229
|
606 |
+
],
|
607 |
+
"shape": 3,
|
608 |
+
"slot_index": 0
|
609 |
+
},
|
610 |
+
{
|
611 |
+
"name": "MASK",
|
612 |
+
"type": "MASK",
|
613 |
+
"links": null,
|
614 |
+
"shape": 3
|
615 |
+
}
|
616 |
+
],
|
617 |
+
"properties": {
|
618 |
+
"Node name for S&R": "LoadImage"
|
619 |
+
},
|
620 |
+
"widgets_values": [
|
621 |
+
"anime_colorful.png",
|
622 |
+
"image"
|
623 |
+
]
|
624 |
+
},
|
625 |
+
{
|
626 |
+
"id": 72,
|
627 |
+
"type": "IPAdapterAdvanced",
|
628 |
+
"pos": [
|
629 |
+
1226,
|
630 |
+
-337
|
631 |
+
],
|
632 |
+
"size": {
|
633 |
+
"0": 315,
|
634 |
+
"1": 278
|
635 |
+
},
|
636 |
+
"flags": {},
|
637 |
+
"order": 12,
|
638 |
+
"mode": 0,
|
639 |
+
"inputs": [
|
640 |
+
{
|
641 |
+
"name": "model",
|
642 |
+
"type": "MODEL",
|
643 |
+
"link": 230
|
644 |
+
},
|
645 |
+
{
|
646 |
+
"name": "ipadapter",
|
647 |
+
"type": "IPADAPTER",
|
648 |
+
"link": 227
|
649 |
+
},
|
650 |
+
{
|
651 |
+
"name": "image",
|
652 |
+
"type": "IMAGE",
|
653 |
+
"link": 229
|
654 |
+
},
|
655 |
+
{
|
656 |
+
"name": "image_negative",
|
657 |
+
"type": "IMAGE",
|
658 |
+
"link": null
|
659 |
+
},
|
660 |
+
{
|
661 |
+
"name": "attn_mask",
|
662 |
+
"type": "MASK",
|
663 |
+
"link": null
|
664 |
+
},
|
665 |
+
{
|
666 |
+
"name": "clip_vision",
|
667 |
+
"type": "CLIP_VISION",
|
668 |
+
"link": 228
|
669 |
+
}
|
670 |
+
],
|
671 |
+
"outputs": [
|
672 |
+
{
|
673 |
+
"name": "MODEL",
|
674 |
+
"type": "MODEL",
|
675 |
+
"links": [
|
676 |
+
231
|
677 |
+
],
|
678 |
+
"shape": 3,
|
679 |
+
"slot_index": 0
|
680 |
+
}
|
681 |
+
],
|
682 |
+
"properties": {
|
683 |
+
"Node name for S&R": "IPAdapterAdvanced"
|
684 |
+
},
|
685 |
+
"widgets_values": [
|
686 |
+
0.5,
|
687 |
+
"linear",
|
688 |
+
"concat",
|
689 |
+
0,
|
690 |
+
1,
|
691 |
+
"V only"
|
692 |
+
]
|
693 |
+
}
|
694 |
+
],
|
695 |
+
"links": [
|
696 |
+
[
|
697 |
+
2,
|
698 |
+
5,
|
699 |
+
0,
|
700 |
+
3,
|
701 |
+
3,
|
702 |
+
"LATENT"
|
703 |
+
],
|
704 |
+
[
|
705 |
+
7,
|
706 |
+
3,
|
707 |
+
0,
|
708 |
+
8,
|
709 |
+
0,
|
710 |
+
"LATENT"
|
711 |
+
],
|
712 |
+
[
|
713 |
+
8,
|
714 |
+
4,
|
715 |
+
2,
|
716 |
+
8,
|
717 |
+
1,
|
718 |
+
"VAE"
|
719 |
+
],
|
720 |
+
[
|
721 |
+
19,
|
722 |
+
8,
|
723 |
+
0,
|
724 |
+
15,
|
725 |
+
0,
|
726 |
+
"IMAGE"
|
727 |
+
],
|
728 |
+
[
|
729 |
+
122,
|
730 |
+
4,
|
731 |
+
1,
|
732 |
+
39,
|
733 |
+
0,
|
734 |
+
"CLIP"
|
735 |
+
],
|
736 |
+
[
|
737 |
+
123,
|
738 |
+
4,
|
739 |
+
1,
|
740 |
+
40,
|
741 |
+
0,
|
742 |
+
"CLIP"
|
743 |
+
],
|
744 |
+
[
|
745 |
+
197,
|
746 |
+
11,
|
747 |
+
0,
|
748 |
+
60,
|
749 |
+
0,
|
750 |
+
"INSTANTID"
|
751 |
+
],
|
752 |
+
[
|
753 |
+
198,
|
754 |
+
38,
|
755 |
+
0,
|
756 |
+
60,
|
757 |
+
1,
|
758 |
+
"FACEANALYSIS"
|
759 |
+
],
|
760 |
+
[
|
761 |
+
199,
|
762 |
+
16,
|
763 |
+
0,
|
764 |
+
60,
|
765 |
+
2,
|
766 |
+
"CONTROL_NET"
|
767 |
+
],
|
768 |
+
[
|
769 |
+
200,
|
770 |
+
60,
|
771 |
+
1,
|
772 |
+
3,
|
773 |
+
1,
|
774 |
+
"CONDITIONING"
|
775 |
+
],
|
776 |
+
[
|
777 |
+
201,
|
778 |
+
60,
|
779 |
+
2,
|
780 |
+
3,
|
781 |
+
2,
|
782 |
+
"CONDITIONING"
|
783 |
+
],
|
784 |
+
[
|
785 |
+
203,
|
786 |
+
39,
|
787 |
+
0,
|
788 |
+
60,
|
789 |
+
5,
|
790 |
+
"CONDITIONING"
|
791 |
+
],
|
792 |
+
[
|
793 |
+
204,
|
794 |
+
40,
|
795 |
+
0,
|
796 |
+
60,
|
797 |
+
6,
|
798 |
+
"CONDITIONING"
|
799 |
+
],
|
800 |
+
[
|
801 |
+
206,
|
802 |
+
4,
|
803 |
+
0,
|
804 |
+
60,
|
805 |
+
4,
|
806 |
+
"MODEL"
|
807 |
+
],
|
808 |
+
[
|
809 |
+
214,
|
810 |
+
13,
|
811 |
+
0,
|
812 |
+
60,
|
813 |
+
3,
|
814 |
+
"IMAGE"
|
815 |
+
],
|
816 |
+
[
|
817 |
+
227,
|
818 |
+
68,
|
819 |
+
0,
|
820 |
+
72,
|
821 |
+
1,
|
822 |
+
"IPADAPTER"
|
823 |
+
],
|
824 |
+
[
|
825 |
+
228,
|
826 |
+
70,
|
827 |
+
0,
|
828 |
+
72,
|
829 |
+
5,
|
830 |
+
"CLIP_VISION"
|
831 |
+
],
|
832 |
+
[
|
833 |
+
229,
|
834 |
+
71,
|
835 |
+
0,
|
836 |
+
72,
|
837 |
+
2,
|
838 |
+
"IMAGE"
|
839 |
+
],
|
840 |
+
[
|
841 |
+
230,
|
842 |
+
60,
|
843 |
+
0,
|
844 |
+
72,
|
845 |
+
0,
|
846 |
+
"MODEL"
|
847 |
+
],
|
848 |
+
[
|
849 |
+
231,
|
850 |
+
72,
|
851 |
+
0,
|
852 |
+
3,
|
853 |
+
0,
|
854 |
+
"MODEL"
|
855 |
+
]
|
856 |
+
],
|
857 |
+
"groups": [],
|
858 |
+
"config": {},
|
859 |
+
"extra": {},
|
860 |
+
"version": 0.4
|
861 |
+
}
|
ComfyUI_InstantID/examples/InstantID_basic.json
ADDED
@@ -0,0 +1,657 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"last_node_id": 66,
|
3 |
+
"last_link_id": 220,
|
4 |
+
"nodes": [
|
5 |
+
{
|
6 |
+
"id": 11,
|
7 |
+
"type": "InstantIDModelLoader",
|
8 |
+
"pos": [
|
9 |
+
560,
|
10 |
+
70
|
11 |
+
],
|
12 |
+
"size": {
|
13 |
+
"0": 238.72393798828125,
|
14 |
+
"1": 58
|
15 |
+
},
|
16 |
+
"flags": {},
|
17 |
+
"order": 0,
|
18 |
+
"mode": 0,
|
19 |
+
"outputs": [
|
20 |
+
{
|
21 |
+
"name": "INSTANTID",
|
22 |
+
"type": "INSTANTID",
|
23 |
+
"links": [
|
24 |
+
197
|
25 |
+
],
|
26 |
+
"shape": 3,
|
27 |
+
"slot_index": 0
|
28 |
+
}
|
29 |
+
],
|
30 |
+
"properties": {
|
31 |
+
"Node name for S&R": "InstantIDModelLoader"
|
32 |
+
},
|
33 |
+
"widgets_values": [
|
34 |
+
"ip-adapter.bin"
|
35 |
+
]
|
36 |
+
},
|
37 |
+
{
|
38 |
+
"id": 38,
|
39 |
+
"type": "InstantIDFaceAnalysis",
|
40 |
+
"pos": [
|
41 |
+
570,
|
42 |
+
180
|
43 |
+
],
|
44 |
+
"size": {
|
45 |
+
"0": 227.09793090820312,
|
46 |
+
"1": 58
|
47 |
+
},
|
48 |
+
"flags": {},
|
49 |
+
"order": 1,
|
50 |
+
"mode": 0,
|
51 |
+
"outputs": [
|
52 |
+
{
|
53 |
+
"name": "FACEANALYSIS",
|
54 |
+
"type": "FACEANALYSIS",
|
55 |
+
"links": [
|
56 |
+
198
|
57 |
+
],
|
58 |
+
"shape": 3,
|
59 |
+
"slot_index": 0
|
60 |
+
}
|
61 |
+
],
|
62 |
+
"properties": {
|
63 |
+
"Node name for S&R": "InstantIDFaceAnalysis"
|
64 |
+
},
|
65 |
+
"widgets_values": [
|
66 |
+
"CPU"
|
67 |
+
]
|
68 |
+
},
|
69 |
+
{
|
70 |
+
"id": 16,
|
71 |
+
"type": "ControlNetLoader",
|
72 |
+
"pos": [
|
73 |
+
560,
|
74 |
+
290
|
75 |
+
],
|
76 |
+
"size": {
|
77 |
+
"0": 250.07241821289062,
|
78 |
+
"1": 58
|
79 |
+
},
|
80 |
+
"flags": {},
|
81 |
+
"order": 2,
|
82 |
+
"mode": 0,
|
83 |
+
"outputs": [
|
84 |
+
{
|
85 |
+
"name": "CONTROL_NET",
|
86 |
+
"type": "CONTROL_NET",
|
87 |
+
"links": [
|
88 |
+
199
|
89 |
+
],
|
90 |
+
"shape": 3,
|
91 |
+
"slot_index": 0
|
92 |
+
}
|
93 |
+
],
|
94 |
+
"properties": {
|
95 |
+
"Node name for S&R": "ControlNetLoader"
|
96 |
+
},
|
97 |
+
"widgets_values": [
|
98 |
+
"instantid/diffusion_pytorch_model.safetensors"
|
99 |
+
]
|
100 |
+
},
|
101 |
+
{
|
102 |
+
"id": 15,
|
103 |
+
"type": "PreviewImage",
|
104 |
+
"pos": [
|
105 |
+
1670,
|
106 |
+
300
|
107 |
+
],
|
108 |
+
"size": {
|
109 |
+
"0": 584.0855712890625,
|
110 |
+
"1": 610.4592895507812
|
111 |
+
},
|
112 |
+
"flags": {},
|
113 |
+
"order": 11,
|
114 |
+
"mode": 0,
|
115 |
+
"inputs": [
|
116 |
+
{
|
117 |
+
"name": "images",
|
118 |
+
"type": "IMAGE",
|
119 |
+
"link": 19
|
120 |
+
}
|
121 |
+
],
|
122 |
+
"properties": {
|
123 |
+
"Node name for S&R": "PreviewImage"
|
124 |
+
}
|
125 |
+
},
|
126 |
+
{
|
127 |
+
"id": 5,
|
128 |
+
"type": "EmptyLatentImage",
|
129 |
+
"pos": [
|
130 |
+
910,
|
131 |
+
540
|
132 |
+
],
|
133 |
+
"size": {
|
134 |
+
"0": 315,
|
135 |
+
"1": 106
|
136 |
+
},
|
137 |
+
"flags": {},
|
138 |
+
"order": 3,
|
139 |
+
"mode": 0,
|
140 |
+
"outputs": [
|
141 |
+
{
|
142 |
+
"name": "LATENT",
|
143 |
+
"type": "LATENT",
|
144 |
+
"links": [
|
145 |
+
2
|
146 |
+
],
|
147 |
+
"slot_index": 0
|
148 |
+
}
|
149 |
+
],
|
150 |
+
"properties": {
|
151 |
+
"Node name for S&R": "EmptyLatentImage"
|
152 |
+
},
|
153 |
+
"widgets_values": [
|
154 |
+
1016,
|
155 |
+
1016,
|
156 |
+
1
|
157 |
+
]
|
158 |
+
},
|
159 |
+
{
|
160 |
+
"id": 8,
|
161 |
+
"type": "VAEDecode",
|
162 |
+
"pos": [
|
163 |
+
1670,
|
164 |
+
210
|
165 |
+
],
|
166 |
+
"size": {
|
167 |
+
"0": 210,
|
168 |
+
"1": 46
|
169 |
+
},
|
170 |
+
"flags": {},
|
171 |
+
"order": 10,
|
172 |
+
"mode": 0,
|
173 |
+
"inputs": [
|
174 |
+
{
|
175 |
+
"name": "samples",
|
176 |
+
"type": "LATENT",
|
177 |
+
"link": 7
|
178 |
+
},
|
179 |
+
{
|
180 |
+
"name": "vae",
|
181 |
+
"type": "VAE",
|
182 |
+
"link": 8
|
183 |
+
}
|
184 |
+
],
|
185 |
+
"outputs": [
|
186 |
+
{
|
187 |
+
"name": "IMAGE",
|
188 |
+
"type": "IMAGE",
|
189 |
+
"links": [
|
190 |
+
19
|
191 |
+
],
|
192 |
+
"slot_index": 0
|
193 |
+
}
|
194 |
+
],
|
195 |
+
"properties": {
|
196 |
+
"Node name for S&R": "VAEDecode"
|
197 |
+
}
|
198 |
+
},
|
199 |
+
{
|
200 |
+
"id": 60,
|
201 |
+
"type": "ApplyInstantID",
|
202 |
+
"pos": [
|
203 |
+
910,
|
204 |
+
210
|
205 |
+
],
|
206 |
+
"size": {
|
207 |
+
"0": 315,
|
208 |
+
"1": 266
|
209 |
+
},
|
210 |
+
"flags": {},
|
211 |
+
"order": 8,
|
212 |
+
"mode": 0,
|
213 |
+
"inputs": [
|
214 |
+
{
|
215 |
+
"name": "instantid",
|
216 |
+
"type": "INSTANTID",
|
217 |
+
"link": 197
|
218 |
+
},
|
219 |
+
{
|
220 |
+
"name": "insightface",
|
221 |
+
"type": "FACEANALYSIS",
|
222 |
+
"link": 198
|
223 |
+
},
|
224 |
+
{
|
225 |
+
"name": "control_net",
|
226 |
+
"type": "CONTROL_NET",
|
227 |
+
"link": 199
|
228 |
+
},
|
229 |
+
{
|
230 |
+
"name": "image",
|
231 |
+
"type": "IMAGE",
|
232 |
+
"link": 214
|
233 |
+
},
|
234 |
+
{
|
235 |
+
"name": "model",
|
236 |
+
"type": "MODEL",
|
237 |
+
"link": 206
|
238 |
+
},
|
239 |
+
{
|
240 |
+
"name": "positive",
|
241 |
+
"type": "CONDITIONING",
|
242 |
+
"link": 203
|
243 |
+
},
|
244 |
+
{
|
245 |
+
"name": "negative",
|
246 |
+
"type": "CONDITIONING",
|
247 |
+
"link": 204
|
248 |
+
},
|
249 |
+
{
|
250 |
+
"name": "image_kps",
|
251 |
+
"type": "IMAGE",
|
252 |
+
"link": null
|
253 |
+
},
|
254 |
+
{
|
255 |
+
"name": "mask",
|
256 |
+
"type": "MASK",
|
257 |
+
"link": null
|
258 |
+
}
|
259 |
+
],
|
260 |
+
"outputs": [
|
261 |
+
{
|
262 |
+
"name": "MODEL",
|
263 |
+
"type": "MODEL",
|
264 |
+
"links": [
|
265 |
+
220
|
266 |
+
],
|
267 |
+
"shape": 3,
|
268 |
+
"slot_index": 0
|
269 |
+
},
|
270 |
+
{
|
271 |
+
"name": "POSITIVE",
|
272 |
+
"type": "CONDITIONING",
|
273 |
+
"links": [
|
274 |
+
200
|
275 |
+
],
|
276 |
+
"shape": 3,
|
277 |
+
"slot_index": 1
|
278 |
+
},
|
279 |
+
{
|
280 |
+
"name": "NEGATIVE",
|
281 |
+
"type": "CONDITIONING",
|
282 |
+
"links": [
|
283 |
+
201
|
284 |
+
],
|
285 |
+
"shape": 3,
|
286 |
+
"slot_index": 2
|
287 |
+
}
|
288 |
+
],
|
289 |
+
"properties": {
|
290 |
+
"Node name for S&R": "ApplyInstantID"
|
291 |
+
},
|
292 |
+
"widgets_values": [
|
293 |
+
0.8,
|
294 |
+
0,
|
295 |
+
1
|
296 |
+
]
|
297 |
+
},
|
298 |
+
{
|
299 |
+
"id": 39,
|
300 |
+
"type": "CLIPTextEncode",
|
301 |
+
"pos": [
|
302 |
+
520,
|
303 |
+
430
|
304 |
+
],
|
305 |
+
"size": {
|
306 |
+
"0": 291.9967346191406,
|
307 |
+
"1": 128.62518310546875
|
308 |
+
},
|
309 |
+
"flags": {},
|
310 |
+
"order": 6,
|
311 |
+
"mode": 0,
|
312 |
+
"inputs": [
|
313 |
+
{
|
314 |
+
"name": "clip",
|
315 |
+
"type": "CLIP",
|
316 |
+
"link": 122
|
317 |
+
}
|
318 |
+
],
|
319 |
+
"outputs": [
|
320 |
+
{
|
321 |
+
"name": "CONDITIONING",
|
322 |
+
"type": "CONDITIONING",
|
323 |
+
"links": [
|
324 |
+
203
|
325 |
+
],
|
326 |
+
"shape": 3,
|
327 |
+
"slot_index": 0
|
328 |
+
}
|
329 |
+
],
|
330 |
+
"properties": {
|
331 |
+
"Node name for S&R": "CLIPTextEncode"
|
332 |
+
},
|
333 |
+
"widgets_values": [
|
334 |
+
"comic character. graphic illustration, comic art, graphic novel art, vibrant, highly detailed"
|
335 |
+
]
|
336 |
+
},
|
337 |
+
{
|
338 |
+
"id": 40,
|
339 |
+
"type": "CLIPTextEncode",
|
340 |
+
"pos": [
|
341 |
+
520,
|
342 |
+
620
|
343 |
+
],
|
344 |
+
"size": {
|
345 |
+
"0": 286.3603515625,
|
346 |
+
"1": 112.35245513916016
|
347 |
+
},
|
348 |
+
"flags": {},
|
349 |
+
"order": 7,
|
350 |
+
"mode": 0,
|
351 |
+
"inputs": [
|
352 |
+
{
|
353 |
+
"name": "clip",
|
354 |
+
"type": "CLIP",
|
355 |
+
"link": 123
|
356 |
+
}
|
357 |
+
],
|
358 |
+
"outputs": [
|
359 |
+
{
|
360 |
+
"name": "CONDITIONING",
|
361 |
+
"type": "CONDITIONING",
|
362 |
+
"links": [
|
363 |
+
204
|
364 |
+
],
|
365 |
+
"shape": 3,
|
366 |
+
"slot_index": 0
|
367 |
+
}
|
368 |
+
],
|
369 |
+
"properties": {
|
370 |
+
"Node name for S&R": "CLIPTextEncode"
|
371 |
+
},
|
372 |
+
"widgets_values": [
|
373 |
+
"photograph, deformed, glitch, noisy, realistic, stock photo"
|
374 |
+
]
|
375 |
+
},
|
376 |
+
{
|
377 |
+
"id": 4,
|
378 |
+
"type": "CheckpointLoaderSimple",
|
379 |
+
"pos": [
|
380 |
+
70,
|
381 |
+
520
|
382 |
+
],
|
383 |
+
"size": {
|
384 |
+
"0": 315,
|
385 |
+
"1": 98
|
386 |
+
},
|
387 |
+
"flags": {},
|
388 |
+
"order": 4,
|
389 |
+
"mode": 0,
|
390 |
+
"outputs": [
|
391 |
+
{
|
392 |
+
"name": "MODEL",
|
393 |
+
"type": "MODEL",
|
394 |
+
"links": [
|
395 |
+
206
|
396 |
+
],
|
397 |
+
"slot_index": 0
|
398 |
+
},
|
399 |
+
{
|
400 |
+
"name": "CLIP",
|
401 |
+
"type": "CLIP",
|
402 |
+
"links": [
|
403 |
+
122,
|
404 |
+
123
|
405 |
+
],
|
406 |
+
"slot_index": 1
|
407 |
+
},
|
408 |
+
{
|
409 |
+
"name": "VAE",
|
410 |
+
"type": "VAE",
|
411 |
+
"links": [
|
412 |
+
8
|
413 |
+
],
|
414 |
+
"slot_index": 2
|
415 |
+
}
|
416 |
+
],
|
417 |
+
"properties": {
|
418 |
+
"Node name for S&R": "CheckpointLoaderSimple"
|
419 |
+
},
|
420 |
+
"widgets_values": [
|
421 |
+
"sdxl/AlbedoBaseXL.safetensors"
|
422 |
+
]
|
423 |
+
},
|
424 |
+
{
|
425 |
+
"id": 3,
|
426 |
+
"type": "KSampler",
|
427 |
+
"pos": [
|
428 |
+
1300,
|
429 |
+
210
|
430 |
+
],
|
431 |
+
"size": {
|
432 |
+
"0": 315,
|
433 |
+
"1": 262
|
434 |
+
},
|
435 |
+
"flags": {},
|
436 |
+
"order": 9,
|
437 |
+
"mode": 0,
|
438 |
+
"inputs": [
|
439 |
+
{
|
440 |
+
"name": "model",
|
441 |
+
"type": "MODEL",
|
442 |
+
"link": 220
|
443 |
+
},
|
444 |
+
{
|
445 |
+
"name": "positive",
|
446 |
+
"type": "CONDITIONING",
|
447 |
+
"link": 200
|
448 |
+
},
|
449 |
+
{
|
450 |
+
"name": "negative",
|
451 |
+
"type": "CONDITIONING",
|
452 |
+
"link": 201
|
453 |
+
},
|
454 |
+
{
|
455 |
+
"name": "latent_image",
|
456 |
+
"type": "LATENT",
|
457 |
+
"link": 2
|
458 |
+
}
|
459 |
+
],
|
460 |
+
"outputs": [
|
461 |
+
{
|
462 |
+
"name": "LATENT",
|
463 |
+
"type": "LATENT",
|
464 |
+
"links": [
|
465 |
+
7
|
466 |
+
],
|
467 |
+
"slot_index": 0
|
468 |
+
}
|
469 |
+
],
|
470 |
+
"properties": {
|
471 |
+
"Node name for S&R": "KSampler"
|
472 |
+
},
|
473 |
+
"widgets_values": [
|
474 |
+
1631591050,
|
475 |
+
"fixed",
|
476 |
+
30,
|
477 |
+
4.5,
|
478 |
+
"ddpm",
|
479 |
+
"karras",
|
480 |
+
1
|
481 |
+
]
|
482 |
+
},
|
483 |
+
{
|
484 |
+
"id": 13,
|
485 |
+
"type": "LoadImage",
|
486 |
+
"pos": [
|
487 |
+
290,
|
488 |
+
70
|
489 |
+
],
|
490 |
+
"size": {
|
491 |
+
"0": 210,
|
492 |
+
"1": 314
|
493 |
+
},
|
494 |
+
"flags": {},
|
495 |
+
"order": 5,
|
496 |
+
"mode": 0,
|
497 |
+
"outputs": [
|
498 |
+
{
|
499 |
+
"name": "IMAGE",
|
500 |
+
"type": "IMAGE",
|
501 |
+
"links": [
|
502 |
+
214
|
503 |
+
],
|
504 |
+
"shape": 3,
|
505 |
+
"slot_index": 0
|
506 |
+
},
|
507 |
+
{
|
508 |
+
"name": "MASK",
|
509 |
+
"type": "MASK",
|
510 |
+
"links": null,
|
511 |
+
"shape": 3
|
512 |
+
}
|
513 |
+
],
|
514 |
+
"properties": {
|
515 |
+
"Node name for S&R": "LoadImage"
|
516 |
+
},
|
517 |
+
"widgets_values": [
|
518 |
+
"joseph-gonzalez-iFgRcqHznqg-unsplash.jpg",
|
519 |
+
"image"
|
520 |
+
]
|
521 |
+
}
|
522 |
+
],
|
523 |
+
"links": [
|
524 |
+
[
|
525 |
+
2,
|
526 |
+
5,
|
527 |
+
0,
|
528 |
+
3,
|
529 |
+
3,
|
530 |
+
"LATENT"
|
531 |
+
],
|
532 |
+
[
|
533 |
+
7,
|
534 |
+
3,
|
535 |
+
0,
|
536 |
+
8,
|
537 |
+
0,
|
538 |
+
"LATENT"
|
539 |
+
],
|
540 |
+
[
|
541 |
+
8,
|
542 |
+
4,
|
543 |
+
2,
|
544 |
+
8,
|
545 |
+
1,
|
546 |
+
"VAE"
|
547 |
+
],
|
548 |
+
[
|
549 |
+
19,
|
550 |
+
8,
|
551 |
+
0,
|
552 |
+
15,
|
553 |
+
0,
|
554 |
+
"IMAGE"
|
555 |
+
],
|
556 |
+
[
|
557 |
+
122,
|
558 |
+
4,
|
559 |
+
1,
|
560 |
+
39,
|
561 |
+
0,
|
562 |
+
"CLIP"
|
563 |
+
],
|
564 |
+
[
|
565 |
+
123,
|
566 |
+
4,
|
567 |
+
1,
|
568 |
+
40,
|
569 |
+
0,
|
570 |
+
"CLIP"
|
571 |
+
],
|
572 |
+
[
|
573 |
+
197,
|
574 |
+
11,
|
575 |
+
0,
|
576 |
+
60,
|
577 |
+
0,
|
578 |
+
"INSTANTID"
|
579 |
+
],
|
580 |
+
[
|
581 |
+
198,
|
582 |
+
38,
|
583 |
+
0,
|
584 |
+
60,
|
585 |
+
1,
|
586 |
+
"FACEANALYSIS"
|
587 |
+
],
|
588 |
+
[
|
589 |
+
199,
|
590 |
+
16,
|
591 |
+
0,
|
592 |
+
60,
|
593 |
+
2,
|
594 |
+
"CONTROL_NET"
|
595 |
+
],
|
596 |
+
[
|
597 |
+
200,
|
598 |
+
60,
|
599 |
+
1,
|
600 |
+
3,
|
601 |
+
1,
|
602 |
+
"CONDITIONING"
|
603 |
+
],
|
604 |
+
[
|
605 |
+
201,
|
606 |
+
60,
|
607 |
+
2,
|
608 |
+
3,
|
609 |
+
2,
|
610 |
+
"CONDITIONING"
|
611 |
+
],
|
612 |
+
[
|
613 |
+
203,
|
614 |
+
39,
|
615 |
+
0,
|
616 |
+
60,
|
617 |
+
5,
|
618 |
+
"CONDITIONING"
|
619 |
+
],
|
620 |
+
[
|
621 |
+
204,
|
622 |
+
40,
|
623 |
+
0,
|
624 |
+
60,
|
625 |
+
6,
|
626 |
+
"CONDITIONING"
|
627 |
+
],
|
628 |
+
[
|
629 |
+
206,
|
630 |
+
4,
|
631 |
+
0,
|
632 |
+
60,
|
633 |
+
4,
|
634 |
+
"MODEL"
|
635 |
+
],
|
636 |
+
[
|
637 |
+
214,
|
638 |
+
13,
|
639 |
+
0,
|
640 |
+
60,
|
641 |
+
3,
|
642 |
+
"IMAGE"
|
643 |
+
],
|
644 |
+
[
|
645 |
+
220,
|
646 |
+
60,
|
647 |
+
0,
|
648 |
+
3,
|
649 |
+
0,
|
650 |
+
"MODEL"
|
651 |
+
]
|
652 |
+
],
|
653 |
+
"groups": [],
|
654 |
+
"config": {},
|
655 |
+
"extra": {},
|
656 |
+
"version": 0.4
|
657 |
+
}
|
ComfyUI_InstantID/examples/InstantID_depth.json
ADDED
@@ -0,0 +1,881 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"last_node_id": 78,
|
3 |
+
"last_link_id": 239,
|
4 |
+
"nodes": [
|
5 |
+
{
|
6 |
+
"id": 11,
|
7 |
+
"type": "InstantIDModelLoader",
|
8 |
+
"pos": [
|
9 |
+
560,
|
10 |
+
70
|
11 |
+
],
|
12 |
+
"size": {
|
13 |
+
"0": 238.72393798828125,
|
14 |
+
"1": 58
|
15 |
+
},
|
16 |
+
"flags": {},
|
17 |
+
"order": 0,
|
18 |
+
"mode": 0,
|
19 |
+
"outputs": [
|
20 |
+
{
|
21 |
+
"name": "INSTANTID",
|
22 |
+
"type": "INSTANTID",
|
23 |
+
"links": [
|
24 |
+
197
|
25 |
+
],
|
26 |
+
"shape": 3,
|
27 |
+
"slot_index": 0
|
28 |
+
}
|
29 |
+
],
|
30 |
+
"properties": {
|
31 |
+
"Node name for S&R": "InstantIDModelLoader"
|
32 |
+
},
|
33 |
+
"widgets_values": [
|
34 |
+
"ip-adapter.bin"
|
35 |
+
]
|
36 |
+
},
|
37 |
+
{
|
38 |
+
"id": 38,
|
39 |
+
"type": "InstantIDFaceAnalysis",
|
40 |
+
"pos": [
|
41 |
+
570,
|
42 |
+
180
|
43 |
+
],
|
44 |
+
"size": {
|
45 |
+
"0": 227.09793090820312,
|
46 |
+
"1": 58
|
47 |
+
},
|
48 |
+
"flags": {},
|
49 |
+
"order": 1,
|
50 |
+
"mode": 0,
|
51 |
+
"outputs": [
|
52 |
+
{
|
53 |
+
"name": "FACEANALYSIS",
|
54 |
+
"type": "FACEANALYSIS",
|
55 |
+
"links": [
|
56 |
+
198
|
57 |
+
],
|
58 |
+
"shape": 3,
|
59 |
+
"slot_index": 0
|
60 |
+
}
|
61 |
+
],
|
62 |
+
"properties": {
|
63 |
+
"Node name for S&R": "InstantIDFaceAnalysis"
|
64 |
+
},
|
65 |
+
"widgets_values": [
|
66 |
+
"CPU"
|
67 |
+
]
|
68 |
+
},
|
69 |
+
{
|
70 |
+
"id": 16,
|
71 |
+
"type": "ControlNetLoader",
|
72 |
+
"pos": [
|
73 |
+
560,
|
74 |
+
290
|
75 |
+
],
|
76 |
+
"size": {
|
77 |
+
"0": 250.07241821289062,
|
78 |
+
"1": 58
|
79 |
+
},
|
80 |
+
"flags": {},
|
81 |
+
"order": 2,
|
82 |
+
"mode": 0,
|
83 |
+
"outputs": [
|
84 |
+
{
|
85 |
+
"name": "CONTROL_NET",
|
86 |
+
"type": "CONTROL_NET",
|
87 |
+
"links": [
|
88 |
+
199
|
89 |
+
],
|
90 |
+
"shape": 3,
|
91 |
+
"slot_index": 0
|
92 |
+
}
|
93 |
+
],
|
94 |
+
"properties": {
|
95 |
+
"Node name for S&R": "ControlNetLoader"
|
96 |
+
},
|
97 |
+
"widgets_values": [
|
98 |
+
"instantid/diffusion_pytorch_model.safetensors"
|
99 |
+
]
|
100 |
+
},
|
101 |
+
{
|
102 |
+
"id": 39,
|
103 |
+
"type": "CLIPTextEncode",
|
104 |
+
"pos": [
|
105 |
+
520,
|
106 |
+
430
|
107 |
+
],
|
108 |
+
"size": {
|
109 |
+
"0": 291.9967346191406,
|
110 |
+
"1": 128.62518310546875
|
111 |
+
},
|
112 |
+
"flags": {},
|
113 |
+
"order": 8,
|
114 |
+
"mode": 0,
|
115 |
+
"inputs": [
|
116 |
+
{
|
117 |
+
"name": "clip",
|
118 |
+
"type": "CLIP",
|
119 |
+
"link": 122
|
120 |
+
}
|
121 |
+
],
|
122 |
+
"outputs": [
|
123 |
+
{
|
124 |
+
"name": "CONDITIONING",
|
125 |
+
"type": "CONDITIONING",
|
126 |
+
"links": [
|
127 |
+
203
|
128 |
+
],
|
129 |
+
"shape": 3,
|
130 |
+
"slot_index": 0
|
131 |
+
}
|
132 |
+
],
|
133 |
+
"properties": {
|
134 |
+
"Node name for S&R": "CLIPTextEncode"
|
135 |
+
},
|
136 |
+
"widgets_values": [
|
137 |
+
"comic character. graphic illustration, comic art, graphic novel art, vibrant, highly detailed"
|
138 |
+
]
|
139 |
+
},
|
140 |
+
{
|
141 |
+
"id": 40,
|
142 |
+
"type": "CLIPTextEncode",
|
143 |
+
"pos": [
|
144 |
+
520,
|
145 |
+
620
|
146 |
+
],
|
147 |
+
"size": {
|
148 |
+
"0": 286.3603515625,
|
149 |
+
"1": 112.35245513916016
|
150 |
+
},
|
151 |
+
"flags": {},
|
152 |
+
"order": 9,
|
153 |
+
"mode": 0,
|
154 |
+
"inputs": [
|
155 |
+
{
|
156 |
+
"name": "clip",
|
157 |
+
"type": "CLIP",
|
158 |
+
"link": 123
|
159 |
+
}
|
160 |
+
],
|
161 |
+
"outputs": [
|
162 |
+
{
|
163 |
+
"name": "CONDITIONING",
|
164 |
+
"type": "CONDITIONING",
|
165 |
+
"links": [
|
166 |
+
204
|
167 |
+
],
|
168 |
+
"shape": 3,
|
169 |
+
"slot_index": 0
|
170 |
+
}
|
171 |
+
],
|
172 |
+
"properties": {
|
173 |
+
"Node name for S&R": "CLIPTextEncode"
|
174 |
+
},
|
175 |
+
"widgets_values": [
|
176 |
+
"photograph, deformed, glitch, noisy, realistic, stock photo"
|
177 |
+
]
|
178 |
+
},
|
179 |
+
{
|
180 |
+
"id": 4,
|
181 |
+
"type": "CheckpointLoaderSimple",
|
182 |
+
"pos": [
|
183 |
+
70,
|
184 |
+
520
|
185 |
+
],
|
186 |
+
"size": {
|
187 |
+
"0": 315,
|
188 |
+
"1": 98
|
189 |
+
},
|
190 |
+
"flags": {},
|
191 |
+
"order": 3,
|
192 |
+
"mode": 0,
|
193 |
+
"outputs": [
|
194 |
+
{
|
195 |
+
"name": "MODEL",
|
196 |
+
"type": "MODEL",
|
197 |
+
"links": [
|
198 |
+
206
|
199 |
+
],
|
200 |
+
"slot_index": 0
|
201 |
+
},
|
202 |
+
{
|
203 |
+
"name": "CLIP",
|
204 |
+
"type": "CLIP",
|
205 |
+
"links": [
|
206 |
+
122,
|
207 |
+
123
|
208 |
+
],
|
209 |
+
"slot_index": 1
|
210 |
+
},
|
211 |
+
{
|
212 |
+
"name": "VAE",
|
213 |
+
"type": "VAE",
|
214 |
+
"links": [
|
215 |
+
8
|
216 |
+
],
|
217 |
+
"slot_index": 2
|
218 |
+
}
|
219 |
+
],
|
220 |
+
"properties": {
|
221 |
+
"Node name for S&R": "CheckpointLoaderSimple"
|
222 |
+
},
|
223 |
+
"widgets_values": [
|
224 |
+
"sdxl/AlbedoBaseXL.safetensors"
|
225 |
+
]
|
226 |
+
},
|
227 |
+
{
|
228 |
+
"id": 60,
|
229 |
+
"type": "ApplyInstantID",
|
230 |
+
"pos": [
|
231 |
+
910,
|
232 |
+
210
|
233 |
+
],
|
234 |
+
"size": {
|
235 |
+
"0": 315,
|
236 |
+
"1": 266
|
237 |
+
},
|
238 |
+
"flags": {},
|
239 |
+
"order": 11,
|
240 |
+
"mode": 0,
|
241 |
+
"inputs": [
|
242 |
+
{
|
243 |
+
"name": "instantid",
|
244 |
+
"type": "INSTANTID",
|
245 |
+
"link": 197
|
246 |
+
},
|
247 |
+
{
|
248 |
+
"name": "insightface",
|
249 |
+
"type": "FACEANALYSIS",
|
250 |
+
"link": 198
|
251 |
+
},
|
252 |
+
{
|
253 |
+
"name": "control_net",
|
254 |
+
"type": "CONTROL_NET",
|
255 |
+
"link": 199
|
256 |
+
},
|
257 |
+
{
|
258 |
+
"name": "image",
|
259 |
+
"type": "IMAGE",
|
260 |
+
"link": 214
|
261 |
+
},
|
262 |
+
{
|
263 |
+
"name": "model",
|
264 |
+
"type": "MODEL",
|
265 |
+
"link": 206
|
266 |
+
},
|
267 |
+
{
|
268 |
+
"name": "positive",
|
269 |
+
"type": "CONDITIONING",
|
270 |
+
"link": 203
|
271 |
+
},
|
272 |
+
{
|
273 |
+
"name": "negative",
|
274 |
+
"type": "CONDITIONING",
|
275 |
+
"link": 204
|
276 |
+
},
|
277 |
+
{
|
278 |
+
"name": "image_kps",
|
279 |
+
"type": "IMAGE",
|
280 |
+
"link": 236
|
281 |
+
},
|
282 |
+
{
|
283 |
+
"name": "mask",
|
284 |
+
"type": "MASK",
|
285 |
+
"link": null
|
286 |
+
}
|
287 |
+
],
|
288 |
+
"outputs": [
|
289 |
+
{
|
290 |
+
"name": "MODEL",
|
291 |
+
"type": "MODEL",
|
292 |
+
"links": [
|
293 |
+
227
|
294 |
+
],
|
295 |
+
"shape": 3,
|
296 |
+
"slot_index": 0
|
297 |
+
},
|
298 |
+
{
|
299 |
+
"name": "POSITIVE",
|
300 |
+
"type": "CONDITIONING",
|
301 |
+
"links": [
|
302 |
+
229
|
303 |
+
],
|
304 |
+
"shape": 3,
|
305 |
+
"slot_index": 1
|
306 |
+
},
|
307 |
+
{
|
308 |
+
"name": "NEGATIVE",
|
309 |
+
"type": "CONDITIONING",
|
310 |
+
"links": [
|
311 |
+
228
|
312 |
+
],
|
313 |
+
"shape": 3,
|
314 |
+
"slot_index": 2
|
315 |
+
}
|
316 |
+
],
|
317 |
+
"properties": {
|
318 |
+
"Node name for S&R": "ApplyInstantID"
|
319 |
+
},
|
320 |
+
"widgets_values": [
|
321 |
+
0.8,
|
322 |
+
0,
|
323 |
+
1
|
324 |
+
]
|
325 |
+
},
|
326 |
+
{
|
327 |
+
"id": 15,
|
328 |
+
"type": "PreviewImage",
|
329 |
+
"pos": [
|
330 |
+
1937,
|
331 |
+
321
|
332 |
+
],
|
333 |
+
"size": {
|
334 |
+
"0": 584.0855712890625,
|
335 |
+
"1": 610.4592895507812
|
336 |
+
},
|
337 |
+
"flags": {},
|
338 |
+
"order": 15,
|
339 |
+
"mode": 0,
|
340 |
+
"inputs": [
|
341 |
+
{
|
342 |
+
"name": "images",
|
343 |
+
"type": "IMAGE",
|
344 |
+
"link": 19
|
345 |
+
}
|
346 |
+
],
|
347 |
+
"properties": {
|
348 |
+
"Node name for S&R": "PreviewImage"
|
349 |
+
}
|
350 |
+
},
|
351 |
+
{
|
352 |
+
"id": 8,
|
353 |
+
"type": "VAEDecode",
|
354 |
+
"pos": [
|
355 |
+
1940,
|
356 |
+
207
|
357 |
+
],
|
358 |
+
"size": {
|
359 |
+
"0": 210,
|
360 |
+
"1": 46
|
361 |
+
},
|
362 |
+
"flags": {},
|
363 |
+
"order": 14,
|
364 |
+
"mode": 0,
|
365 |
+
"inputs": [
|
366 |
+
{
|
367 |
+
"name": "samples",
|
368 |
+
"type": "LATENT",
|
369 |
+
"link": 7
|
370 |
+
},
|
371 |
+
{
|
372 |
+
"name": "vae",
|
373 |
+
"type": "VAE",
|
374 |
+
"link": 8
|
375 |
+
}
|
376 |
+
],
|
377 |
+
"outputs": [
|
378 |
+
{
|
379 |
+
"name": "IMAGE",
|
380 |
+
"type": "IMAGE",
|
381 |
+
"links": [
|
382 |
+
19
|
383 |
+
],
|
384 |
+
"slot_index": 0
|
385 |
+
}
|
386 |
+
],
|
387 |
+
"properties": {
|
388 |
+
"Node name for S&R": "VAEDecode"
|
389 |
+
}
|
390 |
+
},
|
391 |
+
{
|
392 |
+
"id": 5,
|
393 |
+
"type": "EmptyLatentImage",
|
394 |
+
"pos": [
|
395 |
+
910,
|
396 |
+
540
|
397 |
+
],
|
398 |
+
"size": {
|
399 |
+
"0": 315,
|
400 |
+
"1": 106
|
401 |
+
},
|
402 |
+
"flags": {},
|
403 |
+
"order": 4,
|
404 |
+
"mode": 0,
|
405 |
+
"outputs": [
|
406 |
+
{
|
407 |
+
"name": "LATENT",
|
408 |
+
"type": "LATENT",
|
409 |
+
"links": [
|
410 |
+
2
|
411 |
+
],
|
412 |
+
"slot_index": 0
|
413 |
+
}
|
414 |
+
],
|
415 |
+
"properties": {
|
416 |
+
"Node name for S&R": "EmptyLatentImage"
|
417 |
+
},
|
418 |
+
"widgets_values": [
|
419 |
+
1016,
|
420 |
+
1016,
|
421 |
+
1
|
422 |
+
]
|
423 |
+
},
|
424 |
+
{
|
425 |
+
"id": 13,
|
426 |
+
"type": "LoadImage",
|
427 |
+
"pos": [
|
428 |
+
290,
|
429 |
+
70
|
430 |
+
],
|
431 |
+
"size": {
|
432 |
+
"0": 210,
|
433 |
+
"1": 314
|
434 |
+
},
|
435 |
+
"flags": {},
|
436 |
+
"order": 5,
|
437 |
+
"mode": 0,
|
438 |
+
"outputs": [
|
439 |
+
{
|
440 |
+
"name": "IMAGE",
|
441 |
+
"type": "IMAGE",
|
442 |
+
"links": [
|
443 |
+
214
|
444 |
+
],
|
445 |
+
"shape": 3,
|
446 |
+
"slot_index": 0
|
447 |
+
},
|
448 |
+
{
|
449 |
+
"name": "MASK",
|
450 |
+
"type": "MASK",
|
451 |
+
"links": null,
|
452 |
+
"shape": 3
|
453 |
+
}
|
454 |
+
],
|
455 |
+
"properties": {
|
456 |
+
"Node name for S&R": "LoadImage"
|
457 |
+
},
|
458 |
+
"widgets_values": [
|
459 |
+
"face4.jpg",
|
460 |
+
"image"
|
461 |
+
]
|
462 |
+
},
|
463 |
+
{
|
464 |
+
"id": 73,
|
465 |
+
"type": "ControlNetLoader",
|
466 |
+
"pos": [
|
467 |
+
909,
|
468 |
+
706
|
469 |
+
],
|
470 |
+
"size": {
|
471 |
+
"0": 315,
|
472 |
+
"1": 58
|
473 |
+
},
|
474 |
+
"flags": {},
|
475 |
+
"order": 6,
|
476 |
+
"mode": 0,
|
477 |
+
"outputs": [
|
478 |
+
{
|
479 |
+
"name": "CONTROL_NET",
|
480 |
+
"type": "CONTROL_NET",
|
481 |
+
"links": [
|
482 |
+
232
|
483 |
+
],
|
484 |
+
"shape": 3
|
485 |
+
}
|
486 |
+
],
|
487 |
+
"properties": {
|
488 |
+
"Node name for S&R": "ControlNetLoader"
|
489 |
+
},
|
490 |
+
"widgets_values": [
|
491 |
+
"control-lora/control-lora-depth-rank256.safetensors"
|
492 |
+
]
|
493 |
+
},
|
494 |
+
{
|
495 |
+
"id": 74,
|
496 |
+
"type": "LoadImage",
|
497 |
+
"pos": [
|
498 |
+
508,
|
499 |
+
816
|
500 |
+
],
|
501 |
+
"size": {
|
502 |
+
"0": 315,
|
503 |
+
"1": 314.0000305175781
|
504 |
+
},
|
505 |
+
"flags": {},
|
506 |
+
"order": 7,
|
507 |
+
"mode": 0,
|
508 |
+
"outputs": [
|
509 |
+
{
|
510 |
+
"name": "IMAGE",
|
511 |
+
"type": "IMAGE",
|
512 |
+
"links": [
|
513 |
+
236,
|
514 |
+
238
|
515 |
+
],
|
516 |
+
"shape": 3,
|
517 |
+
"slot_index": 0
|
518 |
+
},
|
519 |
+
{
|
520 |
+
"name": "MASK",
|
521 |
+
"type": "MASK",
|
522 |
+
"links": null,
|
523 |
+
"shape": 3
|
524 |
+
}
|
525 |
+
],
|
526 |
+
"properties": {
|
527 |
+
"Node name for S&R": "LoadImage"
|
528 |
+
},
|
529 |
+
"widgets_values": [
|
530 |
+
"666561.jpg",
|
531 |
+
"image"
|
532 |
+
]
|
533 |
+
},
|
534 |
+
{
|
535 |
+
"id": 72,
|
536 |
+
"type": "ControlNetApplyAdvanced",
|
537 |
+
"pos": [
|
538 |
+
1284,
|
539 |
+
416
|
540 |
+
],
|
541 |
+
"size": {
|
542 |
+
"0": 226.8000030517578,
|
543 |
+
"1": 166
|
544 |
+
},
|
545 |
+
"flags": {},
|
546 |
+
"order": 12,
|
547 |
+
"mode": 0,
|
548 |
+
"inputs": [
|
549 |
+
{
|
550 |
+
"name": "positive",
|
551 |
+
"type": "CONDITIONING",
|
552 |
+
"link": 229
|
553 |
+
},
|
554 |
+
{
|
555 |
+
"name": "negative",
|
556 |
+
"type": "CONDITIONING",
|
557 |
+
"link": 228
|
558 |
+
},
|
559 |
+
{
|
560 |
+
"name": "control_net",
|
561 |
+
"type": "CONTROL_NET",
|
562 |
+
"link": 232,
|
563 |
+
"slot_index": 2
|
564 |
+
},
|
565 |
+
{
|
566 |
+
"name": "image",
|
567 |
+
"type": "IMAGE",
|
568 |
+
"link": 239
|
569 |
+
}
|
570 |
+
],
|
571 |
+
"outputs": [
|
572 |
+
{
|
573 |
+
"name": "positive",
|
574 |
+
"type": "CONDITIONING",
|
575 |
+
"links": [
|
576 |
+
230
|
577 |
+
],
|
578 |
+
"shape": 3,
|
579 |
+
"slot_index": 0
|
580 |
+
},
|
581 |
+
{
|
582 |
+
"name": "negative",
|
583 |
+
"type": "CONDITIONING",
|
584 |
+
"links": [
|
585 |
+
231
|
586 |
+
],
|
587 |
+
"shape": 3,
|
588 |
+
"slot_index": 1
|
589 |
+
}
|
590 |
+
],
|
591 |
+
"properties": {
|
592 |
+
"Node name for S&R": "ControlNetApplyAdvanced"
|
593 |
+
},
|
594 |
+
"widgets_values": [
|
595 |
+
0.65,
|
596 |
+
0,
|
597 |
+
0.35000000000000003
|
598 |
+
]
|
599 |
+
},
|
600 |
+
{
|
601 |
+
"id": 77,
|
602 |
+
"type": "Zoe-DepthMapPreprocessor",
|
603 |
+
"pos": [
|
604 |
+
1009,
|
605 |
+
839
|
606 |
+
],
|
607 |
+
"size": [
|
608 |
+
210,
|
609 |
+
58
|
610 |
+
],
|
611 |
+
"flags": {},
|
612 |
+
"order": 10,
|
613 |
+
"mode": 0,
|
614 |
+
"inputs": [
|
615 |
+
{
|
616 |
+
"name": "image",
|
617 |
+
"type": "IMAGE",
|
618 |
+
"link": 238
|
619 |
+
}
|
620 |
+
],
|
621 |
+
"outputs": [
|
622 |
+
{
|
623 |
+
"name": "IMAGE",
|
624 |
+
"type": "IMAGE",
|
625 |
+
"links": [
|
626 |
+
239
|
627 |
+
],
|
628 |
+
"shape": 3,
|
629 |
+
"slot_index": 0
|
630 |
+
}
|
631 |
+
],
|
632 |
+
"properties": {
|
633 |
+
"Node name for S&R": "Zoe-DepthMapPreprocessor"
|
634 |
+
},
|
635 |
+
"widgets_values": [
|
636 |
+
1024
|
637 |
+
]
|
638 |
+
},
|
639 |
+
{
|
640 |
+
"id": 3,
|
641 |
+
"type": "KSampler",
|
642 |
+
"pos": [
|
643 |
+
1570,
|
644 |
+
210
|
645 |
+
],
|
646 |
+
"size": {
|
647 |
+
"0": 315,
|
648 |
+
"1": 262
|
649 |
+
},
|
650 |
+
"flags": {},
|
651 |
+
"order": 13,
|
652 |
+
"mode": 0,
|
653 |
+
"inputs": [
|
654 |
+
{
|
655 |
+
"name": "model",
|
656 |
+
"type": "MODEL",
|
657 |
+
"link": 227
|
658 |
+
},
|
659 |
+
{
|
660 |
+
"name": "positive",
|
661 |
+
"type": "CONDITIONING",
|
662 |
+
"link": 230
|
663 |
+
},
|
664 |
+
{
|
665 |
+
"name": "negative",
|
666 |
+
"type": "CONDITIONING",
|
667 |
+
"link": 231
|
668 |
+
},
|
669 |
+
{
|
670 |
+
"name": "latent_image",
|
671 |
+
"type": "LATENT",
|
672 |
+
"link": 2
|
673 |
+
}
|
674 |
+
],
|
675 |
+
"outputs": [
|
676 |
+
{
|
677 |
+
"name": "LATENT",
|
678 |
+
"type": "LATENT",
|
679 |
+
"links": [
|
680 |
+
7
|
681 |
+
],
|
682 |
+
"slot_index": 0
|
683 |
+
}
|
684 |
+
],
|
685 |
+
"properties": {
|
686 |
+
"Node name for S&R": "KSampler"
|
687 |
+
},
|
688 |
+
"widgets_values": [
|
689 |
+
1631592172,
|
690 |
+
"fixed",
|
691 |
+
30,
|
692 |
+
4.5,
|
693 |
+
"ddpm",
|
694 |
+
"karras",
|
695 |
+
1
|
696 |
+
]
|
697 |
+
}
|
698 |
+
],
|
699 |
+
"links": [
|
700 |
+
[
|
701 |
+
2,
|
702 |
+
5,
|
703 |
+
0,
|
704 |
+
3,
|
705 |
+
3,
|
706 |
+
"LATENT"
|
707 |
+
],
|
708 |
+
[
|
709 |
+
7,
|
710 |
+
3,
|
711 |
+
0,
|
712 |
+
8,
|
713 |
+
0,
|
714 |
+
"LATENT"
|
715 |
+
],
|
716 |
+
[
|
717 |
+
8,
|
718 |
+
4,
|
719 |
+
2,
|
720 |
+
8,
|
721 |
+
1,
|
722 |
+
"VAE"
|
723 |
+
],
|
724 |
+
[
|
725 |
+
19,
|
726 |
+
8,
|
727 |
+
0,
|
728 |
+
15,
|
729 |
+
0,
|
730 |
+
"IMAGE"
|
731 |
+
],
|
732 |
+
[
|
733 |
+
122,
|
734 |
+
4,
|
735 |
+
1,
|
736 |
+
39,
|
737 |
+
0,
|
738 |
+
"CLIP"
|
739 |
+
],
|
740 |
+
[
|
741 |
+
123,
|
742 |
+
4,
|
743 |
+
1,
|
744 |
+
40,
|
745 |
+
0,
|
746 |
+
"CLIP"
|
747 |
+
],
|
748 |
+
[
|
749 |
+
197,
|
750 |
+
11,
|
751 |
+
0,
|
752 |
+
60,
|
753 |
+
0,
|
754 |
+
"INSTANTID"
|
755 |
+
],
|
756 |
+
[
|
757 |
+
198,
|
758 |
+
38,
|
759 |
+
0,
|
760 |
+
60,
|
761 |
+
1,
|
762 |
+
"FACEANALYSIS"
|
763 |
+
],
|
764 |
+
[
|
765 |
+
199,
|
766 |
+
16,
|
767 |
+
0,
|
768 |
+
60,
|
769 |
+
2,
|
770 |
+
"CONTROL_NET"
|
771 |
+
],
|
772 |
+
[
|
773 |
+
203,
|
774 |
+
39,
|
775 |
+
0,
|
776 |
+
60,
|
777 |
+
5,
|
778 |
+
"CONDITIONING"
|
779 |
+
],
|
780 |
+
[
|
781 |
+
204,
|
782 |
+
40,
|
783 |
+
0,
|
784 |
+
60,
|
785 |
+
6,
|
786 |
+
"CONDITIONING"
|
787 |
+
],
|
788 |
+
[
|
789 |
+
206,
|
790 |
+
4,
|
791 |
+
0,
|
792 |
+
60,
|
793 |
+
4,
|
794 |
+
"MODEL"
|
795 |
+
],
|
796 |
+
[
|
797 |
+
214,
|
798 |
+
13,
|
799 |
+
0,
|
800 |
+
60,
|
801 |
+
3,
|
802 |
+
"IMAGE"
|
803 |
+
],
|
804 |
+
[
|
805 |
+
227,
|
806 |
+
60,
|
807 |
+
0,
|
808 |
+
3,
|
809 |
+
0,
|
810 |
+
"MODEL"
|
811 |
+
],
|
812 |
+
[
|
813 |
+
228,
|
814 |
+
60,
|
815 |
+
2,
|
816 |
+
72,
|
817 |
+
1,
|
818 |
+
"CONDITIONING"
|
819 |
+
],
|
820 |
+
[
|
821 |
+
229,
|
822 |
+
60,
|
823 |
+
1,
|
824 |
+
72,
|
825 |
+
0,
|
826 |
+
"CONDITIONING"
|
827 |
+
],
|
828 |
+
[
|
829 |
+
230,
|
830 |
+
72,
|
831 |
+
0,
|
832 |
+
3,
|
833 |
+
1,
|
834 |
+
"CONDITIONING"
|
835 |
+
],
|
836 |
+
[
|
837 |
+
231,
|
838 |
+
72,
|
839 |
+
1,
|
840 |
+
3,
|
841 |
+
2,
|
842 |
+
"CONDITIONING"
|
843 |
+
],
|
844 |
+
[
|
845 |
+
232,
|
846 |
+
73,
|
847 |
+
0,
|
848 |
+
72,
|
849 |
+
2,
|
850 |
+
"CONTROL_NET"
|
851 |
+
],
|
852 |
+
[
|
853 |
+
236,
|
854 |
+
74,
|
855 |
+
0,
|
856 |
+
60,
|
857 |
+
7,
|
858 |
+
"IMAGE"
|
859 |
+
],
|
860 |
+
[
|
861 |
+
238,
|
862 |
+
74,
|
863 |
+
0,
|
864 |
+
77,
|
865 |
+
0,
|
866 |
+
"IMAGE"
|
867 |
+
],
|
868 |
+
[
|
869 |
+
239,
|
870 |
+
77,
|
871 |
+
0,
|
872 |
+
72,
|
873 |
+
3,
|
874 |
+
"IMAGE"
|
875 |
+
]
|
876 |
+
],
|
877 |
+
"groups": [],
|
878 |
+
"config": {},
|
879 |
+
"extra": {},
|
880 |
+
"version": 0.4
|
881 |
+
}
|
ComfyUI_InstantID/examples/InstantID_multi_id.json
ADDED
@@ -0,0 +1,1364 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"last_node_id": 92,
|
3 |
+
"last_link_id": 290,
|
4 |
+
"nodes": [
|
5 |
+
{
|
6 |
+
"id": 15,
|
7 |
+
"type": "PreviewImage",
|
8 |
+
"pos": [
|
9 |
+
2160,
|
10 |
+
-150
|
11 |
+
],
|
12 |
+
"size": {
|
13 |
+
"0": 584.0855712890625,
|
14 |
+
"1": 610.4592895507812
|
15 |
+
},
|
16 |
+
"flags": {},
|
17 |
+
"order": 23,
|
18 |
+
"mode": 0,
|
19 |
+
"inputs": [
|
20 |
+
{
|
21 |
+
"name": "images",
|
22 |
+
"type": "IMAGE",
|
23 |
+
"link": 19
|
24 |
+
}
|
25 |
+
],
|
26 |
+
"properties": {
|
27 |
+
"Node name for S&R": "PreviewImage"
|
28 |
+
}
|
29 |
+
},
|
30 |
+
{
|
31 |
+
"id": 8,
|
32 |
+
"type": "VAEDecode",
|
33 |
+
"pos": [
|
34 |
+
2170,
|
35 |
+
-270
|
36 |
+
],
|
37 |
+
"size": {
|
38 |
+
"0": 210,
|
39 |
+
"1": 46
|
40 |
+
},
|
41 |
+
"flags": {},
|
42 |
+
"order": 22,
|
43 |
+
"mode": 0,
|
44 |
+
"inputs": [
|
45 |
+
{
|
46 |
+
"name": "samples",
|
47 |
+
"type": "LATENT",
|
48 |
+
"link": 7
|
49 |
+
},
|
50 |
+
{
|
51 |
+
"name": "vae",
|
52 |
+
"type": "VAE",
|
53 |
+
"link": 254
|
54 |
+
}
|
55 |
+
],
|
56 |
+
"outputs": [
|
57 |
+
{
|
58 |
+
"name": "IMAGE",
|
59 |
+
"type": "IMAGE",
|
60 |
+
"links": [
|
61 |
+
19
|
62 |
+
],
|
63 |
+
"slot_index": 0
|
64 |
+
}
|
65 |
+
],
|
66 |
+
"properties": {
|
67 |
+
"Node name for S&R": "VAEDecode"
|
68 |
+
}
|
69 |
+
},
|
70 |
+
{
|
71 |
+
"id": 81,
|
72 |
+
"type": "Reroute",
|
73 |
+
"pos": [
|
74 |
+
1980,
|
75 |
+
120
|
76 |
+
],
|
77 |
+
"size": [
|
78 |
+
75,
|
79 |
+
26
|
80 |
+
],
|
81 |
+
"flags": {},
|
82 |
+
"order": 13,
|
83 |
+
"mode": 0,
|
84 |
+
"inputs": [
|
85 |
+
{
|
86 |
+
"name": "",
|
87 |
+
"type": "*",
|
88 |
+
"link": 253
|
89 |
+
}
|
90 |
+
],
|
91 |
+
"outputs": [
|
92 |
+
{
|
93 |
+
"name": "VAE",
|
94 |
+
"type": "VAE",
|
95 |
+
"links": [
|
96 |
+
254
|
97 |
+
],
|
98 |
+
"slot_index": 0
|
99 |
+
}
|
100 |
+
],
|
101 |
+
"properties": {
|
102 |
+
"showOutputText": true,
|
103 |
+
"horizontal": false
|
104 |
+
}
|
105 |
+
},
|
106 |
+
{
|
107 |
+
"id": 38,
|
108 |
+
"type": "InstantIDFaceAnalysis",
|
109 |
+
"pos": [
|
110 |
+
-210,
|
111 |
+
-40
|
112 |
+
],
|
113 |
+
"size": [
|
114 |
+
210,
|
115 |
+
60
|
116 |
+
],
|
117 |
+
"flags": {},
|
118 |
+
"order": 0,
|
119 |
+
"mode": 0,
|
120 |
+
"outputs": [
|
121 |
+
{
|
122 |
+
"name": "FACEANALYSIS",
|
123 |
+
"type": "FACEANALYSIS",
|
124 |
+
"links": [
|
125 |
+
198,
|
126 |
+
239
|
127 |
+
],
|
128 |
+
"shape": 3,
|
129 |
+
"slot_index": 0
|
130 |
+
}
|
131 |
+
],
|
132 |
+
"properties": {
|
133 |
+
"Node name for S&R": "InstantIDFaceAnalysis"
|
134 |
+
},
|
135 |
+
"widgets_values": [
|
136 |
+
"CPU"
|
137 |
+
]
|
138 |
+
},
|
139 |
+
{
|
140 |
+
"id": 16,
|
141 |
+
"type": "ControlNetLoader",
|
142 |
+
"pos": [
|
143 |
+
-210,
|
144 |
+
70
|
145 |
+
],
|
146 |
+
"size": [
|
147 |
+
210,
|
148 |
+
60
|
149 |
+
],
|
150 |
+
"flags": {},
|
151 |
+
"order": 1,
|
152 |
+
"mode": 0,
|
153 |
+
"outputs": [
|
154 |
+
{
|
155 |
+
"name": "CONTROL_NET",
|
156 |
+
"type": "CONTROL_NET",
|
157 |
+
"links": [
|
158 |
+
199,
|
159 |
+
240
|
160 |
+
],
|
161 |
+
"shape": 3,
|
162 |
+
"slot_index": 0
|
163 |
+
}
|
164 |
+
],
|
165 |
+
"properties": {
|
166 |
+
"Node name for S&R": "ControlNetLoader"
|
167 |
+
},
|
168 |
+
"widgets_values": [
|
169 |
+
"instantid/diffusion_pytorch_model.safetensors"
|
170 |
+
]
|
171 |
+
},
|
172 |
+
{
|
173 |
+
"id": 79,
|
174 |
+
"type": "ConditioningCombine",
|
175 |
+
"pos": [
|
176 |
+
1410,
|
177 |
+
-190
|
178 |
+
],
|
179 |
+
"size": [
|
180 |
+
228.39999389648438,
|
181 |
+
46
|
182 |
+
],
|
183 |
+
"flags": {},
|
184 |
+
"order": 19,
|
185 |
+
"mode": 0,
|
186 |
+
"inputs": [
|
187 |
+
{
|
188 |
+
"name": "conditioning_1",
|
189 |
+
"type": "CONDITIONING",
|
190 |
+
"link": 247
|
191 |
+
},
|
192 |
+
{
|
193 |
+
"name": "conditioning_2",
|
194 |
+
"type": "CONDITIONING",
|
195 |
+
"link": 248
|
196 |
+
}
|
197 |
+
],
|
198 |
+
"outputs": [
|
199 |
+
{
|
200 |
+
"name": "CONDITIONING",
|
201 |
+
"type": "CONDITIONING",
|
202 |
+
"links": [
|
203 |
+
249
|
204 |
+
],
|
205 |
+
"shape": 3,
|
206 |
+
"slot_index": 0
|
207 |
+
}
|
208 |
+
],
|
209 |
+
"properties": {
|
210 |
+
"Node name for S&R": "ConditioningCombine"
|
211 |
+
}
|
212 |
+
},
|
213 |
+
{
|
214 |
+
"id": 84,
|
215 |
+
"type": "ImageFlip+",
|
216 |
+
"pos": [
|
217 |
+
990,
|
218 |
+
-210
|
219 |
+
],
|
220 |
+
"size": {
|
221 |
+
"0": 315,
|
222 |
+
"1": 58
|
223 |
+
},
|
224 |
+
"flags": {},
|
225 |
+
"order": 15,
|
226 |
+
"mode": 0,
|
227 |
+
"inputs": [
|
228 |
+
{
|
229 |
+
"name": "image",
|
230 |
+
"type": "IMAGE",
|
231 |
+
"link": 258
|
232 |
+
}
|
233 |
+
],
|
234 |
+
"outputs": [
|
235 |
+
{
|
236 |
+
"name": "IMAGE",
|
237 |
+
"type": "IMAGE",
|
238 |
+
"links": [
|
239 |
+
259
|
240 |
+
],
|
241 |
+
"shape": 3,
|
242 |
+
"slot_index": 0
|
243 |
+
}
|
244 |
+
],
|
245 |
+
"properties": {
|
246 |
+
"Node name for S&R": "ImageFlip+"
|
247 |
+
},
|
248 |
+
"widgets_values": [
|
249 |
+
"x"
|
250 |
+
]
|
251 |
+
},
|
252 |
+
{
|
253 |
+
"id": 13,
|
254 |
+
"type": "LoadImage",
|
255 |
+
"pos": [
|
256 |
+
715,
|
257 |
+
35
|
258 |
+
],
|
259 |
+
"size": [
|
260 |
+
213.36950471073226,
|
261 |
+
296.38119750842566
|
262 |
+
],
|
263 |
+
"flags": {},
|
264 |
+
"order": 2,
|
265 |
+
"mode": 0,
|
266 |
+
"outputs": [
|
267 |
+
{
|
268 |
+
"name": "IMAGE",
|
269 |
+
"type": "IMAGE",
|
270 |
+
"links": [
|
271 |
+
214
|
272 |
+
],
|
273 |
+
"shape": 3,
|
274 |
+
"slot_index": 0
|
275 |
+
},
|
276 |
+
{
|
277 |
+
"name": "MASK",
|
278 |
+
"type": "MASK",
|
279 |
+
"links": null,
|
280 |
+
"shape": 3
|
281 |
+
}
|
282 |
+
],
|
283 |
+
"properties": {
|
284 |
+
"Node name for S&R": "LoadImage"
|
285 |
+
},
|
286 |
+
"widgets_values": [
|
287 |
+
"face4.jpg",
|
288 |
+
"image"
|
289 |
+
]
|
290 |
+
},
|
291 |
+
{
|
292 |
+
"id": 88,
|
293 |
+
"type": "MaskFlip+",
|
294 |
+
"pos": [
|
295 |
+
990,
|
296 |
+
-110
|
297 |
+
],
|
298 |
+
"size": {
|
299 |
+
"0": 315,
|
300 |
+
"1": 58
|
301 |
+
},
|
302 |
+
"flags": {},
|
303 |
+
"order": 17,
|
304 |
+
"mode": 0,
|
305 |
+
"inputs": [
|
306 |
+
{
|
307 |
+
"name": "mask",
|
308 |
+
"type": "MASK",
|
309 |
+
"link": 263
|
310 |
+
}
|
311 |
+
],
|
312 |
+
"outputs": [
|
313 |
+
{
|
314 |
+
"name": "MASK",
|
315 |
+
"type": "MASK",
|
316 |
+
"links": [
|
317 |
+
264
|
318 |
+
],
|
319 |
+
"shape": 3,
|
320 |
+
"slot_index": 0
|
321 |
+
}
|
322 |
+
],
|
323 |
+
"properties": {
|
324 |
+
"Node name for S&R": "MaskFlip+"
|
325 |
+
},
|
326 |
+
"widgets_values": [
|
327 |
+
"x"
|
328 |
+
]
|
329 |
+
},
|
330 |
+
{
|
331 |
+
"id": 78,
|
332 |
+
"type": "LoadImage",
|
333 |
+
"pos": [
|
334 |
+
714,
|
335 |
+
-512
|
336 |
+
],
|
337 |
+
"size": [
|
338 |
+
210,
|
339 |
+
314
|
340 |
+
],
|
341 |
+
"flags": {},
|
342 |
+
"order": 3,
|
343 |
+
"mode": 0,
|
344 |
+
"outputs": [
|
345 |
+
{
|
346 |
+
"name": "IMAGE",
|
347 |
+
"type": "IMAGE",
|
348 |
+
"links": [
|
349 |
+
246
|
350 |
+
],
|
351 |
+
"shape": 3,
|
352 |
+
"slot_index": 0
|
353 |
+
},
|
354 |
+
{
|
355 |
+
"name": "MASK",
|
356 |
+
"type": "MASK",
|
357 |
+
"links": null,
|
358 |
+
"shape": 3
|
359 |
+
}
|
360 |
+
],
|
361 |
+
"properties": {
|
362 |
+
"Node name for S&R": "LoadImage"
|
363 |
+
},
|
364 |
+
"widgets_values": [
|
365 |
+
"joseph-gonzalez-iFgRcqHznqg-unsplash.jpg",
|
366 |
+
"image"
|
367 |
+
]
|
368 |
+
},
|
369 |
+
{
|
370 |
+
"id": 85,
|
371 |
+
"type": "SolidMask",
|
372 |
+
"pos": [
|
373 |
+
970,
|
374 |
+
510
|
375 |
+
],
|
376 |
+
"size": [
|
377 |
+
210,
|
378 |
+
106
|
379 |
+
],
|
380 |
+
"flags": {},
|
381 |
+
"order": 4,
|
382 |
+
"mode": 0,
|
383 |
+
"outputs": [
|
384 |
+
{
|
385 |
+
"name": "MASK",
|
386 |
+
"type": "MASK",
|
387 |
+
"links": [
|
388 |
+
260
|
389 |
+
],
|
390 |
+
"shape": 3,
|
391 |
+
"slot_index": 0
|
392 |
+
}
|
393 |
+
],
|
394 |
+
"properties": {
|
395 |
+
"Node name for S&R": "SolidMask"
|
396 |
+
},
|
397 |
+
"widgets_values": [
|
398 |
+
0,
|
399 |
+
1280,
|
400 |
+
960
|
401 |
+
]
|
402 |
+
},
|
403 |
+
{
|
404 |
+
"id": 11,
|
405 |
+
"type": "InstantIDModelLoader",
|
406 |
+
"pos": [
|
407 |
+
-210,
|
408 |
+
-150
|
409 |
+
],
|
410 |
+
"size": [
|
411 |
+
210,
|
412 |
+
60
|
413 |
+
],
|
414 |
+
"flags": {},
|
415 |
+
"order": 5,
|
416 |
+
"mode": 0,
|
417 |
+
"outputs": [
|
418 |
+
{
|
419 |
+
"name": "INSTANTID",
|
420 |
+
"type": "INSTANTID",
|
421 |
+
"links": [
|
422 |
+
197,
|
423 |
+
238
|
424 |
+
],
|
425 |
+
"shape": 3,
|
426 |
+
"slot_index": 0
|
427 |
+
}
|
428 |
+
],
|
429 |
+
"properties": {
|
430 |
+
"Node name for S&R": "InstantIDModelLoader"
|
431 |
+
},
|
432 |
+
"widgets_values": [
|
433 |
+
"ip-adapter.bin"
|
434 |
+
]
|
435 |
+
},
|
436 |
+
{
|
437 |
+
"id": 4,
|
438 |
+
"type": "CheckpointLoaderSimple",
|
439 |
+
"pos": [
|
440 |
+
-312,
|
441 |
+
198
|
442 |
+
],
|
443 |
+
"size": {
|
444 |
+
"0": 315,
|
445 |
+
"1": 98
|
446 |
+
},
|
447 |
+
"flags": {},
|
448 |
+
"order": 6,
|
449 |
+
"mode": 0,
|
450 |
+
"outputs": [
|
451 |
+
{
|
452 |
+
"name": "MODEL",
|
453 |
+
"type": "MODEL",
|
454 |
+
"links": [
|
455 |
+
206
|
456 |
+
],
|
457 |
+
"slot_index": 0
|
458 |
+
},
|
459 |
+
{
|
460 |
+
"name": "CLIP",
|
461 |
+
"type": "CLIP",
|
462 |
+
"links": [
|
463 |
+
122,
|
464 |
+
123,
|
465 |
+
266
|
466 |
+
],
|
467 |
+
"slot_index": 1
|
468 |
+
},
|
469 |
+
{
|
470 |
+
"name": "VAE",
|
471 |
+
"type": "VAE",
|
472 |
+
"links": [
|
473 |
+
253
|
474 |
+
],
|
475 |
+
"slot_index": 2
|
476 |
+
}
|
477 |
+
],
|
478 |
+
"properties": {
|
479 |
+
"Node name for S&R": "CheckpointLoaderSimple"
|
480 |
+
},
|
481 |
+
"widgets_values": [
|
482 |
+
"sdxl/AlbedoBaseXL.safetensors"
|
483 |
+
]
|
484 |
+
},
|
485 |
+
{
|
486 |
+
"id": 87,
|
487 |
+
"type": "MaskComposite",
|
488 |
+
"pos": [
|
489 |
+
1232,
|
490 |
+
583
|
491 |
+
],
|
492 |
+
"size": [
|
493 |
+
210,
|
494 |
+
126
|
495 |
+
],
|
496 |
+
"flags": {},
|
497 |
+
"order": 14,
|
498 |
+
"mode": 0,
|
499 |
+
"inputs": [
|
500 |
+
{
|
501 |
+
"name": "destination",
|
502 |
+
"type": "MASK",
|
503 |
+
"link": 260
|
504 |
+
},
|
505 |
+
{
|
506 |
+
"name": "source",
|
507 |
+
"type": "MASK",
|
508 |
+
"link": 261
|
509 |
+
}
|
510 |
+
],
|
511 |
+
"outputs": [
|
512 |
+
{
|
513 |
+
"name": "MASK",
|
514 |
+
"type": "MASK",
|
515 |
+
"links": [
|
516 |
+
262,
|
517 |
+
263
|
518 |
+
],
|
519 |
+
"shape": 3,
|
520 |
+
"slot_index": 0
|
521 |
+
}
|
522 |
+
],
|
523 |
+
"properties": {
|
524 |
+
"Node name for S&R": "MaskComposite"
|
525 |
+
},
|
526 |
+
"widgets_values": [
|
527 |
+
0,
|
528 |
+
0,
|
529 |
+
"add"
|
530 |
+
]
|
531 |
+
},
|
532 |
+
{
|
533 |
+
"id": 86,
|
534 |
+
"type": "SolidMask",
|
535 |
+
"pos": [
|
536 |
+
970,
|
537 |
+
660
|
538 |
+
],
|
539 |
+
"size": {
|
540 |
+
"0": 210,
|
541 |
+
"1": 106
|
542 |
+
},
|
543 |
+
"flags": {},
|
544 |
+
"order": 7,
|
545 |
+
"mode": 0,
|
546 |
+
"outputs": [
|
547 |
+
{
|
548 |
+
"name": "MASK",
|
549 |
+
"type": "MASK",
|
550 |
+
"links": [
|
551 |
+
261
|
552 |
+
],
|
553 |
+
"shape": 3,
|
554 |
+
"slot_index": 0
|
555 |
+
}
|
556 |
+
],
|
557 |
+
"properties": {
|
558 |
+
"Node name for S&R": "SolidMask"
|
559 |
+
},
|
560 |
+
"widgets_values": [
|
561 |
+
1,
|
562 |
+
640,
|
563 |
+
960
|
564 |
+
]
|
565 |
+
},
|
566 |
+
{
|
567 |
+
"id": 82,
|
568 |
+
"type": "LoadImage",
|
569 |
+
"pos": [
|
570 |
+
591,
|
571 |
+
511
|
572 |
+
],
|
573 |
+
"size": [
|
574 |
+
315,
|
575 |
+
314.0000190734863
|
576 |
+
],
|
577 |
+
"flags": {},
|
578 |
+
"order": 8,
|
579 |
+
"mode": 0,
|
580 |
+
"outputs": [
|
581 |
+
{
|
582 |
+
"name": "IMAGE",
|
583 |
+
"type": "IMAGE",
|
584 |
+
"links": [
|
585 |
+
257,
|
586 |
+
258
|
587 |
+
],
|
588 |
+
"shape": 3,
|
589 |
+
"slot_index": 0
|
590 |
+
},
|
591 |
+
{
|
592 |
+
"name": "MASK",
|
593 |
+
"type": "MASK",
|
594 |
+
"links": null,
|
595 |
+
"shape": 3
|
596 |
+
}
|
597 |
+
],
|
598 |
+
"properties": {
|
599 |
+
"Node name for S&R": "LoadImage"
|
600 |
+
},
|
601 |
+
"widgets_values": [
|
602 |
+
"pose (1).jpg",
|
603 |
+
"image"
|
604 |
+
]
|
605 |
+
},
|
606 |
+
{
|
607 |
+
"id": 40,
|
608 |
+
"type": "CLIPTextEncode",
|
609 |
+
"pos": [
|
610 |
+
146,
|
611 |
+
487
|
612 |
+
],
|
613 |
+
"size": {
|
614 |
+
"0": 286.3603515625,
|
615 |
+
"1": 112.35245513916016
|
616 |
+
},
|
617 |
+
"flags": {},
|
618 |
+
"order": 11,
|
619 |
+
"mode": 0,
|
620 |
+
"inputs": [
|
621 |
+
{
|
622 |
+
"name": "clip",
|
623 |
+
"type": "CLIP",
|
624 |
+
"link": 123
|
625 |
+
}
|
626 |
+
],
|
627 |
+
"outputs": [
|
628 |
+
{
|
629 |
+
"name": "CONDITIONING",
|
630 |
+
"type": "CONDITIONING",
|
631 |
+
"links": [
|
632 |
+
204,
|
633 |
+
278
|
634 |
+
],
|
635 |
+
"shape": 3,
|
636 |
+
"slot_index": 0
|
637 |
+
}
|
638 |
+
],
|
639 |
+
"properties": {
|
640 |
+
"Node name for S&R": "CLIPTextEncode"
|
641 |
+
},
|
642 |
+
"widgets_values": [
|
643 |
+
"photograph, deformed, glitch, noisy, realistic, stock photo, naked"
|
644 |
+
],
|
645 |
+
"color": "#322",
|
646 |
+
"bgcolor": "#533"
|
647 |
+
},
|
648 |
+
{
|
649 |
+
"id": 5,
|
650 |
+
"type": "EmptyLatentImage",
|
651 |
+
"pos": [
|
652 |
+
1431,
|
653 |
+
20
|
654 |
+
],
|
655 |
+
"size": [
|
656 |
+
210,
|
657 |
+
106
|
658 |
+
],
|
659 |
+
"flags": {},
|
660 |
+
"order": 9,
|
661 |
+
"mode": 0,
|
662 |
+
"outputs": [
|
663 |
+
{
|
664 |
+
"name": "LATENT",
|
665 |
+
"type": "LATENT",
|
666 |
+
"links": [
|
667 |
+
2
|
668 |
+
],
|
669 |
+
"slot_index": 0
|
670 |
+
}
|
671 |
+
],
|
672 |
+
"properties": {
|
673 |
+
"Node name for S&R": "EmptyLatentImage"
|
674 |
+
},
|
675 |
+
"widgets_values": [
|
676 |
+
1280,
|
677 |
+
960,
|
678 |
+
1
|
679 |
+
]
|
680 |
+
},
|
681 |
+
{
|
682 |
+
"id": 3,
|
683 |
+
"type": "KSampler",
|
684 |
+
"pos": [
|
685 |
+
1730,
|
686 |
+
-180
|
687 |
+
],
|
688 |
+
"size": {
|
689 |
+
"0": 315,
|
690 |
+
"1": 262
|
691 |
+
},
|
692 |
+
"flags": {},
|
693 |
+
"order": 21,
|
694 |
+
"mode": 0,
|
695 |
+
"inputs": [
|
696 |
+
{
|
697 |
+
"name": "model",
|
698 |
+
"type": "MODEL",
|
699 |
+
"link": 256
|
700 |
+
},
|
701 |
+
{
|
702 |
+
"name": "positive",
|
703 |
+
"type": "CONDITIONING",
|
704 |
+
"link": 249
|
705 |
+
},
|
706 |
+
{
|
707 |
+
"name": "negative",
|
708 |
+
"type": "CONDITIONING",
|
709 |
+
"link": 288
|
710 |
+
},
|
711 |
+
{
|
712 |
+
"name": "latent_image",
|
713 |
+
"type": "LATENT",
|
714 |
+
"link": 2
|
715 |
+
}
|
716 |
+
],
|
717 |
+
"outputs": [
|
718 |
+
{
|
719 |
+
"name": "LATENT",
|
720 |
+
"type": "LATENT",
|
721 |
+
"links": [
|
722 |
+
7
|
723 |
+
],
|
724 |
+
"slot_index": 0
|
725 |
+
}
|
726 |
+
],
|
727 |
+
"properties": {
|
728 |
+
"Node name for S&R": "KSampler"
|
729 |
+
},
|
730 |
+
"widgets_values": [
|
731 |
+
1631594039,
|
732 |
+
"fixed",
|
733 |
+
30,
|
734 |
+
4.5,
|
735 |
+
"ddpm",
|
736 |
+
"normal",
|
737 |
+
1
|
738 |
+
]
|
739 |
+
},
|
740 |
+
{
|
741 |
+
"id": 80,
|
742 |
+
"type": "ConditioningCombine",
|
743 |
+
"pos": [
|
744 |
+
1410,
|
745 |
+
-90
|
746 |
+
],
|
747 |
+
"size": {
|
748 |
+
"0": 228.39999389648438,
|
749 |
+
"1": 46
|
750 |
+
},
|
751 |
+
"flags": {},
|
752 |
+
"order": 20,
|
753 |
+
"mode": 0,
|
754 |
+
"inputs": [
|
755 |
+
{
|
756 |
+
"name": "conditioning_1",
|
757 |
+
"type": "CONDITIONING",
|
758 |
+
"link": 290
|
759 |
+
},
|
760 |
+
{
|
761 |
+
"name": "conditioning_2",
|
762 |
+
"type": "CONDITIONING",
|
763 |
+
"link": 287
|
764 |
+
}
|
765 |
+
],
|
766 |
+
"outputs": [
|
767 |
+
{
|
768 |
+
"name": "CONDITIONING",
|
769 |
+
"type": "CONDITIONING",
|
770 |
+
"links": [
|
771 |
+
288
|
772 |
+
],
|
773 |
+
"shape": 3,
|
774 |
+
"slot_index": 0
|
775 |
+
}
|
776 |
+
],
|
777 |
+
"properties": {
|
778 |
+
"Node name for S&R": "ConditioningCombine"
|
779 |
+
}
|
780 |
+
},
|
781 |
+
{
|
782 |
+
"id": 77,
|
783 |
+
"type": "ApplyInstantID",
|
784 |
+
"pos": [
|
785 |
+
990,
|
786 |
+
-528
|
787 |
+
],
|
788 |
+
"size": {
|
789 |
+
"0": 315,
|
790 |
+
"1": 266
|
791 |
+
},
|
792 |
+
"flags": {},
|
793 |
+
"order": 18,
|
794 |
+
"mode": 0,
|
795 |
+
"inputs": [
|
796 |
+
{
|
797 |
+
"name": "instantid",
|
798 |
+
"type": "INSTANTID",
|
799 |
+
"link": 238
|
800 |
+
},
|
801 |
+
{
|
802 |
+
"name": "insightface",
|
803 |
+
"type": "FACEANALYSIS",
|
804 |
+
"link": 239
|
805 |
+
},
|
806 |
+
{
|
807 |
+
"name": "control_net",
|
808 |
+
"type": "CONTROL_NET",
|
809 |
+
"link": 240
|
810 |
+
},
|
811 |
+
{
|
812 |
+
"name": "image",
|
813 |
+
"type": "IMAGE",
|
814 |
+
"link": 246
|
815 |
+
},
|
816 |
+
{
|
817 |
+
"name": "model",
|
818 |
+
"type": "MODEL",
|
819 |
+
"link": 255
|
820 |
+
},
|
821 |
+
{
|
822 |
+
"name": "positive",
|
823 |
+
"type": "CONDITIONING",
|
824 |
+
"link": 272
|
825 |
+
},
|
826 |
+
{
|
827 |
+
"name": "negative",
|
828 |
+
"type": "CONDITIONING",
|
829 |
+
"link": 278
|
830 |
+
},
|
831 |
+
{
|
832 |
+
"name": "image_kps",
|
833 |
+
"type": "IMAGE",
|
834 |
+
"link": 259
|
835 |
+
},
|
836 |
+
{
|
837 |
+
"name": "mask",
|
838 |
+
"type": "MASK",
|
839 |
+
"link": 264
|
840 |
+
}
|
841 |
+
],
|
842 |
+
"outputs": [
|
843 |
+
{
|
844 |
+
"name": "MODEL",
|
845 |
+
"type": "MODEL",
|
846 |
+
"links": [
|
847 |
+
256
|
848 |
+
],
|
849 |
+
"shape": 3,
|
850 |
+
"slot_index": 0
|
851 |
+
},
|
852 |
+
{
|
853 |
+
"name": "POSITIVE",
|
854 |
+
"type": "CONDITIONING",
|
855 |
+
"links": [
|
856 |
+
247
|
857 |
+
],
|
858 |
+
"shape": 3,
|
859 |
+
"slot_index": 1
|
860 |
+
},
|
861 |
+
{
|
862 |
+
"name": "NEGATIVE",
|
863 |
+
"type": "CONDITIONING",
|
864 |
+
"links": [
|
865 |
+
290
|
866 |
+
],
|
867 |
+
"shape": 3,
|
868 |
+
"slot_index": 2
|
869 |
+
}
|
870 |
+
],
|
871 |
+
"properties": {
|
872 |
+
"Node name for S&R": "ApplyInstantID"
|
873 |
+
},
|
874 |
+
"widgets_values": [
|
875 |
+
0.8,
|
876 |
+
0,
|
877 |
+
1
|
878 |
+
]
|
879 |
+
},
|
880 |
+
{
|
881 |
+
"id": 60,
|
882 |
+
"type": "ApplyInstantID",
|
883 |
+
"pos": [
|
884 |
+
991,
|
885 |
+
73
|
886 |
+
],
|
887 |
+
"size": {
|
888 |
+
"0": 315,
|
889 |
+
"1": 266
|
890 |
+
},
|
891 |
+
"flags": {},
|
892 |
+
"order": 16,
|
893 |
+
"mode": 0,
|
894 |
+
"inputs": [
|
895 |
+
{
|
896 |
+
"name": "instantid",
|
897 |
+
"type": "INSTANTID",
|
898 |
+
"link": 197
|
899 |
+
},
|
900 |
+
{
|
901 |
+
"name": "insightface",
|
902 |
+
"type": "FACEANALYSIS",
|
903 |
+
"link": 198
|
904 |
+
},
|
905 |
+
{
|
906 |
+
"name": "control_net",
|
907 |
+
"type": "CONTROL_NET",
|
908 |
+
"link": 199
|
909 |
+
},
|
910 |
+
{
|
911 |
+
"name": "image",
|
912 |
+
"type": "IMAGE",
|
913 |
+
"link": 214
|
914 |
+
},
|
915 |
+
{
|
916 |
+
"name": "model",
|
917 |
+
"type": "MODEL",
|
918 |
+
"link": 206
|
919 |
+
},
|
920 |
+
{
|
921 |
+
"name": "positive",
|
922 |
+
"type": "CONDITIONING",
|
923 |
+
"link": 203
|
924 |
+
},
|
925 |
+
{
|
926 |
+
"name": "negative",
|
927 |
+
"type": "CONDITIONING",
|
928 |
+
"link": 204
|
929 |
+
},
|
930 |
+
{
|
931 |
+
"name": "image_kps",
|
932 |
+
"type": "IMAGE",
|
933 |
+
"link": 257
|
934 |
+
},
|
935 |
+
{
|
936 |
+
"name": "mask",
|
937 |
+
"type": "MASK",
|
938 |
+
"link": 262
|
939 |
+
}
|
940 |
+
],
|
941 |
+
"outputs": [
|
942 |
+
{
|
943 |
+
"name": "MODEL",
|
944 |
+
"type": "MODEL",
|
945 |
+
"links": [
|
946 |
+
255
|
947 |
+
],
|
948 |
+
"shape": 3,
|
949 |
+
"slot_index": 0
|
950 |
+
},
|
951 |
+
{
|
952 |
+
"name": "POSITIVE",
|
953 |
+
"type": "CONDITIONING",
|
954 |
+
"links": [
|
955 |
+
248
|
956 |
+
],
|
957 |
+
"shape": 3,
|
958 |
+
"slot_index": 1
|
959 |
+
},
|
960 |
+
{
|
961 |
+
"name": "NEGATIVE",
|
962 |
+
"type": "CONDITIONING",
|
963 |
+
"links": [
|
964 |
+
287
|
965 |
+
],
|
966 |
+
"shape": 3,
|
967 |
+
"slot_index": 2
|
968 |
+
}
|
969 |
+
],
|
970 |
+
"properties": {
|
971 |
+
"Node name for S&R": "ApplyInstantID"
|
972 |
+
},
|
973 |
+
"widgets_values": [
|
974 |
+
0.9,
|
975 |
+
0,
|
976 |
+
1
|
977 |
+
]
|
978 |
+
},
|
979 |
+
{
|
980 |
+
"id": 89,
|
981 |
+
"type": "CLIPTextEncode",
|
982 |
+
"pos": [
|
983 |
+
314,
|
984 |
+
-421
|
985 |
+
],
|
986 |
+
"size": {
|
987 |
+
"0": 291.9967346191406,
|
988 |
+
"1": 128.62518310546875
|
989 |
+
},
|
990 |
+
"flags": {},
|
991 |
+
"order": 12,
|
992 |
+
"mode": 0,
|
993 |
+
"inputs": [
|
994 |
+
{
|
995 |
+
"name": "clip",
|
996 |
+
"type": "CLIP",
|
997 |
+
"link": 266
|
998 |
+
}
|
999 |
+
],
|
1000 |
+
"outputs": [
|
1001 |
+
{
|
1002 |
+
"name": "CONDITIONING",
|
1003 |
+
"type": "CONDITIONING",
|
1004 |
+
"links": [
|
1005 |
+
272
|
1006 |
+
],
|
1007 |
+
"shape": 3,
|
1008 |
+
"slot_index": 0
|
1009 |
+
}
|
1010 |
+
],
|
1011 |
+
"properties": {
|
1012 |
+
"Node name for S&R": "CLIPTextEncode"
|
1013 |
+
},
|
1014 |
+
"widgets_values": [
|
1015 |
+
"comic male character. graphic illustration, comic art, graphic novel art, vibrant, highly detailed. New York background"
|
1016 |
+
],
|
1017 |
+
"color": "#232",
|
1018 |
+
"bgcolor": "#353"
|
1019 |
+
},
|
1020 |
+
{
|
1021 |
+
"id": 39,
|
1022 |
+
"type": "CLIPTextEncode",
|
1023 |
+
"pos": [
|
1024 |
+
309,
|
1025 |
+
171
|
1026 |
+
],
|
1027 |
+
"size": {
|
1028 |
+
"0": 291.9967346191406,
|
1029 |
+
"1": 128.62518310546875
|
1030 |
+
},
|
1031 |
+
"flags": {},
|
1032 |
+
"order": 10,
|
1033 |
+
"mode": 0,
|
1034 |
+
"inputs": [
|
1035 |
+
{
|
1036 |
+
"name": "clip",
|
1037 |
+
"type": "CLIP",
|
1038 |
+
"link": 122
|
1039 |
+
}
|
1040 |
+
],
|
1041 |
+
"outputs": [
|
1042 |
+
{
|
1043 |
+
"name": "CONDITIONING",
|
1044 |
+
"type": "CONDITIONING",
|
1045 |
+
"links": [
|
1046 |
+
203
|
1047 |
+
],
|
1048 |
+
"shape": 3,
|
1049 |
+
"slot_index": 0
|
1050 |
+
}
|
1051 |
+
],
|
1052 |
+
"properties": {
|
1053 |
+
"Node name for S&R": "CLIPTextEncode"
|
1054 |
+
},
|
1055 |
+
"widgets_values": [
|
1056 |
+
"comic female character. graphic illustration, comic art, graphic novel art, vibrant, highly detailed. New York background"
|
1057 |
+
],
|
1058 |
+
"color": "#232",
|
1059 |
+
"bgcolor": "#353"
|
1060 |
+
}
|
1061 |
+
],
|
1062 |
+
"links": [
|
1063 |
+
[
|
1064 |
+
2,
|
1065 |
+
5,
|
1066 |
+
0,
|
1067 |
+
3,
|
1068 |
+
3,
|
1069 |
+
"LATENT"
|
1070 |
+
],
|
1071 |
+
[
|
1072 |
+
7,
|
1073 |
+
3,
|
1074 |
+
0,
|
1075 |
+
8,
|
1076 |
+
0,
|
1077 |
+
"LATENT"
|
1078 |
+
],
|
1079 |
+
[
|
1080 |
+
19,
|
1081 |
+
8,
|
1082 |
+
0,
|
1083 |
+
15,
|
1084 |
+
0,
|
1085 |
+
"IMAGE"
|
1086 |
+
],
|
1087 |
+
[
|
1088 |
+
122,
|
1089 |
+
4,
|
1090 |
+
1,
|
1091 |
+
39,
|
1092 |
+
0,
|
1093 |
+
"CLIP"
|
1094 |
+
],
|
1095 |
+
[
|
1096 |
+
123,
|
1097 |
+
4,
|
1098 |
+
1,
|
1099 |
+
40,
|
1100 |
+
0,
|
1101 |
+
"CLIP"
|
1102 |
+
],
|
1103 |
+
[
|
1104 |
+
197,
|
1105 |
+
11,
|
1106 |
+
0,
|
1107 |
+
60,
|
1108 |
+
0,
|
1109 |
+
"INSTANTID"
|
1110 |
+
],
|
1111 |
+
[
|
1112 |
+
198,
|
1113 |
+
38,
|
1114 |
+
0,
|
1115 |
+
60,
|
1116 |
+
1,
|
1117 |
+
"FACEANALYSIS"
|
1118 |
+
],
|
1119 |
+
[
|
1120 |
+
199,
|
1121 |
+
16,
|
1122 |
+
0,
|
1123 |
+
60,
|
1124 |
+
2,
|
1125 |
+
"CONTROL_NET"
|
1126 |
+
],
|
1127 |
+
[
|
1128 |
+
203,
|
1129 |
+
39,
|
1130 |
+
0,
|
1131 |
+
60,
|
1132 |
+
5,
|
1133 |
+
"CONDITIONING"
|
1134 |
+
],
|
1135 |
+
[
|
1136 |
+
204,
|
1137 |
+
40,
|
1138 |
+
0,
|
1139 |
+
60,
|
1140 |
+
6,
|
1141 |
+
"CONDITIONING"
|
1142 |
+
],
|
1143 |
+
[
|
1144 |
+
206,
|
1145 |
+
4,
|
1146 |
+
0,
|
1147 |
+
60,
|
1148 |
+
4,
|
1149 |
+
"MODEL"
|
1150 |
+
],
|
1151 |
+
[
|
1152 |
+
214,
|
1153 |
+
13,
|
1154 |
+
0,
|
1155 |
+
60,
|
1156 |
+
3,
|
1157 |
+
"IMAGE"
|
1158 |
+
],
|
1159 |
+
[
|
1160 |
+
238,
|
1161 |
+
11,
|
1162 |
+
0,
|
1163 |
+
77,
|
1164 |
+
0,
|
1165 |
+
"INSTANTID"
|
1166 |
+
],
|
1167 |
+
[
|
1168 |
+
239,
|
1169 |
+
38,
|
1170 |
+
0,
|
1171 |
+
77,
|
1172 |
+
1,
|
1173 |
+
"FACEANALYSIS"
|
1174 |
+
],
|
1175 |
+
[
|
1176 |
+
240,
|
1177 |
+
16,
|
1178 |
+
0,
|
1179 |
+
77,
|
1180 |
+
2,
|
1181 |
+
"CONTROL_NET"
|
1182 |
+
],
|
1183 |
+
[
|
1184 |
+
246,
|
1185 |
+
78,
|
1186 |
+
0,
|
1187 |
+
77,
|
1188 |
+
3,
|
1189 |
+
"IMAGE"
|
1190 |
+
],
|
1191 |
+
[
|
1192 |
+
247,
|
1193 |
+
77,
|
1194 |
+
1,
|
1195 |
+
79,
|
1196 |
+
0,
|
1197 |
+
"CONDITIONING"
|
1198 |
+
],
|
1199 |
+
[
|
1200 |
+
248,
|
1201 |
+
60,
|
1202 |
+
1,
|
1203 |
+
79,
|
1204 |
+
1,
|
1205 |
+
"CONDITIONING"
|
1206 |
+
],
|
1207 |
+
[
|
1208 |
+
249,
|
1209 |
+
79,
|
1210 |
+
0,
|
1211 |
+
3,
|
1212 |
+
1,
|
1213 |
+
"CONDITIONING"
|
1214 |
+
],
|
1215 |
+
[
|
1216 |
+
253,
|
1217 |
+
4,
|
1218 |
+
2,
|
1219 |
+
81,
|
1220 |
+
0,
|
1221 |
+
"*"
|
1222 |
+
],
|
1223 |
+
[
|
1224 |
+
254,
|
1225 |
+
81,
|
1226 |
+
0,
|
1227 |
+
8,
|
1228 |
+
1,
|
1229 |
+
"VAE"
|
1230 |
+
],
|
1231 |
+
[
|
1232 |
+
255,
|
1233 |
+
60,
|
1234 |
+
0,
|
1235 |
+
77,
|
1236 |
+
4,
|
1237 |
+
"MODEL"
|
1238 |
+
],
|
1239 |
+
[
|
1240 |
+
256,
|
1241 |
+
77,
|
1242 |
+
0,
|
1243 |
+
3,
|
1244 |
+
0,
|
1245 |
+
"MODEL"
|
1246 |
+
],
|
1247 |
+
[
|
1248 |
+
257,
|
1249 |
+
82,
|
1250 |
+
0,
|
1251 |
+
60,
|
1252 |
+
7,
|
1253 |
+
"IMAGE"
|
1254 |
+
],
|
1255 |
+
[
|
1256 |
+
258,
|
1257 |
+
82,
|
1258 |
+
0,
|
1259 |
+
84,
|
1260 |
+
0,
|
1261 |
+
"IMAGE"
|
1262 |
+
],
|
1263 |
+
[
|
1264 |
+
259,
|
1265 |
+
84,
|
1266 |
+
0,
|
1267 |
+
77,
|
1268 |
+
7,
|
1269 |
+
"IMAGE"
|
1270 |
+
],
|
1271 |
+
[
|
1272 |
+
260,
|
1273 |
+
85,
|
1274 |
+
0,
|
1275 |
+
87,
|
1276 |
+
0,
|
1277 |
+
"MASK"
|
1278 |
+
],
|
1279 |
+
[
|
1280 |
+
261,
|
1281 |
+
86,
|
1282 |
+
0,
|
1283 |
+
87,
|
1284 |
+
1,
|
1285 |
+
"MASK"
|
1286 |
+
],
|
1287 |
+
[
|
1288 |
+
262,
|
1289 |
+
87,
|
1290 |
+
0,
|
1291 |
+
60,
|
1292 |
+
8,
|
1293 |
+
"MASK"
|
1294 |
+
],
|
1295 |
+
[
|
1296 |
+
263,
|
1297 |
+
87,
|
1298 |
+
0,
|
1299 |
+
88,
|
1300 |
+
0,
|
1301 |
+
"MASK"
|
1302 |
+
],
|
1303 |
+
[
|
1304 |
+
264,
|
1305 |
+
88,
|
1306 |
+
0,
|
1307 |
+
77,
|
1308 |
+
8,
|
1309 |
+
"MASK"
|
1310 |
+
],
|
1311 |
+
[
|
1312 |
+
266,
|
1313 |
+
4,
|
1314 |
+
1,
|
1315 |
+
89,
|
1316 |
+
0,
|
1317 |
+
"CLIP"
|
1318 |
+
],
|
1319 |
+
[
|
1320 |
+
272,
|
1321 |
+
89,
|
1322 |
+
0,
|
1323 |
+
77,
|
1324 |
+
5,
|
1325 |
+
"CONDITIONING"
|
1326 |
+
],
|
1327 |
+
[
|
1328 |
+
278,
|
1329 |
+
40,
|
1330 |
+
0,
|
1331 |
+
77,
|
1332 |
+
6,
|
1333 |
+
"CONDITIONING"
|
1334 |
+
],
|
1335 |
+
[
|
1336 |
+
287,
|
1337 |
+
60,
|
1338 |
+
2,
|
1339 |
+
80,
|
1340 |
+
1,
|
1341 |
+
"CONDITIONING"
|
1342 |
+
],
|
1343 |
+
[
|
1344 |
+
288,
|
1345 |
+
80,
|
1346 |
+
0,
|
1347 |
+
3,
|
1348 |
+
2,
|
1349 |
+
"CONDITIONING"
|
1350 |
+
],
|
1351 |
+
[
|
1352 |
+
290,
|
1353 |
+
77,
|
1354 |
+
2,
|
1355 |
+
80,
|
1356 |
+
0,
|
1357 |
+
"CONDITIONING"
|
1358 |
+
]
|
1359 |
+
],
|
1360 |
+
"groups": [],
|
1361 |
+
"config": {},
|
1362 |
+
"extra": {},
|
1363 |
+
"version": 0.4
|
1364 |
+
}
|
ComfyUI_InstantID/examples/InstantID_posed.json
ADDED
@@ -0,0 +1,704 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"last_node_id": 67,
|
3 |
+
"last_link_id": 221,
|
4 |
+
"nodes": [
|
5 |
+
{
|
6 |
+
"id": 11,
|
7 |
+
"type": "InstantIDModelLoader",
|
8 |
+
"pos": [
|
9 |
+
560,
|
10 |
+
70
|
11 |
+
],
|
12 |
+
"size": {
|
13 |
+
"0": 238.72393798828125,
|
14 |
+
"1": 58
|
15 |
+
},
|
16 |
+
"flags": {},
|
17 |
+
"order": 0,
|
18 |
+
"mode": 0,
|
19 |
+
"outputs": [
|
20 |
+
{
|
21 |
+
"name": "INSTANTID",
|
22 |
+
"type": "INSTANTID",
|
23 |
+
"links": [
|
24 |
+
197
|
25 |
+
],
|
26 |
+
"shape": 3,
|
27 |
+
"slot_index": 0
|
28 |
+
}
|
29 |
+
],
|
30 |
+
"properties": {
|
31 |
+
"Node name for S&R": "InstantIDModelLoader"
|
32 |
+
},
|
33 |
+
"widgets_values": [
|
34 |
+
"ip-adapter.bin"
|
35 |
+
]
|
36 |
+
},
|
37 |
+
{
|
38 |
+
"id": 38,
|
39 |
+
"type": "InstantIDFaceAnalysis",
|
40 |
+
"pos": [
|
41 |
+
570,
|
42 |
+
180
|
43 |
+
],
|
44 |
+
"size": {
|
45 |
+
"0": 227.09793090820312,
|
46 |
+
"1": 58
|
47 |
+
},
|
48 |
+
"flags": {},
|
49 |
+
"order": 1,
|
50 |
+
"mode": 0,
|
51 |
+
"outputs": [
|
52 |
+
{
|
53 |
+
"name": "FACEANALYSIS",
|
54 |
+
"type": "FACEANALYSIS",
|
55 |
+
"links": [
|
56 |
+
198
|
57 |
+
],
|
58 |
+
"shape": 3,
|
59 |
+
"slot_index": 0
|
60 |
+
}
|
61 |
+
],
|
62 |
+
"properties": {
|
63 |
+
"Node name for S&R": "InstantIDFaceAnalysis"
|
64 |
+
},
|
65 |
+
"widgets_values": [
|
66 |
+
"CPU"
|
67 |
+
]
|
68 |
+
},
|
69 |
+
{
|
70 |
+
"id": 16,
|
71 |
+
"type": "ControlNetLoader",
|
72 |
+
"pos": [
|
73 |
+
560,
|
74 |
+
290
|
75 |
+
],
|
76 |
+
"size": {
|
77 |
+
"0": 250.07241821289062,
|
78 |
+
"1": 58
|
79 |
+
},
|
80 |
+
"flags": {},
|
81 |
+
"order": 2,
|
82 |
+
"mode": 0,
|
83 |
+
"outputs": [
|
84 |
+
{
|
85 |
+
"name": "CONTROL_NET",
|
86 |
+
"type": "CONTROL_NET",
|
87 |
+
"links": [
|
88 |
+
199
|
89 |
+
],
|
90 |
+
"shape": 3,
|
91 |
+
"slot_index": 0
|
92 |
+
}
|
93 |
+
],
|
94 |
+
"properties": {
|
95 |
+
"Node name for S&R": "ControlNetLoader"
|
96 |
+
},
|
97 |
+
"widgets_values": [
|
98 |
+
"instantid/diffusion_pytorch_model.safetensors"
|
99 |
+
]
|
100 |
+
},
|
101 |
+
{
|
102 |
+
"id": 15,
|
103 |
+
"type": "PreviewImage",
|
104 |
+
"pos": [
|
105 |
+
1670,
|
106 |
+
300
|
107 |
+
],
|
108 |
+
"size": {
|
109 |
+
"0": 584.0855712890625,
|
110 |
+
"1": 610.4592895507812
|
111 |
+
},
|
112 |
+
"flags": {},
|
113 |
+
"order": 12,
|
114 |
+
"mode": 0,
|
115 |
+
"inputs": [
|
116 |
+
{
|
117 |
+
"name": "images",
|
118 |
+
"type": "IMAGE",
|
119 |
+
"link": 19
|
120 |
+
}
|
121 |
+
],
|
122 |
+
"properties": {
|
123 |
+
"Node name for S&R": "PreviewImage"
|
124 |
+
}
|
125 |
+
},
|
126 |
+
{
|
127 |
+
"id": 5,
|
128 |
+
"type": "EmptyLatentImage",
|
129 |
+
"pos": [
|
130 |
+
910,
|
131 |
+
540
|
132 |
+
],
|
133 |
+
"size": {
|
134 |
+
"0": 315,
|
135 |
+
"1": 106
|
136 |
+
},
|
137 |
+
"flags": {},
|
138 |
+
"order": 3,
|
139 |
+
"mode": 0,
|
140 |
+
"outputs": [
|
141 |
+
{
|
142 |
+
"name": "LATENT",
|
143 |
+
"type": "LATENT",
|
144 |
+
"links": [
|
145 |
+
2
|
146 |
+
],
|
147 |
+
"slot_index": 0
|
148 |
+
}
|
149 |
+
],
|
150 |
+
"properties": {
|
151 |
+
"Node name for S&R": "EmptyLatentImage"
|
152 |
+
},
|
153 |
+
"widgets_values": [
|
154 |
+
1016,
|
155 |
+
1016,
|
156 |
+
1
|
157 |
+
]
|
158 |
+
},
|
159 |
+
{
|
160 |
+
"id": 8,
|
161 |
+
"type": "VAEDecode",
|
162 |
+
"pos": [
|
163 |
+
1670,
|
164 |
+
210
|
165 |
+
],
|
166 |
+
"size": {
|
167 |
+
"0": 210,
|
168 |
+
"1": 46
|
169 |
+
},
|
170 |
+
"flags": {},
|
171 |
+
"order": 11,
|
172 |
+
"mode": 0,
|
173 |
+
"inputs": [
|
174 |
+
{
|
175 |
+
"name": "samples",
|
176 |
+
"type": "LATENT",
|
177 |
+
"link": 7
|
178 |
+
},
|
179 |
+
{
|
180 |
+
"name": "vae",
|
181 |
+
"type": "VAE",
|
182 |
+
"link": 8
|
183 |
+
}
|
184 |
+
],
|
185 |
+
"outputs": [
|
186 |
+
{
|
187 |
+
"name": "IMAGE",
|
188 |
+
"type": "IMAGE",
|
189 |
+
"links": [
|
190 |
+
19
|
191 |
+
],
|
192 |
+
"slot_index": 0
|
193 |
+
}
|
194 |
+
],
|
195 |
+
"properties": {
|
196 |
+
"Node name for S&R": "VAEDecode"
|
197 |
+
}
|
198 |
+
},
|
199 |
+
{
|
200 |
+
"id": 60,
|
201 |
+
"type": "ApplyInstantID",
|
202 |
+
"pos": [
|
203 |
+
910,
|
204 |
+
210
|
205 |
+
],
|
206 |
+
"size": {
|
207 |
+
"0": 315,
|
208 |
+
"1": 266
|
209 |
+
},
|
210 |
+
"flags": {},
|
211 |
+
"order": 9,
|
212 |
+
"mode": 0,
|
213 |
+
"inputs": [
|
214 |
+
{
|
215 |
+
"name": "instantid",
|
216 |
+
"type": "INSTANTID",
|
217 |
+
"link": 197
|
218 |
+
},
|
219 |
+
{
|
220 |
+
"name": "insightface",
|
221 |
+
"type": "FACEANALYSIS",
|
222 |
+
"link": 198
|
223 |
+
},
|
224 |
+
{
|
225 |
+
"name": "control_net",
|
226 |
+
"type": "CONTROL_NET",
|
227 |
+
"link": 199
|
228 |
+
},
|
229 |
+
{
|
230 |
+
"name": "image",
|
231 |
+
"type": "IMAGE",
|
232 |
+
"link": 214
|
233 |
+
},
|
234 |
+
{
|
235 |
+
"name": "model",
|
236 |
+
"type": "MODEL",
|
237 |
+
"link": 206
|
238 |
+
},
|
239 |
+
{
|
240 |
+
"name": "positive",
|
241 |
+
"type": "CONDITIONING",
|
242 |
+
"link": 203
|
243 |
+
},
|
244 |
+
{
|
245 |
+
"name": "negative",
|
246 |
+
"type": "CONDITIONING",
|
247 |
+
"link": 204
|
248 |
+
},
|
249 |
+
{
|
250 |
+
"name": "image_kps",
|
251 |
+
"type": "IMAGE",
|
252 |
+
"link": 221
|
253 |
+
},
|
254 |
+
{
|
255 |
+
"name": "mask",
|
256 |
+
"type": "MASK",
|
257 |
+
"link": null
|
258 |
+
}
|
259 |
+
],
|
260 |
+
"outputs": [
|
261 |
+
{
|
262 |
+
"name": "MODEL",
|
263 |
+
"type": "MODEL",
|
264 |
+
"links": [
|
265 |
+
220
|
266 |
+
],
|
267 |
+
"shape": 3,
|
268 |
+
"slot_index": 0
|
269 |
+
},
|
270 |
+
{
|
271 |
+
"name": "POSITIVE",
|
272 |
+
"type": "CONDITIONING",
|
273 |
+
"links": [
|
274 |
+
200
|
275 |
+
],
|
276 |
+
"shape": 3,
|
277 |
+
"slot_index": 1
|
278 |
+
},
|
279 |
+
{
|
280 |
+
"name": "NEGATIVE",
|
281 |
+
"type": "CONDITIONING",
|
282 |
+
"links": [
|
283 |
+
201
|
284 |
+
],
|
285 |
+
"shape": 3,
|
286 |
+
"slot_index": 2
|
287 |
+
}
|
288 |
+
],
|
289 |
+
"properties": {
|
290 |
+
"Node name for S&R": "ApplyInstantID"
|
291 |
+
},
|
292 |
+
"widgets_values": [
|
293 |
+
0.8,
|
294 |
+
0,
|
295 |
+
1
|
296 |
+
]
|
297 |
+
},
|
298 |
+
{
|
299 |
+
"id": 39,
|
300 |
+
"type": "CLIPTextEncode",
|
301 |
+
"pos": [
|
302 |
+
520,
|
303 |
+
430
|
304 |
+
],
|
305 |
+
"size": {
|
306 |
+
"0": 291.9967346191406,
|
307 |
+
"1": 128.62518310546875
|
308 |
+
},
|
309 |
+
"flags": {},
|
310 |
+
"order": 7,
|
311 |
+
"mode": 0,
|
312 |
+
"inputs": [
|
313 |
+
{
|
314 |
+
"name": "clip",
|
315 |
+
"type": "CLIP",
|
316 |
+
"link": 122
|
317 |
+
}
|
318 |
+
],
|
319 |
+
"outputs": [
|
320 |
+
{
|
321 |
+
"name": "CONDITIONING",
|
322 |
+
"type": "CONDITIONING",
|
323 |
+
"links": [
|
324 |
+
203
|
325 |
+
],
|
326 |
+
"shape": 3,
|
327 |
+
"slot_index": 0
|
328 |
+
}
|
329 |
+
],
|
330 |
+
"properties": {
|
331 |
+
"Node name for S&R": "CLIPTextEncode"
|
332 |
+
},
|
333 |
+
"widgets_values": [
|
334 |
+
"comic character. graphic illustration, comic art, graphic novel art, vibrant, highly detailed"
|
335 |
+
]
|
336 |
+
},
|
337 |
+
{
|
338 |
+
"id": 40,
|
339 |
+
"type": "CLIPTextEncode",
|
340 |
+
"pos": [
|
341 |
+
520,
|
342 |
+
620
|
343 |
+
],
|
344 |
+
"size": {
|
345 |
+
"0": 286.3603515625,
|
346 |
+
"1": 112.35245513916016
|
347 |
+
},
|
348 |
+
"flags": {},
|
349 |
+
"order": 8,
|
350 |
+
"mode": 0,
|
351 |
+
"inputs": [
|
352 |
+
{
|
353 |
+
"name": "clip",
|
354 |
+
"type": "CLIP",
|
355 |
+
"link": 123
|
356 |
+
}
|
357 |
+
],
|
358 |
+
"outputs": [
|
359 |
+
{
|
360 |
+
"name": "CONDITIONING",
|
361 |
+
"type": "CONDITIONING",
|
362 |
+
"links": [
|
363 |
+
204
|
364 |
+
],
|
365 |
+
"shape": 3,
|
366 |
+
"slot_index": 0
|
367 |
+
}
|
368 |
+
],
|
369 |
+
"properties": {
|
370 |
+
"Node name for S&R": "CLIPTextEncode"
|
371 |
+
},
|
372 |
+
"widgets_values": [
|
373 |
+
"photograph, deformed, glitch, noisy, realistic, stock photo"
|
374 |
+
]
|
375 |
+
},
|
376 |
+
{
|
377 |
+
"id": 4,
|
378 |
+
"type": "CheckpointLoaderSimple",
|
379 |
+
"pos": [
|
380 |
+
70,
|
381 |
+
520
|
382 |
+
],
|
383 |
+
"size": {
|
384 |
+
"0": 315,
|
385 |
+
"1": 98
|
386 |
+
},
|
387 |
+
"flags": {},
|
388 |
+
"order": 4,
|
389 |
+
"mode": 0,
|
390 |
+
"outputs": [
|
391 |
+
{
|
392 |
+
"name": "MODEL",
|
393 |
+
"type": "MODEL",
|
394 |
+
"links": [
|
395 |
+
206
|
396 |
+
],
|
397 |
+
"slot_index": 0
|
398 |
+
},
|
399 |
+
{
|
400 |
+
"name": "CLIP",
|
401 |
+
"type": "CLIP",
|
402 |
+
"links": [
|
403 |
+
122,
|
404 |
+
123
|
405 |
+
],
|
406 |
+
"slot_index": 1
|
407 |
+
},
|
408 |
+
{
|
409 |
+
"name": "VAE",
|
410 |
+
"type": "VAE",
|
411 |
+
"links": [
|
412 |
+
8
|
413 |
+
],
|
414 |
+
"slot_index": 2
|
415 |
+
}
|
416 |
+
],
|
417 |
+
"properties": {
|
418 |
+
"Node name for S&R": "CheckpointLoaderSimple"
|
419 |
+
},
|
420 |
+
"widgets_values": [
|
421 |
+
"sdxl/AlbedoBaseXL.safetensors"
|
422 |
+
]
|
423 |
+
},
|
424 |
+
{
|
425 |
+
"id": 13,
|
426 |
+
"type": "LoadImage",
|
427 |
+
"pos": [
|
428 |
+
290,
|
429 |
+
70
|
430 |
+
],
|
431 |
+
"size": {
|
432 |
+
"0": 210,
|
433 |
+
"1": 314
|
434 |
+
},
|
435 |
+
"flags": {},
|
436 |
+
"order": 5,
|
437 |
+
"mode": 0,
|
438 |
+
"outputs": [
|
439 |
+
{
|
440 |
+
"name": "IMAGE",
|
441 |
+
"type": "IMAGE",
|
442 |
+
"links": [
|
443 |
+
214
|
444 |
+
],
|
445 |
+
"shape": 3,
|
446 |
+
"slot_index": 0
|
447 |
+
},
|
448 |
+
{
|
449 |
+
"name": "MASK",
|
450 |
+
"type": "MASK",
|
451 |
+
"links": null,
|
452 |
+
"shape": 3
|
453 |
+
}
|
454 |
+
],
|
455 |
+
"properties": {
|
456 |
+
"Node name for S&R": "LoadImage"
|
457 |
+
},
|
458 |
+
"widgets_values": [
|
459 |
+
"joseph-gonzalez-iFgRcqHznqg-unsplash.jpg",
|
460 |
+
"image"
|
461 |
+
]
|
462 |
+
},
|
463 |
+
{
|
464 |
+
"id": 67,
|
465 |
+
"type": "LoadImage",
|
466 |
+
"pos": [
|
467 |
+
592,
|
468 |
+
781
|
469 |
+
],
|
470 |
+
"size": {
|
471 |
+
"0": 210,
|
472 |
+
"1": 314
|
473 |
+
},
|
474 |
+
"flags": {},
|
475 |
+
"order": 6,
|
476 |
+
"mode": 0,
|
477 |
+
"outputs": [
|
478 |
+
{
|
479 |
+
"name": "IMAGE",
|
480 |
+
"type": "IMAGE",
|
481 |
+
"links": [
|
482 |
+
221
|
483 |
+
],
|
484 |
+
"shape": 3,
|
485 |
+
"slot_index": 0
|
486 |
+
},
|
487 |
+
{
|
488 |
+
"name": "MASK",
|
489 |
+
"type": "MASK",
|
490 |
+
"links": null,
|
491 |
+
"shape": 3
|
492 |
+
}
|
493 |
+
],
|
494 |
+
"properties": {
|
495 |
+
"Node name for S&R": "LoadImage"
|
496 |
+
},
|
497 |
+
"widgets_values": [
|
498 |
+
"miranda.jpg",
|
499 |
+
"image"
|
500 |
+
]
|
501 |
+
},
|
502 |
+
{
|
503 |
+
"id": 3,
|
504 |
+
"type": "KSampler",
|
505 |
+
"pos": [
|
506 |
+
1300,
|
507 |
+
210
|
508 |
+
],
|
509 |
+
"size": {
|
510 |
+
"0": 315,
|
511 |
+
"1": 262
|
512 |
+
},
|
513 |
+
"flags": {},
|
514 |
+
"order": 10,
|
515 |
+
"mode": 0,
|
516 |
+
"inputs": [
|
517 |
+
{
|
518 |
+
"name": "model",
|
519 |
+
"type": "MODEL",
|
520 |
+
"link": 220
|
521 |
+
},
|
522 |
+
{
|
523 |
+
"name": "positive",
|
524 |
+
"type": "CONDITIONING",
|
525 |
+
"link": 200
|
526 |
+
},
|
527 |
+
{
|
528 |
+
"name": "negative",
|
529 |
+
"type": "CONDITIONING",
|
530 |
+
"link": 201
|
531 |
+
},
|
532 |
+
{
|
533 |
+
"name": "latent_image",
|
534 |
+
"type": "LATENT",
|
535 |
+
"link": 2
|
536 |
+
}
|
537 |
+
],
|
538 |
+
"outputs": [
|
539 |
+
{
|
540 |
+
"name": "LATENT",
|
541 |
+
"type": "LATENT",
|
542 |
+
"links": [
|
543 |
+
7
|
544 |
+
],
|
545 |
+
"slot_index": 0
|
546 |
+
}
|
547 |
+
],
|
548 |
+
"properties": {
|
549 |
+
"Node name for S&R": "KSampler"
|
550 |
+
},
|
551 |
+
"widgets_values": [
|
552 |
+
1631591431,
|
553 |
+
"fixed",
|
554 |
+
30,
|
555 |
+
4.5,
|
556 |
+
"ddpm",
|
557 |
+
"karras",
|
558 |
+
1
|
559 |
+
]
|
560 |
+
}
|
561 |
+
],
|
562 |
+
"links": [
|
563 |
+
[
|
564 |
+
2,
|
565 |
+
5,
|
566 |
+
0,
|
567 |
+
3,
|
568 |
+
3,
|
569 |
+
"LATENT"
|
570 |
+
],
|
571 |
+
[
|
572 |
+
7,
|
573 |
+
3,
|
574 |
+
0,
|
575 |
+
8,
|
576 |
+
0,
|
577 |
+
"LATENT"
|
578 |
+
],
|
579 |
+
[
|
580 |
+
8,
|
581 |
+
4,
|
582 |
+
2,
|
583 |
+
8,
|
584 |
+
1,
|
585 |
+
"VAE"
|
586 |
+
],
|
587 |
+
[
|
588 |
+
19,
|
589 |
+
8,
|
590 |
+
0,
|
591 |
+
15,
|
592 |
+
0,
|
593 |
+
"IMAGE"
|
594 |
+
],
|
595 |
+
[
|
596 |
+
122,
|
597 |
+
4,
|
598 |
+
1,
|
599 |
+
39,
|
600 |
+
0,
|
601 |
+
"CLIP"
|
602 |
+
],
|
603 |
+
[
|
604 |
+
123,
|
605 |
+
4,
|
606 |
+
1,
|
607 |
+
40,
|
608 |
+
0,
|
609 |
+
"CLIP"
|
610 |
+
],
|
611 |
+
[
|
612 |
+
197,
|
613 |
+
11,
|
614 |
+
0,
|
615 |
+
60,
|
616 |
+
0,
|
617 |
+
"INSTANTID"
|
618 |
+
],
|
619 |
+
[
|
620 |
+
198,
|
621 |
+
38,
|
622 |
+
0,
|
623 |
+
60,
|
624 |
+
1,
|
625 |
+
"FACEANALYSIS"
|
626 |
+
],
|
627 |
+
[
|
628 |
+
199,
|
629 |
+
16,
|
630 |
+
0,
|
631 |
+
60,
|
632 |
+
2,
|
633 |
+
"CONTROL_NET"
|
634 |
+
],
|
635 |
+
[
|
636 |
+
200,
|
637 |
+
60,
|
638 |
+
1,
|
639 |
+
3,
|
640 |
+
1,
|
641 |
+
"CONDITIONING"
|
642 |
+
],
|
643 |
+
[
|
644 |
+
201,
|
645 |
+
60,
|
646 |
+
2,
|
647 |
+
3,
|
648 |
+
2,
|
649 |
+
"CONDITIONING"
|
650 |
+
],
|
651 |
+
[
|
652 |
+
203,
|
653 |
+
39,
|
654 |
+
0,
|
655 |
+
60,
|
656 |
+
5,
|
657 |
+
"CONDITIONING"
|
658 |
+
],
|
659 |
+
[
|
660 |
+
204,
|
661 |
+
40,
|
662 |
+
0,
|
663 |
+
60,
|
664 |
+
6,
|
665 |
+
"CONDITIONING"
|
666 |
+
],
|
667 |
+
[
|
668 |
+
206,
|
669 |
+
4,
|
670 |
+
0,
|
671 |
+
60,
|
672 |
+
4,
|
673 |
+
"MODEL"
|
674 |
+
],
|
675 |
+
[
|
676 |
+
214,
|
677 |
+
13,
|
678 |
+
0,
|
679 |
+
60,
|
680 |
+
3,
|
681 |
+
"IMAGE"
|
682 |
+
],
|
683 |
+
[
|
684 |
+
220,
|
685 |
+
60,
|
686 |
+
0,
|
687 |
+
3,
|
688 |
+
0,
|
689 |
+
"MODEL"
|
690 |
+
],
|
691 |
+
[
|
692 |
+
221,
|
693 |
+
67,
|
694 |
+
0,
|
695 |
+
60,
|
696 |
+
7,
|
697 |
+
"IMAGE"
|
698 |
+
]
|
699 |
+
],
|
700 |
+
"groups": [],
|
701 |
+
"config": {},
|
702 |
+
"extra": {},
|
703 |
+
"version": 0.4
|
704 |
+
}
|
ComfyUI_InstantID/examples/daydreaming.jpg
ADDED
ComfyUI_InstantID/examples/instant_id_ipadapter.jpg
ADDED
ComfyUI_InstantID/examples/instantid_basic_workflow.jpg
ADDED
ComfyUI_InstantID/examples/instantid_multi_id.jpg
ADDED
ComfyUI_InstantID/pyproject.toml
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
[project]
|
2 |
+
name = "comfyui_instantid"
|
3 |
+
description = "Native InstantID support for ComfyUI. This extension differs from the many already available as it doesn't use diffusers but instead implements InstantID natively and it fully integrates with ComfyUI."
|
4 |
+
version = "1.0.0"
|
5 |
+
license = "LICENSE"
|
6 |
+
dependencies = ["insightface", "onnxruntime", "onnxruntime-gpu"]
|
7 |
+
|
8 |
+
[project.urls]
|
9 |
+
Repository = "https://github.com/cubiq/ComfyUI_InstantID"
|
10 |
+
# Used by Comfy Registry https://comfyregistry.org
|
11 |
+
|
12 |
+
[tool.comfy]
|
13 |
+
PublisherId = "matteo"
|
14 |
+
DisplayName = "ComfyUI_InstantID"
|
15 |
+
Icon = ""
|
ComfyUI_InstantID/requirements.txt
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
insightface
|
2 |
+
onnxruntime
|
3 |
+
onnxruntime-gpu; sys_platform != 'darwin' and platform_machine == 'x86_64'
|
ComfyUI_InstantID/resampler.py
ADDED
@@ -0,0 +1,121 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# modified from https://github.com/mlfoundations/open_flamingo/blob/main/open_flamingo/src/helpers.py
|
2 |
+
import math
|
3 |
+
|
4 |
+
import torch
|
5 |
+
import torch.nn as nn
|
6 |
+
|
7 |
+
|
8 |
+
# FFN
|
9 |
+
def FeedForward(dim, mult=4):
|
10 |
+
inner_dim = int(dim * mult)
|
11 |
+
return nn.Sequential(
|
12 |
+
nn.LayerNorm(dim),
|
13 |
+
nn.Linear(dim, inner_dim, bias=False),
|
14 |
+
nn.GELU(),
|
15 |
+
nn.Linear(inner_dim, dim, bias=False),
|
16 |
+
)
|
17 |
+
|
18 |
+
|
19 |
+
def reshape_tensor(x, heads):
|
20 |
+
bs, length, width = x.shape
|
21 |
+
#(bs, length, width) --> (bs, length, n_heads, dim_per_head)
|
22 |
+
x = x.view(bs, length, heads, -1)
|
23 |
+
# (bs, length, n_heads, dim_per_head) --> (bs, n_heads, length, dim_per_head)
|
24 |
+
x = x.transpose(1, 2)
|
25 |
+
# (bs, n_heads, length, dim_per_head) --> (bs*n_heads, length, dim_per_head)
|
26 |
+
x = x.reshape(bs, heads, length, -1)
|
27 |
+
return x
|
28 |
+
|
29 |
+
|
30 |
+
class PerceiverAttention(nn.Module):
|
31 |
+
def __init__(self, *, dim, dim_head=64, heads=8):
|
32 |
+
super().__init__()
|
33 |
+
self.scale = dim_head**-0.5
|
34 |
+
self.dim_head = dim_head
|
35 |
+
self.heads = heads
|
36 |
+
inner_dim = dim_head * heads
|
37 |
+
|
38 |
+
self.norm1 = nn.LayerNorm(dim)
|
39 |
+
self.norm2 = nn.LayerNorm(dim)
|
40 |
+
|
41 |
+
self.to_q = nn.Linear(dim, inner_dim, bias=False)
|
42 |
+
self.to_kv = nn.Linear(dim, inner_dim * 2, bias=False)
|
43 |
+
self.to_out = nn.Linear(inner_dim, dim, bias=False)
|
44 |
+
|
45 |
+
|
46 |
+
def forward(self, x, latents):
|
47 |
+
"""
|
48 |
+
Args:
|
49 |
+
x (torch.Tensor): image features
|
50 |
+
shape (b, n1, D)
|
51 |
+
latent (torch.Tensor): latent features
|
52 |
+
shape (b, n2, D)
|
53 |
+
"""
|
54 |
+
x = self.norm1(x)
|
55 |
+
latents = self.norm2(latents)
|
56 |
+
|
57 |
+
b, l, _ = latents.shape
|
58 |
+
|
59 |
+
q = self.to_q(latents)
|
60 |
+
kv_input = torch.cat((x, latents), dim=-2)
|
61 |
+
k, v = self.to_kv(kv_input).chunk(2, dim=-1)
|
62 |
+
|
63 |
+
q = reshape_tensor(q, self.heads)
|
64 |
+
k = reshape_tensor(k, self.heads)
|
65 |
+
v = reshape_tensor(v, self.heads)
|
66 |
+
|
67 |
+
# attention
|
68 |
+
scale = 1 / math.sqrt(math.sqrt(self.dim_head))
|
69 |
+
weight = (q * scale) @ (k * scale).transpose(-2, -1) # More stable with f16 than dividing afterwards
|
70 |
+
weight = torch.softmax(weight.float(), dim=-1).type(weight.dtype)
|
71 |
+
out = weight @ v
|
72 |
+
|
73 |
+
out = out.permute(0, 2, 1, 3).reshape(b, l, -1)
|
74 |
+
|
75 |
+
return self.to_out(out)
|
76 |
+
|
77 |
+
|
78 |
+
class Resampler(nn.Module):
|
79 |
+
def __init__(
|
80 |
+
self,
|
81 |
+
dim=1024,
|
82 |
+
depth=8,
|
83 |
+
dim_head=64,
|
84 |
+
heads=16,
|
85 |
+
num_queries=8,
|
86 |
+
embedding_dim=768,
|
87 |
+
output_dim=1024,
|
88 |
+
ff_mult=4,
|
89 |
+
):
|
90 |
+
super().__init__()
|
91 |
+
|
92 |
+
self.latents = nn.Parameter(torch.randn(1, num_queries, dim) / dim**0.5)
|
93 |
+
|
94 |
+
self.proj_in = nn.Linear(embedding_dim, dim)
|
95 |
+
|
96 |
+
self.proj_out = nn.Linear(dim, output_dim)
|
97 |
+
self.norm_out = nn.LayerNorm(output_dim)
|
98 |
+
|
99 |
+
self.layers = nn.ModuleList([])
|
100 |
+
for _ in range(depth):
|
101 |
+
self.layers.append(
|
102 |
+
nn.ModuleList(
|
103 |
+
[
|
104 |
+
PerceiverAttention(dim=dim, dim_head=dim_head, heads=heads),
|
105 |
+
FeedForward(dim=dim, mult=ff_mult),
|
106 |
+
]
|
107 |
+
)
|
108 |
+
)
|
109 |
+
|
110 |
+
def forward(self, x):
|
111 |
+
|
112 |
+
latents = self.latents.repeat(x.size(0), 1, 1)
|
113 |
+
|
114 |
+
x = self.proj_in(x)
|
115 |
+
|
116 |
+
for attn, ff in self.layers:
|
117 |
+
latents = attn(x, latents) + latents
|
118 |
+
latents = ff(latents) + latents
|
119 |
+
|
120 |
+
latents = self.proj_out(latents)
|
121 |
+
return self.norm_out(latents)
|
ComfyUI_InstantID/utils.py
ADDED
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import torch
|
2 |
+
|
3 |
+
def tensor_to_size(source, dest_size):
|
4 |
+
if isinstance(dest_size, torch.Tensor):
|
5 |
+
dest_size = dest_size.shape[0]
|
6 |
+
source_size = source.shape[0]
|
7 |
+
|
8 |
+
if source_size < dest_size:
|
9 |
+
shape = [dest_size - source_size] + [1]*(source.dim()-1)
|
10 |
+
source = torch.cat((source, source[-1:].repeat(shape)), dim=0)
|
11 |
+
elif source_size > dest_size:
|
12 |
+
source = source[:dest_size]
|
13 |
+
|
14 |
+
return source
|
15 |
+
|
16 |
+
def tensor_to_image(tensor):
|
17 |
+
image = tensor.mul(255).clamp(0, 255).byte().cpu()
|
18 |
+
image = image[..., [2, 1, 0]].numpy()
|
19 |
+
return image
|
20 |
+
|
21 |
+
def image_to_tensor(image):
|
22 |
+
tensor = torch.clamp(torch.from_numpy(image).float() / 255., 0, 1)
|
23 |
+
tensor = tensor[..., [2, 1, 0]]
|
24 |
+
return tensor
|
ComfyUI_essentials/.github/workflows/publish.yml
ADDED
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
name: Publish to Comfy registry
|
2 |
+
on:
|
3 |
+
workflow_dispatch:
|
4 |
+
push:
|
5 |
+
branches:
|
6 |
+
- main
|
7 |
+
- master
|
8 |
+
paths:
|
9 |
+
- "pyproject.toml"
|
10 |
+
|
11 |
+
jobs:
|
12 |
+
publish-node:
|
13 |
+
name: Publish Custom Node to registry
|
14 |
+
runs-on: ubuntu-latest
|
15 |
+
steps:
|
16 |
+
- name: Check out code
|
17 |
+
uses: actions/checkout@v4
|
18 |
+
- name: Publish Custom Node
|
19 |
+
uses: Comfy-Org/publish-node-action@main
|
20 |
+
with:
|
21 |
+
## Add your own personal access token to your Github Repository secrets and reference it here.
|
22 |
+
personal_access_token: ${{ secrets.REGISTRY_ACCESS_TOKEN }}
|
ComfyUI_essentials/.gitignore
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/__pycache__/
|
2 |
+
/luts/*.cube
|
3 |
+
/luts/*.CUBE
|
4 |
+
/fonts/*.ttf
|
5 |
+
/fonts/*.otf
|
6 |
+
!/fonts/ShareTechMono-Regular.ttf
|
ComfyUI_essentials/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
MIT License
|
2 |
+
|
3 |
+
Copyright (c) 2023 Matteo Spinelli
|
4 |
+
|
5 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6 |
+
of this software and associated documentation files (the "Software"), to deal
|
7 |
+
in the Software without restriction, including without limitation the rights
|
8 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9 |
+
copies of the Software, and to permit persons to whom the Software is
|
10 |
+
furnished to do so, subject to the following conditions:
|
11 |
+
|
12 |
+
The above copyright notice and this permission notice shall be included in all
|
13 |
+
copies or substantial portions of the Software.
|
14 |
+
|
15 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21 |
+
SOFTWARE.
|
ComfyUI_essentials/README.md
ADDED
@@ -0,0 +1,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# :wrench: ComfyUI Essentials
|
2 |
+
|
3 |
+
Essential nodes that are weirdly missing from ComfyUI core. With few exceptions they are new features and not commodities. I hope this will be just a temporary repository until the nodes get included into ComfyUI.
|
4 |
+
|
5 |
+
# Sponsorship
|
6 |
+
|
7 |
+
<div align="center">
|
8 |
+
|
9 |
+
**[:heart: Github Sponsor](https://github.com/sponsors/cubiq) | [:coin: Paypal](https://paypal.me/matt3o)**
|
10 |
+
|
11 |
+
</div>
|
12 |
+
|
13 |
+
If you like my work and wish to see updates and new features please consider sponsoring my projects.
|
14 |
+
|
15 |
+
- [ComfyUI IPAdapter Plus](https://github.com/cubiq/ComfyUI_IPAdapter_plus)
|
16 |
+
- [ComfyUI InstantID (Native)](https://github.com/cubiq/ComfyUI_InstantID)
|
17 |
+
- [ComfyUI Essentials](https://github.com/cubiq/ComfyUI_essentials)
|
18 |
+
- [ComfyUI FaceAnalysis](https://github.com/cubiq/ComfyUI_FaceAnalysis)
|
19 |
+
|
20 |
+
Not to mention the documentation and videos tutorials. Check my **ComfyUI Advanced Understanding** videos on YouTube for example, [part 1](https://www.youtube.com/watch?v=_C7kR2TFIX0) and [part 2](https://www.youtube.com/watch?v=ijqXnW_9gzc)
|
21 |
+
|
22 |
+
The only way to keep the code open and free is by sponsoring its development. The more sponsorships the more time I can dedicate to my open source projects.
|
23 |
+
|
24 |
+
Please consider a [Github Sponsorship](https://github.com/sponsors/cubiq) or [PayPal donation](https://paypal.me/matt3o) (Matteo "matt3o" Spinelli). For sponsorships of $50+, let me know if you'd like to be mentioned in this readme file, you can find me on [Discord](https://latent.vision/discord) or _matt3o :snail: gmail.com_.
|
25 |
+
|
26 |
+
## Current sponsors
|
27 |
+
|
28 |
+
It's only thanks to generous sponsors that **the whole community** can enjoy open and free software. Please join me in thanking the following companies and individuals!
|
29 |
+
|
30 |
+
### :trophy: Gold sponsors
|
31 |
+
|
32 |
+
[![Kaiber.ai](https://f.latent.vision/imgs/kaiber.png)](https://kaiber.ai/) [![InstaSD](https://f.latent.vision/imgs/instasd.png)](https://www.instasd.com/)
|
33 |
+
|
34 |
+
### :tada: Silver sponsors
|
35 |
+
|
36 |
+
[![OperArt.ai](https://f.latent.vision/imgs/openart.png?r=1)](https://openart.ai/workflows) [![Finetuners](https://f.latent.vision/imgs/finetuners.png)](https://www.finetuners.ai/) [![Comfy.ICU](https://f.latent.vision/imgs/comfyicu.png?r=1)](https://comfy.icu/)
|
37 |
+
|
38 |
+
### Other companies supporting my projects
|
39 |
+
|
40 |
+
- [RunComfy](https://www.runcomfy.com/) (ComfyUI Cloud)
|
41 |
+
|
42 |
+
### Esteemed individuals
|
43 |
+
|
44 |
+
- [Øystein Ø. Olsen](https://github.com/FireNeslo)
|
45 |
+
- [Jack Gane](https://github.com/ganeJackS)
|
46 |
+
- [Nathan Shipley](https://www.nathanshipley.com/)
|
47 |
+
- [Dkdnzia](https://github.com/Dkdnzia)
|
48 |
+
|
49 |
+
[And all my public and private sponsors!](https://github.com/sponsors/cubiq)
|
ComfyUI_essentials/__init__.py
ADDED
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#from .essentials import NODE_CLASS_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS
|
2 |
+
from .image import IMAGE_CLASS_MAPPINGS, IMAGE_NAME_MAPPINGS
|
3 |
+
from .mask import MASK_CLASS_MAPPINGS, MASK_NAME_MAPPINGS
|
4 |
+
from .sampling import SAMPLING_CLASS_MAPPINGS, SAMPLING_NAME_MAPPINGS
|
5 |
+
from .segmentation import SEG_CLASS_MAPPINGS, SEG_NAME_MAPPINGS
|
6 |
+
from .misc import MISC_CLASS_MAPPINGS, MISC_NAME_MAPPINGS
|
7 |
+
from .conditioning import COND_CLASS_MAPPINGS, COND_NAME_MAPPINGS
|
8 |
+
from .text import TEXT_CLASS_MAPPINGS, TEXT_NAME_MAPPINGS
|
9 |
+
|
10 |
+
WEB_DIRECTORY = "./js"
|
11 |
+
|
12 |
+
NODE_CLASS_MAPPINGS = {}
|
13 |
+
NODE_DISPLAY_NAME_MAPPINGS = {}
|
14 |
+
|
15 |
+
NODE_CLASS_MAPPINGS.update(COND_CLASS_MAPPINGS)
|
16 |
+
NODE_DISPLAY_NAME_MAPPINGS.update(COND_NAME_MAPPINGS)
|
17 |
+
|
18 |
+
NODE_CLASS_MAPPINGS.update(IMAGE_CLASS_MAPPINGS)
|
19 |
+
NODE_DISPLAY_NAME_MAPPINGS.update(IMAGE_NAME_MAPPINGS)
|
20 |
+
|
21 |
+
NODE_CLASS_MAPPINGS.update(MASK_CLASS_MAPPINGS)
|
22 |
+
NODE_DISPLAY_NAME_MAPPINGS.update(MASK_NAME_MAPPINGS)
|
23 |
+
|
24 |
+
NODE_CLASS_MAPPINGS.update(SAMPLING_CLASS_MAPPINGS)
|
25 |
+
NODE_DISPLAY_NAME_MAPPINGS.update(SAMPLING_NAME_MAPPINGS)
|
26 |
+
|
27 |
+
NODE_CLASS_MAPPINGS.update(SEG_CLASS_MAPPINGS)
|
28 |
+
NODE_DISPLAY_NAME_MAPPINGS.update(SEG_NAME_MAPPINGS)
|
29 |
+
|
30 |
+
NODE_CLASS_MAPPINGS.update(TEXT_CLASS_MAPPINGS)
|
31 |
+
NODE_DISPLAY_NAME_MAPPINGS.update(TEXT_NAME_MAPPINGS)
|
32 |
+
|
33 |
+
NODE_CLASS_MAPPINGS.update(MISC_CLASS_MAPPINGS)
|
34 |
+
NODE_DISPLAY_NAME_MAPPINGS.update(MISC_NAME_MAPPINGS)
|
35 |
+
|
36 |
+
__all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS', "WEB_DIRECTORY"]
|
ComfyUI_essentials/__pycache__/__init__.cpython-312.pyc
ADDED
Binary file (1.61 kB). View file
|
|
ComfyUI_essentials/__pycache__/conditioning.cpython-312.pyc
ADDED
Binary file (13.7 kB). View file
|
|
ComfyUI_essentials/__pycache__/image.cpython-312.pyc
ADDED
Binary file (77.4 kB). View file
|
|
ComfyUI_essentials/__pycache__/mask.cpython-312.pyc
ADDED
Binary file (28.6 kB). View file
|
|
ComfyUI_essentials/__pycache__/misc.cpython-312.pyc
ADDED
Binary file (24.3 kB). View file
|
|
ComfyUI_essentials/__pycache__/sampling.cpython-312.pyc
ADDED
Binary file (39.7 kB). View file
|
|
ComfyUI_essentials/__pycache__/segmentation.cpython-312.pyc
ADDED
Binary file (3.91 kB). View file
|
|
ComfyUI_essentials/__pycache__/text.cpython-312.pyc
ADDED
Binary file (5.75 kB). View file
|
|
ComfyUI_essentials/__pycache__/utils.cpython-312.pyc
ADDED
Binary file (4.58 kB). View file
|
|
ComfyUI_essentials/carve.py
ADDED
@@ -0,0 +1,454 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# MIT licensed code from https://github.com/li-plus/seam-carving/
|
2 |
+
|
3 |
+
from enum import Enum
|
4 |
+
from typing import Optional, Tuple
|
5 |
+
|
6 |
+
import numba as nb
|
7 |
+
import numpy as np
|
8 |
+
from scipy.ndimage import sobel
|
9 |
+
|
10 |
+
DROP_MASK_ENERGY = 1e5
|
11 |
+
KEEP_MASK_ENERGY = 1e3
|
12 |
+
|
13 |
+
|
14 |
+
class OrderMode(str, Enum):
|
15 |
+
WIDTH_FIRST = "width-first"
|
16 |
+
HEIGHT_FIRST = "height-first"
|
17 |
+
|
18 |
+
|
19 |
+
class EnergyMode(str, Enum):
|
20 |
+
FORWARD = "forward"
|
21 |
+
BACKWARD = "backward"
|
22 |
+
|
23 |
+
|
24 |
+
def _list_enum(enum_class) -> Tuple:
|
25 |
+
return tuple(x.value for x in enum_class)
|
26 |
+
|
27 |
+
|
28 |
+
def _rgb2gray(rgb: np.ndarray) -> np.ndarray:
|
29 |
+
"""Convert an RGB image to a grayscale image"""
|
30 |
+
coeffs = np.array([0.2125, 0.7154, 0.0721], dtype=np.float32)
|
31 |
+
return (rgb @ coeffs).astype(rgb.dtype)
|
32 |
+
|
33 |
+
|
34 |
+
def _get_seam_mask(src: np.ndarray, seam: np.ndarray) -> np.ndarray:
|
35 |
+
"""Convert a list of seam column indices to a mask"""
|
36 |
+
return np.eye(src.shape[1], dtype=bool)[seam]
|
37 |
+
|
38 |
+
|
39 |
+
def _remove_seam_mask(src: np.ndarray, seam_mask: np.ndarray) -> np.ndarray:
|
40 |
+
"""Remove a seam from the source image according to the given seam_mask"""
|
41 |
+
if src.ndim == 3:
|
42 |
+
h, w, c = src.shape
|
43 |
+
seam_mask = np.broadcast_to(seam_mask[:, :, None], src.shape)
|
44 |
+
dst = src[~seam_mask].reshape((h, w - 1, c))
|
45 |
+
else:
|
46 |
+
h, w = src.shape
|
47 |
+
dst = src[~seam_mask].reshape((h, w - 1))
|
48 |
+
return dst
|
49 |
+
|
50 |
+
|
51 |
+
def _get_energy(gray: np.ndarray) -> np.ndarray:
|
52 |
+
"""Get backward energy map from the source image"""
|
53 |
+
assert gray.ndim == 2
|
54 |
+
|
55 |
+
gray = gray.astype(np.float32)
|
56 |
+
grad_x = sobel(gray, axis=1)
|
57 |
+
grad_y = sobel(gray, axis=0)
|
58 |
+
energy = np.abs(grad_x) + np.abs(grad_y)
|
59 |
+
return energy
|
60 |
+
|
61 |
+
|
62 |
+
@nb.njit(nb.int32[:](nb.float32[:, :]), cache=True)
|
63 |
+
def _get_backward_seam(energy: np.ndarray) -> np.ndarray:
|
64 |
+
"""Compute the minimum vertical seam from the backward energy map"""
|
65 |
+
h, w = energy.shape
|
66 |
+
inf = np.array([np.inf], dtype=np.float32)
|
67 |
+
cost = np.concatenate((inf, energy[0], inf))
|
68 |
+
parent = np.empty((h, w), dtype=np.int32)
|
69 |
+
base_idx = np.arange(-1, w - 1, dtype=np.int32)
|
70 |
+
|
71 |
+
for r in range(1, h):
|
72 |
+
choices = np.vstack((cost[:-2], cost[1:-1], cost[2:]))
|
73 |
+
min_idx = np.argmin(choices, axis=0) + base_idx
|
74 |
+
parent[r] = min_idx
|
75 |
+
cost[1:-1] = cost[1:-1][min_idx] + energy[r]
|
76 |
+
|
77 |
+
c = np.argmin(cost[1:-1])
|
78 |
+
seam = np.empty(h, dtype=np.int32)
|
79 |
+
for r in range(h - 1, -1, -1):
|
80 |
+
seam[r] = c
|
81 |
+
c = parent[r, c]
|
82 |
+
|
83 |
+
return seam
|
84 |
+
|
85 |
+
|
86 |
+
def _get_backward_seams(
|
87 |
+
gray: np.ndarray, num_seams: int, aux_energy: Optional[np.ndarray]
|
88 |
+
) -> np.ndarray:
|
89 |
+
"""Compute the minimum N vertical seams using backward energy"""
|
90 |
+
h, w = gray.shape
|
91 |
+
seams = np.zeros((h, w), dtype=bool)
|
92 |
+
rows = np.arange(h, dtype=np.int32)
|
93 |
+
idx_map = np.broadcast_to(np.arange(w, dtype=np.int32), (h, w))
|
94 |
+
energy = _get_energy(gray)
|
95 |
+
if aux_energy is not None:
|
96 |
+
energy += aux_energy
|
97 |
+
for _ in range(num_seams):
|
98 |
+
seam = _get_backward_seam(energy)
|
99 |
+
seams[rows, idx_map[rows, seam]] = True
|
100 |
+
|
101 |
+
seam_mask = _get_seam_mask(gray, seam)
|
102 |
+
gray = _remove_seam_mask(gray, seam_mask)
|
103 |
+
idx_map = _remove_seam_mask(idx_map, seam_mask)
|
104 |
+
if aux_energy is not None:
|
105 |
+
aux_energy = _remove_seam_mask(aux_energy, seam_mask)
|
106 |
+
|
107 |
+
# Only need to re-compute the energy in the bounding box of the seam
|
108 |
+
_, cur_w = energy.shape
|
109 |
+
lo = max(0, np.min(seam) - 1)
|
110 |
+
hi = min(cur_w, np.max(seam) + 1)
|
111 |
+
pad_lo = 1 if lo > 0 else 0
|
112 |
+
pad_hi = 1 if hi < cur_w - 1 else 0
|
113 |
+
mid_block = gray[:, lo - pad_lo : hi + pad_hi]
|
114 |
+
_, mid_w = mid_block.shape
|
115 |
+
mid_energy = _get_energy(mid_block)[:, pad_lo : mid_w - pad_hi]
|
116 |
+
if aux_energy is not None:
|
117 |
+
mid_energy += aux_energy[:, lo:hi]
|
118 |
+
energy = np.hstack((energy[:, :lo], mid_energy, energy[:, hi + 1 :]))
|
119 |
+
|
120 |
+
return seams
|
121 |
+
|
122 |
+
|
123 |
+
@nb.njit(
|
124 |
+
[
|
125 |
+
nb.int32[:](nb.float32[:, :], nb.none),
|
126 |
+
nb.int32[:](nb.float32[:, :], nb.float32[:, :]),
|
127 |
+
],
|
128 |
+
cache=True,
|
129 |
+
)
|
130 |
+
def _get_forward_seam(gray: np.ndarray, aux_energy: Optional[np.ndarray]) -> np.ndarray:
|
131 |
+
"""Compute the minimum vertical seam using forward energy"""
|
132 |
+
h, w = gray.shape
|
133 |
+
|
134 |
+
gray = np.hstack((gray[:, :1], gray, gray[:, -1:]))
|
135 |
+
|
136 |
+
inf = np.array([np.inf], dtype=np.float32)
|
137 |
+
dp = np.concatenate((inf, np.abs(gray[0, 2:] - gray[0, :-2]), inf))
|
138 |
+
|
139 |
+
parent = np.empty((h, w), dtype=np.int32)
|
140 |
+
base_idx = np.arange(-1, w - 1, dtype=np.int32)
|
141 |
+
|
142 |
+
inf = np.array([np.inf], dtype=np.float32)
|
143 |
+
for r in range(1, h):
|
144 |
+
curr_shl = gray[r, 2:]
|
145 |
+
curr_shr = gray[r, :-2]
|
146 |
+
cost_mid = np.abs(curr_shl - curr_shr)
|
147 |
+
if aux_energy is not None:
|
148 |
+
cost_mid += aux_energy[r]
|
149 |
+
|
150 |
+
prev_mid = gray[r - 1, 1:-1]
|
151 |
+
cost_left = cost_mid + np.abs(prev_mid - curr_shr)
|
152 |
+
cost_right = cost_mid + np.abs(prev_mid - curr_shl)
|
153 |
+
|
154 |
+
dp_mid = dp[1:-1]
|
155 |
+
dp_left = dp[:-2]
|
156 |
+
dp_right = dp[2:]
|
157 |
+
|
158 |
+
choices = np.vstack(
|
159 |
+
(cost_left + dp_left, cost_mid + dp_mid, cost_right + dp_right)
|
160 |
+
)
|
161 |
+
min_idx = np.argmin(choices, axis=0)
|
162 |
+
parent[r] = min_idx + base_idx
|
163 |
+
# numba does not support specifying axis in np.min, below loop is equivalent to:
|
164 |
+
# `dp_mid[:] = np.min(choices, axis=0)` or `dp_mid[:] = choices[min_idx, np.arange(w)]`
|
165 |
+
for j, i in enumerate(min_idx):
|
166 |
+
dp_mid[j] = choices[i, j]
|
167 |
+
|
168 |
+
c = np.argmin(dp[1:-1])
|
169 |
+
seam = np.empty(h, dtype=np.int32)
|
170 |
+
for r in range(h - 1, -1, -1):
|
171 |
+
seam[r] = c
|
172 |
+
c = parent[r, c]
|
173 |
+
|
174 |
+
return seam
|
175 |
+
|
176 |
+
|
177 |
+
def _get_forward_seams(
|
178 |
+
gray: np.ndarray, num_seams: int, aux_energy: Optional[np.ndarray]
|
179 |
+
) -> np.ndarray:
|
180 |
+
"""Compute minimum N vertical seams using forward energy"""
|
181 |
+
h, w = gray.shape
|
182 |
+
seams = np.zeros((h, w), dtype=bool)
|
183 |
+
rows = np.arange(h, dtype=np.int32)
|
184 |
+
idx_map = np.broadcast_to(np.arange(w, dtype=np.int32), (h, w))
|
185 |
+
for _ in range(num_seams):
|
186 |
+
seam = _get_forward_seam(gray, aux_energy)
|
187 |
+
seams[rows, idx_map[rows, seam]] = True
|
188 |
+
seam_mask = _get_seam_mask(gray, seam)
|
189 |
+
gray = _remove_seam_mask(gray, seam_mask)
|
190 |
+
idx_map = _remove_seam_mask(idx_map, seam_mask)
|
191 |
+
if aux_energy is not None:
|
192 |
+
aux_energy = _remove_seam_mask(aux_energy, seam_mask)
|
193 |
+
|
194 |
+
return seams
|
195 |
+
|
196 |
+
|
197 |
+
def _get_seams(
|
198 |
+
gray: np.ndarray, num_seams: int, energy_mode: str, aux_energy: Optional[np.ndarray]
|
199 |
+
) -> np.ndarray:
|
200 |
+
"""Get the minimum N seams from the grayscale image"""
|
201 |
+
gray = np.asarray(gray, dtype=np.float32)
|
202 |
+
if energy_mode == EnergyMode.BACKWARD:
|
203 |
+
return _get_backward_seams(gray, num_seams, aux_energy)
|
204 |
+
elif energy_mode == EnergyMode.FORWARD:
|
205 |
+
return _get_forward_seams(gray, num_seams, aux_energy)
|
206 |
+
else:
|
207 |
+
raise ValueError(
|
208 |
+
f"expect energy_mode to be one of {_list_enum(EnergyMode)}, got {energy_mode}"
|
209 |
+
)
|
210 |
+
|
211 |
+
|
212 |
+
def _reduce_width(
|
213 |
+
src: np.ndarray,
|
214 |
+
delta_width: int,
|
215 |
+
energy_mode: str,
|
216 |
+
aux_energy: Optional[np.ndarray],
|
217 |
+
) -> Tuple[np.ndarray, Optional[np.ndarray]]:
|
218 |
+
"""Reduce the width of image by delta_width pixels"""
|
219 |
+
assert src.ndim in (2, 3) and delta_width >= 0
|
220 |
+
if src.ndim == 2:
|
221 |
+
gray = src
|
222 |
+
src_h, src_w = src.shape
|
223 |
+
dst_shape: Tuple[int, ...] = (src_h, src_w - delta_width)
|
224 |
+
else:
|
225 |
+
gray = _rgb2gray(src)
|
226 |
+
src_h, src_w, src_c = src.shape
|
227 |
+
dst_shape = (src_h, src_w - delta_width, src_c)
|
228 |
+
|
229 |
+
to_keep = ~_get_seams(gray, delta_width, energy_mode, aux_energy)
|
230 |
+
dst = src[to_keep].reshape(dst_shape)
|
231 |
+
if aux_energy is not None:
|
232 |
+
aux_energy = aux_energy[to_keep].reshape(dst_shape[:2])
|
233 |
+
return dst, aux_energy
|
234 |
+
|
235 |
+
|
236 |
+
@nb.njit(
|
237 |
+
nb.float32[:, :, :](nb.float32[:, :, :], nb.boolean[:, :], nb.int32), cache=True
|
238 |
+
)
|
239 |
+
def _insert_seams_kernel(
|
240 |
+
src: np.ndarray, seams: np.ndarray, delta_width: int
|
241 |
+
) -> np.ndarray:
|
242 |
+
"""The numba kernel for inserting seams"""
|
243 |
+
src_h, src_w, src_c = src.shape
|
244 |
+
dst = np.empty((src_h, src_w + delta_width, src_c), dtype=src.dtype)
|
245 |
+
for row in range(src_h):
|
246 |
+
dst_col = 0
|
247 |
+
for src_col in range(src_w):
|
248 |
+
if seams[row, src_col]:
|
249 |
+
left = src[row, max(src_col - 1, 0)]
|
250 |
+
right = src[row, src_col]
|
251 |
+
dst[row, dst_col] = (left + right) / 2
|
252 |
+
dst_col += 1
|
253 |
+
dst[row, dst_col] = src[row, src_col]
|
254 |
+
dst_col += 1
|
255 |
+
return dst
|
256 |
+
|
257 |
+
|
258 |
+
def _insert_seams(src: np.ndarray, seams: np.ndarray, delta_width: int) -> np.ndarray:
|
259 |
+
"""Insert multiple seams into the source image"""
|
260 |
+
dst = src.astype(np.float32)
|
261 |
+
if dst.ndim == 2:
|
262 |
+
dst = dst[:, :, None]
|
263 |
+
dst = _insert_seams_kernel(dst, seams, delta_width).astype(src.dtype)
|
264 |
+
if src.ndim == 2:
|
265 |
+
dst = dst.squeeze(-1)
|
266 |
+
return dst
|
267 |
+
|
268 |
+
|
269 |
+
def _expand_width(
|
270 |
+
src: np.ndarray,
|
271 |
+
delta_width: int,
|
272 |
+
energy_mode: str,
|
273 |
+
aux_energy: Optional[np.ndarray],
|
274 |
+
step_ratio: float,
|
275 |
+
) -> Tuple[np.ndarray, Optional[np.ndarray]]:
|
276 |
+
"""Expand the width of image by delta_width pixels"""
|
277 |
+
assert src.ndim in (2, 3) and delta_width >= 0
|
278 |
+
if not 0 < step_ratio <= 1:
|
279 |
+
raise ValueError(f"expect `step_ratio` to be between (0,1], got {step_ratio}")
|
280 |
+
|
281 |
+
dst = src
|
282 |
+
while delta_width > 0:
|
283 |
+
max_step_size = max(1, round(step_ratio * dst.shape[1]))
|
284 |
+
step_size = min(max_step_size, delta_width)
|
285 |
+
gray = dst if dst.ndim == 2 else _rgb2gray(dst)
|
286 |
+
seams = _get_seams(gray, step_size, energy_mode, aux_energy)
|
287 |
+
dst = _insert_seams(dst, seams, step_size)
|
288 |
+
if aux_energy is not None:
|
289 |
+
aux_energy = _insert_seams(aux_energy, seams, step_size)
|
290 |
+
delta_width -= step_size
|
291 |
+
|
292 |
+
return dst, aux_energy
|
293 |
+
|
294 |
+
|
295 |
+
def _resize_width(
|
296 |
+
src: np.ndarray,
|
297 |
+
width: int,
|
298 |
+
energy_mode: str,
|
299 |
+
aux_energy: Optional[np.ndarray],
|
300 |
+
step_ratio: float,
|
301 |
+
) -> Tuple[np.ndarray, Optional[np.ndarray]]:
|
302 |
+
"""Resize the width of image by removing vertical seams"""
|
303 |
+
assert src.size > 0 and src.ndim in (2, 3)
|
304 |
+
assert width > 0
|
305 |
+
|
306 |
+
src_w = src.shape[1]
|
307 |
+
if src_w < width:
|
308 |
+
dst, aux_energy = _expand_width(
|
309 |
+
src, width - src_w, energy_mode, aux_energy, step_ratio
|
310 |
+
)
|
311 |
+
else:
|
312 |
+
dst, aux_energy = _reduce_width(src, src_w - width, energy_mode, aux_energy)
|
313 |
+
return dst, aux_energy
|
314 |
+
|
315 |
+
|
316 |
+
def _transpose_image(src: np.ndarray) -> np.ndarray:
|
317 |
+
"""Transpose a source image in rgb or grayscale format"""
|
318 |
+
if src.ndim == 3:
|
319 |
+
dst = src.transpose((1, 0, 2))
|
320 |
+
else:
|
321 |
+
dst = src.T
|
322 |
+
return dst
|
323 |
+
|
324 |
+
|
325 |
+
def _resize_height(
|
326 |
+
src: np.ndarray,
|
327 |
+
height: int,
|
328 |
+
energy_mode: str,
|
329 |
+
aux_energy: Optional[np.ndarray],
|
330 |
+
step_ratio: float,
|
331 |
+
) -> Tuple[np.ndarray, Optional[np.ndarray]]:
|
332 |
+
"""Resize the height of image by removing horizontal seams"""
|
333 |
+
assert src.ndim in (2, 3) and height > 0
|
334 |
+
if aux_energy is not None:
|
335 |
+
aux_energy = aux_energy.T
|
336 |
+
src = _transpose_image(src)
|
337 |
+
src, aux_energy = _resize_width(src, height, energy_mode, aux_energy, step_ratio)
|
338 |
+
src = _transpose_image(src)
|
339 |
+
if aux_energy is not None:
|
340 |
+
aux_energy = aux_energy.T
|
341 |
+
return src, aux_energy
|
342 |
+
|
343 |
+
|
344 |
+
def _check_mask(mask: np.ndarray, shape: Tuple[int, ...]) -> np.ndarray:
|
345 |
+
"""Ensure the mask to be a 2D grayscale map of specific shape"""
|
346 |
+
mask = np.asarray(mask, dtype=bool)
|
347 |
+
if mask.ndim != 2:
|
348 |
+
raise ValueError(f"expect mask to be a 2d binary map, got shape {mask.shape}")
|
349 |
+
if mask.shape != shape:
|
350 |
+
raise ValueError(
|
351 |
+
f"expect the shape of mask to match the image, got {mask.shape} vs {shape}"
|
352 |
+
)
|
353 |
+
return mask
|
354 |
+
|
355 |
+
|
356 |
+
def _check_src(src: np.ndarray) -> np.ndarray:
|
357 |
+
"""Ensure the source to be RGB or grayscale"""
|
358 |
+
src = np.asarray(src)
|
359 |
+
if src.size == 0 or src.ndim not in (2, 3):
|
360 |
+
raise ValueError(
|
361 |
+
f"expect a 3d rgb image or a 2d grayscale image, got image in shape {src.shape}"
|
362 |
+
)
|
363 |
+
return src
|
364 |
+
|
365 |
+
|
366 |
+
def seam_carving(
|
367 |
+
src: np.ndarray,
|
368 |
+
size: Optional[Tuple[int, int]] = None,
|
369 |
+
energy_mode: str = "backward",
|
370 |
+
order: str = "width-first",
|
371 |
+
keep_mask: Optional[np.ndarray] = None,
|
372 |
+
drop_mask: Optional[np.ndarray] = None,
|
373 |
+
step_ratio: float = 0.5,
|
374 |
+
) -> np.ndarray:
|
375 |
+
"""Resize the image using the content-aware seam-carving algorithm.
|
376 |
+
|
377 |
+
:param src: A source image in RGB or grayscale format.
|
378 |
+
:param size: The target size in pixels, as a 2-tuple (width, height).
|
379 |
+
:param energy_mode: Policy to compute energy for the source image. Could be
|
380 |
+
one of ``backward`` or ``forward``. If ``backward``, compute the energy
|
381 |
+
as the gradient at each pixel. If ``forward``, compute the energy as the
|
382 |
+
distances between adjacent pixels after each pixel is removed.
|
383 |
+
:param order: The order to remove horizontal and vertical seams. Could be
|
384 |
+
one of ``width-first`` or ``height-first``. In ``width-first`` mode, we
|
385 |
+
remove or insert all vertical seams first, then the horizontal ones,
|
386 |
+
while ``height-first`` is the opposite.
|
387 |
+
:param keep_mask: An optional mask where the foreground is protected from
|
388 |
+
seam removal. If not specified, no area will be protected.
|
389 |
+
:param drop_mask: An optional binary object mask to remove. If given, the
|
390 |
+
object will be removed before resizing the image to the target size.
|
391 |
+
:param step_ratio: The maximum size expansion ratio in one seam carving step.
|
392 |
+
The image will be expanded in multiple steps if target size is too large.
|
393 |
+
:return: A resized copy of the source image.
|
394 |
+
"""
|
395 |
+
src = _check_src(src)
|
396 |
+
|
397 |
+
if order not in _list_enum(OrderMode):
|
398 |
+
raise ValueError(
|
399 |
+
f"expect order to be one of {_list_enum(OrderMode)}, got {order}"
|
400 |
+
)
|
401 |
+
|
402 |
+
aux_energy = None
|
403 |
+
|
404 |
+
if keep_mask is not None:
|
405 |
+
keep_mask = _check_mask(keep_mask, src.shape[:2])
|
406 |
+
|
407 |
+
aux_energy = np.zeros(src.shape[:2], dtype=np.float32)
|
408 |
+
aux_energy[keep_mask] += KEEP_MASK_ENERGY
|
409 |
+
|
410 |
+
# remove object if `drop_mask` is given
|
411 |
+
if drop_mask is not None:
|
412 |
+
drop_mask = _check_mask(drop_mask, src.shape[:2])
|
413 |
+
|
414 |
+
if aux_energy is None:
|
415 |
+
aux_energy = np.zeros(src.shape[:2], dtype=np.float32)
|
416 |
+
aux_energy[drop_mask] -= DROP_MASK_ENERGY
|
417 |
+
|
418 |
+
if order == OrderMode.HEIGHT_FIRST:
|
419 |
+
src = _transpose_image(src)
|
420 |
+
aux_energy = aux_energy.T
|
421 |
+
|
422 |
+
num_seams = (aux_energy < 0).sum(1).max()
|
423 |
+
while num_seams > 0:
|
424 |
+
src, aux_energy = _reduce_width(src, num_seams, energy_mode, aux_energy)
|
425 |
+
num_seams = (aux_energy < 0).sum(1).max()
|
426 |
+
|
427 |
+
if order == OrderMode.HEIGHT_FIRST:
|
428 |
+
src = _transpose_image(src)
|
429 |
+
aux_energy = aux_energy.T
|
430 |
+
|
431 |
+
# resize image if `size` is given
|
432 |
+
if size is not None:
|
433 |
+
width, height = size
|
434 |
+
width = round(width)
|
435 |
+
height = round(height)
|
436 |
+
if width <= 0 or height <= 0:
|
437 |
+
raise ValueError(f"expect target size to be positive, got {size}")
|
438 |
+
|
439 |
+
if order == OrderMode.WIDTH_FIRST:
|
440 |
+
src, aux_energy = _resize_width(
|
441 |
+
src, width, energy_mode, aux_energy, step_ratio
|
442 |
+
)
|
443 |
+
src, aux_energy = _resize_height(
|
444 |
+
src, height, energy_mode, aux_energy, step_ratio
|
445 |
+
)
|
446 |
+
else:
|
447 |
+
src, aux_energy = _resize_height(
|
448 |
+
src, height, energy_mode, aux_energy, step_ratio
|
449 |
+
)
|
450 |
+
src, aux_energy = _resize_width(
|
451 |
+
src, width, energy_mode, aux_energy, step_ratio
|
452 |
+
)
|
453 |
+
|
454 |
+
return src
|
ComfyUI_essentials/conditioning.py
ADDED
@@ -0,0 +1,280 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from nodes import MAX_RESOLUTION, ConditioningZeroOut, ConditioningSetTimestepRange, ConditioningCombine
|
2 |
+
import re
|
3 |
+
|
4 |
+
class CLIPTextEncodeSDXLSimplified:
|
5 |
+
@classmethod
|
6 |
+
def INPUT_TYPES(s):
|
7 |
+
return {"required": {
|
8 |
+
"width": ("INT", {"default": 1024.0, "min": 0, "max": MAX_RESOLUTION}),
|
9 |
+
"height": ("INT", {"default": 1024.0, "min": 0, "max": MAX_RESOLUTION}),
|
10 |
+
"size_cond_factor": ("INT", {"default": 4, "min": 1, "max": 16 }),
|
11 |
+
"text": ("STRING", {"multiline": True, "dynamicPrompts": True, "default": ""}),
|
12 |
+
"clip": ("CLIP", ),
|
13 |
+
}}
|
14 |
+
RETURN_TYPES = ("CONDITIONING",)
|
15 |
+
FUNCTION = "execute"
|
16 |
+
CATEGORY = "essentials/conditioning"
|
17 |
+
|
18 |
+
def execute(self, clip, width, height, size_cond_factor, text):
|
19 |
+
crop_w = 0
|
20 |
+
crop_h = 0
|
21 |
+
width = width*size_cond_factor
|
22 |
+
height = height*size_cond_factor
|
23 |
+
target_width = width
|
24 |
+
target_height = height
|
25 |
+
text_g = text_l = text
|
26 |
+
|
27 |
+
tokens = clip.tokenize(text_g)
|
28 |
+
tokens["l"] = clip.tokenize(text_l)["l"]
|
29 |
+
if len(tokens["l"]) != len(tokens["g"]):
|
30 |
+
empty = clip.tokenize("")
|
31 |
+
while len(tokens["l"]) < len(tokens["g"]):
|
32 |
+
tokens["l"] += empty["l"]
|
33 |
+
while len(tokens["l"]) > len(tokens["g"]):
|
34 |
+
tokens["g"] += empty["g"]
|
35 |
+
cond, pooled = clip.encode_from_tokens(tokens, return_pooled=True)
|
36 |
+
return ([[cond, {"pooled_output": pooled, "width": width, "height": height, "crop_w": crop_w, "crop_h": crop_h, "target_width": target_width, "target_height": target_height}]], )
|
37 |
+
|
38 |
+
class ConditioningCombineMultiple:
|
39 |
+
@classmethod
|
40 |
+
def INPUT_TYPES(s):
|
41 |
+
return {
|
42 |
+
"required": {
|
43 |
+
"conditioning_1": ("CONDITIONING",),
|
44 |
+
"conditioning_2": ("CONDITIONING",),
|
45 |
+
}, "optional": {
|
46 |
+
"conditioning_3": ("CONDITIONING",),
|
47 |
+
"conditioning_4": ("CONDITIONING",),
|
48 |
+
"conditioning_5": ("CONDITIONING",),
|
49 |
+
},
|
50 |
+
}
|
51 |
+
RETURN_TYPES = ("CONDITIONING",)
|
52 |
+
FUNCTION = "execute"
|
53 |
+
CATEGORY = "essentials/conditioning"
|
54 |
+
|
55 |
+
def execute(self, conditioning_1, conditioning_2, conditioning_3=None, conditioning_4=None, conditioning_5=None):
|
56 |
+
c = conditioning_1 + conditioning_2
|
57 |
+
|
58 |
+
if conditioning_3 is not None:
|
59 |
+
c += conditioning_3
|
60 |
+
if conditioning_4 is not None:
|
61 |
+
c += conditioning_4
|
62 |
+
if conditioning_5 is not None:
|
63 |
+
c += conditioning_5
|
64 |
+
|
65 |
+
return (c,)
|
66 |
+
|
67 |
+
class SD3NegativeConditioning:
|
68 |
+
@classmethod
|
69 |
+
def INPUT_TYPES(s):
|
70 |
+
return {"required": {
|
71 |
+
"conditioning": ("CONDITIONING",),
|
72 |
+
"end": ("FLOAT", {"default": 0.1, "min": 0.0, "max": 1.0, "step": 0.001 }),
|
73 |
+
}}
|
74 |
+
RETURN_TYPES = ("CONDITIONING",)
|
75 |
+
FUNCTION = "execute"
|
76 |
+
CATEGORY = "essentials/conditioning"
|
77 |
+
|
78 |
+
def execute(self, conditioning, end):
|
79 |
+
zero_c = ConditioningZeroOut().zero_out(conditioning)[0]
|
80 |
+
|
81 |
+
if end == 0:
|
82 |
+
return (zero_c, )
|
83 |
+
|
84 |
+
c = ConditioningSetTimestepRange().set_range(conditioning, 0, end)[0]
|
85 |
+
zero_c = ConditioningSetTimestepRange().set_range(zero_c, end, 1.0)[0]
|
86 |
+
c = ConditioningCombine().combine(zero_c, c)[0]
|
87 |
+
|
88 |
+
return (c, )
|
89 |
+
|
90 |
+
class FluxAttentionSeeker:
|
91 |
+
@classmethod
|
92 |
+
def INPUT_TYPES(s):
|
93 |
+
return {"required": {
|
94 |
+
"clip": ("CLIP",),
|
95 |
+
"apply_to_query": ("BOOLEAN", { "default": True }),
|
96 |
+
"apply_to_key": ("BOOLEAN", { "default": True }),
|
97 |
+
"apply_to_value": ("BOOLEAN", { "default": True }),
|
98 |
+
"apply_to_out": ("BOOLEAN", { "default": True }),
|
99 |
+
**{f"clip_l_{s}": ("FLOAT", { "display": "slider", "default": 1.0, "min": 0, "max": 5, "step": 0.05 }) for s in range(12)},
|
100 |
+
**{f"t5xxl_{s}": ("FLOAT", { "display": "slider", "default": 1.0, "min": 0, "max": 5, "step": 0.05 }) for s in range(24)},
|
101 |
+
}}
|
102 |
+
|
103 |
+
RETURN_TYPES = ("CLIP",)
|
104 |
+
FUNCTION = "execute"
|
105 |
+
|
106 |
+
CATEGORY = "essentials/conditioning"
|
107 |
+
|
108 |
+
def execute(self, clip, apply_to_query, apply_to_key, apply_to_value, apply_to_out, **values):
|
109 |
+
if not apply_to_key and not apply_to_query and not apply_to_value and not apply_to_out:
|
110 |
+
return (clip, )
|
111 |
+
|
112 |
+
m = clip.clone()
|
113 |
+
sd = m.patcher.model_state_dict()
|
114 |
+
|
115 |
+
for k in sd:
|
116 |
+
if "self_attn" in k:
|
117 |
+
layer = re.search(r"\.layers\.(\d+)\.", k)
|
118 |
+
layer = int(layer.group(1)) if layer else None
|
119 |
+
|
120 |
+
if layer is not None and values[f"clip_l_{layer}"] != 1.0:
|
121 |
+
if (apply_to_query and "q_proj" in k) or (apply_to_key and "k_proj" in k) or (apply_to_value and "v_proj" in k) or (apply_to_out and "out_proj" in k):
|
122 |
+
m.add_patches({k: (None,)}, 0.0, values[f"clip_l_{layer}"])
|
123 |
+
elif "SelfAttention" in k:
|
124 |
+
block = re.search(r"\.block\.(\d+)\.", k)
|
125 |
+
block = int(block.group(1)) if block else None
|
126 |
+
|
127 |
+
if block is not None and values[f"t5xxl_{block}"] != 1.0:
|
128 |
+
if (apply_to_query and ".q." in k) or (apply_to_key and ".k." in k) or (apply_to_value and ".v." in k) or (apply_to_out and ".o." in k):
|
129 |
+
m.add_patches({k: (None,)}, 0.0, values[f"t5xxl_{block}"])
|
130 |
+
|
131 |
+
return (m, )
|
132 |
+
|
133 |
+
class SD3AttentionSeekerLG:
|
134 |
+
@classmethod
|
135 |
+
def INPUT_TYPES(s):
|
136 |
+
return {"required": {
|
137 |
+
"clip": ("CLIP",),
|
138 |
+
"apply_to_query": ("BOOLEAN", { "default": True }),
|
139 |
+
"apply_to_key": ("BOOLEAN", { "default": True }),
|
140 |
+
"apply_to_value": ("BOOLEAN", { "default": True }),
|
141 |
+
"apply_to_out": ("BOOLEAN", { "default": True }),
|
142 |
+
**{f"clip_l_{s}": ("FLOAT", { "display": "slider", "default": 1.0, "min": 0, "max": 5, "step": 0.05 }) for s in range(12)},
|
143 |
+
**{f"clip_g_{s}": ("FLOAT", { "display": "slider", "default": 1.0, "min": 0, "max": 5, "step": 0.05 }) for s in range(32)},
|
144 |
+
}}
|
145 |
+
|
146 |
+
RETURN_TYPES = ("CLIP",)
|
147 |
+
FUNCTION = "execute"
|
148 |
+
|
149 |
+
CATEGORY = "essentials/conditioning"
|
150 |
+
|
151 |
+
def execute(self, clip, apply_to_query, apply_to_key, apply_to_value, apply_to_out, **values):
|
152 |
+
if not apply_to_key and not apply_to_query and not apply_to_value and not apply_to_out:
|
153 |
+
return (clip, )
|
154 |
+
|
155 |
+
m = clip.clone()
|
156 |
+
sd = m.patcher.model_state_dict()
|
157 |
+
|
158 |
+
for k in sd:
|
159 |
+
if "self_attn" in k:
|
160 |
+
layer = re.search(r"\.layers\.(\d+)\.", k)
|
161 |
+
layer = int(layer.group(1)) if layer else None
|
162 |
+
|
163 |
+
if layer is not None:
|
164 |
+
if "clip_l" in k and values[f"clip_l_{layer}"] != 1.0:
|
165 |
+
if (apply_to_query and "q_proj" in k) or (apply_to_key and "k_proj" in k) or (apply_to_value and "v_proj" in k) or (apply_to_out and "out_proj" in k):
|
166 |
+
m.add_patches({k: (None,)}, 0.0, values[f"clip_l_{layer}"])
|
167 |
+
elif "clip_g" in k and values[f"clip_g_{layer}"] != 1.0:
|
168 |
+
if (apply_to_query and "q_proj" in k) or (apply_to_key and "k_proj" in k) or (apply_to_value and "v_proj" in k) or (apply_to_out and "out_proj" in k):
|
169 |
+
m.add_patches({k: (None,)}, 0.0, values[f"clip_g_{layer}"])
|
170 |
+
|
171 |
+
return (m, )
|
172 |
+
|
173 |
+
class SD3AttentionSeekerT5:
|
174 |
+
@classmethod
|
175 |
+
def INPUT_TYPES(s):
|
176 |
+
return {"required": {
|
177 |
+
"clip": ("CLIP",),
|
178 |
+
"apply_to_query": ("BOOLEAN", { "default": True }),
|
179 |
+
"apply_to_key": ("BOOLEAN", { "default": True }),
|
180 |
+
"apply_to_value": ("BOOLEAN", { "default": True }),
|
181 |
+
"apply_to_out": ("BOOLEAN", { "default": True }),
|
182 |
+
**{f"t5xxl_{s}": ("FLOAT", { "display": "slider", "default": 1.0, "min": 0, "max": 5, "step": 0.05 }) for s in range(24)},
|
183 |
+
}}
|
184 |
+
|
185 |
+
RETURN_TYPES = ("CLIP",)
|
186 |
+
FUNCTION = "execute"
|
187 |
+
|
188 |
+
CATEGORY = "essentials/conditioning"
|
189 |
+
|
190 |
+
def execute(self, clip, apply_to_query, apply_to_key, apply_to_value, apply_to_out, **values):
|
191 |
+
if not apply_to_key and not apply_to_query and not apply_to_value and not apply_to_out:
|
192 |
+
return (clip, )
|
193 |
+
|
194 |
+
m = clip.clone()
|
195 |
+
sd = m.patcher.model_state_dict()
|
196 |
+
|
197 |
+
for k in sd:
|
198 |
+
if "SelfAttention" in k:
|
199 |
+
block = re.search(r"\.block\.(\d+)\.", k)
|
200 |
+
block = int(block.group(1)) if block else None
|
201 |
+
|
202 |
+
if block is not None and values[f"t5xxl_{block}"] != 1.0:
|
203 |
+
if (apply_to_query and ".q." in k) or (apply_to_key and ".k." in k) or (apply_to_value and ".v." in k) or (apply_to_out and ".o." in k):
|
204 |
+
m.add_patches({k: (None,)}, 0.0, values[f"t5xxl_{block}"])
|
205 |
+
|
206 |
+
return (m, )
|
207 |
+
|
208 |
+
class FluxBlocksBuster:
|
209 |
+
@classmethod
|
210 |
+
def INPUT_TYPES(s):
|
211 |
+
return {"required": {
|
212 |
+
"model": ("MODEL",),
|
213 |
+
"blocks": ("STRING", {"default": "## 0 = 1.0\n## 1 = 1.0\n## 2 = 1.0\n## 3 = 1.0\n## 4 = 1.0\n## 5 = 1.0\n## 6 = 1.0\n## 7 = 1.0\n## 8 = 1.0\n## 9 = 1.0\n## 10 = 1.0\n## 11 = 1.0\n## 12 = 1.0\n## 13 = 1.0\n## 14 = 1.0\n## 15 = 1.0\n## 16 = 1.0\n## 17 = 1.0\n## 18 = 1.0\n# 0 = 1.0\n# 1 = 1.0\n# 2 = 1.0\n# 3 = 1.0\n# 4 = 1.0\n# 5 = 1.0\n# 6 = 1.0\n# 7 = 1.0\n# 8 = 1.0\n# 9 = 1.0\n# 10 = 1.0\n# 11 = 1.0\n# 12 = 1.0\n# 13 = 1.0\n# 14 = 1.0\n# 15 = 1.0\n# 16 = 1.0\n# 17 = 1.0\n# 18 = 1.0\n# 19 = 1.0\n# 20 = 1.0\n# 21 = 1.0\n# 22 = 1.0\n# 23 = 1.0\n# 24 = 1.0\n# 25 = 1.0\n# 26 = 1.0\n# 27 = 1.0\n# 28 = 1.0\n# 29 = 1.0\n# 30 = 1.0\n# 31 = 1.0\n# 32 = 1.0\n# 33 = 1.0\n# 34 = 1.0\n# 35 = 1.0\n# 36 = 1.0\n# 37 = 1.0", "multiline": True, "dynamicPrompts": True}),
|
214 |
+
#**{f"double_block_{s}": ("FLOAT", { "display": "slider", "default": 1.0, "min": 0, "max": 5, "step": 0.05 }) for s in range(19)},
|
215 |
+
#**{f"single_block_{s}": ("FLOAT", { "display": "slider", "default": 1.0, "min": 0, "max": 5, "step": 0.05 }) for s in range(38)},
|
216 |
+
}}
|
217 |
+
RETURN_TYPES = ("MODEL", "STRING")
|
218 |
+
RETURN_NAMES = ("MODEL", "patched_blocks")
|
219 |
+
FUNCTION = "patch"
|
220 |
+
|
221 |
+
CATEGORY = "essentials/conditioning"
|
222 |
+
|
223 |
+
def patch(self, model, blocks):
|
224 |
+
if blocks == "":
|
225 |
+
return (model, )
|
226 |
+
|
227 |
+
m = model.clone()
|
228 |
+
sd = model.model_state_dict()
|
229 |
+
patched_blocks = []
|
230 |
+
|
231 |
+
"""
|
232 |
+
Also compatible with the following format:
|
233 |
+
|
234 |
+
double_blocks\.0\.(img|txt)_(mod|attn|mlp)\.(lin|qkv|proj|0|2)\.(weight|bias)=1.1
|
235 |
+
single_blocks\.0\.(linear[12]|modulation\.lin)\.(weight|bias)=1.1
|
236 |
+
|
237 |
+
The regex is used to match the block names
|
238 |
+
"""
|
239 |
+
|
240 |
+
blocks = blocks.split("\n")
|
241 |
+
blocks = [b.strip() for b in blocks if b.strip()]
|
242 |
+
|
243 |
+
for k in sd:
|
244 |
+
for block in blocks:
|
245 |
+
block = block.split("=")
|
246 |
+
value = float(block[1].strip()) if len(block) > 1 else 1.0
|
247 |
+
block = block[0].strip()
|
248 |
+
if block.startswith("##"):
|
249 |
+
block = r"double_blocks\." + block[2:].strip() + r"\.(img|txt)_(mod|attn|mlp)\.(lin|qkv|proj|0|2)\.(weight|bias)"
|
250 |
+
elif block.startswith("#"):
|
251 |
+
block = r"single_blocks\." + block[1:].strip() + r"\.(linear[12]|modulation\.lin)\.(weight|bias)"
|
252 |
+
|
253 |
+
if value != 1.0 and re.search(block, k):
|
254 |
+
m.add_patches({k: (None,)}, 0.0, value)
|
255 |
+
patched_blocks.append(f"{k}: {value}")
|
256 |
+
|
257 |
+
patched_blocks = "\n".join(patched_blocks)
|
258 |
+
|
259 |
+
return (m, patched_blocks,)
|
260 |
+
|
261 |
+
|
262 |
+
COND_CLASS_MAPPINGS = {
|
263 |
+
"CLIPTextEncodeSDXL+": CLIPTextEncodeSDXLSimplified,
|
264 |
+
"ConditioningCombineMultiple+": ConditioningCombineMultiple,
|
265 |
+
"SD3NegativeConditioning+": SD3NegativeConditioning,
|
266 |
+
"FluxAttentionSeeker+": FluxAttentionSeeker,
|
267 |
+
"SD3AttentionSeekerLG+": SD3AttentionSeekerLG,
|
268 |
+
"SD3AttentionSeekerT5+": SD3AttentionSeekerT5,
|
269 |
+
"FluxBlocksBuster+": FluxBlocksBuster,
|
270 |
+
}
|
271 |
+
|
272 |
+
COND_NAME_MAPPINGS = {
|
273 |
+
"CLIPTextEncodeSDXL+": "🔧 SDXL CLIPTextEncode",
|
274 |
+
"ConditioningCombineMultiple+": "🔧 Cond Combine Multiple",
|
275 |
+
"SD3NegativeConditioning+": "🔧 SD3 Negative Conditioning",
|
276 |
+
"FluxAttentionSeeker+": "🔧 Flux Attention Seeker",
|
277 |
+
"SD3AttentionSeekerLG+": "🔧 SD3 Attention Seeker L/G",
|
278 |
+
"SD3AttentionSeekerT5+": "🔧 SD3 Attention Seeker T5",
|
279 |
+
"FluxBlocksBuster+": "🔧 Flux Model Blocks Buster",
|
280 |
+
}
|
ComfyUI_essentials/fonts/ShareTechMono-Regular.ttf
ADDED
Binary file (42.8 kB). View file
|
|
ComfyUI_essentials/fonts/put_font_files_here.txt
ADDED
File without changes
|
ComfyUI_essentials/histogram_matching.py
ADDED
@@ -0,0 +1,87 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# from MIT licensed https://github.com/nemodleo/pytorch-histogram-matching
|
2 |
+
import torch
|
3 |
+
import torch.nn as nn
|
4 |
+
import torch.nn.functional as F
|
5 |
+
|
6 |
+
class Histogram_Matching(nn.Module):
|
7 |
+
def __init__(self, differentiable=False):
|
8 |
+
super(Histogram_Matching, self).__init__()
|
9 |
+
self.differentiable = differentiable
|
10 |
+
|
11 |
+
def forward(self, dst, ref):
|
12 |
+
# B C
|
13 |
+
B, C, H, W = dst.size()
|
14 |
+
# assertion
|
15 |
+
assert dst.device == ref.device
|
16 |
+
# [B*C 256]
|
17 |
+
hist_dst = self.cal_hist(dst)
|
18 |
+
hist_ref = self.cal_hist(ref)
|
19 |
+
# [B*C 256]
|
20 |
+
tables = self.cal_trans_batch(hist_dst, hist_ref)
|
21 |
+
# [B C H W]
|
22 |
+
rst = dst.clone()
|
23 |
+
for b in range(B):
|
24 |
+
for c in range(C):
|
25 |
+
rst[b,c] = tables[b*c, (dst[b,c] * 255).long()]
|
26 |
+
# [B C H W]
|
27 |
+
rst /= 255.
|
28 |
+
return rst
|
29 |
+
|
30 |
+
def cal_hist(self, img):
|
31 |
+
B, C, H, W = img.size()
|
32 |
+
# [B*C 256]
|
33 |
+
if self.differentiable:
|
34 |
+
hists = self.soft_histc_batch(img * 255, bins=256, min=0, max=256, sigma=3*25)
|
35 |
+
else:
|
36 |
+
hists = torch.stack([torch.histc(img[b,c] * 255, bins=256, min=0, max=255) for b in range(B) for c in range(C)])
|
37 |
+
hists = hists.float()
|
38 |
+
hists = F.normalize(hists, p=1)
|
39 |
+
# BC 256
|
40 |
+
bc, n = hists.size()
|
41 |
+
# [B*C 256 256]
|
42 |
+
triu = torch.ones(bc, n, n, device=hists.device).triu()
|
43 |
+
# [B*C 256]
|
44 |
+
hists = torch.bmm(hists[:,None,:], triu)[:,0,:]
|
45 |
+
return hists
|
46 |
+
|
47 |
+
def soft_histc_batch(self, x, bins=256, min=0, max=256, sigma=3*25):
|
48 |
+
# B C H W
|
49 |
+
B, C, H, W = x.size()
|
50 |
+
# [B*C H*W]
|
51 |
+
x = x.view(B*C, -1)
|
52 |
+
# 1
|
53 |
+
delta = float(max - min) / float(bins)
|
54 |
+
# [256]
|
55 |
+
centers = float(min) + delta * (torch.arange(bins, device=x.device, dtype=torch.bfloat16) + 0.5)
|
56 |
+
# [B*C 1 H*W]
|
57 |
+
x = torch.unsqueeze(x, 1)
|
58 |
+
# [1 256 1]
|
59 |
+
centers = centers[None,:,None]
|
60 |
+
# [B*C 256 H*W]
|
61 |
+
x = x - centers
|
62 |
+
# [B*C 256 H*W]
|
63 |
+
x = x.type(torch.bfloat16)
|
64 |
+
# [B*C 256 H*W]
|
65 |
+
x = torch.sigmoid(sigma * (x + delta/2)) - torch.sigmoid(sigma * (x - delta/2))
|
66 |
+
# [B*C 256]
|
67 |
+
x = x.sum(dim=2)
|
68 |
+
# [B*C 256]
|
69 |
+
x = x.type(torch.float32)
|
70 |
+
# prevent oom
|
71 |
+
# torch.cuda.empty_cache()
|
72 |
+
return x
|
73 |
+
|
74 |
+
def cal_trans_batch(self, hist_dst, hist_ref):
|
75 |
+
# [B*C 256 256]
|
76 |
+
hist_dst = hist_dst[:,None,:].repeat(1,256,1)
|
77 |
+
# [B*C 256 256]
|
78 |
+
hist_ref = hist_ref[:,:,None].repeat(1,1,256)
|
79 |
+
# [B*C 256 256]
|
80 |
+
table = hist_dst - hist_ref
|
81 |
+
# [B*C 256 256]
|
82 |
+
table = torch.where(table>=0, 1., 0.)
|
83 |
+
# [B*C 256]
|
84 |
+
table = torch.sum(table, dim=1) - 1
|
85 |
+
# [B*C 256]
|
86 |
+
table = torch.clamp(table, min=0, max=255)
|
87 |
+
return table
|
ComfyUI_essentials/image.py
ADDED
@@ -0,0 +1,1770 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from .utils import max_, min_
|
2 |
+
from nodes import MAX_RESOLUTION
|
3 |
+
import comfy.utils
|
4 |
+
from nodes import SaveImage
|
5 |
+
from node_helpers import pillow
|
6 |
+
from PIL import Image, ImageOps
|
7 |
+
|
8 |
+
import kornia
|
9 |
+
import torch
|
10 |
+
import torch.nn.functional as F
|
11 |
+
import torchvision.transforms.v2 as T
|
12 |
+
|
13 |
+
#import warnings
|
14 |
+
#warnings.filterwarnings('ignore', module="torchvision")
|
15 |
+
import math
|
16 |
+
import os
|
17 |
+
import numpy as np
|
18 |
+
import folder_paths
|
19 |
+
from pathlib import Path
|
20 |
+
import random
|
21 |
+
|
22 |
+
"""
|
23 |
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
24 |
+
Image analysis
|
25 |
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
26 |
+
"""
|
27 |
+
|
28 |
+
class ImageEnhanceDifference:
|
29 |
+
@classmethod
|
30 |
+
def INPUT_TYPES(s):
|
31 |
+
return {
|
32 |
+
"required": {
|
33 |
+
"image1": ("IMAGE",),
|
34 |
+
"image2": ("IMAGE",),
|
35 |
+
"exponent": ("FLOAT", { "default": 0.75, "min": 0.00, "max": 1.00, "step": 0.05, }),
|
36 |
+
}
|
37 |
+
}
|
38 |
+
|
39 |
+
RETURN_TYPES = ("IMAGE",)
|
40 |
+
FUNCTION = "execute"
|
41 |
+
CATEGORY = "essentials/image analysis"
|
42 |
+
|
43 |
+
def execute(self, image1, image2, exponent):
|
44 |
+
if image1.shape[1:] != image2.shape[1:]:
|
45 |
+
image2 = comfy.utils.common_upscale(image2.permute([0,3,1,2]), image1.shape[2], image1.shape[1], upscale_method='bicubic', crop='center').permute([0,2,3,1])
|
46 |
+
|
47 |
+
diff_image = image1 - image2
|
48 |
+
diff_image = torch.pow(diff_image, exponent)
|
49 |
+
diff_image = torch.clamp(diff_image, 0, 1)
|
50 |
+
|
51 |
+
return(diff_image,)
|
52 |
+
|
53 |
+
"""
|
54 |
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
55 |
+
Batch tools
|
56 |
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
57 |
+
"""
|
58 |
+
|
59 |
+
class ImageBatchMultiple:
|
60 |
+
@classmethod
|
61 |
+
def INPUT_TYPES(s):
|
62 |
+
return {
|
63 |
+
"required": {
|
64 |
+
"image_1": ("IMAGE",),
|
65 |
+
"method": (["nearest-exact", "bilinear", "area", "bicubic", "lanczos"], { "default": "lanczos" }),
|
66 |
+
}, "optional": {
|
67 |
+
"image_2": ("IMAGE",),
|
68 |
+
"image_3": ("IMAGE",),
|
69 |
+
"image_4": ("IMAGE",),
|
70 |
+
"image_5": ("IMAGE",),
|
71 |
+
},
|
72 |
+
}
|
73 |
+
RETURN_TYPES = ("IMAGE",)
|
74 |
+
FUNCTION = "execute"
|
75 |
+
CATEGORY = "essentials/image batch"
|
76 |
+
|
77 |
+
def execute(self, image_1, method, image_2=None, image_3=None, image_4=None, image_5=None):
|
78 |
+
out = image_1
|
79 |
+
|
80 |
+
if image_2 is not None:
|
81 |
+
if image_1.shape[1:] != image_2.shape[1:]:
|
82 |
+
image_2 = comfy.utils.common_upscale(image_2.movedim(-1,1), image_1.shape[2], image_1.shape[1], method, "center").movedim(1,-1)
|
83 |
+
out = torch.cat((image_1, image_2), dim=0)
|
84 |
+
if image_3 is not None:
|
85 |
+
if image_1.shape[1:] != image_3.shape[1:]:
|
86 |
+
image_3 = comfy.utils.common_upscale(image_3.movedim(-1,1), image_1.shape[2], image_1.shape[1], method, "center").movedim(1,-1)
|
87 |
+
out = torch.cat((out, image_3), dim=0)
|
88 |
+
if image_4 is not None:
|
89 |
+
if image_1.shape[1:] != image_4.shape[1:]:
|
90 |
+
image_4 = comfy.utils.common_upscale(image_4.movedim(-1,1), image_1.shape[2], image_1.shape[1], method, "center").movedim(1,-1)
|
91 |
+
out = torch.cat((out, image_4), dim=0)
|
92 |
+
if image_5 is not None:
|
93 |
+
if image_1.shape[1:] != image_5.shape[1:]:
|
94 |
+
image_5 = comfy.utils.common_upscale(image_5.movedim(-1,1), image_1.shape[2], image_1.shape[1], method, "center").movedim(1,-1)
|
95 |
+
out = torch.cat((out, image_5), dim=0)
|
96 |
+
|
97 |
+
return (out,)
|
98 |
+
|
99 |
+
|
100 |
+
class ImageExpandBatch:
|
101 |
+
@classmethod
|
102 |
+
def INPUT_TYPES(s):
|
103 |
+
return {
|
104 |
+
"required": {
|
105 |
+
"image": ("IMAGE",),
|
106 |
+
"size": ("INT", { "default": 16, "min": 1, "step": 1, }),
|
107 |
+
"method": (["expand", "repeat all", "repeat first", "repeat last"],)
|
108 |
+
}
|
109 |
+
}
|
110 |
+
|
111 |
+
RETURN_TYPES = ("IMAGE",)
|
112 |
+
FUNCTION = "execute"
|
113 |
+
CATEGORY = "essentials/image batch"
|
114 |
+
|
115 |
+
def execute(self, image, size, method):
|
116 |
+
orig_size = image.shape[0]
|
117 |
+
|
118 |
+
if orig_size == size:
|
119 |
+
return (image,)
|
120 |
+
|
121 |
+
if size <= 1:
|
122 |
+
return (image[:size],)
|
123 |
+
|
124 |
+
if 'expand' in method:
|
125 |
+
out = torch.empty([size] + list(image.shape)[1:], dtype=image.dtype, device=image.device)
|
126 |
+
if size < orig_size:
|
127 |
+
scale = (orig_size - 1) / (size - 1)
|
128 |
+
for i in range(size):
|
129 |
+
out[i] = image[min(round(i * scale), orig_size - 1)]
|
130 |
+
else:
|
131 |
+
scale = orig_size / size
|
132 |
+
for i in range(size):
|
133 |
+
out[i] = image[min(math.floor((i + 0.5) * scale), orig_size - 1)]
|
134 |
+
elif 'all' in method:
|
135 |
+
out = image.repeat([math.ceil(size / image.shape[0])] + [1] * (len(image.shape) - 1))[:size]
|
136 |
+
elif 'first' in method:
|
137 |
+
if size < image.shape[0]:
|
138 |
+
out = image[:size]
|
139 |
+
else:
|
140 |
+
out = torch.cat([image[:1].repeat(size-image.shape[0], 1, 1, 1), image], dim=0)
|
141 |
+
elif 'last' in method:
|
142 |
+
if size < image.shape[0]:
|
143 |
+
out = image[:size]
|
144 |
+
else:
|
145 |
+
out = torch.cat((image, image[-1:].repeat((size-image.shape[0], 1, 1, 1))), dim=0)
|
146 |
+
|
147 |
+
return (out,)
|
148 |
+
|
149 |
+
class ImageFromBatch:
|
150 |
+
@classmethod
|
151 |
+
def INPUT_TYPES(s):
|
152 |
+
return {
|
153 |
+
"required": {
|
154 |
+
"image": ("IMAGE", ),
|
155 |
+
"start": ("INT", { "default": 0, "min": 0, "step": 1, }),
|
156 |
+
"length": ("INT", { "default": -1, "min": -1, "step": 1, }),
|
157 |
+
}
|
158 |
+
}
|
159 |
+
|
160 |
+
RETURN_TYPES = ("IMAGE",)
|
161 |
+
FUNCTION = "execute"
|
162 |
+
CATEGORY = "essentials/image batch"
|
163 |
+
|
164 |
+
def execute(self, image, start, length):
|
165 |
+
if length<0:
|
166 |
+
length = image.shape[0]
|
167 |
+
start = min(start, image.shape[0]-1)
|
168 |
+
length = min(image.shape[0]-start, length)
|
169 |
+
return (image[start:start + length], )
|
170 |
+
|
171 |
+
|
172 |
+
class ImageListToBatch:
|
173 |
+
@classmethod
|
174 |
+
def INPUT_TYPES(s):
|
175 |
+
return {
|
176 |
+
"required": {
|
177 |
+
"image": ("IMAGE",),
|
178 |
+
}
|
179 |
+
}
|
180 |
+
|
181 |
+
RETURN_TYPES = ("IMAGE",)
|
182 |
+
FUNCTION = "execute"
|
183 |
+
INPUT_IS_LIST = True
|
184 |
+
CATEGORY = "essentials/image batch"
|
185 |
+
|
186 |
+
def execute(self, image):
|
187 |
+
shape = image[0].shape[1:3]
|
188 |
+
out = []
|
189 |
+
|
190 |
+
for i in range(len(image)):
|
191 |
+
img = image[i]
|
192 |
+
if image[i].shape[1:3] != shape:
|
193 |
+
img = comfy.utils.common_upscale(img.permute([0,3,1,2]), shape[1], shape[0], upscale_method='bicubic', crop='center').permute([0,2,3,1])
|
194 |
+
out.append(img)
|
195 |
+
|
196 |
+
out = torch.cat(out, dim=0)
|
197 |
+
|
198 |
+
return (out,)
|
199 |
+
|
200 |
+
class ImageBatchToList:
|
201 |
+
@classmethod
|
202 |
+
def INPUT_TYPES(s):
|
203 |
+
return {
|
204 |
+
"required": {
|
205 |
+
"image": ("IMAGE",),
|
206 |
+
}
|
207 |
+
}
|
208 |
+
|
209 |
+
RETURN_TYPES = ("IMAGE",)
|
210 |
+
OUTPUT_IS_LIST = (True,)
|
211 |
+
FUNCTION = "execute"
|
212 |
+
CATEGORY = "essentials/image batch"
|
213 |
+
|
214 |
+
def execute(self, image):
|
215 |
+
return ([image[i].unsqueeze(0) for i in range(image.shape[0])], )
|
216 |
+
|
217 |
+
|
218 |
+
"""
|
219 |
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
220 |
+
Image manipulation
|
221 |
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
222 |
+
"""
|
223 |
+
|
224 |
+
class ImageCompositeFromMaskBatch:
|
225 |
+
@classmethod
|
226 |
+
def INPUT_TYPES(s):
|
227 |
+
return {
|
228 |
+
"required": {
|
229 |
+
"image_from": ("IMAGE", ),
|
230 |
+
"image_to": ("IMAGE", ),
|
231 |
+
"mask": ("MASK", )
|
232 |
+
}
|
233 |
+
}
|
234 |
+
|
235 |
+
RETURN_TYPES = ("IMAGE",)
|
236 |
+
FUNCTION = "execute"
|
237 |
+
CATEGORY = "essentials/image manipulation"
|
238 |
+
|
239 |
+
def execute(self, image_from, image_to, mask):
|
240 |
+
frames = mask.shape[0]
|
241 |
+
|
242 |
+
if image_from.shape[1] != image_to.shape[1] or image_from.shape[2] != image_to.shape[2]:
|
243 |
+
image_to = comfy.utils.common_upscale(image_to.permute([0,3,1,2]), image_from.shape[2], image_from.shape[1], upscale_method='bicubic', crop='center').permute([0,2,3,1])
|
244 |
+
|
245 |
+
if frames < image_from.shape[0]:
|
246 |
+
image_from = image_from[:frames]
|
247 |
+
elif frames > image_from.shape[0]:
|
248 |
+
image_from = torch.cat((image_from, image_from[-1].unsqueeze(0).repeat(frames-image_from.shape[0], 1, 1, 1)), dim=0)
|
249 |
+
|
250 |
+
mask = mask.unsqueeze(3).repeat(1, 1, 1, 3)
|
251 |
+
|
252 |
+
if image_from.shape[1] != mask.shape[1] or image_from.shape[2] != mask.shape[2]:
|
253 |
+
mask = comfy.utils.common_upscale(mask.permute([0,3,1,2]), image_from.shape[2], image_from.shape[1], upscale_method='bicubic', crop='center').permute([0,2,3,1])
|
254 |
+
|
255 |
+
out = mask * image_to + (1 - mask) * image_from
|
256 |
+
|
257 |
+
return (out, )
|
258 |
+
|
259 |
+
class ImageComposite:
|
260 |
+
@classmethod
|
261 |
+
def INPUT_TYPES(s):
|
262 |
+
return {
|
263 |
+
"required": {
|
264 |
+
"destination": ("IMAGE",),
|
265 |
+
"source": ("IMAGE",),
|
266 |
+
"x": ("INT", { "default": 0, "min": -MAX_RESOLUTION, "max": MAX_RESOLUTION, "step": 1 }),
|
267 |
+
"y": ("INT", { "default": 0, "min": -MAX_RESOLUTION, "max": MAX_RESOLUTION, "step": 1 }),
|
268 |
+
"offset_x": ("INT", { "default": 0, "min": -MAX_RESOLUTION, "max": MAX_RESOLUTION, "step": 1 }),
|
269 |
+
"offset_y": ("INT", { "default": 0, "min": -MAX_RESOLUTION, "max": MAX_RESOLUTION, "step": 1 }),
|
270 |
+
},
|
271 |
+
"optional": {
|
272 |
+
"mask": ("MASK",),
|
273 |
+
}
|
274 |
+
}
|
275 |
+
|
276 |
+
RETURN_TYPES = ("IMAGE",)
|
277 |
+
FUNCTION = "execute"
|
278 |
+
CATEGORY = "essentials/image manipulation"
|
279 |
+
|
280 |
+
def execute(self, destination, source, x, y, offset_x, offset_y, mask=None):
|
281 |
+
if mask is None:
|
282 |
+
mask = torch.ones_like(source)[:,:,:,0]
|
283 |
+
|
284 |
+
mask = mask.unsqueeze(-1).repeat(1, 1, 1, 3)
|
285 |
+
|
286 |
+
if mask.shape[1:3] != source.shape[1:3]:
|
287 |
+
mask = F.interpolate(mask.permute([0, 3, 1, 2]), size=(source.shape[1], source.shape[2]), mode='bicubic')
|
288 |
+
mask = mask.permute([0, 2, 3, 1])
|
289 |
+
|
290 |
+
if mask.shape[0] > source.shape[0]:
|
291 |
+
mask = mask[:source.shape[0]]
|
292 |
+
elif mask.shape[0] < source.shape[0]:
|
293 |
+
mask = torch.cat((mask, mask[-1:].repeat((source.shape[0]-mask.shape[0], 1, 1, 1))), dim=0)
|
294 |
+
|
295 |
+
if destination.shape[0] > source.shape[0]:
|
296 |
+
destination = destination[:source.shape[0]]
|
297 |
+
elif destination.shape[0] < source.shape[0]:
|
298 |
+
destination = torch.cat((destination, destination[-1:].repeat((source.shape[0]-destination.shape[0], 1, 1, 1))), dim=0)
|
299 |
+
|
300 |
+
if not isinstance(x, list):
|
301 |
+
x = [x]
|
302 |
+
if not isinstance(y, list):
|
303 |
+
y = [y]
|
304 |
+
|
305 |
+
if len(x) < destination.shape[0]:
|
306 |
+
x = x + [x[-1]] * (destination.shape[0] - len(x))
|
307 |
+
if len(y) < destination.shape[0]:
|
308 |
+
y = y + [y[-1]] * (destination.shape[0] - len(y))
|
309 |
+
|
310 |
+
x = [i + offset_x for i in x]
|
311 |
+
y = [i + offset_y for i in y]
|
312 |
+
|
313 |
+
output = []
|
314 |
+
for i in range(destination.shape[0]):
|
315 |
+
d = destination[i].clone()
|
316 |
+
s = source[i]
|
317 |
+
m = mask[i]
|
318 |
+
|
319 |
+
if x[i]+source.shape[2] > destination.shape[2]:
|
320 |
+
s = s[:, :, :destination.shape[2]-x[i], :]
|
321 |
+
m = m[:, :, :destination.shape[2]-x[i], :]
|
322 |
+
if y[i]+source.shape[1] > destination.shape[1]:
|
323 |
+
s = s[:, :destination.shape[1]-y[i], :, :]
|
324 |
+
m = m[:destination.shape[1]-y[i], :, :]
|
325 |
+
|
326 |
+
#output.append(s * m + d[y[i]:y[i]+s.shape[0], x[i]:x[i]+s.shape[1], :] * (1 - m))
|
327 |
+
d[y[i]:y[i]+s.shape[0], x[i]:x[i]+s.shape[1], :] = s * m + d[y[i]:y[i]+s.shape[0], x[i]:x[i]+s.shape[1], :] * (1 - m)
|
328 |
+
output.append(d)
|
329 |
+
|
330 |
+
output = torch.stack(output)
|
331 |
+
|
332 |
+
# apply the source to the destination at XY position using the mask
|
333 |
+
#for i in range(destination.shape[0]):
|
334 |
+
# output[i, y[i]:y[i]+source.shape[1], x[i]:x[i]+source.shape[2], :] = source * mask + destination[i, y[i]:y[i]+source.shape[1], x[i]:x[i]+source.shape[2], :] * (1 - mask)
|
335 |
+
|
336 |
+
#for x_, y_ in zip(x, y):
|
337 |
+
# output[:, y_:y_+source.shape[1], x_:x_+source.shape[2], :] = source * mask + destination[:, y_:y_+source.shape[1], x_:x_+source.shape[2], :] * (1 - mask)
|
338 |
+
|
339 |
+
#output[:, y:y+source.shape[1], x:x+source.shape[2], :] = source * mask + destination[:, y:y+source.shape[1], x:x+source.shape[2], :] * (1 - mask)
|
340 |
+
#output = destination * (1 - mask) + source * mask
|
341 |
+
|
342 |
+
return (output,)
|
343 |
+
|
344 |
+
class ImageResize:
|
345 |
+
@classmethod
|
346 |
+
def INPUT_TYPES(s):
|
347 |
+
return {
|
348 |
+
"required": {
|
349 |
+
"image": ("IMAGE",),
|
350 |
+
"width": ("INT", { "default": 512, "min": 0, "max": MAX_RESOLUTION, "step": 1, }),
|
351 |
+
"height": ("INT", { "default": 512, "min": 0, "max": MAX_RESOLUTION, "step": 1, }),
|
352 |
+
"interpolation": (["nearest", "bilinear", "bicubic", "area", "nearest-exact", "lanczos"],),
|
353 |
+
"method": (["stretch", "keep proportion", "fill / crop", "pad"],),
|
354 |
+
"condition": (["always", "downscale if bigger", "upscale if smaller", "if bigger area", "if smaller area"],),
|
355 |
+
"multiple_of": ("INT", { "default": 0, "min": 0, "max": 512, "step": 1, }),
|
356 |
+
}
|
357 |
+
}
|
358 |
+
|
359 |
+
RETURN_TYPES = ("IMAGE", "INT", "INT",)
|
360 |
+
RETURN_NAMES = ("IMAGE", "width", "height",)
|
361 |
+
FUNCTION = "execute"
|
362 |
+
CATEGORY = "essentials/image manipulation"
|
363 |
+
|
364 |
+
def execute(self, image, width, height, method="stretch", interpolation="nearest", condition="always", multiple_of=0, keep_proportion=False):
|
365 |
+
_, oh, ow, _ = image.shape
|
366 |
+
x = y = x2 = y2 = 0
|
367 |
+
pad_left = pad_right = pad_top = pad_bottom = 0
|
368 |
+
|
369 |
+
if keep_proportion:
|
370 |
+
method = "keep proportion"
|
371 |
+
|
372 |
+
if multiple_of > 1:
|
373 |
+
width = width - (width % multiple_of)
|
374 |
+
height = height - (height % multiple_of)
|
375 |
+
|
376 |
+
if method == 'keep proportion' or method == 'pad':
|
377 |
+
if width == 0 and oh < height:
|
378 |
+
width = MAX_RESOLUTION
|
379 |
+
elif width == 0 and oh >= height:
|
380 |
+
width = ow
|
381 |
+
|
382 |
+
if height == 0 and ow < width:
|
383 |
+
height = MAX_RESOLUTION
|
384 |
+
elif height == 0 and ow >= width:
|
385 |
+
height = oh
|
386 |
+
|
387 |
+
ratio = min(width / ow, height / oh)
|
388 |
+
new_width = round(ow*ratio)
|
389 |
+
new_height = round(oh*ratio)
|
390 |
+
|
391 |
+
if method == 'pad':
|
392 |
+
pad_left = (width - new_width) // 2
|
393 |
+
pad_right = width - new_width - pad_left
|
394 |
+
pad_top = (height - new_height) // 2
|
395 |
+
pad_bottom = height - new_height - pad_top
|
396 |
+
|
397 |
+
width = new_width
|
398 |
+
height = new_height
|
399 |
+
elif method.startswith('fill'):
|
400 |
+
width = width if width > 0 else ow
|
401 |
+
height = height if height > 0 else oh
|
402 |
+
|
403 |
+
ratio = max(width / ow, height / oh)
|
404 |
+
new_width = round(ow*ratio)
|
405 |
+
new_height = round(oh*ratio)
|
406 |
+
x = (new_width - width) // 2
|
407 |
+
y = (new_height - height) // 2
|
408 |
+
x2 = x + width
|
409 |
+
y2 = y + height
|
410 |
+
if x2 > new_width:
|
411 |
+
x -= (x2 - new_width)
|
412 |
+
if x < 0:
|
413 |
+
x = 0
|
414 |
+
if y2 > new_height:
|
415 |
+
y -= (y2 - new_height)
|
416 |
+
if y < 0:
|
417 |
+
y = 0
|
418 |
+
width = new_width
|
419 |
+
height = new_height
|
420 |
+
else:
|
421 |
+
width = width if width > 0 else ow
|
422 |
+
height = height if height > 0 else oh
|
423 |
+
|
424 |
+
if "always" in condition \
|
425 |
+
or ("downscale if bigger" == condition and (oh > height or ow > width)) or ("upscale if smaller" == condition and (oh < height or ow < width)) \
|
426 |
+
or ("bigger area" in condition and (oh * ow > height * width)) or ("smaller area" in condition and (oh * ow < height * width)):
|
427 |
+
|
428 |
+
outputs = image.permute(0,3,1,2)
|
429 |
+
|
430 |
+
if interpolation == "lanczos":
|
431 |
+
outputs = comfy.utils.lanczos(outputs, width, height)
|
432 |
+
else:
|
433 |
+
outputs = F.interpolate(outputs, size=(height, width), mode=interpolation)
|
434 |
+
|
435 |
+
if method == 'pad':
|
436 |
+
if pad_left > 0 or pad_right > 0 or pad_top > 0 or pad_bottom > 0:
|
437 |
+
outputs = F.pad(outputs, (pad_left, pad_right, pad_top, pad_bottom), value=0)
|
438 |
+
|
439 |
+
outputs = outputs.permute(0,2,3,1)
|
440 |
+
|
441 |
+
if method.startswith('fill'):
|
442 |
+
if x > 0 or y > 0 or x2 > 0 or y2 > 0:
|
443 |
+
outputs = outputs[:, y:y2, x:x2, :]
|
444 |
+
else:
|
445 |
+
outputs = image
|
446 |
+
|
447 |
+
if multiple_of > 1 and (outputs.shape[2] % multiple_of != 0 or outputs.shape[1] % multiple_of != 0):
|
448 |
+
width = outputs.shape[2]
|
449 |
+
height = outputs.shape[1]
|
450 |
+
x = (width % multiple_of) // 2
|
451 |
+
y = (height % multiple_of) // 2
|
452 |
+
x2 = width - ((width % multiple_of) - x)
|
453 |
+
y2 = height - ((height % multiple_of) - y)
|
454 |
+
outputs = outputs[:, y:y2, x:x2, :]
|
455 |
+
|
456 |
+
outputs = torch.clamp(outputs, 0, 1)
|
457 |
+
|
458 |
+
return(outputs, outputs.shape[2], outputs.shape[1],)
|
459 |
+
|
460 |
+
class ImageFlip:
|
461 |
+
@classmethod
|
462 |
+
def INPUT_TYPES(s):
|
463 |
+
return {
|
464 |
+
"required": {
|
465 |
+
"image": ("IMAGE",),
|
466 |
+
"axis": (["x", "y", "xy"],),
|
467 |
+
}
|
468 |
+
}
|
469 |
+
|
470 |
+
RETURN_TYPES = ("IMAGE",)
|
471 |
+
FUNCTION = "execute"
|
472 |
+
CATEGORY = "essentials/image manipulation"
|
473 |
+
|
474 |
+
def execute(self, image, axis):
|
475 |
+
dim = ()
|
476 |
+
if "y" in axis:
|
477 |
+
dim += (1,)
|
478 |
+
if "x" in axis:
|
479 |
+
dim += (2,)
|
480 |
+
image = torch.flip(image, dim)
|
481 |
+
|
482 |
+
return(image,)
|
483 |
+
|
484 |
+
class ImageCrop:
|
485 |
+
@classmethod
|
486 |
+
def INPUT_TYPES(s):
|
487 |
+
return {
|
488 |
+
"required": {
|
489 |
+
"image": ("IMAGE",),
|
490 |
+
"width": ("INT", { "default": 256, "min": 0, "max": MAX_RESOLUTION, "step": 8, }),
|
491 |
+
"height": ("INT", { "default": 256, "min": 0, "max": MAX_RESOLUTION, "step": 8, }),
|
492 |
+
"position": (["top-left", "top-center", "top-right", "right-center", "bottom-right", "bottom-center", "bottom-left", "left-center", "center"],),
|
493 |
+
"x_offset": ("INT", { "default": 0, "min": -99999, "step": 1, }),
|
494 |
+
"y_offset": ("INT", { "default": 0, "min": -99999, "step": 1, }),
|
495 |
+
}
|
496 |
+
}
|
497 |
+
|
498 |
+
RETURN_TYPES = ("IMAGE","INT","INT",)
|
499 |
+
RETURN_NAMES = ("IMAGE","x","y",)
|
500 |
+
FUNCTION = "execute"
|
501 |
+
CATEGORY = "essentials/image manipulation"
|
502 |
+
|
503 |
+
def execute(self, image, width, height, position, x_offset, y_offset):
|
504 |
+
_, oh, ow, _ = image.shape
|
505 |
+
|
506 |
+
width = min(ow, width)
|
507 |
+
height = min(oh, height)
|
508 |
+
|
509 |
+
if "center" in position:
|
510 |
+
x = round((ow-width) / 2)
|
511 |
+
y = round((oh-height) / 2)
|
512 |
+
if "top" in position:
|
513 |
+
y = 0
|
514 |
+
if "bottom" in position:
|
515 |
+
y = oh-height
|
516 |
+
if "left" in position:
|
517 |
+
x = 0
|
518 |
+
if "right" in position:
|
519 |
+
x = ow-width
|
520 |
+
|
521 |
+
x += x_offset
|
522 |
+
y += y_offset
|
523 |
+
|
524 |
+
x2 = x+width
|
525 |
+
y2 = y+height
|
526 |
+
|
527 |
+
if x2 > ow:
|
528 |
+
x2 = ow
|
529 |
+
if x < 0:
|
530 |
+
x = 0
|
531 |
+
if y2 > oh:
|
532 |
+
y2 = oh
|
533 |
+
if y < 0:
|
534 |
+
y = 0
|
535 |
+
|
536 |
+
image = image[:, y:y2, x:x2, :]
|
537 |
+
|
538 |
+
return(image, x, y, )
|
539 |
+
|
540 |
+
class ImageTile:
|
541 |
+
@classmethod
|
542 |
+
def INPUT_TYPES(s):
|
543 |
+
return {
|
544 |
+
"required": {
|
545 |
+
"image": ("IMAGE",),
|
546 |
+
"rows": ("INT", { "default": 2, "min": 1, "max": 256, "step": 1, }),
|
547 |
+
"cols": ("INT", { "default": 2, "min": 1, "max": 256, "step": 1, }),
|
548 |
+
"overlap": ("FLOAT", { "default": 0, "min": 0, "max": 0.5, "step": 0.01, }),
|
549 |
+
"overlap_x": ("INT", { "default": 0, "min": 0, "max": MAX_RESOLUTION//2, "step": 1, }),
|
550 |
+
"overlap_y": ("INT", { "default": 0, "min": 0, "max": MAX_RESOLUTION//2, "step": 1, }),
|
551 |
+
}
|
552 |
+
}
|
553 |
+
|
554 |
+
RETURN_TYPES = ("IMAGE", "INT", "INT", "INT", "INT")
|
555 |
+
RETURN_NAMES = ("IMAGE", "tile_width", "tile_height", "overlap_x", "overlap_y",)
|
556 |
+
FUNCTION = "execute"
|
557 |
+
CATEGORY = "essentials/image manipulation"
|
558 |
+
|
559 |
+
def execute(self, image, rows, cols, overlap, overlap_x, overlap_y):
|
560 |
+
h, w = image.shape[1:3]
|
561 |
+
tile_h = h // rows
|
562 |
+
tile_w = w // cols
|
563 |
+
h = tile_h * rows
|
564 |
+
w = tile_w * cols
|
565 |
+
overlap_h = int(tile_h * overlap) + overlap_y
|
566 |
+
overlap_w = int(tile_w * overlap) + overlap_x
|
567 |
+
|
568 |
+
# max overlap is half of the tile size
|
569 |
+
overlap_h = min(tile_h // 2, overlap_h)
|
570 |
+
overlap_w = min(tile_w // 2, overlap_w)
|
571 |
+
|
572 |
+
if rows == 1:
|
573 |
+
overlap_h = 0
|
574 |
+
if cols == 1:
|
575 |
+
overlap_w = 0
|
576 |
+
|
577 |
+
tiles = []
|
578 |
+
for i in range(rows):
|
579 |
+
for j in range(cols):
|
580 |
+
y1 = i * tile_h
|
581 |
+
x1 = j * tile_w
|
582 |
+
|
583 |
+
if i > 0:
|
584 |
+
y1 -= overlap_h
|
585 |
+
if j > 0:
|
586 |
+
x1 -= overlap_w
|
587 |
+
|
588 |
+
y2 = y1 + tile_h + overlap_h
|
589 |
+
x2 = x1 + tile_w + overlap_w
|
590 |
+
|
591 |
+
if y2 > h:
|
592 |
+
y2 = h
|
593 |
+
y1 = y2 - tile_h - overlap_h
|
594 |
+
if x2 > w:
|
595 |
+
x2 = w
|
596 |
+
x1 = x2 - tile_w - overlap_w
|
597 |
+
|
598 |
+
tiles.append(image[:, y1:y2, x1:x2, :])
|
599 |
+
tiles = torch.cat(tiles, dim=0)
|
600 |
+
|
601 |
+
return(tiles, tile_w+overlap_w, tile_h+overlap_h, overlap_w, overlap_h,)
|
602 |
+
|
603 |
+
class ImageUntile:
|
604 |
+
@classmethod
|
605 |
+
def INPUT_TYPES(s):
|
606 |
+
return {
|
607 |
+
"required": {
|
608 |
+
"tiles": ("IMAGE",),
|
609 |
+
"overlap_x": ("INT", { "default": 0, "min": 0, "max": MAX_RESOLUTION//2, "step": 1, }),
|
610 |
+
"overlap_y": ("INT", { "default": 0, "min": 0, "max": MAX_RESOLUTION//2, "step": 1, }),
|
611 |
+
"rows": ("INT", { "default": 2, "min": 1, "max": 256, "step": 1, }),
|
612 |
+
"cols": ("INT", { "default": 2, "min": 1, "max": 256, "step": 1, }),
|
613 |
+
}
|
614 |
+
}
|
615 |
+
|
616 |
+
RETURN_TYPES = ("IMAGE",)
|
617 |
+
FUNCTION = "execute"
|
618 |
+
CATEGORY = "essentials/image manipulation"
|
619 |
+
|
620 |
+
def execute(self, tiles, overlap_x, overlap_y, rows, cols):
|
621 |
+
tile_h, tile_w = tiles.shape[1:3]
|
622 |
+
tile_h -= overlap_y
|
623 |
+
tile_w -= overlap_x
|
624 |
+
out_w = cols * tile_w
|
625 |
+
out_h = rows * tile_h
|
626 |
+
|
627 |
+
out = torch.zeros((1, out_h, out_w, tiles.shape[3]), device=tiles.device, dtype=tiles.dtype)
|
628 |
+
|
629 |
+
for i in range(rows):
|
630 |
+
for j in range(cols):
|
631 |
+
y1 = i * tile_h
|
632 |
+
x1 = j * tile_w
|
633 |
+
|
634 |
+
if i > 0:
|
635 |
+
y1 -= overlap_y
|
636 |
+
if j > 0:
|
637 |
+
x1 -= overlap_x
|
638 |
+
|
639 |
+
y2 = y1 + tile_h + overlap_y
|
640 |
+
x2 = x1 + tile_w + overlap_x
|
641 |
+
|
642 |
+
if y2 > out_h:
|
643 |
+
y2 = out_h
|
644 |
+
y1 = y2 - tile_h - overlap_y
|
645 |
+
if x2 > out_w:
|
646 |
+
x2 = out_w
|
647 |
+
x1 = x2 - tile_w - overlap_x
|
648 |
+
|
649 |
+
mask = torch.ones((1, tile_h+overlap_y, tile_w+overlap_x), device=tiles.device, dtype=tiles.dtype)
|
650 |
+
|
651 |
+
# feather the overlap on top
|
652 |
+
if i > 0 and overlap_y > 0:
|
653 |
+
mask[:, :overlap_y, :] *= torch.linspace(0, 1, overlap_y, device=tiles.device, dtype=tiles.dtype).unsqueeze(1)
|
654 |
+
# feather the overlap on bottom
|
655 |
+
#if i < rows - 1:
|
656 |
+
# mask[:, -overlap_y:, :] *= torch.linspace(1, 0, overlap_y, device=tiles.device, dtype=tiles.dtype).unsqueeze(1)
|
657 |
+
# feather the overlap on left
|
658 |
+
if j > 0 and overlap_x > 0:
|
659 |
+
mask[:, :, :overlap_x] *= torch.linspace(0, 1, overlap_x, device=tiles.device, dtype=tiles.dtype).unsqueeze(0)
|
660 |
+
# feather the overlap on right
|
661 |
+
#if j < cols - 1:
|
662 |
+
# mask[:, :, -overlap_x:] *= torch.linspace(1, 0, overlap_x, device=tiles.device, dtype=tiles.dtype).unsqueeze(0)
|
663 |
+
|
664 |
+
mask = mask.unsqueeze(-1).repeat(1, 1, 1, tiles.shape[3])
|
665 |
+
tile = tiles[i * cols + j] * mask
|
666 |
+
out[:, y1:y2, x1:x2, :] = out[:, y1:y2, x1:x2, :] * (1 - mask) + tile
|
667 |
+
return(out, )
|
668 |
+
|
669 |
+
class ImageSeamCarving:
|
670 |
+
@classmethod
|
671 |
+
def INPUT_TYPES(cls):
|
672 |
+
return {
|
673 |
+
"required": {
|
674 |
+
"image": ("IMAGE",),
|
675 |
+
"width": ("INT", { "default": 512, "min": 1, "max": MAX_RESOLUTION, "step": 1, }),
|
676 |
+
"height": ("INT", { "default": 512, "min": 1, "max": MAX_RESOLUTION, "step": 1, }),
|
677 |
+
"energy": (["backward", "forward"],),
|
678 |
+
"order": (["width-first", "height-first"],),
|
679 |
+
},
|
680 |
+
"optional": {
|
681 |
+
"keep_mask": ("MASK",),
|
682 |
+
"drop_mask": ("MASK",),
|
683 |
+
}
|
684 |
+
}
|
685 |
+
|
686 |
+
RETURN_TYPES = ("IMAGE",)
|
687 |
+
CATEGORY = "essentials/image manipulation"
|
688 |
+
FUNCTION = "execute"
|
689 |
+
|
690 |
+
def execute(self, image, width, height, energy, order, keep_mask=None, drop_mask=None):
|
691 |
+
from .carve import seam_carving
|
692 |
+
|
693 |
+
img = image.permute([0, 3, 1, 2])
|
694 |
+
|
695 |
+
if keep_mask is not None:
|
696 |
+
#keep_mask = keep_mask.reshape((-1, 1, keep_mask.shape[-2], keep_mask.shape[-1])).movedim(1, -1)
|
697 |
+
keep_mask = keep_mask.unsqueeze(1)
|
698 |
+
|
699 |
+
if keep_mask.shape[2] != img.shape[2] or keep_mask.shape[3] != img.shape[3]:
|
700 |
+
keep_mask = F.interpolate(keep_mask, size=(img.shape[2], img.shape[3]), mode="bilinear")
|
701 |
+
if drop_mask is not None:
|
702 |
+
drop_mask = drop_mask.unsqueeze(1)
|
703 |
+
|
704 |
+
if drop_mask.shape[2] != img.shape[2] or drop_mask.shape[3] != img.shape[3]:
|
705 |
+
drop_mask = F.interpolate(drop_mask, size=(img.shape[2], img.shape[3]), mode="bilinear")
|
706 |
+
|
707 |
+
out = []
|
708 |
+
for i in range(img.shape[0]):
|
709 |
+
resized = seam_carving(
|
710 |
+
T.ToPILImage()(img[i]),
|
711 |
+
size=(width, height),
|
712 |
+
energy_mode=energy,
|
713 |
+
order=order,
|
714 |
+
keep_mask=T.ToPILImage()(keep_mask[i]) if keep_mask is not None else None,
|
715 |
+
drop_mask=T.ToPILImage()(drop_mask[i]) if drop_mask is not None else None,
|
716 |
+
)
|
717 |
+
out.append(T.ToTensor()(resized))
|
718 |
+
|
719 |
+
out = torch.stack(out).permute([0, 2, 3, 1])
|
720 |
+
|
721 |
+
return(out, )
|
722 |
+
|
723 |
+
class ImageRandomTransform:
|
724 |
+
@classmethod
|
725 |
+
def INPUT_TYPES(s):
|
726 |
+
return {
|
727 |
+
"required": {
|
728 |
+
"image": ("IMAGE",),
|
729 |
+
"seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}),
|
730 |
+
"repeat": ("INT", { "default": 1, "min": 1, "max": 256, "step": 1, }),
|
731 |
+
"variation": ("FLOAT", { "default": 0.1, "min": 0.0, "max": 1.0, "step": 0.05, }),
|
732 |
+
}
|
733 |
+
}
|
734 |
+
|
735 |
+
RETURN_TYPES = ("IMAGE",)
|
736 |
+
FUNCTION = "execute"
|
737 |
+
CATEGORY = "essentials/image manipulation"
|
738 |
+
|
739 |
+
def execute(self, image, seed, repeat, variation):
|
740 |
+
h, w = image.shape[1:3]
|
741 |
+
image = image.repeat(repeat, 1, 1, 1).permute([0, 3, 1, 2])
|
742 |
+
|
743 |
+
distortion = 0.2 * variation
|
744 |
+
rotation = 5 * variation
|
745 |
+
brightness = 0.5 * variation
|
746 |
+
contrast = 0.5 * variation
|
747 |
+
saturation = 0.5 * variation
|
748 |
+
hue = 0.2 * variation
|
749 |
+
scale = 0.5 * variation
|
750 |
+
|
751 |
+
torch.manual_seed(seed)
|
752 |
+
|
753 |
+
out = []
|
754 |
+
for i in image:
|
755 |
+
tramsforms = T.Compose([
|
756 |
+
T.RandomPerspective(distortion_scale=distortion, p=0.5),
|
757 |
+
T.RandomRotation(degrees=rotation, interpolation=T.InterpolationMode.BILINEAR, expand=True),
|
758 |
+
T.ColorJitter(brightness=brightness, contrast=contrast, saturation=saturation, hue=(-hue, hue)),
|
759 |
+
T.RandomHorizontalFlip(p=0.5),
|
760 |
+
T.RandomResizedCrop((h, w), scale=(1-scale, 1+scale), ratio=(w/h, w/h), interpolation=T.InterpolationMode.BICUBIC),
|
761 |
+
])
|
762 |
+
out.append(tramsforms(i.unsqueeze(0)))
|
763 |
+
|
764 |
+
out = torch.cat(out, dim=0).permute([0, 2, 3, 1]).clamp(0, 1)
|
765 |
+
|
766 |
+
return (out,)
|
767 |
+
|
768 |
+
class RemBGSession:
|
769 |
+
@classmethod
|
770 |
+
def INPUT_TYPES(s):
|
771 |
+
return {
|
772 |
+
"required": {
|
773 |
+
"model": (["u2net: general purpose", "u2netp: lightweight general purpose", "u2net_human_seg: human segmentation", "u2net_cloth_seg: cloths Parsing", "silueta: very small u2net", "isnet-general-use: general purpose", "isnet-anime: anime illustrations", "sam: general purpose"],),
|
774 |
+
"providers": (['CPU', 'CUDA', 'ROCM', 'DirectML', 'OpenVINO', 'CoreML', 'Tensorrt', 'Azure'],),
|
775 |
+
},
|
776 |
+
}
|
777 |
+
|
778 |
+
RETURN_TYPES = ("REMBG_SESSION",)
|
779 |
+
FUNCTION = "execute"
|
780 |
+
CATEGORY = "essentials/image manipulation"
|
781 |
+
|
782 |
+
def execute(self, model, providers):
|
783 |
+
from rembg import new_session, remove
|
784 |
+
|
785 |
+
model = model.split(":")[0]
|
786 |
+
|
787 |
+
class Session:
|
788 |
+
def __init__(self, model, providers):
|
789 |
+
self.session = new_session(model, providers=[providers+"ExecutionProvider"])
|
790 |
+
def process(self, image):
|
791 |
+
return remove(image, session=self.session)
|
792 |
+
|
793 |
+
return (Session(model, providers),)
|
794 |
+
|
795 |
+
class TransparentBGSession:
|
796 |
+
@classmethod
|
797 |
+
def INPUT_TYPES(s):
|
798 |
+
return {
|
799 |
+
"required": {
|
800 |
+
"mode": (["base", "fast", "base-nightly"],),
|
801 |
+
"use_jit": ("BOOLEAN", { "default": True }),
|
802 |
+
},
|
803 |
+
}
|
804 |
+
|
805 |
+
RETURN_TYPES = ("REMBG_SESSION",)
|
806 |
+
FUNCTION = "execute"
|
807 |
+
CATEGORY = "essentials/image manipulation"
|
808 |
+
|
809 |
+
def execute(self, mode, use_jit):
|
810 |
+
from transparent_background import Remover
|
811 |
+
|
812 |
+
class Session:
|
813 |
+
def __init__(self, mode, use_jit):
|
814 |
+
self.session = Remover(mode=mode, jit=use_jit)
|
815 |
+
def process(self, image):
|
816 |
+
return self.session.process(image)
|
817 |
+
|
818 |
+
return (Session(mode, use_jit),)
|
819 |
+
|
820 |
+
class ImageRemoveBackground:
|
821 |
+
@classmethod
|
822 |
+
def INPUT_TYPES(s):
|
823 |
+
return {
|
824 |
+
"required": {
|
825 |
+
"rembg_session": ("REMBG_SESSION",),
|
826 |
+
"image": ("IMAGE",),
|
827 |
+
},
|
828 |
+
}
|
829 |
+
|
830 |
+
RETURN_TYPES = ("IMAGE", "MASK",)
|
831 |
+
FUNCTION = "execute"
|
832 |
+
CATEGORY = "essentials/image manipulation"
|
833 |
+
|
834 |
+
def execute(self, rembg_session, image):
|
835 |
+
image = image.permute([0, 3, 1, 2])
|
836 |
+
output = []
|
837 |
+
for img in image:
|
838 |
+
img = T.ToPILImage()(img)
|
839 |
+
img = rembg_session.process(img)
|
840 |
+
output.append(T.ToTensor()(img))
|
841 |
+
|
842 |
+
output = torch.stack(output, dim=0)
|
843 |
+
output = output.permute([0, 2, 3, 1])
|
844 |
+
mask = output[:, :, :, 3] if output.shape[3] == 4 else torch.ones_like(output[:, :, :, 0])
|
845 |
+
# output = output[:, :, :, :3]
|
846 |
+
|
847 |
+
return(output, mask,)
|
848 |
+
|
849 |
+
"""
|
850 |
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
851 |
+
Image processing
|
852 |
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
853 |
+
"""
|
854 |
+
|
855 |
+
class ImageDesaturate:
|
856 |
+
@classmethod
|
857 |
+
def INPUT_TYPES(s):
|
858 |
+
return {
|
859 |
+
"required": {
|
860 |
+
"image": ("IMAGE",),
|
861 |
+
"factor": ("FLOAT", { "default": 1.00, "min": 0.00, "max": 1.00, "step": 0.05, }),
|
862 |
+
"method": (["luminance (Rec.709)", "luminance (Rec.601)", "average", "lightness"],),
|
863 |
+
}
|
864 |
+
}
|
865 |
+
|
866 |
+
RETURN_TYPES = ("IMAGE",)
|
867 |
+
FUNCTION = "execute"
|
868 |
+
CATEGORY = "essentials/image processing"
|
869 |
+
|
870 |
+
def execute(self, image, factor, method):
|
871 |
+
if method == "luminance (Rec.709)":
|
872 |
+
grayscale = 0.2126 * image[..., 0] + 0.7152 * image[..., 1] + 0.0722 * image[..., 2]
|
873 |
+
elif method == "luminance (Rec.601)":
|
874 |
+
grayscale = 0.299 * image[..., 0] + 0.587 * image[..., 1] + 0.114 * image[..., 2]
|
875 |
+
elif method == "average":
|
876 |
+
grayscale = image.mean(dim=3)
|
877 |
+
elif method == "lightness":
|
878 |
+
grayscale = (torch.max(image, dim=3)[0] + torch.min(image, dim=3)[0]) / 2
|
879 |
+
|
880 |
+
grayscale = (1.0 - factor) * image + factor * grayscale.unsqueeze(-1).repeat(1, 1, 1, 3)
|
881 |
+
grayscale = torch.clamp(grayscale, 0, 1)
|
882 |
+
|
883 |
+
return(grayscale,)
|
884 |
+
|
885 |
+
class PixelOEPixelize:
|
886 |
+
@classmethod
|
887 |
+
def INPUT_TYPES(s):
|
888 |
+
return {
|
889 |
+
"required": {
|
890 |
+
"image": ("IMAGE",),
|
891 |
+
"downscale_mode": (["contrast", "bicubic", "nearest", "center", "k-centroid"],),
|
892 |
+
"target_size": ("INT", { "default": 128, "min": 0, "max": MAX_RESOLUTION, "step": 8 }),
|
893 |
+
"patch_size": ("INT", { "default": 16, "min": 4, "max": 32, "step": 2 }),
|
894 |
+
"thickness": ("INT", { "default": 2, "min": 1, "max": 16, "step": 1 }),
|
895 |
+
"color_matching": ("BOOLEAN", { "default": True }),
|
896 |
+
"upscale": ("BOOLEAN", { "default": True }),
|
897 |
+
#"contrast": ("FLOAT", { "default": 1.0, "min": 0.0, "max": 100.0, "step": 0.1 }),
|
898 |
+
#"saturation": ("FLOAT", { "default": 1.0, "min": 0.0, "max": 100.0, "step": 0.1 }),
|
899 |
+
},
|
900 |
+
}
|
901 |
+
|
902 |
+
RETURN_TYPES = ("IMAGE",)
|
903 |
+
FUNCTION = "execute"
|
904 |
+
CATEGORY = "essentials/image processing"
|
905 |
+
|
906 |
+
def execute(self, image, downscale_mode, target_size, patch_size, thickness, color_matching, upscale):
|
907 |
+
from pixeloe.pixelize import pixelize
|
908 |
+
|
909 |
+
image = image.clone().mul(255).clamp(0, 255).byte().cpu().numpy()
|
910 |
+
output = []
|
911 |
+
for img in image:
|
912 |
+
img = pixelize(img,
|
913 |
+
mode=downscale_mode,
|
914 |
+
target_size=target_size,
|
915 |
+
patch_size=patch_size,
|
916 |
+
thickness=thickness,
|
917 |
+
contrast=1.0,
|
918 |
+
saturation=1.0,
|
919 |
+
color_matching=color_matching,
|
920 |
+
no_upscale=not upscale)
|
921 |
+
output.append(T.ToTensor()(img))
|
922 |
+
|
923 |
+
output = torch.stack(output, dim=0).permute([0, 2, 3, 1])
|
924 |
+
|
925 |
+
return(output,)
|
926 |
+
|
927 |
+
class ImagePosterize:
|
928 |
+
@classmethod
|
929 |
+
def INPUT_TYPES(s):
|
930 |
+
return {
|
931 |
+
"required": {
|
932 |
+
"image": ("IMAGE",),
|
933 |
+
"threshold": ("FLOAT", { "default": 0.50, "min": 0.00, "max": 1.00, "step": 0.05, }),
|
934 |
+
}
|
935 |
+
}
|
936 |
+
|
937 |
+
RETURN_TYPES = ("IMAGE",)
|
938 |
+
FUNCTION = "execute"
|
939 |
+
CATEGORY = "essentials/image processing"
|
940 |
+
|
941 |
+
def execute(self, image, threshold):
|
942 |
+
image = image.mean(dim=3, keepdim=True)
|
943 |
+
image = (image > threshold).float()
|
944 |
+
image = image.repeat(1, 1, 1, 3)
|
945 |
+
|
946 |
+
return(image,)
|
947 |
+
|
948 |
+
# From https://github.com/yoonsikp/pycubelut/blob/master/pycubelut.py (MIT license)
|
949 |
+
class ImageApplyLUT:
|
950 |
+
@classmethod
|
951 |
+
def INPUT_TYPES(s):
|
952 |
+
return {
|
953 |
+
"required": {
|
954 |
+
"image": ("IMAGE",),
|
955 |
+
"lut_file": (folder_paths.get_filename_list("luts"),),
|
956 |
+
"gamma_correction": ("BOOLEAN", { "default": True }),
|
957 |
+
"clip_values": ("BOOLEAN", { "default": True }),
|
958 |
+
"strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.1 }),
|
959 |
+
}}
|
960 |
+
|
961 |
+
RETURN_TYPES = ("IMAGE",)
|
962 |
+
FUNCTION = "execute"
|
963 |
+
CATEGORY = "essentials/image processing"
|
964 |
+
|
965 |
+
# TODO: check if we can do without numpy
|
966 |
+
def execute(self, image, lut_file, gamma_correction, clip_values, strength):
|
967 |
+
lut_file_path = folder_paths.get_full_path("luts", lut_file)
|
968 |
+
if not lut_file_path or not Path(lut_file_path).exists():
|
969 |
+
print(f"Could not find LUT file: {lut_file_path}")
|
970 |
+
return (image,)
|
971 |
+
|
972 |
+
from colour.io.luts.iridas_cube import read_LUT_IridasCube
|
973 |
+
|
974 |
+
device = image.device
|
975 |
+
lut = read_LUT_IridasCube(lut_file_path)
|
976 |
+
lut.name = lut_file
|
977 |
+
|
978 |
+
if clip_values:
|
979 |
+
if lut.domain[0].max() == lut.domain[0].min() and lut.domain[1].max() == lut.domain[1].min():
|
980 |
+
lut.table = np.clip(lut.table, lut.domain[0, 0], lut.domain[1, 0])
|
981 |
+
else:
|
982 |
+
if len(lut.table.shape) == 2: # 3x1D
|
983 |
+
for dim in range(3):
|
984 |
+
lut.table[:, dim] = np.clip(lut.table[:, dim], lut.domain[0, dim], lut.domain[1, dim])
|
985 |
+
else: # 3D
|
986 |
+
for dim in range(3):
|
987 |
+
lut.table[:, :, :, dim] = np.clip(lut.table[:, :, :, dim], lut.domain[0, dim], lut.domain[1, dim])
|
988 |
+
|
989 |
+
out = []
|
990 |
+
for img in image: # TODO: is this more resource efficient? should we use a batch instead?
|
991 |
+
lut_img = img.cpu().numpy().copy()
|
992 |
+
|
993 |
+
is_non_default_domain = not np.array_equal(lut.domain, np.array([[0., 0., 0.], [1., 1., 1.]]))
|
994 |
+
dom_scale = None
|
995 |
+
if is_non_default_domain:
|
996 |
+
dom_scale = lut.domain[1] - lut.domain[0]
|
997 |
+
lut_img = lut_img * dom_scale + lut.domain[0]
|
998 |
+
if gamma_correction:
|
999 |
+
lut_img = lut_img ** (1/2.2)
|
1000 |
+
lut_img = lut.apply(lut_img)
|
1001 |
+
if gamma_correction:
|
1002 |
+
lut_img = lut_img ** (2.2)
|
1003 |
+
if is_non_default_domain:
|
1004 |
+
lut_img = (lut_img - lut.domain[0]) / dom_scale
|
1005 |
+
|
1006 |
+
lut_img = torch.from_numpy(lut_img).to(device)
|
1007 |
+
if strength < 1.0:
|
1008 |
+
lut_img = strength * lut_img + (1 - strength) * img
|
1009 |
+
out.append(lut_img)
|
1010 |
+
|
1011 |
+
out = torch.stack(out)
|
1012 |
+
|
1013 |
+
return (out, )
|
1014 |
+
|
1015 |
+
# From https://github.com/Jamy-L/Pytorch-Contrast-Adaptive-Sharpening/
|
1016 |
+
class ImageCAS:
|
1017 |
+
@classmethod
|
1018 |
+
def INPUT_TYPES(cls):
|
1019 |
+
return {
|
1020 |
+
"required": {
|
1021 |
+
"image": ("IMAGE",),
|
1022 |
+
"amount": ("FLOAT", {"default": 0.8, "min": 0, "max": 1, "step": 0.05}),
|
1023 |
+
},
|
1024 |
+
}
|
1025 |
+
|
1026 |
+
RETURN_TYPES = ("IMAGE",)
|
1027 |
+
CATEGORY = "essentials/image processing"
|
1028 |
+
FUNCTION = "execute"
|
1029 |
+
|
1030 |
+
def execute(self, image, amount):
|
1031 |
+
epsilon = 1e-5
|
1032 |
+
img = F.pad(image.permute([0,3,1,2]), pad=(1, 1, 1, 1))
|
1033 |
+
|
1034 |
+
a = img[..., :-2, :-2]
|
1035 |
+
b = img[..., :-2, 1:-1]
|
1036 |
+
c = img[..., :-2, 2:]
|
1037 |
+
d = img[..., 1:-1, :-2]
|
1038 |
+
e = img[..., 1:-1, 1:-1]
|
1039 |
+
f = img[..., 1:-1, 2:]
|
1040 |
+
g = img[..., 2:, :-2]
|
1041 |
+
h = img[..., 2:, 1:-1]
|
1042 |
+
i = img[..., 2:, 2:]
|
1043 |
+
|
1044 |
+
# Computing contrast
|
1045 |
+
cross = (b, d, e, f, h)
|
1046 |
+
mn = min_(cross)
|
1047 |
+
mx = max_(cross)
|
1048 |
+
|
1049 |
+
diag = (a, c, g, i)
|
1050 |
+
mn2 = min_(diag)
|
1051 |
+
mx2 = max_(diag)
|
1052 |
+
mx = mx + mx2
|
1053 |
+
mn = mn + mn2
|
1054 |
+
|
1055 |
+
# Computing local weight
|
1056 |
+
inv_mx = torch.reciprocal(mx + epsilon)
|
1057 |
+
amp = inv_mx * torch.minimum(mn, (2 - mx))
|
1058 |
+
|
1059 |
+
# scaling
|
1060 |
+
amp = torch.sqrt(amp)
|
1061 |
+
w = - amp * (amount * (1/5 - 1/8) + 1/8)
|
1062 |
+
div = torch.reciprocal(1 + 4*w)
|
1063 |
+
|
1064 |
+
output = ((b + d + f + h)*w + e) * div
|
1065 |
+
output = output.clamp(0, 1)
|
1066 |
+
#output = torch.nan_to_num(output)
|
1067 |
+
|
1068 |
+
output = output.permute([0,2,3,1])
|
1069 |
+
|
1070 |
+
return (output,)
|
1071 |
+
|
1072 |
+
class ImageSmartSharpen:
|
1073 |
+
@classmethod
|
1074 |
+
def INPUT_TYPES(s):
|
1075 |
+
return {
|
1076 |
+
"required": {
|
1077 |
+
"image": ("IMAGE",),
|
1078 |
+
"noise_radius": ("INT", { "default": 7, "min": 1, "max": 25, "step": 1, }),
|
1079 |
+
"preserve_edges": ("FLOAT", { "default": 0.75, "min": 0.0, "max": 1.0, "step": 0.05 }),
|
1080 |
+
"sharpen": ("FLOAT", { "default": 5.0, "min": 0.0, "max": 25.0, "step": 0.5 }),
|
1081 |
+
"ratio": ("FLOAT", { "default": 0.5, "min": 0.0, "max": 1.0, "step": 0.1 }),
|
1082 |
+
}}
|
1083 |
+
|
1084 |
+
RETURN_TYPES = ("IMAGE",)
|
1085 |
+
CATEGORY = "essentials/image processing"
|
1086 |
+
FUNCTION = "execute"
|
1087 |
+
|
1088 |
+
def execute(self, image, noise_radius, preserve_edges, sharpen, ratio):
|
1089 |
+
import cv2
|
1090 |
+
|
1091 |
+
output = []
|
1092 |
+
#diagonal = np.sqrt(image.shape[1]**2 + image.shape[2]**2)
|
1093 |
+
if preserve_edges > 0:
|
1094 |
+
preserve_edges = max(1 - preserve_edges, 0.05)
|
1095 |
+
|
1096 |
+
for img in image:
|
1097 |
+
if noise_radius > 1:
|
1098 |
+
sigma = 0.3 * ((noise_radius - 1) * 0.5 - 1) + 0.8 # this is what pytorch uses for blur
|
1099 |
+
#sigma_color = preserve_edges * (diagonal / 2048)
|
1100 |
+
blurred = cv2.bilateralFilter(img.cpu().numpy(), noise_radius, preserve_edges, sigma)
|
1101 |
+
blurred = torch.from_numpy(blurred)
|
1102 |
+
else:
|
1103 |
+
blurred = img
|
1104 |
+
|
1105 |
+
if sharpen > 0:
|
1106 |
+
sharpened = kornia.enhance.sharpness(img.permute(2,0,1), sharpen).permute(1,2,0)
|
1107 |
+
else:
|
1108 |
+
sharpened = img
|
1109 |
+
|
1110 |
+
img = ratio * sharpened + (1 - ratio) * blurred
|
1111 |
+
img = torch.clamp(img, 0, 1)
|
1112 |
+
output.append(img)
|
1113 |
+
|
1114 |
+
del blurred, sharpened
|
1115 |
+
output = torch.stack(output)
|
1116 |
+
|
1117 |
+
return (output,)
|
1118 |
+
|
1119 |
+
|
1120 |
+
class ExtractKeyframes:
|
1121 |
+
@classmethod
|
1122 |
+
def INPUT_TYPES(s):
|
1123 |
+
return {
|
1124 |
+
"required": {
|
1125 |
+
"image": ("IMAGE",),
|
1126 |
+
"threshold": ("FLOAT", { "default": 0.85, "min": 0.00, "max": 1.00, "step": 0.01, }),
|
1127 |
+
}
|
1128 |
+
}
|
1129 |
+
|
1130 |
+
RETURN_TYPES = ("IMAGE", "STRING")
|
1131 |
+
RETURN_NAMES = ("KEYFRAMES", "indexes")
|
1132 |
+
|
1133 |
+
FUNCTION = "execute"
|
1134 |
+
CATEGORY = "essentials"
|
1135 |
+
|
1136 |
+
def execute(self, image, threshold):
|
1137 |
+
window_size = 2
|
1138 |
+
|
1139 |
+
variations = torch.sum(torch.abs(image[1:] - image[:-1]), dim=[1, 2, 3])
|
1140 |
+
#variations = torch.sum((image[1:] - image[:-1]) ** 2, dim=[1, 2, 3])
|
1141 |
+
threshold = torch.quantile(variations.float(), threshold).item()
|
1142 |
+
|
1143 |
+
keyframes = []
|
1144 |
+
for i in range(image.shape[0] - window_size + 1):
|
1145 |
+
window = image[i:i + window_size]
|
1146 |
+
variation = torch.sum(torch.abs(window[-1] - window[0])).item()
|
1147 |
+
|
1148 |
+
if variation > threshold:
|
1149 |
+
keyframes.append(i + window_size - 1)
|
1150 |
+
|
1151 |
+
return (image[keyframes], ','.join(map(str, keyframes)),)
|
1152 |
+
|
1153 |
+
class ImageColorMatch:
|
1154 |
+
@classmethod
|
1155 |
+
def INPUT_TYPES(s):
|
1156 |
+
return {
|
1157 |
+
"required": {
|
1158 |
+
"image": ("IMAGE",),
|
1159 |
+
"reference": ("IMAGE",),
|
1160 |
+
"color_space": (["LAB", "YCbCr", "RGB", "LUV", "YUV", "XYZ"],),
|
1161 |
+
"factor": ("FLOAT", { "default": 1.0, "min": 0.0, "max": 1.0, "step": 0.05, }),
|
1162 |
+
"device": (["auto", "cpu", "gpu"],),
|
1163 |
+
"batch_size": ("INT", { "default": 0, "min": 0, "max": 1024, "step": 1, }),
|
1164 |
+
},
|
1165 |
+
"optional": {
|
1166 |
+
"reference_mask": ("MASK",),
|
1167 |
+
}
|
1168 |
+
}
|
1169 |
+
|
1170 |
+
RETURN_TYPES = ("IMAGE",)
|
1171 |
+
FUNCTION = "execute"
|
1172 |
+
CATEGORY = "essentials/image processing"
|
1173 |
+
|
1174 |
+
def execute(self, image, reference, color_space, factor, device, batch_size, reference_mask=None):
|
1175 |
+
if "gpu" == device:
|
1176 |
+
device = comfy.model_management.get_torch_device()
|
1177 |
+
elif "auto" == device:
|
1178 |
+
device = comfy.model_management.intermediate_device()
|
1179 |
+
else:
|
1180 |
+
device = 'cpu'
|
1181 |
+
|
1182 |
+
image = image.permute([0, 3, 1, 2])
|
1183 |
+
reference = reference.permute([0, 3, 1, 2]).to(device)
|
1184 |
+
|
1185 |
+
# Ensure reference_mask is in the correct format and on the right device
|
1186 |
+
if reference_mask is not None:
|
1187 |
+
assert reference_mask.ndim == 3, f"Expected reference_mask to have 3 dimensions, but got {reference_mask.ndim}"
|
1188 |
+
assert reference_mask.shape[0] == reference.shape[0], f"Frame count mismatch: reference_mask has {reference_mask.shape[0]} frames, but reference has {reference.shape[0]}"
|
1189 |
+
|
1190 |
+
# Reshape mask to (batch, 1, height, width)
|
1191 |
+
reference_mask = reference_mask.unsqueeze(1).to(device)
|
1192 |
+
|
1193 |
+
# Ensure the mask is binary (0 or 1)
|
1194 |
+
reference_mask = (reference_mask > 0.5).float()
|
1195 |
+
|
1196 |
+
# Ensure spatial dimensions match
|
1197 |
+
if reference_mask.shape[2:] != reference.shape[2:]:
|
1198 |
+
reference_mask = comfy.utils.common_upscale(
|
1199 |
+
reference_mask,
|
1200 |
+
reference.shape[3], reference.shape[2],
|
1201 |
+
upscale_method='bicubic',
|
1202 |
+
crop='center'
|
1203 |
+
)
|
1204 |
+
|
1205 |
+
if batch_size == 0 or batch_size > image.shape[0]:
|
1206 |
+
batch_size = image.shape[0]
|
1207 |
+
|
1208 |
+
if "LAB" == color_space:
|
1209 |
+
reference = kornia.color.rgb_to_lab(reference)
|
1210 |
+
elif "YCbCr" == color_space:
|
1211 |
+
reference = kornia.color.rgb_to_ycbcr(reference)
|
1212 |
+
elif "LUV" == color_space:
|
1213 |
+
reference = kornia.color.rgb_to_luv(reference)
|
1214 |
+
elif "YUV" == color_space:
|
1215 |
+
reference = kornia.color.rgb_to_yuv(reference)
|
1216 |
+
elif "XYZ" == color_space:
|
1217 |
+
reference = kornia.color.rgb_to_xyz(reference)
|
1218 |
+
|
1219 |
+
reference_mean, reference_std = self.compute_mean_std(reference, reference_mask)
|
1220 |
+
|
1221 |
+
image_batch = torch.split(image, batch_size, dim=0)
|
1222 |
+
output = []
|
1223 |
+
|
1224 |
+
for image in image_batch:
|
1225 |
+
image = image.to(device)
|
1226 |
+
|
1227 |
+
if color_space == "LAB":
|
1228 |
+
image = kornia.color.rgb_to_lab(image)
|
1229 |
+
elif color_space == "YCbCr":
|
1230 |
+
image = kornia.color.rgb_to_ycbcr(image)
|
1231 |
+
elif color_space == "LUV":
|
1232 |
+
image = kornia.color.rgb_to_luv(image)
|
1233 |
+
elif color_space == "YUV":
|
1234 |
+
image = kornia.color.rgb_to_yuv(image)
|
1235 |
+
elif color_space == "XYZ":
|
1236 |
+
image = kornia.color.rgb_to_xyz(image)
|
1237 |
+
|
1238 |
+
image_mean, image_std = self.compute_mean_std(image)
|
1239 |
+
|
1240 |
+
matched = torch.nan_to_num((image - image_mean) / image_std) * torch.nan_to_num(reference_std) + reference_mean
|
1241 |
+
matched = factor * matched + (1 - factor) * image
|
1242 |
+
|
1243 |
+
if color_space == "LAB":
|
1244 |
+
matched = kornia.color.lab_to_rgb(matched)
|
1245 |
+
elif color_space == "YCbCr":
|
1246 |
+
matched = kornia.color.ycbcr_to_rgb(matched)
|
1247 |
+
elif color_space == "LUV":
|
1248 |
+
matched = kornia.color.luv_to_rgb(matched)
|
1249 |
+
elif color_space == "YUV":
|
1250 |
+
matched = kornia.color.yuv_to_rgb(matched)
|
1251 |
+
elif color_space == "XYZ":
|
1252 |
+
matched = kornia.color.xyz_to_rgb(matched)
|
1253 |
+
|
1254 |
+
out = matched.permute([0, 2, 3, 1]).clamp(0, 1).to(comfy.model_management.intermediate_device())
|
1255 |
+
output.append(out)
|
1256 |
+
|
1257 |
+
out = None
|
1258 |
+
output = torch.cat(output, dim=0)
|
1259 |
+
return (output,)
|
1260 |
+
|
1261 |
+
def compute_mean_std(self, tensor, mask=None):
|
1262 |
+
if mask is not None:
|
1263 |
+
# Apply mask to the tensor
|
1264 |
+
masked_tensor = tensor * mask
|
1265 |
+
|
1266 |
+
# Calculate the sum of the mask for each channel
|
1267 |
+
mask_sum = mask.sum(dim=[2, 3], keepdim=True)
|
1268 |
+
|
1269 |
+
# Avoid division by zero
|
1270 |
+
mask_sum = torch.clamp(mask_sum, min=1e-6)
|
1271 |
+
|
1272 |
+
# Calculate mean and std only for masked area
|
1273 |
+
mean = torch.nan_to_num(masked_tensor.sum(dim=[2, 3], keepdim=True) / mask_sum)
|
1274 |
+
std = torch.sqrt(torch.nan_to_num(((masked_tensor - mean) ** 2 * mask).sum(dim=[2, 3], keepdim=True) / mask_sum))
|
1275 |
+
else:
|
1276 |
+
mean = tensor.mean(dim=[2, 3], keepdim=True)
|
1277 |
+
std = tensor.std(dim=[2, 3], keepdim=True)
|
1278 |
+
return mean, std
|
1279 |
+
|
1280 |
+
class ImageColorMatchAdobe(ImageColorMatch):
|
1281 |
+
@classmethod
|
1282 |
+
def INPUT_TYPES(s):
|
1283 |
+
return {
|
1284 |
+
"required": {
|
1285 |
+
"image": ("IMAGE",),
|
1286 |
+
"reference": ("IMAGE",),
|
1287 |
+
"color_space": (["RGB", "LAB"],),
|
1288 |
+
"luminance_factor": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 2.0, "step": 0.05}),
|
1289 |
+
"color_intensity_factor": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 2.0, "step": 0.05}),
|
1290 |
+
"fade_factor": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.05}),
|
1291 |
+
"neutralization_factor": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.05}),
|
1292 |
+
"device": (["auto", "cpu", "gpu"],),
|
1293 |
+
},
|
1294 |
+
"optional": {
|
1295 |
+
"reference_mask": ("MASK",),
|
1296 |
+
}
|
1297 |
+
}
|
1298 |
+
|
1299 |
+
RETURN_TYPES = ("IMAGE",)
|
1300 |
+
FUNCTION = "execute"
|
1301 |
+
CATEGORY = "essentials/image processing"
|
1302 |
+
|
1303 |
+
def analyze_color_statistics(self, image, mask=None):
|
1304 |
+
# Assuming image is in RGB format
|
1305 |
+
l, a, b = kornia.color.rgb_to_lab(image).chunk(3, dim=1)
|
1306 |
+
|
1307 |
+
if mask is not None:
|
1308 |
+
# Ensure mask is binary and has the same spatial dimensions as the image
|
1309 |
+
mask = F.interpolate(mask, size=image.shape[2:], mode='nearest')
|
1310 |
+
mask = (mask > 0.5).float()
|
1311 |
+
|
1312 |
+
# Apply mask to each channel
|
1313 |
+
l = l * mask
|
1314 |
+
a = a * mask
|
1315 |
+
b = b * mask
|
1316 |
+
|
1317 |
+
# Compute masked mean and std
|
1318 |
+
num_pixels = mask.sum()
|
1319 |
+
mean_l = (l * mask).sum() / num_pixels
|
1320 |
+
mean_a = (a * mask).sum() / num_pixels
|
1321 |
+
mean_b = (b * mask).sum() / num_pixels
|
1322 |
+
std_l = torch.sqrt(((l - mean_l)**2 * mask).sum() / num_pixels)
|
1323 |
+
var_ab = ((a - mean_a)**2 + (b - mean_b)**2) * mask
|
1324 |
+
std_ab = torch.sqrt(var_ab.sum() / num_pixels)
|
1325 |
+
else:
|
1326 |
+
mean_l = l.mean()
|
1327 |
+
std_l = l.std()
|
1328 |
+
mean_a = a.mean()
|
1329 |
+
mean_b = b.mean()
|
1330 |
+
std_ab = torch.sqrt(a.var() + b.var())
|
1331 |
+
|
1332 |
+
return mean_l, std_l, mean_a, mean_b, std_ab
|
1333 |
+
|
1334 |
+
def apply_color_transformation(self, image, source_stats, dest_stats, L, C, N):
|
1335 |
+
l, a, b = kornia.color.rgb_to_lab(image).chunk(3, dim=1)
|
1336 |
+
|
1337 |
+
# Unpack statistics
|
1338 |
+
src_mean_l, src_std_l, src_mean_a, src_mean_b, src_std_ab = source_stats
|
1339 |
+
dest_mean_l, dest_std_l, dest_mean_a, dest_mean_b, dest_std_ab = dest_stats
|
1340 |
+
|
1341 |
+
# Adjust luminance
|
1342 |
+
l_new = (l - dest_mean_l) * (src_std_l / dest_std_l) * L + src_mean_l
|
1343 |
+
|
1344 |
+
# Neutralize color cast
|
1345 |
+
a = a - N * dest_mean_a
|
1346 |
+
b = b - N * dest_mean_b
|
1347 |
+
|
1348 |
+
# Adjust color intensity
|
1349 |
+
a_new = a * (src_std_ab / dest_std_ab) * C
|
1350 |
+
b_new = b * (src_std_ab / dest_std_ab) * C
|
1351 |
+
|
1352 |
+
# Combine channels
|
1353 |
+
lab_new = torch.cat([l_new, a_new, b_new], dim=1)
|
1354 |
+
|
1355 |
+
# Convert back to RGB
|
1356 |
+
rgb_new = kornia.color.lab_to_rgb(lab_new)
|
1357 |
+
|
1358 |
+
return rgb_new
|
1359 |
+
|
1360 |
+
def execute(self, image, reference, color_space, luminance_factor, color_intensity_factor, fade_factor, neutralization_factor, device, reference_mask=None):
|
1361 |
+
if "gpu" == device:
|
1362 |
+
device = comfy.model_management.get_torch_device()
|
1363 |
+
elif "auto" == device:
|
1364 |
+
device = comfy.model_management.intermediate_device()
|
1365 |
+
else:
|
1366 |
+
device = 'cpu'
|
1367 |
+
|
1368 |
+
# Ensure image and reference are in the correct shape (B, C, H, W)
|
1369 |
+
image = image.permute(0, 3, 1, 2).to(device)
|
1370 |
+
reference = reference.permute(0, 3, 1, 2).to(device)
|
1371 |
+
|
1372 |
+
# Handle reference_mask (if provided)
|
1373 |
+
if reference_mask is not None:
|
1374 |
+
# Ensure reference_mask is 4D (B, 1, H, W)
|
1375 |
+
if reference_mask.ndim == 2:
|
1376 |
+
reference_mask = reference_mask.unsqueeze(0).unsqueeze(0)
|
1377 |
+
elif reference_mask.ndim == 3:
|
1378 |
+
reference_mask = reference_mask.unsqueeze(1)
|
1379 |
+
reference_mask = reference_mask.to(device)
|
1380 |
+
|
1381 |
+
# Analyze color statistics
|
1382 |
+
source_stats = self.analyze_color_statistics(reference, reference_mask)
|
1383 |
+
dest_stats = self.analyze_color_statistics(image)
|
1384 |
+
|
1385 |
+
# Apply color transformation
|
1386 |
+
transformed = self.apply_color_transformation(
|
1387 |
+
image, source_stats, dest_stats,
|
1388 |
+
luminance_factor, color_intensity_factor, neutralization_factor
|
1389 |
+
)
|
1390 |
+
|
1391 |
+
# Apply fade factor
|
1392 |
+
result = fade_factor * transformed + (1 - fade_factor) * image
|
1393 |
+
|
1394 |
+
# Convert back to (B, H, W, C) format and ensure values are in [0, 1] range
|
1395 |
+
result = result.permute(0, 2, 3, 1).clamp(0, 1).to(comfy.model_management.intermediate_device())
|
1396 |
+
|
1397 |
+
return (result,)
|
1398 |
+
|
1399 |
+
|
1400 |
+
class ImageHistogramMatch:
|
1401 |
+
@classmethod
|
1402 |
+
def INPUT_TYPES(s):
|
1403 |
+
return {
|
1404 |
+
"required": {
|
1405 |
+
"image": ("IMAGE",),
|
1406 |
+
"reference": ("IMAGE",),
|
1407 |
+
"method": (["pytorch", "skimage"],),
|
1408 |
+
"factor": ("FLOAT", { "default": 1.0, "min": 0.0, "max": 1.0, "step": 0.05, }),
|
1409 |
+
"device": (["auto", "cpu", "gpu"],),
|
1410 |
+
}
|
1411 |
+
}
|
1412 |
+
|
1413 |
+
RETURN_TYPES = ("IMAGE",)
|
1414 |
+
FUNCTION = "execute"
|
1415 |
+
CATEGORY = "essentials/image processing"
|
1416 |
+
|
1417 |
+
def execute(self, image, reference, method, factor, device):
|
1418 |
+
if "gpu" == device:
|
1419 |
+
device = comfy.model_management.get_torch_device()
|
1420 |
+
elif "auto" == device:
|
1421 |
+
device = comfy.model_management.intermediate_device()
|
1422 |
+
else:
|
1423 |
+
device = 'cpu'
|
1424 |
+
|
1425 |
+
if "pytorch" in method:
|
1426 |
+
from .histogram_matching import Histogram_Matching
|
1427 |
+
|
1428 |
+
image = image.permute([0, 3, 1, 2]).to(device)
|
1429 |
+
reference = reference.permute([0, 3, 1, 2]).to(device)[0].unsqueeze(0)
|
1430 |
+
image.requires_grad = True
|
1431 |
+
reference.requires_grad = True
|
1432 |
+
|
1433 |
+
out = []
|
1434 |
+
|
1435 |
+
for i in image:
|
1436 |
+
i = i.unsqueeze(0)
|
1437 |
+
hm = Histogram_Matching(differentiable=True)
|
1438 |
+
out.append(hm(i, reference))
|
1439 |
+
out = torch.cat(out, dim=0)
|
1440 |
+
out = factor * out + (1 - factor) * image
|
1441 |
+
out = out.permute([0, 2, 3, 1]).clamp(0, 1)
|
1442 |
+
else:
|
1443 |
+
from skimage.exposure import match_histograms
|
1444 |
+
|
1445 |
+
out = torch.from_numpy(match_histograms(image.cpu().numpy(), reference.cpu().numpy(), channel_axis=3)).to(device)
|
1446 |
+
out = factor * out + (1 - factor) * image.to(device)
|
1447 |
+
|
1448 |
+
return (out.to(comfy.model_management.intermediate_device()),)
|
1449 |
+
|
1450 |
+
"""
|
1451 |
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
1452 |
+
Utilities
|
1453 |
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
1454 |
+
"""
|
1455 |
+
|
1456 |
+
class ImageToDevice:
|
1457 |
+
@classmethod
|
1458 |
+
def INPUT_TYPES(s):
|
1459 |
+
return {
|
1460 |
+
"required": {
|
1461 |
+
"image": ("IMAGE",),
|
1462 |
+
"device": (["auto", "cpu", "gpu"],),
|
1463 |
+
}
|
1464 |
+
}
|
1465 |
+
|
1466 |
+
RETURN_TYPES = ("IMAGE",)
|
1467 |
+
FUNCTION = "execute"
|
1468 |
+
CATEGORY = "essentials/image utils"
|
1469 |
+
|
1470 |
+
def execute(self, image, device):
|
1471 |
+
if "gpu" == device:
|
1472 |
+
device = comfy.model_management.get_torch_device()
|
1473 |
+
elif "auto" == device:
|
1474 |
+
device = comfy.model_management.intermediate_device()
|
1475 |
+
else:
|
1476 |
+
device = 'cpu'
|
1477 |
+
|
1478 |
+
image = image.clone().to(device)
|
1479 |
+
torch.cuda.empty_cache()
|
1480 |
+
|
1481 |
+
return (image,)
|
1482 |
+
|
1483 |
+
class GetImageSize:
|
1484 |
+
@classmethod
|
1485 |
+
def INPUT_TYPES(s):
|
1486 |
+
return {
|
1487 |
+
"required": {
|
1488 |
+
"image": ("IMAGE",),
|
1489 |
+
}
|
1490 |
+
}
|
1491 |
+
|
1492 |
+
RETURN_TYPES = ("INT", "INT", "INT",)
|
1493 |
+
RETURN_NAMES = ("width", "height", "count")
|
1494 |
+
FUNCTION = "execute"
|
1495 |
+
CATEGORY = "essentials/image utils"
|
1496 |
+
|
1497 |
+
def execute(self, image):
|
1498 |
+
return (image.shape[2], image.shape[1], image.shape[0])
|
1499 |
+
|
1500 |
+
class ImageRemoveAlpha:
|
1501 |
+
@classmethod
|
1502 |
+
def INPUT_TYPES(s):
|
1503 |
+
return {
|
1504 |
+
"required": {
|
1505 |
+
"image": ("IMAGE",),
|
1506 |
+
},
|
1507 |
+
}
|
1508 |
+
|
1509 |
+
RETURN_TYPES = ("IMAGE",)
|
1510 |
+
FUNCTION = "execute"
|
1511 |
+
CATEGORY = "essentials/image utils"
|
1512 |
+
|
1513 |
+
def execute(self, image):
|
1514 |
+
if image.shape[3] == 4:
|
1515 |
+
image = image[..., :3]
|
1516 |
+
return (image,)
|
1517 |
+
|
1518 |
+
class ImagePreviewFromLatent(SaveImage):
|
1519 |
+
def __init__(self):
|
1520 |
+
self.output_dir = folder_paths.get_temp_directory()
|
1521 |
+
self.type = "temp"
|
1522 |
+
self.prefix_append = "_temp_" + ''.join(random.choice("abcdefghijklmnopqrstupvxyz") for x in range(5))
|
1523 |
+
self.compress_level = 1
|
1524 |
+
|
1525 |
+
@classmethod
|
1526 |
+
def INPUT_TYPES(s):
|
1527 |
+
return {
|
1528 |
+
"required": {
|
1529 |
+
"latent": ("LATENT",),
|
1530 |
+
"vae": ("VAE", ),
|
1531 |
+
"tile_size": ("INT", {"default": 0, "min": 0, "max": 4096, "step": 64})
|
1532 |
+
}, "optional": {
|
1533 |
+
"image": (["none"], {"image_upload": False}),
|
1534 |
+
}, "hidden": {
|
1535 |
+
"prompt": "PROMPT",
|
1536 |
+
"extra_pnginfo": "EXTRA_PNGINFO",
|
1537 |
+
},
|
1538 |
+
}
|
1539 |
+
|
1540 |
+
RETURN_TYPES = ("IMAGE", "MASK", "INT", "INT",)
|
1541 |
+
RETURN_NAMES = ("IMAGE", "MASK", "width", "height",)
|
1542 |
+
FUNCTION = "execute"
|
1543 |
+
CATEGORY = "essentials/image utils"
|
1544 |
+
|
1545 |
+
def execute(self, latent, vae, tile_size, prompt=None, extra_pnginfo=None, image=None, filename_prefix="ComfyUI"):
|
1546 |
+
mask = torch.zeros((64,64), dtype=torch.float32, device="cpu")
|
1547 |
+
ui = None
|
1548 |
+
|
1549 |
+
if image.startswith("clipspace"):
|
1550 |
+
image_path = folder_paths.get_annotated_filepath(image)
|
1551 |
+
if not os.path.exists(image_path):
|
1552 |
+
raise ValueError(f"Clipspace image does not exist anymore, select 'none' in the image field.")
|
1553 |
+
|
1554 |
+
img = pillow(Image.open, image_path)
|
1555 |
+
img = pillow(ImageOps.exif_transpose, img)
|
1556 |
+
if img.mode == "I":
|
1557 |
+
img = img.point(lambda i: i * (1 / 255))
|
1558 |
+
image = img.convert("RGB")
|
1559 |
+
image = np.array(image).astype(np.float32) / 255.0
|
1560 |
+
image = torch.from_numpy(image)[None,]
|
1561 |
+
if "A" in img.getbands():
|
1562 |
+
mask = np.array(img.getchannel('A')).astype(np.float32) / 255.0
|
1563 |
+
mask = 1. - torch.from_numpy(mask)
|
1564 |
+
ui = {
|
1565 |
+
"filename": os.path.basename(image_path),
|
1566 |
+
"subfolder": os.path.dirname(image_path),
|
1567 |
+
"type": "temp",
|
1568 |
+
}
|
1569 |
+
else:
|
1570 |
+
if tile_size > 0:
|
1571 |
+
tile_size = max(tile_size, 320)
|
1572 |
+
image = vae.decode_tiled(latent["samples"], tile_x=tile_size // 8, tile_y=tile_size // 8, )
|
1573 |
+
else:
|
1574 |
+
image = vae.decode(latent["samples"])
|
1575 |
+
ui = self.save_images(image, filename_prefix, prompt, extra_pnginfo)
|
1576 |
+
|
1577 |
+
out = {**ui, "result": (image, mask, image.shape[2], image.shape[1],)}
|
1578 |
+
return out
|
1579 |
+
|
1580 |
+
class NoiseFromImage:
|
1581 |
+
@classmethod
|
1582 |
+
def INPUT_TYPES(s):
|
1583 |
+
return {
|
1584 |
+
"required": {
|
1585 |
+
"image": ("IMAGE",),
|
1586 |
+
"noise_strenght": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01 }),
|
1587 |
+
"noise_size": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01 }),
|
1588 |
+
"color_noise": ("FLOAT", {"default": 0.2, "min": 0.0, "max": 1.0, "step": 0.01 }),
|
1589 |
+
"mask_strength": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 1.0, "step": 0.01 }),
|
1590 |
+
"mask_scale_diff": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.01 }),
|
1591 |
+
"mask_contrast": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 100.0, "step": 0.1 }),
|
1592 |
+
"saturation": ("FLOAT", {"default": 2.0, "min": 0.0, "max": 100.0, "step": 0.1 }),
|
1593 |
+
"contrast": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 100.0, "step": 0.1 }),
|
1594 |
+
"blur": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.1 }),
|
1595 |
+
},
|
1596 |
+
"optional": {
|
1597 |
+
"noise_mask": ("IMAGE",),
|
1598 |
+
}
|
1599 |
+
}
|
1600 |
+
|
1601 |
+
RETURN_TYPES = ("IMAGE",)
|
1602 |
+
FUNCTION = "execute"
|
1603 |
+
CATEGORY = "essentials/image utils"
|
1604 |
+
|
1605 |
+
def execute(self, image, noise_size, color_noise, mask_strength, mask_scale_diff, mask_contrast, noise_strenght, saturation, contrast, blur, noise_mask=None):
|
1606 |
+
torch.manual_seed(0)
|
1607 |
+
|
1608 |
+
elastic_alpha = max(image.shape[1], image.shape[2])# * noise_size
|
1609 |
+
elastic_sigma = elastic_alpha / 400 * noise_size
|
1610 |
+
|
1611 |
+
blur_size = int(6 * blur+1)
|
1612 |
+
if blur_size % 2 == 0:
|
1613 |
+
blur_size+= 1
|
1614 |
+
|
1615 |
+
if noise_mask is None:
|
1616 |
+
noise_mask = image
|
1617 |
+
|
1618 |
+
# increase contrast of the mask
|
1619 |
+
if mask_contrast != 1:
|
1620 |
+
noise_mask = T.ColorJitter(contrast=(mask_contrast,mask_contrast))(noise_mask.permute([0, 3, 1, 2])).permute([0, 2, 3, 1])
|
1621 |
+
|
1622 |
+
# Ensure noise mask is the same size as the image
|
1623 |
+
if noise_mask.shape[1:] != image.shape[1:]:
|
1624 |
+
noise_mask = F.interpolate(noise_mask.permute([0, 3, 1, 2]), size=(image.shape[1], image.shape[2]), mode='bicubic', align_corners=False)
|
1625 |
+
noise_mask = noise_mask.permute([0, 2, 3, 1])
|
1626 |
+
# Ensure we have the same number of masks and images
|
1627 |
+
if noise_mask.shape[0] > image.shape[0]:
|
1628 |
+
noise_mask = noise_mask[:image.shape[0]]
|
1629 |
+
else:
|
1630 |
+
noise_mask = torch.cat((noise_mask, noise_mask[-1:].repeat((image.shape[0]-noise_mask.shape[0], 1, 1, 1))), dim=0)
|
1631 |
+
|
1632 |
+
# Convert mask to grayscale mask
|
1633 |
+
noise_mask = noise_mask.mean(dim=3).unsqueeze(-1)
|
1634 |
+
|
1635 |
+
# add color noise
|
1636 |
+
imgs = image.clone().permute([0, 3, 1, 2])
|
1637 |
+
if color_noise > 0:
|
1638 |
+
color_noise = torch.normal(torch.zeros_like(imgs), std=color_noise)
|
1639 |
+
color_noise *= (imgs - imgs.min()) / (imgs.max() - imgs.min())
|
1640 |
+
|
1641 |
+
imgs = imgs + color_noise
|
1642 |
+
imgs = imgs.clamp(0, 1)
|
1643 |
+
|
1644 |
+
# create fine and coarse noise
|
1645 |
+
fine_noise = []
|
1646 |
+
for n in imgs:
|
1647 |
+
avg_color = n.mean(dim=[1,2])
|
1648 |
+
|
1649 |
+
tmp_noise = T.ElasticTransform(alpha=elastic_alpha, sigma=elastic_sigma, fill=avg_color.tolist())(n)
|
1650 |
+
if blur > 0:
|
1651 |
+
tmp_noise = T.GaussianBlur(blur_size, blur)(tmp_noise)
|
1652 |
+
tmp_noise = T.ColorJitter(contrast=(contrast,contrast), saturation=(saturation,saturation))(tmp_noise)
|
1653 |
+
fine_noise.append(tmp_noise)
|
1654 |
+
|
1655 |
+
imgs = None
|
1656 |
+
del imgs
|
1657 |
+
|
1658 |
+
fine_noise = torch.stack(fine_noise, dim=0)
|
1659 |
+
fine_noise = fine_noise.permute([0, 2, 3, 1])
|
1660 |
+
#fine_noise = torch.stack(fine_noise, dim=0)
|
1661 |
+
#fine_noise = pb(fine_noise)
|
1662 |
+
mask_scale_diff = min(mask_scale_diff, 0.99)
|
1663 |
+
if mask_scale_diff > 0:
|
1664 |
+
coarse_noise = F.interpolate(fine_noise.permute([0, 3, 1, 2]), scale_factor=1-mask_scale_diff, mode='area')
|
1665 |
+
coarse_noise = F.interpolate(coarse_noise, size=(fine_noise.shape[1], fine_noise.shape[2]), mode='bilinear', align_corners=False)
|
1666 |
+
coarse_noise = coarse_noise.permute([0, 2, 3, 1])
|
1667 |
+
else:
|
1668 |
+
coarse_noise = fine_noise
|
1669 |
+
|
1670 |
+
output = (1 - noise_mask) * coarse_noise + noise_mask * fine_noise
|
1671 |
+
|
1672 |
+
if mask_strength < 1:
|
1673 |
+
noise_mask = noise_mask.pow(mask_strength)
|
1674 |
+
noise_mask = torch.nan_to_num(noise_mask).clamp(0, 1)
|
1675 |
+
output = noise_mask * output + (1 - noise_mask) * image
|
1676 |
+
|
1677 |
+
# apply noise to image
|
1678 |
+
output = output * noise_strenght + image * (1 - noise_strenght)
|
1679 |
+
output = output.clamp(0, 1)
|
1680 |
+
|
1681 |
+
return (output, )
|
1682 |
+
|
1683 |
+
IMAGE_CLASS_MAPPINGS = {
|
1684 |
+
# Image analysis
|
1685 |
+
"ImageEnhanceDifference+": ImageEnhanceDifference,
|
1686 |
+
|
1687 |
+
# Image batch
|
1688 |
+
"ImageBatchMultiple+": ImageBatchMultiple,
|
1689 |
+
"ImageExpandBatch+": ImageExpandBatch,
|
1690 |
+
"ImageFromBatch+": ImageFromBatch,
|
1691 |
+
"ImageListToBatch+": ImageListToBatch,
|
1692 |
+
"ImageBatchToList+": ImageBatchToList,
|
1693 |
+
|
1694 |
+
# Image manipulation
|
1695 |
+
"ImageCompositeFromMaskBatch+": ImageCompositeFromMaskBatch,
|
1696 |
+
"ImageComposite+": ImageComposite,
|
1697 |
+
"ImageCrop+": ImageCrop,
|
1698 |
+
"ImageFlip+": ImageFlip,
|
1699 |
+
"ImageRandomTransform+": ImageRandomTransform,
|
1700 |
+
"ImageRemoveAlpha+": ImageRemoveAlpha,
|
1701 |
+
"ImageRemoveBackground+": ImageRemoveBackground,
|
1702 |
+
"ImageResize+": ImageResize,
|
1703 |
+
"ImageSeamCarving+": ImageSeamCarving,
|
1704 |
+
"ImageTile+": ImageTile,
|
1705 |
+
"ImageUntile+": ImageUntile,
|
1706 |
+
"RemBGSession+": RemBGSession,
|
1707 |
+
"TransparentBGSession+": TransparentBGSession,
|
1708 |
+
|
1709 |
+
# Image processing
|
1710 |
+
"ImageApplyLUT+": ImageApplyLUT,
|
1711 |
+
"ImageCASharpening+": ImageCAS,
|
1712 |
+
"ImageDesaturate+": ImageDesaturate,
|
1713 |
+
"PixelOEPixelize+": PixelOEPixelize,
|
1714 |
+
"ImagePosterize+": ImagePosterize,
|
1715 |
+
"ImageColorMatch+": ImageColorMatch,
|
1716 |
+
"ImageColorMatchAdobe+": ImageColorMatchAdobe,
|
1717 |
+
"ImageHistogramMatch+": ImageHistogramMatch,
|
1718 |
+
"ImageSmartSharpen+": ImageSmartSharpen,
|
1719 |
+
|
1720 |
+
# Utilities
|
1721 |
+
"GetImageSize+": GetImageSize,
|
1722 |
+
"ImageToDevice+": ImageToDevice,
|
1723 |
+
"ImagePreviewFromLatent+": ImagePreviewFromLatent,
|
1724 |
+
"NoiseFromImage+": NoiseFromImage,
|
1725 |
+
#"ExtractKeyframes+": ExtractKeyframes,
|
1726 |
+
}
|
1727 |
+
|
1728 |
+
IMAGE_NAME_MAPPINGS = {
|
1729 |
+
# Image analysis
|
1730 |
+
"ImageEnhanceDifference+": "🔧 Image Enhance Difference",
|
1731 |
+
|
1732 |
+
# Image batch
|
1733 |
+
"ImageBatchMultiple+": "🔧 Images Batch Multiple",
|
1734 |
+
"ImageExpandBatch+": "🔧 Image Expand Batch",
|
1735 |
+
"ImageFromBatch+": "🔧 Image From Batch",
|
1736 |
+
"ImageListToBatch+": "🔧 Image List To Batch",
|
1737 |
+
"ImageBatchToList+": "🔧 Image Batch To List",
|
1738 |
+
|
1739 |
+
# Image manipulation
|
1740 |
+
"ImageCompositeFromMaskBatch+": "🔧 Image Composite From Mask Batch",
|
1741 |
+
"ImageComposite+": "🔧 Image Composite",
|
1742 |
+
"ImageCrop+": "🔧 Image Crop",
|
1743 |
+
"ImageFlip+": "🔧 Image Flip",
|
1744 |
+
"ImageRandomTransform+": "🔧 Image Random Transform",
|
1745 |
+
"ImageRemoveAlpha+": "🔧 Image Remove Alpha",
|
1746 |
+
"ImageRemoveBackground+": "🔧 Image Remove Background",
|
1747 |
+
"ImageResize+": "🔧 Image Resize",
|
1748 |
+
"ImageSeamCarving+": "🔧 Image Seam Carving",
|
1749 |
+
"ImageTile+": "🔧 Image Tile",
|
1750 |
+
"ImageUntile+": "🔧 Image Untile",
|
1751 |
+
"RemBGSession+": "🔧 RemBG Session",
|
1752 |
+
"TransparentBGSession+": "🔧 InSPyReNet TransparentBG",
|
1753 |
+
|
1754 |
+
# Image processing
|
1755 |
+
"ImageApplyLUT+": "🔧 Image Apply LUT",
|
1756 |
+
"ImageCASharpening+": "🔧 Image Contrast Adaptive Sharpening",
|
1757 |
+
"ImageDesaturate+": "🔧 Image Desaturate",
|
1758 |
+
"PixelOEPixelize+": "🔧 Pixelize",
|
1759 |
+
"ImagePosterize+": "🔧 Image Posterize",
|
1760 |
+
"ImageColorMatch+": "🔧 Image Color Match",
|
1761 |
+
"ImageColorMatchAdobe+": "🔧 Image Color Match Adobe",
|
1762 |
+
"ImageHistogramMatch+": "🔧 Image Histogram Match",
|
1763 |
+
"ImageSmartSharpen+": "🔧 Image Smart Sharpen",
|
1764 |
+
|
1765 |
+
# Utilities
|
1766 |
+
"GetImageSize+": "🔧 Get Image Size",
|
1767 |
+
"ImageToDevice+": "🔧 Image To Device",
|
1768 |
+
"ImagePreviewFromLatent+": "🔧 Image Preview From Latent",
|
1769 |
+
"NoiseFromImage+": "🔧 Noise From Image",
|
1770 |
+
}
|
ComfyUI_essentials/js/DisplayAny.js
ADDED
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { app } from "../../scripts/app.js";
|
2 |
+
import { ComfyWidgets } from "../../scripts/widgets.js";
|
3 |
+
|
4 |
+
app.registerExtension({
|
5 |
+
name: "essentials.DisplayAny",
|
6 |
+
async beforeRegisterNodeDef(nodeType, nodeData, app) {
|
7 |
+
if (!nodeData?.category?.startsWith("essentials")) {
|
8 |
+
return;
|
9 |
+
}
|
10 |
+
|
11 |
+
if (nodeData.name === "DisplayAny") {
|
12 |
+
const onExecuted = nodeType.prototype.onExecuted;
|
13 |
+
|
14 |
+
nodeType.prototype.onExecuted = function (message) {
|
15 |
+
onExecuted?.apply(this, arguments);
|
16 |
+
|
17 |
+
if (this.widgets) {
|
18 |
+
for (let i = 1; i < this.widgets.length; i++) {
|
19 |
+
this.widgets[i].onRemove?.();
|
20 |
+
}
|
21 |
+
this.widgets.length = 1;
|
22 |
+
}
|
23 |
+
|
24 |
+
// Check if the "text" widget already exists.
|
25 |
+
let textWidget = this.widgets && this.widgets.find(w => w.name === "displaytext");
|
26 |
+
if (!textWidget) {
|
27 |
+
textWidget = ComfyWidgets["STRING"](this, "displaytext", ["STRING", { multiline: true }], app).widget;
|
28 |
+
textWidget.inputEl.readOnly = true;
|
29 |
+
textWidget.inputEl.style.border = "none";
|
30 |
+
textWidget.inputEl.style.backgroundColor = "transparent";
|
31 |
+
}
|
32 |
+
textWidget.value = message["text"].join("");
|
33 |
+
};
|
34 |
+
}
|
35 |
+
},
|
36 |
+
});
|
ComfyUI_essentials/js/FluxAttentionSeeker.js
ADDED
@@ -0,0 +1,133 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { app } from "../../scripts/app.js";
|
2 |
+
|
3 |
+
app.registerExtension({
|
4 |
+
name: "essentials.FluxAttentionSeeker",
|
5 |
+
async beforeRegisterNodeDef(nodeType, nodeData, app) {
|
6 |
+
if (!nodeData?.category?.startsWith("essentials")) {
|
7 |
+
return;
|
8 |
+
}
|
9 |
+
|
10 |
+
if (nodeData.name === "FluxAttentionSeeker+") {
|
11 |
+
const onCreated = nodeType.prototype.onNodeCreated;
|
12 |
+
|
13 |
+
nodeType.prototype.onNodeCreated = function () {
|
14 |
+
this.addWidget("button", "RESET ALL", null, () => {
|
15 |
+
this.widgets.forEach(w => {
|
16 |
+
if (w.type === "slider") {
|
17 |
+
w.value = 1.0;
|
18 |
+
}
|
19 |
+
});
|
20 |
+
});
|
21 |
+
|
22 |
+
this.addWidget("button", "ZERO ALL", null, () => {
|
23 |
+
this.widgets.forEach(w => {
|
24 |
+
if (w.type === "slider") {
|
25 |
+
w.value = 0.0;
|
26 |
+
}
|
27 |
+
});
|
28 |
+
});
|
29 |
+
|
30 |
+
this.addWidget("button", "REPEAT FIRST", null, () => {
|
31 |
+
var clip_value = undefined;
|
32 |
+
var t5_value = undefined;
|
33 |
+
this.widgets.forEach(w => {
|
34 |
+
if (w.name.startsWith('clip_l')) {
|
35 |
+
if (clip_value === undefined) {
|
36 |
+
clip_value = w.value;
|
37 |
+
}
|
38 |
+
w.value = clip_value;
|
39 |
+
} else if (w.name.startsWith('t5')) {
|
40 |
+
if (t5_value === undefined) {
|
41 |
+
t5_value = w.value;
|
42 |
+
}
|
43 |
+
w.value = t5_value;
|
44 |
+
}
|
45 |
+
});
|
46 |
+
});
|
47 |
+
};
|
48 |
+
}
|
49 |
+
},
|
50 |
+
});
|
51 |
+
|
52 |
+
app.registerExtension({
|
53 |
+
name: "essentials.SD3AttentionSeekerLG",
|
54 |
+
async beforeRegisterNodeDef(nodeType, nodeData, app) {
|
55 |
+
if (!nodeData?.category?.startsWith("essentials")) {
|
56 |
+
return;
|
57 |
+
}
|
58 |
+
|
59 |
+
if (nodeData.name === "SD3AttentionSeekerLG+") {
|
60 |
+
const onCreated = nodeType.prototype.onNodeCreated;
|
61 |
+
|
62 |
+
nodeType.prototype.onNodeCreated = function () {
|
63 |
+
this.addWidget("button", "RESET L", null, () => {
|
64 |
+
this.widgets.forEach(w => {
|
65 |
+
if (w.type === "slider" && w.name.startsWith('clip_l')) {
|
66 |
+
w.value = 1.0;
|
67 |
+
}
|
68 |
+
});
|
69 |
+
});
|
70 |
+
this.addWidget("button", "RESET G", null, () => {
|
71 |
+
this.widgets.forEach(w => {
|
72 |
+
if (w.type === "slider" && w.name.startsWith('clip_g')) {
|
73 |
+
w.value = 1.0;
|
74 |
+
}
|
75 |
+
});
|
76 |
+
});
|
77 |
+
|
78 |
+
this.addWidget("button", "REPEAT FIRST", null, () => {
|
79 |
+
var clip_l_value = undefined;
|
80 |
+
var clip_g_value = undefined;
|
81 |
+
this.widgets.forEach(w => {
|
82 |
+
if (w.name.startsWith('clip_l')) {
|
83 |
+
if (clip_l_value === undefined) {
|
84 |
+
clip_l_value = w.value;
|
85 |
+
}
|
86 |
+
w.value = clip_l_value;
|
87 |
+
} else if (w.name.startsWith('clip_g')) {
|
88 |
+
if (clip_g_value === undefined) {
|
89 |
+
clip_g_value = w.value;
|
90 |
+
}
|
91 |
+
w.value = clip_g_value;
|
92 |
+
}
|
93 |
+
});
|
94 |
+
});
|
95 |
+
};
|
96 |
+
}
|
97 |
+
},
|
98 |
+
});
|
99 |
+
|
100 |
+
app.registerExtension({
|
101 |
+
name: "essentials.SD3AttentionSeekerT5",
|
102 |
+
async beforeRegisterNodeDef(nodeType, nodeData, app) {
|
103 |
+
if (!nodeData?.category?.startsWith("essentials")) {
|
104 |
+
return;
|
105 |
+
}
|
106 |
+
|
107 |
+
if (nodeData.name === "SD3AttentionSeekerT5+") {
|
108 |
+
const onCreated = nodeType.prototype.onNodeCreated;
|
109 |
+
|
110 |
+
nodeType.prototype.onNodeCreated = function () {
|
111 |
+
this.addWidget("button", "RESET ALL", null, () => {
|
112 |
+
this.widgets.forEach(w => {
|
113 |
+
if (w.type === "slider") {
|
114 |
+
w.value = 1.0;
|
115 |
+
}
|
116 |
+
});
|
117 |
+
});
|
118 |
+
|
119 |
+
this.addWidget("button", "REPEAT FIRST", null, () => {
|
120 |
+
var t5_value = undefined;
|
121 |
+
this.widgets.forEach(w => {
|
122 |
+
if (w.name.startsWith('t5')) {
|
123 |
+
if (t5_value === undefined) {
|
124 |
+
t5_value = w.value;
|
125 |
+
}
|
126 |
+
w.value = t5_value;
|
127 |
+
}
|
128 |
+
});
|
129 |
+
});
|
130 |
+
};
|
131 |
+
}
|
132 |
+
},
|
133 |
+
});
|