gartajackhats1985 commited on
Commit
681fa96
1 Parent(s): c89ae80

Upload 1633 files

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .gitattributes +5 -0
  2. ComfyUI_InstantID/.github/FUNDING.yml +1 -0
  3. ComfyUI_InstantID/.github/workflows/publish.yml +22 -0
  4. ComfyUI_InstantID/.gitignore +160 -0
  5. ComfyUI_InstantID/CrossAttentionPatch.py +190 -0
  6. ComfyUI_InstantID/InstantID.py +611 -0
  7. ComfyUI_InstantID/LICENSE +201 -0
  8. ComfyUI_InstantID/README.md +141 -0
  9. ComfyUI_InstantID/README.zh-CN.md +137 -0
  10. ComfyUI_InstantID/__init__.py +3 -0
  11. ComfyUI_InstantID/__pycache__/CrossAttentionPatch.cpython-312.pyc +0 -0
  12. ComfyUI_InstantID/__pycache__/InstantID.cpython-312.pyc +0 -0
  13. ComfyUI_InstantID/__pycache__/__init__.cpython-312.pyc +0 -0
  14. ComfyUI_InstantID/__pycache__/resampler.cpython-312.pyc +0 -0
  15. ComfyUI_InstantID/__pycache__/utils.cpython-312.pyc +0 -0
  16. ComfyUI_InstantID/examples/InstantID_IPAdapter.json +861 -0
  17. ComfyUI_InstantID/examples/InstantID_basic.json +657 -0
  18. ComfyUI_InstantID/examples/InstantID_depth.json +881 -0
  19. ComfyUI_InstantID/examples/InstantID_multi_id.json +1364 -0
  20. ComfyUI_InstantID/examples/InstantID_posed.json +704 -0
  21. ComfyUI_InstantID/examples/daydreaming.jpg +0 -0
  22. ComfyUI_InstantID/examples/instant_id_ipadapter.jpg +0 -0
  23. ComfyUI_InstantID/examples/instantid_basic_workflow.jpg +0 -0
  24. ComfyUI_InstantID/examples/instantid_multi_id.jpg +0 -0
  25. ComfyUI_InstantID/pyproject.toml +15 -0
  26. ComfyUI_InstantID/requirements.txt +3 -0
  27. ComfyUI_InstantID/resampler.py +121 -0
  28. ComfyUI_InstantID/utils.py +24 -0
  29. ComfyUI_essentials/.github/workflows/publish.yml +22 -0
  30. ComfyUI_essentials/.gitignore +6 -0
  31. ComfyUI_essentials/LICENSE +21 -0
  32. ComfyUI_essentials/README.md +49 -0
  33. ComfyUI_essentials/__init__.py +36 -0
  34. ComfyUI_essentials/__pycache__/__init__.cpython-312.pyc +0 -0
  35. ComfyUI_essentials/__pycache__/conditioning.cpython-312.pyc +0 -0
  36. ComfyUI_essentials/__pycache__/image.cpython-312.pyc +0 -0
  37. ComfyUI_essentials/__pycache__/mask.cpython-312.pyc +0 -0
  38. ComfyUI_essentials/__pycache__/misc.cpython-312.pyc +0 -0
  39. ComfyUI_essentials/__pycache__/sampling.cpython-312.pyc +0 -0
  40. ComfyUI_essentials/__pycache__/segmentation.cpython-312.pyc +0 -0
  41. ComfyUI_essentials/__pycache__/text.cpython-312.pyc +0 -0
  42. ComfyUI_essentials/__pycache__/utils.cpython-312.pyc +0 -0
  43. ComfyUI_essentials/carve.py +454 -0
  44. ComfyUI_essentials/conditioning.py +280 -0
  45. ComfyUI_essentials/fonts/ShareTechMono-Regular.ttf +0 -0
  46. ComfyUI_essentials/fonts/put_font_files_here.txt +0 -0
  47. ComfyUI_essentials/histogram_matching.py +87 -0
  48. ComfyUI_essentials/image.py +1770 -0
  49. ComfyUI_essentials/js/DisplayAny.js +36 -0
  50. 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/)&nbsp; &nbsp;[![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)&nbsp; &nbsp;[![Finetuners](https://f.latent.vision/imgs/finetuners.png)](https://www.finetuners.ai/)&nbsp; &nbsp;[![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/)&nbsp; &nbsp;[![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)&nbsp; &nbsp;[![Finetuners](https://f.latent.vision/imgs/finetuners.png)](https://www.finetuners.ai/)&nbsp; &nbsp;[![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/)&nbsp; &nbsp;[![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)&nbsp; &nbsp;[![Finetuners](https://f.latent.vision/imgs/finetuners.png)](https://www.finetuners.ai/)&nbsp; &nbsp;[![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
+ });